xref: /kernel/linux/linux-5.10/drivers/acpi/numa/hmat.c (revision 8c2ecf20)
18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (c) 2019, Intel Corporation.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Heterogeneous Memory Attributes Table (HMAT) representation
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * This program parses and reports the platform's HMAT tables, and registers
88c2ecf20Sopenharmony_ci * the applicable attributes with the node's interfaces.
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "acpi/hmat: " fmt
128c2ecf20Sopenharmony_ci#define dev_fmt(fmt) "acpi/hmat: " fmt
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include <linux/acpi.h>
158c2ecf20Sopenharmony_ci#include <linux/bitops.h>
168c2ecf20Sopenharmony_ci#include <linux/device.h>
178c2ecf20Sopenharmony_ci#include <linux/init.h>
188c2ecf20Sopenharmony_ci#include <linux/list.h>
198c2ecf20Sopenharmony_ci#include <linux/mm.h>
208c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
218c2ecf20Sopenharmony_ci#include <linux/list_sort.h>
228c2ecf20Sopenharmony_ci#include <linux/memregion.h>
238c2ecf20Sopenharmony_ci#include <linux/memory.h>
248c2ecf20Sopenharmony_ci#include <linux/mutex.h>
258c2ecf20Sopenharmony_ci#include <linux/node.h>
268c2ecf20Sopenharmony_ci#include <linux/sysfs.h>
278c2ecf20Sopenharmony_ci#include <linux/dax.h>
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_cistatic u8 hmat_revision;
308c2ecf20Sopenharmony_cistatic int hmat_disable __initdata;
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_civoid __init disable_hmat(void)
338c2ecf20Sopenharmony_ci{
348c2ecf20Sopenharmony_ci	hmat_disable = 1;
358c2ecf20Sopenharmony_ci}
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_cistatic LIST_HEAD(targets);
388c2ecf20Sopenharmony_cistatic LIST_HEAD(initiators);
398c2ecf20Sopenharmony_cistatic LIST_HEAD(localities);
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(target_lock);
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci/*
448c2ecf20Sopenharmony_ci * The defined enum order is used to prioritize attributes to break ties when
458c2ecf20Sopenharmony_ci * selecting the best performing node.
468c2ecf20Sopenharmony_ci */
478c2ecf20Sopenharmony_cienum locality_types {
488c2ecf20Sopenharmony_ci	WRITE_LATENCY,
498c2ecf20Sopenharmony_ci	READ_LATENCY,
508c2ecf20Sopenharmony_ci	WRITE_BANDWIDTH,
518c2ecf20Sopenharmony_ci	READ_BANDWIDTH,
528c2ecf20Sopenharmony_ci};
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_cistatic struct memory_locality *localities_types[4];
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_cistruct target_cache {
578c2ecf20Sopenharmony_ci	struct list_head node;
588c2ecf20Sopenharmony_ci	struct node_cache_attrs cache_attrs;
598c2ecf20Sopenharmony_ci};
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_cistruct memory_target {
628c2ecf20Sopenharmony_ci	struct list_head node;
638c2ecf20Sopenharmony_ci	unsigned int memory_pxm;
648c2ecf20Sopenharmony_ci	unsigned int processor_pxm;
658c2ecf20Sopenharmony_ci	struct resource memregions;
668c2ecf20Sopenharmony_ci	struct node_hmem_attrs hmem_attrs[2];
678c2ecf20Sopenharmony_ci	struct list_head caches;
688c2ecf20Sopenharmony_ci	struct node_cache_attrs cache_attrs;
698c2ecf20Sopenharmony_ci	bool registered;
708c2ecf20Sopenharmony_ci};
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_cistruct memory_initiator {
738c2ecf20Sopenharmony_ci	struct list_head node;
748c2ecf20Sopenharmony_ci	unsigned int processor_pxm;
758c2ecf20Sopenharmony_ci	bool has_cpu;
768c2ecf20Sopenharmony_ci};
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_cistruct memory_locality {
798c2ecf20Sopenharmony_ci	struct list_head node;
808c2ecf20Sopenharmony_ci	struct acpi_hmat_locality *hmat_loc;
818c2ecf20Sopenharmony_ci};
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_cistatic struct memory_initiator *find_mem_initiator(unsigned int cpu_pxm)
848c2ecf20Sopenharmony_ci{
858c2ecf20Sopenharmony_ci	struct memory_initiator *initiator;
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	list_for_each_entry(initiator, &initiators, node)
888c2ecf20Sopenharmony_ci		if (initiator->processor_pxm == cpu_pxm)
898c2ecf20Sopenharmony_ci			return initiator;
908c2ecf20Sopenharmony_ci	return NULL;
918c2ecf20Sopenharmony_ci}
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_cistatic struct memory_target *find_mem_target(unsigned int mem_pxm)
948c2ecf20Sopenharmony_ci{
958c2ecf20Sopenharmony_ci	struct memory_target *target;
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	list_for_each_entry(target, &targets, node)
988c2ecf20Sopenharmony_ci		if (target->memory_pxm == mem_pxm)
998c2ecf20Sopenharmony_ci			return target;
1008c2ecf20Sopenharmony_ci	return NULL;
1018c2ecf20Sopenharmony_ci}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_cistatic __init void alloc_memory_initiator(unsigned int cpu_pxm)
1048c2ecf20Sopenharmony_ci{
1058c2ecf20Sopenharmony_ci	struct memory_initiator *initiator;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	if (pxm_to_node(cpu_pxm) == NUMA_NO_NODE)
1088c2ecf20Sopenharmony_ci		return;
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	initiator = find_mem_initiator(cpu_pxm);
1118c2ecf20Sopenharmony_ci	if (initiator)
1128c2ecf20Sopenharmony_ci		return;
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	initiator = kzalloc(sizeof(*initiator), GFP_KERNEL);
1158c2ecf20Sopenharmony_ci	if (!initiator)
1168c2ecf20Sopenharmony_ci		return;
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	initiator->processor_pxm = cpu_pxm;
1198c2ecf20Sopenharmony_ci	initiator->has_cpu = node_state(pxm_to_node(cpu_pxm), N_CPU);
1208c2ecf20Sopenharmony_ci	list_add_tail(&initiator->node, &initiators);
1218c2ecf20Sopenharmony_ci}
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_cistatic __init void alloc_memory_target(unsigned int mem_pxm,
1248c2ecf20Sopenharmony_ci		resource_size_t start, resource_size_t len)
1258c2ecf20Sopenharmony_ci{
1268c2ecf20Sopenharmony_ci	struct memory_target *target;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	target = find_mem_target(mem_pxm);
1298c2ecf20Sopenharmony_ci	if (!target) {
1308c2ecf20Sopenharmony_ci		target = kzalloc(sizeof(*target), GFP_KERNEL);
1318c2ecf20Sopenharmony_ci		if (!target)
1328c2ecf20Sopenharmony_ci			return;
1338c2ecf20Sopenharmony_ci		target->memory_pxm = mem_pxm;
1348c2ecf20Sopenharmony_ci		target->processor_pxm = PXM_INVAL;
1358c2ecf20Sopenharmony_ci		target->memregions = (struct resource) {
1368c2ecf20Sopenharmony_ci			.name	= "ACPI mem",
1378c2ecf20Sopenharmony_ci			.start	= 0,
1388c2ecf20Sopenharmony_ci			.end	= -1,
1398c2ecf20Sopenharmony_ci			.flags	= IORESOURCE_MEM,
1408c2ecf20Sopenharmony_ci		};
1418c2ecf20Sopenharmony_ci		list_add_tail(&target->node, &targets);
1428c2ecf20Sopenharmony_ci		INIT_LIST_HEAD(&target->caches);
1438c2ecf20Sopenharmony_ci	}
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	/*
1468c2ecf20Sopenharmony_ci	 * There are potentially multiple ranges per PXM, so record each
1478c2ecf20Sopenharmony_ci	 * in the per-target memregions resource tree.
1488c2ecf20Sopenharmony_ci	 */
1498c2ecf20Sopenharmony_ci	if (!__request_region(&target->memregions, start, len, "memory target",
1508c2ecf20Sopenharmony_ci				IORESOURCE_MEM))
1518c2ecf20Sopenharmony_ci		pr_warn("failed to reserve %#llx - %#llx in pxm: %d\n",
1528c2ecf20Sopenharmony_ci				start, start + len, mem_pxm);
1538c2ecf20Sopenharmony_ci}
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_cistatic __init const char *hmat_data_type(u8 type)
1568c2ecf20Sopenharmony_ci{
1578c2ecf20Sopenharmony_ci	switch (type) {
1588c2ecf20Sopenharmony_ci	case ACPI_HMAT_ACCESS_LATENCY:
1598c2ecf20Sopenharmony_ci		return "Access Latency";
1608c2ecf20Sopenharmony_ci	case ACPI_HMAT_READ_LATENCY:
1618c2ecf20Sopenharmony_ci		return "Read Latency";
1628c2ecf20Sopenharmony_ci	case ACPI_HMAT_WRITE_LATENCY:
1638c2ecf20Sopenharmony_ci		return "Write Latency";
1648c2ecf20Sopenharmony_ci	case ACPI_HMAT_ACCESS_BANDWIDTH:
1658c2ecf20Sopenharmony_ci		return "Access Bandwidth";
1668c2ecf20Sopenharmony_ci	case ACPI_HMAT_READ_BANDWIDTH:
1678c2ecf20Sopenharmony_ci		return "Read Bandwidth";
1688c2ecf20Sopenharmony_ci	case ACPI_HMAT_WRITE_BANDWIDTH:
1698c2ecf20Sopenharmony_ci		return "Write Bandwidth";
1708c2ecf20Sopenharmony_ci	default:
1718c2ecf20Sopenharmony_ci		return "Reserved";
1728c2ecf20Sopenharmony_ci	}
1738c2ecf20Sopenharmony_ci}
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_cistatic __init const char *hmat_data_type_suffix(u8 type)
1768c2ecf20Sopenharmony_ci{
1778c2ecf20Sopenharmony_ci	switch (type) {
1788c2ecf20Sopenharmony_ci	case ACPI_HMAT_ACCESS_LATENCY:
1798c2ecf20Sopenharmony_ci	case ACPI_HMAT_READ_LATENCY:
1808c2ecf20Sopenharmony_ci	case ACPI_HMAT_WRITE_LATENCY:
1818c2ecf20Sopenharmony_ci		return " nsec";
1828c2ecf20Sopenharmony_ci	case ACPI_HMAT_ACCESS_BANDWIDTH:
1838c2ecf20Sopenharmony_ci	case ACPI_HMAT_READ_BANDWIDTH:
1848c2ecf20Sopenharmony_ci	case ACPI_HMAT_WRITE_BANDWIDTH:
1858c2ecf20Sopenharmony_ci		return " MB/s";
1868c2ecf20Sopenharmony_ci	default:
1878c2ecf20Sopenharmony_ci		return "";
1888c2ecf20Sopenharmony_ci	}
1898c2ecf20Sopenharmony_ci}
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_cistatic u32 hmat_normalize(u16 entry, u64 base, u8 type)
1928c2ecf20Sopenharmony_ci{
1938c2ecf20Sopenharmony_ci	u32 value;
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	/*
1968c2ecf20Sopenharmony_ci	 * Check for invalid and overflow values
1978c2ecf20Sopenharmony_ci	 */
1988c2ecf20Sopenharmony_ci	if (entry == 0xffff || !entry)
1998c2ecf20Sopenharmony_ci		return 0;
2008c2ecf20Sopenharmony_ci	else if (base > (UINT_MAX / (entry)))
2018c2ecf20Sopenharmony_ci		return 0;
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	/*
2048c2ecf20Sopenharmony_ci	 * Divide by the base unit for version 1, convert latency from
2058c2ecf20Sopenharmony_ci	 * picosenonds to nanoseconds if revision 2.
2068c2ecf20Sopenharmony_ci	 */
2078c2ecf20Sopenharmony_ci	value = entry * base;
2088c2ecf20Sopenharmony_ci	if (hmat_revision == 1) {
2098c2ecf20Sopenharmony_ci		if (value < 10)
2108c2ecf20Sopenharmony_ci			return 0;
2118c2ecf20Sopenharmony_ci		value = DIV_ROUND_UP(value, 10);
2128c2ecf20Sopenharmony_ci	} else if (hmat_revision == 2) {
2138c2ecf20Sopenharmony_ci		switch (type) {
2148c2ecf20Sopenharmony_ci		case ACPI_HMAT_ACCESS_LATENCY:
2158c2ecf20Sopenharmony_ci		case ACPI_HMAT_READ_LATENCY:
2168c2ecf20Sopenharmony_ci		case ACPI_HMAT_WRITE_LATENCY:
2178c2ecf20Sopenharmony_ci			value = DIV_ROUND_UP(value, 1000);
2188c2ecf20Sopenharmony_ci			break;
2198c2ecf20Sopenharmony_ci		default:
2208c2ecf20Sopenharmony_ci			break;
2218c2ecf20Sopenharmony_ci		}
2228c2ecf20Sopenharmony_ci	}
2238c2ecf20Sopenharmony_ci	return value;
2248c2ecf20Sopenharmony_ci}
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_cistatic void hmat_update_target_access(struct memory_target *target,
2278c2ecf20Sopenharmony_ci				      u8 type, u32 value, int access)
2288c2ecf20Sopenharmony_ci{
2298c2ecf20Sopenharmony_ci	switch (type) {
2308c2ecf20Sopenharmony_ci	case ACPI_HMAT_ACCESS_LATENCY:
2318c2ecf20Sopenharmony_ci		target->hmem_attrs[access].read_latency = value;
2328c2ecf20Sopenharmony_ci		target->hmem_attrs[access].write_latency = value;
2338c2ecf20Sopenharmony_ci		break;
2348c2ecf20Sopenharmony_ci	case ACPI_HMAT_READ_LATENCY:
2358c2ecf20Sopenharmony_ci		target->hmem_attrs[access].read_latency = value;
2368c2ecf20Sopenharmony_ci		break;
2378c2ecf20Sopenharmony_ci	case ACPI_HMAT_WRITE_LATENCY:
2388c2ecf20Sopenharmony_ci		target->hmem_attrs[access].write_latency = value;
2398c2ecf20Sopenharmony_ci		break;
2408c2ecf20Sopenharmony_ci	case ACPI_HMAT_ACCESS_BANDWIDTH:
2418c2ecf20Sopenharmony_ci		target->hmem_attrs[access].read_bandwidth = value;
2428c2ecf20Sopenharmony_ci		target->hmem_attrs[access].write_bandwidth = value;
2438c2ecf20Sopenharmony_ci		break;
2448c2ecf20Sopenharmony_ci	case ACPI_HMAT_READ_BANDWIDTH:
2458c2ecf20Sopenharmony_ci		target->hmem_attrs[access].read_bandwidth = value;
2468c2ecf20Sopenharmony_ci		break;
2478c2ecf20Sopenharmony_ci	case ACPI_HMAT_WRITE_BANDWIDTH:
2488c2ecf20Sopenharmony_ci		target->hmem_attrs[access].write_bandwidth = value;
2498c2ecf20Sopenharmony_ci		break;
2508c2ecf20Sopenharmony_ci	default:
2518c2ecf20Sopenharmony_ci		break;
2528c2ecf20Sopenharmony_ci	}
2538c2ecf20Sopenharmony_ci}
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_cistatic __init void hmat_add_locality(struct acpi_hmat_locality *hmat_loc)
2568c2ecf20Sopenharmony_ci{
2578c2ecf20Sopenharmony_ci	struct memory_locality *loc;
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	loc = kzalloc(sizeof(*loc), GFP_KERNEL);
2608c2ecf20Sopenharmony_ci	if (!loc) {
2618c2ecf20Sopenharmony_ci		pr_notice_once("Failed to allocate HMAT locality\n");
2628c2ecf20Sopenharmony_ci		return;
2638c2ecf20Sopenharmony_ci	}
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	loc->hmat_loc = hmat_loc;
2668c2ecf20Sopenharmony_ci	list_add_tail(&loc->node, &localities);
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	switch (hmat_loc->data_type) {
2698c2ecf20Sopenharmony_ci	case ACPI_HMAT_ACCESS_LATENCY:
2708c2ecf20Sopenharmony_ci		localities_types[READ_LATENCY] = loc;
2718c2ecf20Sopenharmony_ci		localities_types[WRITE_LATENCY] = loc;
2728c2ecf20Sopenharmony_ci		break;
2738c2ecf20Sopenharmony_ci	case ACPI_HMAT_READ_LATENCY:
2748c2ecf20Sopenharmony_ci		localities_types[READ_LATENCY] = loc;
2758c2ecf20Sopenharmony_ci		break;
2768c2ecf20Sopenharmony_ci	case ACPI_HMAT_WRITE_LATENCY:
2778c2ecf20Sopenharmony_ci		localities_types[WRITE_LATENCY] = loc;
2788c2ecf20Sopenharmony_ci		break;
2798c2ecf20Sopenharmony_ci	case ACPI_HMAT_ACCESS_BANDWIDTH:
2808c2ecf20Sopenharmony_ci		localities_types[READ_BANDWIDTH] = loc;
2818c2ecf20Sopenharmony_ci		localities_types[WRITE_BANDWIDTH] = loc;
2828c2ecf20Sopenharmony_ci		break;
2838c2ecf20Sopenharmony_ci	case ACPI_HMAT_READ_BANDWIDTH:
2848c2ecf20Sopenharmony_ci		localities_types[READ_BANDWIDTH] = loc;
2858c2ecf20Sopenharmony_ci		break;
2868c2ecf20Sopenharmony_ci	case ACPI_HMAT_WRITE_BANDWIDTH:
2878c2ecf20Sopenharmony_ci		localities_types[WRITE_BANDWIDTH] = loc;
2888c2ecf20Sopenharmony_ci		break;
2898c2ecf20Sopenharmony_ci	default:
2908c2ecf20Sopenharmony_ci		break;
2918c2ecf20Sopenharmony_ci	}
2928c2ecf20Sopenharmony_ci}
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_cistatic __init int hmat_parse_locality(union acpi_subtable_headers *header,
2958c2ecf20Sopenharmony_ci				      const unsigned long end)
2968c2ecf20Sopenharmony_ci{
2978c2ecf20Sopenharmony_ci	struct acpi_hmat_locality *hmat_loc = (void *)header;
2988c2ecf20Sopenharmony_ci	struct memory_target *target;
2998c2ecf20Sopenharmony_ci	unsigned int init, targ, total_size, ipds, tpds;
3008c2ecf20Sopenharmony_ci	u32 *inits, *targs, value;
3018c2ecf20Sopenharmony_ci	u16 *entries;
3028c2ecf20Sopenharmony_ci	u8 type, mem_hier;
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	if (hmat_loc->header.length < sizeof(*hmat_loc)) {
3058c2ecf20Sopenharmony_ci		pr_notice("HMAT: Unexpected locality header length: %u\n",
3068c2ecf20Sopenharmony_ci			 hmat_loc->header.length);
3078c2ecf20Sopenharmony_ci		return -EINVAL;
3088c2ecf20Sopenharmony_ci	}
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	type = hmat_loc->data_type;
3118c2ecf20Sopenharmony_ci	mem_hier = hmat_loc->flags & ACPI_HMAT_MEMORY_HIERARCHY;
3128c2ecf20Sopenharmony_ci	ipds = hmat_loc->number_of_initiator_Pds;
3138c2ecf20Sopenharmony_ci	tpds = hmat_loc->number_of_target_Pds;
3148c2ecf20Sopenharmony_ci	total_size = sizeof(*hmat_loc) + sizeof(*entries) * ipds * tpds +
3158c2ecf20Sopenharmony_ci		     sizeof(*inits) * ipds + sizeof(*targs) * tpds;
3168c2ecf20Sopenharmony_ci	if (hmat_loc->header.length < total_size) {
3178c2ecf20Sopenharmony_ci		pr_notice("HMAT: Unexpected locality header length:%u, minimum required:%u\n",
3188c2ecf20Sopenharmony_ci			 hmat_loc->header.length, total_size);
3198c2ecf20Sopenharmony_ci		return -EINVAL;
3208c2ecf20Sopenharmony_ci	}
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	pr_info("HMAT: Locality: Flags:%02x Type:%s Initiator Domains:%u Target Domains:%u Base:%lld\n",
3238c2ecf20Sopenharmony_ci		hmat_loc->flags, hmat_data_type(type), ipds, tpds,
3248c2ecf20Sopenharmony_ci		hmat_loc->entry_base_unit);
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	inits = (u32 *)(hmat_loc + 1);
3278c2ecf20Sopenharmony_ci	targs = inits + ipds;
3288c2ecf20Sopenharmony_ci	entries = (u16 *)(targs + tpds);
3298c2ecf20Sopenharmony_ci	for (init = 0; init < ipds; init++) {
3308c2ecf20Sopenharmony_ci		alloc_memory_initiator(inits[init]);
3318c2ecf20Sopenharmony_ci		for (targ = 0; targ < tpds; targ++) {
3328c2ecf20Sopenharmony_ci			value = hmat_normalize(entries[init * tpds + targ],
3338c2ecf20Sopenharmony_ci					       hmat_loc->entry_base_unit,
3348c2ecf20Sopenharmony_ci					       type);
3358c2ecf20Sopenharmony_ci			pr_info("  Initiator-Target[%u-%u]:%u%s\n",
3368c2ecf20Sopenharmony_ci				inits[init], targs[targ], value,
3378c2ecf20Sopenharmony_ci				hmat_data_type_suffix(type));
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci			if (mem_hier == ACPI_HMAT_MEMORY) {
3408c2ecf20Sopenharmony_ci				target = find_mem_target(targs[targ]);
3418c2ecf20Sopenharmony_ci				if (target && target->processor_pxm == inits[init]) {
3428c2ecf20Sopenharmony_ci					hmat_update_target_access(target, type, value, 0);
3438c2ecf20Sopenharmony_ci					/* If the node has a CPU, update access 1 */
3448c2ecf20Sopenharmony_ci					if (node_state(pxm_to_node(inits[init]), N_CPU))
3458c2ecf20Sopenharmony_ci						hmat_update_target_access(target, type, value, 1);
3468c2ecf20Sopenharmony_ci				}
3478c2ecf20Sopenharmony_ci			}
3488c2ecf20Sopenharmony_ci		}
3498c2ecf20Sopenharmony_ci	}
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	if (mem_hier == ACPI_HMAT_MEMORY)
3528c2ecf20Sopenharmony_ci		hmat_add_locality(hmat_loc);
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	return 0;
3558c2ecf20Sopenharmony_ci}
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_cistatic __init int hmat_parse_cache(union acpi_subtable_headers *header,
3588c2ecf20Sopenharmony_ci				   const unsigned long end)
3598c2ecf20Sopenharmony_ci{
3608c2ecf20Sopenharmony_ci	struct acpi_hmat_cache *cache = (void *)header;
3618c2ecf20Sopenharmony_ci	struct memory_target *target;
3628c2ecf20Sopenharmony_ci	struct target_cache *tcache;
3638c2ecf20Sopenharmony_ci	u32 attrs;
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	if (cache->header.length < sizeof(*cache)) {
3668c2ecf20Sopenharmony_ci		pr_notice("HMAT: Unexpected cache header length: %u\n",
3678c2ecf20Sopenharmony_ci			 cache->header.length);
3688c2ecf20Sopenharmony_ci		return -EINVAL;
3698c2ecf20Sopenharmony_ci	}
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	attrs = cache->cache_attributes;
3728c2ecf20Sopenharmony_ci	pr_info("HMAT: Cache: Domain:%u Size:%llu Attrs:%08x SMBIOS Handles:%d\n",
3738c2ecf20Sopenharmony_ci		cache->memory_PD, cache->cache_size, attrs,
3748c2ecf20Sopenharmony_ci		cache->number_of_SMBIOShandles);
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	target = find_mem_target(cache->memory_PD);
3778c2ecf20Sopenharmony_ci	if (!target)
3788c2ecf20Sopenharmony_ci		return 0;
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	tcache = kzalloc(sizeof(*tcache), GFP_KERNEL);
3818c2ecf20Sopenharmony_ci	if (!tcache) {
3828c2ecf20Sopenharmony_ci		pr_notice_once("Failed to allocate HMAT cache info\n");
3838c2ecf20Sopenharmony_ci		return 0;
3848c2ecf20Sopenharmony_ci	}
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	tcache->cache_attrs.size = cache->cache_size;
3878c2ecf20Sopenharmony_ci	tcache->cache_attrs.level = (attrs & ACPI_HMAT_CACHE_LEVEL) >> 4;
3888c2ecf20Sopenharmony_ci	tcache->cache_attrs.line_size = (attrs & ACPI_HMAT_CACHE_LINE_SIZE) >> 16;
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	switch ((attrs & ACPI_HMAT_CACHE_ASSOCIATIVITY) >> 8) {
3918c2ecf20Sopenharmony_ci	case ACPI_HMAT_CA_DIRECT_MAPPED:
3928c2ecf20Sopenharmony_ci		tcache->cache_attrs.indexing = NODE_CACHE_DIRECT_MAP;
3938c2ecf20Sopenharmony_ci		break;
3948c2ecf20Sopenharmony_ci	case ACPI_HMAT_CA_COMPLEX_CACHE_INDEXING:
3958c2ecf20Sopenharmony_ci		tcache->cache_attrs.indexing = NODE_CACHE_INDEXED;
3968c2ecf20Sopenharmony_ci		break;
3978c2ecf20Sopenharmony_ci	case ACPI_HMAT_CA_NONE:
3988c2ecf20Sopenharmony_ci	default:
3998c2ecf20Sopenharmony_ci		tcache->cache_attrs.indexing = NODE_CACHE_OTHER;
4008c2ecf20Sopenharmony_ci		break;
4018c2ecf20Sopenharmony_ci	}
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	switch ((attrs & ACPI_HMAT_WRITE_POLICY) >> 12) {
4048c2ecf20Sopenharmony_ci	case ACPI_HMAT_CP_WB:
4058c2ecf20Sopenharmony_ci		tcache->cache_attrs.write_policy = NODE_CACHE_WRITE_BACK;
4068c2ecf20Sopenharmony_ci		break;
4078c2ecf20Sopenharmony_ci	case ACPI_HMAT_CP_WT:
4088c2ecf20Sopenharmony_ci		tcache->cache_attrs.write_policy = NODE_CACHE_WRITE_THROUGH;
4098c2ecf20Sopenharmony_ci		break;
4108c2ecf20Sopenharmony_ci	case ACPI_HMAT_CP_NONE:
4118c2ecf20Sopenharmony_ci	default:
4128c2ecf20Sopenharmony_ci		tcache->cache_attrs.write_policy = NODE_CACHE_WRITE_OTHER;
4138c2ecf20Sopenharmony_ci		break;
4148c2ecf20Sopenharmony_ci	}
4158c2ecf20Sopenharmony_ci	list_add_tail(&tcache->node, &target->caches);
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	return 0;
4188c2ecf20Sopenharmony_ci}
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_cistatic int __init hmat_parse_proximity_domain(union acpi_subtable_headers *header,
4218c2ecf20Sopenharmony_ci					      const unsigned long end)
4228c2ecf20Sopenharmony_ci{
4238c2ecf20Sopenharmony_ci	struct acpi_hmat_proximity_domain *p = (void *)header;
4248c2ecf20Sopenharmony_ci	struct memory_target *target = NULL;
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci	if (p->header.length != sizeof(*p)) {
4278c2ecf20Sopenharmony_ci		pr_notice("HMAT: Unexpected address range header length: %u\n",
4288c2ecf20Sopenharmony_ci			 p->header.length);
4298c2ecf20Sopenharmony_ci		return -EINVAL;
4308c2ecf20Sopenharmony_ci	}
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	if (hmat_revision == 1)
4338c2ecf20Sopenharmony_ci		pr_info("HMAT: Memory (%#llx length %#llx) Flags:%04x Processor Domain:%u Memory Domain:%u\n",
4348c2ecf20Sopenharmony_ci			p->reserved3, p->reserved4, p->flags, p->processor_PD,
4358c2ecf20Sopenharmony_ci			p->memory_PD);
4368c2ecf20Sopenharmony_ci	else
4378c2ecf20Sopenharmony_ci		pr_info("HMAT: Memory Flags:%04x Processor Domain:%u Memory Domain:%u\n",
4388c2ecf20Sopenharmony_ci			p->flags, p->processor_PD, p->memory_PD);
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci	if ((hmat_revision == 1 && p->flags & ACPI_HMAT_MEMORY_PD_VALID) ||
4418c2ecf20Sopenharmony_ci	    hmat_revision > 1) {
4428c2ecf20Sopenharmony_ci		target = find_mem_target(p->memory_PD);
4438c2ecf20Sopenharmony_ci		if (!target) {
4448c2ecf20Sopenharmony_ci			pr_debug("HMAT: Memory Domain missing from SRAT\n");
4458c2ecf20Sopenharmony_ci			return -EINVAL;
4468c2ecf20Sopenharmony_ci		}
4478c2ecf20Sopenharmony_ci	}
4488c2ecf20Sopenharmony_ci	if (target && p->flags & ACPI_HMAT_PROCESSOR_PD_VALID) {
4498c2ecf20Sopenharmony_ci		int p_node = pxm_to_node(p->processor_PD);
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci		if (p_node == NUMA_NO_NODE) {
4528c2ecf20Sopenharmony_ci			pr_debug("HMAT: Invalid Processor Domain\n");
4538c2ecf20Sopenharmony_ci			return -EINVAL;
4548c2ecf20Sopenharmony_ci		}
4558c2ecf20Sopenharmony_ci		target->processor_pxm = p->processor_PD;
4568c2ecf20Sopenharmony_ci	}
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci	return 0;
4598c2ecf20Sopenharmony_ci}
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_cistatic int __init hmat_parse_subtable(union acpi_subtable_headers *header,
4628c2ecf20Sopenharmony_ci				      const unsigned long end)
4638c2ecf20Sopenharmony_ci{
4648c2ecf20Sopenharmony_ci	struct acpi_hmat_structure *hdr = (void *)header;
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci	if (!hdr)
4678c2ecf20Sopenharmony_ci		return -EINVAL;
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci	switch (hdr->type) {
4708c2ecf20Sopenharmony_ci	case ACPI_HMAT_TYPE_PROXIMITY:
4718c2ecf20Sopenharmony_ci		return hmat_parse_proximity_domain(header, end);
4728c2ecf20Sopenharmony_ci	case ACPI_HMAT_TYPE_LOCALITY:
4738c2ecf20Sopenharmony_ci		return hmat_parse_locality(header, end);
4748c2ecf20Sopenharmony_ci	case ACPI_HMAT_TYPE_CACHE:
4758c2ecf20Sopenharmony_ci		return hmat_parse_cache(header, end);
4768c2ecf20Sopenharmony_ci	default:
4778c2ecf20Sopenharmony_ci		return -EINVAL;
4788c2ecf20Sopenharmony_ci	}
4798c2ecf20Sopenharmony_ci}
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_cistatic __init int srat_parse_mem_affinity(union acpi_subtable_headers *header,
4828c2ecf20Sopenharmony_ci					  const unsigned long end)
4838c2ecf20Sopenharmony_ci{
4848c2ecf20Sopenharmony_ci	struct acpi_srat_mem_affinity *ma = (void *)header;
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci	if (!ma)
4878c2ecf20Sopenharmony_ci		return -EINVAL;
4888c2ecf20Sopenharmony_ci	if (!(ma->flags & ACPI_SRAT_MEM_ENABLED))
4898c2ecf20Sopenharmony_ci		return 0;
4908c2ecf20Sopenharmony_ci	alloc_memory_target(ma->proximity_domain, ma->base_address, ma->length);
4918c2ecf20Sopenharmony_ci	return 0;
4928c2ecf20Sopenharmony_ci}
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_cistatic u32 hmat_initiator_perf(struct memory_target *target,
4958c2ecf20Sopenharmony_ci			       struct memory_initiator *initiator,
4968c2ecf20Sopenharmony_ci			       struct acpi_hmat_locality *hmat_loc)
4978c2ecf20Sopenharmony_ci{
4988c2ecf20Sopenharmony_ci	unsigned int ipds, tpds, i, idx = 0, tdx = 0;
4998c2ecf20Sopenharmony_ci	u32 *inits, *targs;
5008c2ecf20Sopenharmony_ci	u16 *entries;
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci	ipds = hmat_loc->number_of_initiator_Pds;
5038c2ecf20Sopenharmony_ci	tpds = hmat_loc->number_of_target_Pds;
5048c2ecf20Sopenharmony_ci	inits = (u32 *)(hmat_loc + 1);
5058c2ecf20Sopenharmony_ci	targs = inits + ipds;
5068c2ecf20Sopenharmony_ci	entries = (u16 *)(targs + tpds);
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci	for (i = 0; i < ipds; i++) {
5098c2ecf20Sopenharmony_ci		if (inits[i] == initiator->processor_pxm) {
5108c2ecf20Sopenharmony_ci			idx = i;
5118c2ecf20Sopenharmony_ci			break;
5128c2ecf20Sopenharmony_ci		}
5138c2ecf20Sopenharmony_ci	}
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci	if (i == ipds)
5168c2ecf20Sopenharmony_ci		return 0;
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci	for (i = 0; i < tpds; i++) {
5198c2ecf20Sopenharmony_ci		if (targs[i] == target->memory_pxm) {
5208c2ecf20Sopenharmony_ci			tdx = i;
5218c2ecf20Sopenharmony_ci			break;
5228c2ecf20Sopenharmony_ci		}
5238c2ecf20Sopenharmony_ci	}
5248c2ecf20Sopenharmony_ci	if (i == tpds)
5258c2ecf20Sopenharmony_ci		return 0;
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_ci	return hmat_normalize(entries[idx * tpds + tdx],
5288c2ecf20Sopenharmony_ci			      hmat_loc->entry_base_unit,
5298c2ecf20Sopenharmony_ci			      hmat_loc->data_type);
5308c2ecf20Sopenharmony_ci}
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_cistatic bool hmat_update_best(u8 type, u32 value, u32 *best)
5338c2ecf20Sopenharmony_ci{
5348c2ecf20Sopenharmony_ci	bool updated = false;
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci	if (!value)
5378c2ecf20Sopenharmony_ci		return false;
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci	switch (type) {
5408c2ecf20Sopenharmony_ci	case ACPI_HMAT_ACCESS_LATENCY:
5418c2ecf20Sopenharmony_ci	case ACPI_HMAT_READ_LATENCY:
5428c2ecf20Sopenharmony_ci	case ACPI_HMAT_WRITE_LATENCY:
5438c2ecf20Sopenharmony_ci		if (!*best || *best > value) {
5448c2ecf20Sopenharmony_ci			*best = value;
5458c2ecf20Sopenharmony_ci			updated = true;
5468c2ecf20Sopenharmony_ci		}
5478c2ecf20Sopenharmony_ci		break;
5488c2ecf20Sopenharmony_ci	case ACPI_HMAT_ACCESS_BANDWIDTH:
5498c2ecf20Sopenharmony_ci	case ACPI_HMAT_READ_BANDWIDTH:
5508c2ecf20Sopenharmony_ci	case ACPI_HMAT_WRITE_BANDWIDTH:
5518c2ecf20Sopenharmony_ci		if (!*best || *best < value) {
5528c2ecf20Sopenharmony_ci			*best = value;
5538c2ecf20Sopenharmony_ci			updated = true;
5548c2ecf20Sopenharmony_ci		}
5558c2ecf20Sopenharmony_ci		break;
5568c2ecf20Sopenharmony_ci	}
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ci	return updated;
5598c2ecf20Sopenharmony_ci}
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_cistatic int initiator_cmp(void *priv, const struct list_head *a,
5628c2ecf20Sopenharmony_ci			 const struct list_head *b)
5638c2ecf20Sopenharmony_ci{
5648c2ecf20Sopenharmony_ci	struct memory_initiator *ia;
5658c2ecf20Sopenharmony_ci	struct memory_initiator *ib;
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_ci	ia = list_entry(a, struct memory_initiator, node);
5688c2ecf20Sopenharmony_ci	ib = list_entry(b, struct memory_initiator, node);
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci	return ia->processor_pxm - ib->processor_pxm;
5718c2ecf20Sopenharmony_ci}
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_cistatic int initiators_to_nodemask(unsigned long *p_nodes)
5748c2ecf20Sopenharmony_ci{
5758c2ecf20Sopenharmony_ci	struct memory_initiator *initiator;
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci	if (list_empty(&initiators))
5788c2ecf20Sopenharmony_ci		return -ENXIO;
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_ci	list_for_each_entry(initiator, &initiators, node)
5818c2ecf20Sopenharmony_ci		set_bit(initiator->processor_pxm, p_nodes);
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ci	return 0;
5848c2ecf20Sopenharmony_ci}
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_cistatic void hmat_register_target_initiators(struct memory_target *target)
5878c2ecf20Sopenharmony_ci{
5888c2ecf20Sopenharmony_ci	static DECLARE_BITMAP(p_nodes, MAX_NUMNODES);
5898c2ecf20Sopenharmony_ci	struct memory_initiator *initiator;
5908c2ecf20Sopenharmony_ci	unsigned int mem_nid, cpu_nid;
5918c2ecf20Sopenharmony_ci	struct memory_locality *loc = NULL;
5928c2ecf20Sopenharmony_ci	u32 best = 0;
5938c2ecf20Sopenharmony_ci	bool access0done = false;
5948c2ecf20Sopenharmony_ci	int i;
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_ci	mem_nid = pxm_to_node(target->memory_pxm);
5978c2ecf20Sopenharmony_ci	/*
5988c2ecf20Sopenharmony_ci	 * If the Address Range Structure provides a local processor pxm, link
5998c2ecf20Sopenharmony_ci	 * only that one. Otherwise, find the best performance attributes and
6008c2ecf20Sopenharmony_ci	 * register all initiators that match.
6018c2ecf20Sopenharmony_ci	 */
6028c2ecf20Sopenharmony_ci	if (target->processor_pxm != PXM_INVAL) {
6038c2ecf20Sopenharmony_ci		cpu_nid = pxm_to_node(target->processor_pxm);
6048c2ecf20Sopenharmony_ci		register_memory_node_under_compute_node(mem_nid, cpu_nid, 0);
6058c2ecf20Sopenharmony_ci		access0done = true;
6068c2ecf20Sopenharmony_ci		if (node_state(cpu_nid, N_CPU)) {
6078c2ecf20Sopenharmony_ci			register_memory_node_under_compute_node(mem_nid, cpu_nid, 1);
6088c2ecf20Sopenharmony_ci			return;
6098c2ecf20Sopenharmony_ci		}
6108c2ecf20Sopenharmony_ci	}
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_ci	if (list_empty(&localities))
6138c2ecf20Sopenharmony_ci		return;
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_ci	/*
6168c2ecf20Sopenharmony_ci	 * We need the initiator list sorted so we can use bitmap_clear for
6178c2ecf20Sopenharmony_ci	 * previously set initiators when we find a better memory accessor.
6188c2ecf20Sopenharmony_ci	 * We'll also use the sorting to prime the candidate nodes with known
6198c2ecf20Sopenharmony_ci	 * initiators.
6208c2ecf20Sopenharmony_ci	 */
6218c2ecf20Sopenharmony_ci	bitmap_zero(p_nodes, MAX_NUMNODES);
6228c2ecf20Sopenharmony_ci	list_sort(NULL, &initiators, initiator_cmp);
6238c2ecf20Sopenharmony_ci	if (initiators_to_nodemask(p_nodes) < 0)
6248c2ecf20Sopenharmony_ci		return;
6258c2ecf20Sopenharmony_ci
6268c2ecf20Sopenharmony_ci	if (!access0done) {
6278c2ecf20Sopenharmony_ci		for (i = WRITE_LATENCY; i <= READ_BANDWIDTH; i++) {
6288c2ecf20Sopenharmony_ci			loc = localities_types[i];
6298c2ecf20Sopenharmony_ci			if (!loc)
6308c2ecf20Sopenharmony_ci				continue;
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_ci			best = 0;
6338c2ecf20Sopenharmony_ci			list_for_each_entry(initiator, &initiators, node) {
6348c2ecf20Sopenharmony_ci				u32 value;
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_ci				if (!test_bit(initiator->processor_pxm, p_nodes))
6378c2ecf20Sopenharmony_ci					continue;
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci				value = hmat_initiator_perf(target, initiator,
6408c2ecf20Sopenharmony_ci							    loc->hmat_loc);
6418c2ecf20Sopenharmony_ci				if (hmat_update_best(loc->hmat_loc->data_type, value, &best))
6428c2ecf20Sopenharmony_ci					bitmap_clear(p_nodes, 0, initiator->processor_pxm);
6438c2ecf20Sopenharmony_ci				if (value != best)
6448c2ecf20Sopenharmony_ci					clear_bit(initiator->processor_pxm, p_nodes);
6458c2ecf20Sopenharmony_ci			}
6468c2ecf20Sopenharmony_ci			if (best)
6478c2ecf20Sopenharmony_ci				hmat_update_target_access(target, loc->hmat_loc->data_type,
6488c2ecf20Sopenharmony_ci							  best, 0);
6498c2ecf20Sopenharmony_ci		}
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_ci		for_each_set_bit(i, p_nodes, MAX_NUMNODES) {
6528c2ecf20Sopenharmony_ci			cpu_nid = pxm_to_node(i);
6538c2ecf20Sopenharmony_ci			register_memory_node_under_compute_node(mem_nid, cpu_nid, 0);
6548c2ecf20Sopenharmony_ci		}
6558c2ecf20Sopenharmony_ci	}
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_ci	/* Access 1 ignores Generic Initiators */
6588c2ecf20Sopenharmony_ci	bitmap_zero(p_nodes, MAX_NUMNODES);
6598c2ecf20Sopenharmony_ci	if (initiators_to_nodemask(p_nodes) < 0)
6608c2ecf20Sopenharmony_ci		return;
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_ci	for (i = WRITE_LATENCY; i <= READ_BANDWIDTH; i++) {
6638c2ecf20Sopenharmony_ci		loc = localities_types[i];
6648c2ecf20Sopenharmony_ci		if (!loc)
6658c2ecf20Sopenharmony_ci			continue;
6668c2ecf20Sopenharmony_ci
6678c2ecf20Sopenharmony_ci		best = 0;
6688c2ecf20Sopenharmony_ci		list_for_each_entry(initiator, &initiators, node) {
6698c2ecf20Sopenharmony_ci			u32 value;
6708c2ecf20Sopenharmony_ci
6718c2ecf20Sopenharmony_ci			if (!initiator->has_cpu) {
6728c2ecf20Sopenharmony_ci				clear_bit(initiator->processor_pxm, p_nodes);
6738c2ecf20Sopenharmony_ci				continue;
6748c2ecf20Sopenharmony_ci			}
6758c2ecf20Sopenharmony_ci			if (!test_bit(initiator->processor_pxm, p_nodes))
6768c2ecf20Sopenharmony_ci				continue;
6778c2ecf20Sopenharmony_ci
6788c2ecf20Sopenharmony_ci			value = hmat_initiator_perf(target, initiator, loc->hmat_loc);
6798c2ecf20Sopenharmony_ci			if (hmat_update_best(loc->hmat_loc->data_type, value, &best))
6808c2ecf20Sopenharmony_ci				bitmap_clear(p_nodes, 0, initiator->processor_pxm);
6818c2ecf20Sopenharmony_ci			if (value != best)
6828c2ecf20Sopenharmony_ci				clear_bit(initiator->processor_pxm, p_nodes);
6838c2ecf20Sopenharmony_ci		}
6848c2ecf20Sopenharmony_ci		if (best)
6858c2ecf20Sopenharmony_ci			hmat_update_target_access(target, loc->hmat_loc->data_type, best, 1);
6868c2ecf20Sopenharmony_ci	}
6878c2ecf20Sopenharmony_ci	for_each_set_bit(i, p_nodes, MAX_NUMNODES) {
6888c2ecf20Sopenharmony_ci		cpu_nid = pxm_to_node(i);
6898c2ecf20Sopenharmony_ci		register_memory_node_under_compute_node(mem_nid, cpu_nid, 1);
6908c2ecf20Sopenharmony_ci	}
6918c2ecf20Sopenharmony_ci}
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_cistatic void hmat_register_target_cache(struct memory_target *target)
6948c2ecf20Sopenharmony_ci{
6958c2ecf20Sopenharmony_ci	unsigned mem_nid = pxm_to_node(target->memory_pxm);
6968c2ecf20Sopenharmony_ci	struct target_cache *tcache;
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ci	list_for_each_entry(tcache, &target->caches, node)
6998c2ecf20Sopenharmony_ci		node_add_cache(mem_nid, &tcache->cache_attrs);
7008c2ecf20Sopenharmony_ci}
7018c2ecf20Sopenharmony_ci
7028c2ecf20Sopenharmony_cistatic void hmat_register_target_perf(struct memory_target *target, int access)
7038c2ecf20Sopenharmony_ci{
7048c2ecf20Sopenharmony_ci	unsigned mem_nid = pxm_to_node(target->memory_pxm);
7058c2ecf20Sopenharmony_ci	node_set_perf_attrs(mem_nid, &target->hmem_attrs[access], access);
7068c2ecf20Sopenharmony_ci}
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_cistatic void hmat_register_target_devices(struct memory_target *target)
7098c2ecf20Sopenharmony_ci{
7108c2ecf20Sopenharmony_ci	struct resource *res;
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_ci	/*
7138c2ecf20Sopenharmony_ci	 * Do not bother creating devices if no driver is available to
7148c2ecf20Sopenharmony_ci	 * consume them.
7158c2ecf20Sopenharmony_ci	 */
7168c2ecf20Sopenharmony_ci	if (!IS_ENABLED(CONFIG_DEV_DAX_HMEM))
7178c2ecf20Sopenharmony_ci		return;
7188c2ecf20Sopenharmony_ci
7198c2ecf20Sopenharmony_ci	for (res = target->memregions.child; res; res = res->sibling) {
7208c2ecf20Sopenharmony_ci		int target_nid = pxm_to_node(target->memory_pxm);
7218c2ecf20Sopenharmony_ci
7228c2ecf20Sopenharmony_ci		hmem_register_device(target_nid, res);
7238c2ecf20Sopenharmony_ci	}
7248c2ecf20Sopenharmony_ci}
7258c2ecf20Sopenharmony_ci
7268c2ecf20Sopenharmony_cistatic void hmat_register_target(struct memory_target *target)
7278c2ecf20Sopenharmony_ci{
7288c2ecf20Sopenharmony_ci	int nid = pxm_to_node(target->memory_pxm);
7298c2ecf20Sopenharmony_ci
7308c2ecf20Sopenharmony_ci	/*
7318c2ecf20Sopenharmony_ci	 * Devices may belong to either an offline or online
7328c2ecf20Sopenharmony_ci	 * node, so unconditionally add them.
7338c2ecf20Sopenharmony_ci	 */
7348c2ecf20Sopenharmony_ci	hmat_register_target_devices(target);
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_ci	/*
7378c2ecf20Sopenharmony_ci	 * Skip offline nodes. This can happen when memory
7388c2ecf20Sopenharmony_ci	 * marked EFI_MEMORY_SP, "specific purpose", is applied
7398c2ecf20Sopenharmony_ci	 * to all the memory in a promixity domain leading to
7408c2ecf20Sopenharmony_ci	 * the node being marked offline / unplugged, or if
7418c2ecf20Sopenharmony_ci	 * memory-only "hotplug" node is offline.
7428c2ecf20Sopenharmony_ci	 */
7438c2ecf20Sopenharmony_ci	if (nid == NUMA_NO_NODE || !node_online(nid))
7448c2ecf20Sopenharmony_ci		return;
7458c2ecf20Sopenharmony_ci
7468c2ecf20Sopenharmony_ci	mutex_lock(&target_lock);
7478c2ecf20Sopenharmony_ci	if (!target->registered) {
7488c2ecf20Sopenharmony_ci		hmat_register_target_initiators(target);
7498c2ecf20Sopenharmony_ci		hmat_register_target_cache(target);
7508c2ecf20Sopenharmony_ci		hmat_register_target_perf(target, 0);
7518c2ecf20Sopenharmony_ci		hmat_register_target_perf(target, 1);
7528c2ecf20Sopenharmony_ci		target->registered = true;
7538c2ecf20Sopenharmony_ci	}
7548c2ecf20Sopenharmony_ci	mutex_unlock(&target_lock);
7558c2ecf20Sopenharmony_ci}
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_cistatic void hmat_register_targets(void)
7588c2ecf20Sopenharmony_ci{
7598c2ecf20Sopenharmony_ci	struct memory_target *target;
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_ci	list_for_each_entry(target, &targets, node)
7628c2ecf20Sopenharmony_ci		hmat_register_target(target);
7638c2ecf20Sopenharmony_ci}
7648c2ecf20Sopenharmony_ci
7658c2ecf20Sopenharmony_cistatic int hmat_callback(struct notifier_block *self,
7668c2ecf20Sopenharmony_ci			 unsigned long action, void *arg)
7678c2ecf20Sopenharmony_ci{
7688c2ecf20Sopenharmony_ci	struct memory_target *target;
7698c2ecf20Sopenharmony_ci	struct memory_notify *mnb = arg;
7708c2ecf20Sopenharmony_ci	int pxm, nid = mnb->status_change_nid;
7718c2ecf20Sopenharmony_ci
7728c2ecf20Sopenharmony_ci	if (nid == NUMA_NO_NODE || action != MEM_ONLINE)
7738c2ecf20Sopenharmony_ci		return NOTIFY_OK;
7748c2ecf20Sopenharmony_ci
7758c2ecf20Sopenharmony_ci	pxm = node_to_pxm(nid);
7768c2ecf20Sopenharmony_ci	target = find_mem_target(pxm);
7778c2ecf20Sopenharmony_ci	if (!target)
7788c2ecf20Sopenharmony_ci		return NOTIFY_OK;
7798c2ecf20Sopenharmony_ci
7808c2ecf20Sopenharmony_ci	hmat_register_target(target);
7818c2ecf20Sopenharmony_ci	return NOTIFY_OK;
7828c2ecf20Sopenharmony_ci}
7838c2ecf20Sopenharmony_ci
7848c2ecf20Sopenharmony_cistatic struct notifier_block hmat_callback_nb = {
7858c2ecf20Sopenharmony_ci	.notifier_call = hmat_callback,
7868c2ecf20Sopenharmony_ci	.priority = 2,
7878c2ecf20Sopenharmony_ci};
7888c2ecf20Sopenharmony_ci
7898c2ecf20Sopenharmony_cistatic __init void hmat_free_structures(void)
7908c2ecf20Sopenharmony_ci{
7918c2ecf20Sopenharmony_ci	struct memory_target *target, *tnext;
7928c2ecf20Sopenharmony_ci	struct memory_locality *loc, *lnext;
7938c2ecf20Sopenharmony_ci	struct memory_initiator *initiator, *inext;
7948c2ecf20Sopenharmony_ci	struct target_cache *tcache, *cnext;
7958c2ecf20Sopenharmony_ci
7968c2ecf20Sopenharmony_ci	list_for_each_entry_safe(target, tnext, &targets, node) {
7978c2ecf20Sopenharmony_ci		struct resource *res, *res_next;
7988c2ecf20Sopenharmony_ci
7998c2ecf20Sopenharmony_ci		list_for_each_entry_safe(tcache, cnext, &target->caches, node) {
8008c2ecf20Sopenharmony_ci			list_del(&tcache->node);
8018c2ecf20Sopenharmony_ci			kfree(tcache);
8028c2ecf20Sopenharmony_ci		}
8038c2ecf20Sopenharmony_ci
8048c2ecf20Sopenharmony_ci		list_del(&target->node);
8058c2ecf20Sopenharmony_ci		res = target->memregions.child;
8068c2ecf20Sopenharmony_ci		while (res) {
8078c2ecf20Sopenharmony_ci			res_next = res->sibling;
8088c2ecf20Sopenharmony_ci			__release_region(&target->memregions, res->start,
8098c2ecf20Sopenharmony_ci					resource_size(res));
8108c2ecf20Sopenharmony_ci			res = res_next;
8118c2ecf20Sopenharmony_ci		}
8128c2ecf20Sopenharmony_ci		kfree(target);
8138c2ecf20Sopenharmony_ci	}
8148c2ecf20Sopenharmony_ci
8158c2ecf20Sopenharmony_ci	list_for_each_entry_safe(initiator, inext, &initiators, node) {
8168c2ecf20Sopenharmony_ci		list_del(&initiator->node);
8178c2ecf20Sopenharmony_ci		kfree(initiator);
8188c2ecf20Sopenharmony_ci	}
8198c2ecf20Sopenharmony_ci
8208c2ecf20Sopenharmony_ci	list_for_each_entry_safe(loc, lnext, &localities, node) {
8218c2ecf20Sopenharmony_ci		list_del(&loc->node);
8228c2ecf20Sopenharmony_ci		kfree(loc);
8238c2ecf20Sopenharmony_ci	}
8248c2ecf20Sopenharmony_ci}
8258c2ecf20Sopenharmony_ci
8268c2ecf20Sopenharmony_cistatic __init int hmat_init(void)
8278c2ecf20Sopenharmony_ci{
8288c2ecf20Sopenharmony_ci	struct acpi_table_header *tbl;
8298c2ecf20Sopenharmony_ci	enum acpi_hmat_type i;
8308c2ecf20Sopenharmony_ci	acpi_status status;
8318c2ecf20Sopenharmony_ci
8328c2ecf20Sopenharmony_ci	if (srat_disabled() || hmat_disable)
8338c2ecf20Sopenharmony_ci		return 0;
8348c2ecf20Sopenharmony_ci
8358c2ecf20Sopenharmony_ci	status = acpi_get_table(ACPI_SIG_SRAT, 0, &tbl);
8368c2ecf20Sopenharmony_ci	if (ACPI_FAILURE(status))
8378c2ecf20Sopenharmony_ci		return 0;
8388c2ecf20Sopenharmony_ci
8398c2ecf20Sopenharmony_ci	if (acpi_table_parse_entries(ACPI_SIG_SRAT,
8408c2ecf20Sopenharmony_ci				sizeof(struct acpi_table_srat),
8418c2ecf20Sopenharmony_ci				ACPI_SRAT_TYPE_MEMORY_AFFINITY,
8428c2ecf20Sopenharmony_ci				srat_parse_mem_affinity, 0) < 0)
8438c2ecf20Sopenharmony_ci		goto out_put;
8448c2ecf20Sopenharmony_ci	acpi_put_table(tbl);
8458c2ecf20Sopenharmony_ci
8468c2ecf20Sopenharmony_ci	status = acpi_get_table(ACPI_SIG_HMAT, 0, &tbl);
8478c2ecf20Sopenharmony_ci	if (ACPI_FAILURE(status))
8488c2ecf20Sopenharmony_ci		goto out_put;
8498c2ecf20Sopenharmony_ci
8508c2ecf20Sopenharmony_ci	hmat_revision = tbl->revision;
8518c2ecf20Sopenharmony_ci	switch (hmat_revision) {
8528c2ecf20Sopenharmony_ci	case 1:
8538c2ecf20Sopenharmony_ci	case 2:
8548c2ecf20Sopenharmony_ci		break;
8558c2ecf20Sopenharmony_ci	default:
8568c2ecf20Sopenharmony_ci		pr_notice("Ignoring HMAT: Unknown revision:%d\n", hmat_revision);
8578c2ecf20Sopenharmony_ci		goto out_put;
8588c2ecf20Sopenharmony_ci	}
8598c2ecf20Sopenharmony_ci
8608c2ecf20Sopenharmony_ci	for (i = ACPI_HMAT_TYPE_PROXIMITY; i < ACPI_HMAT_TYPE_RESERVED; i++) {
8618c2ecf20Sopenharmony_ci		if (acpi_table_parse_entries(ACPI_SIG_HMAT,
8628c2ecf20Sopenharmony_ci					     sizeof(struct acpi_table_hmat), i,
8638c2ecf20Sopenharmony_ci					     hmat_parse_subtable, 0) < 0) {
8648c2ecf20Sopenharmony_ci			pr_notice("Ignoring HMAT: Invalid table");
8658c2ecf20Sopenharmony_ci			goto out_put;
8668c2ecf20Sopenharmony_ci		}
8678c2ecf20Sopenharmony_ci	}
8688c2ecf20Sopenharmony_ci	hmat_register_targets();
8698c2ecf20Sopenharmony_ci
8708c2ecf20Sopenharmony_ci	/* Keep the table and structures if the notifier may use them */
8718c2ecf20Sopenharmony_ci	if (!register_hotmemory_notifier(&hmat_callback_nb))
8728c2ecf20Sopenharmony_ci		return 0;
8738c2ecf20Sopenharmony_ciout_put:
8748c2ecf20Sopenharmony_ci	hmat_free_structures();
8758c2ecf20Sopenharmony_ci	acpi_put_table(tbl);
8768c2ecf20Sopenharmony_ci	return 0;
8778c2ecf20Sopenharmony_ci}
8788c2ecf20Sopenharmony_cidevice_initcall(hmat_init);
879