summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
authorattilio <attilio@FreeBSD.org>2013-02-28 00:24:13 +0000
committerattilio <attilio@FreeBSD.org>2013-02-28 00:24:13 +0000
commitbcc31f462ba76b89e3789cb24393126466e3775d (patch)
tree1d327faf4ac955ce717c24887b97073fcbb8ef2c /sys
parent9c0acdc6918f8dc4e438e9eacfea645d87119a06 (diff)
parent98a0da7498845f515692f5d3913c7369c4740cfe (diff)
downloadFreeBSD-src-bcc31f462ba76b89e3789cb24393126466e3775d.zip
FreeBSD-src-bcc31f462ba76b89e3789cb24393126466e3775d.tar.gz
MFC
Diffstat (limited to 'sys')
-rw-r--r--sys/boot/common/load_elf.c2
-rw-r--r--sys/dev/mfi/mfi.c16
-rw-r--r--sys/dev/watchdog/watchdog.c242
-rw-r--r--sys/netinet/sctputil.c3
-rw-r--r--sys/sys/watchdog.h25
5 files changed, 262 insertions, 26 deletions
diff --git a/sys/boot/common/load_elf.c b/sys/boot/common/load_elf.c
index d712b3d..3a4152e 100644
--- a/sys/boot/common/load_elf.c
+++ b/sys/boot/common/load_elf.c
@@ -304,7 +304,7 @@ __elfN(loadimage)(struct preloaded_file *fp, elf_file_t ef, u_int64_t off)
* only adjust the entry point if it's a virtual address to begin with.
*/
off = -0xc0000000u;
- if ((ehdr->e_entry & 0xc0000000u) == 0xc000000u)
+ if ((ehdr->e_entry & 0xc0000000u) == 0xc0000000u)
ehdr->e_entry += off;
#ifdef ELF_VERBOSE
printf("ehdr->e_entry 0x%08x, va<->pa off %llx\n", ehdr->e_entry, off);
diff --git a/sys/dev/mfi/mfi.c b/sys/dev/mfi/mfi.c
index e799b9d..e2401f6 100644
--- a/sys/dev/mfi/mfi.c
+++ b/sys/dev/mfi/mfi.c
@@ -157,6 +157,11 @@ SYSCTL_INT(_hw_mfi, OID_AUTO, polled_cmd_timeout, CTLFLAG_RWTUN,
&mfi_polled_cmd_timeout, 0,
"Polled command timeout - used for firmware flash etc (in seconds)");
+static int mfi_cmd_timeout = MFI_CMD_TIMEOUT;
+TUNABLE_INT("hw.mfi.cmd_timeout", &mfi_cmd_timeout);
+SYSCTL_INT(_hw_mfi, OID_AUTO, cmd_timeout, CTLFLAG_RWTUN, &mfi_cmd_timeout,
+ 0, "Command timeout (in seconds)");
+
/* Management interface */
static d_open_t mfi_open;
static d_close_t mfi_close;
@@ -782,7 +787,7 @@ mfi_attach(struct mfi_softc *sc)
/* Start the timeout watchdog */
callout_init(&sc->mfi_watchdog_callout, CALLOUT_MPSAFE);
- callout_reset(&sc->mfi_watchdog_callout, MFI_CMD_TIMEOUT * hz,
+ callout_reset(&sc->mfi_watchdog_callout, mfi_cmd_timeout * hz,
mfi_timeout, sc);
if (sc->mfi_flags & MFI_FLAGS_TBOLT) {
@@ -3714,7 +3719,7 @@ mfi_dump_all(void)
break;
device_printf(sc->mfi_dev, "Dumping\n\n");
timedout = 0;
- deadline = time_uptime - MFI_CMD_TIMEOUT;
+ deadline = time_uptime - mfi_cmd_timeout;
mtx_lock(&sc->mfi_io_lock);
TAILQ_FOREACH(cm, &sc->mfi_busy, cm_link) {
if (cm->cm_timestamp <= deadline) {
@@ -3745,10 +3750,11 @@ mfi_timeout(void *data)
time_t deadline;
int timedout = 0;
- deadline = time_uptime - MFI_CMD_TIMEOUT;
+ deadline = time_uptime - mfi_cmd_timeout;
if (sc->adpreset == 0) {
if (!mfi_tbolt_reset(sc)) {
- callout_reset(&sc->mfi_watchdog_callout, MFI_CMD_TIMEOUT * hz, mfi_timeout, sc);
+ callout_reset(&sc->mfi_watchdog_callout,
+ mfi_cmd_timeout * hz, mfi_timeout, sc);
return;
}
}
@@ -3785,7 +3791,7 @@ mfi_timeout(void *data)
mtx_unlock(&sc->mfi_io_lock);
- callout_reset(&sc->mfi_watchdog_callout, MFI_CMD_TIMEOUT * hz,
+ callout_reset(&sc->mfi_watchdog_callout, mfi_cmd_timeout * hz,
mfi_timeout, sc);
if (0)
diff --git a/sys/dev/watchdog/watchdog.c b/sys/dev/watchdog/watchdog.c
index e7edf31..71d8ecd 100644
--- a/sys/dev/watchdog/watchdog.c
+++ b/sys/dev/watchdog/watchdog.c
@@ -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,21 +32,40 @@
__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 void wd_timeout_cb(void *arg);
+
+static struct callout wd_pretimeo_handle;
+static int wd_pretimeout;
+static int wd_pretimeout_act = WD_SOFT_LOG;
+
+static struct callout wd_softtimeo_handle;
+static int wd_softtimer; /* true = use softtimer instead of hardware
+ watchdog */
+static int wd_softtimeout_act = WD_SOFT_LOG; /* action for the software timeout */
+
static struct cdev *wd_dev;
-static volatile u_int wd_last_u;
+static volatile u_int wd_last_u; /* last timeout value set by kern_do_pat */
-static int
-kern_do_pat(u_int utim)
+static int wd_lastpat_valid = 0;
+static time_t wd_lastpat = 0; /* when the watchdog was last patted */
+
+int
+wdog_kern_pat(u_int utim)
{
int error;
@@ -51,11 +73,20 @@ kern_do_pat(u_int utim)
return (EINVAL);
if ((utim & WD_LASTVAL) != 0) {
+ /*
+ * if WD_LASTVAL is set, fill in the bits for timeout
+ * from the saved value in wd_last_u.
+ */
MPASS((wd_last_u & ~WD_INTERVAL) == 0);
utim &= ~WD_LASTVAL;
utim |= wd_last_u;
- } else
+ } else {
+ /*
+ * Otherwise save the new interval.
+ * This can be zero (to disable the watchdog)
+ */
wd_last_u = (utim & WD_INTERVAL);
+ }
if ((utim & WD_INTERVAL) == WD_TO_NEVER) {
utim = 0;
@@ -65,18 +96,49 @@ kern_do_pat(u_int utim)
/* Assume no watchdog available; watchdog flags success */
error = EOPNOTSUPP;
}
- EVENTHANDLER_INVOKE(watchdog_list, utim, &error);
+ if (wd_softtimer) {
+ if (utim == 0) {
+ callout_stop(&wd_softtimeo_handle);
+ } else {
+ (void) callout_reset(&wd_softtimeo_handle,
+ hz*utim, wd_timeout_cb, "soft");
+ }
+ error = 0;
+ } else {
+ 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
+ */
+ if (!error) {
+ struct timespec ts;
+
+ error = kern_clock_gettime(curthread /* XXX */,
+ CLOCK_MONOTONIC_FAST, &ts);
+ if (!error) {
+ wd_lastpat = ts.tv_sec;
+ wd_lastpat_valid = 1;
+ }
+ }
return (error);
}
static int
-wd_ioctl(struct cdev *dev __unused, u_long cmd, caddr_t data,
- int flags __unused, struct thread *td)
+wd_valid_act(int act)
+{
+
+ if ((act & ~(WD_SOFT_MASK)) != 0)
+ return false;
+ return true;
+}
+
+static int
+wd_ioctl_patpat(caddr_t data)
{
u_int u;
- if (cmd != WDIOCPATPAT)
- return (ENOIOCTL);
u = *(u_int *)data;
if (u & ~(WD_ACTIVE | WD_PASSIVE | WD_LASTVAL | WD_INTERVAL))
return (EINVAL);
@@ -89,24 +151,162 @@ wd_ioctl(struct cdev *dev __unused, u_long cmd, caddr_t data,
return (ENOSYS); /* XXX Not implemented yet */
u &= ~(WD_ACTIVE | WD_PASSIVE);
- return (kern_do_pat(u));
+ return (wdog_kern_pat(u));
}
-u_int
-wdog_kern_last_timeout(void)
+static int
+wd_get_time_left(struct thread *td, time_t *remainp)
{
+ struct timespec ts;
+ int error;
- return (wd_last_u);
+ error = kern_clock_gettime(td, CLOCK_MONOTONIC_FAST, &ts);
+ if (error)
+ return (error);
+ if (!wd_lastpat_valid)
+ return (ENOENT);
+ *remainp = ts.tv_sec - wd_lastpat;
+ return (0);
}
-int
-wdog_kern_pat(u_int utim)
+static void
+wd_timeout_cb(void *arg)
{
+ const char *type = arg;
- if (utim & ~(WD_LASTVAL | WD_INTERVAL))
- return (EINVAL);
+#ifdef DDB
+ if ((wd_pretimeout_act & WD_SOFT_DDB)) {
+ char kdb_why[80];
+ snprintf(kdb_why, sizeof(buf), "watchdog %s timeout", type);
+ kdb_backtrace();
+ kdb_enter(KDB_WHY_WATCHDOG, kdb_why);
+ }
+#endif
+ if ((wd_pretimeout_act & WD_SOFT_LOG))
+ log(LOG_EMERG, "watchdog %s-timeout, WD_SOFT_LOG", type);
+ if ((wd_pretimeout_act & WD_SOFT_PRINTF))
+ printf("watchdog %s-timeout, WD_SOFT_PRINTF\n", type);
+ if ((wd_pretimeout_act & WD_SOFT_PANIC))
+ panic("watchdog %s-timeout, WD_SOFT_PANIC set", type);
+}
+
+/*
+ * 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_timeout_cb, "pre-timeout");
+ wd_pretimeout = newtimeout;
+ return 0;
+}
- return (kern_do_pat(utim));
+static int
+wd_ioctl(struct cdev *dev __unused, u_long cmd, caddr_t data,
+ int flags __unused, struct thread *td)
+{
+ u_int u;
+ time_t timeleft;
+ int error;
+
+ error = 0;
+
+ switch (cmd) {
+ case WDIOC_SETSOFT:
+ u = *(int *)data;
+ /* do nothing? */
+ if (u == wd_softtimer)
+ break;
+ /* If there is a pending timeout disallow this ioctl */
+ if (wd_last_u != 0) {
+ error = EINVAL;
+ break;
+ }
+ wd_softtimer = u;
+ break;
+ case WDIOC_SETSOFTTIMEOUTACT:
+ u = *(int *)data;
+ if (wd_valid_act(u)) {
+ wd_softtimeout_act = u;
+ } else {
+ error = EINVAL;
+ }
+ break;
+ case WDIOC_SETPRETIMEOUTACT:
+ u = *(int *)data;
+ if (wd_valid_act(u)) {
+ wd_pretimeout_act = u;
+ } else {
+ error = EINVAL;
+ }
+ 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)
+ break;
+ *(int *)data = (int)timeleft;
+ break;
+ case WDIOC_SETTIMEOUT:
+ u = *(u_int *)data;
+ error = wdog_kern_pat(u);
+ break;
+ case WDIOC_GETTIMEOUT:
+ u = wdog_kern_last_timeout();
+ *(u_int *)data = u;
+ break;
+ case WDIOCPATPAT:
+ error = wd_ioctl_patpat(data);
+ break;
+ default:
+ error = ENOIOCTL;
+ break;
+ }
+ return (error);
+}
+
+/*
+ * Return the last timeout set, this is NOT the seconds from NOW until timeout,
+ * rather it is the amount of seconds passed to WDIOCPATPAT/WDIOC_SETTIMEOUT.
+ */
+u_int
+wdog_kern_last_timeout(void)
+{
+
+ return (wd_last_u);
}
static struct cdevsw wd_cdevsw = {
@@ -120,10 +320,16 @@ watchdog_modevent(module_t mod __unused, int type, void *data __unused)
{
switch(type) {
case MOD_LOAD:
+ callout_init(&wd_pretimeo_handle, true);
+ callout_init(&wd_softtimeo_handle, true);
wd_dev = make_dev(&wd_cdevsw, 0,
UID_ROOT, GID_WHEEL, 0600, _PATH_WATCHDOG);
return 0;
case MOD_UNLOAD:
+ callout_stop(&wd_pretimeo_handle);
+ callout_stop(&wd_softtimeo_handle);
+ callout_drain(&wd_pretimeo_handle);
+ callout_drain(&wd_softtimeo_handle);
destroy_dev(wd_dev);
return 0;
case MOD_SHUTDOWN:
diff --git a/sys/netinet/sctputil.c b/sys/netinet/sctputil.c
index fe2b945..7bd8c7a9 100644
--- a/sys/netinet/sctputil.c
+++ b/sys/netinet/sctputil.c
@@ -2678,6 +2678,7 @@ set_error:
if (((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) ||
(stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL)) &&
((state == SCTP_COMM_LOST) || (state == SCTP_CANT_STR_ASSOC))) {
+ SOCK_LOCK(stcb->sctp_socket);
if (from_peer) {
if (SCTP_GET_STATE(&stcb->asoc) == SCTP_STATE_COOKIE_WAIT) {
SCTP_LTRACE_ERR_RET(NULL, stcb, NULL, SCTP_FROM_SCTPUTIL, ECONNREFUSED);
@@ -2709,7 +2710,7 @@ set_error:
if (((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) ||
(stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL)) &&
((state == SCTP_COMM_LOST) || (state == SCTP_CANT_STR_ASSOC))) {
- socantrcvmore(stcb->sctp_socket);
+ socantrcvmore_locked(stcb->sctp_socket);
}
sorwakeup(stcb->sctp_socket);
sowwakeup(stcb->sctp_socket);
diff --git a/sys/sys/watchdog.h b/sys/sys/watchdog.h
index ba58a7c..92c47de 100644
--- a/sys/sys/watchdog.h
+++ b/sys/sys/watchdog.h
@@ -1,5 +1,8 @@
/*-
* Copyright (c) 2003 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
@@ -32,7 +35,18 @@
#define _PATH_WATCHDOG "fido"
-#define WDIOCPATPAT _IOW('W', 42, u_int)
+#define WDIOCPATPAT _IOW('W', 42, u_int) /* pat the watchdog */
+#define WDIOC_SETTIMEOUT _IOW('W', 43, int) /* set/reset the timer */
+#define WDIOC_GETTIMEOUT _IOR('W', 44, int) /* get total timeout */
+#define WDIOC_GETTIMELEFT _IOR('W', 45, int) /* get time left */
+#define WDIOC_GETPRETIMEOUT _IOR('W', 46, int) /* get the pre-timeout */
+#define WDIOC_SETPRETIMEOUT _IOW('W', 47, int) /* set the pre-timeout */
+/* set the action when a pre-timeout occurs see: WD_SOFT_* */
+#define WDIOC_SETPRETIMEOUTACT _IOW('W', 48, int)
+
+/* use software watchdog instead of hardware */
+#define WDIOC_SETSOFT _IOW('W', 49, int)
+#define WDIOC_SETSOFTTIMEOUTACT _IOW('W', 50, int)
#define WD_ACTIVE 0x8000000
/*
@@ -76,6 +90,15 @@
#define WD_TO_8SEC 33
#define WD_TO_16SEC 34
#define WD_TO_32SEC 35
+#define WD_TO_64SEC 36
+#define WD_TO_128SEC 37
+
+/* action on pre-timeout trigger */
+#define WD_SOFT_PANIC 0x01 /* panic */
+#define WD_SOFT_DDB 0x02 /* enter debugger */
+#define WD_SOFT_LOG 0x04 /* log(9) */
+#define WD_SOFT_PRINTF 0x08 /* printf(9) */
+#define WD_SOFT_MASK 0x0f /* all of the above */
#ifdef _KERNEL
OpenPOWER on IntegriCloud