| /* |
| * NUMA support for s390 |
| * |
| * NUMA emulation (aka fake NUMA) distributes the available memory to nodes |
| * without using real topology information about the physical memory of the |
| * machine. |
| * |
| * It distributes the available CPUs to nodes while respecting the original |
| * machine topology information. This is done by trying to avoid to separate |
| * CPUs which reside on the same book or even on the same MC. |
| * |
| * Because the current Linux scheduler code requires a stable cpu to node |
| * mapping, cores are pinned to nodes when the first CPU thread is set online. |
| * |
| * Copyright IBM Corp. 2015 |
| */ |
| |
| #define KMSG_COMPONENT "numa_emu" |
| #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt |
| |
| #include <linux/kernel.h> |
| #include <linux/cpumask.h> |
| #include <linux/memblock.h> |
| #include <linux/node.h> |
| #include <linux/memory.h> |
| #include <linux/slab.h> |
| #include <asm/smp.h> |
| #include <asm/topology.h> |
| #include "numa_mode.h" |
| #include "toptree.h" |
| |
| /* Distances between the different system components */ |
| #define DIST_EMPTY 0 |
| #define DIST_CORE 1 |
| #define DIST_MC 2 |
| #define DIST_BOOK 3 |
| #define DIST_MAX 4 |
| |
| /* Node distance reported to common code */ |
| #define EMU_NODE_DIST 10 |
| |
| /* Node ID for free (not yet pinned) cores */ |
| #define NODE_ID_FREE -1 |
| |
| /* Different levels of toptree */ |
| enum toptree_level {CORE, MC, BOOK, NODE, TOPOLOGY}; |
| |
| /* The two toptree IDs */ |
| enum {TOPTREE_ID_PHYS, TOPTREE_ID_NUMA}; |
| |
| /* Number of NUMA nodes */ |
| static int emu_nodes = 1; |
| /* NUMA stripe size */ |
| static unsigned long emu_size; |
| |
| /* |
| * Node to core pinning information updates are protected by |
| * "sched_domains_mutex". |
| */ |
| static struct { |
| s32 to_node_id[CONFIG_NR_CPUS]; /* Pinned core to node mapping */ |
| int total; /* Total number of pinned cores */ |
| int per_node_target; /* Cores per node without extra cores */ |
| int per_node[MAX_NUMNODES]; /* Number of cores pinned to node */ |
| } *emu_cores; |
| |
| /* |
| * Pin a core to a node |
| */ |
| static void pin_core_to_node(int core_id, int node_id) |
| { |
| if (emu_cores->to_node_id[core_id] == NODE_ID_FREE) { |
| emu_cores->per_node[node_id]++; |
| emu_cores->to_node_id[core_id] = node_id; |
| emu_cores->total++; |
| } else { |
| WARN_ON(emu_cores->to_node_id[core_id] != node_id); |
| } |
| } |
| |
| /* |
| * Number of pinned cores of a node |
| */ |
| static int cores_pinned(struct toptree *node) |
| { |
| return emu_cores->per_node[node->id]; |
| } |
| |
| /* |
| * ID of the node where the core is pinned (or NODE_ID_FREE) |
| */ |
| static int core_pinned_to_node_id(struct toptree *core) |
| { |
| return emu_cores->to_node_id[core->id]; |
| } |
| |
| /* |
| * Number of cores in the tree that are not yet pinned |
| */ |
| static int cores_free(struct toptree *tree) |
| { |
| struct toptree *core; |
| int count = 0; |
| |
| toptree_for_each(core, tree, CORE) { |
| if (core_pinned_to_node_id(core) == NODE_ID_FREE) |
| count++; |
| } |
| return count; |
| } |
| |
| /* |
| * Return node of core |
| */ |
| static struct toptree *core_node(struct toptree *core) |
| { |
| return core->parent->parent->parent; |
| } |
| |
| /* |
| * Return book of core |
| */ |
| static struct toptree *core_book(struct toptree *core) |
| { |
| return core->parent->parent; |
| } |
| |
| /* |
| * Return mc of core |
| */ |
| static struct toptree *core_mc(struct toptree *core) |
| { |
| return core->parent; |
| } |
| |
| /* |
| * Distance between two cores |
| */ |
| static int dist_core_to_core(struct toptree *core1, struct toptree *core2) |
| { |
| if (core_book(core1)->id != core_book(core2)->id) |
| return DIST_BOOK; |
| if (core_mc(core1)->id != core_mc(core2)->id) |
| return DIST_MC; |
| /* Same core or sibling on same MC */ |
| return DIST_CORE; |
| } |
| |
| /* |
| * Distance of a node to a core |
| */ |
| static int dist_node_to_core(struct toptree *node, struct toptree *core) |
| { |
| struct toptree *core_node; |
| int dist_min = DIST_MAX; |
| |
| toptree_for_each(core_node, node, CORE) |
| dist_min = min(dist_min, dist_core_to_core(core_node, core)); |
| return dist_min == DIST_MAX ? DIST_EMPTY : dist_min; |
| } |
| |
| /* |
| * Unify will delete empty nodes, therefore recreate nodes. |
| */ |
| static void toptree_unify_tree(struct toptree *tree) |
| { |
| int nid; |
| |
| toptree_unify(tree); |
| for (nid = 0; nid < emu_nodes; nid++) |
| toptree_get_child(tree, nid); |
| } |
| |
| /* |
| * Find the best/nearest node for a given core and ensure that no node |
| * gets more than "emu_cores->per_node_target + extra" cores. |
| */ |
| static struct toptree *node_for_core(struct toptree *numa, struct toptree *core, |
| int extra) |
| { |
| struct toptree *node, *node_best = NULL; |
| int dist_cur, dist_best, cores_target; |
| |
| cores_target = emu_cores->per_node_target + extra; |
| dist_best = DIST_MAX; |
| node_best = NULL; |
| toptree_for_each(node, numa, NODE) { |
| /* Already pinned cores must use their nodes */ |
| if (core_pinned_to_node_id(core) == node->id) { |
| node_best = node; |
| break; |
| } |
| /* Skip nodes that already have enough cores */ |
| if (cores_pinned(node) >= cores_target) |
| continue; |
| dist_cur = dist_node_to_core(node, core); |
| if (dist_cur < dist_best) { |
| dist_best = dist_cur; |
| node_best = node; |
| } |
| } |
| return node_best; |
| } |
| |
| /* |
| * Find the best node for each core with respect to "extra" core count |
| */ |
| static void toptree_to_numa_single(struct toptree *numa, struct toptree *phys, |
| int extra) |
| { |
| struct toptree *node, *core, *tmp; |
| |
| toptree_for_each_safe(core, tmp, phys, CORE) { |
| node = node_for_core(numa, core, extra); |
| if (!node) |
| return; |
| toptree_move(core, node); |
| pin_core_to_node(core->id, node->id); |
| } |
| } |
| |
| /* |
| * Move structures of given level to specified NUMA node |
| */ |
| static void move_level_to_numa_node(struct toptree *node, struct toptree *phys, |
| enum toptree_level level, bool perfect) |
| { |
| int cores_free, cores_target = emu_cores->per_node_target; |
| struct toptree *cur, *tmp; |
| |
| toptree_for_each_safe(cur, tmp, phys, level) { |
| cores_free = cores_target - toptree_count(node, CORE); |
| if (perfect) { |
| if (cores_free == toptree_count(cur, CORE)) |
| toptree_move(cur, node); |
| } else { |
| if (cores_free >= toptree_count(cur, CORE)) |
| toptree_move(cur, node); |
| } |
| } |
| } |
| |
| /* |
| * Move structures of a given level to NUMA nodes. If "perfect" is specified |
| * move only perfectly fitting structures. Otherwise move also smaller |
| * than needed structures. |
| */ |
| static void move_level_to_numa(struct toptree *numa, struct toptree *phys, |
| enum toptree_level level, bool perfect) |
| { |
| struct toptree *node; |
| |
| toptree_for_each(node, numa, NODE) |
| move_level_to_numa_node(node, phys, level, perfect); |
| } |
| |
| /* |
| * For the first run try to move the big structures |
| */ |
| static void toptree_to_numa_first(struct toptree *numa, struct toptree *phys) |
| { |
| struct toptree *core; |
| |
| /* Always try to move perfectly fitting structures first */ |
| move_level_to_numa(numa, phys, BOOK, true); |
| move_level_to_numa(numa, phys, BOOK, false); |
| move_level_to_numa(numa, phys, MC, true); |
| move_level_to_numa(numa, phys, MC, false); |
| /* Now pin all the moved cores */ |
| toptree_for_each(core, numa, CORE) |
| pin_core_to_node(core->id, core_node(core)->id); |
| } |
| |
| /* |
| * Allocate new topology and create required nodes |
| */ |
| static struct toptree *toptree_new(int id, int nodes) |
| { |
| struct toptree *tree; |
| int nid; |
| |
| tree = toptree_alloc(TOPOLOGY, id); |
| if (!tree) |
| goto fail; |
| for (nid = 0; nid < nodes; nid++) { |
| if (!toptree_get_child(tree, nid)) |
| goto fail; |
| } |
| return tree; |
| fail: |
| panic("NUMA emulation could not allocate topology"); |
| } |
| |
| /* |
| * Allocate and initialize core to node mapping |
| */ |
| static void create_core_to_node_map(void) |
| { |
| int i; |
| |
| emu_cores = kzalloc(sizeof(*emu_cores), GFP_KERNEL); |
| if (emu_cores == NULL) |
| panic("Could not allocate cores to node memory"); |
| for (i = 0; i < ARRAY_SIZE(emu_cores->to_node_id); i++) |
| emu_cores->to_node_id[i] = NODE_ID_FREE; |
| } |
| |
| /* |
| * Move cores from physical topology into NUMA target topology |
| * and try to keep as much of the physical topology as possible. |
| */ |
| static struct toptree *toptree_to_numa(struct toptree *phys) |
| { |
| static int first = 1; |
| struct toptree *numa; |
| int cores_total; |
| |
| cores_total = emu_cores->total + cores_free(phys); |
| emu_cores->per_node_target = cores_total / emu_nodes; |
| numa = toptree_new(TOPTREE_ID_NUMA, emu_nodes); |
| if (first) { |
| toptree_to_numa_first(numa, phys); |
| first = 0; |
| } |
| toptree_to_numa_single(numa, phys, 0); |
| toptree_to_numa_single(numa, phys, 1); |
| toptree_unify_tree(numa); |
| |
| WARN_ON(cpumask_weight(&phys->mask)); |
| return numa; |
| } |
| |
| /* |
| * Create a toptree out of the physical topology that we got from the hypervisor |
| */ |
| static struct toptree *toptree_from_topology(void) |
| { |
| struct toptree *phys, *node, *book, *mc, *core; |
| struct cpu_topology_s390 *top; |
| int cpu; |
| |
| phys = toptree_new(TOPTREE_ID_PHYS, 1); |
| |
| for_each_online_cpu(cpu) { |
| top = &per_cpu(cpu_topology, cpu); |
| node = toptree_get_child(phys, 0); |
| book = toptree_get_child(node, top->book_id); |
| mc = toptree_get_child(book, top->socket_id); |
| core = toptree_get_child(mc, top->core_id); |
| if (!book || !mc || !core) |
| panic("NUMA emulation could not allocate memory"); |
| cpumask_set_cpu(cpu, &core->mask); |
| toptree_update_mask(mc); |
| } |
| return phys; |
| } |
| |
| /* |
| * Add toptree core to topology and create correct CPU masks |
| */ |
| static void topology_add_core(struct toptree *core) |
| { |
| struct cpu_topology_s390 *top; |
| int cpu; |
| |
| for_each_cpu(cpu, &core->mask) { |
| top = &per_cpu(cpu_topology, cpu); |
| cpumask_copy(&top->thread_mask, &core->mask); |
| cpumask_copy(&top->core_mask, &core_mc(core)->mask); |
| cpumask_copy(&top->book_mask, &core_book(core)->mask); |
| cpumask_set_cpu(cpu, &node_to_cpumask_map[core_node(core)->id]); |
| top->node_id = core_node(core)->id; |
| } |
| } |
| |
| /* |
| * Apply toptree to topology and create CPU masks |
| */ |
| static void toptree_to_topology(struct toptree *numa) |
| { |
| struct toptree *core; |
| int i; |
| |
| /* Clear all node masks */ |
| for (i = 0; i < MAX_NUMNODES; i++) |
| cpumask_clear(&node_to_cpumask_map[i]); |
| |
| /* Rebuild all masks */ |
| toptree_for_each(core, numa, CORE) |
| topology_add_core(core); |
| } |
| |
| /* |
| * Show the node to core mapping |
| */ |
| static void print_node_to_core_map(void) |
| { |
| int nid, cid; |
| |
| if (!numa_debug_enabled) |
| return; |
| printk(KERN_DEBUG "NUMA node to core mapping\n"); |
| for (nid = 0; nid < emu_nodes; nid++) { |
| printk(KERN_DEBUG " node %3d: ", nid); |
| for (cid = 0; cid < ARRAY_SIZE(emu_cores->to_node_id); cid++) { |
| if (emu_cores->to_node_id[cid] == nid) |
| printk(KERN_CONT "%d ", cid); |
| } |
| printk(KERN_CONT "\n"); |
| } |
| } |
| |
| /* |
| * Transfer physical topology into a NUMA topology and modify CPU masks |
| * according to the NUMA topology. |
| * |
| * Must be called with "sched_domains_mutex" lock held. |
| */ |
| static void emu_update_cpu_topology(void) |
| { |
| struct toptree *phys, *numa; |
| |
| if (emu_cores == NULL) |
| create_core_to_node_map(); |
| phys = toptree_from_topology(); |
| numa = toptree_to_numa(phys); |
| toptree_free(phys); |
| toptree_to_topology(numa); |
| toptree_free(numa); |
| print_node_to_core_map(); |
| } |
| |
| /* |
| * If emu_size is not set, use CONFIG_EMU_SIZE. Then round to minimum |
| * alignment (needed for memory hotplug). |
| */ |
| static unsigned long emu_setup_size_adjust(unsigned long size) |
| { |
| unsigned long size_new; |
| |
| size = size ? : CONFIG_EMU_SIZE; |
| size_new = roundup(size, memory_block_size_bytes()); |
| if (size_new == size) |
| return size; |
| pr_warn("Increasing memory stripe size from %ld MB to %ld MB\n", |
| size >> 20, size_new >> 20); |
| return size_new; |
| } |
| |
| /* |
| * If we have not enough memory for the specified nodes, reduce the node count. |
| */ |
| static int emu_setup_nodes_adjust(int nodes) |
| { |
| int nodes_max; |
| |
| nodes_max = memblock.memory.total_size / emu_size; |
| nodes_max = max(nodes_max, 1); |
| if (nodes_max >= nodes) |
| return nodes; |
| pr_warn("Not enough memory for %d nodes, reducing node count\n", nodes); |
| return nodes_max; |
| } |
| |
| /* |
| * Early emu setup |
| */ |
| static void emu_setup(void) |
| { |
| emu_size = emu_setup_size_adjust(emu_size); |
| emu_nodes = emu_setup_nodes_adjust(emu_nodes); |
| pr_info("Creating %d nodes with memory stripe size %ld MB\n", |
| emu_nodes, emu_size >> 20); |
| } |
| |
| /* |
| * Return node id for given page number |
| */ |
| static int emu_pfn_to_nid(unsigned long pfn) |
| { |
| return (pfn / (emu_size >> PAGE_SHIFT)) % emu_nodes; |
| } |
| |
| /* |
| * Return stripe size |
| */ |
| static unsigned long emu_align(void) |
| { |
| return emu_size; |
| } |
| |
| /* |
| * Return distance between two nodes |
| */ |
| static int emu_distance(int node1, int node2) |
| { |
| return (node1 != node2) * EMU_NODE_DIST; |
| } |
| |
| /* |
| * Define callbacks for generic s390 NUMA infrastructure |
| */ |
| const struct numa_mode numa_mode_emu = { |
| .name = "emu", |
| .setup = emu_setup, |
| .update_cpu_topology = emu_update_cpu_topology, |
| .__pfn_to_nid = emu_pfn_to_nid, |
| .align = emu_align, |
| .distance = emu_distance, |
| }; |
| |
| /* |
| * Kernel parameter: emu_nodes=<n> |
| */ |
| static int __init early_parse_emu_nodes(char *p) |
| { |
| int count; |
| |
| if (kstrtoint(p, 0, &count) != 0 || count <= 0) |
| return 0; |
| if (count <= 0) |
| return 0; |
| emu_nodes = min(count, MAX_NUMNODES); |
| return 0; |
| } |
| early_param("emu_nodes", early_parse_emu_nodes); |
| |
| /* |
| * Kernel parameter: emu_size=[<n>[k|M|G|T]] |
| */ |
| static int __init early_parse_emu_size(char *p) |
| { |
| emu_size = memparse(p, NULL); |
| return 0; |
| } |
| early_param("emu_size", early_parse_emu_size); |