
6.1.1 到 6.1.5 逐个拆解了 dma_fence 家族的成员base fence 的生命周期、context/seqno 的排序身份、spinlock 的并发保护、enable_signaling 的按需使能以及两个组合原语 array 与 chain。本篇换一个视角把它们作为一个整体重新审视——dma_fence_array、dma_fence_chain都「内嵌一个dma_fence且又持有一组dma_fence」从面向对象设计的设计模式视角总结下。用一句话概括本篇要论证的结论dma_fence 家族是「组合模式Composite」在内核 C 语言中的一次典范实现用结构体内嵌 ops虚表实现子类型多态让「一组 fence」对上层伪装成「一个 fence」。下面从机制层、模式层、约束层三层展开。1. 家族全景谁是谁先把成员和它们的角色列清楚成员本质角色详见struct dma_fence抽象基类接口 生命周期骨架抽象 fence6.1驱动 hw fence内嵌dma_fence对应一次硬件完成叶子Leaf6.1dma_fence_stub已 signal 的空 fence占位/兜底特殊叶子6.1dma_fence_array内嵌base 持有 N 个子 fence组合Composite·扁平6.1.4dma_fence_chain内嵌base 持有 prev 链 1 个工作 fence组合Composite·线性6.1.5它们共享同一个基类构成一棵「部分—整体」树叶子是真正对应硬件完成的 fence组合节点内部聚合其它 fence而对上层消费者两者都只是一个struct dma_fence *。N 个子 fenceprev 工作 fence«abstract»dma_fencecontext / seqnoflags / refcountops : dma_fence_opslock : spinlock_t驱动 hw fence (Leaf)stub (Leaf)dma_fence_array (Composite·扁平)dma_fence_chain (Composite·线性)注意 array 与 chain 的聚合边o--又指回dma_fence自身——组合节点持有的仍是抽象 fence可以是叶子也可以在受限条件下是另一个组合。这种「基类聚合基类」的自引用正是组合模式树形结构的来源。2. 机制层C 语言如何实现「继承」家族没有 C 的class它用内核惯用的三件套实现子类型多态面向对象概念C 语言实现家族中的体现继承 / is-a结构体把基类作为第一个成员内嵌struct dma_fence_array { struct dma_fence base; ... }向上转型upcast取内嵌成员地址array-base当作struct dma_fence *传出向下转型downcastcontainer_ofto_dma_fence_array()/to_dma_fence_chain()虚函数 / 动态分发函数指针表fence-ops-{enable_signaling, signaled, wait, release}运行时类型识别RTTI比较ops指针dma_fence_is_array()/dma_fence_is_chain()base放在首成员使struct dma_fence *与struct dma_fence_array *在地址上重合向上转型零开销ops指针再把同名调用分发到各自实现。这与struct kobject、struct inode、sk_buff的做法一脉相承——是 Linux 内核里「面向对象 C」的标准范式。而lock字段是指针spinlock_t *这一点也在此得到解释基类不内嵌锁实例而由子类提供自己的lock并让base.lock指过去array 的base.lock指向array-lockchain 同理。基类只约定「有一把锁」锁的归属交给子类——这正是继承中「基类定接口、子类定实现」的体现详见 6.1.2。3. 模式层核心是组合模式Composite继承只是手段家族真正要表达的意图是组合模式。GoF 对组合模式的定义是将对象组合成树形结构以表示「部分—整体」层次使客户端对「单个对象」和「组合对象」的使用方式一致。逐条对应到 dma_fence 家族统一接口Componentdma_fencedma_fence_ops。消费接口dma_fence_wait()/dma_fence_add_callback()/ 挂入dma_resv全部只认struct dma_fence *。叶子Leaf驱动 hw fence对应一次真实硬件完成没有子节点。组合Compositedma_fence_array、dma_fence_chain内部聚合子 fence自身完成与否取决于子节点。一致性关键收益消费者无需知道手里的 fence 是叶子还是组合。等待一块 BO 的写完成和等待「N 块 BO 全部写完成」的聚合 fence写法完全一样。这份「一致性」就是家族全部价值所在上层dma_resv、drm_syncobj、命令提交永远只面对单个dma_fence接口而「一个还是一组」「无序还是有序」的复杂度被封装进组合节点内部。除 Composite 外家族还叠加了三个配套模式各司其职设计模式在家族中的落点作用Composite组合array / chain 聚合子 fence让一组 fence 伪装成一个 fence主模式Strategy策略/ 虚表dma_fence_ops四个钩子同一动作路由到 leaf/array/chain 各自实现Template Method模板方法dma_fence_signal()/dma_fence_wait_timeout()核心定骨架可变步骤下沉到opsObserver观察者add_callback注册、signal时遍历cb_list通知完成事件的一对多异步通知详见 6.2可以说Composite 是目的Strategy(ops) 是手段Template Method 定骨架Observer 管通知。4. 两种组合拓扑array 扁平 vs chain 线性同样是 Compositearray 与 chain 是这棵组合树的两种不同形态这正是家族最有意思的地方维度dma_fence_array6.1.4dma_fence_chain6.1.5拓扑扁平、宽一层N 个兄弟线性、深单向链prev递归聚合语义AND全就绪/ OR任一靠num_pending有序 timeline单调递增 蕴含性context/seqno自身独立 context一条 timeline 共享 contextseqno point 值signal 传播子回调递减num_pending归零经 irq_work 冒泡沿链逐段 enable、经 irq_work 推进定位能力无find_seqno按 point 定位节点类比数据结构集合 / 计数栅栏链表 / 版本号轴一句话表达「并列的一组」表达「递进的一串」两者互补需要「多个结果同时(无序)就绪」用 array需要「有序进度点、后者蕴含前者」用 chain。它们共同覆盖了组合模式在同步语义上的两个正交维度——无序并列聚合与有序串接。5. 虚表对照同一接口三套实现把 Strategy 落到实处看四个核心ops钩子在叶子与两个组合上的不同实现最能体现「多态」ops钩子叶子hw fencedma_fence_arraydma_fence_chainget_timeline_name驱动 timeline 名unboundunboundenable_signaling开硬件完成中断为每个子 fence 挂回调为链上首个未完成节点挂回调signaled查硬件状态读num_pending 0遍历全链判定releasedma_fence_freekfree_rcuput 所有子 fence kfree 数组手动解链防栈溢出同一个dma_fence_wait()调用落到不同ops上就有截然不同的行为——这就是虚表分发的意义。上层代码写一次家族里每种 fence 各自「翻译」成自己的完成判定方式。6. 模式带来的约束家族里几条看似琐碎的实现约束其实都是「组合树」这一模式在内核无栈递归约束下的直接后果。理解了模式这些规矩就不再是需要死记的特例array 禁止嵌套容器WARN_ON(dma_fence_is_container(...))dma_fence_unwrap_merge()摊平array 选择做扁平组合用「构造前 flatten 成一层」替代「运行时递归遍历」避免组合树过深、在 signal/遍历/release 时耗尽内核栈。chain 的release手动解链chain 是深链形态的组合靠析构递归dma_fence_put(prev)会栈溢出于是把递归展平为循环。与 array「禁止嵌套」是同一问题递归深度的两种解法——array 从源头压平chain 在析构时展平。num_pending irq_workarray/ 逐段 irq_workchain组合节点要等所有/下一个子节点完成再向上游 signal是组合树里「完成事件向根冒泡」的实现用 irq_work 把 signal 收尾切到干净上下文规避在子 fence 锁内触发下游回调造成的锁序反转与递归。cb/work共用 union组合节点在「等待阶段挂回调」与「收尾阶段irq_work」不会同时发生复用同一块内存——是组合节点两阶段生命周期的体现。两段式allocinit把「可能失败的分配」与「不可失败的初始化」分离支持在 reclaim / signaling 上下文用预分配对象构造组合节点附录 B.6 的预分配模式。两套遍历接口dma_fence_array_for_each浅层只展开一层与dma_fence_unwrap_for_each深度递归展开所有容器交出叶子——正是组合模式经典的树遍历按「是否深入子树」分成两档。7. 家族如何协同出场这些成员并非各自孤立它们在上层同步设施里经常一起出现dma_fence 家族上层同步设施get_singleton 合成一个binary 模式timeline 模式dma_resv 隐式同步(6.3)drm_syncobj 显式同步(6.4.2)dma_fence_array扁平 AND/ORdma_fence_chain线性 timeline驱动 hw fence (叶子)dma_resv_get_singleton()把一块 BO 上的多个 fence 合成一个对外句柄底层构造扁平的 array6.3.2。drm_syncobj的 binary 模式直接持单个 fence叶子timeline 模式则用chain表达单调递增的 point6.4.2。无论哪条路径最终的叶子都是驱动的 hw fence——组合节点只是把它们按不同拓扑组织起来。8. 小结从继承视角回看dma_fence 家族是一套结构清晰的面向对象设计机制层结构体内嵌basecontainer_of转型 ops虚表实现 C 语言的子类型多态lock用指针交由子类提供是「基类定接口、子类定实现」的缩影。模式层核心是组合模式Composite——让「一组 fence」对上层伪装成「一个 fence」配套 Strategyops 分发、Template Method核心定骨架、Observersignal 通知回调。两种拓扑array 是扁平的 AND/OR 组合chain 是线性有序的 timeline 组合正交互补。约束即模式不可嵌套 / 手动解链 / unwrap 遍历 / irq_work 冒泡都是「组合树」在内核无栈递归约束下的工程化取舍而非零散特例。dma_fence定义了「一次异步完成」的抽象array 与 chain 用组合模式把这个抽象递归地组织成树——并列成组、有序成链。上层只需面对一个dma_fence *家族在内部消化了「一个还是一组、无序还是有序」的全部复杂度。至此 6.1 全章基类、context/seqno、spinlock、enable_signaling、array、chain 及本总结篇完结。接下来6.2回到 fence 的消费侧深入阻塞等待与异步回调两种同步策略。