LXR | KVM | PM | Time | Interrupt | Systems Performance | Bootup Optimization

Linux watchdog子系统概述

关键词:watchdog、pretimeout、governor、softdog等等。

watchdog子系统是内核为保证系统正常运行,将系统从死循环或者死锁等状态中退出并重启的一种机制。

内核中支持基于hrtimer的softdog和基于硬件的watchdog。硬件看门狗从dts读取配置,创建同样的watchdog设备。

注册到watchdog子系统的设备,都会闯将/dev/watchdogX设备,以及watchdog类设备节点。

用空空间watchdog守护进程,打开/dev/watchdogX设备,并周期性喂狗。

1 watchdog配置

对watchdog的配置包括core配置、governor配置、驱动配置:

Device Drivers
  Watchdog Timer Support  
    WatchDog Timer Driver Core--watchdog子系统core配置。     Disable watchdog shutdown on close--选择Y时,在打开/dev/watchdog进程关闭时不会停止内核watchdog。否则会在关闭/dev/watchdog时停止watchdog。     Update boot
-enabled watchdog until userspace takes over--选择Y时,内核会在watchdog使能后,用户空间喂狗之前负责喂狗。     Timeout value for opening watchdog device--watchdog框架负责的喂狗时间,0表示无穷。等同于模块参数watchdog.open_timeout。     Read different watchdog information through sysfs     *** Watchdog Pretimeout Governors ***--watchdog子系统governor配置。     Enable watchdog pretimeout governors      Noop watchdog pretimeout governor--超时后仅输出一条消息到内核日志。      Panic watchdog pretimeout governor--超时后使内核进入panic()。      Default Watchdog Pretimeout Governor (panic)--默认pretimeout governor。     *** Watchdog Device Drivers ***--watchdog子系统驱动配置。     Software watchdog--基于软件实现的watchdog。      Software watchdog pretimeout governor support--软watchdog支持governor。     STM32 Independent WatchDoG (IWDG) support--STM32的硬件watchdog。

涉及到的文件主要有:

drivers/watchdog/
├── pretimeout_noop.c--pretimeout的noop governor。 ├── pretimeout_panic.c--pretimerout的panic governor。 ├── softdog.c--内核中软件模拟的soft watchdog。 ├── stm32_iwdg.c--STM32 watchdog驱动。 ├── watchdog_core.c--watchdog子系统初始化、设备注册/注销接口。 ├── watchdog_dev.c--watchdog类设备sysfs节点、以及core关于cdev操作函数集实现等。 ├── watchdog_pretimeout.c--pretimeout注册/注销、governor设置、超时后通知等。

2 watchdog DTS

以stm32 watchdog为例,dts很简单:

        iwdg2: watchdog@5a002000 {
            compatible = "st,stm32mp1-iwdg";
            reg = <0x5a002000 0x400>;
            clocks = <&rcc IWDG2>, <&scmi0_clk CK_SCMI0_LSI>;
            clock-names = "pclk", "lsi";
            status = "disabled";
        };

&iwdg2 {
  timeout-sec = <32>;--配置watchdog的timerout。
  status = "okay";
};

3 watchdog core

3.1 watchdog数据结构

struct watchdog_device表示一个watchdog设备,作为参数注册到watchdog子系统,以及对watchdog等的操作参数。

struct watchdog_device {
    int id;--watchdog注册时分配的ID序号。
    struct device *parent;
    const struct attribute_group **groups;--创建设备时sysfs属性。
    const struct watchdog_info *info;
    const struct watchdog_ops *ops;--watchdog操作函数集。
    const struct watchdog_governor *gov;--pretimeout使用的governor。
    unsigned int bootstatus;
    unsigned int timeout;
    unsigned int pretimeout;
    unsigned int min_timeout;
    unsigned int max_timeout;
    unsigned int min_hw_heartbeat_ms;
    unsigned int max_hw_heartbeat_ms;
    struct notifier_block reboot_nb;--在reboot时,遍历reboot_notifier_list调用。
    struct notifier_block restart_nb;--在restart是,遍历restart_handler_list调用。
    void *driver_data;
    struct watchdog_core_data *wd_data;
    unsigned long status;
/* Bit numbers for status flags */
#define WDOG_ACTIVE        0    /* Is the watchdog running/active */
#define WDOG_NO_WAY_OUT        1    /* Is 'nowayout' feature set ? */
#define WDOG_STOP_ON_REBOOT    2    /* Should be stopped on reboot */
#define WDOG_HW_RUNNING        3    /* True if HW watchdog running */
#define WDOG_STOP_ON_UNREGISTER    4    /* Should be stopped on unregister */
    struct list_head deferred;
};

