DPDK 在 CentOS 7 上的编译使用记录
 
0x00 准备 1. BIOS 设置 如果 UEFI secure boot 被启用了, Linux 内核可能会禁止系统使用 UIO, 请关闭相应设置或者使用 vfio-pci 内核模块, 详见 .
2. 系统环境 
Linux kernel >= 3.10 
Linux kernel headers and modules(etc. kernel-devel) 
gcc 4.9+ / clang 3.4+ 
Python 2.7+ / 3.5+ 
Meson 0.47.1+ 
ninjia 
Library for handling NUMA(Non Uniform Memory Access)
numactl-devel 
libnuma-dev 
 
 
 
1 2 #  centos 7 yum install -y kernel-devel gcc python make numactl-devel libpcap-devel pciutils 
 
2.1 查看和升级 Linux 内核(CentOS 7) DPDK 对 Linux 内核有要求, CentOS 默认的内核版本为 3.10, 低版本 DPDK 无法使用.
查看当前内核版本:
1 2 3 cat /boot/grub2/grub.cfg | grep menuentry  #  或者 uname -r 
 
推荐使用 elrepo 编译好的 CentOS 内核.
安装 elrepo:
1 2 3 4 rpm -Uvh https://www.elrepo.org/elrepo-release-7.0-3.el7.elrepo.noarch.rpm #  可以同时设置好镜像加速 推荐使用清华源 #  baseurl=https://mirrors.tuna.tsinghua.edu.cn/elrepo/elrepo/el7/$basearch / #  baseurl=https://mirrors.tuna.tsinghua.edu.cn/elrepo/kernel/el7/$basearch / 
 
避免冲突,卸载旧版的一些包
1 yum remove -y kernel-headers kernel-tools kernel-tools-libs 
 
安装lt版内核:
1 yum --enablerepo=elrepo-kernel install kernel-lt kernel-lt-devel kernel-lt-headers 
 
查看当前可以使用的内核启动项:
1 2 3 4 #  bios awk -F\' '$1=="menuentry " {print i++ " : " $2}' /etc/grub2.cfg #  uefi awk -F\' '$1=="menuentry " {print i++ " : " $2}' /boot//efi/EFI/centos/grub.cfg 
 
设置默认启动内核:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 grub2-set-default 0 #  或者 grub2-set-default "CentOS Linux (xxxx) 7 (Core)" #  备份 #  如果是 BIOS cp /boot/grub2/grub.cfg /boot/grub2/grub.cfg.bak #  如果是 UEFI cp /boot/efi/EFI/centos/grub.cfg /boot/efi/EFI/centosgrub.cfg.bak #  重新生成配置 BIOS grub2-mkconfig -o /boot/grub2/grub.cfg #  重新生成配置 UEFI grub2-mkconfig -o /boot/efi/EFI/centos/grub.cfg 
 
验证:
1 2 3 grub2-editenv list #  或者 uname -r 
 
2.2 支持的网卡设备 参考官网设备列表 
0x01 编译 1. 下载 DPDK 源文件 从官网 下载源文件,解压。本文基于 19.11。
lib: DPDK 库文件 
drivers: DPDK 轮询驱动源文件 
app: DPDK 应用程序 (自动测试)源文件 
examples: DPDK 应用例程 
config, buildtools, mk: 框架相关的 makefile、脚本及配置文件 
 
