[译 10] vDPA 实操

渗透技巧 1年前 (2023) admin
676 0 0

0. 目录

1. 垫话
2. 前言
3. 环境搭建
3.1 要求
3.2 创建 VM
3.3 准备 host
3.4 准备 guest
4. 创建 accelerated data path
4.1 构建 vDPA 应用
4.2 启动 vDPA 应用
4.3 启动用户应用
5. 结论

1. 垫话

本文乃对 Red Hat 《vDPA hands on: The proof is in the pudding》一文的翻译(系列文章第 10 篇)。

2. 前言

本文我们通过 DPDK 框架建立 vDPA。因为兼容 vDPA 的硬件网卡尚未大规模普及,我们通过在 guest 中使用半虚拟化的 virtio-net 设备(假装成一个全 virtio 硬件 offload NIC)来绕过硬件上的限制。
[译 10] vDPA 实操
图 1

3. 环境搭建

对 DPDK 的应用而非配置、安装感兴趣的用户,我们在 Github 仓库中准备了开箱即用的脚本,可以自动化搞定一切。我们从最基本的配置开始。

3.1 要求

  • 一台运行 linux 发行版的计算机。本手册基于 CentOS7,但文中所涉及命令在其他 linux 发行版上应该不会有太大的变化,尤其是 Red Hat Enterprise Linux 7。
  • 一个有 sudo 权限的账户。
  • home 目录下至少有 25G 可用空间。
  • 至少 8G 的 RAM。
安装依赖的包:
sudo yum install qemu-kvm libvirt-daemon-qemu libvirt-daemon-kvm libvirt virt-install libguestfs-tools-c kernel-tools dpdk dpdk-tools

3.2 创建 VM

首先,下载最新的 CentOS-Cloud-Base 镜像:
user@host $ sudo wget -O /var/lib/libvirt/images/CentOS-7-x86_64-GenericCloud.qcow2 http://cloud.centos.org/centos/7/images/CentOS-7-x86_64-GenericCloud.qcow2
上面命令中的 URL 可能会发生变更,需要的话自行去 wiki.centos.org/Download 中获取。
这里下载一个已经安装好了的、可以在 OpenStack 环境下运行的 CentOS 7。由于我们并没有运行 OpenStack,因此需要清理一下镜像。首先,为此镜像做一个备份:
user@host $ sudo qemu-img create -f qcow2 -b  /var/lib/libvirt/images/CentOS-7-x86_64-GenericCloud.qcow2   /var/lib/libvirt/images//vhuser-test1.qcow2 20G
添加如下环境变量,这样后续的 libvirt 命令可以在非特权级用户下执行(推荐的做法):
user@host $ export LIBVIRT_DEFAULT_URI="qemu:///system"
执行清理命令(注意将密码改成你自己的):
user@host $ sudo virt-sysprep --root-password password:changeme --uninstall cloud-init --selinux-relabel -a /var/lib/libvirt/images/vhuser-test1.qcow2 --network --install “dpdk,dpdk-tools,pciutils”
此命令会挂载文件系统,并自动做一些基本的配置,这样镜像就可以重新 boot 了。
还需要一个网络来连接我们的 VM。libvirt 处理网络的方法与它管理 VM 类似,你可以通过 XML 定义一个网络,并通过命令行来启动或停止它。
在本文例子中,我们将使用一个叫“default”的网络,方便起见我们将其定义在 libvirt 中。下面的命令定义“default”网络,启动它并检查它的运行状态。
user@host $ virsh net-define /usr/share/libvirt/networks/default.xmlNetwork default defined from /usr/share/libvirt/networks/default.xmluser@host $ virsh net-start defaultNetwork default starteduser@host $virsh net-list Name      State    Autostart   Persistent-------------------------------------------- default   active   no          yes
最后,我们通过 virt-install 来创建 VM。此命令行工具为很多常见的 OS 创建所需的定义。如下命令做了基本定义,后续可以基于此做定制:
user@host $ virt-install --import --name vdpa-test1 --ram=5120 --vcpus=3 --nographics --accelerate --machine q35        --network network:default,model=virtio --mac 02:ca:fe:fa:ce:aa       --debug --wait 0 --console pty       --disk /var/lib/libvirt/images/vdpa-test1.qcow2,bus=virtio --os-variant centos7.0
此命令的选项指定了 vCPU 的数量,VM 的 RAM 大小,磁盘路径,以及我们希望该 VM 连接到的网络。
你可能已经注意到,与 vhost-user 实操中不同,machine 类型是 “q35” 而不是 “pc”。因为此类型才能支持虚拟 IOMMU。
除了根据我们指定的选项定义了 VM 之外,virt-install 命令应该还为我们启动了 VM,我们可以列出它:
user@host $ virsh list Id   Name           State------------------------------ 1    vdpa-test1   running
我们的 VM 跑起来了!
由于后续要对其定义做修改,所以先关机:
user@host $ virsh shutdown vdpa-test1

