AMDGPU驱动性能实战: KFD Queue Quiesce/Restore 机制分析与优化方案探讨
1. 问题提出Per-Process 粒度的 Queue Quiesce/Restore1.1 核心问题在 AMDGPU KFD 驱动中当某个 BOBuffer Object或 SVM range 需要被 evict 或 invalidate 时驱动会quiesce停止该进程的所有 user queues而不仅仅是引用了该 BO 的 queues。如amdgpu_amdkfd_fence.c中的注释所述Big Idea - Since KFD submissions are done by user queues, a BO cannot beevicted unless all the user queues for that process are evicted.这意味着即使某个 queue 完全不访问被 evict 的 BO它也会被一起停掉。这是一个per-process、all-or-nothing的操作粒度较粗。1.2 所有触发 Quiesce/Restore 的场景在当前内核代码中共有9 个场景会触发进程级别的 queue quiesce 或 restore最终都通过kfd_process_evict_queues()/kfd_process_restore_queues()执行。SVM 相关3 个场景场景 1MMU Notifier InvalidationXNACK Off入口svm_range_evict()→kgd2kfd_quiesce_mm()kfd_svm.c触发条件CPU page table 发生变更如MMU_NOTIFY_CLEAR、migration且 XNACK 关闭行为第一个 range 被 evict 时evicted_ranges 1quiesce 所有 queues后续 range 只递增计数器不重复 quiesce恢复调度svm_range_restore_work延迟 1ms 后尝试 restore/* kfd_svm.c - svm_range_evict() */evicted_rangesatomic_inc_return(svms-evicted_ranges);if(evicted_ranges!1)returnr;/* First eviction, stop the queues */rkgd2kfd_quiesce_mm(mm,KFD_QUEUE_EVICTION_TRIGGER_SVM);场景 2Queue-Vital Buffer 被 Unmap入口svm_range_unmap_from_cpu()→kgd2kfd_quiesce_mm()kfd_svm.c触发条件MMU_NOTIFY_UNMAP事件中被 unmap 的 range 有非零queue_refcount即该 range 是 doorbell、MQD、wptr 等 queue 关键内存的 backing store行为立即 quiesce 所有 queues恢复无对应 restore 路径queue 赖以运行的内存已被释放/* kfd_svm.c - svm_range_unmap_from_cpu() */if(atomic_read(prange-queue_refcount)){pr_warn(Freeing queue vital buffer 0x%lx, queue evicted\n,...);rkgd2kfd_quiesce_mm(mm,KFD_QUEUE_EVICTION_TRIGGER_SVM);}场景 3SVM Range Restore Work入口svm_range_restore_work()→kgd2kfd_resume_mm()kfd_svm.c触发条件场景 1 的延迟恢复行为遍历所有 evicted ranges逐一调用svm_range_validate_and_map()重建 GPU 映射全部成功后调用kgd2kfd_resume_mm()恢复 queues失败处理重调度 restore work期间 queues 持续处于 quiesced 状态TTM BO Eviction 相关2 个场景场景 4VRAM 压力 Eviction入口evict_process_worker()→kfd_process_evict_queues()kfd_process.c触发条件TTM 内存管理器需要腾出 VRAM 空间通过 eviction fence 触发行为evict 所有 queuessignal eviction fence 允许 BO move恢复调度restore_process_worker场景 5TTM Restore入口restore_process_worker()→kfd_process_restore_queues()kfd_process.c触发条件场景 4 的恢复行为先调用amdgpu_amdkfd_gpuvm_restore_process_bos()将 BO 恢复到 VRAM再 restore 所有 queues失败处理以PROCESS_BACK_OFF_TIME_MS间隔重调度系统级2 个场景场景 6System Suspend入口kfd_suspend_all_processes()→kfd_process_evict_queues()kfd_process.c触发条件系统挂起或 GPU reset行为遍历kfd_processes_tableevict 每个进程的所有 queues 并 signal eviction fence场景 7System Resume入口kfd_resume_all_processes()kfd_process.c触发条件系统恢复行为为每个进程调度restore_process_workerCRIU 相关2 个场景场景 8CRIU Checkpoint入口criu_checkpoint()→kfd_process_evict_queues()kfd_chardev.c触发条件CRIU 对进程做快照行为暂停所有 queues 以保证进程状态一致性场景 9CRIU Restore入口criu_restore()→kfd_process_evict_queues()kfd_chardev.c触发条件CRIU 恢复进程行为先 evict queues 防止在 BO/mapping 完全就绪前运行后续恢复1.3 场景分类总结类别场景Quiesce 入口Restore 入口触发频率SVMMMU notifier invalidationsvm_range_evict()svm_range_restore_work()中-高SVMQueue-vital buffer unmapsvm_range_unmap_from_cpu()无极低TTMVRAM 压力 evictionevict_process_worker()restore_process_worker()低-中系统Suspend / Resumekfd_suspend_all_processes()kfd_resume_all_processes()极低CRIUCheckpoint / Restorecriu_checkpoint/restore()后续流程极低所有路径的共同特征quiesce/restore 都是 per-process 粒度没有 per-queue 或 per-BO 的细粒度控制。其中 SVM MMU notifier 路径场景 1是运行时触发频率最高的路径对性能影响最大。2. 架构原因分析为什么选择 Per-Process 粒度2.1 Per-Process VMPage Table共享KFD 的 GPU 虚拟内存管理采用per-process的 VM 模型同一进程的所有 user queues 共享同一个 GPU page table即同一个 VMID 和 page directory base。在 queue 创建时page table base 取自进程级的qcm_process_device而非queue 自身/* kfd_device_queue_manager.c - add_queue_mes() */queue_input.process_idpdd-pasid;queue_input.page_table_base_addrqpd-page_table_base;// 进程级 page tablequeue_input.process_va_start0;queue_input.process_va_endadev-vm_manager.max_pfn-1;qpd-page_table_base是在register_process()时设置的进程级 page directory所有 queue 共享同一地址空间范围[0, max_pfn)。直接后果当某个 BO 或 SVM range 的 GPU page table entry 被 invalidate 后该进程的任意 queue的 shader 都可能访问到这个已失效的 VA。驱动无法判断哪些 queues “安全”、哪些 queues 会触发 fault因此只能保守地停掉所有 queues。2.2 为什么不做 Per-Queue / Per-BO 的细粒度 Eviction从架构上看细粒度 eviction 面临根本性障碍——User Queue 模式下内核无法得知每个 queue 访问了哪些 BO。1User Queue 与图形 Job 提交模型的本质区别在传统图形提交如 amdgpu CS ioctl路径中内核负责构建 command buffer 并提交 job因此内核清楚知道每个 job 引用了哪些 BO通过bo_list参数显式传入。TTM 可以基于这些信息做 per-job 粒度的 eviction 和 fence 管理。但 KFD 采用的是user queue模型queue 创建后用户态直接向 queue 提交 dispatch内核完全不参与提交过程也无法拦截或解析用户态的提交内容。因此内核无法建立 BO → Queue 的映射关系——它根本不知道某个 queue 的 shader 会访问哪些地址。图形提交模型 App → ioctl(CS, bo_list) → Kernel(知道 BO 集合) → GPU User Queue 模型App → 直接写 doorbell → GPU内核不可见2BO → Queue 映射关系不存在且无法建立当前内核中没有维护哪个 queue 引用了哪些 BO的映射关系。KFD 的内存分配kfd_ioctl_alloc_memory_of_gpu和 queue 创建kfd_ioctl_create_queue是独立的 ioctl它们之间没有建立关联。用户态通过 SVM 或map_memory_to_gpu建立的映射是进程级的任何 queue 都可以访问。即使引入 BO → Queue 的 tracking这个映射也是高度动态的用户态随时可以map/unmap内存SVM range 的 GPU mapping 可以因 fault、migration、eviction 等原因变化需要在每次 mapping 变更时更新 tracking 数据结构引入额外的锁和同步开销更关键的是用户态的 shader 可以通过指针运算访问任意已映射的 VA 这些访问模式内核完全不可见2.3 小结Per-process 粒度的 quiesce/restore 不是设计疏忽而是以下架构约束的必然结果约束影响User Queue 模型内核不参与提交无法得知每个 queue 访问了哪些 BO无 BO → Queue 映射关系且无法有效建立无法做选择性 eviction这是一个典型的correctness vs. performance 的 trade-off以粗粒度保证正确性用简单的实现换取可维护性。3. 性能影响分析与优化方向3.1 各场景的性能影响评估在第一章列举的 9 个场景中系统级suspend/resume和 CRIU 场景属于低频操作quiesce/restore 的开销可以忽略。真正影响运行时性能的是SVM 路径和TTM 路径。SVM 路径场景 1—— 运行时主要瓶颈SVM MMU notifier invalidation 是触发频率最高的路径其性能影响体现在三个阶段Quiesce 阶段开销本身较小但有去重机制控制频率/* 只在第一个 range 被 evict 时 quiesce后续只递增计数器 */evicted_rangesatomic_inc_return(svms-evicted_ranges);if(evicted_ranges!1)returnr;此外mapped_to_gpu检查避免了未映射 range 的无效 quiesce。这两个优化确保了单次 burst invalidation 只触发一次 quiesce。Restore 阶段这是主要的性能瓶颈svm_range_restore_work()的开销包括三把锁串行获取process_info-lock→mmap_write_lock→svms-lock持锁期间阻塞该进程所有其他内存操作全量遍历 range listlist_for_each_entry(prange,svms-list,list){invalidatomic_read(prange-invalid);if(!invalid)continue;// 大量 range 在这里跳过svm_range_validate_and_map(...);}即使只有 1 个 range 被 invalidate也要遍历整个 list。当进程有大量 SVM ranges 时如大 working set 的 HPC 应用遍历开销线性增长。逐 range validate_and_map每个 invalid range 需要重新获取 CPU 页面、更新 GPU page table涉及get_user_pages和 GPU page table 写操作Thrashing 风险restore 过程中如果又有新的 invalidation 发生atomic_cmpxchg会失败导致整个 restore 重来if(atomic_cmpxchg(svms-evicted_ranges,evicted_ranges,0)!evicted_ranges)gotoout_reschedule;// 全部重来在 CPU 内存活动频繁的场景下如大量mmap/munmap/migration可能出现 quiesce → restore → 又被 quiesce 的反复抖动GPU 在此期间完全空转。TTM 路径场景 4—— VRAM 压力下的影响TTM eviction 的触发频率低于 SVM 路径但单次开销更大Quiesce 后需要等待 GPU 上所有进行中的工作完成Restore 需要将所有 BO 重新 validate 到 VRAMamdgpu_amdkfd_gpuvm_restore_process_bos涉及 VRAM 分配和可能的 BO migrationRestore 延迟为PROCESS_RESTORE_TIME_MS 100ms远大于 SVM 路径的 1ms失败时 back off 也是 100msPROCESS_BACK_OFF_TIME_MS在多进程共享 GPU 且 VRAM 紧张的场景下进程间可能互相 evict导致大量时间消耗在 quiesce/restore 循环中。3.2 性能影响程度对比因素SVM 路径TTM 路径触发频率中-高CPU 内存活动驱动低-中VRAM 压力驱动Quiesce 开销低有去重低Restore 延迟1ms 起步 validate 时间100ms 起步 BO restore 时间主要瓶颈全量遍历 range list 锁竞争BO validate VRAM 分配Thrashing 风险高CAS 失败重调度中进程间互相 evictGPU 空转时间与 invalid range 数量成正比与 BO 总量成正比3.3 XNACK Off 模式下的优化方向在不改变 XNACK off 前提下以下优化可以缓解性能问题优化 1Evicted List 替代全量遍历推荐低成本高收益当前 restore work 遍历svms-list上的所有 range逐一检查invalid标记。可以引入独立的 evicted list只追踪被 invalidate 的 ranges/* 当前实现O(total_ranges) */list_for_each_entry(prange,svms-list,list){if(!atomic_read(prange-invalid))continue;svm_range_validate_and_map(...);}/* 优化后O(evicted_ranges) */list_for_each_entry_safe(prange,next,svms-evicted_list,evict_link){svm_range_validate_and_map(...);list_del(prange-evict_link);}收益restore 时间从 O(total_ranges) 降为 O(evicted_ranges)对大 working set 场景改善明显。改动局部风险可控。优化 2Batch Coalescing 合并多次 Eviction不可行当前第一次 eviction 立刻 quiescerestore delay 仅 1ms。在短时间内发生大量 MMU notifier 事件时如批量munmap可能触发多次 quiesce → restore → 再 quiesce 的抖动。此优化不可行。MMU notifier 的.invalidate回调即svm_range_cpu_invalidate_pagetables在invalidate_range_start路径中被调用其语义要求回调返回前设备侧必须不再访问被 invalidate 的页面。如果引入延迟窗口来 batch invalidation在窗口期内 CPU page table 已经 invalidate但 GPU queues 仍在运行并可能访问旧映射这直接违反了 MMU notifier 的正确性 contract。因此 quiesce 必须在 notifier 回调返回前完成不能延迟。优化 3锁粒度优化值得探索当前 restore work 持有三把锁process_info-lockmmap_write_locksvms-lock贯穿整个 validate_and_map 过程阻塞了其他操作。可以将 restore 拆分为两阶段锁外阶段准备 page 数据get_user_pages等 IO 密集操作短暂持锁更新 GPU page table收益减少持锁时间降低对 SVM fault handling 等并发路径的阻塞。复杂度中等需要仔细处理并发 invalidation 与 restore 的竞争。不推荐的方向方向原因Per-queue evictionUser queue 模型下无法得知 queue 访问了哪些 BO见第二章分析延迟 quiesce GPU fault recoveryXNACK off 下 VM fault 是 fatal 的等于重新实现 XNACK3.4 小结Per-process quiesce/restore 的性能影响主要集中在 SVM restore 路径全量遍历 range list、重锁竞争、以及 CAS 失败导致的 thrashing。在 XNACK off 模式下evicted list 和锁粒度优化是可探索的优化方向可以降低 restore 开销。但这些都是渐进式改良无法从根本上消除 quiesce/restore 的开销——这需要 XNACK on。4. 终极方案XNACK On 模式4.1 XNACK On 如何从根本上消除 SVM 路径的 Quiesce/Restore在 XNACK on 模式下GPU MMU 支持retry fault当 GPU 访问到无效的 page table entry 时不会产生 fatal VM fault而是暂停发出访问的 wavefront产生一个 retry fault 中断等待内核处理完成并更新 page table 后GPU 自动 retry 该访问。这从根本上改变了 SVM invalidation 的处理方式。对比svm_range_evict()中的两个分支/* kfd_svm.c - svm_range_evict() */if(!p-xnack_enabled||(prange-flagsKFD_IOCTL_SVM_FLAG_GPU_ALWAYS_MAPPED)){/* XNACK Off 路径quiesce 所有 queuesschedule restore work */evicted_rangesatomic_inc_return(svms-evicted_ranges);if(evicted_ranges!1)returnr;rkgd2kfd_quiesce_mm(mm,KFD_QUEUE_EVICTION_TRIGGER_SVM);queue_delayed_work(...,svms-restore_work,...);}else{/* XNACK On 路径只 unmap GPU page table不 quiesce queues */svm_range_unmap_from_gpus(prange,s,l,trigger);}XNACK on 路径的关键区别不调用kgd2kfd_quiesce_mm()——queues 保持运行不调度svm_range_restore_work()——不需要全量 restore只做svm_range_unmap_from_gpus()——invalidate 受影响的 GPU page table entries如果 queue 的 shader 后续访问了被 unmap 的地址GPU 硬件自动产生 retry fault由内核 fault handler 按需恢复映射4.2 GPU Retry Fault 处理流程当 XNACK on 模式下 GPU 遇到 page fault 时处理流程如下GPU shader 访问无效 VA → GPU MMU retry fault → 中断 → KFD interrupt handler → svm_range_restore_pages() → 查找对应的 svm_range → svm_range_best_restore_location() 决定最佳位置 → 如果需要执行 migrationsvm_migrate_to_vram / svm_migrate_vram_to_ram → svm_range_validate_and_map() 重建单个 range 的 GPU mapping → GPU 自动 retry 访问wavefront 恢复执行关键代码在svm_range_restore_pages()中它只处理 fault 涉及的那一个 range/* kfd_svm.c - svm_range_restore_pages() */prangesvm_range_from_addr(svms,addr,NULL);...rsvm_range_validate_and_map(mm,start,last,prange,gpuidx,false,false,false);4.3 XNACK On 消除的三个核心问题问题XNACK OffXNACK OnQuiesce 所有 queues每次 MMU invalidation 都要停掉所有 queues不需要 quiescequeues 保持运行全量遍历 range listrestore work 遍历整个svms-list按需处理只 validate fault 涉及的 rangeGPU 空转等待 restorequiesce 到 restore 完成期间 GPU 完全空闲只有 fault 的 wavefront 暂停其他 wavefront 继续执行这三个优势叠加使得 XNACK on 在 SVM 场景下的性能显著优于 XNACK off。4.4 XNACK On 未能消除的场景需要注意的是XNACK on只消除了 SVM MMU notifier 路径场景 1的 quiesce/restore。以下场景仍然需要 per-process quiesceTTM BO eviction场景 4VRAM 压力下的 BO eviction 仍走 eviction fence 路径System suspend/resume场景 6、7系统级操作不受 XNACK 影响CRIU场景 8、9进程状态序列化仍需要 quiesceQueue-vital buffer unmap场景 2queue 关键内存释放仍需要 quiesce但这些场景触发频率远低于 SVM 路径实际影响有限。4.5 XNACK On 的 Trade-offXNACK on 并非没有代价1Retry fault 的延迟开销每次 retry fault 需要经过中断处理、内核 fault handler、page table 更新、GPU retry 的完整路径。如果 fault 频率高如首次访问大量新映射累积的 fault handling 开销可能显著。不过对比 XNACK off 下停掉所有 queues 全量 restore 的方案单次 fault 的代价远小于全量 quiesce。2TLB 管理开销XNACK on 要求 GPU TLB invalidation 更频繁、更精确以确保 retry fault 能正确触发。这增加了 TLB miss rate 和 invalidation 的开销。3硬件兼容性限制不是所有 GPU 都支持 XNACK on/* kfd_process.c - kfd_process_xnack_mode() *//* Aldebaran (MI200) 支持 per-process XNACK 模式选择 */if(supportedKFD_SUPPORT_XNACK_PER_PROCESS(dev))continue;/* GFXv10 不支持 page fault 期间的 shader preemption可能导致死锁 */if(KFD_GC_VERSION(dev)IP_VERSION(10,1,1))returnfalse;/* 如果硬件设置了 noretry不支持 XNACK */if(dev-kfd-noretry)returnfalse;GFX9Vega 系列支持 XNACK但 SQ retry 模式必须在 boot 时全局设定混合 XNACK on/off 进程可能 hang GPUAldebaranMI200/MI210/MI250支持per-process XNACK 模式选择 可以每个进程独立配置是 XNACK on 的最佳平台GFX10RDNA 系列不支持 page fault 期间的 shader preemption可能导致 QoS 问题或死锁因此代码直接返回 falseMI300 系列延续 Aldebaran 的 per-process XNACK 支持4XNACK on 对 SVM memory accounting 的影响XNACK on 时不预留 system memory limit因为映射是按需建立的XNACK off 时会预留/* kfd_svm.c - svm_range_new() */if(!p-xnack_enabledupdate_mem_usageamdgpu_amdkfd_reserve_mem_limit(NULL,sizePAGE_SHIFT,KFD_IOC_ALLOC_MEM_FLAGS_USERPTR,0)){...}4.6 小结XNACK on 通过 GPU 硬件 retry fault 机制从根本上消除了 SVM 路径场景 1中per-process quiesce/restore 的需求XNACK Off: MMU invalidation → quiesce 所有 queues → restore work 全量遍历恢复 → resume XNACK On: MMU invalidation → unmap GPU PTE → GPU retry fault → 按需恢复单个 range核心收益零 quiescequeues 永不因 SVM invalidation 被停止按需恢复只处理 GPU 实际访问到的 range而非全量 restore最小粒度暂停只有触发 fault 的 wavefront 暂停其余继续执行在支持 per-process XNACK 的硬件MI200/MI300上XNACK on 应作为 SVM 场景的默认模式。五、总结章节关键结论问题KFD 的 quiesce/restore 是 per-process 粒度的 all-or-nothing 操作共 9 个触发场景根因User queue 模型下内核无法得知 queue 访问了哪些 BO无法做细粒度 eviction性能SVM restore 路径是主要瓶颈全量遍历、重锁竞争、thrashing 风险XNACK off 优化Evicted list可行、锁粒度优化值得探索但都是渐进式改良XNACK on从根本上消除 SVM 路径的 quiesce/restore是终极解决方案
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2558833.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!