对应路线图: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 掩码匹配
① 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(¤t_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 datapath | OVS-DPDK (dpif-netdev) |
|---|---|---|
| 收包 | NAPI 软中断 | PMD 轮询(pmd_thread_main) |
| 缓存 | per-CPU mask cache(n_cache_hit) | 每 PMD 私有 EMC 8192 槽 |
| 流表 | ovs_flow_tbl_lookup_stats | dpcls 子表批量查 |
| miss | ovs_dp_upcall 发 netlink | dp_netdev_upcall 直调 upcall_cb |
同构:两级查找 + miss 上送,这是 OVS 全平台的骨架;命中后内核 ovs_execute_actions 在内核态执行 actions,DPDK 在用户态执行。差异在运行模型:内核版每包构造 skb、受 NAPI budget 约束;DPDK 版批量 32 包、无预算、mbuf 复用。
面试 2 问
- EMC 为什么只要 2 次探测? → 32 位 hash 按
EM_FLOW_HASH_SEGS=2切两段做组相联索引(dpif-netdev-dfc.h:70、120-124),每段 13 位索引 8192 槽(:67-68)。 - 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 流数量表)。
评论 (0)