☰
RDMA无损网络PFC配置调优实战:从踩坑到稳定
2026/10/9 10:33:39 网站建设 项目流程

RDMA网络刚上手那阵子,我一度以为只要把网卡插上、驱动装好、跑个测试工具,就能享受到“绕过内核、零拷贝、超低延迟”的极致体验。结果被现实狠狠教育了一顿:Performance 上不去、断连、重传、丢包,各种玄学问题轮番上演。折腾到最后,几乎所有问题的矛头都指向同一个东西——PFC(Priority-based Flow Control,基于优先级的流控),也就是无损网络里那个让人又爱又恨的“开关”。

这篇东西不打算写成教科书,而是想把我自己在配置无损网络、调优PFC过程中踩过的坑、总结出的思路,以及最后怎么把网络从“半残”调到“相对稳定”的过程记录下来。如果你也在搞RDMA,或者正准备给存储集群、AI训练集群搭建RoCE网络,希望这篇能让你少走点弯路。

1. 先搞清楚无损网络和PFC到底在解决什么问题

1.1 RDMA与“有损”网络的天然冲突

RDMA(Remote Direct Memory Access)的核心卖点是绕过CPU、绕过内核,让数据直接从网卡到应用内存,省去内核协议栈的拷贝、中断、上下文切换。理念很性感,但这套机制有一个致命前提:网络不能丢包。

传统TCP为什么能容忍“有损”?因为TCP有滑动窗口、有ACK重传、有拥塞控制,丢了包可以重传,延迟大了可以降速。但RDMA的设计假设是“网络足够可靠”,数据到了网卡就直接交给应用了,没有内核兜底。一旦发生丢包,RDMA网卡只能整个报文序列重传,而且重传粒度不是单个MTU,可能是多个报文一起重传。这个代价在数据中心场景里是灾难性的:哪怕只有万分之一的丢包率,吞吐量都可能直接暴跌到一半以下。

所以就有了“无损网络”这个概念:让网络设备在正常情况下不会丢包。而实现无损的关键技术之一,就是PFC。

1.2 PFC不是“暂停整个网口”,而是“暂停某一条队列”

很多人对PFC的误解是:它像802.3x流控一样,把一个端口整个暂停掉,网卡说停就停,交换机能空转就空转。实际不是,PFC的粒度是“优先级队列”级别。

以太网报文里有PCP(Priority Code Point)字段,3个bit,可以标识8个优先级。PFC协议(IEEE 802.1Qbb)允许交换机对每一条优先级队列独立做流控:当某个队列的缓存占用超过阈值时,交换机向对端设备发送暂停帧(Pause Frame),让对端暂停发送这个优先级的数据,但其他优先级不受影响。

这意味着:你可以把RDMA流量放到高优先级队列,把普通TCP放到低优先级队列,PFC暂停的时候只暂停高优先级,TCP流量照常走。这样RDMA流量“无损”,TCP流量用传统重传机制兜底,各得其所。

但是,这个“各得其所”是理想状态。实际配置中,队列划分、优先级映射、缓存阈值、死锁检测,每一个环节都可能搞出问题。我后面会详细拆。

1.3 分类探讨:RoCE v2环境下PFC的典型架构

目前数据中心里常见的RDMA实现是RoCE v2(RDMA over Converged Ethernet version 2),它把RDMA报文封装在UDP/IP里,可以在普通IP网络上跑。但因为跑在UDP上,就没有TCP的拥塞控制机制,必须依赖底层网络提供无损通道。

典型架构是这样的:

  • 服务器上的RDMA网卡(Mellanox CX5/CX6、Broadcom网卡等)把流量打上特定优先级(通常是优先级3,也有用4的),作为高优先级流量发送。
  • 交换机端启用PFC,针对优先级3开启流控。
  • CNP(Congestion Notification Packet)机制配合DCQCN(数据中心量化拥塞通知)算法,作为端到端的二级流控:发送端通过接收CNP来感知拥塞,调整发送速率。

PFC解决的是“交换机缓存溢出”的问题,DCQCN解决的是“源端速率调整”的问题。很多人只配了PFC,没配DCQCN,结果PFC频繁触发,Pause帧把整个网络的局部带宽全拖垮了,这就是典型的“只治标不治本”。

