【RDMA】RDMA编程入门--编辑中

2023-11-19

目录

一、前言

二、基本概念

1、队列和队列成员

2、传输模式

简介

单边双边传输流程简述

3、编程接口(verbs API)

三、编程示例

工作大致流程说明

四、编程代码实例

5.RDMA编程概述

5.1 传输操作

5.2传输模式

5.3相关概念

5.4典型实例

6.RDMA通信过程

6.1单向通信-读Read

6.2 单向通信-写Write

6.3双向通信-Send\Recv

四、RDMA通信和ceph AsyncMessenger的关系


【RDMA】RDMA 学习资料总目录_bandaoyu的博客-CSDN博客SavirRDMA 分享1. RDMA概述https://blog.csdn.net/bandaoyu/article/details/112859853https://zhuanlan.zhihu.com/p/1388747382. 比较基于Socket与RDMA的通信https://blog.csdn.net/bandaoyu/article/details/1128613993. RDMA基本元素和编程基础https://blog.csdn.net/bandaoyu/article/de.https://blog.csdn.net/bandaoyu/article/details/120485737

作者:bandaoyu 本文随时更新,地址:https://blog.csdn.net/bandaoyu/article/details/125681856

一、前言

首先应该先了解RDMA:https://blog.csdn.net/bandaoyu/article/details/112859853
RDMA 学习资料总目录:https://blog.csdn.net/bandaoyu/article/details/120485737

本文所讲述的主要是IBoE的RDMA编程,主要是ROCE、iWrap,与IB可能略有不同。


二、基本概念

1、队列和队列成员

关键词:点对点通信、QP(SQ+RQ)、CQ、*QE


RDMA提供了基于消息队列的点对点通信,每个应用都可以直接获取自己的消息,无需OS和协议栈的介入。

队列:QP (Queue Pairs) 和CQ

RDMA提供了基于消息队列的点对点通信,当应用需要通信时,就会创建一条Channel连接,每条Channel的两端各有一对队列(Send Queue(SQ)和Receive Queue(RQ)),合称Queue Pairs(QP),除此之外,两端还需要一个 完成队列-Complete Queue(CQ),CQ用来存放 发送和接收请求处理完成的通知。

队列成员:SQE、RQE、CQE等*QE

队列里面的成员叫xxE,SQ里面的成员叫SQE,RQ里面的叫RQE,CQ里面的就是CQE……

而发送队列SQ和接收队列RQ,统称工作队列WQ (work Queue),所以里面单元又叫WQE;

SR(发送请求)和RR(接收请求)都是WR=工作请求;

每当网卡发送or接收完成一个请求,会在CQ产生一个CQE,知会用户消息已经被处理完。

总结

SQ:发送队列,SQ中的单元叫SQE;

RQ:接收队列,RQ中的单元叫RQE;

SQ和RQ都是WQ,就像男人和女人都叫人,WQ中的单元叫WQE;

SR(发送请求)和RR(接收请求)都是WR=工作请求;

QP=SQ+RQ

CQ:完成队列,CQ中的单元叫CQE;

2、传输模式

简介

RDMA 单边和双边两种传输模式。

SEND/RECEIVE:

是双边操作,每一次数据传输都需要双边参与。

READ和WRITE :

是单边操作,除了第一次握手(获得对端的内存地址),后续的数据传输,单端直接DMA读写对端的内存。

第一次握手获得对端接收数据的内存地址后,本端明确信息的源和目的地址,数据的读或存都通过远端的DMA在RNIC与应用buffer之间完成,再由远端RNIC封装成消息返回到本端。对端的CPU毫无知觉。(所以WRITE完成后,一般要再发一个消息去通知对端:我已经完成一次操作了,去查看CQ队列吧。然后对端就会去读CQ队列,拿出CQE解析或者本地把数据写到哪里了,它再去处理数据)

在实际中,SEND/RECEIVE多用于连接控制类报文,而数据报文多是通过READ/WRITE来完成的。

单边双边传输流程简述

双边


对于双边操作为例,A向B发送数据的流程如下:

首先,A和B都要创建并初始化好各自的QP,CQ

A 向SQ中放入一个发送请求(SQE),SQE描述指向一个等待被发送的数据的buffer地址 S_addr;

对于B,要提前向RQ中放入N个接收请求(RQE),RQE描述指向一块用于存储数据的buffer地址R_addr。

(SQ和RQ都是WQ,就像男人和女人都叫人,WQ中的单元叫WQE;所以SQE也称WQE)
(SQ和RQ都是WQ,就像男人和女人都叫人,WQ中的单元叫WQE;所以RQE、SQE也称WQE)

A的RNIC异步从A的SQ中读取SQE(WQE),读到刚才放入的SQ,解析到这是一个SEND消息,从S_addr中将数据直接DMA到B。数据流到达B的RNIC后,B从RQ中消耗掉一个RQE(WQE),从RQE中解析出存数据的地址R_addr,并把数据直接存储R_addr。

AB通信完成后,A的CQ中会产生一个完成消息CQE表示发送完成。与此同时,B的CQ中也会产生一个完成CQE消息表示接收完成。即每个WQ中WQE的处理完成都会产生一个CQE。

双边操作与传统网络的底层buffer pool类似,收发双方的参与过程并无差别,区别在零拷贝、kernel bypass。

对于RDMA,这是一种复杂的消息传输模式,多用于传输短的控制消息。

单边

以WRITE为例:

首先,A和B都要创建并初始化好各自的QP,CQ。

双方握手交换信息。(A的buffer地址VA告知了B,B的buffer地址VB告知了A)

A把内存地址VA,key封装到专用的报文传送到B,这相当于A把数据buffer的操作权交给了B。

B把内存地址VB,key封装到专用的报文传送到A,这相当于B把数据buffer的操作权交给了A。

A要向B“发送”数据:

A 向SQ中放入一个发送请求(SQE),SQE描述指向一个等待被发送的数据的buffer地址 S_addr;

A的RNIC异步从A的SQ中读取SQE(WQE),读到刚才放入的SQ,解析到这是一个WRITE消息,从S_addr中将数据直接DMA到B的内存地址VB(范围内的某个地址VB+xx)。数据流到达B的RNIC后,B从A的SQE中解析出存数据的地址VB+xx,并把数据直接存储VB+xx。

AB通信完成后,A的CQ中会产生一个完成消息CQE表示发送完成。

与此同时,B的CQ中是否也要产生CQE,取决于A发过来的WR中设定的条件(又是为了减少中断。A可能想发多个消息合并为一个CQ)。

这个过程A、B两端不需要任何软件(CPU\内核)参与,只有两个网卡“悄咪咪”的完成了将A的数据存储到B的VB虚拟地址。

3、编程接口(verbs API)

Verbs API

由OpenFabrics推动实现的一组RDMA应用编程接口(API)。地位相当于以太网编程的socket接口。传统以太网的用户,基于Socket API来编写应用程序;而RDMA的用户,基于Verbs API来编写应用程序。

Verbs API是RDMA最基本的软件接口,业界的RDMA应用,要么直接基于这组API编写,要么基于在Verbs API上又封装了一层接口的各种中间件编写。(如rdma_cm)

Verbs api 分为用户态Verbs接口和内核态Verbs接口,分别用于用户态和内核态的RDMA应用。 对于Linux系统来说,由rdma-core和内核中的RDMA子系统(如intel的irdma)提供,所以我们可以看到服务器安装intel 的RDMA网卡后,要使用RDMA网卡,需要安装rdma-core和irdma。

原文链接:https://blog.csdn.net/bandaoyu/article/details/113125244

rdma_cm

对于rdma编程,目前主流实现是利用rdma_cm来建立连接,然后利用verbs来传输数据。

rdma_cm和ibverbs分别会创建一个fd,这两个fd的分工不同。rdma_cm fd主要用于通知建连相关的事件,verbs fd则主要通知有新的cqe发生。当直接对rdma_cm fd进行poll/epoll监听时,此时只能监听到POLLIN事件,这意味着有rdma_cm事件发生。当直接对verbs fd进行poll/epoll监听时,同样只能监听到POLLIN事件,这意味着有新的cqe。

《rdma_cm和verbs的区别》原文链接:https://blog.csdn.net/bandaoyu/article/details/115668933

三、编程示例

工作大致流程说明

RDMA有两种通信模式 Read/Write和Send/Receive,此处以Write为例:

1、编程步骤1准备工作和资源初始化:

  • 获取设备列表

 ibv_get_device_list -->rocep216s0f0、rocep216s0f1 (RDMA网卡有多个网口)

  • 打开要请求的设备

 ibv_open_device(rocep216s0f0)

  • 查询端口

ibv_query_port(res.ib_ctx, 1, &res.port_attr)

  • 分配保护域以及您的资源   

res.pd = ibv_alloc_pd(res.ib_ctx)

  • 创建 CQ
res.cq = ibv_create_cq(res.ib_ctx, cq_size, NULL, NULL, 0);
  • 注册一个内存区域  (注册过之后产生key,把key交给对端,对端才能DMA该区域)
res.mr = ibv_reg_mr(res.pd, res.buf, size, mr_flags);
  • 创建QP
res.qp = ibv_create_qp(res.pd, &qp_init_attr);
  • 交换控制信息(交换QP的ID,各自的内存地址等)

可以通过 Socket 或者 RDMA_CM API 来交换控制信息,这里演示的是使用 Socket 交换信息。因为RDMA 是根据点到点的通信,RDMA没有IP的概念,只有QP的ID的概念,所以要通过 Socket 或者 RDMA_CM API 来交换控制信息告诉对方,本地的QP的ID---->LID 。

