| From 540a0f7584169651f485e8ab67461fcb06934e38 Mon Sep 17 00:00:00 2001 |
| From: Trond Myklebust <Trond.Myklebust@netapp.com> |
| Date: Mon, 19 Mar 2012 13:39:35 -0400 |
| Subject: SUNRPC: We must not use list_for_each_entry_safe() in rpc_wake_up() |
| |
| From: Trond Myklebust <Trond.Myklebust@netapp.com> |
| |
| commit 540a0f7584169651f485e8ab67461fcb06934e38 upstream. |
| |
| The problem is that for the case of priority queues, we |
| have to assume that __rpc_remove_wait_queue_priority will move new |
| elements from the tk_wait.links lists into the queue->tasks[] list. |
| We therefore cannot use list_for_each_entry_safe() on queue->tasks[], |
| since that will skip these new tasks that __rpc_remove_wait_queue_priority |
| is adding. |
| |
| Without this fix, rpc_wake_up and rpc_wake_up_status will both fail |
| to wake up all functions on priority wait queues, which can result |
| in some nasty hangs. |
| |
| Reported-by: Andy Adamson <andros@netapp.com> |
| Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| net/sunrpc/sched.c | 15 +++++++++++---- |
| 1 file changed, 11 insertions(+), 4 deletions(-) |
| |
| --- a/net/sunrpc/sched.c |
| +++ b/net/sunrpc/sched.c |
| @@ -500,14 +500,18 @@ EXPORT_SYMBOL_GPL(rpc_wake_up_next); |
| */ |
| void rpc_wake_up(struct rpc_wait_queue *queue) |
| { |
| - struct rpc_task *task, *next; |
| struct list_head *head; |
| |
| spin_lock_bh(&queue->lock); |
| head = &queue->tasks[queue->maxpriority]; |
| for (;;) { |
| - list_for_each_entry_safe(task, next, head, u.tk_wait.list) |
| + while (!list_empty(head)) { |
| + struct rpc_task *task; |
| + task = list_first_entry(head, |
| + struct rpc_task, |
| + u.tk_wait.list); |
| rpc_wake_up_task_queue_locked(queue, task); |
| + } |
| if (head == &queue->tasks[0]) |
| break; |
| head--; |
| @@ -525,13 +529,16 @@ EXPORT_SYMBOL_GPL(rpc_wake_up); |
| */ |
| void rpc_wake_up_status(struct rpc_wait_queue *queue, int status) |
| { |
| - struct rpc_task *task, *next; |
| struct list_head *head; |
| |
| spin_lock_bh(&queue->lock); |
| head = &queue->tasks[queue->maxpriority]; |
| for (;;) { |
| - list_for_each_entry_safe(task, next, head, u.tk_wait.list) { |
| + while (!list_empty(head)) { |
| + struct rpc_task *task; |
| + task = list_first_entry(head, |
| + struct rpc_task, |
| + u.tk_wait.list); |
| task->tk_status = status; |
| rpc_wake_up_task_queue_locked(queue, task); |
| } |