0%

RDMA 基础概念

参考:

  1. https://zhuanlan.zhihu.com/p/164908617
  2. Linux Kernel Networking - Implement and Theory.

内存保护机制

Memory Key

IB 规范相关章节: 3.5.3 Memory Keys介绍

1
2
3
4
       Memory Key (32-bit)
+----------------+------------+
| Index(24-bit) | Key(8-bit) |
+----------------+------------+
  • Index: 用于 HCA 快速索引到本地的 VA-PA 映射等 MR 相关的信息
  • Key: 用于校验整个字段的合法性,以防止未授权的用户任意传递 Index

Memory Key 按照用途分为两种: Local Key, Remote Key

Local Key (L_Key)

Local Key 关联到一个 MR 上,用于 HCA 访问本端内存

当本端的某个进程试图使用一个已经注册的 MR 的内存时

  • HCA 校验其传递的 L_Key 中的 key
  • HCA 利用 L_Key 中的 index 查找 VA-PA 映射进行虚拟地址转换

用户在填写 WR 时,如果需要 HCA 访问本端内存,那么就需要通过一个 sge 的链表(sgl)来描述内存块

  • 此处的 sge 的 key 是 L_Key
  • 如果没有 L_Key,那么任何一个本地用户进程都可以指挥硬件访问其他本地用户注册的 MR 的内容,硬件也无法查找到相应的 PTE

Remote Key (R_Key)

Remote Key 关联到一个 MR 或 MW 上,用于远端节点访问本端内存

当远端节点试图访问本端的内存时

  • 本端 HCA 校验 R_Key 是否合法
  • 本端 HCA 利用 R_Key 中的 index 查找 VA-PA 映射进行虚拟地址转换

凡是 RDMA 操作(Write/Read/Atomic),用户都要在 WR 中携带远端内存区域的 R_Key

Memory Region (MR)

Memory Region (MR) 是由 RDMA 软件层在内存中规划出的一片区域,用于存放收发的数据。IB 协议中,用户在申请完用于存放数据的内存区域之后,都需要通过调用 IB 框架提供的 API 注册 MR,才能让 RDMA 网卡访问这片内存区域。

RDMA 硬件为 HCA(Host Channel Adapter, 宿主通道适配器),IB 协议中对其的定义是 “处理器和 I/O 单元中能够产生和消耗数据包的 IB 设备”

主要作用:

  1. 实现虚拟地址与物理地址转换
    • WQE 中保存以及传递的地址都是 VA (直接来自用户空间的 VA)
    • WQE 中直接把 VA 传递给 HCA (既包括本端的源 VA ,也包括对端的目的 VA)
    • 本端和对端的 HCA 都具有进行虚拟地址转换的功能,而不需要经过 CPU 的 IOMMU/SMMU
  2. 控制 HCA 访问内存的权限
    • 用户注册 MR 时会产生两把密钥: L_KEY (Local Key) 和 R_KEY (Remote Key) ,将分别用于保障对于本端和远端内存区域的访问权限
    • 在进行真正的 RDMA 通信之前会先进行简单的握手交换 RDMA 通信必需的基本信息(Socket 连接或 CM 连接),包括 VA, QPN,Key 等
  3. 避免换页
    • HCA 在注册 MR 时会锁住页表中相应的虚拟地址映射,避免被换出到磁盘,防止 HCA 在虚拟地址转换时物理页面失效

MR Context:
类似于 QPC/CQC/SRQC 等记录每个 MR 的相关信息,但没有统一的命名:

  • Mellanox 叫 Mkey Context
  • 海思叫 MPT(Memory Protection Table)

包含了硬件通过 VA 访问 MR 所需的全部信息,比如 PD, 起始 VA , 长度, L_Key, R_Key, 权限以及页表的相关信息等

因此 MR 的实体包括:

  • 从用户的视角看,MR 是一个可以被 HCA 访问的虚拟地址连续的内存空间
  • 从驱动程序和硬件的视角看,MR 包含了 MRC, 特殊的页表以及若干分散到物理内存空间中的页面

MR 注册

  1. 从用户态陷入内核态
  2. 调用内核提供的函数 pin 住内存(防止换页)
  3. 创建 VA-PA 映射下发给硬件

MR 进行虚拟地址转换