(LID的概念来自于IB,可以阅读本地了解LID的概念:http://t.csdn.cn/HBuXG

  • 转换 QP 状态

QP 创建后处于RESET重置状态,要经过切换到RTS:Ready To Send 状态后,才能正常工作,可以把这一步理解为QP的初始化。

  • RESET:重置状态,QP 刚创建时即处于 RESET 状态,此时不能在 QP 中添加发送请求或接收请求,所有入站消息都被默默丢弃
  • INIT:已初始化状态,此时不能添加发送请求,可以添加接收请求,但是请求不会被处理,所有入站消息都被默默丢弃。最好在QP处于这种状态时将接收请求加入到其中,再切换到 RTR 状态。这样可以避免发送消息的远程 QP 在需要使用接收请求时没有接收请求可用的情况发生。
  • RTR:Ready To Receive 状态,此时不能添加发送请求,但是可以添加并且处理接收请求,所有入站信息都将得到处理。在这种状态下收到的第一条消息,将触发异步事件「通信已建立」
  • RTS:Ready To Send 状态,此时可以添加和处理发送和接收请求,所有入站信息都将得到处理

2、编程步骤1 创建请求和执行发送和处理

发送端:

应用程序创建WR请求,既填充ibv_send_wr结构体。
调用ibv_post_send将WR放入QP的SQ中(成为WQE)

网卡做的事情:
RDMA网卡从QP的SQ中取出WQE,把WQE指定的数据DMA到WQE指定的对端的内存地址中。
RDMA网卡DMA完成后产生一个WC(work complete),放入CQ(成为CQE)

应用程序polling CQ(或者event通知),看到有CQE,就知道数据传输(出去)完成。

poll_result = ibv_poll_cq(res->cq, 1, &wc);

应用程序对poll_result  进行处理(解析处理,看发送成功or失败)


接收端:

 网卡做的事情:

RDMA网卡接收到对端RDMA传来的数据和WQE,根据WQE中指定的地址,把数据放到本地的内存上,接收完成后。生成一个WC,放到CQ中。

应用程序polling CQ(或者event通知),看到有CQE,就知道数据传输(到来)完成。

poll_result = ibv_poll_cq(res->cq, 1, &wc);

应用程序对poll_result  进行处理(读出来,解析,知道对端DMA过来的数据放在哪个内存地址,然后去读取)

四、编程代码实例

链接:《RDMA SEND/WRITE编程实例(IBV Verbs )》http://t.csdn.cn/4UwMw

          《RDMA 编程实例(rdma_cm API) 》http://t.csdn.cn/WPff3

5.RDMA编程概述

(摘自:RDMA概念,特点,协议,通信流程_https://blog.csdn.net/u011458874/article/details/121602188)

5.1 传输操作

RDMA有两种基本操作,包括Memory verbsMessaging verbs

  • Memory verbs:包括read、write和atomic操作。
    • RDMA Read:从远程主机读取部分内存。调用者指定远程虚拟地址,像本地内存地址一样用来拷贝。在执行 RDMA 读操作之前,远程主机必须提供适当的权限来访问它的内存。一旦权限设置完成, RDMA 读操作就可以在对远程主机没有任何通知的条件下执行。不管是 RDMA 读还是 RDMA 写,远程主机都不会意识到操作正在执行 (除了权限和相关资源的准备操作)。
    • RDMA Write:与RDMA Read类似,只是数据写到远端主机中。RDMA写操作在执行时不通知远程主机。然而带即时数的RDMA写操作会将即时数通知给远程主机。
    • RDMA Atomic:包括原子取、原子加、原子比较和原子交换,属于RDMA原子操作的扩展。
       
  • Messaging verbs:包括send和receive操作。
    • RDMA Send:发送操作允许你把数据发送到远程 QP 的接收队列里。接收端必须已经事先注册好了用来接收数据的缓冲 区。发送者无法控制数据在远程主机中的放置位置。可选择是否使用即时数,一个4位的即时数可以和数据缓冲一起被传送。这个即时数发送到接收端是作为接收的通知,不包含在数据缓冲之中。
    • RDMA Receive:这是与发送操作相对应的操作。接收主机被告知接收到数据缓冲,还可能附带一个即时数。接收端应用 程序负责接收缓冲区的维护和发布。

5.2传输模式

按照连接和可靠两个标准,可以划分出下图四种不同的传输模式:
在这里插入图片描述

  • 可靠连接(RC)
    • 一个QP只和另一个QP相连,消息通过一个QP的发送队列可靠地传输到另一个QP的接收队列。数据包按序交付,RC连接很类似于TCP连接。
  • 不可靠连接(UC)
    • 一个QP只和另一个QP相连,连接是不可靠的,所以数据包可能有丢失。传输层出错的消息不会进行重传,错误处理必须由高层的协议来进行。
  • 不可靠数据报(UD)
    • 一个 QP 可以和其它任意的 UD QP 进行数据传输和单包数据的接收。不保证按序性和交付性。交付的数据包可能被接收端丢弃。支持多播消息(一对多)。UD连接很类似于UDP连接。
        每种模式中可用的操作如下表所示:
      在这里插入图片描述

5.3相关概念

在RDMA通信的过程中,有诸多需要理解的概念,例如QP队列对、内存注册等。下面我们来着重介绍一下RDMA通信涉及到了操作和概念。

  • 1.发送请求(SR)

    • SR定义了数据的发送量、从哪里、发送方式、是否通过 RDMA、到哪里。 结构 ibv_send_wr 用来描述 SR。
  • 2.接收请求(RR)

    • RR 定义用来放置通过RDMA 操作接收到的数据的缓冲区。如没有定义缓冲区,并且有个传输者尝试执行一个发送操作或者一个带即时数的 RDMA写操作,那么接收者将会发出接收未就绪的错误(RNR)。结构 ibv_recv_wr用来描述 RR。
  • 3.完成队列(CQ)

    • (CQ)完成队列包含了发送到工作队列(WQ)中已完成的工作请求(WR)。每次完成表示一个特定的 WR 执行完毕(包括成功完成的 WR 和不成功完成的 WR)。完成队列是一个用来告知应用程序已经结束的工作请求的信息(状态、操作码、大小、来源)的机制。
    • CQ有n个完成队列实体(CQE)。CQE的数量在CQ创建时指定。当一个CQE被轮询到,他就从CQ中被删除。CQ是一个CQE的先进先出(FIFO)队列。CQ能服务于发送队列、接收队列或者同时服务于这两种队列。多个不同QP中的工作请求(WQ)可联系到同一个CQ上。结构ibv_cq用来描述CQ。
  • 4.内存注册(MR)

    • 内存注册机制允许应用程序申请一些连续的虚拟内存或者连续的物理内存空间,将这些内存空间提供给网络适配器作为虚拟的连续缓冲区,缓冲区使用虚拟地址。内存注册进程锁定了内存页。为了防止页被替换出去,同时保持物理和虚拟内存的映射。在注册期间,操作系统检查被注册块的许可。注册进程将虚拟地址与物理地址的映射表写入网络适配器。在注册内存时,对应内存区域的权限会被设定。权限包括本地写、远程读、远程写、原子操作、绑定。
    • 每个内存注册(MR)有一个远程的和一个本地的标志(r_key,l_key)。本地标志被本地的 HCA 用来访问本地内存,例如在接收数据操作的期间。远程标志提供给远程 HCA 用来在 RDMA 操作期间允许远程进程访问本地的系统内存。同一内存缓冲区可以被多次注册(甚至设置不同的操作权限),并且每次注册都会生成不同的标志。结构ibv_mr用来描述内存注册。
  • 5.内存窗口(MW)

    • 内存窗口使应用程序对来自远程对本地的内存访问有更灵活的控制。内存窗口作用于以下场景:1)动态地授予和回收已注册缓冲区的远程访问权限,这种方式相较于将缓冲区取消注册、再注册或者重注册,有更低的性能损耗代价。2)想为不同的远程代理授予不同的远程访问方式,或者在一个已注册的缓冲区中不同范围授予哪些权限。内存窗口和内存注册之间的关联操作叫做绑定。不同的MW可以做用于同一个MR,即使有不同的访问权限。
  • 6.地址向量(Address Vector)

    • 地址向量用来描述本地节点到远程节点的路由。在QP的每个UC/RC中,都有一个地址向量存在于QP的上下文中。在UD的QP中,每个提交的发送请求(SR)中都应该定义地址向量。结构ibv_ah用来描述地址向量。
  • 7.全局路由头部(GRH)

    • GRH用于子网之间的路由。当用到RoCE时,GRH用于子网内部的路由,并且是强制使用的,强制使用GRH是为了保证应用程序即支持IB又支持RoCE。当全局路由用在给予UD的QP时,在接受缓冲区的前40自己会包含有一个GRH。这个区域撞门存储全局路由信息,为了回应接收到的数据包,会产生一个合适的地址向量。如果向量用在UD中,接收请求RR应该总是有额外的40字节用来GRH。结构ibv_grh用来描述GRH。
  • 8.保护域(PD)

    • 保护域是一种集合,它的内部元素只能与集合内部的其它元素相互作用。这些元素可以是AH、QP、MR、和SRQ。保护域用于QP与内存注册和内存窗口相关联,这是一种授权和管理网络适配器对主机系统内存的访问。PD也用于将给予不可靠数据报(UD)的QP关联到地址处理(AH),这是一种对UD目的端的访问控制。

