Linux kernel 无法使用ib_create_qp创建队列对

Linux kernel 无法使用ib_create_qp创建队列对,linux-kernel,linux-device-driver,infiniband,rdma,Linux Kernel,Linux Device Driver,Infiniband,Rdma,我正在编写一个RDMA(InfiniBand)内核模块 到目前为止,我已经成功创建了保护域、发送和接收队列的完成队列 但每当我试图通过调用ib_create_qp来创建队列对时,它都无法创建队列对。我编写的代码如下所示: #include <linux/kernel.h> #include <linux/init.h> #include <linux/module.h> #include <linux/list.h> #include <li

我正在编写一个RDMA(InfiniBand)内核模块

到目前为止,我已经成功创建了保护域、发送和接收队列的完成队列

但每当我试图通过调用ib_create_qp来创建队列对时,它都无法创建队列对。我编写的代码如下所示:

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/err.h>
#include "myClient.h"


struct workqueue_struct *myClient_workqueue;
struct ib_sa_client myClient_sa_client;
/*
static void myClient_add_one(struct ib_device *device);
static void myClient_remove_one(struct ib_device *device);
*/

struct ib_pd *mypd;
struct ib_cq *myrcvcq;
struct ib_cq *myClientsendcq;
struct ib_qp *myClientqp;

void myClient_ib_recvcompletion(struct ib_cq *cq)
{
    printk("A user-specified callback that is invoked when a completion event occurs on the CQ.\n");
}


void myClient_ib_sendcompletion(struct ib_cq *cq)
{
        printk("A user-specified callback that is invoked when a completion event occurs on the CQ.\n");
}
static void my_qp_event_handler(struct ib_event *myqpAsyncEvent, void *anyPointer)
{
        printk(KERN_INFO "Dummy affiliated asynchronous event occured function called \n");
}


static void myClient_add_one(struct ib_device *device)
{
    union ib_gid tmp_gid;
    int ret;
    int hcaport = 1;
    int result = -ENOMEM;
    u16 port1Pkey;
    struct ib_port_attr attr;

        ret = ib_query_port(device,hcaport,&attr);
        printk("ib query port result %d  \n", ret);

//  Creating the Protection Domain for RDMA
    mypd = ib_alloc_pd(device);

    if(IS_ERR(mypd)){
        printk(KERN_INFO "Failed to allocate PD\n");
        return;
    }
    else{
        printk(KERN_INFO "1Successfully allocated the PD\n");
        pdset = true;
    }

//  Creating the receive completion queue for RDMA
    myrcvcq = ib_create_cq(device,myClient_ib_recvcompletion,NULL,NULL,myClient_recvq_size,0);
        if(IS_ERR(myrcvcq)){
                pr_err("%s:%d error code for receive cq%d\n", __func__, __LINE__, PTR_ERR(myrcvcq));
                //printk("Error creating QP: %d \n",PTR_ERR(myClientqp));
        }
    else{
        printk("Recieve CQ successfully created in address: %x \n",myrcvcq);
    }

//  Creating the send completion queue for RDMA
    myClientsendcq = ib_create_cq(device,myClient_ib_sendcompletion, NULL, NULL,myClient_sendq_size,0 );
        if(IS_ERR(myClientsendcq)){
                pr_err("%s:%d scqerror code for send cq%d\n", __func__, __LINE__, PTR_ERR(myClientsendcq));
                //printk("Error creating QP: %d \n",PTR_ERR(myClientqp));
        }
        else{
                printk("1Send CQ successfully created in address: %x \n",myClientsendcq);
        }

//  Creating the queue pair
//      Creating the queue pair

        struct ib_qp_init_attr init_qpattr;

        memset(&init_qpattr,0,sizeof(init_qpattr));
        init_qpattr.event_handler = myClient_qp_event_handler;
        init_qpattr.cap.max_send_wr = 2;
        init_qpattr.cap.max_recv_wr = 2;
        init_qpattr.cap.max_recv_sge = 1;
        init_qpattr.cap.max_send_sge = 1;
        init_qpattr.sq_sig_type = IB_SIGNAL_ALL_WR;
        init_qpattr.qp_type = IB_QPT_UD;
        init_qpattr.send_cq = myClientsendcq;
        init_qpattr.recv_cq = myrcvcq;

        myClientqp = ib_create_qp(mypd,&init_qpattr);

        if(IS_ERR(myClientqp)){
                pr_err("%s:%d error code %d\n", __func__, __LINE__, PTR_ERR(myClientqp));
                //printk("Error creating QP: %d \n",PTR_ERR(myClientqp));
        }
        else{
                printk(KERN_INFO "1The queue pair is successfully created \n");
                qpcreated = true;
        }



}
static void myClient_remove_one(struct ib_device *device)
{
}

static struct ib_client my_client = {
        .name   = "myRDMAclient",
        .add    = myClient_add_one,
        .remove = myClient_remove_one
};


static int __init myRDMAclient_init(void)
{
    int ret;

    ret = ib_register_client(&my_client);
    if(ret){
        //printk(KERN_ALERT "KERN_ERR Failed to register IB client\n");
        goto err_sa;
    }
    printk(KERN_ALERT "lKERN_INFO Successfully registered myRDMAclient module \n");
    return 0;

err_sa:


    return ret;
}


