Commit | Line | Data |
---|---|---|
1da177e4 | 1 | /* |
41151e77 | 2 | * PPC Huge TLB Page Support for Kernel. |
1da177e4 LT |
3 | * |
4 | * Copyright (C) 2003 David Gibson, IBM Corporation. | |
41151e77 | 5 | * Copyright (C) 2011 Becky Bruce, Freescale Semiconductor |
1da177e4 LT |
6 | * |
7 | * Based on the IA-32 version: | |
8 | * Copyright (C) 2002, Rohit Seth <rohit.seth@intel.com> | |
9 | */ | |
10 | ||
1da177e4 | 11 | #include <linux/mm.h> |
883a3e52 | 12 | #include <linux/io.h> |
5a0e3ad6 | 13 | #include <linux/slab.h> |
1da177e4 | 14 | #include <linux/hugetlb.h> |
342d3db7 | 15 | #include <linux/export.h> |
41151e77 BB |
16 | #include <linux/of_fdt.h> |
17 | #include <linux/memblock.h> | |
13020be8 | 18 | #include <linux/moduleparam.h> |
50791e6d AK |
19 | #include <linux/swap.h> |
20 | #include <linux/swapops.h> | |
803d690e | 21 | #include <linux/kmemleak.h> |
1da177e4 LT |
22 | #include <asm/pgalloc.h> |
23 | #include <asm/tlb.h> | |
41151e77 | 24 | #include <asm/setup.h> |
29409997 | 25 | #include <asm/hugetlb.h> |
94171b19 | 26 | #include <asm/pte-walk.h> |
46d60bdb | 27 | #include <asm/firmware.h> |
94171b19 | 28 | |
85975387 HB |
29 | bool hugetlb_disabled = false; |
30 | ||
b12c07a4 CL |
31 | #define PTE_T_ORDER (__builtin_ffs(sizeof(pte_basic_t)) - \ |
32 | __builtin_ffs(sizeof(void *))) | |
03566562 | 33 | |
7868a208 | 34 | pte_t *huge_pte_offset(struct mm_struct *mm, unsigned long addr, unsigned long sz) |
a4fe3ce7 | 35 | { |
94171b19 AK |
36 | /* |
37 | * Only called for hugetlbfs pages, hence can ignore THP and the | |
38 | * irq disabled walk. | |
39 | */ | |
40 | return __find_linux_pte(mm->pgd, addr, NULL, NULL); | |
a4fe3ce7 DG |
41 | } |
42 | ||
d6a1a9a3 CL |
43 | pte_t *huge_pte_alloc(struct mm_struct *mm, struct vm_area_struct *vma, |
44 | unsigned long addr, unsigned long sz) | |
45 | { | |
46 | p4d_t *p4d; | |
47 | pud_t *pud; | |
48 | pmd_t *pmd; | |
49 | ||
50 | addr &= ~(sz - 1); | |
51 | ||
52 | p4d = p4d_offset(pgd_offset(mm, addr), addr); | |
53 | if (!mm_pud_folded(mm) && sz >= P4D_SIZE) | |
54 | return (pte_t *)p4d; | |
55 | ||
56 | pud = pud_alloc(mm, p4d, addr); | |
57 | if (!pud) | |
58 | return NULL; | |
59 | if (!mm_pmd_folded(mm) && sz >= PUD_SIZE) | |
60 | return (pte_t *)pud; | |
61 | ||
62 | pmd = pmd_alloc(mm, pud, addr); | |
63 | if (!pmd) | |
64 | return NULL; | |
65 | ||
0549e766 CL |
66 | if (sz >= PMD_SIZE) { |
67 | /* On 8xx, all hugepages are handled as contiguous PTEs */ | |
68 | if (IS_ENABLED(CONFIG_PPC_8xx)) { | |
69 | int i; | |
70 | ||
71 | for (i = 0; i < sz / PMD_SIZE; i++) { | |
72 | if (!pte_alloc_huge(mm, pmd + i, addr)) | |
73 | return NULL; | |
74 | } | |
75 | } | |
d6a1a9a3 | 76 | return (pte_t *)pmd; |
0549e766 | 77 | } |
d6a1a9a3 CL |
78 | |
79 | return pte_alloc_huge(mm, pmd, addr); | |
80 | } | |
4ec161cf | 81 | |
79cc38de | 82 | #ifdef CONFIG_PPC_BOOK3S_64 |
41151e77 | 83 | /* |
79cc38de AK |
84 | * Tracks gpages after the device tree is scanned and before the |
85 | * huge_boot_pages list is ready on pseries. | |
41151e77 | 86 | */ |
79cc38de AK |
87 | #define MAX_NUMBER_GPAGES 1024 |
88 | __initdata static u64 gpage_freearray[MAX_NUMBER_GPAGES]; | |
89 | __initdata static unsigned nr_gpages; | |
41151e77 BB |
90 | |
91 | /* | |
79cc38de | 92 | * Build list of addresses of gigantic pages. This function is used in early |
14ed7409 | 93 | * boot before the buddy allocator is setup. |
41151e77 | 94 | */ |
79cc38de | 95 | void __init pseries_add_gpage(u64 addr, u64 page_size, unsigned long number_of_pages) |
658013e9 JT |
96 | { |
97 | if (!addr) | |
98 | return; | |
99 | while (number_of_pages > 0) { | |
100 | gpage_freearray[nr_gpages] = addr; | |
101 | nr_gpages++; | |
102 | number_of_pages--; | |
103 | addr += page_size; | |
104 | } | |
105 | } | |
106 | ||
94b87d72 | 107 | static int __init pseries_alloc_bootmem_huge_page(struct hstate *hstate) |
ec4b2c0c JT |
108 | { |
109 | struct huge_bootmem_page *m; | |
110 | if (nr_gpages == 0) | |
111 | return 0; | |
112 | m = phys_to_virt(gpage_freearray[--nr_gpages]); | |
113 | gpage_freearray[nr_gpages] = 0; | |
b78b27d0 | 114 | list_add(&m->list, &huge_boot_pages[0]); |
0d9ea754 | 115 | m->hstate = hstate; |
752fe17a | 116 | m->flags = 0; |
ec4b2c0c JT |
117 | return 1; |
118 | } | |
b5389086 ZY |
119 | |
120 | bool __init hugetlb_node_alloc_supported(void) | |
121 | { | |
122 | return false; | |
123 | } | |
41151e77 | 124 | #endif |
ec4b2c0c | 125 | |
79cc38de | 126 | |
b5389086 | 127 | int __init alloc_bootmem_huge_page(struct hstate *h, int nid) |
79cc38de AK |
128 | { |
129 | ||
130 | #ifdef CONFIG_PPC_BOOK3S_64 | |
131 | if (firmware_has_feature(FW_FEATURE_LPAR) && !radix_enabled()) | |
132 | return pseries_alloc_bootmem_huge_page(h); | |
133 | #endif | |
b5389086 | 134 | return __alloc_bootmem_huge_page(h, nid); |
79cc38de AK |
135 | } |
136 | ||
ae94da89 | 137 | bool __init arch_hugetlb_valid_size(unsigned long size) |
4ec161cf | 138 | { |
d1837cba DG |
139 | int shift = __ffs(size); |
140 | int mmu_psize; | |
a4fe3ce7 | 141 | |
4ec161cf | 142 | /* Check that it is a page size supported by the hardware and |
d1837cba | 143 | * that it fits within pagetable and slice limits. */ |
723f268f | 144 | if (size <= PAGE_SIZE || !is_power_of_2(size)) |
ae94da89 | 145 | return false; |
91224346 | 146 | |
7338874c | 147 | mmu_psize = check_and_get_huge_psize(shift); |
723f268f | 148 | if (mmu_psize < 0) |
ae94da89 | 149 | return false; |
d1837cba | 150 | |
d1837cba DG |
151 | BUG_ON(mmu_psize_defs[mmu_psize].shift != shift); |
152 | ||
ae94da89 MK |
153 | return true; |
154 | } | |
d1837cba | 155 | |
ae94da89 MK |
156 | static int __init add_huge_page_size(unsigned long long size) |
157 | { | |
158 | int shift = __ffs(size); | |
159 | ||
160 | if (!arch_hugetlb_valid_size((unsigned long)size)) | |
161 | return -EINVAL; | |
d1837cba | 162 | |
38237830 | 163 | hugetlb_add_hstate(shift - PAGE_SHIFT); |
d1837cba | 164 | return 0; |
4ec161cf JT |
165 | } |
166 | ||
41151e77 BB |
167 | static int __init hugetlbpage_init(void) |
168 | { | |
ac25ba68 | 169 | bool configured = false; |
41151e77 BB |
170 | int psize; |
171 | ||
85975387 HB |
172 | if (hugetlb_disabled) { |
173 | pr_info("HugeTLB support is disabled!\n"); | |
174 | return 0; | |
175 | } | |
176 | ||
4df4b275 CL |
177 | if (IS_ENABLED(CONFIG_PPC_BOOK3S_64) && !radix_enabled() && |
178 | !mmu_has_feature(MMU_FTR_16M_PAGE)) | |
f10a04c0 | 179 | return -ENODEV; |
4df4b275 | 180 | |
d1837cba DG |
181 | for (psize = 0; psize < MMU_PAGE_COUNT; ++psize) { |
182 | unsigned shift; | |
0d9ea754 | 183 | |
d1837cba DG |
184 | if (!mmu_psize_defs[psize].shift) |
185 | continue; | |
00df438e | 186 | |
d1837cba DG |
187 | shift = mmu_psize_to_shift(psize); |
188 | ||
6fa50483 AK |
189 | if (add_huge_page_size(1ULL << shift) < 0) |
190 | continue; | |
ac25ba68 AK |
191 | |
192 | configured = true; | |
0d9ea754 | 193 | } |
f10a04c0 | 194 | |
2354ad25 | 195 | if (!configured) |
ac25ba68 | 196 | pr_info("Failed to initialize. Disabling HugeTLB"); |
c5710cd2 | 197 | |
f10a04c0 DG |
198 | return 0; |
199 | } | |
03bb2d65 | 200 | |
6f114281 | 201 | arch_initcall(hugetlbpage_init); |
0895ecda | 202 | |
ef26b76d AK |
203 | void __init gigantic_hugetlb_cma_reserve(void) |
204 | { | |
205 | unsigned long order = 0; | |
206 | ||
207 | if (radix_enabled()) | |
208 | order = PUD_SHIFT - PAGE_SHIFT; | |
209 | else if (!firmware_has_feature(FW_FEATURE_LPAR) && mmu_psize_defs[MMU_PAGE_16G].shift) | |
210 | /* | |
211 | * For pseries we do use ibm,expected#pages for reserving 16G pages. | |
212 | */ | |
213 | order = mmu_psize_to_shift(MMU_PAGE_16G) - PAGE_SHIFT; | |
214 | ||
ce70cfb1 | 215 | if (order) |
ef26b76d | 216 | hugetlb_cma_reserve(order); |
ef26b76d | 217 | } |