diff options
author | Nathaniel Wesley Filardo <nwf@cs.jhu.edu> | 2014-05-21 09:58:26 -0400 |
---|---|---|
committer | David Howells <dhowells@redhat.com> | 2014-05-23 08:05:22 -0400 |
commit | 150a6b478982475c60fa25b7060ab990ece5483d (patch) | |
tree | e9dacc67a8091709971041578ecc1bed3141ba76 | |
parent | 6cf12869f5c1a837f18af5f8b2308fa243772735 (diff) |
AFS: Fix kafs module unloading
At present, it is not possible to successfully unload the kafs module if there
are outstanding async outgoing calls (those made with afs_make_call()). This
appears to be due to the changes introduced by:
commit 059499453a9abd1857d442b44da8b4c126dc72a8
Author: Tejun Heo <tj@kernel.org>
Date: Fri Mar 7 10:24:50 2014 -0500
Subject: afs: don't use PREPARE_WORK
which didn't go far enough. The problem is due to:
(1) The aforementioned commit introduced a separate handler function pointer
in the call, call->async_workfn, in addition to the original workqueue
item, call->async_work, for asynchronous operations because workqueues
subsystem cannot handle the workqueue item pointer being changed whilst
the item is queued or being processed.
(2) afs_async_workfn() was introduced in that commit to be the callback for
call->async_work. Its sole purpose is to run whatever call->async_workfn
points to.
(3) call->async_workfn is only used from afs_async_workfn(), which is only
set on async_work by afs_collect_incoming_call() - ie. for incoming
calls.
(4) call->async_workfn is *not* set by afs_make_call() when outgoing calls are
made, and call->async_work is set afs_process_async_call() - and not
afs_async_workfn().
(5) afs_process_async_call() now changes call->async_workfn rather than
call->async_work to point to afs_delete_async_call() to clean up, but this
is only effective for incoming calls because call->async_work does not
point to afs_async_workfn() for outgoing calls.
(6) Because, for incoming calls, call->async_work remains pointing to
afs_process_async_call() this results in an infinite loop.
Instead, make the workqueue uniformly vector through call->async_workfn, via
afs_async_workfn() and simply initialise call->async_workfn to point to
afs_process_async_call() in afs_make_call().
Signed-off-by: Nathaniel Wesley Filardo <nwf@cs.jhu.edu>
Signed-off-by: David Howells <dhowells@redhat.com>
Reviewed-by: Tejun Heo <tj@kernel.org>
-rw-r--r-- | fs/afs/rxrpc.c | 17 |
1 files changed, 9 insertions, 8 deletions
diff --git a/fs/afs/rxrpc.c b/fs/afs/rxrpc.c index 1a1110b1a7ff..5a05014ea7b0 100644 --- a/fs/afs/rxrpc.c +++ b/fs/afs/rxrpc.c | |||
@@ -58,6 +58,13 @@ static void afs_collect_incoming_call(struct work_struct *); | |||
58 | static struct sk_buff_head afs_incoming_calls; | 58 | static struct sk_buff_head afs_incoming_calls; |
59 | static DECLARE_WORK(afs_collect_incoming_call_work, afs_collect_incoming_call); | 59 | static DECLARE_WORK(afs_collect_incoming_call_work, afs_collect_incoming_call); |
60 | 60 | ||
61 | static void afs_async_workfn(struct work_struct *work) | ||
62 | { | ||
63 | struct afs_call *call = container_of(work, struct afs_call, async_work); | ||
64 | |||
65 | call->async_workfn(work); | ||
66 | } | ||
67 | |||
61 | /* | 68 | /* |
62 | * open an RxRPC socket and bind it to be a server for callback notifications | 69 | * open an RxRPC socket and bind it to be a server for callback notifications |
63 | * - the socket is left in blocking mode and non-blocking ops use MSG_DONTWAIT | 70 | * - the socket is left in blocking mode and non-blocking ops use MSG_DONTWAIT |
@@ -348,7 +355,8 @@ int afs_make_call(struct in_addr *addr, struct afs_call *call, gfp_t gfp, | |||
348 | atomic_read(&afs_outstanding_calls)); | 355 | atomic_read(&afs_outstanding_calls)); |
349 | 356 | ||
350 | call->wait_mode = wait_mode; | 357 | call->wait_mode = wait_mode; |
351 | INIT_WORK(&call->async_work, afs_process_async_call); | 358 | call->async_workfn = afs_process_async_call; |
359 | INIT_WORK(&call->async_work, afs_async_workfn); | ||
352 | 360 | ||
353 | memset(&srx, 0, sizeof(srx)); | 361 | memset(&srx, 0, sizeof(srx)); |
354 | srx.srx_family = AF_RXRPC; | 362 | srx.srx_family = AF_RXRPC; |
@@ -672,13 +680,6 @@ void afs_transfer_reply(struct afs_call *call, struct sk_buff *skb) | |||
672 | call->reply_size += len; | 680 | call->reply_size += len; |
673 | } | 681 | } |
674 | 682 | ||
675 | static void afs_async_workfn(struct work_struct *work) | ||
676 | { | ||
677 | struct afs_call *call = container_of(work, struct afs_call, async_work); | ||
678 | |||
679 | call->async_workfn(work); | ||
680 | } | ||
681 | |||
682 | /* | 683 | /* |
683 | * accept the backlog of incoming calls | 684 | * accept the backlog of incoming calls |
684 | */ | 685 | */ |