Now that the IOASID allocator is in place, use it to allocate shared PASIDs.
Signed-off-by: Jean-Philippe Brucker <jean-philippe.bruc...@arm.com> --- drivers/iommu/Kconfig | 1 + drivers/iommu/iommu-sva.c | 80 ++++++++++++++++++++++----------------- 2 files changed, 46 insertions(+), 35 deletions(-) diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig index e89f5a97d3c4..83a62634bbc4 100644 --- a/drivers/iommu/Kconfig +++ b/drivers/iommu/Kconfig @@ -116,6 +116,7 @@ config IOMMU_SVA bool select IOMMU_API select MMU_NOTIFIER + select IOASID config IOMMU_PAGE_FAULT bool diff --git a/drivers/iommu/iommu-sva.c b/drivers/iommu/iommu-sva.c index ea34e7383ab4..28eaa617b4f0 100644 --- a/drivers/iommu/iommu-sva.c +++ b/drivers/iommu/iommu-sva.c @@ -6,6 +6,7 @@ */ #include <linux/idr.h> +#include <linux/ioasid.h> #include <linux/iommu.h> #include <linux/mmu_notifier.h> #include <linux/sched/mm.h> @@ -115,12 +116,7 @@ struct iommu_bond { void *drvdata; }; -/* - * Because we're using an IDR, PASIDs are limited to 31 bits (the sign bit is - * used for returning errors). In practice implementations will use at most 20 - * bits, which is the PCI limit. - */ -static DEFINE_IDR(iommu_pasid_idr); +static DECLARE_IOASID_SET(ioasid_shared); /* * For the moment this is an all-purpose lock. It serializes @@ -163,18 +159,13 @@ static struct io_mm *io_mm_alloc(struct device *dev, struct mm_struct *mm, INIT_LIST_HEAD(&io_mm->devices); /* Leave kref as zero until the io_mm is fully initialized */ - idr_preload(GFP_KERNEL); - spin_lock(&iommu_sva_lock); - pasid = idr_alloc(&iommu_pasid_idr, io_mm, param->min_pasid, - param->max_pasid + 1, GFP_ATOMIC); - io_mm->pasid = pasid; - spin_unlock(&iommu_sva_lock); - idr_preload_end(); - + pasid = ioasid_alloc(&ioasid_shared, param->min_pasid, + param->max_pasid + 1, io_mm); if (pasid < 0) { ret = pasid; goto err_free_mm; } + io_mm->pasid = pasid; ret = mmu_notifier_register(&io_mm->notifier, mm); if (ret) @@ -201,7 +192,7 @@ static struct io_mm *io_mm_alloc(struct device *dev, struct mm_struct *mm, * 0 so no user could get a reference to it. Free it manually. */ spin_lock(&iommu_sva_lock); - idr_remove(&iommu_pasid_idr, io_mm->pasid); + ioasid_free(pasid); spin_unlock(&iommu_sva_lock); err_free_mm: @@ -231,7 +222,7 @@ static void io_mm_release(struct kref *kref) io_mm = container_of(kref, struct io_mm, kref); WARN_ON(!list_empty(&io_mm->devices)); - idr_remove(&iommu_pasid_idr, io_mm->pasid); + ioasid_free(io_mm->pasid); /* * If we're being released from mm exit, the notifier callback ->release @@ -491,15 +482,44 @@ static struct mmu_notifier_ops iommu_mmu_notifier = { .invalidate_range = iommu_notifier_invalidate_range, }; +struct io_mm_bond_info { + struct device *dev; + struct mm_struct *mm; + struct io_mm *io_mm; +}; + +static int io_mm_compare_bond(ioasid_t pasid, void *priv, void *data) +{ + struct iommu_bond *bond; + struct io_mm *io_mm = priv; + struct io_mm_bond_info *info = data; + + if (io_mm->mm != info->mm || !io_mm_get_locked(io_mm)) + /* Keep looking */ + return 0; + + /* Is it already bound to this device? */ + list_for_each_entry(bond, &io_mm->devices, mm_head) { + if (bond->dev == info->dev) { + io_mm_put_locked(io_mm); + return -EEXIST; + } + } + info->io_mm = io_mm; + return 1; +} + int __iommu_sva_bind_device(struct device *dev, struct mm_struct *mm, int *pasid, unsigned long flags, void *drvdata) { - int i; int ret = 0; - struct iommu_bond *bond; - struct io_mm *io_mm = NULL; + struct io_mm *io_mm; struct iommu_sva_param *param; const struct iommu_ops *ops = dev->bus->iommu_ops; + struct io_mm_bond_info bond_info = { + .dev = dev, + .mm = mm, + }; if (!ops || !dev->iommu_param) return -ENODEV; @@ -511,25 +531,15 @@ int __iommu_sva_bind_device(struct device *dev, struct mm_struct *mm, int *pasid goto out_unlock; } - /* If an io_mm already exists, use it */ + /* If an io_mm already exists, get a reference and use it */ spin_lock(&iommu_sva_lock); - idr_for_each_entry(&iommu_pasid_idr, io_mm, i) { - if (io_mm->mm == mm && io_mm_get_locked(io_mm)) { - /* ... Unless it's already bound to this device */ - list_for_each_entry(bond, &io_mm->devices, mm_head) { - if (bond->dev == dev) { - ret = -EEXIST; - io_mm_put_locked(io_mm); - break; - } - } - break; - } - } + ret = ioasid_for_each(&ioasid_shared, io_mm_compare_bond, &bond_info); spin_unlock(&iommu_sva_lock); - if (ret) + if (ret < 0) goto out_unlock; + io_mm = bond_info.io_mm; + /* Require identical features within an io_mm for now */ if (io_mm && (flags != io_mm->flags)) { io_mm_put(io_mm); @@ -640,7 +650,7 @@ struct mm_struct *iommu_sva_find(int pasid) struct mm_struct *mm = NULL; spin_lock(&iommu_sva_lock); - io_mm = idr_find(&iommu_pasid_idr, pasid); + io_mm = ioasid_find(&ioasid_shared, pasid); if (io_mm && io_mm_get_locked(io_mm)) { if (mmget_not_zero(io_mm->mm)) mm = io_mm->mm; -- 2.19.1 _______________________________________________ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu