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]>
---
 tools/testing/selftests/vfio/lib/drivers/igb/igb.c | 40 ++++++++++++++++++----
 1 file changed, 34 insertions(+), 6 deletions(-)

diff --git a/tools/testing/selftests/vfio/lib/drivers/igb/igb.c 
b/tools/testing/selftests/vfio/lib/drivers/igb/igb.c
index f4f6d2ee262d..637d05e872de 100644
--- a/tools/testing/selftests/vfio/lib/drivers/igb/igb.c
+++ b/tools/testing/selftests/vfio/lib/drivers/igb/igb.c
@@ -280,11 +280,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, E1000_GPIE, E1000_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, E1000_GPIE, E1000_GPIE_MSIX_MODE | E1000_GPIE_EIAME);
+       igb_write32(igb, E1000_EIAC, E1000_EICR_RX_QUEUE0);
+       igb_write32(igb, E1000_EIAM, E1000_EICR_RX_QUEUE0);
 
        /* Enable interrupts on vector 0 */
-       igb_write32(igb, E1000_EIMS, 1);
+       igb_write32(igb, E1000_EIMS, E1000_EICR_RX_QUEUE0);
 
        /* Map vector 0 to interrupt cause 0 and mark it valid */
        igb_write32(igb, E1000_IVAR0, E1000_IVAR_VALID);
@@ -310,17 +331,24 @@ static void igb_remove(struct vfio_pci_device *device)
 
 static void igb_irq_disable(struct igb *igb)
 {
-       igb_write32(igb, E1000_EIMC, 1);
+       igb_write32(igb, E1000_EIMC, E1000_EICR_RX_QUEUE0);
 }
 
 static void igb_irq_enable(struct igb *igb)
 {
-       igb_write32(igb, E1000_EIMS, 1);
+       igb_write32(igb, E1000_EIMS, E1000_EICR_RX_QUEUE0);
 }
 
 static void igb_irq_clear(struct igb *igb)
 {
-       igb_read32(igb, E1000_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, E1000_EICR, 0xFFFFFFFF);
 }
 
 static void igb_memcpy_start(struct vfio_pci_device *device, iova_t src,

-- 
2.55.0.795.g602f6c329a-goog


Reply via email to