diff mbox series

[v8,2/7] iommu: Move global PASID allocation from SVA to core

Message ID 20230602182212.150825-3-jacob.jun.pan@linux.intel.com (mailing list archive)
State Superseded
Headers show
Series Re-enable IDXD kernel workqueue under DMA API | expand

Commit Message

Jacob Pan June 2, 2023, 6:22 p.m. UTC
Global PASID can be used beyond SVA. For example, drivers that use
Intel ENQCMD to submit work must use global PASIDs in that PASID
is stored in a per CPU MSR. When such device need to submit work
for in-kernel DMA with PASID, it must allocate PASIDs from the same
global number space to avoid conflict.

This patch moves global PASID allocation APIs from SVA to IOMMU APIs.
Reserved PASIDs, currently only RID_PASID, are excluded from the global
PASID allocation.

It is expected that device drivers will use the allocated PASIDs to
attach to appropriate IOMMU domains for use.

Reviewed-by: Lu Baolu <baolu.lu@linux.intel.com>
Reviewed-by: Kevin Tian <kevin.tian@intel.com>
Signed-off-by: Jacob Pan <jacob.jun.pan@linux.intel.com>
---
v8: fix off-by-one in max_pasid check
v7: simplify range check (Baolu)
v6: explicitly exclude reserved a range from SVA PASID allocation
    check mm PASID compatibility with device
v5: move PASID range check inside API so that device drivers only pass
    in struct device* (Kevin)
v4: move dummy functions outside ifdef CONFIG_IOMMU_SVA (Baolu)
---
 drivers/iommu/iommu-sva.c | 28 ++++++++++------------------
 drivers/iommu/iommu.c     | 28 ++++++++++++++++++++++++++++
 include/linux/iommu.h     | 10 ++++++++++
 3 files changed, 48 insertions(+), 18 deletions(-)

Comments

