图片无法显示,请右键点击新窗口打开图片

CXL 硬件架构由不同的组件构成,这些组件按照一定的规则组成类似 PCIe 树形拓扑,这是因为 CXL 基于 PCIe 进行构建,因此可以在 CXL 树形结构中找到很多 PCIe 的影子。CXL 要能工作,那么必须支持以下硬件组件,结合上图进行介绍:

  • ACPI.CXL: ACPI0017 CXL Root Object, 由系统固件在 ACPI 树的 _SB 下实现的虚拟软件实体,表示 CXL 早期发现表 CEDT 的存在. 在系统初始化 CXL 架构时,最先通过 ACPI 树找到 ACPI.CXL 设备,该设备作为 CXL 树的根节点.
  • CXL Host Bridge: ACPI0016 CXL Host Bridge Object, 由系统固件在 ACPI 树的 _SB(系统总线)下实现的虚拟软件实体,由操作系统使用。每个 ACPI0016 对象代表单个 CXL 根复合体。由于 CXL 树的根(CXL 根复合体)是平台特定的,并且不会通过 PCI BAR 显示,系统固件负责生成一个对象来表示 CXL 主桥所代表的一组 CXL 根端口。每个 HB 由 ACPI/SB 设备树顶端下的唯一 ACPI0016 对象表示. 系统固件将在操作系统的代表下实现一些 ACPI 方法. 这不是 CXL 主桥设备应支持的 ACPI 方法的详尽列表.
  • CXL Root Complex: 平台特定的 CXL 根端口,相当于 PCIe 根复合体。总线编号分配由系统固件负责,并通过 CXL 主桥 ACPI 命名空间设备公开.
  • CXL Root Port: 通过一个或多个 Flexbus 通道连接到 CXL 内存设备或 CXL 交换机端口的 CXL 硬件连接,相当于 PCIe 根端口.
  • CXL Switch: 提供 CXL 根端口扩展到多个下行交换机端口,允许更多的 CXL 内存设备以可扩展的方式连接。每个交换机有一组 HDM 解码器,负责上游交换机端口的 HPA 解码。系统固件、UEFI 和操作系统负责编程这些 HDM 解码器,以涵盖所有连接的下游设备和交换机
  • HDM Decoder: HDM 解码器通过系统固件为已知的 CXL 易失性容量设置,通过 UEFI 和操作系统驱动程序为已知的 CXL 持久性容量设置,并由操作系统为热插拔的 CXL 易失性和持久性内存容量设置。这些寄存器确定将主机物理地址(HPA)范围映射到设备暴露的设备物理地址(DPA)范围。HDM 解码器存在于所有上游 CXL 交换机端口以及每个 CXL 根复合体中。这些 HDM 解码器还需要编程以涵盖所有下游设备的 HDM 解码器编程。CXL 根复合体中的 HDM 解码器决定内存事务的目标根端口。同样,上游端口中的 HDM 解码器决定目标下游端口
  • CXL Type3 Device: 可以通过一个或多个 Flexbus 通道连接到 CXL 根端口或 CXL 交换机下行端口。设备使用标准 PCIe 机制映射到 MMCFG 和 MMIO 区域. 类型 3 特定内存设备命令 MMIO 邮箱接口用于管理和配置设备.

以上的组件是 CXL 硬件拓扑必备的硬件组件,除了图中描述的,还包括 CEDT、CHBS、CFMWS、以及 CDAT 等组件,只是这些组件不是实际的硬件,而是 CXL 相关的信息表. 为了更好的让开发者对 CXL 硬件有更好的了解,接下来结合 BiscuitOS 实践场景进行讲解,因此需要按如下命令进行环境部署:

# 切换到 BiscuitOS 项目目录
cd BiscuitOS
# 通过 Kbuild 选择需要部署的应用程序
make menuconfig

  [*] DIY BiscuitOS/Broiler Hardware  --->
      <*> Intel Q35
      [*] CXL: Compute Express Link
            CXL Hardware Topology (CXL Switch With TYPE3: x1 Volatile Memory(DDR))  --->

# 配置完毕保存,然后进行部署
make
# 运行 BiscuitOS
cd BiscuitOS/output/linux-6.10-x86_64/
./RunBiscuitOS.sh

图片无法显示,请右键点击新窗口打开图片

当 BiscuitOS 运行之后,使用 lspci-common 根据查看 PCIe 树的拓扑结构,其中 Device 8086:0d93 所在的子树正好是 上上图描述的硬件拓扑,为了让各位开发者更好的了解 CXL 硬件拓扑,接下来从 PCIe 角度进行分析,一步步引出 CXL 硬件拓扑.

图片无法显示,请右键点击新窗口打开图片

