summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTrond Myklebust <Trond.Myklebust@netapp.com>2013-05-22 12:57:24 -0400
committerBen Hutchings <ben@decadent.org.uk>2013-05-30 14:35:04 +0100
commitf7b72b71fe00b5159d71f0d385c8d5e5865bcd32 (patch)
treeefaa16d1b96ec549491db7cc4bc4f396bbe29fee
parenta27e05b0cf32d711c1ec57625b6053658efd11fc (diff)
SUNRPC: Prevent an rpc_task wakeup race
commit a3c3cac5d31879cd9ae2de7874dc6544ca704aec upstream. The lockless RPC_IS_QUEUED() test in __rpc_execute means that we need to be careful about ordering the calls to rpc_test_and_set_running(task) and rpc_clear_queued(task). If we get the order wrong, then we may end up testing the RPC_TASK_RUNNING flag after __rpc_execute() has looped and changed the state of the rpc_task. Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com> Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
-rw-r--r--net/sunrpc/sched.c8
1 files changed, 7 insertions, 1 deletions
diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c
index 150848bf717c..206c61eda924 100644
--- a/net/sunrpc/sched.c
+++ b/net/sunrpc/sched.c
@@ -299,11 +299,17 @@ EXPORT_SYMBOL_GPL(__rpc_wait_for_completion_task);
* Note: If the task is ASYNC, and is being made runnable after sitting on an
* rpc_wait_queue, this must be called with the queue spinlock held to protect
* the wait queue operation.
+ * Note the ordering of rpc_test_and_set_running() and rpc_clear_queued(),
+ * which is needed to ensure that __rpc_execute() doesn't loop (due to the
+ * lockless RPC_IS_QUEUED() test) before we've had a chance to test
+ * the RPC_TASK_RUNNING flag.
*/
static void rpc_make_runnable(struct rpc_task *task)
{
+ bool need_wakeup = !rpc_test_and_set_running(task);
+
rpc_clear_queued(task);
- if (rpc_test_and_set_running(task))
+ if (!need_wakeup)
return;
if (RPC_IS_ASYNC(task)) {
INIT_WORK(&task->u.tk_work, rpc_async_schedule);