【RDMA】RDMA技术详解(四):RDMA之Verbs和编程步骤-程序员宅基地

技术标签: python  java  linux  编程语言  内核  

目录

什么是Verbs

相关名词解释

Verbs API

Verbs API是什么

设计Verbs API的原因

Verbs API所包含的内容

使用Verbs API编写RDMA应用程序

查看接口定义

包含头文件

编写应用

编译 & 执行

官方示例程序

libibverbs

librdmacm

参考文献

ibv_poll_cq()

ibv_req_notify_cq()

ibv_get_cq_event()

ibv_ack_cq_events()

Errors

IBV_WC_WR_FLUSH_ERR(5/0x5)

IBV_WC_RNR_RETRY_EXC_ERR(13/0xd)


原文:https://blog.csdn.net/weixin_33978451/article/details/112398245

什么是Verbs

Verbs直译过来是“动词”的意思,它在RDMA领域中有两种含义:

1) 由IB规范所描述的一组抽象定义

规定了各厂商的软硬件在各种Verbs下应该执行的动作或者表现出的行为,IB规范并未规定如何编程实现这些Verbs,在这种含义下,Verbs是与操作系统无关的。

举个例子,IB规范要求所有RDMA设备必须支持Create QP的行为(IB 规范11.2.5.1):

描述:
​ 为指定的设备创建一个QP。
​ 用户必须指定一组用于初始化QP的属性。
​ 如果创建QP所需的属性有非法值或者缺失,那么应该返回错误,该QP不会被创建;如果成功, 那么返回该QP的指针和QPN。
​ ……
输入:
​ 设备指针;
​ SQ关联到的CQ;
​ RQ关联到的CQ,如果是XRC的INI QP,则可以不携带此参数;
​ ……
输出:
​ 新创建的QP的指针;
​ QP Number;
​ SQ的最大WR容量。
​ ……

可以看出IB规范中的Verbs是对一个概念进行定义,讲的是“需要支持什么,但具体怎么实现我不做规定”。

2) 由OpenFabrics推动实现的一组RDMA应用编程接口(API)

既然是API,那么必然和运行的操作系统相关。Verbs API有Linux版本以及Windows版本(Windows版很久没有更新了)。

以Create QP为例,下文引用自Linux用户态Verbs API的帮助文档(ibv_create_qp(3): create/destroy queue pair):

名称:
​ ibv_create_qp - create a queue pair (QP)
概要:

#include <infiniband/verbs.h>

struct ibv_qp ibv_create_qp(struct ibv_pd pd, struct ibv_qp_init_attr *qp_init_attr);

描述:
​ ibv_create_qp()通过一个关联的PD创建一个QP,参数qp_init_attr是一个ibv_qp_init_attr类型的结构体,其定义在<infiniband/verbs.h>中。

struct ibv_qp_init_attr {

struct ibv_cq *send_cq; /* CQ to be associated with the Send Queue (SQ) */

struct ibv_cq *recv_cq; /* CQ to be associated with the Receive Queue (RQ) */

struct ibv_srq *srq; /* SRQ handle if QP is to be associated with an SRQ, otherwise NULL */

struct ibv_qp_cap cap; /* QP capabilities */

enum ibv_qp_type qp_type; /* QP Transport Service Type: IBV_QPT_RC, IBV_QPT_UC, or IBV_QPT_UD */

...

};

​ 函数ibv_create_qp()会更新qp_init_attr->cap struct的内容,返回创建的QP所真正支持的规格……
返回值:
​ ibv_create_qp()返回被创建的QP的指针,或者在失败时返回NULL。QPN将在返回的指针所指向的结构体中。

可见Verbs API即是对IB规范中的Verbs定义的具体软件实现。

Verbs的第一种语义直接查阅IB规范的第11章即可,里面做了非常详细的描述。

本文介绍的是第二种语义,包含Verbs API是什么,如何和硬件产生交互,我们如何通过Verbs API来编写RDMA程序。如无特殊说明,下文中的Verbs均特指Verbs API。

相关名词解释

  • rdma-core

开源RDMA用户态软件协议栈,包含用户态框架、各厂商用户态驱动、API帮助手册以及开发自测试工具等。

rdma-core在github上维护,我们的用户态Verbs API实际上就是它实现的。https://github.com/linux-rdma/rdma-core

  • kernel RDMA subsystem

开源的Linux内核中的RDMA子系统,包含RDMA内核框架及各厂商的驱动。

RDMA子系统跟随Linux维护,是内核的的一部分。一方面提供内核态的Verbs API,一方面负责对接用户态的接口。

  • OFED

