Lazy loaded image
Words 0Read Time 1 min
Invalid Date
📚
核心判断
一套专题如果最终不能沉淀成模板、清单和快查表,它的复用价值会迅速衰减。真正高杠杆的做法,不是把知识记成印象,而是把知识压缩成以后还能直接调用的工程资产

这一章解决什么问题

前面十一章已经把 DMA 从问题、原理、设计、实战、排障到表达都讲完了。最后这一章要做的,是把它们压缩成工具箱。
 
也就是把“看过”变成“以后还用得上”。

一、DMA 常用 API 使用时机快查

API / 动作
什么时候用
你真正要确认什么
dma_set_mask_and_coherent()
probe 早期
设备到底能看多大地址空间
dma_alloc_coherent()
控制面、descriptor、状态结构
是否优先要简单一致性语义
dma_map_single()
高吞吐数据面(单 buffer)
是否需要显式同步与严格生命周期
dma_map_sg()
分散内存拼成一条传输
硬件是否支持 SG descriptor
dma_sync_single_for_device()
CPU 写完,设备准备读之前
设备下一步能否看到最新内容
dma_sync_single_for_cpu()
设备写完,CPU/用户准备读之前
CPU 下一步能否看到设备刚写的新内容
dma_pool_create() / dma_pool_alloc()
频繁分配的小块一致性对象
descriptor / command buffer 是否值得池化
barrier / dma_wmb() / dma_rmb()
descriptor、状态位、doorbell 之间
设备会不会看到半成品顺序
mmap
需要共享数据面给用户态
数据面和控制面有没有分开
poll
需要事件驱动完成通知
是否只承担通知,而不偷渡复杂语义

一点二五、Coherent / Streaming / Pool 快查

类型
更适合什么
你真正换来的是什么
dma_alloc_coherent()
descriptor、ring、状态结构、第一版稳定闭环
更简单的一致性语义,代价是灵活性和数据面性能未必最优
dma_map_single() / dma_unmap_single()
高吞吐单 buffer 数据面
更贴近真实收发路径,但生命周期和同步纪律更重
dma_map_sg() / dma_unmap_sg()
分散内存拼装成一条 DMA 事务
解决不连续内存问题,但必须把整组 SG 当成一个事务治理
dma_pool_create() / dma_pool_alloc()
频繁分配小块一致性对象
减少小对象反复分配成本,适合 descriptor / command buffer 池化

一点五、方向与同步动作快查

场景
优先检查什么
常见失误
CPU 写完,设备准备读
是否已把 CPU 新内容对设备可见
buffer 还没刷回去就提交 DMA
设备写完,CPU 准备读
是否已把 CPU 旧 cache 作废
DMA 明明完成了,CPU 还在读旧副本
descriptor 写完后敲 doorbell
顺序是否已被 barrier 约束
设备看到半成品 descriptor
reset / timeout 后重启事务
旧 completion / 旧状态是否已清理
新事务被旧生命周期污染

一点七五、map / unmap 配对速记

资源形态
典型建立动作
典型收尾动作
最容易犯的错
单 buffer streaming
dma_map_single()
dma_unmap_single()
完成后忘记 unmap,或 map 失败还继续下发 descriptor
SG 事务
dma_map_sg()
dma_unmap_sg()
只盯某一段回收,没有把整条 SG 事务当整体治理
一致性 descriptor / ring
dma_alloc_coherent()
dma_free_coherent()
把它误当成 streaming buffer,再额外乱加 map/unmap
用户态共享 coherent 区
dma_alloc_coherent()dma_mmap_coherent()
dma_free_coherent() • fd / 映射生命周期收口
以为 mmap 自动附带了 submit / consume / reclaim 协议

一点九、最小地址视图速记

  • CPU 虚拟地址:驱动自己访问内存时使用。
  • DMA 地址:设备真正发起 DMA 时使用。
  • 用户虚拟地址mmap 之后用户态看到的地址。
 
三者可能都不同,但它们可能映射到同一批物理页。
 
所以看到 mmap 成功时,脑子里应该自动补一句:
数据面桥接完成了,但控制面协议还没自动出现。

一点九五、长度与失败守卫速记

检查点
最低要求
长度边界
transfer_size <= buffer_size
映射结果
立刻检查 dma_mapping_error()
地址能力
probe 早期确认 dma_set_mask*() 是否成功
顺序边界
descriptor 完整后再 doorbell,必要处补 barrier

二、最小 slot 状态机模板