MR 虚拟地址空间是连续的,因此只需要记录 MR 的起始虚拟地址, 以及 MR Buffer 所映射到的所有物理页面(可能不连续)

地址计算如下:

1
2
3
4
offset = target_va - start_va
index = offset / page_size
offset_in_page = offset % page_size
target_pa = page[index]_pa + offset_in_page

在常规的 4KB 页面下:

1
2
3
4
offset = target_va - start_va
index = offset[63:12];
offset_in_page = offset[11:0];
target_pa = page[index]_pa + offset_in_page

每次注册 MR ,驱动程序都要重新构建一个新的特殊页表并告知硬件

MR 修改

注册之后修改 MR 需要:重注册 MR (Reregister MR) 接口

  • 等价于先取消注册 MR (Deregister MR) ,然后注册 MR (Register MR)
  • 需要陷入内核态完成,耗时较长

Memory Window

IB 规范相关章节

  • 9.4.1.1 Invalidate操作
  • 10.6.7 权限管理
  • 11.2.10.9~12 相关Verbs介绍

由用户申请的,用于让远端节点访问本端内存区域的 RDMA 资源

  • 每个 MW 都会绑定在一个已经注册的 MR 上
  • 相比于 MR , MW 可以提供更灵活的权限控制
  • 一个 MR 上可以划分出很多 MW ,每个 MW 都可以设置自己的权限

MW 在创建好之后,可以动态的绑定到一个已经注册的 MR 上,并同时设置或者更改 MW 的访问权限

MR/MW 的权限配置

基础权限:

Local Node Remote Node
Read Local Read Remote Read
Write Local Write Remote Write
Atomic Local Atomic Remote Atomic
  • Local Read 权限
    • 用户必须赋予 MR/MW 的权限
    • 权限最低
  • Local Write 权限
    • 如果某个 MR 需要配置 Remote Write 或 Remote Atomic,那么也一定要配置 Local Write 权限
  • 除此之外,每个 MR 或者 MW 都可以按需配置权限

MR 与 MW 的权限关系:

  • 如果想要给 MW 配置 Remote Write 或者 Remote Atomic 权限,那么它绑定到的 MR 必须有 Local Write 权限
  • 其他情况下两者权限互不干扰
    • 远端用户用 MW,就要遵循 MW 的权限配置
    • 远端用户用 MR ,就要遵循 MR 的权限配置

MW 的类型

Type 1 MW Type 2A MW Type 2B MW
关联关系 PD QP PD + QP
key 管理 驱动 + 硬件 用户 用户
Bind 方式 Bind MW Post Send Bind MW WR Post Send Bind MW WR
支持零长度 Y N N
支持 Invalidation N Y Y
关联的 QP 是否可以被销毁 - N Y
  • HCA 必须实现 Type 1 MW
  • HCA 可以仅选择实现 Type 2A 和 2B 中的一种
  • Type 1 和 Type 2 的 MW 可以同时关联到同一个 MR 上

Type 1 MW

Type 1 MW 的 R_Key 的 key 域段由驱动和硬件管理 (因此不能被执行 Invalidate 操作)

  • Type 1 MW 通过 PD 和一个 QP 关联,不会绑定到一个 QP 上,所以也不会影响销毁同一个 PD 下的 QP
  • 如果 Type 1 MW 的用户想要使一个 R_Key 失效,那么重新通过 Bind MW 接口绑定一次这个 MW ,硬件或者驱动就会自动分配一个新的 R_Key 的 key 域段,原有的 R_Key 相应也失效
  • 如果用户暂时想要使一个 MW 不再绑定到任何 MR,但是又想保留相关的资源而不是销毁这个 MW,那么可以通过调用 Bind MW 接口,并将 MW 长度设置为 0

Type 2 MW

Type 2 MW 的 R_Key 的 key 域段由用户管理

  • 用户通过 Post Send Bind MW WR 操作来进行绑定(并不会返回R_Key)
  • 用户必须保存 Allocate MW 时的 index,并且自己选择 8 bit key 组成 R_Key 并发送给对端
  • 用户可以通过 Invalidate 操作来使一个 R_Key 无效
  • 如果想要分配一个新的 R_Key 到 MW 上,必须先通过 Invalidate 操作无效之前的 R_Key
  • Type 2 MW 不支持 0 长度的绑定

根据绑定关系不同,Type 2 还可以分为两种实现方式:

  • Type 2A
    • 通过 QPN 和一个 QP 关联
    • 远端访问这个 MW 范围内的内存时,除了 R_Key 外,还必须指定正确的 QPN
    • 如果一个 QP 上还有绑定的 Type 2A MW,那么这个 QP 不可以被销毁
  • Type 2B
    • 通过 QPN 和 PD 与一个 QP 关联
    • 远端通过 RDMA 操作访问 MW 的内存时,除了 QPN 要正确之外,其指定的本端 QP 的 PD 要与绑定这个 MW 时的 PD 相同
    • QP 如果还有 Type 2B MW 绑定关系时是可以被销毁的

用户接口

控制接口

  • Allocate MW
    • 创建 MW 相关的软件结构和让硬件做好准备
    • 用户需要指定 MW 的类型
    • 这个接口会产生一个 Memory Window 的句柄,用户以后可以用这个句柄指代这个 MW
    • 此时 MW 没有绑定到 MR 上,处于不可从远端访问的状态
  • Deallocate MW: 销毁 MW 相关资源
  • Query MW: 查询 MW 的信息,包括 R_Key 及其状态, MW 类型以及 PD 等

数据接口

Bind: 将一个 MW 关联到一个已经注册的 MR 的指定范围上,并配置一定的读写权限
  • 绑定的结果会产生一个 R_key,用户可以把这个 R_Key 传递给远端节点用于远程访问
  • 一个 MW 可以被多次绑定
  • 一个 MR 上也可以绑定多个 MW
  • 如果一个 MR 还有被绑定的 MW,那么这个 MR 不能被取消注册
  • 有两种方式
    1. 调用 Post Send 接口 下发 Bind MW WR
      • 用户通过 post send 接口下发一个 WR 到 SQ 中
      • 这个 WR 的操作类型被指定为 BIND MW
      • WR 中还携带有权限和要绑定到的 MR 的范围信息
      • 下发 Bind MW 的 WR 之后,硬件并不会发送任何数据包,而是将 MW 绑定到了指定 MR 上
    2. 调用 Bind MW 接口
      • 独立的接口
      • 实际是在 Post Send Bind MW WR 外面封装了一层
      • 仅适用于 Type 1 MW
Invalidate: 用户通过下发一个带有 Invalidate 操作码的 WR 到硬件而使一个 R_Key 无效
  • 操作的对象是 R_Key 而不是 MW 本身
  • Invalidate 之后,远端用户无法再使用这个 R_Key 访问对应的 MW
  • MW 资源仍然存在,以后仍然可以生成新的 R_Key 给远端使用
  • 只能用于 Type 2 MW
  • 有两种
    1. Local Invalidate 本地无效操作
    2. Remote Invalidate 远端无效操作
Local Invalidate 本地无效操作

  • 在不回收 MW 资源的情况下,收回某个远端的用户的 R_Key 的权限
  • 用户需要下发一个 Local Invalidate 操作到 SQ 中,硬件收到之后会对相应的 MW 的配置进行修改
  • 成功执行之后,如果持有这个 R_Key 的远端用户想要对 MW 进行 RDMA 操作,将会被本地的硬件拒绝并返回错误
  • 硬件收到这个 WR 之后不会发送消息到链路上
Remote Invalidate 远端无效操作

  • 当一个远端用户不再使用一个 R_Key 之后,可以主动发送消息,让本端回收这个 R_Key
  • 远端用户下发一个带有此操作码的 WR 到 SQ 中,其硬件组装报文并发送到本端
  • 本端硬件收到远端的 Remote Invalidate 操作之后,将会把对应的 R_Key 置为不可用状态
  • 此后远端将无法使用这个 R_Key 对对应的 MW 进行 RDMA 操作

Protection Domain (PD)

IB 协议中相应的内容:

  • 3.5.5 PD的基本概念和作用
  • 10.2.3 介绍了PD和其他一些RDMA资源的关系,以及PD相关的软件接口。
  • 10.6.3.5 再次强调PD和MR及QP的关系。
  • 11.2.1.5 详细介绍PD的Verbs接口,包括作用、入参、出参和返回值等。
  • 本地节点才有的概念,由本地的用户负责创建,每个节点上的 RD 对其他节点不可见
  • 每个节点都至少要有一个 PD
  • 每个 QP 都必须属于一个 PD ,每个 MR 也必须属于一个 PD

