DPDK编译及使用

  软件(虚拟)交换机性能低下的主要原因是它们未经优化或设计用于处理和交换过高速率的数据包,而DPDK(Data Plane Development Kit, 数据平面开发工具包)则专门解决这个问题。在解释DPDK如何改善这种情况之前,需要回顾常规虚拟交换机存在的局限性。犹豫虚拟交换机对高速数据包的处理缺乏优化,因而导致数据包处理的过程中的很多步骤都要用到CPU,由于CPU需要处理多任务,因而其可用性(特别是超载的情况下)会出现性能瓶颈的问题。此外,虚拟交换机也无法高效实用系统内存,它们首先将数据包复制到内存缓冲区,然后中断客户端CPU并将数据包复制到客户端内存,最后再从vNIC将数据读取给应用程序。单纯的内存读写所产生的内存分配,去分配以及CPU中断等处理操作都会降低虚拟交换机的性能。
  开发DPK的主要目的是为软件处理数据包提供一种优化方式,DPDK是由Intel公司创建的一组库函数和NIC驱动程序。2013年,Intel将DPDK作为开源开发工具包提供给开发者社区,允许开发人员在软件交换机以及利用DPDK提供的微调能力的类似应用程序中使用这些库函数。虽然DPDK对任何希望使用它的软件来说都一视同仁,但是在OVS中的应用较为突出。
  DPDK用自己的库函数替换了Linux内核的内置数据平面,DPDK的轻量级库函数采用了非常有效的内存处理机制,即利用环形缓冲区在物理网卡与使用了DPDK的应用程序(如OVS)之间来回传送数据包,从而提高了系统整体性能。为了减少数据包读取所需的CPU中断数,DPDK采用周期性轮询机制,由系统内核定期轮询新数据包。如果数据包速率降至非常低数值,那么就可以切换到中断模式而不是定期轮询模式。通过有效的缓存管理、优化的最少CPU中断数以及其他增强型功能,证实DPDK能够让OVS实现接近原生性能。但是,DPDK的功能特性较少,没有自己的网络协议栈,主要功能就是完成数据包的处理和转发,DPDK与实现网络功能的应用程序结合(如OVS-DPDK),就能提供丰富的功能特性和良好的转发性能。
                ----《网络虚拟化技术详解 NFV与SDN》

  因本文持续更新,最新状态可参看本文源站链接

1. 安装DPDK依赖环境

  为确保DPDK安装成功,程序正常编译使用,安装前可执行如下命令,安装程序依赖。DPDK18.11建议系统内核版本>=3.2,自19.02建议系统内核版本>=3.16。

  建议参考官方文档说明http://doc.dpdk.org/guides-20.02/linux_gsg/sys_reqs.html
dpdk_dependency

$ yum update
$ yum install libpcap-devel kernel*

2. 下载并安装DPDK

2.1 Git安装

$ git config --global core.autocrlf input
$ git clone -b releases https://github.com/DPDK/dpdk.git
$ git checkout v20.02
# make config T=x86_64-native-linux-gcc && make
./usertools/dpdk-setup.sh

2.2 下载源码压缩包编译

$ wget https://github.com/DPDK/dpdk/archive/v20.02.tar.gz
$ tar zxvf v20.02.tar.gz
$ cd dpdk-20.02
# make config T=x86_64-native-linux-gcc && make
$ ./usertools/dpdk-setup.sh

2.3. dpdk编译后无igb_uio.ko或安装Insert IGB UIO module失败