3.3 准备 host

DPDK 可以有效地分配和管理内存 buffers。linux 上需要大页支持,这需要在你的 kernel 上开启此特性。使用比常规 4K 更大的页可以减少 TLB 查找从而提升性能(TLB 查找用于将虚拟地址翻译成物理地址)。要让内核在启动阶段分配大页,将如下内核参数添加到 bootloader 配置中:
user@host $ sudo grubby --args=“default_hugepagesz=1G hugepagesz=1G hugepages=6” --update-kernel /boot/<your kernel image file>
具体说一下每个参数是干啥的:
  • default_hugepagesz=1G:指定默认创建 1G 大页。
  • hugepagesz=1G:指定启动阶段创建 1G 大页。
  • hugepagesz=6:初始时创建 6 个大页(每个 1G)。该信息启动后可通过 /proc/meminfo 查看。
重启后通过如下命令查看内核参数是否修改生效:
user@host $ cat /proc/cmdline

3.4 准备 guest

virt-install 命令通过 libvirt 创建并启动了一个 VM。要想让 QEMU 连接基于 DPDK 的 vswitch testpmd,需要将 vhost-user 接口(backed 为 Unix sockets)定义以及虚拟 IOMMU 添加到 VM XML 的设备描述中:
user@host $ virsh edit vdpa-test1
我们添加所有必要设备,以让 virtio-net 设备可以在 IOMMU 的支持下工作。
首先,创建一个专用于 virtio-net 设备(用作 vDPA 设备)的 PCIe root port,如此该 virtio-net 设备方能有其自己的 IOMMU 组。否则,它将与同总线上的其他设备共享,从而让它无法被在用户空间中使用。
<controller type='pci' index='8' model='pcie-root-port'>   <model name='pcie-root-port'/>   <target chassis='8' port='0xe'/>   <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0'/></controller>
然后,我们再添加一个使用 vhost-user 后端的 virtio-net 设备,并将其插入到我们刚刚创建的 PCIe root port(Bus 8)。同时,需要使能 IOMMU 及 ATS(Address Translation Service) 支持:
<interface type='vhostuser'>   <mac address='52:54:00:58:d7:01'/>   <source type='unix' path='/tmp/vhost-user1' mode='client'/>   <model type='virtio'/>   <driver name='vhost' rx_queue_size='256' iommu='on' ats='on'/>   <address type='pci' domain='0x0000' bus='0x08' slot='0x00' function='0x0'/></interface>

最后,添加 IOMMU 设备,并使能中断重映射及 IOTLB 支持:

<iommu model='intel'>   <driver intremap='on' iotlb='on'/></iommu>
要让使能 IOMMU 的 vhost-user 工作,还需要对 XML 进行一些修改。
首先,如 vhost-user 实操,我们让 guest 使用大页,向 guest 添加如下定义:
<memoryBacking>    <hugepages>      <page size='1048576' unit='KiB' nodeset='0'/>    </hugepages>    <locked/>  </memoryBacking>   <numatune>    <memory mode='strict' nodeset='0'/>  </numatune>

为了让 vhost-user 后端可以访问内存,我们需要在 guest 配置中添加额外设置。这很重要,没有它我们将看不到任何被传输的 packets:

<cpu mode='host-passthrough' check='none'>     <topology sockets='1' cores='3' threads='1'/>     <numa>     <cell id='0' cpus='0-2' memory=’5242880’ unit='KiB' memAccess='shared'/>     </numa></cpu>
    最后,我们需要让 qemu 接管 I/O APIC,以便中断重映射可以工作:
<features>      <acpi/>      <apic/>      <ioapic driver='qemu'/></features>
现在启动 guest。因为我们配置了 QEMU 连接 vhost-user 的 UNIX socket,所以要确保 guest 启动时 socket 都已建好。启动 testpmd,它会帮我们建好 socket:
user@host $ sudo testpmd -l 0,2 --socket-mem=1024 -n 4     --vdev 'net_vhost0,iface=/tmp/vhost-user1,iommu-support=1'  --     --portmask=1 -i --rxq=1 --txq=1     --nb-cores=1 --forward-mode=io
由于要连接到 vhost-user unix sockets 上,因而要以 root 运行 QEMU。可以在 /etc/libvirt/qemu.conf 中添加 user=root 配置,此操作仅限本文案例,并不推荐,读者应该在搞完本文试验后恢复原来的设置(简单一点可以将 user=root 注释掉即可)。
现在可以启动 VM 了:
user@host $ virsh start vdpa-test1
以 root 登录,我们要做的第一件事,是向 guest 内核命令行中添加一些参数,以在 boot 阶段保留一些大页,并使能内核对 pass-through 模式 IOMMU 的支持:
root@guest $ sudo grubby --args=“default_hugepagesz=1G hugepagesz=1G hugepages=2 intel_iommu=on iommu=pt” --update-kernel /boot/<your kernel image file>

如此,新的内核参数已设置好,重启 guest。

启动后,将 virtio 设备绑定到 vfio-pci 驱动。先加载所需的内核模块:
root@guest $ modprobe vfio-pci

找到 virtio-net 设备的 PCI 地址:

root@guest $ dpdk-devbind --status netNetwork devices using kernel driver===================================0000:01:00.0 'Virtio network device 1041' if=eth0 drv=virtio-pci unused=virtio_pci,vfio-pci *Active*0000:08:00.0 'Virtio network device 1041' if=eth1 drv=virtio-pci unused=virtio_pci,vfio-pci

dpdk-devbind 输出中找到没有标记 “Active” 的项,可以使用它们来做实验。

root@guests $ dpdk-devbind.py -b vfio-pci 0000:08:00.0
至此,guest 可以运行基于 DPDK 的程序了。但在做 vDPA 实验之前,我们先确保在启用 IOMMU 时,我们可以通过 testpmd 利用 virtio PMD 驱动访问到 virtio 设备:
root@guests $ testpmd -l 0,1 --socket-mem 1024 -n 4 -- --portmask=1 -i --auto-start

然后我们可以在 host testpmd 实例中开始数据包转发,在 IO 转发 loop 中注入 packets burst:

HOST testpmd> start tx_first 512

为了检查这一切是否正常工作,我们可以通过重复调用 show port stats all 命令来检查任一 testpmd 实例中的端口统计信息:

HOST testpmd> show port stats all  ######################## NIC statistics for port 0  ########################  RX-packets: 60808544   RX-missed: 0          RX-bytes:  3891746816  RX-errors: 0  RX-nombuf:  0           TX-packets: 60824928   TX-errors: 0          TX-bytes:  3892795392  Throughput (since last show)  Rx-pps:     12027830  Tx-pps:     12027830  ############################################################################

4. 创建 accelerated data path

4.1 构建 vDPA 应用

搞定了 host 及 guest 的配置后,我们正式开始。
如此前文章所提到的,virtio-vDPA DPDK 驱动(使能对半虚化 virtio-net 设备或全 virtio offload 硬件 NIC的使用)在 upstream 的 DPDK mailing list 中尚处于 review 阶段,在官方 upstream release 中还不可用(计划在 v19.11 发布)。
这意味着,我们只能基于这些驱动 patch 来构建 DPDK,而不是使用 DPDK downstream 包:
首先,安装构建 DPDK 的所有依赖:
root@guests $ yum install git gcc numactl-devel kernel-devel

然后,构建 DPDK 库:

root@guests $ git clone https://gitlab.com/mcoquelin/dpdk-next-virtio.git dpdkroot@guests $ cd dpdkroot@guests $ git checkout remotes/origin/virtio_vdpa_v1root@guests $ export RTE_SDK=`pwd`root@guests $ export RTE_TARGET=x86_64-native-linuxapp-gccroot@guests $ make -j2 install T=$RTE_TARGET DESTDIR=install

最后,我们构建 DPDK 目录中的 vDPA 示例程序。此应用将 probe vDPA 驱动,并通过 vDPA 框架将 vDPA 设备绑定到一个 vhost-user socket:

