6.1.4 dma_fence_array — 多 fence 聚合机制
一次操作往往依赖多个异步结果同时就绪一次提交等待多块 BO 的写完成、一个drm_syncobj收集多条 in-fence、dma_resv把多个 fence 合成一个对外句柄。这些场景需要把「一组 fence」包装成「一个 fence」让上层用统一的dma_fence接口去等待。dma_fence_array就是这个聚合原语它本身是一个dma_fence内部持有一组子 fence当子 fence全部或任一就绪时它自己随之 signal。本篇我们关注这个功能的数据结构、创建接口、signal 传播机制。1. 为什么需要聚合dma_fence的消费接口dma_fence_wait、dma_fence_add_callback、挂入dma_resv都以单个fence 为单位。当一个逻辑操作依赖 N 个 fence 时若不聚合调用方就得自己维护「还差几个未完成」的计数、自己串联回调重复实现同一套逻辑。dma_fence_array把这套逻辑收进一个标准dma_fence子类对上它是一个普通 fence可被wait/add_callback/ 挂入dma_resv对内它跟踪 N 个子 fence 的完成情况按「全部就绪」或「任一就绪」的策略决定自己何时 signal。典型使用者包括dma_resv_get_singleton()N 个 fence 合成一个、drm_syncobj、i915/xe 的提交路径以及经dma_fence_unwrap_merge()合并多个容器的场景。2. 数据结构structdma_fence_array{structdma_fencebase;// 自身也是一个 dma_fencespinlock_tlock;// base.lock 指向它unsignednum_fences;// 子 fence 总数atomic_tnum_pending;// 仍未 signal 的子 fence 计数structdma_fence**fences;// 子 fence 指针数组所有权归 arraystructirq_workwork;// signal 收尾放到 irq_work 中执行structdma_fence_array_cbcallbacks[]__counted_by(num_fences);};几个要点base让它天然是一个dma_fencebase.lock指向内嵌的lock。num_pending是聚合的核心状态每个子 fence signal 时原子递减减到 0 触发自身 signal。它的初值由signal_on_any决定见 §3。callbacks[]是柔性数组与num_fences对齐——为每个子 fence 预留一个回调结构避免在 signaling 路径再分配内存。fences数组的所有权移交给 arrayrelease 时对每个子 fencedma_fence_put()再kfree(fences)。dma_fence_array_cb把回调与所属 array 关联起来structdma_fence_array_cb{structdma_fence_cbcb;structdma_fence_array*array;};类型判定与转换staticinlinebooldma_fence_is_array(structdma_fence*fence);// ops dma_fence_array_opsstaticinlinestructdma_fence_array*to_dma_fence_array(structdma_fence*fence);3. 创建接口structdma_fence_array*dma_fence_array_alloc(intnum_fences);voiddma_fence_array_init(structdma_fence_array*array,intnum_fences,structdma_fence**fences,u64 context,unsignedseqno,bool signal_on_any);structdma_fence_array*dma_fence_array_create(intnum_fences,structdma_fence**fences,u64 context,unsignedseqno,bool signal_on_any);dma_fence_array_create是常用入口它内部先dma_fence_array_alloc()一次kzalloc用struct_size()把柔性数组callbacks[num_fences]一并算进去再调用dma_fence_array_init()。分配失败返回NULL。调用方需自行分配fences数组并填入要聚合的子 fence该数组的所有权被 array 接管无需自己释放。signal_on_any决定聚合语义体现在num_pending的初值上signal_on_anynum_pending初值语义falsenum_fences全部子 fence signal 后array 才 signalANDtrue1任一子 fence signal 后array 即 signalORcontext/seqno是 array 作为一个dma_fence自身的 context 与序号context/seqno 语义见 6.1.1。聚合 fence 通常不属于任何硬件 timeline调用方一般用dma_fence_context_alloc()分配一个专用 context 传入。OR 语义下的生命周期陷阱signal_on_anytrue时 array 在第一个子 fence signal 时即对外 signal但 §4.1 的enable_signaling已为每个子 fence 挂了回调并各持一份 array 引用这些回调不会在提前 signal 时被摘除。因此 array 虽已 signal要等所有子 fence 都 signal、逐个释放引用后才真正被回收。使用方不应假设「array 一 signal子 fence 与 array 就能马上释放」。allocinit的两段式拆分对应了「预分配 关键路径不失败」的模式dma_fence_array_init()不做任何分配专供在reclaim 路径或 dma_fence signaling 上下文中初始化一个预分配好的 array这类上下文禁止会触发回收的分配机理见附录 B.6。init的两条断言值得注意WARN_ON(!num_fences||!fences);// 不允许空聚合...while(num_fences--)WARN_ON(dma_fence_is_container(fences[num_fences]));// 子 fence 不能是容器第二条是本篇的关键约束§6 详述。4. signal 传播机制array 自己何时 signal取决于子 fence 的完成事件如何汇总。整条链路围绕num_pending展开。4.1 enable_signaling为每个子 fence 挂回调当有消费者首次要等待这个 array 时框架调用dma_fence_array_ops.enable_signalingenable_signaling的通用机制见 6.1.3。它遍历所有子 fence为每个挂上dma_fence_array_cb_func回调for(i0;iarray-num_fences;i){cb[i].arrayarray;dma_fence_get(array-base);// 每挂一个回调给 array 自身加一次引用if(dma_fence_add_callback(array-fences[i],cb[i].cb,dma_fence_array_cb_func)){/* add_callback 返回非 0该子 fence 已 signal */dma_fence_array_set_pending_error(array,array-fences[i]-error);dma_fence_put(array-base);if(atomic_dec_and_test(array-num_pending)){dma_fence_array_clear_pending_error(array);returnfalse;// 全部已就绪无需异步等待}}}要点额外引用array 可能在所有回调完成前就 signal因此每挂一个回调都给array-base加一次引用防止过早释放。引用在回调触发或 array signal 时释放。已 signal 的子 fencedma_fence_add_callback对已 signal 的 fence 返回非 0此时直接递减num_pending若递减到 0说明所有子 fence 已就绪返回false表示无需启用异步信号。4.2 回调与收尾num_pending 归零后 signal每个子 fence signal 时触发dma_fence_array_cb_funcstaticvoiddma_fence_array_cb_func(structdma_fence*f,structdma_fence_cb*cb){structdma_fence_array*array...;dma_fence_array_set_pending_error(array,f-error);if(atomic_dec_and_test(array-num_pending))irq_work_queue(array-work);// 最后一个调度收尾elsedma_fence_put(array-base);// 非最后一个释放本回调持有的引用}减到 0 的那个回调不直接 signal而是irq_work_queue把收尾推到 irq_work 上下文执行staticvoidirq_dma_fence_array_work(structirq_work*wrk){dma_fence_array_clear_pending_error(array);dma_fence_signal(array-base);// 在这里真正 signaldma_fence_put(array-base);// 释放最后一个引用}用 irq_work 的原因子 fence 的回调可能在持有其 spinlock 的上下文中被调用直接在此调用dma_fence_signal()会去拿 array 自己的锁、并触发 array 的下游回调容易造成锁序问题或深层递归。irq_work 把 signal 收尾切换到一个干净的上下文规避这类风险。未归零归零子 fence signaldma_fence_array_cb_funcatomic_dec_and_test(num_pending)dma_fence_put(base)释放该回调引用irq_work_queue(work)irq_dma_fence_array_workdma_fence_signal(base)唤醒 array 的下游等待者4.3 signaled 快路径dma_fence_is_signaled()会先走dma_fence_array_ops.signaled做无锁快查num_pendingatomic_read_acquire(array-num_pending);if(test_bit(DMA_FENCE_FLAG_ENABLE_SIGNAL_BIT,array-base.flags)){/* 已启用异步信号直接看 num_pending 是否 0 */returnnum_pending0;}/* 尚未启用主动轮询子 fence */for(i0;iarray-num_fences;i)if(dma_fence_is_signaled(array-fences[i])!--num_pending)returntrue;returnfalse;先用atomic_read_acquire读num_pending再判ENABLE_SIGNAL位acquire语义与enable_signaling里的atomic_dec_and_test配对避免与并发的使能过程发生撕裂读。5. 错误传播array 的base.error汇总子 fence 的错误。实现用一个占位值PENDING_ERROR (1)表示「尚未决定」init时把base.error置为PENDING_ERROR任一子 fence 带错误时dma_fence_array_set_pending_error()用cmpxchg把第一个报告的错误写入仅在仍为PENDING_ERROR时从而只保留首个错误signal 收尾时dma_fence_array_clear_pending_error()把仍是占位值的error归零。效果array 对外报告其子 fence 中首个出现的错误若都成功则error 0。6. 关键约束容器不能嵌套init结尾的断言禁止子 fence 本身是容器dma_fence_array或dma_fence_chainwhile(num_fences--)WARN_ON(dma_fence_is_container(fences[num_fences]));原因是递归深度如果 array 里再嵌 array对聚合 fence 的遍历、signal、release 等操作就会沿嵌套层层递归在深层嵌套下可能耗尽内核栈。正确做法是由调用方先把嵌套结构**摊平flatten**成一层扁平数组再构造 array。内核提供了现成的摊平合并入口dma_fence_unwrap_merge()fencedma_fence_unwrap_merge(fence_a,fence_b,...);它深度展开每个参数中的dma_fence_array/dma_fence_chain去重后合并成一个扁平的dma_fence_array。amdgpu 在 VM 更新后合并vm-last_update与bo_va-last_pt_update时正是这样用的fencedma_fence_unwrap_merge(vm-last_update,bo_va-last_pt_update);if(!fence){/* OOM 兜底退化为逐个同步等待 */dma_fence_wait(vm-last_update,false);dma_fence_wait(bo_va-last_pt_update,false);fencedma_fence_get_stub();}dma_resv_get_singleton()见 6.3.2在需要把多个 fence 合成一个时底层也是构造一个扁平的dma_fence_array。7. 遍历接口按是否深入容器提供两套迭代浅层遍历——只遍历一个 array 的直接子 fence若head不是 array则只迭代它自身一个structdma_fence*dma_fence_array_first(structdma_fence*head);structdma_fence*dma_fence_array_next(structdma_fence*head,unsignedintindex);dma_fence_array_for_each(fence,index,head){/* fence 是 head 的第 index 个子 fence */}深度遍历——递归展开dma_fence_array与dma_fence_chain把所有底层 fence 逐个交出定义在dma-fence-unwrap.hdma_fence_unwrap_for_each(fence,cursor,head){/* fence 是 head 中所有容器展开后的每一个叶子 fence */}辅助判断dma_fence_match_context()检查一个 fence或 array 中全部子 fence是否都来自同一 context。8. 小结dma_fence_array用「一个 fence 代表一组 fence」的方式把多依赖等待收敛成标准的dma_fence语义数据结构内嵌base使其本身是 fencenum_pending跟踪未就绪计数柔性callbacks[]为每个子 fence 预置回调。聚合语义signal_on_any决定「全部就绪」AND还是「任一就绪」OR映射为num_pending的初值。signal 传播enable_signaling为每个子 fence 挂回调回调递减num_pending归零后经 irq_work 完成自身 signal——用 irq_work 规避锁序与递归风险。两段式创建allocinit支持在 reclaim / signaling 上下文用预分配对象初始化对应附录 B.6 的预分配模式。不可嵌套子 fence 不能是容器需先用dma_fence_unwrap_merge()摊平避免递归耗尽内核栈。下一篇6.1.5转向另一个组合原语dma_fence_chain——它以 timeline 语义把 fence 串成链是drm_syncobjtimeline 的底层支撑。

相关新闻

螺丝漏打、垫片漏装、排线反接?传统质检管不住,合米科技AI SOP视觉系统怎么做到的?

螺丝漏打、垫片漏装、排线反接?传统质检管不住,合米科技AI SOP视觉系统怎么做到的?

📌 摘要 制造企业的产线上,几乎每条线都挂着 SOP——纸质文件贴在工位旁,或电子看板循环播放。但管理者心里都清楚:员工"看了"不等于"做对","做完了"也不等于"没漏步"。深圳…

2026/11/1 9:32:14 阅读更多 →
层次分析法实战:从主观判断到科学决策的量化指南

层次分析法实战:从主观判断到科学决策的量化指南

1. 从“拍脑袋”到“结构化”:为什么我们需要层次分析法在项目评审、方案选择、风险评估这些日常工作中,我们常常面临一个难题:如何把一堆定性的、模糊的、甚至相互矛盾的判断,转化成一个可以量化的、有说服力的决策依据&#xff…

2026/11/1 8:55:30 阅读更多 →
从出货量到有效工时:具身智能行业的新标尺

从出货量到有效工时:具身智能行业的新标尺

具身智能行业的热闹,已经从发布会现场转移到了仓库、工厂和展厅的角落。如果你近期关注过宇树科技和智元机器人的新闻,会发现两个信号非常明确:一边是宇树的上市节奏明显加快,一边是智元在出货量上走到了行业前列。这两个信号放在…

2026/10/24 2:32:38 阅读更多 →

最新新闻

Electron桌面应用开发实战:从环境搭建到打包发布全流程

Electron桌面应用开发实战:从环境搭建到打包发布全流程

做前端这些年,我大部分时间都在跟浏览器标签页打交道,直到第一次把桌面应用跑起来,才发现原来自己一直被限制在地址栏和刷新按钮围起来的框框里。Electron这个词,最开始是在某段时间频繁出现在同事的讨论里,一个用HTML…

2026/11/1 0:24:43 阅读更多 →
滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

简介:亚太天能T109滑盖智能锁产品说明书是一份PDF格式的官方技术文档,主要面向智能锁安装人员、终端用户及售后维护者,帮助快速掌握产品安装、配置与常见故障处理。全包仅1个PDF文件,大小4.37MB,内容结构清晰&#xff…

2026/10/31 10:20:19 阅读更多 →
PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

上个月我在调试一台高速AGV的驱动系统,整车要求空载跑到8m/s,电机转速已经拉到额定点,驱动器的输出电压也顶到了母线电压极限附近。再往上踩速度指令,转速纹丝不动,三相电流反而开始震荡。那会儿我的第一反应就是&…

2026/10/31 10:20:23 阅读更多 →
C++核心知识点:从内存管理到STL的实战指南

C++核心知识点:从内存管理到STL的实战指南

C 这门语言我前前后后折腾了十几年,用它写过嵌入式驱动、做过游戏服务端,也搭过基础组件库。每次看到新人一头扎进"C 核心知识点"这个搜索词,我其实挺感慨的——这门语言的难点从来不是语法多,而是知识点之间的耦合太深…

2026/11/1 1:06:58 阅读更多 →
T109滑盖智能锁怎么装?安装调试与故障排查全攻略

T109滑盖智能锁怎么装?安装调试与故障排查全攻略

简介:亚太天能T109滑盖智能锁产品说明书以PDF格式发布,适合智能锁用户、安装人员与售后维修人员阅读。内容围绕T109系列指纹密码刷卡锁展开,先介绍前面板、后面板、锁体、机械钥匙、电池盒盖等部件名称与功能,再给出完整安装步骤&…

2026/10/31 10:30:19 阅读更多 →
LLM知识降熵:从信息垄断到理解权下放

LLM知识降熵:从信息垄断到理解权下放

你有没有过这种经历:翻开源码注释、论文摘要或者一份专业报告,每个字都认识,连在一起却像天书。想找人聊聊,身边要么没人懂,要么懂的懒得跟你解释。后来我试着把同一段内容丢给一个大语言模型,几秒钟之后&a…

2026/10/31 9:29:43 阅读更多 →

日新闻

周新闻

月新闻