4. 创建完成队列

任务目标

在RDMA编程中,完成队列(Completion Queue, CQ)用于存放异步操作(如发送、接收、读写)的完成通知。本案例创建CQ并设置队列长度,随后查询其属性以验证创建结果。

环境与依赖

需要Linux系统、RDMA网卡(或Soft-RoCE模拟设备)、librdmacm和libibverbs开发库。编译时链接-libverbs。

关键完整代码


#include <infiniband/verbs.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main() {
    struct ibv_device **dev_list;
    struct ibv_context *ctx = NULL;
    struct ibv_pd *pd = NULL;
    struct ibv_cq *cq = NULL;
    struct ibv_cq_attr cq_attr;
    struct ibv_device_attr dev_attr;
    int num_devices, cqe = 256;  /* 请求的CQ深度 */

    /* 1. 获取设备列表 */
    dev_list = ibv_get_device_list(&num_devices);
    if (!dev_list || num_devices < 1) {
        fprintf(stderr, "未找到RDMA设备\n");
        return 1;
    }

    /* 2. 打开第一个可用设备 */
    ctx = ibv_open_device(dev_list[0]);
    if (!ctx) {
        fprintf(stderr, "打开设备失败\n");
        goto cleanup_dev_list;
    }

    /* 3. 查询设备属性,确认最大CQ容量 */
    if (ibv_query_device(ctx, &dev_attr)) {
        fprintf(stderr, "查询设备属性失败\n");
        goto cleanup_ctx;
    }
    printf("设备支持最大CQ深度: %u\n", dev_attr.max_cqe);

    /* 4. 分配保护域(CQ不强制需要PD,但后续关联QP时必需) */
    pd = ibv_alloc_pd(ctx);
    if (!pd) {
        fprintf(stderr, "分配PD失败\n");
        goto cleanup_ctx;
    }

    /* 5. 创建CQ */
    cq = ibv_create_cq(ctx, cqe, NULL, NULL, 0);
    if (!cq) {
        fprintf(stderr, "创建CQ失败\n");
        goto cleanup_pd;
    }
    printf("CQ创建成功,请求深度=%d\n", cqe);

    /* 6. 查询CQ属性验证 */
    memset(&cq_attr, 0, sizeof(cq_attr));
    if (ibv_query_cq(cq, &cq_attr)) {
        fprintf(stderr, "查询CQ属性失败\n");
        goto cleanup_cq;
    }
    printf("CQ实际深度: %d\n", cq_attr.cqe);

    /* 7. 清理资源 */
    ibv_destroy_cq(cq);
    ibv_dealloc_pd(pd);
    ibv_close_device(ctx);
    ibv_free_device_list(dev_list);
    return 0;

cleanup_cq:
    ibv_destroy_cq(cq);
cleanup_pd:
    ibv_dealloc_pd(pd);
cleanup_ctx:
    ibv_close_device(ctx);
cleanup_dev_list:
    ibv_free_device_list(dev_list);
    return 1;
}

编译与运行


gcc -o create_cq create_cq.c -libverbs
./create_cq

验证方法与判定标准

程序执行后应依次输出:

  • 设备支持的最大CQ深度(数值取决于硬件,通常≥65536)
  • CQ创建成功,请求深度=256
  • CQ实际深度: 256(或设备允许的最近值)

若最后一行输出的实际深度与请求深度一致(或为设备支持的最大值向下取整),且程序退出码为0,则判定CQ创建与查询功能正常。若输出创建CQ失败,请检查设备是否支持RDMA、权限是否足够(可能需要sudo),或尝试减小cqe值(如64)后重试。

常见问题

  • ibv_open_device返回NULL:确认ibv_get_device_list返回的设备可用,且当前用户有访问权限。
  • 创建CQ返回NULL:请求的cqe超过设备max_cqe,或内存不足。可先查询设备属性后设置合理值。
  • 实际深度与请求不同:部分硬件会向上取整到2的幂次或受限于max_cqe,属正常现象,只要实际值≥请求值即可。