RDMA 基础概念
📡

RDMA 基础概念

Created
Jul 17, 2022 08:45 AM
Tags
RDMA
SmartNIC
Category
Networking
Last Edited
Last updated July 17, 2022
Abstract
本文介绍了 RDMA 的一些基础概念,并使用 Send/ Recv 为例介绍了一次通信的流程。
Related to Reading List (Column)

一、基础概念

1. QP — Queue Pair

RDMA 通信的基础,其中包含了一个 RQ 和一个 SQ。基于连接的 QP 只能一对一交流,而基于数据报的非连接通信能实现 QP 一对多通信。

2. RQ — Receive Queue

存储了接收的 WQE。

3. SQ — Send Queue

存储了发送的 WQE。

4. CQ — Completion Queue

存放通信完成的信号,其中存放的每个实体较说 CQE — Completion Queue Entry。

5. WQE — Work Queue Elements

用于下发操作指令,一般是放置在 RQ 或者 SQ 中,其大小受多种因素影响。

6. MR — Message Region

用于记录从主机内存中分配来的内存,可用于之后传输数据和接收数据使用。

二、动作原语 (Verbs)

1. 内存交互原语

  • Read
  • Write
这类原语需要提前知道对端主机里要被操作的内存地址(相对地址,基于virtual address),同时它无需对端主机 CPU 的参与。

2. 消息交互原语

  • Send
  • Receive
这类原语无需知道地址,Send 操作会将消息写到对端通过 Receive 操作声明的内存中,对端声明地址时需要 CPU 参与。

三、传输方式

1. 基于连接

RC — Reliable Connected

NIC 会使用确认机制来保证按顺序传递消息。

UC — Unreliable Connected

不可靠连接不保证消息顺序,但 InfiniBand 提供了链路层的流控制,保证极少丢包。

2. 无连接的

UD — Unreliable Datagram

使用较少,但可以实现广播

3. 对 Verbs 的支持

RDMA 传输方式
RDMA 传输方式

四、传输流程

这里使用 Send 和 Recv 作为示例进行介绍,常规 RDMA 编程还需提前建立连接、协商地址等过程,这里进行了省略,详细可参考 RDMA_CA 的使用。

1. 开辟内存空间

RDMA 操作开始于“搞”内存。当你在对内存进行操作的时候,就是告诉内核这段内存名花有主了,主人就是你的应用程序。因此我们要先注册一个内存区域(MR)。一旦 MR 注册完毕,我们就可以使用这段内存来做任何 RDMA 操作。
struct ibv_mr { struct ibv_context *context; struct ibv_pd *pd; void *addr; size_t length; uint32_t handle; uint32_t lkey; // 本地操作的凭据 uint32_t rkey; // 远端操作的凭据 };

2. 创建 WQE (send / recv)

有了内存下面就可以进行传输了,首先发送端应该在 SQ 中创建一个 WQE 或者叫 Work Request,并使用 sg_list 指定需要传输的缓冲区地址。
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; };
对应的接收端也要创建一个 WQE 用于接收。
struct ibv_recv_wr { uint64_t wr_id; struct ibv_recv_wr *next; struct ibv_sge *sg_list; int num_sge; };
RDMA编程中,SGL(Scatter/Gather List)是最基本的数据组织形式。 SGL 是一个数组,该数组中的元素被称之为 SGE(Scatter/Gather Element),每一个 SGE 就是一个 Data Segment (数据段)。RDMA 支持 Scatter/Gather 操作,具体来讲就是 RDMA 可以支持一个连续的 Buffer 空间,进行 Scatter 分散到多个目的主机的不连续的 Buffer 空间。Gather 指的就是多个不连续的 Buffer 空间可以 Gather 到目的主机的一段连续的 Buffer 空间。
struct ibv_sge { uint64_t addr; // 数据段所在的虚拟内存的起始 // 地址 (Virtual Address of the Data Segment (i.e. Buffer)) uint32_t length; // 数据段长度(Length of the Data Segment) uint32_t lkey; // 该数据段对应的L_Key (Key of the local Memory Region) }

3. 添加任务

之后需要将任务添加到对应的 QP 中,让网卡去执行,需要使用 ibv_post_send() 函数
int ibv_post_send(struct ibv_qp *qp, struct ibv_send_wr *wr, struct ibv_send_wr **bad_wr);

4. 网卡执行

根据 sg_list 字段网卡驱动便可以知道需要操作的内存地址,之后即可进行内存操作,同时为了防止不同主机操作本不属于它的内存,又出现了 PD 这个概念。通过 PD 可以将 MR 与 QP 进行绑定,通过网卡驱动硬件上防止内存被错误访问到。
Protection Domain
Protection Domain
网卡检验 PD 后便可以通过 SGL 组合内存,进行数据传输。
Gather Step of RDMA
Gather Step of RDMA