]> git.itanic.dy.fi Git - linux-stable/commitdiff
staging: qlge: Fix irq masking in INTx mode
authorBenjamin Poirier <bpoirier@suse.com>
Fri, 27 Sep 2019 10:11:55 +0000 (19:11 +0900)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 4 Oct 2019 15:17:44 +0000 (17:17 +0200)
Tracing the driver operation reveals that the INTR_EN_EN bit (per-queue
interrupt control) does not immediately prevent rx completion interrupts
when the device is operating in INTx mode. This leads to interrupts being
raised while napi is scheduled/running. Those interrupts are ignored by
qlge_isr() and falsely reported as IRQ_NONE thanks to the irq_cnt scheme.
This in turn can cause frames to loiter in the receive queue until a later
frame leads to another rx interrupt that will schedule napi.

Use the INTR_EN_EI bit (master interrupt control) instead.

Signed-off-by: Benjamin Poirier <bpoirier@suse.com>
Link: https://lore.kernel.org/r/20190927101210.23856-2-bpoirier@suse.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/staging/qlge/qlge_main.c

index 6cae33072496cf5fb80ce50a9bee65370695d2ea..d7b64d360ea812994f3edef397dff7d1f214cd29 100644 (file)
@@ -3366,6 +3366,7 @@ static void ql_enable_msix(struct ql_adapter *qdev)
                }
        }
        qlge_irq_type = LEG_IRQ;
+       set_bit(QL_LEGACY_ENABLED, &qdev->flags);
        netif_printk(qdev, ifup, KERN_DEBUG, qdev->ndev,
                     "Running with legacy interrupts.\n");
 }
@@ -3509,6 +3510,16 @@ static void ql_resolve_queues_to_irqs(struct ql_adapter *qdev)
                intr_context->intr_dis_mask =
                    INTR_EN_TYPE_MASK | INTR_EN_INTR_MASK |
                    INTR_EN_TYPE_DISABLE;
+               if (test_bit(QL_LEGACY_ENABLED, &qdev->flags)) {
+                       /* Experience shows that when using INTx interrupts,
+                        * the device does not always auto-mask INTR_EN_EN.
+                        * Moreover, masking INTR_EN_EN manually does not
+                        * immediately prevent interrupt generation.
+                        */
+                       intr_context->intr_en_mask |= INTR_EN_EI << 16 |
+                               INTR_EN_EI;
+                       intr_context->intr_dis_mask |= INTR_EN_EI << 16;
+               }
                intr_context->intr_read_mask =
                    INTR_EN_TYPE_MASK | INTR_EN_INTR_MASK | INTR_EN_TYPE_READ;
                /*