summaryrefslogtreecommitdiffstats
path: root/sys/cam
diff options
context:
space:
mode:
authormav <mav@FreeBSD.org>2011-07-29 20:32:27 +0000
committermav <mav@FreeBSD.org>2011-07-29 20:32:27 +0000
commit6afa7d74ec658020bda648febc51f45c677cd2a3 (patch)
treeec0188e087a3bdcc39cc8e83d050730214e2994e /sys/cam
parent56747017c93fe2b157d540210ca731a4901ae11b (diff)
downloadFreeBSD-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.c113
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) {
OpenPOWER on IntegriCloud