xref: /openbmc/linux/drivers/acpi/numa/hmat.c (revision 9a87ffc99ec8eb8d35eed7c4f816d75f5cc9662e)
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