numa.c 16.6 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/*
 * pSeries NUMA support
 *
 * Copyright (C) 2002 Anton Blanchard <anton@au.ibm.com>, IBM
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version
 * 2 of the License, or (at your option) any later version.
 */
#include <linux/threads.h>
#include <linux/bootmem.h>
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/mmzone.h>
#include <linux/module.h>
#include <linux/nodemask.h>
#include <linux/cpu.h>
#include <linux/notifier.h>
20
#include <asm/sparsemem.h>
L
Linus Torvalds 已提交
21
#include <asm/lmb.h>
22
#include <asm/system.h>
P
Paul Mackerras 已提交
23
#include <asm/smp.h>
L
Linus Torvalds 已提交
24 25 26 27 28 29

static int numa_enabled = 1;

static int numa_debug;
#define dbg(args...) if (numa_debug) { printk(KERN_INFO args); }

30
int numa_cpu_lookup_table[NR_CPUS];
L
Linus Torvalds 已提交
31 32
cpumask_t numa_cpumask_lookup_table[MAX_NUMNODES];
struct pglist_data *node_data[MAX_NUMNODES];
33 34 35 36 37 38

EXPORT_SYMBOL(numa_cpu_lookup_table);
EXPORT_SYMBOL(numa_cpumask_lookup_table);
EXPORT_SYMBOL(node_data);

static bootmem_data_t __initdata plat_node_bdata[MAX_NUMNODES];
L
Linus Torvalds 已提交
39
static int min_common_depth;
40
static int n_mem_addr_cells, n_mem_size_cells;
L
Linus Torvalds 已提交
41

42
static void __cpuinit map_cpu_to_node(int cpu, int node)
L
Linus Torvalds 已提交
43 44
{
	numa_cpu_lookup_table[cpu] = node;
45

46 47
	dbg("adding cpu %d to node %d\n", cpu, node);

48
	if (!(cpu_isset(cpu, numa_cpumask_lookup_table[node])))
L
Linus Torvalds 已提交
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
		cpu_set(cpu, numa_cpumask_lookup_table[node]);
}

#ifdef CONFIG_HOTPLUG_CPU
static void unmap_cpu_from_node(unsigned long cpu)
{
	int node = numa_cpu_lookup_table[cpu];

	dbg("removing cpu %lu from node %d\n", cpu, node);

	if (cpu_isset(cpu, numa_cpumask_lookup_table[node])) {
		cpu_clear(cpu, numa_cpumask_lookup_table[node]);
	} else {
		printk(KERN_ERR "WARNING: cpu %lu not found in node %d\n",
		       cpu, node);
	}
}
#endif /* CONFIG_HOTPLUG_CPU */

68
static struct device_node * __cpuinit find_cpu_node(unsigned int cpu)
L
Linus Torvalds 已提交
69 70 71
{
	unsigned int hw_cpuid = get_hard_smp_processor_id(cpu);
	struct device_node *cpu_node = NULL;
72
	const unsigned int *interrupt_server, *reg;
L
Linus Torvalds 已提交
73 74 75 76
	int len;

	while ((cpu_node = of_find_node_by_type(cpu_node, "cpu")) != NULL) {
		/* Try interrupt server first */
77
		interrupt_server = get_property(cpu_node,
L
Linus Torvalds 已提交
78 79 80 81 82 83 84 85 86 87
					"ibm,ppc-interrupt-server#s", &len);

		len = len / sizeof(u32);

		if (interrupt_server && (len > 0)) {
			while (len--) {
				if (interrupt_server[len] == hw_cpuid)
					return cpu_node;
			}
		} else {
88
			reg = get_property(cpu_node, "reg", &len);
L
Linus Torvalds 已提交
89 90 91 92 93 94 95 96 97
			if (reg && (len > 0) && (reg[0] == hw_cpuid))
				return cpu_node;
		}
	}

	return NULL;
}

/* must hold reference to node during call */
98
static const int *of_get_associativity(struct device_node *dev)
L
Linus Torvalds 已提交
99
{
100
	return get_property(dev, "ibm,associativity", NULL);
L
Linus Torvalds 已提交
101 102
}

103 104 105
/* Returns nid in the range [0..MAX_NUMNODES-1], or -1 if no useful numa
 * info is found.
 */
