Android稳定性KE原子锁[Android稳定性] 第005篇 [问题篇] 原子状态调度引起死机
thonmin
0. 问题现象
锁的使用不当往往会导致死机异常:
之前碰到过一例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| [2021:11:18 15:28:05](1)BUG: scheduling while atomic: Binder:1199_2/5201/0x00000002 [2021:11:18 15:28:05](1)------------[ cut here ]------------ [2021:11:18 15:28:05](1)kernel BUG at kernel/sched/walt/walt_debug.c:16! [2021:11:18 15:28:05](1)pc : android_rvh_schedule_bug+0x4/0x8 [sched_walt_debug] [2021:11:18 15:28:05](1)lr : __schedule_bug+0x100/0x138 [2021:11:18 15:28:05](1)Call trace: [2021:11:18 15:28:05](1) android_rvh_schedule_bug+0x4/0x8 [sched_walt_debug] [2021:11:18 15:28:05](1) __schedule+0x6a0/0xacc [2021:11:18 15:28:05](1) schedule+0x68/0x190 [2021:11:18 15:28:05](1) __mutex_lock+0x39c/0x9c8 [2021:11:18 15:28:05](1) __mutex_lock_slowpath+0x18/0x28 [2021:11:18 15:28:05](1) clk_get_parent+0xd0/0x254 [2021:11:18 15:28:05](1) clock_debug_print_enabled_clocks+0xdc/0x268 [clk_qcom] [2021:11:18 15:28:05](1) clk_debug_suspend_trace_probe+0x68/0x74 [clk_qcom] [2021:11:18 15:28:05](1) s2idle_enter+0x294/0x40c [2021:11:18 15:28:05](1) suspend_enter+0x158/0x7c8 [2021:11:18 15:28:05](1) suspend_devices_and_enter+0x134/0x560 [2021:11:18 15:28:05](1) enter_state+0x26c/0x724 [2021:11:18 15:28:05](1) state_store+0x15c/0x1e8 [2021:11:18 15:28:05](1) kobj_attr_store+0x38/0x88 [2021:11:18 15:28:05](1) sysfs_kf_write+0x64/0xc0 [2021:11:18 15:28:05](1) kernfs_fop_write_iter+0x1a4/0x48c [2021:11:18 15:28:05](1) vfs_write+0x300/0x374 [2021:11:18 15:28:05](1) ksys_write+0x7c/0x150 [2021:11:18 15:28:05](1) __arm64_sys_write+0x20/0x30 [2021:11:18 15:28:05](1) el0_svc_common+0xc4/0x250 [2021:11:18 15:28:05](1) el0_svc+0x38/0x9c [2021:11:18 15:28:05](1) el0_sync_handler+0x8c/0xf0 [2021:11:18 15:28:05](1) el0_sync+0x1a8/0x1c0
|
1. 问题分析
其实错误原因log中已经很直白的告诉你了:
BUG: scheduling while atomic: Binder:1199_2/5201/0x00000002 : 在原子状态下执行了调度命令,而原子状态是不允许调度的。
具体原因可以参考这篇文章:Linux中的preempt_count - 知乎
这类问题,其实反而比较容易定位,看调用栈,找到出问题的函数,然后看这个函数附近是不是有mutex_lock,spin_lock之类的操作。
上面这个例子是一个开源社区的人上传的,涉及逻辑比较多,只能找高通的人帮忙反馈了。
2. 另一个案例
下面是一个典型的错误代码,也是报错scheduling while atomic
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| static int onewr_open_ic(void) { opt_swr_slk_irqsave(); opt_get_gpio_dir(); opt_gpio_dir_output(HIGH); opt_swr_slk_irqstore(); return 0; }
void opt_swr_slk_irqsave(void) { if (!g_lock_status) { spin_lock_irqsave(g_onewr_lock, g_slk_flags); g_lock_status = true; } } EXPORT_SYMBOL(opt_swr_slk_irqsave); void opt_swr_slk_irqstore(void) { if (g_lock_status) { spin_unlock_irqrestore(g_onewr_lock, g_slk_flags); g_lock_status = false; } } EXPORT_SYMBOL(opt_swr_slk_irqstore);
|
3. 原因分析
当有两个cpu同时执行这个加锁解锁操作的时候,
- CPU0 先持锁,CPU1等锁。此时g_lock_status = true
- CPU0 释放锁,CPU1持锁。这里就会出问题,是CPU0先把g_lock_status = false,还是CPU1先把g_lock_status = true;
- 如果cpu0卡了下,cpu1先设置g_lock_status = true;,然后cpu0才设置g_lock_status = false;那么,当cpu3执行完,准备释放锁的时候,发现g_lock_status = false;就不会执行释放操作。
- 紧接着,cpu3准备调度去执行其他线程的时候,就会报错,因为它还持着锁,处于原子状态。
这个问题的解决方法就是把全局变量去掉,完全是多此一举的代码逻辑。因为本来就是成对使用的。
4. 总结
我们要记住,锁就是用来保护临界区的,也可以狭义的理解为,全局变量/资源。
不能反过来用全局变量来控制加解锁,这种大部分都会出问题,特别是现在都是多核的系统,会并发执行的。


thonmin
人生南北多歧路,君向潇湘我向秦
此文章版权归thonmin所有,如有转载,请注明来自原作者