基础资源

Queue Pair (QP)

IB 协议中的相关内容:

  • 3.5.1 10.2.4 QP的基本概念
  • 10.3 QP 状态机
  • 10.2.5 QP相关的软件接口
  • 11.4 Post Send Post Recv

QP 之间互相独立,彼此通过 PD 隔离

支持服务类型:RC, UD, RD, UC (所有的源 QP 和目的 QP 必须为同一种类型才能进行数据交互)

  • 硬件上: QP 是一段包含着若干个 WQE 的存储空间,IB 网卡会从这段空间中读取 WQE 的内容,并按照 WQE 的要求去内存中存取数据
    • 关于 QP 存储空间的实现: IB 协议未做出限制,可以是内存空间或 IB 网卡的片内存储空间
  • 软件上: QP 是一个由 IB 网卡的驱动程序所维护的数据结构,其中包含 QP 的地址指针以及一些相关的软件属性

Queue Pair Context (QPC):
用于存储 QP 相关属性(用于控制硬件行为)

  • 由软件(驱动程序)申请 QPC 的内存,硬件通过 QPC 地址指针访问

Queue Pair Number (QPN): 每个 QP 的编号

  • IB 协议中规定用 24 bit 来表示 QPN ,即每个节点最大可以同时使用 $2^{24}$ 个 QP
  • 每个节点维护各自的 QPN 集合
  • 特殊的 QPN:
    • QP0: 用于子网管理接口 SMI(Subnet Management Interface),用于管理子网中的全部节点
    • QP1: 用于通用服务接口GSI(General Service Interface)
      • GSI 是一组管理服务,其中最出名的就是 CM (Communication Management), 是一种在通信双方节点正式建立连接之前用来交换必须信息的一种方式

用户层管理

Verbs 接口是 RDMA 对上层应用的 API

应用层 QP 控制操作

  • Create QP:
    创建一个 QP 的软硬件资源,包含 QP 本身以及 QPC 。用户创建时会写传入一系列的初始化属性,包含该 QP 的服务类型,可以储存的 WQE 数量等信息
  • Destroy QP:
    释放一个 QP 的全部软硬件资源,包含 QP 本身及 QPC。销毁 QP 后,用户将无法通过 QPN 索引到这个 QP。
  • Modify QP:
    修改一个 QP 的某些属性,比如 QP 的状态,路径的 MTU 等等。这个修改过程既包括软件数据结构的修改,也包括对 QPC 的修改。
  • Query QP:
    查询一个 QP 当前的状态和一些属性,查询到的数据来源于驱动以及 QPC 的内容。

这些操作都有相应的函数声明。

应用层 QP 数据操作

在行为上都是软件向 QP 中填写一个 WQE (对应用层来说叫 WR, Work Request),请求硬件执行一个动作

  • Post Send Request : 通信发起方
    • WQE/WR 可以是 Send 操作,RDMA Write 操作以及 RDMA Read 操作等
  • Post Receive Request
    • 接收端需要提前准备好接收数据的缓冲区,并将缓冲区地址等信息以 WQE 的形式告知硬件

QP 状态机

  • RST(Reset)复位状态
    • 当一个 QP 通过 Create QP 创建好之后就处于这个状态,相关的资源都已经申请完毕
    • 但是这个 QP 目前什么都做不了,其无法接收用户下发的 WQE,也无法接受对端某个 QP 的消息。
  • INIT(Initialized)已初始化状态
    • 用户可以通过 Post Receive 给这个 QP 下发 Receive WR
    • 接收到的消息并不会被处理,会被静默丢弃
    • 如果用户下发了一个 Post Send 的 WR,则会报错
  • RTR(Ready to Receive)准备接收状态
    • 在 INIT 状态的基础上,RQ 可以正常工作,
      即对于接收到的消息,可以按照其中 WQE 的指示搬移数据到指定内存位置
    • 此状态下 SQ 仍然不能工作
  • RTS(Ready to Send)准备发送状态
    • 在 RTR 基础上,SQ 可以正常工作,
      即用户可以进行 Post Send,并且硬件也会根据 SQ 的内容将数据发送出去
    • 进入该状态前,QP 必须已与对端建立好链接
  • SQD(Send Queue Drain): SQ 排空状态
    • 该状态会将 SQ 中现存的未处理的 WQE 全部进行处理
    • 用户依旧可以下发新的 WQE,但是这些 WQE 要等到旧的 WQE 全处理之后才会被处理。
  • SQEr(Send Queue Error): SQ 错误状态
    • 当某个 Send WR 发生完成错误(即硬件通过 CQE 告知驱动发生的错误)时,QP 进入此状态
  • ERR(Error)错误状态
    • 其他状态如果发生了错误,都可能进入该状态
    • Error 状态时,QP 会停止处理 WQE,已经处理到一半的 WQE 也会停止
    • 上层需要在修复错误后再将 QP 重新切换到 RST 的初始状态

