watchdog
文章目录
- watchdog
- lockup检测的初始化
- 高精度定时器的初始化
- 高精度定时器任务
- 看门狗线程的初始化
- 看门狗线程的任务
- sysctl控制参数
总结
watchdog
的代码,大概是几个关键变量和两个关键任务:
- 定时器任务更新定时器中断数
- 定期器任务唤醒每CPU线程,每CPU线程进行软中断数更新以及时间戳更新
- 定时器任务中根据时间戳判断是否进入软锁,软锁时打印相关信息
所以陷入soft lockuop
的情况直接表现就是每CPU的watchdog_touch_ts
时间戳长时间没有被更新,更新这个时间戳的是在每CPU线程watchdog
里面执行的,每CPU线程watchdog
是由hrtimer
定时器的工作任务watchdog_timer_fn
唤醒,也就是说出现soft lockuop
应该是由上面几个阶段条件触发导致的:
- 每CPU线程
watchdog
得不到唤醒 - 工作任务
watchdog_timer_fn
得不到执行
定时器中断任务优先级高于内核线程优先级。如果是NMI
中断,NMI
中断优先级高于定时器中断优先级。
关于第一个条件,每CPU线程watchdog
是SCHED_FIFO
调度策略,优先级为最高的99
,利用chrt
命令或者看内核代码一样可以看到相关的信息:
如果当前这个线程得不到调度,有可能是在一些不可抢占的上下文中占用了CPU,比如:
- 某些内核线程死循环?
- 软中断占用CPU时间过长?
- 过量的耗时timer任务?
- 关中断调度的自旋锁?
当前内核配置抢占是没有开启的:
# CONFIG_PREEMPT is not set
发现看了代码出现问题时也不一定能很快排查到,实际还是需要多复现结合实际的情况去排查,待问题解决的话看看能不能写一篇解决过程的吧。
lockup检测的初始化
初始化流程:
start_kernel()
-> rest_init()
-> kernel_init()
-> kernel_init_freeable()
-> lockup_detector_init()
lockup_detector_init()
是看门狗初始化的入口,设置高精度定时器的定时时间后,在所有的CPU上使能看门狗。
转换watchdog_thresh
时间为ns
级别的:
设置soft lockup
的超时时间是hard lockup
的两倍,注释解释说是soft lockup
出现的情况比较极端:
默认的watchdog_thresh
时间是10
:
高精度定时器的初始化
在初始化过程中,为每一个CPU上都创建一个线程:
这个结构体里面,数据没有CPU私有,函数共享,私有的数据指向的是文件开头静态定义的每CPU变量softlockup_watchdog
:
在smpboot_thread_fn()
函数中,会先调用setup
回调,即watchdog_enable()
函数,初始化一个高精度定时器,设置工作任务为watchdog_timer_fn()
函数。
watchdog_nmi_enable()
为弱定义,本代码配置中没有配置CONFIG_HARDLOCKUP_DETECTOR
,所有没有走/kernel/watchdog_hld.c
里面的函数。
修改当前线程的优先级和调度策略:(这个进程应该是通过smpboot_register_percpu_thread_cpumask()
注册的每一个CPU的进程)
__touch_watchdog()
记录时间戳到本地CPU的变量上:
高精度定时器任务
函数第一部分获取本CPU看门狗线程上次更新的时间戳,更新本地的hrtimer_interrupts
数目,唤醒本CPU的看门狗线程去更新时间戳和高精度定时器的中断数。
调用watchdog_interrupt_count()
更新本地CPU的hrtimer_interrupts
中断数:
第二部分检查是否陷入了soft lockup
的过程,如果陷入了soft lockup
,判断是否以及被警告过了,如果被警告过了,如果当前进程与保存的进程信息不一致,设置为未被警告,更新看门狗时间。
判断是否陷入soft lockup
的函数是根据当前时间戳和上一次看门狗线程更新的时间戳进行比较来判断的:
第三部分是打印soft lockup
相关的信息,比如当前CPU、进程信息、模块信息、寄存器信息等,如果设置了panic
,打印panic
信息。
看门狗线程的初始化
在smpboot_thread_fn()
函数中,会调用thread_should_run()
去判断条件是否满足,满足时执行thread_fn
回调函数:
在这个看门狗中,当本地 CPU 记录的高精度定时器的值与看门狗线程写入的值不相等时视为条件满足,smpboot_thread_fn()
函数应该就会执行watchdog()
函数。
看门狗线程的任务
看门狗线程主要是更新高精度定时器的中断数到本地CPU,并更新本地CPU的记录的时间戳:
sysctl控制参数
在/proc/sys/kernel
下,有控制watchdog
相关的参数,比如watchdog_thresh
、softlockup_panic
等,在代码中也有表现。
几个sysctl
控制参数:
更新watchdog_thresh
周期:
更新CPU掩码,使能或禁止某些CPU上的看门狗线程:
proc_watchdog_common()
函数如下:
使能或者禁止看门狗: