Commit cb4789b0 authored by Jean-Philippe Brucker's avatar Jean-Philippe Brucker Committed by Will Deacon

iommu/ioasid: Add ioasid references

Let IOASID users take references to existing ioasids with ioasid_get().
ioasid_put() drops a reference and only frees the ioasid when its
reference number is zero. It returns true if the ioasid was freed.
For drivers that don't call ioasid_get(), ioasid_put() is the same as
ioasid_free().
Signed-off-by: default avatarJean-Philippe Brucker <jean-philippe@linaro.org>
Reviewed-by: default avatarEric Auger <eric.auger@redhat.com>
Reviewed-by: default avatarLu Baolu <baolu.lu@linux.intel.com>
Link: https://lore.kernel.org/r/20201106155048.997886-2-jean-philippe@linaro.orgSigned-off-by: default avatarWill Deacon <will@kernel.org>
parent f8394f23
...@@ -5198,7 +5198,7 @@ static void auxiliary_unlink_device(struct dmar_domain *domain, ...@@ -5198,7 +5198,7 @@ static void auxiliary_unlink_device(struct dmar_domain *domain,
domain->auxd_refcnt--; domain->auxd_refcnt--;
if (!domain->auxd_refcnt && domain->default_pasid > 0) if (!domain->auxd_refcnt && domain->default_pasid > 0)
ioasid_free(domain->default_pasid); ioasid_put(domain->default_pasid);
} }
static int aux_domain_add_dev(struct dmar_domain *domain, static int aux_domain_add_dev(struct dmar_domain *domain,
...@@ -5259,7 +5259,7 @@ static int aux_domain_add_dev(struct dmar_domain *domain, ...@@ -5259,7 +5259,7 @@ static int aux_domain_add_dev(struct dmar_domain *domain,
spin_unlock(&iommu->lock); spin_unlock(&iommu->lock);
spin_unlock_irqrestore(&device_domain_lock, flags); spin_unlock_irqrestore(&device_domain_lock, flags);
if (!domain->auxd_refcnt && domain->default_pasid > 0) if (!domain->auxd_refcnt && domain->default_pasid > 0)
ioasid_free(domain->default_pasid); ioasid_put(domain->default_pasid);
return ret; return ret;
} }
......
...@@ -598,7 +598,7 @@ intel_svm_bind_mm(struct device *dev, unsigned int flags, ...@@ -598,7 +598,7 @@ intel_svm_bind_mm(struct device *dev, unsigned int flags,
if (mm) { if (mm) {
ret = mmu_notifier_register(&svm->notifier, mm); ret = mmu_notifier_register(&svm->notifier, mm);
if (ret) { if (ret) {
ioasid_free(svm->pasid); ioasid_put(svm->pasid);
kfree(svm); kfree(svm);
kfree(sdev); kfree(sdev);
goto out; goto out;
...@@ -616,7 +616,7 @@ intel_svm_bind_mm(struct device *dev, unsigned int flags, ...@@ -616,7 +616,7 @@ intel_svm_bind_mm(struct device *dev, unsigned int flags,
if (ret) { if (ret) {
if (mm) if (mm)
mmu_notifier_unregister(&svm->notifier, mm); mmu_notifier_unregister(&svm->notifier, mm);
ioasid_free(svm->pasid); ioasid_put(svm->pasid);
kfree(svm); kfree(svm);
kfree(sdev); kfree(sdev);
goto out; goto out;
...@@ -689,7 +689,7 @@ static int intel_svm_unbind_mm(struct device *dev, u32 pasid) ...@@ -689,7 +689,7 @@ static int intel_svm_unbind_mm(struct device *dev, u32 pasid)
kfree_rcu(sdev, rcu); kfree_rcu(sdev, rcu);
if (list_empty(&svm->devs)) { if (list_empty(&svm->devs)) {
ioasid_free(svm->pasid); ioasid_put(svm->pasid);
if (svm->mm) { if (svm->mm) {
mmu_notifier_unregister(&svm->notifier, svm->mm); mmu_notifier_unregister(&svm->notifier, svm->mm);
/* Clear mm's pasid. */ /* Clear mm's pasid. */
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
/* /*
* I/O Address Space ID allocator. There is one global IOASID space, split into * I/O Address Space ID allocator. There is one global IOASID space, split into
* subsets. Users create a subset with DECLARE_IOASID_SET, then allocate and * subsets. Users create a subset with DECLARE_IOASID_SET, then allocate and
* free IOASIDs with ioasid_alloc and ioasid_free. * free IOASIDs with ioasid_alloc and ioasid_put.
*/ */
#include <linux/ioasid.h> #include <linux/ioasid.h>
#include <linux/module.h> #include <linux/module.h>
...@@ -15,6 +15,7 @@ struct ioasid_data { ...@@ -15,6 +15,7 @@ struct ioasid_data {
struct ioasid_set *set; struct ioasid_set *set;
void *private; void *private;
struct rcu_head rcu; struct rcu_head rcu;
refcount_t refs;
}; };
/* /*
...@@ -314,6 +315,7 @@ ioasid_t ioasid_alloc(struct ioasid_set *set, ioasid_t min, ioasid_t max, ...@@ -314,6 +315,7 @@ ioasid_t ioasid_alloc(struct ioasid_set *set, ioasid_t min, ioasid_t max,
data->set = set; data->set = set;
data->private = private; data->private = private;
refcount_set(&data->refs, 1);
/* /*
* Custom allocator needs allocator data to perform platform specific * Custom allocator needs allocator data to perform platform specific
...@@ -346,11 +348,34 @@ ioasid_t ioasid_alloc(struct ioasid_set *set, ioasid_t min, ioasid_t max, ...@@ -346,11 +348,34 @@ ioasid_t ioasid_alloc(struct ioasid_set *set, ioasid_t min, ioasid_t max,
EXPORT_SYMBOL_GPL(ioasid_alloc); EXPORT_SYMBOL_GPL(ioasid_alloc);
/** /**
* ioasid_free - Free an IOASID * ioasid_get - obtain a reference to the IOASID
*/
void ioasid_get(ioasid_t ioasid)
{
struct ioasid_data *ioasid_data;
spin_lock(&ioasid_allocator_lock);
ioasid_data = xa_load(&active_allocator->xa, ioasid);
if (ioasid_data)
refcount_inc(&ioasid_data->refs);
else
WARN_ON(1);
spin_unlock(&ioasid_allocator_lock);
}
EXPORT_SYMBOL_GPL(ioasid_get);
/**
* ioasid_put - Release a reference to an ioasid
* @ioasid: the ID to remove * @ioasid: the ID to remove
*
* Put a reference to the IOASID, free it when the number of references drops to
* zero.
*
* Return: %true if the IOASID was freed, %false otherwise.
*/ */
void ioasid_free(ioasid_t ioasid) bool ioasid_put(ioasid_t ioasid)
{ {
bool free = false;
struct ioasid_data *ioasid_data; struct ioasid_data *ioasid_data;
spin_lock(&ioasid_allocator_lock); spin_lock(&ioasid_allocator_lock);
...@@ -360,6 +385,10 @@ void ioasid_free(ioasid_t ioasid) ...@@ -360,6 +385,10 @@ void ioasid_free(ioasid_t ioasid)
goto exit_unlock; goto exit_unlock;
} }
free = refcount_dec_and_test(&ioasid_data->refs);
if (!free)
goto exit_unlock;
active_allocator->ops->free(ioasid, active_allocator->ops->pdata); active_allocator->ops->free(ioasid, active_allocator->ops->pdata);
/* Custom allocator needs additional steps to free the xa element */ /* Custom allocator needs additional steps to free the xa element */
if (active_allocator->flags & IOASID_ALLOCATOR_CUSTOM) { if (active_allocator->flags & IOASID_ALLOCATOR_CUSTOM) {
...@@ -369,8 +398,9 @@ void ioasid_free(ioasid_t ioasid) ...@@ -369,8 +398,9 @@ void ioasid_free(ioasid_t ioasid)
exit_unlock: exit_unlock:
spin_unlock(&ioasid_allocator_lock); spin_unlock(&ioasid_allocator_lock);
return free;
} }
EXPORT_SYMBOL_GPL(ioasid_free); EXPORT_SYMBOL_GPL(ioasid_put);
/** /**
* ioasid_find - Find IOASID data * ioasid_find - Find IOASID data
......
...@@ -34,7 +34,8 @@ struct ioasid_allocator_ops { ...@@ -34,7 +34,8 @@ struct ioasid_allocator_ops {
#if IS_ENABLED(CONFIG_IOASID) #if IS_ENABLED(CONFIG_IOASID)
ioasid_t ioasid_alloc(struct ioasid_set *set, ioasid_t min, ioasid_t max, ioasid_t ioasid_alloc(struct ioasid_set *set, ioasid_t min, ioasid_t max,
void *private); void *private);
void ioasid_free(ioasid_t ioasid); void ioasid_get(ioasid_t ioasid);
bool ioasid_put(ioasid_t ioasid);
void *ioasid_find(struct ioasid_set *set, ioasid_t ioasid, void *ioasid_find(struct ioasid_set *set, ioasid_t ioasid,
bool (*getter)(void *)); bool (*getter)(void *));
int ioasid_register_allocator(struct ioasid_allocator_ops *allocator); int ioasid_register_allocator(struct ioasid_allocator_ops *allocator);
...@@ -48,10 +49,15 @@ static inline ioasid_t ioasid_alloc(struct ioasid_set *set, ioasid_t min, ...@@ -48,10 +49,15 @@ static inline ioasid_t ioasid_alloc(struct ioasid_set *set, ioasid_t min,
return INVALID_IOASID; return INVALID_IOASID;
} }
static inline void ioasid_free(ioasid_t ioasid) static inline void ioasid_get(ioasid_t ioasid)
{ {
} }
static inline bool ioasid_put(ioasid_t ioasid)
{
return false;
}
static inline void *ioasid_find(struct ioasid_set *set, ioasid_t ioasid, static inline void *ioasid_find(struct ioasid_set *set, ioasid_t ioasid,
bool (*getter)(void *)) bool (*getter)(void *))
{ {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment