欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

用Linux平台学习DPDK,轻松提升你的网络速度

最编程 2024-08-10 11:14:30
...

一、DPDK简介

2008年,DPDK由英特尔公司的网络通信部门提出,主要是针对基于Intel的处理器和网卡开发。正如其全称(Data Plane Development Kit,数据平面开发套件),DPDK提供丰富、完整的框架,让CPU快速实现数据平面应用的数据包处理,高效完成网络转发等工作。

说到这里,有必要解释一下数据平面的概念。在通信框架中,数据传输和连接管理通常被拆分为多个独立的操作,这些操作被称为“平面”,平面包括管理平面、数据平面、控制平面。在传统网络,这些平面都在路由器和交换机的固件中实现,管理平面负责为网络堆栈各个层级和网络系统的其他部分提供管理、监控和配置服务;控制平面决定流量的传输路径;数据平面又称为用户平面,承载用户流量,并负责接口间的数据包转发。

二、系统要求

本章描述了编译DPDK所需的软件包。注意:假如在Intel公司的89xx通信芯片组平台上使用DPDK,请参阅文档 Intel® Communications Chipset 89xx Series Software for Linux Getting Started Guide。

2.1X86上预先设置BIOS

对大多数平台,使用基本DPDK功能无需对BIOS进行特殊设置。然而,对于HPET定时器和电源管理功能,以及为了获得40G网卡上小包处理的高性能,则可能需要更改BIOS设置。可以参阅章节 Enabling Additional Functionality 以获取更为详细的信息。

2.2编译DPDK

工具集:

注意:以下说明在Fedora 18上通过了测试。其他系统所需要的安装命令和软件包可能有所不同。有关其他Linux发行版本和测试版本的详细信息,请参阅DPDK发布说明。

GNU make。

coreutils: cmp, sed, grep, arch 等

gcc: 4.9以上的版本适用于所有的平台。在某些发布版本中,启用了一些特定的编译器标志和链接标志(例如-fstack-protector)。请参阅文档的发布版本和 gcc -dumpspecs.

libc 头文件,通常打包成 gcc-multilib (glibc-devel.i686 / libc6-dev-i386; glibc-devel.x86_64 / libc6-dev 用于Intel 64位架构编译; glibc-devel.ppc64 用于IBM 64位架构编译;)

构建Linux内核模块所需要的头文件和源文件。(kernel - devel.x86_64; kernel - devel.ppc64)

在64位系统上编译32位软件包额外需要的软件为:

  • glibc.i686, libgcc.i686, libstdc++.i686 及 glibc-devel.i686, 适用于Intel的i686/x86_64;
  • glibc.ppc64, libgcc.ppc64, libstdc++.ppc64 及 glibc-devel.ppc64 适用于 IBM ppc_64;
    注意:x86_x32 ABI目前仅在Ubuntu 13.10及以上版本或者Debian最近的发行版本上支持。编译器必须是gcc 4.9+版本。Python, 2.7+ or 3.2+版本, 用于运行DPDK软件包中的各种帮助脚本。

可选工具:

Intel® C++ Compiler (icc). 安装icc可能需要额外的库,请参阅编译器安装目录下的icc安装指南。

IBM® Advance ToolChain for Powerlinux. 这是一组开源开发工具和运行库。允许用户在Linux上使用IBM最新POWER硬件的优势。具体安装请参阅IBM的官方安装文档。

libpcap 头文件和库 (libpcap-devel) ,用于编译和使用基于libcap的轮询模式驱动程序。默认情况下,该驱动程序被禁用,可以通过在构建时修改配置文件 CONFIG_RTE_LIBRTE_PMD_PCAP=y 来开启。

需要使用libarchive 头文件和库来进行某些使用tar获取资源的单元测试。

2.3运行DPDK应用程序

要运行DPDK应用程序,需要在目标机器上进行某些定制。

系统软件

需求:

  • Kernel version >= 2.6.34, 当前内核版本可以通过命令查看:
uname -r

glibc >= 2.7 (方便使用cpuset相关特性), 版本信息通命令 ldd --version 查看。

Kernel configuration, 在 Fedora OS 及其他常见的发行版本中,如 Ubuntu 或 Red Hat Enterprise Linux,供应商提供的配置可以运行大多数的DPDK应用程序。