106
static int of_node_to_nid_single(struct device_node *device)
L
Linus Torvalds 已提交
107
{
108
	int nid = -1;
109
	const unsigned int *tmp;
L
Linus Torvalds 已提交
110 111

	if (min_common_depth == -1)
112
		goto out;
L
Linus Torvalds 已提交
113 114

	tmp = of_get_associativity(device);
115 116 117 118
	if (!tmp)
		goto out;

	if (tmp[0] >= min_common_depth)
119
		nid = tmp[min_common_depth];
120 121

	/* POWER4 LPAR uses 0xffff as invalid node */
122 123 124
	if (nid == 0xffff || nid >= MAX_NUMNODES)
		nid = -1;
out:
125
	return nid;
L
Linus Torvalds 已提交
126 127
}

128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
/* Walk the device tree upwards, looking for an associativity id */
int of_node_to_nid(struct device_node *device)
{
	struct device_node *tmp;
	int nid = -1;

	of_node_get(device);
	while (device) {
		nid = of_node_to_nid_single(device);
		if (nid != -1)
			break;

	        tmp = device;
		device = of_get_parent(tmp);
		of_node_put(tmp);
	}
	of_node_put(device);

	return nid;
}
EXPORT_SYMBOL_GPL(of_node_to_nid);

L
Linus Torvalds 已提交
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166
/*
 * In theory, the "ibm,associativity" property may contain multiple
 * associativity lists because a resource may be multiply connected
 * into the machine.  This resource then has different associativity
 * characteristics relative to its multiple connections.  We ignore
 * this for now.  We also assume that all cpu and memory sets have
 * their distances represented at a common level.  This won't be
 * true for heirarchical NUMA.
 *
 * In any case the ibm,associativity-reference-points should give
 * the correct depth for a normal NUMA system.
 *
 * - Dave Hansen <haveblue@us.ibm.com>
 */
static int __init find_min_common_depth(void)
{
	int depth;
167
	const unsigned int *ref_points;
L
Linus Torvalds 已提交
168 169 170 171 172 173 174 175 176 177 178 179 180 181
	struct device_node *rtas_root;
	unsigned int len;

	rtas_root = of_find_node_by_path("/rtas");

	if (!rtas_root)
		return -1;

	/*
	 * this property is 2 32-bit integers, each representing a level of
	 * depth in the associativity nodes.  The first is for an SMP
	 * configuration (should be all 0's) and the second is for a normal
	 * NUMA configuration.
	 */
182
	ref_points = get_property(rtas_root,
L
Linus Torvalds 已提交
183 184 185 186 187
			"ibm,associativity-reference-points", &len);

	if ((len >= 1) && ref_points) {
		depth = ref_points[1];
	} else {
188
		dbg("NUMA: ibm,associativity-reference-points not found.\n");
L
Linus Torvalds 已提交
189 190 191 192 193 194 195
		depth = -1;
	}
	of_node_put(rtas_root);

	return depth;
}

196
static void __init get_n_mem_cells(int *n_addr_cells, int *n_size_cells)
L
Linus Torvalds 已提交
197 198 199 200
{
	struct device_node *memory = NULL;

	memory = of_find_node_by_type(memory, "memory");
201
	if (!memory)
202
		panic("numa.c: No memory nodes found!");
203

204 205 206
	*n_addr_cells = prom_n_addr_cells(memory);
	*n_size_cells = prom_n_size_cells(memory);
	of_node_put(memory);
L
Linus Torvalds 已提交
207 208
}

209
static unsigned long __devinit read_n_cells(int n, const unsigned int **buf)
L
Linus Torvalds 已提交
210 211 212 213 214 215 216 217 218 219 220 221 222 223
{
	unsigned long result = 0;

	while (n--) {
		result = (result << 32) | **buf;
		(*buf)++;
	}
	return result;
}

/*
 * Figure out to which domain a cpu belongs and stick it there.
 * Return the id of the domain used.
 */
224
static int __cpuinit numa_setup_cpu(unsigned long lcpu)
L
Linus Torvalds 已提交
225
{
226
	int nid = 0;
L
Linus Torvalds 已提交
227 228 229 230 231 232 233
	struct device_node *cpu = find_cpu_node(lcpu);

	if (!cpu) {
		WARN_ON(1);
		goto out;
	}

234
	nid = of_node_to_nid_single(cpu);
L
Linus Torvalds 已提交
235

236 237
	if (nid < 0 || !node_online(nid))
		nid = any_online_node(NODE_MASK_ALL);
L
Linus Torvalds 已提交
238
out:
239
	map_cpu_to_node(lcpu, nid);
L
Linus Torvalds 已提交
240 241 242

	of_node_put(cpu);

243
	return nid;
L
Linus Torvalds 已提交
244 245
}

