Skip to content

Commit 4c36fbb

Browse files
committed
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
2 parents c7138f7 + 510325e commit 4c36fbb

File tree

7 files changed

+210
-63
lines changed

7 files changed

+210
-63
lines changed

drivers/iommu/iommufd/hw_pagetable.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,8 @@ int iommufd_hwpt_alloc(struct iommufd_ucmd *ucmd)
263263

264264
if (cmd->__reserved)
265265
return -EOPNOTSUPP;
266-
if (cmd->data_type == IOMMU_HWPT_DATA_NONE && cmd->data_len)
266+
if ((cmd->data_type == IOMMU_HWPT_DATA_NONE && cmd->data_len) ||
267+
(cmd->data_type != IOMMU_HWPT_DATA_NONE && !cmd->data_len))
267268
return -EINVAL;
268269

269270
idev = iommufd_get_device(ucmd, cmd->dev_id);

drivers/iommu/iommufd/iommufd_test.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ enum {
4545

4646
enum {
4747
MOCK_FLAGS_DEVICE_NO_DIRTY = 1 << 0,
48+
MOCK_FLAGS_DEVICE_HUGE_IOVA = 1 << 1,
4849
};
4950

5051
enum {

drivers/iommu/iommufd/iova_bitmap.c

Lines changed: 58 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ struct iova_bitmap {
100100
struct iova_bitmap_map mapped;
101101

102102
/* userspace address of the bitmap */
103-
u64 __user *bitmap;
103+
u8 __user *bitmap;
104104

105105
/* u64 index that @mapped points to */
106106
unsigned long mapped_base_index;
@@ -113,6 +113,9 @@ struct iova_bitmap {
113113

114114
/* length of the IOVA range for the whole bitmap */
115115
size_t length;
116+
117+
/* length of the IOVA range set ahead the pinned pages */
118+
unsigned long set_ahead_length;
116119
};
117120

118121
/*
@@ -162,7 +165,7 @@ static int iova_bitmap_get(struct iova_bitmap *bitmap)
162165
{
163166
struct iova_bitmap_map *mapped = &bitmap->mapped;
164167
unsigned long npages;
165-
u64 __user *addr;
168+
u8 __user *addr;
166169
long ret;
167170

168171
/*
@@ -175,18 +178,19 @@ static int iova_bitmap_get(struct iova_bitmap *bitmap)
175178
bitmap->mapped_base_index) *
176179
sizeof(*bitmap->bitmap), PAGE_SIZE);
177180

178-
/*
179-
* We always cap at max number of 'struct page' a base page can fit.
180-
* This is, for example, on x86 means 2M of bitmap data max.
181-
*/
182-
npages = min(npages, PAGE_SIZE / sizeof(struct page *));
183-
184181
/*
185182
* Bitmap address to be pinned is calculated via pointer arithmetic
186183
* with bitmap u64 word index.
187184
*/
188185
addr = bitmap->bitmap + bitmap->mapped_base_index;
189186

187+
/*
188+
* We always cap at max number of 'struct page' a base page can fit.
189+
* This is, for example, on x86 means 2M of bitmap data max.
190+
*/
191+
npages = min(npages + !!offset_in_page(addr),
192+
PAGE_SIZE / sizeof(struct page *));
193+
190194
ret = pin_user_pages_fast((unsigned long)addr, npages,
191195
FOLL_WRITE, mapped->pages);
192196
if (ret <= 0)
@@ -247,7 +251,7 @@ struct iova_bitmap *iova_bitmap_alloc(unsigned long iova, size_t length,
247251

248252
mapped = &bitmap->mapped;
249253
mapped->pgshift = __ffs(page_size);
250-
bitmap->bitmap = data;
254+
bitmap->bitmap = (u8 __user *)data;
251255
bitmap->mapped_total_index =
252256
iova_bitmap_offset_to_index(bitmap, length - 1) + 1;
253257
bitmap->iova = iova;
@@ -304,7 +308,7 @@ static unsigned long iova_bitmap_mapped_remaining(struct iova_bitmap *bitmap)
304308

305309
remaining = bitmap->mapped_total_index - bitmap->mapped_base_index;
306310
remaining = min_t(unsigned long, remaining,
307-
bytes / sizeof(*bitmap->bitmap));
311+
DIV_ROUND_UP(bytes, sizeof(*bitmap->bitmap)));
308312

309313
return remaining;
310314
}
@@ -341,6 +345,32 @@ static bool iova_bitmap_done(struct iova_bitmap *bitmap)
341345
return bitmap->mapped_base_index >= bitmap->mapped_total_index;
342346
}
343347

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;
372+
}
373+
344374
/*
345375
* Advances to the next range, releases the current pinned
346376
* pages and pins the next set of bitmap pages.
@@ -357,6 +387,15 @@ static int iova_bitmap_advance(struct iova_bitmap *bitmap)
357387
if (iova_bitmap_done(bitmap))
358388
return 0;
359389

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+
}
398+
360399
/* When advancing the index we pin the next set of bitmap pages */
361400
return iova_bitmap_get(bitmap);
362401
}
@@ -409,6 +448,7 @@ void iova_bitmap_set(struct iova_bitmap *bitmap,
409448
mapped->pgshift) + mapped->pgoff * BITS_PER_BYTE;
410449
unsigned long last_bit = (((iova + length - 1) - mapped->iova) >>
411450
mapped->pgshift) + mapped->pgoff * BITS_PER_BYTE;
451+
unsigned long last_page_idx = mapped->npages - 1;
412452

