首页
关于
Search
1
PacketForge 博客部署文档
16 阅读
2
深入 SONiC 与 SAI:从命令行到芯片的完整数据之旅
14 阅读
3
DPU 与 CPO:AI 时代的网络基础设施革命
11 阅读
4
Broadcom芯片
10 阅读
5
DPDK高性能网络
10 阅读
默认分类
交换芯片
高性能网络
AI算力网络
WebSocket高性能项目
DPDK 成长路线
①性能工程基础
②DPDK 源码深读
③用户态协议栈
④软件交换与 SONiC
⑤PCIe、虚拟化与 DPU
⑥面试与复盘
登录
Search
标签搜索
DPDK→DPU 学习路线
P0
P1
P2
P3
P5
PCIe
P4
P6
libmtcp
NUMA
mTCP
OVS
SONiC
SAI
DPU
perf
gdb
EAL
mempool
PacketForge
累计撰写
53
篇文章
累计收到
3
条评论
首页
栏目
页面
关于
最新文章
热门文章
评论最多
点赞最多
2026-08-18
P3-02 · OVS-DPDK 调优实战:pmd-cpu-mask 与 tx-flush-interval
对应路线图:Phase 3 (Day 92-119) | DPDK 24.11.6 LTS | 内核对照 v6.6上一篇:P3-01 OVS-DPDK 数据面 | 下一篇:P3-03 SONiC 控制面链路一句话定位OVS-DPDK 的性能不靠改代码,靠把线程钉对核、把发送攒够批:pmd-cpu-mask 决定"谁在哪个核上轮询",tx-flush-interval 决定"攒多少包再发一次"。本篇没有独立源码文件——它的"源码"是配置项语义 + 上一节读过的数据面代码,我如实标注出处。pmd-cpu-mask:PMD 线程的出生证明官方语义定义在 vswitchd/vswitch.xml:375-392(节选): <column name="other_config" key="pmd-cpu-mask"> <p> Specifies CPU mask for setting the cpu affinity of PMD (Poll Mode Driver) threads. Value should be in the form of hex string, similar to the dpdk EAL '-c COREMASK' option input or the 'taskset' mask input. The lowest order bit corresponds to the first CPU core. A set bit means the corresponding core is available and a pmd thread will be created and pinned to it. ... </p> <p> If not specified, one pmd thread will be created for each numa node and pinned to any available core on the numa node by default. </p> </column>要点:mask 的每一位 = 一个 PMD 线程。0x6(二进制 0110)就是 core 1、2 各起一个 PMD;不指定则每 NUMA node 只给一个 PMD——多队列网卡会严重饿死,这是最常见的性能坑。数据面依据:PMD 线程创建时按 core_id 绑核(dpif-netdev.c:6033-6034): ovs_numa_thread_setaffinity_core(pmd->core_id); dpdk_attached = dpdk_attach_thread(pmd->core_id);每个 PMD 在 pmd_load_queues_and_ports(dpif-netdev.c:6035)拿到自己的 poll_list,主循环只轮询自己队列组——所以 PMD 数量和 RXQ 数量必须匹配(tests/pmd.at:137 有专门的分配测试用例)。绑定纪律(Phase 1 P1-06 学过的规矩在这里兑现):PMD 核别给 vswitchd 主线程(vswitch.xml:363-367 警告多线程共享单核的后果);用 isolcpus 或 systemd AllowedCPUs 隔离;PMD 所在 socket 必须与它轮询的 NIC 一致,跨 socket 内存带宽腰斩。tx-flush-interval:用一点延迟换吞吐语义与效果(Documentation/intro/install/dpdk.rst:647-669):The ``tx-flush-interval`` parameter can be used to specify the time in microseconds OVS should wait between two send bursts to a given port (default is ``0``). When the intermediate queue fills up before that time is over, the buffered packet batch is sent immediately:: $ ovs-vsctl set Open_vSwitch . other_config:tx-flush-interval=50 ... Low traffic (``packet rate < 1 / tx-flush-interval``) should not experience any significant latency or throughput increase as packets are forwarded immediately. At intermediate load levels (``1 / tx-flush-interval < packet rate < 32 / tx-flush-interval``) traffic should experience an average latency increase of up to ``1 / 2 * tx-flush-interval`` and a possible throughput improvement. Very high traffic (``packet rate >> 32 / tx-flush-interval``) should experience the average latency increase equal to ``32 / (2 * packet rate)``. Most send batches in this case will contain the maximum number of packets (``32``).翻译成白话:TX 侧引入一个攒批窗口。窗口期内收到的包先堆在 tx_port 的输出队列,攒到一批 32 个(NETDEV_MAX_BURST)或窗口到期才调 rte_eth_tx_burst。低流量零影响(没包可攒,直接发);中流量每个包平均多等 1/2 * interval;高流量攒满 32 个才发,延迟上界 32/(2*rate)。官方给的经验值:PHY-VM-PHY 场景 50us 收益明显(dpdk.rst:671-674)。数据面依据:flush_time 与 forcetx-flush-interval 进 dp->tx_flush_interval 原子变量(dpif-netdev.c:284,默认值 DEFAULT_TX_FLUSH_INTERVAL = 0,dpif-netdev.c:106;配置解析在 dpif-netdev.c:3902-3906)。PMD 把包排进某端口的输出队列时设置到期时间(dpif-netdev.c:4391-4392): atomic_read_relaxed(&pmd->dp->tx_flush_interval, &tx_flush_interval); // ... p->flush_time = pmd->ctx.now + tx_flush_interval;真正发出去的是 dp_netdev_pmd_flush_output_packets(dpif-netdev.c:4414-4431): HMAP_FOR_EACH (p, node, &pmd->send_port_cache) { if (!dp_packet_batch_is_empty(&p->output_pkts) && (force || pmd->ctx.now >= p->flush_time)) { output_cnt += dp_netdev_pmd_flush_output_on_port(pmd, p); } }要么 force、要么到期。force 的两个时机:本轮收到包后立即 flush(dpif-netdev.c:4483);主循环发现本轮无包、准备睡 PMD 前强制 flush(dpif-netdev.c:6119-6126,max_sleep && sleep_time ? true : false)——绝不让包在输出队列里陪着 PMD 睡觉。底层仍是批量发送(netdev-dpdk.c:3394-3418)netdev_dpdk_eth_send(struct netdev *netdev, int qid, struct dp_packet_batch *batch, bool concurrent_txq) { struct rte_mbuf **pkts = (struct rte_mbuf **) batch->packets; // ... cnt = netdev_dpdk_common_send(netdev, batch, &stats); dropped = netdev_dpdk_eth_tx_burst(dev, qid, pkts, cnt);攒批的意义就在 rte_eth_tx_burst 的批量摊薄:一次发送 32 个包和发 1 个包的 MMIO/描述符写回成本差不了太多——批越大,每包成本越低。这就是 tx-flush-interval 用延迟换吞吐的微观机制。🐧 内核对照:内核怎么攒批环节内核网络栈OVS-DPDK攒批机制NAPI budget 收一批 + qdisc 自然攒包TX 侧显式 tx_flush_interval 攒批窗口触发发送qdisc 出队(net_tx_action 软中断)flush_time 到期或 force(dpif-netdev.c:4426)低流量中断驱动,来包就发无包可攒,立即发(dpdk.rst:658-660)延迟模型qdisc 排队延迟 + BQL平均 1/2 * interval(dpdk.rst:662-665)每包成本skb 构造 + qdisc 锁mbuf 批量 rte_eth_tx_burst内核靠中断 + qdisc 天然批量,代价是每包软中断和 skb 构造;DPDK 主动让出微秒级延迟换纯批量发送。思路一致:批量摊薄固定成本。面试 3 问pmd-cpu-mask=0x6 会创建几个 PMD 线程? → 2 个(core 1、2),最低位对应 core 0(vswitch.xml:383-386);不设置则每 NUMA node 默认只有 1 个 PMD(vswitch.xml:389-391)。tx-flush-interval 为什么能提吞吐? → 攒批到 32 个再 rte_eth_tx_burst,摊薄每次发送的固定开销;代价是中流量每包平均多等 1/2 * interval(dpdk.rst:662-665)。PMD 要睡之前,输出队列里的包怎么办? → 主循环强制 force=true flush(dpif-netdev.c:6124-6126),包不会陪着 PMD 睡 max_sleep。🔧 动手实验# 1. 两个 PMD 核 + 两个隔离核 ovs-vsctl set Open_vSwitch . other_config:pmd-cpu-mask=0x18 # 2. 验证队列分配(每个 PMD 该看到一半 RXQ) ovs-appctl dpif-netdev/pmd-rxq-show # 3. 打流对比 tx-flush-interval=0 vs 50 的 PPS/延迟 ovs-vsctl set Open_vSwitch . other_config:tx-flush-interval=50 # 4. 观察攒批效果 ovs-appctl dpif-netdev/pmd-stats-show ovs-appctl dpif-netdev/pmd-perf-show # tx batches 直方图产出 perf-data/P3/02-pmd-tune.md:不同 PMD 核数 × tx-flush-interval 的 PPS/平均延迟矩阵。下一篇:P3-03 · SONiC 控制面链路:APPL_DB → ASIC_DB → syncd
2026年08月18日
0 阅读
0 评论
0 点赞
2026-08-18
P3-01 · OVS-DPDK 数据面:EMC 两级缓存 + upcall 慢路径
对应路线图: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 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 问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 流数量表)。下一篇:P3-02 · OVS-DPDK 调优实战:pmd-cpu-mask 与 tx-flush
2026年08月18日
0 阅读
0 评论
0 点赞
第 1/1 页 · 本页 2 条 · 共 2 条