mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-12-04 01:24:12 +08:00
iommufd for 6.8 rc
- Fix dirty tracking bitmap collection when using reporting bitmaps that are not neatly aligned to u64's or match the IO page table radix tree layout. - Add self tests to cover the cases that were found to be broken. - Add missing enforcement of invalidation type in the uapi. - Fix selftest config generation -----BEGIN PGP SIGNATURE----- iHUEABYIAB0WIQRRRCHOFoQz/8F5bUaFwuHvBreFYQUCZddJHAAKCRCFwuHvBreF YaRyAQCuywXMOQsPcTXZk+bepQ0EacRZKfyPrIcKMaHC1QLwKgEA9ApiVSbai0Q+ 5IFX+rWrLUB4jiH5D12kmfhxgydFDAk= =CCxs -----END PGP SIGNATURE----- Merge tag 'for-linus-iommufd' of git://git.kernel.org/pub/scm/linux/kernel/git/jgg/iommufd Pull iommufd fixes from Jason Gunthorpe: - Fix dirty tracking bitmap collection when using reporting bitmaps that are not neatly aligned to u64's or match the IO page table radix tree layout. - Add self tests to cover the cases that were found to be broken. - Add missing enforcement of invalidation type in the uapi. - Fix selftest config generation * tag 'for-linus-iommufd' of git://git.kernel.org/pub/scm/linux/kernel/git/jgg/iommufd: selftests/iommu: fix the config fragment iommufd: Reject non-zero data_type if no data_len is provided iommufd/iova_bitmap: Consider page offset for the pages to be pinned iommufd/selftest: Add mock IO hugepages tests iommufd/selftest: Hugepage mock domain support iommufd/selftest: Refactor mock_domain_read_and_clear_dirty() iommufd/selftest: Refactor dirty bitmap tests iommufd/iova_bitmap: Handle recording beyond the mapped pages iommufd/selftest: Test u64 unaligned bitmaps iommufd/iova_bitmap: Switch iova_bitmap::bitmap to an u8 array iommufd/iova_bitmap: Bounds check mapped::pages access
This commit is contained in:
commit
4c36fbb46f
@ -263,7 +263,8 @@ int iommufd_hwpt_alloc(struct iommufd_ucmd *ucmd)
|
||||
|
||||
if (cmd->__reserved)
|
||||
return -EOPNOTSUPP;
|
||||
if (cmd->data_type == IOMMU_HWPT_DATA_NONE && cmd->data_len)
|
||||
if ((cmd->data_type == IOMMU_HWPT_DATA_NONE && cmd->data_len) ||
|
||||
(cmd->data_type != IOMMU_HWPT_DATA_NONE && !cmd->data_len))
|
||||
return -EINVAL;
|
||||
|
||||
idev = iommufd_get_device(ucmd, cmd->dev_id);
|
||||
|
@ -45,6 +45,7 @@ enum {
|
||||
|
||||
enum {
|
||||
MOCK_FLAGS_DEVICE_NO_DIRTY = 1 << 0,
|
||||
MOCK_FLAGS_DEVICE_HUGE_IOVA = 1 << 1,
|
||||
};
|
||||
|
||||
enum {
|
||||
|
@ -100,7 +100,7 @@ struct iova_bitmap {
|
||||
struct iova_bitmap_map mapped;
|
||||
|
||||
/* userspace address of the bitmap */
|
||||
u64 __user *bitmap;
|
||||
u8 __user *bitmap;
|
||||
|
||||
/* u64 index that @mapped points to */
|
||||
unsigned long mapped_base_index;
|
||||
@ -113,6 +113,9 @@ struct iova_bitmap {
|
||||
|
||||
/* length of the IOVA range for the whole bitmap */
|
||||
size_t length;
|
||||
|
||||
/* length of the IOVA range set ahead the pinned pages */
|
||||
unsigned long set_ahead_length;
|
||||
};
|
||||
|
||||
/*
|
||||
@ -162,7 +165,7 @@ static int iova_bitmap_get(struct iova_bitmap *bitmap)
|
||||
{
|
||||
struct iova_bitmap_map *mapped = &bitmap->mapped;
|
||||
unsigned long npages;
|
||||
u64 __user *addr;
|
||||
u8 __user *addr;
|
||||
long ret;
|
||||
|
||||
/*
|
||||
@ -175,18 +178,19 @@ static int iova_bitmap_get(struct iova_bitmap *bitmap)
|
||||
bitmap->mapped_base_index) *
|
||||
sizeof(*bitmap->bitmap), PAGE_SIZE);
|
||||
|
||||
/*
|
||||
* We always cap at max number of 'struct page' a base page can fit.
|
||||
* This is, for example, on x86 means 2M of bitmap data max.
|
||||
*/
|
||||
npages = min(npages, PAGE_SIZE / sizeof(struct page *));
|
||||
|
||||
/*
|
||||
* Bitmap address to be pinned is calculated via pointer arithmetic
|
||||
* with bitmap u64 word index.
|
||||
*/
|
||||
addr = bitmap->bitmap + bitmap->mapped_base_index;
|
||||
|
||||
/*
|
||||
* We always cap at max number of 'struct page' a base page can fit.
|
||||
* This is, for example, on x86 means 2M of bitmap data max.
|
||||
*/
|
||||
npages = min(npages + !!offset_in_page(addr),
|
||||
PAGE_SIZE / sizeof(struct page *));
|
||||
|
||||
ret = pin_user_pages_fast((unsigned long)addr, npages,
|
||||
FOLL_WRITE, mapped->pages);
|
||||
if (ret <= 0)
|
||||
@ -247,7 +251,7 @@ struct iova_bitmap *iova_bitmap_alloc(unsigned long iova, size_t length,
|
||||
|
||||
mapped = &bitmap->mapped;
|
||||
mapped->pgshift = __ffs(page_size);
|
||||
bitmap->bitmap = data;
|
||||
bitmap->bitmap = (u8 __user *)data;
|
||||
bitmap->mapped_total_index =
|
||||
iova_bitmap_offset_to_index(bitmap, length - 1) + 1;
|
||||
bitmap->iova = iova;
|
||||
@ -304,7 +308,7 @@ static unsigned long iova_bitmap_mapped_remaining(struct iova_bitmap *bitmap)
|
||||
|
||||
remaining = bitmap->mapped_total_index - bitmap->mapped_base_index;
|
||||
remaining = min_t(unsigned long, remaining,
|
||||
bytes / sizeof(*bitmap->bitmap));
|
||||
DIV_ROUND_UP(bytes, sizeof(*bitmap->bitmap)));
|
||||
|
||||
return remaining;
|
||||
}
|
||||
@ -341,6 +345,32 @@ static bool iova_bitmap_done(struct iova_bitmap *bitmap)
|
||||
return bitmap->mapped_base_index >= bitmap->mapped_total_index;
|
||||
}
|
||||
|
||||
static int iova_bitmap_set_ahead(struct iova_bitmap *bitmap,
|
||||
size_t set_ahead_length)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
while (set_ahead_length > 0 && !iova_bitmap_done(bitmap)) {
|
||||
unsigned long length = iova_bitmap_mapped_length(bitmap);
|
||||
unsigned long iova = iova_bitmap_mapped_iova(bitmap);
|
||||
|
||||
ret = iova_bitmap_get(bitmap);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
length = min(length, set_ahead_length);
|
||||
iova_bitmap_set(bitmap, iova, length);
|
||||
|
||||
set_ahead_length -= length;
|
||||
bitmap->mapped_base_index +=
|
||||
iova_bitmap_offset_to_index(bitmap, length - 1) + 1;
|
||||
iova_bitmap_put(bitmap);
|
||||
}
|
||||
|
||||
bitmap->set_ahead_length = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Advances to the next range, releases the current pinned
|
||||
* pages and pins the next set of bitmap pages.
|
||||
@ -357,6 +387,15 @@ static int iova_bitmap_advance(struct iova_bitmap *bitmap)
|
||||
if (iova_bitmap_done(bitmap))
|
||||
return 0;
|
||||
|
||||
/* Iterate, set and skip any bits requested for next iteration */
|
||||
if (bitmap->set_ahead_length) {
|
||||
int ret;
|
||||
|
||||
ret = iova_bitmap_set_ahead(bitmap, bitmap->set_ahead_length);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* When advancing the index we pin the next set of bitmap pages */
|
||||
return iova_bitmap_get(bitmap);
|
||||
}
|
||||
@ -409,6 +448,7 @@ void iova_bitmap_set(struct iova_bitmap *bitmap,
|
||||
mapped->pgshift) + mapped->pgoff * BITS_PER_BYTE;
|
||||
unsigned long last_bit = (((iova + length - 1) - mapped->iova) >>
|
||||
mapped->pgshift) + mapped->pgoff * BITS_PER_BYTE;
|
||||
unsigned long last_page_idx = mapped->npages - 1;
|
||||
|
||||
do {
|
||||
unsigned int page_idx = cur_bit / BITS_PER_PAGE;
|
||||
@ -417,10 +457,18 @@ void iova_bitmap_set(struct iova_bitmap *bitmap,
|
||||
last_bit - cur_bit + 1);
|
||||
void *kaddr;
|
||||
|
||||
if (unlikely(page_idx > last_page_idx))
|
||||
break;
|
||||
|
||||
kaddr = kmap_local_page(mapped->pages[page_idx]);
|
||||
bitmap_set(kaddr, offset, nbits);
|
||||
kunmap_local(kaddr);
|
||||
cur_bit += nbits;
|
||||
} while (cur_bit <= last_bit);
|
||||
|
||||
if (unlikely(cur_bit <= last_bit)) {
|
||||
bitmap->set_ahead_length =
|
||||
((last_bit - cur_bit + 1) << bitmap->mapped.pgshift);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(iova_bitmap_set, IOMMUFD);
|
||||
|
@ -41,6 +41,7 @@ static atomic_t mock_dev_num;
|
||||
enum {
|
||||
MOCK_DIRTY_TRACK = 1,
|
||||
MOCK_IO_PAGE_SIZE = PAGE_SIZE / 2,
|
||||
MOCK_HUGE_PAGE_SIZE = 512 * MOCK_IO_PAGE_SIZE,
|
||||
|
||||
/*
|
||||
* Like a real page table alignment requires the low bits of the address
|
||||
@ -53,6 +54,7 @@ enum {
|
||||
MOCK_PFN_START_IOVA = _MOCK_PFN_START,
|
||||
MOCK_PFN_LAST_IOVA = _MOCK_PFN_START,
|
||||
MOCK_PFN_DIRTY_IOVA = _MOCK_PFN_START << 1,
|
||||
MOCK_PFN_HUGE_IOVA = _MOCK_PFN_START << 2,
|
||||
};
|
||||
|
||||
/*
|
||||
@ -191,6 +193,34 @@ static int mock_domain_set_dirty_tracking(struct iommu_domain *domain,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool mock_test_and_clear_dirty(struct mock_iommu_domain *mock,
|
||||
unsigned long iova, size_t page_size,
|
||||
unsigned long flags)
|
||||
{
|
||||
unsigned long cur, end = iova + page_size - 1;
|
||||
bool dirty = false;
|
||||
void *ent, *old;
|
||||
|
||||
for (cur = iova; cur < end; cur += MOCK_IO_PAGE_SIZE) {
|
||||
ent = xa_load(&mock->pfns, cur / MOCK_IO_PAGE_SIZE);
|
||||
if (!ent || !(xa_to_value(ent) & MOCK_PFN_DIRTY_IOVA))
|
||||
continue;
|
||||
|
||||
dirty = true;
|
||||
/* Clear dirty */
|
||||
if (!(flags & IOMMU_DIRTY_NO_CLEAR)) {
|
||||
unsigned long val;
|
||||
|
||||
val = xa_to_value(ent) & ~MOCK_PFN_DIRTY_IOVA;
|
||||
old = xa_store(&mock->pfns, cur / MOCK_IO_PAGE_SIZE,
|
||||
xa_mk_value(val), GFP_KERNEL);
|
||||
WARN_ON_ONCE(ent != old);
|
||||
}
|
||||
}
|
||||
|
||||
return dirty;
|
||||
}
|
||||
|
||||
static int mock_domain_read_and_clear_dirty(struct iommu_domain *domain,
|
||||
unsigned long iova, size_t size,
|
||||
unsigned long flags,
|
||||
@ -198,31 +228,31 @@ static int mock_domain_read_and_clear_dirty(struct iommu_domain *domain,
|
||||
{
|
||||
struct mock_iommu_domain *mock =
|
||||
container_of(domain, struct mock_iommu_domain, domain);
|
||||
unsigned long i, max = size / MOCK_IO_PAGE_SIZE;
|
||||
void *ent, *old;
|
||||
unsigned long end = iova + size;
|
||||
void *ent;
|
||||
|
||||
if (!(mock->flags & MOCK_DIRTY_TRACK) && dirty->bitmap)
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < max; i++) {
|
||||
unsigned long cur = iova + i * MOCK_IO_PAGE_SIZE;
|
||||
do {
|
||||
unsigned long pgsize = MOCK_IO_PAGE_SIZE;
|
||||
unsigned long head;
|
||||
|
||||
ent = xa_load(&mock->pfns, cur / MOCK_IO_PAGE_SIZE);
|
||||
if (ent && (xa_to_value(ent) & MOCK_PFN_DIRTY_IOVA)) {
|
||||
/* Clear dirty */
|
||||
if (!(flags & IOMMU_DIRTY_NO_CLEAR)) {
|
||||
unsigned long val;
|
||||
|
||||
val = xa_to_value(ent) & ~MOCK_PFN_DIRTY_IOVA;
|
||||
old = xa_store(&mock->pfns,
|
||||
cur / MOCK_IO_PAGE_SIZE,
|
||||
xa_mk_value(val), GFP_KERNEL);
|
||||
WARN_ON_ONCE(ent != old);
|
||||
}
|
||||
iommu_dirty_bitmap_record(dirty, cur,
|
||||
MOCK_IO_PAGE_SIZE);
|
||||
ent = xa_load(&mock->pfns, iova / MOCK_IO_PAGE_SIZE);
|
||||
if (!ent) {
|
||||
iova += pgsize;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (xa_to_value(ent) & MOCK_PFN_HUGE_IOVA)
|
||||
pgsize = MOCK_HUGE_PAGE_SIZE;
|
||||
head = iova & ~(pgsize - 1);
|
||||
|
||||
/* Clear dirty */
|
||||
if (mock_test_and_clear_dirty(mock, head, pgsize, flags))
|
||||
iommu_dirty_bitmap_record(dirty, head, pgsize);
|
||||
iova = head + pgsize;
|
||||
} while (iova < end);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -234,6 +264,7 @@ const struct iommu_dirty_ops dirty_ops = {
|
||||
|
||||
static struct iommu_domain *mock_domain_alloc_paging(struct device *dev)
|
||||
{
|
||||
struct mock_dev *mdev = container_of(dev, struct mock_dev, dev);
|
||||
struct mock_iommu_domain *mock;
|
||||
|
||||
mock = kzalloc(sizeof(*mock), GFP_KERNEL);
|
||||
@ -242,6 +273,8 @@ static struct iommu_domain *mock_domain_alloc_paging(struct device *dev)
|
||||
mock->domain.geometry.aperture_start = MOCK_APERTURE_START;
|
||||
mock->domain.geometry.aperture_end = MOCK_APERTURE_LAST;
|
||||
mock->domain.pgsize_bitmap = MOCK_IO_PAGE_SIZE;
|
||||
if (dev && mdev->flags & MOCK_FLAGS_DEVICE_HUGE_IOVA)
|
||||
mock->domain.pgsize_bitmap |= MOCK_HUGE_PAGE_SIZE;
|
||||
mock->domain.ops = mock_ops.default_domain_ops;
|
||||
mock->domain.type = IOMMU_DOMAIN_UNMANAGED;
|
||||
xa_init(&mock->pfns);
|
||||
@ -287,7 +320,7 @@ mock_domain_alloc_user(struct device *dev, u32 flags,
|
||||
return ERR_PTR(-EOPNOTSUPP);
|
||||
if (user_data || (has_dirty_flag && no_dirty_ops))
|
||||
return ERR_PTR(-EOPNOTSUPP);
|
||||
domain = mock_domain_alloc_paging(NULL);
|
||||
domain = mock_domain_alloc_paging(dev);
|
||||
if (!domain)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
if (has_dirty_flag)
|
||||
@ -350,6 +383,9 @@ static int mock_domain_map_pages(struct iommu_domain *domain,
|
||||
|
||||
if (pgcount == 1 && cur + MOCK_IO_PAGE_SIZE == pgsize)
|
||||
flags = MOCK_PFN_LAST_IOVA;
|
||||
if (pgsize != MOCK_IO_PAGE_SIZE) {
|
||||
flags |= MOCK_PFN_HUGE_IOVA;
|
||||
}
|
||||
old = xa_store(&mock->pfns, iova / MOCK_IO_PAGE_SIZE,
|
||||
xa_mk_value((paddr / MOCK_IO_PAGE_SIZE) |
|
||||
flags),
|
||||
@ -604,7 +640,8 @@ static struct mock_dev *mock_dev_create(unsigned long dev_flags)
|
||||
struct mock_dev *mdev;
|
||||
int rc;
|
||||
|
||||
if (dev_flags & ~(MOCK_FLAGS_DEVICE_NO_DIRTY))
|
||||
if (dev_flags &
|
||||
~(MOCK_FLAGS_DEVICE_NO_DIRTY | MOCK_FLAGS_DEVICE_HUGE_IOVA))
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
mdev = kzalloc(sizeof(*mdev), GFP_KERNEL);
|
||||
|
@ -1,2 +1,3 @@
|
||||
CONFIG_IOMMUFD
|
||||
CONFIG_IOMMUFD_TEST
|
||||
CONFIG_IOMMUFD=y
|
||||
CONFIG_FAULT_INJECTION=y
|
||||
CONFIG_IOMMUFD_TEST=y
|
||||
|
@ -12,6 +12,7 @@
|
||||
static unsigned long HUGEPAGE_SIZE;
|
||||
|
||||
#define MOCK_PAGE_SIZE (PAGE_SIZE / 2)
|
||||
#define MOCK_HUGE_PAGE_SIZE (512 * MOCK_PAGE_SIZE)
|
||||
|
||||
static unsigned long get_huge_page_size(void)
|
||||
{
|
||||
@ -1716,10 +1717,12 @@ FIXTURE(iommufd_dirty_tracking)
|
||||
FIXTURE_VARIANT(iommufd_dirty_tracking)
|
||||
{
|
||||
unsigned long buffer_size;
|
||||
bool hugepages;
|
||||
};
|
||||
|
||||
FIXTURE_SETUP(iommufd_dirty_tracking)
|
||||
{
|
||||
int mmap_flags;
|
||||
void *vrc;
|
||||
int rc;
|
||||
|
||||
@ -1732,25 +1735,41 @@ FIXTURE_SETUP(iommufd_dirty_tracking)
|
||||
variant->buffer_size, rc);
|
||||
}
|
||||
|
||||
mmap_flags = MAP_SHARED | MAP_ANONYMOUS | MAP_FIXED;
|
||||
if (variant->hugepages) {
|
||||
/*
|
||||
* MAP_POPULATE will cause the kernel to fail mmap if THPs are
|
||||
* not available.
|
||||
*/
|
||||
mmap_flags |= MAP_HUGETLB | MAP_POPULATE;
|
||||
}
|
||||
assert((uintptr_t)self->buffer % HUGEPAGE_SIZE == 0);
|
||||
vrc = mmap(self->buffer, variant->buffer_size, PROT_READ | PROT_WRITE,
|
||||
MAP_SHARED | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
|
||||
mmap_flags, -1, 0);
|
||||
assert(vrc == self->buffer);
|
||||
|
||||
self->page_size = MOCK_PAGE_SIZE;
|
||||
self->bitmap_size =
|
||||
variant->buffer_size / self->page_size / BITS_PER_BYTE;
|
||||
|
||||
/* Provision with an extra (MOCK_PAGE_SIZE) for the unaligned case */
|
||||
/* Provision with an extra (PAGE_SIZE) for the unaligned case */
|
||||
rc = posix_memalign(&self->bitmap, PAGE_SIZE,
|
||||
self->bitmap_size + MOCK_PAGE_SIZE);
|
||||
self->bitmap_size + PAGE_SIZE);
|
||||
assert(!rc);
|
||||
assert(self->bitmap);
|
||||
assert((uintptr_t)self->bitmap % PAGE_SIZE == 0);
|
||||
|
||||
test_ioctl_ioas_alloc(&self->ioas_id);
|
||||
test_cmd_mock_domain(self->ioas_id, &self->stdev_id, &self->hwpt_id,
|
||||
&self->idev_id);
|
||||
/* Enable 1M mock IOMMU hugepages */
|
||||
if (variant->hugepages) {
|
||||
test_cmd_mock_domain_flags(self->ioas_id,
|
||||
MOCK_FLAGS_DEVICE_HUGE_IOVA,
|
||||
&self->stdev_id, &self->hwpt_id,
|
||||
&self->idev_id);
|
||||
} else {
|
||||
test_cmd_mock_domain(self->ioas_id, &self->stdev_id,
|
||||
&self->hwpt_id, &self->idev_id);
|
||||
}
|
||||
}
|
||||
|
||||
FIXTURE_TEARDOWN(iommufd_dirty_tracking)
|
||||
@ -1784,12 +1803,26 @@ FIXTURE_VARIANT_ADD(iommufd_dirty_tracking, domain_dirty128M)
|
||||
.buffer_size = 128UL * 1024UL * 1024UL,
|
||||
};
|
||||
|
||||
FIXTURE_VARIANT_ADD(iommufd_dirty_tracking, domain_dirty128M_huge)
|
||||
{
|
||||
/* 4K bitmap (128M IOVA range) */
|
||||
.buffer_size = 128UL * 1024UL * 1024UL,
|
||||
.hugepages = true,
|
||||
};
|
||||
|
||||
FIXTURE_VARIANT_ADD(iommufd_dirty_tracking, domain_dirty256M)
|
||||
{
|
||||
/* 8K bitmap (256M IOVA range) */
|
||||
.buffer_size = 256UL * 1024UL * 1024UL,
|
||||
};
|
||||
|
||||
FIXTURE_VARIANT_ADD(iommufd_dirty_tracking, domain_dirty256M_huge)
|
||||
{
|
||||
/* 8K bitmap (256M IOVA range) */
|
||||
.buffer_size = 256UL * 1024UL * 1024UL,
|
||||
.hugepages = true,
|
||||
};
|
||||
|
||||
TEST_F(iommufd_dirty_tracking, enforce_dirty)
|
||||
{
|
||||
uint32_t ioas_id, stddev_id, idev_id;
|
||||
@ -1849,65 +1882,80 @@ TEST_F(iommufd_dirty_tracking, device_dirty_capability)
|
||||
|
||||
TEST_F(iommufd_dirty_tracking, get_dirty_bitmap)
|
||||
{
|
||||
uint32_t stddev_id;
|
||||
uint32_t page_size = MOCK_PAGE_SIZE;
|
||||
uint32_t hwpt_id;
|
||||
uint32_t ioas_id;
|
||||
|
||||
if (variant->hugepages)
|
||||
page_size = MOCK_HUGE_PAGE_SIZE;
|
||||
|
||||
test_ioctl_ioas_alloc(&ioas_id);
|
||||
test_ioctl_ioas_map_fixed_id(ioas_id, self->buffer,
|
||||
variant->buffer_size, MOCK_APERTURE_START);
|
||||
|
||||
test_cmd_hwpt_alloc(self->idev_id, ioas_id,
|
||||
IOMMU_HWPT_ALLOC_DIRTY_TRACKING, &hwpt_id);
|
||||
test_cmd_mock_domain(hwpt_id, &stddev_id, NULL, NULL);
|
||||
|
||||
test_cmd_set_dirty_tracking(hwpt_id, true);
|
||||
|
||||
test_mock_dirty_bitmaps(hwpt_id, variant->buffer_size,
|
||||
MOCK_APERTURE_START, self->page_size,
|
||||
MOCK_APERTURE_START, self->page_size, page_size,
|
||||
self->bitmap, self->bitmap_size, 0, _metadata);
|
||||
|
||||
/* PAGE_SIZE unaligned bitmap */
|
||||
test_mock_dirty_bitmaps(hwpt_id, variant->buffer_size,
|
||||
MOCK_APERTURE_START, self->page_size,
|
||||
MOCK_APERTURE_START, self->page_size, page_size,
|
||||
self->bitmap + MOCK_PAGE_SIZE,
|
||||
self->bitmap_size, 0, _metadata);
|
||||
|
||||
test_ioctl_destroy(stddev_id);
|
||||
/* u64 unaligned bitmap */
|
||||
test_mock_dirty_bitmaps(hwpt_id, variant->buffer_size,
|
||||
MOCK_APERTURE_START, self->page_size, page_size,
|
||||
self->bitmap + 0xff1, self->bitmap_size, 0,
|
||||
_metadata);
|
||||
|
||||
test_ioctl_destroy(hwpt_id);
|
||||
}
|
||||
|
||||
TEST_F(iommufd_dirty_tracking, get_dirty_bitmap_no_clear)
|
||||
{
|
||||
uint32_t stddev_id;
|
||||
uint32_t page_size = MOCK_PAGE_SIZE;
|
||||
uint32_t hwpt_id;
|
||||
uint32_t ioas_id;
|
||||
|
||||
if (variant->hugepages)
|
||||
page_size = MOCK_HUGE_PAGE_SIZE;
|
||||
|
||||
test_ioctl_ioas_alloc(&ioas_id);
|
||||
test_ioctl_ioas_map_fixed_id(ioas_id, self->buffer,
|
||||
variant->buffer_size, MOCK_APERTURE_START);
|
||||
|
||||
test_cmd_hwpt_alloc(self->idev_id, ioas_id,
|
||||
IOMMU_HWPT_ALLOC_DIRTY_TRACKING, &hwpt_id);
|
||||
test_cmd_mock_domain(hwpt_id, &stddev_id, NULL, NULL);
|
||||
|
||||
test_cmd_set_dirty_tracking(hwpt_id, true);
|
||||
|
||||
test_mock_dirty_bitmaps(hwpt_id, variant->buffer_size,
|
||||
MOCK_APERTURE_START, self->page_size,
|
||||
MOCK_APERTURE_START, self->page_size, page_size,
|
||||
self->bitmap, self->bitmap_size,
|
||||
IOMMU_HWPT_GET_DIRTY_BITMAP_NO_CLEAR,
|
||||
_metadata);
|
||||
|
||||
/* Unaligned bitmap */
|
||||
test_mock_dirty_bitmaps(hwpt_id, variant->buffer_size,
|
||||
MOCK_APERTURE_START, self->page_size,
|
||||
MOCK_APERTURE_START, self->page_size, page_size,
|
||||
self->bitmap + MOCK_PAGE_SIZE,
|
||||
self->bitmap_size,
|
||||
IOMMU_HWPT_GET_DIRTY_BITMAP_NO_CLEAR,
|
||||
_metadata);
|
||||
|
||||
test_ioctl_destroy(stddev_id);
|
||||
/* u64 unaligned bitmap */
|
||||
test_mock_dirty_bitmaps(hwpt_id, variant->buffer_size,
|
||||
MOCK_APERTURE_START, self->page_size, page_size,
|
||||
self->bitmap + 0xff1, self->bitmap_size,
|
||||
IOMMU_HWPT_GET_DIRTY_BITMAP_NO_CLEAR,
|
||||
_metadata);
|
||||
|
||||
test_ioctl_destroy(hwpt_id);
|
||||
}
|
||||
|
||||
|
@ -344,16 +344,19 @@ static int _test_cmd_mock_domain_set_dirty(int fd, __u32 hwpt_id, size_t length,
|
||||
page_size, bitmap, nr))
|
||||
|
||||
static int _test_mock_dirty_bitmaps(int fd, __u32 hwpt_id, size_t length,
|
||||
__u64 iova, size_t page_size, __u64 *bitmap,
|
||||
__u64 iova, size_t page_size,
|
||||
size_t pte_page_size, __u64 *bitmap,
|
||||
__u64 bitmap_size, __u32 flags,
|
||||
struct __test_metadata *_metadata)
|
||||
{
|
||||
unsigned long i, nbits = bitmap_size * BITS_PER_BYTE;
|
||||
unsigned long nr = nbits / 2;
|
||||
unsigned long npte = pte_page_size / page_size, pteset = 2 * npte;
|
||||
unsigned long nbits = bitmap_size * BITS_PER_BYTE;
|
||||
unsigned long j, i, nr = nbits / pteset ?: 1;
|
||||
__u64 out_dirty = 0;
|
||||
|
||||
/* Mark all even bits as dirty in the mock domain */
|
||||
for (i = 0; i < nbits; i += 2)
|
||||
memset(bitmap, 0, bitmap_size);
|
||||
for (i = 0; i < nbits; i += pteset)
|
||||
set_bit(i, (unsigned long *)bitmap);
|
||||
|
||||
test_cmd_mock_domain_set_dirty(fd, hwpt_id, length, iova, page_size,
|
||||
@ -365,8 +368,12 @@ static int _test_mock_dirty_bitmaps(int fd, __u32 hwpt_id, size_t length,
|
||||
test_cmd_get_dirty_bitmap(fd, hwpt_id, length, iova, page_size, bitmap,
|
||||
flags);
|
||||
/* Beware ASSERT_EQ() is two statements -- braces are not redundant! */
|
||||
for (i = 0; i < nbits; i++) {
|
||||
ASSERT_EQ(!(i % 2), test_bit(i, (unsigned long *)bitmap));
|
||||
for (i = 0; i < nbits; i += pteset) {
|
||||
for (j = 0; j < pteset; j++) {
|
||||
ASSERT_EQ(j < npte,
|
||||
test_bit(i + j, (unsigned long *)bitmap));
|
||||
}
|
||||
ASSERT_EQ(!(i % pteset), test_bit(i, (unsigned long *)bitmap));
|
||||
}
|
||||
|
||||
memset(bitmap, 0, bitmap_size);
|
||||
@ -374,19 +381,23 @@ static int _test_mock_dirty_bitmaps(int fd, __u32 hwpt_id, size_t length,
|
||||
flags);
|
||||
|
||||
/* It as read already -- expect all zeroes */
|
||||
for (i = 0; i < nbits; i++) {
|
||||
ASSERT_EQ(!(i % 2) && (flags &
|
||||
IOMMU_HWPT_GET_DIRTY_BITMAP_NO_CLEAR),
|
||||
test_bit(i, (unsigned long *)bitmap));
|
||||
for (i = 0; i < nbits; i += pteset) {
|
||||
for (j = 0; j < pteset; j++) {
|
||||
ASSERT_EQ(
|
||||
(j < npte) &&
|
||||
(flags &
|
||||
IOMMU_HWPT_GET_DIRTY_BITMAP_NO_CLEAR),
|
||||
test_bit(i + j, (unsigned long *)bitmap));
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#define test_mock_dirty_bitmaps(hwpt_id, length, iova, page_size, bitmap, \
|
||||
bitmap_size, flags, _metadata) \
|
||||
#define test_mock_dirty_bitmaps(hwpt_id, length, iova, page_size, pte_size,\
|
||||
bitmap, bitmap_size, flags, _metadata) \
|
||||
ASSERT_EQ(0, _test_mock_dirty_bitmaps(self->fd, hwpt_id, length, iova, \
|
||||
page_size, bitmap, bitmap_size, \
|
||||
flags, _metadata))
|
||||
page_size, pte_size, bitmap, \
|
||||
bitmap_size, flags, _metadata))
|
||||
|
||||
static int _test_cmd_create_access(int fd, unsigned int ioas_id,
|
||||
__u32 *access_id, unsigned int flags)
|
||||
|
Loading…
Reference in New Issue
Block a user