Introduce vfio_find_device() to discover a registered vfio_device from
vfio_device_class matching user-provided criteria.

To ensure safe lifecycle handling, vfio_find_device() acquires a
registration reference (device->refcount) via
vfio_device_try_get_registration() rather than relying solely on
device.kref taken in class_find_device(). Holding only kref allows
vendor modules to unload while external callers hold device pointers,
leading to a kernel panic on release. Callers must release the device
via vfio_device_put_registration().

Additionally, move refcount_set() prior to vfio_device_add() in
__vfio_register_dev() so device->refcount is initialized before the
device is published to class lookups, avoiding registration data races.

On vfio_device_add() failure, the error path drops the initial
registration reference and waits for completion to drain any reference
taken by a concurrent lookup before returning the error to the driver.

Signed-off-by: Vipin Sharma <[email protected]>
---
 drivers/vfio/vfio_main.c | 64 +++++++++++++++++++++++++++++++++++++---
 include/linux/vfio.h     |  2 ++
 2 files changed, 62 insertions(+), 4 deletions(-)

diff --git a/drivers/vfio/vfio_main.c b/drivers/vfio/vfio_main.c
index 2437af031169..e91fd83dc58d 100644
--- a/drivers/vfio/vfio_main.c
+++ b/drivers/vfio/vfio_main.c
@@ -13,6 +13,7 @@
 #include <linux/cdev.h>
 #include <linux/compat.h>
 #include <linux/device.h>
+#include <linux/device/class.h>
 #include <linux/fs.h>
 #include <linux/idr.h>
 #include <linux/iommu.h>
@@ -359,17 +360,20 @@ static int __vfio_register_dev(struct vfio_device *device,
                goto err_out;
        }
 
-       ret = vfio_device_add(device);
-       if (ret)
-               goto err_out;
-
        /* Refcounting can't start until the driver calls register */
        refcount_set(&device->refcount, 1);
 
+       ret = vfio_device_add(device);
+       if (ret)
+               goto err_add;
+
        vfio_device_group_register(device);
        vfio_device_debugfs_init(device);
 
        return 0;
+err_add:
+       vfio_device_put_registration(device);
+       wait_for_completion(&device->comp);
 err_out:
        vfio_device_remove_group(device);
        return ret;
@@ -1797,6 +1801,58 @@ int vfio_dma_rw(struct vfio_device *device, dma_addr_t 
iova, void *data,
 }
 EXPORT_SYMBOL(vfio_dma_rw);
 
+struct vfio_device_match_data {
+       const void *data;
+       device_match_t match;
+};
+
+static int vfio_device_match_fn(struct device *dev, const void *data)
+{
+       struct vfio_device *vdev = container_of(dev, struct vfio_device, 
device);
+       const struct vfio_device_match_data *mdata = data;
+
+       if (mdata->match(dev, mdata->data)) {
+               if (vfio_device_try_get_registration(vdev))
+                       return 1;
+       }
+       return 0;
+}
+
+/**
+ * vfio_find_device - Find a registered VFIO device matching search criteria
+ * @data: Context data passed to the @match function
+ * @match: Device match callback function
+ *
+ * Finds a registered VFIO device in the vfio_device_class matching the search
+ * criteria specified by @match.
+ *
+ * On success, this function acquires a registration reference on the matched
+ * device via vfio_device_try_get_registration(). This pins the device and
+ * prevents its vendor driver module from unloading or unbinding while the
+ * reference is held.
+ *
+ * Return: Pointer to the matching vfio_device on success, or NULL if no
+ * matching device is found. The caller MUST release the acquired registration
+ * reference using vfio_device_put_registration() when finished with the 
device.
+ */
+struct vfio_device *vfio_find_device(const void *data, device_match_t match)
+{
+       struct vfio_device_match_data mdata = { .data = data, .match = match };
+       struct device *device;
+
+       device = class_find_device(&vfio_device_class, NULL, &mdata, 
vfio_device_match_fn);
+       if (!device)
+               return NULL;
+
+       /*
+        * Drop standard kref reference. Caller holds reference from
+        * vfio_device_try_get_registration().
+        */
+       put_device(device);
+       return container_of(device, struct vfio_device, device);
+}
+EXPORT_SYMBOL_GPL(vfio_find_device);
+
 /*
  * Module/class support
  */
diff --git a/include/linux/vfio.h b/include/linux/vfio.h
index cf0d941d0410..42c3a9ae3a85 100644
--- a/include/linux/vfio.h
+++ b/include/linux/vfio.h
@@ -430,4 +430,6 @@ int vfio_virqfd_enable(void *opaque, int (*handler)(void *, 
void *),
 void vfio_virqfd_disable(struct virqfd **pvirqfd);
 void vfio_virqfd_flush_thread(struct virqfd **pvirqfd);
 
+struct vfio_device *vfio_find_device(const void *data, device_match_t match);
+
 #endif /* VFIO_H */
-- 
2.55.0.795.g602f6c329a-goog


Reply via email to