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)里,发送方发出的每一个数据包都会带一个包序号(PSN,Packet Sequence Number),接收方通过PSN来判断这个包是不是乱序、是不是重复、有没有缺漏。很多人以为InfiniBand是“每发一个包就等一个ACK,收到再见缝插针发下一个”,如果真是这样,RDMA的性能根本没法看,因为一个RTT内只能有一个包在飞。实际机制是,发送方可以连续发出大量数据包,接收方不需要回应每一个包,而是会返回一个累积确认(Acknowledgment),表示“我这边连续收到的包已经到哪个序了”。
这里要提一个容易混淆的点:PSN负责的是“包”的次序,而确认报文里的MSN(Message Sequence Number)负责的是“消息”级别的进度。传输层硬件维护这两套账本,目的就是为了一边支持乱序接收,一边又能快速判断哪些数据可以安全地从重传缓存里释放。简单打个比方,PSN像快递运单号,每件快递都有;MSN像订单号,一批货里的某一件到了,不代表整个订单完成,只有订单里所有运单都签收,才算整单结束。
2.2 NAK与RNR:两类“不正常的正常回应”
除了正常ACK,RC连接还有一种常见回应叫NAK(Negative Acknowledgment),表示接收方收到了包,但校验或者序号上有问题。比如接收方收到一个PSN跳跃的包,发现中间有缺失,就会返回带具体PSN的NAK,发送方看到后会从那个包开始重传。这里很多人会问:为什么不直接依赖超时重传就好了?答案是性能。等超时要浪费一个RTT,而NAK能在一个RTT内把丢包信息反馈回去,缩短故障恢复时间。
还有一种特殊回应叫RNR(Receiver Not Ready),它跟丢包无关,跟接收端的缓冲区状态有关。接收方明明物理上还在正常工作,但对应的接收队列里没有预投递WR(Work 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)是一套闭环系统:接收端在检测到拥塞风险时,会在正常返回的包上做一个特殊标记,叫FECN(Forward Explicit Congestion Notification);收到带FECN标记的节点,会向这个连接真正的发送方返回一个带BECN(Backward 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里的关键字段:一块远程内存的“通行证”
RETH(RDMA 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 Write) | MPI控制面、多播、管理消息 |
| 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或者正被性能问题折磨的你,能有一点实实在在的帮助。