4. 获取端口GID列表
任务目标
编写一个InfiniBand用户态程序,查询指定端口的GID表,打印每个GID的索引(index)与128位地址(GID)。GID是端口的全局标识符,由子网管理器(SM)分配,在RoCE/IB网络中用于寻址。
核心实现
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <infiniband/verbs.h>
int main(int argc, char *argv[]) {
struct ibv_device **dev_list;
struct ibv_context *ctx = NULL;
struct ibv_port_attr port_attr;
struct ibv_gid_entry gid_entries[128];
int num_devices, num_gids;
int dev_index = 0, port_num = 1;
int i;
// 解析命令行参数: [device_index] [port_num]
if (argc > 1) dev_index = atoi(argv[1]);
if (argc > 2) port_num = atoi(argv[2]);
// 获取设备列表
dev_list = ibv_get_device_list(&num_devices);
if (!dev_list) {
perror("ibv_get_device_list");
return 1;
}
if (dev_index >= num_devices || dev_index < 0) {
fprintf(stderr, "无效设备索引 %d (共 %d 个设备)\n", dev_index, num_devices);
goto cleanup;
}
// 打开设备
ctx = ibv_open_device(dev_list[dev_index]);
if (!ctx) {
perror("ibv_open_device");
goto cleanup;
}
// 查询端口属性
if (ibv_query_port(ctx, port_num, &port_attr)) {
perror("ibv_query_port");
goto cleanup;
}
// 查询GID表
num_gids = ibv_query_gid_table(ctx, gid_entries, 128, port_num);
if (num_gids < 0) {
perror("ibv_query_gid_table");
goto cleanup;
}
printf("设备: %s, 端口: %d, GID表长度: %d\n",
ibv_get_device_name(dev_list[dev_index]), port_num, num_gids);
// 打印每个GID
for (i = 0; i < num_gids; i++) {
char gid_str[INET6_ADDRSTRLEN] = {0};
uint8_t *raw = gid_entries[i].gid.raw;
// 格式化GID为IPv6风格字符串
snprintf(gid_str, sizeof(gid_str),
"%02x%02x:%02x%02x:%02x%02x:%02x%02x:"
"%02x%02x:%02x%02x:%02x%02x:%02x%02x",
raw[0], raw[1], raw[2], raw[3],
raw[4], raw[5], raw[6], raw[7],
raw[8], raw[9], raw[10], raw[11],
raw[12], raw[13], raw[14], raw[15]);
printf("GID[%2d] %s (所属端口: %d)\n",
gid_entries[i].gid_index, gid_str,
gid_entries[i].port_num);
}
cleanup:
if (ctx) ibv_close_device(ctx);
ibv_free_device_list(dev_list);
return 0;
}
编译与运行
# 编译(需要libibverbs-dev)
gcc -o list_gids list_gids.c -libverbs
# 运行(默认设备0,端口1)
./list_gids
# 指定设备索引和端口
./list_gids 1 2
验证方法
程序执行后,观察输出是否包含GID索引和完整地址。判定通过的依据:
- 程序正常退出(返回码0),无错误信息
- 输出中GID表长度大于0,且每个GID索引连续(0,1,2,...)
- GID地址格式为8组16位十六进制数,每组以冒号分隔
- 至少有一个GID的地址以
fe80:开头(链路本地地址,由端口MAC生成)
若系统配置了RoCE或IB网络,还会看到以0000:开头的GID(对应RoCEv2的IP地址)或SM分配的全局GID。可用ibv_devinfo命令交叉验证端口GID数量。
关键说明
本程序使用ibv_query_gid_table()(verbs 1.2+ API),相比旧版ibv_query_gid()循环调用,一次即可获取完整GID表。GID索引0通常是链路本地地址,索引1及以上为SM分配或RoCEv2使用的全局地址。若设备不支持该API(返回ENOSYS),可改用循环调用ibv_query_gid()直到返回非0。