diff options
author | Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com> | 2013-06-20 05:00:13 -0400 |
---|---|---|
committer | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2013-06-21 02:01:52 -0400 |
commit | db3d8534903c8a9617142975bec6db95acaba753 (patch) | |
tree | eea268dcfdd5cce84aa9b32025528dbc2d71d5aa /arch/powerpc/mm | |
parent | 8998897b8f966c9036307b5130494d4bf1e6cb18 (diff) |
powerpc/mm: handle hugepage size correctly when invalidating hpte entries
If a hash bucket gets full, we "evict" a more/less random entry from it.
When we do that we don't invalidate the TLB (hpte_remove) because we assume
the old translation is still technically "valid". This implies that when
we are invalidating or updating pte, even if HPTE entry is not valid
we should do a tlb invalidate. With hugepages, we need to pass the correct
actual page size value for tlb invalidation.
This change update the patch 0608d692463598c1d6e826d9dd7283381b4f246c
"powerpc/mm: Always invalidate tlb on hpte invalidate and update" to handle
transparent hugepages correctly.
Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Diffstat (limited to 'arch/powerpc/mm')
-rw-r--r-- | arch/powerpc/mm/hash_low_64.S | 21 | ||||
-rw-r--r-- | arch/powerpc/mm/hash_native_64.c | 122 | ||||
-rw-r--r-- | arch/powerpc/mm/hash_utils_64.c | 9 | ||||
-rw-r--r-- | arch/powerpc/mm/hugetlbpage-hash64.c | 2 |
4 files changed, 65 insertions, 89 deletions
diff --git a/arch/powerpc/mm/hash_low_64.S b/arch/powerpc/mm/hash_low_64.S index 0e980acae67c..d3cbda62857b 100644 --- a/arch/powerpc/mm/hash_low_64.S +++ b/arch/powerpc/mm/hash_low_64.S | |||
@@ -289,9 +289,10 @@ htab_modify_pte: | |||
289 | 289 | ||
290 | /* Call ppc_md.hpte_updatepp */ | 290 | /* Call ppc_md.hpte_updatepp */ |
291 | mr r5,r29 /* vpn */ | 291 | mr r5,r29 /* vpn */ |
292 | li r6,MMU_PAGE_4K /* page size */ | 292 | li r6,MMU_PAGE_4K /* base page size */ |
293 | ld r7,STK_PARAM(R9)(r1) /* segment size */ | 293 | li r7,MMU_PAGE_4K /* actual page size */ |
294 | ld r8,STK_PARAM(R8)(r1) /* get "local" param */ | 294 | ld r8,STK_PARAM(R9)(r1) /* segment size */ |
295 | ld r9,STK_PARAM(R8)(r1) /* get "local" param */ | ||
295 | _GLOBAL(htab_call_hpte_updatepp) | 296 | _GLOBAL(htab_call_hpte_updatepp) |
296 | bl . /* Patched by htab_finish_init() */ | 297 | bl . /* Patched by htab_finish_init() */ |
297 | 298 | ||
@@ -649,9 +650,10 @@ htab_modify_pte: | |||
649 | 650 | ||
650 | /* Call ppc_md.hpte_updatepp */ | 651 | /* Call ppc_md.hpte_updatepp */ |
651 | mr r5,r29 /* vpn */ | 652 | mr r5,r29 /* vpn */ |
652 | li r6,MMU_PAGE_4K /* page size */ | 653 | li r6,MMU_PAGE_4K /* base page size */ |
653 | ld r7,STK_PARAM(R9)(r1) /* segment size */ | 654 | li r7,MMU_PAGE_4K /* actual page size */ |
654 | ld r8,STK_PARAM(R8)(r1) /* get "local" param */ | 655 | ld r8,STK_PARAM(R9)(r1) /* segment size */ |
656 | ld r9,STK_PARAM(R8)(r1) /* get "local" param */ | ||
655 | _GLOBAL(htab_call_hpte_updatepp) | 657 | _GLOBAL(htab_call_hpte_updatepp) |
656 | bl . /* patched by htab_finish_init() */ | 658 | bl . /* patched by htab_finish_init() */ |
657 | 659 | ||
@@ -937,9 +939,10 @@ ht64_modify_pte: | |||
937 | 939 | ||
938 | /* Call ppc_md.hpte_updatepp */ | 940 | /* Call ppc_md.hpte_updatepp */ |
939 | mr r5,r29 /* vpn */ | 941 | mr r5,r29 /* vpn */ |
940 | li r6,MMU_PAGE_64K | 942 | li r6,MMU_PAGE_64K /* base page size */ |
941 | ld r7,STK_PARAM(R9)(r1) /* segment size */ | 943 | li r7,MMU_PAGE_64K /* actual page size */ |
942 | ld r8,STK_PARAM(R8)(r1) /* get "local" param */ | 944 | ld r8,STK_PARAM(R9)(r1) /* segment size */ |
945 | ld r9,STK_PARAM(R8)(r1) /* get "local" param */ | ||
943 | _GLOBAL(ht64_call_hpte_updatepp) | 946 | _GLOBAL(ht64_call_hpte_updatepp) |
944 | bl . /* patched by htab_finish_init() */ | 947 | bl . /* patched by htab_finish_init() */ |
945 | 948 | ||
diff --git a/arch/powerpc/mm/hash_native_64.c b/arch/powerpc/mm/hash_native_64.c index 4c122c3f1623..6d152bc993e5 100644 --- a/arch/powerpc/mm/hash_native_64.c +++ b/arch/powerpc/mm/hash_native_64.c | |||
@@ -273,61 +273,15 @@ static long native_hpte_remove(unsigned long hpte_group) | |||
273 | return i; | 273 | return i; |
274 | } | 274 | } |
275 | 275 | ||
276 | static inline int __hpte_actual_psize(unsigned int lp, int psize) | ||
277 | { | ||
278 | int i, shift; | ||
279 | unsigned int mask; | ||
280 | |||
281 | /* start from 1 ignoring MMU_PAGE_4K */ | ||
282 | for (i = 1; i < MMU_PAGE_COUNT; i++) { | ||
283 | |||
284 | /* invalid penc */ | ||
285 | if (mmu_psize_defs[psize].penc[i] == -1) | ||
286 | continue; | ||
287 | /* | ||
288 | * encoding bits per actual page size | ||
289 | * PTE LP actual page size | ||
290 | * rrrr rrrz >=8KB | ||
291 | * rrrr rrzz >=16KB | ||
292 | * rrrr rzzz >=32KB | ||
293 | * rrrr zzzz >=64KB | ||
294 | * ....... | ||
295 | */ | ||
296 | shift = mmu_psize_defs[i].shift - LP_SHIFT; | ||
297 | if (shift > LP_BITS) | ||
298 | shift = LP_BITS; | ||
299 | mask = (1 << shift) - 1; | ||
300 | if ((lp & mask) == mmu_psize_defs[psize].penc[i]) | ||
301 | return i; | ||
302 | } | ||
303 | return -1; | ||
304 | } | ||
305 | |||
306 | static inline int hpte_actual_psize(struct hash_pte *hptep, int psize) | ||
307 | { | ||
308 | /* Look at the 8 bit LP value */ | ||
309 | unsigned int lp = (hptep->r >> LP_SHIFT) & ((1 << LP_BITS) - 1); | ||
310 | |||
311 | if (!(hptep->v & HPTE_V_VALID)) | ||
312 | return -1; | ||
313 | |||
314 | /* First check if it is large page */ | ||
315 | if (!(hptep->v & HPTE_V_LARGE)) | ||
316 | return MMU_PAGE_4K; | ||
317 | |||
318 | return __hpte_actual_psize(lp, psize); | ||
319 | } | ||
320 | |||
321 | static long native_hpte_updatepp(unsigned long slot, unsigned long newpp, | 276 | static long native_hpte_updatepp(unsigned long slot, unsigned long newpp, |
322 | unsigned long vpn, int psize, int ssize, | 277 | unsigned long vpn, int bpsize, |
323 | int local) | 278 | int apsize, int ssize, int local) |
324 | { | 279 | { |
325 | struct hash_pte *hptep = htab_address + slot; | 280 | struct hash_pte *hptep = htab_address + slot; |
326 | unsigned long hpte_v, want_v; | 281 | unsigned long hpte_v, want_v; |
327 | int ret = 0; | 282 | int ret = 0; |
328 | int actual_psize; | ||
329 | 283 | ||
330 | want_v = hpte_encode_avpn(vpn, psize, ssize); | 284 | want_v = hpte_encode_avpn(vpn, bpsize, ssize); |
331 | 285 | ||
332 | DBG_LOW(" update(vpn=%016lx, avpnv=%016lx, group=%lx, newpp=%lx)", | 286 | DBG_LOW(" update(vpn=%016lx, avpnv=%016lx, group=%lx, newpp=%lx)", |
333 | vpn, want_v & HPTE_V_AVPN, slot, newpp); | 287 | vpn, want_v & HPTE_V_AVPN, slot, newpp); |
@@ -335,7 +289,6 @@ static long native_hpte_updatepp(unsigned long slot, unsigned long newpp, | |||
335 | native_lock_hpte(hptep); | 289 | native_lock_hpte(hptep); |
336 | 290 | ||
337 | hpte_v = hptep->v; | 291 | hpte_v = hptep->v; |
338 | actual_psize = hpte_actual_psize(hptep, psize); | ||
339 | /* | 292 | /* |
340 | * We need to invalidate the TLB always because hpte_remove doesn't do | 293 | * We need to invalidate the TLB always because hpte_remove doesn't do |
341 | * a tlb invalidate. If a hash bucket gets full, we "evict" a more/less | 294 | * a tlb invalidate. If a hash bucket gets full, we "evict" a more/less |
@@ -343,12 +296,7 @@ static long native_hpte_updatepp(unsigned long slot, unsigned long newpp, | |||
343 | * (hpte_remove) because we assume the old translation is still | 296 | * (hpte_remove) because we assume the old translation is still |
344 | * technically "valid". | 297 | * technically "valid". |
345 | */ | 298 | */ |
346 | if (actual_psize < 0) { | 299 | if (!HPTE_V_COMPARE(hpte_v, want_v) || !(hpte_v & HPTE_V_VALID)) { |
347 | actual_psize = psize; | ||
348 | ret = -1; | ||
349 | goto err_out; | ||
350 | } | ||
351 | if (!HPTE_V_COMPARE(hpte_v, want_v)) { | ||
352 | DBG_LOW(" -> miss\n"); | 300 | DBG_LOW(" -> miss\n"); |
353 | ret = -1; | 301 | ret = -1; |
354 | } else { | 302 | } else { |
@@ -357,11 +305,10 @@ static long native_hpte_updatepp(unsigned long slot, unsigned long newpp, | |||
357 | hptep->r = (hptep->r & ~(HPTE_R_PP | HPTE_R_N)) | | 305 | hptep->r = (hptep->r & ~(HPTE_R_PP | HPTE_R_N)) | |
358 | (newpp & (HPTE_R_PP | HPTE_R_N | HPTE_R_C)); | 306 | (newpp & (HPTE_R_PP | HPTE_R_N | HPTE_R_C)); |
359 | } | 307 | } |
360 | err_out: | ||
361 | native_unlock_hpte(hptep); | 308 | native_unlock_hpte(hptep); |
362 | 309 | ||
363 | /* Ensure it is out of the tlb too. */ | 310 | /* Ensure it is out of the tlb too. */ |
364 | tlbie(vpn, psize, actual_psize, ssize, local); | 311 | tlbie(vpn, bpsize, apsize, ssize, local); |
365 | 312 | ||
366 | return ret; | 313 | return ret; |
367 | } | 314 | } |
@@ -402,7 +349,6 @@ static long native_hpte_find(unsigned long vpn, int psize, int ssize) | |||
402 | static void native_hpte_updateboltedpp(unsigned long newpp, unsigned long ea, | 349 | static void native_hpte_updateboltedpp(unsigned long newpp, unsigned long ea, |
403 | int psize, int ssize) | 350 | int psize, int ssize) |
404 | { | 351 | { |
405 | int actual_psize; | ||
406 | unsigned long vpn; | 352 | unsigned long vpn; |
407 | unsigned long vsid; | 353 | unsigned long vsid; |
408 | long slot; | 354 | long slot; |
@@ -415,36 +361,33 @@ static void native_hpte_updateboltedpp(unsigned long newpp, unsigned long ea, | |||
415 | if (slot == -1) | 361 | if (slot == -1) |
416 | panic("could not find page to bolt\n"); | 362 | panic("could not find page to bolt\n"); |
417 | hptep = htab_address + slot; | 363 | hptep = htab_address + slot; |
418 | actual_psize = hpte_actual_psize(hptep, psize); | ||
419 | if (actual_psize < 0) | ||
420 | actual_psize = psize; | ||
421 | 364 | ||
422 | /* Update the HPTE */ | 365 | /* Update the HPTE */ |
423 | hptep->r = (hptep->r & ~(HPTE_R_PP | HPTE_R_N)) | | 366 | hptep->r = (hptep->r & ~(HPTE_R_PP | HPTE_R_N)) | |
424 | (newpp & (HPTE_R_PP | HPTE_R_N)); | 367 | (newpp & (HPTE_R_PP | HPTE_R_N)); |
425 | 368 | /* | |
426 | /* Ensure it is out of the tlb too. */ | 369 | * Ensure it is out of the tlb too. Bolted entries base and |
427 | tlbie(vpn, psize, actual_psize, ssize, 0); | 370 | * actual page size will be same. |
371 | */ | ||
372 | tlbie(vpn, psize, psize, ssize, 0); | ||
428 | } | 373 | } |
429 | 374 | ||
430 | static void native_hpte_invalidate(unsigned long slot, unsigned long vpn, | 375 | static void native_hpte_invalidate(unsigned long slot, unsigned long vpn, |
431 | int psize, int ssize, int local) | 376 | int bpsize, int apsize, int ssize, int local) |
432 | { | 377 | { |
433 | struct hash_pte *hptep = htab_address + slot; | 378 | struct hash_pte *hptep = htab_address + slot; |
434 | unsigned long hpte_v; | 379 | unsigned long hpte_v; |
435 | unsigned long want_v; | 380 | unsigned long want_v; |
436 | unsigned long flags; | 381 | unsigned long flags; |
437 | int actual_psize; | ||
438 | 382 | ||
439 | local_irq_save(flags); | 383 | local_irq_save(flags); |
440 | 384 | ||
441 | DBG_LOW(" invalidate(vpn=%016lx, hash: %lx)\n", vpn, slot); | 385 | DBG_LOW(" invalidate(vpn=%016lx, hash: %lx)\n", vpn, slot); |
442 | 386 | ||
443 | want_v = hpte_encode_avpn(vpn, psize, ssize); | 387 | want_v = hpte_encode_avpn(vpn, bpsize, ssize); |
444 | native_lock_hpte(hptep); | 388 | native_lock_hpte(hptep); |
445 | hpte_v = hptep->v; | 389 | hpte_v = hptep->v; |
446 | 390 | ||
447 | actual_psize = hpte_actual_psize(hptep, psize); | ||
448 | /* | 391 | /* |
449 | * We need to invalidate the TLB always because hpte_remove doesn't do | 392 | * We need to invalidate the TLB always because hpte_remove doesn't do |
450 | * a tlb invalidate. If a hash bucket gets full, we "evict" a more/less | 393 | * a tlb invalidate. If a hash bucket gets full, we "evict" a more/less |
@@ -452,23 +395,48 @@ static void native_hpte_invalidate(unsigned long slot, unsigned long vpn, | |||
452 | * (hpte_remove) because we assume the old translation is still | 395 | * (hpte_remove) because we assume the old translation is still |
453 | * technically "valid". | 396 | * technically "valid". |
454 | */ | 397 | */ |
455 | if (actual_psize < 0) { | 398 | if (!HPTE_V_COMPARE(hpte_v, want_v) || !(hpte_v & HPTE_V_VALID)) |
456 | actual_psize = psize; | ||
457 | native_unlock_hpte(hptep); | ||
458 | goto err_out; | ||
459 | } | ||
460 | if (!HPTE_V_COMPARE(hpte_v, want_v)) | ||
461 | native_unlock_hpte(hptep); | 399 | native_unlock_hpte(hptep); |
462 | else | 400 | else |
463 | /* Invalidate the hpte. NOTE: this also unlocks it */ | 401 | /* Invalidate the hpte. NOTE: this also unlocks it */ |
464 | hptep->v = 0; | 402 | hptep->v = 0; |
465 | 403 | ||
466 | err_out: | ||
467 | /* Invalidate the TLB */ | 404 | /* Invalidate the TLB */ |
468 | tlbie(vpn, psize, actual_psize, ssize, local); | 405 | tlbie(vpn, bpsize, apsize, ssize, local); |
406 | |||
469 | local_irq_restore(flags); | 407 | local_irq_restore(flags); |
470 | } | 408 | } |
471 | 409 | ||
410 | static inline int __hpte_actual_psize(unsigned int lp, int psize) | ||
411 | { | ||
412 | int i, shift; | ||
413 | unsigned int mask; | ||
414 | |||
415 | /* start from 1 ignoring MMU_PAGE_4K */ | ||
416 | for (i = 1; i < MMU_PAGE_COUNT; i++) { | ||
417 | |||
418 | /* invalid penc */ | ||
419 | if (mmu_psize_defs[psize].penc[i] == -1) | ||
420 | continue; | ||
421 | /* | ||
422 | * encoding bits per actual page size | ||
423 | * PTE LP actual page size | ||
424 | * rrrr rrrz >=8KB | ||
425 | * rrrr rrzz >=16KB | ||
426 | * rrrr rzzz >=32KB | ||
427 | * rrrr zzzz >=64KB | ||
428 | * ....... | ||
429 | */ | ||
430 | shift = mmu_psize_defs[i].shift - LP_SHIFT; | ||
431 | if (shift > LP_BITS) | ||
432 | shift = LP_BITS; | ||
433 | mask = (1 << shift) - 1; | ||
434 | if ((lp & mask) == mmu_psize_defs[psize].penc[i]) | ||
435 | return i; | ||
436 | } | ||
437 | return -1; | ||
438 | } | ||
439 | |||
472 | static void hpte_decode(struct hash_pte *hpte, unsigned long slot, | 440 | static void hpte_decode(struct hash_pte *hpte, unsigned long slot, |
473 | int *psize, int *apsize, int *ssize, unsigned long *vpn) | 441 | int *psize, int *apsize, int *ssize, unsigned long *vpn) |
474 | { | 442 | { |
diff --git a/arch/powerpc/mm/hash_utils_64.c b/arch/powerpc/mm/hash_utils_64.c index e303a6d74e3a..2f470809876f 100644 --- a/arch/powerpc/mm/hash_utils_64.c +++ b/arch/powerpc/mm/hash_utils_64.c | |||
@@ -1232,7 +1232,11 @@ void flush_hash_page(unsigned long vpn, real_pte_t pte, int psize, int ssize, | |||
1232 | slot = (hash & htab_hash_mask) * HPTES_PER_GROUP; | 1232 | slot = (hash & htab_hash_mask) * HPTES_PER_GROUP; |
1233 | slot += hidx & _PTEIDX_GROUP_IX; | 1233 | slot += hidx & _PTEIDX_GROUP_IX; |
1234 | DBG_LOW(" sub %ld: hash=%lx, hidx=%lx\n", index, slot, hidx); | 1234 | DBG_LOW(" sub %ld: hash=%lx, hidx=%lx\n", index, slot, hidx); |
1235 | ppc_md.hpte_invalidate(slot, vpn, psize, ssize, local); | 1235 | /* |
1236 | * We use same base page size and actual psize, because we don't | ||
1237 | * use these functions for hugepage | ||
1238 | */ | ||
1239 | ppc_md.hpte_invalidate(slot, vpn, psize, psize, ssize, local); | ||
1236 | } pte_iterate_hashed_end(); | 1240 | } pte_iterate_hashed_end(); |
1237 | 1241 | ||
1238 | #ifdef CONFIG_PPC_TRANSACTIONAL_MEM | 1242 | #ifdef CONFIG_PPC_TRANSACTIONAL_MEM |
@@ -1365,7 +1369,8 @@ static void kernel_unmap_linear_page(unsigned long vaddr, unsigned long lmi) | |||
1365 | hash = ~hash; | 1369 | hash = ~hash; |
1366 | slot = (hash & htab_hash_mask) * HPTES_PER_GROUP; | 1370 | slot = (hash & htab_hash_mask) * HPTES_PER_GROUP; |
1367 | slot += hidx & _PTEIDX_GROUP_IX; | 1371 | slot += hidx & _PTEIDX_GROUP_IX; |
1368 | ppc_md.hpte_invalidate(slot, vpn, mmu_linear_psize, mmu_kernel_ssize, 0); | 1372 | ppc_md.hpte_invalidate(slot, vpn, mmu_linear_psize, mmu_linear_psize, |
1373 | mmu_kernel_ssize, 0); | ||
1369 | } | 1374 | } |
1370 | 1375 | ||
1371 | void kernel_map_pages(struct page *page, int numpages, int enable) | 1376 | void kernel_map_pages(struct page *page, int numpages, int enable) |
diff --git a/arch/powerpc/mm/hugetlbpage-hash64.c b/arch/powerpc/mm/hugetlbpage-hash64.c index 0f1d94a1fb82..0b7fb6761015 100644 --- a/arch/powerpc/mm/hugetlbpage-hash64.c +++ b/arch/powerpc/mm/hugetlbpage-hash64.c | |||
@@ -81,7 +81,7 @@ int __hash_page_huge(unsigned long ea, unsigned long access, unsigned long vsid, | |||
81 | slot += (old_pte & _PAGE_F_GIX) >> 12; | 81 | slot += (old_pte & _PAGE_F_GIX) >> 12; |
82 | 82 | ||
83 | if (ppc_md.hpte_updatepp(slot, rflags, vpn, mmu_psize, | 83 | if (ppc_md.hpte_updatepp(slot, rflags, vpn, mmu_psize, |
84 | ssize, local) == -1) | 84 | mmu_psize, ssize, local) == -1) |
85 | old_pte &= ~_PAGE_HPTEFLAGS; | 85 | old_pte &= ~_PAGE_HPTEFLAGS; |
86 | } | 86 | } |
87 | 87 | ||