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

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

Pull CXL updates from Dave Jiang:
"The changes include adding poison injection support, fixing CXL access
coordinates when onlining CXL memory, and delaing the enumeration of
downstream switch ports for CXL hierarchy to ensure that the CXL link
is established at the time of enumeration to address a few issues
observed on AMD and Intel platforms.

Misc changes:
- Use str_plural() instead of open code for emitting strings.
- Use str_enabled_disabled() instead of ternary operator
- Fix emit of type resource_size_t argument for
validate_region_offset()
- Typo fixup in CXL driver-api documentation
- Rename CFMWS coherency restriction defines
- Add convention doc describe dealing with x86 low memory hole
and CXL

Poison Inject support:
- Move hpa_to_spa callback to new reoot decoder ops structure
- Define a SPA to HPA callback for interleave calculation with
XOR math
- Add support for SPA to DPA address translation with XOR
- Add locked variants of poison inject and clear functions
- Add inject and clear poison support by region offset

CXL access coordinates update fix:
- A comment update for hotplug memory callback prority defines
- Add node_update_perf_attrs() for updating perf attrs on a node
- Update cxl_access_coordinates() to use the new node update function
- Remove hmat_update_target_coordinates() and related code

CXL delayed downstream port enumeration and initialization:
- Add helper to detect top of CXL device topology and remove
open coding
- Add helper to delete single dport
- Add a cached copy of target_map to cxl_decoder
- Refactor decoder setup to reduce cxl_test burden
- Defer dport allocation for switch ports
- Add mock version of devm_cxl_add_dport_by_dev() for cxl_test
- Adjust the mock version of devm_cxl_switch_port_decoders_setup()
due to cxl core usage
- Setup target_map for cxl_test decoder initialization
- Change SSLBIS handler to handle single dport
- Move port register setup to when first dport appears"

* tag 'cxl-for-6.18' of git://git.kernel.org/pub/scm/linux/kernel/git/cxl/cxl: (25 commits)
cxl: Move port register setup to when first dport appear
cxl: Change sslbis handler to only handle single dport
cxl/test: Setup target_map for cxl_test decoder initialization
cxl/test: Adjust the mock version of devm_cxl_switch_port_decoders_setup()
cxl/test: Add mock version of devm_cxl_add_dport_by_dev()
cxl: Defer dport allocation for switch ports
cxl/test: Refactor decoder setup to reduce cxl_test burden
cxl: Add a cached copy of target_map to cxl_decoder
cxl: Add helper to delete dport
cxl: Add helper to detect top of CXL device topology
cxl: Documentation/driver-api/cxl: Describe the x86 Low Memory Hole solution
cxl/acpi: Rename CFMW coherency restrictions
Documentation/driver-api: Fix typo error in cxl
acpi/hmat: Remove now unused hmat_update_target_coordinates()
cxl, acpi/hmat: Update CXL access coordinates directly instead of through HMAT
drivers/base/node: Add a helper function node_update_perf_attrs()
mm/memory_hotplug: Update comment for hotplug memory callback priorities
cxl: Fix emit of type resource_size_t argument for validate_region_offset()
cxl/region: Add inject and clear poison by region offset
cxl/core: Add locked variants of the poison inject and clear funcs
...

