第266篇:数据中心 PFC(优先级流控制)死锁故障
关键词
PFC 死锁、优先级流控制、无损网络故障、反压传播、Credit 耗尽、头阻塞、死锁检测
一、PFC 死锁的本质
1.1 什么是 PFC 死锁
PFC 死锁是多个交换机相互等待对方释放缓冲区,导致流量永久停滞的状态:
PFC 死锁示意图:
100G 链路
| Leaf1 Leaf2 ▼ ▼ 双方都收到 Pause,都不发 Class 3 流量 双方都在等对方恢复发送 → 死锁! | ← PFC Pause(Class 3) Class 3 流量 ─────────────────────► ← 我也 Pause 你 | |
|---|---|---|
死锁特征: 1. 某个或多个优先级持续处于 Pause 状态 2. 该优先级上的流量完全停滞 3. 即使链路空闲,也没有数据发送 4. 所有涉及设备相互等待
1.2 死锁发生的必要条件
PFC 死锁的四个必要条件:
条件 1:环路拓扑 + 无损流量
┌─────────► Leaf2 ──────────┐
│ │
│ ▼
Leaf1 ◄─────────────────── Leaf3
如果所有链路上 Class 3 都开启 PFC:
循环反压可能发生
条件 2:缓冲区全部耗尽
每个交换机的 PFC 缓冲区(Headroom)被填满
没有空间接收新的报文
条件 3:相互等待
Leaf1 在等 Leaf2 发送(才能释放 Leaf1 的缓冲区)
Leaf2 在等 Leaf3 发送
Leaf3 在等 Leaf1 发送
循环等待!
条件 4:没有打破机制
没有 PFC 死锁检测
或检测但无恢复动作
二、死锁的根因
2.1 微突发 + 缓存不足
最常见的死锁触发场景:
场景:Leaf1 向 Leaf2 发送大量 RoCE 流量
正常情况:
Leaf1 发 ↘ ↗ Leaf2
X
Leaf3 发 ↗ ↘ Leaf4
流量分散,缓冲区足够
突发情况:
多个 GPU 同时发送计算结果
100G 链路上瞬时 400G 流量
Leaf1 出端口队列急剧增长
PFC 阈值触发 → Leaf2 收到 Pause
如果此时 Leaf2 也在向 Leaf1 发送:
Leaf1 出端口 Pause → Leaf1 入端口停止收
Leaf2 出端口队列填满 → Leaf2 发 Pause 给 Leaf1
Leaf1 和 Leaf2 相互 Pause → 死锁!
2.2 Credit 耗尽
基于 Credit 的流控与 PFC 的关系:
PFC 本质上是一种 Credit 机制:
Sender 有 N 个 Credit(缓冲区配额)
每发一个报文,消耗一个 Credit
Credit 为 0 → 发送端必须停止
正常流程:
发送端有 Credit → 发报文 → 消耗 Credit
接收端处理完报文 → 释放 Credit → 通知发送端
死锁流程:
发送端 Credit = 0 → 停止发送
接收端缓冲区满 → 无法释放 Credit
→ 死锁!
PFC Credit 耗尽的原因:
1. 接收端处理速率 < 接收速率
AI AllReduce 场景最典型
64 入 1 出 → 入远超处理能力
2. 接收端也要发送(双向流量)
RoCE 的 READ/WRITE 是双向的
两边都要发送 → 都可能被 Pause
3. 链路不对称
一边 100G,另一边 40G
低速端必然积累 → Pause
2.3 头阻塞(HOL Blocking)
PFC 的头阻塞问题:
PFC 基于优先级,但同一优先级内部不同流之间会互相影响:
Leaf1 的 Class 3 队列:
| F1 | F2 | F3 | F4 | F5 | F6 | ← 队列头部 |
|---|---|---|---|---|---|---|
| │ | ||||||
| ├─ F1:输出到 Spine1(空闲)→ 可以发送 | ||||||
| ├─ F2:输出到 Spine2(空闲)→ 可以发送 | ||||||
| ├─ F3:输出到 Spine3(拥塞)→ Pause! | ||||||
| ├─ F4:输出到 Spine4(空闲)→ 被 F3 挡住 | ||||||
| ├─ F5:输出到 Spine1(空闲)→ 被挡住 | ||||||
| └─ F6:输出到 Spine2(空闲)→ 被挡住 |
问题: 同一优先级使用 FIFO 队列 F3 的路径拥塞 → PFC 反压 但 F1/F2/F4/F5/F6 的路径是空闲的!
因为 F3 在队列头部挡住了后续所有流
这些"无辜"的流也被 Pause → 带宽浪费
解决方案: 多队列(Per-egress-port queuing) 或 PFC 不用的优先级不开启 PFC
三、死锁的检测
3.1 检测方法
PFC 死锁检测的几种方法:
方法 1:基于持续 Pause 时间
检测:某个优先级持续处于 Pause 状态超过阈值
阈值:通常 100-500ms
判断:连续 Pause → 可能是死锁
配置:
priority-flow-control dead-detect enable
priority-flow-control dead-detect interval 100 # 100ms
priority-flow-control dead-detect count 3 # 3 次
方法 2:基于计数器
display dcb pfc interface 100GE1/0/1
正常情况下:
Pause Frame TX:缓慢增长
Pause Frame RX:缓慢增长
死锁迹象:
Pause Frame 在一段时间内持续高频率
或某个优先级 Pause 状态不恢复
方法 3:基于流量观测
display interface 100GE1/0/1
正常情况下:
端口持续有流量(In/Out 稳定增长)
死锁迹象:
某优先级速率跌零
其他优先级速率正常
链路物理状态正常(Up)
3.2 华为 PFC 死锁检测配置
华为 CloudEngine 系列 PFC 死锁检测:
# 全局死锁检测配置
dcb
pfc dead-detect enable # 全局开启
pfc dead-detect interval 100 # 检测周期 100ms
pfc dead-detect count 3 # 3 次连续判定
pfc dead-recover auto # 自动恢复
#
# 接口级别配置
interface 100GE1/0/1
priority-flow-control dead-detect enable
priority-flow-control dead-detect interval 100
priority-flow-control dead-detect count 3
#
# 查看死锁状态
display dcb pfc dead-detect
Interface Priority DeadLock Status Count
100GE1/0/1 3 Normal 0
100GE1/0/2 3 Normal 0
100GE1/0/3 3 DeadLock 3 ← 已死锁 3 次
100GE1/0/4 3 DeadLock-Re ← 恢复中
四、死锁的恢复
4.1 自动恢复机制
华为 PFC 死锁自动恢复流程:
检测到死锁:
交换机检测到优先级 3 持续 Pause 300ms 恢复动作: Step 1:停止向该优先级发送 Pause Step 2:强制清除该优先级的 Pause 状态 Step 3:丢弃该优先级的部分缓存报文 (丢弃多少取决于配置策略) Step 4:恢复发送 恢复后: 流量重新开始流动 丢弃的报文由 RDMA 层重传 (RDMA 的 Go-Back-N 机制会重传)
丢弃策略配置: # 死锁恢复时丢弃策略 dcb pfc dead-recover drop-mode tail-drop # 丢弃尾部报文 pfc dead-recover drop-packet-count 100 # 最多丢弃 100 个报文
4.2 人工恢复操作
手动恢复 PFC 死锁的操作步骤:
Step 1:确认死锁
display dcb pfc dead-detect
display dcb pfc interface 100GE1/0/3
→ 确认优先级 3 处于 DeadLock 状态
Step 2:尝试策略
# 方案 A:关闭再开启 PFC
interface 100GE1/0/3
undo priority-flow-control enable
priority-flow-control enable
# 方案 B:关闭该优先级上的 PFC
interface 100GE1/0/3
undo priority-flow-control priority 3
# 方案 C:重启端口(最严重情况)
interface 100GE1/0/3
shutdown
undo shutdown
Step 3:确认恢复
display dcb pfc interface 100GE1/0/3
→ 确认 Pause 状态清除
→ 确认流量恢复
五、预防措施
5.1 架构层面的预防
从架构设计上预防 PFC 死锁:
1. 避免 PFC 环路
不佳设计:
Leaf ── Spine ── Leaf ── Spine ── Leaf
↑ │
└──────────────────────────────────┘
存在环路的拓扑更容易死锁
推荐:
标准 Spine-Leaf 无环拓扑
不需要 STP 防止环路
ECMP 所有链路可用但仍无二层环路
2. 减少 PFC 反压深度
单跳反压优于多跳反压:
服务器 ↔ Leaf(PFC 反压仅在接入层)
Leaf ↔ Spine(不使用 PFC,改用 ECN + DCQCN)
3. 合理的超时设计
交换机缓存足够吸收 50ms 的突发
避免小型缓存交换机处理 RoCE 流量
5.2 参数调优
PFC 参数调优预防死锁:
1. PFC 阈值设置
# 华为推荐
dcb pfc buffer shared 4000 # 共享缓存
dcb pfc buffer headroom 2000 # 头房缓存
dcb pfc buffer xoff-threshold 1500 # 反压触发阈值
注意:不要把所有缓存都给 PFC
留部分缓存给其他优先级正常转发
2. ECN 与 PFC 配合
确保 ECN 在 PFC 之前响应:
ECN 标记触发:队列 200-400
PFC 反压触发:队列 > 1500
Kmin > 0
Kmax < PFC 阈值
让 ECN 有足够时间响应
3. 非无损优先级隔离
不要在所有优先级上开启 PFC
只有 RoCE(优先级 3)开启
其他优先级(0)关闭 PFC
5.3 监控与告警
PFC 死锁监控体系:
监控指标:
指标 阈值 告警级别
─────────────────────────────────────────────
PFC Pause Frame Rate > 1000/s 警告
PFC Deadlock Count > 0 严重
优先级 3 速率跌零 - 严重
队列深度 > 80% - 警告
ECN 标记率 < 1% - 注意(可能 ECN 失效)
采集方式:
# SNMP OID(供网管采集)
hwPfcPauseFrameRCv
hwPfcDeadLockCount
hwPfcQueueLength
# Telemetry 流式采集
telemetry
sensor-group PFC_MONITOR
sensor-path hwPfcPauseFrame
sensor-path hwPfcDeadLock
#
六、真实故障案例
6.1 案例:AI 集群死锁
现象:
AI 训练集群每 2 小时出现一次"训练挂死"
所有 GPU 通信超时
网络端口的 RoCE 流量速率为 0
排查:
登录 Spine1 → display dcb pfc dead-detect
→ 发现多个端口频繁出现 DeadLock
Spine1 的 PFC 死锁 → 反压到 Leaf
Leaf 收不到数据 → GPU 通信超时
AllReduce 卡住 → 训练挂死
根因:
ECN 阈值设置过高(Kmin=500)
PFC 在 ECN 之前被触发
Incast 场景下 PFC 频繁触发 → 最终死锁
修复:
调整 ECN 阈值:Kmin=100, Kmax=300
增加 Headroom 缓存:从 1000 到 2000
开启 PFC 死锁自动恢复
效果:
死锁次数:> 10 次/天 → 0 次
训练效率提升 20%
七、总结
| 知识点 | 核心要点 |
|---|---|
| PFC 死锁本质 | 交换机相互等待缓冲区释放,流量永久停滞 |
| 四个必要条件 | 环路 + 缓存耗尽 + 相互等待 + 无打破机制 |
| 根因 | 微突发、Credit 耗尽、头阻塞 |
| 检测 | 持续 Pause 超时(通常 300ms) |
| 恢复 | 自动丢弃缓冲区报文并恢复发送 |
| ECN 配合 | ECN 在 PFC 之前响应,减少 PFC 触发 |
| 架构预防 | 避免 PFC 环路,控制反压深度 |
| 参数调优 | 合理设置阈值,隔离非无损优先级 |
八、思考
- PFC 死锁的四个必要条件是什么?为什么 Spine-Leaf 标准拓扑比三层拓扑更容易出现 PFC 死锁?
- 头阻塞(HOL Blocking)在 PFC 死锁中扮演什么角色?为什么同一优先级的一条流拥塞会阻塞其他正常流?
- PFC 死锁检测的原理是什么?如何区分正常的短暂 Pause 和死锁?
- 死锁自动恢复过程中丢弃的报文由谁负责重传?为什么这种机制是安全的?
- 在一个 AI 训练集群中,如果经常出现 PFC 死锁,应从哪些维度进行调优?(请至少列出 3 个调优方向)
下篇预告:第267篇《存储网络基础:FC-SAN 与 IP-SAN(iSCSI)》——传统存储网络的两种主流方案对比,FC-SAN 的架构与 iSCSI 的协议原理。