diff options
author | Trond Myklebust <Trond.Myklebust@netapp.com> | 2011-07-06 19:58:23 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2011-08-08 10:35:41 -0700 |
commit | cf139882872adb1fffbb0b009244f94eb2c0d7c5 (patch) | |
tree | d76da20f740b44b3a2a2c57a2983ee49561ab6d2 /net | |
parent | 699909bab8955ffa86db1d7be17dbc9859be4388 (diff) |
SUNRPC: Fix a race between work-queue and rpc_killall_tasks
commit b55c59892e1f3b6c7d4b9ccffb4263e1486fb990 upstream.
Since rpc_killall_tasks may modify the rpc_task's tk_action field
without any locking, we need to be careful when dereferencing it.
Reported-by: Ben Greear <greearb@candelatech.com>
Tested-by: Ben Greear <greearb@candelatech.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'net')
-rw-r--r-- | net/sunrpc/sched.c | 27 |
1 files changed, 11 insertions, 16 deletions
diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c index 03a5c9ec2caf..416ca5e9378e 100644 --- a/net/sunrpc/sched.c +++ b/net/sunrpc/sched.c @@ -628,30 +628,25 @@ static void __rpc_execute(struct rpc_task *task) BUG_ON(RPC_IS_QUEUED(task)); for (;;) { + void (*do_action)(struct rpc_task *); /* - * Execute any pending callback. + * Execute any pending callback first. */ - if (task->tk_callback) { - void (*save_callback)(struct rpc_task *); - - /* - * We set tk_callback to NULL before calling it, - * in case it sets the tk_callback field itself: - */ - save_callback = task->tk_callback; - task->tk_callback = NULL; - save_callback(task); - } else { + do_action = task->tk_callback; + task->tk_callback = NULL; + if (do_action == NULL) { /* * Perform the next FSM step. - * tk_action may be NULL when the task has been killed - * by someone else. + * tk_action may be NULL if the task has been killed. + * In particular, note that rpc_killall_tasks may + * do this at any time, so beware when dereferencing. */ - if (task->tk_action == NULL) + do_action = task->tk_action; + if (do_action == NULL) break; - task->tk_action(task); } + do_action(task); /* * Lockless check for whether task is sleeping or not. |