ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

第三章:GEM分析 3.4 GPU 视图:从 GPU 寻址硬件的演进看 gem_object 被 GPU 访问

第三章:GEM分析 3.4 GPU 视图:从 GPU 寻址硬件的演进看 gem_object 被 GPU 访问

上一节(3.3 CPU 视图)讨论了 CPU 访问 BO 的路径:内核通过drm_vma_offset_manager为drm_gem_object分配伪 offset,用户态以该 offset 调用mmap,并在缺页处理中将实际物理页映射至进程页表。

本节讨论 GPU 侧的对应问题:GPU 命令中引用的 BO 地址由何而来。

该问题的答案并非一成不变。过去二十余年间,GPU 寻址硬件经历了数次结构性演进,每一次演进都重新定义了 BO 被 GPU 访问的软件接口,并直接推动了 DRM 中一系列核心机制的形成:drm_mm、BO-list 与 Relocation、基于dma_resv的隐式同步、drm_gpuvm以及 SVM。

本节不展开实现细节,而是以硬件演进为主线,梳理 GPU 视图下的四代寻址模型(如下图),为 3.4.1~3.4.3 及第十、十一章提供整体框架。


1、GPU 视图的三个基本问题

无论硬件代际与驱动实现如何,GPU 访问 BO 均需解决以下三个问题:

  • 谁分配地址: BO 的 GPU 地址由内核分配,还是由用户态分配
  • 何时生效:地址在何时写入 GPU 命令:分配时、提交时,或在提交前预先绑定
  • 如何驻留| GPU 执行期间如何保证 BO 不被换出或迁移,并保证访问顺序正确

后续各代模型均可视为对上述三个问题的不同解答,下文以此为维度逐代分析。


2、第一代:物理直连

硬件特征:GPU 不具备独立的 MMU,直接使用总线物理地址(或经 IOMMU 映射的 DMA 地址)访问内存。早期显卡以及当前多数 SoC 显示控制器、简单 2D 引擎属于此类。

软件契约:由于 GPU 不进行地址翻译,BO 必须物理连续。驱动从 CMA 或预留内存中分配连续区域,并将所得 DMA 地址直接写入寄存器或命令。

DRM 中与之对应的是最简单的后备实现drm_gem_dma_object(4.2 节),其核心字段仅有一个:

structdrm_gem_dma_object{structdrm_gem_objectbase;dma_addr_tdma_addr;/* 设备直接使用的 DMA 地址 */...};

该模型下三个问题的解答为:地址由内核物理分配器决定;分配即生效;BO 常驻且不迁移。

其局限同样明显:大块连续内存分配困难,且 GPU 可访问任意物理地址,缺乏进程间隔离。


3、第二代:全局 GART / GTT 与 Relocation

3.1 硬件:全局地址翻译表

为解除物理连续性约束,硬件引入了全局地址翻译表,将分散于系统内存中的物理页映射为 GPU 地址空间中的一段连续区间(即孔径,aperture)。

  • 1997 年,AGP 总线在芯片组中引入GART(Graphics Address Remapping Table);
  • 进入 PCIe 时代后,该翻译表集成至 GPU 内部,如 radeon 的 PCIE GART 与 Intel 的 GGTT;
  • Intel 将其称为GTT(Graphics Translation Table),DRM 中「GTT 内存域」的命名即源于此。

对于独立显卡,VRAM 通过 GPU 本地地址直接访问;系统内存则须先在 GART 中分配区间并写入表项,方可被 GPU 访问。

aperture 地址空间由内核通过通用区间分配器drm_mm管理。在当前内核中,amdgpu 的 GTT 管理器仍采用ttm_resource_manager与drm_mm组合的实现:

structamdgpu_gtt_mgr{structttm_resource_managermanager;structdrm_mmmm;spinlock_tlock;};

3.2 软件契约:Relocation

全局 GART 引入了一项关键约束:所有进程共享同一 GPU 地址空间,地址只能由内核统一分配。BO 在 GART 中的位置及其是否会被迁移,对用户态均不可见。

