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

P3-01 · OVS-DPDK 数据面:EMC 两级缓存 + upcall 慢路径

PacketForge
2026-08-18 / 0 评论 / 0 阅读 /
对应路线图:Phase 3 (Day 92-119) | DPDK 24.11.6 LTS | 内核对照 v6.6
上一篇:P2-07 性能调优 | 下一篇:P3-02 OVS-DPDK 调优

一句话定位

OVS-DPDK 的数据面 = PMD 轮询收包 + 两级软件查表缓存(EMC → dpcls)+ 查不到就 upcall 回 ofproto 慢路径——把 OpenFlow 流表下沉到每核缓存。

PMD 主循环(pmd_thread_main,dpif-netdev.c:6013)

主循环节选(dpif-netdev.c:6087-6127):

    for (;;) {
        for (i = 0; i < poll_cnt; i++) {
            process_packets =
                dp_netdev_process_rxq_port(pmd, poll_list[i].rxq,
                                           poll_list[i].port_no);
            rx_packets += process_packets;
            if (process_packets >= PMD_SLEEP_THRESH)
                sleep_time = 0;
        }
        if (!rx_packets) {
            tx_packets = dp_netdev_pmd_flush_output_packets(pmd,
                                                   max_sleep && sleep_time
                                                   ? true : false);
        }
        // ...(max_sleep 时按 PMD_SLEEP_INC_US 阶梯递增 sleep_time,省略)
    }

每轮收完包立刻查表转发。PMD_SLEEP_THRESH(dpif-netdev.c:177,= NETDEV_MAX_BURST/2)做自适应休眠:连续收不到半批包才允许 PMD 睡一觉。dp_netdev_process_rxq_port(dpif-netdev.c:4434-4498)内部三步:netdev_rxq_recv(netdev-dpdk 即 netdev_dpdk_rxq_recv,netdev-dpdk.c:2990,底层 rte_eth_rx_burst)→ dp_netdev_input 查表 → dp_netdev_pmd_flush_output_packets 立即发 TX 攒批(:4483)。

两级缓存:EMC 精确命中 → dpcls 掩码匹配

OVS-DPDK 两级缓存与 upcall

① EMC:每 PMD 私有的 8192 槽组相联缓存

结构(dpif-netdev-dfc.h:93-101):struct emc_entry = flow 指针 + netdev_flow_key;struct emc_cache = 8192 个 entry 数组(EM_FLOW_HASH_SHIFT=13,:67-68)。查表(dpif-netdev-dfc.h:150-166 节选):

    EMC_FOR_EACH_POS_WITH_HASH (cache, current_entry, key->hash) {
        if (current_entry->key.hash == key->hash
            && emc_entry_alive(current_entry)
            && emc_flow_key_equal_mf(&current_entry->key, &key->mf)) {
            return current_entry->flow;
        }
    }
    return NULL;

32 位 hash 切 2 段(EM_FLOW_HASH_SEGS=2,:70)各当一次索引,只探 2 个槽;命中三条件——hash 相等、流没死、miniflow 逐位 memcmp(:147)。每 PMD 一份私有 EMC(:62-64 注释),天然免锁——RSS 同流同核的红利。

② dpcls:tuple-space 子表分类器

struct dpcls(dpif-netdev.c:186-191)是一组子表的容器;每个子表收集同一掩码组合的规则(dpif-netdev-dpcls.h:73-100),按指纹选最优查找实现(lookup_func,:93)。批量查找(dpif-netdev.c:9278-9301):

    /* The Datapath classifier - aka dpcls - is composed of subtables.
     * Each subtable collects rules with matches on a specific subset
     * of packet fields as defined by the subtable's mask. ... */
    PVECTOR_FOR_EACH (subtable, &cls->subtables) {
        found_map = subtable->lookup_func(subtable, keys_map, keys, rules);
        keys_map &= ~found_map;
        if (!keys_map) {
            return true;
        }
        subtable_pos++;
    }

一次批量查 32 个包(NETDEV_MAX_BURST):命中即从 keys_map 位图摘掉,全部命中提前返回——把逐包流表匹配变成批量操作。

