2022-05-19 13:29:52

by Lu Baolu

[permalink] [raw]
Subject: [PATCH v7 03/10] iommu/sva: Add iommu_sva_domain support

The iommu_sva_domain represents a hardware pagetable that the IOMMU
hardware could use for SVA translation. This adds some infrastructure
to support SVA domain in the iommu common layer. It includes:

- Add a new struct iommu_sva_domain and new IOMMU_DOMAIN_SVA domain
type.
- Add a new domain ops pointer in iommu_ops. The IOMMU drivers that
support SVA should provide the callbacks.
- Add helpers to allocate and free an SVA domain.
- Add helpers to set an SVA domain to a device and the reverse
operation.

Some buses, like PCI, route packets without considering the PASID value.
Thus a DMA target address with PASID might be treated as P2P if the
address falls into the MMIO BAR of other devices in the group. To make
things simple, the attach/detach interfaces only apply to devices
belonging to the singleton groups, and the singleton is immutable in
fabric i.e. not affected by hotplug.

The iommu_set/block_device_pasid() can be used for other purposes,
such as kernel DMA with pasid, mediation device, etc. Hence, it is put
in the iommu.c.

Suggested-by: Jean-Philippe Brucker <[email protected]>
Suggested-by: Jason Gunthorpe <[email protected]>
Signed-off-by: Lu Baolu <[email protected]>
---
include/linux/iommu.h | 51 +++++++++++++++++++++++++
drivers/iommu/iommu-sva-lib.h | 15 ++++++++
drivers/iommu/iommu-sva-lib.c | 48 +++++++++++++++++++++++
drivers/iommu/iommu.c | 71 +++++++++++++++++++++++++++++++++++
4 files changed, 185 insertions(+)

diff --git a/include/linux/iommu.h b/include/linux/iommu.h
index 0c358b7c583b..e8cf82d46ce1 100644
--- a/include/linux/iommu.h
+++ b/include/linux/iommu.h
@@ -64,6 +64,9 @@ struct iommu_domain_geometry {
#define __IOMMU_DOMAIN_PT (1U << 2) /* Domain is identity mapped */
#define __IOMMU_DOMAIN_DMA_FQ (1U << 3) /* DMA-API uses flush queue */

+#define __IOMMU_DOMAIN_SHARED (1U << 4) /* Page table shared from CPU */
+#define __IOMMU_DOMAIN_HOST_VA (1U << 5) /* Host CPU virtual address */
+
/*
* This are the possible domain-types
*
@@ -86,6 +89,8 @@ struct iommu_domain_geometry {
#define IOMMU_DOMAIN_DMA_FQ (__IOMMU_DOMAIN_PAGING | \
__IOMMU_DOMAIN_DMA_API | \
__IOMMU_DOMAIN_DMA_FQ)
+#define IOMMU_DOMAIN_SVA (__IOMMU_DOMAIN_SHARED | \
+ __IOMMU_DOMAIN_HOST_VA)

struct iommu_domain {
unsigned type;
@@ -254,6 +259,7 @@ struct iommu_ops {
int (*def_domain_type)(struct device *dev);

const struct iommu_domain_ops *default_domain_ops;
+ const struct iommu_domain_ops *sva_domain_ops;
unsigned long pgsize_bitmap;
struct module *owner;
};
@@ -262,6 +268,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
+ * @set_dev_pasid: set an iommu domain to a pasid of device
+ * @block_dev_pasid: block pasid of device from using iommu domain
* @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.
@@ -282,6 +290,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 (*set_dev_pasid)(struct iommu_domain *domain, struct device *dev,
+ ioasid_t pasid);
+ void (*block_dev_pasid)(struct iommu_domain *domain, struct device *dev,
+ ioasid_t pasid);

int (*map)(struct iommu_domain *domain, unsigned long iova,
phys_addr_t paddr, size_t size, int prot, gfp_t gfp);
@@ -677,6 +689,10 @@ 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_set_device_pasid(struct iommu_domain *domain, struct device *dev,
+ ioasid_t pasid);
+void iommu_block_device_pasid(struct iommu_domain *domain, struct device *dev,
+ ioasid_t pasid);
#else /* CONFIG_IOMMU_API */

struct iommu_ops {};
@@ -1050,6 +1066,17 @@ static inline bool iommu_group_dma_owner_claimed(struct iommu_group *group)
{
return false;
}
+
+static inline int iommu_set_device_pasid(struct iommu_domain *domain,
+ struct device *dev, ioasid_t pasid)
+{
+ return -ENODEV;
+}
+
+static inline void iommu_block_device_pasid(struct iommu_domain *domain,
+ struct device *dev, ioasid_t pasid)
+{
+}
#endif /* CONFIG_IOMMU_API */

/**
@@ -1075,4 +1102,28 @@ void iommu_debugfs_setup(void);
static inline void iommu_debugfs_setup(void) {}
#endif

+#ifdef CONFIG_IOMMU_SVA
+struct iommu_domain *
+iommu_sva_alloc_domain(struct bus_type *bus, struct mm_struct *mm);
+void iommu_sva_free_domain(struct iommu_domain *domain);
+int iommu_sva_set_domain(struct iommu_domain *domain, struct device *dev,
+ ioasid_t pasid);
+#else /* CONFIG_IOMMU_SVA */
+static inline struct iommu_domain *
+iommu_sva_alloc_domain(struct bus_type *bus, struct mm_struct *mm)
+{
+ return ERR_PTR(-EINVAL);
+}
+
+static inline void iommu_sva_free_domain(struct iommu_domain *domain)
+{
+}
+
+static inline int iommu_sva_set_domain(struct iommu_domain *domain,
+ struct device *dev, ioasid_t pasid)
+{
+ return -EINVAL;
+}
+#endif /* CONFIG_IOMMU_SVA */
+
#endif /* __LINUX_IOMMU_H */
diff --git a/drivers/iommu/iommu-sva-lib.h b/drivers/iommu/iommu-sva-lib.h
index 8909ea1094e3..1be21e6b93ec 100644
--- a/drivers/iommu/iommu-sva-lib.h
+++ b/drivers/iommu/iommu-sva-lib.h
@@ -7,6 +7,7 @@

#include <linux/ioasid.h>
#include <linux/mm_types.h>
+#include <linux/iommu.h>

int iommu_sva_alloc_pasid(struct mm_struct *mm, ioasid_t min, ioasid_t max);
struct mm_struct *iommu_sva_find(ioasid_t pasid);
@@ -16,6 +17,20 @@ struct device;
struct iommu_fault;
struct iopf_queue;

+struct iommu_sva_domain {
+ struct iommu_domain domain;
+ struct mm_struct *mm;
+};
+
+#define to_sva_domain(d) container_of_safe(d, struct iommu_sva_domain, domain)
+
+static inline struct mm_struct *domain_to_mm(struct iommu_domain *domain)
+{
+ struct iommu_sva_domain *sva_domain = to_sva_domain(domain);
+
+ return sva_domain->mm;
+}
+
#ifdef CONFIG_IOMMU_SVA
int iommu_queue_iopf(struct iommu_fault *fault, void *cookie);

diff --git a/drivers/iommu/iommu-sva-lib.c b/drivers/iommu/iommu-sva-lib.c
index 106506143896..210c376f6043 100644
--- a/drivers/iommu/iommu-sva-lib.c
+++ b/drivers/iommu/iommu-sva-lib.c
@@ -69,3 +69,51 @@ struct mm_struct *iommu_sva_find(ioasid_t pasid)
return ioasid_find(&iommu_sva_pasid, pasid, __mmget_not_zero);
}
EXPORT_SYMBOL_GPL(iommu_sva_find);
+
+/*
+ * IOMMU SVA driver-oriented interfaces
+ */
+struct iommu_domain *
+iommu_sva_alloc_domain(struct bus_type *bus, struct mm_struct *mm)
+{
+ struct iommu_sva_domain *sva_domain;
+ struct iommu_domain *domain;
+
+ if (!bus->iommu_ops || !bus->iommu_ops->sva_domain_ops)
+ return ERR_PTR(-ENODEV);
+
+ sva_domain = kzalloc(sizeof(*sva_domain), GFP_KERNEL);
+ if (!sva_domain)
+ return ERR_PTR(-ENOMEM);
+
+ mmgrab(mm);
+ sva_domain->mm = mm;
+
+ domain = &sva_domain->domain;
+ domain->type = IOMMU_DOMAIN_SVA;
+ domain->ops = bus->iommu_ops->sva_domain_ops;
+
+ return domain;
+}
+
+void iommu_sva_free_domain(struct iommu_domain *domain)
+{
+ struct iommu_sva_domain *sva_domain = to_sva_domain(domain);
+
+ mmdrop(sva_domain->mm);
+ kfree(sva_domain);
+}
+
+int iommu_sva_set_domain(struct iommu_domain *domain, struct device *dev,
+ ioasid_t pasid)
+{
+ struct bus_type *bus = dev->bus;
+
+ if (!bus || !bus->iommu_ops || !bus->iommu_ops->sva_domain_ops)
+ return -ENODEV;
+
+ if (domain->ops != bus->iommu_ops->sva_domain_ops)
+ return -EINVAL;
+
+ return iommu_set_device_pasid(domain, dev, pasid);
+}
diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index 9955f58bd08c..789816e4b9d6 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -38,6 +38,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);
@@ -640,6 +641,7 @@ struct iommu_group *iommu_group_alloc(void)
mutex_init(&group->mutex);
INIT_LIST_HEAD(&group->devices);
INIT_LIST_HEAD(&group->entry);
+ xa_init(&group->pasid_array);

