[PATCH 3/8] selftests/vfio: igb: Program MSI-X interrupt routing
From: Alex Williamson
Date: Fri May 15 2026 - 18:06:33 EST
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 <alex.williamson@xxxxxxxxxx>
---
.../selftests/vfio/lib/drivers/igb/igb.c | 40 ++++++++++++++++---
.../vfio/lib/drivers/igb/registers.h | 9 ++++-
2 files changed, 41 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 594e51ba29f5..d44a08a36171 100644
--- a/tools/testing/selftests/vfio/lib/drivers/igb/igb.c
+++ b/tools/testing/selftests/vfio/lib/drivers/igb/igb.c
@@ -275,11 +275,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);
@@ -305,17 +326,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 c44788642522..139f1c2e6fdd 100644
--- a/tools/testing/selftests/vfio/lib/drivers/igb/registers.h
+++ b/tools/testing/selftests/vfio/lib/drivers/igb/registers.h
@@ -78,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 BIT(2) /* GIO Master Disable */
#define IGB_STATUS_GIO_MASTER_ENABLE BIT(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 BIT(25) /* Transmit Queue Enable */
#define IGB_RXDCTL0_Q_EN BIT(25) /* Receive Queue Enable */
#define IGB_MRQC 0x05818 /* Multiple Receive Queues Command */
@@ -101,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.51.0