RDMA Write 与 RDMA Read 是进行大量数据传输时首选的操作类型,Send 和 Receive 操作通常用于传输一些控制信息。在实际执行 RDMA 操作时,会面临一些关键的技术挑战。本文将深入介绍 Memory Region(MR,内存区域) 机制,它用于解决 RDMA 操作在实际执行过程中的地址转换和内存安全问题。
一、RDMA 操作面临的两个核心问题
1.1 问题场景回顾
先回忆一下 RDMA Write 操作的场景:
1. 计算机1的应用程序向计算机2的内存中写入一段数据
2. 通知驱动程序给本地的 RDMA 网卡下发一个 WQE 到 SQ 中
3. WQE 中包含:
- 源数据缓存的地址
- 目的数据缓存的地址
- 数据长度等信息
4. 网卡解析 WQE 后,从源数据缓存读取数据
5. 封装成数据包发送到对端网卡
6. 计算机2的网卡收到数据包后,解析出数据、长度、目的地址
7. 最后把数据写到计算机2的内存中
1.2 问题一:虚拟地址到物理地址的转换
问题描述:
| 层面 | 地址类型 | 说明 |
|---|---|---|
| 应用程序 | 虚拟地址 | 通过驱动程序向 WQE 中写入的地址是虚拟地址 |
| RDMA 网卡 | 物理地址 | 网卡需要使用物理地址才能通过总线访问主机内存 |
为什么网卡不能使用 MMU?
CPU 的 MMU 模块:
- 可以通过查询操作系统建立的系统页表得到物理地址
- 但 RDMA 网卡无法使用 MMU
RDMA 网卡无法访问系统页表的原因:
1. 没有能力:
- 系统页表是按照 MMU 能理解的格式建立的
- 每种 CPU 体系结构的页表格式不完全相同
- RDMA 网卡无法理解各种不同格式的页表
2. 没有权限:
- 页表是系统的核心功能
- 为了保障操作系统的安全,不能和外设共享
- 允许外设直接访问页表会带来严重的安全隐患
核心问题: RDMA 网卡如何才能获得缓存的物理地址?
1.3 问题二:内存访问的安全性
问题描述:
假设网卡有能力获取缓存的物理地址:
但如果应用程序恶意地或因为 bug 指定了一个非法的虚拟地址:
可能的后果:
├─ 网卡被误导去读写其他应用程序的内存
├─ 网卡被误导去读写系统关键位置的内存
└─ 网卡被误导去访问其他设备的地址空间
如何防范这种安全隐患?
1.4 解决方案:MR 机制
为了解决上述两个问题,InfiniBand 引入了 MR 机制。
二、MR 的基本概念
2.1 MR 的定义
根据 InfiniBand 标准中的定义:
MR 是一个经过注册的、虚拟地址连续的、任意大小的内存区域,支持 RDMA 网卡进行本地内存访问(后者可选)。
2.2 MR 的本质
MR 在本质上:
- 由程序在内存中申请的一段缓存
- 用于保存待收发的数据
- 是一段特殊的内存
按照标准:
1. 程序先申请一段缓存
2. 调用 RDMA 软件框架提供的 Verbs API ibv_reg_mr 注册这段缓存
3. 注册后,这段缓存才能被称为 MR
4. 然后 RDMA 网卡才能访问这段缓存
2.3 MR 示意图
┌─────────────────────────────────────────────────────────────────────────┐
│ 主机内存 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────┐ │
│ │ MR 1 │ ← 经过注册的内存区域 1 │
│ │ 虚拟地址连续 │ │
│ │ 任意大小 │ │
│ └─────────────────────┘ │
│ │
│ ┌─────────────────────┐ │
│ │ MR 2 │ ← 经过注册的内存区域 2 │
│ │ │ │
│ └─────────────────────┘ │
│ │
│ ┌─────────────────────┐ │
│ │ MR 3 │ ← 经过注册的内存区域 3 │
│ │ │ │
│ └─────────────────────┘ │
│ │
│ ... 系统中可以有很多 MR ... │
│ │
└─────────────────────────────────────────────────────────────────────────┘
关键点:
- MR 就是一段特殊的内存
- 一个系统中可以有很多 MR
- 必须先注册,网卡才能访问
2.4 术语说明
注: 为了区分操作系统建立的供 MMU 查询的页表,和 RDMA 驱动程序建立的供 RDMA 网卡查询的 MR 地址转换表,我们把前者称为 系统页表。
三、MR 如何解决地址转换问题
3.1 MR 注册过程
应用程序调用 ibv_reg_mr 注册内存:
┌─────────────────────────────────────────────────────────────────────────┐
│ 应用程序 │
│ │ │
│ │ 1. 申请内存缓存 │
│ ▼ │
│ ┌─────────────────┐ │
│ │ 内存缓存 │ │
│ │ (虚拟地址连续) │ │
│ └────────┬────────┘ │
│ │ │
│ │ 2. 调用 ibv_reg_mr │
│ ▼ │
├─────────────────────────────────────────────────────────────────────────┤
│ RDMA 驱动程序 │
│ │ │
│ │ 3. 查询系统页表 │
│ │ 获取虚拟地址到物理地址的映射 │
│ ▼ │
│ ┌─────────────────┐ │
│ │ MR 地址转换表 │ │
│ │ │ │
│ │ 虚拟地址 → 物理地址 │
│ │ 0x1000 → 0x8000 │
│ │ 0x1004 → 0x9000 (物理页可能不连续) │
│ │ ... │ │
│ └────────┬────────┘ │
│ │ │
│ │ 4. 将转换表下发到网卡 │
│ ▼ │
├─────────────────────────────────────────────────────────────────────────┤
│ RDMA 网卡 │
│ │ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ 网卡硬件 │ │
│ │ MR 地址转换表 │ │
│ │ (可直接查询) │ │
│ └─────────────────┘ │
└─────────────────────────────────────────────────────────────────────────┘
3.2 地址转换流程
RDMA 操作时的地址转换:
1. 应用程序在 WQE 中填写虚拟地址
└─ 例如:addr = 0x12340000
2. RDMA 网卡收到 WQE,解析出虚拟地址
3. 网卡查询自己的 MR 地址转换表
└─ 虚拟地址 0x12340000 → 物理地址 0x87654000
4. 网卡使用物理地址进行 DMA 操作
└─ 通过总线访问主机内存
关键点:
- 驱动程序在注册 MR 时已经完成了地址转换表的构建
- 网卡硬件可以直接查询自己的转换表,无需访问系统页表
- 转换表格式由网卡硬件定义,不依赖 CPU 架构
3.3 为什么这种方式可行?
| 问题 | 传统方式的问题 | MR 机制的解决方案 |
|---|---|---|
| 页表格式不同 | 每种 CPU 架构页表格式不同,网卡无法理解 | RDMA 驱动程序负责统一处理,向网卡下发网卡能理解的格式 |
| 权限问题 | 系统页表是核心功能,不能和外设共享 | MR 转换表由驱动程序专门为网卡创建,不涉及系统页表共享 |
| 效率问题 | 每次访问都要查系统页表 | 转换表在注册时一次性构建,网卡直接使用 |
四、MR 如何解决内存安全问题
4.1 安全机制概述
MR 注册时的安全检查:
┌─────────────────────────────────────────────────────────────────────────┐
│ 应用程序 │
│ │ │
│ │ 调用 ibv_reg_mr │
│ │ 参数:虚拟地址、长度、访问权限 │
│ ▼ │
├─────────────────────────────────────────────────────────────────────────┤
│ RDMA 驱动程序 │
│ │ │
│ │ 安全检查: │
│ │ 1. 虚拟地址是否有效? │
│ │ 2. 该应用程序是否有权限访问这段内存? │
│ │ 3. 内存是否被锁定(防止换页)? │
│ │ │
│ │ 检查通过 → 注册成功 │
│ │ 检查失败 → 返回错误 │
│ ▼ │
│ ┌─────────────────┐ │
│ │ MR │ │
│ │ 访问权限:读/写 │ │
│ │ R_Key:0x1234 │ ← 访问密钥 │
│ └─────────────────┘ │
└─────────────────────────────────────────────────────────────────────────┘
4.2 R_Key 机制
R_Key(Remote Key) 是 MR 注册时生成的一个访问密钥:
R_Key 的作用:
1. 唯一标识一个 MR
2. 访问控制:
- 远端应用程序进行 RDMA Write/Read 时,必须提供 R_Key
- 网卡验证 R_Key 的有效性
- 只有持有正确 R_Key 的请求才能访问对应内存
3. 权限验证:
- MR 可以设置访问权限(只读/只写/读写)
- 网卡会检查操作类型是否与权限匹配
示例:
┌─────────────────────────────────────────────────────────────────┐
│ WQE 内容 │
├─────────────────────────────────────────────────────────────────┤
│ opcode = RDMA_Write │
│ addr = 0x12340000 ← 虚拟地址 │
│ remote_addr = 0x11110000 ← 远端虚拟地址 │
│ len = 100 │
│ key = R_key = 0x1234 ← 访问密钥 │
└─────────────────────────────────────────────────────────────────┘
远端网卡收到请求后:
1. 根据 R_Key 找到对应的 MR
2. 检查 R_Key 是否有效
3. 检查 remote_addr 是否在 MR 的地址范围内
4. 检查操作类型(Write)是否在 MR 的权限范围内
5. 全部通过后,才执行写入操作
4.3 内存锁定(Pin Memory)
问题: 操作系统可能会将内存页换出到磁盘,导致物理地址变化。
解决方案: MR 注册时锁定内存页。
内存锁定机制:
普通内存:
┌─────────────────┐
│ 虚拟内存页 │
└────────┬────────┘
│
│ 可能被换出到磁盘
▼
┌─────────────────┐
│ 磁盘空间 │
└─────────────────┘
MR 注册的内存:
┌─────────────────┐
│ 虚拟内存页 │
└────────┬────────┘
│
│ 被锁定,不会被换出
▼
┌─────────────────┐
│ 物理内存页 │ ← 物理地址固定不变
└─────────────────┘
关键点:
- MR 注册时,驱动程序会锁定(pin)对应的物理内存页
- 锁定后的内存不会被操作系统换出到磁盘
- 物理地址保持不变,网卡可以安全地使用 MR 地址转换表
4.4 边界检查
MR 边界检查:
应用程序注册 MR:
- 虚拟地址:0x10000000
- 长度:1024 字节
- 有效范围:0x10000000 ~ 0x100003FF
远端请求访问:
- remote_addr = 0x10000500 ← 超出 MR 范围!
- len = 100
网卡检查:
1. 根据 R_Key 找到 MR
2. 检查 remote_addr (0x10000500) 是否在 MR 范围内
3. 0x10000500 > 0x100003FF → 超出范围
4. 拒绝访问,返回错误
结果:
- 防止越界访问其他内存区域
- 保护了系统的内存安全
五、MR 的完整工作流程
5.1 MR 注册流程
┌─────────────────────────────────────────────────────────────────────────┐
│ 第一步:应用程序申请内存 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ void *buffer = malloc(1024 * 1024); // 申请 1MB 内存 │
│ │
└─────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ 第二步:调用 ibv_reg_mr 注册 MR │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ struct ibv_mr *mr = ibv_reg_mr( │
│ pd, // Protection Domain │
│ buffer, // 虚拟地址 │
│ 1024 * 1024, // 长度 │
│ IBV_ACCESS_LOCAL_WRITE | IBV_ACCESS_REMOTE_WRITE │
│ ); │
│ │
└─────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ 第三步:驱动程序处理注册请求 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ 1. 验证应用程序对该内存的访问权限 │
│ 2. 查询系统页表,构建虚拟地址到物理地址的映射 │
│ 3. 锁定物理内存页(防止换页) │
│ 4. 构建 MR 地址转换表 │
│ 5. 分配 R_Key │
│ 6. 将 MR 地址转换表下发到网卡硬件 │
│ │
└─────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ 第四步:返回 MR 信息给应用程序 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ mr->addr = buffer; // 虚拟地址 │
│ mr->length = 1024 * 1024; // 长度 │
│ mr->lkey = 0x1234; // 本地访问密钥 │
│ mr->rkey = 0x5678; // 远程访问密钥 │
│ │
└─────────────────────────────────────────────────────────────────────────┘
5.2 MR 在 RDMA 操作中的使用
RDMA Write 操作中 MR 的使用:
请求端:
┌─────────────────────────────────────────────────────────────────────────┐
│ │
│ // 准备 WQE │
│ struct ibv_send_wr wr; │
│ wr.sg_list->addr = local_buffer; // 本地 MR 的虚拟地址 │
│ wr.sg_list->lkey = local_mr->lkey; // 本地 MR 的 lkey │
│ wr.wr.rdma.remote_addr = remote_addr; // 远端 MR 的虚拟地址 │
│ wr.wr.rdma.rkey = remote_mr->rkey; // 远端 MR 的 rkey │
│ │
│ // 网卡收到 WQE 后: │
│ // 1. 使用 lkey 查询本地 MR 地址转换表 │
│ // 2. 将 local_buffer 转换为物理地址 │
│ // 3. 通过 DMA 读取数据 │
│ │
└─────────────────────────────────────────────────────────────────────────┘
│
│ 网络传输
▼
响应端:
┌─────────────────────────────────────────────────────────────────────────┐
│ │
│ // 网卡收到数据包后: │
│ // 1. 从数据包中提取 remote_addr 和 rkey │
│ // 2. 使用 rkey 查找对应的 MR │
│ // 3. 验证 rkey 有效性 │
│ // 4. 验证 remote_addr 在 MR 范围内 │
│ // 5. 验证操作权限(是否允许远程写) │
│ // 6. 查询 MR 地址转换表,得到物理地址 │
│ // 7. 通过 DMA 写入数据 │
│ │
└─────────────────────────────────────────────────────────────────────────┘
六、MR 与系统页表的对比
6.1 对比表
| 对比项 | 系统页表 | MR 地址转换表 |
|---|---|---|
| 创建者 | 操作系统 | RDMA 驱动程序 |
| 使用者 | CPU 的 MMU | RDMA 网卡硬件 |
| 格式 | CPU 架构相关 | 网卡硬件定义 |
| 访问权限 | 仅内核可修改 | 驱动程序可修改 |
| 共享性 | 不能与外设共享 | 专门为网卡创建 |
| 内存锁定 | 支持换页 | 锁定不换页 |
| 查询时机 | 每次 CPU 访问内存 | MR 注册时一次性构建 |
6.2 关系示意
┌─────────────────────────────────────────────────────────────────────────┐
│ 主机内存 │
└─────────────────────────────────────────────────────────────────────────┘
│ │
│ CPU 访问 │ RDMA 网卡访问
▼ ▼
┌─────────────────────────┐ ┌─────────────────────────┐
│ 系统页表 │ │ MR 地址转换表 │
│ (CPU 架构相关格式) │ │ (网卡硬件定义格式) │
│ │ │ │
│ 虚拟地址 → 物理地址 │ │ 虚拟地址 → 物理地址 │
│ 支持换页 │ │ 内存已锁定 │
└─────────────────────────┘ └─────────────────────────┘
│ │
│ MMU 查询 │ 网卡硬件查询
▼ ▼
┌─────────────────────────┐ ┌─────────────────────────┐
│ CPU │ │ RDMA 网卡 │
└─────────────────────────┘ └─────────────────────────┘
关键点:
- 两套独立的地址转换机制
- 系统页表:通用、支持换页、CPU 使用
- MR 转换表:专用、内存锁定、RDMA 网卡使用
七、MR 的访问权限
7.1 权限类型
// MR 访问权限标志(来自 rdma-core)
IBV_ACCESS_LOCAL_WRITE // 允许本地写
IBV_ACCESS_REMOTE_WRITE // 允许远程写
IBV_ACCESS_REMOTE_READ // 允许远程读
IBV_ACCESS_REMOTE_ATOMIC // 允许远程原子操作
IBV_ACCESS_MW_BIND // 允许绑定内存窗口
7.2 权限验证
权限验证流程:
请求端发起 RDMA Write:
┌─────────────────────────────────────────────────────────────────┐
│ WQE: opcode = RDMA_Write │
│ rkey = 0x5678 │
└─────────────────────────────────────────────────────────────────┘
│
▼
响应端网卡验证:
┌─────────────────────────────────────────────────────────────────┐
│ 1. 查找 rkey = 0x5678 对应的 MR │
│ 2. 检查 MR 是否设置了 IBV_ACCESS_REMOTE_WRITE 权限 │
│ - 如果设置了 → 允许写入 │
│ - 如果未设置 → 拒绝访问,返回权限错误 │
└─────────────────────────────────────────────────────────────────┘