如何给qemu编译配置pvrdma设备
缘起于我在审计源码时找到pvrdma的一个bug, 因此需要构建一个pvrdma设备来测试, 但是呢, 因为rdma硬件设备很贵, 所以测试只能用模拟的rdma设备, 这里面, 主要使用的是soft-RoCE类型的模拟设备.
本身这件事不算困难, 奈何网上的资料太少了, 导致我在这方面栽了不少跟头, 分享出来, 希望能够帮助到大家.
准备rdma相关驱动
最简单的方法, 使用centos7或者centos8, 执行lsmod |grep rdma
, 如果存在rdma_rxe结果, 说明系统已经有了rdma相关驱动. 可以直接跳过这个准备步骤.
另一种方法, 编译一个新内核, 再编译用户态模块. 坑爹之路就此开始.
以下以ubuntu 16.04为例, 按照soft-RoCE的wiki指示开始编译内核.
下载此项目的v18分支. (按我的理解, 直接下载其它kernel源码来编译也是ok的, 没必要非得用这个, 我没有测试, 就不清楚行不行了)
准备相关编译组件
1
2
3
4
5
6sudo apt-get install git
sudo apt-get install libncurses5-dev
sudo apt-get install libssl-dev
sudo apt-get install libibcm1 libibcm-dev ibverbs-utils libibverbs-dev
sudo apt-get install libibverbs1 librdmacm-dev librdmacm1 rdmacm-utils
sudo apt-get install libswitch-perl进入项目, 开始配置编译选项
1
2cp /boot/config-`uname -r` .config
make menuconfig弹出一个配置界面, 输入**/, 并键入RXE**, 会得到如下结果:
按1, 跳转到改选项, 按M启用改模块, 之后回到这个配置的主界面保存配置, 再退出
开始编译并安装
1
2
3
4sudo make -j 4 # 此处的4是cpu的个数, 不要超过它, 不然编译可能更慢
sudo make modules_install
sudo make install
sudo make headers_install INSTALL_HDR_PATH=/usr重启并使用新编译的内核
下载用户态项目文件, 解压后进入目录
编译并配置
1
2
3
4
5
6
7
8./configure --libdir=/usr/lib64/ --prefix=
make
make install
sudo ln -s /usr/lib64/librxe.a /usr/lib/librxe.a
sudo ln -s /usr/lib64/librxe.la /usr/lib/librxe.la
sudo ln -s /usr/lib64/librxe-rdmav2.so /usr/lib/librxe-rdmav2.so
sudo ln -s /usr/lib64/librxe.so /usr/lib/librxe.so使用下列命令查看状态, 可能是如下效果
1
2
3$ rxe_cfg status
Name Link Driver Speed NMTU IPv4_addr RDEV RMTU
ens160 yes vmxnet3 10GigE 1500 192.168.170.129执行下列命令添加网卡并查看状态
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25$ rxe_cfg add ens160
$ rxe_cfg start
$ ibv_devices
device node GUID
------ ----------------
rxe0 020c29fffeee3b66
$ ibv_devinfo
hca_id: rxe0
transport: InfiniBand (0)
fw_ver: 0.0.0
node_guid: 020c:29ff:feee:3b66
sys_image_guid: 0000:0000:0000:0000
vendor_id: 0x0000
vendor_part_id: 0
hw_ver: 0x0
phys_port_cnt: 1
port: 1
state: PORT_ACTIVE (4)
max_mtu: 4096 (5)
active_mtu: 1024 (3)
sm_lid: 0
port_lid: 0
port_lmc: 0x00
link_layer: Ethernet测试网络状态
1
2$ ibv_rc_pingpong -d rxe0 -g 1
local address: LID 0x0000, QPN 0x000011, PSN 0x29338a, GID fe80::12cb:883b:5ccf:e656另外开启一个bash, 执行
ibv_rc_pingpong -d rxe0 -g 1 127.0.0.1
这时候server端会多出一行输出:
1
remote address: LID 0x0000, QPN 0x000012, PSN 0xe63ffc, GID fe80::12cb:883b:5ccf:e656
client端会得到如下输出:
1
2local address: LID 0x0000, QPN 0x000012, PSN 0xe63ffc, GID fe80::12cb:883b:5ccf:e656
remote address: LID 0x0000, QPN 0x000011, PSN 0x29338a, GID fe80::12cb:883b:5ccf:e656
配置qemu编译选项
修改qemu项目中如下文件
contrib/rdmacm-mux/meson.build
1
2
3
4
5
6
7
8
9executable('rdmacm-mux', files('main.c'),
dependencies: [glib, libumad],
build_by_default: true,
install: false)
改为
executable('rdmacm-mux', files('main.c'),
dependencies: [glib, libumad],
build_by_default: true,
install: true)执行如下编译配置
1
$ ./configure --enable-rdma --enable-pvrdma --enable-kvm --enable-debug --target-list=x86_64-softmmu
接下来就是make和make install了. 具体参考上一篇关于qemu编译的blog.
编译过程中可能遇到的问题
出现如下提示:
1
2
3
4
5" OpenFabrics librdmacm/libibverbs/libibumad not present." \
" Your options:" \
" (1) Fast: Install infiniband packages (devel) from your distro." \
" (2) Cleanest: Install libraries from www.openfabrics.org" \
" (3) Also: Install softiwarp if you don't have RDMA hardware"查看项目build/config.log文件, 查看出错原因. 我的主要错误是:
1
if has error:config-temp/qemu-conf.c:1:27: fatal error: rdma/rdma_cma.h: No such file or directory
通过执行
find / -name "rdma_cma.h
对整个硬盘搜索rdma_cma.h文件, 发现没找到, 就去网上找了它所在的librdmacm库, 下载解压后, 使用./autogen.sh && ./configure && make && make install
安装即可.ERROR: Could not detect Ninja v1.7 or newer
这个应该是本机安装的ninja组件, 或者版本过低, 可以通过如下命令下载安装解决
1
2
3
4
5
6$ wget https://github.com/ninja-build/ninja/releases/download/v1.8.2/ninja-linux.zip
$ sudo unzip ninja-linux.zip -d /usr/local/bin/
$ sudo update-alternatives --install /usr/bin/ninja ninja /usr/local/bin/ninja 1 --force
输出:update-alternatives: using /usr/local/bin/ninja to provide /usr/bin/ninja (ninja) in auto mode
$ /usr/bin/ninja --version
1.8.2../hw/block/virtio-blk.c:30:22: fatal error: scsi/sg.h: No such file or directory compilation terminated.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16$ wget http://launchpadlibrarian.net/353523714/libc6-dev_2.23-0ubuntu10_amd64.deb
$ dpkg -i libc6-dev_2.23-0ubuntu10_amd64.deb
(Reading database ... 184747 files and directories currently installed.)
Preparing to unpack libc6-dev_2.23-0ubuntu10_amd64.deb ...
Unpacking libc6-dev:amd64 (2.23-0ubuntu10) over (2.23-0ubuntu3) ...
dpkg: dependency problems prevent configuration of libc6-dev:amd64:
libc6-dev:amd64 depends on libc6 (= 2.23-0ubuntu10); however:
Version of libc6:amd64 on system is 2.23-0ubuntu3.
libc6-dev:amd64 depends on libc-dev-bin (= 2.23-0ubuntu10); however:
Version of libc-dev-bin on system is 2.23-0ubuntu3.
dpkg: error processing package libc6-dev:amd64 (--install):
dependency problems - leaving unconfigured
Errors were encountered while processing:
libc6-dev:amd64
$ apt -f install
启动参数
参照官方指导文档, 需要保证ib_cm模块没有加载, 因此先卸载相关模块. 并且加载必要模块
1
2$ rmmod rdma_ucm rdma_cm ib_cm
$ insmod ib_umad注意, 默认情况下, rdmacm-mux文件的输出是调用的syslog, 建议改成printf 并把umad_open_port的返回结果输出出来, 以便找到失败原因.
在执行上述操作前, 请确保已经执行过了
rxe_cfg start
操作.执行下列命令, 创建所需socket
1
2
3
4
5$ qemu-6.0.0/build/contrib/rdmacm-mux/rdmacm-mux -d rxe0
unix_socket_path=/var/run/rdmacm-mux-rxe0-1 这行是改成printf后会输出的内容.
rdma-device-name=rxe0 这行是改成printf后会输出的内容.
rdma-device-port=1 这行是改成printf后会输出的内容.
Service started创建桥接网络
1
2
3
4
5
6
7$ apt-get install uml-utilities
$ tunctl -t tap0 -u `whoami`
$ chmod 0666 /dev/net/tun 让所有用户可读
$ ifconfig tap0 192.168.2.1 up 给tap0设置ip段
添加防火墙规则
$ echo 1 > /proc/sys/net/ipv4/ip_forward
$ iptables -t nat -A POSTROUTING -j MASQUERADE最后, qemu启动参数
1
2
3
4
5$ /usr/local/bin/qemu-system-x86_64 -object memory-backend-ram,id=mb1,size=1G,share=on -numa node,memdev=mb1 \
-netdev tap,id=mynet0,ifname=tap0,script=no,downscript=no \
-device vmxnet3,netdev=mynet0,id=net0,mac=52:54:00:e3:00:81,addr=0x10.0,multifunction=on \
-chardev socket,path=/var/run/rdmacm-mux-rxe0-1,id=mads -device pvrdma,addr=0x10.1,ibdev=rxe0,netdev=net0,mad-chardev=mads \
-m 1G -hda qemu-6.0.0/centos7.img --enable-kvm根据官方文档描述, pvrdma必须要三个参数: ibdev, netdev, mad-chardev, 并且
To support it, pvrdma device is composed of two PCI functions, an Ethernet device of type vmxnet3 on PCI slot 0 and a PVRDMA device on PCI slot 1.
, 即在slot 0必须是一个vmxnet3网卡, slot 1是pvrdma, 所以此处vmxnet3的pci地址给的是 0x10.0, 则pvrdma是0x10.1.
libvirt配置
如果不想直接用命令启动qemu, 用libvirt也可
假如已经存在一个格式为qcow2的虚拟机image文件了, 假设文件名为centos7.img
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21安装相关组件
$ apt install libvirt libvirt-python libguestfs-tools virt-install
启动相关服务
$ systemctl enable libvirtd
$ systemctl start libvirtd
查看桥接网卡
$ brctl show
bridge name bridge id STP enabled interfaces
virbr0 8000.525400455887 yes virbr0-nic
添加现成虚拟机
$ virt-install --import --name vm1 \
--memory 1024 --vcpus 1 --cpu host \
--disk centos7.img,format=qcow2,bus=virtio \
--network bridge=virbr0,model=virtio \
--os-type=linux \
--os-variant=centos7.0 \
--nographics \
--noautoconsole安装后, 先关闭虚拟机, 然后在
/etc/libvirt/qemu/
目录下找到vm1.xml文件1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22<domain type='kvm'>
<name>vm2</name>
<devices>
<interface type='bridge'>
<mac address='56:b4:44:e9:62:dc'/>
<source bridge='virbr0'/>
<model type='vmxnet3'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x10' function='0x0' multifunction='on'/>
</interface>
</devices>
<qemu:commandline>
<qemu:arg value='-object'/>
<qemu:arg value='memory-backend-ram,id=mb1,size=1G,share'/>
<qemu:arg value='-numa'/>
<qemu:arg value='node,memdev=mb1'/>
<qemu:arg value='-chardev'/>
<qemu:arg value='socket,path=/var/run/rdmacm-mux-rxe0-1,id=mads'/>
<qemu:arg value='-device'/>
<qemu:arg value='pvrdma,addr=10.1,ibdev=rxe0,netdev=bridge0,mad-chardev=mads'/> 注意这里是10.1 必须和vmxnet3的bus='0x00' slot='0x10' function='0x0' 对应, 保证bus相同, slot相同, 其中function: 0为vmxnet3, 1为pvrdma.
</qemu:commandline>
</domain>此处的bridge0是哪一个我不是很确定, 需要的人得自己琢磨一下了. 我配置这里主要是为了获取vmxnet3的真实启动配置参数.
常见的virsh命令:
- 列出所有虚拟机
virsh list --all
- 获取虚拟机信息
virsh dominfo vm1
- 关闭虚拟机
virsh shutdown vm1
- 启动虚拟机
virsh start vm1
- 虚拟机随宿主机启动而自动启动
virsh autostart vm1
- 安全重启虚拟机
virsh reboot vm1
- 重启虚拟机(不安全,hard reset)
virsh reset vm1
- 删除虚拟机
virsh shutdown vm1
virsh undefine vm1
virsh pool-destroy vm1
结语
一开始并不知道centos可以省去那么多麻烦, 在ubuntu上卡了很久很久, 问了Li Qiang大佬和官方的人, 也没有得到具体的配置方法, 最后总算是靠着一行行看配置文件找到失败原因, 并一一解决, 这里更不提那个softiWarp了, 这里虽然没用到, 但是那个错误提示里提到后, 我就以为需要配置它, 也踩了不少的坑 T-T.
在rdmacm-mux的编译和启动那一步也卡了很久, 不明白为什么启动不了server, 后来明白是自己给的设备名字不对, 并不是一个任意名字, 必须是设备名.
启动参数那也费了很多力气, 官方文档是用libvirt, 由于缺乏相关使用经验, 就先自己琢磨的参数, 后来实在有问题, 就用libvirt创建了虚拟机, 并修改了相关文件的配置, 添加成功命令, 并一步步找失败原因.
他们文档说是要让vmxnet3在slot 0, 但是并没有说怎么放到slot 0, 中间还遇到PCI: single function device can't be populated in function 10.1
错误, 后来发现是vmxnet3缺少multifunction参数所致.
qemu官方文档真的太坑了, 信息给的少, 网上也缺乏相关资料, 只能自己琢磨, 唉, 坑啊~
最后呢, 是我发现的3个相关bug: cve-2021-3582, cve-2021-3607, cve-2021-3608, 虽然其中两个都有可能导致虚拟化逃逸, 但是现在没有任何厂商在用这个设备, 所以也就没什么实质性的危害.