aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCatalin Marinas <catalin.marinas@arm.com>2012-07-25 09:32:38 -0400
committerSteve Capper <steve.capper@linaro.org>2013-06-04 11:52:37 -0400
commit1355e2a6eb88f04d76125c057dc5fca64d4b6a9e (patch)
treeaf2c21aba209583f46e2424aec2e5d70b80c7965
parent0b19f93351dd68cb68a1a5b2d74e13d2ddfcfc64 (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/Kconfig4
-rw-r--r--arch/arm/include/asm/hugetlb-3level.h71
-rw-r--r--arch/arm/include/asm/hugetlb.h84
-rw-r--r--arch/arm/include/asm/pgtable-3level-hwdef.h2
-rw-r--r--arch/arm/include/asm/pgtable-3level.h11
-rw-r--r--arch/arm/mm/Makefile1
-rw-r--r--arch/arm/mm/dma-mapping.c2
-rw-r--r--arch/arm/mm/fsr-3level.c2
-rw-r--r--arch/arm/mm/hugetlbpage.c101
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
1710config SYS_SUPPORTS_HUGETLBFS
1711 def_bool y
1712 depends on ARM_LPAE
1713
1710source "mm/Kconfig" 1714source "mm/Kconfig"
1711 1715
1712config FORCE_MAX_ZONEORDER 1716config 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 */
32static 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
40static 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
46static 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
52static 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
58static 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
64static 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
30static 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
39static inline int is_hugepage_only_range(struct mm_struct *mm,
40 unsigned long addr, unsigned long len)
41{
42 return 0;
43}
44
45static 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
56static inline void hugetlb_prefault_arch_hook(struct mm_struct *mm)
57{
58}
59
60static inline int huge_pte_none(pte_t pte)
61{
62 return pte_none(pte);
63}
64
65static inline pte_t huge_pte_wrprotect(pte_t pte)
66{
67 return pte_wrprotect(pte);
68}
69
70static inline int arch_prepare_hugepage(struct page *page)
71{
72 return 0;
73}
74
75static inline void arch_release_hugepage(struct page *page)
76{
77}
78
79static 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
17obj-$(CONFIG_ALIGNMENT_TRAP) += alignment.o 17obj-$(CONFIG_ALIGNMENT_TRAP) += alignment.o
18obj-$(CONFIG_HIGHMEM) += highmem.o 18obj-$(CONFIG_HIGHMEM) += highmem.o
19obj-$(CONFIG_HUGETLB_PAGE) += hugetlbpage.o
19 20
20obj-$(CONFIG_CPU_ABRT_NOMMU) += abort-nommu.o 21obj-$(CONFIG_CPU_ABRT_NOMMU) += abort-nommu.o
21obj-$(CONFIG_CPU_ABRT_EV4) += abort-ev4.o 22obj-$(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
256static void *__alloc_from_contiguous(struct device *dev, size_t size, 256static 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
39pte_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
55struct page *follow_huge_addr(struct mm_struct *mm, unsigned long address,
56 int write)
57{
58 return ERR_PTR(-EINVAL);
59}
60
61int pud_huge(pud_t pud)
62{
63 return 0;
64}
65
66int huge_pmd_unshare(struct mm_struct *mm, unsigned long *addr, pte_t *ptep)
67{
68 return 0;
69}
70
71pte_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
86struct page *
87follow_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
98int pmd_huge(pmd_t pmd)
99{
100 return pmd_val(pmd) && !(pmd_val(pmd) & PMD_TABLE_BIT);
101}