make SW_WATCHDOG dynamic

Enable the hardclock-based watchdog previously conditional on the
SW_WATCHDOG option whenever hardware watchdogs are not found, and
watchdogd attempts to enable the watchdog. The SW_WATCHDOG option
still causes the sofware watchdog to be enabled even if there is a
hardware watchdog. This does not change the other software-based
watchdog enabled by the --softtimeout option to watchdogd.

Note that the code to reprime the watchdog during kernel core dumps is
no longer conditional on SW_WATCHDOG. I think this was previously a bug.

Reviewed by:	imp alfred bjk
MFC after:	1 week
Relnotes:	yes
Differential Revision:	https://reviews.freebsd.org/D13713
This commit is contained in:
Mike Karels 2018-01-03 00:56:30 +00:00
parent f68e716ffe
commit d626b50b9d
6 changed files with 102 additions and 25 deletions

View File

@ -25,7 +25,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd December 21, 2009
.Dd January 2, 2018
.Dt WATCHDOG 4
.Os
.Sh NAME
@ -40,8 +40,11 @@ facility is used for controlling hardware and software watchdogs.
.Pp
The device
.Pa /dev/fido
responds to a single
supports several optional
.Xr ioctl 2
calls for configuration, and
responds to a single operational
.Xr ioctl
call,
.Dv WDIOCPATPAT .
It takes a single argument which represents a timeout value specified as a
@ -60,12 +63,16 @@ indicates that the
will be kept from timing out from the kernel.
.Pp
The
.Dv WDIOCPATPAT
.Xr ioctl 2
call will return success if just one of the available
.Xr watchdog 9
implementations supports setting the timeout to the specified timeout.
This
means that at least one watchdog is armed.
By default, this will be a hardware watchdog if one is present, but if
no hardware watchdog is able to process the request, a default software
watchdog is enabled.
If the call fails, for instance if
none of
.Xr watchdog 9
@ -77,8 +84,53 @@ To disable the watchdogs pass
If disarming the watchdog(s) failed an error is returned.
The watchdog might
still be armed!
.Pp
The optional configuration
.Xr ioctl
commands are listed here, along with the type of the parameter used.
Examples of their use can be found in
.Xr watchdogd 8 .
.Bl -tag -width "WDIOC_SETSOFTTIMEOUTACT int "
.It Dv WDIOC_SETTIMEOUT Fa int
set/reset the timer
.It Dv WDIOC_GETTIMEOUT Fa int
get total timeout
.It Dv WDIOC_GETTIMELEFT Fa int
get time left
.It Dv WDIOC_GETPRETIMEOUT Fa int
get the pre-timeout
.It Dv WDIOC_SETPRETIMEOUT Fa int
set the pre-timeout
.It Dv WDIOC_SETPRETIMEOUTACT Fa int
Set the action when a pre-timeout occurs (see
.Li WD_SOFT_*
below).
.It Dv WDIOC_SETSOFT Fa int
Use an internal software watchdog instead of hardware.
There is also an external software watchdog, which is used by default
if no hardware watchdog was attached.
.It Dv WDIOC_SETSOFTTIMEOUTACT Fa int
Set the action whan a soft timeout occurs.
.El
.Pp
The actions that may be specified for the pre-timeout or the internal software
watchdog are listed here.
Multiple actions can be specified by ORing values together.
.Bl -tag -width WD_SOFT_PRINT
.It Dv WD_SOFT_PANIC
panic
.It Dv WD_SOFT_DDB
enter debugger
.It Dv WD_SOFT_LOG
log(9)
.It Dv WD_SOFT_PRINT
printf(9)
.El
.Sh RETURN VALUES
The ioctl returns zero on success and non-zero on failure.
The
.Dv WDIOCPATPAT
.Xr ioctl
returns zero on success and non-zero on failure.
.Bl -tag -width Er
.It Bq Er EOPNOTSUPP
No watchdog present in the kernel or
@ -89,6 +141,10 @@ Watchdog could not be disabled (timeout value of 0).
.It Bq Er EINVAL
Invalid flag combination passed.
.El
.Pp
The configuration
.Xr ioctl
operations return zero on success and non-zero on failure.
.Sh EXAMPLES
.Bd -literal -offset indent
#include <paths.h>
@ -122,8 +178,10 @@ Enables a watchdog to recover from a potentially freezing piece of code.
.Pp
.Dl "options SW_WATCHDOG"
.Pp
in your kernel config adds a software watchdog in the kernel, dropping to KDB
or panic-ing when firing.
in your kernel config forces a software watchdog in the kernel
to be configured even if a hardware watchdog is configured,
dropping to KDB or panicking when firing, depending
on the KDB and KDB_UNATTENDED kernel configuration options.
.Sh SEE ALSO
.Xr watchdogd 8 ,
.Xr watchdog 9

View File

@ -2609,7 +2609,9 @@ options BOOTP_WIRED_TO=fxp0 # Use interface fxp0 for BOOTP
options BOOTP_BLOCKSIZE=8192 # Override NFS block size
#
# Add software watchdog routines.
# Enable software watchdog routines, even if hardware watchdog is present.
# By default, software watchdog timer is enabled only if no hardware watchdog
# is present.
#
options SW_WATCHDOG

View File

