1.相關名詞解釋
1.1 rdma-core
指開源RDMA使用者态軟體協定棧,(使用者空間的驅動)包含使用者态架構、各廠商使用者态驅動、API幫助手冊以及開發自測試工具等。
rdma-core在github上維護,我們的使用者态Verbs API實際上就是它實作的。https://github.com/linux-rdma/rdma-core
1.2 kernel RDMA subsystem
指開源的Linux核心中的RDMA子系統,(核心空間的驅動)包含RDMA核心架構及各廠商的驅動。
RDMA子系統跟随Linux維護,是核心的的一部分。一方面提供核心态的Verbs API,一方面負責對接使用者态的接口。
1.3 OFED
全稱為OpenFabrics Enterprise Distribution,是一個開源軟體包集合,其中包含核心架構和驅動、使用者架構和驅動、以及各種中間件、測試工具和API文檔。
開源OFED由OFA組織負責開發、釋出和維護,它會定期從rdma-core和核心的RDMA子系統取軟體版本,并對各商用OS發行版進行适配。除了協定棧和驅動外,還包含了perftest等測試工具。
除了開源OFED之外,各廠商也會提供定制版本的OFED軟體包,比如華為的HW_OFED和Mellanox的MLNX_OFED。這些定制版本基于開源OFED開發,由廠商自己測試和維護,會在開源軟體包基礎上提供私有的增強特性,并附上自己的配置、測試工具等。
以上三者是包含關系。無論是使用者态還是核心态,整個RDMA社群非常活躍,架構幾乎每天都在變動,都是平均每兩個月一個版本。而OFED會定期從兩個社群中取得代碼,進行功能和相容性測試後釋出版本,時間跨度較大,以年為機關計。
1.4 Mellanox 的OFED下載下傳安裝
wget http://content.mellanox.com/ofed/MLNX_OFED-4.3-1.0.1.0/MLNX_OFED_LINUX-4.3-1.0.1.0-rhel7.2-x86_64.tgz
tar -xvf MLNX_OFED_LINUX-4.3-1.0.1.0-rhel7.2-x86_64.tgz
cd MLNX_OFED_LINUX-4.3-1.0.1.0-rhel7.2-x86_64/
yum install lsof tcl tk libxml2-python -y
yum install createrepo.noarch -y
yum install python-devel -y
./mlnxofedinstall --dpdk --upstream-libs --add-kernel-support
安裝完成後會提示:
/etc/init.d/openibd restart
在執行restart時,可能需要首先執行(modprobe -rv ib_isert rpcrdma ib_srpt),否則會restart失敗
安裝完了需要restart
1.4.1安裝過程中遇到的問題
驅動下載下傳位址:
驅動下載下傳位址:
https://www.mellanox.com/page/products_dyn?product_family=26&mtag=linux_sw_drivers&ssn=e76bu2ei7i1dcniinql196evj4
Linux InfiniBand Drivers
安裝過程中遇到的問題
解決方法
vi /usr/src/kernels/3.10.0-693.21.1.el7.x86_64/arch/x86/Makefile 166行,把ifneq到endif之間注釋掉
1.4.2 源碼包
2. Verbs API
廣義的Verbs API主要由兩大部分組成verbs和rdma_cm
Verbs API 操作RDMA的函數接口,也就是說業界的RDMA應用,要麼直接基于這組API編寫,要麼基于在Verbs API上又封裝了一層接口的各種中間件編寫。(rdma_cm)
Verbs API向使用者提供了有關RDMA的一切功能,典型的包括:注冊MR、建立QP、Post Send、Poll CQ等等。
對于Linux系統來說,Verbs的功能由rdma-core和核心中的RDMA子系統提供,分為使用者空間驅動的 Verbs api和核心空間驅動的Verbs api,分别用于使用者态和核心态的RDMA 程式。
2.1 rdma_cm和verbs的差別
2.1.1 互相關系
1> rdma_cm依賴ib_verbs
2> ib_verbs和rdma_cm 都是Mellanox公司提供的兩個動态連結庫。(這兩個庫的API ,queue pair (QP) 類似于 TCP的sockets)
3> rdma_cm連接配接管理器庫,包含了對ib_verbs的封裝或具體化,通過verbs API 對硬體進行通路。
rdma_post_send = ibv_post_send(qp,wr.opcode=IBV_WR_SEND,bad_wr)
rdma_post_read = ibv_post_send(qp,wr.opcode=IBV_WR_RDMA_READ ,bad_wr)
rdma_post_write = ibv_post_send(qp,wr.opcode=IBV_WR_RDMA_WRITE,bad_wr)
2.1.2 libibverbs和librdmacm的差別
在infiniband/verbs.h中,定義了ibv_post_send()和ibv_post_recv()操作,分别表示,将wr釋出到SQ和RQ中,至于是什麼操作(send or write/read),和wr中的opcode有關。
對ibv_post_send()來說,對應的是struct ibv_send_wr,其中有opcode,表示操作碼,有SEND/WRITE/READ等。
對于ibv_post_recv()來說,對應的是struct ibv_recv_wr,沒有操作碼,因為隻有接收一個動作,是以不需要定義其它的操作碼。但是發送來說,有三類。
在rdma/rdma_verbs.h中,有rdma_post_send(),rdma_post_recv(),rdma_post_read(),rdma_post_write()。
rdma_post_send():把wr釋出到QP的SQ中,需要mr
rdma_post_recv():把wr釋出到QP的RQ中,需要mr
rdma_post_read():把wr釋出到QP的SQ中,執行RDMA READ操作,需要遠端位址和rkey,以及本地存儲位址和長度,以及mr
rdma_post_write():把wr釋出到QP的SQ中,RDMA WRITE操作,需要遠端的被寫入位址和rkey,以及本地要發送資料的位址和長度,以及mr
是以rdma/rdma_verbs.h中的四種通信函數其實和infiniband/verbs.h中的兩種方法是一緻的。
ibv_post_send()對應rdma_post_send()、rdma_post_write()
ibv_post_recv()對應rdma_post_recv(),rdma_post_read()
2.2 IB_VERBS
接口以ibv_xx(使用者态)或者ib_xx(核心态)作為字首,是最基礎的程式設計接口,使用IB_VERBS就足夠編寫RDMA應用了。
比如:
ibv_create_qp() 用于建立QP
ibv_post_send() 用于下發Send WR
ibv_poll_cq() 用于從CQ中輪詢CQE
2.3 RDMA_CM
以rdma_為字首,主要分為兩個功能:
1)CMA(Connection Management Abstraction)---- 建連連接配接管理
在Socket和Verbs API基礎上實作的,用于CM建鍊并交換資訊的一組接口。CM建鍊是在Socket基礎上封裝為QP實作,從使用者的角度來看,是在通過QP交換之後資料交換所需要的QPN,Key等資訊。
比如:
rdma_listen()用于監聽鍊路上的CM建鍊請求。
rdma_connect()用于确認CM連接配接。
2)CM VERBS ----收發資料
RDMA_CM也可以用于資料交換 (收發資料),相當于在verbs API上又封裝了一套資料交換接口。
比如:
rdma_post_read()可以直接下發RDMA READ操作的WR,而不像ibv_post_send(),需要在參數中指定操作類型為READ。
rdma_post_ud_send()可以直接傳入遠端QPN,指向遠端的AH,本地緩沖區指針等資訊觸發一次UD SEND操作。
傳統以太網的使用者,基于Socket API來編寫應用程式;而RDMA的使用者,基于Verbs API來編寫應用程式。
RDMA有三種協定IB/iWARP/RoCE,設計Verbs API通過統一接口,讓同一份RDMA程式程式可以無視底層的硬體和鍊路差異運作在不同的環境中。
2.4 常用API介紹
- 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(記憶體)保護域
- 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的指針
- 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)
從事件隊列中擷取一個異步事件,傳回異步事件的資訊(事件來源,事件類型等)以及錯誤碼
- 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)
專門用來下發RECV操作WR的接口
- num, wc ibv_poll_cq(cq, max_num)
從完成隊列CQ中輪詢CQE,使用者需要提前準備好記憶體來存放WC,并傳入可以接收多少個WC。該接口會傳回一組WC結構體(其内容包括wr_id,狀态,操作碼,QPN等資訊)以及WC的數量