Published on

rdma-core用户态内核态兼容性分析

Authors
  • avatar
    Name
    Mao
    Twitter

rdma-core & umdk 用户态内核态兼容性分析

代码基线linux-rdma/rdma-core(用户态)、torvalds/linux(内核态)
分析日期:2026-06-28


1. 概述

本文档基于 rdma-core 和 Linux 内核的实际源码,从代码级别分析 rdma-core 用户态库与内核驱动之间的兼容性保障机制,为 openEuler UMDK 的兼容性方案设计提供参考。

核心结论:rdma-core 通过 五层防御体系 实现用户态库与内核驱动的兼容性保障:编译时 UAPI 同步、运行时 ABI 协商、传输层双路径降级、特性级 graceful degradation、二进制符号版本控制。


2. 关键概念区分

Provider ABI 与 ELF 符号版本

rdma-core 中存在两个独立的版本维度,需严格区分:

维度名称当前值(mlx5)作用
uverbs Provider ABIuverbs_abi_ver1内核 ib_device_ops 中声明,用户态 Provider 通过 per-device abi_version sysfs 匹配,决定设备能否初始化
ELF 符号版本MLX5_1.xMLX5_1.27libmlx5.so 对外 mlx5dv_* API 的二进制兼容粒度,与内核无关,供应用 dlvsym(..., "MLX5_1.27") 查询

前者控制设备能否使用,后者控制API 能否调用,二者互不替代。


3. rdma-core 软件分层架构

┌─────────────────────────────────────────────────────────┐
Application (UCX / DPDK / NCCL / MPI / 自研应用)├─────────────────────────────────────────────────────────┤
│  libibverbs.so  — 通用 verbs 框架 + 设备枚举/命令分发     │
│  libmlx5.so 等  — Provider(硬件相关 verbs 实现)          │
├─────────────────────────────────────────────────────────┤
/dev/infiniband/uverbsN  — ib_uverbs 字符设备(控制面)   │
├─────────────────────────────────────────────────────────┤
│  mlx5_ib.ko + mlx5_core.ko  — 内核 RDMA 驱动             │
│  drivers/infiniband/core/   — ib_core + uverbs 核心       │
├─────────────────────────────────────────────────────────┤
ConnectX / HNS RoCE 等硬件                                │
└─────────────────────────────────────────────────────────┘

控制面路径:用户态通过 ioctl(RDMA_VERBS_IOCTL) 或 legacy write() 向 uverbs 字符设备下发命令;内核 uverbs_cmd.c / uverbs_ioctl.c 解析后调用具体 ib_device 驱动。

数据面路径:完成队列轮询、Doorbell 写、WQE 投递等由用户态直接访问硬件 MMIO/UAR,不经内核 per-packet 路径。


4. 五层兼容性防御体系

┌──────────────────────────────────────────────────────────────────┐
Layer 1  编译时:UAPI 头文件同步 + ABI 结构体代码生成              │
├──────────────────────────────────────────────────────────────────┤
Layer 2  运行时:全局/Provider ABI 版本检查(拒绝或跳过)          │
├──────────────────────────────────────────────────────────────────┤
Layer 3  运行时:ioctl 优先 + write legacy 自动降级               │
├──────────────────────────────────────────────────────────────────┤
Layer 4  运行时:comp_mask / 多长度重试 / 零值语义(特性级降级)   │
├──────────────────────────────────────────────────────────────────┤
Layer 5  二进制:ELF 符号版本 + compat-1_0 包装(API 级兼容)     │
└──────────────────────────────────────────────────────────────────┘

4.1 Layer 1:UAPI 头文件同步(编译时契约)

脚本rdma-core/kernel-headers/update

从指定内核 git commit 的 include/uapi/rdma/ 精确复制头文件到 rdma-core/kernel-headers/rdma/,保证用户态编译所用 UAPI 与目标内核一致。

