Skip to content

Commit e88d4ec

Browse files
committed
iommufd: Add iommufd_device_replace()
Replace allows all the devices in a group to move in one step to a new HWPT. Further, the HWPT move is done without going through a blocking domain so that the IOMMU driver can implement some level of non-distruption to ongoing DMA if that has meaning for it (eg for future special driver domains) Replace uses a lot of the same logic as normal attach, except the actual domain change over has different restrictions, and we are careful to sequence things so that failure is going to leave everything the way it was, and not get trapped in a blocking domain or something if there is ENOMEM. Link: https://lore.kernel.org/r/14-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 addb665 commit e88d4ec

File tree

2 files changed

+102
-0
lines changed

2 files changed

+102
-0
lines changed

drivers/iommu/iommufd/device.c

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include <linux/iommufd.h>
55
#include <linux/slab.h>
66
#include <linux/iommu.h>
7+
#include "../iommu-priv.h"
78

89
#include "io_pagetable.h"
910
#include "iommufd_private.h"
@@ -408,6 +409,84 @@ iommufd_device_do_attach(struct iommufd_device *idev,
408409
return NULL;
409410
}
410411

412+
static struct iommufd_hw_pagetable *
413+
iommufd_device_do_replace(struct iommufd_device *idev,
414+
struct iommufd_hw_pagetable *hwpt)
415+
{
416+
struct iommufd_group *igroup = idev->igroup;
417+
struct iommufd_hw_pagetable *old_hwpt;
418+
unsigned int num_devices = 0;
419+
struct iommufd_device *cur;
420+
int rc;
421+
422+
mutex_lock(&idev->igroup->lock);
423+
424+
if (igroup->hwpt == NULL) {
425+
rc = -EINVAL;
426+
goto err_unlock;
427+
}
428+
429+
if (hwpt == igroup->hwpt) {
430+
mutex_unlock(&idev->igroup->lock);
431+
return NULL;
432+
}
433+
434+
/* Try to upgrade the domain we have */
435+
list_for_each_entry(cur, &igroup->device_list, group_item) {
436+
num_devices++;
437+
if (cur->enforce_cache_coherency) {
438+
rc = iommufd_hw_pagetable_enforce_cc(hwpt);
439+
if (rc)
440+
goto err_unlock;
441+
}
442+
}
443+
444+
old_hwpt = igroup->hwpt;
445+
if (hwpt->ioas != old_hwpt->ioas) {
446+
list_for_each_entry(cur, &igroup->device_list, group_item) {
447+
rc = iopt_table_enforce_dev_resv_regions(
448+
&hwpt->ioas->iopt, cur->dev, NULL);
449+
if (rc)
450+
goto err_unresv;
451+
}
452+
}
453+
454+
rc = iommufd_group_setup_msi(idev->igroup, hwpt);
455+
if (rc)
456+
goto err_unresv;
457+
458+
rc = iommu_group_replace_domain(igroup->group, hwpt->domain);
459+
if (rc)
460+
goto err_unresv;
461+
462+
if (hwpt->ioas != old_hwpt->ioas) {
463+
list_for_each_entry(cur, &igroup->device_list, group_item)
464+
iopt_remove_reserved_iova(&old_hwpt->ioas->iopt,
465+
cur->dev);
466+
}
467+
468+
igroup->hwpt = hwpt;
469+
470+
/*
471+
* Move the refcounts held by the device_list to the new hwpt. Retain a
472+
* refcount for this thread as the caller will free it.
473+
*/
474+
refcount_add(num_devices, &hwpt->obj.users);
475+
if (num_devices > 1)
476+
WARN_ON(refcount_sub_and_test(num_devices - 1,
477+
&old_hwpt->obj.users));
478+
mutex_unlock(&idev->igroup->lock);
479+
480+
/* Caller must destroy old_hwpt */
481+
return old_hwpt;
482+
err_unresv:
483+
list_for_each_entry(cur, &igroup->device_list, group_item)
484+
iopt_remove_reserved_iova(&hwpt->ioas->iopt, cur->dev);
485+
err_unlock:
486+
mutex_unlock(&idev->igroup->lock);
487+
return ERR_PTR(rc);
488+
}
489+
411490
typedef struct iommufd_hw_pagetable *(*attach_fn)(
412491
struct iommufd_device *idev, struct iommufd_hw_pagetable *hwpt);
413492

@@ -566,6 +645,28 @@ int iommufd_device_attach(struct iommufd_device *idev, u32 *pt_id)
566645
}
567646
EXPORT_SYMBOL_NS_GPL(iommufd_device_attach, IOMMUFD);
568647

648+
/**
649+
* iommufd_device_replace - Change the device's iommu_domain
650+
* @idev: device to change
651+
* @pt_id: Input a IOMMUFD_OBJ_IOAS, or IOMMUFD_OBJ_HW_PAGETABLE
652+
* Output the IOMMUFD_OBJ_HW_PAGETABLE ID
653+
*
654+
* This is the same as::
655+
*
656+
* iommufd_device_detach();
657+
* iommufd_device_attach();
658+
*
659+
* If it fails then no change is made to the attachment. The iommu driver may
660+
* implement this so there is no disruption in translation. This can only be
661+
* called if iommufd_device_attach() has already succeeded.
662+
*/
663+
int iommufd_device_replace(struct iommufd_device *idev, u32 *pt_id)
664+
{
665+
return iommufd_device_change_pt(idev, pt_id,
666+
&iommufd_device_do_replace);
667+
}
668+
EXPORT_SYMBOL_NS_GPL(iommufd_device_replace, IOMMUFD);
669+
569670
/**
570671
* iommufd_device_detach - Disconnect a device to an iommu_domain
571672
* @idev: device to detach

drivers/iommu/iommufd/main.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -490,5 +490,6 @@ module_exit(iommufd_exit);
490490
MODULE_ALIAS_MISCDEV(VFIO_MINOR);
491491
MODULE_ALIAS("devname:vfio/vfio");
492492
#endif
493+
MODULE_IMPORT_NS(IOMMUFD_INTERNAL);
493494
MODULE_DESCRIPTION("I/O Address Space Management for passthrough devices");
494495
MODULE_LICENSE("GPL");

0 commit comments

Comments
 (0)