Commit 2fb69c60 authored by Yi Liu's avatar Yi Liu Committed by Jason Gunthorpe
Browse files

iommufd: Support pasid attach/replace

This extends the below APIs to support PASID. Device drivers to manage pasid
attach/replace/detach.

    int iommufd_device_attach(struct iommufd_device *idev,
			      ioasid_t pasid, u32 *pt_id);
    int iommufd_device_replace(struct iommufd_device *idev,
			       ioasid_t pasid, u32 *pt_id);
    void iommufd_device_detach(struct iommufd_device *idev,
			       ioasid_t pasid);

The pasid operations share underlying attach/replace/detach infrastructure
with the device operations, but still have some different implications:

 - no reserved region per pasid otherwise SVA architecture is already
   broken (CPU address space doesn't count device reserved regions);

 - accordingly no sw_msi trick;

Cache coherency enforcement is still applied to pasid operations since
it is about memory accesses post page table walking (no matter the walk
is per RID or per PASID).

Link: https://patch.msgid.link/r/20250321171940.7213-12-yi.l.liu@intel.com


Reviewed-by: default avatarJason Gunthorpe <jgg@nvidia.com>
Signed-off-by: default avatarKevin Tian <kevin.tian@intel.com>
Reviewed-by: default avatarNicolin Chen <nicolinc@nvidia.com>
Signed-off-by: default avatarYi Liu <yi.l.liu@intel.com>
Tested-by: default avatarNicolin Chen <nicolinc@nvidia.com>
Signed-off-by: default avatarJason Gunthorpe <jgg@nvidia.com>
parent ff3f014e
Loading
Loading
Loading
Loading
+37 −22
Original line number Diff line number Diff line
@@ -428,9 +428,12 @@ static int iommufd_hwpt_attach_device(struct iommufd_hw_pagetable *hwpt,
	}

	handle->idev = idev;
	WARN_ON(pasid != IOMMU_NO_PASID);
	if (pasid == IOMMU_NO_PASID)
		rc = iommu_attach_group_handle(hwpt->domain, idev->igroup->group,
					       &handle->handle);
	else
		rc = iommu_attach_device_pasid(hwpt->domain, idev->dev, pasid,
					       &handle->handle);
	if (rc)
		goto out_disable_iopf;

@@ -464,10 +467,12 @@ static void iommufd_hwpt_detach_device(struct iommufd_hw_pagetable *hwpt,
{
	struct iommufd_attach_handle *handle;

	WARN_ON(pasid != IOMMU_NO_PASID);

	handle = iommufd_device_get_attach_handle(idev, pasid);
	if (pasid == IOMMU_NO_PASID)
		iommu_detach_group_handle(hwpt->domain, idev->igroup->group);
	else
		iommu_detach_device_pasid(hwpt->domain, idev->dev, pasid);

	if (hwpt->fault) {
		iommufd_auto_response_faults(hwpt, handle);
		iommufd_fault_iopf_disable(idev);
@@ -483,8 +488,6 @@ static int iommufd_hwpt_replace_device(struct iommufd_device *idev,
	struct iommufd_attach_handle *handle, *old_handle;
	int rc;

	WARN_ON(pasid != IOMMU_NO_PASID);

	rc = iommufd_hwpt_pasid_compat(hwpt, idev, pasid);
	if (rc)
		return rc;
@@ -502,8 +505,12 @@ static int iommufd_hwpt_replace_device(struct iommufd_device *idev,
	}

	handle->idev = idev;
	rc = iommu_replace_group_handle(idev->igroup->group, hwpt->domain,
					&handle->handle);
	if (pasid == IOMMU_NO_PASID)
		rc = iommu_replace_group_handle(idev->igroup->group,
						hwpt->domain, &handle->handle);
	else
		rc = iommu_replace_device_pasid(hwpt->domain, idev->dev,
						pasid, &handle->handle);
	if (rc)
		goto out_disable_iopf;

@@ -904,22 +911,25 @@ static int iommufd_device_change_pt(struct iommufd_device *idev,
}

/**
 * iommufd_device_attach - Connect a device to an iommu_domain
 * iommufd_device_attach - Connect a device/pasid to an iommu_domain
 * @idev: device to attach
 * @pasid: pasid to attach
 * @pt_id: Input a IOMMUFD_OBJ_IOAS, or IOMMUFD_OBJ_HWPT_PAGING
 *         Output the IOMMUFD_OBJ_HWPT_PAGING ID
 *
 * This connects the device to an iommu_domain, either automatically or manually
 * selected. Once this completes the device could do DMA.
 * This connects the device/pasid to an iommu_domain, either automatically
 * or manually selected. Once this completes the device could do DMA with
 * @pasid. @pasid is IOMMU_NO_PASID if this attach is for no pasid usage.
 *
 * The caller should return the resulting pt_id back to userspace.
 * This function is undone by calling iommufd_device_detach().
 */
int iommufd_device_attach(struct iommufd_device *idev, u32 *pt_id)
int iommufd_device_attach(struct iommufd_device *idev, ioasid_t pasid,
			  u32 *pt_id)
{
	int rc;

	rc = iommufd_device_change_pt(idev, IOMMU_NO_PASID, pt_id,
	rc = iommufd_device_change_pt(idev, pasid, pt_id,
				      &iommufd_device_do_attach);
	if (rc)
		return rc;
@@ -934,8 +944,9 @@ int iommufd_device_attach(struct iommufd_device *idev, u32 *pt_id)
EXPORT_SYMBOL_NS_GPL(iommufd_device_attach, "IOMMUFD");

/**
 * iommufd_device_replace - Change the device's iommu_domain
 * iommufd_device_replace - Change the device/pasid's iommu_domain
 * @idev: device to change
 * @pasid: pasid to change
 * @pt_id: Input a IOMMUFD_OBJ_IOAS, or IOMMUFD_OBJ_HWPT_PAGING
 *         Output the IOMMUFD_OBJ_HWPT_PAGING ID
 *
@@ -946,27 +957,31 @@ EXPORT_SYMBOL_NS_GPL(iommufd_device_attach, "IOMMUFD");
 *
 * If it fails then no change is made to the attachment. The iommu driver may
 * implement this so there is no disruption in translation. This can only be
 * called if iommufd_device_attach() has already succeeded.
 * called if iommufd_device_attach() has already succeeded. @pasid is
 * IOMMU_NO_PASID for no pasid usage.
 */
int iommufd_device_replace(struct iommufd_device *idev, u32 *pt_id)
int iommufd_device_replace(struct iommufd_device *idev, ioasid_t pasid,
			   u32 *pt_id)
{
	return iommufd_device_change_pt(idev, IOMMU_NO_PASID, pt_id,
	return iommufd_device_change_pt(idev, pasid, pt_id,
					&iommufd_device_do_replace);
}
EXPORT_SYMBOL_NS_GPL(iommufd_device_replace, "IOMMUFD");

/**
 * iommufd_device_detach - Disconnect a device to an iommu_domain
 * iommufd_device_detach - Disconnect a device/device to an iommu_domain
 * @idev: device to detach
 * @pasid: pasid to detach
 *
 * Undo iommufd_device_attach(). This disconnects the idev from the previously
 * attached pt_id. The device returns back to a blocked DMA translation.
 * @pasid is IOMMU_NO_PASID for no pasid usage.
 */
void iommufd_device_detach(struct iommufd_device *idev)
void iommufd_device_detach(struct iommufd_device *idev, ioasid_t pasid)
{
	struct iommufd_hw_pagetable *hwpt;

	hwpt = iommufd_hw_pagetable_detach(idev, IOMMU_NO_PASID);
	hwpt = iommufd_hw_pagetable_detach(idev, pasid);
	iommufd_hw_pagetable_put(idev->ictx, hwpt);
	refcount_dec(&idev->obj.users);
}
+4 −4
Original line number Diff line number Diff line
@@ -945,7 +945,7 @@ static int iommufd_test_mock_domain(struct iommufd_ucmd *ucmd,
	}
	sobj->idev.idev = idev;

	rc = iommufd_device_attach(idev, &pt_id);
	rc = iommufd_device_attach(idev, IOMMU_NO_PASID, &pt_id);
	if (rc)
		goto out_unbind;

@@ -960,7 +960,7 @@ static int iommufd_test_mock_domain(struct iommufd_ucmd *ucmd,
	return 0;

out_detach:
	iommufd_device_detach(idev);
	iommufd_device_detach(idev, IOMMU_NO_PASID);
out_unbind:
	iommufd_device_unbind(idev);
out_mdev:
@@ -994,7 +994,7 @@ static int iommufd_test_mock_domain_replace(struct iommufd_ucmd *ucmd,
		goto out_dev_obj;
	}

	rc = iommufd_device_replace(sobj->idev.idev, &pt_id);
	rc = iommufd_device_replace(sobj->idev.idev, IOMMU_NO_PASID, &pt_id);
	if (rc)
		goto out_dev_obj;

@@ -1655,7 +1655,7 @@ void iommufd_selftest_destroy(struct iommufd_object *obj)

	switch (sobj->type) {
	case TYPE_IDEV:
		iommufd_device_detach(sobj->idev.idev);
		iommufd_device_detach(sobj->idev.idev, IOMMU_NO_PASID);
		iommufd_device_unbind(sobj->idev.idev);
		mock_dev_destroy(sobj->idev.mock_dev);
		break;
+6 −4
Original line number Diff line number Diff line
@@ -128,7 +128,7 @@ void vfio_iommufd_physical_unbind(struct vfio_device *vdev)
	lockdep_assert_held(&vdev->dev_set->lock);

	if (vdev->iommufd_attached) {
		iommufd_device_detach(vdev->iommufd_device);
		iommufd_device_detach(vdev->iommufd_device, IOMMU_NO_PASID);
		vdev->iommufd_attached = false;
	}
	iommufd_device_unbind(vdev->iommufd_device);
@@ -146,9 +146,11 @@ int vfio_iommufd_physical_attach_ioas(struct vfio_device *vdev, u32 *pt_id)
		return -EINVAL;

	if (vdev->iommufd_attached)
		rc = iommufd_device_replace(vdev->iommufd_device, pt_id);
		rc = iommufd_device_replace(vdev->iommufd_device,
					    IOMMU_NO_PASID, pt_id);
	else
		rc = iommufd_device_attach(vdev->iommufd_device, pt_id);
		rc = iommufd_device_attach(vdev->iommufd_device,
					   IOMMU_NO_PASID, pt_id);
	if (rc)
		return rc;
	vdev->iommufd_attached = true;
@@ -163,7 +165,7 @@ void vfio_iommufd_physical_detach_ioas(struct vfio_device *vdev)
	if (WARN_ON(!vdev->iommufd_device) || !vdev->iommufd_attached)
		return;

	iommufd_device_detach(vdev->iommufd_device);
	iommufd_device_detach(vdev->iommufd_device, IOMMU_NO_PASID);
	vdev->iommufd_attached = false;
}
EXPORT_SYMBOL_GPL(vfio_iommufd_physical_detach_ioas);
+6 −3
Original line number Diff line number Diff line
@@ -8,6 +8,7 @@

#include <linux/err.h>
#include <linux/errno.h>
#include <linux/iommu.h>
#include <linux/refcount.h>
#include <linux/types.h>
#include <linux/xarray.h>
@@ -54,9 +55,11 @@ struct iommufd_device *iommufd_device_bind(struct iommufd_ctx *ictx,
					   struct device *dev, u32 *id);
void iommufd_device_unbind(struct iommufd_device *idev);

int iommufd_device_attach(struct iommufd_device *idev, u32 *pt_id);
int iommufd_device_replace(struct iommufd_device *idev, u32 *pt_id);
void iommufd_device_detach(struct iommufd_device *idev);
int iommufd_device_attach(struct iommufd_device *idev, ioasid_t pasid,
			  u32 *pt_id);
int iommufd_device_replace(struct iommufd_device *idev, ioasid_t pasid,
			   u32 *pt_id);
void iommufd_device_detach(struct iommufd_device *idev, ioasid_t pasid);

struct iommufd_ctx *iommufd_device_to_ictx(struct iommufd_device *idev);
u32 iommufd_device_to_id(struct iommufd_device *idev);