【RDMA】RDMA编程

 

RDMA的学习环境搭建

RDMA需要专门的RDMA网卡或者InfiniBand卡才能使用,学习RDMA而又没有这些硬件设备,可以使用一个软件RDMA模拟环境,softiwarp ,
- 这是加载地址:https://github.com/zrlio/softiwarp
- 这是安装教程:http://www.reflectionsofthevoid.com/2011/03/how-to-install-soft-iwarp-on-ubuntu.html

这里也有一个RDMA编程的入门示例,
https://github.com/tarickb/the-geek-in-the-corner
需要注意的是,这个例子里面缺省用的是IPv6连接,如果希望在IPv4环境下测试,需要先改代码用IPv4地址。

参考资料:

RDMA与socket的类比

和Socket连接类似,RDMA连接也分为可靠连接和不可靠连接。然而也不完全相同,Socket的可靠连接就是TCP连接,是流式的;不可靠连接也就是UDP,是消息式的。对于RDMA来说,无论是可靠连接和不可靠连接,都是消息式的。
编程角度看,RDMA代码也分为Server端,Client端,也有bind, listen, connect, accept,等动作,然而细节上仍有不少区别。

在Server端,一个RDMA服务器的代码流程如下:

1 rdma_create_event_channel

这一步是创建一个event channel,event channel是RDMA设备在操作完成后,或者有连接请求等事件发生时,用来通知应用程序的通道。其内部就是一个file descriptor, 因此可以进行poll等操作。

2 rdma_create_id

这一步创建一个rdma_cm_id, 概念上等价与socket编程时的listen socket。

3 rdma_bind_addr

和socket编程一样,也要先绑定一个本地的地址和端口,以进行listen操作。

4 rdma_listen

开始侦听客户端的连接请求

5 rdma_get_cm_event

这个调用就是作用在第一步创建的event channel上面,要从event channel中获取一个事件。这是个阻塞调用,只有有事件时才会返回。在一切正常的情况下,函数返回时会得到一个 RDMA_CM_EVENT_CONNECT_REQUEST事件,也就是说,有客户端发起连接了。
在事件的参数里面,会有一个新的rdma_cm_id传入。这点和socket是不同的,socket只有在accept后才有新的socket fd创建。

6 ibv_alloc_pd

创建一个protection domain。protection domain可以看作是一个内存保护单位,在内存区域和队列直接建立一个关联关系,防止未授权的访问。

(RDMAWorker::listent >> ib.init() >> ProtectionDomain >> ibv_alloc_pd)

7 ibv_create_comp_channel

和之前创建的event channel类似,这也是一个event channel,但只用来报告完成队列里面的事件。当完成队列里有新的任务完成时,就通过这个channel向应用程序报告。

(RDMADispatcher::polling_start  >> ib->create_comp_channel >>  cc->init  >>ibv_create_comp_channel)

8 ibv_create_cq

创建[完成队列],创建时就指定使用第6步的channel。

9 rdma_create_qp

创建一个queue pair, 一个queue pair包括一个发送queue和一个接收queue. 指定使用前面创建的cq作为完成队列。该qp创建时就指定关联到第6步创建的pd上。

10 ibv_reg_mr

注册内存区域。RDMA使用的内存,必须事先进行注册。这个是可以理解的,DMA的内存在边界对齐,能否被swap等方面,都有要求。

11 rdma_accept

至此,做好了全部的准备工作,可以调用accept接受客户端的这个请求了。 –:)长出一口气 ~~ 且慢,

12 rdma_ack_cm_event

对于每个从event channel得到的事件,都要调用ack函数,否则会产生内存泄漏。这一步的ack是对应第5步的get。每一次get调用,都要有对应的ack调用。

13 rdma_get_cm_event

继续调用rdma_get_cm_event, 一切正常的话我们此时应该得到 RDMA_CM_EVENT_ESTABLISHED 事件,表示连接已经建立起来。不需要做额外的处理,直接rdma_ack_cm_event就行了

终于可以开始进行数据传输了 ==== (如何传输下篇再说)

