diff options
author | Steve Capper <steve.capper@linaro.org> | 2013-04-10 08:48:00 -0400 |
---|---|---|
committer | Steve Capper <steve.capper@linaro.org> | 2013-06-14 04:52:40 -0400 |
commit | 084bd29810a5689e423d2f085255a3200a03a06e (patch) | |
tree | 2c26ab58c8b95fb5c6f925044c3873979217da4a /arch | |
parent | 59911ca4325dc7bd95e05c988fef3593b694e62c (diff) |
ARM64: mm: HugeTLB support.
Add huge page support to ARM64, different huge page sizes are
supported depending on the size of normal pages:
PAGE_SIZE is 4KB:
2MB - (pmds) these can be allocated at any time.
1024MB - (puds) usually allocated on bootup with the command line
with something like: hugepagesz=1G hugepages=6
PAGE_SIZE is 64KB:
512MB - (pmds) usually allocated on bootup via command line.
Signed-off-by: Steve Capper <steve.capper@linaro.org>
Acked-by: Catalin Marinas <catalin.marinas@arm.com>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/arm64/Kconfig | 9 | ||||
-rw-r--r-- | arch/arm64/include/asm/hugetlb.h | 117 | ||||
-rw-r--r-- | arch/arm64/include/asm/pgtable-hwdef.h | 8 | ||||
-rw-r--r-- | arch/arm64/include/asm/pgtable.h | 13 | ||||
-rw-r--r-- | arch/arm64/mm/Makefile | 1 | ||||
-rw-r--r-- | arch/arm64/mm/fault.c | 19 | ||||
-rw-r--r-- | arch/arm64/mm/hugetlbpage.c | 70 |
7 files changed, 220 insertions, 17 deletions
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 56b3f6d447ae..cd6eca84a21c 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig | |||
@@ -180,6 +180,15 @@ config HW_PERF_EVENTS | |||
180 | Enable hardware performance counter support for perf events. If | 180 | Enable hardware performance counter support for perf events. If |
181 | disabled, perf events will use software events only. | 181 | disabled, perf events will use software events only. |
182 | 182 | ||
183 | config SYS_SUPPORTS_HUGETLBFS | ||
184 | def_bool y | ||
185 | |||
186 | config ARCH_WANT_GENERAL_HUGETLB | ||
187 | def_bool y | ||
188 | |||
189 | config ARCH_WANT_HUGE_PMD_SHARE | ||
190 | def_bool y if !ARM64_64K_PAGES | ||
191 | |||
183 | source "mm/Kconfig" | 192 | source "mm/Kconfig" |
184 | 193 | ||
185 | endmenu | 194 | endmenu |
diff --git a/arch/arm64/include/asm/hugetlb.h b/arch/arm64/include/asm/hugetlb.h new file mode 100644 index 000000000000..5b7ca8ace95f --- /dev/null +++ b/arch/arm64/include/asm/hugetlb.h | |||
@@ -0,0 +1,117 @@ | |||
1 | /* | ||
2 | * arch/arm64/include/asm/hugetlb.h | ||
3 | * | ||
4 | * Copyright (C) 2013 Linaro Ltd. | ||
5 | * | ||
6 | * Based on arch/x86/include/asm/hugetlb.h | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
20 | */ | ||
21 | |||
22 | #ifndef __ASM_HUGETLB_H | ||
23 | #define __ASM_HUGETLB_H | ||
24 | |||
25 | #include <asm-generic/hugetlb.h> | ||
26 | #include <asm/page.h> | ||
27 | |||
28 | static inline pte_t huge_ptep_get(pte_t *ptep) | ||
29 | { | ||
30 | return *ptep; | ||
31 | } | ||
32 | |||
33 | static inline void set_huge_pte_at(struct mm_struct *mm, unsigned long addr, | ||
34 | pte_t *ptep, pte_t pte) | ||
35 | { | ||
36 | set_pte_at(mm, addr, ptep, pte); | ||
37 | } | ||
38 | |||
39 | static inline void huge_ptep_clear_flush(struct vm_area_struct *vma, | ||
40 | unsigned long addr, pte_t *ptep) | ||
41 | { | ||
42 | ptep_clear_flush(vma, addr, ptep); | ||
43 | } | ||
44 | |||
45 | static inline void huge_ptep_set_wrprotect(struct mm_struct *mm, | ||
46 | unsigned long addr, pte_t *ptep) | ||
47 | { | ||
48 | ptep_set_wrprotect(mm, addr, ptep); | ||
49 | } | ||
50 | |||
51 | static inline pte_t huge_ptep_get_and_clear(struct mm_struct *mm, | ||
52 | unsigned long addr, pte_t *ptep) | ||
53 | { | ||
54 | return ptep_get_and_clear(mm, addr, ptep); | ||
55 | } | ||
56 | |||
57 | static inline int huge_ptep_set_access_flags(struct vm_area_struct *vma, | ||
58 | unsigned long addr, pte_t *ptep, | ||
59 | pte_t pte, int dirty) | ||
60 | { | ||
61 | return ptep_set_access_flags(vma, addr, ptep, pte, dirty); | ||
62 | } | ||
63 | |||
64 | static inline void hugetlb_free_pgd_range(struct mmu_gather *tlb, | ||
65 | unsigned long addr, unsigned long end, | ||
66 | unsigned long floor, | ||
67 | unsigned long ceiling) | ||
68 | { | ||
69 | free_pgd_range(tlb, addr, end, floor, ceiling); | ||
70 | } | ||
71 | |||
72 | static inline int is_hugepage_only_range(struct mm_struct *mm, | ||
73 | unsigned long addr, unsigned long len) | ||
74 | { | ||
75 | return 0; | ||
76 | } | ||
77 | |||
78 | static inline int prepare_hugepage_range(struct file *file, | ||
79 | unsigned long addr, unsigned long len) | ||
80 | { | ||
81 | struct hstate *h = hstate_file(file); | ||
82 | if (len & ~huge_page_mask(h)) | ||
83 | return -EINVAL; | ||
84 | if (addr & ~huge_page_mask(h)) | ||
85 | return -EINVAL; | ||
86 | return 0; | ||
87 | } | ||
88 | |||
89 | static inline void hugetlb_prefault_arch_hook(struct mm_struct *mm) | ||
90 | { | ||
91 | } | ||
92 | |||
93 | static inline int huge_pte_none(pte_t pte) | ||
94 | { | ||
95 | return pte_none(pte); | ||
96 | } | ||
97 | |||
98 | static inline pte_t huge_pte_wrprotect(pte_t pte) | ||
99 | { | ||
100 | return pte_wrprotect(pte); | ||
101 | } | ||
102 | |||
103 | static inline int arch_prepare_hugepage(struct page *page) | ||
104 | { | ||
105 | return 0; | ||
106 | } | ||
107 | |||
108 | static inline void arch_release_hugepage(struct page *page) | ||
109 | { | ||
110 | } | ||
111 | |||
112 | static inline void arch_clear_hugepage_flags(struct page *page) | ||
113 | { | ||
114 | clear_bit(PG_dcache_clean, &page->flags); | ||
115 | } | ||
116 | |||
117 | #endif /* __ASM_HUGETLB_H */ | ||
diff --git a/arch/arm64/include/asm/pgtable-hwdef.h b/arch/arm64/include/asm/pgtable-hwdef.h index 75fd13d289b9..e6e0a0d4cf9a 100644 --- a/arch/arm64/include/asm/pgtable-hwdef.h +++ b/arch/arm64/include/asm/pgtable-hwdef.h | |||
@@ -25,12 +25,19 @@ | |||
25 | /* | 25 | /* |
26 | * Hardware page table definitions. | 26 | * Hardware page table definitions. |
27 | * | 27 | * |
28 | * Level 1 descriptor (PUD). | ||
29 | */ | ||
30 | |||
31 | #define PUD_TABLE_BIT (_AT(pgdval_t, 1) << 1) | ||
32 | |||
33 | /* | ||
28 | * Level 2 descriptor (PMD). | 34 | * Level 2 descriptor (PMD). |
29 | */ | 35 | */ |
30 | #define PMD_TYPE_MASK (_AT(pmdval_t, 3) << 0) | 36 | #define PMD_TYPE_MASK (_AT(pmdval_t, 3) << 0) |
31 | #define PMD_TYPE_FAULT (_AT(pmdval_t, 0) << 0) | 37 | #define PMD_TYPE_FAULT (_AT(pmdval_t, 0) << 0) |
32 | #define PMD_TYPE_TABLE (_AT(pmdval_t, 3) << 0) | 38 | #define PMD_TYPE_TABLE (_AT(pmdval_t, 3) << 0) |
33 | #define PMD_TYPE_SECT (_AT(pmdval_t, 1) << 0) | 39 | #define PMD_TYPE_SECT (_AT(pmdval_t, 1) << 0) |
40 | #define PMD_TABLE_BIT (_AT(pmdval_t, 1) << 1) | ||
34 | 41 | ||
35 | /* | 42 | /* |
36 | * Section | 43 | * Section |
@@ -53,6 +60,7 @@ | |||
53 | #define PTE_TYPE_MASK (_AT(pteval_t, 3) << 0) | 60 | #define PTE_TYPE_MASK (_AT(pteval_t, 3) << 0) |
54 | #define PTE_TYPE_FAULT (_AT(pteval_t, 0) << 0) | 61 | #define PTE_TYPE_FAULT (_AT(pteval_t, 0) << 0) |
55 | #define PTE_TYPE_PAGE (_AT(pteval_t, 3) << 0) | 62 | #define PTE_TYPE_PAGE (_AT(pteval_t, 3) << 0) |
63 | #define PTE_TABLE_BIT (_AT(pteval_t, 1) << 1) | ||
56 | #define PTE_USER (_AT(pteval_t, 1) << 6) /* AP[1] */ | 64 | #define PTE_USER (_AT(pteval_t, 1) << 6) /* AP[1] */ |
57 | #define PTE_RDONLY (_AT(pteval_t, 1) << 7) /* AP[2] */ | 65 | #define PTE_RDONLY (_AT(pteval_t, 1) << 7) /* AP[2] */ |
58 | #define PTE_SHARED (_AT(pteval_t, 3) << 8) /* SH[1:0], inner shareable */ | 66 | #define PTE_SHARED (_AT(pteval_t, 3) << 8) /* SH[1:0], inner shareable */ |
diff --git a/arch/arm64/include/asm/pgtable.h b/arch/arm64/include/asm/pgtable.h index 2291de0258ed..21771330f809 100644 --- a/arch/arm64/include/asm/pgtable.h +++ b/arch/arm64/include/asm/pgtable.h | |||
@@ -173,8 +173,17 @@ static inline void set_pte_at(struct mm_struct *mm, unsigned long addr, | |||
173 | /* | 173 | /* |
174 | * Huge pte definitions. | 174 | * Huge pte definitions. |
175 | */ | 175 | */ |
176 | #define pte_huge(pte) ((pte_val(pte) & PTE_TYPE_MASK) == PTE_TYPE_HUGEPAGE) | 176 | #define pte_huge(pte) (!(pte_val(pte) & PTE_TABLE_BIT)) |
177 | #define pte_mkhuge(pte) (__pte((pte_val(pte) & ~PTE_TYPE_MASK) | PTE_TYPE_HUGEPAGE)) | 177 | #define pte_mkhuge(pte) (__pte(pte_val(pte) & ~PTE_TABLE_BIT)) |
178 | |||
179 | /* | ||
180 | * Hugetlb definitions. | ||
181 | */ | ||
182 | #define HUGE_MAX_HSTATE 2 | ||
183 | #define HPAGE_SHIFT PMD_SHIFT | ||
184 | #define HPAGE_SIZE (_AC(1, UL) << HPAGE_SHIFT) | ||
185 | #define HPAGE_MASK (~(HPAGE_SIZE - 1)) | ||
186 | #define HUGETLB_PAGE_ORDER (HPAGE_SHIFT - PAGE_SHIFT) | ||
178 | 187 | ||
179 | #define __HAVE_ARCH_PTE_SPECIAL | 188 | #define __HAVE_ARCH_PTE_SPECIAL |
180 | 189 | ||
diff --git a/arch/arm64/mm/Makefile b/arch/arm64/mm/Makefile index 3140a2abcdc2..b51d36401d83 100644 --- a/arch/arm64/mm/Makefile +++ b/arch/arm64/mm/Makefile | |||
@@ -2,3 +2,4 @@ obj-y := dma-mapping.o extable.o fault.o init.o \ | |||
2 | cache.o copypage.o flush.o \ | 2 | cache.o copypage.o flush.o \ |
3 | ioremap.o mmap.o pgd.o mmu.o \ | 3 | ioremap.o mmap.o pgd.o mmu.o \ |
4 | context.o tlb.o proc.o | 4 | context.o tlb.o proc.o |
5 | obj-$(CONFIG_HUGETLB_PAGE) += hugetlbpage.o | ||
diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c index 1426468b77f3..0ecac8980aae 100644 --- a/arch/arm64/mm/fault.c +++ b/arch/arm64/mm/fault.c | |||
@@ -365,17 +365,6 @@ static int __kprobes do_translation_fault(unsigned long addr, | |||
365 | } | 365 | } |
366 | 366 | ||
367 | /* | 367 | /* |
368 | * Some section permission faults need to be handled gracefully. They can | ||
369 | * happen due to a __{get,put}_user during an oops. | ||
370 | */ | ||
371 | static int do_sect_fault(unsigned long addr, unsigned int esr, | ||
372 | struct pt_regs *regs) | ||
373 | { | ||
374 | do_bad_area(addr, esr, regs); | ||
375 | return 0; | ||
376 | } | ||
377 | |||
378 | /* | ||
379 | * This abort handler always returns "fault". | 368 | * This abort handler always returns "fault". |
380 | */ | 369 | */ |
381 | static int do_bad(unsigned long addr, unsigned int esr, struct pt_regs *regs) | 370 | static int do_bad(unsigned long addr, unsigned int esr, struct pt_regs *regs) |
@@ -398,12 +387,12 @@ static struct fault_info { | |||
398 | { do_translation_fault, SIGSEGV, SEGV_MAPERR, "level 2 translation fault" }, | 387 | { do_translation_fault, SIGSEGV, SEGV_MAPERR, "level 2 translation fault" }, |
399 | { do_page_fault, SIGSEGV, SEGV_MAPERR, "level 3 translation fault" }, | 388 | { do_page_fault, SIGSEGV, SEGV_MAPERR, "level 3 translation fault" }, |
400 | { do_bad, SIGBUS, 0, "reserved access flag fault" }, | 389 | { do_bad, SIGBUS, 0, "reserved access flag fault" }, |
401 | { do_bad, SIGSEGV, SEGV_ACCERR, "level 1 access flag fault" }, | 390 | { do_page_fault, SIGSEGV, SEGV_ACCERR, "level 1 access flag fault" }, |
402 | { do_bad, SIGSEGV, SEGV_ACCERR, "level 2 access flag fault" }, | 391 | { do_page_fault, SIGSEGV, SEGV_ACCERR, "level 2 access flag fault" }, |
403 | { do_page_fault, SIGSEGV, SEGV_ACCERR, "level 3 access flag fault" }, | 392 | { do_page_fault, SIGSEGV, SEGV_ACCERR, "level 3 access flag fault" }, |
404 | { do_bad, SIGBUS, 0, "reserved permission fault" }, | 393 | { do_bad, SIGBUS, 0, "reserved permission fault" }, |
405 | { do_bad, SIGSEGV, SEGV_ACCERR, "level 1 permission fault" }, | 394 | { do_page_fault, SIGSEGV, SEGV_ACCERR, "level 1 permission fault" }, |
406 | { do_sect_fault, SIGSEGV, SEGV_ACCERR, "level 2 permission fault" }, | 395 | { do_page_fault, SIGSEGV, SEGV_ACCERR, "level 2 permission fault" }, |
407 | { do_page_fault, SIGSEGV, SEGV_ACCERR, "level 3 permission fault" }, | 396 | { do_page_fault, SIGSEGV, SEGV_ACCERR, "level 3 permission fault" }, |
408 | { do_bad, SIGBUS, 0, "synchronous external abort" }, | 397 | { do_bad, SIGBUS, 0, "synchronous external abort" }, |
409 | { do_bad, SIGBUS, 0, "asynchronous external abort" }, | 398 | { do_bad, SIGBUS, 0, "asynchronous external abort" }, |
diff --git a/arch/arm64/mm/hugetlbpage.c b/arch/arm64/mm/hugetlbpage.c new file mode 100644 index 000000000000..2fc8258bab2d --- /dev/null +++ b/arch/arm64/mm/hugetlbpage.c | |||
@@ -0,0 +1,70 @@ | |||
1 | /* | ||
2 | * arch/arm64/mm/hugetlbpage.c | ||
3 | * | ||
4 | * Copyright (C) 2013 Linaro Ltd. | ||
5 | * | ||
6 | * Based on arch/x86/mm/hugetlbpage.c. | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
20 | */ | ||
21 | |||
22 | #include <linux/init.h> | ||
23 | #include <linux/fs.h> | ||
24 | #include <linux/mm.h> | ||
25 | #include <linux/hugetlb.h> | ||
26 | #include <linux/pagemap.h> | ||
27 | #include <linux/err.h> | ||
28 | #include <linux/sysctl.h> | ||
29 | #include <asm/mman.h> | ||
30 | #include <asm/tlb.h> | ||
31 | #include <asm/tlbflush.h> | ||
32 | #include <asm/pgalloc.h> | ||
33 | |||
34 | #ifndef CONFIG_ARCH_WANT_HUGE_PMD_SHARE | ||
35 | int huge_pmd_unshare(struct mm_struct *mm, unsigned long *addr, pte_t *ptep) | ||
36 | { | ||
37 | return 0; | ||
38 | } | ||
39 | #endif | ||
40 | |||
41 | struct page *follow_huge_addr(struct mm_struct *mm, unsigned long address, | ||
42 | int write) | ||
43 | { | ||
44 | return ERR_PTR(-EINVAL); | ||
45 | } | ||
46 | |||
47 | int pmd_huge(pmd_t pmd) | ||
48 | { | ||
49 | return !(pmd_val(pmd) & PMD_TABLE_BIT); | ||
50 | } | ||
51 | |||
52 | int pud_huge(pud_t pud) | ||
53 | { | ||
54 | return !(pud_val(pud) & PUD_TABLE_BIT); | ||
55 | } | ||
56 | |||
57 | static __init int setup_hugepagesz(char *opt) | ||
58 | { | ||
59 | unsigned long ps = memparse(opt, &opt); | ||
60 | if (ps == PMD_SIZE) { | ||
61 | hugetlb_add_hstate(PMD_SHIFT - PAGE_SHIFT); | ||
62 | } else if (ps == PUD_SIZE) { | ||
63 | hugetlb_add_hstate(PUD_SHIFT - PAGE_SHIFT); | ||
64 | } else { | ||
65 | pr_err("hugepagesz: Unsupported page size %lu M\n", ps >> 20); | ||
66 | return 0; | ||
67 | } | ||
68 | return 1; | ||
69 | } | ||
70 | __setup("hugepagesz=", setup_hugepagesz); | ||