Skip to content

Commit ea2d612

Browse files
committed
iommufd: Reorganize iommufd_device_attach into iommufd_device_change_pt
The code flow for first time attaching a PT and replacing a PT is very similar except for the lowest do_attach step. Reorganize this so that the do_attach step is a function pointer. Replace requires destroying the old HWPT once it is replaced. This destruction cannot be done under all the locks that are held in the function pointer, so the signature allows returning a HWPT which will be destroyed by the caller after everything is unlocked. Link: https://lore.kernel.org/r/12-v8-6659224517ea+532-iommufd_alloc_jgg@nvidia.com Reviewed-by: Lu Baolu <baolu.lu@linux.intel.com> Reviewed-by: Kevin Tian <kevin.tian@intel.com> Tested-by: Nicolin Chen <nicolinc@nvidia.com> Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
1 parent 31422df commit ea2d612

File tree

1 file changed

+102
-39
lines changed

1 file changed

+102
-39
lines changed

drivers/iommu/iommufd/device.c

Lines changed: 102 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -396,16 +396,41 @@ iommufd_hw_pagetable_detach(struct iommufd_device *idev)
396396
return hwpt;
397397
}
398398

399+
static struct iommufd_hw_pagetable *
400+
iommufd_device_do_attach(struct iommufd_device *idev,
401+
struct iommufd_hw_pagetable *hwpt)
402+
{
403+
int rc;
404+
405+
rc = iommufd_hw_pagetable_attach(hwpt, idev);
406+
if (rc)
407+
return ERR_PTR(rc);
408+
return NULL;
409+
}
410+
411+
typedef struct iommufd_hw_pagetable *(*attach_fn)(
412+
struct iommufd_device *idev, struct iommufd_hw_pagetable *hwpt);
413+
399414
/*
400415
* When automatically managing the domains we search for a compatible domain in
401416
* the iopt and if one is found use it, otherwise create a new domain.
402417
* Automatic domain selection will never pick a manually created domain.
403418
*/
404-
static int iommufd_device_auto_get_domain(struct iommufd_device *idev,
405-
struct iommufd_ioas *ioas, u32 *pt_id)
419+
static struct iommufd_hw_pagetable *
420+
iommufd_device_auto_get_domain(struct iommufd_device *idev,
421+
struct iommufd_ioas *ioas, u32 *pt_id,
422+
attach_fn do_attach)
406423
{
424+
/*
425+
* iommufd_hw_pagetable_attach() is called by
426+
* iommufd_hw_pagetable_alloc() in immediate attachment mode, same as
427+
* iommufd_device_do_attach(). So if we are in this mode then we prefer
428+
* to use the immediate_attach path as it supports drivers that can't
429+
* directly allocate a domain.
430+
*/
431+
bool immediate_attach = do_attach == iommufd_device_do_attach;
432+
struct iommufd_hw_pagetable *destroy_hwpt;
407433
struct iommufd_hw_pagetable *hwpt;
408-
int rc;
409434

410435
/*
411436
* There is no differentiation when domains are allocated, so any domain
@@ -419,52 +444,58 @@ static int iommufd_device_auto_get_domain(struct iommufd_device *idev,
419444

420445
if (!iommufd_lock_obj(&hwpt->obj))
421446
continue;
422-
rc = iommufd_hw_pagetable_attach(hwpt, idev);
423-
iommufd_put_object(&hwpt->obj);
424-
425-
/*
426-
* -EINVAL means the domain is incompatible with the device.
427-
* Other error codes should propagate to userspace as failure.
428-
* Success means the domain is attached.
429-
*/
430-
if (rc == -EINVAL)
431-
continue;
447+
destroy_hwpt = (*do_attach)(idev, hwpt);
448+
if (IS_ERR(destroy_hwpt)) {
449+
iommufd_put_object(&hwpt->obj);
450+
/*
451+
* -EINVAL means the domain is incompatible with the
452+
* device. Other error codes should propagate to
453+
* userspace as failure. Success means the domain is
454+
* attached.
455+
*/
456+
if (PTR_ERR(destroy_hwpt) == -EINVAL)
457+
continue;
458+
goto out_unlock;
459+
}
432460
*pt_id = hwpt->obj.id;
461+
iommufd_put_object(&hwpt->obj);
433462
goto out_unlock;
434463
}
435464

436-
hwpt = iommufd_hw_pagetable_alloc(idev->ictx, ioas, idev, true);
465+
hwpt = iommufd_hw_pagetable_alloc(idev->ictx, ioas, idev,
466+
immediate_attach);
437467
if (IS_ERR(hwpt)) {
438-
rc = PTR_ERR(hwpt);
468+
destroy_hwpt = ERR_CAST(hwpt);
439469
goto out_unlock;
440470
}
471+
472+
if (!immediate_attach) {
473+
destroy_hwpt = (*do_attach)(idev, hwpt);
474+
if (IS_ERR(destroy_hwpt))
475+
goto out_abort;
476+
} else {
477+
destroy_hwpt = NULL;
478+
}
479+
441480
hwpt->auto_domain = true;
442481
*pt_id = hwpt->obj.id;
443482

