diff options
| author | David Miller <davem@davemloft.net> | 2012-10-08 19:34:29 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-10-09 03:23:06 -0400 |
| commit | 9e695d2ecc8451cc2c1603d60b5c8e7f5581923a (patch) | |
| tree | 77528ae73fe70d1bae3ced18a50e59fd81d2372c | |
| parent | f5c8ad47284ca01dafc37da5a72bb9644174d387 (diff) | |
sparc64: Support transparent huge pages.
This is relatively easy since PMD's now cover exactly 4MB of memory.
Our PMD entries are 32-bits each, so we use a special encoding. The
lowest bit, PMD_ISHUGE, determines the interpretation. This is possible
because sparc64's page tables are purely software entities so we can use
whatever encoding scheme we want. We just have to make the TLB miss
assembler page table walkers aware of the layout.
set_pmd_at() works much like set_pte_at() but it has to operate in two
page from a table of non-huge PTEs, so we have to queue up TLB flushes
based upon what mappings are valid in the PTE table. In the second regime
we are going from huge-page to non-huge-page, and in that case we need
only queue up a single TLB flush to push out the huge page mapping.
We still have 5 bits remaining in the huge PMD encoding so we can very
likely support any new pieces of THP state tracking that might get added
in the future.
With lots of help from Johannes Weiner.
Signed-off-by: David S. Miller <davem@davemloft.net>
Cc: Andrea Arcangeli <aarcange@redhat.com>
Cc: Johannes Weiner <hannes@cmpxchg.org>
Cc: Gerald Schaefer <gerald.schaefer@de.ibm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
| -rw-r--r-- | arch/sparc/include/asm/hugetlb.h | 5 | ||||
| -rw-r--r-- | arch/sparc/include/asm/mmu_64.h | 2 | ||||
| -rw-r--r-- | arch/sparc/include/asm/mmu_context_64.h | 2 | ||||
| -rw-r--r-- | arch/sparc/include/asm/page_64.h | 7 | ||||
| -rw-r--r-- | arch/sparc/include/asm/pgalloc_64.h | 4 | ||||
| -rw-r--r-- | arch/sparc/include/asm/pgtable_64.h | 177 | ||||
| -rw-r--r-- | arch/sparc/include/asm/tsb.h | 94 | ||||
| -rw-r--r-- | arch/sparc/kernel/sun4v_tlb_miss.S | 2 | ||||
| -rw-r--r-- | arch/sparc/kernel/tsb.S | 9 | ||||
| -rw-r--r-- | arch/sparc/mm/fault_64.c | 4 | ||||
| -rw-r--r-- | arch/sparc/mm/hugetlbpage.c | 50 | ||||
| -rw-r--r-- | arch/sparc/mm/init_64.c | 204 | ||||
| -rw-r--r-- | arch/sparc/mm/tlb.c | 118 | ||||
| -rw-r--r-- | arch/sparc/mm/tsb.c | 14 |
14 files changed, 582 insertions, 110 deletions
diff --git a/arch/sparc/include/asm/hugetlb.h b/arch/sparc/include/asm/hugetlb.h index e7927c9758a1..8c5eed6d267f 100644 --- a/arch/sparc/include/asm/hugetlb.h +++ b/arch/sparc/include/asm/hugetlb.h | |||
| @@ -10,7 +10,10 @@ void set_huge_pte_at(struct mm_struct *mm, unsigned long addr, | |||
| 10 | pte_t huge_ptep_get_and_clear(struct mm_struct *mm, unsigned long addr, | 10 | pte_t huge_ptep_get_and_clear(struct mm_struct *mm, unsigned long addr, |
| 11 | pte_t *ptep); | 11 | pte_t *ptep); |
| 12 | 12 | ||
| 13 | void hugetlb_prefault_arch_hook(struct mm_struct *mm); | 13 | static inline void hugetlb_prefault_arch_hook(struct mm_struct *mm) |
| 14 | { | ||
| 15 | hugetlb_setup(mm); | ||
| 16 | } | ||
| 14 | 17 | ||
| 15 | static inline int is_hugepage_only_range(struct mm_struct *mm, | 18 | static inline int is_hugepage_only_range(struct mm_struct *mm, |
| 16 | unsigned long addr, | 19 | unsigned long addr, |
diff --git a/arch/sparc/include/asm/mmu_64.h b/arch/sparc/include/asm/mmu_64.h index 31977c8dd942..76092c4dd277 100644 --- a/arch/sparc/include/asm/mmu_64.h +++ b/arch/sparc/include/asm/mmu_64.h | |||
| @@ -82,7 +82,7 @@ struct tsb_config { | |||
| 82 | 82 | ||
| 83 | #define MM_TSB_BASE 0 | 83 | #define MM_TSB_BASE 0 |
| 84 | 84 | ||
| 85 | #ifdef CONFIG_HUGETLB_PAGE | 85 | #if defined(CONFIG_HUGETLB_PAGE) || defined(CONFIG_TRANSPARENT_HUGEPAGE) |
| 86 | #define MM_TSB_HUGE 1 | 86 | #define MM_TSB_HUGE 1 |
| 87 | #define MM_NUM_TSBS 2 | 87 | #define MM_NUM_TSBS 2 |
| 88 | #else | 88 | #else |
diff --git a/arch/sparc/include/asm/mmu_context_64.h b/arch/sparc/include/asm/mmu_context_64.h index a97fd085cebe..9191ca62ed9c 100644 --- a/arch/sparc/include/asm/mmu_context_64.h +++ b/arch/sparc/include/asm/mmu_context_64.h | |||
| @@ -36,7 +36,7 @@ static inline void tsb_context_switch(struct mm_struct *mm) | |||
| 36 | { | 36 | { |
| 37 | __tsb_context_switch(__pa(mm->pgd), | 37 | __tsb_context_switch(__pa(mm->pgd), |
| 38 | &mm->context.tsb_block[0], | 38 | &mm->context.tsb_block[0], |
| 39 | #ifdef CONFIG_HUGETLB_PAGE | 39 | #if defined(CONFIG_HUGETLB_PAGE) || defined(CONFIG_TRANSPARENT_HUGEPAGE) |
| 40 | (mm->context.tsb_block[1].tsb ? | 40 | (mm->context.tsb_block[1].tsb ? |
| 41 | &mm->context.tsb_block[1] : | 41 | &mm->context.tsb_block[1] : |
| 42 | NULL) | 42 | NULL) |
diff --git a/arch/sparc/include/asm/page_64.h b/arch/sparc/include/asm/page_64.h index 087a5c505c69..4b39f74d6ca0 100644 --- a/arch/sparc/include/asm/page_64.h +++ b/arch/sparc/include/asm/page_64.h | |||
| @@ -17,7 +17,7 @@ | |||
| 17 | 17 | ||
| 18 | #define HPAGE_SHIFT 22 | 18 | #define HPAGE_SHIFT 22 |
| 19 | 19 | ||
| 20 | #ifdef CONFIG_HUGETLB_PAGE | 20 | #if defined(CONFIG_HUGETLB_PAGE) || defined(CONFIG_TRANSPARENT_HUGEPAGE) |
| 21 | #define HPAGE_SIZE (_AC(1,UL) << HPAGE_SHIFT) | 21 | #define HPAGE_SIZE (_AC(1,UL) << HPAGE_SHIFT) |
| 22 | #define HPAGE_MASK (~(HPAGE_SIZE - 1UL)) | 22 | #define HPAGE_MASK (~(HPAGE_SIZE - 1UL)) |
| 23 | #define HUGETLB_PAGE_ORDER (HPAGE_SHIFT - PAGE_SHIFT) | 23 | #define HUGETLB_PAGE_ORDER (HPAGE_SHIFT - PAGE_SHIFT) |
| @@ -26,6 +26,11 @@ | |||
| 26 | 26 | ||
| 27 | #ifndef __ASSEMBLY__ | 27 | #ifndef __ASSEMBLY__ |
| 28 | 28 | ||
| 29 | #if defined(CONFIG_HUGETLB_PAGE) || defined(CONFIG_TRANSPARENT_HUGEPAGE) | ||
| 30 | struct mm_struct; | ||
| 31 | extern void hugetlb_setup(struct mm_struct *mm); | ||
| 32 | #endif | ||
| 33 | |||
| 29 | #define WANT_PAGE_VIRTUAL | 34 | #define WANT_PAGE_VIRTUAL |
| 30 | 35 | ||
| 31 | extern void _clear_page(void *page); | 36 | extern void _clear_page(void *page); |
diff --git a/arch/sparc/include/asm/pgalloc_64.h b/arch/sparc/include/asm/pgalloc_64.h index 0ebca93ef0f5..bcfe063bce23 100644 --- a/arch/sparc/include/asm/pgalloc_64.h +++ b/arch/sparc/include/asm/pgalloc_64.h | |||
| @@ -45,8 +45,8 @@ extern pgtable_t pte_alloc_one(struct mm_struct *mm, | |||
| 45 | extern void pte_free_kernel(struct mm_struct *mm, pte_t *pte); | 45 | extern void pte_free_kernel(struct mm_struct *mm, pte_t *pte); |
| 46 | extern void pte_free(struct mm_struct *mm, pgtable_t ptepage); | 46 | extern void pte_free(struct mm_struct *mm, pgtable_t ptepage); |
| 47 | 47 | ||
| 48 | #define pmd_populate_kernel(MM, PMD, PTE) pmd_set(PMD, PTE) | 48 | #define pmd_populate_kernel(MM, PMD, PTE) pmd_set(MM, PMD, PTE) |
| 49 | #define pmd_populate(MM, PMD, PTE) pmd_set(PMD, PTE) | 49 | #define pmd_populate(MM, PMD, PTE) pmd_set(MM, PMD, PTE) |
| 50 | #define pmd_pgtable(PMD) ((pte_t *)__pmd_page(PMD)) | 50 | #define pmd_pgtable(PMD) ((pte_t *)__pmd_page(PMD)) |
| 51 | 51 | ||
| 52 | #define check_pgt_cache() do { } while (0) | 52 | #define check_pgt_cache() do { } while (0) |
diff --git a/arch/sparc/include/asm/pgtable_64.h b/arch/sparc/include/asm/pgtable_64.h index af3cd7a9e9ac..95515f1e7cef 100644 --- a/arch/sparc/include/asm/pgtable_64.h +++ b/arch/sparc/include/asm/pgtable_64.h | |||
| @@ -63,10 +63,31 @@ | |||
| 63 | #error Page table parameters do not cover virtual address space properly. | 63 | #error Page table parameters do not cover virtual address space properly. |
| 64 | #endif | 64 | #endif |
| 65 | 65 | ||
| 66 | #if (PMD_SHIFT != HPAGE_SHIFT) | ||
| 67 | #error PMD_SHIFT must equal HPAGE_SHIFT for transparent huge pages. | ||
| 68 | #endif | ||
| 69 | |||
| 66 | /* PMDs point to PTE tables which are 4K aligned. */ | 70 | /* PMDs point to PTE tables which are 4K aligned. */ |
| 67 | #define PMD_PADDR _AC(0xfffffffe,UL) | 71 | #define PMD_PADDR _AC(0xfffffffe,UL) |
| 68 | #define PMD_PADDR_SHIFT _AC(11,UL) | 72 | #define PMD_PADDR_SHIFT _AC(11,UL) |
| 69 | 73 | ||
| 74 | #ifdef CONFIG_TRANSPARENT_HUGEPAGE | ||
| 75 | #define PMD_ISHUGE _AC(0x00000001,UL) | ||
| 76 | |||
| 77 | /* This is the PMD layout when PMD_ISHUGE is set. With 4MB huge | ||
| 78 | * pages, this frees up a bunch of bits in the layout that we can | ||
| 79 | * use for the protection settings and software metadata. | ||
| 80 | */ | ||
| 81 | #define PMD_HUGE_PADDR _AC(0xfffff800,UL) | ||
| 82 | #define PMD_HUGE_PROTBITS _AC(0x000007ff,UL) | ||
| 83 | #define PMD_HUGE_PRESENT _AC(0x00000400,UL) | ||
| 84 | #define PMD_HUGE_WRITE _AC(0x00000200,UL) | ||
| 85 | #define PMD_HUGE_DIRTY _AC(0x00000100,UL) | ||
| 86 | #define PMD_HUGE_ACCESSED _AC(0x00000080,UL) | ||
| 87 | #define PMD_HUGE_EXEC _AC(0x00000040,UL) | ||
| 88 | #define PMD_HUGE_SPLITTING _AC(0x00000020,UL) | ||
| 89 | #endif | ||
| 90 | |||
| 70 | /* PGDs point to PMD tables which are 8K aligned. */ | 91 | /* PGDs point to PMD tables which are 8K aligned. */ |
| 71 | #define PGD_PADDR _AC(0xfffffffc,UL) | 92 | #define PGD_PADDR _AC(0xfffffffc,UL) |
| 72 | #define PGD_PADDR_SHIFT _AC(11,UL) | 93 | #define PGD_PADDR_SHIFT _AC(11,UL) |
| @@ -219,6 +240,19 @@ static inline pte_t pfn_pte(unsigned long pfn, pgprot_t prot) | |||
| 219 | } | 240 | } |
| 220 | #define mk_pte(page, pgprot) pfn_pte(page_to_pfn(page), (pgprot)) | 241 | #define mk_pte(page, pgprot) pfn_pte(page_to_pfn(page), (pgprot)) |
| 221 | 242 | ||
| 243 | #ifdef CONFIG_TRANSPARENT_HUGEPAGE | ||
| 244 | extern pmd_t pfn_pmd(unsigned long page_nr, pgprot_t pgprot); | ||
| 245 | #define mk_pmd(page, pgprot) pfn_pmd(page_to_pfn(page), (pgprot)) | ||
| 246 | |||
| 247 | extern pmd_t pmd_modify(pmd_t pmd, pgprot_t newprot); | ||
| 248 | |||
| 249 | static inline pmd_t pmd_mkhuge(pmd_t pmd) | ||
| 250 | { | ||
| 251 | /* Do nothing, mk_pmd() does this part. */ | ||
| 252 | return pmd; | ||
| 253 | } | ||
| 254 | #endif | ||
| 255 | |||
| 222 | /* This one can be done with two shifts. */ | 256 | /* This one can be done with two shifts. */ |
| 223 | static inline unsigned long pte_pfn(pte_t pte) | 257 | static inline unsigned long pte_pfn(pte_t pte) |
| 224 | { | 258 | { |
| @@ -588,19 +622,130 @@ static inline unsigned long pte_special(pte_t pte) | |||
| 588 | return pte_val(pte) & _PAGE_SPECIAL; | 622 | return pte_val(pte) & _PAGE_SPECIAL; |
| 589 | } | 623 | } |
| 590 | 624 | ||
| 591 | #define pmd_set(pmdp, ptep) \ | 625 | #ifdef CONFIG_TRANSPARENT_HUGEPAGE |
| 592 | (pmd_val(*(pmdp)) = (__pa((unsigned long) (ptep)) >> PMD_PADDR_SHIFT)) | 626 | static inline int pmd_young(pmd_t pmd) |
| 627 | { | ||
| 628 | return pmd_val(pmd) & PMD_HUGE_ACCESSED; | ||
| 629 | } | ||
| 630 | |||
| 631 | static inline int pmd_write(pmd_t pmd) | ||
| 632 | { | ||
| 633 | return pmd_val(pmd) & PMD_HUGE_WRITE; | ||
| 634 | } | ||
| 635 | |||