root@guests $ cd examples/vdparoot@guests $ make

4.2 启动 vDPA 应用

将 virtio-net 设备绑定到 vfio-pci 驱动上:
root@guests $ dpdk-devbind.py -b vfio-pci 0000:08:00.0
现在可以启动 vDPA 应用,指定其将可用的 vDPA 设备绑定到前缀为 /tmp/vdpa 的 vhost-user socket。
为使用 virtio-vDPA 驱动(而不是 virtio PMD)来 probe virtio-net 设备,我们必须将 “vdpa=1” 设备参数传递给 EAL 命令行中的白名单设备。
由于本例中只有一个 vDPA 设备,应用将在 /tmp/vdpa0 中创建一个 vhost-user socket:
root@guests $ ./build/vdpa -l 0,2 --socket-mem 1024 -w 0000:08:00.0,vdpa=1 -- --iface /tmp/vdpaEAL: Detected 3 lcore(s)EAL: Detected 1 NUMA nodesEAL: Multi-process socket /var/run/dpdk/rte/mp_socketEAL: Selected IOVA mode 'VA'EAL: Probing VFIO support...EAL: VFIO support initializedEAL: WARNING: cpu flags constant_tsc=yes nonstop_tsc=no -> using unreliable clock cycles !EAL: PCI device 0000:08:00.0 on NUMA socket -1EAL:   Invalid NUMA socket, default to 0EAL:   probe driver: 1af4:1041 net_virtioEAL: PCI device 0000:08:00.0 on NUMA socket 0EAL:   probe driver: 1af4:1041 net_virtio_vdpaEAL:   using IOMMU type 1 (Type 1)iface /tmp/vdpaVHOST_CONFIG: vhost-user server: socket created, fd: 27VHOST_CONFIG: bind to /tmp/vdpa0enter 'q' to quit

4.3 启动用户应用

至此,绑定到 vDPA 设备的 vhost-user socket 可以使用了,我们可以启动使用 accelerated virtio datapath 的应用。
在现实情况下,可以将此 socket 传递给 VM,以便其 virtio 数据面 bypass host,但我们已经在 guest 中,这意味着需要建立起嵌套虚拟化。
其他可能的情况是将 vhost-user socket 传递给容器,以便其可以享受到高速接口,但关于这部分内容我们放到后面的文章。
简单起见,我们将只启动一个 testpmd 应用,它将使用 virtio-user PMD 连接到 vhost-user socket。virtio-user PMD 复用 virtio PMD 的数据面,但它不是通过 PCI 来管理控制面,而是实现了 vhost-user 协议的 master 侧。
由于我们已经有一个正在运行的 DPDK 应用,因此必须注意为它们所分配大页添加前缀。通过如下命令完成:
root@guests $ ./install/bin/testpmd -l 0,2 --socket-mem 1024 --file-prefix=virtio-user --no-pci --vdev=net_virtio_user0,path=/tmp/vdpa0
至此,完成了 accelerated data path 的建立!为了验证是否一切工作正常,我们注入一些 packet:
HOST testpmd> start tx_first 512
HOST testpmd> show port stats all ######################## NIC statistics for port 0 ######################## RX-packets: 3565442688 RX-missed: 0 RX-bytes: 228188332032 RX-errors: 0 RX-nombuf: 0 TX-packets: 3565459040 TX-errors: 0 TX-bytes: 228189378560 Throughput (since last show) Rx-pps: 11997065 Tx-pps: 11997065 ############################################################################

5. 结论

本文我们通过 DPDK 的 vDPA 框架,创建了一个标准 accelerated data path。在一开始直接使用 virtio PMD 的 testpmd 运行,到建立 vDPA 后的 testpmd 运行,我们引入了 vDPA 层以提供额外的灵活性而不对性能产生影响。
该框架的价值在于,你现在就可以对 vDPA 控制面做实验,而无需你的 vendor NIC 支持业界目前正在开发中的 vDPA 数据面(硬件对支持 ring 布局的支持)。
我们现在将 VM 领域转移到容器领域。下一篇文章将基于此框架构建,以在本地和混合云部署中实验 vDPA 加速容器。

原文始发于微信公众号(窗有老梅):[译 10] vDPA 实操

版权声明:admin 发表于 2023年2月2日 上午8:58。
转载请注明:[译 10] vDPA 实操 | CTF导航

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
暂无评论...