5.4典型实例

一个典型的应用程序结构如下:

  1. 获取设备列表:首先必须检查得到本机可用的IB设备列表。列表中的每个设备都包含一个名字和GUID。
  2. 打开要请求的设备:遍历设备列表,通过设备的GUID或者名字选择并打开它。
  3. 查询设备的工作能力:设备的工作能力能使用户了解已打开设备支持的特性和能力。
  4. 分配保护域以及您的资源:保护域(PD)允许用户限制哪些组件只能相互交互。这个组件可以是AH、QP、MR、MW、和SRQ。
  5. 注册一个内存区域:VPI仅适用于已注册的内存。进程的虚拟空间中任何有效的内存缓冲区都可以进行注册。在注册过程中,用户设置内存权限并接收本地和远程秘钥(lkey,rkey),稍后将使用这些秘钥来引用此内存缓冲区。
  6. 创建完成队列:一个CQ包含完成的工作请求(WR)。每个WR将生成放置在CQ中的完成队列实体CQE。CQE将制定WR是否成功完成。
  7. 创建队列对(QP):创建QP还将创建关联的发送队列和接收队列 。
  8. 提出QP:创建的QP仍无法使用,直到它转换为几个状态,最终进入Ready To Send(RTS)。者提供了QP用于 发送/接收数据所需的信息。
  9. 发布工作请求并poll完成:使用创建的QP进行通信。
  10. 清理:按照创建前述对象的相反顺序销毁对象:删除QP,删除CQ,取消注册MR,接触分配PD,关闭设备。

