dpdk pmd 驱动中多个收包函数使用的限制
dpdk pmd 驱动一般都提供多个收发包函数,每一个都有自己的适用场景。实际使用中,为了得到更高的性能,常常需要开启向量收发包函数来进行优化。这一选择过程对用户不可见,它隐藏在 pmd 驱动内部并依赖用户接口配置内容。
由于不同版本向量收发包函数实现不一致,最终驱动是否会选择使用向量收发包函数在不同版本的表现不太一样。
本文将从基础的概念开始,描述老版本收发包函数使用的限制,以及新版本对此限制的改善,引出这其中存在的几个重要问题。
两种类型 mbuf 结构
- One segment mbuf
- multi-segmented mbuf
在 dpdk 收发包问题案例:使用不匹配的收发包函数触发的不收包问题定位 这篇博文中,我描述了使用不匹配的收发包函数导致收发包异常的问题。核心问题是收到 jumbo frame 报文时网卡硬件对报文处理的区别,导致报文在 dpdk 中的组织形式不同,而不同的组织形式又依赖特定的底层驱动收发包函数类型。
当收包函数使用 multi-segmented 方式组织 jumbo frame,而发包函数却不支持 multi-segmented mbuf 发出 jumbo frame 包时,就会产生异常。
下文中收发包函数的分类,其根源就来自于上述 mbuf 的不同组织形式。同时要说明的是这两种组织形式是由【网卡硬件工作行为】决定的,而非软件结构。
dpdk pmd 驱动收发包函数类别
收包函数分类:
-
普通收包函数
每次申请一个 mbuf,处理一个填充了报文的描述符并重填地址,一个报文对应一个 mbuf,不支持 multi-segmented mbuf 链式收包
-
bulk_alloc 收包函数
设置一个描述符回收数量门限,当空闲描述符超过设定数量后批量申请 mbuf 并重填描述符,一个报文对应一个 mbuf,不支持 mbuf 链式收包,
-
scatter 收包函数
每次申请一个 mbuf,处理一个填充了报文的描述符并重填地址,支持将 jumbo frame 包使用 mbuf 链组织起来,不受限于单个 mbuf dataroom 长度限制,可以称为聚合收包函数
-
vec 向量收包函数
- 普通收包函数的 vec 实现
- scatter 收包函数的 vec 实现
发包函数分类:
-
普通发包函数
支持 tx offload 配置,支持链式 mbuf 发包
-
简单发包函数
不支持 tx offload 配置,直接使用 mbuf 相关字段填充描述符,不支持链式 mbuf 发包
-
向量发包函数
简单发包函数的向量实现
i40e 驱动收发包函数实例(摘自 22 年 dpdk 源代码)
收包函数及其类别定义:
static const struct {
eth_rx_burst_t pkt_burst;
const char *info;
} i40e_rx_burst_infos[] = {
{ i40e_recv_scattered_pkts, "Scalar Scattered" },
{ i40e_recv_pkts_bulk_alloc, "Scalar Bulk Alloc" },
{ i40e_recv_pkts, "Scalar" },
#ifdef RTE_ARCH_X86
#ifdef CC_AVX512_SUPPORT
{ i40e_recv_scattered_pkts_vec_avx512, "Vector AVX512 Scattered" },
{ i40e_recv_pkts_vec_avx512, "Vector AVX512" },
#endif
{ i40e_recv_scattered_pkts_vec_avx2, "Vector AVX2 Scattered" },
{ i40e_recv_pkts_vec_avx2, "Vector AVX2" },
{ i40e_recv_scattered_pkts_vec, "Vector SSE Scattered" },
{ i40e_recv_pkts_vec, "Vector SSE" },
#elif defined(RTE_ARCH_ARM64)
{ i40e_recv_scattered_pkts_vec, "Vector Neon Scattered" },
{ i40e_recv_pkts_vec, "Vector Neon" },
#elif defined(RTE_ARCH_PPC_64)
{ i40e_recv_scattered_pkts_vec, "Vector AltiVec Scattered" },
{ i40e_recv_pkts_vec, "Vector AltiVec" },
#endif
};
可以将 Scalar 翻译为标量,它是一个一维的量,在这里表示一次处理单个描述符,而向量是一个多维的量,在这里表示一次处理多个描述符。
Scalar 收包函数对应上文描述的普通收包函数,上述代码中,i40e 的收包函数支持不同架构的多种 vec 指令实现,每一种实现按照是否支持 scatter 收包方式分为两种,与上文的描述一致。
此版本驱动实现中,要使用向量收包函数,需要满足如下几个条件:
- header split offload 与 vlan_extend offload 功能不能配置
- 收包队列的描述符数量必须是 2 的幂
发包函数及其类别定义:
static const struct {
eth_tx_burst_t pkt_burst;
const char *info;
} i40e_tx_burst_infos[] = {
{ i40e_xmit_pkts_simple, "Scalar Simple" },
{ i40e_xmit_pkts, "Scalar" },
#ifdef RTE_ARCH_X86
#ifdef CC_AVX512_SUPPORT
{ i40e_xmit_pkts_vec_avx512, "Vector AVX512" },
#endif
{ i40e_xmit_pkts_vec_avx2, "Vector AVX2" },
{ i40e_xmit_pkts_vec, "Vector SSE" },
#elif defined(RTE_ARCH_ARM64)
{ i40e_xmit_pkts_vec, "Vector Neon" },
#elif defined(RTE_ARCH_PPC_64)
{ i40e_xmit_pkts_vec, "Vector AltiVec" },
#endif
};
上述发包函数实现中,只有 i40e_xmit_pkts 支持 multi-segmented mbuf 发送以及硬件 tx offload 功能,其它的发包函数均不支持 multi-segmented mbuf 以及硬件 tx offload。
此版本驱动中要使用向量发包函数,不能配置除了 RTE_I40E_TX_MAX_FREE_BUF_SZ 之外的其它硬件 tx offload 卸载功能,且发包队列的 tx_rs_thresh 不能大于 RTE_I40E_TX_MAX_FREE_BUF_SZ(64)。
i40e 驱动收发包函数实例(摘自 dpdk-16.04 源代码)
此版本驱动实现中,要使用向量收包函数,需要满足如下几个条件:
- 不能配置 hw_vlan_strip 与 hw_vlan_extend (可选)
- 不能配置 fdir
- 不能配置 hw_ip_checksum 与 header_split
与高版本相比,16.04 中使用向量收包函数的限制更多一些。例如 hw_ip_checksum 这个配置。开启它的本意是为了优化性能,但配置了后在此版本中就不能使用向量收包函数,性能反而要下降,而在高版本中却没有这一限制。
使用向量发包函数的条件基本一致。
dpdk pmd 驱动内部设置收发包函数的位置
在设备 probe 驱动的过程中会设置一次收发包函数,此时由于用户尚未配置接口参数,使用的收发包函数为默认值。
当用户配置了接口参数后,在调用 rte_eth_dev_start up 接口时时驱动会检查依赖条件并重新设置收发包函数。
总结
本文从描述 dpdk 支持的多种收发包函数的核心区别开始,想说明不同版本向量收发包函数使用的限制仅仅是【软件实现】的限制,这些限制随着技术的演进会被逐渐被打破。
但长期维护某个特定版本很容易让维护者形成“不能”的固定思维,很容易得出“不支持”的结论。以成长的思维来看,这个结论今天能够成立,明天却不一定能成立。我相信技术发展的方向应该是以更小的限制同时集成多个技术的优点,这点也不仅限于 IT 领域,其它领域也有类似的过程。