这几年在数据中心和超算圈子折腾网络,RDMA 这个词基本绕不开。做分布式存储、高性能数据库、AI 训练集群的同学,应该都听过它,但很多人对它的认知停留在“这是个很快的网络技术”这个层面。至于它到底解决了什么问题、底层是怎么把数据传过去的、到手之后怎么验证链路是不是通的,网上资料虽然多,但系统性讲清楚的不多。
我这些年从 InfiniBand 到 RoCEv2 都摸过,踩了不少坑,这篇就用自己的话把 RDMA 从背景、实现原理到连通性测试完整捋一遍。不管你是刚接触分布式高性能网络的新手,还是已经在用 RDMA 但没仔细抠过原理的开发者,这篇应该都能给你一些参考。
1. RDMA 出现背景:传统网络协议栈撑不住高性能场景了
1.1 传统 TCP/IP 路径的问题到底出在哪
要理解 RDMA 为什么会出现,先得看看传统网络发送一包数据时 CPU 在干什么。
假设一个应用要往网络对面发一段数据,走的是传统 socket + TCP/IP 路径。数据从应用的用户态缓冲区,先要通过send()系统调用进入内核,内核把这份数据从用户态内存拷贝到内核态 socket 缓冲区。然后 TCP 协议栈开始干活:分段、计算校验和、加上 TCP 头和 IP 头。接着数据被送到网卡驱动,驱动把它拷贝到网卡 DMA 缓冲区,网卡才会把数据发出去。
到对端之后,同样是一连串动作的逆过程:网卡收到数据,触发中断,CPU 从网卡缓冲区把数据拷贝到内核协议栈缓冲区,TCP 层做校验、重组,然后拷贝到 socket 接收缓冲区,最后应用调用recv()再从内核拷贝到用户态缓冲区。
看到问题了吗?一次简单的收发,数据至少被搬了四次,而且每一次拷贝都占用 CPU 和内存带宽。更麻烦的是,每个包到达时网卡都会触发中断,CPU 要暂停手头的活儿去处理收包。虽然有了中断合并、NAPI 这些优化手段,但在高吞吐场景下,CPU 大量时间都花在“搬数据、处理中断”上,真正留给应用计算的时间就少了。很多千兆网络的服务器,传输大文件时 CPU 占用率轻松到 80% 以上,就是被协议栈吃掉的。
另一个问题是延迟。TCP 的可靠传输依赖 ACK,一个包的延迟由内核协议栈处理时间、拷贝次数、中断调度等叠加。哪怕是同机架内两台服务器,传统 TCP 的往返延迟也基本在几十微秒这个量级。这个延迟在普通 Web 服务下没人在意,但对高频交易、分布式数据库同步、机器学习参数交换这种场景,就是致命的瓶颈。
1.2 高性能场景对网络提出了什么新要求
看一下 RDMA 主要解决的应用场景,就能明白它的设计目标从哪来。
第一类是高性能计算。MPI 并行计算中,上千个计算节点每算一步就要同步一次数据。比如天气预报模拟、流体力学计算,通信时间往往占了整体运行时间的 30% 以上。这种场景要求网络不仅带宽高,延迟还必须低,因为每多一微秒延迟,整个集群就要多等一微秒。
第二类是分布式存储。分布式存储系统要把一份数据同时写到多个副本节点,客户端的每个写请求都要等所有副本落盘确认。这里的网络开销直接叠加在每条 IO 路径上,网络延迟高一点,整个存储系统的 IOPS 就断崖式下跌。传统 TCP 在 4K 小 IO 这种场景下,CPU 成本和延迟都太高了。
第三类是数据库和缓存。像 Oracle RAC、Redis 集群这类系统,节点之间需要频繁交换日志和状态信息。这个场景的流量特征是“小消息、超高频率”,单条消息可能只有几百字节,但每秒要发几百万次。TCP 在这种场景下不仅 CPU 撑不住,每包几十微秒的延迟也扛不住。
还有一个容易被忽略的问题:内存带宽浪费。大数据量的传输如果经过多次拷贝,数据还没到网卡,先占用了大量内存总线带宽。当网络吞吐高的时候,应用本身访问内存都会被拖慢。RDMA 的“零拷贝”机制直接绕开这个问题——数据从应用内存直接到网卡,不需要中间介质。
所以 RDMA 的设计目标非常清晰:把数据搬运这件事从 CPU 手里拿过来,交给网卡硬件完成;把内核从数据通路上拿掉,让应用能直接和网卡对话。这就是“Remote Direct Memory Access”名称的来源——远程直接内存访问,本地应用直接读写远端机器的内存。
2. RDMA 怎么实现:三大关键机制与三种落地路线
2.1 内核旁路、零拷贝、直接数据放置,我没说玄乎话
RDMA 的核心实现靠三个机制,名字听起来高大上,掰开看其实不复杂。
内核旁路(Kernel Bypass)的意思是,应用的数据不经过操作系统内核协议栈,而是直接通过用户态驱动和网卡硬件打交道。具体实现是网卡厂商提供用户态的库(比如 Mellanox 的 libibverbs),应用调用这个库的接口,直接往网卡硬件里下发指令,内核只在初始化阶段参与一下,之后的收发路径完全绕开内核。类比一下,传统网络相当于你寄快递要先去邮局排队填单、分拣、装车,RDMA 相当于快递员直接在你家门口接货,中间环节全部跳过。
零拷贝(Zero-Copy)解决的是数据搬运次数问题。RDMA 通信前,应用要把自己的一块内存注册到网卡上,这个操作叫内存注册(Memory Registration)。注册之后,网卡硬件就“认识”这块内存了,可以直接通过 DMA 从这块内存读写数据,CPU 完全不需要参与数据搬移。发送数据时,网卡直接从应用内存把数据读走发到网络;接收数据时,网卡直接把网络上的数据写入应用预先准备好的内存区域,中间不经过内核缓冲区、不经过 socket 缓冲区。
直接数据放置(Direct Data Placement)是配合零拷贝的关键。接收端应用需要在通信前把一块“接收缓冲区”注册好,并把这个缓冲区的地址信息告诉对端。数据到达时,网卡硬件根据报文里携带的内存地址信息,直接把数据放到指定的应用内存位置。不需要内核帮忙找缓冲区,不需要拷贝。
这三个机制叠加起来的效果就是:数据从一台机器的应用内存,通过网卡硬件直接传到另一台机器的应用内存,中间 CPU 不碰数据,内核不碰数据。这也是为什么 RDMA 能把单端延迟压到微秒级——不是网络带宽本身变了,是绕过了所有软件开销。
2.2 QP 是什么:RDMA 通信的最小单元,搞懂它才算入门
很多人看 RDMA 资料时,会频繁遇到QP这个词。热处理里的rdma qp是什么被搜索很多次,说明这是新手最容易卡住的点。
QP 的全称是 Queue Pair,中文叫队列对。它是 RDMA 通信的最小单元,是通信两端建立的一个逻辑连接。一个 QP 由两个队列组成:发送队列(Send Queue,缩写 SQ)和接收队列(Receive Queue,缩写 RQ)。应用要发数据,就往 SQ 里放一个 Work Request(工作请求,WR);要接收数据,就往 RQ 里放一个 WR,网卡硬件会自己处理这些请求,处理完成后把结果写到完成队列(Completion Queue,CQ)里,应用通过轮询 CQ 来知道收发成功。
QP 和 TCP 连接有点像,一台机器上的同一个 QP 只能连接对端的一个 QP,是一对一的关系。但和 TCP 不同的地方是,创建 QP 之前必须先明确它的类型。QP 有三种类型:
可靠连接(Reliable Connection,RC):类似 TCP,保证可靠交付、保序,网卡硬件负责确认和重传。消息长度几乎没限制,可以传大块数据。绝大多数场景都用 RC,尤其是存储、数据库这类要求数据不能丢的场景。
不可靠连接(Unreliable Connection,UC):不做确认和重传,但保持报文顺序。因为去掉了 ACK 机制,建链成本低,理论延迟更低,但丢包要应用自己兜底。
不可靠数据报(Unreliable Datagram,UD):类似 UDP,不支持多包消息,单消息长度受限制(一般不能超过 MTU),也不需要预先建立一对一连接,一个 UD QP 可以和任意多个对端 UD QP 通信。适合广播、多播这类应用,但要求数据包不能大、可以容忍丢包。
QP 还有一个状态机:RESET(复位)→ INIT(初始化)→ RTR(Ready to Receive,可以收)→ RTS(Ready to Send,可以发)。通信之前,两端的 QP 要先在硬件层面完成状态流转。这个流转过程在生产环境中经常出问题,我在后面测试章节会展开说。
理解 QP 的关键是:RDMA 通信本质上是“应用把内存地址告诉网卡,网卡把数据送到对端应用的内存地址”,QP 就是承载这个地址信息和传输状态的载体。所以调测连通性时,如果你在两个节点上能看到 QP 状态正常,基本就等于 RDMA 链路真正打通了。
2.3 InfiniBand / RoCE / iWARP 三条落地路线的选型对比
RDMA 是一个抽象的协议思想,落到物理实现上有三条路线。很多新手以为 RDMA 就是 InfiniBand,其实不是。
InfiniBand(IB)是 RDMA 的“原生血统”。它从物理层到传输层都是专门为 RDMA 设计的,有专用的交换机、专用的线缆、专用的网卡(HCA 卡)。优点是最稳定、性能最好、延迟最低,当年世界上最快的超算基本都是 IB 网络。缺点是贵——一套 IB 交换机的价格是同规格以太网交换机的数倍,而且 IB 网络和传统以太网不兼容,部署和管理需要专门的知识。现在一般只有对性能极致敏感、预算充足的高性能计算中心才会全套上 IB。
RoCE(RDMA over Converged Ethernet)是把 RDMA 报文封装在以太网帧里,跑在普通以太网交换机上。RoCE 有两个版本:RoCEv1 直接封装在以太网二层,无法跨网段路由;RoCEv2 用 UDP 封装,可以走 IP 三层路由,是目前数据中心大多数新部署的首选方案。它最大的价值是成本和兼容性——可以用现有的以太网设备和网管体系,交换机和线缆都是成熟量产的,价格比 IB 便宜很多。代价是:普通以太网是“尽力而为”的丢包网络,而 RDMA 对丢包极其敏感,所以 RoCE 网络需要额外做无损保障,这个我在后面坑的部分详细讲。
iWARP(Internet Wide Area RDMA Protocol)是把 RDMA 封装在 TCP 之上。理论上可以利用现有以太网设施,不需要做无损改造。但实际效果大打折扣——因为 TCP 协议栈的处理开销依然存在,CPU 释放得不彻底,性能相比前两者有明显差距。目前在主流数据中心里 iWARP 的话语权比较低,Intel 自家网卡还在推,但 Mellanox 生态基本主导了整个市场。
选型怎么选?简单粗暴的标准:预算充足、追求极致性能就上 InfiniBand;要在现有以太网基础上平滑升级、性价比优先就上 RoCEv2;对性能要求不极致、还要兼容老网络设施就考虑 iWARP。从目前行业趋势看,RoCEv2 是增长最猛的,AI 大模型训练集群里大量使用。
3. 动手搭一套 RDMA 环境:硬件、驱动和基础配置
3.1 硬件和软件栈怎么准备
测试 RDMA 最基本的条件是要有两台机器,每台至少一块支持 RDMA 的网卡。市场上主流的 RDMA 网卡基本都来自 NVIDIA(收购了 Mellanox)的 ConnectX 系列,比如 ConnectX-4/5/6/7,速率从 25GbE 到 400GbE 都有。Intel 也有支持 iWARP 的网卡,但在 RoCE 生态里兼容性不如 Mellanox。另外博通等厂商也有对应方案,但驱动和工具链成熟度上 Mellanox 依然是首选。个人测试如果暂时没有硬件,也可以用 Soft-RoCE 方案,用普通网卡在软件层模拟 RDMA,性能当然不能和硬件比,但用来学习 QP、工具链和连通性验证足够了。
驱动方面,Mellanox 网卡通常需要安装两个东西:一个是内核驱动模块,比如mlx5_core,用来驱动 ConnectX-4 及以后的卡;另一个是用户态驱动库,即libibverbs和librdmacm,这是 RDMA 编程和测试工具的关键依赖。前者通过 OFED(OpenFabrics Enterprise Distribution)安装,后者在 Debian/Ubuntu 上直接:
apt install libibverbs-dev librdmacm-dev ibutils ibverbs-utils perftestRedHat/CentOS 系列对应的是:
yum install libibverbs-devel librdmacm-devel perftest装完之后,第一步是确认内核已经识别到 RDMA 设备。用ibv_devinfo或者ibstatus查看:
ibv_devinfo正常的输出里会列出设备名(比如mlx5_0)、端口数、端口状态、支持的链接层模式(InfiniBand或Ethernet)、链路速率等信息。如果这个命令报找不到设备,大概率是驱动没装好,或者固件太老不匹配。lspci | grep Mellanox先看 PCIe 层面是否识别,再看/sys/class/infiniband/目录下有没有对应的设备目录。有个很常见的坑:内核自带的驱动版本太旧,导致新网卡初始化失败,这时候要更新 OFED 驱动,而不是手动改内核参数。
3.2 配置 RoCEv2 网络的两个关键动作
以最常见的 RoCEv2 为例,两块 ConnectX-5 网卡直连或者接同一台交换机,在 OS 层面其实不用做太多配置——RDMA 网络和普通以太网共用同一个物理端口,IP 地址也是用ip命令正常配置:
ip addr add 192.168.100.1/24 dev ens3f0 ip link set ens3f0 up对端配192.168.100.2/24。配完之后先试一下普通网络ping通不通,这是最基础的物理连通性验证。
但要注意,RoCE 通信不只是靠 IP,还依赖一个叫GID(Global ID)的东西,它是 RDMA 层面的地址标识。把 IP 配到网卡上之后,网卡驱动会自动在 GID 索引表里生成一条记录。用ibv_devinfo可以看到每个端口支持的 GID 数量,用下面的命令可以查看 GID 类型:
cat /sys/class/infiniband/mlx5_0/ports/1/gid_attrs/types/0如果网卡端口跑的是 RoCEv2,这条记录的类型应该显示为RoCEv2。如果显示IB/RoCE v1,说明是 RoCEv1 模式或者配置不对。RoCEv2 和 RoCEv1 之间还有个兼容性区别:RoCEv2 使用 UDP 封装,端口号固定为 4791;RoCEv1 直接嵌在以太网头后面,没有 UDP 层。跨网段通信必须用 RoCEv2。
还有一个在多个网卡场景下容易犯的错:机器上有两张 RoCE 网卡时,默认的 GID index 可能绑定到错误的网卡上。测试时如果发现状态正常但数据不通,可以检查一下 GID index 和实际用到的 IP 是否在同一张卡上。
3.3 环境自检:链路速率和 MTU 别忽视
配置完成后,别急着跑测试,先做一轮环境自检。链路速率是最常见的问题来源:两个网卡协商出的速率如果不匹配,或者交换机端口被强制设置成低速率,性能测试结果会非常难看。用ethtool查看:
ethtool ens3f0重点看Speed字段,比如Speed: 100000Mb/s表示 100GbE 链路。如果协商速率远低于预期,检查线缆、光模块、交换机端口配置,这些硬件的故障概率远大于软件。
另一个是 MTU。RoCE 报文虽然有 UDP 封装,但底层还是依赖以太网帧。如果 MTU 设置不一致,数据包在链路中间被分片或者丢弃,会导致连接建立失败或者性能异常。建议两端和交换机全部统一使用mtu 9000(巨型帧),可以有效降低包头开销和报文数量。配置方式:
ip link set ens3f0 mtu 9000如果交换机上行口没开巨型帧,链路层就会出问题,表现是ping大包不通但小包通,或者 RDMA 测试时大量超时。这里的经验是:在小包ping通之后,务必用ping -s 8972试一次大包(8972 是 9000 MTU 下能承载的最大 ICMP 载荷),能通才能说明整条链路支持巨型帧。我之前就有一次因为交换机端口 MTU 忘了改,RDMA 写带宽死活上不去,教训很深刻。
4. 连通性测试怎么做:从 ping 到带宽延迟,层层递进
4.1 第一层:设备与物理链路可达性测试
RDMA 连通性测试不是一步到位,我习惯分三层来做:设备层、连接层、性能层。每一层都有对应的工具和判定标准。
设备层验证的是网卡、驱动、物理链路是否就绪。除了上面说的ibv_devinfo,还可以用ibping做一次“RDMA 层面的 ping”。ibping需要先在一台机器上启动服务端:
ibping -S -C mlx5_0 -P 1-S表示服务端模式,-C mlx5_0指定设备名,-P 1指定端口号 1。启动后它会监听 RDMA 端口的 ping 请求。
然后在另一端启动客户端:
ibping -c 100 -C mlx5_0 -P 1 -L 1这里-c 100表示发 100 个请求,-L 1是本地端口号。输出会给出响应延迟的统计信息。ibping 如果通了,说明 RDMA 设备和物理链路层面没有问题。如果有问题,比如两端设备名写错、端口号不对,会直接卡在握手阶段。
这里顺便说一下,很多人第一次跑ibping会找不到服务端,原因不在服务端,而是客户端的-L参数没写对。-L指定的是客户端自身的 RDMA 端口,不是服务端的端口。搞反了怎么都连不上。还有一种情况是机器上有多张 RDMA 网卡,ibping默认走 index 0 的设备,但实际接线的可能是 index 1 的设备,这时候要明确用-C指定设备。
4.2 第二层:验证 RC 连接与 QP 状态流转
设备层通了,不代表 RDMA 通信能用,还得验证 QP 能不能建立起来。这一步用rping最直接。rping基于 librdmacm 封装,专门用来建立和管理 RDMA 连接,它是测试 RC QP 建链流程的标准工具。
服务端先跑:
rping -s -a 192.168.100.1 -v-s服务端模式,-a指定本地 IP 地址,-v输出详细信息。rping 服务启动后会等待客户端连接。
客户端执行:
rping -c -a 192.168.100.1 -v-c客户端模式,-a这里填服务端 IP。如果连接成功,客户端会向服务端发送数据,服务端收到后原样返回,客户端再确认接收。整个过程会打印 RTT 延迟数据。
rping 通了的实际意义是:两端已经成功完成了 QP 从 RESET 到 RTR/RTS 的完整状态流转,RC 连接真实建立起来,数据面收发也正常了。
如果你用的是 Mellanox 网卡,还可以用rdma命令直接看 QP 状态:
rdma res show qp输出大概长这样:
dev mlx5_0 qpn 0x25 type RC state RTS sq-psn 0 comm rping dev mlx5_0 qpn 0x26 type RC state RTS sq-psn 0 comm rpingstate RTS表示状态已经达到 Ready to Send,这是 QP 的正常工作状态。如果这里显示的是INIT、RTR或者ERROR,说明连接有问题。这个命令在排查问题的时候特别好用,能直观定位链路卡在哪一步。
4.3 第三层:用 perftest 测带宽和延迟,判断“通不通”和“快不快”
rping 只能证明能连,证明不了性能。实际做性能验证,行业标准工具是 perftest 包里的ib_write_bw、ib_read_bw、ib_send_bw和对应的延迟测试ib_write_lat、ib_read_lat、ib_send_lat。
最常用的带宽测试是ib_write_bw,测的是 RDMA 写操作的带宽。服务端启动:
ib_write_bw -d mlx5_0 -i 1 -F参数含义:-d mlx5_0指定设备,-i 1指定端口,-F是强制运行,因为不同版本 perftest 之间版本号不匹配时会拒绝跑,加-F可以跳过版本检查。
客户端执行:
ib_write_bw -d mlx5_0 -i 1 -F 192.168.100.1最后面的 IP 是服务端地址。如果 IP 不给,程序会进入交互模式,让你手动输入 IP。测试结束后,两端都会输出结果,以客户端为准,包含带宽、消息大小、传输次数、平均延迟等指标。
延迟测试用ib_write_lat:
# 服务端 ib_write_lat -d mlx5_0 -i 1 -F # 客户端 ib_write_lat -d mlx5_0 -i 1 -F 192.168.100.1注意 perftest 的延迟测试默认做 1000 次小包往返,输出的是微秒级数据。结果怎么判断是否正常?拿常见硬件打个比方:同样是 100GbE 的 ConnectX-5 网卡,单流ib_write_bw做 1MB 大包测试,实测带宽应该能到 90Gbps 以上;ib_write_lat小包延迟应该在 2~3 微秒量级。如果带宽只有几十 Gbps,或者延迟到了几十上百微秒,链路大概率有病根。
带宽上不去的排查方向我在下一节展开,这里只说测试时的一个建议:跑性能测试时要绑核。命令前面加taskset -c 2,把进程固定到一个 CPU 核上,避免进程在不同核之间漂移导致缓存失效和调度抖动。比如:
taskset -c 2 ib_write_bw -d mlx5_0 -i 1 -F 192.168.100.1不绑核也能跑,但结果波动会明显变大,不利于判断真实性能。
4.4 测试结果的判断标准:什么样的数值算正常
给一个参考阈值。25GbE 网卡做ib_write_bw大包测试,实测带宽一般能到 23~24Gbps 左右;100GbE 网卡通常在 90~95Gbps 左右,具体取决于 PCIe 代际和 CPU 主频。延迟方面,RoCEv2 同机架环境,小包写延迟一般 2~4 微秒;InfiniBand 环境能到 1~2 微秒。低于这个量级而且明显稳定的,可以认为性能基本正常。如果你测出来的带宽只有标称值的五六成,或者延迟比预期高一个数量级,就得怀疑链路配置、交换机丢包这类问题了。
5. 实战中踩过的坑与排查思路,一次讲清
5.1 QP 一直卡在 INIT/RTR,连接建立失败
这是我最常遇到的 RDMA 连接问题。现象是服务端和客户端都启动了,但rdma res show qp显示 QP 状态停在INIT或者RTR,数据发不出去。
排查步骤按顺序来:
第一步查防火墙。RoCEv2 使用 UDP 4791 端口,很多系统默认防火墙会拦截这个端口的包。如果服务端开了 iptables 或者 firewalld,先放行:
iptables -I INPUT -p udp --dport 4791 -j ACCEPT但这个坑很多老手会忽略,因为ping用的是 ICMP 协议,不受这个规则影响。ping通了就以为网络没问题,但 RDMA 数据包实际被挡在外面。
第二步查 GID。确认两端网卡的 GID index 和 IP 对应关系,尤其是多网卡机器。命令:
show_gids这个命令会列出所有端口的 GID、index、IP 和类型。确认对端可达的 IP 对应正确的 GID index。如果测试命令里没指定-x(GID index),默认用的是 index 0,但 index 0 可能对应的是 RoCEv1 或者错误的网卡,导致包发出去对端没法识别。这时候在 perftest 命令里加-x 3(换成实际 GID index)就能解决。
第三步查 MTU。两端 MTU 不一致,尤其是服务端开了巨型帧而客户端没开,RoCE 建链时会因为 MTU 协商失败卡在中间状态。把两端和交换机 MTU 统一,问题一般就能解决。
5.2 链路显示 ACTIVE,但带宽上不去,问题可能在 PCIe
链路状态正常、rping 也能通,但带宽测试结果不理想,这是第二大类问题。第一步检查的是网卡协商速率:
ethtool ens3f0如果网卡协商成了 25GbE,但你买的线缆和交换机端口支持 100GbE,那问题出在物理链路。换线、换光模块、检查交换机端口配置。如果协商速率正常,第二步就要怀疑 PCIe 链路了。
RDMA 的高性能依赖网卡和 CPU/内存之间的 PCIe 总线。网卡插在 PCIe 3.0 x8 的插槽上,理论上限只有约 8GB/s(64Gbps),跑 100GbE 网卡时这个带宽就已经不够了。用lspci -vvv查网卡当前的 PCIe 速率和宽度:
lspci -vvv | grep -A 20 Mellanox看LnkSta字段,比如Speed 8GT/s, Width x16表示 PCIe 3.0 x16,最大能到 128Gbps,跑 100GbE 没问题。如果是Speed 8GT/s, Width x8,最大只有 64Gbps,那 100GbE 网卡性能直接腰斩。这时候要把网卡换到 x16 插槽,或者检查 BIOS 里 PCIe 拆分设置。
还有个容易被忽略的:CPU 频率。RDMA 虽然是网卡硬件转发,但测试程序本身需要轮询 CQ 完成事件,CPU 频率低或者开了省电模式,小包延迟会明显变差。测试前可以先把 CPU 调到 performance 模式:
cpupower frequency-set -g performance5.3 RoCE 在普通交换机上跑,丢包导致性能雪崩
RoCE 最大的坑在交换机。普通以太网交换机收到突发流量时,缓冲区溢出会直接丢包,而 RoCE 硬件重传机制远没有 TCP 那么完善,丢包率只要超过千分之一,有效带宽可能直接掉到原来的十分之一以下。这是因为 RoCE 依赖的 Go-Back-N 重传机制,一个包丢了,后面的包可能全部被丢弃等待重传,性能呈雪崩式下降。
解决思路是给交换机开启无损以太网特性,核心是 PFC(Priority Flow Control)和 ECN(Explicit Congestion Notification)。PFC 的作用是:当交换机某个端口缓冲区快满时,给上游设备发暂停帧,让上游暂时不要继续发包。ECN 的作用是:交换机检测到拥塞时,在报文里打标记,接收端根据标记通知发送端降速。RoCEv2 的网络规划里,通常还要把 RoCE 流量专门划分到一个优先级队列里,与普通 TCP 流量隔离,避免互相影响。
自己测试两机直连时没有交换机,不存在这个问题。但只要经过交换机,就得确认交换机型号和配置。很多园区交换机的默认配置是不开 PFC/ECN 的,RDMA 流量一上去就性能暴跌。所以买交换机之前,一定要确认是否支持无损以太网,配置时也要把 RoCE 的优先级队列单独调优。
5.4 perftest 报 “Couldn't connect to remote” 的常见原因
这个报错信息很直白,但原因有好几种。第一种是 IP 填错或服务端没起来,这个好排查。第二种是服务端起来了但监听地址不对,比如机器上有多个 IP,服务端用-a绑定的 IP 和客户端填的不是同一个。第三种是 librdmacm 的 address resolution 失败,可能和/etc/rdma/rdma.conf里的加载模块配置有关,或者对应设备的 rdma 服务没起来。第四种很隐蔽:系统同时插了多张网卡,librdmacm 默认解析到错误的设备上,这时候用环境变量强制指定:
export RDMA_CM_SOURCE_ADDRESS=192.168.100.1再跑测试,大概率就能解决。
5.5 一个顺手的小工具:把环境检查写成一条命令
测试环境搭得多了,我习惯把这些命令攒成一个小脚本,每次换新环境先跑一遍,快速排出基础硬伤:
#!/bin/bash echo "===== RDMA devices =====" ibv_devinfo | grep -E "hca_id|state|port|link_layer" echo "===== link info =====" ethtool ens3f0 | grep -E "Speed|Duplex|Link detected" echo "===== PCIe info =====" lspci -vvv | grep -A 15 "Network controller" | grep -E "LnkSta" echo "===== gid =====" show_gids | grep mlx5跑一遍就知道设备、链路、PCIe、GID 四个层面的状态,省得每次从头排查。
写在最后的经验
RDMA 这套技术本身不复杂,核心就一句话:把数据通路上所有软件模块拿掉,让网卡直接访问应用内存。但实际工程里把它跑好,考验的是对整个硬件链路和网络环境的理解。我做过很多次从零搭建 RDMA 测试环境的活儿,最大的体会是,所有问题几乎都能归到这四类:驱动固件没配对、QoS 配置没做对、MTU/PCIe 没校准、交换机丢包没人管。
如果你现在正准备开始折腾 RDMA,我给的建议是:先别急着上一堆参数调优,第一步只做连通性验证。用ibv_devinfo确认设备在位,用rping验证 RC 建链,用ib_write_bw和ib_write_lat看基线的带宽延迟。这三层走通了,再往下做应用集成和深度调优才有意义。后面如果大家感兴趣,我可以再写一篇讲 RDMA 编程接口怎么用,把ibv_post_send、ibv_post_recv这些核心 API 配合一个最简单的 ping-pong 示例完整走一遍。