Linux-Watchdog Archive on lore.kernel.org
 help / color / Atom feed
* [PATCH] watchdog: Add software pretimeout support
@ 2020-12-17  1:36 Curtis Klein
  0 siblings, 0 replies; only message in thread
From: Curtis Klein @ 2020-12-17  1:36 UTC (permalink / raw)
  To: wim, linux; +Cc: linux-watchdog, Curtis Klein

This adds the option to use a software timer to generate a watchdog
pretimeout event for hardware watchdogs that do not support the
pretimeout feature.

With this enabled, all watchdogs will appear to have pretimeout support
in userspace. If no pretimeout value is set, there will be no change in
the watchdog's behavior. If a pretimeout value is set for a specific
watchdog that does not have built-in pretimeout support, a timer will be
started that should fire at the specified time before the watchdog
timeout would occur. When the watchdog is successfully pinged, the timer
will be restarted. If the timer is allowed to fire it will generate a
pretimeout event.

If the watchdog does support a pretimeout natively, that functionality
will be used instead of the software timer.

Signed-off-by: Curtis Klein <curtis.klein@hpe.com>
---
 drivers/watchdog/Kconfig               |  8 +++++
 drivers/watchdog/watchdog_dev.c        | 62 +++++++++++++++++++++++++++++++---
 drivers/watchdog/watchdog_pretimeout.c |  7 ++--
 3 files changed, 71 insertions(+), 6 deletions(-)

diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index f22e373..6a4554b 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -73,6 +73,14 @@ config WATCHDOG_SYSFS
 	  Say Y here if you want to enable watchdog device status read through
 	  sysfs attributes.
 
+config WATCHDOG_SOFTWARE_PRETIMEOUT
+	bool "Enable software timer based pretimeout support"
+	help
+	  Enable this if you want to use a software timer based pretimeout for
+	  watchdogs that do not have pretimeout support. Be aware that because
+	  this pretimeout is purely software based, it may not be able to fire
+	  before the actual watchdog fires in some situations.
+
 comment "Watchdog Pretimeout Governors"
 
 config WATCHDOG_PRETIMEOUT_GOV
diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c
index 2946f3a..ae4b5b4 100644
--- a/drivers/watchdog/watchdog_dev.c
+++ b/drivers/watchdog/watchdog_dev.c
@@ -7,6 +7,8 @@
  *
  *	(c) Copyright 2008-2011 Wim Van Sebroeck <wim@iguana.be>.
  *
+ *	(c) Copyright 2020 Hewlett Packard Enterprise Development LP.
+ *
  *
  *	This source code is part of the generic code that can be used
  *	by all the watchdog timer drivers.
@@ -64,6 +66,9 @@ struct watchdog_core_data {
 	ktime_t open_deadline;
 	struct hrtimer timer;
 	struct kthread_work work;
+#ifdef CONFIG_WATCHDOG_SOFTWARE_PRETIMEOUT
+	struct hrtimer pretimeout_timer;
+#endif
 	unsigned long status;		/* Internal status bits */
 #define _WDOG_DEV_OPEN		0	/* Opened ? */
 #define _WDOG_ALLOW_RELEASE	1	/* Did we receive the magic char ? */
@@ -185,6 +190,17 @@ static int __watchdog_ping(struct watchdog_device *wdd)
 	else
 		err = wdd->ops->start(wdd); /* restart watchdog */
 
+#ifdef CONFIG_WATCHDOG_SOFTWARE_PRETIMEOUT
+	if (!(wdd->info->options & WDIOF_PRETIMEOUT) && wdd->pretimeout) {
+		if (err == 0)
+			hrtimer_start(&wd_data->pretimeout_timer,
+				      ktime_set(wdd->timeout - wdd->pretimeout, 0),
+				      HRTIMER_MODE_REL);
+	} else {
+		hrtimer_cancel(&wd_data->pretimeout_timer);
+	}
+#endif
+
 	watchdog_update_worker(wdd);
 
 	return err;
@@ -250,6 +266,19 @@ static enum hrtimer_restart watchdog_timer_expired(struct hrtimer *timer)
 	return HRTIMER_NORESTART;
 }
 