413453
do {
414454
unsigned int page_idx = cur_bit / BITS_PER_PAGE;
@@ -417,10 +457,18 @@ void iova_bitmap_set(struct iova_bitmap *bitmap,
417457
last_bit - cur_bit + 1);
418458
void *kaddr;
419459

460+
if (unlikely(page_idx > last_page_idx))
461+
break;
462+
420463
kaddr = kmap_local_page(mapped->pages[page_idx]);
421464
bitmap_set(kaddr, offset, nbits);
422465
kunmap_local(kaddr);
423466
cur_bit += nbits;
424467
} 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+
}
425473
}
426474
EXPORT_SYMBOL_NS_GPL(iova_bitmap_set, IOMMUFD);

drivers/iommu/iommufd/selftest.c

Lines changed: 58 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ static atomic_t mock_dev_num;
4141
enum {
4242
MOCK_DIRTY_TRACK = 1,
4343
MOCK_IO_PAGE_SIZE = PAGE_SIZE / 2,
44+
MOCK_HUGE_PAGE_SIZE = 512 * MOCK_IO_PAGE_SIZE,
4445

4546
/*
4647
* Like a real page table alignment requires the low bits of the address
@@ -53,6 +54,7 @@ enum {
5354
MOCK_PFN_START_IOVA = _MOCK_PFN_START,
5455
MOCK_PFN_LAST_IOVA = _MOCK_PFN_START,
5556
MOCK_PFN_DIRTY_IOVA = _MOCK_PFN_START << 1,
57+
MOCK_PFN_HUGE_IOVA = _MOCK_PFN_START << 2,
5658
};
5759

5860
/*
@@ -191,38 +193,66 @@ static int mock_domain_set_dirty_tracking(struct iommu_domain *domain,
191193
return 0;
192194
}
193195

196+
static bool mock_test_and_clear_dirty(struct mock_iommu_domain *mock,
197+
unsigned long iova, size_t page_size,
198+
unsigned long flags)
199+
{
200+
unsigned long cur, end = iova + page_size - 1;
201+
bool dirty = false;
202+
void *ent, *old;
203+
204+
for (cur = iova; cur < end; cur += MOCK_IO_PAGE_SIZE) {
205+
ent = xa_load(&mock->pfns, cur / MOCK_IO_PAGE_SIZE);
206+
if (!ent || !(xa_to_value(ent) & MOCK_PFN_DIRTY_IOVA))
207+
continue;
208+
209+
dirty = true;
210+
/* Clear dirty */
211+
if (!(flags & IOMMU_DIRTY_NO_CLEAR)) {
212+
unsigned long val;
213+
214+
val = xa_to_value(ent) & ~MOCK_PFN_DIRTY_IOVA;
215+
old = xa_store(&mock->pfns, cur / MOCK_IO_PAGE_SIZE,
216+
xa_mk_value(val), GFP_KERNEL);
217+
WARN_ON_ONCE(ent != old);
218+
}
219+
}
220+
221+
return dirty;
222+
}
223+
194224
static int mock_domain_read_and_clear_dirty(struct iommu_domain *domain,
195225
unsigned long iova, size_t size,
196226
unsigned long flags,
197227
struct iommu_dirty_bitmap *dirty)
198228
{
199229
struct mock_iommu_domain *mock =
200230
container_of(domain, struct mock_iommu_domain, domain);
201-
unsigned long i, max = size / MOCK_IO_PAGE_SIZE;
202-
void *ent, *old;
231+
unsigned long end = iova + size;
232+
void *ent;
203233

204234
if (!(mock->flags & MOCK_DIRTY_TRACK) && dirty->bitmap)
205235
return -EINVAL;
206236

207-
for (i = 0; i < max; i++) {
208-
unsigned long cur = iova + i * MOCK_IO_PAGE_SIZE;
237+
do {
238+
unsigned long pgsize = MOCK_IO_PAGE_SIZE;
239+
unsigned long head;
209240

210-
ent = xa_load(&mock->pfns, cur / MOCK_IO_PAGE_SIZE);
211-
if (ent && (xa_to_value(ent) & MOCK_PFN_DIRTY_IOVA)) {
212-
/* Clear dirty */
213-
if (!(flags & IOMMU_DIRTY_NO_CLEAR)) {
214-
unsigned long val;
215-
216-
val = xa_to_value(ent) & ~MOCK_PFN_DIRTY_IOVA;
217-
old = xa_store(&mock->pfns,
218-
cur / MOCK_IO_PAGE_SIZE,
219-
xa_mk_value(val), GFP_KERNEL);
220-
WARN_ON_ONCE(ent != old);
221-
}
222-
iommu_dirty_bitmap_record(dirty, cur,
223-
MOCK_IO_PAGE_SIZE);
241+
ent = xa_load(&mock->pfns, iova / MOCK_IO_PAGE_SIZE);
242+
if (!ent) {
243+
iova += pgsize;
244+
continue;
224245
}
225-
}
246+
247+
if (xa_to_value(ent) & MOCK_PFN_HUGE_IOVA)
248+
pgsize = MOCK_HUGE_PAGE_SIZE;
249+
head = iova & ~(pgsize - 1);
250+
251+
/* Clear dirty */
252+
if (mock_test_and_clear_dirty(mock, head, pgsize, flags))
253+
iommu_dirty_bitmap_record(dirty, head, pgsize);
254+
iova = head + pgsize;
255+
} while (iova < end);
226256

227257
return 0;
228258
}
@@ -234,6 +264,7 @@ const struct iommu_dirty_ops dirty_ops = {
234264

235265
static struct iommu_domain *mock_domain_alloc_paging(struct device *dev)
236266
{
267+
struct mock_dev *mdev = container_of(dev, struct mock_dev, dev);
237268
struct mock_iommu_domain *mock;
238269

239270
mock = kzalloc(sizeof(*mock), GFP_KERNEL);
@@ -242,6 +273,8 @@ static struct iommu_domain *mock_domain_alloc_paging(struct device *dev)
242273
mock->domain.geometry.aperture_start = MOCK_APERTURE_START;
243274
mock->domain.geometry.aperture_end = MOCK_APERTURE_LAST;
244275
mock->domain.pgsize_bitmap = MOCK_IO_PAGE_SIZE;
276+
if (dev && mdev->flags & MOCK_FLAGS_DEVICE_HUGE_IOVA)
277+
mock->domain.pgsize_bitmap |= MOCK_HUGE_PAGE_SIZE;
245278
mock->domain.ops = mock_ops.default_domain_ops;
246279
mock->domain.type = IOMMU_DOMAIN_UNMANAGED;
247280
xa_init(&mock->pfns);
@@ -287,7 +320,7 @@ mock_domain_alloc_user(struct device *dev, u32 flags,
287320
return ERR_PTR(-EOPNOTSUPP);
288321
if (user_data || (has_dirty_flag && no_dirty_ops))
289322
return ERR_PTR(-EOPNOTSUPP);
290-
domain = mock_domain_alloc_paging(NULL);
323+
domain = mock_domain_alloc_paging(dev);
291324
if (!domain)
292325
return ERR_PTR(-ENOMEM);
293326
if (has_dirty_flag)
@@ -350,6 +383,9 @@ static int mock_domain_map_pages(struct iommu_domain *domain,
350383

351384
if (pgcount == 1 && cur + MOCK_IO_PAGE_SIZE == pgsize)
352385
flags = MOCK_PFN_LAST_IOVA;
386+
if (pgsize != MOCK_IO_PAGE_SIZE) {
387+
flags |= MOCK_PFN_HUGE_IOVA;
388+
}
353389
old = xa_store(&mock->pfns, iova / MOCK_IO_PAGE_SIZE,
354390
xa_mk_value((paddr / MOCK_IO_PAGE_SIZE) |
355391
flags),
@@ -604,7 +640,8 @@ static struct mock_dev *mock_dev_create(unsigned long dev_flags)
604640
struct mock_dev *mdev;
605641
int rc;
606642

607-
if (dev_flags & ~(MOCK_FLAGS_DEVICE_NO_DIRTY))
643+
if (dev_flags &
644+
~(MOCK_FLAGS_DEVICE_NO_DIRTY | MOCK_FLAGS_DEVICE_HUGE_IOVA))
608645
return ERR_PTR(-EINVAL);
609646

610647
mdev = kzalloc(sizeof(*mdev), GFP_KERNEL);

tools/testing/selftests/iommu/config

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
1-
CONFIG_IOMMUFD
2-
CONFIG_IOMMUFD_TEST
1+
CONFIG_IOMMUFD=y
2+
CONFIG_FAULT_INJECTION=y
3+
CONFIG_IOMMUFD_TEST=y

0 commit comments

Comments
 (0)