Skip to the content.

RDMA Core 详解

什么是 RDMA Core

RDMA Core 是 Linux 内核 RDMA 子系统的用户态组件,提供用户空间程序访问 RDMA 硬件的能力。它本身不替代内核功能,而是作为用户态通往 RDMA 硬件的桥梁

用户态程序 (SPDK, NCCL, MPI, Redis...)
         │
         ▼
┌─────────────────────────────────────┐
│          RDMA Core (用户态)          │
│  ┌──────────────┐ ┌──────────────┐  │
│  │ libibverbs   │ │ librdmacm    │  │
│  │ (数据传输)    │ │ (连接管理)    │  │
│  └──────────────┘ └──────────────┘  │
│  ┌────────────────────────────────┐ │
│  │ providers/ (mlx5, mlx4, efa...)│ │
│  │ 用户态驱动部分,直接硬件访问     │ │
│  └────────────────────────────────┘ │
└────────────┬────────────────────────┘
             │ /dev/infiniband/uverbsX
             │ (ioctl / mmap)
             ▼
┌─────────────────────────────────────┐
│        内核 RDMA 子系统              │
│    drivers/infiniband/core/         │
│    drivers/infiniband/hw/mlx5/      │
└─────────────────────────────────────┘

核心组件

1. 主要库

功能
libibverbs 提供 IB Verbs API,是 RDMA 编程的核心接口(设备管理、QP/CQ/MR 等)
librdmacm 提供 RDMA 连接管理(Connection Manager),处理地址解析、连接建立
libibumad 提供 IB 管理数据报文接口(MAD),用于诊断和管理工具

2. 设备驱动用户态部分 (providers/)

providers/
├── mlx5/          # Mellanox ConnectX-5/6/7 网卡驱动用户态部分
├── mlx4/          # Mellanox ConnectX-3 网卡驱动
├── irdma/         # Intel E810 等网卡驱动
├── efa/           # AWS EFA (Elastic Fabric Adapter)
├── rxe/           # Soft-RoCE (软件 RoCE)
├── siw/           # Soft-iWARP
└── ...            # 其他厂商驱动

控制路径 vs 数据路径分离

RDMA Core 的关键设计:控制路径在内核,数据路径在用户态

需要内核的操作(控制路径)

操作 为什么需要内核
设备发现/初始化 内核创建设备文件 /dev/infiniband/uverbsX
创建 QP/CQ 内核分配硬件资源,检查权限
内存注册 (MR) 内核 pin 住内存(防止 swap),建立 DMA 映射
连接建立 内核处理网络层面的连接协商
销毁资源 内核回收硬件资源
// 这些操作通过 ioctl 进入内核
struct ibv_qp *qp = ibv_create_qp(pd, &attr);     // ← 内核协助
struct ibv_mr *mr = ibv_reg_mr(pd, buf, size, ...); // ← 内核 pin 内存

不需要内核的操作(数据路径)

操作 实现方式
post_send (发送) 用户态直接写硬件 doorbell 寄存器(MMIO)
post_recv (接收) 用户态直接写硬件 doorbell 寄存器(MMIO)
poll_cq (轮询完成) 用户态直接读取内存映射的 CQ
RDMA Read/Write 网卡硬件直接操作,CPU 不介入
// 这些操作直接访问 mmap 映射的硬件,无系统调用
ibv_post_send(qp, &wr, &bad_wr);    // ← 用户态直接写 doorbell
ibv_poll_cq(cq, num_entries, wc);   // ← 用户态直接读内存

RDMA Core 与内核的关系

关键机制:mmap

RDMA Core 通过 mmap 将内核准备好的资源映射到用户态,之后独立操作:

1. 创建阶段(进入内核)
   ibv_create_cq() → ioctl → 内核分配资源 → 返回 fd

2. 映射阶段
   mmap(fd) → 用户态获得直接访问权限

3. 使用阶段(纯用户态)
   ibv_poll_cq() → 直接读内存,不进内核

与 DPDK 的区别

