AMDGPU SVM Range Restore 机制

张开发
2026/6/15 5:21:09 15 分钟阅读
AMDGPU SVM Range Restore 机制
AMD 正在使用 drm svm框架重构SVM的实现看来drm svm框架要进入大范围应用了。下面是在kernel社区上由AMD的开发人员提交的POC 验证版本的patches的技术方案实现。这里快速总结了实现以飨读者。因是POC版本设计可能会变动读者们慎重使用。本文仅用来跟踪前沿驱动技术的迭代发展现状。概述SVM range restore 机制用于在 MMU notifier 事件使 GPU 映射失效后恢复 GPU页表一致性。它通过双队列工作系统协调三个组件——notifier 回调、GC worker和restore worker——并使用引用计数跟踪 range 生命周期。核心不变量在begin_restore暂停队列和end_restore恢复队列之间GPU shader 不可访问 SVM 映射。restore worker 负责在恢复队列前重建所有失效的映射。架构MMU notifier 事件 (UNMAP / MIGRATE / PROTECTION / ...) | v amdgpu_svm_range_invalidate() | --[清除 GPU PTE按需] --[解除 DMA 映射] --[置 gpu_mapped false] --[将 range 入队到 gc_list 或 restore_work_list] --[begin_restore → 暂停队列仅首次 eviction] | v ----------- | | v v gc_worker restore_worker (UNMAP) (RESTORE) | | | -- map_attr_ranges(MIGRATE_NONE) | -- 成功: end_restore → 恢复队列 | -- 失败: 重新入队 重新调度 | -- 清除属性 -- 移除重叠 range -- 重建 range若 !xnack -- 转发 RESTORE 操作 → restore_worker关键数据结构每 Range 的队列状态 (amdgpu_svm_range)structamdgpu_svm_range{structdrm_gpusvm_rangebase;structlist_headgc_node;/* 挂在 gc_list 或 restore_work_list 上 */unsignedlongpending_start;/* 合并后的 VA 起始页粒度 */unsignedlongpending_last;/* 合并后的 VA 结束页粒度 */uint8_tpending_ops;/* 位掩码: UNMAP | RESTORE */bool in_queue;/* 引用计数守卫: 在队列中时为 true */bool gc_queued;/* 在 gc_list 上 */bool restore_queued;/* 在 restore_work_list 上 */bool gpu_mapped;/* GPU PTE 是否有效 */uint64_tpte_flags;/* 上次写入的 PTE 标志 */uint32_tattr_flags;/* 上次写入的 SVM 属性标志 */};每 SVM 的工作状态 (amdgpu_svm)structamdgpu_svm{spinlock_tgc_lock;/* 保护两个队列 */structlist_headgc_list;/* UNMAP 工作项 */structlist_headrestore_work_list;/* RESTORE 工作项 */structwork_structgc_work;/* gc_worker */structdelayed_workrestore_work;/* restore_worker1ms 延迟 */structworkqueue_struct*gc_wq;/* WQ_UNBOUND | WQ_HIGHPRI */structworkqueue_struct*restore_wq;/* 有序队列, WQ_HIGHPRI */atomic_tevicted_ranges;/* 0 表示队列已暂停 */atomic_tkfd_queues_quiesced;/* quiesce/resume 守卫 */void(*begin_restore)(structamdgpu_svm*);/* 暂停回调 */void(*end_restore)(structamdgpu_svm*);/* 恢复回调 */};详细流程1. MMU Notifier 回调 (amdgpu_svm_range_invalidate)在notifier_lock下调用mmu_interval_notifier 的读侧。事件分类事件清除 GPU PTE队列操作是否暂停队列MMU_NOTIFY_UNMAP是UNMAP是若!xnack非 UNMAP!xnack或ALWAYS_MAPPED否RESTORE是非 UNMAPxnack无 ALWAYS_MAPPED是仅 PTERESTORE否自发起的 populatein_populate置位全部跳过——MMU_NOTIFY_RELEASE跳过——每 range 的操作通过amdgpu_vm_update_range()清除 GPU PTE若CLEAR_PTEdrm_gpusvm_range_unmap_pages()—— 拆除 DMA 映射range_invalidate_gpu_mapping()—— 置gpu_mapped falsedrm_gpusvm_range_set_unmapped()—— 标记 VMA 已解映射仅 UNMAPamdgpu_svm_range_enqueue()—— 加入工作队列2. 入队逻辑 (amdgpu_svm_range_enqueue)对同一 range 的多次操作通过 VA 合并进行聚合pending_start min(pending_start, new_start) pending_last max(pending_last, new_last) pending_ops | new_op队列优先级UNMAP RESTORE。若一个已在 RESTORE 队列中的 range收到 UNMAP会通过list_move_tail从restore_work_list移至gc_list。引用计数首次入队时取drm_gpusvm_range_get()引用在所有 pending 操作排空后由range_put_if_dequeued()释放。3. GC Worker (amdgpu_svm_range_gc_worker)运行在gc_wq上无绑定、高优先级。对每个出队的 rangeUNMAP 处理(amdgpu_svm_range_process_unmap_interval)amdgpu_svm_attr_clear_pages()—— 清除属性条目amdgpu_svm_range_rebuild_locked()移除所有重叠的drm_gpusvm_range对象若!xnack为扩展后的范围重建 GPU 映射覆盖被部分移除的 rangeRESTORE 转发若同时设置了RESTORE位若!xnack且重建成功queue_delayed_work(restore_wq)否则重新入队为OP_RESTORE→ 进入 restore_work_list4. Restore Worker (amdgpu_svm_range_restore_worker)运行在restore_wq上有序队列、高优先级、初始延迟 1ms。restore_worker(): 若 exiting 或 evicted_ranges 0: 返回 遍历 restore_work_list 中的每个 range: 出队 range down_write(svm_lock) map_attr_ranges(start, last, MIGRATE_NONE) ← 关键不触发迁移 up_write(svm_lock) 若失败: 重新入队 range need_resched true 若全部完成且无 pending 工作: 加锁 notifier_lock gc_lock双重检查 若仍无 pending: atomic_set(evicted_ranges, 0) end_restore() → kgd2kfd_resume_mm() 返回 重新调度自身带延迟关键设计决策 ——MIGRATE_NONErestore worker 绝对不能触发迁移。否则当preferred_locVRAM时会导致活锁CPU fault → migrate_to_ram (VRAM→RAM) → MMU_NOTIFY_MIGRATE → notifier → 入队 RESTORE → restore_worker → map_attr_ranges(MIGRATE_TO_VRAM) ← 错误会迁移回 VRAM → populate_mm → MMU_NOTIFY_MIGRATE → CPU fault → ...使用MIGRATE_NONE后restore worker 仅重建 GPU PTE指向页面当前所在位置RAM 或 VRAM不触发任何迁移。5. Begin/End Restore队列暂停/恢复begin_restore: end_restore: evicted_ranges首次: 0→1 evicted_ranges 0 kfd_queues_quiesced: 0→1 kfd_queues_quiesced: 1→0 kgd2kfd_quiesce_mm() kgd2kfd_resume_mm()begin_restore从 notifier 中调用仅首次 evictionend_restore从 restore worker 中调用所有工作排空后两者均使用atomic_cmpxchg确保恰好执行一次完成竞态防护restore worker 在检查队列是否为空并重置evicted_ranges时持有notifier_lock。这防止了在空检查和end_restore调用之间notifier 入队新工作的竞态。锁层级mmu_notifier_lock中断安全notifier 路径中最外层 └── gc_lockspinlock保护两个工作列表 svm_lock读写信号量映射/重建时写持有 └── vm PD lockdrm_exec用于 GPU PTE 更新 └── notifier_lock映射期间的页面有效性检查 └── gc_locknotifier 路径在notifier_lock下使用memalloc_noreclaim_save()避免与内存回收的死锁。状态机enqueue(RESTORE) IDLE ──────────────────────────────► QUEUED_RESTORE ▲ │ │ put_if_dequeued │ 出队 映射成功 │ 释放引用 │ │◄─────────────────────────────────────┘ │ │ enqueue(UNMAP) ├──────────────────────────────────► QUEUED_GC │ │ │ ┌───────────┤ │ │ UNMAP │ RESTORE │ │ 完成 │ 转发 │ ▼ ▼ │◄──── put_if_dequeued ── DONE QUEUED_RESTORE ──► ...range 可以通过list_move_tail在gc_list和restore_work_list之间转换——当一个已入队 RESTORE 的 range 收到 UNMAP 时即会发生。错误处理失败场景恢复策略map_attr_ranges返回错误重新入队 range重新调度 workerkgd2kfd_quiesce_mm返回-ESRCH无 KFD 进程重置 quiesced 标志drm_gpusvm_range_get_pages返回-EAGAIN在下一次amdgpu_svm_range_map迭代中重试进程退出 (svm-exiting)worker 提前退出work_fini排空队列与 xnack 的关系模式行为xnack_enabled重试模式GPU 在硬件中处理缺页触发 RESTORE 的 notifier 事件更少NEED_REBUILD为 false!xnack_enabled所有失效都需要软件重建UNMAP 总是附带 RESTORE每次失效都暂停队列

更多文章