xref: /linux/drivers/acpi/numa/hmat.c (revision a1ff5a7d78a036d6c2178ee5acd6ba4946243800)
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>
273718c02dSHuang Ying #include <linux/memory-tiers.h>
28c710fcc5SDan Williams 
29c710fcc5SDan Williams static u8 hmat_revision;
303b0d3101SDan Williams static int hmat_disable __initdata;
313b0d3101SDan Williams 
disable_hmat(void)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 
6169b789b6SDave Jiang enum {
621745a7b3SDave Jiang 	NODE_ACCESS_CLASS_GENPORT_SINK_LOCAL = ACCESS_COORDINATE_MAX,
631745a7b3SDave Jiang 	NODE_ACCESS_CLASS_GENPORT_SINK_CPU,
6469b789b6SDave Jiang 	NODE_ACCESS_CLASS_MAX,
6569b789b6SDave Jiang };
6669b789b6SDave Jiang 
67c710fcc5SDan Williams struct memory_target {
68c710fcc5SDan Williams 	struct list_head node;
69c710fcc5SDan Williams 	unsigned int memory_pxm;
70c710fcc5SDan Williams 	unsigned int processor_pxm;
71cf8741acSDan Williams 	struct resource memregions;
7269b789b6SDave Jiang 	struct access_coordinate coord[NODE_ACCESS_CLASS_MAX];
73c710fcc5SDan Williams 	struct list_head caches;
74c710fcc5SDan Williams 	struct node_cache_attrs cache_attrs;
756373c48bSDave Jiang 	u8 gen_port_device_handle[ACPI_SRAT_DEVICE_HANDLE_SIZE];
76c710fcc5SDan Williams 	bool registered;
77067353a4SDave Jiang 	bool ext_updated;	/* externally updated */
78c710fcc5SDan Williams };
79c710fcc5SDan Williams 
80c710fcc5SDan Williams struct memory_initiator {
81c710fcc5SDan Williams 	struct list_head node;
82c710fcc5SDan Williams 	unsigned int processor_pxm;
83b9fffe47SJonathan Cameron 	bool has_cpu;
84c710fcc5SDan Williams };
85c710fcc5SDan Williams 
86c710fcc5SDan Williams struct memory_locality {
87c710fcc5SDan Williams 	struct list_head node;
88c710fcc5SDan Williams 	struct acpi_hmat_locality *hmat_loc;
89c710fcc5SDan Williams };
90c710fcc5SDan Williams 
find_mem_initiator(unsigned int cpu_pxm)91c710fcc5SDan Williams static struct memory_initiator *find_mem_initiator(unsigned int cpu_pxm)
92c710fcc5SDan Williams {
93c710fcc5SDan Williams 	struct memory_initiator *initiator;
94c710fcc5SDan Williams 
95c710fcc5SDan Williams 	list_for_each_entry(initiator, &initiators, node)
96c710fcc5SDan Williams 		if (initiator->processor_pxm == cpu_pxm)
97c710fcc5SDan Williams 			return initiator;
98c710fcc5SDan Williams 	return NULL;
99c710fcc5SDan Williams }
100c710fcc5SDan Williams 
find_mem_target(unsigned int mem_pxm)101c710fcc5SDan Williams static struct memory_target *find_mem_target(unsigned int mem_pxm)
102c710fcc5SDan Williams {
103c710fcc5SDan Williams 	struct memory_target *target;
104c710fcc5SDan Williams 
105c710fcc5SDan Williams 	list_for_each_entry(target, &targets, node)
106c710fcc5SDan Williams 		if (target->memory_pxm == mem_pxm)
107c710fcc5SDan Williams 			return target;
108c710fcc5SDan Williams 	return NULL;
109c710fcc5SDan Williams }
110c710fcc5SDan Williams 
acpi_find_genport_target(u32 uid)111ca53543dSDave Jiang static struct memory_target *acpi_find_genport_target(u32 uid)
112ca53543dSDave Jiang {
113ca53543dSDave Jiang 	struct memory_target *target;
114ca53543dSDave Jiang 	u32 target_uid;
115ca53543dSDave Jiang 	u8 *uid_ptr;
116ca53543dSDave Jiang 
117ca53543dSDave Jiang 	list_for_each_entry(target, &targets, node) {
118ca53543dSDave Jiang 		uid_ptr = target->gen_port_device_handle + 8;
119ca53543dSDave Jiang 		target_uid = *(u32 *)uid_ptr;
120ca53543dSDave Jiang 		if (uid == target_uid)
121ca53543dSDave Jiang 			return target;
122ca53543dSDave Jiang 	}
123ca53543dSDave Jiang 
124ca53543dSDave Jiang 	return NULL;
125ca53543dSDave Jiang }
126ca53543dSDave Jiang 
127ca53543dSDave Jiang /**
128ca53543dSDave Jiang  * acpi_get_genport_coordinates - Retrieve the access coordinates for a generic port
129ca53543dSDave Jiang  * @uid: ACPI unique id
130bd98cbbbSDave Jiang  * @coord: The access coordinates written back out for the generic port.
131bd98cbbbSDave Jiang  *	   Expect 2 levels array.
132ca53543dSDave Jiang  *
133ca53543dSDave Jiang  * Return: 0 on success. Errno on failure.
134ca53543dSDave Jiang  *
135ca53543dSDave Jiang  * Only supports device handles that are ACPI. Assume ACPI0016 HID for CXL.
136ca53543dSDave Jiang  */
acpi_get_genport_coordinates(u32 uid,struct access_coordinate * coord)137ca53543dSDave Jiang int acpi_get_genport_coordinates(u32 uid,
138ca53543dSDave Jiang 				 struct access_coordinate *coord)
139ca53543dSDave Jiang {
140ca53543dSDave Jiang 	struct memory_target *target;
141ca53543dSDave Jiang 
142ca53543dSDave Jiang 	guard(mutex)(&target_lock);
143ca53543dSDave Jiang 	target = acpi_find_genport_target(uid);
144ca53543dSDave Jiang 	if (!target)
145ca53543dSDave Jiang 		return -ENOENT;
146ca53543dSDave Jiang 
147bd98cbbbSDave Jiang 	coord[ACCESS_COORDINATE_LOCAL] =
148bd98cbbbSDave Jiang 		target->coord[NODE_ACCESS_CLASS_GENPORT_SINK_LOCAL];
149bd98cbbbSDave Jiang 	coord[ACCESS_COORDINATE_CPU] =
150bd98cbbbSDave Jiang 		target->coord[NODE_ACCESS_CLASS_GENPORT_SINK_CPU];
151ca53543dSDave Jiang 
152ca53543dSDave Jiang 	return 0;
153ca53543dSDave Jiang }
154ca53543dSDave Jiang EXPORT_SYMBOL_NS_GPL(acpi_get_genport_coordinates, CXL);
155ca53543dSDave Jiang 
alloc_memory_initiator(unsigned int cpu_pxm)156c710fcc5SDan Williams static __init void alloc_memory_initiator(unsigned int cpu_pxm)
157c710fcc5SDan Williams {
158c710fcc5SDan Williams 	struct memory_initiator *initiator;
159c710fcc5SDan Williams 
160c710fcc5SDan Williams 	if (pxm_to_node(cpu_pxm) == NUMA_NO_NODE)
161c710fcc5SDan Williams 		return;
162c710fcc5SDan Williams 
163c710fcc5SDan Williams 	initiator = find_mem_initiator(cpu_pxm);
164c710fcc5SDan Williams 	if (initiator)
165c710fcc5SDan Williams 		return;
166c710fcc5SDan Williams 
167c710fcc5SDan Williams 	initiator = kzalloc(sizeof(*initiator), GFP_KERNEL);
168c710fcc5SDan Williams 	if (!initiator)
169c710fcc5SDan Williams 		return;
170c710fcc5SDan Williams 
171c710fcc5SDan Williams 	initiator->processor_pxm = cpu_pxm;
172b9fffe47SJonathan Cameron 	initiator->has_cpu = node_state(pxm_to_node(cpu_pxm), N_CPU);
173c710fcc5SDan Williams 	list_add_tail(&initiator->node, &initiators);
174c710fcc5SDan Williams }
175c710fcc5SDan Williams 
alloc_target(unsigned int mem_pxm)1766373c48bSDave Jiang static __init struct memory_target *alloc_target(unsigned int mem_pxm)
177c710fcc5SDan Williams {
178c710fcc5SDan Williams 	struct memory_target *target;
179c710fcc5SDan Williams 
180c710fcc5SDan Williams 	target = find_mem_target(mem_pxm);
181cf8741acSDan Williams 	if (!target) {
182c710fcc5SDan Williams 		target = kzalloc(sizeof(*target), GFP_KERNEL);
183c710fcc5SDan Williams 		if (!target)
1846373c48bSDave Jiang 			return NULL;
185c710fcc5SDan Williams 		target->memory_pxm = mem_pxm;
186c710fcc5SDan Williams 		target->processor_pxm = PXM_INVAL;
187cf8741acSDan Williams 		target->memregions = (struct resource) {
188cf8741acSDan Williams 			.name	= "ACPI mem",
189cf8741acSDan Williams 			.start	= 0,
190cf8741acSDan Williams 			.end	= -1,
191cf8741acSDan Williams 			.flags	= IORESOURCE_MEM,
192cf8741acSDan Williams 		};
193c710fcc5SDan Williams 		list_add_tail(&target->node, &targets);
194c710fcc5SDan Williams 		INIT_LIST_HEAD(&target->caches);
195c710fcc5SDan Williams 	}
196c710fcc5SDan Williams 
1976373c48bSDave Jiang 	return target;
1986373c48bSDave Jiang }
1996373c48bSDave Jiang 
alloc_memory_target(unsigned int mem_pxm,resource_size_t start,resource_size_t len)2006373c48bSDave Jiang static __init void alloc_memory_target(unsigned int mem_pxm,
2016373c48bSDave Jiang 				       resource_size_t start,
2026373c48bSDave Jiang 				       resource_size_t len)
2036373c48bSDave Jiang {
2046373c48bSDave Jiang 	struct memory_target *target;
2056373c48bSDave Jiang 
2066373c48bSDave Jiang 	target = alloc_target(mem_pxm);
2076373c48bSDave Jiang 	if (!target)
2086373c48bSDave Jiang 		return;
2096373c48bSDave Jiang 
210cf8741acSDan Williams 	/*
211cf8741acSDan Williams 	 * There are potentially multiple ranges per PXM, so record each
212cf8741acSDan Williams 	 * in the per-target memregions resource tree.
213cf8741acSDan Williams 	 */
214cf8741acSDan Williams 	if (!__request_region(&target->memregions, start, len, "memory target",
215cf8741acSDan Williams 				IORESOURCE_MEM))
216cf8741acSDan Williams 		pr_warn("failed to reserve %#llx - %#llx in pxm: %d\n",
217cf8741acSDan Williams 				start, start + len, mem_pxm);
218cf8741acSDan Williams }
219cf8741acSDan Williams 
alloc_genport_target(unsigned int mem_pxm,u8 * handle)2206373c48bSDave Jiang static __init void alloc_genport_target(unsigned int mem_pxm, u8 *handle)
2216373c48bSDave Jiang {
2226373c48bSDave Jiang 	struct memory_target *target;
2236373c48bSDave Jiang 
2246373c48bSDave Jiang 	target = alloc_target(mem_pxm);
2256373c48bSDave Jiang 	if (!target)
2266373c48bSDave Jiang 		return;
2276373c48bSDave Jiang 
2286373c48bSDave Jiang 	memcpy(target->gen_port_device_handle, handle,
2296373c48bSDave Jiang 	       ACPI_SRAT_DEVICE_HANDLE_SIZE);
2306373c48bSDave Jiang }
2316373c48bSDave Jiang 
hmat_data_type(u8 type)232c710fcc5SDan Williams static __init const char *hmat_data_type(u8 type)
233c710fcc5SDan Williams {
234c710fcc5SDan Williams 	switch (type) {
235c710fcc5SDan Williams 	case ACPI_HMAT_ACCESS_LATENCY:
236c710fcc5SDan Williams 		return "Access Latency";
237c710fcc5SDan Williams 	case ACPI_HMAT_READ_LATENCY:
238c710fcc5SDan Williams 		return "Read Latency";
239c710fcc5SDan Williams 	case ACPI_HMAT_WRITE_LATENCY:
240c710fcc5SDan Williams 		return "Write Latency";
241c710fcc5SDan Williams 	case ACPI_HMAT_ACCESS_BANDWIDTH:
242c710fcc5SDan Williams 		return "Access Bandwidth";
243c710fcc5SDan Williams 	case ACPI_HMAT_READ_BANDWIDTH:
244c710fcc5SDan Williams 		return "Read Bandwidth";
245c710fcc5SDan Williams 	case ACPI_HMAT_WRITE_BANDWIDTH:
246c710fcc5SDan Williams 		return "Write Bandwidth";
247c710fcc5SDan Williams 	default:
248c710fcc5SDan Williams 		return "Reserved";
249c710fcc5SDan Williams 	}
250c710fcc5SDan Williams }
251c710fcc5SDan Williams 
hmat_data_type_suffix(u8 type)252c710fcc5SDan Williams static __init const char *hmat_data_type_suffix(u8 type)
253c710fcc5SDan Williams {
254c710fcc5SDan Williams 	switch (type) {
255c710fcc5SDan Williams 	case ACPI_HMAT_ACCESS_LATENCY:
256c710fcc5SDan Williams 	case ACPI_HMAT_READ_LATENCY:
257c710fcc5SDan Williams 	case ACPI_HMAT_WRITE_LATENCY:
258c710fcc5SDan Williams 		return " nsec";
259c710fcc5SDan Williams 	case ACPI_HMAT_ACCESS_BANDWIDTH:
260c710fcc5SDan Williams 	case ACPI_HMAT_READ_BANDWIDTH:
261c710fcc5SDan Williams 	case ACPI_HMAT_WRITE_BANDWIDTH:
262c710fcc5SDan Williams 		return " MB/s";
263c710fcc5SDan Williams 	default:
264c710fcc5SDan Williams 		return "";
265c710fcc5SDan Williams 	}
266c710fcc5SDan Williams }
267c710fcc5SDan Williams 
hmat_normalize(u16 entry,u64 base,u8 type)268c710fcc5SDan Williams static u32 hmat_normalize(u16 entry, u64 base, u8 type)
269c710fcc5SDan Williams {
270c710fcc5SDan Williams 	u32 value;
271c710fcc5SDan Williams 
272c710fcc5SDan Williams 	/*
273c710fcc5SDan Williams 	 * Check for invalid and overflow values
274c710fcc5SDan Williams 	 */
275c710fcc5SDan Williams 	if (entry == 0xffff || !entry)
276c710fcc5SDan Williams 		return 0;
277c710fcc5SDan Williams 	else if (base > (UINT_MAX / (entry)))
278c710fcc5SDan Williams 		return 0;
279c710fcc5SDan Williams 
280c710fcc5SDan Williams 	/*
281c710fcc5SDan Williams 	 * Divide by the base unit for version 1, convert latency from
282c710fcc5SDan Williams 	 * picosenonds to nanoseconds if revision 2.
283c710fcc5SDan Williams 	 */
284c710fcc5SDan Williams 	value = entry * base;
285c710fcc5SDan Williams 	if (hmat_revision == 1) {
286c710fcc5SDan Williams 		if (value < 10)
287c710fcc5SDan Williams 			return 0;
288c710fcc5SDan Williams 		value = DIV_ROUND_UP(value, 10);
289c710fcc5SDan Williams 	} else if (hmat_revision == 2) {
290c710fcc5SDan Williams 		switch (type) {
291c710fcc5SDan Williams 		case ACPI_HMAT_ACCESS_LATENCY:
292c710fcc5SDan Williams 		case ACPI_HMAT_READ_LATENCY:
293c710fcc5SDan Williams 		case ACPI_HMAT_WRITE_LATENCY:
294c710fcc5SDan Williams 			value = DIV_ROUND_UP(value, 1000);
295c710fcc5SDan Williams 			break;
296c710fcc5SDan Williams 		default:
297c710fcc5SDan Williams 			break;
298c710fcc5SDan Williams 		}
299c710fcc5SDan Williams 	}
300c710fcc5SDan Williams 	return value;
301c710fcc5SDan Williams }
302c710fcc5SDan Williams 
hmat_update_target_access(struct memory_target * target,u8 type,u32 value,int access)303c710fcc5SDan Williams static void hmat_update_target_access(struct memory_target *target,
304b9fffe47SJonathan Cameron 				      u8 type, u32 value, int access)
305c710fcc5SDan Williams {
306c710fcc5SDan Williams 	switch (type) {
307c710fcc5SDan Williams 	case ACPI_HMAT_ACCESS_LATENCY:
3086a954e94SDave Jiang 		target->coord[access].read_latency = value;
3096a954e94SDave Jiang 		target->coord[access].write_latency = value;
310c710fcc5SDan Williams 		break;
311c710fcc5SDan Williams 	case ACPI_HMAT_READ_LATENCY:
3126a954e94SDave Jiang 		target->coord[access].read_latency = value;
313c710fcc5SDan Williams 		break;
314c710fcc5SDan Williams 	case ACPI_HMAT_WRITE_LATENCY:
3156a954e94SDave Jiang 		target->coord[access].write_latency = value;
316c710fcc5SDan Williams 		break;
317c710fcc5SDan Williams 	case ACPI_HMAT_ACCESS_BANDWIDTH:
3186a954e94SDave Jiang 		target->coord[access].read_bandwidth = value;
3196a954e94SDave Jiang 		target->coord[access].write_bandwidth = value;
320c710fcc5SDan Williams 		break;
321c710fcc5SDan Williams 	case ACPI_HMAT_READ_BANDWIDTH:
3226a954e94SDave Jiang 		target->coord[access].read_bandwidth = value;
323c710fcc5SDan Williams 		break;
324c710fcc5SDan Williams 	case ACPI_HMAT_WRITE_BANDWIDTH:
3256a954e94SDave Jiang 		target->coord[access].write_bandwidth = value;
326c710fcc5SDan Williams 		break;
327c710fcc5SDan Williams 	default:
328c710fcc5SDan Williams 		break;
329c710fcc5SDan Williams 	}
330c710fcc5SDan Williams }
331c710fcc5SDan Williams 
hmat_update_target_coordinates(int nid,struct access_coordinate * coord,enum access_coordinate_class access)332067353a4SDave Jiang int hmat_update_target_coordinates(int nid, struct access_coordinate *coord,
333067353a4SDave Jiang 				   enum access_coordinate_class access)
334067353a4SDave Jiang {
335067353a4SDave Jiang 	struct memory_target *target;
336067353a4SDave Jiang 	int pxm;
337067353a4SDave Jiang 
338067353a4SDave Jiang 	if (nid == NUMA_NO_NODE)
339067353a4SDave Jiang 		return -EINVAL;
340067353a4SDave Jiang 
341067353a4SDave Jiang 	pxm = node_to_pxm(nid);
342067353a4SDave Jiang 	guard(mutex)(&target_lock);
343067353a4SDave Jiang 	target = find_mem_target(pxm);
344067353a4SDave Jiang 	if (!target)
345067353a4SDave Jiang 		return -ENODEV;
346067353a4SDave Jiang 
347067353a4SDave Jiang 	hmat_update_target_access(target, ACPI_HMAT_READ_LATENCY,
348067353a4SDave Jiang 				  coord->read_latency, access);
349067353a4SDave Jiang 	hmat_update_target_access(target, ACPI_HMAT_WRITE_LATENCY,
350067353a4SDave Jiang 				  coord->write_latency, access);
351067353a4SDave Jiang 	hmat_update_target_access(target, ACPI_HMAT_READ_BANDWIDTH,
352067353a4SDave Jiang 				  coord->read_bandwidth, access);
353067353a4SDave Jiang 	hmat_update_target_access(target, ACPI_HMAT_WRITE_BANDWIDTH,
354067353a4SDave Jiang 				  coord->write_bandwidth, access);
355067353a4SDave Jiang 	target->ext_updated = true;
356067353a4SDave Jiang 
357067353a4SDave Jiang 	return 0;
358067353a4SDave Jiang }
359067353a4SDave Jiang EXPORT_SYMBOL_GPL(hmat_update_target_coordinates);
360067353a4SDave Jiang 
hmat_add_locality(struct acpi_hmat_locality * hmat_loc)361c710fcc5SDan Williams static __init void hmat_add_locality(struct acpi_hmat_locality *hmat_loc)
362c710fcc5SDan Williams {
363c710fcc5SDan Williams 	struct memory_locality *loc;
364c710fcc5SDan Williams 
365c710fcc5SDan Williams 	loc = kzalloc(sizeof(*loc), GFP_KERNEL);
366c710fcc5SDan Williams 	if (!loc) {
367c710fcc5SDan Williams 		pr_notice_once("Failed to allocate HMAT locality\n");
368c710fcc5SDan Williams 		return;
369c710fcc5SDan Williams 	}
370c710fcc5SDan Williams 
371c710fcc5SDan Williams 	loc->hmat_loc = hmat_loc;
372c710fcc5SDan Williams 	list_add_tail(&loc->node, &localities);
373c710fcc5SDan Williams 
374c710fcc5SDan Williams 	switch (hmat_loc->data_type) {
375c710fcc5SDan Williams 	case ACPI_HMAT_ACCESS_LATENCY:
376c710fcc5SDan Williams 		localities_types[READ_LATENCY] = loc;
377c710fcc5SDan Williams 		localities_types[WRITE_LATENCY] = loc;
378c710fcc5SDan Williams 		break;
379c710fcc5SDan Williams 	case ACPI_HMAT_READ_LATENCY:
380c710fcc5SDan Williams 		localities_types[READ_LATENCY] = loc;
381c710fcc5SDan Williams 		break;
382c710fcc5SDan Williams 	case ACPI_HMAT_WRITE_LATENCY:
383c710fcc5SDan Williams 		localities_types[WRITE_LATENCY] = loc;
384c710fcc5SDan Williams 		break;
385c710fcc5SDan Williams 	case ACPI_HMAT_ACCESS_BANDWIDTH:
386c710fcc5SDan Williams 		localities_types[READ_BANDWIDTH] = loc;
387c710fcc5SDan Williams 		localities_types[WRITE_BANDWIDTH] = loc;
388c710fcc5SDan Williams 		break;
389c710fcc5SDan Williams 	case ACPI_HMAT_READ_BANDWIDTH:
390c710fcc5SDan Williams 		localities_types[READ_BANDWIDTH] = loc;
391c710fcc5SDan Williams 		break;
392c710fcc5SDan Williams 	case ACPI_HMAT_WRITE_BANDWIDTH:
393c710fcc5SDan Williams 		localities_types[WRITE_BANDWIDTH] = loc;
394c710fcc5SDan Williams 		break;
395c710fcc5SDan Williams 	default:
396c710fcc5SDan Williams 		break;
397c710fcc5SDan Williams 	}
398c710fcc5SDan Williams }
399c710fcc5SDan Williams 
hmat_update_target(unsigned int tgt_pxm,unsigned int init_pxm,u8 mem_hier,u8 type,u32 value)40079205651SDave Jiang static __init void hmat_update_target(unsigned int tgt_pxm, unsigned int init_pxm,
40179205651SDave Jiang 				      u8 mem_hier, u8 type, u32 value)
40279205651SDave Jiang {
40379205651SDave Jiang 	struct memory_target *target = find_mem_target(tgt_pxm);
40479205651SDave Jiang 
40579205651SDave Jiang 	if (mem_hier != ACPI_HMAT_MEMORY)
40679205651SDave Jiang 		return;
40779205651SDave Jiang 
40879205651SDave Jiang 	if (target && target->processor_pxm == init_pxm) {
40979205651SDave Jiang 		hmat_update_target_access(target, type, value,
41011270e52SDave Jiang 					  ACCESS_COORDINATE_LOCAL);
41174c2a2aeSHuang Ying 		/* If the node has a CPU, update access ACCESS_COORDINATE_CPU */
41279205651SDave Jiang 		if (node_state(pxm_to_node(init_pxm), N_CPU))
41379205651SDave Jiang 			hmat_update_target_access(target, type, value,
41411270e52SDave Jiang 						  ACCESS_COORDINATE_CPU);
41579205651SDave Jiang 	}
41679205651SDave Jiang }
41779205651SDave Jiang 
hmat_parse_locality(union acpi_subtable_headers * header,const unsigned long end)418c710fcc5SDan Williams static __init int hmat_parse_locality(union acpi_subtable_headers *header,
419c710fcc5SDan Williams 				      const unsigned long end)
420c710fcc5SDan Williams {
421c710fcc5SDan Williams 	struct acpi_hmat_locality *hmat_loc = (void *)header;
422c710fcc5SDan Williams 	unsigned int init, targ, total_size, ipds, tpds;
423c710fcc5SDan Williams 	u32 *inits, *targs, value;
424c710fcc5SDan Williams 	u16 *entries;
425c710fcc5SDan Williams 	u8 type, mem_hier;
426c710fcc5SDan Williams 
427c710fcc5SDan Williams 	if (hmat_loc->header.length < sizeof(*hmat_loc)) {
42856216359SLiu Shixin 		pr_notice("Unexpected locality header length: %u\n",
429c710fcc5SDan Williams 			 hmat_loc->header.length);
430c710fcc5SDan Williams 		return -EINVAL;
431c710fcc5SDan Williams 	}
432c710fcc5SDan Williams 
433c710fcc5SDan Williams 	type = hmat_loc->data_type;
434c710fcc5SDan Williams 	mem_hier = hmat_loc->flags & ACPI_HMAT_MEMORY_HIERARCHY;
435c710fcc5SDan Williams 	ipds = hmat_loc->number_of_initiator_Pds;
436c710fcc5SDan Williams 	tpds = hmat_loc->number_of_target_Pds;
437c710fcc5SDan Williams 	total_size = sizeof(*hmat_loc) + sizeof(*entries) * ipds * tpds +
438c710fcc5SDan Williams 		     sizeof(*inits) * ipds + sizeof(*targs) * tpds;
439c710fcc5SDan Williams 	if (hmat_loc->header.length < total_size) {
44056216359SLiu Shixin 		pr_notice("Unexpected locality header length:%u, minimum required:%u\n",
441c710fcc5SDan Williams 			 hmat_loc->header.length, total_size);
442c710fcc5SDan Williams 		return -EINVAL;
443c710fcc5SDan Williams 	}
444c710fcc5SDan Williams 
44556216359SLiu Shixin 	pr_info("Locality: Flags:%02x Type:%s Initiator Domains:%u Target Domains:%u Base:%lld\n",
446c710fcc5SDan Williams 		hmat_loc->flags, hmat_data_type(type), ipds, tpds,
447c710fcc5SDan Williams 		hmat_loc->entry_base_unit);
448c710fcc5SDan Williams 
449c710fcc5SDan Williams 	inits = (u32 *)(hmat_loc + 1);
450c710fcc5SDan Williams 	targs = inits + ipds;
451c710fcc5SDan Williams 	entries = (u16 *)(targs + tpds);
452c710fcc5SDan Williams 	for (init = 0; init < ipds; init++) {
453c710fcc5SDan Williams 		alloc_memory_initiator(inits[init]);
454c710fcc5SDan Williams 		for (targ = 0; targ < tpds; targ++) {
455c710fcc5SDan Williams 			value = hmat_normalize(entries[init * tpds + targ],
456c710fcc5SDan Williams 					       hmat_loc->entry_base_unit,
457c710fcc5SDan Williams 					       type);
4580f1839d0STao Xu 			pr_info("  Initiator-Target[%u-%u]:%u%s\n",
459c710fcc5SDan Williams 				inits[init], targs[targ], value,
460c710fcc5SDan Williams 				hmat_data_type_suffix(type));
461c710fcc5SDan Williams 
46279205651SDave Jiang 			hmat_update_target(targs[targ], inits[init],
46379205651SDave Jiang 					   mem_hier, type, value);
464c710fcc5SDan Williams 		}
465c710fcc5SDan Williams 	}
466c710fcc5SDan Williams 
467c710fcc5SDan Williams 	if (mem_hier == ACPI_HMAT_MEMORY)
468c710fcc5SDan Williams 		hmat_add_locality(hmat_loc);
469c710fcc5SDan Williams 
470c710fcc5SDan Williams 	return 0;
471c710fcc5SDan Williams }
472c710fcc5SDan Williams 
hmat_parse_cache(union acpi_subtable_headers * header,const unsigned long end)473c710fcc5SDan Williams static __init int hmat_parse_cache(union acpi_subtable_headers *header,
474c710fcc5SDan Williams 				   const unsigned long end)
475c710fcc5SDan Williams {
476c710fcc5SDan Williams 	struct acpi_hmat_cache *cache = (void *)header;
477c710fcc5SDan Williams 	struct memory_target *target;
478c710fcc5SDan Williams 	struct target_cache *tcache;
479c710fcc5SDan Williams 	u32 attrs;
480c710fcc5SDan Williams 
481c710fcc5SDan Williams 	if (cache->header.length < sizeof(*cache)) {
48256216359SLiu Shixin 		pr_notice("Unexpected cache header length: %u\n",
483c710fcc5SDan Williams 			 cache->header.length);
484c710fcc5SDan Williams 		return -EINVAL;
485c710fcc5SDan Williams 	}
486c710fcc5SDan Williams 
487c710fcc5SDan Williams 	attrs = cache->cache_attributes;
48856216359SLiu Shixin 	pr_info("Cache: Domain:%u Size:%llu Attrs:%08x SMBIOS Handles:%d\n",
489c710fcc5SDan Williams 		cache->memory_PD, cache->cache_size, attrs,
490c710fcc5SDan Williams 		cache->number_of_SMBIOShandles);
491c710fcc5SDan Williams 
492c710fcc5SDan Williams 	target = find_mem_target(cache->memory_PD);
493c710fcc5SDan Williams 	if (!target)
494c710fcc5SDan Williams 		return 0;
495c710fcc5SDan Williams 
496c710fcc5SDan Williams 	tcache = kzalloc(sizeof(*tcache), GFP_KERNEL);
497c710fcc5SDan Williams 	if (!tcache) {
498c710fcc5SDan Williams 		pr_notice_once("Failed to allocate HMAT cache info\n");
499c710fcc5SDan Williams 		return 0;
500c710fcc5SDan Williams 	}
501c710fcc5SDan Williams 
502c710fcc5SDan Williams 	tcache->cache_attrs.size = cache->cache_size;
503c710fcc5SDan Williams 	tcache->cache_attrs.level = (attrs & ACPI_HMAT_CACHE_LEVEL) >> 4;
504c710fcc5SDan Williams 	tcache->cache_attrs.line_size = (attrs & ACPI_HMAT_CACHE_LINE_SIZE) >> 16;
505c710fcc5SDan Williams 
506c710fcc5SDan Williams 	switch ((attrs & ACPI_HMAT_CACHE_ASSOCIATIVITY) >> 8) {
507c710fcc5SDan Williams 	case ACPI_HMAT_CA_DIRECT_MAPPED:
508c710fcc5SDan Williams 		tcache->cache_attrs.indexing = NODE_CACHE_DIRECT_MAP;
509c710fcc5SDan Williams 		break;
510c710fcc5SDan Williams 	case ACPI_HMAT_CA_COMPLEX_CACHE_INDEXING:
511c710fcc5SDan Williams 		tcache->cache_attrs.indexing = NODE_CACHE_INDEXED;
512c710fcc5SDan Williams 		break;
513c710fcc5SDan Williams 	case ACPI_HMAT_CA_NONE:
514c710fcc5SDan Williams 	default:
515c710fcc5SDan Williams 		tcache->cache_attrs.indexing = NODE_CACHE_OTHER;
516c710fcc5SDan Williams 		break;
517c710fcc5SDan Williams 	}
518c710fcc5SDan Williams 
519c710fcc5SDan Williams 	switch ((attrs & ACPI_HMAT_WRITE_POLICY) >> 12) {
520c710fcc5SDan Williams 	case ACPI_HMAT_CP_WB:
521c710fcc5SDan Williams 		tcache->cache_attrs.write_policy = NODE_CACHE_WRITE_BACK;
522c710fcc5SDan Williams 		break;
523c710fcc5SDan Williams 	case ACPI_HMAT_CP_WT:
524c710fcc5SDan Williams 		tcache->cache_attrs.write_policy = NODE_CACHE_WRITE_THROUGH;
525c710fcc5SDan Williams 		break;
526c710fcc5SDan Williams 	case ACPI_HMAT_CP_NONE:
527c710fcc5SDan Williams 	default:
528c710fcc5SDan Williams 		tcache->cache_attrs.write_policy = NODE_CACHE_WRITE_OTHER;
529c710fcc5SDan Williams 		break;
530c710fcc5SDan Williams 	}
531c710fcc5SDan Williams 	list_add_tail(&tcache->node, &target->caches);
532c710fcc5SDan Williams 
533c710fcc5SDan Williams 	return 0;
534c710fcc5SDan Williams }
535c710fcc5SDan Williams 
hmat_parse_proximity_domain(union acpi_subtable_headers * header,const unsigned long end)536c710fcc5SDan Williams static int __init hmat_parse_proximity_domain(union acpi_subtable_headers *header,
537c710fcc5SDan Williams 					      const unsigned long end)
538c710fcc5SDan Williams {
539c710fcc5SDan Williams 	struct acpi_hmat_proximity_domain *p = (void *)header;
540c710fcc5SDan Williams 	struct memory_target *target = NULL;
541c710fcc5SDan Williams 
542c710fcc5SDan Williams 	if (p->header.length != sizeof(*p)) {
54356216359SLiu Shixin 		pr_notice("Unexpected address range header length: %u\n",
544c710fcc5SDan Williams 			 p->header.length);
545c710fcc5SDan Williams 		return -EINVAL;
546c710fcc5SDan Williams 	}
547c710fcc5SDan Williams 
548c710fcc5SDan Williams 	if (hmat_revision == 1)
54956216359SLiu Shixin 		pr_info("Memory (%#llx length %#llx) Flags:%04x Processor Domain:%u Memory Domain:%u\n",
550c710fcc5SDan Williams 			p->reserved3, p->reserved4, p->flags, p->processor_PD,
551c710fcc5SDan Williams 			p->memory_PD);
552c710fcc5SDan Williams 	else
55356216359SLiu Shixin 		pr_info("Memory Flags:%04x Processor Domain:%u Memory Domain:%u\n",
554c710fcc5SDan Williams 			p->flags, p->processor_PD, p->memory_PD);
555c710fcc5SDan Williams 
5562c5b9bdeSJonathan Cameron 	if ((hmat_revision == 1 && p->flags & ACPI_HMAT_MEMORY_PD_VALID) ||
5572c5b9bdeSJonathan Cameron 	    hmat_revision > 1) {
558c710fcc5SDan Williams 		target = find_mem_target(p->memory_PD);
559c710fcc5SDan Williams 		if (!target) {
56056216359SLiu Shixin 			pr_debug("Memory Domain missing from SRAT\n");
561c710fcc5SDan Williams 			return -EINVAL;
562c710fcc5SDan Williams 		}
563c710fcc5SDan Williams 	}
564c710fcc5SDan Williams 	if (target && p->flags & ACPI_HMAT_PROCESSOR_PD_VALID) {
565c710fcc5SDan Williams 		int p_node = pxm_to_node(p->processor_PD);
566c710fcc5SDan Williams 
567c710fcc5SDan Williams 		if (p_node == NUMA_NO_NODE) {
56856216359SLiu Shixin 			pr_debug("Invalid Processor Domain\n");
569c710fcc5SDan Williams 			return -EINVAL;
570c710fcc5SDan Williams 		}
5714caa525bSBrice Goglin 		target->processor_pxm = p->processor_PD;
572c710fcc5SDan Williams 	}
573c710fcc5SDan Williams 
574c710fcc5SDan Williams 	return 0;
575c710fcc5SDan Williams }
576c710fcc5SDan Williams 
hmat_parse_subtable(union acpi_subtable_headers * header,const unsigned long end)577c710fcc5SDan Williams static int __init hmat_parse_subtable(union acpi_subtable_headers *header,
578c710fcc5SDan Williams 				      const unsigned long end)
579c710fcc5SDan Williams {
580c710fcc5SDan Williams 	struct acpi_hmat_structure *hdr = (void *)header;
581c710fcc5SDan Williams 
582c710fcc5SDan Williams 	if (!hdr)
583c710fcc5SDan Williams 		return -EINVAL;
584c710fcc5SDan Williams 
585c710fcc5SDan Williams 	switch (hdr->type) {
586c710fcc5SDan Williams 	case ACPI_HMAT_TYPE_PROXIMITY:
587c710fcc5SDan Williams 		return hmat_parse_proximity_domain(header, end);
588c710fcc5SDan Williams 	case ACPI_HMAT_TYPE_LOCALITY:
589c710fcc5SDan Williams 		return hmat_parse_locality(header, end);
590c710fcc5SDan Williams 	case ACPI_HMAT_TYPE_CACHE:
591c710fcc5SDan Williams 		return hmat_parse_cache(header, end);
592c710fcc5SDan Williams 	default:
593c710fcc5SDan Williams 		return -EINVAL;
594c710fcc5SDan Williams 	}
595c710fcc5SDan Williams }
596c710fcc5SDan Williams 
srat_parse_mem_affinity(union acpi_subtable_headers * header,const unsigned long end)597c710fcc5SDan Williams static __init int srat_parse_mem_affinity(union acpi_subtable_headers *header,
598c710fcc5SDan Williams 					  const unsigned long end)
599c710fcc5SDan Williams {
600c710fcc5SDan Williams 	struct acpi_srat_mem_affinity *ma = (void *)header;
601c710fcc5SDan Williams 
602c710fcc5SDan Williams 	if (!ma)
603c710fcc5SDan Williams 		return -EINVAL;
604c710fcc5SDan Williams 	if (!(ma->flags & ACPI_SRAT_MEM_ENABLED))
605c710fcc5SDan Williams 		return 0;
606cf8741acSDan Williams 	alloc_memory_target(ma->proximity_domain, ma->base_address, ma->length);
607c710fcc5SDan Williams 	return 0;
608c710fcc5SDan Williams }
609c710fcc5SDan Williams 
srat_parse_genport_affinity(union acpi_subtable_headers * header,const unsigned long end)6106373c48bSDave Jiang static __init int srat_parse_genport_affinity(union acpi_subtable_headers *header,
6116373c48bSDave Jiang 					      const unsigned long end)
6126373c48bSDave Jiang {
6136373c48bSDave Jiang 	struct acpi_srat_generic_affinity *ga = (void *)header;
6146373c48bSDave Jiang 
6156373c48bSDave Jiang 	if (!ga)
6166373c48bSDave Jiang 		return -EINVAL;
6176373c48bSDave Jiang 
6186373c48bSDave Jiang 	if (!(ga->flags & ACPI_SRAT_GENERIC_AFFINITY_ENABLED))
6196373c48bSDave Jiang 		return 0;
6206373c48bSDave Jiang 
6216373c48bSDave Jiang 	/* Skip PCI device_handle for now */
6226373c48bSDave Jiang 	if (ga->device_handle_type != 0)
6236373c48bSDave Jiang 		return 0;
6246373c48bSDave Jiang 
6256373c48bSDave Jiang 	alloc_genport_target(ga->proximity_domain,
6266373c48bSDave Jiang 			     (u8 *)ga->device_handle);
6276373c48bSDave Jiang 
6286373c48bSDave Jiang 	return 0;
6296373c48bSDave Jiang }
6306373c48bSDave Jiang 
hmat_initiator_perf(struct memory_target * target,struct memory_initiator * initiator,struct acpi_hmat_locality * hmat_loc)631c710fcc5SDan Williams static u32 hmat_initiator_perf(struct memory_target *target,
632c710fcc5SDan Williams 			       struct memory_initiator *initiator,
633c710fcc5SDan Williams 			       struct acpi_hmat_locality *hmat_loc)
634c710fcc5SDan Williams {
635c710fcc5SDan Williams 	unsigned int ipds, tpds, i, idx = 0, tdx = 0;
636c710fcc5SDan Williams 	u32 *inits, *targs;
637c710fcc5SDan Williams 	u16 *entries;
638c710fcc5SDan Williams 
639c710fcc5SDan Williams 	ipds = hmat_loc->number_of_initiator_Pds;
640c710fcc5SDan Williams 	tpds = hmat_loc->number_of_target_Pds;
641c710fcc5SDan Williams 	inits = (u32 *)(hmat_loc + 1);
642c710fcc5SDan Williams 	targs = inits + ipds;
643c710fcc5SDan Williams 	entries = (u16 *)(targs + tpds);
644c710fcc5SDan Williams 
645c710fcc5SDan Williams 	for (i = 0; i < ipds; i++) {
646c710fcc5SDan Williams 		if (inits[i] == initiator->processor_pxm) {
647c710fcc5SDan Williams 			idx = i;
648c710fcc5SDan Williams 			break;
649c710fcc5SDan Williams 		}
650c710fcc5SDan Williams 	}
651c710fcc5SDan Williams 
652c710fcc5SDan Williams 	if (i == ipds)
653c710fcc5SDan Williams 		return 0;
654c710fcc5SDan Williams 
655c710fcc5SDan Williams 	for (i = 0; i < tpds; i++) {
656c710fcc5SDan Williams 		if (targs[i] == target->memory_pxm) {
657c710fcc5SDan Williams 			tdx = i;
658c710fcc5SDan Williams 			break;
659c710fcc5SDan Williams 		}
660c710fcc5SDan Williams 	}
661c710fcc5SDan Williams 	if (i == tpds)
662c710fcc5SDan Williams 		return 0;
663c710fcc5SDan Williams 
664c710fcc5SDan Williams 	return hmat_normalize(entries[idx * tpds + tdx],
665c710fcc5SDan Williams 			      hmat_loc->entry_base_unit,
666c710fcc5SDan Williams 			      hmat_loc->data_type);
667c710fcc5SDan Williams }
668c710fcc5SDan Williams 
hmat_update_best(u8 type,u32 value,u32 * best)669c710fcc5SDan Williams static bool hmat_update_best(u8 type, u32 value, u32 *best)
670c710fcc5SDan Williams {
671c710fcc5SDan Williams 	bool updated = false;
672c710fcc5SDan Williams 
673c710fcc5SDan Williams 	if (!value)
674c710fcc5SDan Williams 		return false;
675c710fcc5SDan Williams 
676c710fcc5SDan Williams 	switch (type) {
677c710fcc5SDan Williams 	case ACPI_HMAT_ACCESS_LATENCY:
678c710fcc5SDan Williams 	case ACPI_HMAT_READ_LATENCY:
679c710fcc5SDan Williams 	case ACPI_HMAT_WRITE_LATENCY:
680c710fcc5SDan Williams 		if (!*best || *best > value) {
681c710fcc5SDan Williams 			*best = value;
682c710fcc5SDan Williams 			updated = true;
683c710fcc5SDan Williams 		}
684c710fcc5SDan Williams 		break;
685c710fcc5SDan Williams 	case ACPI_HMAT_ACCESS_BANDWIDTH:
686c710fcc5SDan Williams 	case ACPI_HMAT_READ_BANDWIDTH:
687c710fcc5SDan Williams 	case ACPI_HMAT_WRITE_BANDWIDTH:
688c710fcc5SDan Williams 		if (!*best || *best < value) {
689c710fcc5SDan Williams 			*best = value;
690c710fcc5SDan Williams 			updated = true;
691c710fcc5SDan Williams 		}
692c710fcc5SDan Williams 		break;
693c710fcc5SDan Williams 	}
694c710fcc5SDan Williams 
695c710fcc5SDan Williams 	return updated;
696c710fcc5SDan Williams }
697c710fcc5SDan Williams 
initiator_cmp(void * priv,const struct list_head * a,const struct list_head * b)6984f0f586bSSami Tolvanen static int initiator_cmp(void *priv, const struct list_head *a,
6994f0f586bSSami Tolvanen 			 const struct list_head *b)
700c710fcc5SDan Williams {
701c710fcc5SDan Williams 	struct memory_initiator *ia;
702c710fcc5SDan Williams 	struct memory_initiator *ib;
703c710fcc5SDan Williams 
704c710fcc5SDan Williams 	ia = list_entry(a, struct memory_initiator, node);
705c710fcc5SDan Williams 	ib = list_entry(b, struct memory_initiator, node);
706c710fcc5SDan Williams 
707c710fcc5SDan Williams 	return ia->processor_pxm - ib->processor_pxm;
708c710fcc5SDan Williams }
709c710fcc5SDan Williams 
initiators_to_nodemask(unsigned long * p_nodes)71048d41809SVishal Verma static int initiators_to_nodemask(unsigned long *p_nodes)
71148d41809SVishal Verma {
71248d41809SVishal Verma 	struct memory_initiator *initiator;
71348d41809SVishal Verma 
71448d41809SVishal Verma 	if (list_empty(&initiators))
71548d41809SVishal Verma 		return -ENXIO;
71648d41809SVishal Verma 
71748d41809SVishal Verma 	list_for_each_entry(initiator, &initiators, node)
71848d41809SVishal Verma 		set_bit(initiator->processor_pxm, p_nodes);
71948d41809SVishal Verma 
72048d41809SVishal Verma 	return 0;
72148d41809SVishal Verma }
72248d41809SVishal Verma 
hmat_update_target_attrs(struct memory_target * target,unsigned long * p_nodes,int access)723d0376aacSHuang Ying static void hmat_update_target_attrs(struct memory_target *target,
724d0376aacSHuang Ying 				     unsigned long *p_nodes, int access)
725c710fcc5SDan Williams {
726c710fcc5SDan Williams 	struct memory_initiator *initiator;
727d0376aacSHuang Ying 	unsigned int cpu_nid;
728c710fcc5SDan Williams 	struct memory_locality *loc = NULL;
729c710fcc5SDan Williams 	u32 best = 0;
730c710fcc5SDan Williams 	int i;
731c710fcc5SDan Williams 
732067353a4SDave Jiang 	/* Don't update if an external agent has changed the data.  */
733067353a4SDave Jiang 	if (target->ext_updated)
734067353a4SDave Jiang 		return;
735067353a4SDave Jiang 
736a3a3e341SDave Jiang 	/* Don't update for generic port if there's no device handle */
7371745a7b3SDave Jiang 	if ((access == NODE_ACCESS_CLASS_GENPORT_SINK_LOCAL ||
7381745a7b3SDave Jiang 	     access == NODE_ACCESS_CLASS_GENPORT_SINK_CPU) &&
739a3a3e341SDave Jiang 	    !(*(u16 *)target->gen_port_device_handle))
740a3a3e341SDave Jiang 		return;
741a3a3e341SDave Jiang 
742d0376aacSHuang Ying 	bitmap_zero(p_nodes, MAX_NUMNODES);
743c710fcc5SDan Williams 	/*
744d0376aacSHuang Ying 	 * If the Address Range Structure provides a local processor pxm, set
745c710fcc5SDan Williams 	 * only that one. Otherwise, find the best performance attributes and
746d0376aacSHuang Ying 	 * collect all initiators that match.
747c710fcc5SDan Williams 	 */
748c710fcc5SDan Williams 	if (target->processor_pxm != PXM_INVAL) {
749c710fcc5SDan Williams 		cpu_nid = pxm_to_node(target->processor_pxm);
75011270e52SDave Jiang 		if (access == ACCESS_COORDINATE_LOCAL ||
75111270e52SDave Jiang 		    node_state(cpu_nid, N_CPU)) {
752d0376aacSHuang Ying 			set_bit(target->processor_pxm, p_nodes);
753c710fcc5SDan Williams 			return;
754c710fcc5SDan Williams 		}
755b9fffe47SJonathan Cameron 	}
756c710fcc5SDan Williams 
757c710fcc5SDan Williams 	if (list_empty(&localities))
758c710fcc5SDan Williams 		return;
759c710fcc5SDan Williams 
760c710fcc5SDan Williams 	/*
761c710fcc5SDan Williams 	 * We need the initiator list sorted so we can use bitmap_clear for
762c710fcc5SDan Williams 	 * previously set initiators when we find a better memory accessor.
763c710fcc5SDan Williams 	 * We'll also use the sorting to prime the candidate nodes with known
764c710fcc5SDan Williams 	 * initiators.
765c710fcc5SDan Williams 	 */
76648d41809SVishal Verma 	list_sort(NULL, &initiators, initiator_cmp);
76748d41809SVishal Verma 	if (initiators_to_nodemask(p_nodes) < 0)
76848d41809SVishal Verma 		return;
76948d41809SVishal Verma 
770c710fcc5SDan Williams 	for (i = WRITE_LATENCY; i <= READ_BANDWIDTH; i++) {
771c710fcc5SDan Williams 		loc = localities_types[i];
772c710fcc5SDan Williams 		if (!loc)
773c710fcc5SDan Williams 			continue;
774c710fcc5SDan Williams 
775c710fcc5SDan Williams 		best = 0;
776c710fcc5SDan Williams 		list_for_each_entry(initiator, &initiators, node) {
777c710fcc5SDan Williams 			u32 value;
778c710fcc5SDan Williams 
7791745a7b3SDave Jiang 			if ((access == ACCESS_COORDINATE_CPU ||
7801745a7b3SDave Jiang 			     access == NODE_ACCESS_CLASS_GENPORT_SINK_CPU) &&
78111270e52SDave Jiang 			    !initiator->has_cpu) {
782b9fffe47SJonathan Cameron 				clear_bit(initiator->processor_pxm, p_nodes);
783b9fffe47SJonathan Cameron 				continue;
784b9fffe47SJonathan Cameron 			}
785b9fffe47SJonathan Cameron 			if (!test_bit(initiator->processor_pxm, p_nodes))
786b9fffe47SJonathan Cameron 				continue;
787b9fffe47SJonathan Cameron 
788c710fcc5SDan Williams 			value = hmat_initiator_perf(target, initiator, loc->hmat_loc);
789c710fcc5SDan Williams 			if (hmat_update_best(loc->hmat_loc->data_type, value, &best))
790c710fcc5SDan Williams 				bitmap_clear(p_nodes, 0, initiator->processor_pxm);
791c710fcc5SDan Williams 			if (value != best)
792c710fcc5SDan Williams 				clear_bit(initiator->processor_pxm, p_nodes);
793c710fcc5SDan Williams 		}
794c710fcc5SDan Williams 		if (best)
795d0376aacSHuang Ying 			hmat_update_target_access(target, loc->hmat_loc->data_type, best, access);
796c710fcc5SDan Williams 	}
797d0376aacSHuang Ying }
798d0376aacSHuang Ying 
__hmat_register_target_initiators(struct memory_target * target,unsigned long * p_nodes,int access)799d0376aacSHuang Ying static void __hmat_register_target_initiators(struct memory_target *target,
800d0376aacSHuang Ying 					      unsigned long *p_nodes,
801d0376aacSHuang Ying 					      int access)
802d0376aacSHuang Ying {
803d0376aacSHuang Ying 	unsigned int mem_nid, cpu_nid;
804d0376aacSHuang Ying 	int i;
805d0376aacSHuang Ying 
806d0376aacSHuang Ying 	mem_nid = pxm_to_node(target->memory_pxm);
807d0376aacSHuang Ying 	hmat_update_target_attrs(target, p_nodes, access);
808c710fcc5SDan Williams 	for_each_set_bit(i, p_nodes, MAX_NUMNODES) {
809c710fcc5SDan Williams 		cpu_nid = pxm_to_node(i);
810d0376aacSHuang Ying 		register_memory_node_under_compute_node(mem_nid, cpu_nid, access);
811c710fcc5SDan Williams 	}
812c710fcc5SDan Williams }
813c710fcc5SDan Williams 
hmat_update_generic_target(struct memory_target * target)81454b9460bSDave Jiang static void hmat_update_generic_target(struct memory_target *target)
815a3a3e341SDave Jiang {
816a3a3e341SDave Jiang 	static DECLARE_BITMAP(p_nodes, MAX_NUMNODES);
817a3a3e341SDave Jiang 
81854b9460bSDave Jiang 	hmat_update_target_attrs(target, p_nodes,
8191745a7b3SDave Jiang 				 NODE_ACCESS_CLASS_GENPORT_SINK_LOCAL);
8201745a7b3SDave Jiang 	hmat_update_target_attrs(target, p_nodes,
8211745a7b3SDave Jiang 				 NODE_ACCESS_CLASS_GENPORT_SINK_CPU);
822a3a3e341SDave Jiang }
823a3a3e341SDave Jiang 
hmat_register_target_initiators(struct memory_target * target)824d0376aacSHuang Ying static void hmat_register_target_initiators(struct memory_target *target)
825d0376aacSHuang Ying {
826d0376aacSHuang Ying 	static DECLARE_BITMAP(p_nodes, MAX_NUMNODES);
827d0376aacSHuang Ying 
82811270e52SDave Jiang 	__hmat_register_target_initiators(target, p_nodes,
82911270e52SDave Jiang 					  ACCESS_COORDINATE_LOCAL);
83011270e52SDave Jiang 	__hmat_register_target_initiators(target, p_nodes,
83111270e52SDave Jiang 					  ACCESS_COORDINATE_CPU);
832d0376aacSHuang Ying }
833d0376aacSHuang Ying 
hmat_register_target_cache(struct memory_target * target)834c710fcc5SDan Williams static void hmat_register_target_cache(struct memory_target *target)
835c710fcc5SDan Williams {
836c710fcc5SDan Williams 	unsigned mem_nid = pxm_to_node(target->memory_pxm);
837c710fcc5SDan Williams 	struct target_cache *tcache;
838c710fcc5SDan Williams 
839c710fcc5SDan Williams 	list_for_each_entry(tcache, &target->caches, node)
840c710fcc5SDan Williams 		node_add_cache(mem_nid, &tcache->cache_attrs);
841c710fcc5SDan Williams }
842c710fcc5SDan Williams 
hmat_register_target_perf(struct memory_target * target,int access)843b9fffe47SJonathan Cameron static void hmat_register_target_perf(struct memory_target *target, int access)
844c710fcc5SDan Williams {
845c710fcc5SDan Williams 	unsigned mem_nid = pxm_to_node(target->memory_pxm);
8466a954e94SDave Jiang 	node_set_perf_attrs(mem_nid, &target->coord[access], access);
847c710fcc5SDan Williams }
848c710fcc5SDan Williams 
hmat_register_target_devices(struct memory_target * target)84959b2c5b6SQian Cai static void hmat_register_target_devices(struct memory_target *target)
850cf8741acSDan Williams {
851cf8741acSDan Williams 	struct resource *res;
852cf8741acSDan Williams 
853cf8741acSDan Williams 	/*
854cf8741acSDan Williams 	 * Do not bother creating devices if no driver is available to
855cf8741acSDan Williams 	 * consume them.
856cf8741acSDan Williams 	 */
857cf8741acSDan Williams 	if (!IS_ENABLED(CONFIG_DEV_DAX_HMEM))
858cf8741acSDan Williams 		return;
859cf8741acSDan Williams 
860c01044ccSDan Williams 	for (res = target->memregions.child; res; res = res->sibling) {
861cf1d2b44SLinus Torvalds 		int target_nid = pxm_to_node(target->memory_pxm);
862c01044ccSDan Williams 
8637dab174eSDan Williams 		hmem_register_resource(target_nid, res);
864c01044ccSDan Williams 	}
865cf8741acSDan Williams }
866cf8741acSDan Williams 
hmat_register_target(struct memory_target * target)867c710fcc5SDan Williams static void hmat_register_target(struct memory_target *target)
868c710fcc5SDan Williams {
869c710fcc5SDan Williams 	int nid = pxm_to_node(target->memory_pxm);
870c710fcc5SDan Williams 
871c710fcc5SDan Williams 	/*
872cf8741acSDan Williams 	 * Devices may belong to either an offline or online
873cf8741acSDan Williams 	 * node, so unconditionally add them.
874cf8741acSDan Williams 	 */
875cf8741acSDan Williams 	hmat_register_target_devices(target);
876cf8741acSDan Williams 
877cf8741acSDan Williams 	/*
878a3a3e341SDave Jiang 	 * Register generic port perf numbers. The nid may not be
879a3a3e341SDave Jiang 	 * initialized and is still NUMA_NO_NODE.
880a3a3e341SDave Jiang 	 */
881a3a3e341SDave Jiang 	mutex_lock(&target_lock);
882a3a3e341SDave Jiang 	if (*(u16 *)target->gen_port_device_handle) {
88354b9460bSDave Jiang 		hmat_update_generic_target(target);
884a3a3e341SDave Jiang 		target->registered = true;
885a3a3e341SDave Jiang 	}
886a3a3e341SDave Jiang 	mutex_unlock(&target_lock);
887a3a3e341SDave Jiang 
888a3a3e341SDave Jiang 	/*
889c710fcc5SDan Williams 	 * Skip offline nodes. This can happen when memory
890c710fcc5SDan Williams 	 * marked EFI_MEMORY_SP, "specific purpose", is applied
891935ab850STom Saeger 	 * to all the memory in a proximity domain leading to
892c710fcc5SDan Williams 	 * the node being marked offline / unplugged, or if
893c710fcc5SDan Williams 	 * memory-only "hotplug" node is offline.
894c710fcc5SDan Williams 	 */
895c710fcc5SDan Williams 	if (nid == NUMA_NO_NODE || !node_online(nid))
896c710fcc5SDan Williams 		return;
897c710fcc5SDan Williams 
898c710fcc5SDan Williams 	mutex_lock(&target_lock);
899c710fcc5SDan Williams 	if (!target->registered) {
900c710fcc5SDan Williams 		hmat_register_target_initiators(target);
901c710fcc5SDan Williams 		hmat_register_target_cache(target);
90211270e52SDave Jiang 		hmat_register_target_perf(target, ACCESS_COORDINATE_LOCAL);
90311270e52SDave Jiang 		hmat_register_target_perf(target, ACCESS_COORDINATE_CPU);
904c710fcc5SDan Williams 		target->registered = true;
905c710fcc5SDan Williams 	}
906c710fcc5SDan Williams 	mutex_unlock(&target_lock);
907c710fcc5SDan Williams }
908c710fcc5SDan Williams 
hmat_register_targets(void)909c710fcc5SDan Williams static void hmat_register_targets(void)
910c710fcc5SDan Williams {
911c710fcc5SDan Williams 	struct memory_target *target;
912c710fcc5SDan Williams 
913c710fcc5SDan Williams 	list_for_each_entry(target, &targets, node)
914c710fcc5SDan Williams 		hmat_register_target(target);
915c710fcc5SDan Williams }
916c710fcc5SDan Williams 
hmat_callback(struct notifier_block * self,unsigned long action,void * arg)917c710fcc5SDan Williams static int hmat_callback(struct notifier_block *self,
918c710fcc5SDan Williams 			 unsigned long action, void *arg)
919c710fcc5SDan Williams {
920c710fcc5SDan Williams 	struct memory_target *target;
921c710fcc5SDan Williams 	struct memory_notify *mnb = arg;
922c710fcc5SDan Williams 	int pxm, nid = mnb->status_change_nid;
923c710fcc5SDan Williams 
924c710fcc5SDan Williams 	if (nid == NUMA_NO_NODE || action != MEM_ONLINE)
925c710fcc5SDan Williams 		return NOTIFY_OK;
926c710fcc5SDan Williams 
927c710fcc5SDan Williams 	pxm = node_to_pxm(nid);
928c710fcc5SDan Williams 	target = find_mem_target(pxm);
929c710fcc5SDan Williams 	if (!target)
930c710fcc5SDan Williams 		return NOTIFY_OK;
931c710fcc5SDan Williams 
932c710fcc5SDan Williams 	hmat_register_target(target);
933c710fcc5SDan Williams 	return NOTIFY_OK;
934c710fcc5SDan Williams }
935c710fcc5SDan Williams 
hmat_set_default_dram_perf(void)936*fbc90c04SLinus Torvalds static int __init hmat_set_default_dram_perf(void)
9373718c02dSHuang Ying {
9383718c02dSHuang Ying 	int rc;
9393718c02dSHuang Ying 	int nid, pxm;
9403718c02dSHuang Ying 	struct memory_target *target;
9416a954e94SDave Jiang 	struct access_coordinate *attrs;
9423718c02dSHuang Ying 
943823430c8SHo-Ren (Jack) Chuang 	for_each_node_mask(nid, default_dram_nodes) {
9443718c02dSHuang Ying 		pxm = node_to_pxm(nid);
9453718c02dSHuang Ying 		target = find_mem_target(pxm);
9463718c02dSHuang Ying 		if (!target)
9473718c02dSHuang Ying 			continue;
94874c2a2aeSHuang Ying 		attrs = &target->coord[ACCESS_COORDINATE_CPU];
9493718c02dSHuang Ying 		rc = mt_set_default_dram_perf(nid, attrs, "ACPI HMAT");
9503718c02dSHuang Ying 		if (rc)
9513718c02dSHuang Ying 			return rc;
9523718c02dSHuang Ying 	}
9533718c02dSHuang Ying 
9543718c02dSHuang Ying 	return 0;
9553718c02dSHuang Ying }
9563718c02dSHuang Ying 
hmat_calculate_adistance(struct notifier_block * self,unsigned long nid,void * data)9573718c02dSHuang Ying static int hmat_calculate_adistance(struct notifier_block *self,
9583718c02dSHuang Ying 				    unsigned long nid, void *data)
9593718c02dSHuang Ying {
9603718c02dSHuang Ying 	static DECLARE_BITMAP(p_nodes, MAX_NUMNODES);
9613718c02dSHuang Ying 	struct memory_target *target;
9626a954e94SDave Jiang 	struct access_coordinate *perf;
9633718c02dSHuang Ying 	int *adist = data;
9643718c02dSHuang Ying 	int pxm;
9653718c02dSHuang Ying 
9663718c02dSHuang Ying 	pxm = node_to_pxm(nid);
9673718c02dSHuang Ying 	target = find_mem_target(pxm);
9683718c02dSHuang Ying 	if (!target)
9693718c02dSHuang Ying 		return NOTIFY_OK;
9703718c02dSHuang Ying 
9713718c02dSHuang Ying 	mutex_lock(&target_lock);
97211270e52SDave Jiang 	hmat_update_target_attrs(target, p_nodes, ACCESS_COORDINATE_CPU);
9733718c02dSHuang Ying 	mutex_unlock(&target_lock);
9743718c02dSHuang Ying 
97574c2a2aeSHuang Ying 	perf = &target->coord[ACCESS_COORDINATE_CPU];
9763718c02dSHuang Ying 
9773718c02dSHuang Ying 	if (mt_perf_to_adistance(perf, adist))
9783718c02dSHuang Ying 		return NOTIFY_OK;
9793718c02dSHuang Ying 
9803718c02dSHuang Ying 	return NOTIFY_STOP;
9813718c02dSHuang Ying }
9823718c02dSHuang Ying 
9833718c02dSHuang Ying static struct notifier_block hmat_adist_nb __meminitdata = {
9843718c02dSHuang Ying 	.notifier_call = hmat_calculate_adistance,
9853718c02dSHuang Ying 	.priority = 100,
9863718c02dSHuang Ying };
9873718c02dSHuang Ying 
hmat_free_structures(void)988c710fcc5SDan Williams static __init void hmat_free_structures(void)
989c710fcc5SDan Williams {
990c710fcc5SDan Williams 	struct memory_target *target, *tnext;
991c710fcc5SDan Williams 	struct memory_locality *loc, *lnext;
992c710fcc5SDan Williams 	struct memory_initiator *initiator, *inext;
993c710fcc5SDan Williams 	struct target_cache *tcache, *cnext;
994c710fcc5SDan Williams 
995c710fcc5SDan Williams 	list_for_each_entry_safe(target, tnext, &targets, node) {
996cf8741acSDan Williams 		struct resource *res, *res_next;
997cf8741acSDan Williams 
998c710fcc5SDan Williams 		list_for_each_entry_safe(tcache, cnext, &target->caches, node) {
999c710fcc5SDan Williams 			list_del(&tcache->node);
1000c710fcc5SDan Williams 			kfree(tcache);
1001c710fcc5SDan Williams 		}
1002cf8741acSDan Williams 
1003c710fcc5SDan Williams 		list_del(&target->node);
1004cf8741acSDan Williams 		res = target->memregions.child;
1005cf8741acSDan Williams 		while (res) {
1006cf8741acSDan Williams 			res_next = res->sibling;
1007cf8741acSDan Williams 			__release_region(&target->memregions, res->start,
1008cf8741acSDan Williams 					resource_size(res));
1009cf8741acSDan Williams 			res = res_next;
1010cf8741acSDan Williams 		}
1011c710fcc5SDan Williams 		kfree(target);
1012c710fcc5SDan Williams 	}
1013c710fcc5SDan Williams 
1014c710fcc5SDan Williams 	list_for_each_entry_safe(initiator, inext, &initiators, node) {
1015c710fcc5SDan Williams 		list_del(&initiator->node);
1016c710fcc5SDan Williams 		kfree(initiator);
1017c710fcc5SDan Williams 	}
1018c710fcc5SDan Williams 
1019c710fcc5SDan Williams 	list_for_each_entry_safe(loc, lnext, &localities, node) {
1020c710fcc5SDan Williams 		list_del(&loc->node);
1021c710fcc5SDan Williams 		kfree(loc);
1022c710fcc5SDan Williams 	}
1023c710fcc5SDan Williams }
1024c710fcc5SDan Williams 
hmat_init(void)1025c710fcc5SDan Williams static __init int hmat_init(void)
1026c710fcc5SDan Williams {
1027c710fcc5SDan Williams 	struct acpi_table_header *tbl;
1028c710fcc5SDan Williams 	enum acpi_hmat_type i;
1029c710fcc5SDan Williams 	acpi_status status;
1030c710fcc5SDan Williams 
10313b0d3101SDan Williams 	if (srat_disabled() || hmat_disable)
1032c710fcc5SDan Williams 		return 0;
1033c710fcc5SDan Williams 
1034c710fcc5SDan Williams 	status = acpi_get_table(ACPI_SIG_SRAT, 0, &tbl);
1035c710fcc5SDan Williams 	if (ACPI_FAILURE(status))
1036c710fcc5SDan Williams 		return 0;
1037c710fcc5SDan Williams 
1038c710fcc5SDan Williams 	if (acpi_table_parse_entries(ACPI_SIG_SRAT,
1039c710fcc5SDan Williams 				sizeof(struct acpi_table_srat),
1040c710fcc5SDan Williams 				ACPI_SRAT_TYPE_MEMORY_AFFINITY,
1041c710fcc5SDan Williams 				srat_parse_mem_affinity, 0) < 0)
1042c710fcc5SDan Williams 		goto out_put;
10436373c48bSDave Jiang 
10446373c48bSDave Jiang 	if (acpi_table_parse_entries(ACPI_SIG_SRAT,
10456373c48bSDave Jiang 				     sizeof(struct acpi_table_srat),
10466373c48bSDave Jiang 				     ACPI_SRAT_TYPE_GENERIC_PORT_AFFINITY,
10476373c48bSDave Jiang 				     srat_parse_genport_affinity, 0) < 0)
10486373c48bSDave Jiang 		goto out_put;
10496373c48bSDave Jiang 
1050c710fcc5SDan Williams 	acpi_put_table(tbl);
1051c710fcc5SDan Williams 
1052c710fcc5SDan Williams 	status = acpi_get_table(ACPI_SIG_HMAT, 0, &tbl);
1053c710fcc5SDan Williams 	if (ACPI_FAILURE(status))
1054c710fcc5SDan Williams 		goto out_put;
1055c710fcc5SDan Williams 
1056c710fcc5SDan Williams 	hmat_revision = tbl->revision;
1057c710fcc5SDan Williams 	switch (hmat_revision) {
1058c710fcc5SDan Williams 	case 1:
1059c710fcc5SDan Williams 	case 2:
1060c710fcc5SDan Williams 		break;
1061c710fcc5SDan Williams 	default:
106256216359SLiu Shixin 		pr_notice("Ignoring: Unknown revision:%d\n", hmat_revision);
1063c710fcc5SDan Williams 		goto out_put;
1064c710fcc5SDan Williams 	}
1065c710fcc5SDan Williams 
1066c710fcc5SDan Williams 	for (i = ACPI_HMAT_TYPE_PROXIMITY; i < ACPI_HMAT_TYPE_RESERVED; i++) {
1067c710fcc5SDan Williams 		if (acpi_table_parse_entries(ACPI_SIG_HMAT,
1068c710fcc5SDan Williams 					     sizeof(struct acpi_table_hmat), i,
1069c710fcc5SDan Williams 					     hmat_parse_subtable, 0) < 0) {
107056216359SLiu Shixin 			pr_notice("Ignoring: Invalid table");
1071c710fcc5SDan Williams 			goto out_put;
1072c710fcc5SDan Williams 		}
1073c710fcc5SDan Williams 	}
1074c710fcc5SDan Williams 	hmat_register_targets();
1075c710fcc5SDan Williams 
1076c710fcc5SDan Williams 	/* Keep the table and structures if the notifier may use them */
10773718c02dSHuang Ying 	if (hotplug_memory_notifier(hmat_callback, HMAT_CALLBACK_PRI))
10783718c02dSHuang Ying 		goto out_put;
10793718c02dSHuang Ying 
10803718c02dSHuang Ying 	if (!hmat_set_default_dram_perf())
10813718c02dSHuang Ying 		register_mt_adistance_algorithm(&hmat_adist_nb);
10823718c02dSHuang Ying 
1083c710fcc5SDan Williams 	return 0;
1084c710fcc5SDan Williams out_put:
1085c710fcc5SDan Williams 	hmat_free_structures();
1086c710fcc5SDan Williams 	acpi_put_table(tbl);
1087c710fcc5SDan Williams 	return 0;
1088c710fcc5SDan Williams }
1089df2798bcSDan Williams subsys_initcall(hmat_init);
1090