From: Trond Myklebust Date: Sat, 19 Apr 2008 18:41:49 -0400 SUNRPC: Fix a task locking race... Signed-off-by: Trond Myklebust --- net/sunrpc/sched.c | 50 ++++++++++++++++++++++++++++++-------------------- 1 files changed, 30 insertions(+), 20 deletions(-) diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c index 6eab9bf..62e218e 100644 --- a/net/sunrpc/sched.c +++ b/net/sunrpc/sched.c @@ -166,6 +166,7 @@ static void __rpc_remove_wait_queue(struct rpc_wait_queue *queue, struct rpc_tas __rpc_remove_wait_queue_priority(task); list_del(&task->u.tk_wait.list); queue->qlen--; + rpc_clear_queued(task); dprintk("RPC: %5u removed from queue %p \"%s\"\n", task->tk_pid, queue, rpc_qname(queue)); } @@ -290,14 +291,8 @@ EXPORT_SYMBOL_GPL(__rpc_wait_for_completion_task); */ static void rpc_make_runnable(struct rpc_task *task) { - rpc_clear_queued(task); if (rpc_test_and_set_running(task)) return; - /* We might have raced */ - if (RPC_IS_QUEUED(task)) { - rpc_clear_running(task); - return; - } if (RPC_IS_ASYNC(task)) { int status; @@ -609,6 +604,32 @@ void rpc_release_calldata(const struct rpc_call_ops *ops, void *calldata) } } +static int rpc_try_clear_running(struct rpc_task *task) +{ + struct rpc_wait_queue *queue; + int ret = 0; + + /* + * check for whether task is sleeping or not. + */ + if (!RPC_IS_QUEUED(task)) + goto out; + + /* Note: nobody else may change task->tk_waitqueue while waiting + * for the spinlock, since __rpc_execute() 'owns' the task. + */ + queue = task->tk_waitqueue; + spin_lock_bh(&queue->lock); + /* Have we raced with a wakeup() call? */ + if (RPC_IS_QUEUED(task)) { + rpc_clear_running(task); + ret = 1; + } + spin_unlock_bh(&queue->lock); +out: + return ret; +} + /* * This is the RPC `scheduler' (or rather, the finite state machine). */ @@ -653,20 +674,10 @@ static void __rpc_execute(struct rpc_task *task) task->tk_action(task); } - /* - * Lockless check for whether task is sleeping or not. - */ - if (!RPC_IS_QUEUED(task)) - continue; - rpc_clear_running(task); - if (RPC_IS_ASYNC(task)) { - /* Careful! we may have raced... */ - if (RPC_IS_QUEUED(task)) - return; - if (rpc_test_and_set_running(task)) - return; + if (!rpc_try_clear_running(task)) continue; - } + if (RPC_IS_ASYNC(task)) + return; /* sync task: sleep here */ dprintk("RPC: %5u sync task going to sleep\n", task->tk_pid); @@ -685,7 +696,6 @@ static void __rpc_execute(struct rpc_task *task) rpc_exit(task, -ERESTARTSYS); rpc_wake_up_task(task); } - rpc_set_running(task); dprintk("RPC: %5u sync task resuming\n", task->tk_pid); }