Commit d05fdb0c authored by Trond Myklebust's avatar Trond Myklebust

[PATCH] RPC: Fix a race with rpc_restart_call()

 If the task->tk_exit() wants to restart the RPC call after delaying
 then the current RPC code will clobber the timer by calling
 rpc_delete_timer() immediately after re-entering the loop in
 __rpc_execute().

 Problem noticed by Oleg Nesterov <oleg@tv-sign.ru>
Signed-off-by: default avatarTrond Myklebust <Trond.Myklebust@netapp.com>
parent 4e93d3e8
...@@ -554,6 +554,30 @@ __rpc_atrun(struct rpc_task *task) ...@@ -554,6 +554,30 @@ __rpc_atrun(struct rpc_task *task)
rpc_wake_up_task(task); rpc_wake_up_task(task);
} }
/*
* Helper that calls task->tk_exit if it exists and then returns
* true if we should exit __rpc_execute.
*/
static inline int __rpc_do_exit(struct rpc_task *task)
{
if (task->tk_exit != NULL) {
lock_kernel();
task->tk_exit(task);
unlock_kernel();
/* If tk_action is non-null, we should restart the call */
if (task->tk_action != NULL) {
if (!RPC_ASSASSINATED(task)) {
/* Release RPC slot and buffer memory */
xprt_release(task);
rpc_free(task);
return 0;
}
printk(KERN_ERR "RPC: dead task tried to walk away.\n");
}
}
return 1;
}
/* /*
* This is the RPC `scheduler' (or rather, the finite state machine). * This is the RPC `scheduler' (or rather, the finite state machine).
*/ */
...@@ -566,8 +590,7 @@ static int __rpc_execute(struct rpc_task *task) ...@@ -566,8 +590,7 @@ static int __rpc_execute(struct rpc_task *task)
BUG_ON(RPC_IS_QUEUED(task)); BUG_ON(RPC_IS_QUEUED(task));
restarted: for (;;) {
while (1) {
/* /*
* Garbage collection of pending timers... * Garbage collection of pending timers...
*/ */
...@@ -600,11 +623,12 @@ static int __rpc_execute(struct rpc_task *task) ...@@ -600,11 +623,12 @@ static int __rpc_execute(struct rpc_task *task)
* by someone else. * by someone else.
*/ */
if (!RPC_IS_QUEUED(task)) { if (!RPC_IS_QUEUED(task)) {
if (!task->tk_action) if (task->tk_action != NULL) {
break;
lock_kernel(); lock_kernel();
task->tk_action(task); task->tk_action(task);
unlock_kernel(); unlock_kernel();
} else if (__rpc_do_exit(task))
break;
} }
/* /*
...@@ -645,23 +669,6 @@ static int __rpc_execute(struct rpc_task *task) ...@@ -645,23 +669,6 @@ static int __rpc_execute(struct rpc_task *task)
dprintk("RPC: %4d sync task resuming\n", task->tk_pid); dprintk("RPC: %4d sync task resuming\n", task->tk_pid);
} }
if (task->tk_exit) {
lock_kernel();
task->tk_exit(task);
unlock_kernel();
/* If tk_action is non-null, the user wants us to restart */
if (task->tk_action) {
if (!RPC_ASSASSINATED(task)) {
/* Release RPC slot and buffer memory */
if (task->tk_rqstp)
xprt_release(task);
rpc_free(task);
goto restarted;
}
printk(KERN_ERR "RPC: dead task tries to walk away.\n");
}
}
dprintk("RPC: %4d exit() = %d\n", task->tk_pid, task->tk_status); dprintk("RPC: %4d exit() = %d\n", task->tk_pid, task->tk_status);
status = task->tk_status; status = task->tk_status;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment