Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
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> | |
68a3a7fe | 18 | #include <linux/bootmem.h> |
a9ce6bc1 | 19 | #include <linux/memblock.h> |
68a3a7fe | 20 | #include <linux/mm.h> |
1da177e4 LT |
21 | #include <asm/proto.h> |
22 | #include <asm/numa.h> | |
8a6fdd3e | 23 | #include <asm/e820.h> |
7b6aa335 | 24 | #include <asm/apic.h> |
4ec71fa2 | 25 | #include <asm/uv/uv.h> |
1da177e4 | 26 | |
c31fbb1a AK |
27 | int acpi_numa __initdata; |
28 | ||
4942e998 | 29 | static struct bootnode nodes_add[MAX_NUMNODES]; |
1da177e4 LT |
30 | |
31 | static __init int setup_node(int pxm) | |
32 | { | |
762834e8 | 33 | return acpi_map_pxm_to_node(pxm); |
1da177e4 LT |
34 | } |
35 | ||
1da177e4 LT |
36 | static __init void bad_srat(void) |
37 | { | |
38 | printk(KERN_ERR "SRAT: SRAT not used.\n"); | |
39 | acpi_numa = -1; | |
91556237 | 40 | memset(nodes_add, 0, sizeof(nodes_add)); |
1da177e4 LT |
41 | } |
42 | ||
43 | static __init inline int srat_disabled(void) | |
44 | { | |
ffe77a46 | 45 | return acpi_numa < 0; |
1da177e4 LT |
46 | } |
47 | ||
48 | /* Callback for SLIT parsing */ | |
49 | void __init acpi_numa_slit_init(struct acpi_table_slit *slit) | |
50 | { | |
ac7136b6 | 51 | int i, j; |
f302a5bb | 52 | |
ac7136b6 TH |
53 | for (i = 0; i < slit->locality_count; i++) |
54 | for (j = 0; j < slit->locality_count; j++) | |
55 | numa_set_distance(pxm_to_node(i), pxm_to_node(j), | |
56 | slit->entry[slit->locality_count * i + j]); | |
1da177e4 LT |
57 | } |
58 | ||
7237d3de SS |
59 | /* Callback for Proximity Domain -> x2APIC mapping */ |
60 | void __init | |
61 | acpi_numa_x2apic_affinity_init(struct acpi_srat_x2apic_cpu_affinity *pa) | |
62 | { | |
63 | int pxm, node; | |
64 | int apic_id; | |
65 | ||
66 | if (srat_disabled()) | |
67 | return; | |
68 | if (pa->header.length < sizeof(struct acpi_srat_x2apic_cpu_affinity)) { | |
69 | bad_srat(); | |
70 | return; | |
71 | } | |
72 | if ((pa->flags & ACPI_SRAT_CPU_ENABLED) == 0) | |
73 | return; | |
74 | pxm = pa->proximity_domain; | |
75 | node = setup_node(pxm); | |
76 | if (node < 0) { | |
77 | printk(KERN_ERR "SRAT: Too many proximity domains %x\n", pxm); | |
78 | bad_srat(); | |
79 | return; | |
80 | } | |
81 | ||
82 | apic_id = pa->apic_id; | |
d3bd0588 YL |
83 | if (apic_id >= MAX_LOCAL_APIC) { |
84 | printk(KERN_INFO "SRAT: PXM %u -> APIC 0x%04x -> Node %u skipped apicid that is too big\n", pxm, apic_id, node); | |
85 | return; | |
86 | } | |
bbc9e2f4 | 87 | set_apicid_to_node(apic_id, node); |
92d4a437 | 88 | node_set(node, numa_nodes_parsed); |
7237d3de | 89 | acpi_numa = 1; |
163d3866 | 90 | printk(KERN_INFO "SRAT: PXM %u -> APIC 0x%04x -> Node %u\n", |
7237d3de SS |
91 | pxm, apic_id, node); |
92 | } | |
93 | ||
1da177e4 LT |
94 | /* Callback for Proximity Domain -> LAPIC mapping */ |
95 | void __init | |
15a58ed1 | 96 | acpi_numa_processor_affinity_init(struct acpi_srat_cpu_affinity *pa) |
1da177e4 LT |
97 | { |
98 | int pxm, node; | |
ef97001f | 99 | int apic_id; |
100 | ||
d22fe808 AK |
101 | if (srat_disabled()) |
102 | return; | |
15a58ed1 | 103 | if (pa->header.length != sizeof(struct acpi_srat_cpu_affinity)) { |
fad7906d | 104 | bad_srat(); |
d22fe808 AK |
105 | return; |
106 | } | |
15a58ed1 | 107 | if ((pa->flags & ACPI_SRAT_CPU_ENABLED) == 0) |
1da177e4 | 108 | return; |
15a58ed1 | 109 | pxm = pa->proximity_domain_lo; |
1da177e4 LT |
110 | node = setup_node(pxm); |
111 | if (node < 0) { | |
112 | printk(KERN_ERR "SRAT: Too many proximity domains %x\n", pxm); | |
113 | bad_srat(); | |
114 | return; | |
115 | } | |
beafe91f | 116 | |
2e42060c | 117 | if (get_uv_system_type() >= UV_X2APIC) |
a65d1d64 JS |
118 | apic_id = (pa->apic_id << 8) | pa->local_sapic_eid; |
119 | else | |
120 | apic_id = pa->apic_id; | |
d3bd0588 YL |
121 | |
122 | if (apic_id >= MAX_LOCAL_APIC) { | |
123 | printk(KERN_INFO "SRAT: PXM %u -> APIC 0x%02x -> Node %u skipped apicid that is too big\n", pxm, apic_id, node); | |
124 | return; | |
125 | } | |
126 | ||
bbc9e2f4 | 127 | set_apicid_to_node(apic_id, node); |
92d4a437 | 128 | node_set(node, numa_nodes_parsed); |
1da177e4 | 129 | acpi_numa = 1; |
163d3866 | 130 | printk(KERN_INFO "SRAT: PXM %u -> APIC 0x%02x -> Node %u\n", |
ef97001f | 131 | pxm, apic_id, node); |
1da177e4 LT |
132 | } |
133 | ||
71efa8fd KM |
134 | #ifdef CONFIG_MEMORY_HOTPLUG_SPARSE |
135 | static inline int save_add_info(void) {return 1;} | |
136 | #else | |
137 | static inline int save_add_info(void) {return 0;} | |
138 | #endif | |
68a3a7fe | 139 | /* |
888a589f YL |
140 | * Update nodes_add[] |
141 | * This code supports one contiguous hot add area per node | |
68a3a7fe | 142 | */ |
888a589f YL |
143 | static void __init |
144 | update_nodes_add(int node, unsigned long start, unsigned long end) | |
68a3a7fe AK |
145 | { |
146 | unsigned long s_pfn = start >> PAGE_SHIFT; | |
147 | unsigned long e_pfn = end >> PAGE_SHIFT; | |
888a589f | 148 | int changed = 0; |
68a3a7fe AK |
149 | struct bootnode *nd = &nodes_add[node]; |
150 | ||
151 | /* I had some trouble with strange memory hotadd regions breaking | |
152 | the boot. Be very strict here and reject anything unexpected. | |
153 | If you want working memory hotadd write correct SRATs. | |
154 | ||
155 | The node size check is a basic sanity check to guard against | |
156 | mistakes */ | |
157 | if ((signed long)(end - start) < NODE_MIN_SIZE) { | |
158 | printk(KERN_ERR "SRAT: Hotplug area too small\n"); | |
888a589f | 159 | return; |
68a3a7fe AK |
160 | } |
161 | ||
162 | /* This check might be a bit too strict, but I'm keeping it for now. */ | |
5cb248ab | 163 | if (absent_pages_in_range(s_pfn, e_pfn) != e_pfn - s_pfn) { |
9c7cd687 MG |
164 | printk(KERN_ERR |
165 | "SRAT: Hotplug area %lu -> %lu has existing memory\n", | |
166 | s_pfn, e_pfn); | |
888a589f | 167 | return; |
68a3a7fe AK |
168 | } |
169 | ||
170 | /* Looks good */ | |
171 | ||
68a3a7fe | 172 | if (nd->start == nd->end) { |
15a58ed1 AS |
173 | nd->start = start; |
174 | nd->end = end; | |
68a3a7fe | 175 | changed = 1; |
15a58ed1 AS |
176 | } else { |
177 | if (nd->start == end) { | |
178 | nd->start = start; | |
68a3a7fe AK |
179 | changed = 1; |
180 | } | |
15a58ed1 AS |
181 | if (nd->end == start) { |
182 | nd->end = end; | |
68a3a7fe AK |
183 | changed = 1; |
184 | } | |
185 | if (!changed) | |
186 | printk(KERN_ERR "SRAT: Hotplug zone not continuous. Partly ignored\n"); | |
15a58ed1 | 187 | } |
68a3a7fe | 188 | |
3a5fc0e4 | 189 | if (changed) { |
92d4a437 | 190 | node_set(node, numa_nodes_parsed); |
888a589f YL |
191 | printk(KERN_INFO "SRAT: hot plug zone found %Lx - %Lx\n", |
192 | nd->start, nd->end); | |
3a5fc0e4 | 193 | } |
68a3a7fe | 194 | } |
68a3a7fe | 195 | |
1da177e4 LT |
196 | /* Callback for parsing of the Proximity Domain <-> Memory Area mappings */ |
197 | void __init | |
15a58ed1 | 198 | acpi_numa_memory_affinity_init(struct acpi_srat_mem_affinity *ma) |
1da177e4 | 199 | { |
1da177e4 LT |
200 | unsigned long start, end; |
201 | int node, pxm; | |
1da177e4 | 202 | |
d22fe808 | 203 | if (srat_disabled()) |
1da177e4 | 204 | return; |
15a58ed1 | 205 | if (ma->header.length != sizeof(struct acpi_srat_mem_affinity)) { |
d22fe808 AK |
206 | bad_srat(); |
207 | return; | |
208 | } | |
15a58ed1 | 209 | if ((ma->flags & ACPI_SRAT_MEM_ENABLED) == 0) |
d22fe808 | 210 | return; |
15a58ed1 AS |
211 | |
212 | if ((ma->flags & ACPI_SRAT_MEM_HOT_PLUGGABLE) && !save_add_info()) | |
68a3a7fe | 213 | return; |
15a58ed1 AS |
214 | start = ma->base_address; |
215 | end = start + ma->length; | |
1da177e4 LT |
216 | pxm = ma->proximity_domain; |
217 | node = setup_node(pxm); | |
218 | if (node < 0) { | |
219 | printk(KERN_ERR "SRAT: Too many proximity domains.\n"); | |
220 | bad_srat(); | |
221 | return; | |
222 | } | |
ef396ec9 TH |
223 | |
224 | if (numa_add_memblk(node, start, end) < 0) { | |
1da177e4 LT |
225 | bad_srat(); |
226 | return; | |
227 | } | |
68a3a7fe | 228 | |
6ec6e0d9 SS |
229 | printk(KERN_INFO "SRAT: Node %u PXM %u %lx-%lx\n", node, pxm, |
230 | start, end); | |
68a3a7fe | 231 | |
4697bdcc | 232 | if (ma->flags & ACPI_SRAT_MEM_HOT_PLUGGABLE) |
888a589f | 233 | update_nodes_add(node, start, end); |
1da177e4 LT |
234 | } |
235 | ||
236 | void __init acpi_numa_arch_fixup(void) {} | |
237 | ||
a9aec56a TH |
238 | int __init x86_acpi_numa_init(void) |
239 | { | |
240 | int ret; | |
241 | ||
242 | ret = acpi_numa_init(); | |
243 | if (ret < 0) | |
244 | return ret; | |
245 | return srat_disabled() ? -EINVAL : 0; | |
246 | } | |
247 | ||
6a1673ae | 248 | #if defined(CONFIG_MEMORY_HOTPLUG_SPARSE) || defined(CONFIG_ACPI_HOTPLUG_MEMORY) |
4942e998 KM |
249 | int memory_add_physaddr_to_nid(u64 start) |
250 | { | |
251 | int i, ret = 0; | |
252 | ||
253 | for_each_node(i) | |
254 | if (nodes_add[i].start <= start && nodes_add[i].end > start) | |
255 | ret = i; | |
256 | ||
257 | return ret; | |
258 | } | |
8c2676a5 | 259 | EXPORT_SYMBOL_GPL(memory_add_physaddr_to_nid); |
6a1673ae | 260 | #endif |