全称为OpenFabrics Enterprise Distribution,是一个开源软件包集合,其中包含内核框架和驱动、用户框架和驱动、以及各种中间件、测试工具和API文档。

开源OFED由OFA组织负责开发、发布和维护,它会定期从rdma-core和内核的RDMA子系统取软件版本,并对各商用OS发行版进行适配。除了协议栈和驱动外,还包含了perftest等测试工具。

下图为OFA给出的OFED的概览:

43666da8107d1235b94797d995820b3b.png

除了开源OFED之外,各厂商也会提供定制版本的OFED软件包,比如华为的HW_OFED和Mellanox的MLNX_OFED。这些定制版本基于开源OFED开发,由厂商自己测试和维护,会在开源软件包基础上提供私有的增强特性,并附上自己的配置、测试工具等。

以上三者是包含关系。无论是用户态还是内核态,整个RDMA社区非常活跃,框架几乎每天都在变动,都是平均每两个月一个版本。而OFED会定期从两个社区中取得代码,进行功能和兼容性测试后发布版本,时间跨度较大,以年为单位计。

Verbs API

Verbs API是什么

Verbs API是一组用于使用RDMA服务的最基本的软件接口,也就是说业界的RDMA应用,要么直接基于这组API编写,要么基于在Verbs API上又封装了一层接口的各种中间件编写。

Verbs API向用户提供了有关RDMA的一切功能,典型的包括:注册MR、创建QP、Post Send、Poll CQ等等。

对于Linux系统来说,Verbs的功能由rdma-core和内核中的RDMA子系统提供,分为用户态Verbs接口和内核态Verbs接口,分别用于用户态和内核态的RDMA应用。

结合上一部分的内容,我们给出一个OFED的全景:

e0eba2a4af3d4e6179e178ec3a005632.png

广义的Verbs API主要由两大部分组成:

1、IB_VERBS

接口以ibv_xx(用户态)或者ib_xx(内核态)作为前缀,是最基础的编程接口,使用IB_VERBS就足够编写RDMA应用了。

比如:

  • ibv_create_qp() 用于创建QP
  • ibv_post_send() 用于下发Send WR
  • ibv_poll_cq() 用于从CQ中轮询CQE

2、RDMA_CM

以rdma_为前缀,主要分为两个功能:

CMA(Connection Management Abstraction)

在Socket和Verbs API基础上实现的,用于CM建链并交换信息的一组接口。CM建链是在Socket基础上封装为QP实现,从用户的角度来看,是在通过QP交换之后数据交换所需要的QPN,Key等信息。

比如:

  • rdma_listen()用于监听链路上的CM建链请求。
  • rdma_connect()用于确认CM连接。

CM VERBS

RDMA_CM也可以用于数据交换,相当于在verbs API上又封装了一套数据交换接口。

比如:

  • rdma_post_read()可以直接下发RDMA READ操作的WR,而不像ibv_post_send(),需要在参数中指定操作类型为READ。
  • rdma_post_ud_send()可以直接传入远端QPN,指向远端的AH,本地缓冲区指针等信息触发一次UD SEND操作。

上述接口虽然方便,但是需要配合CMA管理的链路使用,不能配合Verbs API使用。

Verbs API除了IB_VERBS和RDMA_CM之外,还有MAD(Management Datagram)接口等。

需要注意的是,软件栈中的Verbs API具体实现和IB规范中的描述并不是完全一致的。IB规范迭代较慢,而软件栈几乎每天都有变化,所以编写应用或者驱动程序时,应以软件栈API文档中的描述为准。

狭义的Verbs API专指以ibv_/ib_为前缀的用户态Verbs接口,因为RDMA的典型应用是在用户态,下文主要介绍用户态的Verbs API。

设计Verbs API的原因

传统以太网的用户,基于Socket API来编写应用程序;而RDMA的用户,基于Verbs API来编写应用程序。

Verbs API支持IB/iWARP/RoCE三大RDMA协议,通过统一接口,让同一份RDMA程序程序可以无视底层的硬件和链路差异运行在不同的环境中。

Verbs API所包含的内容

用户态Verbs API主要包含两个层面的功能:

为方便讲解,下面对各接口的形式做了简化,格式为"返回值1,返回值2 函数名(参数1, 参数2)"

1)控制面:

设备管理:

  • device_list ibv_get_device_list()

用户获取可用的RDMA设备列表,会返回一组可用设备的指针。

  • device_context ibv_open_device(device)

打开一个可用的RDMA设备,返回其上下文指针(这个指针会在以后用来对这个设备进行各种操作)。

  • device_attr, errno ibv_query_device(device_context*)

查询一个设备的属性/能力,比如其支持的最大QP,CQ数量等。返回设备的属性结构体指针,以及错误码。

资源的创建,查询,修改和销毁:

  • pd ibv_alloc_pd(device_context)

申请PD。该函数会返回一个PD的指针。(PD(内存)保护域--见:https://blog.csdn.net/bandaoyu/article/details/112859981)

  • mr ibv_reg_mr(pd, addr, length, access_flag)

注册MR。用户传入要注册的内存的起始地址和长度,以及这个MR将要从属的PD和它的访问权限(本地读/写,远端读/写等),返回一个MR的指针给用户。

  • cq ibv_create_cq(device_context, cqe_depth, ...)

创建CQ。用户传入CQ的最小深度(驱动实际申请的可能比这个值大),然后该函数返回CQ的指针。

  • qp ibv_create_qp(pd, qp_init_attr)

创建QP。用户传入PD和一组属性(包括RQ和SQ绑定到的CQ、QP绑定的SRQ、QP的能力、QP类型等),向用户返回QP的指针。(SRQ=shared receive queue)

  • errno ibv_modiy_qp(qp, attr, attr_mask)

修改QP。用户传入QP的指针,以及表示要修改的属性的掩码和要修改值。修改的内容可以是QP状态、对端QPN(QP的序号)、QP能力、端口号和重传次数等等。如果失败,该函数会返回错误码。
Modify QP最重要的作用是让QP在不同的状态间迁移,完成RST-->INIT-->RTR-->RTS的状态机转移后才具备下发Send WR的能力。也可用来将QP切换到ERROR状态。

  • errno ibv_destroy_qp(qp)

销毁QP。即销毁QP相关的软件资源。其他的资源也都有类似的销毁接口。

中断处理:

  • event_info, errno ibv_get_async_event(device_context)

从事件队列中获取一个异步事件,返回异步事件的信息(事件来源,事件类型等)以及错误码。

连接管理

  • rdma_xxx()

用于CM建链,不在本文展开讲。

  • ...

2)数据面:

下发WR

  • bad_wr, errno ibv_post_send(qp, wr)

向一个QP下发一个Send WR,参数wr是一个结构体,包含了WR的所有信息。包括wr_id、sge数量、操作码(SEND/WRITE/READ等以及更细分的类型)。WR的结构会根据服务类型和操作类型有所差异,比如RC服务的WRITE和READ操作的WR会包含远端内存地址和R_Key,UD服务类型会包含AH,远端QPN和Q_Key等。

WR经由驱动进一步处理后,会转化成WQE下发给硬件。

出错之后,该接口会返回出错的WR的指针以及错误码。

  • bad_wr, errno ibv_post_recv(qp, wr)

同ibv_post_send,只不过是专门用来下发RECV操作的WR的接口。

获取WC

  • num, wc ibv_poll_cq(cq, max_num)

从完成队列CQ中轮询CQE,用户需要提前准备好内存来存放WC,并传入可以接收多少个WC。该接口会返回一组WC结构体(其内容包括wr_id,状态,操作码,QPN等信息)以及WC的数量。

使用Verbs API编写RDMA应用程序

  • 查看接口定义

内核态

内核态Verbs接口没有专门的API手册,编程时需要参考头文件中的函数注释。声明这些接口的头文件位于内核源码目录中的:

.../include/rdma/ib_verbs.h

比如ib_post_send()接口:

bcd542df2b9ba4968da751fa7e71fa47.png

函数注释中有明确介绍该函数的作用,输入、输出参数以及返回值。

用户态

有多种方法查阅用户态的Verbs API:

  • 在线查阅最新man page

用户态的Verbs API手册跟代码在一个仓库维护,手册地址:

https://github.com/linux-rdma/rdma-core/tree/master/libibverbs/man

这里是按照Linux的man page格式编写的源文件,直接看源文件可能不太直观。有很多在线的man page网站可以查阅这些接口的说明,比如官方的连接:

https://man7.org/linux/man-pages/man3/ibv_post_send.3.html

也有一些其他非官方网页,支持在线搜索:

https://linux.die.net/man/3/ibv_post_send

731d4842e6599fa10bc0fa7457f1231c.png
  • 查阅系统man page

如果你使用的商用OS安装了rdma-core或者libibverbs库,那么可以直接用man命令查询接口:

man ibv_post_send

e0d2fb93b12caf6996274de5b19dd75b.png
  • 查询Mellanox的编程手册

《RDMA Aware Networks Programming User Manual Rev 1.7》,最新版是2015年更新的。该手册写的比较详尽,并且附有示例程序,但是可能与最新的接口有一些差异。

  • 包含头文件

