摘要

RDMA(Remote Direct Memory Access,远程直接内存访问)正在从根本上改变数据中心网络编程模型。在分布式存储(Ceph、TiKV)、高性能计算(MPI)、GPUDirect 加速等场景中,RDMA 绕过内核协议栈实现亚微秒级延迟和零拷贝数据传输,单链路可达 400Gbps 吞吐。

本文深入分析 Linux 内核 RDMA 子系统完整架构,从 Verbs API 抽象、Queue Pair 状态机、内存注册机制,到 RoCE v2 与 InfiniBand 双栈实战,帮助你构建生产级 RDMA 应用。

1. 为什么需要 RDMA

1.1 传统 TCP/IP 栈的瓶颈

传统 Linux TCP/IP 网络存在三大核心开销:

开销类型典型耗时说明
系统调用1~5 μs每次 I/O 触发用户态到内核态切换
内存拷贝3~10 μs数据在 socket buffer 与用户态间 copy
协议栈处理2~10 μssk_buff 协议封装/解封装
上下文切换~2 μs进程在调度与阻塞间切换
总计10+ μs无法满足高性能场景

RDMA 通过内核旁路(Kernel Bypass)将数据传输延迟降至 1~2 μs,吞吐带宽利用率接近线速。

1.2 RDMA 三大核心能力

1. 零拷贝(Zero-Copy):应用程序数据直接在本地内存与远端 RDMA 网卡(HCA)DMA 引擎之间传输,完全跳过内核协议栈。

2. 内核旁路(Kernel Bypass):用户态驱动(libibverbs)直接操作网卡寄存器,免去系统调用开销,实现真正的异步全双工通信。

3. CPU 卸载(CPU Offload):协议处理(重传、分片、ACK 生成)全部卸载到网卡硬件,传输过程零 CPU 占用。

2. RDMA 硬件与传输层

2.1 InfiniBand(IB)

InfiniBand 是原生 RDMA 协议,从物理层到传输层全部专为 RDMA 设计:

  • 物理层:QSFP28/QSFP56 光模块,单端口 200/400 Gbps
  • 链路层:Credit-based Flow Control,无损传输
  • 网络层:Subnet Manager(SM)管理 LID 路由
  • 传输层:RC(可靠连接)、UC(不可靠连接)、UD(不可靠数据报)、RD(可靠数据报)
  • 原生延迟:~600 ns(交换机端口到端口)

2.2 RoCE v2(RDMA over Converged Ethernet)

RoCE v2 允许在标准以太网上运行 RDMA,但依赖以下基础设施:

  • PFC(Priority Flow Control):802.1Qbb 基于优先级的流控,实现无损以太网
  • ECN(Explicit Congestion Notification):拥塞标记与反馈
  • DCQCN(Data Center Quantized Congestion Notification):RoCE v2 的速率控制算法
  • Basic QoS:DSCP 标记,带宽隔离

2.3 iWARP vs RoCE v2

特性RoCE v2iWARPInfiniBand
网络层UDP/IPTCP/SCTP原生 IB
延迟~1-3 μs~5-10 μs~600 ns
交换机要求需 PFC/ECN 支持标准以太网IB 交换机
路由能力可路由可路由不可路由(需)
部署成本中低高

3. Linux RDMA 内核子系统架构

3.1 内核模块拓扑

Linux 内核从 2.6 版本开始集成 RDMA 子系统(drivers/infiniband),核心模块包括:

  • ib_core:核心框架层,管理设备、注册驱动接口
  • ib_uverbs:用户态 verbs 通信通道(/dev/infiniband/uverbsX)
  • rdma_cm:RDMA 连接管理(对应 TCP socket API 的角色)
  • ib_umad:用户态管理数据报(子网管理/SM 通信)
  • mlx5_core:Mellanox ConnectX 系列网卡驱动
  • ib_ipoib:IP over IB 网络层(提供传统 socket 兼容)
  • ib_isert/ib_srpt:iSER/SRP SCSI 目标端(块存储协议)
  • nvmet-rdma/nvme-rdma:NVMe-oF RDMA 目标/发起端

完整模块可通过以下命令查看:

# 查看已加载的 RDMA 内核模块
$ lsmod | grep -E "ib_|rdma|mlx|mlx5"

