diff options
author | Trond Myklebust <Trond.Myklebust@netapp.com> | 2013-01-27 14:20:49 -0500 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2013-01-30 17:45:14 -0500 |
commit | edd2e36fe8bd3cec4fa67e746d4c4a9246d0830e (patch) | |
tree | d44df6639a2431212bcb50f6117ba7b1083dc67d /net | |
parent | ab225417825963b6dc66be7ea80f94ac1378dfdf (diff) | |
download | linux-edd2e36fe8bd3cec4fa67e746d4c4a9246d0830e.tar.gz linux-edd2e36fe8bd3cec4fa67e746d4c4a9246d0830e.tar.bz2 linux-edd2e36fe8bd3cec4fa67e746d4c4a9246d0830e.zip |
SUNRPC: When changing the queue priority, ensure that we change the owner
This fixes a livelock in the xprt->sending queue where we end up never
making progress on lower priority tasks because sleep_on_priority()
keeps adding new tasks with the same owner to the head of the queue,
and priority bumps mean that we keep resetting the queue->owner to
whatever task is at the head of the queue.
Regression introduced by commit c05eecf636101dd4347b2d8fa457626bf0088e0a
(SUNRPC: Don't allow low priority tasks to pre-empt higher priority ones).
Reported-by: Andy Adamson <andros@netapp.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Diffstat (limited to 'net')
-rw-r--r-- | net/sunrpc/sched.c | 18 |
1 files changed, 17 insertions, 1 deletions
diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c index bfa31714581f..fb20f25ddec9 100644 --- a/net/sunrpc/sched.c +++ b/net/sunrpc/sched.c @@ -98,9 +98,25 @@ __rpc_add_timer(struct rpc_wait_queue *queue, struct rpc_task *task) list_add(&task->u.tk_wait.timer_list, &queue->timer_list.list); } +static void rpc_rotate_queue_owner(struct rpc_wait_queue *queue) +{ + struct list_head *q = &queue->tasks[queue->priority]; + struct rpc_task *task; + + if (!list_empty(q)) { + task = list_first_entry(q, struct rpc_task, u.tk_wait.list); + if (task->tk_owner == queue->owner) + list_move_tail(&task->u.tk_wait.list, q); + } +} + static void rpc_set_waitqueue_priority(struct rpc_wait_queue *queue, int priority) { - queue->priority = priority; + if (queue->priority != priority) { + /* Fairness: rotate the list when changing priority */ + rpc_rotate_queue_owner(queue); + queue->priority = priority; + } } static void rpc_set_waitqueue_owner(struct rpc_wait_queue *queue, pid_t pid) |