热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

内核线程卡死两例——sysrq运用简例

syncfrommygithub:https:donald-zhuang.github.io20181215SysrqInDebug这阵子一直在处理各种死机和STR开关机问题&

sync from my github:https://donald-zhuang.github.io/2018/12/15/SysrqInDebug/
  这阵子一直在处理各种死机和STR开关机问题,虽说稍微有点虐,不过技术面的扩展和更多新技巧的学习了解,也让我挺enjoy的。kernel世界对我来说,一直是一个black box,很迷。随着上阵子切bionic、看完程序员的自我修养以及这阵子为处理一个语音唤醒问题学习AN8.0的DC待机流程,发现这个是通过reboot函数做syscall进kernel待机,感觉打开一个新世界。虽然之前也是知道这条通路,但当问题和整个代码流程浮现在你面前时,一切依旧像盒子中的巧克力让你兴奋不已。


这篇文章首先通过一个实例介绍sysrq debug定位进程卡死问题,之前我没接触过sysrq,因此这次是由alix分析完之后,请他指导我怎么分析流程,而我还没去看sysrq部分的代码和实现机理。随后,介绍另一个通过修改线程在内核中的调度策略和优先级导致异常CPU占用的问题,我这边是先怀疑,然后请客户review code确认,后续有现场由Futang通过ICE分析线程参数确认问题。这次都是不太熟悉的东西,因为有bug请评论指出。


1. Driver异常问题

问题系统为AN8.0,运行在4核系统上,接USB CAM卡做切台压测,异常时UI操作能够响应,有一个进程卡死,导致TV应用卡在某一帧画面上,不运行。通过复现抓取log,分析到卡在某个接口的CMA申请上,具体如下:
在这里插入图片描述
  一般申请不到CMA就是内存不足,不过我司的平台比较特殊,一些关键IP是有专用的memory,因此一般不会出现这种问题。在异常发生时,我这边分析了整体系统的内存情况正常,对应申请的memory部分也很充足,照理不应该卡住的。又因无下手点,所以请kernel team的alix指导处理,如下也就是他的分析了。
  1. 透過sysrq check blocked state,可以知道Binder:2058_7 3(PID 3224) 目前為 block 的狀態,卡在CMA申请上面。

[ 735.108723] sysrq: SysRq : HELP : loglevel(0-9) reboot(b) crash(c) terminate-all-tasks(e) memory-full-oom-kill(f) kill-all-tasks(i) thaw-filesystems(j) sak(k) show-backtrace-all-active-cpus(l) show-memory-usage(m) nice-all-RT-tasks(n) poweroff(o) show-registers(p) show-all-timers(q) unraw(r) sync(s) show-task-states(t) unmount(u) show-blocked-tasks(w) dump-ftrace-buffer(z)
[ 738.933149] sysrq: SysRq : Show Blocked State
[ 738.937536]task PC stack pid father
[ 739.091866] Binder:2058_7 D0 3224 2012 0x00400008
[ 739.097368] Call trace:
[ 739.099820] [<ffffff800808bf8c>] __switch_to+0x8c/0xac
[ 739.104966] [<ffffff8008a3bec4>] __schedule+0x1b0/0xdac
[ 739.110196] [<ffffff8008a3caf8>] schedule+0x38/0x98
[ 739.115078] [<ffffff8008a3fab0>] schedule_timeout+0x1cc/0x374
[ 739.120829] [<ffffff8008a3d704>] wait_for_common+0xb4/0x158
[ 739.126406] [<ffffff8008a3d7bc>] wait_for_completion+0x14/0x1c
[ 739.132243] [<ffffff80080da87c>] flush_work+0xcc/0x140
[ 739.137389] [<ffffff80081e3a94>] lru_add_drain_all+0x13c/0x188
[ 739.143228] [<ffffff8008256e2c>] migrate_prep+0xc/0x18
[ 739.148373] [<ffffff80081dcc78>] alloc_contig_range+0xec/0x3f8
[ 739.154213] [<ffffff800826293c>] dma_alloc_at_from_contiguous+0x160/0x474
[ 739.161010] [<ffffff80087b9b94>] CMA_Pool_ioctl+0x5fc/0x2364
[ 739.166674] [<ffffff80087b6278>] Compat_CMA_Pool_ioctl+0xd10/0x1318
[ 739.172950] [<ffffff80082cc740>] compat_SyS_ioctl+0xbc/0x294
[ 739.178614] [<ffffff8008082e70>] el0_svc_naked+0x24/0x28

