首页 > 解决方案 > 具有多线程的 Infiniband RDMA 完成队列

问题描述

我正在学习如何通过 Inifniband 使用 RDMA,而我遇到的一个问题是使用超过 1 个线程的连接,因为我无法弄清楚如何创建另一个完成队列,因此工作完成在线程之间混杂在一起,它就废话了out,如何使用连接为每个线程创建一个队列?

以这个呕吐物为例:

void worker(struct ibv_cq* cq){
    while(conn->peer_mr.empty()) Sleep(1);
    struct ibv_wc wc{};
    struct ibv_send_wr wr{};
    memset(&wr, 0, sizeof wr);
    struct ibv_sge sge{};
    sge.addr = reinterpret_cast<unsigned long long>(conn->rdma_memory_region);
    sge.length = RDMA_BUFFER_SIZE;
    sge.lkey = conn->rdma_mr->lkey;
    wr.wr_id = reinterpret_cast<unsigned long long>(conn);
    wr.opcode = IBV_WR_RDMA_READ;
    wr.sg_list = &sge;
    wr.num_sge = 1;
    wr.send_flags = IBV_SEND_SIGNALED;
    struct ibv_send_wr* bad_wr = nullptr;
    while(true){
        if(queue >= maxqueue) continue;
        for(auto i = 0ULL; i < conn->peer_mr.size(); ++i){
            wr.wr.rdma.remote_addr = reinterpret_cast<unsigned long long>(conn->peer_mr[i]->mr.addr) + conn->peer_mr[i]->offset;
            wr.wr.rdma.rkey = conn->peer_mr[i]->mr.rkey;
            const auto err = ibv_post_send(conn->qp, &wr, &bad_wr);
            if(err){
                std::cout << "ibv_post_send " << err << "\n" << "Errno: " << std::strerror(errno) << "\n";
                exit(err);
            }
            ++queue;
            conn->peer_mr[i]->offset += RDMA_BUFFER_SIZE;
            if(conn->peer_mr[i]->offset >= conn->peer_mr[i]->mr.length) conn->peer_mr[i]->offset = 0;
        }
        int ne;
        do{
            ne = ibv_poll_cq(cq, 1, &wc);
        } while(!ne);
        --queue;
        ++number;
    }
}

如果我有不止一个线程,他们都会收到彼此的工作完成,我希望他们只收到自己的,而不是其他线程的。

标签: c++multithreadinginfinibandrdma

解决方案


完成队列是在此代码之外的某处创建的(您正在传递一个ibv_cq *)。如果您想弄清楚如何创建多个,那就是要关注的领域。

但是,“废话”并没有(只是)发生,因为线程之间的完成混合在一起:ibv_poll_cqandibv_post_send函数是线程安全的。相反,可能的问题是您的代码不是线程安全的:有些共享数据结构可以在没有锁的情况下访问(conn->peer_mr)。即使没有 RDMA,您也会遇到同样的问题。

第一步是弄清楚如何将工作分成几部分。考虑每个线程需要使其独立于其他线程的部分。它可能是您peer_mr的. 然后编码:)ibv_cq *rdma_mr


推荐阅读