# 查看 RDMA 设备列表
$ ibv_devinfo
hca_id: mlx5_0
    transport:                      InfiniBand (0)
    fw_ver:                         16.35.1012
    node_guid:                      248a:0703:00b4:7f80
    sys_image_guid:                 248a:0703:00b4:7f80
    vendor_id:                      0x02c9
    vendor_part_id:                 4123
    hw_ver:                         0x0
    board_id:                       MT_0000000221
    phys_port_cnt:                  1
        state:                      PORT_ACTIVE (4)
        max_mtu:                    4096 (5)
        active_mtu:                 4096 (5)
        sm_lid:                     1
        port_lid:                   1
        port_lmc:                   0x00
        link_layer:                 InfiniBand

3.2 uverbs 用户态驱动框架

用户态驱动程序运行在进程地址空间,通过 uverbs 字符设备与内核 RDMA 子系统交互:

// uverbs 设备打开
struct ibv_context *ctx = ibv_open_device(ib_dev);
// 分配 Protection Domain
struct ibv_pd *pd = ibv_alloc_pd(ctx);
// 创建 Completion Queue
struct ibv_cq *cq = ibv_create_cq(ctx, cq_depth, NULL, NULL, 0);
// 创建 Queue Pair
struct ibv_qp_init_attr qp_init_attr = {
    .send_cq = cq,
    .recv_cq = cq,
    .cap     = { .max_send_wr = 128, .max_recv_wr = 128,
                 .max_send_sge = 1, .max_recv_sge = 1 },
    .qp_type = IBV_QPT_RC,  // Reliable Connection
};
struct ibv_qp *qp = ibv_create_qp(pd, &qp_init_attr);

关键文件路径:

  • 内核驱动:drivers/infiniband/core/uverbs_main.c
  • 用户态 lib:libibverbs/verbs.c(rdma-core 项目)
  • 设备节点:/dev/infiniband/uverbsX、/dev/infiniband/rdma_cm
  • sysfs 接口:/sys/class/infiniband/

4. RDMA Verbs API 核心编程模型

4.1 Queue Pair(QP)状态机

Queue Pair 是 RDMA 通信的核心抽象,由 Send Queue 和 Receive Queue 组成,可支持 RC(可靠连接)和 UD(不可靠数据报)。状态迁移如下:

RESET → INIT → RTR(Ready To Receive) → RTS(Ready To Send)

RESET:复位状态,清除所有资源
INIT:初始化端口属性,配置 P_Key、Q_Key
RTR:远端 QP 信息已知,可接收数据包
RTS:激活发送/接收路径,完全可操作

状态迁移通过 ibv_modify_qp() 实现,对应内核中 drivers/infiniband/core/verbs.c 的 ib_modify_qp() 函数,最终调用网卡驱动 modify_qp 回调。

4.2 Memory Registration & Memory Region

RDMA 网络 DMA 引擎绕过 CPU 访问物理内存,因此需要提前注册(钉住)内存区域:

// 分配内存
void *buf = aligned_alloc(4096, BUFFER_SIZE);

// 注册 Memory Region
struct ibv_mr *mr = ibv_reg_mr(pd, buf, BUFFER_SIZE,
              IBV_ACCESS_LOCAL_WRITE |
              IBV_ACCESS_REMOTE_READ |
              IBV_ACCESS_REMOTE_WRITE);

注册时内核将执行:

  1. 钉住页面(get_user_pages):防止物理内存被换出(swap)
  2. IOMMU 映射:建立 HCA 设备可访问的 DMA 地址映射
  3. 注册 MR 描述符:创建 lkey(本地访问)和 rkey(远程访问授权)

关键参数选择:

  • IBV_ACCESS_LOCAL_WRITE:允许本端写入
  • IBV_ACCESS_REMOTE_READ:允许远端读取
  • IBV_ACCESS_REMOTE_WRITE:允许远端写入
  • IBV_ACCESS_REMOTE_ATOMIC:允许远端原子操作(CAS/FAA)
  • IBV_ACCESS_ON_DEMAND:按需分页(与内存注册解耦,延迟注册)

4.3 Work Request 与 Completion Queue

RDMA 的工作模式是完全异步的:

// 发送端:构造 SGE(Scatter-Gather Element)
struct ibv_sge sge = {
    .addr   = (uint64_t)local_buf,
    .length = data_size,
    .lkey   = mr->lkey,
};

