From: Manish Honap <[email protected]> The HDM region is a device region, not a BAR, so vfio_pci_zap_bars() leaves its PTEs in place. A runtime-PM entry, a D3 transition, or a reset would then leave the guest with live mappings into a quiesced device.
Add a zap hook, called alongside the BAR zap under memory_lock, that unmaps the window. The fault path already refuses to re-insert PFNs while the device is suspended or its Memory Space is disabled. Signed-off-by: Manish Honap <[email protected]> --- drivers/vfio/pci/cxl/vfio_cxl_core.c | 29 ++++++++++++++++++++++++++++ drivers/vfio/pci/vfio_pci_core.c | 8 ++++++++ include/linux/vfio_pci_core.h | 2 ++ 3 files changed, 39 insertions(+) diff --git a/drivers/vfio/pci/cxl/vfio_cxl_core.c b/drivers/vfio/pci/cxl/vfio_cxl_core.c index 0fb5ed5d86b7..f1c6bf06c408 100644 --- a/drivers/vfio/pci/cxl/vfio_cxl_core.c +++ b/drivers/vfio/pci/cxl/vfio_cxl_core.c @@ -530,6 +530,31 @@ static void vfio_cxl_release_device(struct vfio_pci_core_device *vdev) vdev->cxl = NULL; } +static void vfio_cxl_zap(struct vfio_pci_core_device *vdev) +{ + struct vfio_cxl_state *cxl = vdev->cxl; + + lockdep_assert_held_write(&vdev->memory_lock); + + if (!cxl) + return; + + /* + * Revoke the mapping so a later access re-faults. Do not touch hdm_valid + * here: zap also runs on a plain PCI Memory-Space disable, across which + * the committed HDM decoder stays valid (CXL.mem is not gated by PCI + * Memory-Space). hdm_valid tracks decoder validity and is cleared only by + * the paths that can leave the decoder unrestored (a failed reset or PM + * restore). A reset or D3 transition holds memory_lock for write while it + * runs, so no fault races the revoke, and a runtime-suspended device is + * caught by the pm_runtime_engaged check on the insert path. + */ + unmap_mapping_range(vdev->vdev.inode->i_mapping, + VFIO_PCI_INDEX_TO_OFFSET(VFIO_PCI_NUM_REGIONS + + cxl->hdm_region_idx), + range_len(&cxl->hpa_range), true); +} + static int vfio_cxl_open_device(struct vfio_pci_core_device *vdev) { struct vfio_cxl_state *cxl = vdev->cxl; @@ -594,6 +619,9 @@ static int vfio_cxl_open_device(struct vfio_pci_core_device *vdev) if (ret) goto err_free_shadows; + /* Remember where the HDM region landed so it can be zapped by index. */ + cxl->hdm_region_idx = vdev->num_regions - 1; + ret = vfio_pci_core_register_dev_region(vdev, VFIO_REGION_TYPE_CXL, VFIO_REGION_SUBTYPE_CXL_COMP_REGS, &vfio_cxl_comp_regops, cxl->hdm_len, @@ -738,6 +766,7 @@ static const struct vfio_cxl_ops vfio_cxl_ops = { .close_device = vfio_cxl_close_device, .config_read = vfio_cxl_config_read, .config_write = vfio_cxl_config_write, + .zap = vfio_cxl_zap, .owner = THIS_MODULE, }; diff --git a/drivers/vfio/pci/vfio_pci_core.c b/drivers/vfio/pci/vfio_pci_core.c index 77f8f39dd670..1a54f15d1c2c 100644 --- a/drivers/vfio/pci/vfio_pci_core.c +++ b/drivers/vfio/pci/vfio_pci_core.c @@ -1832,6 +1832,14 @@ void vfio_pci_zap_and_down_write_memory_lock(struct vfio_pci_core_device *vdev) { down_write(&vdev->memory_lock); vfio_pci_zap_bars(vdev); + /* + * The HDM region lives in the device-region offset range that + * vfio_pci_zap_bars() does not cover, so revoke it here too. Otherwise + * a runtime-PM entry, D3 transition, or reset would leave the guest + * with live mappings into a quiesced device. + */ + if (vdev->cxl_ops && vdev->cxl_ops->zap) + vdev->cxl_ops->zap(vdev); } u16 vfio_pci_memory_lock_and_enable(struct vfio_pci_core_device *vdev) diff --git a/include/linux/vfio_pci_core.h b/include/linux/vfio_pci_core.h index 294e95b5e881..8b93949d4484 100644 --- a/include/linux/vfio_pci_core.h +++ b/include/linux/vfio_pci_core.h @@ -76,6 +76,8 @@ struct vfio_cxl_ops { int count, __le32 *val); int (*config_write)(struct vfio_pci_core_device *vdev, int pos, int count, __le32 val); + /* Revoke the HDM mapping; paired with the BAR zap */ + void (*zap)(struct vfio_pci_core_device *vdev); /* Pinned per bound CXL device so vfio-cxl cannot unload under usage */ struct module *owner; -- 2.25.1

