[PATCH v6 19/31] vfio/cxl: Create the CXL memdev and set media ready at bind
From: mhonap
Date: Fri Oct 16 2026 - 12:42:59 EST
From: Manish Honap <mhonap@xxxxxxxxxx>
At bind, build the CXL memory device for the passed-through Type-2
accelerator so it joins the CXL topology and its HDM region resolves to a
host physical range. A Type-2 device has no mailbox, so there is no
media-ready register to poll: set media ready directly once the component
registers validate (mirroring drivers/net/ethernet/sfc/efx_cxl.c)
As per current vfio-cxl support, reject a device with:
- more than one HDM decoder
- interleaving enabled
- whose reset the host cannot service, including the CPU cache
invalidation that the reset needs
The CXL-core allocations are grouped with devres, in a helper that
returns on the first error, so a failed bind unwinds them: a failed init
leaves the device bound without CXL support, so devm would otherwise hold
them until unbind.
The device is not kept in D0. Low power loses the committed HDM
decoder, and the host restores the decoder configuration when the device
returns to D0. The contents of device memory across low power are the
user's to keep.
Take the bind-time copy of the cached HDM state with
pci_cxl_hdm_snapshot(), which needs no device lock: probe may run in
a worker on the device's NUMA node that does not own the lock.
Assisted-by: LLM
Signed-off-by: Manish Honap <mhonap@xxxxxxxxxx>
---
drivers/vfio/pci/cxl/vfio_cxl_core.c | 135 +++++++++++++++++++++++++++
1 file changed, 135 insertions(+)
diff --git a/drivers/vfio/pci/cxl/vfio_cxl_core.c b/drivers/vfio/pci/cxl/vfio_cxl_core.c
index ee49d68d689c..ef1fc715020e 100644
--- a/drivers/vfio/pci/cxl/vfio_cxl_core.c
+++ b/drivers/vfio/pci/cxl/vfio_cxl_core.c
@@ -5,17 +5,151 @@
* Copyright (c) 2026 NVIDIA Corporation & Affiliates
*/
+#include <linux/cleanup.h>
#include <linux/module.h>
#include <linux/pci.h>
+#include <linux/range.h>
+#include <linux/slab.h>
#include <linux/vfio_pci_core.h>
+#include <cxl/cxl.h>
+#include <cxl/hdm.h>
+#include <cxl/pci.h>
+
+/**
+ * struct vfio_cxl_state - per-device state for a vfio-cxl device
+ * @cxlds: CXL device state; kept first for devm_cxl_dev_state_create()
+ * @cxlmd: memory device joined to the CXL topology at bind
+ * @hpa_range: host physical range of the HDM region
+ * @hdm: copy of the HDM state cached at PCI enumeration, taken at bind
+ */
+struct vfio_cxl_state {
+ struct cxl_dev_state cxlds;
+ struct cxl_memdev *cxlmd;
+ struct range hpa_range;
+ struct cxl_hdm_info *hdm;
+};
+
+/*
+ * Create the CXL device state and join the device to the CXL topology. The
+ * caller groups these devres allocations, so return on the first error.
+ */
+static struct vfio_cxl_state *vfio_cxl_create_memdev(struct pci_dev *pdev,
+ u64 hdm_size)
+{
+ struct vfio_cxl_state *cxl;
+ struct cxl_memdev *cxlmd;
+ u64 serial;
+ u16 dvsec;
+ int ret;
+
+ dvsec = pci_find_dvsec_capability(pdev, PCI_VENDOR_ID_CXL,
+ PCI_DVSEC_CXL_DEVICE);
+ serial = pci_get_dsn(pdev);
+
+ cxl = devm_cxl_dev_state_create(&pdev->dev, CXL_DEVTYPE_DEVMEM, serial,
+ dvsec, struct vfio_cxl_state, cxlds,
+ false);
+ if (!cxl)
+ return ERR_PTR(-ENOMEM);
+
+ ret = cxl_pci_setup_regs(pdev, CXL_REGLOC_RBI_COMPONENT,
+ &cxl->cxlds.reg_map);
+ if (ret) {
+ pci_err(pdev, "vfio-cxl: no component registers\n");
+ return ERR_PTR(ret);
+ }
+
+ if (!cxl->cxlds.reg_map.component_map.hdm_decoder.valid) {
+ pci_err(pdev, "vfio-cxl: HDM decoder registers not found\n");
+ return ERR_PTR(-ENODEV);
+ }
+
+ /*
+ * A Type-2 accelerator has no mailbox and no media-ready register, so
+ * set media ready directly.
+ */
+ cxl->cxlds.media_ready = true;
+
+ ret = cxl_set_capacity(&cxl->cxlds, hdm_size);
+ if (ret)
+ return ERR_PTR(ret);
+
+ cxlmd = devm_cxl_probe_mem(&cxl->cxlds, &cxl->hpa_range);
+ if (IS_ERR(cxlmd))
+ return ERR_CAST(cxlmd);
+
+ cxl->cxlmd = cxlmd;
+ return cxl;
+}
static int vfio_cxl_init_device(struct vfio_pci_core_device *vdev)
{
+ struct pci_dev *pdev = vdev->pdev;
+ struct cxl_hdm_info *hdm __free(kfree) = NULL;
+ struct vfio_cxl_state *cxl;
+ u64 hdm_size;
+
+ /*
+ * pdev->hdm is cached at PCI enumeration, before any driver binds, so a
+ * device without it has no usable HDM decoder. Continue without CXL
+ * support rather than defer the bind forever. The cache belongs to the
+ * CXL core, so work from a copy. Probe may run in a worker on the
+ * device's NUMA node that does not own the device lock, so take the
+ * copy that needs none.
+ */
+ hdm = pci_cxl_hdm_snapshot(pdev);
+ if (IS_ERR(hdm))
+ return PTR_ERR(hdm);
+ if (!hdm)
+ return -ENODEV;
+
+ /* The guest drives one virtual decoder; multiple are unsupported. */
+ if (hdm->decoder_count != 1)
+ return -EOPNOTSUPP;
+
+ /* An interleaved decoder cannot be mapped 1:1 to the guest. */
+ if (hdm->settings[0].config.interleave_ways != 1)
+ return -EOPNOTSUPP;
+
+ /*
+ * The guest drives resets through the CXL Device DVSEC and polls the
+ * shadow for completion. If the host cannot service a function-scoped
+ * CXL reset, that request could never complete, so refuse the device
+ * rather than advertise a reset the guest would poll on forever. The
+ * check includes the CPU cache invalidation that the reset needs.
+ */
+ if (!pci_cxl_reset_capable(pdev))
+ return -EOPNOTSUPP;
+
+ hdm_size = range_len(&hdm->settings[0].config.hpa_range);
+ if (!hdm_size)
+ return -ENXIO;
+
+ /*
+ * Group the CXL-core allocations so a failure unwinds them here. A
+ * failed init leaves the device bound without CXL support, so devm
+ * would otherwise hold them until unbind.
+ */
+ if (!devres_open_group(&pdev->dev, NULL, GFP_KERNEL))
+ return -ENOMEM;
+
+ cxl = vfio_cxl_create_memdev(pdev, hdm_size);
+ if (IS_ERR(cxl)) {
+ devres_release_group(&pdev->dev, NULL);
+ return PTR_ERR(cxl);
+ }
+ devres_close_group(&pdev->dev, NULL);
+
+ cxl->hdm = no_free_ptr(hdm);
+ vdev->cxl = cxl;
+
return 0;
}
static void vfio_cxl_release_device(struct vfio_pci_core_device *vdev)
{
+ kfree(vdev->cxl->hdm);
+ vdev->cxl = NULL;
}
static int vfio_cxl_open_device(struct vfio_pci_core_device *vdev)
@@ -80,3 +214,4 @@ module_exit(vfio_cxl_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("VFIO support for CXL Type-2 devices");
+MODULE_IMPORT_NS("CXL");
--
2.53.0