ret = ida_simple_get(&iommu_group_ida, 0, 0, GFP_KERNEL);
if (ret < 0) {
@@ -3251,3 +3253,72 @@ bool iommu_group_dma_owner_claimed(struct iommu_group *group)
return user;
}
EXPORT_SYMBOL_GPL(iommu_group_dma_owner_claimed);
+
+static bool device_group_immutable_singleton(struct device *dev)
+{
+ struct iommu_group *group = iommu_group_get(dev);
+ int count;
+
+ if (!group)
+ return false;
+
+ mutex_lock(&group->mutex);
+ count = iommu_group_device_count(group);
+ mutex_unlock(&group->mutex);
+ iommu_group_put(group);
+
+ if (count != 1)
+ return false;
+
+ /*
+ * The PCI device could be considered to be fully isolated if all
+ * devices on the path from the device to the host-PCI bridge are
+ * protected from peer-to-peer DMA by ACS.
+ */
+ if (dev_is_pci(dev))
+ return pci_acs_path_enabled(to_pci_dev(dev), NULL,
+ REQ_ACS_FLAGS);
+
+ return true;
+}
+
+int iommu_set_device_pasid(struct iommu_domain *domain, struct device *dev,
+ ioasid_t pasid)
+{
+ struct iommu_group *group;
+ int ret = -EBUSY;
+ void *curr;
+
+ if (!domain->ops->set_dev_pasid)
+ return -EOPNOTSUPP;
+
+ if (!device_group_immutable_singleton(dev))
+ return -EINVAL;
+
+ group = iommu_group_get(dev);
+ mutex_lock(&group->mutex);
+ curr = xa_cmpxchg(&group->pasid_array, pasid, NULL, domain, GFP_KERNEL);
+ if (curr)
+ goto out_unlock;
+ ret = domain->ops->set_dev_pasid(domain, dev, pasid);
+ if (ret)
+ xa_erase(&group->pasid_array, pasid);
+out_unlock:
+ mutex_unlock(&group->mutex);
+ iommu_group_put(group);
+
+ return ret;
+}
+
+void iommu_block_device_pasid(struct iommu_domain *domain, struct device *dev,
+ ioasid_t pasid)
+{
+ struct iommu_group *group = iommu_group_get(dev);
+
+ mutex_lock(&group->mutex);
+ domain->ops->block_dev_pasid(domain, dev, pasid);
+ xa_erase(&group->pasid_array, pasid);
+ mutex_unlock(&group->mutex);
+
+ iommu_group_put(group);
+}
--
2.25.1



2022-05-20 07:10:06

by Jean-Philippe Brucker

[permalink] [raw]
Subject: Re: [PATCH v7 03/10] iommu/sva: Add iommu_sva_domain support

On Thu, May 19, 2022 at 03:20:40PM +0800, Lu Baolu wrote:
> The iommu_sva_domain represents a hardware pagetable that the IOMMU
> hardware could use for SVA translation. This adds some infrastructure
> to support SVA domain in the iommu common layer. It includes:
>
> - Add a new struct iommu_sva_domain and new IOMMU_DOMAIN_SVA domain
> type.
> - Add a new domain ops pointer in iommu_ops. The IOMMU drivers that
> support SVA should provide the callbacks.
> - Add helpers to allocate and free an SVA domain.
> - Add helpers to set an SVA domain to a device and the reverse
> operation.
>
> Some buses, like PCI, route packets without considering the PASID value.
> Thus a DMA target address with PASID might be treated as P2P if the
> address falls into the MMIO BAR of other devices in the group. To make
> things simple, the attach/detach interfaces only apply to devices
> belonging to the singleton groups, and the singleton is immutable in
> fabric i.e. not affected by hotplug.
>
> The iommu_set/block_device_pasid() can be used for other purposes,
> such as kernel DMA with pasid, mediation device, etc. Hence, it is put
> in the iommu.c.
>
> Suggested-by: Jean-Philippe Brucker <[email protected]>
> Suggested-by: Jason Gunthorpe <[email protected]>
> Signed-off-by: Lu Baolu <[email protected]>
> ---
> include/linux/iommu.h | 51 +++++++++++++++++++++++++
> drivers/iommu/iommu-sva-lib.h | 15 ++++++++
> drivers/iommu/iommu-sva-lib.c | 48 +++++++++++++++++++++++
> drivers/iommu/iommu.c | 71 +++++++++++++++++++++++++++++++++++
> 4 files changed, 185 insertions(+)
>
> diff --git a/include/linux/iommu.h b/include/linux/iommu.h
> index 0c358b7c583b..e8cf82d46ce1 100644
> --- a/include/linux/iommu.h
> +++ b/include/linux/iommu.h
> @@ -64,6 +64,9 @@ struct iommu_domain_geometry {
> #define __IOMMU_DOMAIN_PT (1U << 2) /* Domain is identity mapped */
> #define __IOMMU_DOMAIN_DMA_FQ (1U << 3) /* DMA-API uses flush queue */
>
> +#define __IOMMU_DOMAIN_SHARED (1U << 4) /* Page table shared from CPU */
> +#define __IOMMU_DOMAIN_HOST_VA (1U << 5) /* Host CPU virtual address */
> +
> /*
> * This are the possible domain-types
> *
> @@ -86,6 +89,8 @@ struct iommu_domain_geometry {
> #define IOMMU_DOMAIN_DMA_FQ (__IOMMU_DOMAIN_PAGING | \
> __IOMMU_DOMAIN_DMA_API | \
> __IOMMU_DOMAIN_DMA_FQ)
> +#define IOMMU_DOMAIN_SVA (__IOMMU_DOMAIN_SHARED | \
> + __IOMMU_DOMAIN_HOST_VA)
>
> struct iommu_domain {
> unsigned type;
> @@ -254,6 +259,7 @@ struct iommu_ops {
> int (*def_domain_type)(struct device *dev);
>
> const struct iommu_domain_ops *default_domain_ops;
> + const struct iommu_domain_ops *sva_domain_ops;
> unsigned long pgsize_bitmap;
> struct module *owner;
> };
> @@ -262,6 +268,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
> + * @set_dev_pasid: set an iommu domain to a pasid of device
> + * @block_dev_pasid: block pasid of device from using iommu domain
> * @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.
> @@ -282,6 +290,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 (*set_dev_pasid)(struct iommu_domain *domain, struct device *dev,
> + ioasid_t pasid);
> + void (*block_dev_pasid)(struct iommu_domain *domain, struct device *dev,
> + ioasid_t pasid);
>
> int (*map)(struct iommu_domain *domain, unsigned long iova,
> phys_addr_t paddr, size_t size, int prot, gfp_t gfp);
> @@ -677,6 +689,10 @@ 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_set_device_pasid(struct iommu_domain *domain, struct device *dev,
> + ioasid_t pasid);
> +void iommu_block_device_pasid(struct iommu_domain *domain, struct device *dev,
> + ioasid_t pasid);
> #else /* CONFIG_IOMMU_API */
>
> struct iommu_ops {};
> @@ -1050,6 +1066,17 @@ static inline bool iommu_group_dma_owner_claimed(struct iommu_group *group)
> {
> return false;
> }
> +
> +static inline int iommu_set_device_pasid(struct iommu_domain *domain,
> + struct device *dev, ioasid_t pasid)
> +{
> + return -ENODEV;
> +}
> +
> +static inline void iommu_block_device_pasid(struct iommu_domain *domain,
> + struct device *dev, ioasid_t pasid)
> +{
> +}
> #endif /* CONFIG_IOMMU_API */
>
> /**
> @@ -1075,4 +1102,28 @@ void iommu_debugfs_setup(void);
> static inline void iommu_debugfs_setup(void) {}
> #endif
>
> +#ifdef CONFIG_IOMMU_SVA
> +struct iommu_domain *
> +iommu_sva_alloc_domain(struct bus_type *bus, struct mm_struct *mm);
> +void iommu_sva_free_domain(struct iommu_domain *domain);
> +int iommu_sva_set_domain(struct iommu_domain *domain, struct device *dev,
> + ioasid_t pasid);
> +#else /* CONFIG_IOMMU_SVA */
> +static inline struct iommu_domain *
> +iommu_sva_alloc_domain(struct bus_type *bus, struct mm_struct *mm)
> +{
> + return ERR_PTR(-EINVAL);
> +}
> +
> +static inline void iommu_sva_free_domain(struct iommu_domain *domain)
> +{
> +}
> +
> +static inline int iommu_sva_set_domain(struct iommu_domain *domain,
> + struct device *dev, ioasid_t pasid)
> +{
> + return -EINVAL;
> +}
> +#endif /* CONFIG_IOMMU_SVA */
> +
> #endif /* __LINUX_IOMMU_H */
> diff --git a/drivers/iommu/iommu-sva-lib.h b/drivers/iommu/iommu-sva-lib.h
> index 8909ea1094e3..1be21e6b93ec 100644
> --- a/drivers/iommu/iommu-sva-lib.h
> +++ b/drivers/iommu/iommu-sva-lib.h
> @@ -7,6 +7,7 @@
>
> #include <linux/ioasid.h>
> #include <linux/mm_types.h>
> +#include <linux/iommu.h>
>
> int iommu_sva_alloc_pasid(struct mm_struct *mm, ioasid_t min, ioasid_t max);
> struct mm_struct *iommu_sva_find(ioasid_t pasid);
> @@ -16,6 +17,20 @@ struct device;
> struct iommu_fault;
> struct iopf_queue;
>
> +struct iommu_sva_domain {
> + struct iommu_domain domain;
> + struct mm_struct *mm;
> +};
> +
> +#define to_sva_domain(d) container_of_safe(d, struct iommu_sva_domain, domain)

Is there a reason to use the 'safe' version of container_of()? Callers of
to_sva_domain() don't check the return value before dereferencing it so
they would break anyway if someone passes an error pointer as domain. I
think it matters because there is no other user of container_of_safe() in
the kernel (the only user, lustre, went away in 2018) so someone will want
to remove it.

Apart from that

Reviewed-by: Jean-Philippe Brucker <[email protected]>

> +
> +static inline struct mm_struct *domain_to_mm(struct iommu_domain *domain)
> +{
> + struct iommu_sva_domain *sva_domain = to_sva_domain(domain);
> +
> + return sva_domain->mm;
> +}
> +
> #ifdef CONFIG_IOMMU_SVA
> int iommu_queue_iopf(struct iommu_fault *fault, void *cookie);
>
> diff --git a/drivers/iommu/iommu-sva-lib.c b/drivers/iommu/iommu-sva-lib.c
> index 106506143896..210c376f6043 100644
> --- a/drivers/iommu/iommu-sva-lib.c
> +++ b/drivers/iommu/iommu-sva-lib.c
> @@ -69,3 +69,51 @@ struct mm_struct *iommu_sva_find(ioasid_t pasid)
> return ioasid_find(&iommu_sva_pasid, pasid, __mmget_not_zero);
> }
> EXPORT_SYMBOL_GPL(iommu_sva_find);
> +
> +/*
> + * IOMMU SVA driver-oriented interfaces
> + */
> +struct iommu_domain *
> +iommu_sva_alloc_domain(struct bus_type *bus, struct mm_struct *mm)
> +{
> + struct iommu_sva_domain *sva_domain;
> + struct iommu_domain *domain;
> +
> + if (!bus->iommu_ops || !bus->iommu_ops->sva_domain_ops)
> + return ERR_PTR(-ENODEV);
> +
> + sva_domain = kzalloc(sizeof(*sva_domain), GFP_KERNEL);
> + if (!sva_domain)
> + return ERR_PTR(-ENOMEM);
> +
> + mmgrab(mm);
> + sva_domain->mm = mm;
> +
> + domain = &sva_domain->domain;
> + domain->type = IOMMU_DOMAIN_SVA;
> + domain->ops = bus->iommu_ops->sva_domain_ops;
> +
> + return domain;
> +}
> +
> +void iommu_sva_free_domain(struct iommu_domain *domain)
> +{
> + struct iommu_sva_domain *sva_domain = to_sva_domain(domain);
> +
> + mmdrop(sva_domain->mm);
> + kfree(sva_domain);
> +}
> +
> +int iommu_sva_set_domain(struct iommu_domain *domain, struct device *dev,
> + ioasid_t pasid)
> +{
> + struct bus_type *bus = dev->bus;
> +
> + if (!bus || !bus->iommu_ops || !bus->iommu_ops->sva_domain_ops)
> + return -ENODEV;
> +
> + if (domain->ops != bus->iommu_ops->sva_domain_ops)
> + return -EINVAL;
> +
> + return iommu_set_device_pasid(domain, dev, pasid);
> +}
> diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
> index 9955f58bd08c..789816e4b9d6 100644
> --- a/drivers/iommu/iommu.c
> +++ b/drivers/iommu/iommu.c
> @@ -38,6 +38,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);
> @@ -640,6 +641,7 @@ struct iommu_group *iommu_group_alloc(void)
> mutex_init(&group->mutex);
> INIT_LIST_HEAD(&group->devices);
> INIT_LIST_HEAD(&group->entry);
> + xa_init(&group->pasid_array);
>
> ret = ida_simple_get(&iommu_group_ida, 0, 0, GFP_KERNEL);
> if (ret < 0) {
> @@ -3251,3 +3253,72 @@ bool iommu_group_dma_owner_claimed(struct iommu_group *group)
> return user;
> }
> EXPORT_SYMBOL_GPL(iommu_group_dma_owner_claimed);
> +
> +static bool device_group_immutable_singleton(struct device *dev)
> +{
> + struct iommu_group *group = iommu_group_get(dev);
> + int count;
> +
> + if (!group)
> + return false;
> +
> + mutex_lock(&group->mutex);
> + count = iommu_group_device_count(group);
> + mutex_unlock(&group->mutex);
> + iommu_group_put(group);
> +
> + if (count != 1)
> + return false;
> +
> + /*
> + * The PCI device could be considered to be fully isolated if all
> + * devices on the path from the device to the host-PCI bridge are
> + * protected from peer-to-peer DMA by ACS.
> + */
> + if (dev_is_pci(dev))
> + return pci_acs_path_enabled(to_pci_dev(dev), NULL,
> + REQ_ACS_FLAGS);
> +
> + return true;
> +}
> +
> +int iommu_set_device_pasid(struct iommu_domain *domain, struct device *dev,
> + ioasid_t pasid)
> +{
> + struct iommu_group *group;
> + int ret = -EBUSY;
> + void *curr;
> +
> + if (!domain->ops->set_dev_pasid)
> + return -EOPNOTSUPP;
> +
> + if (!device_group_immutable_singleton(dev))
> + return -EINVAL;
> +
> + group = iommu_group_get(dev);
> + mutex_lock(&group->mutex);
> + curr = xa_cmpxchg(&group->pasid_array, pasid, NULL, domain, GFP_KERNEL);
> + if (curr)
> + goto out_unlock;
> + ret = domain->ops->set_dev_pasid(domain, dev, pasid);
> + if (ret)
> + xa_erase(&group->pasid_array, pasid);
> +out_unlock:
> + mutex_unlock(&group->mutex);
> + iommu_group_put(group);
> +
> + return ret;
> +}
> +
> +void iommu_block_device_pasid(struct iommu_domain *domain, struct device *dev,
> + ioasid_t pasid)
> +{
> + struct iommu_group *group = iommu_group_get(dev);
> +
> + mutex_lock(&group->mutex);
> + domain->ops->block_dev_pasid(domain, dev, pasid);
> + xa_erase(&group->pasid_array, pasid);
> + mutex_unlock(&group->mutex);
> +
> + iommu_group_put(group);
> +}
> --
> 2.25.1
>

