Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Merge tag 'cxl-for-6.8' of git://git.kernel.org/pub/scm/linux/kernel/git/cxl/cxl

Pull CXL (Compute Express Link) updates from Dan Williams:
"The bulk of this update is support for enumerating the performance
capabilities of CXL memory targets and connecting that to a platform
CXL memory QoS class. Some follow-on work remains to hook up this data
into core-mm policy, but that is saved for v6.9.

The next significant update is unifying how CXL event records (things
like background scrub errors) are processed between so called
"firmware first" and native error record retrieval. The CXL driver
handler that processes the record retrieved from the device mailbox is
now the handler for that same record format coming from an EFI/ACPI
notification source.

This also contains miscellaneous feature updates, like Get Timestamp,
and other fixups.

Summary:

- Add support for parsing the Coherent Device Attribute Table (CDAT)

- Add support for calculating a platform CXL QoS class from CDAT data

- Unify the tracing of EFI CXL Events with native CXL Events.

- Add Get Timestamp support

- Miscellaneous cleanups and fixups"

* tag 'cxl-for-6.8' of git://git.kernel.org/pub/scm/linux/kernel/git/cxl/cxl: (41 commits)
cxl/core: use sysfs_emit() for attr's _show()
cxl/pci: Register for and process CPER events
PCI: Introduce cleanup helpers for device reference counts and locks
acpi/ghes: Process CXL Component Events
cxl/events: Create a CXL event union
cxl/events: Separate UUID from event structures
cxl/events: Remove passing a UUID to known event traces
cxl/events: Create common event UUID defines
cxl/events: Promote CXL event structures to a core header
cxl: Refactor to use __free() for cxl_root allocation in cxl_endpoint_port_probe()
cxl: Refactor to use __free() for cxl_root allocation in cxl_find_nvdimm_bridge()
cxl: Fix device reference leak in cxl_port_perf_data_calculate()
cxl: Convert find_cxl_root() to return a 'struct cxl_root *'
cxl: Introduce put_cxl_root() helper
cxl/port: Fix missing target list lock
cxl/port: Fix decoder initialization when nr_targets > interleave_ways
cxl/region: fix x9 interleave typo
cxl/trace: Pass UUID explicitly to event traces
cxl/region: use %pap format to print resource_size_t
cxl/region: Add dev_dbg() detail on failure to allocate HPA space
...

