☰
RDMA-Verbs-内核 RDMA Core(六)
2026/10/22 2:27:48 网站建设 项目流程

1. 概述

Linux RDMA Core 位于 uverbs、内核消费者与硬件驱动之间。其用struct ib_device表示一个 RDMA 设备,以struct ib_device_ops连接 mlx5_ib 驱动,并为 PD、MR、CQ、QP 等资源提供通用对象、依赖计数和资源跟踪。用户态命令和内核 Verbs 最终都可以到达同一组驱动回调,但两条路径的对象入口、用户数据和清理规则不同。

RDMA 驱动创建设备对象:mlx5_ib 的mlx5r_probe()调用ib_alloc_device_with_net(),由 RDMA Core 的_ib_alloc_device()分配包含内嵌ib_device的完整mlx5_ib_dev对象。Device(设备管理)负责ib_device的注册、端口和设备引用;Verbs 通过显式设备参数或 PD、QP、CQ 等对象的device成员取得该设备,再使用内嵌的ib_device.ops分发到驱动。例如ib_create_qp_kernel()从pd->device取得设备,通用create_qp()再调用dev->ops.create_qp()。mlx5_ib 在初始化时通过ib_set_device_ops()安装操作表,并通过ib_register_device()发布设备。

RDMA Core 的关键作用可以归纳为四点:

  • 设备抽象:为不同厂商驱动提供统一的ib_device、端口、属性和注册状态。
  • 操作分发:通过ib_device_ops把通用 Verbs 请求分发到具体驱动。
  • 对象管理:建立ib_ucontext、PD、MR、CQ、QP 等通用对象及其依赖关系。
  • 生命周期:使用设备引用、对象usecnt、uverbs 对象引用和 restrack 引用维护生命周期。

2. 设备注册与注销

2.1 设备对象分配

mlx5_ib 的设备探测入口mlx5r_probe()使用ib_alloc_device_with_net(mlx5_ib_dev, ib_dev, net)分配struct mlx5_ib_dev。宏通过成员类型和偏移检查保证ib_dev位于驱动对象起始位置,再调用_ib_alloc_device()分配完整的驱动对象。这样,同一块内存既能以struct ib_device *交给 RDMA Core,也能在 mlx5_ib 内部还原为struct mlx5_ib_dev *。

_ib_alloc_device()此时只建立尚未发布的设备对象,主要完成以下工作:

  1. 按驱动对象大小分配并清零内存。
  2. 调用rdma_restrack_init(),为各类可跟踪资源建立 XArray 根。
  3. 初始化内嵌的 Linuxstruct device、网络命名空间和端口链表。
  4. 初始化事件、Client 数据、注销、CQ pool、cache 和子设备相关的锁、XArray、链表与 completion。
  5. 设置 legacy uverbs 命令能力掩码。

struct ib_device中与本文主线有关的成员如下。设备操作表、注册引用和 restrack 根分别承担分发、注销同步和资源可观察性。

structib_device{/* 执行 DMA 的底层 Linux 设备 */structdevice*dma_device;/* 由硬件驱动安装的操作回调和对象大小 */structib_device_opsops;/* RDMA 设备名称 */charname[IB_DEVICE_NAME_MAX];/* 端口属性、cache 与端口状态 */structib_port_data*port_data;/* Linux 设备模型与 RDMA Core 设备视图 */union{structdevicedev;structib_core_devicecoredev;};/* 全局设备 XArray 使用的设备索引 */u32 index;/* 按资源类型划分的 restrack 根数组 */structrdma_restrack_root*res;/* 非零表示设备仍处于可获取的注册状态 */refcount_trefcount;/* refcount 归零时唤醒注销流程 */structcompletionunreg_completion;/* 串行化并发注销与注册竞争 */structmutexunregistration_lock;};

2.2 注册流程

