Messages in this thread Patch in this message |  | | Date | Wed, 23 Aug 2000 13:54:59 +0200 (CEST) | Subject | [PATCH 2.4.0-test7-pre7]: Fix 2 rpc_wait_queue hangups (slight rewrite) | From | Trond Myklebust <> |
| |
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/
|  |