[Kernel_exception4] mtk HWT debug

一、原理:

  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
您的支持是对博主最大的鼓励,感谢您的认真阅读。
本文无所谓版权,欢迎转载。

猜你喜欢

转载自blog.csdn.net/frank_zyp/article/details/86537459
MTK