~ubuntu-branches/ubuntu/utopic/xen/utopic

« back to all changes in this revision

Viewing changes to xen/arch/x86/numa.c

  • Committer: Bazaar Package Importer
  • Author(s): Bastian Blank
  • Date: 2010-05-06 15:47:38 UTC
  • mto: (1.3.1) (15.1.1 sid) (4.1.1 experimental)
  • mto: This revision was merged to the branch mainline in revision 3.
  • Revision ID: james.westby@ubuntu.com-20100506154738-agoz0rlafrh1fnq7
Tags: upstream-4.0.0
ImportĀ upstreamĀ versionĀ 4.0.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* 
 
2
 * Generic VM initialization for x86-64 NUMA setups.
 
3
 * Copyright 2002,2003 Andi Kleen, SuSE Labs.
 
4
 * Adapted for Xen: Ryan Harper <ryanh@us.ibm.com>
 
5
 */ 
 
6
 
 
7
#include <xen/mm.h>
 
8
#include <xen/string.h>
 
9
#include <xen/init.h>
 
10
#include <xen/ctype.h>
 
11
#include <xen/nodemask.h>
 
12
#include <xen/numa.h>
 
13
#include <xen/keyhandler.h>
 
14
#include <xen/time.h>
 
15
#include <xen/smp.h>
 
16
#include <asm/acpi.h>
 
17
#include <xen/sched.h>
 
18
 
 
19
static int numa_setup(char *s);
 
20
custom_param("numa", numa_setup);
 
21
 
 
22
#ifndef Dprintk
 
23
#define Dprintk(x...)
 
24
#endif
 
25
 
 
26
/* from proto.h */
 
27
#define round_up(x,y) ((((x)+(y))-1) & (~((y)-1)))
 
28
 
 
29
struct node_data node_data[MAX_NUMNODES];
 
30
 
 
31
/* Mapping from pdx to node id */
 
32
int memnode_shift;
 
33
static typeof(*memnodemap) _memnodemap[64];
 
34
unsigned long memnodemapsize;
 
35
u8 *memnodemap;
 
36
 
 
37
unsigned char cpu_to_node[NR_CPUS] __read_mostly = {
 
38
        [0 ... NR_CPUS-1] = NUMA_NO_NODE
 
39
};
 
40
/*
 
41
 * Keep BIOS's CPU2node information, should not be used for memory allocaion
 
42
 */
 
43
unsigned char apicid_to_node[MAX_LOCAL_APIC] __cpuinitdata = {
 
44
        [0 ... MAX_LOCAL_APIC-1] = NUMA_NO_NODE
 
45
};
 
46
cpumask_t node_to_cpumask[MAX_NUMNODES] __read_mostly;
 
47
 
 
48
nodemask_t __read_mostly node_online_map = { { [0] = 1UL } };
 
49
 
 
50
int numa_off __devinitdata = 0;
 
51
 
 
52
int acpi_numa __devinitdata;
 
53
 
 
54
/*
 
55
 * Given a shift value, try to populate memnodemap[]
 
56
 * Returns :
 
57
 * 1 if OK
 
58
 * 0 if memnodmap[] too small (of shift too small)
 
59
 * -1 if node overlap or lost ram (shift too big)
 
60
 */
 
61
static int __init populate_memnodemap(const struct node *nodes,
 
62
                                      int numnodes, int shift, int *nodeids)
 
63
{
 
64
        unsigned long spdx, epdx;
 
65
        int i, res = -1;
 
66
 
 
67
        memset(memnodemap, NUMA_NO_NODE, memnodemapsize * sizeof(*memnodemap));
 
68
        for (i = 0; i < numnodes; i++) {
 
69
                spdx = paddr_to_pdx(nodes[i].start);
 
70
                epdx = paddr_to_pdx(nodes[i].end - 1) + 1;
 
71
                if (spdx >= epdx)
 
72
                        continue;
 
73
                if ((epdx >> shift) >= memnodemapsize)
 
74
                        return 0;
 
75
                do {
 
76
                        if (memnodemap[spdx >> shift] != NUMA_NO_NODE)
 
77
                                return -1;
 
78
 
 
79
                        if (!nodeids)
 
80
                                memnodemap[spdx >> shift] = i;
 
81
                        else
 
82
                                memnodemap[spdx >> shift] = nodeids[i];
 
83
 
 
84
                        spdx += (1UL << shift);
 
85
                } while (spdx < epdx);
 
86
                res = 1;
 
87
        }
 
88
        return res;
 
89
}
 
90
 
 
91
static int __init allocate_cachealigned_memnodemap(void)
 
92
{
 
93
#ifndef __i386__
 
94
        unsigned long size = PFN_UP(memnodemapsize * sizeof(*memnodemap));
 
95
        unsigned long mfn = alloc_boot_pages(size, 1);
 
96
 
 
97
        if (!mfn) {
 
98
                printk(KERN_ERR
 
99
                       "NUMA: Unable to allocate Memory to Node hash map\n");
 
100
                memnodemapsize = 0;
 
101
                return -1;
 
102
        }
 
103
 
 
104
        memnodemap = mfn_to_virt(mfn);
 
105
        mfn <<= PAGE_SHIFT;
 
106
        size <<= PAGE_SHIFT;
 
107
        printk(KERN_DEBUG "NUMA: Allocated memnodemap from %lx - %lx\n",
 
108
               mfn, mfn + size);
 
109
        memnodemapsize = size / sizeof(*memnodemap);
 
110
 
 
111
        return 0;
 
112
#else
 
113
        printk(KERN_ERR
 
114
               "Memory to Node hash needs %lu entries, got only %zu\n",
 
115
               memnodemapsize, ARRAY_SIZE(_memnodemap));
 
116
        memnodemapsize = 0;
 
117
        return -1;
 
118
#endif
 
119
}
 
120
 
 
121
/*
 
122
 * The LSB of all start and end addresses in the node map is the value of the
 
123
 * maximum possible shift.
 
124
 */
 
125
static int __init extract_lsb_from_nodes(const struct node *nodes,
 
126
                                         int numnodes)
 
127
{
 
128
        int i, nodes_used = 0;
 
129
        unsigned long spdx, epdx;
 
130
        unsigned long bitfield = 0, memtop = 0;
 
131
 
 
132
        for (i = 0; i < numnodes; i++) {
 
133
                spdx = paddr_to_pdx(nodes[i].start);
 
134
                epdx = paddr_to_pdx(nodes[i].end - 1) + 1;
 
135
                if (spdx >= epdx)
 
136
                        continue;
 
137
                bitfield |= spdx;
 
138
                nodes_used++;
 
139
                if (epdx > memtop)
 
140
                        memtop = epdx;
 
141
        }
 
142
        if (nodes_used <= 1)
 
143
                i = BITS_PER_LONG - 1;
 
144
        else
 
145
                i = find_first_bit(&bitfield, sizeof(unsigned long)*8);
 
146
        memnodemapsize = (memtop >> i) + 1;
 
147
        return i;
 
148
}
 
149
 
 
150
int __init compute_hash_shift(struct node *nodes, int numnodes,
 
151
                              int *nodeids)
 
152
{
 
153
        int shift;
 
154
 
 
155
        shift = extract_lsb_from_nodes(nodes, numnodes);
 
156
        if (memnodemapsize <= ARRAY_SIZE(_memnodemap))
 
157
                memnodemap = _memnodemap;
 
158
        else if (allocate_cachealigned_memnodemap())
 
159
                return -1;
 
160
        printk(KERN_DEBUG "NUMA: Using %d for the hash shift.\n",
 
161
                shift);
 
162
 
 
163
        if (populate_memnodemap(nodes, numnodes, shift, nodeids) != 1) {
 
164
                printk(KERN_INFO "Your memory is not aligned you need to "
 
165
                       "rebuild your kernel with a bigger NODEMAPSIZE "
 
166
                       "shift=%d\n", shift);
 
167
                return -1;
 
168
        }
 
169
        return shift;
 
170
}
 
171
/* initialize NODE_DATA given nodeid and start/end */
 
172
void __init setup_node_bootmem(int nodeid, u64 start, u64 end)
 
173
 
174
        unsigned long start_pfn, end_pfn;
 
175
 
 
176
        start_pfn = start >> PAGE_SHIFT;
 
177
        end_pfn = end >> PAGE_SHIFT;
 
178
 
 
179
        NODE_DATA(nodeid)->node_id = nodeid;
 
180
        NODE_DATA(nodeid)->node_start_pfn = start_pfn;
 
181
        NODE_DATA(nodeid)->node_spanned_pages = end_pfn - start_pfn;
 
182
 
 
183
        node_set_online(nodeid);
 
184
 
185
 
 
186
void __init numa_init_array(void)
 
187
{
 
188
        int rr, i;
 
189
        /* There are unfortunately some poorly designed mainboards around
 
190
           that only connect memory to a single CPU. This breaks the 1:1 cpu->node
 
191
           mapping. To avoid this fill in the mapping for all possible
 
192
           CPUs, as the number of CPUs is not known yet. 
 
193
           We round robin the existing nodes. */
 
194
        rr = first_node(node_online_map);
 
195
        for (i = 0; i < NR_CPUS; i++) {
 
196
                if (cpu_to_node[i] != NUMA_NO_NODE)
 
197
                        continue;
 
198
                numa_set_node(i, rr);
 
199
                rr = next_node(rr, node_online_map);
 
200
                if (rr == MAX_NUMNODES)
 
201
                        rr = first_node(node_online_map);
 
202
        }
 
203
 
 
204
}
 
205
 
 
206
#ifdef CONFIG_NUMA_EMU
 
207
static int numa_fake __initdata = 0;
 
208
 
 
209
/* Numa emulation */
 
210
static int numa_emulation(u64 start_pfn, u64 end_pfn)
 
211
{
 
212
        int i;
 
213
        struct node nodes[MAX_NUMNODES];
 
214
        u64 sz = ((end_pfn - start_pfn)<<PAGE_SHIFT) / numa_fake;
 
215
 
 
216
        /* Kludge needed for the hash function */
 
217
        if (hweight64(sz) > 1) {
 
218
                u64 x = 1;
 
219
                while ((x << 1) < sz)
 
220
                        x <<= 1;
 
221
                if (x < sz/2)
 
222
                        printk(KERN_ERR "Numa emulation unbalanced. Complain to maintainer\n");
 
223
                sz = x;
 
224
        }
 
225
 
 
226
        memset(&nodes,0,sizeof(nodes));
 
227
        for (i = 0; i < numa_fake; i++) {
 
228
                nodes[i].start = (start_pfn<<PAGE_SHIFT) + i*sz;
 
229
                if (i == numa_fake-1)
 
230
                        sz = (end_pfn<<PAGE_SHIFT) - nodes[i].start;
 
231
                nodes[i].end = nodes[i].start + sz;
 
232
                printk(KERN_INFO "Faking node %d at %"PRIx64"-%"PRIx64" (%"PRIu64"MB)\n",
 
233
                       i,
 
234
                       nodes[i].start, nodes[i].end,
 
235
                       (nodes[i].end - nodes[i].start) >> 20);
 
236
                node_set_online(i);
 
237
        }
 
238
        memnode_shift = compute_hash_shift(nodes, numa_fake, NULL);
 
239
        if (memnode_shift < 0) {
 
240
                memnode_shift = 0;
 
241
                printk(KERN_ERR "No NUMA hash function found. Emulation disabled.\n");
 
242
                return -1;
 
243
        }
 
244
        for_each_online_node(i)
 
245
                setup_node_bootmem(i, nodes[i].start, nodes[i].end);
 
246
        numa_init_array();
 
247
        return 0;
 
248
}
 
249
#endif
 
250
 
 
251
void __init numa_initmem_init(unsigned long start_pfn, unsigned long end_pfn)
 
252
 
253
        int i;
 
254
 
 
255
#ifdef CONFIG_NUMA_EMU
 
256
        if (numa_fake && !numa_emulation(start_pfn, end_pfn))
 
257
                return;
 
258
#endif
 
259
 
 
260
#ifdef CONFIG_ACPI_NUMA
 
261
        if (!numa_off && !acpi_scan_nodes((u64)start_pfn << PAGE_SHIFT,
 
262
                                          (u64)end_pfn << PAGE_SHIFT))
 
263
                return;
 
264
#endif
 
265
 
 
266
        printk(KERN_INFO "%s\n",
 
267
               numa_off ? "NUMA turned off" : "No NUMA configuration found");
 
268
 
 
269
        printk(KERN_INFO "Faking a node at %016"PRIx64"-%016"PRIx64"\n",
 
270
               (u64)start_pfn << PAGE_SHIFT,
 
271
               (u64)end_pfn << PAGE_SHIFT);
 
272
        /* setup dummy node covering all memory */ 
 
273
        memnode_shift = BITS_PER_LONG - 1;
 
274
        memnodemap = _memnodemap;
 
275
        nodes_clear(node_online_map);
 
276
        node_set_online(0);
 
277
        for (i = 0; i < NR_CPUS; i++)
 
278
                numa_set_node(i, 0);
 
279
        node_to_cpumask[0] = cpumask_of_cpu(0);
 
280
        setup_node_bootmem(0, (u64)start_pfn << PAGE_SHIFT, (u64)end_pfn << PAGE_SHIFT);
 
281
}
 
282
 
 
283
__cpuinit void numa_add_cpu(int cpu)
 
