ata_da: allow to force standby mode after idle period

Andriy Gapon avg at FreeBSD.org
Sat Sep 22 16:12:11 UTC 2012


I have a disk that obeys idle/standby timeout only once.  The disk goes into
standby the first time the timeout expires, but when it is waken up it acts as
if no timeout is set.

I modeled the following patch after a similar change made to ad(4) by phk a long
time ago.

I think that patch might be good to have for some, but I won't be insisting on
it getting into the tree.

commit 8df792704c7181d3448d47b72efd9f77d2e091ff
Author: Andriy Gapon <avg at icyb.net.ua>
Date:   Sat Jun 9 13:03:48 2012 +0300

    ata_da: allow to force standby mode after idle period

    ... for disks that do not enter the mode via internal timer

diff --git a/sys/cam/ata/ata_da.c b/sys/cam/ata/ata_da.c
index 92ed0c6..1c5e2c3 100644
--- a/sys/cam/ata/ata_da.c
+++ b/sys/cam/ata/ata_da.c
@@ -136,6 +136,7 @@ struct ada_softc {
 	int	 trim_running;
 	int	 read_ahead;
 	int	 write_cache;
+	int	 spindown;
 #ifdef ADA_TEST_FAILURE
 	int      force_read_error;
 	int      force_write_error;
@@ -149,6 +150,7 @@ struct ada_softc {
 	struct sysctl_oid	*sysctl_tree;
 	struct callout		sendordered_c;
 	struct trim_request	trim_req;
+	struct callout		spindown_timer;
 };

 struct ada_quirk_entry {
@@ -298,6 +300,7 @@ static timeout_t	adasendorderedtag;
 static void		adashutdown(void *arg, int howto);
 static void		adasuspend(void *arg);
 static void		adaresume(void *arg);
+static void		adadiskspindown(void *);

 #ifndef	ADA_DEFAULT_LEGACY_ALIASES
 #ifdef ATA_CAM
@@ -335,10 +338,16 @@ static void		adaresume(void *arg);
 #define	ADA_DEFAULT_WRITE_CACHE	1
 #endif

+#ifndef	ADA_DEFAULT_SPINDOWN
+#define	ADA_DEFAULT_SPINDOWN	0
+#endif
+
 #define	ADA_RA	(softc->read_ahead >= 0 ? \
 		 softc->read_ahead : ada_read_ahead)
 #define	ADA_WC	(softc->write_cache >= 0 ? \
 		 softc->write_cache : ada_write_cache)
+#define	ADA_SD	(softc->spindown >= 0 ? \
+		 softc->spindown : ada_spindown)

 /*
  * Most platforms map firmware geometry to actual, but some don't.  If
@@ -356,6 +365,7 @@ static int ada_spindown_shutdown =
ADA_DEFAULT_SPINDOWN_SHUTDOWN;
 static int ada_spindown_suspend = ADA_DEFAULT_SPINDOWN_SUSPEND;
 static int ada_read_ahead = ADA_DEFAULT_READ_AHEAD;
 static int ada_write_cache = ADA_DEFAULT_WRITE_CACHE;
+static int ada_spindown = ADA_DEFAULT_SPINDOWN;

 static SYSCTL_NODE(_kern_cam, OID_AUTO, ada, CTLFLAG_RD, 0,
             "CAM Direct Access Disk driver");
@@ -383,6 +393,9 @@ TUNABLE_INT("kern.cam.ada.read_ahead", &ada_read_ahead);
 SYSCTL_INT(_kern_cam_ada, OID_AUTO, write_cache, CTLFLAG_RW,
            &ada_write_cache, 0, "Enable disk write cache");
 TUNABLE_INT("kern.cam.ada.write_cache", &ada_write_cache);
+SYSCTL_INT(_kern_cam_ada, OID_AUTO, spindown, CTLFLAG_RW,
+           &ada_spindown, 0, "Forced idle spin-down timeout");
+TUNABLE_INT("kern.cam.ada.spindown", &ada_spindown);

 /*
  * ADA_ORDEREDTAG_INTERVAL determines how often, relative
@@ -543,6 +556,10 @@ adastrategy(struct bio *bp)
 	}
 	softc = (struct ada_softc *)periph->softc;

+	if (ADA_SD > 0)
+		callout_reset(&softc->spindown_timer, ADA_SD * hz,
+		    adadiskspindown, periph);
+
 	cam_periph_lock(periph);

 	CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("adastrategy(%p)\n", bp));
@@ -709,6 +726,8 @@ adaoninvalidate(struct cam_periph *periph)

 	softc->flags |= ADA_FLAG_PACK_INVALID;

+	callout_drain(&softc->spindown_timer);
+
 	/*
 	 * Return all queued I/O with ENXIO.
 	 * XXX Handle any transactions queued to the card
@@ -731,6 +750,8 @@ adacleanup(struct cam_periph *periph)
 	xpt_print(periph->path, "removing device entry\n");
 	cam_periph_unlock(periph);

+	callout_drain(&softc->spindown_timer);
+
 	/*
 	 * If we can't free the sysctl tree, oh well...
 	 */
