@@ -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);
}
@@ -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,
@@ -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;
};
@@ -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 */
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(+)