---
tags : Session one
title : data-path flow/memory management/verbs in short (應凱軒)
---
# Session one RDMA Foundation
1. 何謂RDMA
允許使用者態的應用程式直接讀取或寫入遠端記憶體。
2. DMA
直接記憶體存取,而不用通過CPU,可以直接寫入記憶體。

3. WQ WQE CQ CQE
a. WQE
簡單來說就是按順序儲存軟體給硬體下發的任務
舉例來說某一份任務是這樣的:“我想把位于地址0x12345678的長度為10byte的數據發送给對面的節點,硬體接到任務之后,就會通過DMA去記憶體中取數據,组裝數據包,然后發送。WQE的含意應該比較明確了。而其中的Queue 是一種先進先出的數據結構。
b. WQ
了解WQE後,就可以很簡單的來了解WQ了
它就是用來存放“任務書”的“文件夾”,WQ里面可以容纳很多WQE。
c. CQE
其實跟WQE的方向類似,只是他是在硬體完成任務後返回給軟體的。
d. CQ
跟WQ的意思一樣。

## WQ的概念圖

## CQ的概念圖
而一整個QP看起來就會像這樣子

1. 接收端硬體從RQ中拿到任務,準備接收數據。
2. 發送端APP以WQE的形式下發一次SEND任務。
3. 發送端硬體從SQ中拿到任務,從記憶體中拿到待發送數據,组裝數據包。
4. 發送端網卡將數據包通過物理鏈路發送给接收端網卡。
5. 接收端收到數據,進行校驗后回復ACK封包给發送端。
6. 接收端硬體將數據放到WQE中指定的位置,然后生成“任務報告”CQE,放置到CQ中。
7. 接收端APP取得任务完成信息。
8. 發送端網卡收到ACK(確認資料有無正確傳輸到接收端)后,生成CQE,放置到CQ中。
9. 发送端APP取得任务完成信息。
### 然而我們再細看的話

用戶端傳送資料時會通過verbs 而verbs為 " 一組標準動作,又類似於一個function
- 那旁邊的SGL呢?
SGL(Scatter/Gather List)是最基本的數據組織形式。 而SGL為SGE組合而成的,那每一個SGE就是一個Data Segment(資料區段)也就是DS。RDMA支持Scatter/Gather操作,
- 具體來講就是RDMA可以支持一個連續的Buffer空間,進行Scatter分散到多個目的主機的不連續的Buffer空間。Gather指的就是多個不連續的Buffer空間,可以Gather到目的主機的一段連續的Buffer空間。
- 以下為一個ibv_sge的定義
``` C++
struct ibv_sge {
uint64_t address; //資料區段所在的虛擬記憶體
uint32_t length; //資料區段長度
uint32_t lkey; // 該資料區段對應的L_key
};
```
- 而這裡就可以細講Verbs了
- 在數據傳輸中,發送/傳輸的Verbs API為
###### ibv_post_send() - post a list of work requests (WRs) to a send queue 將一個WR列表放置到發送隊列中
###### ibv_post_recv() - post a list of work requests (WRs) to a receive queue 將一個WR列表放置到接收隊列中
ibv_post_send的結構
```c++
#include <infiniband/verbs.h>
int ibv_post_send(struct ibv_qp *qp,
struct ibv_send_wr *wr,
struct ibv_send_wr **bad_wr);
```
而其中ibv_send_wr的結構
```
struct ibv_send_wr {
uint64_t wr_id; /* User defined WR ID */
struct ibv_send_wr *next; /* Pointer to next WR in list, NULL if last WR */
struct ibv_sge *sg_list; /* Pointer to the s/g array */
int num_sge; /* Size of the s/g array */
enum ibv_wr_opcode opcode; /* Operation type */
int send_flags; /* Flags of the WR properties */
uint32_t imm_data; /* Immediate data (in network byte order) */
union {
struct {
uint64_t remote_addr; /* Start address of remote memory buffer */
uint32_t rkey; /* Key of the remote Memory Region */
} rdma;
struct {
uint64_t remote_addr; /* Start address of remote memory buffer */
uint64_t compare_add; /* Compare operand */
uint64_t swap; /* Swap operand */
uint32_t rkey; /* Key of the remote Memory Region */
} atomic;
struct {
struct ibv_ah *ah; /* Address handle (AH) for the remote node address */
uint32_t remote_qpn; /* QP number of the destination QP */
uint32_t remote_qkey; /* Q_Key number of the destination QP */
} ud;
} wr;
};
```

- 從上圖中,我們可以看到wr鏈表中的每一個結點都包含了一個SGL,SGL是一個數組,包含一個或多個SGE。通過ibv_post_send提交一個RDMA SEND 請求。這個WR請求中,包括一個sg_list的元素。它是一個SGE鏈表,SGE指向具體需要發送數據的Buffer。
- 
- 我們在發送一段記憶體地址的時候,我們需要將這段記憶體地址通過Memory Registration註冊到RDMA中。也就是說註冊到PD記憶體保護域當中。一個SGL至少被一個MR保護, 多個MR存在同一個PD中。如圖所示一段記憶體MR可以保護多個SGE元素。
- 
在上圖中,一個SGL數組包含了3個SGE, 長度分別爲N1, N2, N3字節。我們可以看到,這3個buffer並不連續,它們Scatter(分散)在記憶體中的各個地方。RDMA硬體讀取到SGL後,進行Gather(聚合)操作,於是在RDMA硬體的Wire上看到的就是N3+N2+N1個連續的字節。換句話說,通過使用SGL, 我們可以把分散(Scatter)在記憶體中的多個資料區段(不連續)交給RDMA硬體去聚合(Gather)成連續的資料區段
#### RDMA 記憶體
Memory Registration(MR) | 記憶體註冊
RDMA 就是用來對記憶體進行數據傳輸。那麼怎樣才能對記憶體進行傳输,很簡單,那就是註冊。 因為RDMA硬體對用來做數據傳輸的記憶體是有特殊要求的。
在數據傳輸的過程中,應用程序不能修改數據所在的記憶體。
操作系统不能對數據所在的記憶體進行page out操作 -- 實體地址和虛擬地址的映射必须是固定不變的。
注意無論是DMA或者RDMA都要求實體地址連續,這是由DMA引擎所决定的。 那麼怎麼進行記憶體註冊呢?
創建兩個key (local和remote)指向需要操作的記憶體區域
註冊的keys是數據傳輸請求的一部分
註冊一个Memory Region之后,这個時候這個Memory Region也就有了它自己的屬性:
context : RDMA操作上下文
addr : MR被註冊的Buffer地址
length : MR被註冊的Buffer長度
lkey:MR被註冊的本地key
rkey:MR被註冊的遠程key
對Memrory Registration:Memory Registration只是RDMA中對記憶體保護的一種措施,只有將要操作的記憶體註冊到RDMA Memory Region中,這塊操作的記憶體就交给RDMA 保護域來操作了。這個時候我們就可以對這塊記憶體進行操作,至於操作的起始地址、操作Buffer的長度,可以根據程序的具體需求進行操作。我們只要保證接受方的Buffer 接受的長度大於等於發送的Buffer長度。
### Reference
https://zhuanlan.zhihu.com/p/195757767
https://www.twblogs.net/a/5c404c9ebd9eee35b3a6a3a2
https://zhuanlan.zhihu.com/p/55142557
https://zhuanlan.zhihu.com/p/141267386
https://www.zhihu.com/column/c_1231181516811390976