1 背景
数据平面开发工具包 Data Plane Development Kit(DPDK) 由一组库和用户空间驱动组成的,用来加速包处理和转发的速度并降低延时,它支持多种不同的处理器架构和操作系统。DPDK被设计运行在用户空间,这样,基于DPDK的应用程序可以直接与网卡交互收发数据包,对于某些网络性能密集型场景下的应用有着现实的意义。
具体来说,DPDK所带来的主要的优势在于:通过用户空间的驱动,旁路kernel和TCP协议栈,规避了过多的内存拷贝和系统调用;通过轮询的方式,避免传统包处理时的中断和上下文切换;通过hugepage降低TLB miss,同时利用多通道内存,避免了过多访存开销;最后,还可以设置亲和性和独占,避免了不同核心的线程切换。除此之外,DPDK还在管理上提供了更多的包处理的可控制性。
进一步,在容器环境下,无论是原生的Docker还是平台化的kubernetes,不同的网络插件也提供了对于DPDK的支持,使得在容器环境中也可以利用到DPDK所提供的优势和性能提升。
2 DPDK对AWS的支持
在AWS平台上,DPDK可以支持具有增强网络(Enhanced Networking)的实例,包括了基于intel的82599(ixgbevf)和基于AWS的Elastic Network Adapter(ena)。具体来说,基于Nitro的实例,例如C5,M5,I3和T3以及上一代基于intel的C4,M4和T2等具备增强网络的实例都可以支持DPDK。在使用DPDK时,需要选用16.04之后的版本,DPDK在该版本之后才提供了对AWS EC2的支持。
下图示意了non-DPDK和DPDK优化的应用之间的区别。
本文使用AWS EC2 C5.2xlarge实例,说明了如何在AWS EC2中安装和部署DPDK环境。
在后续的文章中,会进一步针对AWS上的容器平台如何和DPDK集成给出说明。
3 设置DPDK环境
使用amazon linux2 AMI启动一台c5.2xlarge实例,并通过ssh登录。
3.1 设置hugepages
CPU所能支持的hugepages大小可以通过查看CPU的flag确认,如果支持pse,则平台支持2MB的hugepages,如果支持pdpe1gb,则平台支持1GB的hugepages。
查看我们使用的操作系统环境c5.2xlarge。
# cat /proc/cpuinfo |grep -e pse -e pdpe1gb
flags: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss ht syscall nx pdpe1gb rdtscp
可见,c5.2xlarge可以支持2MB的hugepages和1GB的hugepages。 对于不同的hugepages,用不同的方式完成配置。一般来说,对于64-bit的应用,建议使用1GB hugepages。下面我们会分别来进行说明。 针对2M hugepage,设置如下: 在大多数较新的linux发行版,默认已经启用的2M hugepages,并已经通过dev-hugepages.mount服务进行挂载,比如本文所使用的amazon linux2。镜像为amzn2-ami-hvm-2.0.20191116.0-x86_64-gp2 (ami-07539a31f72d244e7),kernel版本4.14.154-128.181.amzn2.x86_64。 更新系统。
查看当前系统对于hugepages分配和支持。
# cat /proc/meminfo |grep Huge
AnonHugePages: 0 kB
ShmemHugePages: 0 kB
HugePages_Total: 0
HugePages_Free: 0
HugePages_Rsvd: 0
HugePages_Surp: 0
Hugepagesize: 2048 kB
# cat /proc/mounts |grep -i hugepage
hugetlbfs /dev/hugepages hugetlbfs rw,relatime,pagesize=2M 0 0
针对2M hugepages,可以通过不同的方式来进行配置。
设置内核参数的方式。针对grub2引导的发行版,配置如下:
# echo GRUB_CMDLINE_LINUX="hugepages=2048">>/etc/default/grub
# grub2-mkconfig -o /boot/grub2/grub.cfg
重启后可以确认已经生效。
# cat /proc/cmdline | grep hugepages
BOOT_IMAGE=/boot/vmlinuz-4.14.152-127.182.amzn2.x86_64 root=UUID=e8f49d85-e739-436f-82ed-d474016253fe ro hugepages=2048 console=tty0 console=ttyS0,115200n8 net.ifnames=0 biosdevname=0 nvme_core.io_timeout=4294967295 rd.emergency=poweroff rd.shell=0
# cat /proc/meminfo | grep -i hugepage
AnonHugePages: 0 kB
ShmemHugePages: 0 kB
HugePages_Total: 2048
HugePages_Free: 2048
HugePages_Rsvd: 0
HugePages_Surp: 0
Hugepagesize: 2048 kB
临时修改修改内核参数的方式。
# echo 1024 > /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages
# cat /proc/meminfo |grep Huge
AnonHugePages: 0 kB
ShmemHugePages: 0 kB
HugePages_Total: 1024
HugePages_Free: 1024
HugePages_Rsvd: 0
HugePages_Surp: 0
Hugepagesize: 2048 kB
对于多节点的NUMA实例,需要显式的设定不同的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
当然,上述方式重启丢失需要重新设置,可以通过sysctl.conf配置来配置持久生效。
# echo "vm.nr_hugepages=512" >> /etc/sysctl.conf
# sysctl -p
# cat /proc/meminfo |grep Huge
AnonHugePages: 0 kB
ShmemHugePages: 0 kB
HugePages_Total: 512
HugePages_Free: 512
HugePages_Rsvd: 0
HugePages_Surp: 0
Hugepagesize: 2048 kB
针对1G hugepages,只能通过配置内核启动参数的方式进行设置。后续的操作会使用1G hugepage来进行。
首先修改/etc/default/grub,增加GRUB_CMDLINE_LINUX=”default_hugepagesz=1G hugepagesz=1G hugepages=4”
# grub2-mkconfig -o /boot/grub2/grub.cfg
重新启动实例。可以查看设置已经生效。
# cat /proc/cmdline
BOOT_IMAGE=/boot/vmlinuz-4.14.152-127.182.amzn2.x86_64 root=UUID=e8f49d85-e739-436f-82ed-d474016253fe ro default_hugepagesz=1G hugepagesz=1G hugepages=4 console=tty0 console=ttyS0,115200n8 net.ifnames=0 biosdevname=0 nvme_core.io_timeout=4294967295 rd.emergency=poweroff rd.shell=0
# cat /proc/meminfo |grep -i hugepage
AnonHugePages: 0 kB
ShmemHugePages: 0 kB
HugePages_Total: 4
HugePages_Free: 4
HugePages_Rsvd: 0
HugePages_Surp: 0
Hugepagesize: 1048576 kB
3.2 安装和配置DPDK
配置编译和使用所需的系统环境。
# yum groupinstall "development tools" -y
# yum install python3 numactl numactl-devel -y
DPDK可以使用meson和ninjia来进行配置,构建和安装,如果使用这种方式请首先安装meson和ninjia。
# pip3 install meson ninja
下面的说明中,我们使用makefile而并没有使用meson和ninjia进行安装。下载DPDK包。
# wget http://fast.dpdk.org/rel/dpdk-19.11.tar.xz
# tar Jxvf dpdk-19.11.tar.xz
# cd dpdk-19.11/
配置和安装。
# export RTE_SDK=/root/dpdk-19.11
# export RTE_TARGET=x86_64-native-linuxapp-gcc
# make config install T=$RTE_TARGET DESTDIR=$RTE_SDK
另外,DPDK提供了安装配置脚本dpdk-19.11/usertools/dpdk-setup.sh,我们也可以使用该脚本进行安装并可以进行后续的配置操作。
基于脚本配置方式如下。
# ./usertools/dpdk-setup.sh
------------------------------------------------------------------------------
RTE_SDK exported as /root/dpdk-19.11.script
------------------------------------------------------------------------------
----------------------------------------------------------
Step 1: Select the DPDK environment to build
----------------------------------------------------------
[1] arm64-armada-linuxapp-gcc
…………
…………
[37] x86_64-native-linuxapp-clang
[38] x86_64-native-linuxapp-gcc
[39] x86_64-native-linuxapp-icc
[40] x86_64-native-linux-clang
[41] x86_64-native-linux-gcc
[42] x86_64-native-linux-icc
[43] x86_x32-native-linuxapp-gcc
[44] x86_x32-native-linux-gcc
----------------------------------------------------------
Step 2: Setup linux environment
----------------------------------------------------------
[45] Insert IGB UIO module
[46] Insert VFIO module
[47] Insert KNI module
[48] Setup hugepage mappings for non-NUMA systems
[49] Setup hugepage mappings for NUMA systems
[50] Display current Ethernet/Baseband/Crypto device settings
[51] Bind Ethernet/Baseband/Crypto device to IGB UIO module
[52] Bind Ethernet/Baseband/Crypto device to VFIO module
[53] Setup VFIO permissions
----------------------------------------------------------
Step 3: Run test application for linux environment
----------------------------------------------------------
[54] Run test application ($RTE_TARGET/app/test)
[55] Run testpmd application in interactive mode ($RTE_TARGET/app/testpmd)
----------------------------------------------------------
Step 4: Other tools
----------------------------------------------------------
[56] List hugepage info from /proc/meminfo
----------------------------------------------------------
Step 5: Uninstall and system cleanup
----------------------------------------------------------
[57] Unbind devices from IGB UIO or VFIO driver
[58] Remove IGB UIO module
[59] Remove VFIO module
[60] Remove KNI module
[61] Remove hugepage mappings
[62] Exit Script
安装完毕之后,创建的目标目录内会包括了所有的lib库,poll-mode驱动和header文件,用以后续构建基于DPDK的应用。此外,该目录还包括一些内置编译完成可用的DPDK应用,可以用于测试。
接下来,我们需要加载相应的驱动来替代默认的ena驱动,以支持DPDK。ENA网卡可以支持UIO模式或者VFIO模式的DPDK驱动,linux的内核已经包含了标准的uio_pic_generic驱动,而刚刚我们编译也获得了DPDK提供的igb_uio驱动,uio_pic_generic对于Virtual functions的支持会有限制,通常在UIO模式下,我们会使用igb_uio。
# modprobe uio
# insmod /root/dpdk-19.11/x86_64-native-linuxapp-gcc/kmod/igb_uio.ko wc_activate=1
相对来说,VFIO会是一个更加安全和健壮的选择,但是VFIO需要依赖于IOMMU,并且也不支持创建Virtual functions,同时还需要设置所需的权限来运行非特权用户的DPDK应用。当在不支持IOMMU的环境中,比如我们使用的EC2 C5.2xlarge环境,想要使用VIFO时,也会工作在类似UIO的非安全环境。另外,你可能需要使用AWS的patch https://github.com/amzn/amzn-drivers/tree/master/userspace/dpdk来重编译vfio-pci,以避免可能的性能问题或支持某些特性比如write combining。如下为直接加载vfio-pci模块并配置权限。
# modprobe vfio-pci
# chmod a+x /dev/vfio
# chmod 0666 /dev/vfio/*
对于大部分实例来说,并不支持IOMMU,需要确保驱动的noiommu参数被设置。
# echo 1 > /sys/module/vfio/parameters/enable_unsafe_noiommu_mode
对于其他一些实例来说,比如 i3.metal,可以支持IOMMU,需要在内核参数指定。通过类似的方法,修改grub2启动设定。在GRUB_CMDLINE_LINUX中追加iommu=1 intel_iommu=on。然后重新生成引导参数文件。
# grub2-mkconfig > /boot/grub2/grub.cfg
重启即可生效。
3.3 挂载hugepagefs
之前的操作已经完成了hugepages内存的预留,要使得DPDK能够使用这些内存,还需要如下步骤的配置和操作。
对于2M hugepage来说。
# mkdir /mnt/huge
# mount -t hugetlbfs nodev /mnt/huge
或者通过修改/etc/fatab来持久化设置挂载点。
# echo “nodev /mnt/huge hugetlbfs defaults 0 0” >> /etc/fstab
对于1G hugepage,必须要指定pagesize作为挂载参数,本文的操作采用此方式。
# mkdir /mnt/huge_1gb
# echo “nodev /mnt/huge_1gb hugetlbfs pagesize=1GB 0 0” >> /etc/fstab
3.4 绑定ENA设备到UIO或者VFIO模块
这里以UIO模块为例。
首先,我们在测试的实例上增加一个新的ENI网口来配置DPDK。只需在控制台新增网络接口,并附加到正在使用的实例上。如下所示。
之后,我们可以在实例内查看到这块新加入的设备。
# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc mq state UP group default qlen 1000
link/ether 0a:ff:5f:49:42:c6 brd ff:ff:ff:ff:ff:ff
inet 172.31.9.193/20 brd 172.31.15.255 scope global dynamic eth0
valid_lft 2820sec preferred_lft 2820sec
inet6 fe80::8ff:5fff:fe49:42c6/64 scope link
valid_lft forever preferred_lft forever
3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc mq state UP group default qlen 1000
link/ether 0a:a1:03:74:08:90 brd ff:ff:ff:ff:ff:ff
inet 172.31.3.226/20 brd 172.31.15.255 scope global dynamic eth1
valid_lft 3095sec preferred_lft 3095sec
inet6 fe80::8a1:3ff:fe74:890/64 scope link
valid_lft forever preferred_lft forever
我们将eth1绑定到igb_uio模块,DPDK提供的dpdk-devbind.py脚本可以用来进行绑定,解绑或者状态查看等操作(或者使用dpdk-setup.sh脚本来进行配置)。当前两个ENA设备均使用的ena驱动。
# /root/dpdk-19.11/usertools/dpdk-devbind.py --status
Network devices using kernel driver
===================================
0000:00:05.0 'Elastic Network Adapter (ENA) ec20' if=eth0 drv=ena unused=igb_uio *Active*
0000:00:06.0 'Elastic Network Adapter (ENA) ec20' if=eth1 drv=ena unused=igb_uio *Active*
接下来,将设备 eth1,0000:06:00.0绑定到 igb_uio 驱动。
首先关闭eth1端口
绑定到igb_uio驱动。
/root/dpdk-19.11/usertools/dpdk-devbind.py --bind=igb_uio 06:00.0
也可以使用设备名:
/root/dpdk-19.11/usertools/dpdk-devbind.py --bind=igb_uio eth1
确认是否正常绑定。
# /root/dpdk-19.11/usertools/dpdk-devbind.py --status
Network devices using DPDK-compatible driver
============================================
0000:00:06.0 'Elastic Network Adapter (ENA) ec20' drv=igb_uio unused=ena
Network devices using kernel driver
===================================
0000:00:05.0 'Elastic Network Adapter (ENA) ec20' if=eth0 drv=ena unused=igb_uio *Active*
如果需要恢复eth1使用原有的内核ena驱动,类似的简单进行如下操作。
/root/dpdk-19.11/usertools/dpdk-devbind.py --bind=ena 06.00.0
4 测试DPDK环境
请注意前面设置的RTE_SDK和RTE_RARGET环境变量是否正确。编译一个内嵌的helloworld应用来进行测试。
# cd /root/dpdk-19.11examples/helloworld
# make
# ./build/helloworld
EAL: Detected 8 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:00:05.0 on NUMA socket -1
EAL: Invalid NUMA socket, default to 0
EAL: probe driver: 1d0f:ec20 net_ena
EAL: PCI device 0000:00:06.0 on NUMA socket -1
EAL: Invalid NUMA socket, default to 0
EAL: probe driver: 1d0f:ec20 net_ena
Message from syslogd@ip-172-31-9-193 at Dec 18 07:30:15 ...
kernel:do_IRQ: 6.229 No irq handler for vector
EAL: PCI device 0000:00:07.0 on NUMA socket -1
EAL: Invalid NUMA socket, default to 0
EAL: probe driver: 1d0f:ec20 net_ena
hello from core 1
hello from core 2
hello from core 3
hello from core 4
hello from core 5
hello from core 6
hello from core 7
hello from core 0
5 结论
DPDK可以支持AWS ENA和82599网络设备,可以在AWS多种不同的实例上部署DPDK和运行支持DPDK的应用,从而对多种应用场景,比如电信行业虚拟化分组核心等提供更全面的支撑。
另外,DPDK也已经和容器网络有了良好集成,这体现在原生的Docker网络上,也体现在包括kubernetes在内的诸多平台使用的CNI实现中,比如contiv和Vhostuser等都可以支持DPDK。
在后续的文章中,我们会针对AWS上的Docker和容器编排平台使用DPDK来进行进一步说明。
6 参考链接
http://www.dpdk.org
https://github.com/amzn/amzn-drivers/tree/master/userspace/dpdk
https://docs.thinkwithwp.com/AWSEC2/latest/UserGuide/index.html
https://github.com/DPDK/dpdk
本篇作者