Skip to content

Commit 86c4d58

Browse files
committed
Merge tag 'for-linus-iommufd' of git://git.kernel.org/pub/scm/linux/kernel/git/jgg/iommufd
Pull iommufd updates from Jason Gunthorpe: "This brings the first of three planned user IO page table invalidation operations: - IOMMU_HWPT_INVALIDATE allows invalidating the IOTLB integrated into the iommu itself. The Intel implementation will also generate an ATC invalidation to flush the device IOTLB as it unambiguously knows the device, but other HW will not. It goes along with the prior PR to implement userspace IO page tables (aka nested translation for VMs) to allow Intel to have full functionality for simple cases. An Intel implementation of the operation is provided. Also fix a small bug in the selftest mock iommu driver probe" * tag 'for-linus-iommufd' of git://git.kernel.org/pub/scm/linux/kernel/git/jgg/iommufd: iommufd/selftest: Check the bus type during probe iommu/vt-d: Add iotlb flush for nested domain iommufd: Add data structure for Intel VT-d stage-1 cache invalidation iommufd/selftest: Add coverage for IOMMU_HWPT_INVALIDATE ioctl iommufd/selftest: Add IOMMU_TEST_OP_MD_CHECK_IOTLB test op iommufd/selftest: Add mock_domain_cache_invalidate_user support iommu: Add iommu_copy_struct_from_user_array helper iommufd: Add IOMMU_HWPT_INVALIDATE iommu: Add cache_invalidate_user op
2 parents 0dde2bf + 47f2bd2 commit 86c4d58

10 files changed

Lines changed: 619 additions & 13 deletions

File tree

drivers/iommu/intel/nested.c

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,97 @@ static void intel_nested_domain_free(struct iommu_domain *domain)
7373
kfree(to_dmar_domain(domain));
7474
}
7575

76+
static void nested_flush_dev_iotlb(struct dmar_domain *domain, u64 addr,
77+
unsigned int mask)
78+
{
79+
struct device_domain_info *info;
80+
unsigned long flags;
81+
u16 sid, qdep;
82+
83+
spin_lock_irqsave(&domain->lock, flags);
84+
list_for_each_entry(info, &domain->devices, link) {
85+
if (!info->ats_enabled)
86+
continue;
87+
sid = info->bus << 8 | info->devfn;
88+
qdep = info->ats_qdep;
89+
qi_flush_dev_iotlb(info->iommu, sid, info->pfsid,
90+
qdep, addr, mask);
91+
quirk_extra_dev_tlb_flush(info, addr, mask,
92+
IOMMU_NO_PASID, qdep);
93+
}
94+
spin_unlock_irqrestore(&domain->lock, flags);
95+
}
96+
97+
static void intel_nested_flush_cache(struct dmar_domain *domain, u64 addr,
98+
unsigned long npages, bool ih)
99+
{
100+
struct iommu_domain_info *info;
101+
unsigned int mask;
102+
unsigned long i;
103+
104+
xa_for_each(&domain->iommu_array, i, info)
105+
qi_flush_piotlb(info->iommu,
106+
domain_id_iommu(domain, info->iommu),
107+
IOMMU_NO_PASID, addr, npages, ih);
108+
109+
if (!domain->has_iotlb_device)
110+
return;
111+
112+
if (npages == U64_MAX)
113+
mask = 64 - VTD_PAGE_SHIFT;
114+
else
115+
mask = ilog2(__roundup_pow_of_two(npages));
116+
117+
nested_flush_dev_iotlb(domain, addr, mask);
118+
}
119+
120+
static int intel_nested_cache_invalidate_user(struct iommu_domain *domain,
121+
struct iommu_user_data_array *array)
122+
{
123+
struct dmar_domain *dmar_domain = to_dmar_domain(domain);
124+
struct iommu_hwpt_vtd_s1_invalidate inv_entry;
125+
u32 index, processed = 0;
126+
int ret = 0;
127+
128+
if (array->type != IOMMU_HWPT_INVALIDATE_DATA_VTD_S1) {
129+
ret = -EINVAL;
130+
goto out;
131+
}
132+
133+
for (index = 0; index < array->entry_num; index++) {
134+
ret = iommu_copy_struct_from_user_array(&inv_entry, array,
135+
IOMMU_HWPT_INVALIDATE_DATA_VTD_S1,
136+
index, __reserved);
137+
if (ret)
138+
break;
139+
140+
if ((inv_entry.flags & ~IOMMU_VTD_INV_FLAGS_LEAF) ||
141+
inv_entry.__reserved) {
142+
ret = -EOPNOTSUPP;
143+
break;
144+
}
145+
146+
if (!IS_ALIGNED(inv_entry.addr, VTD_PAGE_SIZE) ||
147+
((inv_entry.npages == U64_MAX) && inv_entry.addr)) {
148+
ret = -EINVAL;
149+
break;
150+
}
151+
152+
intel_nested_flush_cache(dmar_domain, inv_entry.addr,
153+
inv_entry.npages,
154+
inv_entry.flags & IOMMU_VTD_INV_FLAGS_LEAF);
155+
processed++;
156+
}
157+
158+
out:
159+
array->entry_num = processed;
160+
return ret;
161+
}
162+
76163
static const struct iommu_domain_ops intel_nested_domain_ops = {
77164
.attach_dev = intel_nested_attach_dev,
78165
.free = intel_nested_domain_free,
166+
.cache_invalidate_user = intel_nested_cache_invalidate_user,
79167
};
80168