PCIe 全称是 PCI-Express(Peripheral Component Interconnect Express),是一种高速串行计算机扩展总线标准. CXL 基于 PCIe5.0 进行构建,其传输速率可达 32 GT/s. PCIe 组件之间采用双向单工(Dual-Simplex)的传输方式, 其在一个物理链接上有收/发两个方向. 一对物理连接称为一条 Lane, PCIe 允许两个设备间可以由多条Lane连接, 以此增加传输带宽. PCIe 协议的拓扑结构是树状型,组件之间是点到点(Point-to-Point)连接. 正如上图,其由以下组件构成:

  • Root Complex: PCIe 根复合体, I/O层次结构的根节点, 其位于 PCIe Host Bridge 之下. RC 可以支持 1 个或者多个根端口(Root Port). RP 可以连接 PCIe Switch 或者直连 EP. 其等同于 CXL Root Complex. Root Complex 和 Root Port 组合在一起更像 Switch 一样,其中 Root Complex 作为上游端口的 P2P 桥,而 Root Port 则作为下游端口的 P2P 桥.
  • Switch: PCIe 是点到点连接,一条链路需要一组端口。如果 RC 直接连接EP,那么 N 个 EP 就需要 RC 有 N 组 RP. 由于 RP 有限,需要 Switch 负责 RC 与 EP 之间的通信, 这样可以 PCIe 树连接更能多的 EP. PCIe 树里可以支持 0 个或者多个 SWITCH 的拓扑. 这里等比与 CXL Switch 即可.
  • Endpoints: 位于 PCIe 树的叶子,也就是 PCIe 设备. 这里就等比于 CXL Type3 Device.

除此之外,PCIe 树还包括 PCIe Bus,并且每个 PCIe Bus 都需要通过深度优先算法进行编号,每个 EP 设备也具有唯一的标识 BDF, 其中 B 标识所在的 PCIe BUS,D 则标识 BUS 上的 DEVICE ID,最后 F 则标识 DEVICE 内部的 Function. 通过 BDF 可以在 PCIe 总线上找到所需的组件. 接下来分析 BDF 的划分逻辑.

图片无法显示,请右键点击新窗口打开图片

在系统初始化会根据 ACPI 树找到 CXL 的根设备 ACPI0017,然后继续查找到 ACPI 设备 ACPI0016,其作为 CXL Host Bridge,可以通过其找到对应的 PCIe 根节点和对应的 Host Bridge,此时 Host Bridge 是 CXL 所在 PCIe 的根节点,因此可以获得其 PCIe Bus 为 PCIe BUS-C, BUS-C 上直接连接 Root Complex,Root Complex 可以包含一个或者多个 RootPort,在该场景下, 其 BDF 和 BUS 的划分如下:

  • RootPort: 作为 Root Complex 下的第一个 Root Port,那么其 DEVICE ID 为 0,由于其位于 BUS-C 上,因此 RootPort 的 BDF 则为 “0C:00.0”.
  • BUS-D: BUS-D 用于连接 RootPort 和 PCIe Switch, 因为深度优先算法,BUS-C 之后就是 BUS-D.
  • SWITCH Upstream P2P 桥: 将 PCIe Switch 拆开,里面其实由多个 P2P 桥组成,其上游端口也是一个 P2P 桥,由于其与 BUS-D 连接,并且 BUS-D 下游只有一个设备,那么其 BDF 为 “0D:00.0”.
  • BUS-E: Switch 内部拆开,上游 P2P 桥和下游 P2P 桥之间通过 PCIe 总线连接,根据深度优先算法,该 PCIe 总线为 BUS-D,此时所有的下游 P2P 桥连接在 BUS-D 上
  • SWITCH Downstream P2P 桥: 将 PCIe Switch 拆开,下游端口则由一个个 P2P 桥组成,其连接在内部 PCIe 总线上,每个下游 P2P 桥都有独立的 DEVICE ID,以此可以结合 BUS-E 进行编号,例如最左边的下游端口的 BDF 为 “0E:00.0”, 同理其左边的下游端口的 BDF 依次为 “0E:01.0”、”0E:02.0”、和 “0E:03.0”
  • BUS-F - BUS-12: 根据深度优先算法,PCIe 从左到右依次给连接 EP 的 PCIe BUS 进行编号,从最左边的 BUS-F, 到最后边的 BUS 为 BUS-12
  • EP: Switch DownStream Port 连接 EP,此时 EP 是 PCIe Bus 上唯一的设备,因此其 Device 为 0,结合 PCIe Bus,例如最左边到最右边 EP 的 BDF 依次为: “0F:00.0”、”10:00.0”、”11:00.0” 和 “12:00.0”.

图片无法显示,请右键点击新窗口打开图片

回到 BiscuitOS,此时 CXL 的拓扑对应的 BDF 可以从 PCIe 中找到对应关系,通过 lspci-common 工具可以看到具有如下的映射关系:

  • [0000:0C]: 表示 CXL Host Bridge,其对应的 PCIe BUS 为 BUS-C.
  • 00.0-[0d-0f]: 表示 BUS-C 下面有一个的 RootPort 连接的 PCIe 总线范围是 BUS-D 到 BUS-F,从这里也可以知道 RootPort 的 BDF 为 0C:00.0
  • 00.0-[0e-0f]: 表示 CXL Switch 的上游 P2P 桥,其 BDF 为 0D:00.0, 其下游总线的范围是 BUS-E 到 BUS-F
  • 00.0-[0f]: 表示 CXL Switch 的第一个下游 P2P 桥,其 BDF 为 0E:00.0, 其下游总线 BUS-F
  • 00.0: 表示 CXL Type3 Device 位于 BUS-F 的第一个 DEVICE,因此 BDF 为 0F:00.0

