1. 定位与层次关系
两者不是替代关系,而是层次关系 :drm_pagemap 建立在 HMM 基础设施之上。
┌─────────────────────────────────────────────────┐
│ DRM GPU SVM (drm_gpusvm) │ ← 用 hmm_range_fault() + mmu_interval_notifier
│ (GPU 页表填充、地址空间追踪) │
├─────────────────────────────────────────────────┤
│ drm_pagemap │ ← 用 migrate_vma_* / migrate_device_*
│ (设备内存分配、迁移抽象、DMA 映射) │
├─────────────────────────────────────────────────┤
│ HMM 核心 (mm/hmm.c) │
│ hmm_range_fault(), mmu_interval_notifier │
├─────────────────────────────────────────────────┤
│ migrate_vma / dev_pagemap / ZONE_DEVICE │ ← 内核基础设施
└─────────────────────────────────────────────────┘
2. 功能对比
| 维度 | HMM (include/linux/hmm.h) |
drm_pagemap (include/drm/drm_pagemap.h) |
|---|---|---|
| 所属子系统 | mm(内存管理核心) | drm(GPU 驱动框架) |
| 主要功能 | 将 CPU 页表状态镜像到设备 | 管理设备内存分配与迁移 |
| 核心 API | hmm_range_fault() |
drm_pagemap_migrate_to_devmem() / drm_pagemap_evict_to_ram() |
| 谁调用 | 设备驱动(直接或通过 drm_gpusvm) | drm_gpusvm 或 GPU 驱动 |
| CPU 页表追踪 | mmu_interval_notifier 回调 |
不提供,由上层 drm_gpusvm 负责 |
| 内存迁移 | 提供底层 migrate_vma_* 框架 |
封装 migrate_vma_*,加入 DMA 映射和驱动回调 |
| 设备内存管理 | dev_pagemap + ZONE_DEVICE(原语) |
封装 dev_pagemap,增加引用计数、zdd、cache/shrinker |
| P2P 支持 | 无直接 P2P | drm_pagemap_ops.device_map/unmap + interconnect protocol |
| 抽象层级 | 通用、硬件无关 | DRM/GPU 特化 |
3. 关键设计差异
3.1 页表镜像 vs 内存放置
-
HMM 解决的核心问题是页表镜像 :设备如何知道进程虚拟地址 X 对应哪个物理页,以及该映射何时失效。
hmm_range_fault()将 CPU PTE 批量翻译为设备可用的 PFN 数组。 -
drm_pagemap 解决的核心问题是内存放置:数据应该存放在系统内存还是设备内存,以及如何安全地在两者之间搬移数据。
3.2 对迁移的封装程度
HMM 的 migrate_vma_* 是裸 API,驱动需要自己处理:
c
// test_hmm.c 中的典型模式(约 50 行代码)
migrate_vma_setup(&args);
// 手动分配目标页面
// 手动拷贝数据
// 手动设置 zone_device_data
migrate_vma_pages(&args);
migrate_vma_finalize(&args);
drm_pagemap 将这些封装成单次调用,内部处理:
c
// GPU 驱动只需提供 ops 回调
drm_pagemap_migrate_to_devmem(devmem_allocation, mm, start, end, &mdetails);
封装的内容包括:
- 自动管理 zdd(zone device data)的引用计数
- 自动处理 DMA 映射/解映射
- 自动处理 P2P 迁移(设备间直接搬移)
- 内置 timeslice 防止迁移活锁
- 内置 retry 机制
3.3 设备间互操作
-
HMM :
dev_private_owner仅区分"是不是我的页面",跨设备访问时必须先迁回系统再迁到另一设备。 -
drm_pagemap :通过
drm_interconnect_protocol和device_map/unmap回调支持设备间直接 DMA (P2P),并通过source_peer_migrates标志控制谁负责跨设备拷贝。
3.4 生命周期管理
-
HMM:无内存管理策略,页面何时迁移完全由驱动决定。
-
drm_pagemap:
timeslice_expiration:页面在设备上的最低驻留时间,防止 CPU fault 立即迁回drm_pagemap_cache+ shrinker:内存压力时自动回收设备内存- 引用计数(
kref)管理drm_pagemap生命周期 devmem_allocation的detachedcompletion 处理设备解绑
4. 核心 API 对比
HMM 核心 API
| API | 作用 |
|---|---|
hmm_range_fault() |
将 CPU 页表翻译为设备 PFN 数组(可选触发缺页) |
mmu_interval_notifier |
监听 CPU 页表变化,通知设备失效映射 |
migrate_vma_setup/pages/finalize() |
VMA 级别页面迁移 |
migrate_device_range/pages/finalize() |
设备级别页面迁移(不需 mmap lock) |
make_device_exclusive() |
页面独占访问 |
drm_pagemap 核心 API
| API | 作用 |
|---|---|
drm_pagemap_migrate_to_devmem() |
将系统/一致性内存迁移到设备内存 |
drm_pagemap_evict_to_ram() |
将设备内存驱逐回系统内存(不需 mmap lock) |
drm_pagemap_populate_mm() |
用设备内存填充 mm 地址范围 |
drm_pagemap_ops.device_map/unmap |
P2P 设备间 DMA 映射 |
drm_pagemap_devmem_ops.copy_to_devmem/copy_to_ram |
驱动实现的数据拷贝回调 |
5. 驱动接入方式对比
使用裸 HMM(如 test_hmm.c)
驱动需要自行实现:
- 注册
mmu_interval_notifier,处理失效回调 - 调用
hmm_range_fault()获取 PFN,处理重试逻辑 - 分配 ZONE_DEVICE 页面,管理空闲链表
- 实现
dev_pagemap_ops(folio_free, migrate_to_ram) - 手动调用
migrate_vma_*系列 API - 手动管理
zone_device_data指针
使用 drm_pagemap(如 Intel Xe 驱动)
驱动只需提供:
drm_pagemap_devmem_ops:populate_devmem_pfn、copy_to_devmem、copy_to_ramdrm_pagemap_ops:device_map、device_unmap(可选,P2P 用)- 调用
drm_pagemap_migrate_to_devmem()/drm_pagemap_evict_to_ram()
其余的 mmu_notifier、迁移状态机、DMA 映射、引用计数等由框架处理。
6. 总结
| HMM | drm_pagemap | |
|---|---|---|
| 角色 | 内核基础设施("砖块") | GPU 驱动框架("房子") |
| 设计哲学 | 通用、最小化、机制不策略 | GPU 专用、策略丰富、生产级 |
| 直接使用者 | 任意设备驱动、test_hmm | DRM GPU 驱动(Xe 等) |
| 关联 | drm_pagemap 的底层依赖 | HMM 的上层消费者 |
HMM 是内核提供的通用异构内存原语,drm_pagemap 是 DRM 子系统基于 HMM 构建的 GPU 设备内存管理框架。 test_hmm.c 直接使用裸 HMM API 进行测试,而真实 GPU 驱动(如 Intel Xe)通过 drm_pagemap 间接使用 HMM。