From owner-freebsd-scsi@FreeBSD.ORG Sat Sep 22 16:12:11 2012 Return-Path: Delivered-To: freebsd-scsi@FreeBSD.org Received: from mx1.freebsd.org (mx1.freebsd.org [69.147.83.52]) by hub.freebsd.org (Postfix) with ESMTP id 97F2E106564A; Sat, 22 Sep 2012 16:12:11 +0000 (UTC) (envelope-from avg@FreeBSD.org) Received: from citadel.icyb.net.ua (citadel.icyb.net.ua [212.40.38.140]) by mx1.freebsd.org (Postfix) with ESMTP id 925D78FC12; Sat, 22 Sep 2012 16:12:10 +0000 (UTC) Received: from porto.starpoint.kiev.ua (porto-e.starpoint.kiev.ua [212.40.38.100]) by citadel.icyb.net.ua (8.8.8p3/ICyb-2.3exp) with ESMTP id TAA07359; Sat, 22 Sep 2012 19:12:09 +0300 (EEST) (envelope-from avg@FreeBSD.org) Received: from localhost ([127.0.0.1]) by porto.starpoint.kiev.ua with esmtp (Exim 4.34 (FreeBSD)) id 1TFSJM-000NSn-Q7; Sat, 22 Sep 2012 19:12:08 +0300 Message-ID: <505DE358.8050304@FreeBSD.org> Date: Sat, 22 Sep 2012 19:12:08 +0300 From: Andriy Gapon User-Agent: Mozilla/5.0 (X11; FreeBSD amd64; rv:15.0) Gecko/20120913 Thunderbird/15.0.1 MIME-Version: 1.0 To: freebsd-scsi@FreeBSD.org X-Enigmail-Version: 1.4.3 Content-Type: text/plain; charset=X-VIET-VPS Content-Transfer-Encoding: 7bit Cc: Subject: ata_da: allow to force standby mode after idle period X-BeenThere: freebsd-scsi@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: SCSI subsystem List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sat, 22 Sep 2012 16:12:11 -0000 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 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