Skip to content

Commit 65c619a

Browse files
committed
iommufd/selftest: Make selftest create a more complete mock device
iommufd wants to use more infrastructure, like the iommu_group, that the mock device does not support. Create a more complete mock device that can go through the whole cycle of ownership, blocking domain, and has an iommu_group. This requires creating a real struct device on a real bus to be able to connect it to a iommu_group. Unfortunately we cannot formally attach the mock iommu driver as an actual driver as the iommu core does not allow more than one driver or provide a general way for busses to link to iommus. This can be solved with a little hack to open code the dev_iommus struct. With this infrastructure things work exactly the same as the normal domain path, including the auto domains mechanism and direct attach of hwpts. As the created hwpt is now an autodomain it is no longer required to destroy it and trying to do so will trigger a failure. Link: https://lore.kernel.org/r/11-v3-ae9c2975a131+2e1e8-iommufd_hwpt_jgg@nvidia.com Reviewed-by: Kevin Tian <kevin.tian@intel.com> Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
1 parent f8406f6 commit 65c619a

5 files changed

Lines changed: 181 additions & 76 deletions

File tree

drivers/iommu/iommufd/device.c

Lines changed: 6 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ void iommufd_device_destroy(struct iommufd_object *obj)
2222

2323
iommu_device_release_dma_owner(idev->dev);
2424
iommu_group_put(idev->group);
25-
iommufd_ctx_put(idev->ictx);
25+
if (!iommufd_selftest_is_mock_dev(idev->dev))
26+
iommufd_ctx_put(idev->ictx);
2627
}
2728