6.RDMA通信过程

为了执行 RDMA 操作,首选需要建立与远程主机的连接和适当的认证。实现这些的机制是队列对(QP) 。与标准的 IP 协议栈类似,一个 QP 大概等同于一个接字(socket)。 QP 需要在连接两端进行初始化。 连接管理器(CM)用来在 QP 建立之前进行 QP 信息的交换。一旦一个 QP 建立起来, verbs API 就可以用来执行 RDMA 读/写和原子操作。与套接字的读/写类似的连续收/发操作也能执行。RDMA的操作过程大致如下:

  • 当一个应用程执行度或者写请求时,不执行任何数据复制,再不需要任何内核参与的条件下,RDMA请求从用户空间中的应用发送到本地NIC网卡。
  • NIC读取缓冲区的内容,并通过网络传输到远程NIC。
  • 在网络上传输的RDMA信息包括虚拟地址内存钥匙数据本身。请求既可以完全在用户空间中处理,又或者在应用一直睡眠到请求完成时的情况下通过系统级中断处理。RDMA操作使应用可以从一个远程应用的内存中读取数据或向这个内存中写数据。
  • 目标NIC确认内存钥匙,直接将数据写入应用缓存中,用于操作的远程虚拟内存地址包含在RDMA信息中。

在RDMA操作中,Read/Write是单边操作,秩序本地端明确信息的源和目的地址,远端应用不必感知此次通信,数据的读或写都通过RDMA在RNIC与应用Buffer之间完成,再由远端RNIC封装成消息返回到本地端。Send/Receive是双边操作,即必须要远端的应用感知参与才能完成收发,在实际中,Send/Receive多用于连接控制类报文,而数据报文是通过Read/Write来完成的。

6.1单向通信-读Read

在这里插入图片描述

  • 首先A、B建立连接,QP已经创建并初始化。
  • 数据被存档在B的buffer,地址为VB,注意VB是提前注册到B的RNIC,并且它是一个Memory Region,并拿到返回的local key,相当于RDMA操作这块buffer的权限。
  • B把数据地址VB,key封装到专用报文传送到A,这相当于B把数据buffer的操作权交给了A。同时B在他的WQ中注册一个WR,用于接收数据传输的A返回的状态。
  • A在收到B发送过来的数据地址VB和R_key之后,RNIC会把它们连同本地存储数据的地址VA封装到Read请求中,将这个请求消息发送到B,这个过程A、B两端不需任何软件参与,就可以将B中的数据存储到A的VA虚拟地址。
  • A在存储完成后,向B返回数据传输的状态信息。

6.2 单向通信-写Write

在这里插入图片描述

  • 首先A、B建立连接,QP已经创建并初始化。
  • 数据远端的目标存储空间buffer的地址为VB,注意VB是提前注册到B的RNIC,并且它是一个Memory Region,并拿到返回的local key,相当于RDMA操作这块buffer的权限。
  • B把数据地址VB,key封装到专用报文传送到A,这相当于B把数据buffer的操作权交给了A。同时B在他的WQ中注册一个WR,用于接收数据传输的A返回的状态。
  • A在收到B发送过来的数据地址VB和R_key之后,RNIC会把它们连同本地存储数据的地址VA封装到Write请求中,这个过程A、B两端不需任何软件参与,就可以将A中的数据存储到B的VB虚拟地址。
  • A在发送数据完成后,向B返回数据传输的状态信息。

6.3双向通信-Send\Recv

在这里插入图片描述

  • 首先,A和B都要创建并初始化好各自的QP、CQ。
  • A、B分别想自己的WQ中注册WQE,对于A来说,WQ=SQ,WQE描述指向一个等待被发送的数据;对于B,WQ=RQ,WQE描述指向一块用于存储数据的Buffer。
  • A的RNIC异步调度轮到A的WQE,解析到这是一个Send消息,从Buffer中直接向B发送数据。数据流到达B的RNIC后,B的WQE被消耗,并把数据直接存储到WQE指向的存储位置。
  • AB通信完成后,A的CQ中会产生一个完成消息CQE表示发送完成。同时,B的CQ中会产生一个完成消息CQE表示接收完成。每个WQ中的WQE的处理完成都会产生一个CQE。

四、RDMA通信和ceph AsyncMessenger的关系

Messenger::send_message(Message *m, dest)

AsyncMessenger::send_message(Message *m, dest)

--|AsyncMessenger::_send_message(Message *m, dest)

----|AsyncMessenger::submit_message(Message *m,conn,dest,...)

------|AsyncConnection::send_message(Message *m)

--------|out_q[priority].emplace_back(std::move(bl),m) #放入队列

//回调操作(write_handler= new C_handle_write(this))放入event中心

--------|EventCenter::dispatch_event_external(write_handler)

----------|external_event.push_back(write_handler)

//wakeup process_events 线程执行

