summary refs log tree commit diff
path: root/fs/afs
diff options
context:
space:
mode:
authorDavid Howells <dhowells@redhat.com>2017-11-02 15:27:45 +0000
committerDavid Howells <dhowells@redhat.com>2017-11-13 15:38:16 +0000
commit59fa1c4a9f528c2a1556f4b2cd4e055b560c1c0a (patch)
treeca7a896cd83bccf789a1d29791e22d144768ac5e /fs/afs
parente3b2ffe0f0e1471854dc53bb69ff452e65cc88f2 (diff)
downloadlinux-59fa1c4a9f528c2a1556f4b2cd4e055b560c1c0a.tar.gz
afs: Fix server reaping
Fix server reaping and make sure it's all done before we start trying to
purge cells, given that servers currently pin cells.

Signed-off-by: David Howells <dhowells@redhat.com>
Diffstat (limited to 'fs/afs')
-rw-r--r--fs/afs/internal.h5
-rw-r--r--fs/afs/main.c3
-rw-r--r--fs/afs/server.c59
3 files changed, 57 insertions, 10 deletions
diff --git a/fs/afs/internal.h b/fs/afs/internal.h
index 53bd11d73469..bc9ded443b11 100644
--- a/fs/afs/internal.h
+++ b/fs/afs/internal.h
@@ -238,7 +238,9 @@ struct afs_net {
 	rwlock_t		servers_lock;
 	struct list_head	server_graveyard;	/* Inactive server LRU list */
 	spinlock_t		server_graveyard_lock;
-	struct delayed_work	server_reaper;
+	struct timer_list	server_timer;
+	struct work_struct	server_reaper;
+	atomic_t		servers_outstanding;
 
 	/* Misc */
 	struct proc_dir_entry	*proc_afs;		/* /proc/net/afs directory */
@@ -700,6 +702,7 @@ do {								\
 	atomic_inc(&(S)->usage);				\
 } while(0)
 
+extern void afs_server_timer(struct timer_list *);
 extern struct afs_server *afs_lookup_server(struct afs_cell *,
 					    const struct in_addr *);
 extern struct afs_server *afs_find_server(struct afs_net *,
diff --git a/fs/afs/main.c b/fs/afs/main.c
index 6bd2f3a426de..38e15b1f0eec 100644
--- a/fs/afs/main.c
+++ b/fs/afs/main.c
@@ -62,7 +62,8 @@ static int __net_init afs_net_init(struct afs_net *net)
 	rwlock_init(&net->servers_lock);
 	INIT_LIST_HEAD(&net->server_graveyard);
 	spin_lock_init(&net->server_graveyard_lock);
-	INIT_DELAYED_WORK(&net->server_reaper, afs_reap_server);
+	INIT_WORK(&net->server_reaper, afs_reap_server);
+	timer_setup(&net->server_timer, afs_server_timer, 0);
 
 	/* Register the /proc stuff */
 	ret = afs_proc_init(net);
diff --git a/fs/afs/server.c b/fs/afs/server.c
index e47fd9bc0ddc..33aeb527ac7e 100644
--- a/fs/afs/server.c
+++ b/fs/afs/server.c
@@ -15,6 +15,25 @@
 
 static unsigned afs_server_timeout = 10;	/* server timeout in seconds */
 
+static void afs_inc_servers_outstanding(struct afs_net *net)
+{
+	atomic_inc(&net->servers_outstanding);
+}
+
+static void afs_dec_servers_outstanding(struct afs_net *net)
+{
+	if (atomic_dec_and_test(&net->servers_outstanding))
+		wake_up_atomic_t(&net->servers_outstanding);
+}
+
+void afs_server_timer(struct timer_list *timer)
+{
+	struct afs_net *net = container_of(timer, struct afs_net, server_timer);
+
+	if (!queue_work(afs_wq, &net->server_reaper))
+		afs_dec_servers_outstanding(net);
+}
+
 /*
  * install a server record in the master tree
  */
@@ -81,6 +100,7 @@ static struct afs_server *afs_alloc_server(struct afs_cell *cell,
 
 		memcpy(&server->addr, addr, sizeof(struct in_addr));
 		server->addr.s_addr = addr->s_addr;
+		afs_inc_servers_outstanding(cell->net);
 		_leave(" = %p{%d}", server, atomic_read(&server->usage));
 	} else {
 		_leave(" = NULL [nomem]");
@@ -159,6 +179,7 @@ found_server:
 server_in_two_cells:
 	write_unlock(&cell->servers_lock);
 	kfree(candidate);
+	afs_dec_servers_outstanding(cell->net);
 	printk(KERN_NOTICE "kAFS: Server %pI4 appears to be in two cells\n",
 	       addr);
 	_leave(" = -EEXIST");
@@ -208,6 +229,18 @@ found:
 	return server;
 }
 
+static void afs_set_server_timer(struct afs_net *net, time64_t delay)
+{
+	afs_inc_servers_outstanding(net);
+	if (net->live) {
+		if (timer_reduce(&net->server_timer, jiffies + delay * HZ))
+			afs_dec_servers_outstanding(net);
+	} else {
+		if (!queue_work(afs_wq, &net->server_reaper))
+			afs_dec_servers_outstanding(net);
+	}
+}
+
 /*
  * destroy a server record
  * - removes from the cell list
@@ -236,8 +269,7 @@ void afs_put_server(struct afs_server *server)
 	if (atomic_read(&server->usage) == 0) {
 		list_move_tail(&server->grave, &net->server_graveyard);
 		server->time_of_death = ktime_get_real_seconds();
-		queue_delayed_work(afs_wq, &net->server_reaper,
-				   net->live ? afs_server_timeout * HZ : 0);
+		afs_set_server_timer(net, afs_server_timeout);
 	}
 	spin_unlock(&net->server_graveyard_lock);
 	_leave(" [dead]");
@@ -246,7 +278,7 @@ void afs_put_server(struct afs_server *server)
 /*
  * destroy a dead server
  */
-static void afs_destroy_server(struct afs_server *server)
+static void afs_destroy_server(struct afs_net *net, struct afs_server *server)
 {
 	_enter("%p", server);
 
@@ -260,6 +292,7 @@ static void afs_destroy_server(struct afs_server *server)
 
 	afs_put_cell(server->cell);
 	kfree(server);
+	afs_dec_servers_outstanding(net);
 }
 
 /*
@@ -269,7 +302,7 @@ void afs_reap_server(struct work_struct *work)
 {
 	LIST_HEAD(corpses);
 	struct afs_server *server;
-	struct afs_net *net = container_of(work, struct afs_net, server_reaper.work);
+	struct afs_net *net = container_of(work, struct afs_net, server_reaper);
 	unsigned long delay, expiry;
 	time64_t now;
 
@@ -284,8 +317,8 @@ void afs_reap_server(struct work_struct *work)
 		if (net->live) {
 			expiry = server->time_of_death + afs_server_timeout;
 			if (expiry > now) {
-				delay = (expiry - now) * HZ;
-				mod_delayed_work(afs_wq, &net->server_reaper, delay);
+				delay = (expiry - now);
+				afs_set_server_timer(net, delay);
 				break;
 			}
 		}
@@ -309,8 +342,10 @@ void afs_reap_server(struct work_struct *work)
 	while (!list_empty(&corpses)) {
 		server = list_entry(corpses.next, struct afs_server, grave);
 		list_del(&server->grave);
-		afs_destroy_server(server);
+		afs_destroy_server(net, server);
 	}
+
+	afs_dec_servers_outstanding(net);
 }
 
 /*
@@ -319,5 +354,13 @@ void afs_reap_server(struct work_struct *work)
  */
 void __net_exit afs_purge_servers(struct afs_net *net)
 {
-	mod_delayed_work(afs_wq, &net->server_reaper, 0);
+	if (del_timer_sync(&net->server_timer))
+		atomic_dec(&net->servers_outstanding);
+
+	afs_inc_servers_outstanding(net);
+	if (!queue_work(afs_wq, &net->server_reaper))
+		afs_dec_servers_outstanding(net);
+
+	wait_on_atomic_t(&net->servers_outstanding, atomic_t_wait,
+			 TASK_UNINTERRUPTIBLE);
 }