然而,用户态在构造命令时必须引用 BO 地址。为此形成了Relocation(重定位)契约:

  1. 用户态在命令中预留地址占位,并附带重定位表,每个表项描述「命令缓冲区偏移 N 处应填入 handle 为 H 的 BO 地址加 delta」;
  2. 同时提交BO-list,列出本次提交引用的全部 BO handle;
  3. 内核在提交时将各 BO 放置到 GART 中(必要时先行换入),确定最终地址后回填至命令缓冲区。

i915 的execbuffer与 radeon 的 CS 均采用该模型。

在此模型中,GEM core 负责将用户态 handle 数组解析为drm_gem_object指针数组,对应接口为drm_gem_objects_lookup():

intdrm_gem_objects_lookup(structdrm_file*filp,void__user*bo_handles,intcount,structdrm_gem_object***objs_out);

3.3 同步:隐式同步

由于内核在每次提交时均持有完整的 BO-list,同步亦可由内核统一完成:每个 BO 关联一个dma_resv,提交前等待其中已有的 fence,提交后将本次作业的 fence 添加至其中。用户态无需显式声明依赖关系,此即隐式同步(7.3 节)。

3.4 局限

Relocation 模型实现简单且安全性可控,但其开销随 GPU 负载增长而显著上升:

  • 逐次回填:每次提交均需处理重定位表,draw call 较多时表项可达数千乃至上万,CPU 开销显著;
  • 全局地址空间:进程间缺乏硬件隔离,内核须校验命令内容,以防止越权访问其他进程的 BO;
  • 地址不稳定:BO 一旦迁移,后续提交须重新回填。

该模型下三个问题的解答为:地址由内核(drm_mm管理 GTT)分配;提交时回填生效;每次提交通过校验 BO-list 保证驻留。3.4.2 将对该模型展开分析。


4、第三代:每进程 GPU VM

4.1 硬件:每上下文独立的 GPU 页表

关键转变源于硬件:GPU 具备了与 CPU 类似的多级页表 MMU,并支持按上下文切换页表。

  • AMD 自 Cayman / SI 起引入VMID,为每个进程提供独立的 GPU 虚拟地址空间(radeon 于 2012 年支持 GPU VM);
  • Intel 自 Gen6 的 aliasing PPGTT 起步,至 Gen8(2014)实现full PPGTT;
  • NVIDIA 自 NV50 起支持 GPU VM。

4.2 软件契约:用户态管理 VA

在每进程地址空间下,GPU 地址无需全局协调,可由用户态自行分配:用户态选定 VA 区间,经绑定接口请求内核将 BO 映射至该区间;此后命令直接引用该 VA,提交时无需回填。

主流驱动的绑定接口如下:

驱动绑定方式uAPI
i915softpin:用户态指定 BO 的 GPU 地址EXEC_OBJECT_PINNED(v4.5)
amdgpu独立的 VA ioctlDRM_AMDGPU_GEM_VA
nouveauVM_BINDDRM_NOUVEAU_VM_BIND
Xe仅支持 VM_BIND,不支持 relocationDRM_XE_VM_BIND

Relocation 已逐步退出主流。i915 在 TGL 之后的平台上明确拒绝 relocation:

/* Relocations are disallowed for all platforms after TGL-LP. This * also covers all platforms with local memory. */if(entry->relocation_count&&GRAPHICS_VER(eb->i915)>=12&&!IS_TIGERLAKE(eb->i915))return-EINVAL;

需要指出,各代模型之间并非简单的替代关系。amdgpu 虽通过 VA ioctl 管理地址,CS 时仍需经AMDGPU_CHUNK_ID_BO_HANDLES提交 BO-list,用于驻留校验与 fence 记账(第九章);GART 亦继续存在,转为内核内部使用的映射窗口。

4.3 映射反向索引

地址分配权下放至用户态后,内核需处理新的问题:同一 BO 可被映射至多个 VM 及多个 VA 区间。当 TTM 驱逐或迁移该 BO 时,内核必须定位其全部映射并逐一失效相应页表项,迁移完成后再行重建。

这要求维护「BO → 映射」的反向索引。早期由各驱动自行实现(如 amdgpu 的amdgpu_bo_va);自 v6.6 起,DRM 提供通用框架drm_gpuvm,并在drm_gem_object中引入专用字段:

structdrm_gem_object{...struct{structlist_headlist;/* 所有指向本 BO 的 GPUVM 映射 */structmutexlock;}gpuva;};

该模型下三个问题的解答为:地址由用户态分配;绑定后生效,命令直接引用 VA;驱逐时失效映射,迁移后重建。


5、第四代:统一寻址与可恢复缺页

第三代模型仍有一项前提:GPU 可访问的内存必须先以 BO 的形式存在,并经显式绑定。第四代硬件能力消除了这一前提:

  • ATS / PASID:GPU 经 IOMMU 直接使用 CPU 进程页表(2015 年 KFD 在 Kaveri APU 上基于 IOMMUv2 实现);
  • 可恢复缺页:GPU 访问未映射地址时不再触发致命错误,而是暂停执行并上报缺页,待驱动建立映射后重试(如 AMD GFX9+ 的 retry fault、Intel Xe);
  • HMM(v4.14):对于不支持 ATS 的独立显卡,由驱动将 CPU 页表镜像至 GPU 页表,并通过mmu_notifier维持一致性。

由此,GPU 可直接使用malloc返回的 CPU 指针:GPU 地址与 CPU 进程 VA 一致,映射在 GPU 缺页时建立,数据按需在系统内存与显存之间迁移。KFD SVM(v5.14,2021 年)与通用框架drm_gpusvm(v6.15,2025 年)先后合入主线。

在该模型下,GPU 访问的对象不再限于drm_gem_object,已超出 GEM 的范畴。本节仅作概述,详细内容见第十一章 SVM。


6、各代模型对比

谁分配地址何时生效如何驻留关键机制
第一代 物理直连内核物理分配器分配即确定常驻,不迁移CMA、drm_gem_dma_object
第二代 全局 GART内核(drm_mm管 GTT)提交时内核回填每次提交校验 BO-listRelocation、隐式同步
第三代 每进程 VM用户态提前绑定,命令直接用 VA驱逐时失效并重建映射VM_BIND、drm_gpuvm
第四代 统一寻址与 CPU 进程 VA 一致GPU 缺页时建立按需缺页与迁移HMM、drm_gpusvm

由此可归纳出演进的主线:地址分配权由内核逐步下放至用户态,最终与 CPU 地址空间统一;地址生效时机由「提交时」前移至「绑定时」,进而后移至「访问时」。


7、GEM core 在 GPU 视图中的职责

GEM core 并未定义通用的 GPU 绑定接口,这是有意的设计选择:绑定方式随硬件代际差异显著,从 execbuffer、CS 到 GEM_VA、VM_BIND,均以驱动私有 ioctl 实现。GEM core 仅提供三项与硬件代际无关的基础设施:

基础设施位置用途详见
handle → object 查找drm_gem_object_lookup()/drm_gem_objects_lookup()BO-list、绑定 ioctl 的参数解析3.4.2
resvdrm_gem_object.resv隐式同步、驻留期间的 fence 记账第七章
gpuvadrm_gem_object.gpuvaBO → GPU 映射的反向索引3.4.3、第十章

GPU 地址空间本身的分配同样不由 GEM 负责,驱动根据需要选用相应的通用分配器:

  • GTT aperture:drm_mm(3.4.1);
  • VRAM:drm_buddy(5.7);
  • GPU 虚拟地址空间:drm_gpuvm(第十章)。

这与 3.3 节形成对照:在 CPU 视图中,GEM core 提供完整的 offset 与 mmap 机制;在 GPU 视图中,GEM core 仅提供基础设施,具体机制由驱动及上层框架实现。


8、本节结构

GPU 视图的后续三节按硬件演进顺序展开:

  • 3.4.1 drm_mm 管理的 GTT 空间在 amdgpu bo 创建中的应用:第二代模型的地址空间管理,分析drm_mm对 GART 孔径的划分;
  • 3.4.2 gem_object 如何被 GPU 访问(上):旧模型 BO-list + Relocation + 隐式同步:第二代模型的软件契约;
  • 3.4.3 gem_object 如何被 GPU 访问(下):gpuva 字段与映射反向索引:第三代模型在 GEM 侧的数据结构。

第三代模型的完整实现见第十章 GPUVM,第四代模型见第十一章 SVM。BO 跨设备访问的相关内容见3.5 设备间视图。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进