Skip to content

Commit 8a9e1e7

Browse files
yiliu1765jgunthorpe
authored andcommitted
iommu: Introduce a replace API for device pasid
Provide a high-level API to allow replacements of one domain with another for specific pasid of a device. This is similar to iommu_replace_group_handle() and it is expected to be used only by IOMMUFD. Link: https://patch.msgid.link/r/20250321171940.7213-3-yi.l.liu@intel.com Co-developed-by: Lu Baolu <baolu.lu@linux.intel.com> Signed-off-by: Lu Baolu <baolu.lu@linux.intel.com> Reviewed-by: Jason Gunthorpe <jgg@nvidia.com> Reviewed-by: Kevin Tian <kevin.tian@intel.com> Reviewed-by: Nicolin Chen <nicolinc@nvidia.com> Signed-off-by: Yi Liu <yi.l.liu@intel.com> Tested-by: Nicolin Chen <nicolinc@nvidia.com> Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
1 parent ada14b9 commit 8a9e1e7

2 files changed

Lines changed: 114 additions & 4 deletions

File tree

drivers/iommu/iommu-priv.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,4 +56,7 @@ static inline int iommufd_sw_msi(struct iommu_domain *domain,
5656
}
5757
#endif /* CONFIG_IOMMUFD_DRIVER_CORE && CONFIG_IRQ_MSI_IOMMU */
5858

59+
int iommu_replace_device_pasid(struct iommu_domain *domain,
60+
struct device *dev, ioasid_t pasid,
61+
struct iommu_attach_handle *handle);
5962
#endif /* __LINUX_IOMMU_PRIV_H */

drivers/iommu/iommu.c

Lines changed: 111 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -514,6 +514,13 @@ static void iommu_deinit_device(struct device *dev)
514514
dev_iommu_free(dev);
515515
}
516516

517+
static struct iommu_domain *pasid_array_entry_to_domain(void *entry)
518+
{
519+
if (xa_pointer_tag(entry) == IOMMU_PASID_ARRAY_DOMAIN)
520+
return xa_untag_pointer(entry);
521+
return ((struct iommu_attach_handle *)xa_untag_pointer(entry))->domain;
522+
}
523+
517524
DEFINE_MUTEX(iommu_probe_device_lock);
518525

519526
static int __iommu_probe_device(struct device *dev, struct list_head *group_list)
@@ -3324,14 +3331,15 @@ static void iommu_remove_dev_pasid(struct device *dev, ioasid_t pasid,
33243331
}
33253332

33263333
static int __iommu_set_group_pasid(struct iommu_domain *domain,
3327-
struct iommu_group *group, ioasid_t pasid)
3334+
struct iommu_group *group, ioasid_t pasid,
3335+
struct iommu_domain *old)
33283336
{
33293337
struct group_device *device, *last_gdev;
33303338
int ret;
33313339

33323340
for_each_group_device(group, device) {
33333341
ret = domain->ops->set_dev_pasid(domain, device->dev,
3334-
pasid, NULL);
3342+
pasid, old);
33353343
if (ret)
33363344
goto err_revert;
33373345
}
@@ -3343,7 +3351,15 @@ static int __iommu_set_group_pasid(struct iommu_domain *domain,
33433351
for_each_group_device(group, device) {
33443352
if (device == last_gdev)
33453353
break;
3346-
iommu_remove_dev_pasid(device->dev, pasid, domain);
3354+
/*
3355+
* If no old domain, undo the succeeded devices/pasid.
3356+
* Otherwise, rollback the succeeded devices/pasid to the old
3357+
* domain. And it is a driver bug to fail attaching with a
3358+
* previously good domain.
3359+
*/
3360+
if (!old || WARN_ON(old->ops->set_dev_pasid(old, device->dev,
3361+
pasid, domain)))
3362+
iommu_remove_dev_pasid(device->dev, pasid, domain);
33473363
}
33483364
return ret;
33493365
}
@@ -3412,7 +3428,7 @@ int iommu_attach_device_pasid(struct iommu_domain *domain,
34123428
if (ret)
34133429
goto out_unlock;
34143430

3415-
ret = __iommu_set_group_pasid(domain, group, pasid);
3431+
ret = __iommu_set_group_pasid(domain, group, pasid, NULL);
34163432
if (ret) {
34173433
xa_release(&group->pasid_array, pasid);
34183434
goto out_unlock;
@@ -3433,6 +3449,97 @@ int iommu_attach_device_pasid(struct iommu_domain *domain,
34333449
}
34343450
EXPORT_SYMBOL_GPL(iommu_attach_device_pasid);
34353451

3452+
/**
3453+
* iommu_replace_device_pasid - Replace the domain that a specific pasid
3454+
* of the device is attached to
3455+
* @domain: the new iommu domain
3456+
* @dev: the attached device.
3457+
* @pasid: the pasid of the device.
3458+
* @handle: the attach handle.
3459+
*
3460+
* This API allows the pasid to switch domains. The @pasid should have been
3461+
* attached. Otherwise, this fails. The pasid will keep the old configuration
3462+
* if replacement failed.
3463+
*
3464+
* Caller should always provide a new handle to avoid race with the paths
3465+
* that have lockless reference to handle if it intends to pass a valid handle.
3466+
*
3467+
* Return 0 on success, or an error.
3468+
*/
3469+
int iommu_replace_device_pasid(struct iommu_domain *domain,
3470+
struct device *dev, ioasid_t pasid,
3471+
struct iommu_attach_handle *handle)
3472+
{
3473+
/* Caller must be a probed driver on dev */
3474+
struct iommu_group *group = dev->iommu_group;
3475+
struct iommu_attach_handle *entry;
3476+
struct iommu_domain *curr_domain;
3477+
void *curr;
3478+
int ret;
3479+
3480+
if (!group)
3481+
return -ENODEV;
3482+
3483+
if (!domain->ops->set_dev_pasid)
3484+
return -EOPNOTSUPP;
3485+
3486+
if (dev_iommu_ops(dev) != domain->owner ||
3487+
pasid == IOMMU_NO_PASID || !handle)
3488+
return -EINVAL;
3489+
3490+
mutex_lock(&group->mutex);
3491+
entry = iommu_make_pasid_array_entry(domain, handle);
3492+
curr = xa_cmpxchg(&group->pasid_array, pasid, NULL,
3493+
XA_ZERO_ENTRY, GFP_KERNEL);
3494+
if (xa_is_err(curr)) {
3495+
ret = xa_err(curr);
3496+
goto out_unlock;
3497+
}
3498+
3499+
/*
3500+
* No domain (with or without handle) attached, hence not
3501+
* a replace case.
3502+
*/
3503+
if (!curr) {
3504+
xa_release(&group->pasid_array, pasid);
3505+
ret = -EINVAL;
3506+
goto out_unlock;
3507+
}
3508+
3509+
/*
3510+
* Reusing handle is problematic as there are paths that refers
3511+
* the handle without lock. To avoid race, reject the callers that
3512+
* attempt it.
3513+
*/
3514+
if (curr == entry) {
3515+
WARN_ON(1);
3516+
ret = -EINVAL;
3517+
goto out_unlock;
3518+
}
3519+
3520+
curr_domain = pasid_array_entry_to_domain(curr);
3521+
ret = 0;
3522+
3523+
if (curr_domain != domain) {
3524+
ret = __iommu_set_group_pasid(domain, group,
3525+
pasid, curr_domain);
3526+
if (ret)
3527+
goto out_unlock;
3528+
}
3529+
3530+
/*
3531+
* The above xa_cmpxchg() reserved the memory, and the
3532+
* group->mutex is held, this cannot fail.
3533+
*/
3534+
WARN_ON(xa_is_err(xa_store(&group->pasid_array,
3535+
pasid, entry, GFP_KERNEL)));
3536+
3537+
out_unlock:
3538+
mutex_unlock(&group->mutex);
3539+
return ret;
3540+
}
3541+
EXPORT_SYMBOL_NS_GPL(iommu_replace_device_pasid, "IOMMUFD_INTERNAL");
3542+
34363543
/*
34373544
* iommu_detach_device_pasid() - Detach the domain from pasid of device
34383545
* @domain: the iommu domain.

0 commit comments

Comments
 (0)