81169
struct iommu_domain *intel_nested_domain_alloc(struct iommu_domain *parent,

drivers/iommu/iommufd/hw_pagetable.c

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,3 +373,44 @@ int iommufd_hwpt_get_dirty_bitmap(struct iommufd_ucmd *ucmd)
373373
iommufd_put_object(ucmd->ictx, &hwpt_paging->common.obj);
374374
return rc;
375375
}
376+
377+
int iommufd_hwpt_invalidate(struct iommufd_ucmd *ucmd)
378+
{
379+
struct iommu_hwpt_invalidate *cmd = ucmd->cmd;
380+
struct iommu_user_data_array data_array = {
381+
.type = cmd->data_type,
382+
.uptr = u64_to_user_ptr(cmd->data_uptr),
383+
.entry_len = cmd->entry_len,
384+
.entry_num = cmd->entry_num,
385+
};
386+
struct iommufd_hw_pagetable *hwpt;
387+
u32 done_num = 0;
388+
int rc;
389+
390+
if (cmd->__reserved) {
391+
rc = -EOPNOTSUPP;
392+
goto out;
393+
}
394+
395+
if (cmd->entry_num && (!cmd->data_uptr || !cmd->entry_len)) {
396+
rc = -EINVAL;
397+
goto out;
398+
}
399+
400+
hwpt = iommufd_get_hwpt_nested(ucmd, cmd->hwpt_id);
401+
if (IS_ERR(hwpt)) {
402+
rc = PTR_ERR(hwpt);
403+
goto out;
404+
}
405+
406+
rc = hwpt->domain->ops->cache_invalidate_user(hwpt->domain,
407+
&data_array);
408+
done_num = data_array.entry_num;
409+
410+
iommufd_put_object(ucmd->ictx, &hwpt->obj);
411+
out:
412+
cmd->entry_num = done_num;
413+
if (iommufd_ucmd_respond(ucmd, sizeof(*cmd)))
414+
return -EFAULT;
415+
return rc;
416+
}

drivers/iommu/iommufd/iommufd_private.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,15 @@ iommufd_get_hwpt_paging(struct iommufd_ucmd *ucmd, u32 id)
328328
IOMMUFD_OBJ_HWPT_PAGING),
329329
struct iommufd_hwpt_paging, common.obj);
330330
}
331+
332+
static inline struct iommufd_hw_pagetable *
333+
iommufd_get_hwpt_nested(struct iommufd_ucmd *ucmd, u32 id)
334+
{
335+
return container_of(iommufd_get_object(ucmd->ictx, id,
336+
IOMMUFD_OBJ_HWPT_NESTED),
337+
struct iommufd_hw_pagetable, obj);
338+
}
339+
331340
int iommufd_hwpt_set_dirty_tracking(struct iommufd_ucmd *ucmd);
332341
int iommufd_hwpt_get_dirty_bitmap(struct iommufd_ucmd *ucmd);
333342

@@ -345,6 +354,7 @@ void iommufd_hwpt_paging_abort(struct iommufd_object *obj);
345354
void iommufd_hwpt_nested_destroy(struct iommufd_object *obj);
346355
void iommufd_hwpt_nested_abort(struct iommufd_object *obj);
347356
int iommufd_hwpt_alloc(struct iommufd_ucmd *ucmd);
357+
int iommufd_hwpt_invalidate(struct iommufd_ucmd *ucmd);
348358

349359
static inline void iommufd_hw_pagetable_put(struct iommufd_ctx *ictx,
350360
struct iommufd_hw_pagetable *hwpt)

drivers/iommu/iommufd/iommufd_test.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ enum {
2121
IOMMU_TEST_OP_ACCESS_REPLACE_IOAS,
2222
IOMMU_TEST_OP_MOCK_DOMAIN_FLAGS,
2323
IOMMU_TEST_OP_DIRTY,
24+
IOMMU_TEST_OP_MD_CHECK_IOTLB,
2425
};
2526