+1844 -328
+34
Documentation/ABI/testing/sysfs-bus-cxl
··· 28 28 Payload in the CXL-2.0 specification. 29 29 30 30 31 + What: /sys/bus/cxl/devices/memX/ram/qos_class 32 + Date: May, 2023 33 + KernelVersion: v6.8 34 + Contact: linux-cxl@vger.kernel.org 35 + Description: 36 + (RO) For CXL host platforms that support "QoS Telemmetry" 37 + this attribute conveys a comma delimited list of platform 38 + specific cookies that identifies a QoS performance class 39 + for the volatile partition of the CXL mem device. These 40 + class-ids can be compared against a similar "qos_class" 41 + published for a root decoder. While it is not required 42 + that the endpoints map their local memory-class to a 43 + matching platform class, mismatches are not recommended 44 + and there are platform specific performance related 45 + side-effects that may result. First class-id is displayed. 46 + 47 + 31 48 What: /sys/bus/cxl/devices/memX/pmem/size 32 49 Date: December, 2020 33 50 KernelVersion: v5.12 ··· 53 36 (RO) "Persistent Only Capacity" as bytes. Represents the 54 37 identically named field in the Identify Memory Device Output 55 38 Payload in the CXL-2.0 specification. 39 + 40 + 41 + What: /sys/bus/cxl/devices/memX/pmem/qos_class 42 + Date: May, 2023 43 + KernelVersion: v6.8 44 + Contact: linux-cxl@vger.kernel.org 45 + Description: 46 + (RO) For CXL host platforms that support "QoS Telemmetry" 47 + this attribute conveys a comma delimited list of platform 48 + specific cookies that identifies a QoS performance class 49 + for the persistent partition of the CXL mem device. These 50 + class-ids can be compared against a similar "qos_class" 51 + published for a root decoder. While it is not required 52 + that the endpoints map their local memory-class to a 53 + matching platform class, mismatches are not recommended 54 + and there are platform specific performance related 55 + side-effects that may result. First class-id is displayed. 56 56 57 57 58 58 What: /sys/bus/cxl/devices/memX/serial
+1
MAINTAINERS
··· 5280 5280 L: linux-cxl@vger.kernel.org 5281 5281 S: Maintained 5282 5282 F: drivers/cxl/ 5283 + F: include/linux/cxl-event.h 5283 5284 F: include/uapi/linux/cxl_mem.h 5284 5285 F: tools/testing/cxl/ 5285 5286
+89
drivers/acpi/apei/ghes.c
··· 26 26 #include <linux/interrupt.h> 27 27 #include <linux/timer.h> 28 28 #include <linux/cper.h> 29 + #include <linux/cxl-event.h> 29 30 #include <linux/platform_device.h> 30 31 #include <linux/mutex.h> 31 32 #include <linux/ratelimit.h> ··· 674 673 schedule_work(&entry->work); 675 674 } 676 675 676 + /* 677 + * Only a single callback can be registered for CXL CPER events. 678 + */ 679 + static DECLARE_RWSEM(cxl_cper_rw_sem); 680 + static cxl_cper_callback cper_callback; 681 + 682 + /* CXL Event record UUIDs are formatted as GUIDs and reported in section type */ 683 + 684 + /* 685 + * General Media Event Record 686 + * CXL rev 3.0 Section 8.2.9.2.1.1; Table 8-43 687 + */ 688 + #define CPER_SEC_CXL_GEN_MEDIA_GUID \ 689 + GUID_INIT(0xfbcd0a77, 0xc260, 0x417f, \ 690 + 0x85, 0xa9, 0x08, 0x8b, 0x16, 0x21, 0xeb, 0xa6) 691 + 692 + /* 693 + * DRAM Event Record 694 + * CXL rev 3.0 section 8.2.9.2.1.2; Table 8-44 695 + */ 696 + #define CPER_SEC_CXL_DRAM_GUID \ 697 + GUID_INIT(0x601dcbb3, 0x9c06, 0x4eab, \ 698 + 0xb8, 0xaf, 0x4e, 0x9b, 0xfb, 0x5c, 0x96, 0x24) 699 + 700 + /* 701 + * Memory Module Event Record 702 + * CXL rev 3.0 section 8.2.9.2.1.3; Table 8-45 703 + */ 704 + #define CPER_SEC_CXL_MEM_MODULE_GUID \ 705 + GUID_INIT(0xfe927475, 0xdd59, 0x4339, \ 706 + 0xa5, 0x86, 0x79, 0xba, 0xb1, 0x13, 0xb7, 0x74) 707 + 708 + static void cxl_cper_post_event(enum cxl_event_type event_type, 709 + struct cxl_cper_event_rec *rec) 710 + { 711 + if (rec->hdr.length <= sizeof(rec->hdr) || 712 + rec->hdr.length > sizeof(*rec)) { 713 + pr_err(FW_WARN "CXL CPER Invalid section length (%u)\n", 714 + rec->hdr.length); 715 + return; 716 + } 717 + 718 + if (!(rec->hdr.validation_bits & CPER_CXL_COMP_EVENT_LOG_VALID)) { 719 + pr_err(FW_WARN "CXL CPER invalid event\n"); 720 + return; 721 + } 722 + 723 + guard(rwsem_read)(&cxl_cper_rw_sem); 724 + if (cper_callback) 725 + cper_callback(event_type, rec); 726 + } 727 + 728 + int cxl_cper_register_callback(cxl_cper_callback callback) 729 + { 730 + guard(rwsem_write)(&cxl_cper_rw_sem); 731 + if (cper_callback) 732 + return -EINVAL; 733 + cper_callback = callback; 734 + return 0; 735 + } 736 + EXPORT_SYMBOL_NS_GPL(cxl_cper_register_callback, CXL); 737 + 738 + int cxl_cper_unregister_callback(cxl_cper_callback callback) 739 + { 740 + guard(rwsem_write)(&cxl_cper_rw_sem); 741 + if (callback != cper_callback) 742 + return -EINVAL; 743 + cper_callback = NULL; 744 + return 0; 745 + } 746 + EXPORT_SYMBOL_NS_GPL(cxl_cper_unregister_callback, CXL); 747 + 677 748 static bool ghes_do_proc(struct ghes *ghes, 678 749 const struct acpi_hest_generic_status *estatus) 679 750 { ··· 780 707 } 781 708 else if (guid_equal(sec_type, &CPER_SEC_PROC_ARM)) { 782 709 queued = ghes_handle_arm_hw_error(gdata, sev, sync); 710 + } else if (guid_equal(sec_type, &CPER_SEC_CXL_GEN_MEDIA_GUID)) { 711 + struct cxl_cper_event_rec *rec = 712 + acpi_hest_get_payload(gdata); 713 + 714 + cxl_cper_post_event(CXL_CPER_EVENT_GEN_MEDIA, rec); 715 + } else if (guid_equal(sec_type, &CPER_SEC_CXL_DRAM_GUID)) { 716 + struct cxl_cper_event_rec *rec = 717 + acpi_hest_get_payload(gdata); 718 + 719 + cxl_cper_post_event(CXL_CPER_EVENT_DRAM, rec); 720 + } else if (guid_equal(sec_type, 721 + &CPER_SEC_CXL_MEM_MODULE_GUID)) { 722 + struct cxl_cper_event_rec *rec = 723 + acpi_hest_get_payload(gdata); 724 + 725 + cxl_cper_post_event(CXL_CPER_EVENT_MEM_MODULE, rec); 783 726 } else { 784 727 void *err = acpi_hest_get_payload(gdata); 785 728
+164 -29
drivers/acpi/numa/hmat.c
··· 58 58 struct node_cache_attrs cache_attrs; 59 59 }; 60 60 61 + enum { 62 + NODE_ACCESS_CLASS_0 = 0, 63 + NODE_ACCESS_CLASS_1, 64 + NODE_ACCESS_CLASS_GENPORT_SINK, 65 + NODE_ACCESS_CLASS_MAX, 66 + }; 67 + 61 68 struct memory_target { 62 69 struct list_head node; 63 70 unsigned int memory_pxm; 64 71 unsigned int processor_pxm; 65 72 struct resource memregions; 66 - struct node_hmem_attrs hmem_attrs[2]; 73 + struct access_coordinate coord[NODE_ACCESS_CLASS_MAX]; 67 74 struct list_head caches; 68 75 struct node_cache_attrs cache_attrs; 76 + u8 gen_port_device_handle[ACPI_SRAT_DEVICE_HANDLE_SIZE]; 69 77 bool registered; 70 78 }; 71 79 ··· 108 100 return NULL; 109 101 } 110 102 103 + static struct memory_target *acpi_find_genport_target(u32 uid) 104 + { 105 + struct memory_target *target; 106 + u32 target_uid; 107 + u8 *uid_ptr; 108 + 109 + list_for_each_entry(target, &targets, node) { 110 + uid_ptr = target->gen_port_device_handle + 8; 111 + target_uid = *(u32 *)uid_ptr; 112 + if (uid == target_uid) 113 + return target; 114 + } 115 + 116 + return NULL; 117 + } 118 + 119 + /** 120 + * acpi_get_genport_coordinates - Retrieve the access coordinates for a generic port 121 + * @uid: ACPI unique id 122 + * @coord: The access coordinates written back out for the generic port 123 + * 124 + * Return: 0 on success. Errno on failure. 125 + * 126 + * Only supports device handles that are ACPI. Assume ACPI0016 HID for CXL. 127 + */ 128 + int acpi_get_genport_coordinates(u32 uid, 129 + struct access_coordinate *coord) 130 + { 131 + struct memory_target *target; 132 + 133 + guard(mutex)(&target_lock); 134 + target = acpi_find_genport_target(uid); 135 + if (!target) 136 + return -ENOENT; 137 + 138 + *coord = target->coord[NODE_ACCESS_CLASS_GENPORT_SINK]; 139 + 140 + return 0; 141 + } 142 + EXPORT_SYMBOL_NS_GPL(acpi_get_genport_coordinates, CXL); 143 + 111 144 static __init void alloc_memory_initiator(unsigned int cpu_pxm) 112 145 { 113 146 struct memory_initiator *initiator; ··· 169 120 list_add_tail(&initiator->node, &initiators); 170 121 } 171 122 172 - static __init void alloc_memory_target(unsigned int mem_pxm, 173 - resource_size_t start, resource_size_t len) 123 + static __init struct memory_target *alloc_target(unsigned int mem_pxm) 174 124 { 175 125 struct memory_target *target; 176 126 ··· 177 129 if (!target) { 178 130 target = kzalloc(sizeof(*target), GFP_KERNEL); 179 131 if (!target) 180 - return; 132 + return NULL; 181 133 target->memory_pxm = mem_pxm; 182 134 target->processor_pxm = PXM_INVAL; 183 135 target->memregions = (struct resource) { ··· 190 142 INIT_LIST_HEAD(&target->caches); 191 143 } 192 144 145 + return target; 146 + } 147 + 148 + static __init void alloc_memory_target(unsigned int mem_pxm, 149 + resource_size_t start, 150 + resource_size_t len) 151 + { 152 + struct memory_target *target; 153 + 154 + target = alloc_target(mem_pxm); 155 + if (!target) 156 + return; 157 + 193 158 /* 194 159 * There are potentially multiple ranges per PXM, so record each 195 160 * in the per-target memregions resource tree. ··· 211 150 IORESOURCE_MEM)) 212 151 pr_warn("failed to reserve %#llx - %#llx in pxm: %d\n", 213 152 start, start + len, mem_pxm); 153 + } 154 + 155 + static __init void alloc_genport_target(unsigned int mem_pxm, u8 *handle) 156 + { 157 + struct memory_target *target; 158 + 159 + target = alloc_target(mem_pxm); 160 + if (!target) 161 + return; 162 + 163 + memcpy(target->gen_port_device_handle, handle, 164 + ACPI_SRAT_DEVICE_HANDLE_SIZE); 214 165 } 215 166 216 167 static __init const char *hmat_data_type(u8 type) ··· 301 228 { 302 229 switch (type) { 303 230 case ACPI_HMAT_ACCESS_LATENCY: 304 - target->hmem_attrs[access].read_latency = value; 305 - target->hmem_attrs[access].write_latency = value; 231 + target->coord[access].read_latency = value; 232 + target->coord[access].write_latency = value; 306 233 break; 307 234 case ACPI_HMAT_READ_LATENCY: 308 - target->hmem_attrs[access].read_latency = value; 235 + target->coord[access].read_latency = value; 309 236 break; 310 237 case ACPI_HMAT_WRITE_LATENCY: 311 - target->hmem_attrs[access].write_latency = value; 238 + target->coord[access].write_latency = value; 312 239 break; 313 240 case ACPI_HMAT_ACCESS_BANDWIDTH: 314 - target->hmem_attrs[access].read_bandwidth = value; 315 - target->hmem_attrs[access].write_bandwidth = value; 241 + target->coord[access].read_bandwidth = value; 242 + target->coord[access].write_bandwidth = value; 316 243 break; 317 244 case ACPI_HMAT_READ_BANDWIDTH: 318 - target->hmem_attrs[access].read_bandwidth = value; 245 + target->coord[access].read_bandwidth = value; 319 246 break; 320 247 case ACPI_HMAT_WRITE_BANDWIDTH: 321 - target->hmem_attrs[access].write_bandwidth = value; 248 + target->coord[access].write_bandwidth = value; 322 249 break; 323 250 default: 324 251 break; ··· 364 291 } 365 292 } 366 293 294 + static __init void hmat_update_target(unsigned int tgt_pxm, unsigned int init_pxm, 295 + u8 mem_hier, u8 type, u32 value) 296 + { 297 + struct memory_target *target = find_mem_target(tgt_pxm); 298 + 299 + if (mem_hier != ACPI_HMAT_MEMORY) 300 + return; 301 + 302 + if (target && target->processor_pxm == init_pxm) { 303 + hmat_update_target_access(target, type, value, 304 + NODE_ACCESS_CLASS_0); 305 + /* If the node has a CPU, update access 1 */ 306 + if (node_state(pxm_to_node(init_pxm), N_CPU)) 307 + hmat_update_target_access(target, type, value, 308 + NODE_ACCESS_CLASS_1); 309 + } 310 + } 311 + 367 312 static __init int hmat_parse_locality(union acpi_subtable_headers *header, 368 313 const unsigned long end) 369 314 { 370 315 struct acpi_hmat_locality *hmat_loc = (void *)header; 371 - struct memory_target *target; 372 316 unsigned int init, targ, total_size, ipds, tpds; 373 317 u32 *inits, *targs, value; 374 318 u16 *entries; ··· 426 336 inits[init], targs[targ], value, 427 337 hmat_data_type_suffix(type)); 428 338 429 - if (mem_hier == ACPI_HMAT_MEMORY) { 430 - target = find_mem_target(targs[targ]); 431 - if (target && target->processor_pxm == inits[init]) { 432 - hmat_update_target_access(target, type, value, 0); 433 - /* If the node has a CPU, update access 1 */ 434 - if (node_state(pxm_to_node(inits[init]), N_CPU)) 435 - hmat_update_target_access(target, type, value, 1); 436 - } 437 - } 339 + hmat_update_target(targs[targ], inits[init], 340 + mem_hier, type, value); 438 341 } 439 342 } 440 343 ··· 574 491 return 0; 575 492 } 576 493 494 + static __init int srat_parse_genport_affinity(union acpi_subtable_headers *header, 495 + const unsigned long end) 496 + { 497 + struct acpi_srat_generic_affinity *ga = (void *)header; 498 + 499 + if (!ga) 500 + return -EINVAL; 501 + 502 + if (!(ga->flags & ACPI_SRAT_GENERIC_AFFINITY_ENABLED)) 503 + return 0; 504 + 505 + /* Skip PCI device_handle for now */ 506 + if (ga->device_handle_type != 0) 507 + return 0; 508 + 509 + alloc_genport_target(ga->proximity_domain, 510 + (u8 *)ga->device_handle); 511 + 512 + return 0; 513 + } 514 + 577 515 static u32 hmat_initiator_perf(struct memory_target *target, 578 516 struct memory_initiator *initiator, 579 517 struct acpi_hmat_locality *hmat_loc) ··· 696 592 u32 best = 0; 697 593 int i; 698 594 595 + /* Don't update for generic port if there's no device handle */ 596 + if (access == NODE_ACCESS_CLASS_GENPORT_SINK && 597 + !(*(u16 *)target->gen_port_device_handle)) 598 + return; 599 + 699 600 bitmap_zero(p_nodes, MAX_NUMNODES); 700 601 /* 701 602 * If the Address Range Structure provides a local processor pxm, set ··· 770 661 } 771 662 } 772 663 664 + static void hmat_register_generic_target_initiators(struct memory_target *target) 665 + { 666 + static DECLARE_BITMAP(p_nodes, MAX_NUMNODES); 667 + 668 + __hmat_register_target_initiators(target, p_nodes, 669 + NODE_ACCESS_CLASS_GENPORT_SINK); 670 + } 671 + 773 672 static void hmat_register_target_initiators(struct memory_target *target) 774 673 { 775 674 static DECLARE_BITMAP(p_nodes, MAX_NUMNODES); ··· 798 681 static void hmat_register_target_perf(struct memory_target *target, int access) 799 682 { 800 683 unsigned mem_nid = pxm_to_node(target->memory_pxm); 801 - node_set_perf_attrs(mem_nid, &target->hmem_attrs[access], access); 684 + node_set_perf_attrs(mem_nid, &target->coord[access], access); 802 685 } 803 686 804 687 static void hmat_register_target_devices(struct memory_target *target) ··· 830 713 hmat_register_target_devices(target); 831 714 832 715 /* 716 + * Register generic port perf numbers. The nid may not be 717 + * initialized and is still NUMA_NO_NODE. 718 + */ 719 + mutex_lock(&target_lock); 720 + if (*(u16 *)target->gen_port_device_handle) { 721 + hmat_register_generic_target_initiators(target); 722 + target->registered = true; 723 + } 724 + mutex_unlock(&target_lock); 725 + 726 + /* 833 727 * Skip offline nodes. This can happen when memory 834 728 * marked EFI_MEMORY_SP, "specific purpose", is applied 835 729 * to all the memory in a proximity domain leading to ··· 854 726 if (!target->registered) { 855 727 hmat_register_target_initiators(target); 856 728 hmat_register_target_cache(target); 857 - hmat_register_target_perf(target, 0); 858 - hmat_register_target_perf(target, 1); 729 + hmat_register_target_perf(target, NODE_ACCESS_CLASS_0); 730 + hmat_register_target_perf(target, NODE_ACCESS_CLASS_1); 859 731 target->registered = true; 860 732 } 861 733 mutex_unlock(&target_lock); ··· 893 765 int rc; 894 766 int nid, pxm; 895 767 struct memory_target *target; 896 - struct node_hmem_attrs *attrs; 768 + struct access_coordinate *attrs; 897 769 898 770 if (!default_dram_type) 899 771 return -EIO; ··· 903 775 target = find_mem_target(pxm); 904 776 if (!target) 905 777 continue; 906 - attrs = &target->hmem_attrs[1]; 778 + attrs = &target->coord[1]; 907 779 rc = mt_set_default_dram_perf(nid, attrs, "ACPI HMAT"); 908 780 if (rc) 909 781 return rc; ··· 917 789 { 918 790 static DECLARE_BITMAP(p_nodes, MAX_NUMNODES); 919 791 struct memory_target *target; 920 - struct node_hmem_attrs *perf; 792 + struct access_coordinate *perf; 921 793 int *adist = data; 922 794 int pxm; 923 795 ··· 930 802 hmat_update_target_attrs(target, p_nodes, 1); 931 803 mutex_unlock(&target_lock); 932 804 933 - perf = &target->hmem_attrs[1]; 805 + perf = &target->coord[1]; 934 806 935 807 if (mt_perf_to_adistance(perf, adist)) 936 808 return NOTIFY_OK; ··· 998 870 ACPI_SRAT_TYPE_MEMORY_AFFINITY, 999 871 srat_parse_mem_affinity, 0) < 0) 1000 872 goto out_put; 873 + 874 + if (acpi_table_parse_entries(ACPI_SIG_SRAT, 875 + sizeof(struct acpi_table_srat), 876 + ACPI_SRAT_TYPE_GENERIC_PORT_AFFINITY, 877 + srat_parse_genport_affinity, 0) < 0) 878 + goto out_put; 879 + 1001 880 acpi_put_table(tbl); 1002 881 1003 882 status = acpi_get_table(ACPI_SIG_HMAT, 0, &tbl);
+3 -2
drivers/acpi/tables.c
··· 251 251 return -ENODEV; 252 252 } 253 253 254 - count = acpi_parse_entries_array(id, table_size, table_header, 255 - proc, proc_num, max_entries); 254 + count = acpi_parse_entries_array(id, table_size, 255 + (union fw_table_header *)table_header, 256 + proc, proc_num, max_entries); 256 257 257 258 acpi_put_table(table_header); 258 259 return count;
+6 -6
drivers/base/node.c
··· 74 74 * @dev: Device for this memory access class 75 75 * @list_node: List element in the node's access list 76 76 * @access: The access class rank 77 - * @hmem_attrs: Heterogeneous memory performance attributes 77 + * @coord: Heterogeneous memory performance coordinates 78 78 */ 79 79 struct node_access_nodes { 80 80 struct device dev; 81 81 struct list_head list_node; 82 82 unsigned int access; 83 83 #ifdef CONFIG_HMEM_REPORTING 84 - struct node_hmem_attrs hmem_attrs; 84 + struct access_coordinate coord; 85 85 #endif 86 86 }; 87 87 #define to_access_nodes(dev) container_of(dev, struct node_access_nodes, dev) ··· 167 167 char *buf) \ 168 168 { \ 169 169 return sysfs_emit(buf, "%u\n", \ 170 - to_access_nodes(dev)->hmem_attrs.property); \ 170 + to_access_nodes(dev)->coord.property); \ 171 171 } \ 172 172 static DEVICE_ATTR_RO(property) 173 173 ··· 187 187 /** 188 188 * node_set_perf_attrs - Set the performance values for given access class 189 189 * @nid: Node identifier to be set 190 - * @hmem_attrs: Heterogeneous memory performance attributes 190 + * @coord: Heterogeneous memory performance coordinates 191 191 * @access: The access class the for the given attributes 192 192 */ 193 - void node_set_perf_attrs(unsigned int nid, struct node_hmem_attrs *hmem_attrs, 193 + void node_set_perf_attrs(unsigned int nid, struct access_coordinate *coord, 194 194 unsigned int access) 195 195 { 196 196 struct node_access_nodes *c; ··· 205 205 if (!c) 206 206 return; 207 207 208 - c->hmem_attrs = *hmem_attrs; 208 + c->coord = *coord; 209 209 for (i = 0; access_attrs[i] != NULL; i++) { 210 210 if (sysfs_add_file_to_group(&c->dev.kobj, access_attrs[i], 211 211 "initiators")) {
+3
drivers/cxl/Kconfig
··· 5 5 select FW_LOADER 6 6 select FW_UPLOAD 7 7 select PCI_DOE 8 + select FIRMWARE_TABLE 8 9 help 9 10 CXL is a bus that is electrically compatible with PCI Express, but 10 11 layers three protocols on that signalling (CXL.io, CXL.cache, and ··· 55 54 config CXL_ACPI 56 55 tristate "CXL ACPI: Platform Support" 57 56 depends on ACPI 57 + depends on ACPI_NUMA 58 58 default CXL_BUS 59 59 select ACPI_TABLE_LIB 60 + select ACPI_HMAT 60 61 help 61 62 Enable support for host managed device memory (HDM) resources 62 63 published by a platform's ACPI CXL memory layout description. See
+152 -3
drivers/cxl/acpi.c
··· 6 6 #include <linux/kernel.h> 7 7 #include <linux/acpi.h> 8 8 #include <linux/pci.h> 9 + #include <linux/node.h> 9 10 #include <asm/div64.h> 10 11 #include "cxlpci.h" 11 12 #include "cxl.h" ··· 17 16 int nr_maps; 18 17 u64 xormaps[] __counted_by(nr_maps); 19 18 }; 19 + 20 + static const guid_t acpi_cxl_qtg_id_guid = 21 + GUID_INIT(0xF365F9A6, 0xA7DE, 0x4071, 22 + 0xA6, 0x6A, 0xB4, 0x0C, 0x0B, 0x4F, 0x8E, 0x52); 20 23 21 24 /* 22 25 * Find a targets entry (n) in the host bridge interleave list. ··· 197 192 struct cxl_port *root_port; 198 193 struct resource *cxl_res; 199 194 int id; 195 + }; 196 + 197 + /** 198 + * cxl_acpi_evaluate_qtg_dsm - Retrieve QTG ids via ACPI _DSM 199 + * @handle: ACPI handle 200 + * @coord: performance access coordinates 201 + * @entries: number of QTG IDs to return 202 + * @qos_class: int array provided by caller to return QTG IDs 203 + * 204 + * Return: number of QTG IDs returned, or -errno for errors 205 + * 206 + * Issue QTG _DSM with accompanied bandwidth and latency data in order to get 207 + * the QTG IDs that are suitable for the performance point in order of most 208 + * suitable to least suitable. Write back array of QTG IDs and return the 209 + * actual number of QTG IDs written back. 210 + */ 211 + static int 212 + cxl_acpi_evaluate_qtg_dsm(acpi_handle handle, struct access_coordinate *coord, 213 + int entries, int *qos_class) 214 + { 215 + union acpi_object *out_obj, *out_buf, *obj; 216 + union acpi_object in_array[4] = { 217 + [0].integer = { ACPI_TYPE_INTEGER, coord->read_latency }, 218 + [1].integer = { ACPI_TYPE_INTEGER, coord->write_latency }, 219 + [2].integer = { ACPI_TYPE_INTEGER, coord->read_bandwidth }, 220 + [3].integer = { ACPI_TYPE_INTEGER, coord->write_bandwidth }, 221 + }; 222 + union acpi_object in_obj = { 223 + .package = { 224 + .type = ACPI_TYPE_PACKAGE, 225 + .count = 4, 226 + .elements = in_array, 227 + }, 228 + }; 229 + int count, pkg_entries, i; 230 + u16 max_qtg; 231 + int rc; 232 + 233 + if (!entries) 234 + return -EINVAL; 235 + 236 + out_obj = acpi_evaluate_dsm(handle, &acpi_cxl_qtg_id_guid, 1, 1, &in_obj); 237 + if (!out_obj) 238 + return -ENXIO; 239 + 240 + if (out_obj->type != ACPI_TYPE_PACKAGE) { 241 + rc = -ENXIO; 242 + goto out; 243 + } 244 + 245 + /* Check Max QTG ID */ 246 + obj = &out_obj->package.elements[0]; 247 + if (obj->type != ACPI_TYPE_INTEGER) { 248 + rc = -ENXIO; 249 + goto out; 250 + } 251 + 252 + max_qtg = obj->integer.value; 253 + 254 + /* It's legal to have 0 QTG entries */ 255 + pkg_entries = out_obj->package.count; 256 + if (pkg_entries <= 1) { 257 + rc = 0; 258 + goto out; 259 + } 260 + 261 + /* Retrieve QTG IDs package */ 262 + obj = &out_obj->package.elements[1]; 263 + if (obj->type != ACPI_TYPE_PACKAGE) { 264 + rc = -ENXIO; 265 + goto out; 266 + } 267 + 268 + pkg_entries = obj->package.count; 269 + count = min(entries, pkg_entries); 270 + for (i = 0; i < count; i++) { 271 + u16 qtg_id; 272 + 273 + out_buf = &obj->package.elements[i]; 274 + if (out_buf->type != ACPI_TYPE_INTEGER) { 275 + rc = -ENXIO; 276 + goto out; 277 + } 278 + 279 + qtg_id = out_buf->integer.value; 280 + if (qtg_id > max_qtg) 281 + pr_warn("QTG ID %u greater than MAX %u\n", 282 + qtg_id, max_qtg); 283 + 284 + qos_class[i] = qtg_id; 285 + } 286 + rc = count; 287 + 288 + out: 289 + ACPI_FREE(out_obj); 290 + return rc; 291 + } 292 + 293 + static int cxl_acpi_qos_class(struct cxl_root *cxl_root, 294 + struct access_coordinate *coord, int entries, 295 + int *qos_class) 296 + { 297 + struct device *dev = cxl_root->port.uport_dev; 298 + acpi_handle handle; 299 + 300 + if (!dev_is_platform(dev)) 301 + return -ENODEV; 302 + 303 + handle = ACPI_HANDLE(dev); 304 + if (!handle) 305 + return -ENODEV; 306 + 307 + return cxl_acpi_evaluate_qtg_dsm(handle, coord, entries, qos_class); 308 + } 309 + 310 + static const struct cxl_root_ops acpi_root_ops = { 311 + .qos_class = cxl_acpi_qos_class, 200 312 }; 201 313 202 314 static int cxl_parse_cfmws(union acpi_subtable_headers *header, void *arg, ··· 511 389 return 0; 512 390 } 513 391 392 + static int get_genport_coordinates(struct device *dev, struct cxl_dport *dport) 393 + { 394 + struct acpi_device *hb = to_cxl_host_bridge(NULL, dev); 395 + u32 uid; 396 + int rc; 397 + 398 + if (kstrtou32(acpi_device_uid(hb), 0, &uid)) 399 + return -EINVAL; 400 + 401 + rc = acpi_get_genport_coordinates(uid, &dport->hb_coord); 402 + if (rc < 0) 403 + return rc; 404 + 405 + /* Adjust back to picoseconds from nanoseconds */ 406 + dport->hb_coord.read_latency *= 1000; 407 + dport->hb_coord.write_latency *= 1000; 408 + 409 + return 0; 410 + } 411 + 514 412 static int add_host_bridge_dport(struct device *match, void *arg) 515 413 { 414 + int ret; 516 415 acpi_status rc; 517 416 struct device *bridge; 518 417 struct cxl_dport *dport; ··· 582 439 583 440 if (IS_ERR(dport)) 584 441 return PTR_ERR(dport); 442 + 443 + ret = get_genport_coordinates(match, dport); 444 + if (ret) 445 + dev_dbg(match, "Failed to get generic port perf coordinates.\n"); 585 446 586 447 return 0; 587 448 } ··· 803 656 { 804 657 int rc; 805 658 struct resource *cxl_res; 659 + struct cxl_root *cxl_root; 806 660 struct cxl_port *root_port; 807 661 struct device *host = &pdev->dev; 808 662 struct acpi_device *adev = ACPI_COMPANION(host); ··· 823 675 cxl_res->end = -1; 824 676 cxl_res->flags = IORESOURCE_MEM; 825 677 826 - root_port = devm_cxl_add_port(host, host, CXL_RESOURCE_NONE, NULL); 827 - if (IS_ERR(root_port)) 828 - return PTR_ERR(root_port); 678 + cxl_root = devm_cxl_add_root(host, &acpi_root_ops); 679 + if (IS_ERR(cxl_root)) 680 + return PTR_ERR(cxl_root); 681 + root_port = &cxl_root->port; 829 682 830 683 rc = bus_for_each_dev(adev->dev.bus, NULL, root_port, 831 684 add_host_bridge_dport);
+1
drivers/cxl/core/Makefile
··· 13 13 cxl_core-y += pci.o 14 14 cxl_core-y += hdm.o 15 15 cxl_core-y += pmu.o 16 + cxl_core-y += cdat.o 16 17 cxl_core-$(CONFIG_TRACING) += trace.o 17 18 cxl_core-$(CONFIG_CXL_REGION) += region.o
+521
drivers/cxl/core/cdat.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* Copyright(c) 2023 Intel Corporation. All rights reserved. */ 3 + #include <linux/acpi.h> 4 + #include <linux/xarray.h> 5 + #include <linux/fw_table.h> 6 + #include <linux/node.h> 7 + #include <linux/overflow.h> 8 + #include "cxlpci.h" 9 + #include "cxlmem.h" 10 + #include "core.h" 11 + #include "cxl.h" 12 + 13 + struct dsmas_entry { 14 + struct range dpa_range; 15 + u8 handle; 16 + struct access_coordinate coord; 17 + 18 + int entries; 19 + int qos_class; 20 + }; 21 + 22 + static int cdat_dsmas_handler(union acpi_subtable_headers *header, void *arg, 23 + const unsigned long end) 24 + { 25 + struct acpi_cdat_header *hdr = &header->cdat; 26 + struct acpi_cdat_dsmas *dsmas; 27 + int size = sizeof(*hdr) + sizeof(*dsmas); 28 + struct xarray *dsmas_xa = arg; 29 + struct dsmas_entry *dent; 30 + u16 len; 31 + int rc; 32 + 33 + len = le16_to_cpu((__force __le16)hdr->length); 34 + if (len != size || (unsigned long)hdr + len > end) { 35 + pr_warn("Malformed DSMAS table length: (%u:%u)\n", size, len); 36 + return -EINVAL; 37 + } 38 + 39 + /* Skip common header */ 40 + dsmas = (struct acpi_cdat_dsmas *)(hdr + 1); 41 + 42 + dent = kzalloc(sizeof(*dent), GFP_KERNEL); 43 + if (!dent) 44 + return -ENOMEM; 45 + 46 + dent->handle = dsmas->dsmad_handle; 47 + dent->dpa_range.start = le64_to_cpu((__force __le64)dsmas->dpa_base_address); 48 + dent->dpa_range.end = le64_to_cpu((__force __le64)dsmas->dpa_base_address) + 49 + le64_to_cpu((__force __le64)dsmas->dpa_length) - 1; 50 + 51 + rc = xa_insert(dsmas_xa, dent->handle, dent, GFP_KERNEL); 52 + if (rc) { 53 + kfree(dent); 54 + return rc; 55 + } 56 + 57 + return 0; 58 + } 59 + 60 + static void cxl_access_coordinate_set(struct access_coordinate *coord, 61 + int access, unsigned int val) 62 + { 63 + switch (access) { 64 + case ACPI_HMAT_ACCESS_LATENCY: 65 + coord->read_latency = val; 66 + coord->write_latency = val; 67 + break; 68 + case ACPI_HMAT_READ_LATENCY: 69 + coord->read_latency = val; 70 + break; 71 + case ACPI_HMAT_WRITE_LATENCY: 72 + coord->write_latency = val; 73 + break; 74 + case ACPI_HMAT_ACCESS_BANDWIDTH: 75 + coord->read_bandwidth = val; 76 + coord->write_bandwidth = val; 77 + break; 78 + case ACPI_HMAT_READ_BANDWIDTH: 79 + coord->read_bandwidth = val; 80 + break; 81 + case ACPI_HMAT_WRITE_BANDWIDTH: 82 + coord->write_bandwidth = val; 83 + break; 84 + } 85 + } 86 + 87 + static int cdat_dslbis_handler(union acpi_subtable_headers *header, void *arg, 88 + const unsigned long end) 89 + { 90 + struct acpi_cdat_header *hdr = &header->cdat; 91 + struct acpi_cdat_dslbis *dslbis; 92 + int size = sizeof(*hdr) + sizeof(*dslbis); 93 + struct xarray *dsmas_xa = arg; 94 + struct dsmas_entry *dent; 95 + __le64 le_base; 96 + __le16 le_val; 97 + u64 val; 98 + u16 len; 99 + int rc; 100 + 101 + len = le16_to_cpu((__force __le16)hdr->length); 102 + if (len != size || (unsigned long)hdr + len > end) { 103 + pr_warn("Malformed DSLBIS table length: (%u:%u)\n", size, len); 104 + return -EINVAL; 105 + } 106 + 107 + /* Skip common header */ 108 + dslbis = (struct acpi_cdat_dslbis *)(hdr + 1); 109 + 110 + /* Skip unrecognized data type */ 111 + if (dslbis->data_type > ACPI_HMAT_WRITE_BANDWIDTH) 112 + return 0; 113 + 114 + /* Not a memory type, skip */ 115 + if ((dslbis->flags & ACPI_HMAT_MEMORY_HIERARCHY) != ACPI_HMAT_MEMORY) 116 + return 0; 117 + 118 + dent = xa_load(dsmas_xa, dslbis->handle); 119 + if (!dent) { 120 + pr_warn("No matching DSMAS entry for DSLBIS entry.\n"); 121 + return 0; 122 + } 123 + 124 + le_base = (__force __le64)dslbis->entry_base_unit; 125 + le_val = (__force __le16)dslbis->entry[0]; 126 + rc = check_mul_overflow(le64_to_cpu(le_base), 127 + le16_to_cpu(le_val), &val); 128 + if (rc) 129 + pr_warn("DSLBIS value overflowed.\n"); 130 + 131 + cxl_access_coordinate_set(&dent->coord, dslbis->data_type, val); 132 + 133 + return 0; 134 + } 135 + 136 + static int cdat_table_parse_output(int rc) 137 + { 138 + if (rc < 0) 139 + return rc; 140 + if (rc == 0) 141 + return -ENOENT; 142 + 143 + return 0; 144 + } 145 + 146 + static int cxl_cdat_endpoint_process(struct cxl_port *port, 147 + struct xarray *dsmas_xa) 148 + { 149 + int rc; 150 + 151 + rc = cdat_table_parse(ACPI_CDAT_TYPE_DSMAS, cdat_dsmas_handler, 152 + dsmas_xa, port->cdat.table); 153 + rc = cdat_table_parse_output(rc); 154 + if (rc) 155 + return rc; 156 + 157 + rc = cdat_table_parse(ACPI_CDAT_TYPE_DSLBIS, cdat_dslbis_handler, 158 + dsmas_xa, port->cdat.table); 159 + return cdat_table_parse_output(rc); 160 + } 161 + 162 + static int cxl_port_perf_data_calculate(struct cxl_port *port, 163 + struct xarray *dsmas_xa) 164 + { 165 + struct access_coordinate c; 166 + struct dsmas_entry *dent; 167 + int valid_entries = 0; 168 + unsigned long index; 169 + int rc; 170 + 171 + rc = cxl_endpoint_get_perf_coordinates(port, &c); 172 + if (rc) { 173 + dev_dbg(&port->dev, "Failed to retrieve perf coordinates.\n"); 174 + return rc; 175 + } 176 + 177 + struct cxl_root *cxl_root __free(put_cxl_root) = find_cxl_root(port); 178 + 179 + if (!cxl_root) 180 + return -ENODEV; 181 + 182 + if (!cxl_root->ops || !cxl_root->ops->qos_class) 183 + return -EOPNOTSUPP; 184 + 185 + xa_for_each(dsmas_xa, index, dent) { 186 + int qos_class; 187 + 188 + dent->coord.read_latency = dent->coord.read_latency + 189 + c.read_latency; 190 + dent->coord.write_latency = dent->coord.write_latency + 191 + c.write_latency; 192 + dent->coord.read_bandwidth = min_t(int, c.read_bandwidth, 193 + dent->coord.read_bandwidth); 194 + dent->coord.write_bandwidth = min_t(int, c.write_bandwidth, 195 + dent->coord.write_bandwidth); 196 + 197 + dent->entries = 1; 198 + rc = cxl_root->ops->qos_class(cxl_root, &dent->coord, 1, 199 + &qos_class); 200 + if (rc != 1) 201 + continue; 202 + 203 + valid_entries++; 204 + dent->qos_class = qos_class; 205 + } 206 + 207 + if (!valid_entries) 208 + return -ENOENT; 209 + 210 + return 0; 211 + } 212 + 213 + static void add_perf_entry(struct device *dev, struct dsmas_entry *dent, 214 + struct list_head *list) 215 + { 216 + struct cxl_dpa_perf *dpa_perf; 217 + 218 + dpa_perf = kzalloc(sizeof(*dpa_perf), GFP_KERNEL); 219 + if (!dpa_perf) 220 + return; 221 + 222 + dpa_perf->dpa_range = dent->dpa_range; 223 + dpa_perf->coord = dent->coord; 224 + dpa_perf->qos_class = dent->qos_class; 225 + list_add_tail(&dpa_perf->list, list); 226 + dev_dbg(dev, 227 + "DSMAS: dpa: %#llx qos: %d read_bw: %d write_bw %d read_lat: %d write_lat: %d\n", 228 + dent->dpa_range.start, dpa_perf->qos_class, 229 + dent->coord.read_bandwidth, dent->coord.write_bandwidth, 230 + dent->coord.read_latency, dent->coord.write_latency); 231 + } 232 + 233 + static void free_perf_ents(void *data) 234 + { 235 + struct cxl_memdev_state *mds = data; 236 + struct cxl_dpa_perf *dpa_perf, *n; 237 + LIST_HEAD(discard); 238 + 239 + list_splice_tail_init(&mds->ram_perf_list, &discard); 240 + list_splice_tail_init(&mds->pmem_perf_list, &discard); 241 + list_for_each_entry_safe(dpa_perf, n, &discard, list) { 242 + list_del(&dpa_perf->list); 243 + kfree(dpa_perf); 244 + } 245 + } 246 + 247 + static void cxl_memdev_set_qos_class(struct cxl_dev_state *cxlds, 248 + struct xarray *dsmas_xa) 249 + { 250 + struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlds); 251 + struct device *dev = cxlds->dev; 252 + struct range pmem_range = { 253 + .start = cxlds->pmem_res.start, 254 + .end = cxlds->pmem_res.end, 255 + }; 256 + struct range ram_range = { 257 + .start = cxlds->ram_res.start, 258 + .end = cxlds->ram_res.end, 259 + }; 260 + struct dsmas_entry *dent; 261 + unsigned long index; 262 + 263 + xa_for_each(dsmas_xa, index, dent) { 264 + if (resource_size(&cxlds->ram_res) && 265 + range_contains(&ram_range, &dent->dpa_range)) 266 + add_perf_entry(dev, dent, &mds->ram_perf_list); 267 + else if (resource_size(&cxlds->pmem_res) && 268 + range_contains(&pmem_range, &dent->dpa_range)) 269 + add_perf_entry(dev, dent, &mds->pmem_perf_list); 270 + else 271 + dev_dbg(dev, "no partition for dsmas dpa: %#llx\n", 272 + dent->dpa_range.start); 273 + } 274 + 275 + devm_add_action_or_reset(&cxlds->cxlmd->dev, free_perf_ents, mds); 276 + } 277 + 278 + static int match_cxlrd_qos_class(struct device *dev, void *data) 279 + { 280 + int dev_qos_class = *(int *)data; 281 + struct cxl_root_decoder *cxlrd; 282 + 283 + if (!is_root_decoder(dev)) 284 + return 0; 285 + 286 + cxlrd = to_cxl_root_decoder(dev); 287 + if (cxlrd->qos_class == CXL_QOS_CLASS_INVALID) 288 + return 0; 289 + 290 + if (cxlrd->qos_class == dev_qos_class) 291 + return 1; 292 + 293 + return 0; 294 + } 295 + 296 + static void cxl_qos_match(struct cxl_port *root_port, 297 + struct list_head *work_list, 298 + struct list_head *discard_list) 299 + { 300 + struct cxl_dpa_perf *dpa_perf, *n; 301 + 302 + list_for_each_entry_safe(dpa_perf, n, work_list, list) { 303 + int rc; 304 + 305 + if (dpa_perf->qos_class == CXL_QOS_CLASS_INVALID) 306 + return; 307 + 308 + rc = device_for_each_child(&root_port->dev, 309 + (void *)&dpa_perf->qos_class, 310 + match_cxlrd_qos_class); 311 + if (!rc) 312 + list_move_tail(&dpa_perf->list, discard_list); 313 + } 314 + } 315 + 316 + static int match_cxlrd_hb(struct device *dev, void *data) 317 + { 318 + struct device *host_bridge = data; 319 + struct cxl_switch_decoder *cxlsd; 320 + struct cxl_root_decoder *cxlrd; 321 + 322 + if (!is_root_decoder(dev)) 323 + return 0; 324 + 325 + cxlrd = to_cxl_root_decoder(dev); 326 + cxlsd = &cxlrd->cxlsd; 327 + 328 + guard(rwsem_read)(&cxl_region_rwsem); 329 + for (int i = 0; i < cxlsd->nr_targets; i++) { 330 + if (host_bridge == cxlsd->target[i]->dport_dev) 331 + return 1; 332 + } 333 + 334 + return 0; 335 + } 336 + 337 + static void discard_dpa_perf(struct list_head *list) 338 + { 339 + struct cxl_dpa_perf *dpa_perf, *n; 340 + 341 + list_for_each_entry_safe(dpa_perf, n, list, list) { 342 + list_del(&dpa_perf->list); 343 + kfree(dpa_perf); 344 + } 345 + } 346 + DEFINE_FREE(dpa_perf, struct list_head *, if (!list_empty(_T)) discard_dpa_perf(_T)) 347 + 348 + static int cxl_qos_class_verify(struct cxl_memdev *cxlmd) 349 + { 350 + struct cxl_dev_state *cxlds = cxlmd->cxlds; 351 + struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlds); 352 + LIST_HEAD(__discard); 353 + struct list_head *discard __free(dpa_perf) = &__discard; 354 + struct cxl_port *root_port; 355 + int rc; 356 + 357 + struct cxl_root *cxl_root __free(put_cxl_root) = 358 + find_cxl_root(cxlmd->endpoint); 359 + 360 + if (!cxl_root) 361 + return -ENODEV; 362 + 363 + root_port = &cxl_root->port; 364 + 365 + /* Check that the QTG IDs are all sane between end device and root decoders */ 366 + cxl_qos_match(root_port, &mds->ram_perf_list, discard); 367 + cxl_qos_match(root_port, &mds->pmem_perf_list, discard); 368 + 369 + /* Check to make sure that the device's host bridge is under a root decoder */ 370 + rc = device_for_each_child(&root_port->dev, 371 + (void *)cxlmd->endpoint->host_bridge, 372 + match_cxlrd_hb); 373 + if (!rc) { 374 + list_splice_tail_init(&mds->ram_perf_list, discard); 375 + list_splice_tail_init(&mds->pmem_perf_list, discard); 376 + } 377 + 378 + return rc; 379 + } 380 + 381 + static void discard_dsmas(struct xarray *xa) 382 + { 383 + unsigned long index; 384 + void *ent; 385 + 386 + xa_for_each(xa, index, ent) { 387 + xa_erase(xa, index); 388 + kfree(ent); 389 + } 390 + xa_destroy(xa); 391 + } 392 + DEFINE_FREE(dsmas, struct xarray *, if (_T) discard_dsmas(_T)) 393 + 394 + void cxl_endpoint_parse_cdat(struct cxl_port *port) 395 + { 396 + struct cxl_memdev *cxlmd = to_cxl_memdev(port->uport_dev); 397 + struct cxl_dev_state *cxlds = cxlmd->cxlds; 398 + struct xarray __dsmas_xa; 399 + struct xarray *dsmas_xa __free(dsmas) = &__dsmas_xa; 400 + int rc; 401 + 402 + xa_init(&__dsmas_xa); 403 + if (!port->cdat.table) 404 + return; 405 + 406 + rc = cxl_cdat_endpoint_process(port, dsmas_xa); 407 + if (rc < 0) { 408 + dev_dbg(&port->dev, "Failed to parse CDAT: %d\n", rc); 409 + return; 410 + } 411 + 412 + rc = cxl_port_perf_data_calculate(port, dsmas_xa); 413 + if (rc) { 414 + dev_dbg(&port->dev, "Failed to do perf coord calculations.\n"); 415 + return; 416 + } 417 + 418 + cxl_memdev_set_qos_class(cxlds, dsmas_xa); 419 + cxl_qos_class_verify(cxlmd); 420 + } 421 + EXPORT_SYMBOL_NS_GPL(cxl_endpoint_parse_cdat, CXL); 422 + 423 + static int cdat_sslbis_handler(union acpi_subtable_headers *header, void *arg, 424 + const unsigned long end) 425 + { 426 + struct acpi_cdat_sslbis *sslbis; 427 + int size = sizeof(header->cdat) + sizeof(*sslbis); 428 + struct cxl_port *port = arg; 429 + struct device *dev = &port->dev; 430 + struct acpi_cdat_sslbe *entry; 431 + int remain, entries, i; 432 + u16 len; 433 + 434 + len = le16_to_cpu((__force __le16)header->cdat.length); 435 + remain = len - size; 436 + if (!remain || remain % sizeof(*entry) || 437 + (unsigned long)header + len > end) { 438 + dev_warn(dev, "Malformed SSLBIS table length: (%u)\n", len); 439 + return -EINVAL; 440 + } 441 + 442 + /* Skip common header */ 443 + sslbis = (struct acpi_cdat_sslbis *)((unsigned long)header + 444 + sizeof(header->cdat)); 445 + 446 + /* Unrecognized data type, we can skip */ 447 + if (sslbis->data_type > ACPI_HMAT_WRITE_BANDWIDTH) 448 + return 0; 449 + 450 + entries = remain / sizeof(*entry); 451 + entry = (struct acpi_cdat_sslbe *)((unsigned long)header + sizeof(*sslbis)); 452 + 453 + for (i = 0; i < entries; i++) { 454 + u16 x = le16_to_cpu((__force __le16)entry->portx_id); 455 + u16 y = le16_to_cpu((__force __le16)entry->porty_id); 456 + __le64 le_base; 457 + __le16 le_val; 458 + struct cxl_dport *dport; 459 + unsigned long index; 460 + u16 dsp_id; 461 + u64 val; 462 + 463 + switch (x) { 464 + case ACPI_CDAT_SSLBIS_US_PORT: 465 + dsp_id = y; 466 + break; 467 + case ACPI_CDAT_SSLBIS_ANY_PORT: 468 + switch (y) { 469 + case ACPI_CDAT_SSLBIS_US_PORT: 470 + dsp_id = x; 471 + break; 472 + case ACPI_CDAT_SSLBIS_ANY_PORT: 473 + dsp_id = ACPI_CDAT_SSLBIS_ANY_PORT; 474 + break; 475 + default: 476 + dsp_id = y; 477 + break; 478 + } 479 + break; 480 + default: 481 + dsp_id = x; 482 + break; 483 + } 484 + 485 + le_base = (__force __le64)sslbis->entry_base_unit; 486 + le_val = (__force __le16)entry->latency_or_bandwidth; 487 + 488 + if (check_mul_overflow(le64_to_cpu(le_base), 489 + le16_to_cpu(le_val), &val)) 490 + dev_warn(dev, "SSLBIS value overflowed!\n"); 491 + 492 + xa_for_each(&port->dports, index, dport) { 493 + if (dsp_id == ACPI_CDAT_SSLBIS_ANY_PORT || 494 + dsp_id == dport->port_id) 495 + cxl_access_coordinate_set(&dport->sw_coord, 496 + sslbis->data_type, 497 + val); 498 + } 499 + 500 + entry++; 501 + } 502 + 503 + return 0; 504 + } 505 + 506 + void cxl_switch_parse_cdat(struct cxl_port *port) 507 + { 508 + int rc; 509 + 510 + if (!port->cdat.table) 511 + return; 512 + 513 + rc = cdat_table_parse(ACPI_CDAT_TYPE_SSLBIS, cdat_sslbis_handler, 514 + port, port->cdat.table); 515 + rc = cdat_table_parse_output(rc); 516 + if (rc) 517 + dev_dbg(&port->dev, "Failed to parse SSLBIS: %d\n", rc); 518 + } 519 + EXPORT_SYMBOL_NS_GPL(cxl_switch_parse_cdat, CXL); 520 + 521 + MODULE_IMPORT_NS(CXL);
+2
drivers/cxl/core/core.h
··· 88 88 CXL_POISON_TRACE_CLEAR, 89 89 }; 90 90 91 + long cxl_pci_get_latency(struct pci_dev *pdev); 92 + 91 93 #endif /* __CXL_CORE_H__ */
+36 -47
drivers/cxl/core/mbox.c
··· 63 63 CXL_CMD(GET_SHUTDOWN_STATE, 0, 0x1, 0), 64 64 CXL_CMD(SET_SHUTDOWN_STATE, 0x1, 0, 0), 65 65 CXL_CMD(GET_SCAN_MEDIA_CAPS, 0x10, 0x4, 0), 66 + CXL_CMD(GET_TIMESTAMP, 0, 0x8, 0), 66 67 }; 67 68 68 69 /* ··· 837 836 } 838 837 EXPORT_SYMBOL_NS_GPL(cxl_enumerate_cmds, CXL); 839 838 840 - /* 841 - * General Media Event Record 842 - * CXL rev 3.0 Section 8.2.9.2.1.1; Table 8-43 843 - */ 844 - static const uuid_t gen_media_event_uuid = 845 - UUID_INIT(0xfbcd0a77, 0xc260, 0x417f, 846 - 0x85, 0xa9, 0x08, 0x8b, 0x16, 0x21, 0xeb, 0xa6); 847 - 848 - /* 849 - * DRAM Event Record 850 - * CXL rev 3.0 section 8.2.9.2.1.2; Table 8-44 851 - */ 852 - static const uuid_t dram_event_uuid = 853 - UUID_INIT(0x601dcbb3, 0x9c06, 0x4eab, 854 - 0xb8, 0xaf, 0x4e, 0x9b, 0xfb, 0x5c, 0x96, 0x24); 855 - 856 - /* 857 - * Memory Module Event Record 858 - * CXL rev 3.0 section 8.2.9.2.1.3; Table 8-45 859 - */ 860 - static const uuid_t mem_mod_event_uuid = 861 - UUID_INIT(0xfe927475, 0xdd59, 0x4339, 862 - 0xa5, 0x86, 0x79, 0xba, 0xb1, 0x13, 0xb7, 0x74); 863 - 864 - static void cxl_event_trace_record(const struct cxl_memdev *cxlmd, 865 - enum cxl_event_log_type type, 866 - struct cxl_event_record_raw *record) 839 + void cxl_event_trace_record(const struct cxl_memdev *cxlmd, 840 + enum cxl_event_log_type type, 841 + enum cxl_event_type event_type, 842 + const uuid_t *uuid, union cxl_event *evt) 867 843 { 868 - uuid_t *id = &record->hdr.id; 844 + if (event_type == CXL_CPER_EVENT_GEN_MEDIA) 845 + trace_cxl_general_media(cxlmd, type, &evt->gen_media); 846 + else if (event_type == CXL_CPER_EVENT_DRAM) 847 + trace_cxl_dram(cxlmd, type, &evt->dram); 848 + else if (event_type == CXL_CPER_EVENT_MEM_MODULE) 849 + trace_cxl_memory_module(cxlmd, type, &evt->mem_module); 850 + else 851 + trace_cxl_generic_event(cxlmd, type, uuid, &evt->generic); 852 + } 853 + EXPORT_SYMBOL_NS_GPL(cxl_event_trace_record, CXL); 869 854 870 - if (uuid_equal(id, &gen_media_event_uuid)) { 871 - struct cxl_event_gen_media *rec = 872 - (struct cxl_event_gen_media *)record; 855 + static void __cxl_event_trace_record(const struct cxl_memdev *cxlmd, 856 + enum cxl_event_log_type type, 857 + struct cxl_event_record_raw *record) 858 + { 859 + enum cxl_event_type ev_type = CXL_CPER_EVENT_GENERIC; 860 + const uuid_t *uuid = &record->id; 873 861 874 - trace_cxl_general_media(cxlmd, type, rec); 875 - } else if (uuid_equal(id, &dram_event_uuid)) { 876 - struct cxl_event_dram *rec = (struct cxl_event_dram *)record; 862 + if (uuid_equal(uuid, &CXL_EVENT_GEN_MEDIA_UUID)) 863 + ev_type = CXL_CPER_EVENT_GEN_MEDIA; 864 + else if (uuid_equal(uuid, &CXL_EVENT_DRAM_UUID)) 865 + ev_type = CXL_CPER_EVENT_DRAM; 866 + else if (uuid_equal(uuid, &CXL_EVENT_MEM_MODULE_UUID)) 867 + ev_type = CXL_CPER_EVENT_MEM_MODULE; 877 868 878 - trace_cxl_dram(cxlmd, type, rec); 879 - } else if (uuid_equal(id, &mem_mod_event_uuid)) { 880 - struct cxl_event_mem_module *rec = 881 - (struct cxl_event_mem_module *)record; 882 - 883 - trace_cxl_memory_module(cxlmd, type, rec); 884 - } else { 885 - /* For unknown record types print just the header */ 886 - trace_cxl_generic_event(cxlmd, type, record); 887 - } 869 + cxl_event_trace_record(cxlmd, type, ev_type, uuid, &record->event); 888 870 } 889 871 890 872 static int cxl_clear_event_record(struct cxl_memdev_state *mds, ··· 910 926 */ 911 927 i = 0; 912 928 for (cnt = 0; cnt < total; cnt++) { 913 - payload->handles[i++] = get_pl->records[cnt].hdr.handle; 929 + struct cxl_event_record_raw *raw = &get_pl->records[cnt]; 930 + struct cxl_event_generic *gen = &raw->event.generic; 931 + 932 + payload->handles[i++] = gen->hdr.handle; 914 933 dev_dbg(mds->cxlds.dev, "Event log '%d': Clearing %u\n", log, 915 934 le16_to_cpu(payload->handles[i])); 916 935 ··· 978 991 break; 979 992 980 993 for (i = 0; i < nr_rec; i++) 981 - cxl_event_trace_record(cxlmd, type, 982 - &payload->records[i]); 994 + __cxl_event_trace_record(cxlmd, type, 995 + &payload->records[i]); 983 996 984 997 if (payload->flags & CXL_GET_EVENT_FLAG_OVERFLOW) 985 998 trace_cxl_overflow(cxlmd, type, payload); ··· 1391 1404 mds->cxlds.reg_map.host = dev; 1392 1405 mds->cxlds.reg_map.resource = CXL_RESOURCE_NONE; 1393 1406 mds->cxlds.type = CXL_DEVTYPE_CLASSMEM; 1407 + INIT_LIST_HEAD(&mds->ram_perf_list); 1408 + INIT_LIST_HEAD(&mds->pmem_perf_list); 1394 1409 1395 1410 return mds; 1396 1411 }
+1 -1
drivers/cxl/core/memdev.c
··· 114 114 static ssize_t numa_node_show(struct device *dev, struct device_attribute *attr, 115 115 char *buf) 116 116 { 117 - return sprintf(buf, "%d\n", dev_to_node(dev)); 117 + return sysfs_emit(buf, "%d\n", dev_to_node(dev)); 118 118 } 119 119 static DEVICE_ATTR_RO(numa_node); 120 120
+36
drivers/cxl/core/pci.c
··· 1 1 // SPDX-License-Identifier: GPL-2.0-only 2 2 /* Copyright(c) 2021 Intel Corporation. All rights reserved. */ 3 + #include <linux/units.h> 3 4 #include <linux/io-64-nonatomic-lo-hi.h> 4 5 #include <linux/device.h> 5 6 #include <linux/delay.h> ··· 980 979 return PCI_ERS_RESULT_NEED_RESET; 981 980 } 982 981 EXPORT_SYMBOL_NS_GPL(cxl_error_detected, CXL); 982 + 983 + static int cxl_flit_size(struct pci_dev *pdev) 984 + { 985 + if (cxl_pci_flit_256(pdev)) 986 + return 256; 987 + 988 + return 68; 989 + } 990 + 991 + /** 992 + * cxl_pci_get_latency - calculate the link latency for the PCIe link 993 + * @pdev: PCI device 994 + * 995 + * return: calculated latency or 0 for no latency 996 + * 997 + * CXL Memory Device SW Guide v1.0 2.11.4 Link latency calculation 998 + * Link latency = LinkPropagationLatency + FlitLatency + RetimerLatency 999 + * LinkProgationLatency is negligible, so 0 will be used 1000 + * RetimerLatency is assumed to be negligible and 0 will be used 1001 + * FlitLatency = FlitSize / LinkBandwidth 1002 + * FlitSize is defined by spec. CXL rev3.0 4.2.1. 1003 + * 68B flit is used up to 32GT/s. >32GT/s, 256B flit size is used. 1004 + * The FlitLatency is converted to picoseconds. 1005 + */ 1006 + long cxl_pci_get_latency(struct pci_dev *pdev) 1007 + { 1008 + long bw; 1009 + 1010 + bw = pcie_link_speed_mbps(pdev); 1011 + if (bw < 0) 1012 + return 0; 1013 + bw /= BITS_PER_BYTE; 1014 + 1015 + return cxl_flit_size(pdev) * MEGA / bw; 1016 + }
+4 -4
drivers/cxl/core/pmem.c
··· 64 64 65 65 struct cxl_nvdimm_bridge *cxl_find_nvdimm_bridge(struct cxl_memdev *cxlmd) 66 66 { 67 - struct cxl_port *port = find_cxl_root(cxlmd->endpoint); 67 + struct cxl_root *cxl_root __free(put_cxl_root) = 68 + find_cxl_root(cxlmd->endpoint); 68 69 struct device *dev; 69 70 70 - if (!port) 71 + if (!cxl_root) 71 72 return NULL; 72 73 73 - dev = device_find_child(&port->dev, NULL, match_nvdimm_bridge); 74 - put_device(&port->dev); 74 + dev = device_find_child(&cxl_root->port.dev, NULL, match_nvdimm_bridge); 75 75 76 76 if (!dev) 77 77 return NULL;
+139 -28
drivers/cxl/core/port.c
··· 9 9 #include <linux/pci.h> 10 10 #include <linux/slab.h> 11 11 #include <linux/idr.h> 12 + #include <linux/node.h> 12 13 #include <cxlmem.h> 13 14 #include <cxlpci.h> 14 15 #include <cxl.h> ··· 173 172 { 174 173 struct cxl_switch_decoder *cxlsd = to_cxl_switch_decoder(dev); 175 174 ssize_t offset; 176 - unsigned int seq; 177 175 int rc; 178 176 179 - do { 180 - seq = read_seqbegin(&cxlsd->target_lock); 181 - rc = emit_target_list(cxlsd, buf); 182 - } while (read_seqretry(&cxlsd->target_lock, seq)); 183 - 177 + guard(rwsem_read)(&cxl_region_rwsem); 178 + rc = emit_target_list(cxlsd, buf); 184 179 if (rc < 0) 185 180 return rc; 186 181 offset = rc; ··· 538 541 xa_destroy(&port->dports); 539 542 xa_destroy(&port->regions); 540 543 ida_free(&cxl_port_ida, port->id); 541 - kfree(port); 544 + if (is_cxl_root(port)) 545 + kfree(to_cxl_root(port)); 546 + else 547 + kfree(port); 542 548 } 543 549 544 550 static ssize_t decoders_committed_show(struct device *dev, ··· 669 669 static struct cxl_port *cxl_port_alloc(struct device *uport_dev, 670 670 struct cxl_dport *parent_dport) 671 671 { 672 - struct cxl_port *port; 672 + struct cxl_root *cxl_root __free(kfree) = NULL; 673 + struct cxl_port *port, *_port __free(kfree) = NULL; 673 674 struct device *dev; 674 675 int rc; 675 676 676 - port = kzalloc(sizeof(*port), GFP_KERNEL); 677 - if (!port) 678 - return ERR_PTR(-ENOMEM); 677 + /* No parent_dport, root cxl_port */ 678 + if (!parent_dport) { 679 + cxl_root = kzalloc(sizeof(*cxl_root), GFP_KERNEL); 680 + if (!cxl_root) 681 + return ERR_PTR(-ENOMEM); 682 + } else { 683 + _port = kzalloc(sizeof(*port), GFP_KERNEL); 684 + if (!_port) 685 + return ERR_PTR(-ENOMEM); 686 + } 679 687 680 688 rc = ida_alloc(&cxl_port_ida, GFP_KERNEL); 681 689 if (rc < 0) 682 - goto err; 690 + return ERR_PTR(rc); 691 + 692 + if (cxl_root) 693 + port = &no_free_ptr(cxl_root)->port; 694 + else 695 + port = no_free_ptr(_port); 696 + 683 697 port->id = rc; 684 698 port->uport_dev = uport_dev; 685 699 ··· 745 731 dev->type = &cxl_port_type; 746 732 747 733 return port; 748 - 749 - err: 750 - kfree(port); 751 - return ERR_PTR(rc); 752 734 } 753 735 754 736 static int cxl_setup_comp_regs(struct device *host, struct cxl_register_map *map, ··· 851 841 if (rc) 852 842 return ERR_PTR(rc); 853 843 844 + if (parent_dport && dev_is_pci(uport_dev)) 845 + port->pci_latency = cxl_pci_get_latency(to_pci_dev(uport_dev)); 846 + 854 847 return port; 855 848 856 849 err: ··· 896 883 return port; 897 884 } 898 885 EXPORT_SYMBOL_NS_GPL(devm_cxl_add_port, CXL); 886 + 887 + struct cxl_root *devm_cxl_add_root(struct device *host, 888 + const struct cxl_root_ops *ops) 889 + { 890 + struct cxl_root *cxl_root; 891 + struct cxl_port *port; 892 + 893 + port = devm_cxl_add_port(host, host, CXL_RESOURCE_NONE, NULL); 894 + if (IS_ERR(port)) 895 + return (struct cxl_root *)port; 896 + 897 + cxl_root = to_cxl_root(port); 898 + cxl_root->ops = ops; 899 + return cxl_root; 900 + } 901 + EXPORT_SYMBOL_NS_GPL(devm_cxl_add_root, CXL); 899 902 900 903 struct pci_bus *cxl_port_to_pci_bus(struct cxl_port *port) 901 904 { ··· 968 939 return false; 969 940 } 970 941 971 - struct cxl_port *find_cxl_root(struct cxl_port *port) 942 + struct cxl_root *find_cxl_root(struct cxl_port *port) 972 943 { 973 944 struct cxl_port *iter = port; 974 945 ··· 978 949 if (!iter) 979 950 return NULL; 980 951 get_device(&iter->dev); 981 - return iter; 952 + return to_cxl_root(iter); 982 953 } 983 954 EXPORT_SYMBOL_NS_GPL(find_cxl_root, CXL); 955 + 956 + void put_cxl_root(struct cxl_root *cxl_root) 957 + { 958 + if (!cxl_root) 959 + return; 960 + 961 + put_device(&cxl_root->port.dev); 962 + } 963 + EXPORT_SYMBOL_NS_GPL(put_cxl_root, CXL); 984 964 985 965 static struct cxl_dport *find_dport(struct cxl_port *port, int id) 986 966 { ··· 1145 1107 rc = devm_add_action_or_reset(host, cxl_dport_unlink, dport); 1146 1108 if (rc) 1147 1109 return ERR_PTR(rc); 1110 + 1111 + if (dev_is_pci(dport_dev)) 1112 + dport->link_latency = cxl_pci_get_latency(to_pci_dev(dport_dev)); 1148 1113 1149 1114 return dport; 1150 1115 } ··· 1674 1633 static int decoder_populate_targets(struct cxl_switch_decoder *cxlsd, 1675 1634 struct cxl_port *port, int *target_map) 1676 1635 { 1677 - int i, rc = 0; 1636 + int i; 1678 1637 1679 1638 if (!target_map) 1680 1639 return 0; ··· 1684 1643 if (xa_empty(&port->dports)) 1685 1644 return -EINVAL; 1686 1645 1687 - write_seqlock(&cxlsd->target_lock); 1688 - for (i = 0; i < cxlsd->nr_targets; i++) { 1646 + guard(rwsem_write)(&cxl_region_rwsem); 1647 + for (i = 0; i < cxlsd->cxld.interleave_ways; i++) { 1689 1648 struct cxl_dport *dport = find_dport(port, target_map[i]); 1690 1649 1691 - if (!dport) { 1692 - rc = -ENXIO; 1693 - break; 1694 - } 1650 + if (!dport) 1651 + return -ENXIO; 1695 1652 cxlsd->target[i] = dport; 1696 1653 } 1697 - write_sequnlock(&cxlsd->target_lock); 1698 1654 1699 - return rc; 1655 + return 0; 1700 1656 } 1701 1657 1702 1658 struct cxl_dport *cxl_hb_modulo(struct cxl_root_decoder *cxlrd, int pos) ··· 1763 1725 return -EINVAL; 1764 1726 1765 1727 cxlsd->nr_targets = nr_targets; 1766 - seqlock_init(&cxlsd->target_lock); 1767 1728 return cxl_decoder_init(port, &cxlsd->cxld); 1768 1729 } 1769 1730 ··· 2095 2058 return queue_work(cxl_bus_wq, &cxlmd->detach_work); 2096 2059 } 2097 2060 EXPORT_SYMBOL_NS_GPL(schedule_cxl_memdev_detach, CXL); 2061 + 2062 + static void combine_coordinates(struct access_coordinate *c1, 2063 + struct access_coordinate *c2) 2064 + { 2065 + if (c2->write_bandwidth) 2066 + c1->write_bandwidth = min(c1->write_bandwidth, 2067 + c2->write_bandwidth); 2068 + c1->write_latency += c2->write_latency; 2069 + 2070 + if (c2->read_bandwidth) 2071 + c1->read_bandwidth = min(c1->read_bandwidth, 2072 + c2->read_bandwidth); 2073 + c1->read_latency += c2->read_latency; 2074 + } 2075 + 2076 + /** 2077 + * cxl_endpoint_get_perf_coordinates - Retrieve performance numbers stored in dports 2078 + * of CXL path 2079 + * @port: endpoint cxl_port 2080 + * @coord: output performance data 2081 + * 2082 + * Return: errno on failure, 0 on success. 2083 + */ 2084 + int cxl_endpoint_get_perf_coordinates(struct cxl_port *port, 2085 + struct access_coordinate *coord) 2086 + { 2087 + struct access_coordinate c = { 2088 + .read_bandwidth = UINT_MAX, 2089 + .write_bandwidth = UINT_MAX, 2090 + }; 2091 + struct cxl_port *iter = port; 2092 + struct cxl_dport *dport; 2093 + struct pci_dev *pdev; 2094 + unsigned int bw; 2095 + 2096 + if (!is_cxl_endpoint(port)) 2097 + return -EINVAL; 2098 + 2099 + dport = iter->parent_dport; 2100 + 2101 + /* 2102 + * Exit the loop when the parent port of the current port is cxl root. 2103 + * The iterative loop starts at the endpoint and gathers the 2104 + * latency of the CXL link from the current iter to the next downstream 2105 + * port each iteration. If the parent is cxl root then there is 2106 + * nothing to gather. 2107 + */ 2108 + while (iter && !is_cxl_root(to_cxl_port(iter->dev.parent))) { 2109 + combine_coordinates(&c, &dport->sw_coord); 2110 + c.write_latency += dport->link_latency; 2111 + c.read_latency += dport->link_latency; 2112 + 2113 + iter = to_cxl_port(iter->dev.parent); 2114 + dport = iter->parent_dport; 2115 + } 2116 + 2117 + /* Augment with the generic port (host bridge) perf data */ 2118 + combine_coordinates(&c, &dport->hb_coord); 2119 + 2120 + /* Get the calculated PCI paths bandwidth */ 2121 + pdev = to_pci_dev(port->uport_dev->parent); 2122 + bw = pcie_bandwidth_available(pdev, NULL, NULL, NULL); 2123 + if (bw == 0) 2124 + return -ENXIO; 2125 + bw /= BITS_PER_BYTE; 2126 + 2127 + c.write_bandwidth = min(c.write_bandwidth, bw); 2128 + c.read_bandwidth = min(c.read_bandwidth, bw); 2129 + 2130 + *coord = c; 2131 + 2132 + return 0; 2133 + } 2134 + EXPORT_SYMBOL_NS_GPL(cxl_endpoint_get_perf_coordinates, CXL); 2098 2135 2099 2136 /* for user tooling to ensure port disable work has completed */ 2100 2137 static ssize_t flush_store(const struct bus_type *bus, const char *buf, size_t count)
+8 -7
drivers/cxl/core/region.c
··· 397 397 return rc; 398 398 399 399 /* 400 - * Even for x3, x9, and x12 interleaves the region interleave must be a 400 + * Even for x3, x6, and x12 interleaves the region interleave must be a 401 401 * power of 2 multiple of the host bridge interleave. 402 402 */ 403 403 if (!is_power_of_2(val / cxld->interleave_ways) || ··· 552 552 res = alloc_free_mem_region(cxlrd->res, size, SZ_256M, 553 553 dev_name(&cxlr->dev)); 554 554 if (IS_ERR(res)) { 555 - dev_dbg(&cxlr->dev, "failed to allocate HPA: %ld\n", 556 - PTR_ERR(res)); 555 + dev_dbg(&cxlr->dev, 556 + "HPA allocation error (%ld) for size:%pap in %s %pr\n", 557 + PTR_ERR(res), &size, cxlrd->res->name, cxlrd->res); 557 558 return PTR_ERR(res); 558 559 } 559 560 ··· 2084 2083 return container_of(dev, struct cxl_region, dev); 2085 2084 } 2086 2085 2087 - static void unregister_region(void *dev) 2086 + static void unregister_region(void *_cxlr) 2088 2087 { 2089 - struct cxl_region *cxlr = to_cxl_region(dev); 2088 + struct cxl_region *cxlr = _cxlr; 2090 2089 struct cxl_region_params *p = &cxlr->params; 2091 2090 int i; 2092 2091 2093 - device_del(dev); 2092 + device_del(&cxlr->dev); 2094 2093 2095 2094 /* 2096 2095 * Now that region sysfs is shutdown, the parameter block is now ··· 2101 2100 detach_target(cxlr, i); 2102 2101 2103 2102 cxl_region_iomem_release(cxlr); 2104 - put_device(dev); 2103 + put_device(&cxlr->dev); 2105 2104 } 2106 2105 2107 2106 static struct lock_class_key cxl_region_key;
+9 -5
drivers/cxl/core/trace.h
··· 181 181 * 1) Add CXL_EVT_TP_entry to TP_STRUCT__entry 182 182 * 2) Use CXL_EVT_TP_fast_assign within TP_fast_assign; 183 183 * pass the dev, log, and CXL event header 184 + * NOTE: The uuid must be assigned by the specific trace event 184 185 * 3) Use CXL_EVT_TP_printk() instead of TP_printk() 185 186 * 186 187 * See the generic_event tracepoint as an example. ··· 204 203 __assign_str(host, dev_name((cxlmd)->dev.parent)); \ 205 204 __entry->log = (l); \ 206 205 __entry->serial = (cxlmd)->cxlds->serial; \ 207 - memcpy(&__entry->hdr_uuid, &(hdr).id, sizeof(uuid_t)); \ 208 206 __entry->hdr_length = (hdr).length; \ 209 207 __entry->hdr_flags = get_unaligned_le24((hdr).flags); \ 210 208 __entry->hdr_handle = le16_to_cpu((hdr).handle); \ ··· 225 225 TRACE_EVENT(cxl_generic_event, 226 226 227 227 TP_PROTO(const struct cxl_memdev *cxlmd, enum cxl_event_log_type log, 228 - struct cxl_event_record_raw *rec), 228 + const uuid_t *uuid, struct cxl_event_generic *gen_rec), 229 229 230 - TP_ARGS(cxlmd, log, rec), 230 + TP_ARGS(cxlmd, log, uuid, gen_rec), 231 231 232 232 TP_STRUCT__entry( 233 233 CXL_EVT_TP_entry ··· 235 235 ), 236 236 237 237 TP_fast_assign( 238 - CXL_EVT_TP_fast_assign(cxlmd, log, rec->hdr); 239 - memcpy(__entry->data, &rec->data, CXL_EVENT_RECORD_DATA_LENGTH); 238 + CXL_EVT_TP_fast_assign(cxlmd, log, gen_rec->hdr); 239 + memcpy(&__entry->hdr_uuid, uuid, sizeof(uuid_t)); 240 + memcpy(__entry->data, gen_rec->data, CXL_EVENT_RECORD_DATA_LENGTH); 240 241 ), 241 242 242 243 CXL_EVT_TP_printk("%s", ··· 338 337 339 338 TP_fast_assign( 340 339 CXL_EVT_TP_fast_assign(cxlmd, log, rec->hdr); 340 + memcpy(&__entry->hdr_uuid, &CXL_EVENT_GEN_MEDIA_UUID, sizeof(uuid_t)); 341 341 342 342 /* General Media */ 343 343 __entry->dpa = le64_to_cpu(rec->phys_addr); ··· 425 423 426 424 TP_fast_assign( 427 425 CXL_EVT_TP_fast_assign(cxlmd, log, rec->hdr); 426 + memcpy(&__entry->hdr_uuid, &CXL_EVENT_DRAM_UUID, sizeof(uuid_t)); 428 427 429 428 /* DRAM */ 430 429 __entry->dpa = le64_to_cpu(rec->phys_addr); ··· 573 570 574 571 TP_fast_assign( 575 572 CXL_EVT_TP_fast_assign(cxlmd, log, rec->hdr); 573 + memcpy(&__entry->hdr_uuid, &CXL_EVENT_MEM_MODULE_UUID, sizeof(uuid_t)); 576 574 577 575 /* Memory Module Event */ 578 576 __entry->event_type = rec->event_type;
+44 -3
drivers/cxl/cxl.h
··· 8 8 #include <linux/bitfield.h> 9 9 #include <linux/bitops.h> 10 10 #include <linux/log2.h> 11 + #include <linux/node.h> 11 12 #include <linux/io.h> 12 13 13 14 /** ··· 413 412 /** 414 413 * struct cxl_switch_decoder - Switch specific CXL HDM Decoder 415 414 * @cxld: base cxl_decoder object 416 - * @target_lock: coordinate coherent reads of the target list 417 415 * @nr_targets: number of elements in @target 418 416 * @target: active ordered target list in current decoder configuration 419 417 * ··· 424 424 */ 425 425 struct cxl_switch_decoder { 426 426 struct cxl_decoder cxld; 427 - seqlock_t target_lock; 428 427 int nr_targets; 429 428 struct cxl_dport *target[]; 430 429 }; ··· 589 590 * @depth: How deep this port is relative to the root. depth 0 is the root. 590 591 * @cdat: Cached CDAT data 591 592 * @cdat_available: Should a CDAT attribute be available in sysfs 593 + * @pci_latency: Upstream latency in picoseconds 592 594 */ 593 595 struct cxl_port { 594 596 struct device dev; ··· 612 612 size_t length; 613 613 } cdat; 614 614 bool cdat_available; 615 + long pci_latency; 616 + }; 617 + 618 + /** 619 + * struct cxl_root - logical collection of root cxl_port items 620 + * 621 + * @port: cxl_port member 622 + * @ops: cxl root operations 623 + */ 624 + struct cxl_root { 625 + struct cxl_port port; 626 + const struct cxl_root_ops *ops; 627 + }; 628 + 629 + static inline struct cxl_root * 630 + to_cxl_root(const struct cxl_port *port) 631 + { 632 + return container_of(port, struct cxl_root, port); 633 + } 634 + 635 + struct cxl_root_ops { 636 + int (*qos_class)(struct cxl_root *cxl_root, 637 + struct access_coordinate *coord, int entries, 638 + int *qos_class); 615 639 }; 616 640 617 641 static inline struct cxl_dport * ··· 658 634 * @rch: Indicate whether this dport was enumerated in RCH or VH mode 659 635 * @port: reference to cxl_port that contains this downstream port 660 636 * @regs: Dport parsed register blocks 637 + * @sw_coord: access coordinates (performance) for switch from CDAT 638 + * @hb_coord: access coordinates (performance) from ACPI generic port (host bridge) 639 + * @link_latency: calculated PCIe downstream latency 661 640 */ 662 641 struct cxl_dport { 663 642 struct device *dport_dev; ··· 670 643 bool rch; 671 644 struct cxl_port *port; 672 645 struct cxl_regs regs; 646 + struct access_coordinate sw_coord; 647 + struct access_coordinate hb_coord; 648 + long link_latency; 673 649 }; 674 650 675 651 /** ··· 730 700 struct device *uport_dev, 731 701 resource_size_t component_reg_phys, 732 702 struct cxl_dport *parent_dport); 733 - struct cxl_port *find_cxl_root(struct cxl_port *port); 703 + struct cxl_root *devm_cxl_add_root(struct device *host, 704 + const struct cxl_root_ops *ops); 705 + struct cxl_root *find_cxl_root(struct cxl_port *port); 706 + void put_cxl_root(struct cxl_root *cxl_root); 707 + DEFINE_FREE(put_cxl_root, struct cxl_root *, if (_T) put_cxl_root(_T)) 708 + 734 709 int devm_cxl_enumerate_ports(struct cxl_memdev *cxlmd); 735 710 void cxl_bus_rescan(void); 736 711 void cxl_bus_drain(void); ··· 873 838 return NULL; 874 839 } 875 840 #endif 841 + 842 + void cxl_endpoint_parse_cdat(struct cxl_port *port); 843 + void cxl_switch_parse_cdat(struct cxl_port *port); 844 + 845 + int cxl_endpoint_get_perf_coordinates(struct cxl_port *port, 846 + struct access_coordinate *coord); 876 847 877 848 /* 878 849 * Unit test builds overrides this to __weak, find the 'strong' version
+47 -85
drivers/cxl/cxlmem.h
··· 6 6 #include <linux/cdev.h> 7 7 #include <linux/uuid.h> 8 8 #include <linux/rcuwait.h> 9 + #include <linux/cxl-event.h> 10 + #include <linux/node.h> 9 11 #include "cxl.h" 10 12 11 13 /* CXL 2.0 8.2.8.5.1.1 Memory Device Status Register */ ··· 394 392 }; 395 393 396 394 /** 395 + * struct cxl_dpa_perf - DPA performance property entry 396 + * @list - list entry 397 + * @dpa_range - range for DPA address 398 + * @coord - QoS performance data (i.e. latency, bandwidth) 399 + * @qos_class - QoS Class cookies 400 + */ 401 + struct cxl_dpa_perf { 402 + struct list_head list; 403 + struct range dpa_range; 404 + struct access_coordinate coord; 405 + int qos_class; 406 + }; 407 + 408 + /** 397 409 * struct cxl_dev_state - The driver device state 398 410 * 399 411 * cxl_dev_state represents the CXL driver/device state. It provides an ··· 471 455 * @security: security driver state info 472 456 * @fw: firmware upload / activation state 473 457 * @mbox_send: @dev specific transport for transmitting mailbox commands 458 + * @ram_perf_list: performance data entries matched to RAM 459 + * @pmem_perf_list: performance data entries matched to PMEM 474 460 * 475 461 * See CXL 3.0 8.2.9.8.2 Capacity Configuration and Label Storage for 476 462 * details on capacity parameters. ··· 493 475 u64 active_persistent_bytes; 494 476 u64 next_volatile_bytes; 495 477 u64 next_persistent_bytes; 478 + 479 + struct list_head ram_perf_list; 480 + struct list_head pmem_perf_list; 481 + 496 482 struct cxl_event_state event; 497 483 struct cxl_poison_state poison; 498 484 struct cxl_security_state security; ··· 525 503 CXL_MBOX_OP_GET_FW_INFO = 0x0200, 526 504 CXL_MBOX_OP_TRANSFER_FW = 0x0201, 527 505 CXL_MBOX_OP_ACTIVATE_FW = 0x0202, 506 + CXL_MBOX_OP_GET_TIMESTAMP = 0x0300, 528 507 CXL_MBOX_OP_SET_TIMESTAMP = 0x0301, 529 508 CXL_MBOX_OP_GET_SUPPORTED_LOGS = 0x0400, 530 509 CXL_MBOX_OP_GET_LOG = 0x0401, ··· 603 580 } __packed; 604 581 605 582 /* 606 - * Common Event Record Format 607 - * CXL rev 3.0 section 8.2.9.2.1; Table 8-42 583 + * General Media Event Record UUID 584 + * CXL rev 3.0 Section 8.2.9.2.1.1; Table 8-43 608 585 */ 609 - struct cxl_event_record_hdr { 610 - uuid_t id; 611 - u8 length; 612 - u8 flags[3]; 613 - __le16 handle; 614 - __le16 related_handle; 615 - __le64 timestamp; 616 - u8 maint_op_class; 617 - u8 reserved[15]; 618 - } __packed; 586 + #define CXL_EVENT_GEN_MEDIA_UUID \ 587 + UUID_INIT(0xfbcd0a77, 0xc260, 0x417f, 0x85, 0xa9, 0x08, 0x8b, 0x16, \ 588 + 0x21, 0xeb, 0xa6) 619 589 620 - #define CXL_EVENT_RECORD_DATA_LENGTH 0x50 621 - struct cxl_event_record_raw { 622 - struct cxl_event_record_hdr hdr; 623 - u8 data[CXL_EVENT_RECORD_DATA_LENGTH]; 624 - } __packed; 590 + /* 591 + * DRAM Event Record UUID 592 + * CXL rev 3.0 section 8.2.9.2.1.2; Table 8-44 593 + */ 594 + #define CXL_EVENT_DRAM_UUID \ 595 + UUID_INIT(0x601dcbb3, 0x9c06, 0x4eab, 0xb8, 0xaf, 0x4e, 0x9b, 0xfb, \ 596 + 0x5c, 0x96, 0x24) 597 + 598 + /* 599 + * Memory Module Event Record UUID 600 + * CXL rev 3.0 section 8.2.9.2.1.3; Table 8-45 601 + */ 602 + #define CXL_EVENT_MEM_MODULE_UUID \ 603 + UUID_INIT(0xfe927475, 0xdd59, 0x4339, 0xa5, 0x86, 0x79, 0xba, 0xb1, \ 604 + 0x13, 0xb7, 0x74) 625 605 626 606 /* 627 607 * Get Event Records output payload ··· 666 640 __le16 handles[]; 667 641 } __packed; 668 642 #define CXL_CLEAR_EVENT_MAX_HANDLES U8_MAX 669 - 670 - /* 671 - * General Media Event Record 672 - * CXL rev 3.0 Section 8.2.9.2.1.1; Table 8-43 673 - */ 674 - #define CXL_EVENT_GEN_MED_COMP_ID_SIZE 0x10 675 - struct cxl_event_gen_media { 676 - struct cxl_event_record_hdr hdr; 677 - __le64 phys_addr; 678 - u8 descriptor; 679 - u8 type; 680 - u8 transaction_type; 681 - u8 validity_flags[2]; 682 - u8 channel; 683 - u8 rank; 684 - u8 device[3]; 685 - u8 component_id[CXL_EVENT_GEN_MED_COMP_ID_SIZE]; 686 - u8 reserved[46]; 687 - } __packed; 688 - 689 - /* 690 - * DRAM Event Record - DER 691 - * CXL rev 3.0 section 8.2.9.2.1.2; Table 3-44 692 - */ 693 - #define CXL_EVENT_DER_CORRECTION_MASK_SIZE 0x20 694 - struct cxl_event_dram { 695 - struct cxl_event_record_hdr hdr; 696 - __le64 phys_addr; 697 - u8 descriptor; 698 - u8 type; 699 - u8 transaction_type; 700 - u8 validity_flags[2]; 701 - u8 channel; 702 - u8 rank; 703 - u8 nibble_mask[3]; 704 - u8 bank_group; 705 - u8 bank; 706 - u8 row[3]; 707 - u8 column[2]; 708 - u8 correction_mask[CXL_EVENT_DER_CORRECTION_MASK_SIZE]; 709 - u8 reserved[0x17]; 710 - } __packed; 711 - 712 - /* 713 - * Get Health Info Record 714 - * CXL rev 3.0 section 8.2.9.8.3.1; Table 8-100 715 - */ 716 - struct cxl_get_health_info { 717 - u8 health_status; 718 - u8 media_status; 719 - u8 add_status; 720 - u8 life_used; 721 - u8 device_temp[2]; 722 - u8 dirty_shutdown_cnt[4]; 723 - u8 cor_vol_err_cnt[4]; 724 - u8 cor_per_err_cnt[4]; 725 - } __packed; 726 - 727 - /* 728 - * Memory Module Event Record 729 - * CXL rev 3.0 section 8.2.9.2.1.3; Table 8-45 730 - */ 731 - struct cxl_event_mem_module { 732 - struct cxl_event_record_hdr hdr; 733 - u8 event_type; 734 - struct cxl_get_health_info info; 735 - u8 reserved[0x3d]; 736 - } __packed; 737 643 738 644 struct cxl_mbox_get_partition_info { 739 645 __le64 active_volatile_cap; ··· 824 866 void clear_exclusive_cxl_commands(struct cxl_memdev_state *mds, 825 867 unsigned long *cmds); 826 868 void cxl_mem_get_event_records(struct cxl_memdev_state *mds, u32 status); 869 + void cxl_event_trace_record(const struct cxl_memdev *cxlmd, 870 + enum cxl_event_log_type type, 871 + enum cxl_event_type event_type, 872 + const uuid_t *uuid, union cxl_event *evt); 827 873 int cxl_set_timestamp(struct cxl_memdev_state *mds); 828 874 int cxl_poison_state_init(struct cxl_memdev_state *mds); 829 875 int cxl_mem_get_poison(struct cxl_memdev *cxlmd, u64 offset, u64 len,
+13
drivers/cxl/cxlpci.h
··· 85 85 __le16 length; 86 86 } __packed; 87 87 88 + /* 89 + * CXL v3.0 6.2.3 Table 6-4 90 + * The table indicates that if PCIe Flit Mode is set, then CXL is in 256B flits 91 + * mode, otherwise it's 68B flits mode. 92 + */ 93 + static inline bool cxl_pci_flit_256(struct pci_dev *pdev) 94 + { 95 + u16 lnksta2; 96 + 97 + pcie_capability_read_word(pdev, PCI_EXP_LNKSTA2, &lnksta2); 98 + return lnksta2 & PCI_EXP_LNKSTA2_FLIT; 99 + } 100 + 88 101 int devm_cxl_port_enumerate_dports(struct cxl_port *port); 89 102 struct cxl_dev_state; 90 103 int cxl_hdm_decode_init(struct cxl_dev_state *cxlds, struct cxl_hdm *cxlhdm,
+61 -6
drivers/cxl/mem.c
··· 215 215 } 216 216 static DEVICE_ATTR_WO(trigger_poison_list); 217 217 218 + static ssize_t ram_qos_class_show(struct device *dev, 219 + struct device_attribute *attr, char *buf) 220 + { 221 + struct cxl_memdev *cxlmd = to_cxl_memdev(dev); 222 + struct cxl_dev_state *cxlds = cxlmd->cxlds; 223 + struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlds); 224 + struct cxl_dpa_perf *dpa_perf; 225 + 226 + if (!dev->driver) 227 + return -ENOENT; 228 + 229 + if (list_empty(&mds->ram_perf_list)) 230 + return -ENOENT; 231 + 232 + dpa_perf = list_first_entry(&mds->ram_perf_list, struct cxl_dpa_perf, 233 + list); 234 + 235 + return sysfs_emit(buf, "%d\n", dpa_perf->qos_class); 236 + } 237 + 238 + static struct device_attribute dev_attr_ram_qos_class = 239 + __ATTR(qos_class, 0444, ram_qos_class_show, NULL); 240 + 241 + static ssize_t pmem_qos_class_show(struct device *dev, 242 + struct device_attribute *attr, char *buf) 243 + { 244 + struct cxl_memdev *cxlmd = to_cxl_memdev(dev); 245 + struct cxl_dev_state *cxlds = cxlmd->cxlds; 246 + struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlds); 247 + struct cxl_dpa_perf *dpa_perf; 248 + 249 + if (!dev->driver) 250 + return -ENOENT; 251 + 252 + if (list_empty(&mds->pmem_perf_list)) 253 + return -ENOENT; 254 + 255 + dpa_perf = list_first_entry(&mds->pmem_perf_list, struct cxl_dpa_perf, 256 + list); 257 + 258 + return sysfs_emit(buf, "%d\n", dpa_perf->qos_class); 259 + } 260 + 261 + static struct device_attribute dev_attr_pmem_qos_class = 262 + __ATTR(qos_class, 0444, pmem_qos_class_show, NULL); 263 + 218 264 static umode_t cxl_mem_visible(struct kobject *kobj, struct attribute *a, int n) 219 265 { 220 - if (a == &dev_attr_trigger_poison_list.attr) { 221 - struct device *dev = kobj_to_dev(kobj); 222 - struct cxl_memdev *cxlmd = to_cxl_memdev(dev); 223 - struct cxl_memdev_state *mds = 224 - to_cxl_memdev_state(cxlmd->cxlds); 266 + struct device *dev = kobj_to_dev(kobj); 267 + struct cxl_memdev *cxlmd = to_cxl_memdev(dev); 268 + struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlmd->cxlds); 225 269 270 + if (a == &dev_attr_trigger_poison_list.attr) 226 271 if (!test_bit(CXL_POISON_ENABLED_LIST, 227 272 mds->poison.enabled_cmds)) 228 273 return 0; 229 - } 274 + 275 + if (a == &dev_attr_pmem_qos_class.attr) 276 + if (list_empty(&mds->pmem_perf_list)) 277 + return 0; 278 + 279 + if (a == &dev_attr_ram_qos_class.attr) 280 + if (list_empty(&mds->ram_perf_list)) 281 + return 0; 282 + 230 283 return a->mode; 231 284 } 232 285 233 286 static struct attribute *cxl_mem_attrs[] = { 234 287 &dev_attr_trigger_poison_list.attr, 288 + &dev_attr_ram_qos_class.attr, 289 + &dev_attr_pmem_qos_class.attr, 235 290 NULL 236 291 }; 237 292
+57 -1
drivers/cxl/pci.c
··· 1 1 // SPDX-License-Identifier: GPL-2.0-only 2 2 /* Copyright(c) 2020 Intel Corporation. All rights reserved. */ 3 + #include <asm-generic/unaligned.h> 3 4 #include <linux/io-64-nonatomic-lo-hi.h> 4 5 #include <linux/moduleparam.h> 5 6 #include <linux/module.h> ··· 970 969 }, 971 970 }; 972 971 972 + #define CXL_EVENT_HDR_FLAGS_REC_SEVERITY GENMASK(1, 0) 973 + static void cxl_cper_event_call(enum cxl_event_type ev_type, 974 + struct cxl_cper_event_rec *rec) 975 + { 976 + struct cper_cxl_event_devid *device_id = &rec->hdr.device_id; 977 + struct pci_dev *pdev __free(pci_dev_put) = NULL; 978 + enum cxl_event_log_type log_type; 979 + struct cxl_dev_state *cxlds; 980 + unsigned int devfn; 981 + u32 hdr_flags; 982 + 983 + devfn = PCI_DEVFN(device_id->device_num, device_id->func_num); 984 + pdev = pci_get_domain_bus_and_slot(device_id->segment_num, 985 + device_id->bus_num, devfn); 986 + if (!pdev) 987 + return; 988 + 989 + guard(pci_dev)(pdev); 990 + if (pdev->driver != &cxl_pci_driver) 991 + return; 992 + 993 + cxlds = pci_get_drvdata(pdev); 994 + if (!cxlds) 995 + return; 996 + 997 + /* Fabricate a log type */ 998 + hdr_flags = get_unaligned_le24(rec->event.generic.hdr.flags); 999 + log_type = FIELD_GET(CXL_EVENT_HDR_FLAGS_REC_SEVERITY, hdr_flags); 1000 + 1001 + cxl_event_trace_record(cxlds->cxlmd, log_type, ev_type, 1002 + &uuid_null, &rec->event); 1003 + } 1004 + 1005 + static int __init cxl_pci_driver_init(void) 1006 + { 1007 + int rc; 1008 + 1009 + rc = cxl_cper_register_callback(cxl_cper_event_call); 1010 + if (rc) 1011 + return rc; 1012 + 1013 + rc = pci_register_driver(&cxl_pci_driver); 1014 + if (rc) 1015 + cxl_cper_unregister_callback(cxl_cper_event_call); 1016 + 1017 + return rc; 1018 + } 1019 + 1020 + static void __exit cxl_pci_driver_exit(void) 1021 + { 1022 + pci_unregister_driver(&cxl_pci_driver); 1023 + cxl_cper_unregister_callback(cxl_cper_event_call); 1024 + } 1025 + 1026 + module_init(cxl_pci_driver_init); 1027 + module_exit(cxl_pci_driver_exit); 973 1028 MODULE_LICENSE("GPL v2"); 974 - module_pci_driver(cxl_pci_driver); 975 1029 MODULE_IMPORT_NS(CXL);
+6 -2
drivers/cxl/port.c
··· 69 69 if (rc < 0) 70 70 return rc; 71 71 72 + cxl_switch_parse_cdat(port); 73 + 72 74 cxlhdm = devm_cxl_setup_hdm(port, NULL); 73 75 if (!IS_ERR(cxlhdm)) 74 76 return devm_cxl_enumerate_decoders(cxlhdm, NULL); ··· 111 109 112 110 /* Cache the data early to ensure is_visible() works */ 113 111 read_cdat_data(port); 112 + cxl_endpoint_parse_cdat(port); 114 113 115 114 get_device(&cxlmd->dev); 116 115 rc = devm_add_action_or_reset(&port->dev, schedule_detach, cxlmd); ··· 130 127 * This can't fail in practice as CXL root exit unregisters all 131 128 * descendant ports and that in turn synchronizes with cxl_port_probe() 132 129 */ 133 - root = find_cxl_root(port); 130 + struct cxl_root *cxl_root __free(put_cxl_root) = find_cxl_root(port); 131 + 132 + root = &cxl_root->port; 134 133 135 134 /* 136 135 * Now that all endpoint decoders are successfully enumerated, try to 137 136 * assemble regions from committed decoders 138 137 */ 139 138 device_for_each_child(&port->dev, root, discover_region); 140 - put_device(&root->dev); 141 139 142 140 return 0; 143 141 }
+36 -2
drivers/pci/pci.c
··· 6295 6295 } 6296 6296 EXPORT_SYMBOL(pcie_set_mps); 6297 6297 6298 + static enum pci_bus_speed to_pcie_link_speed(u16 lnksta) 6299 + { 6300 + return pcie_link_speed[FIELD_GET(PCI_EXP_LNKSTA_CLS, lnksta)]; 6301 + } 6302 + 6303 + int pcie_link_speed_mbps(struct pci_dev *pdev) 6304 + { 6305 + u16 lnksta; 6306 + int err; 6307 + 6308 + err = pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnksta); 6309 + if (err) 6310 + return err; 6311 + 6312 + switch (to_pcie_link_speed(lnksta)) { 6313 + case PCIE_SPEED_2_5GT: 6314 + return 2500; 6315 + case PCIE_SPEED_5_0GT: 6316 + return 5000; 6317 + case PCIE_SPEED_8_0GT: 6318 + return 8000; 6319 + case PCIE_SPEED_16_0GT: 6320 + return 16000; 6321 + case PCIE_SPEED_32_0GT: 6322 + return 32000; 6323 + case PCIE_SPEED_64_0GT: 6324 + return 64000; 6325 + default: 6326 + break; 6327 + } 6328 + 6329 + return -EINVAL; 6330 + } 6331 + EXPORT_SYMBOL(pcie_link_speed_mbps); 6332 + 6298 6333 /** 6299 6334 * pcie_bandwidth_available - determine minimum link settings of a PCIe 6300 6335 * device and its bandwidth limitation ··· 6363 6328 while (dev) { 6364 6329 pcie_capability_read_word(dev, PCI_EXP_LNKSTA, &lnksta); 6365 6330 6366 - next_speed = pcie_link_speed[FIELD_GET(PCI_EXP_LNKSTA_CLS, 6367 - lnksta)]; 6331 + next_speed = to_pcie_link_speed(lnksta); 6368 6332 next_width = FIELD_GET(PCI_EXP_LNKSTA_NLW, lnksta); 6369 6333 6370 6334 next_bw = next_width * PCIE_SPEED2MBS_ENC(next_speed);
+11
include/linux/acpi.h
··· 15 15 #include <linux/mod_devicetable.h> 16 16 #include <linux/property.h> 17 17 #include <linux/uuid.h> 18 + #include <linux/node.h> 18 19 19 20 struct irq_domain; 20 21 struct irq_domain_ops; ··· 430 429 int thermal_acpi_passive_trip_temp(struct acpi_device *adev, int *ret_temp); 431 430 int thermal_acpi_hot_trip_temp(struct acpi_device *adev, int *ret_temp); 432 431 int thermal_acpi_critical_trip_temp(struct acpi_device *adev, int *ret_temp); 432 + #endif 433 + 434 + #ifdef CONFIG_ACPI_HMAT 435 + int acpi_get_genport_coordinates(u32 uid, struct access_coordinate *coord); 436 + #else 437 + static inline int acpi_get_genport_coordinates(u32 uid, 438 + struct access_coordinate *coord) 439 + { 440 + return -EOPNOTSUPP; 441 + } 433 442 #endif 434 443 435 444 #ifdef CONFIG_ACPI_NUMA
+161
include/linux/cxl-event.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + /* Copyright(c) 2023 Intel Corporation. */ 3 + #ifndef _LINUX_CXL_EVENT_H 4 + #define _LINUX_CXL_EVENT_H 5 + 6 + /* 7 + * Common Event Record Format 8 + * CXL rev 3.0 section 8.2.9.2.1; Table 8-42 9 + */ 10 + struct cxl_event_record_hdr { 11 + u8 length; 12 + u8 flags[3]; 13 + __le16 handle; 14 + __le16 related_handle; 15 + __le64 timestamp; 16 + u8 maint_op_class; 17 + u8 reserved[15]; 18 + } __packed; 19 + 20 + #define CXL_EVENT_RECORD_DATA_LENGTH 0x50 21 + struct cxl_event_generic { 22 + struct cxl_event_record_hdr hdr; 23 + u8 data[CXL_EVENT_RECORD_DATA_LENGTH]; 24 + } __packed; 25 + 26 + /* 27 + * General Media Event Record 28 + * CXL rev 3.0 Section 8.2.9.2.1.1; Table 8-43 29 + */ 30 + #define CXL_EVENT_GEN_MED_COMP_ID_SIZE 0x10 31 + struct cxl_event_gen_media { 32 + struct cxl_event_record_hdr hdr; 33 + __le64 phys_addr; 34 + u8 descriptor; 35 + u8 type; 36 + u8 transaction_type; 37 + u8 validity_flags[2]; 38 + u8 channel; 39 + u8 rank; 40 + u8 device[3]; 41 + u8 component_id[CXL_EVENT_GEN_MED_COMP_ID_SIZE]; 42 + u8 reserved[46]; 43 + } __packed; 44 + 45 + /* 46 + * DRAM Event Record - DER 47 + * CXL rev 3.0 section 8.2.9.2.1.2; Table 3-44 48 + */ 49 + #define CXL_EVENT_DER_CORRECTION_MASK_SIZE 0x20 50 + struct cxl_event_dram { 51 + struct cxl_event_record_hdr hdr; 52 + __le64 phys_addr; 53 + u8 descriptor; 54 + u8 type; 55 + u8 transaction_type; 56 + u8 validity_flags[2]; 57 + u8 channel; 58 + u8 rank; 59 + u8 nibble_mask[3]; 60 + u8 bank_group; 61 + u8 bank; 62 + u8 row[3]; 63 + u8 column[2]; 64 + u8 correction_mask[CXL_EVENT_DER_CORRECTION_MASK_SIZE]; 65 + u8 reserved[0x17]; 66 + } __packed; 67 + 68 + /* 69 + * Get Health Info Record 70 + * CXL rev 3.0 section 8.2.9.8.3.1; Table 8-100 71 + */ 72 + struct cxl_get_health_info { 73 + u8 health_status; 74 + u8 media_status; 75 + u8 add_status; 76 + u8 life_used; 77 + u8 device_temp[2]; 78 + u8 dirty_shutdown_cnt[4]; 79 + u8 cor_vol_err_cnt[4]; 80 + u8 cor_per_err_cnt[4]; 81 + } __packed; 82 + 83 + /* 84 + * Memory Module Event Record 85 + * CXL rev 3.0 section 8.2.9.2.1.3; Table 8-45 86 + */ 87 + struct cxl_event_mem_module { 88 + struct cxl_event_record_hdr hdr; 89 + u8 event_type; 90 + struct cxl_get_health_info info; 91 + u8 reserved[0x3d]; 92 + } __packed; 93 + 94 + union cxl_event { 95 + struct cxl_event_generic generic; 96 + struct cxl_event_gen_media gen_media; 97 + struct cxl_event_dram dram; 98 + struct cxl_event_mem_module mem_module; 99 + } __packed; 100 + 101 + /* 102 + * Common Event Record Format; in event logs 103 + * CXL rev 3.0 section 8.2.9.2.1; Table 8-42 104 + */ 105 + struct cxl_event_record_raw { 106 + uuid_t id; 107 + union cxl_event event; 108 + } __packed; 109 + 110 + enum cxl_event_type { 111 + CXL_CPER_EVENT_GENERIC, 112 + CXL_CPER_EVENT_GEN_MEDIA, 113 + CXL_CPER_EVENT_DRAM, 114 + CXL_CPER_EVENT_MEM_MODULE, 115 + }; 116 + 117 + #define CPER_CXL_DEVICE_ID_VALID BIT(0) 118 + #define CPER_CXL_DEVICE_SN_VALID BIT(1) 119 + #define CPER_CXL_COMP_EVENT_LOG_VALID BIT(2) 120 + struct cxl_cper_event_rec { 121 + struct { 122 + u32 length; 123 + u64 validation_bits; 124 + struct cper_cxl_event_devid { 125 + u16 vendor_id; 126 + u16 device_id; 127 + u8 func_num; 128 + u8 device_num; 129 + u8 bus_num; 130 + u16 segment_num; 131 + u16 slot_num; /* bits 2:0 reserved */ 132 + u8 reserved; 133 + } __packed device_id; 134 + struct cper_cxl_event_sn { 135 + u32 lower_dw; 136 + u32 upper_dw; 137 + } __packed dev_serial_num; 138 + } __packed hdr; 139 + 140 + union cxl_event event; 141 + } __packed; 142 + 143 + typedef void (*cxl_cper_callback)(enum cxl_event_type type, 144 + struct cxl_cper_event_rec *rec); 145 + 146 + #ifdef CONFIG_ACPI_APEI_GHES 147 + int cxl_cper_register_callback(cxl_cper_callback callback); 148 + int cxl_cper_unregister_callback(cxl_cper_callback callback); 149 + #else 150 + static inline int cxl_cper_register_callback(cxl_cper_callback callback) 151 + { 152 + return 0; 153 + } 154 + 155 + static inline int cxl_cper_unregister_callback(cxl_cper_callback callback) 156 + { 157 + return 0; 158 + } 159 + #endif 160 + 161 + #endif /* _LINUX_CXL_EVENT_H */
+20 -1
include/linux/fw_table.h
··· 25 25 int count; 26 26 }; 27 27 28 + union fw_table_header { 29 + struct acpi_table_header acpi; 30 + struct acpi_table_cdat cdat; 31 + }; 32 + 28 33 union acpi_subtable_headers { 29 34 struct acpi_subtable_header common; 30 35 struct acpi_hmat_structure hmat; 31 36 struct acpi_prmt_module_header prmt; 32 37 struct acpi_cedt_header cedt; 38 + struct acpi_cdat_header cdat; 33 39 }; 34 40 35 41 int acpi_parse_entries_array(char *id, unsigned long table_size, 36 - struct acpi_table_header *table_header, 42 + union fw_table_header *table_header, 37 43 struct acpi_subtable_proc *proc, 38 44 int proc_num, unsigned int max_entries); 45 + 46 + int cdat_table_parse(enum acpi_cdat_type type, 47 + acpi_tbl_entry_handler_arg handler_arg, void *arg, 48 + struct acpi_table_cdat *table_header); 49 + 50 + /* CXL is the only non-ACPI consumer of the FIRMWARE_TABLE library */ 51 + #if IS_ENABLED(CONFIG_ACPI) && !IS_ENABLED(CONFIG_CXL_BUS) 52 + #define EXPORT_SYMBOL_FWTBL_LIB(x) EXPORT_SYMBOL_ACPI_LIB(x) 53 + #define __init_or_fwtbl_lib __init_or_acpilib 54 + #else 55 + #define EXPORT_SYMBOL_FWTBL_LIB(x) EXPORT_SYMBOL_NS_GPL(x, CXL) 56 + #define __init_or_fwtbl_lib 57 + #endif 39 58 40 59 #endif
+5 -5
include/linux/memory-tiers.h
··· 33 33 struct kref kref; 34 34 }; 35 35 36 - struct node_hmem_attrs; 36 + struct access_coordinate; 37 37 38 38 #ifdef CONFIG_NUMA 39 39 extern bool numa_demotion_enabled; ··· 45 45 int register_mt_adistance_algorithm(struct notifier_block *nb); 46 46 int unregister_mt_adistance_algorithm(struct notifier_block *nb); 47 47 int mt_calc_adistance(int node, int *adist); 48 - int mt_set_default_dram_perf(int nid, struct node_hmem_attrs *perf, 48 + int mt_set_default_dram_perf(int nid, struct access_coordinate *perf, 49 49 const char *source); 50 - int mt_perf_to_adistance(struct node_hmem_attrs *perf, int *adist); 50 + int mt_perf_to_adistance(struct access_coordinate *perf, int *adist); 51 51 #ifdef CONFIG_MIGRATION 52 52 int next_demotion_node(int node); 53 53 void node_get_allowed_targets(pg_data_t *pgdat, nodemask_t *targets); ··· 126 126 return NOTIFY_DONE; 127 127 } 128 128 129 - static inline int mt_set_default_dram_perf(int nid, struct node_hmem_attrs *perf, 129 + static inline int mt_set_default_dram_perf(int nid, struct access_coordinate *perf, 130 130 const char *source) 131 131 { 132 132 return -EIO; 133 133 } 134 134 135 - static inline int mt_perf_to_adistance(struct node_hmem_attrs *perf, int *adist) 135 + static inline int mt_perf_to_adistance(struct access_coordinate *perf, int *adist) 136 136 { 137 137 return -EIO; 138 138 }
+4 -4
include/linux/node.h
··· 20 20 #include <linux/list.h> 21 21 22 22 /** 23 - * struct node_hmem_attrs - heterogeneous memory performance attributes 23 + * struct access_coordinate - generic performance coordinates container 24 24 * 25 25 * @read_bandwidth: Read bandwidth in MB/s 26 26 * @write_bandwidth: Write bandwidth in MB/s 27 27 * @read_latency: Read latency in nanoseconds 28 28 * @write_latency: Write latency in nanoseconds 29 29 */ 30 - struct node_hmem_attrs { 30 + struct access_coordinate { 31 31 unsigned int read_bandwidth; 32 32 unsigned int write_bandwidth; 33 33 unsigned int read_latency; ··· 65 65 66 66 #ifdef CONFIG_HMEM_REPORTING 67 67 void node_add_cache(unsigned int nid, struct node_cache_attrs *cache_attrs); 68 - void node_set_perf_attrs(unsigned int nid, struct node_hmem_attrs *hmem_attrs, 68 + void node_set_perf_attrs(unsigned int nid, struct access_coordinate *coord, 69 69 unsigned access); 70 70 #else 71 71 static inline void node_add_cache(unsigned int nid, ··· 74 74 } 75 75 76 76 static inline void node_set_perf_attrs(unsigned int nid, 77 - struct node_hmem_attrs *hmem_attrs, 77 + struct access_coordinate *coord, 78 78 unsigned access) 79 79 { 80 80 }
+3
include/linux/pci.h
··· 1171 1171 u8 pci_common_swizzle(struct pci_dev *dev, u8 *pinp); 1172 1172 struct pci_dev *pci_dev_get(struct pci_dev *dev); 1173 1173 void pci_dev_put(struct pci_dev *dev); 1174 + DEFINE_FREE(pci_dev_put, struct pci_dev *, if (_T) pci_dev_put(_T)) 1174 1175 void pci_remove_bus(struct pci_bus *b); 1175 1176 void pci_stop_and_remove_bus_device(struct pci_dev *dev); 1176 1177 void pci_stop_and_remove_bus_device_locked(struct pci_dev *dev); ··· 1368 1367 u32 pcie_bandwidth_available(struct pci_dev *dev, struct pci_dev **limiting_dev, 1369 1368 enum pci_bus_speed *speed, 1370 1369 enum pcie_link_width *width); 1370 + int pcie_link_speed_mbps(struct pci_dev *pdev); 1371 1371 void pcie_print_link_status(struct pci_dev *dev); 1372 1372 int pcie_reset_flr(struct pci_dev *dev, bool probe); 1373 1373 int pcie_flr(struct pci_dev *dev); ··· 1879 1877 void pci_dev_lock(struct pci_dev *dev); 1880 1878 int pci_dev_trylock(struct pci_dev *dev); 1881 1879 void pci_dev_unlock(struct pci_dev *dev); 1880 + DEFINE_GUARD(pci_dev, struct pci_dev *, pci_dev_lock(_T), pci_dev_unlock(_T)) 1882 1881 1883 1882 /* 1884 1883 * PCI domain support. Sometimes called PCI segment (eg by ACPI),
+1
include/uapi/linux/cxl_mem.h
··· 46 46 ___C(GET_SCAN_MEDIA_CAPS, "Get Scan Media Capabilities"), \ 47 47 ___DEPRECATED(SCAN_MEDIA, "Scan Media"), \ 48 48 ___DEPRECATED(GET_SCAN_MEDIA, "Get Scan Media Results"), \ 49 + ___C(GET_TIMESTAMP, "Get Timestamp"), \ 49 50 ___C(MAX, "invalid / last command") 50 51 51 52 #define ___C(a, b) CXL_MEM_COMMAND_ID_##a
+62 -11
lib/fw_table.c
··· 12 12 #include <linux/kernel.h> 13 13 #include <linux/string.h> 14 14 #include <linux/types.h> 15 + #include <linux/fw_table.h> 15 16 16 17 enum acpi_subtable_type { 17 18 ACPI_SUBTABLE_COMMON, 18 19 ACPI_SUBTABLE_HMAT, 19 20 ACPI_SUBTABLE_PRMT, 20 21 ACPI_SUBTABLE_CEDT, 22 + CDAT_SUBTABLE, 21 23 }; 22 24 23 25 struct acpi_subtable_entry { ··· 27 25 enum acpi_subtable_type type; 28 26 }; 29 27 30 - static unsigned long __init_or_acpilib 28 + static unsigned long __init_or_fwtbl_lib 31 29 acpi_get_entry_type(struct acpi_subtable_entry *entry) 32 30 { 33 31 switch (entry->type) { ··· 39 37 return 0; 40 38 case ACPI_SUBTABLE_CEDT: 41 39 return entry->hdr->cedt.type; 40 + case CDAT_SUBTABLE: 41 + return entry->hdr->cdat.type; 42 42 } 43 43 return 0; 44 44 } 45 45 46 - static unsigned long __init_or_acpilib 46 + static unsigned long __init_or_fwtbl_lib 47 47 acpi_get_entry_length(struct acpi_subtable_entry *entry) 48 48 { 49 49 switch (entry->type) { ··· 57 53 return entry->hdr->prmt.length; 58 54 case ACPI_SUBTABLE_CEDT: 59 55 return entry->hdr->cedt.length; 56 + case CDAT_SUBTABLE: { 57 + __le16 length = (__force __le16)entry->hdr->cdat.length; 58 + 59 + return le16_to_cpu(length); 60 + } 60 61 } 61 62 return 0; 62 63 } 63 64 64 - static unsigned long __init_or_acpilib 65 + static unsigned long __init_or_fwtbl_lib 65 66 acpi_get_subtable_header_length(struct acpi_subtable_entry *entry) 66 67 { 67 68 switch (entry->type) { ··· 78 69 return sizeof(entry->hdr->prmt); 79 70 case ACPI_SUBTABLE_CEDT: 80 71 return sizeof(entry->hdr->cedt); 72 + case CDAT_SUBTABLE: 73 + return sizeof(entry->hdr->cdat); 81 74 } 82 75 return 0; 83 76 } 84 77 85 - static enum acpi_subtable_type __init_or_acpilib 78 + static enum acpi_subtable_type __init_or_fwtbl_lib 86 79 acpi_get_subtable_type(char *id) 87 80 { 88 81 if (strncmp(id, ACPI_SIG_HMAT, 4) == 0) ··· 93 82 return ACPI_SUBTABLE_PRMT; 94 83 if (strncmp(id, ACPI_SIG_CEDT, 4) == 0) 95 84 return ACPI_SUBTABLE_CEDT; 85 + if (strncmp(id, ACPI_SIG_CDAT, 4) == 0) 86 + return CDAT_SUBTABLE; 96 87 return ACPI_SUBTABLE_COMMON; 97 88 } 98 89 99 - static __init_or_acpilib int call_handler(struct acpi_subtable_proc *proc, 100 - union acpi_subtable_headers *hdr, 101 - unsigned long end) 90 + static unsigned long __init_or_fwtbl_lib 91 + acpi_table_get_length(enum acpi_subtable_type type, 92 + union fw_table_header *header) 93 + { 94 + if (type == CDAT_SUBTABLE) { 95 + __le32 length = (__force __le32)header->cdat.length; 96 + 97 + return le32_to_cpu(length); 98 + } 99 + 100 + return header->acpi.length; 101 + } 102 + 103 + static __init_or_fwtbl_lib int call_handler(struct acpi_subtable_proc *proc, 104 + union acpi_subtable_headers *hdr, 105 + unsigned long end) 102 106 { 103 107 if (proc->handler) 104 108 return proc->handler(hdr, end); ··· 145 119 * On success returns sum of all matching entries for all proc handlers. 146 120 * Otherwise, -ENODEV or -EINVAL is returned. 147 121 */ 148 - int __init_or_acpilib 122 + int __init_or_fwtbl_lib 149 123 acpi_parse_entries_array(char *id, unsigned long table_size, 150 - struct acpi_table_header *table_header, 124 + union fw_table_header *table_header, 151 125 struct acpi_subtable_proc *proc, 152 126 int proc_num, unsigned int max_entries) 153 127 { 154 128 unsigned long table_end, subtable_len, entry_len; 155 129 struct acpi_subtable_entry entry; 130 + enum acpi_subtable_type type; 156 131 int count = 0; 157 132 int i; 158 133 159 - table_end = (unsigned long)table_header + table_header->length; 134 + type = acpi_get_subtable_type(id); 135 + table_end = (unsigned long)table_header + 136 + acpi_table_get_length(type, table_header); 160 137 161 138 /* Parse all entries looking for a match. */ 162 139 163 - entry.type = acpi_get_subtable_type(id); 140 + entry.type = type; 164 141 entry.hdr = (union acpi_subtable_headers *) 165 142 ((unsigned long)table_header + table_size); 166 143 subtable_len = acpi_get_subtable_header_length(&entry); ··· 203 174 204 175 return count; 205 176 } 177 + 178 + int __init_or_fwtbl_lib 179 + cdat_table_parse(enum acpi_cdat_type type, 180 + acpi_tbl_entry_handler_arg handler_arg, 181 + void *arg, 182 + struct acpi_table_cdat *table_header) 183 + { 184 + struct acpi_subtable_proc proc = { 185 + .id = type, 186 + .handler_arg = handler_arg, 187 + .arg = arg, 188 + }; 189 + 190 + if (!table_header) 191 + return -EINVAL; 192 + 193 + return acpi_parse_entries_array(ACPI_SIG_CDAT, 194 + sizeof(struct acpi_table_cdat), 195 + (union fw_table_header *)table_header, 196 + &proc, 1, 0); 197 + } 198 + EXPORT_SYMBOL_FWTBL_LIB(cdat_table_parse);
+6 -6
mm/memory-tiers.c
··· 109 109 static BLOCKING_NOTIFIER_HEAD(mt_adistance_algorithms); 110 110 111 111 static bool default_dram_perf_error; 112 - static struct node_hmem_attrs default_dram_perf; 112 + static struct access_coordinate default_dram_perf; 113 113 static int default_dram_perf_ref_nid = NUMA_NO_NODE; 114 114 static const char *default_dram_perf_ref_source; 115 115 ··· 601 601 } 602 602 EXPORT_SYMBOL_GPL(clear_node_memory_type); 603 603 604 - static void dump_hmem_attrs(struct node_hmem_attrs *attrs, const char *prefix) 604 + static void dump_hmem_attrs(struct access_coordinate *coord, const char *prefix) 605 605 { 606 606 pr_info( 607 607 "%sread_latency: %u, write_latency: %u, read_bandwidth: %u, write_bandwidth: %u\n", 608 - prefix, attrs->read_latency, attrs->write_latency, 609 - attrs->read_bandwidth, attrs->write_bandwidth); 608 + prefix, coord->read_latency, coord->write_latency, 609 + coord->read_bandwidth, coord->write_bandwidth); 610 610 } 611 611 612 - int mt_set_default_dram_perf(int nid, struct node_hmem_attrs *perf, 612 + int mt_set_default_dram_perf(int nid, struct access_coordinate *perf, 613 613 const char *source) 614 614 { 615 615 int rc = 0; ··· 666 666 return rc; 667 667 } 668 668 669 - int mt_perf_to_adistance(struct node_hmem_attrs *perf, int *adist) 669 + int mt_perf_to_adistance(struct access_coordinate *perf, int *adist) 670 670 { 671 671 if (default_dram_perf_error) 672 672 return -EIO;
+1
tools/testing/cxl/Kbuild
··· 58 58 cxl_core-y += $(CXL_CORE_SRC)/pci.o 59 59 cxl_core-y += $(CXL_CORE_SRC)/hdm.o 60 60 cxl_core-y += $(CXL_CORE_SRC)/pmu.o 61 + cxl_core-y += $(CXL_CORE_SRC)/cdat.o 61 62 cxl_core-$(CONFIG_TRACING) += $(CXL_CORE_SRC)/trace.o 62 63 cxl_core-$(CONFIG_CXL_REGION) += $(CXL_CORE_SRC)/region.o 63 64 cxl_core-y += config_check.o
+4
tools/testing/cxl/test/cxl.c
··· 68 68 static struct acpi_device host_bridge[NR_BRIDGES] = { 69 69 [0] = { 70 70 .handle = &host_bridge[0], 71 + .pnp.unique_id = "0", 71 72 }, 72 73 [1] = { 73 74 .handle = &host_bridge[1], 75 + .pnp.unique_id = "1", 74 76 }, 75 77 [2] = { 76 78 .handle = &host_bridge[2], 79 + .pnp.unique_id = "2", 77 80 }, 78 81 [3] = { 79 82 .handle = &host_bridge[3], 83 + .pnp.unique_id = "3", 80 84 }, 81 85 }; 82 86
+93 -70
tools/testing/cxl/test/mem.c
··· 251 251 for (i = 0; i < CXL_TEST_EVENT_CNT && !event_log_empty(log); i++) { 252 252 memcpy(&pl->records[i], event_get_current(log), 253 253 sizeof(pl->records[i])); 254 - pl->records[i].hdr.handle = event_get_cur_event_handle(log); 254 + pl->records[i].event.generic.hdr.handle = 255 + event_get_cur_event_handle(log); 255 256 log->cur_idx++; 256 257 } 257 258 ··· 338 337 } 339 338 340 339 struct cxl_event_record_raw maint_needed = { 341 - .hdr = { 342 - .id = UUID_INIT(0xBA5EBA11, 0xABCD, 0xEFEB, 343 - 0xa5, 0x5a, 0xa5, 0x5a, 0xa5, 0xa5, 0x5a, 0xa5), 344 - .length = sizeof(struct cxl_event_record_raw), 345 - .flags[0] = CXL_EVENT_RECORD_FLAG_MAINT_NEEDED, 346 - /* .handle = Set dynamically */ 347 - .related_handle = cpu_to_le16(0xa5b6), 340 + .id = UUID_INIT(0xBA5EBA11, 0xABCD, 0xEFEB, 341 + 0xa5, 0x5a, 0xa5, 0x5a, 0xa5, 0xa5, 0x5a, 0xa5), 342 + .event.generic = { 343 + .hdr = { 344 + .length = sizeof(struct cxl_event_record_raw), 345 + .flags[0] = CXL_EVENT_RECORD_FLAG_MAINT_NEEDED, 346 + /* .handle = Set dynamically */ 347 + .related_handle = cpu_to_le16(0xa5b6), 348 + }, 349 + .data = { 0xDE, 0xAD, 0xBE, 0xEF }, 348 350 }, 349 - .data = { 0xDE, 0xAD, 0xBE, 0xEF }, 350 351 }; 351 352 352 353 struct cxl_event_record_raw hardware_replace = { 353 - .hdr = { 354 - .id = UUID_INIT(0xABCDEFEB, 0xBA11, 0xBA5E, 355 - 0xa5, 0x5a, 0xa5, 0x5a, 0xa5, 0xa5, 0x5a, 0xa5), 356 - .length = sizeof(struct cxl_event_record_raw), 357 - .flags[0] = CXL_EVENT_RECORD_FLAG_HW_REPLACE, 358 - /* .handle = Set dynamically */ 359 - .related_handle = cpu_to_le16(0xb6a5), 354 + .id = UUID_INIT(0xABCDEFEB, 0xBA11, 0xBA5E, 355 + 0xa5, 0x5a, 0xa5, 0x5a, 0xa5, 0xa5, 0x5a, 0xa5), 356 + .event.generic = { 357 + .hdr = { 358 + .length = sizeof(struct cxl_event_record_raw), 359 + .flags[0] = CXL_EVENT_RECORD_FLAG_HW_REPLACE, 360 + /* .handle = Set dynamically */ 361 + .related_handle = cpu_to_le16(0xb6a5), 362 + }, 363 + .data = { 0xDE, 0xAD, 0xBE, 0xEF }, 360 364 }, 361 - .data = { 0xDE, 0xAD, 0xBE, 0xEF }, 362 365 }; 363 366 364 - struct cxl_event_gen_media gen_media = { 365 - .hdr = { 366 - .id = UUID_INIT(0xfbcd0a77, 0xc260, 0x417f, 367 - 0x85, 0xa9, 0x08, 0x8b, 0x16, 0x21, 0xeb, 0xa6), 368 - .length = sizeof(struct cxl_event_gen_media), 369 - .flags[0] = CXL_EVENT_RECORD_FLAG_PERMANENT, 370 - /* .handle = Set dynamically */ 371 - .related_handle = cpu_to_le16(0), 367 + struct cxl_test_gen_media { 368 + uuid_t id; 369 + struct cxl_event_gen_media rec; 370 + } __packed; 371 + 372 + struct cxl_test_gen_media gen_media = { 373 + .id = CXL_EVENT_GEN_MEDIA_UUID, 374 + .rec = { 375 + .hdr = { 376 + .length = sizeof(struct cxl_test_gen_media), 377 + .flags[0] = CXL_EVENT_RECORD_FLAG_PERMANENT, 378 + /* .handle = Set dynamically */ 379 + .related_handle = cpu_to_le16(0), 380 + }, 381 + .phys_addr = cpu_to_le64(0x2000), 382 + .descriptor = CXL_GMER_EVT_DESC_UNCORECTABLE_EVENT, 383 + .type = CXL_GMER_MEM_EVT_TYPE_DATA_PATH_ERROR, 384 + .transaction_type = CXL_GMER_TRANS_HOST_WRITE, 385 + /* .validity_flags = <set below> */ 386 + .channel = 1, 387 + .rank = 30 372 388 }, 373 - .phys_addr = cpu_to_le64(0x2000), 374 - .descriptor = CXL_GMER_EVT_DESC_UNCORECTABLE_EVENT, 375 - .type = CXL_GMER_MEM_EVT_TYPE_DATA_PATH_ERROR, 376 - .transaction_type = CXL_GMER_TRANS_HOST_WRITE, 377 - /* .validity_flags = <set below> */ 378 - .channel = 1, 379 - .rank = 30 380 389 }; 381 390 382 - struct cxl_event_dram dram = { 383 - .hdr = { 384 - .id = UUID_INIT(0x601dcbb3, 0x9c06, 0x4eab, 385 - 0xb8, 0xaf, 0x4e, 0x9b, 0xfb, 0x5c, 0x96, 0x24), 386 - .length = sizeof(struct cxl_event_dram), 387 - .flags[0] = CXL_EVENT_RECORD_FLAG_PERF_DEGRADED, 388 - /* .handle = Set dynamically */ 389 - .related_handle = cpu_to_le16(0), 391 + struct cxl_test_dram { 392 + uuid_t id; 393 + struct cxl_event_dram rec; 394 + } __packed; 395 + 396 + struct cxl_test_dram dram = { 397 + .id = CXL_EVENT_DRAM_UUID, 398 + .rec = { 399 + .hdr = { 400 + .length = sizeof(struct cxl_test_dram), 401 + .flags[0] = CXL_EVENT_RECORD_FLAG_PERF_DEGRADED, 402 + /* .handle = Set dynamically */ 403 + .related_handle = cpu_to_le16(0), 404 + }, 405 + .phys_addr = cpu_to_le64(0x8000), 406 + .descriptor = CXL_GMER_EVT_DESC_THRESHOLD_EVENT, 407 + .type = CXL_GMER_MEM_EVT_TYPE_INV_ADDR, 408 + .transaction_type = CXL_GMER_TRANS_INTERNAL_MEDIA_SCRUB, 409 + /* .validity_flags = <set below> */ 410 + .channel = 1, 411 + .bank_group = 5, 412 + .bank = 2, 413 + .column = {0xDE, 0xAD}, 390 414 }, 391 - .phys_addr = cpu_to_le64(0x8000), 392 - .descriptor = CXL_GMER_EVT_DESC_THRESHOLD_EVENT, 393 - .type = CXL_GMER_MEM_EVT_TYPE_INV_ADDR, 394 - .transaction_type = CXL_GMER_TRANS_INTERNAL_MEDIA_SCRUB, 395 - /* .validity_flags = <set below> */ 396 - .channel = 1, 397 - .bank_group = 5, 398 - .bank = 2, 399 - .column = {0xDE, 0xAD}, 400 415 }; 401 416 402 - struct cxl_event_mem_module mem_module = { 403 - .hdr = { 404 - .id = UUID_INIT(0xfe927475, 0xdd59, 0x4339, 405 - 0xa5, 0x86, 0x79, 0xba, 0xb1, 0x13, 0xb7, 0x74), 406 - .length = sizeof(struct cxl_event_mem_module), 407 - /* .handle = Set dynamically */ 408 - .related_handle = cpu_to_le16(0), 417 + struct cxl_test_mem_module { 418 + uuid_t id; 419 + struct cxl_event_mem_module rec; 420 + } __packed; 421 + 422 + struct cxl_test_mem_module mem_module = { 423 + .id = CXL_EVENT_MEM_MODULE_UUID, 424 + .rec = { 425 + .hdr = { 426 + .length = sizeof(struct cxl_test_mem_module), 427 + /* .handle = Set dynamically */ 428 + .related_handle = cpu_to_le16(0), 429 + }, 430 + .event_type = CXL_MMER_TEMP_CHANGE, 431 + .info = { 432 + .health_status = CXL_DHI_HS_PERFORMANCE_DEGRADED, 433 + .media_status = CXL_DHI_MS_ALL_DATA_LOST, 434 + .add_status = (CXL_DHI_AS_CRITICAL << 2) | 435 + (CXL_DHI_AS_WARNING << 4) | 436 + (CXL_DHI_AS_WARNING << 5), 437 + .device_temp = { 0xDE, 0xAD}, 438 + .dirty_shutdown_cnt = { 0xde, 0xad, 0xbe, 0xef }, 439 + .cor_vol_err_cnt = { 0xde, 0xad, 0xbe, 0xef }, 440 + .cor_per_err_cnt = { 0xde, 0xad, 0xbe, 0xef }, 441 + } 409 442 }, 410 - .event_type = CXL_MMER_TEMP_CHANGE, 411 - .info = { 412 - .health_status = CXL_DHI_HS_PERFORMANCE_DEGRADED, 413 - .media_status = CXL_DHI_MS_ALL_DATA_LOST, 414 - .add_status = (CXL_DHI_AS_CRITICAL << 2) | 415 - (CXL_DHI_AS_WARNING << 4) | 416 - (CXL_DHI_AS_WARNING << 5), 417 - .device_temp = { 0xDE, 0xAD}, 418 - .dirty_shutdown_cnt = { 0xde, 0xad, 0xbe, 0xef }, 419 - .cor_vol_err_cnt = { 0xde, 0xad, 0xbe, 0xef }, 420 - .cor_per_err_cnt = { 0xde, 0xad, 0xbe, 0xef }, 421 - } 422 443 }; 423 444 424 445 static int mock_set_timestamp(struct cxl_dev_state *cxlds, ··· 462 439 static void cxl_mock_add_event_logs(struct mock_event_store *mes) 463 440 { 464 441 put_unaligned_le16(CXL_GMER_VALID_CHANNEL | CXL_GMER_VALID_RANK, 465 - &gen_media.validity_flags); 442 + &gen_media.rec.validity_flags); 466 443 467 444 put_unaligned_le16(CXL_DER_VALID_CHANNEL | CXL_DER_VALID_BANK_GROUP | 468 445 CXL_DER_VALID_BANK | CXL_DER_VALID_COLUMN, 469 - &dram.validity_flags); 446 + &dram.rec.validity_flags); 470 447 471 448 mes_add_event(mes, CXL_EVENT_TYPE_INFO, &maint_needed); 472 449 mes_add_event(mes, CXL_EVENT_TYPE_INFO,