1474fe91fSFrank van der Linden // SPDX-License-Identifier: GPL-2.0-only
2474fe91fSFrank van der Linden
3474fe91fSFrank van der Linden #include <linux/mm.h>
4474fe91fSFrank van der Linden #include <linux/cma.h>
5474fe91fSFrank van der Linden #include <linux/compiler.h>
6474fe91fSFrank van der Linden #include <linux/mm_inline.h>
7474fe91fSFrank van der Linden
8474fe91fSFrank van der Linden #include <asm/page.h>
9474fe91fSFrank van der Linden #include <asm/setup.h>
10474fe91fSFrank van der Linden
11474fe91fSFrank van der Linden #include <linux/hugetlb.h>
12474fe91fSFrank van der Linden #include "internal.h"
13474fe91fSFrank van der Linden #include "hugetlb_cma.h"
14474fe91fSFrank van der Linden
15474fe91fSFrank van der Linden
16474fe91fSFrank van der Linden static struct cma *hugetlb_cma[MAX_NUMNODES];
17474fe91fSFrank van der Linden static unsigned long hugetlb_cma_size_in_node[MAX_NUMNODES] __initdata;
18474fe91fSFrank van der Linden static bool hugetlb_cma_only;
19474fe91fSFrank van der Linden static unsigned long hugetlb_cma_size __initdata;
20474fe91fSFrank van der Linden
hugetlb_cma_free_folio(struct folio * folio)21474fe91fSFrank van der Linden void hugetlb_cma_free_folio(struct folio *folio)
22474fe91fSFrank van der Linden {
23474fe91fSFrank van der Linden int nid = folio_nid(folio);
24474fe91fSFrank van der Linden
25474fe91fSFrank van der Linden WARN_ON_ONCE(!cma_free_folio(hugetlb_cma[nid], folio));
26474fe91fSFrank van der Linden }
27474fe91fSFrank van der Linden
28474fe91fSFrank van der Linden
hugetlb_cma_alloc_folio(struct hstate * h,gfp_t gfp_mask,int nid,nodemask_t * nodemask)29474fe91fSFrank van der Linden struct folio *hugetlb_cma_alloc_folio(struct hstate *h, gfp_t gfp_mask,
30474fe91fSFrank van der Linden int nid, nodemask_t *nodemask)
31474fe91fSFrank van der Linden {
32474fe91fSFrank van der Linden int node;
33474fe91fSFrank van der Linden int order = huge_page_order(h);
34474fe91fSFrank van der Linden struct folio *folio = NULL;
35474fe91fSFrank van der Linden
36474fe91fSFrank van der Linden if (hugetlb_cma[nid])
37474fe91fSFrank van der Linden folio = cma_alloc_folio(hugetlb_cma[nid], order, gfp_mask);
38474fe91fSFrank van der Linden
39474fe91fSFrank van der Linden if (!folio && !(gfp_mask & __GFP_THISNODE)) {
40474fe91fSFrank van der Linden for_each_node_mask(node, *nodemask) {
41474fe91fSFrank van der Linden if (node == nid || !hugetlb_cma[node])
42474fe91fSFrank van der Linden continue;
43474fe91fSFrank van der Linden
44474fe91fSFrank van der Linden folio = cma_alloc_folio(hugetlb_cma[node], order, gfp_mask);
45474fe91fSFrank van der Linden if (folio)
46474fe91fSFrank van der Linden break;
47474fe91fSFrank van der Linden }
48474fe91fSFrank van der Linden }
49474fe91fSFrank van der Linden
50474fe91fSFrank van der Linden if (folio)
51474fe91fSFrank van der Linden folio_set_hugetlb_cma(folio);
52474fe91fSFrank van der Linden
53474fe91fSFrank van der Linden return folio;
54474fe91fSFrank van der Linden }
55474fe91fSFrank van der Linden
56474fe91fSFrank van der Linden struct huge_bootmem_page * __init
hugetlb_cma_alloc_bootmem(struct hstate * h,int * nid,bool node_exact)57474fe91fSFrank van der Linden hugetlb_cma_alloc_bootmem(struct hstate *h, int *nid, bool node_exact)
58474fe91fSFrank van der Linden {
59474fe91fSFrank van der Linden struct cma *cma;
60474fe91fSFrank van der Linden struct huge_bootmem_page *m;
61474fe91fSFrank van der Linden int node = *nid;
62474fe91fSFrank van der Linden
63474fe91fSFrank van der Linden cma = hugetlb_cma[*nid];
64474fe91fSFrank van der Linden m = cma_reserve_early(cma, huge_page_size(h));
65474fe91fSFrank van der Linden if (!m) {
66474fe91fSFrank van der Linden if (node_exact)
67474fe91fSFrank van der Linden return NULL;
68474fe91fSFrank van der Linden
69*8d88b076SFrank van der Linden for_each_node_mask(node, hugetlb_bootmem_nodes) {
70474fe91fSFrank van der Linden cma = hugetlb_cma[node];
71474fe91fSFrank van der Linden if (!cma || node == *nid)
72474fe91fSFrank van der Linden continue;
73474fe91fSFrank van der Linden m = cma_reserve_early(cma, huge_page_size(h));
74474fe91fSFrank van der Linden if (m) {
75474fe91fSFrank van der Linden *nid = node;
76474fe91fSFrank van der Linden break;
77474fe91fSFrank van der Linden }
78474fe91fSFrank van der Linden }
79474fe91fSFrank van der Linden }
80474fe91fSFrank van der Linden
81474fe91fSFrank van der Linden if (m) {
82474fe91fSFrank van der Linden m->flags = HUGE_BOOTMEM_CMA;
83474fe91fSFrank van der Linden m->cma = cma;
84474fe91fSFrank van der Linden }
85474fe91fSFrank van der Linden
86474fe91fSFrank van der Linden return m;
87474fe91fSFrank van der Linden }
88474fe91fSFrank van der Linden
89474fe91fSFrank van der Linden
90474fe91fSFrank van der Linden static bool cma_reserve_called __initdata;
91474fe91fSFrank van der Linden
cmdline_parse_hugetlb_cma(char * p)92474fe91fSFrank van der Linden static int __init cmdline_parse_hugetlb_cma(char *p)
93474fe91fSFrank van der Linden {
94474fe91fSFrank van der Linden int nid, count = 0;
95474fe91fSFrank van der Linden unsigned long tmp;
96474fe91fSFrank van der Linden char *s = p;
97474fe91fSFrank van der Linden
98474fe91fSFrank van der Linden while (*s) {
99474fe91fSFrank van der Linden if (sscanf(s, "%lu%n", &tmp, &count) != 1)
100474fe91fSFrank van der Linden break;
101474fe91fSFrank van der Linden
102474fe91fSFrank van der Linden if (s[count] == ':') {
103474fe91fSFrank van der Linden if (tmp >= MAX_NUMNODES)
104474fe91fSFrank van der Linden break;
105474fe91fSFrank van der Linden nid = array_index_nospec(tmp, MAX_NUMNODES);
106474fe91fSFrank van der Linden
107474fe91fSFrank van der Linden s += count + 1;
108474fe91fSFrank van der Linden tmp = memparse(s, &s);
109474fe91fSFrank van der Linden hugetlb_cma_size_in_node[nid] = tmp;
110474fe91fSFrank van der Linden hugetlb_cma_size += tmp;
111474fe91fSFrank van der Linden
112474fe91fSFrank van der Linden /*
113474fe91fSFrank van der Linden * Skip the separator if have one, otherwise
114474fe91fSFrank van der Linden * break the parsing.
115474fe91fSFrank van der Linden */
116474fe91fSFrank van der Linden if (*s == ',')
117474fe91fSFrank van der Linden s++;
118474fe91fSFrank van der Linden else
119474fe91fSFrank van der Linden break;
120474fe91fSFrank van der Linden } else {
121474fe91fSFrank van der Linden hugetlb_cma_size = memparse(p, &p);
122474fe91fSFrank van der Linden break;
123474fe91fSFrank van der Linden }
124474fe91fSFrank van der Linden }
125474fe91fSFrank van der Linden
126474fe91fSFrank van der Linden return 0;
127474fe91fSFrank van der Linden }
128474fe91fSFrank van der Linden
129474fe91fSFrank van der Linden early_param("hugetlb_cma", cmdline_parse_hugetlb_cma);
130474fe91fSFrank van der Linden
cmdline_parse_hugetlb_cma_only(char * p)131474fe91fSFrank van der Linden static int __init cmdline_parse_hugetlb_cma_only(char *p)
132474fe91fSFrank van der Linden {
133474fe91fSFrank van der Linden return kstrtobool(p, &hugetlb_cma_only);
134474fe91fSFrank van der Linden }
135474fe91fSFrank van der Linden
136474fe91fSFrank van der Linden early_param("hugetlb_cma_only", cmdline_parse_hugetlb_cma_only);
137474fe91fSFrank van der Linden
hugetlb_cma_reserve(int order)138474fe91fSFrank van der Linden void __init hugetlb_cma_reserve(int order)
139474fe91fSFrank van der Linden {
140474fe91fSFrank van der Linden unsigned long size, reserved, per_node;
141474fe91fSFrank van der Linden bool node_specific_cma_alloc = false;
142474fe91fSFrank van der Linden int nid;
143474fe91fSFrank van der Linden
144474fe91fSFrank van der Linden /*
145474fe91fSFrank van der Linden * HugeTLB CMA reservation is required for gigantic
146474fe91fSFrank van der Linden * huge pages which could not be allocated via the
147474fe91fSFrank van der Linden * page allocator. Just warn if there is any change
148474fe91fSFrank van der Linden * breaking this assumption.
149474fe91fSFrank van der Linden */
150474fe91fSFrank van der Linden VM_WARN_ON(order <= MAX_PAGE_ORDER);
151474fe91fSFrank van der Linden cma_reserve_called = true;
152474fe91fSFrank van der Linden
153474fe91fSFrank van der Linden if (!hugetlb_cma_size)
154474fe91fSFrank van der Linden return;
155474fe91fSFrank van der Linden
156*8d88b076SFrank van der Linden hugetlb_bootmem_set_nodes();
157*8d88b076SFrank van der Linden
158474fe91fSFrank van der Linden for (nid = 0; nid < MAX_NUMNODES; nid++) {
159474fe91fSFrank van der Linden if (hugetlb_cma_size_in_node[nid] == 0)
160474fe91fSFrank van der Linden continue;
161474fe91fSFrank van der Linden
162*8d88b076SFrank van der Linden if (!node_isset(nid, hugetlb_bootmem_nodes)) {
163474fe91fSFrank van der Linden pr_warn("hugetlb_cma: invalid node %d specified\n", nid);
164474fe91fSFrank van der Linden hugetlb_cma_size -= hugetlb_cma_size_in_node[nid];
165474fe91fSFrank van der Linden hugetlb_cma_size_in_node[nid] = 0;
166474fe91fSFrank van der Linden continue;
167474fe91fSFrank van der Linden }
168474fe91fSFrank van der Linden
169474fe91fSFrank van der Linden if (hugetlb_cma_size_in_node[nid] < (PAGE_SIZE << order)) {
170474fe91fSFrank van der Linden pr_warn("hugetlb_cma: cma area of node %d should be at least %lu MiB\n",
171474fe91fSFrank van der Linden nid, (PAGE_SIZE << order) / SZ_1M);
172474fe91fSFrank van der Linden hugetlb_cma_size -= hugetlb_cma_size_in_node[nid];
173474fe91fSFrank van der Linden hugetlb_cma_size_in_node[nid] = 0;
174474fe91fSFrank van der Linden } else {
175474fe91fSFrank van der Linden node_specific_cma_alloc = true;
176474fe91fSFrank van der Linden }
177474fe91fSFrank van der Linden }
178474fe91fSFrank van der Linden
179474fe91fSFrank van der Linden /* Validate the CMA size again in case some invalid nodes specified. */
180474fe91fSFrank van der Linden if (!hugetlb_cma_size)
181474fe91fSFrank van der Linden return;
182474fe91fSFrank van der Linden
183474fe91fSFrank van der Linden if (hugetlb_cma_size < (PAGE_SIZE << order)) {
184474fe91fSFrank van der Linden pr_warn("hugetlb_cma: cma area should be at least %lu MiB\n",
185474fe91fSFrank van der Linden (PAGE_SIZE << order) / SZ_1M);
186474fe91fSFrank van der Linden hugetlb_cma_size = 0;
187474fe91fSFrank van der Linden return;
188474fe91fSFrank van der Linden }
189474fe91fSFrank van der Linden
190474fe91fSFrank van der Linden if (!node_specific_cma_alloc) {
191474fe91fSFrank van der Linden /*
192474fe91fSFrank van der Linden * If 3 GB area is requested on a machine with 4 numa nodes,
193474fe91fSFrank van der Linden * let's allocate 1 GB on first three nodes and ignore the last one.
194474fe91fSFrank van der Linden */
195*8d88b076SFrank van der Linden per_node = DIV_ROUND_UP(hugetlb_cma_size,
196*8d88b076SFrank van der Linden nodes_weight(hugetlb_bootmem_nodes));
197474fe91fSFrank van der Linden pr_info("hugetlb_cma: reserve %lu MiB, up to %lu MiB per node\n",
198474fe91fSFrank van der Linden hugetlb_cma_size / SZ_1M, per_node / SZ_1M);
199474fe91fSFrank van der Linden }
200474fe91fSFrank van der Linden
201474fe91fSFrank van der Linden reserved = 0;
202*8d88b076SFrank van der Linden for_each_node_mask(nid, hugetlb_bootmem_nodes) {
203474fe91fSFrank van der Linden int res;
204474fe91fSFrank van der Linden char name[CMA_MAX_NAME];
205474fe91fSFrank van der Linden
206474fe91fSFrank van der Linden if (node_specific_cma_alloc) {
207474fe91fSFrank van der Linden if (hugetlb_cma_size_in_node[nid] == 0)
208474fe91fSFrank van der Linden continue;
209474fe91fSFrank van der Linden
210474fe91fSFrank van der Linden size = hugetlb_cma_size_in_node[nid];
211474fe91fSFrank van der Linden } else {
212474fe91fSFrank van der Linden size = min(per_node, hugetlb_cma_size - reserved);
213474fe91fSFrank van der Linden }
214474fe91fSFrank van der Linden
215474fe91fSFrank van der Linden size = round_up(size, PAGE_SIZE << order);
216474fe91fSFrank van der Linden
217474fe91fSFrank van der Linden snprintf(name, sizeof(name), "hugetlb%d", nid);
218474fe91fSFrank van der Linden /*
219474fe91fSFrank van der Linden * Note that 'order per bit' is based on smallest size that
220474fe91fSFrank van der Linden * may be returned to CMA allocator in the case of
221474fe91fSFrank van der Linden * huge page demotion.
222474fe91fSFrank van der Linden */
223474fe91fSFrank van der Linden res = cma_declare_contiguous_multi(size, PAGE_SIZE << order,
224474fe91fSFrank van der Linden HUGETLB_PAGE_ORDER, name,
225474fe91fSFrank van der Linden &hugetlb_cma[nid], nid);
226474fe91fSFrank van der Linden if (res) {
227474fe91fSFrank van der Linden pr_warn("hugetlb_cma: reservation failed: err %d, node %d",
228474fe91fSFrank van der Linden res, nid);
229474fe91fSFrank van der Linden continue;
230474fe91fSFrank van der Linden }
231474fe91fSFrank van der Linden
232474fe91fSFrank van der Linden reserved += size;
233474fe91fSFrank van der Linden pr_info("hugetlb_cma: reserved %lu MiB on node %d\n",
234474fe91fSFrank van der Linden size / SZ_1M, nid);
235474fe91fSFrank van der Linden
236474fe91fSFrank van der Linden if (reserved >= hugetlb_cma_size)
237474fe91fSFrank van der Linden break;
238474fe91fSFrank van der Linden }
239474fe91fSFrank van der Linden
240474fe91fSFrank van der Linden if (!reserved)
241474fe91fSFrank van der Linden /*
242474fe91fSFrank van der Linden * hugetlb_cma_size is used to determine if allocations from
243474fe91fSFrank van der Linden * cma are possible. Set to zero if no cma regions are set up.
244474fe91fSFrank van der Linden */
245474fe91fSFrank van der Linden hugetlb_cma_size = 0;
246474fe91fSFrank van der Linden }
247474fe91fSFrank van der Linden
hugetlb_cma_check(void)248474fe91fSFrank van der Linden void __init hugetlb_cma_check(void)
249474fe91fSFrank van der Linden {
250474fe91fSFrank van der Linden if (!hugetlb_cma_size || cma_reserve_called)
251474fe91fSFrank van der Linden return;
252474fe91fSFrank van der Linden
253474fe91fSFrank van der Linden pr_warn("hugetlb_cma: the option isn't supported by current arch\n");
254474fe91fSFrank van der Linden }
255474fe91fSFrank van der Linden
hugetlb_cma_exclusive_alloc(void)256474fe91fSFrank van der Linden bool hugetlb_cma_exclusive_alloc(void)
257474fe91fSFrank van der Linden {
258474fe91fSFrank van der Linden return hugetlb_cma_only;
259474fe91fSFrank van der Linden }
260474fe91fSFrank van der Linden
hugetlb_cma_total_size(void)261474fe91fSFrank van der Linden unsigned long __init hugetlb_cma_total_size(void)
262474fe91fSFrank van der Linden {
263474fe91fSFrank van der Linden return hugetlb_cma_size;
264474fe91fSFrank van der Linden }
265474fe91fSFrank van der Linden
hugetlb_cma_validate_params(void)266474fe91fSFrank van der Linden void __init hugetlb_cma_validate_params(void)
267474fe91fSFrank van der Linden {
268474fe91fSFrank van der Linden if (!hugetlb_cma_size)
269474fe91fSFrank van der Linden hugetlb_cma_only = false;
270474fe91fSFrank van der Linden }
271474fe91fSFrank van der Linden
hugetlb_early_cma(struct hstate * h)272474fe91fSFrank van der Linden bool __init hugetlb_early_cma(struct hstate *h)
273474fe91fSFrank van der Linden {
274474fe91fSFrank van der Linden if (arch_has_huge_bootmem_alloc())
275474fe91fSFrank van der Linden return false;
276474fe91fSFrank van der Linden
277474fe91fSFrank van der Linden return hstate_is_gigantic(h) && hugetlb_cma_only;
278474fe91fSFrank van der Linden }
279