]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
iommufd/selftest: Add test ops to test pasid attach/detach
authorYi Liu <yi.l.liu@intel.com>
Fri, 21 Mar 2025 17:19:39 +0000 (10:19 -0700)
committerJason Gunthorpe <jgg@nvidia.com>
Tue, 25 Mar 2025 13:18:31 +0000 (10:18 -0300)
This adds 4 test ops for pasid attach/replace/detach testing. There are
ops to attach/detach pasid, and also op to check the attached hwpt of a
pasid.

Link: https://patch.msgid.link/r/20250321171940.7213-18-yi.l.liu@intel.com
Reviewed-by: Kevin Tian <kevin.tian@intel.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/iommufd_test.h
drivers/iommu/iommufd/selftest.c

index 1a066feb8697e956654b14b7b543d0e7c6d2548c..1cd7e8394129802344ca051d9345a5456962d05f 100644 (file)
@@ -25,6 +25,10 @@ enum {
        IOMMU_TEST_OP_TRIGGER_IOPF,
        IOMMU_TEST_OP_DEV_CHECK_CACHE,
        IOMMU_TEST_OP_TRIGGER_VEVENT,
+       IOMMU_TEST_OP_PASID_ATTACH,
+       IOMMU_TEST_OP_PASID_REPLACE,
+       IOMMU_TEST_OP_PASID_DETACH,
+       IOMMU_TEST_OP_PASID_CHECK_HWPT,
 };
 
 enum {
@@ -62,6 +66,9 @@ enum {
        MOCK_DEV_CACHE_NUM = 4,
 };
 
+/* Reserved for special pasid replace test */
+#define IOMMU_TEST_PASID_RESERVED 1024
+
 struct iommu_test_cmd {
        __u32 size;
        __u32 op;
@@ -150,6 +157,25 @@ struct iommu_test_cmd {
                struct {
                        __u32 dev_id;
                } trigger_vevent;
+               struct {
+                       __u32 pasid;
+                       __u32 pt_id;
+                       /* @id is stdev_id */
+               } pasid_attach;
+               struct {
+                       __u32 pasid;
+                       __u32 pt_id;
+                       /* @id is stdev_id */
+               } pasid_replace;
+               struct {
+                       __u32 pasid;
+                       /* @id is stdev_id */
+               } pasid_detach;
+               struct {
+                       __u32 pasid;
+                       __u32 hwpt_id;
+                       /* @id is stdev_id */
+               } pasid_check;
        };
        __u32 last;
 };
index 04a4b84f5fa1b1401e4e6a5511776719b8d26371..18d9a216eb30d48555dd43b17ba72840d4ae8a17 100644 (file)
@@ -167,6 +167,7 @@ struct mock_dev {
        unsigned long vdev_id;
        int id;
        u32 cache[MOCK_DEV_CACHE_NUM];
+       atomic_t pasid_1024_fake_error;
 };
 
 static inline struct mock_dev *to_mock_dev(struct device *dev)
@@ -227,6 +228,34 @@ static int mock_domain_set_dev_pasid_nop(struct iommu_domain *domain,
                                         struct device *dev, ioasid_t pasid,
                                         struct iommu_domain *old)
 {
+       struct mock_dev *mdev = to_mock_dev(dev);
+
+       /*
+        * Per the first attach with pasid 1024, set the
+        * mdev->pasid_1024_fake_error. Hence the second call of this op
+        * can fake an error to validate the error path of the core. This
+        * is helpful to test the case in which the iommu core needs to
+        * rollback to the old domain due to driver failure. e.g. replace.
+        * User should be careful about the third call of this op, it shall
+        * succeed since the mdev->pasid_1024_fake_error is cleared in the
+        * second call.
+        */
+       if (pasid == 1024) {
+               if (domain->type == IOMMU_DOMAIN_BLOCKED) {
+                       atomic_set(&mdev->pasid_1024_fake_error, 0);
+               } else if (atomic_read(&mdev->pasid_1024_fake_error)) {
+                       /*
+                        * Clear the flag, and fake an error to fail the
+                        * replacement.
+                        */
+                       atomic_set(&mdev->pasid_1024_fake_error, 0);
+                       return -ENOMEM;
+               } else {
+                       /* Set the flag to fake an error in next call */
+                       atomic_set(&mdev->pasid_1024_fake_error, 1);
+               }
+       }
+
        return 0;
 }
 
@@ -1685,6 +1714,131 @@ out_unlock:
        return rc;
 }
 
