3. PFC(优先级流控)原理:逐跳反压机制,如何防止丢包?

大家好,我是老张。今天聊PFC,Priority Flow Control。

说白了,这就是数据中心无损网络的核心命脉。没有PFC,RoCEv2就是空中楼阁。我当年第一次接触无损网络时,觉得PFC不就是个流控嘛,有啥难的?结果被现实狠狠教育了一顿。

3.1 为什么需要PFC?

传统以太网丢包了怎么办?重传呗。但RDMA不行。RDMA要求网络零丢包,一旦丢包,性能直接断崖式下跌。你想想看,一个GPU集群正在跑大模型训练,突然丢了一个包,整个AllReduce都得等重传,那画面太美我不敢看。

那传统的802.3x流控呢?它是基于端口的。一个端口忙,就把整个端口都停了。这太粗暴了。比如端口上有两个流,一个高优先级,一个低优先级。低优先级的把端口堵了,高优先级的也得跟着等。这不合理。

所以PFC来了。它把流控粒度从端口细化到了优先级。每个优先级可以独立做流控。嗯,这里要注意,PFC定义了8个优先级(0-7),每个优先级对应一个虚拟通道。

核心思想:PFC不是停整个端口,而是只停某个优先级。其他优先级照常转发。

3.2 逐跳反压机制

PFC是逐跳的,不是端到端的。什么意思?

每一跳交换机都只跟它的上游邻居说:兄弟,你慢点,我这边某个优先级快满了。上游收到后,就暂停发送该优先级的报文。这就是反压。

我画个图你就明白了:

PFC逐跳反压机制示意图 发送端 交换机1 交换机2 接收端 数据 数据 数据 PFC反压 PFC反压 当交换机2的某个优先级队列快满时 它向上游(交换机1)发送PFC暂停帧 交换机1收到后暂停该优先级报文的发送 队列 快满了

你看这个图。接收端处理不过来时,交换机2的某个优先级队列开始堆积。快到阈值了,交换机2就给交换机1发PFC暂停帧。交换机1收到后,暂停该优先级的发送。如果交换机1的队列也满了,它继续往上游发暂停帧。一直反压到发送端。

这就是逐跳反压。每一跳都只跟邻居说话,不跨级。好处是响应快,坏处是...嗯,后面会讲它的坑。

3.3 PFC暂停帧长什么样?

PFC暂停帧是一个MAC控制帧。我直接给你看格式:

PFC MAC控制帧格式:
+----------------+----------------+----------------+----------------+
| 目的MAC地址    | 源MAC地址      | 以太类型(0x8808)| MAC控制opcode  |
| (6字节)        | (6字节)        | (2字节)        | (2字节) 0x0101 |
+----------------+----------------+----------------+----------------+
| 优先级使能向量  | 优先级0暂停时间 | 优先级1暂停时间 | ...            |
| (2字节)        | (2字节)        | (2字节)        |                |
+----------------+----------------+----------------+----------------+
| 优先级7暂停时间 | 保留           | FCS            |
| (2字节)        | (26字节)       | (4字节)        |
+----------------+----------------+----------------+----------------+

关键字段就两个:

  • 优先级使能向量:告诉对端,我这次要暂停哪些优先级。每个bit对应一个优先级。
  • 暂停时间:每个优先级独立设置暂停时长。单位是512比特时间。比如10Gbps下,一个单位约51.2纳秒。

暂停时间设为0,就是恢复发送。所以PFC可以精确控制每个优先级的暂停时长。

我的经验:暂停时间不要设太长。我见过有人设了65535,结果一个优先级被停了快1秒。对于RDMA来说,1秒的延迟简直是灾难。建议设100-500个时间单位,够用就行。

3.4 如何防止丢包?

PFC防止丢包的核心逻辑就一句话:在队列溢出之前,让上游停下来。

具体怎么做?每个交换机端口有8个优先级队列。每个队列有两个阈值:

阈值名称 触发条件 动作
XOFF阈值 队列占用超过此阈值 发送PFC暂停帧给上游
XON阈值 队列占用低于此阈值 发送PFC恢复帧(暂停时间=0)

XOFF和XON之间有个差值,叫滞后(Hysteresis)。为什么要加滞后?

你想想看,如果没有滞后,队列在阈值附近抖动,就会频繁发送暂停/恢复帧。这会导致链路利用率下降。加个滞后,让队列多消化一些再恢复,能减少控制帧的数量。

我举个例子:

  1. 队列深度1000个cell,XOFF设800,XON设600
  2. 队列涨到801,触发XOFF,发暂停帧
  3. 上游停止发送,队列开始下降
  4. 队列降到599,触发XON,发恢复帧
  5. 上游恢复发送

这个过程中,队列最大占用801,远小于队列深度1000。所以不会丢包。

注意:XOFF阈值不能设得太高。要留出足够的余量,因为从发送暂停帧到上游真正停止发送,有链路延迟。这个延迟内,上游可能还在发数据。如果XOFF设成990,那这10个cell的余量可能不够,队列就溢出了。

3.5 我曾经踩过的坑

讲几个真实案例吧。

案例1:PFC风暴

有一次,某个数据中心部署RoCEv2,结果网络性能极差。我上去一看,PFC帧满天飞。怎么回事?

原来,某个交换机的XOFF阈值设得太低,队列稍微一波动就触发暂停。上游收到暂停后,队列下降,又触发XON。然后上游又开始发。结果就是频繁暂停-恢复,链路利用率不到50%。

解决方案:把XOFF阈值调高,同时加大滞后。让队列有足够的缓冲能力,减少PFC帧的发送频率。

案例2:死锁

这个更坑。两个交换机互相发PFC暂停帧,形成死锁。A停了B的优先级3,B停了A的优先级5。结果两个优先级都停了,谁也发不出去。

为什么会这样?因为流量拓扑是环形的,或者有多个路径。PFC只关心优先级,不关心路径。一旦形成环路,就可能死锁。

解决方案:要么用DCQCN这种端到端拥塞控制替代PFC,要么在拓扑设计上避免环路。我个人更倾向于DCQCN,但那是另一个话题了。

3.6 PFC的优缺点

说了这么多,总结一下:

优点 缺点
逐跳响应快,延迟低 可能引发PFC风暴
粒度细,只暂停特定优先级 可能造成死锁
实现简单,硬件成本低 公平性问题:一个流捣乱,整个优先级遭殃
兼容现有以太网 需要精细调参,否则性能差

说白了,PFC是个好东西,但用不好就是双刃剑。我建议你在部署前,先做充分的测试。特别是XOFF阈值、暂停时间这些参数,要根据你的流量模型来调。

核心要点回顾:

  • PFC是逐跳的,不是端到端的
  • 每个优先级独立做流控
  • XOFF触发暂停,XON触发恢复
  • 阈值设置要留余量,防止溢出
  • 避免PFC风暴和死锁

好了,PFC的原理就讲到这里。记住,它只是无损网络的基础设施之一。真正要玩转无损网络,还得结合ECN、DCQCN这些机制。后面我们会慢慢展开。


公众号:蓝海资料掘金营,微信deep3321