Commit 55a01657 authored by Nicolin Chen's avatar Nicolin Chen Committed by Jason Gunthorpe
Browse files

iommufd/selftest: Add coverage for IOMMU_HWPT_ALLOC with nested HWPTs

The IOMMU_HWPT_ALLOC ioctl now supports passing user_data to allocate a
user-managed domain for nested HWPTs. Add its coverage for that. Also,
update _test_cmd_hwpt_alloc() and add test_cmd/err_hwpt_alloc_nested().

Link: https://lore.kernel.org/r/20231026043938.63898-11-yi.l.liu@intel.com


Signed-off-by: default avatarNicolin Chen <nicolinc@nvidia.com>
Signed-off-by: default avatarYi Liu <yi.l.liu@intel.com>
Reviewed-by: default avatarKevin Tian <kevin.tian@intel.com>
Signed-off-by: default avatarJason Gunthorpe <jgg@nvidia.com>
parent 65fe32f7
Loading
Loading
Loading
Loading
+115 −0
Original line number Diff line number Diff line
@@ -264,6 +264,121 @@ TEST_F(iommufd_ioas, ioas_destroy)
	}
}

TEST_F(iommufd_ioas, alloc_hwpt_nested)
{
	const uint32_t min_data_len =
		offsetofend(struct iommu_hwpt_selftest, iotlb);
	struct iommu_hwpt_selftest data = {
		.iotlb = IOMMU_TEST_IOTLB_DEFAULT,
	};
	uint32_t nested_hwpt_id[2] = {};
	uint32_t parent_hwpt_id = 0;
	uint32_t parent_hwpt_id_not_work = 0;
	uint32_t test_hwpt_id = 0;

	if (self->device_id) {
		/* Negative tests */
		test_err_hwpt_alloc(ENOENT, self->ioas_id, self->device_id, 0,
				    &test_hwpt_id);
		test_err_hwpt_alloc(EINVAL, self->device_id, self->device_id, 0,
				    &test_hwpt_id);

		test_cmd_hwpt_alloc(self->device_id, self->ioas_id,
				    IOMMU_HWPT_ALLOC_NEST_PARENT,
				    &parent_hwpt_id);

		test_cmd_hwpt_alloc(self->device_id, self->ioas_id, 0,
				    &parent_hwpt_id_not_work);

		/* Negative nested tests */
		test_err_hwpt_alloc_nested(EINVAL, self->device_id,
					   parent_hwpt_id, 0,
					   &nested_hwpt_id[0],
					   IOMMU_HWPT_DATA_NONE, &data,
					   sizeof(data));
		test_err_hwpt_alloc_nested(EOPNOTSUPP, self->device_id,
					   parent_hwpt_id, 0,
					   &nested_hwpt_id[0],
					   IOMMU_HWPT_DATA_SELFTEST + 1, &data,
					   sizeof(data));
		test_err_hwpt_alloc_nested(EINVAL, self->device_id,
					   parent_hwpt_id, 0,
					   &nested_hwpt_id[0],
					   IOMMU_HWPT_DATA_SELFTEST, &data,
					   min_data_len - 1);
		test_err_hwpt_alloc_nested(EFAULT, self->device_id,
					   parent_hwpt_id, 0,
					   &nested_hwpt_id[0],
					   IOMMU_HWPT_DATA_SELFTEST, NULL,
					   sizeof(data));
		test_err_hwpt_alloc_nested(
			EOPNOTSUPP, self->device_id, parent_hwpt_id,
			IOMMU_HWPT_ALLOC_NEST_PARENT, &nested_hwpt_id[0],
			IOMMU_HWPT_DATA_SELFTEST, &data, sizeof(data));
		test_err_hwpt_alloc_nested(EINVAL, self->device_id,
					   parent_hwpt_id_not_work, 0,
					   &nested_hwpt_id[0],
					   IOMMU_HWPT_DATA_SELFTEST, &data,
					   sizeof(data));

		/* Allocate two nested hwpts sharing one common parent hwpt */
		test_cmd_hwpt_alloc_nested(self->device_id, parent_hwpt_id, 0,
					   &nested_hwpt_id[0],
					   IOMMU_HWPT_DATA_SELFTEST, &data,
					   sizeof(data));
		test_cmd_hwpt_alloc_nested(self->device_id, parent_hwpt_id, 0,
					   &nested_hwpt_id[1],
					   IOMMU_HWPT_DATA_SELFTEST, &data,
					   sizeof(data));

		/* Negative test: a nested hwpt on top of a nested hwpt */
		test_err_hwpt_alloc_nested(EINVAL, self->device_id,
					   nested_hwpt_id[0], 0, &test_hwpt_id,
					   IOMMU_HWPT_DATA_SELFTEST, &data,
					   sizeof(data));
		/* Negative test: parent hwpt now cannot be freed */
		EXPECT_ERRNO(EBUSY,
			     _test_ioctl_destroy(self->fd, parent_hwpt_id));

		/* Attach device to nested_hwpt_id[0] that then will be busy */
		test_cmd_mock_domain_replace(self->stdev_id, nested_hwpt_id[0]);
		EXPECT_ERRNO(EBUSY,
			     _test_ioctl_destroy(self->fd, nested_hwpt_id[0]));

		/* Switch from nested_hwpt_id[0] to nested_hwpt_id[1] */
		test_cmd_mock_domain_replace(self->stdev_id, nested_hwpt_id[1]);
		EXPECT_ERRNO(EBUSY,
			     _test_ioctl_destroy(self->fd, nested_hwpt_id[1]));
		test_ioctl_destroy(nested_hwpt_id[0]);

		/* Detach from nested_hwpt_id[1] and destroy it */
		test_cmd_mock_domain_replace(self->stdev_id, parent_hwpt_id);
		test_ioctl_destroy(nested_hwpt_id[1]);

		/* Detach from the parent hw_pagetable and destroy it */
		test_cmd_mock_domain_replace(self->stdev_id, self->ioas_id);
		test_ioctl_destroy(parent_hwpt_id);
		test_ioctl_destroy(parent_hwpt_id_not_work);
	} else {
		test_err_hwpt_alloc(ENOENT, self->device_id, self->ioas_id, 0,
				    &parent_hwpt_id);
		test_err_hwpt_alloc_nested(ENOENT, self->device_id,
					   parent_hwpt_id, 0,
					   &nested_hwpt_id[0],
					   IOMMU_HWPT_DATA_SELFTEST, &data,
					   sizeof(data));
		test_err_hwpt_alloc_nested(ENOENT, self->device_id,
					   parent_hwpt_id, 0,
					   &nested_hwpt_id[1],
					   IOMMU_HWPT_DATA_SELFTEST, &data,
					   sizeof(data));
		test_err_mock_domain_replace(ENOENT, self->stdev_id,
					     nested_hwpt_id[0]);
		test_err_mock_domain_replace(ENOENT, self->stdev_id,
					     nested_hwpt_id[1]);
	}
}

TEST_F(iommufd_ioas, hwpt_attach)
{
	/* Create a device attached directly to a hwpt */
+2 −1
Original line number Diff line number Diff line
@@ -615,7 +615,8 @@ TEST_FAIL_NTH(basic_fail_nth, device)
	if (_test_cmd_get_hw_info(self->fd, idev_id, &info, sizeof(info), NULL))
		return -1;

	if (_test_cmd_hwpt_alloc(self->fd, idev_id, ioas_id, 0, &hwpt_id))
	if (_test_cmd_hwpt_alloc(self->fd, idev_id, ioas_id, 0, &hwpt_id,
				 IOMMU_HWPT_DATA_NONE, 0, 0))
		return -1;

	if (_test_cmd_mock_domain_replace(self->fd, stdev_id, ioas_id2, NULL))
+23 −7
Original line number Diff line number Diff line
@@ -154,13 +154,17 @@ static int _test_cmd_mock_domain_replace(int fd, __u32 stdev_id, __u32 pt_id,
							   pt_id, NULL))

static int _test_cmd_hwpt_alloc(int fd, __u32 device_id, __u32 pt_id,
				__u32 flags, __u32 *hwpt_id)
				__u32 flags, __u32 *hwpt_id, __u32 data_type,
				void *data, size_t data_len)
{
	struct iommu_hwpt_alloc cmd = {
		.size = sizeof(cmd),
		.flags = flags,
		.dev_id = device_id,
		.pt_id = pt_id,
		.data_type = data_type,
		.data_len = data_len,
		.data_uptr = (uint64_t)data,
	};
	int ret;

@@ -173,11 +177,23 @@ static int _test_cmd_hwpt_alloc(int fd, __u32 device_id, __u32 pt_id,
}

#define test_cmd_hwpt_alloc(device_id, pt_id, flags, hwpt_id)                  \
	ASSERT_EQ(0, _test_cmd_hwpt_alloc(self->fd, device_id, \
					  pt_id, flags, hwpt_id))
	ASSERT_EQ(0, _test_cmd_hwpt_alloc(self->fd, device_id, pt_id, flags,   \
					  hwpt_id, IOMMU_HWPT_DATA_NONE, NULL, \
					  0))
#define test_err_hwpt_alloc(_errno, device_id, pt_id, flags, hwpt_id)   \
	EXPECT_ERRNO(_errno, _test_cmd_hwpt_alloc(self->fd, device_id, \
						  pt_id, flags, hwpt_id))
	EXPECT_ERRNO(_errno, _test_cmd_hwpt_alloc(                      \
				     self->fd, device_id, pt_id, flags, \
				     hwpt_id, IOMMU_HWPT_DATA_NONE, NULL, 0))

#define test_cmd_hwpt_alloc_nested(device_id, pt_id, flags, hwpt_id,         \
				   data_type, data, data_len)                \
	ASSERT_EQ(0, _test_cmd_hwpt_alloc(self->fd, device_id, pt_id, flags, \
					  hwpt_id, data_type, data, data_len))
#define test_err_hwpt_alloc_nested(_errno, device_id, pt_id, flags, hwpt_id, \
				   data_type, data, data_len)                \
	EXPECT_ERRNO(_errno,                                                 \
		     _test_cmd_hwpt_alloc(self->fd, device_id, pt_id, flags, \
					  hwpt_id, data_type, data, data_len))

static int _test_cmd_access_replace_ioas(int fd, __u32 access_id,
					 unsigned int ioas_id)