2022-05-19 10:19:47

by Lu Baolu

[permalink] [raw]
Subject: [PATCH v7 06/10] iommu/sva: Refactoring iommu_sva_bind/unbind_device()

The existing iommu SVA interfaces are implemented by calling the SVA
specific iommu ops provided by the IOMMU drivers. There's no need for
any SVA specific ops in iommu_ops vector anymore as we can achieve
this through the generic attach/detach_dev_pasid domain ops.

This refactors the IOMMU SVA interfaces implementation by using the
set/block_pasid_dev ops and align them with the concept of the SVA
iommu domain. Put the new SVA code in the sva related file in order
to make it self-contained.

Signed-off-by: Lu Baolu <[email protected]>
---
include/linux/iommu.h | 48 ++++++++------
drivers/iommu/iommu-sva-lib.h | 1 +
drivers/iommu/iommu-sva-lib.c | 113 ++++++++++++++++++++++++++++++++
drivers/iommu/iommu.c | 119 ++++++++--------------------------
4 files changed, 170 insertions(+), 111 deletions(-)

diff --git a/include/linux/iommu.h b/include/linux/iommu.h
index e8cf82d46ce1..d9ac5ebe5bbb 100644
--- a/include/linux/iommu.h
+++ b/include/linux/iommu.h
@@ -635,6 +635,7 @@ struct iommu_fwspec {
*/
struct iommu_sva {
struct device *dev;
+ refcount_t users;
};

int iommu_fwspec_init(struct device *dev, struct fwnode_handle *iommu_fwnode,
@@ -677,11 +678,6 @@ int iommu_dev_enable_feature(struct device *dev, enum iommu_dev_features f);
int iommu_dev_disable_feature(struct device *dev, enum iommu_dev_features f);
bool iommu_dev_feature_enabled(struct device *dev, enum iommu_dev_features f);

-struct iommu_sva *iommu_sva_bind_device(struct device *dev,
- struct mm_struct *mm);
-void iommu_sva_unbind_device(struct iommu_sva *handle);
-u32 iommu_sva_get_pasid(struct iommu_sva *handle);
-
int iommu_device_use_default_domain(struct device *dev);
void iommu_device_unuse_default_domain(struct device *dev);

@@ -693,6 +689,8 @@ 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);
+struct iommu_domain *
+iommu_get_domain_for_dev_pasid(struct device *dev, ioasid_t pasid);
#else /* CONFIG_IOMMU_API */

struct iommu_ops {};
@@ -1023,21 +1021,6 @@ iommu_dev_disable_feature(struct device *dev, enum iommu_dev_features feat)
return -ENODEV;
}

-static inline struct iommu_sva *
-iommu_sva_bind_device(struct device *dev, struct mm_struct *mm)
-{
- return NULL;
-}
-
-static inline void iommu_sva_unbind_device(struct iommu_sva *handle)
-{
-}
-
-static inline u32 iommu_sva_get_pasid(struct iommu_sva *handle)
-{
- return IOMMU_PASID_INVALID;
-}
-
static inline struct iommu_fwspec *dev_iommu_fwspec_get(struct device *dev)
{
return NULL;
@@ -1077,6 +1060,12 @@ static inline void iommu_block_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;
+}
#endif /* CONFIG_IOMMU_API */