284
{
 
285
        cpu_set(cpu, node_to_cpumask[cpu_to_node(cpu)]);
 
286
 
287
 
 
288
void __cpuinit numa_set_node(int cpu, int node)
 
289
{
 
290
        cpu_to_node[cpu] = node;
 
291
}
 
292
 
 
293
/* [numa=off] */
 
294
static __init int numa_setup(char *opt) 
 
295
 
296
        if (!strncmp(opt,"off",3))
 
297
                numa_off = 1;
 
298
        if (!strncmp(opt,"on",2))
 
299
                numa_off = 0;
 
300
#ifdef CONFIG_NUMA_EMU
 
301
        if(!strncmp(opt, "fake=", 5)) {
 
302
                numa_off = 0;
 
303
                numa_fake = simple_strtoul(opt+5,NULL,0); ;
 
304
                if (numa_fake >= MAX_NUMNODES)
 
305
                        numa_fake = MAX_NUMNODES;
 
306
        }
 
307
#endif
 
308
#ifdef CONFIG_ACPI_NUMA
 
309
        if (!strncmp(opt,"noacpi",6)) {
 
310
                numa_off = 0;
 
311
                acpi_numa = -1;
 
312
        }
 
313
#endif
 
314
        return 1;
 
315
 
316
 
 
317
/*
 
318
 * Setup early cpu_to_node.
 
319
 *
 
320
 * Populate cpu_to_node[] only if x86_cpu_to_apicid[],
 
321
 * and apicid_to_node[] tables have valid entries for a CPU.
 
322
 * This means we skip cpu_to_node[] initialisation for NUMA
 
323
 * emulation and faking node case (when running a kernel compiled
 
324
 * for NUMA on a non NUMA box), which is OK as cpu_to_node[]
 
325
 * is already initialized in a round robin manner at numa_init_array,
 
326
 * prior to this call, and this initialization is good enough
 
327
 * for the fake NUMA cases.
 
328
 */
 
329
void __devinit init_cpu_to_node(void)
 
330
{
 
331
        int i, node;
 
332
        for (i = 0; i < NR_CPUS; i++) {
 
333
                u32 apicid = x86_cpu_to_apicid[i];
 
334
                if (apicid == BAD_APICID)
 
335
                        continue;
 
336
                node = apicid_to_node[apicid];
 
337
                if ( node == NUMA_NO_NODE || !node_online(node) )
 
338
                        node = 0;
 
339
                numa_set_node(i, node);
 
340
        }
 
341
}
 
342
 
 
343
EXPORT_SYMBOL(cpu_to_node);
 
344
EXPORT_SYMBOL(node_to_cpumask);
 
345
EXPORT_SYMBOL(memnode_shift);
 
346
EXPORT_SYMBOL(memnodemap);
 
347
EXPORT_SYMBOL(node_data);
 
348
 
 
349
static void dump_numa(unsigned char key)
 
350
{
 
351
        s_time_t now = NOW();
 
352
        int i;
 
353
        struct domain *d;
 
354
        struct page_info *page;
 
355
        unsigned int page_num_node[MAX_NUMNODES];
 
356
 
 
357
        printk("'%c' pressed -> dumping numa info (now-0x%X:%08X)\n", key,
 
358
                  (u32)(now>>32), (u32)now);
 
359
 
 
360
        for_each_online_node(i) {
 
361
                paddr_t pa = (paddr_t)(NODE_DATA(i)->node_start_pfn + 1)<< PAGE_SHIFT;
 
362
                printk("idx%d -> NODE%d start->%lu size->%lu\n",
 
363
                          i, NODE_DATA(i)->node_id,
 
364
                          NODE_DATA(i)->node_start_pfn,
 
365
                          NODE_DATA(i)->node_spanned_pages);
 
366
                /* sanity check phys_to_nid() */
 
367
                printk("phys_to_nid(%"PRIpaddr") -> %d should be %d\n", pa, phys_to_nid(pa),
 
368
                          NODE_DATA(i)->node_id);
 
369
        }
 
370
        for_each_online_cpu(i)
 
371
                printk("CPU%d -> NODE%d\n", i, cpu_to_node[i]);
 
372
 
 
373
        rcu_read_lock(&domlist_read_lock);
 
374
 
 
375
        printk("Memory location of each domain:\n");
 
376
        for_each_domain(d)
 
377
        {
 
378
                printk("Domain %u (total: %u):\n", d->domain_id, d->tot_pages);
 
379
 
 
380
                for_each_online_node(i)
 
381
                        page_num_node[i] = 0;
 
382
 
 
383
                page_list_for_each(page, &d->page_list)
 
384
                {
 
385
                        i = phys_to_nid((paddr_t)page_to_mfn(page) << PAGE_SHIFT);
 
386
                        page_num_node[i]++;
 
387
                }
 
388
 
 
389
                for_each_online_node(i)
 
390
                        printk("    Node %u: %u\n", i, page_num_node[i]);
 
391
        }
 
392
 
 
393
        rcu_read_unlock(&domlist_read_lock);
 
394
}
 
395
 
 
396
static struct keyhandler dump_numa_keyhandler = {
 
397
        .diagnostic = 1,
 
398
        .u.fn = dump_numa,
 
399
        .desc = "dump numa info"
 
400
};
 
401
 
 
402
static __init int register_numa_trigger(void)
 
403
{
 
404
        register_keyhandler('u', &dump_numa_keyhandler);
 
405
        return 0;
 
406
}
 
407
__initcall(register_numa_trigger);
 
408