Commit 3af91771 authored by Christoph Hellwig's avatar Christoph Hellwig Committed by Alex Williamson
Browse files

vfio: refactor noiommu group creation



Split the actual noiommu group creation from vfio_iommu_group_get into a
new helper, and open code the rest of vfio_iommu_group_get in its only
caller.  This creates an entirely separate and clear code path for the
noiommu group creation.

Signed-off-by: default avatarChristoph Hellwig <hch@lst.de>
Reviewed-by: default avatarJason Gunthorpe <jgg@nvidia.com>
Reviewed-by: default avatarKevin Tian <kevin.tian@intel.com>
Link: https://lore.kernel.org/r/20210924155705.4258-6-hch@lst.de


Signed-off-by: default avatarAlex Williamson <alex.williamson@redhat.com>
parent 1362591f
Loading
Loading
Loading
Loading
+52 −52
Original line number Diff line number Diff line
@@ -169,50 +169,6 @@ static void vfio_release_device_set(struct vfio_device *device)
	xa_unlock(&vfio_device_set_xa);
}

static struct iommu_group *vfio_iommu_group_get(struct device *dev)
{
	struct iommu_group *group;
	int __maybe_unused ret;

	group = iommu_group_get(dev);

#ifdef CONFIG_VFIO_NOIOMMU
	/*
	 * With noiommu enabled, an IOMMU group will be created for a device
	 * that doesn't already have one and doesn't have an iommu_ops on their
	 * bus.  We set iommudata simply to be able to identify these groups
	 * as special use and for reclamation later.
	 */
	if (group || !noiommu || iommu_present(dev->bus))
		return group;

	group = iommu_group_alloc();
	if (IS_ERR(group))
		return NULL;

	iommu_group_set_name(group, "vfio-noiommu");
	iommu_group_set_iommudata(group, &noiommu, NULL);
	ret = iommu_group_add_device(group, dev);
	if (ret) {
		iommu_group_put(group);
		return NULL;
	}

	/*
	 * Where to taint?  At this point we've added an IOMMU group for a
	 * device that is not backed by iommu_ops, therefore any iommu_
	 * callback using iommu_ops can legitimately Oops.  So, while we may
	 * be about to give a DMA capable device to a user without IOMMU
	 * protection, which is clearly taint-worthy, let's go ahead and do
	 * it here.
	 */
	add_taint(TAINT_USER, LOCKDEP_STILL_OK);
	dev_warn(dev, "Adding kernel taint for vfio-noiommu group on device\n");
#endif

	return group;
}

#ifdef CONFIG_VFIO_NOIOMMU
static void *vfio_noiommu_open(unsigned long arg)
{
@@ -823,12 +779,61 @@ void vfio_uninit_group_dev(struct vfio_device *device)
}
EXPORT_SYMBOL_GPL(vfio_uninit_group_dev);

struct vfio_group *vfio_group_find_or_alloc(struct device *dev)
#ifdef CONFIG_VFIO_NOIOMMU
static struct vfio_group *vfio_noiommu_group_alloc(struct device *dev)
{
	struct iommu_group *iommu_group;
	struct vfio_group *group;
	int ret;

	iommu_group = iommu_group_alloc();
	if (IS_ERR(iommu_group))
		return ERR_CAST(iommu_group);

	iommu_group_set_name(iommu_group, "vfio-noiommu");
	iommu_group_set_iommudata(iommu_group, &noiommu, NULL);
	ret = iommu_group_add_device(iommu_group, dev);
	if (ret)
		goto out_put_group;

	group = vfio_create_group(iommu_group);
	if (IS_ERR(group)) {
		ret = PTR_ERR(group);
		goto out_remove_device;
	}

	return group;

out_remove_device:
	iommu_group_remove_device(dev);
out_put_group:
	iommu_group_put(iommu_group);
	return ERR_PTR(ret);
}
#endif

static struct vfio_group *vfio_group_find_or_alloc(struct device *dev)
{
	struct iommu_group *iommu_group;
	struct vfio_group *group;

	iommu_group = vfio_iommu_group_get(dev);
	iommu_group = iommu_group_get(dev);
#ifdef CONFIG_VFIO_NOIOMMU
	if (!iommu_group && noiommu && !iommu_present(dev->bus)) {
		/*
		 * With noiommu enabled, create an IOMMU group for devices that
		 * don't already have one and don't have an iommu_ops on their
		 * bus.  Taint the kernel because we're about to give a DMA
		 * capable device to a user without IOMMU protection.
		 */
		group = vfio_noiommu_group_alloc(dev);
		if (!IS_ERR(group)) {
			add_taint(TAINT_USER, LOCKDEP_STILL_OK);
			dev_warn(dev, "Adding kernel taint for vfio-noiommu group on device\n");
		}
		return group;
	}
#endif
	if (!iommu_group)
		return ERR_PTR(-EINVAL);

@@ -840,14 +845,9 @@ struct vfio_group *vfio_group_find_or_alloc(struct device *dev)
	/* a newly created vfio_group keeps the reference. */
	group = vfio_create_group(iommu_group);
	if (IS_ERR(group))
		goto out_remove;
		goto out_put;
	return group;

out_remove:
#ifdef CONFIG_VFIO_NOIOMMU
	if (iommu_group_get_iommudata(iommu_group) == &noiommu)
		iommu_group_remove_device(dev);
#endif
out_put:
	iommu_group_put(iommu_group);
	return group;