struct watchdog_ops是对watchdog操作总结:

struct watchdog_ops {
    struct module *owner;
    /* mandatory operations */
    int (*start)(struct watchdog_device *);
    int (*stop)(struct watchdog_device *);
    /* optional operations */
    int (*ping)(struct watchdog_device *);--喂狗。
    unsigned int (*status)(struct watchdog_device *);
    int (*set_timeout)(struct watchdog_device *, unsigned int);
    int (*set_pretimeout)(struct watchdog_device *, unsigned int);
    unsigned int (*get_timeleft)(struct watchdog_device *);
    int (*restart)(struct watchdog_device *, unsigned long, void *);
    long (*ioctl)(struct watchdog_device *, unsigned int, unsigned long);
};

3.2 watchdog子系统初始化

 watchdog_init()进行watchdog子系统初始化:

  • 创建一个kthread_worker。
  • 创建watchdog_class。
  • 分配watchdog设备号范围。
  • 处理watchdog延迟注册。
watchdog_init
  ->watchdog_dev_init
    ->kthread_create_worker--创建内核kthread worker(watchdog_kworker),对应的内核线程名称为watchdogd,线程处理函数为kthread_worker_fn()。
    ->sched_setscheduler--设置watchdog_worker的线程调度策略为SCHED_FIFO。
    ->class_register--创建watchdog类watchdog_class。
    ->alloc_chrdev_region--分配watchdog设备号范围。
  ->watchdog_deferred_registration--如果watchdog设备驱动早于watchdog_init则将设备加入到wtd_deferred_reg_list,此处集中进行列表上watchdog设备注册。

创建的内核线程watchdogd不跟任何硬件绑定,是统一处理watchdog kthread_work的工作。

watchdog类设备属性包括:

static struct class watchdog_class = {
    .name =        "watchdog",
    .owner =    THIS_MODULE,
    .dev_groups =    wdt_groups,
};

static struct attribute *wdt_attrs[] = {
    &dev_attr_state.attr,
    &dev_attr_identity.attr,
    &dev_attr_timeout.attr,
    &dev_attr_pretimeout.attr,
    &dev_attr_timeleft.attr,
    &dev_attr_bootstatus.attr,
    &dev_attr_status.attr,
    &dev_attr_nowayout.attr,
    &dev_attr_pretimeout_governor.attr,
    &dev_attr_pretimeout_available_governors.attr,
    NULL,
};

static const struct attribute_group wdt_group = {
    .attrs = wdt_attrs,
    .is_visible = wdt_is_visible,
};
__ATTRIBUTE_GROUPS(wdt);

3.3 watchdog API

watchdog core API主要是watchd的注册函数:

void watchdog_notify_pretimeout(struct watchdog_device *wdd);

void watchdog_set_restart_priority(struct watchdog_device *wdd, int priority);
extern int watchdog_init_timeout(struct watchdog_device *wdd,
                  unsigned int timeout_parm, struct device *dev);
extern int watchdog_register_device(struct watchdog_device *);
extern void watchdog_unregister_device(struct watchdog_device *);

int devm_watchdog_register_device(struct device *dev, struct watchdog_device *);

watchdog core的注册函数流程如下:

devm_watchdog_register_device--带资源管理的watchdog_register_device版本。
  ->watchdog_register_device
    ->__watchdog_register_device/watchdog_deferred_registration_add--如果此函数调用早于watchdog_init()则不会进行实质初始化,而是加入wtd_deferred_reg_list列表稍后处理。
      ->watchdog_check_min_max_timeout
      ->watchdog_dev_register
        ->watchdog_cdev_register
          ->kthread_init_work--创建一个kthread_work,work函数为watchdog_ping_work
          ->hrtimer_init--创建一个hrtimer,超时函数为watchdog_timer_expired
          ->misc_register--在注册第一个watchdog时,注册一个misc设备,操作函数集为watchdog_miscdev。misc设备号为10:130,对应的设备节点为/dev/watchdog
          ->cdev_init/cdev_device_add--初始化并创建watchdog设备,对应的设备节点为/dev/watchdogX
          ->watchdog_hw_running
        ->watchdog_register_pretimeout
      ->register_reboot_notifier--将watchdog的notifier_call放入reboot_notifier_list,如果系统重启则遍历列表上notifier。
        ->watchdog_reboot_notifier--根据重启类型,关闭watchdog。
      ->register_restart_handler
        ->watchdog_restart_notifier--在系统重启时,调用watchdog的restart函数。
  ->watchdog_register_pretimeout--将当前watchdog_device加入到pretimeout_list中。

