RDMA:队列、内存注册与完成语义

本页目录

工作提交成功后,设备可能还在读源缓冲;远端内存更新后,应用可能尚未消费。RDMA 减少特定数据路径的处理和复制,仍需明确资源授权、完成与复用⁠。

查阅目标阅读位置
传输、队列与内存注册第 1~3 节
操作与完成语义第 4~5 节
应用协议、旁路与观察第 6~8 节
RDMA 操作需要先建立内存与队列授权 发起端应用 准备本地缓冲与 WR QP 与 RNIC 校验 key、范围和权限 远端注册内存 地址范围 + rkey 授权 本地 CQ 完成 检查 status,管理本地寿命 远端应用协议 通知、消费、版本与确认 不自动代表业务完成 普通 RDMA WRITE 不自动生成远端应用通知;本地完成也不等于业务提交。

1. InfiniBand、RoCE 与 iWARP

传输网络基础需要理解的条件
InfiniBandIB 网络及适配器链路、子网管理和相应路由/服务
RoCEv1以太网二层与基于 IP 路由的 RoCEv2 不同
RoCEv2UDP/IP,标准目的端口 4791拥塞、丢包恢复及网卡能力影响表现
iWARPTCP 上的 RDMA 协议栈TCP 传输与 RDMA 资源/完成语义共同作用

RoCE 部署常配合 ECN、拥塞控制和适当的 PFC 策略,但 PFC 不是“有 RoCEv2 就必须无条件全网开启”的逻辑前提。丢包及暂停策略的实际效果依硬件和网络设计,需要明确验证;PFC 自身还可能引起队头阻塞和拥塞传播。

Linux 的 RXE、SIW 等软件实现有助于学习/测试相应 RDMA 接口,数据面仍消耗 CPU 并经过软件处理。不能把软件 RDMA 测得的行为直接当成硬件 RNIC 的性能或旁路证明。

2. QP、CQ 和 PD 是分别管理的对象

QP 通常包含 SQ 和 RQ:SQ 接收主动发起的 SEND、READ、WRITE 等工作;RQ 提供接收 SEND 等操作需要的缓冲区。CQ 是单独创建并关联的对象,不是 QP 结构定义里必然私有的第三条队列。

多个 QP 可以共享 CQ;send_cq 与 recv_cq 可相同或不同。使用 SRQ 时,多个 QP 又可共享接收资源。创建时请求的能力与 provider 返回的实际能力要核实,尤其最大 WR、SGE 和 inline 大小。见 ibv_create_qp。

PD(protection domain)参与关联 QP、MR 等资源的访问控制。把对象放入正确 PD,并满足地址、key 和访问标志等要求,才能合法使用注册内存;不是知道一个指针数值就可以发 DMA。

RC QP 常经历 RESET、INIT、RTR、RTS,需要配置地址、路径、PSN、访问权限和重试等参数。RDMA CM 可帮助连接管理,也可以由程序另行交换并配置参数;并非所有应用都必须使用同一种 CM 流程。UD 等 QP 类型的连接、可靠性和支持操作又不同。

3. Memory Region:注册建立什么承诺

普通 MR 注册给 provider/RNIC 建立内存地址范围及权限,通常涉及页固定、DMA 映射和硬件转换表,并消耗受限制的资源。lkey 用于本地 SGE 访问验证,rkey 用于相应远端访问验证。

ODP 等机制可在支持的环境中按需处理页映射,不需要把所有页面从创建到销毁一直固定。因此“注册必然把所有页永久锁住”与“RDMA 永远不能缺页”都太绝对。

远端收到的地址含义取决于 MR 的 IOVA/zero-based 等设置,不能始终当成未经转换的本地 CPU 指针。访问标志还决定 remote read/write/atomic 是否被允许;需要远端写时,本地写权限等关联要求也要按接口设置。见 ibv_reg_mr。

Key 不是加密密钥

rkey 是访问授权相关标识,不提供传输加密、身份认证或应用数据的保密封装。它与地址、长度等信息应通过可信的协议交换,限制暴露范围,并在资源撤销时处理远端仍在途的操作。

注册整个进程的大地址范围虽然可能减少管理次数,也会扩大错误远端写入的影响。MR/MW 的范围、权限、key 寿命和连接身份要共同设计,而不是只追求减少注册开销。

4. SEND、WRITE、READ 的职责差异

操作发起端提供远端需要准备通知含义
SEND本地数据 SGE已 post 的 receive 缓冲区接收侧 CQE 可报告消息到达
RDMA WRITE本地源、远端地址与 rkey可远端写的内存范围普通 WRITE 不自动产生远端 receive CQE
RDMA READ本地目标、远端地址与 rkey可远端读的内存范围发起端完成后才按契约读取结果
WRITE_WITH_IMMWRITE 参数和 immediate data数据 MR 及接收通知资源写数据到指定地址,并产生相应接收完成

RC SEND 在远端接收资源不足时可能触发 RNR 和重试,最终也可能失败。WRITE_WITH_IMM 的数据写入指定远端地址,不能把它误认为普通 SEND 将数据写进 receive SGE;但接收通知仍消耗相应 RQ/SRQ 工作资源。

