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

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
This commit is contained in:
Warner Losh 2017-10-25 15:30:53 +00:00
parent 6a9fbe3a70
commit 1170c2fecc
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=324990
4 changed files with 93 additions and 4 deletions

View File

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

View File

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

View File

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

View File

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