2. 配置PFC前的关键抉择:方案选型与设计思路

2.1 先确定流量优先级,别乱抢“车队”

我见过很多团队直接照抄网络大厂的配置:优先级3给RDMA,VLAN里优先级的映射按标准来,Egress buffer留足。结果全网一上业务就出问题——因为他们没考虑自己的业务特点。

举个例子,AI训练集群里有两个重要流量:GPU通信流量(AllReduce、AllGather)和普通存储流量(读写日志、模型文件)。这两个都是RDMA流量,但特点是不同的:GPU通信对延迟极端敏感,必须在几微秒内完成同步;存储流量对吞吐有要求,但延迟容忍度稍微高一些。

如果你把两种流量都放到同一个PFC优先级队列里,一旦存储流量突发占用完buffer,GPU通信就得一起被Pause,整个训练任务的同步时间就会被拖长。这非常影响分布式训练效率。

我的建议是:把RDMA流量至少分成两个PFC优先级。

  • 优先级3:GPU通信流量,高优先,转发优先,但触发PFC后要快速速降。
  • 优先级4:存储流量,中高优先,带宽变化容忍度更高。

同一个物理网络,让不同业务走各自的PFC队列,可以减少“小队里的菜鸟拖累全队”的情况。代价是配置复杂度上升,交换机上要配好map,网卡上要配置好UP和queue的映射。

2.2 网卡侧还是交换机侧:PFC配置其实要“两头热”

PFC不是交换机一端配置完就完事的,它需要服务器网卡和交换机端口之间协商一致。简单来说:

  • 网卡侧:你要告诉网卡“哪些优先级开PFC,哪些不开”,并且配置好队列映射。

    • 如果用Mellanox网卡,可以用cma_roce_mode、cma_roce_tos等参数,或者用mlxconfig改LINK_TYPE_P1、KEEP_ETH_LINK_UP_P1这些参数。
    • 更常用的做法是直接用mstconfig或mlxconfig配合tc命令,在网卡上配置优先级队列映射。
  • 交换机侧:在端口下开启PFC,并把对应的PCP映射到正确的优先级队列。

    • 以Cisco Nexus为例,配置priority-flow-control mode on,加上priority-flow-control priority 3。
    • 以Arista为例,使用priority-flow-control on、priority-flow-control priority 3。
    • 以SONiC为例,需要编写qos.json和qdpm.json,配置PFC的watchdog、buffer pool、profile等。

两头任何一边配置不匹配,PFC都不会正常工作。最常见的问题是:交换机侧开了PFC优先级3,但网卡发出的报文优先级是0或者2,Pause帧根本管不到它。

2.3 为什么不能全链路全开PFC:死锁和PFC风暴的隐患

还有一个新手常犯的错误:为了确保无损,把交换机的所有端口、所有优先级都开启PFC。这看起来是“既然要无损,那就全面无损”,实际会埋下两个雷。

第一个雷是PFC死锁。假设多台交换机形成拓扑环,某些队列的Pause帧互相等待,就会形成“谁都在暂停、谁都收不到数据”的死锁状态。一旦PFC死锁,整个网络的链路都会卡死,只能重启交换机设备。

第二个雷是PFC风暴。当某个队列持续触发PFC,会对对端设备产生大量Pause帧,疯狂占用交换机的内部带宽,影响其他队列转发,相当于“一台失控的车堵住了整条高速路”。

解决办法是:只在必要的接入端口开启PFC,核心和汇聚尽量不开;只对RDMA流量所属的优先级开启PFC,其他优先级全部关闭;同时在交换机上配置PFC Watchdog,一旦检测到持续的PFC风暴,自动强制丢弃该优先级的报文,打破死锁。

这些都是血泪教训里总结出来的。

3. 实操过程:一步一步把PFC配置拉起来

3.1 第一步:网卡侧的基础配置

先确认网卡型号和驱动版本。我这边用的是Mellanox ConnectX-5和ConnectX-6,驱动用官方OFED版本,建议至少更新到LTS版本。老的驱动对DCQCN、PFC的支持不全面,经常出现奇怪的丢包问题。

基础配置分三步。

第一,开启RMDA相关特性:

# 加载驱动 modprobe mlx5_core # 查看当前网卡设备 ibdev2netdev

确认设备正常后,用mlxconfig配置网卡,核心配置如下:

mlxconfig -d /dev/mst/mt4119_pciconf0 set LINK_TYPE_P1=ETH mlxconfig -d /dev/mst/mt4119_pciconf0 set KEEP_ETH_LINK_UP_P1=1 mlxconfig -d /dev/mst/mt4119_pciconf0 set IP_OVER_IB=0

LINK_TYPE_P1=ETH是让网卡工作在以太网模式,而不是InfiniBand模式。RoCE v2跑在以太网模式下。KEEP_ETH_LINK_UP_P1的作用是保持网卡链路UP,即使没有IP也不down掉,这对RDMA链路状态稳定很重要。

第二,设置网卡上RoCE流量的优先级映射。以优先级4为例,需要把DSCP(Differentiated Services Code Point)和用户优先级(UP)对应起来。RoCE v2的报文是UDP封装,DSCP在IP头的ToS字段里。常见的做法是从优先级4到DSCP 32或者28,具体看你的网络规划。

用rdma link命令可以管理RoCE链接:

rdma link set rxe0 pkey 0xffff

第三,配置网卡上的QoS映射:

# 查看当前映射 ip link show dev enp175s0f0 # 恢复默认映射 rdma link set enp175s0f0 up

真实环境下,推荐直接用tc工具标准化配置:

# 创建优先级4对应的高优先级队列 tc qdisc add dev enp175s0f0 root handle 1: mqprio num_tc 2 map 0 0 0 0 4 0 0 0 queues 0-0@0 1-1@1 hw 1 # 对优先级4的流量设置调度权重 tc qdisc replace dev enp175s0f0 parent 1:1 handle 2: tbf rate 100gbit burst 100m latency 10ms

这里说明一下:map的含义是把优先级0-7映射到不同的TC(Traffic Class),上面的命令里map0 0 0 0 4 0 0 0表示优先级4的流量分配到TC1,其余分配到TC0。这样网卡在发时候就有了多队列的基础,PFC才能按照TC粒度控制。

3.2 第二步:交换机侧PFC配置

由于市面上交换机品牌较多,我以一个比较通用的、类似SONiC的CLI方式来描述。SONiC在云厂商里应用非常广泛,本身也是开源网络操作系统,很多白盒交换机都支持。

首先开启PFC:

# 全局开启PFC config qos pfc enable default # 在指定端口上开启PFC(只有端口1/1和1/2作为RDMA接入端口) config interface pfc on Ethernet1/1 config interface pfc on Ethernet1/2

然后,设置优先级映射。SONiC里一般要写QoS MAP,把报文的优先级映射到内部队列:

{ "TC_TO_PRIORITY_MAP": { "AZURE": { "map": [ {"tc": 0, "priority": 0}, {"tc": 1, "priority": 4} ] } } }

同时配置DSCP到TC的映射,保证RoCE流量可以被识别:

{ "DSCP_TO_TC_MAP": { "AZURE": { "map": [ {"dscp": 32, "tc": 1}, {"dscp": 28, "tc": 1} ] } } }

在SONiC的环境中,这类配置通常放在/etc/sonic/qos.json里,配置完之后需要config reload才生效。

如果是Cisco Nexus设备,配置则更直接:

interface Ethernet1/1 priority-flow-control mode on priority-flow-control priority 4 no shutdown

当然,不同厂商命令有细微差别。但原则一致:写清楚哪个端口开PFC、哪个优先级开PFC、DSCP怎么映射到TC。

3.3 第三步:验证PFC是否生效

配置完并不代表万事大吉,验证手段是绕不开的。

第一步,在服务器上用ethtool查看网口的流控状态:

ethtool -a enp175s0f0

输出里有RX和TX两个方向。PFC模式下,RX表示“是否能够接收Pause帧”,TX表示“是否能够发送Pause帧”。对于无损网络,一般要求RX和TX都开启。如果你看到Pause frames: RX off TX off,就要回头检查网卡驱动和优先级配置了。

第二步,查看PFC帧计数器。在交换机和网卡上都能看到PFC帧的统计:

# 交换机上查看 show interface counters pfc # 网卡上查看 ethtool -S enp175s0f0 | grep pfc

