iommu: Move global PASID allocation from SVA to core
Intel ENQCMD requires a single PASID to be shared between multiple devices, as the PASID is stored in a single MSR register per-process and userspace can use only that one PASID. This means that the PASID allocation for any ENQCMD using device driver must always come from a shared global pool, regardless of what kind of domain the PASID will be used with. Split the code for the global PASID allocator into iommu_alloc/free_global_pasid() so that drivers can attach non-SVA domains to PASIDs as well. 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> Reviewed-by: Jason Gunthorpe <jgg@nvidia.com> Signed-off-by: Jacob Pan <jacob.jun.pan@linux.intel.com> Link: https://lore.kernel.org/r/20230802212427.1497170-3-jacob.jun.pan@linux.intel.com Signed-off-by: Lu Baolu <baolu.lu@linux.intel.com> Signed-off-by: Joerg Roedel <jroedel@suse.de>
This commit is contained in:
parent
4298780126
commit
2dcebc7ddc
@ -10,34 +10,30 @@
|
|||||||
#include "iommu-sva.h"
|
#include "iommu-sva.h"
|
||||||
|
|
||||||
static DEFINE_MUTEX(iommu_sva_lock);
|
static DEFINE_MUTEX(iommu_sva_lock);
|
||||||
static DEFINE_IDA(iommu_global_pasid_ida);
|
|
||||||
|
|
||||||
/* Allocate a PASID for the mm within range (inclusive) */
|
/* 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;
|
int ret = 0;
|
||||||
|
|
||||||
if (min == IOMMU_PASID_INVALID ||
|
|
||||||
max == IOMMU_PASID_INVALID ||
|
|
||||||
min == 0 || max < min)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
if (!arch_pgtable_dma_compat(mm))
|
if (!arch_pgtable_dma_compat(mm))
|
||||||
return -EBUSY;
|
return -EBUSY;
|
||||||
|
|
||||||
mutex_lock(&iommu_sva_lock);
|
mutex_lock(&iommu_sva_lock);
|
||||||
/* Is a PASID already associated with this mm? */
|
/* Is a PASID already associated with this mm? */
|
||||||
if (mm_valid_pasid(mm)) {
|
if (mm_valid_pasid(mm)) {
|
||||||
if (mm->pasid < min || mm->pasid > max)
|
if (mm->pasid >= dev->iommu->max_pasids)
|
||||||
ret = -EOVERFLOW;
|
ret = -EOVERFLOW;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = ida_alloc_range(&iommu_global_pasid_ida, min, max, GFP_KERNEL);
|
pasid = iommu_alloc_global_pasid(dev);
|
||||||
if (ret < 0)
|
if (pasid == IOMMU_PASID_INVALID) {
|
||||||
|
ret = -ENOSPC;
|
||||||
goto out;
|
goto out;
|
||||||
|
}
|
||||||
mm->pasid = ret;
|
mm->pasid = pasid;
|
||||||
ret = 0;
|
ret = 0;
|
||||||
out:
|
out:
|
||||||
mutex_unlock(&iommu_sva_lock);
|
mutex_unlock(&iommu_sva_lock);
|
||||||
@ -64,15 +60,10 @@ struct iommu_sva *iommu_sva_bind_device(struct device *dev, struct mm_struct *mm
|
|||||||
{
|
{
|
||||||
struct iommu_domain *domain;
|
struct iommu_domain *domain;
|
||||||
struct iommu_sva *handle;
|
struct iommu_sva *handle;
|
||||||
ioasid_t max_pasids;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
max_pasids = dev->iommu->max_pasids;
|
|
||||||
if (!max_pasids)
|
|
||||||
return ERR_PTR(-EOPNOTSUPP);
|
|
||||||
|
|
||||||
/* Allocate mm->pasid if necessary. */
|
/* Allocate mm->pasid if necessary. */
|
||||||
ret = iommu_sva_alloc_pasid(mm, 1, max_pasids - 1);
|
ret = iommu_sva_alloc_pasid(mm, dev);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ERR_PTR(ret);
|
return ERR_PTR(ret);
|
||||||
|
|
||||||
@ -217,5 +208,5 @@ void mm_pasid_drop(struct mm_struct *mm)
|
|||||||
if (likely(!mm_valid_pasid(mm)))
|
if (likely(!mm_valid_pasid(mm)))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ida_free(&iommu_global_pasid_ida, mm->pasid);
|
iommu_free_global_pasid(mm->pasid);
|
||||||
}
|
}
|
||||||
|
@ -39,6 +39,7 @@
|
|||||||
|
|
||||||
static struct kset *iommu_group_kset;
|
static struct kset *iommu_group_kset;
|
||||||
static DEFINE_IDA(iommu_group_ida);
|
static DEFINE_IDA(iommu_group_ida);
|
||||||
|
static DEFINE_IDA(iommu_global_pasid_ida);
|
||||||
|
|
||||||
static unsigned int iommu_def_domain_type __read_mostly;
|
static unsigned int iommu_def_domain_type __read_mostly;
|
||||||
static bool iommu_dma_strict __read_mostly = IS_ENABLED(CONFIG_IOMMU_DEFAULT_DMA_STRICT);
|
static bool iommu_dma_strict __read_mostly = IS_ENABLED(CONFIG_IOMMU_DEFAULT_DMA_STRICT);
|
||||||
@ -3400,3 +3401,30 @@ struct iommu_domain *iommu_sva_domain_alloc(struct device *dev,
|
|||||||
|
|
||||||
return domain;
|
return domain;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ioasid_t iommu_alloc_global_pasid(struct device *dev)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* max_pasids == 0 means that the device does not support PASID */
|
||||||
|
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);
|
||||||
|
|
||||||
|
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);
|
||||||
|
@ -197,6 +197,7 @@ enum iommu_dev_features {
|
|||||||
};
|
};
|
||||||
|
|
||||||
#define IOMMU_NO_PASID (0U) /* Reserved for DMA w/o PASID */
|
#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)
|
#define IOMMU_PASID_INVALID (-1U)
|
||||||
typedef unsigned int ioasid_t;
|
typedef unsigned int ioasid_t;
|
||||||
|
|
||||||
@ -728,6 +729,8 @@ void iommu_detach_device_pasid(struct iommu_domain *domain,
|
|||||||
struct iommu_domain *
|
struct iommu_domain *
|
||||||
iommu_get_domain_for_dev_pasid(struct device *dev, ioasid_t pasid,
|
iommu_get_domain_for_dev_pasid(struct device *dev, ioasid_t pasid,
|
||||||
unsigned int type);
|
unsigned int type);
|
||||||
|
ioasid_t iommu_alloc_global_pasid(struct device *dev);
|
||||||
|
void iommu_free_global_pasid(ioasid_t pasid);
|
||||||
#else /* CONFIG_IOMMU_API */
|
#else /* CONFIG_IOMMU_API */
|
||||||
|
|
||||||
struct iommu_ops {};
|
struct iommu_ops {};
|
||||||
@ -1089,6 +1092,13 @@ iommu_get_domain_for_dev_pasid(struct device *dev, ioasid_t pasid,
|
|||||||
{
|
{
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline ioasid_t iommu_alloc_global_pasid(struct device *dev)
|
||||||
|
{
|
||||||
|
return IOMMU_PASID_INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void iommu_free_global_pasid(ioasid_t pasid) {}
|
||||||
#endif /* CONFIG_IOMMU_API */
|
#endif /* CONFIG_IOMMU_API */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
x
Reference in New Issue
Block a user