[PATCH 2.4.0-test7-pre7]: Fix 2 rpc_wait_queue hangups (slight rewrite)

From: Trond Myklebust (trond.myklebust@fys.uio.no)
Date: Wed Aug 23 2000 - 06:54:59 EST


The following patch is a slight rewrite of the 2 patches that I sent
in last week. It fixes the following conditions:

  - Fix deadlock between rpc_unlock_task() and rpc_wake_up() /
    rpc_wake_up_status(). The latter 2 functions would loop forever
    if a task was locked since __rpc_wake_up_task() would not remove
    it from the wait queue. In addition since the spinlock was being
    held, rpc_unlock_task() could never succeed.

    This patch changes the above so that running __rpc_wake_up_task()
    on a locked task will remove it from the wait queue, but will not
    schedule it for running until tk_lock goes to zero.

  - Fix loop in rpc_find_parent(). The latter was assuming the wrong
    topology for rpc_wait_queue: it assumed a linear rather than a
    circular list.

In addition, a bit of documentation has been thrown in so as to make
the above clear.

Cheers,
  Trond

diff -u --recursive --new-file linux-2.4.0-test7-pre7/net/sunrpc/sched.c linux-2.4.0-test7-fix_rpc/net/sunrpc/sched.c
--- linux-2.4.0-test7-pre7/net/sunrpc/sched.c Sat Aug 5 03:18:49 2000
+++ linux-2.4.0-test7-fix_rpc/net/sunrpc/sched.c Wed Aug 23 12:55:57 2000
@@ -369,8 +369,15 @@
         spin_unlock_bh(&rpc_queue_lock);
 }
 
-/*
- * Wake up a single task -- must be invoked with spin lock held.
+/**
+ * __rpc_wake_up_task - wake up a single rpc_task
+ * @task: task to be woken up
+ *
+ * If the task is locked, it is merely removed from the queue, and
+ * 'task->tk_wakeup' is set. rpc_unlock_task() will then ensure
+ * that it is woken up as soon as the lock count goes to zero.
+ *
+ * Caller must hold rpc_queue_lock
  */
 static void
 __rpc_wake_up_task(struct rpc_task *task)
@@ -395,6 +402,8 @@
                 return;
 
         __rpc_disable_timer(task);
+ if (task->tk_rpcwait != &schedq)
+ __rpc_remove_wait_queue(task);
 
         /* If the task has been locked, then set tk_wakeup so that
          * rpc_unlock_task() wakes us up... */
@@ -404,8 +413,6 @@
         } else
                 task->tk_wakeup = 0;
 
- if (task->tk_rpcwait != &schedq)
- __rpc_remove_wait_queue(task);
         rpc_make_runnable(task);
 
         dprintk("RPC: __rpc_wake_up_task done\n");
@@ -452,8 +459,11 @@
         return task;
 }
 
-/*
- * Wake up all tasks on a queue
+/**
+ * rpc_wake_up - wake up all rpc_tasks
+ * @queue: rpc_wait_queue on which the tasks are sleeping
+ *
+ * Grabs rpc_queue_lock
  */
 void
 rpc_wake_up(struct rpc_wait_queue *queue)
@@ -464,8 +474,12 @@
         spin_unlock_bh(&rpc_queue_lock);
 }
 
-/*
- * Wake up all tasks on a queue, and set their status value.
+/**
+ * rpc_wake_up_status - wake up all rpc_tasks and set their status value.
+ * @queue: rpc_wait_queue on which the tasks are sleeping
+ * @status: status value to set
+ *
+ * Grabs rpc_queue_lock
  */
 void
 rpc_wake_up_status(struct rpc_wait_queue *queue, int status)
@@ -931,20 +945,27 @@
                 task->tk_release(task);
 }
 
-/*
- * Handling of RPC child tasks
- * We can't simply call wake_up(parent) here, because the
- * parent task may already have gone away
+/**
+ * rpc_find_parent - find the parent of a child task.
+ * @child: child task
+ *
+ * Checks that the parent task is still sleeping on the
+ * queue 'childq'. If so returns a pointer to the parent.
+ * Upon failure returns NULL.
+ *
+ * Caller must hold rpc_queue_lock
  */
 static inline struct rpc_task *
 rpc_find_parent(struct rpc_task *child)
 {
- struct rpc_task *temp, *parent;
+ struct rpc_task *task, *parent;
 
         parent = (struct rpc_task *) child->tk_calldata;
- for (temp = childq.task; temp; temp = temp->tk_next) {
- if (temp == parent)
- return parent;
+ if ((task = childq.task) != NULL) {
+ do {
+ if (task == parent)
+ return parent;
+ } while ((task = task->tk_next) != childq.task);
         }
         return NULL;
 }
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
Please read the FAQ at http://www.tux.org/lkml/



This archive was generated by hypermail 2b29 : Wed Aug 23 2000 - 21:00:08 EST