diff options
Diffstat (limited to 'sys')
-rw-r--r-- | sys/boot/common/load_elf.c | 2 | ||||
-rw-r--r-- | sys/dev/mfi/mfi.c | 16 | ||||
-rw-r--r-- | sys/dev/watchdog/watchdog.c | 242 | ||||
-rw-r--r-- | sys/netinet/sctputil.c | 3 | ||||
-rw-r--r-- | sys/sys/watchdog.h | 25 |
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 |