推荐迁移规则

  • FREE -> CPU_OWNED:prepare / alloc
  • CPU_OWNED -> DEVICE_OWNED:submit 后正式交给设备
  • DEVICE_OWNED -> READY_FOR_CPU:completion + 必要同步后
  • READY_FOR_CPU -> FREE:consume / reclaim
  • 任意关键状态 -> ERROR:timeout / fault / reset 前异常
 
如果你的驱动连这张最小状态图都画不出来,那先别谈优化,先把秩序补上。

三、ring buffer 设计检查清单

  • ring 的 producer / consumer 分别是谁推进?
  • completion 是按 slot、period 还是 batch 到达?
  • slot 回收的边界点是 completion 还是 consume?
  • reset 后哪些 slot 必须整体作废?
  • 用户态慢消费时,系统是阻塞、丢弃还是回压?
 
ring 真正难的,从来不是“画一圈数组”,而是定义并发世界里的秩序。

四、零拷贝协议模板

推荐最小协议

动作
语义
GET_WRITE_SLOT
获取当前可写 slot
SUBMIT
声明写完并交给设备
WAIT_DONE / PEEK_DONE
等待或查询完成 slot
CONSUME / ACK
声明结果已使用完,可回收
RESET / CANCEL / QUERY
异常控制与状态查询

协议设计原则

  • 数据面走 mmap
  • 控制面走 ioctl/poll
  • 状态迁移必须可验证
  • 用户态 SUBMIT 后不得再写该 slot
  • CONSUME 不是装饰,而是回收语义的边界

五、probe / remove 模板清单

probe

  • 建立设备私有结构体
  • 初始化锁、wait queue、ring 元数据
  • 声明 DMA mask
  • 分配 buffer / descriptor
  • 注册 IRQ
  • 注册字符设备或上层接口

remove

  • 阻止新请求进入
  • 停止硬件 DMA
  • 作废或回收 in-flight slot
  • 注销接口
  • 释放 IRQ
  • 释放 DMA 资源
 
probe 看的是“怎么生”,remove 看的是“怎么死”。能死干净,系统才算真正活过。

六、最值钱的日志模板

状态迁移日志

时序日志

异常日志

七、最常见错误速查

错误现象
优先怀疑什么
设备读到旧数据
提交前同步缺失,或 CPU 仍在改 buffer
CPU 读到旧结果
完成后同步缺失,或状态过早切换
偶发错帧
slot 过早回收 / ring 边界不清
reset 后随机炸
旧 completion / 旧状态污染新事务
remove 时崩溃
DMA 未停机,资源先释放
随机内存污染
DMA 长度越界,覆盖相邻对象

八、调试开关与运行时观测口

推荐内核配置

常用运行时入口

九、阅读建议:不同目标怎么走

如果你是第一次系统学 DMA

推荐顺序:
  1. 01 问题定义
  1. 02 全链路图
  1. 03 地基
  1. 04 中轴
  1. 05-07 实战路径

如果你正在写驱动

推荐顺序:
  1. 🔄
    04|Ownership、协议与生命周期:DMA 设计的中轴
  1. 🧱
    05|最小 DMA 驱动骨架:从 probe 到 remove
  1. ⚙️
    06|内核态 DMA 收发实战:先跑通一条闭环
  1. 🚀
    07|mmap 零拷贝与用户态协作:接口协议比映射更重要
  1. ⚠️
    09|常见坑、异常路径与调试验证:别把 DMA 写成玄学

如果你在准备面试

推荐顺序:
  1. 🎯
    01|DMA 在真实驱动里到底解决什么问题
  1. 🧭
    03|地址、Cache、一致性与 IOMMU:写驱动前必须打牢的地基
  1. 🔄
    04|Ownership、协议与生命周期:DMA 设计的中轴
  1. ⚖️
    08|Coherent、Streaming、SG、Cyclic:方案怎么选
  1. 🎤
    11|面试高频问答:从会写到会讲
 
别从附录开始学,但写代码或复盘时,附录最适合拿来当操作台。

如果你正在现场写代码

建议把本页和下面几页并排看:
 
因为真正写代码时,你最需要的不是“概念完整”,而是骨架 + 生命周期 + 排障抓手 同时在线。

十、本专题压缩成一句总纲

DMA 不是“调几个 API”,而是围绕数据路径、ownership、生命周期、同步和异常治理构建的一套工程系统。

本章结论

  1. 真正有杠杆的知识,一定要沉淀成模板、清单和快查表。
  1. DMA 最该反复复用的资产,不是定义,而是状态机、协议、日志和检查表。
  1. 当这些资产建立起来后,你后面写新的驱动,不会再从零开始靠灵感乱撞。
上一篇
Data Structure and Algorithm
下一篇
用面试拷问嵌入式技术栈

Comments
Loading...