DPDK程序配置:仅保留UDP/TCP报文头入队(ConnectX-6 DX网卡)
解决方案:DPDK中仅保留报文头存储的实现方案
一、硬件层面:利用网卡截断功能直接接收报文头
你的Mellanox ConnectX-6 DX网卡支持硬件数据包截断,配合rte_flow可以直接让网卡只传输指定长度的报文头(比如前64字节,足以覆盖以太网+IP+TCP/UDP头部),数据部分直接在网卡端丢弃,从根源上减少内存占用和主机处理开销。
实现步骤:
在创建rte_flow过滤UDP/TCP的规则时,添加RTE_FLOW_ACTION_TYPE_TRUNCATE动作,指定截断长度:
struct rte_flow_action_truncate truncate_cfg = { .length = 64, // 根据实际头部长度调整,确保覆盖L2-L4头部 }; struct rte_flow_action actions[] = { { .type = RTE_FLOW_ACTION_TYPE_TRUNCATE, .conf = &truncate_cfg }, { .type = RTE_FLOW_ACTION_TYPE_ACCEPT }, }; // 后续搭配UDP/TCP的匹配规则完成flow创建
二、软件层面:处理mbuf时裁剪或分离数据部分
如果硬件截断不满足需求,可在软件层面对接收后的mbuf做处理:
1. 直接裁剪mbuf保留头部
用rte_pktmbuf_trim()调整mbuf的长度,只保留所需头部,多余的数据部分会被标记为无效,释放mbuf时完整归还内存池:
uint32_t needed_header_len = 64; struct rte_mbuf *rx_mbufs[BURST_SIZE]; uint16_t nb_rx = rte_eth_rx_burst(port_id, queue_id, rx_mbufs, BURST_SIZE); for (int i = 0; i < nb_rx; i++) { struct rte_mbuf *mbuf = rx_mbufs[i]; if (rte_pktmbuf_pkt_len(mbuf) > needed_header_len) { int ret = rte_pktmbuf_trim(mbuf, rte_pktmbuf_pkt_len(mbuf) - needed_header_len); if (ret < 0) { rte_pktmbuf_free(mbuf); continue; } } // 将裁剪后的mbuf存入队列 enqueue_to_custom_queue(mbuf); }
2. 用Indirect Mbuf分离元数据与头部
如果需要保留原始mbuf的元数据(如端口号、RSS哈希值)但只存头部,可创建间接mbuf复制头部后释放原始mbuf:
uint32_t needed_header_len = 64; struct rte_mempool *indirect_mempool = /* 提前初始化的间接mbuf内存池 */; for (int i = 0; i < nb_rx; i++) { struct rte_mbuf *orig_mbuf = rx_mbufs[i]; struct rte_mbuf *header_mbuf = rte_pktmbuf_indirect_alloc(indirect_mempool); if (!header_mbuf) { rte_pktmbuf_free(orig_mbuf); continue; } // 复制头部数据到间接mbuf rte_memcpy(rte_pktmbuf_mtod(header_mbuf, void*), rte_pktmbuf_mtod(orig_mbuf, void*), needed_header_len); // 设置间接mbuf的长度字段 header_mbuf->pkt_len = needed_header_len; header_mbuf->data_len = needed_header_len; rte_pktmbuf_free(orig_mbuf); enqueue_to_custom_queue(header_mbuf); }
三、结合RX Callback优化流程
基于你使用的收发包回调示例,可把裁剪逻辑放到RX回调中,实现接收时自动处理:
static uint16_t rx_header_only_callback(uint16_t port_id, uint16_t queue_id, struct rte_mbuf **mbufs, uint16_t nb_bufs, void *user_param) { uint32_t header_len = *(uint32_t*)user_param; uint16_t valid_cnt = 0; for (int i = 0; i < nb_bufs; i++) { struct rte_mbuf *mbuf = mbufs[i]; if (rte_pktmbuf_pkt_len(mbuf) > header_len) { if (rte_pktmbuf_trim(mbuf, rte_pktmbuf_pkt_len(mbuf) - header_len) < 0) { rte_pktmbuf_free(mbuf); mbufs[i] = NULL; continue; } } mbufs[valid_cnt++] = mbuf; } return valid_cnt; } // 初始化时注册回调 uint32_t target_header_len = 64; rte_eth_rx_callback_register(port_id, queue_id, rx_header_only_callback, &target_header_len);
四、关键注意事项
- ConnectX-6 DX完全支持
rte_flow的TRUNCATE动作,需确保DPDK版本≥20.11(建议用LTS版本),mlx5驱动正常加载。 - 若用硬件截断,可配置更小的mbuf数据缓冲区(比如128字节),进一步节省内存。
- 性能优先级:硬件截断 > 软件裁剪 > 间接mbuf(间接mbuf存在内存拷贝,适合需保留元数据的场景)
内容的提问来源于stack exchange,提问作者Daniel Gilkarov
相关产品推荐
相关产品推荐

