本文最后更新于:2026年5月20日 晚上
Linux 内核 AI Infra 全景:GPU、加速器、RDMA 与异构计算
目录
- 内核 AI 基础设施总览
- 专用 AI 加速器框架:drivers/accel
- GPU 计算驱动
- RDMA 子系统详解
- 软 RDMA vs 硬 RDMA:架构差异深度对比
- GPU ↔ RDMA 集成:零拷贝数据传输
- 异构内存管理:HMM/GPUSVM/Pagemap
- 其他 AI 相关基础设施
- 分布式 AI 训练的完整内核栈
内核 AI 基础设施总览
Linux 内核中与 AI Infra 相关的代码分四大板块:
1 2 3 4 5 6 7 8 9 10 11
| ┌─────────────────────────────────────────────────────────────┐ │ AI Infra in Linux │ │ │ │ ┌─────────┐ ┌──────────┐ ┌─────────┐ ┌──────────────┐ │ │ │ accel │ │ GPU DRM │ │ RDMA/IB │ │ 跨子系统基础 │ │ │ │ 加速器 │ │ GPU 驱动 │ │ 网络互联 │ │ HMM/DMA-BUF │ │ │ └────┬────┘ └────┬─────┘ └────┬────┘ └──────┬───────┘ │ │ │ │ │ │ │ │ ─────┴────────────┴─────────────┴───────────────┴────── │ │ all built on DRM base + IB core + mm core │ └─────────────────────────────────────────────────────────────┘
|
| 板块 |
路径 |
核心职责 |
| accel |
drivers/accel/ |
专用 NPU/AI 加速器驱动框架 |
| GPU DRM |
drivers/gpu/drm/ |
GPU 计算驱动(amdgpu/nouveau/xe/panthor) |
| RDMA/IB |
drivers/infiniband/ |
高速网络互联(InfiniBand/RoCE/iWARP) |
| 跨子系统 |
mm/hmm.c, drivers/dma-buf/, drivers/cxl/ |
异构内存、零拷贝共享、缓存一致性互联 |
专用 AI 加速器框架:drivers/accel/
内核为 AI/ML 加速器设立了专门的子系统 drivers/accel/,Kconfig 明确声明:
“Machine-Learning and Deep-Learning acceleration devices.”
它基于 DRM 构建,但有独立的设备节点 /dev/accel/accel*(major 号与 DRM 分离),提供专门的 UAPI。
核心框架
1 2 3 4
| drivers/accel/ ├── Kconfig ← DRM_ACCEL config entry ├── Makefile ← 构建 6 个加速器驱动 └── drm_accel.c ← 核心粘合层,与 DRM 衔接
|
已有驱动
| 驱动 |
目录 |
硬件 |
定位 |
| amdxdna |
drivers/accel/amdxdna/ |
AMD NPU (Ryzen AI 300) |
客户端 AI 推理,AIE2 solver,HMM 支持 |
| habanalabs |
drivers/accel/habanalabs/ |
Habana Gaudi/Goya |
数据中心 DL 训练/推理 |
| ivpu |
drivers/accel/ivpu/ |
Intel NPU (Meteor Lake+) |
客户端 CV/DL 推理 |
| qaic |
drivers/accel/qaic/ |
Qualcomm Cloud AI |
云端 DL 推理加速卡 |
| rocket |
drivers/accel/rocket/ |
Rockchip NPU (RK3588) |
边缘 AI 推理 (RKNN) |
| ethosu |
drivers/accel/ethosu/ |
Arm Ethos-U65/U85 |
嵌入式 microNPU |
UAPI 头文件
1 2 3 4 5 6
| include/uapi/drm/amdxdna_accel.h include/uapi/drm/habanalabs_accel.h include/uapi/drm/ivpu_accel.h include/uapi/drm/qaic_accel.h include/uapi/drm/rocket_accel.h include/uapi/drm/ethosu_accel.h
|
GPU 计算驱动
GPU 仍是 AI 训练/推理的主力硬件,内核中主要 GPU 计算驱动如下:
AMD GPU — 最完整的开源 AI 计算栈
1 2 3 4
| drivers/gpu/drm/amd/ ├── amdgpu/ ← GPU 驱动核心 (CDNA/MI Instinct + RDNA) ├── amdkfd/ ← HSA Kernel Fusion Driver (ROCm 后端) └── ...
|
amdkfd 是 AMD AI 计算的关键:
HSA_AMD_SVM:HMM-based shared virtual memory(HIP 统一内存)
HSA_AMD_P2P:GPU to GPU peer-to-peer DMA
- 选择
HMM_MIRROR,支持 CPU-GPU 页表镜像
Intel GPU — oneAPI 后端
1 2 3 4 5
| drivers/gpu/drm/ ├── i915/ ← Intel 集显 (pre-Xe),OpenCL/Vulkan compute ├── xe/ ← Intel Xe2 独立显卡 (Battlemage/Lunar Lake) │ ├── DRM_XE_GPUSVM ← GPU 共享虚拟内存 (ZONE_DEVICE) │ └── DRM_XE_PAGEMAP ← 设备内存池管理器
|
NVIDIA GPU — 开源 + Rust 新驱动
1 2 3 4 5 6
| drivers/gpu/drm/ ├── nouveau/ ← 传统开源驱动 │ ├── nouveau_svm.c ← HMM SVM (CUDA Unified Memory) │ └── nouveau_dmem.c ← 设备内存管理 ├── nova/ ← Rust 编写的 GSP 架构新驱动 (WIP) └── nova-core/ ← Rust GSP 核心库 (WIP)
|
ARM Mali GPU — 移动/边缘 AI
1 2 3 4
| drivers/gpu/drm/ ├── panfrost/ ← Mali Midgard/Bifrost ├── panthor/ ← Mali Valhall CSF (v10+) └── tyr/ ← Rust 编写的 Mali CSF 新驱动 (WIP)
|
其他有计算能力的 GPU
1 2 3 4 5 6 7
| drivers/gpu/drm/ ├── msm/ ← Qualcomm Adreno (Vulkan/OpenCL 通过 freedreno) ├── v3d/ ← Broadcom VideoCore (Raspberry Pi, OpenCL) ├── etnaviv/ ← Vivante GCxxx (NXP i.MX) ├── lima/ ← ARM Mali Utgard (Mali-400/450) ├── imagination/ ← PowerVR Rogue └── virtio/ ← 虚拟 GPU (KVM GPU 直通/虚拟化)
|
GPU 核心计算框架
1 2 3 4 5 6 7
| drivers/gpu/drm/ ├── scheduler/ ← GPU 命令提交调度 (DRM_SCHED) │ amdgpu/nouveau/xe/panthor/所有 accel 驱动均依赖 ├── ttm/ ← GPU 多内存类型管理 (VRAM/GTT/system) ├── drm_gpuvm.c ← GPU 虚拟地址空间管理 (rb-tree) ├── drm_gpusvm.c ← GPU 共享虚拟内存层 (基于 HMM) └── drm_pagemap.c ← 设备内存池 (xe 使用)
|
RDMA 子系统详解
RDMA 代码在 drivers/infiniband/ 下,分四个层次:
1 2 3 4 5
| drivers/infiniband/ ├── core/ ← RDMA 核心框架 ├── hw/ ← 硬件驱动 (19 个) ├── sw/ ← 软件实现 └── ulp/ ← Upper Layer Protocols
|
核心模块 (core/)
| 模块 |
产出的 .ko |
功能 |
ib_core |
ib_core.ko |
IB 核心:verbs API、MR、CQ、QP |
rdma_cm |
rdma_cm.ko |
RDMA 连接管理器 |
ib_cm |
ib_cm.ko |
IB 通信管理器 |
iw_cm |
iw_cm.ko |
iWARP 通信管理器 |
ib_uverbs |
ib_uverbs.ko |
用户态 Verbs API |
ib_umad |
ib_umad.ko |
用户态 MAD 接口 |
rdma_ucm |
rdma_ucm.ko |
用户态连接管理器 |
硬件驱动 (hw/)
| 驱动 |
厂商 |
协议 |
AI 训练集群常见度 |
| mlx5 |
NVIDIA/Mellanox ConnectX-4+ |
InfiniBand + RoCE |
★★★★★ |
| mlx4 |
Mellanox ConnectX-3 |
InfiniBand + RoCE |
★★★ |
| irdma |
Intel E810+ |
RoCEv2 |
★★★ |
| hfi1 |
Intel Omni-Path |
Omni-Path |
★★★ |
| erdma |
阿里云 Elastic RDMA |
RoCEv2 (云原生) |
★★★ |
| hns |
华为 HiSilicon |
RoCE |
★★ |
| bnxt_re |
Broadcom NetXtreme-E |
RoCEv2 |
★★ |
| ionic |
AMD Pensando |
RoCE |
★★ |
| mana |
Microsoft Azure MANA |
RDMA (Azure) |
★★ |
| efa |
Amazon EFA |
SRD (AWS) |
★★★ |
| vmw_pvrdma |
VMware |
虚拟 RDMA |
★★ |
| cxgb4 |
Chelsio T4/T5 |
iWARP |
★ |
| qedr |
Marvell FastLinQ |
RoCE/iWARP |
★ |
| ocrdma |
Emulex OneConnect |
RoCE |
★ |
| usnic |
Cisco UCS VIC |
USNIC |
★ |
| bng_re |
Broadcom NetXtreme-C/E |
RoCEv2 |
★ |
| mthca |
Mellanox InfiniHost (legacy) |
InfiniBand |
★ |
软件实现 (sw/)
| 模块 |
协议 |
说明 |
| rxe |
RoCEv2 软件实现 |
无需 RDMA 硬件,以太网即可 |
| siw |
iWARP 软件实现 |
无 RDMA 硬件也能跑 iWARP |
| rdmavt |
— |
RDMA 虚拟传输层库 |
Upper Layer Protocols (ulp/)
| 模块 |
说明 |
| ipoib |
IP over InfiniBand |
| iser/isert |
iSCSI over RDMA (initiator/target) |
| srp/srpt |
SCSI RDMA Protocol (initiator/target) |
| rtrs |
RDMA Transport (块设备远程挂载) |
软 RDMA vs 硬 RDMA:架构差异深度对比
drivers/infiniband/sw/ 和 drivers/infiniband/hw/ 的根本区别:数据搬运谁来做。
核心对比
|
rxe (软 RoCE) |
siw (软 iWARP) |
mlx5 (硬 RDMA) |
| 数据搬运 |
CPU memcpy |
CPU skb_copy_bits |
网卡 PCIe DMA 引擎 |
| 网络栈 |
走内核 IP/UDP 栈 |
走内核 TCP 栈 |
网卡硬件直接发包 |
| 内存注册 |
只 pin 页,记录 struct page* 数组 |
同 rxe |
写入网卡 IOMMU/TLB,存总线地址 |
| 每字节 CPU 拷贝 |
≥ 2 次 |
≥ 2 次 |
0 次 |
| 完成通知 |
workqueue 直接回调 |
sk_data_ready socket 回调 |
MSI-X 硬中断 + doorbell |
| 单向延迟 |
10-30 µs |
15-50 µs |
1-2 µs (IB),2-5 µs (RoCEv2) |
| 吞吐 |
~10-30 Gbps (受 CPU 限) |
~5-20 Gbps (受 TCP 限) |
100-400 Gbps (受 PCIe 限) |
| CPU 开销 |
100%(数据搬运全占用) |
100%(数据搬运全占用) |
< 5%(只写描述符 + doorbell) |
数据路径对比(代码佐证)
rxe — CPU 搬数据,走内核 IP/UDP 栈:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| va = kmap_local_page(info->page); if (dir == RXE_FROM_MR_OBJ) memcpy(addr, va + page_offset, bytes); else memcpy(va + page_offset, addr, bytes); kunmap_local(va);
if (skb->protocol == htons(ETH_P_IP)) err = ip_local_out(dev_net(...), skb->sk, skb); else err = ip6_local_out(dev_net(...), skb->sk, skb);
rxe_udp_encap_recv() → rxe_rcv() → workqueue tasklet → copy_data() 拷到用户页
|
siw — CPU 搬数据,走内核 TCP 栈:
1 2 3 4 5 6 7 8 9
| dest = kmap_atomic(p); rv = skb_copy_bits(srx->skb, srx->skb_offset, dest + pg_off, bytes);
rv = kernel_sendmsg(sock, &msg, vec, num, len);
tcp_read_sock(sk, bytes, siw_tcp_rx_data);
|
mlx5 — CPU 只写描述符,网卡 DMA:
1 2 3 4 5 6 7 8 9 10 11
| qp->db.db[MLX5_SND_DBR] = cpu_to_be32(qp->sq.cur_post); wmb(); mlx5_write64((__be32 *)ctrl, bf->bfreg->map + bf->offset);
cq->mcq.tasklet_ctx.comp = mlx5_ib_cq_comp;
mlx5_cq_arm(&cq->mcq, ..., uar_page, to_mcq(ibcq)->mcq.cons_index);
|
内存注册的本质区别
软 RDMA:ib_umem_get() 只是 pin 住用户页,存个 page_info[] 数组。后续访问靠 kmap + memcpy:
1 2 3 4 5
| umem = ib_umem_get(&rxe->ib_dev, start, length, access); rxe_mr_fill_pages_from_sgt(mr, &umem->sgt_append.sgt);
|
硬 RDMA:create_mkey 固件命令将 DMA 总线地址编程进网卡 IOMMU/TLB。网卡拿到 MR key 后能从 TLB 查到物理地址直接 DMA:
1 2 3 4
| pas = (__be64 *)MLX5_ADDR_OF(create_mkey_in, in, klm_pas_mtt); mlx5_ib_populate_pas(umem, 1UL << mr->page_shift, pas, ...); err = mlx5_ib_create_mkey(dev, &mr->mmkey, in, inlen);
|
软 RDMA 的底层依赖 INFINIBAND_VIRT_DMA(drivers/infiniband/Kconfig:77-78),这是一个编译时恒等映射技巧:!HIGHMEM 时所有内核内存都在 direct map 中,virt_to_page() 和 page_to_virt() 是 O(1) 操作。没有真实的 DMA 映射。
软 RDMA 的价值
既然性能差距悬殊,软 RDMA 有什么用?
- 开发调试:在没有 RDMA 硬件的笔记本上开发和测试 RDMA 应用
- 协议验证:验证 RoCE/iWARP 协议正确性,无需昂贵硬件
- 功能测试:CI/CD 流水线中跑 RDMA verbs 功能测试
- 学习研究:理解 RDMA 协议和行为,源码线性可读
- 低端需求:吞吐要求不高但需要 RDMA 语义(单边操作、零拷贝)的场景
RDMA 丢包解析:两层视角
“RDMA 不丢包” 是流传最广也最容易误解的说法。正确理解需要分两个层次看:链路层和传输层不是一回事。
1 2 3 4 5 6 7 8 9 10 11 12
| ┌──────────────────────────────────────────────────┐ │ 传输层(IB Transport) │ │ • QP 重传、超时、RNR NAK ← 这里会"丢" │ │ • IB_WC_RETRY_EXC_ERR, IB_WC_RESP_TIMEOUT_ERR │ │ • 原因:对端 buffer 不够、处理超时、协议违例 │ │ • 软硬 RDMA 都走同一套 ib_qp_attr 重传参数 │ ├──────────────────────────────────────────────────┤ │ 链路层(IB Link Layer) │ │ • credit-based 逐跳流控 ← 这里"不丢" │ │ • 物理线缆上缓冲溢出被消除 │ │ • 硬 RDMA 硬件保证,软 RDMA 做不到 │ └──────────────────────────────────────────────────┘
|
类比 TCP over Ethernet 更容易理解:
以太网链路层 CRC 校验保证帧传输不 bit-flip(链路层保护),但 TCP 仍然要做重传,因为路由丢包、接收端 socket buffer 满、三次握手失败等(传输层保护)。CRC 没坏 ≠ TCP 不会超时重传。
链路层:硬 RDMA 真不丢,软 RDMA 做不到
硬 RDMA (InfiniBand) — 逐跳硬件信用(credit)流控:
1 2
| Host HCA ──[credits]──→ Switch 1 ──[credits]──→ Switch 2 ──[credits]──→ Target HCA "有 32 个 VL15 buffer,发吧" "有 16 个,发吧" "能收 8 个,发吧"
|
核心特点:
- 逐跳(per-hop):每根线缆两端独立协商 credits,不依赖端到端
- 硬件实现:HCA/Switch ASIC 纳秒级响应,不经过 OS
- 按虚拟通道(VL)隔离:每个 VL 有独立 credit 池,管理流量不被数据流量饿死
- 发送方不发没有 buffer 的包 → 物理上没有缓冲溢出 → 链路层丢包不可能
这跟 TCP/IP 的设计哲学根本不同:
1 2 3 4 5 6 7 8 9 10
| TCP over Ethernet 的流控: Host ──────────→ Router ──────────→ Server TCP 滑动窗口(端到端) ARP(链路层) 丢包 = 拥塞信号 无逐跳硬件信用 AIMD 降速 交换机只管转发
InfiniBand 的流控: HCA ──[credits]──→ Switch ──[credits]──→ HCA 信用协商消除拥塞(前验) 逐跳硬件保证(后验) 物理上禁止缓冲溢出 每跳独立协商
|
|
TCP/IP (Ethernet) |
InfiniBand |
| 流控位置 |
端到端(TCP 滑动窗口,只关心最终接收方 rwnd) |
逐跳(每根线缆硬件 credit,关心中间每一跳) |
| 拥塞信号 |
丢包 = 拥塞信号,触发慢启动/AIMD 降速(后验) |
credit 协商消除拥塞可能(前验) |
| 丢包态度 |
容忍丢包,丢了再重传 |
设计上禁止丢包 |
| 中间交换机 |
buffer 不够就 tail drop 或 RED 随机丢 |
每跳有 credit 池,不会溢出 |
以太网的 PAUSE 帧 ≠ credit 流控。 802.3x PAUSE 做了链路层流控但:
- 全局停等而非逐 VL:一发 PAUSE 整根线全停,不分流量类型
- 头阻(HOL blocking):一根线停导致上游全堵
- PFC 改进为按优先级停,但仍有 PFC 风暴和死锁风险
- 生产环境中基本不用:数据中心 TCP 靠 ECN + DCTCP/DCQCN,不依赖链路层无损
软 RDMA (rxe/siw) — 没有链路层保护,走的是普通以太网 + 内核 IP/UDP/TCP 栈。链路层该丢照丢。
传输层:无论软硬,RDMA 都会丢
链路层 credit 保证的是:包从 HCA → 交换机 → 远端 HCA 这段物理路径上不会因 buffer 溢出被丢弃。 但传输层仍然面临:
链路层 CRC 没坏 ≠ TCP 不会超时重传。网卡不收错包 ≠ 对端软件不拒绝。
创建 QP 必须配置重传参数(struct ib_qp_attr):
| 参数 |
含义 |
典型值 |
retry_cnt |
发送端重试上限(重传次数耗尽 → QP error) |
7 |
rnr_retry |
Receiver Not Ready 重试上限 |
7 |
min_rnr_timer |
RNR NAK 最小等待延迟(对端多久准备好 buffer) |
12 (≈1ms) |
timeout |
本地 ACK 超时 4.096 × 2^timeout µs |
14 (≈67ms) |
packet_life_time |
包在网络中存活的最长时间 |
— |
这些参数存在的本身就在说:RDMA 不仅会丢,还必须设计应对”丢”的机制。
软硬 RDMA 共用同一套参数,代码完全一致:
1 2 3 4 5
| qp->attr.retry_cnt = attr->retry_cnt; qp->comp.retry_cnt = attr->retry_cnt; qp->attr.rnr_retry = attr->rnr_retry; qp->comp.rnr_retry = attr->rnr_retry;
|
传输层错误码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| enum ib_wc_status { IB_WC_SUCCESS, IB_WC_RETRY_EXC_ERR, IB_WC_RNR_RETRY_EXC_ERR, IB_WC_RESP_TIMEOUT_ERR, IB_WC_LOC_ACCESS_ERR, IB_WC_REM_ACCESS_ERR, IB_WC_WR_FLUSH_ERR, };
case MLX5_CQE_SYNDROME_LOCAL_ACCESS_ERR: wc->status = IB_WC_RETRY_EXC_ERR; wc->status = IB_WC_RNR_RETRY_EXC_ERR;
|
传输层”丢”的常见场景
链路层可能完好,但传输层照样失败:
| 场景 |
链路层 |
传输层 |
| 远端 QP recv queue 满了 |
链路正常,包成功到达 |
RNR NAK,发送方等 min_rnr_timer 后重试 → rnr_retry 耗尽 → IB_WC_RNR_RETRY_EXC_ERR |
| 远端处理超时没回 ACK |
链路正常 |
ACK 超时,发送方重传 → retry_cnt 耗尽 → IB_WC_RETRY_EXC_ERR |
| 远端 MR key 不合法 |
链路正常,包成功到达 |
访问违例,远端网卡拒绝 → IB_WC_REM_ACCESS_ERR |
| 光纤断了/光模块坏 |
链路挂了,包没了 |
超时重传耗尽 → QP error |
| RoCEv2 PFC 死锁/风暴 |
链路层无损机制失效 |
实际丢包 → ACK 超时 → 重传 |
软 RDMA 的丢包恢复路径
软 RDMA 没有链路层 credit,链路层该丢照丢。可靠交付靠 PSN 包序号(类似 TCP 序列号)+ 定时器重传 补回来。
1 2 3 4 5 6 7 8 9
| 发送方 rxe 接收方 rxe │ │ │ PSN=1 ────────── [丢了] ────X │ │ PSN=2 ──────────────────────────→ │ diff > 0:PSN 2 比期望大 → 1 丢了 │ │ 发 NAK(out-of-sequence) │ ←────── PSN NAK ──────────────── │ │ req_retry() 重置 WQE,从 PSN 1 重发 │ │ PSN=1 ──────────────────────────→ │ │ PSN=2 ──────────────────────────→ │ 收齐 ack_psn 推进
|
PSN 序号检测 — 发现丢包、重复、乱序:
1 2 3 4 5 6 7 8 9
| if (diff > 0) { qp->resp.sent_psn_nak = 1; rxe_counter_inc(rxe, RXE_CNT_OUT_OF_SEQ_REQ); return RESPST_ERR_PSN_OUT_OF_SEQ; } else if (diff < 0) { rxe_counter_inc(rxe, RXE_CNT_DUP_REQ); return RESPST_DUPLICATE_REQUEST; }
|
RNR NAK Timer — 用内核 timer 模拟硬件 QP 的 RNR 等待:
1 2 3 4 5 6 7 8
|
void rnr_nak_timer(struct timer_list *t) { qp->req.need_retry = 1; qp->req.wait_for_rnr_timer = 0; rxe_sched_task(&qp->send_task); }
|
req_retry() — 收到 NAK 后重置整个 SQ 从头再发:
1 2 3 4 5 6 7 8 9 10 11 12
|
static void req_retry(struct rxe_qp *qp) { qp->req.psn = qp->comp.psn; for (...) { wqe->dma.resid = wqe->dma.length; wqe->dma.cur_sge = 0; wqe->dma.sge_offset = 0; wqe->state = wqe_state_posted; } }
|
软硬丢包机制对比
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| 硬 RDMA (IB/mlx5): 链路层: 硬件 credit 协商,绝对不丢 ─→ 传输层: 只需处理 RNR/超时/违例 重传: 网卡 ASIC 内部硬件引擎,纳秒级响应 丢包率: 链路层 0,传输层看参数配置
软 RDMA (rxe): 链路层: 普通以太网 + 内核 UDP 栈,该丢照丢 ─→ 传输层: PSN 序号检测 + timer 重传 重传: 内核 workqueue + timer,毫秒级精度 丢包率: 链路层和物理网络一样,传输层靠重传补
共同点: • 都用同一套 ib_qp_attr(retry_cnt / rnr_retry / timeout) • 都走 PSN 序号校验 → NAK → 重传的 IB 协议标准 • PSN 之于 RDMA,就像序列号之于 TCP • 错误码同一套 enum ib_wc_status
本质区别: • 硬 RDMA:链路层物理保证不乱丢,传输层只管 RNR/超时/违例 • 软 RDMA:链路层完全不可靠,传输层用 PSN + timer 模拟可靠交付,和 TCP 思路一致 • TCP = "丢了再重传,通过丢包感知拥塞"(后验模型) • IB = "确保绝对不丢,通过信用协商消除拥塞可能"(前验模型)
|
一句话总结
InfiniBand 链路层是无损的(credit-based 硬件逐跳流控),RDMA 传输层仍然有重传和超时机制。RoCEv2 依赖 PFC 尝试链路无损但不稳定。软 RDMA 完全没有链路层保护,靠 PSN 序号(类似 TCP seq)+ 内核 timer 重传模拟可靠交付。QP 的 retry_cnt/rnr_retry/timeout 参数的存在就是最好的证据——RDMA 不仅会”丢”,还必须为此设计全套恢复机制。
GPU ↔ RDMA 集成:零拷贝数据传输
GPU 和 RDMA 的核心集成点是零拷贝——RDMA 网卡直接 DMA 读写 GPU 显存,CPU 和系统内存被完全旁路。
集成点 1:DMA-BUF (umem_dmabuf.c)
1
| drivers/infiniband/core/umem_dmabuf.c
|
RDMA 通过 ib_umem_dmabuf_get() 接收 GPU 导出的 dmabuf:
1 2 3 4 5 6
| sgt = dma_buf_map_attachment(umem_dmabuf->attach, DMA_BIDIRECTIONAL);
umem_dmabuf->umem.sgt_append.sgt.sgl = umem_dmabuf->first_sg; umem_dmabuf->umem.sgt_append.sgt.nents = nmap;
|
之后 RDMA 网卡 DMA 直接操作 GPU 显存:
1 2
| GPU 显存 → dmabuf export → ib_umem_dmabuf_get → dma_buf_map → RDMA NIC DMA ↑ CPU 旁路
|
GPU 驱动端(以 amdgpu 为例)负责将显存 Buffer 导出为 dmabuf,RDMA 端接收后直接做 DMA 映射。整个过程不需要 CPU 拷贝数据。
集成点 2:HMM + ODP (umem_odp.c)
1
| drivers/infiniband/core/umem_odp.c
|
1 2 3 4 5 6 7
| #include <linux/hmm.h> #include <linux/hmm-dma.h>
range.hmm_pfns = &(umem_odp->map.pfn_list[pfn_start_idx]); ret = hmm_range_fault(&range);
|
IB On-Demand Paging (ODP) 直接调用 hmm_range_fault()。当 GPU 和 CPU 共享统一内存(通过 HMM)时,RDMA 可以按需获取页映射,不需要预先 pin 所有物理内存。
完整的 GPUDirect RDMA 数据路径
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| ┌────────┐ ┌────────┐ │ GPU 0 │ VRAM │ GPU 1 │ VRAM │ ┌────┐ │ │ ┌────┐ │ │ │Buffer│──► dmabuf export │ │Buffer│ │ └────┘ │ │ │ └────┘ │ └────────┘ │ └────────┘ ▼ ▲ ┌─────────────┐ ┌─────────────┐ │ ib_umem_dmabuf│──► ib_core MR ──► QP send──► ib_core │ │ _get() │ │ verbs │ └──────┬──────┘ └──────┬──────┘ │ │ ┌──────▼──────┐ ┌──────▼──────┐ │ RDMA NIC │════════ RDMA wire ═════════│ RDMA NIC │ │ mlx5 │ zero-copy DMA │ mlx5 │ └─────────────┘ └─────────────┘ ▲ ▲ └───────── CPU 完全旁路 ──────────────────┘
|
关键点:
- GPU 驱动 (amdgpu/nouveau/xe):导出 dmabuf
- RDMA core (
umem_dmabuf.c):接收 dmabuf,建立 DMA 映射
- RDMA NIC 驱动 (mlx5/irdma/etc):硬件 DMA 引擎直接读写 GPU 显存
- HMM/ODP (
mm/hmm.c + umem_odp.c):按需页镜像,避免预 pin
异构内存管理:HMM/GPUSVM/Pagemap
1 2 3 4
| mm/hmm.c ← 核心:CPU 页表镜像到设备页表 drivers/gpu/drm/drm_gpusvm.c ← DRM 层 GPU SVM(基于 HMM) drivers/gpu/drm/drm_pagemap.c ← 设备内存池(xe 使用) drivers/gpu/drm/drm_gpuvm.c ← GPU 虚拟地址空间管理
|
调用关系
1 2 3 4 5 6 7 8 9 10 11 12
| 用户态 (CUDA/HIP/oneAPI) │ ├── gpu_buddy_alloc_blocks() ← GPU 显存分配 (drivers/gpu/buddy.c) │ 元数据从 SLUB kmem_cache 分配 │ ├── drm_gpusvm / drm_pagemap ← 共享虚拟内存管理 │ │ │ └── hmm_range_fault() ← CPU 页表镜像 (mm/hmm.c) │ ├── drm_gpuvm ← GPU VA 空间管理 (rb-tree) │ └── dmabuf export → ib_umem_dmabuf → RDMA DMA
|
其他 AI 相关基础设施
CXL (Compute Express Link)
1 2 3 4 5
| drivers/cxl/ ├── core/ ← CXL 核心:区域管理、内存设备、端口、HDM 解码器、RAS ├── pci.c ← CXL PCI 设备枚举 ├── acpi.c ← CXL ACPI 表解析 └── pmem.c ← CXL 持久内存支持
|
CXL 允许 CPU 和加速器之间缓存一致性内存共享(CXL.cache / CXL.mem),是未来 GPU/加速器直接访问系统内存或扩展显存的关键技术。
DMA-BUF 共享框架
1 2 3 4 5 6 7
| drivers/dma-buf/ ├── dma-buf.c ← buffer 共享核心 ├── dma-fence.c ← 跨设备同步 ├── dma-resv.c ← 缓冲区预留管理 ├── sync_file.c ← 用户态同步文件 ├── udmabuf.c ← 用户态可映射 DMA-BUF └── heaps/ ← 用户态可分配 DMA-BUF heaps (system, CMA)
|
GPU 内存分配器
1 2 3
| drivers/gpu/buddy.c ← GPU Buddy 分配器(增广红黑树,clear/dirty 双树) drivers/gpu/drm/drm_buddy.c ← DRM 层 buddy 打印封装 drivers/gpu/drm/ttm/ ← TTM 多内存类型管理器
|
详细对比见 gpu_buddy_analysis.md。
分布式 AI 训练的完整内核栈
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| ┌──────────────────────────────────────────────────────────────────┐ │ 分布式 AI 训练 (e.g. PyTorch DDP) │ │ │ │ All-Reduce: GPU 0 ←─RDMA─→ GPU 1 ←─RDMA─→ GPU 2 ←─... │ └──────────────────────────────────────────────────────────────────┘ │ ┌────────────────────────┼────────────────────────┐ │ 用户态 │ │ │ ┌──────────┐ ┌────────┴─────┐ ┌────────────┐ │ │ │ CUDA/ │ │ libibverbs │ │ 用户态驱动 │ │ │ │ ROCm │ │ (rdma-core) │ │ (UMD) │ │ │ └────┬─────┘ └──────┬───────┘ └─────┬──────┘ │ └──────┼───────────────┼────────────────┼────────┘ │ │ │ ═══════╪═══════════════╪════════════════╪══════════════ │ 内核态 │ │ │ │ │ ┌──────▼──────┐ ┌─────▼──────┐ ┌─────▼──────┐ │ amdgpu/xe/ │ │ ib_core │ │ accel │ │ nouveau │ │ ib_uverbs │ │ (amdxdna/ │ │ (GPU 驱动) │ │ rdma_cm │ │ habanalabs) │ └──┬──────┬───┘ └──┬────┬────┘ └────────────┘ │ │ │ │ ┌────▼──┐ ┌─▼────────┐ │ ┌──▼──────────┐ │ gpu_ │ │drm_gpusvm│ │ │ umem_dmabuf │ ← GPU dmabuf → RDMA 零拷贝 │ buddy │ │drm_gpuvm │ │ │ umem_odp │ ← HMM 按需页映射 └───────┘ └──┬───────┘ │ └─────────────┘ │ │ ┌─────▼──────┐ ┌─▼──────────┐ │ mm/hmm.c │ │ mlx5/irdma │ │ HMM │ │ (RDMA NIC) │ └────────────┘ └────────────┘ │ ┌─────▼──────┐ │ 内核 Buddy │ ← 物理 RAM 页框 │ page_alloc │ └────────────┘
|
关键数据流
- 内存分配:GPU Buddy 从 SLUB 借元数据管理 GPU 显存,内核 Buddy 管理物理 RAM
- 显存注册:GPU 驱动将显存 buffer 导出为 dmabuf
- RDMA 注册:
ib_umem_dmabuf_get() 接收 dmabuf,建立 DMA 映射
- 数据传输:RDMA NIC DMA 引擎直接读写 GPU 显存,CPU 完全旁路
- 统一内存:HMM 镜像 CPU 页表 → GPU SVM 层 → GPUDirect RDMA + ODP 按需 pin
源码速查
| 想了解的内容 |
先看这里 |
| NPU 加速器驱动怎么写 |
drivers/accel/drm_accel.c + 任意一个 accel 驱动 |
| GPU 显存怎么管 |
drivers/gpu/buddy.c (分配), drivers/gpu/drm/ttm/ (迁移) |
| GPU 统一内存怎么实现 |
mm/hmm.c → drivers/gpu/drm/drm_gpusvm.c |
| RDMA 怎么收 GPU 内存 |
drivers/infiniband/core/umem_dmabuf.c |
| RDMA 怎么用 HMM |
drivers/infiniband/core/umem_odp.c:365 hmm_range_fault() |
| 多 GPU 怎么 P2P |
drivers/gpu/drm/amd/amdkfd/ HSA_AMD_P2P |
| CXL 内存怎么共享 |
drivers/cxl/core/region.c |
| 软 RDMA 怎么实现 |
drivers/infiniband/sw/rxe/rxe_mr.c (MR), rxe_req.c (QP send), rxe_net.c (IP/UDP) |
| 硬 RDMA 怎么实现 |
drivers/infiniband/hw/mlx5/wr.c (doorbell), mr.c (MKey/IOMMU) |
| RDMA 丢包/重传机制 |
rxe_resp.c:78 (PSN 乱序检测), rxe_req.c:37 (req_retry), ib_verbs.h:991 (WC status codes) |