Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 540a0f75 authored by Trond Myklebust's avatar Trond Myklebust
Browse files

SUNRPC: We must not use list_for_each_entry_safe() in rpc_wake_up()



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: default avatarAndy Adamson <andros@netapp.com>
Signed-off-by: default avatarTrond Myklebust <Trond.Myklebust@netapp.com>
Cc: stable@vger.kernel.org
parent e49a29bd
Loading
Loading
Loading
Loading
+11 −4
Original line number Original line Diff line number Diff line
@@ -534,14 +534,18 @@ EXPORT_SYMBOL_GPL(rpc_wake_up_next);
 */
 */
void rpc_wake_up(struct rpc_wait_queue *queue)
void rpc_wake_up(struct rpc_wait_queue *queue)
{
{
	struct rpc_task *task, *next;
	struct list_head *head;
	struct list_head *head;


	spin_lock_bh(&queue->lock);
	spin_lock_bh(&queue->lock);
	head = &queue->tasks[queue->maxpriority];
	head = &queue->tasks[queue->maxpriority];
	for (;;) {
	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);
			rpc_wake_up_task_queue_locked(queue, task);
		}
		if (head == &queue->tasks[0])
		if (head == &queue->tasks[0])
			break;
			break;
		head--;
		head--;
@@ -559,13 +563,16 @@ EXPORT_SYMBOL_GPL(rpc_wake_up);
 */
 */
void rpc_wake_up_status(struct rpc_wait_queue *queue, int status)
void rpc_wake_up_status(struct rpc_wait_queue *queue, int status)
{
{
	struct rpc_task *task, *next;
	struct list_head *head;
	struct list_head *head;


	spin_lock_bh(&queue->lock);
	spin_lock_bh(&queue->lock);
	head = &queue->tasks[queue->maxpriority];
	head = &queue->tasks[queue->maxpriority];
	for (;;) {
	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;
			task->tk_status = status;
			rpc_wake_up_task_queue_locked(queue, task);
			rpc_wake_up_task_queue_locked(queue, task);
		}
		}