图片无法显示,请右键点击新窗口打开图片

查看 CXL RootPort 的 PCIe Configuration Space,可以看到其 BDF 为 “0C:00.0”, 并从其第一个 Capabilities 看到其描述 PCIe 设备为 “Express Root Port”,与上面分析的逻辑匹配.

图片无法显示,请右键点击新窗口打开图片

查看 CXL Switch UpStream Port 的 PCIe Configuration Space,可以看到其 BDF 为 “0D:00.0”, 并从其第一个 Capabilities 看到其描述 PCIe 设备为 “Express Upstream Port”,与上面分析的逻辑匹配.

图片无法显示,请右键点击新窗口打开图片

查看 CXL Switch DownStream Port 的 PCIe Configuration Space,可以看到其 BDF 为 “0E:00.0”, 并从其第一个 Capabilities 看到其描述 PCIe 设备为 “Express Downstream Port”,与上面分析的逻辑匹配.

图片无法显示,请右键点击新窗口打开图片

查看 CXL Endpoint 的 PCIe Configuration Space,可以看到其 BDF 为 “0F:00.0”, 并从其第二个 Capabilities 看到其描述 PCIe 设备为 “Express Endpoint”,这就是 CXL Type3 Device,与上面分析的逻辑匹配.


CXL 软件架构

图片无法显示,请右键点击新窗口打开图片

CXL 软件架构指的是 Linux 维护了一套虚拟的 CXL BUS 总线,基于该总线构建 CXL 不同组件之间的层级关系,其包括 CXL Decoder、CXL Bridge、CXL Device、CXL Port 等组件,这些组件的层级关系映射 CXL 硬件层级关系,因此结合 CXL 硬件拓扑,一起了解 Linux 是如何基于 Linux 设备树构建 CXL 架构.

图片无法显示,请右键点击新窗口打开图片

Linux 使用 STRUCT cxl_root 描述 CXL 树的根节点,也可以理解为 CXL 树形结构的根节点. 其 port 成员用于描述该根节点的信息,其用于建立根节点与下游节点的连接关系.

图片无法显示,请右键点击新窗口打开图片

Linux 使用 STRUCT cxl_port 表示 CXL 树型结构的节点,可以把 CXL PORT 类比成 CXL Swith 的 Upstream P2P 桥,其既可以表示 CXL Switch,并通过其成员来维护 CXL Switch Downstream P2P 桥的连接关系.

图片无法显示,请右键点击新窗口打开图片

Linux 使用 STRUCT cxl_dport 表示 CXL 实行结构的下游端口,可以把 CXL DPort 类比成 CXL Switch 的 Downstream P2P 桥,其由 CXL Port 统一维护.

图片无法显示,请右键点击新窗口打开图片

介绍了基础的数据结构之后,Linux 内核通过三者构建了 CXL 树型架构,其实现逻辑如下:

  • 首先使用 STRUCT cxl_root 作为 CXL 的根,其内部的 CXL Port 则定义为 ROOT0,并将 uport_dev 成员指向 ACPI0017 设备
  • 接着创建多个 CXL Dport,这些 Dport 被 ROOT0 的 XARRAY 进行维护,类似于这些 Dport 挂接到 ROOT0 的内部总线上,其中的一个 Dport 的 dport_dev 指向了 CXL Host Bridge
  • 接着使用 STRUCT cxl_port 作为 CXL Host Bridge,其 uport_dev 指向 ACPI0016 的 Host Bridge, 并将该 CXL Port 定义为 Port1. 另外 Port1 会将 parent 指向 ROOT0,以此构成层级关系.
  • 同理创建多个 CXL Dport,这些 Dport 被 PORT1 的 XARRAY 进行维护,类似与内部的 PCIe Bus,这些 DPORT 都挂接在该 PCIe Bus 上. 其中一个 Dport 作为 RootPort,其 dport_dev 指向了 CXL SWITCH
  • 接着使用 STRUCT cxl_port 作为 CXL Switch,其 uport_dev 指向了 CXL Switch Upstream P2P 桥. 并将 CXL Port 定义为 PORT2. 另外 PORT2 会将 parent 指向 PORT1,以此构成层级关系.
  • 同理创建多个 CXL Dport,这些 Dport 被 PORT2 的 XARRAY 进行维护,类似与内部的 PCIe Bus,这些 DPORT 都挂接在该 PCIe Bus 上. 其中一个 Dport 作为 CXL Switch Downstream Port,其 dport_dev 指向了 CXL Type Device
  • Linux 使用 STRUCT cxl_ep 表示一个 Endpoint 设备,这里的 Endpoint 即是 CXL Type3 设备.

通过上面的数据结构,Linux 构建了 CXL 软件架构,基于该结构,可以在用户空间使用 ndctl、cxl 等工具来访问这些数据结构,并通过这些数据结构找到对应的 PCIe 设备,以此和设备进行通信等.