> -----Original Message-----
> From: Richard Cheng <[email protected]>
> Sent: Thursday, September 17, 2026 2:18 PM
> To: Manish Honap <[email protected]>
> Cc: [email protected]; [email protected]; Ankit Agrawal <[email protected]>;
> [email protected]; [email protected]; [email protected];
> Srirangan Madhavan <[email protected]>; [email protected];
> [email protected]; [email protected]; [email protected];
> [email protected]; [email protected]; [email protected]; Yishai
> Hadas <[email protected]>; Shameer Kolothum Thodi
> <[email protected]>; [email protected]; [email protected];
> [email protected]; [email protected]; [email protected]; Neo Jia
> <[email protected]>; Krishnakant Jaju <[email protected]>; Vikram Sethi
> <[email protected]>; Zhi Wang <[email protected]>; linux-
> [email protected]; [email protected]; [email protected];
> [email protected]; [email protected]; linux-
> [email protected]; [email protected]
> Subject: Re: [PATCH v5 06/27] vfio/pci: Detect CXL devices and load the CXL
> provider on demand
> 
> On Thu, Sep 17, 2026 at 12:05:19AM +0800, [email protected] wrote:
> > From: Manish Honap <[email protected]>
> >
> > A CXL device needs the vfio-cxl callbacks, but pulling vfio-cxl and
> > the CXL core in unconditionally would bloat every vfio-pci setup.
> > Detect a CXL Type-2 device at bind and request_module("vfio-cxl") only
> > then, then hand the device to the registered ops.
> >
> > Each bound CXL device pins the provider through
> > vfio_pci_get_cxl_ops() (try_module_get) and drops it with
> > vfio_pci_put_cxl_ops() at release, so vfio-cxl can unload once no CXL
> > device is bound.
> >
> > If the provider is absent the device is driven as plain vfio-pci. A
> > built-in provider whose initcall has not run yet is waited for with
> > -EPROBE_DEFER; the deferred-probe machinery will retry the bind once
> > the provider registers.
> >
> > Assisted-by: LLM
> > Signed-off-by: Manish Honap <[email protected]>
> > ---
> >  drivers/vfio/pci/vfio_pci_core.c | 84
> ++++++++++++++++++++++++++++++++
> >  include/linux/vfio_pci_core.h    |  3 ++
> >  2 files changed, 87 insertions(+)
> >
> > diff --git a/drivers/vfio/pci/vfio_pci_core.c
> > b/drivers/vfio/pci/vfio_pci_core.c
> > index 2cc5dd20396c..9eede1e56ab5 100644
> > --- a/drivers/vfio/pci/vfio_pci_core.c
> > +++ b/drivers/vfio/pci/vfio_pci_core.c
> > @@ -2202,6 +2202,84 @@ void vfio_pci_core_unregister_cxl_ops(const
> > struct vfio_cxl_ops *ops)  }
> > EXPORT_SYMBOL_GPL(vfio_pci_core_unregister_cxl_ops);
> >
> > +static const struct vfio_cxl_ops *vfio_pci_get_cxl_ops(void) {
> > +   guard(rwsem_read)(&vfio_pci_cxl_ops_rwsem);
> > +
> > +   if (vfio_pci_cxl_ops && try_module_get(vfio_pci_cxl_ops->owner))
> > +           return vfio_pci_cxl_ops;
> > +
> > +   return NULL;
> > +}
> > +
> > +static void vfio_pci_put_cxl_ops(const struct vfio_cxl_ops *ops) {
> > +   module_put(ops->owner);
> > +}
> > +
> 
> Anything guarantees that we can finish devres cleanup before dropping
> module ref ?
> 
> This function drops the module ref during vfio_pci_remove(), while devres
> might still pending.
> 
> I see in patch 18 it register vfio_cxl_release_hpa() as a devres callback, 
> and it
> lives in vfio-cxl.
> If this drops the last module ref, another process could unload vfio-cxl 
> before
> devres invokes that callback.
> 
> Best regards,
> Richard Cheng.
> 

Yes, I think it is possible that a concurrent unload can free the module text
the callback points at.

For v6, I dropped the devm action entirely and released the exclusive HDM range
in the provider .release op instead.

vfio_pci_core_cxl_release() calls ->release() and then vfio_pci_put_cxl_ops(),
so the range is released synchronously before the module_put; this way no
vfio-cxl callback outlives the module reference.

Thanks,
Manish