+
+#ifdef CONFIG_WATCHDOG_SOFTWARE_PRETIMEOUT
+static enum hrtimer_restart watchdog_software_pretimeout(struct hrtimer *timer)
+{
+	struct watchdog_core_data *wd_data;
+
+	wd_data = container_of(timer, struct watchdog_core_data, pretimeout_timer);
+
+	watchdog_notify_pretimeout(wd_data->wdd);
+	return HRTIMER_NORESTART;
+}
+#endif
+
 /*
  *	watchdog_start: wrapper to start the watchdog.
  *	@wdd: the watchdog device to start
@@ -287,6 +316,12 @@ static int watchdog_start(struct watchdog_device *wdd)
 		}
 	}
 
+#ifdef CONFIG_WATCHDOG_SOFTWARE_PRETIMEOUT
+		if (!(wdd->info->options & WDIOF_PRETIMEOUT) && wdd->pretimeout)
+			hrtimer_start(&wd_data->pretimeout_timer,
+				      ktime_set(wdd->timeout - wdd->pretimeout, 0),
+				      HRTIMER_MODE_REL);
+#endif
 	return err;
 }
 
@@ -325,6 +360,10 @@ static int watchdog_stop(struct watchdog_device *wdd)
 	if (err == 0) {
 		clear_bit(WDOG_ACTIVE, &wdd->status);
 		watchdog_update_worker(wdd);
+
+#ifdef CONFIG_WATCHDOG_SOFTWARE_PRETIMEOUT
+		hrtimer_cancel(&wdd->wd_data->pretimeout_timer);
+#endif
 	}
 
 	return err;
@@ -361,6 +400,9 @@ static unsigned int watchdog_get_status(struct watchdog_device *wdd)
 	if (test_and_clear_bit(_WDOG_KEEPALIVE, &wd_data->status))
 		status |= WDIOF_KEEPALIVEPING;
 
+	if (IS_ENABLED(CONFIG_WATCHDOG_SOFTWARE_PRETIMEOUT))
+		status |= WDIOF_PRETIMEOUT;
+
 	return status;
 }
 
@@ -408,7 +450,8 @@ static int watchdog_set_pretimeout(struct watchdog_device *wdd,
 {
 	int err = 0;
 
-	if (!(wdd->info->options & WDIOF_PRETIMEOUT))
+	if (!(wdd->info->options & WDIOF_PRETIMEOUT) &&
+	    !IS_ENABLED(CONFIG_WATCHDOG_SOFTWARE_PRETIMEOUT))
 		return -EOPNOTSUPP;
 
 	if (watchdog_pretimeout_invalid(wdd, timeout))
@@ -595,12 +638,14 @@ static umode_t wdt_is_visible(struct kobject *kobj, struct attribute *attr,
 	if (attr == &dev_attr_timeleft.attr && !wdd->ops->get_timeleft)
 		mode = 0;
 	else if (attr == &dev_attr_pretimeout.attr &&
-		 !(wdd->info->options & WDIOF_PRETIMEOUT))
+		 !(wdd->info->options & WDIOF_PRETIMEOUT) &&
+		 !IS_ENABLED(CONFIG_WATCHDOG_SOFTWARE_PRETIMEOUT))
 		mode = 0;
 	else if ((attr == &dev_attr_pretimeout_governor.attr ||
 		  attr == &dev_attr_pretimeout_available_governors.attr) &&
-		 (!(wdd->info->options & WDIOF_PRETIMEOUT) ||
-		  !IS_ENABLED(CONFIG_WATCHDOG_PRETIMEOUT_GOV)))
+		 ((!(wdd->info->options & WDIOF_PRETIMEOUT) &&
+		   !IS_ENABLED(CONFIG_WATCHDOG_SOFTWARE_PRETIMEOUT)) ||
+		   !IS_ENABLED(CONFIG_WATCHDOG_PRETIMEOUT_GOV)))
 		mode = 0;
 
 	return mode;
@@ -1010,6 +1055,11 @@ static int watchdog_cdev_register(struct watchdog_device *wdd)
 	hrtimer_init(&wd_data->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_HARD);
 	wd_data->timer.function = watchdog_timer_expired;
 
+#ifdef CONFIG_WATCHDOG_SOFTWARE_PRETIMEOUT
+	hrtimer_init(&wd_data->pretimeout_timer,  CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+	wd_data->pretimeout_timer.function = watchdog_software_pretimeout;
+#endif
+
 	if (wdd->id == 0) {
 		old_wd_data = wd_data;
 		watchdog_miscdev.parent = wdd->parent;
@@ -1097,6 +1147,10 @@ static void watchdog_cdev_unregister(struct watchdog_device *wdd)
 	hrtimer_cancel(&wd_data->timer);
 	kthread_cancel_work_sync(&wd_data->work);
 
+#ifdef CONFIG_WATCHDOG_SOFTWARE_PRETIMEOUT
+	hrtimer_cancel(&wd_data->pretimeout_timer);
+#endif
+
 	put_device(&wd_data->dev);
 }
 
diff --git a/drivers/watchdog/watchdog_pretimeout.c b/drivers/watchdog/watchdog_pretimeout.c
index 01ca84b..cb3f8f4 100644
--- a/drivers/watchdog/watchdog_pretimeout.c
+++ b/drivers/watchdog/watchdog_pretimeout.c
@@ -1,6 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0-or-later
 /*
  * Copyright (C) 2015-2016 Mentor Graphics
+ * Copyright (C) 2020 Hewlett Packard Enterprise Development LP.
  */
 
 #include <linux/list.h>
@@ -177,7 +178,8 @@ int watchdog_register_pretimeout(struct watchdog_device *wdd)
 {
 	struct watchdog_pretimeout *p;
 
-	if (!(wdd->info->options & WDIOF_PRETIMEOUT))
+	if (!(wdd->info->options & WDIOF_PRETIMEOUT) &&
+	    !IS_ENABLED(CONFIG_WATCHDOG_SOFTWARE_PRETIMEOUT))
 		return 0;
 
 	p = kzalloc(sizeof(*p), GFP_KERNEL);
@@ -197,7 +199,8 @@ void watchdog_unregister_pretimeout(struct watchdog_device *wdd)
 {
 	struct watchdog_pretimeout *p, *t;
 
-	if (!(wdd->info->options & WDIOF_PRETIMEOUT))
+	if (!(wdd->info->options & WDIOF_PRETIMEOUT) &&
+	    !IS_ENABLED(CONFIG_WATCHDOG_SOFTWARE_PRETIMEOUT))
 		return;
 
 	spin_lock_irq(&pretimeout_lock);
-- 
2.7.4


^ permalink raw reply	[flat|nested] only message in thread

only message in thread, back to index

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-12-17  1:36 [PATCH] watchdog: Add software pretimeout support Curtis Klein

Linux-Watchdog Archive on lore.kernel.org

Archives are clonable:
	git clone --mirror https://lore.kernel.org/linux-watchdog/0 linux-watchdog/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 linux-watchdog linux-watchdog/ https://lore.kernel.org/linux-watchdog \
		linux-watchdog@vger.kernel.org
	public-inbox-index linux-watchdog

Example config snippet for mirrors

Newsgroup available over NNTP:
	nntp://nntp.lore.kernel.org/org.kernel.vger.linux-watchdog


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git