diff options
Diffstat (limited to 'arch/powerpc/mm/hash_native_64.c')
-rw-r--r-- | arch/powerpc/mm/hash_native_64.c | 446 |
1 files changed, 446 insertions, 0 deletions
diff --git a/arch/powerpc/mm/hash_native_64.c b/arch/powerpc/mm/hash_native_64.c new file mode 100644 index 000000000000..174d14576c28 --- /dev/null +++ b/arch/powerpc/mm/hash_native_64.c | |||
@@ -0,0 +1,446 @@ | |||
1 | /* | ||
2 | * native hashtable management. | ||
3 | * | ||
4 | * SMP scalability work: | ||
5 | * Copyright (C) 2001 Anton Blanchard <anton@au.ibm.com>, IBM | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or | ||
8 | * modify it under the terms of the GNU General Public License | ||
9 | * as published by the Free Software Foundation; either version | ||
10 | * 2 of the License, or (at your option) any later version. | ||
11 | */ | ||
12 | #include <linux/spinlock.h> | ||
13 | #include <linux/bitops.h> | ||
14 | #include <linux/threads.h> | ||
15 | #include <linux/smp.h> | ||
16 | |||
17 | #include <asm/abs_addr.h> | ||
18 | #include <asm/machdep.h> | ||
19 | #include <asm/mmu.h> | ||
20 | #include <asm/mmu_context.h> | ||
21 | #include <asm/pgtable.h> | ||
22 | #include <asm/tlbflush.h> | ||
23 | #include <asm/tlb.h> | ||
24 | #include <asm/cputable.h> | ||
25 | |||
26 | #define HPTE_LOCK_BIT 3 | ||
27 | |||
28 | static DEFINE_SPINLOCK(native_tlbie_lock); | ||
29 | |||
30 | static inline void native_lock_hpte(hpte_t *hptep) | ||
31 | { | ||
32 | unsigned long *word = &hptep->v; | ||
33 | |||
34 | while (1) { | ||
35 | if (!test_and_set_bit(HPTE_LOCK_BIT, word)) | ||
36 | break; | ||
37 | while(test_bit(HPTE_LOCK_BIT, word)) | ||
38 | cpu_relax(); | ||
39 | } | ||
40 | } | ||
41 | |||
42 | static inline void native_unlock_hpte(hpte_t *hptep) | ||
43 | { | ||
44 | unsigned long *word = &hptep->v; | ||
45 | |||
46 | asm volatile("lwsync":::"memory"); | ||
47 | clear_bit(HPTE_LOCK_BIT, word); | ||
48 | } | ||
49 | |||
50 | long native_hpte_insert(unsigned long hpte_group, unsigned long va, | ||
51 | unsigned long prpn, unsigned long vflags, | ||
52 | unsigned long rflags) | ||
53 | { | ||
54 | hpte_t *hptep = htab_address + hpte_group; | ||
55 | unsigned long hpte_v, hpte_r; | ||
56 | int i; | ||
57 | |||
58 | for (i = 0; i < HPTES_PER_GROUP; i++) { | ||
59 | if (! (hptep->v & HPTE_V_VALID)) { | ||
60 | /* retry with lock held */ | ||
61 | native_lock_hpte(hptep); | ||
62 | if (! (hptep->v & HPTE_V_VALID)) | ||
63 | break; | ||
64 | native_unlock_hpte(hptep); | ||
65 | } | ||
66 | |||
67 | hptep++; | ||
68 | } | ||
69 | |||
70 | if (i == HPTES_PER_GROUP) | ||
71 | return -1; | ||
72 | |||
73 | hpte_v = (va >> 23) << HPTE_V_AVPN_SHIFT | vflags | HPTE_V_VALID; | ||
74 | if (vflags & HPTE_V_LARGE) | ||
75 | va &= ~(1UL << HPTE_V_AVPN_SHIFT); | ||
76 | hpte_r = (prpn << HPTE_R_RPN_SHIFT) | rflags; | ||
77 | |||
78 | hptep->r = hpte_r; | ||
79 | /* Guarantee the second dword is visible before the valid bit */ | ||
80 | __asm__ __volatile__ ("eieio" : : : "memory"); | ||
81 | /* | ||
82 | * Now set the first dword including the valid bit | ||
83 | * NOTE: this also unlocks the hpte | ||
84 | */ | ||
85 | hptep->v = hpte_v; | ||
86 | |||
87 | __asm__ __volatile__ ("ptesync" : : : "memory"); | ||
88 | |||
89 | return i | (!!(vflags & HPTE_V_SECONDARY) << 3); | ||
90 | } | ||
91 | |||
92 | static long native_hpte_remove(unsigned long hpte_group) | ||
93 | { | ||
94 | hpte_t *hptep; | ||
95 | int i; | ||
96 | int slot_offset; | ||
97 | unsigned long hpte_v; | ||
98 | |||
99 | /* pick a random entry to start at */ | ||
100 | slot_offset = mftb() & 0x7; | ||
101 | |||
102 | for (i = 0; i < HPTES_PER_GROUP; i++) { | ||
103 | hptep = htab_address + hpte_group + slot_offset; | ||
104 | hpte_v = hptep->v; | ||
105 | |||
106 | if ((hpte_v & HPTE_V_VALID) && !(hpte_v & HPTE_V_BOLTED)) { | ||
107 | /* retry with lock held */ | ||
108 | native_lock_hpte(hptep); | ||
109 | hpte_v = hptep->v; | ||
110 | if ((hpte_v & HPTE_V_VALID) | ||
111 | && !(hpte_v & HPTE_V_BOLTED)) | ||
112 | break; | ||
113 | native_unlock_hpte(hptep); | ||
114 | } | ||
115 | |||
116 | slot_offset++; | ||
117 | slot_offset &= 0x7; | ||
118 | } | ||
119 | |||
120 | if (i == HPTES_PER_GROUP) | ||
121 | return -1; | ||
122 | |||
123 | /* Invalidate the hpte. NOTE: this also unlocks it */ | ||
124 | hptep->v = 0; | ||
125 | |||
126 | return i; | ||
127 | } | ||
128 | |||
129 | static inline void set_pp_bit(unsigned long pp, hpte_t *addr) | ||
130 | { | ||
131 | unsigned long old; | ||
132 | unsigned long *p = &addr->r; | ||
133 | |||
134 | __asm__ __volatile__( | ||
135 | "1: ldarx %0,0,%3\n\ | ||
136 | rldimi %0,%2,0,61\n\ | ||
137 | stdcx. %0,0,%3\n\ | ||
138 | bne 1b" | ||
139 | : "=&r" (old), "=m" (*p) | ||
140 | : "r" (pp), "r" (p), "m" (*p) | ||
141 | : "cc"); | ||
142 | } | ||
143 | |||
144 | /* | ||
145 | * Only works on small pages. Yes its ugly to have to check each slot in | ||
146 | * the group but we only use this during bootup. | ||
147 | */ | ||
148 | static long native_hpte_find(unsigned long vpn) | ||
149 | { | ||
150 | hpte_t *hptep; | ||
151 | unsigned long hash; | ||
152 | unsigned long i, j; | ||
153 | long slot; | ||
154 | unsigned long hpte_v; | ||
155 | |||
156 | hash = hpt_hash(vpn, 0); | ||
157 | |||
158 | for (j = 0; j < 2; j++) { | ||
159 | slot = (hash & htab_hash_mask) * HPTES_PER_GROUP; | ||
160 | for (i = 0; i < HPTES_PER_GROUP; i++) { | ||
161 | hptep = htab_address + slot; | ||
162 | hpte_v = hptep->v; | ||
163 | |||
164 | if ((HPTE_V_AVPN_VAL(hpte_v) == (vpn >> 11)) | ||
165 | && (hpte_v & HPTE_V_VALID) | ||
166 | && ( !!(hpte_v & HPTE_V_SECONDARY) == j)) { | ||
167 | /* HPTE matches */ | ||
168 | if (j) | ||
169 | slot = -slot; | ||
170 | return slot; | ||
171 | } | ||
172 | ++slot; | ||
173 | } | ||
174 | hash = ~hash; | ||
175 | } | ||
176 | |||
177 | return -1; | ||
178 | } | ||
179 | |||
180 | static long native_hpte_updatepp(unsigned long slot, unsigned long newpp, | ||
181 | unsigned long va, int large, int local) | ||
182 | { | ||
183 | hpte_t *hptep = htab_address + slot; | ||
184 | unsigned long hpte_v; | ||
185 | unsigned long avpn = va >> 23; | ||
186 | int ret = 0; | ||
187 | |||
188 | if (large) | ||
189 | avpn &= ~1; | ||
190 | |||
191 | native_lock_hpte(hptep); | ||
192 | |||
193 | hpte_v = hptep->v; | ||
194 | |||
195 | /* Even if we miss, we need to invalidate the TLB */ | ||
196 | if ((HPTE_V_AVPN_VAL(hpte_v) != avpn) | ||
197 | || !(hpte_v & HPTE_V_VALID)) { | ||
198 | native_unlock_hpte(hptep); | ||
199 | ret = -1; | ||
200 | } else { | ||
201 | set_pp_bit(newpp, hptep); | ||
202 | native_unlock_hpte(hptep); | ||
203 | } | ||
204 | |||
205 | /* Ensure it is out of the tlb too */ | ||
206 | if (cpu_has_feature(CPU_FTR_TLBIEL) && !large && local) { | ||
207 | tlbiel(va); | ||
208 | } else { | ||
209 | int lock_tlbie = !cpu_has_feature(CPU_FTR_LOCKLESS_TLBIE); | ||
210 | |||
211 | if (lock_tlbie) | ||
212 | spin_lock(&native_tlbie_lock); | ||
213 | tlbie(va, large); | ||
214 | if (lock_tlbie) | ||
215 | spin_unlock(&native_tlbie_lock); | ||
216 | } | ||
217 | |||
218 | return ret; | ||
219 | } | ||
220 | |||
221 | /* | ||
222 | * Update the page protection bits. Intended to be used to create | ||
223 | * guard pages for kernel data structures on pages which are bolted | ||
224 | * in the HPT. Assumes pages being operated on will not be stolen. | ||
225 | * Does not work on large pages. | ||
226 | * | ||
227 | * No need to lock here because we should be the only user. | ||
228 | */ | ||
229 | static void native_hpte_updateboltedpp(unsigned long newpp, unsigned long ea) | ||
230 | { | ||
231 | unsigned long vsid, va, vpn, flags = 0; | ||
232 | long slot; | ||
233 | hpte_t *hptep; | ||
234 | int lock_tlbie = !cpu_has_feature(CPU_FTR_LOCKLESS_TLBIE); | ||
235 | |||
236 | vsid = get_kernel_vsid(ea); | ||
237 | va = (vsid << 28) | (ea & 0x0fffffff); | ||
238 | vpn = va >> PAGE_SHIFT; | ||
239 | |||
240 | slot = native_hpte_find(vpn); | ||
241 | if (slot == -1) | ||
242 | panic("could not find page to bolt\n"); | ||
243 | hptep = htab_address + slot; | ||
244 | |||
245 | set_pp_bit(newpp, hptep); | ||
246 | |||
247 | /* Ensure it is out of the tlb too */ | ||
248 | if (lock_tlbie) | ||
249 | spin_lock_irqsave(&native_tlbie_lock, flags); | ||
250 | tlbie(va, 0); | ||
251 | if (lock_tlbie) | ||
252 | spin_unlock_irqrestore(&native_tlbie_lock, flags); | ||
253 | } | ||
254 | |||
255 | static void native_hpte_invalidate(unsigned long slot, unsigned long va, | ||
256 | int large, int local) | ||
257 | { | ||
258 | hpte_t *hptep = htab_address + slot; | ||
259 | unsigned long hpte_v; | ||
260 | unsigned long avpn = va >> 23; | ||
261 | unsigned long flags; | ||
262 | int lock_tlbie = !cpu_has_feature(CPU_FTR_LOCKLESS_TLBIE); | ||
263 | |||
264 | if (large) | ||
265 | avpn &= ~1; | ||
266 | |||
267 | local_irq_save(flags); | ||
268 | native_lock_hpte(hptep); | ||
269 | |||
270 | hpte_v = hptep->v; | ||
271 | |||
272 | /* Even if we miss, we need to invalidate the TLB */ | ||
273 | if ((HPTE_V_AVPN_VAL(hpte_v) != avpn) | ||
274 | || !(hpte_v & HPTE_V_VALID)) { | ||
275 | native_unlock_hpte(hptep); | ||
276 | } else { | ||
277 | /* Invalidate the hpte. NOTE: this also unlocks it */ | ||
278 | hptep->v = 0; | ||
279 | } | ||
280 | |||
281 | /* Invalidate the tlb */ | ||
282 | if (cpu_has_feature(CPU_FTR_TLBIEL) && !large && local) { | ||
283 | tlbiel(va); | ||
284 | } else { | ||
285 | if (lock_tlbie) | ||
286 | spin_lock(&native_tlbie_lock); | ||
287 | tlbie(va, large); | ||
288 | if (lock_tlbie) | ||
289 | spin_unlock(&native_tlbie_lock); | ||
290 | } | ||
291 | local_irq_restore(flags); | ||
292 | } | ||
293 | |||
294 | /* | ||
295 | * clear all mappings on kexec. All cpus are in real mode (or they will | ||
296 | * be when they isi), and we are the only one left. We rely on our kernel | ||
297 | * mapping being 0xC0's and the hardware ignoring those two real bits. | ||
298 | * | ||
299 | * TODO: add batching support when enabled. remember, no dynamic memory here, | ||
300 | * athough there is the control page available... | ||
301 | */ | ||
302 | static void native_hpte_clear(void) | ||
303 | { | ||
304 | unsigned long slot, slots, flags; | ||
305 | hpte_t *hptep = htab_address; | ||
306 | unsigned long hpte_v; | ||
307 | unsigned long pteg_count; | ||
308 | |||
309 | pteg_count = htab_hash_mask + 1; | ||
310 | |||
311 | local_irq_save(flags); | ||
312 | |||
313 | /* we take the tlbie lock and hold it. Some hardware will | ||
314 | * deadlock if we try to tlbie from two processors at once. | ||
315 | */ | ||
316 | spin_lock(&native_tlbie_lock); | ||
317 | |||
318 | slots = pteg_count * HPTES_PER_GROUP; | ||
319 | |||
320 | for (slot = 0; slot < slots; slot++, hptep++) { | ||
321 | /* | ||
322 | * we could lock the pte here, but we are the only cpu | ||
323 | * running, right? and for crash dump, we probably | ||
324 | * don't want to wait for a maybe bad cpu. | ||
325 | */ | ||
326 | hpte_v = hptep->v; | ||
327 | |||
328 | if (hpte_v & HPTE_V_VALID) { | ||
329 | hptep->v = 0; | ||
330 | tlbie(slot2va(hpte_v, slot), hpte_v & HPTE_V_LARGE); | ||
331 | } | ||
332 | } | ||
333 | |||
334 | spin_unlock(&native_tlbie_lock); | ||
335 | local_irq_restore(flags); | ||
336 | } | ||
337 | |||
338 | static void native_flush_hash_range(unsigned long number, int local) | ||
339 | { | ||
340 | unsigned long va, vpn, hash, secondary, slot, flags, avpn; | ||
341 | int i, j; | ||
342 | hpte_t *hptep; | ||
343 | unsigned long hpte_v; | ||
344 | struct ppc64_tlb_batch *batch = &__get_cpu_var(ppc64_tlb_batch); | ||
345 | unsigned long large = batch->large; | ||
346 | |||
347 | local_irq_save(flags); | ||
348 | |||
349 | j = 0; | ||
350 | for (i = 0; i < number; i++) { | ||
351 | va = batch->vaddr[j]; | ||
352 | if (large) | ||
353 | vpn = va >> HPAGE_SHIFT; | ||
354 | else | ||
355 | vpn = va >> PAGE_SHIFT; | ||
356 | hash = hpt_hash(vpn, large); | ||
357 | secondary = (pte_val(batch->pte[i]) & _PAGE_SECONDARY) >> 15; | ||
358 | if (secondary) | ||
359 | hash = ~hash; | ||
360 | slot = (hash & htab_hash_mask) * HPTES_PER_GROUP; | ||
361 | slot += (pte_val(batch->pte[i]) & _PAGE_GROUP_IX) >> 12; | ||
362 | |||
363 | hptep = htab_address + slot; | ||
364 | |||
365 | avpn = va >> 23; | ||
366 | if (large) | ||
367 | avpn &= ~0x1UL; | ||
368 | |||
369 | native_lock_hpte(hptep); | ||
370 | |||
371 | hpte_v = hptep->v; | ||
372 | |||
373 | /* Even if we miss, we need to invalidate the TLB */ | ||
374 | if ((HPTE_V_AVPN_VAL(hpte_v) != avpn) | ||
375 | || !(hpte_v & HPTE_V_VALID)) { | ||
376 | native_unlock_hpte(hptep); | ||
377 | } else { | ||
378 | /* Invalidate the hpte. NOTE: this also unlocks it */ | ||
379 | hptep->v = 0; | ||
380 | } | ||
381 | |||
382 | j++; | ||
383 | } | ||
384 | |||
385 | if (cpu_has_feature(CPU_FTR_TLBIEL) && !large && local) { | ||
386 | asm volatile("ptesync":::"memory"); | ||
387 | |||
388 | for (i = 0; i < j; i++) | ||
389 | __tlbiel(batch->vaddr[i]); | ||
390 | |||
391 | asm volatile("ptesync":::"memory"); | ||
392 | } else { | ||
393 | int lock_tlbie = !cpu_has_feature(CPU_FTR_LOCKLESS_TLBIE); | ||
394 | |||
395 | if (lock_tlbie) | ||
396 | spin_lock(&native_tlbie_lock); | ||
397 | |||
398 | asm volatile("ptesync":::"memory"); | ||
399 | |||
400 | for (i = 0; i < j; i++) | ||
401 | __tlbie(batch->vaddr[i], large); | ||
402 | |||
403 | asm volatile("eieio; tlbsync; ptesync":::"memory"); | ||
404 | |||
405 | if (lock_tlbie) | ||
406 | spin_unlock(&native_tlbie_lock); | ||
407 | } | ||
408 | |||
409 | local_irq_restore(flags); | ||
410 | } | ||
411 | |||
412 | #ifdef CONFIG_PPC_PSERIES | ||
413 | /* Disable TLB batching on nighthawk */ | ||
414 | static inline int tlb_batching_enabled(void) | ||
415 | { | ||
416 | struct device_node *root = of_find_node_by_path("/"); | ||
417 | int enabled = 1; | ||
418 | |||
419 | if (root) { | ||
420 | const char *model = get_property(root, "model", NULL); | ||
421 | if (model && !strcmp(model, "IBM,9076-N81")) | ||
422 | enabled = 0; | ||
423 | of_node_put(root); | ||
424 | } | ||
425 | |||
426 | return enabled; | ||
427 | } | ||
428 | #else | ||
429 | static inline int tlb_batching_enabled(void) | ||
430 | { | ||
431 | return 1; | ||
432 | } | ||
433 | #endif | ||
434 | |||
435 | void hpte_init_native(void) | ||
436 | { | ||
437 | ppc_md.hpte_invalidate = native_hpte_invalidate; | ||
438 | ppc_md.hpte_updatepp = native_hpte_updatepp; | ||
439 | ppc_md.hpte_updateboltedpp = native_hpte_updateboltedpp; | ||
440 | ppc_md.hpte_insert = native_hpte_insert; | ||
441 | ppc_md.hpte_remove = native_hpte_remove; | ||
442 | ppc_md.hpte_clear_all = native_hpte_clear; | ||
443 | if (tlb_batching_enabled()) | ||
444 | ppc_md.flush_hash_range = native_flush_hash_range; | ||
445 | htab_finish_init(); | ||
446 | } | ||