diff options
Diffstat (limited to 'include/asm-generic/pgtable.h')
-rw-r--r-- | include/asm-generic/pgtable.h | 61 |
1 files changed, 61 insertions, 0 deletions
diff --git a/include/asm-generic/pgtable.h b/include/asm-generic/pgtable.h index 236b1056839..125c54e9851 100644 --- a/include/asm-generic/pgtable.h +++ b/include/asm-generic/pgtable.h | |||
@@ -426,6 +426,8 @@ extern void untrack_pfn_vma(struct vm_area_struct *vma, unsigned long pfn, | |||
426 | unsigned long size); | 426 | unsigned long size); |
427 | #endif | 427 | #endif |
428 | 428 | ||
429 | #ifdef CONFIG_MMU | ||
430 | |||
429 | #ifndef CONFIG_TRANSPARENT_HUGEPAGE | 431 | #ifndef CONFIG_TRANSPARENT_HUGEPAGE |
430 | static inline int pmd_trans_huge(pmd_t pmd) | 432 | static inline int pmd_trans_huge(pmd_t pmd) |
431 | { | 433 | { |
@@ -442,7 +444,66 @@ static inline int pmd_write(pmd_t pmd) | |||
442 | return 0; | 444 | return 0; |
443 | } | 445 | } |
444 | #endif /* __HAVE_ARCH_PMD_WRITE */ | 446 | #endif /* __HAVE_ARCH_PMD_WRITE */ |
447 | #endif /* CONFIG_TRANSPARENT_HUGEPAGE */ | ||
448 | |||
449 | /* | ||
450 | * This function is meant to be used by sites walking pagetables with | ||
451 | * the mmap_sem hold in read mode to protect against MADV_DONTNEED and | ||
452 | * transhuge page faults. MADV_DONTNEED can convert a transhuge pmd | ||
453 | * into a null pmd and the transhuge page fault can convert a null pmd | ||
454 | * into an hugepmd or into a regular pmd (if the hugepage allocation | ||
455 | * fails). While holding the mmap_sem in read mode the pmd becomes | ||
456 | * stable and stops changing under us only if it's not null and not a | ||
457 | * transhuge pmd. When those races occurs and this function makes a | ||
458 | * difference vs the standard pmd_none_or_clear_bad, the result is | ||
459 | * undefined so behaving like if the pmd was none is safe (because it | ||
460 | * can return none anyway). The compiler level barrier() is critically | ||
461 | * important to compute the two checks atomically on the same pmdval. | ||
462 | */ | ||
463 | static inline int pmd_none_or_trans_huge_or_clear_bad(pmd_t *pmd) | ||
464 | { | ||
465 | /* depend on compiler for an atomic pmd read */ | ||
466 | pmd_t pmdval = *pmd; | ||
467 | /* | ||
468 | * The barrier will stabilize the pmdval in a register or on | ||
469 | * the stack so that it will stop changing under the code. | ||
470 | */ | ||
471 | #ifdef CONFIG_TRANSPARENT_HUGEPAGE | ||
472 | barrier(); | ||
473 | #endif | ||
474 | if (pmd_none(pmdval)) | ||
475 | return 1; | ||
476 | if (unlikely(pmd_bad(pmdval))) { | ||
477 | if (!pmd_trans_huge(pmdval)) | ||
478 | pmd_clear_bad(pmd); | ||
479 | return 1; | ||
480 | } | ||
481 | return 0; | ||
482 | } | ||
483 | |||
484 | /* | ||
485 | * This is a noop if Transparent Hugepage Support is not built into | ||
486 | * the kernel. Otherwise it is equivalent to | ||
487 | * pmd_none_or_trans_huge_or_clear_bad(), and shall only be called in | ||
488 | * places that already verified the pmd is not none and they want to | ||
489 | * walk ptes while holding the mmap sem in read mode (write mode don't | ||
490 | * need this). If THP is not enabled, the pmd can't go away under the | ||
491 | * code even if MADV_DONTNEED runs, but if THP is enabled we need to | ||
492 | * run a pmd_trans_unstable before walking the ptes after | ||
493 | * split_huge_page_pmd returns (because it may have run when the pmd | ||
494 | * become null, but then a page fault can map in a THP and not a | ||
495 | * regular page). | ||
496 | */ | ||
497 | static inline int pmd_trans_unstable(pmd_t *pmd) | ||
498 | { | ||
499 | #ifdef CONFIG_TRANSPARENT_HUGEPAGE | ||
500 | return pmd_none_or_trans_huge_or_clear_bad(pmd); | ||
501 | #else | ||
502 | return 0; | ||
445 | #endif | 503 | #endif |
504 | } | ||
505 | |||
506 | #endif /* CONFIG_MMU */ | ||
446 | 507 | ||
447 | #endif /* !__ASSEMBLY__ */ | 508 | #endif /* !__ASSEMBLY__ */ |
448 | 509 | ||