Skip to content

Commit a9af47e

Browse files
jpemartinsjgunthorpe
authored andcommitted
iommufd/selftest: Test IOMMU_HWPT_GET_DIRTY_BITMAP
Add a new test ioctl for simulating the dirty IOVAs in the mock domain, and implement the mock iommu domain ops that get the dirty tracking supported. The selftest exercises the usual main workflow of: 1) Setting dirty tracking from the iommu domain 2) Read and clear dirty IOPTEs Different fixtures will test different IOVA range sizes, that exercise corner cases of the bitmaps. Link: https://lore.kernel.org/r/20231024135109.73787-17-joao.m.martins@oracle.com Signed-off-by: Joao Martins <joao.m.martins@oracle.com> Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
1 parent 7adf267 commit a9af47e

File tree

4 files changed

+334
-5
lines changed

4 files changed

+334
-5
lines changed

drivers/iommu/iommufd/iommufd_test.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ enum {
2020
IOMMU_TEST_OP_MOCK_DOMAIN_REPLACE,
2121
IOMMU_TEST_OP_ACCESS_REPLACE_IOAS,
2222
IOMMU_TEST_OP_MOCK_DOMAIN_FLAGS,
23+
IOMMU_TEST_OP_DIRTY,
2324
};
2425

2526
enum {
@@ -107,6 +108,14 @@ struct iommu_test_cmd {
107108
struct {
108109
__u32 ioas_id;
109110
} access_replace_ioas;
111+
struct {
112+
__u32 flags;
113+
__aligned_u64 iova;
114+
__aligned_u64 length;
115+
__aligned_u64 page_size;
116+
__aligned_u64 uptr;
117+
__aligned_u64 out_nr_dirty;
118+
} dirty;
110119
};
111120
__u32 last;
112121
};

drivers/iommu/iommufd/selftest.c

Lines changed: 102 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ enum {
3737
_MOCK_PFN_START = MOCK_PFN_MASK + 1,
3838
MOCK_PFN_START_IOVA = _MOCK_PFN_START,
3939
MOCK_PFN_LAST_IOVA = _MOCK_PFN_START,
40+
MOCK_PFN_DIRTY_IOVA = _MOCK_PFN_START << 1,
4041
};
4142

4243
/*
@@ -179,6 +180,31 @@ static int mock_domain_read_and_clear_dirty(struct iommu_domain *domain,
179180
unsigned long flags,
180181
struct iommu_dirty_bitmap *dirty)
181182
{
183+
struct mock_iommu_domain *mock =
184+
container_of(domain, struct mock_iommu_domain, domain);
185+
unsigned long i, max = size / MOCK_IO_PAGE_SIZE;
186+
void *ent, *old;
187+
188+
if (!(mock->flags & MOCK_DIRTY_TRACK) && dirty->bitmap)
189+
return -EINVAL;
190+
191+
for (i = 0; i < max; i++) {
192+
unsigned long cur = iova + i * MOCK_IO_PAGE_SIZE;
193+
194+
ent = xa_load(&mock->pfns, cur / MOCK_IO_PAGE_SIZE);
195+
if (ent && (xa_to_value(ent) & MOCK_PFN_DIRTY_IOVA)) {
196+
unsigned long val;
197+
198+
/* Clear dirty */
199+
val = xa_to_value(ent) & ~MOCK_PFN_DIRTY_IOVA;
200+
old = xa_store(&mock->pfns, cur / MOCK_IO_PAGE_SIZE,
201+
xa_mk_value(val), GFP_KERNEL);
202+
WARN_ON_ONCE(ent != old);
203+
iommu_dirty_bitmap_record(dirty, cur,
204+
MOCK_IO_PAGE_SIZE);
205+
}
206+
}
207+
182208
return 0;
183209
}
184210

