Module Name: src Committed By: kardel Date: Thu Mar 28 10:44:29 UTC 2019
Modified Files: src/sys/dev/scsipi: scsi_spc.h scsiconf.c scsipi_base.c scsipiconf.c scsipiconf.h src/sys/dev/usb: umass_scsipi.c Log Message: Add reading of supported opcodes and their timeouts at attachment time. Though this information is optional, it allows to override our fixed timeouts with device provided timeouts. These timeouts will override the hardcoded values if the device provided timeouts exceed the hardcoded values and are less than a day. Using the device provided timeouts avoids premature device resets and unreliable operation due to inadequate timeouts. Due to the limited implementations of USB umass devices this feature is disabled for all umass attached devices. To generate a diff of this commit: cvs rdiff -u -r1.5 -r1.6 src/sys/dev/scsipi/scsi_spc.h cvs rdiff -u -r1.283 -r1.284 src/sys/dev/scsipi/scsiconf.c cvs rdiff -u -r1.181 -r1.182 src/sys/dev/scsipi/scsipi_base.c cvs rdiff -u -r1.44 -r1.45 src/sys/dev/scsipi/scsipiconf.c cvs rdiff -u -r1.129 -r1.130 src/sys/dev/scsipi/scsipiconf.h cvs rdiff -u -r1.60 -r1.61 src/sys/dev/usb/umass_scsipi.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/dev/scsipi/scsi_spc.h diff -u src/sys/dev/scsipi/scsi_spc.h:1.5 src/sys/dev/scsipi/scsi_spc.h:1.6 --- src/sys/dev/scsipi/scsi_spc.h:1.5 Sat Feb 6 23:13:59 2010 +++ src/sys/dev/scsipi/scsi_spc.h Thu Mar 28 10:44:29 2019 @@ -1,4 +1,4 @@ -/* $NetBSD: scsi_spc.h,v 1.5 2010/02/06 23:13:59 cegger Exp $ */ +/* $NetBSD: scsi_spc.h,v 1.6 2019/03/28 10:44:29 kardel Exp $ */ /*- * Copyright (c) 2005 The NetBSD Foundation, Inc. @@ -406,6 +406,70 @@ struct scsi_reserve_release_10_idparam { */ /* + * MAINTENANCE_IN[REPORT SUPPORTED OPERATION CODES] + */ +#define SCSI_MAINTENANCE_IN 0xA3 + +struct scsi_repsuppopcode { + u_int8_t opcode; + u_int8_t svcaction; +#define RSOC_REPORT_SUPPORTED_OPCODES 0x0C + + u_int8_t repoption; +#define RSOC_ALL 0x00 /* report all */ +#define RSOC_ONE 0x01 /* report one */ +#define RSOC_ONESACD 0x02 /* report one or CHECK CONDITION */ +#define RSOC_ONESA 0x03 /* report one mark presense in data */ +#define RSOC_RCTD 0x80 /* report timeouts */ + + u_int8_t reqopcode; + u_int8_t reqsvcaction[2]; + u_int8_t alloclen[4]; + u_int8_t _res0; + u_int8_t control; +}; + +struct scsi_repsupopcode_all_commands_descriptor { + u_int8_t opcode; + u_int8_t _res0; + u_int8_t serviceaction[2]; + u_int8_t _res1; + u_int8_t flags; +#define RSOC_ACD_CTDP 0x02 /* timeouts present */ +#define RSOC_ACD_SERVACTV 0x01 /* service action valid */ + u_int8_t cdblen[2]; +}; + +struct scsi_repsupopcode_one_command_descriptor { + u_int8_t _res0; + u_int8_t support; +#define RSOC_OCD_CTDP 0x80 /* timeouts present */ +#define RSOC_OCD_SUP_NOT_AVAIL 0x00 /* not available */ +#define RSOC_OCD_SUP_NOT_SUPP 0x01 /* not supported */ +#define RSOC_OCD_SUP_SUPP_STD 0x03 /* supported - standard */ +#define RSOC_OCD_SUP_SUPP_VENDOR 0x05 /* supported - vendor */ +#define RSOC_OCD_SUP 0x07 /* mask for support field */ + + u_int8_t cdblen[2]; + /* + * u_int8_t usage[0...]- cdblen bytes + * usage data + */ + /* + * scsi_repsupopcode_timeouts_descriptor + * if RSOC_OCD_CTDP is set + */ +}; + +struct scsi_repsupopcode_timeouts_descriptor { + u_int8_t descriptor_length[2]; + u_int8_t _res0; + u_int8_t cmd_specific; + u_int8_t nom_process_timeout[4]; + u_int8_t cmd_process_timeout[4]; +}; + +/* * REQUEST SENSE */ Index: src/sys/dev/scsipi/scsiconf.c diff -u src/sys/dev/scsipi/scsiconf.c:1.283 src/sys/dev/scsipi/scsiconf.c:1.284 --- src/sys/dev/scsipi/scsiconf.c:1.283 Sat Jan 12 13:59:53 2019 +++ src/sys/dev/scsipi/scsiconf.c Thu Mar 28 10:44:29 2019 @@ -1,4 +1,4 @@ -/* $NetBSD: scsiconf.c,v 1.283 2019/01/12 13:59:53 tsutsui Exp $ */ +/* $NetBSD: scsiconf.c,v 1.284 2019/03/28 10:44:29 kardel Exp $ */ /*- * Copyright (c) 1998, 1999, 2004 The NetBSD Foundation, Inc. @@ -48,7 +48,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: scsiconf.c,v 1.283 2019/01/12 13:59:53 tsutsui Exp $"); +__KERNEL_RCSID(0, "$NetBSD: scsiconf.c,v 1.284 2019/03/28 10:44:29 kardel Exp $"); #include <sys/param.h> #include <sys/systm.h> @@ -500,9 +500,11 @@ scsibusprint(void *aux, const char *pnp) strnvisx(revision, sizeof(revision), inqbuf->revision, 4, VIS_TRIM|VIS_SAFE|VIS_OCTAL); - aprint_normal(" target %d lun %d: <%s, %s, %s> %s %s", - target, lun, vendor, product, revision, dtype, - inqbuf->removable ? "removable" : "fixed"); + aprint_normal(" target %d lun %d: <%s, %s, %s> %s %s%s", + target, lun, vendor, product, revision, dtype, + inqbuf->removable ? "removable" : "fixed", + (sa->sa_periph->periph_opcs != NULL) + ? " timeout-info" : ""); return (UNCONF); } @@ -1018,6 +1020,13 @@ scsi_probe_device(struct scsibus_softc * if ((cf = config_search_loc(config_stdsubmatch, sc->sc_dev, "scsibus", locs, &sa)) != NULL) { scsipi_insert_periph(chan, periph); + + /* + * determine supported opcodes and + * timeouts if available + */ + scsipi_get_opcodeinfo(periph); + /* * XXX Can't assign periph_dev here, because we'll * XXX need it before config_attach() returns. Must Index: src/sys/dev/scsipi/scsipi_base.c diff -u src/sys/dev/scsipi/scsipi_base.c:1.181 src/sys/dev/scsipi/scsipi_base.c:1.182 --- src/sys/dev/scsipi/scsipi_base.c:1.181 Tue Feb 5 11:11:32 2019 +++ src/sys/dev/scsipi/scsipi_base.c Thu Mar 28 10:44:29 2019 @@ -1,4 +1,4 @@ -/* $NetBSD: scsipi_base.c,v 1.181 2019/02/05 11:11:32 mrg Exp $ */ +/* $NetBSD: scsipi_base.c,v 1.182 2019/03/28 10:44:29 kardel Exp $ */ /*- * Copyright (c) 1998, 1999, 2000, 2002, 2003, 2004 The NetBSD Foundation, Inc. @@ -31,7 +31,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: scsipi_base.c,v 1.181 2019/02/05 11:11:32 mrg Exp $"); +__KERNEL_RCSID(0, "$NetBSD: scsipi_base.c,v 1.182 2019/03/28 10:44:29 kardel Exp $"); #ifdef _KERNEL_OPT #include "opt_scsi.h" @@ -84,6 +84,8 @@ static void scsipi_channel_freeze_locked static void scsipi_adapter_lock(struct scsipi_adapter *adapt); static void scsipi_adapter_unlock(struct scsipi_adapter *adapt); +static void scsipi_update_timeouts(struct scsipi_xfer *xs); + static struct pool scsipi_xfer_pool; int scsipi_xs_count = 0; @@ -860,7 +862,7 @@ scsipi_interpret_sense(struct scsipi_xfe sense = &xs->sense.scsi_sense; #ifdef SCSIPI_DEBUG if (periph->periph_flags & SCSIPI_DB1) { - int count; + int count, len; scsipi_printaddr(periph); printf(" sense debug information:\n"); printf("\tcode 0x%x valid %d\n", @@ -879,8 +881,9 @@ scsipi_interpret_sense(struct scsipi_xfe sense->info[2], sense->info[3], sense->extra_len); - printf("\textra: "); - for (count = 0; count < SSD_ADD_BYTES_LIM(sense); count++) + len = SSD_ADD_BYTES_LIM(sense); + printf("\textra (up to %d bytes): ", len); + for (count = 0; count < len; count++) printf("0x%x ", sense->csi[count]); printf("\n"); } @@ -1354,6 +1357,197 @@ scsipi_mode_select_big(struct scsipi_per } /* + * scsipi_get_opcodeinfo: + * + * query the device for supported commends and their timeout + * building a timeout lookup table if timeout information is available. + */ +void +scsipi_get_opcodeinfo(struct scsipi_periph *periph) +{ + u_int8_t *data; + int len = 16*1024; + int rc; + struct scsi_repsuppopcode cmd; + + /* refrain from asking for supported opcodes */ + if (periph->periph_quirks & PQUIRK_NOREPSUPPOPC || + periph->periph_type == T_PROCESSOR || /* spec. */ + periph->periph_type == T_CDROM) /* spec. */ + return; + + scsipi_free_opcodeinfo(periph); + + /* + * query REPORT SUPPORTED OPERATION CODES + * if OK + * enumerate all codes + * if timeout exists insert maximum into opcode table + */ + + data = malloc(len, M_DEVBUF, M_NOWAIT|M_ZERO); + if (data == NULL) { + SC_DEBUG(periph, SCSIPI_DB3, + ("unable to allocate data buffer " + "for REPORT SUPPORTED OPERATION CODES\n")); + return; + } + + memset(&cmd, 0, sizeof(cmd)); + + cmd.opcode = SCSI_MAINTENANCE_IN; + cmd.svcaction = RSOC_REPORT_SUPPORTED_OPCODES; + cmd.repoption = RSOC_RCTD|RSOC_ALL; + _lto4b(len, cmd.alloclen); + + rc = scsipi_command(periph, (void *)&cmd, sizeof(cmd), + (void *)data, len, 0, 1000, NULL, + XS_CTL_DATA_IN|XS_CTL_SILENT); + + if (rc == 0) { + int count; + int dlen = _4btol(data); + u_int8_t *c = data + 4; + + SC_DEBUG(periph, SCSIPI_DB3, + ("supported opcode timeout-values loaded\n")); + SC_DEBUG(periph, SCSIPI_DB3, + ("CMD LEN SA spec nom. time cmd timeout\n")); + + struct scsipi_opcodes *tot = + (struct scsipi_opcodes *)malloc(sizeof(struct scsipi_opcodes), + M_DEVBUF, M_NOWAIT|M_ZERO); + + count = 0; + while (tot != NULL && + dlen >= (int)sizeof(struct scsi_repsupopcode_all_commands_descriptor)) { + struct scsi_repsupopcode_all_commands_descriptor *acd + = (struct scsi_repsupopcode_all_commands_descriptor *)c; +#ifdef SCSIPI_DEBUG + int cdblen = _2btol((const u_int8_t *)&acd->cdblen); +#endif + dlen -= sizeof(struct scsi_repsupopcode_all_commands_descriptor); + c += sizeof(struct scsi_repsupopcode_all_commands_descriptor); + SC_DEBUG(periph, SCSIPI_DB3, + ("0x%02x(%2d) ", acd->opcode, cdblen)); + + tot->opcode_info[acd->opcode].ti_flags = SCSIPI_TI_VALID; + + if (acd->flags & RSOC_ACD_SERVACTV) { + SC_DEBUGN(periph, SCSIPI_DB3, + ("0x%02x%02x ", + acd->serviceaction[0], + acd->serviceaction[1])); + } else { + SC_DEBUGN(periph, SCSIPI_DB3, (" ")); + } + + if (acd->flags & RSOC_ACD_CTDP + && dlen >= (int)sizeof(struct scsi_repsupopcode_timeouts_descriptor)) { + struct scsi_repsupopcode_timeouts_descriptor *td + = (struct scsi_repsupopcode_timeouts_descriptor *)c; + long nomto = _4btol(td->nom_process_timeout); + long cmdto = _4btol(td->cmd_process_timeout); + long t = (cmdto > nomto) ? cmdto : nomto; + + dlen -= sizeof(struct scsi_repsupopcode_timeouts_descriptor); + c += sizeof(struct scsi_repsupopcode_timeouts_descriptor); + + SC_DEBUGN(periph, SCSIPI_DB3, + ("0x%02x %10ld %10ld", + td->cmd_specific, + nomto, cmdto)); + + if (t > tot->opcode_info[acd->opcode].ti_timeout) { + tot->opcode_info[acd->opcode].ti_timeout = t; + ++count; + } + } + SC_DEBUGN(periph, SCSIPI_DB3,("\n")); + } + + if (count > 0) { + periph->periph_opcs = tot; + } else { + free(tot, M_DEVBUF); + SC_DEBUG(periph, SCSIPI_DB3, + ("no usable timeout values available\n")); + } + } else { + SC_DEBUG(periph, SCSIPI_DB3, + ("SCSI_MAINTENANCE_IN" + "[RSOC_REPORT_SUPPORTED_OPCODES] failed error=%d" + " - no device provided timeout " + "values available\n", rc)); + } + + free(data, M_DEVBUF); +} + +/* + * scsipi_update_timeouts: + * Overide timeout value if device/config provided + * timeouts are available. + */ +static void +scsipi_update_timeouts(struct scsipi_xfer *xs) +{ + struct scsipi_opcodes *opcs; + u_int8_t cmd; + int timeout; + struct scsipi_opinfo *oi; + + if (xs->timeout <= 0) { + return; + } + + opcs = xs->xs_periph->periph_opcs; + + if (opcs == NULL) { + return; + } + + cmd = xs->cmd->opcode; + oi = &opcs->opcode_info[cmd]; + + timeout = 1000 * (int)oi->ti_timeout; + + + if (timeout > xs->timeout && timeout < 86400000) { + /* + * pick up device configured timeouts if they + * are longer than the requested ones but less + * than a day + */ +#ifdef SCSIPI_DEBUG + if ((oi->ti_flags & SCSIPI_TI_LOGGED) == 0) { + SC_DEBUG(xs->xs_periph, SCSIPI_DB3, + ("Overriding command 0x%02x " + "timeout of %d with %d ms\n", + cmd, xs->timeout, timeout)); + oi->ti_flags |= SCSIPI_TI_LOGGED; + } +#endif + xs->timeout = timeout; + } +} + +/* + * scsipi_free_opcodeinfo: + * + * free the opcode information table + */ +void +scsipi_free_opcodeinfo(struct scsipi_periph *periph) +{ + if (periph->periph_opcs != NULL) { + free(periph->periph_opcs, M_DEVBUF); + } + + periph->periph_opcs = NULL; +} + +/* * scsipi_done: * * This routine is called by an adapter's interrupt handler when @@ -1970,6 +2164,8 @@ scsipi_execute_xs(struct scsipi_xfer *xs KASSERT(!cold); + scsipi_update_timeouts(xs); + (chan->chan_bustype->bustype_cmd)(xs); xs->xs_status &= ~XS_STS_DONE; Index: src/sys/dev/scsipi/scsipiconf.c diff -u src/sys/dev/scsipi/scsipiconf.c:1.44 src/sys/dev/scsipi/scsipiconf.c:1.45 --- src/sys/dev/scsipi/scsipiconf.c:1.44 Tue Apr 11 14:32:43 2017 +++ src/sys/dev/scsipi/scsipiconf.c Thu Mar 28 10:44:29 2019 @@ -1,4 +1,4 @@ -/* $NetBSD: scsipiconf.c,v 1.44 2017/04/11 14:32:43 christos Exp $ */ +/* $NetBSD: scsipiconf.c,v 1.45 2019/03/28 10:44:29 kardel Exp $ */ /*- * Copyright (c) 1998, 1999, 2004 The NetBSD Foundation, Inc. @@ -48,7 +48,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: scsipiconf.c,v 1.44 2017/04/11 14:32:43 christos Exp $"); +__KERNEL_RCSID(0, "$NetBSD: scsipiconf.c,v 1.45 2019/03/28 10:44:29 kardel Exp $"); #include <sys/param.h> #include <sys/systm.h> @@ -134,6 +134,7 @@ scsipi_alloc_periph(int malloc_flag) return NULL; periph->periph_dev = NULL; + periph->periph_opcs = NULL; /* * Start with one command opening. The periph driver @@ -158,6 +159,7 @@ scsipi_alloc_periph(int malloc_flag) void scsipi_free_periph(struct scsipi_periph *periph) { + scsipi_free_opcodeinfo(periph); cv_destroy(&periph->periph_cv); free(periph, M_DEVBUF); } Index: src/sys/dev/scsipi/scsipiconf.h diff -u src/sys/dev/scsipi/scsipiconf.h:1.129 src/sys/dev/scsipi/scsipiconf.h:1.130 --- src/sys/dev/scsipi/scsipiconf.h:1.129 Sun Oct 7 18:14:32 2018 +++ src/sys/dev/scsipi/scsipiconf.h Thu Mar 28 10:44:29 2019 @@ -1,4 +1,4 @@ -/* $NetBSD: scsipiconf.h,v 1.129 2018/10/07 18:14:32 christos Exp $ */ +/* $NetBSD: scsipiconf.h,v 1.130 2019/03/28 10:44:29 kardel Exp $ */ /*- * Copyright (c) 1998, 1999, 2000, 2004 The NetBSD Foundation, Inc. @@ -352,9 +352,28 @@ struct scsipi_channel { */ #define PERIPH_NTAGWORDS ((256 / 8) / sizeof(u_int32_t)) - #ifdef _KERNEL /* + * scsipi_opcodes: + * This optionally allocated structure documents + * valid opcodes and timeout values for the respective + * opcodes overriding the requested timeouts. + * It is created when SCSI_MAINTENANCE_IN/ + * RSOC_REPORT_SUPPORTED_OPCODES can provide information + * at attach time. + */ +struct scsipi_opcodes +{ + struct scsipi_opinfo + { + long ti_timeout; /* timeout in seconds (> 0 => VALID) */ + unsigned long ti_flags; +#define SCSIPI_TI_VALID 0x0001 /* valid op code */ +#define SCSIPI_TI_LOGGED 0x8000 /* override logged during debug */ + } opcode_info[0x100]; +}; + +/* * scsipi_periph: * * This structure describes the path between a peripheral device @@ -401,6 +420,9 @@ struct scsipi_periph { int periph_qfreeze; /* queue freeze count */ + /* available opcodes and timeout information */ + struct scsipi_opcodes *periph_opcs; + /* Bitmap of free command tags. */ u_int32_t periph_freetags[PERIPH_NTAGWORDS]; @@ -479,8 +501,9 @@ struct scsipi_periph { #define PQUIRK_CAP_NODT 0x00200000 /* signals DT, but can't. */ #define PQUIRK_START 0x00400000 /* needs start before tur */ #define PQUIRK_NOFUA 0x00800000 /* does not grok FUA */ - - +#define PQUIRK_NOREPSUPPOPC 0x01000000 /* does not grok + REPORT SUPPORTED OPCODES + to fetch device timeouts */ /* * Error values an adapter driver may return */ @@ -681,6 +704,8 @@ void scsipi_user_done(struct scsipi_xfer int scsipi_interpret_sense(struct scsipi_xfer *); void scsipi_wait_drain(struct scsipi_periph *); void scsipi_kill_pending(struct scsipi_periph *); +void scsipi_get_opcodeinfo(struct scsipi_periph *periph); +void scsipi_free_opcodeinfo(struct scsipi_periph *periph); struct scsipi_periph *scsipi_alloc_periph(int); void scsipi_free_periph(struct scsipi_periph *); Index: src/sys/dev/usb/umass_scsipi.c diff -u src/sys/dev/usb/umass_scsipi.c:1.60 src/sys/dev/usb/umass_scsipi.c:1.61 --- src/sys/dev/usb/umass_scsipi.c:1.60 Sun Feb 10 19:23:55 2019 +++ src/sys/dev/usb/umass_scsipi.c Thu Mar 28 10:44:29 2019 @@ -1,4 +1,4 @@ -/* $NetBSD: umass_scsipi.c,v 1.60 2019/02/10 19:23:55 jdolecek Exp $ */ +/* $NetBSD: umass_scsipi.c,v 1.61 2019/03/28 10:44:29 kardel Exp $ */ /* * Copyright (c) 2001, 2003, 2012 The NetBSD Foundation, Inc. @@ -31,7 +31,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: umass_scsipi.c,v 1.60 2019/02/10 19:23:55 jdolecek Exp $"); +__KERNEL_RCSID(0, "$NetBSD: umass_scsipi.c,v 1.61 2019/03/28 10:44:29 kardel Exp $"); #ifdef _KERNEL_OPT #include "opt_usb.h" @@ -212,7 +212,8 @@ umass_scsipi_setup(struct umass_softc *s sc->bus = &scbus->base; /* Only use big commands for USB SCSI devices. */ - sc->sc_busquirks |= PQUIRK_ONLYBIG; + /* Do not ask for timeouts. */ + sc->sc_busquirks |= PQUIRK_ONLYBIG|PQUIRK_NOREPSUPPOPC; /* Fill in the adapter. */ memset(&scbus->sc_adapter, 0, sizeof(scbus->sc_adapter));