162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2019, Intel Corporation. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Heterogeneous Memory Attributes Table (HMAT) representation 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * This program parses and reports the platform's HMAT tables, and registers 862306a36Sopenharmony_ci * the applicable attributes with the node's interfaces. 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#define pr_fmt(fmt) "acpi/hmat: " fmt 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/acpi.h> 1462306a36Sopenharmony_ci#include <linux/bitops.h> 1562306a36Sopenharmony_ci#include <linux/device.h> 1662306a36Sopenharmony_ci#include <linux/init.h> 1762306a36Sopenharmony_ci#include <linux/list.h> 1862306a36Sopenharmony_ci#include <linux/mm.h> 1962306a36Sopenharmony_ci#include <linux/platform_device.h> 2062306a36Sopenharmony_ci#include <linux/list_sort.h> 2162306a36Sopenharmony_ci#include <linux/memregion.h> 2262306a36Sopenharmony_ci#include <linux/memory.h> 2362306a36Sopenharmony_ci#include <linux/mutex.h> 2462306a36Sopenharmony_ci#include <linux/node.h> 2562306a36Sopenharmony_ci#include <linux/sysfs.h> 2662306a36Sopenharmony_ci#include <linux/dax.h> 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistatic u8 hmat_revision; 2962306a36Sopenharmony_cistatic int hmat_disable __initdata; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_civoid __init disable_hmat(void) 3262306a36Sopenharmony_ci{ 3362306a36Sopenharmony_ci hmat_disable = 1; 3462306a36Sopenharmony_ci} 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistatic LIST_HEAD(targets); 3762306a36Sopenharmony_cistatic LIST_HEAD(initiators); 3862306a36Sopenharmony_cistatic LIST_HEAD(localities); 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistatic DEFINE_MUTEX(target_lock); 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci/* 4362306a36Sopenharmony_ci * The defined enum order is used to prioritize attributes to break ties when 4462306a36Sopenharmony_ci * selecting the best performing node. 4562306a36Sopenharmony_ci */ 4662306a36Sopenharmony_cienum locality_types { 4762306a36Sopenharmony_ci WRITE_LATENCY, 4862306a36Sopenharmony_ci READ_LATENCY, 4962306a36Sopenharmony_ci WRITE_BANDWIDTH, 5062306a36Sopenharmony_ci READ_BANDWIDTH, 5162306a36Sopenharmony_ci}; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistatic struct memory_locality *localities_types[4]; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistruct target_cache { 5662306a36Sopenharmony_ci struct list_head node; 5762306a36Sopenharmony_ci struct node_cache_attrs cache_attrs; 5862306a36Sopenharmony_ci}; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistruct memory_target { 6162306a36Sopenharmony_ci struct list_head node; 6262306a36Sopenharmony_ci unsigned int memory_pxm; 6362306a36Sopenharmony_ci unsigned int processor_pxm; 6462306a36Sopenharmony_ci struct resource memregions; 6562306a36Sopenharmony_ci struct node_hmem_attrs hmem_attrs[2]; 6662306a36Sopenharmony_ci struct list_head caches; 6762306a36Sopenharmony_ci struct node_cache_attrs cache_attrs; 6862306a36Sopenharmony_ci bool registered; 6962306a36Sopenharmony_ci}; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistruct memory_initiator { 7262306a36Sopenharmony_ci struct list_head node; 7362306a36Sopenharmony_ci unsigned int processor_pxm; 7462306a36Sopenharmony_ci bool has_cpu; 7562306a36Sopenharmony_ci}; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistruct memory_locality { 7862306a36Sopenharmony_ci struct list_head node; 7962306a36Sopenharmony_ci struct acpi_hmat_locality *hmat_loc; 8062306a36Sopenharmony_ci}; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistatic struct memory_initiator *find_mem_initiator(unsigned int cpu_pxm) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci struct memory_initiator *initiator; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci list_for_each_entry(initiator, &initiators, node) 8762306a36Sopenharmony_ci if (initiator->processor_pxm == cpu_pxm) 8862306a36Sopenharmony_ci return initiator; 8962306a36Sopenharmony_ci return NULL; 9062306a36Sopenharmony_ci} 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cistatic struct memory_target *find_mem_target(unsigned int mem_pxm) 9362306a36Sopenharmony_ci{ 9462306a36Sopenharmony_ci struct memory_target *target; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci list_for_each_entry(target, &targets, node) 9762306a36Sopenharmony_ci if (target->memory_pxm == mem_pxm) 9862306a36Sopenharmony_ci return target; 9962306a36Sopenharmony_ci return NULL; 10062306a36Sopenharmony_ci} 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistatic __init void alloc_memory_initiator(unsigned int cpu_pxm) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci struct memory_initiator *initiator; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci if (pxm_to_node(cpu_pxm) == NUMA_NO_NODE) 10762306a36Sopenharmony_ci return; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci initiator = find_mem_initiator(cpu_pxm); 11062306a36Sopenharmony_ci if (initiator) 11162306a36Sopenharmony_ci return; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci initiator = kzalloc(sizeof(*initiator), GFP_KERNEL); 11462306a36Sopenharmony_ci if (!initiator) 11562306a36Sopenharmony_ci return; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci initiator->processor_pxm = cpu_pxm; 11862306a36Sopenharmony_ci initiator->has_cpu = node_state(pxm_to_node(cpu_pxm), N_CPU); 11962306a36Sopenharmony_ci list_add_tail(&initiator->node, &initiators); 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic __init void alloc_memory_target(unsigned int mem_pxm, 12362306a36Sopenharmony_ci resource_size_t start, resource_size_t len) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci struct memory_target *target; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci target = find_mem_target(mem_pxm); 12862306a36Sopenharmony_ci if (!target) { 12962306a36Sopenharmony_ci target = kzalloc(sizeof(*target), GFP_KERNEL); 13062306a36Sopenharmony_ci if (!target) 13162306a36Sopenharmony_ci return; 13262306a36Sopenharmony_ci target->memory_pxm = mem_pxm; 13362306a36Sopenharmony_ci target->processor_pxm = PXM_INVAL; 13462306a36Sopenharmony_ci target->memregions = (struct resource) { 13562306a36Sopenharmony_ci .name = "ACPI mem", 13662306a36Sopenharmony_ci .start = 0, 13762306a36Sopenharmony_ci .end = -1, 13862306a36Sopenharmony_ci .flags = IORESOURCE_MEM, 13962306a36Sopenharmony_ci }; 14062306a36Sopenharmony_ci list_add_tail(&target->node, &targets); 14162306a36Sopenharmony_ci INIT_LIST_HEAD(&target->caches); 14262306a36Sopenharmony_ci } 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci /* 14562306a36Sopenharmony_ci * There are potentially multiple ranges per PXM, so record each 14662306a36Sopenharmony_ci * in the per-target memregions resource tree. 14762306a36Sopenharmony_ci */ 14862306a36Sopenharmony_ci if (!__request_region(&target->memregions, start, len, "memory target", 14962306a36Sopenharmony_ci IORESOURCE_MEM)) 15062306a36Sopenharmony_ci pr_warn("failed to reserve %#llx - %#llx in pxm: %d\n", 15162306a36Sopenharmony_ci start, start + len, mem_pxm); 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cistatic __init const char *hmat_data_type(u8 type) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci switch (type) { 15762306a36Sopenharmony_ci case ACPI_HMAT_ACCESS_LATENCY: 15862306a36Sopenharmony_ci return "Access Latency"; 15962306a36Sopenharmony_ci case ACPI_HMAT_READ_LATENCY: 16062306a36Sopenharmony_ci return "Read Latency"; 16162306a36Sopenharmony_ci case ACPI_HMAT_WRITE_LATENCY: 16262306a36Sopenharmony_ci return "Write Latency"; 16362306a36Sopenharmony_ci case ACPI_HMAT_ACCESS_BANDWIDTH: 16462306a36Sopenharmony_ci return "Access Bandwidth"; 16562306a36Sopenharmony_ci case ACPI_HMAT_READ_BANDWIDTH: 16662306a36Sopenharmony_ci return "Read Bandwidth"; 16762306a36Sopenharmony_ci case ACPI_HMAT_WRITE_BANDWIDTH: 16862306a36Sopenharmony_ci return "Write Bandwidth"; 16962306a36Sopenharmony_ci default: 17062306a36Sopenharmony_ci return "Reserved"; 17162306a36Sopenharmony_ci } 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_cistatic __init const char *hmat_data_type_suffix(u8 type) 17562306a36Sopenharmony_ci{ 17662306a36Sopenharmony_ci switch (type) { 17762306a36Sopenharmony_ci case ACPI_HMAT_ACCESS_LATENCY: 17862306a36Sopenharmony_ci case ACPI_HMAT_READ_LATENCY: 17962306a36Sopenharmony_ci case ACPI_HMAT_WRITE_LATENCY: 18062306a36Sopenharmony_ci return " nsec"; 18162306a36Sopenharmony_ci case ACPI_HMAT_ACCESS_BANDWIDTH: 18262306a36Sopenharmony_ci case ACPI_HMAT_READ_BANDWIDTH: 18362306a36Sopenharmony_ci case ACPI_HMAT_WRITE_BANDWIDTH: 18462306a36Sopenharmony_ci return " MB/s"; 18562306a36Sopenharmony_ci default: 18662306a36Sopenharmony_ci return ""; 18762306a36Sopenharmony_ci } 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_cistatic u32 hmat_normalize(u16 entry, u64 base, u8 type) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci u32 value; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci /* 19562306a36Sopenharmony_ci * Check for invalid and overflow values 19662306a36Sopenharmony_ci */ 19762306a36Sopenharmony_ci if (entry == 0xffff || !entry) 19862306a36Sopenharmony_ci return 0; 19962306a36Sopenharmony_ci else if (base > (UINT_MAX / (entry))) 20062306a36Sopenharmony_ci return 0; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci /* 20362306a36Sopenharmony_ci * Divide by the base unit for version 1, convert latency from 20462306a36Sopenharmony_ci * picosenonds to nanoseconds if revision 2. 20562306a36Sopenharmony_ci */ 20662306a36Sopenharmony_ci value = entry * base; 20762306a36Sopenharmony_ci if (hmat_revision == 1) { 20862306a36Sopenharmony_ci if (value < 10) 20962306a36Sopenharmony_ci return 0; 21062306a36Sopenharmony_ci value = DIV_ROUND_UP(value, 10); 21162306a36Sopenharmony_ci } else if (hmat_revision == 2) { 21262306a36Sopenharmony_ci switch (type) { 21362306a36Sopenharmony_ci case ACPI_HMAT_ACCESS_LATENCY: 21462306a36Sopenharmony_ci case ACPI_HMAT_READ_LATENCY: 21562306a36Sopenharmony_ci case ACPI_HMAT_WRITE_LATENCY: 21662306a36Sopenharmony_ci value = DIV_ROUND_UP(value, 1000); 21762306a36Sopenharmony_ci break; 21862306a36Sopenharmony_ci default: 21962306a36Sopenharmony_ci break; 22062306a36Sopenharmony_ci } 22162306a36Sopenharmony_ci } 22262306a36Sopenharmony_ci return value; 22362306a36Sopenharmony_ci} 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_cistatic void hmat_update_target_access(struct memory_target *target, 22662306a36Sopenharmony_ci u8 type, u32 value, int access) 22762306a36Sopenharmony_ci{ 22862306a36Sopenharmony_ci switch (type) { 22962306a36Sopenharmony_ci case ACPI_HMAT_ACCESS_LATENCY: 23062306a36Sopenharmony_ci target->hmem_attrs[access].read_latency = value; 23162306a36Sopenharmony_ci target->hmem_attrs[access].write_latency = value; 23262306a36Sopenharmony_ci break; 23362306a36Sopenharmony_ci case ACPI_HMAT_READ_LATENCY: 23462306a36Sopenharmony_ci target->hmem_attrs[access].read_latency = value; 23562306a36Sopenharmony_ci break; 23662306a36Sopenharmony_ci case ACPI_HMAT_WRITE_LATENCY: 23762306a36Sopenharmony_ci target->hmem_attrs[access].write_latency = value; 23862306a36Sopenharmony_ci break; 23962306a36Sopenharmony_ci case ACPI_HMAT_ACCESS_BANDWIDTH: 24062306a36Sopenharmony_ci target->hmem_attrs[access].read_bandwidth = value; 24162306a36Sopenharmony_ci target->hmem_attrs[access].write_bandwidth = value; 24262306a36Sopenharmony_ci break; 24362306a36Sopenharmony_ci case ACPI_HMAT_READ_BANDWIDTH: 24462306a36Sopenharmony_ci target->hmem_attrs[access].read_bandwidth = value; 24562306a36Sopenharmony_ci break; 24662306a36Sopenharmony_ci case ACPI_HMAT_WRITE_BANDWIDTH: 24762306a36Sopenharmony_ci target->hmem_attrs[access].write_bandwidth = value; 24862306a36Sopenharmony_ci break; 24962306a36Sopenharmony_ci default: 25062306a36Sopenharmony_ci break; 25162306a36Sopenharmony_ci } 25262306a36Sopenharmony_ci} 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_cistatic __init void hmat_add_locality(struct acpi_hmat_locality *hmat_loc) 25562306a36Sopenharmony_ci{ 25662306a36Sopenharmony_ci struct memory_locality *loc; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci loc = kzalloc(sizeof(*loc), GFP_KERNEL); 25962306a36Sopenharmony_ci if (!loc) { 26062306a36Sopenharmony_ci pr_notice_once("Failed to allocate HMAT locality\n"); 26162306a36Sopenharmony_ci return; 26262306a36Sopenharmony_ci } 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci loc->hmat_loc = hmat_loc; 26562306a36Sopenharmony_ci list_add_tail(&loc->node, &localities); 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci switch (hmat_loc->data_type) { 26862306a36Sopenharmony_ci case ACPI_HMAT_ACCESS_LATENCY: 26962306a36Sopenharmony_ci localities_types[READ_LATENCY] = loc; 27062306a36Sopenharmony_ci localities_types[WRITE_LATENCY] = loc; 27162306a36Sopenharmony_ci break; 27262306a36Sopenharmony_ci case ACPI_HMAT_READ_LATENCY: 27362306a36Sopenharmony_ci localities_types[READ_LATENCY] = loc; 27462306a36Sopenharmony_ci break; 27562306a36Sopenharmony_ci case ACPI_HMAT_WRITE_LATENCY: 27662306a36Sopenharmony_ci localities_types[WRITE_LATENCY] = loc; 27762306a36Sopenharmony_ci break; 27862306a36Sopenharmony_ci case ACPI_HMAT_ACCESS_BANDWIDTH: 27962306a36Sopenharmony_ci localities_types[READ_BANDWIDTH] = loc; 28062306a36Sopenharmony_ci localities_types[WRITE_BANDWIDTH] = loc; 28162306a36Sopenharmony_ci break; 28262306a36Sopenharmony_ci case ACPI_HMAT_READ_BANDWIDTH: 28362306a36Sopenharmony_ci localities_types[READ_BANDWIDTH] = loc; 28462306a36Sopenharmony_ci break; 28562306a36Sopenharmony_ci case ACPI_HMAT_WRITE_BANDWIDTH: 28662306a36Sopenharmony_ci localities_types[WRITE_BANDWIDTH] = loc; 28762306a36Sopenharmony_ci break; 28862306a36Sopenharmony_ci default: 28962306a36Sopenharmony_ci break; 29062306a36Sopenharmony_ci } 29162306a36Sopenharmony_ci} 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_cistatic __init int hmat_parse_locality(union acpi_subtable_headers *header, 29462306a36Sopenharmony_ci const unsigned long end) 29562306a36Sopenharmony_ci{ 29662306a36Sopenharmony_ci struct acpi_hmat_locality *hmat_loc = (void *)header; 29762306a36Sopenharmony_ci struct memory_target *target; 29862306a36Sopenharmony_ci unsigned int init, targ, total_size, ipds, tpds; 29962306a36Sopenharmony_ci u32 *inits, *targs, value; 30062306a36Sopenharmony_ci u16 *entries; 30162306a36Sopenharmony_ci u8 type, mem_hier; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci if (hmat_loc->header.length < sizeof(*hmat_loc)) { 30462306a36Sopenharmony_ci pr_notice("Unexpected locality header length: %u\n", 30562306a36Sopenharmony_ci hmat_loc->header.length); 30662306a36Sopenharmony_ci return -EINVAL; 30762306a36Sopenharmony_ci } 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci type = hmat_loc->data_type; 31062306a36Sopenharmony_ci mem_hier = hmat_loc->flags & ACPI_HMAT_MEMORY_HIERARCHY; 31162306a36Sopenharmony_ci ipds = hmat_loc->number_of_initiator_Pds; 31262306a36Sopenharmony_ci tpds = hmat_loc->number_of_target_Pds; 31362306a36Sopenharmony_ci total_size = sizeof(*hmat_loc) + sizeof(*entries) * ipds * tpds + 31462306a36Sopenharmony_ci sizeof(*inits) * ipds + sizeof(*targs) * tpds; 31562306a36Sopenharmony_ci if (hmat_loc->header.length < total_size) { 31662306a36Sopenharmony_ci pr_notice("Unexpected locality header length:%u, minimum required:%u\n", 31762306a36Sopenharmony_ci hmat_loc->header.length, total_size); 31862306a36Sopenharmony_ci return -EINVAL; 31962306a36Sopenharmony_ci } 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci pr_info("Locality: Flags:%02x Type:%s Initiator Domains:%u Target Domains:%u Base:%lld\n", 32262306a36Sopenharmony_ci hmat_loc->flags, hmat_data_type(type), ipds, tpds, 32362306a36Sopenharmony_ci hmat_loc->entry_base_unit); 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci inits = (u32 *)(hmat_loc + 1); 32662306a36Sopenharmony_ci targs = inits + ipds; 32762306a36Sopenharmony_ci entries = (u16 *)(targs + tpds); 32862306a36Sopenharmony_ci for (init = 0; init < ipds; init++) { 32962306a36Sopenharmony_ci alloc_memory_initiator(inits[init]); 33062306a36Sopenharmony_ci for (targ = 0; targ < tpds; targ++) { 33162306a36Sopenharmony_ci value = hmat_normalize(entries[init * tpds + targ], 33262306a36Sopenharmony_ci hmat_loc->entry_base_unit, 33362306a36Sopenharmony_ci type); 33462306a36Sopenharmony_ci pr_info(" Initiator-Target[%u-%u]:%u%s\n", 33562306a36Sopenharmony_ci inits[init], targs[targ], value, 33662306a36Sopenharmony_ci hmat_data_type_suffix(type)); 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci if (mem_hier == ACPI_HMAT_MEMORY) { 33962306a36Sopenharmony_ci target = find_mem_target(targs[targ]); 34062306a36Sopenharmony_ci if (target && target->processor_pxm == inits[init]) { 34162306a36Sopenharmony_ci hmat_update_target_access(target, type, value, 0); 34262306a36Sopenharmony_ci /* If the node has a CPU, update access 1 */ 34362306a36Sopenharmony_ci if (node_state(pxm_to_node(inits[init]), N_CPU)) 34462306a36Sopenharmony_ci hmat_update_target_access(target, type, value, 1); 34562306a36Sopenharmony_ci } 34662306a36Sopenharmony_ci } 34762306a36Sopenharmony_ci } 34862306a36Sopenharmony_ci } 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci if (mem_hier == ACPI_HMAT_MEMORY) 35162306a36Sopenharmony_ci hmat_add_locality(hmat_loc); 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci return 0; 35462306a36Sopenharmony_ci} 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_cistatic __init int hmat_parse_cache(union acpi_subtable_headers *header, 35762306a36Sopenharmony_ci const unsigned long end) 35862306a36Sopenharmony_ci{ 35962306a36Sopenharmony_ci struct acpi_hmat_cache *cache = (void *)header; 36062306a36Sopenharmony_ci struct memory_target *target; 36162306a36Sopenharmony_ci struct target_cache *tcache; 36262306a36Sopenharmony_ci u32 attrs; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci if (cache->header.length < sizeof(*cache)) { 36562306a36Sopenharmony_ci pr_notice("Unexpected cache header length: %u\n", 36662306a36Sopenharmony_ci cache->header.length); 36762306a36Sopenharmony_ci return -EINVAL; 36862306a36Sopenharmony_ci } 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci attrs = cache->cache_attributes; 37162306a36Sopenharmony_ci pr_info("Cache: Domain:%u Size:%llu Attrs:%08x SMBIOS Handles:%d\n", 37262306a36Sopenharmony_ci cache->memory_PD, cache->cache_size, attrs, 37362306a36Sopenharmony_ci cache->number_of_SMBIOShandles); 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci target = find_mem_target(cache->memory_PD); 37662306a36Sopenharmony_ci if (!target) 37762306a36Sopenharmony_ci return 0; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci tcache = kzalloc(sizeof(*tcache), GFP_KERNEL); 38062306a36Sopenharmony_ci if (!tcache) { 38162306a36Sopenharmony_ci pr_notice_once("Failed to allocate HMAT cache info\n"); 38262306a36Sopenharmony_ci return 0; 38362306a36Sopenharmony_ci } 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci tcache->cache_attrs.size = cache->cache_size; 38662306a36Sopenharmony_ci tcache->cache_attrs.level = (attrs & ACPI_HMAT_CACHE_LEVEL) >> 4; 38762306a36Sopenharmony_ci tcache->cache_attrs.line_size = (attrs & ACPI_HMAT_CACHE_LINE_SIZE) >> 16; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci switch ((attrs & ACPI_HMAT_CACHE_ASSOCIATIVITY) >> 8) { 39062306a36Sopenharmony_ci case ACPI_HMAT_CA_DIRECT_MAPPED: 39162306a36Sopenharmony_ci tcache->cache_attrs.indexing = NODE_CACHE_DIRECT_MAP; 39262306a36Sopenharmony_ci break; 39362306a36Sopenharmony_ci case ACPI_HMAT_CA_COMPLEX_CACHE_INDEXING: 39462306a36Sopenharmony_ci tcache->cache_attrs.indexing = NODE_CACHE_INDEXED; 39562306a36Sopenharmony_ci break; 39662306a36Sopenharmony_ci case ACPI_HMAT_CA_NONE: 39762306a36Sopenharmony_ci default: 39862306a36Sopenharmony_ci tcache->cache_attrs.indexing = NODE_CACHE_OTHER; 39962306a36Sopenharmony_ci break; 40062306a36Sopenharmony_ci } 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci switch ((attrs & ACPI_HMAT_WRITE_POLICY) >> 12) { 40362306a36Sopenharmony_ci case ACPI_HMAT_CP_WB: 40462306a36Sopenharmony_ci tcache->cache_attrs.write_policy = NODE_CACHE_WRITE_BACK; 40562306a36Sopenharmony_ci break; 40662306a36Sopenharmony_ci case ACPI_HMAT_CP_WT: 40762306a36Sopenharmony_ci tcache->cache_attrs.write_policy = NODE_CACHE_WRITE_THROUGH; 40862306a36Sopenharmony_ci break; 40962306a36Sopenharmony_ci case ACPI_HMAT_CP_NONE: 41062306a36Sopenharmony_ci default: 41162306a36Sopenharmony_ci tcache->cache_attrs.write_policy = NODE_CACHE_WRITE_OTHER; 41262306a36Sopenharmony_ci break; 41362306a36Sopenharmony_ci } 41462306a36Sopenharmony_ci list_add_tail(&tcache->node, &target->caches); 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci return 0; 41762306a36Sopenharmony_ci} 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_cistatic int __init hmat_parse_proximity_domain(union acpi_subtable_headers *header, 42062306a36Sopenharmony_ci const unsigned long end) 42162306a36Sopenharmony_ci{ 42262306a36Sopenharmony_ci struct acpi_hmat_proximity_domain *p = (void *)header; 42362306a36Sopenharmony_ci struct memory_target *target = NULL; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci if (p->header.length != sizeof(*p)) { 42662306a36Sopenharmony_ci pr_notice("Unexpected address range header length: %u\n", 42762306a36Sopenharmony_ci p->header.length); 42862306a36Sopenharmony_ci return -EINVAL; 42962306a36Sopenharmony_ci } 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci if (hmat_revision == 1) 43262306a36Sopenharmony_ci pr_info("Memory (%#llx length %#llx) Flags:%04x Processor Domain:%u Memory Domain:%u\n", 43362306a36Sopenharmony_ci p->reserved3, p->reserved4, p->flags, p->processor_PD, 43462306a36Sopenharmony_ci p->memory_PD); 43562306a36Sopenharmony_ci else 43662306a36Sopenharmony_ci pr_info("Memory Flags:%04x Processor Domain:%u Memory Domain:%u\n", 43762306a36Sopenharmony_ci p->flags, p->processor_PD, p->memory_PD); 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci if ((hmat_revision == 1 && p->flags & ACPI_HMAT_MEMORY_PD_VALID) || 44062306a36Sopenharmony_ci hmat_revision > 1) { 44162306a36Sopenharmony_ci target = find_mem_target(p->memory_PD); 44262306a36Sopenharmony_ci if (!target) { 44362306a36Sopenharmony_ci pr_debug("Memory Domain missing from SRAT\n"); 44462306a36Sopenharmony_ci return -EINVAL; 44562306a36Sopenharmony_ci } 44662306a36Sopenharmony_ci } 44762306a36Sopenharmony_ci if (target && p->flags & ACPI_HMAT_PROCESSOR_PD_VALID) { 44862306a36Sopenharmony_ci int p_node = pxm_to_node(p->processor_PD); 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci if (p_node == NUMA_NO_NODE) { 45162306a36Sopenharmony_ci pr_debug("Invalid Processor Domain\n"); 45262306a36Sopenharmony_ci return -EINVAL; 45362306a36Sopenharmony_ci } 45462306a36Sopenharmony_ci target->processor_pxm = p->processor_PD; 45562306a36Sopenharmony_ci } 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci return 0; 45862306a36Sopenharmony_ci} 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_cistatic int __init hmat_parse_subtable(union acpi_subtable_headers *header, 46162306a36Sopenharmony_ci const unsigned long end) 46262306a36Sopenharmony_ci{ 46362306a36Sopenharmony_ci struct acpi_hmat_structure *hdr = (void *)header; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci if (!hdr) 46662306a36Sopenharmony_ci return -EINVAL; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci switch (hdr->type) { 46962306a36Sopenharmony_ci case ACPI_HMAT_TYPE_PROXIMITY: 47062306a36Sopenharmony_ci return hmat_parse_proximity_domain(header, end); 47162306a36Sopenharmony_ci case ACPI_HMAT_TYPE_LOCALITY: 47262306a36Sopenharmony_ci return hmat_parse_locality(header, end); 47362306a36Sopenharmony_ci case ACPI_HMAT_TYPE_CACHE: 47462306a36Sopenharmony_ci return hmat_parse_cache(header, end); 47562306a36Sopenharmony_ci default: 47662306a36Sopenharmony_ci return -EINVAL; 47762306a36Sopenharmony_ci } 47862306a36Sopenharmony_ci} 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_cistatic __init int srat_parse_mem_affinity(union acpi_subtable_headers *header, 48162306a36Sopenharmony_ci const unsigned long end) 48262306a36Sopenharmony_ci{ 48362306a36Sopenharmony_ci struct acpi_srat_mem_affinity *ma = (void *)header; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci if (!ma) 48662306a36Sopenharmony_ci return -EINVAL; 48762306a36Sopenharmony_ci if (!(ma->flags & ACPI_SRAT_MEM_ENABLED)) 48862306a36Sopenharmony_ci return 0; 48962306a36Sopenharmony_ci alloc_memory_target(ma->proximity_domain, ma->base_address, ma->length); 49062306a36Sopenharmony_ci return 0; 49162306a36Sopenharmony_ci} 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_cistatic u32 hmat_initiator_perf(struct memory_target *target, 49462306a36Sopenharmony_ci struct memory_initiator *initiator, 49562306a36Sopenharmony_ci struct acpi_hmat_locality *hmat_loc) 49662306a36Sopenharmony_ci{ 49762306a36Sopenharmony_ci unsigned int ipds, tpds, i, idx = 0, tdx = 0; 49862306a36Sopenharmony_ci u32 *inits, *targs; 49962306a36Sopenharmony_ci u16 *entries; 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci ipds = hmat_loc->number_of_initiator_Pds; 50262306a36Sopenharmony_ci tpds = hmat_loc->number_of_target_Pds; 50362306a36Sopenharmony_ci inits = (u32 *)(hmat_loc + 1); 50462306a36Sopenharmony_ci targs = inits + ipds; 50562306a36Sopenharmony_ci entries = (u16 *)(targs + tpds); 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci for (i = 0; i < ipds; i++) { 50862306a36Sopenharmony_ci if (inits[i] == initiator->processor_pxm) { 50962306a36Sopenharmony_ci idx = i; 51062306a36Sopenharmony_ci break; 51162306a36Sopenharmony_ci } 51262306a36Sopenharmony_ci } 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci if (i == ipds) 51562306a36Sopenharmony_ci return 0; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci for (i = 0; i < tpds; i++) { 51862306a36Sopenharmony_ci if (targs[i] == target->memory_pxm) { 51962306a36Sopenharmony_ci tdx = i; 52062306a36Sopenharmony_ci break; 52162306a36Sopenharmony_ci } 52262306a36Sopenharmony_ci } 52362306a36Sopenharmony_ci if (i == tpds) 52462306a36Sopenharmony_ci return 0; 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci return hmat_normalize(entries[idx * tpds + tdx], 52762306a36Sopenharmony_ci hmat_loc->entry_base_unit, 52862306a36Sopenharmony_ci hmat_loc->data_type); 52962306a36Sopenharmony_ci} 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_cistatic bool hmat_update_best(u8 type, u32 value, u32 *best) 53262306a36Sopenharmony_ci{ 53362306a36Sopenharmony_ci bool updated = false; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci if (!value) 53662306a36Sopenharmony_ci return false; 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci switch (type) { 53962306a36Sopenharmony_ci case ACPI_HMAT_ACCESS_LATENCY: 54062306a36Sopenharmony_ci case ACPI_HMAT_READ_LATENCY: 54162306a36Sopenharmony_ci case ACPI_HMAT_WRITE_LATENCY: 54262306a36Sopenharmony_ci if (!*best || *best > value) { 54362306a36Sopenharmony_ci *best = value; 54462306a36Sopenharmony_ci updated = true; 54562306a36Sopenharmony_ci } 54662306a36Sopenharmony_ci break; 54762306a36Sopenharmony_ci case ACPI_HMAT_ACCESS_BANDWIDTH: 54862306a36Sopenharmony_ci case ACPI_HMAT_READ_BANDWIDTH: 54962306a36Sopenharmony_ci case ACPI_HMAT_WRITE_BANDWIDTH: 55062306a36Sopenharmony_ci if (!*best || *best < value) { 55162306a36Sopenharmony_ci *best = value; 55262306a36Sopenharmony_ci updated = true; 55362306a36Sopenharmony_ci } 55462306a36Sopenharmony_ci break; 55562306a36Sopenharmony_ci } 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci return updated; 55862306a36Sopenharmony_ci} 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_cistatic int initiator_cmp(void *priv, const struct list_head *a, 56162306a36Sopenharmony_ci const struct list_head *b) 56262306a36Sopenharmony_ci{ 56362306a36Sopenharmony_ci struct memory_initiator *ia; 56462306a36Sopenharmony_ci struct memory_initiator *ib; 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci ia = list_entry(a, struct memory_initiator, node); 56762306a36Sopenharmony_ci ib = list_entry(b, struct memory_initiator, node); 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci return ia->processor_pxm - ib->processor_pxm; 57062306a36Sopenharmony_ci} 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_cistatic int initiators_to_nodemask(unsigned long *p_nodes) 57362306a36Sopenharmony_ci{ 57462306a36Sopenharmony_ci struct memory_initiator *initiator; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci if (list_empty(&initiators)) 57762306a36Sopenharmony_ci return -ENXIO; 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci list_for_each_entry(initiator, &initiators, node) 58062306a36Sopenharmony_ci set_bit(initiator->processor_pxm, p_nodes); 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci return 0; 58362306a36Sopenharmony_ci} 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_cistatic void hmat_register_target_initiators(struct memory_target *target) 58662306a36Sopenharmony_ci{ 58762306a36Sopenharmony_ci static DECLARE_BITMAP(p_nodes, MAX_NUMNODES); 58862306a36Sopenharmony_ci struct memory_initiator *initiator; 58962306a36Sopenharmony_ci unsigned int mem_nid, cpu_nid; 59062306a36Sopenharmony_ci struct memory_locality *loc = NULL; 59162306a36Sopenharmony_ci u32 best = 0; 59262306a36Sopenharmony_ci bool access0done = false; 59362306a36Sopenharmony_ci int i; 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci mem_nid = pxm_to_node(target->memory_pxm); 59662306a36Sopenharmony_ci /* 59762306a36Sopenharmony_ci * If the Address Range Structure provides a local processor pxm, link 59862306a36Sopenharmony_ci * only that one. Otherwise, find the best performance attributes and 59962306a36Sopenharmony_ci * register all initiators that match. 60062306a36Sopenharmony_ci */ 60162306a36Sopenharmony_ci if (target->processor_pxm != PXM_INVAL) { 60262306a36Sopenharmony_ci cpu_nid = pxm_to_node(target->processor_pxm); 60362306a36Sopenharmony_ci register_memory_node_under_compute_node(mem_nid, cpu_nid, 0); 60462306a36Sopenharmony_ci access0done = true; 60562306a36Sopenharmony_ci if (node_state(cpu_nid, N_CPU)) { 60662306a36Sopenharmony_ci register_memory_node_under_compute_node(mem_nid, cpu_nid, 1); 60762306a36Sopenharmony_ci return; 60862306a36Sopenharmony_ci } 60962306a36Sopenharmony_ci } 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci if (list_empty(&localities)) 61262306a36Sopenharmony_ci return; 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci /* 61562306a36Sopenharmony_ci * We need the initiator list sorted so we can use bitmap_clear for 61662306a36Sopenharmony_ci * previously set initiators when we find a better memory accessor. 61762306a36Sopenharmony_ci * We'll also use the sorting to prime the candidate nodes with known 61862306a36Sopenharmony_ci * initiators. 61962306a36Sopenharmony_ci */ 62062306a36Sopenharmony_ci bitmap_zero(p_nodes, MAX_NUMNODES); 62162306a36Sopenharmony_ci list_sort(NULL, &initiators, initiator_cmp); 62262306a36Sopenharmony_ci if (initiators_to_nodemask(p_nodes) < 0) 62362306a36Sopenharmony_ci return; 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci if (!access0done) { 62662306a36Sopenharmony_ci for (i = WRITE_LATENCY; i <= READ_BANDWIDTH; i++) { 62762306a36Sopenharmony_ci loc = localities_types[i]; 62862306a36Sopenharmony_ci if (!loc) 62962306a36Sopenharmony_ci continue; 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci best = 0; 63262306a36Sopenharmony_ci list_for_each_entry(initiator, &initiators, node) { 63362306a36Sopenharmony_ci u32 value; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci if (!test_bit(initiator->processor_pxm, p_nodes)) 63662306a36Sopenharmony_ci continue; 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci value = hmat_initiator_perf(target, initiator, 63962306a36Sopenharmony_ci loc->hmat_loc); 64062306a36Sopenharmony_ci if (hmat_update_best(loc->hmat_loc->data_type, value, &best)) 64162306a36Sopenharmony_ci bitmap_clear(p_nodes, 0, initiator->processor_pxm); 64262306a36Sopenharmony_ci if (value != best) 64362306a36Sopenharmony_ci clear_bit(initiator->processor_pxm, p_nodes); 64462306a36Sopenharmony_ci } 64562306a36Sopenharmony_ci if (best) 64662306a36Sopenharmony_ci hmat_update_target_access(target, loc->hmat_loc->data_type, 64762306a36Sopenharmony_ci best, 0); 64862306a36Sopenharmony_ci } 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci for_each_set_bit(i, p_nodes, MAX_NUMNODES) { 65162306a36Sopenharmony_ci cpu_nid = pxm_to_node(i); 65262306a36Sopenharmony_ci register_memory_node_under_compute_node(mem_nid, cpu_nid, 0); 65362306a36Sopenharmony_ci } 65462306a36Sopenharmony_ci } 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci /* Access 1 ignores Generic Initiators */ 65762306a36Sopenharmony_ci bitmap_zero(p_nodes, MAX_NUMNODES); 65862306a36Sopenharmony_ci if (initiators_to_nodemask(p_nodes) < 0) 65962306a36Sopenharmony_ci return; 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci for (i = WRITE_LATENCY; i <= READ_BANDWIDTH; i++) { 66262306a36Sopenharmony_ci loc = localities_types[i]; 66362306a36Sopenharmony_ci if (!loc) 66462306a36Sopenharmony_ci continue; 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci best = 0; 66762306a36Sopenharmony_ci list_for_each_entry(initiator, &initiators, node) { 66862306a36Sopenharmony_ci u32 value; 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci if (!initiator->has_cpu) { 67162306a36Sopenharmony_ci clear_bit(initiator->processor_pxm, p_nodes); 67262306a36Sopenharmony_ci continue; 67362306a36Sopenharmony_ci } 67462306a36Sopenharmony_ci if (!test_bit(initiator->processor_pxm, p_nodes)) 67562306a36Sopenharmony_ci continue; 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci value = hmat_initiator_perf(target, initiator, loc->hmat_loc); 67862306a36Sopenharmony_ci if (hmat_update_best(loc->hmat_loc->data_type, value, &best)) 67962306a36Sopenharmony_ci bitmap_clear(p_nodes, 0, initiator->processor_pxm); 68062306a36Sopenharmony_ci if (value != best) 68162306a36Sopenharmony_ci clear_bit(initiator->processor_pxm, p_nodes); 68262306a36Sopenharmony_ci } 68362306a36Sopenharmony_ci if (best) 68462306a36Sopenharmony_ci hmat_update_target_access(target, loc->hmat_loc->data_type, best, 1); 68562306a36Sopenharmony_ci } 68662306a36Sopenharmony_ci for_each_set_bit(i, p_nodes, MAX_NUMNODES) { 68762306a36Sopenharmony_ci cpu_nid = pxm_to_node(i); 68862306a36Sopenharmony_ci register_memory_node_under_compute_node(mem_nid, cpu_nid, 1); 68962306a36Sopenharmony_ci } 69062306a36Sopenharmony_ci} 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_cistatic void hmat_register_target_cache(struct memory_target *target) 69362306a36Sopenharmony_ci{ 69462306a36Sopenharmony_ci unsigned mem_nid = pxm_to_node(target->memory_pxm); 69562306a36Sopenharmony_ci struct target_cache *tcache; 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci list_for_each_entry(tcache, &target->caches, node) 69862306a36Sopenharmony_ci node_add_cache(mem_nid, &tcache->cache_attrs); 69962306a36Sopenharmony_ci} 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_cistatic void hmat_register_target_perf(struct memory_target *target, int access) 70262306a36Sopenharmony_ci{ 70362306a36Sopenharmony_ci unsigned mem_nid = pxm_to_node(target->memory_pxm); 70462306a36Sopenharmony_ci node_set_perf_attrs(mem_nid, &target->hmem_attrs[access], access); 70562306a36Sopenharmony_ci} 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_cistatic void hmat_register_target_devices(struct memory_target *target) 70862306a36Sopenharmony_ci{ 70962306a36Sopenharmony_ci struct resource *res; 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci /* 71262306a36Sopenharmony_ci * Do not bother creating devices if no driver is available to 71362306a36Sopenharmony_ci * consume them. 71462306a36Sopenharmony_ci */ 71562306a36Sopenharmony_ci if (!IS_ENABLED(CONFIG_DEV_DAX_HMEM)) 71662306a36Sopenharmony_ci return; 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci for (res = target->memregions.child; res; res = res->sibling) { 71962306a36Sopenharmony_ci int target_nid = pxm_to_node(target->memory_pxm); 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci hmem_register_resource(target_nid, res); 72262306a36Sopenharmony_ci } 72362306a36Sopenharmony_ci} 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_cistatic void hmat_register_target(struct memory_target *target) 72662306a36Sopenharmony_ci{ 72762306a36Sopenharmony_ci int nid = pxm_to_node(target->memory_pxm); 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci /* 73062306a36Sopenharmony_ci * Devices may belong to either an offline or online 73162306a36Sopenharmony_ci * node, so unconditionally add them. 73262306a36Sopenharmony_ci */ 73362306a36Sopenharmony_ci hmat_register_target_devices(target); 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci /* 73662306a36Sopenharmony_ci * Skip offline nodes. This can happen when memory 73762306a36Sopenharmony_ci * marked EFI_MEMORY_SP, "specific purpose", is applied 73862306a36Sopenharmony_ci * to all the memory in a proximity domain leading to 73962306a36Sopenharmony_ci * the node being marked offline / unplugged, or if 74062306a36Sopenharmony_ci * memory-only "hotplug" node is offline. 74162306a36Sopenharmony_ci */ 74262306a36Sopenharmony_ci if (nid == NUMA_NO_NODE || !node_online(nid)) 74362306a36Sopenharmony_ci return; 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci mutex_lock(&target_lock); 74662306a36Sopenharmony_ci if (!target->registered) { 74762306a36Sopenharmony_ci hmat_register_target_initiators(target); 74862306a36Sopenharmony_ci hmat_register_target_cache(target); 74962306a36Sopenharmony_ci hmat_register_target_perf(target, 0); 75062306a36Sopenharmony_ci hmat_register_target_perf(target, 1); 75162306a36Sopenharmony_ci target->registered = true; 75262306a36Sopenharmony_ci } 75362306a36Sopenharmony_ci mutex_unlock(&target_lock); 75462306a36Sopenharmony_ci} 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_cistatic void hmat_register_targets(void) 75762306a36Sopenharmony_ci{ 75862306a36Sopenharmony_ci struct memory_target *target; 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci list_for_each_entry(target, &targets, node) 76162306a36Sopenharmony_ci hmat_register_target(target); 76262306a36Sopenharmony_ci} 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_cistatic int hmat_callback(struct notifier_block *self, 76562306a36Sopenharmony_ci unsigned long action, void *arg) 76662306a36Sopenharmony_ci{ 76762306a36Sopenharmony_ci struct memory_target *target; 76862306a36Sopenharmony_ci struct memory_notify *mnb = arg; 76962306a36Sopenharmony_ci int pxm, nid = mnb->status_change_nid; 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci if (nid == NUMA_NO_NODE || action != MEM_ONLINE) 77262306a36Sopenharmony_ci return NOTIFY_OK; 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci pxm = node_to_pxm(nid); 77562306a36Sopenharmony_ci target = find_mem_target(pxm); 77662306a36Sopenharmony_ci if (!target) 77762306a36Sopenharmony_ci return NOTIFY_OK; 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci hmat_register_target(target); 78062306a36Sopenharmony_ci return NOTIFY_OK; 78162306a36Sopenharmony_ci} 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_cistatic __init void hmat_free_structures(void) 78462306a36Sopenharmony_ci{ 78562306a36Sopenharmony_ci struct memory_target *target, *tnext; 78662306a36Sopenharmony_ci struct memory_locality *loc, *lnext; 78762306a36Sopenharmony_ci struct memory_initiator *initiator, *inext; 78862306a36Sopenharmony_ci struct target_cache *tcache, *cnext; 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci list_for_each_entry_safe(target, tnext, &targets, node) { 79162306a36Sopenharmony_ci struct resource *res, *res_next; 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci list_for_each_entry_safe(tcache, cnext, &target->caches, node) { 79462306a36Sopenharmony_ci list_del(&tcache->node); 79562306a36Sopenharmony_ci kfree(tcache); 79662306a36Sopenharmony_ci } 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci list_del(&target->node); 79962306a36Sopenharmony_ci res = target->memregions.child; 80062306a36Sopenharmony_ci while (res) { 80162306a36Sopenharmony_ci res_next = res->sibling; 80262306a36Sopenharmony_ci __release_region(&target->memregions, res->start, 80362306a36Sopenharmony_ci resource_size(res)); 80462306a36Sopenharmony_ci res = res_next; 80562306a36Sopenharmony_ci } 80662306a36Sopenharmony_ci kfree(target); 80762306a36Sopenharmony_ci } 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci list_for_each_entry_safe(initiator, inext, &initiators, node) { 81062306a36Sopenharmony_ci list_del(&initiator->node); 81162306a36Sopenharmony_ci kfree(initiator); 81262306a36Sopenharmony_ci } 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci list_for_each_entry_safe(loc, lnext, &localities, node) { 81562306a36Sopenharmony_ci list_del(&loc->node); 81662306a36Sopenharmony_ci kfree(loc); 81762306a36Sopenharmony_ci } 81862306a36Sopenharmony_ci} 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_cistatic __init int hmat_init(void) 82162306a36Sopenharmony_ci{ 82262306a36Sopenharmony_ci struct acpi_table_header *tbl; 82362306a36Sopenharmony_ci enum acpi_hmat_type i; 82462306a36Sopenharmony_ci acpi_status status; 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci if (srat_disabled() || hmat_disable) 82762306a36Sopenharmony_ci return 0; 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci status = acpi_get_table(ACPI_SIG_SRAT, 0, &tbl); 83062306a36Sopenharmony_ci if (ACPI_FAILURE(status)) 83162306a36Sopenharmony_ci return 0; 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci if (acpi_table_parse_entries(ACPI_SIG_SRAT, 83462306a36Sopenharmony_ci sizeof(struct acpi_table_srat), 83562306a36Sopenharmony_ci ACPI_SRAT_TYPE_MEMORY_AFFINITY, 83662306a36Sopenharmony_ci srat_parse_mem_affinity, 0) < 0) 83762306a36Sopenharmony_ci goto out_put; 83862306a36Sopenharmony_ci acpi_put_table(tbl); 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci status = acpi_get_table(ACPI_SIG_HMAT, 0, &tbl); 84162306a36Sopenharmony_ci if (ACPI_FAILURE(status)) 84262306a36Sopenharmony_ci goto out_put; 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci hmat_revision = tbl->revision; 84562306a36Sopenharmony_ci switch (hmat_revision) { 84662306a36Sopenharmony_ci case 1: 84762306a36Sopenharmony_ci case 2: 84862306a36Sopenharmony_ci break; 84962306a36Sopenharmony_ci default: 85062306a36Sopenharmony_ci pr_notice("Ignoring: Unknown revision:%d\n", hmat_revision); 85162306a36Sopenharmony_ci goto out_put; 85262306a36Sopenharmony_ci } 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci for (i = ACPI_HMAT_TYPE_PROXIMITY; i < ACPI_HMAT_TYPE_RESERVED; i++) { 85562306a36Sopenharmony_ci if (acpi_table_parse_entries(ACPI_SIG_HMAT, 85662306a36Sopenharmony_ci sizeof(struct acpi_table_hmat), i, 85762306a36Sopenharmony_ci hmat_parse_subtable, 0) < 0) { 85862306a36Sopenharmony_ci pr_notice("Ignoring: Invalid table"); 85962306a36Sopenharmony_ci goto out_put; 86062306a36Sopenharmony_ci } 86162306a36Sopenharmony_ci } 86262306a36Sopenharmony_ci hmat_register_targets(); 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci /* Keep the table and structures if the notifier may use them */ 86562306a36Sopenharmony_ci if (!hotplug_memory_notifier(hmat_callback, HMAT_CALLBACK_PRI)) 86662306a36Sopenharmony_ci return 0; 86762306a36Sopenharmony_ciout_put: 86862306a36Sopenharmony_ci hmat_free_structures(); 86962306a36Sopenharmony_ci acpi_put_table(tbl); 87062306a36Sopenharmony_ci return 0; 87162306a36Sopenharmony_ci} 87262306a36Sopenharmony_cisubsys_initcall(hmat_init); 873