2、RDMA基础回顾:RDMA概念、InfiniBand与RoCEv2协议栈、内核态与用户态数据通路对比

2.1 RDMA核心概念与价值定位

RDMA(Remote Direct Memory Access)的核心思想是:允许一端主机的应用直接读写另一端主机的内存,而整个过程不经过两端操作系统的内核,也不占用两端CPU。传统网络路径中,数据从网卡到内核协议栈、再从内核拷贝到用户态缓冲区,经历了多次拷贝和上下文切换;而RDMA通过网卡硬件(HCA/RNIC)完成传输层处理,配合内核态注册的内存区域(MR),实现“零拷贝”和“内核旁路”。

RDMA的三大核心特性可归纳为:

  • 内核旁路(Kernel Bypass):应用与网卡之间直接交换描述符,不经过socket层和TCP/IP协议栈。
  • 零拷贝(Zero-Copy):数据在网卡与用户缓冲区之间直接DMA搬运,无需中间缓冲。
  • CPU卸载(Offload):数据的封装、解封装、可靠性确认、乱序重组等由网卡硬件完成。
理解关键:RDMA不是一种“新网络协议”,而是一种内存访问语义。它把网络通信抽象为“本地内存读写远端内存”,因此编程模型与socket完全不同。
传统TCP/IP数据通路 用户态应用 socket send/recv 内核协议栈 TCP/IP处理 + 数据拷贝 传统网卡 (NIC) 仅收发数据帧 ① 用户态→内核态拷贝 ② 内核态→网卡拷贝 RDMA数据通路 用户态应用 verbs API (ibv_post_send) 内核态(仅初始化) 注册MR、创建QP、mmap RDMA网卡 (RNIC/HCA) 硬件处理传输层协议 直接DMA读写 远端主机内存 内存区域 (MR) lkey/rkey 保护 队列对 (QP) SQ / RQ / CQ 完成队列 (CQ) 完成事件通知 RDMA读写请求 关键差异对比 ✗ 多次拷贝:用户↔内核↔网卡 ✗ CPU参与每个包的处理 ✗ 上下文切换开销大 ✓ 零拷贝:网卡直接访问用户内存 ✓ CPU仅处理控制面,数据面全卸载 ✓ 内核旁路,无上下文切换

2.2 InfiniBand与RoCEv2协议栈

RDMA的物理实现主要有三种网络:InfiniBand(IB)、RoCEv2(RDMA over Converged Ethernet version 2)和iWARP。其中前两者在生产环境中占绝对主导地位。理解它们的协议栈差异,是后续优化数据通路的基础。

层次 InfiniBand RoCEv2
上层协议 IB Transport (RC/UC/UD) IB Transport (RC/UC/UD)
网络层 IB Global Route (LRH+GRH) UDP/IPv4或IPv6(端口4791)
数据链路层 IB Link Layer (LRH) 以太网MAC + VLAN
物理层 IB物理层(IB线缆/光模块) 以太网物理层(RJ45/光模块)
流控机制 基于信用(Credit)的链路层流控 依赖PFC(优先级流控)保证无损
路由方式 IB子网管理器(SM) 传统IP路由(ECMP等)

RoCEv2之所以采用UDP封装(目的端口4791),是为了让报文能够通过传统IP网络路由,同时利用UDP的熵源(Entropy)做负载均衡。但这也带来了一个关键问题:UDP本身不保证可靠性和顺序,因此RoCEv2网络必须依赖以太网的无损特性(PFC、ECN等)来避免丢包,否则RDMA的可靠传输(RC)会因重传而性能骤降。

协议栈对比要点:IB和RoCEv2的上三层(IB Transport、Packet Relay、Packet)完全一致,差异仅在网络层以下。这意味着RDMA的verbs层编程接口(ibv_post_send等)对两者完全通用,但网卡硬件处理路径不同——RoCEv2网卡需要额外处理UDP/IP头,这会带来微秒级的额外延迟。

2.3 内核态与用户态数据通路对比

