
8.1 勾勒了amdgpu_cs_ioctl的九步全景8.2 讲解了前端的 chunk 解析与作业构建第 ①—③ 阶段。本节承接其后聚焦第 ④ 阶段amdgpu_cs_parser_bos——整条路径中最关键的节点之一剖析内核如何在一致的加锁保护下将本次提交涉及的所有 BO 拉回到 GPU 可访问的内存域并为后续的页表更新与作业入队奠定基础。一次命令提交要正确执行前提是其引用的每一个 BO 都满足两个条件其一被稳定地锁定使得从校验到入队的整个窗口内没有其他线程能够驱逐或搬移它其二处于合法的内存域VRAM 或 GTT即已完成驻留resident并具备有效的 GPU 映射。amdgpu_cs_parser_bos正是同时达成这两点的环节其内部由三种机制协同完成——drm_exec负责加锁、ttm_bo_validate负责回迁、dma_resv则作为二者共同操作的锁与栅栏容器。1. drm_exec本阶段如何锁定全部 BO命令提交需要同时锁定的 BO 往往数以百计且不同提交所引用的集合彼此交叠。若以不一致的顺序逐个加锁极易形成ABBA 死锁。DRM 将「无死锁地一次性锁定一组dma_resv」这一通用问题抽象为drm_exec框架——其底层基于ww_mutexwait/wound 互斥锁DRM 的reservation_ww_class采用 Wait-Die 算法把「预留—竞争—回退—重试」的完整逻辑封装为一组宏。该框架的原理与实现属于同步机制范畴本专栏在 6.3.3 drm_exec — 多 BO 无死锁加锁框架 中已专文详述本节仅从提交流程的应用视角说明它如何被使用。在展开加锁前parser_bos首先取得一把贯穿全函数的外层锁mutex_lock(p-bo_list-bo_list_mutex)以防 bo_list 在整个提交期间被并发修改该锁在函数返回无论成功或失败时释放。随后进入drm_exec锁定所有对象的核心结构如下drm_exec_until_all_locked(p-exec){ramdgpu_vm_lock_pd(fpriv-vm,p-exec,1p-gang_size);drm_exec_retry_on_contention(p-exec);if(unlikely(r))gotoout_free_user_pages;amdgpu_bo_list_for_each_entry(e,p-bo_list){rdrm_exec_prepare_obj(p-exec,e-bo-tbo.base,TTM_NUM_MOVE_FENCESp-gang_size);drm_exec_retry_on_contention(p-exec);if(unlikely(r))gotoout_free_user_pages;e-bo_vaamdgpu_vm_bo_find(vm,e-bo);}if(p-uf_bo){rdrm_exec_prepare_obj(p-exec,p-uf_bo-tbo.base,TTM_NUM_MOVE_FENCESp-gang_size);drm_exec_retry_on_contention(p-exec);...}}就本阶段而言只需把握drm_exec的三个使用要点其内部回退机制见 6.3.3构件在 parser_bos 中的作用drm_exec_until_all_locked(p-exec)外层循环若循环体内任一对象加锁遭遇竞争则回退所有已持有的锁并从头重执直至一次性锁定全部对象amdgpu_vm_lock_pd首先锁定该进程 VM 的页目录 BOPD——第 ⑥ 阶段页表更新将修改它故必须一并纳入锁集drm_exec_prepare_obj(exec, bo-tbo.base, N)锁定单个 BO 的dma_resv并为其预留 N 个 fence slotTTM_NUM_MOVE_FENCES gang_sizedrm_exec_retry_on_contention检测到锁竞争时跳回外层循环头触发回退p-uf_bouser-fence BO若提交请求将完成信号写入某 BO则该 BO 也须锁定循环体内在锁定每个 BO 的同时还通过amdgpu_vm_bo_find查出其bo_va映射句柄并缓存至e-bo_va供第 ⑥ 阶段vm_handling直接复用省去届时的重复查找。值得强调的是fence slot 预留drm_exec_prepare_obj的第三参数。第 ⑧ 阶段submit在notifier_lock保护下回写 fence 时禁止任何内存分配因此所需的dma_resvfence slot 必须在此刻提前预留妥当。gang 提交一次提交多个 job会为每个 job 各占一个 slot故预留数含gang_size。bo_list 各 BOVM 页目录drm_execparser_bosbo_list 各 BOVM 页目录drm_execparser_bosalt[发生竞争]loop[每个 BO]loop[until_all_locked]全部对象一次性锁定成功后退出循环amdgpu_vm_lock_pd1锁定 PD 的 dma_resv2drm_exec_prepare_obj(bo, 预留 N slot)3锁定 dma_resv4retry_on_contention → 回退全部锁重来52. userptr加锁之前的页面抓取对于用户指针型 BOuserptr即以进程虚拟地址注册、无独立显存后备的 BOparser_bos在进入drm_exec加锁循环之前先行抓取其后备页面amdgpu_bo_list_for_each_userptr_entry(e,p-bo_list){e-rangeamdgpu_hmm_range_alloc(NULL);ramdgpu_ttm_tt_get_user_pages(bo,e-range);...for(i0;ibo-tbo.ttm-num_pages;i)if(bo-tbo.ttm-pages[i]!hmm_pfn_to_page(...))userpage_invalidatedtrue;e-user_invalidateduserpage_invalidated;}关键点HMM range通过amdgpu_hmm_range_alloc分配一段 HMM range再由amdgpu_ttm_tt_get_user_pages走hmm_range_fault路径抓取当前有效的物理页。HMM 使内核得以在不 pin 住页面的前提下追踪其变化——若 CPU 侧发生迁移或换出mmu_notifier会通知驱动。失效标记将新抓取的页与 BO 当前记录的页逐一比对若不一致则置user_invalidated留待加锁之后处理。顺序考量get_user_pages可能触发缺页、睡眠乃至内存回收故不能在持有dma_resv锁时执行因此该步骤必须前置于drm_exec循环。加锁完成后才对被标记失效的 userptr 做校验与页面回填amdgpu_bo_list_for_each_userptr_entry(e,p-bo_list){usermmamdgpu_ttm_tt_get_usermm(e-bo-tbo.ttm);if(usermmusermm!current-mm)return-EPERM;/* 禁止跨进程引用他人的 userptr */if(amdgpu_ttm_tt_is_userptr(...)e-user_invalidated){amdgpu_bo_placement_from_domain(e-bo,AMDGPU_GEM_DOMAIN_CPU);ttm_bo_validate(e-bo-tbo,e-bo-placement,ctx);amdgpu_ttm_tt_set_user_pages(e-bo-tbo.ttm,e-range);}}即先做归属校验userptr 只能由注册它的进程引用再对失效者重新绑定新页。此处 userptr 与 SVM/HMM 的缺页处理同源详见第十章。3. ttm_bo_validate把 BO 拉回合法内存域锁定就绪后parser_bos依次校验所有对象的驻留状态。真正的搬移由amdgpu_cs_bo_validate完成其内部封装ttm_bo_validatestaticintamdgpu_cs_bo_validate(void*param,structamdgpu_bo*bo){structttm_operation_ctxctx{.interruptibletrue,.no_wait_gpufalse,.resvbo-tbo.base.resv,};...if(bo-tbo.pin_count)return0;/* 已 pin无须搬移 *//* 依据 move 配额选择目标域 */if(p-bytes_movedp-bytes_moved_threshold...)domainbo-preferred_domains;elsedomainbo-allowed_domains;retry:amdgpu_bo_placement_from_domain(bo,domain);rttm_bo_validate(bo-tbo,bo-placement,ctx);p-bytes_movedctx.bytes_moved;if(r-ENOMEMdomain!bo-allowed_domains){domainbo-allowed_domains;/* 降级重试 */gotoretry;}returnr;}要点move 节流throttlingp-bytes_moved与bytes_moved_threshold构成一次提交的搬移预算。预算充足时使用 BO 的preferred_domains通常为高带宽的 VRAM预算耗尽则退让至allowed_domains含 GTT以避免单次提交把整块 VRAM 耗尽而饿死其他进程。可见 VRAMbytes_moved_vis另有独立配额。域降级重试若首选域因-ENOMEM无法容纳则降级到allowed_domains再试一次——这保证了即便 VRAM 紧张提交仍能以 GTT 驻留的方式成功而非直接失败。ctx.resv将ttm_bo_validate的操作绑定到该 BO 已锁定的dma_resv使 TTM 内部的驱逐等待与本阶段的锁保持一致。parser_bos分两轮触发校验先经amdgpu_vm_validate校验 VM 自身的页表相关 BO再遍历drm_exec已锁定的每个对象逐一amdgpu_cs_bo_validateramdgpu_vm_validate(p-adev,fpriv-vm,NULL,amdgpu_cs_bo_validate,p);...drm_exec_for_each_locked_object(p-exec,index,obj){ramdgpu_cs_bo_validate(p,gem_to_amdgpu_bo(obj));...}4. 三者协同的全景至此可以看清drm_exec、ttm_bo_validate、dma_resv三者的分工与耦合机制职责在本阶段的体现dma_resv每个 BO 的锁 fence 容器既是drm_exec锁定的对象也是ttm_bo_validate依据的ctx.resv还是 ⑧ 阶段回写 fence 的落点drm_exec无死锁地一次性锁定全部 BO 的dma_resvuntil_all_lockedprepare_objretry_on_contention并预留 fence slotttm_bo_validate将 BO回迁至合法内存域并建立驻留由amdgpu_cs_bo_validate封装配合 move 节流与域降级三者以dma_resv为交汇点drm_exec锁定它、ttm_bo_validate在它的保护下搬移、submit最终向它回写完成 fence。这一设计使得锁定—验证—回写在同一把锁的语义下前后连贯无缝衔接。锁定ctx.resv 保护下搬移add_fence 至预留 slot预留 slot 供驻留就绪后交由BO 的 dma_resv锁 fence 容器drm_exec无死锁加锁ttm_bo_validate回迁至合法域submit ⑧回写完成 fence5. 收尾user-fence BO 与 GDS 资源校验通过后parser_bos完成两项收尾工作user-fence BO 的 GART 映射若存在uf_bo调用amdgpu_ttm_alloc_gart为其建立 GART 映射并将其 GPU 偏移累加到gang_leader-uf_addr使 GPU 完成时能将信号写入该地址。GDS / GWS / OA 全局资源通过amdgpu_job_set_resources把 bo_list 中的 GDSGlobal Data Share、GWSGlobal Wave Sync、OAOrdered Append对象绑定到各 job。随后parser_bos返回控制权交回amdgpu_cs_ioctl主流程进入第 ⑤ 阶段patch_jobs。错误回退上述任一步骤失败均跳转至out_free_user_pages——释放各 userptr 的 HMM range、解锁bo_list_mutex后返回而drm_exec已持有的 BO 锁则由其框架在上层统一 backoff与 8.1 所述整体回滚一致确保不残留半锁定状态。6. 小结amdgpu_cs_parser_bos是命令提交中锁定 验证的核心阶段同时保证 BO 被稳定锁定且处于合法内存域drm_exec无死锁地一次性锁定全部dma_resv并为后续回写 fence预留 slotuserptr 型 BO 在加锁之前经 HMM range 抓取页面并标记失效加锁后再做归属校验与页面回填ttm_bo_validate经amdgpu_cs_bo_validate封装在 move 节流与域降级策略下将 BO 回迁至 VRAM 或 GTT三种机制以dma_resv为交汇点协同使锁定—验证—回写在同一锁语义下前后连贯。下一节 8.4 将进入依赖与同步点阐述命令提交如何收集 in-fence显式依赖 syncobj / 隐式依赖dma_resv并生成 out-fence对应第 ③ ⑦ 阶段的依赖 chunk 解析与amdgpu_cs_sync_rings。至于第 ⑥ 阶段amdgpu_cs_vm_handling涉及的 GPUVM 页表更新属于 GPU 地址空间管理范畴留待第九章 GPUVM 专门展开。