diff options
| author | Kenneth D. Merry <ken@FreeBSD.org> | 2022-01-13 21:07:58 +0000 |
|---|---|---|
| committer | Kenneth D. Merry <ken@FreeBSD.org> | 2022-02-10 13:38:00 +0000 |
| commit | 75900d1eb898fac8c8c4a793b828309bd2bd684e (patch) | |
| tree | 699795a369253a8de8d164fcf50aac5201d8844b /sys/cam | |
| parent | be1776da76c77f9e5015a3f3ef46dbef723c83bb (diff) | |
Diffstat (limited to 'sys/cam')
| -rw-r--r-- | sys/cam/scsi/scsi_sa.c | 591 |
1 files changed, 533 insertions, 58 deletions
diff --git a/sys/cam/scsi/scsi_sa.c b/sys/cam/scsi/scsi_sa.c index 9441e0d4673b..5bd104237bdb 100644 --- a/sys/cam/scsi/scsi_sa.c +++ b/sys/cam/scsi/scsi_sa.c @@ -4,7 +4,7 @@ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 1999, 2000 Matthew Jacob - * Copyright (c) 2013, 2014, 2015 Spectra Logic Corporation + * Copyright (c) 2013, 2014, 2015, 2021 Spectra Logic Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -85,7 +85,7 @@ __FBSDID("$FreeBSD$"); #define SA_ERASE_TIMEOUT 4 * 60 #endif #ifndef SA_REP_DENSITY_TIMEOUT -#define SA_REP_DENSITY_TIMEOUT 90 +#define SA_REP_DENSITY_TIMEOUT 1 #endif #define SCSIOP_TIMEOUT (60 * 1000) /* not an option */ @@ -115,7 +115,7 @@ __FBSDID("$FreeBSD$"); static MALLOC_DEFINE(M_SCSISA, "SCSI sa", "SCSI sequential access buffers"); typedef enum { - SA_STATE_NORMAL, SA_STATE_ABNORMAL + SA_STATE_NORMAL, SA_STATE_PROBE, SA_STATE_ABNORMAL } sa_state; #define ccb_pflags ppriv_field0 @@ -125,27 +125,28 @@ typedef enum { #define SA_POSITION_UPDATED 0x1 typedef enum { - SA_FLAG_OPEN = 0x0001, - SA_FLAG_FIXED = 0x0002, - SA_FLAG_TAPE_LOCKED = 0x0004, - SA_FLAG_TAPE_MOUNTED = 0x0008, - SA_FLAG_TAPE_WP = 0x0010, - SA_FLAG_TAPE_WRITTEN = 0x0020, - SA_FLAG_EOM_PENDING = 0x0040, - SA_FLAG_EIO_PENDING = 0x0080, - SA_FLAG_EOF_PENDING = 0x0100, + SA_FLAG_OPEN = 0x00001, + SA_FLAG_FIXED = 0x00002, + SA_FLAG_TAPE_LOCKED = 0x00004, + SA_FLAG_TAPE_MOUNTED = 0x00008, + SA_FLAG_TAPE_WP = 0x00010, + SA_FLAG_TAPE_WRITTEN = 0x00020, + SA_FLAG_EOM_PENDING = 0x00040, + SA_FLAG_EIO_PENDING = 0x00080, + SA_FLAG_EOF_PENDING = 0x00100, SA_FLAG_ERR_PENDING = (SA_FLAG_EOM_PENDING|SA_FLAG_EIO_PENDING| SA_FLAG_EOF_PENDING), - SA_FLAG_INVALID = 0x0200, - SA_FLAG_COMP_ENABLED = 0x0400, - SA_FLAG_COMP_SUPP = 0x0800, - SA_FLAG_COMP_UNSUPP = 0x1000, - SA_FLAG_TAPE_FROZEN = 0x2000, - SA_FLAG_PROTECT_SUPP = 0x4000, + SA_FLAG_INVALID = 0x00200, + SA_FLAG_COMP_ENABLED = 0x00400, + SA_FLAG_COMP_SUPP = 0x00800, + SA_FLAG_COMP_UNSUPP = 0x01000, + SA_FLAG_TAPE_FROZEN = 0x02000, + SA_FLAG_PROTECT_SUPP = 0x04000, SA_FLAG_COMPRESSION = (SA_FLAG_COMP_SUPP|SA_FLAG_COMP_ENABLED| SA_FLAG_COMP_UNSUPP), - SA_FLAG_SCTX_INIT = 0x8000 + SA_FLAG_SCTX_INIT = 0x08000, + SA_FLAG_RSOC_TO_TRY = 0x10000, } sa_flags; typedef enum { @@ -155,6 +156,64 @@ typedef enum { } sa_mode; typedef enum { + SA_TIMEOUT_ERASE, + SA_TIMEOUT_LOAD, + SA_TIMEOUT_LOCATE, + SA_TIMEOUT_MODE_SELECT, + SA_TIMEOUT_MODE_SENSE, + SA_TIMEOUT_PREVENT, + SA_TIMEOUT_READ, + SA_TIMEOUT_READ_BLOCK_LIMITS, + SA_TIMEOUT_READ_POSITION, + SA_TIMEOUT_REP_DENSITY, + SA_TIMEOUT_RESERVE, + SA_TIMEOUT_REWIND, + SA_TIMEOUT_SPACE, + SA_TIMEOUT_TUR, + SA_TIMEOUT_WRITE, + SA_TIMEOUT_WRITE_FILEMARKS, + SA_TIMEOUT_TYPE_MAX +} sa_timeout_types; + +/* + * These are the default timeout values that apply to all tape drives. + * + * We get timeouts from the following places in order of increasing + * priority: + * 1. Driver default timeouts. (Set in the structure below.) + * 2. Timeouts loaded from the drive via REPORT SUPPORTED OPERATION + * CODES. (If the drive supports it, SPC-4/LTO-5 and newer should.) + * 3. Global loader tunables, used for all sa(4) driver instances on + * a machine. + * 4. Instance-specific loader tunables, used for say sa5. + * 5. On the fly user sysctl changes. + * + * Each step will overwrite the timeout value set from the one + * before, so you go from general to most specific. + */ +static struct sa_timeout_desc { + const char *desc; + int value; +} sa_default_timeouts[SA_TIMEOUT_TYPE_MAX] = { + {"erase", ERASE_TIMEOUT}, + {"load", REWIND_TIMEOUT}, + {"locate", SPACE_TIMEOUT}, + {"mode_select", SCSIOP_TIMEOUT}, + {"mode_sense", SCSIOP_TIMEOUT}, + {"prevent", SCSIOP_TIMEOUT}, + {"read", IO_TIMEOUT}, + {"read_block_limits", SCSIOP_TIMEOUT}, + {"read_position", SCSIOP_TIMEOUT}, + {"report_density", REP_DENSITY_TIMEOUT}, + {"reserve", SCSIOP_TIMEOUT}, + {"rewind", REWIND_TIMEOUT}, + {"space", SPACE_TIMEOUT}, + {"tur", SCSIOP_TIMEOUT}, + {"write", IO_TIMEOUT}, + {"write_filemarks", IO_TIMEOUT}, +}; + +typedef enum { SA_PARAM_NONE = 0x000, SA_PARAM_BLOCKSIZE = 0x001, SA_PARAM_DENSITY = 0x002, @@ -356,6 +415,7 @@ struct sa_softc { uint8_t density_type_bits[SA_DENSITY_TYPES]; int density_info_valid[SA_DENSITY_TYPES]; uint8_t density_info[SA_DENSITY_TYPES][SRDS_MAX_LENGTH]; + int timeout_info[SA_TIMEOUT_TYPE_MAX]; struct sa_prot_info prot_info; @@ -414,6 +474,8 @@ struct sa_softc { struct task sysctl_task; struct sysctl_ctx_list sysctl_ctx; struct sysctl_oid *sysctl_tree; + struct sysctl_ctx_list sysctl_timeout_ctx; + struct sysctl_oid *sysctl_timeout_tree; }; struct sa_quirk_entry { @@ -586,6 +648,8 @@ static int saspace(struct cam_periph *periph, int count, scsi_space_code code); static void sadevgonecb(void *arg); static void sasetupdev(struct sa_softc *softc, struct cdev *dev); +static void saloadtotunables(struct sa_softc *softc); +static void sasysctlinit(void *context, int pending); static int samount(struct cam_periph *, int, struct cdev *); static int saretension(struct cam_periph *periph); static int sareservereleaseunit(struct cam_periph *periph, @@ -603,6 +667,7 @@ static void safilldenstypesb(struct sbuf *sb, int *indent, int is_density); static void safilldensitysb(struct sa_softc *softc, int *indent, struct sbuf *sb); +static void saloadtimeouts(struct sa_softc *softc, union ccb *ccb); #ifndef SA_DEFAULT_IO_SPLIT #define SA_DEFAULT_IO_SPLIT 0 @@ -2212,7 +2277,9 @@ sacleanup(struct cam_periph *periph) cam_periph_unlock(periph); if ((softc->flags & SA_FLAG_SCTX_INIT) != 0 - && sysctl_ctx_free(&softc->sysctl_ctx) != 0) + && (((softc->sysctl_timeout_tree != NULL) + && (sysctl_ctx_free(&softc->sysctl_timeout_ctx) != 0)) + || sysctl_ctx_free(&softc->sysctl_ctx) != 0)) xpt_print(periph->path, "can't remove sysctl context\n"); cam_periph_lock(periph); @@ -2283,12 +2350,47 @@ sasetupdev(struct sa_softc *softc, struct cdev *dev) softc->num_devs_to_destroy++; } +/* + * Load the global (for all sa(4) instances) and per-instance tunable + * values for timeouts for various sa(4) commands. This should be run + * after the default timeouts are fetched from the drive, so the user's + * preference will override the drive's defaults. + */ +static void +saloadtotunables(struct sa_softc *softc) +{ + int i; + char tmpstr[80]; + + for (i = 0; i < SA_TIMEOUT_TYPE_MAX; i++) { + int tmpval, retval; + + /* First grab any global timeout setting */ + snprintf(tmpstr, sizeof(tmpstr), "kern.cam.sa.timeout.%s", + sa_default_timeouts[i].desc); + retval = TUNABLE_INT_FETCH(tmpstr, &tmpval); + if (retval != 0) + softc->timeout_info[i] = tmpval; + + /* + * Then overwrite any global timeout settings with + * per-instance timeout settings. + */ + snprintf(tmpstr, sizeof(tmpstr), "kern.cam.sa.%u.timeout.%s", + softc->periph->unit_number, sa_default_timeouts[i].desc); + retval = TUNABLE_INT_FETCH(tmpstr, &tmpval); + if (retval != 0) + softc->timeout_info[i] = tmpval; + } +} + static void sasysctlinit(void *context, int pending) { struct cam_periph *periph; struct sa_softc *softc; - char tmpstr[32], tmpstr2[16]; + char tmpstr[64], tmpstr2[16]; + int i; periph = (struct cam_periph *)context; /* @@ -2323,6 +2425,32 @@ sasysctlinit(void *context, int pending) OID_AUTO, "inject_eom", CTLFLAG_RW, &softc->inject_eom, 0, "Queue EOM for the next write/read"); + sysctl_ctx_init(&softc->sysctl_timeout_ctx); + softc->sysctl_timeout_tree = SYSCTL_ADD_NODE(&softc->sysctl_timeout_ctx, + SYSCTL_CHILDREN(softc->sysctl_tree), OID_AUTO, "timeout", + CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "Timeouts"); + if (softc->sysctl_timeout_tree == NULL) + goto bailout; + + for (i = 0; i < SA_TIMEOUT_TYPE_MAX; i++) { + snprintf(tmpstr, sizeof(tmpstr), "%s timeout", + sa_default_timeouts[i].desc); + + /* + * Do NOT change this sysctl declaration to also load any + * tunable values for this sa(4) instance. In other words, + * do not change this to CTLFLAG_RWTUN. This function is + * run in parallel with the probe routine that fetches + * recommended timeout values from the tape drive, and we + * don't want the values from the drive to override the + * user's preference. + */ + SYSCTL_ADD_INT(&softc->sysctl_timeout_ctx, + SYSCTL_CHILDREN(softc->sysctl_timeout_tree), + OID_AUTO, sa_default_timeouts[i].desc, CTLFLAG_RW, + &softc->timeout_info[i], 0, tmpstr); + } + bailout: /* * Release the reference that was held when this task was enqueued. @@ -2340,6 +2468,7 @@ saregister(struct cam_periph *periph, void *arg) caddr_t match; char tmpstr[80]; int error; + int i; cgd = (struct ccb_getdev *)arg; if (cgd == NULL) { @@ -2384,6 +2513,15 @@ saregister(struct cam_periph *periph, void *arg) } else softc->quirks = SA_QUIRK_NONE; + + /* + * Initialize the default timeouts. If this drive supports + * timeout descriptors we'll overwrite these values with the + * recommended timeouts from the drive. + */ + for (i = 0; i < SA_TIMEOUT_TYPE_MAX; i++) + softc->timeout_info[i] = sa_default_timeouts[i].value; + /* * Long format data for READ POSITION was introduced in SSC, which * was after SCSI-2. (Roughly equivalent to SCSI-3.) If the drive @@ -2397,6 +2535,19 @@ saregister(struct cam_periph *periph, void *arg) if (cgd->inq_data.version <= SCSI_REV_CCS) softc->quirks |= SA_QUIRK_NO_LONG_POS; + /* + * The SCSI REPORT SUPPORTED OPERATION CODES command was added in + * SPC-4. That command optionally includes timeout data for + * different commands. Timeout values can vary wildly among + * different drives, so if the drive itself has recommended values, + * we will try to use them. Set this flag to indicate we're going + * to ask the drive for timeout data. This flag also tells us to + * wait on loading timeout tunables so we can properly override + * timeouts with any user-specified values. + */ + if (SID_ANSI_REV(&cgd->inq_data) >= SCSI_REV_SPC4) + softc->flags |= SA_FLAG_RSOC_TO_TRY; + if (cgd->inq_data.spc3_flags & SPC3_SID_PROTECT) { struct ccb_dev_advinfo cdai; struct scsi_vpd_extended_inquiry_data ext_inq; @@ -2545,7 +2696,9 @@ saregister(struct cam_periph *periph, void *arg) softc->density_type_bits[3] = SRDS_MEDIUM_TYPE | SRDS_MEDIA; /* * Bump the peripheral refcount for the sysctl thread, in case we - * get invalidated before the thread has a chance to run. + * get invalidated before the thread has a chance to run. Note + * that this runs in parallel with the probe for the timeout + * values. */ cam_periph_acquire(periph); taskqueue_enqueue(taskqueue_thread, &softc->sysctl_task); @@ -2556,8 +2709,41 @@ saregister(struct cam_periph *periph, void *arg) */ xpt_register_async(AC_LOST_DEVICE, saasync, periph, periph->path); - xpt_announce_periph(periph, NULL); - xpt_announce_quirks(periph, softc->quirks, SA_QUIRK_BIT_STRING); + /* + * See comment above, try fetching timeout values for drives that + * might support it. Otherwise, use the defaults. + * + * We get timeouts from the following places in order of increasing + * priority: + * 1. Driver default timeouts. + * 2. Timeouts loaded from the drive via REPORT SUPPORTED OPERATION + * CODES. (We kick that off here if SA_FLAG_RSOC_TO_TRY is set.) + * 3. Global loader tunables, used for all sa(4) driver instances on + * a machine. + * 4. Instance-specific loader tunables, used for say sa5. + * 5. On the fly user sysctl changes. + * + * Each step will overwrite the timeout value set from the one + * before, so you go from general to most specific. + */ + if (softc->flags & SA_FLAG_RSOC_TO_TRY) { + /* + * Bump the peripheral refcount while we are probing. + */ + cam_periph_acquire(periph); + softc->state = SA_STATE_PROBE; + xpt_schedule(periph, CAM_PRIORITY_DEV); + } else { + /* + * This drive doesn't support Report Supported Operation + * Codes, so we load the tunables at this point to bring + * in any user preferences. + */ + saloadtotunables(softc); + + xpt_announce_periph(periph, NULL); + xpt_announce_quirks(periph, softc->quirks, SA_QUIRK_BIT_STRING); + } return (CAM_REQ_CMP); } @@ -2752,7 +2938,9 @@ again: (softc->flags & SA_FLAG_FIXED) != 0, length, (bp->bio_flags & BIO_UNMAPPED) != 0 ? (void *)bp : bp->bio_data, bp->bio_bcount, SSD_FULL_SIZE, - IO_TIMEOUT); + (bp->bio_cmd == BIO_READ) ? + softc->timeout_info[SA_TIMEOUT_READ] : + softc->timeout_info[SA_TIMEOUT_WRITE]); start_ccb->ccb_h.ccb_pflags &= ~SA_POSITION_UPDATED; start_ccb->ccb_h.ccb_bp = bp; bp = bioq_first(&softc->bio_queue); @@ -2765,6 +2953,59 @@ again: } break; } + case SA_STATE_PROBE: { + int num_opcodes; + size_t alloc_len; + uint8_t *params; + + /* + * This is an arbitrary number. An IBM LTO-6 drive reports + * 67 entries, and an IBM LTO-9 drive reports 71 entries. + * There can theoretically be more than 256 because + * service actions of a particular opcode are reported + * separately, but we're far enough ahead of the practical + * number here that we don't need to implement logic to + * retry if we don't get all the timeout descriptors. + */ + num_opcodes = 256; + + alloc_len = num_opcodes * + (sizeof(struct scsi_report_supported_opcodes_descr) + + sizeof(struct scsi_report_supported_opcodes_timeout)); + + params = malloc(alloc_len, M_SCSISA, M_NOWAIT| M_ZERO); + if (params == NULL) { + /* + * If this happens, go with default + * timeouts and announce the drive. + */ + saloadtotunables(softc); + + softc->state = SA_STATE_NORMAL; + + xpt_announce_periph(periph, NULL); + xpt_announce_quirks(periph, softc->quirks, + SA_QUIRK_BIT_STRING); + xpt_release_ccb(start_ccb); + cam_periph_release_locked(periph); + return; + } + + scsi_report_supported_opcodes(&start_ccb->csio, + /*retries*/ 3, + /*cbfcnp*/ sadone, + /*tag_action*/ MSG_SIMPLE_Q_TAG, + /*options*/ RSO_RCTD, + /*req_opcode*/ 0, + /*req_service_action*/ 0, + /*data_ptr*/ params, + /*dxfer_len*/ alloc_len, + /*sense_len*/ SSD_FULL_SIZE, + /*timeout*/ softc->timeout_info[SA_TIMEOUT_TUR]); + + xpt_action(start_ccb); + break; + } case SA_STATE_ABNORMAL: default: panic("state 0x%x in sastart", softc->state); @@ -2782,17 +3023,79 @@ sadone(struct cam_periph *periph, union ccb *done_ccb) softc = (struct sa_softc *)periph->softc; csio = &done_ccb->csio; - - softc->dsreg = MTIO_DSREG_REST; - bp = (struct bio *)done_ccb->ccb_h.ccb_bp; error = 0; - if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { - if ((error = saerror(done_ccb, 0, 0)) == ERESTART) { + + if (softc->state == SA_STATE_NORMAL) { + softc->dsreg = MTIO_DSREG_REST; + bp = (struct bio *)done_ccb->ccb_h.ccb_bp; + + if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { + if ((error = saerror(done_ccb, 0, 0)) == ERESTART) { + /* + * A retry was scheduled, so just return. + */ + return; + } + } + } else if (softc->state == SA_STATE_PROBE) { + bp = NULL; + if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { /* - * A retry was scheduled, so just return. + * Note that on probe, we just run through + * cam_periph_error(), since saerror() has a lot of + * special handling for I/O errors. We don't need + * that to get the opcodes. We either succeed + * after a retry or two, or give up. We don't + * print sense, we don't need to worry the user if + * this drive doesn't support timeout descriptors. */ - return; + if ((error = cam_periph_error(done_ccb, 0, + SF_NO_PRINT)) == ERESTART) { + /* + * A retry was scheduled, so just return. + */ + return; + } else if (error != 0) { + /* We failed to get opcodes. Give up. */ + + saloadtotunables(softc); + + softc->state = SA_STATE_NORMAL; + + xpt_release_ccb(done_ccb); + + xpt_announce_periph(periph, NULL); + xpt_announce_quirks(periph, softc->quirks, + SA_QUIRK_BIT_STRING); + cam_periph_release_locked(periph); + return; + } } + /* + * At this point, we have succeeded, so load the timeouts + * and go into the normal state. + */ + softc->state = SA_STATE_NORMAL; + + /* + * First, load the timeouts we got from the drive. + */ + saloadtimeouts(softc, done_ccb); + + /* + * Next, overwrite the timeouts from the drive with any + * loader tunables that the user set. + */ + saloadtotunables(softc); + + xpt_release_ccb(done_ccb); + xpt_announce_periph(periph, NULL); + xpt_announce_quirks(periph, softc->quirks, + SA_QUIRK_BIT_STRING); + cam_periph_release_locked(periph); + return; + } else { + panic("state 0x%x in sadone", softc->state); } if (error == EIO) { @@ -2890,13 +3193,15 @@ samount(struct cam_periph *periph, int oflags, struct cdev *dev) if (softc->flags & SA_FLAG_TAPE_MOUNTED) { ccb = cam_periph_getccb(periph, 1); scsi_test_unit_ready(&ccb->csio, 0, NULL, - MSG_SIMPLE_Q_TAG, SSD_FULL_SIZE, IO_TIMEOUT); + MSG_SIMPLE_Q_TAG, SSD_FULL_SIZE, + softc->timeout_info[SA_TIMEOUT_TUR]); error = cam_periph_runccb(ccb, saerror, 0, SF_NO_PRINT, softc->device_stats); if (error == ENXIO) { softc->flags &= ~SA_FLAG_TAPE_MOUNTED; scsi_test_unit_ready(&ccb->csio, 0, NULL, - MSG_SIMPLE_Q_TAG, SSD_FULL_SIZE, IO_TIMEOUT); + MSG_SIMPLE_Q_TAG, SSD_FULL_SIZE, + softc->timeout_info[SA_TIMEOUT_TUR]); error = cam_periph_runccb(ccb, saerror, 0, SF_NO_PRINT, softc->device_stats); } else if (error) { @@ -2917,7 +3222,8 @@ samount(struct cam_periph *periph, int oflags, struct cdev *dev) } ccb = cam_periph_getccb(periph, 1); scsi_test_unit_ready(&ccb->csio, 0, NULL, - MSG_SIMPLE_Q_TAG, SSD_FULL_SIZE, IO_TIMEOUT); + MSG_SIMPLE_Q_TAG, SSD_FULL_SIZE, + softc->timeout_info[SA_TIMEOUT_TUR]); error = cam_periph_runccb(ccb, saerror, 0, SF_NO_PRINT, softc->device_stats); } @@ -2938,7 +3244,8 @@ samount(struct cam_periph *periph, int oflags, struct cdev *dev) * *Very* first off, make sure we're loaded to BOT. */ scsi_load_unload(&ccb->csio, 2, NULL, MSG_SIMPLE_Q_TAG, FALSE, - FALSE, FALSE, 1, SSD_FULL_SIZE, REWIND_TIMEOUT); + FALSE, FALSE, 1, SSD_FULL_SIZE, + softc->timeout_info[SA_TIMEOUT_LOAD]); error = cam_periph_runccb(ccb, saerror, 0, SF_NO_PRINT, softc->device_stats); @@ -2947,7 +3254,8 @@ samount(struct cam_periph *periph, int oflags, struct cdev *dev) */ if (error) { scsi_rewind(&ccb->csio, 2, NULL, MSG_SIMPLE_Q_TAG, - FALSE, SSD_FULL_SIZE, REWIND_TIMEOUT); + FALSE, SSD_FULL_SIZE, + softc->timeout_info[SA_TIMEOUT_REWIND]); error = cam_periph_runccb(ccb, saerror, 0, SF_NO_PRINT, softc->device_stats); } @@ -2976,11 +3284,12 @@ samount(struct cam_periph *periph, int oflags, struct cdev *dev) scsi_sa_read_write(&ccb->csio, 0, NULL, MSG_SIMPLE_Q_TAG, 1, FALSE, 0, 8192, (void *) rblim, 8192, SSD_FULL_SIZE, - IO_TIMEOUT); + softc->timeout_info[SA_TIMEOUT_READ]); (void) cam_periph_runccb(ccb, saerror, 0, SF_NO_PRINT, softc->device_stats); scsi_rewind(&ccb->csio, 1, NULL, MSG_SIMPLE_Q_TAG, - FALSE, SSD_FULL_SIZE, REWIND_TIMEOUT); + FALSE, SSD_FULL_SIZE, + softc->timeout_info[SA_TIMEOUT_REWIND]); error = cam_periph_runccb(ccb, saerror, CAM_RETRY_SELTO, SF_NO_PRINT | SF_RETRY_UA, softc->device_stats); @@ -2996,7 +3305,8 @@ samount(struct cam_periph *periph, int oflags, struct cdev *dev) * Next off, determine block limits. */ scsi_read_block_limits(&ccb->csio, 5, NULL, MSG_SIMPLE_Q_TAG, - rblim, SSD_FULL_SIZE, SCSIOP_TIMEOUT); + rblim, SSD_FULL_SIZE, + softc->timeout_info[SA_TIMEOUT_READ_BLOCK_LIMITS]); error = cam_periph_runccb(ccb, saerror, CAM_RETRY_SELTO, SF_NO_PRINT | SF_RETRY_UA, softc->device_stats); @@ -3610,7 +3920,7 @@ retry: scsi_mode_sense(&ccb->csio, 5, NULL, MSG_SIMPLE_Q_TAG, FALSE, SMS_PAGE_CTRL_CURRENT, (params_to_get & SA_PARAM_COMPRESSION) ? cpage : SMS_VENDOR_SPECIFIC_PAGE, mode_buffer, mode_buffer_len, - SSD_FULL_SIZE, SCSIOP_TIMEOUT); + SSD_FULL_SIZE, softc->timeout_info[SA_TIMEOUT_MODE_SENSE]); error = cam_periph_runccb(ccb, saerror, 0, SF_NO_PRINT, softc->device_stats); @@ -3672,7 +3982,7 @@ retry: scsi_mode_sense(&ccb->csio, 2, NULL, MSG_SIMPLE_Q_TAG, FALSE, SMS_PAGE_CTRL_CURRENT, SMS_VENDOR_SPECIFIC_PAGE, mode_buffer, mode_buffer_len, SSD_FULL_SIZE, - SCSIOP_TIMEOUT); + softc->timeout_info[SA_TIMEOUT_MODE_SENSE]); error = cam_periph_runccb(ccb, saerror, 0, SF_NO_PRINT, softc->device_stats); @@ -3739,7 +4049,8 @@ retry: /*data_ptr*/ softc->density_info[i], /*length*/ sizeof(softc->density_info[i]), /*sense_len*/ SSD_FULL_SIZE, - /*timeout*/ REP_DENSITY_TIMEOUT); + /*timeout*/ + softc->timeout_info[SA_TIMEOUT_REP_DENSITY]); error = cam_periph_runccb(ccb, saerror, 0, SF_NO_PRINT, softc->device_stats); status = ccb->ccb_h.status & CAM_STATUS_MASK; @@ -3801,7 +4112,8 @@ retry: /*param_len*/ dp_len, /*minimum_cmd_size*/ 10, /*sense_len*/ SSD_FULL_SIZE, - /*timeout*/ SCSIOP_TIMEOUT); + /*timeout*/ + softc->timeout_info[SA_TIMEOUT_MODE_SENSE]); /* * XXX KDM we need to be able to set the subpage in the * fill function. @@ -4029,7 +4341,8 @@ retry_length: /*param_len*/ dp_len, /*minimum_cmd_size*/ 10, /*sense_len*/ SSD_FULL_SIZE, - /*timeout*/ SCSIOP_TIMEOUT); + /*timeout*/ + softc->timeout_info[SA_TIMEOUT_MODE_SELECT]); error = cam_periph_runccb(ccb, saerror, 0, 0, softc->device_stats); if (error != 0) @@ -4299,7 +4612,8 @@ retry: /* It is safe to retry this operation */ scsi_mode_select(&ccb->csio, 5, NULL, MSG_SIMPLE_Q_TAG, (params_to_set & SA_PARAM_COMPRESSION)? TRUE : FALSE, - FALSE, mode_buffer, mode_buffer_len, SSD_FULL_SIZE, SCSIOP_TIMEOUT); + FALSE, mode_buffer, mode_buffer_len, SSD_FULL_SIZE, + softc->timeout_info[SA_TIMEOUT_MODE_SELECT]); error = cam_periph_runccb(ccb, saerror, 0, sense_flags, softc->device_stats); @@ -4615,7 +4929,7 @@ saprevent(struct cam_periph *periph, int action) /* It is safe to retry this operation */ scsi_prevent(&ccb->csio, 5, NULL, MSG_SIMPLE_Q_TAG, action, - SSD_FULL_SIZE, SCSIOP_TIMEOUT); + SSD_FULL_SIZE, softc->timeout_info[SA_TIMEOUT_PREVENT]); error = cam_periph_runccb(ccb, saerror, 0, sf, softc->device_stats); if (error == 0) { @@ -4641,7 +4955,7 @@ sarewind(struct cam_periph *periph) /* It is safe to retry this operation */ scsi_rewind(&ccb->csio, 2, NULL, MSG_SIMPLE_Q_TAG, FALSE, - SSD_FULL_SIZE, REWIND_TIMEOUT); + SSD_FULL_SIZE, softc->timeout_info[SA_TIMEOUT_REWIND]); softc->dsreg = MTIO_DSREG_REW; error = cam_periph_runccb(ccb, saerror, 0, 0, softc->device_stats); @@ -4673,7 +4987,7 @@ saspace(struct cam_periph *periph, int count, scsi_space_code code) /* This cannot be retried */ scsi_space(&ccb->csio, 0, NULL, MSG_SIMPLE_Q_TAG, code, count, - SSD_FULL_SIZE, SPACE_TIMEOUT); + SSD_FULL_SIZE, softc->timeout_info[SA_TIMEOUT_SPACE]); /* * Clear residual because we will be using it. @@ -4754,7 +5068,8 @@ sawritefilemarks(struct cam_periph *periph, int nmarks, int setmarks, int immed) softc->dsreg = MTIO_DSREG_FMK; /* this *must* not be retried */ scsi_write_filemarks(&ccb->csio, 0, NULL, MSG_SIMPLE_Q_TAG, - immed, setmarks, nmarks, SSD_FULL_SIZE, IO_TIMEOUT); + immed, setmarks, nmarks, SSD_FULL_SIZE, + softc->timeout_info[SA_TIMEOUT_WRITE_FILEMARKS]); softc->dsreg = MTIO_DSREG_REST; error = cam_periph_runccb(ccb, saerror, 0, 0, softc->device_stats); @@ -4821,7 +5136,8 @@ sagetpos(struct cam_periph *periph) /*data_ptr*/ (uint8_t *)&long_pos, /*length*/ sizeof(long_pos), /*sense_len*/ SSD_FULL_SIZE, - /*timeout*/ SCSIOP_TIMEOUT); + /*timeout*/ + softc->timeout_info[SA_TIMEOUT_READ_POSITION]); softc->dsreg = MTIO_DSREG_RBSY; error = cam_periph_runccb(ccb, saerror, 0, SF_QUIET_IR, @@ -4917,7 +5233,8 @@ sardpos(struct cam_periph *periph, int hard, u_int32_t *blkptr) ccb = cam_periph_getccb(periph, 1); scsi_read_position(&ccb->csio, 1, NULL, MSG_SIMPLE_Q_TAG, - hard, &loc, SSD_FULL_SIZE, SCSIOP_TIMEOUT); + hard, &loc, SSD_FULL_SIZE, + softc->timeout_info[SA_TIMEOUT_READ_POSITION]); softc->dsreg = MTIO_DSREG_RBSY; error = cam_periph_runccb(ccb, saerror, 0, 0, softc->device_stats); softc->dsreg = MTIO_DSREG_REST; @@ -4985,7 +5302,8 @@ sasetpos(struct cam_periph *periph, int hard, struct mtlocate *locate_info) /*partition*/ locate_info->partition, /*logical_id*/ locate_info->logical_id, /*sense_len*/ SSD_FULL_SIZE, - /*timeout*/ SPACE_TIMEOUT); + /*timeout*/ + softc->timeout_info[SA_TIMEOUT_LOCATE]); } else { scsi_locate_10(&ccb->csio, /*retries*/ 1, @@ -4997,7 +5315,8 @@ sasetpos(struct cam_periph *periph, int hard, struct mtlocate *locate_info) /*partition*/ locate_info->partition, /*block_address*/ locate_info->logical_id, /*sense_len*/ SSD_FULL_SIZE, - /*timeout*/ SPACE_TIMEOUT); + /*timeout*/ + softc->timeout_info[SA_TIMEOUT_LOCATE]); } softc->dsreg = MTIO_DSREG_POS; @@ -5060,7 +5379,8 @@ saretension(struct cam_periph *periph) /* It is safe to retry this operation */ scsi_load_unload(&ccb->csio, 5, NULL, MSG_SIMPLE_Q_TAG, FALSE, - FALSE, TRUE, TRUE, SSD_FULL_SIZE, ERASE_TIMEOUT); + FALSE, TRUE, TRUE, SSD_FULL_SIZE, + softc->timeout_info[SA_TIMEOUT_LOAD]); softc->dsreg = MTIO_DSREG_TEN; error = cam_periph_runccb(ccb, saerror, 0, 0, softc->device_stats); @@ -5087,7 +5407,8 @@ sareservereleaseunit(struct cam_periph *periph, int reserve) /* It is safe to retry this operation */ scsi_reserve_release_unit(&ccb->csio, 2, NULL, MSG_SIMPLE_Q_TAG, - FALSE, 0, SSD_FULL_SIZE, SCSIOP_TIMEOUT, reserve); + FALSE, 0, SSD_FULL_SIZE, softc->timeout_info[SA_TIMEOUT_RESERVE], + reserve); softc->dsreg = MTIO_DSREG_RBSY; error = cam_periph_runccb(ccb, saerror, 0, SF_RETRY_UA | SF_NO_PRINT, softc->device_stats); @@ -5118,7 +5439,8 @@ saloadunload(struct cam_periph *periph, int load) /* It is safe to retry this operation */ scsi_load_unload(&ccb->csio, 5, NULL, MSG_SIMPLE_Q_TAG, FALSE, - FALSE, FALSE, load, SSD_FULL_SIZE, REWIND_TIMEOUT); + FALSE, FALSE, load, SSD_FULL_SIZE, + softc->timeout_info[SA_TIMEOUT_LOAD]); softc->dsreg = (load)? MTIO_DSREG_LD : MTIO_DSREG_UNL; error = cam_periph_runccb(ccb, saerror, 0, 0, softc->device_stats); @@ -5150,7 +5472,7 @@ saerase(struct cam_periph *periph, int longerase) ccb = cam_periph_getccb(periph, 1); scsi_erase(&ccb->csio, 1, NULL, MSG_SIMPLE_Q_TAG, FALSE, longerase, - SSD_FULL_SIZE, ERASE_TIMEOUT); + SSD_FULL_SIZE, softc->timeout_info[SA_TIMEOUT_ERASE]); softc->dsreg = MTIO_DSREG_ZER; error = cam_periph_runccb(ccb, saerror, 0, 0, softc->device_stats); @@ -5449,6 +5771,159 @@ safilldensitysb(struct sa_softc *softc, int *indent, struct sbuf *sb) SASBENDNODE(sb, *indent, mtdensity); } +/* + * Given a completed REPORT SUPPORTED OPERATION CODES command with timeout + * descriptors, go through the descriptors and set the sa(4) driver + * timeouts to the recommended values. + */ +static void +saloadtimeouts(struct sa_softc *softc, union ccb *ccb) +{ + uint32_t valid_len, avail_len = 0, used_len = 0; + struct scsi_report_supported_opcodes_all *hdr; + struct scsi_report_supported_opcodes_descr *desc; + uint8_t *buf; + + hdr = (struct scsi_report_supported_opcodes_all *)ccb->csio.data_ptr; + valid_len = ccb->csio.dxfer_len - ccb->csio.resid; + + if (valid_len < sizeof(*hdr)) + return; + + avail_len = scsi_4btoul(hdr->length) + sizeof(hdr->length); + if ((avail_len != 0) + && (avail_len > valid_len)) { + xpt_print(softc->periph->path, "WARNING: available timeout " + "descriptor len %zu > valid len %u\n", avail_len,valid_len); + } + + used_len = sizeof(hdr->length); + avail_len = MIN(avail_len, valid_len - sizeof(*hdr)); + buf = ccb->csio.data_ptr; + while ((avail_len - used_len) > sizeof(*desc)) { + struct scsi_report_supported_opcodes_timeout *td; + uint32_t td_len; + uint32_t rec_time; + uint8_t *cur_ptr; + + cur_ptr = &buf[used_len]; + desc = (struct scsi_report_supported_opcodes_descr *)cur_ptr; + + used_len += sizeof(*desc); + /* If there's no timeout descriptor, keep going */ + if ((desc->flags & RSO_CTDP) == 0) + continue; + + /* + * If we don't have enough space to fit a timeout + * descriptor then we're done. + */ + if ((avail_len - used_len) < sizeof(*td)) { + used_len = avail_len; + continue; + } + + cur_ptr = &buf[used_len]; + td = (struct scsi_report_supported_opcodes_timeout *)cur_ptr; + td_len = scsi_2btoul(td->length); + td_len += sizeof(td->length); + used_len += td_len; + + if (td_len < sizeof(*td)) + continue; + + /* + * Use the recommended timeout. The nominal time is the + * time to wait before querying for status. + */ + rec_time = scsi_4btoul(td->recommended_time); + + /* + * Our timeouts are set in thousandths of a seconds. + */ + rec_time *= 1000; + + switch(desc->opcode) { + case ERASE: + softc->timeout_info[SA_TIMEOUT_ERASE] = rec_time; + break; + case LOAD_UNLOAD: + softc->timeout_info[SA_TIMEOUT_LOAD] = rec_time; + break; + case LOCATE: + case LOCATE_16: + /* + * We are assuming these are the same timeout. + */ + softc->timeout_info[SA_TIMEOUT_LOCATE] = rec_time; + break; + case MODE_SELECT_6: + case MODE_SELECT_10: + /* + * We are assuming these are the same timeout. + */ + softc->timeout_info[SA_TIMEOUT_MODE_SELECT] = rec_time; + break; + case MODE_SENSE_6: + case MODE_SENSE_10: + /* + * We are assuming these are the same timeout. + */ + softc->timeout_info[SA_TIMEOUT_MODE_SENSE] = rec_time; + break; + case PREVENT_ALLOW: + softc->timeout_info[SA_TIMEOUT_PREVENT] = rec_time; + break; + case SA_READ: + softc->timeout_info[SA_TIMEOUT_READ] = rec_time; + break; + case READ_BLOCK_LIMITS: + softc->timeout_info[SA_TIMEOUT_READ_BLOCK_LIMITS] = + rec_time; + break; + case READ_POSITION: + /* + * Note that this may show up multiple times for + * the short form, long form and extended form + * service actions. We're assuming they are all + * the same. + */ + softc->timeout_info[SA_TIMEOUT_READ_POSITION] =rec_time; + break; + case REPORT_DENSITY_SUPPORT: + softc->timeout_info[SA_TIMEOUT_REP_DENSITY] = rec_time; + break; + case RESERVE_UNIT: + case RELEASE_UNIT: + /* We are assuming these are the same timeout.*/ + softc->timeout_info[SA_TIMEOUT_RESERVE] = rec_time; + break; + case REWIND: + softc->timeout_info[SA_TIMEOUT_REWIND] = rec_time; + break; + case SPACE: + softc->timeout_info[SA_TIMEOUT_SPACE] = rec_time; + break; + case TEST_UNIT_READY: + softc->timeout_info[SA_TIMEOUT_TUR] = rec_time; + break; + case SA_WRITE: + softc->timeout_info[SA_TIMEOUT_WRITE] = rec_time; + break; + case WRITE_FILEMARKS: + softc->timeout_info[SA_TIMEOUT_WRITE_FILEMARKS] = + rec_time; + break; + default: + /* + * We have explicit cases for all of the timeouts + * we use. + */ + break; + } + } +} + #endif /* _KERNEL */ /* |
