AMDGPU SVM Map Path 与 Migration 融合设计技术分析

张开发
2026/4/18 20:42:50 15 分钟阅读

分享文章

AMDGPU SVM Map Path 与 Migration 融合设计技术分析
AMD 正在使用 drm svm框架重构SVM的实现看来drm svm框架要进入大范围应用了。下面是在kernel社区上由AMD的开发人员提交的POC 验证版本的patches的技术方案实现。这里快速总结了实现以飨读者。因是POC版本设计可能会变动读者们慎重使用。本文仅用来跟踪前沿驱动技术的迭代发展现状。1. 总体架构AMDGPU SVM 的 map 流程采用了**统一漏斗unified funnel的设计模式无论触发源来自哪里page fault、ioctl、worker所有 map 操作最终都收敛到同一个核心函数amdgpu_svm_range_map()。Migration 功能以每个 range 的前置步骤per-range pre-step**的形式嵌入 map 路径而不是作为独立操作。这种设计的核心抽象是enum amdgpu_svm_migrate_mode作为迁移意图从调用者一路传递到最底层AMDGPU_SVM_MIGRATE_PREFERRED — 遵循 preferred_loc 策略决策 AMDGPU_SVM_MIGRATE_TO_VRAM — 强制迁移到 VRAMprefetch 一次性覆盖 AMDGPU_SVM_MIGRATE_TO_SYSMEM — 强制回迁到系统内存 AMDGPU_SVM_MIGRATE_NONE — 抑制所有迁移post-eviction remap2. 四条 Map 调用链路2.1 链路一GPU Page FaultXNACK 模式触发条件: GPU 访问未映射的 SVM 地址硬件产生 retry fault。迁移模式:AMDGPU_SVM_MIGRATE_PREFERREDGPU hardware retry fault │ ▼ amdgpu_svm_handle_fault(adev, pasid, fault_addr, write_fault) │ // 通过 pasid 查找 svm 上下文 │ // 仅 xnack_enabled 时走此路径 ▼ amdgpu_svm_range_map_attr_ranges(svm, fault_page, fault_page, AMDGPU_SVM_MIGRATE_PREFERRED) │ // 查询 attr_tree 获取该页的属性 │ // 按属性段segment循环 ▼ amdgpu_svm_range_map_interval(svm, start, last, attrs, MIGRATE_PREFERRED) │ // 计算 pte_flags、设置 gpusvm_ctxdevmem_possible ▼ amdgpu_svm_range_map(svm, start, end, attrs, ctx, pte_flags, MIGRATE_PREFERRED) │ // 对每个 drm_gpusvm_range: │ // ① migrate_range() ← 迁移决策点 │ // ② get_pages() ← 获取 DMA 地址 │ // ③ update_gpu_range() ← 写 GPU 页表 ▼ [GPU 页表更新完成, fault 恢复]特点:仅处理单页fault_page到fault_page迁移模式为PREFERRED意味着如果用户之前通过 ioctl 将preferred_loc设为 VRAM则 fault 时会自动将数据迁移到 VRAM这是 XNACK 硬件的按需映射语义的核心2.2 链路二IOCTL Set Attr属性变更 Prefetch触发条件: 用户态通过AMDGPU_SVM_OP_SET_ATTRioctl 修改 SVM 属性。迁移模式: 由属性变更类型trigger动态决定。userspace: ioctl(AMDGPU_SVM_OP_SET_ATTR) │ ▼ amdgpu_gem_svm_ioctl() → amdgpu_svm_set_attr() │ ▼ amdgpu_svm_attr_set_pages(svm, start, last, nattr, attrs) │ // 遍历 attr_tree, 计算 prev_attrs vs new_attrs │ // 生成 trigger bitmask ▼ amdgpu_svm_attr_apply_change(svm, change) │ // 过滤 ATTR_ONLY 等无需 remap 的变更 ▼ amdgpu_svm_range_apply_attr_change(svm, start, last, trigger, prev, new) │ // ★ 迁移模式决策: │ // LOCATION_CHANGE prefetch_loc VRAM → MIGRATE_TO_VRAM │ // LOCATION_CHANGE prefetch_loc SYSMEM → MIGRATE_TO_SYSMEM │ // 其他有效变更 → MIGRATE_PREFERRED ▼ amdgpu_svm_range_map_interval() → amdgpu_svm_range_map() │ ▼ [per-range: migrate → get_pages → update_gpu]trigger 类型与行为对照:Trigger 类型迁移模式行为LOCATION_CHANGEprefetch_locVRAMTO_VRAM强制一次性迁移到 VRAMLOCATION_CHANGEprefetch_locSYSMEMTO_SYSMEM从 VRAM 回迁到系统内存ACCESS_CHANGE(获得访问权)PREFERRED遵循 preferred_loc 策略PTE_FLAG_CHANGE/MAPPING_FLAG_CHANGEPREFERRED仅更新 PTE flagsATTR_ONLY/GRANULARITY_CHANGE—不触发 map关键设计:prefetch_loc是一次性命令one-shot commandpreferred_loc是持久策略persistent policy。两者在 attr 层分开管理在 map path 中通过不同的migrate_mode值统一表达。2.3 链路三Restore Workereviction 恢复触发条件: MMU notifier 通知页面失效非 UNMAP 事件range 加入 restore work list。迁移模式:AMDGPU_SVM_MIGRATE_NONEMMU notifier callback (MMU_NOTIFY_CLEAR / MIGRATE / ...) │ ▼ amdgpu_svm_range_invalidate(svm, notifier, mmu_range) │ // 判断 event 类型: │ // 非 XNACK 或 ALWAYS_MAPPED → QUEUE_INTERVAL OP_RESTORE │ // XNACK 非 ALWAYS_MAPPED → CLEAR_PTE only │ // 调用 begin_restore() (quiesce KFD queues) ▼ amdgpu_svm_range_enqueue(svm, range, start, last, OP_RESTORE) │ ▼ [经过 gc_worker 优先级排序后] │ amdgpu_svm_range_restore_worker() │ // 从 restore_work_list 取出 pending ranges ▼ amdgpu_svm_range_map_attr_ranges(svm, start, last, AMDGPU_SVM_MIGRATE_NONE) │ ▼ amdgpu_svm_range_map() → per-range: migrate_range(NONE) → get_pages → update_gpu │ ▼ [所有 pending ranges 处理完 → end_restore() → resume KFD queues]关键设计:Restore 路径使用MIGRATE_NONE明确禁止迁移。这是因为 restore 的语义是用当前物理页原地重建 GPU 映射不应改变数据位置如果某些 range 之前在 VRAM 中但被 evict 回 RAM此时 restore 只是用 RAM 页重建映射不会试图再迁移回 VRAMbegin_restore()/end_restore()配对控制 KFD 队列的 quiesce/resume2.4 链路四GC Worker → Rebuildmunmap 后重建触发条件: MMU_NOTIFY_UNMAP 事件VMA munmap/mremapold range 被销毁后需重建。迁移模式:AMDGPU_SVM_MIGRATE_PREFERREDMMU notifier: MMU_NOTIFY_UNMAP │ ▼ amdgpu_svm_range_invalidate() │ // is_unmaptrue → CLEAR_PTE QUEUE_INTERVAL OP_UNMAP │ // 非 XNACK 时同时加 OP_RESTORE │ // 调用 begin_restore() ▼ amdgpu_svm_range_enqueue(svm, range, start, last, OP_UNMAP) │ ▼ amdgpu_svm_range_gc_worker() │ // UNMAP_WORK → process_unmap_interval ▼ amdgpu_svm_range_process_unmap_interval(svm, start, last, rebuildtrue) │ // 清理 attr_tree 中的属性 │ // 销毁重叠的 old ranges ▼ amdgpu_svm_range_rebuild_locked() │ // 记录 rebuild start/last可能比 unmap 区间更大 ▼ amdgpu_svm_range_map_attr_ranges(svm, rebuild_start, rebuild_last, AMDGPU_SVM_MIGRATE_PREFERRED) │ ▼ amdgpu_svm_range_map() → [标准 map 流程]关键设计:Unmap 后 rebuild 使用PREFERRED因为新的 VMA 可能完全不同应该根据当前策略重新决策rebuild_start/rebuild_last可能比原 unmap 区间更大——因为 old range 可能跨越 unmap 边界被整体删除后需要扩大重建范围GC Worker 优先于 Restore Workergc_list优先于restore_work_list3. 核心函数amdgpu_svm_range_map()中的 Migration 融入amdgpu_svm_range_map(svm,start,end,attrs,gpusvm_ctx,pte_flags,migrate_mode){while(addrend){// Step 1: 获取或创建 drm_gpusvm_rangerangedrm_gpusvm_range_find_or_insert(...);// Step 2: ★ Per-range migration decision ★amdgpu_svm_range_migrate_range(svm,range,attrs,migrate_mode);// Step 3: 获取 DMA 地址系统页或 VRAM 页// 如果 Step 2 迁移到了 VRAM这里会通过 device_map()// 获取 VRAM MC 地址 AMDGPU_INTERCONNECT_VRAM 协议标识drm_gpusvm_range_get_pages(svm-gpusvm,range,map_ctx);// Step 4: 写 GPU 页表// 根据 drm_pagemap_addr.proto 区分系统页和 VRAM 页// VRAM 页清除 SYSTEM/SNOOPED 标志amdgpu_svm_range_update_gpu_range(svm,range,pte_flags,...);}}3.1amdgpu_svm_range_migrate_range()三层决策模型range_needs_migrate_to_vram(svm, range, attrs, mode): │ ├─ Capability: range-pages.flags.migrate_devmem false? → NO │ ├─ Suppress: mode NONE || mode TO_SYSMEM? → NO │ ├─ Already: range_in_vram(range)? → NO (已在 VRAM) │ ├─ Command: mode TO_VRAM? → YES (强制迁移不看 policy) │ └─ Policy: preferred_loc_is_vram(attrs)? → YES / NO层级名称含义来源L1Capability硬件是否支持 VRAMdrm_gpusvm_range_find_or_insert设置L2Command一次性强制迁移prefetch_locioctlL3Policy持久位置偏好preferred_locioctl4. Migration 与 Map 的数据流交互4.1 RAM→VRAM 迁移后的数据流amdgpu_svm_range_migrate_to_vram(svm, range): │ ├─ atomic_set(svm-in_populate, 1) // 防止自身迁移触发 notifier 递归 │ ├─ drm_pagemap_populate_mm(dpagemap, start, end, mm, 0) │ └─ .populate_mm amdgpu_svm_populate_mm() │ ├─ amdgpu_svm_bo_alloc() // 分配 VRAM BO │ └─ drm_pagemap_migrate_to_devmem() │ ├─ migrate_vma_setup() // Linux MM 迁移框架 │ ├─ .populate_devmem_pfn() // BO → PFN 数组 │ ├─ .copy_to_devmem() // SDMA: RAM → VRAM │ └─ migrate_vma_finalize() // 完成 page table 切换 │ ├─ atomic_set(svm-in_populate, 0) │ ├─ drm_gpusvm_range_unmap_pages() // 使缓存的 DMA 地址失效 └─ WRITE_ONCE(range-gpu_mapped, false) // 强制下次 get_pages 重新获取invalidate 后的 get_pages() 调用会通过drm_gpusvm_range_get_pages()重新走device_map()路径获取 VRAM MC 地址drm_gpusvm_range_get_pages() │ // 发现页面是 ZONE_DEVICE (device_private) ▼ dpagemap-ops-device_map(page) amdgpu_svm_device_map() │ // page → PFN → HPA → VRAM offset → PTE address ▼ drm_pagemap_addr { .addr MC_addr, .proto AMDGPU_INTERCONNECT_VRAM }4.2 GPU PTE 中的 VRAM vs 系统内存区分// amdgpu_svm_range_update_gpu_range() 中if(entry-protoAMDGPU_INTERCONNECT_VRAM)seg_pte_flagspte_flags~(AMDGPU_PTE_SYSTEM|AMDGPU_PTE_SNOOPED);elseseg_pte_flagspte_flags;// 系统内存保留 SYSTEM SNOOPED5. 设计初衷总结5.1 为什么将 migration 融入 map path原子性保证: migration mapping 在同一个 range 循环内完成。迁移后立即更新 GPU 页表避免已迁移但未映射的窗口期避免重复遍历: 不需要先遍历一遍做迁移决策再遍历一遍做映射。单次遍历中每个 range 先迁移、再获取地址、再更新页表统一的 notifier 保护:get_pages()和update_gpu_range()都在notifier_lock保护下执行如果迁移完成后页面被外部 notifier 失效range_pages_valid()检查会返回 false触发-EAGAIN重试migrate_mode 作为统一抽象: 四种调用链路各有不同的迁移语义但通过一个枚举值统一传递。调用者只需声明意图具体决策由range_needs_migrate_to_vram()统一处理5.2 关键设计决策决策理由Restore 用MIGRATE_NONE恢复语义不改变数据位置Rebuild 用MIGRATE_PREFERREDmunmap 后 VMA 可能改变应按当前策略重新决策Fault 用MIGRATE_PREFERRED按需映射遵循用户策略Prefetch 用TO_VRAM/TO_SYSMEM一次性显式命令覆盖策略Migration 失败不阻塞 mapmigrate_range()返回非零时继续用系统内存映射in_populateflag防止自身drm_pagemap_populate_mm()触发的MMU_NOTIFY_MIGRATE导致 notifier 递归清理6. 完整调用链路汇总图┌─────────────────┐ │ Userspace │ └──────┬──────────┘ ioctl │ GPU access SET_ATTR │ (unmapped VA) │ │ │ ▼ │ ▼ amdgpu_gem_svm_ioctl() │ amdgpu_svm_handle_fault() │ │ │ ▼ │ ▼ amdgpu_svm_attr_set_pages() │ map_attr_ranges(PREFERRED) │ │ │ ▼ │ │ apply_attr_change() │ │ ├─ TO_VRAM │ │ ├─ TO_SYSMEM │ │ └─ PREFERRED │ │ │ │ │ ▼ │ ▼ ┌──────────────────────────────────────────────┐ │ amdgpu_svm_range_map_interval() │ │ → amdgpu_svm_range_map() │ │ for each range: │ │ ① migrate_range(mode) ← DECISION │ │ ② get_pages() ← DMA ADDR │ │ ③ update_gpu_range() ← GPU PTE │ └──────────────────────────────────────────────┘ ▲ ▲ │ │ map_attr_ranges(PREFERRED) map_attr_ranges(NONE) │ │ rebuild_locked() restore_worker() │ │ gc_worker(UNMAP) restore_work_list │ │ ┌──────┴─────┐ ┌────────┴────────┐ │ MMU_NOTIFY │ │ MMU_NOTIFY │ │ UNMAP │ │ CLEAR/MIGRATE │ └────────────┘ └─────────────────┘7. 文件职责对照文件职责amdgpu_svm.cSVM 生命周期管理、fault 入口、ioctl 入口amdgpu_svm_range.c核心 map 循环、notifier 处理、GC/Restore workeramdgpu_svm_range_migrate.cper-range 迁移决策与执行 (migrate_range,migrate_to_vram)amdgpu_svm_range_migrate.henum amdgpu_svm_migrate_mode定义amdgpu_migrate.cdrm_pagemap_ops实现:device_map,populate_mm, SDMA copyamdgpu_migrate.hAMDGPU_INTERCONNECT_VRAM,AMDGPU_SVM_PGMAP_OWNERamdgpu_svm_attr.c属性树管理、trigger 计算、change 生成

更多文章