特性 DPDK RDMA (rdma-core)
内核依赖 完全独立,通过 VFIO 直接控制 需要内核初始化,部分操作依赖内核
内存管理 自己管理 hugepage 需要内核 pin 内存(ibv_reg_mr
设备发现 扫描 PCI 总线 依赖内核创建的设备文件
安全性 应用自己负责 内核检查权限

为什么内核 NVMe-oF 不需要 RDMA Core

内核态驱动直接使用内核原生的 RDMA API,与 rdma-core 完全无关:

// 内核态 NVMe-oF (drivers/nvme/target/rdma.c)
#include <rdma/ib_verbs.h>      // 内核头文件
#include <rdma/rdma_cm.h>       // 内核连接管理

struct ib_qp *qp = ib_create_qp(pd, &attr);  // 直接内核函数调用
ib_post_send(qp, &wr, &bad_wr);              // 直接内核函数调用

对比用户态 SPDK:

// 用户态 SPDK 通过 rdma-core
#include <infiniband/verbs.h>   // 用户态头文件

struct ibv_qp *qp = ibv_create_qp(pd, &attr);  // ioctl 到内核
ibv_post_send(qp, &wr, &bad_wr);               // 用户态直接写 doorbell

使用方对比

使用方 调用的 API 是否需要 rdma-core 运行位置
内核 NVMe-oF <rdma/ib_verbs.h> 内核 API 不需要 内核态
SPDK <infiniband/verbs.h> libibverbs 需要 用户态
NCCL <infiniband/verbs.h> libibverbs 需要 用户态
Lustre/CIFS 内核 RDMA API 不需要 内核态

RDMA Core 的核心价值

RDMA Core 像 RDMA 领域的 “libc”——只为用户态服务

  1. 系统调用封装:通过 /dev/infiniband/uverbsX 与内核通信
  2. mmap 管理:将内核资源映射到用户态,实现零拷贝访问
  3. 用户态 doorbell:提供用户态直接写硬件的能力(性能关键)
  4. 统一 API:封装不同厂商硬件的差异

一句话总结

rdma-core 是”用户态通往 RDMA 硬件的桥梁”

内核中的 rdma 的代码分布

  1. RoCEv2 相关代码

核心 RoCE 代码

文件/目录 说明 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ drivers/infiniband/core/roce_gid_mgmt.c RoCE GID (全局标识符) 管理 drivers/infiniband/sw/rxe/ Soft RoCE - 软件实现的 RoCEv2,可在任何以太网卡上运行

硬件驱动 (RoCEv2 网卡驱动)

目录 厂商/芯片 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ drivers/infiniband/hw/mlx4/ Mellanox ConnectX-3 系列 drivers/infiniband/hw/mlx5/ Mellanox ConnectX-4/5/6/7 系列 drivers/infiniband/hw/bnxt_re/ Broadcom RoCE (BNXT 系列) drivers/infiniband/hw/irdma/ Intel RDMA (i40e, ice 网卡) drivers/infiniband/hw/qedr/ QLogic/Marvell RoCE drivers/infiniband/hw/hns/ 华为 RoCE (HiNIC) drivers/infiniband/hw/erdma/ 阿里/其他 RDMA drivers/infiniband/hw/ocrdma/ Emulex RoCE

──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

  1. RDMA 标准/核心代码

核心子系统

目录 说明 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ drivers/infiniband/core/ RDMA 核心层 (348+ 个文件) include/rdma/ 内核 RDMA 头文件 include/uapi/rdma/ 用户空间 API 头文件 (应用程序接口)

关键核心文件

drivers/infiniband/core/ ├── verbs.c # RDMA Verbs 核心实现 ├── cma.c # Connection Manager (连接管理) ├── cm.c # Communication Manager ├── mad.c # Management Datagram ├── sa_query.c # Subnet Administrator query ├── uverbs_*.c # 用户空间 verbs 接口 ├── rdma_core.c # RDMA 核心功能 └── addr.c # 地址解析

ULP (上层协议)

目录 协议 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ drivers/infiniband/ulp/ipoib/ IP over IB (IPoIB) drivers/infiniband/ulp/iser/ iSCSI over RDMA drivers/infiniband/ulp/srpt/ SCSI RDMA Protocol Target drivers/infiniband/ulp/rtrs/ RDMA Transport

──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

  1. 相关文档

Documentation/driver-api/infiniband.rst # RDMA 内核 API 文档 Documentation/networking/device_drivers/ethernet/mellanox/mlx5/ # MLX5 文档 Documentation/admin-guide/nfs/nfs-rdma.rst # NFS over RDMA

──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

  1. 外部标准规范

RDMA 和 RoCEv2 的标准由以下组织定义:

标准 组织 描述 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ InfiniBand Architecture Spec IBTA InfiniBand 基础规范 RoCEv2 Spec IBTA RDMA over Converged Ethernet v2 RDMA Verbs OFED / IBTA 编程接口标准 iWARP (RFC 5040-5044) IETF Internet Wide Area RDMA Protocol libibverbs Linux-RDMA 用户空间库 (用户态)

nfs over rdma 如何理解

NFS over RDMA 通过绕过传统 TCP/IP 协议栈,利用 RDMA 的零拷贝能力,实现了极高性能的 NFS 存储访问。内核中的实现主要分为客户端 (x prtrdma) 和服务端 (svc_rdma) 两部分,通过高效的 FRWR 内存注册机制,最小化了内存管理开销。

RDMA 是基于哪一层的

  1. RoCEv2 与 UDP 的关系

RoCEv2 确实使用了 UDP 作为封装层,但这只是网络传输的需要:

RoCEv2 数据包结构: ┌─────────┬─────────┬─────────┬─────────┬─────────────────┐ │ Eth Hdr │ IP Hdr │ UDP Hdr │ RoCEv2 │ RDMA Payload │ │ │ │ Dst=4791│ Header │ │ └─────────┴─────────┴─────────┴─────────┴─────────────────┘ ↑ ↑ 路由可达性 真正的 RDMA 语义 (使用 IP/UDP) (Send/Write/Read/Atomic)

关键点:

• RoCEv2 使用 UDP 仅为了在 IP 网络中路由 • UDP 端口号固定为 4791 • RDMA 的操作语义(Send/Recv/RDMA Write/RDMA Read/Atomic)与 UDP 无关

3. Soft RoCE (RXE) 实现分析

  1. 发送路径
// drivers/infiniband/sw/rxe/rxe_net.c:360-384
static int prepare4(struct rxe_av *av, struct rxe_pkt_info *pkt,
                    struct sk_buff *skb)
{
    // ... 路由查找 ...

    // 步骤 1: 软件构建 UDP 头部
    prepare_udp_hdr(skb, cpu_to_be16(qp->src_port),
                    cpu_to_be16(ROCE_V2_UDP_DPORT));  // 4791

    // 步骤 2: 软件构建 IP 头部
    prepare_ipv4_hdr(dst, skb, saddr->s_addr, daddr->s_addr,
                     IPPROTO_UDP,  // 协议字段设置为 UDP
                     av->grh.traffic_class, av->grh.hop_limit, df, xnet);

    // ... 发送 ...
}

// UDP 头部构建
static void prepare_udp_hdr(struct sk_buff *skb, __be16 src_port,
                            __be16 dst_port)
{
    struct udphdr *udph;

    __skb_push(skb, sizeof(*udph));
    skb_reset_transport_header(skb);
    udph = udp_hdr(skb);

    udph->dest = dst_port;      // 4791
    udph->source = src_port;    // 动态源端口
    udph->len = htons(skb->len);
    udph->check = 0;            // RoCEv2 不使用 UDP 校验和
}
  1. 接收路径
// drivers/infiniband/sw/rxe/rxe_net.c:214-252
static int rxe_udp_encap_recv(struct sock *sk, struct sk_buff *skb)
{
    struct udphdr *udph;
    struct rxe_pkt_info *pkt = SKB_TO_PKT(skb);

    // 获取 UDP 头部
    udph = udp_hdr(skb);

    // 关键: 跳过 UDP 头部,直接指向 RDMA Payload
    pkt->hdr = (u8 *)(udph + 1);  // 指向 BTH/GRH 开始位置
    pkt->mask = RXE_GRH_MASK;
    pkt->paylen = be16_to_cpu(udph->len) - sizeof(*udph);

    // 移除 UDP 头部
    skb_pull(skb, sizeof(struct udphdr));

    // 处理 RDMA 数据包
    rxe_rcv(skb);
    return 0;
}
  1. 初始化 UDP 监听
// drivers/infiniband/sw/rxe/rxe_net.c:713-735
recv_sockets.sk4 = rxe_setup_udp_tunnel(&init_net,
                htons(ROCE_V2_UDP_DPORT), false);  // IPv4
recv_sockets.sk6 = rxe_setup_udp_tunnel(&init_net,
                htons(ROCE_V2_UDP_DPORT), true);   // IPv6

4. 硬件 RDMA (Mellanox mlx5) 实现

  1. Doorbell 机制
// drivers/infiniband/hw/mlx5/wr.c:1030-1050
void mlx5r_send_wqe(struct mlx5_ib_qp *qp, struct mlx5r_qp_sq *sq,
                    struct mlx5r_send_bf *bf, int size)
{
    // 1. 构建 WQE (Work Queue Entry) - 只包含 RDMA 语义
    struct mlx5_wqe_ctrl_seg *ctrl = ...;
    ctrl->opmod_idx_opcode = cpu_to_be32(...);

    // 2. 写入 doorbell (MMIO 到网卡硬件)
    wmb();  // 内存屏障
    mlx5_write64((__be32 *)ctrl, bf->bfreg->map + bf->offset);

    // 网卡硬件自动处理:
    // - 构建 UDP/IP 头部
    // - 计算校验和
    // - 直接 DMA 数据到对端
}
  1. UAR (User Access Region) 映射

mlx5_ib_mmap 的:

// drivers/infiniband/hw/mlx5/main.c:2350-2460
static int mlx5_ib_mmap(struct ib_ucontext *context, struct vm_area_struct *vma)
{
    // 将网卡寄存器映射到用户空间
    pfn = uar_index2pfn(dev, uar_index);
    vma->vm_page_prot = pgprot_writecombine(prot);
    io_remap_pfn_range(vma, vma->vm_start, pfn, PAGE_SIZE, vma->vm_page_prot);
}
┌──────────────────────────────────────────────────────────────────────────┐
│                      Soft RoCE (RXE) vs 硬件 RDMA                         │
├──────────────────────────────────────────────────────────────────────────┤
│                                                                          │
│  RXE (软件实现)                      硬件 RDMA (mlx5/mlx4)               │
│  ─────────────                      ────────────────────                 │
│                                                                          │
│  用户态                              用户态                               │
│    │                                   │                                  │
│    ▼                                   ▼                                  │
│  libibverbs ──► 内核驱动              libibverbs ──► 直接 MMIO (mmap)     │
│                    │                            (用户态直接写网卡)        │
│                    ▼                                                      │
│              prepare4()                       ┌─────────────────┐        │
│                    │                          │  网卡硬件 (NIC)  │        │
│                    ▼                          │                 │        │
│              build UDP hdr                    │ • 自动构建 ETH   │        │
│                    │                          │ • 自动构建 IP    │        │
│                    ▼                          │ • 自动构建 UDP   │        │
│              build IP hdr                     │ • 自动 DMA 数据  │        │
│                    │                          │ • 硬校验和计算   │        │
│                    ▼                          └─────────────────┘        │
│              Linux 网络栈                                                 │
│                    │                                                      │
│                    ▼                                                      │
│              网卡驱动 (常规发送)                                          │
│                                                                          │
│  ⚠️ 经过内核协议栈                    ✅ 绕过内核,用户态直达网卡         │
│                                                                          │
└──────────────────────────────────────────────────────────────────────────┘

┌────────────────────────────────────────────────────────────────────┐ │ RoCEv2 的本质 │ ├────────────────────────────────────────────────────────────────────┤ │ │ │ UDP 层的作用 (仅用于网络传输): │ │ ├── 目的端口 4791 用于标识这是 RoCEv2 流量 │ │ ├── 允许数据包通过标准 IP 路由器 │ │ ├── 支持 ECN (Explicit Congestion Notification) │ │ └── 不依赖专门的 InfiniBand 交换机 │ │ │ │ UDP 层不做的事: │ │ ├── 不处理重传 (RDMA 自己做) │ │ ├── 不做流量控制 (RDMA Credit 机制) │ │ └── 不做可靠性保证 (RDMA PSN 机制) │ │ │ │ RDMA 头部 (BTH + 扩展头部): │ │ ├── 包含完整的 RDMA 操作语义 │ │ ├── 支持 Send/Write/Read/Atomic 等操作 │ │ ├── 实现可靠传输 (RC) 和不可靠传输 (UD) │ │ └── 与 InfiniBand 原生头部兼容 │ │ │ └────────────────────────────────────────────────────────────────────┘

RDMA 中断的处理模式

(感觉就是这么回事了,但是需要继续核查一下)

用户态接收完成的两种模式

┌───────────────────────────────────────────────────────────────────────────┐ │ RDMA 用户态完成通知机制 │ ├─────────────────────────────┬─────────────────────────────────────────────┤ │ 1. 轮询 (Polling) │ 2. 中断/事件通知 (Event) │ ├─────────────────────────────┼─────────────────────────────────────────────┤ │ │ │ │ 用户态直接读取 CQ 内存 │ 通过文件描述符等待中断通知 │ │ (mmap 的 CQ 缓冲区) │ (comp_channel) │ │ │ │ │ ├── 延迟最低 │ ├── 节省 CPU │ │ ├── 100% 用户态 │ ├── 适合低频率完成场景 │ │ └── 持续占用 CPU │ └── 有上下文切换开销 │ │ │ │ └─────────────────────────────┴─────────────────────────────────────────────┘

  1. 轮询模式 (Polling)

用户态通过 mmap 直接访问 CQ (Completion Queue) 缓冲区,不断轮询检查新的完成条目。

1.1 CQ 内存映射流程

// drivers/infiniband/hw/mlx5/cq.c:718-863
static int create_cq_user(struct mlx5_ib_dev *dev, struct ib_udata *udata,
                          struct mlx5_ib_cq *cq, int entries, ...)
{
    // 1. 用户态分配 CQ 缓冲区内存
    // 2. 内核通过 ib_umem_get() 获取该内存的物理页
    cq->buf.umem = ib_umem_get(&dev->ib_dev, ucmd.buf_addr,
                               entries * ucmd.cqe_size,
                               IB_ACCESS_LOCAL_WRITE);

    // 3. 将物理页地址 (PAS) 传递给硬件
    pas = (__be64 *)MLX5_ADDR_OF(create_cq_in, *cqb, pas);
    mlx5_ib_populate_pas(cq->buf.umem, page_size, pas, 0);

    // 4. 硬件直接 DMA 完成事件到这个缓冲区
    // 用户态通过 mmap 可以直接读取
}

1.2 用户态轮询代码

// 用户态代码 (libibverbs)

struct ibv_cq *cq;  // 已创建并 mmap 的 CQ

// 轮询完成 (无系统调用,纯用户态)
int poll_cq() {
    struct ibv_wc wc;
    int n;

    // 直接读取 mmap 的 CQ 内存
    while ((n = ibv_poll_cq(cq, 1, &wc)) == 0) {
        // 自旋等待,无中断
        // 可选:pause/yield 避免过度占用 CPU
    }

    // 处理完成
    if (n > 0) {
        handle_completion(&wc);
    }
}

(这个就有待确认了)

  1. 中断/事件通知模式 (Event)

当完成频率较低时,使用 中断 + 文件描述符 机制避免 CPU 空转。

2.1 创建完成通道 (Completion Channel)

// drivers/infiniband/core/uverbs_cmd.c:998-1020
static int ib_uverbs_create_comp_channel(
    struct uverbs_attr_bundle *attrs)
{
    struct ib_uverbs_completion_event_file *ev_file;

    // 创建一个文件描述符 (fd)
    ev_file = container_of(uobj, struct ib_uverbs_completion_event_file,
                           uobj);

    // 初始化事件队列
    ib_uverbs_init_event_queue(&ev_file->ev_queue);

    // 返回 fd 给用户态
    resp.fd = uobj->id;
}

2.2 CQ 关联完成通道

// drivers/infiniband/core/uverbs_std_types_cq.c:102-108
ev_file_uobj = uverbs_attr_get_uobject(attrs,
                   UVERBS_ATTR_CREATE_CQ_COMP_CHANNEL);
if (!IS_ERR(ev_file_uobj)) {
    ev_file = container_of(ev_file_uobj,
                   struct ib_uverbs_completion_event_file, uobj);
    // 将 CQ 与完成通道关联
    cq->cq_context = &ev_file->ev_queue;
}

2.3 中断处理流程

┌─────────────────────────────────────────────────────────────────────┐ │ 完成中断处理流程 │ ├─────────────────────────────────────────────────────────────────────┤ │ │ │ 1. 网卡硬件产生完成中断 │ │ │ │ │ ▼ │ │ 2. 内核中断处理程序 (mlx5_ib_cq_comp) │ │ │ │ │ ▼ │ │ 3. 调用 ib_uverbs_comp_handler() │ │ // drivers/infiniband/core/uverbs_main.c:369 │ │ │ │ │ ▼ │ │ 4. 将事件加入队列 │ │ list_add_tail(&entry->list, &ev_queue->event_list); │ │ │ │ │ ▼ │ │ 5. 唤醒等待的用户态进程 │ │ wake_up_interruptible(&ev_queue->poll_head); │ │ │ │ │ ▼ │ │ 6. 用户态通过 read(fd) 读取事件 │ │ // 返回 ib_uverbs_comp_event_desc │ │ │ └─────────────────────────────────────────────────────────────────────┘

2.4 内核事件处理代码

// drivers/infiniband/core/uverbs_main.c:369-399 void ib_uverbs_comp_handler(struct ib_cq *cq, void *cq_context) { struct ib_uverbs_event_queue *ev_queue = cq_context; struct ib_uverbs_event *entry;

// 分配事件条目
entry = kzalloc(sizeof(*entry), GFP_ATOMIC);

// 记录完成信息
entry->desc.comp.cq_handle = cq->uobject->uevent.uobject.user_handle;
entry->counter = &uobj->comp_events_reported;

// 加入事件队列
list_add_tail(&entry->list, &ev_queue->event_list);
list_add_tail(&entry->obj_list, &uobj->comp_list);

// 唤醒等待的用户态进程
wake_up_interruptible(&ev_queue->poll_head);
kill_fasync(&ev_queue->async_queue, SIGIO, POLL_IN); }