246
static int __cpuinit cpu_numa_callback(struct notifier_block *nfb,
L
Linus Torvalds 已提交
247 248 249 250 251 252 253 254
			     unsigned long action,
			     void *hcpu)
{
	unsigned long lcpu = (unsigned long)hcpu;
	int ret = NOTIFY_DONE;

	switch (action) {
	case CPU_UP_PREPARE:
255
		numa_setup_cpu(lcpu);
L
Linus Torvalds 已提交
256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276
		ret = NOTIFY_OK;
		break;
#ifdef CONFIG_HOTPLUG_CPU
	case CPU_DEAD:
	case CPU_UP_CANCELED:
		unmap_cpu_from_node(lcpu);
		break;
		ret = NOTIFY_OK;
#endif
	}
	return ret;
}

/*
 * Check and possibly modify a memory region to enforce the memory limit.
 *
 * Returns the size the region should have to enforce the memory limit.
 * This will either be the original value of size, a truncated value,
 * or zero. If the returned value of size is 0 the region should be
 * discarded as it lies wholy above the memory limit.
 */
277 278
static unsigned long __init numa_enforce_memory_limit(unsigned long start,
						      unsigned long size)
L
Linus Torvalds 已提交
279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301
{
	/*
	 * We use lmb_end_of_DRAM() in here instead of memory_limit because
	 * we've already adjusted it for the limit and it takes care of
	 * having memory holes below the limit.
	 */

	if (! memory_limit)
		return size;

	if (start + size <= lmb_end_of_DRAM())
		return size;

	if (start >= lmb_end_of_DRAM())
		return 0;

	return lmb_end_of_DRAM() - start;
}

static int __init parse_numa_properties(void)
{
	struct device_node *cpu = NULL;
	struct device_node *memory = NULL;
302
	int default_nid = 0;
L
Linus Torvalds 已提交
303 304 305 306 307 308 309 310 311 312 313 314
	unsigned long i;

	if (numa_enabled == 0) {
		printk(KERN_WARNING "NUMA disabled by user\n");
		return -1;
	}

	min_common_depth = find_min_common_depth();

	if (min_common_depth < 0)
		return min_common_depth;

315 316
	dbg("NUMA associativity depth for CPU/Memory: %d\n", min_common_depth);

L
Linus Torvalds 已提交
317
	/*
318 319 320
	 * Even though we connect cpus to numa domains later in SMP
	 * init, we need to know the node ids now. This is because
	 * each node to be onlined must have NODE_DATA etc backing it.
L
Linus Torvalds 已提交
321
	 */
322
	for_each_present_cpu(i) {
323
		int nid;
L
Linus Torvalds 已提交
324 325

		cpu = find_cpu_node(i);
326
		BUG_ON(!cpu);
327
		nid = of_node_to_nid_single(cpu);
328
		of_node_put(cpu);
L
Linus Torvalds 已提交
329

330 331 332 333 334 335 336 337
		/*
		 * Don't fall back to default_nid yet -- we will plug
		 * cpus into nodes once the memory scan has discovered
		 * the topology.
		 */
		if (nid < 0)
			continue;
		node_set_online(nid);
L
Linus Torvalds 已提交
338 339
	}

340
	get_n_mem_cells(&n_mem_addr_cells, &n_mem_size_cells);
L
Linus Torvalds 已提交
341 342 343 344
	memory = NULL;
	while ((memory = of_find_node_by_type(memory, "memory")) != NULL) {
		unsigned long start;
		unsigned long size;
345
		int nid;
L
Linus Torvalds 已提交
346
		int ranges;
347
		const unsigned int *memcell_buf;
L
Linus Torvalds 已提交
348 349
		unsigned int len;

350
		memcell_buf = get_property(memory,
351 352
			"linux,usable-memory", &len);
		if (!memcell_buf || len <= 0)
353
			memcell_buf = get_property(memory, "reg", &len);
L
Linus Torvalds 已提交
354 355 356
		if (!memcell_buf || len <= 0)
			continue;

357 358
		/* ranges in cell */
		ranges = (len >> 2) / (n_mem_addr_cells + n_mem_size_cells);
L
Linus Torvalds 已提交
359 360
new_range:
		/* these are order-sensitive, and modify the buffer pointer */
361 362
		start = read_n_cells(n_mem_addr_cells, &memcell_buf);
		size = read_n_cells(n_mem_size_cells, &memcell_buf);
L
Linus Torvalds 已提交
363

364 365 366 367 368
		/*
		 * Assumption: either all memory nodes or none will
		 * have associativity properties.  If none, then
		 * everything goes to default_nid.
		 */
369
		nid = of_node_to_nid_single(memory);
370 371 372
		if (nid < 0)
			nid = default_nid;
		node_set_online(nid);
L
Linus Torvalds 已提交
373

374
		if (!(size = numa_enforce_memory_limit(start, size))) {
L
Linus Torvalds 已提交
375 376 377 378 379 380
			if (--ranges)
				goto new_range;
			else
				continue;
		}

381 382
		add_active_range(nid, start >> PAGE_SHIFT,
				(start >> PAGE_SHIFT) + (size >> PAGE_SHIFT));
L
Linus Torvalds 已提交
383 384 385 386 387 388 389 390 391 392 393 394

		if (--ranges)
			goto new_range;
	}

	return 0;
}

