softlockup原理分析
一、概述
Softlockup主要用于检查cpu上的任务是否有无法被调度的情况发生。其原理就是在cpu上创建一个实时FIFO优先级为99的percpu内核线程(一般情况下可以认为是系统中优先级最高的任务),其名字为watchdog;此任务一般会由一个高精度定时器htimer定期唤醒,唤醒后watchdog线程会去执行“喂狗”操作(具体而言就是将当前的时间戳写到变量watchdog_touch_ts)。同时,c这个htimer定时器还会定期检查此cpu上“喂狗”是否有发生,即判断当前时间戳与watchdog_touch_ts的差值是否大于一个门限,内核用get_softlockup_thresh()函数来取得此门限值,默认为20秒。 如果htimer定时器检测到watchdog_touch_ts距离现在已经超过了门限值就判断为发生了softlockup。
触发softlockup的场景有许多种可能,例如在某个cpu上有优先级99的死循环FIFO任务;或者系统中有不健康的软中断在长时间进行软中断处理;又或者内核中产生了死循环等等
二、检测原理
在上面已经了解到softlockup的检测依赖于两个对象:watchdog线程和定时器htimer。
定时器htimer。 这个定时器的周期由内核中的变量sample_period来表示,默认为4秒。这个变量又由内核中的另外一个变量watchdog_thresh计算而来,其具体计算方式为:sample_period=watchdog_thresh * 2*(NSEC_PER_SEC/5),单位纳秒。内核中watchdog_thresh默认为10,用户可以通过/proc/sys/kernel/watchdog_thresh来指定。例如执行
echo 15 > /proc/sys/kernelwatchdog_thresh
后,sample_period的值,即定时器的周期就设置为6秒。
当定时器到期后会调用htimer的时钟处理函数watchdog_timer_fn()做如下事情:
(1) 唤醒watchdog线程;
(2) 检查是否有发生softlockup;
(3) 如果有发生softlockup则进行处理。
这里第(2)步是如何检查是否有发生softlockup呢?原理就是检查上一次watchdog线程“喂狗”时距离现在的时间间隔是否超过了门限值softlockup_thresh,默认为20秒。其计算方式为softlockup_thresh=watchdog_thresh * 2。 上面已经提过watchdog_thresh默认为10,可通过/proc接口修改。
watchdog内核线程。 看门狗线程watchdog是由内核创建的percpu线程,创建后一直睡眠,然后等待htimer周期性的唤醒自己。被唤醒后watchdog线程就会去“喂狗”,即将当前时间戳写入到percpu变量watchdog_touch_ts中;
正常情况下每隔每隔sample_period秒的实际watchdog线程就会被唤醒一次,由于watchdog线程的优先级是实时FIFO调度策略99的优先级,因而它是系统中优先级最高的任务了。由于它首屈一指的高优先级,可以预测的是,唤醒后它会有机会立刻得到运行(除非系统中也有优先级为99的FIFO任务在运行状态),即去更新watchdog_touch_ts变量。因而,我们还可以预测,正常情况下watchdog_touch_ts每隔sample_period秒就会更新一次,或者说递增一次,递增的粒度也正好是htimer时钟的周期sample_period。
这种预测在何时可能不成立呢?
(1) watchdog没有被及时唤醒。这种情况就是htimer没有及时触发;由于htimer唤醒是在高精度时钟中断中完成的,除非时钟中断出现问题,否则watchdog不会出现这种情况。而这种情况多半是长时间关闭本地中断造成的,这种情况一般需要通过hardlockup机制来检测;
(2) 系统中有长时间处于软中断处理函数。虽然watchdog线程优先级很高,但是只是任务级别的执行流,对于软中断执行流程是优先级更低的。假如系统中的有一个软中断由于某种原因进入到了死锁或者死循环,那么watchdog线程是无法执行的,这样watchdog_touch_ts也无法及时得到更新。
(3) cpu上有优先级为99的FIFO任务一直占有cpu。实时FIFO的调度策略是严格按优先级调度的,而同等优先级的任务遵守先来先运行、一直到无法再运行为止的原则。因而,如果cpu上原来已经有一个调度策略为FIFO且优先级为99的任务一直占有CPU时watchdog即使被唤醒后也是无法得到运行的。
这里列举了典型的三种watchdog无法及时获得cpu运行的情况,也就是发生soft lockup的典型情况,也就是说明此cpu有比FIFO99优先级相等或更高优先级的任务/模块在一直占用cpu。
三、softlockup的处理
一旦发生softlockup内核一般会有类似如下的打印:
# BUG: soft lockup - CPU#%d stuck for 22us! [loop:1023]
即softlockup的处理主要是打印告警信息以通知相关人员有相关情况发生。整个处理流程有如下几个部分:
(1) 通过soft_watchdog_warn判断是否已经发生过告警信息打印,则不再继续;
(2) 打印告警信息和current现场等等告警信息;
(3) 如果softlockup_all_cpu_backtrace变量使能,则打印其他各个cpu的堆栈回溯信息,这个变量可通过/proc/sys/kernel/softlockup_all_cpu_backtrace接口来enable/disable;
(4) 如果使能softlockup_panic变量,则进入panic。变量softlockup_panic可通过/proc/sys/kernel/softlockup_panic来修改。