----------|wakeup()

|

| w->center.process_events //执行process_events,取出前面放入event中心的event并处理

        | cb = event->read_cb; cb->do_request()

        |--|C_handle_write

//process_events处理(do_request)我们的让入的event,实际就是执行C_handle_write

| --|write_handler = new C_handle_write(this)

//所以就是执行:AsyncConnection::handle_write

C_handle_write::do_request(int fd)

--| AsyncConnection::handle_write()

----|bufferlist data;m=_get_next_outgoing(&data); #out_q

----|AsyncConnection::write_message(m,data,more)

------|AsyncConnection::outcoming_bl <--bl ------|AsyncConnection::_try_send(bool more)

--------|AsyncConnection::connectedSocket cs->send(outcoming_bl,more)

----------|connectedSocket::_csi->send(outcoming_bl,more)

/*_csi是std::unique_ptr<ConnectedSocketImpl> _csi;

_csi->send是_csi->sendConnectedSocketImpl:: virtual ssizet_t send(bl,more)是虚函数,RDMA模式的时候,其实现就是RDMAConnectedSocketImpl::send *(posix就调用socket的send,RDMA就调用RDMA的send)*/

//所以下面就是ceph AsyncMessenger网络模块的RDMA入口

----------|RDMAConnectedSocketImpl::send(outcoming_bl,more)#<---------RDMA send 入口

=====================================草稿================================

 基本概念

原理

RDMA提供了基于消息队列的点对点通信,每个应用都可以直接获取自己的消息,无需OS和协议栈的介入。

消息服务建立在通信双方本端和远端应用之间创建的channel-IO连接之上。

当应用需要通信时,就会创建一条Channel连接,每条Channel的首尾端点是两对Queue Pairs(QP),每对QP由Send Queue(SQ)和Receive Queue(RQ)构成,这些队列中管理着各种类型的消息。

QP会被映射到应用的虚拟地址空间,使得应用直接通过它访问RNIC。除了QP描述的两种基本队列之外,RDMA还提供一种队列-Complete Queue(CQ),CQ用来知会用户WQ上的消息已经被处理完。

RDMA提供了一套software transport interface,方便用户创建传输请求-Work Request(WR),WR中描述了应用希望传输到Channel对端的消息内容。

WR通知给QP中的某个队列-Work Queue(WQ)。

在WQ中,用户的WR被转化为Work Queue Ellement(WQE)的格式,等待RNIC的异步调度解析,并从WQE指向的buffer中拿到真正的消息发送到Channel对端。

传输

RDMA 的send/receive和read/write传输

传输

RDMA共有三种底层数据传输模式。

SEND/RECEIVE是双边操作,即必须要远端的应用感知参与才能完成收发。

READ和WRITE是单边操作,只需要本端明确信息的源和目的地址,远端应用不必感知此次通信,数据的读或存都通过远端的DMA在RNIC与应用buffer之间完成,再由远端RNIC封装成消息返回到本端。

在实际中,SEND/RECEIVE多用于连接控制类报文,而数据报文多是通过READ/WRITE来完成的。

双边

对于双边操作为例,A向B发送数据的流程如下:

  1. 首先,A和B都要创建并初始化好各自的QP,CQ

  2. A和B分别向自己的WQ中注册WQE,对于A,WQ=SQ,WQE描述指向一个等到被发送的数据;对于B,WQ=RQ,WQE描述指向一块用于存储数据的buffer。

  3. A的RNIC异步调度轮到A的WQE,解析到这是一个SEND消息,从buffer中直接向B发出数据。数据流到达B的RNIC后,B的WQE被消耗,并把数据直接存储到WQE指向的存储位置。

  4. AB通信完成后,A的CQ中会产生一个完成消息CQE表示发送完成。与此同时,B的CQ中也会产生一个完成消息表示接收完成。每个WQ中WQE的处理完成都会产生一个CQE。

双边操作与传统网络的底层buffer pool类似,收发双方的参与过程并无差别,区别在零拷贝、kernel bypass,实际上传统网络中一些高级的网络SOC已经实现类似功能。

对于RDMA,这是一种复杂的消息传输模式,多用于传输短的控制消息。

单边

对于单边操作,以存储网络环境下的存储为例(A作为文件系统,B作为存储介质):

  1. 首先A、B建立连接,QP已经创建并且初始化。

  2. 数据被存档在A的buffer地址VA,注意VA应该提前注册到A的RNIC,并拿到返回的local key,相当于RDMA操作这块buffer的权限。

  3. A把数据地址VA,key封装到专用的报文传送到B,这相当于A把数据buffer的操作权交给了B。同时A在它的WQ中注册进一个WR,以用于接收数据传输的B返回的状态。

  4. B在收到A的送过来的数据VA和R_key后,RNIC会把它们连同存储地址VB到封装RDMA READ,这个过程A、B两端不需要任何软件参与,就可以将A的数据存储到B的VB虚拟地址。

  5. B在存储完成后,会向A返回整个数据传输的状态信息。

单边操作传输方式是RDMA与传统网络传输的最大不同,提供直接访问远程的虚拟地址,无须远程应用的参与,这种方式适用于批量数据传输。