mlx5_ib 在设备对象和操作表准备好后,调用ib_register_device()注册设备。

  1. 确定身份与能力。分配唯一名称和索引,取得设备及端口属性,为缓存和资源操作提供依据。
  2. 准备端口缓存。建立 GID、P_Key 和端口状态的初始视图;RoCE GID 随网络设备扫描及事件更新,为后续查询提供统一数据。
  3. 建立设备视图。接入 Linux 设备模型,准备 sysfs、统计和 cgroup 状态。暂缓 ADD 事件,避免用户空间收到通知时设备还未准备好。
  4. 发布并保护注册状态。设置DEVICE_REGISTERED,建立基础引用和注册线程临时引用;用锁保护接入阶段,防止并发注销提前 remove 设备。
  5. 接入消费者。执行可选项、关联 Client,并按需建立网络命名空间兼容视图。Client 机制见 RDMA-Verbs-内核 uverbs 接口(五)。
  6. 通知就绪。发送设备 ADD 和 RDMA 注册等通知。

2.3 注销流程

mlx5_ib 注销流程和初始化流程相反:先注销设备,清理完全部资源后再调用ib_dealloc_device()。

  1. 保护清理过程。临时持有 Linux device 内存引用,并串行化设备注销过程,避免对象提前释放或被重复清理。
  2. 撤销注册资格。清除注册标记,解除 Client 关联并清理共用 CQ pool。
  3. 等待使用者退出。返还注册引用,等待所有注册引用归零,再移除兼容视图。
  4. 停止外部与后台活动。remove 设备、端口和 cgroup 视图,禁止 GID 更新并等待异步任务结束;缓存停止工作,但内存暂不释放。
  5. 交还分配引用。驱动清理完成后,dealloc 移除全局索引、检查资源是否遗留,并释放分配阶段持有的 Linux device 引用。
  6. 最终回收内存。最后一个内存引用触发ib_device_release(),释放缓存等内部状态,再通过 RCU 延迟回收设备对象。因此,注销完成不等于内存立即释放。

3. 操作接口与分发

3.1ib_device_ops

struct ib_device_ops是 RDMA Core 与硬件驱动之间的主要接口。它既包含控制路径回调,也包含内核消费者使用的数据路径回调。

操作类别代表回调RDMA Core 的主要工作
设备与端口query_device、query_port、get_port_immutable建立统一设备属性、端口属性和 link layer 视图
用户上下文alloc_ucontext、dealloc_ucontext、mmap、disassociate_ucontext关联 uverbs file、映射对象和设备移除状态
资源创建alloc_pd、create_cq、create_qp、reg_user_mr分配或接收通用对象,传递ib_udata,建立依赖并加入 restrack
资源销毁dealloc_pd、destroy_cq、destroy_qp、dereg_mr检查依赖、调用驱动、解除引用并移除 restrack
内核数据路径post_send、post_recv、poll_cq、req_notify_cq提供统一内核 Verbs 入口并分发到驱动实现
对象布局size_ib_pd、size_ib_cq、size_ib_qp、size_ib_ucontext决定 Core 分配的通用对象与驱动私有对象总大小

操作表中的重点声明如下。这里保留实际参数类型,用户态附加请求通过ib_udata或uverbs_attr_bundle进入驱动。

