Merge tag 'pci-v6.16-fixes-3' of git://git.kernel.org/pub/scm/linux/kernel/git/pci/pci
[linux-2.6-block.git] / arch / powerpc / mm / hugetlbpage.c
CommitLineData
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
29bool 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 34pte_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
43pte_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 95void __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 107static 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
120bool __init hugetlb_node_alloc_supported(void)
121{
122 return false;
123}
41151e77 124#endif
ec4b2c0c 125
79cc38de 126
b5389086 127int __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 137bool __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
156static 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
167static 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 201arch_initcall(hugetlbpage_init);
0895ecda 202
ef26b76d
AK
203void __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}