2627
enum {
@@ -121,6 +122,10 @@ struct iommu_test_cmd {
121122
__aligned_u64 uptr;
122123
__aligned_u64 out_nr_dirty;
123124
} dirty;
125+
struct {
126+
__u32 id;
127+
__u32 iotlb;
128+
} check_iotlb;
124129
};
125130
__u32 last;
126131
};
@@ -148,4 +153,22 @@ struct iommu_hwpt_selftest {
148153
__u32 iotlb;
149154
};
150155

156+
/* Should not be equal to any defined value in enum iommu_hwpt_invalidate_data_type */
157+
#define IOMMU_HWPT_INVALIDATE_DATA_SELFTEST 0xdeadbeef
158+
#define IOMMU_HWPT_INVALIDATE_DATA_SELFTEST_INVALID 0xdadbeef
159+
160+
/**
161+
* struct iommu_hwpt_invalidate_selftest - Invalidation data for Mock driver
162+
* (IOMMU_HWPT_INVALIDATE_DATA_SELFTEST)
163+
* @flags: Invalidate flags
164+
* @iotlb_id: Invalidate iotlb entry index
165+
*
166+
* If IOMMU_TEST_INVALIDATE_ALL is set in @flags, @iotlb_id will be ignored
167+
*/
168+
struct iommu_hwpt_invalidate_selftest {
169+
#define IOMMU_TEST_INVALIDATE_FLAG_ALL (1 << 0)
170+
__u32 flags;
171+
__u32 iotlb_id;
172+
};
173+
151174
#endif

