Messages in this thread Patch in this message |  | | From | George Cherian <> | | Subject | [PATCH v5 1/2] drivers: watchdog: Introduce watchdog reset timeout on panic | | Date | Wed, 9 Apr 2025 05:34:51 +0000 |
| |
In a kernel panic situation followed by loading of kdump kernel, it can so happen that watchdog reset can happen while booting the kdump kernel. Add a provision to configure/update the watchdog timeout in case of panic. This is acheived by: 1. Provide a sysfs entry to configure the timeout (reset_on_panic) in case of kernel panic. 2. reset_on_panic takes time in seconds. If set to 0 then the watchdog is disarmed completely. 3. Register a panic notifer and update the watchdog timeout in case of panic. 4. Introduce a new flag to watchdog drivers to set whether ops are atmoic. The above feature cannot be supported to watchdog drivers which sleeps during set_timeout or stop.
Signed-off-by: George Cherian <george.cherian@marvell.com> --- drivers/watchdog/watchdog_core.c | 33 ++++++++++++++++++++++++++++++++ drivers/watchdog/watchdog_dev.c | 28 +++++++++++++++++++++++++++ include/linux/watchdog.h | 3 +++ include/uapi/linux/watchdog.h | 1 + 4 files changed, 65 insertions(+)
diff --git a/drivers/watchdog/watchdog_core.c b/drivers/watchdog/watchdog_core.c index 6152dba4b52c..7ec46dfe9888 100644 --- a/drivers/watchdog/watchdog_core.c +++ b/drivers/watchdog/watchdog_core.c @@ -35,6 +35,7 @@ #include <linux/err.h> /* For IS_ERR macros */ #include <linux/of.h> /* For of_alias_get_id */ #include <linux/property.h> /* For device_property_read_u32 */ +#include <linux/panic_notifier.h> /* For panic handler */ #include <linux/suspend.h> #include "watchdog_core.h" /* For watchdog_dev_register/... */ @@ -155,6 +156,28 @@ int watchdog_init_timeout(struct watchdog_device *wdd, } EXPORT_SYMBOL_GPL(watchdog_init_timeout); +static int watchdog_panic_notify(struct notifier_block *nb, + unsigned long action, void *data) +{ + int ret; + struct watchdog_device *wdd; + + wdd = container_of(nb, struct watchdog_device, panic_nb); + if (watchdog_active(wdd)) { + if (!wdd->reset_on_panic) { + ret = wdd->ops->stop(wdd); + if (ret) + return NOTIFY_BAD; + } else { + ret = wdd->ops->set_timeout(wdd, wdd->reset_on_panic); + if (ret) + return NOTIFY_BAD; + } + } + + return NOTIFY_DONE; +} + static int watchdog_reboot_notifier(struct notifier_block *nb, unsigned long code, void *data) { @@ -334,6 +357,13 @@ static int ___watchdog_register_device(struct watchdog_device *wdd) wdd->id, ret); } + if (wdd->info->options & WDIOF_OPS_ATOMIC) { + wdd->panic_nb.notifier_call = watchdog_panic_notify; + atomic_notifier_chain_register(&panic_notifier_list, + &wdd->panic_nb); + set_bit(WDOG_RESET_ON_PANIC, &wdd->status); + } + return 0; } @@ -390,6 +420,9 @@ static void __watchdog_unregister_device(struct watchdog_device *wdd) if (test_bit(WDOG_STOP_ON_REBOOT, &wdd->status)) unregister_reboot_notifier(&wdd->reboot_nb); + if (test_bit(WDOG_RESET_ON_PANIC, &wdd->status)) + atomic_notifier_chain_unregister(&panic_notifier_list, + &wdd->panic_nb); watchdog_dev_unregister(wdd); ida_free(&watchdog_ida, wdd->id); } diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c index 8369fd94fc1a..89cc9b681992 100644 --- a/drivers/watchdog/watchdog_dev.c +++ b/drivers/watchdog/watchdog_dev.c @@ -617,6 +617,33 @@ static ssize_t pretimeout_governor_store(struct device *dev, } static DEVICE_ATTR_RW(pretimeout_governor); +static ssize_t reset_on_panic_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct watchdog_device *wdd = dev_get_drvdata(dev); + + return sysfs_emit(buf, "%u\n", wdd->reset_on_panic); +} + +static ssize_t reset_on_panic_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct watchdog_device *wdd = dev_get_drvdata(dev); + unsigned int value; + int ret; + + ret = kstrtouint(buf, 0, &value); + if (ret) + return ret; + + wdd->reset_on_panic = value; + + return count; +} +static DEVICE_ATTR_RW(reset_on_panic); + static umode_t wdt_is_visible(struct kobject *kobj, struct attribute *attr, int n) { @@ -648,6 +675,7 @@ static struct attribute *wdt_attrs[] = { &dev_attr_bootstatus.attr, &dev_attr_status.attr, &dev_attr_nowayout.attr, + &dev_attr_reset_on_panic.attr, &dev_attr_pretimeout_governor.attr, &dev_attr_pretimeout_available_governors.attr, NULL, diff --git a/include/linux/watchdog.h b/include/linux/watchdog.h index 99660197a36c..8783edf93bab 100644 --- a/include/linux/watchdog.h +++ b/include/linux/watchdog.h @@ -105,9 +105,11 @@ struct watchdog_device { unsigned int max_timeout; unsigned int min_hw_heartbeat_ms; unsigned int max_hw_heartbeat_ms; + unsigned int reset_on_panic; struct notifier_block reboot_nb; struct notifier_block restart_nb; struct notifier_block pm_nb; + struct notifier_block panic_nb; void *driver_data; struct watchdog_core_data *wd_data; unsigned long status; @@ -118,6 +120,7 @@ struct watchdog_device { #define WDOG_HW_RUNNING 3 /* True if HW watchdog running */ #define WDOG_STOP_ON_UNREGISTER 4 /* Should be stopped on unregister */ #define WDOG_NO_PING_ON_SUSPEND 5 /* Ping worker should be stopped on suspend */ +#define WDOG_RESET_ON_PANIC 6 /* Reset the watchdog on panic for loading kdump kernels */ struct list_head deferred; }; diff --git a/include/uapi/linux/watchdog.h b/include/uapi/linux/watchdog.h index b15cde5c9054..4e19cc653a72 100644 --- a/include/uapi/linux/watchdog.h +++ b/include/uapi/linux/watchdog.h @@ -48,6 +48,7 @@ struct watchdog_info { #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_OPS_ATOMIC 0x0800 /* Indicate whether watchdog ops will sleep or not */ #define WDIOF_KEEPALIVEPING 0x8000 /* Keep alive ping reply */ #define WDIOS_DISABLECARD 0x0001 /* Turn off the watchdog timer */ -- 2.34.1
|  |