构建期二次处理

  • buildlib/make_abi_structs.py 生成 build/include/kernel-abi/*
  • libibverbs/kern-abi.hDECLARE_CMD / DECLARE_CMDX 宏包装 write 命令布局
  • 各 Provider 的 *-abi.h(如 mlx5-abi.h)定义 UHW 扩展载荷

风险:若未运行 update 即编译,可能出现结构体布局不一致——ABI 版本检查无法覆盖(版本号相同但字段布局已变)。

4.2 Layer 2:运行时 ABI 版本协商

两级检查

(1)全局检查libibverbs/init.c:check_abi_version()

#define IB_USER_VERBS_MIN_ABI_VERSION  3
#define IB_USER_VERBS_MAX_ABI_VERSION  6

abi_ver = sysfs_read("/sys/class/infiniband_verbs/abi_version");
if (abi_ver < MIN || abi_ver > MAX)
    return ENOSYS;  // 所有 RDMA 设备不可用

两层 abi_version 语义

sysfs 路径值来源含义
/sys/class/infiniband_verbs/abi_versionIB_USER_VERBS_ABI_VERSION = 6全局 uverbs 框架 ABI
/sys/class/infiniband_verbs/uverbsN/abi_versionib_dev->ops.uverbs_abi_verper-device 驱动 ABI

(2)Provider 检查init.c:match_device()

if (sysfs_dev->abi_ver < ops->match_min_abi_version ||
    sysfs_dev->abi_ver > ops->match_max_abi_version)
    return false;  // 跳过该 Provider

mlx5 Provider 的 match_min/max_abi_version 均为 1MLX5_UVERBS_MIN/MAX_ABI_VERSION)。Provider ABI 与全局 ABI 是两个独立维度。

Provider ABI 策略差异:mlx5 严格(1–1),hns 宽松(0–INT_MAX)。

4.3 Layer 3:ioctl / write 双路径降级

现代路径ioctl(cmd_fd, RDMA_VERBS_IOCTL, &hdr)
Legacy 路径write(cmd_fd, req, req_size)

自动降级逻辑libibverbs/cmd_fallback.c):

*ret = execute_ioctl(ctx, cmdb);
if (*ret == ENOTTY) {
    /* ioctl 框架完全不存在 → 永久降级 write */
    bitmap_fill(priv->unsupported_ioctls, VERBS_OPS_NUM);
    return _check_legacy(cmdb, ret);
}
if (*ret == EPROTONOSUPPORT) {
    /* 该 method 不支持 → per-command 降级 */
    bitmap_set_bit(priv->unsupported_ioctls, cmd_bit);
    return _check_legacy(cmdb, ret);
}

内核侧 Legacy Workarounduverbs_main.c):

if (hdr->command == IB_USER_VERBS_CMD_DESTROY_CQ && count == 16) {
    hdr->in_words = 6;  // 兼容 rdma-core v18/v19 的 16 字节命令
    return 0;
}

这一机制使得新用户态库可在旧内核(不支持 ioctl 时)自动降级到 write 路径,是生产环境滚动升级的关键保障。

4.4 Layer 4:特性级兼容

(1)Provider 多长度重试providers/mlx5/mlx5.c

if (!ibv_cmd_get_context(verbs_ctx, &req->ibv_cmd,
                         offsetof(struct mlx5_alloc_ucontext, lib_caps),
                         fds, &resp->ibv_resp, resp_len))
    return 0;

return ibv_cmd_get_context(verbs_ctx, &req->ibv_cmd,
                           offsetof(struct mlx5_alloc_ucontext, max_cqe_version),
                           fds, &resp->ibv_resp, resp_len);

先用完整请求长度,失败则缩短到旧内核已知字段边界重试。响应中零值表示特性不支持。

(2)comp_mask 扩展字段uverbs_cmd.c

if (cmd.comp_mask & ~IB_UVERBS_CREATE_QP_SUP_COMP_MASK)
    return -EINVAL;

