vserver 1.9.5.x5
[linux-2.6.git] / arch / x86_64 / mm / numa.c
index e891e9d..fd9f25d 100644 (file)
 #include <linux/mmzone.h>
 #include <linux/ctype.h>
 #include <linux/module.h>
+#include <linux/nodemask.h>
+
 #include <asm/e820.h>
 #include <asm/proto.h>
 #include <asm/dma.h>
 #include <asm/numa.h>
+#include <asm/acpi.h>
 
 #ifndef Dprintk
 #define Dprintk(x...)
 #endif
 
-struct pglist_data *node_data[MAXNODE];
+struct pglist_data *node_data[MAX_NUMNODES];
 bootmem_data_t plat_node_bdata[MAX_NUMNODES];
 
 int memnode_shift;
 u8  memnodemap[NODEMAPSIZE];
 
-unsigned char cpu_to_node[NR_CPUS];  
-cpumask_t     node_to_cpumask[MAXNODE]; 
-
-static int numa_off __initdata; 
+unsigned char cpu_to_node[NR_CPUS] = { [0 ... NR_CPUS-1] = NUMA_NO_NODE };
+cpumask_t     node_to_cpumask[MAX_NUMNODES];
 
-unsigned long nodes_present; 
+int numa_off __initdata;
 
-int __init compute_hash_shift(struct node *nodes)
+int __init compute_hash_shift(struct node *nodes, int numnodes)
 {
        int i; 
        int shift = 24;
@@ -41,7 +42,7 @@ int __init compute_hash_shift(struct node *nodes)
        /* When in doubt use brute force. */
        while (shift < 48) { 
                memset(memnodemap,0xff,sizeof(*memnodemap) * NODEMAPSIZE); 
-               for (i = 0; i < numnodes; i++) { 
+               for (i = 0; i < numnodes; i++) {
                        if (nodes[i].start == nodes[i].end) 
                                continue;
                        for (addr = nodes[i].start; 
@@ -107,8 +108,6 @@ void __init setup_node_bootmem(int nodeid, unsigned long start, unsigned long en
 
        reserve_bootmem_node(NODE_DATA(nodeid), nodedata_phys, pgdat_size); 
        reserve_bootmem_node(NODE_DATA(nodeid), bootmap_start, bootmap_pages<<PAGE_SHIFT);
-       if (nodeid + 1 > numnodes)
-               numnodes = nodeid + 1;
        node_set_online(nodeid);
 } 
 
@@ -124,7 +123,7 @@ void __init setup_node_zones(int nodeid)
        start_pfn = node_start_pfn(nodeid);
        end_pfn = node_end_pfn(nodeid);
 
-       printk(KERN_INFO "setting up node %d %lx-%lx\n", nodeid, start_pfn, end_pfn); 
+       Dprintk(KERN_INFO "setting up node %d %lx-%lx\n", nodeid, start_pfn, end_pfn);
        
        /* All nodes > 0 have a zero length zone DMA */ 
        dma_end_pfn = __pa(MAX_DMA_ADDRESS) >> PAGE_SHIFT; 
@@ -135,7 +134,7 @@ void __init setup_node_zones(int nodeid)
                zones[ZONE_NORMAL] = end_pfn - start_pfn; 
        } 
     
-       free_area_init_node(nodeid, NODE_DATA(nodeid), NULL, zones, 
+       free_area_init_node(nodeid, NODE_DATA(nodeid), zones,
                            start_pfn, NULL); 
 } 
 
@@ -148,13 +147,12 @@ void __init numa_init_array(void)
           CPUs, as the number of CPUs is not known yet. 
           We round robin the existing nodes. */
        rr = 0;
-       for (i = 0; i < MAXNODE; i++) {
-               if (node_online(i))
+       for (i = 0; i < NR_CPUS; i++) {
+               if (cpu_to_node[i] != NUMA_NO_NODE)
                        continue;
-               rr = find_next_bit(node_online_map, MAX_NUMNODES, rr);
+               rr = next_node(rr, node_online_map);
                if (rr == MAX_NUMNODES)
-                       rr = find_first_bit(node_online_map, MAX_NUMNODES);
-               node_data[i] = node_data[rr];
+                       rr = first_node(node_online_map);
                cpu_to_node[i] = rr;
                rr++; 
        }
@@ -162,10 +160,68 @@ void __init numa_init_array(void)
        set_bit(0, &node_to_cpumask[cpu_to_node(0)]);
 }
 
+#ifdef CONFIG_NUMA_EMU
+int numa_fake __initdata = 0;
+
+/* Numa emulation */
+static int numa_emulation(unsigned long start_pfn, unsigned long end_pfn)
+{
+       int i;
+       struct node nodes[MAX_NUMNODES];
+       unsigned long sz = ((end_pfn - start_pfn)<<PAGE_SHIFT) / numa_fake;
+
+       /* Kludge needed for the hash function */
+       if (hweight64(sz) > 1) {
+               unsigned long x = 1;
+               while ((x << 1) < sz)
+                       x <<= 1;
+               if (x < sz/2)
+                       printk("Numa emulation unbalanced. Complain to maintainer\n");
+               sz = x;
+       }
+
+       memset(&nodes,0,sizeof(nodes));
+       for (i = 0; i < numa_fake; i++) {
+               nodes[i].start = (start_pfn<<PAGE_SHIFT) + i*sz;
+               if (i == numa_fake-1)
+                       sz = (end_pfn<<PAGE_SHIFT) - nodes[i].start;
+               nodes[i].end = nodes[i].start + sz;
+               if (i != numa_fake-1)
+                       nodes[i].end--;
+               printk(KERN_INFO "Faking node %d at %016Lx-%016Lx (%LuMB)\n",
+                      i,
+                      nodes[i].start, nodes[i].end,
+                      (nodes[i].end - nodes[i].start) >> 20);
+               node_set_online(i);
+       }
+       memnode_shift = compute_hash_shift(nodes, numa_fake);
+       if (memnode_shift < 0) {
+               memnode_shift = 0;
+               printk(KERN_ERR "No NUMA hash function found. Emulation disabled.\n");
+               return -1;
+       }
+       for_each_online_node(i)
+               setup_node_bootmem(i, nodes[i].start, nodes[i].end);
+       numa_init_array();
+       return 0;
+}
+#endif
+
 void __init numa_initmem_init(unsigned long start_pfn, unsigned long end_pfn)
 { 
        int i;
 
+#ifdef CONFIG_NUMA_EMU
+       if (numa_fake && !numa_emulation(start_pfn, end_pfn))
+               return;
+#endif
+
+#ifdef CONFIG_ACPI_NUMA
+       if (!numa_off && !acpi_scan_nodes(start_pfn << PAGE_SHIFT,
+                                         end_pfn << PAGE_SHIFT))
+               return;
+#endif
+
 #ifdef CONFIG_K8_NUMA
        if (!numa_off && !k8_scan_nodes(start_pfn<<PAGE_SHIFT, end_pfn<<PAGE_SHIFT))
                return;
@@ -179,11 +235,12 @@ void __init numa_initmem_init(unsigned long start_pfn, unsigned long end_pfn)
                /* setup dummy node covering all memory */ 
        memnode_shift = 63; 
        memnodemap[0] = 0;
-       numnodes = 1;
+       nodes_clear(node_online_map);
+       node_set_online(0);
        for (i = 0; i < NR_CPUS; i++)
                cpu_to_node[i] = 0;
-       node_to_cpumask[0] = 1;
-       setup_node_bootmem(0, start_pfn<<PAGE_SHIFT, end_pfn<<PAGE_SHIFT);
+       node_to_cpumask[0] = cpumask_of_cpu(0);
+       setup_node_bootmem(0, start_pfn << PAGE_SHIFT, end_pfn << PAGE_SHIFT);
 }
 
 __init void numa_add_cpu(int cpu)
@@ -197,7 +254,7 @@ unsigned long __init numa_free_all_bootmem(void)
 { 
        int i;
        unsigned long pages = 0;
-       for_all_nodes(i) {
+       for_each_online_node(i) {
                pages += free_all_bootmem_node(NODE_DATA(i));
        }
        return pages;
@@ -206,7 +263,7 @@ unsigned long __init numa_free_all_bootmem(void)
 void __init paging_init(void)
 { 
        int i;
-       for_all_nodes(i) { 
+       for_each_online_node(i) {
                setup_node_zones(i); 
        }
 } 
@@ -216,6 +273,17 @@ __init int numa_setup(char *opt)
 { 
        if (!strncmp(opt,"off",3))
                numa_off = 1;
+#ifdef CONFIG_NUMA_EMU
+       if(!strncmp(opt, "fake=", 5)) {
+               numa_fake = simple_strtoul(opt+5,NULL,0); ;
+               if (numa_fake >= MAX_NUMNODES)
+                       numa_fake = MAX_NUMNODES;
+       }
+#endif
+#ifdef CONFIG_ACPI_NUMA
+       if (!strncmp(opt,"noacpi",6))
+               acpi_numa = -1;
+#endif
        return 1;
 }