Re: [PATCH RFC v2 04/11] iommu: Add attach/detach_dev_pasid domain ops
Hi Jason, On 2022/3/31 3:08, Jason Gunthorpe wrote: On Tue, Mar 29, 2022 at 01:37:53PM +0800, Lu Baolu wrote: Attaching an IOMMU domain to a PASID of a device is a generic operation for modern IOMMU drivers which support PASID-granular DMA address translation. Currently visible usage scenarios include (but not limited): - SVA (Shared Virtual Address) - kernel DMA with PASID - hardware-assist mediated device This adds a pair of common domain ops for this purpose and adds some common helpers to attach/detach a domain to/from a {device, PASID} and get/put the domain attached to {device, PASID}. Signed-off-by: Lu Baolu include/linux/iommu.h | 36 ++ drivers/iommu/iommu.c | 88 +++ 2 files changed, 124 insertions(+) diff --git a/include/linux/iommu.h b/include/linux/iommu.h index 29c4c2edd706..a46285488a57 100644 +++ b/include/linux/iommu.h @@ -269,6 +269,8 @@ struct iommu_ops { * struct iommu_domain_ops - domain specific operations * @attach_dev: attach an iommu domain to a device * @detach_dev: detach an iommu domain from a device + * @attach_dev_pasid: attach an iommu domain to a pasid of device + * @detach_dev_pasid: detach an iommu domain from a pasid of device * @map: map a physically contiguous memory region to an iommu domain * @map_pages: map a physically contiguous set of pages of the same size to * an iommu domain. @@ -286,6 +288,10 @@ struct iommu_ops { struct iommu_domain_ops { int (*attach_dev)(struct iommu_domain *domain, struct device *dev); void (*detach_dev)(struct iommu_domain *domain, struct device *dev); + int (*attach_dev_pasid)(struct iommu_domain *domain, + struct device *dev, ioasid_t id); + void (*detach_dev_pasid)(struct iommu_domain *domain, +struct device *dev, ioasid_t id); ID should be pasid for consistency Sure. +int iommu_attach_device_pasid(struct iommu_domain *domain, + struct device *dev, ioasid_t pasid) +{ + struct iommu_group *group; + int ret = -EINVAL; + void *curr; + + if (!domain->ops->attach_dev_pasid) + return -EINVAL; + + group = iommu_group_get(dev); + if (!group) + return -ENODEV; + + mutex_lock(>mutex); + /* +* To keep things simple, we currently don't support IOMMU groups +* with more than one device. Existing SVA-capable systems are not +* affected by the problems that required IOMMU groups (lack of ACS +* isolation, device ID aliasing and other hardware issues). +*/ + if (!iommu_group_singleton_lockdown(group)) + goto out_unlock; + + xa_lock(>pasid_array); + curr = __xa_cmpxchg(>pasid_array, pasid, NULL, + domain, GFP_KERNEL); + xa_unlock(>pasid_array); Why the xa_lock/unlock? Just call the normal xa_cmpxchg? I should use xa_cmpxchg() instead. +void iommu_detach_device_pasid(struct iommu_domain *domain, + struct device *dev, ioasid_t pasid) +{ + struct iommu_group *group; + + group = iommu_group_get(dev); + if (WARN_ON(!group)) + return; This group_get stuff really needs some cleaning, this makes no sense at all. If the kref to group can go to zero within this function then the initial access of the kref is already buggy: if (group) kobject_get(group->devices_kobj); Because it will crash or WARN_ON. We don't hit this because it is required that a group cannot be destroyed while a struct device has a driver bound, and all these paths are driver bound paths. So none of these group_get/put patterns have any purpose at all, and now we are adding impossible WARN_ONs to.. The original intention of this check is that the helper is called on the correct device. I agree that WARN_ON() is unnecessary because NULL pointer reference will be caught automatically. +struct iommu_domain * +iommu_get_domain_for_dev_pasid(struct device *dev, ioasid_t pasid) +{ + struct iommu_domain *domain; + struct iommu_group *group; + + group = iommu_group_get(dev); + if (!group) + return NULL; And now we are doing useless things on a performance path! Agreed. + mutex_lock(>mutex); + domain = xa_load(>pasid_array, pasid); + if (domain && domain->type == IOMMU_DOMAIN_SVA) + iommu_sva_domain_get_user(domain); + mutex_unlock(>mutex); + iommu_group_put(group); Why do we need so much locking on a performance path? RCU out of the xarray.. Not sure I see how this get_user refcounting can work ? I should move the refcountering things to iommu_domain and make the change easier for review. Will improve this in the new version. Jason Best regards, baolu
Re: [PATCH RFC v2 04/11] iommu: Add attach/detach_dev_pasid domain ops
On Tue, Mar 29, 2022 at 01:37:53PM +0800, Lu Baolu wrote: > Attaching an IOMMU domain to a PASID of a device is a generic operation > for modern IOMMU drivers which support PASID-granular DMA address > translation. Currently visible usage scenarios include (but not limited): > > - SVA (Shared Virtual Address) > - kernel DMA with PASID > - hardware-assist mediated device > > This adds a pair of common domain ops for this purpose and adds some > common helpers to attach/detach a domain to/from a {device, PASID} and > get/put the domain attached to {device, PASID}. > > Signed-off-by: Lu Baolu > include/linux/iommu.h | 36 ++ > drivers/iommu/iommu.c | 88 +++ > 2 files changed, 124 insertions(+) > > diff --git a/include/linux/iommu.h b/include/linux/iommu.h > index 29c4c2edd706..a46285488a57 100644 > +++ b/include/linux/iommu.h > @@ -269,6 +269,8 @@ struct iommu_ops { > * struct iommu_domain_ops - domain specific operations > * @attach_dev: attach an iommu domain to a device > * @detach_dev: detach an iommu domain from a device > + * @attach_dev_pasid: attach an iommu domain to a pasid of device > + * @detach_dev_pasid: detach an iommu domain from a pasid of device > * @map: map a physically contiguous memory region to an iommu domain > * @map_pages: map a physically contiguous set of pages of the same size to > * an iommu domain. > @@ -286,6 +288,10 @@ struct iommu_ops { > struct iommu_domain_ops { > int (*attach_dev)(struct iommu_domain *domain, struct device *dev); > void (*detach_dev)(struct iommu_domain *domain, struct device *dev); > + int (*attach_dev_pasid)(struct iommu_domain *domain, > + struct device *dev, ioasid_t id); > + void (*detach_dev_pasid)(struct iommu_domain *domain, > + struct device *dev, ioasid_t id); ID should be pasid for consistency > +int iommu_attach_device_pasid(struct iommu_domain *domain, > + struct device *dev, ioasid_t pasid) > +{ > + struct iommu_group *group; > + int ret = -EINVAL; > + void *curr; > + > + if (!domain->ops->attach_dev_pasid) > + return -EINVAL; > + > + group = iommu_group_get(dev); > + if (!group) > + return -ENODEV; > + > + mutex_lock(>mutex); > + /* > + * To keep things simple, we currently don't support IOMMU groups > + * with more than one device. Existing SVA-capable systems are not > + * affected by the problems that required IOMMU groups (lack of ACS > + * isolation, device ID aliasing and other hardware issues). > + */ > + if (!iommu_group_singleton_lockdown(group)) > + goto out_unlock; > + > + xa_lock(>pasid_array); > + curr = __xa_cmpxchg(>pasid_array, pasid, NULL, > + domain, GFP_KERNEL); > + xa_unlock(>pasid_array); Why the xa_lock/unlock? Just call the normal xa_cmpxchg? > +void iommu_detach_device_pasid(struct iommu_domain *domain, > +struct device *dev, ioasid_t pasid) > +{ > + struct iommu_group *group; > + > + group = iommu_group_get(dev); > + if (WARN_ON(!group)) > + return; This group_get stuff really needs some cleaning, this makes no sense at all. If the kref to group can go to zero within this function then the initial access of the kref is already buggy: if (group) kobject_get(group->devices_kobj); Because it will crash or WARN_ON. We don't hit this because it is required that a group cannot be destroyed while a struct device has a driver bound, and all these paths are driver bound paths. So none of these group_get/put patterns have any purpose at all, and now we are adding impossible WARN_ONs to.. > +struct iommu_domain * > +iommu_get_domain_for_dev_pasid(struct device *dev, ioasid_t pasid) > +{ > + struct iommu_domain *domain; > + struct iommu_group *group; > + > + group = iommu_group_get(dev); > + if (!group) > + return NULL; And now we are doing useless things on a performance path! > + mutex_lock(>mutex); > + domain = xa_load(>pasid_array, pasid); > + if (domain && domain->type == IOMMU_DOMAIN_SVA) > + iommu_sva_domain_get_user(domain); > + mutex_unlock(>mutex); > + iommu_group_put(group); Why do we need so much locking on a performance path? RCU out of the xarray.. Not sure I see how this get_user refcounting can work ? Jason ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCH RFC v2 04/11] iommu: Add attach/detach_dev_pasid domain ops
Attaching an IOMMU domain to a PASID of a device is a generic operation for modern IOMMU drivers which support PASID-granular DMA address translation. Currently visible usage scenarios include (but not limited): - SVA (Shared Virtual Address) - kernel DMA with PASID - hardware-assist mediated device This adds a pair of common domain ops for this purpose and adds some common helpers to attach/detach a domain to/from a {device, PASID} and get/put the domain attached to {device, PASID}. Signed-off-by: Lu Baolu --- include/linux/iommu.h | 36 ++ drivers/iommu/iommu.c | 88 +++ 2 files changed, 124 insertions(+) diff --git a/include/linux/iommu.h b/include/linux/iommu.h index 29c4c2edd706..a46285488a57 100644 --- a/include/linux/iommu.h +++ b/include/linux/iommu.h @@ -269,6 +269,8 @@ struct iommu_ops { * struct iommu_domain_ops - domain specific operations * @attach_dev: attach an iommu domain to a device * @detach_dev: detach an iommu domain from a device + * @attach_dev_pasid: attach an iommu domain to a pasid of device + * @detach_dev_pasid: detach an iommu domain from a pasid of device * @map: map a physically contiguous memory region to an iommu domain * @map_pages: map a physically contiguous set of pages of the same size to * an iommu domain. @@ -286,6 +288,10 @@ struct iommu_ops { struct iommu_domain_ops { int (*attach_dev)(struct iommu_domain *domain, struct device *dev); void (*detach_dev)(struct iommu_domain *domain, struct device *dev); + int (*attach_dev_pasid)(struct iommu_domain *domain, + struct device *dev, ioasid_t id); + void (*detach_dev_pasid)(struct iommu_domain *domain, +struct device *dev, ioasid_t id); int (*map)(struct iommu_domain *domain, unsigned long iova, phys_addr_t paddr, size_t size, int prot, gfp_t gfp); @@ -679,6 +685,14 @@ int iommu_group_claim_dma_owner(struct iommu_group *group, void *owner); void iommu_group_release_dma_owner(struct iommu_group *group); bool iommu_group_dma_owner_claimed(struct iommu_group *group); +int iommu_attach_device_pasid(struct iommu_domain *domain, + struct device *dev, ioasid_t pasid); +void iommu_detach_device_pasid(struct iommu_domain *domain, + struct device *dev, ioasid_t pasid); +struct iommu_domain * +iommu_get_domain_for_dev_pasid(struct device *dev, ioasid_t pasid); +void iommu_put_domain_for_dev_pasid(struct iommu_domain *domain); + #else /* CONFIG_IOMMU_API */ struct iommu_ops {}; @@ -1047,6 +1061,28 @@ static inline bool iommu_group_dma_owner_claimed(struct iommu_group *group) { return false; } + +static inline int iommu_attach_device_pasid(struct iommu_domain *domain, + struct device *dev, ioasid_t pasid) +{ + return -ENODEV; +} + +static inline void iommu_detach_device_pasid(struct iommu_domain *domain, +struct device *dev, ioasid_t pasid) +{ +} + +static inline struct iommu_domain * +iommu_get_domain_for_dev_pasid(struct device *dev, ioasid_t pasid) +{ + return NULL; +} + +static inline void +iommu_put_domain_for_dev_pasid(struct iommu_domain *domain) +{ +} #endif /* CONFIG_IOMMU_API */ /** diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index 9fb8a5b4491e..8163ad7f6902 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -27,6 +27,8 @@ #include #include +#include "iommu-sva-lib.h" + static struct kset *iommu_group_kset; static DEFINE_IDA(iommu_group_ida); @@ -38,6 +40,7 @@ struct iommu_group { struct kobject kobj; struct kobject *devices_kobj; struct list_head devices; + struct xarray pasid_array; struct mutex mutex; void *iommu_data; void (*iommu_data_release)(void *iommu_data); @@ -631,6 +634,7 @@ struct iommu_group *iommu_group_alloc(void) mutex_init(>mutex); INIT_LIST_HEAD(>devices); INIT_LIST_HEAD(>entry); + xa_init(>pasid_array); ret = ida_simple_get(_group_ida, 0, 0, GFP_KERNEL); if (ret < 0) { @@ -3173,3 +3177,87 @@ bool iommu_group_dma_owner_claimed(struct iommu_group *group) return user; } EXPORT_SYMBOL_GPL(iommu_group_dma_owner_claimed); + +int iommu_attach_device_pasid(struct iommu_domain *domain, + struct device *dev, ioasid_t pasid) +{ + struct iommu_group *group; + int ret = -EINVAL; + void *curr; + + if (!domain->ops->attach_dev_pasid) + return -EINVAL; + + group = iommu_group_get(dev); + if (!group) + return -ENODEV; + + mutex_lock(>mutex); + /* +* To keep things simple, we currently don't support IOMMU groups +* with more than one device. Existing SVA-capable systems