用户态设置 comp_mask 表明需要哪些扩展;内核仅处理已知位,未知位直接拒绝。这是前向兼容和特性探测的重要手段。

4.5 Layer 5:二进制 API 兼容

组件机制最新版本
libmlx5MLX5_1.0MLX5_1.27 递增MLX5_1.27
libibverbscompat-1_0.c + IBVERBS_1.0 符号IBVERBS_1.14

应用可通过 dlvsym(handle, "mlx5dv_devx_obj_export", "MLX5_1.27") 探测特定版本。


5. 用户态代码级分析

5.1 全局 ABI 检查(libibverbs/init.c

static int check_abi_version(void)
{
    if (ibv_read_sysfs_file(ibv_get_sysfs_path(),
                            "class/infiniband_verbs/abi_version",
                            value, sizeof(value)) < 0)
        return ENOSYS;

    abi_ver = strtol(value, NULL, 10);
    if (abi_ver < IB_USER_VERBS_MIN_ABI_VERSION ||
        abi_ver > IB_USER_VERBS_MAX_ABI_VERSION) {
        fprintf(stderr, "Fatal: kernel ABI version %d doesn't match"
                " library version %d.\n", abi_ver,
                IB_USER_VERBS_MAX_ABI_VERSION);
        return ENOSYS;
    }
    return 0;
}

触发时机:ibverbs_get_device_list() 在发现设备后、加载 Provider 前调用。

5.2 Provider 匹配(providers/mlx5/mlx5.c

const struct verbs_match_ent mlx5_hca_table[] = {
    VERBS_DRIVER_ID(RDMA_DRIVER_MLX5),
    HCA(MELLANOX, 0x1011),  // Connect-IB
    // ... 多款 ConnectX / BlueField PCI ID
    {}
};

static const struct verbs_device_ops mlx5_dev_ops = {
    .name = "mlx5",
    .match_min_abi_version = MLX5_UVERBS_MIN_ABI_VERSION,  // = 1
    .match_max_abi_version = MLX5_UVERBS_MAX_ABI_VERSION,  // = 1
    .match_table = mlx5_hca_table,
    .alloc_device = mlx5_device_alloc,
    .alloc_context = mlx5_alloc_context,
};

匹配逻辑:sysfs_dev->abi_ver 必须在 [1, 1] 范围内,否则跳过。

5.3 ELF 符号版本(providers/mlx5/libmlx5.map

MLX5_1.27 {
    global:
        _mlx5dv_get_export_sizes;
        mlx5dv_devx_obj_export;
        mlx5dv_devx_obj_import;
        ...
} MLX5_1.26;

6. 内核态代码级分析

6.1 ib_uverbs 字符设备(drivers/infiniband/core/uverbs_main.c

static ssize_t abi_version_show(struct device *device,
                                struct device_attribute *attr, char *buf)
{
    if (ib_dev)
        ret = sysfs_emit(buf, "%u\n", ib_dev->ops.uverbs_abi_ver);
}

static CLASS_ATTR_STRING(abi_version, S_IRUGO,
                         __stringify(IB_USER_VERBS_ABI_VERSION));  // = 6

6.2 mlx5_ib 驱动(drivers/infiniband/hw/mlx5/main.c

.uverbs_abi_ver = MLX5_IB_UVERBS_ABI_VERSION,  // 值为 1

内核通过 ib_register_device() 注册设备,uverbs 核心为每个 ib_device 创建 /dev/infiniband/uverbsN

6.3 comp_mask 校验(drivers/infiniband/core/uverbs_cmd.c

if (cmd.comp_mask & ~IB_UVERBS_CREATE_QP_SUP_COMP_MASK)
    return -EINVAL;

7. 用户态与内核态交互全景

App: ibv_get_device_list()
  → libibverbs: check_abi_version() [全局 ABI 3..6]
  → libibverbs: match_device() [Provider ABI 匹配]
Provider: mlx5_alloc_context()
ibv_cmd_get_context() [多长度重试]
ioctl(RDMA_VERBS_IOCTL)write() [双路径]
        → kernel: ib_uverbs_ioctl() / ib_uverbs_write()
mlx5_ib_get_context()
            → 返回 context + UAR/能力信息

关键共享头文件

头文件用户态路径内核路径
ib_user_verbs.hrdma-core/kernel-headers/rdma/kernel/include/uapi/rdma/
ib_user_ioctl_cmds.h同上同上
mlx5-abi.h同上同上

8. 能力评估与局限性

8.1 已具备的能力

场景行为实现机制
新用户态 + 过旧内核拒绝初始化check_abi_version()
新用户态 + 旧 Provider ABI跳过 Providermatch_min/max_abi_version
新用户态 + 旧内核(无 ioctl)降级 write_execute_ioctl_fallback()
新用户态 + 旧内核(部分命令)per-command 降级unsupported_ioctls bitmap
新用户态 + 旧内核(mlx5 特性)多长度重试mlx5_cmd_get_context()
新应用 + 旧 libmlx5dlvsym 返回 NULLlibmlx5.map 符号版本
旧应用 + 新 libibverbs包装调用compat-1_0.c
新用户态 + 新内核(扩展字段)探测/拒绝comp_mask

8.2 局限性

  1. 头文件同步依赖人工update 脚本非自动触发,升级后若未同步存在结构体不一致风险
  2. 全局 ABI 检查粒度粗:仅检查 [3,6] 整数版本,无法感知同版本内的字段变更
  3. Provider ABI 策略不统一:mlx5 严格(1–1),hns 宽松(0–INT_MAX)
  4. 无法跨 major ABI 自动适配:超出 [3,6] 直接 Fatal
  5. 容器场景:需保证容器内 rdma-core 版本与宿主机内核驱动匹配

9. 与 openEuler UMDK 的映射

UMDK Layerrdma-core 对应实现
Layer 1 UAPI 同步kernel-headers/update
Layer 2 运行时 ABI 协商check_abi_version + 多长度重试
Layer 3 传输层降级ioctl/write 双路径 + _execute_ioctl_fallback
Layer 4 comp_maskib_user_verbs.h + uverbs_cmd.c
Layer 5 符号版本libmlx5.map(最高 1.27

对 openEuler 的实践建议

  1. 发布 rdma-core 包时,明确与 OLK 内核 IB_USER_VERBS_ABI_VERSION 及 Provider uverbs_abi_ver 的配套关系
  2. 内核 UAPI 变更后必须运行 kernel-headers/update 并走完整 CI
  3. UMDK 设计可复用 rdma-core 五层模型,但需独立实现 UDMA_ABI_VERSIONlibudma.map 等 UMDK 特有机制
  4. 建议 UMDK 引入 Layer 3(传输层降级)设计:在 ioctl 不可用时自动降级到 write 路径

附录:关键源文件索引

用户态(rdma-core)

文件功能
libibverbs/init.c设备枚举、ABI 检查、Provider 匹配
libibverbs/kern-abi.hABI 范围(MIN=3, MAX=6)
libibverbs/cmd_fallback.cioctl → write 降级(Layer 3)
libibverbs/cmd_ioctl.cioctl 命令执行
libibverbs/compat-1_0.cIBVERBS_1.0 二进制兼容
kernel-headers/updateUAPI 头文件同步脚本
providers/mlx5/mlx5.cmlx5 Provider、多长度重试
providers/mlx5/mlx5-abi.hProvider ABI = 1
providers/mlx5/libmlx5.map符号版本最高 1.27

内核态(linux/drivers/infiniband)

文件功能
core/uverbs_main.c字符设备、write 路径、sysfs abi_version
core/uverbs_ioctl.cioctl 路径
core/uverbs_cmd.c命令分发、comp_mask 校验
hw/mlx5/main.cmlx5_ib 驱动、uverbs_abi_ver = 1
include/uapi/rdma/ib_user_verbs.h内核 UAPI 权威定义