2022-05-23 06:08:25

by Lu Baolu

[permalink] [raw]
Subject: Re: [PATCH v7 03/10] iommu/sva: Add iommu_sva_domain support

On 2022/5/20 00:33, Jean-Philippe Brucker wrote:
>> diff --git a/drivers/iommu/iommu-sva-lib.h b/drivers/iommu/iommu-sva-lib.h
>> index 8909ea1094e3..1be21e6b93ec 100644
>> --- a/drivers/iommu/iommu-sva-lib.h
>> +++ b/drivers/iommu/iommu-sva-lib.h
>> @@ -7,6 +7,7 @@
>>
>> #include <linux/ioasid.h>
>> #include <linux/mm_types.h>
>> +#include <linux/iommu.h>
>>
>> int iommu_sva_alloc_pasid(struct mm_struct *mm, ioasid_t min, ioasid_t max);
>> struct mm_struct *iommu_sva_find(ioasid_t pasid);
>> @@ -16,6 +17,20 @@ struct device;
>> struct iommu_fault;
>> struct iopf_queue;
>>
>> +struct iommu_sva_domain {
>> + struct iommu_domain domain;
>> + struct mm_struct *mm;
>> +};
>> +
>> +#define to_sva_domain(d) container_of_safe(d, struct iommu_sva_domain, domain)
> Is there a reason to use the 'safe' version of container_of()? Callers of
> to_sva_domain() don't check the return value before dereferencing it so
> they would break anyway if someone passes an error pointer as domain. I
> think it matters because there is no other user of container_of_safe() in
> the kernel (the only user, lustre, went away in 2018) so someone will want
> to remove it.

Fair enough. I wondered why there's no user in the tree. Thanks for the
explanation. I will replace it with container_of().

>
> Apart from that
>
> Reviewed-by: Jean-Philippe Brucker<[email protected]>
>

Thank you!

Best regards,
baolu

2022-05-23 08:22:07

by Lu Baolu

[permalink] [raw]
Subject: Re: [PATCH v7 03/10] iommu/sva: Add iommu_sva_domain support

