一、hang_detect介绍:
MTK平台存在一个hang_detect的机制,用于检查应用层和驱动是否有卡住,应用层的system_server进程会
30s喂狗,也就是对kernel driver中的watchdog节点进行kick的操作,当kernel 里面的watchdog线程卡住时,或者
应用层发生SWT时不能及时喂狗,都会出现hang_detect的问题,出现问题时会有一定的等待时间重启,不同的
等待时间具有不同的意义:
二、具体问题分析:
下面来看实际项目中遇到的一个问题,通过如下log可以看出发生了hang_detect重启,并且重启的count=10,
说明问题可能发生在重启前的300S:
[ 451.448876] (0)[142:hang_detect][Hang_Detect] hang_detect thread counts down -1:10, status 1.
[ 453.277108] -(0)[142:hang_detect]PC is at hang_detect_thread+0x244/0x3a4
[ 453.277126] -(0)[142:hang_detect]LR is at console_unlock+0x25c/0x4d8
[ 453.277142] -(0)[142:hang_detect]pc : [] lr : [] psr: 200b0113
这时查看DB文件中的SYS_HANG_DETECT_RAW文件中watchdog的状态,可以看到很多进程挂在了__refrigerator
函数中,其中D状态是不可中断的睡眠状态,这一点可以查询进程的状态切换的相关知识。
watchdog D 3.505309 927 7 6 0x800
__schedule+0x364/0x7a4
schedule+0x38/0x84
__refrigerator+0xa0/0x188
futex_wait_queue_me+0x178/0x17c
futex_wait+0x118/0x264
do_futex+0x120/0xb70
SyS_futex+0x90/0x1a8
0xffffffff
......
main D 1234.788544 874 29 608 0x1
__schedule+0x364/0x7a4
schedule+0x38/0x84
__refrigerator+0xa0/0x188
get_signal+0x67c/0x680
do_signal+0x7c/0x500
do_work_pending+0xb8/0xc8
通过进程挂在了__refrigerator函数中,可以得知,问题可能在suspend的流程中挂住,由以下信息可以推断,
所有冻结的进程会系统调用到__refrigerator:
到这里,查看system_server,看到正好处于pm_suspend流程中,可以看到,system_server进程的处于可
中断的睡眠状态,其中调用函数挂在了xxx_suspend中:
system_server S 97.000000 682 228 34 0x800
__schedule+0x364/0x7a4
schedule+0x38/0x84
schedule_timeout+0x1e8/0x228
__down_interruptible+0x90/0xf8
down_interruptible+0x54/0x60
xxx_suspend+0x58/0xd8
i2c_device_pm_suspend+0x68/0x74
dpm_run_callback+0x64/0x1c8
__device_suspend+0x114/0x39c
dpm_suspend+0xb0/0x3a4
dpm_suspend_start+0x5c/0x60
suspend_devices_and_enter+0xb4/0x314
pm_suspend+0x4c4/0x6d0
state_store+0x88/0xb0
kobj_attr_store+0x1c/0x28
sysfs_kf_write+0x48/0x4c
kernfs_fop_write+0x134/0x1b8
vfs_write+0xa8/0x1ec
SyS_write+0x54/0xb4
这时需要去看xxx_suspend函数:
static struct semaphore s_tSemaProtect;
static int xxx_mutex_lock(void)
{
if (down_interruptible(&s_tSemaProtect))
return (-ERESTARTSYS);
return 0;
}
static void xxx_mutex_unlock(void)
{
up(&s_tSemaProtect);
}
static int xxx_suspend(struct i2c_client *client, pm_message_t msg)
{
if(msg.event == PM_EVENT_SUSPEND)
{
atomic_set(&obj->suspend, 1);
xxx_mutex_lock();
err = xxx_setPowerMode(client, false);
xxx_mutex_unlock();
}
return err;
}
从上面的代码中,可以看到在xxx_suspend中确实去拿s_tSemaProtect信号量,我们知道,当线程去拿信号量时,
如果不能够及时获得会进入可中断的睡眠状态,这里就可以解释上面system_server处于S状态的现象。但可中断的
睡眠状态为何会导致hang_detect呢?这里需要进一步的去看s_tSemaProtect信号量的调用,可以通过GDB看此信号量
的信息如下,count = 0此时信号量不能被拿到:
这时怀疑有地方拿到了此信号量被其他地方拿住没有释放,所以继续看信号量的调用,发现如下:
static int xxx_resume(struct i2c_client *client)
{
xxx_mutex_lock();
err = xxx_chip_resume(obj->client);
if(err) {
MI_ERR("chip resume fail!!\n");
//xxx_mutex_unlock(); //缺少这个释放锁的操作
return err;
}
err = xxx_setPowerMode(obj->client, true);
xxx_mutex_unlock();
atomic_set(&obj->suspend, 0);
return 0;
}
可以发现,在xxx_resume函数中,会获取硬件xxx_chip_resume的chip信息,当获取fail时会直接return error,
没有去做xxx_mutex_unlock的操作,导致信号一直没有被释放,这时去对应的kernel log再次验证问题:
[ 94.647859] (0)[682:system_server]Do softreset failed !
[ 94.647871] (0)[682:system_server] chip resume fail!!
解决方法是在出错时也要及时的释放信号量,这样此模块出现问题时,不会导致心痛hang住。
从此问题可以看出在问题的异常处理机制也需要做到完善,才能保证软件的正常运行。
作者:frank_zyp
您的支持是对博主最大的鼓励,感谢您的认真阅读。
本文无所谓版权,欢迎转载。