[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-ID: <20250221023550.2092232-1-george.cherian@marvell.com>
Date: Fri, 21 Feb 2025 02:35:49 +0000
From: George Cherian <george.cherian@...vell.com>
To: <wim@...ux-watchdog.org>, <linux@...ck-us.net>, <corbet@....net>
CC: <linux-watchdog@...r.kernel.org>, <linux-doc@...r.kernel.org>,
<linux-kernel@...r.kernel.org>,
George Cherian <george.cherian@...vell.com>
Subject: [PATCH 1/2] drivers: watchdog: Add support for panic notifier callback
Watchdog is not turned off in kernel panic situation.
In certain systems this might prevent the successful loading
of kdump kernel. The kdump kernel might hit a watchdog reset
while it is booting.
To avoid such scenarios add a panic notifier call back function
which can stop the watchdog. This provision can be enabled on per
driver basis if the user wishes to by setting the flag
WDOG_STOP_ON_PANIC from respective drivers.
Signed-off-by: George Cherian <george.cherian@...vell.com>
---
drivers/watchdog/watchdog_core.c | 31 +++++++++++++++++++++++++++++++
include/linux/watchdog.h | 8 ++++++++
2 files changed, 39 insertions(+)
diff --git a/drivers/watchdog/watchdog_core.c b/drivers/watchdog/watchdog_core.c
index d46d8c8c01f2..cce29d54535c 100644
--- a/drivers/watchdog/watchdog_core.c
+++ b/drivers/watchdog/watchdog_core.c
@@ -34,6 +34,7 @@
#include <linux/idr.h> /* For ida_* macros */
#include <linux/err.h> /* For IS_ERR macros */
#include <linux/of.h> /* For of_get_timeout_sec */
+#include <linux/panic_notifier.h>
#include <linux/suspend.h>
#include "watchdog_core.h" /* For watchdog_dev_register/... */
@@ -155,6 +156,23 @@ 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)
+{
+ struct watchdog_device *wdd;
+
+ wdd = container_of(nb, struct watchdog_device, panic_nb);
+ if (watchdog_active(wdd)) {
+ int ret;
+
+ ret = wdd->ops->stop(wdd);
+ if (ret)
+ return NOTIFY_BAD;
+ }
+
+ return NOTIFY_DONE;
+}
+
static int watchdog_reboot_notifier(struct notifier_block *nb,
unsigned long code, void *data)
{
@@ -334,6 +352,16 @@ static int ___watchdog_register_device(struct watchdog_device *wdd)
wdd->id, ret);
}
+ if (test_bit(WDOG_STOP_ON_PANIC, &wdd->status)) {
+ if (!wdd->ops->stop) {
+ pr_warn("watchdog%d: stop_on_panic not supported\n", wdd->id);
+ } else {
+ wdd->panic_nb.notifier_call = watchdog_panic_notify;
+ atomic_notifier_chain_register(&panic_notifier_list,
+ &wdd->panic_nb);
+ }
+ }
+
return 0;
}
@@ -390,6 +418,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_STOP_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/include/linux/watchdog.h b/include/linux/watchdog.h
index 99660197a36c..3c21b527ede9 100644
--- a/include/linux/watchdog.h
+++ b/include/linux/watchdog.h
@@ -108,6 +108,7 @@ struct watchdog_device {
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 +119,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_STOP_ON_PANIC 6 /* Should be stopped on panic for loading kdump kernels */
struct list_head deferred;
};
@@ -146,6 +148,12 @@ static inline void watchdog_set_nowayout(struct watchdog_device *wdd, bool noway
set_bit(WDOG_NO_WAY_OUT, &wdd->status);
}
+/* Use the following function to stop the watchdog on panic */
+static inline void watchdog_stop_on_panic(struct watchdog_device *wdd)
+{
+ set_bit(WDOG_STOP_ON_PANIC, &wdd->status);
+}
+
/* Use the following function to stop the watchdog on reboot */
static inline void watchdog_stop_on_reboot(struct watchdog_device *wdd)
{
--
2.34.1
Powered by blists - more mailing lists