Commit 4741f2e9 authored by Jason Gunthorpe's avatar Jason Gunthorpe
Browse files

vfio-iommufd: Support iommufd for emulated VFIO devices

Emulated VFIO devices are calling vfio_register_emulated_iommu_dev() and
consist of all the mdev drivers.

Like the physical drivers, support for iommufd is provided by the driver
supplying the correct standard ops. Provide ops from the core that
duplicate what vfio_register_emulated_iommu_dev() does.

Emulated drivers are where it is more likely to see variation in the
iommfd support ops. For instance IDXD will probably need to setup both a
iommfd_device context linked to a PASID and an iommufd_access context to
support all their mdev operations.

Link: https://lore.kernel.org/r/7-v4-42cd2eb0e3eb+335a-vfio_iommufd_jgg@nvidia.com


Reviewed-by: default avatarKevin Tian <kevin.tian@intel.com>
Reviewed-by: default avatarAlex Williamson <alex.williamson@redhat.com>
Tested-by: default avatarAlex Williamson <alex.williamson@redhat.com>
Tested-by: default avatarNicolin Chen <nicolinc@nvidia.com>
Tested-by: default avatarYi Liu <yi.l.liu@intel.com>
Tested-by: default avatarLixiao Yang <lixiao.yang@intel.com>
Tested-by: default avatarMatthew Rosato <mjrosato@linux.ibm.com>
Tested-by: default avatarYu He <yu.he@intel.com>
Signed-off-by: default avatarJason Gunthorpe <jgg@nvidia.com>
parent a4d1f91d
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -1484,6 +1484,9 @@ static const struct vfio_device_ops intel_vgpu_dev_ops = {
	.mmap		= intel_vgpu_mmap,
	.ioctl		= intel_vgpu_ioctl,
	.dma_unmap	= intel_vgpu_dma_unmap,
	.bind_iommufd	= vfio_iommufd_emulated_bind,
	.unbind_iommufd = vfio_iommufd_emulated_unbind,
	.attach_ioas	= vfio_iommufd_emulated_attach_ioas,
};

static int intel_vgpu_probe(struct mdev_device *mdev)
+3 −0
Original line number Diff line number Diff line
@@ -588,6 +588,9 @@ static const struct vfio_device_ops vfio_ccw_dev_ops = {
	.ioctl = vfio_ccw_mdev_ioctl,
	.request = vfio_ccw_mdev_request,
	.dma_unmap = vfio_ccw_dma_unmap,
	.bind_iommufd = vfio_iommufd_emulated_bind,
	.unbind_iommufd = vfio_iommufd_emulated_unbind,
	.attach_ioas = vfio_iommufd_emulated_attach_ioas,
};