/**
@@ -1108,6 +1097,10 @@ 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);
+struct iommu_sva *iommu_sva_bind_device(struct device *dev,
+ struct mm_struct *mm);
+void iommu_sva_unbind_device(struct iommu_sva *handle);
+u32 iommu_sva_get_pasid(struct iommu_sva *handle);
#else /* CONFIG_IOMMU_SVA */
static inline struct iommu_domain *
iommu_sva_alloc_domain(struct bus_type *bus, struct mm_struct *mm)
@@ -1124,6 +1117,21 @@ static inline int iommu_sva_set_domain(struct iommu_domain *domain,
{
return -EINVAL;
}
+
+static inline struct iommu_sva *
+iommu_sva_bind_device(struct device *dev, struct mm_struct *mm)
+{
+ return NULL;
+}
+
+static inline void iommu_sva_unbind_device(struct iommu_sva *handle)
+{
+}
+
+static inline u32 iommu_sva_get_pasid(struct iommu_sva *handle)
+{
+ return IOMMU_PASID_INVALID;
+}
#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 1be21e6b93ec..ebab5a8cb126 100644
--- a/drivers/iommu/iommu-sva-lib.h
+++ b/drivers/iommu/iommu-sva-lib.h
@@ -20,6 +20,7 @@ struct iopf_queue;
struct iommu_sva_domain {
struct iommu_domain domain;
struct mm_struct *mm;
+ struct iommu_sva bond;
};

#define to_sva_domain(d) container_of_safe(d, struct iommu_sva_domain, domain)
diff --git a/drivers/iommu/iommu-sva-lib.c b/drivers/iommu/iommu-sva-lib.c
index 210c376f6043..568e0f64edac 100644
--- a/drivers/iommu/iommu-sva-lib.c
+++ b/drivers/iommu/iommu-sva-lib.c
@@ -4,6 +4,8 @@
*/
#include <linux/mutex.h>
#include <linux/sched/mm.h>
+#include <linux/pci.h>
+#include <linux/pci-ats.h>

#include "iommu-sva-lib.h"

@@ -117,3 +119,114 @@ int iommu_sva_set_domain(struct iommu_domain *domain, struct device *dev,

return iommu_set_device_pasid(domain, dev, pasid);
}
+
+/**
+ * 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 mm_users
+ *
+ * Create a bond between device and address space, allowing the device to access
+ * the mm using the returned PASID. If a bond already exists between @device and
+ * @mm, it is returned and an additional reference is taken. Caller must call
+ * iommu_sva_unbind_device() to release each reference.
+ *
+ * iommu_dev_enable_feature(dev, IOMMU_DEV_FEAT_SVA) must be called first, to
+ * initialize the required SVA features.
+ *
+ * On error, returns an ERR_PTR value.
+ */
+struct iommu_sva *iommu_sva_bind_device(struct device *dev, struct mm_struct *mm)
+{
+ struct iommu_sva_domain *sva_domain;
+ struct iommu_domain *domain;
+ ioasid_t max_pasid = 0;
+ int ret = -EINVAL;
+
+ /* Allocate mm->pasid if necessary. */
+ if (!dev->iommu->iommu_dev->pasids)
+ return ERR_PTR(-EOPNOTSUPP);
+
+ if (dev_is_pci(dev)) {
+ max_pasid = pci_max_pasids(to_pci_dev(dev));
+ if (max_pasid < 0)
+ return ERR_PTR(max_pasid);
+ } else {
+ ret = device_property_read_u32(dev, "pasid-num-bits",
+ &max_pasid);
+ if (ret)
+ return ERR_PTR(ret);
+ max_pasid = (1UL << max_pasid);
+ }
+ max_pasid = min_t(u32, max_pasid, dev->iommu->iommu_dev->pasids);
+ ret = iommu_sva_alloc_pasid(mm, 1, max_pasid - 1);
+ if (ret)
+ return ERR_PTR(ret);
+
+ mutex_lock(&iommu_sva_lock);
+ /* Search for an existing domain. */
+ domain = iommu_get_domain_for_dev_pasid(dev, mm->pasid);
+ if (domain) {
+ sva_domain = to_sva_domain(domain);
+ refcount_inc(&sva_domain->bond.users);
+ goto out_success;
+ }
+
+ /* Allocate a new domain and set it on device pasid. */
+ domain = iommu_sva_alloc_domain(dev->bus, mm);
+ if (IS_ERR(domain)) {
+ ret = PTR_ERR(domain);
+ goto out_unlock;
+ }
+
+ ret = iommu_sva_set_domain(domain, dev, mm->pasid);
+ if (ret)
+ goto out_free_domain;
+ sva_domain = to_sva_domain(domain);
+ sva_domain->bond.dev = dev;
+ refcount_set(&sva_domain->bond.users, 1);
+
+out_success:
+ mutex_unlock(&iommu_sva_lock);
+ return &sva_domain->bond;
+
+out_free_domain:
+ iommu_sva_free_domain(domain);
+out_unlock:
+ mutex_unlock(&iommu_sva_lock);
+
+ return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(iommu_sva_bind_device);
+
+/**
+ * iommu_sva_unbind_device() - Remove a bond created with iommu_sva_bind_device
+ * @handle: the handle returned by iommu_sva_bind_device()
+ *
+ * Put reference to a bond between device and address space. The device should
+ * not be issuing any more transaction for this PASID. All outstanding page
+ * requests for this PASID must have been flushed to the IOMMU.
+ */
+void iommu_sva_unbind_device(struct iommu_sva *handle)
+{
+ struct device *dev = handle->dev;
+ struct iommu_sva_domain *sva_domain =
+ container_of(handle, struct iommu_sva_domain, bond);
+ ioasid_t pasid = iommu_sva_get_pasid(handle);
+
+ mutex_lock(&iommu_sva_lock);
+ if (refcount_dec_and_test(&sva_domain->bond.users)) {
+ iommu_block_device_pasid(&sva_domain->domain, dev, pasid);
+ iommu_sva_free_domain(&sva_domain->domain);
+ }
+ mutex_unlock(&iommu_sva_lock);
+}
+EXPORT_SYMBOL_GPL(iommu_sva_unbind_device);
+
+u32 iommu_sva_get_pasid(struct iommu_sva *handle)
+{
+ struct iommu_sva_domain *sva_domain =
+ container_of(handle, struct iommu_sva_domain, bond);
+
+ return sva_domain->mm->pasid;
+}
+EXPORT_SYMBOL_GPL(iommu_sva_get_pasid);
diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index 789816e4b9d6..e49c5a5b8cc1 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -2766,97 +2766,6 @@ bool iommu_dev_feature_enabled(struct device *dev, enum iommu_dev_features feat)
}
EXPORT_SYMBOL_GPL(iommu_dev_feature_enabled);

-/**
- * 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
- *
- * Create a bond between device and address space, allowing the device to access
- * the mm using the returned PASID. If a bond already exists between @device and
- * @mm, it is returned and an additional reference is taken. Caller must call
- * iommu_sva_unbind_device() to release each reference.
- *
- * iommu_dev_enable_feature(dev, IOMMU_DEV_FEAT_SVA) must be called first, to
- * initialize the required SVA features.
- *
- * On error, returns an ERR_PTR value.
- */
-struct iommu_sva *
-iommu_sva_bind_device(struct device *dev, struct mm_struct *mm)
-{
- struct iommu_group *group;
- struct iommu_sva *handle = ERR_PTR(-EINVAL);
- const struct iommu_ops *ops = dev_iommu_ops(dev);
-
- if (!ops->sva_bind)
- return ERR_PTR(-ENODEV);
-
- group = iommu_group_get(dev);
- if (!group)
- return ERR_PTR(-ENODEV);
-
- /* Ensure device count and domain don't change while we're binding */
- mutex_lock(&group->mutex);
-
- /*
- * To keep things simple, SVA currently doesn'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_device_count(group) != 1)
- goto out_unlock;
-
- handle = ops->sva_bind(dev, mm);
-
-out_unlock:
- mutex_unlock(&group->mutex);
- iommu_group_put(group);
-
- return handle;
-}
-EXPORT_SYMBOL_GPL(iommu_sva_bind_device);
-
-/**
- * iommu_sva_unbind_device() - Remove a bond created with iommu_sva_bind_device
- * @handle: the handle returned by iommu_sva_bind_device()
- *
- * Put reference to a bond between device and address space. The device should
- * not be issuing any more transaction for this PASID. All outstanding page
- * requests for this PASID must have been flushed to the IOMMU.
- */
-void iommu_sva_unbind_device(struct iommu_sva *handle)
-{
- struct iommu_group *group;
- struct device *dev = handle->dev;
- const struct iommu_ops *ops = dev_iommu_ops(dev);
-
- if (!ops->sva_unbind)
- return;
-
- group = iommu_group_get(dev);
- if (!group)
- return;
-
- mutex_lock(&group->mutex);
- ops->sva_unbind(handle);
- mutex_unlock(&group->mutex);
-
- iommu_group_put(group);
-}
-EXPORT_SYMBOL_GPL(iommu_sva_unbind_device);
-
-u32 iommu_sva_get_pasid(struct iommu_sva *handle)
-{
- const struct iommu_ops *ops = dev_iommu_ops(handle->dev);
-
- if (!ops->sva_get_pasid)
- return IOMMU_PASID_INVALID;
-
- return ops->sva_get_pasid(handle);
-}
-EXPORT_SYMBOL_GPL(iommu_sva_get_pasid);
-
/*
* Changes the default domain of an iommu group that has *only* one device
*
@@ -3322,3 +3231,31 @@ void iommu_block_device_pasid(struct iommu_domain *domain, struct device *dev,

iommu_group_put(group);
}
+
+/*
+ * This is a variant of iommu_get_domain_for_dev(). It returns the existing
+ * domain attached to pasid of a device. It's only for internal use of the
+ * IOMMU subsystem. The caller must take care to avoid any possible
+ * use-after-free case.
+ */
+struct iommu_domain *
+iommu_get_domain_for_dev_pasid(struct device *dev, ioasid_t pasid)
+{
+ struct iommu_domain *domain;
+ struct iommu_group *group;
+
+ if (!pasid_valid(pasid))
+ return NULL;
+
+ group = iommu_group_get(dev);
+ if (!group)
+ return NULL;
+ /*
+ * The xarray protects its internal state with RCU. Hence the domain
+ * obtained is either NULL or fully formed.
+ */
+ domain = xa_load(&group->pasid_array, pasid);
+ iommu_group_put(group);
+
+ return domain;
+}
--
2.25.1



2022-05-20 14:23:18

by Lu Baolu

[permalink] [raw]
Subject: Re: [PATCH v7 06/10] iommu/sva: Refactoring iommu_sva_bind/unbind_device()

On 2022/5/20 00:39, Jean-Philippe Brucker wrote:
>> +struct iommu_sva *iommu_sva_bind_device(struct device *dev, struct mm_struct *mm)
>> +{
>> + struct iommu_sva_domain *sva_domain;
>> + struct iommu_domain *domain;
>> + ioasid_t max_pasid = 0;
>> + int ret = -EINVAL;
>> +
>> + /* Allocate mm->pasid if necessary. */
>> + if (!dev->iommu->iommu_dev->pasids)
>> + return ERR_PTR(-EOPNOTSUPP);
>> +
>> + if (dev_is_pci(dev)) {
>> + max_pasid = pci_max_pasids(to_pci_dev(dev));
>> + if (max_pasid < 0)
>> + return ERR_PTR(max_pasid);
>> + } else {
>> + ret = device_property_read_u32(dev, "pasid-num-bits",
>> + &max_pasid);
>> + if (ret)
>> + return ERR_PTR(ret);
>> + max_pasid = (1UL << max_pasid);
>> + }
> The IOMMU driver needs this PASID width information earlier, when creating
> the PASID table (in .probe_device(), .attach_dev()). Since we're moving it
> to the IOMMU core to avoid code duplication, it should be done earlier and
> stored in dev->iommu

Yes, really. How about below changes?

From f1382579e8a15ca49acdf758d38fd36451ea174d Mon Sep 17 00:00:00 2001
From: Lu Baolu <[email protected]>
Date: Mon, 28 Feb 2022 15:01:35 +0800
Subject: [PATCH 1/1] iommu: Add pasids field in struct dev_iommu

Use this field to save the number of PASIDs that a device is able to
consume. It is a generic attribute of a device and lifting it into the
per-device dev_iommu struct could help to avoid the boilerplate code
in various IOMMU drivers.

Signed-off-by: Lu Baolu <[email protected]>
---
drivers/iommu/iommu.c | 15 +++++++++++++++
include/linux/iommu.h | 2 ++
2 files changed, 17 insertions(+)

diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index e49c5a5b8cc1..6b731171d42f 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -20,6 +20,7 @@
#include <linux/idr.h>
#include <linux/err.h>
#include <linux/pci.h>
+#include <linux/pci-ats.h>
#include <linux/bitops.h>
#include <linux/property.h>
#include <linux/fsl/mc.h>
@@ -194,6 +195,8 @@ EXPORT_SYMBOL_GPL(iommu_device_unregister);
static struct dev_iommu *dev_iommu_get(struct device *dev)
{
struct dev_iommu *param = dev->iommu;
+ u32 max_pasids = 0;
+ int ret;

if (param)
return param;
@@ -202,6 +205,18 @@ static struct dev_iommu *dev_iommu_get(struct
device *dev)
if (!param)
return NULL;

+ if (dev_is_pci(dev)) {
+ ret = pci_max_pasids(to_pci_dev(dev));
+ if (ret > 0)
+ max_pasids = ret;
+ } else {
+ ret = device_property_read_u32(dev, "pasid-num-bits",
+ &max_pasids);
+ if (!ret)
+ max_pasids = (1UL << max_pasids);
+ }
+ param->pasids = max_pasids;
+
mutex_init(&param->lock);
dev->iommu = param;
return param;
diff --git a/include/linux/iommu.h b/include/linux/iommu.h
index 45f274b2640d..d4296136ba75 100644
--- a/include/linux/iommu.h
+++ b/include/linux/iommu.h
@@ -371,6 +371,7 @@ struct iommu_fault_param {
* @fwspec: IOMMU fwspec data
* @iommu_dev: IOMMU device this device is linked to
* @priv: IOMMU Driver private data
+ * @pasids: number of supported PASIDs
*
* TODO: migrate other per device data pointers under iommu_dev_data, e.g.
* struct iommu_group *iommu_group;
@@ -382,6 +383,7 @@ struct dev_iommu {
struct iommu_fwspec *fwspec;
struct iommu_device *iommu_dev;
void *priv;
+ u32 pasids;
};

int iommu_device_register(struct iommu_device *iommu,
--
2.25.1

Best regards,
baolu

2022-05-20 19:52:26

by Jean-Philippe Brucker

[permalink] [raw]
Subject: Re: [PATCH v7 06/10] iommu/sva: Refactoring iommu_sva_bind/unbind_device()

On Thu, May 19, 2022 at 03:20:43PM +0800, Lu Baolu wrote:
> The existing iommu SVA interfaces are implemented by calling the SVA
> specific iommu ops provided by the IOMMU drivers. There's no need for
> any SVA specific ops in iommu_ops vector anymore as we can achieve
> this through the generic attach/detach_dev_pasid domain ops.
>
> This refactors the IOMMU SVA interfaces implementation by using the
> set/block_pasid_dev ops and align them with the concept of the SVA
> iommu domain. Put the new SVA code in the sva related file in order
> to make it self-contained.
>
> Signed-off-by: Lu Baolu <[email protected]>
> ---
> include/linux/iommu.h | 48 ++++++++------
> drivers/iommu/iommu-sva-lib.h | 1 +
> drivers/iommu/iommu-sva-lib.c | 113 ++++++++++++++++++++++++++++++++
> drivers/iommu/iommu.c | 119 ++++++++--------------------------
> 4 files changed, 170 insertions(+), 111 deletions(-)
>
> diff --git a/include/linux/iommu.h b/include/linux/iommu.h
> index e8cf82d46ce1..d9ac5ebe5bbb 100644
> --- a/include/linux/iommu.h
> +++ b/include/linux/iommu.h
> @@ -635,6 +635,7 @@ struct iommu_fwspec {
> */
> struct iommu_sva {
> struct device *dev;
> + refcount_t users;
> };
>
> int iommu_fwspec_init(struct device *dev, struct fwnode_handle *iommu_fwnode,
> @@ -677,11 +678,6 @@ int iommu_dev_enable_feature(struct device *dev, enum iommu_dev_features f);
> int iommu_dev_disable_feature(struct device *dev, enum iommu_dev_features f);
> bool iommu_dev_feature_enabled(struct device *dev, enum iommu_dev_features f);
>
> -struct iommu_sva *iommu_sva_bind_device(struct device *dev,
> - struct mm_struct *mm);
> -void iommu_sva_unbind_device(struct iommu_sva *handle);
> -u32 iommu_sva_get_pasid(struct iommu_sva *handle);
> -
> int iommu_device_use_default_domain(struct device *dev);
> void iommu_device_unuse_default_domain(struct device *dev);
>
> @@ -693,6 +689,8 @@ 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);
> +struct iommu_domain *
> +iommu_get_domain_for_dev_pasid(struct device *dev, ioasid_t pasid);
> #else /* CONFIG_IOMMU_API */
>
> struct iommu_ops {};
> @@ -1023,21 +1021,6 @@ iommu_dev_disable_feature(struct device *dev, enum iommu_dev_features feat)
> return -ENODEV;
> }
>
> -static inline struct iommu_sva *
> -iommu_sva_bind_device(struct device *dev, struct mm_struct *mm)
> -{
> - return NULL;
> -}
> -
> -static inline void iommu_sva_unbind_device(struct iommu_sva *handle)
> -{
> -}
> -
> -static inline u32 iommu_sva_get_pasid(struct iommu_sva *handle)
> -{
> - return IOMMU_PASID_INVALID;
> -}
> -
> static inline struct iommu_fwspec *dev_iommu_fwspec_get(struct device *dev)
> {
> return NULL;
> @@ -1077,6 +1060,12 @@ static inline void iommu_block_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;
> +}
> #endif /* CONFIG_IOMMU_API */
>
> /**
> @@ -1108,6 +1097,10 @@ 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);
> +struct iommu_sva *iommu_sva_bind_device(struct device *dev,
> + struct mm_struct *mm);
> +void iommu_sva_unbind_device(struct iommu_sva *handle);
> +u32 iommu_sva_get_pasid(struct iommu_sva *handle);
> #else /* CONFIG_IOMMU_SVA */
> static inline struct iommu_domain *
> iommu_sva_alloc_domain(struct bus_type *bus, struct mm_struct *mm)
> @@ -1124,6 +1117,21 @@ static inline int iommu_sva_set_domain(struct iommu_domain *domain,
> {
> return -EINVAL;
> }
> +
> +static inline struct iommu_sva *
> +iommu_sva_bind_device(struct device *dev, struct mm_struct *mm)
> +{
> + return NULL;
> +}
> +
> +static inline void iommu_sva_unbind_device(struct iommu_sva *handle)
> +{
> +}
> +
> +static inline u32 iommu_sva_get_pasid(struct iommu_sva *handle)
> +{
> + return IOMMU_PASID_INVALID;
> +}
> #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 1be21e6b93ec..ebab5a8cb126 100644
> --- a/drivers/iommu/iommu-sva-lib.h
> +++ b/drivers/iommu/iommu-sva-lib.h
> @@ -20,6 +20,7 @@ struct iopf_queue;
> struct iommu_sva_domain {
> struct iommu_domain domain;
> struct mm_struct *mm;
> + struct iommu_sva bond;
> };
>
> #define to_sva_domain(d) container_of_safe(d, struct iommu_sva_domain, domain)
> diff --git a/drivers/iommu/iommu-sva-lib.c b/drivers/iommu/iommu-sva-lib.c
> index 210c376f6043..568e0f64edac 100644
> --- a/drivers/iommu/iommu-sva-lib.c
> +++ b/drivers/iommu/iommu-sva-lib.c
> @@ -4,6 +4,8 @@
> */
> #include <linux/mutex.h>
> #include <linux/sched/mm.h>
> +#include <linux/pci.h>
> +#include <linux/pci-ats.h>
>
> #include "iommu-sva-lib.h"
>
> @@ -117,3 +119,114 @@ int iommu_sva_set_domain(struct iommu_domain *domain, struct device *dev,
>
> return iommu_set_device_pasid(domain, dev, pasid);
> }
> +
> +/**
> + * 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 mm_users
> + *
> + * Create a bond between device and address space, allowing the device to access
> + * the mm using the returned PASID. If a bond already exists between @device and
> + * @mm, it is returned and an additional reference is taken. Caller must call
> + * iommu_sva_unbind_device() to release each reference.
> + *
> + * iommu_dev_enable_feature(dev, IOMMU_DEV_FEAT_SVA) must be called first, to
> + * initialize the required SVA features.
> + *
> + * On error, returns an ERR_PTR value.
> + */
> +struct iommu_sva *iommu_sva_bind_device(struct device *dev, struct mm_struct *mm)
> +{
> + struct iommu_sva_domain *sva_domain;
> + struct iommu_domain *domain;
> + ioasid_t max_pasid = 0;
> + int ret = -EINVAL;
> +
> + /* Allocate mm->pasid if necessary. */
> + if (!dev->iommu->iommu_dev->pasids)
> + return ERR_PTR(-EOPNOTSUPP);
> +
> + if (dev_is_pci(dev)) {
> + max_pasid = pci_max_pasids(to_pci_dev(dev));
> + if (max_pasid < 0)
> + return ERR_PTR(max_pasid);
> + } else {
> + ret = device_property_read_u32(dev, "pasid-num-bits",
> + &max_pasid);
> + if (ret)
> + return ERR_PTR(ret);
> + max_pasid = (1UL << max_pasid);
> + }

The IOMMU driver needs this PASID width information earlier, when creating
the PASID table (in .probe_device(), .attach_dev()). Since we're moving it
to the IOMMU core to avoid code duplication, it should be done earlier and
stored in dev->iommu

Thanks,
Jean

> + max_pasid = min_t(u32, max_pasid, dev->iommu->iommu_dev->pasids);
> + ret = iommu_sva_alloc_pasid(mm, 1, max_pasid - 1);
> + if (ret)
> + return ERR_PTR(ret);
> +
> + mutex_lock(&iommu_sva_lock);
> + /* Search for an existing domain. */
> + domain = iommu_get_domain_for_dev_pasid(dev, mm->pasid);
> + if (domain) {
> + sva_domain = to_sva_domain(domain);
> + refcount_inc(&sva_domain->bond.users);
> + goto out_success;
> + }
> +
> + /* Allocate a new domain and set it on device pasid. */
> + domain = iommu_sva_alloc_domain(dev->bus, mm);
> + if (IS_ERR(domain)) {
> + ret = PTR_ERR(domain);
> + goto out_unlock;
> + }
> +
> + ret = iommu_sva_set_domain(domain, dev, mm->pasid);
> + if (ret)
> + goto out_free_domain;
> + sva_domain = to_sva_domain(domain);
> + sva_domain->bond.dev = dev;
> + refcount_set(&sva_domain->bond.users, 1);
> +
> +out_success:
> + mutex_unlock(&iommu_sva_lock);
> + return &sva_domain->bond;
> +
> +out_free_domain:
> + iommu_sva_free_domain(domain);
> +out_unlock:
> + mutex_unlock(&iommu_sva_lock);
> +
> + return ERR_PTR(ret);
> +}
> +EXPORT_SYMBOL_GPL(iommu_sva_bind_device);
> +
> +/**
> + * iommu_sva_unbind_device() - Remove a bond created with iommu_sva_bind_device
> + * @handle: the handle returned by iommu_sva_bind_device()
> + *
> + * Put reference to a bond between device and address space. The device should
> + * not be issuing any more transaction for this PASID. All outstanding page
> + * requests for this PASID must have been flushed to the IOMMU.
> + */
> +void iommu_sva_unbind_device(struct iommu_sva *handle)
> +{
> + struct device *dev = handle->dev;
> + struct iommu_sva_domain *sva_domain =
> + container_of(handle, struct iommu_sva_domain, bond);
> + ioasid_t pasid = iommu_sva_get_pasid(handle);
> +
> + mutex_lock(&iommu_sva_lock);
> + if (refcount_dec_and_test(&sva_domain->bond.users)) {
> + iommu_block_device_pasid(&sva_domain->domain, dev, pasid);
> + iommu_sva_free_domain(&sva_domain->domain);
> + }
> + mutex_unlock(&iommu_sva_lock);
> +}
> +EXPORT_SYMBOL_GPL(iommu_sva_unbind_device);
> +
> +u32 iommu_sva_get_pasid(struct iommu_sva *handle)
> +{
> + struct iommu_sva_domain *sva_domain =
> + container_of(handle, struct iommu_sva_domain, bond);
> +
> + return sva_domain->mm->pasid;
> +}
> +EXPORT_SYMBOL_GPL(iommu_sva_get_pasid);
> diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
> index 789816e4b9d6..e49c5a5b8cc1 100644
> --- a/drivers/iommu/iommu.c
> +++ b/drivers/iommu/iommu.c
> @@ -2766,97 +2766,6 @@ bool iommu_dev_feature_enabled(struct device *dev, enum iommu_dev_features feat)
> }
> EXPORT_SYMBOL_GPL(iommu_dev_feature_enabled);
>
> -/**
> - * 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
> - *
> - * Create a bond between device and address space, allowing the device to access
> - * the mm using the returned PASID. If a bond already exists between @device and
> - * @mm, it is returned and an additional reference is taken. Caller must call
> - * iommu_sva_unbind_device() to release each reference.
> - *
> - * iommu_dev_enable_feature(dev, IOMMU_DEV_FEAT_SVA) must be called first, to
> - * initialize the required SVA features.
> - *
> - * On error, returns an ERR_PTR value.
> - */
> -struct iommu_sva *
> -iommu_sva_bind_device(struct device *dev, struct mm_struct *mm)
> -{
> - struct iommu_group *group;
> - struct iommu_sva *handle = ERR_PTR(-EINVAL);
> - const struct iommu_ops *ops = dev_iommu_ops(dev);
> -
> - if (!ops->sva_bind)
> - return ERR_PTR(-ENODEV);
> -
> - group = iommu_group_get(dev);
> - if (!group)
> - return ERR_PTR(-ENODEV);
> -
> - /* Ensure device count and domain don't change while we're binding */
> - mutex_lock(&group->mutex);
> -
> - /*
> - * To keep things simple, SVA currently doesn'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_device_count(group) != 1)
> - goto out_unlock;
> -
> - handle = ops->sva_bind(dev, mm);
> -
> -out_unlock:
> - mutex_unlock(&group->mutex);
> - iommu_group_put(group);
> -
> - return handle;
> -}
> -EXPORT_SYMBOL_GPL(iommu_sva_bind_device);
> -
> -/**
> - * iommu_sva_unbind_device() - Remove a bond created with iommu_sva_bind_device
> - * @handle: the handle returned by iommu_sva_bind_device()
> - *
> - * Put reference to a bond between device and address space. The device should
> - * not be issuing any more transaction for this PASID. All outstanding page
> - * requests for this PASID must have been flushed to the IOMMU.
> - */
> -void iommu_sva_unbind_device(struct iommu_sva *handle)
> -{
> - struct iommu_group *group;
> - struct device *dev = handle->dev;
> - const struct iommu_ops *ops = dev_iommu_ops(dev);
> -
> - if (!ops->sva_unbind)
> - return;
> -
> - group = iommu_group_get(dev);
> - if (!group)
> - return;
> -
> - mutex_lock(&group->mutex);
> - ops->sva_unbind(handle);
> - mutex_unlock(&group->mutex);
> -
> - iommu_group_put(group);
> -}
> -EXPORT_SYMBOL_GPL(iommu_sva_unbind_device);
> -
> -u32 iommu_sva_get_pasid(struct iommu_sva *handle)
> -{
> - const struct iommu_ops *ops = dev_iommu_ops(handle->dev);
> -
> - if (!ops->sva_get_pasid)
> - return IOMMU_PASID_INVALID;
> -
> - return ops->sva_get_pasid(handle);
> -}
> -EXPORT_SYMBOL_GPL(iommu_sva_get_pasid);
> -
> /*
> * Changes the default domain of an iommu group that has *only* one device
> *
> @@ -3322,3 +3231,31 @@ void iommu_block_device_pasid(struct iommu_domain *domain, struct device *dev,
>
> iommu_group_put(group);
> }
> +
> +/*
> + * This is a variant of iommu_get_domain_for_dev(). It returns the existing
> + * domain attached to pasid of a device. It's only for internal use of the
> + * IOMMU subsystem. The caller must take care to avoid any possible
> + * use-after-free case.
> + */
> +struct iommu_domain *
> +iommu_get_domain_for_dev_pasid(struct device *dev, ioasid_t pasid)
> +{
> + struct iommu_domain *domain;
> + struct iommu_group *group;
> +
> + if (!pasid_valid(pasid))
> + return NULL;
> +
> + group = iommu_group_get(dev);
> + if (!group)
> + return NULL;
> + /*
> + * The xarray protects its internal state with RCU. Hence the domain
> + * obtained is either NULL or fully formed.
> + */
> + domain = xa_load(&group->pasid_array, pasid);
> + iommu_group_put(group);
> +
> + return domain;
> +}
> --
> 2.25.1
>

2022-05-21 19:46:26

by Jean-Philippe Brucker

[permalink] [raw]
Subject: Re: [PATCH v7 06/10] iommu/sva: Refactoring iommu_sva_bind/unbind_device()

On Fri, May 20, 2022 at 02:38:12PM +0800, Baolu Lu wrote:
> On 2022/5/20 00:39, Jean-Philippe Brucker wrote:
> > > +struct iommu_sva *iommu_sva_bind_device(struct device *dev, struct mm_struct *mm)
> > > +{
> > > + struct iommu_sva_domain *sva_domain;
> > > + struct iommu_domain *domain;
> > > + ioasid_t max_pasid = 0;
> > > + int ret = -EINVAL;
> > > +
> > > + /* Allocate mm->pasid if necessary. */
> > > + if (!dev->iommu->iommu_dev->pasids)
> > > + return ERR_PTR(-EOPNOTSUPP);
> > > +
> > > + if (dev_is_pci(dev)) {
> > > + max_pasid = pci_max_pasids(to_pci_dev(dev));
> > > + if (max_pasid < 0)
> > > + return ERR_PTR(max_pasid);
> > > + } else {
> > > + ret = device_property_read_u32(dev, "pasid-num-bits",
> > > + &max_pasid);
> > > + if (ret)
> > > + return ERR_PTR(ret);
> > > + max_pasid = (1UL << max_pasid);
> > > + }
> > The IOMMU driver needs this PASID width information earlier, when creating
> > the PASID table (in .probe_device(), .attach_dev()). Since we're moving it
> > to the IOMMU core to avoid code duplication, it should be done earlier and
> > stored in dev->iommu
>
> Yes, really. How about below changes?
>
> From f1382579e8a15ca49acdf758d38fd36451ea174d Mon Sep 17 00:00:00 2001
> From: Lu Baolu <[email protected]>
> Date: Mon, 28 Feb 2022 15:01:35 +0800
> Subject: [PATCH 1/1] iommu: Add pasids field in struct dev_iommu
>
> Use this field to save the number of PASIDs that a device is able to
> consume. It is a generic attribute of a device and lifting it into the
> per-device dev_iommu struct could help to avoid the boilerplate code
> in various IOMMU drivers.
>
> Signed-off-by: Lu Baolu <[email protected]>
> ---
> drivers/iommu/iommu.c | 15 +++++++++++++++
> include/linux/iommu.h | 2 ++
> 2 files changed, 17 insertions(+)
>
> diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
> index e49c5a5b8cc1..6b731171d42f 100644
> --- a/drivers/iommu/iommu.c
> +++ b/drivers/iommu/iommu.c
> @@ -20,6 +20,7 @@
> #include <linux/idr.h>
> #include <linux/err.h>
> #include <linux/pci.h>
> +#include <linux/pci-ats.h>
> #include <linux/bitops.h>
> #include <linux/property.h>
> #include <linux/fsl/mc.h>
> @@ -194,6 +195,8 @@ EXPORT_SYMBOL_GPL(iommu_device_unregister);
> static struct dev_iommu *dev_iommu_get(struct device *dev)
> {
> struct dev_iommu *param = dev->iommu;
> + u32 max_pasids = 0;
> + int ret;
>
> if (param)
> return param;
> @@ -202,6 +205,18 @@ static struct dev_iommu *dev_iommu_get(struct device
> *dev)
> if (!param)
> return NULL;
>
> + if (dev_is_pci(dev)) {
> + ret = pci_max_pasids(to_pci_dev(dev));
> + if (ret > 0)
> + max_pasids = ret;
> + } else {
> + ret = device_property_read_u32(dev, "pasid-num-bits",
> + &max_pasids);
> + if (!ret)
> + max_pasids = (1UL << max_pasids);
> + }
> + param->pasids = max_pasids;
> +

we could also do a min() with the IOMMU PASID size here

> mutex_init(&param->lock);
> dev->iommu = param;
> return param;
> diff --git a/include/linux/iommu.h b/include/linux/iommu.h
> index 45f274b2640d..d4296136ba75 100644
> --- a/include/linux/iommu.h
> +++ b/include/linux/iommu.h
> @@ -371,6 +371,7 @@ struct iommu_fault_param {
> * @fwspec: IOMMU fwspec data
> * @iommu_dev: IOMMU device this device is linked to
> * @priv: IOMMU Driver private data
> + * @pasids: number of supported PASIDs

'max_pasids' to stay consistent?

Thanks,
Jean

> *
> * TODO: migrate other per device data pointers under iommu_dev_data, e.g.
> * struct iommu_group *iommu_group;
> @@ -382,6 +383,7 @@ struct dev_iommu {
> struct iommu_fwspec *fwspec;
> struct iommu_device *iommu_dev;
> void *priv;
> + u32 pasids;
> };
>
> int iommu_device_register(struct iommu_device *iommu,
> --
> 2.25.1
>
> Best regards,
> baolu

2022-05-23 07:41:56

by Lu Baolu

[permalink] [raw]
Subject: Re: [PATCH v7 06/10] iommu/sva: Refactoring iommu_sva_bind/unbind_device()

On 2022/5/20 19:28, Jean-Philippe Brucker wrote:
> On Fri, May 20, 2022 at 02:38:12PM +0800, Baolu Lu wrote:
>> On 2022/5/20 00:39, Jean-Philippe Brucker wrote:
>>>> +struct iommu_sva *iommu_sva_bind_device(struct device *dev, struct mm_struct *mm)
>>>> +{
>>>> + struct iommu_sva_domain *sva_domain;
>>>> + struct iommu_domain *domain;
>>>> + ioasid_t max_pasid = 0;
>>>> + int ret = -EINVAL;
>>>> +
>>>> + /* Allocate mm->pasid if necessary. */
>>>> + if (!dev->iommu->iommu_dev->pasids)
>>>> + return ERR_PTR(-EOPNOTSUPP);
>>>> +
>>>> + if (dev_is_pci(dev)) {
>>>> + max_pasid = pci_max_pasids(to_pci_dev(dev));
>>>> + if (max_pasid < 0)
>>>> + return ERR_PTR(max_pasid);
>>>> + } else {
>>>> + ret = device_property_read_u32(dev, "pasid-num-bits",
>>>> + &max_pasid);
>>>> + if (ret)
>>>> + return ERR_PTR(ret);
>>>> + max_pasid = (1UL << max_pasid);
>>>> + }
>>> The IOMMU driver needs this PASID width information earlier, when creating
>>> the PASID table (in .probe_device(), .attach_dev()). Since we're moving it
>>> to the IOMMU core to avoid code duplication, it should be done earlier and
>>> stored in dev->iommu
>> Yes, really. How about below changes?
>>
>> From f1382579e8a15ca49acdf758d38fd36451ea174d Mon Sep 17 00:00:00 2001
>> From: Lu Baolu<[email protected]>
>> Date: Mon, 28 Feb 2022 15:01:35 +0800
>> Subject: [PATCH 1/1] iommu: Add pasids field in struct dev_iommu
>>
>> Use this field to save the number of PASIDs that a device is able to
>> consume. It is a generic attribute of a device and lifting it into the
>> per-device dev_iommu struct could help to avoid the boilerplate code
>> in various IOMMU drivers.
>>
>> Signed-off-by: Lu Baolu<[email protected]>
>> ---
>> drivers/iommu/iommu.c | 15 +++++++++++++++
>> include/linux/iommu.h | 2 ++
>> 2 files changed, 17 insertions(+)
>>
>> diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
>> index e49c5a5b8cc1..6b731171d42f 100644
>> --- a/drivers/iommu/iommu.c
>> +++ b/drivers/iommu/iommu.c
>> @@ -20,6 +20,7 @@
>> #include <linux/idr.h>
>> #include <linux/err.h>
>> #include <linux/pci.h>
>> +#include <linux/pci-ats.h>
>> #include <linux/bitops.h>
>> #include <linux/property.h>
>> #include <linux/fsl/mc.h>
>> @@ -194,6 +195,8 @@ EXPORT_SYMBOL_GPL(iommu_device_unregister);
>> static struct dev_iommu *dev_iommu_get(struct device *dev)
>> {
>> struct dev_iommu *param = dev->iommu;
>> + u32 max_pasids = 0;
>> + int ret;
>>
>> if (param)
>> return param;
>> @@ -202,6 +205,18 @@ static struct dev_iommu *dev_iommu_get(struct device
>> *dev)
>> if (!param)
>> return NULL;
>>
>> + if (dev_is_pci(dev)) {
>> + ret = pci_max_pasids(to_pci_dev(dev));
>> + if (ret > 0)
>> + max_pasids = ret;
>> + } else {
>> + ret = device_property_read_u32(dev, "pasid-num-bits",
>> + &max_pasids);
>> + if (!ret)
>> + max_pasids = (1UL << max_pasids);
>> + }
>> + param->pasids = max_pasids;
>> +
> we could also do a min() with the IOMMU PASID size here
>
>> mutex_init(&param->lock);
>> dev->iommu = param;
>> return param;
>> diff --git a/include/linux/iommu.h b/include/linux/iommu.h
>> index 45f274b2640d..d4296136ba75 100644
>> --- a/include/linux/iommu.h
>> +++ b/include/linux/iommu.h
>> @@ -371,6 +371,7 @@ struct iommu_fault_param {
>> * @fwspec: IOMMU fwspec data
>> * @iommu_dev: IOMMU device this device is linked to
>> * @priv: IOMMU Driver private data
>> + * @pasids: number of supported PASIDs
> 'max_pasids' to stay consistent?

Both done. How about below changes?

From 008c73b9c0ad51a4a70a18d60361a76c28a63342 Mon Sep 17 00:00:00 2001
From: Lu Baolu <[email protected]>
Date: Mon, 28 Feb 2022 15:01:35 +0800
Subject: [PATCH 1/1] iommu: Add max_pasids field in struct dev_iommu

Use this field to save the number of PASIDs that a device is able to
consume. It is a generic attribute of a device and lifting it into the
per-device dev_iommu struct could help to avoid the boilerplate code
in various IOMMU drivers.

Signed-off-by: Lu Baolu <[email protected]>
---
drivers/iommu/iommu.c | 26 ++++++++++++++++++++++++++
include/linux/iommu.h | 2 ++
2 files changed, 28 insertions(+)

diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index 847ad47a2dfd..365d0f2b7f55 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -20,6 +20,7 @@
#include <linux/idr.h>
#include <linux/err.h>
#include <linux/pci.h>
+#include <linux/pci-ats.h>
#include <linux/bitops.h>
#include <linux/property.h>
#include <linux/fsl/mc.h>
@@ -218,6 +219,30 @@ static void dev_iommu_free(struct device *dev)
kfree(param);
}

+static u32 dev_iommu_get_max_pasids(struct device *dev)
+{
+ u32 max_pasids = dev->iommu->iommu_dev->max_pasids;
+ u32 num_bits;
+ int ret;
+
+ if (!max_pasids)
+ return 0;
+
+ if (dev_is_pci(dev)) {
+ ret = pci_max_pasids(to_pci_dev(dev));
+ if (ret < 0)
+ return 0;
+
+ return min_t(u32, max_pasids, ret);
+ }
+
+ ret = device_property_read_u32(dev, "pasid-num-bits", &num_bits);
+ if (ret)
+ return 0;
+
+ return min_t(u32, max_pasids, 1UL << num_bits);
+}
+
static int __iommu_probe_device(struct device *dev, struct list_head
*group_list)
{
const struct iommu_ops *ops = dev->bus->iommu_ops;
@@ -242,6 +267,7 @@ static int __iommu_probe_device(struct device *dev,
struct list_head *group_list
goto out_module_put;
}

+ iommu_dev->max_pasids = dev_iommu_get_max_pasids(dev);
dev->iommu->iommu_dev = iommu_dev;

group = iommu_group_get_for_dev(dev);
diff --git a/include/linux/iommu.h b/include/linux/iommu.h
index 03fbb1b71536..d50afb2c9a09 100644
--- a/include/linux/iommu.h
+++ b/include/linux/iommu.h
@@ -364,6 +364,7 @@ struct iommu_fault_param {
* @fwspec: IOMMU fwspec data
* @iommu_dev: IOMMU device this device is linked to
* @priv: IOMMU Driver private data
+ * @max_pasids: number of PASIDs device can consume
*
* TODO: migrate other per device data pointers under iommu_dev_data, e.g.
* struct iommu_group *iommu_group;
@@ -375,6 +376,7 @@ struct dev_iommu {
struct iommu_fwspec *fwspec;
struct iommu_device *iommu_dev;
void *priv;
+ u32 max_pasids;
};

int iommu_device_register(struct iommu_device *iommu,

Best regards,
baolu

2022-05-24 16:30:46

by Jean-Philippe Brucker

[permalink] [raw]
Subject: Re: [PATCH v7 06/10] iommu/sva: Refactoring iommu_sva_bind/unbind_device()

On Tue, May 24, 2022 at 10:22:28AM +0000, Tian, Kevin wrote:
> > From: Lu Baolu <[email protected]>
> > Sent: Thursday, May 19, 2022 3:21 PM
> >
> > The existing iommu SVA interfaces are implemented by calling the SVA
> > specific iommu ops provided by the IOMMU drivers. There's no need for
> > any SVA specific ops in iommu_ops vector anymore as we can achieve
> > this through the generic attach/detach_dev_pasid domain ops.
>
> set/block_pasid_dev, to be consistent.
>
> > +
> > + mutex_lock(&iommu_sva_lock);
> > + /* Search for an existing domain. */
> > + domain = iommu_get_domain_for_dev_pasid(dev, mm->pasid);
> > + if (domain) {
> > + sva_domain = to_sva_domain(domain);
> > + refcount_inc(&sva_domain->bond.users);
> > + goto out_success;
> > + }
> > +
>
> why would one device/pasid be bound to a mm more than once?

Device drivers can call bind() multiple times for the same device and mm,
for example if one process wants to open multiple accelerator queues.

Thanks,
Jean

2022-05-25 01:17:55

by Tian, Kevin

[permalink] [raw]
Subject: RE: [PATCH v7 06/10] iommu/sva: Refactoring iommu_sva_bind/unbind_device()

> From: Lu Baolu <[email protected]>
> Sent: Thursday, May 19, 2022 3:21 PM
>
> The existing iommu SVA interfaces are implemented by calling the SVA
> specific iommu ops provided by the IOMMU drivers. There's no need for
> any SVA specific ops in iommu_ops vector anymore as we can achieve
> this through the generic attach/detach_dev_pasid domain ops.

set/block_pasid_dev, to be consistent.

> +
> + mutex_lock(&iommu_sva_lock);
> + /* Search for an existing domain. */
> + domain = iommu_get_domain_for_dev_pasid(dev, mm->pasid);
> + if (domain) {
> + sva_domain = to_sva_domain(domain);
> + refcount_inc(&sva_domain->bond.users);
> + goto out_success;
> + }
> +

why would one device/pasid be bound to a mm more than once?

2022-05-25 13:09:32

by Tian, Kevin

[permalink] [raw]
Subject: RE: [PATCH v7 06/10] iommu/sva: Refactoring iommu_sva_bind/unbind_device()

> From: Jean-Philippe Brucker <[email protected]>
> Sent: Tuesday, May 24, 2022 6:58 PM
>
> On Tue, May 24, 2022 at 10:22:28AM +0000, Tian, Kevin wrote:
> > > From: Lu Baolu <[email protected]>
> > > Sent: Thursday, May 19, 2022 3:21 PM
> > >
> > > The existing iommu SVA interfaces are implemented by calling the SVA
> > > specific iommu ops provided by the IOMMU drivers. There's no need for
> > > any SVA specific ops in iommu_ops vector anymore as we can achieve
> > > this through the generic attach/detach_dev_pasid domain ops.
> >
> > set/block_pasid_dev, to be consistent.
> >
> > > +
> > > + mutex_lock(&iommu_sva_lock);
> > > + /* Search for an existing domain. */
> > > + domain = iommu_get_domain_for_dev_pasid(dev, mm->pasid);
> > > + if (domain) {
> > > + sva_domain = to_sva_domain(domain);
> > > + refcount_inc(&sva_domain->bond.users);
> > > + goto out_success;
> > > + }
> > > +
> >
> > why would one device/pasid be bound to a mm more than once?
>
> Device drivers can call bind() multiple times for the same device and mm,
> for example if one process wants to open multiple accelerator queues.
>

Is it clearer to have a sva_bond_get/put() pair instead of calling
bind() multiple times here?

2022-05-26 07:38:33

by Jean-Philippe Brucker

[permalink] [raw]
Subject: Re: [PATCH v7 06/10] iommu/sva: Refactoring iommu_sva_bind/unbind_device()

On Wed, May 25, 2022 at 02:04:49AM +0000, Tian, Kevin wrote:
> > From: Jean-Philippe Brucker <[email protected]>
> > Sent: Tuesday, May 24, 2022 6:58 PM
> >
> > On Tue, May 24, 2022 at 10:22:28AM +0000, Tian, Kevin wrote:
> > > > From: Lu Baolu <[email protected]>
> > > > Sent: Thursday, May 19, 2022 3:21 PM
> > > >
> > > > The existing iommu SVA interfaces are implemented by calling the SVA
> > > > specific iommu ops provided by the IOMMU drivers. There's no need for
> > > > any SVA specific ops in iommu_ops vector anymore as we can achieve
> > > > this through the generic attach/detach_dev_pasid domain ops.
> > >
> > > set/block_pasid_dev, to be consistent.
> > >
> > > > +
> > > > + mutex_lock(&iommu_sva_lock);
> > > > + /* Search for an existing domain. */
> > > > + domain = iommu_get_domain_for_dev_pasid(dev, mm->pasid);
> > > > + if (domain) {
> > > > + sva_domain = to_sva_domain(domain);
> > > > + refcount_inc(&sva_domain->bond.users);
> > > > + goto out_success;
> > > > + }
> > > > +
> > >
> > > why would one device/pasid be bound to a mm more than once?
> >
> > Device drivers can call bind() multiple times for the same device and mm,
> > for example if one process wants to open multiple accelerator queues.
> >
>
> Is it clearer to have a sva_bond_get/put() pair instead of calling
> bind() multiple times here?

I don't think it's clearer, and it would force device drivers to keep
track of {dev, mm} pairs, when the IOMMU subsystem already does that.
At the moment a device driver calls

bond = iommu_sva_bind_device(dev, mm)

for each ADI that it wants to assign to userspace. If a process happens to
want multiple ADIs on one device, then the {dev, mm} parameters are the
same and bind() returns the same bond. Since the IOMMU driver needs to
track these anyway, it might as well refcount them.

Thanks,
Jean



2022-06-02 09:20:49

by Tian, Kevin

[permalink] [raw]
Subject: RE: [PATCH v7 06/10] iommu/sva: Refactoring iommu_sva_bind/unbind_device()

> From: Jean-Philippe Brucker <[email protected]>
> Sent: Wednesday, May 25, 2022 3:30 PM
>
> On Wed, May 25, 2022 at 02:04:49AM +0000, Tian, Kevin wrote:
> > > From: Jean-Philippe Brucker <[email protected]>
> > > Sent: Tuesday, May 24, 2022 6:58 PM
> > >
> > > On Tue, May 24, 2022 at 10:22:28AM +0000, Tian, Kevin wrote:
> > > > > From: Lu Baolu <[email protected]>
> > > > > Sent: Thursday, May 19, 2022 3:21 PM
> > > > >
> > > > > The existing iommu SVA interfaces are implemented by calling the SVA
> > > > > specific iommu ops provided by the IOMMU drivers. There's no need
> for
> > > > > any SVA specific ops in iommu_ops vector anymore as we can achieve
> > > > > this through the generic attach/detach_dev_pasid domain ops.
> > > >
> > > > set/block_pasid_dev, to be consistent.
> > > >
> > > > > +
> > > > > + mutex_lock(&iommu_sva_lock);
> > > > > + /* Search for an existing domain. */
> > > > > + domain = iommu_get_domain_for_dev_pasid(dev, mm-
> >pasid);
> > > > > + if (domain) {
> > > > > + sva_domain = to_sva_domain(domain);
> > > > > + refcount_inc(&sva_domain->bond.users);
> > > > > + goto out_success;
> > > > > + }
> > > > > +
> > > >
> > > > why would one device/pasid be bound to a mm more than once?
> > >
> > > Device drivers can call bind() multiple times for the same device and mm,
> > > for example if one process wants to open multiple accelerator queues.
> > >
> >
> > Is it clearer to have a sva_bond_get/put() pair instead of calling
> > bind() multiple times here?
>
> I don't think it's clearer, and it would force device drivers to keep
> track of {dev, mm} pairs, when the IOMMU subsystem already does that.
> At the moment a device driver calls
>
> bond = iommu_sva_bind_device(dev, mm)
>
> for each ADI that it wants to assign to userspace. If a process happens to
> want multiple ADIs on one device, then the {dev, mm} parameters are the
> same and bind() returns the same bond. Since the IOMMU driver needs to
> track these anyway, it might as well refcount them.
>

My impression was that when an interface returns an object
then further reference to this object is usually directly acquired
on the object hence requires the caller to track it. But not a
strong opinion here if others all agree to favor simplicity for
the caller side.

Thanks
Kevin