Watchdog使用参考


REVISION HISTORY

Revision No.
Description
Date
1.0
  • Initial release
  • 04/21/2023

    1. 概述

    watchdog(看门狗,简称 "WDT")是一种具备超时复位系统功能的硬件或软件机制,核心由定时器与复位模块组成,具体工作原理如下:

    • 用户预先设定一个超时时间,在该时间周期内需通过 “喂狗” 操作重置 watchdog 的内部计数器
    • 若内部计数器数值达到设定的超时时间,且未及时 “喂狗”,watchdog 将触发系统复位,使系统重新启动

    本文中出现的 "WDT","看门狗","Watchdog" 表述,均指代上述超时复位系统的功能。本文介绍PCUPID 系列芯片的硬件看门狗机制。

    2. 关键字说明

    • 硬件watchdog:独立的看门狗硬件电路(包含定时和复位电路,SoC内置),软件定期向硬件寄存器写入特定值,超时后输出硬件复位信号,强制系统重启,不依赖操作系统运行,即使 CPU 死机仍能触发硬件复位

    • 软件watchdog:基于内核定时器的软件逻辑模拟,用户程序定期喂狗,超时后通过内核 API 触发系统重启或告警,无需硬件支持,实现灵活,可定制超时动作,CPU死机后不能触发超时动作

    • watchdog timeout:看门狗的超时时长,如果超过这个时长不喂狗,硬件看门狗会输出硬件复位信号,软件看门狗会触发超时动作

    • watchdog pretimeout:看门狗预超时是看门狗机制中的一种提前预警机制,指在看门狗真正超时前的一个提前告警阶段。当预超时触发时,系统会收到一个预警信号(如中断),此时仍有一段缓冲时间(预超时到实际超时的间隔)供系统进行故障排查(如记录日志、保存关键数据)或尝试自我恢复(如杀死异常进程)

    3. 功能描述

    linux 内核支持基于高精度定时器的软件看门狗(softdog)和基于硬件的硬件看门狗,提供了/dev/watchdog* 设备文件与用户空间程序进行交互。应用程序可以通过/dev/watchdog* 设备进行周期性喂狗。

    Linux下watchdog框架

    软件看门狗:linux自带功能,按需打开,默认关闭

    硬件看门狗:soc内置,sigmastar提供驱动,linux内核提供devfs的watchdog接口,用户使用ioctl操作watchdog设备

    4. 硬件连接介绍

    PCUPID 系列芯片的硬件看门狗内置在soc里,没有引脚对外开放,无需外围硬件电路连接,由驱动程序写soc的寄存器实现对看门狗硬件电路的控制

    5. Uboot用法介绍

    5.1 dts配置

    watchdog 驱动相关的 DTS 配置如下:

    watchdog: watchdog {
        compatible = "sstar,wdt";
        reg = <0x1F006000 0x40>;
        status = "okay";
    };
    

    watchdog DTS配置说明:

    属性 描述 设定值 备注
    compatible 匹配驱动进行驱动注册 "sstar,wdt" 禁止修改
    reg watchdog寄存器信息 硬件设计决定 禁止修改
    status 选择是否使能watchdog驱动 "ok" or "disable" 按需修改,默认打开

    5.2 uboot config配置

    通过make menuconfig打开或关闭watchdog相关的config配置项:

    (1) 驱动配置项 CONFIG_SSTAR_WDT:

    [*] SigmaStar drivers  --->
    [*]   Sigmastar watchdog
    

    (2) 启动看门狗配置项 CONFIG_WATCHDOG_AUTOSTART 和超时时间配置项 CONFIG_WATCHDOG_TIMEOUT_MSECS:

    [*] Device Drivers  --->
    [*]   Watchdog Timer Support --->
    [*]     Automatically start watchdog timer
    [*]     Watchdog timeout in msec
    

    注意:如果CONFIG_WATCHDOG_AUTOSTART = Y 则uboot启动时会默认启动看门狗,并且超时时间由CONFIG_WATCHDOG_TIMEOUT_MSECS设定。CONFIG_WATCHDOG_TIMEOUT_MSECS默认值为2000,单位ms,也就是2s

    (3) 喂狗配置项 CONFIG_WATCHDOG:

    [*] Device Drivers  --->
    [*]   Watchdog Timer Support --->
    [*]       Enable U-Boot watchdog reset
    

    注意:如果开启这个宏,uboot会每隔一秒喂一次狗。如果开启 CONFIG_WATCHDOG_AUTOSTART 但是不开启 CONFIG_WATCHDOG,系统在WATCHDOG_TIMEOUT_MSECS ms后将重启

    (4) 打开wdt命令配置项 CONFIG_WATCHDOG:

    [*] Command line interface  --->
    [*]   Device access commands --->
    [*]       wdt
    

    注意: 只有开启这个宏,uboot下才可以执行wdt命令

    5.3 wdt命令参数说明

    1. 查看可用的watchdog:

      wdt list - list watchdog devices
      
      示例:
      SigmaStar # wdt list
      watchdog (sstar_wdt)
      SigmaStar #
      

      注意:示例里回显的"watchdog"才是设备name,sstar_wdt不是设备name

    2. 获取/设置当前watchdog设备:

      wdt dev [<name>] - get/set current watchdog device
      
      参数名称 描述
      name 不带参数时为获取当前操作的 watchdog 设备名称
    3. 启动watchdog:

      wdt start <timeout ms> [flags] - start watchdog timer
      
      参数名称 描述
      timeout ms 指定 watchdog 的超时时间,单位为毫秒
      flags 传递给 watchdog 驱动的 flags 信息,当前驱动未使用该参数
    4. 停止watchdog:

      wdt stop - stop watchdog timer
      
    5. 复位 watchdog 计时:

      wdt reset - reset watchdog timer
      
    6. 立刻触发 watchdog 超时:

      wdt expire [flags] - expire watchdog timer immediately
      
      参数名称 描述
      flag -f 强制触发超时; -d 延迟触发超时; -s 触发超时过程中不输出日志信息

      注意: wdt expire命令的作用为立刻触发watchdog超时,U-Boot原生的实现为将超时设置为1ms,当前硬件不支持此命令。

    5.4 wdt使用示例

    先关闭“自启动看门狗”和“自动喂狗”配置项,然后执行下列操作

    wdt list →  wdt list                // 查看有哪些watchdog设备
    
    wdt dev [<name>] → wdt dev watchdog // 使用名为"watchdog"的看门狗
    
    wdt start <timeout ms> [flags] → wdt start 10000 // 设置watchdog超时时间为10s
    
    wdt reset → wdt reset //在watchdog未超时前,复位watchdog计时
    
    wdt stop → wdt stop   // 在watchdog未超时前,停止watchdog
    

    注意:关闭自启动看门狗功能,即关闭CONFIG_WATCHDOG_AUTOSTART配置项。关闭自动喂狗功能,即关闭CONFIG_WATCHDOG配置项,关闭方法见上文uboot config配置小节。如果不关闭这两个配置项,uboot系统每隔一段时间会自动ping一次watchdog,你无法用此demo测试wdt start功能。

    6. Kernel用法介绍

    6.1 dts配置

    Watchdog 驱动相关的 DTS 配置如下:

        WDT: watchdog {
            compatible = "sstar,wdt";
            reg = <0x0 0x1F006000 0x0 0x40>;
            interrupts = <GIC_SPI INT_FIQ_WDT IRQ_TYPE_LEVEL_HIGH>;
            max-length = <40>;
            status = "okay";
        };
    

    watchdog DTS配置说明:

    属性 描述 设定值 备注
    compatible 匹配驱动进行驱动注册 "sstar,wdt" 禁止修改
    reg 设定寄存器bank地址 硬件设计决定 禁止修改
    interrupts 指定 Watchdog 使用的硬件中断号 硬件设计决定 禁止修改
    max-length watchdog计数宽带 硬件设计决定 禁止修改
    status 选择是否使能Watchdog驱动 "ok" or "disable" 按需修改,默认打开

    6.2 kernel config配置

    通过make menuconfig打开或关闭watchdog相关的config配置项:

        Device Drivers  --->
        [*] SStar SoC platform drivers  --->
        <*>   watchdog driver
    

    6.3 使用介绍

    6.3.1 devfs使用方法

    Linux提供了/dev/watchdog节点供用户使用,可以用ioctl操作,需要用户程序主动打开watchdog功能。

    • 头文件:include/uapi/linux/watchdog.h

    • 数据结构:struct watchdog_info结构体描述了看门狗设备的信息

    • ioctl控制命令:

      1. WDIOC_GETSUPPORT:获取看门狗支持哪些功能

      2. WDIOC_SETOPTIONS:用于开启或关闭看门狗

      3. WDIOC_KEEPALIVE:喂狗操作

      4. WDIOC_SETTIMEOUT:设置看门狗超时时间

      5. WDIOC_GETPRETIMEOUT:获取看门狗超时时间

    watchdog.h头文件内容如下

    #ifndef _UAPI_LINUX_WATCHDOG_H
    #define _UAPI_LINUX_WATCHDOG_H
    
    #include <linux/ioctl.h>
    #include <linux/types.h>
    
    #define WATCHDOG_IOCTL_BASE 'W'
    
    struct watchdog_info {
        __u32 options;      /* Options the card/driver supports */
        __u32 firmware_version; /* Firmware version of the card */
        __u8  identity[32]; /* Identity of the board */
    };
    
    #define WDIOC_GETSUPPORT    _IOR(WATCHDOG_IOCTL_BASE, 0, struct watchdog_info)
    #define WDIOC_GETSTATUS     _IOR(WATCHDOG_IOCTL_BASE, 1, int)
    #define WDIOC_GETBOOTSTATUS _IOR(WATCHDOG_IOCTL_BASE, 2, int)
    #define WDIOC_GETTEMP       _IOR(WATCHDOG_IOCTL_BASE, 3, int)
    #define WDIOC_SETOPTIONS    _IOR(WATCHDOG_IOCTL_BASE, 4, int)
    #define WDIOC_KEEPALIVE     _IOR(WATCHDOG_IOCTL_BASE, 5, int)
    #define WDIOC_SETTIMEOUT    _IOWR(WATCHDOG_IOCTL_BASE, 6, int)
    #define WDIOC_GETTIMEOUT    _IOR(WATCHDOG_IOCTL_BASE, 7, int)
    #define WDIOC_SETPRETIMEOUT _IOWR(WATCHDOG_IOCTL_BASE, 8, int)
    #define WDIOC_GETPRETIMEOUT _IOR(WATCHDOG_IOCTL_BASE, 9, int)
    #define WDIOC_GETTIMELEFT   _IOR(WATCHDOG_IOCTL_BASE, 10, int)
    
    #define WDIOF_UNKNOWN       -1  /* Unknown flag error */
    #define WDIOS_UNKNOWN       -1  /* Unknown status error */
    
    #define WDIOF_OVERHEAT      0x0001  /* Reset due to CPU overheat */
    #define WDIOF_FANFAULT      0x0002  /* Fan failed */
    #define WDIOF_EXTERN1       0x0004  /* External relay 1 */
    #define WDIOF_EXTERN2       0x0008  /* External relay 2 */
    #define WDIOF_POWERUNDER    0x0010  /* Power bad/power fault */
    #define WDIOF_CARDRESET     0x0020  /* Card previously reset the CPU */
    #define WDIOF_POWEROVER     0x0040  /* Power over voltage */
    #define WDIOF_SETTIMEOUT    0x0080  /* Set timeout (in seconds) */
    #define WDIOF_MAGICCLOSE    0x0100  /* Supports magic close char */
    #define WDIOF_PRETIMEOUT    0x0200  /* Pretimeout (in seconds), get/set */
    #define WDIOF_ALARMONLY     0x0400  /* Watchdog triggers a management or
                        other external alarm not a reboot */
    #define WDIOF_KEEPALIVEPING 0x8000  /* Keep alive ping reply */
    
    #define WDIOS_DISABLECARD   0x0001  /* Turn off the watchdog timer */
    #define WDIOS_ENABLECARD    0x0002  /* Turn on the watchdog timer */
    #define WDIOS_TEMPPANIC     0x0004  /* Kernel panic on temperature trip */
    
    #endif /* _UAPI_LINUX_WATCHDOG_H */
    

    6.4 示例代码

    6.4.1 启动看门狗

    打开/dev/watchdog设备,watchdog将被启动。

    参考代码如下:

    int wdt_fd = -1;
    wdt_fd = open("/dev/watchdog", O_WRONLY);
    if (wdt_fd == -1)
    {
        // fail to open watchdog device
    }
    

    6.4.2 关闭看门狗

    参考代码如下:

    int option = WDIOS_DISABLECARD;
    ioctl(wdt_fd, WDIOC_SETOPTIONS, &option);
    if (wdt_fd != -1)
    {
        close(wdt_fd);
        wdt_fd = -1;
    }
    

    6.4.3 设置超时时间

    通过标准的IOCTL命令WDIOC_SETTIMEOUT以秒为单位来设定超时,超时时间建议大于5s,参考代码如下:

    #define WATCHDOG_IOCTL_BASE    'W'
    #define WDIOC_SETTIMEOUT       _IOWR(WATCHDOG_IOCTL_BASE, 6, int)
    int timeout = 20;
    ioctl(wdt_fd, WDIOC_SETTIMEOUT, &timeout);
    

    6.4.4 喂狗

    通过标准的IOCTL命令"WDIOC_KEEPALIVE"来喂狗,喂狗的间隔应该比超时时间小,参考代码如下:

    #define WATCHDOG_IOCTL_BASE    'W'
    #define WDIOC_KEEPALIVE        _IOR(WATCHDOG_IOCTL_BASE, 5, int)
    ioctl(wdt_fd, WDIOC_KEEPALIVE, 0);
    

    6.4.5 demo

    #include <stdio.h>
    #include <iwatchdog.h>
    #include <unistd.h>
    #include <fcntl.h>
    #include <string.h>
    #include <errno.h>
    #include <sys/ioctl.h>
    #include <linux/watchdog.h>
    
    int main() {
        int fd = open("/dev/watchdog", O_WRONLY);
        if (fd < 0) {
            perror("Failed to open /dev/watchdog");
            return -1;
        }
        if (ioctl(fd, WDIOC_SETOPTIONS, WDIOS_ENABLECARD) < 0) {
            perror("Failed to enable watchdog");
            close(fd);
            return -1;
        }
        while (1) {
            if (write(fd, "V", 1) != 1) {  // Reset watchdog timer
                perror("Failed to reset watchdog timer");
                break;
            }
            sleep(1);  // Sleep for a while before resetting again
        }
        close(fd);
        return 0;
    }
    

    6.4.6 测试用例

    kernel的硬件看门狗单元测试demo:<Kernel>drivers/sstar/watchdog/ut/wdt_ut.c

    Example:
      # wdt_ut start 10   /* 启动watchdog后,系统会不断去喂狗,所以10秒后系统不会重启 */
      # wdt_ut reset 10   /* 启动watchdog后,系统不会去喂狗,所以10秒后系统将重启 */