libverbs 初始化失败主因是RDMA驱动未加载或权限不足,需检查ib_uverbs等模块、/dev/infiniband/设备权限及用户组;RoCEv2连接必须正确获取GID索引并启用PFC/ECN无损网络;内存注册须用posix_memalign对齐且锁定。

libverbs 初始化失败:找不到设备或 no devices found
调用 ibv_get_device_list() 返回 nullptr 或空列表,最常见原因是内核没加载 RDMA 驱动,或者用户态没权限访问设备文件。
- 先确认硬件和驱动:运行
lsmod | grep ib_,至少要有ib_uverbs、ib_core、对应网卡的驱动(如mlx5_core);若无,需安装厂商 OFED 或内核配套 RDMA 模块 - 检查设备节点:
ls -l /dev/infiniband/uverbs*,权限应为crw-rw----且所属组通常是rdma;普通用户需加入该组:sudo usermod -aG rdma $USER,然后重新登录 - 容器环境要额外暴露设备:Docker 启动时加
--device=/dev/infiniband/ --cap-add=SYS_RAWIO,否则ibv_get_device_list()一定为空
创建 QP 前必须搞清 port_attr 和 gid_index
RDMA 连接不是靠 IP 地址,而是靠 GID(Global Identifier),它绑定在物理端口上。跳过查询端口属性直接硬编码 gid_index=0 是多数初学者连接失败的根源。
- 必须先调用
ibv_query_port()获取port_attr,确认端口是否处于IB_PORT_ACTIVE状态;再用ibv_query_gid()查指定gid_index是否有效(返回 0 表示成功) -
gid_index不等于网卡编号,也不等于 IPv6 地址序号;它对应 RoCEv2 下的 GID 表索引,通常从 0 开始找第一个非全零 GID:ibv_query_gid(ctx, port, 0, &gid),然后逐个试到port_attr.gid_tbl_len - RoCEv2 要求 GID 类型为
IB_GID_TYPE_ROCE_V2;如果用错类型(比如默认的IB_GID_TYPE_IB),ibv_create_qp()可能成功但后续连接超时
post_send 失败:WR status 为 IB_WC_RETRY_EXC_ERR 或 IB_WC_TIMEOUT
这不是代码写错了,而是底层链路没通——QP 已创建,但没完成地址解析(ARP for RoCE)或没建立连接上下文。
- 确保两端已交换并设置对方的
qpn、lid(InfiniBand)、port_num、gid;漏掉任意一项,ibv_post_send()就会返回 0(提交成功),但 WC 中状态是失败 - RoCEv2 必须启用无损网络:交换机需开 PFC(Priority Flow Control)和 ECN;服务器侧需配置 DCB:用
dcbtool检查ets和pfc是否启用,否则重传压垮队列,直接触发RETRY_EXC_ERR - 别忽略
ibv_poll_cq()的返回值:即使post_send()成功,也必须轮询 CQ 拿到完成事件(WC),才能确认数据真正发出;不 poll 就等同于“发了但不知道发没发出去”
内存注册(mr)后不能直接用 std::vector 或 new 分配的地址
ibv_reg_mr() 要求内存页必须是“锁定且连续”的物理页,而标准堆分配几乎不可能满足——哪怕只注册 1KB,也会因缺页中断或 TLB miss 导致 WR 失败。
立即学习“C++免费学习笔记(深入)”;
- 必须用
posix_memalign()或ibv_alloc_pd()配套的ibv_mem_alloc()(部分厂商扩展);对齐要求通常是 2MB 或 4KB,例如:posix_memalign(&buf, 4096, size) - 注册后记得检查返回值:若
ibv_reg_mr()返回nullptr,常见原因是内存未锁住(mlock()失败)或超出 ulimit -l 限制;用ulimit -l unlimited临时放开 - MR 生命周期必须长于所有依赖它的 WR;释放 MR 后还往里 post_send,会触发段错误或静默丢包——这种 bug 很难复现,但必崩










