CXL Type3 设备作为 CXL ENDPOINT 角色存在于 CXL 拓扑结构里,向系统提供内存拓展功能. 在 CXL 2.0 架构里,CXL Type3 设备支持动态热插(HOTPLUG)热拔(HOT-REMOVE)操作, 通过该功能操作系统可以实现内存的动态扩容(Memory Expansion)缩容(Memory Reduction)操作. 本文接下来分析 CXL Type3 设备热插拔原理. 由于 CXL Type3 设备本质是 PCIe 设备,因此其热插拔能力需要分为 PCIe 设备热插拔和 CXL 设备热插拔两阶段进行研究.

在某些特殊场景,系统要求 PCIe 设备能够以高可靠性持续不断的运行,为此 PCIe 总线采用热插(HOTPLUG)热拔(HOT-REMOVE)技术,以此实现不关闭电源的情况下更换 PCIe 设备. 如上图,PCIe 设备的金手指有两个用于热插拔机制的引脚: PRSNT1#PRSNT2#. PCIe 设备上的这两个信号之间是短路的, 并且 PRSNT1#PRSNT2# 两信号的金手指长度要比其他信号的金手指长度要段一些.

上图是 PCIe 卡槽,其同样包含 PRSNT1#PRSNT2# 两个引脚,只是 PRSNT1# 被固连接到地, PRSNT2# 则被上拉. 由于 PRSNT1#PRSNT2# 两信号的金手指长度要比其他信号的金手指长度要段一些,当 PCIe 卡未被完全插入卡槽时,卡槽的 PRSNT2# 信号由于上拉的作用,将一直处于高电平状态.

当 PCIe 卡被完全插入到卡槽后,卡上的 PRSNT1# 和卡槽上接地的 PRSNT1# 连接,同时卡槽上的 PRSNT2# 信号则会被 PCIe 卡的 PRSNT2# 信号短接到地,从而卡槽 PRSNT2# 信号的电平变为低电平. 从插槽的角度来看,当 PRSNT2# 信号为高电平时,则认为 PCIe 卡未能正确插入或没有 PCIe 卡插入; 当 PRSNT2# 信号为低电平时,标明 PCIe 卡被正确插入.

由于 PCIe 总线采用的是点到点的链接(Point-to-Point Connections), 因此不用像 PCI 总线那样需要用于 PCI 设备的隔离逻辑(Isolation Logic),但每个端口都必须包含一个独立的热插拔控制器(HOTPLUG Controller),例如 RootPort 和 SWITCH 都需要配备热插拔控制器. 当 PCIe 卡被正确插入之后,卡槽 PRSNT2# 信号的低电平会通过 HOTPLUG Control Logic 单元传递到 SWITCH 到达 RootComplex,然后生成中断通知系统. 接下来通过一个实践案例边实践边了解整个过程

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

BiscuitOS 目前支持 CXL2.0 SWITCH 的实践,本节用于介绍如何在 BiscuitOS 上实践上图所示的 CXL 拓扑, 项目包含了一台主机,CPU 通过南桥转 CXL Host Bridge,其引入 PCI:0C 总线,直接连接到 CXL Root Complex,CXL RC 里仅包含一个 CXL RootPort,此时 CXL RootPort 的 BDF 为 “0C:00.0”. RP 通过 “BUS-D” 总线连接到 CXL SWITCH 上,SWITCH 只虚拟了一个 VCS,那么 Upstream Port(UP vPPB) 的 BDF 为 “0D:00.0”, VCS 通过 “BUS-E” 总线连接 4 个 Downstream Port(DP vPPB), 每个 DP 都连接到一个 PCIe 插槽上. 实践案例里只有 “BUS-F” 连接的 PCIe 插槽上插入一个 “CXL Type3” 设备,该设备的 BDF 为 “0F:00.0”. 实践案例在 BiscuitOS 上的部署逻辑如下:

# 切换到 BiscuitOS 项目目录
cd /BiscuitOS
# 选择开发环境,如果已经选择过可以跳过,这里与 linux 6.10 X86 为例
make linux-6.10-x86_64_defconfig
# 通过 Kbuild 选择需要部署的应用程序
make menuconfig

  [*] DIY BiscuitOS/Broiler Hardware  --->
      [*] CXL: Compute Express Link
            CXL Hardware Topology (CXL2.0: x1 VCS + x1 Type DDR)  --->
  [*] Package  --->
      [*] HETEROGENEOUS MEMORY MANAGEMENT
          [*] CXL HOTPLUG: HOTPLUG AND HOTREMOVE Type3  --->

