diff options
Diffstat (limited to 'arch/powerpc/mm/pgtable.c')
-rw-r--r-- | arch/powerpc/mm/pgtable.c | 104 |
1 files changed, 104 insertions, 0 deletions
diff --git a/arch/powerpc/mm/pgtable.c b/arch/powerpc/mm/pgtable.c index d3d61d29b4f1..9f4ccd15849f 100644 --- a/arch/powerpc/mm/pgtable.c +++ b/arch/powerpc/mm/pgtable.c | |||
@@ -30,6 +30,7 @@ | |||
30 | #include <asm/pgalloc.h> | 30 | #include <asm/pgalloc.h> |
31 | #include <asm/tlbflush.h> | 31 | #include <asm/tlbflush.h> |
32 | #include <asm/tlb.h> | 32 | #include <asm/tlb.h> |
33 | #include <asm/hugetlb.h> | ||
33 | 34 | ||
34 | static inline int is_exec_fault(void) | 35 | static inline int is_exec_fault(void) |
35 | { | 36 | { |
@@ -299,3 +300,106 @@ unsigned long vmalloc_to_phys(void *va) | |||
299 | return __pa(pfn_to_kaddr(pfn)) + offset_in_page(va); | 300 | return __pa(pfn_to_kaddr(pfn)) + offset_in_page(va); |
300 | } | 301 | } |
301 | EXPORT_SYMBOL_GPL(vmalloc_to_phys); | 302 | EXPORT_SYMBOL_GPL(vmalloc_to_phys); |
303 | |||
304 | /* | ||
305 | * We have 4 cases for pgds and pmds: | ||
306 | * (1) invalid (all zeroes) | ||
307 | * (2) pointer to next table, as normal; bottom 6 bits == 0 | ||
308 | * (3) leaf pte for huge page _PAGE_PTE set | ||
309 | * (4) hugepd pointer, _PAGE_PTE = 0 and bits [2..6] indicate size of table | ||
310 | * | ||
311 | * So long as we atomically load page table pointers we are safe against teardown, | ||
312 | * we can follow the address down to the the page and take a ref on it. | ||
313 | * This function need to be called with interrupts disabled. We use this variant | ||
314 | * when we have MSR[EE] = 0 but the paca->irq_soft_mask = IRQS_ENABLED | ||
315 | */ | ||
316 | pte_t *__find_linux_pte(pgd_t *pgdir, unsigned long ea, | ||
317 | bool *is_thp, unsigned *hpage_shift) | ||
318 | { | ||
319 | pgd_t pgd, *pgdp; | ||
320 | pud_t pud, *pudp; | ||
321 | pmd_t pmd, *pmdp; | ||
322 | pte_t *ret_pte; | ||
323 | hugepd_t *hpdp = NULL; | ||
324 | unsigned pdshift = PGDIR_SHIFT; | ||
325 | |||
326 | if (hpage_shift) | ||
327 | *hpage_shift = 0; | ||
328 | |||
329 | if (is_thp) | ||
330 | *is_thp = false; | ||
331 | |||
332 | pgdp = pgdir + pgd_index(ea); | ||
333 | pgd = READ_ONCE(*pgdp); | ||
334 | /* | ||
335 | * Always operate on the local stack value. This make sure the | ||
336 | * value don't get updated by a parallel THP split/collapse, | ||
337 | * page fault or a page unmap. The return pte_t * is still not | ||
338 | * stable. So should be checked there for above conditions. | ||
339 | */ | ||
340 | if (pgd_none(pgd)) | ||
341 | return NULL; | ||
342 | else if (pgd_huge(pgd)) { | ||
343 | ret_pte = (pte_t *) pgdp; | ||
344 | goto out; | ||
345 | } else if (is_hugepd(__hugepd(pgd_val(pgd)))) | ||
346 | hpdp = (hugepd_t *)&pgd; | ||
347 | else { | ||
348 | /* | ||
349 | * Even if we end up with an unmap, the pgtable will not | ||
350 | * be freed, because we do an rcu free and here we are | ||
351 | * irq disabled | ||
352 | */ | ||
353 | pdshift = PUD_SHIFT; | ||
354 | pudp = pud_offset(&pgd, ea); | ||
355 | pud = READ_ONCE(*pudp); | ||
356 | |||
357 | if (pud_none(pud)) | ||
358 | return NULL; | ||
359 | else if (pud_huge(pud)) { | ||
360 | ret_pte = (pte_t *) pudp; | ||
361 | goto out; | ||
362 | } else if (is_hugepd(__hugepd(pud_val(pud)))) | ||
363 | hpdp = (hugepd_t *)&pud; | ||
364 | else { | ||
365 | pdshift = PMD_SHIFT; | ||
366 | pmdp = pmd_offset(&pud, ea); | ||
367 | pmd = READ_ONCE(*pmdp); | ||
368 | /* | ||
369 | * A hugepage collapse is captured by pmd_none, because | ||
370 | * it mark the pmd none and do a hpte invalidate. | ||
371 | */ | ||
372 | if (pmd_none(pmd)) | ||
373 | return NULL; | ||
374 | |||
375 | if (pmd_trans_huge(pmd) || pmd_devmap(pmd)) { | ||
376 | if (is_thp) | ||
377 | *is_thp = true; | ||
378 | ret_pte = (pte_t *) pmdp; | ||
379 | goto out; | ||
380 | } | ||
381 | /* | ||
382 | * pmd_large check below will handle the swap pmd pte | ||
383 | * we need to do both the check because they are config | ||
384 | * dependent. | ||
385 | */ | ||
386 | if (pmd_huge(pmd) || pmd_large(pmd)) { | ||
387 | ret_pte = (pte_t *) pmdp; | ||
388 | goto out; | ||
389 | } else if (is_hugepd(__hugepd(pmd_val(pmd)))) | ||
390 | hpdp = (hugepd_t *)&pmd; | ||
391 | else | ||
392 | return pte_offset_kernel(&pmd, ea); | ||
393 | } | ||
394 | } | ||
395 | if (!hpdp) | ||
396 | return NULL; | ||
397 | |||
398 | ret_pte = hugepte_offset(*hpdp, ea, pdshift); | ||
399 | pdshift = hugepd_shift(*hpdp); | ||
400 | out: | ||
401 | if (hpage_shift) | ||
402 | *hpage_shift = pdshift; | ||
403 | return ret_pte; | ||
404 | } | ||
405 | EXPORT_SYMBOL_GPL(__find_linux_pte); | ||