Skip site navigation (1)Skip section navigation (2)
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>