Skip to content
numa.c 25.3 KiB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
/*
 * 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>
#include <linux/of.h>
#include <asm/sparsemem.h>
#include <asm/smp.h>
Linus Torvalds's avatar
Linus Torvalds committed

static int numa_enabled = 1;

static char *cmdline __initdata;

Linus Torvalds's avatar
Linus Torvalds committed
static int numa_debug;
#define dbg(args...) if (numa_debug) { printk(KERN_INFO args); }

int numa_cpu_lookup_table[NR_CPUS];
Linus Torvalds's avatar
Linus Torvalds committed
cpumask_t numa_cpumask_lookup_table[MAX_NUMNODES];
struct pglist_data *node_data[MAX_NUMNODES];

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

Linus Torvalds's avatar
Linus Torvalds committed
static int min_common_depth;
static int n_mem_addr_cells, n_mem_size_cells;
Linus Torvalds's avatar
Linus Torvalds committed

static int __cpuinit fake_numa_create_new_node(unsigned long end_pfn,
						unsigned int *nid)
{
	unsigned long long mem;
	char *p = cmdline;
	static unsigned int fake_nid;
	static unsigned long long curr_boundary;

	/*
	 * Modify node id, iff we started creating NUMA nodes
	 * We want to continue from where we left of the last time
	 */
	if (fake_nid)
		*nid = fake_nid;
	/*
	 * In case there are no more arguments to parse, the
	 * node_id should be the same as the last fake node id
	 * (we've handled this above).
	 */
	if (!p)
		return 0;

	mem = memparse(p, &p);
	if (!mem)
		return 0;

	if (mem < curr_boundary)
		return 0;

	curr_boundary = mem;

	if ((end_pfn << PAGE_SHIFT) > mem) {
		/*
		 * Skip commas and spaces
		 */
		while (*p == ',' || *p == ' ' || *p == '\t')
			p++;

		cmdline = p;
		fake_nid++;
		*nid = fake_nid;
		dbg("created new fake_node with id %d\n", fake_nid);
		return 1;
	}
	return 0;
}

static void __cpuinit map_cpu_to_node(int cpu, int node)
Linus Torvalds's avatar
Linus Torvalds committed
{
	numa_cpu_lookup_table[cpu] = node;
	dbg("adding cpu %d to node %d\n", cpu, node);

	if (!(cpu_isset(cpu, numa_cpumask_lookup_table[node])))
Linus Torvalds's avatar
Linus Torvalds committed
		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 */

static struct device_node * __cpuinit find_cpu_node(unsigned int cpu)
Linus Torvalds's avatar
Linus Torvalds committed
{
	unsigned int hw_cpuid = get_hard_smp_processor_id(cpu);
	struct device_node *cpu_node = NULL;
	const unsigned int *interrupt_server, *reg;
Linus Torvalds's avatar
Linus Torvalds committed
	int len;

	while ((cpu_node = of_find_node_by_type(cpu_node, "cpu")) != NULL) {
		/* Try interrupt server first */
		interrupt_server = of_get_property(cpu_node,
Linus Torvalds's avatar
Linus Torvalds committed
					"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 {
			reg = of_get_property(cpu_node, "reg", &len);
Linus Torvalds's avatar
Linus Torvalds committed
			if (reg && (len > 0) && (reg[0] == hw_cpuid))
				return cpu_node;
		}
	}

	return NULL;
}

/* must hold reference to node during call */
static const int *of_get_associativity(struct device_node *dev)
Linus Torvalds's avatar
Linus Torvalds committed
{
	return of_get_property(dev, "ibm,associativity", NULL);
/*
 * Returns the property linux,drconf-usable-memory if
 * it exists (the property exists only in kexec/kdump kernels,
 * added by kexec-tools)
 */
static const u32 *of_get_usable_memory(struct device_node *memory)
{
	const u32 *prop;
	u32 len;
	prop = of_get_property(memory, "linux,drconf-usable-memory", &len);
	if (!prop || len < sizeof(unsigned int))
		return 0;
	return prop;
}

/* Returns nid in the range [0..MAX_NUMNODES-1], or -1 if no useful numa
 * info is found.
 */
static int of_node_to_nid_single(struct device_node *device)
Linus Torvalds's avatar
Linus Torvalds committed
{
	const unsigned int *tmp;
Linus Torvalds's avatar
Linus Torvalds committed

	if (min_common_depth == -1)
Linus Torvalds's avatar
Linus Torvalds committed

	tmp = of_get_associativity(device);
	if (!tmp)
		goto out;

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

	/* POWER4 LPAR uses 0xffff as invalid node */
	if (nid == 0xffff || nid >= MAX_NUMNODES)
		nid = -1;
out:
/* 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) {
Loading
Loading full blame...