diff options
author | Catalin Marinas <catalin.marinas@arm.com> | 2012-07-25 09:32:38 -0400 |
---|---|---|
committer | Steve Capper <steve.capper@linaro.org> | 2013-06-04 11:52:37 -0400 |
commit | 1355e2a6eb88f04d76125c057dc5fca64d4b6a9e (patch) | |
tree | af2c21aba209583f46e2424aec2e5d70b80c7965 | |
parent | 0b19f93351dd68cb68a1a5b2d74e13d2ddfcfc64 (diff) |
ARM: mm: HugeTLB support for LPAE systems.
This patch adds support for hugetlbfs based on the x86 implementation.
It allows mapping of 2MB sections (see Documentation/vm/hugetlbpage.txt
for usage). The 64K pages configuration is not supported (section size
is 512MB in this case).
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
[steve.capper@linaro.org: symbolic constants replace numbers in places.
Split up into multiple files, to simplify future non-LPAE support,
removed huge_pmd_share code, as this is very rarely executed,
Added PROT_NONE support].
Signed-off-by: Steve Capper <steve.capper@linaro.org>
Reviewed-by: Will Deacon <will.deacon@arm.com>
-rw-r--r-- | arch/arm/Kconfig | 4 | ||||
-rw-r--r-- | arch/arm/include/asm/hugetlb-3level.h | 71 | ||||
-rw-r--r-- | arch/arm/include/asm/hugetlb.h | 84 | ||||
-rw-r--r-- | arch/arm/include/asm/pgtable-3level-hwdef.h | 2 | ||||
-rw-r--r-- | arch/arm/include/asm/pgtable-3level.h | 11 | ||||
-rw-r--r-- | arch/arm/mm/Makefile | 1 | ||||
-rw-r--r-- | arch/arm/mm/dma-mapping.c | 2 | ||||
-rw-r--r-- | arch/arm/mm/fsr-3level.c | 2 | ||||
-rw-r--r-- | arch/arm/mm/hugetlbpage.c | 101 |
9 files changed, 276 insertions, 2 deletions
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 49d993cee512..860f034653a8 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig | |||
@@ -1707,6 +1707,10 @@ config HW_PERF_EVENTS | |||
1707 | Enable hardware performance counter support for perf events. If | 1707 | Enable hardware performance counter support for perf events. If |
1708 | disabled, perf events will use software events only. | 1708 | disabled, perf events will use software events only. |
1709 | 1709 | ||
1710 | config SYS_SUPPORTS_HUGETLBFS | ||
1711 | def_bool y | ||
1712 | depends on ARM_LPAE | ||
1713 | |||
1710 | source "mm/Kconfig" | 1714 | source "mm/Kconfig" |
1711 | 1715 | ||
1712 | config FORCE_MAX_ZONEORDER | 1716 | config FORCE_MAX_ZONEORDER |
diff --git a/arch/arm/include/asm/hugetlb-3level.h b/arch/arm/include/asm/hugetlb-3level.h new file mode 100644 index 000000000000..d4014fbe5ea3 --- /dev/null +++ b/arch/arm/include/asm/hugetlb-3level.h | |||
@@ -0,0 +1,71 @@ | |||
1 | /* | ||
2 | * arch/arm/include/asm/hugetlb-3level.h | ||
3 | * | ||
4 | * Copyright (C) 2012 ARM 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_ARM_HUGETLB_3LEVEL_H | ||
23 | #define _ASM_ARM_HUGETLB_3LEVEL_H | ||
24 | |||
25 | |||
26 | /* | ||
27 | * If our huge pte is non-zero then mark the valid bit. | ||
28 | * This allows pte_present(huge_ptep_get(ptep)) to return true for non-zero | ||
29 | * ptes. | ||
30 | * (The valid bit is automatically cleared by set_pte_at for PROT_NONE ptes). | ||
31 | */ | ||
32 | static inline pte_t huge_ptep_get(pte_t *ptep) | ||
33 | { | ||
34 | pte_t retval = *ptep; | ||
35 | if (pte_val(retval)) | ||
36 | pte_val(retval) |= L_PTE_VALID; | ||
37 | return retval; | ||
38 | } | ||
39 | |||
40 | static inline void set_huge_pte_at(struct mm_struct *mm, unsigned long addr, | ||
41 | pte_t *ptep, pte_t pte) | ||
42 | { | ||
43 | set_pte_at(mm, addr, ptep, pte); | ||
44 | } | ||
45 | |||
46 | static inline void huge_ptep_clear_flush(struct vm_area_struct *vma, | ||
47 | unsigned long addr, pte_t *ptep) | ||
48 | { | ||
49 | ptep_clear_flush(vma, addr, ptep); | ||
50 | } | ||
51 | |||
52 | static inline void huge_ptep_set_wrprotect(struct mm_struct *mm, | ||
53 | unsigned long addr, pte_t *ptep) | ||
54 | { | ||
55 | ptep_set_wrprotect(mm, addr, ptep); | ||
56 | } | ||
57 | |||
58 | static inline pte_t huge_ptep_get_and_clear(struct mm_struct *mm, | ||
59 | unsigned long addr, pte_t *ptep) | ||
60 | { | ||
61 | return ptep_get_and_clear(mm, addr, ptep); | ||
62 | } | ||
63 | |||
64 | static inline int huge_ptep_set_access_flags(struct vm_area_struct *vma, | ||
65 | unsigned long addr, pte_t *ptep, | ||
66 | pte_t pte, int dirty) | ||
67 | { | ||
68 | return ptep_set_access_flags(vma, addr, ptep, pte, dirty); | ||
69 | } | ||
70 | |||
71 | #endif /* _ASM_ARM_HUGETLB_3LEVEL_H */ | ||
diff --git a/arch/arm/include/asm/hugetlb.h b/arch/arm/include/asm/hugetlb.h new file mode 100644 index 000000000000..1f1b1cd112f3 --- /dev/null +++ b/arch/arm/include/asm/hugetlb.h | |||
@@ -0,0 +1,84 @@ | |||
1 | /* | ||
2 | * arch/arm/include/asm/hugetlb.h | ||
3 | * | ||
4 | * Copyright (C) 2012 ARM 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_ARM_HUGETLB_H | ||
23 | #define _ASM_ARM_HUGETLB_H | ||
24 | |||
25 | #include <asm/page.h> | ||
26 | #include <asm-generic/hugetlb.h> | ||
27 | |||
28 | #include <asm/hugetlb-3level.h> | ||
29 | |||
30 | static inline void hugetlb_free_pgd_range(struct mmu_gather *tlb, | ||
31 | unsigned long addr, unsigned long end, | ||
32 | unsigned long floor, | ||
33 | unsigned long ceiling) | ||
34 | { | ||
35 | free_pgd_range(tlb, addr, end, floor, ceiling); | ||
36 | } | ||
37 | |||
38 | |||
39 | static inline int is_hugepage_only_range(struct mm_struct *mm, | ||
40 | unsigned long addr, unsigned long len) | ||
41 | { | ||
42 | return 0; | ||
43 | } | ||
44 | |||
45 | static inline int prepare_hugepage_range(struct file *file, | ||
46 | unsigned long addr, unsigned long len) | ||
47 | { | ||
48 | struct hstate *h = hstate_file(file); | ||
49 | if (len & ~huge_page_mask(h)) | ||
50 | return -EINVAL; | ||
51 | if (addr & ~huge_page_mask(h)) | ||
52 | return -EINVAL; | ||
53 | return 0; | ||
54 | } | ||
55 | |||
56 | static inline void hugetlb_prefault_arch_hook(struct mm_struct *mm) | ||
57 | { | ||
58 | } | ||
59 | |||
60 | static inline int huge_pte_none(pte_t pte) | ||
61 | { | ||
62 | return pte_none(pte); | ||
63 | } | ||
64 | |||
65 | static inline pte_t huge_pte_wrprotect(pte_t pte) | ||
66 | { | ||
67 | return pte_wrprotect(pte); | ||
68 | } | ||
69 | |||
70 | static inline int arch_prepare_hugepage(struct page *page) | ||
71 | { | ||
72 | return 0; | ||
73 | } | ||
74 | |||
75 | static inline void arch_release_hugepage(struct page *page) | ||
76 | { | ||
77 | } | ||
78 | |||
79 | static inline void arch_clear_hugepage_flags(struct page *page) | ||
80 | { | ||
81 | clear_bit(PG_dcache_clean, &page->flags); | ||
82 | } | ||
83 | |||
84 | #endif /* _ASM_ARM_HUGETLB_H */ | ||
diff --git a/arch/arm/include/asm/pgtable-3level-hwdef.h b/arch/arm/include/asm/pgtable-3level-hwdef.h index 18f5cef82ad5..42df407ee3e3 100644 --- a/arch/arm/include/asm/pgtable-3level-hwdef.h +++ b/arch/arm/include/asm/pgtable-3level-hwdef.h | |||
@@ -30,6 +30,7 @@ | |||
30 | #define PMD_TYPE_FAULT (_AT(pmdval_t, 0) << 0) | 30 | #define PMD_TYPE_FAULT (_AT(pmdval_t, 0) << 0) |
31 | #define PMD_TYPE_TABLE (_AT(pmdval_t, 3) << 0) | 31 | #define PMD_TYPE_TABLE (_AT(pmdval_t, 3) << 0) |
32 | #define PMD_TYPE_SECT (_AT(pmdval_t, 1) << 0) | 32 | #define PMD_TYPE_SECT (_AT(pmdval_t, 1) << 0) |
33 | #define PMD_TABLE_BIT (_AT(pmdval_t, 1) << 1) | ||
33 | #define PMD_BIT4 (_AT(pmdval_t, 0)) | 34 | #define PMD_BIT4 (_AT(pmdval_t, 0)) |
34 | #define PMD_DOMAIN(x) (_AT(pmdval_t, 0)) | 35 | #define PMD_DOMAIN(x) (_AT(pmdval_t, 0)) |
35 | #define PMD_APTABLE_SHIFT (61) | 36 | #define PMD_APTABLE_SHIFT (61) |
@@ -66,6 +67,7 @@ | |||
66 | #define PTE_TYPE_MASK (_AT(pteval_t, 3) << 0) | 67 | #define PTE_TYPE_MASK (_AT(pteval_t, 3) << 0) |
67 | #define PTE_TYPE_FAULT (_AT(pteval_t, 0) << 0) | 68 | #define PTE_TYPE_FAULT (_AT(pteval_t, 0) << 0) |
68 | #define PTE_TYPE_PAGE (_AT(pteval_t, 3) << 0) | 69 | #define PTE_TYPE_PAGE (_AT(pteval_t, 3) << 0) |
70 | #define PTE_TABLE_BIT (_AT(pteval_t, 1) << 1) | ||
69 | #define PTE_BUFFERABLE (_AT(pteval_t, 1) << 2) /* AttrIndx[0] */ | 71 | #define PTE_BUFFERABLE (_AT(pteval_t, 1) << 2) /* AttrIndx[0] */ |
70 | #define PTE_CACHEABLE (_AT(pteval_t, 1) << 3) /* AttrIndx[1] */ | 72 | #define PTE_CACHEABLE (_AT(pteval_t, 1) << 3) /* AttrIndx[1] */ |
71 | #define PTE_EXT_SHARED (_AT(pteval_t, 3) << 8) /* SH[1:0], inner shareable */ | 73 | #define PTE_EXT_SHARED (_AT(pteval_t, 3) << 8) /* SH[1:0], inner shareable */ |
diff --git a/arch/arm/include/asm/pgtable-3level.h b/arch/arm/include/asm/pgtable-3level.h index 70f041cb50d1..d1bcd8226cb1 100644 --- a/arch/arm/include/asm/pgtable-3level.h +++ b/arch/arm/include/asm/pgtable-3level.h | |||
@@ -62,6 +62,14 @@ | |||
62 | #define USER_PTRS_PER_PGD (PAGE_OFFSET / PGDIR_SIZE) | 62 | #define USER_PTRS_PER_PGD (PAGE_OFFSET / PGDIR_SIZE) |
63 | 63 | ||
64 | /* | 64 | /* |
65 | * Hugetlb definitions. | ||
66 | */ | ||
67 | #define HPAGE_SHIFT PMD_SHIFT | ||
68 | #define HPAGE_SIZE (_AC(1, UL) << HPAGE_SHIFT) | ||
69 | #define HPAGE_MASK (~(HPAGE_SIZE - 1)) | ||
70 | #define HUGETLB_PAGE_ORDER (HPAGE_SHIFT - PAGE_SHIFT) | ||
71 | |||
72 | /* | ||
65 | * "Linux" PTE definitions for LPAE. | 73 | * "Linux" PTE definitions for LPAE. |
66 | * | 74 | * |
67 | * These bits overlap with the hardware bits but the naming is preserved for | 75 | * These bits overlap with the hardware bits but the naming is preserved for |
@@ -185,6 +193,9 @@ static inline pmd_t *pmd_offset(pud_t *pud, unsigned long addr) | |||
185 | 193 | ||
186 | #define set_pte_ext(ptep,pte,ext) cpu_set_pte_ext(ptep,__pte(pte_val(pte)|(ext))) | 194 | #define set_pte_ext(ptep,pte,ext) cpu_set_pte_ext(ptep,__pte(pte_val(pte)|(ext))) |
187 | 195 | ||
196 | #define pte_huge(pte) (pte_val(pte) && !(pte_val(pte) & PTE_TABLE_BIT)) | ||
197 | #define pte_mkhuge(pte) (__pte(pte_val(pte) & ~PTE_TABLE_BIT)) | ||
198 | |||
188 | #endif /* __ASSEMBLY__ */ | 199 | #endif /* __ASSEMBLY__ */ |
189 | 200 | ||
190 | #endif /* _ASM_PGTABLE_3LEVEL_H */ | 201 | #endif /* _ASM_PGTABLE_3LEVEL_H */ |
diff --git a/arch/arm/mm/Makefile b/arch/arm/mm/Makefile index 9e51be96f635..224a9cc09877 100644 --- a/arch/arm/mm/Makefile +++ b/arch/arm/mm/Makefile | |||
@@ -16,6 +16,7 @@ obj-$(CONFIG_MODULES) += proc-syms.o | |||
16 | 16 | ||
17 | obj-$(CONFIG_ALIGNMENT_TRAP) += alignment.o | 17 | obj-$(CONFIG_ALIGNMENT_TRAP) += alignment.o |
18 | obj-$(CONFIG_HIGHMEM) += highmem.o | 18 | obj-$(CONFIG_HIGHMEM) += highmem.o |
19 | obj-$(CONFIG_HUGETLB_PAGE) += hugetlbpage.o | ||
19 | 20 | ||
20 | obj-$(CONFIG_CPU_ABRT_NOMMU) += abort-nommu.o | 21 | obj-$(CONFIG_CPU_ABRT_NOMMU) += abort-nommu.o |
21 | obj-$(CONFIG_CPU_ABRT_EV4) += abort-ev4.o | 22 | obj-$(CONFIG_CPU_ABRT_EV4) += abort-ev4.o |
diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c index ef3e0f3aac96..9674476a75dc 100644 --- a/arch/arm/mm/dma-mapping.c +++ b/arch/arm/mm/dma-mapping.c | |||
@@ -250,7 +250,7 @@ static void __dma_free_buffer(struct page *page, size_t size) | |||
250 | 250 | ||
251 | #ifdef CONFIG_MMU | 251 | #ifdef CONFIG_MMU |
252 | #ifdef CONFIG_HUGETLB_PAGE | 252 | #ifdef CONFIG_HUGETLB_PAGE |
253 | #error ARM Coherent DMA allocator does not (yet) support huge TLB | 253 | #warning ARM Coherent DMA allocator does not (yet) support huge TLB |
254 | #endif | 254 | #endif |
255 | 255 | ||
256 | static void *__alloc_from_contiguous(struct device *dev, size_t size, | 256 | static void *__alloc_from_contiguous(struct device *dev, size_t size, |
diff --git a/arch/arm/mm/fsr-3level.c b/arch/arm/mm/fsr-3level.c index 05a4e9431836..e115fc7a69bd 100644 --- a/arch/arm/mm/fsr-3level.c +++ b/arch/arm/mm/fsr-3level.c | |||
@@ -13,7 +13,7 @@ static struct fsr_info fsr_info[] = { | |||
13 | { do_page_fault, SIGSEGV, SEGV_ACCERR, "level 3 access flag fault" }, | 13 | { do_page_fault, SIGSEGV, SEGV_ACCERR, "level 3 access flag fault" }, |
14 | { do_bad, SIGBUS, 0, "reserved permission fault" }, | 14 | { do_bad, SIGBUS, 0, "reserved permission fault" }, |
15 | { do_bad, SIGSEGV, SEGV_ACCERR, "level 1 permission fault" }, | 15 | { do_bad, SIGSEGV, SEGV_ACCERR, "level 1 permission fault" }, |
16 | { do_sect_fault, SIGSEGV, SEGV_ACCERR, "level 2 permission fault" }, | 16 | { do_page_fault, SIGSEGV, SEGV_ACCERR, "level 2 permission fault" }, |
17 | { do_page_fault, SIGSEGV, SEGV_ACCERR, "level 3 permission fault" }, | 17 | { do_page_fault, SIGSEGV, SEGV_ACCERR, "level 3 permission fault" }, |
18 | { do_bad, SIGBUS, 0, "synchronous external abort" }, | 18 | { do_bad, SIGBUS, 0, "synchronous external abort" }, |
19 | { do_bad, SIGBUS, 0, "asynchronous external abort" }, | 19 | { do_bad, SIGBUS, 0, "asynchronous external abort" }, |
diff --git a/arch/arm/mm/hugetlbpage.c b/arch/arm/mm/hugetlbpage.c new file mode 100644 index 000000000000..3d1e4a205b0b --- /dev/null +++ b/arch/arm/mm/hugetlbpage.c | |||
@@ -0,0 +1,101 @@ | |||
1 | /* | ||
2 | * arch/arm/mm/hugetlbpage.c | ||
3 | * | ||
4 | * Copyright (C) 2012 ARM Ltd. | ||
5 | * | ||
6 | * Based on arch/x86/include/asm/hugetlb.h and Bill Carson's patches | ||
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 | /* | ||
35 | * On ARM, huge pages are backed by pmd's rather than pte's, so we do a lot | ||
36 | * of type casting from pmd_t * to pte_t *. | ||
37 | */ | ||
38 | |||
39 | pte_t *huge_pte_offset(struct mm_struct *mm, unsigned long addr) | ||
40 | { | ||
41 | pgd_t *pgd; | ||
42 | pud_t *pud; | ||
43 | pmd_t *pmd = NULL; | ||
44 | |||
45 | pgd = pgd_offset(mm, addr); | ||
46 | if (pgd_present(*pgd)) { | ||
47 | pud = pud_offset(pgd, addr); | ||
48 | if (pud_present(*pud)) | ||
49 | pmd = pmd_offset(pud, addr); | ||
50 | } | ||
51 | |||
52 | return (pte_t *)pmd; | ||
53 | } | ||
54 | |||
55 | struct page *follow_huge_addr(struct mm_struct *mm, unsigned long address, | ||
56 | int write) | ||
57 | { | ||
58 | return ERR_PTR(-EINVAL); | ||
59 | } | ||
60 | |||
61 | int pud_huge(pud_t pud) | ||
62 | { | ||
63 | return 0; | ||
64 | } | ||
65 | |||
66 | int huge_pmd_unshare(struct mm_struct *mm, unsigned long *addr, pte_t *ptep) | ||
67 | { | ||
68 | return 0; | ||
69 | } | ||
70 | |||
71 | pte_t *huge_pte_alloc(struct mm_struct *mm, | ||
72 | unsigned long addr, unsigned long sz) | ||
73 | { | ||
74 | pgd_t *pgd; | ||
75 | pud_t *pud; | ||
76 | pte_t *pte = NULL; | ||
77 | |||
78 | pgd = pgd_offset(mm, addr); | ||
79 | pud = pud_alloc(mm, pgd, addr); | ||
80 | if (pud) | ||
81 | pte = (pte_t *)pmd_alloc(mm, pud, addr); | ||
82 | |||
83 | return pte; | ||
84 | } | ||
85 | |||
86 | struct page * | ||
87 | follow_huge_pmd(struct mm_struct *mm, unsigned long address, | ||
88 | pmd_t *pmd, int write) | ||
89 | { | ||
90 | struct page *page; | ||
91 | |||
92 | page = pte_page(*(pte_t *)pmd); | ||
93 | if (page) | ||
94 | page += ((address & ~PMD_MASK) >> PAGE_SHIFT); | ||
95 | return page; | ||
96 | } | ||
97 | |||
98 | int pmd_huge(pmd_t pmd) | ||
99 | { | ||
100 | return pmd_val(pmd) && !(pmd_val(pmd) & PMD_TABLE_BIT); | ||
101 | } | ||