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