linux 2.6.16.38 w/ vs2.0.3-rc1
[linux-2.6.git] / arch / x86_64 / mm / numa.c
index fd9f25d..22e51be 100644 (file)
 #define Dprintk(x...)
 #endif
 
-struct pglist_data *node_data[MAX_NUMNODES];
+struct pglist_data *node_data[MAX_NUMNODES] __read_mostly;
 bootmem_data_t plat_node_bdata[MAX_NUMNODES];
 
 int memnode_shift;
 u8  memnodemap[NODEMAPSIZE];
 
-unsigned char cpu_to_node[NR_CPUS] = { [0 ... NR_CPUS-1] = NUMA_NO_NODE };
-cpumask_t     node_to_cpumask[MAX_NUMNODES];
+unsigned char cpu_to_node[NR_CPUS] __read_mostly = {
+       [0 ... NR_CPUS-1] = NUMA_NO_NODE
+};
+unsigned char apicid_to_node[MAX_LOCAL_APIC] __cpuinitdata = {
+       [0 ... MAX_LOCAL_APIC-1] = NUMA_NO_NODE
+};
+cpumask_t node_to_cpumask[MAX_NUMNODES] __read_mostly;
 
 int numa_off __initdata;
 
-int __init compute_hash_shift(struct node *nodes, int numnodes)
+
+/*
+ * Given a shift value, try to populate memnodemap[]
+ * Returns :
+ * 1 if OK
+ * 0 if memnodmap[] too small (of shift too small)
+ * -1 if node overlap or lost ram (shift too big)
+ */
+static int __init
+populate_memnodemap(const struct node *nodes, int numnodes, int shift)
 {
        int i; 
-       int shift = 24;
-       u64 addr;
-       
-       /* When in doubt use brute force. */
-       while (shift < 48) { 
-               memset(memnodemap,0xff,sizeof(*memnodemap) * NODEMAPSIZE); 
-               for (i = 0; i < numnodes; i++) {
-                       if (nodes[i].start == nodes[i].end) 
-                               continue;
-                       for (addr = nodes[i].start; 
-                            addr < nodes[i].end; 
-                            addr += (1UL << shift)) {
-                               if (memnodemap[addr >> shift] != 0xff && 
-                                   memnodemap[addr >> shift] != i) { 
-                                       printk(KERN_INFO 
-                                           "node %d shift %d addr %Lx conflict %d\n", 
-                                              i, shift, addr, memnodemap[addr>>shift]);
-                                       goto next; 
-                               } 
-                               memnodemap[addr >> shift] = i; 
-                       } 
-               } 
-               return shift; 
-       next:
-               shift++; 
+       int res = -1;
+       unsigned long addr, end;
+
+       if (shift >= 64)
+               return -1;
+       memset(memnodemap, 0xff, sizeof(memnodemap));
+       for (i = 0; i < numnodes; i++) {
+               addr = nodes[i].start;
+               end = nodes[i].end;
+               if (addr >= end)
+                       continue;
+               if ((end >> shift) >= NODEMAPSIZE)
+                       return 0;
+               do {
+                       if (memnodemap[addr >> shift] != 0xff)
+                               return -1;
+                       memnodemap[addr >> shift] = i;
+                       addr += (1UL << shift);
+               } while (addr < end);
+               res = 1;
        } 
-       memset(memnodemap,0,sizeof(*memnodemap) * NODEMAPSIZE); 
-       return -1; 
+       return res;
+}
+
+int __init compute_hash_shift(struct node *nodes, int numnodes)
+{
+       int shift = 20;
+
+       while (populate_memnodemap(nodes, numnodes, shift + 1) >= 0)
+               shift++;
+
+       printk(KERN_DEBUG "NUMA: Using %d for the hash shift.\n",
+               shift);
+
+       if (populate_memnodemap(nodes, numnodes, shift) != 1) {
+               printk(KERN_INFO
+       "Your memory is not aligned you need to rebuild your kernel "
+       "with a bigger NODEMAPSIZE shift=%d\n",
+                       shift);
+               return -1;
+       }
+       return shift;
 }
 
+#ifdef CONFIG_SPARSEMEM
+int early_pfn_to_nid(unsigned long pfn)
+{
+       return phys_to_nid(pfn << PAGE_SHIFT);
+}
+#endif
+
 /* Initialize bootmem allocator for a node */
 void __init setup_node_bootmem(int nodeid, unsigned long start, unsigned long end)
 { 
@@ -75,7 +110,7 @@ void __init setup_node_bootmem(int nodeid, unsigned long start, unsigned long en
 
        start = round_up(start, ZONE_ALIGN); 
 
-       printk("Bootmem setup node %d %016lx-%016lx\n", nodeid, start, end);
+       printk(KERN_INFO "Bootmem setup node %d %016lx-%016lx\n", nodeid, start, end);
 
        start_pfn = start >> PAGE_SHIFT;
        end_pfn = end >> PAGE_SHIFT;
@@ -116,26 +151,17 @@ void __init setup_node_zones(int nodeid)
 { 
        unsigned long start_pfn, end_pfn; 
        unsigned long zones[MAX_NR_ZONES];
-       unsigned long dma_end_pfn;
+       unsigned long holes[MAX_NR_ZONES];
 
-       memset(zones, 0, sizeof(unsigned long) * MAX_NR_ZONES); 
+       start_pfn = node_start_pfn(nodeid);
+       end_pfn = node_end_pfn(nodeid);
 
-       start_pfn = node_start_pfn(nodeid);
-       end_pfn = node_end_pfn(nodeid);
+       Dprintk(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; 
-       if (start_pfn < dma_end_pfn) { 
-               zones[ZONE_DMA] = dma_end_pfn - start_pfn;
-               zones[ZONE_NORMAL] = end_pfn - dma_end_pfn; 
-       } else { 
-               zones[ZONE_NORMAL] = end_pfn - start_pfn; 
-       } 
-    
+       size_zones(zones, holes, start_pfn, end_pfn);
        free_area_init_node(nodeid, NODE_DATA(nodeid), zones,
-                           start_pfn, NULL); 
+                           start_pfn, holes);
 } 
 
 void __init numa_init_array(void)
@@ -146,18 +172,16 @@ void __init numa_init_array(void)
           mapping. To avoid this fill in the mapping for all possible
           CPUs, as the number of CPUs is not known yet. 
           We round robin the existing nodes. */
-       rr = 0;
+       rr = first_node(node_online_map);
        for (i = 0; i < NR_CPUS; i++) {
                if (cpu_to_node[i] != NUMA_NO_NODE)
                        continue;
+               numa_set_node(i, rr);
                rr = next_node(rr, node_online_map);
                if (rr == MAX_NUMNODES)
                        rr = first_node(node_online_map);
-               cpu_to_node[i] = rr;
-               rr++; 
        }
 
-       set_bit(0, &node_to_cpumask[cpu_to_node(0)]);
 }
 
 #ifdef CONFIG_NUMA_EMU
@@ -176,7 +200,7 @@ static int numa_emulation(unsigned long start_pfn, unsigned long end_pfn)
                while ((x << 1) < sz)
                        x <<= 1;
                if (x < sz/2)
-                       printk("Numa emulation unbalanced. Complain to maintainer\n");
+                       printk(KERN_ERR "Numa emulation unbalanced. Complain to maintainer\n");
                sz = x;
        }
 
@@ -186,8 +210,6 @@ static int numa_emulation(unsigned long start_pfn, unsigned long end_pfn)
                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,
@@ -238,18 +260,22 @@ void __init numa_initmem_init(unsigned long start_pfn, unsigned long end_pfn)
        nodes_clear(node_online_map);
        node_set_online(0);
        for (i = 0; i < NR_CPUS; i++)
-               cpu_to_node[i] = 0;
+               numa_set_node(i, 0);
        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)