watchdog hrtimer将kthread_work加入调度,进行喂狗。

watchdog_timer_expired
  ->kthread_queue_work--将当前的kthread_work放入到watchdog_kworker上排队。
    ->watchdog_ping_work--watchdog_kworker上执行此函数。
      ->__watchdog_ping--对watchdog进行喂狗。

misc设备/dev/watchdog和cdev设备/dev/watchdogX的文件操作函数都是watchdog_fops:

static const struct file_operations watchdog_fops = {
    .owner        = THIS_MODULE,
    .write        = watchdog_write,
    .unlocked_ioctl    = watchdog_ioctl,
    .open        = watchdog_open,
    .release    = watchdog_release,
};
static struct miscdevice watchdog_miscdev = {
    .minor        = WATCHDOG_MINOR,
    .name        = "watchdog",
    .fops        = &watchdog_fops,
};

watchdog_open()使能watchdog开始工作:

watchdog_open
  ->watchdog_start
    ->watchdog_hw_running--如果softdog硬件已经运行,则ping;否则start。
      ->watchdog_device->ops->ping--调用具体驱动的ping函数。
    ->watchdog_device->ops->start--调用具体驱动的start函数。

watchdog_ioctl()设置或者获取watchdog:

watchdog_ioctl
  ->WDIOC_GETSUPPORT--返回struct watchdog_info结构体。
  ->WDIOC_GETSTATUS--返回watchdog状态。
  ->WDIOC_GETBOOTSTATUS--返回bootstatus。
  ->WDIOC_SETOPTIONS--如果包含WDIOS_DISABLECARD和WDIOS_ENABLECARD,则调用watchdog_stop()和watchdog_start()。
  ->WDIOC_KEEPALIVE--调用watchdog_ping()喂狗。
  ->WDIOC_SETTIMEOUT--设置timeout。
  ->WDIOC_GETTIMEOUT--获取timeout值。
  ->WDIOC_SETPRETIMEOUT--设置pretimeout。
  ->WDIOC_GETPRETIMEOUT--获取pretimeout。
  ->WDIOC_GETTIMELEFT--获取到reboot的剩余时间。

watchdog_write()是对watchdog进行喂狗接口:

watchdog_write--写入任意字符都触发ping进行喂狗。V是magic字符。
  ->watchdog_ping
    ->__watchdog_ping
      ->优先使用ping,没有pin则使用start。
      ->watchdog_update_worker
        ->watchdog_need_worker
          ->hrtimer_start--重置hrtimer,超时后调度kthread_work。

关于pretimeout和governor的API有:

int watchdog_register_governor(struct watchdog_governor *gov);--governor的注册和注销接口。
void watchdog_unregister_governor(struct watchdog_governor *gov);

int watchdog_register_pretimeout(struct watchdog_device *wdd);--pretimeout注册和注销接口。
void watchdog_unregister_pretimeout(struct watchdog_device *wdd);
int watchdog_pretimeout_available_governors_get(char *buf);
int watchdog_pretimeout_governor_get(struct watchdog_device *wdd, char *buf);--设置/获取pretimeout对应的governor。
int watchdog_pretimeout_governor_set(struct watchdog_device *wdd,
                     const char *buf);

3.4 watchdog governor

watchdog governor包括两个:null和panic。

3.4.1 noop governor

noop governor仅输出一条alert消息:

watchdog_gov_noop_register
  ->watchdog_unregister_governor
    ->watchdog_gov_noop--noop governor结构体。
      ->pretimeout_noop--pretimeout回调函数,输出一个alert消息。。

3.4.2 panic governor

panic governor在pretimeout超时后执行panic()函数:

watchdog_gov_panic_register
  ->watchdog_register_governor
    ->watchdog_gov_panic
      ->pretimeout_panic--pretimeout回调函数,调用panic()函数。

4 softdog

 softdog_init()创建一个软件watchdog,通过hrtimer模拟硬件watchdog/pretimeout。喂狗则重置hrtimer,关闭则终止hrtimer。

