watchdog: Add watchdog pretimeout support
Add support for managing and configuring watchdog pretimeout values if the watchdog hardware supports it. The ping interval is adjusted to account for a pretimeout so that it will still ping at half the timeout interval before a pretimeout event would be triggered. By default the pretimeout defaults to 0s or disabled. The RuntimeWatchdogPreSec config option is added to allow the pretimeout to be specified (similar to RuntimeWatchdogSec). The RuntimeWatchdogPreUSec dbus property is added to override the pretimeout value at runtime (similar to RuntimeWatchdogUSec). Setting the pretimeout to 0s will disable the pretimeout.
This commit is contained in:
committed by
Luca Boccassi
parent
b491d74064
commit
5717062e93
+105
-7
@@ -18,6 +18,7 @@
|
||||
static int watchdog_fd = -1;
|
||||
static char *watchdog_device;
|
||||
static usec_t watchdog_timeout; /* 0 → close device and USEC_INFINITY → don't change timeout */
|
||||
static usec_t watchdog_pretimeout; /* 0 → disable pretimeout and USEC_INFINITY → don't change pretimeout */
|
||||
static usec_t watchdog_last_ping = USEC_INFINITY;
|
||||
|
||||
/* Starting from kernel version 4.5, the maximum allowable watchdog timeout is
|
||||
@@ -84,6 +85,46 @@ static int watchdog_set_timeout(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int watchdog_get_pretimeout(void) {
|
||||
int sec = 0;
|
||||
|
||||
assert(watchdog_fd >= 0);
|
||||
|
||||
if (ioctl(watchdog_fd, WDIOC_GETPRETIMEOUT, &sec) < 0) {
|
||||
watchdog_pretimeout = 0;
|
||||
return log_full_errno(ERRNO_IS_NOT_SUPPORTED(errno) ? LOG_DEBUG : LOG_WARNING, errno, "Failed to get pretimeout value, ignoring: %m");
|
||||
}
|
||||
|
||||
watchdog_pretimeout = sec * USEC_PER_SEC;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int watchdog_set_pretimeout(void) {
|
||||
int sec;
|
||||
|
||||
assert(watchdog_fd >= 0);
|
||||
assert(watchdog_pretimeout != USEC_INFINITY);
|
||||
|
||||
sec = saturated_usec_to_sec(watchdog_pretimeout);
|
||||
|
||||
if (ioctl(watchdog_fd, WDIOC_SETPRETIMEOUT, &sec) < 0) {
|
||||
watchdog_pretimeout = 0;
|
||||
|
||||
if (ERRNO_IS_NOT_SUPPORTED(errno)) {
|
||||
log_info("Watchdog does not support pretimeouts.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return log_error_errno(errno, "Failed to set pretimeout to %s: %m", FORMAT_TIMESPAN(sec, USEC_PER_SEC));
|
||||
}
|
||||
|
||||
/* The set ioctl does not return the actual value set so get it now. */
|
||||
(void) watchdog_get_pretimeout();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int watchdog_ping_now(void) {
|
||||
assert(watchdog_fd >= 0);
|
||||
|
||||
@@ -95,6 +136,34 @@ static int watchdog_ping_now(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int update_pretimeout(void) {
|
||||
int r, t_sec, pt_sec;
|
||||
|
||||
if (watchdog_fd < 0)
|
||||
return 0;
|
||||
|
||||
if (watchdog_timeout == USEC_INFINITY || watchdog_pretimeout == USEC_INFINITY)
|
||||
return 0;
|
||||
|
||||
/* Determine if the pretimeout is valid for the current watchdog timeout. */
|
||||
t_sec = saturated_usec_to_sec(watchdog_timeout);
|
||||
pt_sec = saturated_usec_to_sec(watchdog_pretimeout);
|
||||
if (pt_sec >= t_sec) {
|
||||
r = log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Cannot set watchdog pretimeout to %is (%s watchdog timeout of %is)",
|
||||
pt_sec, pt_sec == t_sec ? "same as" : "longer than", t_sec);
|
||||
(void) watchdog_get_pretimeout();
|
||||
} else
|
||||
r = watchdog_set_pretimeout();
|
||||
|
||||
if (watchdog_pretimeout == 0)
|
||||
log_info("Watchdog pretimeout is disabled.");
|
||||
else
|
||||
log_info("Watchdog running with a pretimeout of %s.", FORMAT_TIMESPAN(watchdog_pretimeout, 0));
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int update_timeout(void) {
|
||||
int r;
|
||||
|
||||
@@ -121,6 +190,12 @@ static int update_timeout(void) {
|
||||
return log_error_errno(r, "Failed to query watchdog HW timeout: %m");
|
||||
}
|
||||
|
||||
/* If the watchdog timeout was changed, the pretimeout could have been
|
||||
* changed as well by the driver or the kernel so we need to update the
|
||||
* pretimeout now. Or if the watchdog is being configured for the first
|
||||
* time, we want to configure the pretimeout before it is enabled. */
|
||||
(void) update_pretimeout();
|
||||
|
||||
r = watchdog_set_enable(true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
@@ -210,9 +285,31 @@ int watchdog_setup(usec_t timeout) {
|
||||
return r;
|
||||
}
|
||||
|
||||
usec_t watchdog_runtime_wait(void) {
|
||||
int watchdog_setup_pretimeout(usec_t timeout) {
|
||||
/* timeout=0 disables the pretimeout whereas timeout=USEC_INFINITY is a nop. */
|
||||
if ((watchdog_fd >= 0 && timeout == watchdog_pretimeout) || timeout == USEC_INFINITY)
|
||||
return 0;
|
||||
|
||||
if (!timestamp_is_set(watchdog_timeout))
|
||||
/* Initialize the watchdog timeout with the caller value. This value is
|
||||
* going to be updated by update_pretimeout() with the running value,
|
||||
* even if it fails to update the timeout. */
|
||||
watchdog_pretimeout = timeout;
|
||||
|
||||
return update_pretimeout();
|
||||
}
|
||||
|
||||
static usec_t calc_timeout(void) {
|
||||
/* Calculate the effective timeout which accounts for the watchdog
|
||||
* pretimeout if configured and supported. */
|
||||
if (timestamp_is_set(watchdog_pretimeout) && watchdog_timeout >= watchdog_pretimeout)
|
||||
return watchdog_timeout - watchdog_pretimeout;
|
||||
else
|
||||
return watchdog_timeout;
|
||||
}
|
||||
|
||||
usec_t watchdog_runtime_wait(void) {
|
||||
usec_t timeout = calc_timeout();
|
||||
if (!timestamp_is_set(timeout))
|
||||
return USEC_INFINITY;
|
||||
|
||||
/* Sleep half the watchdog timeout since the last successful ping at most */
|
||||
@@ -220,14 +317,14 @@ usec_t watchdog_runtime_wait(void) {
|
||||
usec_t ntime = now(clock_boottime_or_monotonic());
|
||||
|
||||
assert(ntime >= watchdog_last_ping);
|
||||
return usec_sub_unsigned(watchdog_last_ping + (watchdog_timeout / 2), ntime);
|
||||
return usec_sub_unsigned(watchdog_last_ping + (timeout / 2), ntime);
|
||||
}
|
||||
|
||||
return watchdog_timeout / 2;
|
||||
return timeout / 2;
|
||||
}
|
||||
|
||||
int watchdog_ping(void) {
|
||||
usec_t ntime;
|
||||
usec_t ntime, timeout;
|
||||
|
||||
if (watchdog_timeout == 0)
|
||||
return 0;
|
||||
@@ -237,12 +334,13 @@ int watchdog_ping(void) {
|
||||
return open_watchdog();
|
||||
|
||||
ntime = now(clock_boottime_or_monotonic());
|
||||
timeout = calc_timeout();
|
||||
|
||||
/* Never ping earlier than watchdog_timeout/4 and try to ping
|
||||
* by watchdog_timeout/2 plus scheduling latencies the latest */
|
||||
* by watchdog_timeout/2 plus scheduling latencies at the latest */
|
||||
if (timestamp_is_set(watchdog_last_ping)) {
|
||||
assert(ntime >= watchdog_last_ping);
|
||||
if ((ntime - watchdog_last_ping) < (watchdog_timeout / 4))
|
||||
if ((ntime - watchdog_last_ping) < (timeout / 4))
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
int watchdog_set_device(const char *path);
|
||||
int watchdog_setup(usec_t timeout);
|
||||
int watchdog_setup_pretimeout(usec_t usec);
|
||||
int watchdog_ping(void);
|
||||
void watchdog_close(bool disarm);
|
||||
usec_t watchdog_runtime_wait(void);
|
||||
|
||||
Reference in New Issue
Block a user