
先说一个结论RDMA无损网络里最容易出幺蛾子的往往不是RDMA协议本身而是PFCPriority Flow Control优先级流控制。我在机房调了大半年无损配置最惨的一次是某个训练集群压测时整网吞吐从接近线速掉到几百兆查了三个通宵最后发现只是某个中间交换机把RoCEv2的优先级映射弄错了。这篇文章不讲PPT直接讲把PFC从配置到稳定运行的完整过程包括五步配置法、死锁排查和一个可落地的巡检清单适合正在做RoCE集群、存储网络或者AI算力网络的人参考。1. 为什么RDMA网络必须“无损”丢包意味着性能崩塌1.1 一次线上训练故障的复盘当时我负责一个由40台GPU服务器组成的小集群用RoCEv2跑分布式训练交换机都是万兆。最开始没有开PFC压测时发现只要多机同时读一个存储节点典型Incast模式接入交换机的出口瞬间就会饱和。TCP流量还能靠内核重传兜底RoCE流量直接卡死几分钟训练任务频繁中断。后来逐步搞清楚了一件事RDMA依赖网卡硬件直接读写远端内存它的传输模型默认网络不会丢包。数据包一旦被丢弃要么等超时重传要么触发大量NAK重传风暴。重传风暴会消耗大量CPU中断和带宽真正的计算任务反而跑不起来。正因如此RDMA对丢包率的要求通常在1e-9甚至更低而普通以太网虽然日常丢包率不高但在突发拥塞场景下远达不到这个量级。1.2 RDMA丢包的真实代价很多朋友会把RDMA和TCP混为一谈觉得“丢包以后有重传就行”这是最大的认知误区。下面这张对比表可以帮助理解差异维度TCPRoCEv2封装底层内核协议栈处理UDP封装网卡硬件处理丢包检测靠ACK超时/重复ACK拥塞窗口减半靠QP超时或NAK流恢复依赖上层丢包惩罚带宽腰斩但还能慢速续命重传风暴CPU被中断占满业务停滞拥塞控制内核TCP栈自带cubic/reno需要网卡与交换机的ECN/DCQCN联动从这张表就能看出TCP是“丢包也能凑合活”RoCEv2是“一旦丢包就崩给你看”。为了保证不丢包二层网络必须额外提供无损能力这也是PFC存在的最根本原因。1.3 PFC在无损网络中的角色PFC是IEEE 802.1Qbb定义的链路层流量控制协议。它的工作方式很简单接收端某个优先级的队列占用超过阈值就向上游设备发送一个pause帧上游收到后暂停发送该优先级的数据等水位回落再恢复。注意PFC是逐跳的背压机制不是端到端的可靠传输。PFC解决了“端口buffer溢出导致丢包”的问题但它也有代价不会丢包不代表不会延迟。一个队列被pause住数据就在路径上积累整体延迟会上升。因此生产环境里的无损网络一定不是只靠PFC而是三层配合PFC做最后一层兜底防止buffer溢出丢包交换机WRED/ECN提前打拥塞标记让网卡主动降速RDMA网卡根据ECN反馈执行DCQCN拥塞控制三者缺一个网络都会出问题但最底层、最容易配错的就是PFC。2. PFC配置前先想清楚的事队列、映射与buffer预留2.1 核心原则先对齐端网映射再动交换机很多人一上来就跑到交换机上敲PFC命令敲完发现不生效根本原因是端侧网卡发出的DSCP值、802.1p优先级和交换机内部的队列没有对齐。以RoCEv2最常用的配置为例Mellanox/NVIDIA网卡默认使用DSCP 26推荐映射到802.1p优先级3交换机上对应队列3开启PFC。在网卡侧可以用rdma qos show link这类命令确认当前生效的DSCP、PFC和ECN配置。只要任何一跳把DSCP映射到别的队列整条无损链路上的PFC都不会按预期工作。我踩过最典型的坑是级联场景接入交换机按DSCP映射到队列3但汇聚交换机没有开启trust dscp二层帧里的802.1p优先级被重新打了标签RoCE流量被丢到普通队列结果接入交换机一直在发pause帧汇聚交换机却根本不响应。2.2 PFC五步配置法这里给出一套我在多个品牌设备上都验证过逻辑的流程具体命令语法以你手头设备为准但步骤顺序千万不要乱选定PFC优先级队列通常取队列3或队列6整条链路保持一致在端侧和交换机侧完成DSCP到802.1p到内部队列的映射对齐在参与RoCE的所有物理端口开启PFC模式设置为on而不用auto协商为该队列配置合理的buffer阈值和headroom空间开启PFC死锁检测与自动恢复下面这段是“示意配置”不是直接复制到某品牌设备就能用的但语义完全一致! 交换机侧以常见设备语义为例具体命令请查对应手册 interface Ethernet1/1 priority-flow-control mode on trust dscp qos queue 3 pfc enable qos queue 3 pfc headroom 512 KB ! 网卡侧以RDMA工具为例 rdma qos set link eth2 dscp 26 rdma qos set link eth2 pfc on queue 3 rdma qos set link eth2 ecn enable queue 3这里重点说两个容易忽略的地方。第一PFC模式不要用auto。有些交换机支持自动协商但无损网络要求两端都确认开启自动协商失败时设备会偷偷关掉PFC这种“悄悄退化”非常致命。生产环境我全部显式写死为on。第二headroom不能拍脑袋填。headroom是接收端发出pause帧后对端在收到请求前还能继续发送的数据量。它需要覆盖最差情况下的带宽乘以端到端往返时延再加上安全余量。如果headroom设太小突发时照样丢包设太大又会吃掉共享buffer普通流量容易挤爆。2.3 验收怎么确认PFC真的生效了配置完不要急着跑业务先确认PFC是真的在起作用还是只是“配置没报错”。我的验收顺序如下查看交换机各端口PFC暂停帧计数跑流前后这个计数应该有明显变化查看网卡侧ethtool -S中的rx_prio3_pause_xon/xoff类计数确认收到了pause帧用多打一压测构造Incast场景观察端到端吞吐曲线是否稳定重点观察队列drop计数开启PFC后RoCE优先级的丢包计数应当接近零如果其他队列的流量丢包突然增多也别急着怀疑PFC很有可能是因为headroom挤占了共享buffer需要回头调整阈值。3. 配置完就翻车死锁与反压风暴的完整排查链路3.1 现象一流量瞬时归零恢复时间不定我第一次在生产集群上正式启用PFC后业务平稳跑了大概两周突然有一天所有RoCE流量同时归零管理面SSH还能通但训练任务全部卡住。更诡异的是过几分钟又自己恢复了没有任何端口报错TCP却没受影响。排查时盯着各端口的drop计数发现全是零后来切换视角看pause帧统计才发现队列3的pause帧像洪水一样在几个端口之间来回发送。简单画了数据路径之后真相大白两条上联链路形成了逻辑环路队列3的流量在环路里互相pause最终谁都发不出去形成PFC死锁。3.2 现象二非PFC队列流量被饿死另一个高频事故是普通流量被“饿死”。有一阵子管理面和存储读流量经常出现高延迟SSH操作像老年机一样但RoCE业务本身很稳。查到最后发现有人把管理流量和RoCE流量都映射到了同一个DSCP 26管理流量也进了PFC队列3。这带来的问题很微妙管理流量本来不使用ECN降速但它和RoCE挤在同一个队列里一旦队列被pause管理流量也跟着停。更要命的是PFC按优先级暂停不会区分是业务流量还是管理流量。后面我定了一条铁律管理、带外、存储和RoCE流量的DSCP必须严格隔离宁可在交换机上多配几条队列映射规则也绝不混用。3.3 排查链路从错误包计数到队列统计很多人遇到PFC相关故障会习惯性看接口error、CRC、Oversize但PFC问题往往一个错误都找不到。完整的排查链路应该是先确认是“丢包型故障”还是“暂停型故障”看队列drop和pause帧计数查看所有交换节点上每个优先级的pause帧收发计数定位谁在发pause、谁被pause画出RoCE所有数据路径标出可能的环路和故障点检查PFC死锁检测是否开启超时时间是否合理逐步隔离可疑端口验证故障是否消失我用的一个笨办法把故障期间的pause帧计数器按端口拉成曲线看是“从某个端口扩散出去”还是“全网同时爆”。前者多半是单点反压风暴后者多半是环路或配置漂移。3.4 为什么标准协议栈看不出端倪不少刚接触无损网络的同事会问Linux的ifconfig里没有报错怎么网络就不通了因为PFC的pause帧发生在链路层IP层和TCP层完全无感设备状态显示UP也没有error包。只有专门去数每个优先级的pause帧才能发现问题。更麻烦的是PFC死锁恢复前队列里的数据既不丢也不前进所有上层超时机制都不起作用。这也解释了我第一次遇到的“流量凭空消失几分钟又恢复”的现象——可能是死锁被某种机制打断或者队列在超时后自己清空了。所以生产环境一定不能只依赖“自然恢复”必须主动开启死锁检测。4. Buffer预算、ECN联动与日常巡检从“能跑”到“跑得稳”4.1 buffer预算的计算方法PFC的headroom不是一个可以随便填的参数它应该有一个明确的预算推导。可以用下面这个估算公式headroom_bytes ≈ (N - 1) × 端口速率 × 最大RTT / 8其中N是突发方向同时进入的端口数RTT要取最差情况下的端到端延迟而不是平均值。举个例子8个入端口同时向一个100Gbps出口突发最大RTT约5微秒那么headroom大约是(8 - 1) × 100Gbps × 5us / 8 437.5KB这还只是理论最小值实战中我习惯乘以1.5到2的安全系数再考虑同队列正常通信量的占用最终为这个队列预留2MB到3MB的buffer空间。问题来了如果交换机共享buffer一共就十几MB其他队列的剩余空间会变得很紧张普通流量在拥塞时更容易丢包。所以每调一次headroom都要同时关注非PFC队列的丢包计数。有一次我把某个存储队列的headroom从2MB调到8MBRoCE流量稳了但同一个交换机上的监控流量开始频繁丢掉因为共享buffer被挤没了。这类取舍必须结合业务的实际流量模型来做。4.2 ECMP和Hash冲突引发的不均衡PFC只保证“不丢”但它管不了“均不均”。无损网络里最容易被忽视的问题是ECMP哈希不均衡多条大流hash到同一条链路时即便没有丢包延迟也会很高且触发ECN降速。调整思路有这么几个增加RoCE流的数量让哈希结果更分散使用交换机支持的对称哈希让双向流落到相同路径把大流和小流适当隔离避免小流被pause影响关键存储流量走独立物理链路或独立的PFC优先级我在一个存储集群上把两条100G上联的哈希策略从默认IP五元组改成对称哈希之后带宽利用率从65%提升到90%PFC的pause帧数量也下降了一个数量级。这个改动不需要动任何线缆但效果极其明显。4.3 ECN与PFC的组合拳PFC单独使用很容易进入“慢速拥塞”状态队列没有溢出但延迟越来越高。ECN的价值就在这里。交换机WRED在队列深度达到阈值时不再丢弃RoCE包而是打上CE标记网卡收到后主动降低发送速率。我给接入交换机设置的ECN阈值通常是min-threshold设为PFC预留buffer的40%到60%max-threshold设为80%到90%标记概率不要用默认的1/100我一般用1/10反应更灵敏这样设计后正常情况下流量由ECN提前调速PFC只处理极端突发全网buffer不会长期被占满。如果只开PFC不开ECN即使不丢包也容易碰到“网络没断但性能骤降”的诡异问题。4.4 常规巡检清单最后列一份我现在每次变更和每周巡检都会对照的清单每一条都对应真实的故障经验检查项查看位置预期表现队列drop计数交换机各端口队列统计RoCE队列接近零PFC暂停帧计数端口priority pause统计有波动但无持续风暴PFC死锁事件计数交换机死锁检测统计连续一周不应增加ECN标记计数WRED/ECN计数器与拥塞程度正相关各优先级buffer占用交换机queue occupancy不超过预留阈值的90%阈值配置漂移变更管理数据库配置与模板完全一致这套巡检脚本我尽量自动化每五分钟采集一次计数器设两个告警阈值PFC死锁计数变化属于高优先级告警pause帧环比大幅上升属于中优先级告警。第一次踩坑之后我再也不想靠人肉盯屏发现问题了。调PFC这段时间我最大的体会是无损网络不是一次性配置出来的而是一个不断迭代的工程。后来我每次做变更都带着巡检清单改PFC阈值的同时会盯普通流量观察至少一周。尤其不要一次把headroom调到最大风险非常高最好以500KB到1MB的粒度逐步调整。如果你们也准备上无损网络建议先在一个小规模环境把PFC计数、死锁检测和ECN联动这套流程跑顺再去碰生产集群。这些坑提前踩过后面能少走很多弯路。