自v20.02版本以后,DPDK就默认关闭igb_uio模块。若构建它,需要配置文件选项CONFIG_RTE_EAL_IGB_UIO设置为enabled。并且官方已计划将其移到其他项目.

  • 解决办法:

    • 一是配置文件dpdk/config/common_base中开启该配置CONFIG_RTE_EAL_IGB_UIO=y,注意这个文件是全局配置。如果仅修改局部的编译,可以在编译时各自文件夹dpdk/x86_64-native-linux-gcc/.config文件中对应修改该参数。本文采用v20.02版本,系统内核3.10,可以正常编译出igb_uio.ko文件。(调试DPDK可开启参数CONF_RTE_LIBRTE_CRYPTODEV_DEBUG=y)
    $ vi config/common_base
    #修改CONFIG_RTE_EAL_IGB_UIO=y
    #:wq
    • 二是使用dpdk18.11或之前的版本。
    $ git tag
    $ git checkout v18.11

3 DPDK加载绑定

3.1 载入用户态驱动

  启用DPDK用户控件I/O的模块,模块驱动类型就是uio_pci_generic,igb_uio,以及vfio-pci。不同的设备需要不同的内核驱动才能工作正常。因此需要根据使用的设备类型,来加载相应的内核驱动并绑定到网络接口上,可参考官方文档http://doc.dpdk.org/guides-20.05/linux_gsg/linux_drivers.html
  UIO(Userspace I/O)是运行在用户空间的I/O技术,Linux系统中一般的驱动设备都是运行在内核空间,而在用户空间用应用程序调用即可,而UIO则是将驱动的很少一部分运行在内核空间,而在用户空间实现驱动的绝大多数功能!使用UIO可以避免设备的驱动程序需要随着内核的更新而更新的问题。
  三种驱动类型的个人理解:UIO驱动(uio_pci_generic,igb_uio)主要作为轻量级内核模块UIO提供给设备,例如uio_pci_generic,映射设备存储到内核空间并注册中断机制。对于缺少传统中断的设备,例如缺少VF虚拟功能,可以采用igb_uio模块替换uio_pci_generic。如果想要比UIO更健壮更安全的内核模块,可以采用vfio-pci,基于IOMMU的安全保护。

$ su
$ modprobe uio
#本文操作环境采用虚拟机CentOS7.6,所以采用igb_uio,也可采用vfio-pci
$ modprobe igb_uio
$ insmod dpdk/x86_64-native-linux-gcc/kmod/igb_uio.ko
#modprobe uio_pci_generic
#modprobe vfio
#modprobe vfio-pci
#也可以采用如下命令
$ ./usertools/dpdk-setup.sh
[45] Insert IGB UIO module
[46] Insert VFIO module
[47] Insert KNI module
#然后选择安装根据提示,选择加载的内核模块
# 如果出现该报错
## ERROR: Target does not have the DPDK UIO Kernel Module.
#      To fix, please try to rebuild target.
# 需要提前载入UIO基础模块
# modprobe uio

3.2 驱动绑定网卡

  建议使用多网卡,用dpdk接管其他非ssh网卡通道,例如ens34网卡。通过驱动绑定网卡pci端口,-b表示--bind的意思;0000:0b:00.0是当前物理机或虚拟机采用的网卡pci端口号。

#0.查询网卡物理地址
$ lspci |grep Ethernet
#或采用如下命令
$ sudo lshw -class network -businfo
#回显返回02:02.0 Ethernet controller: Intel Corporation 82545EM Gigabit Ethernet Controller (Copper) (rev 01),其中02.02.0为设备物理地址。

$ ifconfig ens34 down
$ ./usertools/dpdk-devbind.py -b igb_uio ens34
#或者
$ ifconfig ens34 down
$ ./usertools/dpdk-devbind.py -b igb_uio 0000:02:01.0
#或者
$ ifconfig ens34 down
$ ./usertools/dpdk-setup
#然后选择[51] Bind Ethernet/Baseband/Crypto device to IGB UIO module

#查看网卡信息:
$ ./usertools/dpdk-devbind.py --status

#如修改回普通网卡模式,则使用如下命令
$ ./usertools/dpdk-devbind.py -b e1000 0000:02:05.0
$ ifconfig ens34 up

