Skip to content

Commit

Permalink
iommufd/selftest: Test iommufd_device_replace()
Browse files Browse the repository at this point in the history
Allow the selftest to call the function on the mock idev, add some tests
to exercise it.

Link: https://lore.kernel.org/r/[email protected]
Reviewed-by: Kevin Tian <[email protected]>
Tested-by: Nicolin Chen <[email protected]>
Signed-off-by: Nicolin Chen <[email protected]>
Signed-off-by: Yi Liu <[email protected]>
Signed-off-by: Jason Gunthorpe <[email protected]>
  • Loading branch information
nicolinc authored and jgunthorpe committed Jul 26, 2023
1 parent 83f7bc6 commit fa1ffdb
Show file tree
Hide file tree
Showing 6 changed files with 149 additions and 4 deletions.
4 changes: 4 additions & 0 deletions drivers/iommu/iommufd/iommufd_test.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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;
Expand Down
39 changes: 39 additions & 0 deletions drivers/iommu/iommufd/selftest.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions include/linux/iommufd.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
37 changes: 33 additions & 4 deletions tools/testing/selftests/iommu/iommufd.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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;
};
Expand All @@ -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;
Expand Down Expand Up @@ -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)
Expand Down
42 changes: 42 additions & 0 deletions tools/testing/selftests/iommu/iommufd_fail_nth.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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
30 changes: 30 additions & 0 deletions tools/testing/selftests/iommu/iommufd_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
{
Expand Down

0 comments on commit fa1ffdb

Please sign in to comment.