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

传统的存储以磁盘和 SSD 为主,通过 PCIe 插入到各主机上,作为主机的个人存储设备. 在服务器中心,将多张磁盘插入到存储节点构建存储服务器,其他主机通过网络或通过 RDMA 存储前后端模式,从存储服务器上获得存储服务, 这便是常见的存储中心的池化方案. 当 CXL 2.0 出现之后,主机之间可以通过 CDFP 和 CXL SWITCH 形成新的互联方式, 在新的链接方式下,存储池化的模型也发生了变化.

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

在 CXL 池化方案中,在一个机柜内,服务器之间通过 CDFP 统一连接到顶部的 CXL SWITCH 上,服务器被分作 CPU、内存、存储、GPU 异构和网络节点,在 CPU 节点,服务器内部部署了高密的 CPU Core,内存服务器则插满了 CXL Type3 内存设备,存储服务器则插满了 CXL Type3 PMEM/DDR 设备作为存储节点,而 GPU 异构节点则插满了 GPU.

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

当一个任务下发下来,首先动态从计算节点热插指定数量的 CPU Core,接着从内存服务器热插指定容量的内存,然后从存储服务器节点热插指定容量的磁盘,最后根据需求热插指定数量的 GPU 和网卡, 通过上面的操作之后运行任务的环境弹性构建完毕. 当任务处理完毕之后,将弹性热插的资源全部归还给各模块.

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

在该方案中,存储节点的池化可以存在多种实现方案,本节重点介绍基于 CXL 的存储池化方案. 池化中两个基础功能就是: Pooling(隔离) 和 Sharing(共享). 首先是 Pooling(隔离),存储池可以根据需求弹性提供指定容量的存储设备,并且存储设备需要与其他主机之间隔离,也就是需求方独占(Exclusive) 存储设备. 其次是 Sharing(共享),存储池根据需求将同一个存储设备同时让多个主机之间共享,以便在特定场景下实现多主机之间数据共享.接下来重点介绍一下 Pooling(隔离) 的实现原理.

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

在 CXL 架构中,存储服务器由管理节点和存储节点构成,存储节点由 CXL Type3 设备构成,CXL Type3 设备分作两类,首先一类是由 NVDIMM 颗粒构成的非易失 CXL Type3 PMEM 设备,其特点是可以掉电存储数据. 另外一类是由 Volatile DRAM 颗粒构成的易失 CXL Type3 DDR 设备. 管理节点在收到命令之后,可以将 CXL Type3 设备热插到指定主机,也可以将设备从指定主机热拔设备.

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

主机则通过 CDPF 连接到 CXL SWITCH,CXL SWITCH 下游则连接到存储节点的 CXL Type3 设备,CXL SWITCH 独立于主机,由 Fabric Manager(FM) 管理, FM 可以让多台主机同时连接到 CXL SWITCH,并让每个主机看到的 CXL SWITCH 是一个独立隔离的 VCS(Virtual CXL SWITCH),这就是 CXL 池化方案的基础.

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

FM 为每个主机创建了独立的 CXL 总线(PCIe 总线)之后,池化系统可以根据需求,从存储节点将指定容量的 CXL Type3 设备热插到 CXL 总线. 接下来 CXL Type3 支持 MLD 和 SLD,简单的来说就是可以将一个 CXL Type3 设备虚拟成多个 CXL Type3 设备. 为了实现 Pooling(隔离) 的效果,FM 只能让一个 SLD 或者 MLD 挂到一条 CXL 总线上,另外 SLD/MLD 不会让其他 CXL 总线越界访问到被隔离的设备.

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

硬件上构建了一条隔离的 CXL 总线,以及隔离的 CXL Type3 设备. 接下来 CXL Type3 设备热插到系统之后,系统内部识别到热插的 CXL 设备,接下来就是对该设备创建新的 CXL REGION,以及构建文件系统. 如果 CXL REGION 的模式配置为 FSDAX,那么可以选择 XFS 和 EXT4 文件系统,因为这类文件系统支持 FSDAX 模式. 如果 CXL REGION 的模式配置为 DEVDAX,则可以选择 FAMFS 文件系统,因为该文件系统支持 DEVDAX 模式.

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

