DPDK高性能交换机深度实践:一次RCU延迟释放引发的转发表性能雪崩

一、故障背景

某大型云数据中心部署了一批DPDK软件交换机。

主要承担:

  • EVPN VXLAN Leaf
  • L2/L3 Gateway
  • Overlay Routing
  • ACL Filtering
  • Telemetry

硬件平台:

项目 配置
CPU Intel Xeon Gold 6430
网卡 Intel E810
DPDK 23.11
PMD Core 32
Route Scale 600万 Prefix

正常运行时:

复制代码
92Mpps

长期稳定。


某次业务迁移期间:

大量租户迁移。

BGP EVPN发生:

复制代码
路由撤销
路由学习
MAC迁移
ARP迁移

此后开始出现:

复制代码
P99 RTT升高
TCP重传增加
PPS下降

但监控显示:

复制代码
PMD线程 100%

始终如此。


二、第一轮排查

查看网卡统计:

复制代码
rte_eth_stats_get()

结果:

复制代码
imissed = 0
ierrors = 0
rx_nombuf = 0

完全正常。


RSS统计:

复制代码
32 Queue
均衡

正常。


ACL统计:

复制代码
Lookup Cycles
无明显变化

正常。


LPM查找:

复制代码
DIR24_8
稳定

正常。


问题陷入僵局。


三、发现关键线索

运维团队发现:

问题只出现在:

复制代码
EVPN收敛期间

而:

复制代码
静态路由环境

完全正常。


这意味着:

问题可能不在:

复制代码
查表

而在:

复制代码
更新表

四、交换机中的控制面与数据面

现代交换机:

控制面:

复制代码
BGP
EVPN
OSPF

负责:

复制代码
增删路由

数据面:

复制代码
PMD

负责:

复制代码
查找路由

问题来了:

当控制面删除一条路由时:

复制代码
fib_delete(prefix);

如果立即释放:

复制代码
free(route);

危险。


因为:

PMD线程可能正在访问。


五、为什么不能立即释放

假设:

Core7:

复制代码
route = fib_lookup(ip);

刚拿到指针。


此时:

控制面:

复制代码
free(route);

那么:

Core7:

复制代码
route->nh

立刻变成:

复制代码
野指针

因此:

现代交换机大量使用:

RCU(Read Copy Update)

机制。


六、RCU工作原理

控制面:

复制代码
创建新版本
↓
替换指针
↓
旧对象进入回收队列

数据面:

继续访问旧对象。


等待:

复制代码
Grace Period

结束。


再释放。


下面是典型的RCU结构示意:


七、问题开始浮现

EVPN收敛期间:

每秒:

复制代码
120万次
Route Update

意味着:

复制代码
旧路由对象
旧邻接对象
旧MAC对象

持续进入:

复制代码
RCU回收链表

理论上:

没问题。


但:

现场统计发现:

复制代码
RCU Pending Objects

不断增长。


八、为什么会增长

继续分析。

发现:

PMD线程:

复制代码
while(1)
{
    rte_eth_rx_burst();
    process();
    rte_eth_tx_burst();
}

永远不睡眠。


对于RCU来说:

需要:

复制代码
Reader离开临界区

才能完成Grace Period。


而某些代码:

复制代码
rcu_read_lock();

process_packet();

rcu_read_unlock();

包围了整个Burst处理。


结果:

复制代码
Grace Period
极长

九、RCU对象开始堆积

系统运行数分钟后:

复制代码
Pending Route
280万

继续增长:

复制代码
Pending Route
430万

最终:

复制代码
700万+

十、第二个隐患

这些对象虽然:

复制代码
逻辑删除

但:

复制代码
物理未释放

于是:

内存占用:

复制代码
16GB
↓
44GB
↓
78GB

持续增长。


十一、缓存开始失效

原来:

活跃FIB:

复制代码
8GB

后来:

复制代码
8GB
+
70GB Pending

导致:

CPU缓存命中率下降。


Perf统计:

复制代码
LLC Miss

从:

复制代码
4%

增长到:

复制代码
18%

十二、为什么查找时间没变

这是最迷惑人的地方。

单次:

复制代码
fib_lookup()

耗时仍然正常。


但:

缓存污染导致:

复制代码
Route Metadata
Neighbor Metadata

访问成本上升。


于是:

复制代码
Cycles/Packet

持续增加。


十三、真正根因

完整链路:

复制代码
EVPN收敛
      ↓
大量Route Delete
      ↓
RCU Pending增长
      ↓
Grace Period过长
      ↓
对象无法释放
      ↓
内存膨胀
      ↓
Cache污染
      ↓
Cycles/Packet增加
      ↓
PPS下降

十四、定位证据

统计:

复制代码
rcu_pending_count();

结果:

时间 Pending
初始 2万
5分钟 180万
10分钟 420万
20分钟 730万

与PPS下降趋势完全一致。


十五、解决方案

方案一

缩小RCU读侧范围:

原来:

复制代码
rcu_read_lock();

process_burst();

rcu_read_unlock();

改为:

复制代码
for(each packet)
{
    rcu_read_lock();

    fib_lookup();

    rcu_read_unlock();
}

Grace Period显著缩短。


方案二

分层路由对象

把:

复制代码
Route
Neighbor
Statistics

拆分。


减少回收对象大小。


方案三

异步批量释放

引入:

复制代码
RCU Reclaimer Thread

集中回收。


方案四

控制面限速

避免:

复制代码
百万级更新风暴

瞬时冲击。


优化结果

指标 优化前 优化后
PPS 61M 91M
Pending Route 730万 8万
LLC Miss 18% 4%
RTT P99 5.3ms 0.7ms

核心知识点总结

知识点1

高性能交换机中的性能问题不一定发生在:

复制代码
查表

也可能发生在:

复制代码
删表

知识点2

RCU解决的是:

复制代码
读写并发

问题。


知识点3

RCU的成本不是查找。

而是:

复制代码
延迟释放

知识点4

PMD线程100%运行时。

Grace Period设计尤为关键。


知识点5

大量Pending对象会污染缓存。


知识点6

缓存污染不一定体现在Lookup时间上。

却会体现在:

复制代码
Cycles Per Packet

上。


知识点7

大型EVPN交换机设计中:

复制代码
FIB更新架构

和:

复制代码
FIB查找架构

同样重要。