并发、锁、中断、DMA 与错误恢复
1. 并发来源
Bus driver并发通常来自:
- child driver的I/O;
- controller IRQ;
- runtime/system PM;
- sysfs/debugfs;
- hotplug或firmware rescan;
- reset/recovery work;
- DMA completion;
- device remove。
仅用一个mutex不能覆盖hardirq和ring更新,也不应让spinlock包围可睡眠路径。
2. 锁分层
典型规则:
- mutex:probe/remove、配置、枚举、sysfs;
- spinlock:IRQ状态、短ring指针、doorbell;
- rwlock/RCU:读多写少状态或device查找;
- completion/waitqueue:命令及状态转换;
- refcount/device reference:异步对象生命周期;
- workqueue:从IRQ转到可睡眠恢复。
必须规定锁顺序,例如controller state → channel → ring,避免recovery与I/O形成ABBA。
3. 中断
错误IRQ:
- 读取并保存全部latch;
- mask或ack;
- 最小化格式化日志;
- 重处理放thread/work;
- 根据严重度决定继续、reset或panic。
若先清后读会丢fault;若不mask持续level IRQ会形成风暴。
MHI event IRQ还需确保ring element在读取前已通过DMA barrier可见。
4. DMA
MHI/FSL-MC及bus children可能DMA。关键点:
- 使用DMA API,不直接假定物理地址;
- 检查dma mask;
- coherent ring仍需遵守producer/consumer顺序;
- streaming buffer必须map/unmap和sync;
- reset前停止device DMA;
- IOMMU domain必须先于设备启动;
- controller释放前等待completion不再访问ring。
5. Ring一致性
Producer顺序:
1 | write descriptor/payload metadata |
Consumer:
1 | observe producer pointer/event |
普通spin_lock不自动替代所有设备DMA barrier。
6. 错误分级
| 级别 | 处理 |
|---|---|
| 可重试命令失败 | 返回errno,不重置全总线 |
| 单channel错误 | stop/reset channel |
| timeout/target abort | 保存现场,隔离故障child |
| controller SYS_ERR | 阻止I/O,重建controller |
| DMA越界/不可恢复一致性错误 | 禁用设备,必要时系统级恢复 |
错误恢复不能在旧I/O仍可能completion时直接重新分配同一ring。
7. Recovery状态机
1 | RUNNING |
所有提交API先检查状态。只有一个worker负责主恢复;重复错误合并,避免并发reset。
8. Hot Remove
MHI PCI设备拔出或FSL-MC object消失时:
- 从匹配表阻止新probe;
- 标记device offline;
- 取消client callbacks;
- 完成pending I/O为
-ENODEV; device_unregister();- 等release后释放parent对象。
功能driver不能缓存未持引用的channel/object指针。
9. Sysfs/Debugfs
写timeout、priority或状态触发器必须和I/O串行。Debugfs默认不是稳定ABI,不能让生产守护进程依赖其格式。
显示register时要避免:
- 读取会clear的状态;
- 访问已断电域;
- 暴露物理地址或敏感DMA内容;
- 在锁内生成超长输出。
10. Fault Injection
建议测试:
- clock/reset失败;
- IRQ丢失/风暴;
- ring满和wrap;
- DMA map失败;
- firmware超时;
- suspend与I/O竞争;
- remove与completion竞争;
- malformed descriptor;
- recovery中再次SYS_ERR。
成功probe和正常传输不足以证明生命周期正确。
正在加载留言…