module_init(myRDMAclient_init);

我想你忘记注册内存区域了。 创建QP之前需要执行的操作包括:

  • 创建保护域
  • 寄存器存储区
  • 创建完成队列
  • 只有这样才能创建QP

    我不知道您使用的是什么设备和库,但在Mellanox IB库中:

    char mr_buffer[REGION_SIZE];
    //mypd its your protection domain that you allocated 
    struct ibv_mr *mr = ibv_reg_mr(mypd , mr_buffer, REGION_SIZE, 0);
    if (!mr) {
        //ERROR MSG
    }
    

    更新

    根据下面评论中的讨论,我猜您在当前发行版的基础上安装了Mellanox OFED驱动程序。查看Mellanox OFED内核驱动程序的3.1-1.0.3源代码,我发现它们通过添加一些字段更改了
    struct ib_qp_init_attr
    的布局。我敢肯定,您的问题在于,您是根据原始SLE 3.0.76-0.11内核头构建模块的,因此传递给create QP函数的
    init_qpatr
    结构没有在正确的位置设置值

    我不知道您是如何安装新的树外驱动程序的,因此我无法确切地告诉您如何正确构建模块,但您可以尝试添加类似的内容

        init_qpattr.qpg_type = 0;
    
    到您设置结构的位置。(我知道你
    memset
    整个东西已经归零了,但是这将确保你正在构建的头文件中有新的
    qpg_类型的成员作为结构。我认为这是OFED添加的一个新字段,它不在你原来的内核头文件中,所以如果你的模块编译了,那么你就在构建正确的头文件卢比)

    旧答案:

    因此,我怀疑您在mlx4驱动程序中遇到了与创建如此小的QP相关的错误(
    max\u send\u wr==max\u recv\u wr==2
    max\u send\u sge==max\u recv\u sge==1
    )。我设法找到了您正在使用的3.0.76-0.11内核的源代码,不幸的是,我没有看到任何明显的错误

    您可以尝试帮助调试此文件

  • 加载模块时,将模块参数
    debug_level=1
    添加到
    mlx4_核心
    模块中。使用驱动程序初始化的所有输出更新您的问题(关于“Max cques:”等的一系列行)。mlx4驱动程序中有相当数量的逻辑依赖于fimrware在初始化期间返回的参数,此输出将让我们看到这些是什么
  • 因此,值得检查您的HCA固件是否是最新的-使用较新的固件可能会获得更好的结果(尽管驱动程序应该可以正常工作,但由于缺少触发不同代码路径的固件功能,您可能会在未测试的驱动程序代码中遇到错误)
  • 尝试更新代码以增加这些参数。您可以尝试将
    max_send_sge
    max_recv_sge
    增加到2,并将
    max_send_wr
    max_recv_wr
    增加到32或128。(尝试单独或组合增加这些参数)
  • 如果您知道如何启用函数跟踪程序(很有帮助;我假设旧的SLES内核具有所有必需的特性),然后为mlx4_ib和mlx4_核心模块启用跟踪,然后加载您的模块将非常好。如果您使用跟踪更新您的问题,那么我们可以查看创建QP操作失败的地方—例如,它是否在
    set_rq_size()
    中失败,进入
    set_kernel_sq_size()
    还是在其他地方失败

  • 出错(myClientqp)时,打印的错误消息是什么
    是真的吗?它说myClient\u add\u one:错误代码-22OK。那么错误代码22是什么意思呢?linux内核目录中是否有一个文件可以找到这些代码的含义?我想要它,因为不同的网页提到了不同的含义,因为它22是EINVAL。它说您传入的一个参数无效。我什么也看不到您的代码显然有问题;什么内核版本和低级IB驱动程序(mlx4、mthca等)您正在使用?您使用的典型区域大小值是什么?这是绝对错误的。在创建QP之前不需要注册MR。@Dor marcus:我做了一些更改,因为内核ib_谓词中没有ib_reg_MR。h和ib_reg_phys没有实现,我使用ib_get_dma进行注册。它成功注册了内存。但是,我我仍然得到错误-22(表示无效参数)对于ib_create_qp。请提供帮助。感谢您在3.0.76-0.11内核上运行此代码吗?它是否成功创建了队列对?我想我的内核中没有启用调试选项,因此无法使用建议编号1和4。我检查了建议编号3,但这也显示了相同的“无效参数”错误代码-22。建议2,我正在进行这项工作,需要一些时间来确认。我甚至没有安装SLES11,所以我实际上没有尝试您的代码。我真的很困惑IPoIB能够在您的系统上成功运行(并创建QP)当代码失败时。IPoIB驱动程序确实在QP创建上设置了一些您没有设置的标志,但我不认为这会有什么不同。您确定根据正确的内核头构建模块吗?我查看了SLES内核配置,AFAICT config_MLX4_DEBUG设置为“y”,因此您至少可以执行#1。很遗憾,函数图跟踪器未启用,这一点看起来您是正确的。如果您能够重建内核,启用该功能和/或仅向mlx4驱动程序中添加printK将使您更容易调试。我已使用kprobe进行跟踪。我发现“mlx4#ib#u create#qp”每次我加载模块时,函数都会被执行。但在该函数中,我有另一个函数“check_qpg_attr”。但是这个“che”
        init_qpattr.qpg_type = 0;