structib_device_ops{/* 查询设备和端口能力 */int(*query_device)(structib_device*device,structib_device_attr*device_attr,structib_udata*udata);int(*query_port)(structib_device*device,u32 port_num,structib_port_attr*port_attr);/* 建立和释放用户上下文 */int(*alloc_ucontext)(structib_ucontext*context,structib_udata*udata);void(*dealloc_ucontext)(structib_ucontext*context);void(*disassociate_ucontext)(structib_ucontext*ibcontext);/* 创建和释放 PD */int(*alloc_pd)(structib_pd*pd,structib_udata*udata);int(*dealloc_pd)(structib_pd*pd,structib_udata*udata);/* 创建、修改和销毁 QP */int(*create_qp)(structib_qp*qp,structib_qp_init_attr*qp_init_attr,structib_udata*udata);int(*modify_qp)(structib_qp*qp,structib_qp_attr*qp_attr,intqp_attr_mask,structib_udata*udata);int(*destroy_qp)(structib_qp*qp,structib_udata*udata);/* 创建和销毁 CQ */int(*create_cq)(structib_cq*cq,conststructib_cq_init_attr*attr,structuverbs_attr_bundle*attrs);int(*destroy_cq)(structib_cq*cq,structib_udata*udata);/* 注册和注销用户 MR */structib_mr*(*reg_user_mr)(structib_pd*pd,u64 start,u64 length,u64 virt_addr,intmr_access_flags,structib_dmah*dmah,structib_udata*udata);int(*dereg_mr)(structib_mr*mr,structib_udata*udata);/* 内核态请求提交和完成处理 */int(*post_send)(structib_qp*qp,conststructib_send_wr*send_wr,conststructib_send_wr**bad_send_wr);int(*post_recv)(structib_qp*qp,conststructib_recv_wr*recv_wr,conststructib_recv_wr**bad_recv_wr);int(*poll_cq)(structib_cq*cq,intnum_entries,structib_wc*wc);};

3.2 操作表安装与对象大小

ib_device_ops不是由 mlx5_ib 一次性整体赋值,而是通过多张操作表逐步拼装。ib_set_device_ops()遍历传入操作表:非空回调只在目标槽位尚为空时写入,因此先安装的回调优先,后续操作表只能补齐空槽位,不能覆盖已有实现。owner、driver_id、uverbs_abi_ver和 uverbs 能力标志单独处理。

mlx5_ib 在设备注册前按以下顺序安装操作表:

  1. mlx5_ib_stage_caps_init()根据硬件能力安装 SR-IOV、MW、XRC、DM 等可选操作表,再安装mlx5_ib_dev_ops,由主操作表补齐通用 Verbs 回调。
  2. MLX5_IB_STAGE_NON_DEFAULT_CB安装端口操作。普通设备使用mlx5_ib_dev_port_ops;raw Ethernet representor 使用mlx5_ib_dev_port_rep_ops。
  3. RoCE 初始化按需安装 WQ 和 RWQ indirection table 操作。之后ib_register_device()才检查必选回调并发布设备。

3.3 内核 Verbs 调用路径

内核消费者通过<rdma/ib_verbs.h>提供的 Kernel Verbs 调用 RDMA Core,不经过 uverbs。对外入口通常是宏或static inline函数:宏把KBUILD_MODNAME一并传给 Core,用于标记资源所属的内核模块;对象型接口则从 PD、QP、CQ 等对象的device成员找到ib_device,再访问其中的操作表。

资源管理接口会进入verbs.c的通用实现。以 QP 创建为例,ib_create_qp()调用ib_create_qp_kernel(),后者从pd->device取得设备并进入create_qp()。RDMA Core 按驱动登记的对象大小分配 QP,初始化通用成员和 restrack,再调用dev->ops.create_qp();mlx5_ib 安装在该槽位的mlx5_ib_create_qp()最终创建驱动私有资源和硬件 QP。驱动成功后,Core 再建立安全状态和 PD、CQ、SRQ 等父对象的usecnt依赖。

PD 和 CQ 采用相同结构:ib_alloc_pd()、ib_create_cq()分别进入__ib_alloc_pd()、__ib_create_cq(),完成通用对象初始化后调用device->ops.alloc_pd()、device->ops.create_cq()。

基础收发接口更短。ib_post_send()、ib_post_recv()和ib_poll_cq()在ib_verbs.h中以内联函数实现,直接从 QP 或 CQ 找到device->ops并调用驱动回调,不再进入verbs.c的对象生命周期流程。这样仍保留统一的 Kernel Verbs 和ib_device_ops抽象,同时减少数据路径上的额外调用层次。

4. 核心对象模型

4.1 device 与 ucontext

ib_device是所有资源的根。每次用户进程打开 uverbs 设备并执行 GET_CONTEXT,Core 通过ib_alloc_ucontext()按size_ib_ucontext分配通用/私有上下文,设置设备和 uverbs file,再由ib_init_ucontext()调用ops.alloc_ucontext()。成功后,context 加入 restrack,并以 release store 发布到file->ucontext。

