]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
iommufd: Support pasid attach/replace
authorYi Liu <yi.l.liu@intel.com>
Fri, 21 Mar 2025 17:19:33 +0000 (10:19 -0700)
committerJason Gunthorpe <jgg@nvidia.com>
Tue, 25 Mar 2025 13:18:31 +0000 (10:18 -0300)
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: Jason Gunthorpe <jgg@nvidia.com>
Signed-off-by: Kevin Tian <kevin.tian@intel.com>
Reviewed-by: Nicolin Chen <nicolinc@nvidia.com>
Signed-off-by: Yi Liu <yi.l.liu@intel.com>
Tested-by: Nicolin Chen <nicolinc@nvidia.com>
Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
drivers/iommu/iommufd/device.c
drivers/iommu/iommufd/selftest.c
drivers/vfio/iommufd.c
include/linux/iommufd.h

index ba21b81e43bc3538b552d33a5f227f63c27e6abc..4cc6de03f76ee5c19b1f0c21d0438a1c4ab725b2 100644 (file)
@@ -428,9 +428,12 @@ static int iommufd_hwpt_attach_device(struct iommufd_hw_pagetable *hwpt,
        }
 
        handle->idev = idev;
-       WARN_ON(pasid != IOMMU_NO_PASID);
-       rc = iommu_attach_group_handle(hwpt->domain, idev->igroup->group,
-                                      &handle->handle);
+       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);
-       iommu_detach_group_handle(hwpt->domain, idev->igroup->group);
+       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 @@ out_put_pt_obj:
 }
 
 /**
- * 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);
 }
index d55dde28e9bc47c79993592f19c6321999e55589..0b3f5cbf242b19bed7d7192d4d13254ad9802e17 100644 (file)
@@ -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;
index 516294fd901be4f40bba018f8565fbde495e5aa6..37e1efa2c7bfe68a9bb47597833b2fe47db5dbf6 100644 (file)
@@ -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);
index 60eff9272551d650ecb34f911d70290df1f422bc..34b6e6ca4bfa0f5350bec09c14f7f107d05d18de 100644 (file)
@@ -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);