驱动开发里,probe 成功路径通常最先被验证。设备能起来,功能能跑,日志看起来正常,问题似乎就结束了。
但很多线上问题并不来自成功路径,而是来自失败路径。尤其是资源申请到一半失败、硬件初始化中途 timeout、某个 optional 资源缺失、或者 defer probe 反复发生时,错误路径才是真正考验驱动质量的地方。
半初始化状态最危险
probe 的中间状态很复杂。常见顺序包括:
- 分配私有结构
- 获取寄存器资源
- 获取 clock/reset/regulator
- 初始化 lock/list/work/timer
- 注册中断
- 注册子系统对象
- 启动硬件
如果中途失败,已经完成的步骤必须按反序清理。否则设备虽然没有成功上线,却留下了可执行的回调、已注册的对象或打开的硬件资源。
最典型的问题是 work 或 timer 在失败后仍然可能执行,回调里访问的私有结构却已经释放。
devm 不是万能保险
devm_* 能减少很多清理代码,但它不等于自动解决所有生命周期问题。
需要特别注意:
- devm 管的是 device 生命周期,不一定等于功能停止时机
- 注册到其它子系统的对象可能需要显式 unregister
- workqueue、timer、threaded irq 需要停止并同步
- 硬件状态可能需要显式关闭
- 子设备和父设备的释放顺序可能影响回调
如果驱动注册了外部可见接口,失败路径必须保证接口不会在对象无效后继续被调用。
defer probe 会放大问题
-EPROBE_DEFER 看起来只是“以后再试”,但它会让 probe 路径多次进入和退出。
这会暴露很多问题:
- 日志重复刷屏
- 已初始化状态没有重置
- 资源重复申请
- 全局变量被污染
- 引用计数不平衡
如果一个驱动只在依赖设备未准备好时出问题,应该重点审查 defer probe 之前是否已经做了不可逆操作。
错误路径要主动测试
失败路径不能只靠代码审查。可以通过临时修改设备树、关闭 clock、屏蔽 regulator、制造中断申请失败等方式主动触发。
至少要覆盖:
- 资源不存在
- clock enable 失败
- reset deassert 失败
- irq 获取失败
- 子系统注册失败
- 硬件 ready timeout
每个失败点都应该满足两个条件:日志清楚,资源回收完整。
结尾
probe 成功只能说明主路径可用,不能说明驱动健壮。
真正稳定的驱动,应该在失败路径里也保持状态干净。没有半注册对象,没有残留异步任务,没有没有关闭的硬件资源,也没有下一次 probe 会继承的脏状态。
评论