diff --git a/drivers/iommu/iommu-sva.c b/drivers/iommu/iommu-sva.c index 8b4afb7c63aedf06d4b1bf8fd07999a54785acf0..8d98f9c09864a86069c28a139a8534126b769161 100644 --- a/drivers/iommu/iommu-sva.c +++ b/drivers/iommu/iommu-sva.c @@ -93,6 +93,8 @@ int iommu_sva_device_shutdown(struct device *dev) if (!domain) return -ENODEV; + __iommu_sva_unbind_dev_all(dev); + mutex_lock(&dev->iommu_param->lock); param = dev->iommu_param->sva_param; dev->iommu_param->sva_param = NULL; @@ -108,3 +110,28 @@ int iommu_sva_device_shutdown(struct device *dev) return 0; } EXPORT_SYMBOL_GPL(iommu_sva_device_shutdown); + +int __iommu_sva_bind_device(struct device *dev, struct mm_struct *mm, + int *pasid, unsigned long flags, void *drvdata) +{ + return -ENOSYS; /* TODO */ +} +EXPORT_SYMBOL_GPL(__iommu_sva_bind_device); + +int __iommu_sva_unbind_device(struct device *dev, int pasid) +{ + return -ENOSYS; /* TODO */ +} +EXPORT_SYMBOL_GPL(__iommu_sva_unbind_device); + +/** + * __iommu_sva_unbind_dev_all() - Detach all address spaces from this device + * @dev: the device + * + * When detaching @device from a domain, IOMMU drivers should use this helper. + */ +void __iommu_sva_unbind_dev_all(struct device *dev) +{ + /* TODO */ +} +EXPORT_SYMBOL_GPL(__iommu_sva_unbind_dev_all); diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index 92f3840d9f3835f4de07ff2be0637e76f1ac2d6b..28c68c8af57e37da671c763ed8a02f06d5b7d186 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -2348,3 +2348,86 @@ int iommu_fwspec_add_ids(struct device *dev, u32 *ids, int num_ids) return 0; } EXPORT_SYMBOL_GPL(iommu_fwspec_add_ids); + +/** + * iommu_sva_bind_device() - Bind a process address space to a device + * @dev: the device + * @mm: the mm to bind, caller must hold a reference to it + * @pasid: valid address where the PASID will be stored + * @flags: bond properties + * @drvdata: private data passed to the mm exit handler + * + * Create a bond between device and task, allowing the device to access the mm + * using the returned PASID. If unbind() isn't called first, a subsequent bind() + * for the same device and mm fails with -EEXIST. + * + * iommu_sva_device_init() must be called first, to initialize the required SVA + * features. @flags is a subset of these features. + * + * The caller must pin down using get_user_pages*() all mappings shared with the + * device. mlock() isn't sufficient, as it doesn't prevent minor page faults + * (e.g. copy-on-write). + * + * On success, 0 is returned and @pasid contains a valid ID. Otherwise, an error + * is returned. + */ +int iommu_sva_bind_device(struct device *dev, struct mm_struct *mm, int *pasid, + unsigned long flags, void *drvdata) +{ + int ret = -EINVAL; + struct iommu_group *group; + + if (!pasid) + return -EINVAL; + + group = iommu_group_get(dev); + if (!group) + return -ENODEV; + + /* Ensure device count and domain don't change while we're binding */ + mutex_lock(&group->mutex); + if (iommu_group_device_count(group) != 1) + goto out_unlock; + + ret = __iommu_sva_bind_device(dev, mm, pasid, flags, drvdata); + +out_unlock: + mutex_unlock(&group->mutex); + iommu_group_put(group); + + return ret; +} +EXPORT_SYMBOL_GPL(iommu_sva_bind_device); + +/** + * iommu_sva_unbind_device() - Remove a bond created with iommu_sva_bind_device + * @dev: the device + * @pasid: the pasid returned by bind() + * + * Remove bond between device and address space identified by @pasid. Users + * should not call unbind() if the corresponding mm exited (as the PASID might + * have been reallocated for another process). + * + * The device must not be issuing any more transaction for this PASID. All + * outstanding page requests for this PASID must have been flushed to the IOMMU. + * + * Returns 0 on success, or an error value + */ +int iommu_sva_unbind_device(struct device *dev, int pasid) +{ + int ret = -EINVAL; + struct iommu_group *group; + + group = iommu_group_get(dev); + if (!group) + return -ENODEV; + + mutex_lock(&group->mutex); + ret = __iommu_sva_unbind_device(dev, pasid); + mutex_unlock(&group->mutex); + + iommu_group_put(group); + + return ret; +} +EXPORT_SYMBOL_GPL(iommu_sva_unbind_device); diff --git a/include/linux/iommu.h b/include/linux/iommu.h index fbf84d25849b9b7ad33a6abb1a819f3d21f54612..57ebf82d69fb0dbc4b32b4a63afefe74a4082127 100644 --- a/include/linux/iommu.h +++ b/include/linux/iommu.h @@ -607,6 +607,10 @@ void iommu_fwspec_free(struct device *dev); int iommu_fwspec_add_ids(struct device *dev, u32 *ids, int num_ids); const struct iommu_ops *iommu_ops_from_fwnode(struct fwnode_handle *fwnode); +extern int iommu_sva_bind_device(struct device *dev, struct mm_struct *mm, + int *pasid, unsigned long flags, void *drvdata); +extern int iommu_sva_unbind_device(struct device *dev, int pasid); + #else /* CONFIG_IOMMU_API */ struct iommu_ops {}; @@ -940,12 +944,29 @@ static inline int iommu_sva_invalidate(struct iommu_domain *domain, return -EINVAL; } +static inline int iommu_sva_bind_device(struct device *dev, + struct mm_struct *mm, int *pasid, + unsigned long flags, void *drvdata) +{ + return -ENODEV; +} + +static inline int iommu_sva_unbind_device(struct device *dev, int pasid) +{ + return -ENODEV; +} + #endif /* CONFIG_IOMMU_API */ #ifdef CONFIG_IOMMU_SVA extern int iommu_sva_device_init(struct device *dev, unsigned long features, unsigned int max_pasid); extern int iommu_sva_device_shutdown(struct device *dev); +extern int __iommu_sva_bind_device(struct device *dev, struct mm_struct *mm, + int *pasid, unsigned long flags, + void *drvdata); +extern int __iommu_sva_unbind_device(struct device *dev, int pasid); +extern void __iommu_sva_unbind_dev_all(struct device *dev); #else /* CONFIG_IOMMU_SVA */ static inline int iommu_sva_device_init(struct device *dev, unsigned long features, @@ -958,6 +979,22 @@ static inline int iommu_sva_device_shutdown(struct device *dev) { return -ENODEV; } + +static inline int __iommu_sva_bind_device(struct device *dev, + struct mm_struct *mm, int *pasid, + unsigned long flags, void *drvdata) +{ + return -ENODEV; +} + +static inline int __iommu_sva_unbind_device(struct device *dev, int pasid) +{ + return -ENODEV; +} + +static inline void __iommu_sva_unbind_dev_all(struct device *dev) +{ +} #endif /* CONFIG_IOMMU_SVA */ #ifdef CONFIG_IOMMU_DEBUGFS