# 配置完毕保存,然后进行部署
make

# 切换到实践案例所在目录
cd output/linux-6.10-x86_64/package/BiscuitOS-CXL-HOTPLUG-default
# 准备依赖工具
make prepare
# 编译实践案例
make download
make build

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

当 BiscuitOS 启动之后,可以使用 “lspci-common -vt” 查看当前 PCIe 总线拓扑结构,可以看到 CXL BUS(0000:0C) 上只挂着一个 CXL type3 设备. 接下来虚拟机外执行如下命令,用于模拟热插一块 CXL Type3 设备:

# 进入源码目录
cd /BiscuitOS/output/linux-6.10-x86_64/package/BiscuitOS-CXL-HOTPLUG-default/BiscuitOS-CXL-HOTPLUG-default

# 利用 QEMU QMP 热插 CXL Type3 设备
./RunQMP.sh HOTPLUG

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

当执行完之后,在 BiscuitOS 内再次使用 “lspci-common -vt” 查看 CXL BUS 的拓扑结构,并未看到新插入的 PCIe 设备,这是由于系统没有启用自动扫描 PCIe 总线的功能,需要手动更新 PCIe 硬件状态, 如果开发者想自动扫描 PCIe 总线,那么可以打开内核宏: CONFIG_HOTPLUG_PCICONFIG_HOTPLUG_PCI_PCIE. 如果没有开启自动扫描 PCIe 总线功能,那么接下来进行热插拔的第二阶段: CXL 设备热插拔. 参考如下命令:

# 重新扫描 PCIe BUS
echo 1 > /sys/bus/pci/rescan

# 查看新插入的 CXL Type3 设备
cxl list -M

# 安装驱动
insmod /lib/modules/$(uname -r)/kernel/drivers/dax/kmem.ko
insmod /lib/modules/$(uname -r)/kernel/drivers/dax/device_dax.ko
insmod /lib/modules/$(uname -r)/kernel/drivers/dax/dax_pmem.ko
insmod /lib/modules/$(uname -r)/kernel/drivers/dax/dax_cxl.ko

# 为新插入的 CXL Type3 设备创建一个 CXL REGION,并使能 CXL REGION
cxl create-region -d decoder0.0 -m mem1 -s 512M -t ram -w 1 -g 4096 -u
cxl enable-region region0

# 将 CXL REGION 模式设置为 SYSTEM-RAM, 以此当做普通内存使用
daxctl reconfigure-device  -N --mode=system-ram dax0.0 --force

# 运行测试用例,从新插入的 CXL Type3 内存拓展设备上读写内存
numactl --membind=1 BiscuitOS-CXL-HOTPLUG-default

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

当手动重新扫描 PCIe 总线之后,可以看到系统发现一条新的 PCIe 总线 “0000:10”, 并发现一个新的 PCIe 设备,其 BDF 为 “0000:10:00.0”. 接着使用 “lspci-common -vt” 命令可以看到 CXL SWITCH 的一个下游端口连接了一个新的 CXL Type3 设备. 使用 “cxl list” 命令,可以看到 CXL 子系统新增了一个 CXL Type3 设备.接下来对新增的设备进行使用:

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

