Commit 5481fc6e authored by David Howells's avatar David Howells Committed by Linus Torvalds

afs: Fix hang on rmmod due to outstanding timer

The fileserver probe timer, net->fs_probe_timer, isn't cancelled when
the kafs module is being removed and so the count it holds on
net->servers_outstanding doesn't get dropped..

This causes rmmod to wait forever.  The hung process shows a stack like:

	afs_purge_servers+0x1b5/0x23c [kafs]
	afs_net_exit+0x44/0x6e [kafs]
	ops_exit_list+0x72/0x93
	unregister_pernet_operations+0x14c/0x1ba
	unregister_pernet_subsys+0x1d/0x2a
	afs_exit+0x29/0x6f [kafs]
	__do_sys_delete_module.isra.0+0x1a2/0x24b
	do_syscall_64+0x51/0x95
	entry_SYSCALL_64_after_hwframe+0x44/0xa9

Fix this by:

 (1) Attempting to cancel the probe timer and, if successful, drop the
     count that the timer was holding.

 (2) Make the timer function just drop the count and not schedule the
     prober if the afs portion of net namespace is being destroyed.

Also, whilst we're at it, make the following changes:

 (3) Initialise net->servers_outstanding to 1 and decrement it before
     waiting on it so that it doesn't generate wake up events by being
     decremented to 0 until we're cleaning up.

 (4) Switch the atomic_dec() on ->servers_outstanding for ->fs_timer in
     afs_purge_servers() to use the helper function for that.

Fixes: f6cbb368 ("afs: Actively poll fileservers to maintain NAT or firewall openings")
Signed-off-by: default avatarDavid Howells <dhowells@redhat.com>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent f8ea5c7b
...@@ -314,7 +314,7 @@ void afs_fs_probe_timer(struct timer_list *timer) ...@@ -314,7 +314,7 @@ void afs_fs_probe_timer(struct timer_list *timer)
{ {
struct afs_net *net = container_of(timer, struct afs_net, fs_probe_timer); struct afs_net *net = container_of(timer, struct afs_net, fs_probe_timer);
if (!queue_work(afs_wq, &net->fs_prober)) if (!net->live || !queue_work(afs_wq, &net->fs_prober))
afs_dec_servers_outstanding(net); afs_dec_servers_outstanding(net);
} }
...@@ -458,3 +458,12 @@ int afs_wait_for_one_fs_probe(struct afs_server *server, bool is_intr) ...@@ -458,3 +458,12 @@ int afs_wait_for_one_fs_probe(struct afs_server *server, bool is_intr)
return -ETIME; return -ETIME;
return -EDESTADDRREQ; return -EDESTADDRREQ;
} }
/*
* Clean up the probing when the namespace is killed off.
*/
void afs_fs_probe_cleanup(struct afs_net *net)
{
if (del_timer_sync(&net->fs_probe_timer))
afs_dec_servers_outstanding(net);
}
...@@ -1065,6 +1065,7 @@ extern int afs_wait_for_fs_probes(struct afs_server_list *, unsigned long); ...@@ -1065,6 +1065,7 @@ extern int afs_wait_for_fs_probes(struct afs_server_list *, unsigned long);
extern void afs_probe_fileserver(struct afs_net *, struct afs_server *); extern void afs_probe_fileserver(struct afs_net *, struct afs_server *);
extern void afs_fs_probe_dispatcher(struct work_struct *); extern void afs_fs_probe_dispatcher(struct work_struct *);
extern int afs_wait_for_one_fs_probe(struct afs_server *, bool); extern int afs_wait_for_one_fs_probe(struct afs_server *, bool);
extern void afs_fs_probe_cleanup(struct afs_net *);
/* /*
* inode.c * inode.c
......
...@@ -100,6 +100,7 @@ static int __net_init afs_net_init(struct net *net_ns) ...@@ -100,6 +100,7 @@ static int __net_init afs_net_init(struct net *net_ns)
timer_setup(&net->fs_timer, afs_servers_timer, 0); timer_setup(&net->fs_timer, afs_servers_timer, 0);
INIT_WORK(&net->fs_prober, afs_fs_probe_dispatcher); INIT_WORK(&net->fs_prober, afs_fs_probe_dispatcher);
timer_setup(&net->fs_probe_timer, afs_fs_probe_timer, 0); timer_setup(&net->fs_probe_timer, afs_fs_probe_timer, 0);
atomic_set(&net->servers_outstanding, 1);
ret = -ENOMEM; ret = -ENOMEM;
sysnames = kzalloc(sizeof(*sysnames), GFP_KERNEL); sysnames = kzalloc(sizeof(*sysnames), GFP_KERNEL);
...@@ -130,6 +131,7 @@ static int __net_init afs_net_init(struct net *net_ns) ...@@ -130,6 +131,7 @@ static int __net_init afs_net_init(struct net *net_ns)
error_open_socket: error_open_socket:
net->live = false; net->live = false;
afs_fs_probe_cleanup(net);
afs_cell_purge(net); afs_cell_purge(net);
afs_purge_servers(net); afs_purge_servers(net);
error_cell_init: error_cell_init:
...@@ -150,6 +152,7 @@ static void __net_exit afs_net_exit(struct net *net_ns) ...@@ -150,6 +152,7 @@ static void __net_exit afs_net_exit(struct net *net_ns)
struct afs_net *net = afs_net(net_ns); struct afs_net *net = afs_net(net_ns);
net->live = false; net->live = false;
afs_fs_probe_cleanup(net);
afs_cell_purge(net); afs_cell_purge(net);
afs_purge_servers(net); afs_purge_servers(net);
afs_close_socket(net); afs_close_socket(net);
......
...@@ -605,11 +605,12 @@ void afs_purge_servers(struct afs_net *net) ...@@ -605,11 +605,12 @@ void afs_purge_servers(struct afs_net *net)
_enter(""); _enter("");
if (del_timer_sync(&net->fs_timer)) if (del_timer_sync(&net->fs_timer))
atomic_dec(&net->servers_outstanding); afs_dec_servers_outstanding(net);
afs_queue_server_manager(net); afs_queue_server_manager(net);
_debug("wait"); _debug("wait");
atomic_dec(&net->servers_outstanding);
wait_var_event(&net->servers_outstanding, wait_var_event(&net->servers_outstanding,
!atomic_read(&net->servers_outstanding)); !atomic_read(&net->servers_outstanding));
_leave(""); _leave("");
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment