iommufd/selftest: Test iommufd_device_replace()

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>
This commit is contained in:
Nicolin Chen 2023-07-17 15:12:12 -03:00 committed by Jason Gunthorpe
parent 83f7bc6fdf
commit fa1ffdb9e2
6 changed files with 149 additions and 4 deletions

View 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 {
@ -52,6 +53,9 @@ struct iommu_test_cmd {
__u32 out_stdev_id;
__u32 out_hwpt_id;
} mock_domain;
struct {
__u32 pt_id;
} mock_domain_replace;
struct {
__aligned_u64 iova;
__aligned_u64 length;

View file

@ -455,6 +455,42 @@ static int iommufd_test_mock_domain(struct iommufd_ucmd *ucmd,
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,

View 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);

View 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)

View 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

View 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)
{