驱动开发里,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 会继承的脏状态。