【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

智能推荐

使用nginx解决浏览器跨域问题_nginx不停的xhr-程序员宅基地

文章浏览阅读1k次。通过使用ajax方法跨域请求是浏览器所不允许的,浏览器出于安全考虑是禁止的。警告信息如下:不过jQuery对跨域问题也有解决方案,使用jsonp的方式解决,方法如下:$.ajax({ async:false, url: 'http://www.mysite.com/demo.do', // 跨域URL ty..._nginx不停的xhr

在 Oracle 中配置 extproc 以访问 ST_Geometry-程序员宅基地

文章浏览阅读2k次。关于在 Oracle 中配置 extproc 以访问 ST_Geometry,也就是我们所说的 使用空间SQL 的方法,官方文档链接如下。http://desktop.arcgis.com/zh-cn/arcmap/latest/manage-data/gdbs-in-oracle/configure-oracle-extproc.htm其实简单总结一下,主要就分为以下几个步骤。..._extproc

Linux C++ gbk转为utf-8_linux c++ gbk->utf8-程序员宅基地

文章浏览阅读1.5w次。linux下没有上面的两个函数,需要使用函数 mbstowcs和wcstombsmbstowcs将多字节编码转换为宽字节编码wcstombs将宽字节编码转换为多字节编码这两个函数,转换过程中受到系统编码类型的影响,需要通过设置来设定转换前和转换后的编码类型。通过函数setlocale进行系统编码的设置。linux下输入命名locale -a查看系统支持的编码_linux c++ gbk->utf8

IMP-00009: 导出文件异常结束-程序员宅基地

文章浏览阅读750次。今天准备从生产库向测试库进行数据导入,结果在imp导入的时候遇到“ IMP-00009:导出文件异常结束” 错误,google一下,发现可能有如下原因导致imp的数据太大,没有写buffer和commit两个数据库字符集不同从低版本exp的dmp文件,向高版本imp导出的dmp文件出错传输dmp文件时,文件损坏解决办法:imp时指定..._imp-00009导出文件异常结束

python程序员需要深入掌握的技能_Python用数据说明程序员需要掌握的技能-程序员宅基地

文章浏览阅读143次。当下是一个大数据的时代,各个行业都离不开数据的支持。因此,网络爬虫就应运而生。网络爬虫当下最为火热的是Python,Python开发爬虫相对简单,而且功能库相当完善,力压众多开发语言。本次教程我们爬取前程无忧的招聘信息来分析Python程序员需要掌握那些编程技术。首先在谷歌浏览器打开前程无忧的首页,按F12打开浏览器的开发者工具。浏览器开发者工具是用于捕捉网站的请求信息,通过分析请求信息可以了解请..._初级python程序员能力要求

Spring @Service生成bean名称的规则(当类的名字是以两个或以上的大写字母开头的话,bean的名字会与类名保持一致)_@service beanname-程序员宅基地

文章浏览阅读7.6k次,点赞2次,收藏6次。@Service标注的bean,类名:ABDemoService查看源码后发现,原来是经过一个特殊处理:当类的名字是以两个或以上的大写字母开头的话,bean的名字会与类名保持一致public class AnnotationBeanNameGenerator implements BeanNameGenerator { private static final String C..._@service beanname

随便推点

二叉树的各种创建方法_二叉树的建立-程序员宅基地

文章浏览阅读6.9w次,点赞73次,收藏463次。1.前序创建#include&lt;stdio.h&gt;#include&lt;string.h&gt;#include&lt;stdlib.h&gt;#include&lt;malloc.h&gt;#include&lt;iostream&gt;#include&lt;stack&gt;#include&lt;queue&gt;using namespace std;typed_二叉树的建立

解决asp.net导出excel时中文文件名乱码_asp.net utf8 导出中文字符乱码-程序员宅基地

文章浏览阅读7.1k次。在Asp.net上使用Excel导出功能,如果文件名出现中文,便会以乱码视之。 解决方法: fileName = HttpUtility.UrlEncode(fileName, System.Text.Encoding.UTF8);_asp.net utf8 导出中文字符乱码

笔记-编译原理-实验一-词法分析器设计_对pl/0作以下修改扩充。增加单词-程序员宅基地

文章浏览阅读2.1k次,点赞4次,收藏23次。第一次实验 词法分析实验报告设计思想词法分析的主要任务是根据文法的词汇表以及对应约定的编码进行一定的识别,找出文件中所有的合法的单词,并给出一定的信息作为最后的结果,用于后续语法分析程序的使用;本实验针对 PL/0 语言 的文法、词汇表编写一个词法分析程序,对于每个单词根据词汇表输出: (单词种类, 单词的值) 二元对。词汇表:种别编码单词符号助记符0beginb..._对pl/0作以下修改扩充。增加单词

android adb shell 权限,android adb shell权限被拒绝-程序员宅基地

文章浏览阅读773次。我在使用adb.exe时遇到了麻烦.我想使用与bash相同的adb.exe shell提示符,所以我决定更改默认的bash二进制文件(当然二进制文件是交叉编译的,一切都很完美)更改bash二进制文件遵循以下顺序> adb remount> adb push bash / system / bin /> adb shell> cd / system / bin> chm..._adb shell mv 权限

投影仪-相机标定_相机-投影仪标定-程序员宅基地

文章浏览阅读6.8k次,点赞12次,收藏125次。1. 单目相机标定引言相机标定已经研究多年,标定的算法可以分为基于摄影测量的标定和自标定。其中,应用最为广泛的还是张正友标定法。这是一种简单灵活、高鲁棒性、低成本的相机标定算法。仅需要一台相机和一块平面标定板构建相机标定系统,在标定过程中,相机拍摄多个角度下(至少两个角度,推荐10~20个角度)的标定板图像(相机和标定板都可以移动),即可对相机的内外参数进行标定。下面介绍张氏标定法(以下也这么称呼)的原理。原理相机模型和单应矩阵相机标定,就是对相机的内外参数进行计算的过程,从而得到物体到图像的投影_相机-投影仪标定

Wayland架构、渲染、硬件支持-程序员宅基地

文章浏览阅读2.2k次。文章目录Wayland 架构Wayland 渲染Wayland的 硬件支持简 述: 翻译一篇关于和 wayland 有关的技术文章, 其英文标题为Wayland Architecture .Wayland 架构若是想要更好的理解 Wayland 架构及其与 X (X11 or X Window System) 结构;一种很好的方法是将事件从输入设备就开始跟踪, 查看期间所有的屏幕上出现的变化。这就是我们现在对 X 的理解。 内核是从一个输入设备中获取一个事件,并通过 evdev 输入_wayland

推荐文章

热门文章

相关标签