Skip to content

Commit 9e1d54c

Browse files
committed
Merge: [RHEL-9.5 RHEL-40162] Fix for iommufd dirty tracking
MR: https://gitlab.com/redhat/centos-stream/src/kernel/centos-stream-9/-/merge_requests/4920 # Merge Request Required Information JIRA: https://issues.redhat.com/browse/RHEL-40162 Upstream Status: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git Tested: iommufd kselftest run on x86, aarch64, and aarch64 64k page ## Summary of Changes An issue was noticed when running iommufd selftest on a aarch64 kernel with 64k pages. It exposed a bug where it was assumed a bitmap would be at least a byte in size, causing the 128k iova length test to fail. Since the fix also involved changes to the iommufd iova bitmap code, I'm pulling this in for 9.5. Signed-off-by: Jerry Snitselaar <jsnitsel@redhat.com> ## Approved Development Ticket All submissions to CentOS Stream must reference an approved ticket in [Red Hat Jira](https://issues.redhat.com/). Please follow the CentOS Stream [contribution documentation](https://docs.centos.org/en-US/stream-contrib/quickstart/) for how to file this ticket and have it approved. Approved-by: Donald Dutile <ddutile@redhat.com> Approved-by: Phil Auld <pauld@redhat.com> Approved-by: Eder Zulian <ezulian@redhat.com> Approved-by: CKI KWF Bot <cki-ci-bot+kwf-gitlab-com@redhat.com> Merged-by: Lucas Zampieri <lzampier@redhat.com>
2 parents 52396ac + e1722f1 commit 9e1d54c

File tree

4 files changed

+97
-103
lines changed

4 files changed

+97
-103
lines changed

drivers/iommu/iommufd/iova_bitmap.c

Lines changed: 49 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ struct iova_bitmap_map {
3535
/* base IOVA representing bit 0 of the first page */
3636
unsigned long iova;
3737

38+
/* mapped length */
39+
unsigned long length;
40+
3841
/* page size order that each bit granules to */
3942
unsigned long pgshift;
4043

@@ -113,9 +116,6 @@ struct iova_bitmap {
113116

114117
/* length of the IOVA range for the whole bitmap */
115118
size_t length;
116-
117-
/* length of the IOVA range set ahead the pinned pages */
118-
unsigned long set_ahead_length;
119119
};
120120

121121
/*
@@ -156,6 +156,8 @@ static unsigned long iova_bitmap_mapped_iova(struct iova_bitmap *bitmap)
156156
return bitmap->iova + iova_bitmap_index_to_offset(bitmap, skip);
157157
}
158158

159+
static unsigned long iova_bitmap_mapped_length(struct iova_bitmap *bitmap);
160+
159161
/*
160162
* Pins the bitmap user pages for the current range window.
161163
* This is internal to IOVA bitmap and called when advancing the
@@ -206,6 +208,7 @@ static int iova_bitmap_get(struct iova_bitmap *bitmap)
206208
* aligned.
207209
*/
208210
mapped->pgoff = offset_in_page(addr);
211+
mapped->length = iova_bitmap_mapped_length(bitmap);
209212
return 0;
210213
}
211214

@@ -263,9 +266,6 @@ struct iova_bitmap *iova_bitmap_alloc(unsigned long iova, size_t length,
263266
goto err;
264267
}
265268

266-
rc = iova_bitmap_get(bitmap);
267-
if (rc)
268-
goto err;
269269
return bitmap;
270270

271271
err:
@@ -338,65 +338,34 @@ static unsigned long iova_bitmap_mapped_length(struct iova_bitmap *bitmap)
338338
}
339339

340340
/*
341-
* Returns true if there's not more data to iterate.
341+
* Returns true if [@iova..@iova+@length-1] is part of the mapped IOVA range.
342342
*/
343-
static bool iova_bitmap_done(struct iova_bitmap *bitmap)
343+
static bool iova_bitmap_mapped_range(struct iova_bitmap_map *mapped,
344+
unsigned long iova, size_t length)
344345
{
345-
return bitmap->mapped_base_index >= bitmap->mapped_total_index;
346-
}
347-
348-
static int iova_bitmap_set_ahead(struct iova_bitmap *bitmap,
349-
size_t set_ahead_length)
350-
{
351-
int ret = 0;
352-
353-
while (set_ahead_length > 0 && !iova_bitmap_done(bitmap)) {
354-
unsigned long length = iova_bitmap_mapped_length(bitmap);
355-
unsigned long iova = iova_bitmap_mapped_iova(bitmap);
356-
357-
ret = iova_bitmap_get(bitmap);
358-
if (ret)
359-
break;
360-
361-
length = min(length, set_ahead_length);
362-
iova_bitmap_set(bitmap, iova, length);
363-
364-
set_ahead_length -= length;
365-
bitmap->mapped_base_index +=
366-
iova_bitmap_offset_to_index(bitmap, length - 1) + 1;
367-
iova_bitmap_put(bitmap);
368-
}
369-
370-
bitmap->set_ahead_length = 0;
371-
return ret;
346+
return mapped->npages &&
347+
(iova >= mapped->iova &&
348+
(iova + length - 1) <= (mapped->iova + mapped->length - 1));
372349
}
373350

