Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v4.7-rc2 396 lines 9.6 kB view raw
1/* 2 * NUMA support, based on the x86 implementation. 3 * 4 * Copyright (C) 2015 Cavium Inc. 5 * Author: Ganapatrao Kulkarni <gkulkarni@cavium.com> 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License version 2 as 9 * published by the Free Software Foundation. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program. If not, see <http://www.gnu.org/licenses/>. 18 */ 19 20#include <linux/bootmem.h> 21#include <linux/memblock.h> 22#include <linux/module.h> 23#include <linux/of.h> 24 25struct pglist_data *node_data[MAX_NUMNODES] __read_mostly; 26EXPORT_SYMBOL(node_data); 27nodemask_t numa_nodes_parsed __initdata; 28static int cpu_to_node_map[NR_CPUS] = { [0 ... NR_CPUS-1] = NUMA_NO_NODE }; 29 30static int numa_distance_cnt; 31static u8 *numa_distance; 32static int numa_off; 33 34static __init int numa_parse_early_param(char *opt) 35{ 36 if (!opt) 37 return -EINVAL; 38 if (!strncmp(opt, "off", 3)) { 39 pr_info("%s\n", "NUMA turned off"); 40 numa_off = 1; 41 } 42 return 0; 43} 44early_param("numa", numa_parse_early_param); 45 46cpumask_var_t node_to_cpumask_map[MAX_NUMNODES]; 47EXPORT_SYMBOL(node_to_cpumask_map); 48 49#ifdef CONFIG_DEBUG_PER_CPU_MAPS 50 51/* 52 * Returns a pointer to the bitmask of CPUs on Node 'node'. 53 */ 54const struct cpumask *cpumask_of_node(int node) 55{ 56 if (WARN_ON(node >= nr_node_ids)) 57 return cpu_none_mask; 58 59 if (WARN_ON(node_to_cpumask_map[node] == NULL)) 60 return cpu_online_mask; 61 62 return node_to_cpumask_map[node]; 63} 64EXPORT_SYMBOL(cpumask_of_node); 65 66#endif 67 68static void map_cpu_to_node(unsigned int cpu, int nid) 69{ 70 set_cpu_numa_node(cpu, nid); 71 if (nid >= 0) 72 cpumask_set_cpu(cpu, node_to_cpumask_map[nid]); 73} 74 75void numa_clear_node(unsigned int cpu) 76{ 77 int nid = cpu_to_node(cpu); 78 79 if (nid >= 0) 80 cpumask_clear_cpu(cpu, node_to_cpumask_map[nid]); 81 set_cpu_numa_node(cpu, NUMA_NO_NODE); 82} 83 84/* 85 * Allocate node_to_cpumask_map based on number of available nodes 86 * Requires node_possible_map to be valid. 87 * 88 * Note: cpumask_of_node() is not valid until after this is done. 89 * (Use CONFIG_DEBUG_PER_CPU_MAPS to check this.) 90 */ 91static void __init setup_node_to_cpumask_map(void) 92{ 93 unsigned int cpu; 94 int node; 95 96 /* setup nr_node_ids if not done yet */ 97 if (nr_node_ids == MAX_NUMNODES) 98 setup_nr_node_ids(); 99 100 /* allocate and clear the mapping */ 101 for (node = 0; node < nr_node_ids; node++) { 102 alloc_bootmem_cpumask_var(&node_to_cpumask_map[node]); 103 cpumask_clear(node_to_cpumask_map[node]); 104 } 105 106 for_each_possible_cpu(cpu) 107 set_cpu_numa_node(cpu, NUMA_NO_NODE); 108 109 /* cpumask_of_node() will now work */ 110 pr_debug("NUMA: Node to cpumask map for %d nodes\n", nr_node_ids); 111} 112 113/* 114 * Set the cpu to node and mem mapping 115 */ 116void numa_store_cpu_info(unsigned int cpu) 117{ 118 map_cpu_to_node(cpu, numa_off ? 0 : cpu_to_node_map[cpu]); 119} 120 121void __init early_map_cpu_to_node(unsigned int cpu, int nid) 122{ 123 /* fallback to node 0 */ 124 if (nid < 0 || nid >= MAX_NUMNODES) 125 nid = 0; 126 127 cpu_to_node_map[cpu] = nid; 128} 129 130/** 131 * numa_add_memblk - Set node id to memblk 132 * @nid: NUMA node ID of the new memblk 133 * @start: Start address of the new memblk 134 * @size: Size of the new memblk 135 * 136 * RETURNS: 137 * 0 on success, -errno on failure. 138 */ 139int __init numa_add_memblk(int nid, u64 start, u64 size) 140{ 141 int ret; 142 143 ret = memblock_set_node(start, size, &memblock.memory, nid); 144 if (ret < 0) { 145 pr_err("NUMA: memblock [0x%llx - 0x%llx] failed to add on node %d\n", 146 start, (start + size - 1), nid); 147 return ret; 148 } 149 150 node_set(nid, numa_nodes_parsed); 151 pr_info("NUMA: Adding memblock [0x%llx - 0x%llx] on node %d\n", 152 start, (start + size - 1), nid); 153 return ret; 154} 155 156/** 157 * Initialize NODE_DATA for a node on the local memory 158 */ 159static void __init setup_node_data(int nid, u64 start_pfn, u64 end_pfn) 160{ 161 const size_t nd_size = roundup(sizeof(pg_data_t), SMP_CACHE_BYTES); 162 u64 nd_pa; 163 void *nd; 164 int tnid; 165 166 pr_info("NUMA: Initmem setup node %d [mem %#010Lx-%#010Lx]\n", 167 nid, start_pfn << PAGE_SHIFT, 168 (end_pfn << PAGE_SHIFT) - 1); 169 170 nd_pa = memblock_alloc_try_nid(nd_size, SMP_CACHE_BYTES, nid); 171 nd = __va(nd_pa); 172 173 /* report and initialize */ 174 pr_info("NUMA: NODE_DATA [mem %#010Lx-%#010Lx]\n", 175 nd_pa, nd_pa + nd_size - 1); 176 tnid = early_pfn_to_nid(nd_pa >> PAGE_SHIFT); 177 if (tnid != nid) 178 pr_info("NUMA: NODE_DATA(%d) on node %d\n", nid, tnid); 179 180 node_data[nid] = nd; 181 memset(NODE_DATA(nid), 0, sizeof(pg_data_t)); 182 NODE_DATA(nid)->node_id = nid; 183 NODE_DATA(nid)->node_start_pfn = start_pfn; 184 NODE_DATA(nid)->node_spanned_pages = end_pfn - start_pfn; 185} 186 187/** 188 * numa_free_distance 189 * 190 * The current table is freed. 191 */ 192void __init numa_free_distance(void) 193{ 194 size_t size; 195 196 if (!numa_distance) 197 return; 198 199 size = numa_distance_cnt * numa_distance_cnt * 200 sizeof(numa_distance[0]); 201 202 memblock_free(__pa(numa_distance), size); 203 numa_distance_cnt = 0; 204 numa_distance = NULL; 205} 206 207/** 208 * 209 * Create a new NUMA distance table. 210 * 211 */ 212static int __init numa_alloc_distance(void) 213{ 214 size_t size; 215 u64 phys; 216 int i, j; 217 218 size = nr_node_ids * nr_node_ids * sizeof(numa_distance[0]); 219 phys = memblock_find_in_range(0, PFN_PHYS(max_pfn), 220 size, PAGE_SIZE); 221 if (WARN_ON(!phys)) 222 return -ENOMEM; 223 224 memblock_reserve(phys, size); 225 226 numa_distance = __va(phys); 227 numa_distance_cnt = nr_node_ids; 228 229 /* fill with the default distances */ 230 for (i = 0; i < numa_distance_cnt; i++) 231 for (j = 0; j < numa_distance_cnt; j++) 232 numa_distance[i * numa_distance_cnt + j] = i == j ? 233 LOCAL_DISTANCE : REMOTE_DISTANCE; 234 235 pr_debug("NUMA: Initialized distance table, cnt=%d\n", 236 numa_distance_cnt); 237 238 return 0; 239} 240 241/** 242 * numa_set_distance - Set inter node NUMA distance from node to node. 243 * @from: the 'from' node to set distance 244 * @to: the 'to' node to set distance 245 * @distance: NUMA distance 246 * 247 * Set the distance from node @from to @to to @distance. 248 * If distance table doesn't exist, a warning is printed. 249 * 250 * If @from or @to is higher than the highest known node or lower than zero 251 * or @distance doesn't make sense, the call is ignored. 252 * 253 */ 254void __init numa_set_distance(int from, int to, int distance) 255{ 256 if (!numa_distance) { 257 pr_warn_once("NUMA: Warning: distance table not allocated yet\n"); 258 return; 259 } 260 261 if (from >= numa_distance_cnt || to >= numa_distance_cnt || 262 from < 0 || to < 0) { 263 pr_warn_once("NUMA: Warning: node ids are out of bound, from=%d to=%d distance=%d\n", 264 from, to, distance); 265 return; 266 } 267 268 if ((u8)distance != distance || 269 (from == to && distance != LOCAL_DISTANCE)) { 270 pr_warn_once("NUMA: Warning: invalid distance parameter, from=%d to=%d distance=%d\n", 271 from, to, distance); 272 return; 273 } 274 275 numa_distance[from * numa_distance_cnt + to] = distance; 276} 277 278/** 279 * Return NUMA distance @from to @to 280 */ 281int __node_distance(int from, int to) 282{ 283 if (from >= numa_distance_cnt || to >= numa_distance_cnt) 284 return from == to ? LOCAL_DISTANCE : REMOTE_DISTANCE; 285 return numa_distance[from * numa_distance_cnt + to]; 286} 287EXPORT_SYMBOL(__node_distance); 288 289static int __init numa_register_nodes(void) 290{ 291 int nid; 292 struct memblock_region *mblk; 293 294 /* Check that valid nid is set to memblks */ 295 for_each_memblock(memory, mblk) 296 if (mblk->nid == NUMA_NO_NODE || mblk->nid >= MAX_NUMNODES) { 297 pr_warn("NUMA: Warning: invalid memblk node %d [mem %#010Lx-%#010Lx]\n", 298 mblk->nid, mblk->base, 299 mblk->base + mblk->size - 1); 300 return -EINVAL; 301 } 302 303 /* Finally register nodes. */ 304 for_each_node_mask(nid, numa_nodes_parsed) { 305 unsigned long start_pfn, end_pfn; 306 307 get_pfn_range_for_nid(nid, &start_pfn, &end_pfn); 308 setup_node_data(nid, start_pfn, end_pfn); 309 node_set_online(nid); 310 } 311 312 /* Setup online nodes to actual nodes*/ 313 node_possible_map = numa_nodes_parsed; 314 315 return 0; 316} 317 318static int __init numa_init(int (*init_func)(void)) 319{ 320 int ret; 321 322 nodes_clear(numa_nodes_parsed); 323 nodes_clear(node_possible_map); 324 nodes_clear(node_online_map); 325 numa_free_distance(); 326 327 ret = numa_alloc_distance(); 328 if (ret < 0) 329 return ret; 330 331 ret = init_func(); 332 if (ret < 0) 333 return ret; 334 335 if (nodes_empty(numa_nodes_parsed)) 336 return -EINVAL; 337 338 ret = numa_register_nodes(); 339 if (ret < 0) 340 return ret; 341 342 setup_node_to_cpumask_map(); 343 344 /* init boot processor */ 345 cpu_to_node_map[0] = 0; 346 map_cpu_to_node(0, 0); 347 348 return 0; 349} 350 351/** 352 * dummy_numa_init - Fallback dummy NUMA init 353 * 354 * Used if there's no underlying NUMA architecture, NUMA initialization 355 * fails, or NUMA is disabled on the command line. 356 * 357 * Must online at least one node (node 0) and add memory blocks that cover all 358 * allowed memory. It is unlikely that this function fails. 359 */ 360static int __init dummy_numa_init(void) 361{ 362 int ret; 363 struct memblock_region *mblk; 364 365 pr_info("%s\n", "No NUMA configuration found"); 366 pr_info("NUMA: Faking a node at [mem %#018Lx-%#018Lx]\n", 367 0LLU, PFN_PHYS(max_pfn) - 1); 368 369 for_each_memblock(memory, mblk) { 370 ret = numa_add_memblk(0, mblk->base, mblk->size); 371 if (!ret) 372 continue; 373 374 pr_err("NUMA init failed\n"); 375 return ret; 376 } 377 378 numa_off = 1; 379 return 0; 380} 381 382/** 383 * arm64_numa_init - Initialize NUMA 384 * 385 * Try each configured NUMA initialization method until one succeeds. The 386 * last fallback is dummy single node config encomapssing whole memory. 387 */ 388void __init arm64_numa_init(void) 389{ 390 if (!numa_off) { 391 if (!numa_init(of_numa_init)) 392 return; 393 } 394 395 numa_init(dummy_numa_init); 396}