]>
Commit | Line | Data |
---|---|---|
cfc78dfd JPB |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * Helpers for IOMMU drivers implementing SVA | |
4 | */ | |
5 | #include <linux/mutex.h> | |
6 | #include <linux/sched/mm.h> | |
7 | ||
8 | #include "iommu-sva-lib.h" | |
9 | ||
10 | static DEFINE_MUTEX(iommu_sva_lock); | |
11 | static DECLARE_IOASID_SET(iommu_sva_pasid); | |
12 | ||
13 | /** | |
14 | * iommu_sva_alloc_pasid - Allocate a PASID for the mm | |
15 | * @mm: the mm | |
16 | * @min: minimum PASID value (inclusive) | |
17 | * @max: maximum PASID value (inclusive) | |
18 | * | |
19 | * Try to allocate a PASID for this mm, or take a reference to the existing one | |
20 | * 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. | |
23 | * | |
24 | * Returns 0 on success and < 0 on error. | |
25 | */ | |
26 | int iommu_sva_alloc_pasid(struct mm_struct *mm, ioasid_t min, ioasid_t max) | |
27 | { | |
28 | int ret = 0; | |
29 | ioasid_t pasid; | |
30 | ||
31 | if (min == INVALID_IOASID || max == INVALID_IOASID || | |
32 | min == 0 || max < min) | |
33 | return -EINVAL; | |
34 | ||
35 | mutex_lock(&iommu_sva_lock); | |
36 | if (mm->pasid) { | |
37 | if (mm->pasid >= min && mm->pasid <= max) | |
38 | ioasid_get(mm->pasid); | |
39 | else | |
40 | 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; | |
47 | } | |
48 | mutex_unlock(&iommu_sva_lock); | |
49 | return ret; | |
50 | } | |
51 | EXPORT_SYMBOL_GPL(iommu_sva_alloc_pasid); | |
52 | ||
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 | ||
68 | /* ioasid_find getter() requires a void * argument */ | |
69 | static bool __mmget_not_zero(void *mm) | |
70 | { | |
71 | return mmget_not_zero(mm); | |
72 | } | |
73 | ||
74 | /** | |
75 | * iommu_sva_find() - Find mm associated to the given PASID | |
76 | * @pasid: Process Address Space ID assigned to the mm | |
77 | * | |
78 | * On success a reference to the mm is taken, and must be released with mmput(). | |
79 | * | |
80 | * Returns the mm corresponding to this PASID, or an error if not found. | |
81 | */ | |
82 | struct mm_struct *iommu_sva_find(ioasid_t pasid) | |
83 | { | |
84 | return ioasid_find(&iommu_sva_pasid, pasid, __mmget_not_zero); | |
85 | } | |
86 | EXPORT_SYMBOL_GPL(iommu_sva_find); |