On 2022/5/19 15:20, Lu Baolu wrote:
> The iommu_sva_domain represents a hardware pagetable that the IOMMU
> hardware could use for SVA translation. This adds some infrastructure
> to support SVA domain in the iommu common layer. It includes:
>
> - Add a new struct iommu_sva_domain and new IOMMU_DOMAIN_SVA domain
> type.
> - Add a new domain ops pointer in iommu_ops. The IOMMU drivers that
> support SVA should provide the callbacks.
> - Add helpers to allocate and free an SVA domain.
> - Add helpers to set an SVA domain to a device and the reverse
> operation.
>
> Some buses, like PCI, route packets without considering the PASID value.
> Thus a DMA target address with PASID might be treated as P2P if the
> address falls into the MMIO BAR of other devices in the group. To make
> things simple, the attach/detach interfaces only apply to devices
> belonging to the singleton groups, and the singleton is immutable in
> fabric i.e. not affected by hotplug.
>
> The iommu_set/block_device_pasid() can be used for other purposes,
> such as kernel DMA with pasid, mediation device, etc. Hence, it is put
> in the iommu.c.
>
> Suggested-by: Jean-Philippe Brucker<[email protected]>
> Suggested-by: Jason Gunthorpe<[email protected]>
> Signed-off-by: Lu Baolu<[email protected]>
> ---
> include/linux/iommu.h | 51 +++++++++++++++++++++++++
> drivers/iommu/iommu-sva-lib.h | 15 ++++++++
> drivers/iommu/iommu-sva-lib.c | 48 +++++++++++++++++++++++
> drivers/iommu/iommu.c | 71 +++++++++++++++++++++++++++++++++++
> 4 files changed, 185 insertions(+)
>
> diff --git a/include/linux/iommu.h b/include/linux/iommu.h
> index 0c358b7c583b..e8cf82d46ce1 100644
> --- a/include/linux/iommu.h
> +++ b/include/linux/iommu.h
> @@ -64,6 +64,9 @@ struct iommu_domain_geometry {
> #define __IOMMU_DOMAIN_PT (1U << 2) /* Domain is identity mapped */
> #define __IOMMU_DOMAIN_DMA_FQ (1U << 3) /* DMA-API uses flush queue */
>
> +#define __IOMMU_DOMAIN_SHARED (1U << 4) /* Page table shared from CPU */
> +#define __IOMMU_DOMAIN_HOST_VA (1U << 5) /* Host CPU virtual address */
> +
> /*
> * This are the possible domain-types
> *
> @@ -86,6 +89,8 @@ struct iommu_domain_geometry {
> #define IOMMU_DOMAIN_DMA_FQ (__IOMMU_DOMAIN_PAGING | \
> __IOMMU_DOMAIN_DMA_API | \
> __IOMMU_DOMAIN_DMA_FQ)
> +#define IOMMU_DOMAIN_SVA (__IOMMU_DOMAIN_SHARED | \
> + __IOMMU_DOMAIN_HOST_VA)
>
> struct iommu_domain {
> unsigned type;
> @@ -254,6 +259,7 @@ struct iommu_ops {
> int (*def_domain_type)(struct device *dev);
>
> const struct iommu_domain_ops *default_domain_ops;
> + const struct iommu_domain_ops *sva_domain_ops;

Per Joerg's comment in anther thread,

https://lore.kernel.org/linux-iommu/[email protected]/

adding a sva_domain_ops here is not the right way to go.

If no objection, I will make the sva domain go through the
generic domain_alloc/free() callbacks in the next version.

Best regards,
baolu

2022-05-24 13:37:10

by Tian, Kevin

[permalink] [raw]
Subject: RE: [PATCH v7 03/10] iommu/sva: Add iommu_sva_domain support

> From: Baolu Lu <[email protected]>
> Sent: Monday, May 23, 2022 3:13 PM
> > @@ -254,6 +259,7 @@ struct iommu_ops {
> > int (*def_domain_type)(struct device *dev);
> >
> > const struct iommu_domain_ops *default_domain_ops;
> > + const struct iommu_domain_ops *sva_domain_ops;
>
> Per Joerg's comment in anther thread,
>
> https://lore.kernel.org/linux-iommu/[email protected]/
>
> adding a sva_domain_ops here is not the right way to go.
>
> If no objection, I will make the sva domain go through the
> generic domain_alloc/free() callbacks in the next version.
>

suppose it's just back to what v1-v6 did which all registered the ops
in domain_alloc() callback?

2022-05-24 16:35:40

by Robin Murphy

[permalink] [raw]
Subject: Re: [PATCH v7 03/10] iommu/sva: Add iommu_sva_domain support

On 2022-05-19 08:20, Lu Baolu wrote:
[...]
> diff --git a/drivers/iommu/iommu-sva-lib.c b/drivers/iommu/iommu-sva-lib.c
> index 106506143896..210c376f6043 100644
> --- a/drivers/iommu/iommu-sva-lib.c
> +++ b/drivers/iommu/iommu-sva-lib.c
> @@ -69,3 +69,51 @@ struct mm_struct *iommu_sva_find(ioasid_t pasid)
> return ioasid_find(&iommu_sva_pasid, pasid, __mmget_not_zero);
> }
> EXPORT_SYMBOL_GPL(iommu_sva_find);
> +
> +/*
> + * IOMMU SVA driver-oriented interfaces
> + */
> +struct iommu_domain *
> +iommu_sva_alloc_domain(struct bus_type *bus, struct mm_struct *mm)

Argh, please no new bus-based external interfaces! Domain allocation
needs to resolve to the right IOMMU instance to solve a number of
issues, and cleaning up existing users of iommu_domain_alloc() to
prepare for that is already hard enough. This is arguably even more
relevant here than for other domain types, since SVA support is more
likely to depend on specific features that can vary between IOMMU
instances even with the same driver. Please make the external interface
take a struct device, then resolve the ops through dev->iommu.

Further nit: the naming inconsistency bugs me a bit -
iommu_sva_domain_alloc() seems more natural. Also I'd question the
symmetry vs. usability dichotomy of whether we *really* want two
different free functions for a struct iommu_domain pointer, where any
caller which might mix SVA and non-SVA usage then has to remember how
they allocated any particular domain :/

> +{
> + struct iommu_sva_domain *sva_domain;
> + struct iommu_domain *domain;
> +
> + if (!bus->iommu_ops || !bus->iommu_ops->sva_domain_ops)
> + return ERR_PTR(-ENODEV);
> +
> + sva_domain = kzalloc(sizeof(*sva_domain), GFP_KERNEL);
> + if (!sva_domain)
> + return ERR_PTR(-ENOMEM);
> +
> + mmgrab(mm);
> + sva_domain->mm = mm;
> +
> + domain = &sva_domain->domain;
> + domain->type = IOMMU_DOMAIN_SVA;
> + domain->ops = bus->iommu_ops->sva_domain_ops;

I'd have thought it would be logical to pass IOMMU_DOMAIN_SVA to the
normal domain_alloc call, so that driver-internal stuff like context
descriptors can be still be hung off the domain as usual (rather than
all drivers having to implement some extra internal lookup mechanism to
handle all the SVA domain ops), but that's something we're free to come
back and change later. FWIW I'd just stick the mm pointer in struct
iommu_domain, in a union with the fault handler stuff and/or iova_cookie
- those are mutually exclusive with SVA, right?

Cheers,
Robin.

> +
> + return domain;
> +}
> +
> +void iommu_sva_free_domain(struct iommu_domain *domain)
> +{
> + struct iommu_sva_domain *sva_domain = to_sva_domain(domain);
> +
> + mmdrop(sva_domain->mm);
> + kfree(sva_domain);
> +}
> +

2022-05-25 03:09:45

by Lu Baolu

[permalink] [raw]
Subject: Re: [PATCH v7 03/10] iommu/sva: Add iommu_sva_domain support

On 2022/5/25 08:44, Tian, Kevin wrote:
>> From: Jason Gunthorpe <[email protected]>
>> Sent: Tuesday, May 24, 2022 9:39 PM
>>
>> On Tue, May 24, 2022 at 09:39:52AM +0000, Tian, Kevin wrote:
>>>> From: Lu Baolu <[email protected]>
>>>> Sent: Thursday, May 19, 2022 3:21 PM
>>>>
>>>> The iommu_sva_domain represents a hardware pagetable that the
>> IOMMU
>>>> hardware could use for SVA translation. This adds some infrastructure
>>>> to support SVA domain in the iommu common layer. It includes:
>>>>
>>>> - Add a new struct iommu_sva_domain and new IOMMU_DOMAIN_SVA
>>>> domain
>>>> type.
>>>> - Add a new domain ops pointer in iommu_ops. The IOMMU drivers that
>>>> support SVA should provide the callbacks.
>>>> - Add helpers to allocate and free an SVA domain.
>>>> - Add helpers to set an SVA domain to a device and the reverse
>>>> operation.
>>>>
>>>> Some buses, like PCI, route packets without considering the PASID value.
>>>> Thus a DMA target address with PASID might be treated as P2P if the
>>>> address falls into the MMIO BAR of other devices in the group. To make
>>>> things simple, the attach/detach interfaces only apply to devices
>>>> belonging to the singleton groups, and the singleton is immutable in
>>>> fabric i.e. not affected by hotplug.
>>>>
>>>> The iommu_set/block_device_pasid() can be used for other purposes,
>>>> such as kernel DMA with pasid, mediation device, etc. Hence, it is put
>>>> in the iommu.c.
>>>
>>> usually we have 'set/clear' pair or 'allow/block'. Having 'set' paired
>>> with 'block' doesn't read very clearly.
>>
>> I thought we agreed we'd use the blocking domain for this? Why did it
>> go back to an op?
>>
>
> Probably it's based on following discussion:
>
> https://lore.kernel.org/all/[email protected]/
>
> --
>> FWIW from my point of view I'm happy with having a .detach_dev_pasid op
>> meaning implicitly-blocked access for now.
>
> If this is the path then lets not call it attach/detach
> please. 'set_dev_pasid' and 'set_dev_blocking_pasid' are clearer
> names.

Yes. Learning from above discussion, we are about to implement the
set_dev_pasid and blocking domain in parallel. We will convert all
the callback names to set_dev and set_dev_pasid after blocking domain
support is merged.

> --
>
> Looks Baolu chooses this path and plans to use the blocking domain
> later.

Yes. I have already started to implement the blocking domain in Intel
driver. With it as an example, we can extend it to other possible IOMMU
drivers.

Best regards,
baolu

2022-05-25 06:55:24

by Lu Baolu

[permalink] [raw]
Subject: Re: [PATCH v7 03/10] iommu/sva: Add iommu_sva_domain support

Hi Robin,

On 2022/5/24 22:36, Robin Murphy wrote:
> On 2022-05-19 08:20, Lu Baolu wrote:
> [...]
>> diff --git a/drivers/iommu/iommu-sva-lib.c
>> b/drivers/iommu/iommu-sva-lib.c
>> index 106506143896..210c376f6043 100644
>> --- a/drivers/iommu/iommu-sva-lib.c
>> +++ b/drivers/iommu/iommu-sva-lib.c
>> @@ -69,3 +69,51 @@ struct mm_struct *iommu_sva_find(ioasid_t pasid)
>>       return ioasid_find(&iommu_sva_pasid, pasid, __mmget_not_zero);
>>   }
>>   EXPORT_SYMBOL_GPL(iommu_sva_find);
>> +
>> +/*
>> + * IOMMU SVA driver-oriented interfaces
>> + */
>> +struct iommu_domain *
>> +iommu_sva_alloc_domain(struct bus_type *bus, struct mm_struct *mm)
>
> Argh, please no new bus-based external interfaces! Domain allocation
> needs to resolve to the right IOMMU instance to solve a number of
> issues, and cleaning up existing users of iommu_domain_alloc() to
> prepare for that is already hard enough. This is arguably even more
> relevant here than for other domain types, since SVA support is more
> likely to depend on specific features that can vary between IOMMU
> instances even with the same driver. Please make the external interface
> take a struct device, then resolve the ops through dev->iommu.
>
> Further nit: the naming inconsistency bugs me a bit -
> iommu_sva_domain_alloc() seems more natural. Also I'd question the
> symmetry vs. usability dichotomy of whether we *really* want two
> different free functions for a struct iommu_domain pointer, where any
> caller which might mix SVA and non-SVA usage then has to remember how
> they allocated any particular domain :/
>
>> +{
>> +    struct iommu_sva_domain *sva_domain;
>> +    struct iommu_domain *domain;
>> +
>> +    if (!bus->iommu_ops || !bus->iommu_ops->sva_domain_ops)
>> +        return ERR_PTR(-ENODEV);
>> +
>> +    sva_domain = kzalloc(sizeof(*sva_domain), GFP_KERNEL);
>> +    if (!sva_domain)
>> +        return ERR_PTR(-ENOMEM);
>> +
>> +    mmgrab(mm);
>> +    sva_domain->mm = mm;
>> +
>> +    domain = &sva_domain->domain;
>> +    domain->type = IOMMU_DOMAIN_SVA;
>> +    domain->ops = bus->iommu_ops->sva_domain_ops;
>
> I'd have thought it would be logical to pass IOMMU_DOMAIN_SVA to the
> normal domain_alloc call, so that driver-internal stuff like context
> descriptors can be still be hung off the domain as usual (rather than
> all drivers having to implement some extra internal lookup mechanism to
> handle all the SVA domain ops), but that's something we're free to come

Agreed with above comments. Thanks! I will post an additional patch
for review later.

> back and change later. FWIW I'd just stick the mm pointer in struct
> iommu_domain, in a union with the fault handler stuff and/or iova_cookie
> - those are mutually exclusive with SVA, right?

"iova_cookie" is mutually exclusive with SVA, but I am not sure about
the fault handler stuff.

Did you mean @handler and @handler_token staffs below?

struct iommu_domain {
unsigned type;
const struct iommu_domain_ops *ops;
unsigned long pgsize_bitmap; /* Bitmap of page sizes in use */
iommu_fault_handler_t handler;
void *handler_token;
struct iommu_domain_geometry geometry;
struct iommu_dma_cookie *iova_cookie;
};

Is it only for DMA domains? From the point view of IOMMU faults, it
seems to be generic.

Best regards,
baolu

2022-05-25 07:51:08

by Tian, Kevin

[permalink] [raw]
Subject: RE: [PATCH v7 03/10] iommu/sva: Add iommu_sva_domain support

> From: Jason Gunthorpe <[email protected]>
> Sent: Tuesday, May 24, 2022 9:39 PM
>
> On Tue, May 24, 2022 at 09:39:52AM +0000, Tian, Kevin wrote:
> > > From: Lu Baolu <[email protected]>
> > > Sent: Thursday, May 19, 2022 3:21 PM
> > >
> > > The iommu_sva_domain represents a hardware pagetable that the
> IOMMU
> > > hardware could use for SVA translation. This adds some infrastructure
> > > to support SVA domain in the iommu common layer. It includes:
> > >
> > > - Add a new struct iommu_sva_domain and new IOMMU_DOMAIN_SVA
> > > domain
> > > type.
> > > - Add a new domain ops pointer in iommu_ops. The IOMMU drivers that
> > > support SVA should provide the callbacks.
> > > - Add helpers to allocate and free an SVA domain.
> > > - Add helpers to set an SVA domain to a device and the reverse
> > > operation.
> > >
> > > Some buses, like PCI, route packets without considering the PASID value.
> > > Thus a DMA target address with PASID might be treated as P2P if the
> > > address falls into the MMIO BAR of other devices in the group. To make
> > > things simple, the attach/detach interfaces only apply to devices
> > > belonging to the singleton groups, and the singleton is immutable in
> > > fabric i.e. not affected by hotplug.
> > >
> > > The iommu_set/block_device_pasid() can be used for other purposes,
> > > such as kernel DMA with pasid, mediation device, etc. Hence, it is put
> > > in the iommu.c.
> >
> > usually we have 'set/clear' pair or 'allow/block'. Having 'set' paired
> > with 'block' doesn't read very clearly.
>
> I thought we agreed we'd use the blocking domain for this? Why did it
> go back to an op?
>

Probably it's based on following discussion:

https://lore.kernel.org/all/[email protected]/

--
> FWIW from my point of view I'm happy with having a .detach_dev_pasid op
> meaning implicitly-blocked access for now.

If this is the path then lets not call it attach/detach
please. 'set_dev_pasid' and 'set_dev_blocking_pasid' are clearer
names.
--

Looks Baolu chooses this path and plans to use the blocking domain
later.

Thanks
Kevin

2022-05-25 09:04:19

by Lu Baolu

[permalink] [raw]
Subject: Re: [PATCH v7 03/10] iommu/sva: Add iommu_sva_domain support

On 2022/5/24 17:39, Tian, Kevin wrote:
>> From: Lu Baolu <[email protected]>
>> Sent: Thursday, May 19, 2022 3:21 PM
>>
>> The iommu_sva_domain represents a hardware pagetable that the IOMMU
>> hardware could use for SVA translation. This adds some infrastructure
>> to support SVA domain in the iommu common layer. It includes:
>>
>> - Add a new struct iommu_sva_domain and new IOMMU_DOMAIN_SVA
>> domain
>> type.
>> - Add a new domain ops pointer in iommu_ops. The IOMMU drivers that
>> support SVA should provide the callbacks.
>> - Add helpers to allocate and free an SVA domain.
>> - Add helpers to set an SVA domain to a device and the reverse
>> operation.
>>
>> Some buses, like PCI, route packets without considering the PASID value.
>> Thus a DMA target address with PASID might be treated as P2P if the
>> address falls into the MMIO BAR of other devices in the group. To make
>> things simple, the attach/detach interfaces only apply to devices
>> belonging to the singleton groups, and the singleton is immutable in
>> fabric i.e. not affected by hotplug.
>>
>> The iommu_set/block_device_pasid() can be used for other purposes,
>> such as kernel DMA with pasid, mediation device, etc. Hence, it is put
>> in the iommu.c.
>
> usually we have 'set/clear' pair or 'allow/block'. Having 'set' paired
> with 'block' doesn't read very clearly.

Yes. Let's still use the attach/detach semantics.

>
>> +static bool device_group_immutable_singleton(struct device *dev)
>> +{
>> + struct iommu_group *group = iommu_group_get(dev);
>
> what about passing group as the parameter since the caller will
> get the group again right after calling this function? In that case
> the function could be renamed as:
>
> iommu_group_immutable_singleton()
>
> or be shorter:
>
> iommu_group_fixed_singleton()

Fair enough. I will tune it as below:

+static bool iommu_group_immutable_singleton(struct iommu_group *group)
+{
+ int count;
+
+ mutex_lock(&group->mutex);
+ count = iommu_group_device_count(group);
+ mutex_unlock(&group->mutex);
+
+ if (count != 1)
+ return false;
+
+ /*
+ * The PCI device could be considered to be fully isolated if all
+ * devices on the path from the device to the host-PCI bridge are
+ * protected from peer-to-peer DMA by ACS.
+ */
+ if (dev_is_pci(dev))
+ return pci_acs_path_enabled(to_pci_dev(dev), NULL,
+ REQ_ACS_FLAGS);
+
+ /*
+ * Otherwise, the device came from DT/ACPI, assume it is static and
+ * then singleton can know from the device count in the group.
+ */
+ return true;
+}


>
>> + int count;
>> +
>> + if (!group)
>> + return false;
>> +
>> + mutex_lock(&group->mutex);
>> + count = iommu_group_device_count(group);
>> + mutex_unlock(&group->mutex);
>> + iommu_group_put(group);
>> +
>> + if (count != 1)
>> + return false;
>
> For non-pci devices above doesn't check anything against immutable.
> Please add some comment to explain why doing so is correct.

Yes, as above code shows.

>
>> +
>> + /*
>> + * The PCI device could be considered to be fully isolated if all
>> + * devices on the path from the device to the host-PCI bridge are
>> + * protected from peer-to-peer DMA by ACS.
>> + */
>> + if (dev_is_pci(dev))
>> + return pci_acs_path_enabled(to_pci_dev(dev), NULL,
>> + REQ_ACS_FLAGS);
>> +
>> + return true;
>> +}
>> +

Best regards,
baolu

2022-05-25 09:07:19

by Tian, Kevin

[permalink] [raw]
Subject: RE: [PATCH v7 03/10] iommu/sva: Add iommu_sva_domain support

> From: Lu Baolu <[email protected]>
> Sent: Thursday, May 19, 2022 3:21 PM
>
> The iommu_sva_domain represents a hardware pagetable that the IOMMU
> hardware could use for SVA translation. This adds some infrastructure
> to support SVA domain in the iommu common layer. It includes:
>
> - Add a new struct iommu_sva_domain and new IOMMU_DOMAIN_SVA
> domain
> type.
> - Add a new domain ops pointer in iommu_ops. The IOMMU drivers that
> support SVA should provide the callbacks.
> - Add helpers to allocate and free an SVA domain.
> - Add helpers to set an SVA domain to a device and the reverse
> operation.
>
> Some buses, like PCI, route packets without considering the PASID value.
> Thus a DMA target address with PASID might be treated as P2P if the
> address falls into the MMIO BAR of other devices in the group. To make
> things simple, the attach/detach interfaces only apply to devices
> belonging to the singleton groups, and the singleton is immutable in
> fabric i.e. not affected by hotplug.
>
> The iommu_set/block_device_pasid() can be used for other purposes,
> such as kernel DMA with pasid, mediation device, etc. Hence, it is put
> in the iommu.c.

usually we have 'set/clear' pair or 'allow/block'. Having 'set' paired
with 'block' doesn't read very clearly.

> +static bool device_group_immutable_singleton(struct device *dev)
> +{
> + struct iommu_group *group = iommu_group_get(dev);

what about passing group as the parameter since the caller will
get the group again right after calling this function? In that case
the function could be renamed as:

iommu_group_immutable_singleton()

or be shorter:

iommu_group_fixed_singleton()

> + int count;
> +
> + if (!group)
> + return false;
> +
> + mutex_lock(&group->mutex);
> + count = iommu_group_device_count(group);
> + mutex_unlock(&group->mutex);
> + iommu_group_put(group);
> +
> + if (count != 1)
> + return false;

For non-pci devices above doesn't check anything against immutable.
Please add some comment to explain why doing so is correct.

> +
> + /*
> + * The PCI device could be considered to be fully isolated if all
> + * devices on the path from the device to the host-PCI bridge are
> + * protected from peer-to-peer DMA by ACS.
> + */
> + if (dev_is_pci(dev))
> + return pci_acs_path_enabled(to_pci_dev(dev), NULL,
> + REQ_ACS_FLAGS);
> +
> + return true;
> +}
> +

2022-05-25 10:11:17

by Lu Baolu

[permalink] [raw]
Subject: Re: [PATCH v7 03/10] iommu/sva: Add iommu_sva_domain support

Hi Jason,

On 2022/5/24 21:44, Jason Gunthorpe wrote:
>> diff --git a/drivers/iommu/iommu-sva-lib.c b/drivers/iommu/iommu-sva-lib.c
>> index 106506143896..210c376f6043 100644
>> +++ b/drivers/iommu/iommu-sva-lib.c
>> @@ -69,3 +69,51 @@ struct mm_struct *iommu_sva_find(ioasid_t pasid)
>> return ioasid_find(&iommu_sva_pasid, pasid, __mmget_not_zero);
>> }
>> EXPORT_SYMBOL_GPL(iommu_sva_find);
>> +
>> +/*
>> + * IOMMU SVA driver-oriented interfaces
>> + */
>> +struct iommu_domain *
>> +iommu_sva_alloc_domain(struct bus_type *bus, struct mm_struct *mm)
> This should return the proper type
>

