18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * NUMA support, based on the x86 implementation.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2015 Cavium Inc.
68c2ecf20Sopenharmony_ci * Author: Ganapatrao Kulkarni <gkulkarni@cavium.com>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "NUMA: " fmt
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/acpi.h>
128c2ecf20Sopenharmony_ci#include <linux/memblock.h>
138c2ecf20Sopenharmony_ci#include <linux/module.h>
148c2ecf20Sopenharmony_ci#include <linux/of.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include <asm/acpi.h>
178c2ecf20Sopenharmony_ci#include <asm/sections.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_cistruct pglist_data *node_data[MAX_NUMNODES] __read_mostly;
208c2ecf20Sopenharmony_ciEXPORT_SYMBOL(node_data);
218c2ecf20Sopenharmony_cinodemask_t numa_nodes_parsed __initdata;
228c2ecf20Sopenharmony_cistatic int cpu_to_node_map[NR_CPUS] = { [0 ... NR_CPUS-1] = NUMA_NO_NODE };
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_cistatic int numa_distance_cnt;
258c2ecf20Sopenharmony_cistatic u8 *numa_distance;
268c2ecf20Sopenharmony_cibool numa_off;
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_cistatic __init int numa_parse_early_param(char *opt)
298c2ecf20Sopenharmony_ci{
308c2ecf20Sopenharmony_ci	if (!opt)
318c2ecf20Sopenharmony_ci		return -EINVAL;
328c2ecf20Sopenharmony_ci	if (str_has_prefix(opt, "off"))
338c2ecf20Sopenharmony_ci		numa_off = true;
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci	return 0;
368c2ecf20Sopenharmony_ci}
378c2ecf20Sopenharmony_ciearly_param("numa", numa_parse_early_param);
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_cicpumask_var_t node_to_cpumask_map[MAX_NUMNODES];
408c2ecf20Sopenharmony_ciEXPORT_SYMBOL(node_to_cpumask_map);
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_PER_CPU_MAPS
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci/*
458c2ecf20Sopenharmony_ci * Returns a pointer to the bitmask of CPUs on Node 'node'.
468c2ecf20Sopenharmony_ci */
478c2ecf20Sopenharmony_ciconst struct cpumask *cpumask_of_node(int node)
488c2ecf20Sopenharmony_ci{
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	if (node == NUMA_NO_NODE)
518c2ecf20Sopenharmony_ci		return cpu_all_mask;
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	if (WARN_ON(node < 0 || node >= nr_node_ids))
548c2ecf20Sopenharmony_ci		return cpu_none_mask;
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	if (WARN_ON(node_to_cpumask_map[node] == NULL))
578c2ecf20Sopenharmony_ci		return cpu_online_mask;
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	return node_to_cpumask_map[node];
608c2ecf20Sopenharmony_ci}
618c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cpumask_of_node);
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci#endif
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cistatic void numa_update_cpu(unsigned int cpu, bool remove)
668c2ecf20Sopenharmony_ci{
678c2ecf20Sopenharmony_ci	int nid = cpu_to_node(cpu);
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	if (nid == NUMA_NO_NODE)
708c2ecf20Sopenharmony_ci		return;
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	if (remove)
738c2ecf20Sopenharmony_ci		cpumask_clear_cpu(cpu, node_to_cpumask_map[nid]);
748c2ecf20Sopenharmony_ci	else
758c2ecf20Sopenharmony_ci		cpumask_set_cpu(cpu, node_to_cpumask_map[nid]);
768c2ecf20Sopenharmony_ci}
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_civoid numa_add_cpu(unsigned int cpu)
798c2ecf20Sopenharmony_ci{
808c2ecf20Sopenharmony_ci	numa_update_cpu(cpu, false);
818c2ecf20Sopenharmony_ci}
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_civoid numa_remove_cpu(unsigned int cpu)
848c2ecf20Sopenharmony_ci{
858c2ecf20Sopenharmony_ci	numa_update_cpu(cpu, true);
868c2ecf20Sopenharmony_ci}
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_civoid numa_clear_node(unsigned int cpu)
898c2ecf20Sopenharmony_ci{
908c2ecf20Sopenharmony_ci	numa_remove_cpu(cpu);
918c2ecf20Sopenharmony_ci	set_cpu_numa_node(cpu, NUMA_NO_NODE);
928c2ecf20Sopenharmony_ci}
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci/*
958c2ecf20Sopenharmony_ci * Allocate node_to_cpumask_map based on number of available nodes
968c2ecf20Sopenharmony_ci * Requires node_possible_map to be valid.
978c2ecf20Sopenharmony_ci *
988c2ecf20Sopenharmony_ci * Note: cpumask_of_node() is not valid until after this is done.
998c2ecf20Sopenharmony_ci * (Use CONFIG_DEBUG_PER_CPU_MAPS to check this.)
1008c2ecf20Sopenharmony_ci */
1018c2ecf20Sopenharmony_cistatic void __init setup_node_to_cpumask_map(void)
1028c2ecf20Sopenharmony_ci{
1038c2ecf20Sopenharmony_ci	int node;
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	/* setup nr_node_ids if not done yet */
1068c2ecf20Sopenharmony_ci	if (nr_node_ids == MAX_NUMNODES)
1078c2ecf20Sopenharmony_ci		setup_nr_node_ids();
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	/* allocate and clear the mapping */
1108c2ecf20Sopenharmony_ci	for (node = 0; node < nr_node_ids; node++) {
1118c2ecf20Sopenharmony_ci		alloc_bootmem_cpumask_var(&node_to_cpumask_map[node]);
1128c2ecf20Sopenharmony_ci		cpumask_clear(node_to_cpumask_map[node]);
1138c2ecf20Sopenharmony_ci	}
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	/* cpumask_of_node() will now work */
1168c2ecf20Sopenharmony_ci	pr_debug("Node to cpumask map for %u nodes\n", nr_node_ids);
1178c2ecf20Sopenharmony_ci}
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci/*
1208c2ecf20Sopenharmony_ci * Set the cpu to node and mem mapping
1218c2ecf20Sopenharmony_ci */
1228c2ecf20Sopenharmony_civoid numa_store_cpu_info(unsigned int cpu)
1238c2ecf20Sopenharmony_ci{
1248c2ecf20Sopenharmony_ci	set_cpu_numa_node(cpu, cpu_to_node_map[cpu]);
1258c2ecf20Sopenharmony_ci}
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_civoid __init early_map_cpu_to_node(unsigned int cpu, int nid)
1288c2ecf20Sopenharmony_ci{
1298c2ecf20Sopenharmony_ci	/* fallback to node 0 */
1308c2ecf20Sopenharmony_ci	if (nid < 0 || nid >= MAX_NUMNODES || numa_off)
1318c2ecf20Sopenharmony_ci		nid = 0;
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	cpu_to_node_map[cpu] = nid;
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	/*
1368c2ecf20Sopenharmony_ci	 * We should set the numa node of cpu0 as soon as possible, because it
1378c2ecf20Sopenharmony_ci	 * has already been set up online before. cpu_to_node(0) will soon be
1388c2ecf20Sopenharmony_ci	 * called.
1398c2ecf20Sopenharmony_ci	 */
1408c2ecf20Sopenharmony_ci	if (!cpu)
1418c2ecf20Sopenharmony_ci		set_cpu_numa_node(cpu, nid);
1428c2ecf20Sopenharmony_ci}
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci#ifdef CONFIG_HAVE_SETUP_PER_CPU_AREA
1458c2ecf20Sopenharmony_ciunsigned long __per_cpu_offset[NR_CPUS] __read_mostly;
1468c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__per_cpu_offset);
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_cistatic int __init early_cpu_to_node(int cpu)
1498c2ecf20Sopenharmony_ci{
1508c2ecf20Sopenharmony_ci	return cpu_to_node_map[cpu];
1518c2ecf20Sopenharmony_ci}
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_cistatic int __init pcpu_cpu_distance(unsigned int from, unsigned int to)
1548c2ecf20Sopenharmony_ci{
1558c2ecf20Sopenharmony_ci	return node_distance(early_cpu_to_node(from), early_cpu_to_node(to));
1568c2ecf20Sopenharmony_ci}
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_cistatic void * __init pcpu_fc_alloc(unsigned int cpu, size_t size,
1598c2ecf20Sopenharmony_ci				       size_t align)
1608c2ecf20Sopenharmony_ci{
1618c2ecf20Sopenharmony_ci	int nid = early_cpu_to_node(cpu);
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	return  memblock_alloc_try_nid(size, align,
1648c2ecf20Sopenharmony_ci			__pa(MAX_DMA_ADDRESS), MEMBLOCK_ALLOC_ACCESSIBLE, nid);
1658c2ecf20Sopenharmony_ci}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_cistatic void __init pcpu_fc_free(void *ptr, size_t size)
1688c2ecf20Sopenharmony_ci{
1698c2ecf20Sopenharmony_ci	memblock_free_early(__pa(ptr), size);
1708c2ecf20Sopenharmony_ci}
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_civoid __init setup_per_cpu_areas(void)
1738c2ecf20Sopenharmony_ci{
1748c2ecf20Sopenharmony_ci	unsigned long delta;
1758c2ecf20Sopenharmony_ci	unsigned int cpu;
1768c2ecf20Sopenharmony_ci	int rc;
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	/*
1798c2ecf20Sopenharmony_ci	 * Always reserve area for module percpu variables.  That's
1808c2ecf20Sopenharmony_ci	 * what the legacy allocator did.
1818c2ecf20Sopenharmony_ci	 */
1828c2ecf20Sopenharmony_ci	rc = pcpu_embed_first_chunk(PERCPU_MODULE_RESERVE,
1838c2ecf20Sopenharmony_ci				    PERCPU_DYNAMIC_RESERVE, PAGE_SIZE,
1848c2ecf20Sopenharmony_ci				    pcpu_cpu_distance,
1858c2ecf20Sopenharmony_ci				    pcpu_fc_alloc, pcpu_fc_free);
1868c2ecf20Sopenharmony_ci	if (rc < 0)
1878c2ecf20Sopenharmony_ci		panic("Failed to initialize percpu areas.");
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	delta = (unsigned long)pcpu_base_addr - (unsigned long)__per_cpu_start;
1908c2ecf20Sopenharmony_ci	for_each_possible_cpu(cpu)
1918c2ecf20Sopenharmony_ci		__per_cpu_offset[cpu] = delta + pcpu_unit_offsets[cpu];
1928c2ecf20Sopenharmony_ci}
1938c2ecf20Sopenharmony_ci#endif
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci/**
1968c2ecf20Sopenharmony_ci * numa_add_memblk() - Set node id to memblk
1978c2ecf20Sopenharmony_ci * @nid: NUMA node ID of the new memblk
1988c2ecf20Sopenharmony_ci * @start: Start address of the new memblk
1998c2ecf20Sopenharmony_ci * @end:  End address of the new memblk
2008c2ecf20Sopenharmony_ci *
2018c2ecf20Sopenharmony_ci * RETURNS:
2028c2ecf20Sopenharmony_ci * 0 on success, -errno on failure.
2038c2ecf20Sopenharmony_ci */
2048c2ecf20Sopenharmony_ciint __init numa_add_memblk(int nid, u64 start, u64 end)
2058c2ecf20Sopenharmony_ci{
2068c2ecf20Sopenharmony_ci	int ret;
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	ret = memblock_set_node(start, (end - start), &memblock.memory, nid);
2098c2ecf20Sopenharmony_ci	if (ret < 0) {
2108c2ecf20Sopenharmony_ci		pr_err("memblock [0x%llx - 0x%llx] failed to add on node %d\n",
2118c2ecf20Sopenharmony_ci			start, (end - 1), nid);
2128c2ecf20Sopenharmony_ci		return ret;
2138c2ecf20Sopenharmony_ci	}
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	node_set(nid, numa_nodes_parsed);
2168c2ecf20Sopenharmony_ci	return ret;
2178c2ecf20Sopenharmony_ci}
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci/*
2208c2ecf20Sopenharmony_ci * Initialize NODE_DATA for a node on the local memory
2218c2ecf20Sopenharmony_ci */
2228c2ecf20Sopenharmony_cistatic void __init setup_node_data(int nid, u64 start_pfn, u64 end_pfn)
2238c2ecf20Sopenharmony_ci{
2248c2ecf20Sopenharmony_ci	const size_t nd_size = roundup(sizeof(pg_data_t), SMP_CACHE_BYTES);
2258c2ecf20Sopenharmony_ci	u64 nd_pa;
2268c2ecf20Sopenharmony_ci	void *nd;
2278c2ecf20Sopenharmony_ci	int tnid;
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	if (start_pfn >= end_pfn)
2308c2ecf20Sopenharmony_ci		pr_info("Initmem setup node %d [<memory-less node>]\n", nid);
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	nd_pa = memblock_phys_alloc_try_nid(nd_size, SMP_CACHE_BYTES, nid);
2338c2ecf20Sopenharmony_ci	if (!nd_pa)
2348c2ecf20Sopenharmony_ci		panic("Cannot allocate %zu bytes for node %d data\n",
2358c2ecf20Sopenharmony_ci		      nd_size, nid);
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	nd = __va(nd_pa);
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	/* report and initialize */
2408c2ecf20Sopenharmony_ci	pr_info("NODE_DATA [mem %#010Lx-%#010Lx]\n",
2418c2ecf20Sopenharmony_ci		nd_pa, nd_pa + nd_size - 1);
2428c2ecf20Sopenharmony_ci	tnid = early_pfn_to_nid(nd_pa >> PAGE_SHIFT);
2438c2ecf20Sopenharmony_ci	if (tnid != nid)
2448c2ecf20Sopenharmony_ci		pr_info("NODE_DATA(%d) on node %d\n", nid, tnid);
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	node_data[nid] = nd;
2478c2ecf20Sopenharmony_ci	memset(NODE_DATA(nid), 0, sizeof(pg_data_t));
2488c2ecf20Sopenharmony_ci	NODE_DATA(nid)->node_id = nid;
2498c2ecf20Sopenharmony_ci	NODE_DATA(nid)->node_start_pfn = start_pfn;
2508c2ecf20Sopenharmony_ci	NODE_DATA(nid)->node_spanned_pages = end_pfn - start_pfn;
2518c2ecf20Sopenharmony_ci}
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci/*
2548c2ecf20Sopenharmony_ci * numa_free_distance
2558c2ecf20Sopenharmony_ci *
2568c2ecf20Sopenharmony_ci * The current table is freed.
2578c2ecf20Sopenharmony_ci */
2588c2ecf20Sopenharmony_civoid __init numa_free_distance(void)
2598c2ecf20Sopenharmony_ci{
2608c2ecf20Sopenharmony_ci	size_t size;
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	if (!numa_distance)
2638c2ecf20Sopenharmony_ci		return;
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	size = numa_distance_cnt * numa_distance_cnt *
2668c2ecf20Sopenharmony_ci		sizeof(numa_distance[0]);
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	memblock_free(__pa(numa_distance), size);
2698c2ecf20Sopenharmony_ci	numa_distance_cnt = 0;
2708c2ecf20Sopenharmony_ci	numa_distance = NULL;
2718c2ecf20Sopenharmony_ci}
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci/*
2748c2ecf20Sopenharmony_ci * Create a new NUMA distance table.
2758c2ecf20Sopenharmony_ci */
2768c2ecf20Sopenharmony_cistatic int __init numa_alloc_distance(void)
2778c2ecf20Sopenharmony_ci{
2788c2ecf20Sopenharmony_ci	size_t size;
2798c2ecf20Sopenharmony_ci	u64 phys;
2808c2ecf20Sopenharmony_ci	int i, j;
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	size = nr_node_ids * nr_node_ids * sizeof(numa_distance[0]);
2838c2ecf20Sopenharmony_ci	phys = memblock_find_in_range(0, PFN_PHYS(max_pfn),
2848c2ecf20Sopenharmony_ci				      size, PAGE_SIZE);
2858c2ecf20Sopenharmony_ci	if (WARN_ON(!phys))
2868c2ecf20Sopenharmony_ci		return -ENOMEM;
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	memblock_reserve(phys, size);
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	numa_distance = __va(phys);
2918c2ecf20Sopenharmony_ci	numa_distance_cnt = nr_node_ids;
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	/* fill with the default distances */
2948c2ecf20Sopenharmony_ci	for (i = 0; i < numa_distance_cnt; i++)
2958c2ecf20Sopenharmony_ci		for (j = 0; j < numa_distance_cnt; j++)
2968c2ecf20Sopenharmony_ci			numa_distance[i * numa_distance_cnt + j] = i == j ?
2978c2ecf20Sopenharmony_ci				LOCAL_DISTANCE : REMOTE_DISTANCE;
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	pr_debug("Initialized distance table, cnt=%d\n", numa_distance_cnt);
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	return 0;
3028c2ecf20Sopenharmony_ci}
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci/**
3058c2ecf20Sopenharmony_ci * numa_set_distance() - Set inter node NUMA distance from node to node.
3068c2ecf20Sopenharmony_ci * @from: the 'from' node to set distance
3078c2ecf20Sopenharmony_ci * @to: the 'to'  node to set distance
3088c2ecf20Sopenharmony_ci * @distance: NUMA distance
3098c2ecf20Sopenharmony_ci *
3108c2ecf20Sopenharmony_ci * Set the distance from node @from to @to to @distance.
3118c2ecf20Sopenharmony_ci * If distance table doesn't exist, a warning is printed.
3128c2ecf20Sopenharmony_ci *
3138c2ecf20Sopenharmony_ci * If @from or @to is higher than the highest known node or lower than zero
3148c2ecf20Sopenharmony_ci * or @distance doesn't make sense, the call is ignored.
3158c2ecf20Sopenharmony_ci */
3168c2ecf20Sopenharmony_civoid __init numa_set_distance(int from, int to, int distance)
3178c2ecf20Sopenharmony_ci{
3188c2ecf20Sopenharmony_ci	if (!numa_distance) {
3198c2ecf20Sopenharmony_ci		pr_warn_once("Warning: distance table not allocated yet\n");
3208c2ecf20Sopenharmony_ci		return;
3218c2ecf20Sopenharmony_ci	}
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	if (from >= numa_distance_cnt || to >= numa_distance_cnt ||
3248c2ecf20Sopenharmony_ci			from < 0 || to < 0) {
3258c2ecf20Sopenharmony_ci		pr_warn_once("Warning: node ids are out of bound, from=%d to=%d distance=%d\n",
3268c2ecf20Sopenharmony_ci			    from, to, distance);
3278c2ecf20Sopenharmony_ci		return;
3288c2ecf20Sopenharmony_ci	}
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	if ((u8)distance != distance ||
3318c2ecf20Sopenharmony_ci	    (from == to && distance != LOCAL_DISTANCE)) {
3328c2ecf20Sopenharmony_ci		pr_warn_once("Warning: invalid distance parameter, from=%d to=%d distance=%d\n",
3338c2ecf20Sopenharmony_ci			     from, to, distance);
3348c2ecf20Sopenharmony_ci		return;
3358c2ecf20Sopenharmony_ci	}
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	numa_distance[from * numa_distance_cnt + to] = distance;
3388c2ecf20Sopenharmony_ci}
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci/*
3418c2ecf20Sopenharmony_ci * Return NUMA distance @from to @to
3428c2ecf20Sopenharmony_ci */
3438c2ecf20Sopenharmony_ciint __node_distance(int from, int to)
3448c2ecf20Sopenharmony_ci{
3458c2ecf20Sopenharmony_ci	if (from >= numa_distance_cnt || to >= numa_distance_cnt)
3468c2ecf20Sopenharmony_ci		return from == to ? LOCAL_DISTANCE : REMOTE_DISTANCE;
3478c2ecf20Sopenharmony_ci	return numa_distance[from * numa_distance_cnt + to];
3488c2ecf20Sopenharmony_ci}
3498c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__node_distance);
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_cistatic int __init numa_register_nodes(void)
3528c2ecf20Sopenharmony_ci{
3538c2ecf20Sopenharmony_ci	int nid;
3548c2ecf20Sopenharmony_ci	struct memblock_region *mblk;
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	/* Check that valid nid is set to memblks */
3578c2ecf20Sopenharmony_ci	for_each_mem_region(mblk) {
3588c2ecf20Sopenharmony_ci		int mblk_nid = memblock_get_region_node(mblk);
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci		if (mblk_nid == NUMA_NO_NODE || mblk_nid >= MAX_NUMNODES) {
3618c2ecf20Sopenharmony_ci			pr_warn("Warning: invalid memblk node %d [mem %#010Lx-%#010Lx]\n",
3628c2ecf20Sopenharmony_ci				mblk_nid, mblk->base,
3638c2ecf20Sopenharmony_ci				mblk->base + mblk->size - 1);
3648c2ecf20Sopenharmony_ci			return -EINVAL;
3658c2ecf20Sopenharmony_ci		}
3668c2ecf20Sopenharmony_ci	}
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	/* Finally register nodes. */
3698c2ecf20Sopenharmony_ci	for_each_node_mask(nid, numa_nodes_parsed) {
3708c2ecf20Sopenharmony_ci		unsigned long start_pfn, end_pfn;
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci		get_pfn_range_for_nid(nid, &start_pfn, &end_pfn);
3738c2ecf20Sopenharmony_ci		setup_node_data(nid, start_pfn, end_pfn);
3748c2ecf20Sopenharmony_ci		node_set_online(nid);
3758c2ecf20Sopenharmony_ci	}
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	/* Setup online nodes to actual nodes*/
3788c2ecf20Sopenharmony_ci	node_possible_map = numa_nodes_parsed;
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	return 0;
3818c2ecf20Sopenharmony_ci}
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_cistatic int __init numa_init(int (*init_func)(void))
3848c2ecf20Sopenharmony_ci{
3858c2ecf20Sopenharmony_ci	int ret;
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci	nodes_clear(numa_nodes_parsed);
3888c2ecf20Sopenharmony_ci	nodes_clear(node_possible_map);
3898c2ecf20Sopenharmony_ci	nodes_clear(node_online_map);
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	ret = numa_alloc_distance();
3928c2ecf20Sopenharmony_ci	if (ret < 0)
3938c2ecf20Sopenharmony_ci		return ret;
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	ret = init_func();
3968c2ecf20Sopenharmony_ci	if (ret < 0)
3978c2ecf20Sopenharmony_ci		goto out_free_distance;
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci	if (nodes_empty(numa_nodes_parsed)) {
4008c2ecf20Sopenharmony_ci		pr_info("No NUMA configuration found\n");
4018c2ecf20Sopenharmony_ci		ret = -EINVAL;
4028c2ecf20Sopenharmony_ci		goto out_free_distance;
4038c2ecf20Sopenharmony_ci	}
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci	ret = numa_register_nodes();
4068c2ecf20Sopenharmony_ci	if (ret < 0)
4078c2ecf20Sopenharmony_ci		goto out_free_distance;
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	setup_node_to_cpumask_map();
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	return 0;
4128c2ecf20Sopenharmony_ciout_free_distance:
4138c2ecf20Sopenharmony_ci	numa_free_distance();
4148c2ecf20Sopenharmony_ci	return ret;
4158c2ecf20Sopenharmony_ci}
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci/**
4188c2ecf20Sopenharmony_ci * dummy_numa_init() - Fallback dummy NUMA init
4198c2ecf20Sopenharmony_ci *
4208c2ecf20Sopenharmony_ci * Used if there's no underlying NUMA architecture, NUMA initialization
4218c2ecf20Sopenharmony_ci * fails, or NUMA is disabled on the command line.
4228c2ecf20Sopenharmony_ci *
4238c2ecf20Sopenharmony_ci * Must online at least one node (node 0) and add memory blocks that cover all
4248c2ecf20Sopenharmony_ci * allowed memory. It is unlikely that this function fails.
4258c2ecf20Sopenharmony_ci *
4268c2ecf20Sopenharmony_ci * Return: 0 on success, -errno on failure.
4278c2ecf20Sopenharmony_ci */
4288c2ecf20Sopenharmony_cistatic int __init dummy_numa_init(void)
4298c2ecf20Sopenharmony_ci{
4308c2ecf20Sopenharmony_ci	phys_addr_t start = memblock_start_of_DRAM();
4318c2ecf20Sopenharmony_ci	phys_addr_t end = memblock_end_of_DRAM();
4328c2ecf20Sopenharmony_ci	int ret;
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci	if (numa_off)
4358c2ecf20Sopenharmony_ci		pr_info("NUMA disabled\n"); /* Forced off on command line. */
4368c2ecf20Sopenharmony_ci	pr_info("Faking a node at [mem %#018Lx-%#018Lx]\n", start, end - 1);
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	ret = numa_add_memblk(0, start, end);
4398c2ecf20Sopenharmony_ci	if (ret) {
4408c2ecf20Sopenharmony_ci		pr_err("NUMA init failed\n");
4418c2ecf20Sopenharmony_ci		return ret;
4428c2ecf20Sopenharmony_ci	}
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	numa_off = true;
4458c2ecf20Sopenharmony_ci	return 0;
4468c2ecf20Sopenharmony_ci}
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci/**
4498c2ecf20Sopenharmony_ci * arm64_numa_init() - Initialize NUMA
4508c2ecf20Sopenharmony_ci *
4518c2ecf20Sopenharmony_ci * Try each configured NUMA initialization method until one succeeds. The
4528c2ecf20Sopenharmony_ci * last fallback is dummy single node config encompassing whole memory.
4538c2ecf20Sopenharmony_ci */
4548c2ecf20Sopenharmony_civoid __init arm64_numa_init(void)
4558c2ecf20Sopenharmony_ci{
4568c2ecf20Sopenharmony_ci	if (!numa_off) {
4578c2ecf20Sopenharmony_ci		if (!acpi_disabled && !numa_init(arm64_acpi_numa_init))
4588c2ecf20Sopenharmony_ci			return;
4598c2ecf20Sopenharmony_ci		if (acpi_disabled && !numa_init(of_numa_init))
4608c2ecf20Sopenharmony_ci			return;
4618c2ecf20Sopenharmony_ci	}
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci	numa_init(dummy_numa_init);
4648c2ecf20Sopenharmony_ci}
465