> 
> 
> 
> > +/*
> > + * A CXL Type-2 device advertises both CXL.cache and CXL.mem in its CXL
> DVSEC.
> > + * pcie_is_cxl() is also true for Type-1 (cache only) and Type-3 (mem
> > +only)
> > + * devices, which the vfio-cxl provider does not handle, so confirm
> > +the Type-2
> > + * identity before engaging it.
> > + */
> > +static bool vfio_pci_is_cxl_type2(struct pci_dev *pdev) {
> > +   u16 dvsec, cap;
> > +
> > +   if (!pcie_is_cxl(pdev))
> > +           return false;
> > +
> > +   dvsec = pci_find_dvsec_capability(pdev, PCI_VENDOR_ID_CXL,
> > +                                     PCI_DVSEC_CXL_DEVICE);
> > +   if (!dvsec)
> > +           return false;
> > +
> > +   if (pci_read_config_word(pdev, dvsec + PCI_DVSEC_CXL_CAP, &cap))
> > +           return false;
> > +
> > +   return (cap & PCI_DVSEC_CXL_CACHE_CAPABLE) &&
> > +           (cap & PCI_DVSEC_CXL_MEM_CAPABLE);
> > +}
> > +
> > +/*
> > + * Load vfio-cxl on demand for a CXL Type-2 device and hand the
> > +device to its
> > + * ops. If the provider is absent the device is driven as plain
> > +vfio-pci; a
> > + * built-in provider whose initcall has not run yet is waited for
> > +with
> > + * -EPROBE_DEFER.
> > + */
> > +static int vfio_pci_core_cxl_init(struct vfio_pci_core_device *vdev)
> > +{
> > +   const struct vfio_cxl_ops *ops;
> > +   int ret;
> > +
> > +   if (!vfio_pci_is_cxl_type2(vdev->pdev))
> > +           return 0;
> > +
> > +   request_module("vfio-cxl");
> > +   ops = vfio_pci_get_cxl_ops();
> > +   if (!ops)
> > +           return IS_BUILTIN(CONFIG_VFIO_CXL) ? -EPROBE_DEFER : 0;
> > +
> > +   ret = ops->init(vdev);
> > +   if (ret) {
> > +           vfio_pci_put_cxl_ops(ops);
> > +           return ret;
> > +   }
> > +
> > +   vdev->cxl_ops = ops;
> > +   return 0;
> > +}
> > +
> > +static void vfio_pci_core_cxl_release(struct vfio_pci_core_device
> > +*vdev) {
> > +   if (!vdev->cxl_ops)
> > +           return;
> > +
> > +   vdev->cxl_ops->release(vdev);
> > +   vfio_pci_put_cxl_ops(vdev->cxl_ops);
> > +}
> > +
> >  int vfio_pci_core_init_dev(struct vfio_device *core_vdev)  {
> >     struct vfio_pci_core_device *vdev =
> > @@ -2223,6 +2301,10 @@ int vfio_pci_core_init_dev(struct vfio_device
> *core_vdev)
> >     init_rwsem(&vdev->memory_lock);
> >     xa_init(&vdev->ctx);
> >
> > +   ret = vfio_pci_core_cxl_init(vdev);
> > +   if (ret)
> > +           return ret;
> > +
> >     return 0;
> >  }
> >  EXPORT_SYMBOL_GPL(vfio_pci_core_init_dev);
> > @@ -2232,6 +2314,8 @@ void vfio_pci_core_release_dev(struct
> vfio_device *core_vdev)
> >     struct vfio_pci_core_device *vdev =
> >             container_of(core_vdev, struct vfio_pci_core_device, vdev);
> >
> > +   vfio_pci_core_cxl_release(vdev);
> > +
> >     mutex_destroy(&vdev->igate);
> >     mutex_destroy(&vdev->ioeventfds_lock);
> >     kfree(vdev->region);
> > diff --git a/include/linux/vfio_pci_core.h
> > b/include/linux/vfio_pci_core.h index 9fe0d1a3a370..7f3a2bcb5830
> > 100644
> > --- a/include/linux/vfio_pci_core.h
> > +++ b/include/linux/vfio_pci_core.h
> > @@ -29,6 +29,7 @@ struct vfio_pci_core_device;  struct
> > vfio_pci_region;  struct p2pdma_provider;  struct dma_buf_attachment;
> > +struct vfio_cxl_state;
> >
> >  struct vfio_pci_eventfd {
> >     struct eventfd_ctx      *ctx;
> > @@ -109,6 +110,8 @@ struct vfio_pci_core_device {
> >     struct vfio_device      vdev;
> >     struct pci_dev          *pdev;
> >     const struct vfio_pci_device_ops *pci_ops;
> > +   const struct vfio_cxl_ops *cxl_ops;
> > +   struct vfio_cxl_state   *cxl;
> >     void __iomem            *barmap[PCI_STD_NUM_BARS];
> >     bool
>       bar_mmap_supported[PCI_STD_NUM_BARS];
> >     /* Flags modified at runtime - dedicated storage unit */
> > --
> > 2.25.1
> >
> >

Reply via email to