vfio: avoid inefficient operations on VFIO group in vfio_pin/unpin_pages
authorYan Zhao <yan.y.zhao@intel.com>
Tue, 24 Mar 2020 15:27:57 +0000 (09:27 -0600)
committerAlex Williamson <alex.williamson@redhat.com>
Tue, 24 Mar 2020 15:27:57 +0000 (09:27 -0600)
vfio_group_pin_pages() and vfio_group_unpin_pages() are introduced to
avoid inefficient search/check/ref/deref opertions associated with VFIO
group as those in each calling into vfio_pin_pages() and
vfio_unpin_pages().

VFIO group is taken as arg directly. The callers combine
search/check/ref/deref operations associated with VFIO group by calling
vfio_group_get_external_user()/vfio_group_get_external_user_from_dev()
beforehand, and vfio_group_put_external_user() afterwards.

Suggested-by: Alex Williamson <alex.williamson@redhat.com>
Signed-off-by: Yan Zhao <yan.y.zhao@intel.com>
Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
drivers/vfio/vfio.c
include/linux/vfio.h

index 6997f71..210fcf4 100644 (file)
@@ -1999,6 +1999,97 @@ 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
index 34b2fdf..be2bd35 100644 (file)
@@ -111,6 +111,12 @@ extern int vfio_pin_pages(struct device *dev, unsigned long *user_pfn,
 extern int vfio_unpin_pages(struct device *dev, unsigned long *user_pfn,
                            int npage);
 
+extern int vfio_group_pin_pages(struct vfio_group *group,
+                               unsigned long *user_iova_pfn, int npage,
+                               int prot, unsigned long *phys_pfn);
+extern int vfio_group_unpin_pages(struct vfio_group *group,
+                                 unsigned long *user_iova_pfn, int npage);
+
 extern int vfio_dma_rw(struct vfio_group *group, dma_addr_t user_iova,
                       void *data, size_t len, bool write);