V4L2 sensor驱动中.s_ctrl() 自陷死锁问题排查全过程总结

V4L2 sensor驱动中.s_ctrl() 自陷死锁问题排查全过程总结

Armin
2025-05-15 / 0 评论 / 8 阅读 / 正在检测是否收录...

一、问题背景

在开发 LT9211C 的 V4L2 子设备驱动时,实现了一个控制接口:

v4l2-ctl -d /dev/v4l-subdevX --set-ctrl=chip_init=1

该命令触发 .s_ctrl() 中的设备初始化逻辑。驱动中使用了 mutex 来保护共享状态:

mutex_lock(&lt9211c->confctl_mutex);
...
mutex_unlock(&lt9211c->confctl_mutex);

但执行命令时,.s_ctrl() 永远卡在 mutex_lock(),导致控制操作无法完成。


二、初步怀疑与验证

1:其他线程(如 workqueue)持有锁

  • 存在工作队列周期性使用 mutex_trylock(),怀疑抢占了锁
  • 注释掉所有 schedule_work() 和工作队列逻辑
  • 结果:问题仍然存在,.s_ctrl() 卡住不动

2:驱动中其他路径加了锁

  • 全局查找所有 mutex_lock()mutex_trylock() 路径
  • 结果:确认无其他加锁路径

三、深入排查

尝试用 mutex_is_locked() 检查锁状态

.s_ctrl() 内部加打印:

pr_info("locked = %d\n", mutex_is_locked(&lt->confctl_mutex));

结果显示锁始终处于加锁状态,但代码中没人持有锁。


四、关键线索发现

在控件初始化函数中:

handler->lock = &lt9211c->confctl_mutex;
    
lt9211c->chip_init_ctrl = v4l2_ctrl_new_custom(handler,
        &lt9211c_ctrl_chip_init, NULL);

.s_ctrl() 回调正好是 chip_init 控件对应的处理函数。

这意味着:V4L2 框架在调用 .s_ctrl() 之前已经自动加了 handler->lock 对应的 mutex

五、框架调用链分析

VIDIOC_S_CTRL
 └── v4l2_ctrl_handler_setup()
      └── mutex_lock(handler->lock);    //框架加锁
      └── ctrl->ops->s_ctrl()           //调用驱动 .s_ctrl()
      └── mutex_unlock(handler->lock);

因此 .s_ctrl() 执行时已经处于加锁状态,若再次 mutex_lock() 同一把锁,就会死锁(线程阻塞等待自己)。


六、最终修复方式

删除 .s_ctrl() 中的重复加锁:

- mutex_lock(&lt->confctl_mutex);  // 不需要
...
- mutex_unlock(&lt->confctl_mutex); // 不需要

改为直接执行逻辑(由框架管理加解锁)。


七、验证结果

  • 编译驱动
  • 重新执行控制命令:

    v4l2-ctl --set-ctrl=chip_init=1
  • 控制操作立即成功返回,卡死问题彻底解决

八、经验总结

项目说明
问题类型自陷死锁(Self-deadlock)
原因.s_ctrl() 中手动 mutex_lock() 框架已加锁的 mutex
最大误导点锁好像“被别人持有”,实际是“自己卡自己”
排查突破口handler->lock 设置 → 框架自动加锁机制分析
根本解决方法不在 .s_ctrl() 中重复加锁 handler->lock 指向的 mutex
凡设置了 handler->lock 的 V4L2 控制器,.s_ctrl() 中禁止重复加锁,否则极易造成自陷死锁。

九、其他调试方法

pr_info("s_ctrl: current thread = %s, is_locked = %d\n",
        current->comm,
        mutex_is_locked(&lt->confctl_mutex));
可以查看当前的锁被谁持有。

1

评论 (0)

取消