InfiniBand 1.4规范精读:队列对、RoCEv2与RDMA绕过CPU的底层机制 简介《InfiniBand架构规范1.4版》是InfiniBand贸易协会IBTA于2020年发布的官方技术标准面向数据中心、超算与高性能网络领域的架构师、工程师、运维人员及学习者用于系统理解InfiniBand互连、RoCEv2等关键技术。包内为单个PDF文件压缩后约12.64MB。内容不仅包含第1章概念总览还详细说明HCA、CA、Switch与点对点串行连接等组成覆盖物理层与数据链路层、传输协议、服务质量、错误处理、连接管理、路由机制、队列对与完成队列等核心内容并新增虚拟化附录及RoCE v1/v2附录解释无损以太网与支持IPv4/IPv6的部署要求。这份规范已有2856人浏览学习。读者可借此完整查阅Vol.1通用规范原文快速定位InfiniBand分层架构、RoCE部署要点与术语定义也可对照历史版本理解从1.0到1.4的功能演进支撑日常开发、运维与学习中的快速查阅是深入学习InfiniBand技术及现代高速网络的重要参考。1. InfiniBand 规范 1.4为什么一张协议文档值得逐字段啃先给一个反直觉结论InfiniBand 敢说“绕过 CPU”靠的不是网卡硬件多么玄学而是把内存地址、访问密钥、队列状态全部摊在报文头里让协议机器自己在对端内存上完成“精准打击”。2020 年定稿的《InfiniBand 架构规范 1.4》InfiniBand Architecture即 IB 标准把 RoCEv2、虚拟化和 XRC 这些现代数据中心互连的拼图正式收进规范体系。很多人把这本上千页的文档当字典但真正读懂它的人能一眼看穿一次 RDMA 读为什么能落到对端某块内存、16 条虚拟通道如何化解流控死锁、为什么 RoCEv2 能跑在普通 IP 网络上。这不是泛泛的协议科普而是理解高性能互连底层设计决策的钥匙。2. 通信模型从队列对到一次 RDMA 读数据是怎么“绕过 CPU”的2.1 队列对 QP 与完成队列 CQ发送/接收/完成的三段式队列对QP是 InfiniBand 通信的绝对核心。一个 QP 并不是一条网络连接而是端点内由硬件维护的一对工作队列发送队列SQ和接收队列RQ。软件通过动词接口把工作请求WQE投递到 SQ 或 RQ硬件自行把它变成线上报文再由完成队列CQ统一回收结果。换句话说QP 是「发送/接收/完成」三段式流水线的状态机不是 socket 那种每次操作都让内核参与的数据结构。WQE 的关键字段值得记清楚Scatter/Gather EntrySGE里有本地内存地址、长度和 lkeyOpCode 指明操作类型QP 上下文则保存 PSN、PKey、访问控制状态。当驱动把 WQE 写入内存后只需要再写一次门铃寄存器HCA 就会自行取回 WQE、解析 SGE、发起 DMA。整个过程 CPU 只负责队列维护和完成检查数据本身不经过内核这是「绕过 CPU」的第一层含义。QP 状态机则是设计上容易被忽略的细节。Reset 到 Init 阶段只能修改 QP 上下文Init 之后允许发起子网管理相关报文Ready-to-ReceiveRTR表示这个 QP 可以处理对端请求Ready-to-SendRTS之后才能主动发起 SEND、RDMA 读等操作。SQDSend Draining、SQESend Queue Error和 Error 状态则负责清理路径。为什么一定要这么严格因为两个端点的状态必须一致目标 QP 没有进入 RTR 之前任何数据都不该出现在链路上。曾经有开发者把所有 QP 随手设成 RTS 就能跑通但一旦遇到异常排查就发现状态错乱带来的问题比链路丢包更隐蔽。2.2 传输服务类型可靠连接、不可靠数据报与 XRC 的取舍规范定义了五种传输服务可靠连接RC、不可靠连接UC、可靠数据报RD、不可靠数据报UD和扩展可靠连接XRC。它们的差异集中在这两个维度QP 之间的连接关系、是否做端到端重传。理解这一点才能看懂为什么高性能集群里 RC 泛滥、UD 只用来跑管理消息。四种常用服务的差别可以用一张表说清服务类型连接关系端到端可靠性典型用途RC一个 QP 只对应一个远端 QPACK/NAK超时重传RDMA 读/写、原子操作、消息传递UC一个 QP 只对应一个远端 QP无 ACK/NAK 重传低延迟但允许有限丢失的流媒体UD一个 QP 可向多个 QP 发送无端到端重传管理报文、事件通知、通用服务XRC一个接收 QP 可被多个远端 QP 共享按连接做可靠重传大规模 MPI 集群减少 QP 数量RC 最好理解建连时双方交换初始 PSN之后每个数据包都带 PSN接收端用 ACK/NAK 反馈发送端对未确认包超时重传。它的代价是一个 QP 只能对应一个远端 QP。N 个进程做全两两通信就需要 N 的平方个 QP。XRC 就是冲着这个来的把接收端的 QP 做成共享资源多个发送端 QP 通过 XRC 的扩展头携带目标进程的队列上下文让每台机器只需要极少数 QP把复杂度从 N² 压到 N。XRC 在 1.3 版本中被整合进正文1.4 延续了这套共享接收队列的模型并在此后的集群方案里被广泛采用。UD 走了另一个方向不需要建连每个包的 DETH 头直接写远端 QP 号包长被限制在 MTU 内也没有端到端确认。这恰好适合子网管理、通用服务这类短报文。许多人问为什么不能全部用 UD答案也很直接UD 没有可靠性重传也没有 RDMA 读写的 RKey 校验机制真正的高性能传输服务靠它撑不住。2.3 一次 RDMA 读的协议时序请求、ACK 与重传把一次 RDMA 读拆开看才能理解「绕过 CPU」的完整含义。假设节点 A 要从节点 B 的内存 0x10000 处读 64KB。A 在本地 SGE 里指定一块接收缓冲区然后提交一条 RDMA_READ WQE。硬件 A 构造 Read Request 报文BTH 的 OpCode 置为 RDMA_READPSN 为当前发送序号RETH 里写入 B 的远程虚拟地址、RKey 和长度。报文到达 B 后B 的 HCA 依次校验目的 QP 处于 RTS 状态、报文的 PKey 与 QP 的 PKey 匹配、RKey 对应一块注册过的内存区域。全部通过后B 的 HCA 直接 DMA 读取自身内存封装成 Read Response 报文发回 A这个响应同时承担 ACK 的功能。A 收到后把 payload 写入本地 SGE 缓冲区写 CQE随后软件才被通知。整条路径中 B 的 CPU 完全无感这才是 RDMA 读与普通远程读取的本质区别。可靠性由 PSN 机制保证。RC 连接使用的 PSN 是一个 24 位循环计数器发送端每发包 PSN 加一接收端维护期望 PSN发现空洞就反馈 NAK。规范要求发送端必须保存未确认包收到 ACK 之后才能释放缓冲超时则按原始顺序重传。这里有个容易误读的点接收端遇到乱序包会选择丢弃而不是尝试重排。原因很实际——RDMA 要求顺序交付硬件顺序重排的开销比丢弃大得多靠发送端重传补齐才是低成本方案。提示调试重传问题时先确认两端 QP 的初始 PSN 是否一致。很多看似网络丢包的问题其实是建连阶段 PSN 协商没完成。3. 寻址与报文格式LRH、GRH、GID/LID 到底在解决什么问题3.1 LID 与 GID子网内与跨子网的两套寻址InfiniBand 的寻址体系和 TCP/IP 完全不同。规范同时定义了两套地址LIDLocal Identifier和 GIDGlobal Identifier。LID 是 16 位由子网管理器在子网初始化时动态分配交换机转发包时只查 LIDGID 是 128 位采用 IPv6 的格式高 64 位是子网前缀低 64 位通常是端口 GUID。交换机和路由器分别消费这两类地址信息。为什么需要双层地址最直接的原因是转发效率。处理 16 位 LID 的线性转发表可以做得很大且查找很快一次查表就能确定出端口如果每个 hop 都拿 128 位 GID 做路由查找成本会高得多。LID 是子网内的高速路牌GID 是跨子网的全球定位。第 4 章明确要求路由器除了 LID 路由必须在 GRH 里读 DGID 做决策。这里有个关键细节LID 不是端口硬件自带的物理地址而是子网管理器分配的临时身份。每个节点的 HCA 端口至少有一个 LID交换机的每个端口也有一个子网重配置后 LID 可能改变。初学者在 SMI 查询结果里看到 LID就把它当成 MAC 地址用跨子网时翻车发现 DLID 和 SLID 完全不参与 L3 路由。正确的心智模型是LID 是子网拓扑内的局部标签GID 才是地址的稳定身份。3.2 数据包格式LRH 到 BTH 字段为什么存在数据包是协议机制落地的实体。1.4 规范里的数据包格式非常克制基础包由 LRH、可选 GRH、BTH 和扩展头组成每个头都有自己的设计动机值得逐个看。头长度关键字段设计动机LRH8 字节VL、SL、LVer、LNH、DLID、SLID子网内一跳转发所需的全部信息GRH40 字节SGID、DGID、HopLimit、TrafficClass跨子网的全局路由BTH12 字节OpCode、PKey、DestQP、PSN传输层可靠性和寻址RETH16 字节VA、RKey、长度远程内存读写的 DMA 描述AETH4 字节ACK/NAK、MSN、SYN确认与流量窗口控制LRH 里的 VL 字段直接对应物理链路上的虚拟通道流控按 VL 排队SL 是上层标记的服务等级交换机要依据 SL 做映射。DLID 是转发的核心依据SLID 用于源端口识别。这些字段统一用网络字节序规范专门写了 byte ordering。字节序错位是极其隐蔽的调试陷阱两端解析出来的 VL、SL 全反报文长度越界却不知道根因。BTH 是所有传输服务必须的头。OpCode 区分 SEND、RDMA_WRITE、RDMA_READ、ATOMIC、ACK 等PKey 是 16 位分区键DestQP 告诉 HCA 这个包交给哪个 QPPSN 支撑排序和重传。注意一点驱动构造 WQE 时不会手写 BTH它由 HCA 根据 QP 上下文自动生成。所以调试时抓到 PSN 对不上问题多半在 QP 状态机初始化的早期阶段而不是链路丢包。方向错了花几天也找不出原因。3.3 GRH 与 RoCEv2以太网上的 InfiniBand1.4 规范的两个重要附录RoCEv1 和 RoCEv2把 IB 的传输层搬到了以太网上。RoCEv1 的做法最直接保留 GRH、BTH 及以上的全部 IB 报文去掉 LRH把它们直接当作以太网 payload以太网类型字段设为 0x8915。它的前提是以太网必须无损要用 PFC 优先级流控模拟 IB 链路层的 credit 机制否则拥塞丢包会让上层可靠性迅速崩溃。RoCEv2 更彻底在 BTH 前面套一个 UDP 头和一个 IPv4/IPv6 头UDP 目的端口固定 4791。UDP 在这里的身份不是可靠传输协议而是一个轻量多路复用器目的端口标记这是 RoCEv2 流量源端口由软件设置可以被交换机拿去做 ECMP 的哈希因子。为什么不选 TCP因为 TCP 的连接状态和重传会与 IB 传输层自己的重传机制打架而且 TCP 的拥塞控制会给 RDMA 引入不可控的延迟。UDP 虽然不可靠但 IP 层负责路由payload 完整交给 IB 传输层处理。对比 native IB 和 RoCEv2 的转发路径能看清设计native IB 里交换机看 LRH 的 DLID 转发RoCEv2 里普通以太网交换机看 IP 头路由支持动态负载均衡的交换机看 UDP 五元组做逐流分流。上层 QP、PKey、RKey、PSN 机制完全不变。这正是 1.4 把 RoCEv2 写成附录而不是另立新协议的原因它复用的是 IB 传输语义只是换了一层物理转发底座。4. 服务质量与隔离机制虚拟通道、服务等级、分区键如何保证“不互相干扰”4.1 虚拟通道 VL 与流控InfiniBand 物理链路上并不是一条单车道而是被切分成多条虚拟通道VL。规范允许最多 16 条 VL编号 0 到 15其中 VL15 保留给子网管理其余用于数据。每个 VL 在链路接收端都有一组独立缓冲区和 credit 计数器。发送端向某个 VL 发包时要先确认该 VL 的 credit 充足否则必须等待。credit 流控是 InfiniBand 低延迟的基石。以太网依赖丢包后重传IB 则试图通过链路层信用机制从物理上避免中间队列耗尽导致的丢包接收端把自己可用缓冲的大小换算成 credit发送端严格按照 credit 发包。每个 VL 独立结算意味着高优先级的 VL 不会被低优先级流量占满缓冲。这就是「虚拟通道隔离」的底层含义隔离的不是带宽而是缓冲和流控状态。为什么需要 16 条而不是一条经典动机是避免死锁和头阻塞。设想两条方向交叉的流量共用一条缓冲队列一旦缓冲区占满且双方都在等对方释放 credit就会形成死锁。多个 VL 提供多份独立缓冲池让不同语义的流量走不同的通道把死锁概率压到极低。这个设计在 1.4 里没有改动因为它是链路层稳定性的基座。4.2 SL 到 VL 映射与注入速率限制SLService Level和 VL 是常被混淆的一对概念。SL 是端到端的 QoS 标签放在 LRH 里报文沿路径不变VL 是每一跳链路局部的转发通道在交换机上可以重新映射。规范规定每个交换机的每个输入端口都有一张 SL-to-VL 映射表决定某个 SL 的包进入哪个 VL 队列。SL 本身不直接决定优先级真正决定调度优先级的是 VL 在端口仲裁器中的配置。这种两层 QoS 映射的目的是把业务等级和物理通道解耦。上层看到的 SL 是稳定的业务属性控制流量 SL 0存储流量 SL 1底层网络管理员可以针对每条链路设置不同映射甚至把一个 SL 映射到多条 VL 做负载分担。排障时也要用到这个视角两个 SL 映射到同一个 VL 时它们共享流控和调度域会互相抢占映射到不同 VL才能获得真正意义上的隔离。很多 QoS 调优无效根因就是把 SL 和 VL 当成一回事。注入速率限制Injection Rate Limiting则从源头治理拥塞。规范允许端点按配置的速率平滑发包避免多个节点同时向同一热点猛灌数据。实现上硬件维护令牌桶以字节或包为单位补充 token。它和流控是互补关系流控是链路级的接收端反馈速率限制是发送端自主限速。只配流控不配限速热点交换机仍可能被瞬时突发压塌。4.3 分区与保护域密钥机制如何做访问控制InfiniBand 的隔离不只有 QoS还有访问控制分区键PKey和保护域PD。PKey 是 16 位整数每个 QP 关联一个 PKey多播组也关联 PKey。当报文到达对端 HCA 时接收端校验报文 PKey 与 QP 上下文的 PKey 是否匹配。不匹配的包会被直接丢弃甚至不回错误确认——这是刻意为之的「安静失败」避免信息泄露。分区机制类似 VLAN 隔离但更细致。规范规定编号 0xFFFF 代表默认全分区任何 QP 都可以属于它。很多 API 实现里创建 QP 时若不显式指定 PKey就会默认落到 0xFFFF。在 RoCEv2 环境中这会导致一个隐蔽的安全隐患只要目标网卡有可用的 QP同一二层域里的其他节点可能把报文送进来。1.4 的虚拟化附录进一步把 PKey 用作虚拟隔离网络的标识相当于每个租户分配独立广播域。PD 则是 verbs 层的内存保护对象。注册内存时得到一个 lkey/rkey它们内部关联 PD创建 QP 或 Memory Window 时也关联 PD。当 RDMA 请求中的 RKey 指向某块内存HCA 会同时校验 RKey 和 PD 一致性。这个机制的动机很直接不允许另一个进程的 QP 访问受保护进程的内存。只盯 PKey、忽略 PD 的人踩到跨进程 RKey 访问被拒时常常发现日志里什么也没有其实失败点在 PD 校验。5. 常见问题与避坑读规范时最容易理解偏的几个点读规范最容易走偏的不是看不完而是把某一层的语义套到另一层。以下五条是我在真实环境里反复踩过的坑每一条都符合“现象-原因-解决”三段式按这个顺序排查能省下不少血泪时间。5.1 现象把 LID 当成全局地址跨子网一试就懵现象在子网 A 里通过 sa query 拿到某个 HCA 端口的 LID 是 0x0002于是把它写进对端连接的地址属性期望跨过路由器直接互通。结果报文在子网 A 的边界路由器上被正常转发到了子网 B 却无人接收。原因LID 是子网管理器为每个端口临时分配的局部标签只在分配它的那个子网内有意义。子网 B 的端口可能也有 LID 0x0002但它属于另一个管理域路由器根据 GRH 的 DGID 做跨子网决策DLID 只对子网内一跳有效。很多人把 LID 类比为 MAC 地址这个类比在单子网里勉强成立跨子网立刻失效。解决设计多子网集群时把跨子网通信的端点信息统一收敛到 GID 上。GID 的高 64 位是子网前缀天然标识了归属低 64 位是端口 GUID不会因为子网重配置而变化。至于 LID留给子网内的线性转发表和故障排查。抓包看到 DLID 变化时先问自己这个包当时处于第几跳而不是直接怀疑交换机表项坏掉。5.2 现象以为不可靠连接完全不重传出错了就是玄学现象用不可靠连接 UC 跑 RDMA 写线上偶尔出现数据不一致应用层重试一次又好了于是把它当成某种玄学。检查物理链路又是正常的误以为链路偶发误码。原因UC 排除了端到端 ACK/NAK 与超时重传只保留连接关系和分片语义。链路层虽然能做 CRC 校验但校验失败后能做的只是丢弃坏包没有上层重传丢失的 payload 就彻底没了。高层的重试之所以有效是因为应用重新提交了 WQE而不是网络自己恢复了数据。相比之下 RC 的发送端保存未确认包并超时重传对上层表现为「几乎没有偶发错误」。解决选传输服务前先回答这条数据流能否容忍端到端丢包存储、分布式事务这类对一致性强敏感的业务直接上 RC 或 XRCUC 只适合流媒体、监控画面这类丢一个帧可以接受的场景。另外即使是 UC也不代表错误码完全不可见HCA 会记录本地错误和远端丢弃计数排查时先看这些计数器再下结论。5.3 现象RoCEv1 和 RoCEv2 混为一谈现象项目规划时写「网络支持 RoCE」结果上线时发现交换机不路由 RoCEv1 帧流量一出接入层就消失。原因RoCEv1 帧没有 IP 头以太网类型 0x8915 只能被二层交换机处理普通三层交换机不认识这个类型不能路由。而且 RoCEv1 依赖无损以太网需要 PFC 在所有链路上一致配置否则拥塞丢包直接触发上层超时。RoCEv2 把 IB 报文封装进 UDP/IPv4/IPv6目的端口 4791才具备跨子网能力和 ECMP 路径选择能力。解决在任何「RoCE 支持矩阵」里都明确标注 v1 还是 v2。抓包时看以太网类型0x8915 是 v10x0800 后面的 UDP 负载就是 v2。规划网络时优先选 v2因为它可以复用现有 IP 路由和负载均衡但要注意 RoCEv2 仍建议配合 PFC 或 DCQCN 这类拥塞控制否则在满负载交换机上一样会丢包。5.4 现象把 SL 当成优先级忽略 SL 到 VL 映射现象给高优先级业务打上 SL 1低优先级打上 SL 0预期高优先级带宽更高、延迟更低。实际测试看两个流几乎平分端口带宽排队延迟也没有差别。原因SL 只是 LRH 里的一个 4 位标签交换机不会凭 SL 直接调度。它必须先去查本端口的 SL-to-VL 映射表得到一个 VL 编号再进入该 VL 的缓冲队列队列的调度权重由 VL 的仲裁配置决定。如果两个 SL 映射到同一个 VL它们的优先级差异在交换机内部根本不存在。解决配置 QoS 时至少要完成三步建立 SL-to-VL 映射表为不同 VL 分配独立的缓冲和调度权重再把应用流量正确标记到 SL。排查时先看端口计数器区分不同 SL 实际落入的 VL若发现落入同一 VL再回头查映射表。很多 SL 优先级的失效问题最后都落在这张表上。5.5 现象忽略分区键和保护域导致队列对访问控制失效现象两个服务在同一台机器上各自使用不同进程A 进程注册内存后把 rkey 和地址交给 B 进程的 QP 使用B 端 RDMA 写一直得到访问错误日志里还没有任何说明。原因PKey 和 PD 双重校验没通过。QP 上配置的 PKey 与目标分区的 PKey 不一致HCA 会静默丢弃如果 PKey 一致但 rkey 对应的内存区域所属 PD 与 QP 所属 PD 不同HCA 同样拒绝访问。RoCEv2 环境下默认 PKey 0xFFFF 掩盖了很多问题让两者看似都通一旦有人改成其他值队列对访问立刻失效。解决把 PKey 和 PD 当作权限上下文来管理创建 QP 时显式指定 PKey不依赖默认值注册内存、创建 QP、创建 Memory Window 时统一用同一个 PD。排查时先读 HCA 的端口错误计数和 QP 的错误状态寄存器不对就从 PKey 到 PD 逐层核对。这类问题不会产生传统的网络错误包只能靠计数器和上下文来定位。6. 设计取舍与现代延伸为什么 1.4 是理解数据中心互连的“分水岭”1.4 规范最值得玩味的设计决策是它没有推倒重来而是选择了向后兼容的增量演进。从 1.0 到 1.3规范在 SA 与 CM、XRC、子网管理等方向上持续叠加到 1.4RoCEv1/RoCEv2 被正式纳入附录。这种做法的代价是报文头里保留了相当多的保留字段和兼容位实现者必须处理大量「合法但很少用」的分支收益则是十多年的硬件实现可以平滑升级数据中心不需要一夜之间更换全部网卡。现代延伸里最典型的是 RoCEv2 在高性能计算和存储网络中的地位它把 IB 的传输语义和 IP 网络的运维基础设施合在一起让 ECMP、传统交换机、监控体系都能复用。另一个值得关注的方向是规范里的虚拟化附录它把 PKey、PD、虚拟队列这些抽象提升为多租户隔离的正式手段说明 InfiniBand 已经不只是超算内部的高速总线而是云基础设施的底层互连选项。我建议手头常备这份 1.4 规范的 PDF精读顺序是第 3 章架构、第 5 章数据包格式、链路层相关章节再回来看 RoCE 附录。验证我是否读懂一份协议方法很固定先在纸上画出 QP 状态机和一次 RDMA 读的时序再上真实环境抓包按 LRH、BTH、RETH 逐字段和规范对照一遍。曾经有一次我发现抓包里的 P_Key 与配置不一致翻遍代码才发现是默认值在作怪。从那以后我每次处理互连问题都强制走一遍「状态机 → 报文 → 配置」的检查顺序很多自认玄学的问题最后都落在一两个字段的语义错位上。希望帮到你。本文还有配套的精品资源点击获取