Skip to content

Commit 6df969b

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: "Two series: - Reorganize how the hardware page table objects are managed, particularly their destruction flow. Increase the selftest test coverage in this area by creating a more complete mock iommu driver. This is preparation to add a replace operation for HWPT binding, which is done but waiting for the VFIO parts to complete so there is a user. - Split the iommufd support for "access" to make it two step - allocate an access then link it to an IOAS. Update VFIO and have VFIO always create an access even for the VFIO mdevs that never do DMA. This is also preperation for the replace VFIO series that will allow replace to work on access types as well. Three minor fixes: - Sykzaller found the selftest code didn't check for overflow when processing user VAs - smatch noted a .data item should have been static - Add a selftest that reproduces a syzkaller bug for batch carry already fixed in rc" * tag 'for-linus-iommufd' of git://git.kernel.org/pub/scm/linux/kernel/git/jgg/iommufd: (21 commits) iommufd/selftest: Cover domain unmap with huge pages and access iommufd/selftest: Set varaiable mock_iommu_device storage-class-specifier to static vfio: Check the presence for iommufd callbacks in __vfio_register_dev() vfio/mdev: Uses the vfio emulated iommufd ops set in the mdev sample drivers vfio-iommufd: Make vfio_iommufd_emulated_bind() return iommufd_access ID vfio-iommufd: No need to record iommufd_ctx in vfio_device iommufd: Create access in vfio_iommufd_emulated_bind() iommu/iommufd: Pass iommufd_ctx pointer in iommufd_get_ioas() iommufd/selftest: Catch overflow of uptr and length iommufd/selftest: Add a selftest for iommufd_device_attach() with a hwpt argument iommufd/selftest: Make selftest create a more complete mock device iommufd/selftest: Rename the remaining mock device_id's to stdev_id iommufd/selftest: Rename domain_id to hwpt_id for FIXTURE iommufd_mock_domain iommufd/selftest: Rename domain_id to stdev_id for FIXTURE iommufd_ioas iommufd/selftest: Rename the sefltest 'device_id' to 'stdev_id' iommufd: Make iommufd_hw_pagetable_alloc() do iopt_table_add_domain() iommufd: Move iommufd_device to iommufd_private.h iommufd: Move ioas related HWPT destruction into iommufd_hw_pagetable_destroy() iommufd: Consistently manage hwpt_item iommufd: Add iommufd_lock_obj() around the auto-domains hwpts ...
2 parents 32f7ad0 + 62e37c8 commit 6df969b

17 files changed

Lines changed: 494 additions & 272 deletions

File tree

drivers/iommu/iommufd/device.c

Lines changed: 80 additions & 125 deletions
Original file line numberDiff line numberDiff line change
@@ -15,31 +15,15 @@ MODULE_PARM_DESC(
1515
"Allow IOMMUFD to bind to devices even if the platform cannot isolate "
1616
"the MSI interrupt window. Enabling this is a security weakness.");
1717

18-
/*
19-
* A iommufd_device object represents the binding relationship between a
20-
* consuming driver and the iommufd. These objects are created/destroyed by
21-
* external drivers, not by userspace.
22-
*/
23-
struct iommufd_device {
24-
struct iommufd_object obj;
25-
struct iommufd_ctx *ictx;
26-
struct iommufd_hw_pagetable *hwpt;
27-
/* Head at iommufd_hw_pagetable::devices */
28-
struct list_head devices_item;
29-
/* always the physical device */
30-
struct device *dev;
31-
struct iommu_group *group;
32-
bool enforce_cache_coherency;
33-
};
34-
3518
void iommufd_device_destroy(struct iommufd_object *obj)
3619
{
3720
struct iommufd_device *idev =
3821
container_of(obj, struct iommufd_device, obj);
3922

4023
iommu_device_release_dma_owner(idev->dev);
4124
iommu_group_put(idev->group);
42-
iommufd_ctx_put(idev->ictx);
25+
if (!iommufd_selftest_is_mock_dev(idev->dev))
26+
iommufd_ctx_put(idev->ictx);
4327
}
4428

