在正式开始第一次RDMA Verbs编程之前,我们先得有点基础知识。
除了本博客的基础知识,还建议看下以前的视频教程。
RDMA verbs编程 源码案例IB ROCE_哔哩哔哩_bilibili
了解PD、MR、CQ、QP、CQ等基本概念。
一、简介
RDMA Verbs API向用户提供了有关RDMA的相关功能,典型的包括注册MR、创建QP、Post Send、Poll CQ等。用于管理RDMA通信和数据传输。
本期视频教程在这:
RDMA verbs编程 源码案例IB ROCE_哔哩哔哩_bilibili
二、程序执行流程
三、常用RDMA Verbs API
1. ibv_get_device_list()
- 功能:获取当前系统中可用的RDMA设备列表。
- 返回值:返回一个指向RDMA设备列表的指针,以及设备数量。如果返回NULL,则表示获取设备列表失败。
- 使用场景:在初始化RDMA应用程序时,首先需要获取系统中可用的RDMA设备列表,以便后续选择和使用。
2. ibv_open_device()
- 功能:打开并初始化一个RDMA设备,获取其上下文信息。
- 参数:需要打开的RDMA设备的指针。
- 返回值:成功时返回一个指向RDMA设备上下文的指针,失败时返回NULL。
- 使用场景:在获取到可用的RDMA设备列表后,应用程序需要选择一个设备并打开它,以便进行后续的RDMA操作。
3. ibv_query_device()
- 功能:查询RDMA设备的属性或能力,如支持的最大QP数量、CQ数量等。
- 参数:RDMA设备上下文指针以及一个用于存储查询结果的结构体指针。
- 返回值:返回查询结果结构体中的信息,以及可能的错误码。
- 使用场景:在打开RDMA设备后,应用程序可能需要查询设备的属性或能力,以便根据这些信息配置后续的RDMA操作。
4. ibv_alloc_pd()
- 功能:为RDMA设备分配一个保护域(PD)。
- 参数:RDMA设备上下文指针。
- 返回值:成功时返回一个指向新分配的PD的指针,失败时返回NULL。
- 使用场景:在RDMA通信中,PD用于保护内存区域,防止未经授权的访问。因此,在创建QP之前,应用程序需要为RDMA设备分配一个PD。
5. ibv_create_cq()
- 功能:创建一个完成队列(CQ),用于存储已完成的工作请求。
- 参数:RDMA设备上下文指针、CQ的最小深度、CQ上下文指针等。
- 返回值:成功时返回一个指向新创建的CQ的指针,失败时返回NULL。
- 使用场景:在RDMA通信中,CQ用于存储已完成的工作请求,以便应用程序可以轮询并处理这些请求。因此,在创建QP之前,应用程序需要创建一个CQ。
6. ibv_create_qp()
- 功能:创建一个Queue Pair(QP),用于RDMA通信的数据传输。
- 参数:PD指针、QP初始化属性结构体指针等。
- 返回值:成功时返回一个指向新创建的QP的指针,失败时返回NULL。
- 使用场景:QP是RDMA通信的核心对象,用于数据的发送和接收。因此,在RDMA通信之前,应用程序需要创建一个QP。
7. ibv_reg_mr()
功能:ibv_reg_mr
函数用于注册一个内存区域(Memory Region,MR),使其可用于RDMA通信。注册后的内存区域会获得一个本地键(lkey)和一个远程键(rkey),这些键在RDMA操作中用于标识和访问内存区域。
参数:
pd
:指向保护域(Protection Domain,PD)的指针,该保护域用于保护注册的内存区域。addr
:要注册的内存区域的起始地址。length
:要注册的内存区域的长度。access_flags
:指定对内存区域的访问权限,如读、写、远程访问等。
返回值:
成功时,返回一个指向已注册内存区域的句柄(包含lkey和rkey);失败时,返回NULL。
8.1. ibv_modify_qp(INIT)
功能:
ibv_modify_qp
函数用于修改Queue Pair(QP)的状态。当使用INIT
作为参数时,该函数将QP从初始状态(通常是在创建后)修改为初始化(INIT)状态。在INIT状态下,QP可以进行进一步的配置,但还不能进行数据传输。
参数:
qp
:指向要修改的QP的指针。attr
:指向一个结构体,该结构体包含要修改的QP属性,如QP类型、发送和接收队列的大小等。对于INIT状态,主要关注的是QP类型的设置。attr_mask
:一个位掩码,用于指定attr
结构体中哪些字段是有效的。
返回值:
成功时返回0;失败时返回错误码。
8.2. ibv_modify_qp(RTR)
功能:
当使用RTR
(Ready To Receive)作为参数时,ibv_modify_qp
函数将QP从INIT状态修改为RTR状态。在RTR状态下,QP已经准备好接收数据,但还不能发送数据。这通常是在QP的双方都已经完成初始化并建立了连接之后进行的。
参数:
与ibv_modify_qp(INIT)
相同,但attr
结构体中的字段会根据RTR状态的要求进行设置,如远程QP的地址和QP号等。
返回值:
成功时返回0;失败时返回错误码。
8.3. ibv_modify_qp(RTS)
功能:
当使用RTS
(Ready To Send and Receive)作为参数时,ibv_modify_qp
函数将QP从RTR状态(或其他适当的状态)修改为RTS状态。在RTS状态下,QP已经准备好进行数据的发送和接收。
参数:
与ibv_modify_qp(INIT)
和ibv_modify_qp(RTR)
相同,但attr
结构体中的字段会根据RTS状态的要求进行设置。对于RTS状态,通常不需要额外的配置,因为QP已经通过前面的步骤进行了初始化和连接。
返回值:
成功时返回0;失败时返回错误码。
9. ibv_post_send() / ibv_post_recv()
- 功能:分别用于发布发送和接收工作请求到QP的发送和接收队列中。
- 参数:QP指针、工作请求结构体指针等。
- 返回值:成功时返回0,失败时返回错误码。
- 使用场景:在RDMA通信中,应用程序通过发布发送和接收工作请求来启动数据传输。这些工作请求会被RDMA硬件处理,并在完成后将相应的事件放入CQ中。
10. ibv_poll_cq()
- 功能:轮询CQ以检查是否有已完成的工作请求。
- 参数:CQ指针、要检查的完成事件数量、存储完成事件的结构体指针等。
- 返回值:返回已完成的工作请求数量,以及可能的错误码。
- 使用场景:在RDMA通信中,应用程序需要不断地轮询CQ以获取已完成的工作请求,并据此进行后续处理。
11. ibv_destroy_qp() / ibv_dereg_mr() / ibv_destroy_cq() / ibv_close_device()
- 功能:分别用于销毁QP、注销MR、销毁CQ和关闭RDMA设备。
- 参数:要销毁或关闭的对象指针。
- 返回值:成功时返回0,失败时返回错误码。
- 使用场景:在RDMA通信结束后,应用程序需要销毁不再使用的QP、注销不再需要的MR、销毁CQ并关闭RDMA设备,以释放相关资源。
12.ibv_free_device_list
用于释放之前通过 ibv_get_device_list
函数获取的设备列表所占用的资源。
四、常见函数调用流程
五、RDMA verbs编程相关名词解释
RC、UC、UD
- 可靠连接(RC)一个QP只和另一个QP相连,消息通过一个QP的发送队列可靠地传输到另一个QP的接收队列。数据包按序交付,RC连接很类似于TCP连接。
- 不可靠连接(UC)一个QP只和另一个QP相连,连接是不可靠的,所以数据包可能有丢失。传输层出错的消息不会进行重传,错误处理必须由高层的协议来进行。
- 不可靠数据报(UD)一个 QP 可以和其它任意的 UD QP 进行数据传输和单包数据的接收。不保证按序性和交付性。交付的数据包可能被接收端丢弃。支持多播消息(一对多),UD连接很类似于UDP连接。
-
QPN(Queue Pair Number):
- QPN指的是队列对编号,在InfiniBand网络中,每个队列对(Queue Pair,QP)都有一个唯一的编号。
- QP是InfiniBand通信的基本单位,用于在两个节点之间发送和接收消息。
- QPN在建立连接时用于标识要连接的队列对。
-
PSN(Packet Sequence Number):
- PSN指的是数据包序列号,用于确保消息的可靠传输。
- 在InfiniBand的可靠连接(RC)传输类型中,每个发送的数据包都会分配一个唯一的序列号。
- 接收端会使用这些序列号来确认消息的接收顺序,并检测任何丢失或重复的数据包。
-
GID(Global Identifier):(可理解为IP地址)
- GID指的是全局标识符,在InfiniBand和RoCE网络中,GID用于标识网络中的每个节点。
- GID通常与节点的MAC地址或IP地址相关联,并用于在以太网中进行通信。
- 在RoCE网络中,GID表存储了网卡的GID信息,应用程序可以根据GID获取MAC地址和IP地址。
-
LID(Local Identifier):(可理解为MAC地址)
- LID指的是本地标识符,在InfiniBand网络中,LID用于标识网络中的每个本地节点或交换机端口。
- 然而,在RoCE网络中,LID并不使用,因为RoCE是基于以太网的,它使用GID和标准的以太网地址来进行通信。
- 在InfiniBand网络中,LID是由子网管理器(Subnet Manager)分配的,并用于在子网内部进行路由。
参考资料:
Linux高性能网络详解,从DPDP、RDMA到XDP