Verbs 的身世

RDMAC(RDMA Consortium)和IBTA(InfiniBand Trade Association)主导了RDMA,RDMAC是IETF的一个补充,它主要定义的是iWRAP和iSER,IBTA是infiniband的全部标准制定者,并补充了RoCE v1 v2的标准化。

应用和RNIC之间的传输接口层(software transport interface)被称为Verbs。

IBTA解释了RDMA传输过程中应具备的特性行为,而并没有规定Verbs的具体接口和数据结构原型。

这部分工作由另一个组织OFA(Open Fabric Alliance)来完成,OFA提供了RDMA传输的一系列Verbs API。

OFA开发出了OFED(Open Fabric Enterprise Distribution)协议栈,支持多种RDMA传输层协议。

OFED中除了提供向下与RNIC基本的队列消息服务,向上还提供了ULP(Upper Layer Protocols),通过ULPs,上层应用不需要直接到Verbs API对接,而是借助于ULP与应用对接,常见的应用不需要做修改,就可以跑在RDMA传输层上。

队列和队列成员

QP (Queue Pairs)

RDMA提供了基于消息队列的点对点通信,当应用需要通信时,就会创建一条Channel连接,每条Channel的两端是 各有一对Queue Pairs(QP)。每对QP由Send Queue(SQ)和Receive Queue(RQ)构成,除了QP之外,通信还需一种队列:完成队列--Complete Queue(CQ)。

队列里面的成员叫xxE,SQ里面的成员叫SQE,RQ里面的叫RQE,CQ里面的就是CQE……

而发送队列SQ和接收队列RQ,统称工作队列WQ (work Queue),所以里面单元又叫WQE;

SR(发送请求)和RR(接收请求)都是WR=工作请求;

每当网卡发送or接收完成一个请求,会在CQ产生一个CQE,知会用户消息已经被处理完。

总结:

SQ:发送队列,SQ中的单元叫SQE;

RQ:接收队列,RQ中的单元叫RQE;

SQ和RQ都是WQ,就像男人和女人都叫人,WQ中的单元叫WQE;

SR(发送请求)和RR(接收请求)都是WR=工作请求;

QP=SQ+RQ

CQ:完成队列,CQ中的单元叫CQE;

通信过程

(RDMA有两种通信模式 Read/Write和Send/Receive,此处以Write为例)

获取设备列表  ibv_get_device_list -->rocep216s0f0、rocep216s0f1

打开要请求的设备  ibv_open_device(rocep216s0f0)

……

创建QP、创建 CQ

交换控制信息(交换QP的ID,各自的内存地址等)

连接QP

发送端:

  • 应用程序创建WR请求,调用ibv_send_wr(send work request)
  • ibv_send_wr将WR放入QP的SQ中(成为WQE)
  • RDMA网卡从QP的SQ中取出WQE,把WQE指定的数据DMA到WQE指定的对端的内存地址中。
  • RDMA网卡DMA完成后产生一个WC(work complete),放入CQ(成为CQE)
  • 应用程序polling CQ(或者event通知),看到有CQE,就知道数据传输完成。

接收端:

     RDMA网卡接收到对端RDMA传来的数据和WQE,根据WQE中指定的地址,把数据放到本地的内存上,接收完成后。生成一个WC,放到CQ中。

因为不经过内核(CPU),所以内核完全不知道有数据到。

InfiniBand和IBoE(RoCE、iWARP)编程的差异

InfiniBand是真正的RDMA,有自己的链路层、网络层、传输层的协议栈,所以编程代码中的LRH 、 LID、L3 中的 GRH 都是为L1-L3服务。而RoCEv2、iWARP基于以太网实现RDMA ,L1-L3依赖于TCP,所以RoCEv2、iWARP通信代码中可能不太关注这几个参数。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【RDMA】RDMA编程入门--编辑中 的相关文章

  • Qt5.9.1安装包下载

    一 下载地址 附上百度网盘下载链接链接 https pan baidu com s 1wwvHe5kHwSg68hy1YM6Lhg 提取码 a999 二 安装步骤 选中qt opensource windows x86 5 9 1 exe右