4529
/**
@@ -86,7 +70,8 @@ struct iommufd_device *iommufd_device_bind(struct iommufd_ctx *ictx,
8670
goto out_release_owner;
8771
}
8872
idev->ictx = ictx;
89-
iommufd_ctx_get(ictx);
73+
if (!iommufd_selftest_is_mock_dev(dev))
74+
iommufd_ctx_get(ictx);
9075
idev->dev = dev;
9176
idev->enforce_cache_coherency =
9277
device_iommu_capable(dev, IOMMU_CAP_ENFORCE_CACHE_COHERENCY);
@@ -168,7 +153,8 @@ static int iommufd_device_setup_msi(struct iommufd_device *idev,
168153
* operation from the device (eg a simple DMA) cannot trigger an
169154
* interrupt outside this iommufd context.
170155
*/
171-
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)) {
172158
if (!allow_unsafe_interrupts)
173159
return -EPERM;
174160

@@ -186,19 +172,24 @@ static bool iommufd_hw_pagetable_has_group(struct iommufd_hw_pagetable *hwpt,
186172
{
187173
struct iommufd_device *cur_dev;
188174

175+
lockdep_assert_held(&hwpt->devices_lock);
176+
189177
list_for_each_entry(cur_dev, &hwpt->devices, devices_item)
190178
if (cur_dev->group == group)
191179
return true;
192180
return false;
193181
}
194182

195-
static int iommufd_device_do_attach(struct iommufd_device *idev,
196-
struct iommufd_hw_pagetable *hwpt)
183+
int iommufd_hw_pagetable_attach(struct iommufd_hw_pagetable *hwpt,
184+
struct iommufd_device *idev)
197185
{
198186
phys_addr_t sw_msi_start = PHYS_ADDR_MAX;
199187
int rc;
200188

201-
mutex_lock(&hwpt->devices_lock);
189+
lockdep_assert_held(&hwpt->devices_lock);
190+
191+
if (WARN_ON(idev->hwpt))
192+
return -EINVAL;
202193

203194
/*
204195
* Try to upgrade the domain we have, it is an iommu driver bug to
@@ -213,19 +204,18 @@ static int iommufd_device_do_attach(struct iommufd_device *idev,
213204
hwpt->domain);
214205
if (!hwpt->enforce_cache_coherency) {
215206
WARN_ON(list_empty(&hwpt->devices));
216-
rc = -EINVAL;
217-
goto out_unlock;
207+
return -EINVAL;
218208
}
219209
}
220210

221211
rc = iopt_table_enforce_group_resv_regions(&hwpt->ioas->iopt, idev->dev,
222212
idev->group, &sw_msi_start);
223213
if (rc)
224-
goto out_unlock;
214+
return rc;
225215

226216
rc = iommufd_device_setup_msi(idev, hwpt, sw_msi_start);
227217
if (rc)
228-
goto out_iova;
218+
goto err_unresv;
229219

230220
/*
231221
* FIXME: Hack around missing a device-centric iommu api, only attach to
@@ -234,26 +224,35 @@ static int iommufd_device_do_attach(struct iommufd_device *idev,
234224
if (!iommufd_hw_pagetable_has_group(hwpt, idev->group)) {
235225
rc = iommu_attach_group(hwpt->domain, idev->group);
236226
if (rc)
237-
goto out_iova;
238-
239-
if (list_empty(&hwpt->devices)) {
240-
rc = iopt_table_add_domain(&hwpt->ioas->iopt,
241-
hwpt->domain);
242-
if (rc)
243-
goto out_detach;
244-
}
227+
goto err_unresv;
245228
}
229+
return 0;
230+
err_unresv:
231+
iopt_remove_reserved_iova(&hwpt->ioas->iopt, idev->dev);
232+
return rc;
233+
}
234+
235+
void iommufd_hw_pagetable_detach(struct iommufd_hw_pagetable *hwpt,
236+
struct iommufd_device *idev)
237+
{
238+
if (!iommufd_hw_pagetable_has_group(hwpt, idev->group))
239+
iommu_detach_group(hwpt->domain, idev->group);
240+
iopt_remove_reserved_iova(&hwpt->ioas->iopt, idev->dev);
241+
}
242+
243+
static int iommufd_device_do_attach(struct iommufd_device *idev,
244+
struct iommufd_hw_pagetable *hwpt)
245+
{
246+
int rc;
247+
248+
mutex_lock(&hwpt->devices_lock);
249+
rc = iommufd_hw_pagetable_attach(hwpt, idev);
250+
if (rc)
251+
goto out_unlock;
246252

247253
idev->hwpt = hwpt;
248254
refcount_inc(&hwpt->obj.users);
249255
list_add(&idev->devices_item, &hwpt->devices);
250-
mutex_unlock(&hwpt->devices_lock);
251-
return 0;
252-
253-
out_detach:
254-
iommu_detach_group(hwpt->domain, idev->group);
255-
out_iova:
256-
iopt_remove_reserved_iova(&hwpt->ioas->iopt, idev->dev);
257256
out_unlock:
258257
mutex_unlock(&hwpt->devices_lock);
259258
return rc;
@@ -280,7 +279,10 @@ static int iommufd_device_auto_get_domain(struct iommufd_device *idev,
280279
if (!hwpt->auto_domain)
281280
continue;
282281

282+
if (!iommufd_lock_obj(&hwpt->obj))
283+
continue;
283284
rc = iommufd_device_do_attach(idev, hwpt);
285+
iommufd_put_object(&hwpt->obj);
284286

285287
/*
286288
* -EINVAL means the domain is incompatible with the device.
@@ -292,24 +294,16 @@ static int iommufd_device_auto_get_domain(struct iommufd_device *idev,
292294
goto out_unlock;
293295
}
294296

295-
hwpt = iommufd_hw_pagetable_alloc(idev->ictx, ioas, idev->dev);
297+
hwpt = iommufd_hw_pagetable_alloc(idev->ictx, ioas, idev, true);
296298
if (IS_ERR(hwpt)) {
297299
rc = PTR_ERR(hwpt);
298300
goto out_unlock;
299301
}
300302
hwpt->auto_domain = true;
301303

302-
rc = iommufd_device_do_attach(idev, hwpt);
303-
if (rc)
304-
goto out_abort;
305-
list_add_tail(&hwpt->hwpt_item, &ioas->hwpt_list);
306-
307304
mutex_unlock(&ioas->mutex);
308305
iommufd_object_finalize(idev->ictx, &hwpt->obj);
309306
return 0;
310-
311-
out_abort:
312-
iommufd_object_abort_and_destroy(idev->ictx, &hwpt->obj);
313307
out_unlock:
314308
mutex_unlock(&ioas->mutex);
315309
return rc;
@@ -381,28 +375,17 @@ void iommufd_device_detach(struct iommufd_device *idev)
381375
{
382376
struct iommufd_hw_pagetable *hwpt = idev->hwpt;
383377

384-
mutex_lock(&hwpt->ioas->mutex);
385378
mutex_lock(&hwpt->devices_lock);
386379
list_del(&idev->devices_item);
387-
if (!iommufd_hw_pagetable_has_group(hwpt, idev->group)) {
388-
if (list_empty(&hwpt->devices)) {
389-
iopt_table_remove_domain(&hwpt->ioas->iopt,
390-
hwpt->domain);
391-
list_del(&hwpt->hwpt_item);
392-
}
393-
iommu_detach_group(hwpt->domain, idev->group);
394-
}
395-
iopt_remove_reserved_iova(&hwpt->ioas->iopt, idev->dev);
380+
idev->hwpt = NULL;
381+
iommufd_hw_pagetable_detach(hwpt, idev);
396382
mutex_unlock(&hwpt->devices_lock);
397-
mutex_unlock(&hwpt->ioas->mutex);
398383

399384
if (hwpt->auto_domain)
400385
iommufd_object_destroy_user(idev->ictx, &hwpt->obj);
401386
else
402387
refcount_dec(&hwpt->obj.users);
403388

404-
idev->hwpt = NULL;
405-
406389
refcount_dec(&idev->obj.users);
407390
}
408391
EXPORT_SYMBOL_NS_GPL(iommufd_device_detach, IOMMUFD);
@@ -412,17 +395,20 @@ void iommufd_access_destroy_object(struct iommufd_object *obj)
412395
struct iommufd_access *access =
413396
container_of(obj, struct iommufd_access, obj);
414397

415-
iopt_remove_access(&access->ioas->iopt, access);
398+
if (access->ioas) {
399+
iopt_remove_access(&access->ioas->iopt, access);
400+
refcount_dec(&access->ioas->obj.users);
401+
access->ioas = NULL;
402+
}
416403
iommufd_ctx_put(access->ictx);
417-
refcount_dec(&access->ioas->obj.users);
418404
}
419405

420406
/**
421407
* iommufd_access_create - Create an iommufd_access
422408
* @ictx: iommufd file descriptor
423-
* @ioas_id: ID for a IOMMUFD_OBJ_IOAS
424409
* @ops: Driver's ops to associate with the access
425410
* @data: Opaque data to pass into ops functions
411+
* @id: Output ID number to return to userspace for this access
426412
*
427413
* An iommufd_access allows a driver to read/write to the IOAS without using
428414
* DMA. The underlying CPU memory can be accessed using the
@@ -431,12 +417,10 @@ void iommufd_access_destroy_object(struct iommufd_object *obj)
431417
* The provided ops are required to use iommufd_access_pin_pages().
432418
*/
433419
struct iommufd_access *
434-
iommufd_access_create(struct iommufd_ctx *ictx, u32 ioas_id,
435-
const struct iommufd_access_ops *ops, void *data)
420+
iommufd_access_create(struct iommufd_ctx *ictx,
421+
const struct iommufd_access_ops *ops, void *data, u32 *id)
436422
{
437423
struct iommufd_access *access;
438-
struct iommufd_object *obj;
439-
int rc;
440424

441425
/*
442426
* There is no uAPI for the access object, but to keep things symmetric
@@ -449,33 +433,18 @@ iommufd_access_create(struct iommufd_ctx *ictx, u32 ioas_id,
449433
access->data = data;
450434
access->ops = ops;
451435

452-
obj = iommufd_get_object(ictx, ioas_id, IOMMUFD_OBJ_IOAS);
453-
if (IS_ERR(obj)) {
454-
rc = PTR_ERR(obj);
455-
goto out_abort;
456-
}
457-
access->ioas = container_of(obj, struct iommufd_ioas, obj);
458-
iommufd_ref_to_users(obj);
459-
460436
if (ops->needs_pin_pages)
461437
access->iova_alignment = PAGE_SIZE;
462438
else
463439
access->iova_alignment = 1;
464-
rc = iopt_add_access(&access->ioas->iopt, access);
465-
if (rc)
466-
goto out_put_ioas;
467440

468441
/* The calling driver is a user until iommufd_access_destroy() */
469442
refcount_inc(&access->obj.users);
470443
access->ictx = ictx;
471444
iommufd_ctx_get(ictx);
472445
iommufd_object_finalize(ictx, &access->obj);
446+
*id = access->obj.id;
473447
return access;
474-
out_put_ioas:
475-
refcount_dec(&access->ioas->obj.users);
476-
out_abort:
477-
iommufd_object_abort(ictx, &access->obj);
478-
return ERR_PTR(rc);
479448
}
480449
EXPORT_SYMBOL_NS_GPL(iommufd_access_create, IOMMUFD);
481450

@@ -494,6 +463,30 @@ void iommufd_access_destroy(struct iommufd_access *access)
494463
}
495464
EXPORT_SYMBOL_NS_GPL(iommufd_access_destroy, IOMMUFD);
496465

466+
int iommufd_access_attach(struct iommufd_access *access, u32 ioas_id)
467+
{
468+
struct iommufd_ioas *new_ioas;
469+
int rc = 0;
470+
471+
if (access->ioas)
472+
return -EINVAL;
473+
474+
new_ioas = iommufd_get_ioas(access->ictx, ioas_id);
475+
if (IS_ERR(new_ioas))
476+
return PTR_ERR(new_ioas);
477+
478+
rc = iopt_add_access(&new_ioas->iopt, access);
479+
if (rc) {
480+
iommufd_put_object(&new_ioas->obj);
481+
return rc;
482+
}
483+
iommufd_ref_to_users(&new_ioas->obj);
484+
485+
access->ioas = new_ioas;
486+
return 0;
487+
}
488+
EXPORT_SYMBOL_NS_GPL(iommufd_access_attach, IOMMUFD);
489+
497490
/**
498491
* iommufd_access_notify_unmap - Notify users of an iopt to stop using it
499492
* @iopt: iopt to work on
@@ -726,41 +719,3 @@ int iommufd_access_rw(struct iommufd_access *access, unsigned long iova,
726719
return rc;
727720
}
728721
EXPORT_SYMBOL_NS_GPL(iommufd_access_rw, IOMMUFD);
729-
730-
#ifdef CONFIG_IOMMUFD_TEST
731-
/*
732-
* Creating a real iommufd_device is too hard, bypass creating a iommufd_device
733-
* and go directly to attaching a domain.
734-
*/
735-
struct iommufd_hw_pagetable *
736-
iommufd_device_selftest_attach(struct iommufd_ctx *ictx,
737-
struct iommufd_ioas *ioas,
738-
struct device *mock_dev)
739-
{
740-
struct iommufd_hw_pagetable *hwpt;
741-
int rc;
742-
743-
hwpt = iommufd_hw_pagetable_alloc(ictx, ioas, mock_dev);
744-
if (IS_ERR(hwpt))
745-
return hwpt;
746-
747-
rc = iopt_table_add_domain(&hwpt->ioas->iopt, hwpt->domain);
748-
if (rc)
749-
goto out_hwpt;
750-
751-
refcount_inc(&hwpt->obj.users);
752-
iommufd_object_finalize(ictx, &hwpt->obj);
753-
return hwpt;
754-
755-
out_hwpt:
756-
iommufd_object_abort_and_destroy(ictx, &hwpt->obj);
757-
return ERR_PTR(rc);
758-
}
759-
760-
void iommufd_device_selftest_detach(struct iommufd_ctx *ictx,
761-
struct iommufd_hw_pagetable *hwpt)
762-
{
763-
iopt_table_remove_domain(&hwpt->ioas->iopt, hwpt->domain);
764-
refcount_dec(&hwpt->obj.users);
765-
}
766-
#endif

0 commit comments

Comments
 (0)