2. 第一个RDMA程序
任务目标
编写第一个RDMA程序,使用verbs API完成以下操作:枚举系统中的RDMA设备、查询每个设备的端口属性、打印端口的GID(Global ID)与端口状态,验证RDMA环境可用性。
环境依赖
- Linux系统,已安装RDMA网卡(如Mellanox CX系列)或软RoCE环境
- 安装rdma-core开发库:
apt install libibverbs-dev rdma-core(Debian/Ubuntu)或yum install libibverbs-devel rdma-core(RHEL/CentOS) - 编译需链接
-libverbs
关键完整代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <infiniband/verbs.h>
static const char *port_state_str(enum ibv_port_state state) {
switch (state) {
case IBV_PORT_DOWN: return "DOWN";
case IBV_PORT_ACTIVE: return "ACTIVE";
default: return "UNKNOWN";
}
}
int main(void) {
struct ibv_device **dev_list;
struct ibv_context *ctx = NULL;
struct ibv_port_attr port_attr;
struct ibv_gid_entry gid_entry;
int num_devices, i, j;
dev_list = ibv_get_device_list(&num_devices);
if (!dev_list) {
perror("ibv_get_device_list");
return 1;
}
if (num_devices == 0) {
fprintf(stderr, "未发现RDMA设备,请检查驱动或硬件\n");
ibv_free_device_list(dev_list);
return 1;
}
printf("发现 %d 个RDMA设备:\n", num_devices);
for (i = 0; i < num_devices; i++) {
printf("\n设备 %d: %s (%s)\n", i,
ibv_get_device_name(dev_list[i]),
ibv_node_type_str(ibv_node_type(dev_list[i])));
ctx = ibv_open_device(dev_list[i]);
if (!ctx) {
fprintf(stderr, " 无法打开设备: %s\n", strerror(errno));
continue;
}
/* 查询端口1的属性(多数RNIC端口号为1) */
if (ibv_query_port(ctx, 1, &port_attr) != 0) {
fprintf(stderr, " 查询端口1属性失败\n");
ibv_close_device(ctx);
continue;
}
printf(" 端口: %d\n", 1);
printf(" 状态: %s\n", port_state_str(port_attr.state));
printf(" 链路层: %s\n",
(port_attr.link_layer == IBV_LINK_LAYER_INFINIBAND) ? "InfiniBand" :
(port_attr.link_layer == IBV_LINK_LAYER_ETHERNET) ? "Ethernet" : "Unknown");
printf(" LID: 0x%04x\n", port_attr.lid);
printf(" 速率: %d Gbps\n", port_attr.active_speed == IBV_SPEED_EDR ? 100 :
port_attr.active_speed == IBV_SPEED_HDR ? 200 :
port_attr.active_speed == IBV_SPEED_NDR ? 400 :
port_attr.active_speed == IBV_SPEED_FDR ? 56 :
port_attr.active_speed == IBV_SPEED_QDR ? 40 :
port_attr.active_speed == IBV_SPEED_SDR ? 10 : 0);
/* 枚举并打印GID(前4个) */
printf(" GID列表:\n");
for (j = 0; j < 4; j++) {
if (ibv_query_gid_ex(ctx, 1, j, &gid_entry, 0) != 0)
break;
printf(" [%d] %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n",
j,
gid_entry.gid.raw[0], gid_entry.gid.raw[1],
gid_entry.gid.raw[2], gid_entry.gid.raw[3],
gid_entry.gid.raw[4], gid_entry.gid.raw[5],
gid_entry.gid.raw[6], gid_entry.gid.raw[7],
gid_entry.gid.raw[8], gid_entry.gid.raw[9],
gid_entry.gid.raw[10], gid_entry.gid.raw[11],
gid_entry.gid.raw[12], gid_entry.gid.raw[13],
gid_entry.gid.raw[14], gid_entry.gid.raw[15]);
}
ibv_close_device(ctx);
}
ibv_free_device_list(dev_list);
return 0;
}
编译与运行
gcc -o rdma_query rdma_query.c -libverbs
sudo ./rdma_query # 部分环境需要root权限访问设备
验证方法
- 通过条件:程序能列出至少1个RDMA设备,端口状态显示为
ACTIVE(若网卡已连接)或DOWN(未插线/未配置),且能打印出非全零的GID(通常index 0为全零,index 1及以上为有效RoCE GID)。 - 交叉验证:运行系统命令
ibv_devinfo -v对比输出,设备名、端口状态、LID和GID应一致。 - 预期输出示例(具体值取决于硬件环境):
发现 1 个RDMA设备:
设备 0: mlx5_0 (InfiniBand)
端口: 1
状态: ACTIVE
链路层: Ethernet
LID: 0x0000
速率: 100 Gbps
GID列表:
[0] 00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00
[1] fe:80:00:00:00:00:00:00:ec:0d:9a:03:fe:80:00:01
常见问题
- ibv_get_device_list返回NULL:检查是否加载驱动模块(
lsmod | grep rdma),或使用ibv_devinfo确认系统能看到设备。 - ibv_query_gid_ex返回ENOSYS:旧版rdma-core不支持该API,可改用
ibv_query_gid()(非_ex版本),但需自行处理GID index范围。 - 权限不足:访问verbs设备通常需要root或CAP_NET_RAW权限,使用
sudo运行即可。