电源管理入门:Watchdog看门狗

电子说

1.2w人已加入

描述

看门狗,又叫 watchdog timer,是一个定时器电路, 一般有一个输入,叫喂狗,一个输出到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 watchdog

kernel 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 lockup

Lockup分为两种: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的话,可以设置

 

echo 1 > /proc/sys/kernel/softlockup_panic

 

可以同时设置 watchdog_thresh参数来定义发现softlockup以后系统panic的时间,默认是10s, 也就是说20s后系统panic。最大能设到60s,也就是说,120s后启动系统panic。

一般来说,在production system上,不建议使用softlockup_panic选项(有可能误伤)。可以在调试系统上使用。

2.1.1 hard lockup

Hard lockup比soft lockup更加严重,CPU不仅无法执行其它进程,而且不再响应中断。检测hard lockup的原理利用了PMU的NMI perf event,因为NMI中断是不可屏蔽的,在CPU不再响应中断的情况下仍然可以得到执行,它再去检查时钟中断的计数器hrtimer_interrupts是否在保持递增,如果停滞就意味着时钟中断未得到响应,也就是发生了hard lockup

Linux 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_panic

2.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 (Index=71h, Default=00h) 控制寄存器,主要是设置中断,这里不涉及 

WDTCONF: Watch Dog Timer Configuration Register (Index=72h, Default=001s0000b) 配置寄存器 Bit6 or Bit4设置为1即可开启看门狗功能,这里使用Bit6脉冲信号

看门狗WDTVALLSB: Watch Dog Timer Time-out Value (LSB) Register (Index=73h, Default=38h) 低位数据寄存器 WDTVALMSB: Watch Dog Timer Time-out Value (MSB) Register (Index=74h, Default=00h) 高位数据寄存器

3.2 喂狗操作

samples/watchdog/watchdog-simple.c中有一个简单的例子:

 

int main(void)
{
int fd = open("/dev/watchdog", O_WRONLY);
int ret = 0;
if (fd == -1) {
perror("watchdog");
exit(EXIT_FAILURE);
}
while (1) {
ret = write(fd, "�", 1);
if (ret != 1) {
ret = -1;
break;
}
sleep(10);
}
close(fd);
return ret;
}

 

3.3 watchdog硬件驱动编写

例如:drivers/watchdog/imx_sc_wdt.c中 imx_sc_wdt_probe中会调用devm_watchdog_register_device

 

#define DEFAULT_TIMEOUT 60
#define MAX_TIMEOUT 128

static int imx_sc_wdt_probe(struct platform_device *pdev)
{
struct watchdog_device *wdog;
struct device *dev = &pdev->dev;
int ret;

wdog = &imx_sc_wdd->wdd;
wdog->info = &imx_sc_wdt_info;
wdog->ops = &imx_sc_wdt_ops;
wdog->min_timeout = 1;
wdog->max_timeout = MAX_TIMEOUT;
wdog->parent = dev;
wdog->timeout = DEFAULT_TIMEOUT;

ret = imx_sc_wdt_set_timeout(wdog, wdog->timeout);
if (ret)
return ret;

return devm_watchdog_register_device(dev, wdog);

 

imx_sc_wdt_ops的定义为:

 

static const struct watchdog_ops imx_sc_wdt_ops = {
.owner = THIS_MODULE,
.start = imx_sc_wdt_start,
.stop  = imx_sc_wdt_stop,
.ping  = imx_sc_wdt_ping,
.set_timeout = imx_sc_wdt_set_timeout,
.set_pretimeout = imx_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);
return res.a0 ? -EACCES : 0;
}

 

后记:

对于重要的驱动操作的寄存器,在ARM中需要放入BL31中,也就是说寄存器地址是在安全世界的,不能让内核直接按地址操作,只暴露了通用的协议接口,就是一个约束,只能这么用,其他用法不行。暴露API而不是所有寄存器是安全世界和非安全世界的一个重要区别。“啥都懂一点,啥都不精通,

  审核编辑:汤梓红
 
打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分