正常情况下,PFC帧数量应该很少,或者为零。如果PFC帧数量频繁增长,说明网络拥塞或者配置存在问题,需要进一步分析。

第三步,跑一个真正的RDMA流量测试。常用工具是ib_write_bw、ib_send_bw,也可以直接跑perftest系列的ib_write_bw -x 1,其中x表示使用RoCE模式。

# 服务端 ib_write_bw -d rxe0 -x 1 -q 8 # 客户端 ib_write_bw -d rxe0 -x 1 -q 8 192.168.1.2

重点看两个指标:带宽是否达到预期(比如100Gbps网卡能跑到90Gbps以上),以及重传计数是否为零。带宽上不去或者重传有增长,基本可以判断PFC或者拥塞控制有问题。

3.4 参数“玄学”:Buffer 阈值、Credit、Watchdog

再深入一点,PFC的“微妙”之处主要集中在三个参数上:

  • Buffer Pool大小:交换机端口上有多个Buffer Pool,PFC队列的Buffer能否被其他队列借用,直接影响了PFC触发频率。Buffer太大,丢包少但延迟高,还可能造成“缓存膨胀”;Buffer太小,PFC频繁触发,带宽利用率低。建议按“一个RTT字节数”来估算。RTT按1微秒算,100Gbps下,一个RTT的带宽时延积约为100Gb/s × 1us ≈ 12.5KB。考虑到突发流量,建议至少给RDMA队列预留2-3个RTT的Buffer,也就是25-40KB,具体看交换机芯片是否有共享Buffer。

  • Credit(信用量):类似流控中的令牌机制,不是所有交换机都叫这个名字,但原理就是每发送多少字节或者每经过多少时间,才能接着发送PFC队列的数据。Credit配置过小会导致带宽利用率下降,过大则失去流控意义,需要根据实际流量模型调优。

  • PFC Watchdog:很重要。配置了Watchdog后,交换机会监控PFC状态,如果长时间收不到数据(比如持续STALL),就会触发恢复机制,强制丢弃或者重置队列状态,避免永久死锁。

这些参数的调整没有“万能公式”,必须根据你的业务流量模型来测试。建议用iperf3压测普通TCP,用ib_write_bw压测RDMA,逐步降低Buffer大小,看吞吐和延迟的变化,直到找到一个“丢包为零、延迟不太高”的平衡点。

4. 血泪总结:常见问题与排查技巧实录

4.1 同一条链路,为什么一边PFC生效一边失效?

有一次我配置了两台服务器直连交换机,A网卡显示链路UP、RDMA能跑,B网卡也能跑,但互相通信时,只要流量一大,A网卡就有大量重传,B网卡倒正常。查了很久,最后发现是A网卡的RoCE流量发出去是优先级3,B网卡接收端所在的交换机端口只开启了优先级4的PFC,导致从B侧发送方向出来没有Pause帧,A侧认为B主动丢包。

这个问题本质上就是优先级不一致。排查方法是用tcpdump抓RoCE报文,看里面IP头中的TOS/DSCP字段。RoCE v2的报文是UDP,端口号是4791(RoCE v2已经改用4791端口),抓到包之后就能看到实际用到的优先级。两端不一致就调整映射。

4.2 PFC死锁在集群环境里比单链路影响的更大

单链路死锁还好排查,真正可怕的是大规模集群里出现PFC风暴。我在测试一个32节点的AI训练集群时,所有节点间跑AllReduce,结果在某种负载条件下,整个集群吞吐会突然降为零,所有交换机端口疯狂打出Pause帧。原因就是多个交换机形成环路,Pause帧互相追赶,造成buffer耗尽。

这种问题的排查手段:

  • 优先看交换机日志,有没有持续大量PFC的告警。
  • 用show interface counters pfc确认是哪些端口在发PFC帧,如果在短时间内计数暴涨,基本可以断定PFC风暴。
  • 再进一步检查拓扑,是不是有冗余链路形成了二层环路。PFC的Pause帧不受STP控制,一旦形成环路,Pause帧会打满全网。

解决办法:除了调整拓扑,还要在交换机上开启PFC Watchdog,给PFC机制上一道保险丝。一旦持续PFC超过阈值,交换机就强制丢弃该队列报文,打破死锁。