Can you please elaborate a bit on "return the proper type"? Did you mean
return iommu_sva_domain instead? That's a wrapper of iommu_domain and
only for iommu internal usage.

Best regards,
baolu

2022-05-25 10:47:05

by Jason Gunthorpe

[permalink] [raw]
Subject: Re: [PATCH v7 03/10] iommu/sva: Add iommu_sva_domain support

On Tue, May 24, 2022 at 09:39:52AM +0000, Tian, Kevin wrote:
> > From: Lu Baolu <[email protected]>
> > Sent: Thursday, May 19, 2022 3:21 PM
> >
> > The iommu_sva_domain represents a hardware pagetable that the IOMMU
> > hardware could use for SVA translation. This adds some infrastructure
> > to support SVA domain in the iommu common layer. It includes:
> >
> > - Add a new struct iommu_sva_domain and new IOMMU_DOMAIN_SVA
> > domain
> > type.
> > - Add a new domain ops pointer in iommu_ops. The IOMMU drivers that
> > support SVA should provide the callbacks.
> > - Add helpers to allocate and free an SVA domain.
> > - Add helpers to set an SVA domain to a device and the reverse
> > operation.
> >
> > Some buses, like PCI, route packets without considering the PASID value.
> > Thus a DMA target address with PASID might be treated as P2P if the
> > address falls into the MMIO BAR of other devices in the group. To make
> > things simple, the attach/detach interfaces only apply to devices
> > belonging to the singleton groups, and the singleton is immutable in
> > fabric i.e. not affected by hotplug.
> >
> > The iommu_set/block_device_pasid() can be used for other purposes,
> > such as kernel DMA with pasid, mediation device, etc. Hence, it is put
> > in the iommu.c.
>
> usually we have 'set/clear' pair or 'allow/block'. Having 'set' paired
> with 'block' doesn't read very clearly.

I thought we agreed we'd use the blocking domain for this? Why did it
go back to an op?

Jason

2022-05-25 13:18:03

by Lu Baolu

[permalink] [raw]
Subject: Re: [PATCH v7 03/10] iommu/sva: Add iommu_sva_domain support