Baolu Lu June 10, 2023, 12:13 p.m. UTC | #1
On 6/3/23 2:22 AM, Jacob Pan wrote:
> +ioasid_t iommu_alloc_global_pasid_dev(struct device *dev)
> +{
> +	int ret;
> +	ioasid_t max;
> +
> +	max = dev->iommu->max_pasids;
> +	/*
> +	 * max_pasids is set up by vendor driver based on number of PASID bits
> +	 * supported but the IDA allocation is inclusive.
> +	 */
> +	ret = ida_alloc_range(&iommu_global_pasid_ida, IOMMU_FIRST_GLOBAL_PASID, max - 1, GFP_KERNEL);
> +	if (ret < 0)
> +		return IOMMU_PASID_INVALID;
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(iommu_alloc_global_pasid_dev);

"dev->iommu->max_pasids == 0" indicates no pasid support on the device.
The code should return IOMMU_PASID_INVALID explicitly. Perhaps we can
make this function like this:

ioasid_t iommu_alloc_global_pasid_dev(struct device *dev)
{
         int ret;

         if (!dev->iommu->max_pasids)
                 return IOMMU_PASID_INVALID;

         /*
          * max_pasids is set up by vendor driver based on number of 
PASID bits
          * supported but the IDA allocation is inclusive.
          */
         ret = ida_alloc_range(&iommu_global_pasid_ida, 
IOMMU_FIRST_GLOBAL_PASID,
                               dev->iommu->max_pasids - 1, GFP_KERNEL);

         return ret < 0 ? IOMMU_PASID_INVALID : ret;
}
EXPORT_SYMBOL_GPL(iommu_alloc_global_pasid_dev);

Other change in this series looks good to me.

I hope I can queue this series including above change as part of my VT-d
update for v6.5 to Joerg if no objection.

Let's try to re-enable this key feature of Intel idxd driver in v6.5.

Best regards,
baolu
Baolu Lu June 13, 2023, 3:06 a.m. UTC | #2
On 6/10/23 8:13 PM, Baolu Lu wrote:
> On 6/3/23 2:22 AM, Jacob Pan wrote:
>> +ioasid_t iommu_alloc_global_pasid_dev(struct device *dev)
>> +{
>> +    int ret;
>> +    ioasid_t max;
>> +
>> +    max = dev->iommu->max_pasids;
>> +    /*
>> +     * max_pasids is set up by vendor driver based on number of PASID 
>> bits
>> +     * supported but the IDA allocation is inclusive.
>> +     */
>> +    ret = ida_alloc_range(&iommu_global_pasid_ida, 
>> IOMMU_FIRST_GLOBAL_PASID, max - 1, GFP_KERNEL);
>> +    if (ret < 0)
>> +        return IOMMU_PASID_INVALID;
>> +
>> +    return ret;
>> +}
>> +EXPORT_SYMBOL_GPL(iommu_alloc_global_pasid_dev);
> 
> "dev->iommu->max_pasids == 0" indicates no pasid support on the device.
> The code should return IOMMU_PASID_INVALID explicitly. Perhaps we can
> make this function like this:
> 
> ioasid_t iommu_alloc_global_pasid_dev(struct device *dev)
> {
>          int ret;
> 
>          if (!dev->iommu->max_pasids)
>                  return IOMMU_PASID_INVALID;
> 
>          /*
>           * max_pasids is set up by vendor driver based on number of 
> PASID bits
>           * supported but the IDA allocation is inclusive.
>           */
>          ret = ida_alloc_range(&iommu_global_pasid_ida, 
> IOMMU_FIRST_GLOBAL_PASID,
>                                dev->iommu->max_pasids - 1, GFP_KERNEL);
> 
>          return ret < 0 ? IOMMU_PASID_INVALID : ret;
> }
> EXPORT_SYMBOL_GPL(iommu_alloc_global_pasid_dev);
> 
> Other change in this series looks good to me.
> 
> I hope I can queue this series including above change as part of my VT-d
> update for v6.5 to Joerg if no objection.
> 
> Let's try to re-enable this key feature of Intel idxd driver in v6.5.

This series didn't pass my test.

The first time when I run "idxd_ktest.sh -c 1 -t 1 -i 100 -m shared", it
passed. But when I run it again, the idxd hardware operation resulted in
timed-out issues.

I have removed this series from my queue.

Best regards,
baolu
Jacob Pan June 14, 2023, 5:19 p.m. UTC | #3
Hi Baolu,

On Tue, 13 Jun 2023 11:06:03 +0800, Baolu Lu <baolu.lu@linux.intel.com>
wrote:

> On 6/10/23 8:13 PM, Baolu Lu wrote:
> > On 6/3/23 2:22 AM, Jacob Pan wrote:  
> >> +ioasid_t iommu_alloc_global_pasid_dev(struct device *dev)
> >> +{
> >> +    int ret;
> >> +    ioasid_t max;
> >> +
> >> +    max = dev->iommu->max_pasids;
> >> +    /*
> >> +     * max_pasids is set up by vendor driver based on number of PASID 
> >> bits
> >> +     * supported but the IDA allocation is inclusive.
> >> +     */
> >> +    ret = ida_alloc_range(&iommu_global_pasid_ida, 
> >> IOMMU_FIRST_GLOBAL_PASID, max - 1, GFP_KERNEL);
> >> +    if (ret < 0)
> >> +        return IOMMU_PASID_INVALID;
> >> +
> >> +    return ret;
> >> +}
> >> +EXPORT_SYMBOL_GPL(iommu_alloc_global_pasid_dev);  
> > 
> > "dev->iommu->max_pasids == 0" indicates no pasid support on the device.
> > The code should return IOMMU_PASID_INVALID explicitly. Perhaps we can
> > make this function like this:
> > 
> > ioasid_t iommu_alloc_global_pasid_dev(struct device *dev)
> > {
> >          int ret;
> > 
> >          if (!dev->iommu->max_pasids)
> >                  return IOMMU_PASID_INVALID;
> > 
> >          /*
> >           * max_pasids is set up by vendor driver based on number of 
> > PASID bits
> >           * supported but the IDA allocation is inclusive.
> >           */
> >          ret = ida_alloc_range(&iommu_global_pasid_ida, 
> > IOMMU_FIRST_GLOBAL_PASID,
> >                                dev->iommu->max_pasids - 1, GFP_KERNEL);
> > 
> >          return ret < 0 ? IOMMU_PASID_INVALID : ret;
> > }
> > EXPORT_SYMBOL_GPL(iommu_alloc_global_pasid_dev);
> > 
> > Other change in this series looks good to me.
> > 
> > I hope I can queue this series including above change as part of my VT-d
> > update for v6.5 to Joerg if no objection.
> > 
> > Let's try to re-enable this key feature of Intel idxd driver in v6.5.  
> 
> This series didn't pass my test.
> 
> The first time when I run "idxd_ktest.sh -c 1 -t 1 -i 100 -m shared", it
> passed. But when I run it again, the idxd hardware operation resulted in
> timed-out issues.
> 
Hmm, not sure what happened. Are you using the out of tree idxd_ktest
kernel module or the dmaengine test sysfs?

e.g.
echo "Config params for DMA test"                   
echo $1 > /sys/module/dmatest/parameters/iterations 
#echo 1 > /sys/module/dmatest/parameters/noverify   
echo "" > /sys/module/dmatest/parameters/channel    
echo 1 > /sys/module/dmatest/parameters/run         
sleep 2                                             
echo 0 > /sys/module/dmatest/parameters/run         
echo "Completed!"                                   

It passed my test many iterations for shared
in-kernel DSA test, will get your tree and test again.


Thanks,

Jacob
Jacob Pan July 11, 2023, 2:32 p.m. UTC | #4
Hi Baolu,

On Sat, 10 Jun 2023 20:13:03 +0800, Baolu Lu <baolu.lu@linux.intel.com>
wrote:

> On 6/3/23 2:22 AM, Jacob Pan wrote:
> > +ioasid_t iommu_alloc_global_pasid_dev(struct device *dev)
> > +{
> > +	int ret;
> > +	ioasid_t max;
> > +
> > +	max = dev->iommu->max_pasids;
> > +	/*
> > +	 * max_pasids is set up by vendor driver based on number of
> > PASID bits
> > +	 * supported but the IDA allocation is inclusive.
> > +	 */
> > +	ret = ida_alloc_range(&iommu_global_pasid_ida,
> > IOMMU_FIRST_GLOBAL_PASID, max - 1, GFP_KERNEL);
> > +	if (ret < 0)
> > +		return IOMMU_PASID_INVALID;
> > +
> > +	return ret;
> > +}
> > +EXPORT_SYMBOL_GPL(iommu_alloc_global_pasid_dev);  
> 
> "dev->iommu->max_pasids == 0" indicates no pasid support on the device.
> The code should return IOMMU_PASID_INVALID explicitly. Perhaps we can
> make this function like this:
> 
> ioasid_t iommu_alloc_global_pasid_dev(struct device *dev)
> {
>          int ret;
> 
>          if (!dev->iommu->max_pasids)
>                  return IOMMU_PASID_INVALID;
> 
>          /*
>           * max_pasids is set up by vendor driver based on number of 
> PASID bits
>           * supported but the IDA allocation is inclusive.
>           */
>          ret = ida_alloc_range(&iommu_global_pasid_ida, 
> IOMMU_FIRST_GLOBAL_PASID,
>                                dev->iommu->max_pasids - 1, GFP_KERNEL);
> 
>          return ret < 0 ? IOMMU_PASID_INVALID : ret;
> }
> EXPORT_SYMBOL_GPL(iommu_alloc_global_pasid_dev);
> 
good catch, sorry i missed this. let me send an updated version.

> Other change in this series looks good to me.
> 
> I hope I can queue this series including above change as part of my VT-d
> update for v6.5 to Joerg if no objection.
> 
> Let's try to re-enable this key feature of Intel idxd driver in v6.5.
> 
> Best regards,
> baolu


Thanks,

Jacob
diff mbox series

Patch

diff --git a/drivers/iommu/iommu-sva.c b/drivers/iommu/iommu-sva.c
index 9821bc44f5ac..b033cc415a00 100644
--- a/drivers/iommu/iommu-sva.c
+++ b/drivers/iommu/iommu-sva.c
@@ -10,33 +10,30 @@ 
 #include "iommu-sva.h"
 
 static DEFINE_MUTEX(iommu_sva_lock);
-static DEFINE_IDA(iommu_global_pasid_ida);
 
 /* Allocate a PASID for the mm within range (inclusive) */
-static int iommu_sva_alloc_pasid(struct mm_struct *mm, ioasid_t min, ioasid_t max)
+static int iommu_sva_alloc_pasid(struct mm_struct *mm, struct device *dev)
 {
+	ioasid_t pasid;
 	int ret = 0;
 
-	if (min == IOMMU_PASID_INVALID ||
-	    max == IOMMU_PASID_INVALID ||
-	    min == 0 || max < min)
-		return -EINVAL;
-
 	if (!arch_pgtable_dma_compat(mm))
 		return -EBUSY;
 
 	mutex_lock(&iommu_sva_lock);
 	/* Is a PASID already associated with this mm? */
 	if (mm_valid_pasid(mm)) {
-		if (mm->pasid < min || mm->pasid > max)
+		if (mm->pasid >= dev->iommu->max_pasids)
 			ret = -EOVERFLOW;
 		goto out;
 	}
 
-	ret = ida_alloc_range(&iommu_global_pasid_ida, min, max, GFP_KERNEL);
-	if (ret < min)
+	pasid = iommu_alloc_global_pasid_dev(dev);
+	if (pasid == IOMMU_PASID_INVALID) {
+		ret = -ENOSPC;
 		goto out;
-	mm->pasid = ret;
+	}
+	mm->pasid = pasid;
 	ret = 0;
 out:
 	mutex_unlock(&iommu_sva_lock);
@@ -63,15 +60,10 @@  struct iommu_sva *iommu_sva_bind_device(struct device *dev, struct mm_struct *mm
 {
 	struct iommu_domain *domain;
 	struct iommu_sva *handle;
-	ioasid_t max_pasids;
 	int ret;
 
-	max_pasids = dev->iommu->max_pasids;
-	if (!max_pasids)
-		return ERR_PTR(-EOPNOTSUPP);
-
 	/* Allocate mm->pasid if necessary. */
-	ret = iommu_sva_alloc_pasid(mm, 1, max_pasids - 1);
+	ret = iommu_sva_alloc_pasid(mm, dev);
 	if (ret)
 		return ERR_PTR(ret);
 
@@ -216,5 +208,5 @@  void mm_pasid_drop(struct mm_struct *mm)
 	if (likely(!mm_valid_pasid(mm)))
 		return;
 
-	ida_free(&iommu_global_pasid_ida, mm->pasid);
+	iommu_free_global_pasid(mm->pasid);
 }
diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index f1dcfa3f1a1b..d4f9ab210d6b 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -39,6 +39,7 @@ 
 
 static struct kset *iommu_group_kset;
 static DEFINE_IDA(iommu_group_ida);
+static DEFINE_IDA(iommu_global_pasid_ida);
 
 static unsigned int iommu_def_domain_type __read_mostly;
 static bool iommu_dma_strict __read_mostly = IS_ENABLED(CONFIG_IOMMU_DEFAULT_DMA_STRICT);
@@ -3393,3 +3394,30 @@  struct iommu_domain *iommu_sva_domain_alloc(struct device *dev,
 
 	return domain;
 }
+
+ioasid_t iommu_alloc_global_pasid_dev(struct device *dev)
+{
+	int ret;
+	ioasid_t max;
+
+	max = dev->iommu->max_pasids;
+	/*
+	 * max_pasids is set up by vendor driver based on number of PASID bits
+	 * supported but the IDA allocation is inclusive.
+	 */
+	ret = ida_alloc_range(&iommu_global_pasid_ida, IOMMU_FIRST_GLOBAL_PASID, max - 1, GFP_KERNEL);
+	if (ret < 0)
+		return IOMMU_PASID_INVALID;
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(iommu_alloc_global_pasid_dev);
+
+void iommu_free_global_pasid(ioasid_t pasid)
+{
+	if (WARN_ON(pasid == IOMMU_PASID_INVALID))
+		return;
+
+	ida_free(&iommu_global_pasid_ida, pasid);
+}
+EXPORT_SYMBOL_GPL(iommu_free_global_pasid);
diff --git a/include/linux/iommu.h b/include/linux/iommu.h
index c714d659d114..f7bfe03bda19 100644
--- a/include/linux/iommu.h
+++ b/include/linux/iommu.h
@@ -191,6 +191,7 @@  enum iommu_dev_features {
 };
 
 #define IOMMU_NO_PASID	(0U) /* Reserved for DMA w/o PASID */
+#define IOMMU_FIRST_GLOBAL_PASID	(1U) /*starting range for allocation */
 #define IOMMU_PASID_INVALID	(-1U)
 typedef unsigned int ioasid_t;
 
@@ -722,6 +723,8 @@  void iommu_detach_device_pasid(struct iommu_domain *domain,
 struct iommu_domain *
 iommu_get_domain_for_dev_pasid(struct device *dev, ioasid_t pasid,
 			       unsigned int type);
+ioasid_t iommu_alloc_global_pasid_dev(struct device *dev);
+void iommu_free_global_pasid(ioasid_t pasid);
 #else /* CONFIG_IOMMU_API */
 
 struct iommu_ops {};
@@ -1083,6 +1086,13 @@  iommu_get_domain_for_dev_pasid(struct device *dev, ioasid_t pasid,
 {
 	return NULL;
 }
+
+static inline ioasid_t iommu_alloc_global_pasid_dev(struct device *dev)
+{
+	return IOMMU_PASID_INVALID;
+}
+
+static inline void iommu_free_global_pasid(ioasid_t pasid) {}
 #endif /* CONFIG_IOMMU_API */
 
 /**