diff --git a/src/shared/watchdog.c b/src/shared/watchdog.c index f29d3761b0..98ef979dbe 100644 --- a/src/shared/watchdog.c +++ b/src/shared/watchdog.c @@ -20,6 +20,20 @@ static char *watchdog_device; static usec_t watchdog_timeout; /* 0 → close device and USEC_INFINITY → don't change timeout */ static usec_t watchdog_last_ping = USEC_INFINITY; +/* Starting from kernel version 4.5, the maximum allowable watchdog timeout is + * UINT_MAX/1000U seconds (since internal calculations are done in milliseconds + * using unsigned integers. However, the kernel's userspace API for the watchdog + * uses signed integers for its ioctl parameters (even for timeout values and + * bit flags) so this is why we must consider the maximum signed integer value + * as well. + */ +#define WATCHDOG_TIMEOUT_MAX_SEC (CONST_MIN(UINT_MAX/1000U, (unsigned)INT_MAX)) + +static int saturated_usec_to_sec(usec_t val) { + usec_t t = DIV_ROUND_UP(val, USEC_PER_SEC); + return MIN(t, (usec_t) WATCHDOG_TIMEOUT_MAX_SEC); /* Saturate to watchdog max */ +} + static int watchdog_set_enable(bool enable) { int flags = enable ? WDIOS_ENABLECARD : WDIOS_DISABLECARD; @@ -54,14 +68,12 @@ static int watchdog_get_timeout(void) { } static int watchdog_set_timeout(void) { - usec_t t; int sec; assert(watchdog_fd >= 0); assert(timestamp_is_set(watchdog_timeout)); - t = DIV_ROUND_UP(watchdog_timeout, USEC_PER_SEC); - sec = MIN(t, (usec_t) INT_MAX); /* Saturate */ + sec = saturated_usec_to_sec(watchdog_timeout); if (ioctl(watchdog_fd, WDIOC_SETTIMEOUT, &sec) < 0) return -errno;