XDC2026: 从 KFD 到 DRM SVM:面向 DRM 统一内存模型的演进——技术报告

来源: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)

  1. 将 VRAM 注册为 MEMORY_DEVICE_PRIVATE(devm_memremap_pages),owner token 让 HMM 区分本设备与他设备的 device-private 页。
  2. 向框架交付两张回调表:drm_pagemap_ops(destroy/device_map/populate_mm)与 drm_pagemap_devmem_ops(devmem_release/populate_devmem_pfn/copy_to_devmem/copy_to_ram)。框架驱动迁移,驱动只提供设备相关原语。
  3. 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)

  1. 分配承载 devmem 的 VRAM BO(amdgpu_bo_svm_alloc(),domain = AMDGPU_GEM_DOMAIN_VRAM)。
  2. 将 TTM buddy 块转为 device PFN:用 cursor 把碎片化的 buddy 块展平 成 HMM 期望的线性数组(pfn_base = PHYS_PFN(cursor.start + hpa_base))。
  3. 实际拷贝:经 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

没有单一锁覆盖所有写页表的路径。

修复------让它们成为同一把锁:

  1. eviction_lock 变为可选(读/写 rwsem)。
  2. 页表分配可回收:unlock → pt_create → relock,若 seqno 变动则返回 -EAGAIN。
  3. 将其指向 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_gpusvm notifier 锁做 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 模式。

三点要点:

  1. 框架优先:复用 DRM GPUSVM 并与 Xe 共享演进(一个框架 THP 计数修复已独立回馈上游)。
  2. 一把锁而非多把 :给定页表必须由单一锁串行化,合并 notifier_lock 与 eviction_lock 是关键。
  3. 无重试改变一切:XNACK-off 把惰性 fault handler 变成急切的 quiesce/restore/resume 流水线------数据结构相同,控制流相反。

参考链接

© 2026 Advanced Micro Devices, Inc. 本报告为演讲内容的中文整理,信息可能随产品与路线图变化而调整。