Align the interfaces for the various watchdogs and make the interface

behave as expected.

Also:
- Return an error if WD_PASSIVE is passed in to the ioctl as only
  WD_ACTIVE is implemented at the moment. See sys/watchdog.h for an
  explanation of the difference between WD_ACTIVE and WD_PASSIVE.
- Remove the I_HAVE_TOTALLY_LOST_MY_SENSE_OF_HUMOR define. If you've
  lost your sense of humor, than don't add a define.

Specific changes:

i80321_wdog.c
  Don't roll your own passive watchdog tickle as this would defeat the
  purpose of an active (userland) watchdog tickle.

ichwd.c / ipmi.c:
  WD_ACTIVE means active patting of the watchdog by a userland process,
  not whether the watchdog is active. See sys/watchdog.h.

kern_clock.c:
  (software watchdog) Remove a check for WD_ACTIVE as this does not make
  sense here. This reverts r1.181.
This commit is contained in:
n_hibma 2006-12-15 21:44:49 +00:00
parent 705f242eca
commit c98f016084
12 changed files with 138 additions and 104 deletions

View File

@ -516,6 +516,7 @@ MLINKS+=wb.4 if_wb.4
MLINKS+=wi.4 if_wi.4
MLINKS+=xe.4 if_xe.4
MLINKS+=xl.4 if_xl.4
MLINKS+=watchdog.4 SW_WATCHDOG.4
.if ${MACHINE_ARCH} == "amd64" || ${MACHINE_ARCH} == "i386"
_acpi_dock.4= acpi_dock.4

View File

@ -32,54 +32,95 @@
.Nm watchdog
.Nd "hardware and software watchdog"
.Sh SYNOPSIS
.Cd "options CPU_ELAN"
.Cd "options CPU_GEODE"
.Cd "options SW_WATCHDOG"
.Pp
.In sys/watchdog.h
.Sh DESCRIPTION
The
.Nm
facility is used for controlling hardware and software watchdogs.
.Pp
The interface is through a device
.Pa /dev/fido
which responds to a single
responds to a single
.Xr ioctl 2
call,
.Dv WDIOCPATPAT .
It takes a single argument which represents a timeout value specified as a
power of two nanoseconds, or-ed with a flag selecting active or passive control
of the watchdog.
.Pp
The call takes a single argument which represents a timeout value
specified as an integer power of two nanoseconds.
.Pp
The
.Dv WD_ACTIVE
flag signals that the
indicates that the
.Nm
will be kept from
timing out from userland, for instance by the
will be kept from timing out from userland, for instance by the
.Xr watchdogd 8
daemon.
.Pp
To disable the watchdogs, an argument of zero should be used.
.Dv WD_PASSIVE
indicates that the
.Nm
will be kept from timing out from the kernel.
.Pp
The
.Xr ioctl 2
call will return success if just one of the available
.Xr watchdog 9
implementations support the request.
If the call fails, for instance if none of
implementations supports setting the timeout to the specified timeout. This
means that at least one watchdog is armed. If the call fails, for instance if
none of
.Xr watchdog 9
implementations support the timeout
length, all watchdogs are disabled and must be explicitly re-enabled.
implementations support the timeout length, all watchdogs are disabled and must
be explicitly re-enabled.
.Pp
To disable the watchdogs pass
.Dv WD_TO_NEVER .
If disarming the watchdog(s) failed an error is returned. The watchdog might
still be armed!
.Sh RETURN VALUES
The ioctl returns zero on success and non-zero on failure.
.Bl -tag -width Er
.It Bq Er EOPNOTSUPP
No watchdog present in the kernel (timeout value other than 0).
.It Bq Er EOPNOTSUPP
Watchdog could not be disabled (timeout value of 0).
.It Bq Er EINVALID
Invalid flag combination passed.
.It Bq Er EINVALID
None of the watchdogs supports the requested timeout value.
.Sh EXAMPLES
.\" XXX insert some descriptive text here
.Bd -literal -offset indent
u_int u = WD_ACTIVE | WD_TO_8SEC;
int fd = open("/dev/fido", O_RDWR);
#include <paths.h>
#include <sys/watchdog.h>
ioctl(fd, WDIOCPATPAT, &u);
#define WDPATH "/dev/" _PATH_WATCHDOG
int wdfd = -1;
static void
wd_init(void)
{
wdfd = open(WDPATH, O_RDWR);
if (wdfd == -1)
err(1, WDPATH);
}
static void
wd_reset(u_int timeout)
{
if (ioctl(wdfd, WDIOCPATPAT, &timeout) == -1)
err(1, "WDIOCPATPAT");
}
/* in main() */
wd_init();
wd_reset(WD_ACTIVE|WD_TO_8SEC);
/* potential freeze point */
wd_reset(WD_TO_NEVER);
.Ed
.Pp
Enables a watchdog to recover from a potentially freezing piece of code.
.Pp
.Bd -literal -offset indent
options SW_WATCHDOG
.Ed
.Pp
in your kernel config adds a software watchdog in the kernel, dropping to KDB
or panic-ing when firing.
.Sh SEE ALSO
.Xr watchdogd 8 ,
.Xr watchdog 9
@ -88,14 +129,17 @@ The
.Nm
code first appeared in
.Fx 5.1 .
.Sh BUGS
The
.Dv WD_PASSIVE
option has not yet been implemented.
.Sh AUTHORS
.An -nosplit
The
.Nm
facility was written by
.An Poul-Henning Kamp Aq phk@FreeBSD.org .
The software watchdog code
and this manual page were written by
The software watchdog code and this manual page were written by
.An Sean Kelly Aq smkelly@FreeBSD.org .
Some contributions were made by
.An Jeff Roberson Aq jeff@FreeBSD.org .

View File

@ -35,6 +35,7 @@
.Ft void
.Fn watchdog_fn "void *private" "u_int cmd" "int *error"
.Fn EVENTHANDLER_REGISTER watchdog_list watchdog_fn private 0
.Fn EVENTHANDLER_DEREGISTER watchdog_list eventhandler_tag
.Sh DESCRIPTION
To implement a watchdog in software or hardware, only a single
function needs to be written and registered on the global
@ -60,7 +61,12 @@ argument be set to zero.
If the watchdog cannot be configured to the proposed timeout, it
must be disabled and the
.Fa error
argument left untouched.
argument set to
.Dv EINVAL .
If the watchdog cannot be disabled, the
.Fa error
argument must be set to
.Dv EOPNOTSUPP .
.Pp
There is no specification of what the watchdog should do when it
times out, but a hardware reset or similar

View File

@ -170,10 +170,15 @@ at91st_watchdog(void *argp, u_int cmd, int *error)
uint32_t wdog;
int t;
wdog = 0;
t = cmd & WD_INTERVAL;
if (cmd != 0 && t >= 22 && t <= 37)
if (cmd > 0 && t >= 22 && t <= 37) {
wdog = (1 << (t - 22)) | ST_WDMR_RSTEN;
*error = 0;
} else {
wdog = 0;
if (cmd > 0)
*error = EINVAL;
}
WR4(ST_WDMR, wdog);
WR4(ST_CR, ST_CR_WDRST);
}

View File

@ -62,7 +62,6 @@ struct iopwdog_softc {
device_t dev;
int armed;
int wdog_period;
struct callout_handle wdog_callout;
};
static __inline void
@ -83,8 +82,6 @@ iopwdog_tickle(void *arg)
return;
wdtcr_write(WDTCR_ENABLE1);
wdtcr_write(WDTCR_ENABLE2);
sc->wdog_callout = timeout(iopwdog_tickle, sc,
hz * (sc->wdog_period - 1));
}
static int
@ -112,14 +109,21 @@ iopwdog_watchdog_fn(void *private, u_int cmd, int *error)
{
struct iopwdog_softc *sc = private;
if (cmd == 0)
return;
if ((((uint64_t)1 << (cmd & WD_INTERVAL))) >
(uint64_t)sc->wdog_period * 1000000000)
return;
sc->armed = 1;
iopwdog_tickle(sc);
*error = 0;
cmd &= WD_INTERVAL;
if (cmd > 0 && cmd <= 63
&& (uint64_t)1 << (cmd & WD_INTERVAL) <=
(uint64_t)sc->wdog_period * 1000000000) {
/* Valid value -> Enable watchdog */
iopwdog_tickle(sc);
sc->armed = 1;
*error = 0;
} else {
/* XXX Can't disable this watchdog? */
if (sc->armed)
*error = EOPNOTSUPP;
else if (cmd > 0)
*error = EINVAL;
}
}
static int

