From 1170c2fecc28eb00f6f9d470c804450408d7a0e7 Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Wed, 25 Oct 2017 15:30:53 +0000 Subject: [PATCH] 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 --- share/man/man4/ipmi.4 | 13 +++++++++ sys/dev/ipmi/ipmi.c | 65 ++++++++++++++++++++++++++++++++++++++--- sys/dev/ipmi/ipmivars.h | 2 ++ sys/sys/ipmi.h | 17 +++++++++++ 4 files changed, 93 insertions(+), 4 deletions(-) diff --git a/share/man/man4/ipmi.4 b/share/man/man4/ipmi.4 index ba99464f17f8..cc82cb65199f 100644 --- a/share/man/man4/ipmi.4 +++ b/share/man/man4/ipmi.4 @@ -90,6 +90,17 @@ is heavily adopted from the standard and .Tn Linux driver; however, not all features described in the standard are supported. +.Pp +The +.Nm +driver implements the power cycling option to +.Xr shutdown 8 +to implement power cycling of the system. +The motherboard's BMC must support the chassis device and the optional +power cycle subcomand of the chassis control command as described in section 28.3 +if the IPMI standard. +The length of time the system is off will be at least one second, but +may be longer if the power cycle interval has been set (see section 28.9). .Sh IOCTLS Sending and receiving messages through the .Nm @@ -179,6 +190,8 @@ An address supplied was invalid. .Sh SEE ALSO .Xr ioctl 2 , .Xr watchdog 4 , +.Xr reboot 8 , +.Xr shutdown 8 , .Xr watchdog 8 , .Xr watchdogd 8 , .Xr watchdog 9 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 #include #include +#include #include #include #include @@ -689,6 +690,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) { @@ -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); diff --git a/sys/dev/ipmi/ipmivars.h b/sys/dev/ipmi/ipmivars.h index 6682b56b6299..eae6127790ba 100644 --- a/sys/dev/ipmi/ipmivars.h +++ b/sys/dev/ipmi/ipmivars.h @@ -103,9 +103,11 @@ struct ipmi_softc { void *ipmi_irq; int ipmi_detaching; int ipmi_opened; + uint8_t ipmi_dev_support; /* IPMI_ADS_* */ struct cdev *ipmi_cdev; TAILQ_HEAD(,ipmi_request) ipmi_pending_requests; int ipmi_driver_requests_polled; + eventhandler_tag ipmi_power_cycle_tag; eventhandler_tag ipmi_watchdog_tag; int ipmi_watchdog_active; struct intr_config_hook ipmi_ich; diff --git a/sys/sys/ipmi.h b/sys/sys/ipmi.h index b8e318a921bc..6bb212c30468 100644 --- a/sys/sys/ipmi.h +++ b/sys/sys/ipmi.h @@ -56,8 +56,25 @@ #define IPMI_ASYNC_EVENT_RECV_TYPE 2 #define IPMI_CMD_RECV_TYPE 3 +#define IPMI_CHASSIS_REQUEST 0x00 +# define IPMI_CHASSIS_CONTROL 0x02 +# define IPMI_CC_POWER_DOWN 0x0 +# define IPMI_CC_POWER_UP 0x1 +# define IPMI_CC_POWER_CYCLE 0x2 +# define IPMI_CC_HARD_RESET 0x3 +# define IPMI_CC_PULSE_DI 0x4 +# define IPMI_CC_SOFT_OVERTEMP 0x5 + #define IPMI_APP_REQUEST 0x06 #define IPMI_GET_DEVICE_ID 0x01 +# define IPMI_ADS_CHASSIS 0x80 +# define IPMI_ADS_BRIDGE 0x40 +# define IPMI_ADS_EVENT_GEN 0x20 +# define IPMI_ADS_EVENT_RCV 0x10 +# define IPMI_ADS_FRU_INV 0x08 +# define IPMI_ADS_SEL 0x04 +# define IPMI_ADS_SDR 0x02 +# define IPMI_ADS_SENSOR 0x01 #define IPMI_CLEAR_FLAGS 0x30 #define IPMI_GET_MSG_FLAGS 0x31 # define IPMI_MSG_AVAILABLE 0x01