Commit e54e4785 authored by Julian Wiedmann's avatar Julian Wiedmann Committed by Vasily Gorbik

s390/qdio: (re-)initialize tiqdio list entries

When tiqdio_remove_input_queues() removes a queue from the tiq_list as
part of qdio_shutdown(), it doesn't re-initialize the queue's list entry
and the prev/next pointers go stale.

If a subsequent qdio_establish() fails while sending the ESTABLISH cmd,
it calls qdio_shutdown() again in QDIO_IRQ_STATE_ERR state and
tiqdio_remove_input_queues() will attempt to remove the queue entry a
second time. This dereferences the stale pointers, and bad things ensue.
Fix this by re-initializing the list entry after removing it from the
list.

For good practice also initialize the list entry when the queue is first
allocated, and remove the quirky checks that papered over this omission.
Note that prior to
commit e5218134 ("s390/qdio: fix access to uninitialized qdio_q fields"),
these checks were bogus anyway.

setup_queues_misc() clears the whole queue struct, and thus needs to
re-init the prev/next pointers as well.

Fixes: 779e6e1c ("[S390] qdio: new qdio driver.")
Cc: <stable@vger.kernel.org>
Signed-off-by: default avatarJulian Wiedmann <jwi@linux.ibm.com>
Signed-off-by: default avatarVasily Gorbik <gor@linux.ibm.com>
parent 83eb1a41
...@@ -150,6 +150,7 @@ static int __qdio_allocate_qs(struct qdio_q **irq_ptr_qs, int nr_queues) ...@@ -150,6 +150,7 @@ static int __qdio_allocate_qs(struct qdio_q **irq_ptr_qs, int nr_queues)
return -ENOMEM; return -ENOMEM;
} }
irq_ptr_qs[i] = q; irq_ptr_qs[i] = q;
INIT_LIST_HEAD(&q->entry);
} }
return 0; return 0;
} }
...@@ -178,6 +179,7 @@ static void setup_queues_misc(struct qdio_q *q, struct qdio_irq *irq_ptr, ...@@ -178,6 +179,7 @@ static void setup_queues_misc(struct qdio_q *q, struct qdio_irq *irq_ptr,
q->mask = 1 << (31 - i); q->mask = 1 << (31 - i);
q->nr = i; q->nr = i;
q->handler = handler; q->handler = handler;
INIT_LIST_HEAD(&q->entry);
} }
static void setup_storage_lists(struct qdio_q *q, struct qdio_irq *irq_ptr, static void setup_storage_lists(struct qdio_q *q, struct qdio_irq *irq_ptr,
......
...@@ -87,14 +87,14 @@ void tiqdio_remove_input_queues(struct qdio_irq *irq_ptr) ...@@ -87,14 +87,14 @@ void tiqdio_remove_input_queues(struct qdio_irq *irq_ptr)
struct qdio_q *q; struct qdio_q *q;
q = irq_ptr->input_qs[0]; q = irq_ptr->input_qs[0];
/* if establish triggered an error */ if (!q)
if (!q || !q->entry.prev || !q->entry.next)
return; return;
mutex_lock(&tiq_list_lock); mutex_lock(&tiq_list_lock);
list_del_rcu(&q->entry); list_del_rcu(&q->entry);
mutex_unlock(&tiq_list_lock); mutex_unlock(&tiq_list_lock);
synchronize_rcu(); synchronize_rcu();
INIT_LIST_HEAD(&q->entry);
} }
static inline int has_multiple_inq_on_dsci(struct qdio_irq *irq_ptr) static inline int has_multiple_inq_on_dsci(struct qdio_irq *irq_ptr)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment