x86-64, NUMA: Unify emulated distance mapping
[firefly-linux-kernel-4.4.55.git] / arch / x86 / mm / srat_64.c
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>
18 #include <linux/bootmem.h>
19 #include <linux/memblock.h>
20 #include <linux/mm.h>
21 #include <asm/proto.h>
22 #include <asm/numa.h>
23 #include <asm/e820.h>
24 #include <asm/apic.h>
25 #include <asm/uv/uv.h>
26
27 int acpi_numa __initdata;
28
29 static struct bootnode nodes_add[MAX_NUMNODES];
30
31 static __init int setup_node(int pxm)
32 {
33         return acpi_map_pxm_to_node(pxm);
34 }
35
36 static __init void bad_srat(void)
37 {
38         printk(KERN_ERR "SRAT: SRAT not used.\n");
39         acpi_numa = -1;
40         memset(nodes_add, 0, sizeof(nodes_add));
41 }
42
43 static __init inline int srat_disabled(void)
44 {
45         return acpi_numa < 0;
46 }
47
48 /* Callback for SLIT parsing */
49 void __init acpi_numa_slit_init(struct acpi_table_slit *slit)
50 {
51         int i, j;
52
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]);
57 }
58
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;
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         }
87         set_apicid_to_node(apic_id, node);
88         node_set(node, numa_nodes_parsed);
89         acpi_numa = 1;
90         printk(KERN_INFO "SRAT: PXM %u -> APIC 0x%04x -> Node %u\n",
91                pxm, apic_id, node);
92 }
93
94 /* Callback for Proximity Domain -> LAPIC mapping */
95 void __init
96 acpi_numa_processor_affinity_init(struct acpi_srat_cpu_affinity *pa)
97 {
98         int pxm, node;
99         int apic_id;
100
101         if (srat_disabled())
102                 return;
103         if (pa->header.length != sizeof(struct acpi_srat_cpu_affinity)) {
104                 bad_srat();
105                 return;
106         }
107         if ((pa->flags & ACPI_SRAT_CPU_ENABLED) == 0)
108                 return;
109         pxm = pa->proximity_domain_lo;
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         }
116
117         if (get_uv_system_type() >= UV_X2APIC)
118                 apic_id = (pa->apic_id << 8) | pa->local_sapic_eid;
119         else
120                 apic_id = pa->apic_id;
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
127         set_apicid_to_node(apic_id, node);
128         node_set(node, numa_nodes_parsed);
129         acpi_numa = 1;
130         printk(KERN_INFO "SRAT: PXM %u -> APIC 0x%02x -> Node %u\n",
131                pxm, apic_id, node);
132 }
133
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
139 /*
140  * Update nodes_add[]
141  * This code supports one contiguous hot add area per node
142  */
143 static void __init
144 update_nodes_add(int node, unsigned long start, unsigned long end)
145 {
146         unsigned long s_pfn = start >> PAGE_SHIFT;
147         unsigned long e_pfn = end >> PAGE_SHIFT;
148         int changed = 0;
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");
159                 return;
160         }
161
162         /* This check might be a bit too strict, but I'm keeping it for now. */
163         if (absent_pages_in_range(s_pfn, e_pfn) != e_pfn - s_pfn) {
164                 printk(KERN_ERR
165                         "SRAT: Hotplug area %lu -> %lu has existing memory\n",
166                         s_pfn, e_pfn);
167                 return;
168         }
169
170         /* Looks good */
171
172         if (nd->start == nd->end) {
173                 nd->start = start;
174                 nd->end = end;
175                 changed = 1;
176         } else {
177                 if (nd->start == end) {
178                         nd->start = start;
179                         changed = 1;
180                 }
181                 if (nd->end == start) {
182                         nd->end = end;
183                         changed = 1;
184                 }
185                 if (!changed)
186                         printk(KERN_ERR "SRAT: Hotplug zone not continuous. Partly ignored\n");
187         }
188
189         if (changed) {
190                 node_set(node, numa_nodes_parsed);
191                 printk(KERN_INFO "SRAT: hot plug zone found %Lx - %Lx\n",
192                                  nd->start, nd->end);
193         }
194 }
195
196 /* Callback for parsing of the Proximity Domain <-> Memory Area mappings */
197 void __init
198 acpi_numa_memory_affinity_init(struct acpi_srat_mem_affinity *ma)
199 {
200         unsigned long start, end;
201         int node, pxm;
202
203         if (srat_disabled())
204                 return;
205         if (ma->header.length != sizeof(struct acpi_srat_mem_affinity)) {
206                 bad_srat();
207                 return;
208         }
209         if ((ma->flags & ACPI_SRAT_MEM_ENABLED) == 0)
210                 return;
211
212         if ((ma->flags & ACPI_SRAT_MEM_HOT_PLUGGABLE) && !save_add_info())
213                 return;
214         start = ma->base_address;
215         end = start + ma->length;
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         }
223
224         if (numa_add_memblk(node, start, end) < 0) {
225                 bad_srat();
226                 return;
227         }
228
229         printk(KERN_INFO "SRAT: Node %u PXM %u %lx-%lx\n", node, pxm,
230                start, end);
231
232         if (ma->flags & ACPI_SRAT_MEM_HOT_PLUGGABLE)
233                 update_nodes_add(node, start, end);
234 }
235
236 void __init acpi_numa_arch_fixup(void) {}
237
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
248 #if defined(CONFIG_MEMORY_HOTPLUG_SPARSE) || defined(CONFIG_ACPI_HOTPLUG_MEMORY)
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 }
259 EXPORT_SYMBOL_GPL(memory_add_physaddr_to_nid);
260 #endif