Skip to content

Commit 701fac4

Browse files
Fenghua Yusuryasaimadhu
authored andcommitted
iommu/sva: Assign a PASID to mm on PASID allocation and free it on mm exit
PASIDs are process-wide. It was attempted to use refcounted PASIDs to free them when the last thread drops the refcount. This turned out to be complex and error prone. Given the fact that the PASID space is 20 bits, which allows up to 1M processes to have a PASID associated concurrently, PASID resource exhaustion is not a realistic concern. Therefore, it was decided to simplify the approach and stick with lazy on demand PASID allocation, but drop the eager free approach and make an allocated PASID's lifetime bound to the lifetime of the process. Get rid of the refcounting mechanisms and replace/rename the interfaces to reflect this new approach. [ bp: Massage commit message. ] Suggested-by: Dave Hansen <[email protected]> Signed-off-by: Fenghua Yu <[email protected]> Signed-off-by: Borislav Petkov <[email protected]> Reviewed-by: Tony Luck <[email protected]> Reviewed-by: Lu Baolu <[email protected]> Reviewed-by: Jacob Pan <[email protected]> Reviewed-by: Thomas Gleixner <[email protected]> Acked-by: Joerg Roedel <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent a6cbd44 commit 701fac4

File tree

9 files changed

+38
-88
lines changed

9 files changed

+38
-88
lines changed

drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-sva.c

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -340,14 +340,12 @@ __arm_smmu_sva_bind(struct device *dev, struct mm_struct *mm)
340340
bond->smmu_mn = arm_smmu_mmu_notifier_get(smmu_domain, mm);
341341
if (IS_ERR(bond->smmu_mn)) {
342342
ret = PTR_ERR(bond->smmu_mn);
343-
goto err_free_pasid;
343+
goto err_free_bond;
344344
}
345345

346346
list_add(&bond->list, &master->bonds);
347347
return &bond->sva;
348348

349-
err_free_pasid:
350-
iommu_sva_free_pasid(mm);
351349
err_free_bond:
352350
kfree(bond);
353351
return ERR_PTR(ret);
@@ -377,7 +375,6 @@ void arm_smmu_sva_unbind(struct iommu_sva *handle)
377375
if (refcount_dec_and_test(&bond->refs)) {
378376
list_del(&bond->list);
379377
arm_smmu_mmu_notifier_put(bond->smmu_mn);
380-
iommu_sva_free_pasid(bond->mm);
381378
kfree(bond);
382379
}
383380
mutex_unlock(&sva_lock);

drivers/iommu/intel/iommu.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4781,7 +4781,7 @@ static int aux_domain_add_dev(struct dmar_domain *domain,
47814781
link_failed:
47824782
spin_unlock_irqrestore(&device_domain_lock, flags);
47834783
if (list_empty(&domain->subdevices) && domain->default_pasid > 0)
4784-
ioasid_put(domain->default_pasid);
4784+
ioasid_free(domain->default_pasid);
47854785

47864786
return ret;
47874787
}
@@ -4811,7 +4811,7 @@ static void aux_domain_remove_dev(struct dmar_domain *domain,
48114811
spin_unlock_irqrestore(&device_domain_lock, flags);
48124812

48134813
if (list_empty(&domain->subdevices) && domain->default_pasid > 0)
4814-
ioasid_put(domain->default_pasid);
4814+
ioasid_free(domain->default_pasid);
48154815
}
48164816

48174817
static int prepare_domain_attach_device(struct iommu_domain *domain,

drivers/iommu/intel/svm.c

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -514,11 +514,6 @@ static int intel_svm_alloc_pasid(struct device *dev, struct mm_struct *mm,
514514
return iommu_sva_alloc_pasid(mm, PASID_MIN, max_pasid - 1);
515515
}
516516

517-
static void intel_svm_free_pasid(struct mm_struct *mm)
518-
{
519-
iommu_sva_free_pasid(mm);
520-
}
521-
522517
static struct iommu_sva *intel_svm_bind_mm(struct intel_iommu *iommu,
523518
struct device *dev,
524519
struct mm_struct *mm,
@@ -662,8 +657,6 @@ static int intel_svm_unbind_mm(struct device *dev, u32 pasid)
662657
kfree(svm);
663658
}
664659
}
665-
/* Drop a PASID reference and free it if no reference. */
666-
intel_svm_free_pasid(mm);
667660
}
668661
out:
669662
return ret;
@@ -1047,8 +1040,6 @@ struct iommu_sva *intel_svm_bind(struct device *dev, struct mm_struct *mm, void
10471040
}
10481041

10491042
sva = intel_svm_bind_mm(iommu, dev, mm, flags);
1050-
if (IS_ERR_OR_NULL(sva))
1051-
intel_svm_free_pasid(mm);
10521043
mutex_unlock(&pasid_mutex);
10531044