4. 关闭连接

  1. 断开连接
    rdma_get_cm_event返回RDMA_CM_EVENT_DISCONNECTED事件时,表示客户端断开了连接,server端要进行对应的清理。此时可以调用rdma_ack_cm_event释放事件资源。然后依次调用下面的函数,释放连接资源,内存资源,队列资源。

  2. rdma_disconnect

  3. rdma_destroy_qp

  4. ibv_dereg_mr

  5. rdma_destroy_id
    释放同客户端连接的rdma_cm_id

  6. rdma_destroy_id
    释放用于侦听的rdma_cm_id

  7. rdma_destroy_event_channel
    释放 event channel

原文:https://blog.csdn.net/winux/article/details/51772742

图链接

“rdma event channel”的图片搜索结果


《RDMA技术详解(一):RDMA概述》https://blog.csdn.net/qq_21125183/article/details/86522475

《RDMA技术详解(二):RDMA Send Receive操作》https://blog.csdn.net/qq_21125183/article/details/86525012

《RDMA技术详解(三):理解RDMA Scatter Gather List》https://blog.csdn.net/qq_21125183/article/details/86527199

creating Scatter Gather Elements

https://www.openfabrics.org/downloads/Media/SC11/SC11_Writing_Application_Programs_for_RDMA_using_OFA_Software-v5.pdf

https://www.cs.mtsu.edu/~waderholdt/6430/papers/ibverbs.pdf

RDMA的原理、传输与Verbs https://www.cnblogs.com/zafu/p/8335200.html

access open 知乎_12. RDMA之Verbs https://blog.csdn.net/weixin_33978451/article/details/112398245

目前有两张硬件可以使用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找到需要使用的设备;

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接口接收。

实际例子在perftest中有

rdma的使用

相关结构体

// structure to save the address of remote channels.
struct RdmaAddress {
  uint32_t lid;
  uint32_t qpn;
  uint32_t psn;
  uint64_t snp;
  uint64_t iid;
};
// structure to save information for remote memory regions.
struct RemoteMR {
  uint64_t remote_addr;
  uint32_t rkey;
};
enum BufferStatus { none, idle, busy };
enum Location { local, remote };
enum BufferType { ACK, MESSAGE, TENSOR };
enum RdmaMessageType {
  RDMA_MESSAGE_ACK,
  RDMA_MESSAGE_BUFFER_IDLE,
  RDMA_MESSAGE_BUFFER_REQUEST,
  RDMA_MESSAGE_BUFFER_RESPONSE,
  RDMA_MESSAGE_TENSOR_REQUEST,
  RDMA_MESSAGE_TENSOR_WRITE
};

RdmaAdapter

RdmaAdapter(const WorkerEnv* worker_env)

  1. dev_list = ibv_get_device_list(NULL);获取主机可用的VPI设备列表
  2. ib_dev = dev_list[0]默认取第一个设备
  3. ibv_context* context = ibv_open_device(ib_dev);打开设备获取context
  4. ibv_pd* pd = ibv_alloc_pd(context);创建Protection Domain
  5. worker_env_ (worker_env)拷贝工作环境参数
  6. event_channel_ = ibv_create_comp_channel(context_);创建完成通道,用于通知完成队列
  7. cq_ = ibv_create_cq(context_, MAX_CONCURRENT_WRITES * 2, NULL, event_channel_, 0);创建完成队列
  8. ibv_req_notify_cq(cq_, 0)完成完成队列与完成通道的关联
  9. 启动处理线程Process_CQ()

~RdmaAdapter()

  1. ibv_destroy_cq(cq_)
  2. ibv_destroy_comp_channel(event_channel_)
  3. ibv_dealloc_pd(pd_)
  4. ibv_close_device(context_)

void Process_CQ()

  1. ibv_get_cq_event(event_channel_, &cq, &cq_context)阻塞等待队列中进入新元素
  2. ibv_ack_cq_events(cq, 1);确认收到的事件
  3. ibv_req_notify_cq(cq_, 0)重新注册,等待下次事件触发
  4. int ne = ibv_poll_cq(cq\_, MAX_CONCURRENT_WRITES * 2, static_cast<ibv_wc*>(wc_)); 从CQ队列中获取所有的事件,ne表示事件个数
  5. 遍历每个cqe
    1. 判断wc_[i].status == IBV_WC_SUCCESS,检查wr的状态是否正确
    2. 若wc_[i].opcode == IBV_WC_RECV_RDMA_WITH_IMM
      1. RdmaChannel* rc = reinterpret_cast

RdmaChannel