structib_ucontext{/* context 所属 RDMA 设备 */structib_device*device;/* 拥有该 context 的 uverbs 文件 */structib_uverbs_file*ufile;/* 用户 mmap 对象索引 */structxarraymmap_xa;/* CTX 类型的资源跟踪项 */structrdma_restrack_entryres;};

内核消费者不创建ib_ucontext,而是直接持有ib_device及其资源对象。ib_ucontext因此表示用户文件与驱动用户上下文之间的关系,不是所有内核 RDMA 资源的共同父对象。

4.2 PD、MR、CQ 与 QP

PD 建立访问保护域。MR 和 QP 引用 PD;QP 还引用 send CQ、recv CQ,以及可选的 SRQ 和 RWQ indirection table。CQ 可以被多个 QP 共享,因此对象关系是带共享边的有向图,不是单一所有权树。

structib_pd{/* PD 所属设备 */structib_device*device;/* 用户 PD 对应的 uverbs 对象;内核 PD 为空 */structib_uobject*uobject;/* MR、QP、AH 等子资源使用计数 */atomic_tusecnt;/* PD 类型的资源跟踪项 */structrdma_restrack_entryres;};structib_cq{/* CQ 所属设备 */structib_device*device;/* 用户 CQ 的 uverbs 包装;内核 CQ 为空 */structib_ucq_object*uobject;/* 引用该 CQ 的工作队列数量 */atomic_tusecnt;/* CQ 类型的资源跟踪项 */structrdma_restrack_entryres;};structib_qp{/* QP 所属设备和保护域 */structib_device*device;structib_pd*pd;/* 发送和接收完成队列,可以指向同一 CQ */structib_cq*send_cq;structib_cq*recv_cq;/* 可选共享接收队列 */structib_srq*srq;/* QP 自身被 open、multicast 或 flow 使用的计数 */atomic_tusecnt;/* 用户 QP 的 uverbs 包装;内核 QP 为空 */structib_uqp_object*uobject;/* 驱动创建后给出的 QP number */u32 qp_num;/* QP 类型的资源跟踪项 */structrdma_restrack_entryres;};structib_mr{/* MR 所属设备和保护域 */structib_device*device;structib_pd*pd;/* WQE 使用的本地和远端 key */u32 lkey;u32 rkey;/* RDMA 地址范围 */u64 iova;u64 length;/* 用户 MR 对应的 uverbs 对象 */union{structib_uobject*uobject;structlist_headqp_entry;};/* MR 类型的资源跟踪项 */structrdma_restrack_entryres;};

4.3 用户对象与驱动对象

用户句柄指向通用对象,驱动私有对象内嵌通用对象;句柄事务细节见uverbs 接口(五):

  • ib_uobject保存用户 handle、uverbs file、context、通用对象指针及句柄访问引用。
  • ib_pd、ib_cq、ib_qp、ib_mr保存跨驱动统一的语义和对象关系。
  • mlx5_ib_pd、mlx5_ib_cq、mlx5_ib_qp、mlx5_ib_mr保存 mlx5 私有状态,并以内嵌的通用对象作为 Core 入口。
  • PDN、CQN、QPN 和 MKey 等设备标识由 mlx5_ib 回调建立,不能与用户 handle 或内核指针互换。

下图汇总对象之间的关系。实线表示 Core 保存的直接引用,虚线表示包装或索引关系;usecnt沿子对象到父对象增加,销毁时按相反方向解除。

4.4 对象依赖关系

Core 在对象成功创建后建立依赖计数。普通 QP 调用ib_qp_usecnt_inc()时分别增加 PD、send CQ、recv CQ、SRQ 和 RWQ indirection table 的usecnt;成功销毁后由ib_qp_usecnt_dec()减少引用计数。send CQ 与 recv CQ 相同时会分别增加两次,也会在销毁时分别减少两次。

