blob: 8e9d3394f6d452d97703f2e639e2d0b8f409d473 [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>
Andi Kleen68a3a7f2006-04-07 19:49:18 +020018#include <linux/bootmem.h>
Yinghai Lua9ce6bc2010-08-25 13:39:17 -070019#include <linux/memblock.h>
Andi Kleen68a3a7f2006-04-07 19:49:18 +020020#include <linux/mm.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070021#include <asm/proto.h>
22#include <asm/numa.h>
Andi Kleen8a6fdd32006-01-11 22:44:39 +010023#include <asm/e820.h>
Ingo Molnar7b6aa332009-02-17 13:58:15 +010024#include <asm/apic.h>
Ingo Molnar4ec71fa2009-01-21 10:24:27 +010025#include <asm/uv/uv.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070026
Andi Kleenc31fbb12006-09-26 10:52:33 +020027int acpi_numa __initdata;
28
Keith Mannthey4942e992006-09-30 23:27:06 -070029static struct bootnode nodes_add[MAX_NUMNODES];
Linus Torvalds1da177e2005-04-16 15:20:36 -070030
31static __init int setup_node(int pxm)
32{
Yasunori Goto762834e2006-06-23 02:03:19 -070033 return acpi_map_pxm_to_node(pxm);
Linus Torvalds1da177e2005-04-16 15:20:36 -070034}
35
Linus Torvalds1da177e2005-04-16 15:20:36 -070036static __init void bad_srat(void)
37{
38 printk(KERN_ERR "SRAT: SRAT not used.\n");
39 acpi_numa = -1;
Tejun Heo91556232011-02-16 17:11:09 +010040 memset(nodes_add, 0, sizeof(nodes_add));
Linus Torvalds1da177e2005-04-16 15:20:36 -070041}
42
43static __init inline int srat_disabled(void)
44{
Tejun Heoffe77a42011-02-16 12:13:06 +010045 return acpi_numa < 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070046}
47
48/* Callback for SLIT parsing */
49void __init acpi_numa_slit_init(struct acpi_table_slit *slit)
50{
Tejun Heoac7136b2011-02-16 17:11:09 +010051 int i, j;
Yinghai Luf302a5b2008-07-10 20:36:37 -070052
Tejun Heoac7136b2011-02-16 17:11:09 +010053 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]);
Linus Torvalds1da177e2005-04-16 15:20:36 -070057}
58
Suresh Siddha7237d3d2009-03-30 13:55:30 -080059/* Callback for Proximity Domain -> x2APIC mapping */
60void __init
61acpi_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;
Yinghai Lud3bd0582010-12-16 19:09:58 -080083 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 }
Tejun Heobbc9e2f2011-01-23 14:37:39 +010087 set_apicid_to_node(apic_id, node);
Tejun Heo92d4a432011-02-16 17:11:09 +010088 node_set(node, numa_nodes_parsed);
Suresh Siddha7237d3d2009-03-30 13:55:30 -080089 acpi_numa = 1;
Yinghai Lu163d3862009-11-21 00:23:37 -080090 printk(KERN_INFO "SRAT: PXM %u -> APIC 0x%04x -> Node %u\n",
Suresh Siddha7237d3d2009-03-30 13:55:30 -080091 pxm, apic_id, node);
92}
93
Linus Torvalds1da177e2005-04-16 15:20:36 -070094/* Callback for Proximity Domain -> LAPIC mapping */
95void __init
Alexey Starikovskiy15a58ed2007-02-02 19:48:22 +030096acpi_numa_processor_affinity_init(struct acpi_srat_cpu_affinity *pa)
Linus Torvalds1da177e2005-04-16 15:20:36 -070097{
98 int pxm, node;
travis@sgi.comef970012008-01-30 13:33:10 +010099 int apic_id;
100
Andi Kleend22fe802006-02-03 21:51:26 +0100101 if (srat_disabled())
102 return;
Alexey Starikovskiy15a58ed2007-02-02 19:48:22 +0300103 if (pa->header.length != sizeof(struct acpi_srat_cpu_affinity)) {
Andi Kleenfad79062006-05-15 18:19:44 +0200104 bad_srat();
Andi Kleend22fe802006-02-03 21:51:26 +0100105 return;
106 }
Alexey Starikovskiy15a58ed2007-02-02 19:48:22 +0300107 if ((pa->flags & ACPI_SRAT_CPU_ENABLED) == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700108 return;
Alexey Starikovskiy15a58ed2007-02-02 19:48:22 +0300109 pxm = pa->proximity_domain_lo;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700110 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 }
Yinghai Lubeafe912008-02-16 23:00:22 -0800116
Jack Steiner2e420602008-09-23 15:37:13 -0500117 if (get_uv_system_type() >= UV_X2APIC)
Jack Steinera65d1d62008-03-28 14:12:08 -0500118 apic_id = (pa->apic_id << 8) | pa->local_sapic_eid;
119 else
120 apic_id = pa->apic_id;
Yinghai Lud3bd0582010-12-16 19:09:58 -0800121
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
Tejun Heobbc9e2f2011-01-23 14:37:39 +0100127 set_apicid_to_node(apic_id, node);
Tejun Heo92d4a432011-02-16 17:11:09 +0100128 node_set(node, numa_nodes_parsed);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700129 acpi_numa = 1;
Yinghai Lu163d3862009-11-21 00:23:37 -0800130 printk(KERN_INFO "SRAT: PXM %u -> APIC 0x%02x -> Node %u\n",
travis@sgi.comef970012008-01-30 13:33:10 +0100131 pxm, apic_id, node);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700132}
133
Keith Mannthey71efa8f2006-09-30 23:27:05 -0700134#ifdef CONFIG_MEMORY_HOTPLUG_SPARSE
135static inline int save_add_info(void) {return 1;}
136#else
137static inline int save_add_info(void) {return 0;}
138#endif
Andi Kleen68a3a7f2006-04-07 19:49:18 +0200139/*
Yinghai Lu888a5892009-05-15 13:59:37 -0700140 * Update nodes_add[]
141 * This code supports one contiguous hot add area per node
Andi Kleen68a3a7f2006-04-07 19:49:18 +0200142 */
Yinghai Lu888a5892009-05-15 13:59:37 -0700143static void __init
144update_nodes_add(int node, unsigned long start, unsigned long end)
Andi Kleen68a3a7f2006-04-07 19:49:18 +0200145{
146 unsigned long s_pfn = start >> PAGE_SHIFT;
147 unsigned long e_pfn = end >> PAGE_SHIFT;
Yinghai Lu888a5892009-05-15 13:59:37 -0700148 int changed = 0;
Andi Kleen68a3a7f2006-04-07 19:49:18 +0200149 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");
Yinghai Lu888a5892009-05-15 13:59:37 -0700159 return;
Andi Kleen68a3a7f2006-04-07 19:49:18 +0200160 }
161
162 /* This check might be a bit too strict, but I'm keeping it for now. */
Mel Gorman5cb248a2006-09-27 01:49:52 -0700163 if (absent_pages_in_range(s_pfn, e_pfn) != e_pfn - s_pfn) {
Mel Gorman9c7cd682006-09-27 01:49:58 -0700164 printk(KERN_ERR
165 "SRAT: Hotplug area %lu -> %lu has existing memory\n",
166 s_pfn, e_pfn);
Yinghai Lu888a5892009-05-15 13:59:37 -0700167 return;
Andi Kleen68a3a7f2006-04-07 19:49:18 +0200168 }
169
170 /* Looks good */
171
Andi Kleen68a3a7f2006-04-07 19:49:18 +0200172 if (nd->start == nd->end) {
Alexey Starikovskiy15a58ed2007-02-02 19:48:22 +0300173 nd->start = start;
174 nd->end = end;
Andi Kleen68a3a7f2006-04-07 19:49:18 +0200175 changed = 1;
Alexey Starikovskiy15a58ed2007-02-02 19:48:22 +0300176 } else {
177 if (nd->start == end) {
178 nd->start = start;
Andi Kleen68a3a7f2006-04-07 19:49:18 +0200179 changed = 1;
180 }
Alexey Starikovskiy15a58ed2007-02-02 19:48:22 +0300181 if (nd->end == start) {
182 nd->end = end;
Andi Kleen68a3a7f2006-04-07 19:49:18 +0200183 changed = 1;
184 }
185 if (!changed)
186 printk(KERN_ERR "SRAT: Hotplug zone not continuous. Partly ignored\n");
Alexey Starikovskiy15a58ed2007-02-02 19:48:22 +0300187 }
Andi Kleen68a3a7f2006-04-07 19:49:18 +0200188
David Rientjes3a5fc0e2010-01-20 12:10:47 -0800189 if (changed) {
Tejun Heo92d4a432011-02-16 17:11:09 +0100190 node_set(node, numa_nodes_parsed);
Yinghai Lu888a5892009-05-15 13:59:37 -0700191 printk(KERN_INFO "SRAT: hot plug zone found %Lx - %Lx\n",
192 nd->start, nd->end);
David Rientjes3a5fc0e2010-01-20 12:10:47 -0800193 }
Andi Kleen68a3a7f2006-04-07 19:49:18 +0200194}
Andi Kleen68a3a7f2006-04-07 19:49:18 +0200195
Linus Torvalds1da177e2005-04-16 15:20:36 -0700196/* Callback for parsing of the Proximity Domain <-> Memory Area mappings */
197void __init
Alexey Starikovskiy15a58ed2007-02-02 19:48:22 +0300198acpi_numa_memory_affinity_init(struct acpi_srat_mem_affinity *ma)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700199{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700200 unsigned long start, end;
201 int node, pxm;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700202
Andi Kleend22fe802006-02-03 21:51:26 +0100203 if (srat_disabled())
Linus Torvalds1da177e2005-04-16 15:20:36 -0700204 return;
Alexey Starikovskiy15a58ed2007-02-02 19:48:22 +0300205 if (ma->header.length != sizeof(struct acpi_srat_mem_affinity)) {
Andi Kleend22fe802006-02-03 21:51:26 +0100206 bad_srat();
207 return;
208 }
Alexey Starikovskiy15a58ed2007-02-02 19:48:22 +0300209 if ((ma->flags & ACPI_SRAT_MEM_ENABLED) == 0)
Andi Kleend22fe802006-02-03 21:51:26 +0100210 return;
Alexey Starikovskiy15a58ed2007-02-02 19:48:22 +0300211
212 if ((ma->flags & ACPI_SRAT_MEM_HOT_PLUGGABLE) && !save_add_info())
Andi Kleen68a3a7f2006-04-07 19:49:18 +0200213 return;
Alexey Starikovskiy15a58ed2007-02-02 19:48:22 +0300214 start = ma->base_address;
215 end = start + ma->length;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700216 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 }
Tejun Heoef396ec2011-02-16 17:11:07 +0100223
224 if (numa_add_memblk(node, start, end) < 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700225 bad_srat();
226 return;
227 }
Andi Kleen68a3a7f2006-04-07 19:49:18 +0200228
Suresh Siddha6ec6e0d2008-03-25 10:14:35 -0700229 printk(KERN_INFO "SRAT: Node %u PXM %u %lx-%lx\n", node, pxm,
230 start, end);
Andi Kleen68a3a7f2006-04-07 19:49:18 +0200231
Tejun Heo4697bdc2011-02-16 17:11:09 +0100232 if (ma->flags & ACPI_SRAT_MEM_HOT_PLUGGABLE)
Yinghai Lu888a5892009-05-15 13:59:37 -0700233 update_nodes_add(node, start, end);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700234}
235
236void __init acpi_numa_arch_fixup(void) {}
237
Tejun Heoa9aec562011-02-16 12:13:06 +0100238int __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
Thomas Gleixner6a1673a2008-05-12 15:43:38 +0200248#if defined(CONFIG_MEMORY_HOTPLUG_SPARSE) || defined(CONFIG_ACPI_HOTPLUG_MEMORY)
Keith Mannthey4942e992006-09-30 23:27:06 -0700249int 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}
Keith Mannthey8c2676a2006-09-30 23:27:07 -0700259EXPORT_SYMBOL_GPL(memory_add_physaddr_to_nid);
Thomas Gleixner6a1673a2008-05-12 15:43:38 +0200260#endif