Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 29 Jul 2011 20:32:27 +0000 (UTC)
From:      Alexander Motin <mav@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r224497 - head/sys/cam/ata
Message-ID:  <201107292032.p6TKWRWl064999@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: mav
Date: Fri Jul 29 20:32:27 2011
New Revision: 224497
URL: http://svn.freebsd.org/changeset/base/224497

Log:
  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)

Modified:
  head/sys/cam/ata/ata_da.c

Modified: head/sys/cam/ata/ata_da.c
==============================================================================
--- head/sys/cam/ata/ata_da.c	Fri Jul 29 20:30:28 2011	(r224496)
+++ head/sys/cam/ata/ata_da.c	Fri Jul 29 20:32:27 2011	(r224497)
@@ -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_DEF
 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_shutd
 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 c
 
 		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, v
 	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, v
 	    (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
 {
 	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
 			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) {



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201107292032.p6TKWRWl064999>