RdmaChannel(const RdmaAdapter* adapter, const string local_name, const string remote_name_)

  1. qp_ = ibv_create_qp(adapter_->pd_, &attr); 创建Queue Pair
  2. ibv_modify_qp(qp_, &attr, mask) 初始化QP
  3. 创建4个buffer并建立hash,同时加入索引表,tx_message_buffer_ = new RdmaMessageBuffer(this, buffer_names[0]);
  4. 执行100次Recv() (ibv_post_recv()),使得buffer准备好接收。

~RdmaChannel()

  1. ibv_destroy_qp(qp_) 销毁QP
  2. 销毁buffer

https://blog.csdn.net/qiangli_strong/article/details/81038082

RDMA学习路线总结

原创

sysu_huyh5

rdma

2017/02/21 13:57

阅读数 1.7W

本博客仅作为分享本人学习rdma技术过程中一些经验和资料分享,若有错误之处,还请指教。如有侵犯版权问题,请立即通知本人,本人立即删除.特此声明!

 简介--什么是rdma

    RDMA(Remote Direct Memory Access)技术全称远程直接数据存取。

    RDMA (Remote DMA) is the ability of accessing (i.e. reading from or writing to) memory on a remote machine withoutinterrupting the processing of the CPU(s) on that system.

RDMA的好处与优势:

     1、零拷贝(zero-copy) RDMA transfers data directly from user virtual memory on one node to user virtual memory on another node, tcp copies into/out of system buffers on both nodes.

    2、CPU占有率( Low CPU utilization) 鉴于zero-copy的存在,使得机器的cpu占有率下降,让cpu更多的执行其他逻辑计算指令。

     3、异步事件(Asynchronous operation) rdma在io操作过程中并不阻塞相关线程,tcp/ip确是阻塞方式(此处所说的阻塞,是指tcp将数据发送到内核缓冲区是阻塞的,不同于tcp的noblocking的概念)

          RDMA是asynchronous的,即no blocking during a transfer, which – starts when metadata added to “work queue” – finishes when status available in “completion queue”

       tcp/ip中是synchronous的,即send(), recv() block until data copied – O_NONBLOCK, MSG_DONTWAIT are not asynchronous,they are “try” and get error

     4、Message boundaties preserved. rdma是基于消息模式的,本身保留了消息边界,不同于tcp/ip流传输方式,需要做拆包解包操作。 

rdma对于高性能的网络通讯来说,优势很多,但学习成本以及相关推广成本也是蛮高的,后面分享下本人摸索过程中的一些资料。

编程环境

    网络环境分为一下几种: InfiniBand环境,iWarp(internet Wide Area RMDA Protocol)环境, RoCE(RDMA over Converged Ethernet)环境,SoftRoCE环境。

    在没有硬件设备的支持下,我们可以搭一套软环境来熟悉相关rdma编程的知识点,至于如何搭建一套SoftRoCE环境,请参见本人和小伙伴通过摸索亲测有效的博客

https://my.oschina.net/SysuHuyh5LoveHqq/blog/798275

推荐编程库

    MLNX_OFED 4.0 , 一般来说,就本人个人经验来看,rdma编程最好还是使用硬件厂商的相关库,一般IB网络我们使用Mellanox的硬件(网卡,交换机等等),因此编程所用驱动、代码库还是推荐迈络思OFED,目前为止4.0是最新版本,详情参考:

http://www.mellanox.com/page/products_dyn?product_family=26&mtag=linux_sw_drivers ,

    除了安装版本,最好下载相关源码,里面有相关参考demo供大家学习理解rdma编程。

    主要熟悉libibverbs和librdmacm库,其中librdmacm在libibverbs上封装了一层,个人推荐直接使用libibverbs作为初期学习之用,这样更好了解整个事件的来龙去脉。

 

编程参考手册

    RDMA Aware Networks Programming User Manual 1.7, 目前最新版就是1.7版本,此手册可研读多次,对基本概念性了解很有帮助

http://www.mellanox.com/related-docs/prod_software/RDMA_Aware_Programming_user_manual.pdf

 

相关资料和代码参考

www.mellanox.com/

http://www.rdmamojo.com/

samplecode: https://github.com/tarickb/the-geek-in-the-corner 

代码解析https://thegeekinthecorner.wordpress.com/page/2/

 

rdma网络课程培训