@@ -889,6 +910,9 @@ adasysctlinit(void *context, int pending)
 	SYSCTL_ADD_INT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree),
 		OID_AUTO, "write_cache", CTLFLAG_RW | CTLFLAG_MPSAFE,
 		&softc->write_cache, 0, "Enable disk write cache.");
+	SYSCTL_ADD_INT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree),
+		OID_AUTO, "spindown", CTLFLAG_RW | CTLFLAG_MPSAFE,
+		&softc->spindown, 0, "Forced idle spin-down timeout.");
 #ifdef ADA_TEST_FAILURE
 	/*
 	 * Add a 'door bell' sysctl which allows one to set it from userland
@@ -1029,6 +1053,10 @@ adaregister(struct cam_periph *periph, void *arg)
 	snprintf(announce_buf, sizeof(announce_buf),
 	    "kern.cam.ada.%d.write_cache", periph->unit_number);
 	TUNABLE_INT_FETCH(announce_buf, &softc->write_cache);
+	softc->spindown = -1;
+	snprintf(announce_buf, sizeof(announce_buf),
+	    "kern.cam.ada.%d.spindown", periph->unit_number);
+	TUNABLE_INT_FETCH(announce_buf, &softc->spindown);
 	adagetparams(periph, cgd);
 	softc->disk = disk_alloc();
 	softc->disk->d_devstat = devstat_new_entry(periph->periph_name,
@@ -1167,6 +1195,11 @@ adaregister(struct cam_periph *periph, void *arg)
 	} else
 		softc->state = ADA_STATE_NORMAL;

+	callout_init(&softc->spindown_timer, 1);
+	if (ADA_SD > 0)
+		callout_reset(&softc->spindown_timer, ADA_SD * hz,
+		    adadiskspindown, periph);
+
 	return(CAM_REQ_CMP);
 }

@@ -1822,6 +1855,53 @@ adaspindown(uint8_t cmd, int flags)
 }

 static void
+adadiskspindown(void *arg)
+{
+	union ccb ccb;
+	struct cam_periph *periph = arg;
+	struct ada_softc *softc;
+
+	cam_periph_lock(periph);
+	softc = (struct ada_softc *)periph->softc;
+
+	/*
+	 * We only spin-down the drive if it is capable of it..
+	 */
+	if ((softc->flags & ADA_FLAG_CAN_POWERMGT) == 0) {
+		cam_periph_unlock(periph);
+		return;
+	}
+
+	if (bootverbose)
+		xpt_print(periph->path, "spin-down\n");
+
+	xpt_setup_ccb(&ccb.ccb_h, periph->path, CAM_PRIORITY_NORMAL);
+	ccb.ccb_h.ccb_state = ADA_CCB_DUMP;
+	cam_fill_ataio(&ccb.ataio,
+			    1,
+			    adadone,
+			    CAM_DIR_NONE,
+			    0,
+			    NULL,
+			    0,
+			    ada_default_timeout*1000);
+
+	ata_28bit_cmd(&ccb.ataio, ATA_STANDBY_IMMEDIATE, 0, 0, 0);
+	xpt_polled_action(&ccb);
+
+	if ((ccb.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP)
+		xpt_print(periph->path, "Spin-down disk 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);
+	cam_periph_unlock(periph);
+}
+
+static void
 adashutdown(void *arg, int howto)
 {


-- 
Andriy Gapon


More information about the freebsd-scsi mailing list