kernel.org linux-2.6.10
[linux-2.6.git] / arch / x86_64 / mm / numa.c
1 /* 
2  * Generic VM initialization for x86-64 NUMA setups.
3  * Copyright 2002,2003 Andi Kleen, SuSE Labs.
4  */ 
5 #include <linux/kernel.h>
6 #include <linux/mm.h>
7 #include <linux/string.h>
8 #include <linux/init.h>
9 #include <linux/bootmem.h>
10 #include <linux/mmzone.h>
11 #include <linux/ctype.h>
12 #include <linux/module.h>
13 #include <linux/nodemask.h>
14
15 #include <asm/e820.h>
16 #include <asm/proto.h>
17 #include <asm/dma.h>
18 #include <asm/numa.h>
19
20 #ifndef Dprintk
21 #define Dprintk(x...)
22 #endif
23
24 struct pglist_data *node_data[MAXNODE];
25 bootmem_data_t plat_node_bdata[MAX_NUMNODES];
26
27 int memnode_shift;
28 u8  memnodemap[NODEMAPSIZE];
29
30 unsigned char cpu_to_node[NR_CPUS];  
31 cpumask_t     node_to_cpumask[MAXNODE]; 
32
33 static int numa_off __initdata; 
34
35 unsigned long nodes_present; 
36
37 int __init compute_hash_shift(struct node *nodes)
38 {
39         int i; 
40         int shift = 24;
41         u64 addr;
42         
43         /* When in doubt use brute force. */
44         while (shift < 48) { 
45                 memset(memnodemap,0xff,sizeof(*memnodemap) * NODEMAPSIZE); 
46                 for (i = 0; i < numnodes; i++) { 
47                         if (nodes[i].start == nodes[i].end) 
48                                 continue;
49                         for (addr = nodes[i].start; 
50                              addr < nodes[i].end; 
51                              addr += (1UL << shift)) {
52                                 if (memnodemap[addr >> shift] != 0xff && 
53                                     memnodemap[addr >> shift] != i) { 
54                                         printk(KERN_INFO 
55                                             "node %d shift %d addr %Lx conflict %d\n", 
56                                                i, shift, addr, memnodemap[addr>>shift]);
57                                         goto next; 
58                                 } 
59                                 memnodemap[addr >> shift] = i; 
60                         } 
61                 } 
62                 return shift; 
63         next:
64                 shift++; 
65         } 
66         memset(memnodemap,0,sizeof(*memnodemap) * NODEMAPSIZE); 
67         return -1; 
68 }
69
70 /* Initialize bootmem allocator for a node */
71 void __init setup_node_bootmem(int nodeid, unsigned long start, unsigned long end)
72
73         unsigned long start_pfn, end_pfn, bootmap_pages, bootmap_size, bootmap_start; 
74         unsigned long nodedata_phys;
75         const int pgdat_size = round_up(sizeof(pg_data_t), PAGE_SIZE);
76
77         start = round_up(start, ZONE_ALIGN); 
78
79         printk("Bootmem setup node %d %016lx-%016lx\n", nodeid, start, end);
80
81         start_pfn = start >> PAGE_SHIFT;
82         end_pfn = end >> PAGE_SHIFT;
83
84         nodedata_phys = find_e820_area(start, end, pgdat_size); 
85         if (nodedata_phys == -1L) 
86                 panic("Cannot find memory pgdat in node %d\n", nodeid);
87
88         Dprintk("nodedata_phys %lx\n", nodedata_phys); 
89
90         node_data[nodeid] = phys_to_virt(nodedata_phys);
91         memset(NODE_DATA(nodeid), 0, sizeof(pg_data_t));
92         NODE_DATA(nodeid)->bdata = &plat_node_bdata[nodeid];
93         NODE_DATA(nodeid)->node_start_pfn = start_pfn;
94         NODE_DATA(nodeid)->node_spanned_pages = end_pfn - start_pfn;
95
96         /* Find a place for the bootmem map */
97         bootmap_pages = bootmem_bootmap_pages(end_pfn - start_pfn); 
98         bootmap_start = round_up(nodedata_phys + pgdat_size, PAGE_SIZE);
99         bootmap_start = find_e820_area(bootmap_start, end, bootmap_pages<<PAGE_SHIFT);
100         if (bootmap_start == -1L) 
101                 panic("Not enough continuous space for bootmap on node %d", nodeid); 
102         Dprintk("bootmap start %lu pages %lu\n", bootmap_start, bootmap_pages); 
103         
104         bootmap_size = init_bootmem_node(NODE_DATA(nodeid),
105                                          bootmap_start >> PAGE_SHIFT, 
106                                          start_pfn, end_pfn); 
107
108         e820_bootmem_free(NODE_DATA(nodeid), start, end);
109
110         reserve_bootmem_node(NODE_DATA(nodeid), nodedata_phys, pgdat_size); 
111         reserve_bootmem_node(NODE_DATA(nodeid), bootmap_start, bootmap_pages<<PAGE_SHIFT);
112         if (nodeid + 1 > numnodes)
113                 numnodes = nodeid + 1;
114         node_set_online(nodeid);
115
116
117 /* Initialize final allocator for a zone */
118 void __init setup_node_zones(int nodeid)
119
120         unsigned long start_pfn, end_pfn; 
121         unsigned long zones[MAX_NR_ZONES];
122         unsigned long dma_end_pfn;
123
124         memset(zones, 0, sizeof(unsigned long) * MAX_NR_ZONES); 
125
126         start_pfn = node_start_pfn(nodeid);
127         end_pfn = node_end_pfn(nodeid);
128
129         Dprintk(KERN_INFO "setting up node %d %lx-%lx\n", nodeid, start_pfn, end_pfn);
130         
131         /* All nodes > 0 have a zero length zone DMA */ 
132         dma_end_pfn = __pa(MAX_DMA_ADDRESS) >> PAGE_SHIFT; 
133         if (start_pfn < dma_end_pfn) { 
134                 zones[ZONE_DMA] = dma_end_pfn - start_pfn;
135                 zones[ZONE_NORMAL] = end_pfn - dma_end_pfn; 
136         } else { 
137                 zones[ZONE_NORMAL] = end_pfn - start_pfn; 
138         } 
139     
140         free_area_init_node(nodeid, NODE_DATA(nodeid), zones,
141                             start_pfn, NULL); 
142
143
144 void __init numa_init_array(void)
145 {
146         int rr, i;
147         /* There are unfortunately some poorly designed mainboards around
148            that only connect memory to a single CPU. This breaks the 1:1 cpu->node
149            mapping. To avoid this fill in the mapping for all possible
150            CPUs, as the number of CPUs is not known yet. 
151            We round robin the existing nodes. */
152         rr = 0;
153         for (i = 0; i < MAXNODE; i++) {
154                 if (node_online(i))
155                         continue;
156                 rr = next_node(rr, node_online_map);
157                 if (rr == MAX_NUMNODES)
158                         rr = first_node(node_online_map);
159                 node_data[i] = node_data[rr];
160                 cpu_to_node[i] = rr;
161                 rr++; 
162         }
163
164         set_bit(0, &node_to_cpumask[cpu_to_node(0)]);
165 }
166
167 #ifdef CONFIG_NUMA_EMU
168 int numa_fake __initdata = 0;
169
170 /* Numa emulation */
171 static int numa_emulation(unsigned long start_pfn, unsigned long end_pfn)
172 {
173         int i;
174         struct node nodes[MAXNODE];
175         unsigned long sz = ((end_pfn - start_pfn)<<PAGE_SHIFT) / numa_fake;
176
177         /* Kludge needed for the hash function */
178         if (hweight64(sz) > 1) {
179                 unsigned long x = 1;
180                 while ((x << 1) < sz)
181                         x <<= 1;
182                 if (x < sz/2)
183                         printk("Numa emulation unbalanced. Complain to maintainer\n");
184                 sz = x;
185         }
186
187         memset(&nodes,0,sizeof(nodes));
188         for (i = 0; i < numa_fake; i++) {
189                 nodes[i].start = (start_pfn<<PAGE_SHIFT) + i*sz;
190                 if (i == numa_fake-1)
191                         sz = (end_pfn<<PAGE_SHIFT) - nodes[i].start;
192                 nodes[i].end = nodes[i].start + sz;
193                 if (i != numa_fake-1)
194                         nodes[i].end--;
195                 printk(KERN_INFO "Faking node %d at %016Lx-%016Lx (%LuMB)\n",
196                        i,
197                        nodes[i].start, nodes[i].end,
198                        (nodes[i].end - nodes[i].start) >> 20);
199         }
200         numnodes = numa_fake;
201         memnode_shift = compute_hash_shift(nodes);
202         if (memnode_shift < 0) {
203                 memnode_shift = 0;
204                 printk(KERN_ERR "No NUMA hash function found. Emulation disabled.\n");
205                 return -1;
206         }
207         for (i = 0; i < numa_fake; i++)
208                 setup_node_bootmem(i, nodes[i].start, nodes[i].end);
209         numa_init_array();
210         return 0;
211 }
212 #endif
213
214 void __init numa_initmem_init(unsigned long start_pfn, unsigned long end_pfn)
215
216         int i;
217
218 #ifdef CONFIG_NUMA_EMU
219         if (numa_fake && !numa_emulation(start_pfn, end_pfn))
220                 return;
221 #endif
222
223 #ifdef CONFIG_K8_NUMA
224         if (!numa_off && !k8_scan_nodes(start_pfn<<PAGE_SHIFT, end_pfn<<PAGE_SHIFT))
225                 return;
226 #endif
227         printk(KERN_INFO "%s\n",
228                numa_off ? "NUMA turned off" : "No NUMA configuration found");
229
230         printk(KERN_INFO "Faking a node at %016lx-%016lx\n", 
231                start_pfn << PAGE_SHIFT,
232                end_pfn << PAGE_SHIFT); 
233                 /* setup dummy node covering all memory */ 
234         memnode_shift = 63; 
235         memnodemap[0] = 0;
236         numnodes = 1;
237         for (i = 0; i < NR_CPUS; i++)
238                 cpu_to_node[i] = 0;
239         node_to_cpumask[0] = cpumask_of_cpu(0);
240         setup_node_bootmem(0, start_pfn<<PAGE_SHIFT, end_pfn<<PAGE_SHIFT);
241 }
242
243 __init void numa_add_cpu(int cpu)
244 {
245         /* BP is initialized elsewhere */
246         if (cpu) 
247                 set_bit(cpu, &node_to_cpumask[cpu_to_node(cpu)]);
248
249
250 unsigned long __init numa_free_all_bootmem(void) 
251
252         int i;
253         unsigned long pages = 0;
254         for_all_nodes(i) {
255                 pages += free_all_bootmem_node(NODE_DATA(i));
256         }
257         return pages;
258
259
260 void __init paging_init(void)
261
262         int i;
263         for_all_nodes(i) { 
264                 setup_node_zones(i); 
265         }
266
267
268 /* [numa=off] */
269 __init int numa_setup(char *opt) 
270
271         if (!strcmp(opt,"off"))
272                 numa_off = 1;
273 #ifdef CONFIG_NUMA_EMU
274         if(!strncmp(opt, "fake=", 5)) {
275                 numa_fake = simple_strtoul(opt+5,NULL,0); ;
276                 if (numa_fake >= MAX_NUMNODES)
277                         numa_fake = MAX_NUMNODES;
278         }
279 #endif
280         return 1;
281
282
283 EXPORT_SYMBOL(cpu_to_node);
284 EXPORT_SYMBOL(node_to_cpumask);
285 EXPORT_SYMBOL(memnode_shift);
286 EXPORT_SYMBOL(memnodemap);
287 EXPORT_SYMBOL(node_data);