按需包含以下头文件:

#include <infiniband/verbs.h> // IB_VERBS 基础头文件

#include <rdma/rdma_cma.h> // RDMA_CM CMA 头文件 用于CM建链

#include <rdma/rdma_verbs.h> // RDMA_CM VERBS 头文件 用于使用基于CM的Verbs接口

编写应用

下面附上一个简单的RDMA程序的大致接口调用流程,Client端的程序会发送一个SEND请求给Server端的程序,图中的接口上文中都有简单介绍。

需要注意的是图中的建链过程是为了交换对端的GID,QPN等信息,可以通过传统的Socket接口实现,也可以通过本文中介绍的CMA接口实现。

图中特意列出了多次modify QP的流程,一方面是把建链之后交互得到的信息存入QPC中(即QP间建立连接的过程),另一方面是为了使QP处于具备收/发能力状态才能进行下一步的数据交互。具体状态机的内容请回顾9. RDMA之Queue Pair

(假设A节点的某个QP要跟B节点的某个QP交换信息,除了要知道B节点的QP序号——QPN之外,还需要GID,在传统TCP-IP协议栈中,使用了家喻户晓的IP地址来标识网络层的每个节点。而IB协议中的这个标识被称为GID(Global Identifier,全局ID)https://zhuanlan.zhihu.com/p/163552044)

3bba5d9bae953ee8ca3519516cbfdec5.png

编译 & 执行

不在本文讨论范围内。

官方示例程序

rdma-core的源码目录下,为libibverbs和librdmacm都提供了简单的示例程序,大家编程时可以参考。

libibverbs

位于rdma-core/libibverbs/examples/目录下,都使用最基础的IB_VERBS接口实现,所以建链方式都是基于Socket的。

  • asyncwatch.c 查询指定RDMA设备是否有异步事件上报
  • device_list.c 列出本端RDMA设备列表
  • devinfo.c 查询并打印本端RDMA设备详细信息,没有双端数据交互
  • rc_pingpong.c 基于RC服务类型双端数据收发示例
  • srq_pingpong.c 基于RC服务类型双端数据收发示例,与上一个示例程序的差异是使用了SRQ而不是普通的RQ。
  • ud_pingpong.c 基于UD服务类型双端数据收发示例
  • ud_pingpong.c 基于UC服务类型双端数据收发示例
  • xsrq_pingpong.c 基于XRC服务类型双端数据收发示例

librdmacm

位于rdma-core/librdmacm/examples/目录下:

  • rdma_client/server.c 基础示例,通过CM建链并使用CM VERBS进行数据收发。

该目录剩下的程序就没有研究了。

参考文献

[1] RDMA Aware Networks Programming User Manual Rev 1.7

[2] part1-OFA_Training_Sept_2016

[3] https://en.wikipedia.org/wiki/OpenFabrics_Alliance

API查询地址:https://www.rdmamojo.com/2012/11/03/ibv_create_cq/

 ibv_get_cq_event,ibv_ack_cq_events-获取和确认完成队列(CQ)事件

概要

#include <infiniband / verbs.h>

int ibv_get_cq_event(struct ibv_comp_channel * channel, struct ibv_cq ** cq,void ** cq_context);

void ibv_ack_cq_events(struct ibv_cq * cq,unsigned int nevents);

描述

ibv_get_cq_event()等待事件通道channel中的下一个完成事件。用获取事件的CQ填充参数cq,并用CQ的上下文填充cq_context。

ibv_ack_cq_events()确认 cq上的nevents事件CQ。

返回值

ibv_get_cq_event()成功返回0,错误返回-1。

ibv_ack_cq_events()不返回任何值。

提示

ibv_get_cq_events()返回的所有完成事件必须使用ibv_ack_cq_events()进行确认。

为了避免竞争,销毁CQ将等待所有完成事件得到确认;这保证了在成功与成功之间一对一的对应关系。

在数据路径中调用ibv_ack_cq_events()可能相对昂贵,因为它必须使用互斥量。因此,最好是通过对需要确认的事件数量进行计数并在对ibv_ack_cq_events()的一次调用中同时确认几个完成事件来分摊此成本。

例子

下面的代码示例演示了一种处理完成事件的可能方法。它执行以下步骤:

第一阶段:准备

1.创建一个CQ

2.请求在新的(第一个)完成事件上进行通知

第二阶段:完成处理程序

3.等待完成事件并确认

4.要求在下一次完成活动时发出通知

5.清空CQ

注意,可能会触发额外事件,而无需在CQ中具有相应的完成条目。如果将完成条目添加到步骤4和步骤5之间的CQ,然后在步骤5中清空(轮询)CQ,则会发生这种情况。

cq = ibv_create_cq(ctx, 1, ev_ctx, channel, 0);
if (!cq)
{
    fprintf(stderr, "Failed to create CQ\n");
    return 1;
}

/* Request notification before any completion can be created */
if (ibv_req_notify_cq(cq, 0))
{
    fprintf(stderr, "Couldn't request CQ notification\n");
    return 1;
}

.
.
.

/* Wait for the completion event */
if (ibv_get_cq_event(channel, &ev_cq, &ev_ctx))
{
    fprintf(stderr, "Failed to get cq_event\n");
    return 1;
}
/* Ack the event */
ibv_ack_cq_events(ev_cq, 1);

/* Request notification upon the next completion event */
if (ibv_req_notify_cq(ev_cq, 0))
{
    fprintf(stderr, "Couldn't request CQ notification\n");
    return 1;
}

/* Empty the CQ: poll all of the completions from the CQ (if any exist)
*/
do
{
    ne = ibv_poll_cq(cq, 1, &wc);
    if (ne < 0)
    {
        fprintf(stderr, "Failed to poll completions from the CQ\n");
        return 1;
    }
    /* there may be an extra event with no completion in the CQ */
    if (ne == 0)
        continue;

    if (wc.status != IBV_WC_SUCCESS)
    {
        fprintf(stderr, "Completion with status 0x%x was found\n",
                wc.status);
        return 1;
    }
}
while (ne);
The following code example demonstrates one possible way to work with 
completion events in non - blocking mode. It performs the following steps:
1. Set the completion event channel to be non - blocked
2. Poll the channel until there it has a completion event
3. Get the completion event and ack it

/* change the blocking mode of the completion channel */
flags = fcntl(channel->fd, F_GETFL);
rc = fcntl(channel->fd, F_SETFL, flags | O_NONBLOCK);
if (rc < 0)
{
    fprintf(stderr, "Failed to change file descriptor of completion event channel\n");
    return 1;
}
/*
 * poll the channel until it has an event and sleep ms_timeout
 * milliseconds between any iteration
 */
my_pollfd.fd      = channel->fd;
my_pollfd.events  = POLLIN;
my_pollfd.revents = 0;
do
{
    rc = poll(&my_pollfd, 1, ms_timeout);
}
while (rc == 0);
if (rc < 0)
{
    fprintf(stderr, "poll failed\n");
    return 1;
}
ev_cq = cq;
/* Wait for the completion event */
if (ibv_get_cq_event(channel, &ev_cq, &ev_ctx))
{
    fprintf(stderr, "Failed to get cq_event\n");
    return 1;
}
/* Ack the event */
ibv_ack_cq_events(ev_cq, 1);

ibv_poll_cq()

intibv_poll_cq(structibv_cq *cq,intnum_entries,structibv_wc *wc);

用于从 Completion Queue 中查询已完成的 Work Request。

所有的 Receive Request、signaled Send Request 和出错的 Send Request 在完成之后都会产生一个 Work Completion,Work Completion 就被放入完成队列(Completion Queue)中。

完成队列是 FIFO 的,ibv_poll_cq() 检查是否有 Work Completion 在完成队列中,如果是那么就将队首弹出,并返回那个 Work Completion 到 *wc 中。

ibv_wc 的结构如下,描述了一个 Work Completion 的情况。

structibv_wc {
uint64_twr_id;
enumibv_wc_status status;
enumibv_wc_opcode opcode;
uint32_tvendor_err;
uint32_tbyte_len;
uint32_timm_data;
uint32_tqp_num;
uint32_tsrc_qp;
intwc_flags;
uint16_tpkey_index;
uint16_tslid;
uint8_tsl;
uint8_tdlid_path_bits;
};
  • wr_id 由产生 Work Completion 的 Request 决定

  • status 是操作的状态,通常为 IBV_WC_SUCCESS 表示 Work Completion 成功完成,其他还有一些错误信息

  • opcode 表示当前的 Work Competition 是怎么产生的

  • bute_len 表示传输的字节数

参数说明:

Name Direction Description
*cq in 用于存放 Work Completion 的完成队列
num_entries in 表示最大从完成队列中读取多少个 Work Completion
*wc out 将读取到的 Work Completion 返回出来,如果有多个则返回的是数组

函数的返回值:成功则返回读取到的 Work Completion 数量,为 0 表示未读取到 Work Completion,可认为是完成队列为空,为负值则表示读取出错。

ibv_req_notify_cq()

intibv_req_notify_cq(structibv_cq *cq,intsolicited_only);

用于在完成队列中请求一个完成通知。

调用 ibv_req_notify_cq() 之后,下一个被加到 CQ 中的请求(发送请求或者接收请求)会被加上通知标记,当请求完成产生一个 Work Completion 之后就会产生通知,完成通知将被 ibv_get_cq_event() 函数读取出来。

传入的参数中:

  • solicited_only 为 0 时表示无论下一个加入 CQ 的请求是哪种类型的都会产生通知,否则只有 Solicited 或者出错的 Work Completion 才会产生通知

ibv_get_cq_event()

intibv_get_cq_event(structibv_comp_channel *channel,
structibv_cq **cq,void**cq_context);

用于等待某一 channel 中的下一个通知产生。

ibv_get_cq_event() 默认是一个阻塞函数,调用之后会将当前程序阻塞在这里,直到下一个通知事件产生。

当 ibv_get_cq_event() 收到完成事件的通知之后,需要调用 ibv_get_cq_event() 来确认事件。

典型用法:

  • Stage I:准备阶段

创建一个 CQ,并且将它与一个 Completion Event Channel 相关联;

用 ibv_req_notify_cq() 对一个 Completion Work 调用通知请求;

  • Stage II:运行中

等待事件产生;

产生之后处理事件,并且调用 ibv_ack_cq_events() 来确认事件;

对下一个 Completion Work 调用 ibv_req_notify_cq() 的通知请求;

参数说明:

Name Direction Description
*channel in 关联在 CQ 上的 Completion Event Channel
**cq out 从 Completion 事件中得到的一个 CQ
**cq_context out 从 Completion 事件中得到的 CQ context

返回值:为 0 表示成功,-1 在非阻塞模式下表示当前没有事件可读,阻塞模式则表示出错。

这种机制用于避免 CPU 反复读取 Work Completion ,若不采用事件的方法则只能通过不断地 ibv_poll_cq() 来轮询是否有事件完成

ibv_ack_cq_events()

voidibv_ack_cq_events(structibv_cq *cq,unsignedintnevents);

用于确认已完成 Completion events。

Errors

IBV_WC_WR_FLUSH_ERR(5/0x5)

Work Request Flushed Error

当 QP 的传送状态处于 Error 状态时,任何操作都会引发该错误。

IBV_WC_RNR_RETRY_EXC_ERR(13/0xd)

Receiver-Not-Ready Retry Error

当接收端没有准备好 Recv Request 时发送端产生了一个 Send Request 就会发生 RNR_RETRY 错误。

要求 ibv_post_recv() 必须在 ibv_post_send 之前完成,所以一种基本的思路就是一开始就 Post 一堆 Recv Request 到队列中去,然后检查当队列中的 Recv Request 少于一定数量时补充,保证不管发送端什么时候 Post Send Request 时,接收端都有足够的 Recv Request 来接收。

问题是如果发送端毫无顾忌地可以任意发送数据,尤其是在 RDMA_WRITE 方式,接收端这边会不会来不及取走数据,就被发送端传过来的新数据覆盖掉了?

或者设置 ibv_modify_qp() 参数中的 min_rnr_timer 以及 rnr_retry,前者是重试间隔时间,后者是重试次数,当 rnr_retry 为 7 时表示重试无限次。这种方法可用于重试直到接收端确认取走数据,并且准备好下一次的 Recv Request,然后发送端再进行发送。

当发送端发生 RNR_RETRY 错误时,重新调用 ibv_post_send() 是没用的,因为此时 QP 已经进入错误状态,接下来不管什么样的操作都会继续引发 IBV_WC_WR_FLUSH_ERR 错误。

除非另外使用一种流控制的方式,不然上面的两种解决方案都总会存在一定的局限性。

注意:本文来自Chenfan Blog。本站无法对本文内容的真实性、完整性、及时性、原创性提供任何保证,请您自行验证核实并承担相关的风险与后果!
CoLaBug.com遵循[CC BY-SA 4.0]分享并保持客观立场,本站不承担此类作品侵权行为的直接责任及连带责任。您有版权、意见、投诉等问题,请通过[eMail]联系我们处理,如需商业授权请联系原作者/原网站。

  • 发布在

在分布式应用中,用以太网组网往往成为性能的瓶颈,所以需要低时延大带宽,使用RDMA传输协议,能满足低时延要求。目前有两张硬件可以使用RDMA传输,一个是infiniband,一个是RDMA over Ethernet,由于IB的成本较高,所以RoCE成为一种趋势。

RoCE可以在以太网上运行RDMA协议,时延比普通以太网可以提升30%以上,也可以支持双协议栈,同时用TCP和RDMA,编程过程类似IB。

有两张建链方式,一种是通过RDMA_CM建链,一种是先通过TCP建链,通过tcp通道交换双方的设备信息,QP信息,简历RDMA链路,然后关闭tcp链路,第二种更常用。

RDMA编程流程

1)初始化RDMA设备

