Date: Tue, 12 Feb 2013 01:37:34 +0000 (UTC) From: Alfred Perlstein <alfred@FreeBSD.org> To: src-committers@freebsd.org, svn-src-user@freebsd.org Subject: svn commit: r246701 - in user/alfred/ewatchdog/sys: dev/watchdog sys Message-ID: <201302120137.r1C1bYbC027634@svn.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: alfred Date: Tue Feb 12 01:37:33 2013 New Revision: 246701 URL: http://svnweb.freebsd.org/changeset/base/246701 Log: Support for software pre-watchdog timeout. This allows a software setting for the kernel to panic, enter debugger or log some time before the watchdog fires. Modified: user/alfred/ewatchdog/sys/dev/watchdog/watchdog.c user/alfred/ewatchdog/sys/sys/watchdog.h Modified: user/alfred/ewatchdog/sys/dev/watchdog/watchdog.c ============================================================================== --- user/alfred/ewatchdog/sys/dev/watchdog/watchdog.c Mon Feb 11 23:33:50 2013 (r246700) +++ user/alfred/ewatchdog/sys/dev/watchdog/watchdog.c Tue Feb 12 01:37:33 2013 (r246701) @@ -1,5 +1,8 @@ /*- * Copyright (c) 2004 Poul-Henning Kamp + * Copyright (c) 2013 iXsystems.com, + * author: Alfred Perlstein <alfred@freebsd.org> + * * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,18 +32,26 @@ __FBSDID("$FreeBSD$"); #include <sys/param.h> +#include <sys/types.h> #include <sys/systm.h> #include <sys/conf.h> #include <sys/uio.h> #include <sys/kernel.h> #include <sys/malloc.h> #include <sys/module.h> +#include <sys/syslog.h> #include <sys/watchdog.h> #include <sys/bus.h> #include <machine/bus.h> #include <sys/syscallsubr.h> /* kern_clock_gettime() */ +static int wd_set_pretimeout(int newtimeout, int disableiftoolong); + +static struct callout wd_pretimeo_handle; +static int wd_pretimeout; +static int wd_pretimeout_act; + static struct cdev *wd_dev; static volatile u_int wd_last_u; /* last timeout value set by kern_do_pat */ @@ -80,6 +91,7 @@ kern_do_pat(u_int utim) error = EOPNOTSUPP; } EVENTHANDLER_INVOKE(watchdog_list, utim, &error); + wd_set_pretimeout(wd_pretimeout, true); /* * If we were able to arm/strobe the watchdog, then * update the last time it was strobed for WDIOC_GETTIMELEFT @@ -132,6 +144,67 @@ wd_get_time_left(struct thread *td, time return (0); } +static void +wd_pretimeout_cb(void *arg __unused) +{ + + switch (wd_pretimeout_act) { + case WD_PRE_PANIC: + panic("watchdog pre-timeout, WD_PRE_PANIC set"); + break; +#ifdef DDB + case WD_PRE_DDB: + debugger(); + break; +#endif + case WD_PRE_LOG: + log(LOG_EMERG, "watchdog pre-timeout, WD_PRE_LOG"); + break; + default: + panic("watchdog: unexpected wd_pretimeout_act %d", + wd_pretimeout_act); + } +} + +/* + * Called to manage timeouts. + * newtimeout needs to be in the range of 0 to actual watchdog timeout. + * if 0, we disable the pre-timeout. + * otherwise we set the pre-timeout provided it's not greater than the + * current actual watchdog timeout. + */ +static int +wd_set_pretimeout(int newtimeout, int disableiftoolong) +{ + u_int utime; + + utime = wdog_kern_last_timeout(); + /* do not permit a pre-timeout >= than the timeout. */ + if (newtimeout >= utime) { + /* + * If 'disableiftoolong' then just fall through + * so as to disable the pre-watchdog + */ + if (disableiftoolong) + newtimeout = 0; + else + return EINVAL; + } + + /* disable the pre-timeout */ + if (newtimeout == 0) { + wd_pretimeout = 0; + callout_stop(&wd_pretimeo_handle); + return 0; + } + + /* We determined the value is sane, so reset the callout */ + (void) callout_reset(&wd_pretimeo_handle, hz*(utime - newtimeout), + wd_pretimeout_cb, NULL); + wd_pretimeout = newtimeout; + return 0; +} + static int wd_ioctl(struct cdev *dev __unused, u_long cmd, caddr_t data, int flags __unused, struct thread *td) @@ -143,6 +216,27 @@ wd_ioctl(struct cdev *dev __unused, u_lo error = 0; switch (cmd) { + case WDIOC_SETPRETIMEOUTACT: + u = *(int *)data; + switch (u) { + case WD_PRE_PANIC: +#ifdef DDB + case WD_PRE_DDB: +#endif + case WD_PRE_LOG: + wd_pretimeout_act = u; + break; + default: + error = EINVAL; + break; + } + break; + case WDIOC_GETPRETIMEOUT: + *(int *)data = (int)wd_pretimeout; + break; + case WDIOC_SETPRETIMEOUT: + error = wd_set_pretimeout(*(int *)data, false); + break; case WDIOC_GETTIMELEFT: error = wd_get_time_left(td, &timeleft); if (error) @@ -166,7 +260,6 @@ wd_ioctl(struct cdev *dev __unused, u_lo } return (error); } - u_int wdog_kern_last_timeout(void) @@ -196,10 +289,12 @@ watchdog_modevent(module_t mod __unused, { switch(type) { case MOD_LOAD: + callout_init(&wd_pretimeo_handle, true); wd_dev = make_dev(&wd_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, _PATH_WATCHDOG); return 0; case MOD_UNLOAD: + callout_drain(&wd_pretimeo_handle); destroy_dev(wd_dev); return 0; case MOD_SHUTDOWN: Modified: user/alfred/ewatchdog/sys/sys/watchdog.h ============================================================================== --- user/alfred/ewatchdog/sys/sys/watchdog.h Mon Feb 11 23:33:50 2013 (r246700) +++ user/alfred/ewatchdog/sys/sys/watchdog.h Tue Feb 12 01:37:33 2013 (r246701) @@ -36,6 +36,9 @@ #define WDIOC_SETTIMEOUT _IOW('W', 43, int) #define WDIOC_GETTIMEOUT _IOR('W', 44, int) #define WDIOC_GETTIMELEFT _IOR('W', 45, int) +#define WDIOC_GETPRETIMEOUT _IOR('W', 46, int) +#define WDIOC_SETPRETIMEOUT _IOW('W', 47, int) +#define WDIOC_SETPRETIMEOUTACT _IOW('W', 48, int) #define WD_ACTIVE 0x8000000 /* @@ -80,6 +83,11 @@ #define WD_TO_16SEC 34 #define WD_TO_32SEC 35 +/* action on pre-timeout trigger */ +#define WD_PRE_PANIC 1 /* panic */ +#define WD_PRE_DDB 2 /* enter debugger */ +#define WD_PRE_LOG 3 /* log(9) */ + #ifdef _KERNEL #include <sys/eventhandler.h>
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201302120137.r1C1bYbC027634>