MR 注册成功后增加所属 PD 的usecnt,注销成功后减少。PD 销毁入口要求所有子资源已经同步销毁;用户对象路径中的uverbs_free_pd()检查pd->usecnt,非零时返回-EBUSY。CQ 销毁由ib_destroy_cq_user()检查cq->usecnt。QP 的usecnt则保护 open QP、multicast attachment、flow 等直接依赖,父对象依赖由单独的ib_qp_usecnt_inc()/ib_qp_usecnt_dec()维护。

这些计数只表达对象之间的使用关系,不代替设备注册引用或 restrack 引用:前者保护设备的注册可用状态,后者保证资源观察期间的元数据访问。

5. 总结

RDMA Kernel Core 以ib_device作为设备与资源的根,以ib_device_ops作为通用语义和硬件实现之间的分发边界。mlx5_ib 分配包含ib_device的驱动对象,根据硬件能力分批安装操作表并发起注册;Core 负责建立设备名称、端口、cache、Linux 设备模型和可见状态。注销沿相反方向撤销这些状态,先阻止新的使用,再等待已有访问退出。

内核消费者通过 Kernel Verbs 使用 RDMA Core。PD、CQ、QP 等资源管理接口进入verbs.c完成通用对象分配、成员初始化和依赖建立,再通过ib_device_ops调用 mlx5_ib;ib_post_send()、ib_post_recv()和ib_poll_cq()等基础收发接口则从对象定位设备并直接分发驱动回调。PD、CQ、MR 和 QP 构成带共享关系的依赖图,用户对象由ib_ucontext和ib_uobject包装,mlx5_ib 私有对象以内嵌通用对象接入 Core。用户 handle、内核对象指针与 QPN、CQN、MKey 等硬件标识分别属于不同管理层次。

6. 参考资料

6.1 源码地图

功能源码位置重点内容
设备分配、注册与注销linux/drivers/infiniband/core/device.c_ib_alloc_device()、ib_register_device()、enable_device_and_get()、disable_device()、ib_unregister_device()、ib_set_device_ops()
设备缓存与 sysfs 属性linux/drivers/infiniband/core/cache.c、linux/drivers/infiniband/core/sysfs.cGID/P_Key 缓存初始化与清理、设备统计属性、端口与 GID 属性发布
通用 Verbs 对象linux/drivers/infiniband/core/verbs.cPD、AH、SRQ、QP、CQ、MR 的创建、依赖计数、销毁和回滚
uverbs 对象生命周期linux/drivers/infiniband/core/rdma_core.cib_uobject的分配、查找、commit、abort、destroy 与文件清理
用户上下文入口linux/drivers/infiniband/core/uverbs_cmd.cib_alloc_ucontext()、ib_init_ucontext()与 GET_CONTEXT
标准对象 handlerlinux/drivers/infiniband/core/uverbs_std_types.c、uverbs_std_types_qp.c、uverbs_std_types_cq.c用户对象创建后的 Core 调用、usecnt检查和销毁入口
资源跟踪linux/drivers/infiniband/core/restrack.c、linux/include/rdma/restrack.hrestrack 类型、XArray、kref、添加、查找、同步与删除
通用对象与操作表定义linux/include/rdma/ib_verbs.hib_device、ib_device_ops、ib_ucontext、PD、MR、CQ、QP 及对象大小宏
mlx5_ib 操作表与注册入口linux/drivers/infiniband/hw/mlx5/main.cmlx5_ib_dev_ops、能力操作表拼接、mlx5_ib_stage_ib_reg_init()与清理 stage

6.2 相关文档

  • 《RDMA-Verbs-软件架构(二)》:RDMA Core 在整体软件栈中的位置及四类路径。
  • 《RDMA-Verbs-API 和数据结构(三)》:公开 Verbs API、用户态对象及 libibverbs 分发。
  • 《RDMA-Verbs-MLX5 用户态库(四)》:mlx5 provider 私有对象、队列内存和用户态数据路径。
  • 《RDMA-Verbs-内核 uverbs 接口(五)》:Client 接入、ABI、命令属性、用户句柄和文件关闭处理。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询