ibv_get_device_list()获取使用可以使用RDMA传输的设备个数,可以根据ibv_get_device_list结构中的dev_name找到需要使用的设备;

struct ibv_device **ibv_get_device_list(int *num_devices);

ibv_open_device()打开设备,获取设备句柄;

ibv_query_device()查询设备,获取设备属性

ibv_query_port()查询设备端口属性

如果类型为Ethernet,bv_query_gid()获取设备GID,用于交换双方信息使用

2)创建QP信息

ibv_alloc_pd()用于创建qp接口的参数

ibv_create_cq()创建CQ,一个CQ可以完成的CQE的个数,CQE与队列个数有关,队列多,CQE个数就设置多,否则设置少,一个CQ可以对应一个QP,也可以两个CQ对应一个QP。一个CQ包含发送和接收队列。

ibv_create_qp()创建QP。类似tcp的socket

3)注册MR信息

ibv_reg_mr()注册网卡内存信息,把操作系统物理内存注册到网卡

4)交换QP信息

ibv_modify_qp()交换双方QP信息,修改QP信息状态级

Client端:先创建QP,修改状态级reset到INIT,修改INIT到RTR,然后发送到server端,server端创建QP,修改状态机有INIT到RTR,然后发送到客户端,客户端修改状态机有RTR到RTS,发送到server端,server端修改状态机有RTR到RTS,这样rmda链路简建立成功。