2829
/**
@@ -69,7 +70,8 @@ struct iommufd_device *iommufd_device_bind(struct iommufd_ctx *ictx,
6970
goto out_release_owner;
7071
}
7172
idev->ictx = ictx;
72-
iommufd_ctx_get(ictx);
73+
if (!iommufd_selftest_is_mock_dev(dev))
74+
iommufd_ctx_get(ictx);
7375
idev->dev = dev;
7476
idev->enforce_cache_coherency =
7577
device_iommu_capable(dev, IOMMU_CAP_ENFORCE_CACHE_COHERENCY);
@@ -151,7 +153,8 @@ static int iommufd_device_setup_msi(struct iommufd_device *idev,
151153
* operation from the device (eg a simple DMA) cannot trigger an
152154
* interrupt outside this iommufd context.
153155
*/
154-
if (!iommu_group_has_isolated_msi(idev->group)) {
156+
if (!iommufd_selftest_is_mock_dev(idev->dev) &&
157+
!iommu_group_has_isolated_msi(idev->group)) {
155158
if (!allow_unsafe_interrupts)
156159
return -EPERM;
157160

@@ -706,34 +709,3 @@ int iommufd_access_rw(struct iommufd_access *access, unsigned long iova,
706709
return rc;
707710
}
708711
EXPORT_SYMBOL_NS_GPL(iommufd_access_rw, IOMMUFD);
709-
710-
#ifdef CONFIG_IOMMUFD_TEST
711-
/*
712-
* Creating a real iommufd_device is too hard, bypass creating a iommufd_device
713-
* and go directly to attaching a domain.
714-
*/
715-
struct iommufd_hw_pagetable *
716-
iommufd_device_selftest_attach(struct iommufd_ctx *ictx,
717-
struct iommufd_ioas *ioas,
718-
struct device *mock_dev)
719-
{
720-
struct iommufd_device tmp_idev = { .dev = mock_dev };
721-
struct iommufd_hw_pagetable *hwpt;
722-
723-
mutex_lock(&ioas->mutex);
724-
hwpt = iommufd_hw_pagetable_alloc(ictx, ioas, &tmp_idev, false);
725-
mutex_unlock(&ioas->mutex);
726-
if (IS_ERR(hwpt))
727-
return hwpt;
728-
729-
refcount_inc(&hwpt->obj.users);
730-
iommufd_object_finalize(ictx, &hwpt->obj);
731-
return hwpt;
732-
}
733-
734-
void iommufd_device_selftest_detach(struct iommufd_ctx *ictx,
735-
struct iommufd_hw_pagetable *hwpt)
736-
{
737-
refcount_dec(&hwpt->obj.users);
738-
}
739-
#endif

drivers/iommu/iommufd/iommufd_private.h

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -297,12 +297,6 @@ void iopt_remove_access(struct io_pagetable *iopt,
297297
void iommufd_access_destroy_object(struct iommufd_object *obj);
298298

299299
#ifdef CONFIG_IOMMUFD_TEST
300-
struct iommufd_hw_pagetable *
301-
iommufd_device_selftest_attach(struct iommufd_ctx *ictx,
302-
struct iommufd_ioas *ioas,
303-
struct device *mock_dev);
304-
void iommufd_device_selftest_detach(struct iommufd_ctx *ictx,
305-
struct iommufd_hw_pagetable *hwpt);
306300
int iommufd_test(struct iommufd_ucmd *ucmd);
307301
void iommufd_selftest_destroy(struct iommufd_object *obj);
308302
extern size_t iommufd_test_memory_limit;
@@ -311,6 +305,7 @@ void iommufd_test_syz_conv_iova_id(struct iommufd_ucmd *ucmd,
311305
bool iommufd_should_fail(void);
312306
void __init iommufd_test_init(void);
313307
void iommufd_test_exit(void);
308+
bool iommufd_selftest_is_mock_dev(struct device *dev);
314309
#else
315310
static inline void iommufd_test_syz_conv_iova_id(struct iommufd_ucmd *ucmd,
316311
unsigned int ioas_id,
@@ -327,5 +322,9 @@ static inline void __init iommufd_test_init(void)
327322
static inline void iommufd_test_exit(void)
328323
{
329324
}
325+
static inline bool iommufd_selftest_is_mock_dev(struct device *dev)
326+
{
327+
return false;
328+
}
330329
#endif
331330
#endif

drivers/iommu/iommufd/selftest.c

Lines changed: 170 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -91,23 +91,50 @@ enum selftest_obj_type {
9191
TYPE_IDEV,
9292
};
9393

94+
struct mock_dev {
95+
struct device dev;
96+
};
97+
9498
struct selftest_obj {
9599
struct iommufd_object obj;
96100
enum selftest_obj_type type;
97101

98102
union {
99103
struct {
100-
struct iommufd_hw_pagetable *hwpt;
104+
struct iommufd_device *idev;
101105
struct iommufd_ctx *ictx;
102-
struct device mock_dev;
106+
struct mock_dev *mock_dev;
103107
} idev;
104108
};
105109
};
106110

111+
static void mock_domain_blocking_free(struct iommu_domain *domain)
112+
{
113+
}
114+
115+
static int mock_domain_nop_attach(struct iommu_domain *domain,
116+
struct device *dev)
117+
{
118+
return 0;
119+
}
120+
121+
static const struct iommu_domain_ops mock_blocking_ops = {
122+
.free = mock_domain_blocking_free,
123+
.attach_dev = mock_domain_nop_attach,
124+
};
125+
126+
static struct iommu_domain mock_blocking_domain = {
127+
.type = IOMMU_DOMAIN_BLOCKED,
128+
.ops = &mock_blocking_ops,
129+
};
130+
107131
static struct iommu_domain *mock_domain_alloc(unsigned int iommu_domain_type)
108132
{
109133
struct mock_iommu_domain *mock;
110134

135+
if (iommu_domain_type == IOMMU_DOMAIN_BLOCKED)
136+
return &mock_blocking_domain;
137+
111138
if (WARN_ON(iommu_domain_type != IOMMU_DOMAIN_UNMANAGED))
112139
return NULL;
113140

@@ -236,19 +263,39 @@ static phys_addr_t mock_domain_iova_to_phys(struct iommu_domain *domain,
236263
return (xa_to_value(ent) & MOCK_PFN_MASK) * MOCK_IO_PAGE_SIZE;
237264
}
238265

266+
static bool mock_domain_capable(struct device *dev, enum iommu_cap cap)
267+
{
268+
return cap == IOMMU_CAP_CACHE_COHERENCY;
269+
}
270+
271+
static void mock_domain_set_plaform_dma_ops(struct device *dev)
272+
{
273+
/*
274+
* mock doesn't setup default domains because we can't hook into the
275+
* normal probe path
276+
*/
277+
}
278+
239279
static const struct iommu_ops mock_ops = {
240280
.owner = THIS_MODULE,
241281
.pgsize_bitmap = MOCK_IO_PAGE_SIZE,
242282
.domain_alloc = mock_domain_alloc,
283+
.capable = mock_domain_capable,
284+
.set_platform_dma_ops = mock_domain_set_plaform_dma_ops,
243285
.default_domain_ops =
244286
&(struct iommu_domain_ops){
245287
.free = mock_domain_free,
288+
.attach_dev = mock_domain_nop_attach,
246289
.map_pages = mock_domain_map_pages,
247290
.unmap_pages = mock_domain_unmap_pages,
248291
.iova_to_phys = mock_domain_iova_to_phys,
249292
},
250293
};
251294

295+
struct iommu_device mock_iommu_device = {
296+
.ops = &mock_ops,
297+
};
298+
252299
static inline struct iommufd_hw_pagetable *
253300
get_md_pagetable(struct iommufd_ucmd *ucmd, u32 mockpt_id,
254301
struct mock_iommu_domain **mock)
@@ -269,48 +316,142 @@ get_md_pagetable(struct iommufd_ucmd *ucmd, u32 mockpt_id,
269316
return hwpt;
270317
}
271318

319+
static struct bus_type iommufd_mock_bus_type = {
320+
.name = "iommufd_mock",
321+
.iommu_ops = &mock_ops,
322+
};
323+
324+
static void mock_dev_release(struct device *dev)
325+
{
326+
struct mock_dev *mdev = container_of(dev, struct mock_dev, dev);
327+
328+
kfree(mdev);
329+
}
330+
331+
static struct mock_dev *mock_dev_create(void)
332+
{
333+
struct iommu_group *iommu_group;
334+
struct dev_iommu *dev_iommu;
335+
struct mock_dev *mdev;
336+
int rc;
337+
338+
mdev = kzalloc(sizeof(*mdev), GFP_KERNEL);
339+
if (!mdev)
340+
return ERR_PTR(-ENOMEM);
341+
342+
device_initialize(&mdev->dev);
343+
mdev->dev.release = mock_dev_release;
344+
mdev->dev.bus = &iommufd_mock_bus_type;
345+
346+
iommu_group = iommu_group_alloc();
347+
if (IS_ERR(iommu_group)) {
348+
rc = PTR_ERR(iommu_group);
349+
goto err_put;
350+
}
351+
352+
rc = dev_set_name(&mdev->dev, "iommufd_mock%u",
353+
iommu_group_id(iommu_group));
354+
if (rc)
355+
goto err_group;
356+
357+
/*
358+
* The iommu core has no way to associate a single device with an iommu
359+
* driver (heck currently it can't even support two iommu_drivers
360+
* registering). Hack it together with an open coded dev_iommu_get().
361+
* Notice that the normal notifier triggered iommu release process also
362+
* does not work here because this bus is not in iommu_buses.
363+
*/
364+
mdev->dev.iommu = kzalloc(sizeof(*dev_iommu), GFP_KERNEL);
365+
if (!mdev->dev.iommu) {
366+
rc = -ENOMEM;
367+
goto err_group;
368+
}
369+
mutex_init(&mdev->dev.iommu->lock);
370+
mdev->dev.iommu->iommu_dev = &mock_iommu_device;
371+
372+
rc = device_add(&mdev->dev);
373+
if (rc)
374+
goto err_dev_iommu;
375+
376+
rc = iommu_group_add_device(iommu_group, &mdev->dev);
377+
if (rc)
378+
goto err_del;
379+
iommu_group_put(iommu_group);
380+
return mdev;
381+
382+
err_del:
383+
device_del(&mdev->dev);
384+
err_dev_iommu:
385+
kfree(mdev->dev.iommu);
386+
mdev->dev.iommu = NULL;
387+
err_group:
388+
iommu_group_put(iommu_group);
389+
err_put:
390+
put_device(&mdev->dev);
391+
return ERR_PTR(rc);
392+
}
393+
394+
static void mock_dev_destroy(struct mock_dev *mdev)
395+
{
396+
iommu_group_remove_device(&mdev->dev);
397+
device_del(&mdev->dev);
398+
kfree(mdev->dev.iommu);
399+
mdev->dev.iommu = NULL;
400+
put_device(&mdev->dev);
401+
}
402+
403+
bool iommufd_selftest_is_mock_dev(struct device *dev)
404+
{
405+
return dev->release == mock_dev_release;
406+
}
407+
272408
/* Create an hw_pagetable with the mock domain so we can test the domain ops */
273409
static int iommufd_test_mock_domain(struct iommufd_ucmd *ucmd,
274410
struct iommu_test_cmd *cmd)
275411
{
276-
static struct bus_type mock_bus = { .iommu_ops = &mock_ops };
277-
struct iommufd_hw_pagetable *hwpt;
412+
struct iommufd_device *idev;
278413
struct selftest_obj *sobj;
279-
struct iommufd_ioas *ioas;
414+
u32 pt_id = cmd->id;
415+
u32 idev_id;
280416
int rc;
281417

282-
ioas = iommufd_get_ioas(ucmd, cmd->id);
283-
if (IS_ERR(ioas))
284-
return PTR_ERR(ioas);
285-
286418
sobj = iommufd_object_alloc(ucmd->ictx, sobj, IOMMUFD_OBJ_SELFTEST);
287-
if (IS_ERR(sobj)) {
288-
rc = PTR_ERR(sobj);
289-
goto out_ioas;
290-
}
419+
if (IS_ERR(sobj))
420+
return PTR_ERR(sobj);
421+
291422
sobj->idev.ictx = ucmd->ictx;
292423
sobj->type = TYPE_IDEV;
293-
sobj->idev.mock_dev.bus = &mock_bus;
294424

295-
hwpt = iommufd_device_selftest_attach(ucmd->ictx, ioas,
296-
&sobj->idev.mock_dev);
297-
if (IS_ERR(hwpt)) {
298-
rc = PTR_ERR(hwpt);
425+
sobj->idev.mock_dev = mock_dev_create();
426+
if (IS_ERR(sobj->idev.mock_dev)) {
427+
rc = PTR_ERR(sobj->idev.mock_dev);
299428
goto out_sobj;
300429
}
301-
sobj->idev.hwpt = hwpt;
302430

303-
/* Userspace must destroy both of these IDs to destroy the object */
304-
cmd->mock_domain.out_hwpt_id = hwpt->obj.id;
431+
idev = iommufd_device_bind(ucmd->ictx, &sobj->idev.mock_dev->dev,
432+
&idev_id);
433+
if (IS_ERR(idev)) {
434+
rc = PTR_ERR(idev);
435+
goto out_mdev;
436+
}
437+
sobj->idev.idev = idev;
438+
439+
rc = iommufd_device_attach(idev, &pt_id);
440+
if (rc)
441+
goto out_unbind;
442+
443+
/* Userspace must destroy the device_id to destroy the object */
444+
cmd->mock_domain.out_hwpt_id = pt_id;
305445
cmd->mock_domain.out_stdev_id = sobj->obj.id;
306446
iommufd_object_finalize(ucmd->ictx, &sobj->obj);
307-
iommufd_put_object(&ioas->obj);
308447
return iommufd_ucmd_respond(ucmd, sizeof(*cmd));
309448

449+
out_unbind:
450+
iommufd_device_unbind(idev);
451+
out_mdev:
452+
mock_dev_destroy(sobj->idev.mock_dev);
310453
out_sobj:
311454
iommufd_object_abort(ucmd->ictx, &sobj->obj);
312-
out_ioas:
313-
iommufd_put_object(&ioas->obj);
314455
return rc;
315456
}
316457

@@ -780,8 +921,9 @@ void iommufd_selftest_destroy(struct iommufd_object *obj)
780921

781922
switch (sobj->type) {
782923
case TYPE_IDEV:
783-
iommufd_device_selftest_detach(sobj->idev.ictx,
784-
sobj->idev.hwpt);
924+
iommufd_device_detach(sobj->idev.idev);
925+
iommufd_device_unbind(sobj->idev.idev);
926+
mock_dev_destroy(sobj->idev.mock_dev);
785927
break;
786928
}
787929
}
@@ -845,9 +987,11 @@ void __init iommufd_test_init(void)
845987
{
846988
dbgfs_root =
847989
fault_create_debugfs_attr("fail_iommufd", NULL, &fail_iommufd);
990+
WARN_ON(bus_register(&iommufd_mock_bus_type));
848991
}
849992

850993
void iommufd_test_exit(void)
851994
{
852995
debugfs_remove_recursive(dbgfs_root);
996+
bus_unregister(&iommufd_mock_bus_type);
853997
}

tools/testing/selftests/iommu/iommufd.c

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -645,7 +645,6 @@ TEST_F(iommufd_ioas, access_pin)
645645
&check_map_cmd));
646646

647647
test_ioctl_destroy(mock_stdev_id);
648-
test_ioctl_destroy(mock_hwpt_id);
649648
test_cmd_destroy_access_pages(
650649
access_cmd.id,
651650
access_cmd.access_pages.out_access_pages_id);
@@ -1214,7 +1213,6 @@ TEST_F(iommufd_mock_domain, all_aligns_copy)
12141213
1);
12151214

12161215
test_ioctl_destroy(mock_stdev_id);
1217-
test_ioctl_destroy(self->hwpt_ids[1]);
12181216
self->hwpt_ids[1] = old_id;
12191217

12201218
test_ioctl_ioas_unmap(iova, length);

0 commit comments

Comments
 (0)