【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 操作對端的內存。)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM