Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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>

authored by

Yi Liu and committed by
Jason Gunthorpe
8a9e1e77 ada14b9f

+114 -4
+3
drivers/iommu/iommu-priv.h
··· 56 56 } 57 57 #endif /* CONFIG_IOMMUFD_DRIVER_CORE && CONFIG_IRQ_MSI_IOMMU */ 58 58 59 + int iommu_replace_device_pasid(struct iommu_domain *domain, 60 + struct device *dev, ioasid_t pasid, 61 + struct iommu_attach_handle *handle); 59 62 #endif /* __LINUX_IOMMU_PRIV_H */
+111 -4
drivers/iommu/iommu.c
··· 514 514 dev_iommu_free(dev); 515 515 } 516 516 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 + 517 524 DEFINE_MUTEX(iommu_probe_device_lock); 518 525 519 526 static int __iommu_probe_device(struct device *dev, struct list_head *group_list) ··· 3331 3324 } 3332 3325 3333 3326 static int __iommu_set_group_pasid(struct iommu_domain *domain, 3334 - struct iommu_group *group, ioasid_t pasid) 3327 + struct iommu_group *group, ioasid_t pasid, 3328 + struct iommu_domain *old) 3335 3329 { 3336 3330 struct group_device *device, *last_gdev; 3337 3331 int ret; 3338 3332 3339 3333 for_each_group_device(group, device) { 3340 3334 ret = domain->ops->set_dev_pasid(domain, device->dev, 3341 - pasid, NULL); 3335 + pasid, old); 3342 3336 if (ret) 3343 3337 goto err_revert; 3344 3338 } ··· 3351 3343 for_each_group_device(group, device) { 3352 3344 if (device == last_gdev) 3353 3345 break; 3354 - iommu_remove_dev_pasid(device->dev, pasid, domain); 3346 + /* 3347 + * If no old domain, undo the succeeded devices/pasid. 3348 + * Otherwise, rollback the succeeded devices/pasid to the old 3349 + * domain. And it is a driver bug to fail attaching with a 3350 + * previously good domain. 3351 + */ 3352 + if (!old || WARN_ON(old->ops->set_dev_pasid(old, device->dev, 3353 + pasid, domain))) 3354 + iommu_remove_dev_pasid(device->dev, pasid, domain); 3355 3355 } 3356 3356 return ret; 3357 3357 } ··· 3428 3412 if (ret) 3429 3413 goto out_unlock; 3430 3414 3431 - ret = __iommu_set_group_pasid(domain, group, pasid); 3415 + ret = __iommu_set_group_pasid(domain, group, pasid, NULL); 3432 3416 if (ret) { 3433 3417 xa_release(&group->pasid_array, pasid); 3434 3418 goto out_unlock; ··· 3448 3432 return ret; 3449 3433 } 3450 3434 EXPORT_SYMBOL_GPL(iommu_attach_device_pasid); 3435 + 3436 + /** 3437 + * iommu_replace_device_pasid - Replace the domain that a specific pasid 3438 + * of the device is attached to 3439 + * @domain: the new iommu domain 3440 + * @dev: the attached device. 3441 + * @pasid: the pasid of the device. 3442 + * @handle: the attach handle. 3443 + * 3444 + * This API allows the pasid to switch domains. The @pasid should have been 3445 + * attached. Otherwise, this fails. The pasid will keep the old configuration 3446 + * if replacement failed. 3447 + * 3448 + * Caller should always provide a new handle to avoid race with the paths 3449 + * that have lockless reference to handle if it intends to pass a valid handle. 3450 + * 3451 + * Return 0 on success, or an error. 3452 + */ 3453 + int iommu_replace_device_pasid(struct iommu_domain *domain, 3454 + struct device *dev, ioasid_t pasid, 3455 + struct iommu_attach_handle *handle) 3456 + { 3457 + /* Caller must be a probed driver on dev */ 3458 + struct iommu_group *group = dev->iommu_group; 3459 + struct iommu_attach_handle *entry; 3460 + struct iommu_domain *curr_domain; 3461 + void *curr; 3462 + int ret; 3463 + 3464 + if (!group) 3465 + return -ENODEV; 3466 + 3467 + if (!domain->ops->set_dev_pasid) 3468 + return -EOPNOTSUPP; 3469 + 3470 + if (dev_iommu_ops(dev) != domain->owner || 3471 + pasid == IOMMU_NO_PASID || !handle) 3472 + return -EINVAL; 3473 + 3474 + mutex_lock(&group->mutex); 3475 + entry = iommu_make_pasid_array_entry(domain, handle); 3476 + curr = xa_cmpxchg(&group->pasid_array, pasid, NULL, 3477 + XA_ZERO_ENTRY, GFP_KERNEL); 3478 + if (xa_is_err(curr)) { 3479 + ret = xa_err(curr); 3480 + goto out_unlock; 3481 + } 3482 + 3483 + /* 3484 + * No domain (with or without handle) attached, hence not 3485 + * a replace case. 3486 + */ 3487 + if (!curr) { 3488 + xa_release(&group->pasid_array, pasid); 3489 + ret = -EINVAL; 3490 + goto out_unlock; 3491 + } 3492 + 3493 + /* 3494 + * Reusing handle is problematic as there are paths that refers 3495 + * the handle without lock. To avoid race, reject the callers that 3496 + * attempt it. 3497 + */ 3498 + if (curr == entry) { 3499 + WARN_ON(1); 3500 + ret = -EINVAL; 3501 + goto out_unlock; 3502 + } 3503 + 3504 + curr_domain = pasid_array_entry_to_domain(curr); 3505 + ret = 0; 3506 + 3507 + if (curr_domain != domain) { 3508 + ret = __iommu_set_group_pasid(domain, group, 3509 + pasid, curr_domain); 3510 + if (ret) 3511 + goto out_unlock; 3512 + } 3513 + 3514 + /* 3515 + * The above xa_cmpxchg() reserved the memory, and the 3516 + * group->mutex is held, this cannot fail. 3517 + */ 3518 + WARN_ON(xa_is_err(xa_store(&group->pasid_array, 3519 + pasid, entry, GFP_KERNEL))); 3520 + 3521 + out_unlock: 3522 + mutex_unlock(&group->mutex); 3523 + return ret; 3524 + } 3525 + EXPORT_SYMBOL_NS_GPL(iommu_replace_device_pasid, "IOMMUFD_INTERNAL"); 3451 3526 3452 3527 /* 3453 3528 * iommu_detach_device_pasid() - Detach the domain from pasid of device