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 | 抽象基类(接口 + 生命周期骨架) | 抽象 fence | 6.1 |
| 驱动 hw fence | 内嵌 dma_fence,对应一次硬件完成 | 叶子(Leaf) | 6.1 |
| dma_fence_stub | 已 signal 的空 fence(占位/兜底) | 特殊叶子 | 6.1 |
| dma_fence_array | 内嵌 base + 持有 N 个子 fence | 组合(Composite)·扁平 | 6.1.4 |
| dma_fence_chain | 内嵌 base + 持有 prev 链 + 1 个工作 fence | 组合(Composite)·线性 | 6.1.5 |
它们共享同一个基类,构成一棵「部分—整体」树:叶子是真正对应硬件完成的 fence,组合节点内部聚合其它 fence,而对上层消费者,两者都只是一个 struct dma_fence *。
#mermaid-svg-5mpgtLsnwD59Ix1o{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-5mpgtLsnwD59Ix1o .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-5mpgtLsnwD59Ix1o .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-5mpgtLsnwD59Ix1o .error-icon{fill:#552222;}#mermaid-svg-5mpgtLsnwD59Ix1o .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-5mpgtLsnwD59Ix1o .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-5mpgtLsnwD59Ix1o .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-5mpgtLsnwD59Ix1o .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-5mpgtLsnwD59Ix1o .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-5mpgtLsnwD59Ix1o .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-5mpgtLsnwD59Ix1o .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-5mpgtLsnwD59Ix1o .marker{fill:#333333;stroke:#333333;}#mermaid-svg-5mpgtLsnwD59Ix1o .marker.cross{stroke:#333333;}#mermaid-svg-5mpgtLsnwD59Ix1o svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-5mpgtLsnwD59Ix1o p{margin:0;}#mermaid-svg-5mpgtLsnwD59Ix1o g.classGroup text{fill:#9370DB;stroke:none;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:10px;}#mermaid-svg-5mpgtLsnwD59Ix1o g.classGroup text .title{font-weight:bolder;}#mermaid-svg-5mpgtLsnwD59Ix1o .cluster-label text{fill:#333;}#mermaid-svg-5mpgtLsnwD59Ix1o .cluster-label span{color:#333;}#mermaid-svg-5mpgtLsnwD59Ix1o .cluster-label span p{background-color:transparent;}#mermaid-svg-5mpgtLsnwD59Ix1o .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-5mpgtLsnwD59Ix1o .cluster text{fill:#333;}#mermaid-svg-5mpgtLsnwD59Ix1o .cluster span{color:#333;}#mermaid-svg-5mpgtLsnwD59Ix1o .nodeLabel,#mermaid-svg-5mpgtLsnwD59Ix1o .edgeLabel{color:#131300;}#mermaid-svg-5mpgtLsnwD59Ix1o .edgeLabel .label rect{fill:#ECECFF;}#mermaid-svg-5mpgtLsnwD59Ix1o .label text{fill:#131300;}#mermaid-svg-5mpgtLsnwD59Ix1o .labelBkg{background:#ECECFF;}#mermaid-svg-5mpgtLsnwD59Ix1o .edgeLabel .label span{background:#ECECFF;}#mermaid-svg-5mpgtLsnwD59Ix1o .classTitle{font-weight:bolder;}#mermaid-svg-5mpgtLsnwD59Ix1o .node rect,#mermaid-svg-5mpgtLsnwD59Ix1o .node circle,#mermaid-svg-5mpgtLsnwD59Ix1o .node ellipse,#mermaid-svg-5mpgtLsnwD59Ix1o .node polygon,#mermaid-svg-5mpgtLsnwD59Ix1o .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-5mpgtLsnwD59Ix1o .divider{stroke:#9370DB;stroke-width:1;}#mermaid-svg-5mpgtLsnwD59Ix1o g.clickable{cursor:pointer;}#mermaid-svg-5mpgtLsnwD59Ix1o g.classGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-5mpgtLsnwD59Ix1o g.classGroup line{stroke:#9370DB;stroke-width:1;}#mermaid-svg-5mpgtLsnwD59Ix1o .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-5mpgtLsnwD59Ix1o .classLabel .label{fill:#9370DB;font-size:10px;}#mermaid-svg-5mpgtLsnwD59Ix1o .relation{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-5mpgtLsnwD59Ix1o .dashed-line{stroke-dasharray:3;}#mermaid-svg-5mpgtLsnwD59Ix1o .dotted-line{stroke-dasharray:1 2;}#mermaid-svg-5mpgtLsnwD59Ix1o #compositionStart,#mermaid-svg-5mpgtLsnwD59Ix1o .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-5mpgtLsnwD59Ix1o #compositionEnd,#mermaid-svg-5mpgtLsnwD59Ix1o .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-5mpgtLsnwD59Ix1o #dependencyStart,#mermaid-svg-5mpgtLsnwD59Ix1o .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-5mpgtLsnwD59Ix1o #dependencyStart,#mermaid-svg-5mpgtLsnwD59Ix1o .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-5mpgtLsnwD59Ix1o #extensionStart,#mermaid-svg-5mpgtLsnwD59Ix1o .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-5mpgtLsnwD59Ix1o #extensionEnd,#mermaid-svg-5mpgtLsnwD59Ix1o .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-5mpgtLsnwD59Ix1o #aggregationStart,#mermaid-svg-5mpgtLsnwD59Ix1o .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-5mpgtLsnwD59Ix1o #aggregationEnd,#mermaid-svg-5mpgtLsnwD59Ix1o .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-5mpgtLsnwD59Ix1o #lollipopStart,#mermaid-svg-5mpgtLsnwD59Ix1o .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-5mpgtLsnwD59Ix1o #lollipopEnd,#mermaid-svg-5mpgtLsnwD59Ix1o .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-5mpgtLsnwD59Ix1o .edgeTerminals{font-size:11px;line-height:initial;}#mermaid-svg-5mpgtLsnwD59Ix1o .classTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-5mpgtLsnwD59Ix1o .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-5mpgtLsnwD59Ix1o .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-5mpgtLsnwD59Ix1o :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
N 个子 fence
prev + 工作 fence
«abstract»
dma_fence
+context / seqno
+flags / refcount
+ops : dma_fence_ops
+lock : 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,它用内核惯用的三件套实现子类型多态:
| 继承 / is-a | 结构体把基类作为第一个成员内嵌 | struct dma_fence_array { struct dma_fence base; … } |
| 向上转型(upcast) | 取内嵌成员地址 | &array->base 当作 struct dma_fence * 传出 |
| 向下转型(downcast) | container_of | to_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->lock,chain 同理)。基类只约定「有一把锁」,锁的归属交给子类——这正是继承中「基类定接口、子类定实现」的体现(详见 6.1.2)。
3. 模式层:核心是组合模式(Composite)
继承只是手段,家族真正要表达的意图是组合模式。GoF 对组合模式的定义是:
将对象组合成树形结构以表示「部分—整体」层次,使客户端对「单个对象」和「组合对象」的使用方式一致。
逐条对应到 dma_fence 家族:
- 统一接口(Component):dma_fence + dma_fence_ops。消费接口 dma_fence_wait() / dma_fence_add_callback() / 挂入 dma_resv 全部只认 struct dma_fence *。
- 叶子(Leaf):驱动 hw fence,对应一次真实硬件完成,没有子节点。
- 组合(Composite):dma_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() | 核心定骨架,可变步骤下沉到 ops |
| Observer(观察者) | add_callback 注册、signal 时遍历 cb_list 通知 | 完成事件的一对多异步通知(详见 6.2) |
可以说:Composite 是目的,Strategy(ops) 是手段,Template Method 定骨架,Observer 管通知。
4. 两种组合拓扑:array 扁平 vs chain 线性
同样是 Composite,array 与 chain 是这棵组合树的两种不同形态,这正是家族最有意思的地方:
| 拓扑 | 扁平、宽:一层,N 个兄弟 | 线性、深:单向链,prev 递归 |
| 聚合语义 | AND(全就绪)/ OR(任一),靠 num_pending | 有序 timeline:单调递增 + 蕴含性 |
| context/seqno | 自身独立 context | 一条 timeline 共享 context,seqno = point 值 |
| signal 传播 | 子回调递减 num_pending,归零经 irq_work 冒泡 | 沿链逐段 enable、经 irq_work 推进 |
| 定位能力 | 无 | find_seqno 按 point 定位节点 |
| 类比数据结构 | 集合 / 计数栅栏 | 链表 / 版本号轴 |
| 一句话 | 表达「并列的一组」 | 表达「递进的一串」 |
两者互补:需要「多个结果同时(无序)就绪」用 array,需要「有序进度点、后者蕴含前者」用 chain。它们共同覆盖了组合模式在同步语义上的两个正交维度——无序并列聚合与有序串接。
5. 虚表对照:同一接口,三套实现
把 Strategy 落到实处,看四个核心 ops 钩子在叶子与两个组合上的不同实现,最能体现「多态」:
| get_timeline_name | 驱动 timeline 名 | "unbound" | "unbound" |
| enable_signaling | 开硬件完成中断 | 为每个子 fence 挂回调 | 为链上首个未完成节点挂回调 |
| signaled | 查硬件状态 | 读 num_pending <= 0 | 遍历全链判定 |
| release | dma_fence_free(kfree_rcu) | put 所有子 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_work(array)/ 逐段 irq_work(chain):组合节点要等所有/下一个子节点完成再向上游 signal,是组合树里「完成事件向根冒泡」的实现;用 irq_work 把 signal 收尾切到干净上下文,规避在子 fence 锁内触发下游回调造成的锁序反转与递归。
- cb / work 共用 union:组合节点在「等待阶段(挂回调)」与「收尾阶段(irq_work)」不会同时发生,复用同一块内存——是组合节点两阶段生命周期的体现。
- 两段式 alloc + init:把「可能失败的分配」与「不可失败的初始化」分离,支持在 reclaim / signaling 上下文用预分配对象构造组合节点(附录 B.6 的预分配模式)。
- 两套遍历接口:dma_fence_array_for_each(浅层,只展开一层)与 dma_fence_unwrap_for_each(深度,递归展开所有容器交出叶子)——正是组合模式经典的树遍历,按「是否深入子树」分成两档。
7. 家族如何协同出场
这些成员并非各自孤立,它们在上层同步设施里经常一起出现:
#mermaid-svg-NDP1IxQ09KXa93GO{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-NDP1IxQ09KXa93GO .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-NDP1IxQ09KXa93GO .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-NDP1IxQ09KXa93GO .error-icon{fill:#552222;}#mermaid-svg-NDP1IxQ09KXa93GO .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-NDP1IxQ09KXa93GO .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-NDP1IxQ09KXa93GO .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-NDP1IxQ09KXa93GO .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-NDP1IxQ09KXa93GO .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-NDP1IxQ09KXa93GO .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-NDP1IxQ09KXa93GO .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-NDP1IxQ09KXa93GO .marker{fill:#333333;stroke:#333333;}#mermaid-svg-NDP1IxQ09KXa93GO .marker.cross{stroke:#333333;}#mermaid-svg-NDP1IxQ09KXa93GO svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-NDP1IxQ09KXa93GO p{margin:0;}#mermaid-svg-NDP1IxQ09KXa93GO .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-NDP1IxQ09KXa93GO .cluster-label text{fill:#333;}#mermaid-svg-NDP1IxQ09KXa93GO .cluster-label span{color:#333;}#mermaid-svg-NDP1IxQ09KXa93GO .cluster-label span p{background-color:transparent;}#mermaid-svg-NDP1IxQ09KXa93GO .label text,#mermaid-svg-NDP1IxQ09KXa93GO span{fill:#333;color:#333;}#mermaid-svg-NDP1IxQ09KXa93GO .node rect,#mermaid-svg-NDP1IxQ09KXa93GO .node circle,#mermaid-svg-NDP1IxQ09KXa93GO .node ellipse,#mermaid-svg-NDP1IxQ09KXa93GO .node polygon,#mermaid-svg-NDP1IxQ09KXa93GO .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-NDP1IxQ09KXa93GO .rough-node .label text,#mermaid-svg-NDP1IxQ09KXa93GO .node .label text,#mermaid-svg-NDP1IxQ09KXa93GO .image-shape .label,#mermaid-svg-NDP1IxQ09KXa93GO .icon-shape .label{text-anchor:middle;}#mermaid-svg-NDP1IxQ09KXa93GO .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-NDP1IxQ09KXa93GO .rough-node .label,#mermaid-svg-NDP1IxQ09KXa93GO .node .label,#mermaid-svg-NDP1IxQ09KXa93GO .image-shape .label,#mermaid-svg-NDP1IxQ09KXa93GO .icon-shape .label{text-align:center;}#mermaid-svg-NDP1IxQ09KXa93GO .node.clickable{cursor:pointer;}#mermaid-svg-NDP1IxQ09KXa93GO .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-NDP1IxQ09KXa93GO .arrowheadPath{fill:#333333;}#mermaid-svg-NDP1IxQ09KXa93GO .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-NDP1IxQ09KXa93GO .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-NDP1IxQ09KXa93GO .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-NDP1IxQ09KXa93GO .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-NDP1IxQ09KXa93GO .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-NDP1IxQ09KXa93GO .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-NDP1IxQ09KXa93GO .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-NDP1IxQ09KXa93GO .cluster text{fill:#333;}#mermaid-svg-NDP1IxQ09KXa93GO .cluster span{color:#333;}#mermaid-svg-NDP1IxQ09KXa93GO div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-NDP1IxQ09KXa93GO .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-NDP1IxQ09KXa93GO rect.text{fill:none;stroke-width:0;}#mermaid-svg-NDP1IxQ09KXa93GO .icon-shape,#mermaid-svg-NDP1IxQ09KXa93GO .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-NDP1IxQ09KXa93GO .icon-shape p,#mermaid-svg-NDP1IxQ09KXa93GO .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-NDP1IxQ09KXa93GO .icon-shape .label rect,#mermaid-svg-NDP1IxQ09KXa93GO .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-NDP1IxQ09KXa93GO .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-NDP1IxQ09KXa93GO .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-NDP1IxQ09KXa93GO :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
dma_fence 家族
上层同步设施
get_singleton 合成一个
binary 模式
timeline 模式
dma_resv 隐式同步(6.3)
drm_syncobj 显式同步(6.4.2)
dma_fence_array扁平 AND/OR
dma_fence_chain线性 timeline
驱动 hw fence (叶子)
- dma_resv_get_singleton() 把一块 BO 上的多个 fence 合成一个对外句柄,底层构造扁平的 array(6.3.2)。
- drm_syncobj 的 binary 模式直接持单个 fence(叶子),timeline 模式则用 chain 表达单调递增的 point(6.4.2)。
- 无论哪条路径,最终的叶子都是驱动的 hw fence——组合节点只是把它们按不同拓扑组织起来。
8. 小结
从继承视角回看,dma_fence 家族是一套结构清晰的面向对象设计:
- 机制层:结构体内嵌 base + container_of 转型 + ops 虚表,实现 C 语言的子类型多态;lock 用指针交由子类提供,是「基类定接口、子类定实现」的缩影。
- 模式层:核心是组合模式(Composite)——让「一组 fence」对上层伪装成「一个 fence」;配套 Strategy(ops 分发)、Template Method(核心定骨架)、Observer(signal 通知回调)。
- 两种拓扑: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 的消费侧,深入阻塞等待与异步回调两种同步策略。
