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