在 BiscuitOS 里直接运行 RunBiscuitOS.sh 脚本,脚本里包含了实践所需的全部命令,从输出可以看到脚本创建了一个 CXL REGION,该 CXL REGION 来自 mem1,也就是新热插的 CXL Type3 设备. 接着将 CXL REGION 模式修改为 SYSTEM-RAM,此时系统可以将热插的 CXL Type3 设备内存当做普通内存进行使用. 以上便是 CXL 热插的整个过程,那么接下来重点分析热插的第二阶段,也就是 CXL 设备热插拔阶段的实现逻辑.

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

  • A: 当向 rescan 节点写入 1 时,通过系统调用会调用到 rescan_store 函数,该函数会按深度优先算法重新遍历 PCIe 所有总线,对于 CXL BUS 所在的 PCIe 0000:0C 总线,接着在 CXL BUS 所在的总线按深度优先算法遍历所有的设备和子总线,每遍历到一个 PCIe 设备时,都会通过读取 PCIe 设备的配置空间,以此确认硬件上 PCIe 设备是存在的. 当发现硬件上设备存在,当 PCIe 子系统并没有对应的 PCIe 设备,于是调用 pci_alloc_dev 函数添加一个 struct pci_dev 描述的 PCIe 设备,并调用 pci_setup_device 函数对 PCIe 设备进行初始化, 最后调用 pci_device_add 函数将该 PCIe 设备添加到 PCIe 子系统,此时并不会调用新 PCIe 设备对应的驱动.
  • B: 新的 PCIe 设备创建完毕之后,继续调用 pci_bus_add_devices 函数找到 PCIe 设备对应的驱动函数,此时通过调用 device_attach 函数进行查找,最终会查找到 CXL Type3 PCIe 设备对应的驱动 cxl_pci_probe 函数.
  • C: cxl_pci_probe 函数用于为 CXL Type3 PCIe 设备创建对应的 CXL MEMDEV 设备(mem1),并对 CXL MEMDEV 设备进行初始化,并在 CXL 子系统中创建对应的 CXL ENDPOINT 组件. 该步骤与一个 CXL Type3 设备的初始化无异. 驱动执行完毕之后,CXL Type3 设备就加入到 CXL 子系统,并在 “/sys/bus/cxl/devices/” 目录看到为其新增加的设备,包括 mem1、endpoint4 等.

以上便是 CXL Type3 设备热插(HOYPLUG)的整个过程,其基于 PCIe 热插拔创建 PCIe 设备,并调用 CXL Type3 设备对应的驱动进行初始化,并添加到 CXL 子系统里,之后和开机初始化的 CXL Type3 设备无异.

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

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

当支持 HOTPLUG 之后,同时也需要支持动态的热拔(HOT-REMOVE), 热拔操作并不是直接将 CXL Type3 设备拔除,而是要按热插的反顺序进行,因此热拔也分作两个阶段: CXL 设备热拔和 PCIe 设备热拔. 基于上面的实践,热拔一个 CXL Type3 设备使用如下命令:

# 关闭 CXL Type3 设备对应的 CXL REGION
cxl disable-region region0
# 摧毁 CXL REGION
cxl destroy-region region0
# 关闭 CXL Type3 对应的 CXL MEMDEV 设备
cxl disable-memdev mem1
# 查看 CXL BUS 确认 CXL Type3 设备已经从 CXL 子系统中移除
cxl list

当执行完上面的操作之后,CXL Type3 设备将从 CXL 子系统中移除,此时 CXL Type3 设备只是以一个 PCIe 设备存在 PCIe 子系统中,因此此时可以使用 “lspci-common -vt” 在 PCIe 拓扑结构里看到要热拔的设备. 接下来从 PCIe 子系统中将 CXL Type3 设备移除, 使用如下命令:

# 触发 CXL Type 设备对应 PCIe 设备热拔操作
echo 1 > /sys/bus/pci/devices/${DEV_BDF}/remove
# 查看 PCIe 总线拓扑结构
lspci-common -vt

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

当向 CXL Type3 设备对应的 PCIe 设备节点 remove 文件写入 1 之后,会触发该 PCIe 设备的热拔操作,其会将 PCIe 设备从 PCIe 子系统中移除. 当执行完上面的操作之后,可以通过 “lspci-common -vt” 命令查看 PCIe 总线拓扑,此时已经看不到热拔的设备. 此时可以将 CXL Type3 设备从 PCIe 卡槽中拔除. 如果此时不拔,再次遍历 PCIe 总线还是会触发该 PCIe 设备的热插操作. 那么接下来看一下 PCIe 热拔流程:

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

当向 CXL Type3 设备对应 PCIe 设备节点的 remove 写入 1 之后,通过系统调用会调用到 remove_store 函数,该函数调用 pci_stop_and_remove_bus_device_locked 函数,其内部的 pci_stop_bus_device 函数将 PCIe 设备 STOP,然后调用 pci_remove_bus_device 函数将 PCIe 设备从 PCIe BUS 上移除,并最从 PCIe 子系统中将 PCIe 设备移除和释放资源. 至此便是 CXL Type3 设备的热插拔操作,开发者可以根据需求进行使用.

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