drivers/iommu/iommufd/main.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,7 @@ union ucmd_buffer {
322322
struct iommu_hw_info info;
323323
struct iommu_hwpt_alloc hwpt;
324324
struct iommu_hwpt_get_dirty_bitmap get_dirty_bitmap;
325+
struct iommu_hwpt_invalidate cache;
325326
struct iommu_hwpt_set_dirty_tracking set_dirty_tracking;
326327
struct iommu_ioas_alloc alloc;
327328
struct iommu_ioas_allow_iovas allow_iovas;
@@ -360,6 +361,8 @@ static const struct iommufd_ioctl_op iommufd_ioctl_ops[] = {
360361
__reserved),
361362
IOCTL_OP(IOMMU_HWPT_GET_DIRTY_BITMAP, iommufd_hwpt_get_dirty_bitmap,
362363
struct iommu_hwpt_get_dirty_bitmap, data),
364+
IOCTL_OP(IOMMU_HWPT_INVALIDATE, iommufd_hwpt_invalidate,
365+
struct iommu_hwpt_invalidate, __reserved),
363366
IOCTL_OP(IOMMU_HWPT_SET_DIRTY_TRACKING, iommufd_hwpt_set_dirty_tracking,
364367
struct iommu_hwpt_set_dirty_tracking, __reserved),
365368
IOCTL_OP(IOMMU_IOAS_ALLOC, iommufd_ioas_alloc_ioctl,

drivers/iommu/iommufd/selftest.c

Lines changed: 91 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,19 @@ static struct iommu_domain_ops domain_nested_ops;
2525

2626
size_t iommufd_test_memory_limit = 65536;
2727

28+
struct mock_bus_type {
29+
struct bus_type bus;
30+
struct notifier_block nb;
31+
};
32+
33+
static struct mock_bus_type iommufd_mock_bus_type = {
34+
.bus = {
35+
.name = "iommufd_mock",
36+
},
37+
};
38+
39+
static atomic_t mock_dev_num;
40+
2841
enum {
2942
MOCK_DIRTY_TRACK = 1,
3043
MOCK_IO_PAGE_SIZE = PAGE_SIZE / 2,
@@ -437,6 +450,8 @@ static struct iommu_device mock_iommu_device = {
437450

438451
static struct iommu_device *mock_probe_device(struct device *dev)
439452
{
453+
if (dev->bus != &iommufd_mock_bus_type.bus)
454+
return ERR_PTR(-ENODEV);
440455
return &mock_iommu_device;
441456
}
442457

@@ -473,9 +488,59 @@ static void mock_domain_free_nested(struct iommu_domain *domain)
473488
kfree(mock_nested);
474489
}
475490

491+
static int
492+
mock_domain_cache_invalidate_user(struct iommu_domain *domain,
493+
struct iommu_user_data_array *array)
494+
{
495+
struct mock_iommu_domain_nested *mock_nested =
496+
container_of(domain, struct mock_iommu_domain_nested, domain);
497+
struct iommu_hwpt_invalidate_selftest inv;
498+
u32 processed = 0;
499+
int i = 0, j;
500+
int rc = 0;
501+
502+
if (array->type != IOMMU_HWPT_INVALIDATE_DATA_SELFTEST) {
503+
rc = -EINVAL;
504+
goto out;
505+
}
506+
507+
for ( ; i < array->entry_num; i++) {
508+
rc = iommu_copy_struct_from_user_array(&inv, array,
509+
IOMMU_HWPT_INVALIDATE_DATA_SELFTEST,
510+
i, iotlb_id);
511+
if (rc)
512+
break;
513+
514+
if (inv.flags & ~IOMMU_TEST_INVALIDATE_FLAG_ALL) {
515+
rc = -EOPNOTSUPP;
516+
break;
517+
}
518+
519+
if (inv.iotlb_id > MOCK_NESTED_DOMAIN_IOTLB_ID_MAX) {
520+
rc = -EINVAL;
521+
break;
522+
}
523+
524+
if (inv.flags & IOMMU_TEST_INVALIDATE_FLAG_ALL) {
525+
/* Invalidate all mock iotlb entries and ignore iotlb_id */
526+
for (j = 0; j < MOCK_NESTED_DOMAIN_IOTLB_NUM; j++)
527+
mock_nested->iotlb[j] = 0;
528+
} else {
529+
mock_nested->iotlb[inv.iotlb_id] = 0;
530+
}
531+
532+
processed++;
533+
}
534+
535+
out:
536+
array->entry_num = processed;
537+
return rc;
538+
}
539+
476540
static struct iommu_domain_ops domain_nested_ops = {
477541
.free = mock_domain_free_nested,
478542
.attach_dev = mock_domain_nop_attach,
543+
.cache_invalidate_user = mock_domain_cache_invalidate_user,
479544
};
480545

481546
static inline struct iommufd_hw_pagetable *
@@ -526,19 +591,6 @@ get_md_pagetable_nested(struct iommufd_ucmd *ucmd, u32 mockpt_id,
526591
return hwpt;
527592
}
528593

529-
struct mock_bus_type {
530-
struct bus_type bus;
531-
struct notifier_block nb;
532-
};
533-
534-
static struct mock_bus_type iommufd_mock_bus_type = {
535-
.bus = {
536-
.name = "iommufd_mock",
537-
},
538-
};
539-
540-
static atomic_t mock_dev_num;
541-
542594
static void mock_dev_release(struct device *dev)
543595
{
544596
struct mock_dev *mdev = container_of(dev, struct mock_dev, dev);
@@ -793,6 +845,28 @@ static int iommufd_test_md_check_refs(struct iommufd_ucmd *ucmd,
793845
return 0;
794846
}
795847

848+
static int iommufd_test_md_check_iotlb(struct iommufd_ucmd *ucmd,
849+
u32 mockpt_id, unsigned int iotlb_id,
850+
u32 iotlb)
851+
{
852+
struct mock_iommu_domain_nested *mock_nested;
853+
struct iommufd_hw_pagetable *hwpt;
854+
int rc = 0;
855+
856+
hwpt = get_md_pagetable_nested(ucmd, mockpt_id, &mock_nested);
857+
if (IS_ERR(hwpt))
858+
return PTR_ERR(hwpt);
859+
860+
mock_nested = container_of(hwpt->domain,
861+
struct mock_iommu_domain_nested, domain);
862+
863+
if (iotlb_id > MOCK_NESTED_DOMAIN_IOTLB_ID_MAX ||
864+
mock_nested->iotlb[iotlb_id] != iotlb)
865+
rc = -EINVAL;
866+
iommufd_put_object(ucmd->ictx, &hwpt->obj);
867+
return rc;
868+
}
869+
796870
struct selftest_access {
797871
struct iommufd_access *access;
798872
struct file *file;
@@ -1274,6 +1348,10 @@ int iommufd_test(struct iommufd_ucmd *ucmd)
12741348
return iommufd_test_md_check_refs(
12751349
ucmd, u64_to_user_ptr(cmd->check_refs.uptr),
12761350
cmd->check_refs.length, cmd->check_refs.refs);
1351+
case IOMMU_TEST_OP_MD_CHECK_IOTLB:
1352+
return iommufd_test_md_check_iotlb(ucmd, cmd->id,
1353+
cmd->check_iotlb.id,
1354+
cmd->check_iotlb.iotlb);
12771355
case IOMMU_TEST_OP_CREATE_ACCESS:
12781356
return iommufd_test_create_access(ucmd, cmd->id,
12791357
cmd->create_access.flags);

0 commit comments

Comments
 (0)