| From foo@baz Sun Jun 17 12:07:33 CEST 2018 |
| From: David Howells <dhowells@redhat.com> |
| Date: Wed, 18 Apr 2018 09:38:34 +0100 |
| Subject: afs: Fix server record deletion |
| |
| From: David Howells <dhowells@redhat.com> |
| |
| [ Upstream commit 660625922b3d9fcb376e5870299bc5c1086e1d32 ] |
| |
| AFS server records get removed from the net->fs_servers tree when |
| they're deleted, but not from the net->fs_addresses{4,6} lists, which |
| can lead to an oops in afs_find_server() when a server record has been |
| removed, for instance during rmmod. |
| |
| Fix this by deleting the record from the by-address lists before posting |
| it for RCU destruction. |
| |
| The reason this hasn't been noticed before is that the fileserver keeps |
| probing the local cache manager, thereby keeping the service record |
| alive, so the oops would only happen when a fileserver eventually gets |
| bored and stops pinging or if the module gets rmmod'd and a call comes |
| in from the fileserver during the window between the server records |
| being destroyed and the socket being closed. |
| |
| The oops looks something like: |
| |
| BUG: unable to handle kernel NULL pointer dereference at 000000000000001c |
| ... |
| Workqueue: kafsd afs_process_async_call [kafs] |
| RIP: 0010:afs_find_server+0x271/0x36f [kafs] |
| ... |
| Call Trace: |
| afs_deliver_cb_init_call_back_state3+0x1f2/0x21f [kafs] |
| afs_deliver_to_call+0x1ee/0x5e8 [kafs] |
| afs_process_async_call+0x5b/0xd0 [kafs] |
| process_one_work+0x2c2/0x504 |
| worker_thread+0x1d4/0x2ac |
| kthread+0x11f/0x127 |
| ret_from_fork+0x24/0x30 |
| |
| Fixes: d2ddc776a458 ("afs: Overhaul volume and server record caching and fileserver rotation") |
| Signed-off-by: David Howells <dhowells@redhat.com> |
| Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> |
| Signed-off-by: Sasha Levin <alexander.levin@microsoft.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| fs/afs/server.c | 9 ++++++++- |
| 1 file changed, 8 insertions(+), 1 deletion(-) |
| |
| --- a/fs/afs/server.c |
| +++ b/fs/afs/server.c |
| @@ -426,8 +426,15 @@ static void afs_gc_servers(struct afs_ne |
| } |
| write_sequnlock(&net->fs_lock); |
| |
| - if (deleted) |
| + if (deleted) { |
| + write_seqlock(&net->fs_addr_lock); |
| + if (!hlist_unhashed(&server->addr4_link)) |
| + hlist_del_rcu(&server->addr4_link); |
| + if (!hlist_unhashed(&server->addr6_link)) |
| + hlist_del_rcu(&server->addr6_link); |
| + write_sequnlock(&net->fs_addr_lock); |
| afs_destroy_server(net, server); |
| + } |
| } |
| } |
| |