444483
iommufd_object_finalize(idev->ictx, &hwpt->obj);
445484
mutex_unlock(&ioas->mutex);
446-
return 0;
485+
return destroy_hwpt;
486+
487+
out_abort:
488+
iommufd_object_abort_and_destroy(idev->ictx, &hwpt->obj);
447489
out_unlock:
448490
mutex_unlock(&ioas->mutex);
449-
return rc;
491+
return destroy_hwpt;
450492
}
451493

452-
/**
453-
* iommufd_device_attach - Connect a device from an iommu_domain
454-
* @idev: device to attach
455-
* @pt_id: Input a IOMMUFD_OBJ_IOAS, or IOMMUFD_OBJ_HW_PAGETABLE
456-
* Output the IOMMUFD_OBJ_HW_PAGETABLE ID
457-
*
458-
* This connects the device to an iommu_domain, either automatically or manually
459-
* selected. Once this completes the device could do DMA.
460-
*
461-
* The caller should return the resulting pt_id back to userspace.
462-
* This function is undone by calling iommufd_device_detach().
463-
*/
464-
int iommufd_device_attach(struct iommufd_device *idev, u32 *pt_id)
494+
static int iommufd_device_change_pt(struct iommufd_device *idev, u32 *pt_id,
495+
attach_fn do_attach)
465496
{
497+
struct iommufd_hw_pagetable *destroy_hwpt;
466498
struct iommufd_object *pt_obj;
467-
int rc;
468499

469500
pt_obj = iommufd_get_object(idev->ictx, *pt_id, IOMMUFD_OBJ_ANY);
470501
if (IS_ERR(pt_obj))
@@ -475,31 +506,63 @@ int iommufd_device_attach(struct iommufd_device *idev, u32 *pt_id)
475506
struct iommufd_hw_pagetable *hwpt =
476507
container_of(pt_obj, struct iommufd_hw_pagetable, obj);
477508

478-
rc = iommufd_hw_pagetable_attach(hwpt, idev);
479-
if (rc)
509+
destroy_hwpt = (*do_attach)(idev, hwpt);
510+
if (IS_ERR(destroy_hwpt))
480511
goto out_put_pt_obj;
481512
break;
482513
}
483514
case IOMMUFD_OBJ_IOAS: {
484515
struct iommufd_ioas *ioas =
485516
container_of(pt_obj, struct iommufd_ioas, obj);
486517

487-
rc = iommufd_device_auto_get_domain(idev, ioas, pt_id);
488-
if (rc)
518+
destroy_hwpt = iommufd_device_auto_get_domain(idev, ioas, pt_id,
519+
do_attach);
520+
if (IS_ERR(destroy_hwpt))
489521
goto out_put_pt_obj;
490522
break;
491523
}
492524
default:
493-
rc = -EINVAL;
525+
destroy_hwpt = ERR_PTR(-EINVAL);
494526
goto out_put_pt_obj;
495527
}
528+
iommufd_put_object(pt_obj);
496529

497-
refcount_inc(&idev->obj.users);
498-
rc = 0;
530+
/* This destruction has to be after we unlock everything */
531+
if (destroy_hwpt)
532+
iommufd_hw_pagetable_put(idev->ictx, destroy_hwpt);
533+
return 0;
499534

500535
out_put_pt_obj:
501536
iommufd_put_object(pt_obj);
502-
return rc;
537+
return PTR_ERR(destroy_hwpt);
538+
}
539+
540+
/**
541+
* iommufd_device_attach - Connect a device to an iommu_domain
542+
* @idev: device to attach
543+
* @pt_id: Input a IOMMUFD_OBJ_IOAS, or IOMMUFD_OBJ_HW_PAGETABLE
544+
* Output the IOMMUFD_OBJ_HW_PAGETABLE ID
545+
*
546+
* This connects the device to an iommu_domain, either automatically or manually
547+
* selected. Once this completes the device could do DMA.
548+
*
549+
* The caller should return the resulting pt_id back to userspace.
550+
* This function is undone by calling iommufd_device_detach().
551+
*/
552+
int iommufd_device_attach(struct iommufd_device *idev, u32 *pt_id)
553+
{
554+
int rc;
555+
556+
rc = iommufd_device_change_pt(idev, pt_id, &iommufd_device_do_attach);
557+
if (rc)
558+
return rc;
559+
560+
/*
561+
* Pairs with iommufd_device_detach() - catches caller bugs attempting
562+
* to destroy a device with an attachment.
563+
*/
564+
refcount_inc(&idev->obj.users);
565+
return 0;
503566
}
504567
EXPORT_SYMBOL_NS_GPL(iommufd_device_attach, IOMMUFD);
505568

0 commit comments

Comments
 (0)