2. Blocked task Backtrace 中 lru_add_drain_all 中的 flush_work 會讓其他所有 cpu 強迫將 workqueue 的事情做完才離開,因此懷疑的方向改為分析 workqueue 卡住。注:这部分深入可以参考读写信号量与实时进程阻塞挂死问题
  3. 透過 sysrq dump workqueue status 可以看CPU2 的 workqueue 卡住了

[ 922.802525] workqueue lru-add-drain: flags=0x8
[ 922.806970]pwq 4: cpus=2 node=0 flags=0x0 nice=0 active=1/256
[ 922.813027] pending: lru_add_drain_per_cpu BAR(3224)

4. 透過 sysrq dump all cpu (特別是cpu2), 得知CPU2 目前的 PC 值為 el0_irq_naked+48/0x50

[ 2167.926823] sysrq: CPU2:
[ 2167.929358] Call trace:
[ 2167.931810] [<ffffff800808f4fc>] dump_backtrace+0x0/0x1a4
[ 2167.937210] [<ffffff800808f6b4>] show_stack+0x14/0x1c
[ 2167.942263] [<ffffff800852f4a8>] showacpu+0x5c/0x6c
[ 2167.947145] [<ffffff8008147e4c>] flush_smp_call_function_queue+0x94/0x16c
[ 2167.953934] [<ffffff8008148c5c>] generic_smp_call_function_single_interrupt+0x10/0x18
[ 2167.961767] [<ffffff8008098f40>] handle_IPI+0x180/0x298
[ 2167.966992] [<ffffff8008081514>] gic_handle_irq+0xa4/0xbc
[ 2167.972389] Exception stack(0xffffffc06cf3bec0 to 0xffffffc06cf3bff0)
[ 2167.978830] bec0: 00000000e7d00000 0000000000017800 00000000e7c02928 00000000eb76fb5c
[ 2167.986660] bee0: 00000000e7d00000 00000000e7c02928 00000000eb79ef60 0000000000000000
[ 2167.994489] bf00: 0000000000000000 00000000eb79ef58 00000000e7c02928 00000000eb79ef44
[ 2168.002319] bf20: 0000000000017800 00000000e7c02898 00000000eb5d5400 0000000000000000
[ 2168.010149] bf40: 0000000000000000 0000000000000000 0000000000000000 0000000000000000
[ 2168.017979] bf60: 0000000000000000 0000000000000000 0000000000000000 0000000000000000
[ 2168.025808] bf80: 0000000000000000 0000000000000000 0000000000000000 0000000000000000
[ 2168.033637] bfa0: 0000000000000000 0000000000000000 0000000000000000 0000000000000000
[ 2168.041468] bfc0: 00000000eb5d2b3c 00000000a00f0010 0000000000000024 ffffffffffffffff
[ 2168.049297] bfe0: 0000000000000000 0000000000000000
[ 2168.054174] [<ffffff8008082c88>] el0_irq_naked+0x48/0x50

5. 通过重复sysrq dump all CPU status,我们可以看到Exception Stack一直不变,因此查看CPU2上处于running的task确认为PID 2548占着没退出。而这个task的调用栈如下,我们可以看到异常时卡在media_intf_read这只函数上,该函数采用自旋锁,不能进行处理器抢占,其他进程无法获取CPU,因此需要请对应的kernel driver owner定位下media_intf_read卡住的原因。

[ 2167.972389] Exception stack(0xffffffc06cf3bec0 to 0xffffffc06cf3bff0)[10148.240821] hstvos R running task 0 2548 2122 0x00400000
[10148.247899] Call trace:
[10148.250353] [<ffffff800808bf8c>] __switch_to+0x8c/0xac
[10148.255497] [<ffffff80080eae84>] preempt_count_add+0x100/0x11c
[10148.261337] [<ffffff8008a406fc>] _raw_spin_lock_irqsave+0x20/0x60
[10148.267435] [<ffffff8008a409ac>] _raw_spin_unlock_irqrestore+0x20/0x44
[10148.273968] [<ffffff800810cc78>] finish_wait+0x5c/0x7c
[10148.279112] [<ffffff80086683c0>] media_intf_read+0x154/0x374
[10148.284775] [<ffffff8008266ea8>] __vfs_read+0x28/0x108
[10148.289917] [<0000000000000001>] 0x1