2.5 用户态事件接收代码

// 用户态代码 (libibverbs) struct ibv_comp_channel *channel = ibv_create_comp_channel(ctx); struct ibv_cq *cq = ibv_create_cq(ctx, 100, NULL, channel, 0);

// 请求通知 (启用中断模式) ibv_req_notify_cq(cq, 0);

// 等待完成事件 struct ibv_cq *ev_cq; void *ev_ctx; ibv_get_cq_event(channel, &ev_cq, &ev_ctx); // 阻塞等待

// 收到事件后,仍需 poll_cq 获取具体完成 int n = ibv_poll_cq(ev_cq, max_wc, wc_array);

// 确认事件已处理 ibv_ack_cq_events(ev_cq, 1);

───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

  1. 关键数据结构

// 完成事件文件 (对应用户态文件描述符) struct ib_uverbs_completion_event_file { struct ib_uobject uobj; struct ib_uverbs_event_queue ev_queue; // 事件队列 };

// 事件队列 struct ib_uverbs_event_queue { spinlock_t lock; wait_queue_head_t poll_head; // 等待队列 (用于 wake_up) struct fasync_struct *async_queue; // 异步通知 (SIGIO) struct list_head event_list; // 事件链表 };

// 完成事件描述符 struct ib_uverbs_comp_event_desc { __u64 cq_handle; // CQ 句柄 };

