1c710fcc5SDan Williams // SPDX-License-Identifier: GPL-2.0 2c710fcc5SDan Williams /* 3c710fcc5SDan Williams * Copyright (c) 2019, Intel Corporation. 4c710fcc5SDan Williams * 5c710fcc5SDan Williams * Heterogeneous Memory Attributes Table (HMAT) representation 6c710fcc5SDan Williams * 7c710fcc5SDan Williams * This program parses and reports the platform's HMAT tables, and registers 8c710fcc5SDan Williams * the applicable attributes with the node's interfaces. 9c710fcc5SDan Williams */ 10c710fcc5SDan Williams 11cf8741acSDan Williams #define pr_fmt(fmt) "acpi/hmat: " fmt 12cf8741acSDan Williams 13c710fcc5SDan Williams #include <linux/acpi.h> 14c710fcc5SDan Williams #include <linux/bitops.h> 15c710fcc5SDan Williams #include <linux/device.h> 16c710fcc5SDan Williams #include <linux/init.h> 17c710fcc5SDan Williams #include <linux/list.h> 18cf8741acSDan Williams #include <linux/mm.h> 19cf8741acSDan Williams #include <linux/platform_device.h> 20c710fcc5SDan Williams #include <linux/list_sort.h> 21cf8741acSDan Williams #include <linux/memregion.h> 22c710fcc5SDan Williams #include <linux/memory.h> 23c710fcc5SDan Williams #include <linux/mutex.h> 24c710fcc5SDan Williams #include <linux/node.h> 25c710fcc5SDan Williams #include <linux/sysfs.h> 26c01044ccSDan Williams #include <linux/dax.h> 27*3718c02dSHuang Ying #include <linux/memory-tiers.h> 28c710fcc5SDan Williams 29c710fcc5SDan Williams static u8 hmat_revision; 303b0d3101SDan Williams static int hmat_disable __initdata; 313b0d3101SDan Williams 323b0d3101SDan Williams void __init disable_hmat(void) 333b0d3101SDan Williams { 343b0d3101SDan Williams hmat_disable = 1; 353b0d3101SDan Williams } 36c710fcc5SDan Williams 37c710fcc5SDan Williams static LIST_HEAD(targets); 38c710fcc5SDan Williams static LIST_HEAD(initiators); 39c710fcc5SDan Williams static LIST_HEAD(localities); 40c710fcc5SDan Williams 41c710fcc5SDan Williams static DEFINE_MUTEX(target_lock); 42c710fcc5SDan Williams 43c710fcc5SDan Williams /* 44c710fcc5SDan Williams * The defined enum order is used to prioritize attributes to break ties when 45c710fcc5SDan Williams * selecting the best performing node. 46c710fcc5SDan Williams */ 47c710fcc5SDan Williams enum locality_types { 48c710fcc5SDan Williams WRITE_LATENCY, 49c710fcc5SDan Williams READ_LATENCY, 50c710fcc5SDan Williams WRITE_BANDWIDTH, 51c710fcc5SDan Williams READ_BANDWIDTH, 52c710fcc5SDan Williams }; 53c710fcc5SDan Williams 54c710fcc5SDan Williams static struct memory_locality *localities_types[4]; 55c710fcc5SDan Williams 56c710fcc5SDan Williams struct target_cache { 57c710fcc5SDan Williams struct list_head node; 58c710fcc5SDan Williams struct node_cache_attrs cache_attrs; 59c710fcc5SDan Williams }; 60c710fcc5SDan Williams 61c710fcc5SDan Williams struct memory_target { 62c710fcc5SDan Williams struct list_head node; 63c710fcc5SDan Williams unsigned int memory_pxm; 64c710fcc5SDan Williams unsigned int processor_pxm; 65cf8741acSDan Williams struct resource memregions; 66b9fffe47SJonathan Cameron struct node_hmem_attrs hmem_attrs[2]; 67c710fcc5SDan Williams struct list_head caches; 68c710fcc5SDan Williams struct node_cache_attrs cache_attrs; 69c710fcc5SDan Williams bool registered; 70c710fcc5SDan Williams }; 71c710fcc5SDan Williams 72c710fcc5SDan Williams struct memory_initiator { 73c710fcc5SDan Williams struct list_head node; 74c710fcc5SDan Williams unsigned int processor_pxm; 75b9fffe47SJonathan Cameron bool has_cpu; 76c710fcc5SDan Williams }; 77c710fcc5SDan Williams 78c710fcc5SDan Williams struct memory_locality { 79c710fcc5SDan Williams struct list_head node; 80c710fcc5SDan Williams struct acpi_hmat_locality *hmat_loc; 81c710fcc5SDan Williams }; 82c710fcc5SDan Williams 83c710fcc5SDan Williams static struct memory_initiator *find_mem_initiator(unsigned int cpu_pxm) 84c710fcc5SDan Williams { 85c710fcc5SDan Williams struct memory_initiator *initiator; 86c710fcc5SDan Williams 87c710fcc5SDan Williams list_for_each_entry(initiator, &initiators, node) 88c710fcc5SDan Williams if (initiator->processor_pxm == cpu_pxm) 89c710fcc5SDan Williams return initiator; 90c710fcc5SDan Williams return NULL; 91c710fcc5SDan Williams } 92c710fcc5SDan Williams 93c710fcc5SDan Williams static struct memory_target *find_mem_target(unsigned int mem_pxm) 94c710fcc5SDan Williams { 95c710fcc5SDan Williams struct memory_target *target; 96c710fcc5SDan Williams 97c710fcc5SDan Williams list_for_each_entry(target, &targets, node) 98c710fcc5SDan Williams if (target->memory_pxm == mem_pxm) 99c710fcc5SDan Williams return target; 100c710fcc5SDan Williams return NULL; 101c710fcc5SDan Williams } 102c710fcc5SDan Williams 103c710fcc5SDan Williams static __init void alloc_memory_initiator(unsigned int cpu_pxm) 104c710fcc5SDan Williams { 105c710fcc5SDan Williams struct memory_initiator *initiator; 106c710fcc5SDan Williams 107c710fcc5SDan Williams if (pxm_to_node(cpu_pxm) == NUMA_NO_NODE) 108c710fcc5SDan Williams return; 109c710fcc5SDan Williams 110c710fcc5SDan Williams initiator = find_mem_initiator(cpu_pxm); 111c710fcc5SDan Williams if (initiator) 112c710fcc5SDan Williams return; 113c710fcc5SDan Williams 114c710fcc5SDan Williams initiator = kzalloc(sizeof(*initiator), GFP_KERNEL); 115c710fcc5SDan Williams if (!initiator) 116c710fcc5SDan Williams return; 117c710fcc5SDan Williams 118c710fcc5SDan Williams initiator->processor_pxm = cpu_pxm; 119b9fffe47SJonathan Cameron initiator->has_cpu = node_state(pxm_to_node(cpu_pxm), N_CPU); 120c710fcc5SDan Williams list_add_tail(&initiator->node, &initiators); 121c710fcc5SDan Williams } 122c710fcc5SDan Williams 123cf8741acSDan Williams static __init void alloc_memory_target(unsigned int mem_pxm, 124cf8741acSDan Williams resource_size_t start, resource_size_t len) 125c710fcc5SDan Williams { 126c710fcc5SDan Williams struct memory_target *target; 127c710fcc5SDan Williams 128c710fcc5SDan Williams target = find_mem_target(mem_pxm); 129cf8741acSDan Williams if (!target) { 130c710fcc5SDan Williams target = kzalloc(sizeof(*target), GFP_KERNEL); 131c710fcc5SDan Williams if (!target) 132c710fcc5SDan Williams return; 133c710fcc5SDan Williams target->memory_pxm = mem_pxm; 134c710fcc5SDan Williams target->processor_pxm = PXM_INVAL; 135cf8741acSDan Williams target->memregions = (struct resource) { 136cf8741acSDan Williams .name = "ACPI mem", 137cf8741acSDan Williams .start = 0, 138cf8741acSDan Williams .end = -1, 139cf8741acSDan Williams .flags = IORESOURCE_MEM, 140cf8741acSDan Williams }; 141c710fcc5SDan Williams list_add_tail(&target->node, &targets); 142c710fcc5SDan Williams INIT_LIST_HEAD(&target->caches); 143c710fcc5SDan Williams } 144c710fcc5SDan Williams 145cf8741acSDan Williams /* 146cf8741acSDan Williams * There are potentially multiple ranges per PXM, so record each 147cf8741acSDan Williams * in the per-target memregions resource tree. 148cf8741acSDan Williams */ 149cf8741acSDan Williams if (!__request_region(&target->memregions, start, len, "memory target", 150cf8741acSDan Williams IORESOURCE_MEM)) 151cf8741acSDan Williams pr_warn("failed to reserve %#llx - %#llx in pxm: %d\n", 152cf8741acSDan Williams start, start + len, mem_pxm); 153cf8741acSDan Williams } 154cf8741acSDan Williams 155c710fcc5SDan Williams static __init const char *hmat_data_type(u8 type) 156c710fcc5SDan Williams { 157c710fcc5SDan Williams switch (type) { 158c710fcc5SDan Williams case ACPI_HMAT_ACCESS_LATENCY: 159c710fcc5SDan Williams return "Access Latency"; 160c710fcc5SDan Williams case ACPI_HMAT_READ_LATENCY: 161c710fcc5SDan Williams return "Read Latency"; 162c710fcc5SDan Williams case ACPI_HMAT_WRITE_LATENCY: 163c710fcc5SDan Williams return "Write Latency"; 164c710fcc5SDan Williams case ACPI_HMAT_ACCESS_BANDWIDTH: 165c710fcc5SDan Williams return "Access Bandwidth"; 166c710fcc5SDan Williams case ACPI_HMAT_READ_BANDWIDTH: 167c710fcc5SDan Williams return "Read Bandwidth"; 168c710fcc5SDan Williams case ACPI_HMAT_WRITE_BANDWIDTH: 169c710fcc5SDan Williams return "Write Bandwidth"; 170c710fcc5SDan Williams default: 171c710fcc5SDan Williams return "Reserved"; 172c710fcc5SDan Williams } 173c710fcc5SDan Williams } 174c710fcc5SDan Williams 175c710fcc5SDan Williams static __init const char *hmat_data_type_suffix(u8 type) 176c710fcc5SDan Williams { 177c710fcc5SDan Williams switch (type) { 178c710fcc5SDan Williams case ACPI_HMAT_ACCESS_LATENCY: 179c710fcc5SDan Williams case ACPI_HMAT_READ_LATENCY: 180c710fcc5SDan Williams case ACPI_HMAT_WRITE_LATENCY: 181c710fcc5SDan Williams return " nsec"; 182c710fcc5SDan Williams case ACPI_HMAT_ACCESS_BANDWIDTH: 183c710fcc5SDan Williams case ACPI_HMAT_READ_BANDWIDTH: 184c710fcc5SDan Williams case ACPI_HMAT_WRITE_BANDWIDTH: 185c710fcc5SDan Williams return " MB/s"; 186c710fcc5SDan Williams default: 187c710fcc5SDan Williams return ""; 188c710fcc5SDan Williams } 189c710fcc5SDan Williams } 190c710fcc5SDan Williams 191c710fcc5SDan Williams static u32 hmat_normalize(u16 entry, u64 base, u8 type) 192c710fcc5SDan Williams { 193c710fcc5SDan Williams u32 value; 194c710fcc5SDan Williams 195c710fcc5SDan Williams /* 196c710fcc5SDan Williams * Check for invalid and overflow values 197c710fcc5SDan Williams */ 198c710fcc5SDan Williams if (entry == 0xffff || !entry) 199c710fcc5SDan Williams return 0; 200c710fcc5SDan Williams else if (base > (UINT_MAX / (entry))) 201c710fcc5SDan Williams return 0; 202c710fcc5SDan Williams 203c710fcc5SDan Williams /* 204c710fcc5SDan Williams * Divide by the base unit for version 1, convert latency from 205c710fcc5SDan Williams * picosenonds to nanoseconds if revision 2. 206c710fcc5SDan Williams */ 207c710fcc5SDan Williams value = entry * base; 208c710fcc5SDan Williams if (hmat_revision == 1) { 209c710fcc5SDan Williams if (value < 10) 210c710fcc5SDan Williams return 0; 211c710fcc5SDan Williams value = DIV_ROUND_UP(value, 10); 212c710fcc5SDan Williams } else if (hmat_revision == 2) { 213c710fcc5SDan Williams switch (type) { 214c710fcc5SDan Williams case ACPI_HMAT_ACCESS_LATENCY: 215c710fcc5SDan Williams case ACPI_HMAT_READ_LATENCY: 216c710fcc5SDan Williams case ACPI_HMAT_WRITE_LATENCY: 217c710fcc5SDan Williams value = DIV_ROUND_UP(value, 1000); 218c710fcc5SDan Williams break; 219c710fcc5SDan Williams default: 220c710fcc5SDan Williams break; 221c710fcc5SDan Williams } 222c710fcc5SDan Williams } 223c710fcc5SDan Williams return value; 224c710fcc5SDan Williams } 225c710fcc5SDan Williams 226c710fcc5SDan Williams static void hmat_update_target_access(struct memory_target *target, 227b9fffe47SJonathan Cameron u8 type, u32 value, int access) 228c710fcc5SDan Williams { 229c710fcc5SDan Williams switch (type) { 230c710fcc5SDan Williams case ACPI_HMAT_ACCESS_LATENCY: 231b9fffe47SJonathan Cameron target->hmem_attrs[access].read_latency = value; 232b9fffe47SJonathan Cameron target->hmem_attrs[access].write_latency = value; 233c710fcc5SDan Williams break; 234c710fcc5SDan Williams case ACPI_HMAT_READ_LATENCY: 235b9fffe47SJonathan Cameron target->hmem_attrs[access].read_latency = value; 236c710fcc5SDan Williams break; 237c710fcc5SDan Williams case ACPI_HMAT_WRITE_LATENCY: 238b9fffe47SJonathan Cameron target->hmem_attrs[access].write_latency = value; 239c710fcc5SDan Williams break; 240c710fcc5SDan Williams case ACPI_HMAT_ACCESS_BANDWIDTH: 241b9fffe47SJonathan Cameron target->hmem_attrs[access].read_bandwidth = value; 242b9fffe47SJonathan Cameron target->hmem_attrs[access].write_bandwidth = value; 243c710fcc5SDan Williams break; 244c710fcc5SDan Williams case ACPI_HMAT_READ_BANDWIDTH: 245b9fffe47SJonathan Cameron target->hmem_attrs[access].read_bandwidth = value; 246c710fcc5SDan Williams break; 247c710fcc5SDan Williams case ACPI_HMAT_WRITE_BANDWIDTH: 248b9fffe47SJonathan Cameron target->hmem_attrs[access].write_bandwidth = value; 249c710fcc5SDan Williams break; 250c710fcc5SDan Williams default: 251c710fcc5SDan Williams break; 252c710fcc5SDan Williams } 253c710fcc5SDan Williams } 254c710fcc5SDan Williams 255c710fcc5SDan Williams static __init void hmat_add_locality(struct acpi_hmat_locality *hmat_loc) 256c710fcc5SDan Williams { 257c710fcc5SDan Williams struct memory_locality *loc; 258c710fcc5SDan Williams 259c710fcc5SDan Williams loc = kzalloc(sizeof(*loc), GFP_KERNEL); 260c710fcc5SDan Williams if (!loc) { 261c710fcc5SDan Williams pr_notice_once("Failed to allocate HMAT locality\n"); 262c710fcc5SDan Williams return; 263c710fcc5SDan Williams } 264c710fcc5SDan Williams 265c710fcc5SDan Williams loc->hmat_loc = hmat_loc; 266c710fcc5SDan Williams list_add_tail(&loc->node, &localities); 267c710fcc5SDan Williams 268c710fcc5SDan Williams switch (hmat_loc->data_type) { 269c710fcc5SDan Williams case ACPI_HMAT_ACCESS_LATENCY: 270c710fcc5SDan Williams localities_types[READ_LATENCY] = loc; 271c710fcc5SDan Williams localities_types[WRITE_LATENCY] = loc; 272c710fcc5SDan Williams break; 273c710fcc5SDan Williams case ACPI_HMAT_READ_LATENCY: 274c710fcc5SDan Williams localities_types[READ_LATENCY] = loc; 275c710fcc5SDan Williams break; 276c710fcc5SDan Williams case ACPI_HMAT_WRITE_LATENCY: 277c710fcc5SDan Williams localities_types[WRITE_LATENCY] = loc; 278c710fcc5SDan Williams break; 279c710fcc5SDan Williams case ACPI_HMAT_ACCESS_BANDWIDTH: 280c710fcc5SDan Williams localities_types[READ_BANDWIDTH] = loc; 281c710fcc5SDan Williams localities_types[WRITE_BANDWIDTH] = loc; 282c710fcc5SDan Williams break; 283c710fcc5SDan Williams case ACPI_HMAT_READ_BANDWIDTH: 284c710fcc5SDan Williams localities_types[READ_BANDWIDTH] = loc; 285c710fcc5SDan Williams break; 286c710fcc5SDan Williams case ACPI_HMAT_WRITE_BANDWIDTH: 287c710fcc5SDan Williams localities_types[WRITE_BANDWIDTH] = loc; 288c710fcc5SDan Williams break; 289c710fcc5SDan Williams default: 290c710fcc5SDan Williams break; 291c710fcc5SDan Williams } 292c710fcc5SDan Williams } 293c710fcc5SDan Williams 294c710fcc5SDan Williams static __init int hmat_parse_locality(union acpi_subtable_headers *header, 295c710fcc5SDan Williams const unsigned long end) 296c710fcc5SDan Williams { 297c710fcc5SDan Williams struct acpi_hmat_locality *hmat_loc = (void *)header; 298c710fcc5SDan Williams struct memory_target *target; 299c710fcc5SDan Williams unsigned int init, targ, total_size, ipds, tpds; 300c710fcc5SDan Williams u32 *inits, *targs, value; 301c710fcc5SDan Williams u16 *entries; 302c710fcc5SDan Williams u8 type, mem_hier; 303c710fcc5SDan Williams 304c710fcc5SDan Williams if (hmat_loc->header.length < sizeof(*hmat_loc)) { 30556216359SLiu Shixin pr_notice("Unexpected locality header length: %u\n", 306c710fcc5SDan Williams hmat_loc->header.length); 307c710fcc5SDan Williams return -EINVAL; 308c710fcc5SDan Williams } 309c710fcc5SDan Williams 310c710fcc5SDan Williams type = hmat_loc->data_type; 311c710fcc5SDan Williams mem_hier = hmat_loc->flags & ACPI_HMAT_MEMORY_HIERARCHY; 312c710fcc5SDan Williams ipds = hmat_loc->number_of_initiator_Pds; 313c710fcc5SDan Williams tpds = hmat_loc->number_of_target_Pds; 314c710fcc5SDan Williams total_size = sizeof(*hmat_loc) + sizeof(*entries) * ipds * tpds + 315c710fcc5SDan Williams sizeof(*inits) * ipds + sizeof(*targs) * tpds; 316c710fcc5SDan Williams if (hmat_loc->header.length < total_size) { 31756216359SLiu Shixin pr_notice("Unexpected locality header length:%u, minimum required:%u\n", 318c710fcc5SDan Williams hmat_loc->header.length, total_size); 319c710fcc5SDan Williams return -EINVAL; 320c710fcc5SDan Williams } 321c710fcc5SDan Williams 32256216359SLiu Shixin pr_info("Locality: Flags:%02x Type:%s Initiator Domains:%u Target Domains:%u Base:%lld\n", 323c710fcc5SDan Williams hmat_loc->flags, hmat_data_type(type), ipds, tpds, 324c710fcc5SDan Williams hmat_loc->entry_base_unit); 325c710fcc5SDan Williams 326c710fcc5SDan Williams inits = (u32 *)(hmat_loc + 1); 327c710fcc5SDan Williams targs = inits + ipds; 328c710fcc5SDan Williams entries = (u16 *)(targs + tpds); 329c710fcc5SDan Williams for (init = 0; init < ipds; init++) { 330c710fcc5SDan Williams alloc_memory_initiator(inits[init]); 331c710fcc5SDan Williams for (targ = 0; targ < tpds; targ++) { 332c710fcc5SDan Williams value = hmat_normalize(entries[init * tpds + targ], 333c710fcc5SDan Williams hmat_loc->entry_base_unit, 334c710fcc5SDan Williams type); 3350f1839d0STao Xu pr_info(" Initiator-Target[%u-%u]:%u%s\n", 336c710fcc5SDan Williams inits[init], targs[targ], value, 337c710fcc5SDan Williams hmat_data_type_suffix(type)); 338c710fcc5SDan Williams 339c710fcc5SDan Williams if (mem_hier == ACPI_HMAT_MEMORY) { 340c710fcc5SDan Williams target = find_mem_target(targs[targ]); 341b9fffe47SJonathan Cameron if (target && target->processor_pxm == inits[init]) { 342b9fffe47SJonathan Cameron hmat_update_target_access(target, type, value, 0); 343b9fffe47SJonathan Cameron /* If the node has a CPU, update access 1 */ 344b9fffe47SJonathan Cameron if (node_state(pxm_to_node(inits[init]), N_CPU)) 345b9fffe47SJonathan Cameron hmat_update_target_access(target, type, value, 1); 346b9fffe47SJonathan Cameron } 347c710fcc5SDan Williams } 348c710fcc5SDan Williams } 349c710fcc5SDan Williams } 350c710fcc5SDan Williams 351c710fcc5SDan Williams if (mem_hier == ACPI_HMAT_MEMORY) 352c710fcc5SDan Williams hmat_add_locality(hmat_loc); 353c710fcc5SDan Williams 354c710fcc5SDan Williams return 0; 355c710fcc5SDan Williams } 356c710fcc5SDan Williams 357c710fcc5SDan Williams static __init int hmat_parse_cache(union acpi_subtable_headers *header, 358c710fcc5SDan Williams const unsigned long end) 359c710fcc5SDan Williams { 360c710fcc5SDan Williams struct acpi_hmat_cache *cache = (void *)header; 361c710fcc5SDan Williams struct memory_target *target; 362c710fcc5SDan Williams struct target_cache *tcache; 363c710fcc5SDan Williams u32 attrs; 364c710fcc5SDan Williams 365c710fcc5SDan Williams if (cache->header.length < sizeof(*cache)) { 36656216359SLiu Shixin pr_notice("Unexpected cache header length: %u\n", 367c710fcc5SDan Williams cache->header.length); 368c710fcc5SDan Williams return -EINVAL; 369c710fcc5SDan Williams } 370c710fcc5SDan Williams 371c710fcc5SDan Williams attrs = cache->cache_attributes; 37256216359SLiu Shixin pr_info("Cache: Domain:%u Size:%llu Attrs:%08x SMBIOS Handles:%d\n", 373c710fcc5SDan Williams cache->memory_PD, cache->cache_size, attrs, 374c710fcc5SDan Williams cache->number_of_SMBIOShandles); 375c710fcc5SDan Williams 376c710fcc5SDan Williams target = find_mem_target(cache->memory_PD); 377c710fcc5SDan Williams if (!target) 378c710fcc5SDan Williams return 0; 379c710fcc5SDan Williams 380c710fcc5SDan Williams tcache = kzalloc(sizeof(*tcache), GFP_KERNEL); 381c710fcc5SDan Williams if (!tcache) { 382c710fcc5SDan Williams pr_notice_once("Failed to allocate HMAT cache info\n"); 383c710fcc5SDan Williams return 0; 384c710fcc5SDan Williams } 385c710fcc5SDan Williams 386c710fcc5SDan Williams tcache->cache_attrs.size = cache->cache_size; 387c710fcc5SDan Williams tcache->cache_attrs.level = (attrs & ACPI_HMAT_CACHE_LEVEL) >> 4; 388c710fcc5SDan Williams tcache->cache_attrs.line_size = (attrs & ACPI_HMAT_CACHE_LINE_SIZE) >> 16; 389c710fcc5SDan Williams 390c710fcc5SDan Williams switch ((attrs & ACPI_HMAT_CACHE_ASSOCIATIVITY) >> 8) { 391c710fcc5SDan Williams case ACPI_HMAT_CA_DIRECT_MAPPED: 392c710fcc5SDan Williams tcache->cache_attrs.indexing = NODE_CACHE_DIRECT_MAP; 393c710fcc5SDan Williams break; 394c710fcc5SDan Williams case ACPI_HMAT_CA_COMPLEX_CACHE_INDEXING: 395c710fcc5SDan Williams tcache->cache_attrs.indexing = NODE_CACHE_INDEXED; 396c710fcc5SDan Williams break; 397c710fcc5SDan Williams case ACPI_HMAT_CA_NONE: 398c710fcc5SDan Williams default: 399c710fcc5SDan Williams tcache->cache_attrs.indexing = NODE_CACHE_OTHER; 400c710fcc5SDan Williams break; 401c710fcc5SDan Williams } 402c710fcc5SDan Williams 403c710fcc5SDan Williams switch ((attrs & ACPI_HMAT_WRITE_POLICY) >> 12) { 404c710fcc5SDan Williams case ACPI_HMAT_CP_WB: 405c710fcc5SDan Williams tcache->cache_attrs.write_policy = NODE_CACHE_WRITE_BACK; 406c710fcc5SDan Williams break; 407c710fcc5SDan Williams case ACPI_HMAT_CP_WT: 408c710fcc5SDan Williams tcache->cache_attrs.write_policy = NODE_CACHE_WRITE_THROUGH; 409c710fcc5SDan Williams break; 410c710fcc5SDan Williams case ACPI_HMAT_CP_NONE: 411c710fcc5SDan Williams default: 412c710fcc5SDan Williams tcache->cache_attrs.write_policy = NODE_CACHE_WRITE_OTHER; 413c710fcc5SDan Williams break; 414c710fcc5SDan Williams } 415c710fcc5SDan Williams list_add_tail(&tcache->node, &target->caches); 416c710fcc5SDan Williams 417c710fcc5SDan Williams return 0; 418c710fcc5SDan Williams } 419c710fcc5SDan Williams 420c710fcc5SDan Williams static int __init hmat_parse_proximity_domain(union acpi_subtable_headers *header, 421c710fcc5SDan Williams const unsigned long end) 422c710fcc5SDan Williams { 423c710fcc5SDan Williams struct acpi_hmat_proximity_domain *p = (void *)header; 424c710fcc5SDan Williams struct memory_target *target = NULL; 425c710fcc5SDan Williams 426c710fcc5SDan Williams if (p->header.length != sizeof(*p)) { 42756216359SLiu Shixin pr_notice("Unexpected address range header length: %u\n", 428c710fcc5SDan Williams p->header.length); 429c710fcc5SDan Williams return -EINVAL; 430c710fcc5SDan Williams } 431c710fcc5SDan Williams 432c710fcc5SDan Williams if (hmat_revision == 1) 43356216359SLiu Shixin pr_info("Memory (%#llx length %#llx) Flags:%04x Processor Domain:%u Memory Domain:%u\n", 434c710fcc5SDan Williams p->reserved3, p->reserved4, p->flags, p->processor_PD, 435c710fcc5SDan Williams p->memory_PD); 436c710fcc5SDan Williams else 43756216359SLiu Shixin pr_info("Memory Flags:%04x Processor Domain:%u Memory Domain:%u\n", 438c710fcc5SDan Williams p->flags, p->processor_PD, p->memory_PD); 439c710fcc5SDan Williams 4402c5b9bdeSJonathan Cameron if ((hmat_revision == 1 && p->flags & ACPI_HMAT_MEMORY_PD_VALID) || 4412c5b9bdeSJonathan Cameron hmat_revision > 1) { 442c710fcc5SDan Williams target = find_mem_target(p->memory_PD); 443c710fcc5SDan Williams if (!target) { 44456216359SLiu Shixin pr_debug("Memory Domain missing from SRAT\n"); 445c710fcc5SDan Williams return -EINVAL; 446c710fcc5SDan Williams } 447c710fcc5SDan Williams } 448c710fcc5SDan Williams if (target && p->flags & ACPI_HMAT_PROCESSOR_PD_VALID) { 449c710fcc5SDan Williams int p_node = pxm_to_node(p->processor_PD); 450c710fcc5SDan Williams 451c710fcc5SDan Williams if (p_node == NUMA_NO_NODE) { 45256216359SLiu Shixin pr_debug("Invalid Processor Domain\n"); 453c710fcc5SDan Williams return -EINVAL; 454c710fcc5SDan Williams } 4554caa525bSBrice Goglin target->processor_pxm = p->processor_PD; 456c710fcc5SDan Williams } 457c710fcc5SDan Williams 458c710fcc5SDan Williams return 0; 459c710fcc5SDan Williams } 460c710fcc5SDan Williams 461c710fcc5SDan Williams static int __init hmat_parse_subtable(union acpi_subtable_headers *header, 462c710fcc5SDan Williams const unsigned long end) 463c710fcc5SDan Williams { 464c710fcc5SDan Williams struct acpi_hmat_structure *hdr = (void *)header; 465c710fcc5SDan Williams 466c710fcc5SDan Williams if (!hdr) 467c710fcc5SDan Williams return -EINVAL; 468c710fcc5SDan Williams 469c710fcc5SDan Williams switch (hdr->type) { 470c710fcc5SDan Williams case ACPI_HMAT_TYPE_PROXIMITY: 471c710fcc5SDan Williams return hmat_parse_proximity_domain(header, end); 472c710fcc5SDan Williams case ACPI_HMAT_TYPE_LOCALITY: 473c710fcc5SDan Williams return hmat_parse_locality(header, end); 474c710fcc5SDan Williams case ACPI_HMAT_TYPE_CACHE: 475c710fcc5SDan Williams return hmat_parse_cache(header, end); 476c710fcc5SDan Williams default: 477c710fcc5SDan Williams return -EINVAL; 478c710fcc5SDan Williams } 479c710fcc5SDan Williams } 480c710fcc5SDan Williams 481c710fcc5SDan Williams static __init int srat_parse_mem_affinity(union acpi_subtable_headers *header, 482c710fcc5SDan Williams const unsigned long end) 483c710fcc5SDan Williams { 484c710fcc5SDan Williams struct acpi_srat_mem_affinity *ma = (void *)header; 485c710fcc5SDan Williams 486c710fcc5SDan Williams if (!ma) 487c710fcc5SDan Williams return -EINVAL; 488c710fcc5SDan Williams if (!(ma->flags & ACPI_SRAT_MEM_ENABLED)) 489c710fcc5SDan Williams return 0; 490cf8741acSDan Williams alloc_memory_target(ma->proximity_domain, ma->base_address, ma->length); 491c710fcc5SDan Williams return 0; 492c710fcc5SDan Williams } 493c710fcc5SDan Williams 494c710fcc5SDan Williams static u32 hmat_initiator_perf(struct memory_target *target, 495c710fcc5SDan Williams struct memory_initiator *initiator, 496c710fcc5SDan Williams struct acpi_hmat_locality *hmat_loc) 497c710fcc5SDan Williams { 498c710fcc5SDan Williams unsigned int ipds, tpds, i, idx = 0, tdx = 0; 499c710fcc5SDan Williams u32 *inits, *targs; 500c710fcc5SDan Williams u16 *entries; 501c710fcc5SDan Williams 502c710fcc5SDan Williams ipds = hmat_loc->number_of_initiator_Pds; 503c710fcc5SDan Williams tpds = hmat_loc->number_of_target_Pds; 504c710fcc5SDan Williams inits = (u32 *)(hmat_loc + 1); 505c710fcc5SDan Williams targs = inits + ipds; 506c710fcc5SDan Williams entries = (u16 *)(targs + tpds); 507c710fcc5SDan Williams 508c710fcc5SDan Williams for (i = 0; i < ipds; i++) { 509c710fcc5SDan Williams if (inits[i] == initiator->processor_pxm) { 510c710fcc5SDan Williams idx = i; 511c710fcc5SDan Williams break; 512c710fcc5SDan Williams } 513c710fcc5SDan Williams } 514c710fcc5SDan Williams 515c710fcc5SDan Williams if (i == ipds) 516c710fcc5SDan Williams return 0; 517c710fcc5SDan Williams 518c710fcc5SDan Williams for (i = 0; i < tpds; i++) { 519c710fcc5SDan Williams if (targs[i] == target->memory_pxm) { 520c710fcc5SDan Williams tdx = i; 521c710fcc5SDan Williams break; 522c710fcc5SDan Williams } 523c710fcc5SDan Williams } 524c710fcc5SDan Williams if (i == tpds) 525c710fcc5SDan Williams return 0; 526c710fcc5SDan Williams 527c710fcc5SDan Williams return hmat_normalize(entries[idx * tpds + tdx], 528c710fcc5SDan Williams hmat_loc->entry_base_unit, 529c710fcc5SDan Williams hmat_loc->data_type); 530c710fcc5SDan Williams } 531c710fcc5SDan Williams 532c710fcc5SDan Williams static bool hmat_update_best(u8 type, u32 value, u32 *best) 533c710fcc5SDan Williams { 534c710fcc5SDan Williams bool updated = false; 535c710fcc5SDan Williams 536c710fcc5SDan Williams if (!value) 537c710fcc5SDan Williams return false; 538c710fcc5SDan Williams 539c710fcc5SDan Williams switch (type) { 540c710fcc5SDan Williams case ACPI_HMAT_ACCESS_LATENCY: 541c710fcc5SDan Williams case ACPI_HMAT_READ_LATENCY: 542c710fcc5SDan Williams case ACPI_HMAT_WRITE_LATENCY: 543c710fcc5SDan Williams if (!*best || *best > value) { 544c710fcc5SDan Williams *best = value; 545c710fcc5SDan Williams updated = true; 546c710fcc5SDan Williams } 547c710fcc5SDan Williams break; 548c710fcc5SDan Williams case ACPI_HMAT_ACCESS_BANDWIDTH: 549c710fcc5SDan Williams case ACPI_HMAT_READ_BANDWIDTH: 550c710fcc5SDan Williams case ACPI_HMAT_WRITE_BANDWIDTH: 551c710fcc5SDan Williams if (!*best || *best < value) { 552c710fcc5SDan Williams *best = value; 553c710fcc5SDan Williams updated = true; 554c710fcc5SDan Williams } 555c710fcc5SDan Williams break; 556c710fcc5SDan Williams } 557c710fcc5SDan Williams 558c710fcc5SDan Williams return updated; 559c710fcc5SDan Williams } 560c710fcc5SDan Williams 5614f0f586bSSami Tolvanen static int initiator_cmp(void *priv, const struct list_head *a, 5624f0f586bSSami Tolvanen const struct list_head *b) 563c710fcc5SDan Williams { 564c710fcc5SDan Williams struct memory_initiator *ia; 565c710fcc5SDan Williams struct memory_initiator *ib; 566c710fcc5SDan Williams 567c710fcc5SDan Williams ia = list_entry(a, struct memory_initiator, node); 568c710fcc5SDan Williams ib = list_entry(b, struct memory_initiator, node); 569c710fcc5SDan Williams 570c710fcc5SDan Williams return ia->processor_pxm - ib->processor_pxm; 571c710fcc5SDan Williams } 572c710fcc5SDan Williams 57348d41809SVishal Verma static int initiators_to_nodemask(unsigned long *p_nodes) 57448d41809SVishal Verma { 57548d41809SVishal Verma struct memory_initiator *initiator; 57648d41809SVishal Verma 57748d41809SVishal Verma if (list_empty(&initiators)) 57848d41809SVishal Verma return -ENXIO; 57948d41809SVishal Verma 58048d41809SVishal Verma list_for_each_entry(initiator, &initiators, node) 58148d41809SVishal Verma set_bit(initiator->processor_pxm, p_nodes); 58248d41809SVishal Verma 58348d41809SVishal Verma return 0; 58448d41809SVishal Verma } 58548d41809SVishal Verma 586d0376aacSHuang Ying static void hmat_update_target_attrs(struct memory_target *target, 587d0376aacSHuang Ying unsigned long *p_nodes, int access) 588c710fcc5SDan Williams { 589c710fcc5SDan Williams struct memory_initiator *initiator; 590d0376aacSHuang Ying unsigned int cpu_nid; 591c710fcc5SDan Williams struct memory_locality *loc = NULL; 592c710fcc5SDan Williams u32 best = 0; 593c710fcc5SDan Williams int i; 594c710fcc5SDan Williams 595d0376aacSHuang Ying bitmap_zero(p_nodes, MAX_NUMNODES); 596c710fcc5SDan Williams /* 597d0376aacSHuang Ying * If the Address Range Structure provides a local processor pxm, set 598c710fcc5SDan Williams * only that one. Otherwise, find the best performance attributes and 599d0376aacSHuang Ying * collect all initiators that match. 600c710fcc5SDan Williams */ 601c710fcc5SDan Williams if (target->processor_pxm != PXM_INVAL) { 602c710fcc5SDan Williams cpu_nid = pxm_to_node(target->processor_pxm); 603d0376aacSHuang Ying if (access == 0 || node_state(cpu_nid, N_CPU)) { 604d0376aacSHuang Ying set_bit(target->processor_pxm, p_nodes); 605c710fcc5SDan Williams return; 606c710fcc5SDan Williams } 607b9fffe47SJonathan Cameron } 608c710fcc5SDan Williams 609c710fcc5SDan Williams if (list_empty(&localities)) 610c710fcc5SDan Williams return; 611c710fcc5SDan Williams 612c710fcc5SDan Williams /* 613c710fcc5SDan Williams * We need the initiator list sorted so we can use bitmap_clear for 614c710fcc5SDan Williams * previously set initiators when we find a better memory accessor. 615c710fcc5SDan Williams * We'll also use the sorting to prime the candidate nodes with known 616c710fcc5SDan Williams * initiators. 617c710fcc5SDan Williams */ 61848d41809SVishal Verma list_sort(NULL, &initiators, initiator_cmp); 61948d41809SVishal Verma if (initiators_to_nodemask(p_nodes) < 0) 62048d41809SVishal Verma return; 62148d41809SVishal Verma 622c710fcc5SDan Williams for (i = WRITE_LATENCY; i <= READ_BANDWIDTH; i++) { 623c710fcc5SDan Williams loc = localities_types[i]; 624c710fcc5SDan Williams if (!loc) 625c710fcc5SDan Williams continue; 626c710fcc5SDan Williams 627c710fcc5SDan Williams best = 0; 628c710fcc5SDan Williams list_for_each_entry(initiator, &initiators, node) { 629c710fcc5SDan Williams u32 value; 630c710fcc5SDan Williams 631d0376aacSHuang Ying if (access == 1 && !initiator->has_cpu) { 632b9fffe47SJonathan Cameron clear_bit(initiator->processor_pxm, p_nodes); 633b9fffe47SJonathan Cameron continue; 634b9fffe47SJonathan Cameron } 635b9fffe47SJonathan Cameron if (!test_bit(initiator->processor_pxm, p_nodes)) 636b9fffe47SJonathan Cameron continue; 637b9fffe47SJonathan Cameron 638c710fcc5SDan Williams value = hmat_initiator_perf(target, initiator, loc->hmat_loc); 639c710fcc5SDan Williams if (hmat_update_best(loc->hmat_loc->data_type, value, &best)) 640c710fcc5SDan Williams bitmap_clear(p_nodes, 0, initiator->processor_pxm); 641c710fcc5SDan Williams if (value != best) 642c710fcc5SDan Williams clear_bit(initiator->processor_pxm, p_nodes); 643c710fcc5SDan Williams } 644c710fcc5SDan Williams if (best) 645d0376aacSHuang Ying hmat_update_target_access(target, loc->hmat_loc->data_type, best, access); 646c710fcc5SDan Williams } 647d0376aacSHuang Ying } 648d0376aacSHuang Ying 649d0376aacSHuang Ying static void __hmat_register_target_initiators(struct memory_target *target, 650d0376aacSHuang Ying unsigned long *p_nodes, 651d0376aacSHuang Ying int access) 652d0376aacSHuang Ying { 653d0376aacSHuang Ying unsigned int mem_nid, cpu_nid; 654d0376aacSHuang Ying int i; 655d0376aacSHuang Ying 656d0376aacSHuang Ying mem_nid = pxm_to_node(target->memory_pxm); 657d0376aacSHuang Ying hmat_update_target_attrs(target, p_nodes, access); 658c710fcc5SDan Williams for_each_set_bit(i, p_nodes, MAX_NUMNODES) { 659c710fcc5SDan Williams cpu_nid = pxm_to_node(i); 660d0376aacSHuang Ying register_memory_node_under_compute_node(mem_nid, cpu_nid, access); 661c710fcc5SDan Williams } 662c710fcc5SDan Williams } 663c710fcc5SDan Williams 664d0376aacSHuang Ying static void hmat_register_target_initiators(struct memory_target *target) 665d0376aacSHuang Ying { 666d0376aacSHuang Ying static DECLARE_BITMAP(p_nodes, MAX_NUMNODES); 667d0376aacSHuang Ying 668d0376aacSHuang Ying __hmat_register_target_initiators(target, p_nodes, 0); 669d0376aacSHuang Ying __hmat_register_target_initiators(target, p_nodes, 1); 670d0376aacSHuang Ying } 671d0376aacSHuang Ying 672c710fcc5SDan Williams static void hmat_register_target_cache(struct memory_target *target) 673c710fcc5SDan Williams { 674c710fcc5SDan Williams unsigned mem_nid = pxm_to_node(target->memory_pxm); 675c710fcc5SDan Williams struct target_cache *tcache; 676c710fcc5SDan Williams 677c710fcc5SDan Williams list_for_each_entry(tcache, &target->caches, node) 678c710fcc5SDan Williams node_add_cache(mem_nid, &tcache->cache_attrs); 679c710fcc5SDan Williams } 680c710fcc5SDan Williams 681b9fffe47SJonathan Cameron static void hmat_register_target_perf(struct memory_target *target, int access) 682c710fcc5SDan Williams { 683c710fcc5SDan Williams unsigned mem_nid = pxm_to_node(target->memory_pxm); 684b9fffe47SJonathan Cameron node_set_perf_attrs(mem_nid, &target->hmem_attrs[access], access); 685c710fcc5SDan Williams } 686c710fcc5SDan Williams 68759b2c5b6SQian Cai static void hmat_register_target_devices(struct memory_target *target) 688cf8741acSDan Williams { 689cf8741acSDan Williams struct resource *res; 690cf8741acSDan Williams 691cf8741acSDan Williams /* 692cf8741acSDan Williams * Do not bother creating devices if no driver is available to 693cf8741acSDan Williams * consume them. 694cf8741acSDan Williams */ 695cf8741acSDan Williams if (!IS_ENABLED(CONFIG_DEV_DAX_HMEM)) 696cf8741acSDan Williams return; 697cf8741acSDan Williams 698c01044ccSDan Williams for (res = target->memregions.child; res; res = res->sibling) { 699cf1d2b44SLinus Torvalds int target_nid = pxm_to_node(target->memory_pxm); 700c01044ccSDan Williams 7017dab174eSDan Williams hmem_register_resource(target_nid, res); 702c01044ccSDan Williams } 703cf8741acSDan Williams } 704cf8741acSDan Williams 705c710fcc5SDan Williams static void hmat_register_target(struct memory_target *target) 706c710fcc5SDan Williams { 707c710fcc5SDan Williams int nid = pxm_to_node(target->memory_pxm); 708c710fcc5SDan Williams 709c710fcc5SDan Williams /* 710cf8741acSDan Williams * Devices may belong to either an offline or online 711cf8741acSDan Williams * node, so unconditionally add them. 712cf8741acSDan Williams */ 713cf8741acSDan Williams hmat_register_target_devices(target); 714cf8741acSDan Williams 715cf8741acSDan Williams /* 716c710fcc5SDan Williams * Skip offline nodes. This can happen when memory 717c710fcc5SDan Williams * marked EFI_MEMORY_SP, "specific purpose", is applied 718935ab850STom Saeger * to all the memory in a proximity domain leading to 719c710fcc5SDan Williams * the node being marked offline / unplugged, or if 720c710fcc5SDan Williams * memory-only "hotplug" node is offline. 721c710fcc5SDan Williams */ 722c710fcc5SDan Williams if (nid == NUMA_NO_NODE || !node_online(nid)) 723c710fcc5SDan Williams return; 724c710fcc5SDan Williams 725c710fcc5SDan Williams mutex_lock(&target_lock); 726c710fcc5SDan Williams if (!target->registered) { 727c710fcc5SDan Williams hmat_register_target_initiators(target); 728c710fcc5SDan Williams hmat_register_target_cache(target); 729b9fffe47SJonathan Cameron hmat_register_target_perf(target, 0); 730b9fffe47SJonathan Cameron hmat_register_target_perf(target, 1); 731c710fcc5SDan Williams target->registered = true; 732c710fcc5SDan Williams } 733c710fcc5SDan Williams mutex_unlock(&target_lock); 734c710fcc5SDan Williams } 735c710fcc5SDan Williams 736c710fcc5SDan Williams static void hmat_register_targets(void) 737c710fcc5SDan Williams { 738c710fcc5SDan Williams struct memory_target *target; 739c710fcc5SDan Williams 740c710fcc5SDan Williams list_for_each_entry(target, &targets, node) 741c710fcc5SDan Williams hmat_register_target(target); 742c710fcc5SDan Williams } 743c710fcc5SDan Williams 744c710fcc5SDan Williams static int hmat_callback(struct notifier_block *self, 745c710fcc5SDan Williams unsigned long action, void *arg) 746c710fcc5SDan Williams { 747c710fcc5SDan Williams struct memory_target *target; 748c710fcc5SDan Williams struct memory_notify *mnb = arg; 749c710fcc5SDan Williams int pxm, nid = mnb->status_change_nid; 750c710fcc5SDan Williams 751c710fcc5SDan Williams if (nid == NUMA_NO_NODE || action != MEM_ONLINE) 752c710fcc5SDan Williams return NOTIFY_OK; 753c710fcc5SDan Williams 754c710fcc5SDan Williams pxm = node_to_pxm(nid); 755c710fcc5SDan Williams target = find_mem_target(pxm); 756c710fcc5SDan Williams if (!target) 757c710fcc5SDan Williams return NOTIFY_OK; 758c710fcc5SDan Williams 759c710fcc5SDan Williams hmat_register_target(target); 760c710fcc5SDan Williams return NOTIFY_OK; 761c710fcc5SDan Williams } 762c710fcc5SDan Williams 763*3718c02dSHuang Ying static int hmat_set_default_dram_perf(void) 764*3718c02dSHuang Ying { 765*3718c02dSHuang Ying int rc; 766*3718c02dSHuang Ying int nid, pxm; 767*3718c02dSHuang Ying struct memory_target *target; 768*3718c02dSHuang Ying struct node_hmem_attrs *attrs; 769*3718c02dSHuang Ying 770*3718c02dSHuang Ying if (!default_dram_type) 771*3718c02dSHuang Ying return -EIO; 772*3718c02dSHuang Ying 773*3718c02dSHuang Ying for_each_node_mask(nid, default_dram_type->nodes) { 774*3718c02dSHuang Ying pxm = node_to_pxm(nid); 775*3718c02dSHuang Ying target = find_mem_target(pxm); 776*3718c02dSHuang Ying if (!target) 777*3718c02dSHuang Ying continue; 778*3718c02dSHuang Ying attrs = &target->hmem_attrs[1]; 779*3718c02dSHuang Ying rc = mt_set_default_dram_perf(nid, attrs, "ACPI HMAT"); 780*3718c02dSHuang Ying if (rc) 781*3718c02dSHuang Ying return rc; 782*3718c02dSHuang Ying } 783*3718c02dSHuang Ying 784*3718c02dSHuang Ying return 0; 785*3718c02dSHuang Ying } 786*3718c02dSHuang Ying 787*3718c02dSHuang Ying static int hmat_calculate_adistance(struct notifier_block *self, 788*3718c02dSHuang Ying unsigned long nid, void *data) 789*3718c02dSHuang Ying { 790*3718c02dSHuang Ying static DECLARE_BITMAP(p_nodes, MAX_NUMNODES); 791*3718c02dSHuang Ying struct memory_target *target; 792*3718c02dSHuang Ying struct node_hmem_attrs *perf; 793*3718c02dSHuang Ying int *adist = data; 794*3718c02dSHuang Ying int pxm; 795*3718c02dSHuang Ying 796*3718c02dSHuang Ying pxm = node_to_pxm(nid); 797*3718c02dSHuang Ying target = find_mem_target(pxm); 798*3718c02dSHuang Ying if (!target) 799*3718c02dSHuang Ying return NOTIFY_OK; 800*3718c02dSHuang Ying 801*3718c02dSHuang Ying mutex_lock(&target_lock); 802*3718c02dSHuang Ying hmat_update_target_attrs(target, p_nodes, 1); 803*3718c02dSHuang Ying mutex_unlock(&target_lock); 804*3718c02dSHuang Ying 805*3718c02dSHuang Ying perf = &target->hmem_attrs[1]; 806*3718c02dSHuang Ying 807*3718c02dSHuang Ying if (mt_perf_to_adistance(perf, adist)) 808*3718c02dSHuang Ying return NOTIFY_OK; 809*3718c02dSHuang Ying 810*3718c02dSHuang Ying return NOTIFY_STOP; 811*3718c02dSHuang Ying } 812*3718c02dSHuang Ying 813*3718c02dSHuang Ying static struct notifier_block hmat_adist_nb __meminitdata = { 814*3718c02dSHuang Ying .notifier_call = hmat_calculate_adistance, 815*3718c02dSHuang Ying .priority = 100, 816*3718c02dSHuang Ying }; 817*3718c02dSHuang Ying 818c710fcc5SDan Williams static __init void hmat_free_structures(void) 819c710fcc5SDan Williams { 820c710fcc5SDan Williams struct memory_target *target, *tnext; 821c710fcc5SDan Williams struct memory_locality *loc, *lnext; 822c710fcc5SDan Williams struct memory_initiator *initiator, *inext; 823c710fcc5SDan Williams struct target_cache *tcache, *cnext; 824c710fcc5SDan Williams 825c710fcc5SDan Williams list_for_each_entry_safe(target, tnext, &targets, node) { 826cf8741acSDan Williams struct resource *res, *res_next; 827cf8741acSDan Williams 828c710fcc5SDan Williams list_for_each_entry_safe(tcache, cnext, &target->caches, node) { 829c710fcc5SDan Williams list_del(&tcache->node); 830c710fcc5SDan Williams kfree(tcache); 831c710fcc5SDan Williams } 832cf8741acSDan Williams 833c710fcc5SDan Williams list_del(&target->node); 834cf8741acSDan Williams res = target->memregions.child; 835cf8741acSDan Williams while (res) { 836cf8741acSDan Williams res_next = res->sibling; 837cf8741acSDan Williams __release_region(&target->memregions, res->start, 838cf8741acSDan Williams resource_size(res)); 839cf8741acSDan Williams res = res_next; 840cf8741acSDan Williams } 841c710fcc5SDan Williams kfree(target); 842c710fcc5SDan Williams } 843c710fcc5SDan Williams 844c710fcc5SDan Williams list_for_each_entry_safe(initiator, inext, &initiators, node) { 845c710fcc5SDan Williams list_del(&initiator->node); 846c710fcc5SDan Williams kfree(initiator); 847c710fcc5SDan Williams } 848c710fcc5SDan Williams 849c710fcc5SDan Williams list_for_each_entry_safe(loc, lnext, &localities, node) { 850c710fcc5SDan Williams list_del(&loc->node); 851c710fcc5SDan Williams kfree(loc); 852c710fcc5SDan Williams } 853c710fcc5SDan Williams } 854c710fcc5SDan Williams 855c710fcc5SDan Williams static __init int hmat_init(void) 856c710fcc5SDan Williams { 857c710fcc5SDan Williams struct acpi_table_header *tbl; 858c710fcc5SDan Williams enum acpi_hmat_type i; 859c710fcc5SDan Williams acpi_status status; 860c710fcc5SDan Williams 8613b0d3101SDan Williams if (srat_disabled() || hmat_disable) 862c710fcc5SDan Williams return 0; 863c710fcc5SDan Williams 864c710fcc5SDan Williams status = acpi_get_table(ACPI_SIG_SRAT, 0, &tbl); 865c710fcc5SDan Williams if (ACPI_FAILURE(status)) 866c710fcc5SDan Williams return 0; 867c710fcc5SDan Williams 868c710fcc5SDan Williams if (acpi_table_parse_entries(ACPI_SIG_SRAT, 869c710fcc5SDan Williams sizeof(struct acpi_table_srat), 870c710fcc5SDan Williams ACPI_SRAT_TYPE_MEMORY_AFFINITY, 871c710fcc5SDan Williams srat_parse_mem_affinity, 0) < 0) 872c710fcc5SDan Williams goto out_put; 873c710fcc5SDan Williams acpi_put_table(tbl); 874c710fcc5SDan Williams 875c710fcc5SDan Williams status = acpi_get_table(ACPI_SIG_HMAT, 0, &tbl); 876c710fcc5SDan Williams if (ACPI_FAILURE(status)) 877c710fcc5SDan Williams goto out_put; 878c710fcc5SDan Williams 879c710fcc5SDan Williams hmat_revision = tbl->revision; 880c710fcc5SDan Williams switch (hmat_revision) { 881c710fcc5SDan Williams case 1: 882c710fcc5SDan Williams case 2: 883c710fcc5SDan Williams break; 884c710fcc5SDan Williams default: 88556216359SLiu Shixin pr_notice("Ignoring: Unknown revision:%d\n", hmat_revision); 886c710fcc5SDan Williams goto out_put; 887c710fcc5SDan Williams } 888c710fcc5SDan Williams 889c710fcc5SDan Williams for (i = ACPI_HMAT_TYPE_PROXIMITY; i < ACPI_HMAT_TYPE_RESERVED; i++) { 890c710fcc5SDan Williams if (acpi_table_parse_entries(ACPI_SIG_HMAT, 891c710fcc5SDan Williams sizeof(struct acpi_table_hmat), i, 892c710fcc5SDan Williams hmat_parse_subtable, 0) < 0) { 89356216359SLiu Shixin pr_notice("Ignoring: Invalid table"); 894c710fcc5SDan Williams goto out_put; 895c710fcc5SDan Williams } 896c710fcc5SDan Williams } 897c710fcc5SDan Williams hmat_register_targets(); 898c710fcc5SDan Williams 899c710fcc5SDan Williams /* Keep the table and structures if the notifier may use them */ 900*3718c02dSHuang Ying if (hotplug_memory_notifier(hmat_callback, HMAT_CALLBACK_PRI)) 901*3718c02dSHuang Ying goto out_put; 902*3718c02dSHuang Ying 903*3718c02dSHuang Ying if (!hmat_set_default_dram_perf()) 904*3718c02dSHuang Ying register_mt_adistance_algorithm(&hmat_adist_nb); 905*3718c02dSHuang Ying 906c710fcc5SDan Williams return 0; 907c710fcc5SDan Williams out_put: 908c710fcc5SDan Williams hmat_free_structures(); 909c710fcc5SDan Williams acpi_put_table(tbl); 910c710fcc5SDan Williams return 0; 911c710fcc5SDan Williams } 912df2798bcSDan Williams subsys_initcall(hmat_init); 913