struct ibv_send_wr wr = {
    .wr_id      = 1,
    .opcode     = IBV_WR_RDMA_WRITE,   // RDMA Write(零拷贝单向写入)
    .send_flags = IBV_SEND_SIGNALED,   // 产生 Completion Queue Entry
    .sg_list    = &sge,
    .num_sge    = 1,
    .wr.rdma.remote_addr = remote_mr_addr,
    .wr.rdma.rkey        = remote_rkey,
};
struct ibv_send_wr *bad_wr;
ibv_post_send(qp, &wr, &bad_wr);       // 异步投递

// 轮询等待完成
struct ibv_wc wc;
while (ibv_poll_cq(cq, 1, &wc) == 0) { /* spin */ }
if (wc.status != IBV_WC_SUCCESS) { /* 错误处理 */ }

可选 OPCODE:

操作码语义CPU 参与
IBV_WR_SENDSend/Recv(双向)接收端需 post_recv
IBV_WR_RDMA_WRITERDMA Write(单向写入)接收端零感知
IBV_WR_RDMA_READRDMA Read(单向读取)接收端零感知
IBV_WR_ATOMIC_CMP_AND_SWPCompare-and-Swap远端原子操作
IBV_WR_ATOMIC_FETCH_AND_ADDFetch-and-Add远端原子操作

5. RDMA-CM 连接管理实战

RDMA Connection Manager(rdma_cm)提供面向连接的通信建立,协议类似 TCP socket:

5.1 服务端流程

// 创建 rdma_cm 事件通道
struct rdma_event_channel *ec = rdma_create_event_channel();

// 创建监听 ID
struct rdma_cm_id *listen_id;
rdma_create_id(ec, &listen_id, NULL, RDMA_PS_TCP);  // TCP 类型(RoCE v2 over UDP)

// 绑定地址并监听
struct sockaddr_in addr = { .sin_family = AF_INET, .sin_port = htons(18515) };
rdma_bind_addr(listen_id, (struct sockaddr *)&addr);
rdma_listen(listen_id, 8);

// 阻塞等待连接事件
struct rdma_cm_event *event;
rdma_get_cm_event(ec, &event);  // 阻塞,直到 RDMA_CM_EVENT_CONNECT_REQUEST
struct rdma_cm_id *conn_id = event->id;

// 分配并初始化资源
conn_id->verbs = listen_id->verbs;
setup_qp_resources(conn_id);  // CQ, PD, QP

// 接受连接
rdma_accept(conn_id, NULL);

rdma_ack_cm_event(event);  // 必须 ACK 事件

5.2 客户端流程

struct rdma_cm_id *conn_id;
rdma_create_id(ec, &conn_id, NULL, RDMA_PS_TCP);
setup_qp_resources(conn_id);

// 解析地址
struct sockaddr_in dst_addr = { .sin_family = AF_INET, .sin_port = htons(18515) };
inet_pton(AF_INET, "192.168.10.100", &dst_addr.sin_addr);

// rdma_resolve_addr 先做 ARP 解析(ICM path record)
rdma_resolve_addr(conn_id, NULL, (struct sockaddr *)&dst_addr, 2000);

// 等待地址解析完成
rdma_get_cm_event(ec, &event);  // RDMA_CM_EVENT_ADDR_RESOLVED
rdma_ack_cm_event(event);

// 发起连接
rdma_resolve_route(conn_id, 2000);
rdma_get_cm_event(ec, &event);  // RDMA_CM_EVENT_ROUTE_RESOLVED
rdma_ack_cm_event(event);

// 发送 CONNECT 报文
struct rdma_conn_param param = { .responder_resources = 1, .initiator_depth = 1 };
rdma_connect(conn_id, &param);

// 等待远端 ACCEPT
rdma_get_cm_event(ec, &event);  // RDMA_CM_EVENT_ESTABLISHED

6. SR-IOV 虚拟化与 RDMA

6.1 内核 SR-IOV 框架

Mellanox ConnectX-5 及更新网卡支持 SR-IOV 虚拟化,将物理 HCA 拆分为多个 VF(Virtual Function):

# 启用 SR-IOV VF
echo 8 > /sys/class/infiniband/mlx5_0 device/sriov_numvfs

# 查看 VF PCI 设备
lspci | grep Mellanox
b5:00.0 InfiniBand controller: Mellanox Technologies MT27800 Family [ConnectX-5]  # PF
b5:02.0 InfiniBand controller: Mellanox Technologies ConnectX-5 VF               # VF0
b5:02.1 InfiniBand controller: Mellanox Technologies ConnectX-5 VF               # VF1