@@ -310,7 +336,7 @@ static size_t mock_domain_unmap_pages(struct iommu_domain *domain,
310336

311337
for (cur = 0; cur != pgsize; cur += MOCK_IO_PAGE_SIZE) {
312338
ent = xa_erase(&mock->pfns, iova / MOCK_IO_PAGE_SIZE);
313-
WARN_ON(!ent);
339+
314340
/*
315341
* iommufd generates unmaps that must be a strict
316342
* superset of the map's performend So every starting
@@ -320,13 +346,13 @@ static size_t mock_domain_unmap_pages(struct iommu_domain *domain,
320346
* passed to map_pages
321347
*/
322348
if (first) {
323-
WARN_ON(!(xa_to_value(ent) &
324-
MOCK_PFN_START_IOVA));
349+
WARN_ON(ent && !(xa_to_value(ent) &
350+
MOCK_PFN_START_IOVA));
325351
first = false;
326352
}
327353
if (pgcount == 1 && cur + MOCK_IO_PAGE_SIZE == pgsize)
328-
WARN_ON(!(xa_to_value(ent) &
329-
MOCK_PFN_LAST_IOVA));
354+
WARN_ON(ent && !(xa_to_value(ent) &
355+
MOCK_PFN_LAST_IOVA));
330356

331357
iova += MOCK_IO_PAGE_SIZE;
332358
ret += MOCK_IO_PAGE_SIZE;
@@ -1053,6 +1079,71 @@ static_assert((unsigned int)MOCK_ACCESS_RW_WRITE == IOMMUFD_ACCESS_RW_WRITE);
10531079
static_assert((unsigned int)MOCK_ACCESS_RW_SLOW_PATH ==
10541080
__IOMMUFD_ACCESS_RW_SLOW_PATH);
10551081

1082+
static int iommufd_test_dirty(struct iommufd_ucmd *ucmd, unsigned int mockpt_id,
1083+
unsigned long iova, size_t length,
1084+
unsigned long page_size, void __user *uptr,
1085+
u32 flags)
1086+
{
1087+
unsigned long bitmap_size, i, max = length / page_size;
1088+
struct iommu_test_cmd *cmd = ucmd->cmd;
1089+
struct iommufd_hw_pagetable *hwpt;
1090+
struct mock_iommu_domain *mock;
1091+
int rc, count = 0;
1092+
void *tmp;
1093+
1094+
if (iova % page_size || length % page_size || !uptr)
1095+
return -EINVAL;
1096+
1097+
hwpt = get_md_pagetable(ucmd, mockpt_id, &mock);
1098+
if (IS_ERR(hwpt))
1099+
return PTR_ERR(hwpt);
1100+
1101+
if (!(mock->flags & MOCK_DIRTY_TRACK)) {
1102+
rc = -EINVAL;
1103+
goto out_put;
1104+
}
1105+
1106+
bitmap_size = max / BITS_PER_BYTE;
1107+
1108+
tmp = kvzalloc(bitmap_size, GFP_KERNEL_ACCOUNT);
1109+
if (!tmp) {
1110+
rc = -ENOMEM;
1111+
goto out_put;
1112+
}
1113+
1114+
if (copy_from_user(tmp, uptr, bitmap_size)) {
1115+
rc = -EFAULT;
1116+
goto out_free;
1117+
}
1118+
1119+
for (i = 0; i < max; i++) {
1120+
unsigned long cur = iova + i * page_size;
1121+
void *ent, *old;
1122+
1123+
if (!test_bit(i, (unsigned long *)tmp))
1124+
continue;
1125+
1126+
ent = xa_load(&mock->pfns, cur / page_size);
1127+
if (ent) {
1128+
unsigned long val;
1129+
1130+
val = xa_to_value(ent) | MOCK_PFN_DIRTY_IOVA;
1131+
old = xa_store(&mock->pfns, cur / page_size,
1132+
xa_mk_value(val), GFP_KERNEL);
1133+
WARN_ON_ONCE(ent != old);
1134+
count++;
1135+
}
1136+
}
1137+
1138+
cmd->dirty.out_nr_dirty = count;
1139+
rc = iommufd_ucmd_respond(ucmd, sizeof(*cmd));
1140+
out_free:
1141+
kvfree(tmp);
1142+
out_put:
1143+
iommufd_put_object(&hwpt->obj);
1144+
return rc;
1145+
}
1146+
10561147
void iommufd_selftest_destroy(struct iommufd_object *obj)
10571148
{
10581149
struct selftest_obj *sobj = container_of(obj, struct selftest_obj, obj);
@@ -1118,6 +1209,12 @@ int iommufd_test(struct iommufd_ucmd *ucmd)
11181209
return -EINVAL;
11191210
iommufd_test_memory_limit = cmd->memory_limit.limit;
11201211
return 0;
1212+
case IOMMU_TEST_OP_DIRTY:
1213+
return iommufd_test_dirty(ucmd, cmd->id, cmd->dirty.iova,
1214+
cmd->dirty.length,
1215+
cmd->dirty.page_size,
1216+
u64_to_user_ptr(cmd->dirty.uptr),
1217+
cmd->dirty.flags);
11211218
default:
11221219
return -EOPNOTSUPP;
11231220
}

tools/testing/selftests/iommu/iommufd.c

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1440,23 +1440,89 @@ FIXTURE(iommufd_dirty_tracking)
14401440
uint32_t hwpt_id;
14411441
uint32_t stdev_id;
14421442
uint32_t idev_id;
1443+
unsigned long page_size;
1444+
unsigned long bitmap_size;
1445+
void *bitmap;
1446+
void *buffer;
1447+
};
1448+
1449+
FIXTURE_VARIANT(iommufd_dirty_tracking)
1450+
{
1451+
unsigned long buffer_size;
14431452
};
14441453

14451454
FIXTURE_SETUP(iommufd_dirty_tracking)
14461455
{
1456+
void *vrc;
1457+
int rc;
1458+
14471459
self->fd = open("/dev/iommu", O_RDWR);
14481460
ASSERT_NE(-1, self->fd);
14491461

1462+
rc = posix_memalign(&self->buffer, HUGEPAGE_SIZE, variant->buffer_size);
1463+
if (rc || !self->buffer) {
1464+
SKIP(return, "Skipping buffer_size=%lu due to errno=%d",
1465+
variant->buffer_size, rc);
1466+
}
1467+
1468+
assert((uintptr_t)self->buffer % HUGEPAGE_SIZE == 0);
1469+
vrc = mmap(self->buffer, variant->buffer_size, PROT_READ | PROT_WRITE,
1470+
MAP_SHARED | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
1471+
assert(vrc == self->buffer);
1472+
1473+
self->page_size = MOCK_PAGE_SIZE;
1474+
self->bitmap_size =
1475+
variant->buffer_size / self->page_size / BITS_PER_BYTE;
1476+
1477+
/* Provision with an extra (MOCK_PAGE_SIZE) for the unaligned case */
1478+
rc = posix_memalign(&self->bitmap, PAGE_SIZE,
1479+
self->bitmap_size + MOCK_PAGE_SIZE);
1480+
assert(!rc);
1481+
assert(self->bitmap);
1482+
assert((uintptr_t)self->bitmap % PAGE_SIZE == 0);
1483+
14501484
test_ioctl_ioas_alloc(&self->ioas_id);
14511485
test_cmd_mock_domain(self->ioas_id, &self->stdev_id, &self->hwpt_id,
14521486
&self->idev_id);
14531487
}
14541488

14551489
FIXTURE_TEARDOWN(iommufd_dirty_tracking)
14561490
{
1491+
munmap(self->buffer, variant->buffer_size);
1492+
munmap(self->bitmap, self->bitmap_size);
14571493
teardown_iommufd(self->fd, _metadata);
14581494
}
14591495

1496+
FIXTURE_VARIANT_ADD(iommufd_dirty_tracking, domain_dirty128k)
1497+
{
1498+
/* one u32 index bitmap */
1499+
.buffer_size = 128UL * 1024UL,
1500+
};
1501+
1502+
FIXTURE_VARIANT_ADD(iommufd_dirty_tracking, domain_dirty256k)
1503+
{
1504+
/* one u64 index bitmap */
1505+
.buffer_size = 256UL * 1024UL,
1506+
};
1507+
1508+
FIXTURE_VARIANT_ADD(iommufd_dirty_tracking, domain_dirty640k)
1509+
{
1510+
/* two u64 index and trailing end bitmap */
1511+
.buffer_size = 640UL * 1024UL,
1512+
};
1513+
1514+
FIXTURE_VARIANT_ADD(iommufd_dirty_tracking, domain_dirty128M)
1515+
{
1516+
/* 4K bitmap (128M IOVA range) */
1517+
.buffer_size = 128UL * 1024UL * 1024UL,
1518+
};
1519+
1520+
FIXTURE_VARIANT_ADD(iommufd_dirty_tracking, domain_dirty256M)
1521+
{
1522+
/* 8K bitmap (256M IOVA range) */
1523+
.buffer_size = 256UL * 1024UL * 1024UL,
1524+
};
1525+
14601526
TEST_F(iommufd_dirty_tracking, enforce_dirty)
14611527
{
14621528
uint32_t ioas_id, stddev_id, idev_id;
@@ -1497,6 +1563,36 @@ TEST_F(iommufd_dirty_tracking, set_dirty_tracking)
14971563
test_ioctl_destroy(hwpt_id);
14981564
}
14991565

1566+
TEST_F(iommufd_dirty_tracking, get_dirty_bitmap)
1567+
{
1568+
uint32_t stddev_id;
1569+
uint32_t hwpt_id;
1570+
uint32_t ioas_id;
1571+
1572+
test_ioctl_ioas_alloc(&ioas_id);
1573+
test_ioctl_ioas_map_fixed_id(ioas_id, self->buffer,
1574+
variant->buffer_size, MOCK_APERTURE_START);
1575+
1576+
test_cmd_hwpt_alloc(self->idev_id, ioas_id,
1577+
IOMMU_HWPT_ALLOC_DIRTY_TRACKING, &hwpt_id);
1578+
test_cmd_mock_domain(hwpt_id, &stddev_id, NULL, NULL);
1579+
1580+
test_cmd_set_dirty_tracking(hwpt_id, true);
1581+
1582+
test_mock_dirty_bitmaps(hwpt_id, variant->buffer_size,
1583+
MOCK_APERTURE_START, self->page_size,
1584+
self->bitmap, self->bitmap_size, _metadata);
1585+
1586+
/* PAGE_SIZE unaligned bitmap */
1587+
test_mock_dirty_bitmaps(hwpt_id, variant->buffer_size,
1588+
MOCK_APERTURE_START, self->page_size,
1589+
self->bitmap + MOCK_PAGE_SIZE,
1590+
self->bitmap_size, _metadata);
1591+
1592+
test_ioctl_destroy(stddev_id);
1593+
test_ioctl_destroy(hwpt_id);
1594+
}
1595+
15001596
/* VFIO compatibility IOCTLs */
15011597

15021598
TEST_F(iommufd, simple_ioctls)

0 commit comments

Comments
 (0)