]>
Commit | Line | Data |
---|---|---|
00e5a55c BS |
1 | From: Suresh Siddha <suresh.b.siddha@intel.com> |
2 | Subject: x64, x2apic/intr-remap: routines managing Interrupt remapping table entries. | |
3 | References: fate #303948 and fate #303984 | |
4 | Patch-Mainline: queued for .28 | |
5 | Commit-ID: b6fcb33ad6c05f152a672f7c96c1fab006527b80 | |
6 | ||
7 | Signed-off-by: Thomas Renninger <trenn@suse.de> | |
8 | ||
9 | Routines handling the management of interrupt remapping table entries. | |
10 | ||
11 | Signed-off-by: Suresh Siddha <suresh.b.siddha@intel.com> | |
12 | Cc: akpm@linux-foundation.org | |
13 | Cc: arjan@linux.intel.com | |
14 | Cc: andi@firstfloor.org | |
15 | Cc: ebiederm@xmission.com | |
16 | Cc: jbarnes@virtuousgeek.org | |
17 | Cc: steiner@sgi.com | |
18 | Signed-off-by: Ingo Molnar <mingo@elte.hu> | |
19 | ||
20 | --- | |
21 | drivers/pci/intel-iommu.h | 4 | |
22 | drivers/pci/intr_remapping.c | 243 +++++++++++++++++++++++++++++++++++++++++++ | |
23 | include/linux/dmar.h | 12 ++ | |
24 | 3 files changed, 259 insertions(+) | |
25 | ||
26 | Index: linux-2.6.26/drivers/pci/intel-iommu.h | |
27 | =================================================================== | |
28 | --- linux-2.6.26.orig/drivers/pci/intel-iommu.h | |
29 | +++ linux-2.6.26/drivers/pci/intel-iommu.h | |
30 | @@ -123,6 +123,7 @@ static inline void dmar_writeq(void __io | |
31 | #define ecap_qis(e) ((e) & 0x2) | |
32 | #define ecap_eim_support(e) ((e >> 4) & 0x1) | |
33 | #define ecap_ir_support(e) ((e >> 3) & 0x1) | |
34 | +#define ecap_max_handle_mask(e) ((e >> 20) & 0xf) | |
35 | ||
36 | ||
37 | /* IOTLB_REG */ | |
38 | @@ -255,6 +256,8 @@ struct q_inval { | |
39 | #define INTR_REMAP_PAGE_ORDER 8 | |
40 | #define INTR_REMAP_TABLE_REG_SIZE 0xf | |
41 | ||
42 | +#define INTR_REMAP_TABLE_ENTRIES 65536 | |
43 | + | |
44 | struct ir_table { | |
45 | struct irte *base; | |
46 | }; | |
47 | @@ -300,4 +303,5 @@ extern void free_iommu(struct intel_iomm | |
48 | extern int dmar_enable_qi(struct intel_iommu *iommu); | |
49 | extern void qi_global_iec(struct intel_iommu *iommu); | |
50 | ||
51 | +extern void qi_submit_sync(struct qi_desc *desc, struct intel_iommu *iommu); | |
52 | #endif | |
53 | Index: linux-2.6.26/drivers/pci/intr_remapping.c | |
54 | =================================================================== | |
55 | --- linux-2.6.26.orig/drivers/pci/intr_remapping.c | |
56 | +++ linux-2.6.26/drivers/pci/intr_remapping.c | |
57 | @@ -2,6 +2,7 @@ | |
58 | #include <linux/spinlock.h> | |
59 | #include <linux/jiffies.h> | |
60 | #include <linux/pci.h> | |
61 | +#include <linux/irq.h> | |
62 | #include <asm/io_apic.h> | |
63 | #include "intel-iommu.h" | |
64 | #include "intr_remapping.h" | |
65 | @@ -10,6 +11,248 @@ static struct ioapic_scope ir_ioapic[MAX | |
66 | static int ir_ioapic_num; | |
67 | int intr_remapping_enabled; | |
68 | ||
69 | +static struct { | |
70 | + struct intel_iommu *iommu; | |
71 | + u16 irte_index; | |
72 | + u16 sub_handle; | |
73 | + u8 irte_mask; | |
74 | +} irq_2_iommu[NR_IRQS]; | |
75 | + | |
76 | +static DEFINE_SPINLOCK(irq_2_ir_lock); | |
77 | + | |
78 | +int irq_remapped(int irq) | |
79 | +{ | |
80 | + if (irq > NR_IRQS) | |
81 | + return 0; | |
82 | + | |
83 | + if (!irq_2_iommu[irq].iommu) | |
84 | + return 0; | |
85 | + | |
86 | + return 1; | |
87 | +} | |
88 | + | |
89 | +int get_irte(int irq, struct irte *entry) | |
90 | +{ | |
91 | + int index; | |
92 | + | |
93 | + if (!entry || irq > NR_IRQS) | |
94 | + return -1; | |
95 | + | |
96 | + spin_lock(&irq_2_ir_lock); | |
97 | + if (!irq_2_iommu[irq].iommu) { | |
98 | + spin_unlock(&irq_2_ir_lock); | |
99 | + return -1; | |
100 | + } | |
101 | + | |
102 | + index = irq_2_iommu[irq].irte_index + irq_2_iommu[irq].sub_handle; | |
103 | + *entry = *(irq_2_iommu[irq].iommu->ir_table->base + index); | |
104 | + | |
105 | + spin_unlock(&irq_2_ir_lock); | |
106 | + return 0; | |
107 | +} | |
108 | + | |
109 | +int alloc_irte(struct intel_iommu *iommu, int irq, u16 count) | |
110 | +{ | |
111 | + struct ir_table *table = iommu->ir_table; | |
112 | + u16 index, start_index; | |
113 | + unsigned int mask = 0; | |
114 | + int i; | |
115 | + | |
116 | + if (!count) | |
117 | + return -1; | |
118 | + | |
119 | + /* | |
120 | + * start the IRTE search from index 0. | |
121 | + */ | |
122 | + index = start_index = 0; | |
123 | + | |
124 | + if (count > 1) { | |
125 | + count = __roundup_pow_of_two(count); | |
126 | + mask = ilog2(count); | |
127 | + } | |
128 | + | |
129 | + if (mask > ecap_max_handle_mask(iommu->ecap)) { | |
130 | + printk(KERN_ERR | |
131 | + "Requested mask %x exceeds the max invalidation handle" | |
132 | + " mask value %Lx\n", mask, | |
133 | + ecap_max_handle_mask(iommu->ecap)); | |
134 | + return -1; | |
135 | + } | |
136 | + | |
137 | + spin_lock(&irq_2_ir_lock); | |
138 | + do { | |
139 | + for (i = index; i < index + count; i++) | |
140 | + if (table->base[i].present) | |
141 | + break; | |
142 | + /* empty index found */ | |
143 | + if (i == index + count) | |
144 | + break; | |
145 | + | |
146 | + index = (index + count) % INTR_REMAP_TABLE_ENTRIES; | |
147 | + | |
148 | + if (index == start_index) { | |
149 | + spin_unlock(&irq_2_ir_lock); | |
150 | + printk(KERN_ERR "can't allocate an IRTE\n"); | |
151 | + return -1; | |
152 | + } | |
153 | + } while (1); | |
154 | + | |
155 | + for (i = index; i < index + count; i++) | |
156 | + table->base[i].present = 1; | |
157 | + | |
158 | + irq_2_iommu[irq].iommu = iommu; | |
159 | + irq_2_iommu[irq].irte_index = index; | |
160 | + irq_2_iommu[irq].sub_handle = 0; | |
161 | + irq_2_iommu[irq].irte_mask = mask; | |
162 | + | |
163 | + spin_unlock(&irq_2_ir_lock); | |
164 | + | |
165 | + return index; | |
166 | +} | |
167 | + | |
168 | +static void qi_flush_iec(struct intel_iommu *iommu, int index, int mask) | |
169 | +{ | |
170 | + struct qi_desc desc; | |
171 | + | |
172 | + desc.low = QI_IEC_IIDEX(index) | QI_IEC_TYPE | QI_IEC_IM(mask) | |
173 | + | QI_IEC_SELECTIVE; | |
174 | + desc.high = 0; | |
175 | + | |
176 | + qi_submit_sync(&desc, iommu); | |
177 | +} | |
178 | + | |
179 | +int map_irq_to_irte_handle(int irq, u16 *sub_handle) | |
180 | +{ | |
181 | + int index; | |
182 | + | |
183 | + spin_lock(&irq_2_ir_lock); | |
184 | + if (irq >= NR_IRQS || !irq_2_iommu[irq].iommu) { | |
185 | + spin_unlock(&irq_2_ir_lock); | |
186 | + return -1; | |
187 | + } | |
188 | + | |
189 | + *sub_handle = irq_2_iommu[irq].sub_handle; | |
190 | + index = irq_2_iommu[irq].irte_index; | |
191 | + spin_unlock(&irq_2_ir_lock); | |
192 | + return index; | |
193 | +} | |
194 | + | |
195 | +int set_irte_irq(int irq, struct intel_iommu *iommu, u16 index, u16 subhandle) | |
196 | +{ | |
197 | + spin_lock(&irq_2_ir_lock); | |
198 | + if (irq >= NR_IRQS || irq_2_iommu[irq].iommu) { | |
199 | + spin_unlock(&irq_2_ir_lock); | |
200 | + return -1; | |
201 | + } | |
202 | + | |
203 | + irq_2_iommu[irq].iommu = iommu; | |
204 | + irq_2_iommu[irq].irte_index = index; | |
205 | + irq_2_iommu[irq].sub_handle = subhandle; | |
206 | + irq_2_iommu[irq].irte_mask = 0; | |
207 | + | |
208 | + spin_unlock(&irq_2_ir_lock); | |
209 | + | |
210 | + return 0; | |
211 | +} | |
212 | + | |
213 | +int clear_irte_irq(int irq, struct intel_iommu *iommu, u16 index) | |
214 | +{ | |
215 | + spin_lock(&irq_2_ir_lock); | |
216 | + if (irq >= NR_IRQS || !irq_2_iommu[irq].iommu) { | |
217 | + spin_unlock(&irq_2_ir_lock); | |
218 | + return -1; | |
219 | + } | |
220 | + | |
221 | + irq_2_iommu[irq].iommu = NULL; | |
222 | + irq_2_iommu[irq].irte_index = 0; | |
223 | + irq_2_iommu[irq].sub_handle = 0; | |
224 | + irq_2_iommu[irq].irte_mask = 0; | |
225 | + | |
226 | + spin_unlock(&irq_2_ir_lock); | |
227 | + | |
228 | + return 0; | |
229 | +} | |
230 | + | |
231 | +int modify_irte(int irq, struct irte *irte_modified) | |
232 | +{ | |
233 | + int index; | |
234 | + struct irte *irte; | |
235 | + struct intel_iommu *iommu; | |
236 | + | |
237 | + spin_lock(&irq_2_ir_lock); | |
238 | + if (irq >= NR_IRQS || !irq_2_iommu[irq].iommu) { | |
239 | + spin_unlock(&irq_2_ir_lock); | |
240 | + return -1; | |
241 | + } | |
242 | + | |
243 | + iommu = irq_2_iommu[irq].iommu; | |
244 | + | |
245 | + index = irq_2_iommu[irq].irte_index + irq_2_iommu[irq].sub_handle; | |
246 | + irte = &iommu->ir_table->base[index]; | |
247 | + | |
248 | + set_64bit((unsigned long *)irte, irte_modified->low | (1 << 1)); | |
249 | + __iommu_flush_cache(iommu, irte, sizeof(*irte)); | |
250 | + | |
251 | + qi_flush_iec(iommu, index, 0); | |
252 | + | |
253 | + spin_unlock(&irq_2_ir_lock); | |
254 | + return 0; | |
255 | +} | |
256 | + | |
257 | +int flush_irte(int irq) | |
258 | +{ | |
259 | + int index; | |
260 | + struct intel_iommu *iommu; | |
261 | + | |
262 | + spin_lock(&irq_2_ir_lock); | |
263 | + if (irq >= NR_IRQS || !irq_2_iommu[irq].iommu) { | |
264 | + spin_unlock(&irq_2_ir_lock); | |
265 | + return -1; | |
266 | + } | |
267 | + | |
268 | + iommu = irq_2_iommu[irq].iommu; | |
269 | + | |
270 | + index = irq_2_iommu[irq].irte_index + irq_2_iommu[irq].sub_handle; | |
271 | + | |
272 | + qi_flush_iec(iommu, index, irq_2_iommu[irq].irte_mask); | |
273 | + spin_unlock(&irq_2_ir_lock); | |
274 | + | |
275 | + return 0; | |
276 | +} | |
277 | + | |
278 | +int free_irte(int irq) | |
279 | +{ | |
280 | + int index, i; | |
281 | + struct irte *irte; | |
282 | + struct intel_iommu *iommu; | |
283 | + | |
284 | + spin_lock(&irq_2_ir_lock); | |
285 | + if (irq >= NR_IRQS || !irq_2_iommu[irq].iommu) { | |
286 | + spin_unlock(&irq_2_ir_lock); | |
287 | + return -1; | |
288 | + } | |
289 | + | |
290 | + iommu = irq_2_iommu[irq].iommu; | |
291 | + | |
292 | + index = irq_2_iommu[irq].irte_index + irq_2_iommu[irq].sub_handle; | |
293 | + irte = &iommu->ir_table->base[index]; | |
294 | + | |
295 | + if (!irq_2_iommu[irq].sub_handle) { | |
296 | + for (i = 0; i < (1 << irq_2_iommu[irq].irte_mask); i++) | |
297 | + set_64bit((unsigned long *)irte, 0); | |
298 | + qi_flush_iec(iommu, index, irq_2_iommu[irq].irte_mask); | |
299 | + } | |
300 | + | |
301 | + irq_2_iommu[irq].iommu = NULL; | |
302 | + irq_2_iommu[irq].irte_index = 0; | |
303 | + irq_2_iommu[irq].sub_handle = 0; | |
304 | + irq_2_iommu[irq].irte_mask = 0; | |
305 | + | |
306 | + spin_unlock(&irq_2_ir_lock); | |
307 | + | |
308 | + return 0; | |
309 | +} | |
310 | + | |
311 | static void iommu_set_intr_remapping(struct intel_iommu *iommu, int mode) | |
312 | { | |
313 | u64 addr; | |
314 | Index: linux-2.6.26/include/linux/dmar.h | |
315 | =================================================================== | |
316 | --- linux-2.6.26.orig/include/linux/dmar.h | |
317 | +++ linux-2.6.26/include/linux/dmar.h | |
318 | @@ -98,7 +98,19 @@ struct irte { | |
319 | __u64 high; | |
320 | }; | |
321 | }; | |
322 | +extern int get_irte(int irq, struct irte *entry); | |
323 | +extern int modify_irte(int irq, struct irte *irte_modified); | |
324 | +extern int alloc_irte(struct intel_iommu *iommu, int irq, u16 count); | |
325 | +extern int set_irte_irq(int irq, struct intel_iommu *iommu, u16 index, | |
326 | + u16 sub_handle); | |
327 | +extern int map_irq_to_irte_handle(int irq, u16 *sub_handle); | |
328 | +extern int clear_irte_irq(int irq, struct intel_iommu *iommu, u16 index); | |
329 | +extern int flush_irte(int irq); | |
330 | +extern int free_irte(int irq); | |
331 | + | |
332 | +extern int irq_remapped(int irq); | |
333 | #else | |
334 | +#define irq_remapped(irq) (0) | |
335 | #define enable_intr_remapping(mode) (-1) | |
336 | #define intr_remapping_enabled (0) | |
337 | #endif |