]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
watchdog: Always return time left until watchdog times out
authorGuenter Roeck <linux@roeck-us.net>
Tue, 18 Nov 2025 21:04:07 +0000 (13:04 -0800)
committerWim Van Sebroeck <wim@linux-watchdog.org>
Sun, 18 Jan 2026 09:55:08 +0000 (10:55 +0100)
The watchdog core knows when the most recent keepalive was sent. It also
knows the configured timeout. With that, it can always calculate and
return the time left until a watchdog times out, even if its driver does
not support it.

Convert watchdog_get_timeleft() into a void function. It never returns an
error after this patch is applied, so the error checks in the calling code
are now pointless and can be removed.

Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Reviewed-by: Wim Van Sebroeck <wim@linux-watchdog.org>
Signed-off-by: Wim Van Sebroeck <wim@linux-watchdog.org>
drivers/watchdog/watchdog_dev.c

index 8369fd94fc1a61ec359305ac2bf4be7a42bb8940..9a5e544b886bf96c4568dbb64dc361daf1ef6c30 100644 (file)
@@ -424,20 +424,22 @@ static int watchdog_set_pretimeout(struct watchdog_device *wdd,
  *
  * Get the time before a watchdog will reboot (if not pinged).
  * The caller must hold wd_data->lock.
- *
- * Return: 0 if successful, error otherwise.
  */
-static int watchdog_get_timeleft(struct watchdog_device *wdd,
-                                                       unsigned int *timeleft)
+static void watchdog_get_timeleft(struct watchdog_device *wdd,
+                                 unsigned int *timeleft)
 {
        *timeleft = 0;
 
-       if (!wdd->ops->get_timeleft)
-               return -EOPNOTSUPP;
-
-       *timeleft = wdd->ops->get_timeleft(wdd);
+       if (wdd->ops->get_timeleft) {
+               *timeleft = wdd->ops->get_timeleft(wdd);
+       } else {
+               struct watchdog_core_data *wd_data = wdd->wd_data;
+               s64 last_keepalive_ms = ktime_ms_delta(ktime_get(), wd_data->last_keepalive);
+               s64 last_keepalive = DIV_ROUND_UP_ULL(last_keepalive_ms, 1000);
 
-       return 0;
+               if (wdd->timeout > last_keepalive)
+                       *timeleft = wdd->timeout - last_keepalive;
+       }
 }
 
 #ifdef CONFIG_WATCHDOG_SYSFS
@@ -499,16 +501,13 @@ static ssize_t timeleft_show(struct device *dev, struct device_attribute *attr,
 {
        struct watchdog_device *wdd = dev_get_drvdata(dev);
        struct watchdog_core_data *wd_data = wdd->wd_data;
-       ssize_t status;
        unsigned int val;
 
        mutex_lock(&wd_data->lock);
-       status = watchdog_get_timeleft(wdd, &val);
+       watchdog_get_timeleft(wdd, &val);
        mutex_unlock(&wd_data->lock);
-       if (!status)
-               status = sysfs_emit(buf, "%u\n", val);
 
-       return status;
+       return sysfs_emit(buf, "%u\n", val);
 }
 static DEVICE_ATTR_RO(timeleft);
 
@@ -624,9 +623,7 @@ static umode_t wdt_is_visible(struct kobject *kobj, struct attribute *attr,
        struct watchdog_device *wdd = dev_get_drvdata(dev);
        umode_t mode = attr->mode;
 
-       if (attr == &dev_attr_timeleft.attr && !wdd->ops->get_timeleft)
-               mode = 0;
-       else if (attr == &dev_attr_pretimeout.attr && !watchdog_have_pretimeout(wdd))
+       if (attr == &dev_attr_pretimeout.attr && !watchdog_have_pretimeout(wdd))
                mode = 0;
        else if ((attr == &dev_attr_pretimeout_governor.attr ||
                  attr == &dev_attr_pretimeout_available_governors.attr) &&
@@ -825,9 +822,7 @@ static long watchdog_ioctl(struct file *file, unsigned int cmd,
                err = put_user(wdd->timeout, p);
                break;
        case WDIOC_GETTIMELEFT:
-               err = watchdog_get_timeleft(wdd, &val);
-               if (err < 0)
-                       break;
+               watchdog_get_timeleft(wdd, &val);
                err = put_user(val, p);
                break;
        case WDIOC_SETPRETIMEOUT: