Date: Thu, 26 Oct 2017 22:52:51 +0000 (UTC) From: Warner Losh <imp@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r325024 - in head/sys: dev/ipmi sys Message-ID: <201710262252.v9QMqpvL009359@repo.freebsd.org>
next in thread | raw e-mail | index | archive | help
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;
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201710262252.v9QMqpvL009359>