Direct mmap-backed BAR accesses are not reliable while the PE is undergoing EEH reset. Temporarily disable the mmap-backed BAR subregions so accesses fall back to the VFIO region read/write path during the reset window.
After VFIO_EEH_PE_CONFIGURE succeeds, restore normal VFIO mmap behavior. To keep VFIOPCIDevice internals encapsulated and avoid direct manipulation of vdev->intx.mmap_timer and vdev->bars[] from hw/ppc/spapr_pci_vfio.c, introduce two VFIO PCI helpers in hw/vfio/pci.c: - vfio_pci_eeh_pre_reset(): cancels any pending INTx mmap re-enable timer and disables BAR mmaps across all BAR regions. - vfio_pci_eeh_post_configure(): restores normal VFIO mmap policy. If INTx is still pending, the INTx mmap timer is re-armed so mmap remains disabled until the quiet period completes; otherwise, BAR mmaps are immediately re-enabled. The pSeries EEH handlers in hw/ppc/spapr_pci_vfio.c call vfio_pci_eeh_pre_reset() during the pre-reset device preparation (after MSI-X teardown) and call vfio_pci_eeh_post_configure() only after VFIO_EEH_PE_CONFIGURE succeeds. On configure failure, BAR mmaps remain disabled. RTAS_SLOT_RESET_DEACTIVATE does not invoke either helper. Signed-off-by: Narayana Murty N <[email protected]> --- hw/ppc/spapr_pci_vfio.c | 60 ++++++++++++++++++++++++++++++++++++++--- hw/vfio/pci.c | 32 ++++++++++++++++++++++ hw/vfio/pci.h | 2 ++ 3 files changed, 90 insertions(+), 4 deletions(-) diff --git a/hw/ppc/spapr_pci_vfio.c b/hw/ppc/spapr_pci_vfio.c index 1fa8847031..ab78489fa5 100644 --- a/hw/ppc/spapr_pci_vfio.c +++ b/hw/ppc/spapr_pci_vfio.c @@ -254,8 +254,8 @@ int spapr_phb_vfio_eeh_get_state(SpaprPhbState *sphb, int *state) * leaving the shadow intact. */ static void spapr_phb_vfio_eeh_prepare_dev(PCIBus *bus, - PCIDevice *pdev, - void *opaque) + PCIDevice *pdev, + void *opaque) { uint16_t flags; @@ -263,13 +263,27 @@ static void spapr_phb_vfio_eeh_prepare_dev(PCIBus *bus, return; } + /* + * Disable MSI-X through the VFIO config-write path so the + * active interrupt routes are released while preserving the + * pSeries RTAS-managed MSI-X software shadow. + */ if (msix_enabled(pdev)) { - flags = pci_get_word(pdev->config + pdev->msix_cap + PCI_MSIX_FLAGS); + flags = pci_get_word(pdev->config + + pdev->msix_cap + PCI_MSIX_FLAGS); + flags &= ~PCI_MSIX_FLAGS_ENABLE; + pci_host_config_write_common(pdev, pdev->msix_cap + PCI_MSIX_FLAGS, - pci_config_size(pdev), flags, 2); + pci_config_size(pdev), + flags, 2); } + + /* + * This must be the final preparation step before the PE reset. + */ + vfio_pci_eeh_pre_reset(pdev); } static void spapr_phb_vfio_eeh_prepare_bus(PCIBus *bus, void *opaque) @@ -286,6 +300,37 @@ static void spapr_phb_vfio_eeh_pre_reset(SpaprPhbState *sphb) pci_for_each_bus(phb->bus, spapr_phb_vfio_eeh_prepare_bus, NULL); } +/* + * Restore normal VFIO mmap policy for a single VFIO PCI device after a + * successful EEH PE configure. Called only on the configure success path; + * on failure mmaps remain disabled until the next hot/fundamental reset + * attempt. + */ +static void spapr_phb_vfio_eeh_post_configure_dev(PCIBus *bus, + PCIDevice *pdev, + void *opaque) +{ + if (!object_dynamic_cast(OBJECT(pdev), TYPE_VFIO_PCI_DEVICE)) { + return; + } + + vfio_pci_eeh_post_configure(pdev); +} + +static void spapr_phb_vfio_eeh_post_configure_bus(PCIBus *bus, void *opaque) +{ + pci_for_each_device_under_bus(bus, + spapr_phb_vfio_eeh_post_configure_dev, + NULL); +} + +static void spapr_phb_vfio_eeh_post_configure(SpaprPhbState *sphb) +{ + PCIHostState *phb = PCI_HOST_BRIDGE(sphb); + + pci_for_each_bus(phb->bus, spapr_phb_vfio_eeh_post_configure_bus, NULL); +} + int spapr_phb_vfio_eeh_reset(SpaprPhbState *sphb, int option) { uint32_t op; @@ -293,6 +338,11 @@ int spapr_phb_vfio_eeh_reset(SpaprPhbState *sphb, int option) switch (option) { case RTAS_SLOT_RESET_DEACTIVATE: + /* + * Deactivate does not perform a full PE reset; BAR mmaps were already + * disabled by the preceding HOT or FUNDAMENTAL reset call and must not + * be re-enabled here. + */ op = VFIO_EEH_PE_RESET_DEACTIVATE; break; case RTAS_SLOT_RESET_HOT: @@ -324,6 +374,8 @@ int spapr_phb_vfio_eeh_configure(SpaprPhbState *sphb) return RTAS_OUT_PARAM_ERROR; } + spapr_phb_vfio_eeh_post_configure(sphb); + return RTAS_OUT_SUCCESS; } diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 428ab2f069..931df22add 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -1949,6 +1949,38 @@ static void vfio_mmap_set_enabled(VFIOPCIDevice *vdev, bool enabled) } } +/* + * Prevent a pending INTx mmap timer from restoring BAR mmap while + * the PE is undergoing EEH reset. Keep BAR accesses on the VFIO + * region read/write path until PE configuration completes. + */ +void vfio_pci_eeh_pre_reset(PCIDevice *pdev) +{ + VFIOPCIDevice *vdev = VFIO_PCI_DEVICE(pdev); + + if (vdev->intx.mmap_timer) { + timer_del(vdev->intx.mmap_timer); + } + + vfio_mmap_set_enabled(vdev, false); +} + +void vfio_pci_eeh_post_configure(PCIDevice *pdev) +{ + VFIOPCIDevice *vdev = VFIO_PCI_DEVICE(pdev); + + if (vdev->interrupt == VFIO_INT_INTx && vdev->intx.pending) { + if (vdev->intx.mmap_timer && vdev->intx.mmap_timeout) { + timer_mod(vdev->intx.mmap_timer, + qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + + vdev->intx.mmap_timeout); + } + return; + } + + vfio_mmap_set_enabled(vdev, true); +} + static void vfio_bar_prepare(VFIOPCIDevice *vdev, int nr) { VFIOBAR *bar = &vdev->bars[nr]; diff --git a/hw/vfio/pci.h b/hw/vfio/pci.h index c9ab949870..63fa378fbc 100644 --- a/hw/vfio/pci.h +++ b/hw/vfio/pci.h @@ -266,6 +266,8 @@ struct vfio_pci_hot_reset_info; void vfio_pci_pre_reset(VFIOPCIDevice *vdev); void vfio_pci_post_reset(VFIOPCIDevice *vdev); +void vfio_pci_eeh_pre_reset(PCIDevice *pdev); +void vfio_pci_eeh_post_configure(PCIDevice *pdev); bool vfio_pci_host_match(PCIHostDeviceAddress *addr, const char *name); int vfio_pci_get_pci_hot_reset_info(VFIOPCIDevice *vdev, struct vfio_pci_hot_reset_info **info_p); -- 2.54.0
