1c710fcc5SDan Williams // SPDX-License-Identifier: GPL-2.0
2c710fcc5SDan Williams /*
3c710fcc5SDan Williams * Copyright (c) 2019, Intel Corporation.
4c710fcc5SDan Williams *
5c710fcc5SDan Williams * Heterogeneous Memory Attributes Table (HMAT) representation
6c710fcc5SDan Williams *
7c710fcc5SDan Williams * This program parses and reports the platform's HMAT tables, and registers
8c710fcc5SDan Williams * the applicable attributes with the node's interfaces.
9c710fcc5SDan Williams */
10c710fcc5SDan Williams
11cf8741acSDan Williams #define pr_fmt(fmt) "acpi/hmat: " fmt
12cf8741acSDan Williams
13c710fcc5SDan Williams #include <linux/acpi.h>
14c710fcc5SDan Williams #include <linux/bitops.h>
15c710fcc5SDan Williams #include <linux/device.h>
16c710fcc5SDan Williams #include <linux/init.h>
17c710fcc5SDan Williams #include <linux/list.h>
18cf8741acSDan Williams #include <linux/mm.h>
19cf8741acSDan Williams #include <linux/platform_device.h>
20c710fcc5SDan Williams #include <linux/list_sort.h>
21cf8741acSDan Williams #include <linux/memregion.h>
22c710fcc5SDan Williams #include <linux/memory.h>
23c710fcc5SDan Williams #include <linux/mutex.h>
24c710fcc5SDan Williams #include <linux/node.h>
25c710fcc5SDan Williams #include <linux/sysfs.h>
26c01044ccSDan Williams #include <linux/dax.h>
27c710fcc5SDan Williams
28c710fcc5SDan Williams static u8 hmat_revision;
293b0d3101SDan Williams static int hmat_disable __initdata;
303b0d3101SDan Williams
disable_hmat(void)313b0d3101SDan Williams void __init disable_hmat(void)
323b0d3101SDan Williams {
333b0d3101SDan Williams hmat_disable = 1;
343b0d3101SDan Williams }
35c710fcc5SDan Williams
36c710fcc5SDan Williams static LIST_HEAD(targets);
37c710fcc5SDan Williams static LIST_HEAD(initiators);
38c710fcc5SDan Williams static LIST_HEAD(localities);
39c710fcc5SDan Williams
40c710fcc5SDan Williams static DEFINE_MUTEX(target_lock);
41c710fcc5SDan Williams
42c710fcc5SDan Williams /*
43c710fcc5SDan Williams * The defined enum order is used to prioritize attributes to break ties when
44c710fcc5SDan Williams * selecting the best performing node.
45c710fcc5SDan Williams */
46c710fcc5SDan Williams enum locality_types {
47c710fcc5SDan Williams WRITE_LATENCY,
48c710fcc5SDan Williams READ_LATENCY,
49c710fcc5SDan Williams WRITE_BANDWIDTH,
50c710fcc5SDan Williams READ_BANDWIDTH,
51c710fcc5SDan Williams };
52c710fcc5SDan Williams
53c710fcc5SDan Williams static struct memory_locality *localities_types[4];
54c710fcc5SDan Williams
55c710fcc5SDan Williams struct target_cache {
56c710fcc5SDan Williams struct list_head node;
57c710fcc5SDan Williams struct node_cache_attrs cache_attrs;
58c710fcc5SDan Williams };
59c710fcc5SDan Williams
60c710fcc5SDan Williams struct memory_target {
61c710fcc5SDan Williams struct list_head node;
62c710fcc5SDan Williams unsigned int memory_pxm;
63c710fcc5SDan Williams unsigned int processor_pxm;
64cf8741acSDan Williams struct resource memregions;
65b9fffe47SJonathan Cameron struct node_hmem_attrs hmem_attrs[2];
66c710fcc5SDan Williams struct list_head caches;
67c710fcc5SDan Williams struct node_cache_attrs cache_attrs;
68c710fcc5SDan Williams bool registered;
69c710fcc5SDan Williams };
70c710fcc5SDan Williams
71c710fcc5SDan Williams struct memory_initiator {
72c710fcc5SDan Williams struct list_head node;
73c710fcc5SDan Williams unsigned int processor_pxm;
74b9fffe47SJonathan Cameron bool has_cpu;
75c710fcc5SDan Williams };
76c710fcc5SDan Williams
77c710fcc5SDan Williams struct memory_locality {
78c710fcc5SDan Williams struct list_head node;
79c710fcc5SDan Williams struct acpi_hmat_locality *hmat_loc;
80c710fcc5SDan Williams };
81c710fcc5SDan Williams
find_mem_initiator(unsigned int cpu_pxm)82c710fcc5SDan Williams static struct memory_initiator *find_mem_initiator(unsigned int cpu_pxm)
83c710fcc5SDan Williams {
84c710fcc5SDan Williams struct memory_initiator *initiator;
85c710fcc5SDan Williams
86c710fcc5SDan Williams list_for_each_entry(initiator, &initiators, node)
87c710fcc5SDan Williams if (initiator->processor_pxm == cpu_pxm)
88c710fcc5SDan Williams return initiator;
89c710fcc5SDan Williams return NULL;
90c710fcc5SDan Williams }
91c710fcc5SDan Williams
find_mem_target(unsigned int mem_pxm)92c710fcc5SDan Williams static struct memory_target *find_mem_target(unsigned int mem_pxm)
93c710fcc5SDan Williams {
94c710fcc5SDan Williams struct memory_target *target;
95c710fcc5SDan Williams
96c710fcc5SDan Williams list_for_each_entry(target, &targets, node)
97c710fcc5SDan Williams if (target->memory_pxm == mem_pxm)
98c710fcc5SDan Williams return target;
99c710fcc5SDan Williams return NULL;
100c710fcc5SDan Williams }
101c710fcc5SDan Williams
alloc_memory_initiator(unsigned int cpu_pxm)102c710fcc5SDan Williams static __init void alloc_memory_initiator(unsigned int cpu_pxm)
103c710fcc5SDan Williams {
104c710fcc5SDan Williams struct memory_initiator *initiator;
105c710fcc5SDan Williams
106c710fcc5SDan Williams if (pxm_to_node(cpu_pxm) == NUMA_NO_NODE)
107c710fcc5SDan Williams return;
108c710fcc5SDan Williams
109c710fcc5SDan Williams initiator = find_mem_initiator(cpu_pxm);
110c710fcc5SDan Williams if (initiator)
111c710fcc5SDan Williams return;
112c710fcc5SDan Williams
113c710fcc5SDan Williams initiator = kzalloc(sizeof(*initiator), GFP_KERNEL);
114c710fcc5SDan Williams if (!initiator)
115c710fcc5SDan Williams return;
116c710fcc5SDan Williams
117c710fcc5SDan Williams initiator->processor_pxm = cpu_pxm;
118b9fffe47SJonathan Cameron initiator->has_cpu = node_state(pxm_to_node(cpu_pxm), N_CPU);
119c710fcc5SDan Williams list_add_tail(&initiator->node, &initiators);
120c710fcc5SDan Williams }
121c710fcc5SDan Williams
alloc_memory_target(unsigned int mem_pxm,resource_size_t start,resource_size_t len)122cf8741acSDan Williams static __init void alloc_memory_target(unsigned int mem_pxm,
123cf8741acSDan Williams resource_size_t start, resource_size_t len)
124c710fcc5SDan Williams {
125c710fcc5SDan Williams struct memory_target *target;
126c710fcc5SDan Williams
127c710fcc5SDan Williams target = find_mem_target(mem_pxm);
128cf8741acSDan Williams if (!target) {
129c710fcc5SDan Williams target = kzalloc(sizeof(*target), GFP_KERNEL);
130c710fcc5SDan Williams if (!target)
131c710fcc5SDan Williams return;
132c710fcc5SDan Williams target->memory_pxm = mem_pxm;
133c710fcc5SDan Williams target->processor_pxm = PXM_INVAL;
134cf8741acSDan Williams target->memregions = (struct resource) {
135cf8741acSDan Williams .name = "ACPI mem",
136cf8741acSDan Williams .start = 0,
137cf8741acSDan Williams .end = -1,
138cf8741acSDan Williams .flags = IORESOURCE_MEM,
139cf8741acSDan Williams };
140c710fcc5SDan Williams list_add_tail(&target->node, &targets);
141c710fcc5SDan Williams INIT_LIST_HEAD(&target->caches);
142c710fcc5SDan Williams }
143c710fcc5SDan Williams
144cf8741acSDan Williams /*
145cf8741acSDan Williams * There are potentially multiple ranges per PXM, so record each
146cf8741acSDan Williams * in the per-target memregions resource tree.
147cf8741acSDan Williams */
148cf8741acSDan Williams if (!__request_region(&target->memregions, start, len, "memory target",
149cf8741acSDan Williams IORESOURCE_MEM))
150cf8741acSDan Williams pr_warn("failed to reserve %#llx - %#llx in pxm: %d\n",
151cf8741acSDan Williams start, start + len, mem_pxm);
152cf8741acSDan Williams }
153cf8741acSDan Williams
hmat_data_type(u8 type)154c710fcc5SDan Williams static __init const char *hmat_data_type(u8 type)
155c710fcc5SDan Williams {
156c710fcc5SDan Williams switch (type) {
157c710fcc5SDan Williams case ACPI_HMAT_ACCESS_LATENCY:
158c710fcc5SDan Williams return "Access Latency";
159c710fcc5SDan Williams case ACPI_HMAT_READ_LATENCY:
160c710fcc5SDan Williams return "Read Latency";
161c710fcc5SDan Williams case ACPI_HMAT_WRITE_LATENCY:
162c710fcc5SDan Williams return "Write Latency";
163c710fcc5SDan Williams case ACPI_HMAT_ACCESS_BANDWIDTH:
164c710fcc5SDan Williams return "Access Bandwidth";
165c710fcc5SDan Williams case ACPI_HMAT_READ_BANDWIDTH:
166c710fcc5SDan Williams return "Read Bandwidth";
167c710fcc5SDan Williams case ACPI_HMAT_WRITE_BANDWIDTH:
168c710fcc5SDan Williams return "Write Bandwidth";
169c710fcc5SDan Williams default:
170c710fcc5SDan Williams return "Reserved";
171c710fcc5SDan Williams }
172c710fcc5SDan Williams }
173c710fcc5SDan Williams
hmat_data_type_suffix(u8 type)174c710fcc5SDan Williams static __init const char *hmat_data_type_suffix(u8 type)
175c710fcc5SDan Williams {
176c710fcc5SDan Williams switch (type) {
177c710fcc5SDan Williams case ACPI_HMAT_ACCESS_LATENCY:
178c710fcc5SDan Williams case ACPI_HMAT_READ_LATENCY:
179c710fcc5SDan Williams case ACPI_HMAT_WRITE_LATENCY:
180c710fcc5SDan Williams return " nsec";
181c710fcc5SDan Williams case ACPI_HMAT_ACCESS_BANDWIDTH:
182c710fcc5SDan Williams case ACPI_HMAT_READ_BANDWIDTH:
183c710fcc5SDan Williams case ACPI_HMAT_WRITE_BANDWIDTH:
184c710fcc5SDan Williams return " MB/s";
185c710fcc5SDan Williams default:
186c710fcc5SDan Williams return "";
187c710fcc5SDan Williams }
188c710fcc5SDan Williams }
189c710fcc5SDan Williams
hmat_normalize(u16 entry,u64 base,u8 type)190c710fcc5SDan Williams static u32 hmat_normalize(u16 entry, u64 base, u8 type)
191c710fcc5SDan Williams {
192c710fcc5SDan Williams u32 value;
193c710fcc5SDan Williams
194c710fcc5SDan Williams /*
195c710fcc5SDan Williams * Check for invalid and overflow values
196c710fcc5SDan Williams */
197c710fcc5SDan Williams if (entry == 0xffff || !entry)
198c710fcc5SDan Williams return 0;
199c710fcc5SDan Williams else if (base > (UINT_MAX / (entry)))
200c710fcc5SDan Williams return 0;
201c710fcc5SDan Williams
202c710fcc5SDan Williams /*
203c710fcc5SDan Williams * Divide by the base unit for version 1, convert latency from
204c710fcc5SDan Williams * picosenonds to nanoseconds if revision 2.
205c710fcc5SDan Williams */
206c710fcc5SDan Williams value = entry * base;
207c710fcc5SDan Williams if (hmat_revision == 1) {
208c710fcc5SDan Williams if (value < 10)
209c710fcc5SDan Williams return 0;
210c710fcc5SDan Williams value = DIV_ROUND_UP(value, 10);
211c710fcc5SDan Williams } else if (hmat_revision == 2) {
212c710fcc5SDan Williams switch (type) {
213c710fcc5SDan Williams case ACPI_HMAT_ACCESS_LATENCY:
214c710fcc5SDan Williams case ACPI_HMAT_READ_LATENCY:
215c710fcc5SDan Williams case ACPI_HMAT_WRITE_LATENCY:
216c710fcc5SDan Williams value = DIV_ROUND_UP(value, 1000);
217c710fcc5SDan Williams break;
218c710fcc5SDan Williams default:
219c710fcc5SDan Williams break;
220c710fcc5SDan Williams }
221c710fcc5SDan Williams }
222c710fcc5SDan Williams return value;
223c710fcc5SDan Williams }
224c710fcc5SDan Williams
hmat_update_target_access(struct memory_target * target,u8 type,u32 value,int access)225c710fcc5SDan Williams static void hmat_update_target_access(struct memory_target *target,
226b9fffe47SJonathan Cameron u8 type, u32 value, int access)
227c710fcc5SDan Williams {
228c710fcc5SDan Williams switch (type) {
229c710fcc5SDan Williams case ACPI_HMAT_ACCESS_LATENCY:
230b9fffe47SJonathan Cameron target->hmem_attrs[access].read_latency = value;
231b9fffe47SJonathan Cameron target->hmem_attrs[access].write_latency = value;
232c710fcc5SDan Williams break;
233c710fcc5SDan Williams case ACPI_HMAT_READ_LATENCY:
234b9fffe47SJonathan Cameron target->hmem_attrs[access].read_latency = value;
235c710fcc5SDan Williams break;
236c710fcc5SDan Williams case ACPI_HMAT_WRITE_LATENCY:
237b9fffe47SJonathan Cameron target->hmem_attrs[access].write_latency = value;
238c710fcc5SDan Williams break;
239c710fcc5SDan Williams case ACPI_HMAT_ACCESS_BANDWIDTH:
240b9fffe47SJonathan Cameron target->hmem_attrs[access].read_bandwidth = value;
241b9fffe47SJonathan Cameron target->hmem_attrs[access].write_bandwidth = value;
242c710fcc5SDan Williams break;
243c710fcc5SDan Williams case ACPI_HMAT_READ_BANDWIDTH:
244b9fffe47SJonathan Cameron target->hmem_attrs[access].read_bandwidth = value;
245c710fcc5SDan Williams break;
246c710fcc5SDan Williams case ACPI_HMAT_WRITE_BANDWIDTH:
247b9fffe47SJonathan Cameron target->hmem_attrs[access].write_bandwidth = value;
248c710fcc5SDan Williams break;
249c710fcc5SDan Williams default:
250c710fcc5SDan Williams break;
251c710fcc5SDan Williams }
252c710fcc5SDan Williams }
253c710fcc5SDan Williams
hmat_add_locality(struct acpi_hmat_locality * hmat_loc)254c710fcc5SDan Williams static __init void hmat_add_locality(struct acpi_hmat_locality *hmat_loc)
255c710fcc5SDan Williams {
256c710fcc5SDan Williams struct memory_locality *loc;
257c710fcc5SDan Williams
258c710fcc5SDan Williams loc = kzalloc(sizeof(*loc), GFP_KERNEL);
259c710fcc5SDan Williams if (!loc) {
260c710fcc5SDan Williams pr_notice_once("Failed to allocate HMAT locality\n");
261c710fcc5SDan Williams return;
262c710fcc5SDan Williams }
263c710fcc5SDan Williams
264c710fcc5SDan Williams loc->hmat_loc = hmat_loc;
265c710fcc5SDan Williams list_add_tail(&loc->node, &localities);
266c710fcc5SDan Williams
267c710fcc5SDan Williams switch (hmat_loc->data_type) {
268c710fcc5SDan Williams case ACPI_HMAT_ACCESS_LATENCY:
269c710fcc5SDan Williams localities_types[READ_LATENCY] = loc;
270c710fcc5SDan Williams localities_types[WRITE_LATENCY] = loc;
271c710fcc5SDan Williams break;
272c710fcc5SDan Williams case ACPI_HMAT_READ_LATENCY:
273c710fcc5SDan Williams localities_types[READ_LATENCY] = loc;
274c710fcc5SDan Williams break;
275c710fcc5SDan Williams case ACPI_HMAT_WRITE_LATENCY:
276c710fcc5SDan Williams localities_types[WRITE_LATENCY] = loc;
277c710fcc5SDan Williams break;
278c710fcc5SDan Williams case ACPI_HMAT_ACCESS_BANDWIDTH:
279c710fcc5SDan Williams localities_types[READ_BANDWIDTH] = loc;
280c710fcc5SDan Williams localities_types[WRITE_BANDWIDTH] = loc;
281c710fcc5SDan Williams break;
282c710fcc5SDan Williams case ACPI_HMAT_READ_BANDWIDTH:
283c710fcc5SDan Williams localities_types[READ_BANDWIDTH] = loc;
284c710fcc5SDan Williams break;
285c710fcc5SDan Williams case ACPI_HMAT_WRITE_BANDWIDTH:
286c710fcc5SDan Williams localities_types[WRITE_BANDWIDTH] = loc;
287c710fcc5SDan Williams break;
288c710fcc5SDan Williams default:
289c710fcc5SDan Williams break;
290c710fcc5SDan Williams }
291c710fcc5SDan Williams }
292c710fcc5SDan Williams
hmat_parse_locality(union acpi_subtable_headers * header,const unsigned long end)293c710fcc5SDan Williams static __init int hmat_parse_locality(union acpi_subtable_headers *header,
294c710fcc5SDan Williams const unsigned long end)
295c710fcc5SDan Williams {
296c710fcc5SDan Williams struct acpi_hmat_locality *hmat_loc = (void *)header;
297c710fcc5SDan Williams struct memory_target *target;
298c710fcc5SDan Williams unsigned int init, targ, total_size, ipds, tpds;
299c710fcc5SDan Williams u32 *inits, *targs, value;
300c710fcc5SDan Williams u16 *entries;
301c710fcc5SDan Williams u8 type, mem_hier;
302c710fcc5SDan Williams
303c710fcc5SDan Williams if (hmat_loc->header.length < sizeof(*hmat_loc)) {
30456216359SLiu Shixin pr_notice("Unexpected locality header length: %u\n",
305c710fcc5SDan Williams hmat_loc->header.length);
306c710fcc5SDan Williams return -EINVAL;
307c710fcc5SDan Williams }
308c710fcc5SDan Williams
309c710fcc5SDan Williams type = hmat_loc->data_type;
310c710fcc5SDan Williams mem_hier = hmat_loc->flags & ACPI_HMAT_MEMORY_HIERARCHY;
311c710fcc5SDan Williams ipds = hmat_loc->number_of_initiator_Pds;
312c710fcc5SDan Williams tpds = hmat_loc->number_of_target_Pds;
313c710fcc5SDan Williams total_size = sizeof(*hmat_loc) + sizeof(*entries) * ipds * tpds +
314c710fcc5SDan Williams sizeof(*inits) * ipds + sizeof(*targs) * tpds;
315c710fcc5SDan Williams if (hmat_loc->header.length < total_size) {
31656216359SLiu Shixin pr_notice("Unexpected locality header length:%u, minimum required:%u\n",
317c710fcc5SDan Williams hmat_loc->header.length, total_size);
318c710fcc5SDan Williams return -EINVAL;
319c710fcc5SDan Williams }
320c710fcc5SDan Williams
32156216359SLiu Shixin pr_info("Locality: Flags:%02x Type:%s Initiator Domains:%u Target Domains:%u Base:%lld\n",
322c710fcc5SDan Williams hmat_loc->flags, hmat_data_type(type), ipds, tpds,
323c710fcc5SDan Williams hmat_loc->entry_base_unit);
324c710fcc5SDan Williams
325c710fcc5SDan Williams inits = (u32 *)(hmat_loc + 1);
326c710fcc5SDan Williams targs = inits + ipds;
327c710fcc5SDan Williams entries = (u16 *)(targs + tpds);
328c710fcc5SDan Williams for (init = 0; init < ipds; init++) {
329c710fcc5SDan Williams alloc_memory_initiator(inits[init]);
330c710fcc5SDan Williams for (targ = 0; targ < tpds; targ++) {
331c710fcc5SDan Williams value = hmat_normalize(entries[init * tpds + targ],
332c710fcc5SDan Williams hmat_loc->entry_base_unit,
333c710fcc5SDan Williams type);
3340f1839d0STao Xu pr_info(" Initiator-Target[%u-%u]:%u%s\n",
335c710fcc5SDan Williams inits[init], targs[targ], value,
336c710fcc5SDan Williams hmat_data_type_suffix(type));
337c710fcc5SDan Williams
338c710fcc5SDan Williams if (mem_hier == ACPI_HMAT_MEMORY) {
339c710fcc5SDan Williams target = find_mem_target(targs[targ]);
340b9fffe47SJonathan Cameron if (target && target->processor_pxm == inits[init]) {
341b9fffe47SJonathan Cameron hmat_update_target_access(target, type, value, 0);
342b9fffe47SJonathan Cameron /* If the node has a CPU, update access 1 */
343b9fffe47SJonathan Cameron if (node_state(pxm_to_node(inits[init]), N_CPU))
344b9fffe47SJonathan Cameron hmat_update_target_access(target, type, value, 1);
345b9fffe47SJonathan Cameron }
346c710fcc5SDan Williams }
347c710fcc5SDan Williams }
348c710fcc5SDan Williams }
349c710fcc5SDan Williams
350c710fcc5SDan Williams if (mem_hier == ACPI_HMAT_MEMORY)
351c710fcc5SDan Williams hmat_add_locality(hmat_loc);
352c710fcc5SDan Williams
353c710fcc5SDan Williams return 0;
354c710fcc5SDan Williams }
355c710fcc5SDan Williams
hmat_parse_cache(union acpi_subtable_headers * header,const unsigned long end)356c710fcc5SDan Williams static __init int hmat_parse_cache(union acpi_subtable_headers *header,
357c710fcc5SDan Williams const unsigned long end)
358c710fcc5SDan Williams {
359c710fcc5SDan Williams struct acpi_hmat_cache *cache = (void *)header;
360c710fcc5SDan Williams struct memory_target *target;
361c710fcc5SDan Williams struct target_cache *tcache;
362c710fcc5SDan Williams u32 attrs;
363c710fcc5SDan Williams
364c710fcc5SDan Williams if (cache->header.length < sizeof(*cache)) {
36556216359SLiu Shixin pr_notice("Unexpected cache header length: %u\n",
366c710fcc5SDan Williams cache->header.length);
367c710fcc5SDan Williams return -EINVAL;
368c710fcc5SDan Williams }
369c710fcc5SDan Williams
370c710fcc5SDan Williams attrs = cache->cache_attributes;
37156216359SLiu Shixin pr_info("Cache: Domain:%u Size:%llu Attrs:%08x SMBIOS Handles:%d\n",
372c710fcc5SDan Williams cache->memory_PD, cache->cache_size, attrs,
373c710fcc5SDan Williams cache->number_of_SMBIOShandles);
374c710fcc5SDan Williams
375c710fcc5SDan Williams target = find_mem_target(cache->memory_PD);
376c710fcc5SDan Williams if (!target)
377c710fcc5SDan Williams return 0;
378c710fcc5SDan Williams
379c710fcc5SDan Williams tcache = kzalloc(sizeof(*tcache), GFP_KERNEL);
380c710fcc5SDan Williams if (!tcache) {
381c710fcc5SDan Williams pr_notice_once("Failed to allocate HMAT cache info\n");
382c710fcc5SDan Williams return 0;
383c710fcc5SDan Williams }
384c710fcc5SDan Williams
385c710fcc5SDan Williams tcache->cache_attrs.size = cache->cache_size;
386c710fcc5SDan Williams tcache->cache_attrs.level = (attrs & ACPI_HMAT_CACHE_LEVEL) >> 4;
387c710fcc5SDan Williams tcache->cache_attrs.line_size = (attrs & ACPI_HMAT_CACHE_LINE_SIZE) >> 16;
388c710fcc5SDan Williams
389c710fcc5SDan Williams switch ((attrs & ACPI_HMAT_CACHE_ASSOCIATIVITY) >> 8) {
390c710fcc5SDan Williams case ACPI_HMAT_CA_DIRECT_MAPPED:
391c710fcc5SDan Williams tcache->cache_attrs.indexing = NODE_CACHE_DIRECT_MAP;
392c710fcc5SDan Williams break;
393c710fcc5SDan Williams case ACPI_HMAT_CA_COMPLEX_CACHE_INDEXING:
394c710fcc5SDan Williams tcache->cache_attrs.indexing = NODE_CACHE_INDEXED;
395c710fcc5SDan Williams break;
396c710fcc5SDan Williams case ACPI_HMAT_CA_NONE:
397c710fcc5SDan Williams default:
398c710fcc5SDan Williams tcache->cache_attrs.indexing = NODE_CACHE_OTHER;
399c710fcc5SDan Williams break;
400c710fcc5SDan Williams }
401c710fcc5SDan Williams
402c710fcc5SDan Williams switch ((attrs & ACPI_HMAT_WRITE_POLICY) >> 12) {
403c710fcc5SDan Williams case ACPI_HMAT_CP_WB:
404c710fcc5SDan Williams tcache->cache_attrs.write_policy = NODE_CACHE_WRITE_BACK;
405c710fcc5SDan Williams break;
406c710fcc5SDan Williams case ACPI_HMAT_CP_WT:
407c710fcc5SDan Williams tcache->cache_attrs.write_policy = NODE_CACHE_WRITE_THROUGH;
408c710fcc5SDan Williams break;
409c710fcc5SDan Williams case ACPI_HMAT_CP_NONE:
410c710fcc5SDan Williams default:
411c710fcc5SDan Williams tcache->cache_attrs.write_policy = NODE_CACHE_WRITE_OTHER;
412c710fcc5SDan Williams break;
413c710fcc5SDan Williams }
414c710fcc5SDan Williams list_add_tail(&tcache->node, &target->caches);
415c710fcc5SDan Williams
416c710fcc5SDan Williams return 0;
417c710fcc5SDan Williams }
418c710fcc5SDan Williams
hmat_parse_proximity_domain(union acpi_subtable_headers * header,const unsigned long end)419c710fcc5SDan Williams static int __init hmat_parse_proximity_domain(union acpi_subtable_headers *header,
420c710fcc5SDan Williams const unsigned long end)
421c710fcc5SDan Williams {
422c710fcc5SDan Williams struct acpi_hmat_proximity_domain *p = (void *)header;
423c710fcc5SDan Williams struct memory_target *target = NULL;
424c710fcc5SDan Williams
425c710fcc5SDan Williams if (p->header.length != sizeof(*p)) {
42656216359SLiu Shixin pr_notice("Unexpected address range header length: %u\n",
427c710fcc5SDan Williams p->header.length);
428c710fcc5SDan Williams return -EINVAL;
429c710fcc5SDan Williams }
430c710fcc5SDan Williams
431c710fcc5SDan Williams if (hmat_revision == 1)
43256216359SLiu Shixin pr_info("Memory (%#llx length %#llx) Flags:%04x Processor Domain:%u Memory Domain:%u\n",
433c710fcc5SDan Williams p->reserved3, p->reserved4, p->flags, p->processor_PD,
434c710fcc5SDan Williams p->memory_PD);
435c710fcc5SDan Williams else
43656216359SLiu Shixin pr_info("Memory Flags:%04x Processor Domain:%u Memory Domain:%u\n",
437c710fcc5SDan Williams p->flags, p->processor_PD, p->memory_PD);
438c710fcc5SDan Williams
4392c5b9bdeSJonathan Cameron if ((hmat_revision == 1 && p->flags & ACPI_HMAT_MEMORY_PD_VALID) ||
4402c5b9bdeSJonathan Cameron hmat_revision > 1) {
441c710fcc5SDan Williams target = find_mem_target(p->memory_PD);
442c710fcc5SDan Williams if (!target) {
44356216359SLiu Shixin pr_debug("Memory Domain missing from SRAT\n");
444c710fcc5SDan Williams return -EINVAL;
445c710fcc5SDan Williams }
446c710fcc5SDan Williams }
447c710fcc5SDan Williams if (target && p->flags & ACPI_HMAT_PROCESSOR_PD_VALID) {
448c710fcc5SDan Williams int p_node = pxm_to_node(p->processor_PD);
449c710fcc5SDan Williams
450c710fcc5SDan Williams if (p_node == NUMA_NO_NODE) {
45156216359SLiu Shixin pr_debug("Invalid Processor Domain\n");
452c710fcc5SDan Williams return -EINVAL;
453c710fcc5SDan Williams }
4544caa525bSBrice Goglin target->processor_pxm = p->processor_PD;
455c710fcc5SDan Williams }
456c710fcc5SDan Williams
457c710fcc5SDan Williams return 0;
458c710fcc5SDan Williams }
459c710fcc5SDan Williams
hmat_parse_subtable(union acpi_subtable_headers * header,const unsigned long end)460c710fcc5SDan Williams static int __init hmat_parse_subtable(union acpi_subtable_headers *header,
461c710fcc5SDan Williams const unsigned long end)
462c710fcc5SDan Williams {
463c710fcc5SDan Williams struct acpi_hmat_structure *hdr = (void *)header;
464c710fcc5SDan Williams
465c710fcc5SDan Williams if (!hdr)
466c710fcc5SDan Williams return -EINVAL;
467c710fcc5SDan Williams
468c710fcc5SDan Williams switch (hdr->type) {
469c710fcc5SDan Williams case ACPI_HMAT_TYPE_PROXIMITY:
470c710fcc5SDan Williams return hmat_parse_proximity_domain(header, end);
471c710fcc5SDan Williams case ACPI_HMAT_TYPE_LOCALITY:
472c710fcc5SDan Williams return hmat_parse_locality(header, end);
473c710fcc5SDan Williams case ACPI_HMAT_TYPE_CACHE:
474c710fcc5SDan Williams return hmat_parse_cache(header, end);
475c710fcc5SDan Williams default:
476c710fcc5SDan Williams return -EINVAL;
477c710fcc5SDan Williams }
478c710fcc5SDan Williams }
479c710fcc5SDan Williams
srat_parse_mem_affinity(union acpi_subtable_headers * header,const unsigned long end)480c710fcc5SDan Williams static __init int srat_parse_mem_affinity(union acpi_subtable_headers *header,
481c710fcc5SDan Williams const unsigned long end)
482c710fcc5SDan Williams {
483c710fcc5SDan Williams struct acpi_srat_mem_affinity *ma = (void *)header;
484c710fcc5SDan Williams
485c710fcc5SDan Williams if (!ma)
486c710fcc5SDan Williams return -EINVAL;
487c710fcc5SDan Williams if (!(ma->flags & ACPI_SRAT_MEM_ENABLED))
488c710fcc5SDan Williams return 0;
489cf8741acSDan Williams alloc_memory_target(ma->proximity_domain, ma->base_address, ma->length);
490c710fcc5SDan Williams return 0;
491c710fcc5SDan Williams }
492c710fcc5SDan Williams
hmat_initiator_perf(struct memory_target * target,struct memory_initiator * initiator,struct acpi_hmat_locality * hmat_loc)493c710fcc5SDan Williams static u32 hmat_initiator_perf(struct memory_target *target,
494c710fcc5SDan Williams struct memory_initiator *initiator,
495c710fcc5SDan Williams struct acpi_hmat_locality *hmat_loc)
496c710fcc5SDan Williams {
497c710fcc5SDan Williams unsigned int ipds, tpds, i, idx = 0, tdx = 0;
498c710fcc5SDan Williams u32 *inits, *targs;
499c710fcc5SDan Williams u16 *entries;
500c710fcc5SDan Williams
501c710fcc5SDan Williams ipds = hmat_loc->number_of_initiator_Pds;
502c710fcc5SDan Williams tpds = hmat_loc->number_of_target_Pds;
503c710fcc5SDan Williams inits = (u32 *)(hmat_loc + 1);
504c710fcc5SDan Williams targs = inits + ipds;
505c710fcc5SDan Williams entries = (u16 *)(targs + tpds);
506c710fcc5SDan Williams
507c710fcc5SDan Williams for (i = 0; i < ipds; i++) {
508c710fcc5SDan Williams if (inits[i] == initiator->processor_pxm) {
509c710fcc5SDan Williams idx = i;
510c710fcc5SDan Williams break;
511c710fcc5SDan Williams }
512c710fcc5SDan Williams }
513c710fcc5SDan Williams
514c710fcc5SDan Williams if (i == ipds)
515c710fcc5SDan Williams return 0;
516c710fcc5SDan Williams
517c710fcc5SDan Williams for (i = 0; i < tpds; i++) {
518c710fcc5SDan Williams if (targs[i] == target->memory_pxm) {
519c710fcc5SDan Williams tdx = i;
520c710fcc5SDan Williams break;
521c710fcc5SDan Williams }
522c710fcc5SDan Williams }
523c710fcc5SDan Williams if (i == tpds)
524c710fcc5SDan Williams return 0;
525c710fcc5SDan Williams
526c710fcc5SDan Williams return hmat_normalize(entries[idx * tpds + tdx],
527c710fcc5SDan Williams hmat_loc->entry_base_unit,
528c710fcc5SDan Williams hmat_loc->data_type);
529c710fcc5SDan Williams }
530c710fcc5SDan Williams
hmat_update_best(u8 type,u32 value,u32 * best)531c710fcc5SDan Williams static bool hmat_update_best(u8 type, u32 value, u32 *best)
532c710fcc5SDan Williams {
533c710fcc5SDan Williams bool updated = false;
534c710fcc5SDan Williams
535c710fcc5SDan Williams if (!value)
536c710fcc5SDan Williams return false;
537c710fcc5SDan Williams
538c710fcc5SDan Williams switch (type) {
539c710fcc5SDan Williams case ACPI_HMAT_ACCESS_LATENCY:
540c710fcc5SDan Williams case ACPI_HMAT_READ_LATENCY:
541c710fcc5SDan Williams case ACPI_HMAT_WRITE_LATENCY:
542c710fcc5SDan Williams if (!*best || *best > value) {
543c710fcc5SDan Williams *best = value;
544c710fcc5SDan Williams updated = true;
545c710fcc5SDan Williams }
546c710fcc5SDan Williams break;
547c710fcc5SDan Williams case ACPI_HMAT_ACCESS_BANDWIDTH:
548c710fcc5SDan Williams case ACPI_HMAT_READ_BANDWIDTH:
549c710fcc5SDan Williams case ACPI_HMAT_WRITE_BANDWIDTH:
550c710fcc5SDan Williams if (!*best || *best < value) {
551c710fcc5SDan Williams *best = value;
552c710fcc5SDan Williams updated = true;
553c710fcc5SDan Williams }
554c710fcc5SDan Williams break;
555c710fcc5SDan Williams }
556c710fcc5SDan Williams
557c710fcc5SDan Williams return updated;
558c710fcc5SDan Williams }
559c710fcc5SDan Williams
initiator_cmp(void * priv,const struct list_head * a,const struct list_head * b)5604f0f586bSSami Tolvanen static int initiator_cmp(void *priv, const struct list_head *a,
5614f0f586bSSami Tolvanen const struct list_head *b)
562c710fcc5SDan Williams {
563c710fcc5SDan Williams struct memory_initiator *ia;
564c710fcc5SDan Williams struct memory_initiator *ib;
565c710fcc5SDan Williams
566c710fcc5SDan Williams ia = list_entry(a, struct memory_initiator, node);
567c710fcc5SDan Williams ib = list_entry(b, struct memory_initiator, node);
568c710fcc5SDan Williams
569c710fcc5SDan Williams return ia->processor_pxm - ib->processor_pxm;
570c710fcc5SDan Williams }
571c710fcc5SDan Williams
initiators_to_nodemask(unsigned long * p_nodes)57248d41809SVishal Verma static int initiators_to_nodemask(unsigned long *p_nodes)
57348d41809SVishal Verma {
57448d41809SVishal Verma struct memory_initiator *initiator;
57548d41809SVishal Verma
57648d41809SVishal Verma if (list_empty(&initiators))
57748d41809SVishal Verma return -ENXIO;
57848d41809SVishal Verma
57948d41809SVishal Verma list_for_each_entry(initiator, &initiators, node)
58048d41809SVishal Verma set_bit(initiator->processor_pxm, p_nodes);
58148d41809SVishal Verma
58248d41809SVishal Verma return 0;
58348d41809SVishal Verma }
58448d41809SVishal Verma
hmat_register_target_initiators(struct memory_target * target)585c710fcc5SDan Williams static void hmat_register_target_initiators(struct memory_target *target)
586c710fcc5SDan Williams {
587c710fcc5SDan Williams static DECLARE_BITMAP(p_nodes, MAX_NUMNODES);
588c710fcc5SDan Williams struct memory_initiator *initiator;
589c710fcc5SDan Williams unsigned int mem_nid, cpu_nid;
590c710fcc5SDan Williams struct memory_locality *loc = NULL;
591c710fcc5SDan Williams u32 best = 0;
592b9fffe47SJonathan Cameron bool access0done = false;
593c710fcc5SDan Williams int i;
594c710fcc5SDan Williams
595c710fcc5SDan Williams mem_nid = pxm_to_node(target->memory_pxm);
596c710fcc5SDan Williams /*
597c710fcc5SDan Williams * If the Address Range Structure provides a local processor pxm, link
598c710fcc5SDan Williams * only that one. Otherwise, find the best performance attributes and
599c710fcc5SDan Williams * register all initiators that match.
600c710fcc5SDan Williams */
601c710fcc5SDan Williams if (target->processor_pxm != PXM_INVAL) {
602c710fcc5SDan Williams cpu_nid = pxm_to_node(target->processor_pxm);
603c710fcc5SDan Williams register_memory_node_under_compute_node(mem_nid, cpu_nid, 0);
604b9fffe47SJonathan Cameron access0done = true;
605b9fffe47SJonathan Cameron if (node_state(cpu_nid, N_CPU)) {
606b9fffe47SJonathan Cameron register_memory_node_under_compute_node(mem_nid, cpu_nid, 1);
607c710fcc5SDan Williams return;
608c710fcc5SDan Williams }
609b9fffe47SJonathan Cameron }
610c710fcc5SDan Williams
611c710fcc5SDan Williams if (list_empty(&localities))
612c710fcc5SDan Williams return;
613c710fcc5SDan Williams
614c710fcc5SDan Williams /*
615c710fcc5SDan Williams * We need the initiator list sorted so we can use bitmap_clear for
616c710fcc5SDan Williams * previously set initiators when we find a better memory accessor.
617c710fcc5SDan Williams * We'll also use the sorting to prime the candidate nodes with known
618c710fcc5SDan Williams * initiators.
619c710fcc5SDan Williams */
620c710fcc5SDan Williams bitmap_zero(p_nodes, MAX_NUMNODES);
62148d41809SVishal Verma list_sort(NULL, &initiators, initiator_cmp);
62248d41809SVishal Verma if (initiators_to_nodemask(p_nodes) < 0)
62348d41809SVishal Verma return;
62448d41809SVishal Verma
625b9fffe47SJonathan Cameron if (!access0done) {
626c710fcc5SDan Williams for (i = WRITE_LATENCY; i <= READ_BANDWIDTH; i++) {
627c710fcc5SDan Williams loc = localities_types[i];
628c710fcc5SDan Williams if (!loc)
629c710fcc5SDan Williams continue;
630c710fcc5SDan Williams
631c710fcc5SDan Williams best = 0;
632c710fcc5SDan Williams list_for_each_entry(initiator, &initiators, node) {
633c710fcc5SDan Williams u32 value;
634c710fcc5SDan Williams
635c710fcc5SDan Williams if (!test_bit(initiator->processor_pxm, p_nodes))
636c710fcc5SDan Williams continue;
637c710fcc5SDan Williams
638b9fffe47SJonathan Cameron value = hmat_initiator_perf(target, initiator,
639b9fffe47SJonathan Cameron loc->hmat_loc);
640b9fffe47SJonathan Cameron if (hmat_update_best(loc->hmat_loc->data_type, value, &best))
641b9fffe47SJonathan Cameron bitmap_clear(p_nodes, 0, initiator->processor_pxm);
642b9fffe47SJonathan Cameron if (value != best)
643b9fffe47SJonathan Cameron clear_bit(initiator->processor_pxm, p_nodes);
644b9fffe47SJonathan Cameron }
645b9fffe47SJonathan Cameron if (best)
646b9fffe47SJonathan Cameron hmat_update_target_access(target, loc->hmat_loc->data_type,
647b9fffe47SJonathan Cameron best, 0);
648b9fffe47SJonathan Cameron }
649b9fffe47SJonathan Cameron
650b9fffe47SJonathan Cameron for_each_set_bit(i, p_nodes, MAX_NUMNODES) {
651b9fffe47SJonathan Cameron cpu_nid = pxm_to_node(i);
652b9fffe47SJonathan Cameron register_memory_node_under_compute_node(mem_nid, cpu_nid, 0);
653b9fffe47SJonathan Cameron }
654b9fffe47SJonathan Cameron }
655b9fffe47SJonathan Cameron
656b9fffe47SJonathan Cameron /* Access 1 ignores Generic Initiators */
657b9fffe47SJonathan Cameron bitmap_zero(p_nodes, MAX_NUMNODES);
65848d41809SVishal Verma if (initiators_to_nodemask(p_nodes) < 0)
65948d41809SVishal Verma return;
66048d41809SVishal Verma
661b9fffe47SJonathan Cameron for (i = WRITE_LATENCY; i <= READ_BANDWIDTH; i++) {
662b9fffe47SJonathan Cameron loc = localities_types[i];
663b9fffe47SJonathan Cameron if (!loc)
664b9fffe47SJonathan Cameron continue;
665b9fffe47SJonathan Cameron
666b9fffe47SJonathan Cameron best = 0;
667b9fffe47SJonathan Cameron list_for_each_entry(initiator, &initiators, node) {
668b9fffe47SJonathan Cameron u32 value;
669b9fffe47SJonathan Cameron
670b9fffe47SJonathan Cameron if (!initiator->has_cpu) {
671b9fffe47SJonathan Cameron clear_bit(initiator->processor_pxm, p_nodes);
672b9fffe47SJonathan Cameron continue;
673b9fffe47SJonathan Cameron }
674b9fffe47SJonathan Cameron if (!test_bit(initiator->processor_pxm, p_nodes))
675b9fffe47SJonathan Cameron continue;
676b9fffe47SJonathan Cameron
677c710fcc5SDan Williams value = hmat_initiator_perf(target, initiator, loc->hmat_loc);
678c710fcc5SDan Williams if (hmat_update_best(loc->hmat_loc->data_type, value, &best))
679c710fcc5SDan Williams bitmap_clear(p_nodes, 0, initiator->processor_pxm);
680c710fcc5SDan Williams if (value != best)
681c710fcc5SDan Williams clear_bit(initiator->processor_pxm, p_nodes);
682c710fcc5SDan Williams }
683c710fcc5SDan Williams if (best)
684b9fffe47SJonathan Cameron hmat_update_target_access(target, loc->hmat_loc->data_type, best, 1);
685c710fcc5SDan Williams }
686c710fcc5SDan Williams for_each_set_bit(i, p_nodes, MAX_NUMNODES) {
687c710fcc5SDan Williams cpu_nid = pxm_to_node(i);
688b9fffe47SJonathan Cameron register_memory_node_under_compute_node(mem_nid, cpu_nid, 1);
689c710fcc5SDan Williams }
690c710fcc5SDan Williams }
691c710fcc5SDan Williams
hmat_register_target_cache(struct memory_target * target)692c710fcc5SDan Williams static void hmat_register_target_cache(struct memory_target *target)
693c710fcc5SDan Williams {
694c710fcc5SDan Williams unsigned mem_nid = pxm_to_node(target->memory_pxm);
695c710fcc5SDan Williams struct target_cache *tcache;
696c710fcc5SDan Williams
697c710fcc5SDan Williams list_for_each_entry(tcache, &target->caches, node)
698c710fcc5SDan Williams node_add_cache(mem_nid, &tcache->cache_attrs);
699c710fcc5SDan Williams }
700c710fcc5SDan Williams
hmat_register_target_perf(struct memory_target * target,int access)701b9fffe47SJonathan Cameron static void hmat_register_target_perf(struct memory_target *target, int access)
702c710fcc5SDan Williams {
703c710fcc5SDan Williams unsigned mem_nid = pxm_to_node(target->memory_pxm);
704b9fffe47SJonathan Cameron node_set_perf_attrs(mem_nid, &target->hmem_attrs[access], access);
705c710fcc5SDan Williams }
706c710fcc5SDan Williams
hmat_register_target_devices(struct memory_target * target)70759b2c5b6SQian Cai static void hmat_register_target_devices(struct memory_target *target)
708cf8741acSDan Williams {
709cf8741acSDan Williams struct resource *res;
710cf8741acSDan Williams
711cf8741acSDan Williams /*
712cf8741acSDan Williams * Do not bother creating devices if no driver is available to
713cf8741acSDan Williams * consume them.
714cf8741acSDan Williams */
715cf8741acSDan Williams if (!IS_ENABLED(CONFIG_DEV_DAX_HMEM))
716cf8741acSDan Williams return;
717cf8741acSDan Williams
718c01044ccSDan Williams for (res = target->memregions.child; res; res = res->sibling) {
719cf1d2b44SLinus Torvalds int target_nid = pxm_to_node(target->memory_pxm);
720c01044ccSDan Williams
721*7dab174eSDan Williams hmem_register_resource(target_nid, res);
722c01044ccSDan Williams }
723cf8741acSDan Williams }
724cf8741acSDan Williams
hmat_register_target(struct memory_target * target)725c710fcc5SDan Williams static void hmat_register_target(struct memory_target *target)
726c710fcc5SDan Williams {
727c710fcc5SDan Williams int nid = pxm_to_node(target->memory_pxm);
728c710fcc5SDan Williams
729c710fcc5SDan Williams /*
730cf8741acSDan Williams * Devices may belong to either an offline or online
731cf8741acSDan Williams * node, so unconditionally add them.
732cf8741acSDan Williams */
733cf8741acSDan Williams hmat_register_target_devices(target);
734cf8741acSDan Williams
735cf8741acSDan Williams /*
736c710fcc5SDan Williams * Skip offline nodes. This can happen when memory
737c710fcc5SDan Williams * marked EFI_MEMORY_SP, "specific purpose", is applied
738935ab850STom Saeger * to all the memory in a proximity domain leading to
739c710fcc5SDan Williams * the node being marked offline / unplugged, or if
740c710fcc5SDan Williams * memory-only "hotplug" node is offline.
741c710fcc5SDan Williams */
742c710fcc5SDan Williams if (nid == NUMA_NO_NODE || !node_online(nid))
743c710fcc5SDan Williams return;
744c710fcc5SDan Williams
745c710fcc5SDan Williams mutex_lock(&target_lock);
746c710fcc5SDan Williams if (!target->registered) {
747c710fcc5SDan Williams hmat_register_target_initiators(target);
748c710fcc5SDan Williams hmat_register_target_cache(target);
749b9fffe47SJonathan Cameron hmat_register_target_perf(target, 0);
750b9fffe47SJonathan Cameron hmat_register_target_perf(target, 1);
751c710fcc5SDan Williams target->registered = true;
752c710fcc5SDan Williams }
753c710fcc5SDan Williams mutex_unlock(&target_lock);
754c710fcc5SDan Williams }
755c710fcc5SDan Williams
hmat_register_targets(void)756c710fcc5SDan Williams static void hmat_register_targets(void)
757c710fcc5SDan Williams {
758c710fcc5SDan Williams struct memory_target *target;
759c710fcc5SDan Williams
760c710fcc5SDan Williams list_for_each_entry(target, &targets, node)
761c710fcc5SDan Williams hmat_register_target(target);
762c710fcc5SDan Williams }
763c710fcc5SDan Williams
hmat_callback(struct notifier_block * self,unsigned long action,void * arg)764c710fcc5SDan Williams static int hmat_callback(struct notifier_block *self,
765c710fcc5SDan Williams unsigned long action, void *arg)
766c710fcc5SDan Williams {
767c710fcc5SDan Williams struct memory_target *target;
768c710fcc5SDan Williams struct memory_notify *mnb = arg;
769c710fcc5SDan Williams int pxm, nid = mnb->status_change_nid;
770c710fcc5SDan Williams
771c710fcc5SDan Williams if (nid == NUMA_NO_NODE || action != MEM_ONLINE)
772c710fcc5SDan Williams return NOTIFY_OK;
773c710fcc5SDan Williams
774c710fcc5SDan Williams pxm = node_to_pxm(nid);
775c710fcc5SDan Williams target = find_mem_target(pxm);
776c710fcc5SDan Williams if (!target)
777c710fcc5SDan Williams return NOTIFY_OK;
778c710fcc5SDan Williams
779c710fcc5SDan Williams hmat_register_target(target);
780c710fcc5SDan Williams return NOTIFY_OK;
781c710fcc5SDan Williams }
782c710fcc5SDan Williams
hmat_free_structures(void)783c710fcc5SDan Williams static __init void hmat_free_structures(void)
784c710fcc5SDan Williams {
785c710fcc5SDan Williams struct memory_target *target, *tnext;
786c710fcc5SDan Williams struct memory_locality *loc, *lnext;
787c710fcc5SDan Williams struct memory_initiator *initiator, *inext;
788c710fcc5SDan Williams struct target_cache *tcache, *cnext;
789c710fcc5SDan Williams
790c710fcc5SDan Williams list_for_each_entry_safe(target, tnext, &targets, node) {
791cf8741acSDan Williams struct resource *res, *res_next;
792cf8741acSDan Williams
793c710fcc5SDan Williams list_for_each_entry_safe(tcache, cnext, &target->caches, node) {
794c710fcc5SDan Williams list_del(&tcache->node);
795c710fcc5SDan Williams kfree(tcache);
796c710fcc5SDan Williams }
797cf8741acSDan Williams
798c710fcc5SDan Williams list_del(&target->node);
799cf8741acSDan Williams res = target->memregions.child;
800cf8741acSDan Williams while (res) {
801cf8741acSDan Williams res_next = res->sibling;
802cf8741acSDan Williams __release_region(&target->memregions, res->start,
803cf8741acSDan Williams resource_size(res));
804cf8741acSDan Williams res = res_next;
805cf8741acSDan Williams }
806c710fcc5SDan Williams kfree(target);
807c710fcc5SDan Williams }
808c710fcc5SDan Williams
809c710fcc5SDan Williams list_for_each_entry_safe(initiator, inext, &initiators, node) {
810c710fcc5SDan Williams list_del(&initiator->node);
811c710fcc5SDan Williams kfree(initiator);
812c710fcc5SDan Williams }
813c710fcc5SDan Williams
814c710fcc5SDan Williams list_for_each_entry_safe(loc, lnext, &localities, node) {
815c710fcc5SDan Williams list_del(&loc->node);
816c710fcc5SDan Williams kfree(loc);
817c710fcc5SDan Williams }
818c710fcc5SDan Williams }
819c710fcc5SDan Williams
hmat_init(void)820c710fcc5SDan Williams static __init int hmat_init(void)
821c710fcc5SDan Williams {
822c710fcc5SDan Williams struct acpi_table_header *tbl;
823c710fcc5SDan Williams enum acpi_hmat_type i;
824c710fcc5SDan Williams acpi_status status;
825c710fcc5SDan Williams
8263b0d3101SDan Williams if (srat_disabled() || hmat_disable)
827c710fcc5SDan Williams return 0;
828c710fcc5SDan Williams
829c710fcc5SDan Williams status = acpi_get_table(ACPI_SIG_SRAT, 0, &tbl);
830c710fcc5SDan Williams if (ACPI_FAILURE(status))
831c710fcc5SDan Williams return 0;
832c710fcc5SDan Williams
833c710fcc5SDan Williams if (acpi_table_parse_entries(ACPI_SIG_SRAT,
834c710fcc5SDan Williams sizeof(struct acpi_table_srat),
835c710fcc5SDan Williams ACPI_SRAT_TYPE_MEMORY_AFFINITY,
836c710fcc5SDan Williams srat_parse_mem_affinity, 0) < 0)
837c710fcc5SDan Williams goto out_put;
838c710fcc5SDan Williams acpi_put_table(tbl);
839c710fcc5SDan Williams
840c710fcc5SDan Williams status = acpi_get_table(ACPI_SIG_HMAT, 0, &tbl);
841c710fcc5SDan Williams if (ACPI_FAILURE(status))
842c710fcc5SDan Williams goto out_put;
843c710fcc5SDan Williams
844c710fcc5SDan Williams hmat_revision = tbl->revision;
845c710fcc5SDan Williams switch (hmat_revision) {
846c710fcc5SDan Williams case 1:
847c710fcc5SDan Williams case 2:
848c710fcc5SDan Williams break;
849c710fcc5SDan Williams default:
85056216359SLiu Shixin pr_notice("Ignoring: Unknown revision:%d\n", hmat_revision);
851c710fcc5SDan Williams goto out_put;
852c710fcc5SDan Williams }
853c710fcc5SDan Williams
854c710fcc5SDan Williams for (i = ACPI_HMAT_TYPE_PROXIMITY; i < ACPI_HMAT_TYPE_RESERVED; i++) {
855c710fcc5SDan Williams if (acpi_table_parse_entries(ACPI_SIG_HMAT,
856c710fcc5SDan Williams sizeof(struct acpi_table_hmat), i,
857c710fcc5SDan Williams hmat_parse_subtable, 0) < 0) {
85856216359SLiu Shixin pr_notice("Ignoring: Invalid table");
859c710fcc5SDan Williams goto out_put;
860c710fcc5SDan Williams }
861c710fcc5SDan Williams }
862c710fcc5SDan Williams hmat_register_targets();
863c710fcc5SDan Williams
864c710fcc5SDan Williams /* Keep the table and structures if the notifier may use them */
8651eeaa4fdSLiu Shixin if (!hotplug_memory_notifier(hmat_callback, HMAT_CALLBACK_PRI))
866c710fcc5SDan Williams return 0;
867c710fcc5SDan Williams out_put:
868c710fcc5SDan Williams hmat_free_structures();
869c710fcc5SDan Williams acpi_put_table(tbl);
870c710fcc5SDan Williams return 0;
871c710fcc5SDan Williams }
872df2798bcSDan Williams subsys_initcall(hmat_init);
873