当系统不在需要存储设备时,系统内部先做软件层面的热插插座,然后向池化系统发送热拔(HOT-REMOVE)操作. 池化系统收到热拔请求之后,将对应的 CXL Type3 设备从 CXL 总线上热拔掉,这样该 CXL Type3 设备在存储池子里变成空闲可用的存储节点. 那么接下来通过一个实践案例讲解整个过程,实践案例在 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 Type3 PMEM)  --->
            (5120)  CXL Type3 PMEM Size(MiB)
            [*]     CXL STORAGE POOLING 
  [*] Package  --->
      [*] HETEROGENEOUS MEMORY MANAGEMENT
          [*] CXL STORAGE POOL: POOLING MODE  --->

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

# 切换到实践案例所在目录
cd /BiscuitOS/output/linux-6.10-x86_64/package/BiscuitOS-CXL-STORAGE-POOL-POOLING-default
# 注意!第一次部署,给内核打补丁,并重编译内核
patch -d ../../linux/linux -p1 < BiscuitOS-CXL-STORAGE-POOL-POOLING-default/CXL-FAMFS.patch
cd ../../linux/linux/
scripts/config --disable CONFIG_FAMFS
cd -
make kernel
# 准备依赖工具
make prepare
# 编译实践案例
make download
make build

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

首先启动第一台主机,当主机启动之后,首先使用 “lspci-common -vt” 查看 CXL 总线(PCIe 总线),可以看到 0000:0C 总线为 CXL 总线,其 CXL SWITCH 上只挂接了一个 CXL Type3 设备. 接下来向池化管理者发起弹性热插一块存储的命令:

# 切换路径
cd /BiscuitOS/output/linux-6.10-x86_64/package/BiscuitOS-CXL-STORAGE-POOL-POOLING-default/BiscuitOS-CXL-STORAGE-POOL-POOLING-default
# 发送热拔请求
./FManager.sh HOTPLUG HOST1
# 主机内部执行热拔命令
RunBiscuitOS.sh
# 查看 CXL 总线 
lspci-common -vt

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

当在主机 1 里面执行 RunBiscuitOS.sh 脚本,脚本里包含了热插存储节点的命令,当命令执行完毕之后,再次使用 “lspci-common -vt” 查看 CXL 总线,可以看到新插入的存储节点(0000:10:00.0), 并使用 mount 命令将存储节点挂载到 “/mnt/BiscuitOS-FAMFS”, 该文件系统里包含了一个文件 BiscuitOS.txt, 接下着使用 APP 命令对该文件进行读写操作, 可以看到现在文件里的数据是 “THIS IS BISCUITOS-HOST1”. 接下来将在主机 2 上也热插一个存储节点,使用如下命令:

# 切换路径
cd /BiscuitOS/output/linux-6.10-x86_64
# 启动主机 2
./RunBiscuitOS-CXL.sh

# 主机 2 外新开一个 Docker 窗口发起发送热拔请求
./FManager.sh HOTPLUG HOST2
# 主机 2 内部执行热拔命令
RunBiscuitOS.sh
# 查看 CXL 总线
lspci-common -vt

当主机 2 其中之后执行 RunBiscuitOS.sh 脚本,脚本里包含了热插存储节点的命令,当命令执行完毕之后,再次使用 “lspci-common -vt” 查看 CXL 总线,可以看到新插入的存储节点(0000:19:00.0), 并使用 mount 命令将存储节点挂载到 “/mnt/BiscuitOS-FAMFS”,该文件系统此时包含一个文件 BiscuitOS.txt, 接下来使用 APP 命令对该文件进行读写数据,此时可以在主机 1 和 主机 2 上使用 APP 进行读写操作,发现两者都是独立的存储节点,数据不会被污染, 这里可以很好的证明 Pooling(隔离)的特性. Pooling(弹性) 的另外一个特点就是可以弹性将存储节点热插到不同的主机,并保证隔离性,那么接下来进行实践, 使用如下命令:

# 主机 1 使用 APP 写入数据 "THIS IS BISCUITOS-HOST1"
APP WRITE "THIS IS BISCUITOS-HOST1"
# 然后在两个主机上同时执行相同的命令将存储节点热拔
RunBiscuitOS.sh HOTREMOVE

# 向池化管理发送弹性插拔命令
./FManager.sh HOTREMOVE HOST1
./FManager.sh REHOTPLUG HOST2
# 主机 2 内部执行热拔命令
RunBiscuitOS.sh
# 查看 CXL 总线
lspci-common -vt
# 读取数据
APP READ

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

可以看到存储节点被弹性热插到主机 2,并从该存储节点上读取到了主机 1 之前写入的数据. 从上面的实践可以验证存储池化 Pooling(弹性) 可以将存储节点动态热插到不同的主机,并可以保持隔离性. 以上便是 CXL 存储池化 POOLING 方案.

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