On 2022/5/24 17:44, Tian, Kevin wrote:
>> From: Baolu Lu <[email protected]>
>> Sent: Monday, May 23, 2022 3:13 PM
>>> @@ -254,6 +259,7 @@ struct iommu_ops {
>>> int (*def_domain_type)(struct device *dev);
>>>
>>> const struct iommu_domain_ops *default_domain_ops;
>>> + const struct iommu_domain_ops *sva_domain_ops;
>>
>> Per Joerg's comment in anther thread,
>>
>> https://lore.kernel.org/linux-iommu/[email protected]/
>>
>> adding a sva_domain_ops here is not the right way to go.
>>
>> If no objection, I will make the sva domain go through the
>> generic domain_alloc/free() callbacks in the next version.
>>
>
> suppose it's just back to what v1-v6 did which all registered the ops
> in domain_alloc() callback?

Yes.

Best regards,
baolu

2022-05-25 19:54:30

by Jean-Philippe Brucker

[permalink] [raw]
Subject: Re: [PATCH v7 03/10] iommu/sva: Add iommu_sva_domain support

On Wed, May 25, 2022 at 11:07:49AM +0100, Robin Murphy wrote:
> > Did you mean @handler and @handler_token staffs below?
> >
> > struct iommu_domain {
> >         unsigned type;
> >         const struct iommu_domain_ops *ops;
> >         unsigned long pgsize_bitmap;    /* Bitmap of page sizes in use */
> >         iommu_fault_handler_t handler;
> >         void *handler_token;
> >         struct iommu_domain_geometry geometry;
> >         struct iommu_dma_cookie *iova_cookie;
> > };
> >
> > Is it only for DMA domains? From the point view of IOMMU faults, it
> > seems to be generic.
>
> Yes, it's the old common iommu_set_fault_handler() stuff (which arguably is
> more of a "notifier" than a "handler"), but I assume that that's irrelevant
> if SVA is using IOPF instead?

Yes IOMMU drivers call either the newer iommu_report_device_fault() or the
old report_iommu_fault(), and only the former can support IOPF/SVA. I've
tried to merge them before but never completed it. I think the main issue
was with finding the endpoint that caused the fault from the fault
handler. Some IOMMU drivers just pass the IOMMU device to
report_iommu_fault(). I'll probably pick that up at some point.

Thanks,
Jean

2022-05-26 00:44:59

by Robin Murphy

[permalink] [raw]
Subject: Re: [PATCH v7 03/10] iommu/sva: Add iommu_sva_domain support

On 2022-05-25 07:20, Baolu Lu wrote:
> Hi Robin,
>
> On 2022/5/24 22:36, Robin Murphy wrote:
>> On 2022-05-19 08:20, Lu Baolu wrote:
>> [...]
>>> diff --git a/drivers/iommu/iommu-sva-lib.c
>>> b/drivers/iommu/iommu-sva-lib.c
>>> index 106506143896..210c376f6043 100644
>>> --- a/drivers/iommu/iommu-sva-lib.c
>>> +++ b/drivers/iommu/iommu-sva-lib.c
>>> @@ -69,3 +69,51 @@ struct mm_struct *iommu_sva_find(ioasid_t pasid)
>>>       return ioasid_find(&iommu_sva_pasid, pasid, __mmget_not_zero);
>>>   }
>>>   EXPORT_SYMBOL_GPL(iommu_sva_find);
>>> +
>>> +/*
>>> + * IOMMU SVA driver-oriented interfaces
>>> + */
>>> +struct iommu_domain *
>>> +iommu_sva_alloc_domain(struct bus_type *bus, struct mm_struct *mm)
>>
>> Argh, please no new bus-based external interfaces! Domain allocation
>> needs to resolve to the right IOMMU instance to solve a number of
>> issues, and cleaning up existing users of iommu_domain_alloc() to
>> prepare for that is already hard enough. This is arguably even more
>> relevant here than for other domain types, since SVA support is more
>> likely to depend on specific features that can vary between IOMMU
>> instances even with the same driver. Please make the external
>> interface take a struct device, then resolve the ops through dev->iommu.
>>
>> Further nit: the naming inconsistency bugs me a bit -
>> iommu_sva_domain_alloc() seems more natural. Also I'd question the
>> symmetry vs. usability dichotomy of whether we *really* want two
>> different free functions for a struct iommu_domain pointer, where any
>> caller which might mix SVA and non-SVA usage then has to remember how
>> they allocated any particular domain :/
>>
>>> +{
>>> +    struct iommu_sva_domain *sva_domain;
>>> +    struct iommu_domain *domain;
>>> +
>>> +    if (!bus->iommu_ops || !bus->iommu_ops->sva_domain_ops)
>>> +        return ERR_PTR(-ENODEV);
>>> +
>>> +    sva_domain = kzalloc(sizeof(*sva_domain), GFP_KERNEL);
>>> +    if (!sva_domain)
>>> +        return ERR_PTR(-ENOMEM);
>>> +
>>> +    mmgrab(mm);
>>> +    sva_domain->mm = mm;
>>> +
>>> +    domain = &sva_domain->domain;
>>> +    domain->type = IOMMU_DOMAIN_SVA;
>>> +    domain->ops = bus->iommu_ops->sva_domain_ops;
>>
>> I'd have thought it would be logical to pass IOMMU_DOMAIN_SVA to the
>> normal domain_alloc call, so that driver-internal stuff like context
>> descriptors can be still be hung off the domain as usual (rather than
>> all drivers having to implement some extra internal lookup mechanism
>> to handle all the SVA domain ops), but that's something we're free to
>> come
>
> Agreed with above comments. Thanks! I will post an additional patch
> for review later.
>
>> back and change later. FWIW I'd just stick the mm pointer in struct
>> iommu_domain, in a union with the fault handler stuff and/or
>> iova_cookie - those are mutually exclusive with SVA, right?
>
> "iova_cookie" is mutually exclusive with SVA, but I am not sure about
> the fault handler stuff.

To correct myself, the IOVA cookie should be irrelevant to *current*
SVA, but if we ever get as far as whole-device-SVA without PASIDs then
we might need an MSI cookie (modulo the additional problem of stealing
some procvess address space for it).

> Did you mean @handler and @handler_token staffs below?
>
> struct iommu_domain {
>         unsigned type;
>         const struct iommu_domain_ops *ops;
>         unsigned long pgsize_bitmap;    /* Bitmap of page sizes in use */
>         iommu_fault_handler_t handler;
>         void *handler_token;
>         struct iommu_domain_geometry geometry;
>         struct iommu_dma_cookie *iova_cookie;
> };
>
> Is it only for DMA domains? From the point view of IOMMU faults, it
> seems to be generic.

Yes, it's the old common iommu_set_fault_handler() stuff (which arguably
is more of a "notifier" than a "handler"), but I assume that that's
irrelevant if SVA is using IOPF instead?

Thanks,
Robin.

2022-05-26 01:43:05

by Lu Baolu

[permalink] [raw]
Subject: Re: [PATCH v7 03/10] iommu/sva: Add iommu_sva_domain support

On 2022/5/24 21:44, Jason Gunthorpe wrote:
>> +{
>> + struct iommu_sva_domain *sva_domain;
>> + struct iommu_domain *domain;
>> +
>> + if (!bus->iommu_ops || !bus->iommu_ops->sva_domain_ops)
>> + return ERR_PTR(-ENODEV);
>> +
>> + sva_domain = kzalloc(sizeof(*sva_domain), GFP_KERNEL);
>> + if (!sva_domain)
>> + return ERR_PTR(-ENOMEM);
>> +
>> + mmgrab(mm);
>> + sva_domain->mm = mm;
>> +
>> + domain = &sva_domain->domain;
>> + domain->type = IOMMU_DOMAIN_SVA;
>> + domain->ops = bus->iommu_ops->sva_domain_ops;
>> +
>> + return domain;
>> +}
>> +
>> +void iommu_sva_free_domain(struct iommu_domain *domain)
>> +{
>> + struct iommu_sva_domain *sva_domain = to_sva_domain(domain);
>> +
>> + mmdrop(sva_domain->mm);
>> + kfree(sva_domain);
>> +}
> No callback to the driver?

Should do this in the next version. This version added an sva-specific
iommu_domain_ops pointer in iommu_ops. This is not the right way to go.

