如下是一个kernel的panic的Oops和堆栈信息:
panic info:
PCis at __run_hrtimer+0x230/0x298
[19816.115957] c0 LR is at__raw_spin_lock+0x2c/0x94
[19816.115968] c0 pc :[<c0064db4>] lr :[<c0593884>] psr: 20000193
[19816.115974] c0 sp : caa3dbe8 ip : caa3dbc0 fp : caa3dc1c
[19816.115983] c0 r10: c0080e44 r9 : c0f14a00 r8 : 00000001
[19816.115992] c0 r7 : 00000002 r6 : c0f14a00 r5 : c0f14aa8 r4 : c093c4b0
[19816.116002] c0 r3 : 00000003 r2 : 00000103 r1 : 00000000 r0 : 00000001
[19816.116013] c0 Flags: nzCv IRQs off FIQs on Mode SVC_32 ISA ARM Segment user
[19816.116023] c0 Control: 10c53c7d Table: 9aa0006a DAC: 00000015
....................
.......................
.........................
[19816.118270] c0 [<c0064db4>](__run_hrtimer+0x230/0x298) from [<c00659f0>](hrtimer_interrupt+0x120/0x248)
[19816.118289] c0 [<c00659f0>](hrtimer_interrupt+0x120/0x248) from [<c0065b64>] (__hrtimer_peek_ahead_timers.part.9+0x4c/0x58)
[19816.118308] c0 [<c0065b64>](__hrtimer_peek_ahead_timers.part.9+0x4c/0x58) from [<c0065bbc>](hrtimer_peek_ahead_timers+0x4c/0x74)
[19816.118327] c0 [<c0065bbc>](hrtimer_peek_ahead_timers+0x4c/0x74) from [<c0065bfc>](run_hrtimer_softirq+0x18/0x1c)
[19816.118346] c0 [<c0065bfc>](run_hrtimer_softirq+0x18/0x1c) from [<c004771c>](__do_softirq+0x150/0x29c)
[19816.118364] c0 [<c004771c>](__do_softirq+0x150/0x29c) from [<c0047d68>] (irq_exit+0x64/0xac)
[19816.118383] c0 [<c0047d68>](irq_exit+0x64/0xac) from [<c0010234>] (handle_IRQ+0x8c/0xc8)
[19816.118401] c0 [<c0010234>](handle_IRQ+0x8c/0xc8) from [<c00093a8>] (gic_handle_irq+0xd8/0x188)
[19816.118420] c0 [<c00093a8>](gic_handle_irq+0xd8/0x188) from [<c000f3c4>] (__irq_svc+0x44/0x78)
[19816.118431] c0 Exceptionstack(0xcaa3dd50 to 0xcaa3dd98)
[19816.118442] c0 dd40: d8bc2f2c60000013 00000000 c08c2cec
[19816.118457] c0 dd60: 60000013 6000001300000001 00000001 00000000 d4421c00 cccc8b00 caa3ddac
[19816.118471] c0 dd80: caa3dd98 caa3dd98c0593590 c0593594 60000013 ffffffff
[19816.118489] c0 [<c000f3c4>](__irq_svc+0x44/0x78) from [<c0593594>](_raw_spin_unlock_irqrestore+0x48/0x70)
[19816.118510] c0 [<c0593594>](_raw_spin_unlock_irqrestore+0x48/0x70) from [<c006a0f0>](__wake_up+0x54/0x5c)
[19816.118532] c0 [<c006a0f0>](__wake_up+0x54/0x5c) from [<c03e4fd0>](binder_thread_write+0x21c0/0x2488)
[19816.118552] c0 [<c03e4fd0>](binder_thread_write+0x21c0/0x2488) from [<c03e65c4>](binder_ioctl+0x38c/0x998)
[19816.118571] c0 [<c03e65c4>](binder_ioctl+0x38c/0x998) from [<c011694c>] (do_vfs_ioctl+0x4f4/0x568)
[19816.118590] c0 [<c011694c>](do_vfs_ioctl+0x4f4/0x568) from [<c0116a08>] (sys_ioctl+0x48/0x6c)
[19816.118608] c0 [<c0116a08>](sys_ioctl+0x48/0x6c) from [<c000f840>] (ret_fast_syscall+0x0/0x48)
对于这个问题,现在的分析已经到了怎么来定位问题,整个过程大概如下:
从函数__run_hrtimer反汇编中(下面只贴了三行汇编语句,事实上分析的话,要把整个函数的反汇编都要分析一遍。)
0xc0064dac <__run_hrtimer+552>:cmp r3, #2
0xc0064db0 <__run_hrtimer+556>: beq 0xc0064db8 <__run_hrtimer+564>
0xc0064db4 <__run_hrtimer+560>: ; <UNDEFINED>instruction: 0xe7f001f2
这里面的r3是跟立即数2比较也就是对应代码的BUG_ON(timer->state != HRTIMER_STATE_CALLBACK);中的HRTIMER_STATE_CALLBACK比较,这个宏定义就是0x2,但现在的r3在Oops里面的值是0x3,(事实上我们怎么定位到刚才的BUG_ON这行语句的,首先Oops里面有kernel BUGat/home/raoli/work/8830_branch/20140305_2227_P8360_DZTX_eng/MocorDroid_4.1.3_sc8830/idh.code/kernel/kernel/hrtimer.c:1228!语句,其次可以用gdb命令:b *PC指针的地址进行定位,最后就是分析汇编进行最后的定位确认)
[19816.115992] c0 r7 : 00000002 r6 : c0f14a00 r5 : c0f14aa8 r4 : c093c4b0
[19816.116002] c0 r3 : 00000003 r2 : 00000103 r1 : 00000000 r0 : 00000001
从这里推断出timer->state=0x3的。
再分析分析反汇编这个函数的参数1也就是刚压栈的时候的r0其实一直保存在r4中,也就是地址c093c4b0中,从crash工具的命令:struct hrtimer c093c4b0,可以将结构体进行还原如下而且其state = 3,这跟前面的推断是一至的,这点也证明了这个结构体是正确的,
struct hrtimer {
node = {
node = {
rb_parent_color = 3232271248,
rb_right = 0x0,
rb_left = 0x0
},
expires = {
tv64 = 20494574000000
}
},
_softexpires = {
tv64 = 20494574000000
},
function = 0xc0080e44 <alarmtimer_fired>,
base = 0xc0f14aa8,
state = 3
}
这个结构体的作用是什么呢?最主要的是 function = 0xc0080e44 <alarmtimer_fired>,这个才是这个问题的关键和重点,也就是说__run_hrtimer函数出现panic的时候,里面的回调函数是alarmtimer_fired,这样这个问题点已经找到了,是linux内核本身的问题,是3.4.5的版本,本来前面还曾怀疑过我们自己所使用的hrtimer,我们的震动确实使用了hrtimer,但他的返回值不满足发生panic的if语句,所以被排除了,最后也通过客观条件定位到alarmtimer_fired函数。
现在再来讨论这个alarmtimer_fired,这个是在kernel/kernel/time/alarmtimer.c中,这个文件的代码从3.1开始就疯狂的升级,3.1之前,这个文件只叫做alarm.c,稍微看来下几个版本,里面的内容都有变化,像我们公司最新的内核版本3.10的这个文件里面的内容跟客户使用的3.4的版本的差别非常大,去kernel论坛搜索了下,这个模块的patch倒是挺多,但针对我们这个问题的patch还真没有。
说了这么多大家可能会感觉哪里是不是有点不对,就说这个问题为什么会产生呢?大家可以去看下客户版本内核下alarmtimer_fired函数,这个函数非常的诡异,里面有两对关中断的自旋锁,但中间有一行是执行一个函数却没有用关中断的自旋锁包起来,而从堆栈信息可以看出,这个就是因为中断来了然后引起了这个问题。
这里没有分析解决方案,由于时间有限,而且是linux内核的本身的问题,短时间内无法解决,事实上这个模块也不停的进行了升级,解决这个问题的最好方法肯怕是内核升级。