diff options
author | Warner Losh <imp@FreeBSD.org> | 2017-10-25 15:30:53 +0000 |
---|---|---|
committer | Warner Losh <imp@FreeBSD.org> | 2017-10-25 15:30:53 +0000 |
commit | 1170c2fecc28eb00f6f9d470c804450408d7a0e7 (patch) | |
tree | f29e297fb741c624da0c09e75665009f89e02631 /sys/dev/ipmi/ipmi.c | |
parent | 6a9fbe3a704f475c7af7c4fb980065511e7ca7df (diff) | |
download | src-1170c2fecc28eb00f6f9d470c804450408d7a0e7.tar.gz src-1170c2fecc28eb00f6f9d470c804450408d7a0e7.zip |
Implement IPMI support for RB_POWRECYCLE
Some BMCs support power cycling the chassis via the chassis control
command 2 subcommand 2 (ipmitool called it 'chassis power cycle'). If
the BMC supports the chassis device, register a shutdown_final handler
that sends the power cycle command if request and waits up to 10s for
it to take effect. To minimize stack strain, we preallocate a ipmi
request in the softc. At the moment, we're verbose about what we're
doing.
Sponsored by: Netflix
Notes
Notes:
svn path=/head/; revision=324990
Diffstat (limited to 'sys/dev/ipmi/ipmi.c')
-rw-r--r-- | sys/dev/ipmi/ipmi.c | 65 |
1 files changed, 61 insertions, 4 deletions
diff --git a/sys/dev/ipmi/ipmi.c b/sys/dev/ipmi/ipmi.c index 314d4d696c08..b2e706756a1e 100644 --- a/sys/dev/ipmi/ipmi.c +++ b/sys/dev/ipmi/ipmi.c @@ -36,6 +36,7 @@ __FBSDID("$FreeBSD$"); #include <sys/malloc.h> #include <sys/module.h> #include <sys/poll.h> +#include <sys/reboot.h> #include <sys/rman.h> #include <sys/selinfo.h> #include <sys/sysctl.h> @@ -690,6 +691,45 @@ ipmi_wd_event(void *arg, unsigned int cmd, int *error) } static void +ipmi_power_cycle(void *arg, int howto) +{ + struct ipmi_softc *sc = arg; + struct ipmi_request *req; + + /* + * Ignore everything except power cycling requests + */ + if ((howto & RB_POWERCYCLE) == 0) + return; + + device_printf(sc->ipmi_dev, "Power cycling using IPMI\n"); + + /* + * Send a CHASSIS_CONTROL command to the CHASSIS device, subcommand 2 + * as described in IPMI v2.0 spec section 28.3. + */ + IPMI_ALLOC_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_CHASSIS_REQUEST, 0), + IPMI_CHASSIS_CONTROL, 1, 0); + req->ir_request[0] = IPMI_CC_POWER_CYCLE; + + ipmi_submit_driver_request(sc, req, MAX_TIMEOUT); + + if (req->ir_error != 0 || req->ir_compcode != 0) { + device_printf(sc->ipmi_dev, "Power cycling via IPMI failed code %#x %#x\n", + req->ir_error, req->ir_compcode); + return; + } + + /* + * BMCs are notoriously slow, give it up to 10s to effect the power + * down leg of the power cycle. If that fails, fallback to the next + * hanlder in the shutdown_final chain and/or the platform failsafe. + */ + DELAY(10 * 1000 * 1000); + device_printf(sc->ipmi_dev, "Power cycling via IPMI timed out\n"); +} + +static void ipmi_startup(void *arg) { struct ipmi_softc *sc = arg; @@ -737,10 +777,12 @@ ipmi_startup(void *arg) } device_printf(dev, "IPMI device rev. %d, firmware rev. %d.%d%d, " - "version %d.%d\n", - req->ir_reply[1] & 0x0f, - req->ir_reply[2] & 0x7f, req->ir_reply[3] >> 4, req->ir_reply[3] & 0x0f, - req->ir_reply[4] & 0x0f, req->ir_reply[4] >> 4); + "version %d.%d, device support mask %#x\n", + req->ir_reply[1] & 0x0f, + req->ir_reply[2] & 0x7f, req->ir_reply[3] >> 4, req->ir_reply[3] & 0x0f, + req->ir_reply[4] & 0x0f, req->ir_reply[4] >> 4, req->ir_reply[5]); + + sc->ipmi_dev_support = req->ir_reply[5]; IPMI_INIT_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_APP_REQUEST, 0), IPMI_CLEAR_FLAGS, 1, 0); @@ -792,6 +834,17 @@ ipmi_startup(void *arg) return; } sc->ipmi_cdev->si_drv1 = sc; + + /* + * 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. + */ + if (sc->ipmi_dev_support & IPMI_ADS_CHASSIS) { + device_printf(dev, "Establishing power cycle handler\n"); + sc->ipmi_power_cycle_tag = EVENTHANDLER_REGISTER(shutdown_final, + ipmi_power_cycle, sc, SHUTDOWN_PRI_LAST - 1); + } } int @@ -844,6 +897,10 @@ ipmi_detach(device_t dev) ipmi_set_watchdog(sc, 0); } + /* Detach from shutdown handling for power cycle reboot */ + if (sc->ipmi_power_cycle_tag) + EVENTHANDLER_DEREGISTER(shutdown_final, sc->ipmi_power_cycle_tag); + /* XXX: should use shutdown callout I think. */ /* If the backend uses a kthread, shut it down. */ IPMI_LOCK(sc); |