5)发送和接收

ibv_post_recv()接收消息接口

ibv_post_send()发送消息接口

ibv_poll_cq()用于查询cq队列是否有事件产生,如果有调用recv接口接收。

(fellow0305)

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_42319496/article/details/119371326

智能推荐

分布式光纤传感器的全球与中国市场2022-2028年:技术、参与者、趋势、市场规模及占有率研究报告_预计2026年中国分布式传感器市场规模有多大-程序员宅基地

文章浏览阅读3.2k次。本文研究全球与中国市场分布式光纤传感器的发展现状及未来发展趋势,分别从生产和消费的角度分析分布式光纤传感器的主要生产地区、主要消费地区以及主要的生产商。重点分析全球与中国市场的主要厂商产品特点、产品规格、不同规格产品的价格、产量、产值及全球和中国市场主要生产商的市场份额。主要生产商包括:FISO TechnologiesBrugg KabelSensor HighwayOmnisensAFL GlobalQinetiQ GroupLockheed MartinOSENSA Innovati_预计2026年中国分布式传感器市场规模有多大

07_08 常用组合逻辑电路结构——为IC设计的延时估计铺垫_基4布斯算法代码-程序员宅基地

文章浏览阅读1.1k次,点赞2次,收藏12次。常用组合逻辑电路结构——为IC设计的延时估计铺垫学习目的:估计模块间的delay,确保写的代码的timing 综合能给到多少HZ,以满足需求!_基4布斯算法代码