Complete Queue (CQ)

IB 协议相关章节:

  • 9.9 CQ 错误检测和恢复
  • 10.2.6 CQ 和 WQ 的关系
  • 10.10 错误类型及其处理
  • 11.2.8 CQ 相关控制面接口
  • 11.4.2 CQ 相关数据面接口

作用和 WQ (SQ 和 RQ) 相反,硬件都会产生一个 CQE 放在 CQ 中 (WC)来告诉软件某个 WQE/WR 的完成情况

Complete Queue Context (CQC):

  • 包含了 CQ 的容量大小,当前处理的 CQE 的序号等等信息
  • 同 QPC 类似,需要驱动程序申请内存存储 CQC, 提供给网卡使用

Complete Queue Number (CQN):
CQ 的编号,用来区别不同的 CQ, CQ 没有像 QP0 和 QP1 一样的特殊保留编号

在不同服务类型中产生时机

可靠服务类型(RC)

  • SEND
    • SEND 操作需要硬件从内存中获取数据,然后组装成数据包通过物理链路发送到对端,对端硬件收到数据并校验之后,会回复 ACK 包给发送方
    • 对端(接收方)产生 CQE 表示对端已准确无误的收到数据
    • 本端(发送方)收到对端的 ACK 之后才会产生 CQE
  • RECV
    • 硬件将收到的数据放到用户 WQE 中指定的内存区域,完成校验和数据存放动作后,硬件才会产生 CQE
  • WRITE
    • 本端:硬件会从内存中取出数据,并等待对端回复 ACK (由对端网卡负责回复)后,才会产生 CQE
    • 对端:不感知本端的 Write 操作,不产生 CQE (事实上也不存在相应的 WQE)
  • READ
    • 硬件将收到的数据放到用户 WQE 中指定的内存区域,完成校验和数据存放动作后,硬件产生 CQE
    • 对端不感知本端的 READ 操作,不产生 CQE(事实上也不存在相应的 WQE)

不可靠服务类型(UD)

不可靠的服务类型没有重传和确认机制,所以产生 CQE 表示硬件已经将对应 WQE 指定的数据发送出去了

UD 只支持 SEND-RECV 操作,不支持 RDMA 操作

  • SEND: 本端将数据发送出去之后,就会产生 CQE
  • RECV: 本端接收到数据之后,就会产生 CQE

WQ 与 CQ 的关系

  • 每个 WQ 都必须关联一个 CQ
    • 一个 WQ 的所有 WQE 对应的 CQE ,都会被硬件放到绑定的 CQ 中
    • 同属于一个 QP 的 SQ 和 RQ 可以各自关联不同的 CQ
  • 每个 CQ 可以关联多个 SQ 和 RQ
  • 同一个 WQ 中的 WQE,其对应的 CQE 间是 in-order
    • 但使用 SRQ 的情况以及 RD 服务类型的 RQ 这两种情况是不保序的
  • 不同 WQ 中的 WQE ,其对应的 CQE 间不保证 in-order

Completion Error

IB 协议中规定了三种错误类型:

  1. 立即错误(Immediate Error)
    • 处理:立即停止当前操作,并返回错误给上层用户
  2. 完成错误(Completion Error)
    • 处理:通过 CQE 将错误信息返回给上层用户
  3. 异步错误(Asynchronous Errors)
    • 处理:通过中断事件的方式上报给上层用户

