资讯动态

RDMA与InfiniBand传输层深度解析:可靠性、流控与拥塞控制机制

发布时间:2026/10/19 9:32:48 来源:尧图企业网站定制
RDMA-InfiniBand传输层功能分析这个系列写到第四篇我自己也没想到能坚持这么久。前几篇我们依次拆过传输层在整个协议栈里的位置、QP与WR的运作方式、内存注册机制以及报文头的基本构成。如果你一路跟到这里应该已经能看懂一个数据包从发送方投递到接收方CQ的完整链路。但说实话“看懂流转”和“真正搞明白为什么InfiniBand能在百Gbps的链路上做到不丢包、不乱序、不被突发流量打穿”中间还隔着一层非常深的功夫。这一篇我就打算集中聊聊那些平时看不见、故障时却让人满脑子问号的部分可靠连接的确认与重传、链路层流动控制的隐形约束、拥塞控制闭环以及RDMA Read和原子操作在传输层内部的完整流程。这篇内容主要适合两类人一类是已经在实际环境里用InfiniBand、偶尔遇到性能抖动或者点对点重传异常的人另一类是刚把RDMA理论学完、想把这些协议状态和头字段对应到真实链路行为上的同学。内容会有点烧脑我会尽量把每个机制拆成三个层面来讲——它为什么存在、它怎么工作、出了问题该怎么查。这样即使某个环节你在生产环境里还没遇过至少出问题时能有个排查思路而不是对着计数器瞎猜。1. 这一篇要聊的“看不见的功能”可靠性、流控与拥塞先说个背景。之前有朋友问我你们天天讲传输层是“可靠连接”“不可靠连接”到底靠什么来保证可靠网卡上那些硬件状态机究竟在忙些什么我当时的回答是传输层的可靠很像一个优秀后台系统的日志和重试机制——平时你完全感觉不到它存在直到有一天它开始大量重传、疯狂报错你才知道原来这套机制承担了这么多活。拿一次实际经历开场。某套模拟项目X的高性能计算集群用的是InfiniBand EDR网络平时跑分布式训练任务非常稳众生带宽都能跑到90%以上。结果在某次升级固件之后的第二天节点间的带宽突然从接近线速掉到六成左右链路本身没有任何降级告警光模块光功率正常物理层误码率也几乎没有变化。最后查了一圈才发现问题出在链路初始化后的credit协商阶段新固件对接收缓冲区的credit初始化方式变了导致发送端在高速发包时频繁陷入“有包要发但没信用额度”的等待状态。这个案例我后面会展开细讲但它很好地说明了一件事InfiniBand传输层的稳定靠的不只是物理链路够快还有一套极其严谨的确认、重传、流控和拥塞控制机制。所以这一篇的定位就很清楚了前几篇我们讲的是“包怎么发出去、怎么被接收”这一篇重点讲“传输层如何保证发出去的东西是对的、快的、不会把网络打挂的”。包括四个主题RC连接的确认与超时重传、链路标签化的credit流动控制、FECN/BECN拥塞控制闭环以及RDMA Read和原子操作这类“非简单发送”操作的内部流转。每一条都是我在实际环境里踩过坑或者反复查过规范才弄明白的希望能给你省点时间。2. RC连接确认的秘密ACK不是每一个包都回的2.1 从PSN到MSN可靠连接的核心记账单位在可靠连接RC里发送方发出的每一个数据包都会带一个包序号PSNPacket Sequence Number接收方通过PSN来判断这个包是不是乱序、是不是重复、有没有缺漏。很多人以为InfiniBand是“每发一个包就等一个ACK收到再见缝插针发下一个”如果真是这样RDMA的性能根本没法看因为一个RTT内只能有一个包在飞。实际机制是发送方可以连续发出大量数据包接收方不需要回应每一个包而是会返回一个累积确认Acknowledgment表示“我这边连续收到的包已经到哪个序了”。这里要提一个容易混淆的点PSN负责的是“包”的次序而确认报文里的MSNMessage Sequence Number负责的是“消息”级别的进度。传输层硬件维护这两套账本目的就是为了一边支持乱序接收一边又能快速判断哪些数据可以安全地从重传缓存里释放。简单打个比方PSN像快递运单号每件快递都有MSN像订单号一批货里的某一件到了不代表整个订单完成只有订单里所有运单都签收才算整单结束。2.2 NAK与RNR两类“不正常的正常回应”除了正常ACKRC连接还有一种常见回应叫NAKNegative Acknowledgment表示接收方收到了包但校验或者序号上有问题。比如接收方收到一个PSN跳跃的包发现中间有缺失就会返回带具体PSN的NAK发送方看到后会从那个包开始重传。这里很多人会问为什么不直接依赖超时重传就好了答案是性能。等超时要浪费一个RTT而NAK能在一个RTT内把丢包信息反馈回去缩短故障恢复时间。还有一种特殊回应叫RNRReceiver Not Ready它跟丢包无关跟接收端的缓冲区状态有关。接收方明明物理上还在正常工作但对应的接收队列里没有预投递WRWork Request也就是“接收端的软件还没来得及准备好缓冲区”。此时RC发送方收到RNR后不会立刻重传而是会按RNR重试间隔等待一下再尝试重发。这个机制非常关键因为它隔离了“网络损坏”和“接收端软件繁忙”两种完全不同的问题。RNR重试次数是用独立参数控制的也就是我们常用的rnr_retry_count。2.3 超时参数不是玄学一个指数公式看清全貌RC发送方不会永远等一个ACK。每个未确认的包都对应着一个超时定时器超时后首先触发重传。InfiniBand里超时时间跟local_ack_timeout这个参数相关它的计算方式是指数式的base 4.096微秒乘以2的local_ack_timeout次方。也就是说这个值每增加1超时时间就翻一倍。听上去很简单但实际配置时特别容易踩坑因为网卡驱动和固件对某些特殊状态可能还有额外的内部调整。我整理过一份常用参考值可以让你对这个参数有直观感受local_ack_timeout理论超时时间典型使用场景感观12约16.8ms很短的机房内链路队列深度很浅的测试14约67.1ms常见HPC集群默认级别适合绝大多数短距离网络16约268.4ms链路延时略高或负载重时更稳妥20约4.29s极端情况下才会用通常意味着软件协同上出了问题要注意本地配置的local_ack_timeout必须大于等于对端处理一个请求最坏情况所需的时间否则就会出现“包其实没丢但对端处理太慢本地已经等不及开始重传”的现象。这种非预期重传比真的丢包更折磨人因为链路质量明明没问题性能却在不断下降。2.4 重传风暴的复盘超时值设置错误比网络丢包更常见去年我为某存储项目调优时遇到过一个很有意思的现场。两个节点之间的RC连接明明很稳但节点CPU的中断开销飙高同时网卡重传计数持续增长。最初大家怀疑是光纤链路有隐性故障把链路层计数器全部翻了一遍那些物理错误符号、CRC错误、信号完整性相关计数都为零。后来实在没方向我顺手把两端QP状态里的local_ack_timeout值和linpack实测延迟对了一下发现配置居然只有10按公式算理论超时时间也就4毫秒左右而对面节点当时因为做快照压缩CPU被占满最坏处理延迟跑到了十几毫秒。这就形成了典型的“健康链路、疯狂重传”包根本没丢只是响应慢发送方却误判为超时反复把整串未确认包重新发一遍。结果接收方收到重复数据包后又要去重CPU进一步升高延迟进一步拉大形成一个软性的正反馈循环。解决办法也很朴素——把local_ack_timeout调到16同时给接收端预留足够的接收队列深度几分钟之内重传计数就下来了带宽恢复。所以排查RC性能问题时请先把“是不是真的丢包”和“是不是对方处理太慢”这两件事分开不要一看到重传就觉得是链路坏了。3. Credit机制带宽的隐形天花板藏在缓冲区里3.1 为什么说credit是InfiniBand自己的“信用证制度”InfiniBand的流动控制跟以太网那种“丢包后TCP降速”的思路完全不同。它从链路层就建立了一套信用机制专业叫法是link-level credit。简单理解每个接收端口在初始化时会告诉对端“我的接收缓冲区有多大你可以发多少数据给我”这个额度就是credit。后续每收到一个包接收方会通过反馈机制补充额度发送方只在拥有足够credit的时候才允许发送数据。这个机制最优雅的地方在于它本质上是一种“绝对不丢包”的保险只要发送方严格按credit额度发接收方的硬件缓冲区就不会溢出。换句话说InfiniBand链路层的丢包在正常情况下应该是零。正因为有这层信用保障传输层的重传才可以做得那么轻——它不需要处理链路拥塞导致的随机丢包只需要处理故障、重启、线路物理层差错等低频事件。credit的计量单位不是字节而是64字节的块64-byte words。接收方在链路初始化时发送的credit值和后续动态更新的credit值都是基于这个颗粒度。一个数据包要发送会先从credit里扣掉包长对应的块数接收方把包收进缓冲区后会返回credit增量。这种设计让credit管理在硬件里可以做到非常高效不需要复杂的缓存指针同步只需要一个计数器加减。3.2 credit耗尽的表现不是断连而是“周期性停顿”很多第一次接触InfiniBand的人会有一个直觉credit不够就重传或者丢包呗。实际完全不是。当发送方没有credit时它不会把这个包发出去而是把包留在发送队列里等待credit更新。从外面看链路是up的物理层信号也是好的但发送方的端口实际处于“想发但不能发”的欠授状态。表现出来就是带宽上不去具体点说带宽会出现周期性的锯齿状波动平均吞吐下降但不会出现严重丢包。这里要特别提一个容易误判的点。如果整个网络中只有一个端口的credit不够往往不会同时影响所有流量只会影响从这个端口发往特定对端的那些QP。所以我们排查时会看到某个节点到某个节点性能差但从这个节点发往其他节点都正常。这种局部性性能问题如果不熟悉credit机制很容易被误判成“对端网卡坏了”或者“对端应用处理不过来”。3.3 如何判断credit是不是元凶关注port_xmit_wait这一类计数器判断credit问题最直接的手段是看端口计数器。最常见的是port_xmit_wait这个计数器表示发送端口因为缺少credit而等待的时间。如果它持续快速增长那就说明发送端长期处于等待credit的状态链路根本没有满负荷跑起来。与之对照的还有port_xmit_data这类计数可以用来算实际吞吐。我在排查过程中还会做一个小实验临时把MTU从默认值调低然后重新跑一次perftest里的带宽测试。为什么这么操作因为credit是按64字节块计算的而MTU决定了一个包的最大长度。在credit总额相同的情况下MTU越大每个包消耗的credit块越多如果credit更新频率跟不上大包更容易暴露credit不足把小包测试和大包测试放在一起对比如果小包带宽明显更接近线速而大包带宽下降credit瓶颈的可能性就很大。某个具体阈值会因为网卡型号和固件实现不同有差异但对比逻辑是一致的。3.4 一个关于credit初始化的真实教训回到开头那个项目X的案例。当时升级固件后带宽掉到六成我查过port_xmit_wait数值高得吓人几乎每个发送包都经历过等待。随后对端固件团队确认新版本在链路初始化时把接收缓冲区中用于credit声明的保留区域调整了导致通告给对端的credit数量比旧版本少了很多。链路层本身没有问题发送端的port_xmit_wait却一直在累加带宽自然上不去。这个案例给我的经验是升级网卡固件或者交换机固件后一定不要只看链路状态和误码率还要把port_xmit_wait的基线数据和升级前做对比。很多“升级后性能下降”的诡异问题最后都落在credit这种初始化参数上。毕竟credit的协商发生在链路训练阶段一旦建立连接就不会再自动重新协商除非重启端口。4. 拥塞控制闭环FECN/BECN不是两个开关是一条反馈链4.1 从接收方到发送方的“逐跳刹车”是怎么完成的软硬件发展到InfiniBand这个层面网络不可能完全靠静态配置避免拥塞。多个发送方同时涌向一个目的端口接收端缓冲区就可能出现堆积。InfiniBand的拥塞控制机制Congestion Control是一套闭环系统接收端在检测到拥塞风险时会在正常返回的包上做一个特殊标记叫FECNForward Explicit Congestion Notification收到带FECN标记的节点会向这个连接真正的发送方返回一个带BECNBackward Explicit Congestion Notification标记的控制包真正的发送方看到BECN后就会按照拥塞控制算法主动降速。可以这样理解整条链路FECN是“下游向上游传递堵车情报”BECN是“上游收到情报后踩刹车”。所以BECN并不是一个独立存在的开关值而是整个反馈链的一环。如果接收方从不标记FECN发送方就永远不知道拥塞傻乎乎地继续全速发如果发送方不响应BECN那标记再多也没用该堵还是堵。4.2 CC参数不是越大越好CC_Timer和CC_Slope的实际调优InfiniBand规范里给拥塞控制留了不少可调参数最常被提到的就是CC_Timer拥塞控制定时器和CC_Slope降速斜率。CC_Timer决定了发送方收到BECN后检查速率的频率CC_Slope则决定了每次检查时降速的幅度。很多人的第一个误区是“拥塞控制要激进一点把它调大或者调小都行”这就不对了。CC_Slope如果设置得太陡稍微一点拥塞带宽就会掉得很厉害设置得太缓拥塞又压不住。我遇到过一次比较典型的拥塞参数问题某分布式训练任务在节点间做allreduce时总是出现偶发性的尾部延迟而平均带宽看着还行。抓包发现发送方确实收到了大量BECN但CC_Slope设置过于平缓导致降速后很快又恢复原速然后再次被BECN如此反复震荡。这种震荡比稳定降速更糟因为它会让链路的延迟抖动变大最终影响集合通信的同步性能。把CC_Slope适当调陡、CC_Timer调短一点后尾部延迟就明显稳定下来。不过要提醒一句不同厂商对CC参数的实现和取值范围差异非常大有些还可以在子网管理器里做全局策略。真到调参环节一定要以你手上实际设备的官方文档为准我这里给的是通用思路不是通用数值。4.3 说说最容易混淆的点传输层拥塞还是链路层拥塞排查拥塞问题最容易走进死胡同的地方就是分不清到底是链路层credit不足还是传输层主动降速。我自己的判断顺序是这样的先看port_xmit_wait如果它在增加说明是链路层credit问题跟拥塞控制无关然后看CC相关的计数器某些厂商的网卡驱动会导出received_fecn、transmitted_becn之类的统计如果这些数值也在增长说明是拥塞控制闭环被激活了这时才需要去调CC参数或者检查流量的汇聚模式。这两个机制虽然都表现为“发送端被限制”但性质完全不同。credit限制是物理上不允许发属于链路层的硬性约束CC降速是逻辑上主动放慢属于响应拥塞的软性调节。如果搞反了去调拥塞控制参数解决credit问题那结果大概率是浪费时间还调不出效果。5. RDMA Read与原子操作传输层里最复杂的两个“特殊服务”5.1 RDMA Read不是“把数据拿回来”而是一次完整的子流程很多人学RDMA的时候总觉得RDMA Read就是“远程读到本地内存”好像一个请求过去数据就回来了。实际上在传输层内部RDMA Read远比RDMA Write复杂。发一次RDMA Read发送方首先会构造一个专门读请求包Read Request里面携带远程虚拟地址、远程R_key以及要读取的长度。接收方收到后并不是直接返回一个“数据包”那么简单它会去自己的内存里把这段数据取出来然后可能拆分成多个Read Response包发送回来。这里有一个关键点Read Response的方向和Read Request是相反的。请求是发送方发给接收方而数据是接收方主动发回给发送方。这意味着一个中型规模的RDMA Read就可能触发几十上百个反向数据包它们在网络中会有独立的PSN和发送顺序。发送方收到这些Read Response后要按请求包里的RETH信息进行组装并写入本地内存整个过程涉及发送方、接收方、内存DMA引擎的多方协作。因为存在多个Read Response接收方还必须决定“到底一次可以发回多少数据”这取决于响应buffer和流控约束。所以传输层为RDMA Read设计了一套跟普通Send/Write都不一样的确认节奏接收方会用AETH里的MSN来报告“我已经完成了第几个Read Request”而不仅仅是报告“我收到了几个包”。这种消息级确认能保证多个跨节点的Read请求不会互相穿插错乱。5.2 RETH里的关键字段一块远程内存的“通行证”RETHRDMA Extended Transport Header是专门为RDMA Read/Write设计的一个头里面最重要的三个字段就是虚拟地址、R_key和数据长度。R_key和老朋友L_Key还不完全一样L_Key主要用于本地内存操作的权限校验而R_key是暴露给对端使用的远程访问钥匙。远程节点只有同时拥有正确的R_key和目标内存区域的有效地址数据交换才能继续否则传输层会直接返回错误。实际操作中我们经常用ibv_reg_mr注册内存区域拿到rkey后通过带外方式传给对端之后对端才能发起RDMA Read/Write。这个rkey相当于一把远程钥匙丢了问题不小所以很多成熟的框架都会做权限隔离。不过大家在分析传输层功能时容易忽略的一点是RETH里的长度字段也有讲究。如果远程读取长度大于发起端接收缓冲区的预期可能会引发接收端缓冲区异常处理这属于软件层配合问题但硬件传输层本身会严格按照RETH长度执行DMA搬运。5.3 FetchAdd与CompareSwap远端内存上的原子操作是怎么闭环的原子操作是RC传输服务里的一项特权功能。FetchAdd指的是“读取远端内存值给它加上一个数然后把旧值返回给发起方”CompareSwap则更复杂一点先比较远端内存当前值和调用方传入的compare值相等才用swap值替换最后把旧值返回。传输层需要为这类操作设计专门的原子操作扩展头Atomic Transport Header里面会携带目标虚拟地址、R_key、比较值和交换值。为什么原子操作要放在传输层而不是简单靠CPU指令因为InfiniBand需要保证跨节点的原子性。比如多个节点同时对同一个远端内存位置做FetchAdd如果它们各自读、各自改、各自写就可能读到一个中间数值。为了把这个流程锁住接收端网卡在硬件层面会锁住对应的内存条目标记确保同一时刻只有一个原子操作在执行。下面画个简化流程发送方发出AtomicOp请求包带地址、R_key、操作数和原子操作类型接收方网卡校验R_key后对目标内存进行原子读改写读改写完成后接收方把旧值作为响应数据包发回发送方发送方收到响应把这个旧值用于应用层逻辑。因为原子操作本质是单点串行化的它对网络延迟和响应队列都非常敏感。大量使用原子操作容易让接收端网卡的处理引擎长时间被占用像某些分布式锁实现、共享计数器实现如果做得比较粗暴就会用原子操作把高性能网络变成高延迟网络。这个结论我在好几个项目里都验证过传输层的功能它都有但不代表每个功能都适合高频率使用。5.4 什么时候不该用RDMA Read和原子操作来自性能侧的忠告RDMA Read虽然提供了一种“远程主动拉数据”的范式但从性能角度它不如RDMA Write有优势。原因在于Read的完成依赖请求-响应交互一个请求要等数据从远端回来才算结束端到端延迟基本就是一个完整RTT而RDMA Write只需要发过去确认就好数据流方向单一硬件状态机也更简单。很多高性能存储框架选择write-based而不是read-based的数据通路就是吃透了这两种操作在传输层的开销差异。原子操作就更不用说了。按我个人的经验如果应用对原子操作的调用频率超过每秒几百万次最好重新设计算法把它做成批量提交或者本地聚合后再用RDMA传输。在ROCE上类似机制可能略有出入但在InfiniBand上这个规律还是比较稳定的。6. 服务类型的选型本质用可靠性和乱序约束换资源效率6.1 四种服务类型本质是不同的传输层“取舍”InfiniBand传输层提供了RC、UC、UD、RD四种服务类型很多人会背定义但仍不会选型。先给一张我平时喜欢用的对比表帮你把核心差异压缩在一起服务类型可靠性连接模式支持的主要操作典型场景RC可靠连接高有ACK/重传一对一持久连接Send/Recv、RDMA Read/Write、原子操作存储、分布式训练、数据库UC不可靠连接低无重传一对一但不维护重传状态Send/Recv、RDMA Write对丢包不敏感、重视低延迟的数据流UD不可靠数据报低无重传一发多收无连接状态Send/Recv数据报不能RDMA WriteMPI控制面、多播、管理消息RD可靠数据报高理论支持一对多或多对多类似RC但实际用得极少目前实际项目里几乎见不到把它们排在一起就能看出来可靠性不是免费的。RC的可靠要靠连接状态、重传缓冲、确认定时器来换带来的代价是本地和远端都要为每个连接维护大量上下文。UC和UD则大幅压缩了这些代价代价是上层必须容忍偶尔丢包和乱序。6.2 最容易想当然的三个选型误区第一个误区是“既然RC那么可靠为什么不全部用RC”。在节点数量小、连接数可控的场景RC当然没问题可一旦达到成百上千个QP每对QP消耗的硬件资源就不是小数目了频繁创建/销毁QP也会让网卡处理引擎不堪重负。这时你会看到驱动本身可能没报错但整体吞吐或者建连速度明显下降。第二个误区是“UD不支持RDMA Write所以大消息传输用不了UD”。这其实有点片面。UD的最大消息长度确实被MTU限制但很多MPI库会把大消息拆成多个小消息再用多路UD通道并行发送。UD的天然优势是接收方不需要预先创建很多QP一个QP可以接收来自任意发送方的数据报非常适合“多对一”模式。所以在设计集合通信时UD往往是很重要的备选方案而不是被SRQ替代的过期方案。第三个误区是“UC反正也不可靠干脆用UD算了”。UC和UD虽然都不可靠但UC支持RDMA Write这是UD没有的能力。如果你的应用核心交互是“本地主动把数据推到远端正确定址的内存”又不想承担RC的上下文开销UC在某些数据通路里其实很香。很多视频传输类低延迟应用选UC就是看中它能用较少的远端状态完成高性能写入。6.3 从verbs API看传输层的选型落地实际写代码时我们会在创建QP之前先确定服务类型然后通过ibv_modify_qp完成初始化。RC需要设置access control、超时、重试等一堆参数而UD则更关注Q_Key和P_Key匹配。这时候你会发现选型这件事不只是“拍一个协议名”而是从内存注册方式、完成事件模型到网络隔离策略的一整套决定。我给的一句话原则是追求高性能点对点、共享状态、需要强一致性时选RC海量消息、一对多通信、能容忍偶尔丢消息时选UD特定数据通路、写多读少且不想维护RC状态时考虑UC。RD这种服务类型在规范里很美好但我在真实IDC环境里几乎没有见到有人落地所以除非你有明确的团队能支持否则不要轻易投入。7. 几句实操体会送给正在跟硬件状态机较劲的你写了这么多最后想从个人实战角度留几个印象比较深的心得。第一点传输层问题几乎都是“二楼着火一楼正常”。物理链路很健康、端口也是up、插件都能看到对端可性能就是不对这时候请优先排查credit、CC参数和QP层面这些传输层状态。千万别被链路层正常的假象麻痹infiniBand的优势在于它把所有复杂机制都做进了硬件但这也意味着一旦这个硬件状态机出了问题你能看到的表象永远是简单的。第二点确认参数一定不要只在测试环境验证。local_ack_timeout、rnr_retry_count这些值测试环境里随便配都能跑但到了生产环境遇到CPU抖动、虚拟机调度、存储IO挤兑各种慢路径都会冒出来。我一般会在上线前把最坏场景跑一遍人为把CPU拉满或者把接收队列depth调小看看重传计数是否飙升再决定要不要放宽超时值。这个习惯救过我好几回。第三点永远保留基线数据。每次链路升级、固件更换、子网管理器参数调整都要把带宽、port_xmit_wait、重传计数、FECN/BECN计数记录成基线。我自己现在所有集群都会做一套例行巡检脚本定期导出这些计数哪怕当时没有任何异常。后面一旦出现问题这份基线数据能帮你省掉至少半天的盲查时间。传输层功能分析这个系列到这里第四篇也算告一段落了。如果后面有机会我可能还会再写写SRQ、多路径和子网管理器行为这些偏“调度层”的内容但那需要另起一篇了。这期就先这样希望对正在研究InfiniBand或者正被性能问题折磨的你能有一点实实在在的帮助。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