OpenAI Manager助手(基于SpringBoot和Vue)_chatgpt网页版-程序员宅基地

文章浏览阅读3.3k次,点赞3次,收藏5次。OpenAI Manager助手(基于SpringBoot和Vue)_chatgpt网页版

关于美国计算机奥赛USACO,你想知道的都在这_usaco可以多次提交吗-程序员宅基地

文章浏览阅读2.2k次。USACO自1992年举办,到目前为止已经举办了27届,目的是为了帮助美国信息学国家队选拔IOI的队员,目前逐渐发展为全球热门的线上赛事,成为美国大学申请条件下,含金量相当高的官方竞赛。USACO的比赛成绩可以助力计算机专业留学,越来越多的学生进入了康奈尔,麻省理工,普林斯顿,哈佛和耶鲁等大学,这些同学的共同点是他们都参加了美国计算机科学竞赛(USACO),并且取得过非常好的成绩。适合参赛人群USACO适合国内在读学生有意向申请美国大学的或者想锻炼自己编程能力的同学,高三学生也可以参加12月的第_usaco可以多次提交吗

MySQL存储过程和自定义函数_mysql自定义函数和存储过程-程序员宅基地

文章浏览阅读394次。1.1 存储程序1.2 创建存储过程1.3 创建自定义函数1.3.1 示例1.4 自定义函数和存储过程的区别1.5 变量的使用1.6 定义条件和处理程序1.6.1 定义条件1.6.1.1 示例1.6.2 定义处理程序1.6.2.1 示例1.7 光标的使用1.7.1 声明光标1.7.2 打开光标1.7.3 使用光标1.7.4 关闭光标1.8 流程控制的使用1.8.1 IF语句1.8.2 CASE语句1.8.3 LOOP语句1.8.4 LEAVE语句1.8.5 ITERATE语句1.8.6 REPEAT语句。_mysql自定义函数和存储过程

