Author: mav
Date: Fri Apr  8 14:42:29 2011
New Revision: 220454
URL: http://svn.freebsd.org/changeset/base/220454

Log:
  - 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:
  head/share/man/man4/ada.4
  head/sys/cam/ata/ata_da.c
  head/sys/conf/options

Modified: head/share/man/man4/ada.4
==============================================================================
--- head/share/man/man4/ada.4   Fri Apr  8 14:39:41 2011        (r220453)
+++ head/share/man/man4/ada.4   Fri Apr  8 14:42:29 2011        (r220454)
@@ -25,7 +25,7 @@
 .\"
 .\" $FreeBSD$
 .\"
-.Dd April 7, 2011
+.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,23 +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 kern.cam.ada.write_cache
+.It Va kern.cam.ada.write_cache
+.It Va kern.cam.ada. Ns Ar X Ns Va .write_cache
 .Pp
-This variable determines whether device write cache should be enabled or not.
+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.
-The default is currently enabled.
+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: head/sys/cam/ata/ata_da.c
==============================================================================
--- head/sys/cam/ata/ata_da.c   Fri Apr  8 14:39:41 2011        (r220453)
+++ head/sys/cam/ata/ata_da.c   Fri Apr  8 14:42:29 2011        (r220454)
@@ -27,6 +27,8 @@
 #include <sys/cdefs.h>
 __FBSDID("$FreeBSD$");
 
+#include "opt_ada.h"
+
 #include <sys/param.h>
 
 #ifdef _KERNEL
@@ -127,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;
@@ -618,11 +627,11 @@ 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)
+               if (ada_write_cache < 0 && softc->write_cache < 0)
                        break;
                if (softc->state != ADA_STATE_NORMAL)
                        break;
-               xpt_setup_ccb(&cgd.ccb_h, path, CAM_PRIORITY_NORMAL);
+               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)
@@ -647,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);
@@ -665,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);
 }
 
@@ -738,6 +773,10 @@ adaregister(struct cam_periph *periph, v
                softc->quirks = ((struct ada_quirk_entry *)match)->quirks;
        else
                softc->quirks = ADA_Q_NONE;
+       softc->write_cache = -1;
+       snprintf(announce_buf, sizeof(announce_buf),
+           "kern.cam.ada.%d.writa_cache", periph->unit_number);
+       TUNABLE_INT_FETCH(announce_buf, &softc->write_cache);
 
        bzero(&cpi, sizeof(cpi));
        xpt_setup_ccb(&cpi.ccb_h, periph->path, CAM_PRIORITY_NONE);
@@ -812,6 +851,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
@@ -832,7 +879,7 @@ adaregister(struct cam_periph *periph, v
            (ADA_DEFAULT_TIMEOUT * hz) / ADA_ORDEREDTAG_INTERVAL,
            adasendorderedtag, softc);
 
-       if (ada_write_cache >= 0 &&
+       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);
@@ -944,7 +991,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,
@@ -1062,7 +1147,8 @@ out:
                    0,
                    ada_default_timeout*1000);
 
-               ata_28bit_cmd(ataio, ATA_SETFEATURES, ada_write_cache ?
+               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);

Modified: head/sys/conf/options
==============================================================================
--- head/sys/conf/options       Fri Apr  8 14:39:41 2011        (r220453)
+++ head/sys/conf/options       Fri Apr  8 14:42:29 2011        (r220454)
@@ -305,6 +305,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
_______________________________________________
svn-src-head@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to