1
0
mirror of https://git.FreeBSD.org/src.git synced 2024-12-28 11:57:28 +00:00

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
This commit is contained in:
Warner Losh 2017-10-26 22:52:51 +00:00
parent 96b90524d1
commit 14d004507e
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=325024
3 changed files with 115 additions and 6 deletions

View File

@ -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 sec)
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,15 +718,54 @@ 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;
}
}
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)
{
@ -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);
}
}
@ -835,6 +914,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.
@ -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);

View File

@ -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;

View File

@ -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;