blob: db6b073a149ffb7bbc4eae0641052e38a4e15c27 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * ACPI 3.0 based NUMA setup
3 * Copyright 2004 Andi Kleen, SuSE Labs.
4 *
5 * Reads the ACPI SRAT table to figure out what memory belongs to which CPUs.
6 *
7 * Called from acpi_numa_init while reading the SRAT and SLIT tables.
8 * Assumes all memory regions belonging to a single proximity domain
9 * are in one chunk. Holes between them will be included in the node.
10 */
11
12#include <linux/kernel.h>
13#include <linux/acpi.h>
14#include <linux/mmzone.h>
15#include <linux/bitmap.h>
16#include <linux/module.h>
17#include <linux/topology.h>
18#include <asm/proto.h>
19#include <asm/numa.h>
20
21static struct acpi_table_slit *acpi_slit;
22
23static nodemask_t nodes_parsed __initdata;
24static nodemask_t nodes_found __initdata;
25static struct node nodes[MAX_NUMNODES] __initdata;
26static __u8 pxm2node[256] = { [0 ... 255] = 0xff };
27
Andi Kleen69e1a332005-09-12 18:49:24 +020028int pxm_to_node(int pxm)
29{
30 if ((unsigned)pxm >= 256)
31 return 0;
32 return pxm2node[pxm];
33}
34
Linus Torvalds1da177e2005-04-16 15:20:36 -070035static __init int setup_node(int pxm)
36{
37 unsigned node = pxm2node[pxm];
38 if (node == 0xff) {
39 if (nodes_weight(nodes_found) >= MAX_NUMNODES)
40 return -1;
41 node = first_unset_node(nodes_found);
42 node_set(node, nodes_found);
43 pxm2node[pxm] = node;
44 }
45 return pxm2node[pxm];
46}
47
48static __init int conflicting_nodes(unsigned long start, unsigned long end)
49{
50 int i;
51 for_each_online_node(i) {
52 struct node *nd = &nodes[i];
53 if (nd->start == nd->end)
54 continue;
55 if (nd->end > start && nd->start < end)
56 return 1;
57 if (nd->end == end && nd->start == start)
58 return 1;
59 }
60 return -1;
61}
62
63static __init void cutoff_node(int i, unsigned long start, unsigned long end)
64{
65 struct node *nd = &nodes[i];
66 if (nd->start < start) {
67 nd->start = start;
68 if (nd->end < nd->start)
69 nd->start = nd->end;
70 }
71 if (nd->end > end) {
72 if (!(end & 0xfff))
73 end--;
74 nd->end = end;
75 if (nd->start > nd->end)
76 nd->start = nd->end;
77 }
78}
79
80static __init void bad_srat(void)
81{
82 printk(KERN_ERR "SRAT: SRAT not used.\n");
83 acpi_numa = -1;
84}
85
86static __init inline int srat_disabled(void)
87{
88 return numa_off || acpi_numa < 0;
89}
90
91/* Callback for SLIT parsing */
92void __init acpi_numa_slit_init(struct acpi_table_slit *slit)
93{
94 acpi_slit = slit;
95}
96
97/* Callback for Proximity Domain -> LAPIC mapping */
98void __init
99acpi_numa_processor_affinity_init(struct acpi_table_processor_affinity *pa)
100{
101 int pxm, node;
102 if (srat_disabled() || pa->flags.enabled == 0)
103 return;
104 pxm = pa->proximity_domain;
105 node = setup_node(pxm);
106 if (node < 0) {
107 printk(KERN_ERR "SRAT: Too many proximity domains %x\n", pxm);
108 bad_srat();
109 return;
110 }
Andi Kleen0b07e982005-09-12 18:49:24 +0200111 apicid_to_node[pa->apic_id] = node;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700112 acpi_numa = 1;
Andi Kleen0b07e982005-09-12 18:49:24 +0200113 printk(KERN_INFO "SRAT: PXM %u -> APIC %u -> Node %u\n",
114 pxm, pa->apic_id, node);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700115}
116
117/* Callback for parsing of the Proximity Domain <-> Memory Area mappings */
118void __init
119acpi_numa_memory_affinity_init(struct acpi_table_memory_affinity *ma)
120{
121 struct node *nd;
122 unsigned long start, end;
123 int node, pxm;
124 int i;
125
126 if (srat_disabled() || ma->flags.enabled == 0)
127 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700128 pxm = ma->proximity_domain;
129 node = setup_node(pxm);
130 if (node < 0) {
131 printk(KERN_ERR "SRAT: Too many proximity domains.\n");
132 bad_srat();
133 return;
134 }
135 start = ma->base_addr_lo | ((u64)ma->base_addr_hi << 32);
136 end = start + (ma->length_lo | ((u64)ma->length_hi << 32));
Andi Kleen69cb62e2005-07-28 21:15:39 -0700137 /* It is fine to add this area to the nodes data it will be used later*/
138 if (ma->flags.hot_pluggable == 1)
139 printk(KERN_INFO "SRAT: hot plug zone found %lx - %lx \n",
140 start, end);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700141 i = conflicting_nodes(start, end);
142 if (i >= 0) {
143 printk(KERN_ERR
144 "SRAT: pxm %d overlap %lx-%lx with node %d(%Lx-%Lx)\n",
145 pxm, start, end, i, nodes[i].start, nodes[i].end);
146 bad_srat();
147 return;
148 }
149 nd = &nodes[node];
150 if (!node_test_and_set(node, nodes_parsed)) {
151 nd->start = start;
152 nd->end = end;
153 } else {
154 if (start < nd->start)
155 nd->start = start;
156 if (nd->end < end)
157 nd->end = end;
158 }
159 if (!(nd->end & 0xfff))
160 nd->end--;
161 printk(KERN_INFO "SRAT: Node %u PXM %u %Lx-%Lx\n", node, pxm,
162 nd->start, nd->end);
163}
164
165void __init acpi_numa_arch_fixup(void) {}
166
167/* Use the information discovered above to actually set up the nodes. */
168int __init acpi_scan_nodes(unsigned long start, unsigned long end)
169{
170 int i;
171 if (acpi_numa <= 0)
172 return -1;
173 memnode_shift = compute_hash_shift(nodes, nodes_weight(nodes_parsed));
174 if (memnode_shift < 0) {
175 printk(KERN_ERR
176 "SRAT: No NUMA node hash function found. Contact maintainer\n");
177 bad_srat();
178 return -1;
179 }
180 for (i = 0; i < MAX_NUMNODES; i++) {
181 if (!node_isset(i, nodes_parsed))
182 continue;
183 cutoff_node(i, start, end);
184 if (nodes[i].start == nodes[i].end) {
185 node_clear(i, nodes_parsed);
186 continue;
187 }
188 setup_node_bootmem(i, nodes[i].start, nodes[i].end);
189 }
190 for (i = 0; i < NR_CPUS; i++) {
191 if (cpu_to_node[i] == NUMA_NO_NODE)
192 continue;
193 if (!node_isset(cpu_to_node[i], nodes_parsed))
194 cpu_to_node[i] = NUMA_NO_NODE;
195 }
196 numa_init_array();
197 return 0;
198}
199
200int node_to_pxm(int n)
201{
202 int i;
203 if (pxm2node[n] == n)
204 return n;
205 for (i = 0; i < 256; i++)
206 if (pxm2node[i] == n)
207 return i;
208 return 0;
209}
210
211int __node_distance(int a, int b)
212{
213 int index;
214
215 if (!acpi_slit)
216 return a == b ? 10 : 20;
217 index = acpi_slit->localities * node_to_pxm(a);
218 return acpi_slit->entry[index + node_to_pxm(b)];
219}
220
221EXPORT_SYMBOL(__node_distance);