对于其他内核构件,应为DPDK开启的选项包括:

UIO 支持

HUGETLBFS 支持

PROC_PAGE_MONITOR 支持

如果需要HPET支持,还应开启 HPET and HPET_MMAP 配置选项。有关信息参考 High Precision Event Timer (HPET) Functionality 章节获取更多信息。


在 Linux 环境中使用 Hugepages

用于数据包缓冲区的大型内存池分配需要 Hugepages 支持(如上节所述,必须在运行的内核中开启 HUGETLBFS 选项)。通过使用大页分配,程序需要更少的页面,性能增加,因为较少的TLB减少了将虚拟页面地址翻译成物理页面地址所需的时间。如果没有大页,标准大小4k的页面会导致频繁的TLB miss,性能下降。


预留Hugepages给DPDK使用

大页分配应该在系统引导时或者启动后尽快完成,以避免物理内存碎片化。要在引导时预留大页,需要给Linux内核命令行传递一个参数。对于2MB大小的页面,只需要将hugepages选项传递给内核。如,预留1024个2MB大小的page,使用:

hugepages=1024

对于其他大小的hugepage,例如1G的页,大小必须同时指定。例如,要预留4个1G大小的页面给程序,需要传递以下选项给内核:

default_hugepagesz=1G hugepagesz=1G hugepages=4

注意:CPU支持的hugepage大小可以从Intel架构上的CPU标志位确定。如果存在pse,则支持2Mhugepages,如果page1gb存在,则支持1G的hugepages。在IBM Power架构中,支持的hugepage大小为16MB和16GB。

注意:对于64位程序,如果平台支持,建议使用1GB的hugepages在双插槽NUMA的系统上,在启动时预留的hugepage数目通常在两个插槽之间评分(假设两个插槽上都有足够的内存)。

有关这些和其他内核选项的信息,请参阅Linux源代码目录中/kernel-parameter.txt文件。

特例:对于2MB页面,还可以在系统启动之后再分配,通过向 /sys/devices/ 目录下的nr_hugepages文件写入hugepage数目来实现。对于单节点系统,使用的命令如下(假设需要1024个页):

echo 1024 > /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages

在NUMA设备中,分配应该明确指定在哪个节点上:

echo 1024 > /sys/devices/system/node/node0/hugepages/hugepages-2048kB/nr_hugepages
echo 1024 > /sys/devices/system/node/node1/hugepages/hugepages-2048kB/nr_hugepages

注意:对于1G页面,系统启动之后无法预留页面内存。

DPDK使用Hugepages

一旦预留了hugepage内存,为了使内存可用于DPDK,请执行以下步骤:

mkdir /mnt/huge
mount -t hugetlbfs nodev /mnt/huge

通过将一下命令添加到 /etc/fstab 文件中,安装点可以在重启时永久保存:

nodev /mnt/huge hugetlbfs defaults 0 0

对于1GB内存,页面大小必须在安装选项中指定:

nodev /mnt/huge_1GB hugetlbfs pagesize=1GB 0 0

Linux环境中Xen Domain0支持

现有的内存管理实现是基于Linux内核的hugepage机制。在Xen虚拟机管理程序中,对于DomainU客户端的支持意味着DPDK程序与客户一样正常工作。

但是,Domain0不支持hugepages。为了解决这个限制,添加了一个新的内核模块rte_dom0_mm用于方便内存的分配和映射,通过 IOCTL (分配) 和 MMAP (映射)。

DPDK中使能Xen Dom0模式

默认情况下,DPDK构建时禁止使用Xen Dom0模式。要支持Xen Dom0,CONFIG_RTE_LIBRTE_XEN_DOM0设置应该改为 “y”,编译时弃用该模式。

此外,为了允许接收错误套接字ID,CONFIG_RTE_EAL_ALLOW_INV_SOCKET_ID也必须设置为 “y”。


加载DPDK rte_dom0_mm模块

要在Xen Dom0下运行任何DPDK应用程序,必须使用rsv_memsize选项将rte_dom0_mm 模块加载到运行的内核中。该模块位于DPDK目标目录的kmod子目录中。应该使用insmod命令加载此模块,如下所示:

sudo insmod kmod/rte_dom0_mm.ko rsv_memsize=X

X的值不能大于4096(MB)。

配置内存用于DPDK使用

