DOCA GPUNetIO 开源版(GDAKI)实现分析

分析对象:./gpunetio/(NVIDIA DOCA GPUNetIO Open Source,v5.0.0)

定位:以 DOCA 风格 API 实现的 GPUDirect Async Kernel-Initiated(GDAKI) 通信库------让 CUDA kernel 直接发起 RDMA 通信 (InfiniBand / RoCE),无需 CPU 介入数据面。

开源版范围:仅支持 RC(Reliable Connection)单边语义 的 GPU 数据面(put/signal/counter 等),双边 send/recv、Ethernet、DMA 的 GPU 数据面不开源;CPU 控制面与 CUDA 头文件和完整版一致(README.md 对比表)。


1. 项目结构与总体架构

1.1 目录布局

复制代码
gpunetio/
├── Makefile                        # 顶层构建(产物 lib/libdoca_gpunetio_host.so)
├── include/
│   ├── host/                       # CPU 控制面公开头(doca_gpunetio.h、doca_verbs.h、mlx5_prm.h 等)
│   ├── common/                     # CPU/GPU 共享定义 ★
│   │   ├── doca_gpunetio_verbs_def.h    # 版本、枚举(nic_handler/cq_type/exec_scope...)、工具宏
│   │   └── doca_gpunetio_verbs_dev.h    # ★ GPU 侧 QP/CQ/WQE 结构体(导出到显存的镜像)
│   └── device/                     # ★ GPU 数据面 CUDA 头(.cuh,编译进用户 kernel)
│       ├── doca_gpunetio_dev_verbs_common.cuh    # fence/atomic/mmio 原语
│       ├── doca_gpunetio_dev_verbs_qp.cuh        # WQE 准备 + doorbell 提交
│       ├── doca_gpunetio_dev_verbs_cq.cuh        # CQE 轮询
│       ├── doca_gpunetio_dev_verbs_onesided.cuh  # 高层 put/put_signal/put_inline
│       └── doca_gpunetio_dev_verbs_counter.cuh   # put_counter / wait
├── src/                            # CPU 控制面实现(开源 C++,替代闭源 libdoca_gpunetio)
│   ├── doca_gpunetio.cpp           # GPU handler、显存管理、QP/CQ 导出、CPU proxy
│   ├── doca_verbs_qp.cpp           # QP 创建与 RC 状态机(全走 DevX,不用 ibv_create_qp)
│   ├── doca_verbs_cq.cpp           # CQ 创建(DevX CREATE_CQ,支持 collapsed CQ)
│   ├── doca_verbs_umem.cpp         # GPU 显存经 dmabuf/peermem 注册给网卡
│   ├── doca_verbs_uar.cpp          # 门铃页(UAR)分配
│   ├── doca_verbs_dev.cpp          # 设备封装(ibv_pd → doca_dev)
│   ├── doca_gpunetio_gdrcopy.cpp   # GDRCopy 封装(GPU 显存的 CPU 映射)
│   ├── doca_gpunetio_high_level.cpp# 一键高层 API(*_hl)
│   ├── doca_gpunetio_signal_map.cpp# 信号槽管理
│   └── *_sdk_wrapper.cpp           # dlopen 闭源 DOCA SDK 的转发层(DOCA_SDK_LIB_PATH 控制)
├── examples/                       # 3 个 perftest 风格示例 + verbs_common 公共代码
└── scripts/compile-time-test/      # 配置探测(dmabuf umem、NC_DEDICATED UAR、CUDA dmabuf)

1.2 核心思想:两阶段模型