static void __init setup_nonnuma(void)
{
	unsigned long top_of_ram = lmb_end_of_DRAM();
	unsigned long total_ram = lmb_phys_mem_size();
395
	unsigned long start_pfn, end_pfn;
396
	unsigned int i;
L
Linus Torvalds 已提交
397

398
	printk(KERN_DEBUG "Top of RAM: 0x%lx, Total RAM: 0x%lx\n",
L
Linus Torvalds 已提交
399
	       top_of_ram, total_ram);
400
	printk(KERN_DEBUG "Memory hole size: %ldMB\n",
L
Linus Torvalds 已提交
401 402
	       (top_of_ram - total_ram) >> 20);

403 404 405 406 407
	for (i = 0; i < lmb.memory.cnt; ++i) {
		start_pfn = lmb.memory.region[i].base >> PAGE_SHIFT;
		end_pfn = start_pfn + lmb_size_pages(&lmb.memory, i);
		add_active_range(0, start_pfn, end_pfn);
	}
L
Linus Torvalds 已提交
408 409 410
	node_set_online(0);
}

411 412 413 414 415 416 417 418 419
void __init dump_numa_cpu_topology(void)
{
	unsigned int node;
	unsigned int cpu, count;

	if (min_common_depth == -1 || !numa_enabled)
		return;

	for_each_online_node(node) {
420
		printk(KERN_DEBUG "Node %d CPUs:", node);
421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445

		count = 0;
		/*
		 * If we used a CPU iterator here we would miss printing
		 * the holes in the cpumap.
		 */
		for (cpu = 0; cpu < NR_CPUS; cpu++) {
			if (cpu_isset(cpu, numa_cpumask_lookup_table[node])) {
				if (count == 0)
					printk(" %u", cpu);
				++count;
			} else {
				if (count > 1)
					printk("-%u", cpu - 1);
				count = 0;
			}
		}

		if (count > 1)
			printk("-%u", NR_CPUS - 1);
		printk("\n");
	}
}

static void __init dump_numa_memory_topology(void)
L
Linus Torvalds 已提交
446 447 448 449 450 451 452 453 454 455
{
	unsigned int node;
	unsigned int count;

	if (min_common_depth == -1 || !numa_enabled)
		return;

	for_each_online_node(node) {
		unsigned long i;

456
		printk(KERN_DEBUG "Node %d Memory:", node);
L
Linus Torvalds 已提交
457 458 459

		count = 0;

460 461 462
		for (i = 0; i < lmb_end_of_DRAM();
		     i += (1 << SECTION_SIZE_BITS)) {
			if (early_pfn_to_nid(i >> PAGE_SHIFT) == node) {
L
Linus Torvalds 已提交
463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485
				if (count == 0)
					printk(" 0x%lx", i);
				++count;
			} else {
				if (count > 0)
					printk("-0x%lx", i);
				count = 0;
			}
		}

		if (count > 0)
			printk("-0x%lx", i);
		printk("\n");
	}
}

/*
 * Allocate some memory, satisfying the lmb or bootmem allocator where
 * required. nid is the preferred node and end is the physical address of
 * the highest address in the node.
 *
 * Returns the physical address of the memory.
 */
486 487 488
static void __init *careful_allocation(int nid, unsigned long size,
				       unsigned long align,
				       unsigned long end_pfn)