在加载rte_dom0_mm.ko内核模块之后,用户必须配置DPDK使用的内存大小。这也是通过将内存大小写入到目录 /sys/devices/ 下的文件memsize中来实现的。使用以下命令(假设需要2048MB):

echo 2048 > /sys/kernel/mm/dom0-mm/memsize-mB/memsize

用户还可以使用下面命令检查已经使用了多少内存:

cat /sys/kernel/mm/dom0-mm/memsize-mB/memsize_rsvd

Xen Domain0不支持NUMA配置,因此 --socket-mem 命令选项对Xen Domain0无效。

注意:memsize的值不能大于rsv_memsize。

在Xen Domain0上运行DPDK程序

要在Xen Domain0上运行DPDK程序,需要一个额外的命令行选项 --xen-dom0。

还不熟悉的朋友,这里可以先领取一份dpdk新手学习资料包(入坑不亏):

网络异常,图片无法展示
|

三、使用源码编译DPDK目标文件

注意:这个过程的部分工作可以通过章节“使用脚本快速构建”描述的脚本来实现。

3.1安装DPDK及源码

首先,解压文件并进入到DPDK源文件根目录下:

tar xJf dpdk-<version>.tar.xz
cd dpdk-<version>

DPDK源文件由几个目录组成:

  • lib: DPDK 库文件
  • drivers: DPDK 轮询驱动源文件
  • app: DPDK 应用程序 (自动测试)源文件
  • examples: DPDK 应用例程
  • config, buildtools, mk: 框架相关的makefile、脚本及配置文件

3.2DPDK目标环境安装

DPDK目标文件的格式为:

ARCH-MACHINE-EXECENV-TOOLCHAIN

其中:

  • ARCH 可以是:i686, x86_64, ppc_64
  • MACHINE 可以是:native, power8
  • EXECENV 可以是:linuxapp, bsdapp
  • TOOLCHAIN 可以是:gcc, icc
    目标文件取决于运行环境是32位还是64位设备。可以在DPDK的 /config 目录中找到可用的目标,不能使用defconfig_前缀。

注意:配置文件根据 RTE_MACHINE 优化级别不同分别提供。在配置文件内部,RTE_MACHINE 配置为 native,意味着已编译的软件被调整到其构建的平台上。有关此设置的更多信息,请参阅 DPDK 编程指南。

当使用Intel® C++ 编译器 (icc)时,对64位和32位,需要使用以下命令进行调整。注意,shell脚本会更新 $PATH 值,因此不能再同一个会话中执行。此外,还应该检查编译器的安装目录,因为可能不同。

source /opt/intel/bin/iccvars.sh intel64
source /opt/intel/bin/iccvars.sh ia32

在*目录中使用 make install T=<target> 来生成目标文件。

例如,为了使用icc编译生成64位目标文件,运行如下命令:

make install T=x86_64-native-linuxapp-icc

为了使用gcc编译生成32位目标文件,命令如下:

make install T=i686-native-linuxapp-gcc

如果仅仅只是生成目标文件,并不运行,比如,配置文件改变需要重新编译,使用 make config T=<target> 命令:

make config T=x86_64-native-linuxapp-gcc

注意:任何需要运行的内核模块,如 igb_uio, kni, 必须在与目标文件编译相同的内核下进行编译。如果DPDK未在目标设备上构建,则应使用 RTE_KERNELDIR 环境变量将编译指向要在目标机上使用的内核版本的副本(交叉编译的内核版本)。

创建目标环境之后,用户可以移动到目标环境目录,并继续更改代码并编译。用户还可以通过编辑build目录中的.config文件对DPDK配置进行修改。(这是*目录中defconfig文件的本地副本)。

cd x86_64-native-linuxapp-gcc
vi .config
make

此外,make clean命令可以用于删除任何现有的编译文件,以便后续完整、干净地重新编译代码。

3.3Browsing the Installed DPDK Environment Target

一旦目标文件本创建,它就包含了构建客户应用程序所需的DPDK环境的所有库,包括轮询驱动程序和头文件。此外,test和testpmd应用程序构建在build/app目录下,可以用于测试。还有一个kmod目录,存放可能需要加载的内核模块。

3.4加载模块启动DPDK环境需要的UIO功能

