diff options
author | mav <mav@FreeBSD.org> | 2011-04-08 14:42:29 +0000 |
---|---|---|
committer | mav <mav@FreeBSD.org> | 2011-04-08 14:42:29 +0000 |
commit | 6028082f6855f840b72a34f8a32690727174aa11 (patch) | |
tree | 89503e18f01ec40ada8782c1e46de0da1799ef60 /sys/cam | |
parent | cd5ddda20d6f9cd6771a111ae606c3180aea9b94 (diff) | |
download | FreeBSD-src-6028082f6855f840b72a34f8a32690727174aa11.zip FreeBSD-src-6028082f6855f840b72a34f8a32690727174aa11.tar.gz |
- 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.
Diffstat (limited to 'sys/cam')
-rw-r--r-- | sys/cam/ata/ata_da.c | 98 |
1 files changed, 92 insertions, 6 deletions
diff --git a/sys/cam/ata/ata_da.c b/sys/cam/ata/ata_da.c index cf0cb05..945f13e 100644 --- a/sys/cam/ata/ata_da.c +++ b/sys/cam/ata/ata_da.c @@ -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 code, 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, void *arg) 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, void *arg) 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, void *arg) (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, union ccb *start_ccb) { 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); |