来源:X.Org Developer's Conference 2026(Toronto, Canada)
演讲标题:《From KFD to DRM SVM: Toward a Unified Memory Model in DRM》
演讲人:Ray Huang(AMD)
整理日期:2026-10-08
摘要
本报告基于 AMD 团队在 XDC 2026 的演讲,介绍将 AMD 的共享虚拟内存(SVM)能力从专有的 KFD SVM 迁移到通用的 DRM GPUSVM 框架的工作。该工作在 drm_gpusvm(XDC 2025 于 Intel Xe 上引入)基础上演进,并在 amdgpu 上落地,目标是建立一个所有 DRM 驱动都能复用、与 Intel Xe 共享演进的统一内存模型。报告涵盖动机、架构、缺页(fault)路径、MMU notifier 与垃圾回收、VRAM 迁移、XNACK-on/off 两种模型、锁重构、补丁系列状态与路线图。
1. 动机:为何不直接扩展 KFD SVM?
KFD SVM 在功能上已完备并投入生产(ROCm 的 hipMallocManaged 目前即运行其上),但存在结构性(非 bug)的限制:
| # | 限制 | 问题本质 |
|---|---|---|
| 1 | 同步建立在 mmap 锁之上 |
驱动行为与核心 MM 耦合,每次改动都要重新论证 mmap 锁顺序,易触发 use-after-free |
| 2 | 每个 range 一个 MMU notifier | range 越多,CPU 侧每次 munmap 越慢 |
| 3 | 固定的 per-GPU 开销、脆弱的 range 拆分 | 每个 range 固定 500+ 字节(MAX_GPU_INSTANCE 64),拆分跨 7 个互锁函数 |
| 4 | 位于 DRM 框架之外 | 仅 /dev/kfd 可用,渲染节点(Vulkan/GL/用户队列)不可用,且各厂商重复实现 HMM 管线 |
notifier 粒度对比:
| 指标 | KFD SVM | DRM GPUSVM |
|---|---|---|
| 粒度 | per range | 512 MB |
| 2 GB @ 2 MB 时的 notifier 数 | ~1000 | 4 |
结论:KFD SVM 不是失败的设计,这些是结构性边界而非缺陷。新方案是增量而非替代,两者共存。
2. DRM SVM:面向所有驱动的统一抽象
在 drm_gpusvm 基础上演进并引入 amdgpu,带来三大改进:
- 简化的同步 :驱动路径上无重量级 mmap 锁;与 amdgpu VM 核心共享一把 rwsem,notifier、eviction 与 PTE 更新全部在其上串行化。
- 原生大页支持:框架内建 2 MB / 64 KB / 4 KB 分块;无需侵入式改动即可处理 THP;一个 512 MB notifier 覆盖多个 range。
- 更清晰的属性抽象:策略独立存放于自己的 interval tree;位置、粒度、访问权限按 range 管理;可在不触碰映射的情况下变更策略。
| 成果 | 实现方式 |
|---|---|
| 每个 DRM 客户端都可用 | 渲染节点上的 DRM_IOCTL_AMDGPU_GEM_SVM,无 /dev/kfd 依赖 |
| 与 Intel Xe 共享演进 | 修复与特性经 drm_gpusvm 双向流动 |
| KFD SVM 继续工作 | 增量而非替代,两者共存 |
| 成长空间 | 当前单设备,多设备支持已在推进 |
3. XNACK:硬件基础
XNACK 是按需分页(demand paging)的前提条件。开启 XNACK 后,GPU 访问未映射 VA 不会杀死 wave:
Kernel UTCL2 GPU Wave Kernel UTCL2 GPU Wave #mermaid-svg-N4Ok9ysElG5aZiEc{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-N4Ok9ysElG5aZiEc .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-N4Ok9ysElG5aZiEc .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-N4Ok9ysElG5aZiEc .error-icon{fill:#552222;}#mermaid-svg-N4Ok9ysElG5aZiEc .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-N4Ok9ysElG5aZiEc .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-N4Ok9ysElG5aZiEc .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-N4Ok9ysElG5aZiEc .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-N4Ok9ysElG5aZiEc .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-N4Ok9ysElG5aZiEc .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-N4Ok9ysElG5aZiEc .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-N4Ok9ysElG5aZiEc .marker{fill:#333333;stroke:#333333;}#mermaid-svg-N4Ok9ysElG5aZiEc .marker.cross{stroke:#333333;}#mermaid-svg-N4Ok9ysElG5aZiEc svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-N4Ok9ysElG5aZiEc p{margin:0;}#mermaid-svg-N4Ok9ysElG5aZiEc .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-N4Ok9ysElG5aZiEc text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-N4Ok9ysElG5aZiEc .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-N4Ok9ysElG5aZiEc .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-N4Ok9ysElG5aZiEc .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-N4Ok9ysElG5aZiEc .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-N4Ok9ysElG5aZiEc #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-N4Ok9ysElG5aZiEc .sequenceNumber{fill:white;}#mermaid-svg-N4Ok9ysElG5aZiEc #sequencenumber{fill:#333;}#mermaid-svg-N4Ok9ysElG5aZiEc #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-N4Ok9ysElG5aZiEc .messageText{fill:#333;stroke:none;}#mermaid-svg-N4Ok9ysElG5aZiEc .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-N4Ok9ysElG5aZiEc .labelText,#mermaid-svg-N4Ok9ysElG5aZiEc .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-N4Ok9ysElG5aZiEc .loopText,#mermaid-svg-N4Ok9ysElG5aZiEc .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-N4Ok9ysElG5aZiEc .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-N4Ok9ysElG5aZiEc .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-N4Ok9ysElG5aZiEc .noteText,#mermaid-svg-N4Ok9ysElG5aZiEc .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-N4Ok9ysElG5aZiEc .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-N4Ok9ysElG5aZiEc .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-N4Ok9ysElG5aZiEc .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-N4Ok9ysElG5aZiEc .actorPopupMenu{position:absolute;}#mermaid-svg-N4Ok9ysElG5aZiEc .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-N4Ok9ysElG5aZiEc .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-N4Ok9ysElG5aZiEc .actor-man circle,#mermaid-svg-N4Ok9ysElG5aZiEc line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-N4Ok9ysElG5aZiEc :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} access VA (unmapped) XNACK (retry, wave survives) retry fault → IH ring hmm_range_fault() write PTE hardware replays hit, execution continues
关键点:XNACK 开启时 wave 不被杀死,只是停顿并重试;内核填好 PTE 后硬件重放访问。
两种模式均已实现:XNACK-on 无需 quiesce;XNACK-off 则拥有自己的 invalidate 回调、自己的 GC,以及一对 quiesce/resume。
4. 架构与核心结构
amdgpu SVM 分为五个模块,以嵌入式扩展遵循 amdgpu 惯例:
| 模块 | 职责 | 行数 |
|---|---|---|
amdgpu_svm.c |
ioctl · 生命周期 · GC | 858 |
amdgpu_svm_fault.c |
retry fault | 476 |
amdgpu_svm_attr.c |
属性树 | 977 |
amdgpu_svm_range.c |
range · notifier | 812 |
amdgpu_migrate.c |
ZONE_DEVICE · SDMA | 899 |
配套:drm_gpusvm/drm_pagemap(框架)、amdgpu_vm.c(写页表)、gmc_v9_0/gmc_v12_0(get_svm_pte_flags(),将 per-IP MTYPE 逻辑隔离在 SVM 核心之外)。
核心结构(嵌入式继承,drm_gpusvm 为首成员):
c
struct amdgpu_svm {
struct drm_gpusvm gpusvm; /* first member */
struct kref refcount; /* _get / _put */
struct amdgpu_vm *vm;
struct rw_semaphore svm_lock; /* driver_svm_lock */
bool xnack_enabled;
};
struct amdgpu_svm_range {
struct drm_gpusvm_range base; /* embedded */
struct list_head work_node;
bool gpu_mapped; /* 快路径:跳过已映射 range */
u8 queue_state;
uint32_t attr_flags;
ktime_t validate_timestamp; /* 2 ms 缺页去重 */
};
UAPI 基于属性区间而非 alloc/free------内核只对 malloc 已返回的内存打标签:
c
#define DRM_AMDGPU_GEM_SVM 0x1a /* render node */
struct drm_amdgpu_gem_svm {
__u64 start_addr; __u64 size;
__u32 operation; /* SET / GET / RESET_ATTR */
__u32 nattr; __u64 attrs_ptr;
};
5. 站在框架之上
drivers/gpu/drm/drm_gpusvm.c 与 Intel Xe 共享。框架与驱动的职责划分:
| 框架提供 | 驱动实现 |
|---|---|
| notifier / range 红黑树 | ops->invalidate |
hmm_range_fault 封装 |
PTE flag 计算 |
DMA 映射 + notifier_seq |
写页表 |
notifier_lock |
driver_svm_lock |
| 512 MB notifier / 2M-64K-4K 分块 | GC 回收策略 |
两级一树 :一个 mmu_interval_notifier 覆盖多个不同大小的 range(KFD SVM 则每个 range 注册一个)。
代价:必须遵守框架的锁契约------本系列有三个 commit 纯粹是为处理这一点而存在。
6. 缺页路径(最重要的一页)
GPU retry fault → IH ring
└─ amdgpu_vm_handle_fault()
└─ unreserve root PD(SVM 重新取用)
└─ amdgpu_svm_handle_fault()
├─ checkpoint_ts 过滤过期 fault
├─ [LOCK] svm_lock
├─ 查找/创建属性区间
└─ fault_map_range() 重试 ×3
├─ (1) garbage_collector()
├─ (2) fault_check_allowed()
├─ (3) range_find_or_insert() 2M/64K/4K
├─ (4) 2ms 去重 / gpu_mapped 快路径
├─ (5) get_pages() → hmm_range_fault()
└─ (6) update_mapping()
├─ [LOCK] root PD dma_resv
└─ [LOCK] notifier_lock (R)
├─ pages_valid() ← 关键
├─ map_range() 写 PTE
└─ update_pdes() 写 PDE
- 第 6 步为何关键 :第 5 步取得 DMA 地址到写 PTE 之间,CPU 可能已 unmap 这些页;
pages_valid()与 PTE 写入必须在notifier_lock读模式下保持原子。 - 三类重试 :
-EPERM(只读 VMA,改 RO 重试)、-EAGAIN(CPU 侧变化)、-EBUSY(HMM 超时)。 - 数据流 :CPU VMA →
hmm_range_fault()→pages.dma_addr[]→amdgpu_vm_map_range()→ GPU PTE。
7. MMU Notifier 路径与垃圾回收
SVM 正确性的另一半:
#mermaid-svg-7PbSe7Urh2Ajr6Y6{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-7PbSe7Urh2Ajr6Y6 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-7PbSe7Urh2Ajr6Y6 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-7PbSe7Urh2Ajr6Y6 .error-icon{fill:#552222;}#mermaid-svg-7PbSe7Urh2Ajr6Y6 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-7PbSe7Urh2Ajr6Y6 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-7PbSe7Urh2Ajr6Y6 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-7PbSe7Urh2Ajr6Y6 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-7PbSe7Urh2Ajr6Y6 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-7PbSe7Urh2Ajr6Y6 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-7PbSe7Urh2Ajr6Y6 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-7PbSe7Urh2Ajr6Y6 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-7PbSe7Urh2Ajr6Y6 .marker.cross{stroke:#333333;}#mermaid-svg-7PbSe7Urh2Ajr6Y6 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-7PbSe7Urh2Ajr6Y6 p{margin:0;}#mermaid-svg-7PbSe7Urh2Ajr6Y6 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-7PbSe7Urh2Ajr6Y6 .cluster-label text{fill:#333;}#mermaid-svg-7PbSe7Urh2Ajr6Y6 .cluster-label span{color:#333;}#mermaid-svg-7PbSe7Urh2Ajr6Y6 .cluster-label span p{background-color:transparent;}#mermaid-svg-7PbSe7Urh2Ajr6Y6 .label text,#mermaid-svg-7PbSe7Urh2Ajr6Y6 span{fill:#333;color:#333;}#mermaid-svg-7PbSe7Urh2Ajr6Y6 .node rect,#mermaid-svg-7PbSe7Urh2Ajr6Y6 .node circle,#mermaid-svg-7PbSe7Urh2Ajr6Y6 .node ellipse,#mermaid-svg-7PbSe7Urh2Ajr6Y6 .node polygon,#mermaid-svg-7PbSe7Urh2Ajr6Y6 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-7PbSe7Urh2Ajr6Y6 .rough-node .label text,#mermaid-svg-7PbSe7Urh2Ajr6Y6 .node .label text,#mermaid-svg-7PbSe7Urh2Ajr6Y6 .image-shape .label,#mermaid-svg-7PbSe7Urh2Ajr6Y6 .icon-shape .label{text-anchor:middle;}#mermaid-svg-7PbSe7Urh2Ajr6Y6 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-7PbSe7Urh2Ajr6Y6 .rough-node .label,#mermaid-svg-7PbSe7Urh2Ajr6Y6 .node .label,#mermaid-svg-7PbSe7Urh2Ajr6Y6 .image-shape .label,#mermaid-svg-7PbSe7Urh2Ajr6Y6 .icon-shape .label{text-align:center;}#mermaid-svg-7PbSe7Urh2Ajr6Y6 .node.clickable{cursor:pointer;}#mermaid-svg-7PbSe7Urh2Ajr6Y6 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-7PbSe7Urh2Ajr6Y6 .arrowheadPath{fill:#333333;}#mermaid-svg-7PbSe7Urh2Ajr6Y6 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-7PbSe7Urh2Ajr6Y6 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-7PbSe7Urh2Ajr6Y6 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-7PbSe7Urh2Ajr6Y6 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-7PbSe7Urh2Ajr6Y6 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-7PbSe7Urh2Ajr6Y6 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-7PbSe7Urh2Ajr6Y6 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-7PbSe7Urh2Ajr6Y6 .cluster text{fill:#333;}#mermaid-svg-7PbSe7Urh2Ajr6Y6 .cluster span{color:#333;}#mermaid-svg-7PbSe7Urh2Ajr6Y6 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-7PbSe7Urh2Ajr6Y6 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-7PbSe7Urh2Ajr6Y6 rect.text{fill:none;stroke-width:0;}#mermaid-svg-7PbSe7Urh2Ajr6Y6 .icon-shape,#mermaid-svg-7PbSe7Urh2Ajr6Y6 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-7PbSe7Urh2Ajr6Y6 .icon-shape p,#mermaid-svg-7PbSe7Urh2Ajr6Y6 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-7PbSe7Urh2Ajr6Y6 .icon-shape .label rect,#mermaid-svg-7PbSe7Urh2Ajr6Y6 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-7PbSe7Urh2Ajr6Y6 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-7PbSe7Urh2Ajr6Y6 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-7PbSe7Urh2Ajr6Y6 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} CPU: munmap / migrate
drm_gpusvm_notifier_invalidate()
down_write(notifier_lock)
Pass 1 · event_begin
zap PTEs, clear gpu_mapped,
末尾一次 flush_tlb
Pass 2 · event_end
DMA unmap; UNMAP 事件 → 入队 GC
GC workqueue
svm_lock → drm_gpusvm_range_remove()
- 为何两遍 :Pass 1 只清 PTE 并累积,最后只刷一次 TLB(而非每 range 刷)。
- 为何延迟到 GC 删除 :notifier 上下文不能取
svm_lock(会与 fault 路径反转),只能入队;GC 是为规避锁顺序而非为性能。 - checkpoint_ts 作用:在 UNMAP 时记录 IH ring 时间戳;任何时间戳更早的后续 fault 都被丢弃(它指向的映射已不存在)。
8. VRAM 迁移
8.1 设置与策略(1/2)
- 将 VRAM 注册为
MEMORY_DEVICE_PRIVATE(devm_memremap_pages),owner token 让 HMM 区分本设备与他设备的 device-private 页。 - 向框架交付两张回调表:
drm_pagemap_ops(destroy/device_map/populate_mm)与drm_pagemap_devmem_ops(devmem_release/populate_devmem_pfn/copy_to_devmem/copy_to_ram)。框架驱动迁移,驱动只提供设备相关原语。 - fault、prefetch、restore 都会触发迁移;
drm_pagemap_populate_mm()返回-EBUSY表示有并发迁移竞争------evict 后重试一次。
迁移不受 XNACK 模式门控------它挂在两种模式都调用的
amdgpu_svm_range_map_attrs()上。策略来自属性树:PREFERRED_LOC/PREFETCH_LOC,唯一决策点为amdgpu_svm_attr_prefer_vram()。
8.2 数据路径(2/2)
- 分配承载 devmem 的 VRAM BO(
amdgpu_bo_svm_alloc(),domain =AMDGPU_GEM_DOMAIN_VRAM)。 - 将 TTM buddy 块转为 device PFN:用 cursor 把碎片化的 buddy 块展平 成 HMM 期望的线性数组(
pfn_base = PHYS_PFN(cursor.start + hpa_base))。 - 实际拷贝:经 GART 窗口用 SDMA 完成(
amdgpu_svm_copy_memory_gart())。
| 方向 | 回调 | 触发 |
|---|---|---|
| RAM → VRAM | copy_to_devmem |
fault / prefetch |
| VRAM → RAM | copy_to_ram |
CPU 访问 / eviction |
返程经 TTM eviction:
amdgpu_svm_bo_evict()在内存压力下将 device 页迁回 RAM。该路径沿用 KFD 既有的svm_migrate_copy_memory_gart()模式------成熟代码,新管线。
9. XNACK-off:另一种模型
无重试意味着映射必须在 GPU 运行前就有效:
- XNACK-on(惰性、fault 驱动):GPU 缺页 → 硬件重试 → 驱动按需恢复。
- XNACK-off(急切、quiesce 驱动):一次 fault 杀死 wave,因此必须停队列、重建全部映射、再恢复。
compute queues restore worker MMU notifier CPU mm compute queues restore worker MMU notifier CPU mm #mermaid-svg-kj3cjdbkXwHgDfcu{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-kj3cjdbkXwHgDfcu .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-kj3cjdbkXwHgDfcu .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-kj3cjdbkXwHgDfcu .error-icon{fill:#552222;}#mermaid-svg-kj3cjdbkXwHgDfcu .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-kj3cjdbkXwHgDfcu .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-kj3cjdbkXwHgDfcu .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-kj3cjdbkXwHgDfcu .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-kj3cjdbkXwHgDfcu .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-kj3cjdbkXwHgDfcu .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-kj3cjdbkXwHgDfcu .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-kj3cjdbkXwHgDfcu .marker{fill:#333333;stroke:#333333;}#mermaid-svg-kj3cjdbkXwHgDfcu .marker.cross{stroke:#333333;}#mermaid-svg-kj3cjdbkXwHgDfcu svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-kj3cjdbkXwHgDfcu p{margin:0;}#mermaid-svg-kj3cjdbkXwHgDfcu .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-kj3cjdbkXwHgDfcu text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-kj3cjdbkXwHgDfcu .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-kj3cjdbkXwHgDfcu .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-kj3cjdbkXwHgDfcu .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-kj3cjdbkXwHgDfcu .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-kj3cjdbkXwHgDfcu #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-kj3cjdbkXwHgDfcu .sequenceNumber{fill:white;}#mermaid-svg-kj3cjdbkXwHgDfcu #sequencenumber{fill:#333;}#mermaid-svg-kj3cjdbkXwHgDfcu #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-kj3cjdbkXwHgDfcu .messageText{fill:#333;stroke:none;}#mermaid-svg-kj3cjdbkXwHgDfcu .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-kj3cjdbkXwHgDfcu .labelText,#mermaid-svg-kj3cjdbkXwHgDfcu .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-kj3cjdbkXwHgDfcu .loopText,#mermaid-svg-kj3cjdbkXwHgDfcu .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-kj3cjdbkXwHgDfcu .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-kj3cjdbkXwHgDfcu .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-kj3cjdbkXwHgDfcu .noteText,#mermaid-svg-kj3cjdbkXwHgDfcu .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-kj3cjdbkXwHgDfcu .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-kj3cjdbkXwHgDfcu .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-kj3cjdbkXwHgDfcu .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-kj3cjdbkXwHgDfcu .actorPopupMenu{position:absolute;}#mermaid-svg-kj3cjdbkXwHgDfcu .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-kj3cjdbkXwHgDfcu .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-kj3cjdbkXwHgDfcu .actor-man circle,#mermaid-svg-kj3cjdbkXwHgDfcu line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-kj3cjdbkXwHgDfcu :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} munmap / migrate zap PTEs · evicted_ranges++ quiesce_mm(..._TRIGGER_SVM) queue_delayed_work(1 ms) get_pages → update_mapping requeue while ranges remain resume_mm() · counter == 0
只有空的 restore 列表且计数器归零,才会释放 compute 队列。
四个约束,一个设计: 无 fault 路径 → 急切映射;notifier 同步清 PTE → 先停队列;get_pages() 可能失败 → restore 必须异步;部分 unmap → GC 必须重建而非仅删除。
10. XNACK-off:Quiesce 窗口
evicted_ranges 是窗口而非 range 计数:一个计数器打开它,一次 compare-and-swap 关闭它。
counter == 0:队列运行,无窗口。0 → 1:atomic_inc_return()返回 1 →begin()=quiesce_compute()。1 → N:更多 invalidate 到达,并入同一窗口------不再二次 quiesce/resume。- worker 检查
restore.list与gc.list是否都空(检查三次,因 GC 的部分重建会把工作喂回 restore)。 atomic_cmpxchg(&evicted_ranges, record, 0):== record则 resume,!= record则保持打开。record是 worker 启动时的快照,比较在notifier_lock下进行。
何时跳过 quiesce: 全范围 unmap 或 !gpu_mapped → 跳过;部分 unmap + gpu_mapped → quiesce。整段 munmap() 不再停队列。
绝不能重试的错误:
-ENOENT、-EFAULT、-EPERM、-EINVAL、-EHWPOISON。一旦对其无限重试,计数器永不归零,队列将永久停止。
11. 锁:一把锁,而非三把
三个 commit,均 Suggested-by: Christian König。
问题:同一个 VM 的页表在不同锁域下被更新------
| 锁 | 保护对象 | 使用场景 |
|---|---|---|
svm_lock(rwsem) |
drm_gpusvm range 树 |
fault / attr / GC |
notifier_lock(rwsem) |
DMA 映射 + notifier_seq |
框架 + bind |
eviction_lock(mutex) |
PTE 写入 + TTM eviction | map / unmap_range |
没有单一锁覆盖所有写页表的路径。
修复------让它们成为同一把锁:
eviction_lock变为可选(读/写 rwsem)。- 页表分配可回收:
unlock → pt_create → relock,若 seqno 变动则返回-EAGAIN。 - 将其指向 notifier 锁:
vm->eviction_lock = &svm->gpusvm.notifier_lock。
在 SVM VM 上,
eviction_lock就是notifier_lock------单一串行化点,无反转、无递归。普通图形 VM 保留default_eviction_lock不受影响。
12. 补丁系列状态
共 21 + 8 个 commit:
- xnack-on 核心(12) :UAPI 定义、核心头与 VM 集成、属性树与 set/get/clear、range 类型与工作队列、
get_svm_pte_flags回调、GPU 映射核心、notifier 与 GC、invalidate 回调与 checkpoint、初始化与生命周期、ioctl 入口与 fault handler、构建系统集成。 - 锁重构(3,Suggested-by: Christian König) :VM eviction 锁改读/写、页表分配后重校验 range 有效性、用
drm_gpusvmnotifier 锁做 VM eviction。 - VRAM 迁移(6,服务两种 XNACK 模式) :
drm_pagemap迁移基础设施、SDMA 迁移回调、SVM BO 同步 TTM eviction、ZONE_DEVICE 注册、range 迁移 helper、集成到 fault 与 prefetch。 - xnack-off 系列(8):restore 类型头、集成到核心头、restore 核心与 invalidate cb、restore worker、GC work 函数、队列 quiesce/resume helper、属性变更边界重对齐 helper、接入生命周期与属性设置。
回馈框架 :本工作产出的一个 drm_pagemap THP 页计数修复已独立上游,不再由本系列携带。
发布前待办:尚无系统性性能数据;xnack-off 仍携带本地 TRACE/ERR 临时垫片;cover letter 的 Testing 章节仍空白。
13. 路线图与要点
#mermaid-svg-kKyjZv2SIviGNCQK{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-kKyjZv2SIviGNCQK .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-kKyjZv2SIviGNCQK .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-kKyjZv2SIviGNCQK .error-icon{fill:#552222;}#mermaid-svg-kKyjZv2SIviGNCQK .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-kKyjZv2SIviGNCQK .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-kKyjZv2SIviGNCQK .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-kKyjZv2SIviGNCQK .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-kKyjZv2SIviGNCQK .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-kKyjZv2SIviGNCQK .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-kKyjZv2SIviGNCQK .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-kKyjZv2SIviGNCQK .marker{fill:#333333;stroke:#333333;}#mermaid-svg-kKyjZv2SIviGNCQK .marker.cross{stroke:#333333;}#mermaid-svg-kKyjZv2SIviGNCQK svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-kKyjZv2SIviGNCQK p{margin:0;}#mermaid-svg-kKyjZv2SIviGNCQK .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-kKyjZv2SIviGNCQK .cluster-label text{fill:#333;}#mermaid-svg-kKyjZv2SIviGNCQK .cluster-label span{color:#333;}#mermaid-svg-kKyjZv2SIviGNCQK .cluster-label span p{background-color:transparent;}#mermaid-svg-kKyjZv2SIviGNCQK .label text,#mermaid-svg-kKyjZv2SIviGNCQK span{fill:#333;color:#333;}#mermaid-svg-kKyjZv2SIviGNCQK .node rect,#mermaid-svg-kKyjZv2SIviGNCQK .node circle,#mermaid-svg-kKyjZv2SIviGNCQK .node ellipse,#mermaid-svg-kKyjZv2SIviGNCQK .node polygon,#mermaid-svg-kKyjZv2SIviGNCQK .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-kKyjZv2SIviGNCQK .rough-node .label text,#mermaid-svg-kKyjZv2SIviGNCQK .node .label text,#mermaid-svg-kKyjZv2SIviGNCQK .image-shape .label,#mermaid-svg-kKyjZv2SIviGNCQK .icon-shape .label{text-anchor:middle;}#mermaid-svg-kKyjZv2SIviGNCQK .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-kKyjZv2SIviGNCQK .rough-node .label,#mermaid-svg-kKyjZv2SIviGNCQK .node .label,#mermaid-svg-kKyjZv2SIviGNCQK .image-shape .label,#mermaid-svg-kKyjZv2SIviGNCQK .icon-shape .label{text-align:center;}#mermaid-svg-kKyjZv2SIviGNCQK .node.clickable{cursor:pointer;}#mermaid-svg-kKyjZv2SIviGNCQK .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-kKyjZv2SIviGNCQK .arrowheadPath{fill:#333333;}#mermaid-svg-kKyjZv2SIviGNCQK .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-kKyjZv2SIviGNCQK .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-kKyjZv2SIviGNCQK .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-kKyjZv2SIviGNCQK .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-kKyjZv2SIviGNCQK .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-kKyjZv2SIviGNCQK .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-kKyjZv2SIviGNCQK .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-kKyjZv2SIviGNCQK .cluster text{fill:#333;}#mermaid-svg-kKyjZv2SIviGNCQK .cluster span{color:#333;}#mermaid-svg-kKyjZv2SIviGNCQK div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-kKyjZv2SIviGNCQK .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-kKyjZv2SIviGNCQK rect.text{fill:none;stroke-width:0;}#mermaid-svg-kKyjZv2SIviGNCQK .icon-shape,#mermaid-svg-kKyjZv2SIviGNCQK .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-kKyjZv2SIviGNCQK .icon-shape p,#mermaid-svg-kKyjZv2SIviGNCQK .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-kKyjZv2SIviGNCQK .icon-shape .label rect,#mermaid-svg-kKyjZv2SIviGNCQK .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-kKyjZv2SIviGNCQK .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-kKyjZv2SIviGNCQK .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-kKyjZv2SIviGNCQK :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Phase 1
单 GPU · XNACK-on
fault 驱动映射
已完成·上游中
Phase 2
单 GPU · XNACK-off
quiesce/restore/resume
进行中
Phase 3
多 GPU
P2P / XGMI hive
规划中
Phase 1 已完成并上游,Phase 2 进行中(二者皆为本次主题);VRAM 迁移支撑两者,且不绑定任一 XNACK 模式。
三点要点:
- 框架优先:复用 DRM GPUSVM 并与 Xe 共享演进(一个框架 THP 计数修复已独立回馈上游)。
- 一把锁而非多把 :给定页表必须由单一锁串行化,合并
notifier_lock与eviction_lock是关键。 - 无重试改变一切:XNACK-off 把惰性 fault handler 变成急切的 quiesce/restore/resume 流水线------数据结构相同,控制流相反。
参考链接
- 本次演讲:https://indico.freedesktop.org/event/12/contributions/537/
- 补丁系列 1:https://lore.kernel.org/dri-devel/20260612090928.29682-1-ray.huang@amd.com/
- 补丁系列 2:https://lore.kernel.org/amd-gfx/20260519092313.3115848-1-honglei1.huang@amd.com/
- 补丁系列 3:https://lore.kernel.org/amd-gfx/20260605075340.20199-1-Junhua.Shen@amd.com/
- 补丁系列 4:https://lore.kernel.org/dri-devel/20260603065620.2555316-1-honglei1.huang@amd.com/
- XDC 2025 - GPU SVM in DRM / Xe:https://indico.freedesktop.org/event/10/contributions/437/
- AMD ROCm 文档:https://rocm.docs.amd.com/en/latest/
© 2026 Advanced Micro Devices, Inc. 本报告为演讲内容的中文整理,信息可能随产品与路线图变化而调整。