From: Alex Williamson 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 --- 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