友情提示:全文5000多文字,预计阅读时间15分钟
文章源自现网实践对支撑及用户态/内核态网络报文交换场景的认识,欢迎有Linux/FreeBSD内核、网络协议栈、DPDK优化实践经验的同学留言探讨~
DPDK简介
Intel DPDK全称为Intel Date Plane Development Kit, Intel处理器架构下用户空间高效的数据包处理提供了库函数和驱动的支持,它不同于 Linux/FreeBSD等操作系统以通用性设计为目的,而是专注于网络应用中网络报文的高性能处理。操作系统与DPDK应用程序网络处理如下图所示:
Flow Bifurcation on Intel Ethernet Controller X710/XL710 --Jingjing Wu; Anjali
左侧slowpath
在Linux/FreeBSD等操作系统,网卡通过硬件中断方式通知内核驱动有网络报文到达,内核响应硬件中断处理,进一步移交由网卡驱动硬件中断处理唤醒网卡驱动收包例程(中断上半部),内核调度网卡驱动收包例程执行网络报文接收处理(中断下半部)。在经由一系列的内核网络协议处理流程后,最终由位于用户态的应用程序处理网络报文内容,完成网络报文接收过程
在整个网络报文接收过程涉及一系列的软硬件中断抢占处理、内存拷贝/缺页异常处理、系统调用(用户态/内核上下文切换)、应用程序进程切换等CPU处理资源开销,直接影响操作系统网络报文的处理能力,进而影响网络性能。
右侧fastpath
DPDK采用Kernel Bypass方式实现, 在用户空间实现了一套数据平面进行网络报文的收发与处理。DPDK应用程序基于大页内存、核绑定、用户态轮询机制的驱动实现,通过重载网卡驱动进行网络报文收发,直接交由上层应用程序处理。从而避免操作系统内核/用户态的网络报文拷贝、线程调度、系统调用、中断等CPU处理资源损耗,以达到DPDK应用程序网络处理高性能目的。对于操作系统而言,DPDK应用程序只是一个普通的用户态进程,与普通应用程序并无差别。
用户态/内核态交换网络报文交换
在非SRIOV或虚拟机场景网卡被用户态DPDK应用程序接管,该网卡所有网络报文将由用户态的DPDK应用程序接收处理。对于部分控制面、转发面分离的高性能DPDK应用实现中,存在采用fastpath、slowpath相结合进行网络报文收发处理场景,如DPDK+Nginx+Keepalive高可用场景, 如下图所示:
DPDK+Nginx+Keepalive流量
基于DPDK用户态TCP/IP协议栈与Nginx相结合,通过fastpath提供高性能HTTP应用服务。Keepalive应用程序沿用操作系统slowpath协议栈进行网络报文收发处理逻辑,通过VRRP协议提供DPDK+Nginx 高可用场景下的VIP主备切换控制。在用户态/内核态交换网络报文交换场景,KNI是DPDK引入的实现方式之一。
KNI
KNI(Kernel NIC Interface)是DPDK用于用户态与内核态协议栈网络报文交换的一种机制。其实现原理通过共享内存方式实现用户态DPDK应用程序与内核态网络协议栈网络报文传递, 采用注册KNI虚拟网络设备接口方式实现网络报文收发以达到利用内核协议栈,最终将网络报文转发至上层应用程序的目的。如下图所示:
Components of a DPDK KNI Application
KNI注册的虚拟网络设备接口兼容支持Linux网络工具如ethtool、ifconfig 、tcpdump对网络接口的配置管理。对内核网络协议栈而言,KNI虚拟网络设备接口与普通网卡网络设备接口进行网络报文收发处理类似。
KNI 实现用户态/内核态交换网络报文交换由两部分组成: 内核态RTE_KNI模块、用户态KNI网络报文收发接口, 如下图所示:
Packet Flow via mbufs in the DPDK KNI
左侧内核态RTE_KNI模块
内核态RTE_KNI模块负责创建初始化KNI虚拟网络设备操作接口与注册网络报文接收内核线程实现网络报文收发。
KNI网络报文接收内核线程负责从rx_q网络报文接收队列读取网络报文,移交内核网络协议栈入口netif_rx进一步处理。
KNI虚拟网络设备kni_net_tx接口负责处理来自内核网络协议栈的网络报文发送请求,最终将网络报文移交至tx_q,后续由用户态DPDK应用程序网络报文发送线程处理,完成网络报文发送。
KNI网络报文接收内核线程存在单线程、多线程两种模式选择,可在加载内核模块时指定。在未指定情况下默认单线程模式,加载KNI内核模块设置线程模式如下:
insmod kmod/rte_kni.ko kthread_mode=single或kthread_mode=multiple
单线程、多线程两种模式主要区别在于:
单线程模式:KNI内核模块所有KNI虚拟网络接口创建共享一个内核线程进行网络报文接收处理。
多线程模式:KNI内核模块为每个KNI虚拟网络接口创建单独一个内核线程进行网络报文接收处理。在多个KNI虚拟网络接口场景建议采用多线程模式,以提高单个KNI虚拟网络设备网络接收性能。
右侧用户态KNI网络报文收发接口
用户态KNI网络报文收发主要涉及两个接口:
rte_kni_tx_burst接口:在用户态DPDK应用程序网络报文接收处理线程通过rte_eth_rx_burst接口从网卡完成网络报文读取后,负责将需移交至内核态的网络报文添加至rx_q网络报文接收队列,后续由内核态KNI网络报文接收线程处理。
rte_kni_rx_burst接口:在用户态DPDK应用程序网络报文发送处理线程负责读取tx_q网络报文发送队列中待发送网络报文,通过rte_eth_tx_burst网卡发送接口将网络报文发送。
遇到的问题
现网环境虚拟化计算节点复用一对业务万兆光口来承载两种流量:1)overlay虚拟机流量 2)underlay块存储流量。DPDK-OVS接管业务万兆光口后,通过DPDK KNI注册虚拟化计算节点存储虚拟网络接口,实现虚拟化计算节点通过存储虚拟网络接口访问块存储。在虚拟机内部读写块设备,虚拟化计算节点KNI虚拟网络设备存储网络接口统计计数出现大量TX dropped。
KNI虚拟网络设备内核态网络报文发送接口kni_net_tx处理过程简述如下:
检查网络报文长度及alloc_q/tx_q无可用共享内存缓存,否则释放内核态网络报文skb即跳转至drop丢包处理,并更新KNI虚拟网络设备TX dropped统计信息。
从alloc_q队列获取用户态 mbuf网络报文缓存,通过内存物理地址/虚拟地址转换后完成skb至 mbuf 网络报文内容复制,最终将mbuf添加至tx_q,移交由用户态DPDK应用网络报文发送线程处理,并释放内核态网络报文skb。
初步分析与alloc_q/tx_q无可用共享内存缓存有关。具体源码实现请查阅:dpdk/kernel/linux/kni/kni_net.c KNI虚拟网络设备网络报文发送kni_net_tx接口。
问题模拟复现
通过DPDK examples/kni例子,模拟现网KNI网络报文转发处理过程。简述如下:
在完成大页内存设置/核隔离/网卡绑定等DPDK初始化环境后,启动DPDK KNI
/root/dpdk-19.08/examples/kni/x86_64-native-linuxapp-gcc/kni -l 0-2 -- -P -p 0x1 --config="(0,1,2,0)"
参数说明:
-l 指定DPDK EAL lcore参数即逻辑CPU ID或掩码
-p 指定DPDK port
--config 指定参数(port,lcore_rx,lcore_tx,lcore_kthread)涉及port、KNI网络报文接收线程CPU ID、KNI网络报文发送线程CPU ID、内核KNI网络报文接收线程绑定CPU ID
详细请参见: DPDK Sample Applications User Guides Release 19.08 Kernel NIC Interface Sample Application小节
配置KNI虚拟网络设备地址:
ifconfig vEth0_0 *.*.*.* netmask *.*.*.* up
查看用户态KNI网络报文收发统计计数:
kill -SIGUSR1 8072
客户端通过KNI虚拟网络设备网口运行iperf模拟内核态/用户态转发流量:
iperf3 -c *.*.*.* -t 10
服务端运行iperf接收
iperf3 –s
查看用户态KNI网络报文收发统计计数,无丢包统计
查看内核态KNI网络设备统计计数出现TX dropped
Iperf服务端接收速率在33.4Mb/s, 因KNI虚拟网络设备丢包导致网络性能较差。
改进思路
1)DPDK KNI alloc_q mbuf缓存参数调整
通过对问题模拟复现基本确定内核态的RTE_KNI模块kni_net_tx 在发送网络报文时因kni_fifo_count(kni->alloc_q) == 0触发丢包处理逻辑,导致TCP报文重传间接影响网络性能。
DPDK KNI用户态在对alloc_q 填充在初始化阶段由rte_kni_alloc调用kni_allocate_mbufs进行初始化填充。运行时由用户态DPDK应用程序网络报文发送处理线程调用rte_kni_rx_burst从tx_q获取待发送网络报文后回调kni_allocate_mbufs进行alloc_q 回填操作,以确保内核态kni_net_tx在网络报文发送时存在可用的mbuf进行网络报文内容复制传递至用户态处理。在kni_allocate_mbufs在极端情况下以申请MAX_MBUF_BURST_NUM数量mbuf回填alloc_q。
在调整MAX_MBUF_BURST_NUM后重新编译DPDK KNI验证,如下:
客户端通过KNI虚拟网络设备网口运行iperf模拟内核态/用户态转发流量:
iperf3 -c *.*.*.* -t 10
服务端运行iperf接收
iperf3 –s
查看用户态KNI网络报文收发统计计数:
kill -SIGUSR1 8072
查看内核态KNI网络设备统计计数无TX dropped
Iperf服务端接收速率在652Mb/s相对未调整前的33.4Mb/s, iperf吞吐接近20倍的差距。
2) KNI虚拟网络设备内核网络报文接收线程、KNI用户态进程、iperf进程核分离
KNI虚拟网络设备内核网络报文接收线程、KNI用户态进程、iperf进程核分离主要是验证上述KNI内核态/用户态网络报文交换吞吐是否存在改善空间及进程切换对网络吞吐性能的影响。
通过【pidstate –t –p 进程ID 1】采样,查看KNI虚拟网络设备内核网络报文接收线程、KNI用户态进程、iperf进程CPU运行状态及占用率:
KNI虚拟网络设备内核网络报文接收线程、KNI用户态进程、iperf进程均在争取CPU0处理器资源【注:模拟环境虚拟机CPU0~CPU3,其中CPU1,CPU2己进行核隔离用于用户态DPDK/KNI收发线程】。
taskset –pc 3 52722将用户态DPDK/KNI master lcore调整至CPU3,避免其与KNI虚拟网络设备内核网络报文接收线程、iperf进程争取CPU0处理器资源。
客户端通过KNI虚拟网络设备网口运行iperf模拟内核态/用户态转发流量:
iperf3 -c *.*.*.* -t 10
服务端运行iperf接收
iperf3 –s
Iperf服务端接收速率在1.23Gb/s。从未调整前的33.4Mb/s至1.23Gb/s或许仍有进一步改进的空间,有待尝试验证。
后续
后续改进涉及底层处理逻辑修改及相关功能实现优化,改进思路简述如下:
1)DPDK KNI虚拟网络设备引入多队列多收包线程
DPDK KNI虚拟网络设备用户态与内核态网络报文传递采用单FIFO共享队列实现,针对不同场景可考虑多FIFO共享队列与多收包线程方式,充分利用多核CPU资源以达到优化改善网络吞吐性能的目的。
2)DPDK网卡特性透穿至KNI虚拟网络设备
现网采用DPDK-OVS VHOST-USER与Qemu协商实现网卡特性透传至虚拟机网卡涉及如:TSO、UFO、GSO、CheckSum Offload等。利用网卡特性释放虚拟机在处理网络报文时的CPU处理资源消耗,达到提高虚拟机网络吞吐性能的目的,并进一步优化DPDK-OVS KNI处理转发效率。对于DPDK KNI虚拟网络设备可借鉴该实现原理,提高KNI内核态与用户态网络报文交换网络转发性能。
3)其它改进措施待研究,欢迎提供参考建议
上述均在虚拟机环境进行模拟验证,涉及环境变更可能存在偏差,改进思路仅供参考。
参考说明
DPDK之PMD原理
https://cloud.tencent.com/developer/article/1411982
绝对干货!初学者也能看懂的DPDK解析
https://www.cnblogs.com/qcloud1001/p/9585724.html
DPDK之KNI原理
https://cloud.tencent.com/developer/article/1418603
DPDK内核模块KNI
http://www.freesion.com/article/148821991/
《DPDK Programmer's Guide Release 19.08》
《DPDK Sample Applications User Guides Release 19.08》
《Flow Bifurcation on Intel Ethernet Controller X710/XL710 --Jingjing Wu; Anjali Singhai》
-End:)
往期精选
1、干货分享 | 基于RocketMQ构建MQTT集群系列(1)—从MQTT协议和MQTT集群架构说起
2、干货分享 | 虚拟化性能提升之本地热迁移
3、干货分享 | 时序数据库Graphite在BC-DeepWatch中的设计与使用