分析对象:
./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)
cudaDeviceGetByPCIBusId由 PCIe BDF 定位 CUDA 设备(沿用调用方 context);- 能力探测:
CU_DEVICE_ATTRIBUTE_DMA_BUF_SUPPORTED、GPU_DIRECT_RDMA_WRITES_ORDERING(决定是否需 memory-consistency 算法)、compute capability ≥ 10(async store release)、GDRCopy 可用性; - 建
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):
- 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; - 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):
- reserve (:113
reserve_wq_slots):atomicAdd(&qp->sq_rsvd_index, count)原子领取槽位区间;随后wait_until_slot_available(:94)------若槽位复用一圈,先poll_cq_at等旧 WQE 完成; - prepare:各线程独立写自己的 WQE(互不干扰);
- mark ready (:231
mark_wqes_ready→ :156doca_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 后执行三段式 :cring_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+ 用 TMAcp.async.bulk.global.shared::cta把整条 64B WQE 从 shared memory 直接推进门铃(:445);或 warp 内 8 线程并行写 64B(:458)------WQE 随门铃一次到网卡,省 HCA 一次显存读。 -
CPU proxy (
submit_proxy:638):只把 PIfetch_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.hawk 提取(: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. 小结
- 架构本质 :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。 - 控制面双模 :公开 API 先 dlopen 闭源 DOCA SDK(
DOCA_SDK_LIB_PATH),失败回退纯开源 DevX 实现,兼顾开放与功能完整。 - 并发协议:reserve(atomicAdd)→ 独立 prepare → CAS 保序 mark_ready → atomicMax PI 后敲门铃,支持多线程/多 warp 共享一个 SQ;warp 模式一次提交 32 条 WQE。
- 降级链完整:门铃不可映射→CPU proxy(含 free-flow 批量代理、后台 service 线程);NO_DBR 硬件模式与 GDRCopy 软件模拟;dmabuf→peermem;collapsed/host CQ------在各种硬件能力下都能工作,性能逐档递减。
- 语义聚焦 RC 单边:put(RDMA_WRITE,>1GiB 自动切片)、put_signal(写+对端原子信号)、put_counter、inline put、atomic、wait------正好覆盖 NVSHMEM/NCCL 等 GDAKI 消费者所需的单边通信+通知模型;开源版不含双边 send/recv 与 Ethernet GPU 数据面。