s390/qdio: clear DSCI prior to scanning multiple input queues
authorJulian Wiedmann <jwi@linux.vnet.ibm.com>
Mon, 21 Nov 2016 12:37:48 +0000 (13:37 +0100)
committerMartin Schwidefsky <schwidefsky@de.ibm.com>
Fri, 3 Feb 2017 12:50:19 +0000 (13:50 +0100)
For devices with multiple input queues, tiqdio_call_inq_handlers()
iterates over all input queues and clears the device's DSCI
during each iteration. If the DSCI is re-armed during one
of the later iterations, we therefore do not scan the previous
queues again.
The re-arming also raises a new adapter interrupt. But its
handler does not trigger a rescan for the device, as the DSCI
has already been erroneously cleared.
This can result in queue stalls on devices with multiple
input queues.

Fix it by clearing the DSCI just once, prior to scanning the queues.

As the code is moved in front of the loop, we also need to access
the DSCI directly (ie irq->dsci) instead of going via each queue's
parent pointer to the same irq. This is not a functional change,
and a follow-up patch will clean up the other users.

In practice, this bug only affects CQ-enabled HiperSockets devices,
ie. devices with sysfs-attribute "hsuid" set. Setting a hsuid is
needed for AF_IUCV socket applications that use HiperSockets
communication.

Fixes: 104ea556ee7f ("qdio: support asynchronous delivery of storage blocks")
Cc: <stable@vger.kernel.org> # v3.2+
Reviewed-by: Ursula Braun <ubraun@linux.vnet.ibm.com>
Signed-off-by: Julian Wiedmann <jwi@linux.vnet.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
drivers/s390/cio/qdio_thinint.c

index 5d06253c2a7a385df2a2d951c49992a006d21600..30e9fbbff0511e926e9fa6f06c1b5f87599c6e4d 100644 (file)
@@ -147,11 +147,11 @@ static inline void tiqdio_call_inq_handlers(struct qdio_irq *irq)
        struct qdio_q *q;
        int i;
 
-       for_each_input_queue(irq, q, i) {
-               if (!references_shared_dsci(irq) &&
-                   has_multiple_inq_on_dsci(irq))
-                       xchg(q->irq_ptr->dsci, 0);
+       if (!references_shared_dsci(irq) &&
+           has_multiple_inq_on_dsci(irq))
+               xchg(irq->dsci, 0);
 
+       for_each_input_queue(irq, q, i) {
                if (q->u.in.queue_start_poll) {
                        /* skip if polling is enabled or already in work */
                        if (test_and_set_bit(QDIO_QUEUE_IRQS_DISABLED,