错误检测的时机

Requestor:

  1. 本地错误检测: 对 SQ 中的 WQE 进行检查
    • 如果检测到错误,就从本地错误检查模块直接产生 CQE 到 CQ ,不会发送数据到响应端
    • 如果没有错误,则发送数据到对端
  2. 远端错误检测: 检测响应端的 ACK 是否异常 (只存在于连接的服务类型)
    • ACK/NAK 是由对端的本地错误检测模块检测后产生的,里面包含了响应端是否有错误,以及具体的错误类型
    • 无论远端错误检测的结果是否有问题,都会产生 CQE 到 CQ 中

Responder: 只会进行本地错误检测 (实际上检测的是对端报文是否有问题) (只存在于连接的服务类型)
如果检测到错误,则会体现在 ACK/NAK 报文中回复给对端,以及在本地产生一个CQE

常见 Completion Error

IB 协议: 10.10.3 EFFECTS OF ERRORS ON QP SERVICE TYPES

RC 服务类型
  • Table 81 Completion Error Handling for RC Send Queues
  • Table 82 Completion Error Handling for RC Receive Queues

for RC Send Queues:

  • Local Protection Error (本地保护域错误)
    本地 WQE 中指定的数据内存地址的 MR 不合法,即用户试图使用一片未注册的内存中的数据
  • Remote Access Error (远端权限错误)
    本端没有权限读/写指定的对端内存地址
  • Transport Retry Counter Exceeded Error (重传超次错误)
    对端一直未回复正确的 ACK ,导致本端多次重传,超过了预设的次数

for RC Receive Queues:

  • Local Access Error (本地访问错误)
    说明对端试图写入其没有权限写入的内存区域
  • Local Length Error (本地长度错误)
    本地 RQ 没有足够的空间来接收对端发送的数据

用户接口

CQ 控制操作

  • Create CQ:
    • 用户必须指定 CQ 的大小,即能够储存多少个 CQE
    • 用户还可以填写一个 CQE 产生后的回调函数指针
    • 内核态驱动会将其他相关的参数配置好,填写到跟硬件约定好的 CQC 中告知硬件
  • Destroy CQ: 释放一个 CQ 软硬件资源,包含 CQ 本身及 CQC, CQN 也将失效
  • Resize CQ: 修改 CQ 的大小
  • Query CQ: 查询 CQ 的当前大小,以及用于通知的回调函数指针

CQ 数据操作

  • Poll completion queue
    • 用户调用这个接口之后,CPU 就会定期去检查 CQ 里面是否有新的CQE
    • 如果有的话,就取出这个 CQE, 解析其中的信息并返回给上层用户
  • Request completion notification
    • 用户调用这个接口之后,相当于向系统注册了一个中断
    • 当硬件将 CQE 放到 CQ 中后,会立即触发一个中断给 CPU,CPU 进而就会陷入中断取出 CQE,处理后返回给用户

Shared Receive Queue (SRQ)

IB 协议相关章节:

  • 10.2.9 SRQ的设计思想以及相关操作
  • 10.2.3 SRQ和QP的PD
  • 10.8.2 关联SRQ的QP和不使用SRQ的QP的关系
  • 10.8.5 SRQ相关的返回WC
  • 11.5.2.4 异步事件

SRQ 是 IB 协议为了给接收端节省资源而设计的。把一个 RQ 共享给所有关联的 SQ 使用,这个公用的 RQ 就称为 SRQ。

  • 当与其关联的 SQ 想要下发接收 WQE 时,都填写到这个 SRQ 中
  • 当硬件接收到数据后,就根据 SRQ 中的下一个 WQE 的内容把数据存放到指定位置

SRQ 的必要性:

  • 通常情况下,向 SQ 中下发的任务的数量要远远超过向 RQ 中下发任务的数量
    • SEND/WRITE/READ 都需要通信发起方向 SQ 中下发一个 WR ,而只有和 SEND 配合的 RECV 操作才需要通信响应方下发 WR 到 RQ 中
    • SEND-RECV 操作通常都是用于传递控制信息,WRITE/READ 才是进行大量远端内存读写操作时的主要使用的操作
  • SRQ 可以节省内存
    • 大部分时候有大量的 RQ WQE 处于空闲状态未被使用
    • 如果使用较小的 RQ , 对于 RC 服务类型可以利用流控机制来反压发送方,但会增加通信延时

SRQ WQE: 由多个 sge (Scatter/Gather Element) 组成

  • 每个 sge 指向一段内存区域,用于指定发送/接收数据区域
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
      SRQ WQE                       sge1
    +------+ +------+-------------+-----+
    | sge1 | --------> | addr | length(32b) | key |
    +------+ +------+-------------+-----+
    | sge2 | --------> ...
    +------+
    | ... |
    +------+
    | sgen |
    +------+
  • SRQ 中的每个 WQE 不关联到任何一个 QP ,硬件按队列顺序依次取出 WQE 就把数据放到里面了

Shared Receive Queue Context (SRQC):
用来告知硬件跟 SRQ 有关的属性的,包括深度、WQE 大小等信息

Shared Receive Queue Number (SRQN):
同 QP 一样,每个节点中可能存在多个 SRQ ,SRQN 用于标识和区分这些 SRQ

SRQ PD:

  • 每个 SRQ 都必须指定一个自己的 PD,可以跟自己关联的 QP 的 PD 相同,也可以不同
  • 如果在使用 SRQ 的时候,收到了 Send 数据包,那么只有在要访问的 MR 和 SRQ 处于同一个 PD 下,才会正常接收这个数据包,否则会产生立即错误
  • 如果收到的是 Write/Read ,此时比较的仍然是 QP 和 MR 的 PD

异步事件: SRQ Limit Reached

SRQ 可以设置一个阈值(SRQ Limit),当队列中剩余的 WQE 数量小于阈值时,SRQ 会就上报一个异步事件(SRQ Limit Reached)通知用户下放更多的 WQE

  • 上报异步事件之后,SRQ Limit 的值会被硬件重新设置为0
    • 为了防止一直上报异步事件给上层
  • 用户可以不使用这个机制: 只需要将 SRQ Limit 的值设为 0

用户接口

控制接口

  • Create SRQ
    • 创建 SRQ 的时候,跟 QP 一样会申请所有 SRQ 相关的软硬件资源
      • 驱动程序会申请 SRQN ,申请 SRQC 的空间并向其中填写配置
    • 创建 SRQ 时还必须指定每个 SRQ 的深度(能存放多少 WQE )以及每个 WQE 的最大 sge 数量
  • Destroy SRQ: 销毁 SRQ 的所有相关软硬件资源
  • Modify SRQ: 设置 SRQ 深度,SRQ Limit 等属性
  • Query SRQ: 查询 SRQ 的深度和 SRQ Limit 等属性

数据接口

Post SRQ Receive:
跟 Post Receive 一样,就是向 SRQ 中下发接收 WQE ,里面包含了作为接收缓冲区的内存块的信息

SRQ 状态机

SRQ 只有非错误和错误两种状态

  • 无论是哪种状态下,用户都可以向 SRQ 中下发 WQE
  • 错误状态下
    • 相关联的 QP 不能从这个 SRQ 中获得收到的数据
    • 用户也无法查询和修改 SRQ 的属性
    • 只能通过销毁 SRQ 来退出错误状态

接收流程

  1. 用户创建 SRQ1 ,并创建 QP2 和 QP3 ,都关联到 SRQ1 上
  2. 用户通过 Post SRQ Recv 接口向 SRQ1 中下发两个接收 WQE
  3. 硬件收到数据
  4. 硬件发现是发给 QP3 的,从 SRQ1 中取出队列头的 WQE(WQE1),根据 WQE 内容存放收到的数据
  5. 硬件发现 QP3 的 RQ 关联的 CQ 是 CQ3 ,所以向其中产生一个 CQE
  6. 用户从 CQ3 中取出 CQE ,从指定内存区域取走数据
    • CQE 中有 wr_id 信息:指示数据放到哪个 WQE 指定的内存区域
  7. 硬件收到数据
  8. 硬件发现是发给 QP2 的,从 SRQ1 中取出队列头的 WQE(WQE2),根据 WQE 内容存放收到的数据
  9. 硬件发现 QP2 的 RQ 关联的 CQ 是 CQ2 ,所以向其中产生一个 CQE
  10. 用户从 CQ2 中取出 CQE ,从指定内存区域取走数据