要运行任何的DPDK应用程序,需要将合适的uio模块线加载到当前内核中。在多数情况下,Linux内核包含了标准的 uio_pci_generic 模块就可以提供uio能力。该模块可以使用命令加载

sudo modprobe uio_pci_generic

区别于 uio_pci_generic ,DPDK提供了一个igb_uio模块(可以在kmod目录下找到)。可以通过如下方式加载:

sudo modprobe uio
sudo insmod kmod/igb_uio.ko

注意:对于一下不支持传统中断的设备,例如虚拟功能(VF)设备,必须使用 igb_uio 来替代 uio_pci_generic 模块。由于DPDK 1.7版本提供VFIO支持,所以,对于支持VFIO的平台,可选则UIO,也可以不用。

3.5加载VFIO模块

DPDK程序选择使用VFIO时,需要加载 vfio-pci 模块:

sudo modprobe vfio-pci

注意,要使用VFIO,首先,你的平台内核版本必须支持VFIO功能。Linux内核从3.6.0版本之后就一直包含VFIO模块,通常是默认存在的。不够请查询发行文档以确认是否存在。

此外,要使用VFIO,内核和BIOS都必须支持,并配置为使用IO虚拟化 (如 Intel® VT-d)。为了保证非特权用户运行DPDK时能够正确操作VFIO,还应设置正确的权限。这可以通过DPDK的配置脚本(dpdk-setup.sh文件位于usertools目录中)。

3.6网络端口绑定/解绑定到内核去顶模块

从版本1.4开始,DPDK应用程序不再自动解除所有网络端口与原先内核驱动模块的绑定关系。相反的,DPDK程序在运行前,需要将所要使用的端口绑定到 uio_pci_generic, igb_uio 或 vfio-pci 模块上。任何Linux内核本身控制的端口无法被DPDK PMD驱动所使用。

注意:默认情况下,DPDK将在启动时不再自动解绑定内核模块与端口的关系。DPDK应用程序使用的任何端口必须与Linux无关,并绑定到 uio_pci_generic, igb_uio 或 vfio-pci 模块上。

将端口从Linux内核解绑,然后绑定到 uio_pci_generic, igb_uio 或 vfio-pci 模块上供DPDK使用,可以使用脚本dpdk_nic _bind.py(位于usertools目录下)。这个工具可以用于提供当前系统上网络接口的状态图,绑定或解绑定来自不同内核模块的接口。以下是脚本如何使用的一些实例。通过使用 --help or --usage 选项调用脚本,可以获得脚本的完整描述与帮助信息。请注意,要将接口绑定到uio或vfio的话,需要先将这两个模块加载到内核,再运行 dpdk-devbind.py 脚本。

注意:由于VFIO的工作方式,设备是否可用VFIO是有明确限制的。大部分是由IOMMU组的功能决定的。

任何的虚拟设备可以独立使用VFIO,但是物理设备则要求将所有端口绑定到VFIO,或者其中一些绑定到VFIO,而其他端口不能绑定到任何其他驱动程序。

如果你的设备位于PCI-to-PCI桥之后,桥接器将成为设备所在的IOMMU组的一部分。因此,桥接驱动程序也应该从端口解绑定。

注意:虽然任何用户都可以运行dpdk-devbind.py脚本来查看网络接口的状态,但是绑定和解绑定则需要root权限。查看系统中所有网络接口的状态:

./usertools/dpdk-devbind.py --status
Network devices using DPDK-compatible driver
============================================
0000:82:00.0 '82599EB 10-GbE NIC' drv=uio_pci_generic unused=ixgbe
0000:82:00.1 '82599EB 10-GbE NIC' drv=uio_pci_generic unused=ixgbe
Network devices using kernel driver
===================================
0000:04:00.0 'I350 1-GbE NIC' if=em0  drv=igb unused=uio_pci_generic *Active*
0000:04:00.1 'I350 1-GbE NIC' if=eth1 drv=igb unused=uio_pci_generic
0000:04:00.2 'I350 1-GbE NIC' if=eth2 drv=igb unused=uio_pci_generic
0000:04:00.3 'I350 1-GbE NIC' if=eth3 drv=igb unused=uio_pci_generic
Other network devices
=====================
<none>

绑定设备 eth1,04:00.1, 到 uio_pci_generic 驱动:

./usertools/dpdk-devbind.py --bind=uio_pci_generic 04:00.1
或者
./usertools/dpdk-devbind.py --bind=uio_pci_generic eth1