迈络思网关给出了rdma网络编程培训的相关课程,可以作为相关参考http://academy.mellanox.com/en/

迈络思相关硬件的配置工具

mellanox官网给出的参考资料

详情请见: http://www.mellanox.com/page/configuration-tools

作者:胡宇辉,某券商软件开发工程师,主要从事后台高性能服务端编程,分布式系统设计。使用语言C/C++, Erlang,Golang,了解ELK, open-falcon, RDMA等相关知识点。

https://my.oschina.net/SysuHuyh5LoveHqq/blog/842767

RDMA网络相关数据结构和函数

typedef struct addrinfo
{
    int                 ai_flags; //指示在getaddrinfo函数中使用的选项的标志。
    int                ai_family;
    int                 ai_socktype;
    int               ai_protocol;
    size_t             ai_addrlen;
    char              *ai_canonname;
    struct sockaddr       *ai_addr;
    struct addrinfo       *ai_next; //指向链表中下一个结构的指针。此参数在链接列表的最后一个addrinfo结构中设置为NULL。
} ADDRINFOA, *PADDRINFOA;
其中ai_flags、ai_family、ai_socktype说明如下:
参数            取值            值            说明
ai_family        AF_INET        2             IPv4
                 AF_INET6       23            IPv6
                 AF_UNSPEC      0             协议无关

ai_protocol      IPPROTO_IP      0            IP协议
                 IPPROTO_IPV4    4            IPv4
				 IPPROTO_IPV6    41           IPv6
				 IPPROTO_UDP     17           UDP
				 IPPROTO_TCP     6            TCP

ai_socktype     SOCK_STREAM       1            流
				SOCK_DGRAM        2            数据报

ai_flags        AI_PASSIVE        1            被动的,用于bind,通常用于server socket
				AI_CANONNAME      2
				AI_NUMERICHOST    4            地址为数字串

对于ai_flags值的说明:

AI_NUMERICHOST | AI_CANONNAME | AI_PASSIVE

如上表所示,ai_flags的值范围为0~7,取决于程序如何设置3个标志位,比如设置ai_flags为 “AI_PASSIVE | AI_CANONNAME”,ai_flags值就为3。三个参数的含义分别为:
(1)AI_PASSIVE当此标志置位时,表示调用者将在bind()函数调用中使用返回的地址结构。当此标志不置位时,表示将在connect()函数调用中使用。当节点名位NULL,且此标志置位,则返回的地址将是通配地址。如果节点名NULL,且此标志不置位,则返回的地址将是回环地址。
(2)AI_CANNONAME当此标志置位时,在函数所返回的第一个addrinfo结构中的ai_cannoname成员中,应该包含一个以空字符结尾的字符串,字符串的内容是节点名的正规名。
(3)AI_NUMERICHOST当此标志置位时,此标志表示调用中的节点名必须是一个数字地址字符串。

RDMA的ibv_post_send() 函数

其中struct ibv_send_wr结构体的定义为:

struct ibv_send_wr {
    uint64_t        wr_id;
    struct ibv_send_wr     *next;
    struct ibv_sge           *sg_list;
    int            num_sge;
    enum ibv_wr_opcode    opcode;
    int            send_flags;
    uint32_t        imm_data;
    union {
        struct {
            uint64_t    remote_addr;
            uint32_t    rkey;
        } rdma;
        struct {
            uint64_t    remote_addr;
            uint64_t    compare_add;
            uint64_t    swap;
            uint32_t    rkey;
        } atomic;
        struct {
            struct ibv_ah  *ah;
            uint32_t    remote_qpn;
            uint32_t    remote_qkey;
        } ud;
    } wr;
};

在ibv_send_wr 结构体中opcode参数决定了数据传输类型,比如说:

IBV_WR_SEND——这种传输方式,当前buffer内存中在sg_list中的内容会被发送给远方的QP。发送方并不会知道数据会写到远方节点的何处(接收方决定)。接收方要post_recv,并且接收到的数据要放到指定的地址中。

IBV_WR_RDMA_WRITE——这种传输方式,本地内存buffer中sg_list中的内容会被发送和写到远方节点的QP的虚拟空间中的一段连续内存块中——这并不意味着远方的内存在物理上也是连续的。并且remote QP也不需要post_recv。 (真正的RDMA,对方cpu不参与,本端直接用最开始握手时得到的addr和key 操作对端的内存。)

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值