4.3 DCQCN才是无损网络的“第二层缓冲”

PFC只是解决了“水位即将溢出时停住水流”的问题,但它没有解决“为什么水位会一直上涨”。真正避免PFC频繁触发的手段,是让发送端主动降速,这会用到DCQCN(Data Center Quantized Congestion Notification)。

DCQCN的流程是:接收端感知到拥塞(比如报文排队延迟增大),发送CNP(Congestion Notification Packet)给发送端;发送端收到CNP后,按照算法降低发送速率,过一段时间再尝试恢复。它和TCP Reno慢启动类似,不过RDMA用一个更加精细的量化步长控制。

命令行检查Mellanox网卡的DCQCN参数:

# 查看dcqcn相关参数 cat /sys/class/infiniband/mlx5_0/ports/1/cc_dcqcn_enable # 设置ECN(Explicit Congestion Notification)门限 echo 20000 > /sys/class/infiniband/mlx5_0/ports/1/cc_ecn_threshold

ECN门限值对PFC触发频率影响极大。阈值设置太小,交换机频繁打上ECN标记,发送端频繁降速,带宽利用率不足;阈值设置太大,交换机buffer又可能被填满,触发PFC。我自己得到的经验值是:对于100Gbps链路、RTT 1微秒左右的场景,threshold大约在15000-25000之间比较合适,具体还要实测。

4.4 常见问题速查表

现象可能原因排查手段解决建议
RDMA带宽上不去PFC未开启或优先级不匹配ethtool -a、抓包确认DSCP开启匹配的PFC优先级,统一映射
重传大量出现交换机Buffer过小、丢包网卡计数、交换机drop统计增大RDMA队列Buffer,调节ECN阈值
PFC帧数量持续增长拥塞控制未配置,发送端持续满发查看DCQCN是否开启,CPU占用率开启DCQCN,调低ECN门限
多端口出现PFC风暴拓扑环路、Watchdog未开启检查STP状态、PFC计数开启PFC Watchdog,调整拓扑
一台设备Pause全网优先级队列被跨业务共用检查业务流量DSCP给不同业务设置不同优先级

5. 经验体会:PFC不是“开关”,是一整套系统的协同

操作到后期,我的一个很深的感受是:PFC作为一个协议,本身并不复杂,但它嵌入在QoS、Buffer、ECN、DCQCN、拓扑设计这一大套系统里,任何一个环节失调,PFC就会从“保护神”变成“杀手”。很多网络工程师把PFC仅仅理解为“在端口上敲几个命令打开流控”,结果上线之后遇到各种性能问题,把锅甩给RDMA,说RoCE不稳定。其实真正的问题在于整个无损网络的配套措施没有跟上。

我个人建议的配置顺序是:

  1. 先梳理业务:哪些流量需要无损?哪些可以容忍丢包?各自对延迟和带宽的要求是多少?
  2. 再做优先级规划:为不同业务分配不同的PCP和DSCP。
  3. 然后配置网卡和交换机的映射,保证两端一致。
  4. 再调整ECN和DCQCN参数,让拥塞机制先于PFC介入。
  5. 最后才把PFC作为兜底机制开启,并配上Watchdog。

这套顺序是我自己试过比较顺的路径。如果一上来就把PFC全部打开,后续排查会增加不少成本。

6. 一个小技巧收尾

最后分享一个实用的小技巧:如果你手头的Mellanox网卡遇到了“带宽对不上”的问题,可以先去查看/sys/class/infiniband/mlx5_0/ports/1/cc_*下面的参数,这些文件直观反映了当前ECN门限、DCQCN使能状态、速率恢复时间等。很多情况下,性能不好不是硬件问题,而是ECN阈值设得过高,导致拥塞信号一直没有触发,等到PFC出来救场时已经晚了。

调参时可以一边用ib_write_bw压测,一边修改ECN阈值,观察带宽和PFC帧计数的变化。找到一个“PFC帧数低、带宽稳定”的窗口,把这个阈值固化到网卡配置里,之后再做大规模集群测试,稳定性会好很多。这个“边压测边调阈值”的方法,我百试不爽,你也可以直接照做。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询