恢复设备 82:00.0 到Linux内核绑定状态:

./usertools/dpdk-devbind.py --bind=ixgbe 82:00.0

四、编译和运行简单应用程序

本章介绍如何在DPDK环境下编译和运行应用程序。还指出应用程序的存储位置。注意:此过程的部分操作也可以使用脚本来完成。参考 使用脚本快速构建 章节描述。

4.1编译一个简单应用程序

一个DPDK目标环境创建完成时(如 x86_64-native-linuxapp-gcc),它包含编译一个应用程序所需要的全部库和头文件。

当在Linux* 交叉环境中编译应用程序时,以下变量需要预先导出:

  • RTE_SDK - 指向DPDK安装目录。
  • RTE_TARGET - 指向DPDK目标环境目录。

以下是创建helloworld应用程序实例,该实例将在DPDK Linux环境中运行。这个实例可以在目录${RTE_SDK}/examples找到。

该目录包含 main.c 文件。该文件与DPDK目标环境中的库结合使用时,调用各种函数初始化DPDK环境,然后,为每个要使用的core启动一个入口点(调度应用程序)。默认情况下,二进制文件存储在build目录中。

cd examples/helloworld/
export RTE_SDK=$HOME/DPDK
export RTE_TARGET=x86_64-native-linuxapp-gcc
make
    CC main.o
    LD helloworld
    INSTALL-APP helloworld
    INSTALL-MAP helloworld.map
ls build/app
    helloworld helloworld.map

注意:在上面的例子中, helloworld 是在DPDK的目录结构下的。当然,也可以将其放在DPDK目录之外,以保证DPDK的结构不变。下面的例子, helloworld 应用程序被复制到一个新的目录下。

export RTE_SDK=/home/user/DPDK
cp -r $(RTE_SDK)/examples/helloworld my_rte_app
cd my_rte_app/
export RTE_TARGET=x86_64-native-linuxapp-gcc
make
  CC main.o
  LD helloworld
  INSTALL-APP helloworld
  INSTALL-MAP helloworld.map

4.2运行一个简单的应用程序

注意:UIO驱动和hugepage必须在程序运行前设置好。

注意:应用程序使用的任何端口,必须绑定到合适的内核驱动模块上,如章节 网络端口绑定/解绑定到内核去顶模块 描述的那样。应用程序与DPDK目标环境的环境抽象层(EAL)库相关联,该库提供了所有DPDK程序通用的一些选项。

以下是EAL提供的一些选项列表:

./rte-app -c COREMASK [-n NUM] [-b <domain:bus:devid.func>] \
          [--socket-mem=MB,...] [-m MB] [-r NUM] [-v] [--file-prefix] \
          [--proc-type <primary|secondary|auto>] [-- xen-dom0]

选项描述如下:

  • -c COREMASK: 要运行的内核的十六进制掩码。注意,平台之间编号可能不同,需要事先确定。
  • -n NUM: 每个处理器插槽的内存通道数目。
  • -b <domain:bus:devid.func>: 端口黑名单,避免EAL使用指定的PCI设备。
  • --use-device: 仅使用指定的以太网设备。使用逗号分隔 [domain:]bus:devid.func 值,不能与 -b 选项一起使用。
  • --socket-mem: 从特定插槽上的hugepage分配内存。
  • -m MB: 内存从hugepage分配,不管处理器插槽。建议使用 --socket-mem 而非这个选项。
  • -r NUM: 内存数量。
  • -v: 显示启动时的版本信息。
  • --huge-dir: 挂载hugetlbfs的目录。
  • --file-prefix: 用于hugepage文件名的前缀文本。
  • --proc-type: 程序实例的类型。
  • --xen-dom0: 支持在Xen Domain0上运行,但不具有hugetlbfs的程序。
  • --vmware-tsc-map: 使用VMware TSC 映射而不是本地RDTSC。
  • --base-virtaddr: 指定基本虚拟地址。
  • --vfio-intr: 指定要由VFIO使用的中断类型。(如果不支持VFIO,则配置无效)。
    其中 -c 是强制性的,其他为可选配置。

将DPDK应用程序二进制文件拷贝到目标设备,按照如下命令运行(我们假设每个平台处理器有4个内存通道,并且存在core0~3用于运行程序):