1ab1f9dacSPaul Mackerras /* 2ab1f9dacSPaul Mackerras * pSeries NUMA support 3ab1f9dacSPaul Mackerras * 4ab1f9dacSPaul Mackerras * Copyright (C) 2002 Anton Blanchard <anton@au.ibm.com>, IBM 5ab1f9dacSPaul Mackerras * 6ab1f9dacSPaul Mackerras * This program is free software; you can redistribute it and/or 7ab1f9dacSPaul Mackerras * modify it under the terms of the GNU General Public License 8ab1f9dacSPaul Mackerras * as published by the Free Software Foundation; either version 9ab1f9dacSPaul Mackerras * 2 of the License, or (at your option) any later version. 10ab1f9dacSPaul Mackerras */ 11ab1f9dacSPaul Mackerras #include <linux/threads.h> 12ab1f9dacSPaul Mackerras #include <linux/bootmem.h> 13ab1f9dacSPaul Mackerras #include <linux/init.h> 14ab1f9dacSPaul Mackerras #include <linux/mm.h> 15ab1f9dacSPaul Mackerras #include <linux/mmzone.h> 16ab1f9dacSPaul Mackerras #include <linux/module.h> 17ab1f9dacSPaul Mackerras #include <linux/nodemask.h> 18ab1f9dacSPaul Mackerras #include <linux/cpu.h> 19ab1f9dacSPaul Mackerras #include <linux/notifier.h> 2045fb6ceaSAnton Blanchard #include <asm/sparsemem.h> 21ab1f9dacSPaul Mackerras #include <asm/lmb.h> 22cf00a8d1SPaul Mackerras #include <asm/system.h> 232249ca9dSPaul Mackerras #include <asm/smp.h> 24ab1f9dacSPaul Mackerras 25ab1f9dacSPaul Mackerras static int numa_enabled = 1; 26ab1f9dacSPaul Mackerras 27ab1f9dacSPaul Mackerras static int numa_debug; 28ab1f9dacSPaul Mackerras #define dbg(args...) if (numa_debug) { printk(KERN_INFO args); } 29ab1f9dacSPaul Mackerras 3045fb6ceaSAnton Blanchard int numa_cpu_lookup_table[NR_CPUS]; 31ab1f9dacSPaul Mackerras cpumask_t numa_cpumask_lookup_table[MAX_NUMNODES]; 32ab1f9dacSPaul Mackerras struct pglist_data *node_data[MAX_NUMNODES]; 3345fb6ceaSAnton Blanchard 3445fb6ceaSAnton Blanchard EXPORT_SYMBOL(numa_cpu_lookup_table); 3545fb6ceaSAnton Blanchard EXPORT_SYMBOL(numa_cpumask_lookup_table); 3645fb6ceaSAnton Blanchard EXPORT_SYMBOL(node_data); 3745fb6ceaSAnton Blanchard 3845fb6ceaSAnton Blanchard static bootmem_data_t __initdata plat_node_bdata[MAX_NUMNODES]; 39ab1f9dacSPaul Mackerras static int min_common_depth; 40ab1f9dacSPaul Mackerras 41ab1f9dacSPaul Mackerras /* 4245fb6ceaSAnton Blanchard * We need somewhere to store start/end/node for each region until we have 43ab1f9dacSPaul Mackerras * allocated the real node_data structures. 44ab1f9dacSPaul Mackerras */ 4545fb6ceaSAnton Blanchard #define MAX_REGIONS (MAX_LMB_REGIONS*2) 46ab1f9dacSPaul Mackerras static struct { 4745fb6ceaSAnton Blanchard unsigned long start_pfn; 4845fb6ceaSAnton Blanchard unsigned long end_pfn; 4945fb6ceaSAnton Blanchard int nid; 5045fb6ceaSAnton Blanchard } init_node_data[MAX_REGIONS] __initdata; 51ab1f9dacSPaul Mackerras 5245fb6ceaSAnton Blanchard int __init early_pfn_to_nid(unsigned long pfn) 5345fb6ceaSAnton Blanchard { 5445fb6ceaSAnton Blanchard unsigned int i; 5545fb6ceaSAnton Blanchard 5645fb6ceaSAnton Blanchard for (i = 0; init_node_data[i].end_pfn; i++) { 5745fb6ceaSAnton Blanchard unsigned long start_pfn = init_node_data[i].start_pfn; 5845fb6ceaSAnton Blanchard unsigned long end_pfn = init_node_data[i].end_pfn; 5945fb6ceaSAnton Blanchard 6045fb6ceaSAnton Blanchard if ((start_pfn <= pfn) && (pfn < end_pfn)) 6145fb6ceaSAnton Blanchard return init_node_data[i].nid; 6245fb6ceaSAnton Blanchard } 6345fb6ceaSAnton Blanchard 6445fb6ceaSAnton Blanchard return -1; 6545fb6ceaSAnton Blanchard } 6645fb6ceaSAnton Blanchard 6745fb6ceaSAnton Blanchard void __init add_region(unsigned int nid, unsigned long start_pfn, 6845fb6ceaSAnton Blanchard unsigned long pages) 6945fb6ceaSAnton Blanchard { 7045fb6ceaSAnton Blanchard unsigned int i; 7145fb6ceaSAnton Blanchard 7245fb6ceaSAnton Blanchard dbg("add_region nid %d start_pfn 0x%lx pages 0x%lx\n", 7345fb6ceaSAnton Blanchard nid, start_pfn, pages); 7445fb6ceaSAnton Blanchard 7545fb6ceaSAnton Blanchard for (i = 0; init_node_data[i].end_pfn; i++) { 7645fb6ceaSAnton Blanchard if (init_node_data[i].nid != nid) 7745fb6ceaSAnton Blanchard continue; 7845fb6ceaSAnton Blanchard if (init_node_data[i].end_pfn == start_pfn) { 7945fb6ceaSAnton Blanchard init_node_data[i].end_pfn += pages; 8045fb6ceaSAnton Blanchard return; 8145fb6ceaSAnton Blanchard } 8245fb6ceaSAnton Blanchard if (init_node_data[i].start_pfn == (start_pfn + pages)) { 8345fb6ceaSAnton Blanchard init_node_data[i].start_pfn -= pages; 8445fb6ceaSAnton Blanchard return; 8545fb6ceaSAnton Blanchard } 8645fb6ceaSAnton Blanchard } 8745fb6ceaSAnton Blanchard 8845fb6ceaSAnton Blanchard /* 8945fb6ceaSAnton Blanchard * Leave last entry NULL so we dont iterate off the end (we use 9045fb6ceaSAnton Blanchard * entry.end_pfn to terminate the walk). 9145fb6ceaSAnton Blanchard */ 9245fb6ceaSAnton Blanchard if (i >= (MAX_REGIONS - 1)) { 9345fb6ceaSAnton Blanchard printk(KERN_ERR "WARNING: too many memory regions in " 9445fb6ceaSAnton Blanchard "numa code, truncating\n"); 9545fb6ceaSAnton Blanchard return; 9645fb6ceaSAnton Blanchard } 9745fb6ceaSAnton Blanchard 9845fb6ceaSAnton Blanchard init_node_data[i].start_pfn = start_pfn; 9945fb6ceaSAnton Blanchard init_node_data[i].end_pfn = start_pfn + pages; 10045fb6ceaSAnton Blanchard init_node_data[i].nid = nid; 10145fb6ceaSAnton Blanchard } 10245fb6ceaSAnton Blanchard 10345fb6ceaSAnton Blanchard /* We assume init_node_data has no overlapping regions */ 10445fb6ceaSAnton Blanchard void __init get_region(unsigned int nid, unsigned long *start_pfn, 10545fb6ceaSAnton Blanchard unsigned long *end_pfn, unsigned long *pages_present) 10645fb6ceaSAnton Blanchard { 10745fb6ceaSAnton Blanchard unsigned int i; 10845fb6ceaSAnton Blanchard 10945fb6ceaSAnton Blanchard *start_pfn = -1UL; 11045fb6ceaSAnton Blanchard *end_pfn = *pages_present = 0; 11145fb6ceaSAnton Blanchard 11245fb6ceaSAnton Blanchard for (i = 0; init_node_data[i].end_pfn; i++) { 11345fb6ceaSAnton Blanchard if (init_node_data[i].nid != nid) 11445fb6ceaSAnton Blanchard continue; 11545fb6ceaSAnton Blanchard 11645fb6ceaSAnton Blanchard *pages_present += init_node_data[i].end_pfn - 11745fb6ceaSAnton Blanchard init_node_data[i].start_pfn; 11845fb6ceaSAnton Blanchard 11945fb6ceaSAnton Blanchard if (init_node_data[i].start_pfn < *start_pfn) 12045fb6ceaSAnton Blanchard *start_pfn = init_node_data[i].start_pfn; 12145fb6ceaSAnton Blanchard 12245fb6ceaSAnton Blanchard if (init_node_data[i].end_pfn > *end_pfn) 12345fb6ceaSAnton Blanchard *end_pfn = init_node_data[i].end_pfn; 12445fb6ceaSAnton Blanchard } 12545fb6ceaSAnton Blanchard 12645fb6ceaSAnton Blanchard /* We didnt find a matching region, return start/end as 0 */ 12745fb6ceaSAnton Blanchard if (*start_pfn == -1UL) 12845fb6ceaSAnton Blanchard start_pfn = 0; 12945fb6ceaSAnton Blanchard } 130ab1f9dacSPaul Mackerras 131ab1f9dacSPaul Mackerras static inline void map_cpu_to_node(int cpu, int node) 132ab1f9dacSPaul Mackerras { 133ab1f9dacSPaul Mackerras numa_cpu_lookup_table[cpu] = node; 13445fb6ceaSAnton Blanchard 13545fb6ceaSAnton Blanchard if (!(cpu_isset(cpu, numa_cpumask_lookup_table[node]))) 136ab1f9dacSPaul Mackerras cpu_set(cpu, numa_cpumask_lookup_table[node]); 137ab1f9dacSPaul Mackerras } 138ab1f9dacSPaul Mackerras 139ab1f9dacSPaul Mackerras #ifdef CONFIG_HOTPLUG_CPU 140ab1f9dacSPaul Mackerras static void unmap_cpu_from_node(unsigned long cpu) 141ab1f9dacSPaul Mackerras { 142ab1f9dacSPaul Mackerras int node = numa_cpu_lookup_table[cpu]; 143ab1f9dacSPaul Mackerras 144ab1f9dacSPaul Mackerras dbg("removing cpu %lu from node %d\n", cpu, node); 145ab1f9dacSPaul Mackerras 146ab1f9dacSPaul Mackerras if (cpu_isset(cpu, numa_cpumask_lookup_table[node])) { 147ab1f9dacSPaul Mackerras cpu_clear(cpu, numa_cpumask_lookup_table[node]); 148ab1f9dacSPaul Mackerras } else { 149ab1f9dacSPaul Mackerras printk(KERN_ERR "WARNING: cpu %lu not found in node %d\n", 150ab1f9dacSPaul Mackerras cpu, node); 151ab1f9dacSPaul Mackerras } 152ab1f9dacSPaul Mackerras } 153ab1f9dacSPaul Mackerras #endif /* CONFIG_HOTPLUG_CPU */ 154ab1f9dacSPaul Mackerras 15545fb6ceaSAnton Blanchard static struct device_node *find_cpu_node(unsigned int cpu) 156ab1f9dacSPaul Mackerras { 157ab1f9dacSPaul Mackerras unsigned int hw_cpuid = get_hard_smp_processor_id(cpu); 158ab1f9dacSPaul Mackerras struct device_node *cpu_node = NULL; 159ab1f9dacSPaul Mackerras unsigned int *interrupt_server, *reg; 160ab1f9dacSPaul Mackerras int len; 161ab1f9dacSPaul Mackerras 162ab1f9dacSPaul Mackerras while ((cpu_node = of_find_node_by_type(cpu_node, "cpu")) != NULL) { 163ab1f9dacSPaul Mackerras /* Try interrupt server first */ 164ab1f9dacSPaul Mackerras interrupt_server = (unsigned int *)get_property(cpu_node, 165ab1f9dacSPaul Mackerras "ibm,ppc-interrupt-server#s", &len); 166ab1f9dacSPaul Mackerras 167ab1f9dacSPaul Mackerras len = len / sizeof(u32); 168ab1f9dacSPaul Mackerras 169ab1f9dacSPaul Mackerras if (interrupt_server && (len > 0)) { 170ab1f9dacSPaul Mackerras while (len--) { 171ab1f9dacSPaul Mackerras if (interrupt_server[len] == hw_cpuid) 172ab1f9dacSPaul Mackerras return cpu_node; 173ab1f9dacSPaul Mackerras } 174ab1f9dacSPaul Mackerras } else { 175ab1f9dacSPaul Mackerras reg = (unsigned int *)get_property(cpu_node, 176ab1f9dacSPaul Mackerras "reg", &len); 177ab1f9dacSPaul Mackerras if (reg && (len > 0) && (reg[0] == hw_cpuid)) 178ab1f9dacSPaul Mackerras return cpu_node; 179ab1f9dacSPaul Mackerras } 180ab1f9dacSPaul Mackerras } 181ab1f9dacSPaul Mackerras 182ab1f9dacSPaul Mackerras return NULL; 183ab1f9dacSPaul Mackerras } 184ab1f9dacSPaul Mackerras 185ab1f9dacSPaul Mackerras /* must hold reference to node during call */ 186ab1f9dacSPaul Mackerras static int *of_get_associativity(struct device_node *dev) 187ab1f9dacSPaul Mackerras { 188ab1f9dacSPaul Mackerras return (unsigned int *)get_property(dev, "ibm,associativity", NULL); 189ab1f9dacSPaul Mackerras } 190ab1f9dacSPaul Mackerras 191ab1f9dacSPaul Mackerras static int of_node_numa_domain(struct device_node *device) 192ab1f9dacSPaul Mackerras { 193ab1f9dacSPaul Mackerras int numa_domain; 194ab1f9dacSPaul Mackerras unsigned int *tmp; 195ab1f9dacSPaul Mackerras 196ab1f9dacSPaul Mackerras if (min_common_depth == -1) 197ab1f9dacSPaul Mackerras return 0; 198ab1f9dacSPaul Mackerras 199ab1f9dacSPaul Mackerras tmp = of_get_associativity(device); 200ab1f9dacSPaul Mackerras if (tmp && (tmp[0] >= min_common_depth)) { 201ab1f9dacSPaul Mackerras numa_domain = tmp[min_common_depth]; 202ab1f9dacSPaul Mackerras } else { 203ab1f9dacSPaul Mackerras dbg("WARNING: no NUMA information for %s\n", 204ab1f9dacSPaul Mackerras device->full_name); 205ab1f9dacSPaul Mackerras numa_domain = 0; 206ab1f9dacSPaul Mackerras } 207ab1f9dacSPaul Mackerras return numa_domain; 208ab1f9dacSPaul Mackerras } 209ab1f9dacSPaul Mackerras 210ab1f9dacSPaul Mackerras /* 211ab1f9dacSPaul Mackerras * In theory, the "ibm,associativity" property may contain multiple 212ab1f9dacSPaul Mackerras * associativity lists because a resource may be multiply connected 213ab1f9dacSPaul Mackerras * into the machine. This resource then has different associativity 214ab1f9dacSPaul Mackerras * characteristics relative to its multiple connections. We ignore 215ab1f9dacSPaul Mackerras * this for now. We also assume that all cpu and memory sets have 216ab1f9dacSPaul Mackerras * their distances represented at a common level. This won't be 217ab1f9dacSPaul Mackerras * true for heirarchical NUMA. 218ab1f9dacSPaul Mackerras * 219ab1f9dacSPaul Mackerras * In any case the ibm,associativity-reference-points should give 220ab1f9dacSPaul Mackerras * the correct depth for a normal NUMA system. 221ab1f9dacSPaul Mackerras * 222ab1f9dacSPaul Mackerras * - Dave Hansen <haveblue@us.ibm.com> 223ab1f9dacSPaul Mackerras */ 224ab1f9dacSPaul Mackerras static int __init find_min_common_depth(void) 225ab1f9dacSPaul Mackerras { 226ab1f9dacSPaul Mackerras int depth; 227ab1f9dacSPaul Mackerras unsigned int *ref_points; 228ab1f9dacSPaul Mackerras struct device_node *rtas_root; 229ab1f9dacSPaul Mackerras unsigned int len; 230ab1f9dacSPaul Mackerras 231ab1f9dacSPaul Mackerras rtas_root = of_find_node_by_path("/rtas"); 232ab1f9dacSPaul Mackerras 233ab1f9dacSPaul Mackerras if (!rtas_root) 234ab1f9dacSPaul Mackerras return -1; 235ab1f9dacSPaul Mackerras 236ab1f9dacSPaul Mackerras /* 237ab1f9dacSPaul Mackerras * this property is 2 32-bit integers, each representing a level of 238ab1f9dacSPaul Mackerras * depth in the associativity nodes. The first is for an SMP 239ab1f9dacSPaul Mackerras * configuration (should be all 0's) and the second is for a normal 240ab1f9dacSPaul Mackerras * NUMA configuration. 241ab1f9dacSPaul Mackerras */ 242ab1f9dacSPaul Mackerras ref_points = (unsigned int *)get_property(rtas_root, 243ab1f9dacSPaul Mackerras "ibm,associativity-reference-points", &len); 244ab1f9dacSPaul Mackerras 245ab1f9dacSPaul Mackerras if ((len >= 1) && ref_points) { 246ab1f9dacSPaul Mackerras depth = ref_points[1]; 247ab1f9dacSPaul Mackerras } else { 248ab1f9dacSPaul Mackerras dbg("WARNING: could not find NUMA " 249ab1f9dacSPaul Mackerras "associativity reference point\n"); 250ab1f9dacSPaul Mackerras depth = -1; 251ab1f9dacSPaul Mackerras } 252ab1f9dacSPaul Mackerras of_node_put(rtas_root); 253ab1f9dacSPaul Mackerras 254ab1f9dacSPaul Mackerras return depth; 255ab1f9dacSPaul Mackerras } 256ab1f9dacSPaul Mackerras 257ab1f9dacSPaul Mackerras static int __init get_mem_addr_cells(void) 258ab1f9dacSPaul Mackerras { 259ab1f9dacSPaul Mackerras struct device_node *memory = NULL; 260ab1f9dacSPaul Mackerras int rc; 261ab1f9dacSPaul Mackerras 262ab1f9dacSPaul Mackerras memory = of_find_node_by_type(memory, "memory"); 263ab1f9dacSPaul Mackerras if (!memory) 264ab1f9dacSPaul Mackerras return 0; /* it won't matter */ 265ab1f9dacSPaul Mackerras 266ab1f9dacSPaul Mackerras rc = prom_n_addr_cells(memory); 267ab1f9dacSPaul Mackerras return rc; 268ab1f9dacSPaul Mackerras } 269ab1f9dacSPaul Mackerras 270ab1f9dacSPaul Mackerras static int __init get_mem_size_cells(void) 271ab1f9dacSPaul Mackerras { 272ab1f9dacSPaul Mackerras struct device_node *memory = NULL; 273ab1f9dacSPaul Mackerras int rc; 274ab1f9dacSPaul Mackerras 275ab1f9dacSPaul Mackerras memory = of_find_node_by_type(memory, "memory"); 276ab1f9dacSPaul Mackerras if (!memory) 277ab1f9dacSPaul Mackerras return 0; /* it won't matter */ 278ab1f9dacSPaul Mackerras rc = prom_n_size_cells(memory); 279ab1f9dacSPaul Mackerras return rc; 280ab1f9dacSPaul Mackerras } 281ab1f9dacSPaul Mackerras 28245fb6ceaSAnton Blanchard static unsigned long __init read_n_cells(int n, unsigned int **buf) 283ab1f9dacSPaul Mackerras { 284ab1f9dacSPaul Mackerras unsigned long result = 0; 285ab1f9dacSPaul Mackerras 286ab1f9dacSPaul Mackerras while (n--) { 287ab1f9dacSPaul Mackerras result = (result << 32) | **buf; 288ab1f9dacSPaul Mackerras (*buf)++; 289ab1f9dacSPaul Mackerras } 290ab1f9dacSPaul Mackerras return result; 291ab1f9dacSPaul Mackerras } 292ab1f9dacSPaul Mackerras 293ab1f9dacSPaul Mackerras /* 294ab1f9dacSPaul Mackerras * Figure out to which domain a cpu belongs and stick it there. 295ab1f9dacSPaul Mackerras * Return the id of the domain used. 296ab1f9dacSPaul Mackerras */ 297ab1f9dacSPaul Mackerras static int numa_setup_cpu(unsigned long lcpu) 298ab1f9dacSPaul Mackerras { 299ab1f9dacSPaul Mackerras int numa_domain = 0; 300ab1f9dacSPaul Mackerras struct device_node *cpu = find_cpu_node(lcpu); 301ab1f9dacSPaul Mackerras 302ab1f9dacSPaul Mackerras if (!cpu) { 303ab1f9dacSPaul Mackerras WARN_ON(1); 304ab1f9dacSPaul Mackerras goto out; 305ab1f9dacSPaul Mackerras } 306ab1f9dacSPaul Mackerras 307ab1f9dacSPaul Mackerras numa_domain = of_node_numa_domain(cpu); 308ab1f9dacSPaul Mackerras 309ab1f9dacSPaul Mackerras if (numa_domain >= num_online_nodes()) { 310ab1f9dacSPaul Mackerras /* 311ab1f9dacSPaul Mackerras * POWER4 LPAR uses 0xffff as invalid node, 312ab1f9dacSPaul Mackerras * dont warn in this case. 313ab1f9dacSPaul Mackerras */ 314ab1f9dacSPaul Mackerras if (numa_domain != 0xffff) 315ab1f9dacSPaul Mackerras printk(KERN_ERR "WARNING: cpu %ld " 316ab1f9dacSPaul Mackerras "maps to invalid NUMA node %d\n", 317ab1f9dacSPaul Mackerras lcpu, numa_domain); 318ab1f9dacSPaul Mackerras numa_domain = 0; 319ab1f9dacSPaul Mackerras } 320ab1f9dacSPaul Mackerras out: 321ab1f9dacSPaul Mackerras node_set_online(numa_domain); 322ab1f9dacSPaul Mackerras 323ab1f9dacSPaul Mackerras map_cpu_to_node(lcpu, numa_domain); 324ab1f9dacSPaul Mackerras 325ab1f9dacSPaul Mackerras of_node_put(cpu); 326ab1f9dacSPaul Mackerras 327ab1f9dacSPaul Mackerras return numa_domain; 328ab1f9dacSPaul Mackerras } 329ab1f9dacSPaul Mackerras 330ab1f9dacSPaul Mackerras static int cpu_numa_callback(struct notifier_block *nfb, 331ab1f9dacSPaul Mackerras unsigned long action, 332ab1f9dacSPaul Mackerras void *hcpu) 333ab1f9dacSPaul Mackerras { 334ab1f9dacSPaul Mackerras unsigned long lcpu = (unsigned long)hcpu; 335ab1f9dacSPaul Mackerras int ret = NOTIFY_DONE; 336ab1f9dacSPaul Mackerras 337ab1f9dacSPaul Mackerras switch (action) { 338ab1f9dacSPaul Mackerras case CPU_UP_PREPARE: 339ab1f9dacSPaul Mackerras if (min_common_depth == -1 || !numa_enabled) 340ab1f9dacSPaul Mackerras map_cpu_to_node(lcpu, 0); 341ab1f9dacSPaul Mackerras else 342ab1f9dacSPaul Mackerras numa_setup_cpu(lcpu); 343ab1f9dacSPaul Mackerras ret = NOTIFY_OK; 344ab1f9dacSPaul Mackerras break; 345ab1f9dacSPaul Mackerras #ifdef CONFIG_HOTPLUG_CPU 346ab1f9dacSPaul Mackerras case CPU_DEAD: 347ab1f9dacSPaul Mackerras case CPU_UP_CANCELED: 348ab1f9dacSPaul Mackerras unmap_cpu_from_node(lcpu); 349ab1f9dacSPaul Mackerras break; 350ab1f9dacSPaul Mackerras ret = NOTIFY_OK; 351ab1f9dacSPaul Mackerras #endif 352ab1f9dacSPaul Mackerras } 353ab1f9dacSPaul Mackerras return ret; 354ab1f9dacSPaul Mackerras } 355ab1f9dacSPaul Mackerras 356ab1f9dacSPaul Mackerras /* 357ab1f9dacSPaul Mackerras * Check and possibly modify a memory region to enforce the memory limit. 358ab1f9dacSPaul Mackerras * 359ab1f9dacSPaul Mackerras * Returns the size the region should have to enforce the memory limit. 360ab1f9dacSPaul Mackerras * This will either be the original value of size, a truncated value, 361ab1f9dacSPaul Mackerras * or zero. If the returned value of size is 0 the region should be 362ab1f9dacSPaul Mackerras * discarded as it lies wholy above the memory limit. 363ab1f9dacSPaul Mackerras */ 36445fb6ceaSAnton Blanchard static unsigned long __init numa_enforce_memory_limit(unsigned long start, 36545fb6ceaSAnton Blanchard unsigned long size) 366ab1f9dacSPaul Mackerras { 367ab1f9dacSPaul Mackerras /* 368ab1f9dacSPaul Mackerras * We use lmb_end_of_DRAM() in here instead of memory_limit because 369ab1f9dacSPaul Mackerras * we've already adjusted it for the limit and it takes care of 370ab1f9dacSPaul Mackerras * having memory holes below the limit. 371ab1f9dacSPaul Mackerras */ 372ab1f9dacSPaul Mackerras 373ab1f9dacSPaul Mackerras if (! memory_limit) 374ab1f9dacSPaul Mackerras return size; 375ab1f9dacSPaul Mackerras 376ab1f9dacSPaul Mackerras if (start + size <= lmb_end_of_DRAM()) 377ab1f9dacSPaul Mackerras return size; 378ab1f9dacSPaul Mackerras 379ab1f9dacSPaul Mackerras if (start >= lmb_end_of_DRAM()) 380ab1f9dacSPaul Mackerras return 0; 381ab1f9dacSPaul Mackerras 382ab1f9dacSPaul Mackerras return lmb_end_of_DRAM() - start; 383ab1f9dacSPaul Mackerras } 384ab1f9dacSPaul Mackerras 385ab1f9dacSPaul Mackerras static int __init parse_numa_properties(void) 386ab1f9dacSPaul Mackerras { 387ab1f9dacSPaul Mackerras struct device_node *cpu = NULL; 388ab1f9dacSPaul Mackerras struct device_node *memory = NULL; 389ab1f9dacSPaul Mackerras int addr_cells, size_cells; 39045fb6ceaSAnton Blanchard int max_domain; 391ab1f9dacSPaul Mackerras unsigned long i; 392ab1f9dacSPaul Mackerras 393ab1f9dacSPaul Mackerras if (numa_enabled == 0) { 394ab1f9dacSPaul Mackerras printk(KERN_WARNING "NUMA disabled by user\n"); 395ab1f9dacSPaul Mackerras return -1; 396ab1f9dacSPaul Mackerras } 397ab1f9dacSPaul Mackerras 398ab1f9dacSPaul Mackerras min_common_depth = find_min_common_depth(); 399ab1f9dacSPaul Mackerras 400ab1f9dacSPaul Mackerras dbg("NUMA associativity depth for CPU/Memory: %d\n", min_common_depth); 401ab1f9dacSPaul Mackerras if (min_common_depth < 0) 402ab1f9dacSPaul Mackerras return min_common_depth; 403ab1f9dacSPaul Mackerras 404ab1f9dacSPaul Mackerras max_domain = numa_setup_cpu(boot_cpuid); 405ab1f9dacSPaul Mackerras 406ab1f9dacSPaul Mackerras /* 407ab1f9dacSPaul Mackerras * Even though we connect cpus to numa domains later in SMP init, 408ab1f9dacSPaul Mackerras * we need to know the maximum node id now. This is because each 409ab1f9dacSPaul Mackerras * node id must have NODE_DATA etc backing it. 410ab1f9dacSPaul Mackerras * As a result of hotplug we could still have cpus appear later on 411ab1f9dacSPaul Mackerras * with larger node ids. In that case we force the cpu into node 0. 412ab1f9dacSPaul Mackerras */ 413ab1f9dacSPaul Mackerras for_each_cpu(i) { 414ab1f9dacSPaul Mackerras int numa_domain; 415ab1f9dacSPaul Mackerras 416ab1f9dacSPaul Mackerras cpu = find_cpu_node(i); 417ab1f9dacSPaul Mackerras 418ab1f9dacSPaul Mackerras if (cpu) { 419ab1f9dacSPaul Mackerras numa_domain = of_node_numa_domain(cpu); 420ab1f9dacSPaul Mackerras of_node_put(cpu); 421ab1f9dacSPaul Mackerras 422ab1f9dacSPaul Mackerras if (numa_domain < MAX_NUMNODES && 423ab1f9dacSPaul Mackerras max_domain < numa_domain) 424ab1f9dacSPaul Mackerras max_domain = numa_domain; 425ab1f9dacSPaul Mackerras } 426ab1f9dacSPaul Mackerras } 427ab1f9dacSPaul Mackerras 428ab1f9dacSPaul Mackerras addr_cells = get_mem_addr_cells(); 429ab1f9dacSPaul Mackerras size_cells = get_mem_size_cells(); 430ab1f9dacSPaul Mackerras memory = NULL; 431ab1f9dacSPaul Mackerras while ((memory = of_find_node_by_type(memory, "memory")) != NULL) { 432ab1f9dacSPaul Mackerras unsigned long start; 433ab1f9dacSPaul Mackerras unsigned long size; 434ab1f9dacSPaul Mackerras int numa_domain; 435ab1f9dacSPaul Mackerras int ranges; 436ab1f9dacSPaul Mackerras unsigned int *memcell_buf; 437ab1f9dacSPaul Mackerras unsigned int len; 438ab1f9dacSPaul Mackerras 439ab1f9dacSPaul Mackerras memcell_buf = (unsigned int *)get_property(memory, "reg", &len); 440ab1f9dacSPaul Mackerras if (!memcell_buf || len <= 0) 441ab1f9dacSPaul Mackerras continue; 442ab1f9dacSPaul Mackerras 443ab1f9dacSPaul Mackerras ranges = memory->n_addrs; 444ab1f9dacSPaul Mackerras new_range: 445ab1f9dacSPaul Mackerras /* these are order-sensitive, and modify the buffer pointer */ 446ab1f9dacSPaul Mackerras start = read_n_cells(addr_cells, &memcell_buf); 447ab1f9dacSPaul Mackerras size = read_n_cells(size_cells, &memcell_buf); 448ab1f9dacSPaul Mackerras 449ab1f9dacSPaul Mackerras numa_domain = of_node_numa_domain(memory); 450ab1f9dacSPaul Mackerras 451ab1f9dacSPaul Mackerras if (numa_domain >= MAX_NUMNODES) { 452ab1f9dacSPaul Mackerras if (numa_domain != 0xffff) 453ab1f9dacSPaul Mackerras printk(KERN_ERR "WARNING: memory at %lx maps " 454ab1f9dacSPaul Mackerras "to invalid NUMA node %d\n", start, 455ab1f9dacSPaul Mackerras numa_domain); 456ab1f9dacSPaul Mackerras numa_domain = 0; 457ab1f9dacSPaul Mackerras } 458ab1f9dacSPaul Mackerras 459ab1f9dacSPaul Mackerras if (max_domain < numa_domain) 460ab1f9dacSPaul Mackerras max_domain = numa_domain; 461ab1f9dacSPaul Mackerras 462ab1f9dacSPaul Mackerras if (!(size = numa_enforce_memory_limit(start, size))) { 463ab1f9dacSPaul Mackerras if (--ranges) 464ab1f9dacSPaul Mackerras goto new_range; 465ab1f9dacSPaul Mackerras else 466ab1f9dacSPaul Mackerras continue; 467ab1f9dacSPaul Mackerras } 468ab1f9dacSPaul Mackerras 46945fb6ceaSAnton Blanchard add_region(numa_domain, start >> PAGE_SHIFT, 47045fb6ceaSAnton Blanchard size >> PAGE_SHIFT); 471ab1f9dacSPaul Mackerras 472ab1f9dacSPaul Mackerras if (--ranges) 473ab1f9dacSPaul Mackerras goto new_range; 474ab1f9dacSPaul Mackerras } 475ab1f9dacSPaul Mackerras 476ab1f9dacSPaul Mackerras for (i = 0; i <= max_domain; i++) 477ab1f9dacSPaul Mackerras node_set_online(i); 478ab1f9dacSPaul Mackerras 479ab1f9dacSPaul Mackerras return 0; 480ab1f9dacSPaul Mackerras } 481ab1f9dacSPaul Mackerras 482ab1f9dacSPaul Mackerras static void __init setup_nonnuma(void) 483ab1f9dacSPaul Mackerras { 484ab1f9dacSPaul Mackerras unsigned long top_of_ram = lmb_end_of_DRAM(); 485ab1f9dacSPaul Mackerras unsigned long total_ram = lmb_phys_mem_size(); 486*fb6d73d3SPaul Mackerras unsigned int i; 487ab1f9dacSPaul Mackerras 488ab1f9dacSPaul Mackerras printk(KERN_INFO "Top of RAM: 0x%lx, Total RAM: 0x%lx\n", 489ab1f9dacSPaul Mackerras top_of_ram, total_ram); 490ab1f9dacSPaul Mackerras printk(KERN_INFO "Memory hole size: %ldMB\n", 491ab1f9dacSPaul Mackerras (top_of_ram - total_ram) >> 20); 492ab1f9dacSPaul Mackerras 493ab1f9dacSPaul Mackerras map_cpu_to_node(boot_cpuid, 0); 494*fb6d73d3SPaul Mackerras for (i = 0; i < lmb.memory.cnt; ++i) 495*fb6d73d3SPaul Mackerras add_region(0, lmb.memory.region[i].base >> PAGE_SHIFT, 496*fb6d73d3SPaul Mackerras lmb_size_pages(&lmb.memory, i)); 497ab1f9dacSPaul Mackerras node_set_online(0); 498ab1f9dacSPaul Mackerras } 499ab1f9dacSPaul Mackerras 500ab1f9dacSPaul Mackerras static void __init dump_numa_topology(void) 501ab1f9dacSPaul Mackerras { 502ab1f9dacSPaul Mackerras unsigned int node; 503ab1f9dacSPaul Mackerras unsigned int count; 504ab1f9dacSPaul Mackerras 505ab1f9dacSPaul Mackerras if (min_common_depth == -1 || !numa_enabled) 506ab1f9dacSPaul Mackerras return; 507ab1f9dacSPaul Mackerras 508ab1f9dacSPaul Mackerras for_each_online_node(node) { 509ab1f9dacSPaul Mackerras unsigned long i; 510ab1f9dacSPaul Mackerras 511ab1f9dacSPaul Mackerras printk(KERN_INFO "Node %d Memory:", node); 512ab1f9dacSPaul Mackerras 513ab1f9dacSPaul Mackerras count = 0; 514ab1f9dacSPaul Mackerras 51545fb6ceaSAnton Blanchard for (i = 0; i < lmb_end_of_DRAM(); 51645fb6ceaSAnton Blanchard i += (1 << SECTION_SIZE_BITS)) { 51745fb6ceaSAnton Blanchard if (early_pfn_to_nid(i >> PAGE_SHIFT) == node) { 518ab1f9dacSPaul Mackerras if (count == 0) 519ab1f9dacSPaul Mackerras printk(" 0x%lx", i); 520ab1f9dacSPaul Mackerras ++count; 521ab1f9dacSPaul Mackerras } else { 522ab1f9dacSPaul Mackerras if (count > 0) 523ab1f9dacSPaul Mackerras printk("-0x%lx", i); 524ab1f9dacSPaul Mackerras count = 0; 525ab1f9dacSPaul Mackerras } 526ab1f9dacSPaul Mackerras } 527ab1f9dacSPaul Mackerras 528ab1f9dacSPaul Mackerras if (count > 0) 529ab1f9dacSPaul Mackerras printk("-0x%lx", i); 530ab1f9dacSPaul Mackerras printk("\n"); 531ab1f9dacSPaul Mackerras } 532ab1f9dacSPaul Mackerras return; 533ab1f9dacSPaul Mackerras } 534ab1f9dacSPaul Mackerras 535ab1f9dacSPaul Mackerras /* 536ab1f9dacSPaul Mackerras * Allocate some memory, satisfying the lmb or bootmem allocator where 537ab1f9dacSPaul Mackerras * required. nid is the preferred node and end is the physical address of 538ab1f9dacSPaul Mackerras * the highest address in the node. 539ab1f9dacSPaul Mackerras * 540ab1f9dacSPaul Mackerras * Returns the physical address of the memory. 541ab1f9dacSPaul Mackerras */ 54245fb6ceaSAnton Blanchard static void __init *careful_allocation(int nid, unsigned long size, 54345fb6ceaSAnton Blanchard unsigned long align, 54445fb6ceaSAnton Blanchard unsigned long end_pfn) 545ab1f9dacSPaul Mackerras { 54645fb6ceaSAnton Blanchard int new_nid; 54745fb6ceaSAnton Blanchard unsigned long ret = lmb_alloc_base(size, align, end_pfn << PAGE_SHIFT); 548ab1f9dacSPaul Mackerras 549ab1f9dacSPaul Mackerras /* retry over all memory */ 550ab1f9dacSPaul Mackerras if (!ret) 551ab1f9dacSPaul Mackerras ret = lmb_alloc_base(size, align, lmb_end_of_DRAM()); 552ab1f9dacSPaul Mackerras 553ab1f9dacSPaul Mackerras if (!ret) 554ab1f9dacSPaul Mackerras panic("numa.c: cannot allocate %lu bytes on node %d", 555ab1f9dacSPaul Mackerras size, nid); 556ab1f9dacSPaul Mackerras 557ab1f9dacSPaul Mackerras /* 558ab1f9dacSPaul Mackerras * If the memory came from a previously allocated node, we must 559ab1f9dacSPaul Mackerras * retry with the bootmem allocator. 560ab1f9dacSPaul Mackerras */ 56145fb6ceaSAnton Blanchard new_nid = early_pfn_to_nid(ret >> PAGE_SHIFT); 56245fb6ceaSAnton Blanchard if (new_nid < nid) { 56345fb6ceaSAnton Blanchard ret = (unsigned long)__alloc_bootmem_node(NODE_DATA(new_nid), 564ab1f9dacSPaul Mackerras size, align, 0); 565ab1f9dacSPaul Mackerras 566ab1f9dacSPaul Mackerras if (!ret) 567ab1f9dacSPaul Mackerras panic("numa.c: cannot allocate %lu bytes on node %d", 56845fb6ceaSAnton Blanchard size, new_nid); 569ab1f9dacSPaul Mackerras 57045fb6ceaSAnton Blanchard ret = __pa(ret); 571ab1f9dacSPaul Mackerras 572ab1f9dacSPaul Mackerras dbg("alloc_bootmem %lx %lx\n", ret, size); 573ab1f9dacSPaul Mackerras } 574ab1f9dacSPaul Mackerras 57545fb6ceaSAnton Blanchard return (void *)ret; 576ab1f9dacSPaul Mackerras } 577ab1f9dacSPaul Mackerras 578ab1f9dacSPaul Mackerras void __init do_init_bootmem(void) 579ab1f9dacSPaul Mackerras { 580ab1f9dacSPaul Mackerras int nid; 58145fb6ceaSAnton Blanchard unsigned int i; 582ab1f9dacSPaul Mackerras static struct notifier_block ppc64_numa_nb = { 583ab1f9dacSPaul Mackerras .notifier_call = cpu_numa_callback, 584ab1f9dacSPaul Mackerras .priority = 1 /* Must run before sched domains notifier. */ 585ab1f9dacSPaul Mackerras }; 586ab1f9dacSPaul Mackerras 587ab1f9dacSPaul Mackerras min_low_pfn = 0; 588ab1f9dacSPaul Mackerras max_low_pfn = lmb_end_of_DRAM() >> PAGE_SHIFT; 589ab1f9dacSPaul Mackerras max_pfn = max_low_pfn; 590ab1f9dacSPaul Mackerras 591ab1f9dacSPaul Mackerras if (parse_numa_properties()) 592ab1f9dacSPaul Mackerras setup_nonnuma(); 593ab1f9dacSPaul Mackerras else 594ab1f9dacSPaul Mackerras dump_numa_topology(); 595ab1f9dacSPaul Mackerras 596ab1f9dacSPaul Mackerras register_cpu_notifier(&ppc64_numa_nb); 597ab1f9dacSPaul Mackerras 598ab1f9dacSPaul Mackerras for_each_online_node(nid) { 59945fb6ceaSAnton Blanchard unsigned long start_pfn, end_pfn, pages_present; 600ab1f9dacSPaul Mackerras unsigned long bootmem_paddr; 601ab1f9dacSPaul Mackerras unsigned long bootmap_pages; 602ab1f9dacSPaul Mackerras 60345fb6ceaSAnton Blanchard get_region(nid, &start_pfn, &end_pfn, &pages_present); 604ab1f9dacSPaul Mackerras 605ab1f9dacSPaul Mackerras /* Allocate the node structure node local if possible */ 60645fb6ceaSAnton Blanchard NODE_DATA(nid) = careful_allocation(nid, 607ab1f9dacSPaul Mackerras sizeof(struct pglist_data), 60845fb6ceaSAnton Blanchard SMP_CACHE_BYTES, end_pfn); 60945fb6ceaSAnton Blanchard NODE_DATA(nid) = __va(NODE_DATA(nid)); 610ab1f9dacSPaul Mackerras memset(NODE_DATA(nid), 0, sizeof(struct pglist_data)); 611ab1f9dacSPaul Mackerras 612ab1f9dacSPaul Mackerras dbg("node %d\n", nid); 613ab1f9dacSPaul Mackerras dbg("NODE_DATA() = %p\n", NODE_DATA(nid)); 614ab1f9dacSPaul Mackerras 615ab1f9dacSPaul Mackerras NODE_DATA(nid)->bdata = &plat_node_bdata[nid]; 61645fb6ceaSAnton Blanchard NODE_DATA(nid)->node_start_pfn = start_pfn; 61745fb6ceaSAnton Blanchard NODE_DATA(nid)->node_spanned_pages = end_pfn - start_pfn; 618ab1f9dacSPaul Mackerras 619ab1f9dacSPaul Mackerras if (NODE_DATA(nid)->node_spanned_pages == 0) 620ab1f9dacSPaul Mackerras continue; 621ab1f9dacSPaul Mackerras 62245fb6ceaSAnton Blanchard dbg("start_paddr = %lx\n", start_pfn << PAGE_SHIFT); 62345fb6ceaSAnton Blanchard dbg("end_paddr = %lx\n", end_pfn << PAGE_SHIFT); 624ab1f9dacSPaul Mackerras 62545fb6ceaSAnton Blanchard bootmap_pages = bootmem_bootmap_pages(end_pfn - start_pfn); 62645fb6ceaSAnton Blanchard bootmem_paddr = (unsigned long)careful_allocation(nid, 627ab1f9dacSPaul Mackerras bootmap_pages << PAGE_SHIFT, 62845fb6ceaSAnton Blanchard PAGE_SIZE, end_pfn); 62945fb6ceaSAnton Blanchard memset(__va(bootmem_paddr), 0, bootmap_pages << PAGE_SHIFT); 63045fb6ceaSAnton Blanchard 631ab1f9dacSPaul Mackerras dbg("bootmap_paddr = %lx\n", bootmem_paddr); 632ab1f9dacSPaul Mackerras 633ab1f9dacSPaul Mackerras init_bootmem_node(NODE_DATA(nid), bootmem_paddr >> PAGE_SHIFT, 63445fb6ceaSAnton Blanchard start_pfn, end_pfn); 635ab1f9dacSPaul Mackerras 63645fb6ceaSAnton Blanchard /* Add free regions on this node */ 63745fb6ceaSAnton Blanchard for (i = 0; init_node_data[i].end_pfn; i++) { 63845fb6ceaSAnton Blanchard unsigned long start, end; 639ab1f9dacSPaul Mackerras 64045fb6ceaSAnton Blanchard if (init_node_data[i].nid != nid) 641ab1f9dacSPaul Mackerras continue; 642ab1f9dacSPaul Mackerras 64345fb6ceaSAnton Blanchard start = init_node_data[i].start_pfn << PAGE_SHIFT; 64445fb6ceaSAnton Blanchard end = init_node_data[i].end_pfn << PAGE_SHIFT; 645ab1f9dacSPaul Mackerras 64645fb6ceaSAnton Blanchard dbg("free_bootmem %lx %lx\n", start, end - start); 64745fb6ceaSAnton Blanchard free_bootmem_node(NODE_DATA(nid), start, end - start); 648ab1f9dacSPaul Mackerras } 649ab1f9dacSPaul Mackerras 65045fb6ceaSAnton Blanchard /* Mark reserved regions on this node */ 651ab1f9dacSPaul Mackerras for (i = 0; i < lmb.reserved.cnt; i++) { 652ab1f9dacSPaul Mackerras unsigned long physbase = lmb.reserved.region[i].base; 653ab1f9dacSPaul Mackerras unsigned long size = lmb.reserved.region[i].size; 65445fb6ceaSAnton Blanchard unsigned long start_paddr = start_pfn << PAGE_SHIFT; 65545fb6ceaSAnton Blanchard unsigned long end_paddr = end_pfn << PAGE_SHIFT; 656ab1f9dacSPaul Mackerras 65745fb6ceaSAnton Blanchard if (early_pfn_to_nid(physbase >> PAGE_SHIFT) != nid && 65845fb6ceaSAnton Blanchard early_pfn_to_nid((physbase+size-1) >> PAGE_SHIFT) != nid) 659ab1f9dacSPaul Mackerras continue; 660ab1f9dacSPaul Mackerras 661ab1f9dacSPaul Mackerras if (physbase < end_paddr && 662ab1f9dacSPaul Mackerras (physbase+size) > start_paddr) { 663ab1f9dacSPaul Mackerras /* overlaps */ 664ab1f9dacSPaul Mackerras if (physbase < start_paddr) { 665ab1f9dacSPaul Mackerras size -= start_paddr - physbase; 666ab1f9dacSPaul Mackerras physbase = start_paddr; 667ab1f9dacSPaul Mackerras } 668ab1f9dacSPaul Mackerras 669ab1f9dacSPaul Mackerras if (size > end_paddr - physbase) 670ab1f9dacSPaul Mackerras size = end_paddr - physbase; 671ab1f9dacSPaul Mackerras 672ab1f9dacSPaul Mackerras dbg("reserve_bootmem %lx %lx\n", physbase, 673ab1f9dacSPaul Mackerras size); 674ab1f9dacSPaul Mackerras reserve_bootmem_node(NODE_DATA(nid), physbase, 675ab1f9dacSPaul Mackerras size); 676ab1f9dacSPaul Mackerras } 677ab1f9dacSPaul Mackerras } 678ab1f9dacSPaul Mackerras 67945fb6ceaSAnton Blanchard /* Add regions into sparsemem */ 68045fb6ceaSAnton Blanchard for (i = 0; init_node_data[i].end_pfn; i++) { 68145fb6ceaSAnton Blanchard unsigned long start, end; 68245fb6ceaSAnton Blanchard 68345fb6ceaSAnton Blanchard if (init_node_data[i].nid != nid) 684ab1f9dacSPaul Mackerras continue; 685ab1f9dacSPaul Mackerras 68645fb6ceaSAnton Blanchard start = init_node_data[i].start_pfn; 68745fb6ceaSAnton Blanchard end = init_node_data[i].end_pfn; 688ab1f9dacSPaul Mackerras 68945fb6ceaSAnton Blanchard memory_present(nid, start, end); 690ab1f9dacSPaul Mackerras } 691ab1f9dacSPaul Mackerras } 692ab1f9dacSPaul Mackerras } 693ab1f9dacSPaul Mackerras 694ab1f9dacSPaul Mackerras void __init paging_init(void) 695ab1f9dacSPaul Mackerras { 696ab1f9dacSPaul Mackerras unsigned long zones_size[MAX_NR_ZONES]; 697ab1f9dacSPaul Mackerras unsigned long zholes_size[MAX_NR_ZONES]; 698ab1f9dacSPaul Mackerras int nid; 699ab1f9dacSPaul Mackerras 700ab1f9dacSPaul Mackerras memset(zones_size, 0, sizeof(zones_size)); 701ab1f9dacSPaul Mackerras memset(zholes_size, 0, sizeof(zholes_size)); 702ab1f9dacSPaul Mackerras 703ab1f9dacSPaul Mackerras for_each_online_node(nid) { 70445fb6ceaSAnton Blanchard unsigned long start_pfn, end_pfn, pages_present; 705ab1f9dacSPaul Mackerras 70645fb6ceaSAnton Blanchard get_region(nid, &start_pfn, &end_pfn, &pages_present); 707ab1f9dacSPaul Mackerras 708ab1f9dacSPaul Mackerras zones_size[ZONE_DMA] = end_pfn - start_pfn; 70945fb6ceaSAnton Blanchard zholes_size[ZONE_DMA] = zones_size[ZONE_DMA] - pages_present; 710ab1f9dacSPaul Mackerras 711ab1f9dacSPaul Mackerras dbg("free_area_init node %d %lx %lx (hole: %lx)\n", nid, 712ab1f9dacSPaul Mackerras zones_size[ZONE_DMA], start_pfn, zholes_size[ZONE_DMA]); 713ab1f9dacSPaul Mackerras 71445fb6ceaSAnton Blanchard free_area_init_node(nid, NODE_DATA(nid), zones_size, start_pfn, 71545fb6ceaSAnton Blanchard zholes_size); 716ab1f9dacSPaul Mackerras } 717ab1f9dacSPaul Mackerras } 718ab1f9dacSPaul Mackerras 719ab1f9dacSPaul Mackerras static int __init early_numa(char *p) 720ab1f9dacSPaul Mackerras { 721ab1f9dacSPaul Mackerras if (!p) 722ab1f9dacSPaul Mackerras return 0; 723ab1f9dacSPaul Mackerras 724ab1f9dacSPaul Mackerras if (strstr(p, "off")) 725ab1f9dacSPaul Mackerras numa_enabled = 0; 726ab1f9dacSPaul Mackerras 727ab1f9dacSPaul Mackerras if (strstr(p, "debug")) 728ab1f9dacSPaul Mackerras numa_debug = 1; 729ab1f9dacSPaul Mackerras 730ab1f9dacSPaul Mackerras return 0; 731ab1f9dacSPaul Mackerras } 732ab1f9dacSPaul Mackerras early_param("numa", early_numa); 733