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