Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 19 Apr 2011 16:23:07 +0000 (UTC)
From:      Alexander Motin <mav@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-8@freebsd.org
Subject:   svn commit: r220841 - in stable/8: share/man/man4 sys/cam/ata sys/conf sys/modules/cam
Message-ID:  <201104191623.p3JGN7O3013560@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: mav
Date: Tue Apr 19 16:23:07 2011
New Revision: 220841
URL: http://svn.freebsd.org/changeset/base/220841

Log:
  MFC r220412, r220414, r220454, r220618, r220814:
  - Make ada(4) driver to control device write cache, same as ata(4) does.
  Add kern.cam.ada.write_cache sysctl/tunable to control it alike hw.ata.wc.
  - Add kern.cam.ada.X.write_cache tunables/sysctls to control write caching
  on per-device basis.
  - While adding support for per-device sysctls, merge from graid branch
  support for ADA_TEST_FAILURE kernel option, which opens few more sysctl,
  allowing to simulate read and write errors for testing purposes.

Modified:
  stable/8/share/man/man4/ada.4
  stable/8/sys/cam/ata/ata_da.c
  stable/8/sys/conf/options
  stable/8/sys/modules/cam/Makefile
Directory Properties:
  stable/8/share/man/man4/   (props changed)
  stable/8/sys/   (props changed)
  stable/8/sys/amd64/include/xen/   (props changed)
  stable/8/sys/cddl/contrib/opensolaris/   (props changed)
  stable/8/sys/contrib/dev/acpica/   (props changed)
  stable/8/sys/contrib/pf/   (props changed)

Modified: stable/8/share/man/man4/ada.4
==============================================================================
--- stable/8/share/man/man4/ada.4	Tue Apr 19 16:21:57 2011	(r220840)
+++ stable/8/share/man/man4/ada.4	Tue Apr 19 16:23:07 2011	(r220841)
@@ -25,7 +25,7 @@
 .\"
 .\" $FreeBSD$
 .\"
-.Dd October 24, 2010
+.Dd April 8, 2011
 .Dt ADA 4
 .Os
 .Sh NAME
@@ -101,7 +101,7 @@ variables and
 .Xr loader 8
 tunables:
 .Bl -tag -width 12
-.It kern.cam.ada.retry_count
+.It Va kern.cam.ada.retry_count
 .Pp
 This variable determines how many times the
 .Nm
@@ -111,18 +111,27 @@ the
 .Nm
 driver dump routine.
 This value currently defaults to 4.
-.It kern.cam.ada.default_timeout
+.It Va kern.cam.ada.default_timeout
 .Pp
 This variable determines how long the
 .Nm
 driver will wait before timing out an outstanding command.
 The units for this value are seconds, and the default is currently 30
 seconds.
-.It kern.cam.ada.spindown_shutdown
+.It Va kern.cam.ada.spindown_shutdown
 .Pp
 This variable determines whether to spin-down disks when shutting down.
 Set to 1 to enable spin-down, 0 to disable.  
 The default is currently enabled.
+.It Va kern.cam.ada.write_cache
+.It Va kern.cam.ada. Ns Ar X Ns Va .write_cache
+.Pp
+These variables determines whether device write cache should be enabled
+globally or per-device or disabled.
+Set to 1 to enable write cache, 0 to disable, -1 to leave it as-is.
+Values modified in runtime take effect only after device reset.
+The global default is currently enabled.
+The per-device default is to leave it as-is (follow global setting).
 .El
 .Sh FILES
 .Bl -tag -width ".Pa /dev/ada*" -compact

Modified: stable/8/sys/cam/ata/ata_da.c
==============================================================================
--- stable/8/sys/cam/ata/ata_da.c	Tue Apr 19 16:21:57 2011	(r220840)
+++ stable/8/sys/cam/ata/ata_da.c	Tue Apr 19 16:23:07 2011	(r220841)
@@ -27,6 +27,8 @@
 #include <sys/cdefs.h>
 __FBSDID("$FreeBSD$");
 
