Linux 内核 CXL Access Coordinates 计算全解析:从 ACPI/CDAT 数据源到共享上游链路带宽算法
【免费下载链接】linuxLinux kernel source tree项目地址: https://gitcode.com/GitHub_Trending/li/linux
导读
本文深入讲解 Linux 内核 CXL 驱动中Access Coordinates(访问坐标,即延迟与带宽性能数据)的计算机制,涵盖数据来源(SRAT/HMAT/CDAT/CEDT)、延迟与带宽的数学计算模型、共享上游链路(Shared Upstream Link)带宽重算算法,以及 QTG ID 的获取与使用。读完本文,你将掌握 CXL 内存区域(region)性能坐标的完整计算链路、Linux 源码中对应的实现位置与调用关系,并能结合实际拓扑理解"带宽取最小值、延迟取总和"的核心原则。
一、为什么需要计算 CXL Access Coordinates
一个内存区域的性能坐标(延迟与带宽)通常由 ACPI 表SRAT和HMAT提供。然而,平台固件(BIOS)无法为热插拔的 CXL 设备标注这些性能数据——因为这些设备在固件初始化阶段尚不存在。
CXL 驱动可以在运行时通过从多个组件中检索数据,自行计算性能坐标,从而为热插拔设备补全缺失的 Access Coordinates。这正是 access-coordinates.rst 所描述的核心问题与解决方案。
各性能数据来源一览
| 数据来源 | 提供什么 | 在路径中的角色 |
|---|---|---|
| SRAT Generic Port Affinity 子表 | 将 Proximity Domain 绑定到设备句柄(此处为 CXL Host Bridge) | CPU 到 Generic Port(CXL Host Bridge)之间的性能坐标 |
| HMAT SLLBI 子表 | Proximity Domain 之间的延迟与带宽数据 | 配合 SRAT 取出 Generic Port 的延迟/带宽 |
| CDAT DSMAS 子表 | DSMADHandle 与 DPA 范围的对应关系 | 设备自身各 DPA 区域的划分 |
| CDAT DSLBIS 子表 | 绑定 DSMADHandle 的延迟与带宽 | 设备自身内存区域的性能坐标 |
| CDAT SSLBIS 子表 | 交换机上游端口到下游端口的延迟与带宽 | CXL Switch 的穿越开销 |
| CEDT CFMWS | QTG ID 与内存窗口配置 | QoS 分组匹配 |
二、性能数据来源详解
2.1 SRAT:Generic Port Affinity
SRAT 文档 中的 Generic Port Affinity 子表提供了Proximity Domain 与代表 Generic Port(如 CXL Host Bridge)的设备句柄之间的关联。借助这一关联,可以从 HMAT 子表中检索该 Generic Port 的性能坐标——这一部分代表了CPU 与 Generic Port(CXL Host Bridge)之间的性能坐标。
SRAT Generic Port Affinity 示例:
Subtable Type : 06 [Generic Port Affinity] Length : 20 <- 32d, length of table Reserved : 00 Device Handle Type : 00 <- 0 - ACPI, 1 - PCI Proximity Domain : 00000001 Device Handle : ACPI0016:01 Flags : 00000001 <- Bit 0 (Enabled) Reserved : 00000000驱动使用该关联来为整个 CXL 路径的 Access Coordinates 计算检索 Generic Port 的性能数据。需要注意:SRAT 对于性能信息(HMAT)的枚举是必需的——虽然该表在技术上可选,但要让 Linux 枚举性能信息,SRAT 必须存在。
2.2 HMAT:SLLBI 条目
HMAT 文档 中的System Locality Latency and Bandwidth Information(SLLBI)记录 Proximity Domain 之间的延迟与带宽信息。Linux 使用该表配置交错权重(interleave weights)和内存层级(memory tiers)。示例(经节选):
Structure Type : 0001 [SLLBI] Data Type : 00 <- Latency Target Proximity Domain List : 00000000 Target Proximity Domain List : 00000001 Entry : 0080 <- DRAM LTC Entry : 0100 <- CXL LTC Structure Type : 0001 [SLLBI] Data Type : 03 <- Bandwidth Target Proximity Domain List : 00000000 Target Proximity Domain List : 00000001 Entry : 1200 <- DRAM BW Entry : 0200 <- CXL BW2.3 CDAT:DSMAS + DSLBIS + SSLBIS
CDAT 文档 描述了设备自身的性能属性。CDAT 为 CXL 设备本身提供性能坐标,即访问该设备内存区域的带宽与延迟。
DSMAS(Device Scoped Memory Affinity Structure)提供 DSMADHandle、DPA Base 与 DPA Length;DSLBIS(Device Scoped Latency and Bandwidth Information Structure)提供与 DSMADHandle 绑定的延迟与带宽。两者通过 DSMADHandle 关联,为每个 DPA 区域提供性能坐标。例如,若设备导出一个 DRAM 区域和一个 PMEM 区域,则每个区域的性能特征不同。
在 Linux 内核中,CDAT 的解析位于 drivers/cxl/core/cdat.c:
cdat_dsmas_handler()(cdat.c#L52-L88)解析 DSMAS,将 DSMADHandle 与 DPA 范围存入 xarray;cdat_dslbis_handler()(cdat.c#L124-L168)解析 DSLBIS,通过 handle 找到对应的 DSMAS 条目,再调用cdat_normalize()(cdat.c#L22-L50)将 CDAT 的原始值按 Entry Base Unit 规范化(延迟值会除以 1000 换算),最终写入dent->cdat_coord。
SSLBIS(Switch Scoped Latency and Bandwidth Information Structure)提供交换机穿越(switch upstream port 到指向端点设备的 switch downstream port)的带宽与延迟。对应解析函数为cdat_sslbis_handler()(cdat.c#L433-L509),它处理portx_id/porty_id(其中0100h表示上游端口,FFFFh表示任意端口),找到与下游端口(dport)匹配的条目后写入dport->coord。
CDAT DSLBIS 示例:
Structure Type : 01 [DSLBIS] Handle : 0001 <- DSMAS handle Flags : 00 <- Matches flag field for HMAT SLLBIS Data Type : 00 <- Latency Entry Base Unit : 0000000000001000 Entry : 010000000000 <- First byte used here, CXL LTCCDAT SSLBIS 示例:
Structure Type : 05 [SSLBIS] Data Type : 00 <- Latency Entry Base Unit : 00000000000000001000 <- Matches Entry Base Unit in HMAT SSLBIS <- SSLB Entry 0 Port X ID : 0100 <- 0100h represents an upstream port Port Y ID : 0000 <- downstream port 0 Latency : 01002.4 CEDT:CFMWS 与 QTG ID
CEDT 文档 中的CXL Fixed Memory Window Structure(CFMWS)描述与一个或多个 CXL Host Bridge(由 CHBS 描述)关联的内存区域,并描述 BIOS 配置的 Host Bridge 间交错。CFMWS 中的QtgId 字段提供了与该窗口关联的 QoS Throttling Group(QTG)ID。
三、延迟与带宽的计算模型
3.1 简单拓扑示例
原文档给出了一个包含 CXL Switch 的简单拓扑:
GP0/HB0/ACPI0016-0 RP0 | | L0 | SW 0 / USP0 SW 0 / DSP0 | | L1 | EP0在该示例中,端点与根端口之间存在一个 CXL Switch。
3.2 延迟:求和
总读/写延迟 = 所有组成部分之和。各组成部分为:
- L(EP0):EP0 CDAT DSMAS+DSLBIS 得到的延迟
- L(L1):EP0 与 SW0/DSP0 之间的链路延迟
- L(SW0):SW0 CDAT SSLBIS 得到的交换机延迟
- L(L0):SW0 与 RP0 之间的链路延迟
- L(RP0):通过 SRAT 与 HMAT(Generic Port)得到的从根端口到 CPU 的延迟
Total Latency = L(EP0) + L(L1) + L(SW0) + L(L0) + L(RP0)3.3 带宽:取最小值
总读/写带宽 = 所有组成部分的 min()。对应的带宽组成部分为:
- B(EP0):EP0 CDAT DSMAS+DSLBIS 得到的带宽
- B(L1):EP0 与 SW0/DSP0 之间的链路带宽
- B(SW0):SW0 CDAT SSLBIS 得到的交换机带宽
- B(L0):SW0 与 RP0 之间的链路带宽
- B(RP0):通过 SRAT 与 HMAT(Generic Port)得到的从根端口到 CPU 的带宽
Total Bandwidth = min(B(EP0), B(L1), B(SW0), B(L0), B(RP0))这与内核实现中__cxl_coordinates_combine()(cdat.c#L527-L540)的语义完全一致:带宽取min()、延迟取+:
out->write_bandwidth = min(c1->write_bandwidth, c2->write_bandwidth); out->write_latency = c1->write_latency + c2->write_latency; out->read_bandwidth = min(c1->read_bandwidth, c2->read_bandwidth); out->read_latency = c1->read_latency + c2->read_latency;3.4 链路带宽计算
链路带宽的计算方式:
LinkOperatingFrequency (GT/s) —— 当前协商的链路速率 DataRatePerLink (MB/s) = LinkOperatingFrequency / 8 Bandwidth (MB/s) = PCIeCurrentLinkWidth * DataRatePerLink其中PCIeCurrentLinkWidth是链路的通道(lane)数量。
内核实现位于cxl_pci_get_bandwidth()(drivers/cxl/core/pci.c#L757-L778):通过pcie_link_speed_mbps()获取链路速率并除以 8(BITS_PER_BYTE),通过PCI_EXP_LNKSTA寄存器(PCI_EXP_LNKSTA_NLW字段)读取当前链路宽度,两者相乘即得到每条链路的带宽:
speed = pcie_link_speed_mbps(pdev); speed /= BITS_PER_BYTE; pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnksta); width = FIELD_GET(PCI_EXP_LNKSTA_NLW, lnksta); bw = speed * width;3.5 链路延迟计算
链路延迟的计算方式:
LinkLatency (picoseconds) = FlitSize / LinkBandwidth (MB/s)内核实现位于cxl_pci_get_latency()(drivers/cxl/core/pci.c#L659-L669)。完整链路延迟由三部分组成:LinkPropagationLatency + FlitLatency + RetimerLatency,其中传播延迟与重定时器延迟被假定为可忽略(取 0),Flit 延迟按上述公式计算。cxl_flit_size()(pci.c#L636-L642)根据 PCIe Flit Mode 决定 Flit 大小:CXL rev3.0 规范规定 68B flit 用于最高 32GT/s,超过 32GT/s 使用 256B flit。
更多细节可参考《CXL Memory Device Software Guide r1.0》第 2.11.3 节与 2.11.4 节(此处仅作规范指引,不展开外部链接)。
最终,一个已构造的 CXL 内存区域的 Access Coordinates 是由一个或多个 CXL 设备中每个内存分区(partition)的坐标计算得出的。
四、共享上游链路计算(Shared Upstream Link Calculation)
4.1 动机与假设
对于某些位于 CXL Switch(SW)或根端口(RP)之后的端点所构成的 region 构建场景,交换机后所有端点的总带宽可能超过交换机的上游链路带宽;在主机内部、根端口上游也可能出现类似情况。因此 CXL 驱动在 region 的所有目标(targets)就绪后,会额外执行一轮带宽重算,把可能成为瓶颈的上游链路考虑进来。
该算法假设拓扑是对称的(对称拓扑可最大化性能)。当检测到非对称拓扑时,计算会被中止。非对称拓扑的检测方式:在拓扑遍历过程中,被检测为祖父节点(grandparent)的 RP 数量不等于同一迭代循环中遍历的设备数量。算法还假设属性的细微不对称不会发生,所有通向端点的路径都是等价的。
从源码看,非对称检测出现在cxl_switch_gather_bandwidth()(cdat.c#L842-L850)以及cxl_region_shared_upstream_bandwidth_update()(cdat.c#L1008-L1013)中,检测到后打印 "Asymmetric hierarchy detected, bandwidth not updated" 并返回-EOPNOTSUPP。
4.2 拓扑层级关系
一个 RP 下可以有多个 Switch;一个 CXL Host Bridge(HB)下可以有多个 RP;一个 CEDT 中的 CXL Fixed Memory Window Structure(CFMWS)下可以有多个 HB。
原文档给出的示例层级:
CFMWS 0 | _________|_________ | | ACPI0017-0 ACPI0017-1 GP0/HB0/ACPI0016-0 GP1/HB1/ACPI0016-1 | | | | RP0 RP1 RP2 RP3 | | | | SW 0 SW 1 SW 2 SW 3 | | | | | | | | EP0 EP1 EP2 EP3 EP4 EP5 EP6 EP74.3 示例层级的计算公式
对于上述示例层级,带宽计算为:
Min (GP0 to CPU BW, Min(SW 0 Upstream Link to RP0 BW, Min(SW0SSLBIS for SW0DSP0 (EP0), EP0 DSLBIS, EP0 Upstream Link) + Min(SW0SSLBIS for SW0DSP1 (EP1), EP1 DSLBIS, EP1 Upstream link)) + Min(SW 1 Upstream Link to RP1 BW, Min(SW1SSLBIS for SW1DSP0 (EP2), EP2 DSLBIS, EP2 Upstream Link) + Min(SW1SSLBIS for SW1DSP1 (EP3), EP3 DSLBIS, EP3 Upstream link))) + Min (GP1 to CPU BW, Min(SW 2 Upstream Link to RP2 BW, Min(SW2SSLBIS for SW2DSP0 (EP4), EP4 DSLBIS, EP4 Upstream Link) + Min(SW2SSLBIS for SW2DSP1 (EP5), EP5 DSLBIS, EP5 Upstream link)) + Min(SW 3 Upstream Link to RP3 BW, Min(SW3SSLBIS for SW3DSP0 (EP6), EP6 DSLBIS, EP6 Upstream Link) + Min(SW3SSLBIS for SW3DSP1 (EP7), EP7 DSLBIS, EP7 Upstream link))))可以看出两个关键操作:同一上游设备下的多个端点带宽相加(+,聚合共享上游的流量),而对共享链路的瓶颈则取min()。
4.4 源码实现:自底向上的五阶段遍历
整个计算从cxl_region_shared_upstream_perf_update()(cdat.c#L980-L1050)开始,源码中对应函数名为cxl_region_shared_upstream_bandwidth_update()。它自底向上遍历拓扑:
阶段一:端点收集。创建一个 xarray,通过cxl_endpoint_gather_bandwidth()(cdat.c#L628-L721)收集所有端点带宽。对每个端点:计算端点 CDAT 带宽与上游链路带宽的 min();若端点的父设备是 CXL Switch,则再与该 Switch 的 SSLBIS(对应端点所在下游端口)带宽取 min()。最终带宽存入 xarray 中的struct cxl_perf_ctx(cdat.c#L605-L608),以设备指针为索引:
- 若端点直接连接根端口(RP),设备指针为 RP 设备;
- 若端点位于 Switch 之后,设备指针为父 Switch 的上游设备。
阶段二:交换机遍历。若拓扑中存在一个或多个 Switch,代码继续向上遍历(cxl_switch_gather_bandwidth(),cdat.c#L754-L853)。若存在上游 Switch,则取当前汇总带宽与上游链路带宽的 min();若还有更上游的 Switch,再与其 SSLBIS 取 min()。直接连接 RP 的端点会跳过该步骤。此阶段通过do { ... } while (!is_root)循环处理多层 Switch(cdat.c#L1020-L1029)。
阶段三:根端口汇聚。无论拓扑遍历到达 RP 的方式是直接连接还是穿过 Switch,都会调用cxl_rp_gather_bandwidth()(cdat.c#L862-L897)。此时所有带宽按每个 Host Bridge 聚合,Host Bridge 成为结果 xarray 的索引。
阶段四:Host Bridge 与 Generic Port 取 min。下一步(cxl_hb_gather_bandwidth(),cdat.c#L906-L947)对每个 Host Bridge 的带宽与其Generic Port(GP)带宽取 min()。GP 的带宽通过 ACPI 表(SRAT 与 HMAT)获取。各最小值带宽在同一个 ACPI0017 设备下聚合,形成新的 xarray。
阶段五:更新 region。最后调用cxl_region_update_bandwidth()(cdat.c#L954-L969),将最后一个 xarray 中所有成员的聚合带宽更新到 cxl region(cxlr)上下文中保存的 Access Coordinates 上。
在遍历过程中,cxl_bandwidth_add()(cdat.c#L559-L569)负责对共享同一上游设备的端点带宽做加法聚合,这与公式中"同一 Switch 下多端点带宽求和"的语义一致。
五、QTG ID:性能坐标到 QoS 分组的映射
每个 CEDT 都有一个QTG ID 字段,该字段提供与 CFMWS 窗口关联的QoS Throttling Group(QTG)的 ID。
当 Access Coordinates 计算完成后,驱动可以向 ACPI0016 设备发出 ACPI Device Specific Method(_DSM),将计算得到的 Access Coordinates 作为输入,获取对应的 QTG ID。设备的 QTG ID 可以作为指导,用于匹配到最合适的 CFMWS,从而为设备性能配置最佳的 Linux root decoder。
内核实现位于 drivers/cxl/acpi.c:
cxl_acpi_evaluate_qtg_dsm()(acpi.c#L229-L309)构造包含read_latency、write_latency、read_bandwidth、write_bandwidth四个整数的输入包,通过acpi_evaluate_dsm()调用 QTG_DSM(GUID 为acpi_cxl_qtg_id_guid),返回值是按"最合适到最不合适"排序的 QTG ID 列表;cxl_acpi_qos_class()(acpi.c#L311-L326)作为 root 的回调,从 ACPI 平台设备句柄发起该_DSM调用。
在 cdat.c#L196-L238 的cxl_port_perf_data_calculate()中,每个 DSMAS 条目的 CDAT 坐标会与端点性能坐标(cxl_endpoint_get_perf_coordinates()的结果)合并,然后通过cxl_root->ops.qos_class回调获取 qos_class。随后cxl_qos_class_verify()(cdat.c#L348-L388)会校验:若 memdev 未被 root decoder 映射,或分区 class 与任何 root decoder class 不匹配,则隐藏 QoS class 信息,避免用户空间处理无效值。
六、总结
Linux 内核 CXL 驱动的 Access Coordinates 计算是一条完全自底向上的数据流水线:
- 数据采集:CDAT(DSMAS/DSLBIS/SSLBIS)提供设备与交换机自身的性能数据,SRAT(Generic Port Affinity)+ HMAT(SLLBI)提供 CPU 到 Host Bridge 的性能数据,CEDT(CFMWS)提供窗口与 QTG 信息;
- 坐标合成:单条路径上延迟求和、带宽取 min(
cxl_coordinates_combine),链路带宽由 PCIe 速率与链路宽度计算(cxl_pci_get_bandwidth),链路延迟由 FlitSize 与带宽计算(cxl_pci_get_latency); - 共享上游重算:region 目标全部就绪后,
cxl_region_shared_upstream_perf_update()自底向上(端点 → 交换机 → 根端口 → Host Bridge → ACPI0017/CFMWS)进行多轮 min/聚合,把共享上游链路的瓶颈纳入最终带宽; - QoS 映射:最终 Access Coordinates 通过 ACPI0016 的 QTG
_DSM换取 QTG ID,为 root decoder 选择提供性能依据。
理解这一机制,是分析 CXL 内存性能瓶颈、调试 region 带宽不符合预期以及理解 QoS 分组行为的基础。相关实现与文档均可在当前仓库中继续深入阅读:access-coordinates.rst、SRAT 文档、HMAT 文档、CDAT 文档、CEDT 文档、核心实现 drivers/cxl/core/cdat.c、链路计算 drivers/cxl/core/pci.c 与 QTG 实现 drivers/cxl/acpi.c。
【免费下载链接】linuxLinux kernel source tree项目地址: https://gitcode.com/GitHub_Trending/li/linux
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考