L
Linus Torvalds 已提交
489
{
490
	int new_nid;
491
	unsigned long ret = __lmb_alloc_base(size, align, end_pfn << PAGE_SHIFT);
L
Linus Torvalds 已提交
492 493 494

	/* retry over all memory */
	if (!ret)
495
		ret = __lmb_alloc_base(size, align, lmb_end_of_DRAM());
L
Linus Torvalds 已提交
496 497 498 499 500 501 502 503 504

	if (!ret)
		panic("numa.c: cannot allocate %lu bytes on node %d",
		      size, nid);

	/*
	 * If the memory came from a previously allocated node, we must
	 * retry with the bootmem allocator.
	 */
505 506 507
	new_nid = early_pfn_to_nid(ret >> PAGE_SHIFT);
	if (new_nid < nid) {
		ret = (unsigned long)__alloc_bootmem_node(NODE_DATA(new_nid),
L
Linus Torvalds 已提交
508 509 510 511
				size, align, 0);

		if (!ret)
			panic("numa.c: cannot allocate %lu bytes on node %d",
512
			      size, new_nid);
L
Linus Torvalds 已提交
513

514
		ret = __pa(ret);
L
Linus Torvalds 已提交
515 516 517 518

		dbg("alloc_bootmem %lx %lx\n", ret, size);
	}

519
	return (void *)ret;
L
Linus Torvalds 已提交
520 521
}

522 523 524 525 526
static struct notifier_block __cpuinitdata ppc64_numa_nb = {
	.notifier_call = cpu_numa_callback,
	.priority = 1 /* Must run before sched domains notifier. */
};

L
Linus Torvalds 已提交
527 528 529
void __init do_init_bootmem(void)
{
	int nid;
530
	unsigned int i;
L
Linus Torvalds 已提交
531 532 533 534 535 536 537 538

	min_low_pfn = 0;
	max_low_pfn = lmb_end_of_DRAM() >> PAGE_SHIFT;
	max_pfn = max_low_pfn;

	if (parse_numa_properties())
		setup_nonnuma();
	else
539
		dump_numa_memory_topology();
L
Linus Torvalds 已提交
540 541

	register_cpu_notifier(&ppc64_numa_nb);
542 543
	cpu_numa_callback(&ppc64_numa_nb, CPU_UP_PREPARE,
			  (void *)(unsigned long)boot_cpuid);
L
Linus Torvalds 已提交
544 545

	for_each_online_node(nid) {
546
		unsigned long start_pfn, end_pfn;
L
Linus Torvalds 已提交
547 548 549
		unsigned long bootmem_paddr;
		unsigned long bootmap_pages;

550
		get_pfn_range_for_nid(nid, &start_pfn, &end_pfn);
L
Linus Torvalds 已提交
551 552

		/* Allocate the node structure node local if possible */
553
		NODE_DATA(nid) = careful_allocation(nid,
L
Linus Torvalds 已提交
554
					sizeof(struct pglist_data),
555 556
					SMP_CACHE_BYTES, end_pfn);
		NODE_DATA(nid) = __va(NODE_DATA(nid));
L
Linus Torvalds 已提交
557 558 559 560 561 562
		memset(NODE_DATA(nid), 0, sizeof(struct pglist_data));

  		dbg("node %d\n", nid);
		dbg("NODE_DATA() = %p\n", NODE_DATA(nid));

		NODE_DATA(nid)->bdata = &plat_node_bdata[nid];
563 564
		NODE_DATA(nid)->node_start_pfn = start_pfn;
		NODE_DATA(nid)->node_spanned_pages = end_pfn - start_pfn;
L
Linus Torvalds 已提交
565 566 567 568

		if (NODE_DATA(nid)->node_spanned_pages == 0)
  			continue;

569 570
  		dbg("start_paddr = %lx\n", start_pfn << PAGE_SHIFT);
  		dbg("end_paddr = %lx\n", end_pfn << PAGE_SHIFT);
L
Linus Torvalds 已提交
571

572 573 574 575 576
		bootmap_pages = bootmem_bootmap_pages(end_pfn - start_pfn);
		bootmem_paddr = (unsigned long)careful_allocation(nid,
					bootmap_pages << PAGE_SHIFT,
					PAGE_SIZE, end_pfn);
		memset(__va(bootmem_paddr), 0, bootmap_pages << PAGE_SHIFT);
L
Linus Torvalds 已提交
577 578 579 580

		dbg("bootmap_paddr = %lx\n", bootmem_paddr);

		init_bootmem_node(NODE_DATA(nid), bootmem_paddr >> PAGE_SHIFT,
581
				  start_pfn, end_pfn);
L
Linus Torvalds 已提交
582

583
		free_bootmem_with_active_regions(nid, end_pfn);
L
Linus Torvalds 已提交
584

585
		/* Mark reserved regions on this node */
L
Linus Torvalds 已提交
586
		for (i = 0; i < lmb.reserved.cnt; i++) {
587
			unsigned long physbase = lmb.reserved.region[i].base;
L
Linus Torvalds 已提交
588
			unsigned long size = lmb.reserved.region[i].size;
589 590
			unsigned long start_paddr = start_pfn << PAGE_SHIFT;
			unsigned long end_paddr = end_pfn << PAGE_SHIFT;
L
Linus Torvalds 已提交
591

592 593
			if (early_pfn_to_nid(physbase >> PAGE_SHIFT) != nid &&
			    early_pfn_to_nid((physbase+size-1) >> PAGE_SHIFT) != nid)
L
Linus Torvalds 已提交
594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612
				continue;

			if (physbase < end_paddr &&
			    (physbase+size) > start_paddr) {
				/* overlaps */
				if (physbase < start_paddr) {
					size -= start_paddr - physbase;
					physbase = start_paddr;
				}

				if (size > end_paddr - physbase)
					size = end_paddr - physbase;

				dbg("reserve_bootmem %lx %lx\n", physbase,
				    size);
				reserve_bootmem_node(NODE_DATA(nid), physbase,
						     size);
			}
		}
B
Bob Picco 已提交
613

614
		sparse_memory_present_with_active_regions(nid);
L
Linus Torvalds 已提交
615 616 617 618 619
	}
}

