dmaengine: idxd: fix misc interrupt completion
authorDave Jiang <dave.jiang@intel.com>
Fri, 15 Jan 2021 21:52:33 +0000 (14:52 -0700)
committerVinod Koul <vkoul@kernel.org>
Sun, 17 Jan 2021 06:49:25 +0000 (12:19 +0530)
Nikhil reported the misc interrupt handler can sometimes miss handling
the command interrupt when an error interrupt happens near the same time.
Have the irq handling thread continue to process the misc interrupts until
all interrupts are processed. This is a low usage interrupt and is not
expected to handle high volume traffic. Therefore there is no concern of
this thread running for a long time.

Fixes: 0d5c10b4c84d ("dmaengine: idxd: add work queue drain support")
Reported-by: Nikhil Rao <nikhil.rao@intel.com>
Signed-off-by: Dave Jiang <dave.jiang@intel.com>
Link: https://lore.kernel.org/r/161074755329.2183844.13295528344116907983.stgit@djiang5-desk3.ch.intel.com
Signed-off-by: Vinod Koul <vkoul@kernel.org>
drivers/dma/idxd/irq.c

index fe996ce..a60ca11 100644 (file)
@@ -111,19 +111,14 @@ irqreturn_t idxd_irq_handler(int vec, void *data)
        return IRQ_WAKE_THREAD;
 }
 
-irqreturn_t idxd_misc_thread(int vec, void *data)
+static int process_misc_interrupts(struct idxd_device *idxd, u32 cause)
 {
-       struct idxd_irq_entry *irq_entry = data;
-       struct idxd_device *idxd = irq_entry->idxd;
        struct device *dev = &idxd->pdev->dev;
        union gensts_reg gensts;
-       u32 cause, val = 0;
+       u32 val = 0;
        int i;
        bool err = false;
 
-       cause = ioread32(idxd->reg_base + IDXD_INTCAUSE_OFFSET);
-       iowrite32(cause, idxd->reg_base + IDXD_INTCAUSE_OFFSET);
-
        if (cause & IDXD_INTC_ERR) {
                spin_lock_bh(&idxd->dev_lock);
                for (i = 0; i < 4; i++)
@@ -181,7 +176,7 @@ irqreturn_t idxd_misc_thread(int vec, void *data)
                              val);
 
        if (!err)
-               goto out;
+               return 0;
 
        /*
         * This case should rarely happen and typically is due to software
@@ -211,10 +206,33 @@ irqreturn_t idxd_misc_thread(int vec, void *data)
                                gensts.reset_type == IDXD_DEVICE_RESET_FLR ?
                                "FLR" : "system reset");
                        spin_unlock_bh(&idxd->dev_lock);
+                       return -ENXIO;
                }
        }
 
- out:
+       return 0;
+}
+
+irqreturn_t idxd_misc_thread(int vec, void *data)
+{
+       struct idxd_irq_entry *irq_entry = data;
+       struct idxd_device *idxd = irq_entry->idxd;
+       int rc;
+       u32 cause;
+
+       cause = ioread32(idxd->reg_base + IDXD_INTCAUSE_OFFSET);
+       if (cause)
+               iowrite32(cause, idxd->reg_base + IDXD_INTCAUSE_OFFSET);
+
+       while (cause) {
+               rc = process_misc_interrupts(idxd, cause);
+               if (rc < 0)
+                       break;
+               cause = ioread32(idxd->reg_base + IDXD_INTCAUSE_OFFSET);
+               if (cause)
+                       iowrite32(cause, idxd->reg_base + IDXD_INTCAUSE_OFFSET);
+       }
+
        idxd_unmask_msix_vector(idxd, irq_entry->id);
        return IRQ_HANDLED;
 }