RDMA编程模型中,控制面(创建QP、注册MR、分配CQ等)必须通过内核驱动完成,而数据面(post_send、poll_cq)则有两种实现方式:

  • 用户态数据通路(Userspace Direct):应用通过ibv_reg_mr()注册内存后,内核将网卡的硬件队列(Doorbell、WQ)mmap映射到用户空间。应用直接写Doorbell寄存器通知网卡取走WQE(Work Queue Entry),完成时网卡直接写CQE到用户态内存。全程无系统调用。
  • 内核态数据通路(Kernel Direct):驱动在内核态维护WQ和CQ,应用通过write()或sendmsg()等系统调用进入内核,由内核驱动填充WQE并ring Doorbell。完成时内核驱动处理CQE并唤醒等待的进程。
// 用户态数据通路核心流程(伪代码)
struct ibv_qp *qp;
struct ibv_mr *mr;
struct ibv_send_wr wr = { .opcode = IBV_WR_SEND };

// 1. 注册内存区域(仅一次,内核参与)
mr = ibv_reg_mr(pd, buf, size, IBV_ACCESS_LOCAL_WRITE);

// 2. 数据面:直接写WQE到硬件队列(无系统调用)
wr.sg_list = &sg; wr.num_sge = 1;
ibv_post_send(qp, &wr, &bad_wr);  // 内部写Doorbell

// 3. 轮询完成队列(无系统调用)
while (ibv_poll_cq(cq, 1, &wc) == 0);

两种路径的性能差异非常显著:

维度 用户态数据通路 内核态数据通路
单次操作延迟 约1~3μs(纯硬件路径) 约5~10μs(含系统调用+调度)
CPU占用 发送端CPU几乎为0 每次操作消耗约1~2μs CPU
上下文切换 无 每次操作至少2次切换
适用场景 高频小消息、低延迟交易 块存储、文件系统等内核组件

2.4 案例:为什么存储系统选择内核态数据通路

以NVMe over RDMA为例,存储服务通常运行在内核态(如Linux的nvme-rdma驱动)。原因在于:

  1. 块设备语义需要内核参与:块设备请求(bio)必须经过内核的I/O调度器和块层,无法直接暴露给用户态。
  2. 内存注册管理:存储数据缓冲往往由内核的页缓存(Page Cache)管理,用户态无法直接注册这些页面。
  3. 稳定性与隔离:内核态驱动可以更好地处理错误恢复和QP重连,而用户态应用崩溃可能导致QP状态残留。

但内核态路径的代价是:每个I/O请求都要经历一次系统调用和一次数据拷贝(从用户缓冲区到内核缓冲区)。为了优化,现代存储系统采用轮询模式(Polling Mode)代替中断模式,减少上下文切换,但这会占用一个CPU核持续轮询CQ。

常见误区:很多人认为“RDMA必须用用户态才能发挥性能”。实际上,内核态RDMA通过优化(如busy polling、batch处理、大页内存)也能达到接近用户态的性能,差距通常在20%~30%以内。选择哪种路径,取决于你的应用是否必须经过内核I/O栈,而不是盲目追求用户态。

2.5 误区与复盘:数据通路选择的决策框架

在实际项目中,工程师常犯的错误是:在用户态应用中使用内核态RDMA接口(如通过rdma_cm的rdma_write()),导致性能远低于预期。复盘时需明确:

  • 误区1:混淆“RDMA CM”与“数据通路”。RDMA CM(Connection Manager)仅负责建立连接,它本身是内核态实现,但连接建立后,数据面仍可切换到用户态直接post_send。
  • 误区2:忽略内存注册的开销。用户态虽然数据面无系统调用,但ibv_reg_mr()需要内核参与并做页表锁定,频繁注册/注销MR会严重拖慢性能。应使用内存池预注册策略。
  • 误区3:认为RoCEv2与IB性能完全一致。RoCEv2在无损以太网下性能接近IB,但一旦发生PFC暂停或ECN标记,延迟会急剧恶化。IB的信用流控天然避免缓冲区溢出,而RoCEv2依赖外部网络调优。

决策框架总结如下:

应用类型 推荐数据通路 理由
高频交易、分布式锁 用户态 追求极致低延迟,无内核I/O依赖
分布式文件系统(Ceph) 内核态 需要与内核页缓存、VFS深度集成
数据库(MySQL/PostgreSQL) 用户态(绕过OS) 数据库自带缓冲池,无需内核页缓存
KV存储(Redis) 用户态 纯内存操作,无块设备参与