C.3 amdgpu kfd 中通过 ioctl 创建的 BO 在 unmap 时为何不用暂停队列

📅 2026/6/26 23:47:31
C.3 amdgpu kfd 中通过 ioctl 创建的 BO 在 unmap 时为何不用暂停队列
本文是原 C.3文章 的优化版本。加入实际测试结论。1. 问题描述AMDGPU KFD 驱动中 SVM range 或 BO 在需要被驱逐 (evict) 或失效 (invalidate) 时需要触发进程级别的用户队列暂停 (quiesce) 与恢复。那么为什么一个普通的 BO 在 unmap 的时候不去暂停 queue 的执行该问题涉及一个更为基础且不同的内存管理场景BO 的显式解映射 (explicit unmap) 操作。这通常指用户态程序主动调用hsaKmtUnmapMemoryToGPU或类似接口将已分配的 BO 从 GPU 地址空间中移除。在这种场景下驱动不需要暂停队列执行其根本原因在于操作语义、同步保证和生命周期管理的差异。2. 内存管理操作分类与同步要求操作类型触发方核心语义对 GPU 执行流的同步要求典型场景驱逐/失效 (Eviction/Invalidation)系统/驱动被动、异步、强制。驱动需要保证操作完成后能恢复。必须暂停 (Quiesce)。通过kfd_process_evict_queues或kgd2kfd_quiesce_mm停止所有 queue。TTM VRAM 不足驱逐SVM range 因 CPUmunmap失效系统挂起。显式解映射 (Explicit Unmap)用户程序主动、预期内。应用程序声明不再需要该映射。无需暂停但有防护机制。驱动默认应用程序已完成同步。应用程序释放不再使用的 buffer。3. 显式解映射不暂停队列的原因与内核实现3.1 契约模型责任在应用程序// 应用程序端的正确用法clEnqueueNDRangeKernel(queue,kernel,...);clFinish(queue);// 确保 GPU 工作完成clReleaseMemObject(buffer);// 安全解映射驱动假设应用程序已通过 fence/barrier 确保 GPU 不再访问该 BO。3.2 内核侧的防护机制尽管不暂停 queue内核仍有多层保护a)queue_refcount拒绝机制(amdgpu_amdkfd_gpuvm.c)staticintunmap_bo_from_gpuvm(structkgd_mem*mem,...){if(bo_va-queue_refcount){return-EBUSY;// 拒绝 unmap queue 关键资源}...}如果 BO 被 queue 引用如 ring buffer、EOP bufferunmap 直接失败返回-EBUSY。b) PTE 更新的隐式排序(amdgpu_vm.c)/* Implicitly sync to command submissions in the same VM before unmapping. */amdgpu_sync_resv(adev,sync,vm-root.bo-tbo.base.resv,AMDGPU_SYNC_EQ_OWNER,vm);PTE 清零操作通过 SDMA会等待同一 VM owner 的 fence 完成后再执行。但注意KFD compute dispatch 的 fence owner 不是 VM因此此同步对 KFD queue 的 in-flight shader 不生效。c) TLB flush 保证可见性(kfd_chardev.c:)flush_tlbkfd_flush_tlb_after_unmap(pdd-dev-kfd);if(flush_tlb){amdgpu_amdkfd_gpuvm_sync_memory(...,true);// 等 PTE 更新完成kfd_flush_tlb(peer_pdd);// 使 TLB 失效}3.3 违反契约时的后果实测验证通过 测试验证在 shader 正在访问 BO 时主动 unmap 该 BO 所在的 GPU 节点。结果在没有xnack on的gpu上内核报retry page fault错误显示没有mapping。内存类型Retry Fault 是否能恢复原因SVM range✅ 能内核管理映射fault handler 可自主迁移/映射页面显式 BO❌ 不能映射由用户控制内核无法预测用户意图4. 驱逐为什么必须暂停 queue与显式 unmap 的关键区别维度驱逐显式 Unmap谁做同步驱动必须自己做应用程序不知情应用程序负责是否需要恢复是evict 后需要 restore否用户永久放弃GPU 可能在访问是无法预测不应该用户承诺如果不暂停数据损坏/GPU fault未定义行为用户的错驱逐流程evict_process_worker() → kfd_process_evict_queues() // 暂停所有 queue → [执行内存迁移/页表更新] → kfd_process_restore_queues() // 恢复 queue驱逐是异步发生的应用程序无法预知其时机在驱逐发生时应用程序可能仍期望继续访问该内存只是暂时被系统移出失效操作直接破坏页表后续任何访问都将导致错误。5. 结论显式 BO unmap 不暂停 queue 是基于责任分离的设计应用程序的责任在 unmap 前通过 fence 确保 GPU 不再访问驱动的责任提供防护queue_refcount拒绝关键资源 unmap和可见性保证TLB flush但不为用户的编程错误做全局同步硬件不兜底无法恢复显式 BO 的非法访问这与驱逐路径形成对比——驱逐是系统被动事件驱动必须通过暂停 queue 来主动保护正确性。这种设计使得高频的显式 unmap 操作避免了昂贵的全局同步开销提升整体系统性能。6. 遗留问题xnack on 模式下是否可以留待验证。