RDMA atomic 操作也只提供所支持大小、对齐、操作和作用域的原子性,不使任意多字段数据结构自动具备事务一致性。

5. Post 成功与完成成功

ibv_post_send() 成功说明工作已被提交接受,不说明设备已经结束使用数据。批量 WR 链中途失败时,bad_wr 指向首个失败项,前面的项可能已成功提交;不能一看到错误就释放整批缓冲区。

非 inline 发送缓冲通常要存活到相应完成条件成立;inline 数据被复制进提交结构后,可按 inline 契约更早复用原数据,但仍有队列资源和完成管理。见 ibv_post_send。

CQ 返回数量与 WC 状态分开检查

poll 结果含义下一步
负数调用失败处理调用错误
0此刻没有完成按轮询/通知策略继续
正数取到若干 WC逐条检查 status,不能统一视为成功

完成记录数量与每项工作成功与否是两层判断。

失败完成只保证部分字段有效,例如 wr_id、status、qp_num、vendor_err。CQ 事件通知也不是 WC 本身:事件提示应用来 poll,poll 才取走完成记录。应按通知接口约定重新请求事件并确认事件,避免睡眠与新完成之间的竞争。

选择 unsignaled 发送可降低 CQE 数量,但不意味着可以任意立即复用所有缓冲区。需要依 QP 顺序、操作/fence 语义和后续 signaled 完成建立回收水位,错误路径也必须考虑。CQ 长期不消费可能 overrun,不能把它当作只丢一条统计日志的小问题。见 ibv_poll_cq。

6. 远端内存更新不是应用事务提交

把远端记录槽看成一个需要完整管理的对象:

阶段协议必须回答
取得槽位由谁分配,旧读者是否结束?
写入与发布怎样判断新数据已完整可见?
读取与验证服务器怎样确认版本和一致性?
归还与覆盖谁通知可以再次复用?

RNIC 搬运内容并不会自动替应用补齐这些状态转换。

“先 WRITE payload,再 WRITE ready=1”不能脱离 QP、操作顺序、内存可见性和 relaxed ordering 等条件直接当成通用发布协议。跨 QP 操作也没有自动的全局顺序。需要按 transport/provider 的完成和 fence 契约,并结合 CPU 的同步方式设计通知。

远端更新、消费与持久化分别确认

普通 WRITE 的本地成功完成不等于服务器应用已消费,也不等于服务器已将数据持久化到磁盘。若业务需要持久化确认,应由服务端完成相应存储和元数据步骤后回复,或采用明确支持持久化语义的专门方案。

RDMA READ 同样不自动获得一致快照:远端可能在读取期间修改多字段对象。版本号校验、锁或复制协议仍有必要。

下面依次可见的是请求、传输、CQE、远端应用消费和持久保存,但每个动作只兑现自己的条件。可以在本地完成之后立即复用源,再查看远端内容是否已经持久化。

正在呈现知识画面
本地 CQE、远端消费和持久化是三个确认

post 成功不是设备已停止使用源内存;本地成功完成允许按契约复用源,不表示远端应用已消费,也不表示已持久化。

7. “内核旁路”的实际范围

硬件用户态 verbs 快路径可由 provider 直接访问映射队列/doorbell,减少每次操作的系统调用。内核仍处理资源创建、映射授权、事件、异常、ODP 和设备生命周期等,应用也仍执行提交和 poll 逻辑。

比较性能时,先固定测量边界

忙轮询常用 CPU 换低延迟;事件驱动降低空转但引入通知成本。buffer 是否预注册、消息大小、NUMA、队列深度、拥塞与 provider 都会影响效果。比较 TCP 与 RDMA 时,要说明是单次操作延迟、吞吐、CPU/字节还是业务端到端完成时间。

部分硬件 RoCE 数据路径不经过常规主机 IP socket 栈,不能假设 nftables 的 socket/转发规则或 tcpdump 自动覆盖所有 RDMA 流量。资源权限、网卡/交换机计数和 RDMA 事件需要共同观测。见用户态 verbs。

8. 只读查看 RDMA 设备与端口

from pathlib import Path

root = Path('/sys/class/infiniband')
devices = sorted(root.glob('*'))
if not devices:
    print('no RDMA devices visible in this environment')
for dev in devices:
    print(dev.name)
    for port in sorted((dev / 'ports').glob('*')):
        for field in ('state', 'phys_state', 'link_layer'):
            try:
                print(' ', port.name, field, (port / field).read_text().strip())
            except OSError:
                pass

rdma link show 等工具可补充内核 RDMA 对象信息。设备可见不证明远端 QP、MR、路由或应用协议已经配置成功;无可见设备也只描述当前环境,不能用它验证实际传输。

深入实现可从 rdma-core 的 libibverbs、librdmacm 与 provider,以及内核 drivers/infiniband/ 阅读。优先跟踪资源创建、一次完成与错误清理,而不是只看 post 函数。