diff options
author | Lennart Poettering <lennart@poettering.net> | 2021-10-13 12:56:44 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-10-13 12:56:44 +0200 |
commit | b6e44cd934873659f233bfd962e4a0dcb3026e59 (patch) | |
tree | f8f5ea7891d022cdfdc1dccf204650fce3cc23f0 /src/shared | |
parent | core/bpf-firewall: add missing oom check (diff) | |
parent | watchdog: rename special string "infinity" taken by the watchdog timeout opti... (diff) | |
download | systemd-b6e44cd934873659f233bfd962e4a0dcb3026e59.tar.xz systemd-b6e44cd934873659f233bfd962e4a0dcb3026e59.zip |
Merge pull request #20787 from fbuihuu/watchdog-more-rework
Watchdog more rework
Diffstat (limited to 'src/shared')
-rw-r--r-- | src/shared/watchdog.c | 188 |
1 files changed, 128 insertions, 60 deletions
diff --git a/src/shared/watchdog.c b/src/shared/watchdog.c index 3f6a2f0228..4de6003039 100644 --- a/src/shared/watchdog.c +++ b/src/shared/watchdog.c @@ -15,59 +15,114 @@ #include "watchdog.h" static int watchdog_fd = -1; -static char *watchdog_device = NULL; -static usec_t watchdog_timeout = USEC_INFINITY; +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; +static int watchdog_set_enable(bool enable) { + int flags = enable ? WDIOS_ENABLECARD : WDIOS_DISABLECARD; + int r; + + assert(watchdog_fd >= 0); + + r = ioctl(watchdog_fd, WDIOC_SETOPTIONS, &flags); + if (r < 0) { + if (!enable) + return log_warning_errno(errno, "Failed to disable hardware watchdog, ignoring: %m"); + + /* ENOTTY means the watchdog is always enabled so we're fine */ + log_full_errno(ERRNO_IS_NOT_SUPPORTED(errno) ? LOG_DEBUG : LOG_WARNING, errno, + "Failed to enable hardware watchdog, ignoring: %m"); + if (!ERRNO_IS_NOT_SUPPORTED(errno)) + return -errno; + } + + return 0; +} + +static int watchdog_get_timeout(void) { + int sec = 0; + + assert(watchdog_fd > 0); + + if (ioctl(watchdog_fd, WDIOC_GETTIMEOUT, &sec) < 0) + return -errno; + + assert(sec > 0); + watchdog_timeout = sec * USEC_PER_SEC; + + return 0; +} + +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 */ + + if (ioctl(watchdog_fd, WDIOC_SETTIMEOUT, &sec) < 0) + return -errno; + + assert(sec > 0);/* buggy driver ? */ + watchdog_timeout = sec * USEC_PER_SEC; + + return 0; +} + +static int watchdog_ping_now(void) { + assert(watchdog_fd >= 0); + + if (ioctl(watchdog_fd, WDIOC_KEEPALIVE, 0) < 0) + return log_warning_errno(errno, "Failed to ping hardware watchdog, ignoring: %m"); + + watchdog_last_ping = now(clock_boottime_or_monotonic()); + + return 0; +} + static int update_timeout(void) { + int r; + + assert(watchdog_timeout > 0); + if (watchdog_fd < 0) return 0; - if (watchdog_timeout == USEC_INFINITY) - return 0; - if (watchdog_timeout == 0) { - int flags; + if (watchdog_timeout != USEC_INFINITY) { + r = watchdog_set_timeout(); + if (r < 0) { + if (!ERRNO_IS_NOT_SUPPORTED(r)) + return log_error_errno(r, "Failed to set timeout to %s: %m", + FORMAT_TIMESPAN(watchdog_timeout, 0)); - flags = WDIOS_DISABLECARD; - if (ioctl(watchdog_fd, WDIOC_SETOPTIONS, &flags) < 0) - return log_warning_errno(errno, "Failed to disable hardware watchdog, ignoring: %m"); - } else { - int sec, flags; - usec_t t; - - t = DIV_ROUND_UP(watchdog_timeout, USEC_PER_SEC); - sec = MIN(t, (usec_t) INT_MAX); /* Saturate */ - if (ioctl(watchdog_fd, WDIOC_SETTIMEOUT, &sec) < 0) - return log_warning_errno(errno, "Failed to set timeout to %is, ignoring: %m", sec); - - /* Just in case the driver is buggy */ - assert(sec > 0); - - /* watchdog_timeout stores the actual timeout used by the HW */ - watchdog_timeout = sec * USEC_PER_SEC; - log_info("Set hardware watchdog to %s.", FORMAT_TIMESPAN(watchdog_timeout, 0)); - - flags = WDIOS_ENABLECARD; - if (ioctl(watchdog_fd, WDIOC_SETOPTIONS, &flags) < 0) { - /* ENOTTY means the watchdog is always enabled so we're fine */ - log_full_errno(ERRNO_IS_NOT_SUPPORTED(errno) ? LOG_DEBUG : LOG_WARNING, errno, - "Failed to enable hardware watchdog, ignoring: %m"); - if (!ERRNO_IS_NOT_SUPPORTED(errno)) - return -errno; + log_info("Modifying watchdog timeout is not supported, reusing the programmed timeout."); + watchdog_timeout = USEC_INFINITY; } + } - if (ioctl(watchdog_fd, WDIOC_KEEPALIVE, 0) < 0) - return log_warning_errno(errno, "Failed to ping hardware watchdog, ignoring: %m"); - - watchdog_last_ping = now(clock_boottime_or_monotonic()); + if (watchdog_timeout == USEC_INFINITY) { + r = watchdog_get_timeout(); + if (r < 0) + return log_error_errno(errno, "Failed to query watchdog HW timeout: %m"); } - return 0; + r = watchdog_set_enable(true); + if (r < 0) + return r; + + log_info("Watchdog running with a timeout of %s.", FORMAT_TIMESPAN(watchdog_timeout, 0)); + + return watchdog_ping_now(); } static int open_watchdog(void) { struct watchdog_info ident; const char *fn; + int r; if (watchdog_fd >= 0) return 0; @@ -85,7 +140,11 @@ static int open_watchdog(void) { ident.firmware_version, fn); - return update_timeout(); + r = update_timeout(); + if (r < 0) + watchdog_close(true); + + return r; } int watchdog_set_device(const char *path) { @@ -102,21 +161,38 @@ int watchdog_set_device(const char *path) { } int watchdog_setup(usec_t timeout) { + usec_t previous_timeout; + int r; + + /* timeout=0 closes the device whereas passing timeout=USEC_INFINITY + * opens it (if needed) without configuring any particular timeout and + * thus reuses the programmed value (therefore it's a nop if the device + * is already opened). + */ + + if (timeout == 0) { + watchdog_close(true); + return 0; + } + + /* Let's shortcut duplicated requests */ + if (watchdog_fd >= 0 && (timeout == watchdog_timeout || timeout == USEC_INFINITY)) + return 0; /* Initialize the watchdog timeout with the caller value. This value is * going to be updated by update_timeout() with the closest value * supported by the driver */ + previous_timeout = watchdog_timeout; watchdog_timeout = timeout; - /* If we didn't open the watchdog yet and didn't get any explicit - * timeout value set, don't do anything */ - if (watchdog_fd < 0 && watchdog_timeout == USEC_INFINITY) - return 0; - if (watchdog_fd < 0) return open_watchdog(); - return update_timeout(); + r = update_timeout(); + if (r < 0) + watchdog_timeout = previous_timeout; + + return r; } usec_t watchdog_runtime_wait(void) { @@ -138,7 +214,7 @@ usec_t watchdog_runtime_wait(void) { int watchdog_ping(void) { usec_t ntime; - if (!timestamp_is_set(watchdog_timeout)) + if (watchdog_timeout == 0) return 0; if (watchdog_fd < 0) @@ -155,24 +231,20 @@ int watchdog_ping(void) { return 0; } - if (ioctl(watchdog_fd, WDIOC_KEEPALIVE, 0) < 0) - return log_warning_errno(errno, "Failed to ping hardware watchdog, ignoring: %m"); - - watchdog_last_ping = ntime; - return 0; + return watchdog_ping_now(); } void watchdog_close(bool disarm) { + + /* Once closed, pinging the device becomes a NOP and we request a new + * call to watchdog_setup() to open the device again. */ + watchdog_timeout = 0; + if (watchdog_fd < 0) return; if (disarm) { - int flags; - - /* Explicitly disarm it */ - flags = WDIOS_DISABLECARD; - if (ioctl(watchdog_fd, WDIOC_SETOPTIONS, &flags) < 0) - log_warning_errno(errno, "Failed to disable hardware watchdog, ignoring: %m"); + (void) watchdog_set_enable(false); /* To be sure, use magic close logic, too */ for (;;) { @@ -189,8 +261,4 @@ void watchdog_close(bool disarm) { } watchdog_fd = safe_close(watchdog_fd); - - /* Once closed, pinging the device becomes a NOP and we request a new - * call to watchdog_setup() to open the device again. */ - watchdog_timeout = USEC_INFINITY; } |