───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

  1. 混合使用策略

高性能应用通常结合两种模式:

// 1. 忙轮询一段时间 (低延迟) for (int i = 0; i < max_poll; i++) { n = ibv_poll_cq(cq, batch, wc); if (n > 0) process_completions(wc, n); }

// 2. 如果没有完成,切换到中断模式 (节省 CPU) if (n == 0) { ibv_req_notify_cq(cq, 0); // 启用中断 // 再次检查避免竞态 n = ibv_poll_cq(cq, batch, wc); if (n == 0) { ibv_get_cq_event(channel, &ev_cq, &ev_ctx); // 阻塞等待中断 } }

  1. 总结

机制 触发方式 延迟 CPU 占用 适用场景 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Doorbell (发送) 用户态 MMIO 极低 无 发送请求 CQ Polling (接收) 用户态轮询 极低 高 高频交易 CQ Event (接收) 内核中断 较高 低 低频 I/O

关键理解:虽然 mlx5_ib_mmap 让用户态可以直接通过 MMIO 发送命令(Doorbell),但接收完成有两种路径:

  1. 直接轮询:用户态直接读取 mmap 的 CQ 内存
  2. 中断通知:通过文件描述符等待内核唤醒,然后 poll CQ 获取具体完成

rdma librdmacm 是做什么的

cm 是 Connection Manager 的缩写。

在 RDMA 语境里:

librdmacm/ 是 RDMA Connection Manager 的用户态库实现,也就是 librdmacm。它的作用不是做真正的数据收发,而是帮应用完成“怎么找到对端、怎么建连、 怎么交换队列参数、怎么断开连接”这层工作。

在职责上可以粗分成几块:

和别的目录关系也很明确:

如果你想从代码入口看,先看这些位置:

内核中对应的代码为:

  1. 先看 include/uapi/rdma/rdma_user_cm.h
  2. 再看 drivers/infiniband/core/ucma.c
  3. 最后看 drivers/infiniband/core/cma.c

这样最容易把用户态 librdmacm 的调用链对应到内核。

本站所有文章转发 CSDN 将按侵权追究法律责任,其它情况随意。