>
>> +int iommu_sva_set_domain(struct iommu_domain *domain, struct device *dev,
>> + ioasid_t pasid)
>> +{
> Why does this function exist? Just call iommu_set_device_pasid()

Yes, agreed.

>
>> +int iommu_set_device_pasid(struct iommu_domain *domain, struct device *dev,
>> + ioasid_t pasid)
>> +{
> Here you can continue to use attach/detach language as at this API
> level we expect strict pairing..

Sure.

>
>
>> +void iommu_block_device_pasid(struct iommu_domain *domain, struct device *dev,
>> + ioasid_t pasid)
>> +{
>> + struct iommu_group *group = iommu_group_get(dev);
>> +
>> + mutex_lock(&group->mutex);
>> + domain->ops->block_dev_pasid(domain, dev, pasid);
>> + xa_erase(&group->pasid_array, pasid);
>> + mutex_unlock(&group->mutex);
> Should be the blocking domain.

As we discussed, we should change above to blocking domain when the
blocking domain is supported on at least Intel and arm-smmu-v3 drivers.
I have started the work for Intel driver support.

Best regards,
baolu


2022-05-26 03:01:18

by Lu Baolu

[permalink] [raw]
Subject: Re: [PATCH v7 03/10] iommu/sva: Add iommu_sva_domain support

On 2022/5/25 19:06, Jean-Philippe Brucker wrote:
> On Wed, May 25, 2022 at 11:07:49AM +0100, Robin Murphy wrote:
>>> Did you mean @handler and @handler_token staffs below?
>>>
>>> struct iommu_domain {
>>>         unsigned type;
>>>         const struct iommu_domain_ops *ops;
>>>         unsigned long pgsize_bitmap;    /* Bitmap of page sizes in use */
>>>         iommu_fault_handler_t handler;
>>>         void *handler_token;
>>>         struct iommu_domain_geometry geometry;
>>>         struct iommu_dma_cookie *iova_cookie;
>>> };
>>>
>>> Is it only for DMA domains? From the point view of IOMMU faults, it
>>> seems to be generic.
>> Yes, it's the old common iommu_set_fault_handler() stuff (which arguably is
>> more of a "notifier" than a "handler"), but I assume that that's irrelevant
>> if SVA is using IOPF instead?
> Yes IOMMU drivers call either the newer iommu_report_device_fault() or the
> old report_iommu_fault(), and only the former can support IOPF/SVA. I've
> tried to merge them before but never completed it. I think the main issue
> was with finding the endpoint that caused the fault from the fault
> handler. Some IOMMU drivers just pass the IOMMU device to
> report_iommu_fault(). I'll probably pick that up at some point.

Thank you all for the comments and suggestions. Below is the refreshed
patch. Hope that I didn't miss anything.

From 463c04cada8e8640598f981d8d16157781b9de6f Mon Sep 17 00:00:00 2001
From: Lu Baolu <[email protected]>
Date: Wed, 11 May 2022 20:59:24 +0800
Subject: [PATCH 04/11] iommu: Add sva iommu_domain support

The sva iommu_domain represents a hardware pagetable that the IOMMU
hardware could use for SVA translation. This adds some infrastructure
to support SVA domain in the iommu common layer. It includes:

- Extend the iommu_domain to support a new IOMMU_DOMAIN_SVA domain
type. The IOMMU drivers that support SVA should provide the sva
domain specific iommu_domain_ops.
- Add a helper to allocate an SVA domain. The iommu_domain_free()
is still used to free an SVA domain.
- Add helpers to attach an SVA domain to a device and the reverse
operation.

Some buses, like PCI, route packets without considering the PASID value.
Thus a DMA target address with PASID might be treated as P2P if the
address falls into the MMIO BAR of other devices in the group. To make
things simple, the attach/detach interfaces only apply to devices
belonging to the singleton groups, and the singleton is immutable in
fabric i.e. not affected by hotplug.

The iommu_attach/detach_device_pasid() can be used for other purposes,
such as kernel DMA with pasid, mediation device, etc.

Suggested-by: Jean-Philippe Brucker <[email protected]>
Suggested-by: Jason Gunthorpe <[email protected]>
Signed-off-by: Lu Baolu <[email protected]>
Reviewed-by: Jean-Philippe Brucker <[email protected]>
---
drivers/iommu/iommu.c | 93 +++++++++++++++++++++++++++++++++++++++++++
include/linux/iommu.h | 45 ++++++++++++++++++++-
2 files changed, 136 insertions(+), 2 deletions(-)

diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index 63b64b4e8a38..b1a2ad64a413 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -27,6 +27,7 @@
#include <linux/module.h>
#include <linux/cc_platform.h>
#include <trace/events/iommu.h>
+#include <linux/sched/mm.h>

static struct kset *iommu_group_kset;
static DEFINE_IDA(iommu_group_ida);
@@ -39,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);
@@ -666,6 +668,7 @@ struct iommu_group *iommu_group_alloc(void)
mutex_init(&group->mutex);
INIT_LIST_HEAD(&group->devices);
INIT_LIST_HEAD(&group->entry);
+ xa_init(&group->pasid_array);

ret = ida_simple_get(&iommu_group_ida, 0, 0, GFP_KERNEL);
if (ret < 0) {
@@ -1961,6 +1964,8 @@ EXPORT_SYMBOL_GPL(iommu_domain_alloc);

void iommu_domain_free(struct iommu_domain *domain)
{
+ if (domain->type == IOMMU_DOMAIN_SVA)
+ mmdrop(domain->mm);
iommu_put_dma_cookie(domain);
domain->ops->free(domain);
}
@@ -3277,3 +3282,91 @@ bool iommu_group_dma_owner_claimed(struct
iommu_group *group)
return user;
}
EXPORT_SYMBOL_GPL(iommu_group_dma_owner_claimed);
+
+struct iommu_domain *iommu_sva_domain_alloc(struct device *dev,
+ struct mm_struct *mm)
+{
+ const struct iommu_ops *ops = dev_iommu_ops(dev);
+ struct iommu_domain *domain;
+
+ domain = ops->domain_alloc(IOMMU_DOMAIN_SVA);
+ if (!domain)
+ return NULL;
+
+ domain->type = IOMMU_DOMAIN_SVA;
+ mmgrab(mm);
+ domain->mm = mm;
+
+ return domain;
+}
+
+static bool iommu_group_immutable_singleton(struct iommu_group *group,
+ struct device *dev)
+{
+ int count;
+
+ mutex_lock(&group->mutex);
+ count = iommu_group_device_count(group);
+ mutex_unlock(&group->mutex);
+
+ if (count != 1)
+ return false;
+
+ /*
+ * The PCI device could be considered to be fully isolated if all
+ * devices on the path from the device to the host-PCI bridge are
+ * protected from peer-to-peer DMA by ACS.
+ */
+ if (dev_is_pci(dev))
+ return pci_acs_path_enabled(to_pci_dev(dev), NULL,
+ REQ_ACS_FLAGS);
+
+ /*
+ * Otherwise, the device came from DT/ACPI, assume it is static and
+ * then singleton can know from the device count in the group.
+ */
+ return true;
+}
+
+int iommu_attach_device_pasid(struct iommu_domain *domain, struct
device *dev,
+ ioasid_t pasid)
+{
+ struct iommu_group *group;
+ int ret = -EBUSY;
+ void *curr;
+
+ if (!domain->ops->set_dev_pasid)
+ return -EOPNOTSUPP;
+
+ group = iommu_group_get(dev);
+ if (!group || !iommu_group_immutable_singleton(group, dev)) {
+ iommu_group_put(group);
+ return -EINVAL;
+ }
+
+ mutex_lock(&group->mutex);
+ curr = xa_cmpxchg(&group->pasid_array, pasid, NULL, domain, GFP_KERNEL);
+ if (curr)
+ goto out_unlock;
+ ret = domain->ops->set_dev_pasid(domain, dev, pasid);
+ if (ret)
+ xa_erase(&group->pasid_array, pasid);
+out_unlock:
+ mutex_unlock(&group->mutex);
+ iommu_group_put(group);
+
+ return ret;
+}
+
+void iommu_detach_device_pasid(struct iommu_domain *domain, struct
device *dev,
+ ioasid_t pasid)
+{
+ struct iommu_group *group = iommu_group_get(dev);
+
+ mutex_lock(&group->mutex);
+ domain->ops->block_dev_pasid(domain, dev, pasid);
+ xa_erase(&group->pasid_array, pasid);
+ mutex_unlock(&group->mutex);
+
+ iommu_group_put(group);
+}
diff --git a/include/linux/iommu.h b/include/linux/iommu.h
index 3fbad42c0bf8..9173c5741447 100644
--- a/include/linux/iommu.h
+++ b/include/linux/iommu.h
@@ -64,6 +64,9 @@ struct iommu_domain_geometry {
#define __IOMMU_DOMAIN_PT (1U << 2) /* Domain is identity mapped */
#define __IOMMU_DOMAIN_DMA_FQ (1U << 3) /* DMA-API uses flush queue */

+#define __IOMMU_DOMAIN_SHARED (1U << 4) /* Page table shared from CPU */
+#define __IOMMU_DOMAIN_HOST_VA (1U << 5) /* Host CPU virtual address */
+
/*
* This are the possible domain-types
*
@@ -86,15 +89,24 @@ struct iommu_domain_geometry {
#define IOMMU_DOMAIN_DMA_FQ (__IOMMU_DOMAIN_PAGING | \
__IOMMU_DOMAIN_DMA_API | \
__IOMMU_DOMAIN_DMA_FQ)
+#define IOMMU_DOMAIN_SVA (__IOMMU_DOMAIN_SHARED | \
+ __IOMMU_DOMAIN_HOST_VA)

struct iommu_domain {
unsigned type;
const struct iommu_domain_ops *ops;
unsigned long pgsize_bitmap; /* Bitmap of page sizes in use */
- iommu_fault_handler_t handler;
- void *handler_token;
struct iommu_domain_geometry geometry;
struct iommu_dma_cookie *iova_cookie;
+ union {
+ struct { /* IOMMU_DOMAIN_DMA */
+ iommu_fault_handler_t handler;
+ void *handler_token;
+ };
+ struct { /* IOMMU_DOMAIN_SVA */
+ struct mm_struct *mm;
+ };
+ };
};

static inline bool iommu_is_dma_domain(struct iommu_domain *domain)
@@ -262,6 +274,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
+ * @set_dev_pasid: set an iommu domain to a pasid of device
+ * @block_dev_pasid: block pasid of device from using iommu domain
* @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.
@@ -282,6 +296,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 (*set_dev_pasid)(struct iommu_domain *domain, struct device *dev,
+ ioasid_t pasid);
+ void (*block_dev_pasid)(struct iommu_domain *domain, struct device *dev,
+ ioasid_t pasid);

int (*map)(struct iommu_domain *domain, unsigned long iova,
phys_addr_t paddr, size_t size, int prot, gfp_t gfp);
@@ -679,6 +697,12 @@ 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);

+struct iommu_domain *iommu_sva_domain_alloc(struct device *dev,
+ struct mm_struct *mm);
+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);
#else /* CONFIG_IOMMU_API */

struct iommu_ops {};
@@ -1052,6 +1076,23 @@ static inline bool
iommu_group_dma_owner_claimed(struct iommu_group *group)
{
return false;
}
+
+static inline struct iommu_domain *
+iommu_sva_domain_alloc(struct device *dev, struct mm_struct *mm)
+{
+ return NULL;
+}
+
+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)
+{
+}
#endif /* CONFIG_IOMMU_API */

/**
--
2.25.1

Best regards,
baolu

2022-05-26 03:49:26

by Lu Baolu

[permalink] [raw]
Subject: Re: [PATCH v7 03/10] iommu/sva: Add iommu_sva_domain support

On 2022/5/25 23:25, Jason Gunthorpe wrote:
> On Wed, May 25, 2022 at 01:19:08PM +0800, Baolu Lu wrote:
>> Hi Jason,
>>
>> On 2022/5/24 21:44, Jason Gunthorpe wrote:
>>>> diff --git a/drivers/iommu/iommu-sva-lib.c b/drivers/iommu/iommu-sva-lib.c
>>>> index 106506143896..210c376f6043 100644
>>>> +++ b/drivers/iommu/iommu-sva-lib.c
>>>> @@ -69,3 +69,51 @@ struct mm_struct *iommu_sva_find(ioasid_t pasid)
>>>> return ioasid_find(&iommu_sva_pasid, pasid, __mmget_not_zero);
>>>> }
>>>> EXPORT_SYMBOL_GPL(iommu_sva_find);
>>>> +
>>>> +/*
>>>> + * IOMMU SVA driver-oriented interfaces
>>>> + */
>>>> +struct iommu_domain *
>>>> +iommu_sva_alloc_domain(struct bus_type *bus, struct mm_struct *mm)
>>> This should return the proper type
>>>
>>
>> Can you please elaborate a bit on "return the proper type"? Did you mean
>> return iommu_sva_domain instead? That's a wrapper of iommu_domain and
>> only for iommu internal usage.
>
> If you are exposing special SVA APIs then it is not internal usage
> only anymore, so expose the type.

Ah, got you. Thanks for the explanation.

Best regards,
baolu

2022-05-26 12:16:04

by Jason Gunthorpe

[permalink] [raw]
Subject: Re: [PATCH v7 03/10] iommu/sva: Add iommu_sva_domain support

