Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 25 Oct 2017 15:30:54 +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: r324990 - in head: share/man/man4 sys/dev/ipmi sys/sys
Message-ID:  <201710251530.v9PFUsSw020745@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: imp
Date: Wed Oct 25 15:30:53 2017
New Revision: 324990
URL: https://svnweb.freebsd.org/changeset/base/324990

Log:
  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

Modified:
  head/share/man/man4/ipmi.4
  head/sys/dev/ipmi/ipmi.c
  head/sys/dev/ipmi/ipmivars.h
  head/sys/sys/ipmi.h

Modified: head/share/man/man4/ipmi.4
==============================================================================
--- head/share/man/man4/ipmi.4	Wed Oct 25 15:30:48 2017	(r324989)
+++ head/share/man/man4/ipmi.4	Wed Oct 25 15:30:53 2017	(r324990)
@@ -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

Modified: head/sys/dev/ipmi/ipmi.c
==============================================================================
--- head/sys/dev/ipmi/ipmi.c	Wed Oct 25 15:30:48 2017	(r324989)
+++ head/sys/dev/ipmi/ipmi.c	Wed Oct 25 15:30:53 2017	(r324990)
@@ -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,11 +777,13 @@ 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
@@ -843,6 +896,10 @@ ipmi_detach(device_t dev)
 		EVENTHANDLER_DEREGISTER(watchdog_list, sc->ipmi_watchdog_tag);
 		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. */

Modified: head/sys/dev/ipmi/ipmivars.h
==============================================================================
--- head/sys/dev/ipmi/ipmivars.h	Wed Oct 25 15:30:48 2017	(r324989)
+++ head/sys/dev/ipmi/ipmivars.h	Wed Oct 25 15:30:53 2017	(r324990)
@@ -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;

Modified: head/sys/sys/ipmi.h
==============================================================================
--- head/sys/sys/ipmi.h	Wed Oct 25 15:30:48 2017	(r324989)
+++ head/sys/sys/ipmi.h	Wed Oct 25 15:30:53 2017	(r324990)
@@ -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



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201710251530.v9PFUsSw020745>