对应路线图:Phase 1 (Day 22-49) | DPDK 24.11.6 LTS | 内核对照 v6.6
代码出处:source/dpdk-stable-24.11.6/与source/linux-stable/,引用均标文件:行号
上一篇:P1-06 NUMA | 下一篇:P2-01 mTCP 论文精读(Phase 2 学到时发布)
一句话定位
多核数据面的第一个问题不是"怎么写快",而是"谁处理哪个流"。RSS 让硬件替你把流量平均分到 N 个核,且同一 5-tuple 永远落同一核——这是 per-core 免锁数据结构的物理前提。
RSS 流水线
- Toeplitz 哈希:NIC 对 5-tuple 做硬件哈希(rss_key 可编程),低 N 位经 RETA 表映射到 RX 队列 → 每队列一个 lcore。
- 哈希结果在收包时已灌进 mbuf(
ixgbe_rxtx.c:1943-1945,P1-05 已读):
if (likely(pkt_flags & RTE_MBUF_F_RX_RSS_HASH))
rxm->hash.rss = rte_le_to_cpu_32(
rxd.wb.lower.hi_dword.rss);对称 RSS:用户态协议栈的基石
普通 RSS 下,一个 TCP 连接双向流会 hash 到不同核——连接表必须跨核共享(加锁)。对称 RSS 用特殊 key 让"src→dst"与"dst→src"哈希值相同 → 双向流同核 → 每核连接表完全独立,零共享。这是 Phase 2 libmtcp-v3 和 P3 自研 LB 的 per-core conn table 基石。
P1 实验:testpmd --rxq=16 --rss 观察队列分布,再换对称 key 验证双向流落同队列。
rte_flow:不想平均,要精确控制
pattern + action + attr:pattern 描述匹配(ETH/IPV4/UDP + 5-tuple),action 定动作(QUEUE/RSS/DROP/COUNT/MARK),attr 定方向优先级。rte_flow_create 下发网卡(硬件 offload)或软件模拟。P1 实验:写一条 rule 把特定 5-tuple 导到指定队列;P2 的 SAI ACL 就是把 SAI 规则翻译成 rte_flow。
🐧 内核对照:RPS = 软件版 RSS
真实代码(net/core/dev.c:4651-4734,节选):
static int get_rps_cpu(struct net_device *dev, struct sk_buff *skb,
struct rps_dev_flow **rflowp)
{
// ...
flow_table = rcu_dereference(rxqueue->rps_flow_table);
map = rcu_dereference(rxqueue->rps_map);
if (!flow_table && !map)
goto done;
skb_reset_network_header(skb);
hash = skb_get_hash(skb);
if (!hash)
goto done;
sock_flow_table = rcu_dereference(rps_sock_flow_table);
if (flow_table && sock_flow_table) {
struct rps_dev_flow *rflow;
u32 next_cpu;
u32 ident;
/* First check into global flow table if there is a match. ... */
ident = READ_ONCE(sock_flow_table->ents[hash & sock_flow_table->mask]);
if ((ident ^ hash) & ~rps_cpu_mask)
goto try_rps;
next_cpu = ident & rps_cpu_mask;
// ...
/*
* If the desired CPU (where last recvmsg was done) is
* different from current CPU ..., switch if one of the
* following holds:
* - Current CPU is unset (>= nr_cpu_ids).
* - Current CPU is offline.
* - The current CPU's queue tail has advanced beyond the
* last packet that was enqueued using this table entry.
* This guarantees that all previous packets for the flow
* have been dequeued, thus preserving in order delivery.
*/
if (unlikely(tcpu != next_cpu) &&
(tcpu >= nr_cpu_ids || !cpu_online(tcpu) ||
((int)(per_cpu(softnet_data, tcpu).input_queue_head -
rflow->last_qtail)) >= 0)) {
tcpu = next_cpu;
rflow = set_rps_cpu(dev, skb, rflow, next_cpu);
}
// ...三个对比点:
- RSS 在硬件做(零 CPU 开销,哈希在 NIC),RPS 在软中断做(
skb_get_hash软件算,吃软中断 CPU)。 - RFS 更进一步:把流绑到应用所在 CPU(
sock_flow_table记住上次recvmsg的 CPU)。 - 迁移 CPU 必须保证不乱序:内核要求"目标 CPU 的队列尾部已越过上次入队位置"(dev.c:4708-4718 注释)——而 RSS 硬件天然保证同流同队列,没有乱序问题。这就是硬件分流的真正价值。
面试 2 问
- RSS 和 RPS 区别? → 硬件哈希分流(零 CPU、同流同队列、天然不乱序)vs 软件哈希分流(软中断开销、迁移 CPU 要保证顺序,dev.c:4708-4718)。
- 为什么要对称 RSS? → 双向流同核 → per-core 连接表免锁;代价是队列负载可能略不均。
🔧 动手实验
- testpmd 配置 RSS 观察队列分布;
- 用对称 key 验证双向流同队列;
- 写一条 rte_flow 规则导流验证(
code/P1/rte-flow-demo/); - 实现 16 队列多队列 echo,测单核 vs 16 核扩展曲线,数据留档
perf-data/P1/。
Phase 1 七篇到此完结。 下一篇是 Phase 2 的第一篇:P2-01 · mTCP 论文精读(NSDI'14)——学到 Day 50 时动笔。
评论 (0)