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. 關閉連接
-
斷開連接
當rdma_get_cm_event
返回RDMA_CM_EVENT_DISCONNECTED事件時,表示客戶端斷開了連接,server端要進行對應的清理。此時可以調用rdma_ack_cm_event
釋放事件資源。然后依次調用下面的函數,釋放連接資源,內存資源,隊列資源。 -
rdma_disconnect
-
rdma_destroy_qp
-
ibv_dereg_mr
-
rdma_destroy_id
釋放同客戶端連接的rdma_cm_id -
rdma_destroy_id
釋放用於偵聽的rdma_cm_id -
rdma_destroy_event_channel
釋放 event channel
原文:https://blog.csdn.net/winux/article/details/51772742
《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.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)
- dev_list = ibv_get_device_list(NULL);獲取主機可用的VPI設備列表
- ib_dev = dev_list[0]默認取第一個設備
- ibv_context* context = ibv_open_device(ib_dev);打開設備獲取context
- ibv_pd* pd = ibv_alloc_pd(context);創建Protection Domain
- worker_env_ (worker_env)拷貝工作環境參數
- event_channel_ = ibv_create_comp_channel(context_);創建完成通道,用於通知完成隊列
- cq_ = ibv_create_cq(context_, MAX_CONCURRENT_WRITES * 2, NULL, event_channel_, 0);創建完成隊列
- ibv_req_notify_cq(cq_, 0)完成完成隊列與完成通道的關聯
- 啟動處理線程Process_CQ()
~RdmaAdapter()
- ibv_destroy_cq(cq_)
- ibv_destroy_comp_channel(event_channel_)
- ibv_dealloc_pd(pd_)
- ibv_close_device(context_)
void Process_CQ()
- ibv_get_cq_event(event_channel_, &cq, &cq_context)阻塞等待隊列中進入新元素
- ibv_ack_cq_events(cq, 1);確認收到的事件
- ibv_req_notify_cq(cq_, 0)重新注冊,等待下次事件觸發
int ne = ibv_poll_cq(cq\_, MAX_CONCURRENT_WRITES * 2, static_cast<ibv_wc*>(wc_));
從CQ隊列中獲取所有的事件,ne表示事件個數- 遍歷每個cqe
- 判斷wc_[i].status == IBV_WC_SUCCESS,檢查wr的狀態是否正確
- 若wc_[i].opcode == IBV_WC_RECV_RDMA_WITH_IMM
- RdmaChannel* rc = reinterpret_cast
RdmaChannel
RdmaChannel(const RdmaAdapter* adapter, const string local_name, const string remote_name_)
- qp_ = ibv_create_qp(adapter_->pd_, &attr); 創建Queue Pair
- ibv_modify_qp(qp_, &attr, mask) 初始化QP
- 創建4個buffer並建立hash,同時加入索引表,tx_message_buffer_ = new RdmaMessageBuffer(this, buffer_names[0]);
- 執行100次Recv() (ibv_post_recv()),使得buffer准備好接收。
~RdmaChannel()
- ibv_destroy_qp(qp_) 銷毀QP
- 銷毀buffer
https://blog.csdn.net/qiangli_strong/article/details/81038082
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
相關資料和代碼參考
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 操作對端的內存。)