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

在传统服务器架构了,每个主机包含私有的内存条、磁盘和 GPU 显卡等外设,特别是在大模型和 AI 集群里,大量的 GPU 安插在一台独立的主机上,该主机可以与 GPU 交互数据,但其他主机要访问该 GPU 的数据,要么通过网络,并且该主机作为数据中转,也就是 GPU 先将处理好的数据放到其主机的内存上,其他主机可以通过网络从该主机上访问到这些数据。或者其他主机可以通过 RDMA 直接与 GPU 访问数据. 这些操作都会引入很多次拷贝导致额外的开销.

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

CXL2.0 的出现,可以实现多主机之间直接共享物理内存,那么 GPU 处理好的数据可以放到共享 CXL 共享物理内存池子里,多主机之间无需进行拷贝,直接就可以使用这些数据,大大提供了访问 GPU 的效率. 这里利用了 CXL2.0 的 Sharing 模式,可以实现多台主机的之间共享 CXL 内存,另外还涉及到 PCIe 外设直接使用 CXL 内存.

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

在 GPU 集群上,传统 CPU 通过 DMA 在 DDR 和 GPU 之间搬运数据,如上图路线 A,该路线可以看到 GPU 设备需要经过 PCIe RootPort/Root Complex,在通过 Chipset(南桥) 才能把数据搬运到 CPU 内部的 DDR 上. 而在采用 CXL2.0 架构的 GPU 集群,GPU 可以将存储存储在 CXL Type3 设备上,如上图线路 B, GPU 经过 PCIe RootPort/RootComplex,在 Chipset 处将数据中转到 CXL RootComplex/RootPort, 最终将数据存储到指定的 CXL Type3 设备.

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

在 GPU 集群里,GPU 将处理好的数据直接通过 CXL SWITCH 搬运到 CXL Type3 设备上,此时该主机可以访问 GPU 数据,其他主机同时也能看到这些数据,无需通过多余的拷贝操作,其他主机就可以消费 GPU 产生的数据。在上图中,GPU 服务器可以是集成 CXL SWITCH 的服务端,其通过 CXL 与 CXL SWITCH 连接; GPU 服务器也可以是普通的主机,其通过 CDFP 连接到 CXL 服务器上,这样可以实现多主机之间共享 GPU 产生的数据.

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

GPU 集群要使用 CXL2.0 Sharing 模式共享数据,那么其可以依赖 VFIO 机制实现,VFIO 提供了用户态访问 PCIe 的接口,简单来说就是把内核访态访问 PCIe 的操作挪到用户空间,那么对于 CXL 内存,可以使用 DEVDAX 方式,直接访问 CXL Type3 设备内指定的物理内存, 例如上图在 GPU 服务器上,用户态程序通过 VFIO 框架访问 GPU 设备,包括 BAR 的访问、配置空间的访问,以及将 GPU 产生的中断通过 EVENTFD 通知用户进程. VFIO 在底层需要使用 IOMMU 对离散的物理内存进行访问,这里使用 DEVDAX,那么 CXL 物理内存都是连续的.

CXL GPU Practice

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

BiscuitOS 提供了 CXL GPU 的实践环境,其 CXL 拓扑如上图, BiscuitOS 提供了一个 GPU 服务器,GPU 通过 PCIe 连接到主机上,CXL Type3 设备则通过 CXL Switch 转 CXL RootPort 连接到主机. 应用程序可以在该环境里,基于 VFIO 框架,将 GPU 产生的数据直接 DMA 搬运到 CXL 物理内存上,DMA 搬运完毕之后,GPU 通过 MSIX 中断通知主机. 接下来在 BiscuitOS 上进行实践,请参考如下命令:

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

  [*] DIY BiscuitOS/Broiler Hardware  --->
      -*-   Intel Q35
      [*]     vIOMMU
      (intel_iommu=on) CMDLINE on Kernel
      [*] CXL SHMEM GPU Device
      [*] CXL: Compute Express Link
            CXL Hardware Topology (CXL2.0: x1 VCS + x1 Type3 DDR)  --->
  [*] Package  --->
      [*] HETEROGENEOUS MEMORY MANAGEMENT
          [*] CXL GPU(VFIO)  --->

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

# 切换到实践案例所在目录
cd output/linux-6.10-x86_64/package/BiscuitOS-CXL-GPU-default
# 内核请开启宏: CONFIG_IOMMUFD/CONFIG_VFIO
# 准备依赖工具
make prepare
# 编译实践案例
make download
make build

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

当 BiscuitOS 启动之后,使用 lspci-common 命令查看 PCIe 和 CXL 的拓扑,可以看到 CXL 包含一个 CXL SWITCH,一个 CXL Type3 设备连接到该 CXL SWITCH 上,其 BDF 为 “0f:00.0”. 另外在 PCIe 总线上,可以看到 VENDORID 为 “0601:1991” 的 PCIe 设备,其就是 GPU 设备, 对应的 BDF 为 “00:03.0”.

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

直接运行 RunBiscuitOS.sh 脚本,脚本里包含运行所需的所有命令,可以看到系统现将 CXL Type3 设备配置为 DEVDAX 模式,其在 “/dev” 目录下生成 “dax0.0” 节点,接着使用 VFIO 框架,配置 GPU 的 DMA 相关的寄存器,并发起 DMA 操作,其从 GPU 将数据直接搬运到 CXL Type3 设备指定物理内存上. 可以看到搬运结束之后,GPU 发送一个 MSIX 中断通知系统,此时该中断被 EVENTFD 转发给用户进程,用户进程收到中断之后,从 CXL Type 设备上直接读取内容,可以看到 GPU 传输了 “DMA FROM GPU TO CXL MEMORY” 等字符串. 以上便是单机场景下 CXL 与 GPU 互通访问,接下来基于 CXL Sharing 模式,将 GPU 搬运的数据在多个主机之间互通.

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

BiscuitOS 提供了 CXL Sharing GPU 的实践环境,其 CXL 拓扑如上图, BiscuitOS 提供了两个主机,其中一个主机中包含了 GPU,GPU 通过 PCIe 连接到主机上,CXL Type3 设备则通过 CXL Switch 转 CXL RootPort 连接到主机. 应用程序可以在该环境里,基于 VFIO 框架,将 GPU 产生的数据直接 DMA 搬运到 CXL 物理内存上,DMA 搬运完毕之后,GPU 通过 MSIX 中断通知主机, 此时另外一台主机同时可以看到 GPU 刚刚 DMA 完的数据. 接下来在 BiscuitOS 上进行实践,请参考如下命令:

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

  [*] DIY BiscuitOS/Broiler Hardware  --->
      -*-   Intel Q35
      [*]     vIOMMU
      (intel_iommu=on) CMDLINE on Kernel
      [*] CXL SHMEM GPU Device
      [*] CXL: Compute Express Link
            CXL Hardware Topology (CXL2.0: x2 Host(VH) + MH-MLD)  --->
  [*] Package  --->
      [*] HETEROGENEOUS MEMORY MANAGEMENT
          [*] CXL SHMEM: GPU(VFIO)  --->

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

# 切换到实践案例所在目录
cd output/linux-6.10-x86_64/package/BiscuitOS-CXL-SHMEM-GPU-default
# 内核请开启宏: CONFIG_IOMMUFD/CONFIG_VFIO
# 准备依赖工具
make prepare
# 编译实践案例
make download
make build

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

当第一个主机启动之后,查看 CXL 和 PCIe 拓扑,可以看到 CXL 上包含两个 CXL Type3 设备,而 PCIe 总线上可以看到 VENDOR 为 “0601:1991” 的 GPU 设备. 接下来新启动一个终端来启动第二个主机,参考如下命令:

# 切换到 BiscuitOS 项目目录, 同理以 linux 6.10 X86 为例
cd BiscuitOS/output/linux-6.10-x86_64
# 直接启动第二个主机
./RunBiscuitOS-CXL.sh

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

当第二个主机启动之后,同样查看 CXL 拓扑,此时看到共享的 CXL Type3 设备, 此时在 PCIe 总线上并为看到 VENDOR 为 “0601:1991” 的 GPU 设备,接下来在第一个主机上运行 RunBiscuitOS.sh 脚本,待脚本运行完毕之后,在主机二上运行脚本 RunBiscuitOS-CXL.sh:

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

当主机运行 RunBiscuitOS.sh 脚本,其会从 GPU 设备通过 DMA 搬运数据到 CXL Sharing 的 Type3 设备上,然后在主机上运行 RunBiscuitOS-CXL.sh 脚本,其会将 CXL Type3 设备作为 DEVDAX 模式呈现在主机二上,接着主机二直接运行 CXL-READ 命令,该命令会从共享的 CXL Type3 设备上读取数据,可以看到读到的数据正好是 GPU DMA 搬运的数据. 通过上面的实践,可以看到 CXL Sharing 模式可以实现多 GPU 集群之间数据共享,去掉了 GPU 集群之间的数据拷贝. 以上便是所有的实践.

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