rmda学习1:深入理解GID
GID(Global Identifier)是 RDMA(特别是 RoCEv2)网络中用于唯一标识一个 RDMA 端点的 128 位标识符,其核心作用相当于 TCP/IP 协议栈中的 IP 地址。理解 GID 的生成、查询与使用,是调试 RDMA 连接问题的关键。
0.1. 1. GID 的生成与作用
0.1.1. 1.1 GID 从哪里来?
- RoCEv1:GID 基于网卡端口的 MAC 地址 生成,只能在二层广播域内通信。
- RoCEv2(当前主流):GID 由网卡端口绑定的 IP 地址 动态生成。每个 IPv4 或 IPv6 地址都会在 GID 表中产生一个条目。
关键点:RoCEv2 数据包外层使用 UDP/IP 封装,IP 头部的源/目的 IP 地址正是从通信双方的 GID 转换而来。因此,GID 直接决定了 RDMA 报文在网络中的路由路径。
0.1.2. 1.2 GID 与 ip addr 的关系
对于一个网络接口(如 eth1),其配置的 IP 地址会直接影响 RDMA 设备端口的 GID 表。例如:
1 | |
这两个 IP 地址(一个 IPv4,一个 IPv6 链路本地)会在对应的 RDMA 设备(如 mlx5_1)的 GID 表中生成多个条目。
0.1.3. 1.3 show_gids 命令详解
show_gids 是查看 GID 表最直接的命令(通常由 infiniband-diags 包提供)。在您的环境中,针对 mlx5_1 设备的输出为:
1 | |
列含义:
DEV:RDMA 设备名称(
mlx5_1)。PORT:设备的物理端口号(通常为 1)。
INDEX:GID 在表中的索引号(从 0 开始)。这是编程时最重要的字段。
GID:128 位的十六进制标识符。对于 IPv4 映射的 GID,格式为
::ffff:<IPv4>。IPv4:如果该 GID 是由 IPv4 地址生成的,此处显示 IPv4 地址;若为 IPv6 链路本地地址,则显示
-。VER:版本。
v1对应 RoCEv1(基于 MAC),v2对应 RoCEv2(基于 IP)。现代数据中心普遍使用v2。NETDEV:关联的 Linux 网络接口名(
eth1)。
解读:
索引 0:IPv6 链路本地地址,RoCEv1。
索引 1:IPv6 链路本地地址,RoCEv2。
索引 2:IPv4 映射地址
::ffff:29.127.153.4,RoCEv1。索引 3:IPv4 映射地址,RoCEv2。
您的测试表明,索引 0 和 1 可用于 QP 通信,而索引 2 和 3 在您的驱动/固件环境下不可用(导致 ibv_modify_qp 到 RTR 失败)。这很可能是由于特定版本的 mlx5 驱动对 IPv4 映射 RoCEv2 GID 的支持不完整。
0.2. 2. ibv_query_gid() 函数解析
在 RDMA 编程中,ibv_query_gid() 用于从指定设备的端口读取特定索引的 GID。
0.2.1. 2.1 函数原型
1 | |
context:通过ibv_open_device()打开的 RDMA 设备上下文。port_num:设备的物理端口号(通常为 1,但若设备有多个端口则可选)。index:GID 表的索引(0, 1, 2…)。这是开发者必须明确选择的值。gid:输出参数,返回查询到的 16 字节 GID(定义为union ibv_gid { uint8_t raw[16]; })。
0.2.2. 2.2 示例代码中的硬编码问题
常见示例代码中往往这样写:
1 | |
这里硬编码了 port_num = 1,index = 0。这在生产环境中是不安全的,原因如下:
RoCEv1 vs v2:索引 0 通常是 RoCEv1 的 GID(基于 MAC),而多数现代网络要求使用 RoCEv2(基于 IP)。若强制使用索引 0,可能无法利用 RoCEv2 特性。
IPv4 vs IPv6:如果网络通信基于 IPv4,您应该选择 IPv4 映射的 GID 索引(如 3),而不是链路本地 IPv6 GID。但在您的环境中,索引 3 不可用,因此需要回退到索引 1(IPv6 RoCEv2)。
多网口/容器环境:GID 表的有效索引可能不是 0、1,而是更大的数字(如 4、5)。硬编码索引会导致查询到全零的无效 GID。
0.2.3. 2.3 正确的动态选择 GID 索引的方法
生产级代码应该遍历 GID 表,根据条件选择合适的索引。典型的策略:
跳过全零 GID(无效条目)。
优先选择 RoCEv2 类型(通过读取
/sys/class/infiniband/.../gid_attrs/types/<idx>判断)。在已知 IPv4 映射 GID 不可用时,自动回退到 IPv6 RoCEv2 GID。
可选:匹配关联的 netdev 名称(通过 sysfs)。
以下是一个针对您环境的动态选择函数示例(优先使用 RoCEv2,跳过 IPv4 映射的索引 2/3,选择索引 1):
1 | |
然后在 main() 中使用:
1 | |
0.3. 3. GID 在 QP 状态迁移中的作用
在将 QP 从 INIT 切换到 RTR(Ready to Receive)时,必须提供对端的 GID 和本端的 GID 索引:
1 | |
dgid:目标 GID,将填入 RoCEv2 报文的 目的 IP 地址。sgid_index:本地 GID 索引,指示硬件使用哪个源 IP 地址发送数据包。
如果 sgid_index 指向的 GID 对应的 IP 地址无法路由到对端(例如,选择了 IPv4 映射但该索引在驱动中不可用),RoCEv2 数据包将无法送达,导致 ibv_modify_qp 返回错误(如您遇到的 Failed to modify QP to RTR)。
0.4. 4. 针对您环境的总结与建议
GID 索引
类型
可用性
说明
0
IPv6 链路本地 RoCEv1
可用
旧版本,不建议
1
IPv6 链路本地 RoCEv2
可用
推荐使用,稳定可靠
2
IPv4 映射 RoCEv1
不可用
驱动/固件不支持
3
IPv4 映射 RoCEv2
不可用
驱动/固件不支持(导致 RTR 失败)
结论:
您的 RDMA 环境健康,只是 IPv4 映射 RoCEv2 GID 未被驱动正确支持。
在生产代码中禁止硬编码 GID 索引,必须实现动态选择逻辑,优先选择可用的 RoCEv2 索引(在您环境中为索引 1)。
使用
show_gids和ibv_query_gid配合 sysfs 读取类型,可以写出可移植的代码。如果您需要抓包,由于 IPv6 GID 产生的 RoCEv2 报文使用 IPv6 头部,
tcpdump过滤条件应为ip6 and udp port 4791或直接udp port 4791。
0.5. 5. 附录:常见调试命令
1 | |
通过以上内容,您应该能够理解 GID 的本质、查询方法、编程注意事项,并针对您的环境做出正确的选择。