半导体基础知识与PN结_本征半导体电流为0-程序员宅基地

文章浏览阅读188次。半导体二极管——集成电路最小组成单元。_本征半导体电流为0

随便推点

【Unity3d Shader】水面和岩浆效果_unity 岩浆shader-程序员宅基地

文章浏览阅读2.8k次,点赞3次,收藏18次。游戏水面特效实现方式太多。咱们这边介绍的是一最简单的UV动画(无顶点位移),整个mesh由4个顶点构成。实现了水面效果(左图),不动代码稍微修改下参数和贴图可以实现岩浆效果(右图)。有要思路是1,uv按时间去做正弦波移动2,在1的基础上加个凹凸图混合uv3,在1、2的基础上加个水流方向4,加上对雾效的支持,如没必要请自行删除雾效代码(把包含fog的几行代码删除)S..._unity 岩浆shader

广义线性模型——Logistic回归模型(1)_广义线性回归模型-程序员宅基地

文章浏览阅读5k次。广义线性模型是线性模型的扩展,它通过连接函数建立响应变量的数学期望值与线性组合的预测变量之间的关系。广义线性模型拟合的形式为:其中g(μY)是条件均值的函数(称为连接函数)。另外,你可放松Y为正态分布的假设,改为Y 服从指数分布族中的一种分布即可。设定好连接函数和概率分布后,便可以通过最大似然估计的多次迭代推导出各参数值。在大部分情况下,线性模型就可以通过一系列连续型或类别型预测变量来预测正态分布的响应变量的工作。但是,有时候我们要进行非正态因变量的分析,例如:(1)类别型.._广义线性回归模型

HTML+CSS大作业 环境网页设计与实现(垃圾分类) web前端开发技术 web课程设计 网页规划与设计_垃圾分类网页设计目标怎么写-程序员宅基地

文章浏览阅读69次。环境保护、 保护地球、 校园环保、垃圾分类、绿色家园、等网站的设计与制作。 总结了一些学生网页制作的经验:一般的网页需要融入以下知识点:div+css布局、浮动、定位、高级css、表格、表单及验证、js轮播图、音频 视频 Flash的应用、ul li、下拉导航栏、鼠标划过效果等知识点,网页的风格主题也很全面:如爱好、风景、校园、美食、动漫、游戏、咖啡、音乐、家乡、电影、名人、商城以及个人主页等主题,学生、新手可参考下方页面的布局和设计和HTML源码(有用点赞△) 一套A+的网_垃圾分类网页设计目标怎么写

C# .Net 发布后,把dll全部放在一个文件夹中,让软件目录更整洁_.net dll 全局目录-程序员宅基地

文章浏览阅读614次,点赞7次,收藏11次。之前找到一个修改 exe 中 DLL地址 的方法, 不太好使,虽然能正确启动, 但无法改变 exe 的工作目录,这就影响了.Net 中很多获取 exe 执行目录来拼接的地址 ( 相对路径 ),比如 wwwroot 和 代码中相对目录还有一些复制到目录的普通文件 等等,它们的地址都会指向原来 exe 的目录, 而不是自定义的 “lib” 目录,根本原因就是没有修改 exe 的工作目录这次来搞一个启动程序,把 .net 的所有东西都放在一个文件夹,在文件夹同级的目录制作一个 exe._.net dll 全局目录

BRIEF特征点描述算法_breif description calculation 特征点-程序员宅基地

文章浏览阅读1.5k次。本文为转载,原博客地址:http://blog.csdn.net/hujingshuang/article/details/46910259简介 BRIEF是2010年的一篇名为《BRIEF:Binary Robust Independent Elementary Features》的文章中提出,BRIEF是对已检测到的特征点进行描述,它是一种二进制编码的描述子,摈弃了利用区域灰度..._breif description calculation 特征点

房屋租赁管理系统的设计和实现,SpringBoot计算机毕业设计论文_基于spring boot的房屋租赁系统论文-程序员宅基地

文章浏览阅读4.1k次,点赞21次,收藏79次。本文是《基于SpringBoot的房屋租赁管理系统》的配套原创说明文档,可以给应届毕业生提供格式撰写参考,也可以给开发类似系统的朋友们提供功能业务设计思路。_基于spring boot的房屋租赁系统论文