# 验证 VF 功能
VF 0000:b5:02.0:
    Link Status: up
    Node GUID: 0000:0000:0000:0001

6.2 直通 VF 到容器

通过 Kubernetes Device Plugin,将 VF 注入到 Pod:

# sriov-device-plugin ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: sriovdp-config
data:
  config.json: |
    {
      "resourceList": [{
        "resourceName": "mlx5_rdma",
        "selectors": {
          "isRdma": true,
          "drivers": ["mlx5_core"],
          "pciAddresses": ["0000:b5:02.0"]
        }
      }]
    }

---
# Pod 使用 RDMA VF
apiVersion: v1
kind: Pod
spec:
  containers:
  - name: rdma-app
    resources:
      requests:
        mellanox.com/mlx5_rdma: 1

6.3 Sub-Function 虚拟化(SmartNIC 模式)

Mellanox BlueField SmartNIC 引入 Sub-Function 模型,在有限 PCIe 通道下提供超大规模 VF 调度:

  • 每个 SF 拥有独立的 netdev、E-Switch 端口、verbs 设备
  • 支持热插拔,不依赖 PCIe PF 数量限制
  • 实现 DPU 连接模式(SmartNIC/DPU)从次通道卸载 RDMA 主机通信

7. RDMA 在分布式存储与计算中的应用

7.1 NVMe over Fabrics(NVMe-oF)RDMA

NVMe-oF 将 NVMe 命令封装在 RDMA 消息中,实现跨网络的低延迟块存储访问:

# 目标端(Target)
nvmetcli / - > cd subsystems
/subsystems/ -> create nqn.2024-01.com.vendor:nvme-rdma-01
/subsystems/ -> cd namespaces
/subsystems/nvme-rdma-01/namespaces/ -> create 1
/subsystems/nvme-rdma-01/namespaces/1/ -> set device path=/dev/nvme0n1

# 配置 RDMA 传输端口
/ports/ -> create 1
/ports/1/ -> set addr trtype=rdma,traddr=192.168.10.100,trsvcid=4420

# 发起端(Initiator)
nvme connect -t rdma -n nqn.2024-01.com.vendor:nvme-rdma-01 \
  -a 192.168.10.100 -s 4420

性能表现:

  • 延迟:~10 μs(本地 NVMe ~3 μs)
  • 带宽利用率:>95%(对比TCP的~60%)
  • IOPS:单 RoCE 25G 链路可达 3.8M IOPS

7.2 GPUDirect RDMA

NVIDIA GPUDirect RDMA 允许 HCA 直接读取 GPU 显存,跨节点 GPU 通信带宽突破 600 GB/s(NVLink 级性能):

// 分配 CUDA 显存
cudaMalloc(&gpu_buf, buf_size);

// 注册 CUDA 内存为可 RDMA 访问的 MR
struct ibv_mr *mr = ibv_reg_mr(pd, gpu_buf, buf_size,
              IBV_ACCESS_LOCAL_WRITE | IBV_ACCESS_REMOTE_WRITE |

              IBV_ACCESS_REMOTE_READ);

// RDMA Write 直接写入对端 GPU 显存(不经过 CPU)
// NCCL 内部已集成此机制
ncclAllReduce(send_buf, recv_buf, count, type, op, comm, stream);

7.3 DAOS 分布式存储引擎

Intel DAOS 是专为 NVMe + RDMA 设计的分布式对象存储引擎:

  • 元数据 + 数据双通道分离
  • bulk transfer 绕过 CPU 调度
  • 基于 RDMA 的多层复制仲裁(FPGA 参与故障恢复)
  • 单集群 EB 级容量,每节点 100Gbps RDMA 链路

8. 性能调优与故障排查

8.1 QP Cache 优化

动态 QP 创建有显著上下文切换开销(~20 μs),启用 QP Cache 可降低至 ~1 μs:

// 启用 QP 缓存
struct ibv_qp_init_attr_ex qp_attr = {
    .qp_type = IBV_QPT_RC,
    .comp_mask = IBV_QP_INIT_ATTR_PD |
                 IBV_QP_INIT_ATTR_SEND_OPS_FLAGS,
    .send_ops_flags = IBV_QP_EX_WITH_RDMA_WRITE |
                      IBV_QP_EX_WITH_SEND    |
                      IBV_QP_EX_WITH_ATOMIC_CMP_AND_SWP,
};
// 使用 QP Ex API 创建
ibv_create_qp_ex(ctx, &qp_attr);