2. 修改源文件(非必须) 可能可以提高性能.
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 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 void  ixgbe_enable_relaxed_ordering_gen2 (struct  ixgbe_hw *hw) {     u32 regval;     u32 i;         DEBUGFUNC("ixgbe_enable_relaxed_ordering_gen2" );              for  (i = 0 ; i < hw->mac.max_tx_queues; i++) {         regval = IXGBE_READ_REG(hw, IXGBE_DCA_TXCTRL_82599(i)); #if  0         regval |= IXGBE_DCA_TXCTRL_DESC_WRO_EN; #else             regval |= IXGBE_DCA_TXCTRL_DESC_WRO_EN |                   IXGBE_DCA_TXCTRL_DESC_RRO_EN |                   IXGBE_DCA_TXCTRL_DATA_RRO_EN;         IXGBE_WRITE_REG(hw, IXGBE_DCA_TXCTRL_82599(i), regval); #endif      }         for  (i = 0 ; i < hw->mac.max_rx_queues; i++) {         regval = IXGBE_READ_REG(hw, IXGBE_DCA_RXCTRL(i)); #if  0         regval |= IXGBE_DCA_RXCTRL_DATA_WRO_EN |               IXGBE_DCA_RXCTRL_HEAD_WRO_EN; #else             regval |= IXGBE_DCA_RXCTRL_DATA_WRO_EN |                   IXGBE_DCA_RXCTRL_HEAD_WRO_EN |                   IXGBE_DCA_TXCTRL_DESC_RRO_EN;         IXGBE_WRITE_REG(hw, IXGBE_DCA_RXCTRL(i), regval); #endif      }     } s32 ixgbe_start_hw_gen2 (struct  ixgbe_hw *hw)   {     u32 i;     u32 regval;            for  (i = 0 ; i < hw->mac.max_tx_queues; i++) {         IXGBE_WRITE_REG(hw, IXGBE_RTTDQSEL, i);         IXGBE_WRITE_REG(hw, IXGBE_RTTBCNRC, 0 );     }     IXGBE_WRITE_FLUSH(hw);   #if  0          for  (i = 0 ; i < hw->mac.max_tx_queues; i++) {         regval = IXGBE_READ_REG(hw, IXGBE_DCA_TXCTRL_82599(i));         regval &= ~IXGBE_DCA_TXCTRL_DESC_WRO_EN;         IXGBE_WRITE_REG(hw, IXGBE_DCA_TXCTRL_82599(i), regval);     }       for  (i = 0 ; i < hw->mac.max_rx_queues; i++) {         regval = IXGBE_READ_REG(hw, IXGBE_DCA_RXCTRL(i));         regval &= ~(IXGBE_DCA_RXCTRL_DATA_WRO_EN |                 IXGBE_DCA_RXCTRL_HEAD_WRO_EN);         IXGBE_WRITE_REG(hw, IXGBE_DCA_RXCTRL(i), regval);     } #else      ixgbe_enable_relaxed_ordering(hw); #endif           return  IXGBE_SUCCESS; }                                                                 
 
2. 配置与编译 解压并进入 DPDK 源码根目录
 
设置环境变量
1 2 #  设置 DPDK SDK 所在目录, 设置编译输出目录 export RTE_SDK=$(pwd) && export RTE_TARGET=build 
 
2.1 手动配置 DPDK目标文件的格式为:
ARCH-MACHINE-EXECENV-TOOLCHAIN
 
其中:
ARCH 可以是: i686, x86_64, ppc_64, arm64 
MACHINE 可以是: native, power8, armv8a 
EXECENV 可以是: linux, freebsd 
TOOLCHAIN 可以是: gcc, icc, clang 
 
设置好编译参数并编译 DPDK 库
1 2 3 4 5 6 7 #  直接编译 make install T=x86_64-native-linux-gcc [DESTDIR=/usr/local/dpdk] -j 16 #  设置编译器和环境再进行编译 make config T=x86_64-native-linux-gcc; make -j 16 #  make config T=x86_64-native-linuxapp-gcc; make -j 16 #  make config T=x86_64-native-linux-clang; make -j 16 
 
2.2 使用 meson 和 ninjia 通过 meson 和 ninjia 配置 DPDK 编译环境:
1 2 3 4 5 meson <options> build cd build ninja ninja install ldconfig 
 
设置好编译参数并编译 DPDK 库
1 2 #  直接编译 make install T=x86_64-native-linux-gcc [DESTDIR=/usr/local/dpdk] -j 16 
 
2.3 其他选项 编译成动态库:
1 make CONFIG_RTE_BUILD_SHARED_LIB=y ... 
 
20.02开始默认不编译 igb_uio, 如有需要:
1 make CONFIG_RTE_EAL_IGB_UIO=y ... 
 
添加 debug 选项:
1 make EXTRA_CFLAGS="-O0 -g" 
 
2.4 使用脚本编译和配置 为了能清晰的进行编译和配置, 第一次使用时推荐使用 usertools 下的 dpdk-setup.sh 进行编译和配置:
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 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 cd usertools; ./dpdk-setup.sh ============================ ------------------------------------------------------------------------------  RTE_SDK exported as /root/dpdk-stable-18.11.2 ------------------------------------------------------------------------------ ----------------------------------------------------------  Step 1: Select the DPDK environment to build ---------------------------------------------------------- [1] arm64-armv8a-linuxapp-clang [2] arm64-armv8a-linuxapp-gcc [3] arm64-dpaa2-linuxapp-gcc [4] arm64-dpaa-linuxapp-gcc [5] arm64-stingray-linuxapp-gcc [6] arm64-thunderx-linuxapp-gcc [7] arm64-xgene1-linuxapp-gcc [8] arm-armv7a-linuxapp-gcc [9] i686-native-linuxapp-gcc [10] i686-native-linuxapp-icc [11] ppc_64-power8-linuxapp-gcc [12] x86_64-native-bsdapp-clang [13] x86_64-native-bsdapp-gcc [14] x86_64-native-linuxapp-clang [15] x86_64-native-linuxapp-gcc [16] x86_64-native-linuxapp-icc [17] x86_x32-native-linuxapp-gcc ----------------------------------------------------------  Step 2: Setup linuxapp environment ---------------------------------------------------------- [18] Insert IGB UIO module [19] Insert VFIO module [20] Insert KNI module [21] Setup hugepage mappings for non-NUMA systems [22] Setup hugepage mappings for NUMA systems [23] Display current Ethernet/Crypto device settings [24] Bind Ethernet/Crypto device to IGB UIO module [25] Bind Ethernet/Crypto device to VFIO module [26] Setup VFIO permissions ----------------------------------------------------------  Step 3: Run test application for linuxapp environment ---------------------------------------------------------- [27] Run test application ($RTE_TARGET/app/test) [28] Run testpmd application in interactive mode ($RTE_TARGET/app/testpmd) ----------------------------------------------------------  Step 4: Other tools ---------------------------------------------------------- [29] List hugepage info from /proc/meminfo ----------------------------------------------------------  Step 5: Uninstall and system cleanup ---------------------------------------------------------- [30] Unbind devices from IGB UIO or VFIO driver [31] Remove IGB UIO module [32] Remove VFIO module [33] Remove KNI module [34] Remove hugepage mappings [35] Exit Script Option:  
 
按需进行编译, 如在 x64 的 Linux 下使用 gcc 进行编译则选择15, 要和环境变量的一样.
 
挂载 uio 内核模块, 可选择 igb_uio 或者 vfio. 推荐使用 vfio, 需要开启 iommu.
 
配置大页内存, 至少 512, 按照推荐的 64 会报错.
 
将网卡挂载到 igb_uio / vfio 上.
 
 
0x02 使用 DPDK 1. 加载驱动 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 #  加载 igb_uio #  查看系统是否加载 uio 模块 lsmod | grep uio #  如果没有加载需要手动挂载该模块 modprobe uio #  把编译好的 igb_uio 加载进内核 insmod /path/to/dpdk/build/kmod/igb_uio.ko #  加载 kni insmod /path/to/dpdk/build/kmod/rte_kni.ko #  加载 vfio modprobe vfio-pci #  卸载驱动 rmmod igb_uio rmmod vfio-pci rmmod vfio 
 
  20.02 开始默认使用的是 uio_pci_generic, 它不支持 virtual function, 因此会和 sriov 产生一定 的冲突, 需要换回 igb_uio / vfio, 而且 uio_pci_generic 使用的时候需要把 intel_iommu / amd_iommu 关闭.
 
2. 绑定网卡驱动 1 2 3 4 5 6 7 8 9 10 11 12 #  首先保证网卡非活动 ip link set ethx down #  查看当前网卡信息 ./usertools/dpdk-devbind.py --status #  使用dpdk脚本进行绑定 ./usertools/dpdk-devbind.py --bind=igb_uio 00:09.0 ... ./usertools/dpdk-devbind.py --bind=igb_uio eth1 ... #  还原 ./usertools/dpdk-devbind.py --bind=ixgbe 00:09.0 ... 
 
3. 设置大页内存 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #  查看numa支持 支持的机器会显示 node, 需要安装 numactl numastat #  让系统自动去分配大页内存 echo 2048 > /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages #  对于有 numa 的机器, 假设有两个 node #  清空大页内存只需要把数字改 0 即可 echo 1024 > /sys/devices/system/node/node0/hugepages/hugepages-2048kB/nr_hugepages echo 1024 > /sys/devices/system/node/node1/hugepages/hugepages-2048kB/nr_hugepages #  挂载大页内存 mkdir /mnt/huge mount -t hugetlbfs nodev /mnt/huge #  mount -t hugetlbfs none /mnt/huge -o pagesize=2MB #  mount -t hugetlbfs none /mnt/huge -o pagesize=1GB #  可以把大页内存加入 /etc/fstab 中 echo "nodev /mnt/huge hugetlbfs defaults 0 0" >> /etc/fstab #  如果要使用 1GB 的页 echo "nodev /mnt/huge_1GB hugetlbfs pagesize=1GB 0 0" >> /etc/fstab 
 
4. 设置库路径 如果使用了动态库进行编译, 需要设置动态库环境查找环境:
1 2 echo "/path/to/dpdk/lib" >> /etc/ld.conf.d/dpdk.conf ldconfig 
 
5. 使用 example 进入 example 目录编译
1 2 #  之前已经设置环境变量 RTE_SDK, make 的时候依赖这个环境变量 cd example && make 
 
使用 helloworld 进行测试
1 2 ./helloworld/x86_64-native-linuxapp-gcc/helloworld -c f #  -c 指 coremask 十六进制的数代表使用几号 cpu 核心, f 代表 1111 指使用 0-3 号共 4 颗 cpu. 
 
如果出现 EAL:   Invalid NUMA socket, default to 0, 可以修改对应网卡的 numa 设置:
1 vim /sys/bus/pci/devices/[device_pci_id]/numa_node 
 
0xff 其他 1. EAL 部分参数说明 
-c 以十六进制形式指定需要运行的核,转成二进制后,对应的位数为1表示运行在相应的核上。
  -c 0x5 即二进制 0101 表示运行在0和2号cpu上
 
 
-l 指定运行在哪几个核上
  -l 0-2,4 即运行在id为0,1,2,4的cpu上
 
 
--lcores 设置lcore集运行在指定的cpu集上,格式如下
1 --lcores='<lcore_set>[@cpu_set][,<lcore_set>[@cpu_set],...]' 
 
如果没有指定cpu集,那么cpu号和lcore号一样
  --lcores=’1,2@(5-7),(3-5)@(0,2),(0,6),7-8’
  这会开启9个EAL的线程
  lcore 0 在(0,6)中被设置,因此会运行在id为0和id为6的cpu上(0x41)
  lcore 1 在1中被设置,因此会运行在id为1的cpu上(0x2)
  lcore 2 在2@(5-7)中被设置,因此会运行在id为5到id为7的cpu上(0xe0)
  lcore 3-5 在(3-5)@(0,2)中被设置,因此会运行在id为0和id为2的cpu上(0x5)
  lcore 6 在(0,6)中被设置,因此会运行在id为0和id为6的cpu上(0x41)
  lcore 7 在7-8中被设置,因此会运行在id为7的cpu上(0x80)
  lcore 8 在7-8中被设置,因此会运行在id为8的cpu上(0x100)
 
 
-n 内存通道数
 
 
2. virtio_user 接口使用 启用 virtio_user 接口代替 kni 与内核通信, 添加参数:
1 --vdev=virtio_user<num>,path=/dev/vhost-net<,mac=xx:xx:xx:xx:xx:xx> 
 
virtio 的 mac 需要和业务口的 mac 一样, 否则无法与外界通信.
3. 单机启动多 DPDK 应用 需要进行数据隔离, 启动时添加参数:
1 --file-prefix=<container_prefix_name> 
 
4. 网卡的手动内核绑定 网卡的手动内核绑定
需要使用支持多队列的网卡, 可以用以下命令查看当前网卡是否支持:
1 2 3 4 ➜ ~ cat /proc/interrupts | grep enp4s0  40:         10          0          0          0          0          0  IR-PCI-MSI-edge      enp4s0-TxRx-0  41:          8          0          0          0          0          0  IR-PCI-MSI-edge      enp4s0-TxRx-1  42:         24          0          0          0          0          0  IR-PCI-MSI-edge      enp4s0 
 
最前面的是中断号, 使用以下命令查看绑定状态:
1 2 3 4 5 6 7 8 #  查看当前绑定状态 绑定在 ➜  ~ cat /proc/irq/40/smp_affinity 10 #  绑定到 3 号 cpu 上(按照 0, 1, 2, 4, 8, 10, 11, 12, 14, 18 ...) ➜  ~ echo 4 > /proc/irq/40/smp_affinity       ➜  ~ cat /proc/irq/40/smp_affinity      04