162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * NUMA support, based on the x86 implementation.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2015 Cavium Inc.
662306a36Sopenharmony_ci * Author: Ganapatrao Kulkarni <gkulkarni@cavium.com>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#define pr_fmt(fmt) "NUMA: " fmt
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/acpi.h>
1262306a36Sopenharmony_ci#include <linux/memblock.h>
1362306a36Sopenharmony_ci#include <linux/module.h>
1462306a36Sopenharmony_ci#include <linux/of.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include <asm/sections.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_cistruct pglist_data *node_data[MAX_NUMNODES] __read_mostly;
1962306a36Sopenharmony_ciEXPORT_SYMBOL(node_data);
2062306a36Sopenharmony_cinodemask_t numa_nodes_parsed __initdata;
2162306a36Sopenharmony_cistatic int cpu_to_node_map[NR_CPUS] = { [0 ... NR_CPUS-1] = NUMA_NO_NODE };
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_cistatic int numa_distance_cnt;
2462306a36Sopenharmony_cistatic u8 *numa_distance;
2562306a36Sopenharmony_cibool numa_off;
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cistatic __init int numa_parse_early_param(char *opt)
2862306a36Sopenharmony_ci{
2962306a36Sopenharmony_ci	if (!opt)
3062306a36Sopenharmony_ci		return -EINVAL;
3162306a36Sopenharmony_ci	if (str_has_prefix(opt, "off"))
3262306a36Sopenharmony_ci		numa_off = true;
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	return 0;
3562306a36Sopenharmony_ci}
3662306a36Sopenharmony_ciearly_param("numa", numa_parse_early_param);
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cicpumask_var_t node_to_cpumask_map[MAX_NUMNODES];
3962306a36Sopenharmony_ciEXPORT_SYMBOL(node_to_cpumask_map);
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_PER_CPU_MAPS
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci/*
4462306a36Sopenharmony_ci * Returns a pointer to the bitmask of CPUs on Node 'node'.
4562306a36Sopenharmony_ci */
4662306a36Sopenharmony_ciconst struct cpumask *cpumask_of_node(int node)
4762306a36Sopenharmony_ci{
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	if (node == NUMA_NO_NODE)
5062306a36Sopenharmony_ci		return cpu_all_mask;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	if (WARN_ON(node < 0 || node >= nr_node_ids))
5362306a36Sopenharmony_ci		return cpu_none_mask;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	if (WARN_ON(node_to_cpumask_map[node] == NULL))
5662306a36Sopenharmony_ci		return cpu_online_mask;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	return node_to_cpumask_map[node];
5962306a36Sopenharmony_ci}
6062306a36Sopenharmony_ciEXPORT_SYMBOL(cpumask_of_node);
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci#endif
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_cistatic void numa_update_cpu(unsigned int cpu, bool remove)
6562306a36Sopenharmony_ci{
6662306a36Sopenharmony_ci	int nid = cpu_to_node(cpu);
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	if (nid == NUMA_NO_NODE)
6962306a36Sopenharmony_ci		return;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	if (remove)
7262306a36Sopenharmony_ci		cpumask_clear_cpu(cpu, node_to_cpumask_map[nid]);
7362306a36Sopenharmony_ci	else
7462306a36Sopenharmony_ci		cpumask_set_cpu(cpu, node_to_cpumask_map[nid]);
7562306a36Sopenharmony_ci}
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_civoid numa_add_cpu(unsigned int cpu)
7862306a36Sopenharmony_ci{
7962306a36Sopenharmony_ci	numa_update_cpu(cpu, false);
8062306a36Sopenharmony_ci}
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_civoid numa_remove_cpu(unsigned int cpu)
8362306a36Sopenharmony_ci{
8462306a36Sopenharmony_ci	numa_update_cpu(cpu, true);
8562306a36Sopenharmony_ci}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_civoid numa_clear_node(unsigned int cpu)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	numa_remove_cpu(cpu);
9062306a36Sopenharmony_ci	set_cpu_numa_node(cpu, NUMA_NO_NODE);
9162306a36Sopenharmony_ci}
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci/*
9462306a36Sopenharmony_ci * Allocate node_to_cpumask_map based on number of available nodes
9562306a36Sopenharmony_ci * Requires node_possible_map to be valid.
9662306a36Sopenharmony_ci *
9762306a36Sopenharmony_ci * Note: cpumask_of_node() is not valid until after this is done.
9862306a36Sopenharmony_ci * (Use CONFIG_DEBUG_PER_CPU_MAPS to check this.)
9962306a36Sopenharmony_ci */
10062306a36Sopenharmony_cistatic void __init setup_node_to_cpumask_map(void)
10162306a36Sopenharmony_ci{
10262306a36Sopenharmony_ci	int node;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	/* setup nr_node_ids if not done yet */
10562306a36Sopenharmony_ci	if (nr_node_ids == MAX_NUMNODES)
10662306a36Sopenharmony_ci		setup_nr_node_ids();
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	/* allocate and clear the mapping */
10962306a36Sopenharmony_ci	for (node = 0; node < nr_node_ids; node++) {
11062306a36Sopenharmony_ci		alloc_bootmem_cpumask_var(&node_to_cpumask_map[node]);
11162306a36Sopenharmony_ci		cpumask_clear(node_to_cpumask_map[node]);
11262306a36Sopenharmony_ci	}
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	/* cpumask_of_node() will now work */
11562306a36Sopenharmony_ci	pr_debug("Node to cpumask map for %u nodes\n", nr_node_ids);
11662306a36Sopenharmony_ci}
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci/*
11962306a36Sopenharmony_ci * Set the cpu to node and mem mapping
12062306a36Sopenharmony_ci */
12162306a36Sopenharmony_civoid numa_store_cpu_info(unsigned int cpu)
12262306a36Sopenharmony_ci{
12362306a36Sopenharmony_ci	set_cpu_numa_node(cpu, cpu_to_node_map[cpu]);
12462306a36Sopenharmony_ci}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_civoid __init early_map_cpu_to_node(unsigned int cpu, int nid)
12762306a36Sopenharmony_ci{
12862306a36Sopenharmony_ci	/* fallback to node 0 */
12962306a36Sopenharmony_ci	if (nid < 0 || nid >= MAX_NUMNODES || numa_off)
13062306a36Sopenharmony_ci		nid = 0;
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	cpu_to_node_map[cpu] = nid;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	/*
13562306a36Sopenharmony_ci	 * We should set the numa node of cpu0 as soon as possible, because it
13662306a36Sopenharmony_ci	 * has already been set up online before. cpu_to_node(0) will soon be
13762306a36Sopenharmony_ci	 * called.
13862306a36Sopenharmony_ci	 */
13962306a36Sopenharmony_ci	if (!cpu)
14062306a36Sopenharmony_ci		set_cpu_numa_node(cpu, nid);
14162306a36Sopenharmony_ci}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci#ifdef CONFIG_HAVE_SETUP_PER_CPU_AREA
14462306a36Sopenharmony_ciunsigned long __per_cpu_offset[NR_CPUS] __read_mostly;
14562306a36Sopenharmony_ciEXPORT_SYMBOL(__per_cpu_offset);
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ciint __init early_cpu_to_node(int cpu)
14862306a36Sopenharmony_ci{
14962306a36Sopenharmony_ci	return cpu_to_node_map[cpu];
15062306a36Sopenharmony_ci}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_cistatic int __init pcpu_cpu_distance(unsigned int from, unsigned int to)
15362306a36Sopenharmony_ci{
15462306a36Sopenharmony_ci	return node_distance(early_cpu_to_node(from), early_cpu_to_node(to));
15562306a36Sopenharmony_ci}
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_civoid __init setup_per_cpu_areas(void)
15862306a36Sopenharmony_ci{
15962306a36Sopenharmony_ci	unsigned long delta;
16062306a36Sopenharmony_ci	unsigned int cpu;
16162306a36Sopenharmony_ci	int rc = -EINVAL;
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	if (pcpu_chosen_fc != PCPU_FC_PAGE) {
16462306a36Sopenharmony_ci		/*
16562306a36Sopenharmony_ci		 * Always reserve area for module percpu variables.  That's
16662306a36Sopenharmony_ci		 * what the legacy allocator did.
16762306a36Sopenharmony_ci		 */
16862306a36Sopenharmony_ci		rc = pcpu_embed_first_chunk(PERCPU_MODULE_RESERVE,
16962306a36Sopenharmony_ci					    PERCPU_DYNAMIC_RESERVE, PAGE_SIZE,
17062306a36Sopenharmony_ci					    pcpu_cpu_distance,
17162306a36Sopenharmony_ci					    early_cpu_to_node);
17262306a36Sopenharmony_ci#ifdef CONFIG_NEED_PER_CPU_PAGE_FIRST_CHUNK
17362306a36Sopenharmony_ci		if (rc < 0)
17462306a36Sopenharmony_ci			pr_warn("PERCPU: %s allocator failed (%d), falling back to page size\n",
17562306a36Sopenharmony_ci				   pcpu_fc_names[pcpu_chosen_fc], rc);
17662306a36Sopenharmony_ci#endif
17762306a36Sopenharmony_ci	}
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci#ifdef CONFIG_NEED_PER_CPU_PAGE_FIRST_CHUNK
18062306a36Sopenharmony_ci	if (rc < 0)
18162306a36Sopenharmony_ci		rc = pcpu_page_first_chunk(PERCPU_MODULE_RESERVE, early_cpu_to_node);
18262306a36Sopenharmony_ci#endif
18362306a36Sopenharmony_ci	if (rc < 0)
18462306a36Sopenharmony_ci		panic("Failed to initialize percpu areas (err=%d).", rc);
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	delta = (unsigned long)pcpu_base_addr - (unsigned long)__per_cpu_start;
18762306a36Sopenharmony_ci	for_each_possible_cpu(cpu)
18862306a36Sopenharmony_ci		__per_cpu_offset[cpu] = delta + pcpu_unit_offsets[cpu];
18962306a36Sopenharmony_ci}
19062306a36Sopenharmony_ci#endif
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci/**
19362306a36Sopenharmony_ci * numa_add_memblk() - Set node id to memblk
19462306a36Sopenharmony_ci * @nid: NUMA node ID of the new memblk
19562306a36Sopenharmony_ci * @start: Start address of the new memblk
19662306a36Sopenharmony_ci * @end:  End address of the new memblk
19762306a36Sopenharmony_ci *
19862306a36Sopenharmony_ci * RETURNS:
19962306a36Sopenharmony_ci * 0 on success, -errno on failure.
20062306a36Sopenharmony_ci */
20162306a36Sopenharmony_ciint __init numa_add_memblk(int nid, u64 start, u64 end)
20262306a36Sopenharmony_ci{
20362306a36Sopenharmony_ci	int ret;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	ret = memblock_set_node(start, (end - start), &memblock.memory, nid);
20662306a36Sopenharmony_ci	if (ret < 0) {
20762306a36Sopenharmony_ci		pr_err("memblock [0x%llx - 0x%llx] failed to add on node %d\n",
20862306a36Sopenharmony_ci			start, (end - 1), nid);
20962306a36Sopenharmony_ci		return ret;
21062306a36Sopenharmony_ci	}
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	node_set(nid, numa_nodes_parsed);
21362306a36Sopenharmony_ci	return ret;
21462306a36Sopenharmony_ci}
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci/*
21762306a36Sopenharmony_ci * Initialize NODE_DATA for a node on the local memory
21862306a36Sopenharmony_ci */
21962306a36Sopenharmony_cistatic void __init setup_node_data(int nid, u64 start_pfn, u64 end_pfn)
22062306a36Sopenharmony_ci{
22162306a36Sopenharmony_ci	const size_t nd_size = roundup(sizeof(pg_data_t), SMP_CACHE_BYTES);
22262306a36Sopenharmony_ci	u64 nd_pa;
22362306a36Sopenharmony_ci	void *nd;
22462306a36Sopenharmony_ci	int tnid;
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	if (start_pfn >= end_pfn)
22762306a36Sopenharmony_ci		pr_info("Initmem setup node %d [<memory-less node>]\n", nid);
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	nd_pa = memblock_phys_alloc_try_nid(nd_size, SMP_CACHE_BYTES, nid);
23062306a36Sopenharmony_ci	if (!nd_pa)
23162306a36Sopenharmony_ci		panic("Cannot allocate %zu bytes for node %d data\n",
23262306a36Sopenharmony_ci		      nd_size, nid);
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	nd = __va(nd_pa);
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	/* report and initialize */
23762306a36Sopenharmony_ci	pr_info("NODE_DATA [mem %#010Lx-%#010Lx]\n",
23862306a36Sopenharmony_ci		nd_pa, nd_pa + nd_size - 1);
23962306a36Sopenharmony_ci	tnid = early_pfn_to_nid(nd_pa >> PAGE_SHIFT);
24062306a36Sopenharmony_ci	if (tnid != nid)
24162306a36Sopenharmony_ci		pr_info("NODE_DATA(%d) on node %d\n", nid, tnid);
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	node_data[nid] = nd;
24462306a36Sopenharmony_ci	memset(NODE_DATA(nid), 0, sizeof(pg_data_t));
24562306a36Sopenharmony_ci	NODE_DATA(nid)->node_id = nid;
24662306a36Sopenharmony_ci	NODE_DATA(nid)->node_start_pfn = start_pfn;
24762306a36Sopenharmony_ci	NODE_DATA(nid)->node_spanned_pages = end_pfn - start_pfn;
24862306a36Sopenharmony_ci}
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci/*
25162306a36Sopenharmony_ci * numa_free_distance
25262306a36Sopenharmony_ci *
25362306a36Sopenharmony_ci * The current table is freed.
25462306a36Sopenharmony_ci */
25562306a36Sopenharmony_civoid __init numa_free_distance(void)
25662306a36Sopenharmony_ci{
25762306a36Sopenharmony_ci	size_t size;
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	if (!numa_distance)
26062306a36Sopenharmony_ci		return;
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	size = numa_distance_cnt * numa_distance_cnt *
26362306a36Sopenharmony_ci		sizeof(numa_distance[0]);
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	memblock_free(numa_distance, size);
26662306a36Sopenharmony_ci	numa_distance_cnt = 0;
26762306a36Sopenharmony_ci	numa_distance = NULL;
26862306a36Sopenharmony_ci}
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci/*
27162306a36Sopenharmony_ci * Create a new NUMA distance table.
27262306a36Sopenharmony_ci */
27362306a36Sopenharmony_cistatic int __init numa_alloc_distance(void)
27462306a36Sopenharmony_ci{
27562306a36Sopenharmony_ci	size_t size;
27662306a36Sopenharmony_ci	int i, j;
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	size = nr_node_ids * nr_node_ids * sizeof(numa_distance[0]);
27962306a36Sopenharmony_ci	numa_distance = memblock_alloc(size, PAGE_SIZE);
28062306a36Sopenharmony_ci	if (WARN_ON(!numa_distance))
28162306a36Sopenharmony_ci		return -ENOMEM;
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	numa_distance_cnt = nr_node_ids;
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	/* fill with the default distances */
28662306a36Sopenharmony_ci	for (i = 0; i < numa_distance_cnt; i++)
28762306a36Sopenharmony_ci		for (j = 0; j < numa_distance_cnt; j++)
28862306a36Sopenharmony_ci			numa_distance[i * numa_distance_cnt + j] = i == j ?
28962306a36Sopenharmony_ci				LOCAL_DISTANCE : REMOTE_DISTANCE;
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	pr_debug("Initialized distance table, cnt=%d\n", numa_distance_cnt);
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	return 0;
29462306a36Sopenharmony_ci}
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci/**
29762306a36Sopenharmony_ci * numa_set_distance() - Set inter node NUMA distance from node to node.
29862306a36Sopenharmony_ci * @from: the 'from' node to set distance
29962306a36Sopenharmony_ci * @to: the 'to'  node to set distance
30062306a36Sopenharmony_ci * @distance: NUMA distance
30162306a36Sopenharmony_ci *
30262306a36Sopenharmony_ci * Set the distance from node @from to @to to @distance.
30362306a36Sopenharmony_ci * If distance table doesn't exist, a warning is printed.
30462306a36Sopenharmony_ci *
30562306a36Sopenharmony_ci * If @from or @to is higher than the highest known node or lower than zero
30662306a36Sopenharmony_ci * or @distance doesn't make sense, the call is ignored.
30762306a36Sopenharmony_ci */
30862306a36Sopenharmony_civoid __init numa_set_distance(int from, int to, int distance)
30962306a36Sopenharmony_ci{
31062306a36Sopenharmony_ci	if (!numa_distance) {
31162306a36Sopenharmony_ci		pr_warn_once("Warning: distance table not allocated yet\n");
31262306a36Sopenharmony_ci		return;
31362306a36Sopenharmony_ci	}
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	if (from >= numa_distance_cnt || to >= numa_distance_cnt ||
31662306a36Sopenharmony_ci			from < 0 || to < 0) {
31762306a36Sopenharmony_ci		pr_warn_once("Warning: node ids are out of bound, from=%d to=%d distance=%d\n",
31862306a36Sopenharmony_ci			    from, to, distance);
31962306a36Sopenharmony_ci		return;
32062306a36Sopenharmony_ci	}
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	if ((u8)distance != distance ||
32362306a36Sopenharmony_ci	    (from == to && distance != LOCAL_DISTANCE)) {
32462306a36Sopenharmony_ci		pr_warn_once("Warning: invalid distance parameter, from=%d to=%d distance=%d\n",
32562306a36Sopenharmony_ci			     from, to, distance);
32662306a36Sopenharmony_ci		return;
32762306a36Sopenharmony_ci	}
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	numa_distance[from * numa_distance_cnt + to] = distance;
33062306a36Sopenharmony_ci}
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci/*
33362306a36Sopenharmony_ci * Return NUMA distance @from to @to
33462306a36Sopenharmony_ci */
33562306a36Sopenharmony_ciint __node_distance(int from, int to)
33662306a36Sopenharmony_ci{
33762306a36Sopenharmony_ci	if (from >= numa_distance_cnt || to >= numa_distance_cnt)
33862306a36Sopenharmony_ci		return from == to ? LOCAL_DISTANCE : REMOTE_DISTANCE;
33962306a36Sopenharmony_ci	return numa_distance[from * numa_distance_cnt + to];
34062306a36Sopenharmony_ci}
34162306a36Sopenharmony_ciEXPORT_SYMBOL(__node_distance);
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_cistatic int __init numa_register_nodes(void)
34462306a36Sopenharmony_ci{
34562306a36Sopenharmony_ci	int nid;
34662306a36Sopenharmony_ci	struct memblock_region *mblk;
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	/* Check that valid nid is set to memblks */
34962306a36Sopenharmony_ci	for_each_mem_region(mblk) {
35062306a36Sopenharmony_ci		int mblk_nid = memblock_get_region_node(mblk);
35162306a36Sopenharmony_ci		phys_addr_t start = mblk->base;
35262306a36Sopenharmony_ci		phys_addr_t end = mblk->base + mblk->size - 1;
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci		if (mblk_nid == NUMA_NO_NODE || mblk_nid >= MAX_NUMNODES) {
35562306a36Sopenharmony_ci			pr_warn("Warning: invalid memblk node %d [mem %pap-%pap]\n",
35662306a36Sopenharmony_ci				mblk_nid, &start, &end);
35762306a36Sopenharmony_ci			return -EINVAL;
35862306a36Sopenharmony_ci		}
35962306a36Sopenharmony_ci	}
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	/* Finally register nodes. */
36262306a36Sopenharmony_ci	for_each_node_mask(nid, numa_nodes_parsed) {
36362306a36Sopenharmony_ci		unsigned long start_pfn, end_pfn;
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci		get_pfn_range_for_nid(nid, &start_pfn, &end_pfn);
36662306a36Sopenharmony_ci		setup_node_data(nid, start_pfn, end_pfn);
36762306a36Sopenharmony_ci		node_set_online(nid);
36862306a36Sopenharmony_ci	}
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	/* Setup online nodes to actual nodes*/
37162306a36Sopenharmony_ci	node_possible_map = numa_nodes_parsed;
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	return 0;
37462306a36Sopenharmony_ci}
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_cistatic int __init numa_init(int (*init_func)(void))
37762306a36Sopenharmony_ci{
37862306a36Sopenharmony_ci	int ret;
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	nodes_clear(numa_nodes_parsed);
38162306a36Sopenharmony_ci	nodes_clear(node_possible_map);
38262306a36Sopenharmony_ci	nodes_clear(node_online_map);
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	ret = numa_alloc_distance();
38562306a36Sopenharmony_ci	if (ret < 0)
38662306a36Sopenharmony_ci		return ret;
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	ret = init_func();
38962306a36Sopenharmony_ci	if (ret < 0)
39062306a36Sopenharmony_ci		goto out_free_distance;
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	if (nodes_empty(numa_nodes_parsed)) {
39362306a36Sopenharmony_ci		pr_info("No NUMA configuration found\n");
39462306a36Sopenharmony_ci		ret = -EINVAL;
39562306a36Sopenharmony_ci		goto out_free_distance;
39662306a36Sopenharmony_ci	}
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	ret = numa_register_nodes();
39962306a36Sopenharmony_ci	if (ret < 0)
40062306a36Sopenharmony_ci		goto out_free_distance;
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	setup_node_to_cpumask_map();
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	return 0;
40562306a36Sopenharmony_ciout_free_distance:
40662306a36Sopenharmony_ci	numa_free_distance();
40762306a36Sopenharmony_ci	return ret;
40862306a36Sopenharmony_ci}
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci/**
41162306a36Sopenharmony_ci * dummy_numa_init() - Fallback dummy NUMA init
41262306a36Sopenharmony_ci *
41362306a36Sopenharmony_ci * Used if there's no underlying NUMA architecture, NUMA initialization
41462306a36Sopenharmony_ci * fails, or NUMA is disabled on the command line.
41562306a36Sopenharmony_ci *
41662306a36Sopenharmony_ci * Must online at least one node (node 0) and add memory blocks that cover all
41762306a36Sopenharmony_ci * allowed memory. It is unlikely that this function fails.
41862306a36Sopenharmony_ci *
41962306a36Sopenharmony_ci * Return: 0 on success, -errno on failure.
42062306a36Sopenharmony_ci */
42162306a36Sopenharmony_cistatic int __init dummy_numa_init(void)
42262306a36Sopenharmony_ci{
42362306a36Sopenharmony_ci	phys_addr_t start = memblock_start_of_DRAM();
42462306a36Sopenharmony_ci	phys_addr_t end = memblock_end_of_DRAM() - 1;
42562306a36Sopenharmony_ci	int ret;
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	if (numa_off)
42862306a36Sopenharmony_ci		pr_info("NUMA disabled\n"); /* Forced off on command line. */
42962306a36Sopenharmony_ci	pr_info("Faking a node at [mem %pap-%pap]\n", &start, &end);
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	ret = numa_add_memblk(0, start, end + 1);
43262306a36Sopenharmony_ci	if (ret) {
43362306a36Sopenharmony_ci		pr_err("NUMA init failed\n");
43462306a36Sopenharmony_ci		return ret;
43562306a36Sopenharmony_ci	}
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	numa_off = true;
43862306a36Sopenharmony_ci	return 0;
43962306a36Sopenharmony_ci}
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci#ifdef CONFIG_ACPI_NUMA
44262306a36Sopenharmony_cistatic int __init arch_acpi_numa_init(void)
44362306a36Sopenharmony_ci{
44462306a36Sopenharmony_ci	int ret;
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	ret = acpi_numa_init();
44762306a36Sopenharmony_ci	if (ret) {
44862306a36Sopenharmony_ci		pr_info("Failed to initialise from firmware\n");
44962306a36Sopenharmony_ci		return ret;
45062306a36Sopenharmony_ci	}
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	return srat_disabled() ? -EINVAL : 0;
45362306a36Sopenharmony_ci}
45462306a36Sopenharmony_ci#else
45562306a36Sopenharmony_cistatic int __init arch_acpi_numa_init(void)
45662306a36Sopenharmony_ci{
45762306a36Sopenharmony_ci	return -EOPNOTSUPP;
45862306a36Sopenharmony_ci}
45962306a36Sopenharmony_ci#endif
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci/**
46262306a36Sopenharmony_ci * arch_numa_init() - Initialize NUMA
46362306a36Sopenharmony_ci *
46462306a36Sopenharmony_ci * Try each configured NUMA initialization method until one succeeds. The
46562306a36Sopenharmony_ci * last fallback is dummy single node config encompassing whole memory.
46662306a36Sopenharmony_ci */
46762306a36Sopenharmony_civoid __init arch_numa_init(void)
46862306a36Sopenharmony_ci{
46962306a36Sopenharmony_ci	if (!numa_off) {
47062306a36Sopenharmony_ci		if (!acpi_disabled && !numa_init(arch_acpi_numa_init))
47162306a36Sopenharmony_ci			return;
47262306a36Sopenharmony_ci		if (acpi_disabled && !numa_init(of_numa_init))
47362306a36Sopenharmony_ci			return;
47462306a36Sopenharmony_ci	}
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	numa_init(dummy_numa_init);
47762306a36Sopenharmony_ci}
478