View File

@ -178,38 +178,22 @@ ichwd_event(void *arg, unsigned int cmd, int *error)
struct ichwd_softc *sc = arg;
unsigned int timeout;
/* convert from power-of-two-ns to WDT ticks */
cmd &= WD_INTERVAL;
timeout = ((uint64_t)1 << cmd) / ICHWD_TICK;
if (cmd > 0 && cmd <= 63
&& timeout >= ICHWD_MIN_TIMEOUT && timeout <= ICHWD_MAX_TIMEOUT) {
if (timeout != sc->timeout)
ichwd_tmr_set(sc, timeout);
/* disable / enable */
if (!(cmd & WD_ACTIVE)) {
ichwd_tmr_reload(sc);
*error = 0;
} else {
if (sc->active)
ichwd_tmr_disable(sc);
*error = 0;
return;
if (cmd > 0)
*error = EINVAL;
}
if (!sc->active)
ichwd_tmr_enable(sc);
cmd &= WD_INTERVAL;
/* convert from power-of-to-ns to WDT ticks */
if (cmd >= 64) {
*error = EINVAL;
return;
}
timeout = ((uint64_t)1 << cmd) / ICHWD_TICK;
if (timeout < ICHWD_MIN_TIMEOUT || timeout > ICHWD_MAX_TIMEOUT) {
*error = EINVAL;
return;
}
/* set new initial value */
if (timeout != sc->timeout)
ichwd_tmr_set(sc, timeout);
/* reload */
ichwd_tmr_reload(sc);
*error = 0;
return;
}
static unsigned int pmbase = 0;
@ -332,8 +316,6 @@ ichwd_detach(device_t dev)
{
struct ichwd_softc *sc;
device_printf(dev, "detaching\n");
sc = device_get_softc(dev);
/* halt the watchdog timer */

View File

@ -649,25 +649,16 @@ ipmi_wd_event(void *arg, unsigned int cmd, int *error)
struct ipmi_softc *sc = arg;
unsigned int timeout;
/* disable / enable */
if (!(cmd & WD_ACTIVE)) {
ipmi_set_watchdog(sc, 0);
*error = 0;
return;
}
cmd &= WD_INTERVAL;
/* convert from power-of-to-ns to WDT ticks */
if (cmd >= 64) {
*error = EINVAL;
return;
if (cmd > 0 && cmd <= 63) {
timeout = ((uint64_t)1 << cmd) / 1800000000;
ipmi_set_watchdog(sc, timeout);
*error = 0;
} else {
ipmi_set_watchdog(sc, 0);
if (cmd > 0)
*error = 0;
}
timeout = ((uint64_t)1 << cmd) / 1800000000;
/* reload */
ipmi_set_watchdog(sc, timeout);
*error = 0;
}
#ifdef CLONING

View File

@ -297,7 +297,7 @@ mk48txx_watchdog(void *arg, u_int cmd, int *error)
wdog = 0;
t = cmd & WD_INTERVAL;
if (cmd != 0 && t >= 26 && t <= 37) {
if (cmd > 0 && t >= 26 && t <= 37) {
if (t <= WD_TO_2SEC) {
wdog |= MK48TXX_WDOG_RB_1_16;
t -= 26;
@ -317,6 +317,8 @@ mk48txx_watchdog(void *arg, u_int cmd, int *error)
if (sc->sc_flag & MK48TXX_WDOG_ENABLE_WDS)
wdog |= MK48TXX_WDOG_WDS;
*error = 0;
} else if (cmd > 0) {
*error = EINVAL;
}
mtx_lock(&sc->sc_mtx);
(*sc->sc_nvwr)(dev, sc->sc_clkoffset + MK48TXX_WDOG, wdog);

View File

@ -55,11 +55,14 @@ wd_ioctl(struct cdev *dev __unused, u_long cmd, caddr_t data,
return (EINVAL);
if ((u & (WD_ACTIVE | WD_PASSIVE)) == (WD_ACTIVE | WD_PASSIVE))
return (EINVAL);
if (u & WD_PASSIVE)
return (ENOSYS); /* XXX Not implemented yet */
if ((u & WD_INTERVAL) == WD_TO_NEVER) {
u = 0;
/* Assume all is well; watchdog signals failure. */
error = 0;
} else {
/* Assume no watchdog available; watchdog flags success */
error = EOPNOTSUPP;
}
EVENTHANDLER_INVOKE(watchdog_list, u, &error);

View File

@ -367,11 +367,11 @@ init_AMD_Elan_sc520(void)
static void
elan_watchdog(void *foo __unused, u_int spec, int *error)
{
u_int u, v;
u_int u, v, w;
static u_int cur;
u = spec & WD_INTERVAL;
if (spec && u <= 35) {
if (u > 0 && u <= 35) {
u = imax(u - 5, 24);
v = 2 << (u - 24);
v |= 0xc000;
@ -383,7 +383,7 @@ elan_watchdog(void *foo __unused, u_int spec, int *error)
* for other reasons. Save and restore the GP echo mode
* around our hardware tom-foolery.
*/
u = elan_mmcr->GPECHO;
w = elan_mmcr->GPECHO;
elan_mmcr->GPECHO = 0;
if (v != cur) {
/* Clear the ENB bit */
@ -401,19 +401,19 @@ elan_watchdog(void *foo __unused, u_int spec, int *error)
elan_mmcr->WDTMRCTL = 0xaaaa;
elan_mmcr->WDTMRCTL = 0x5555;
}
elan_mmcr->GPECHO = u;
elan_mmcr->GPECHO = w;
*error = 0;
return;
} else {
u = elan_mmcr->GPECHO;
w = elan_mmcr->GPECHO;
elan_mmcr->GPECHO = 0;
elan_mmcr->WDTMRCTL = 0x3333;
elan_mmcr->WDTMRCTL = 0xcccc;
elan_mmcr->WDTMRCTL = 0x4080;
elan_mmcr->WDTMRCTL = u;
elan_mmcr->GPECHO = u;
elan_mmcr->WDTMRCTL = w; /* XXX What does this statement do? */
elan_mmcr->GPECHO = w;
cur = 0;
return;
if (u > 0)
*error = 0;
}
}

View File

@ -536,15 +536,15 @@ SYSCTL_PROC(_kern, KERN_CLOCKRATE, clockrate, CTLTYPE_STRUCT|CTLFLAG_RD,
#ifdef SW_WATCHDOG
static void
watchdog_config(void *unused __unused, u_int cmd, int *err)
watchdog_config(void *unused __unused, u_int cmd, int *error)
{
u_int u;
u = cmd & WD_INTERVAL;
if ((cmd & WD_ACTIVE) && u >= WD_TO_1SEC) {
if (u >= WD_TO_1SEC) {
watchdog_ticks = (1 << (u - WD_TO_1SEC)) * hz;
watchdog_enabled = 1;
*err = 0;
*error = 0;
} else {
watchdog_enabled = 0;
}

View File

@ -30,11 +30,7 @@
#include <sys/ioccom.h>
#ifdef I_HAVE_TOTALLY_LOST_MY_SENSE_OF_HUMOUR
#define _PATH_WATCHDOG "watchdog"
#else
#define _PATH_WATCHDOG "fido"
#endif
#define WDIOCPATPAT _IOW('W', 42, u_int)