3.3 绑定网卡故障排查及开启Intel-vt-x/vt-d

  如果绑定出现问题,通过dmesg|grep -i igb_uio检查错误信息。实验中总是绑定网卡失败错误码-22,表示无效参数。最后证明是驱动不支持网卡类型,或者是DPDK不支持,或虚拟机中需要启动intel-vt-td/vt-d支持。

# 查看虚拟机中是否开启intel-vt-x/vt-d
$ cat /proc/cpuinfo | grep vmx

# 修改系统内核启动参数,重启系统,查看是否开启IOMMU
$ dmesg | grep -e DMAR -e IOMMU
[ 0.000000] DMAR: IOMMU enabled

# 执行如下脚本检查是否支持中断重定向
#!/bin/sh
if [ $(dmesg | grep ecap | wc -l) -eq 0 ]; then
  echo "No interrupt remapping support found"
  exit 1
fi
for i in $(dmesg | grep ecap | awk '{print $NF}'); do
  if [ $(( (0x$i & 0xf) >> 3 )) -ne 1 ]; then
    echo "Interrupt remapping not supported"
    exit 1
  fi
done

# 如果硬件不支持interrupt remapping,需要执行
echo "options vfio_iommu_type1 allow_unsafe_interrupts=1" > /etc/modprobe.d/iommu_unsafe_interrupts.conf

  这里重点是,一定要启动Intel VT-d/VT-x!!!-22错误一般都是Intel VT-d/VT-x没有开启;
  解决方法如下依次列出,读者可以根据情况尝试。

  1. 开机后按F12进入BIOS界面打开VT-x和VT-d

  2. 若虚拟机,点击设置,处理器,并开启VT-x等,若无法打开,需要提高内存分配;有些需要在虚拟机选项上配置,右键单击虚拟机(关机状态)-》设置-》处理器-》虚拟化引擎标签栏中,选中 启动虚拟化。IntVT_amdV

  3. 有些Linix内核版本可能缺省没有使能CONFIG_INTEL_IOMMU

  上述参考来源:

3.4 官方文档部分翻译

For some devices which lack support for legacy interrupts, e.g. virtual function (VF) devices, the igb_uio module may be needed in place of uio_pci_generic.

  对于一些缺少传统中断的设备,例如缺少VF虚拟功能,可以采用igb_uio模块替换uio_pci_generic。

If UEFI secure boot is enabled, the Linux kernel may disallow the use of UIO on the system. Therefore, devices for use by DPDK should be bound to the vfio-pci kernel module rather than igb_uio or uio_pci_generic.

  如果开启UEFI安全启动,Linux内核可能不允许使用UIO。因此使用DPDK的设备应该绑定vfio-pci内核模块,而不是igb_uio或uio_pci_generic。

If the devices used for DPDK are bound to the uio_pci_generic kernel module, please make sure that the IOMMU is disabled or passthrough. One can add intel_iommu=off or amd_iommu=off or intel_iommu=on iommu=ptin GRUB command line on x86_64 systems, or add iommu.passthrough=1 on arm64 system.

  如果设备使用DPDK绑定了uio_pci_generic内核模块,请确认关闭IOMMU或直通IOMMU。可以在x86_64系统的GRUB命令行中添加intel_iommu=off或amd_iommu=off或intel_iommu=on iommu=pthuo或在arm64 system系统上添加iommu.passthrough=1

4. DPDK测试实例的编译运行