On Wed, May 25, 2022 at 01:19:08PM +0800, Baolu Lu wrote:
> Hi Jason,
>
> On 2022/5/24 21:44, Jason Gunthorpe wrote:
> > > diff --git a/drivers/iommu/iommu-sva-lib.c b/drivers/iommu/iommu-sva-lib.c
> > > index 106506143896..210c376f6043 100644
> > > +++ b/drivers/iommu/iommu-sva-lib.c
> > > @@ -69,3 +69,51 @@ struct mm_struct *iommu_sva_find(ioasid_t pasid)
> > > return ioasid_find(&iommu_sva_pasid, pasid, __mmget_not_zero);
> > > }
> > > EXPORT_SYMBOL_GPL(iommu_sva_find);
> > > +
> > > +/*
> > > + * IOMMU SVA driver-oriented interfaces
> > > + */
> > > +struct iommu_domain *
> > > +iommu_sva_alloc_domain(struct bus_type *bus, struct mm_struct *mm)
> > This should return the proper type
> >
>
> Can you please elaborate a bit on "return the proper type"? Did you mean
> return iommu_sva_domain instead? That's a wrapper of iommu_domain and
> only for iommu internal usage.

If you are exposing special SVA APIs then it is not internal usage
only anymore, so expose the type.

Jason

2022-05-26 22:13:54

by Jason Gunthorpe

[permalink] [raw]
Subject: Re: [PATCH v7 03/10] iommu/sva: Add iommu_sva_domain support

On Thu, May 19, 2022 at 03:20:40PM +0800, Lu Baolu wrote:
> The iommu_sva_domain represents a hardware pagetable that the IOMMU
> hardware could use for SVA translation. This adds some infrastructure
> to support SVA domain in the iommu common layer. It includes:
>
> - Add a new struct iommu_sva_domain and new IOMMU_DOMAIN_SVA domain
> type.
> - Add a new domain ops pointer in iommu_ops. The IOMMU drivers that
> support SVA should provide the callbacks.
> - Add helpers to allocate and free an SVA domain.
> - Add helpers to set an SVA domain to a device and the reverse
> operation.
>
> Some buses, like PCI, route packets without considering the PASID value.
> Thus a DMA target address with PASID might be treated as P2P if the
> address falls into the MMIO BAR of other devices in the group. To make
> things simple, the attach/detach interfaces only apply to devices
> belonging to the singleton groups, and the singleton is immutable in
> fabric i.e. not affected by hotplug.
>
> The iommu_set/block_device_pasid() can be used for other purposes,
> such as kernel DMA with pasid, mediation device, etc. Hence, it is put
> in the iommu.c.
>
> Suggested-by: Jean-Philippe Brucker <[email protected]>
> Suggested-by: Jason Gunthorpe <[email protected]>
> Signed-off-by: Lu Baolu <[email protected]>
> include/linux/iommu.h | 51 +++++++++++++++++++++++++
> drivers/iommu/iommu-sva-lib.h | 15 ++++++++
> drivers/iommu/iommu-sva-lib.c | 48 +++++++++++++++++++++++
> drivers/iommu/iommu.c | 71 +++++++++++++++++++++++++++++++++++
> 4 files changed, 185 insertions(+)
>
> diff --git a/include/linux/iommu.h b/include/linux/iommu.h
> index 0c358b7c583b..e8cf82d46ce1 100644
> +++ b/include/linux/iommu.h
> @@ -64,6 +64,9 @@ struct iommu_domain_geometry {
> #define __IOMMU_DOMAIN_PT (1U << 2) /* Domain is identity mapped */
> #define __IOMMU_DOMAIN_DMA_FQ (1U << 3) /* DMA-API uses flush queue */
>
> +#define __IOMMU_DOMAIN_SHARED (1U << 4) /* Page table shared from CPU */
> +#define __IOMMU_DOMAIN_HOST_VA (1U << 5) /* Host CPU virtual address */
> +
> /*
> * This are the possible domain-types
> *
> @@ -86,6 +89,8 @@ struct iommu_domain_geometry {
> #define IOMMU_DOMAIN_DMA_FQ (__IOMMU_DOMAIN_PAGING | \
> __IOMMU_DOMAIN_DMA_API | \
> __IOMMU_DOMAIN_DMA_FQ)
> +#define IOMMU_DOMAIN_SVA (__IOMMU_DOMAIN_SHARED | \
> + __IOMMU_DOMAIN_HOST_VA)
>
> struct iommu_domain {
> unsigned type;
> @@ -254,6 +259,7 @@ struct iommu_ops {
> int (*def_domain_type)(struct device *dev);
>
> const struct iommu_domain_ops *default_domain_ops;
> + const struct iommu_domain_ops *sva_domain_ops;
> unsigned long pgsize_bitmap;
> struct module *owner;
> };
> @@ -262,6 +268,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
> + * @set_dev_pasid: set an iommu domain to a pasid of device
> + * @block_dev_pasid: block pasid of device from using iommu domain
> * @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.
> @@ -282,6 +290,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 (*set_dev_pasid)(struct iommu_domain *domain, struct device *dev,
> + ioasid_t pasid);
> + void (*block_dev_pasid)(struct iommu_domain *domain, struct device *dev,
> + ioasid_t pasid);
>
> int (*map)(struct iommu_domain *domain, unsigned long iova,
> phys_addr_t paddr, size_t size, int prot, gfp_t gfp);
> @@ -677,6 +689,10 @@ 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_set_device_pasid(struct iommu_domain *domain, struct device *dev,
> + ioasid_t pasid);
> +void iommu_block_device_pasid(struct iommu_domain *domain, struct device *dev,
> + ioasid_t pasid);
> #else /* CONFIG_IOMMU_API */
>
> struct iommu_ops {};
> @@ -1050,6 +1066,17 @@ static inline bool iommu_group_dma_owner_claimed(struct iommu_group *group)
> {
> return false;
> }
> +
> +static inline int iommu_set_device_pasid(struct iommu_domain *domain,
> + struct device *dev, ioasid_t pasid)
> +{
> + return -ENODEV;
> +}
> +
> +static inline void iommu_block_device_pasid(struct iommu_domain *domain,
> + struct device *dev, ioasid_t pasid)
> +{
> +}
> #endif /* CONFIG_IOMMU_API */
>
> /**
> @@ -1075,4 +1102,28 @@ void iommu_debugfs_setup(void);
> static inline void iommu_debugfs_setup(void) {}
> #endif
>
> +#ifdef CONFIG_IOMMU_SVA
> +struct iommu_domain *
> +iommu_sva_alloc_domain(struct bus_type *bus, struct mm_struct *mm);
> +void iommu_sva_free_domain(struct iommu_domain *domain);
> +int iommu_sva_set_domain(struct iommu_domain *domain, struct device *dev,
> + ioasid_t pasid);
> +#else /* CONFIG_IOMMU_SVA */
> +static inline struct iommu_domain *
> +iommu_sva_alloc_domain(struct bus_type *bus, struct mm_struct *mm)
> +{
> + return ERR_PTR(-EINVAL);
> +}
> +
> +static inline void iommu_sva_free_domain(struct iommu_domain *domain)
> +{
> +}
> +
> +static inline int iommu_sva_set_domain(struct iommu_domain *domain,
> + struct device *dev, ioasid_t pasid)
> +{
> + return -EINVAL;
> +}
> +#endif /* CONFIG_IOMMU_SVA */
> +
> #endif /* __LINUX_IOMMU_H */
> diff --git a/drivers/iommu/iommu-sva-lib.h b/drivers/iommu/iommu-sva-lib.h
> index 8909ea1094e3..1be21e6b93ec 100644
> +++ b/drivers/iommu/iommu-sva-lib.h
> @@ -7,6 +7,7 @@
>
> #include <linux/ioasid.h>
> #include <linux/mm_types.h>
> +#include <linux/iommu.h>
>
> int iommu_sva_alloc_pasid(struct mm_struct *mm, ioasid_t min, ioasid_t max);
> struct mm_struct *iommu_sva_find(ioasid_t pasid);
> @@ -16,6 +17,20 @@ struct device;
> struct iommu_fault;
> struct iopf_queue;
>
> +struct iommu_sva_domain {
> + struct iommu_domain domain;
> + struct mm_struct *mm;
> +};
> +
> +#define to_sva_domain(d) container_of_safe(d, struct iommu_sva_domain, domain)
> +
> +static inline struct mm_struct *domain_to_mm(struct iommu_domain *domain)
> +{
> + struct iommu_sva_domain *sva_domain = to_sva_domain(domain);
> +
> + return sva_domain->mm;
> +}
> +
> #ifdef CONFIG_IOMMU_SVA
> int iommu_queue_iopf(struct iommu_fault *fault, void *cookie);
>
> diff --git a/drivers/iommu/iommu-sva-lib.c b/drivers/iommu/iommu-sva-lib.c
> index 106506143896..210c376f6043 100644
> +++ b/drivers/iommu/iommu-sva-lib.c
> @@ -69,3 +69,51 @@ struct mm_struct *iommu_sva_find(ioasid_t pasid)
> return ioasid_find(&iommu_sva_pasid, pasid, __mmget_not_zero);
> }
> EXPORT_SYMBOL_GPL(iommu_sva_find);
> +
> +/*
> + * IOMMU SVA driver-oriented interfaces
> + */
> +struct iommu_domain *
> +iommu_sva_alloc_domain(struct bus_type *bus, struct mm_struct *mm)

This should return the proper type

> +{
> + struct iommu_sva_domain *sva_domain;
> + struct iommu_domain *domain;
> +
> + if (!bus->iommu_ops || !bus->iommu_ops->sva_domain_ops)
> + return ERR_PTR(-ENODEV);
> +
> + sva_domain = kzalloc(sizeof(*sva_domain), GFP_KERNEL);
> + if (!sva_domain)
> + return ERR_PTR(-ENOMEM);
> +
> + mmgrab(mm);
> + sva_domain->mm = mm;
> +
> + domain = &sva_domain->domain;
> + domain->type = IOMMU_DOMAIN_SVA;
> + domain->ops = bus->iommu_ops->sva_domain_ops;
> +
> + return domain;
> +}
> +
> +void iommu_sva_free_domain(struct iommu_domain *domain)
> +{
> + struct iommu_sva_domain *sva_domain = to_sva_domain(domain);
> +
> + mmdrop(sva_domain->mm);
> + kfree(sva_domain);
> +}

No callback to the driver?

> +int iommu_sva_set_domain(struct iommu_domain *domain, struct device *dev,
> + ioasid_t pasid)
> +{

Why does this function exist? Just call iommu_set_device_pasid()

> +int iommu_set_device_pasid(struct iommu_domain *domain, struct device *dev,
> + ioasid_t pasid)
> +{

Here you can continue to use attach/detach language as at this API
level we expect strict pairing..


> +void iommu_block_device_pasid(struct iommu_domain *domain, struct device *dev,
> + ioasid_t pasid)
> +{
> + struct iommu_group *group = iommu_group_get(dev);
> +
> + mutex_lock(&group->mutex);
> + domain->ops->block_dev_pasid(domain, dev, pasid);
> + xa_erase(&group->pasid_array, pasid);
> + mutex_unlock(&group->mutex);

Should be the blocking domain.

Jason