softdog_init
  watchdog_init_timeout--超时时间默认为TIMER_MARGIN,或者通过soft_margin指定。
  watchdog_set_nowayout--默认为WATCHDOG_NOWAYOUT,或通过nowayout指定。
  watchdog_stop_on_reboot
  hrtimer_init--为softdog创建两个hrtimer,分别为softdog_ticktock(超时函数为softdog_fire)和softdog_preticktock(超时函数为softdog_pretimeout)。
    ->softdog_fire--如果softdog_ticktock超时,则调用此函数进行处理。根据情况可能是打印log;panic;restart。
      ->panic()
      ->emergency_restart()
    ->softdog_pretimeout
      ->watchdog_notify_pretimeout--调用governor的pretimeout函数。
  watchdog_register_device--注册softdog。

softdog的设备操作函数如下:

static struct watchdog_info softdog_info = {
    .identity = "Software Watchdog",
    .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
};

static const struct watchdog_ops softdog_ops = {
    .owner = THIS_MODULE,
    .start = softdog_ping,--重置softdog_ticktock/softdog_preticktock,等同于喂狗。
    .stop = softdog_stop,--终止softdog_ticktock/softdog_preticktock,等同于关闭softdog。
};

static struct watchdog_device softdog_dev = {
    .info = &softdog_info,
    .ops = &softdog_ops,
    .min_timeout = 1,
    .max_timeout = 65535,
    .timeout = TIMER_MARGIN,
};

5 watchdog驱动(STM32)

stm32_iwdg_driver
  ->stm32_iwdg_probe
    ->初始化struct watchdog_device,对应操作函数为stm32_iwdg_ops。
    ->watchdog_set_drvdata
    ->watchdog_set_nowayout--将CONFIG_WATCHDOG_NOWAYOUT配置属性写入到watchdog_device->status中。
    ->watchdog_init_timeout--设置timeout_param,或从dts读取timeout-sec初始化watchdog_device->timeout参数。
    ->stm32_iwdg_start--使能watchdog硬件。
    ->设置status位WDOG_HW_RUNNING,如果打开CONFIG_WATCHDOG_HANDLE_BOOT_ENABLED,在注册watchdog的时候,启动hrtimer进行喂狗。
    ->devm_watchdog_register_device--向watchdog core注册带资源管理的watchdog。

 stm32_iwdg_ops操作函数对watchdog硬件进行配置,被watchdog core API调用。

static const struct watchdog_info stm32_iwdg_info = {
    .options    = WDIOF_SETTIMEOUT |
              WDIOF_MAGICCLOSE |
              WDIOF_KEEPALIVEPING,
    .identity    = "STM32 Independent Watchdog",
};

static const struct watchdog_ops stm32_iwdg_ops = {
    .owner        = THIS_MODULE,
    .start        = stm32_iwdg_start,--使能watchdog。
    .ping        = stm32_iwdg_ping,--重置counter进行喂狗。
    .set_timeout    = stm32_iwdg_set_timeout,--重新设置timeout时间,并使能watchdog。
};

6 watchdog sysfs

/dev/watchdog:watchdog子系统初始化时创建的misc设备。

/dev/watchdog0:具体watchdog初始化时创建的设备。

watchdog模块参数:

/sys/module/watchdog/parameters
|-- handle_boot_enabled--初始值为CONFIG_WATCHDOG_HANDLE_BOOT_ENABLED。
`-- open_timeout--初始值为CONFIG_WATCHDOG_OPEN_TIMEOUT。

softdog相关sysfs:

/sys/devices/virtual/watchdog/watchdog0
|-- bootstatus
|-- dev
|-- identity
|-- nowayout
|-- pretimeout
|-- pretimeout_available_governors
|-- pretimeout_governor
|-- state
|-- status
|-- timeout
`-- uevent

7 watchdog(busybox)

通过watchdog -T  60 -t /dev/watchdog0,没30秒喂一次够,60秒没有喂狗则重启。

Usage: watchdog [-t N[ms]] [-T N[ms]] [-F] DEV

Periodically write to watchdog device DEV

        -T N    Reboot after N seconds if not reset (default 60)
        -t N    Reset every N seconds (default 30)
        -F      Run in foreground

watchdog内部实现流程如下:

watchdog_main
  ->shutdown_on_signal--watchdog收到异常信号退出时调用,关闭watchdog。
    ->shutdown_watchdog--关闭watchdog。
  ->watchdog_open
  ->WDIOC_SETOPTIONS--启动watchdog。
  ->WDIOC_SETTIMEOUT--设置timeout。
  ->while(1)--循环写空内容,然后睡眠。

 

posted on 2024-02-03 15:33  ArnoldLu  阅读(420)  评论(0编辑  收藏  举报

导航