dmaengine: idxd: add interrupt handling for event log
authorDave Jiang <dave.jiang@intel.com>
Fri, 7 Apr 2023 20:31:31 +0000 (13:31 -0700)
committerVinod Koul <vkoul@kernel.org>
Wed, 12 Apr 2023 17:48:45 +0000 (23:18 +0530)
An event log interrupt is raised in the misc interrupt INTCAUSE register
when an event is written by the hardware. Add basic event log processing
support to the interrupt handler. The event log is a ring where the
hardware owns the tail and the software owns the head. The hardware will
advance the tail index when an additional event has been pushed to memory.
The software will process the log entry and then advances the head. The
log is full when (tail + 1) % log_size = head. The hardware will stop
writing when the log is full. The user is expected to create a log size
large enough to handle all the expected events.

Tested-by: Tony Zhu <tony.zhu@intel.com>
Signed-off-by: Dave Jiang <dave.jiang@intel.com>
Co-developed-by: Fenghua Yu <fenghua.yu@intel.com>
Signed-off-by: Fenghua Yu <fenghua.yu@intel.com>
Link: https://lore.kernel.org/r/20230407203143.2189681-5-fenghua.yu@intel.com
Signed-off-by: Vinod Koul <vkoul@kernel.org>
drivers/dma/idxd/irq.c
drivers/dma/idxd/registers.h
include/uapi/linux/idxd.h

index 0d63930..52b8b7d 100644 (file)
@@ -217,6 +217,49 @@ static void idxd_int_handle_revoke(struct work_struct *work)
        kfree(revoke);
 }
 
+static void process_evl_entry(struct idxd_device *idxd, struct __evl_entry *entry_head)
+{
+       struct device *dev = &idxd->pdev->dev;
+       u8 status;
+
+       status = DSA_COMP_STATUS(entry_head->error);
+       dev_warn_ratelimited(dev, "Device error %#x operation: %#x fault addr: %#llx\n",
+                            status, entry_head->operation, entry_head->fault_addr);
+}
+
+static void process_evl_entries(struct idxd_device *idxd)
+{
+       union evl_status_reg evl_status;
+       unsigned int h, t;
+       struct idxd_evl *evl = idxd->evl;
+       struct __evl_entry *entry_head;
+       unsigned int ent_size = evl_ent_size(idxd);
+       u32 size;
+
+       evl_status.bits = 0;
+       evl_status.int_pending = 1;
+
+       spin_lock(&evl->lock);
+       /* Clear interrupt pending bit */
+       iowrite32(evl_status.bits_upper32,
+                 idxd->reg_base + IDXD_EVLSTATUS_OFFSET + sizeof(u32));
+       h = evl->head;
+       evl_status.bits = ioread64(idxd->reg_base + IDXD_EVLSTATUS_OFFSET);
+       t = evl_status.tail;
+       size = idxd->evl->size;
+
+       while (h != t) {
+               entry_head = (struct __evl_entry *)(evl->log + (h * ent_size));
+               process_evl_entry(idxd, entry_head);
+               h = (h + 1) % size;
+       }
+
+       evl->head = h;
+       evl_status.head = h;
+       iowrite32(evl_status.bits_lower32, idxd->reg_base + IDXD_EVLSTATUS_OFFSET);
+       spin_unlock(&evl->lock);
+}
+
 irqreturn_t idxd_misc_thread(int vec, void *data)
 {
        struct idxd_irq_entry *irq_entry = data;
@@ -304,6 +347,11 @@ irqreturn_t idxd_misc_thread(int vec, void *data)
                perfmon_counter_overflow(idxd);
        }
 
+       if (cause & IDXD_INTC_EVL) {
+               val |= IDXD_INTC_EVL;
+               process_evl_entries(idxd);
+       }
+
        val ^= cause;
        if (val)
                dev_warn_once(dev, "Unexpected interrupt cause bits set: %#x\n",
index 11bb97c..148db94 100644 (file)
@@ -168,6 +168,7 @@ enum idxd_device_reset_type {
 #define IDXD_INTC_OCCUPY                       0x04
 #define IDXD_INTC_PERFMON_OVFL         0x08
 #define IDXD_INTC_HALT_STATE           0x10
+#define IDXD_INTC_EVL                  0x20
 #define IDXD_INTC_INT_HANDLE_REVOKED   0x80000000
 
 #define IDXD_CMD_OFFSET                        0xa0
@@ -558,6 +559,24 @@ union filter_cfg {
        u64 val;
 } __packed;
 
+#define IDXD_EVLSTATUS_OFFSET          0xf0
+
+union evl_status_reg {
+       struct {
+               u32 head:16;
+               u32 rsvd:16;
+               u32 tail:16;
+               u32 rsvd2:14;
+               u32 int_pending:1;
+               u32 rsvd3:1;
+       };
+       struct {
+               u32 bits_lower32;
+               u32 bits_upper32;
+       };
+       u64 bits;
+} __packed;
+
 struct __evl_entry {
        u64 rsvd:2;
        u64 desc_valid:1;
index 5d05bf1..0bc8eea 100644 (file)
@@ -170,6 +170,7 @@ enum iax_completion_status {
 
 #define DSA_COMP_STATUS_MASK           0x7f
 #define DSA_COMP_STATUS_WRITE          0x80
+#define DSA_COMP_STATUS(status)                ((status) & DSA_COMP_STATUS_MASK)
 
 struct dsa_hw_desc {
        uint32_t        pasid:20;