From: Alex Williamson <[email protected]>

The submitted driver writes only GPIE.EIAME (with a register value of
0x10, which is actually GPIE.Multiple_MSIX, bit 4) and clears EICR by
reading it.  On QEMU this works because the emulated loopback path is
synchronous and EICR is implemented as read-to-clear unconditionally.
Real 82576 hardware needs the full MSI-X programming sequence.

Per 82576 datasheet section 7.3.2.11 Table 7-47, MSI-X mode requires:

  GPIE.Multiple_MSIX (bit 4): route causes through IVAR.
  GPIE.EIAME (bit 30): apply EIAM on MSI-X assertion.  Without EIAME,
    section 7.3.2.11 specifies EIAM only takes effect on EICR
    read/write, which is not the path used here.

Configure auto-clear and auto-mask for vector 0:

  EIAC (section 8.8.5): auto-clear of EICR cause bit on MSI-X assertion.
  EIAM (section 8.8.6): with EIAME set, auto-mask of EIMS on MSI-X
    assertion.  This guarantees one interrupt per memcpy batch and
    prevents repeat delivery if the cause re-asserts before EIMS is
    restored.

Replace the read-to-clear of EICR with write-to-clear.  Section 8.8.5
states "If any bits are set in EIAC, the EICR register should not be
read", and section 7.3.4.3 cautions against read-to-clear in MSI-X
mode in general.  Write-to-clear (section 7.3.4.2) is unconditional.

Replace the magic '1' values written to EIMS/EIMC with IGB_EICR_VEC0,
add the GPIE/EIAC/EIAM macros, and drop the wrong-valued IGB_GPIE_EIAME
macro (the new definition lives next to IGB_GPIE_MULTIPLE_MSIX).

Assisted-by: Claude:claude-opus-4-7
Signed-off-by: Alex Williamson <[email protected]>
---
 .../selftests/vfio/lib/drivers/igb/igb.c      | 40 ++++++++++++++++---
 .../vfio/lib/drivers/igb/registers.h          | 11 ++++-
 2 files changed, 43 insertions(+), 8 deletions(-)

diff --git a/tools/testing/selftests/vfio/lib/drivers/igb/igb.c 
b/tools/testing/selftests/vfio/lib/drivers/igb/igb.c
index c0f9cefb23e5..a8f2072b6820 100644
--- a/tools/testing/selftests/vfio/lib/drivers/igb/igb.c
+++ b/tools/testing/selftests/vfio/lib/drivers/igb/igb.c
@@ -271,11 +271,32 @@ static void igb_init(struct vfio_pci_device *device)
        /* Enable MSI-X with 1 vector for the test */
        vfio_pci_msix_enable(device, MSIX_VECTOR, 1);
 
-       /* Enable auto-masking of interrupts to avoid storms without a real ISR 
*/
-       igb_write32(igb, IGB_GPIE, IGB_GPIE_EIAME);
+       /*
+        * Program MSI-X interrupt routing per 82576 datasheet:
+        *
+        * GPIE (section 7.3.2.11, Table 7-47): set Multiple_MSIX (bit 4) to
+        * route interrupt causes through IVAR mapping, and EIAME (bit 30)
+        * to apply EIAM on MSI-X assertion (without EIAME, EIAM only
+        * applies on EICR read/write).
+        *
+        * EIAC (section 8.8.5): enable auto-clear of EICR for vector 0.
+        * Without auto-clear the cause stays set after delivery and the
+        * test can see spurious interrupts on the next memcpy batch.
+        *
+        * EIAM (section 8.8.6): enable auto-mask of EIMS for vector 0 on
+        * MSI-X assertion (effective because EIAME is set), so a single
+        * interrupt is delivered per memcpy batch even if the cause
+        * re-asserts before software re-enables the mask.
+        *
+        * IVAR (section 7.3.1.2, register definition in 8.8.13): map RX
+        * cause 0 to MSI-X vector 0 and mark the entry valid.
+        */
+       igb_write32(igb, IGB_GPIE, IGB_GPIE_MULTIPLE_MSIX | IGB_GPIE_EIAME);
+       igb_write32(igb, IGB_EIAC, IGB_EICR_VEC0);
+       igb_write32(igb, IGB_EIAM, IGB_EICR_VEC0);
 
        /* Enable interrupts on vector 0 */