10541045
return sva;

drivers/iommu/ioasid.c

Lines changed: 4 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
/*
33
* I/O Address Space ID allocator. There is one global IOASID space, split into
44
* subsets. Users create a subset with DECLARE_IOASID_SET, then allocate and
5-
* free IOASIDs with ioasid_alloc and ioasid_put.
5+
* free IOASIDs with ioasid_alloc() and ioasid_free().
66
*/
77
#include <linux/ioasid.h>
88
#include <linux/module.h>
@@ -15,7 +15,6 @@ struct ioasid_data {
1515
struct ioasid_set *set;
1616
void *private;
1717
struct rcu_head rcu;
18-
refcount_t refs;
1918
};
2019

2120
/*
@@ -315,7 +314,6 @@ ioasid_t ioasid_alloc(struct ioasid_set *set, ioasid_t min, ioasid_t max,
315314

316315
data->set = set;
317316
data->private = private;
318-
refcount_set(&data->refs, 1);
319317

320318
/*
321319
* Custom allocator needs allocator data to perform platform specific
@@ -348,35 +346,11 @@ ioasid_t ioasid_alloc(struct ioasid_set *set, ioasid_t min, ioasid_t max,
348346
EXPORT_SYMBOL_GPL(ioasid_alloc);
349347

350348
/**
351-
* ioasid_get - obtain a reference to the IOASID
352-
* @ioasid: the ID to get
353-
*/
354-
void ioasid_get(ioasid_t ioasid)
355-
{
356-
struct ioasid_data *ioasid_data;
357-
358-
spin_lock(&ioasid_allocator_lock);
359-
ioasid_data = xa_load(&active_allocator->xa, ioasid);
360-
if (ioasid_data)
361-
refcount_inc(&ioasid_data->refs);
362-
else
363-
WARN_ON(1);
364-
spin_unlock(&ioasid_allocator_lock);
365-
}
366-
EXPORT_SYMBOL_GPL(ioasid_get);
367-
368-
/**
369-
* ioasid_put - Release a reference to an ioasid
349+
* ioasid_free - Free an ioasid
370350
* @ioasid: the ID to remove
371-
*
372-
* Put a reference to the IOASID, free it when the number of references drops to
373-
* zero.
374-
*
375-
* Return: %true if the IOASID was freed, %false otherwise.
376351
*/
377-
bool ioasid_put(ioasid_t ioasid)
352+
void ioasid_free(ioasid_t ioasid)
378353
{
379-
bool free = false;
380354
struct ioasid_data *ioasid_data;
381355

382356
spin_lock(&ioasid_allocator_lock);
@@ -386,10 +360,6 @@ bool ioasid_put(ioasid_t ioasid)
386360
goto exit_unlock;
387361
}
388362

389-
free = refcount_dec_and_test(&ioasid_data->refs);
390-
if (!free)
391-
goto exit_unlock;
392-
393363
active_allocator->ops->free(ioasid, active_allocator->ops->pdata);
394364
/* Custom allocator needs additional steps to free the xa element */
395365
if (active_allocator->flags & IOASID_ALLOCATOR_CUSTOM) {
@@ -399,9 +369,8 @@ bool ioasid_put(ioasid_t ioasid)
399369

400370
exit_unlock:
401371
spin_unlock(&ioasid_allocator_lock);
402-
return free;
403372
}
404-
EXPORT_SYMBOL_GPL(ioasid_put);
373+
EXPORT_SYMBOL_GPL(ioasid_free);
405374

406375
/**
407376
* ioasid_find - Find IOASID data

drivers/iommu/iommu-sva-lib.c

Lines changed: 12 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,7 @@ static DECLARE_IOASID_SET(iommu_sva_pasid);
1818
*
1919
* Try to allocate a PASID for this mm, or take a reference to the existing one
2020
* provided it fits within the [@min, @max] range. On success the PASID is
21-
* available in mm->pasid, and must be released with iommu_sva_free_pasid().
22-
* @min must be greater than 0, because 0 indicates an unused mm->pasid.
21+
* available in mm->pasid and will be available for the lifetime of the mm.
2322
*
2423
* Returns 0 on success and < 0 on error.
2524
*/
@@ -33,38 +32,24 @@ int iommu_sva_alloc_pasid(struct mm_struct *mm, ioasid_t min, ioasid_t max)
3332
return -EINVAL;
3433