374351
/*
375-
* Advances to the next range, releases the current pinned
352+
* Advances to a selected range, releases the current pinned
376353
* pages and pins the next set of bitmap pages.
377354
* Returns 0 on success or otherwise errno.
378355
*/
379-
static int iova_bitmap_advance(struct iova_bitmap *bitmap)
356+
static int iova_bitmap_advance_to(struct iova_bitmap *bitmap,
357+
unsigned long iova)
380358
{
381-
unsigned long iova = iova_bitmap_mapped_length(bitmap) - 1;
382-
unsigned long count = iova_bitmap_offset_to_index(bitmap, iova) + 1;
359+
unsigned long index;
383360

384-
bitmap->mapped_base_index += count;
361+
index = iova_bitmap_offset_to_index(bitmap, iova - bitmap->iova);
362+
if (index >= bitmap->mapped_total_index)
363+
return -EINVAL;
364+
bitmap->mapped_base_index = index;
385365

386366
iova_bitmap_put(bitmap);
387-
if (iova_bitmap_done(bitmap))
388-
return 0;
389-
390-
/* Iterate, set and skip any bits requested for next iteration */
391-
if (bitmap->set_ahead_length) {
392-
int ret;
393-
394-
ret = iova_bitmap_set_ahead(bitmap, bitmap->set_ahead_length);
395-
if (ret)
396-
return ret;
397-
}
398367

399-
/* When advancing the index we pin the next set of bitmap pages */
368+
/* Pin the next set of bitmap pages */
400369
return iova_bitmap_get(bitmap);
401370
}
402371

@@ -416,17 +385,7 @@ static int iova_bitmap_advance(struct iova_bitmap *bitmap)
416385
int iova_bitmap_for_each(struct iova_bitmap *bitmap, void *opaque,
417386
iova_bitmap_fn_t fn)
418387
{
419-
int ret = 0;
420-
421-
for (; !iova_bitmap_done(bitmap) && !ret;
422-
ret = iova_bitmap_advance(bitmap)) {
423-
ret = fn(bitmap, iova_bitmap_mapped_iova(bitmap),
424-
iova_bitmap_mapped_length(bitmap), opaque);
425-
if (ret)
426-
break;
427-
}
428-
429-
return ret;
388+
return fn(bitmap, bitmap->iova, bitmap->length, opaque);
430389
}
431390
EXPORT_SYMBOL_NS_GPL(iova_bitmap_for_each, IOMMUFD);
432391

@@ -444,11 +403,25 @@ void iova_bitmap_set(struct iova_bitmap *bitmap,
444403
unsigned long iova, size_t length)
445404
{
446405
struct iova_bitmap_map *mapped = &bitmap->mapped;
447-
unsigned long cur_bit = ((iova - mapped->iova) >>
448-
mapped->pgshift) + mapped->pgoff * BITS_PER_BYTE;
449-
unsigned long last_bit = (((iova + length - 1) - mapped->iova) >>
450-
mapped->pgshift) + mapped->pgoff * BITS_PER_BYTE;
451-
unsigned long last_page_idx = mapped->npages - 1;
406+
unsigned long cur_bit, last_bit, last_page_idx;
407+
408+
update_indexes:
409+
if (unlikely(!iova_bitmap_mapped_range(mapped, iova, length))) {
410+
411+
/*
412+
* The attempt to advance the base index to @iova
413+
* may fail if it's out of bounds, or pinning the pages
414+
* returns an error.
415+
*/
416+
if (iova_bitmap_advance_to(bitmap, iova))
417+
return;
418+
}
419+
420+
last_page_idx = mapped->npages - 1;
421+
cur_bit = ((iova - mapped->iova) >>
422+
mapped->pgshift) + mapped->pgoff * BITS_PER_BYTE;
423+
last_bit = (((iova + length - 1) - mapped->iova) >>
424+
mapped->pgshift) + mapped->pgoff * BITS_PER_BYTE;
452425

453426
do {
454427
unsigned int page_idx = cur_bit / BITS_PER_PAGE;
@@ -457,18 +430,19 @@ void iova_bitmap_set(struct iova_bitmap *bitmap,
457430
last_bit - cur_bit + 1);
458431
void *kaddr;
459432

460-
if (unlikely(page_idx > last_page_idx))
461-
break;
433+
if (unlikely(page_idx > last_page_idx)) {
434+
unsigned long left =
435+
((last_bit - cur_bit + 1) << mapped->pgshift);
436+
437+
iova += (length - left);
438+
length = left;
439+
goto update_indexes;
440+
}
462441

463442
kaddr = kmap_local_page(mapped->pages[page_idx]);
464443
bitmap_set(kaddr, offset, nbits);
465444
kunmap_local(kaddr);
466445
cur_bit += nbits;
467446
} while (cur_bit <= last_bit);
468-
469-
if (unlikely(cur_bit <= last_bit)) {
470-
bitmap->set_ahead_length =
471-
((last_bit - cur_bit + 1) << bitmap->mapped.pgshift);
472-
}
473447
}
474448
EXPORT_SYMBOL_NS_GPL(iova_bitmap_set, IOMMUFD);

drivers/iommu/iommufd/selftest.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -266,8 +266,8 @@ static int mock_domain_read_and_clear_dirty(struct iommu_domain *domain,
266266

267267
/* Clear dirty */
268268
if (mock_test_and_clear_dirty(mock, head, pgsize, flags))
269-
iommu_dirty_bitmap_record(dirty, head, pgsize);
270-
iova = head + pgsize;
269+
iommu_dirty_bitmap_record(dirty, iova, pgsize);
270+
iova += pgsize;
271271
} while (iova < end);
272272

273273
return 0;
@@ -1334,7 +1334,7 @@ static int iommufd_test_dirty(struct iommufd_ucmd *ucmd, unsigned int mockpt_id,
13341334
}
13351335

13361336
max = length / page_size;
1337-
bitmap_size = max / BITS_PER_BYTE;
1337+
bitmap_size = DIV_ROUND_UP(max, BITS_PER_BYTE);
13381338

13391339
tmp = kvzalloc(bitmap_size, GFP_KERNEL_ACCOUNT);
13401340
if (!tmp) {

tools/testing/selftests/iommu/iommufd.c

Lines changed: 41 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1722,10 +1722,17 @@ FIXTURE_VARIANT(iommufd_dirty_tracking)
17221722

17231723
FIXTURE_SETUP(iommufd_dirty_tracking)
17241724
{
1725+
unsigned long size;
17251726
int mmap_flags;
17261727
void *vrc;
17271728
int rc;
17281729

1730+
if (variant->buffer_size < MOCK_PAGE_SIZE) {
1731+
SKIP(return,
1732+
"Skipping buffer_size=%lu, less than MOCK_PAGE_SIZE=%lu",
1733+
variant->buffer_size, MOCK_PAGE_SIZE);
1734+
}
1735+
17291736
self->fd = open("/dev/iommu", O_RDWR);
17301737
ASSERT_NE(-1, self->fd);
17311738

@@ -1749,12 +1756,11 @@ FIXTURE_SETUP(iommufd_dirty_tracking)
17491756
assert(vrc == self->buffer);
17501757

17511758
self->page_size = MOCK_PAGE_SIZE;
1752-
self->bitmap_size =
1753-
variant->buffer_size / self->page_size / BITS_PER_BYTE;
1759+
self->bitmap_size = variant->buffer_size / self->page_size;
17541760

17551761
/* Provision with an extra (PAGE_SIZE) for the unaligned case */
1756-
rc = posix_memalign(&self->bitmap, PAGE_SIZE,
1757-
self->bitmap_size + PAGE_SIZE);
1762+
size = DIV_ROUND_UP(self->bitmap_size, BITS_PER_BYTE);
1763+
rc = posix_memalign(&self->bitmap, PAGE_SIZE, size + PAGE_SIZE);
17581764
assert(!rc);
17591765
assert(self->bitmap);
17601766
assert((uintptr_t)self->bitmap % PAGE_SIZE == 0);
@@ -1775,51 +1781,63 @@ FIXTURE_SETUP(iommufd_dirty_tracking)
17751781
FIXTURE_TEARDOWN(iommufd_dirty_tracking)
17761782
{
17771783
munmap(self->buffer, variant->buffer_size);
1778-
munmap(self->bitmap, self->bitmap_size);
1784+
munmap(self->bitmap, DIV_ROUND_UP(self->bitmap_size, BITS_PER_BYTE));
17791785
teardown_iommufd(self->fd, _metadata);
17801786
}
17811787

1782-
FIXTURE_VARIANT_ADD(iommufd_dirty_tracking, domain_dirty128k)
1788+
FIXTURE_VARIANT_ADD(iommufd_dirty_tracking, domain_dirty8k)
1789+
{
1790+
/* half of an u8 index bitmap */
1791+
.buffer_size = 8UL * 1024UL,
1792+
};
1793+
1794+
FIXTURE_VARIANT_ADD(iommufd_dirty_tracking, domain_dirty16k)
1795+
{
1796+
/* one u8 index bitmap */
1797+
.buffer_size = 16UL * 1024UL,
1798+
};
1799+
1800+
FIXTURE_VARIANT_ADD(iommufd_dirty_tracking, domain_dirty64k)
17831801
{
17841802
/* one u32 index bitmap */
1785-
.buffer_size = 128UL * 1024UL,
1803+
.buffer_size = 64UL * 1024UL,
17861804
};
17871805

1788-
FIXTURE_VARIANT_ADD(iommufd_dirty_tracking, domain_dirty256k)
1806+
FIXTURE_VARIANT_ADD(iommufd_dirty_tracking, domain_dirty128k)
17891807
{
17901808
/* one u64 index bitmap */
1791-
.buffer_size = 256UL * 1024UL,
1809+
.buffer_size = 128UL * 1024UL,
17921810
};
17931811

1794-
FIXTURE_VARIANT_ADD(iommufd_dirty_tracking, domain_dirty640k)
1812+
FIXTURE_VARIANT_ADD(iommufd_dirty_tracking, domain_dirty320k)
17951813
{
17961814
/* two u64 index and trailing end bitmap */
1797-
.buffer_size = 640UL * 1024UL,
1815+
.buffer_size = 320UL * 1024UL,
17981816
};
17991817

1800-
FIXTURE_VARIANT_ADD(iommufd_dirty_tracking, domain_dirty128M)
1818+
FIXTURE_VARIANT_ADD(iommufd_dirty_tracking, domain_dirty64M)
18011819
{
1802-
/* 4K bitmap (128M IOVA range) */
1803-
.buffer_size = 128UL * 1024UL * 1024UL,
1820+
/* 4K bitmap (64M IOVA range) */
1821+
.buffer_size = 64UL * 1024UL * 1024UL,
18041822
};
18051823

1806-
FIXTURE_VARIANT_ADD(iommufd_dirty_tracking, domain_dirty128M_huge)
1824+
FIXTURE_VARIANT_ADD(iommufd_dirty_tracking, domain_dirty64M_huge)
18071825
{
1808-
/* 4K bitmap (128M IOVA range) */
1809-
.buffer_size = 128UL * 1024UL * 1024UL,
1826+
/* 4K bitmap (64M IOVA range) */
1827+
.buffer_size = 64UL * 1024UL * 1024UL,
18101828
.hugepages = true,
18111829
};
18121830

1813-
FIXTURE_VARIANT_ADD(iommufd_dirty_tracking, domain_dirty256M)
1831+
FIXTURE_VARIANT_ADD(iommufd_dirty_tracking, domain_dirty128M)
18141832
{
1815-
/* 8K bitmap (256M IOVA range) */
1816-
.buffer_size = 256UL * 1024UL * 1024UL,
1833+
/* 8K bitmap (128M IOVA range) */
1834+
.buffer_size = 128UL * 1024UL * 1024UL,
18171835
};
18181836

1819-
FIXTURE_VARIANT_ADD(iommufd_dirty_tracking, domain_dirty256M_huge)
1837+
FIXTURE_VARIANT_ADD(iommufd_dirty_tracking, domain_dirty128M_huge)
18201838
{
1821-
/* 8K bitmap (256M IOVA range) */
1822-
.buffer_size = 256UL * 1024UL * 1024UL,
1839+
/* 8K bitmap (128M IOVA range) */
1840+
.buffer_size = 128UL * 1024UL * 1024UL,
18231841
.hugepages = true,
18241842
};
18251843

tools/testing/selftests/iommu/iommufd_utils.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
#define BIT_MASK(nr) (1UL << ((nr) % __BITS_PER_LONG))
2323
#define BIT_WORD(nr) ((nr) / __BITS_PER_LONG)
2424

25+
#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
26+
2527
static inline void set_bit(unsigned int nr, unsigned long *addr)
2628
{
2729
unsigned long mask = BIT_MASK(nr);
@@ -346,12 +348,12 @@ static int _test_cmd_mock_domain_set_dirty(int fd, __u32 hwpt_id, size_t length,
346348
static int _test_mock_dirty_bitmaps(int fd, __u32 hwpt_id, size_t length,
347349
__u64 iova, size_t page_size,
348350
size_t pte_page_size, __u64 *bitmap,
349-
__u64 bitmap_size, __u32 flags,
351+
__u64 nbits, __u32 flags,
350352
struct __test_metadata *_metadata)
351353
{
352354
unsigned long npte = pte_page_size / page_size, pteset = 2 * npte;
353-
unsigned long nbits = bitmap_size * BITS_PER_BYTE;
354355
unsigned long j, i, nr = nbits / pteset ?: 1;
356+
unsigned long bitmap_size = DIV_ROUND_UP(nbits, BITS_PER_BYTE);
355357
__u64 out_dirty = 0;
356358

357359
/* Mark all even bits as dirty in the mock domain */

0 commit comments

Comments
 (0)