华为UVM技术分析:把GPU显存塞进Linux核心MM---GMEM实现简析
本文基于内核mm/gmem*.c、include/linux/gmem.h、include/linux/vm_object.h源码,从统一虚拟地址UVA/SVM视角拆解华为GMEM子系统,同步对比HMM、AMD KFD、Intel Xe现有异构方案,面向内核与GPU驱动研发人员。
一、GMEM核心定位:走出HMM的异构内存新路线
CPU与GPU/NPU异构开发长期存在三大痛点:地址空间割裂、数据频繁拷贝、显存容量不足无法超额使用。
Linux主线成熟方案为HMM:设备镜像CPU页表,设备内存依托ZONE_DEVICE的struct page承载,页面迁移依赖migrate_vma,CPU核心MM永久持有页面所有权。
GMEM提出完全不同的架构思路:将异构设备提升为地址空间一级核心载体。
- 进程新增独立GMEM地址空间
gm_as,通过gm_as_attach挂载GPU/NPU设备; - 跨设备共享VMA绑定专属逻辑页表
vm_object,维护VA到gm_mapping映射,作为页面存储位置的唯一权威; - GPU显存抽象为虚拟NUMA节点h-NUMA,复用内核原生NUMA内存管理、页面迁移、内存统计整套基础设施。
二者底层核心分歧:HMM让设备跟随CPU页表,不改动内核页所有权模型;GMEM新增中间层逻辑页表,CPU与GPU统一受其调度,页面所有权交由中间层管控。
二、核心数据结构:逻辑页表是整个体系中枢
整套代码分为控制面、数据面两条逻辑链路:
- 控制面:
mm->gm_as → gm_context → gm_dev,管理进程挂载设备、显存内存池基础信息; - 数据面:
vma->vm_object → xarray → gm_mapping,记录虚拟地址页面存放位置(主机/显存/无分配)。
1. 逻辑页表 vm_object
仅对带有VM_PEER_SHARED标识的跨设备共享VMA生效,普通进程无额外性能开销,内部以xarray存储虚拟地址与页面映射关系。
structvm_object{spinlock_tlock;structvm_area_struct*vma;structxarray*logical_page_table;// VA -> gm_mappingatomic_tnr_pages;atomic_tref_count;};2. 页状态管理 gm_mapping
定义页面三态:GM_MAPPING_CPU(主机内存)、GM_MAPPING_DEVICE(显存)、GM_MAPPING_NOMAP(未分配),内部互斥锁统一管控缺页、迁移、换出全流程,同一页面同一时间仅存在于一端。
structgm_mapping{unsignedintflag;union{structpage*page;// 主机页structgm_page*gm_page;// 显存页};structgm_dev*dev;structmutexlock;// 迁移状态机串行锁};3. 显存物理页 gm_page(与HMM最大差异)
GMEM不复用内核标准struct page,自研gm_page管理显存物理内存,内置简易反向映射rmap,但仅支持单进程单虚拟地址绑定。
短板显著:无法支撑多进程共享、fork写时复制COW场景。
structgm_page{structlist_headgm_page_list;unsignedlongdev_pfn;unsignedlongdev_dma_addr;unsignedinthnid;// 单槽反向映射,仅记录一组(mm, va)unsignedlongva;structmm_struct*mm;spinlock_trmap_lock;unsignedintflag;atomic_trefcount;};4. GMEM对内核MM的侵入改动(中度改造)
仅针对异构共享内存做最小侵入:
mm_struct新增gm_as成员存储进程GMEM地址空间;vm_area_struct新增vm_obj绑定逻辑页表;- 新增
VM_PEER_SHARED标志位作为GMEM总开关; - 拦截内存缺页、内存卸载钩子,分流至GMEM专属处理函数。
三、驱动接入:gm_mmu统一回调接口
GMEM完成上层策略封装,硬件操作下沉至驱动,驱动只需实现一套gm_mmu回调表即可完成适配,核心接口分为内存分配、映射、DMA拷贝、显存导入四类。peer_map与peer_unmap通过copy标志位统一管理映射创建/销毁与数据DMA迁移,大幅简化驱动开发成本。
structgm_mmu{enumgm_ret(*peer_va_alloc_fixed)(structgm_fault_t*gmf);enumgm_ret(*peer_va_free)(structgm_fault_t*gmf);// 创建设备映射,copy=true同步H2D数据enumgm_ret(*peer_map)(structgm_fault_t*gmf);// 销毁设备映射,copy=true同步D2H数据enumgm_ret(*peer_unmap)(structgm_fault_t*gmf);enumgm_ret(*import_phys_mem)(structmm_struct*mm,inthnid,unsignedlongpage_cnt);enumgm_ret(*peer_hmemcpy)(structgm_memcpy_t*gmc);};四、h-NUMA:显存伪装虚拟NUMA节点
GMEM标志性设计,未占用真实CPU NUMA节点编号的id作为虚拟h-node,每个GPU绑定独立hnode,配套专属内存空闲链表、活跃链表与独立回收线程gm_swapd。
- 显存惰性导入:空闲链表耗尽时才回调驱动批量申请显存,避免一次性占用全部显存;
- 容量管控:
max_memsize限制进程可占用显存上限,原生支持显存超额订阅; - 节点并入进程
mems_allowed,内核内存分配器可识别显存内存池。
对比HMM:HMM依托ZONE_DEVICE复用内核整套页管理;GMEM复用NUMA节点体系,但需要自研页面、反向映射、回收逻辑。
五、核心数据流转:三路径统一互斥锁管控
1. GPU设备缺页(主机→显存迁移)
GPU访问未映射显存内存触发缺页,流程:
锁定gm_mapping→ 判断页面状态 → 主机页则断开CPU页表、DMA搬运数据 → 驱动创建设备映射 → 页面移入显存活跃链表,纳入后台换出候选。
显存页分配三级兜底:空闲链表取用 → 向驱动申请新显存 → 后台换出闲置页面。
2. CPU主机缺页(显存→主机迁移)
CPU访问共享VMA触发缺页,仅支持2MB大页THP:
锁定映射锁,若页面被GPU锁定则直接报错;页面位于显存时,执行peer_unmap将数据DMA回传主机,创建CPU侧PMD大页映射,切换页面状态为主机内存。
3. 显存后台换出swapd
每个虚拟NUMA节点创建独立内核线程,批量驱逐未锁定显存页面回主机内存:
通过gm_page内置单槽rmap反查进程虚拟地址,抢占全局映射锁,执行D2H拷贝,回收显存至空闲链表。
严格锁序保障并发安全:mmap读锁 → rmap锁 → gm_mapping互斥锁 → 节点链表自旋锁。
六、用户态操作接口
mmap(MAP_PEER_SHARED):创建支持CPU/GPU共享的统一虚拟地址内存;hmadvise:GMEM专属内存策略,支持预取至显存、释放显存、页面锁定;hmemcpy:主机与显存间显式DMA拷贝,当前仅实现H2D、D2H,设备间D2D拷贝未落地。
七、GMEM与HMM架构核心对比
| 对比维度 | HMM(KFD/Xe) | GMEM |
|---|---|---|
| 页表权威 | CPU原生页表 | 新增中间逻辑页表vm_object |
| 同步模型 | MMU通知异步失效,弱一致 | 互斥锁强互斥,页面仅存一端 |
| 显存载体 | ZONE_DEVICE+struct page | 虚拟h-NUMA+自研gm_page |
| 页面回收 | 依附内核kswapd,无专属显存回收器 | 单节点独立swapd线程 |
| 反向映射 | 原生内核rmap,支持多进程共享 | 单槽rmap,不支持共享COW |
| 页面粒度 | 4KB细粒度兼容 | 仅2MB THP大页 |
| 内核改动 | 改动极小,主线原生支持 | 中度侵入mm核心结构体 |
Inteldrm_gpusvm与GMEM设计目标高度重合,均希望统一SVM异构逻辑;区别在于drm_gpusvm基于DRM子系统实现,不修改核心内存管理,也是Linux主线更偏好的方案。
八、GMEM现存原生硬伤
- 单槽反向映射:gm_page仅绑定一组进程地址,多进程共享、fork COW完全失效;
- fork语义残缺:子进程复制VMA时新建空白逻辑页表,不继承显存映射关系;
- 仅支持2MB大页,4KB细粒度分配无法使用,小内存场景资源浪费;
- 单地址空间仅支持挂载一台设备,无法多GPU并行共享内存;
- 后台swapd唤醒条件恒假,回收仅在显存耗尽时同步触发,无法主动水位管控;
- 换出仅落主机RAM,不支持磁盘swap,主机内存会成为新瓶颈;
- D2D显存拷贝、硬件上下文切换接口仅预留占位,无完整实现。
九、总结
GMEM是一次激进的内核内存重构尝试,核心两大设计亮点具备参考价值:
- 中心化逻辑页表,统一仲裁CPU/GPU多MMU页面位置;
- 为显存提供内核原生独立回收机制,补齐HMM显存超额订阅短板。
但整套实现当前仅为基础骨架,多处核心能力未落地,且对内核MM存在中度侵入,与上游“优先在DRM层实现异构内存”的路线冲突。短期难以完整合入主线,但其分层管理、显存独立回收的设计思路,大概率会被drm_gpusvm等现有主线方案吸收借鉴。
感兴趣的技术读友,可以去看下源码:https://atomgit.com/openeuler/kernel。