Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 3 Feb 2010 08:42:09 +0000 (UTC)
From:      Alexander Motin <mav@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r203420 - in head/sys: cam kern
Message-ID:  <201002030842.o138g9lt034282@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: mav
Date: Wed Feb  3 08:42:08 2010
New Revision: 203420
URL: http://svn.freebsd.org/changeset/base/203420

Log:
  MFp4:
  Make CAM to stop all attached devices on system shutdown.
  It allows devices to park heads, reducing stress on power loss.
  Add `kern.cam.power_down` tunable and sysctl to controll it.

Modified:
  head/sys/cam/cam_xpt.c
  head/sys/kern/kern_shutdown.c

Modified: head/sys/cam/cam_xpt.c
==============================================================================
--- head/sys/cam/cam_xpt.c	Wed Feb  3 07:21:20 2010	(r203419)
+++ head/sys/cam/cam_xpt.c	Wed Feb  3 08:42:08 2010	(r203420)
@@ -39,7 +39,7 @@ __FBSDID("$FreeBSD$");
 #include <sys/time.h>
 #include <sys/conf.h>
 #include <sys/fcntl.h>
-#include <sys/md5.h>
+#include <sys/reboot.h>
 #include <sys/interrupt.h>
 #include <sys/sbuf.h>
 #include <sys/taskqueue.h>
@@ -153,6 +153,10 @@ static struct xpt_softc xsoftc;
 TUNABLE_INT("kern.cam.boot_delay", &xsoftc.boot_delay);
 SYSCTL_INT(_kern_cam, OID_AUTO, boot_delay, CTLFLAG_RDTUN,
            &xsoftc.boot_delay, 0, "Bus registration wait time");
+static int	xpt_power_down = 1;
+TUNABLE_INT("kern.cam.power_down", &xpt_power_down);
+SYSCTL_INT(_kern_cam, OID_AUTO, power_down, CTLFLAG_RW,
+           &xpt_power_down, 0, "Power down devices on shutdown");
 
 /* Queues for our software interrupt handler */
 typedef TAILQ_HEAD(cam_isrq, ccb_hdr) cam_isrq_t;
@@ -236,6 +240,7 @@ static struct cam_ed*
 		 xpt_find_device(struct cam_et *target, lun_id_t lun_id);
 static void	 xpt_config(void *arg);
 static xpt_devicefunc_t xptpassannouncefunc;
+static void	 xpt_shutdown(void *arg, int howto);
 static void	 xptaction(struct cam_sim *sim, union ccb *work_ccb);
 static void	 xptpoll(struct cam_sim *sim);
 static void	 camisr(void *);
@@ -4660,6 +4665,12 @@ xpt_config(void *arg)
 #endif /* CAM_DEBUG_BUS */
 #endif /* CAMDEBUG */
 
+	/* Register our shutdown event handler */
+	if ((EVENTHANDLER_REGISTER(shutdown_final, xpt_shutdown, 
+				   NULL, SHUTDOWN_PRI_FIRST)) == NULL) {
+		printf("xpt_config: failed to register shutdown event.\n");
+	}
+
 	periphdriver_init(1);
 	xpt_hold_boot();
 	callout_init(&xsoftc.boot_callout, 1);
@@ -4667,7 +4678,7 @@ xpt_config(void *arg)
 	    xpt_boot_delay, NULL);
 	/* Fire up rescan thread. */
 	if (kproc_create(xpt_scanner_thread, NULL, NULL, 0, 0, "xpt_thrd")) {
-		printf("xpt_init: failed to create rescan thread\n");
+		printf("xpt_config: failed to create rescan thread.\n");
 	}
 }
 
@@ -4741,6 +4752,87 @@ xpt_finishconfig_task(void *context, int
 	free(context, M_CAMXPT);
 }
 
+/*
+ * Power down all devices when we are going to power down the system.
+ */
+static void
+xpt_shutdown_dev_done(struct cam_periph *periph, union ccb *done_ccb)
+{
+
+	/* No-op. We're polling. */
+	return;
+}
+
+static int
+xpt_shutdown_dev(struct cam_ed *device, void *arg)
+{
+	union ccb ccb;
+	struct cam_path path;
+
+	if (device->flags & CAM_DEV_UNCONFIGURED)
+		return (1);
+
+	if (device->protocol == PROTO_ATA) {
+		/* Only power down device if it supports power management. */
+		if ((device->ident_data.support.command1 &
+		    ATA_SUPPORT_POWERMGT) == 0)
+			return (1);
+	} else if (device->protocol != PROTO_SCSI)
+		return (1);
+
+	xpt_compile_path(&path,
+			 NULL,
+			 device->target->bus->path_id,
+			 device->target->target_id,
+			 device->lun_id);
+	xpt_setup_ccb(&ccb.ccb_h, &path, CAM_PRIORITY_NORMAL);
+	if (device->protocol == PROTO_ATA) {
+		cam_fill_ataio(&ccb.ataio,
+				    1,
+				    xpt_shutdown_dev_done,
+				    CAM_DIR_NONE,
+				    0,
+				    NULL,
+				    0,
+				    30*1000);
+		ata_28bit_cmd(&ccb.ataio, ATA_SLEEP, 0, 0, 0);
+	} else {
+		scsi_start_stop(&ccb.csio,
+				/*retries*/1,
+				xpt_shutdown_dev_done,
+				MSG_SIMPLE_Q_TAG,
+				/*start*/FALSE,
+				/*load/eject*/FALSE,
+				/*immediate*/TRUE,
+				SSD_FULL_SIZE,
+				/*timeout*/50*1000);
+	}
+	xpt_polled_action(&ccb);
+
+	if ((ccb.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP)
+		xpt_print(&path, "Device power down failed\n");
+	if ((ccb.ccb_h.status & CAM_DEV_QFRZN) != 0)
+		cam_release_devq(ccb.ccb_h.path,
+				 /*relsim_flags*/0,
+				 /*reduction*/0,
+				 /*timeout*/0,
+				 /*getcount_only*/0);
+	xpt_release_path(&path);
+	return (1);
+}
+
+static void
+xpt_shutdown(void * arg, int howto)
+{
+
+	if (!xpt_power_down)
+		return;
+	if ((howto & RB_POWEROFF) == 0)
+		return;
+
+	xpt_for_all_devices(xpt_shutdown_dev, NULL);
+}
+
 cam_status
 xpt_register_async(int event, ac_callback_t *cbfunc, void *cbarg,
 		   struct cam_path *path)

Modified: head/sys/kern/kern_shutdown.c
==============================================================================
--- head/sys/kern/kern_shutdown.c	Wed Feb  3 07:21:20 2010	(r203419)
+++ head/sys/kern/kern_shutdown.c	Wed Feb  3 08:42:08 2010	(r203420)
@@ -142,7 +142,7 @@ shutdown_conf(void *unused)
 {
 
 	EVENTHANDLER_REGISTER(shutdown_final, poweroff_wait, NULL,
-	    SHUTDOWN_PRI_FIRST);
+	    SHUTDOWN_PRI_FIRST + 100);
 	EVENTHANDLER_REGISTER(shutdown_final, shutdown_halt, NULL,
 	    SHUTDOWN_PRI_LAST + 100);
 	EVENTHANDLER_REGISTER(shutdown_final, shutdown_panic, NULL,



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