iommufd/selftest: Test iommufd_device_replace()
authorNicolin Chen <nicolinc@nvidia.com>
Mon, 17 Jul 2023 18:12:12 +0000 (15:12 -0300)
committerJason Gunthorpe <jgg@nvidia.com>
Wed, 26 Jul 2023 13:20:26 +0000 (10:20 -0300)
Allow the selftest to call the function on the mock idev, add some tests
to exercise it.

Link: https://lore.kernel.org/r/16-v8-6659224517ea+532-iommufd_alloc_jgg@nvidia.com
Reviewed-by: Kevin Tian <kevin.tian@intel.com>
Tested-by: Nicolin Chen <nicolinc@nvidia.com>
Signed-off-by: Nicolin Chen <nicolinc@nvidia.com>
Signed-off-by: Yi Liu <yi.l.liu@intel.com>
Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
drivers/iommu/iommufd/iommufd_test.h
drivers/iommu/iommufd/selftest.c
include/linux/iommufd.h
tools/testing/selftests/iommu/iommufd.c
tools/testing/selftests/iommu/iommufd_fail_nth.c
tools/testing/selftests/iommu/iommufd_utils.h

index b3d69cc..e3f1035 100644 (file)
@@ -17,6 +17,7 @@ enum {
        IOMMU_TEST_OP_ACCESS_PAGES,
        IOMMU_TEST_OP_ACCESS_RW,
        IOMMU_TEST_OP_SET_TEMP_MEMORY_LIMIT,
+       IOMMU_TEST_OP_MOCK_DOMAIN_REPLACE,
 };
 
 enum {
@@ -53,6 +54,9 @@ struct iommu_test_cmd {
                        __u32 out_hwpt_id;
                } mock_domain;
                struct {
+                       __u32 pt_id;
+               } mock_domain_replace;
+               struct {
                        __aligned_u64 iova;
                        __aligned_u64 length;
                        __aligned_u64 uptr;
index 74c2076..eb33dff 100644 (file)
@@ -455,6 +455,42 @@ out_sobj:
        return rc;
 }
 
+/* Replace the mock domain with a manually allocated hw_pagetable */
+static int iommufd_test_mock_domain_replace(struct iommufd_ucmd *ucmd,
+                                           unsigned int device_id, u32 pt_id,
+                                           struct iommu_test_cmd *cmd)
+{
+       struct iommufd_object *dev_obj;
+       struct selftest_obj *sobj;
+       int rc;
+
+       /*
+        * Prefer to use the OBJ_SELFTEST because the destroy_rwsem will ensure
+        * it doesn't race with detach, which is not allowed.
+        */
+       dev_obj =
+               iommufd_get_object(ucmd->ictx, device_id, IOMMUFD_OBJ_SELFTEST);
+       if (IS_ERR(dev_obj))
+               return PTR_ERR(dev_obj);
+
+       sobj = container_of(dev_obj, struct selftest_obj, obj);
+       if (sobj->type != TYPE_IDEV) {
+               rc = -EINVAL;
+               goto out_dev_obj;
+       }
+
+       rc = iommufd_device_replace(sobj->idev.idev, &pt_id);
+       if (rc)
+               goto out_dev_obj;
+
+       cmd->mock_domain_replace.pt_id = pt_id;
+       rc = iommufd_ucmd_respond(ucmd, sizeof(*cmd));
+
+out_dev_obj:
+       iommufd_put_object(dev_obj);
+       return rc;
+}
+
 /* Add an additional reserved IOVA to the IOAS */
 static int iommufd_test_add_reserved(struct iommufd_ucmd *ucmd,
                                     unsigned int mockpt_id,
@@ -948,6 +984,9 @@ int iommufd_test(struct iommufd_ucmd *ucmd)
                                                 cmd->add_reserved.length);
        case IOMMU_TEST_OP_MOCK_DOMAIN:
                return iommufd_test_mock_domain(ucmd, cmd);
+       case IOMMU_TEST_OP_MOCK_DOMAIN_REPLACE:
+               return iommufd_test_mock_domain_replace(
+                       ucmd, cmd->id, cmd->mock_domain_replace.pt_id, cmd);
        case IOMMU_TEST_OP_MD_CHECK_MAP:
                return iommufd_test_md_check_pa(
                        ucmd, cmd->id, cmd->check_map.iova,
index 9657c58..0ac6025 100644 (file)
@@ -23,6 +23,7 @@ struct iommufd_device *iommufd_device_bind(struct iommufd_ctx *ictx,
 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);
 
 struct iommufd_ctx *iommufd_device_to_ictx(struct iommufd_device *idev);
index e4a6b33..96881ec 100644 (file)
@@ -9,9 +9,6 @@
 
 #include "iommufd_utils.h"
 
-static void *buffer;
-
-static unsigned long PAGE_SIZE;
 static unsigned long HUGEPAGE_SIZE;
 
 #define MOCK_PAGE_SIZE (PAGE_SIZE / 2)
@@ -1035,6 +1032,7 @@ FIXTURE(iommufd_mock_domain)
        uint32_t ioas_id;
        uint32_t hwpt_id;
        uint32_t hwpt_ids[2];
+       uint32_t stdev_ids[2];
        int mmap_flags;
        size_t mmap_buf_size;
 };
@@ -1056,7 +1054,8 @@ FIXTURE_SETUP(iommufd_mock_domain)
        ASSERT_GE(ARRAY_SIZE(self->hwpt_ids), variant->mock_domains);
 
        for (i = 0; i != variant->mock_domains; i++)
-               test_cmd_mock_domain(self->ioas_id, NULL, &self->hwpt_ids[i]);
+               test_cmd_mock_domain(self->ioas_id, &self->stdev_ids[i],
+                                    &self->hwpt_ids[i]);
        self->hwpt_id = self->hwpt_ids[0];
 
        self->mmap_flags = MAP_SHARED | MAP_ANONYMOUS;
@@ -1308,6 +1307,36 @@ TEST_F(iommufd_mock_domain, user_copy)
        test_ioctl_destroy(ioas_id);
 }
 
+TEST_F(iommufd_mock_domain, replace)
+{
+       uint32_t ioas_id;
+
+       test_ioctl_ioas_alloc(&ioas_id);
+
+       test_cmd_mock_domain_replace(self->stdev_ids[0], ioas_id);
+
+       /*
+        * Replacing the IOAS causes the prior HWPT to be deallocated, thus we
+        * should get enoent when we try to use it.
+        */
+       if (variant->mock_domains == 1)
+               test_err_mock_domain_replace(ENOENT, self->stdev_ids[0],
+                                            self->hwpt_ids[0]);
+
+       test_cmd_mock_domain_replace(self->stdev_ids[0], ioas_id);
+       if (variant->mock_domains >= 2) {
+               test_cmd_mock_domain_replace(self->stdev_ids[0],
+                                            self->hwpt_ids[1]);
+               test_cmd_mock_domain_replace(self->stdev_ids[0],
+                                            self->hwpt_ids[1]);
+               test_cmd_mock_domain_replace(self->stdev_ids[0],
+                                            self->hwpt_ids[0]);
+       }
+
+       test_cmd_mock_domain_replace(self->stdev_ids[0], self->ioas_id);
+       test_ioctl_destroy(ioas_id);
+}
+
 /* VFIO compatibility IOCTLs */
 
 TEST_F(iommufd, simple_ioctls)
index d9afcb2..96fb2f0 100644 (file)
@@ -41,6 +41,8 @@ static int writeat(int dfd, const char *fn, const char *val)
 
 static __attribute__((constructor)) void setup_buffer(void)
 {
+       PAGE_SIZE = sysconf(_SC_PAGE_SIZE);
+
        BUFFER_SIZE = 2*1024*1024;
 
        buffer = mmap(0, BUFFER_SIZE, PROT_READ | PROT_WRITE,
@@ -569,4 +571,44 @@ TEST_FAIL_NTH(basic_fail_nth, access_pin_domain)
        return 0;
 }
 
+/* device.c */
+TEST_FAIL_NTH(basic_fail_nth, device)
+{
+       uint32_t ioas_id;
+       uint32_t ioas_id2;
+       uint32_t stdev_id;
+       __u64 iova;
+
+       self->fd = open("/dev/iommu", O_RDWR);
+       if (self->fd == -1)
+               return -1;
+
+       if (_test_ioctl_ioas_alloc(self->fd, &ioas_id))
+               return -1;
+
+       if (_test_ioctl_ioas_alloc(self->fd, &ioas_id2))
+               return -1;
+
+       iova = MOCK_APERTURE_START;
+       if (_test_ioctl_ioas_map(self->fd, ioas_id, buffer, PAGE_SIZE, &iova,
+                                IOMMU_IOAS_MAP_FIXED_IOVA |
+                                        IOMMU_IOAS_MAP_WRITEABLE |
+                                        IOMMU_IOAS_MAP_READABLE))
+               return -1;
+       if (_test_ioctl_ioas_map(self->fd, ioas_id2, buffer, PAGE_SIZE, &iova,
+                                IOMMU_IOAS_MAP_FIXED_IOVA |
+                                        IOMMU_IOAS_MAP_WRITEABLE |
+                                        IOMMU_IOAS_MAP_READABLE))
+               return -1;
+
+       fail_nth_enable();
+
+       if (_test_cmd_mock_domain(self->fd, ioas_id, &stdev_id, NULL))
+               return -1;
+
+       if (_test_cmd_mock_domain_replace(self->fd, stdev_id, ioas_id2, NULL))
+               return -1;
+       return 0;
+}
+
 TEST_HARNESS_MAIN
index 85d6662..8b11bb7 100644 (file)
@@ -19,6 +19,8 @@
 static void *buffer;
 static unsigned long BUFFER_SIZE;
 
+static unsigned long PAGE_SIZE;
+
 /*
  * Have the kernel check the refcount on pages. I don't know why a freshly
  * mmap'd anon non-compound page starts out with a ref of 3
@@ -66,6 +68,34 @@ static int _test_cmd_mock_domain(int fd, unsigned int ioas_id, __u32 *stdev_id,
        EXPECT_ERRNO(_errno, _test_cmd_mock_domain(self->fd, ioas_id, \
                                                   stdev_id, hwpt_id))
 
+static int _test_cmd_mock_domain_replace(int fd, __u32 stdev_id, __u32 pt_id,
+                                        __u32 *hwpt_id)
+{
+       struct iommu_test_cmd cmd = {
+               .size = sizeof(cmd),
+               .op = IOMMU_TEST_OP_MOCK_DOMAIN_REPLACE,
+               .id = stdev_id,
+               .mock_domain_replace = {
+                       .pt_id = pt_id,
+               },
+       };
+       int ret;
+
+       ret = ioctl(fd, IOMMU_TEST_CMD, &cmd);
+       if (ret)
+               return ret;
+       if (hwpt_id)
+               *hwpt_id = cmd.mock_domain_replace.pt_id;
+       return 0;
+}
+
+#define test_cmd_mock_domain_replace(stdev_id, pt_id)                         \
+       ASSERT_EQ(0, _test_cmd_mock_domain_replace(self->fd, stdev_id, pt_id, \
+                                                  NULL))
+#define test_err_mock_domain_replace(_errno, stdev_id, pt_id)                  \
+       EXPECT_ERRNO(_errno, _test_cmd_mock_domain_replace(self->fd, stdev_id, \
+                                                          pt_id, NULL))
+
 static int _test_cmd_create_access(int fd, unsigned int ioas_id,
                                   __u32 *access_id, unsigned int flags)
 {