PCIe BAR 深入解析:从设备地址窗口到 DMA 与 GPUDirect Storage

在操作系统驱动、高性能网卡、NVMe SSD 和 GPU 系统中,经常会同时看到 BAR、MMIO、DMA、IOMMU、peer-to-peer DMA、GPUDirect RDMA 和 GPUDirect Storage 等概念。这些名词都与“设备怎样通过 PCIe 交换控制信息和数据”有关,但它们处在不同层次。 最简洁的理解是: BAR:让 CPU 能够定位并访问 PCIe 设备中的寄存器或显存窗口 DMA:让 PCIe 设备能够主动读取或写入系统内存 P2P DMA:让一个 PCIe 设备直接访问另一个 PCIe 设备暴露的地址空间 GDS:利用 DMA、GPU 内存映射和驱动协作,让存储数据尽量直接进入 GPU 显存 本文从 PCIe 地址空间和事务模型出发,解释 BAR 是如何分配和映射的,驱动为什么通过 BAR 下发命令,DMA 地址为什么不能简单等同于物理地址,以及 GPUDirect Storage 如何把这些机制组合成一条高性能数据路径。 一、先建立 PCIe 系统视图 一个典型服务器的 PCIe 拓扑如下: CPU │ Memory Controller │ Host RAM │ Root Complex ┌──────┴──────┐ │ │ PCIe Switch PCIe Endpoint ┌────┴────┐ GPU │ │ NVMe SSD NIC CPU 和内存构成主机侧。Root Complex 把 CPU/内存系统连接到 PCIe fabric。NVMe、网卡和 GPU 通常是 Endpoint。PCIe Switch 用于扩展端口和转发事务。 ...

2026年8月21日 · 10 分钟 · Hellokitty

nvidia-fs 源码阅读(三):文件 I/O、批处理、完成路径与诊断

前两篇分别介绍了 nvidia-fs 的模块结构,以及 GPU virtual address 到 peer DMA address 的映射。本文沿一次 cuFileRead 对应的内核路径,分析文件 I/O 如何提交、完成和清理,并介绍 batch、稀疏文件、RDMA 与 /proc 诊断接口。 阅读版本: commit: 328d1d8cce1175c013720985c30e123e9a35242c GDS_VERSION: 2.29.4 一、I/O 入口 nvfs_ioctl() 接收: NVFS_IOCTL_READ NVFS_IOCTL_WRITE NVFS_IOCTL_BATCH_IO 单次读写共用两阶段结构: nvfs_io_init(op, ioargs) -> 验证并构造 nvfs_io nvfs_io_start_op(nvfsio) -> 向目标文件提交真正 I/O 分成两步的意义在于:参数验证、对象引用和资源分配都应在进入异步 I/O 前完成。一旦请求提交,完成回调可能很快发生,初始化不完整会造成竞态。 二、nvfs_io_init 做了什么 nvfs_io_init() 是用户参数到内核 I/O 对象的转换层,主要工作包括: 根据文件描述符取得目标 struct file; 检查读写权限; 查找已经注册的 GPU buffer/mgroup; 验证 GPU buffer offset、文件 offset 和长度; 建立影子 page 对应的 iov 或迭代器; 初始化 kiocb、完成函数和统计字段; 为同步、异步和特殊文件系统路径设置标志。 源码还会检查文件系统类型、direct I/O 条件和文件权限。写操作比读操作更复杂,因为它可能涉及文件扩展、页缓存一致性及磁盘空间预分配。 ...

2026年8月21日 · 3 分钟 · Hellokitty

nvidia-fs 源码阅读(二):GPU 内存注册、影子页与 DMA 映射

上一篇从 nvfs_init()、字符设备和 ioctl 看到了 nvidia-fs 的外部形态。本文进入 GDS direct path 的核心:怎样把用户分配的 GPU virtual address 变成存储设备能够 DMA 的地址,同时又让 Linux 文件 I/O 栈能够携带它。 阅读版本仍为 NVIDIA gds-nvidia-fs: commit: 328d1d8cce1175c013720985c30e123e9a35242c GDS_VERSION: 2.29.4 一、问题本质 普通块 I/O 最终围绕 bio_vec、struct page、scatterlist 和 DMA mapping 展开。但 cudaMalloc 得到的是 GPU 虚拟地址,对 Linux 页缓存和块层来说,它并不是普通 CPU 内存页。 驱动需要解决三次“翻译”: GPU virtual address -> NVIDIA P2P page table 中的 GPU physical pages -> Linux I/O 栈可携带的影子 struct page -> 某个存储 PCIe 设备可使用的 DMA address 三个地址空间不能混为一谈: ...

2026年8月21日 · 4 分钟 · Hellokitty

nvidia-fs 源码阅读(一):模块初始化、设备接口与整体架构

nvidia-fs 是 NVIDIA GPUDirect Storage(GDS)的 Linux 内核模块。它不是一种文件系统,而是连接 libcufile、NVIDIA GPU 驱动、Linux 文件 I/O 和支持 GPUDirect 的存储驱动的一层内核协调组件。 这组文章阅读 NVIDIA 官方 gds-nvidia-fs 仓库,采用的版本为: commit: 328d1d8cce1175c013720985c30e123e9a35242c GDS_VERSION: 2.29.4 commit date: 2026-06-01 系列分为三篇: 模块初始化、设备接口与整体架构; GPU 内存注册、页表与 DMA 映射; 文件 I/O、批量请求、完成路径与可观测性。 本文先回答一个问题:加载 nvidia_fs.ko 后,内核里究竟多了什么? 一、源码目录 核心代码都位于 src/: 文件 职责 nvfs-core.c/.h 字符设备、ioctl、GPU 内存注册和文件 I/O 主流程 nvfs-mod.c 与 NVMe、RDMA 等外部模块动态注册 DMA 回调 nvfs-mmap.c/.h GPU 页对应的影子 struct page 和 mmap 管理 nvfs-dma.c/.h block request 到 GPU DMA 地址的映射 nvfs-batch.c/.h 批量 I/O 提交与完成 nvfs-rdma.c/.h RDMA 注册信息管理 nvfs-pci.c/.h GPU 与存储设备的 PCIe 拓扑、距离和亲和性 nvfs-proc.c、nvfs-stat.c /proc 配置、统计和诊断接口 nvfs-kernel-interface.c 不同 Linux 内核版本的兼容封装 主线集中在 nvfs-core.c,但真正的数据直达能力是多个文件共同完成的。 ...

2026年8月21日 · 3 分钟 · Hellokitty

GDS、GIDS 与 uGDS 有什么区别:从数据直达、GPU 发起到用户态 NVMe

GDS、GIDS 和 uGDS 的名字非常接近,也都在讨论 GPU 与存储之间的数据通路,因此很容易被理解成同一项技术的三个版本。 实际上,它们解决的是三个不同层面的问题: GDS 关注数据路径:怎样让存储数据不经 CPU 内存中转,直接进入 GPU 显存; GIDS 关注控制路径:怎样让 GPU Kernel 自己决定并发起存储请求,减少 GPU 与 CPU 往返; uGDS 关注软件栈:怎样让 CPU 在用户态直接管理 NVMe 队列,绕过内核 NVMe 驱动与文件系统。 可以先用一句话概括: GDS 是“数据直达 GPU”,GIDS 是“GPU 主动要数据”,uGDS 是“用户态 CPU 直接驱动 NVMe 把数据送到 GPU”。 本文从请求由谁发起、数据经过哪里、NVMe 命令由谁构造、是否保留文件系统语义等维度,分析三者的区别与联系。更深入的原理可以继续阅读本系列的三篇文章: NVIDIA GPUDirect Storage(GDS)详解:让存储数据绕过 CPU 直达 GPU NVIDIA GPU-Initiated Data Storage(GIDS)详解:让 GPU Kernel 主动访问存储 uGDS 原理解析:在用户态打通 NVMe SSD 与 GPU 显存 一、先区分数据路径与控制路径 理解这三项技术的关键,是不要把“数据经过哪里”和“谁发起 I/O”混为一谈。 数据路径 数据路径描述有效载荷如何移动。例如: 传统路径:SSD → CPU 内存 → GPU 显存 直接路径:SSD ──────────→ GPU 显存 GDS、GIDS 和 uGDS 都希望建立 SSD 与 GPU 显存之间的 DMA 路径,减少 CPU bounce buffer。但“数据不经过 CPU 内存”并不代表“CPU 没有参与”。 ...

2026年8月20日 · 5 分钟 · Hellokitty

NVIDIA GPUDirect Storage(GDS)详解:让存储数据绕过 CPU 直达 GPU

在 AI 训练、科学计算和数据分析系统中,GPU 的算力越来越强,但数据从存储设备进入 GPU 的路径却可能成为瓶颈。传统 I/O 通常要先把数据读入 CPU 内存,再复制到 GPU 显存,不仅增加内存带宽消耗,还让 CPU 承担大量数据搬运工作。 NVIDIA GPUDirect Storage,简称 GDS,解决的正是这个问题:它在存储设备与 GPU 显存之间建立更直接的数据路径,使应用能够通过 cuFile API 将文件数据读入 GPU 缓冲区,减少 CPU bounce buffer 和不必要的数据复制。 本文介绍 GDS 的工作原理、软件栈、典型使用方式、适用场景以及它与 GPU-Initiated Data Storage(GIDS)的关系。 一、传统 GPU I/O 为什么效率不高 传统文件读取到 GPU 的路径大致如下: NVMe / 文件系统 │ ▼ CPU 内存缓冲区 │ cudaMemcpy ▼ GPU 显存 应用通常先调用 read、pread 或异步 I/O 接口,把文件内容读入主机内存,然后再调用 CUDA memcpy 将数据复制到 GPU。 这条路径存在几个问题: 同一份数据先经过 CPU 内存,再进入 GPU 显存; 存储流量和 GPU 传输流量竞争 CPU 内存带宽; CPU 需要提交、管理和完成数据搬运; 大规模 GPU 系统中,CPU 和内存通道容易成为共享瓶颈; 应用需要维护主机端 staging buffer,并处理双重缓冲。 当 GPU 计算越来越快、单机挂载更多 NVMe 或更高速的并行文件系统后,这些额外开销会更加明显。 ...

2026年8月19日 · 3 分钟 · Hellokitty