CXL可以说是自PCIe技术诞生几十年以来最具变革性的新技术了。可以想象有了CXL以后机箱的边界将被彻底打破,服务器互相使用对方的内存,网卡,GPU 。整个机架甚至跨机架的超级资源池化成为可能,云计算也将进入一个新的时代。
当前Intel, AMD 目前最新CPU型号也都已经支持CXL接口,三星也有支持CXL的内存模组提供,但是普及性还是非常低,可能只有极个别的研究人员才能拿到真正的物理设备。作为不那么有实力的普通IT从业者如何才能体验这一最前沿的科技成果呢?使用模拟器便是最方便的选择。
本文包括两部分内容,
1)如何使用qemu搭建CXL模拟环境,列出关键的避坑点。这部分需要一定的软件开发基础。
2)从模拟器里看到的CXL设备什么样,这部分适合大多数IT从业者,看到就是体验到
搭建模拟环境需要三个软件:1)合适版本的qemu 2)合适版本的linux内核 3)合适版本的ndctl软件。 这里说“合适” 是想说并非随便抓个版本过来就可以使用,因为CXL正在飞速演进,软件生态也在不断迭代中,合适的版本才能配合到一起。
1) 编译qemu, 我使用的qemu代码库在:https://gitlab.com/jic23/qemu/-/tree/cxl-2023-04-19, commit ID: 8eb2a03258
具体编译过程不是本文重点,这里省略,请大家自行解决。
2)使用qemu启动一个VM,我使用的是 CentOS8.4, 可以自己安装也可以从网上下载安装好了的qcow2文件。同样这个的具体操作步骤省略。
3)编译Linux内核,步骤2把VM起来后我们需要更新其内核。本文测试使用的是6.3.0版本内核,从https://gitee.com/mirrors/linux_old1.git 这个代码库克隆,commit ID: 457391b03803
编译内核时需要使能CXL支持,在.config文件里如下配置:
# grep CXL .config
CONFIG_CXL_BUS=m
CONFIG_CXL_PCI=m
CONFIG_CXL_MEM_RAW_COMMANDS=y
CONFIG_CXL_ACPI=m
CONFIG_CXL_PMEM=m
CONFIG_CXL_MEM=m
CONFIG_CXL_PORT=m
CONFIG_CXL_SUSPEND=y
CONFIG_CXL_REGION=y
CONFIG_CXL_REGION_INVALIDATION_TEST=y
CONFIG_DEV_DAX_CXL=m
4) ndctl工具,使用 https://github.com/pmem/ndctl.git , commit ID: 96bae09
这个工具需要在guest OS里面使用。也需要大家自行编译。
应该说上面这几个过程还是颇有些繁琐,光编译环境就要Host, guest搭两次,笔者也是折腾了好久。为了不影响本文主要目标,保持行文简洁只能忍痛省略。
启动qemu时添加CXL.mem设备,这里直接给出启动命令,大家看命令行就知道如何设置
/data/cxl/qemu/build/qemu-system-x86_64 -enable-kvm -cpu host -smp 8 \
-machine type=q35,accel=kvm,nvdimm=on,cxl=on \
-drive if=ide,file=./centosx86.qcow2,cache=none \
-m 32G,slots=8,maxmem=64G -vnc :3 \
-nic user,hostfwd=tcp::10023-:22 \
-net nic,macaddr=12:34:56:78:9a:bc \
-net tap,ifname=tap3 \
-cdrom /data/CentOS-8.4.2105-x86_64-dvd1.iso \
-object memory-backend-file,id=cxl-mem1,share=on,mem-path=/tmp/cxltest.raw,size=256M \
-object memory-backend-file,id=cxl-lsa1,share=on,mem-path=/tmp/lsa.raw,size=256M \
-device pxb-cxl,bus_nr=12,bus=pcie.0,id=cxl.1 \
-device cxl-rp,port=0,bus=cxl.1,id=root_port13,chassis=0,slot=2 \
-device cxl-type3,bus=root_port13,memdev=cxl-mem1,lsa=cxl-lsa1,id=cxl-pmem0 \
-M cxl-fmw.0.targets.0=cxl.1,cxl-fmw.0.size=4G
1) 首先我们看系统里cxl设备出现在哪里:
# ls /dev/cxl/
mem0
没错,就是多出了这么个小小的设备文件。
2)再具体一点,使用刚才编译的ndctl工具,最新的版本里面带了一个叫cxl的工具,
还可以再具体一些,# cxl list -v
# cxl list -v
[
{
"bus":"root0",
"provider":"ACPI.CXL",
"nr_dports":1,
"dports":[
{
"dport":"pci0000:0c",
"alias":"ACPI0016:00",
"id":12
}
],
"ports:root0":[
{
"port":"port1",
"host":"pci0000:0c",
"depth":1,
"nr_dports":1,
"dports":[
{
"dport":"0000:0c:00.0",
"id":0
}
],
"endpoints:port1":[
{
"endpoint":"endpoint2",
"host":"mem0",
"depth":2,
"memdev":{
"memdev":"mem0",
"pmem_size":268435456,
"serial":0,
"host":"0000:0d:00.0",
"partition_info":{
"total_size":268435456,
"volatile_only_size":0,
"persistent_only_size":268435456,
"partition_alignment_size":0
}
},
"decoders:endpoint2":[
{
"decoder":"decoder2.0",
"interleave_ways":1,
"state":"disabled"
},
{
"decoder":"decoder2.1",
"interleave_ways":1,
"state":"disabled"
},
{
"decoder":"decoder2.2",
"interleave_ways":1,
"state":"disabled"
},
{
"decoder":"decoder2.3",
"interleave_ways":1,
"state":"disabled"
}
]
}
],
"decoders:port1":[
{
"decoder":"decoder1.0",
"interleave_ways":1,
"state":"disabled",
"nr_targets":1,
"targets":[
{
"target":"0000:0c:00.0",
"position":0,
"id":0
}
]
}
]
}
],
"decoders:root0":[
{
"decoder":"decoder0.0",
"resource":79725330432,
"size":4294967296,
"interleave_ways":1,
"max_available_extent":4294967296,
"pmem_capable":true,
"volatile_capable":true,
"accelmem_capable":true,
"nr_targets":1,
"targets":[
{
"target":"pci0000:0c",
"alias":"ACPI0016:00",
"position":0,
"id":12
}
]
}
]
}
]
3) 使用这个设备
当前我们模拟出来的是一个CXL接口的pmem设备,像AEP。
# cxl create-region -d decoder0.0 -t pmem -m mem0
{
"region":"region0",
"resource":"0x1290000000",
"size":"256.00 MiB (268.44 MB)",
"interleave_ways":1,
"interleave_granularity":256,
"decode_state":"commit",
"mappings":[
{
"position":0,
"memdev":"mem0",
"decoder":"decoder2.0"
}
]
}
cxl region: cmd_create_region: created 1 region
# cxl list
[
{
"memdevs":[
{
"memdev":"mem0",
"pmem_size":268435456,
"serial":0,
"host":"0000:0d:00.0"
}
]
},
{
"regions":[
{
"region":"region0",
"resource":79725330432,
"size":268435456,
"interleave_ways":1,
"interleave_granularity":256,
"decode_state":"commit"
}
]
}
]
[root@vm3 ~]# ndctl create-namespace
{
"dev":"namespace0.0",
"mode":"fsdax",
"map":"dev",
"size":"250.00 MiB (262.14 MB)",
"uuid":"25fab29a-f4ce-483e-94b4-176c40111f6c",
"sector_size":512,
"align":2097152,
"blockdev":"pmem0"
}
[root@vm3 ~]# lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sda 8:0 0 50G 0 disk
└─sda1 8:1 0 50G 0 part /
sr0 11:0 1 9.3G 0 rom
pmem0 259:0 0 250M 0 disk
经过上面这一系列的cxl命令,ndctl命令,lsblk终于看到pmem0设备了,使用过AEP的的朋友对这个设备肯定不陌生,之后就跟AEP的使用方法一样了,格式化文件系统,映射到内存空间 ...,本文不再赘述。