在达成如上分析后,请客户找USB CAM卡驱动的设计者,回复信息如下,这个反馈也符合了问题现象,异常时拔卡、发signal都能恢复正常。问题也就会回归为media_intf_write为何没有执行了,客户反馈如果hs_UsbCam_Create没有执行完就会出现media_intf_write不执行。


usbcam读写设计是阻塞的,如果没有media write的话,那media read接口会一直阻塞(除非拔出usbcam、close fd或者被信号杀死等特殊情况)。


通过生成coredump确认异常时这个函数卡住的位置,再与客户沟通上层实现逻辑(看不到code…),我梳理出如下一个死锁通路。因为两个流程是在不同线程上跑的,且未加同步机制保证时序,故而有机会出现deadlock。分析完,这个问题的修正方式也就浮出水面:只要保证media_intf_read在media_intf_write之后执行,保证hs_usbcam_create执行完之后才能执行vhal_start,就能避免死锁条件的形成,问题也就解决。
在这里插入图片描述


2. CPU占用100%问题

修正如上问题后,出现小概率客户中间件进程100%占用某颗CPU的情况出现。采用同样的方式分析,可以看到一个未知线程一直占住CPU没有放。直觉怀疑是客户对线程调度策略进行修改,后请客户review code,确认了假想。其实现类似如下,也就是将进程设定为FIFO且优先级为最高。我们后面通过ICE也可以看到异常线程的这个参数设定。

pthread_attr_t attr;
struct sched_param param;
int rs;
int policy;rs=pthread_attr_init(&attr);
pthread_attr_setschedpolicy(&attr, SCHED_FIFO);
param.sched_priority = 99;
pthread_attr_setschedparam(&attr, ¶m);
pthread_create(&threadId, attr, (void*)thread_start_task, NULL);
pthread_attr_destroy(&attr);

从Linux Programmer’s Manual的SCHED一节可以看到如下说明,SCHED_FIFO一旦占用cpu便会一直运行(CHED_FIFO is a simple scheduling algorithm without time slicing.),直到有IO请求、更高优先级任务到达或主动退出(sched_yield)才释放CPU,他和RR都是有很高的调度优先级的。因此代码上对线程调度控制的修改也要十分谨慎,不然很容易出现问题。

SCHED_FIFO: First in-first out schedulingSCHED_FIFO can be used only with static priorities higher than 0,which means that when a SCHED_FIFO threads becomes runnable, it willalways immediately preempt any currently running SCHED_OTHER,SCHED_BATCH, or SCHED_IDLE thread. SCHED_FIFO is a simple schedulingalgorithm without time slicing. For threads scheduled under theSCHED_FIFO policy, the following rules apply:1) A running SCHED_FIFO thread that has been preempted by anotherthread of higher priority will stay at the head of the list forits priority and will resume execution as soon as all threads ofhigher priority are blocked again.2) When a blocked SCHED_FIFO thread becomes runnable, it will beinserted at the end of the list for its priority.3) If a call to sched_setscheduler(2), sched_setparam(2),sched_setattr(2), pthread_setschedparam(3), orpthread_setschedprio(3) changes the priority of the running orrunnable SCHED_FIFO thread identified by pid the effect on thethread&#39;s position in the list depends on the direction of thechange to threads priority:· If the thread&#39;s priority is raised, it is placed at the end ofthe list for its new priority. As a consequence, it maypreempt a currently running thread with the same priority.· If the thread&#39;s priority is unchanged, its position in the runlist is unchanged.· If the thread&#39;s priority is lowered, it is placed at the frontof the list for its new priority.According to POSIX.1-2008, changes to a thread&#39;s priority (orpolicy) using any mechanism other than pthread_setschedprio(3)should result in the thread being placed at the end of the listfor its priority.4) A thread calling sched_yield(2) will be put at the end of thelist.No other events will move a thread scheduled under the SCHED_FIFOpolicy in the wait list of runnable threads with equal staticpriority.A SCHED_FIFO thread runs until either it is blocked by an I/Orequest, it is preempted by a higher priority thread, or it callssched_yield(2).

