驱动 remove 路径很容易在“正常运行”测试中被忽略。真正麻烦的是卸载时仍有中断、work、timer、DMA completion 或用户调用在路上:一边释放资源,另一边又重新入队,偶发卡死就出现了。

处理这类问题时,我会先画出谁产生事件、谁持有对象、谁可能睡眠,再按所有权逆序关闭。

拒绝新入口关闭硬件事件源同步在途回调取消延后工作释放资源
生产者IRQ、timer 与用户入口在途工作同步回调与引用所有权逆序释放资源

先停生产者,再清消费者

如果先 cancel work,却没有屏蔽能再次安排 work 的 IRQ,队列可能在 cancel 返回后重新出现任务。相反,先封住用户入口和硬件事件源,再用 synchronize_irq、cancel_work_sync 等机制等待在途执行结束,才能建立“不会再有新引用”的前提。

等待时还要避免持有回调需要的锁,否则 remove 线程和回调会互相等待。devm 可以帮助释放资源,但不会替你停止设备,也不会自动同步并发回调。

trace 用来验证顺序

我会把 remove、IRQ、work 入队、work 开始/结束和资源释放放到同一时间线,观察是否存在“释放之后仍有回调”或“等待期间回调拿不到锁”。压力测试应包含反复绑定/解绑、I/O 并发、故障注入和挂起恢复。

通过若干次回归只能说明定义场景未再复现,不能证明所有竞态都消失。最终还要靠生命周期不变量解释为什么顺序成立。

参考资料

证据边界

本文不披露具体驱动、设备寄存器、中断号、接口名称或现场次数。不同驱动的关闭顺序必须按自身所有权分析,不能机械照搬。