-       igb_write32(igb, IGB_EIMS, 1);
+       igb_write32(igb, IGB_EIMS, IGB_EICR_VEC0);
 
        /* Map vector 0 to interrupt cause 0 and mark it valid */
        igb_write32(igb, IGB_IVAR0, IGB_IVAR_VALID);
@@ -302,17 +323,24 @@ static void igb_remove(struct vfio_pci_device *device)
 
 static void igb_irq_disable(struct igb *igb)
 {
-       igb_write32(igb, IGB_EIMC, 1);
+       igb_write32(igb, IGB_EIMC, IGB_EICR_VEC0);
 }
 
 static void igb_irq_enable(struct igb *igb)
 {
-       igb_write32(igb, IGB_EIMS, 1);
+       igb_write32(igb, IGB_EIMS, IGB_EICR_VEC0);
 }
 
 static void igb_irq_clear(struct igb *igb)
 {
-       igb_read32(igb, IGB_EICR);
+       /*
+        * Use write-to-clear (datasheet 7.3.4.2).  In MSI-X mode with EIAC
+        * programmed, section 8.8.5 explicitly states "If any bits are set
+        * in EIAC, the EICR register should not be read", which rules out
+        * the read-to-clear path in 7.3.4.3.  Bits not in EIAC are still
+        * cleared by writing 1.
+        */
+       igb_write32(igb, IGB_EICR, 0xFFFFFFFF);
 }
 
 static void igb_memcpy_start(struct vfio_pci_device *device, iova_t src,
diff --git a/tools/testing/selftests/vfio/lib/drivers/igb/registers.h 
b/tools/testing/selftests/vfio/lib/drivers/igb/registers.h
index 9a8b0f830f89..a133a208ed08 100644
--- a/tools/testing/selftests/vfio/lib/drivers/igb/registers.h
+++ b/tools/testing/selftests/vfio/lib/drivers/igb/registers.h
@@ -3,6 +3,8 @@
 #ifndef _IGB_REGISTERS_H_
 #define _IGB_REGISTERS_H_
 
+#include <linux/bits.h>
+
 /* Register Offsets (Intel 82576EB Datasheet) */
 #define IGB_CTRL 0x00000 /* Device Control */
 #define IGB_STATUS 0x00008 /* Device Status */
@@ -76,12 +78,18 @@
 #define IGB_VMOLR_BAM 0x08000000 /* Broadcast Accept Mode */
 #define IGB_RAH_POOL_1 0x00040000 /* Pool 1 assignment */
 
-#define IGB_EIMS 0x01524 /* Extended Interrupt Mask Set */
 #define IGB_EICS 0x01520 /* Extended Interrupt Cause Set */
+#define IGB_EIMS 0x01524 /* Extended Interrupt Mask Set */
 #define IGB_EIMC 0x01528 /* Extended Interrupt Mask Clear */
+#define IGB_EIAC 0x0152C /* Extended Interrupt Auto Clear */
+#define IGB_EIAM 0x01530 /* Extended Interrupt Auto Mask Enable */
+#define IGB_EICR_VEC0 BIT(0) /* MSI-X cause/vector 0 */
 #define IGB_CTRL_GIO_MASTER_DISABLE (1 << 2) /* GIO Master Disable */
 #define IGB_STATUS_GIO_MASTER_ENABLE (1 << 19) /* GIO Master Enable */
 #define IGB_GPIE 0x01514 /* General Purpose Interrupt Enable */
+/* GPIE fields per 82576 datasheet section 7.3.2.11, Table 7-47 */
+#define IGB_GPIE_MULTIPLE_MSIX BIT(4)  /* Multi-vector MSI-X mode */
+#define IGB_GPIE_EIAME         BIT(30) /* Apply EIAM on MSI-X assertion */
 #define IGB_TXDCTL0_Q_EN (1 << 25) /* Transmit Queue Enable */
 #define IGB_RXDCTL0_Q_EN (1 << 25) /* Receive Queue Enable */
 #define IGB_MRQC 0x05818 /* Multiple Receive Queues Command */
@@ -99,7 +107,6 @@
 #define IGB_PHY_CTRL_FULL_DUPLEX       0x0100 /* bit 8 */
 #define IGB_PHY_CTRL_LOOPBACK          0x4000 /* bit 14 */
 
-#define IGB_GPIE_EIAME 0x10 /* Extended Interrupt Auto Mask Enable */
 #define IGB_IVAR_VALID 0x80 /* Valid bit for IVAR register */
 
 /*
-- 
2.54.0.794.g4f17f83d09-goog


Reply via email to