+#include "opt_ada.h"
+
 #include <sys/param.h>
 
 #ifdef _KERNEL
@@ -66,6 +68,7 @@ __FBSDID("$FreeBSD$");
 #define ATA_MAX_28BIT_LBA               268435455UL
 
 typedef enum {
+	ADA_STATE_WCACHE,
 	ADA_STATE_NORMAL
 } ada_state;
 
@@ -89,6 +92,7 @@ typedef enum {
 } ada_quirks;
 
 typedef enum {
+	ADA_CCB_WCACHE		= 0x01,
 	ADA_CCB_BUFFER_IO	= 0x03,
 	ADA_CCB_WAITING		= 0x04,
 	ADA_CCB_DUMP		= 0x05,
@@ -125,6 +129,13 @@ struct ada_softc {
 	int	 outstanding_cmds;
 	int	 trim_max_ranges;
 	int	 trim_running;
+	int	 write_cache;
+#ifdef ADA_TEST_FAILURE
+	int      force_read_error;
+	int      force_write_error;
+	int      periodic_read_error;
+	int      periodic_read_count;
+#endif
 	struct	 disk_params params;
 	struct	 disk *disk;
 	struct task		sysctl_task;
@@ -186,6 +197,10 @@ static void		adashutdown(void *arg, int 
 #define	ADA_DEFAULT_SPINDOWN_SHUTDOWN	1
 #endif
 
+#ifndef	ADA_DEFAULT_WRITE_CACHE
+#define	ADA_DEFAULT_WRITE_CACHE	1
+#endif
+
 /*
  * Most platforms map firmware geometry to actual, but some don't.  If
  * not overridden, default to nothing.
@@ -198,6 +213,7 @@ static int ada_retry_count = ADA_DEFAULT
 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_write_cache = ADA_DEFAULT_WRITE_CACHE;
 
 SYSCTL_NODE(_kern_cam, OID_AUTO, ada, CTLFLAG_RD, 0,
             "CAM Direct Access Disk driver");
@@ -213,6 +229,9 @@ TUNABLE_INT("kern.cam.ada.ada_send_order
 SYSCTL_INT(_kern_cam_ada, OID_AUTO, spindown_shutdown, CTLFLAG_RW,
            &ada_spindown_shutdown, 0, "Spin down upon shutdown");
 TUNABLE_INT("kern.cam.ada.spindown_shutdown", &ada_spindown_shutdown);
+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);
 
 /*
  * ADA_ORDEREDTAG_INTERVAL determines how often, relative
@@ -568,6 +587,7 @@ adaasync(void *callback_arg, u_int32_t c
 	struct cam_path *path, void *arg)
 {
 	struct cam_periph *periph;
+	struct ada_softc *softc;
 
 	periph = (struct cam_periph *)callback_arg;
 	switch (code) {
@@ -600,6 +620,28 @@ adaasync(void *callback_arg, u_int32_t c
 				"due to status 0x%x\n", status);
 		break;
 	}
+	case AC_SENT_BDR:
+	case AC_BUS_RESET:
+	{
+		struct ccb_getdev cgd;
+
+		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;
+		cam_periph_acquire(periph);
+		cam_freeze_devq_arg(periph->path,
+		    RELSIM_RELEASE_RUNLEVEL, CAM_RL_DEV + 1);
+		xpt_schedule(periph, CAM_PRIORITY_DEV);
+	}
 	default:
 		cam_periph_async(periph, code, path, arg);
 		break;
@@ -614,8 +656,12 @@ adasysctlinit(void *context, int pending
 	char tmpstr[80], tmpstr2[80];
 
 	periph = (struct cam_periph *)context;
-	if (cam_periph_acquire(periph) != CAM_REQ_CMP)
+
+	/* periph was held for us when this task was enqueued */
+	if (periph->flags & CAM_PERIPH_INVALID) {
+		cam_periph_release(periph);
 		return;
+	}
 
 	softc = (struct ada_softc *)periph->softc;
 	snprintf(tmpstr, sizeof(tmpstr), "CAM ADA unit %d", periph->unit_number);
@@ -632,6 +678,28 @@ adasysctlinit(void *context, int pending
 		return;
 	}
 
+	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
+	/*
+	 * Add a 'door bell' sysctl which allows one to set it from userland
+	 * and cause something bad to happen.  For the moment, we only allow
+	 * whacking the next read or write.
+	 */
+	SYSCTL_ADD_INT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree),
+		OID_AUTO, "force_read_error", CTLFLAG_RW | CTLFLAG_MPSAFE,
+		&softc->force_read_error, 0,
+		"Force a read error for the next N reads.");
+	SYSCTL_ADD_INT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree),
+		OID_AUTO, "force_write_error", CTLFLAG_RW | CTLFLAG_MPSAFE,
+		&softc->force_write_error, 0,
+		"Force a write error for the next N writes.");
+	SYSCTL_ADD_INT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree),
+		OID_AUTO, "periodic_read_error", CTLFLAG_RW | CTLFLAG_MPSAFE,
+		&softc->periodic_read_error, 0,
+		"Force a read error every N reads (don't set too low).");
+#endif
 	cam_periph_release(periph);
 }
 
