// SPDX-License-Identifier: GPL-2.0 /* * NUMA emulation */ #include <linux/kernel.h> #include <linux/errno.h> #include <linux/topology.h> #include <linux/memblock.h> #include <asm/dma.h> #include "numa_internal.h" static int emu_nid_to_phys[MAX_NUMNODES]; static char *emu_cmdline __initdata; int __init numa_emu_cmdline(char *str) { … } static int __init emu_find_memblk_by_nid(int nid, const struct numa_meminfo *mi) { … } static u64 __init mem_hole_size(u64 start, u64 end) { … } /* * Sets up nid to range from @start to @end. The return value is -errno if * something went wrong, 0 otherwise. */ static int __init emu_setup_memblk(struct numa_meminfo *ei, struct numa_meminfo *pi, int nid, int phys_blk, u64 size) { … } /* * Sets up nr_nodes fake nodes interleaved over physical nodes ranging from addr * to max_addr. * * Returns zero on success or negative on error. */ static int __init split_nodes_interleave(struct numa_meminfo *ei, struct numa_meminfo *pi, u64 addr, u64 max_addr, int nr_nodes) { … } /* * Returns the end address of a node so that there is at least `size' amount of * non-reserved memory or `max_addr' is reached. */ static u64 __init find_end_of_node(u64 start, u64 max_addr, u64 size) { … } static u64 uniform_size(u64 max_addr, u64 base, u64 hole, int nr_nodes) { … } /* * Sets up fake nodes of `size' interleaved over physical nodes ranging from * `addr' to `max_addr'. * * Returns zero on success or negative on error. */ static int __init split_nodes_size_interleave_uniform(struct numa_meminfo *ei, struct numa_meminfo *pi, u64 addr, u64 max_addr, u64 size, int nr_nodes, struct numa_memblk *pblk, int nid) { … } static int __init split_nodes_size_interleave(struct numa_meminfo *ei, struct numa_meminfo *pi, u64 addr, u64 max_addr, u64 size) { … } static int __init setup_emu2phys_nid(int *dfl_phys_nid) { … } /** * numa_emulation - Emulate NUMA nodes * @numa_meminfo: NUMA configuration to massage * @numa_dist_cnt: The size of the physical NUMA distance table * * Emulate NUMA nodes according to the numa=fake kernel parameter. * @numa_meminfo contains the physical memory configuration and is modified * to reflect the emulated configuration on success. @numa_dist_cnt is * used to determine the size of the physical distance table. * * On success, the following modifications are made. * * - @numa_meminfo is updated to reflect the emulated nodes. * * - __apicid_to_node[] is updated such that APIC IDs are mapped to the * emulated nodes. * * - NUMA distance table is rebuilt to represent distances between emulated * nodes. The distances are determined considering how emulated nodes * are mapped to physical nodes and match the actual distances. * * - emu_nid_to_phys[] reflects how emulated nodes are mapped to physical * nodes. This is used by numa_add_cpu() and numa_remove_cpu(). * * If emulation is not enabled or fails, emu_nid_to_phys[] is filled with * identity mapping and no other modification is made. */ void __init numa_emulation(struct numa_meminfo *numa_meminfo, int numa_dist_cnt) { … } #ifndef CONFIG_DEBUG_PER_CPU_MAPS void numa_add_cpu(int cpu) { int physnid, nid; nid = early_cpu_to_node(cpu); BUG_ON(nid == NUMA_NO_NODE || !node_online(nid)); physnid = emu_nid_to_phys[nid]; /* * Map the cpu to each emulated node that is allocated on the physical * node of the cpu's apic id. */ for_each_online_node(nid) if (emu_nid_to_phys[nid] == physnid) cpumask_set_cpu(cpu, node_to_cpumask_map[nid]); } void numa_remove_cpu(int cpu) { int i; for_each_online_node(i) cpumask_clear_cpu(cpu, node_to_cpumask_map[i]); } #else /* !CONFIG_DEBUG_PER_CPU_MAPS */ static void numa_set_cpumask(int cpu, bool enable) { … } void numa_add_cpu(int cpu) { … } void numa_remove_cpu(int cpu) { … } #endif /* !CONFIG_DEBUG_PER_CPU_MAPS */