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