struct mdev_driver vfio_ccw_mdev_driver = {
+3 −0
Original line number Diff line number Diff line
@@ -1805,6 +1805,9 @@ static const struct vfio_device_ops vfio_ap_matrix_dev_ops = {
	.close_device = vfio_ap_mdev_close_device,
	.ioctl = vfio_ap_mdev_ioctl,
	.dma_unmap = vfio_ap_mdev_dma_unmap,
	.bind_iommufd = vfio_iommufd_emulated_bind,
	.unbind_iommufd = vfio_iommufd_emulated_unbind,
	.attach_ioas = vfio_iommufd_emulated_attach_ioas,
};

static struct mdev_driver vfio_ap_matrix_driver = {
+19 −91
Original line number Diff line number Diff line
@@ -540,113 +540,41 @@ void vfio_group_unuse_container(struct vfio_group *group)
	fput(group->opened_file);
}

/*
 * Pin contiguous user pages and return their associated host pages for local
 * domain only.
 * @device [in]  : device
 * @iova [in]    : starting IOVA of user pages to be pinned.
 * @npage [in]   : count of pages to be pinned.  This count should not
 *		   be greater than VFIO_PIN_PAGES_MAX_ENTRIES.
 * @prot [in]    : protection flags
 * @pages[out]   : array of host pages
 * Return error or number of pages pinned.
 *
 * A driver may only call this function if the vfio_device was created
 * by vfio_register_emulated_iommu_dev().
 */
int vfio_pin_pages(struct vfio_device *device, dma_addr_t iova,
int vfio_container_pin_pages(struct vfio_container *container,
			     struct iommu_group *iommu_group, dma_addr_t iova,
			     int npage, int prot, struct page **pages)
{
	struct vfio_container *container;
	struct vfio_group *group = device->group;
	struct vfio_iommu_driver *driver;
	int ret;

	if (!pages || !npage || !vfio_assert_device_open(device))
		return -EINVAL;
	struct vfio_iommu_driver *driver = container->iommu_driver;

	if (npage > VFIO_PIN_PAGES_MAX_ENTRIES)
		return -E2BIG;

	/* group->container cannot change while a vfio device is open */
	container = group->container;
	driver = container->iommu_driver;
	if (likely(driver && driver->ops->pin_pages))
		ret = driver->ops->pin_pages(container->iommu_data,
					     group->iommu_group, iova,
	if (unlikely(!driver || !driver->ops->pin_pages))
		return -ENOTTY;
	return driver->ops->pin_pages(container->iommu_data, iommu_group, iova,
				      npage, prot, pages);
	else
		ret = -ENOTTY;

	return ret;
}
EXPORT_SYMBOL(vfio_pin_pages);

/*
 * Unpin contiguous host pages for local domain only.
 * @device [in]  : device
 * @iova [in]    : starting address of user pages to be unpinned.
 * @npage [in]   : count of pages to be unpinned.  This count should not
 *                 be greater than VFIO_PIN_PAGES_MAX_ENTRIES.
 */
void vfio_unpin_pages(struct vfio_device *device, dma_addr_t iova, int npage)
void vfio_container_unpin_pages(struct vfio_container *container,
				dma_addr_t iova, int npage)
{
	struct vfio_container *container;
	struct vfio_iommu_driver *driver;

	if (WARN_ON(npage <= 0 || npage > VFIO_PIN_PAGES_MAX_ENTRIES))
		return;

	if (WARN_ON(!vfio_assert_device_open(device)))
		return;

	/* group->container cannot change while a vfio device is open */
	container = device->group->container;
	driver = container->iommu_driver;

	driver->ops->unpin_pages(container->iommu_data, iova, npage);
	container->iommu_driver->ops->unpin_pages(container->iommu_data, iova,
						  npage);
}
EXPORT_SYMBOL(vfio_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.
 *
 * @device [in]		: VFIO device
 * @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_device *device, dma_addr_t iova, void *data,
		size_t len, bool write)
int vfio_container_dma_rw(struct vfio_container *container, dma_addr_t iova,
			  void *data, size_t len, bool write)
{
	struct vfio_container *container;
	struct vfio_iommu_driver *driver;
	int ret = 0;

	if (!data || len <= 0 || !vfio_assert_device_open(device))
		return -EINVAL;

	/* group->container cannot change while a vfio device is open */
	container = device->group->container;
	driver = container->iommu_driver;
	struct vfio_iommu_driver *driver = container->iommu_driver;

	if (likely(driver && driver->ops->dma_rw))
		ret = driver->ops->dma_rw(container->iommu_data,
					  iova, data, len, write);
	else
		ret = -ENOTTY;
	return ret;
	if (unlikely(!driver || !driver->ops->dma_rw))
		return -ENOTTY;
	return driver->ops->dma_rw(container->iommu_data, iova, data, len,
				   write);
}
EXPORT_SYMBOL(vfio_dma_rw);

int __init vfio_container_init(void)
{
+58 −0
Original line number Diff line number Diff line
@@ -98,3 +98,61 @@ int vfio_iommufd_physical_attach_ioas(struct vfio_device *vdev, u32 *pt_id)
	return 0;
}
EXPORT_SYMBOL_GPL(vfio_iommufd_physical_attach_ioas);

/*
 * The emulated standard ops mean that vfio_device is going to use the
 * "mdev path" and will call vfio_pin_pages()/vfio_dma_rw(). Drivers using this
 * ops set should call vfio_register_emulated_iommu_dev().
 */

static void vfio_emulated_unmap(void *data, unsigned long iova,
				unsigned long length)
{
	struct vfio_device *vdev = data;

	vdev->ops->dma_unmap(vdev, iova, length);
}

static const struct iommufd_access_ops vfio_user_ops = {
	.needs_pin_pages = 1,
	.unmap = vfio_emulated_unmap,
};

int vfio_iommufd_emulated_bind(struct vfio_device *vdev,
			       struct iommufd_ctx *ictx, u32 *out_device_id)
{
	lockdep_assert_held(&vdev->dev_set->lock);

	vdev->iommufd_ictx = ictx;
	iommufd_ctx_get(ictx);
	return 0;
}
EXPORT_SYMBOL_GPL(vfio_iommufd_emulated_bind);

void vfio_iommufd_emulated_unbind(struct vfio_device *vdev)
{
	lockdep_assert_held(&vdev->dev_set->lock);

	if (vdev->iommufd_access) {
		iommufd_access_destroy(vdev->iommufd_access);
		vdev->iommufd_access = NULL;
	}
	iommufd_ctx_put(vdev->iommufd_ictx);
	vdev->iommufd_ictx = NULL;
}
EXPORT_SYMBOL_GPL(vfio_iommufd_emulated_unbind);

int vfio_iommufd_emulated_attach_ioas(struct vfio_device *vdev, u32 *pt_id)
{
	struct iommufd_access *user;

	lockdep_assert_held(&vdev->dev_set->lock);

	user = iommufd_access_create(vdev->iommufd_ictx, *pt_id, &vfio_user_ops,
				     vdev);
	if (IS_ERR(user))
		return PTR_ERR(user);
	vdev->iommufd_access = user;
	return 0;
}
EXPORT_SYMBOL_GPL(vfio_iommufd_emulated_attach_ioas);
Loading