随机推荐

  • xss-工具-Beef-Xss安装以及使用

    Beef Xss工具的简介 KaliLinux官网对这工具的介绍地址 https www kali org tools beef xss GitHub地址 https github com beefproject beef Beef Xss
  • 如何解决stata数据管理器中变量变红的问题

    目标 解决open变量变红的问题 网上说可以通过以下代码解决 实际上是缘木求鱼 encode encode 红色数字的变量名 gen 新产生的变量名 自己尝试用这个代码之后 发现对应变量不是红色了 但变成了蓝色 我开始以为问题已经解决了 但
  • Spring Security,没有看起来那么复杂(附源码)

    权限管理是每个项目必备的功能 只是各自要求的复杂程度不同 简单的项目可能一个 Filter 或 Interceptor 就解决了 复杂一点的就可能会引入安全框架 如 Shiro Spring Security 等 其中 Spring Sec
  • margin:0 auto是什么意思

    margin是外边距的意 当一个元素样式属性里有dumargin 0 auto时 并且父元素的宽度是确定的 意思是这个元素处于其父元素的居中位置 并且这个元素的上下外边距为0 即 上下外边距为0 左右自动 实际效果为左右居中 补充 marg
  • 缺少msvcp120.dll、msvcr120.dll解决办法

    缺少msvcp120 dll msvcr120 dll解决办法 丢失或缺少msvcp120 dll msvcr120 dll等这些报错是因为我们没有安装vc 运行库 看一下报错对应的数字对应的版本 msvcp msvcr60 71和80 d
  • Python--内建函数大全

    Python 解释器内置了许多函数和类型 列表如下 按字母排序 省略了几个我没用过或者不常用的 内建函数表 abs delattr hash memoryview set
  • 结构体排序------蓝桥杯

    题目 给出 nn 个人的语文 数学 英语的成绩 你需要把他们的成绩降序输出 排序的规则 先按总分排序 如果总分相等 就按语文成绩降序排序 如果语文成绩还相等 就按数学成绩降序排序 如果数学成绩还相等 就按姓名字典序升序排序 输入 第一行是一
  • Python中MongoDB的使用方法

    一 MongoDB是什么 在百度上查询的时候主要看到三个关键字 数据库 非关系型 查询功能强大 总结为查询功能强大的非关系型数据库 什么是数据库 应该是用来存储数据的 非关系型的意思 不不不 关系型的意思我都不懂 查询功能强大的意思应该是查
  • python matplotlib pyplot绘制散点图

    pyplot散点图示例 import matplotlib pyplot as plt import numpy as np import math import random plt rcParams font sans serif Si
  • 【Blender】快捷键整理

    Z 弹出着色模式菜单 shift Z 线框展示 Ctrl 空格 最大化视窗切换 N 隐藏侧栏 T 显示隐藏左侧工具菜单 小键盘 在视口内最大化显示当前选择物体 FN home 在视口内最大化显示场景内所有物体 SHIFT C 查看全部 sh
  • N沟道和P沟道MOS管的四个不同点

    作者 快捷芯 功率半导体创新品牌 1 芯片材质不同 虽然芯片都是硅基 但是掺杂的材质是不同 使得N沟道MOS管是通过电子形成电流沟道 P沟道MOS管是用空穴流作为载流子 具体原理可以参考一些教科书 属于工艺方面的问题 2 同等参数P沟道MO
  • Upload-labs 1-21关 靶场通关攻略(全网最全最完整)

    Pass 01 前端验证 因为是进行前端JS校验 因此可以直接在浏览器检查代码把checkFile 函数 即如下图红色框选中的函数 删了或者也可以把红色框改成true 并按回车 即可成功上传php文件 复制图片地址并用蚁剑进行连接 Pass
  • Hiv练习题之网站连续登陆天数分析

    数据源 有如下登录信息 userId day 1 2019 05 01 1 2019 05 02 1 2019 05 03 1 2019 05 04 1 2019 05 05 1 2019 05 06 1 2019 05 07 1 2019
  • Leetcode28. 找出字符串中第一个匹配项的下标

    代码 class Solution public int strStr String haystack String needle if haystack equals needle return 0 int len needle leng
  • IDEA中创建单元测试过程 JUnit

    1 在src同级别下创意一个test目录 2 右键这个test文件夹 设置为测试专用文件夹 然后在在下面创建一个java目录 根据你的需求 多级建目录 3 选择一个类 比如xxxServiceImpl xxDaoImpl 然后邮件 选择go
  • synchronized关键字修饰static方法和非static方法学习测试结论

    最近在学习研究synchronized关键字 发现有个疑问 在同一个类中 有多个sync方法 当线程调用其中的一个方法的时候 其他的线程能调用其他的sync方法么 为此做了简单的测试 详细的测试过程略过 读者可使用测试代码自行操作 得出结论
  • OKHttp详解

    OkHttp 是一套处理 HTTP 网络请求的依赖库 由 Square 公司设计研发并开源 目前可以在 Java 和 Kotlin 中使用 对于 Android App 来说 OkHttp 现在几乎已经占据了所有的网络请求操作 RetroF
  • Web项目实战

    文章目录 运行环境 1 前言 2 挑选模板 2 1 前端模板 2 2 后端模板 2 3 总结 3 实现注册与登陆 3 1 项目结构 3 2 注册 3 2 1 JDBC连接池连接 3 2 2 dao层实现JDBC的判重 插入 3 2 3 设计
  • Linux字符设备驱动的register_chrdev()与unregister_chrdev()

    Linux下的设备驱动程序被组织为一组完成不同任务的函数的集合 通过这些函数使得Windows的设备操作犹如文件一般 在应用程序看来 硬件设备只是一个设备文件 应用程序可以象操作普通文件一样对硬件设备进行操作 如open close rea
  • 【RDMA】RDMA编程入门--编辑中

    目录 一 前言 二 基本概念 1 队列和队列成员 2 传输模式 简介 单边双边传输流程简述 3 编程接口 verbs API 三 编程示例 工作大致流程说明 四 编程代码实例 5 RDMA编程概述 5 1 传输操作 5 2传输模式 5 3相