3534
mutex_lock(&iommu_sva_lock);
36-
if (mm->pasid) {
37-
if (mm->pasid >= min && mm->pasid <= max)
38-
ioasid_get(mm->pasid);
39-
else
35+
/* Is a PASID already associated with this mm? */
36+
if (pasid_valid(mm->pasid)) {
37+
if (mm->pasid < min || mm->pasid >= max)
4038
ret = -EOVERFLOW;
41-
} else {
42-
pasid = ioasid_alloc(&iommu_sva_pasid, min, max, mm);
43-
if (pasid == INVALID_IOASID)
44-
ret = -ENOMEM;
45-
else
46-
mm->pasid = pasid;
39+
goto out;
4740
}
41+
42+
pasid = ioasid_alloc(&iommu_sva_pasid, min, max, mm);
43+
if (!pasid_valid(pasid))
44+
ret = -ENOMEM;
45+
else
46+
mm_pasid_set(mm, pasid);
47+
out:
4848
mutex_unlock(&iommu_sva_lock);
4949
return ret;
5050
}
5151
EXPORT_SYMBOL_GPL(iommu_sva_alloc_pasid);
5252

53-
/**
54-
* iommu_sva_free_pasid - Release the mm's PASID
55-
* @mm: the mm
56-
*
57-
* Drop one reference to a PASID allocated with iommu_sva_alloc_pasid()
58-
*/
59-
void iommu_sva_free_pasid(struct mm_struct *mm)
60-
{
61-
mutex_lock(&iommu_sva_lock);
62-
if (ioasid_put(mm->pasid))
63-
mm->pasid = 0;
64-
mutex_unlock(&iommu_sva_lock);
65-
}
66-
EXPORT_SYMBOL_GPL(iommu_sva_free_pasid);
67-
6853
/* ioasid_find getter() requires a void * argument */
6954
static bool __mmget_not_zero(void *mm)
7055
{

drivers/iommu/iommu-sva-lib.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
#include <linux/mm_types.h>
1010

1111
int iommu_sva_alloc_pasid(struct mm_struct *mm, ioasid_t min, ioasid_t max);
12-
void iommu_sva_free_pasid(struct mm_struct *mm);
1312
struct mm_struct *iommu_sva_find(ioasid_t pasid);
1413

1514
/* I/O Page fault */

include/linux/ioasid.h

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,7 @@ struct ioasid_allocator_ops {
3434
#if IS_ENABLED(CONFIG_IOASID)
3535
ioasid_t ioasid_alloc(struct ioasid_set *set, ioasid_t min, ioasid_t max,
3636
void *private);
37-
void ioasid_get(ioasid_t ioasid);
38-
bool ioasid_put(ioasid_t ioasid);
37+
void ioasid_free(ioasid_t ioasid);
3938
void *ioasid_find(struct ioasid_set *set, ioasid_t ioasid,
4039
bool (*getter)(void *));
4140
int ioasid_register_allocator(struct ioasid_allocator_ops *allocator);
@@ -53,14 +52,7 @@ static inline ioasid_t ioasid_alloc(struct ioasid_set *set, ioasid_t min,
5352
return INVALID_IOASID;
5453
}
5554

56-
static inline void ioasid_get(ioasid_t ioasid)
57-
{
58-
}
59-
60-
static inline bool ioasid_put(ioasid_t ioasid)
61-
{
62-
return false;
63-
}
55+
static inline void ioasid_free(ioasid_t ioasid) { }
6456

6557
static inline void *ioasid_find(struct ioasid_set *set, ioasid_t ioasid,
6658
bool (*getter)(void *))

include/linux/sched/mm.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -439,8 +439,24 @@ static inline void mm_pasid_init(struct mm_struct *mm)
439439
{
440440
mm->pasid = INVALID_IOASID;
441441
}
442+
443+
/* Associate a PASID with an mm_struct: */
444+
static inline void mm_pasid_set(struct mm_struct *mm, u32 pasid)
445+
{
446+
mm->pasid = pasid;
447+
}
448+
449+
static inline void mm_pasid_drop(struct mm_struct *mm)
450+
{
451+
if (pasid_valid(mm->pasid)) {
452+
ioasid_free(mm->pasid);
453+
mm->pasid = INVALID_IOASID;
454+
}
455+
}
442456
#else
443457
static inline void mm_pasid_init(struct mm_struct *mm) {}
458+
static inline void mm_pasid_set(struct mm_struct *mm, u32 pasid) {}
459+
static inline void mm_pasid_drop(struct mm_struct *mm) {}
444460
#endif
445461

446462
#endif /* _LINUX_SCHED_MM_H */

kernel/fork.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1115,6 +1115,7 @@ static inline void __mmput(struct mm_struct *mm)
11151115
}
11161116
if (mm->binfmt)
11171117
module_put(mm->binfmt->module);
1118+
mm_pasid_drop(mm);
11181119
mmdrop(mm);
11191120
}
11201121

0 commit comments

Comments
 (0)