From 2cfdeaa07be391de5f2faf79a7319f04f39914e3 Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Wed, 1 Mar 2023 15:30:24 -0400 Subject: iommufd/selftest: Rename the sefltest 'device_id' to 'stdev_id' It is too confusing now that we have the 'dev_id' as part of the main interface. Make it clear this is the special selftest device object. This object is analogous to the VFIO device FD. Link: https://lore.kernel.org/r/7-v3-ae9c2975a131+2e1e8-iommufd_hwpt_jgg@nvidia.com Reviewed-by: Kevin Tian Signed-off-by: Jason Gunthorpe --- tools/testing/selftests/iommu/iommufd_utils.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'tools') diff --git a/tools/testing/selftests/iommu/iommufd_utils.h b/tools/testing/selftests/iommu/iommufd_utils.h index 0d1f46369c2a..85d6662ef8e8 100644 --- a/tools/testing/selftests/iommu/iommufd_utils.h +++ b/tools/testing/selftests/iommu/iommufd_utils.h @@ -38,7 +38,7 @@ static unsigned long BUFFER_SIZE; &test_cmd)); \ }) -static int _test_cmd_mock_domain(int fd, unsigned int ioas_id, __u32 *device_id, +static int _test_cmd_mock_domain(int fd, unsigned int ioas_id, __u32 *stdev_id, __u32 *hwpt_id) { struct iommu_test_cmd cmd = { @@ -52,19 +52,19 @@ static int _test_cmd_mock_domain(int fd, unsigned int ioas_id, __u32 *device_id, ret = ioctl(fd, IOMMU_TEST_CMD, &cmd); if (ret) return ret; - if (device_id) - *device_id = cmd.mock_domain.out_device_id; + if (stdev_id) + *stdev_id = cmd.mock_domain.out_stdev_id; assert(cmd.id != 0); if (hwpt_id) *hwpt_id = cmd.mock_domain.out_hwpt_id; return 0; } -#define test_cmd_mock_domain(ioas_id, device_id, hwpt_id) \ - ASSERT_EQ(0, _test_cmd_mock_domain(self->fd, ioas_id, device_id, \ - hwpt_id)) -#define test_err_mock_domain(_errno, ioas_id, device_id, hwpt_id) \ +#define test_cmd_mock_domain(ioas_id, stdev_id, hwpt_id) \ + ASSERT_EQ(0, \ + _test_cmd_mock_domain(self->fd, ioas_id, stdev_id, hwpt_id)) +#define test_err_mock_domain(_errno, ioas_id, stdev_id, hwpt_id) \ EXPECT_ERRNO(_errno, _test_cmd_mock_domain(self->fd, ioas_id, \ - device_id, hwpt_id)) + stdev_id, hwpt_id)) static int _test_cmd_create_access(int fd, unsigned int ioas_id, __u32 *access_id, unsigned int flags) -- cgit v1.2.3 From a150c4229700ded29cf6e376c4137d7c3acaefad Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Wed, 1 Mar 2023 15:30:25 -0400 Subject: iommufd/selftest: Rename domain_id to stdev_id for FIXTURE iommufd_ioas Nothing uses this for anything more than checking if a mock domain had been created. Rename it to stdev_id to match our naming system. Currently domain_id is the hwpt_id. Link: https://lore.kernel.org/r/8-v3-ae9c2975a131+2e1e8-iommufd_hwpt_jgg@nvidia.com Reviewed-by: Kevin Tian Signed-off-by: Jason Gunthorpe --- tools/testing/selftests/iommu/iommufd.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'tools') diff --git a/tools/testing/selftests/iommu/iommufd.c b/tools/testing/selftests/iommu/iommufd.c index fa08209268c4..c1d490410aa8 100644 --- a/tools/testing/selftests/iommu/iommufd.c +++ b/tools/testing/selftests/iommu/iommufd.c @@ -186,7 +186,7 @@ FIXTURE(iommufd_ioas) { int fd; uint32_t ioas_id; - uint32_t domain_id; + uint32_t stdev_id; uint64_t base_iova; }; @@ -212,7 +212,7 @@ FIXTURE_SETUP(iommufd_ioas) } for (i = 0; i != variant->mock_domains; i++) { - test_cmd_mock_domain(self->ioas_id, NULL, &self->domain_id); + test_cmd_mock_domain(self->ioas_id, &self->stdev_id, NULL); self->base_iova = MOCK_APERTURE_START; } } @@ -249,8 +249,8 @@ TEST_F(iommufd_ioas, ioas_auto_destroy) TEST_F(iommufd_ioas, ioas_destroy) { - if (self->domain_id) { - /* IOAS cannot be freed while a domain is on it */ + if (self->stdev_id) { + /* IOAS cannot be freed while a device has a HWPT using it */ EXPECT_ERRNO(EBUSY, _test_ioctl_destroy(self->fd, self->ioas_id)); } else { @@ -263,7 +263,7 @@ TEST_F(iommufd_ioas, ioas_area_destroy) { /* Adding an area does not change ability to destroy */ test_ioctl_ioas_map_fixed(buffer, PAGE_SIZE, self->base_iova); - if (self->domain_id) + if (self->stdev_id) EXPECT_ERRNO(EBUSY, _test_ioctl_destroy(self->fd, self->ioas_id)); else @@ -382,7 +382,7 @@ TEST_F(iommufd_ioas, area_auto_iova) for (i = 0; i != 10; i++) { size_t length = PAGE_SIZE * (i + 1); - if (self->domain_id) { + if (self->stdev_id) { test_ioctl_ioas_map(buffer, length, &iovas[i]); } else { test_ioctl_ioas_map((void *)(1UL << 31), length, @@ -418,7 +418,7 @@ TEST_F(iommufd_ioas, area_auto_iova) ioctl(self->fd, IOMMU_IOAS_ALLOW_IOVAS, &allow_cmd)); /* Allocate from an allowed region */ - if (self->domain_id) { + if (self->stdev_id) { ranges[0].start = MOCK_APERTURE_START + PAGE_SIZE; ranges[0].last = MOCK_APERTURE_START + PAGE_SIZE * 600 - 1; } else { @@ -525,7 +525,7 @@ TEST_F(iommufd_ioas, iova_ranges) /* Range can be read */ ASSERT_EQ(0, ioctl(self->fd, IOMMU_IOAS_IOVA_RANGES, &ranges_cmd)); EXPECT_EQ(1, ranges_cmd.num_iovas); - if (!self->domain_id) { + if (!self->stdev_id) { EXPECT_EQ(0, ranges[0].start); EXPECT_EQ(SIZE_MAX, ranges[0].last); EXPECT_EQ(1, ranges_cmd.out_iova_alignment); @@ -550,7 +550,7 @@ TEST_F(iommufd_ioas, iova_ranges) &test_cmd)); ranges_cmd.num_iovas = BUFFER_SIZE / sizeof(*ranges); ASSERT_EQ(0, ioctl(self->fd, IOMMU_IOAS_IOVA_RANGES, &ranges_cmd)); - if (!self->domain_id) { + if (!self->stdev_id) { EXPECT_EQ(2, ranges_cmd.num_iovas); EXPECT_EQ(0, ranges[0].start); EXPECT_EQ(PAGE_SIZE - 1, ranges[0].last); @@ -565,7 +565,7 @@ TEST_F(iommufd_ioas, iova_ranges) /* Buffer too small */ memset(ranges, 0, BUFFER_SIZE); ranges_cmd.num_iovas = 1; - if (!self->domain_id) { + if (!self->stdev_id) { EXPECT_ERRNO(EMSGSIZE, ioctl(self->fd, IOMMU_IOAS_IOVA_RANGES, &ranges_cmd)); EXPECT_EQ(2, ranges_cmd.num_iovas); @@ -789,7 +789,7 @@ TEST_F(iommufd_ioas, fork_gone) ASSERT_NE(-1, child); ASSERT_EQ(child, waitpid(child, NULL, 0)); - if (self->domain_id) { + if (self->stdev_id) { /* * If a domain already existed then everything was pinned within * the fork, so this copies from one domain to another. -- cgit v1.2.3 From 11a86ae93d7595ec7ed27536ac8a8292dd5746d5 Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Wed, 1 Mar 2023 15:30:26 -0400 Subject: iommufd/selftest: Rename domain_id to hwpt_id for FIXTURE iommufd_mock_domain In this case the domain_id was acting as the hwpt_id, so be clearer. Link: https://lore.kernel.org/r/9-v3-ae9c2975a131+2e1e8-iommufd_hwpt_jgg@nvidia.com Reviewed-by: Kevin Tian Signed-off-by: Jason Gunthorpe --- tools/testing/selftests/iommu/iommufd.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'tools') diff --git a/tools/testing/selftests/iommu/iommufd.c b/tools/testing/selftests/iommu/iommufd.c index c1d490410aa8..830c7485cb30 100644 --- a/tools/testing/selftests/iommu/iommufd.c +++ b/tools/testing/selftests/iommu/iommufd.c @@ -988,8 +988,8 @@ FIXTURE(iommufd_mock_domain) { int fd; uint32_t ioas_id; - uint32_t domain_id; - uint32_t domain_ids[2]; + uint32_t hwpt_id; + uint32_t hwpt_ids[2]; int mmap_flags; size_t mmap_buf_size; }; @@ -1008,11 +1008,11 @@ FIXTURE_SETUP(iommufd_mock_domain) ASSERT_NE(-1, self->fd); test_ioctl_ioas_alloc(&self->ioas_id); - ASSERT_GE(ARRAY_SIZE(self->domain_ids), variant->mock_domains); + 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->domain_ids[i]); - self->domain_id = self->domain_ids[0]; + test_cmd_mock_domain(self->ioas_id, NULL, &self->hwpt_ids[i]); + self->hwpt_id = self->hwpt_ids[0]; self->mmap_flags = MAP_SHARED | MAP_ANONYMOUS; self->mmap_buf_size = PAGE_SIZE * 8; @@ -1061,7 +1061,7 @@ FIXTURE_VARIANT_ADD(iommufd_mock_domain, two_domains_hugepage) struct iommu_test_cmd check_map_cmd = { \ .size = sizeof(check_map_cmd), \ .op = IOMMU_TEST_OP_MD_CHECK_MAP, \ - .id = self->domain_id, \ + .id = self->hwpt_id, \ .check_map = { .iova = _iova, \ .length = _length, \ .uptr = (uintptr_t)(_ptr) }, \ @@ -1070,8 +1070,8 @@ FIXTURE_VARIANT_ADD(iommufd_mock_domain, two_domains_hugepage) ioctl(self->fd, \ _IOMMU_TEST_CMD(IOMMU_TEST_OP_MD_CHECK_MAP), \ &check_map_cmd)); \ - if (self->domain_ids[1]) { \ - check_map_cmd.id = self->domain_ids[1]; \ + if (self->hwpt_ids[1]) { \ + check_map_cmd.id = self->hwpt_ids[1]; \ ASSERT_EQ(0, \ ioctl(self->fd, \ _IOMMU_TEST_CMD( \ @@ -1203,9 +1203,9 @@ TEST_F(iommufd_mock_domain, all_aligns_copy) test_ioctl_ioas_map(buf + start, length, &iova); /* Add and destroy a domain while the area exists */ - old_id = self->domain_ids[1]; + old_id = self->hwpt_ids[1]; test_cmd_mock_domain(self->ioas_id, &mock_device_id, - &self->domain_ids[1]); + &self->hwpt_ids[1]); check_mock_iova(buf + start, iova, length); check_refs(buf + start / PAGE_SIZE * PAGE_SIZE, @@ -1214,8 +1214,8 @@ TEST_F(iommufd_mock_domain, all_aligns_copy) 1); test_ioctl_destroy(mock_device_id); - test_ioctl_destroy(self->domain_ids[1]); - self->domain_ids[1] = old_id; + test_ioctl_destroy(self->hwpt_ids[1]); + self->hwpt_ids[1] = old_id; test_ioctl_ioas_unmap(iova, length); } -- cgit v1.2.3 From f8406f65c1cfe4c8e3646ea66f5bac07b1f10b55 Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Wed, 1 Mar 2023 15:30:27 -0400 Subject: iommufd/selftest: Rename the remaining mock device_id's to stdev_id For consistency. Link: https://lore.kernel.org/r/10-v3-ae9c2975a131+2e1e8-iommufd_hwpt_jgg@nvidia.com Reviewed-by: Kevin Tian Signed-off-by: Jason Gunthorpe --- tools/testing/selftests/iommu/iommufd.c | 12 +++++----- tools/testing/selftests/iommu/iommufd_fail_nth.c | 30 ++++++++++++------------ 2 files changed, 21 insertions(+), 21 deletions(-) (limited to 'tools') diff --git a/tools/testing/selftests/iommu/iommufd.c b/tools/testing/selftests/iommu/iommufd.c index 830c7485cb30..1d72c48157b7 100644 --- a/tools/testing/selftests/iommu/iommufd.c +++ b/tools/testing/selftests/iommu/iommufd.c @@ -605,7 +605,7 @@ TEST_F(iommufd_ioas, access_pin) MOCK_FLAGS_ACCESS_CREATE_NEEDS_PIN_PAGES); for (npages = 1; npages < BUFFER_SIZE / PAGE_SIZE; npages++) { - uint32_t mock_device_id; + uint32_t mock_stdev_id; uint32_t mock_hwpt_id; access_cmd.access_pages.length = npages * PAGE_SIZE; @@ -637,14 +637,14 @@ TEST_F(iommufd_ioas, access_pin) ASSERT_EQ(0, ioctl(self->fd, _IOMMU_TEST_CMD(IOMMU_TEST_OP_ACCESS_PAGES), &access_cmd)); - test_cmd_mock_domain(self->ioas_id, &mock_device_id, + test_cmd_mock_domain(self->ioas_id, &mock_stdev_id, &mock_hwpt_id); check_map_cmd.id = mock_hwpt_id; ASSERT_EQ(0, ioctl(self->fd, _IOMMU_TEST_CMD(IOMMU_TEST_OP_MD_CHECK_MAP), &check_map_cmd)); - test_ioctl_destroy(mock_device_id); + test_ioctl_destroy(mock_stdev_id); test_ioctl_destroy(mock_hwpt_id); test_cmd_destroy_access_pages( access_cmd.id, @@ -1197,14 +1197,14 @@ TEST_F(iommufd_mock_domain, all_aligns_copy) for (; end < buf_size; end += MOCK_PAGE_SIZE) { size_t length = end - start; unsigned int old_id; - uint32_t mock_device_id; + uint32_t mock_stdev_id; __u64 iova; test_ioctl_ioas_map(buf + start, length, &iova); /* Add and destroy a domain while the area exists */ old_id = self->hwpt_ids[1]; - test_cmd_mock_domain(self->ioas_id, &mock_device_id, + test_cmd_mock_domain(self->ioas_id, &mock_stdev_id, &self->hwpt_ids[1]); check_mock_iova(buf + start, iova, length); @@ -1213,7 +1213,7 @@ TEST_F(iommufd_mock_domain, all_aligns_copy) start / PAGE_SIZE * PAGE_SIZE, 1); - test_ioctl_destroy(mock_device_id); + test_ioctl_destroy(mock_stdev_id); test_ioctl_destroy(self->hwpt_ids[1]); self->hwpt_ids[1] = old_id; diff --git a/tools/testing/selftests/iommu/iommufd_fail_nth.c b/tools/testing/selftests/iommu/iommufd_fail_nth.c index 9713111b820d..e7d535680721 100644 --- a/tools/testing/selftests/iommu/iommufd_fail_nth.c +++ b/tools/testing/selftests/iommu/iommufd_fail_nth.c @@ -297,7 +297,7 @@ TEST_FAIL_NTH(basic_fail_nth, basic) TEST_FAIL_NTH(basic_fail_nth, map_domain) { uint32_t ioas_id; - __u32 device_id; + __u32 stdev_id; __u32 hwpt_id; __u64 iova; @@ -313,7 +313,7 @@ TEST_FAIL_NTH(basic_fail_nth, map_domain) fail_nth_enable(); - if (_test_cmd_mock_domain(self->fd, ioas_id, &device_id, &hwpt_id)) + if (_test_cmd_mock_domain(self->fd, ioas_id, &stdev_id, &hwpt_id)) return -1; if (_test_ioctl_ioas_map(self->fd, ioas_id, buffer, 262144, &iova, @@ -321,12 +321,12 @@ TEST_FAIL_NTH(basic_fail_nth, map_domain) IOMMU_IOAS_MAP_READABLE)) return -1; - if (_test_ioctl_destroy(self->fd, device_id)) + if (_test_ioctl_destroy(self->fd, stdev_id)) return -1; if (_test_ioctl_destroy(self->fd, hwpt_id)) return -1; - if (_test_cmd_mock_domain(self->fd, ioas_id, &device_id, &hwpt_id)) + if (_test_cmd_mock_domain(self->fd, ioas_id, &stdev_id, &hwpt_id)) return -1; return 0; } @@ -334,8 +334,8 @@ TEST_FAIL_NTH(basic_fail_nth, map_domain) TEST_FAIL_NTH(basic_fail_nth, map_two_domains) { uint32_t ioas_id; - __u32 device_id2; - __u32 device_id; + __u32 stdev_id2; + __u32 stdev_id; __u32 hwpt_id2; __u32 hwpt_id; __u64 iova; @@ -350,12 +350,12 @@ TEST_FAIL_NTH(basic_fail_nth, map_two_domains) if (_test_ioctl_set_temp_memory_limit(self->fd, 32)) return -1; - if (_test_cmd_mock_domain(self->fd, ioas_id, &device_id, &hwpt_id)) + if (_test_cmd_mock_domain(self->fd, ioas_id, &stdev_id, &hwpt_id)) return -1; fail_nth_enable(); - if (_test_cmd_mock_domain(self->fd, ioas_id, &device_id2, &hwpt_id2)) + if (_test_cmd_mock_domain(self->fd, ioas_id, &stdev_id2, &hwpt_id2)) return -1; if (_test_ioctl_ioas_map(self->fd, ioas_id, buffer, 262144, &iova, @@ -363,19 +363,19 @@ TEST_FAIL_NTH(basic_fail_nth, map_two_domains) IOMMU_IOAS_MAP_READABLE)) return -1; - if (_test_ioctl_destroy(self->fd, device_id)) + if (_test_ioctl_destroy(self->fd, stdev_id)) return -1; if (_test_ioctl_destroy(self->fd, hwpt_id)) return -1; - if (_test_ioctl_destroy(self->fd, device_id2)) + if (_test_ioctl_destroy(self->fd, stdev_id2)) return -1; if (_test_ioctl_destroy(self->fd, hwpt_id2)) return -1; - if (_test_cmd_mock_domain(self->fd, ioas_id, &device_id, &hwpt_id)) + if (_test_cmd_mock_domain(self->fd, ioas_id, &stdev_id, &hwpt_id)) return -1; - if (_test_cmd_mock_domain(self->fd, ioas_id, &device_id2, &hwpt_id2)) + if (_test_cmd_mock_domain(self->fd, ioas_id, &stdev_id2, &hwpt_id2)) return -1; return 0; } @@ -518,7 +518,7 @@ TEST_FAIL_NTH(basic_fail_nth, access_pin_domain) { uint32_t access_pages_id; uint32_t ioas_id; - __u32 device_id; + __u32 stdev_id; __u32 hwpt_id; __u64 iova; @@ -532,7 +532,7 @@ TEST_FAIL_NTH(basic_fail_nth, access_pin_domain) if (_test_ioctl_set_temp_memory_limit(self->fd, 32)) return -1; - if (_test_cmd_mock_domain(self->fd, ioas_id, &device_id, &hwpt_id)) + if (_test_cmd_mock_domain(self->fd, ioas_id, &stdev_id, &hwpt_id)) return -1; if (_test_ioctl_ioas_map(self->fd, ioas_id, buffer, BUFFER_SIZE, &iova, @@ -570,7 +570,7 @@ TEST_FAIL_NTH(basic_fail_nth, access_pin_domain) return -1; self->access_id = 0; - if (_test_ioctl_destroy(self->fd, device_id)) + if (_test_ioctl_destroy(self->fd, stdev_id)) return -1; if (_test_ioctl_destroy(self->fd, hwpt_id)) return -1; -- cgit v1.2.3 From 65c619ae06801e1f21cff1a78c7e64923ad17896 Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Wed, 1 Mar 2023 15:30:28 -0400 Subject: iommufd/selftest: Make selftest create a more complete mock device iommufd wants to use more infrastructure, like the iommu_group, that the mock device does not support. Create a more complete mock device that can go through the whole cycle of ownership, blocking domain, and has an iommu_group. This requires creating a real struct device on a real bus to be able to connect it to a iommu_group. Unfortunately we cannot formally attach the mock iommu driver as an actual driver as the iommu core does not allow more than one driver or provide a general way for busses to link to iommus. This can be solved with a little hack to open code the dev_iommus struct. With this infrastructure things work exactly the same as the normal domain path, including the auto domains mechanism and direct attach of hwpts. As the created hwpt is now an autodomain it is no longer required to destroy it and trying to do so will trigger a failure. Link: https://lore.kernel.org/r/11-v3-ae9c2975a131+2e1e8-iommufd_hwpt_jgg@nvidia.com Reviewed-by: Kevin Tian Signed-off-by: Jason Gunthorpe --- drivers/iommu/iommufd/device.c | 40 +---- drivers/iommu/iommufd/iommufd_private.h | 11 +- drivers/iommu/iommufd/selftest.c | 196 ++++++++++++++++++++--- tools/testing/selftests/iommu/iommufd.c | 2 - tools/testing/selftests/iommu/iommufd_fail_nth.c | 8 - 5 files changed, 181 insertions(+), 76 deletions(-) (limited to 'tools') diff --git a/drivers/iommu/iommufd/device.c b/drivers/iommu/iommufd/device.c index 8dc7ed678e3f..c6f4852a8a0c 100644 --- a/drivers/iommu/iommufd/device.c +++ b/drivers/iommu/iommufd/device.c @@ -22,7 +22,8 @@ void iommufd_device_destroy(struct iommufd_object *obj) iommu_device_release_dma_owner(idev->dev); iommu_group_put(idev->group); - iommufd_ctx_put(idev->ictx); + if (!iommufd_selftest_is_mock_dev(idev->dev)) + iommufd_ctx_put(idev->ictx); } /** @@ -69,7 +70,8 @@ struct iommufd_device *iommufd_device_bind(struct iommufd_ctx *ictx, goto out_release_owner; } idev->ictx = ictx; - iommufd_ctx_get(ictx); + if (!iommufd_selftest_is_mock_dev(dev)) + iommufd_ctx_get(ictx); idev->dev = dev; idev->enforce_cache_coherency = device_iommu_capable(dev, IOMMU_CAP_ENFORCE_CACHE_COHERENCY); @@ -151,7 +153,8 @@ static int iommufd_device_setup_msi(struct iommufd_device *idev, * operation from the device (eg a simple DMA) cannot trigger an * interrupt outside this iommufd context. */ - if (!iommu_group_has_isolated_msi(idev->group)) { + if (!iommufd_selftest_is_mock_dev(idev->dev) && + !iommu_group_has_isolated_msi(idev->group)) { if (!allow_unsafe_interrupts) return -EPERM; @@ -706,34 +709,3 @@ err_out: return rc; } EXPORT_SYMBOL_NS_GPL(iommufd_access_rw, IOMMUFD); - -#ifdef CONFIG_IOMMUFD_TEST -/* - * Creating a real iommufd_device is too hard, bypass creating a iommufd_device - * and go directly to attaching a domain. - */ -struct iommufd_hw_pagetable * -iommufd_device_selftest_attach(struct iommufd_ctx *ictx, - struct iommufd_ioas *ioas, - struct device *mock_dev) -{ - struct iommufd_device tmp_idev = { .dev = mock_dev }; - struct iommufd_hw_pagetable *hwpt; - - mutex_lock(&ioas->mutex); - hwpt = iommufd_hw_pagetable_alloc(ictx, ioas, &tmp_idev, false); - mutex_unlock(&ioas->mutex); - if (IS_ERR(hwpt)) - return hwpt; - - refcount_inc(&hwpt->obj.users); - iommufd_object_finalize(ictx, &hwpt->obj); - return hwpt; -} - -void iommufd_device_selftest_detach(struct iommufd_ctx *ictx, - struct iommufd_hw_pagetable *hwpt) -{ - refcount_dec(&hwpt->obj.users); -} -#endif diff --git a/drivers/iommu/iommufd/iommufd_private.h b/drivers/iommu/iommufd/iommufd_private.h index 331664e917b7..d523ef12890e 100644 --- a/drivers/iommu/iommufd/iommufd_private.h +++ b/drivers/iommu/iommufd/iommufd_private.h @@ -297,12 +297,6 @@ void iopt_remove_access(struct io_pagetable *iopt, void iommufd_access_destroy_object(struct iommufd_object *obj); #ifdef CONFIG_IOMMUFD_TEST -struct iommufd_hw_pagetable * -iommufd_device_selftest_attach(struct iommufd_ctx *ictx, - struct iommufd_ioas *ioas, - struct device *mock_dev); -void iommufd_device_selftest_detach(struct iommufd_ctx *ictx, - struct iommufd_hw_pagetable *hwpt); int iommufd_test(struct iommufd_ucmd *ucmd); void iommufd_selftest_destroy(struct iommufd_object *obj); extern size_t iommufd_test_memory_limit; @@ -311,6 +305,7 @@ void iommufd_test_syz_conv_iova_id(struct iommufd_ucmd *ucmd, bool iommufd_should_fail(void); void __init iommufd_test_init(void); void iommufd_test_exit(void); +bool iommufd_selftest_is_mock_dev(struct device *dev); #else static inline void iommufd_test_syz_conv_iova_id(struct iommufd_ucmd *ucmd, unsigned int ioas_id, @@ -327,5 +322,9 @@ static inline void __init iommufd_test_init(void) static inline void iommufd_test_exit(void) { } +static inline bool iommufd_selftest_is_mock_dev(struct device *dev) +{ + return false; +} #endif #endif diff --git a/drivers/iommu/iommufd/selftest.c b/drivers/iommu/iommufd/selftest.c index e05b41059630..17cb7b95eb27 100644 --- a/drivers/iommu/iommufd/selftest.c +++ b/drivers/iommu/iommufd/selftest.c @@ -91,23 +91,50 @@ enum selftest_obj_type { TYPE_IDEV, }; +struct mock_dev { + struct device dev; +}; + struct selftest_obj { struct iommufd_object obj; enum selftest_obj_type type; union { struct { - struct iommufd_hw_pagetable *hwpt; + struct iommufd_device *idev; struct iommufd_ctx *ictx; - struct device mock_dev; + struct mock_dev *mock_dev; } idev; }; }; +static void mock_domain_blocking_free(struct iommu_domain *domain) +{ +} + +static int mock_domain_nop_attach(struct iommu_domain *domain, + struct device *dev) +{ + return 0; +} + +static const struct iommu_domain_ops mock_blocking_ops = { + .free = mock_domain_blocking_free, + .attach_dev = mock_domain_nop_attach, +}; + +static struct iommu_domain mock_blocking_domain = { + .type = IOMMU_DOMAIN_BLOCKED, + .ops = &mock_blocking_ops, +}; + static struct iommu_domain *mock_domain_alloc(unsigned int iommu_domain_type) { struct mock_iommu_domain *mock; + if (iommu_domain_type == IOMMU_DOMAIN_BLOCKED) + return &mock_blocking_domain; + if (WARN_ON(iommu_domain_type != IOMMU_DOMAIN_UNMANAGED)) return NULL; @@ -236,19 +263,39 @@ static phys_addr_t mock_domain_iova_to_phys(struct iommu_domain *domain, return (xa_to_value(ent) & MOCK_PFN_MASK) * MOCK_IO_PAGE_SIZE; } +static bool mock_domain_capable(struct device *dev, enum iommu_cap cap) +{ + return cap == IOMMU_CAP_CACHE_COHERENCY; +} + +static void mock_domain_set_plaform_dma_ops(struct device *dev) +{ + /* + * mock doesn't setup default domains because we can't hook into the + * normal probe path + */ +} + static const struct iommu_ops mock_ops = { .owner = THIS_MODULE, .pgsize_bitmap = MOCK_IO_PAGE_SIZE, .domain_alloc = mock_domain_alloc, + .capable = mock_domain_capable, + .set_platform_dma_ops = mock_domain_set_plaform_dma_ops, .default_domain_ops = &(struct iommu_domain_ops){ .free = mock_domain_free, + .attach_dev = mock_domain_nop_attach, .map_pages = mock_domain_map_pages, .unmap_pages = mock_domain_unmap_pages, .iova_to_phys = mock_domain_iova_to_phys, }, }; +struct iommu_device mock_iommu_device = { + .ops = &mock_ops, +}; + static inline struct iommufd_hw_pagetable * get_md_pagetable(struct iommufd_ucmd *ucmd, u32 mockpt_id, struct mock_iommu_domain **mock) @@ -269,48 +316,142 @@ get_md_pagetable(struct iommufd_ucmd *ucmd, u32 mockpt_id, return hwpt; } +static struct bus_type iommufd_mock_bus_type = { + .name = "iommufd_mock", + .iommu_ops = &mock_ops, +}; + +static void mock_dev_release(struct device *dev) +{ + struct mock_dev *mdev = container_of(dev, struct mock_dev, dev); + + kfree(mdev); +} + +static struct mock_dev *mock_dev_create(void) +{ + struct iommu_group *iommu_group; + struct dev_iommu *dev_iommu; + struct mock_dev *mdev; + int rc; + + mdev = kzalloc(sizeof(*mdev), GFP_KERNEL); + if (!mdev) + return ERR_PTR(-ENOMEM); + + device_initialize(&mdev->dev); + mdev->dev.release = mock_dev_release; + mdev->dev.bus = &iommufd_mock_bus_type; + + iommu_group = iommu_group_alloc(); + if (IS_ERR(iommu_group)) { + rc = PTR_ERR(iommu_group); + goto err_put; + } + + rc = dev_set_name(&mdev->dev, "iommufd_mock%u", + iommu_group_id(iommu_group)); + if (rc) + goto err_group; + + /* + * The iommu core has no way to associate a single device with an iommu + * driver (heck currently it can't even support two iommu_drivers + * registering). Hack it together with an open coded dev_iommu_get(). + * Notice that the normal notifier triggered iommu release process also + * does not work here because this bus is not in iommu_buses. + */ + mdev->dev.iommu = kzalloc(sizeof(*dev_iommu), GFP_KERNEL); + if (!mdev->dev.iommu) { + rc = -ENOMEM; + goto err_group; + } + mutex_init(&mdev->dev.iommu->lock); + mdev->dev.iommu->iommu_dev = &mock_iommu_device; + + rc = device_add(&mdev->dev); + if (rc) + goto err_dev_iommu; + + rc = iommu_group_add_device(iommu_group, &mdev->dev); + if (rc) + goto err_del; + iommu_group_put(iommu_group); + return mdev; + +err_del: + device_del(&mdev->dev); +err_dev_iommu: + kfree(mdev->dev.iommu); + mdev->dev.iommu = NULL; +err_group: + iommu_group_put(iommu_group); +err_put: + put_device(&mdev->dev); + return ERR_PTR(rc); +} + +static void mock_dev_destroy(struct mock_dev *mdev) +{ + iommu_group_remove_device(&mdev->dev); + device_del(&mdev->dev); + kfree(mdev->dev.iommu); + mdev->dev.iommu = NULL; + put_device(&mdev->dev); +} + +bool iommufd_selftest_is_mock_dev(struct device *dev) +{ + return dev->release == mock_dev_release; +} + /* Create an hw_pagetable with the mock domain so we can test the domain ops */ static int iommufd_test_mock_domain(struct iommufd_ucmd *ucmd, struct iommu_test_cmd *cmd) { - static struct bus_type mock_bus = { .iommu_ops = &mock_ops }; - struct iommufd_hw_pagetable *hwpt; + struct iommufd_device *idev; struct selftest_obj *sobj; - struct iommufd_ioas *ioas; + u32 pt_id = cmd->id; + u32 idev_id; int rc; - ioas = iommufd_get_ioas(ucmd, cmd->id); - if (IS_ERR(ioas)) - return PTR_ERR(ioas); - sobj = iommufd_object_alloc(ucmd->ictx, sobj, IOMMUFD_OBJ_SELFTEST); - if (IS_ERR(sobj)) { - rc = PTR_ERR(sobj); - goto out_ioas; - } + if (IS_ERR(sobj)) + return PTR_ERR(sobj); + sobj->idev.ictx = ucmd->ictx; sobj->type = TYPE_IDEV; - sobj->idev.mock_dev.bus = &mock_bus; - hwpt = iommufd_device_selftest_attach(ucmd->ictx, ioas, - &sobj->idev.mock_dev); - if (IS_ERR(hwpt)) { - rc = PTR_ERR(hwpt); + sobj->idev.mock_dev = mock_dev_create(); + if (IS_ERR(sobj->idev.mock_dev)) { + rc = PTR_ERR(sobj->idev.mock_dev); goto out_sobj; } - sobj->idev.hwpt = hwpt; - /* Userspace must destroy both of these IDs to destroy the object */ - cmd->mock_domain.out_hwpt_id = hwpt->obj.id; + idev = iommufd_device_bind(ucmd->ictx, &sobj->idev.mock_dev->dev, + &idev_id); + if (IS_ERR(idev)) { + rc = PTR_ERR(idev); + goto out_mdev; + } + sobj->idev.idev = idev; + + rc = iommufd_device_attach(idev, &pt_id); + if (rc) + goto out_unbind; + + /* Userspace must destroy the device_id to destroy the object */ + cmd->mock_domain.out_hwpt_id = pt_id; cmd->mock_domain.out_stdev_id = sobj->obj.id; iommufd_object_finalize(ucmd->ictx, &sobj->obj); - iommufd_put_object(&ioas->obj); return iommufd_ucmd_respond(ucmd, sizeof(*cmd)); +out_unbind: + iommufd_device_unbind(idev); +out_mdev: + mock_dev_destroy(sobj->idev.mock_dev); out_sobj: iommufd_object_abort(ucmd->ictx, &sobj->obj); -out_ioas: - iommufd_put_object(&ioas->obj); return rc; } @@ -780,8 +921,9 @@ void iommufd_selftest_destroy(struct iommufd_object *obj) switch (sobj->type) { case TYPE_IDEV: - iommufd_device_selftest_detach(sobj->idev.ictx, - sobj->idev.hwpt); + iommufd_device_detach(sobj->idev.idev); + iommufd_device_unbind(sobj->idev.idev); + mock_dev_destroy(sobj->idev.mock_dev); break; } } @@ -845,9 +987,11 @@ void __init iommufd_test_init(void) { dbgfs_root = fault_create_debugfs_attr("fail_iommufd", NULL, &fail_iommufd); + WARN_ON(bus_register(&iommufd_mock_bus_type)); } void iommufd_test_exit(void) { debugfs_remove_recursive(dbgfs_root); + bus_unregister(&iommufd_mock_bus_type); } diff --git a/tools/testing/selftests/iommu/iommufd.c b/tools/testing/selftests/iommu/iommufd.c index 1d72c48157b7..fe20342abfb0 100644 --- a/tools/testing/selftests/iommu/iommufd.c +++ b/tools/testing/selftests/iommu/iommufd.c @@ -645,7 +645,6 @@ TEST_F(iommufd_ioas, access_pin) &check_map_cmd)); test_ioctl_destroy(mock_stdev_id); - test_ioctl_destroy(mock_hwpt_id); test_cmd_destroy_access_pages( access_cmd.id, access_cmd.access_pages.out_access_pages_id); @@ -1214,7 +1213,6 @@ TEST_F(iommufd_mock_domain, all_aligns_copy) 1); test_ioctl_destroy(mock_stdev_id); - test_ioctl_destroy(self->hwpt_ids[1]); self->hwpt_ids[1] = old_id; test_ioctl_ioas_unmap(iova, length); diff --git a/tools/testing/selftests/iommu/iommufd_fail_nth.c b/tools/testing/selftests/iommu/iommufd_fail_nth.c index e7d535680721..d9afcb23810e 100644 --- a/tools/testing/selftests/iommu/iommufd_fail_nth.c +++ b/tools/testing/selftests/iommu/iommufd_fail_nth.c @@ -323,8 +323,6 @@ TEST_FAIL_NTH(basic_fail_nth, map_domain) if (_test_ioctl_destroy(self->fd, stdev_id)) return -1; - if (_test_ioctl_destroy(self->fd, hwpt_id)) - return -1; if (_test_cmd_mock_domain(self->fd, ioas_id, &stdev_id, &hwpt_id)) return -1; @@ -365,13 +363,9 @@ TEST_FAIL_NTH(basic_fail_nth, map_two_domains) if (_test_ioctl_destroy(self->fd, stdev_id)) return -1; - if (_test_ioctl_destroy(self->fd, hwpt_id)) - return -1; if (_test_ioctl_destroy(self->fd, stdev_id2)) return -1; - if (_test_ioctl_destroy(self->fd, hwpt_id2)) - return -1; if (_test_cmd_mock_domain(self->fd, ioas_id, &stdev_id, &hwpt_id)) return -1; @@ -572,8 +566,6 @@ TEST_FAIL_NTH(basic_fail_nth, access_pin_domain) if (_test_ioctl_destroy(self->fd, stdev_id)) return -1; - if (_test_ioctl_destroy(self->fd, hwpt_id)) - return -1; return 0; } -- cgit v1.2.3 From 4ed4791afb34c61650b17407846174a72e4034f4 Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Wed, 1 Mar 2023 15:30:29 -0400 Subject: iommufd/selftest: Add a selftest for iommufd_device_attach() with a hwpt argument This can now be covered since we have a full struct device. Link: https://lore.kernel.org/r/12-v3-ae9c2975a131+2e1e8-iommufd_hwpt_jgg@nvidia.com Reviewed-by: Kevin Tian Signed-off-by: Jason Gunthorpe --- tools/testing/selftests/iommu/iommufd.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/testing/selftests/iommu/iommufd.c b/tools/testing/selftests/iommu/iommufd.c index fe20342abfb0..da0443ba1683 100644 --- a/tools/testing/selftests/iommu/iommufd.c +++ b/tools/testing/selftests/iommu/iommufd.c @@ -187,6 +187,7 @@ FIXTURE(iommufd_ioas) int fd; uint32_t ioas_id; uint32_t stdev_id; + uint32_t hwpt_id; uint64_t base_iova; }; @@ -212,7 +213,8 @@ FIXTURE_SETUP(iommufd_ioas) } for (i = 0; i != variant->mock_domains; i++) { - test_cmd_mock_domain(self->ioas_id, &self->stdev_id, NULL); + test_cmd_mock_domain(self->ioas_id, &self->stdev_id, + &self->hwpt_id); self->base_iova = MOCK_APERTURE_START; } } @@ -259,6 +261,16 @@ TEST_F(iommufd_ioas, ioas_destroy) } } +TEST_F(iommufd_ioas, hwpt_attach) +{ + /* Create a device attached directly to a hwpt */ + if (self->stdev_id) { + test_cmd_mock_domain(self->hwpt_id, NULL, NULL); + } else { + test_err_mock_domain(ENOENT, self->hwpt_id, NULL, NULL); + } +} + TEST_F(iommufd_ioas, ioas_area_destroy) { /* Adding an area does not change ability to destroy */ -- cgit v1.2.3 From 62e37c86bf0718e1ec0156c7a88a43ced6cdf201 Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Fri, 31 Mar 2023 12:32:27 -0300 Subject: iommufd/selftest: Cover domain unmap with huge pages and access Inspired by the syzkaller reproducer check the batch carry path with a simple test. Link: https://lore.kernel.org/r/4-v1-ceab6a4d7d7a+94-iommufd_syz_jgg@nvidia.com Reviewed-by: Kevin Tian Signed-off-by: Jason Gunthorpe --- tools/testing/selftests/iommu/iommufd.c | 34 +++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) (limited to 'tools') diff --git a/tools/testing/selftests/iommu/iommufd.c b/tools/testing/selftests/iommu/iommufd.c index da0443ba1683..e4a6b33cfde4 100644 --- a/tools/testing/selftests/iommu/iommufd.c +++ b/tools/testing/selftests/iommu/iommufd.c @@ -594,6 +594,40 @@ TEST_F(iommufd_ioas, iova_ranges) EXPECT_EQ(0, ranges[1].last); } +TEST_F(iommufd_ioas, access_domain_destory) +{ + struct iommu_test_cmd access_cmd = { + .size = sizeof(access_cmd), + .op = IOMMU_TEST_OP_ACCESS_PAGES, + .access_pages = { .iova = self->base_iova + PAGE_SIZE, + .length = PAGE_SIZE}, + }; + size_t buf_size = 2 * HUGEPAGE_SIZE; + uint8_t *buf; + + buf = mmap(0, buf_size, PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_ANONYMOUS | MAP_HUGETLB | MAP_POPULATE, -1, + 0); + ASSERT_NE(MAP_FAILED, buf); + test_ioctl_ioas_map_fixed(buf, buf_size, self->base_iova); + + test_cmd_create_access(self->ioas_id, &access_cmd.id, + MOCK_FLAGS_ACCESS_CREATE_NEEDS_PIN_PAGES); + access_cmd.access_pages.uptr = (uintptr_t)buf + PAGE_SIZE; + ASSERT_EQ(0, + ioctl(self->fd, _IOMMU_TEST_CMD(IOMMU_TEST_OP_ACCESS_PAGES), + &access_cmd)); + + /* Causes a complicated unpin across a huge page boundary */ + if (self->stdev_id) + test_ioctl_destroy(self->stdev_id); + + test_cmd_destroy_access_pages( + access_cmd.id, access_cmd.access_pages.out_access_pages_id); + test_cmd_destroy_access(access_cmd.id); + ASSERT_EQ(0, munmap(buf, buf_size)); +} + TEST_F(iommufd_ioas, access_pin) { struct iommu_test_cmd access_cmd = { -- cgit v1.2.3