From owner-svn-src-all@FreeBSD.ORG Mon Oct 3 20:32:56 2011 Return-Path: Delivered-To: svn-src-all@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 659D3106566B; Mon, 3 Oct 2011 20:32:56 +0000 (UTC) (envelope-from ken@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:4f8:fff6::2c]) by mx1.freebsd.org (Postfix) with ESMTP id 511BE8FC0A; Mon, 3 Oct 2011 20:32:56 +0000 (UTC) Received: from svn.freebsd.org (localhost [127.0.0.1]) by svn.freebsd.org (8.14.4/8.14.4) with ESMTP id p93KWutA026720; Mon, 3 Oct 2011 20:32:56 GMT (envelope-from ken@svn.freebsd.org) Received: (from ken@localhost) by svn.freebsd.org (8.14.4/8.14.4/Submit) id p93KWufB026711; Mon, 3 Oct 2011 20:32:56 GMT (envelope-from ken@svn.freebsd.org) Message-Id: <201110032032.p93KWufB026711@svn.freebsd.org> From: "Kenneth D. Merry" Date: Mon, 3 Oct 2011 20:32:56 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Subject: svn commit: r225950 - in head: sbin/camcontrol share/examples/scsi_target share/misc sys/cam sys/cam/scsi sys/dev/ciss sys/dev/firewire sys/dev/iir sys/dev/iscsi/initiator sys/dev/isp sys/dev/mly s... X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 03 Oct 2011 20:32:56 -0000 Author: ken Date: Mon Oct 3 20:32:55 2011 New Revision: 225950 URL: http://svn.freebsd.org/changeset/base/225950 Log: Add descriptor sense support to CAM, and honor sense residuals properly in CAM. Desriptor sense is a new sense data format that originated in SPC-3. Among other things, it allows for an 8-byte info field, which is necessary to pass back block numbers larger than 4 bytes. This change adds a number of new functions to scsi_all.c (and therefore libcam) that abstract out most access to sense data. This includes a bump of CAM_VERSION, because the CCB ABI has changed. Userland programs that use the CAM pass(4) driver will need to be recompiled. camcontrol.c: Change uses of scsi_extract_sense() to use scsi_extract_sense_len(). Use scsi_get_sks() instead of accessing sense key specific data directly. scsi_modes: Update the control mode page to the latest version (SPC-4). scsi_cmds.c, scsi_target.c: Change references to struct scsi_sense_data to struct scsi_sense_data_fixed. This should be changed to allow the user to specify fixed or descriptor sense, and then use scsi_set_sense_data() to build the sense data. ps3cdrom.c: Use scsi_set_sense_data() instead of setting sense data manually. cam_periph.c: Use scsi_extract_sense_len() instead of using scsi_extract_sense() or accessing sense data directly. cam_ccb.h: Bump the CAM_VERSION from 0x15 to 0x16. The change of struct scsi_sense_data from 32 to 252 bytes changes the size of struct ccb_scsiio, but not the size of union ccb. So the version must be bumped to prevent structure mis-matches. scsi_all.h: Lots of updated SCSI sense data and other structures. Add function prototypes for the new sense data functions. Take out the inline implementation of scsi_extract_sense(). It is now too large to put in a header file. Add macros to calculate whether fields are present and filled in fixed and descriptor sense data scsi_all.c: In scsi_op_desc(), allow the user to pass in NULL inquiry data, and we'll assume a direct access device in that case. Changed the SCSI RESERVED sense key name and description to COMPLETED, as it is now defined in the spec. Change the error recovery action for a number of read errors to prevent lots of retries when the drive has said that the block isn't accessible. This speeds up reconstruction of the block by any RAID software running on top of the drive (e.g. ZFS). In scsi_sense_desc(), allow for invalid sense key numbers. This allows calling this routine without checking the input values first. Change scsi_error_action() to use scsi_extract_sense_len(), and handle things when invalid asc/ascq values are encountered. Add a new routine, scsi_desc_iterate(), that will call the supplied function for every descriptor in descriptor format sense data. Add scsi_set_sense_data(), and scsi_set_sense_data_va(), which build descriptor and fixed format sense data. They currently default to fixed format sense data. Add a number of scsi_get_*() functions, which get different types of sense data fields from either fixed or descriptor format sense data, if the data is present. Add a number of scsi_*_sbuf() functions, which print formatted versions of various sense data fields. These functions work for either fixed or descriptor sense. Add a number of scsi_sense_*_sbuf() functions, which have a standard calling interface and print the indicated field. These functions take descriptors only. Add scsi_sense_desc_sbuf(), which will print a formatted version of the given sense descriptor. Pull out a majority of the scsi_sense_sbuf() function and put it into scsi_sense_only_sbuf(). This allows callers that don't use struct ccb_scsiio to easily utilize the printing routines. Revamp that function to handle descriptor sense and use the new sense fetching and printing routines. Move scsi_extract_sense() into scsi_all.c, and implement it in terms of the new function, scsi_extract_sense_len(). The _len() version takes a length (which should be the sense length - residual) and can indicate which fields are present and valid in the sense data. Add a couple of new scsi_get_*() routines to get the sense key, asc, and ascq only. mly.c: Rename struct scsi_sense_data to struct scsi_sense_data_fixed. sbp_targ.c: Use the new sense fetching routines to get sense data instead of accessing it directly. sbp.c: Change the firewire/SCSI sense data transformation code to use struct scsi_sense_data_fixed instead of struct scsi_sense_data. This should be changed later to use scsi_set_sense_data(). ciss.c: Calculate the sense residual properly. Use scsi_get_sense_key() to fetch the sense key. mps_sas.c, mpt_cam.c: Set the sense residual properly. iir.c: Use scsi_set_sense_data() instead of building sense data by hand. iscsi_subr.c: Use scsi_extract_sense_len() instead of grabbing sense data directly. umass.c: Use scsi_set_sense_data() to build sense data. Grab the sense key using scsi_get_sense_key(). Calculate the sense residual properly. isp_freebsd.h: Use scsi_get_*() routines to grab asc, ascq, and sense key values. Calculate and set the sense residual. MFC after: 3 days Sponsored by: Spectra Logic Corporation Modified: head/sbin/camcontrol/camcontrol.c head/share/examples/scsi_target/scsi_cmds.c head/share/examples/scsi_target/scsi_target.c head/share/misc/scsi_modes head/sys/cam/cam_ccb.h head/sys/cam/cam_periph.c head/sys/cam/scsi/scsi_all.c head/sys/cam/scsi/scsi_all.h head/sys/cam/scsi/scsi_cd.c head/sys/cam/scsi/scsi_da.c head/sys/cam/scsi/scsi_low.c head/sys/cam/scsi/scsi_sa.c head/sys/cam/scsi/scsi_targ_bh.c head/sys/dev/ciss/ciss.c head/sys/dev/firewire/sbp.c head/sys/dev/firewire/sbp_targ.c head/sys/dev/iir/iir.c head/sys/dev/iscsi/initiator/iscsi_subr.c head/sys/dev/isp/isp_freebsd.h head/sys/dev/mly/mly.c head/sys/dev/mps/mps_sas.c head/sys/dev/mpt/mpt_cam.c head/sys/dev/usb/storage/umass.c head/sys/powerpc/ps3/ps3cdrom.c Modified: head/sbin/camcontrol/camcontrol.c ============================================================================== --- head/sbin/camcontrol/camcontrol.c Mon Oct 3 20:27:51 2011 (r225949) +++ head/sbin/camcontrol/camcontrol.c Mon Oct 3 20:32:55 2011 (r225950) @@ -1907,7 +1907,9 @@ readdefects(struct cam_device *device, i int error_code, sense_key, asc, ascq; sense = &ccb->csio.sense_data; - scsi_extract_sense(sense, &error_code, &sense_key, &asc, &ascq); + scsi_extract_sense_len(sense, ccb->csio.sense_len - + ccb->csio.sense_resid, &error_code, &sense_key, &asc, + &ascq, /*show_errors*/ 1); /* * According to the SCSI spec, if the disk doesn't support @@ -3798,8 +3800,9 @@ doreport: int error_code, sense_key, asc, ascq; sense = &ccb->csio.sense_data; - scsi_extract_sense(sense, &error_code, &sense_key, - &asc, &ascq); + scsi_extract_sense_len(sense, ccb->csio.sense_len - + ccb->csio.sense_resid, &error_code, &sense_key, + &asc, &ascq, /*show_errors*/ 1); /* * According to the SCSI-2 and SCSI-3 specs, a @@ -3810,15 +3813,15 @@ doreport: */ if ((sense_key == SSD_KEY_NOT_READY) && (asc == 0x04) && (ascq == 0x04)) { - if ((sense->extra_len >= 10) - && ((sense->sense_key_spec[0] & - SSD_SCS_VALID) != 0) + uint8_t sks[3]; + + if ((scsi_get_sks(sense, ccb->csio.sense_len - + ccb->csio.sense_resid, sks) == 0) && (quiet == 0)) { int val; u_int64_t percentage; - val = scsi_2btoul( - &sense->sense_key_spec[1]); + val = scsi_2btoul(&sks[1]); percentage = 10000 * val; fprintf(stdout, Modified: head/share/examples/scsi_target/scsi_cmds.c ============================================================================== --- head/share/examples/scsi_target/scsi_cmds.c Mon Oct 3 20:27:51 2011 (r225949) +++ head/share/examples/scsi_target/scsi_cmds.c Mon Oct 3 20:32:55 2011 (r225950) @@ -242,22 +242,22 @@ tcmd_sense(u_int init_id, struct ccb_scs u_int8_t asc, u_int8_t ascq) { struct initiator_state *istate; - struct scsi_sense_data *sense; + struct scsi_sense_data_fixed *sense; /* Set our initiator's istate */ istate = tcmd_get_istate(init_id); if (istate == NULL) return; istate->pending_ca |= CA_CMD_SENSE; /* XXX set instead of or? */ - sense = &istate->sense_data; + sense = (struct scsi_sense_data_fixed *)&istate->sense_data; bzero(sense, sizeof(*sense)); sense->error_code = SSD_CURRENT_ERROR; sense->flags = flags; sense->add_sense_code = asc; sense->add_sense_code_qual = ascq; sense->extra_len = - offsetof(struct scsi_sense_data, sense_key_spec[2]) - - offsetof(struct scsi_sense_data, extra_len); + offsetof(struct scsi_sense_data_fixed, sense_key_spec[2]) - + offsetof(struct scsi_sense_data_fixed, extra_len); /* Fill out the supplied CTIO */ if (ctio != NULL) { @@ -298,7 +298,7 @@ tcmd_inquiry(struct ccb_accept_tio *atio struct scsi_inquiry *inq; struct atio_descr *a_descr; struct initiator_state *istate; - struct scsi_sense_data *sense; + struct scsi_sense_data_fixed *sense; a_descr = (struct atio_descr *)atio->ccb_h.targ_descr; inq = (struct scsi_inquiry *)a_descr->cdb; @@ -310,7 +310,7 @@ tcmd_inquiry(struct ccb_accept_tio *atio * complain if EVPD or CMDDT is set. */ istate = tcmd_get_istate(ctio->init_id); - sense = &istate->sense_data; + sense = (struct scsi_sense_data_fixed *)&istate->sense_data; if ((inq->byte2 & SI_EVPD) != 0) { tcmd_illegal_req(atio, ctio); sense->sense_key_spec[0] = SSD_SCS_VALID | SSD_FIELDPTR_CMD | @@ -376,7 +376,7 @@ static int tcmd_req_sense(struct ccb_accept_tio *atio, struct ccb_scsiio *ctio) { struct scsi_request_sense *rsense; - struct scsi_sense_data *sense; + struct scsi_sense_data_fixed *sense; struct initiator_state *istate; size_t dlen; struct atio_descr *a_descr; @@ -385,7 +385,7 @@ tcmd_req_sense(struct ccb_accept_tio *at rsense = (struct scsi_request_sense *)a_descr->cdb; istate = tcmd_get_istate(ctio->init_id); - sense = &istate->sense_data; + sense = (struct scsi_sense_data_fixed *)&istate->sense_data; if (debug) { cdb_debug(a_descr->cdb, "REQ SENSE from %u: ", atio->init_id); @@ -400,7 +400,7 @@ tcmd_req_sense(struct ccb_accept_tio *at } bcopy(sense, ctio->data_ptr, sizeof(struct scsi_sense_data)); - dlen = offsetof(struct scsi_sense_data, extra_len) + + dlen = offsetof(struct scsi_sense_data_fixed, extra_len) + sense->extra_len + 1; ctio->dxfer_len = min(dlen, SCSI_CDB6_LEN(rsense->length)); ctio->ccb_h.flags |= CAM_DIR_IN | CAM_SEND_STATUS; @@ -482,7 +482,7 @@ tcmd_rdwr(struct ccb_accept_tio *atio, s c_descr = (struct ctio_descr *)ctio->ccb_h.targ_descr; /* Command needs to be decoded */ - if ((a_descr->flags & CAM_DIR_MASK) == CAM_DIR_RESV) { + if ((a_descr->flags & CAM_DIR_MASK) == CAM_DIR_BOTH) { if (debug) warnx("Calling rdwr_decode"); ret = tcmd_rdwr_decode(atio, ctio); Modified: head/share/examples/scsi_target/scsi_target.c ============================================================================== --- head/share/examples/scsi_target/scsi_target.c Mon Oct 3 20:27:51 2011 (r225949) +++ head/share/examples/scsi_target/scsi_target.c Mon Oct 3 20:32:55 2011 (r225950) @@ -651,7 +651,7 @@ work_atio(struct ccb_accept_tio *atio) * receiving this ATIO. */ if (atio->sense_len != 0) { - struct scsi_sense_data *sense; + struct scsi_sense_data_fixed *sense; if (debug) { warnx("ATIO with %u bytes sense received", @@ -825,9 +825,9 @@ work_inot(struct ccb_immed_notify *inot) /* If there is sense data, use it */ if (sense != 0) { - struct scsi_sense_data *sense; + struct scsi_sense_data_fixed *sense; - sense = &inot->sense_data; + sense = (struct scsi_sense_data_fixed *)&inot->sense_data; tcmd_sense(inot->initiator_id, NULL, sense->flags, sense->add_sense_code, sense->add_sense_code_qual); if (debug) Modified: head/share/misc/scsi_modes ============================================================================== --- head/share/misc/scsi_modes Mon Oct 3 20:27:51 2011 (r225949) +++ head/share/misc/scsi_modes Mon Oct 3 20:32:55 2011 (r225950) @@ -50,19 +50,32 @@ # ALL DEVICE TYPES 0x0a "Control Mode Page" { - {Reserved} *t7 + {TST} t3 + {TMF_ONLY} t1 + {DPICZ} t1 + {D_SENSE} t1 + {GLTSD} t1 {RLEC} t1 {Queue Algorithm Modifier} t4 - {Reserved} *t2 - {QErr} t1 + {NUAR} t1 + {QErr} t2 {DQue} t1 {EECA} t1 - {Reserved} *t4 + {RAC} t1 + {UA_INTLCK_CTRL} t2 + {SWP} t1 {RAENP} t1 {UAAENP} t1 {EAENP} t1 - {Reserved} *i1 + {ATO} t1 + {TAS} t1 + {ATMPE} t1 + {RWWP} t1 + {Reserved} *t1 + {Autoload Mode} t3 {Ready AEN Holdoff Period} i2 + {Busy Timeout Period} i2 + {Extended Self-Test Completion Time} i2 } 0x02 "Disconnect-Reconnect Page" { Modified: head/sys/cam/cam_ccb.h ============================================================================== --- head/sys/cam/cam_ccb.h Mon Oct 3 20:27:51 2011 (r225949) +++ head/sys/cam/cam_ccb.h Mon Oct 3 20:32:55 2011 (r225950) @@ -539,7 +539,7 @@ struct ccb_dev_match { /* * Definitions for the path inquiry CCB fields. */ -#define CAM_VERSION 0x15 /* Hex value for current version */ +#define CAM_VERSION 0x16 /* Hex value for current version */ typedef enum { PI_MDP_ABLE = 0x80, /* Supports MDP message */ Modified: head/sys/cam/cam_periph.c ============================================================================== --- head/sys/cam/cam_periph.c Mon Oct 3 20:27:51 2011 (r225949) +++ head/sys/cam/cam_periph.c Mon Oct 3 20:32:55 2011 (r225950) @@ -1085,7 +1085,6 @@ camperiphsensedone(struct cam_periph *pe union ccb *saved_ccb = (union ccb *)done_ccb->ccb_h.saved_ccb_ptr; cam_status status; int frozen = 0; - u_int sense_key; int depth = done_ccb->ccb_h.recovery_depth; status = done_ccb->ccb_h.status; @@ -1101,22 +1100,25 @@ camperiphsensedone(struct cam_periph *pe switch (status) { case CAM_REQ_CMP: { + int error_code, sense_key, asc, ascq; + + scsi_extract_sense_len(&saved_ccb->csio.sense_data, + saved_ccb->csio.sense_len - + saved_ccb->csio.sense_resid, + &error_code, &sense_key, &asc, &ascq, + /*show_errors*/ 1); /* * If we manually retrieved sense into a CCB and got * something other than "NO SENSE" send the updated CCB * back to the client via xpt_done() to be processed via * the error recovery code again. */ - sense_key = saved_ccb->csio.sense_data.flags; - sense_key &= SSD_KEY; - if (sense_key != SSD_KEY_NO_SENSE) { - saved_ccb->ccb_h.status |= - CAM_AUTOSNS_VALID; + if ((sense_key != -1) + && (sense_key != SSD_KEY_NO_SENSE)) { + saved_ccb->ccb_h.status |= CAM_AUTOSNS_VALID; } else { - saved_ccb->ccb_h.status &= - ~CAM_STATUS_MASK; - saved_ccb->ccb_h.status |= - CAM_AUTOSENSE_FAIL; + saved_ccb->ccb_h.status &= ~CAM_STATUS_MASK; + saved_ccb->ccb_h.status |= CAM_AUTOSENSE_FAIL; } saved_ccb->csio.sense_resid = done_ccb->csio.resid; bcopy(saved_ccb, done_ccb, sizeof(union ccb)); @@ -1198,12 +1200,15 @@ camperiphdone(struct cam_periph *periph, if (status & CAM_AUTOSNS_VALID) { struct ccb_getdev cgd; struct scsi_sense_data *sense; - int error_code, sense_key, asc, ascq; + int error_code, sense_key, asc, ascq, sense_len; scsi_sense_action err_action; sense = &done_ccb->csio.sense_data; - scsi_extract_sense(sense, &error_code, - &sense_key, &asc, &ascq); + sense_len = done_ccb->csio.sense_len - + done_ccb->csio.sense_resid; + scsi_extract_sense_len(sense, sense_len, &error_code, + &sense_key, &asc, &ascq, + /*show_errors*/ 1); /* * Grab the inquiry data for this device. */ Modified: head/sys/cam/scsi/scsi_all.c ============================================================================== --- head/sys/cam/scsi/scsi_all.c Mon Oct 3 20:27:51 2011 (r225949) +++ head/sys/cam/scsi/scsi_all.c Mon Oct 3 20:32:55 2011 (r225950) @@ -31,6 +31,8 @@ __FBSDID("$FreeBSD$"); #include +#include +#include #ifdef _KERNEL #include @@ -54,6 +56,7 @@ __FBSDID("$FreeBSD$"); #include #ifndef _KERNEL #include +#include #ifndef FALSE #define FALSE 0 @@ -608,14 +611,24 @@ scsi_op_desc(u_int16_t opcode, struct sc struct op_table_entry *table[2]; int num_tables; - pd_type = SID_TYPE(inq_data); + /* + * If we've got inquiry data, use it to determine what type of + * device we're dealing with here. Otherwise, assume direct + * access. + */ + if (inq_data == NULL) { + pd_type = T_DIRECT; + match = NULL; + } else { + pd_type = SID_TYPE(inq_data); - match = cam_quirkmatch((caddr_t)inq_data, - (caddr_t)scsi_op_quirk_table, - sizeof(scsi_op_quirk_table)/ - sizeof(*scsi_op_quirk_table), - sizeof(*scsi_op_quirk_table), - scsi_inquiry_match); + match = cam_quirkmatch((caddr_t)inq_data, + (caddr_t)scsi_op_quirk_table, + sizeof(scsi_op_quirk_table)/ + sizeof(*scsi_op_quirk_table), + sizeof(*scsi_op_quirk_table), + scsi_inquiry_match); + } if (match != NULL) { table[0] = ((struct scsi_op_quirk_entry *)match)->op_table; @@ -699,7 +712,7 @@ const struct sense_key_table_entry sense { SSD_KEY_EQUAL, SS_NOP, "EQUAL" }, { SSD_KEY_VOLUME_OVERFLOW, SS_FATAL|EIO, "VOLUME OVERFLOW" }, { SSD_KEY_MISCOMPARE, SS_NOP, "MISCOMPARE" }, - { SSD_KEY_RESERVED, SS_FATAL|EIO, "RESERVED" } + { SSD_KEY_COMPLETED, SS_NOP, "COMPLETED" } }; const int sense_key_table_size = @@ -1062,25 +1075,25 @@ static struct asc_table_entry asc_table[ { SST(0x10, 0x03, SS_RDEF, /* XXX TBD */ "Logical block reference tag check failed") }, /* DT WRO BK */ - { SST(0x11, 0x00, SS_RDEF, + { SST(0x11, 0x00, SS_FATAL|EIO, "Unrecovered read error") }, /* DT WRO BK */ - { SST(0x11, 0x01, SS_RDEF, + { SST(0x11, 0x01, SS_FATAL|EIO, "Read retries exhausted") }, /* DT WRO BK */ - { SST(0x11, 0x02, SS_RDEF, + { SST(0x11, 0x02, SS_FATAL|EIO, "Error too long to correct") }, /* DT W O BK */ - { SST(0x11, 0x03, SS_RDEF, + { SST(0x11, 0x03, SS_FATAL|EIO, "Multiple read errors") }, /* D W O BK */ - { SST(0x11, 0x04, SS_RDEF, + { SST(0x11, 0x04, SS_FATAL|EIO, "Unrecovered read error - auto reallocate failed") }, /* WRO B */ - { SST(0x11, 0x05, SS_RDEF, + { SST(0x11, 0x05, SS_FATAL|EIO, "L-EC uncorrectable error") }, /* WRO B */ - { SST(0x11, 0x06, SS_RDEF, + { SST(0x11, 0x06, SS_FATAL|EIO, "CIRC unrecovered error") }, /* W O B */ { SST(0x11, 0x07, SS_RDEF, @@ -1095,10 +1108,10 @@ static struct asc_table_entry asc_table[ { SST(0x11, 0x0A, SS_RDEF, "Miscorrected error") }, /* D W O BK */ - { SST(0x11, 0x0B, SS_RDEF, + { SST(0x11, 0x0B, SS_FATAL|EIO, "Unrecovered read error - recommend reassignment") }, /* D W O BK */ - { SST(0x11, 0x0C, SS_RDEF, + { SST(0x11, 0x0C, SS_FATAL|EIO, "Unrecovered read error - recommend rewrite the data") }, /* DT WRO B */ { SST(0x11, 0x0D, SS_RDEF, @@ -2790,7 +2803,10 @@ scsi_sense_desc(int sense_key, int asc, &sense_entry, &asc_entry); - *sense_key_desc = sense_entry->desc; + if (sense_entry != NULL) + *sense_key_desc = sense_entry->desc; + else + *sense_key_desc = "Invalid Sense Key"; if (asc_entry != NULL) *asc_desc = asc_entry->desc; @@ -2816,10 +2832,12 @@ scsi_error_action(struct ccb_scsiio *csi int error_code, sense_key, asc, ascq; scsi_sense_action action; - scsi_extract_sense(&csio->sense_data, &error_code, - &sense_key, &asc, &ascq); + scsi_extract_sense_len(&csio->sense_data, csio->sense_len - + csio->sense_resid, &error_code, + &sense_key, &asc, &ascq, /*show_errors*/ 1); - if (error_code == SSD_DEFERRED_ERROR) { + if ((error_code == SSD_DEFERRED_ERROR) + || (error_code == SSD_DESC_DEFERRED_ERROR)) { /* * XXX dufault@FreeBSD.org * This error doesn't relate to the command associated @@ -2857,8 +2875,10 @@ scsi_error_action(struct ccb_scsiio *csi if (asc_entry != NULL && (asc != 0 || ascq != 0)) action = asc_entry->action; - else + else if (sense_entry != NULL) action = sense_entry->action; + else + action = SS_RETRY|SSQ_DECREMENT_COUNT|SSQ_PRINT_SENSE; if (sense_key == SSD_KEY_RECOVERED_ERROR) { /* @@ -3040,308 +3060,1530 @@ scsi_command_string(struct cam_device *d return(0); } - /* - * scsi_sense_sbuf() returns 0 for success and -1 for failure. + * Iterate over sense descriptors. Each descriptor is passed into iter_func(). + * If iter_func() returns 0, list traversal continues. If iter_func() + * returns non-zero, list traversal is stopped. */ -#ifdef _KERNEL -int -scsi_sense_sbuf(struct ccb_scsiio *csio, struct sbuf *sb, - scsi_sense_string_flags flags) -#else /* !_KERNEL */ -int -scsi_sense_sbuf(struct cam_device *device, struct ccb_scsiio *csio, - struct sbuf *sb, scsi_sense_string_flags flags) -#endif /* _KERNEL/!_KERNEL */ +void +scsi_desc_iterate(struct scsi_sense_data_desc *sense, u_int sense_len, + int (*iter_func)(struct scsi_sense_data_desc *sense, + u_int, struct scsi_sense_desc_header *, + void *), void *arg) { - struct scsi_sense_data *sense; - struct scsi_inquiry_data *inq_data; -#ifdef _KERNEL - struct ccb_getdev *cgd; -#endif /* _KERNEL */ - u_int32_t info; - int error_code; - int sense_key; - int asc, ascq; - char path_str[64]; - -#ifndef _KERNEL - if (device == NULL) - return(-1); -#endif /* !_KERNEL */ - if ((csio == NULL) || (sb == NULL)) - return(-1); + int cur_pos; + int desc_len; /* - * If the CDB is a physical address, we can't deal with it.. + * First make sure the extra length field is present. */ - if ((csio->ccb_h.flags & CAM_CDB_PHYS) != 0) - flags &= ~SSS_FLAG_PRINT_COMMAND; + if (SSD_DESC_IS_PRESENT(sense, sense_len, extra_len) == 0) + return; -#ifdef _KERNEL - xpt_path_string(csio->ccb_h.path, path_str, sizeof(path_str)); -#else /* !_KERNEL */ - cam_path_string(device, path_str, sizeof(path_str)); -#endif /* _KERNEL/!_KERNEL */ + /* + * The length of data actually returned may be different than the + * extra_len recorded in the sturcture. + */ + desc_len = sense_len -offsetof(struct scsi_sense_data_desc, sense_desc); -#ifdef _KERNEL - if ((cgd = (struct ccb_getdev*)xpt_alloc_ccb_nowait()) == NULL) - return(-1); /* - * Get the device information. + * Limit this further by the extra length reported, and the maximum + * allowed extra length. */ - xpt_setup_ccb(&cgd->ccb_h, - csio->ccb_h.path, - CAM_PRIORITY_NORMAL); - cgd->ccb_h.func_code = XPT_GDEV_TYPE; - xpt_action((union ccb *)cgd); + desc_len = MIN(desc_len, MIN(sense->extra_len, SSD_EXTRA_MAX)); /* - * If the device is unconfigured, just pretend that it is a hard - * drive. scsi_op_desc() needs this. + * Subtract the size of the header from the descriptor length. + * This is to ensure that we have at least the header left, so we + * don't have to check that inside the loop. This can wind up + * being a negative value. */ - if (cgd->ccb_h.status == CAM_DEV_NOT_THERE) - cgd->inq_data.device = T_DIRECT; + desc_len -= sizeof(struct scsi_sense_desc_header); - inq_data = &cgd->inq_data; + for (cur_pos = 0; cur_pos < desc_len;) { + struct scsi_sense_desc_header *header; -#else /* !_KERNEL */ + header = (struct scsi_sense_desc_header *) + &sense->sense_desc[cur_pos]; - inq_data = &device->inq_data; + /* + * Check to make sure we have the entire descriptor. We + * don't call iter_func() unless we do. + * + * Note that although cur_pos is at the beginning of the + * descriptor, desc_len already has the header length + * subtracted. So the comparison of the length in the + * header (which does not include the header itself) to + * desc_len - cur_pos is correct. + */ + if (header->length > (desc_len - cur_pos)) + break; -#endif /* _KERNEL/!_KERNEL */ + if (iter_func(sense, sense_len, header, arg) != 0) + break; - sense = NULL; + cur_pos += sizeof(*header) + header->length; + } +} - if (flags & SSS_FLAG_PRINT_COMMAND) { +struct scsi_find_desc_info { + uint8_t desc_type; + struct scsi_sense_desc_header *header; +}; - sbuf_cat(sb, path_str); +static int +scsi_find_desc_func(struct scsi_sense_data_desc *sense, u_int sense_len, + struct scsi_sense_desc_header *header, void *arg) +{ + struct scsi_find_desc_info *desc_info; -#ifdef _KERNEL - scsi_command_string(csio, sb); -#else /* !_KERNEL */ - scsi_command_string(device, csio, sb); -#endif /* _KERNEL/!_KERNEL */ - sbuf_printf(sb, "\n"); - } + desc_info = (struct scsi_find_desc_info *)arg; + + if (header->desc_type == desc_info->desc_type) { + desc_info->header = header; + + /* We found the descriptor, tell the iterator to stop. */ + return (1); + } else + return (0); +} + +/* + * Given a descriptor type, return a pointer to it if it is in the sense + * data and not truncated. Avoiding truncating sense data will simplify + * things significantly for the caller. + */ +uint8_t * +scsi_find_desc(struct scsi_sense_data_desc *sense, u_int sense_len, + uint8_t desc_type) +{ + struct scsi_find_desc_info desc_info; + + desc_info.desc_type = desc_type; + desc_info.header = NULL; + + scsi_desc_iterate(sense, sense_len, scsi_find_desc_func, &desc_info); + + return ((uint8_t *)desc_info.header); +} + +/* + * Fill in SCSI sense data with the specified parameters. This routine can + * fill in either fixed or descriptor type sense data. + */ +void +scsi_set_sense_data_va(struct scsi_sense_data *sense_data, + scsi_sense_data_type sense_format, int current_error, + int sense_key, int asc, int ascq, va_list ap) +{ + int descriptor_sense; + scsi_sense_elem_type elem_type; /* - * If the sense data is a physical pointer, forget it. + * Determine whether to return fixed or descriptor format sense + * data. If the user specifies SSD_TYPE_NONE for some reason, + * they'll just get fixed sense data. */ - if (csio->ccb_h.flags & CAM_SENSE_PTR) { - if (csio->ccb_h.flags & CAM_SENSE_PHYS) { -#ifdef _KERNEL - xpt_free_ccb((union ccb*)cgd); -#endif /* _KERNEL/!_KERNEL */ - return(-1); - } else { - /* - * bcopy the pointer to avoid unaligned access - * errors on finicky architectures. We don't - * ensure that the sense data is pointer aligned. - */ - bcopy(&csio->sense_data, &sense, - sizeof(struct scsi_sense_data *)); - } - } else { + if (sense_format == SSD_TYPE_DESC) + descriptor_sense = 1; + else + descriptor_sense = 0; + + /* + * Zero the sense data, so that we don't pass back any garbage data + * to the user. + */ + memset(sense_data, 0, sizeof(*sense_data)); + + if (descriptor_sense != 0) { + struct scsi_sense_data_desc *sense; + + sense = (struct scsi_sense_data_desc *)sense_data; /* - * If the physical sense flag is set, but the sense pointer - * is not also set, we assume that the user is an idiot and - * return. (Well, okay, it could be that somehow, the - * entire csio is physical, but we would have probably core - * dumped on one of the bogus pointer deferences above - * already.) + * The descriptor sense format eliminates the use of the + * valid bit. */ - if (csio->ccb_h.flags & CAM_SENSE_PHYS) { -#ifdef _KERNEL - xpt_free_ccb((union ccb*)cgd); -#endif /* _KERNEL/!_KERNEL */ - return(-1); - } else - sense = &csio->sense_data; - } - + if (current_error != 0) + sense->error_code = SSD_DESC_CURRENT_ERROR; + else + sense->error_code = SSD_DESC_DEFERRED_ERROR; + sense->sense_key = sense_key; + sense->add_sense_code = asc; + sense->add_sense_code_qual = ascq; + /* + * Start off with no extra length, since the above data + * fits in the standard descriptor sense information. + */ + sense->extra_len = 0; + while ((elem_type = (scsi_sense_elem_type)va_arg(ap, + scsi_sense_elem_type)) != SSD_ELEM_NONE) { + int sense_len, len_to_copy; + uint8_t *data; + + if (elem_type >= SSD_ELEM_MAX) { + printf("%s: invalid sense type %d\n", __func__, + elem_type); + break; + } - sbuf_cat(sb, path_str); + sense_len = (int)va_arg(ap, int); + len_to_copy = MIN(sense_len, SSD_EXTRA_MAX - + sense->extra_len); + data = (uint8_t *)va_arg(ap, uint8_t *); - error_code = sense->error_code & SSD_ERRCODE; - sense_key = sense->flags & SSD_KEY; + /* + * We've already consumed the arguments for this one. + */ + if (elem_type == SSD_ELEM_SKIP) + continue; - sbuf_printf(sb, "SCSI sense: "); - switch (error_code) { - case SSD_DEFERRED_ERROR: - sbuf_printf(sb, "Deferred error: "); + switch (elem_type) { + case SSD_ELEM_DESC: { - /* FALLTHROUGH */ - case SSD_CURRENT_ERROR: - { - const char *sense_key_desc; - const char *asc_desc; + /* + * This is a straight descriptor. All we + * need to do is copy the data in. + */ + bcopy(data, &sense->sense_desc[ + sense->extra_len], len_to_copy); + sense->extra_len += len_to_copy; + break; + } + case SSD_ELEM_SKS: { + struct scsi_sense_sks sks; - asc = (sense->extra_len >= 5) ? sense->add_sense_code : 0; - ascq = (sense->extra_len >= 6) ? sense->add_sense_code_qual : 0; - scsi_sense_desc(sense_key, asc, ascq, inq_data, - &sense_key_desc, &asc_desc); - sbuf_cat(sb, sense_key_desc); + bzero(&sks, sizeof(sks)); - info = scsi_4btoul(sense->info); - - if (sense->error_code & SSD_ERRCODE_VALID) { + /* + * This is already-formatted sense key + * specific data. We just need to fill out + * the header and copy everything in. + */ + bcopy(data, &sks.sense_key_spec, + MIN(len_to_copy, + sizeof(sks.sense_key_spec))); + + sks.desc_type = SSD_DESC_SKS; + sks.length = sizeof(sks) - + offsetof(struct scsi_sense_sks, reserved1); + bcopy(&sks,&sense->sense_desc[sense->extra_len], + sizeof(sks)); + sense->extra_len += sizeof(sks); + break; + } + case SSD_ELEM_INFO: + case SSD_ELEM_COMMAND: { + struct scsi_sense_command cmd; + struct scsi_sense_info info; + uint8_t *data_dest; + uint8_t *descriptor; + int descriptor_size, i, copy_len; + + bzero(&cmd, sizeof(cmd)); + bzero(&info, sizeof(info)); + + /* + * Command or information data. The + * operate in pretty much the same way. + */ + if (elem_type == SSD_ELEM_COMMAND) { + len_to_copy = MIN(len_to_copy, + sizeof(cmd.command_info)); + descriptor = (uint8_t *)&cmd; + descriptor_size = sizeof(cmd); + data_dest =(uint8_t *)&cmd.command_info; + cmd.desc_type = SSD_DESC_COMMAND; + cmd.length = sizeof(cmd) - + offsetof(struct scsi_sense_command, + reserved); + } else { + len_to_copy = MIN(len_to_copy, + sizeof(info.info)); + descriptor = (uint8_t *)&info; + descriptor_size = sizeof(cmd); + data_dest = (uint8_t *)&info.info; + info.desc_type = SSD_DESC_INFO; + info.byte2 = SSD_INFO_VALID; + info.length = sizeof(info) - + offsetof(struct scsi_sense_info, + byte2); + } - switch (sense_key) { - case SSD_KEY_NOT_READY: - case SSD_KEY_ILLEGAL_REQUEST: - case SSD_KEY_UNIT_ATTENTION: - case SSD_KEY_DATA_PROTECT: + /* + * Copy this in reverse because the spec + * (SPC-4) says that when 4 byte quantities + * are stored in this 8 byte field, the + * first four bytes shall be 0. + * + * So we fill the bytes in from the end, and + * if we have less than 8 bytes to copy, + * the initial, most significant bytes will + * be 0. + */ + for (i = sense_len - 1; i >= 0 && + len_to_copy > 0; i--, len_to_copy--) + data_dest[len_to_copy - 1] = data[i]; + + /* + * This calculation looks much like the + * initial len_to_copy calculation, but + * we have to do it again here, because + * we're looking at a larger amount that + * may or may not fit. It's not only the + * data the user passed in, but also the + * rest of the descriptor. + */ + copy_len = MIN(descriptor_size, + SSD_EXTRA_MAX - sense->extra_len); + bcopy(descriptor, &sense->sense_desc[ + sense->extra_len], copy_len); + sense->extra_len += copy_len; + break; + } + case SSD_ELEM_FRU: { + struct scsi_sense_fru fru; + int copy_len; + + bzero(&fru, sizeof(fru)); + + fru.desc_type = SSD_DESC_FRU; + fru.length = sizeof(fru) - + offsetof(struct scsi_sense_fru, reserved); + fru.fru = *data; + + copy_len = MIN(sizeof(fru), SSD_EXTRA_MAX - + sense->extra_len); + bcopy(&fru, &sense->sense_desc[ + sense->extra_len], copy_len); + sense->extra_len += copy_len; break; - case SSD_KEY_BLANK_CHECK: - sbuf_printf(sb, " req sz: %d (decimal)", info); + } + case SSD_ELEM_STREAM: { + struct scsi_sense_stream stream_sense; + int copy_len; + + bzero(&stream_sense, sizeof(stream_sense)); + stream_sense.desc_type = SSD_DESC_STREAM; + stream_sense.length = sizeof(stream_sense) - + offsetof(struct scsi_sense_stream, reserved); + stream_sense.byte3 = *data; + + copy_len = MIN(sizeof(stream_sense), + SSD_EXTRA_MAX - sense->extra_len); + bcopy(&stream_sense, &sense->sense_desc[ + sense->extra_len], copy_len); + sense->extra_len += copy_len; break; + } default: - if (info) { - if (sense->flags & SSD_ILI) { - sbuf_printf(sb, " ILI (length " - "mismatch): %d", info); - - } else { - sbuf_printf(sb, " info:%x", - info); - } - } + /* + * We shouldn't get here, but if we do, do + * nothing. We've already consumed the + * arguments above. + */ + break; } - } else if (info) { - sbuf_printf(sb, " info?:%x", info); } + } else { + struct scsi_sense_data_fixed *sense; - if (sense->extra_len >= 4) { - if (bcmp(sense->cmd_spec_info, "\0\0\0\0", 4)) { - sbuf_printf(sb, " csi:%x,%x,%x,%x", - sense->cmd_spec_info[0], - sense->cmd_spec_info[1], - sense->cmd_spec_info[2], - sense->cmd_spec_info[3]); - } - } + sense = (struct scsi_sense_data_fixed *)sense_data; - sbuf_printf(sb, " asc:%x,%x (%s)", asc, ascq, asc_desc); + if (current_error != 0) + sense->error_code = SSD_CURRENT_ERROR; + else + sense->error_code = SSD_DEFERRED_ERROR; - if (sense->extra_len >= 7 && sense->fru) { - sbuf_printf(sb, " field replaceable unit: %x", - sense->fru); - } + sense->flags = sense_key; + sense->add_sense_code = asc; + sense->add_sense_code_qual = ascq; + /* + * We've set the ASC and ASCQ, so we have 6 more bytes of + * valid data. If we wind up setting any of the other + * fields, we'll bump this to 10 extra bytes. + */ + sense->extra_len = 6; - if ((sense->extra_len >= 10) - && (sense->sense_key_spec[0] & SSD_SCS_VALID) != 0) { - switch(sense_key) { - case SSD_KEY_ILLEGAL_REQUEST: { - int bad_command; - char tmpstr2[40]; - - if (sense->sense_key_spec[0] & 0x40) - bad_command = 1; - else - bad_command = 0; - - tmpstr2[0] = '\0'; - - /* Bit pointer is valid */ - if (sense->sense_key_spec[0] & 0x08) - snprintf(tmpstr2, sizeof(tmpstr2), - "bit %d ", - sense->sense_key_spec[0] & 0x7); - sbuf_printf(sb, ": %s byte %d %sis invalid", - bad_command ? "Command" : "Data", - scsi_2btoul( - &sense->sense_key_spec[1]), - tmpstr2); + while ((elem_type = (scsi_sense_elem_type)va_arg(ap, + scsi_sense_elem_type)) != SSD_ELEM_NONE) { + int sense_len, len_to_copy; + uint8_t *data; + + if (elem_type >= SSD_ELEM_MAX) { + printf("%s: invalid sense type %d\n", __func__, + elem_type); break; } - case SSD_KEY_RECOVERED_ERROR: - case SSD_KEY_HARDWARE_ERROR: - case SSD_KEY_MEDIUM_ERROR: - sbuf_printf(sb, " actual retry count: %d", - scsi_2btoul( - &sense->sense_key_spec[1])); + /* + * If we get in here, just bump the extra length to + * 10 bytes. That will encompass anything we're + * going to set here. + */ + sense->extra_len = 10; + sense_len = (int)va_arg(ap, int); + len_to_copy = MIN(sense_len, SSD_EXTRA_MAX - + sense->extra_len); + data = (uint8_t *)va_arg(ap, uint8_t *); + + switch (elem_type) { + case SSD_ELEM_SKS: + /* + * The user passed in pre-formatted sense + * key specific data. + */ + bcopy(data, &sense->sense_key_spec[0], + MIN(sizeof(sense->sense_key_spec), + sense_len)); break; - default: - sbuf_printf(sb, " sks:%#x,%#x", - sense->sense_key_spec[0], - scsi_2btoul( - &sense->sense_key_spec[1])); + case SSD_ELEM_INFO: + case SSD_ELEM_COMMAND: { + uint8_t *data_dest; + int i; + + if (elem_type == SSD_ELEM_COMMAND) + data_dest = &sense->cmd_spec_info[0]; + else { + data_dest = &sense->info[0]; + /* + * We're setting the info field, so + * set the valid bit. + */ + sense->error_code |= SSD_ERRCODE_VALID; + } + + /* + * Copy this in reverse so that if we have + * less than 4 bytes to fill, the least + * significant bytes will be at the end. + * If we have more than 4 bytes, only the + * least significant bytes will be included. + */ + for (i = sense_len - 1; i >= 0 && + len_to_copy > 0; i--, len_to_copy--) *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***