+1261 -390
+87
Documentation/ABI/testing/debugfs-cxl
··· 19 19 is returned to the user. The inject_poison attribute is only 20 20 visible for devices supporting the capability. 21 21 22 + TEST-ONLY INTERFACE: This interface is intended for testing 23 + and validation purposes only. It is not a data repair mechanism 24 + and should never be used on production systems or live data. 25 + 26 + DATA LOSS RISK: For CXL persistent memory (PMEM) devices, 27 + poison injection can result in permanent data loss. Injected 28 + poison may render data permanently inaccessible even after 29 + clearing, as the clear operation writes zeros and does not 30 + recover original data. 31 + 32 + SYSTEM STABILITY RISK: For volatile memory, poison injection 33 + can cause kernel crashes, system instability, or unpredictable 34 + behavior if the poisoned addresses are accessed by running code 35 + or critical kernel structures. 22 36 23 37 What: /sys/kernel/debug/cxl/memX/clear_poison 24 38 Date: April, 2023 ··· 48 34 device cannot clear poison from the address, -ENXIO is returned. 49 35 The clear_poison attribute is only visible for devices 50 36 supporting the capability. 37 + 38 + TEST-ONLY INTERFACE: This interface is intended for testing 39 + and validation purposes only. It is not a data repair mechanism 40 + and should never be used on production systems or live data. 41 + 42 + CLEAR IS NOT DATA RECOVERY: This operation writes zeros to the 43 + specified address range and removes the address from the poison 44 + list. It does NOT recover or restore original data that may have 45 + been present before poison injection. Any original data at the 46 + cleared address is permanently lost and replaced with zeros. 47 + 48 + CLEAR IS NOT A REPAIR MECHANISM: This interface is for testing 49 + purposes only and should not be used as a data repair tool. 50 + Clearing poison is fundamentally different from data recovery 51 + or error correction. 52 + 53 + What: /sys/kernel/debug/cxl/regionX/inject_poison 54 + Date: August, 2025 55 + Contact: linux-cxl@vger.kernel.org 56 + Description: 57 + (WO) When a Host Physical Address (HPA) is written to this 58 + attribute, the region driver translates it to a Device 59 + Physical Address (DPA) and identifies the corresponding 60 + memdev. It then sends an inject poison command to that memdev 61 + at the translated DPA. Refer to the memdev ABI entry at: 62 + /sys/kernel/debug/cxl/memX/inject_poison for the detailed 63 + behavior. This attribute is only visible if all memdevs 64 + participating in the region support both inject and clear 65 + poison commands. 66 + 67 + TEST-ONLY INTERFACE: This interface is intended for testing 68 + and validation purposes only. It is not a data repair mechanism 69 + and should never be used on production systems or live data. 70 + 71 + DATA LOSS RISK: For CXL persistent memory (PMEM) devices, 72 + poison injection can result in permanent data loss. Injected 73 + poison may render data permanently inaccessible even after 74 + clearing, as the clear operation writes zeros and does not 75 + recover original data. 76 + 77 + SYSTEM STABILITY RISK: For volatile memory, poison injection 78 + can cause kernel crashes, system instability, or unpredictable 79 + behavior if the poisoned addresses are accessed by running code 80 + or critical kernel structures. 81 + 82 + What: /sys/kernel/debug/cxl/regionX/clear_poison 83 + Date: August, 2025 84 + Contact: linux-cxl@vger.kernel.org 85 + Description: 86 + (WO) When a Host Physical Address (HPA) is written to this 87 + attribute, the region driver translates it to a Device 88 + Physical Address (DPA) and identifies the corresponding 89 + memdev. It then sends a clear poison command to that memdev 90 + at the translated DPA. Refer to the memdev ABI entry at: 91 + /sys/kernel/debug/cxl/memX/clear_poison for the detailed 92 + behavior. This attribute is only visible if all memdevs 93 + participating in the region support both inject and clear 94 + poison commands. 95 + 96 + TEST-ONLY INTERFACE: This interface is intended for testing 97 + and validation purposes only. It is not a data repair mechanism 98 + and should never be used on production systems or live data. 99 + 100 + CLEAR IS NOT DATA RECOVERY: This operation writes zeros to the 101 + specified address range and removes the address from the poison 102 + list. It does NOT recover or restore original data that may have 103 + been present before poison injection. Any original data at the 104 + cleared address is permanently lost and replaced with zeros. 105 + 106 + CLEAR IS NOT A REPAIR MECHANISM: This interface is for testing 107 + purposes only and should not be used as a data repair tool. 108 + Clearing poison is fundamentally different from data recovery 109 + or error correction. 51 110 52 111 What: /sys/kernel/debug/cxl/einj_types 53 112 Date: January, 2024
+135
Documentation/driver-api/cxl/conventions.rst
··· 45 45 ---------------------------------- 46 46 47 47 <Propose spec language that corrects the conflict.> 48 + 49 + 50 + Resolve conflict between CFMWS, Platform Memory Holes, and Endpoint Decoders 51 + ============================================================================ 52 + 53 + Document 54 + -------- 55 + 56 + CXL Revision 3.2, Version 1.0 57 + 58 + License 59 + ------- 60 + 61 + SPDX-License Identifier: CC-BY-4.0 62 + 63 + Creator/Contributors 64 + -------------------- 65 + 66 + - Fabio M. De Francesco, Intel 67 + - Dan J. Williams, Intel 68 + - Mahesh Natu, Intel 69 + 70 + Summary of the Change 71 + --------------------- 72 + 73 + According to the current Compute Express Link (CXL) Specifications (Revision 74 + 3.2, Version 1.0), the CXL Fixed Memory Window Structure (CFMWS) describes zero 75 + or more Host Physical Address (HPA) windows associated with each CXL Host 76 + Bridge. Each window represents a contiguous HPA range that may be interleaved 77 + across one or more targets, including CXL Host Bridges. Each window has a set 78 + of restrictions that govern its usage. It is the Operating System-directed 79 + configuration and Power Management (OSPM) responsibility to utilize each window 80 + for the specified use. 81 + 82 + Table 9-22 of the current CXL Specifications states that the Window Size field 83 + contains the total number of consecutive bytes of HPA this window describes. 84 + This value must be a multiple of the Number of Interleave Ways (NIW) * 256 MB. 85 + 86 + Platform Firmware (BIOS) might reserve physical addresses below 4 GB where a 87 + memory gap such as the Low Memory Hole for PCIe MMIO may exist. In such cases, 88 + the CFMWS Range Size may not adhere to the NIW * 256 MB rule. 89 + 90 + The HPA represents the actual physical memory address space that the CXL devices 91 + can decode and respond to, while the System Physical Address (SPA), a related 92 + but distinct concept, represents the system-visible address space that users can 93 + direct transaction to and so it excludes reserved regions. 94 + 95 + BIOS publishes CFMWS to communicate the active SPA ranges that, on platforms 96 + with LMH's, map to a strict subset of the HPA. The SPA range trims out the hole, 97 + resulting in lost capacity in the Endpoints with no SPA to map to that part of 98 + the HPA range that intersects the hole. 99 + 100 + E.g, an x86 platform with two CFMWS and an LMH starting at 2 GB: 101 + 102 + +--------+------------+-------------------+------------------+-------------------+------+ 103 + | Window | CFMWS Base | CFMWS Size | HDM Decoder Base | HDM Decoder Size | Ways | 104 + +========+============+===================+==================+===================+======+ 105 + |  0 | 0 GB | 2 GB | 0 GB | 3 GB | 12 | 106 + +--------+------------+-------------------+------------------+-------------------+------+ 107 + |  1 | 4 GB | NIW*256MB Aligned | 4 GB | NIW*256MB Aligned | 12 | 108 + +--------+------------+-------------------+------------------+-------------------+------+ 109 + 110 + HDM decoder base and HDM decoder size represent all the 12 Endpoint Decoders of 111 + a 12 ways region and all the intermediate Switch Decoders. They are configured 112 + by the BIOS according to the NIW * 256MB rule, resulting in a HPA range size of 113 + 3GB. Instead, the CFMWS Base and CFMWS Size are used to configure the Root 114 + Decoder HPA range that results smaller (2GB) than that of the Switch and 115 + Endpoint Decoders in the hierarchy (3GB). 116 + 117 + This creates 2 issues which lead to a failure to construct a region: 118 + 119 + 1) A mismatch in region size between root and any HDM decoder. The root decoders 120 + will always be smaller due to the trim. 121 + 122 + 2) The trim causes the root decoder to violate the (NIW * 256MB) rule. 123 + 124 + This change allows a region with a base address of 0GB to bypass these checks to 125 + allow for region creation with the trimmed root decoder address range. 126 + 127 + This change does not allow for any other arbitrary region to violate these 128 + checks - it is intended exclusively to enable x86 platforms which map CXL memory 129 + under 4GB. 130 + 131 + Despite the HDM decoders covering the PCIE hole HPA region, it is expected that 132 + the platform will never route address accesses to the CXL complex because the 133 + root decoder only covers the trimmed region (which excludes this). This is 134 + outside the ability of Linux to enforce. 135 + 136 + On the example platform, only the first 2GB will be potentially usable, but 137 + Linux, aiming to adhere to the current specifications, fails to construct 138 + Regions and attach Endpoint and intermediate Switch Decoders to them. 139 + 140 + There are several points of failure that due to the expectation that the Root 141 + Decoder HPA size, that is equal to the CFMWS from which it is configured, has 142 + to be greater or equal to the matching Switch and Endpoint HDM Decoders. 143 + 144 + In order to succeed with construction and attachment, Linux must construct a 145 + Region with Root Decoder HPA range size, and then attach to that all the 146 + intermediate Switch Decoders and Endpoint Decoders that belong to the hierarchy 147 + regardless of their range sizes. 148 + 149 + Benefits of the Change 150 + ---------------------- 151 + 152 + Without the change, the OSPM wouldn't match intermediate Switch and Endpoint 153 + Decoders with Root Decoders configured with CFMWS HPA sizes that don't align 154 + with the NIW * 256MB constraint, and so it leads to lost memdev capacity. 155 + 156 + This change allows the OSPM to construct Regions and attach intermediate Switch 157 + and Endpoint Decoders to them, so that the addressable part of the memory 158 + devices total capacity is made available to the users. 159 + 160 + References 161 + ---------- 162 + 163 + Compute Express Link Specification Revision 3.2, Version 1.0 164 + <https://www.computeexpresslink.org/> 165 + 166 + Detailed Description of the Change 167 + ---------------------------------- 168 + 169 + The description of the Window Size field in table 9-22 needs to account for 170 + platforms with Low Memory Holes, where SPA ranges might be subsets of the 171 + endpoints HPA. Therefore, it has to be changed to the following: 172 + 173 + "The total number of consecutive bytes of HPA this window represents. This value 174 + shall be a multiple of NIW * 256 MB. 175 + 176 + On platforms that reserve physical addresses below 4 GB, such as the Low Memory 177 + Hole for PCIe MMIO on x86, an instance of CFMWS whose Base HPA range is 0 might 178 + have a size that doesn't align with the NIW * 256 MB constraint. 179 + 180 + Note that the matching intermediate Switch Decoders and the Endpoint Decoders 181 + HPA range sizes must still align to the above-mentioned rule, but the memory 182 + capacity that exceeds the CFMWS window size won't be accessible.".
+1 -1
Documentation/driver-api/cxl/maturity-map.rst
··· 173 173 User Flow Support 174 174 ----------------- 175 175 176 - * [0] Inject & clear poison by HPA 176 + * [2] Inject & clear poison by region offset 177 177 178 178 Details 179 179 =======
+1 -1
Documentation/driver-api/cxl/platform/bios-and-efi.rst
··· 202 202 203 203 Memory Holes 204 204 ------------ 205 - If your platform includes memory holes intersparsed between your CXL memory, it 205 + If your platform includes memory holes interspersed between your CXL memory, it 206 206 is recommended to utilize multiple decoders to cover these regions of memory, 207 207 rather than try to program the decoders to accept the entire range and expect 208 208 Linux to manage the overlap.
-34
drivers/acpi/numa/hmat.c
··· 74 74 struct node_cache_attrs cache_attrs; 75 75 u8 gen_port_device_handle[ACPI_SRAT_DEVICE_HANDLE_SIZE]; 76 76 bool registered; 77 - bool ext_updated; /* externally updated */ 78 77 }; 79 78 80 79 struct memory_initiator { ··· 366 367 break; 367 368 } 368 369 } 369 - 370 - int hmat_update_target_coordinates(int nid, struct access_coordinate *coord, 371 - enum access_coordinate_class access) 372 - { 373 - struct memory_target *target; 374 - int pxm; 375 - 376 - if (nid == NUMA_NO_NODE) 377 - return -EINVAL; 378 - 379 - pxm = node_to_pxm(nid); 380 - guard(mutex)(&target_lock); 381 - target = find_mem_target(pxm); 382 - if (!target) 383 - return -ENODEV; 384 - 385 - hmat_update_target_access(target, ACPI_HMAT_READ_LATENCY, 386 - coord->read_latency, access); 387 - hmat_update_target_access(target, ACPI_HMAT_WRITE_LATENCY, 388 - coord->write_latency, access); 389 - hmat_update_target_access(target, ACPI_HMAT_READ_BANDWIDTH, 390 - coord->read_bandwidth, access); 391 - hmat_update_target_access(target, ACPI_HMAT_WRITE_BANDWIDTH, 392 - coord->write_bandwidth, access); 393 - target->ext_updated = true; 394 - 395 - return 0; 396 - } 397 - EXPORT_SYMBOL_GPL(hmat_update_target_coordinates); 398 370 399 371 static __init void hmat_add_locality(struct acpi_hmat_locality *hmat_loc) 400 372 { ··· 742 772 struct memory_locality *loc = NULL; 743 773 u32 best = 0; 744 774 int i; 745 - 746 - /* Don't update if an external agent has changed the data. */ 747 - if (target->ext_updated) 748 - return; 749 775 750 776 /* Don't update for generic port if there's no device handle */ 751 777 if ((access == NODE_ACCESS_CLASS_GENPORT_SINK_LOCAL ||
+38
drivers/base/node.c
··· 249 249 EXPORT_SYMBOL_GPL(node_set_perf_attrs); 250 250 251 251 /** 252 + * node_update_perf_attrs - Update the performance values for given access class 253 + * @nid: Node identifier to be updated 254 + * @coord: Heterogeneous memory performance coordinates 255 + * @access: The access class for the given attributes 256 + */ 257 + void node_update_perf_attrs(unsigned int nid, struct access_coordinate *coord, 258 + enum access_coordinate_class access) 259 + { 260 + struct node_access_nodes *access_node; 261 + struct node *node; 262 + int i; 263 + 264 + if (WARN_ON_ONCE(!node_online(nid))) 265 + return; 266 + 267 + node = node_devices[nid]; 268 + list_for_each_entry(access_node, &node->access_list, list_node) { 269 + if (access_node->access != access) 270 + continue; 271 + 272 + access_node->coord = *coord; 273 + for (i = 0; access_attrs[i]; i++) { 274 + sysfs_notify(&access_node->dev.kobj, 275 + NULL, access_attrs[i]->name); 276 + } 277 + break; 278 + } 279 + 280 + /* When setting CPU access coordinates, update mempolicy */ 281 + if (access != ACCESS_COORDINATE_CPU) 282 + return; 283 + 284 + if (mempolicy_set_node_perf(nid, coord)) 285 + pr_info("failed to set mempolicy attrs for node %d\n", nid); 286 + } 287 + EXPORT_SYMBOL_GPL(node_update_perf_attrs); 288 + 289 + /** 252 290 * struct node_cache_info - Internal tracking for memory node caches 253 291 * @dev: Device represeting the cache level 254 292 * @node: List element for tracking in the node
+27 -19
drivers/cxl/acpi.c
··· 20 20 GUID_INIT(0xF365F9A6, 0xA7DE, 0x4071, 21 21 0xA6, 0x6A, 0xB4, 0x0C, 0x0B, 0x4F, 0x8E, 0x52); 22 22 23 - 24 - static u64 cxl_xor_hpa_to_spa(struct cxl_root_decoder *cxlrd, u64 hpa) 23 + static u64 cxl_apply_xor_maps(struct cxl_root_decoder *cxlrd, u64 addr) 25 24 { 26 25 struct cxl_cxims_data *cximsd = cxlrd->platform_data; 27 26 int hbiw = cxlrd->cxlsd.nr_targets; ··· 29 30 30 31 /* No xormaps for host bridge interleave ways of 1 or 3 */ 31 32 if (hbiw == 1 || hbiw == 3) 32 - return hpa; 33 + return addr; 33 34 34 35 /* 35 - * For root decoders using xormaps (hbiw: 2,4,6,8,12,16) restore 36 - * the position bit to its value before the xormap was applied at 37 - * HPA->DPA translation. 36 + * In regions using XOR interleave arithmetic the CXL HPA may not 37 + * be the same as the SPA. This helper performs the SPA->CXL HPA 38 + * or the CXL HPA->SPA translation. Since XOR is self-inverting, 39 + * so is this function. 40 + * 41 + * For root decoders using xormaps (hbiw: 2,4,6,8,12,16) applying the 42 + * xormaps will toggle a position bit. 38 43 * 39 44 * pos is the lowest set bit in an XORMAP 40 - * val is the XORALLBITS(HPA & XORMAP) 45 + * val is the XORALLBITS(addr & XORMAP) 41 46 * 42 47 * XORALLBITS: The CXL spec (3.1 Table 9-22) defines XORALLBITS 43 48 * as an operation that outputs a single bit by XORing all the 44 - * bits in the input (hpa & xormap). Implement XORALLBITS using 49 + * bits in the input (addr & xormap). Implement XORALLBITS using 45 50 * hweight64(). If the hamming weight is even the XOR of those 46 51 * bits results in val==0, if odd the XOR result is val==1. 47 52 */ ··· 54 51 if (!cximsd->xormaps[i]) 55 52 continue; 56 53 pos = __ffs(cximsd->xormaps[i]); 57 - val = (hweight64(hpa & cximsd->xormaps[i]) & 1); 58 - hpa = (hpa & ~(1ULL << pos)) | (val << pos); 54 + val = (hweight64(addr & cximsd->xormaps[i]) & 1); 55 + addr = (addr & ~(1ULL << pos)) | (val << pos); 59 56 } 60 57 61 - return hpa; 58 + return addr; 62 59 } 63 60 64 61 struct cxl_cxims_context { ··· 116 113 { 117 114 unsigned long flags = CXL_DECODER_F_ENABLE; 118 115 119 - if (restrictions & ACPI_CEDT_CFMWS_RESTRICT_TYPE2) 116 + if (restrictions & ACPI_CEDT_CFMWS_RESTRICT_DEVMEM) 120 117 flags |= CXL_DECODER_F_TYPE2; 121 - if (restrictions & ACPI_CEDT_CFMWS_RESTRICT_TYPE3) 118 + if (restrictions & ACPI_CEDT_CFMWS_RESTRICT_HOSTONLYMEM) 122 119 flags |= CXL_DECODER_F_TYPE3; 123 120 if (restrictions & ACPI_CEDT_CFMWS_RESTRICT_VOLATILE) 124 121 flags |= CXL_DECODER_F_RAM; ··· 401 398 static int __cxl_parse_cfmws(struct acpi_cedt_cfmws *cfmws, 402 399 struct cxl_cfmws_context *ctx) 403 400 { 404 - int target_map[CXL_DECODER_MAX_INTERLEAVE]; 405 401 struct cxl_port *root_port = ctx->root_port; 406 402 struct cxl_cxims_context cxims_ctx; 407 403 struct device *dev = ctx->dev; ··· 418 416 rc = eig_to_granularity(cfmws->granularity, &ig); 419 417 if (rc) 420 418 return rc; 421 - for (i = 0; i < ways; i++) 422 - target_map[i] = cfmws->interleave_targets[i]; 423 419 424 420 struct resource *res __free(del_cxl_resource) = alloc_cxl_resource( 425 421 cfmws->base_hpa, cfmws->window_size, ctx->id++); ··· 443 443 .end = cfmws->base_hpa + cfmws->window_size - 1, 444 444 }; 445 445 cxld->interleave_ways = ways; 446 + for (i = 0; i < ways; i++) 447 + cxld->target_map[i] = cfmws->interleave_targets[i]; 446 448 /* 447 449 * Minimize the x1 granularity to advertise support for any 448 450 * valid region granularity ··· 474 472 475 473 cxlrd->qos_class = cfmws->qtg_id; 476 474 477 - if (cfmws->interleave_arithmetic == ACPI_CEDT_CFMWS_ARITHMETIC_XOR) 478 - cxlrd->hpa_to_spa = cxl_xor_hpa_to_spa; 475 + if (cfmws->interleave_arithmetic == ACPI_CEDT_CFMWS_ARITHMETIC_XOR) { 476 + cxlrd->ops = kzalloc(sizeof(*cxlrd->ops), GFP_KERNEL); 477 + if (!cxlrd->ops) 478 + return -ENOMEM; 479 479 480 - rc = cxl_decoder_add(cxld, target_map); 480 + cxlrd->ops->hpa_to_spa = cxl_apply_xor_maps; 481 + cxlrd->ops->spa_to_hpa = cxl_apply_xor_maps; 482 + } 483 + 484 + rc = cxl_decoder_add(cxld); 481 485 if (rc) 482 486 return rc; 483 487
+11 -25
drivers/cxl/core/cdat.c
··· 338 338 339 339 guard(rwsem_read)(&cxl_rwsem.region); 340 340 for (int i = 0; i < cxlsd->nr_targets; i++) { 341 - if (host_bridge == cxlsd->target[i]->dport_dev) 341 + if (cxlsd->target[i] && host_bridge == cxlsd->target[i]->dport_dev) 342 342 return 1; 343 343 } 344 344 ··· 440 440 } *tbl = (struct acpi_cdat_sslbis_table *)header; 441 441 int size = sizeof(header->cdat) + sizeof(tbl->sslbis_header); 442 442 struct acpi_cdat_sslbis *sslbis; 443 - struct cxl_port *port = arg; 444 - struct device *dev = &port->dev; 443 + struct cxl_dport *dport = arg; 444 + struct device *dev = &dport->port->dev; 445 445 int remain, entries, i; 446 446 u16 len; 447 447 ··· 467 467 u16 y = le16_to_cpu((__force __le16)tbl->entries[i].porty_id); 468 468 __le64 le_base; 469 469 __le16 le_val; 470 - struct cxl_dport *dport; 471 - unsigned long index; 472 470 u16 dsp_id; 473 471 u64 val; 474 472 ··· 497 499 val = cdat_normalize(le16_to_cpu(le_val), le64_to_cpu(le_base), 498 500 sslbis->data_type); 499 501 500 - xa_for_each(&port->dports, index, dport) { 501 - if (dsp_id == ACPI_CDAT_SSLBIS_ANY_PORT || 502 - dsp_id == dport->port_id) { 503 - cxl_access_coordinate_set(dport->coord, 504 - sslbis->data_type, 505 - val); 506 - } 502 + if (dsp_id == ACPI_CDAT_SSLBIS_ANY_PORT || 503 + dsp_id == dport->port_id) { 504 + cxl_access_coordinate_set(dport->coord, 505 + sslbis->data_type, val); 506 + return 0; 507 507 } 508 508 } 509 509 510 510 return 0; 511 511 } 512 512 513 - void cxl_switch_parse_cdat(struct cxl_port *port) 513 + void cxl_switch_parse_cdat(struct cxl_dport *dport) 514 514 { 515 + struct cxl_port *port = dport->port; 515 516 int rc; 516 517 517 518 if (!port->cdat.table) 518 519 return; 519 520 520 521 rc = cdat_table_parse(ACPI_CDAT_TYPE_SSLBIS, cdat_sslbis_handler, 521 - port, port->cdat.table, port->cdat.length); 522 + dport, port->cdat.table, port->cdat.length); 522 523 rc = cdat_table_parse_output(rc); 523 524 if (rc) 524 525 dev_dbg(&port->dev, "Failed to parse SSLBIS: %d\n", rc); ··· 1071 1074 cxlr->coord[i].read_bandwidth += perf->coord[i].read_bandwidth; 1072 1075 cxlr->coord[i].write_bandwidth += perf->coord[i].write_bandwidth; 1073 1076 } 1074 - } 1075 - 1076 - int cxl_update_hmat_access_coordinates(int nid, struct cxl_region *cxlr, 1077 - enum access_coordinate_class access) 1078 - { 1079 - return hmat_update_target_coordinates(nid, &cxlr->coord[access], access); 1080 - } 1081 - 1082 - bool cxl_need_node_perf_attrs_update(int nid) 1083 - { 1084 - return !acpi_node_backed_by_real_pxm(nid); 1085 1077 }
+9 -3
drivers/cxl/core/core.h
··· 135 135 CXL_POISON_TRACE_CLEAR, 136 136 }; 137 137 138 + enum poison_cmd_enabled_bits; 139 + bool cxl_memdev_has_poison_cmd(struct cxl_memdev *cxlmd, 140 + enum poison_cmd_enabled_bits cmd); 141 + 138 142 long cxl_pci_get_latency(struct pci_dev *pdev); 139 143 int cxl_pci_get_bandwidth(struct pci_dev *pdev, struct access_coordinate *c); 140 - int cxl_update_hmat_access_coordinates(int nid, struct cxl_region *cxlr, 141 - enum access_coordinate_class access); 142 - bool cxl_need_node_perf_attrs_update(int nid); 143 144 int cxl_port_get_switch_dport_bandwidth(struct cxl_port *port, 144 145 struct access_coordinate *c); 145 146 146 147 int cxl_ras_init(void); 147 148 void cxl_ras_exit(void); 148 149 int cxl_gpf_port_setup(struct cxl_dport *dport); 150 + 151 + struct cxl_hdm; 152 + int cxl_hdm_decode_init(struct cxl_dev_state *cxlds, struct cxl_hdm *cxlhdm, 153 + struct cxl_endpoint_dvsec_info *info); 154 + int cxl_port_get_possible_dports(struct cxl_port *port); 149 155 150 156 #ifdef CONFIG_CXL_FEATURES 151 157 struct cxl_feat_entry *
+81 -26
drivers/cxl/core/hdm.c
··· 21 21 .dpa = __RWSEM_INITIALIZER(cxl_rwsem.dpa), 22 22 }; 23 23 24 - static int add_hdm_decoder(struct cxl_port *port, struct cxl_decoder *cxld, 25 - int *target_map) 24 + static int add_hdm_decoder(struct cxl_port *port, struct cxl_decoder *cxld) 26 25 { 27 26 int rc; 28 27 29 - rc = cxl_decoder_add_locked(cxld, target_map); 28 + rc = cxl_decoder_add_locked(cxld); 30 29 if (rc) { 31 30 put_device(&cxld->dev); 32 31 dev_err(&port->dev, "Failed to add decoder\n"); ··· 49 50 * are claimed and passed to the single dport. Disable the range until the first 50 51 * CXL region is enumerated / activated. 51 52 */ 52 - int devm_cxl_add_passthrough_decoder(struct cxl_port *port) 53 + static int devm_cxl_add_passthrough_decoder(struct cxl_port *port) 53 54 { 54 55 struct cxl_switch_decoder *cxlsd; 55 - struct cxl_dport *dport = NULL; 56 - int single_port_map[1]; 57 - unsigned long index; 58 56 struct cxl_hdm *cxlhdm = dev_get_drvdata(&port->dev); 59 57 60 58 /* ··· 67 71 68 72 device_lock_assert(&port->dev); 69 73 70 - xa_for_each(&port->dports, index, dport) 71 - break; 72 - single_port_map[0] = dport->port_id; 73 - 74 - return add_hdm_decoder(port, &cxlsd->cxld, single_port_map); 74 + return add_hdm_decoder(port, &cxlsd->cxld); 75 75 } 76 - EXPORT_SYMBOL_NS_GPL(devm_cxl_add_passthrough_decoder, "CXL"); 77 76 78 77 static void parse_hdm_decoder_caps(struct cxl_hdm *cxlhdm) 79 78 { ··· 138 147 * @port: cxl_port to map 139 148 * @info: cached DVSEC range register info 140 149 */ 141 - struct cxl_hdm *devm_cxl_setup_hdm(struct cxl_port *port, 142 - struct cxl_endpoint_dvsec_info *info) 150 + static struct cxl_hdm *devm_cxl_setup_hdm(struct cxl_port *port, 151 + struct cxl_endpoint_dvsec_info *info) 143 152 { 144 153 struct cxl_register_map *reg_map = &port->reg_map; 145 154 struct device *dev = &port->dev; ··· 188 197 */ 189 198 if (should_emulate_decoders(info)) { 190 199 dev_dbg(dev, "Fallback map %d range register%s\n", info->ranges, 191 - info->ranges > 1 ? "s" : ""); 200 + str_plural(info->ranges)); 192 201 cxlhdm->decoder_count = info->ranges; 193 202 } 194 203 195 204 return cxlhdm; 196 205 } 197 - EXPORT_SYMBOL_NS_GPL(devm_cxl_setup_hdm, "CXL"); 198 206 199 207 static void __cxl_dpa_debug(struct seq_file *file, struct resource *r, int depth) 200 208 { ··· 974 984 } 975 985 976 986 static int init_hdm_decoder(struct cxl_port *port, struct cxl_decoder *cxld, 977 - int *target_map, void __iomem *hdm, int which, 987 + void __iomem *hdm, int which, 978 988 u64 *dpa_base, struct cxl_endpoint_dvsec_info *info) 979 989 { 980 990 struct cxl_endpoint_decoder *cxled = NULL; ··· 1093 1103 hi = readl(hdm + CXL_HDM_DECODER0_TL_HIGH(which)); 1094 1104 target_list.value = (hi << 32) + lo; 1095 1105 for (i = 0; i < cxld->interleave_ways; i++) 1096 - target_map[i] = target_list.target_id[i]; 1106 + cxld->target_map[i] = target_list.target_id[i]; 1097 1107 1098 1108 return 0; 1099 1109 } ··· 1158 1168 * @cxlhdm: Structure to populate with HDM capabilities 1159 1169 * @info: cached DVSEC range register info 1160 1170 */ 1161 - int devm_cxl_enumerate_decoders(struct cxl_hdm *cxlhdm, 1162 - struct cxl_endpoint_dvsec_info *info) 1171 + static int devm_cxl_enumerate_decoders(struct cxl_hdm *cxlhdm, 1172 + struct cxl_endpoint_dvsec_info *info) 1163 1173 { 1164 1174 void __iomem *hdm = cxlhdm->regs.hdm_decoder; 1165 1175 struct cxl_port *port = cxlhdm->port; ··· 1169 1179 cxl_settle_decoders(cxlhdm); 1170 1180 1171 1181 for (i = 0; i < cxlhdm->decoder_count; i++) { 1172 - int target_map[CXL_DECODER_MAX_INTERLEAVE] = { 0 }; 1173 1182 int rc, target_count = cxlhdm->target_count; 1174 1183 struct cxl_decoder *cxld; 1175 1184 ··· 1196 1207 cxld = &cxlsd->cxld; 1197 1208 } 1198 1209 1199 - rc = init_hdm_decoder(port, cxld, target_map, hdm, i, 1200 - &dpa_base, info); 1210 + rc = init_hdm_decoder(port, cxld, hdm, i, &dpa_base, info); 1201 1211 if (rc) { 1202 1212 dev_warn(&port->dev, 1203 1213 "Failed to initialize decoder%d.%d\n", ··· 1204 1216 put_device(&cxld->dev); 1205 1217 return rc; 1206 1218 } 1207 - rc = add_hdm_decoder(port, cxld, target_map); 1219 + rc = add_hdm_decoder(port, cxld); 1208 1220 if (rc) { 1209 1221 dev_warn(&port->dev, 1210 1222 "Failed to add decoder%d.%d\n", port->id, i); ··· 1214 1226 1215 1227 return 0; 1216 1228 } 1217 - EXPORT_SYMBOL_NS_GPL(devm_cxl_enumerate_decoders, "CXL"); 1229 + 1230 + /** 1231 + * __devm_cxl_switch_port_decoders_setup - allocate and setup switch decoders 1232 + * @port: CXL port context 1233 + * 1234 + * Return 0 or -errno on error 1235 + */ 1236 + int __devm_cxl_switch_port_decoders_setup(struct cxl_port *port) 1237 + { 1238 + struct cxl_hdm *cxlhdm; 1239 + 1240 + if (is_cxl_root(port) || is_cxl_endpoint(port)) 1241 + return -EOPNOTSUPP; 1242 + 1243 + cxlhdm = devm_cxl_setup_hdm(port, NULL); 1244 + if (!IS_ERR(cxlhdm)) 1245 + return devm_cxl_enumerate_decoders(cxlhdm, NULL); 1246 + 1247 + if (PTR_ERR(cxlhdm) != -ENODEV) { 1248 + dev_err(&port->dev, "Failed to map HDM decoder capability\n"); 1249 + return PTR_ERR(cxlhdm); 1250 + } 1251 + 1252 + if (cxl_port_get_possible_dports(port) == 1) { 1253 + dev_dbg(&port->dev, "Fallback to passthrough decoder\n"); 1254 + return devm_cxl_add_passthrough_decoder(port); 1255 + } 1256 + 1257 + dev_err(&port->dev, "HDM decoder capability not found\n"); 1258 + return -ENXIO; 1259 + } 1260 + EXPORT_SYMBOL_NS_GPL(__devm_cxl_switch_port_decoders_setup, "CXL"); 1261 + 1262 + /** 1263 + * devm_cxl_endpoint_decoders_setup - allocate and setup endpoint decoders 1264 + * @port: CXL port context 1265 + * 1266 + * Return 0 or -errno on error 1267 + */ 1268 + int devm_cxl_endpoint_decoders_setup(struct cxl_port *port) 1269 + { 1270 + struct cxl_memdev *cxlmd = to_cxl_memdev(port->uport_dev); 1271 + struct cxl_endpoint_dvsec_info info = { .port = port }; 1272 + struct cxl_dev_state *cxlds = cxlmd->cxlds; 1273 + struct cxl_hdm *cxlhdm; 1274 + int rc; 1275 + 1276 + if (!is_cxl_endpoint(port)) 1277 + return -EOPNOTSUPP; 1278 + 1279 + rc = cxl_dvsec_rr_decode(cxlds, &info); 1280 + if (rc < 0) 1281 + return rc; 1282 + 1283 + cxlhdm = devm_cxl_setup_hdm(port, &info); 1284 + if (IS_ERR(cxlhdm)) { 1285 + if (PTR_ERR(cxlhdm) == -ENODEV) 1286 + dev_err(&port->dev, "HDM decoder registers not found\n"); 1287 + return PTR_ERR(cxlhdm); 1288 + } 1289 + 1290 + rc = cxl_hdm_decode_init(cxlds, cxlhdm, &info); 1291 + if (rc) 1292 + return rc; 1293 + 1294 + return devm_cxl_enumerate_decoders(cxlhdm, &info); 1295 + } 1296 + EXPORT_SYMBOL_NS_GPL(devm_cxl_endpoint_decoders_setup, "CXL");
+44 -16
drivers/cxl/core/memdev.c
··· 200 200 static struct device_attribute dev_attr_security_erase = 201 201 __ATTR(erase, 0200, NULL, security_erase_store); 202 202 203 + bool cxl_memdev_has_poison_cmd(struct cxl_memdev *cxlmd, 204 + enum poison_cmd_enabled_bits cmd) 205 + { 206 + struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlmd->cxlds); 207 + 208 + return test_bit(cmd, mds->poison.enabled_cmds); 209 + } 210 + 203 211 static int cxl_get_poison_by_memdev(struct cxl_memdev *cxlmd) 204 212 { 205 213 struct cxl_dev_state *cxlds = cxlmd->cxlds; ··· 284 276 return 0; 285 277 } 286 278 287 - int cxl_inject_poison(struct cxl_memdev *cxlmd, u64 dpa) 279 + int cxl_inject_poison_locked(struct cxl_memdev *cxlmd, u64 dpa) 288 280 { 289 281 struct cxl_mailbox *cxl_mbox = &cxlmd->cxlds->cxl_mbox; 290 282 struct cxl_mbox_inject_poison inject; ··· 296 288 if (!IS_ENABLED(CONFIG_DEBUG_FS)) 297 289 return 0; 298 290 299 - ACQUIRE(rwsem_read_intr, region_rwsem)(&cxl_rwsem.region); 300 - if ((rc = ACQUIRE_ERR(rwsem_read_intr, &region_rwsem))) 301 - return rc; 302 - 303 - ACQUIRE(rwsem_read_intr, dpa_rwsem)(&cxl_rwsem.dpa); 304 - if ((rc = ACQUIRE_ERR(rwsem_read_intr, &dpa_rwsem))) 305 - return rc; 291 + lockdep_assert_held(&cxl_rwsem.dpa); 292 + lockdep_assert_held(&cxl_rwsem.region); 306 293 307 294 rc = cxl_validate_poison_dpa(cxlmd, dpa); 308 295 if (rc) ··· 327 324 328 325 return 0; 329 326 } 327 + 328 + int cxl_inject_poison(struct cxl_memdev *cxlmd, u64 dpa) 329 + { 330 + int rc; 331 + 332 + ACQUIRE(rwsem_read_intr, region_rwsem)(&cxl_rwsem.region); 333 + if ((rc = ACQUIRE_ERR(rwsem_read_intr, &region_rwsem))) 334 + return rc; 335 + 336 + ACQUIRE(rwsem_read_intr, dpa_rwsem)(&cxl_rwsem.dpa); 337 + if ((rc = ACQUIRE_ERR(rwsem_read_intr, &dpa_rwsem))) 338 + return rc; 339 + 340 + return cxl_inject_poison_locked(cxlmd, dpa); 341 + } 330 342 EXPORT_SYMBOL_NS_GPL(cxl_inject_poison, "CXL"); 331 343 332 - int cxl_clear_poison(struct cxl_memdev *cxlmd, u64 dpa) 344 + int cxl_clear_poison_locked(struct cxl_memdev *cxlmd, u64 dpa) 333 345 { 334 346 struct cxl_mailbox *cxl_mbox = &cxlmd->cxlds->cxl_mbox; 335 347 struct cxl_mbox_clear_poison clear; ··· 356 338 if (!IS_ENABLED(CONFIG_DEBUG_FS)) 357 339 return 0; 358 340 359 - ACQUIRE(rwsem_read_intr, region_rwsem)(&cxl_rwsem.region); 360 - if ((rc = ACQUIRE_ERR(rwsem_read_intr, &region_rwsem))) 361 - return rc; 362 - 363 - ACQUIRE(rwsem_read_intr, dpa_rwsem)(&cxl_rwsem.dpa); 364 - if ((rc = ACQUIRE_ERR(rwsem_read_intr, &dpa_rwsem))) 365 - return rc; 341 + lockdep_assert_held(&cxl_rwsem.dpa); 342 + lockdep_assert_held(&cxl_rwsem.region); 366 343 367 344 rc = cxl_validate_poison_dpa(cxlmd, dpa); 368 345 if (rc) ··· 395 382 trace_cxl_poison(cxlmd, cxlr, &record, 0, 0, CXL_POISON_TRACE_CLEAR); 396 383 397 384 return 0; 385 + } 386 + 387 + int cxl_clear_poison(struct cxl_memdev *cxlmd, u64 dpa) 388 + { 389 + int rc; 390 + 391 + ACQUIRE(rwsem_read_intr, region_rwsem)(&cxl_rwsem.region); 392 + if ((rc = ACQUIRE_ERR(rwsem_read_intr, &region_rwsem))) 393 + return rc; 394 + 395 + ACQUIRE(rwsem_read_intr, dpa_rwsem)(&cxl_rwsem.dpa); 396 + if ((rc = ACQUIRE_ERR(rwsem_read_intr, &dpa_rwsem))) 397 + return rc; 398 + 399 + return cxl_clear_poison_locked(cxlmd, dpa); 398 400 } 399 401 EXPORT_SYMBOL_NS_GPL(cxl_clear_poison, "CXL"); 400 402
+89
drivers/cxl/core/pci.c
··· 24 24 module_param(media_ready_timeout, ushort, 0644); 25 25 MODULE_PARM_DESC(media_ready_timeout, "seconds to wait for media ready"); 26 26 27 + static int pci_get_port_num(struct pci_dev *pdev) 28 + { 29 + u32 lnkcap; 30 + int type; 31 + 32 + type = pci_pcie_type(pdev); 33 + if (type != PCI_EXP_TYPE_DOWNSTREAM && type != PCI_EXP_TYPE_ROOT_PORT) 34 + return -EINVAL; 35 + 36 + if (pci_read_config_dword(pdev, pci_pcie_cap(pdev) + PCI_EXP_LNKCAP, 37 + &lnkcap)) 38 + return -ENXIO; 39 + 40 + return FIELD_GET(PCI_EXP_LNKCAP_PN, lnkcap); 41 + } 42 + 43 + /** 44 + * __devm_cxl_add_dport_by_dev - allocate a dport by dport device 45 + * @port: cxl_port that hosts the dport 46 + * @dport_dev: 'struct device' of the dport 47 + * 48 + * Returns the allocated dport on success or ERR_PTR() of -errno on error 49 + */ 50 + struct cxl_dport *__devm_cxl_add_dport_by_dev(struct cxl_port *port, 51 + struct device *dport_dev) 52 + { 53 + struct cxl_register_map map; 54 + struct pci_dev *pdev; 55 + int port_num, rc; 56 + 57 + if (!dev_is_pci(dport_dev)) 58 + return ERR_PTR(-EINVAL); 59 + 60 + pdev = to_pci_dev(dport_dev); 61 + port_num = pci_get_port_num(pdev); 62 + if (port_num < 0) 63 + return ERR_PTR(port_num); 64 + 65 + rc = cxl_find_regblock(pdev, CXL_REGLOC_RBI_COMPONENT, &map); 66 + if (rc) 67 + return ERR_PTR(rc); 68 + 69 + device_lock_assert(&port->dev); 70 + return devm_cxl_add_dport(port, dport_dev, port_num, map.resource); 71 + } 72 + EXPORT_SYMBOL_NS_GPL(__devm_cxl_add_dport_by_dev, "CXL"); 73 + 27 74 struct cxl_walk_context { 28 75 struct pci_bus *bus; 29 76 struct cxl_port *port; ··· 1215 1168 } 1216 1169 1217 1170 return 0; 1171 + } 1172 + 1173 + static int count_dports(struct pci_dev *pdev, void *data) 1174 + { 1175 + struct cxl_walk_context *ctx = data; 1176 + int type = pci_pcie_type(pdev); 1177 + 1178 + if (pdev->bus != ctx->bus) 1179 + return 0; 1180 + if (!pci_is_pcie(pdev)) 1181 + return 0; 1182 + if (type != ctx->type) 1183 + return 0; 1184 + 1185 + ctx->count++; 1186 + return 0; 1187 + } 1188 + 1189 + int cxl_port_get_possible_dports(struct cxl_port *port) 1190 + { 1191 + struct pci_bus *bus = cxl_port_to_pci_bus(port); 1192 + struct cxl_walk_context ctx; 1193 + int type; 1194 + 1195 + if (!bus) { 1196 + dev_err(&port->dev, "No PCI bus found for port %s\n", 1197 + dev_name(&port->dev)); 1198 + return -ENXIO; 1199 + } 1200 + 1201 + if (pci_is_root_bus(bus)) 1202 + type = PCI_EXP_TYPE_ROOT_PORT; 1203 + else 1204 + type = PCI_EXP_TYPE_DOWNSTREAM; 1205 + 1206 + ctx = (struct cxl_walk_context) { 1207 + .bus = bus, 1208 + .type = type, 1209 + }; 1210 + pci_walk_bus(bus, count_dports, &ctx); 1211 + 1212 + return ctx.count; 1218 1213 }
+242 -77
drivers/cxl/core/port.c
··· 33 33 static DEFINE_IDA(cxl_port_ida); 34 34 static DEFINE_XARRAY(cxl_root_buses); 35 35 36 + /* 37 + * The terminal device in PCI is NULL and @platform_bus 38 + * for platform devices (for cxl_test) 39 + */ 40 + static bool is_cxl_host_bridge(struct device *dev) 41 + { 42 + return (!dev || dev == &platform_bus); 43 + } 44 + 36 45 int cxl_num_decoders_committed(struct cxl_port *port) 37 46 { 38 47 lockdep_assert_held(&cxl_rwsem.region); ··· 459 450 if (atomic_read(&cxlrd->region_id) >= 0) 460 451 memregion_free(atomic_read(&cxlrd->region_id)); 461 452 __cxl_decoder_release(&cxlrd->cxlsd.cxld); 453 + kfree(cxlrd->ops); 462 454 kfree(cxlrd); 463 455 } 464 456 ··· 750 740 xa_init(&port->dports); 751 741 xa_init(&port->endpoints); 752 742 xa_init(&port->regions); 743 + port->component_reg_phys = CXL_RESOURCE_NONE; 753 744 754 745 device_initialize(dev); 755 746 lockdep_set_class_and_subclass(&dev->mutex, &cxl_port_key, port->depth); ··· 869 858 if (rc) 870 859 return rc; 871 860 872 - rc = cxl_port_setup_regs(port, component_reg_phys); 873 - if (rc) 874 - return rc; 861 + port->component_reg_phys = component_reg_phys; 875 862 } else { 876 863 rc = dev_set_name(dev, "root%d", port->id); 877 864 if (rc) ··· 1200 1191 1201 1192 cxl_debugfs_create_dport_dir(dport); 1202 1193 1194 + /* 1195 + * Setup port register if this is the first dport showed up. Having 1196 + * a dport also means that there is at least 1 active link. 1197 + */ 1198 + if (port->nr_dports == 1 && 1199 + port->component_reg_phys != CXL_RESOURCE_NONE) { 1200 + rc = cxl_port_setup_regs(port, port->component_reg_phys); 1201 + if (rc) 1202 + return ERR_PTR(rc); 1203 + port->component_reg_phys = CXL_RESOURCE_NONE; 1204 + } 1205 + 1203 1206 return dport; 1204 1207 } 1205 1208 ··· 1369 1348 return port; 1370 1349 } 1371 1350 1372 - static struct cxl_port *find_cxl_port_at(struct cxl_port *parent_port, 1373 - struct device *dport_dev, 1374 - struct cxl_dport **dport) 1375 - { 1376 - struct cxl_find_port_ctx ctx = { 1377 - .dport_dev = dport_dev, 1378 - .parent_port = parent_port, 1379 - .dport = dport, 1380 - }; 1381 - struct cxl_port *port; 1382 - 1383 - port = __find_cxl_port(&ctx); 1384 - return port; 1385 - } 1386 - 1387 1351 /* 1388 1352 * All users of grandparent() are using it to walk PCIe-like switch port 1389 1353 * hierarchy. A PCIe switch is comprised of a bridge device representing the ··· 1429 1423 * through ->remove(). This "bottom-up" removal selectively removes individual 1430 1424 * child ports manually. This depends on devm_cxl_add_port() to not change is 1431 1425 * devm action registration order, and for dports to have already been 1432 - * destroyed by reap_dports(). 1426 + * destroyed by del_dports(). 1433 1427 */ 1434 1428 static void delete_switch_port(struct cxl_port *port) 1435 1429 { ··· 1438 1432 devm_release_action(port->dev.parent, unregister_port, port); 1439 1433 } 1440 1434 1441 - static void reap_dports(struct cxl_port *port) 1435 + static void del_dport(struct cxl_dport *dport) 1436 + { 1437 + struct cxl_port *port = dport->port; 1438 + 1439 + devm_release_action(&port->dev, cxl_dport_unlink, dport); 1440 + devm_release_action(&port->dev, cxl_dport_remove, dport); 1441 + devm_kfree(&port->dev, dport); 1442 + } 1443 + 1444 + static void del_dports(struct cxl_port *port) 1442 1445 { 1443 1446 struct cxl_dport *dport; 1444 1447 unsigned long index; 1445 1448 1446 1449 device_lock_assert(&port->dev); 1447 1450 1448 - xa_for_each(&port->dports, index, dport) { 1449 - devm_release_action(&port->dev, cxl_dport_unlink, dport); 1450 - devm_release_action(&port->dev, cxl_dport_remove, dport); 1451 - devm_kfree(&port->dev, dport); 1452 - } 1451 + xa_for_each(&port->dports, index, dport) 1452 + del_dport(dport); 1453 1453 } 1454 1454 1455 1455 struct detach_ctx { ··· 1513 1501 */ 1514 1502 died = true; 1515 1503 port->dead = true; 1516 - reap_dports(port); 1504 + del_dports(port); 1517 1505 } 1518 1506 device_unlock(&port->dev); 1519 1507 ··· 1544 1532 return map.resource; 1545 1533 } 1546 1534 1535 + static int match_port_by_uport(struct device *dev, const void *data) 1536 + { 1537 + const struct device *uport_dev = data; 1538 + struct cxl_port *port; 1539 + 1540 + if (!is_cxl_port(dev)) 1541 + return 0; 1542 + 1543 + port = to_cxl_port(dev); 1544 + return uport_dev == port->uport_dev; 1545 + } 1546 + 1547 + /* 1548 + * Function takes a device reference on the port device. Caller should do a 1549 + * put_device() when done. 1550 + */ 1551 + static struct cxl_port *find_cxl_port_by_uport(struct device *uport_dev) 1552 + { 1553 + struct device *dev; 1554 + 1555 + dev = bus_find_device(&cxl_bus_type, NULL, uport_dev, match_port_by_uport); 1556 + if (dev) 1557 + return to_cxl_port(dev); 1558 + return NULL; 1559 + } 1560 + 1561 + static int update_decoder_targets(struct device *dev, void *data) 1562 + { 1563 + struct cxl_dport *dport = data; 1564 + struct cxl_switch_decoder *cxlsd; 1565 + struct cxl_decoder *cxld; 1566 + int i; 1567 + 1568 + if (!is_switch_decoder(dev)) 1569 + return 0; 1570 + 1571 + cxlsd = to_cxl_switch_decoder(dev); 1572 + cxld = &cxlsd->cxld; 1573 + guard(rwsem_write)(&cxl_rwsem.region); 1574 + 1575 + for (i = 0; i < cxld->interleave_ways; i++) { 1576 + if (cxld->target_map[i] == dport->port_id) { 1577 + cxlsd->target[i] = dport; 1578 + dev_dbg(dev, "dport%d found in target list, index %d\n", 1579 + dport->port_id, i); 1580 + return 1; 1581 + } 1582 + } 1583 + 1584 + return 0; 1585 + } 1586 + 1587 + DEFINE_FREE(del_cxl_dport, struct cxl_dport *, if (!IS_ERR_OR_NULL(_T)) del_dport(_T)) 1588 + static struct cxl_dport *cxl_port_add_dport(struct cxl_port *port, 1589 + struct device *dport_dev) 1590 + { 1591 + struct cxl_dport *dport; 1592 + int rc; 1593 + 1594 + device_lock_assert(&port->dev); 1595 + if (!port->dev.driver) 1596 + return ERR_PTR(-ENXIO); 1597 + 1598 + dport = cxl_find_dport_by_dev(port, dport_dev); 1599 + if (dport) { 1600 + dev_dbg(&port->dev, "dport%d:%s already exists\n", 1601 + dport->port_id, dev_name(dport_dev)); 1602 + return ERR_PTR(-EBUSY); 1603 + } 1604 + 1605 + struct cxl_dport *new_dport __free(del_cxl_dport) = 1606 + devm_cxl_add_dport_by_dev(port, dport_dev); 1607 + if (IS_ERR(new_dport)) 1608 + return new_dport; 1609 + 1610 + cxl_switch_parse_cdat(new_dport); 1611 + 1612 + if (ida_is_empty(&port->decoder_ida)) { 1613 + rc = devm_cxl_switch_port_decoders_setup(port); 1614 + if (rc) 1615 + return ERR_PTR(rc); 1616 + dev_dbg(&port->dev, "first dport%d:%s added with decoders\n", 1617 + new_dport->port_id, dev_name(dport_dev)); 1618 + return no_free_ptr(new_dport); 1619 + } 1620 + 1621 + /* New dport added, update the decoder targets */ 1622 + device_for_each_child(&port->dev, new_dport, update_decoder_targets); 1623 + 1624 + dev_dbg(&port->dev, "dport%d:%s added\n", new_dport->port_id, 1625 + dev_name(dport_dev)); 1626 + 1627 + return no_free_ptr(new_dport); 1628 + } 1629 + 1630 + static struct cxl_dport *devm_cxl_create_port(struct device *ep_dev, 1631 + struct cxl_port *parent_port, 1632 + struct cxl_dport *parent_dport, 1633 + struct device *uport_dev, 1634 + struct device *dport_dev) 1635 + { 1636 + resource_size_t component_reg_phys; 1637 + 1638 + device_lock_assert(&parent_port->dev); 1639 + if (!parent_port->dev.driver) { 1640 + dev_warn(ep_dev, 1641 + "port %s:%s:%s disabled, failed to enumerate CXL.mem\n", 1642 + dev_name(&parent_port->dev), dev_name(uport_dev), 1643 + dev_name(dport_dev)); 1644 + } 1645 + 1646 + struct cxl_port *port __free(put_cxl_port) = 1647 + find_cxl_port_by_uport(uport_dev); 1648 + if (!port) { 1649 + component_reg_phys = find_component_registers(uport_dev); 1650 + port = devm_cxl_add_port(&parent_port->dev, uport_dev, 1651 + component_reg_phys, parent_dport); 1652 + if (IS_ERR(port)) 1653 + return ERR_CAST(port); 1654 + 1655 + /* 1656 + * retry to make sure a port is found. a port device 1657 + * reference is taken. 1658 + */ 1659 + port = find_cxl_port_by_uport(uport_dev); 1660 + if (!port) 1661 + return ERR_PTR(-ENODEV); 1662 + 1663 + dev_dbg(ep_dev, "created port %s:%s\n", 1664 + dev_name(&port->dev), dev_name(port->uport_dev)); 1665 + } else { 1666 + /* 1667 + * Port was created before right before this function is 1668 + * called. Signal the caller to deal with it. 1669 + */ 1670 + return ERR_PTR(-EAGAIN); 1671 + } 1672 + 1673 + guard(device)(&port->dev); 1674 + return cxl_port_add_dport(port, dport_dev); 1675 + } 1676 + 1547 1677 static int add_port_attach_ep(struct cxl_memdev *cxlmd, 1548 1678 struct device *uport_dev, 1549 1679 struct device *dport_dev) 1550 1680 { 1551 1681 struct device *dparent = grandparent(dport_dev); 1552 1682 struct cxl_dport *dport, *parent_dport; 1553 - resource_size_t component_reg_phys; 1554 1683 int rc; 1555 1684 1556 - if (!dparent) { 1685 + if (is_cxl_host_bridge(dparent)) { 1557 1686 /* 1558 1687 * The iteration reached the topology root without finding the 1559 1688 * CXL-root 'cxl_port' on a previous iteration, fail for now to ··· 1706 1553 } 1707 1554 1708 1555 struct cxl_port *parent_port __free(put_cxl_port) = 1709 - find_cxl_port(dparent, &parent_dport); 1556 + find_cxl_port_by_uport(dparent->parent); 1710 1557 if (!parent_port) { 1711 1558 /* iterate to create this parent_port */ 1712 1559 return -EAGAIN; 1713 1560 } 1714 1561 1715 - /* 1716 - * Definition with __free() here to keep the sequence of 1717 - * dereferencing the device of the port before the parent_port releasing. 1718 - */ 1719 - struct cxl_port *port __free(put_cxl_port) = NULL; 1720 1562 scoped_guard(device, &parent_port->dev) { 1721 - if (!parent_port->dev.driver) { 1722 - dev_warn(&cxlmd->dev, 1723 - "port %s:%s disabled, failed to enumerate CXL.mem\n", 1724 - dev_name(&parent_port->dev), dev_name(uport_dev)); 1725 - return -ENXIO; 1563 + parent_dport = cxl_find_dport_by_dev(parent_port, dparent); 1564 + if (!parent_dport) { 1565 + parent_dport = cxl_port_add_dport(parent_port, dparent); 1566 + if (IS_ERR(parent_dport)) 1567 + return PTR_ERR(parent_dport); 1726 1568 } 1727 1569 1728 - port = find_cxl_port_at(parent_port, dport_dev, &dport); 1729 - if (!port) { 1730 - component_reg_phys = find_component_registers(uport_dev); 1731 - port = devm_cxl_add_port(&parent_port->dev, uport_dev, 1732 - component_reg_phys, parent_dport); 1733 - if (IS_ERR(port)) 1734 - return PTR_ERR(port); 1735 - 1736 - /* retry find to pick up the new dport information */ 1737 - port = find_cxl_port_at(parent_port, dport_dev, &dport); 1738 - if (!port) 1739 - return -ENXIO; 1570 + dport = devm_cxl_create_port(&cxlmd->dev, parent_port, 1571 + parent_dport, uport_dev, 1572 + dport_dev); 1573 + if (IS_ERR(dport)) { 1574 + /* Port already exists, restart iteration */ 1575 + if (PTR_ERR(dport) == -EAGAIN) 1576 + return 0; 1577 + return PTR_ERR(dport); 1740 1578 } 1741 1579 } 1742 1580 1743 - dev_dbg(&cxlmd->dev, "add to new port %s:%s\n", 1744 - dev_name(&port->dev), dev_name(port->uport_dev)); 1745 1581 rc = cxl_add_ep(dport, &cxlmd->dev); 1746 1582 if (rc == -EBUSY) { 1747 1583 /* ··· 1741 1599 } 1742 1600 1743 1601 return rc; 1602 + } 1603 + 1604 + static struct cxl_dport *find_or_add_dport(struct cxl_port *port, 1605 + struct device *dport_dev) 1606 + { 1607 + struct cxl_dport *dport; 1608 + 1609 + device_lock_assert(&port->dev); 1610 + dport = cxl_find_dport_by_dev(port, dport_dev); 1611 + if (!dport) { 1612 + dport = cxl_port_add_dport(port, dport_dev); 1613 + if (IS_ERR(dport)) 1614 + return dport; 1615 + 1616 + /* New dport added, restart iteration */ 1617 + return ERR_PTR(-EAGAIN); 1618 + } 1619 + 1620 + return dport; 1744 1621 } 1745 1622 1746 1623 int devm_cxl_enumerate_ports(struct cxl_memdev *cxlmd) ··· 1790 1629 struct device *uport_dev; 1791 1630 struct cxl_dport *dport; 1792 1631 1793 - /* 1794 - * The terminal "grandparent" in PCI is NULL and @platform_bus 1795 - * for platform devices 1796 - */ 1797 - if (!dport_dev || dport_dev == &platform_bus) 1632 + if (is_cxl_host_bridge(dport_dev)) 1798 1633 return 0; 1799 1634 1800 1635 uport_dev = dport_dev->parent; ··· 1804 1647 dev_name(iter), dev_name(dport_dev), 1805 1648 dev_name(uport_dev)); 1806 1649 struct cxl_port *port __free(put_cxl_port) = 1807 - find_cxl_port(dport_dev, &dport); 1650 + find_cxl_port_by_uport(uport_dev); 1808 1651 if (port) { 1809 1652 dev_dbg(&cxlmd->dev, 1810 1653 "found already registered port %s:%s\n", 1811 1654 dev_name(&port->dev), 1812 1655 dev_name(port->uport_dev)); 1656 + 1657 + /* 1658 + * RP port enumerated by cxl_acpi without dport will 1659 + * have the dport added here. 1660 + */ 1661 + scoped_guard(device, &port->dev) { 1662 + dport = find_or_add_dport(port, dport_dev); 1663 + if (IS_ERR(dport)) { 1664 + if (PTR_ERR(dport) == -EAGAIN) 1665 + goto retry; 1666 + return PTR_ERR(dport); 1667 + } 1668 + } 1669 + 1813 1670 rc = cxl_add_ep(dport, &cxlmd->dev); 1814 1671 1815 1672 /* ··· 1875 1704 EXPORT_SYMBOL_NS_GPL(cxl_mem_find_port, "CXL"); 1876 1705 1877 1706 static int decoder_populate_targets(struct cxl_switch_decoder *cxlsd, 1878 - struct cxl_port *port, int *target_map) 1707 + struct cxl_port *port) 1879 1708 { 1709 + struct cxl_decoder *cxld = &cxlsd->cxld; 1880 1710 int i; 1881 - 1882 - if (!target_map) 1883 - return 0; 1884 1711 1885 1712 device_lock_assert(&port->dev); 1886 1713 1887 1714 if (xa_empty(&port->dports)) 1888 - return -EINVAL; 1715 + return 0; 1889 1716 1890 1717 guard(rwsem_write)(&cxl_rwsem.region); 1891 1718 for (i = 0; i < cxlsd->cxld.interleave_ways; i++) { 1892 - struct cxl_dport *dport = find_dport(port, target_map[i]); 1719 + struct cxl_dport *dport = find_dport(port, cxld->target_map[i]); 1893 1720 1894 - if (!dport) 1895 - return -ENXIO; 1721 + if (!dport) { 1722 + /* dport may be activated later */ 1723 + continue; 1724 + } 1896 1725 cxlsd->target[i] = dport; 1897 1726 } 1898 1727 ··· 2081 1910 /** 2082 1911 * cxl_decoder_add_locked - Add a decoder with targets 2083 1912 * @cxld: The cxl decoder allocated by cxl_<type>_decoder_alloc() 2084 - * @target_map: A list of downstream ports that this decoder can direct memory 2085 - * traffic to. These numbers should correspond with the port number 2086 - * in the PCIe Link Capabilities structure. 2087 1913 * 2088 1914 * Certain types of decoders may not have any targets. The main example of this 2089 1915 * is an endpoint device. A more awkward example is a hostbridge whose root ··· 2094 1926 * Return: Negative error code if the decoder wasn't properly configured; else 2095 1927 * returns 0. 2096 1928 */ 2097 - int cxl_decoder_add_locked(struct cxl_decoder *cxld, int *target_map) 1929 + int cxl_decoder_add_locked(struct cxl_decoder *cxld) 2098 1930 { 2099 1931 struct cxl_port *port; 2100 1932 struct device *dev; ··· 2115 1947 if (!is_endpoint_decoder(dev)) { 2116 1948 struct cxl_switch_decoder *cxlsd = to_cxl_switch_decoder(dev); 2117 1949 2118 - rc = decoder_populate_targets(cxlsd, port, target_map); 1950 + rc = decoder_populate_targets(cxlsd, port); 2119 1951 if (rc && (cxld->flags & CXL_DECODER_F_ENABLE)) { 2120 1952 dev_err(&port->dev, 2121 1953 "Failed to populate active decoder targets\n"); ··· 2134 1966 /** 2135 1967 * cxl_decoder_add - Add a decoder with targets 2136 1968 * @cxld: The cxl decoder allocated by cxl_<type>_decoder_alloc() 2137 - * @target_map: A list of downstream ports that this decoder can direct memory 2138 - * traffic to. These numbers should correspond with the port number 2139 - * in the PCIe Link Capabilities structure. 2140 1969 * 2141 1970 * This is the unlocked variant of cxl_decoder_add_locked(). 2142 1971 * See cxl_decoder_add_locked(). ··· 2141 1976 * Context: Process context. Takes and releases the device lock of the port that 2142 1977 * owns the @cxld. 2143 1978 */ 2144 - int cxl_decoder_add(struct cxl_decoder *cxld, int *target_map) 1979 + int cxl_decoder_add(struct cxl_decoder *cxld) 2145 1980 { 2146 1981 struct cxl_port *port; 2147 1982 ··· 2154 1989 port = to_cxl_port(cxld->dev.parent); 2155 1990 2156 1991 guard(device)(&port->dev); 2157 - return cxl_decoder_add_locked(cxld, target_map); 1992 + return cxl_decoder_add_locked(cxld); 2158 1993 } 2159 1994 EXPORT_SYMBOL_NS_GPL(cxl_decoder_add, "CXL"); 2160 1995
+251 -15
drivers/cxl/core/region.c
··· 2 2 /* Copyright(c) 2022 Intel Corporation. All rights reserved. */ 3 3 #include <linux/memregion.h> 4 4 #include <linux/genalloc.h> 5 + #include <linux/debugfs.h> 5 6 #include <linux/device.h> 6 7 #include <linux/module.h> 7 8 #include <linux/memory.h> ··· 11 10 #include <linux/sort.h> 12 11 #include <linux/idr.h> 13 12 #include <linux/memory-tiers.h> 13 + #include <linux/string_choices.h> 14 14 #include <cxlmem.h> 15 15 #include <cxl.h> 16 16 #include "core.h" ··· 31 29 * 2. Interleave size 32 30 * 3. Decoder targets 33 31 */ 32 + 33 + /* 34 + * nodemask that sets per node when the access_coordinates for the node has 35 + * been updated by the CXL memory hotplug notifier. 36 + */ 37 + static nodemask_t nodemask_region_seen = NODE_MASK_NONE; 34 38 35 39 static struct cxl_region *to_cxl_region(struct device *dev); 36 40 ··· 1476 1468 dev_name(port->uport_dev), dev_name(&port->dev), 1477 1469 __func__, cxld->interleave_ways, 1478 1470 cxld->interleave_granularity, 1479 - (cxld->flags & CXL_DECODER_F_ENABLE) ? 1480 - "enabled" : 1481 - "disabled", 1471 + str_enabled_disabled(cxld->flags & CXL_DECODER_F_ENABLE), 1482 1472 cxld->hpa_range.start, cxld->hpa_range.end); 1483 1473 return -ENXIO; 1484 1474 } ··· 1516 1510 cxl_rr->nr_targets_set); 1517 1511 return -ENXIO; 1518 1512 } 1519 - } else 1513 + } else { 1520 1514 cxlsd->target[cxl_rr->nr_targets_set] = ep->dport; 1515 + cxlsd->cxld.target_map[cxl_rr->nr_targets_set] = ep->dport->port_id; 1516 + } 1521 1517 inc = 1; 1522 1518 out_target_set: 1523 1519 cxl_rr->nr_targets_set += inc; ··· 2450 2442 2451 2443 for (int i = 0; i < ACCESS_COORDINATE_MAX; i++) { 2452 2444 if (cxlr->coord[i].read_bandwidth) { 2453 - rc = 0; 2454 - if (cxl_need_node_perf_attrs_update(nid)) 2455 - node_set_perf_attrs(nid, &cxlr->coord[i], i); 2456 - else 2457 - rc = cxl_update_hmat_access_coordinates(nid, cxlr, i); 2458 - 2459 - if (rc == 0) 2460 - cset++; 2445 + node_update_perf_attrs(nid, &cxlr->coord[i], i); 2446 + cset++; 2461 2447 } 2462 2448 } 2463 2449 ··· 2487 2485 */ 2488 2486 region_nid = phys_to_target_node(cxlr->params.res->start); 2489 2487 if (nid != region_nid) 2488 + return NOTIFY_DONE; 2489 + 2490 + /* No action needed if node bit already set */ 2491 + if (node_test_and_set(nid, nodemask_region_seen)) 2490 2492 return NOTIFY_DONE; 2491 2493 2492 2494 if (!cxl_region_update_coordinates(cxlr, nid)) ··· 2924 2918 return false; 2925 2919 } 2926 2920 2921 + static bool has_hpa_to_spa(struct cxl_root_decoder *cxlrd) 2922 + { 2923 + return cxlrd->ops && cxlrd->ops->hpa_to_spa; 2924 + } 2925 + 2926 + static bool has_spa_to_hpa(struct cxl_root_decoder *cxlrd) 2927 + { 2928 + return cxlrd->ops && cxlrd->ops->spa_to_hpa; 2929 + } 2930 + 2927 2931 u64 cxl_dpa_to_hpa(struct cxl_region *cxlr, const struct cxl_memdev *cxlmd, 2928 2932 u64 dpa) 2929 2933 { ··· 2988 2972 hpa = hpa_offset + p->res->start + p->cache_size; 2989 2973 2990 2974 /* Root decoder translation overrides typical modulo decode */ 2991 - if (cxlrd->hpa_to_spa) 2992 - hpa = cxlrd->hpa_to_spa(cxlrd, hpa); 2975 + if (has_hpa_to_spa(cxlrd)) 2976 + hpa = cxlrd->ops->hpa_to_spa(cxlrd, hpa); 2993 2977 2994 2978 if (!cxl_resource_contains_addr(p->res, hpa)) { 2995 2979 dev_dbg(&cxlr->dev, ··· 2998 2982 } 2999 2983 3000 2984 /* Simple chunk check, by pos & gran, only applies to modulo decodes */ 3001 - if (!cxlrd->hpa_to_spa && (!cxl_is_hpa_in_chunk(hpa, cxlr, pos))) 2985 + if (!has_hpa_to_spa(cxlrd) && (!cxl_is_hpa_in_chunk(hpa, cxlr, pos))) 3002 2986 return ULLONG_MAX; 3003 2987 3004 2988 return hpa; 2989 + } 2990 + 2991 + struct dpa_result { 2992 + struct cxl_memdev *cxlmd; 2993 + u64 dpa; 2994 + }; 2995 + 2996 + static int region_offset_to_dpa_result(struct cxl_region *cxlr, u64 offset, 2997 + struct dpa_result *result) 2998 + { 2999 + struct cxl_region_params *p = &cxlr->params; 3000 + struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(cxlr->dev.parent); 3001 + struct cxl_endpoint_decoder *cxled; 3002 + u64 hpa, hpa_offset, dpa_offset; 3003 + u64 bits_upper, bits_lower; 3004 + u64 shifted, rem, temp; 3005 + u16 eig = 0; 3006 + u8 eiw = 0; 3007 + int pos; 3008 + 3009 + lockdep_assert_held(&cxl_rwsem.region); 3010 + lockdep_assert_held(&cxl_rwsem.dpa); 3011 + 3012 + /* Input validation ensures valid ways and gran */ 3013 + granularity_to_eig(p->interleave_granularity, &eig); 3014 + ways_to_eiw(p->interleave_ways, &eiw); 3015 + 3016 + /* 3017 + * If the root decoder has SPA to CXL HPA callback, use it. Otherwise 3018 + * CXL HPA is assumed to equal SPA. 3019 + */ 3020 + if (has_spa_to_hpa(cxlrd)) { 3021 + hpa = cxlrd->ops->spa_to_hpa(cxlrd, p->res->start + offset); 3022 + hpa_offset = hpa - p->res->start; 3023 + } else { 3024 + hpa_offset = offset; 3025 + } 3026 + /* 3027 + * Interleave position: CXL Spec 3.2 Section 8.2.4.20.13 3028 + * eiw < 8 3029 + * Position is in the IW bits at HPA_OFFSET[IG+8+IW-1:IG+8]. 3030 + * Per spec "remove IW bits starting with bit position IG+8" 3031 + * eiw >= 8 3032 + * Position is not explicitly stored in HPA_OFFSET bits. It is 3033 + * derived from the modulo operation of the upper bits using 3034 + * the total number of interleave ways. 3035 + */ 3036 + if (eiw < 8) { 3037 + pos = (hpa_offset >> (eig + 8)) & GENMASK(eiw - 1, 0); 3038 + } else { 3039 + shifted = hpa_offset >> (eig + 8); 3040 + div64_u64_rem(shifted, p->interleave_ways, &rem); 3041 + pos = rem; 3042 + } 3043 + if (pos < 0 || pos >= p->nr_targets) { 3044 + dev_dbg(&cxlr->dev, "Invalid position %d for %d targets\n", 3045 + pos, p->nr_targets); 3046 + return -ENXIO; 3047 + } 3048 + 3049 + /* 3050 + * DPA offset: CXL Spec 3.2 Section 8.2.4.20.13 3051 + * Lower bits [IG+7:0] pass through unchanged 3052 + * (eiw < 8) 3053 + * Per spec: DPAOffset[51:IG+8] = (HPAOffset[51:IG+IW+8] >> IW) 3054 + * Clear the position bits to isolate upper section, then 3055 + * reverse the left shift by eiw that occurred during DPA->HPA 3056 + * (eiw >= 8) 3057 + * Per spec: DPAOffset[51:IG+8] = HPAOffset[51:IG+IW] / 3 3058 + * Extract upper bits from the correct bit range and divide by 3 3059 + * to recover the original DPA upper bits 3060 + */ 3061 + bits_lower = hpa_offset & GENMASK_ULL(eig + 7, 0); 3062 + if (eiw < 8) { 3063 + temp = hpa_offset &= ~((u64)GENMASK(eig + eiw + 8 - 1, 0)); 3064 + dpa_offset = temp >> eiw; 3065 + } else { 3066 + bits_upper = div64_u64(hpa_offset >> (eig + eiw), 3); 3067 + dpa_offset = bits_upper << (eig + 8); 3068 + } 3069 + dpa_offset |= bits_lower; 3070 + 3071 + /* Look-up and return the result: a memdev and a DPA */ 3072 + for (int i = 0; i < p->nr_targets; i++) { 3073 + cxled = p->targets[i]; 3074 + if (cxled->pos != pos) 3075 + continue; 3076 + result->cxlmd = cxled_to_memdev(cxled); 3077 + result->dpa = cxl_dpa_resource_start(cxled) + dpa_offset; 3078 + 3079 + return 0; 3080 + } 3081 + dev_err(&cxlr->dev, "No device found for position %d\n", pos); 3082 + 3083 + return -ENXIO; 3005 3084 } 3006 3085 3007 3086 static struct lock_class_key cxl_pmem_region_key; ··· 3653 3542 unregister_mt_adistance_algorithm(&cxlr->adist_notifier); 3654 3543 } 3655 3544 3545 + static void remove_debugfs(void *dentry) 3546 + { 3547 + debugfs_remove_recursive(dentry); 3548 + } 3549 + 3550 + static int validate_region_offset(struct cxl_region *cxlr, u64 offset) 3551 + { 3552 + struct cxl_region_params *p = &cxlr->params; 3553 + resource_size_t region_size; 3554 + u64 hpa; 3555 + 3556 + if (offset < p->cache_size) { 3557 + dev_err(&cxlr->dev, 3558 + "Offset %#llx is within extended linear cache %pr\n", 3559 + offset, &p->cache_size); 3560 + return -EINVAL; 3561 + } 3562 + 3563 + region_size = resource_size(p->res); 3564 + if (offset >= region_size) { 3565 + dev_err(&cxlr->dev, "Offset %#llx exceeds region size %pr\n", 3566 + offset, &region_size); 3567 + return -EINVAL; 3568 + } 3569 + 3570 + hpa = p->res->start + offset; 3571 + if (hpa < p->res->start || hpa > p->res->end) { 3572 + dev_err(&cxlr->dev, "HPA %#llx not in region %pr\n", hpa, 3573 + p->res); 3574 + return -EINVAL; 3575 + } 3576 + 3577 + return 0; 3578 + } 3579 + 3580 + static int cxl_region_debugfs_poison_inject(void *data, u64 offset) 3581 + { 3582 + struct dpa_result result = { .dpa = ULLONG_MAX, .cxlmd = NULL }; 3583 + struct cxl_region *cxlr = data; 3584 + int rc; 3585 + 3586 + ACQUIRE(rwsem_read_intr, region_rwsem)(&cxl_rwsem.region); 3587 + if ((rc = ACQUIRE_ERR(rwsem_read_intr, &region_rwsem))) 3588 + return rc; 3589 + 3590 + ACQUIRE(rwsem_read_intr, dpa_rwsem)(&cxl_rwsem.dpa); 3591 + if ((rc = ACQUIRE_ERR(rwsem_read_intr, &dpa_rwsem))) 3592 + return rc; 3593 + 3594 + if (validate_region_offset(cxlr, offset)) 3595 + return -EINVAL; 3596 + 3597 + rc = region_offset_to_dpa_result(cxlr, offset, &result); 3598 + if (rc || !result.cxlmd || result.dpa == ULLONG_MAX) { 3599 + dev_dbg(&cxlr->dev, 3600 + "Failed to resolve DPA for region offset %#llx rc %d\n", 3601 + offset, rc); 3602 + 3603 + return rc ? rc : -EINVAL; 3604 + } 3605 + 3606 + return cxl_inject_poison_locked(result.cxlmd, result.dpa); 3607 + } 3608 + 3609 + DEFINE_DEBUGFS_ATTRIBUTE(cxl_poison_inject_fops, NULL, 3610 + cxl_region_debugfs_poison_inject, "%llx\n"); 3611 + 3612 + static int cxl_region_debugfs_poison_clear(void *data, u64 offset) 3613 + { 3614 + struct dpa_result result = { .dpa = ULLONG_MAX, .cxlmd = NULL }; 3615 + struct cxl_region *cxlr = data; 3616 + int rc; 3617 + 3618 + ACQUIRE(rwsem_read_intr, region_rwsem)(&cxl_rwsem.region); 3619 + if ((rc = ACQUIRE_ERR(rwsem_read_intr, &region_rwsem))) 3620 + return rc; 3621 + 3622 + ACQUIRE(rwsem_read_intr, dpa_rwsem)(&cxl_rwsem.dpa); 3623 + if ((rc = ACQUIRE_ERR(rwsem_read_intr, &dpa_rwsem))) 3624 + return rc; 3625 + 3626 + if (validate_region_offset(cxlr, offset)) 3627 + return -EINVAL; 3628 + 3629 + rc = region_offset_to_dpa_result(cxlr, offset, &result); 3630 + if (rc || !result.cxlmd || result.dpa == ULLONG_MAX) { 3631 + dev_dbg(&cxlr->dev, 3632 + "Failed to resolve DPA for region offset %#llx rc %d\n", 3633 + offset, rc); 3634 + 3635 + return rc ? rc : -EINVAL; 3636 + } 3637 + 3638 + return cxl_clear_poison_locked(result.cxlmd, result.dpa); 3639 + } 3640 + 3641 + DEFINE_DEBUGFS_ATTRIBUTE(cxl_poison_clear_fops, NULL, 3642 + cxl_region_debugfs_poison_clear, "%llx\n"); 3643 + 3656 3644 static int cxl_region_can_probe(struct cxl_region *cxlr) 3657 3645 { 3658 3646 struct cxl_region_params *p = &cxlr->params; ··· 3781 3571 { 3782 3572 struct cxl_region *cxlr = to_cxl_region(dev); 3783 3573 struct cxl_region_params *p = &cxlr->params; 3574 + bool poison_supported = true; 3784 3575 int rc; 3785 3576 3786 3577 rc = cxl_region_can_probe(cxlr); ··· 3804 3593 rc = devm_add_action_or_reset(&cxlr->dev, shutdown_notifiers, cxlr); 3805 3594 if (rc) 3806 3595 return rc; 3596 + 3597 + /* Create poison attributes if all memdevs support the capabilities */ 3598 + for (int i = 0; i < p->nr_targets; i++) { 3599 + struct cxl_endpoint_decoder *cxled = p->targets[i]; 3600 + struct cxl_memdev *cxlmd = cxled_to_memdev(cxled); 3601 + 3602 + if (!cxl_memdev_has_poison_cmd(cxlmd, CXL_POISON_ENABLED_INJECT) || 3603 + !cxl_memdev_has_poison_cmd(cxlmd, CXL_POISON_ENABLED_CLEAR)) { 3604 + poison_supported = false; 3605 + break; 3606 + } 3607 + } 3608 + 3609 + if (poison_supported) { 3610 + struct dentry *dentry; 3611 + 3612 + dentry = cxl_debugfs_create_dir(dev_name(dev)); 3613 + debugfs_create_file("inject_poison", 0200, dentry, cxlr, 3614 + &cxl_poison_inject_fops); 3615 + debugfs_create_file("clear_poison", 0200, dentry, cxlr, 3616 + &cxl_poison_clear_fops); 3617 + rc = devm_add_action_or_reset(dev, remove_debugfs, dentry); 3618 + if (rc) 3619 + return rc; 3620 + } 3807 3621 3808 3622 switch (cxlr->mode) { 3809 3623 case CXL_PARTMODE_PMEM:
+45 -12
drivers/cxl/cxl.h
··· 357 357 * @target_type: accelerator vs expander (type2 vs type3) selector 358 358 * @region: currently assigned region for this decoder 359 359 * @flags: memory type capabilities and locking 360 + * @target_map: cached copy of hardware port-id list, available at init 361 + * before all @dport objects have been instantiated. While 362 + * dport id is 8bit, CFMWS interleave targets are 32bits. 360 363 * @commit: device/decoder-type specific callback to commit settings to hw 361 364 * @reset: device/decoder-type specific callback to reset hw settings 362 365 */ ··· 372 369 enum cxl_decoder_type target_type; 373 370 struct cxl_region *region; 374 371 unsigned long flags; 372 + u32 target_map[CXL_DECODER_MAX_INTERLEAVE]; 375 373 int (*commit)(struct cxl_decoder *cxld); 376 374 void (*reset)(struct cxl_decoder *cxld); 377 375 }; ··· 423 419 }; 424 420 425 421 struct cxl_root_decoder; 426 - typedef u64 (*cxl_hpa_to_spa_fn)(struct cxl_root_decoder *cxlrd, u64 hpa); 422 + /** 423 + * struct cxl_rd_ops - CXL root decoder callback operations 424 + * @hpa_to_spa: Convert host physical address to system physical address 425 + * @spa_to_hpa: Convert system physical address to host physical address 426 + */ 427 + struct cxl_rd_ops { 428 + u64 (*hpa_to_spa)(struct cxl_root_decoder *cxlrd, u64 hpa); 429 + u64 (*spa_to_hpa)(struct cxl_root_decoder *cxlrd, u64 spa); 430 + }; 427 431 428 432 /** 429 433 * struct cxl_root_decoder - Static platform CXL address decoder 430 434 * @res: host / parent resource for region allocations 431 435 * @cache_size: extended linear cache size if exists, otherwise zero. 432 436 * @region_id: region id for next region provisioning event 433 - * @hpa_to_spa: translate CXL host-physical-address to Platform system-physical-address 434 437 * @platform_data: platform specific configuration data 435 438 * @range_lock: sync region autodiscovery by address range 436 439 * @qos_class: QoS performance class cookie 440 + * @ops: CXL root decoder operations 437 441 * @cxlsd: base cxl switch decoder 438 442 */ 439 443 struct cxl_root_decoder { 440 444 struct resource *res; 441 445 resource_size_t cache_size; 442 446 atomic_t region_id; 443 - cxl_hpa_to_spa_fn hpa_to_spa; 444 447 void *platform_data; 445 448 struct mutex range_lock; 446 449 int qos_class; 450 + struct cxl_rd_ops *ops; 447 451 struct cxl_switch_decoder cxlsd; 448 452 }; 449 453 ··· 607 595 * @cdat: Cached CDAT data 608 596 * @cdat_available: Should a CDAT attribute be available in sysfs 609 597 * @pci_latency: Upstream latency in picoseconds 598 + * @component_reg_phys: Physical address of component register 610 599 */ 611 600 struct cxl_port { 612 601 struct device dev; ··· 631 618 } cdat; 632 619 bool cdat_available; 633 620 long pci_latency; 621 + resource_size_t component_reg_phys; 634 622 }; 635 623 636 624 /** ··· 795 781 unsigned int nr_targets); 796 782 struct cxl_switch_decoder *cxl_switch_decoder_alloc(struct cxl_port *port, 797 783 unsigned int nr_targets); 798 - int cxl_decoder_add(struct cxl_decoder *cxld, int *target_map); 784 + int cxl_decoder_add(struct cxl_decoder *cxld); 799 785 struct cxl_endpoint_decoder *cxl_endpoint_decoder_alloc(struct cxl_port *port); 800 - int cxl_decoder_add_locked(struct cxl_decoder *cxld, int *target_map); 786 + int cxl_decoder_add_locked(struct cxl_decoder *cxld); 801 787 int cxl_decoder_autoremove(struct device *host, struct cxl_decoder *cxld); 802 788 static inline int cxl_root_decoder_autoremove(struct device *host, 803 789 struct cxl_root_decoder *cxlrd) ··· 820 806 struct range dvsec_range[2]; 821 807 }; 822 808 823 - struct cxl_hdm; 824 - struct cxl_hdm *devm_cxl_setup_hdm(struct cxl_port *port, 825 - struct cxl_endpoint_dvsec_info *info); 826 - int devm_cxl_enumerate_decoders(struct cxl_hdm *cxlhdm, 827 - struct cxl_endpoint_dvsec_info *info); 828 - int devm_cxl_add_passthrough_decoder(struct cxl_port *port); 809 + int devm_cxl_switch_port_decoders_setup(struct cxl_port *port); 810 + int __devm_cxl_switch_port_decoders_setup(struct cxl_port *port); 811 + int devm_cxl_endpoint_decoders_setup(struct cxl_port *port); 812 + 829 813 struct cxl_dev_state; 830 814 int cxl_dvsec_rr_decode(struct cxl_dev_state *cxlds, 831 815 struct cxl_endpoint_dvsec_info *info); ··· 902 890 #endif 903 891 904 892 void cxl_endpoint_parse_cdat(struct cxl_port *port); 905 - void cxl_switch_parse_cdat(struct cxl_port *port); 893 + void cxl_switch_parse_cdat(struct cxl_dport *dport); 906 894 907 895 int cxl_endpoint_get_perf_coordinates(struct cxl_port *port, 908 896 struct access_coordinate *coord); ··· 917 905 struct access_coordinate *c2); 918 906 919 907 bool cxl_endpoint_decoder_reset_detected(struct cxl_port *port); 908 + struct cxl_dport *devm_cxl_add_dport_by_dev(struct cxl_port *port, 909 + struct device *dport_dev); 910 + struct cxl_dport *__devm_cxl_add_dport_by_dev(struct cxl_port *port, 911 + struct device *dport_dev); 920 912 921 913 /* 922 914 * Unit test builds overrides this to __weak, find the 'strong' version ··· 931 915 #endif 932 916 933 917 u16 cxl_gpf_get_dvsec(struct device *dev); 918 + 919 + /* 920 + * Declaration for functions that are mocked by cxl_test that are called by 921 + * cxl_core. The respective functions are defined as __foo() and called by 922 + * cxl_core as foo(). The macros below ensures that those functions would 923 + * exist as foo(). See tools/testing/cxl/cxl_core_exports.c and 924 + * tools/testing/cxl/exports.h for setting up the mock functions. The dance 925 + * is done to avoid a circular dependency where cxl_core calls a function that 926 + * ends up being a mock function and goes to * cxl_test where it calls a 927 + * cxl_core function. 928 + */ 929 + #ifndef CXL_TEST_ENABLE 930 + #define DECLARE_TESTABLE(x) __##x 931 + #define devm_cxl_add_dport_by_dev DECLARE_TESTABLE(devm_cxl_add_dport_by_dev) 932 + #define devm_cxl_switch_port_decoders_setup DECLARE_TESTABLE(devm_cxl_switch_port_decoders_setup) 933 + #endif 934 + 934 935 #endif /* __CXL_H__ */
+2
drivers/cxl/cxlmem.h
··· 869 869 int cxl_trigger_poison_list(struct cxl_memdev *cxlmd); 870 870 int cxl_inject_poison(struct cxl_memdev *cxlmd, u64 dpa); 871 871 int cxl_clear_poison(struct cxl_memdev *cxlmd, u64 dpa); 872 + int cxl_inject_poison_locked(struct cxl_memdev *cxlmd, u64 dpa); 873 + int cxl_clear_poison_locked(struct cxl_memdev *cxlmd, u64 dpa); 872 874 873 875 #ifdef CONFIG_CXL_EDAC_MEM_FEATURES 874 876 int devm_cxl_memdev_edac_register(struct cxl_memdev *cxlmd);
-2
drivers/cxl/cxlpci.h
··· 129 129 130 130 int devm_cxl_port_enumerate_dports(struct cxl_port *port); 131 131 struct cxl_dev_state; 132 - int cxl_hdm_decode_init(struct cxl_dev_state *cxlds, struct cxl_hdm *cxlhdm, 133 - struct cxl_endpoint_dvsec_info *info); 134 132 void read_cdat_data(struct cxl_port *port); 135 133 void cxl_cor_error_detected(struct pci_dev *pdev); 136 134 pci_ers_result_t cxl_error_detected(struct pci_dev *pdev,
+4 -43
drivers/cxl/port.c
··· 59 59 60 60 static int cxl_switch_port_probe(struct cxl_port *port) 61 61 { 62 - struct cxl_hdm *cxlhdm; 63 - int rc; 62 + /* Reset nr_dports for rebind of driver */ 63 + port->nr_dports = 0; 64 64 65 65 /* Cache the data early to ensure is_visible() works */ 66 66 read_cdat_data(port); 67 67 68 - rc = devm_cxl_port_enumerate_dports(port); 69 - if (rc < 0) 70 - return rc; 71 - 72 - cxl_switch_parse_cdat(port); 73 - 74 - cxlhdm = devm_cxl_setup_hdm(port, NULL); 75 - if (!IS_ERR(cxlhdm)) 76 - return devm_cxl_enumerate_decoders(cxlhdm, NULL); 77 - 78 - if (PTR_ERR(cxlhdm) != -ENODEV) { 79 - dev_err(&port->dev, "Failed to map HDM decoder capability\n"); 80 - return PTR_ERR(cxlhdm); 81 - } 82 - 83 - if (rc == 1) { 84 - dev_dbg(&port->dev, "Fallback to passthrough decoder\n"); 85 - return devm_cxl_add_passthrough_decoder(port); 86 - } 87 - 88 - dev_err(&port->dev, "HDM decoder capability not found\n"); 89 - return -ENXIO; 68 + return 0; 90 69 } 91 70 92 71 static int cxl_endpoint_port_probe(struct cxl_port *port) 93 72 { 94 - struct cxl_endpoint_dvsec_info info = { .port = port }; 95 73 struct cxl_memdev *cxlmd = to_cxl_memdev(port->uport_dev); 96 - struct cxl_dev_state *cxlds = cxlmd->cxlds; 97 - struct cxl_hdm *cxlhdm; 98 74 int rc; 99 - 100 - rc = cxl_dvsec_rr_decode(cxlds, &info); 101 - if (rc < 0) 102 - return rc; 103 - 104 - cxlhdm = devm_cxl_setup_hdm(port, &info); 105 - if (IS_ERR(cxlhdm)) { 106 - if (PTR_ERR(cxlhdm) == -ENODEV) 107 - dev_err(&port->dev, "HDM decoder registers not found\n"); 108 - return PTR_ERR(cxlhdm); 109 - } 110 75 111 76 /* Cache the data early to ensure is_visible() works */ 112 77 read_cdat_data(port); ··· 82 117 if (rc) 83 118 return rc; 84 119 85 - rc = cxl_hdm_decode_init(cxlds, cxlhdm, &info); 86 - if (rc) 87 - return rc; 88 - 89 - rc = devm_cxl_enumerate_decoders(cxlhdm, &info); 120 + rc = devm_cxl_endpoint_decoders_setup(port); 90 121 if (rc) 91 122 return rc; 92 123
+2 -2
include/acpi/actbl1.h
··· 560 560 561 561 /* Values for Restrictions field above */ 562 562 563 - #define ACPI_CEDT_CFMWS_RESTRICT_TYPE2 (1) 564 - #define ACPI_CEDT_CFMWS_RESTRICT_TYPE3 (1<<1) 563 + #define ACPI_CEDT_CFMWS_RESTRICT_DEVMEM (1) 564 + #define ACPI_CEDT_CFMWS_RESTRICT_HOSTONLYMEM (1<<1) 565 565 #define ACPI_CEDT_CFMWS_RESTRICT_VOLATILE (1<<2) 566 566 #define ACPI_CEDT_CFMWS_RESTRICT_PMEM (1<<3) 567 567 #define ACPI_CEDT_CFMWS_RESTRICT_FIXED (1<<4)
-12
include/linux/acpi.h
··· 1595 1595 ACPI_COMPANION_SET(dev, ACPI_COMPANION(dev->parent)); 1596 1596 } 1597 1597 1598 - #ifdef CONFIG_ACPI_HMAT 1599 - int hmat_update_target_coordinates(int nid, struct access_coordinate *coord, 1600 - enum access_coordinate_class access); 1601 - #else 1602 - static inline int hmat_update_target_coordinates(int nid, 1603 - struct access_coordinate *coord, 1604 - enum access_coordinate_class access) 1605 - { 1606 - return -EOPNOTSUPP; 1607 - } 1608 - #endif 1609 - 1610 1598 #ifdef CONFIG_ACPI_NUMA 1611 1599 bool acpi_node_backed_by_real_pxm(int nid); 1612 1600 #else
+3 -3
include/linux/memory.h
··· 115 115 struct mem_section; 116 116 117 117 /* 118 - * Priorities for the hotplug memory callback routines (stored in decreasing 119 - * order in the callback chain) 118 + * Priorities for the hotplug memory callback routines. Invoked from 119 + * high to low. Higher priorities correspond to higher numbers. 120 120 */ 121 121 #define DEFAULT_CALLBACK_PRI 0 122 122 #define SLAB_CALLBACK_PRI 1 123 - #define HMAT_CALLBACK_PRI 2 124 123 #define CXL_CALLBACK_PRI 5 124 + #define HMAT_CALLBACK_PRI 6 125 125 #define MM_COMPUTE_BATCH_PRI 10 126 126 #define CPUSET_CALLBACK_PRI 10 127 127 #define MEMTIER_HOTPLUG_PRI 100
+8
include/linux/node.h
··· 85 85 void node_add_cache(unsigned int nid, struct node_cache_attrs *cache_attrs); 86 86 void node_set_perf_attrs(unsigned int nid, struct access_coordinate *coord, 87 87 enum access_coordinate_class access); 88 + void node_update_perf_attrs(unsigned int nid, struct access_coordinate *coord, 89 + enum access_coordinate_class access); 88 90 #else 89 91 static inline void node_add_cache(unsigned int nid, 90 92 struct node_cache_attrs *cache_attrs) ··· 96 94 static inline void node_set_perf_attrs(unsigned int nid, 97 95 struct access_coordinate *coord, 98 96 enum access_coordinate_class access) 97 + { 98 + } 99 + 100 + static inline void node_update_perf_attrs(unsigned int nid, 101 + struct access_coordinate *coord, 102 + enum access_coordinate_class access) 99 103 { 100 104 } 101 105 #endif
+2 -5
tools/testing/cxl/Kbuild
··· 5 5 ldflags-y += --wrap=acpi_pci_find_root 6 6 ldflags-y += --wrap=nvdimm_bus_register 7 7 ldflags-y += --wrap=devm_cxl_port_enumerate_dports 8 - ldflags-y += --wrap=devm_cxl_setup_hdm 9 - ldflags-y += --wrap=devm_cxl_add_passthrough_decoder 10 - ldflags-y += --wrap=devm_cxl_enumerate_decoders 11 8 ldflags-y += --wrap=cxl_await_media_ready 12 - ldflags-y += --wrap=cxl_hdm_decode_init 13 - ldflags-y += --wrap=cxl_dvsec_rr_decode 14 9 ldflags-y += --wrap=devm_cxl_add_rch_dport 15 10 ldflags-y += --wrap=cxl_rcd_component_reg_phys 16 11 ldflags-y += --wrap=cxl_endpoint_parse_cdat 17 12 ldflags-y += --wrap=cxl_dport_init_ras_reporting 13 + ldflags-y += --wrap=devm_cxl_endpoint_decoders_setup 18 14 19 15 DRIVERS := ../../../drivers 20 16 CXL_SRC := $(DRIVERS)/cxl 21 17 CXL_CORE_SRC := $(DRIVERS)/cxl/core 22 18 ccflags-y := -I$(srctree)/drivers/cxl/ 23 19 ccflags-y += -D__mock=__weak 20 + ccflags-y += -DCXL_TEST_ENABLE=1 24 21 ccflags-y += -DTRACE_INCLUDE_PATH=$(CXL_CORE_SRC) -I$(srctree)/drivers/cxl/core/ 25 22 26 23 obj-m += cxl_acpi.o
+22
tools/testing/cxl/cxl_core_exports.c
··· 2 2 /* Copyright(c) 2022 Intel Corporation. All rights reserved. */ 3 3 4 4 #include "cxl.h" 5 + #include "exports.h" 5 6 6 7 /* Exporting of cxl_core symbols that are only used by cxl_test */ 7 8 EXPORT_SYMBOL_NS_GPL(cxl_num_decoders_committed, "CXL"); 9 + 10 + cxl_add_dport_by_dev_fn _devm_cxl_add_dport_by_dev = 11 + __devm_cxl_add_dport_by_dev; 12 + EXPORT_SYMBOL_NS_GPL(_devm_cxl_add_dport_by_dev, "CXL"); 13 + 14 + struct cxl_dport *devm_cxl_add_dport_by_dev(struct cxl_port *port, 15 + struct device *dport_dev) 16 + { 17 + return _devm_cxl_add_dport_by_dev(port, dport_dev); 18 + } 19 + EXPORT_SYMBOL_NS_GPL(devm_cxl_add_dport_by_dev, "CXL"); 20 + 21 + cxl_switch_decoders_setup_fn _devm_cxl_switch_port_decoders_setup = 22 + __devm_cxl_switch_port_decoders_setup; 23 + EXPORT_SYMBOL_NS_GPL(_devm_cxl_switch_port_decoders_setup, "CXL"); 24 + 25 + int devm_cxl_switch_port_decoders_setup(struct cxl_port *port) 26 + { 27 + return _devm_cxl_switch_port_decoders_setup(port); 28 + } 29 + EXPORT_SYMBOL_NS_GPL(devm_cxl_switch_port_decoders_setup, "CXL");
+13
tools/testing/cxl/exports.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + /* Copyright(c) 2025 Intel Corporation */ 3 + #ifndef __MOCK_CXL_EXPORTS_H_ 4 + #define __MOCK_CXL_EXPORTS_H_ 5 + 6 + typedef struct cxl_dport *(*cxl_add_dport_by_dev_fn)(struct cxl_port *port, 7 + struct device *dport_dev); 8 + extern cxl_add_dport_by_dev_fn _devm_cxl_add_dport_by_dev; 9 + 10 + typedef int(*cxl_switch_decoders_setup_fn)(struct cxl_port *port); 11 + extern cxl_switch_decoders_setup_fn _devm_cxl_switch_port_decoders_setup; 12 + 13 + #endif
+105 -28
tools/testing/cxl/test/cxl.c
··· 210 210 }, 211 211 .interleave_ways = 0, 212 212 .granularity = 4, 213 - .restrictions = ACPI_CEDT_CFMWS_RESTRICT_TYPE3 | 213 + .restrictions = ACPI_CEDT_CFMWS_RESTRICT_HOSTONLYMEM | 214 214 ACPI_CEDT_CFMWS_RESTRICT_VOLATILE, 215 215 .qtg_id = FAKE_QTG_ID, 216 216 .window_size = SZ_256M * 4UL, ··· 225 225 }, 226 226 .interleave_ways = 1, 227 227 .granularity = 4, 228 - .restrictions = ACPI_CEDT_CFMWS_RESTRICT_TYPE3 | 228 + .restrictions = ACPI_CEDT_CFMWS_RESTRICT_HOSTONLYMEM | 229 229 ACPI_CEDT_CFMWS_RESTRICT_VOLATILE, 230 230 .qtg_id = FAKE_QTG_ID, 231 231 .window_size = SZ_256M * 8UL, ··· 240 240 }, 241 241 .interleave_ways = 0, 242 242 .granularity = 4, 243 - .restrictions = ACPI_CEDT_CFMWS_RESTRICT_TYPE3 | 243 + .restrictions = ACPI_CEDT_CFMWS_RESTRICT_HOSTONLYMEM | 244 244 ACPI_CEDT_CFMWS_RESTRICT_PMEM, 245 245 .qtg_id = FAKE_QTG_ID, 246 246 .window_size = SZ_256M * 4UL, ··· 255 255 }, 256 256 .interleave_ways = 1, 257 257 .granularity = 4, 258 - .restrictions = ACPI_CEDT_CFMWS_RESTRICT_TYPE3 | 258 + .restrictions = ACPI_CEDT_CFMWS_RESTRICT_HOSTONLYMEM | 259 259 ACPI_CEDT_CFMWS_RESTRICT_PMEM, 260 260 .qtg_id = FAKE_QTG_ID, 261 261 .window_size = SZ_256M * 8UL, ··· 270 270 }, 271 271 .interleave_ways = 0, 272 272 .granularity = 4, 273 - .restrictions = ACPI_CEDT_CFMWS_RESTRICT_TYPE3 | 273 + .restrictions = ACPI_CEDT_CFMWS_RESTRICT_HOSTONLYMEM | 274 274 ACPI_CEDT_CFMWS_RESTRICT_PMEM, 275 275 .qtg_id = FAKE_QTG_ID, 276 276 .window_size = SZ_256M * 4UL, ··· 285 285 }, 286 286 .interleave_ways = 0, 287 287 .granularity = 4, 288 - .restrictions = ACPI_CEDT_CFMWS_RESTRICT_TYPE3 | 288 + .restrictions = ACPI_CEDT_CFMWS_RESTRICT_HOSTONLYMEM | 289 289 ACPI_CEDT_CFMWS_RESTRICT_VOLATILE, 290 290 .qtg_id = FAKE_QTG_ID, 291 291 .window_size = SZ_256M, ··· 302 302 .interleave_arithmetic = ACPI_CEDT_CFMWS_ARITHMETIC_XOR, 303 303 .interleave_ways = 0, 304 304 .granularity = 4, 305 - .restrictions = ACPI_CEDT_CFMWS_RESTRICT_TYPE3 | 305 + .restrictions = ACPI_CEDT_CFMWS_RESTRICT_HOSTONLYMEM | 306 306 ACPI_CEDT_CFMWS_RESTRICT_PMEM, 307 307 .qtg_id = FAKE_QTG_ID, 308 308 .window_size = SZ_256M * 8UL, ··· 318 318 .interleave_arithmetic = ACPI_CEDT_CFMWS_ARITHMETIC_XOR, 319 319 .interleave_ways = 1, 320 320 .granularity = 0, 321 - .restrictions = ACPI_CEDT_CFMWS_RESTRICT_TYPE3 | 321 + .restrictions = ACPI_CEDT_CFMWS_RESTRICT_HOSTONLYMEM | 322 322 ACPI_CEDT_CFMWS_RESTRICT_PMEM, 323 323 .qtg_id = FAKE_QTG_ID, 324 324 .window_size = SZ_256M * 8UL, ··· 334 334 .interleave_arithmetic = ACPI_CEDT_CFMWS_ARITHMETIC_XOR, 335 335 .interleave_ways = 8, 336 336 .granularity = 1, 337 - .restrictions = ACPI_CEDT_CFMWS_RESTRICT_TYPE3 | 337 + .restrictions = ACPI_CEDT_CFMWS_RESTRICT_HOSTONLYMEM | 338 338 ACPI_CEDT_CFMWS_RESTRICT_PMEM, 339 339 .qtg_id = FAKE_QTG_ID, 340 340 .window_size = SZ_512M * 6UL, ··· 643 643 return cxlhdm; 644 644 } 645 645 646 - static int mock_cxl_add_passthrough_decoder(struct cxl_port *port) 647 - { 648 - dev_err(&port->dev, "unexpected passthrough decoder for cxl_test\n"); 649 - return -EOPNOTSUPP; 650 - } 651 - 652 - 653 646 struct target_map_ctx { 654 - int *target_map; 647 + u32 *target_map; 655 648 int index; 656 649 int target_count; 657 650 }; ··· 811 818 */ 812 819 if (WARN_ON(!dev)) 813 820 continue; 821 + 814 822 cxlsd = to_cxl_switch_decoder(dev); 815 823 if (i == 0) { 816 824 /* put cxl_mem.4 second in the decode order */ 817 - if (pdev->id == 4) 825 + if (pdev->id == 4) { 818 826 cxlsd->target[1] = dport; 819 - else 827 + cxld->target_map[1] = dport->port_id; 828 + } else { 820 829 cxlsd->target[0] = dport; 821 - } else 830 + cxld->target_map[0] = dport->port_id; 831 + } 832 + } else { 822 833 cxlsd->target[0] = dport; 834 + cxld->target_map[0] = dport->port_id; 835 + } 823 836 cxld = &cxlsd->cxld; 824 837 cxld->target_type = CXL_DECODER_HOSTONLYMEM; 825 838 cxld->flags = CXL_DECODER_F_ENABLE; ··· 862 863 target_count = NR_CXL_SWITCH_PORTS; 863 864 864 865 for (i = 0; i < NR_CXL_PORT_DECODERS; i++) { 865 - int target_map[CXL_DECODER_MAX_INTERLEAVE] = { 0 }; 866 866 struct target_map_ctx ctx = { 867 - .target_map = target_map, 868 867 .target_count = target_count, 869 868 }; 870 869 struct cxl_decoder *cxld; ··· 891 894 cxld = &cxled->cxld; 892 895 } 893 896 897 + ctx.target_map = cxld->target_map; 898 + 894 899 mock_init_hdm_decoder(cxld); 895 900 896 901 if (target_count) { ··· 904 905 } 905 906 } 906 907 907 - rc = cxl_decoder_add_locked(cxld, target_map); 908 + rc = cxl_decoder_add_locked(cxld); 908 909 if (rc) { 909 910 put_device(&cxld->dev); 910 911 dev_err(&port->dev, "Failed to add decoder\n"); ··· 920 921 return 0; 921 922 } 922 923 923 - static int mock_cxl_port_enumerate_dports(struct cxl_port *port) 924 + static int __mock_cxl_decoders_setup(struct cxl_port *port) 925 + { 926 + struct cxl_hdm *cxlhdm; 927 + 928 + cxlhdm = mock_cxl_setup_hdm(port, NULL); 929 + if (IS_ERR(cxlhdm)) { 930 + if (PTR_ERR(cxlhdm) != -ENODEV) 931 + dev_err(&port->dev, "Failed to map HDM decoder capability\n"); 932 + return PTR_ERR(cxlhdm); 933 + } 934 + 935 + return mock_cxl_enumerate_decoders(cxlhdm, NULL); 936 + } 937 + 938 + static int mock_cxl_switch_port_decoders_setup(struct cxl_port *port) 939 + { 940 + if (is_cxl_root(port) || is_cxl_endpoint(port)) 941 + return -EOPNOTSUPP; 942 + 943 + return __mock_cxl_decoders_setup(port); 944 + } 945 + 946 + static int mock_cxl_endpoint_decoders_setup(struct cxl_port *port) 947 + { 948 + if (!is_cxl_endpoint(port)) 949 + return -EOPNOTSUPP; 950 + 951 + return __mock_cxl_decoders_setup(port); 952 + } 953 + 954 + static int get_port_array(struct cxl_port *port, 955 + struct platform_device ***port_array, 956 + int *port_array_size) 924 957 { 925 958 struct platform_device **array; 926 - int i, array_size; 959 + int array_size; 927 960 928 961 if (port->depth == 1) { 929 962 if (is_multi_bridge(port->uport_dev)) { ··· 989 958 return -ENXIO; 990 959 } 991 960 961 + *port_array = array; 962 + *port_array_size = array_size; 963 + 964 + return 0; 965 + } 966 + 967 + static int mock_cxl_port_enumerate_dports(struct cxl_port *port) 968 + { 969 + struct platform_device **array; 970 + int i, array_size; 971 + int rc; 972 + 973 + rc = get_port_array(port, &array, &array_size); 974 + if (rc) 975 + return rc; 976 + 992 977 for (i = 0; i < array_size; i++) { 993 978 struct platform_device *pdev = array[i]; 994 979 struct cxl_dport *dport; ··· 1024 977 } 1025 978 1026 979 return 0; 980 + } 981 + 982 + static struct cxl_dport *mock_cxl_add_dport_by_dev(struct cxl_port *port, 983 + struct device *dport_dev) 984 + { 985 + struct platform_device **array; 986 + int rc, i, array_size; 987 + 988 + rc = get_port_array(port, &array, &array_size); 989 + if (rc) 990 + return ERR_PTR(rc); 991 + 992 + for (i = 0; i < array_size; i++) { 993 + struct platform_device *pdev = array[i]; 994 + 995 + if (pdev->dev.parent != port->uport_dev) { 996 + dev_dbg(&port->dev, "%s: mismatch parent %s\n", 997 + dev_name(port->uport_dev), 998 + dev_name(pdev->dev.parent)); 999 + continue; 1000 + } 1001 + 1002 + if (&pdev->dev != dport_dev) 1003 + continue; 1004 + 1005 + return devm_cxl_add_dport(port, &pdev->dev, pdev->id, 1006 + CXL_RESOURCE_NONE); 1007 + } 1008 + 1009 + return ERR_PTR(-ENODEV); 1027 1010 } 1028 1011 1029 1012 /* ··· 1112 1035 .acpi_table_parse_cedt = mock_acpi_table_parse_cedt, 1113 1036 .acpi_evaluate_integer = mock_acpi_evaluate_integer, 1114 1037 .acpi_pci_find_root = mock_acpi_pci_find_root, 1038 + .devm_cxl_switch_port_decoders_setup = mock_cxl_switch_port_decoders_setup, 1039 + .devm_cxl_endpoint_decoders_setup = mock_cxl_endpoint_decoders_setup, 1115 1040 .devm_cxl_port_enumerate_dports = mock_cxl_port_enumerate_dports, 1116 - .devm_cxl_setup_hdm = mock_cxl_setup_hdm, 1117 - .devm_cxl_add_passthrough_decoder = mock_cxl_add_passthrough_decoder, 1118 - .devm_cxl_enumerate_decoders = mock_cxl_enumerate_decoders, 1119 1041 .cxl_endpoint_parse_cdat = mock_cxl_endpoint_parse_cdat, 1042 + .devm_cxl_add_dport_by_dev = mock_cxl_add_dport_by_dev, 1120 1043 .list = LIST_HEAD_INIT(cxl_mock_ops.list), 1121 1044 }; 1122 1045
+35 -61
tools/testing/cxl/test/mock.c
··· 10 10 #include <cxlmem.h> 11 11 #include <cxlpci.h> 12 12 #include "mock.h" 13 + #include "../exports.h" 13 14 14 15 static LIST_HEAD(mock); 16 + 17 + static struct cxl_dport * 18 + redirect_devm_cxl_add_dport_by_dev(struct cxl_port *port, 19 + struct device *dport_dev); 20 + static int redirect_devm_cxl_switch_port_decoders_setup(struct cxl_port *port); 15 21 16 22 void register_cxl_mock_ops(struct cxl_mock_ops *ops) 17 23 { 18 24 list_add_rcu(&ops->list, &mock); 25 + _devm_cxl_add_dport_by_dev = redirect_devm_cxl_add_dport_by_dev; 26 + _devm_cxl_switch_port_decoders_setup = 27 + redirect_devm_cxl_switch_port_decoders_setup; 19 28 } 20 29 EXPORT_SYMBOL_GPL(register_cxl_mock_ops); 21 30 ··· 32 23 33 24 void unregister_cxl_mock_ops(struct cxl_mock_ops *ops) 34 25 { 26 + _devm_cxl_switch_port_decoders_setup = 27 + __devm_cxl_switch_port_decoders_setup; 28 + _devm_cxl_add_dport_by_dev = __devm_cxl_add_dport_by_dev; 35 29 list_del_rcu(&ops->list); 36 30 synchronize_srcu(&cxl_mock_srcu); 37 31 } ··· 143 131 } 144 132 EXPORT_SYMBOL_GPL(__wrap_nvdimm_bus_register); 145 133 146 - struct cxl_hdm *__wrap_devm_cxl_setup_hdm(struct cxl_port *port, 147 - struct cxl_endpoint_dvsec_info *info) 148 - 149 - { 150 - int index; 151 - struct cxl_hdm *cxlhdm; 152 - struct cxl_mock_ops *ops = get_cxl_mock_ops(&index); 153 - 154 - if (ops && ops->is_mock_port(port->uport_dev)) 155 - cxlhdm = ops->devm_cxl_setup_hdm(port, info); 156 - else 157 - cxlhdm = devm_cxl_setup_hdm(port, info); 158 - put_cxl_mock_ops(index); 159 - 160 - return cxlhdm; 161 - } 162 - EXPORT_SYMBOL_NS_GPL(__wrap_devm_cxl_setup_hdm, "CXL"); 163 - 164 - int __wrap_devm_cxl_add_passthrough_decoder(struct cxl_port *port) 134 + int redirect_devm_cxl_switch_port_decoders_setup(struct cxl_port *port) 165 135 { 166 136 int rc, index; 167 137 struct cxl_mock_ops *ops = get_cxl_mock_ops(&index); 168 138 169 139 if (ops && ops->is_mock_port(port->uport_dev)) 170 - rc = ops->devm_cxl_add_passthrough_decoder(port); 140 + rc = ops->devm_cxl_switch_port_decoders_setup(port); 171 141 else 172 - rc = devm_cxl_add_passthrough_decoder(port); 142 + rc = __devm_cxl_switch_port_decoders_setup(port); 173 143 put_cxl_mock_ops(index); 174 144 175 145 return rc; 176 146 } 177 - EXPORT_SYMBOL_NS_GPL(__wrap_devm_cxl_add_passthrough_decoder, "CXL"); 178 147 179 - int __wrap_devm_cxl_enumerate_decoders(struct cxl_hdm *cxlhdm, 180 - struct cxl_endpoint_dvsec_info *info) 148 + int __wrap_devm_cxl_endpoint_decoders_setup(struct cxl_port *port) 181 149 { 182 150 int rc, index; 183 - struct cxl_port *port = cxlhdm->port; 184 151 struct cxl_mock_ops *ops = get_cxl_mock_ops(&index); 185 152 186 153 if (ops && ops->is_mock_port(port->uport_dev)) 187 - rc = ops->devm_cxl_enumerate_decoders(cxlhdm, info); 154 + rc = ops->devm_cxl_endpoint_decoders_setup(port); 188 155 else 189 - rc = devm_cxl_enumerate_decoders(cxlhdm, info); 156 + rc = devm_cxl_endpoint_decoders_setup(port); 190 157 put_cxl_mock_ops(index); 191 158 192 159 return rc; 193 160 } 194 - EXPORT_SYMBOL_NS_GPL(__wrap_devm_cxl_enumerate_decoders, "CXL"); 161 + EXPORT_SYMBOL_NS_GPL(__wrap_devm_cxl_endpoint_decoders_setup, "CXL"); 195 162 196 163 int __wrap_devm_cxl_port_enumerate_dports(struct cxl_port *port) 197 164 { ··· 201 210 return rc; 202 211 } 203 212 EXPORT_SYMBOL_NS_GPL(__wrap_cxl_await_media_ready, "CXL"); 204 - 205 - int __wrap_cxl_hdm_decode_init(struct cxl_dev_state *cxlds, 206 - struct cxl_hdm *cxlhdm, 207 - struct cxl_endpoint_dvsec_info *info) 208 - { 209 - int rc = 0, index; 210 - struct cxl_mock_ops *ops = get_cxl_mock_ops(&index); 211 - 212 - if (ops && ops->is_mock_dev(cxlds->dev)) 213 - rc = 0; 214 - else 215 - rc = cxl_hdm_decode_init(cxlds, cxlhdm, info); 216 - put_cxl_mock_ops(index); 217 - 218 - return rc; 219 - } 220 - EXPORT_SYMBOL_NS_GPL(__wrap_cxl_hdm_decode_init, "CXL"); 221 - 222 - int __wrap_cxl_dvsec_rr_decode(struct cxl_dev_state *cxlds, 223 - struct cxl_endpoint_dvsec_info *info) 224 - { 225 - int rc = 0, index; 226 - struct cxl_mock_ops *ops = get_cxl_mock_ops(&index); 227 - 228 - if (ops && ops->is_mock_dev(cxlds->dev)) 229 - rc = 0; 230 - else 231 - rc = cxl_dvsec_rr_decode(cxlds, info); 232 - put_cxl_mock_ops(index); 233 - 234 - return rc; 235 - } 236 - EXPORT_SYMBOL_NS_GPL(__wrap_cxl_dvsec_rr_decode, "CXL"); 237 213 238 214 struct cxl_dport *__wrap_devm_cxl_add_rch_dport(struct cxl_port *port, 239 215 struct device *dport_dev, ··· 268 310 put_cxl_mock_ops(index); 269 311 } 270 312 EXPORT_SYMBOL_NS_GPL(__wrap_cxl_dport_init_ras_reporting, "CXL"); 313 + 314 + struct cxl_dport *redirect_devm_cxl_add_dport_by_dev(struct cxl_port *port, 315 + struct device *dport_dev) 316 + { 317 + int index; 318 + struct cxl_mock_ops *ops = get_cxl_mock_ops(&index); 319 + struct cxl_dport *dport; 320 + 321 + if (ops && ops->is_mock_port(port->uport_dev)) 322 + dport = ops->devm_cxl_add_dport_by_dev(port, dport_dev); 323 + else 324 + dport = __devm_cxl_add_dport_by_dev(port, dport_dev); 325 + put_cxl_mock_ops(index); 326 + 327 + return dport; 328 + } 271 329 272 330 MODULE_LICENSE("GPL v2"); 273 331 MODULE_DESCRIPTION("cxl_test: emulation module");
+4 -5
tools/testing/cxl/test/mock.h
··· 20 20 bool (*is_mock_port)(struct device *dev); 21 21 bool (*is_mock_dev)(struct device *dev); 22 22 int (*devm_cxl_port_enumerate_dports)(struct cxl_port *port); 23 - struct cxl_hdm *(*devm_cxl_setup_hdm)( 24 - struct cxl_port *port, struct cxl_endpoint_dvsec_info *info); 25 - int (*devm_cxl_add_passthrough_decoder)(struct cxl_port *port); 26 - int (*devm_cxl_enumerate_decoders)( 27 - struct cxl_hdm *hdm, struct cxl_endpoint_dvsec_info *info); 23 + int (*devm_cxl_switch_port_decoders_setup)(struct cxl_port *port); 24 + int (*devm_cxl_endpoint_decoders_setup)(struct cxl_port *port); 28 25 void (*cxl_endpoint_parse_cdat)(struct cxl_port *port); 26 + struct cxl_dport *(*devm_cxl_add_dport_by_dev)(struct cxl_port *port, 27 + struct device *dport_dev); 29 28 }; 30 29 31 30 void register_cxl_mock_ops(struct cxl_mock_ops *ops);