电源管理入门-14 Watchdog
看门狗,又叫watchdogtimer,是一个定时器电路, 一般有一个输入,叫喂狗,一个输出到MCU的RST端,MCU正常工作的时候,每隔一段时间输出一个信号到喂狗端,给 WDT清零,如果超过规定的时间不喂狗,(一般在程序跑飞时),WDT 定时超过,就会给出一个复位信号到MCU,是MCU复位. 防止MCU死机. 看门狗的作用就是防止程序发生死循环或者说程序跑飞。watchdog的操作就是reset所以跟电源是有联系的。1. 软硬件watchdog的区别通常情况下watchdog需要硬件支持但是如果确实没有相应的硬件还想使用watchdog功能则可以使用liunx模拟的watchdog即软件watchdog。硬件watchdog必须有硬件电路支持, 设备节点**/dev/watchdog**对应着真实的物理设备 不同类型的硬件watchdog设备由相应的硬件驱动管理。软件watchdog由一内核模块softdog.ko 通过定时器机制实现/dev/watchdog并不对应着真实的物理设备只是为应用提供了一个与操作硬件watchdog相同的接口。硬件watchdog比软件watchdog有更好的可靠性。软件watchdog基于内核的定时器实现当内核或中断出现异常时软件watchdog将会失效。而硬件watchdog由自身的硬件电路控制, 独立于内核。无论当前系统状态如何硬件watchdog在设定的时间间隔内没有被执行写操作仍会重新启动系统。一些硬件watchdog卡如WDT501P 以及一些Berkshire卡还可以监测系统温度提供了 /dev/temperature接口。对于应用程序而言, 操作软件、硬件watchdog的方式基本相同打开设备/dev/watchdog, 在重启时间间隔内对/dev/watchdog执行写操作。即软件、硬件watchdog对应用程序而言基本是透明的。在任一时刻 只能有一个watchdog驱动模块被加载管理/dev/watchdog 设备节点。如果系统没有硬件watchdog电路则可以加载软件watchdog驱动softdog.ko。2. 软件看门狗2.1 kernel watchdogkernel watchdog是用来检测Lockup 的。所谓lockup是指某段内核代码占着CPU不放。Lockup严重的情况下会导致整个系统失去响应。Lockup有几个特点首先只有内核代码才能引起lockup因为用户代码是可以被抢占的不可能形成lockup只有一种情况例外就是SCHED_FIFO优先级为99的实时进程即使在用户态也可能使[watchdog/x]内核线程抢不到CPU而形成soft lockup其次内核代码必须处于禁止内核抢占的状态(preemption disabled)因为Linux是可抢占式的内核只在某些特定的代码区才禁止抢占例如spinlock在这些代码区才有可能形成lockup。2.1.1 soft lockupLockup分为两种soft lockup 和 hard lockup它们的区别是hard lockup 发生在CPU屏蔽中断的情况下。而soft lockup则是单个CPU被一直占用的情况中断仍然可以响应。NMI即非可屏蔽中断。即使在内核代码中设置了屏蔽所有中断的时候NMI也是不可以被屏蔽的。可屏蔽中断包含时钟中断外设中断比如键盘中断I/O设备中断等等当我们处理中断处理程序的时候在中断处理程序top half时候在不允许嵌套的情况下需要关闭中断。但NMI就不一样了即便在关闭中断的情况下他也能被响应。触发NMI的条件一般都是ECC error之类的硬件Error。但NMI也给我们提供了一种机制在系统中断被误关闭的情况下依然能通过中断处理程序来执行一些紧急操作比如kernel panic。检测soft lockup的原理是给每个CPU分配一个定时执行的内核线程[watchdog/x]如果该线程在设定的期限内没有得到执行的话就意味着发生了soft lockup[watchdog/x]是SCHED_FIFO实时进程优先级为最高的99拥有优先运行的特权。系统会有一个高精度的计时器hrtimer一般来源于APIC该计时器能定期产生时钟中断该中断对应的中断处理例程是kernel/watchdog.c: watchdog_timer_fn()在该例程中要递增计数器hrtimer_interrupts这个计数器同时为hard lockup detector用于判断CPU是否响应中断还要唤醒[watchdog/x]内核线程该线程的任务是更新一个时间戳soft lock detector检查时间戳如果超过soft lockup threshold一直未更新说明[watchdog/x]未得到运行机会意味着CPU被霸占也就是发生了soft lockup。linux kernel会自动检测softlockup在发生softlockup情况下系统默认会打印相关warning信息。如果需要出发panic的话可以设置echo1/proc/sys/kernel/softlockup_panic可以同时设置 watchdog_thresh参数来定义发现softlockup以后系统panic的时间默认是10s, 也就是说20s后系统panic。最大能设到60s也就是说120s后启动系统panic。一般来说在production system上不建议使用softlockup_panic选项有可能误伤。可以在调试系统上使用。2.1.1 hard lockupHard lockup比soft lockup更加严重CPU不仅无法执行其它进程而且不再响应中断。检测hard lockup的原理利用了PMU的NMI perf event因为NMI中断是不可屏蔽的在CPU不再响应中断的情况下仍然可以得到执行它再去检查时钟中断的计数器hrtimer_interrupts是否在保持递增如果停滞就意味着时钟中断未得到响应也就是发生了hard lockupLinux kernel设计了一个检测lockup的机制称为NMI Watchdog是利用NMI中断实现的用NMI是因为lockup有可能发生在中断被屏蔽的状态下这时唯一能把CPU抢下来的方法就是通过NMI因为NMI中断是不可屏蔽的。NMI watchdog会利用到之前讲到的hrtimer。它的触发条件是基于PMU的NMI perf event当PMU的计数器溢出时会触发NMI中断对应的中断处理例程是 kernel/watchdog.c: watchdog_overflow_callback()hard lockup detector就在其中它会检查上述hrtimer的中断次数(hrtimer_interrupts)是否在保持递增如果停滞则表明hrtimer中断未得到响应也就是发生了hard lockup。这里面被watch的对象是hrtimer而watchdog则是由PMU设备发起的NMI中断处理程序 watchdog_overflow_callback()hardlockup的检测需要启动NMI watchdog。可以通过设置内核参数实现:echo 1 /proc/sys/kernel/nmi_watchdog 1 在发生hardlockup情况下如果我们需要系统panic可以设置默认已设定echo 1 /proc/sys/kernel/hardlockup_panic2.2 用户态watchdog用户程序有可能占着临界资源无法释放系统太忙疲于响应各种中断导致无法执行调度程序。这都可能导致系统无法正常使用。在这种情况下时钟中断和NMI中断仍然能够被响应所以内核lockup检测机制无法检查出来。但由于系统已经无法正常工作我们需要一种机制一种用户态的watchdog来检测这种系统挂起的状态并作出相应的动作。用户态watchdog自然检测的对象是用户态的程序是否能被调度。这里面基于硬件支持程度不同我们分为hardware watchdog和software watchdog。后者简称softdog。2.2.1 softdog使用softdog很简单只需要安装 watchdog rpm启动softdog服务-systemctl start softdog.service默认情况下watchdog程序会通过softdog.ko创建一个叫做/dev/watchdog1的设备timer 设备并且定期往它写东西用于更新时间戳。位于内核的softdog会模拟timer设备通过时钟中断的方式模拟并执行相应的中断处理例程该例程的目的是检查timer 设备即/dev/watchdog1是否timeout。如果timeout则执行相应动作默认为panic。2.2.1 hardware watchdog通过BMC实现、通过iTCO实现3. 硬件看门狗3.1 硬件寄存器介绍看门狗主要由寄存器、计数器和狗叫模块构成通过寄存器对看门狗进行基本设置计数器计算狗叫时间狗叫模块决定看门狗超时后发出的中断或复位方式。QOTOM Q300P自带硬件看门狗由SuperIO芯片提供这里简单实现一下看门狗的复位功能只需要对看门狗的配置寄存器组和数据寄存器组进行操作。WDTCTRL:Watch Dog Timer Control Register (Index71h, Default00h) 控制寄存器主要是设置中断这里不涉及WDTCONF:Watch Dog Timer Configuration Register (Index72h, Default001s0000b) 配置寄存器 Bit6 or Bit4设置为1即可开启看门狗功能这里使用Bit6脉冲信号WDTVALLSB:Watch Dog Timer Time-out Value (LSB) Register (Index73h, Default38h) 低位数据寄存器WDTVALMSB:Watch Dog Timer Time-out Value (MSB) Register (Index74h, Default00h) 高位数据寄存器3.2 喂狗操作samples/watchdog/watchdog-simple.c中有一个简单的例子int main(void){int fdopen(/dev/watchdog, O_WRONLY);int ret0;if(fd-1){perror(watchdog);exit(EXIT_FAILURE);}while(1){retwrite(fd,\0,1);if(ret!1){ret-1;break;}sleep(10);}close(fd);returnret;}3.3 watchdog硬件驱动编写例如drivers/watchdog/imx_sc_wdt.c中 imx_sc_wdt_probe中会调用devm_watchdog_register_device#define DEFAULT_TIMEOUT 60#define MAX_TIMEOUT 128static int imx_sc_wdt_probe(struct platform_device *pdev){struct watchdog_device *wdog;struct device *devpdev-dev;int ret;wdogimx_sc_wdd-wdd;wdog-infoimx_sc_wdt_info;wdog-opsimx_sc_wdt_ops;wdog-min_timeout1;wdog-max_timeoutMAX_TIMEOUT;wdog-parentdev;wdog-timeoutDEFAULT_TIMEOUT;retimx_sc_wdt_set_timeout(wdog, wdog-timeout);if(ret)returnret;returndevm_watchdog_register_device(dev, wdog);imx_sc_wdt_ops的定义为static const struct watchdog_ops imx_sc_wdt_ops{.ownerTHIS_MODULE, .startimx_sc_wdt_start, .stopimx_sc_wdt_stop, .pingimx_sc_wdt_ping, .set_timeoutimx_sc_wdt_set_timeout, .set_pretimeoutimx_sc_wdt_set_pretimeout,};例如imx_sc_wdt_start的实现使用了smc下发到BL31中处理为了突出安全性。static int imx_sc_wdt_start(struct watchdog_device *wdog){struct arm_smccc_res res;arm_smccc_smc(IMX_SIP_TIMER, IMX_SIP_TIMER_START_WDOG,0,0,0,0,0,0,res);if(res.a0)return-EACCES;arm_smccc_smc(IMX_SIP_TIMER, IMX_SIP_TIMER_SET_WDOG_ACT, SC_TIMER_WDOG_ACTION_PARTITION,0,0,0,0,0,res);returnres.a0 ?-EACCES:0;}参考https://qkxu.github.io/2019/04/15/linux%E4%B8%8B%E7%9A%84watchdog.htmlhttps://minipc.netlify.app/posts/fe902d45/https://blog.csdn.net/ericstarmars/article/details/81750919https://blog.csdn.net/weixin_44410537/article/details/86708540后记对于重要的驱动操作的寄存器在ARM中需要放入BL31中也就是说寄存器地址是在安全世界的不能让内核直接按地址操作只暴露了通用的协议接口就是一个约束只能这么用其他用法不行。暴露API而不是所有寄存器是安全世界和非安全世界的一个重要区别。“啥都懂一点啥都不精通干啥都能干干啥啥不是专业入门劝退堪称程序员杂家”。欢迎各位自己有博客公众号的留言申请转载多谢后续会继续更新纯干货分析欢迎分享给朋友欢迎点赞、收藏、在看、划线和评论交流公众号“那路谈OS与SoC嵌入式软件”欢迎关注个人文章汇总https://thatway1989.github.io