3. 数据包接收路径的核间分流设计
在 Marvell DPU 这类多核 SoC 上,收包性能的上限往往不取决于单核处理速度,而取决于“包被分到了哪个核、这个核是否已经过载”。本节围绕 RX 队列、RSS 哈希、流表分发与核亲和性四条主线,给出一个可落地的多核收包分流调优方案。
一、先看清收包路径上的四个分流决策点
一个数据包从网口进入 DPU,到被某个 CPU 核上的应用处理,中间会经过多次“归属决策”。理解这些决策点的先后顺序,是调优的前提:
- RX 队列(RXQ):网卡按队列号把包写入不同的描述符环,队列是硬件层面的分流单元,也是后续所有分流的物理载体。
- RSS 哈希:网卡对包的五元组(源/目的 IP、源/目的端口、协议)做 Toeplitz 哈希,取低若干位映射到某个 RXQ,实现无状态流级分流。
- 流表分发:软件层(如 DPDK rte_flow、OVS 或自研流表)根据更细的规则把包定向到特定队列或特定核,用于处理需要状态跟踪的流。
- 核亲和性:通过中断亲和性(IRQ affinity)或轮询线程绑定(CPU affinity),把某个 RXQ 固定交给某个核,避免核间迁移带来的缓存失效。
这四者不是并列关系,而是“硬件先分、软件再分、最后绑定到核”的串联链路。调优的本质,是让这条链路上的每一级都尽量均匀、稳定、可预测。
二、RSS 哈希:决定“流”落到哪个队列
RSS 的核心是哈希函数与重定向表。以常见的 40 字节 RSS key 为例,网卡对选定的字段做 Toeplitz 哈希,得到一个 32 位结果,再取低 7 位(对应 128 个表项)查重定向表,得到最终队列号。调优时关注三点:
- 哈希域选择:默认 IPv4 TCP 使用“源IP+目的IP+源端口+目的端口”。如果业务中大量流的目的端口相同(如都访问 443),哈希熵会下降,可考虑加入 VLAN 或协议字段增加区分度。
- RSS key 一致性:多队列场景下,同一 key 才能保证同一条流始终落到同一队列。若中途更换 key,已建立的流会“跳队列”,导致乱序和状态丢失。
- 重定向表均衡:128 个表项要均匀映射到 N 个队列。若手工配置不当,可能出现某队列占用 40 个表项、另一队列只有 4 个,造成先天不均。
一个可参考的检查方法是:用 ethtool -x <ifname> 查看当前重定向表,统计每个队列被映射的表项数,偏差超过 ±20% 就应重新分配。
三、流表分发:处理 RSS 覆盖不了的场景
RSS 是无状态的,它无法保证“同一会话的双向包落到同一队列”,也无法处理需要精确匹配的隧道解封装、ACL 或负载均衡场景。这时需要流表分发介入。以 DPDK 为例,典型做法是:
/* 示例:将目的端口 8080 的流定向到 RXQ3 */
struct rte_flow_attr attr = { .ingress = 1, .group = 0 };
struct rte_flow_item pattern[] = {
{ .type = RTE_FLOW_ITEM_TYPE_ETH },
{ .type = RTE_FLOW_ITEM_TYPE_IPV4 },
{ .type = RTE_FLOW_ITEM_TYPE_TCP,
.spec = &(struct rte_flow_item_tcp){
.hdr.dst_port = rte_cpu_to_be_16(8080) } },
{ .type = RTE_FLOW_ITEM_TYPE_END },
};
struct rte_flow_action actions[] = {
{ .type = RTE_FLOW_ACTION_TYPE_QUEUE,
.conf = &(struct rte_flow_action_queue){ .index = 3 } },
{ .type = RTE_FLOW_ACTION_TYPE_END },
};
rte_flow_create(port_id, &attr, pattern, actions, &error);
流表分发的调优要点是规则优先级与命中率。规则越多,查表开销越大;应把高频流放在高优先级组(group 0),低频或兜底规则放到低优先级组。同时要监控流表 miss 计数,miss 率过高说明规则覆盖不足,包会退回 RSS 默认路径。
四、核亲和性:把队列“钉”在核上
队列分好了,还要决定由哪个核来收。两种主流模式:
- 中断模式:网卡收到包后触发中断,由绑定该中断的核处理。通过
/proc/irq/<irq>/smp_affinity设置亲和性,让每个 RXQ 的中断落到不同核。 - 轮询模式(PMD):核持续轮询 RXQ,无中断开销,适合高吞吐。此时用
rte_lcore与队列一一绑定,避免多核争抢同一队列。
亲和性配置的核心原则是“一队列一核、核不跨队列”。如果两个核同时轮询同一个 RXQ,会出现锁竞争和缓存行乒乓;如果一个核绑定多个高流量队列,则会成为瓶颈。建议先用 lscpu 确认物理核与超线程布局,再把 RXQ 绑定到物理核,避免同一物理核的两个超线程争抢。
cat /sys/class/net/<ifname>/device/numa_node 确认网卡所在节点。
五、一个可执行的调优闭环
把上述四点串起来,形成如下调优流程:
- 基线测量:用
sar -n DEV 1或 DPDKtestpmd观察各 RXQ 的收包速率,找出是否存在“零流量队列”或“过载队列”。 - 调整 RSS 重定向表:让表项均匀覆盖所有队列,偏差控制在 ±20% 以内。
- 补充流表规则:对哈希不均的“大象流”用 rte_flow 显式指定队列,把热点流打散。
- 绑定核亲和性:按“一队列一核”原则设置 IRQ affinity 或 PMD 线程绑定,并确认 NUMA 同节点。
- 复测与对比:重新测量各队列速率与 CPU 利用率,若最大队列与最小队列的速率比从 5:1 降到 1.5:1 以内,且无核利用率超过 80%,即可认为分流基本均衡。
实际工程中,建议把上述参数固化为一份配置基线:RSS key、重定向表、流表规则集、IRQ affinity 映射、NUMA 节点,每次变更后重新跑一遍基线测量。这样,多核收包分流就从“凭感觉调”变成了“有据可依的工程闭环”。