@ -78,6 +78,9 @@ SYSCTL_UINT(_hw_watchdog, OID_AUTO, wd_last_u_secs, CTLFLAG_RD,
static int wd_lastpat_valid = 0;
static time_t wd_lastpat = 0; /* when the watchdog was last patted */
/* Hook for external software watchdog to register for use if needed */
void (*wdog_software_attach)(void);
static void
pow2ns_to_ts(int pow2ns, struct timespec *ts)
{
@ -120,6 +123,7 @@ int
wdog_kern_pat(u_int utim)
{
int error;
static int first = 1;
if ((utim & WD_LASTVAL) != 0 && (utim & WD_INTERVAL) > 0)
return (EINVAL);
@ -161,6 +165,17 @@ wdog_kern_pat(u_int utim)
} else {
EVENTHANDLER_INVOKE(watchdog_list, utim, &error);
}
/*
* If we no hardware watchdog responded, we have not tried to
* attach an external software watchdog, and one is available,
* attach it now and retry.
*/
if (error == EOPNOTSUPP && first && *wdog_software_attach != NULL) {
(*wdog_software_attach)();
EVENTHANDLER_INVOKE(watchdog_list, utim, &error);
}
first = 0;
wd_set_pretimeout(wd_pretimeout, true);
/*
* If we were able to arm/strobe the watchdog, then

View File

@ -335,14 +335,18 @@ read_cpu_time(long *cp_time)
}
}
#ifdef SW_WATCHDOG
#include <sys/watchdog.h>
static int watchdog_ticks;
static int watchdog_enabled;
static void watchdog_fire(void);
static void watchdog_config(void *, u_int, int *);
#endif /* SW_WATCHDOG */
static void
watchdog_attach(void)
{
EVENTHANDLER_REGISTER(watchdog_list, watchdog_config, NULL, 0);
}
/*
* Clock handling routines.
@ -410,8 +414,14 @@ initclocks(void *dummy)
if (profhz == 0)
profhz = i;
psratio = profhz / i;
#ifdef SW_WATCHDOG
EVENTHANDLER_REGISTER(watchdog_list, watchdog_config, NULL, 0);
/* Enable hardclock watchdog now, even if a hardware watchdog exists. */
watchdog_attach();
#else
/* Volunteer to run a software watchdog. */
if (wdog_software_attach == NULL)
wdog_software_attach = watchdog_attach;
#endif
}
@ -482,10 +492,8 @@ hardclock(int usermode, uintfptr_t pc)
#ifdef DEVICE_POLLING
hardclock_device_poll(); /* this is very short and quick */
#endif /* DEVICE_POLLING */
#ifdef SW_WATCHDOG
if (watchdog_enabled > 0 && --watchdog_ticks <= 0)
watchdog_fire();
#endif /* SW_WATCHDOG */
}
void
@ -496,9 +504,7 @@ hardclock_cnt(int cnt, int usermode)
struct proc *p = td->td_proc;
int *t = DPCPU_PTR(pcputicks);
int flags, global, newticks;
#ifdef SW_WATCHDOG
int i;
#endif /* SW_WATCHDOG */
/*
* Update per-CPU and possibly global ticks values.
@ -558,13 +564,11 @@ hardclock_cnt(int cnt, int usermode)
atomic_store_rel_int(&devpoll_run, 0);
}
#endif /* DEVICE_POLLING */
#ifdef SW_WATCHDOG
if (watchdog_enabled > 0) {
i = atomic_fetchadd_int(&watchdog_ticks, -newticks);
if (i > 0 && i <= newticks)
watchdog_fire();
}
#endif /* SW_WATCHDOG */
}
if (curcpu == CPU_FIRST())
cpu_tick_calibration();
@ -841,8 +845,6 @@ SYSCTL_PROC(_kern, KERN_CLOCKRATE, clockrate,
0, 0, sysctl_kern_clockrate, "S,clockinfo",
"Rate and period of various kernel clocks");
#ifdef SW_WATCHDOG
static void
watchdog_config(void *unused __unused, u_int cmd, int *error)
{
@ -891,5 +893,3 @@ watchdog_fire(void)
panic("watchdog timeout");
#endif
}
#endif /* SW_WATCHDOG */

View File

@ -27,8 +27,6 @@
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include "opt_watchdog.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/conf.h>
@ -36,9 +34,7 @@ __FBSDID("$FreeBSD$");
#include <sys/kernel.h>
#include <sys/proc.h>
#include <sys/kerneldump.h>
#ifdef SW_WATCHDOG
#include <sys/watchdog.h>
#endif
#include <vm/vm.h>
#include <vm/vm_param.h>
#include <vm/pmap.h>
@ -205,9 +201,7 @@ dumpsys_cb_dumpdata(struct dump_pa *mdp, int seqnr, void *arg)
}
dumpsys_map_chunk(pa, chunk, &va);
#ifdef SW_WATCHDOG
wdog_kern_pat(WD_LASTVAL);
#endif
error = dump_append(di, va, 0, sz);
dumpsys_unmap_chunk(pa, chunk, va);

View File

@ -112,6 +112,14 @@ EVENTHANDLER_DECLARE(watchdog_list, watchdog_fn);
u_int wdog_kern_last_timeout(void);
int wdog_kern_pat(u_int utim);
/*
* The following function pointer is used to attach a software watchdog
* if no hardware watchdog has been attached, and if the software module
* has initialized the function pointer.
*/
extern void (*wdog_software_attach)(void);
#endif
#endif /* _SYS_WATCHDOG_H */