+__cpuinit void numa_add_cpu(int cpu)
 {
-       /* BP is initialized elsewhere */
-       if (cpu) 
-               set_bit(cpu, &node_to_cpumask[cpu_to_node(cpu)]);
+       set_bit(cpu, &node_to_cpumask[cpu_to_node(cpu)]);
 } 
 
+void __cpuinit numa_set_node(int cpu, int node)
+{
+       cpu_pda(cpu)->nodenumber = node;
+       cpu_to_node[cpu] = node;
+}
+
 unsigned long __init numa_free_all_bootmem(void) 
 { 
        int i;
@@ -260,9 +286,26 @@ unsigned long __init numa_free_all_bootmem(void)
        return pages;
 } 
 
+#ifdef CONFIG_SPARSEMEM
+static void __init arch_sparse_init(void)
+{
+       int i;
+
+       for_each_online_node(i)
+               memory_present(i, node_start_pfn(i), node_end_pfn(i));
+
+       sparse_init();
+}
+#else
+#define arch_sparse_init() do {} while (0)
+#endif
+
 void __init paging_init(void)
 { 
        int i;
+
+       arch_sparse_init();
+
        for_each_online_node(i) {
                setup_node_zones(i); 
        }
@@ -287,8 +330,69 @@ __init int numa_setup(char *opt)
        return 1;
 } 
 
+/*
+ * Setup early cpu_to_node.
+ *
+ * Populate cpu_to_node[] only if x86_cpu_to_apicid[],
+ * and apicid_to_node[] tables have valid entries for a CPU.
+ * This means we skip cpu_to_node[] initialisation for NUMA
+ * emulation and faking node case (when running a kernel compiled
+ * for NUMA on a non NUMA box), which is OK as cpu_to_node[]
+ * is already initialized in a round robin manner at numa_init_array,
+ * prior to this call, and this initialization is good enough
+ * for the fake NUMA cases.
+ */
+void __init init_cpu_to_node(void)
+{
+       int i;
+       for (i = 0; i < NR_CPUS; i++) {
+               u8 apicid = x86_cpu_to_apicid[i];
+               if (apicid == BAD_APICID)
+                       continue;
+               if (apicid_to_node[apicid] == NUMA_NO_NODE)
+                       continue;
+               numa_set_node(i,apicid_to_node[apicid]);
+       }
+}
+
 EXPORT_SYMBOL(cpu_to_node);
 EXPORT_SYMBOL(node_to_cpumask);
 EXPORT_SYMBOL(memnode_shift);
 EXPORT_SYMBOL(memnodemap);
 EXPORT_SYMBOL(node_data);
+
+#ifdef CONFIG_DISCONTIGMEM
+/*
+ * Functions to convert PFNs from/to per node page addresses.
+ * These are out of line because they are quite big.
+ * They could be all tuned by pre caching more state.
+ * Should do that.
+ */
+
+/* Requires pfn_valid(pfn) to be true */
+struct page *pfn_to_page(unsigned long pfn)
+{
+       int nid = phys_to_nid(((unsigned long)(pfn)) << PAGE_SHIFT);
+       return (pfn - node_start_pfn(nid)) + NODE_DATA(nid)->node_mem_map;
+}
+EXPORT_SYMBOL(pfn_to_page);
+
+unsigned long page_to_pfn(struct page *page)
+{
+       return (long)(((page) - page_zone(page)->zone_mem_map) +
+                     page_zone(page)->zone_start_pfn);
+}
+EXPORT_SYMBOL(page_to_pfn);
+
+int pfn_valid(unsigned long pfn)
+{
+       unsigned nid;
+       if (pfn >= num_physpages)
+               return 0;
+       nid = pfn_to_nid(pfn);
+       if (nid == 0xff)
+               return 0;
+       return pfn >= node_start_pfn(nid) && (pfn) < node_end_pfn(nid);
+}
+EXPORT_SYMBOL(pfn_valid);
+#endif