推荐阅读
  • 海马s5近光灯能否直接更换为H7?
    本文主要介绍了海马s5车型的近光灯是否可以直接更换为H7灯泡,并提供了完整的教程下载地址。此外,还详细讲解了DSP功能函数中的数据拷贝、数据填充和浮点数转换为定点数的相关内容。 ... [详细]
  • 深入解析Linux下的I/O多路转接epoll技术
    本文深入解析了Linux下的I/O多路转接epoll技术,介绍了select和poll函数的问题,以及epoll函数的设计和优点。同时讲解了epoll函数的使用方法,包括epoll_create和epoll_ctl两个系统调用。 ... [详细]
  • STM32 IO口模拟串口通讯
    转自:http:ziye334.blog.163.comblogstatic224306191201452833850647前阵子,调项目时需要用到低波 ... [详细]
  • 作者一直强调的一个概念叫做oneloopperthread,撇开多线程不谈,本篇博文将学习,怎么将传统的IO复用pollepoll封装到C++类中。1.IO复用复习使用p ... [详细]
  • MySQL数据库锁机制及其应用(数据库锁的概念)
    本文介绍了MySQL数据库锁机制及其应用。数据库锁是计算机协调多个进程或线程并发访问某一资源的机制,在数据库中,数据是一种供许多用户共享的资源,如何保证数据并发访问的一致性和有效性是数据库必须解决的问题。MySQL的锁机制相对简单,不同的存储引擎支持不同的锁机制,主要包括表级锁、行级锁和页面锁。本文详细介绍了MySQL表级锁的锁模式和特点,以及行级锁和页面锁的特点和应用场景。同时还讨论了锁冲突对数据库并发访问性能的影响。 ... [详细]
  • Windows7企业版怎样存储安全新功能详解
    本文介绍了电脑公司发布的GHOST WIN7 SP1 X64 通用特别版 V2019.12,软件大小为5.71 GB,支持简体中文,属于国产软件,免费使用。文章还提到了用户评分和软件分类为Win7系统,运行环境为Windows。同时,文章还介绍了平台检测结果,无插件,通过了360、腾讯、金山和瑞星的检测。此外,文章还提到了本地下载文件大小为5.71 GB,需要先下载高速下载器才能进行高速下载。最后,文章详细解释了Windows7企业版的存储安全新功能。 ... [详细]
  • 深入理解Java虚拟机的并发编程与性能优化
    本文主要介绍了Java内存模型与线程的相关概念,探讨了并发编程在服务端应用中的重要性。同时,介绍了Java语言和虚拟机提供的工具,帮助开发人员处理并发方面的问题,提高程序的并发能力和性能优化。文章指出,充分利用计算机处理器的能力和协调线程之间的并发操作是提高服务端程序性能的关键。 ... [详细]
  • 本文介绍了使用C++Builder实现获取USB优盘序列号的方法,包括相关的代码和说明。通过该方法,可以获取指定盘符的USB优盘序列号,并将其存放在缓冲中。该方法可以在Windows系统中有效地获取USB优盘序列号,并且适用于C++Builder开发环境。 ... [详细]
  • 这个问题困扰了我两天,卸载Dr.COM客户端(我们学校上网要装这个客户端登陆服务器,以后只能在网页里输入用户名和密码了),问题解决了。问题的现象:在实验室机台式机上安装openfire和sp ... [详细]
  • Ansibleplaybook roles安装redis实例(学习笔记二十九)
    1、相关redis参数:2、templatesredis.conf配置相关参数:daemonizeyespidfilevarrunredis_{{red ... [详细]
  • Linux I2C 几个结构体间的关系以及对于一个I2C设备的移植,我们需要做些什么工作
    在Linux内核源代码中的drivers目录下包含一个i2c目录,而在i2c目录下又包含如下文件和文件夹:•i2c-core.c这个文件实现了I2C核心的功能以及proc ... [详细]
  • Linux 中使用 clone 函数来创建线程
    2019独角兽企业重金招聘Python工程师标准Linux上创建线程一般使用的是pthread库实际上libc也给我们提供了创建线程的函数那就是cloneintclone(i ... [详细]
  • http:oj.leetcode.comproblemsminimum-depth-of-binary-tree贡献了一次runtimeerror,因为如果输入为{}即空的时候,出 ... [详细]
  • 主线:设计窗口类注册窗口类产生窗口显示窗口更新窗口消息循环(将消息路由到窗口中去处理)。APPMODUL.CPP源文件被编译链接进入项目,从APPMOD ... [详细]
  • monkey初接触
    第一次听说monkey,根本不知道是什么东西,脑海里就一个印象,很厉害的自动化测试工具,可是体验了一下,似乎不 ... [详细]
author-avatar
lys焦糖布丁cj_625
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有