io_uring: use proper references for fallback_req locking
[linux-2.6-microblaze.git] / drivers / vfio / vfio.c
index c848262..765e0e5 100644 (file)
@@ -875,11 +875,23 @@ EXPORT_SYMBOL_GPL(vfio_device_get_from_dev);
 static struct vfio_device *vfio_device_get_from_name(struct vfio_group *group,
                                                     char *buf)
 {
-       struct vfio_device *it, *device = NULL;
+       struct vfio_device *it, *device = ERR_PTR(-ENODEV);
 
        mutex_lock(&group->device_lock);
        list_for_each_entry(it, &group->device_list, group_next) {
-               if (!strcmp(dev_name(it->dev), buf)) {
+               int ret;
+
+               if (it->ops->match) {
+                       ret = it->ops->match(it->device_data, buf);
+                       if (ret < 0) {
+                               device = ERR_PTR(ret);
+                               break;
+                       }
+               } else {
+                       ret = !strcmp(dev_name(it->dev), buf);
+               }
+
+               if (ret) {
                        device = it;
                        vfio_device_get(device);
                        break;
@@ -1430,8 +1442,8 @@ static int vfio_group_get_device_fd(struct vfio_group *group, char *buf)
                return -EPERM;
 
        device = vfio_device_get_from_name(group, buf);
-       if (!device)
-               return -ENODEV;
+       if (IS_ERR(device))
+               return PTR_ERR(device);
 
        ret = device->ops->open(device->device_data);
        if (ret) {
@@ -1720,6 +1732,44 @@ struct vfio_group *vfio_group_get_external_user(struct file *filep)
 }
 EXPORT_SYMBOL_GPL(vfio_group_get_external_user);
 
+/**
+ * External user API, exported by symbols to be linked dynamically.
+ * The external user passes in a device pointer
+ * to verify that:
+ *     - A VFIO group is assiciated with the device;
+ *     - IOMMU is set for the group.
+ * If both checks passed, vfio_group_get_external_user_from_dev()
+ * increments the container user counter to prevent the VFIO group
+ * from disposal before external user exits and returns the pointer
+ * to the VFIO group.
+ *
+ * When the external user finishes using the VFIO group, it calls
+ * vfio_group_put_external_user() to release the VFIO group and
+ * decrement the container user counter.
+ *
+ * @dev [in]   : device
+ * Return error PTR or pointer to VFIO group.
+ */
+
+struct vfio_group *vfio_group_get_external_user_from_dev(struct device *dev)
+{
+       struct vfio_group *group;
+       int ret;
+
+       group = vfio_group_get_from_dev(dev);
+       if (!group)
+               return ERR_PTR(-ENODEV);
+
+       ret = vfio_group_add_container_user(group);
+       if (ret) {
+               vfio_group_put(group);
+               return ERR_PTR(ret);
+       }
+
+       return group;
+}
+EXPORT_SYMBOL_GPL(vfio_group_get_external_user_from_dev);
+
 void vfio_group_put_external_user(struct vfio_group *group)
 {
        vfio_group_try_dissolve_container(group);
@@ -1961,6 +2011,146 @@ err_unpin_pages:
 }
 EXPORT_SYMBOL(vfio_unpin_pages);
 
+/*
+ * Pin a set of guest IOVA PFNs and return their associated host PFNs for a
+ * VFIO group.
+ *
+ * The caller needs to call vfio_group_get_external_user() or
+ * vfio_group_get_external_user_from_dev() prior to calling this interface,
+ * so as to prevent the VFIO group from disposal in the middle of the call.
+ * But it can keep the reference to the VFIO group for several calls into
+ * this interface.
+ * After finishing using of the VFIO group, the caller needs to release the
+ * VFIO group by calling vfio_group_put_external_user().
+ *
+ * @group [in]         : VFIO group
+ * @user_iova_pfn [in] : array of user/guest IOVA PFNs to be pinned.
+ * @npage [in]         : count of elements in user_iova_pfn array.
+ *                       This count should not be greater
+ *                       VFIO_PIN_PAGES_MAX_ENTRIES.
+ * @prot [in]          : protection flags
+ * @phys_pfn [out]     : array of host PFNs
+ * Return error or number of pages pinned.
+ */
+int vfio_group_pin_pages(struct vfio_group *group,
+                        unsigned long *user_iova_pfn, int npage,
+                        int prot, unsigned long *phys_pfn)
+{
+       struct vfio_container *container;
+       struct vfio_iommu_driver *driver;
+       int ret;
+
+       if (!group || !user_iova_pfn || !phys_pfn || !npage)
+               return -EINVAL;
+
+       if (npage > VFIO_PIN_PAGES_MAX_ENTRIES)
+               return -E2BIG;
+
+       container = group->container;
+       driver = container->iommu_driver;
+       if (likely(driver && driver->ops->pin_pages))
+               ret = driver->ops->pin_pages(container->iommu_data,
+                                            user_iova_pfn, npage,
+                                            prot, phys_pfn);
+       else
+               ret = -ENOTTY;
+
+       return ret;
+}
+EXPORT_SYMBOL(vfio_group_pin_pages);
+
+/*
+ * Unpin a set of guest IOVA PFNs for a VFIO group.
+ *
+ * The caller needs to call vfio_group_get_external_user() or
+ * vfio_group_get_external_user_from_dev() prior to calling this interface,
+ * so as to prevent the VFIO group from disposal in the middle of the call.
+ * But it can keep the reference to the VFIO group for several calls into
+ * this interface.
+ * After finishing using of the VFIO group, the caller needs to release the
+ * VFIO group by calling vfio_group_put_external_user().
+ *
+ * @group [in]         : vfio group
+ * @user_iova_pfn [in] : array of user/guest IOVA PFNs to be unpinned.
+ * @npage [in]         : count of elements in user_iova_pfn array.
+ *                       This count should not be greater than
+ *                       VFIO_PIN_PAGES_MAX_ENTRIES.
+ * Return error or number of pages unpinned.
+ */
+int vfio_group_unpin_pages(struct vfio_group *group,
+                          unsigned long *user_iova_pfn, int npage)
+{
+       struct vfio_container *container;
+       struct vfio_iommu_driver *driver;
+       int ret;
+
+       if (!group || !user_iova_pfn || !npage)
+               return -EINVAL;
+
+       if (npage > VFIO_PIN_PAGES_MAX_ENTRIES)
+               return -E2BIG;
+
+       container = group->container;
+       driver = container->iommu_driver;
+       if (likely(driver && driver->ops->unpin_pages))
+               ret = driver->ops->unpin_pages(container->iommu_data,
+                                              user_iova_pfn, npage);
+       else
+               ret = -ENOTTY;
+
+       return ret;
+}
+EXPORT_SYMBOL(vfio_group_unpin_pages);
+
+
+/*
+ * This interface allows the CPUs to perform some sort of virtual DMA on
+ * behalf of the device.
+ *
+ * CPUs read/write from/into a range of IOVAs pointing to user space memory
+ * into/from a kernel buffer.
+ *
+ * As the read/write of user space memory is conducted via the CPUs and is
+ * not a real device DMA, it is not necessary to pin the user space memory.
+ *
+ * The caller needs to call vfio_group_get_external_user() or
+ * vfio_group_get_external_user_from_dev() prior to calling this interface,
+ * so as to prevent the VFIO group from disposal in the middle of the call.
+ * But it can keep the reference to the VFIO group for several calls into
+ * this interface.
+ * After finishing using of the VFIO group, the caller needs to release the
+ * VFIO group by calling vfio_group_put_external_user().
+ *
+ * @group [in]         : VFIO group
+ * @user_iova [in]     : base IOVA of a user space buffer
+ * @data [in]          : pointer to kernel buffer
+ * @len [in]           : kernel buffer length
+ * @write              : indicate read or write
+ * Return error code on failure or 0 on success.
+ */
+int vfio_dma_rw(struct vfio_group *group, dma_addr_t user_iova,
+               void *data, size_t len, bool write)
+{
+       struct vfio_container *container;
+       struct vfio_iommu_driver *driver;
+       int ret = 0;
+
+       if (!group || !data || len <= 0)
+               return -EINVAL;
+
+       container = group->container;
+       driver = container->iommu_driver;
+
+       if (likely(driver && driver->ops->dma_rw))
+               ret = driver->ops->dma_rw(container->iommu_data,
+                                         user_iova, data, len, write);
+       else
+               ret = -ENOTTY;
+
+       return ret;
+}
+EXPORT_SYMBOL(vfio_dma_rw);
+
 static int vfio_register_iommu_notifier(struct vfio_group *group,
                                        unsigned long *events,
                                        struct notifier_block *nb)