+static inline struct iommufd_hw_pagetable *
+iommufd_get_hwpt(struct iommufd_ucmd *ucmd, u32 id)
+{
+       struct iommufd_object *pt_obj;
+
+       pt_obj = iommufd_get_object(ucmd->ictx, id, IOMMUFD_OBJ_ANY);
+       if (IS_ERR(pt_obj))
+               return ERR_CAST(pt_obj);
+
+       if (pt_obj->type != IOMMUFD_OBJ_HWPT_NESTED &&
+           pt_obj->type != IOMMUFD_OBJ_HWPT_PAGING) {
+               iommufd_put_object(ucmd->ictx, pt_obj);
+               return ERR_PTR(-EINVAL);
+       }
+
+       return container_of(pt_obj, struct iommufd_hw_pagetable, obj);
+}
+
+static int iommufd_test_pasid_check_hwpt(struct iommufd_ucmd *ucmd,
+                                        struct iommu_test_cmd *cmd)
+{
+       u32 hwpt_id = cmd->pasid_check.hwpt_id;
+       struct iommu_domain *attached_domain;
+       struct iommu_attach_handle *handle;
+       struct iommufd_hw_pagetable *hwpt;
+       struct selftest_obj *sobj;
+       struct mock_dev *mdev;
+       int rc = 0;
+
+       sobj = iommufd_test_get_selftest_obj(ucmd->ictx, cmd->id);
+       if (IS_ERR(sobj))
+               return PTR_ERR(sobj);
+
+       mdev = sobj->idev.mock_dev;
+
+       handle = iommu_attach_handle_get(mdev->dev.iommu_group,
+                                        cmd->pasid_check.pasid, 0);
+       if (IS_ERR(handle))
+               attached_domain = NULL;
+       else
+               attached_domain = handle->domain;
+
+       /* hwpt_id == 0 means to check if pasid is detached */
+       if (!hwpt_id) {
+               if (attached_domain)
+                       rc = -EINVAL;
+               goto out_sobj;
+       }
+
+       hwpt = iommufd_get_hwpt(ucmd, hwpt_id);
+       if (IS_ERR(hwpt)) {
+               rc = PTR_ERR(hwpt);
+               goto out_sobj;
+       }
+
+       if (attached_domain != hwpt->domain)
+               rc = -EINVAL;
+
+       iommufd_put_object(ucmd->ictx, &hwpt->obj);
+out_sobj:
+       iommufd_put_object(ucmd->ictx, &sobj->obj);
+       return rc;
+}
+
+static int iommufd_test_pasid_attach(struct iommufd_ucmd *ucmd,
+                                    struct iommu_test_cmd *cmd)
+{
+       struct selftest_obj *sobj;
+       int rc;
+
+       sobj = iommufd_test_get_selftest_obj(ucmd->ictx, cmd->id);
+       if (IS_ERR(sobj))
+               return PTR_ERR(sobj);
+
+       rc = iommufd_device_attach(sobj->idev.idev, cmd->pasid_attach.pasid,
+                                  &cmd->pasid_attach.pt_id);
+       if (rc)
+               goto out_sobj;
+
+       rc = iommufd_ucmd_respond(ucmd, sizeof(*cmd));
+       if (rc)
+               iommufd_device_detach(sobj->idev.idev,
+                                     cmd->pasid_attach.pasid);
+
+out_sobj:
+       iommufd_put_object(ucmd->ictx, &sobj->obj);
+       return rc;
+}
+
+static int iommufd_test_pasid_replace(struct iommufd_ucmd *ucmd,
+                                     struct iommu_test_cmd *cmd)
+{
+       struct selftest_obj *sobj;
+       int rc;
+
+       sobj = iommufd_test_get_selftest_obj(ucmd->ictx, cmd->id);
+       if (IS_ERR(sobj))
+               return PTR_ERR(sobj);
+
+       rc = iommufd_device_replace(sobj->idev.idev, cmd->pasid_attach.pasid,
+                                   &cmd->pasid_attach.pt_id);
+       if (rc)
+               goto out_sobj;
+
+       rc = iommufd_ucmd_respond(ucmd, sizeof(*cmd));
+
+out_sobj:
+       iommufd_put_object(ucmd->ictx, &sobj->obj);
+       return rc;
+}
+
+static int iommufd_test_pasid_detach(struct iommufd_ucmd *ucmd,
+                                    struct iommu_test_cmd *cmd)
+{
+       struct selftest_obj *sobj;
+
+       sobj = iommufd_test_get_selftest_obj(ucmd->ictx, cmd->id);
+       if (IS_ERR(sobj))
+               return PTR_ERR(sobj);
+
+       iommufd_device_detach(sobj->idev.idev, cmd->pasid_detach.pasid);
+       iommufd_put_object(ucmd->ictx, &sobj->obj);
+       return 0;
+}
+
 void iommufd_selftest_destroy(struct iommufd_object *obj)
 {
        struct selftest_obj *sobj = to_selftest_obj(obj);
@@ -1768,6 +1922,14 @@ int iommufd_test(struct iommufd_ucmd *ucmd)
                return iommufd_test_trigger_iopf(ucmd, cmd);
        case IOMMU_TEST_OP_TRIGGER_VEVENT:
                return iommufd_test_trigger_vevent(ucmd, cmd);
+       case IOMMU_TEST_OP_PASID_ATTACH:
+               return iommufd_test_pasid_attach(ucmd, cmd);
+       case IOMMU_TEST_OP_PASID_REPLACE:
+               return iommufd_test_pasid_replace(ucmd, cmd);
+       case IOMMU_TEST_OP_PASID_DETACH:
+               return iommufd_test_pasid_detach(ucmd, cmd);
+       case IOMMU_TEST_OP_PASID_CHECK_HWPT:
+               return iommufd_test_pasid_check_hwpt(ucmd, cmd);
        default:
                return -EOPNOTSUPP;
        }