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:
Linus Torvalds 2024-02-22 11:53:09 -08:00
commit 4c36fbb46f
7 changed files with 210 additions and 63 deletions

View File

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

View File

@ -45,6 +45,7 @@ enum {
enum {
MOCK_FLAGS_DEVICE_NO_DIRTY = 1 << 0,
MOCK_FLAGS_DEVICE_HUGE_IOVA = 1 << 1,
};
enum {

View File

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

View File

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

View File

@ -1,2 +1,3 @@
CONFIG_IOMMUFD
CONFIG_IOMMUFD_TEST
CONFIG_IOMMUFD=y
CONFIG_FAULT_INJECTION=y
CONFIG_IOMMUFD_TEST=y

View File

@ -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);
}

View File

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