概述
InfiniBand 是一种高性能计算机网络通信标准,具有极高的吞吐量和极低的延迟。本文介绍 InfiniBand/RDMA 编程中的关键概念及其相互关系。
核心概念
1. CA (Channel Adapter) - 通道适配器
CA 是 InfiniBand 网络接口卡(NIC),是硬件层面的概念。每个 CA 都有一个或多个端口(Port),用于连接到 InfiniBand 网络。
2. PD (Protection Domain) - 保护域
保护域是一个安全边界,用于将 QP(队列对)和 MR(内存区域)组织在一起。只有属于同一个 PD 的 QP 和 MR 才能相互操作,这提供了内存保护机制。
3. MR (Memory Region) - 内存区域
内存区域是一块经过注册的内存,网卡可以直接访问。每个 MR 包含:
- L_Key (Local Key): 本地访问密钥,用于本地 QP 访问本地 MR
- R_Key (Remote Key): 远程访问密钥,用于远程 QP 访问此 MR(通过 RDMA 操作)
4. QP (Queue Pair) - 队列对
队列对是 InfiniBand 通信的基本单位,由两个队列组成:
- SQ (Send Queue): 发送队列,用于发送数据
- RQ (Receive Queue): 接收队列,用于接收数据
每个 QP 必须属于一个 PD,并且可以关联多个 CQ。
5. CQ (Completion Queue) - 完成队列
完成队列用于接收工作请求(WR)的完成通知。当 WR 执行完成后,会在对应的 CQ 中生成一个完成事件(Completion Event)。
6. WR (Work Request) - 工作请求
工作请求是提交到 QP 的操作指令,包括:
- Send WR: 发送请求
- Receive WR: 接收请求
- RDMA Write WR: RDMA 写请求
- RDMA Read WR: RDMA 读请求
7. SGE (Scatter/Gather Elements) - 分散/聚集元素
SGE 描述了一个内存缓冲区的位置和大小,包含:
- 地址(Address)
- 长度(Length)
- L_Key(用于验证访问权限)
一个 WR 可以包含多个 SGE,实现分散/聚集 I/O。
注: SGE 专门用于描述本地散布的缓冲区, 即散布读写(Scatter read/write), 类似于 Linux 的 writev / readv 函数.
8. LID (Local Identifier) - 本地标识符
LID 是 InfiniBand 网络中每个端口的唯一标识符,用于路由数据包。
9. AH (Address Handle) - 地址句柄
地址句柄用于 UD (Unreliable Datagram) 传输类型,包含目标地址信息。每个 AH 属于一个 PD,用于在 UD QP 发送数据时指定目标地址。AH 包含:
- 目标 LID (Local Identifier)
- 服务级别 (Service Level)
- 路径位 (Path Bits)
- 全局路由头 (GRH) 信息(如果使用)
10. CM (Connection Manager) - 连接管理器
连接管理器负责建立和管理 QP 之间的连接,处理连接建立、断开等事件。
保护域(PD)资源组织结构图
以下 ASCII 图详细说明了 PD(保护域)内资源的结构和关系:
重要说明:图中 CQ 显示在 PD 内是为了展示逻辑关联关系。实际上:
- CQ 通过 Context 创建(
ibv_create_cq()),不属于任何 PD
- CQ 是 Context 级别的资源,可以被不同 PD 的 QP 共享
- 多个 QP(即使属于不同的 PD)可以关联到同一个 CQ
- 例如:PD 1 的 QP 1、QP 2 和 PD 2 的 QP 3、QP 4 可以共享同一个 CQ
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240
| ┌─────────────────────────────────────────────────────────────────────┐ │ Application Process │ └─────────────────────────────────────────────────────────────────────┘ │ │ ibv_open_device() ▼ ┌─────────────────────────────────────────────────────────────────────┐ │ Context │ └─────────────────────────────────────────────────────────────────────┘ │ │ ibv_alloc_pd() ▼ ┌─────────────────────────────────────────────────────────┐ │ │ ▼ ▼ ┌──────────────────────────────────────┐ ┌──────────────────────────────────────┐ │ PD 1 (Protection Domain) │ │ PD 2 (Protection Domain) │ │ │ │ │ │ Resources in PD 1: │ │ Resources in PD 2: │ │ │ │ │ │ ┌────────────────────────────────┐ │ │ ┌────────────────────────────────┐ │ │ │ MR 1 │ │ │ │ MR 3 │ │ │ │ L_Key: 0x01 R_Key: 0x81 │ │ │ │ L_Key: 0x05 R_Key: 0x85 │ │ │ └────────────────────────────────┘ │ │ └────────────────────────────────┘ │ │ │ │ │ │ ┌────────────────────────────────┐ │ │ ┌────────────────────────────────┐ │ │ │ MR 2 │ │ │ │ MR 4 │ │ │ │ L_Key: 0x02 R_Key: 0x82 │ │ │ │ L_Key: 0x06 R_Key: 0x86 │ │ │ └────────────────────────────────┘ │ │ └────────────────────────────────┘ │ │ │ │ │ │ ┌────────────────────────────────┐ │ │ ┌────────────────────────────────┐ │ │ │ AH 1 │ │ │ │ AH 3 │ │ │ │ Target LID, Service Level │ │ │ │ Target LID, Service Level │ │ │ └────────────────────────────────┘ │ │ └────────────────────────────────┘ │ │ │ │ │ │ ┌────────────────────────────────┐ │ │ ┌────────────────────────────────┐ │ │ │ AH 2 │ │ │ │ AH 4 │ │ │ │ Target LID, Service Level │ │ │ │ Target LID, Service Level │ │ │ └────────────────────────────────┘ │ │ └────────────────────────────────┘ │ │ │ │ │ │ ┌────────────────────────────────┐ │ │ ┌────────────────────────────────┐ │ │ │ QP 1 │ │ │ │ QP 3 │ │ │ │ Uses: MR 1/2, AH 1/2, CQ 1 │ │ │ │ Uses: MR 3/4, AH 3/4, CQ 1/2 │ │ │ └────────────────────────────────┘ │ │ └────────────────────────────────┘ │ │ │ │ │ │ ┌────────────────────────────────┐ │ │ ┌────────────────────────────────┐ │ │ │ QP 2 │ │ │ │ QP 4 │ │ │ │ Uses: MR 1/2, AH 1/2, CQ 1 │ │ │ │ Uses: MR 3/4, AH 3/4, CQ 1/2 │ │ │ └────────────────────────────────┘ │ │ └────────────────────────────────┘ │ │ │ │ │ └──────────────────────────────────────┘ └──────────────────────────────────────┘ │ │ │ Security Boundary │ Security Boundary └─────────────────────────────────────────────────────────┘ │ │ ▼ ┌───────────────────────────────────────────────────────────────────┐ │ CQ (Created via Context, NOT belonging to any PD) │ │ CQ can be shared by QP from different PDs! │ │ │ │ ┌──────────────────────────────────────────────────────────────┐ │ │ │ CQ 1 (Shared across PDs) │ │ │ │ Receives completions from: │ │ │ │ - QP 1 (PD 1) │ │ │ │ - QP 2 (PD 1) │ │ │ │ - QP 3 (PD 2) ← Cross-PD sharing │ │ │ │ - QP 4 (PD 2) ← Cross-PD sharing │ │ │ └──────────────────────────────────────────────────────────────┘ │ │ │ │ ┌──────────────────────────────────────────────────────────────┐ │ │ │ CQ 2 (Alternative: separate CQ for PD 2) │ │ │ │ Receives completions from: │ │ │ │ - QP 3 (PD 2) │ │ │ │ - QP 4 (PD 2) │ │ │ └──────────────────────────────────────────────────────────────┘ │ └───────────────────────────────────────────────────────────────────┘ │ │ Hardware Access ▼ ┌──────────────────┐ │ CA (NIC) │ └──────────────────┘
资源使用关系: ───────────────────────────── QP 1 (PD 1) ──使用──> MR 1, MR 2, AH 1, AH 2 QP 1 (PD 1) ──发送完成事件到──> CQ 1
QP 2 (PD 1) ──使用──> MR 1, MR 2, AH 1, AH 2 QP 2 (PD 1) ──发送完成事件到──> CQ 1
QP 3 (PD 2) ──使用──> MR 3, MR 4, AH 3, AH 4 QP 3 (PD 2) ──发送完成事件到──> CQ 1 (共享) 或 CQ 2
QP 4 (PD 2) ──使用──> MR 3, MR 4, AH 3, AH 4 QP 4 (PD 2) ──发送完成事件到──> CQ 1 (共享) 或 CQ 2
关键点:CQ 可以被不同 PD 的 QP 共享!
关键关系说明: ═══════════════════════════════════════════════════════════════════
1. PD 作为安全边界 ───────────────── • PD 1 和 PD 2 相互隔离,互不干扰 • PD 直接管理的资源:MR、QP、AH • CQ 通过 Context 创建,通过 QP 间接关联到 PD(QP 创建时指定 CQ) • 只有属于同一个 PD 的 QP 和 MR 才能相互操作 • PD 提供了内存和队列的访问控制机制
2. MR (Memory Region) - 内存区域 ────────────────────────────── • MR 1 和 MR 2 属于 PD 1 • MR 3 和 MR 4 属于 PD 2 • 每个 MR 都有唯一的 L_Key(本地密钥)和 R_Key(远程密钥) • MR 是 QP 可以访问的内存区域
3. QP (Queue Pair) - 队列对 ────────────────────────── • QP 1 和 QP 2 属于 PD 1 • QP 3 和 QP 4 属于 PD 2 • 每个 QP 包含: - SQ (Send Queue): 发送队列,用于发送数据 - RQ (Receive Queue): 接收队列,用于接收数据 • QP 只能访问同一 PD 内的 MR
4. CQ (Completion Queue) - 完成队列 ────────────────────────────────── • CQ 通过 Context 创建(`ibv_create_cq()`),不属于任何 PD • CQ 是 Context 级别的资源,可以被不同 PD 的 QP 共享 • 多个 QP(即使属于不同的 PD)可以关联到同一个 CQ • 示例: - CQ 1 可以同时接收 PD 1 的 QP 1、QP 2 和 PD 2 的 QP 3、QP 4 的完成事件 - 这种设计提供了灵活性,允许跨 PD 共享完成队列 • 注意:虽然图中 CQ 显示在 PD 内,但这是逻辑关联,CQ 本身不属于 PD
QP 如何将完成事件发送到 CQ: ──────────────────────────────── 1. QP 创建时关联 CQ: - 创建 QP 时,在 `ibv_qp_init_attr` 中指定 `send_cq` 和 `recv_cq` - 每个 QP 可以有不同的 send_cq 和 recv_cq,也可以使用同一个 CQ 2. WR 标志控制完成事件生成: - Send WR:设置 `IBV_SEND_SIGNALED` 标志,完成后在 send_cq 中生成 CQE - Receive WR:总是生成完成事件(在 recv_cq 中生成 CQE) - 未设置 SIGNALED 的 Send WR 不会生成完成事件 3. 硬件自动生成完成事件: - 当 WR 执行完成后,硬件(CA)自动在对应的 CQ 中生成 CQE(Completion Queue Entry) - CQE 包含:状态码、操作类型、WR ID、字节数等信息 - 应用程序通过 `ibv_poll_cq()` 轮询 CQ 获取完成事件 4. 完成事件流程: Application → ibv_post_send/recv() → QP (SQ/RQ) ↓ WR 执行完成 ↓ 硬件生成 CQE ↓ 写入到 CQ ↓ Application ← ibv_poll_cq()
5. AH (Address Handle) - 地址句柄 ──────────────────────────────── • AH 1 和 AH 2 属于 PD 1 • AH 3 和 AH 4 属于 PD 2 • AH 主要用于 UD (Unreliable Datagram) 传输类型 • UD QP 发送数据时,WR 必须包含 AH 来指定目标地址 • AH 包含目标 LID、服务级别等路由信息
6. WR (Work Request) - 工作请求 ────────────────────────────── • WR 提交到 QP 的 SQ 或 RQ • WR 包含一个或多个 SGE(Scatter/Gather Elements) • SGE 引用 MR,使用 L_Key 验证访问权限 • UD QP 的 Send WR 必须包含 AH 来指定目标地址
7. 访问规则 ───────── ✅ 允许:QP 1 → MR 1 (同一 PD) ✅ 允许:QP 1 → MR 2 (同一 PD) ✅ 允许:QP 2 → MR 1 (同一 PD) ✅ 允许:QP 2 → MR 2 (同一 PD) ✅ 允许:QP 3 → MR 3 (同一 PD) ✅ 允许:QP 3 → MR 4 (同一 PD) ✅ 允许:QP 4 → MR 3 (同一 PD) ✅ 允许:QP 4 → MR 4 (同一 PD) ✅ 允许:QP 1 → CQ 1 (CQ 可以被不同 PD 共享) ✅ 允许:QP 2 → CQ 1 (CQ 可以被不同 PD 共享) ✅ 允许:QP 3 → CQ 1 (CQ 可以被不同 PD 共享,跨 PD) ✅ 允许:QP 3 → CQ 2 (CQ 可以被不同 PD 共享) ✅ 允许:QP 4 → CQ 1 (CQ 可以被不同 PD 共享,跨 PD) ✅ 允许:QP 4 → CQ 2 (CQ 可以被不同 PD 共享) ✅ 允许:QP 1 → AH 1 (同一 PD,UD QP) ✅ 允许:QP 1 → AH 2 (同一 PD,UD QP) ✅ 允许:QP 2 → AH 1 (同一 PD,UD QP) ✅ 允许:QP 2 → AH 2 (同一 PD,UD QP) ✅ 允许:QP 3 → AH 3 (同一 PD,UD QP) ✅ 允许:QP 3 → AH 4 (同一 PD,UD QP) ✅ 允许:QP 4 → AH 3 (同一 PD,UD QP) ✅ 允许:QP 4 → AH 4 (同一 PD,UD QP) ❌ 禁止:QP 1 → MR 3 (不同 PD) ❌ 禁止:QP 1 → MR 4 (不同 PD) ❌ 禁止:QP 3 → MR 1 (不同 PD) ❌ 禁止:QP 3 → MR 2 (不同 PD) ❌ 禁止:QP 1 → AH 3 (不同 PD) ❌ 禁止:QP 1 → AH 4 (不同 PD) ❌ 禁止:QP 3 → AH 1 (不同 PD) ❌ 禁止:QP 3 → AH 2 (不同 PD)
8. 内存保护机制 ────────────── • L_Key: 用于本地 QP 访问本地 MR - QP 1 使用 L_Key 0x01 访问 MR 1 - QP 1 使用 L_Key 0x02 访问 MR 2 - SGE 中必须包含正确的 L_Key 才能访问 MR • R_Key: 用于远程 RDMA 操作 - 远程 QP 使用 R_Key 0x81 进行 RDMA Write/Read 到 MR 1 - 远程 QP 使用 R_Key 0x82 进行 RDMA Write/Read 到 MR 2 - RDMA 操作时,远程端必须提供正确的 R_Key
9. 资源创建顺序 ────────────── 1. 创建 Context (ibv_open_device) 2. 创建 PD (ibv_alloc_pd) 3. 注册 MR (ibv_reg_mr) - 需要 PD 4. 创建 CQ (ibv_create_cq) - 需要 Context 5. 创建 AH (ibv_create_ah) - 需要 PD(仅 UD QP 需要) 6. 创建 QP (ibv_create_qp) - 需要 PD 和 CQ 7. 提交 WR (ibv_post_send/recv) - 需要 QP 和 MR(UD QP 还需要 AH)
10. 实际应用场景 ────────────── • 多租户隔离:不同应用使用不同 PD,确保安全隔离 • 资源管理:同一应用的不同模块可以使用不同 PD • 权限控制:通过 PD 限制哪些 QP 可以访问哪些 MR • 性能优化:合理组织 PD 内的资源,减少跨 PD 访问开销
|
概念关系图
以下 Mermaid 图表展示了 InfiniBand 关键概念之间的关系:
graph TB
subgraph Hardware["硬件层"]
CA[CA
Channel Adapter
通道适配器]
Port[Port
端口]
end
subgraph Context["上下文层"]
Context_Obj[Context
上下文]
PD[PD
Protection Domain
保护域]
end
subgraph Memory["内存管理"]
MR[MR
Memory Region
内存区域]
LKey[L_Key
本地密钥]
RKey[R_Key
远程密钥]
Buffer[Buffer
缓冲区]
end
subgraph Queue["队列层"]
QP[QP
Queue Pair
队列对]
SQ[SQ
Send Queue
发送队列]
RQ[RQ
Receive Queue
接收队列]
CQ[CQ
Completion Queue
完成队列]
end
subgraph Operation["操作层"]
WR[WR
Work Request
工作请求]
SGE[SGE
Scatter/Gather Elements
分散/聚集元素]
SendWR[Send WR]
RecvWR[Receive WR]
RDMAWriteWR[RDMA Write WR]
RDMAReadWR[RDMA Read WR]
end
subgraph Network["网络层"]
LID[LID
Local Identifier
本地标识符]
CM[CM
Connection Manager
连接管理器]
end
%% Hardware relationships
CA --> Port
%% Context relationships
CA --> Context_Obj
Context_Obj --> PD
%% Memory relationships
PD --> MR
MR --> LKey
MR --> RKey
MR --> Buffer
%% Queue relationships
PD --> QP
QP --> SQ
QP --> RQ
QP --> CQ
CQ --> QP
%% Operation relationships
SQ --> WR
RQ --> WR
WR --> SGE
SGE --> MR
SGE --> LKey
WR --> SendWR
WR --> RecvWR
WR --> RDMAWriteWR
WR --> RDMAReadWR
RDMAWriteWR --> RKey
RDMAReadWR --> RKey
%% Network relationships
Port --> LID
CM --> QP
%% Completion flow
WR -->|完成通知| CQ
style CA fill:#e1f5ff
style PD fill:#fff4e1
style MR fill:#e8f5e9
style QP fill:#f3e5f5
style CQ fill:#fce4ec
style WR fill:#fff9c4
数据流关系图
以下图表展示了数据在 InfiniBand 系统中的流动路径:
sequenceDiagram
participant App as 应用程序
participant QP as Queue Pair
participant SQ as Send Queue
participant RQ as Receive Queue
participant CQ as Completion Queue
participant MR as Memory Region
participant CA as Channel Adapter
participant Network as InfiniBand网络
Note over App,Network: 发送数据流程
App->>MR: 注册内存区域
App->>SQ: 提交 Send WR (包含 SGE)
SQ->>CA: 处理工作请求
CA->>Network: 发送数据包
Network->>CA: 确认/完成
CA->>CQ: 生成完成事件
CQ->>App: 通知应用完成
Note over App,Network: 接收数据流程
App->>MR: 注册内存区域
App->>RQ: 提交 Receive WR (包含 SGE)
Network->>CA: 接收数据包
CA->>RQ: 匹配 Receive WR
CA->>MR: 写入数据到内存
CA->>CQ: 生成完成事件
CQ->>App: 通知应用完成
Note over App,Network: RDMA Write 流程
App->>MR: 注册内存区域(获取R_Key)
App->>SQ: 提交 RDMA Write WR (包含R_Key)
SQ->>CA: 处理 RDMA Write
CA->>Network: 发送 RDMA Write 请求
Network->>CA: 远程CA接收请求
CA->>MR: 直接写入远程内存(无需CPU参与)
CA->>CQ: 生成完成事件
CQ->>App: 通知应用完成
层次结构图
以下图表展示了 InfiniBand 编程模型的层次结构:
graph TD
subgraph Level1["应用层"]
App[应用程序]
end
subgraph Level2["Verbs API层"]
Verbs[ibVerbs API]
end
subgraph Level3["资源管理层"]
PD_Res[PD: 保护域]
MR_Res[MR: 内存区域]
QP_Res[QP: 队列对]
CQ_Res[CQ: 完成队列]
end
subgraph Level4["操作层"]
WR_Op[WR: 工作请求]
SGE_Op[SGE: 分散/聚集元素]
end
subgraph Level5["硬件层"]
CA_HW[CA: 通道适配器]
Port_HW[Port: 端口]
end
App --> Verbs
Verbs --> PD_Res
Verbs --> MR_Res
Verbs --> QP_Res
Verbs --> CQ_Res
QP_Res --> WR_Op
WR_Op --> SGE_Op
SGE_Op --> MR_Res
WR_Op --> CQ_Res
QP_Res --> CA_HW
CA_HW --> Port_HW
style Level1 fill:#e3f2fd
style Level2 fill:#f1f8e9
style Level3 fill:#fff3e0
style Level4 fill:#fce4ec
style Level5 fill:#e0f2f1
关键概念总结表
| 概念 |
英文全称 |
作用 |
关联对象 |
| CA |
Channel Adapter |
硬件网卡 |
Port |
| PD |
Protection Domain |
安全边界 |
QP, MR, AH (CQ通过QP间接关联) |
| MR |
Memory Region |
注册的内存区域 |
PD, L_Key, R_Key |
| QP |
Queue Pair |
通信端点 |
PD, SQ, RQ, CQ |
| SQ |
Send Queue |
发送队列 |
QP |
| RQ |
Receive Queue |
接收队列 |
QP |
| CQ |
Completion Queue |
完成队列 |
QP, WR |
| AH |
Address Handle |
地址句柄 |
PD, UD QP |
| WR |
Work Request |
工作请求 |
QP, SGE, AH |
| SGE |
Scatter/Gather Elements |
内存描述符 |
MR, L_Key |
| L_Key |
Local Key |
本地访问密钥 |
MR |
| R_Key |
Remote Key |
远程访问密钥 |
MR |
| LID |
Local Identifier |
本地标识符 |
Port |
| CM |
Connection Manager |
连接管理器 |
QP |
编程流程
典型的 InfiniBand 编程流程:
- 打开设备:
ibv_open_device() - 获取 Context
- 分配保护域:
ibv_alloc_pd() - 创建 PD
- 注册内存:
ibv_reg_mr() - 创建 MR,获得 L_Key 和 R_Key
- 创建完成队列:
ibv_create_cq() - 创建 CQ
- 创建地址句柄:
ibv_create_ah() - 创建 AH(仅 UD QP 需要)
- 创建队列对:
ibv_create_qp() - 创建 QP,关联 CQ
- 建立连接: 使用 CM 或手动配置 QP 状态
- 提交工作请求:
ibv_post_send(), ibv_post_recv() - 提交 WR(UD QP 的 Send WR 需要包含 AH)
- 轮询完成:
ibv_poll_cq() - 检查完成事件
- 清理资源: 销毁 QP, AH, CQ, MR, PD,关闭设备
参考资料