8.2 CQ 轮询优化

默认的 CQ 轮询在高速场景下造成大量 CPU 占用:

  • Adaptive coalescing:设置合并阈值(adaptive-moderation),平衡延迟与 CPU 消耗
  • Busy polling:echo 1 > /sys/class/infiniband/mlx5_0/settings/busypolling
  • MSI-X 中断亲和:echo "0f" > /proc/irq/<irq_num>/smp_affinity,绑定到 NUMA 本地核心

8.3 GDB 调试指引

RDMA 常见问题排查:

# 查看QP状态
ibv_query_qp

# 监控 HCA 错误计数器
cat /sys/class/infiniband/mlx5_0/ports/1/counters/port_rcv_errors
cat /sys/class/infiniband/mlx5_0/ports/1/counters/port_xmit_discards

# 查看 rdma resource(QP/MR/CQ 使用统计)
rdma resource qp

# perftest 工具集
ib_write_bw -d mlx5_0 -i 1 --size 65536 -n 1000  # 带宽测试
ib_read_lat -d mlx5_0 -i 1 -n 1000                    # 延迟测试
ib_atomic_bw -d mlx5_0 -i 1 -F --size 8 -n 500     # 原子操作带宽

9. 完整代码框架

完整可编译的 UDMA 示例(简化版),包含 server 端与 client 端的核心逻辑框架:

// rdma_common.hpp
struct RDMAContext {
    struct ibv_context *ctx;
    struct ibv_pd      *pd;
    struct ibv_cq      *cq;
    struct ibv_qp      *qp;
    struct ibv_mr      *mr;
    char *buf;
    uint32_t rkey;
    uint64_t remote_addr;
};

// server.cpp
int main() {
    RDMAContext ctx;
    init_device(ctx, "mlx5_0");
    register_memory(ctx, 1024 * 1024);
    create_qp(ctx);

    // rdma listen + accept loop
    rdma_event_channel *ec = rdma_create_event_channel();
    rdma_cm_id *listen_id;
    rdma_create_id(ec, &listen_id, NULL, RDMA_PS_TCP);
    rdma_bind_addr(listen_id, &server_bind_addr);
    rdma_listen(listen_id, 8);

    while (true) {
        rdma_cm_event *event;
        rdma_get_cm_event(ec, &event);
        handle_connect_request(ctx, event->id);  // setup + accept
        rdma_ack_cm_event(event);

        // 等待数据就绪
        wait_data_processed(ctx);
    }
}

// client.cpp
int main() {
    RDMAContext ctx;
    init_device(ctx, "mlx5_0");
    register_memory(ctx, 1024 * 1024);
    create_qp(ctx);

    rdma_event_channel *ec = rdma_create_event_channel();
    rdma_cm_id *conn_id;
    rdma_create_id(ec, &conn_id, NULL, RDMA_PS_TCP);

    // resolve addr + route + connect
    rdma_resolve_addr(conn_id, ...);
    rdma_resolve_route(conn_id, ...);
    rdma_connect(conn_id, ...);

    // RDMA Write 发送数据
    struct ibv_send_wr wr = { .opcode = IBV_WR_RDMA_WRITE, ... };
    ibv_post_send(conn_id->qp, &wr, &bad_wr);
    // 通知server
    struct ibv_send_wr msg_wr = { .opcode = IBV_WR_SEND, ... };
    ibv_post_send(conn_id->qp, &msg_wr, &bad_wr);
}

10. 总结

RDMA 通过内核旁路、CPU 卸载、零拷贝三位一体机制,实现了亚微秒级延迟与高吞吐。在内核层面,Linux RDMA 子系统(ib_core、ib_uverbs、rdma_cm)为用户态 Verbs API 提供了可靠的基础架构。对于生产环境部署:

  • RoCE v2 是当前数据中心主流选择,需 PFC/ECN/DCQCN 无损网保障
  • InfiniBand 适用于超低延迟(HPC、AI 训练)场景
  • NVMe-oF RDMA 是跨网 NVMe 块存储的最佳实践
  • GPUDirect RDMA 是 AI 分布式训练 GPU 直连通信的事实标准

掌握 RDMA Verbs API 和连接管理(rdma_cm),能够帮助你构建下一代高性能分布式存储、GPU 集群通信和边缘计算应用。

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论
立即
投稿

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部