华为UVM技术分析:把GPU显存塞进Linux核心MM---GMEM实现简析

本文基于内核mm/gmem*.cinclude/linux/gmem.hinclude/linux/vm_object.h源码,从统一虚拟地址UVA/SVM视角拆解华为GMEM子系统,同步对比HMM、AMD KFD、Intel Xe现有异构方案,面向内核与GPU驱动研发人员。

一、GMEM核心定位:走出HMM的异构内存新路线

CPU与GPU/NPU异构开发长期存在三大痛点:地址空间割裂、数据频繁拷贝、显存容量不足无法超额使用。

Linux主线成熟方案为HMM :设备镜像CPU页表,设备内存依托ZONE_DEVICEstruct page承载,页面迁移依赖migrate_vma,CPU核心MM永久持有页面所有权。

GMEM提出完全不同的架构思路:将异构设备提升为地址空间一级核心载体

  1. 进程新增独立GMEM地址空间gm_as,通过gm_as_attach挂载GPU/NPU设备;
  2. 跨设备共享VMA绑定专属逻辑页表 vm_object,维护VA到gm_mapping映射,作为页面存储位置的唯一权威;
  3. 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存储虚拟地址与页面映射关系。

c 复制代码
struct vm_object {
	spinlock_t lock;
	struct vm_area_struct *vma;
	struct xarray *logical_page_table; // VA -> gm_mapping
	atomic_t nr_pages;
	atomic_t ref_count;
};

2. 页状态管理 gm_mapping

定义页面三态:GM_MAPPING_CPU(主机内存)、GM_MAPPING_DEVICE(显存)、GM_MAPPING_NOMAP(未分配),内部互斥锁统一管控缺页、迁移、换出全流程,同一页面同一时间仅存在于一端。

c 复制代码
struct gm_mapping {
	unsigned int flag;
	union {
		struct page *page;        // 主机页
		struct gm_page *gm_page;  // 显存页
	};
	struct gm_dev *dev;
	struct mutex lock; // 迁移状态机串行锁
};

3. 显存物理页 gm_page(与HMM最大差异)

GMEM不复用内核标准struct page,自研gm_page管理显存物理内存,内置简易反向映射rmap,但仅支持单进程单虚拟地址绑定

短板显著:无法支撑多进程共享、fork写时复制COW场景。

c 复制代码
struct gm_page {
	struct list_head gm_page_list;
	unsigned long dev_pfn;
	unsigned long dev_dma_addr;
	unsigned int hnid;

	// 单槽反向映射,仅记录一组(mm, va)
	unsigned long va;
	struct mm_struct *mm;
	spinlock_t rmap_lock;

	unsigned int flag;
	atomic_t refcount;
};

4. GMEM对内核MM的侵入改动(中度改造)

仅针对异构共享内存做最小侵入:

  1. mm_struct新增gm_as成员存储进程GMEM地址空间;
  2. vm_area_struct新增vm_obj绑定逻辑页表;
  3. 新增VM_PEER_SHARED标志位作为GMEM总开关;
  4. 拦截内存缺页、内存卸载钩子,分流至GMEM专属处理函数。

三、驱动接入:gm_mmu统一回调接口

GMEM完成上层策略封装,硬件操作下沉至驱动,驱动只需实现一套gm_mmu回调表即可完成适配,核心接口分为内存分配、映射、DMA拷贝、显存导入四类。

peer_mappeer_unmap通过copy标志位统一管理映射创建/销毁与数据DMA迁移,大幅简化驱动开发成本。

c 复制代码
struct gm_mmu {
	enum gm_ret (*peer_va_alloc_fixed)(struct gm_fault_t *gmf);
	enum gm_ret (*peer_va_free)(struct gm_fault_t *gmf);
	// 创建设备映射,copy=true同步H2D数据
	enum gm_ret (*peer_map)(struct gm_fault_t *gmf);
	// 销毁设备映射,copy=true同步D2H数据
	enum gm_ret (*peer_unmap)(struct gm_fault_t *gmf);
	enum gm_ret (*import_phys_mem)(struct mm_struct *mm, int hnid, unsigned long page_cnt);
	enum gm_ret (*peer_hmemcpy)(struct gm_memcpy_t *gmc);
};

四、h-NUMA:显存伪装虚拟NUMA节点

GMEM标志性设计,未占用真实CPU NUMA节点编号的id作为虚拟h-node,每个GPU绑定独立hnode,配套专属内存空闲链表、活跃链表与独立回收线程gm_swapd

  1. 显存惰性导入:空闲链表耗尽时才回调驱动批量申请显存,避免一次性占用全部显存;
  2. 容量管控:max_memsize限制进程可占用显存上限,原生支持显存超额订阅;
  3. 节点并入进程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互斥锁 → 节点链表自旋锁。

六、用户态操作接口

  1. mmap(MAP_PEER_SHARED):创建支持CPU/GPU共享的统一虚拟地址内存;
  2. hmadvise:GMEM专属内存策略,支持预取至显存、释放显存、页面锁定;
  3. 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核心结构体

Intel drm_gpusvm与GMEM设计目标高度重合,均希望统一SVM异构逻辑;区别在于drm_gpusvm基于DRM子系统实现,不修改核心内存管理,也是Linux主线更偏好的方案。

八、GMEM现存原生硬伤

  1. 单槽反向映射:gm_page仅绑定一组进程地址,多进程共享、fork COW完全失效;
  2. fork语义残缺:子进程复制VMA时新建空白逻辑页表,不继承显存映射关系;
  3. 仅支持2MB大页,4KB细粒度分配无法使用,小内存场景资源浪费;
  4. 单地址空间仅支持挂载一台设备,无法多GPU并行共享内存;
  5. 后台swapd唤醒条件恒假,回收仅在显存耗尽时同步触发,无法主动水位管控;
  6. 换出仅落主机RAM,不支持磁盘swap,主机内存会成为新瓶颈;
  7. D2D显存拷贝、硬件上下文切换接口仅预留占位,无完整实现。

九、总结

GMEM是一次激进的内核内存重构尝试,核心两大设计亮点具备参考价值:

  1. 中心化逻辑页表,统一仲裁CPU/GPU多MMU页面位置;
  2. 为显存提供内核原生独立回收机制,补齐HMM显存超额订阅短板。

但整套实现当前仅为基础骨架,多处核心能力未落地,且对内核MM存在中度侵入,与上游"优先在DRM层实现异构内存"的路线冲突。短期难以完整合入主线,但其分层管理、显存独立回收的设计思路,大概率会被drm_gpusvm等现有主线方案吸收借鉴。


感兴趣的技术读友,可以去看下源码:https://atomgit.com/openeuler/kernel