③ 快路径总装:dfc_processing(dpif-netdev.c:7289)

        miniflow_extract(packet, &key->mf);
        key->len = 0; /* Not computed yet. */
        key->hash = (md_is_valid == false)
                ? dpif_netdev_packet_get_rss_hash_orig_pkt(packet, &key->mf)
                : dpif_netdev_packet_get_rss_hash(packet, &key->mf);

        /* If EMC is disabled skip emc_lookup */
        flow = (cur_min != 0) ? emc_lookup(&cache->emc_cache, key) : NULL;
        if (OVS_LIKELY(flow)) {
            n_emc_hit++;
            dfc_processing_enqueue_classified_packet(...);
        } else {
            /* Exact match cache missed. Group missed packets together at
             * the beginning of the 'packets' array. */
            // ...(miss 的 key 收集进 missed_keys,批量查 dpcls)
        }

hash 直接复用 NIC RSS 哈希(dpif_netdev_packet_get_rss_hash_orig_pkt,dpif-netdev-dpcls.h:116-129);循环内 prefetch 下一个包(dpif-netdev.c:7331-7336)——P1-02 的 cache 纪律在这里兑现。

upcall:miss 了怎么办

handle_packet_upcall(dpif-netdev.c:7430)把 miss 包交给 dp_netdev_upcall(:6973),最终调用 dp->upcall_cb(:7013)——连到 ofproto 层,流表翻译出 actions 后把新流装回 EMC/dpcls。慢路径是逐包的,EMC 命中率是性能第一指标。

🐧 内核对照:同一个"upcall"思想,两套实现

内核 OVS 数据面(net/openvswitch/datapath.c:246-294,节选):

void ovs_dp_process_packet(struct sk_buff *skb, struct sw_flow_key *key)
{
    // ...
    /* Look up flow. */
    flow = ovs_flow_tbl_lookup_stats(&dp->table, key, skb_get_hash(skb),
                     &n_mask_hit, &n_cache_hit);
    if (unlikely(!flow)) {
        struct dp_upcall_info upcall;
        upcall.cmd = OVS_PACKET_CMD_MISS;
        error = ovs_dp_upcall(dp, skb, key, &upcall, 0);
        // ...
        goto out;
    }

    ovs_flow_stats_update(flow, key->tp.flags, skb);
    sf_acts = rcu_dereference(flow->sf_acts);
    error = ovs_execute_actions(dp, skb, sf_acts, key);
    // ...
}
环节内核 OVS datapathOVS-DPDK (dpif-netdev)
收包NAPI 软中断PMD 轮询(pmd_thread_main)
缓存per-CPU mask cache(n_cache_hit)每 PMD 私有 EMC 8192 槽
流表ovs_flow_tbl_lookup_statsdpcls 子表批量查
missovs_dp_upcall 发 netlinkdp_netdev_upcall 直调 upcall_cb

同构:两级查找 + miss 上送,这是 OVS 全平台的骨架;命中后内核 ovs_execute_actions 在内核态执行 actions,DPDK 在用户态执行。差异在运行模型:内核版每包构造 skb、受 NAPI budget 约束;DPDK 版批量 32 包、无预算、mbuf 复用。

面试 2 问

  1. EMC 为什么只要 2 次探测? → 32 位 hash 按 EM_FLOW_HASH_SEGS=2 切两段做组相联索引(dpif-netdev-dfc.h:70、120-124),每段 13 位索引 8192 槽(:67-68)。
  2. upcall 链路的终点在哪? → dp_netdev_upcall 调 dp->upcall_cb(dpif-netdev.c:7013);内核版对应 ovs_dp_upcall 发 OVS_PACKET_CMD_MISS(datapath.c:267-276)。

🔧 动手实验

  • 搭 OVS-DPDK:ovs-vsctl add-br br0 -- set bridge br0 datapath_type=netdev + add-port 两个 DPDK phy 口,ovs-appctl dpif-netdev/pmd-stats-show 看 EMC hit/miss;单流 vs 10k 流各打 30s 对比 miss 曲线。
  • 数据落点:perf-data/P3/01-ovs-emc-hitrate.md(EMC 命中率 vs 流数量表)。

下一篇:P3-02 · OVS-DPDK 调优实战:pmd-cpu-mask 与 tx-flush

0

评论 (0)

取消