svn commit: r325024 - in head/sys: dev/ipmi sys
Warner Losh
imp at FreeBSD.org
Thu Oct 26 22:52:53 UTC 2017
Author: imp
Date: Thu Oct 26 22:52:51 2017
New Revision: 325024
URL: https://svnweb.freebsd.org/changeset/base/325024
Log:
Various IPMI watchdog timer improvements
o Make hw.ipmi.on a tuneable
o Changes to keep shutdown from hanging indefinitately after the wd
would normally have been disabled.
o Add support for setting pretimeout (which fires an interrupt
some time before the actual watchdog expires)
o Allow refinement of the actions to take when the watchdog expires
o Allow special startup timeout to keep us from hanging in boot
before watchdogd is started, but after we've loaded the kernel.
Obtained From: Netflix OCA Firmware
Modified:
head/sys/dev/ipmi/ipmi.c
head/sys/dev/ipmi/ipmivars.h
head/sys/sys/ipmi.h
Modified: head/sys/dev/ipmi/ipmi.c
==============================================================================
--- head/sys/dev/ipmi/ipmi.c Thu Oct 26 22:19:28 2017 (r325023)
+++ head/sys/dev/ipmi/ipmi.c Thu Oct 26 22:52:51 2017 (r325024)
@@ -81,10 +81,28 @@ static void ipmi_dtor(void *arg);
int ipmi_attached = 0;
static int on = 1;
+static bool wd_in_shutdown = false;
+static int wd_timer_actions = IPMI_SET_WD_ACTION_POWER_CYCLE;
+static int wd_shutdown_countdown = 420; /* sec */
+static int wd_startup_countdown = 420; /* sec */
+static int wd_pretimeout_countdown = 120; /* sec */
+
static SYSCTL_NODE(_hw, OID_AUTO, ipmi, CTLFLAG_RD, 0,
"IPMI driver parameters");
-SYSCTL_INT(_hw_ipmi, OID_AUTO, on, CTLFLAG_RW,
+SYSCTL_INT(_hw_ipmi, OID_AUTO, on, CTLFLAG_RWTUN,
&on, 0, "");
+SYSCTL_INT(_hw_ipmi, OID_AUTO, wd_timer_actions, CTLFLAG_RW,
+ &wd_timer_actions, 0,
+ "IPMI watchdog timer actions (including pre-timeout interrupt)");
+SYSCTL_INT(_hw_ipmi, OID_AUTO, wd_shutdown_countdown, CTLFLAG_RW,
+ &wd_shutdown_countdown, 0,
+ "IPMI watchdog countdown for shutdown (seconds)");
+SYSCTL_INT(_hw_ipmi, OID_AUTO, wd_startup_countdown, CTLFLAG_RDTUN,
+ &wd_startup_countdown, 0,
+ "IPMI watchdog countdown initialized during startup (seconds)");
+SYSCTL_INT(_hw_ipmi, OID_AUTO, wd_pretimeout_countdown, CTLFLAG_RW,
+ &wd_pretimeout_countdown, 0,
+ "IPMI watchdog pre-timeout countdown (seconds)");
static struct cdevsw ipmi_cdevsw = {
.d_version = D_VERSION,
@@ -631,8 +649,8 @@ ipmi_set_watchdog(struct ipmi_softc *sc, unsigned int
if (sec) {
req->ir_request[0] = IPMI_SET_WD_TIMER_DONT_STOP
| IPMI_SET_WD_TIMER_SMS_OS;
- req->ir_request[1] = IPMI_SET_WD_ACTION_RESET;
- req->ir_request[2] = 0;
+ req->ir_request[1] = (wd_timer_actions & 0xff);
+ req->ir_request[2] = (wd_pretimeout_countdown & 0xff);
req->ir_request[3] = 0; /* Timer use */
req->ir_request[4] = (sec * 10) & 0xff;
req->ir_request[5] = (sec * 10) >> 8;
@@ -657,21 +675,40 @@ ipmi_wd_event(void *arg, unsigned int cmd, int *error)
unsigned int timeout;
int e;
- if (dumping)
+ /* Ignore requests while disabled. */
+ if (!on)
return;
+ /*
+ * To prevent infinite hangs, we don't let anyone pat or change
+ * the watchdog when we're shutting down. (See ipmi_shutdown_event().)
+ * However, we do want to keep patting the watchdog while we are doing
+ * a coredump.
+ */
+ if (wd_in_shutdown) {
+ if (dumping && sc->ipmi_watchdog_active)
+ ipmi_reset_watchdog(sc);
+ return;
+ }
+
cmd &= WD_INTERVAL;
if (cmd > 0 && cmd <= 63) {
timeout = ((uint64_t)1 << cmd) / 1000000000;
if (timeout == 0)
timeout = 1;
- if (timeout != sc->ipmi_watchdog_active) {
+ if (timeout != sc->ipmi_watchdog_active ||
+ wd_timer_actions != sc->ipmi_watchdog_actions ||
+ wd_pretimeout_countdown != sc->ipmi_watchdog_pretimeout) {
e = ipmi_set_watchdog(sc, timeout);
if (e == 0) {
sc->ipmi_watchdog_active = timeout;
+ sc->ipmi_watchdog_actions = wd_timer_actions;
+ sc->ipmi_watchdog_pretimeout = wd_pretimeout_countdown;
} else {
(void)ipmi_set_watchdog(sc, 0);
sc->ipmi_watchdog_active = 0;
+ sc->ipmi_watchdog_actions = 0;
+ sc->ipmi_watchdog_pretimeout = 0;
}
}
if (sc->ipmi_watchdog_active != 0) {
@@ -681,9 +718,14 @@ ipmi_wd_event(void *arg, unsigned int cmd, int *error)
} else {
(void)ipmi_set_watchdog(sc, 0);
sc->ipmi_watchdog_active = 0;
+ sc->ipmi_watchdog_actions = 0;
+ sc->ipmi_watchdog_pretimeout = 0;
}
}
} else if (atomic_readandclear_int(&sc->ipmi_watchdog_active) != 0) {
+ sc->ipmi_watchdog_actions = 0;
+ sc->ipmi_watchdog_pretimeout = 0;
+
e = ipmi_set_watchdog(sc, 0);
if (e != 0 && cmd == 0)
*error = EOPNOTSUPP;
@@ -691,6 +733,40 @@ ipmi_wd_event(void *arg, unsigned int cmd, int *error)
}
static void
+ipmi_shutdown_event(void *arg, unsigned int cmd, int *error)
+{
+ struct ipmi_softc *sc = arg;
+
+ /* Ignore event if disabled. */
+ if (!on)
+ return;
+
+ /*
+ * Positive wd_shutdown_countdown value will re-arm watchdog;
+ * Zero value in wd_shutdown_countdown will disable watchdog;
+ * Negative value in wd_shutdown_countdown will keep existing state;
+ *
+ * Revert to using a power cycle to ensure that the watchdog will
+ * do something useful here. Having the watchdog send an NMI
+ * instead is useless during shutdown, and might be ignored if an
+ * NMI already triggered.
+ */
+
+ wd_in_shutdown = true;
+ if (wd_shutdown_countdown == 0) {
+ /* disable watchdog */
+ ipmi_set_watchdog(sc, 0);
+ sc->ipmi_watchdog_active = 0;
+ } else if (wd_shutdown_countdown > 0) {
+ /* set desired action and time, and, reset watchdog */
+ wd_timer_actions = IPMI_SET_WD_ACTION_POWER_CYCLE;
+ ipmi_set_watchdog(sc, wd_shutdown_countdown);
+ sc->ipmi_watchdog_active = wd_shutdown_countdown;
+ ipmi_reset_watchdog(sc);
+ }
+}
+
+static void
ipmi_power_cycle(void *arg, int howto)
{
struct ipmi_softc *sc = arg;
@@ -823,7 +899,10 @@ ipmi_startup(void *arg)
device_printf(dev, "Attached watchdog\n");
/* register the watchdog event handler */
sc->ipmi_watchdog_tag = EVENTHANDLER_REGISTER(
- watchdog_list, ipmi_wd_event, sc, 0);
+ watchdog_list, ipmi_wd_event, sc, 0);
+ sc->ipmi_shutdown_tag = EVENTHANDLER_REGISTER(
+ shutdown_pre_sync, ipmi_shutdown_event,
+ sc, 0);
}
}
@@ -836,6 +915,23 @@ ipmi_startup(void *arg)
sc->ipmi_cdev->si_drv1 = sc;
/*
+ * Set initial watchdog state. If desired, set an initial
+ * watchdog on startup. Or, if the watchdog device is
+ * disabled, clear any existing watchdog.
+ */
+ if (on && wd_startup_countdown > 0) {
+ wd_timer_actions = IPMI_SET_WD_ACTION_POWER_CYCLE;
+ if (ipmi_set_watchdog(sc, wd_startup_countdown) == 0 &&
+ ipmi_reset_watchdog(sc) == 0) {
+ sc->ipmi_watchdog_active = wd_startup_countdown;
+ sc->ipmi_watchdog_actions = wd_timer_actions;
+ sc->ipmi_watchdog_pretimeout = wd_pretimeout_countdown;
+ } else
+ (void)ipmi_set_watchdog(sc, 0);
+ ipmi_reset_watchdog(sc);
+ } else if (!on)
+ (void)ipmi_set_watchdog(sc, 0);
+ /*
* Power cycle the system off using IPMI. We use last - 1 since we don't
* handle all the other kinds of reboots. We'll let others handle them.
* We only try to do this if the BMC supports the Chassis device.
@@ -892,6 +988,9 @@ ipmi_detach(device_t dev)
destroy_dev(sc->ipmi_cdev);
/* Detach from watchdog handling and turn off watchdog. */
+ if (sc->ipmi_shutdown_tag)
+ EVENTHANDLER_DEREGISTER(shutdown_pre_sync,
+ sc->ipmi_shutdown_tag);
if (sc->ipmi_watchdog_tag) {
EVENTHANDLER_DEREGISTER(watchdog_list, sc->ipmi_watchdog_tag);
ipmi_set_watchdog(sc, 0);
Modified: head/sys/dev/ipmi/ipmivars.h
==============================================================================
--- head/sys/dev/ipmi/ipmivars.h Thu Oct 26 22:19:28 2017 (r325023)
+++ head/sys/dev/ipmi/ipmivars.h Thu Oct 26 22:52:51 2017 (r325024)
@@ -109,7 +109,10 @@ struct ipmi_softc {
int ipmi_driver_requests_polled;
eventhandler_tag ipmi_power_cycle_tag;
eventhandler_tag ipmi_watchdog_tag;
+ eventhandler_tag ipmi_shutdown_tag;
int ipmi_watchdog_active;
+ int ipmi_watchdog_actions;
+ int ipmi_watchdog_pretimeout;
struct intr_config_hook ipmi_ich;
struct mtx ipmi_requests_lock;
struct cv ipmi_request_added;
Modified: head/sys/sys/ipmi.h
==============================================================================
--- head/sys/sys/ipmi.h Thu Oct 26 22:19:28 2017 (r325023)
+++ head/sys/sys/ipmi.h Thu Oct 26 22:52:51 2017 (r325024)
@@ -89,7 +89,14 @@
#define IPMI_SET_WD_TIMER_SMS_OS 0x04
#define IPMI_SET_WD_TIMER_DONT_STOP 0x40
+#define IPMI_SET_WD_ACTION_NONE 0x00
#define IPMI_SET_WD_ACTION_RESET 0x01
+#define IPMI_SET_WD_ACTION_POWER_DOWN 0x02
+#define IPMI_SET_WD_ACTION_POWER_CYCLE 0x03
+#define IPMI_SET_WD_PREACTION_NONE (0x00 << 4)
+#define IPMI_SET_WD_PREACTION_SMI (0x01 << 4)
+#define IPMI_SET_WD_PREACTION_NMI (0x02 << 4)
+#define IPMI_SET_WD_PREACTION_MI (0x03 << 4)
struct ipmi_msg {
unsigned char netfn;
More information about the svn-src-all
mailing list