@@ -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+
517524DEFINE_MUTEX (iommu_probe_device_lock );
518525
519526static 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
33263333static 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}
34343450EXPORT_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