2、RoCE协议栈深度解析:IBTA协议分层、RoCE v2的UDP封装、GRH与IP路由、RoCE的流控机制(PFC/ECN)

好,咱们接着聊。上一章我们把RoCE的“前世今生”捋了一遍,知道了它为什么能火。这一章,咱们得把袖子撸起来,看看它的“内脏”长什么样。

说白了,RoCE能跑得又快又稳,靠的就是一套精密的协议栈。我刚开始接触的时候,也觉得这东西挺玄乎,不就是把IB的报文塞进以太网吗?后来踩了几个坑才明白,里面的门道深着呢。今天我就带你一层层剥开它。

2.1 IBTA协议分层:从应用到物理的“四层楼”

RoCE的协议栈,本质上继承了InfiniBand(IB)的分层思想。你可以把它想象成一栋四层小楼:

  • 上层应用(ULP):比如Verbs API、MPI、NVMe-oF。这是咱们程序员直接打交道的地方。
  • 传输层(Transport Layer):负责可靠连接(RC)、不可靠数据报(UD)等。这是RDMA的核心,保证数据不丢不乱。
  • 网络层(Network Layer):在IB原生环境里是GRH(全局路由头),在RoCE v2里就变成了IP头。
  • 链路层/物理层(Link/Physical Layer):在IB里是IB链路,在RoCE里就是以太网MAC和PHY。

嗯,这里要注意:RoCE v1其实只改了最下面两层,把IB的链路层换成了以太网,但网络层还是GRH。这就导致了一个问题——它没法跨网段路由。我在一个数据中心项目里就吃过这个亏,两台机器不在同一个VLAN里,RoCE v1死活连不上。后来全换成了v2,才搞定。

核心区别:RoCE v1 = IB传输层 + GRH + 以太网链路层;RoCE v2 = IB传输层 + UDP + IP + 以太网链路层。

2.2 RoCE v2的UDP封装:为什么偏偏是UDP?

你可能会问:为什么RoCE v2要选UDP?而不是TCP?或者干脆直接裸IP?

我个人习惯从两个角度来理解这个问题:

  1. 性能角度:RDMA要的是低延迟、高吞吐。TCP有重传、拥塞控制、保序这些“重武器”,虽然可靠,但太慢了。UDP无状态,网卡硬件处理起来飞快,这才是RDMA要的。
  2. 路由角度:RoCE v1不能路由,因为交换机不认识GRH。但UDP是标准协议,所有三层交换机都认。RoCE v2把GRH的内容(源/目的GID)塞进UDP的载荷里,再用标准的IP头做路由,问题就解决了。

我记得有一次调试一个跨机柜的RoCE链路,抓包一看,UDP目的端口是4791。嗯,这是RoCE v2的“官方端口”。IANA分配的,跑不了。

封装格式大致是这样的:

+------------------+------------------+------------------+------------------+
|  以太网头 (14B)   |   IP头 (20B)     |  UDP头 (8B)      |  BTH + 载荷      |
|  (DMAC, SMAC, Type=0x0800) | (DIP, SIP, Proto=17) | (DPort=4791) |  (IB传输层数据)  |
+------------------+------------------+------------------+------------------+

说白了,RoCE v2就是给IB的传输层数据包,套上了一件UDP的“马甲”,让它能在标准的IP网络里畅行无阻。

2.3 GRH与IP路由:地址转换的“翻译官”

GRH(Global Routing Header)在IB世界里是用于跨子网路由的。它包含了128位的源GID和目的GID。在RoCE v2里,GID是怎么映射到IP地址的呢?

这里有个关键点:RoCE网卡在初始化时,会给自己分配一个GID。这个GID通常是根据网卡的MAC地址和子网前缀生成的。在RoCE v2中,GID的格式被设计成与IPv6地址兼容。实际上,很多实现里,GID就是直接从IPv6地址映射过来的。

我曾经遇到过一个兼容性问题:两台不同厂商的RoCE网卡,在同一个子网里,GID格式解析不一致,导致连接建立失败。后来发现是GID的索引号对不上。所以,我建议你在部署多厂商设备时,一定要先验证GID的交互是否正常。

路由过程是这样的:

  • 发送端:根据目的GID,查找本地路由表,找到对应的目的IP和下一跳MAC。
  • 交换机:根据目的IP做标准的三层转发。
  • 接收端:收到UDP报文后,解封装,取出BTH,根据目的GID判断是否是发给自己的。

避坑指南:我曾经在配置RoCE v2跨网段路由时,忘了在交换机上开启“UDP 4791”的转发。结果报文到了网关就被丢了。记住,交换机必须知道这个UDP端口是RoCE流量,不能随便丢弃。

2.4 RoCE的流控机制:PFC与ECN的“双保险”

RoCE最怕什么?丢包!因为RDMA的传输层(RC模式)一旦丢包,就要重传,延迟瞬间飙升。所以,流控是RoCE网络的命根子。主要有两招:PFC和ECN。

2.4.1 PFC(优先级流控):简单粗暴的“刹车”

PFC是链路层的流控。它把以太网流量分成8个优先级(VLAN PCP)。当某个优先级的接收缓冲区快满时,接收端会发一个PFC暂停帧给发送端,让它在那个优先级上“闭嘴”一会儿。

说白了,就是“你发得太快了,慢点!”

但PFC有个大问题:它可能会引起死锁队头阻塞。比如,一个低优先级的流量被暂停了,但它占着缓冲区不放,导致高优先级流量也进不来。我在一个存储集群里就遇到过,PFC一触发,整个网络的吞吐量直接掉到零。后来我们调整了缓冲区大小和阈值,才缓解了这个问题。

警告:PFC是一把双刃剑。它能防止丢包,但用不好会引发连锁反应。建议只在RoCE流量所在的优先级上启用PFC,其他普通流量不要用。

2.4.2 ECN(显式拥塞通知):更智能的“减速”

ECN是网络层的流控。它比PFC更“文明”。当交换机检测到队列深度超过阈值时,它会在IP头里打上一个标记(CE码点)。接收端看到这个标记后,会通过CNP(拥塞通知包)告诉发送端:“哥们,前面堵了,降速吧!”

发送端收到CNP后,会按照一个算法(比如DCQCN)来降低发送速率。等拥塞缓解了,再慢慢恢复。

我个人更倾向于用ECN。因为它不会像PFC那样“一刀切”,而是让发送端主动调整,更平滑。不过,ECN需要端到端的支持:网卡、交换机、驱动都得配合好。

下面这张图展示了PFC和ECN的工作位置:

RoCE 流控机制:PFC vs ECN 发送端 (Sender) 交换机 (Switch) 接收端 (Receiver) PFC 暂停帧 (XOFF) ECN 标记 (CE) CNP 拥塞通知包 图例: 数据流 PFC 暂停帧 ECN 标记数据 CNP 通知

在实际部署中,我建议采用ECN为主,PFC为辅的策略。ECN处理大部分拥塞场景,PFC作为最后一道防线,防止缓冲区溢出导致丢包。你想想看,如果只靠PFC,一旦拥塞,整个链路都受影响;而ECN只影响那些“肇事”的流,更公平。

好了,这一章的内容就到这里。协议栈的分层、UDP封装、路由、流控,这些都是RoCE的“内功”。理解了这些,你就能明白为什么RoCE能跑得又快又稳,也知道在出问题时该从哪里下手排查。


专注资料整理