4.1 运行实例前置条件

  1. 配置系统巨页HugePages

    
    #分配巨页1024*2M=2G
    $ echo 1024 > /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages
    #该分配方法也可行sysctl -w vm.nr_hugepages=2048
    #查看大页分配数目
    $ cat /proc/meminfo| grep Huge
    ```
  2. 环境变量
      运行DPDK测试实例时,一定要设置环境变量RTE_SDK和RTE_TARGET,用于编译以及索引,无论是采用进入目录直接编译,还是采用usertools/dpdk-setup.sh脚本中的测试实例。

    #这里设置RTE_SDK为dpdk根目录路径
    $ export RTE_SDK=/root/github/dpdk
    #这里设置RTE_TARGET为dpdk构建依赖平台库名称
    $ export RTE_TARGET=x86_64-native-linux-gcc
    #开启调试
    # export EXTRA_CFLAGS="-O0 -g"

4.2 DPDK测试实例$RTE_SDK/example/helloworld

  测试用例位于DPDK根目录的examples目录下。

$ cd examples/helloworld/
$ make
    CC main.o
    LD helloworld
    INSTALL-APP helloworld
    INSTALL-MAP helloworld.map

$ ls build/app
    helloworld helloworld.map

  在此之前,需要满足如下条件:

  • 已经设置好巨页Hugepages
  • 任意的内核模块已被载入
  • 根据实例需求,应用需要的对应网络端口已被绑定到相应内核驱动上

  应用程序会链接DPDK目标环境库——EAL(Environmental Abstraction Layer),它能够提供给所有DPDK应用程序一种通用的功能选项。其中-c/-l是强制的,其他都是可选参数,-l表示内核编号的集合,-n表示每个socket的内存通道数量。读者可参考官方文章http://doc.dpdk.org/guides-20.02/linux_gsg/build_sample_apps.html

cd build
./helloworld -l 0-1 -n 2

4.3 DPDK测试实例$RTE_TARGET/app/test

$ cd /root/github/dpdk/usertools
$ ./dpdk-setup.sh
# 加载驱动并已绑定网卡
# 选择[54] Run test application
# 因测试环境2核,输入内核的16进制掩码,二进制是00000011
bitmask: 0x03
Launching app
EAL: Detected 2 lcore(s)
EAL: Detected 1 NUMA nodes
EAL: Multi-process socket /var/run/dpdk/rte/mp_socket
EAL: Selected IOVA mode 'PA'
EAL: Probing VFIO support...
EAL: PCI device 0000:02:01.0 on NUMA socket -1
EAL:   Invalid NUMA socket, default to 0
EAL:   probe driver: 8086:100f net_e1000_em
EAL: PCI device 0000:02:02.0 on NUMA socket -1
EAL:   Invalid NUMA socket, default to 0
EAL:   probe driver: 8086:100f net_e1000_em
EAL: PCI device 0000:02:06.0 on NUMA socket -1
EAL:   Invalid NUMA socket, default to 0
EAL:   probe driver: 8086:100f net_e1000_em
APP: HPET is not enabled, using TSC as default timer
RTE>>

4.4 虚拟机环境中的错误Input/output error处理

  EAL: Error reading from file descriptor 19: Input/output error
APP: HPET is not enabled, using TSC as default timer
RTE>>EAL: Error reading from file descriptor 22: Input/output error

  如果出现如上问题,是因为虚拟机添加的网卡,dpdk不支持导致的,需要在编译igb_uio驱动代码的时候,修改一行代码,跳过dpdk pci 检查。在dpdk/kernel/linux/igb_uio/igb_uio.c文件中,找到 if (pci_intx_mask_supported(udev->pdev)这行代码(dpdkv20.02),
然后修改为if (pci_intx_mask_supported(udev->pdev)|| 1)
  然后重新编译驱动,并重新加载igb_uio,即可解决该问题。

《DPDK编译及使用》有1个想法

  1. 大牛您好,我在执行helloworld测试程序的时候有报错
    [root@localhost build]# ./helloworld -l 0-1 -n 2
    ./helloworld: error while loading shared libraries: librte_flow_classify.so.1.1: cannot open shared object file: No such file or directory
    但是在dpdk目录下我看这个文件是存在的,请问这种情况应该如何处理呢

萱小落进行回复 取消回复

邮箱地址不会被公开。 必填项已用*标注