一、原理:
MTK平台有纯软件上的看门狗机制hang_detect,hang_detect机制是负责监控应用层重要服务(如system_server等),
当然硬件上也有看门狗机制WDT,防止CPU一直卡住不去执行其他的工作,导致CPU不再进行调度的原因很多:
- 长时间关闭中断。这个直接无法调度了,还可以细分为:
- 关中断的CPU死锁
- 关中断后陷入循环
- 中断频繁触发。则导致CPU超载,进程基本没有机会被执行到。
- 长时间关抢断。
- 其他HW故障,如总线卡死或HW不稳定。
喂狗的方式如下:
-
bit位代表CPU的喂狗状态:CPU核数每个平台都不太一样,我们用bit位代表1个CPU,比如4个CPU就用4个bit,我们用kick_bit变量表示。
-
喂狗进程负责喂狗:在喂狗模块初始化时针对每个CPU创建的对应的内核实时进程(优先级最高)并绑定到对应的CPU上(也就是说这个进程只运行在指定CPU上,不会发生迁移),这些进程喂狗(其实就是将kick_bit对应的bit位置1而已,并没有真正喂狗)后进入睡眠,喂狗间隔是20s,如此循环反复。
-
真正喂外部WDT狗:任何一个喂狗进程在喂狗之后,会检查kick_bit变量,如果该值等于cpus_kick_bit变量(这个变量的每个bit代表对应CPU online情况,如果CPU online,对应bit置1),表示所有CPU都已喂狗,这时才真正喂外部WDT狗,同时将kick_bit清0,进入下一轮喂狗流程,如此循环反复。
复位的方式dual mode:
喂狗间隔是20s,而超时时间是30s,也就是说最长能容忍卡住的时间是10s(卡一小会还是可以的),
超过这个时间,系统就会复位了。这里还有问题,由于喂狗进程之间没有同步,是否有可能存在刚开始一
起喂狗,之后渐渐出现先后呢?误差肯定有的,但在任何30s时间里,喂狗进程都会喂1次狗的(因为喂狗
间隔20s,每个CPU肯至少会喂1次狗)。而只要CPU卡死超过10s就复位了。
二、实际案例解析:
MTK平台发生HWT时会生成对应的DB文件,可以使用GAT工具解析,解析后有很多的文件生成,
HWT问题最先关注SYS_LAST_KMSG文件,此文件会保存重启前的信息,检索kick关键字,可以得到如下:
kick=0x0000000e,check=0x000000ff
上面check显示共8个cpu online,但正常喂狗的cpu只有1110(即CPU1 CPU2 CPU3 ),其他CPU都没有
正常喂狗,log的下方会打出未喂狗CPU的栈信息:
cpu 0 preempt=1, softirq=0, hardirq=0
cpu 0 x0->x30 sp pc pstate
cpu 0 backtrace : ffffff9f313b5288, ffffff9f30817570, ffffff9f308180a8, ffffff9f31118794, ffffff9f31112400, ffffff9f31111068, ffffff9f3083e194, ffffff9f3083e5d4, ffffff9f306835bc,
cpu 4 preempt=2, softirq=0, hardirq=0
cpu 4 x0->x30 sp pc pstate
cpu 4 backtrace : ffffff9f309e1060, ffffff9f309e044c, ffffff9f309e1390, ffffff9f307229bc, ffffff9f307226f8, ffffff9f307208cc, ffffff9f3068b4f4, ffffff9f3068b324, ffffff9f309d4114, ffffff9f307d0358, ffffff9f307d1504, ffffff9f307d1f70, ffffff9f3081e17c, ffffff9f30819a74, ffffff9f30853004, ffffff9f308a7d78,
cpu 5 preempt=1, softirq=0, hardirq=0
cpu 5 x0->x30 sp pc pstate
cpu 5 backtrace : ffffff9f313b5288, ffffff9f30817570, ffffff9f308180a8, ffffff9f308c52c4, ffffff9f3083e6a0, ffffff9f3083e994, ffffff9f306835bc,
cpu 6 preempt=1, softirq=0, hardirq=0
cpu 6 x0->x30 sp pc pstate
cpu 6 backtrace : ffffff9f313b5288, ffffff9f30817570, ffffff9f308180a8, ffffff9f31118794, ffffff9f31112400, ffffff9f31111068, ffffff9f3083e194, ffffff9f3083e5d4, ffffff9f306835bc,
cpu 7 preempt=1, softirq=0, hardirq=0
cpu 7 x0->x30 sp pc pstate
cpu 7 backtrace : ffffff9f313b5288, ffffff9f30817570, ffffff9f308180a8, ffffff9f30853d50, ffffff9f308abdb4, ffffff9f30825880, ffffff9f30825ac4, ffffff9f30835ccc, ffffff9f308353dc, ffffff9f30826074, ffffff9f308261c0, ffffff9f306835bc,
使用add2line加载对应的代码位置add2line -Cfe vmlinx 0xffffff9f308180a8,当前也有对应的KSST工具可以一次
性的解析,解析的结果如下,结果太长,我只看CPU0的栈信息:
CPU0: [email protected], pid: 420, 已执行16秒, 中断: 关
本地调用栈:
vmlinux arch_spin_lock(lock=0xFFFFFFC9BB000F00) + 36 <arch/arm64/include/asm/spinlock.h:89>
vmlinux do_raw_spin_lock(lock=0xFFFFFFC9BB000F00) + 36 <include/linux/spinlock.h:150>
vmlinux __raw_spin_lock(lock=0xFFFFFFC9BB000F00) + 56 <include/linux/spinlock_api_smp.h:145>
vmlinux _raw_spin_lock(lock=0xFFFFFFC9BB000F00) + 76 <kernel/locking/spinlock.c:151>
vmlinux spin_lock() + 8 <include/linux/spinlock.h:304>
vmlinux get_partial_node(n=0xFFFFFFC9BB000F00) + 16 <mm/slub.c:1905>
vmlinux get_partial() + 24 <mm/slub.c:2012>
vmlinux new_slab_objects() + 24 <mm/slub.c:2511>
vmlinux ___slab_alloc(s=0xFFFFFFC9BB001180, gfpflags=37781696, node=823232408, c=0xFFFFFFC9BFEADEA0) + 516 <mm/slub.c:2673>
vmlinux __slab_alloc() + 32 <mm/slub.c:2715>
vmlinux slab_alloc_node() + 668 <mm/slub.c:2778>
vmlinux slab_alloc(gfpflags=37781696) + 668 <mm/slub.c:2829>
vmlinux __kmalloc(size=24, flags=37781696) + 740 <mm/slub.c:3843>
vmlinux kmalloc() + 16 <include/linux/slab.h:499>
vmlinux kzalloc() + 16 <include/linux/slab.h:640>
vmlinux binder_transaction() + 3456 <drivers/android/binder.c:3773>
vmlinux binder_thread_write(thread=0xFFFFFFC9B05B3C80, binder_buffer=505330041088) + 1108 <drivers/android/binder.c:4411>
vmlinux binder_ioctl_write_read() + 1344 <drivers/android/binder.c:5382>
vmlinux binder_ioctl() + 456 <drivers/android/binder.c:5521>
vmlinux vfs_ioctl(filp=0xFFFFFFC9AE42F000, arg=549515263192) + 28 <fs/ioctl.c:43>
== 栈结束 ==
可以看出CPU0卡在了binder通讯时,分配获取spin_lock的代码中,一层层的查看代码,看到:
static void *get_partial_node(struct kmem_cache *s, struct kmem_cache_node *n,
struct kmem_cache_cpu *c, gfp_t flags)
{
spin_lock(&n->list_lock);
......
spin_unlock(&n->list_lock);
}
struct kmem_cache_node {
......
spinlock_t list_lock;
}
这里申请n->list_lock的锁,可能会一直等待导致超时触发HWT,kmem_cache_node结构体用于描叙
slab分配内存链表。所以这里猜测可能给会内存不足相关的导致这里一直获取不到spin_lock导致超时,
这是继续去SYS_LAST_KMSG中去寻找线索,可以看到,上面一直在打印如下log:
[14696.733669] DMA free:130760kB min:5704kB low:28116kB high:30988kB active_anon:470692kB inactive_anon:258076kB active_file:875336kB inactive_file:429480kB unevictable:224kB writepending:6664kB present:3037084kB managed:2873852kB mlocked:224kB slab_reclaimable:134232kB slab_unreclaimable:122188kB kernel_stack:21424kB pagetables:31848kB bounce:0kB free_pcp:4696kB local_pcp:280kB free_cma:0kB
[14696.733683] lowmem_reserve[]: 0 952 952
[14696.733735] Normal free:9468kB min:1932kB low:9532kB high:10504kB active_anon:175628kB inactive_anon:74524kB active_file:360748kB inactive_file:74600kB unevictable:5120kB writepending:1388kB present:1048572kB managed:975056kB mlocked:5120kB slab_reclaimable:48264kB slab_unreclaimable:93916kB kernel_stack:14256kB pagetables:24204kB bounce:0kB free_pcp:3780kB local_pcp:668kB free_cma:0kB
[14696.733750] lowmem_reserve[]: 0 0 0
[14696.733780] DMA: 15716*4kB (UME) 8091*8kB (UME) 198*16kB (UME) 0*32kB 0*64kB 0*128kB 0*256kB 0*512kB 0*1024kB 0*2048kB 0*4096kB = 130760kB
[14696.733873] Normal: 953*4kB (UEH) 599*8kB (UMEH) 27*16kB (UMEH) 14*32kB (H) 0*64kB 0*128kB 0*256kB 0*512kB 0*1024kB 0*2048kB 0*4096kB = 9484kB
[14696.734100] aee_dumpstate: page allocation failure: order:4, mode:0x80020(__GFP_HIGH|__GFP_ATOMIC)
[14696.734161] -(4)[12955:aee_dumpstate]Call trace:
[14696.734182] -(4)[12955:aee_dumpstate][<ffffff9f3068b330>] dump_backtrace+0x0/0x294
[14696.734203] -(4)[12955:aee_dumpstate][<ffffff9f3068b328>] show_stack+0x18/0x20
[14696.734225] -(4)[12955:aee_dumpstate][<ffffff9f309d4118>] dump_stack+0xcc/0x104
[14696.734247] -(4)[12955:aee_dumpstate][<ffffff9f307d035c>] warn_alloc+0x114/0x148
[14696.734269] -(4)[12955:aee_dumpstate][<ffffff9f307d1508>] __alloc_pages_nodemask+0x1118/0x1120
[14696.734291] -(4)[12955:aee_dumpstate][<ffffff9f307d1f74>] __get_free_pages+0x20/0x38
[14696.734313] -(4)[12955:aee_dumpstate][<ffffff9f3081e180>] mtk_memcfg_process_slab+0x288/0x49c
[14696.734334] -(4)[12955:aee_dumpstate][<ffffff9f30819a78>] mtk_memcfg_slabtrace_show+0x1ac/0x35c
[14696.734354] -(4)[12955:aee_dumpstate][<ffffff9f30853008>] seq_read+0x164/0x470
[14696.734374] -(4)[12955:aee_dumpstate][<ffffff9f308a7d7c>] proc_reg_read+0x70/0xbc
[14696.734394] -(4)[12955:aee_dumpstate][<ffffff9f30826f60>] __vfs_read+0x44/0x120
[14696.734414] -(4)[12955:aee_dumpstate][<ffffff9f308270dc>] vfs_read+0xa0/0x138
[14696.734433] -(4)[12955:aee_dumpstate][<ffffff9f30827588>] SyS_read+0x54/0xb4
[14696.734453] -(4)[12955:aee_dumpstate][<ffffff9f306835c0>] el0_svc_naked+0x34/0x38
这里显示page allocation failure: order:4, mode:0x80020(__GFP_HIGH|__GFP_ATOMIC)
2^4 * 4kB 这里申请64KB的内存一直失败,看memory信息64KB以及大于64KB的的内存已经没有:
可以继续看下这里代码:
static int mtk_memcfg_list_locations(struct kmem_cache *s, struct seq_file *m,
enum track_item alloc)
{
......
for_each_kmem_cache_node(s, node, n) {
//这里会获取n->list_lock
spin_lock_irqsave(&n->list_lock, flags);
list_for_each_entry(page, &n->partial, lru)
mtk_memcfg_process_slab(&t, s, page, alloc, map);
list_for_each_entry(page, &n->full, lru)
mtk_memcfg_process_slab(&t, s, page, alloc, map);
spin_unlock_irqrestore(&n->list_lock, flags);
}
......
}
这里申请的比较大块的内存,如果一直分配不到内存,会导致n->list_lock锁一直持有不释放的情况,
其他CPU在尝试获取此锁时,一直等待在哪里,最终超时触发HWT的问题。
作者:frank_zyp
您的支持是对博主最大的鼓励,感谢您的认真阅读。
本文无所谓版权,欢迎转载。