侧边栏壁纸
  • 累计撰写 53 篇文章
  • 累计收到 3 条评论

P1-07 · RSS 与 rte_flow:流与核的绑定

PacketForge
2026-08-18 / 0 评论 / 0 阅读 /
对应路线图: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 流水线

RSS 流水线与内核 RPS 对照

  • 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);
        }
        // ...

三个对比点:

  1. RSS 在硬件做(零 CPU 开销,哈希在 NIC),RPS 在软中断做(skb_get_hash 软件算,吃软中断 CPU)。
  2. RFS 更进一步:把流绑到应用所在 CPU(sock_flow_table 记住上次 recvmsg 的 CPU)。
  3. 迁移 CPU 必须保证不乱序:内核要求"目标 CPU 的队列尾部已越过上次入队位置"(dev.c:4708-4718 注释)——而 RSS 硬件天然保证同流同队列,没有乱序问题。这就是硬件分流的真正价值。

面试 2 问

  1. RSS 和 RPS 区别? → 硬件哈希分流(零 CPU、同流同队列、天然不乱序)vs 软件哈希分流(软中断开销、迁移 CPU 要保证顺序,dev.c:4708-4718)。
  2. 为什么要对称 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

评论 (0)

取消