@@ -691,7 +759,6 @@ adaregister(struct cam_periph *periph, v
 	}
 	if (cgd->ident_data.support.command2 & ATA_SUPPORT_CFA)
 		softc->flags |= ADA_FLAG_CAN_CFA;
-	softc->state = ADA_STATE_NORMAL;
 
 	periph->softc = softc;
 
@@ -717,7 +784,12 @@ adaregister(struct cam_periph *periph, v
 	/*
 	 * Register this media as a disk
 	 */
+	(void)cam_periph_hold(periph, PRIBIO);
 	mtx_unlock(periph->sim->mtx);
+	softc->write_cache = -1;
+	snprintf(announce_buf, sizeof(announce_buf),
+	    "kern.cam.ada.%d.write_cache", periph->unit_number);
+	TUNABLE_INT_FETCH(announce_buf, &softc->write_cache);
 	adagetparams(periph, cgd);
 	softc->disk = disk_alloc();
 	softc->disk->d_open = adaopen;
@@ -764,6 +836,7 @@ adaregister(struct cam_periph *periph, v
 
 	disk_create(softc->disk, DISK_VERSION);
 	mtx_lock(periph->sim->mtx);
+	cam_periph_unhold(periph);
 
 	dp = &softc->params;
 	snprintf(announce_buf, sizeof(announce_buf),
@@ -774,6 +847,14 @@ adaregister(struct cam_periph *periph, v
 		dp->secsize, dp->heads,
 		dp->secs_per_track, dp->cylinders);
 	xpt_announce_periph(periph, announce_buf);
+
+	/*
+	 * Create our sysctl variables, now that we know
+	 * we have successfully attached.
+	 */
+	cam_periph_acquire(periph);
+	taskqueue_enqueue(taskqueue_thread, &softc->sysctl_task);
+
 	/*
 	 * Add async callbacks for bus reset and
 	 * bus device reset calls.  I don't bother
@@ -782,7 +863,7 @@ adaregister(struct cam_periph *periph, v
 	 * them and the only alternative would be to
 	 * not attach the device on failure.
 	 */
-	xpt_register_async(AC_LOST_DEVICE,
+	xpt_register_async(AC_SENT_BDR | AC_BUS_RESET | AC_LOST_DEVICE,
 			   adaasync, periph, periph->path);
 
 	/*
@@ -794,6 +875,16 @@ adaregister(struct cam_periph *periph, v
 	    (ADA_DEFAULT_TIMEOUT * hz) / ADA_ORDEREDTAG_INTERVAL,
 	    adasendorderedtag, softc);
 
+	if ((ada_write_cache >= 0 || softc->write_cache >= 0) &&
+	    cgd->ident_data.support.command1 & ATA_SUPPORT_WRITECACHE) {
+		softc->state = ADA_STATE_WCACHE;
+		cam_periph_acquire(periph);
+		cam_freeze_devq_arg(periph->path,
+		    RELSIM_RELEASE_RUNLEVEL, CAM_RL_DEV + 1);
+		xpt_schedule(periph, CAM_PRIORITY_DEV);
+	} else
+		softc->state = ADA_STATE_NORMAL;
+
 	return(CAM_REQ_CMP);
 }
 
@@ -896,7 +987,45 @@ adastart(struct cam_periph *periph, unio
 		{
 			uint64_t lba = bp->bio_pblkno;
 			uint16_t count = bp->bio_bcount / softc->params.secsize;
+#ifdef ADA_TEST_FAILURE
+			int fail = 0;
 
+			/*
+			 * Support the failure ioctls.  If the command is a
+			 * read, and there are pending forced read errors, or
+			 * if a write and pending write errors, then fail this
+			 * operation with EIO.  This is useful for testing
+			 * purposes.  Also, support having every Nth read fail.
+			 *
+			 * This is a rather blunt tool.
+			 */
+			if (bp->bio_cmd == BIO_READ) {
+				if (softc->force_read_error) {
+					softc->force_read_error--;
+					fail = 1;
+				}
+				if (softc->periodic_read_error > 0) {
+					if (++softc->periodic_read_count >=
+					    softc->periodic_read_error) {
+						softc->periodic_read_count = 0;
+						fail = 1;
+					}
+				}
+			} else {
+				if (softc->force_write_error) {
+					softc->force_write_error--;
+					fail = 1;
+				}
+			}
+			if (fail) {
+				bp->bio_error = EIO;
+				bp->bio_flags |= BIO_ERROR;
+				biodone(bp);
+				xpt_release_ccb(start_ccb);
+				adaschedule(periph);
+				return;
+			}
+#endif
 			cam_fill_ataio(ataio,
 			    ada_retry_count,
 			    adadone,
@@ -1003,6 +1132,24 @@ out:
 		adaschedule(periph);
 		break;
 	}
+	case ADA_STATE_WCACHE:
+	{
+		cam_fill_ataio(ataio,
+		    1,
+		    adadone,
+		    CAM_DIR_NONE,
+		    0,
+		    NULL,
+		    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;
+		xpt_action(start_ccb);
+		break;
+	}
 	}
 }
 
@@ -1091,6 +1238,36 @@ adadone(struct cam_periph *periph, union
 			biodone(bp);
 		break;
 	}
+	case ADA_CCB_WCACHE:
+	{
+		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);
+			}
+		}
+
+		softc->state = ADA_STATE_NORMAL;
+		/*
+		 * 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.
+		 */
+		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_WAITING:
 	{
 		/* Caller will release the CCB */

Modified: stable/8/sys/conf/options
==============================================================================
--- stable/8/sys/conf/options	Tue Apr 19 16:21:57 2011	(r220840)
+++ stable/8/sys/conf/options	Tue Apr 19 16:23:07 2011	(r220841)
@@ -302,6 +302,9 @@ SCSI_DELAY		opt_scsi.h
 SCSI_NO_SENSE_STRINGS	opt_scsi.h
 SCSI_NO_OP_STRINGS	opt_scsi.h
 
+# Options used only in cam/ata/ata_da.c
+ADA_TEST_FAILURE       opt_ada.h
+
 # Options used only in cam/scsi/scsi_cd.c
 CHANGER_MIN_BUSY_SECONDS	opt_cd.h
 CHANGER_MAX_BUSY_SECONDS	opt_cd.h

Modified: stable/8/sys/modules/cam/Makefile
==============================================================================
--- stable/8/sys/modules/cam/Makefile	Tue Apr 19 16:21:57 2011	(r220840)
+++ stable/8/sys/modules/cam/Makefile	Tue Apr 19 16:23:07 2011	(r220841)
@@ -8,6 +8,7 @@ KMOD=	cam
 
 # See sys/conf/options for the flags that go into the different opt_*.h files.
 SRCS=	opt_cam.h
+SRCS+=	opt_ada.h
 SRCS+=	opt_scsi.h
 SRCS+=	opt_cd.h
 SRCS+=	opt_pt.h



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