复制代码
┌─ CPU 控制面(一次性,src/*.cpp)─────────────────────────────────┐
│ ibv_open_device/alloc_pd/reg_mr                               │
│ doca_gpu_create → doca_verbs_dev_open → uar/umem/cq/qp 创建   │
│ (DevX 命令:SQ/CQ/DBR 全部放在 GPU 显存中!)                    │
│ doca_verbs_qp_modify:RC 状态机 RST→INIT→RTR→RTS               │
│ doca_gpu_verbs_export_qp + get_qp_dev:                       │
│    把 SQ/CQ/DBR/门铃的 GPU 地址打包成 doca_gpu_dev_verbs_qp      │
│    memcpy 到显存,交给 CUDA kernel                              │
└───────────────────────────────────────────────────────────────┘
                              │ doca_gpu_dev_verbs_qp*(显存指针)
┌─ GPU 数据面(每次通信,include/device/*.cuh)────────────────────┐
│ CUDA kernel 内:                                              │
│   doca_gpu_dev_verbs_put()  → 在显存 SQ 中写 WQE(RDMA_WRITE)  │
│   doca_gpu_dev_verbs_submit()→ 直接 st.mmio 敲网卡门铃          │
│   doca_gpu_dev_verbs_poll_cq_at() → 轮显存中的 CQE(owner bit) │
│ ------ 全程无系统调用、无 CPU 参与                                   │
└───────────────────────────────────────────────────────────────┘

与传统 verbs(analysis-02/03)的本质差异:SQ/CQ 不再放主机内存由 CPU 的 libibverbs 写,而是放 GPU 显存由 CUDA kernel 写;门铃寄存器映射进 GPU 地址空间由 SM 直接写。这就是"Kernel-Initiated"。


2. CPU 控制面实现(src/)

2.1 双模设计:SDK dlopen 优先 + 开源 DevX 兜底

每个公开 API(doca_gpu_create、doca_verbs_qp_create...)先调用 *_sdk_wrapper_*(doca_*_sdk_wrapper.cpp)------它按环境变量 DOCA_SDK_LIB_PATH 用 dlopen("libdoca_common.so[.2]" → "libdoca_verbs.so" → "libdoca_gpunetio.so") 并 dlsym 转发到闭源 SDK;符号不存在/调用返回 NOT_SUPPORTED 时回退到开源实现 (doca_gpunetio_sdk_wrapper.cpp:99 初始化,std::call_once)。对象上用 type = DOCA_*_LIB_TYPE_SDK/OPEN 记录走哪条路。同理 doca_gpunetio_cuda_wrapper.cpp(dlopen libcuda)与 doca_verbs_{ibv,mlx5dv}_wrapper.cpp(dlopen libibverbs/libmlx5,可选)。

2.2 GPU handler:doca_gpu_create()(doca_gpunetio.cpp:124)

  1. cudaDeviceGetByPCIBusId 由 PCIe BDF 定位 CUDA 设备(沿用调用方 context);
  2. 能力探测:CU_DEVICE_ATTRIBUTE_DMA_BUF_SUPPORTED、GPU_DIRECT_RDMA_WRITES_ORDERING(决定是否需 memory-consistency 算法)、compute capability ≥ 10(async store release)、GDRCopy 可用性;
  3. 建 mtable 跟踪所有 GPU 内存分配。

2.3 显存分配:doca_gpu_mem_alloc()(doca_gpunetio.cpp:268)三种类型

类型 实现 用途
GPU cudaMalloc + 对齐 + cuPointerSetAttribute(CU_POINTER_ATTRIBUTE_SYNC_MEMOPS)(GPUDirect RDMA 必需) SQ/CQ/WQ 缓冲
GPU_CPU(_DATA_DIRECT) 同上 + GDRCopy gdr_pin_buffer+gdr_map 得 CPU 可写映射(DATA_DIRECT 用 GDR_PIN_FLAG_FORCE_PCIE 绕过 GPU L2) dev QP 结构、signal 槽(CPU 也要访问)
CPU_GPU calloc + `cudaHostRegister(Portable Mapped)+cudaHostGetDevicePointer`

2.4 显存注册给网卡:UMEM(doca_verbs_umem.cpp:77 create)

两条路径汇聚到 mlx5dv_devx_umem_reg_ex(:107):

  1. dma-buf 优先 :doca_gpu_get_dmabuf_fd(gpunetio.cpp:603→639)用 cuMemGetHandleForAddressRange(..., CU_MEM_RANGE_HANDLE_TYPE_DMA_BUF_FD) 拿 fd → umem_in.comp_mask = MLX5DV_UMEM_MASK_DMABUF, dmabuf_fd = fd;
  2. peermem 兜底:fd 无效时直接注册 GPU VA(依赖 nvidia-peermem 内核模块)。

产物 umem_id 在 QP/CQ 创建时以 wq_umem_id / cq_umem_id / dbr_umem_id 引用------固件据此把 SQ/CQ/DBR 安置在 GPU 显存,HCA 直接从显存 DMA 取 WQE/CQE。

2.5 门铃页:UAR(doca_verbs_uar.cpp:101)

mlx5dv_devx_alloc_uar 分配 UAR(BF 或 NC/NC_DEDICATED 类型),得 page_id(建 QP 用)、reg_addr(门铃寄存器 CPU VA)、base_addr;另算 dbr_less_addr = base + 0x600(NO_DBR 模式专用门铃偏移,uar.cpp:98/119)。

门铃如何交给 GPU (doca_gpunetio.cpp:682 doca_gpu_verbs_export_uar,注意不是 GDRCopy):

c 复制代码
cudaHostRegister(sq_db, 8, cudaHostRegisterPortable | cudaHostRegisterMapped
                           | cudaHostRegisterIoMemory);   // :698-700 ------ NIC BAR 页映射为设备 MMIO
cudaHostGetDevicePointer(&ptr, sq_db, 0);                  // :711 ------ 得到 GPU 可访问地址

之后 GPU kernel 对该地址的 st.mmio.sys 就是直接敲网卡门铃。registered_uar_refcount + 互斥锁管理多 QP 共享。doca_gpu_verbs_can_gpu_register_uar(:654)预先试注册探测可行性,作为 nic_handler AUTO 决策依据。

2.6 QP/CQ 创建:全部走 DevX(绕过 ibv_create_qp)

CQ (doca_verbs_cq.cpp:165 create → :117 create_cq_obj):CQE 缓冲用外部 umem(可 GPU 显存)或内部 memalign+umem;DBR 独立 umem;发 MLX5_CMD_OP_CREATE_CQ,关键字段:

  • cqe_sz=64B、log_cq_size、uar_page、cq_umem_id/offset(CQE 在显存 )、dbr_umem_id/dbr_addr;
  • cc=cq_collapsed(collapsed CQ :多个完成折叠写同一个 CQE,GPU 轮询时只需盯一个槽位)、oi=overrun;
  • m_ci_dbr = &db[MLX5_CQ_SET_CI]、m_arm_dbr = &db[MLX5_CQ_ARM_DB]、CQ 门铃寄存器 uar_db_reg = base + 0x20(cq.cpp:281,仅 CPU 用)。

QP (doca_verbs_qp.cpp:1424 create → :722 create_qp_obj):计算 WQ 尺寸(SQ WQE = ctrl + raddr + sge×data,按 64B WQEBB 对齐)→ 外部 UAR/umem 取 id 与地址 → 发 MLX5_CMD_OP_CREATE_QP:

  • pd(mlx5dv_init_obj 取 pdn)、uar_page、cqn_snd/cqn_rcv;
  • wq_umem_valid=1, wq_umem_id, wq_umem_offset (:813-815)------SQ/RQ 就在 GPU 显存;
  • dbr_umem_valid=1, dbr_umem_id, dbr_addr(:807-809)------DBR 也可在显存(CPU proxy 模式时故意放主机内存);
  • send_dbr_mode(:806):DBR_VALID(常规)或 NO_DBR_EXT(无 DBR 模式)。

RC 连接状态机 (doca_verbs_qp_modify :3646 分发):

迁移 函数 固件命令 写入内容
RST→INIT rst2init (:835) RST2INIT_QP port、pkey、rwe/rre/rae、lag affinity
INIT→RTR init2rtr (:956) INIT2RTR_QP next_rcv_psn、remote_qpn、MTU;IB 路径 rlid/sl;RoCE:resolve_remote_mac()(:508,临时建 AH 读 dv_ah.av->rmac)+ rgid + RoCEv2 随机 udp_sport/dscp
RTR→RTS rtr2rts (:1131) RTR2RTS_QP next_send_psn、ack_timeout、retry/rnr_retry、log_sra_max

2.7 导出到 GPU:doca_gpu_verbs_export_qp() + get_qp_dev()

export_qp(gpunetio.cpp:881)把主机侧对象翻译为 GPU 侧结构 doca_gpu_dev_verbs_qp(include/common/doca_gpunetio_verbs_dev.h:173):

GPU 侧字段 来源
sq_wqe_daddr doca_verbs_qp_get_wq(qp.cpp:3866)------显存中 SQ 基址
sq_dbrec doca_verbs_qp_get_dbr_addr + MLX5_SND_DBR(=1)(:1016)
sq_db doca_verbs_qp_get_uar_addr(门铃寄存器)→ 非 CPU_PROXY 时 doca_gpu_verbs_export_uar 映射为 GPU 地址 (:1066-1072);CPU_PROXY 时指向共享 cpu_db
sq_num_shift8_be[_1..5ds] QPN<<8 | ds 数的预计算大端值(:999-1011),省 kernel 内计算
sq_wqe_num/mask、sq_rsvd_index/sq_ready_index/sq_wqe_pi、sq_lock 槽位管理状态(见 §3.2)
cq_sq(内嵌 dev CQ) doca_verbs_cq_get_wq(cqe_daddr/num/size)、get_dbr_db_addr(dbrec)、get_cq_num
nic_handler、mem_type §2.8 决策

随后 doca_gpu_verbs_get_qp_dev()(:1171):doca_gpu_mem_alloc(sizeof(dev_qp), GPU 或 GPU_CPU) + cudaMemcpy HtoD------CUDA kernel 收到的 doca_gpu_dev_verbs_qp* 就是这个显存指针 。多 QP 可批量导出(export_multi_qps_dev :1252,一次分配一次拷贝)。

2.8 nic_handler 四种模式(GPU 如何"通知网卡")

枚举于 doca_gpunetio_verbs_def.h:341;选择逻辑在 create_qp(high_level.cpp:760-776)与 export_qp(gpunetio.cpp:1066-1114):

模式 机制 前提
GPU_SM_DB GPU kernel 直接写 DBR + st.mmio 敲门铃寄存器 cudaHostRegister(IoMemory) 门铃页成功(性能最高)
GPU_SM_NO_DBR QP 建为 NO_DBR_EXT,只敲门铃不写 DBR(少一次写);或 NO_DBR_SW_EMULATED:CPU 经 GDRCopy 别名读 sq_wqe_pi 代写 DBR+门铃 网卡支持 NO_DBR / GDRCopy
CPU_PROXY GPU 只把 producer index 写进共享 cpu_db(CPU_GPU 内存),CPU 线程代敲门铃 门铃无法映射给 GPU 时的兜底
CPU_PROXY_FREE_FLOW 同代理,但 cpu_db 有 sq_wqe_num 个槽,GPU 按槽写 pi+1,CPU 批量合并(阈值 64) 高并发代理

CPU proxy 工作方式 (doca_gpu_verbs_cpu_proxy_progress gpunetio.cpp:1481):

  • full_assisted(:1358):读 cpu_db 得新 PI → 构造 8 字节 ctrl 门铃(new_pi<<8 | qpn<<8)→ 先敲门铃(供 NIC 恢复路径)→ 写 sq_dbrec → fence → 再敲门铃(:1381-1399);
  • dbr_assisted(:1406,SW-emulated NO_DBR):CPU 从 GDRCopy 映射读 GPU 侧 PI,代写 DBR+门铃;
  • CQ progress(:1427,COLLAPSED_HOST):CPU 代轮主机内存中的 CQE,把 cqe_ci 经 GDRCopy 回写给 GPU。
  • 可用户线程自调,或 doca_gpu_verbs_create_service(:1533)起后台 pthread 轮询所有受管 QP。

2.9 高层 API 与版本兼容

  • doca_gpu_verbs_create_qp_hl(high_level.cpp:1122):一键编排 create_cq→create_uar→create_qp→export,示例均用它;
  • 版本编码 5.0.0(def.h:49);doca_gpu_verbs_check_device_code_compatibility(gpunetio.cpp:1801)要求 device 代码版本 ∈ 2.0.0, 库版本,check_host_code_compatibility(:1808)要求 host ≥ 5.0.0(v5 有 API breaking change)。

3. GPU 数据面实现(include/device/*.cuh)

这是 GDAKI 的精华:CUDA kernel 内完成 verbs 数据面的全部工作 。模板参数贯穿始终:resource_sharing_mode(EXCLUSIVE/CTA/GPU,决定原子操作范围)、exec_scope(THREAD/WARP)、nic_handler、cq_type。

3.1 WQE 准备(doca_gpunetio_dev_verbs_qp.cuh)

doca_gpu_dev_verbs_wqe(verbs_dev.h:103)是 64B 联合体,覆盖 mlx5 WQE 各种布局(与 analysis-02 中 mlx5_wqe_*_seg 一致:ctrl/raddr/data/atomic/wait 段)。

各 wqe_prepare_* 函数(qp.cuh:695-1211)在 kernel 寄存器里拼好段,再用内联 PTX 写入显存 SQ:

c 复制代码
// qp.cuh:45 ------ streaming 写,绕过 L2,直写显存(网卡马上能看到)
asm volatile("st.weak.cs.v2.b64 [%0], {%1, %2};" :: "l"(ptr), "l"(val[0]), "l"(val[1]));
函数 生成 WQE
wqe_prepare_write(:709/746,1-2 个 SGE) ctrl(opcode=RDMA_WRITE, qpn_ds=预计算值, fm_ce_se=CQ_UPDATE) + raddr 段 + data 段
wqe_prepare_write_inl(:875) inline 写(≤28B 直接嵌进 WQE)
wqe_prepare_read(:895) RDMA_READ
wqe_prepare_atomic(_ext)(:985/1034) ATOMIC_FA/CS 及 masked 扩展
wqe_prepare_nop/wait/dump(:695/1155/1188) NOP / WAIT(等待计数)/ DUMP

字节序处理:地址/长度 kernel 内 bswap64/32,mkey 按编译宏 DOCA_GPUNETIO_VERBS_MKEY_SWAPPED 预交换(def.h:124)。

3.2 SQ 槽位并发管理:reserve → prepare → mark ready

多线程/多 warp 并发向同一 SQ 投递时的三步协议(qp.cuh):

  1. reserve (:113 reserve_wq_slots):atomicAdd(&qp->sq_rsvd_index, count) 原子领取槽位区间;随后 wait_until_slot_available(:94)------若槽位复用一圈,先 poll_cq_at 等旧 WQE 完成;
  2. prepare:各线程独立写自己的 WQE(互不干扰);
  3. mark ready (:231 mark_wqes_ready → :156 doca_gpu_dev_common_mark_wqes_ready):fence.release 后用 atomicCAS 按序推进 sq_ready_index ------只有"前一个区间已 ready"的线程才能把 ready 边界推过自己区间,保证 WQE 按序对网卡可见(EXCLUSIVE 模式直接赋值,CTA/GPU 模式 CAS 自旋)。

3.3 提交:敲门铃(qp.cuh:414-683)

doca_gpu_dev_verbs_submit(:654)按 nic_handler 分发:

  • DB 模式 (submit_db :524):拿 sq_lock,atomicMax(&sq_wqe_pi) 确认自己是最新 PI 后执行三段式 :

    c 复制代码
    ring_db(qp, prod_index);        // ① 尽早敲一次,让网卡尽快取 WQE
    update_dbr(sq_dbrec, pi);       // ② 写 doorbell record(恢复路径用)
    ring_db(qp, prod_index);        // ③ 再敲一次保证正确性

    ring_db(:423)= prepare_db(:339,构造 8B 伪 ctrl 段:qpn_ds | prod_index<<8)+ write_db(:359)→ cuda::atomic_ref<thread_scope_system> 或 st.mmio.relaxed.sys.global.b64(common.cuh:146)写门铃寄存器。

  • BlueFlame (submit_bf :565 / submit_bf_warp :600):Hopper+ 用 TMA cp.async.bulk.global.shared::cta 把整条 64B WQE 从 shared memory 直接推进门铃(:445);或 warp 内 8 线程并行写 64B(:458)------WQE 随门铃一次到网卡,省 HCA 一次显存读。

  • CPU proxy (submit_proxy :638):只把 PI fetch_max 到共享 cpu_db(:479),真实门铃由 CPU 线程敲(§2.8)。

  • NO_DBR / FREE_FLOW:不写 DBR / 不做事。

3.4 CQE 轮询(doca_gpunetio_dev_verbs_cq.cuh)

poll_cq_at(qp, ticket) ------ ticket 即 put 返回的 wqe_idx,阻塞等到该 WQE 完成:

  • device CQ(CQE 在显存,CQ_64B) :poll_one_cq_device_at(cq.cuh:173→:112)按 cons_index & (cqe_num-1) 定位 CQE,ld.relaxed.sys.global 读 op_own,owner bit 与圈数校验 (:131-148,≥sm_90 只读 1B,否则读 4B 并校验 wqe_counter 防撕裂);命中后 fence.acquire + atomicMax(&cq->cqe_ci);
  • collapsed CQ :多完成折叠一个 CQE,只盯固定槽位,靠 wqe_counter 推算新 cqe_ci(:220-254);
  • host CQ(COLLAPSED_HOST) :GPU 不读 CQE,只看 CPU proxy 回写的 cqe_ci(:197-204)。

3.5 高层单边 API(doca_gpunetio_dev_verbs_onesided.cuh)

  • doca_gpu_dev_verbs_put (:173):RDMA Write。thread 模式(:49):>1GiB 自动切多条 WQE(MAX_TRANSFER_SIZE=1GiB,def.h:193)→ reserve → 逐条 prepare → mark ready → submit → 返回 ticket=wqe_idx;warp 模式(:110,sm_80+):lane0 reserve 32 槽、__reduce_max_sync 广播、每 lane 写一条 WQE、lane0 mark+submit------一个 warp 一次推 32 条并发写;
  • doca_gpu_dev_verbs_put_signal (:382→:291):先连续 RDMA_WRITE,最后追加一条 ATOMIC_FA WQE 更新对端信号槽(sig_raddr/sig_val)------数据+完成通知一次完成(NVSHMEM 语义基础);signal 也可打包进 RDMA 立即数(def.h:162-185 的 IMM 位图:op|id|value);
  • put_inline / put_counter (counter.cuh:253,数据+计数器原子加)、doca_gpu_dev_verbs_wait*(轮对端信号/计数到阈值,配合 WAIT WQE :1155)。

3.6 GPU 内存模型原语(common.cuh)

原语 实现 作用
fence_release<scope> (:199) fence.release.cta/gpu/sys(无 PTX 支持时 st.release 模拟) 保证 WQE 写完再敲门铃
fence_acquire_nvidia_nic() (:190) 默认轻量 fence.acquire.cta(宏 :130 可切 sys)------利用 NIC 读显存的特性放宽模型 CQE 命中后读数据
load_relaxed_sys_global (:398) ld.relaxed.sys.global.L1::no_allocate 读 CQE/DBR 不进 L1
store_relaxed/release_mmio (:146/153) st.mmio.relaxed/release.sys.global.b64 敲门铃
load_const (:463) __ldg(只读缓存) 读 dev QP 结构中的常量字段
atomic_add/max、lock/unlock (:321/:339/:368) cuda::atomic_ref(block/device/system scope) 槽位与 PI 并发控制

4. 端到端工作流程

4.1 控制面时序(以 examples/verbs_common.cpp 为例)

复制代码
ibv_open_device(:179) → ibv_alloc_pd → ibv_reg_mr(数据缓冲)
doca_gpu_create(:258) → doca_verbs_dev_open(:292)
doca_gpu_verbs_create_qp_hl(:341)            # 一键:
   CQ 显存 umem → DevX CREATE_CQ(cqe 在显存)
   UAR 分配(NC_DEDICATED/NC/BF)
   QP 显存 umem → DevX CREATE_QP(SQ/DBR 在显存)
   export_qp + get_qp_dev → qp_gpu(显存中的 dev QP)
connect_verbs_qp(:411):socket 交换 QPN/GID/rkey →
   doca_verbs_qp_modify × 3(RST2INIT / INIT2RTR / RTR2RTS,:541/:557/:573)
启动 CUDA kernel <<<blocks, threads>>>(qp_gpu, ...)

4.2 数据面时序(client 发 put_bw,examples/gpunetio_verbs_put_bw/gpunetio_verbs_put_bw_kernel.cu:47)

复制代码
每线程/每 warp 循环:
① doca_gpu_dev_verbs_put(qp, {dst_addr,dst_key}, {src_addr,src_key}, size, &ticket)
   = reserve(atomicAdd sq_rsvd_index,必要时 poll 等槽)
   → wqe_prepare_write(寄存器拼 ctrl+raddr+data,st.weak.cs 写显存 SQ)
   → mark_wqes_ready(CAS 推进 sq_ready_index,保序)
   → submit(GPU_SM_DB:st.mmio 敲门铃;CPU_PROXY:写 cpu_db)
② doca_gpu_dev_verbs_poll_cq_at(qp, ticket)
   = 轮显存 CQE 的 owner bit,命中后 atomicMax cqe_ci → 该 WQE 槽位可复用
(server 侧无 CUDA kernel,数据由 HCA 直接 RDMA 写进 server 显存/内存,ctrl+c 校验)

对比传统 verbs 路径(analysis-02):ibv_post_send(CPU 写主机内存 SQ + MMIO)→ HCA DMA。GDAKI 把"写 WQE + 敲门铃 + 轮 CQE"三件事全部搬进 GPU kernel 与显存,CPU 数据面开销降为零,且天然适配 GPU 计算流水线(算完立即发,发完继续算)。


5. 构建系统(Makefile,225 行)

  • 产物 lib/libdoca_gpunetio_host.so.<ver>(SONAME);版本号从 doca_gpunetio_verbs_def.h awk 提取(:44-49);
  • 依赖:CUDA(CUDA_HOME、CUDA_ARCH ?= 80,-lcudart_static)、libibverbs、libmlx5(或 USE_NET_WRAPPER=1 时改 dlopen 封装);USE_CUDA_WRAPPER=1 时 dlopen libcuda;
  • 配置头 doca_gpunetio_config.h 由 scripts/configure 调 scripts/compile-time-test/ 三个探测脚本生成,决定 dmabuf umem / NC_DEDICATED UAR / CUDA dmabuf 三个特性宏;
  • make -j 编库 + 3 个示例;make install PREFIX=... CUDA_ARCH=90 安装;
  • 日志:DOCA_LOG 宏走 syslog,级别由环境变量 DOCA_GPUNETIO_LOG(0-7) 控制。

6. 关键文件/函数索引

位置 内容
include/common/doca_gpunetio_verbs_dev.h:173/155/103 ★ GPU 侧 QP/CQ/WQE 结构(导出镜像)
include/common/doca_gpunetio_verbs_def.h:49/341 版本 v5.0.0;nic_handler/cq_type/exec_scope 枚举
src/doca_gpunetio.cpp:124/268/603/682/881/1171/1358/1481 gpu_create / mem_alloc / dmabuf_fd / export_uar / export_qp / get_qp_dev / cpu_proxy_progress
src/doca_verbs_qp.cpp:722/835/956/1131/1424/3646 DevX CREATE_QP / 状态机三迁 / create / modify
src/doca_verbs_cq.cpp:117/165/736/858 DevX CREATE_CQ(collapsed)/ create / 公开入口 / get_dbr_db_addr
src/doca_verbs_umem.cpp:77/107/143 umem 注册(dmabuf 优先、peermem 兜底)
src/doca_verbs_uar.cpp:98/101 UAR 分配、dbr_less_addr=base+0x600
src/doca_gpunetio_gdrcopy.cpp:237/285 dlopen libgdrapi;GPU 显存 CPU 映射
src/doca_gpunetio_high_level.cpp:107/160/352/720/1122 高层一键 API
include/device/doca_gpunetio_dev_verbs_qp.cuh:45/113/156/339/423/524/654/709 ★ st.weak.cs / reserve / mark_ready / prepare_db / ring_db / submit_db / submit / wqe_prepare_write
include/device/doca_gpunetio_dev_verbs_cq.cuh:112/173/220/293 CQE owner-bit 轮询 / collapsed CQ / poll_one_cq_at
include/device/doca_gpunetio_dev_verbs_onesided.cuh:49/110/291/382 ★ put(thread/warp)/ put_signal
include/device/doca_gpunetio_dev_verbs_common.cuh:146/190/199/398/463 mmio store / NIC fence / release fence / relaxed load / __ldg
examples/gpunetio_verbs_put_bw/*_kernel.cu:47/64 GDAKI perftest:kernel 内 put + poll_cq_at
examples/verbs_common.cpp:234/341/411 控制面标准流程(create_qp_hl + connect)

7. 小结

  1. 架构本质 :DOCA GPUNetIO 开源版把 RDMA verbs 数据面整体搬迁到 GPU------SQ/CQ/DBR 经 dmabuf umem 安置在 GPU 显存(DevX wq_umem_id/cq_umem_id),门铃寄存器经 cudaHostRegister(IoMemory) 映射进 GPU 地址空间;CUDA kernel 用 st.weak.cs 写 WQE、st.mmio.sys 敲门铃、owner-bit 轮 CQE,实现真正的 kernel-initiated RDMA。
  2. 控制面双模 :公开 API 先 dlopen 闭源 DOCA SDK(DOCA_SDK_LIB_PATH),失败回退纯开源 DevX 实现,兼顾开放与功能完整。
  3. 并发协议:reserve(atomicAdd)→ 独立 prepare → CAS 保序 mark_ready → atomicMax PI 后敲门铃,支持多线程/多 warp 共享一个 SQ;warp 模式一次提交 32 条 WQE。
  4. 降级链完整:门铃不可映射→CPU proxy(含 free-flow 批量代理、后台 service 线程);NO_DBR 硬件模式与 GDRCopy 软件模拟;dmabuf→peermem;collapsed/host CQ------在各种硬件能力下都能工作,性能逐档递减。
  5. 语义聚焦 RC 单边:put(RDMA_WRITE,>1GiB 自动切片)、put_signal(写+对端原子信号)、put_counter、inline put、atomic、wait------正好覆盖 NVSHMEM/NCCL 等 GDAKI 消费者所需的单边通信+通知模型;开源版不含双边 send/recv 与 Ethernet GPU 数据面。
相关推荐
蒸鱼Yuzheng11 小时前
RenderDoc 跨版本回放治理:驱动、API、资源与兼容性矩阵
数据治理·gpu·兼容性测试·vulkan·renderdoc
gwf21615 小时前
RoCEv2网络性能调优:DCQCN、ECN与PFC协同驱动级深度解析
驱动开发·rdma·rocev2·linuxkernel·pfc·dcqcn·ecn
Smoothcloud润云18 小时前
PyTorch推理请求超时与取消处理实战
人工智能·pytorch·python·深度学习·机器学习·gpu算力·gpu
Eloudy20 小时前
MLNX_OFED 的 install.pl 安装流程分析
网络·rdma·roce
Starry-sky(jing)20 小时前
onnxruntime 安装后 GPU 没加速?3 条命令确认它跑在 CPU 还是 GPU
python·gpu·cuda·onnxruntime·推理加速
Eloudy21 小时前
RoCE UC Receive 实现代码结构与原理分析
rdma·roce
Eloudy1 天前
RoCE 的 UC SEND/RECEIVE 的完整交互过程分析
网络·rdma·roce
论文复现现场3 天前
8卡 A100 服务器哪里租?NVLink、RDMA 与 NCCL 验收指南
linux·服务器·php·分布式训练·rdma·nvlink·nccl
gwf2164 天前
RDMA事件与异步通知机制:CMA事件与设备事件深度解析
驱动开发·linux内核·rdma·rdma_cm·异构计算·异步事件·cve分析