目录
Linux环境下DPDK入门... 1
1 引言... 2
2 资料集... 2
3 系统需求... 2
3.1 x86平台的先决条件BIOS设置... 2
3.2 编译DPDK. 3
3.3 运行DPDK程序... 3
4 编译DPDK源码... 5
4.1 安装DPDK和浏览源码... 5
4.2 安装DPDK目标(target)开发环境... 6
4.3 浏览安装好的DPDK环境target. 6
4.4 加载DPDK用户态IO模块... 7
4.5 加载VFIO模块... 7
4.6 从内核模块绑定和解绑网卡... 7
5 编译和运行例子程序... 8
5.1 编译例子程序... 8
5.2 运行例子程序... 9
5.2.1 程序使用的逻辑核... 9
5.2.2 程序使用的大页内存... 10
5.3 其它例子程序... 10
5.4 其它的测试程序... 10
6 开启其它功能... 10
6.1 高精度定时器(HPET)功能... 10
6.1.1 BIOS支持... 10
6.1.2 linux内核支持... 11
6.2 非root用户运行DPDK程序... 11
6.3 电源管理和省电功能... 11
6.4 使用DPDK的CPU亲和性减少上下文切换的开销... 12
6.5 加载DPDK KNI内核模块... 12
6.6 使用linux IOMMU透传来在INTEL VT-d虚拟化环境下运行DPDK. 12
6.7 40G网口高性能小包处理... 12
6.7.1 使用16个字节的RX描述符... 12
6.7.2 高性能和报文时延间的均衡... 12
7 快速安装启动脚本... 12
7.1 脚本结构... 13
7.2 用例... 13
7.3 应用程序... 15
8 怎么在intel平台上获得网卡的最好性能... 17
8.1 硬件和内存需求... 17
8.1.1 网卡需求... 18
8.1.2 BIOS设置... 18
8.1.3 Linux内核启动参数... 18
8.2 运行DPDK前的配置... 18
8.3 获取intel网卡的最好性能例子... 19
文档是安装配置DPDK操作说明,旨在帮助客户快速上手和运行程序。文档介绍了在linux开发环境下如何编译和运行DPDK程序,但是并不深入细节。
之前曾经尝试着翻译来着,当时因为要离职,一时兴起就想着翻译,翻译的太烂,现在重新翻译,一方面是方便新入职的同事能够快速入门,另外一方面是现在工作的需要,还有一方面是学习KVM想休息一下,看内存那块弄得头昏脑胀,而且后面的例子会涉及到虚拟话那块的。下一个将翻译样例那本书(以及在自己机器上运行可能遇到的问题),同时会参杂着介绍开发者手册中的一些个人理解。
下面列出了所有DPDK文档资料的建议读取顺序:
l 版本说明:提供了各个版本相关的信息,包括支持的特性,限制,修复的bug,出现的问题等等。也对频繁提到的问题以FAQ的方式做了回答。
l 入门手册(本文的):讲述了如何安装配置DPDK;意在帮助开发者快速上手和运行程序。
l 开发者手册:
n 软件架构和(通过例子)在linux环境下使用它
n DPDK的内容包括构建系统(包括在DPDK根目录下构建开发环境和程序使用的命令)和移植程序指南。
n 在已有软件和要新开发的软件中需要考虑到的优化。
还提供了一个专有术语表。
l API索引:提供了DPDK函数,数据结构和其它开发用到的结构体的详细信息。
l 范例用户手册:介绍了一系列的范例程序。每一个章节介绍一个程序,展示程序的特殊功能,说明如何编译,运行和使用范例程序。
本章介绍了编译DPDK需要的安装包。
注意:如果DPDK要运行在intel 89xx系统通讯芯片平台,请翻阅对应该系列的linux入门手册。
对于主流的平台,使用DPDK的基本功能并不需要专门的BIOS设置,然而,对于额外的功能像HPET(高精度定时器),电量管理功能,以及在40G网卡上高性能小包处理,需要修改BIOS设置。修改设置的详细信息见第六章。
需要的工具:
注意:已经在Fedora18上测试运行过,在其它系统上安装命令和需要的安装包可能不一样,要知道其它版本linux发行版测试详细的细节,请查看DPDK版本说明。
l GNU make
l coreutils:cmp,sed,grep,arch,等等。
l gcc:4.9版本或者是所有平台带的更新的版本。在一些gcc版本中,很多特殊的编译标志和链接标志默认是打开的,会影响到性能(比如,-fstatck-protector)。请查阅对应版本的文档和执行gcc –dumpspecs。
l libc hreaders,通常打包成gcc-multilib(intel 64位架构上是glibc-devel.i686/libc6-dev-i386;glibc-devel.x86_64/libc6-dev,IBM power架构则是glibc-devel.ppc64)
l 编译内核模块需要的内核头文件和源文件(kernel-devel.x86_64;kernel-devel.ppc64)
l 在64位机器上编译32位程序而外需要的库:
n glibc.i686, libgcc.i686, libstdc++.i686 and glibc-devel.i686 for Intel i686/x86_64;
n glibc.ppc64, libgcc.ppc64, libstdc++.ppc64 and glibc-devel.ppc64 for IBM ppc_64;
注意:x86_x32 ABI库旨在ubuntu13.10以上版本或者最新Debian版本上支持。只支持gcc4.9+版本。
l python,要使用dpdk安装包中各种帮助脚本必需的python版本2.7+或者是3.2+。
其它的工具:
l intel编译器icc:需要安装额外的库。在编译器安装文档看中icc安装说明。
l IBM powerlinux高级工具链:这是一系列的开源开发工具和运行时库,能够让用户用到IBM最新的power硬件特性。要安装的话看IBM的官方安装文档。
l libpcap头文件和库(libpcap-devel)编译和使用基于libpcap的轮询驱动。这个驱动默认是禁用的,可以通过修改编译时的配置文件参数CONFIG_RTE_LIBRTE_PMD_PCAP=y来打开。
l libarchive头文件和库在单元测试用打开源文件用到。
要运行DPDK程序,在目的机器上需要一些定制化的东西。
要求:
l 内核版本>=2.6.34:通过命令uname –r查看内核版本号
l glibc>=2.7(要用到cpuset特性):可以通过命令ldd –version命令查看gcc版本号
l 内核配置
在Fedora操作系统和其它通用发行版,例如Ubuntu,红帽企业版linux,发行商提供的内核配置基本可以跑绝大多数DPDK程序。
对于在其它内核上构建DPDK,下面几个选项需要能支持:
n UIO(用户态IO)
n HUGETLBFS(大页内存)
n PROC_PAGE_MONITOR支持
n HPET和HPET_MMAP配置项必须打开,如果用到HPET,具体细节看第六章。
对于大的报文内存池内存分配,大页内存是必须的。(前一章说过运行的内核必须支持大页内存)。使用大页内存来分配,即使是小部分页要用到,性能也能够得到提升。减少了TLBmiss,对应就减少了虚拟地址转物理地址的时间。使用小页4K内存页,很高频率的TLBmiss发生会导致性能低下。
申请大页给DPDK使用
大页内存的分配最好是在系统启动时或者是尽量在系统启动后越早越好,这样可以减少分配的物理内存页物理地址碎片化。申请大页内存需要在内核启动时传递一个命令行参数。
对于2MB内存页,分配1024个2MB内存页参数如下:
hugepages=1024
其它尺寸的大内存页,例如1G页,必须指定申请的内存页个数和默认内存页的大小。例如分配4个1G的内存页,需要指定的参数如下:
default_hugepagesz=1G hugepagesz=1G hugepages=4
注意:大页内存的尺寸是CPU支持的大小来决定的。intel的机器上通过查看CPU flags来查看支持的大页内存尺寸。比如pse 代表支持2MB的内存页,pdpe1gb代表支持1G内存页。在IBM power架构机器上,支持大页内存尺寸为16MB和16GB。
注意:对于64位的应用,如果平台支持,建议使用1G内存页。
在双插槽的NUMA系统中,申请的大页内存一般会平均在两个socket上分配(假定两个socket都有足够的内存)。
可以看linux内核源码树中Documentation/kernel-parameters.txt获取更多内核选项的细节。
备选项:
对于2M的页,可以在系统启动后申请分配。echo想要分配的大页内存页个数到/sys/devices目录下的nr_hugepages文件。对于单NUMA(非NUMA)节点的系统,如下命令所示(假设需要1024个2M页):
echo 1024 > /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages
在一个NUMA机器上,分别在不同node上分配指定数目的内存页:
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使用大页
一旦申请到大页内存,要想其被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虚拟机管理器上,大页内存对非特权域(DomainU)的支持意味着DPDK在客户机上可以像普通机器上一样跑。
然而,特权域(domain0)并不支持大页内存。需要插入新的内核模块rte_dom0_mm以便绕过这个限制来申请和映射大页内存,通过ioctl来申请,mmap来映射。
开启DPDK对Xen Dom0支持
默认情况下,Xen Dom0模式支持在DPDK的build配置文件中是禁用的。要想支持Xen Dom0,配置项CONFIG_RTE_LIBRTE_XEN_DOM0需要在编译时改成y。
此外,配置项CONFIG_RTE_EAL_ALLOW_INV_SOCKET_ID也必须置成y,以防万一收到错误的socket ID。
加载rte_dom0_mm模块
要在Xen Dom0中跑dpdk程序,rte_dom0_mm模块必须带上rsv_memsize参数加载到运行的内核中。可以在编译的DPDK目标目录的子目录中找到该模块。该模块的加载如下使用insmod命令(假设当前处于DPDK编译的目标目录下):
sudo insmod kmod/rte_dom0_mm.ko rsv_memsize=X
X的值可以大于4095MB。
配置DPDK使用的内存
在rte_dom0_mm.ko模块加载完成后,用户必须配置DPDK使用的内存。将需要的内存大小echo到/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 Dom0不支持NUMA配置,所以参数--socket-mem对于Xen Dom0是无效的。
注意:内存大小值不能超过rsv_memsize值。
在Xen Domain0中运行DPDK程序
要跑DPDK程序需要额外提供一个命令行参数--xen-dom0。
注意:本章写的部分安装过程也可以通过后续<快速安装脚本>一章中描述的脚本实现。
首先,解压压缩包并移动到解压的DPDK源码目录:
tar xJf dpdk-
cd dpdk-
DPDK包含以下几个子目录:
l lib:DPDK库的源码
l drivers:DPDK轮询驱动的源码
l app:DPDK应用程序的源码(自动化测试)
l examples:DPDK应用程序样例源码
l config,buildtools,mk:框架相关的Makefiles,脚本和配置文件
DPDK target格式是:
ARCH-MACHINE-EXECENV-TOOLCHAIN
详解:
l ARCH 可以是:i686,x86_64,ppc_64
l MACHINE 可以是:native,power8
l EXECENV 可以是:linuxapp,bsdapp
l TOOLCHAIN 可以是:gcc,icc
将要被安装的target环境取决于要安装的机器上的包是32位还是64位。可以编译产生的target在DPDK/config目录下定义了。不要用defconfig_前缀,就是编译的时候不要带上这个前缀。
注意:配置文件是RTE_MACHINE下的最优配置集合。在配置文件中,RTE_MACHINE被定义为native,意味着这是针对本机环境下最佳优化的编译。对于这些设置的更多信息,可以采用的值,看DPDK开发者手册。
当使用ICC编译器时,下面的命令分别对应32位和64位环境使用。注意这个shell脚本更新$PATH环境变量,因此换个会话就失效了。编译安装的目录可能是不同的:
source /opt/intel/bin/iccvars.sh intel64
source /opt/intel/bin/iccvars.sh ia32
安装和生成target,在DPDK的根目录下使用make install T=
例如,使用icc编译64位target:
make install T=x86_64-native-linuxapp-icc
使用gcc编译32位的:
make install T=i686-native-linuxapp-gcc
只是做创建target的准备,而不是编译,例如,在编译之前需要手动修改配置文件的,使用命令make config T=
make config T=x86_64-native-linuxapp-gcc
警告:
任何内核模块,比如igb_uio,kni等,必须编译时的内核和要运行时的内核一致。如果DPDK不是在编译产生的目标机器上运行, RTE_KERNELDIR环境变量必须指向要运行target机器对应内核版本。
一旦目标环境创建完成,用户可能需要进入目标环境目录,修改源码并重新编译。用户也可能修改编译时的DPDK配置项,通过编辑build目录下的.config文件(这个文件是从顶层目录的config目录下defconfig文件拷贝到本地来的):
cd x86_64-native-linuxapp-gcc
vi .config
make
此外,make clean命令可以清除所有之前编译留下的文件。
一旦target创建完成就包含所有的苦,包括轮询驱动,和用户程序编译需要的DPDK环境头文件。此外,test和testpmd程序在build/app目录下创建完成,可用于测试。kmod目录下包含需要加载到内核中的几个内核模块。
运行DPDK程序,一个合适的uio模块需要加载到运行中的内核。在很多例子中,标准的uio_pci_generic LINUX内核模块可以提供用户态驱动能力。该模块加载如下:
sudo modprobe uio_pci_generic
和上述不同的是,DPDK提供了igb_uio模块来实现(在kmod目录下):
sudo modprobe uio
sudo insmod kmod/igb_uio.ko
注意:对于缺乏对传统中断支持的设备,比如VF设备,igb_uio模块可能需要被uio_pci_generic替换掉。
在DPDK1.7之前版本提供了对VFIO的支持,在支持VFIO的平台上UIO就不是必须的了。
使用VFIO的DPDK应用程序,vfiopci模块必须加载:
sudo modprobe vfio-pci
需要注意的是要使用VFIO,你的内核必须支持才行。VFIO内核模块在LINUX内核3.6.0版本之后就囊括经来了,默认是有的。然而,请翻阅linux发行版文档确定一下。
使用VFIO,BIOS和内核都必须支持,BIOS需要打开支持虚拟化的配置选项(例如Intel® VT-d).
在非特权用户下运行DPDK程序,要正确的操作VFIO,需要建立正确的权限设置。这个可以通过DPDK安装脚本实现(dpdk-setup.sh,位于usertools目录下)
在1.4版本,DPDK程序不再自动解绑所有支持DPDK的网口,当网口绑定在内核驱动上且正在运行时。取而代之的是,DPDK程序在运行前,必须将使用的所有网口绑定到uio_pci_generic,igb_uio或者是vfio-pci模块上。任何linux控制下的网口将被DPDK轮询驱动忽略,不能被DPDK程序使用。
警告:DPDK默认不再在启动时自动解绑内核驱动管理的网口。任何PDDK程序使用的网口必须在程序运行前解除linux控制,绑定到对应的驱动上(uio_pci_generic,igb_uio或者是vfio-pci).
将绑定到DPDK的网口解绑并交给linux控制,可以使用usertool子目录下的dpdk_nic_bind.py脚本。这个脚本可以展示当前系统中所有网口的状态,可以将其从不同的内核模块间解绑和绑定,包括uio和vfio模块。下面是使用脚本的例子。要想获取脚本的全部使用描述和参数,可以带上—help或者是—usage参数启动脚本。注意,uio或者是vfio内核模块需要在运行dpdk-nic-bind.py脚本之前加载到内核中。
查看系统中所有网口的状态:
./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
=====================
绑定设备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到原始的内核驱动上:
./usertools/dpdk-devbind.py --bind=ixgbe 82:00.0
本章讲的是怎么在DPDK环境下编译和运行程序。也指出了样例程序保存在哪里。
注意:本章写的部分安装过程也可以通过后续<快速安装脚本>一章中描述的脚本实现。
DPDK目标环境目录创建完成(例如x86_64-native-linuxapp-gcc),就包含了创建一个dpdk应用程序需要的库和头文件。
当在linux DPDK环境下编译程序,下面两个环境遍历必须export:
l RTE_SDK 指向DPDK编译安装目录即target所在目录
l RTE_TARGET 指向DPDK target目录名
下面展示的是运行在linux DPDK环境下的例子helloworld。可以在$RTE_SDK/examples目录下找到。
目录下包含main.c文件,默认编译生成的二进制文件在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框架的完整性,可以把helloworld移动到外部。在下面的例子中,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
警告:必须在运行DPDK程序前安装好UIO驱动和大页内存。
警告:任何进程使用的网口在程序启动之前必须绑定到适当的内核模块上,如之前<从内核模块上绑定和解绑网卡>章节所述。
程序必须链接DPDK target环境EAL(环境抽象层)库,这个库提供了每一个DPDK程序通用的选项。
下面是可以传递给EAL的参数列表:
./rte-app -c COREMASK [-n NUM] [-b
[--socket-mem=MB,...] [-m MB] [-r NUM] [-v] [--file-prefix] \
[--proc-type
EAL参数如下:
l -c COREMASK:要使用CPU core16进制掩码。注意core编号在不同的平台不一样,需要事先确定好。
l -n NUM:每个处理器socket的内存通道数
l -b
l --socket-mem:在指定socket上分配大页内存
l -m MB:指定分配大大页内存数,不限处理器的socket。加以使用—socket-mem代替这个参数。
l -r NUM:内存的rank数
l -v:显示程序版本号
l --huge-dir:大页内存的挂载点
l --file-prefix:大页内存文件的前缀
l --proc-type:进程类型
l --xen-dom0:支持程序在Xen Domain0中非大页内存下运行
l --vmware-tsc-map:使用VMware TSC代替本地的RDTSC
l --base-virtaddr :指定虚拟地址的基址
l --vfio-intr:指定VFIO使用的中断类型(如果不是用VFIO则无效)
-c是必须的,其它都是可选的。
运行例子程序(假设该平台每个处理器socket有4个内存通道,有4个cpu核,且程序都使用上):
./helloworld -c f -n 4
注意:--proc-type和—file-prefix EAL参数用于DPDK多进程。可以查阅DPDK范例程序一书中多进程章节和DPDK开发手册获取更多细节。
CPU核掩码参数是DPDK程序必须带上的。掩码的每个bit位代表linux显示的每个逻辑核的编号。不同的平台之间,逻辑核编号和逻辑核编号与NUMA socket的映射关系是不同的,所以建议在选在使用核之前先弄清楚该平台的核分布。
在初始化DPDK 环境抽象层时,会打印出要使用的逻辑核以及其所在的socket。这些信息也可以通过查看/proc/cpuinfo文件来知道,例如,执行 cat /proc/cpuinfo。每个逻辑核的物理ID属性表明其属于哪个CPU socket。
注意:逻辑核分布视图可以通过使用linux工具lstopo获取。在Fedora系统中,可以使用下面的命令安装使用:
sudo yum install hwloc
./lstopo
警告:逻辑核的分布在不同的主板上是不一样的,在选择使用的核之前要检查一下。
运行DPDK程序,建议大页内存用多少就分配多少。如果没有传入-m或者是—socket-mem参数,DPDK程序在启动时自动完成。
如果实际使用的内存超过了传入参数值指定的值,程序将失败并退出。然而,如果用户要申请使用的内存小于预先分配的内存大小,特别是使用了-m选项时,程序自身也会失败。假定系统在socket0和socket1上各预先分配有1024个2MB内存页,用户请求使用128MB内存,64个页可能不符合要求:
l 内核给予应用程序分配的大页内存都在socket1上。这种情况下,如果应用程序试图在socket0上创建一个对象,比如一个无锁队列或者是内存池,程序会失败。要避免这种问题,建议使用—socket-mem选项代替-m选项。
l 那些大页内存在物理上是分布在各处的,然而DPDK EAL试图分配在物理地址上连续的一段内存,而可能现有的内存页不是连续的,这种情形下,应用程序无法申请大的内存池而失败。
socket-mem选项可以在指定的socket上分配指定大小的内存页。要想做到这个,就得在这个选项后面接上要在各个socket上分配内存大小,例如:--socket-mem=0,512,就是在socket1上分配512MB内存。同理,在4socket的系统中,只在socket0和2上各分配1GB内存:--socket-mem=1024,0,1024。某一个CPUsocket上没有内存分配,那么就不需要显式的引用,例如前面socket3就是。如果不能在指定的socket上分配到足够的内存,EAL初始化失败。
其它的例子程序在$RTE_SDK./example目录下。这些程序的编译和运行方式与本文中之前章节介绍的差不多。此外,可以查阅DPDK范例程序手册,该手册介绍了在编译和执行程序时的特殊命令参数,以及代码的说明。
另外,有两个额外的测试程序在DPDK库创建的时候编译出来。源码在DPDK/app目录下,名为test和testpmd。DPDK目标库创建完成,那么就可以在build/app目录中找到。
l test程序提供了DPDK各种函数的特定测试。
l testpmd程序提供了一些不同的报文吞吐测试和特性,例如如何使用intel82599万兆网卡的Flow Director(引流)特性。
要使用HPET必须BIOS中支持,否则默认采用TSC。一般情况下,在系统启动的时候按F2键可以进入BIOS。用户可以开启HPET选项。在intel的Crystal Forest平台BIOS上,路径是Advanced -> PCH-IO Configuration -> High Precision Timer -> (Change from Disabled to Enabled if necessary).
在已经启动的系统中,下面的命令可以查看HPET是否开启:
grep hpet /proc/timer_list
如果返回为空,那么必须重启机器并在BIOS中开启HPET(重复上述的指令)。
DPDK通过将时间戳计数器映射到处理器地址空间来利用平台的HPET,如此需要HPET_MMAP内核配置项支持。
警告:在Fedora系统和其他通用linux发行版,例如ubuntu,HPET_MMAP内核选项是默认关闭的。带此参数重新编译内核,请查阅发行版文档了解相关操作。
在DPDK中使用HPET
默认情况下,DPDK编译配置文件中HPET选项是关闭的。要使用HPET,CONFIG_RTE_LIBEAL_USE_HPET配置项必须改成y,需要在编译时修改参数。
程序使用rte_get_hpet_cycles()和rte_get_hpet_hz()接口调用,在程序初始化时调用rte_eal_hpet_init()接口来将HPET作为rte_timer库的默认时间源。这个接口将确保HPET可用,如果不可用则返回错误值。例如,如果HPET_MMAP内核不支持。那么程序可以针对支持或者是不支持而决定采取什么动作。
注意:如果程序需要时间API,但是HPET不支持,建议采用rte_get_timer_cycles()和rte_get_timer_hz()接口代替HPET接口。这个通用接口既可以使用TSC,也可以使用HPET作为时间源,取决于是否在程序启动是调用rte_eal_hpet_init()接口,如果调用了的话,系统是否支持。
尽管程序通过DPDK能够直接使用网口和硬件资源,但是这些程序如果是作为普通用户而不是root用户,那么需要做一些小的权限调整。要实现这个,下面这些linux系统文件的所有者和权限必须调整,以便普通linux用户也能在使用DPDK的时候访问它们:
l 所有的大页内存挂载点目录,例如/mnt/huge
l 在/dev目录下的用户态IO设备文件,/dev/uio0,/dev/uio1,等等。
l 用户态IO系统文件配置和资源文件,例如uio0:
/sys/class/uio/uio0/device/config
/sys/class/uio/uio0/device/resource
l 如果要使用HPET,文件/dev/hpet
注意:在一些linux系统上安装时,/dev/hugepages会默认作为大页内存的挂载点创建。
如果要使用DPDK的电源管理功能,那么必须在平台BIOS中开启增强型INTEL SpeedStep(步进?)技术开关。否则,系统文件/sys/devices/system/cpu/cpu0/cpufreq不会存在,且基于CPU主频调节的电源管理就无法使用。查阅相关的BIOS文档确定这些设置如何访问。
例如,在一些INTEL平台BIOS是不一致的,增强型INTEL SpeedStep技术的路径是:
Advanced
-> Processor Configuration
-> Enhanced Intel SpeedStep® Tech
此外,要开启电源管理C3和C6也一样要打开。C3和C6在同样的BIOS平台:
Advanced
-> Processor Configuration
-> Processor C3 Advanced
-> Processor Configuration
-> Processor C6
当一些线程被DPDK程序使用时,这些线程每一个会被固定到系统中的一个逻辑核心上(一个线程对应一个逻辑核心),对于linux调度器来说,也有可能会在这些核心上运行其他任务。为了阻止调度器将其他任务调度到DPDK使用的核心上,需要使用内核参数isolcpus来将这些核心从Linux调度器中隔离开来.
例如,DPDK程序使用逻辑核性2,4,6运行程序,下面的这个选项需要加到内核启动参数中:
isolcpus=2,4,6
要运行DPDK KNI例子程序,需要额外加载一个内核模块(kni模块)到运行的linux内核中。可以在DPDK target目录下的kmod目录中找到。同理如加载igb_uio模块,加载kni也是通过ismod命令来(假设我们当前处于DPDK target目录下):
insmod kmod/rte_kni.ko
要在linxu内核中开启intel VT-d,一系列的内核配置项需要设置:
l IOMMU_SUPPORT
l IOMMU_API
l INTEL_IOMMU
此外,要在intel VT-d虚拟化环境下运行DPDK,当使用igb_uio驱动时,必须设置内核参数iommu=pt。这个是主机中DMAR(DMA Remapping(DMA地址重映射)透传的结果。INTEL_IOMMU_DEFAULT_ON在内核中也没有设置,内核参数inte_iommu=on必须使用。这可以确保intel IOMMU按照期望被初始化。
请注意,使用igb_uio驱动时必须使用iommu=pt参数,而vfio-pci驱动则是使用iommu=pt和iommu=on二者都可以运行。
在最新版本的固件镜像中,性能增强的问题解决了,固件更新可以获取更高的性能。用户需要检查本地的intel网卡驱动版本。用户需要查阅DPDK版本说明文档确定这个版本对应的网卡i40E驱动版本。
i40e轮询驱动支持16和32字节的RX描述符大小,而16个字节的能够提供更高的小包处理性能。需要在配置文件中修改CONFIG_RTE_LIBRTE_I40E_16BYTE_RX_DESC参数为使用16个字节的RX描述符。
由于硬件设计的原因,每个报文描述符的回写需要在网卡内产生一个中断信号。最小的中断间隔需要在编译时修改配置文件中的CONFIG_RTE_LIBRTE_I40E_ITR_INTERVAL参数。尽管有默认配置,用户可以根据自己的实际情况,看是更在乎性能还是时延来调优这个参数。
dpdk-setup.sh脚本,在usertools子目录下,可以帮助用户执行下面的任务:
l 编译DPDK库
l 插入和移走DPDK IGB_UIO内核模块
l 插入和移走VFIO内核模块
l 插入和移走DPDK KNI内核模块
l 创建和删除NUMA和非NUMA下的大页内存
l 网口状态试图展示和分配网口给DPDK程序使用
l 为使用VFIO的非特权用户分配权限
l 运行test和testpmd程序
l 展示在meminfo中的大页信息
l 列出/mnt/huge中的大页
l 删除创建的DPDK库
dpdk-setup.sh脚本是用户按照用户的执行命令逻辑顺序来组织起来的一系列的步骤。每一个步每一步都提供了一些提示信息帮助用户完成要求的任务。下面是每一步的简单概述。
step 1:编译DPDK库
最开始,用户必须选择一个DPDK target,匹配类型和编译选项,以便生成正确的库。
用户必须按照前面章节所述,将所有需要的库,模块,更新和编译需要的东西安装好。
step2:安装环境
用户配置DPDK程序运行所需的linux环境。大页内存的建立。任何已存的大页内存会被移除。DPDK用到的内核模块也在这步中插入,DPDK程序使用的网卡接口也绑定到对应的模块。
step3:运行程序
用户可能需要在上面步骤完成后运行test程序。test程序允许执行一系列的DPDK功能测试。testpmd支持收发包相关的测试。
step4:检查系统
这步提供了一些工具来检查系统的大页内存状态。
step5:系统还原
最后一步是恢复系统到原有的状态。
下面是一些使用dpdk-setup.sh脚本的用例。这个脚本应该使用source命令来运行。
警告:脚本需要在root权限下运行。
source usertools/dpdk-setup.sh
------------------------------------------------------------------------
RTE_SDK exported as /home/user/rte
------------------------------------------------------------------------
Step 1: Select the DPDK environment to build
------------------------------------------------------------------------
[1] i686-native-linuxapp-gcc
[2] i686-native-linuxapp-icc
[3] ppc_64-power8-linuxapp-gcc
[4] x86_64-native-bsdapp-clang
[5] x86_64-native-bsdapp-gcc
[6] x86_64-native-linuxapp-clang
[7] x86_64-native-linuxapp-gcc
[8] x86_64-native-linuxapp-icc
------------------------------------------------------------------------
Step 2: Setup linuxapp environment
------------------------------------------------------------------------
[11] Insert IGB UIO module
[12] Insert VFIO module
[13] Insert KNI module
[14] Setup hugepage mappings for non-NUMA systems
[15] Setup hugepage mappings for NUMA systems
[16] Display current Ethernet device settings
[17] Bind Ethernet device to IGB UIO module
[18] Bind Ethernet device to VFIO module
[19] Setup VFIO permissions
------------------------------------------------------------------------
Step 3: Run test application for linuxapp environment
------------------------------------------------------------------------
[20] Run test application ($RTE_TARGET/app/test)
[21] Run testpmd application in interactive mode ($RTE_TARGET/app/testpmd)
------------------------------------------------------------------------
Step 4: Other tools
------------------------------------------------------------------------
[22] List hugepage info from /proc/meminfo
------------------------------------------------------------------------
Step 5: Uninstall and system cleanup
------------------------------------------------------------------------
[23] Uninstall all targets
[24] Unbind NICs from IGB UIO driver
[25] Remove IGB UIO module
[26] Remove VFIO module
[27] Remove KNI module
[28] Remove hugepage mappings
[29] Exit Script
选项:
下面是展示选择创建x86_64-native-linuxapp-gcc DPDK库的例子:
Option: 9
================== Installing x86_64-native-linuxapp-gcc
Configuration done
== Build lib
...
Build complete
RTE_TARGET exported as x86_64-native-linuxapp-gcc
下面是展示插入DPDK UIO驱动的例子:
Option: 25
Unloading any existing DPDK UIO module
Loading DPDK UIO module
下面的选择是展示创建NUMA系统下的大页内存。分别在两个node上各分配1024个2MB内存页。那么程序就需要系统-m 4096参数来访问这两块内存。(如果没有提供-m参数,那么程序会自动执行)。
注意:如果提示移除临时文件,输入y。
Option: 15
Removing currently reserved hugepages
mounting /mnt/huge and removing directory
Input the number of 2MB pages for each node
Example: to have 128MB of hugepages available per node,
enter '64' to reserve 64 * 2MB pages on each node
Number of pages for node0: 1024
Number of pages for node1: 1024
Reserving hugepages
Creating /mnt/huge and mounting as hugetlbfs
下面是展示选择加载test程序到一个逻辑核上运行:
Option: 20
Enter hex bitmask of cores to execute test app on
Example: to execute app on cores 0 to 7, enter 0xff
bitmask: 0x01
Launching app
EAL: coremask set to 1
EAL: Detected lcore 0 on socket 0
...
EAL: Master core 0 is ready (tid=1b2ad720)
RTE>>
一旦用户运行dpdk-setup.sh脚本,编译EAL库,建立好大页内存(加入使用的是linxu EAL目标),用户可能接着会去编译和运行自己的程序或者是例子程序。
在/examples目录下的例子提供了解DPDK操作的好的起点。下面的命令展示了helloworld程序是怎么编译和运行的。如同上述章节建议的,<程序使用的逻辑核心>,逻辑核心在平台上的分布应该在选择程序使用的逻辑核掩码时确定好。
cd helloworld/
make
CC main.o
LD helloworld
INSTALL-APP helloworld
INSTALL-MAP helloworld.map
sudo ./build/app/helloworld -c 0xf -n 3
[sudo] password for rte:
EAL: coremask set to f
EAL: Detected lcore 0 as core 0 on socket 0
EAL: Detected lcore 1 as core 0 on socket 1
EAL: Detected lcore 2 as core 1 on socket 0
EAL: Detected lcore 3 as core 1 on socket 1
EAL: Setting up hugepage memory...
EAL: Ask a virtual area of 0x200000 bytes
EAL: Virtual area found at 0x7f0add800000 (size = 0x200000)
EAL: Ask a virtual area of 0x3d400000 bytes
EAL: Virtual area found at 0x7f0aa0200000 (size = 0x3d400000)
EAL: Ask a virtual area of 0x400000 bytes
EAL: Virtual area found at 0x7f0a9fc00000 (size = 0x400000)
EAL: Ask a virtual area of 0x400000 bytes
EAL: Virtual area found at 0x7f0a9f600000 (size = 0x400000)
EAL: Ask a virtual area of 0x400000 bytes
EAL: Virtual area found at 0x7f0a9f000000 (size = 0x400000)
EAL: Ask a virtual area of 0x800000 bytes
EAL: Virtual area found at 0x7f0a9e600000 (size = 0x800000)
EAL: Ask a virtual area of 0x800000 bytes
EAL: Virtual area found at 0x7f0a9dc00000 (size = 0x800000)
EAL: Ask a virtual area of 0x400000 bytes
EAL: Virtual area found at 0x7f0a9d600000 (size = 0x400000)
EAL: Ask a virtual area of 0x400000 bytes
EAL: Virtual area found at 0x7f0a9d000000 (size = 0x400000)
EAL: Ask a virtual area of 0x400000 bytes
EAL: Virtual area found at 0x7f0a9ca00000 (size = 0x400000)
EAL: Ask a virtual area of 0x200000 bytes
EAL: Virtual area found at 0x7f0a9c600000 (size = 0x200000)
EAL: Ask a virtual area of 0x200000 bytes
EAL: Virtual area found at 0x7f0a9c200000 (size = 0x200000)
EAL: Ask a virtual area of 0x3fc00000 bytes
EAL: Virtual area found at 0x7f0a5c400000 (size = 0x3fc00000)
EAL: Ask a virtual area of 0x200000 bytes
EAL: Virtual area found at 0x7f0a5c000000 (size = 0x200000)
EAL: Requesting 1024 pages of size 2MB from socket 0
EAL: Requesting 1024 pages of size 2MB from socket 1
EAL: Master core 0 is ready (tid=de25b700)
EAL: Core 1 is ready (tid=5b7fe700)
EAL: Core 3 is ready (tid=5a7fc700)
EAL: Core 2 is ready (tid=5affd700)
hello from core 1
hello from core 2
hello from core 3
hello from core 0
本章是在INTEL平台上DPDK程序获得高性能的一步步引导。
要最好的性能最好是使用intel至强系列服务器,例如Ivy Bridge,Haswell或者是更新的架构的服务器。
确保每一个内存通道至少有一个内存DIMM插入,且内存的大小至少4GB。注意:这个在性能上有最直接的影响。
你可以检查内存配置通过dmidecode命令,如下:
dmidecode -t memory | grep Locator
Locator: DIMM_A1
Bank Locator: NODE 1
Locator: DIMM_A2
Bank Locator: NODE 1
Locator: DIMM_B1
Bank Locator: NODE 1
Locator: DIMM_B2
Bank Locator: NODE 1
...
Locator: DIMM_G1
Bank Locator: NODE 2
Locator: DIMM_G2
Bank Locator: NODE 2
Locator: DIMM_H1
Bank Locator: NODE 2
Locator: DIMM_H2
Bank Locator: NODE 2
上面的输出展示了总共8个内存通道,每个通道有2个DIMM。
也可以通过dmidecode命令确定内存的频率:
dmidecode -t memory | grep Speed
Speed: 2133 MHz
Configured Clock Speed: 2134 MHz
Speed: Unknown
Configured Clock Speed: Unknown
Speed: 2133 MHzConfigured Clock Speed: 2134 MHz
Speed: Unknown
...
Speed: 2133 MHz
Configured Clock Speed: 2134 MHz
Speed: Unknown
Configured Clock Speed: Unknown
Speed: 2133 MHz
Configured Clock Speed: 2134 MHz
Speed: Unknown
Configured Clock Speed: Unknown
上述输出表明内存为2133MHz的DDR4内存。和前面的信息结合起来可以确定每个内存通道有一根内存条。
使用DPDK支持的高端网卡,比如INTEL XL710 40GBE。
确保每一个网卡刷入了最新版本的固件。
使用PCIe Gen3插槽,例如Gen3 x8或者是Gen3 x16,因为PCIe Gen2插槽无法提供足够的宽带,2X10Gbe或者是更高。可以通过使用lspci命令检查PCI插槽的使用信息:
lspci -s 03:00.1 -vv | grep LnkSta
LnkSta: Speed 8GT/s, Width x8, TrErr- Train- SlotClk+ DLActive- ...
LnkSta2: Current De-emphasis Level: -6dB, EqualizationComplete+ ...
当擦汗如网卡到PCI插槽时总要检查说明信息,例如CPU0或者是CPU1连接到的是哪个socket上。
多考虑一下NUMA。如果你使用2个或者是更多的不同的网口,最好确保这些网卡在同样的CPU socket上。如何确定看下面的例子。
下面是推荐的BIOS设置项。不同的平台上可能有不同的BIOS名称,所以下面主要是作为参考:
下面是GRUB启动参数建议设置:
default_hugepagesz=1G hugepagesz=1G hugepages=8
isolcpus=2,3,4,5,6,7,8
iommu=pt intel_iommu=on
下面的shell命令有助于编译和配置DPDK。
# Build DPDK target.
cd dpdk_folder
make install T=x86_64-native-linuxapp-gcc -j
# Get the hugepage size.
awk '/Hugepagesize/ {print $2}' /proc/meminfo
# Get the total huge page numbers.
awk '/HugePages_Total/ {print $2} ' /proc/meminfo
# Unmount the hugepages.
umount `awk '/hugetlbfs/ {print $2}' /proc/mounts`
# Create the hugepage mount folder.
mkdir -p /mnt/huge
# Mount to the specific folder.
mount -t hugetlbfs nodev /mnt/huge
cd dpdk_folder
usertools/cpu_layout.py
# List all the NICs with PCI address and device IDs.
lspci -nn | grep Eth
举例说明,假设输出是这样的:
82:00.0 Ethernet [0200]: Intel XL710 for 40GbE QSFP+ [8086:1583]
82:00.1 Ethernet [0200]: Intel XL710 for 40GbE QSFP+ [8086:1583]
85:00.0 Ethernet [0200]: Intel XL710 for 40GbE QSFP+ [8086:1583]
85:00.1 Ethernet [0200]: Intel XL710 for 40GbE QSFP+ [8086:1583]
检查PCI设备所述的NUMA节点ID:
cat /sys/bus/pci/devices/0000\:xx\:00.x/numa_node
通常情况下,0x:00.x是位于socket0上,8x:00.x位于socket1上。注意:为了获得更高的性能,确保cpu核心和网卡在同样的socket上。例如上面的85:00.0在socket1上,那么它被socket1上的核心使用能获得最好的性能。
# Bind ports 82:00.0 and 85:00.0 to dpdk driver
./dpdk_folder/usertools/dpdk-devbind.py -b igb_uio 82:00.0 85:00.0
# Check the port driver status
./dpdk_folder/usertools/dpdk-devbind.py –status
查看dpdk-devbind.py –help获取更多细节。
下面是运行DPDK3层转发例子:l3fwd程序,使用intel服务器平台和intelXL710网卡获的高性能。对于特殊的40GbE午那个卡的配置请查阅i40e网卡手册。
这个例子展示的是在两个INTELXL710 40GbE网口下获得的最好性能。
82:00.0 Ethernet [0200]: Intel XL710 for 40GbE QSFP+ [8086:1583]
85:00.0 Ethernet [0200]: Intel XL710 for 40GbE QSFP+ [8086:1583]
./l3fwd -c 0x3c0000 -n 4 -w 82:00.0 -w 85:00.0 \
-- -p 0x3 --config '(0,0,18),(0,1,19),(1,0,20),(1,1,21)'
这代表着程序使用core18转发port0的队列0,core19转发port0的队列1,core20转发port1的队列0,core21转发port1的队列1。
a) 在发包器上创建一个流
b) 设置以太网II类型为0x8000