xref: /linux/mm/hugetlb_cma.c (revision 06bc7ff0a1e0f2b0102e1314e3527a7ec0997851)
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 
16ae85e561SKefeng Wang static struct cma *hugetlb_cma[MAX_NUMNODES] __ro_after_init;
17474fe91fSFrank van der Linden static unsigned long hugetlb_cma_size_in_node[MAX_NUMNODES] __initdata;
18ae85e561SKefeng Wang static bool hugetlb_cma_only __ro_after_init;
19d9257307SKefeng Wang static unsigned long hugetlb_cma_size __ro_after_init;
20474fe91fSFrank van der Linden 
hugetlb_cma_free_frozen_folio(struct folio * folio)2114f27076SKefeng Wang void hugetlb_cma_free_frozen_folio(struct folio *folio)
22474fe91fSFrank van der Linden {
239bda131cSKefeng Wang 	WARN_ON_ONCE(!cma_release_frozen(hugetlb_cma[folio_nid(folio)],
249bda131cSKefeng Wang 					 &folio->page, folio_nr_pages(folio)));
25474fe91fSFrank van der Linden }
26474fe91fSFrank van der Linden 
hugetlb_cma_alloc_frozen_folio(int order,gfp_t gfp_mask,int nid,nodemask_t * nodemask)2714f27076SKefeng Wang struct folio *hugetlb_cma_alloc_frozen_folio(int order, gfp_t gfp_mask,
28474fe91fSFrank van der Linden 		int nid, nodemask_t *nodemask)
29474fe91fSFrank van der Linden {
30474fe91fSFrank van der Linden 	int node;
319bda131cSKefeng Wang 	struct folio *folio;
329bda131cSKefeng Wang 	struct page *page = NULL;
33474fe91fSFrank van der Linden 
34d9257307SKefeng Wang 	if (!hugetlb_cma_size)
35d9257307SKefeng Wang 		return NULL;
36d9257307SKefeng Wang 
37474fe91fSFrank van der Linden 	if (hugetlb_cma[nid])
389bda131cSKefeng Wang 		page = cma_alloc_frozen_compound(hugetlb_cma[nid], order);
39474fe91fSFrank van der Linden 
409bda131cSKefeng Wang 	if (!page && !(gfp_mask & __GFP_THISNODE)) {
41474fe91fSFrank van der Linden 		for_each_node_mask(node, *nodemask) {
42474fe91fSFrank van der Linden 			if (node == nid || !hugetlb_cma[node])
43474fe91fSFrank van der Linden 				continue;
44474fe91fSFrank van der Linden 
459bda131cSKefeng Wang 			page = cma_alloc_frozen_compound(hugetlb_cma[node], order);
469bda131cSKefeng Wang 			if (page)
47474fe91fSFrank van der Linden 				break;
48474fe91fSFrank van der Linden 		}
49474fe91fSFrank van der Linden 	}
50474fe91fSFrank van der Linden 
519bda131cSKefeng Wang 	if (!page)
529bda131cSKefeng Wang 		return NULL;
53474fe91fSFrank van der Linden 
549bda131cSKefeng Wang 	folio = page_folio(page);
559bda131cSKefeng Wang 	folio_set_hugetlb_cma(folio);
56474fe91fSFrank van der Linden 	return folio;
57474fe91fSFrank van der Linden }
58474fe91fSFrank van der Linden 
59474fe91fSFrank van der Linden struct huge_bootmem_page * __init
hugetlb_cma_alloc_bootmem(struct hstate * h,int * nid,bool node_exact)60474fe91fSFrank van der Linden hugetlb_cma_alloc_bootmem(struct hstate *h, int *nid, bool node_exact)
61474fe91fSFrank van der Linden {
62474fe91fSFrank van der Linden 	struct cma *cma;
63474fe91fSFrank van der Linden 	struct huge_bootmem_page *m;
64474fe91fSFrank van der Linden 	int node = *nid;
65474fe91fSFrank van der Linden 
66474fe91fSFrank van der Linden 	cma = hugetlb_cma[*nid];
67474fe91fSFrank van der Linden 	m = cma_reserve_early(cma, huge_page_size(h));
68474fe91fSFrank van der Linden 	if (!m) {
69474fe91fSFrank van der Linden 		if (node_exact)
70474fe91fSFrank van der Linden 			return NULL;
71474fe91fSFrank van der Linden 
728d88b076SFrank van der Linden 		for_each_node_mask(node, hugetlb_bootmem_nodes) {
73474fe91fSFrank van der Linden 			cma = hugetlb_cma[node];
74474fe91fSFrank van der Linden 			if (!cma || node == *nid)
75474fe91fSFrank van der Linden 				continue;
76474fe91fSFrank van der Linden 			m = cma_reserve_early(cma, huge_page_size(h));
77474fe91fSFrank van der Linden 			if (m) {
78474fe91fSFrank van der Linden 				*nid = node;
79474fe91fSFrank van der Linden 				break;
80474fe91fSFrank van der Linden 			}
81474fe91fSFrank van der Linden 		}
82474fe91fSFrank van der Linden 	}
83474fe91fSFrank van der Linden 
84474fe91fSFrank van der Linden 	if (m) {
85474fe91fSFrank van der Linden 		m->flags = HUGE_BOOTMEM_CMA;
86474fe91fSFrank van der Linden 		m->cma = cma;
87474fe91fSFrank van der Linden 	}
88474fe91fSFrank van der Linden 
89474fe91fSFrank van der Linden 	return m;
90474fe91fSFrank van der Linden }
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 
arch_hugetlb_cma_order(void)1389fac145bSMike Rapoport (Microsoft) unsigned int __weak arch_hugetlb_cma_order(void)
139474fe91fSFrank van der Linden {
1409fac145bSMike Rapoport (Microsoft) 	return 0;
1419fac145bSMike Rapoport (Microsoft) }
1429fac145bSMike Rapoport (Microsoft) 
hugetlb_cma_reserve(void)1439fac145bSMike Rapoport (Microsoft) void __init hugetlb_cma_reserve(void)
1449fac145bSMike Rapoport (Microsoft) {
1459fac145bSMike Rapoport (Microsoft) 	unsigned long size, reserved, per_node, order;
146474fe91fSFrank van der Linden 	bool node_specific_cma_alloc = false;
147474fe91fSFrank van der Linden 	int nid;
148474fe91fSFrank van der Linden 
1499fac145bSMike Rapoport (Microsoft) 	if (!hugetlb_cma_size)
1509fac145bSMike Rapoport (Microsoft) 		return;
1519fac145bSMike Rapoport (Microsoft) 
1529fac145bSMike Rapoport (Microsoft) 	order = arch_hugetlb_cma_order();
1537a9c0bf0SMike Rapoport (Microsoft) 	if (!order) {
1547a9c0bf0SMike Rapoport (Microsoft) 		pr_warn("hugetlb_cma: the option isn't supported by current arch\n");
1559fac145bSMike Rapoport (Microsoft) 		return;
1567a9c0bf0SMike Rapoport (Microsoft) 	}
1579fac145bSMike Rapoport (Microsoft) 
158474fe91fSFrank van der Linden 	/*
159474fe91fSFrank van der Linden 	 * HugeTLB CMA reservation is required for gigantic
160474fe91fSFrank van der Linden 	 * huge pages which could not be allocated via the
161474fe91fSFrank van der Linden 	 * page allocator. Just warn if there is any change
162474fe91fSFrank van der Linden 	 * breaking this assumption.
163474fe91fSFrank van der Linden 	 */
164474fe91fSFrank van der Linden 	VM_WARN_ON(order <= MAX_PAGE_ORDER);
165474fe91fSFrank van der Linden 
1668d88b076SFrank van der Linden 	hugetlb_bootmem_set_nodes();
1678d88b076SFrank van der Linden 
168474fe91fSFrank van der Linden 	for (nid = 0; nid < MAX_NUMNODES; nid++) {
169474fe91fSFrank van der Linden 		if (hugetlb_cma_size_in_node[nid] == 0)
170474fe91fSFrank van der Linden 			continue;
171474fe91fSFrank van der Linden 
1728d88b076SFrank van der Linden 		if (!node_isset(nid, hugetlb_bootmem_nodes)) {
173474fe91fSFrank van der Linden 			pr_warn("hugetlb_cma: invalid node %d specified\n", nid);
174474fe91fSFrank van der Linden 			hugetlb_cma_size -= hugetlb_cma_size_in_node[nid];
175474fe91fSFrank van der Linden 			hugetlb_cma_size_in_node[nid] = 0;
176474fe91fSFrank van der Linden 			continue;
177474fe91fSFrank van der Linden 		}
178474fe91fSFrank van der Linden 
179474fe91fSFrank van der Linden 		if (hugetlb_cma_size_in_node[nid] < (PAGE_SIZE << order)) {
180474fe91fSFrank van der Linden 			pr_warn("hugetlb_cma: cma area of node %d should be at least %lu MiB\n",
181474fe91fSFrank van der Linden 				nid, (PAGE_SIZE << order) / SZ_1M);
182474fe91fSFrank van der Linden 			hugetlb_cma_size -= hugetlb_cma_size_in_node[nid];
183474fe91fSFrank van der Linden 			hugetlb_cma_size_in_node[nid] = 0;
184474fe91fSFrank van der Linden 		} else {
185474fe91fSFrank van der Linden 			node_specific_cma_alloc = true;
186474fe91fSFrank van der Linden 		}
187474fe91fSFrank van der Linden 	}
188474fe91fSFrank van der Linden 
189474fe91fSFrank van der Linden 	/* Validate the CMA size again in case some invalid nodes specified. */
190474fe91fSFrank van der Linden 	if (!hugetlb_cma_size)
191474fe91fSFrank van der Linden 		return;
192474fe91fSFrank van der Linden 
193474fe91fSFrank van der Linden 	if (hugetlb_cma_size < (PAGE_SIZE << order)) {
194474fe91fSFrank van der Linden 		pr_warn("hugetlb_cma: cma area should be at least %lu MiB\n",
195474fe91fSFrank van der Linden 			(PAGE_SIZE << order) / SZ_1M);
196474fe91fSFrank van der Linden 		hugetlb_cma_size = 0;
197474fe91fSFrank van der Linden 		return;
198474fe91fSFrank van der Linden 	}
199474fe91fSFrank van der Linden 
200474fe91fSFrank van der Linden 	if (!node_specific_cma_alloc) {
201474fe91fSFrank van der Linden 		/*
202474fe91fSFrank van der Linden 		 * If 3 GB area is requested on a machine with 4 numa nodes,
203474fe91fSFrank van der Linden 		 * let's allocate 1 GB on first three nodes and ignore the last one.
204474fe91fSFrank van der Linden 		 */
2058d88b076SFrank van der Linden 		per_node = DIV_ROUND_UP(hugetlb_cma_size,
2068d88b076SFrank van der Linden 					nodes_weight(hugetlb_bootmem_nodes));
207*8f5ce56bSSang-Heon Jeon 		per_node = round_up(per_node, PAGE_SIZE << order);
208474fe91fSFrank van der Linden 		pr_info("hugetlb_cma: reserve %lu MiB, up to %lu MiB per node\n",
209474fe91fSFrank van der Linden 			hugetlb_cma_size / SZ_1M, per_node / SZ_1M);
210474fe91fSFrank van der Linden 	}
211474fe91fSFrank van der Linden 
212474fe91fSFrank van der Linden 	reserved = 0;
2138d88b076SFrank van der Linden 	for_each_node_mask(nid, hugetlb_bootmem_nodes) {
214474fe91fSFrank van der Linden 		int res;
215474fe91fSFrank van der Linden 		char name[CMA_MAX_NAME];
216474fe91fSFrank van der Linden 
217474fe91fSFrank van der Linden 		if (node_specific_cma_alloc) {
218474fe91fSFrank van der Linden 			if (hugetlb_cma_size_in_node[nid] == 0)
219474fe91fSFrank van der Linden 				continue;
220474fe91fSFrank van der Linden 
221474fe91fSFrank van der Linden 			size = hugetlb_cma_size_in_node[nid];
222474fe91fSFrank van der Linden 		} else {
223474fe91fSFrank van der Linden 			size = min(per_node, hugetlb_cma_size - reserved);
224474fe91fSFrank van der Linden 		}
225474fe91fSFrank van der Linden 
226474fe91fSFrank van der Linden 		size = round_up(size, PAGE_SIZE << order);
227474fe91fSFrank van der Linden 
228474fe91fSFrank van der Linden 		snprintf(name, sizeof(name), "hugetlb%d", nid);
229474fe91fSFrank van der Linden 		/*
230474fe91fSFrank van der Linden 		 * Note that 'order per bit' is based on smallest size that
231474fe91fSFrank van der Linden 		 * may be returned to CMA allocator in the case of
232474fe91fSFrank van der Linden 		 * huge page demotion.
233474fe91fSFrank van der Linden 		 */
234474fe91fSFrank van der Linden 		res = cma_declare_contiguous_multi(size, PAGE_SIZE << order,
235474fe91fSFrank van der Linden 					HUGETLB_PAGE_ORDER, name,
236474fe91fSFrank van der Linden 					&hugetlb_cma[nid], nid);
237474fe91fSFrank van der Linden 		if (res) {
238474fe91fSFrank van der Linden 			pr_warn("hugetlb_cma: reservation failed: err %d, node %d",
239474fe91fSFrank van der Linden 				res, nid);
240474fe91fSFrank van der Linden 			continue;
241474fe91fSFrank van der Linden 		}
242474fe91fSFrank van der Linden 
243474fe91fSFrank van der Linden 		reserved += size;
244474fe91fSFrank van der Linden 		pr_info("hugetlb_cma: reserved %lu MiB on node %d\n",
245474fe91fSFrank van der Linden 			size / SZ_1M, nid);
246474fe91fSFrank van der Linden 
247474fe91fSFrank van der Linden 		if (reserved >= hugetlb_cma_size)
248474fe91fSFrank van der Linden 			break;
249474fe91fSFrank van der Linden 	}
250474fe91fSFrank van der Linden 
251474fe91fSFrank van der Linden 	if (!reserved)
252474fe91fSFrank van der Linden 		/*
253474fe91fSFrank van der Linden 		 * hugetlb_cma_size is used to determine if allocations from
254474fe91fSFrank van der Linden 		 * cma are possible.  Set to zero if no cma regions are set up.
255474fe91fSFrank van der Linden 		 */
256474fe91fSFrank van der Linden 		hugetlb_cma_size = 0;
257474fe91fSFrank van der Linden }
258474fe91fSFrank van der Linden 
hugetlb_cma_exclusive_alloc(void)259474fe91fSFrank van der Linden bool hugetlb_cma_exclusive_alloc(void)
260474fe91fSFrank van der Linden {
261474fe91fSFrank van der Linden 	return hugetlb_cma_only;
262474fe91fSFrank van der Linden }
263474fe91fSFrank van der Linden 
hugetlb_cma_total_size(void)264474fe91fSFrank van der Linden unsigned long __init hugetlb_cma_total_size(void)
265474fe91fSFrank van der Linden {
266474fe91fSFrank van der Linden 	return hugetlb_cma_size;
267474fe91fSFrank van der Linden }
268474fe91fSFrank van der Linden 
hugetlb_cma_validate_params(void)269474fe91fSFrank van der Linden void __init hugetlb_cma_validate_params(void)
270474fe91fSFrank van der Linden {
271474fe91fSFrank van der Linden 	if (!hugetlb_cma_size)
272474fe91fSFrank van der Linden 		hugetlb_cma_only = false;
273474fe91fSFrank van der Linden }
274474fe91fSFrank van der Linden 
hugetlb_early_cma(struct hstate * h)275474fe91fSFrank van der Linden bool __init hugetlb_early_cma(struct hstate *h)
276474fe91fSFrank van der Linden {
277474fe91fSFrank van der Linden 	if (arch_has_huge_bootmem_alloc())
278474fe91fSFrank van der Linden 		return false;
279474fe91fSFrank van der Linden 
280474fe91fSFrank van der Linden 	return hstate_is_gigantic(h) && hugetlb_cma_only;
281474fe91fSFrank van der Linden }
282