Kaynağa Gözat

watchdog: Add support for minimum time between heartbeats

Some watchdogs require a minimum time between heartbeats.
Examples are the watchdogs in DA9062 and AT91SAM9x.

Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Wim Van Sebroeck <wim@iguana.be>
Guenter Roeck 9 yıl önce
ebeveyn
işleme
15013ad813

+ 3 - 0
Documentation/watchdog/watchdog-kernel-api.txt

@@ -52,6 +52,7 @@ struct watchdog_device {
 	unsigned int timeout;
 	unsigned int timeout;
 	unsigned int min_timeout;
 	unsigned int min_timeout;
 	unsigned int max_timeout;
 	unsigned int max_timeout;
+	unsigned int min_hw_heartbeat_ms;
 	unsigned int max_hw_heartbeat_ms;
 	unsigned int max_hw_heartbeat_ms;
 	struct notifier_block reboot_nb;
 	struct notifier_block reboot_nb;
 	struct notifier_block restart_nb;
 	struct notifier_block restart_nb;
@@ -81,6 +82,8 @@ It contains following fields:
 * max_timeout: the watchdog timer's maximum timeout value (in seconds),
 * max_timeout: the watchdog timer's maximum timeout value (in seconds),
   as seen from userspace. If set, the maximum configurable value for
   as seen from userspace. If set, the maximum configurable value for
   'timeout'. Not used if max_hw_heartbeat_ms is non-zero.
   'timeout'. Not used if max_hw_heartbeat_ms is non-zero.
+* min_hw_heartbeat_ms: Minimum time between heartbeats sent to the chip,
+  in milli-seconds.
 * max_hw_heartbeat_ms: Maximum hardware heartbeat, in milli-seconds.
 * max_hw_heartbeat_ms: Maximum hardware heartbeat, in milli-seconds.
   If set, the infrastructure will send heartbeats to the watchdog driver
   If set, the infrastructure will send heartbeats to the watchdog driver
   if 'timeout' is larger than max_hw_heartbeat_ms, unless WDOG_ACTIVE
   if 'timeout' is larger than max_hw_heartbeat_ms, unless WDOG_ACTIVE

+ 15 - 0
drivers/watchdog/watchdog_dev.c

@@ -64,6 +64,7 @@ struct watchdog_core_data {
 	struct watchdog_device *wdd;
 	struct watchdog_device *wdd;
 	struct mutex lock;
 	struct mutex lock;
 	unsigned long last_keepalive;
 	unsigned long last_keepalive;
+	unsigned long last_hw_keepalive;
 	struct delayed_work work;
 	struct delayed_work work;
 	unsigned long status;		/* Internal status bits */
 	unsigned long status;		/* Internal status bits */
 #define _WDOG_DEV_OPEN		0	/* Opened ? */
 #define _WDOG_DEV_OPEN		0	/* Opened ? */
@@ -137,8 +138,19 @@ static inline void watchdog_update_worker(struct watchdog_device *wdd)
 
 
 static int __watchdog_ping(struct watchdog_device *wdd)
 static int __watchdog_ping(struct watchdog_device *wdd)
 {
 {
+	struct watchdog_core_data *wd_data = wdd->wd_data;
+	unsigned long earliest_keepalive = wd_data->last_hw_keepalive +
+				msecs_to_jiffies(wdd->min_hw_heartbeat_ms);
 	int err;
 	int err;
 
 
+	if (time_is_after_jiffies(earliest_keepalive)) {
+		mod_delayed_work(watchdog_wq, &wd_data->work,
+				 earliest_keepalive - jiffies);
+		return 0;
+	}
+
+	wd_data->last_hw_keepalive = jiffies;
+
 	if (wdd->ops->ping)
 	if (wdd->ops->ping)
 		err = wdd->ops->ping(wdd);  /* ping the watchdog */
 		err = wdd->ops->ping(wdd);  /* ping the watchdog */
 	else
 	else
@@ -819,6 +831,9 @@ static int watchdog_cdev_register(struct watchdog_device *wdd, dev_t devno)
 		return err;
 		return err;
 	}
 	}
 
 
+	/* Record time of most recent heartbeat as 'just before now'. */
+	wd_data->last_hw_keepalive = jiffies - 1;
+
 	/*
 	/*
 	 * If the watchdog is running, prevent its driver from being unloaded,
 	 * If the watchdog is running, prevent its driver from being unloaded,
 	 * and schedule an immediate ping.
 	 * and schedule an immediate ping.

+ 3 - 0
include/linux/watchdog.h

@@ -65,6 +65,8 @@ struct watchdog_ops {
  * @max_timeout:The watchdog devices maximum timeout value (in seconds)
  * @max_timeout:The watchdog devices maximum timeout value (in seconds)
  *		as configurable from user space. Only relevant if
  *		as configurable from user space. Only relevant if
  *		max_hw_heartbeat_ms is not provided.
  *		max_hw_heartbeat_ms is not provided.
+ * @min_hw_heartbeat_ms:
+ *		Minimum time between heartbeats, in milli-seconds.
  * @max_hw_heartbeat_ms:
  * @max_hw_heartbeat_ms:
  *		Hardware limit for maximum timeout, in milli-seconds.
  *		Hardware limit for maximum timeout, in milli-seconds.
  *		Replaces max_timeout if specified.
  *		Replaces max_timeout if specified.
@@ -95,6 +97,7 @@ struct watchdog_device {
 	unsigned int timeout;
 	unsigned int timeout;
 	unsigned int min_timeout;
 	unsigned int min_timeout;
 	unsigned int max_timeout;
 	unsigned int max_timeout;
+	unsigned int min_hw_heartbeat_ms;
 	unsigned int max_hw_heartbeat_ms;
 	unsigned int max_hw_heartbeat_ms;
 	struct notifier_block reboot_nb;
 	struct notifier_block reboot_nb;
 	struct notifier_block restart_nb;
 	struct notifier_block restart_nb;