尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

8.4 依赖与同步点:in-fence 收集与 out-fence 生成

8.4 依赖与同步点:in-fence 收集与 out-fence 生成 8.2 把用户递交的 chunk 翻译成了作业骨架8.3 又为其引用的所有 BO 完成了加锁与驻留。此刻 job 已经「有形有料」但还不能贸然交给调度器——因为一次提交往往并非孤立执行它可能必须等待其他提交先完成例如上一帧的渲染结果、另一个队列写入的纹理也可能被别人等待后续提交要读它的输出。本节聚焦第 ③ 阶段中依赖类 chunk 的解析与第 ⑦ 阶段amdgpu_cs_sync_rings讲清内核如何收集本次提交的入方向依赖in-fence、又如何为其准备出方向信号out-fence。1. 两个方向、两种同步围绕一次提交的同步关系可以拆成两个方向in-fence前向依赖本次提交的 job 开始执行前必须等待的一组 fence。只有它们全部 signaljob 才被允许上 ring。out-fence完成信号本次提交完成时对外发出的 fence供后续提交或 CPU 侧等待。而 in-fence 又按「谁来指定」分为两类这正对应第六章讲过的两种同步策略类别来源对应章节显式依赖用户在 chunk 中明确列出要等待的 fence / syncobj6.4 显式同步隐式依赖从本次引用的 BO 的dma_resv上自动读取已挂载的 fence6.3 隐式同步 dma_resv内核用 parser 上的两个字段分别承接这两个方向p-syncamdgpu_sync容器汇集所有 in-fence无论显式还是隐式最终统一压入 job 作为调度依赖。p-post_deps登记那些需要用本次 out-fence 去「点亮」的 syncobj待提交成功后再逐一 signal。out-fence 分发in-fence 收集 → p-sync显式DEPENDENCIES chunk(ctxringseq)p-sync显式SYNCOBJ_IN / TIMELINE_WAIT隐式各 BO 的 dma_resv压入 job 作为调度依赖amdgpu_sync_push_to_jobjob 完成 → finished fencectx_add_fence → seq → cs-out.handlesignal p-post_deps 中的 syncobj其中「显式依赖的解析」与「out-fence 目标的登记」发生在第 ③ 阶段的pass2「隐式依赖的收集与依赖入队」发生在第 ⑦ 阶段的sync_rings「out-fence 的真正生成与分发」发生在第 ⑧ 阶段的submit。下面按此顺序展开。2. 显式 in-fencepass2 解析依赖类 chunk③回顾 8.2pass2遍历所有 chunkIB 分支已在 8.2 讲过本节接手它的依赖类分支。这些分支的共同归宿都是把解析出的 fence 通过amdgpu_sync_fence(p-sync, fence, ...)塞进p-sync。2.1 fence-based 依赖DEPENDENCIESamdgpu_cs_p2_dependencies处理AMDGPU_CHUNK_ID_DEPENDENCIES。用户以「(ctx_id, ip_type, ip_instance, ring, handle)」五元组指明「我要等某个上下文某条 ring 上序号为 handle 的那次提交」ctxamdgpu_ctx_get(fpriv,deps[i].ctx_id);amdgpu_ctx_get_entity(ctx,deps[i].ip_type,deps[i].ip_instance,deps[i].ring,entity);fenceamdgpu_ctx_get_fence(ctx,entity,deps[i].handle);/* 按 seq 取出目标 fence */...if(chunk-chunk_idAMDGPU_CHUNK_ID_SCHEDULED_DEPENDENCIES){/* 取 scheduled 而非 finished允许流水线重叠 */s_fenceto_drm_sched_fence(fence);fencedma_fence_get(s_fence-scheduled);...}ramdgpu_sync_fence(p-sync,fence,GFP_KERNEL);这里的handle正是被依赖的那次提交返回给用户的 seq见第 5 节 out-fence 的cs-out.handle——依赖链就此闭合。值得注意的是SCHEDULED_DEPENDENCIES变体它取目标 fence 的scheduled已被调度器排上但未必执行完而非finished。这是一种流水线优化——后继作业只需等前驱「排上队」即可开始准备无需苦等其彻底完成前提是二者在同一条 ring 上天然保序。2.2 syncobj 依赖SYNCOBJ_IN 与 TIMELINE_WAIT两者共用辅助函数amdgpu_syncobj_lookup_and_add区别只在于「取哪个点的 fence」rdrm_syncobj_find_fence(p-filp,handle,point,flags,fence);...ramdgpu_sync_fence(p-sync,fence,GFP_KERNEL);amdgpu_cs_p2_syncobj_inSYNCOBJ_IN二元 syncobj固定point 0——取该 syncobj 当前持有的 fence。amdgpu_cs_p2_syncobj_timeline_waitSYNCOBJ_TIMELINE_WAITtimeline syncobj按用户给定的point取对应时间点的 fence。关于 syncobj 与 timeline 的内核实现可回看 6.4.2 drm_syncobj。综上pass2 中所有显式 in-fence 的解析可归纳为一张表——终点都是p-syncchunk 类型处理函数依赖来源DEPENDENCIES/SCHEDULED_DEPENDENCIESp2_dependencies另一提交的 fence按 ctxringseq 取后者取 scheduledSYNCOBJ_INp2_syncobj_in二元 syncobj 当前 fenceSYNCOBJ_TIMELINE_WAITp2_syncobj_timeline_waittimeline syncobj 指定 point 的 fence3. 显式 out-fence 目标登记待 signal 的 syncobj③对称地用户也可要求「本次提交完成后去点亮某些 syncobj」。这类 out chunk 在 pass2 里只做登记把目标 syncobj 存进p-post_deps真正 signal 留到第 ⑧ 阶段staticintamdgpu_cs_p2_syncobj_out(structamdgpu_cs_parser*p,...){...p-post_depskmalloc_array(num_deps,sizeof(*p-post_deps),GFP_KERNEL);for(i0;inum_deps;i){p-post_deps[i].syncobjdrm_syncobj_find(p-filp,deps[i].handle);p-post_deps[i].chainNULL;/* 二元无需 chain */p-post_deps[i].point0;p-num_post_deps;}}amdgpu_cs_p2_syncobj_outSYNCOBJ_OUT二元 syncobjchain NULL、point 0。amdgpu_cs_p2_syncobj_timeline_signalSYNCOBJ_TIMELINE_SIGNALtimeline syncobj若point ! 0则预先dma_fence_chain_alloc()备好一个 chain 节点timeline 语义要求把新 fence 以「点」的形式串上时间线参见 6.1.5。可跳过的实现细节第 2、3 节的多个p2_*函数形态高度相似本质都是「遍历数组 → 查出 fence/syncobj → 存入p-sync或p-post_deps」。若只关心主干记住「显式 in-fence 汇入p-sync显式 out 目标登记进p-post_deps」即可各 chunk 子类型的差异可在需要时再回看。4. 隐式 in-fence 与依赖入队sync_rings⑦BO 加锁完成后8.3第 ⑦ 阶段amdgpu_cs_sync_rings补齐隐式依赖并把p-sync里累积的全部 fence 正式压入 job。它依次做三件事4.1 等待本 entity 的上一次提交ramdgpu_ctx_wait_prev_fence(p-ctx,p-entities[p-gang_leader_idx]);这一步保证同一调度实体上的提交按序推进兼作对 ctx fence 环的流控——避免尚有在途提交时就覆盖其记录。4.2 从每个 BO 的 dma_resv 收集隐式依赖drm_exec_for_each_locked_object(p-exec,index,obj){structdma_resv*resvgem_to_amdgpu_bo(obj)-tbo.base.resv;sync_modeamdgpu_bo_explicit_sync(bo)?AMDGPU_SYNC_EXPLICIT:AMDGPU_SYNC_NE_OWNER;ramdgpu_sync_resv(p-adev,p-sync,resv,sync_mode,fpriv-vm);}遍历 8.3 锁定的每个 BO把其dma_resv上已有的 fence 纳入p-sync。sync_mode是关键AMDGPU_SYNC_NE_OWNER默认只同步不属于本 VM的 fenceNot-Equal-OWNER。同一进程在同一 VM 上对某 BO 的先前写入本就由 ring 顺序保证可见无需再插入依赖——借此避免自我串行化是重要的性能优化。AMDGPU_SYNC_EXPLICIT当 BO 被标记为「显式同步」时隐式路径不再自动为它建立依赖一切交由用户显式指定。dma_resv上 fence 的读/写用途分层DMA_RESV_USAGE_*决定了哪些 fence 需要被等待细节见 6.3.1 dma_resv_usage。4.3 把依赖压入 job并为同 ring 依赖插入流水线同步for(i0;ip-gang_size;i)amdgpu_sync_push_to_job(p-sync,p-jobs[i]);/* 全部 in-fence → 各 job 的调度依赖 */schedp-gang_leader-base.entity-rq-sched;while((fenceamdgpu_sync_get_fence(p-sync))){s_fenceto_drm_sched_fence(fence);if(!s_fence||s_fence-sched!sched){/* 非同一调度器 ring跳过 */dma_fence_put(fence);continue;}/* 同一 ring 上的依赖额外挂到 gang_leader-explicit_sync */amdgpu_sync_fence(p-gang_leader-explicit_sync,fence,GFP_KERNEL);}amdgpu_sync_push_to_job把p-sync中的每个 fence 登记为 job 的调度依赖——调度器要等这些 fence 全部 signal 才会真正发射该 job。随后那段while循环单独处理「依赖恰好落在与本提交同一条 ring 上」的情况把它们额外收进gang_leader-explicit_sync以便在执行前插入一次流水线同步pipeline sync确保上一作业的缓存刷新、结果对下一作业可见——同一 ring 上先后执行的两个作业之间硬件并不天然保证这种可见性。5. out-fence 的生成与分发⑧ 的依赖部分依赖备齐后真正的 out-fence 在第 ⑧ 阶段amdgpu_cs_submit中诞生。submit 的完整临界区drm_sched_job_arm、dma_resv回写、userptr 失效重试等是 8.5 的主题这里只截取与「out-fence」直接相关的三步p-fencedma_fence_get(leader-base.s_fence-finished);/* ① out-fence 领队的 finished fence */...seqamdgpu_ctx_add_fence(p-ctx,p-entities[p-gang_leader_idx],p-fence);/* ② 登记进 ctx得 seq */amdgpu_cs_post_dependencies(p);/* ③ 用 out-fence 点亮 post_deps 中的 syncobj */...cs-out.handleseq;/* seq 返回用户态 */三步含义out-fence 的本体是 gang 领队 job 的调度器finishedfence——它在整个 gang 执行完毕时 signal。amdgpu_ctx_add_fence把该 fence 登记进 ctx 的 fence 环并返回一个序号seqseq经cs-out.handle回传用户态。日后用户可凭此 seq 通过amdgpu_cs_wait_ioctl等待或经amdgpu_cs_fence_to_handle_ioctl转成 sync_file fd——这也正是第 2.1 节里DEPENDENCIES依赖所引用的那个handle。amdgpu_cs_post_dependencies遍历第 3 节登记的p-post_deps用刚诞生的p-fence去点亮每个目标 syncobjif(post_deps[i].chainpost_deps[i].point)drm_syncobj_add_point(syncobj,chain,p-fence,point);/* timeline串上时间点 */elsedrm_syncobj_replace_fence(syncobj,p-fence);/* 二元替换当前 fence */至此入方向等待谁与出方向谁来等我双双闭合本次提交的 job 已挂好全部依赖其完成信号也已既能被 seq 查询、又能点亮用户指定的 syncobj。6. 形象小结一次「排班与交接」若把每次提交看作车间里的一道工序本阶段做的正是排班与交接环节阶段动作列出「我要等哪些前道工序」——点名指定③ pass2解析DEPENDENCIES/SYNCOBJ_IN/TIMELINE_WAIT→p-sync登记「我完工后要通知谁」③ pass2SYNCOBJ_OUT/TIMELINE_SIGNAL→p-post_deps自动补上「我碰过的料还有谁在用」⑦ sync_rings从各 BOdma_resv收集隐式依赖 →p-sync把等待清单钉到工单上⑦ sync_ringspush_to_job 同 ring 流水线同步发出完工凭据、通知下游⑧ submit领队 finished fence →seq/cs-out.handle 点亮 post_deps设计上的两条主线值得记住其一所有 in-fence显式 隐式汇于一处p-sync再统一压入 job让调度器只面对一份干净的依赖清单其二out-fence 一体两面——对内是可按 seq 查询的 ctx fence对外是可点亮 syncobj 的信号源二者同为领队的finishedfence。可跳过的实现细节只关心主干的读者记住三句话即可——(1) 显式依赖来自 chunk、隐式依赖来自 BO 的dma_resv两者都进p-sync并压给 job(2)NE_OWNER模式跳过本 VM 自己的 fence避免自我串行(3) out-fence 就是领队 job 的 finished fence既登记为 seq 返回用户、又用来点亮 out-syncobj。依赖与信号均已就位job 只差最后一步——被真正「武装」并推入调度器。下一节 8.5 将进入第 ⑧ 阶段amdgpu_cs_submit剖析drm_sched_job_arm、notifier_lock保护下的dma_resvfence 回写、userptr 失效导致的-EAGAIN重试以及最终的drm_sched_entity_push_job。
返回列表