void __init paging_init(void)
{
620 621 622 623
	unsigned long max_zone_pfns[MAX_NR_ZONES] = {
				lmb_end_of_DRAM() >> PAGE_SHIFT
	};
	free_area_init_nodes(max_zone_pfns);
L
Linus Torvalds 已提交
624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639
}

static int __init early_numa(char *p)
{
	if (!p)
		return 0;

	if (strstr(p, "off"))
		numa_enabled = 0;

	if (strstr(p, "debug"))
		numa_debug = 1;

	return 0;
}
early_param("numa", early_numa);
640 641 642 643 644 645 646 647 648 649

#ifdef CONFIG_MEMORY_HOTPLUG
/*
 * Find the node associated with a hot added memory section.  Section
 * corresponds to a SPARSEMEM section, not an LMB.  It is assumed that
 * sections are fully contained within a single LMB.
 */
int hot_add_scn_to_nid(unsigned long scn_addr)
{
	struct device_node *memory = NULL;
650
	nodemask_t nodes;
651
	int default_nid = any_online_node(NODE_MASK_ALL);
652
	int nid;
653 654

	if (!numa_enabled || (min_common_depth < 0))
655
		return default_nid;
656 657 658

	while ((memory = of_find_node_by_type(memory, "memory")) != NULL) {
		unsigned long start, size;
659
		int ranges;
660
		const unsigned int *memcell_buf;
661 662
		unsigned int len;

663
		memcell_buf = get_property(memory, "reg", &len);
664 665 666
		if (!memcell_buf || len <= 0)
			continue;

667 668
		/* ranges in cell */
		ranges = (len >> 2) / (n_mem_addr_cells + n_mem_size_cells);
669 670 671
ha_new_range:
		start = read_n_cells(n_mem_addr_cells, &memcell_buf);
		size = read_n_cells(n_mem_size_cells, &memcell_buf);
672
		nid = of_node_to_nid_single(memory);
673 674

		/* Domains not present at boot default to 0 */
675 676
		if (nid < 0 || !node_online(nid))
			nid = default_nid;
677 678 679

		if ((scn_addr >= start) && (scn_addr < (start + size))) {
			of_node_put(memory);
680
			goto got_nid;
681 682 683 684 685 686
		}

		if (--ranges)		/* process all ranges in cell */
			goto ha_new_range;
	}
	BUG();	/* section address should be found above */
687
	return 0;
688 689

	/* Temporary code to ensure that returned node is not empty */
690
got_nid:
691
	nodes_setall(nodes);
692 693 694
	while (NODE_DATA(nid)->node_spanned_pages == 0) {
		node_clear(nid, nodes);
		nid = any_online_node(nodes);
695
	}
696
	return nid;
697 698
}
#endif /* CONFIG_MEMORY_HOTPLUG */