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 30 /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)--循环写空内容,然后睡眠。