xref: /linux/arch/powerpc/mm/numa.c (revision fb6d73d3014babb69f5cc2d1d78b31e9d09fc5df)
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