16- KFD SVM概念与逻辑的高度概括与用例分析

张开发
2026/4/9 13:10:55 15 分钟阅读

分享文章

16- KFD SVM概念与逻辑的高度概括与用例分析
本文档涵盖 AMD GPU KFD SVM (Shared Virtual Memory) 子系统的完整执行逻辑包括核心数据结构、三大执行路径、异步工作队列、迁移机制以及调试过程中遇到的典型问题解析。难度: 高级预计学习时间: 3小时前置知识: 第1-10章特别是第9、10章目录1. 概述2. 核心数据结构3. KFD SVM 三大执行路径3.1 用户态 ioctl 路径3.2 MMU Notifier 路径3.3 GPU Retry Fault 路径4. 异步工作队列4.1 Deferred List Work延迟工作4.2 Restore Work恢复工作5. 迁移机制5.1 RAM → VRAM 迁移5.2 VRAM → RAM 迁移5.3 migrate_to_ram 回调6. XNACK On/Off 差异7. 锁层级8. 调试案例分析8.1 GDB 调试时意外触发 migrate_to_ram8.2 部分 Prefetch 后属性查询返回 0xFFFFFFFF9. 关键源文件索引前面的章节对KFD SVM的实现进行了详细的分析本章进行一个总结并给出调试SVM中可能遇到的问题进行说明。1. 概述AMD GPU KFD SVM 子系统实现了 CPU 和 GPU 之间的统一虚拟地址Shared Virtual Memory允许 GPU 直接访问 CPU 进程的虚拟地址空间并支持页面在 VRAM 和系统内存之间透明迁移。KFD SVM 通过 KFD ioctl (/dev/kfd) 提供用户态接口核心实现位于kfd_svm.c和kfd_migrate.c使用自定义 interval tree 管理svm_range通过amdgpu_hmm_range_get_pages()获取页面使用migrate_vma_* SDMA 进行页面迁移。2. 核心数据结构kfd_process └── kfd_process_device (per GPU) └── svm_range_list (svms) ├── interval_tree (所有 svm_range 的红黑树) ├── deferred_range_list (延迟处理队列) ├── criu_svm_metadata_list └── deferred_list_work (工作队列) svm_range ├── start, last (页对齐的虚拟地址范围) ├── prefetch_loc, actual_loc (迁移位置) ├── flags (CoW, GPU exec, RO 等) ├── granularity (迁移粒度) ├── bitmap_access[] (GPU 访问位图) ├── bitmap_aip[] (GPU AIP 位图) ├── dma_addr[][] (per-GPU DMA 地址数组) ├── ttm_res (VRAM BO 的 TTM 资源) ├── migrate_mutex (迁移互斥锁) ├── lock (范围读写锁) ├── notifier (MMU interval notifier) ├── work_item (延迟工作项) └── child_list (子范围列表用于分裂)3. KFD SVM 三大执行路径3.1 用户态 ioctl 路径入口svm_range_set_attr()— 通过 KFD ioctlAMDKFD_IOC_SVM触发用户态 hsaKmtSVMSetAttr() └── ioctl(KFD_IOC_SVM, SET_ATTR) └── svm_range_set_attr() ├── 1. svm_range_check_attr() — 参数校验 ├── 2. svm_range_debug_dump() — 调试输出当前状态 ├── 3. svm_range_add() — 创建/分裂/合并 svm_range │ ├── 在 [start, last] 区间查找所有重叠 range │ ├── 如果没有 → 创建新 range │ ├── 如果部分重叠 → 分裂(split)现有 range │ └── 新的 range 加入 update_list ├── 4. 遍历 update_list: │ ├── svm_range_apply_attrs() — 应用新属性到每个 range │ └── 累积 update_mapping / flush_tlb 标志 ├── 5. 如果 trigger_migration: │ ├── prefetch_loc 指向 VRAM: │ │ └── svm_range_trigger_migration() │ │ └── svm_migrate_ram_to_vram() │ └── prefetch_loc 指向 SYSMEM: │ └── svm_migrate_vram_to_ram() └── 6. 如果 update_mapping: └── svm_range_validate_and_map() — 更新 GPU 页表映射关键流程 —svm_range_validate_and_map()svm_range_validate_and_map() ├── svm_range_reserve_bos() — 预留 BO/VM 资源 ├── for each GPU that has access: │ ├── amdgpu_hmm_range_get_pages() — HMM 获取页面 │ │ └── hmm_range_fault() — 触发缺页填充 pfn 数组 │ ├── svm_range_dma_map() — DMA 映射 │ └── svm_range_map_to_gpus() — 更新 GPU 页表 (PTE) │ └── svm_range_map_to_gpu() │ └── amdgpu_vm_update_range() — 写入 PDE/PTE └── svm_range_unreserve_bos() — 释放预留3.2 MMU Notifier 路径入口svm_range_cpu_invalidate_pagetables()— Linux MMU notifier 回调当 CPU 页表发生变化时如 munmap、页面迁移、CoW 等内核通过 MMU notifier 通知 SVM 子系统。CPU 页表变化 (munmap / migrate / CoW / swap) └── mmu_interval_notifier_ops.invalidate └── svm_range_cpu_invalidate_pagetables() ├── 检查事件类型: │ ├── MMU_NOTIFY_MIGRATE: │ │ └── 如果 owner 我们自己 → 跳过自触发迁移 │ ├── MMU_NOTIFY_RELEASE: │ │ └── 直接返回 │ └── 其他事件: │ └── 继续处理 ├── notifier_seq 递增使 HMM pages 无效 ├── 如果需要 GPU unmap: │ └── svm_range_unmap_from_gpus() │ └── amdgpu_vm_update_range(clear PTE) │ └── amdgpu_vm_update_pdes() └── 如果需要恢复: ├── svm_range_add_list_work() — 加入延迟工作队列 └── schedule_deferred_list_work() — 调度异步工作3.3 GPU Retry Fault 路径入口svm_range_restore_pages()— GPU 页面错误中断处理当 GPU 访问未映射的虚拟地址时硬件产生 retry fault由 interrupt handler 调用此函数。GPU 访问未映射地址 └── 硬件产生 retry fault └── amdgpu_vm_handle_fault() / kfd_svm_page_fault() └── svm_range_restore_pages() ├── 1. 查找/创建 svm_range │ ├── svm_range_from_addr() — 在 interval tree 中查找 │ └── 如果不存在: │ ├── find_vma() — 检查 VMA 是否存在 │ ├── svm_range_create() — 创建新 range │ └── svm_range_add() — 加入 interval tree ├── 2. 策略驱动迁移: │ ├── svm_range_best_restore_location() — 决定最佳位置 │ │ ├── 检查 actual_loc vs preferred_loc │ │ ├── 检查 GPU 访问权限 │ │ └── 返回目标 node ID │ ├── 如果需要迁移到 VRAM: │ │ └── svm_migrate_ram_to_vram() │ └── 如果需要迁移到 RAM: │ └── svm_migrate_vram_to_ram() └── 3. svm_range_validate_and_map() — 建立 GPU 映射4. 异步工作队列4.1 Deferred List Work延迟工作函数svm_range_deferred_list_work()svm_range_deferred_list_work() └── 遍历 svms-deferred_range_list: ├── 取出 work_item (包含 mm, start, last, op) ├── 根据 op 分类: │ ├── SVM_OP_UNMAP_RANGE: │ │ └── svm_range_unmap_split() │ │ — 处理 munmap清除 PTE分裂/删除 range │ ├── SVM_OP_UPDATE_RANGE_NOTIFIER: │ │ └── svm_range_update_notifier_and_interval_tree() │ │ — 更新 notifier 注册范围 │ ├── SVM_OP_UPDATE_RANGE_NOTIFIER_AND_MAP: │ │ └── 更新 notifier validate_and_map() │ │ — 用于 range 分裂后重建映射 │ └── SVM_OP_ADD_RANGE_AND_MAP: │ └── 添加新 range validate_and_map() └── svm_range_drain_retry_fault() — 等待 GPU fault 处理完成4.2 Restore Work恢复工作函数svm_range_restore_work()仅在XNACK Off模式下使用。当 MMU notifier 清除了 GPU PTE 后需要通过此 worker 重新建立映射。svm_range_restore_work() ├── kfd_process_evict_queues() — 暂停所有 GPU 队列 ├── 遍历所有 svm_range: │ ├── 如果 range 不需要更新 → 跳过 │ └── svm_range_validate_and_map() — 重建 GPU 映射 │ ├── hmm_range_fault() — 重新获取页面 │ └── map_to_gpus() — 更新 PTE └── kfd_process_restore_queues() — 恢复 GPU 队列XNACK Off 下的 Queue Eviction 机制MMU notifier 触发 → 通知 SVM 清除 GPU PTE → kfd_process_evict_queues() — 暂停队列防止 GPU 访问无效地址 → restore_work 重建所有 PTE → kfd_process_restore_queues() — 恢复队列5. 迁移机制5.1 RAM → VRAM 迁移svm_migrate_ram_to_vram(prange, best_loc) ├── svm_range_vram_node_new() — 分配 VRAM BO (TTM) ├── 分段迁移 (每段 256 页): │ ├── migrate_vma_setup() — 初始化迁移上下文 │ ├── svm_migrate_copy_to_vram() │ │ ├── 分配 device private pages (ZONE_DEVICE) │ │ ├── amdgpu_copy_buffer() — SDMA DMA 拷贝 │ │ │ ├── src: 系统内存 DMA 地址 │ │ │ └── dst: VRAM 偏移 │ │ └── 设置 page-pgmap svm_pgmap │ ├── migrate_vma_pages() — 将页面所有权转移给 device │ └── migrate_vma_finalize() — 完成迁移更新 CPU PTE └── 成功后: actual_loc best_loc5.2 VRAM → RAM 迁移svm_migrate_vram_to_ram(prange, mm, ...) ├── 分段迁移 (每段 256 页): │ ├── migrate_vma_setup() — 标记 MIGRATE_VMA_SELECT_DEVICE_PRIVATE │ ├── svm_migrate_copy_to_ram() │ │ ├── 分配系统页面 (alloc_page_vma) │ │ ├── amdgpu_copy_buffer() — SDMA DMA 拷贝 │ │ │ ├── src: VRAM 偏移 │ │ │ └── dst: 系统内存 DMA 地址 │ │ └── dma_fence_wait() — 等待 SDMA 完成 │ ├── migrate_vma_pages() │ └── migrate_vma_finalize() └── 成功后: actual_loc 0 (系统内存)5.3 migrate_to_ram 回调当 CPU 访问 device private page 时内核自动触发CPU 访问 device private page └── do_swap_page() → migrate_to_ram() └── svm_migrate_to_ram() (注册为 pgmap ops) ├── 查找对应的 svm_range ├── svm_migrate_vram_to_ram() — 将页面搬回系统内存 └── 返回CPU 重新访问现在在 RAM 中的页面6. XNACK On/Off 差异方面XNACK OnXNACK OffGPU 缺页处理硬件自动 retry等待 SVM 建立映射产生错误中断MMU notifier标记 range 需要更新GPU 下次访问时 retry清除 GPU PTE 暂停队列 restore_work 重建Queue 管理不需要暂停/恢复队列需要 evict/restore 队列性能更灵活按需映射需要主动维护所有映射validate_and_map惰性GPU fault 时触发主动restore_work 批量重建当 XNACK Off 时每次 MMU notifier 回调都需要清除受影响的 GPU PTE暂停 GPU 队列防止访问无效映射异步通过 restore_work 重建所有映射恢复 GPU 队列7. 锁层级process_info-lock (进程级互斥锁) └── mmap_write_lock(mm) (进程 VMA 锁) └── svms-lock (SVM range list 锁) └── prange-migrate_mutex (迁移互斥锁) └── prange-lock (范围读写锁)8. 调试案例分析8.1 GDB 调试时意外触发 migrate_to_ram问题现象在 GDB 单步调试KFDSVMRangeTest::PrefetchTest时执行SVMRangePrefetchToNode(pBuf, BufSize/2, gpuNode)后dmesg 中意外出现svm_migrate_to_ram日志但用户代码并没有主动触发回迁。根因分析GDB 通过ptrace读取被调试进程的内存来显示变量值、评估表达式。当 GDB 尝试读取已经被 prefetch 到 VRAM 的 device private page 时GDB 读取变量/内存 └── ptrace(PEEK) → access_process_vm() └── CPU 页表查找 → 发现 device private page └── do_swap_page() → migrate_to_ram() └── svm_migrate_to_ram() └── svm_migrate_vram_to_ram() ← 出现在 dmesg!解决方案这是正常行为。GDB 调试会影响 SVM 页面位置在分析迁移行为时需要考虑调试器的干扰。8.2 部分 Prefetch 后属性查询返回 0xFFFFFFFF该测试用用例的理解需要深度理解SVM的range管理机制特别是range分裂策略。问题现象// 16KB buffer, 仅 prefetch 后半部分到 GPUSVMRangePrefetchToNode(pBufBufSize/2,BufSize/2,gpuNode);// 查询整个 buffer 的 prefetch 位置 → 返回 0xFFFFFFFF!SVMRangeGetPrefetchNode(pBuf,BufSize,node_id);// node_id 0xFFFFFFFF (INVALID_NODEID)根因分析属性树中buffer 被分成了两个区段[pBuf, pBufBufSize/2) → prefetch_loc UNDEFINED (未 prefetch) [pBufBufSize/2, pBufBufSize) → prefetch_loc gpuNode (已 prefetch)查询整个 buffer 时amdgpu_svm_attr_get()遍历所有子区段并合并属性// amdgpu_svm_attr.c: attr_get_ctx_add()staticvoidattr_get_ctx_add(structattr_get_ctx*ctx,conststructamdgpu_svm_attrs*seg_attrs,...){if(ctx-count0){ctx-merged*seg_attrs;// 第一个区段: prefetch_loc UNDEFINED}else{// 第二个区段: prefetch_loc gpuNodeif(ctx-merged.prefetch_loc!seg_attrs-prefetch_loc)ctx-merged.prefetch_locAMDGPU_SVM_LOCATION_UNDEFINED;// ↑ 不一致 → 设为 UNDEFINED (0xFFFFFFFF)}ctx-count;}9. 关键源文件索引内核态文件路径描述kfd_svm.cdrivers/gpu/drm/amd/amdkfd/KFD SVM 核心实现 (~4339 行)kfd_svm.hdrivers/gpu/drm/amd/amdkfd/KFD SVM 头文件 (~276 行)kfd_migrate.cdrivers/gpu/drm/amd/amdkfd/KFD 迁移实现 (~1086 行)用户态文件路径描述svm.clibhsakmt/src/Thunk 层 SVM 接口KFD/DRM 切换KFDSVMRangeTest.cpplibhsakmt/tests/kfdtest/src/SVM 测试套件KFDTestUtil.cpplibhsakmt/tests/kfdtest/src/测试工具函数

更多文章