mirror of
https://github.com/torvalds/linux.git
synced 2025-12-01 07:26:02 +07:00
mm/hugetlb: move hugetlb CMA code in to its own file
hugetlb.c contained a number of CONFIG_CMA ifdefs, and the code inside them was large enough to merit being in its own file, so move it, cleaning up things a bit. Hide some direct variable access behind functions to accommodate the move. No functional change intended. Link: https://lkml.kernel.org/r/20250228182928.2645936-28-fvdl@google.com Signed-off-by: Frank van der Linden <fvdl@google.com> Cc: Alexander Gordeev <agordeev@linux.ibm.com> Cc: Andy Lutomirski <luto@kernel.org> Cc: Arnd Bergmann <arnd@arndb.de> Cc: Dan Carpenter <dan.carpenter@linaro.org> Cc: Dave Hansen <dave.hansen@linux.intel.com> Cc: David Hildenbrand <david@redhat.com> Cc: Heiko Carstens <hca@linux.ibm.com> Cc: Joao Martins <joao.m.martins@oracle.com> Cc: Johannes Weiner <hannes@cmpxchg.org> Cc: Madhavan Srinivasan <maddy@linux.ibm.com> Cc: Michael Ellerman <mpe@ellerman.id.au> Cc: Muchun Song <muchun.song@linux.dev> Cc: Oscar Salvador <osalvador@suse.de> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Roman Gushchin (Cruise) <roman.gushchin@linux.dev> Cc: Usama Arif <usamaarif642@gmail.com> Cc: Vasily Gorbik <gor@linux.ibm.com> Cc: Yu Zhao <yuzhao@google.com> Cc: Zi Yan <ziy@nvidia.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
This commit is contained in:
committed by
Andrew Morton
parent
d2d7867140
commit
474fe91f21
275
mm/hugetlb_cma.c
Normal file
275
mm/hugetlb_cma.c
Normal file
@@ -0,0 +1,275 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
#include <linux/mm.h>
|
||||
#include <linux/cma.h>
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/mm_inline.h>
|
||||
|
||||
#include <asm/page.h>
|
||||
#include <asm/setup.h>
|
||||
|
||||
#include <linux/hugetlb.h>
|
||||
#include "internal.h"
|
||||
#include "hugetlb_cma.h"
|
||||
|
||||
|
||||
static struct cma *hugetlb_cma[MAX_NUMNODES];
|
||||
static unsigned long hugetlb_cma_size_in_node[MAX_NUMNODES] __initdata;
|
||||
static bool hugetlb_cma_only;
|
||||
static unsigned long hugetlb_cma_size __initdata;
|
||||
|
||||
void hugetlb_cma_free_folio(struct folio *folio)
|
||||
{
|
||||
int nid = folio_nid(folio);
|
||||
|
||||
WARN_ON_ONCE(!cma_free_folio(hugetlb_cma[nid], folio));
|
||||
}
|
||||
|
||||
|
||||
struct folio *hugetlb_cma_alloc_folio(struct hstate *h, gfp_t gfp_mask,
|
||||
int nid, nodemask_t *nodemask)
|
||||
{
|
||||
int node;
|
||||
int order = huge_page_order(h);
|
||||
struct folio *folio = NULL;
|
||||
|
||||
if (hugetlb_cma[nid])
|
||||
folio = cma_alloc_folio(hugetlb_cma[nid], order, gfp_mask);
|
||||
|
||||
if (!folio && !(gfp_mask & __GFP_THISNODE)) {
|
||||
for_each_node_mask(node, *nodemask) {
|
||||
if (node == nid || !hugetlb_cma[node])
|
||||
continue;
|
||||
|
||||
folio = cma_alloc_folio(hugetlb_cma[node], order, gfp_mask);
|
||||
if (folio)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (folio)
|
||||
folio_set_hugetlb_cma(folio);
|
||||
|
||||
return folio;
|
||||
}
|
||||
|
||||
struct huge_bootmem_page * __init
|
||||
hugetlb_cma_alloc_bootmem(struct hstate *h, int *nid, bool node_exact)
|
||||
{
|
||||
struct cma *cma;
|
||||
struct huge_bootmem_page *m;
|
||||
int node = *nid;
|
||||
|
||||
cma = hugetlb_cma[*nid];
|
||||
m = cma_reserve_early(cma, huge_page_size(h));
|
||||
if (!m) {
|
||||
if (node_exact)
|
||||
return NULL;
|
||||
|
||||
for_each_online_node(node) {
|
||||
cma = hugetlb_cma[node];
|
||||
if (!cma || node == *nid)
|
||||
continue;
|
||||
m = cma_reserve_early(cma, huge_page_size(h));
|
||||
if (m) {
|
||||
*nid = node;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m) {
|
||||
m->flags = HUGE_BOOTMEM_CMA;
|
||||
m->cma = cma;
|
||||
}
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
|
||||
static bool cma_reserve_called __initdata;
|
||||
|
||||
static int __init cmdline_parse_hugetlb_cma(char *p)
|
||||
{
|
||||
int nid, count = 0;
|
||||
unsigned long tmp;
|
||||
char *s = p;
|
||||
|
||||
while (*s) {
|
||||
if (sscanf(s, "%lu%n", &tmp, &count) != 1)
|
||||
break;
|
||||
|
||||
if (s[count] == ':') {
|
||||
if (tmp >= MAX_NUMNODES)
|
||||
break;
|
||||
nid = array_index_nospec(tmp, MAX_NUMNODES);
|
||||
|
||||
s += count + 1;
|
||||
tmp = memparse(s, &s);
|
||||
hugetlb_cma_size_in_node[nid] = tmp;
|
||||
hugetlb_cma_size += tmp;
|
||||
|
||||
/*
|
||||
* Skip the separator if have one, otherwise
|
||||
* break the parsing.
|
||||
*/
|
||||
if (*s == ',')
|
||||
s++;
|
||||
else
|
||||
break;
|
||||
} else {
|
||||
hugetlb_cma_size = memparse(p, &p);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
early_param("hugetlb_cma", cmdline_parse_hugetlb_cma);
|
||||
|
||||
static int __init cmdline_parse_hugetlb_cma_only(char *p)
|
||||
{
|
||||
return kstrtobool(p, &hugetlb_cma_only);
|
||||
}
|
||||
|
||||
early_param("hugetlb_cma_only", cmdline_parse_hugetlb_cma_only);
|
||||
|
||||
void __init hugetlb_cma_reserve(int order)
|
||||
{
|
||||
unsigned long size, reserved, per_node;
|
||||
bool node_specific_cma_alloc = false;
|
||||
int nid;
|
||||
|
||||
/*
|
||||
* HugeTLB CMA reservation is required for gigantic
|
||||
* huge pages which could not be allocated via the
|
||||
* page allocator. Just warn if there is any change
|
||||
* breaking this assumption.
|
||||
*/
|
||||
VM_WARN_ON(order <= MAX_PAGE_ORDER);
|
||||
cma_reserve_called = true;
|
||||
|
||||
if (!hugetlb_cma_size)
|
||||
return;
|
||||
|
||||
for (nid = 0; nid < MAX_NUMNODES; nid++) {
|
||||
if (hugetlb_cma_size_in_node[nid] == 0)
|
||||
continue;
|
||||
|
||||
if (!node_online(nid)) {
|
||||
pr_warn("hugetlb_cma: invalid node %d specified\n", nid);
|
||||
hugetlb_cma_size -= hugetlb_cma_size_in_node[nid];
|
||||
hugetlb_cma_size_in_node[nid] = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (hugetlb_cma_size_in_node[nid] < (PAGE_SIZE << order)) {
|
||||
pr_warn("hugetlb_cma: cma area of node %d should be at least %lu MiB\n",
|
||||
nid, (PAGE_SIZE << order) / SZ_1M);
|
||||
hugetlb_cma_size -= hugetlb_cma_size_in_node[nid];
|
||||
hugetlb_cma_size_in_node[nid] = 0;
|
||||
} else {
|
||||
node_specific_cma_alloc = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* Validate the CMA size again in case some invalid nodes specified. */
|
||||
if (!hugetlb_cma_size)
|
||||
return;
|
||||
|
||||
if (hugetlb_cma_size < (PAGE_SIZE << order)) {
|
||||
pr_warn("hugetlb_cma: cma area should be at least %lu MiB\n",
|
||||
(PAGE_SIZE << order) / SZ_1M);
|
||||
hugetlb_cma_size = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!node_specific_cma_alloc) {
|
||||
/*
|
||||
* If 3 GB area is requested on a machine with 4 numa nodes,
|
||||
* let's allocate 1 GB on first three nodes and ignore the last one.
|
||||
*/
|
||||
per_node = DIV_ROUND_UP(hugetlb_cma_size, nr_online_nodes);
|
||||
pr_info("hugetlb_cma: reserve %lu MiB, up to %lu MiB per node\n",
|
||||
hugetlb_cma_size / SZ_1M, per_node / SZ_1M);
|
||||
}
|
||||
|
||||
reserved = 0;
|
||||
for_each_online_node(nid) {
|
||||
int res;
|
||||
char name[CMA_MAX_NAME];
|
||||
|
||||
if (node_specific_cma_alloc) {
|
||||
if (hugetlb_cma_size_in_node[nid] == 0)
|
||||
continue;
|
||||
|
||||
size = hugetlb_cma_size_in_node[nid];
|
||||
} else {
|
||||
size = min(per_node, hugetlb_cma_size - reserved);
|
||||
}
|
||||
|
||||
size = round_up(size, PAGE_SIZE << order);
|
||||
|
||||
snprintf(name, sizeof(name), "hugetlb%d", nid);
|
||||
/*
|
||||
* Note that 'order per bit' is based on smallest size that
|
||||
* may be returned to CMA allocator in the case of
|
||||
* huge page demotion.
|
||||
*/
|
||||
res = cma_declare_contiguous_multi(size, PAGE_SIZE << order,
|
||||
HUGETLB_PAGE_ORDER, name,
|
||||
&hugetlb_cma[nid], nid);
|
||||
if (res) {
|
||||
pr_warn("hugetlb_cma: reservation failed: err %d, node %d",
|
||||
res, nid);
|
||||
continue;
|
||||
}
|
||||
|
||||
reserved += size;
|
||||
pr_info("hugetlb_cma: reserved %lu MiB on node %d\n",
|
||||
size / SZ_1M, nid);
|
||||
|
||||
if (reserved >= hugetlb_cma_size)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!reserved)
|
||||
/*
|
||||
* hugetlb_cma_size is used to determine if allocations from
|
||||
* cma are possible. Set to zero if no cma regions are set up.
|
||||
*/
|
||||
hugetlb_cma_size = 0;
|
||||
}
|
||||
|
||||
void __init hugetlb_cma_check(void)
|
||||
{
|
||||
if (!hugetlb_cma_size || cma_reserve_called)
|
||||
return;
|
||||
|
||||
pr_warn("hugetlb_cma: the option isn't supported by current arch\n");
|
||||
}
|
||||
|
||||
bool hugetlb_cma_exclusive_alloc(void)
|
||||
{
|
||||
return hugetlb_cma_only;
|
||||
}
|
||||
|
||||
unsigned long __init hugetlb_cma_total_size(void)
|
||||
{
|
||||
return hugetlb_cma_size;
|
||||
}
|
||||
|
||||
void __init hugetlb_cma_validate_params(void)
|
||||
{
|
||||
if (!hugetlb_cma_size)
|
||||
hugetlb_cma_only = false;
|
||||
}
|
||||
|
||||
bool __init hugetlb_early_cma(struct hstate *h)
|
||||
{
|
||||
if (arch_has_huge_bootmem_alloc())
|
||||
return false;
|
||||
|
||||
return hstate_is_gigantic(h) && hugetlb_cma_only;
|
||||
}
|
||||
Reference in New Issue
Block a user