Blame view
drivers/acpi/numa.c
12.2 KB
c942fddf8
|
1 |
// SPDX-License-Identifier: GPL-2.0-or-later |
1da177e4c
|
2 3 4 5 |
/* * acpi_numa.c - ACPI NUMA support * * Copyright (C) 2002 Takayoshi Kochi <t-kochi@bq.jp.nec.com> |
1da177e4c
|
6 |
*/ |
ac906a6d5
|
7 8 |
#define pr_fmt(fmt) "ACPI: " fmt |
1da177e4c
|
9 |
#include <linux/module.h> |
1da177e4c
|
10 11 12 13 14 |
#include <linux/init.h> #include <linux/kernel.h> #include <linux/types.h> #include <linux/errno.h> #include <linux/acpi.h> |
3770442e7
|
15 |
#include <linux/memblock.h> |
b552a8c56
|
16 |
#include <linux/numa.h> |
99759869f
|
17 18 |
#include <linux/nodemask.h> #include <linux/topology.h> |
1da177e4c
|
19 |
|
762834e8b
|
20 |
static nodemask_t nodes_found_map = NODE_MASK_NONE; |
762834e8b
|
21 22 |
/* maps to convert between proximity domain and logical node ID */ |
ffada8913
|
23 |
static int pxm_to_node_map[MAX_PXM_DOMAINS] |
b552a8c56
|
24 |
= { [0 ... MAX_PXM_DOMAINS - 1] = NUMA_NO_NODE }; |
ffada8913
|
25 |
static int node_to_pxm_map[MAX_NUMNODES] |
b552a8c56
|
26 |
= { [0 ... MAX_NUMNODES - 1] = PXM_INVAL }; |
762834e8b
|
27 |
|
8df0eb7c9
|
28 |
unsigned char acpi_srat_revision __initdata; |
e84025e27
|
29 |
int acpi_numa __initdata; |
8df0eb7c9
|
30 |
|
f363d16fb
|
31 |
int pxm_to_node(int pxm) |
762834e8b
|
32 33 |
{ if (pxm < 0) |
b552a8c56
|
34 |
return NUMA_NO_NODE; |
762834e8b
|
35 36 |
return pxm_to_node_map[pxm]; } |
f363d16fb
|
37 |
int node_to_pxm(int node) |
762834e8b
|
38 39 40 41 42 |
{ if (node < 0) return PXM_INVAL; return node_to_pxm_map[node]; } |
d79ed248d
|
43 |
static void __acpi_map_pxm_to_node(int pxm, int node) |
3484d7981
|
44 |
{ |
0f9b75ef3
|
45 46 47 48 |
if (pxm_to_node_map[pxm] == NUMA_NO_NODE || node < pxm_to_node_map[pxm]) pxm_to_node_map[pxm] = node; if (node_to_pxm_map[node] == PXM_INVAL || pxm < node_to_pxm_map[node]) node_to_pxm_map[node] = pxm; |
3484d7981
|
49 |
} |
8ff6f48d9
|
50 |
int acpi_map_pxm_to_node(int pxm) |
762834e8b
|
51 |
{ |
99759869f
|
52 |
int node; |
aec03f89e
|
53 |
if (pxm < 0 || pxm >= MAX_PXM_DOMAINS || numa_off) |
99759869f
|
54 55 56 |
return NUMA_NO_NODE; node = pxm_to_node_map[pxm]; |
762834e8b
|
57 |
|
1bb25df0f
|
58 |
if (node == NUMA_NO_NODE) { |
762834e8b
|
59 |
if (nodes_weight(nodes_found_map) >= MAX_NUMNODES) |
b552a8c56
|
60 |
return NUMA_NO_NODE; |
762834e8b
|
61 |
node = first_unset_node(nodes_found_map); |
3484d7981
|
62 |
__acpi_map_pxm_to_node(pxm, node); |
762834e8b
|
63 64 65 66 67 |
node_set(node, nodes_found_map); } return node; } |
8fc5c7355
|
68 |
EXPORT_SYMBOL(acpi_map_pxm_to_node); |
762834e8b
|
69 |
|
99759869f
|
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
/** * acpi_map_pxm_to_online_node - Map proximity ID to online node * @pxm: ACPI proximity ID * * This is similar to acpi_map_pxm_to_node(), but always returns an online * node. When the mapped node from a given proximity ID is offline, it * looks up the node distance table and returns the nearest online node. * * ACPI device drivers, which are called after the NUMA initialization has * completed in the kernel, can call this interface to obtain their device * NUMA topology from ACPI tables. Such drivers do not have to deal with * offline nodes. A node may be offline when a device proximity ID is * unique, SRAT memory entry does not exist, or NUMA is disabled, ex. * "numa=off" on x86. */ int acpi_map_pxm_to_online_node(int pxm) { |
dc9e0a934
|
87 |
int node, min_node; |
99759869f
|
88 89 90 91 92 |
node = acpi_map_pxm_to_node(pxm); if (node == NUMA_NO_NODE) node = 0; |
dc9e0a934
|
93 |
min_node = node; |
99759869f
|
94 |
if (!node_online(node)) { |
dc9e0a934
|
95 |
int min_dist = INT_MAX, dist, n; |
99759869f
|
96 97 98 99 |
for_each_online_node(n) { dist = node_distance(node, n); if (dist < min_dist) { min_dist = dist; |
dc9e0a934
|
100 |
min_node = n; |
99759869f
|
101 102 103 |
} } } |
dc9e0a934
|
104 |
return min_node; |
99759869f
|
105 106 |
} EXPORT_SYMBOL(acpi_map_pxm_to_online_node); |
ae2c6dcf9
|
107 108 |
static void __init acpi_table_print_srat_entry(struct acpi_subtable_header *header) |
1da177e4c
|
109 |
{ |
1da177e4c
|
110 |
switch (header->type) { |
15a58ed12
|
111 |
case ACPI_SRAT_TYPE_CPU_AFFINITY: |
4be44fcd3
|
112 |
{ |
15a58ed12
|
113 114 |
struct acpi_srat_cpu_affinity *p = (struct acpi_srat_cpu_affinity *)header; |
3dda44818
|
115 116 117 118 119 120 |
pr_debug("SRAT Processor (id[0x%02x] eid[0x%02x]) in proximity domain %d %s ", p->apic_id, p->local_sapic_eid, p->proximity_domain_lo, (p->flags & ACPI_SRAT_CPU_ENABLED) ? "enabled" : "disabled"); |
4be44fcd3
|
121 |
} |
1da177e4c
|
122 |
break; |
15a58ed12
|
123 |
case ACPI_SRAT_TYPE_MEMORY_AFFINITY: |
4be44fcd3
|
124 |
{ |
15a58ed12
|
125 126 |
struct acpi_srat_mem_affinity *p = (struct acpi_srat_mem_affinity *)header; |
b9ced18ac
|
127 128 129 130 |
pr_debug("SRAT Memory (0x%llx length 0x%llx) in proximity domain %d %s%s%s ", (unsigned long long)p->base_address, (unsigned long long)p->length, |
3dda44818
|
131 132 133 134 135 136 137 |
p->proximity_domain, (p->flags & ACPI_SRAT_MEM_ENABLED) ? "enabled" : "disabled", (p->flags & ACPI_SRAT_MEM_HOT_PLUGGABLE) ? " hot-pluggable" : "", (p->flags & ACPI_SRAT_MEM_NON_VOLATILE) ? " non-volatile" : ""); |
4be44fcd3
|
138 |
} |
1da177e4c
|
139 |
break; |
7237d3de7
|
140 |
case ACPI_SRAT_TYPE_X2APIC_CPU_AFFINITY: |
7237d3de7
|
141 142 143 |
{ struct acpi_srat_x2apic_cpu_affinity *p = (struct acpi_srat_x2apic_cpu_affinity *)header; |
3dda44818
|
144 145 146 147 148 149 |
pr_debug("SRAT Processor (x2apicid[0x%08x]) in proximity domain %d %s ", p->apic_id, p->proximity_domain, (p->flags & ACPI_SRAT_CPU_ENABLED) ? "enabled" : "disabled"); |
7237d3de7
|
150 |
} |
7237d3de7
|
151 |
break; |
3dda44818
|
152 |
|
4bac6fa73
|
153 154 155 156 157 158 159 160 161 162 163 164 |
case ACPI_SRAT_TYPE_GICC_AFFINITY: { struct acpi_srat_gicc_affinity *p = (struct acpi_srat_gicc_affinity *)header; pr_debug("SRAT Processor (acpi id[0x%04x]) in proximity domain %d %s ", p->acpi_processor_uid, p->proximity_domain, (p->flags & ACPI_SRAT_GICC_ENABLED) ? "enabled" : "disabled"); } break; |
1da177e4c
|
165 |
default: |
ac906a6d5
|
166 167 168 |
pr_warn("Found unsupported SRAT entry (type = 0x%x) ", header->type); |
1da177e4c
|
169 170 171 |
break; } } |
39b8931b5
|
172 173 174 175 176 177 |
/* * A lot of BIOS fill in 10 (= no distance) everywhere. This messes * up the NUMA heuristics which wants the local node to have a smaller * distance than the others. * Do some quick checks here and only use the SLIT if it passes. */ |
40e318563
|
178 |
static int __init slit_valid(struct acpi_table_slit *slit) |
39b8931b5
|
179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 |
{ int i, j; int d = slit->locality_count; for (i = 0; i < d; i++) { for (j = 0; j < d; j++) { u8 val = slit->entry[d*i + j]; if (i == j) { if (val != LOCAL_DISTANCE) return 0; } else if (val <= LOCAL_DISTANCE) return 0; } } return 1; } |
e84025e27
|
194 195 196 197 198 199 200 201 202 203 204 |
void __init bad_srat(void) { pr_err("SRAT: SRAT not used. "); acpi_numa = -1; } int __init srat_disabled(void) { return acpi_numa < 0; } |
6525afdf5
|
205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 |
#if defined(CONFIG_X86) || defined(CONFIG_ARM64) /* * Callback for SLIT parsing. pxm_to_node() returns NUMA_NO_NODE for * I/O localities since SRAT does not list them. I/O localities are * not supported at this point. */ void __init acpi_numa_slit_init(struct acpi_table_slit *slit) { int i, j; for (i = 0; i < slit->locality_count; i++) { const int from_node = pxm_to_node(i); if (from_node == NUMA_NO_NODE) continue; for (j = 0; j < slit->locality_count; j++) { const int to_node = pxm_to_node(j); if (to_node == NUMA_NO_NODE) continue; numa_set_distance(from_node, to_node, slit->entry[slit->locality_count * i + j]); } } } |
3770442e7
|
232 233 234 235 236 237 238 239 240 241 242 243 244 245 |
/* * Default callback for parsing of the Proximity Domain <-> Memory * Area mappings */ int __init acpi_numa_memory_affinity_init(struct acpi_srat_mem_affinity *ma) { u64 start, end; u32 hotpluggable; int node, pxm; if (srat_disabled()) goto out_err; |
e0af261a4
|
246 247 248 249 |
if (ma->header.length < sizeof(struct acpi_srat_mem_affinity)) { pr_err("SRAT: Unexpected header length: %d ", ma->header.length); |
3770442e7
|
250 |
goto out_err_bad_srat; |
e0af261a4
|
251 |
} |
3770442e7
|
252 253 254 255 256 257 258 259 260 261 262 263 264 |
if ((ma->flags & ACPI_SRAT_MEM_ENABLED) == 0) goto out_err; hotpluggable = ma->flags & ACPI_SRAT_MEM_HOT_PLUGGABLE; if (hotpluggable && !IS_ENABLED(CONFIG_MEMORY_HOTPLUG)) goto out_err; start = ma->base_address; end = start + ma->length; pxm = ma->proximity_domain; if (acpi_srat_revision <= 1) pxm &= 0xff; node = acpi_map_pxm_to_node(pxm); |
e0af261a4
|
265 266 267 |
if (node == NUMA_NO_NODE || node >= MAX_NUMNODES) { pr_err("SRAT: Too many proximity domains. "); |
3770442e7
|
268 269 |
goto out_err_bad_srat; } |
e0af261a4
|
270 271 272 273 274 |
if (numa_add_memblk(node, start, end) < 0) { pr_err("SRAT: Failed to add memblk to node %u [mem %#010Lx-%#010Lx] ", node, (unsigned long long) start, (unsigned long long) end - 1); |
3770442e7
|
275 |
goto out_err_bad_srat; |
e0af261a4
|
276 |
} |
3770442e7
|
277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 |
node_set(node, numa_nodes_parsed); pr_info("SRAT: Node %u PXM %u [mem %#010Lx-%#010Lx]%s%s ", node, pxm, (unsigned long long) start, (unsigned long long) end - 1, hotpluggable ? " hotplug" : "", ma->flags & ACPI_SRAT_MEM_NON_VOLATILE ? " non-volatile" : ""); /* Mark hotplug range in memblock. */ if (hotpluggable && memblock_mark_hotplug(start, ma->length)) pr_warn("SRAT: Failed to mark hotplug range [mem %#010Lx-%#010Lx] in memblock ", (unsigned long long)start, (unsigned long long)end - 1); max_possible_pfn = max(max_possible_pfn, PFN_UP(end - 1)); return 0; out_err_bad_srat: bad_srat(); out_err: return -EINVAL; } |
6525afdf5
|
301 |
#endif /* defined(CONFIG_X86) || defined (CONFIG_ARM64) */ |
15a58ed12
|
302 |
static int __init acpi_parse_slit(struct acpi_table_header *table) |
1da177e4c
|
303 |
{ |
2fad93083
|
304 |
struct acpi_table_slit *slit = (struct acpi_table_slit *)table; |
1da177e4c
|
305 |
|
39b8931b5
|
306 |
if (!slit_valid(slit)) { |
ac906a6d5
|
307 308 |
pr_info("SLIT table looks invalid. Not used. "); |
39b8931b5
|
309 310 |
return -EINVAL; } |
1da177e4c
|
311 312 313 314 |
acpi_numa_slit_init(slit); return 0; } |
beffbe54f
|
315 |
void __init __weak |
7237d3de7
|
316 317 |
acpi_numa_x2apic_affinity_init(struct acpi_srat_x2apic_cpu_affinity *pa) { |
ac906a6d5
|
318 319 |
pr_warn("Found unsupported x2apic [0x%08x] SRAT entry ", pa->apic_id); |
7237d3de7
|
320 |
} |
7237d3de7
|
321 |
static int __init |
60574d1e0
|
322 |
acpi_parse_x2apic_affinity(union acpi_subtable_headers *header, |
7237d3de7
|
323 324 325 326 327 328 329 |
const unsigned long end) { struct acpi_srat_x2apic_cpu_affinity *processor_affinity; processor_affinity = (struct acpi_srat_x2apic_cpu_affinity *)header; if (!processor_affinity) return -EINVAL; |
60574d1e0
|
330 |
acpi_table_print_srat_entry(&header->common); |
7237d3de7
|
331 332 333 334 335 336 |
/* let architecture-dependent part to do it */ acpi_numa_x2apic_affinity_init(processor_affinity); return 0; } |
1da177e4c
|
337 |
static int __init |
60574d1e0
|
338 |
acpi_parse_processor_affinity(union acpi_subtable_headers *header, |
4be44fcd3
|
339 |
const unsigned long end) |
1da177e4c
|
340 |
{ |
15a58ed12
|
341 |
struct acpi_srat_cpu_affinity *processor_affinity; |
1da177e4c
|
342 |
|
15a58ed12
|
343 |
processor_affinity = (struct acpi_srat_cpu_affinity *)header; |
1da177e4c
|
344 345 |
if (!processor_affinity) return -EINVAL; |
60574d1e0
|
346 |
acpi_table_print_srat_entry(&header->common); |
1da177e4c
|
347 348 349 350 351 352 |
/* let architecture-dependent part to do it */ acpi_numa_processor_affinity_init(processor_affinity); return 0; } |
4bac6fa73
|
353 |
static int __init |
60574d1e0
|
354 |
acpi_parse_gicc_affinity(union acpi_subtable_headers *header, |
4bac6fa73
|
355 356 357 358 359 360 361 |
const unsigned long end) { struct acpi_srat_gicc_affinity *processor_affinity; processor_affinity = (struct acpi_srat_gicc_affinity *)header; if (!processor_affinity) return -EINVAL; |
60574d1e0
|
362 |
acpi_table_print_srat_entry(&header->common); |
4bac6fa73
|
363 364 365 366 367 368 |
/* let architecture-dependent part to do it */ acpi_numa_gicc_affinity_init(processor_affinity); return 0; } |
095adbb64
|
369 |
static int __initdata parsed_numa_memblks; |
1da177e4c
|
370 |
static int __init |
60574d1e0
|
371 |
acpi_parse_memory_affinity(union acpi_subtable_headers * header, |
4be44fcd3
|
372 |
const unsigned long end) |
1da177e4c
|
373 |
{ |
15a58ed12
|
374 |
struct acpi_srat_mem_affinity *memory_affinity; |
1da177e4c
|
375 |
|
15a58ed12
|
376 |
memory_affinity = (struct acpi_srat_mem_affinity *)header; |
1da177e4c
|
377 378 |
if (!memory_affinity) return -EINVAL; |
60574d1e0
|
379 |
acpi_table_print_srat_entry(&header->common); |
1da177e4c
|
380 381 |
/* let architecture-dependent part to do it */ |
095adbb64
|
382 383 |
if (!acpi_numa_memory_affinity_init(memory_affinity)) parsed_numa_memblks++; |
1da177e4c
|
384 385 |
return 0; } |
15a58ed12
|
386 |
static int __init acpi_parse_srat(struct acpi_table_header *table) |
1da177e4c
|
387 |
{ |
2fad93083
|
388 |
struct acpi_table_srat *srat = (struct acpi_table_srat *)table; |
1da177e4c
|
389 |
|
8df0eb7c9
|
390 |
acpi_srat_revision = srat->header.revision; |
cfa806f05
|
391 |
/* Real work done in acpi_table_parse_srat below. */ |
1da177e4c
|
392 393 394 |
return 0; } |
ae2c6dcf9
|
395 |
static int __init |
15a58ed12
|
396 |
acpi_table_parse_srat(enum acpi_srat_type id, |
b43e1065c
|
397 |
acpi_tbl_entry_handler handler, unsigned int max_entries) |
1da177e4c
|
398 |
{ |
6eb87fed5
|
399 |
return acpi_table_parse_entries(ACPI_SIG_SRAT, |
4be44fcd3
|
400 401 |
sizeof(struct acpi_table_srat), id, handler, max_entries); |
1da177e4c
|
402 |
} |
20e6926dc
|
403 |
int __init acpi_numa_init(void) |
e8d195525
|
404 |
{ |
20e6926dc
|
405 |
int cnt = 0; |
4bac6fa73
|
406 407 |
if (acpi_disabled) return -EINVAL; |
d3bd05882
|
408 409 410 411 412 |
/* * Should not limit number with cpu num that is from NR_CPUS or nr_cpus= * SRAT cpu entries could have different order with that in MADT. * So go over all cpu entries in SRAT to get apicid to node mapping. */ |
8716273ca
|
413 |
|
02cb489be
|
414 |
/* SRAT: System Resource Affinity Table */ |
7f8f97c3c
|
415 |
if (!acpi_table_parse(ACPI_SIG_SRAT, acpi_parse_srat)) { |
4bac6fa73
|
416 |
struct acpi_subtable_proc srat_proc[3]; |
702b07fcc
|
417 418 419 420 421 422 |
memset(srat_proc, 0, sizeof(srat_proc)); srat_proc[0].id = ACPI_SRAT_TYPE_CPU_AFFINITY; srat_proc[0].handler = acpi_parse_processor_affinity; srat_proc[1].id = ACPI_SRAT_TYPE_X2APIC_CPU_AFFINITY; srat_proc[1].handler = acpi_parse_x2apic_affinity; |
4bac6fa73
|
423 424 |
srat_proc[2].id = ACPI_SRAT_TYPE_GICC_AFFINITY; srat_proc[2].handler = acpi_parse_gicc_affinity; |
702b07fcc
|
425 426 427 428 |
acpi_table_parse_entries_array(ACPI_SIG_SRAT, sizeof(struct acpi_table_srat), srat_proc, ARRAY_SIZE(srat_proc), 0); |
20e6926dc
|
429 |
cnt = acpi_table_parse_srat(ACPI_SRAT_TYPE_MEMORY_AFFINITY, |
fd3e45436
|
430 |
acpi_parse_memory_affinity, 0); |
1da177e4c
|
431 432 433 |
} /* SLIT: System Locality Information Table */ |
7f8f97c3c
|
434 |
acpi_table_parse(ACPI_SIG_SLIT, acpi_parse_slit); |
1da177e4c
|
435 |
|
20e6926dc
|
436 437 |
if (cnt < 0) return cnt; |
095adbb64
|
438 |
else if (!parsed_numa_memblks) |
f3946fb6e
|
439 |
return -ENOENT; |
940fed2e7
|
440 |
return 0; |
1da177e4c
|
441 |
} |
d79ed248d
|
442 |
static int acpi_get_pxm(acpi_handle h) |
1da177e4c
|
443 |
{ |
27663c585
|
444 |
unsigned long long pxm; |
1da177e4c
|
445 446 447 448 449 450 451 452 |
acpi_status status; acpi_handle handle; acpi_handle phandle = h; do { handle = phandle; status = acpi_evaluate_integer(handle, "_PXM", NULL, &pxm); if (ACPI_SUCCESS(status)) |
50dd09697
|
453 |
return pxm; |
1da177e4c
|
454 |
status = acpi_get_parent(handle, &phandle); |
4be44fcd3
|
455 |
} while (ACPI_SUCCESS(status)); |
1da177e4c
|
456 457 |
return -1; } |
1e3590e2e
|
458 |
|
486c79b50
|
459 |
int acpi_get_node(acpi_handle handle) |
1e3590e2e
|
460 |
{ |
962fe9c91
|
461 |
int pxm; |
1e3590e2e
|
462 |
|
1e3590e2e
|
463 |
pxm = acpi_get_pxm(handle); |
1e3590e2e
|
464 |
|
962fe9c91
|
465 |
return acpi_map_pxm_to_node(pxm); |
1e3590e2e
|
466 467 |
} EXPORT_SYMBOL(acpi_get_node); |