
RDMA网络刚上手那阵子我一度以为只要把网卡插上、驱动装好、跑个测试工具就能享受到“绕过内核、零拷贝、超低延迟”的极致体验。结果被现实狠狠教育了一顿Performance 上不去、断连、重传、丢包各种玄学问题轮番上演。折腾到最后几乎所有问题的矛头都指向同一个东西——PFCPriority-based Flow Control基于优先级的流控也就是无损网络里那个让人又爱又恨的“开关”。这篇东西不打算写成教科书而是想把我自己在配置无损网络、调优PFC过程中踩过的坑、总结出的思路以及最后怎么把网络从“半残”调到“相对稳定”的过程记录下来。如果你也在搞RDMA或者正准备给存储集群、AI训练集群搭建RoCE网络希望这篇能让你少走点弯路。1. 先搞清楚无损网络和PFC到底在解决什么问题1.1 RDMA与“有损”网络的天然冲突RDMARemote Direct Memory Access的核心卖点是绕过CPU、绕过内核让数据直接从网卡到应用内存省去内核协议栈的拷贝、中断、上下文切换。理念很性感但这套机制有一个致命前提网络不能丢包。传统TCP为什么能容忍“有损”因为TCP有滑动窗口、有ACK重传、有拥塞控制丢了包可以重传延迟大了可以降速。但RDMA的设计假设是“网络足够可靠”数据到了网卡就直接交给应用了没有内核兜底。一旦发生丢包RDMA网卡只能整个报文序列重传而且重传粒度不是单个MTU可能是多个报文一起重传。这个代价在数据中心场景里是灾难性的哪怕只有万分之一的丢包率吞吐量都可能直接暴跌到一半以下。所以就有了“无损网络”这个概念让网络设备在正常情况下不会丢包。而实现无损的关键技术之一就是PFC。1.2 PFC不是“暂停整个网口”而是“暂停某一条队列”很多人对PFC的误解是它像802.3x流控一样把一个端口整个暂停掉网卡说停就停交换机能空转就空转。实际不是PFC的粒度是“优先级队列”级别。以太网报文里有PCPPriority Code Point字段3个bit可以标识8个优先级。PFC协议IEEE 802.1Qbb允许交换机对每一条优先级队列独立做流控当某个队列的缓存占用超过阈值时交换机向对端设备发送暂停帧Pause Frame让对端暂停发送这个优先级的数据但其他优先级不受影响。这意味着你可以把RDMA流量放到高优先级队列把普通TCP放到低优先级队列PFC暂停的时候只暂停高优先级TCP流量照常走。这样RDMA流量“无损”TCP流量用传统重传机制兜底各得其所。但是这个“各得其所”是理想状态。实际配置中队列划分、优先级映射、缓存阈值、死锁检测每一个环节都可能搞出问题。我后面会详细拆。1.3 分类探讨RoCE v2环境下PFC的典型架构目前数据中心里常见的RDMA实现是RoCE v2RDMA over Converged Ethernet version 2它把RDMA报文封装在UDP/IP里可以在普通IP网络上跑。但因为跑在UDP上就没有TCP的拥塞控制机制必须依赖底层网络提供无损通道。典型架构是这样的服务器上的RDMA网卡Mellanox CX5/CX6、Broadcom网卡等把流量打上特定优先级通常是优先级3也有用4的作为高优先级流量发送。交换机端启用PFC针对优先级3开启流控。CNPCongestion Notification Packet机制配合DCQCN数据中心量化拥塞通知算法作为端到端的二级流控发送端通过接收CNP来感知拥塞调整发送速率。PFC解决的是“交换机缓存溢出”的问题DCQCN解决的是“源端速率调整”的问题。很多人只配了PFC没配DCQCN结果PFC频繁触发Pause帧把整个网络的局部带宽全拖垮了这就是典型的“只治标不治本”。2. 配置PFC前的关键抉择方案选型与设计思路2.1 先确定流量优先级别乱抢“车队”我见过很多团队直接照抄网络大厂的配置优先级3给RDMAVLAN里优先级的映射按标准来Egress buffer留足。结果全网一上业务就出问题——因为他们没考虑自己的业务特点。举个例子AI训练集群里有两个重要流量GPU通信流量AllReduce、AllGather和普通存储流量读写日志、模型文件。这两个都是RDMA流量但特点是不同的GPU通信对延迟极端敏感必须在几微秒内完成同步存储流量对吞吐有要求但延迟容忍度稍微高一些。如果你把两种流量都放到同一个PFC优先级队列里一旦存储流量突发占用完bufferGPU通信就得一起被Pause整个训练任务的同步时间就会被拖长。这非常影响分布式训练效率。我的建议是把RDMA流量至少分成两个PFC优先级。优先级3GPU通信流量高优先转发优先但触发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或者2Pause帧根本管不到它。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_P1ETH mlxconfig -d /dev/mst/mt4119_pciconf0 set KEEP_ETH_LINK_UP_P11 mlxconfig -d /dev/mst/mt4119_pciconf0 set IP_OVER_IB0LINK_TYPE_P1ETH是让网卡工作在以太网模式而不是InfiniBand模式。RoCE v2跑在以太网模式下。KEEP_ETH_LINK_UP_P1的作用是保持网卡链路UP即使没有IP也不down掉这对RDMA链路状态稳定很重要。第二设置网卡上RoCE流量的优先级映射。以优先级4为例需要把DSCPDifferentiated 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-00 1-11 hw 1 # 对优先级4的流量设置调度权重 tc qdisc replace dev enp175s0f0 parent 1:1 handle 2: tbf rate 100gbit burst 100m latency 10ms这里说明一下map的含义是把优先级0-7映射到不同的TCTraffic 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 PoolPFC队列的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流量发出去是优先级3B网卡接收端所在的交换机端口只开启了优先级4的PFC导致从B侧发送方向出来没有Pause帧A侧认为B主动丢包。这个问题本质上就是优先级不一致。排查方法是用tcpdump抓RoCE报文看里面IP头中的TOS/DSCP字段。RoCE v2的报文是UDP端口号是4791RoCE 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频繁触发的手段是让发送端主动降速这会用到DCQCNData Center Quantized Congestion Notification。DCQCN的流程是接收端感知到拥塞比如报文排队延迟增大发送CNPCongestion Notification Packet给发送端发送端收到CNP后按照算法降低发送速率过一段时间再尝试恢复。它和TCP Reno慢启动类似不过RDMA用一个更加精细的量化步长控制。命令行检查Mellanox网卡的DCQCN参数# 查看dcqcn相关参数 cat /sys/class/infiniband/mlx5_0/ports/1/cc_dcqcn_enable # 设置ECNExplicit Congestion Notification门限 echo 20000 /sys/class/infiniband/mlx5_0/ports/1/cc_ecn_thresholdECN门限值对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不稳定。其实真正的问题在于整个无损网络的配套措施没有跟上。我个人建议的配置顺序是先梳理业务哪些流量需要无损?哪些可以容忍丢包各自对延迟和带宽的要求是多少?再做优先级规划为不同业务分配不同的PCP和DSCP。然后配置网卡和交换机的映射保证两端一致。再调整ECN和DCQCN参数让拥塞机制先于PFC介入。最后才把PFC作为兜底机制开启并配上Watchdog。这套顺序是我自己试过比较顺的路径。如果一上来就把PFC全部打开后续排查会增加不少成本。6. 一个小技巧收尾最后分享一个实用的小技巧如果你手头的Mellanox网卡遇到了“带宽对不上”的问题可以先去查看/sys/class/infiniband/mlx5_0/ports/1/cc_*下面的参数这些文件直观反映了当前ECN门限、DCQCN使能状态、速率恢复时间等。很多情况下性能不好不是硬件问题而是ECN阈值设得过高导致拥塞信号一直没有触发等到PFC出来救场时已经晚了。调参时可以一边用ib_write_bw压测一边修改ECN阈值观察带宽和PFC帧计数的变化。找到一个“PFC帧数低、带宽稳定”的窗口把这个阈值固化到网卡配置里之后再做大规模集群测试稳定性会好很多。这个“边压测边调阈值”的方法我百试不爽你也可以直接照做。