diff options
author | mav <mav@FreeBSD.org> | 2011-07-29 20:32:27 +0000 |
---|---|---|
committer | mav <mav@FreeBSD.org> | 2011-07-29 20:32:27 +0000 |
commit | 6afa7d74ec658020bda648febc51f45c677cd2a3 (patch) | |
tree | ec0188e087a3bdcc39cc8e83d050730214e2994e /sys/cam | |
parent | 56747017c93fe2b157d540210ca731a4901ae11b (diff) | |
download | FreeBSD-src-6afa7d74ec658020bda648febc51f45c677cd2a3.zip FreeBSD-src-6afa7d74ec658020bda648febc51f45c677cd2a3.tar.gz |
Add control for ATA disk read-ahead, alike to the previously added write
cache control. Some controller BIOS'es tend to disable read-ahead, that
dramatically reduces read performance. Previously ata(4) always enabled
read-ahead unconditionally.
Approved by: re (kib)
Diffstat (limited to 'sys/cam')
-rw-r--r-- | sys/cam/ata/ata_da.c | 113 |
1 files changed, 102 insertions, 11 deletions
diff --git a/sys/cam/ata/ata_da.c b/sys/cam/ata/ata_da.c index 46ae218..c0be395 100644 --- a/sys/cam/ata/ata_da.c +++ b/sys/cam/ata/ata_da.c @@ -69,6 +69,7 @@ __FBSDID("$FreeBSD$"); #define ATA_MAX_28BIT_LBA 268435455UL typedef enum { + ADA_STATE_RAHEAD, ADA_STATE_WCACHE, ADA_STATE_NORMAL } ada_state; @@ -94,7 +95,8 @@ typedef enum { } ada_quirks; typedef enum { - ADA_CCB_WCACHE = 0x01, + ADA_CCB_RAHEAD = 0x01, + ADA_CCB_WCACHE = 0x02, ADA_CCB_BUFFER_IO = 0x03, ADA_CCB_WAITING = 0x04, ADA_CCB_DUMP = 0x05, @@ -132,6 +134,7 @@ struct ada_softc { int outstanding_cmds; int trim_max_ranges; int trim_running; + int read_ahead; int write_cache; #ifdef ADA_TEST_FAILURE int force_read_error; @@ -294,10 +297,19 @@ static void adaresume(void *arg); #define ADA_DEFAULT_SPINDOWN_SUSPEND 1 #endif +#ifndef ADA_DEFAULT_READ_AHEAD +#define ADA_DEFAULT_READ_AHEAD 1 +#endif + #ifndef ADA_DEFAULT_WRITE_CACHE #define ADA_DEFAULT_WRITE_CACHE 1 #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) + /* * Most platforms map firmware geometry to actual, but some don't. If * not overridden, default to nothing. @@ -312,6 +324,7 @@ static int ada_default_timeout = ADA_DEFAULT_TIMEOUT; static int ada_send_ordered = ADA_DEFAULT_SEND_ORDERED; 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; SYSCTL_NODE(_kern_cam, OID_AUTO, ada, CTLFLAG_RD, 0, @@ -334,6 +347,9 @@ TUNABLE_INT("kern.cam.ada.spindown_shutdown", &ada_spindown_shutdown); SYSCTL_INT(_kern_cam_ada, OID_AUTO, spindown_suspend, CTLFLAG_RW, &ada_spindown_suspend, 0, "Spin down upon suspend"); TUNABLE_INT("kern.cam.ada.spindown_suspend", &ada_spindown_suspend); +SYSCTL_INT(_kern_cam_ada, OID_AUTO, read_ahead, CTLFLAG_RW, + &ada_read_ahead, 0, "Enable disk read-ahead"); +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); @@ -737,16 +753,19 @@ adaasync(void *callback_arg, u_int32_t code, softc = (struct ada_softc *)periph->softc; cam_periph_async(periph, code, path, arg); - if (ada_write_cache < 0 && softc->write_cache < 0) - break; if (softc->state != ADA_STATE_NORMAL) break; xpt_setup_ccb(&cgd.ccb_h, periph->path, CAM_PRIORITY_NORMAL); cgd.ccb_h.func_code = XPT_GDEV_TYPE; xpt_action((union ccb *)&cgd); - if ((cgd.ident_data.support.command1 & ATA_SUPPORT_WRITECACHE) == 0) - break; - softc->state = ADA_STATE_WCACHE; + if (ADA_RA >= 0 && + cgd.ident_data.support.command1 & ATA_SUPPORT_LOOKAHEAD) + softc->state = ADA_STATE_RAHEAD; + else if (ADA_WC >= 0 && + cgd.ident_data.support.command1 & ATA_SUPPORT_WRITECACHE) + softc->state = ADA_STATE_WCACHE; + else + break; cam_periph_acquire(periph); cam_freeze_devq_arg(periph->path, RELSIM_RELEASE_RUNLEVEL, CAM_RL_DEV + 1); @@ -789,6 +808,9 @@ adasysctlinit(void *context, int pending) } SYSCTL_ADD_INT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree), + OID_AUTO, "read_ahead", CTLFLAG_RW | CTLFLAG_MPSAFE, + &softc->read_ahead, 0, "Enable disk read ahead."); + 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."); #ifdef ADA_TEST_FAILURE @@ -922,6 +944,10 @@ adaregister(struct cam_periph *periph, void *arg) quirks = softc->quirks; TUNABLE_INT_FETCH(announce_buf, &quirks); softc->quirks = quirks; + softc->read_ahead = -1; + snprintf(announce_buf, sizeof(announce_buf), + "kern.cam.ada.%d.read_ahead", periph->unit_number); + TUNABLE_INT_FETCH(announce_buf, &softc->read_ahead); softc->write_cache = -1; snprintf(announce_buf, sizeof(announce_buf), "kern.cam.ada.%d.write_cache", periph->unit_number); @@ -1044,7 +1070,14 @@ adaregister(struct cam_periph *periph, void *arg) (ADA_DEFAULT_TIMEOUT * hz) / ADA_ORDEREDTAG_INTERVAL, adasendorderedtag, softc); - if ((ada_write_cache >= 0 || softc->write_cache >= 0) && + if (ADA_RA >= 0 && + cgd->ident_data.support.command1 & ATA_SUPPORT_LOOKAHEAD) { + softc->state = ADA_STATE_RAHEAD; + cam_periph_acquire(periph); + cam_freeze_devq_arg(periph->path, + RELSIM_RELEASE_RUNLEVEL, CAM_RL_DEV + 1); + xpt_schedule(periph, CAM_PRIORITY_DEV); + } else if (ADA_WC >= 0 && cgd->ident_data.support.command1 & ATA_SUPPORT_WRITECACHE) { softc->state = ADA_STATE_WCACHE; cam_periph_acquire(periph); @@ -1317,8 +1350,19 @@ out: adaschedule(periph); break; } + case ADA_STATE_RAHEAD: case ADA_STATE_WCACHE: { + if (softc->flags & ADA_FLAG_PACK_INVALID) { + softc->state = ADA_STATE_NORMAL; + xpt_release_ccb(start_ccb); + cam_release_devq(periph->path, + RELSIM_RELEASE_RUNLEVEL, 0, CAM_RL_DEV + 1, FALSE); + adaschedule(periph); + cam_periph_release_locked(periph); + return; + } + cam_fill_ataio(ataio, 1, adadone, @@ -1328,10 +1372,15 @@ out: 0, ada_default_timeout*1000); - ata_28bit_cmd(ataio, ATA_SETFEATURES, (softc->write_cache > 0 || - (softc->write_cache < 0 && ada_write_cache)) ? - ATA_SF_ENAB_WCACHE : ATA_SF_DIS_WCACHE, 0, 0); - start_ccb->ccb_h.ccb_state = ADA_CCB_WCACHE; + if (softc->state == ADA_STATE_RAHEAD) { + ata_28bit_cmd(ataio, ATA_SETFEATURES, ADA_RA ? + ATA_SF_ENAB_RCACHE : ATA_SF_DIS_RCACHE, 0, 0); + start_ccb->ccb_h.ccb_state = ADA_CCB_RAHEAD; + } else { + ata_28bit_cmd(ataio, ATA_SETFEATURES, ADA_WC ? + ATA_SF_ENAB_WCACHE : ATA_SF_DIS_WCACHE, 0, 0); + start_ccb->ccb_h.ccb_state = ADA_CCB_WCACHE; + } xpt_action(start_ccb); break; } @@ -1343,6 +1392,7 @@ adadone(struct cam_periph *periph, union ccb *done_ccb) { struct ada_softc *softc; struct ccb_ataio *ataio; + struct ccb_getdev *cgd; softc = (struct ada_softc *)periph->softc; ataio = &done_ccb->ataio; @@ -1423,6 +1473,47 @@ adadone(struct cam_periph *periph, union ccb *done_ccb) biodone(bp); break; } + case ADA_CCB_RAHEAD: + { + if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { + if (adaerror(done_ccb, 0, 0) == ERESTART) { + return; + } else if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) { + cam_release_devq(done_ccb->ccb_h.path, + /*relsim_flags*/0, + /*reduction*/0, + /*timeout*/0, + /*getcount_only*/0); + } + } + + /* + * Since our peripheral may be invalidated by an error + * above or an external event, we must release our CCB + * before releasing the reference on the peripheral. + * The peripheral will only go away once the last reference + * is removed, and we need it around for the CCB release + * operation. + */ + cgd = (struct ccb_getdev *)done_ccb; + xpt_setup_ccb(&cgd->ccb_h, periph->path, CAM_PRIORITY_NORMAL); + cgd->ccb_h.func_code = XPT_GDEV_TYPE; + xpt_action((union ccb *)cgd); + if (ADA_WC >= 0 && + cgd->ident_data.support.command1 & ATA_SUPPORT_WRITECACHE) { + softc->state = ADA_STATE_WCACHE; + xpt_release_ccb(done_ccb); + xpt_schedule(periph, CAM_PRIORITY_DEV); + return; + } + softc->state = ADA_STATE_NORMAL; + xpt_release_ccb(done_ccb); + cam_release_devq(periph->path, + RELSIM_RELEASE_RUNLEVEL, 0, CAM_RL_DEV + 1, FALSE); + adaschedule(periph); + cam_periph_release_locked(periph); + return; + } case ADA_CCB_WCACHE: { if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { |