]>
Commit | Line | Data |
---|---|---|
9c6fd700 GKH |
1 | From 910170442944e1f8674fd5ddbeeb8ccd1877ea98 Mon Sep 17 00:00:00 2001 |
2 | From: David Woodhouse <dwmw2@infradead.org> | |
3 | Date: Mon, 12 Sep 2016 10:49:11 +0800 | |
4 | Subject: iommu/vt-d: Fix PASID table allocation | |
5 | MIME-Version: 1.0 | |
6 | Content-Type: text/plain; charset=UTF-8 | |
7 | Content-Transfer-Encoding: 8bit | |
8 | ||
9 | From: David Woodhouse <dwmw2@infradead.org> | |
10 | ||
11 | commit 910170442944e1f8674fd5ddbeeb8ccd1877ea98 upstream. | |
12 | ||
13 | Somehow I ended up with an off-by-three error in calculating the size of | |
14 | the PASID and PASID State tables, which triggers allocations failures as | |
15 | those tables unfortunately have to be physically contiguous. | |
16 | ||
17 | In fact, even the *correct* maximum size of 8MiB is problematic and is | |
18 | wont to lead to allocation failures. Since I have extracted a promise | |
19 | that this *will* be fixed in hardware, I'm happy to limit it on the | |
20 | current hardware to a maximum of 0x20000 PASIDs, which gives us 1MiB | |
21 | tables — still not ideal, but better than before. | |
22 | ||
23 | Reported by Mika Kuoppala <mika.kuoppala@linux.intel.com> and also by | |
24 | Xunlei Pang <xlpang@redhat.com> who submitted a simpler patch to fix | |
25 | only the allocation (and not the free) to the "correct" limit... which | |
26 | was still problematic. | |
27 | ||
28 | Signed-off-by: David Woodhouse <dwmw2@infradead.org> | |
29 | Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> | |
30 | ||
31 | --- | |
32 | drivers/iommu/intel-svm.c | 26 ++++++++++++++++---------- | |
33 | include/linux/intel-iommu.h | 1 + | |
34 | 2 files changed, 17 insertions(+), 10 deletions(-) | |
35 | ||
36 | --- a/drivers/iommu/intel-svm.c | |
37 | +++ b/drivers/iommu/intel-svm.c | |
38 | @@ -39,10 +39,18 @@ int intel_svm_alloc_pasid_tables(struct | |
39 | struct page *pages; | |
40 | int order; | |
41 | ||
42 | - order = ecap_pss(iommu->ecap) + 7 - PAGE_SHIFT; | |
43 | - if (order < 0) | |
44 | - order = 0; | |
45 | + /* Start at 2 because it's defined as 2^(1+PSS) */ | |
46 | + iommu->pasid_max = 2 << ecap_pss(iommu->ecap); | |
47 | ||
48 | + /* Eventually I'm promised we will get a multi-level PASID table | |
49 | + * and it won't have to be physically contiguous. Until then, | |
50 | + * limit the size because 8MiB contiguous allocations can be hard | |
51 | + * to come by. The limit of 0x20000, which is 1MiB for each of | |
52 | + * the PASID and PASID-state tables, is somewhat arbitrary. */ | |
53 | + if (iommu->pasid_max > 0x20000) | |
54 | + iommu->pasid_max = 0x20000; | |
55 | + | |
56 | + order = get_order(sizeof(struct pasid_entry) * iommu->pasid_max); | |
57 | pages = alloc_pages(GFP_KERNEL | __GFP_ZERO, order); | |
58 | if (!pages) { | |
59 | pr_warn("IOMMU: %s: Failed to allocate PASID table\n", | |
60 | @@ -53,6 +61,8 @@ int intel_svm_alloc_pasid_tables(struct | |
61 | pr_info("%s: Allocated order %d PASID table.\n", iommu->name, order); | |
62 | ||
63 | if (ecap_dis(iommu->ecap)) { | |
64 | + /* Just making it explicit... */ | |
65 | + BUILD_BUG_ON(sizeof(struct pasid_entry) != sizeof(struct pasid_state_entry)); | |
66 | pages = alloc_pages(GFP_KERNEL | __GFP_ZERO, order); | |
67 | if (pages) | |
68 | iommu->pasid_state_table = page_address(pages); | |
69 | @@ -68,11 +78,7 @@ int intel_svm_alloc_pasid_tables(struct | |
70 | ||
71 | int intel_svm_free_pasid_tables(struct intel_iommu *iommu) | |
72 | { | |
73 | - int order; | |
74 | - | |
75 | - order = ecap_pss(iommu->ecap) + 7 - PAGE_SHIFT; | |
76 | - if (order < 0) | |
77 | - order = 0; | |
78 | + int order = get_order(sizeof(struct pasid_entry) * iommu->pasid_max); | |
79 | ||
80 | if (iommu->pasid_table) { | |
81 | free_pages((unsigned long)iommu->pasid_table, order); | |
82 | @@ -371,8 +377,8 @@ int intel_svm_bind_mm(struct device *dev | |
83 | } | |
84 | svm->iommu = iommu; | |
85 | ||
86 | - if (pasid_max > 2 << ecap_pss(iommu->ecap)) | |
87 | - pasid_max = 2 << ecap_pss(iommu->ecap); | |
88 | + if (pasid_max > iommu->pasid_max) | |
89 | + pasid_max = iommu->pasid_max; | |
90 | ||
91 | /* Do not use PASID 0 in caching mode (virtualised IOMMU) */ | |
92 | ret = idr_alloc(&iommu->pasid_idr, svm, | |
93 | --- a/include/linux/intel-iommu.h | |
94 | +++ b/include/linux/intel-iommu.h | |
95 | @@ -429,6 +429,7 @@ struct intel_iommu { | |
96 | struct page_req_dsc *prq; | |
97 | unsigned char prq_name[16]; /* Name for PRQ interrupt */ | |
98 | struct idr pasid_idr; | |
99 | + u32 pasid_max; | |
100 | #endif | |
101 | struct q_inval *qi; /* Queued invalidation info */ | |
102 | u32 *iommu_state; /* Store iommu states between suspend and resume.*/ |