[PATCH] RPC: Fix a race with rpc_restart_call()
authorTrond Myklebust <Trond.Myklebust@netapp.com>
Wed, 22 Jun 2005 17:16:19 +0000 (17:16 +0000)
committerTrond Myklebust <Trond.Myklebust@netapp.com>
Wed, 22 Jun 2005 20:07:01 +0000 (16:07 -0400)
 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: Trond Myklebust <Trond.Myklebust@netapp.com>
net/sunrpc/sched.c

index c06614d0e31de5d974f101a4ac90cf37a7764d60..cc298fa4b81dfda784478b8bedd9436425f7dbb1 100644 (file)
@@ -554,6 +554,30 @@ __rpc_atrun(struct rpc_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).
  */
@@ -566,8 +590,7 @@ static int __rpc_execute(struct rpc_task *task)
 
        BUG_ON(RPC_IS_QUEUED(task));
 
- restarted:
-       while (1) {
+       for (;;) {
                /*
                 * Garbage collection of pending timers...
                 */
@@ -600,11 +623,12 @@ static int __rpc_execute(struct rpc_task *task)
                 * by someone else.
                 */
                if (!RPC_IS_QUEUED(task)) {
-                       if (!task->tk_action)
+                       if (task->tk_action != NULL) {
+                               lock_kernel();
+                               task->tk_action(task);
+                               unlock_kernel();
+                       } else if (__rpc_do_exit(task))
                                break;
-                       lock_kernel();
-                       task->tk_action(task);
-                       unlock_kernel();
                }
 
                /*
@@ -645,23 +669,6 @@ static int __rpc_execute(struct rpc_task *task)
                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);
        status = task->tk_status;