diff options
author | gibbs <gibbs@FreeBSD.org> | 1999-04-07 22:59:12 +0000 |
---|---|---|
committer | gibbs <gibbs@FreeBSD.org> | 1999-04-07 22:59:12 +0000 |
commit | 0c39daf09dc2ddf892202ede4d9b0ca4516c8799 (patch) | |
tree | 67668507db66a36b2f30083d6b4675f5a3822e00 /sys/dev/advansys | |
parent | e139476c732198be23f1fa3cba1c76800676123f (diff) | |
download | FreeBSD-src-0c39daf09dc2ddf892202ede4d9b0ca4516c8799.zip FreeBSD-src-0c39daf09dc2ddf892202ede4d9b0ca4516c8799.tar.gz |
Beef up the error handling routine to handle more errors.
Compensate for a bug in the AdvanSys firmware where a valid queue
full condition can be reported via a different error code.
Diffstat (limited to 'sys/dev/advansys')
-rw-r--r-- | sys/dev/advansys/advansys.c | 92 |
1 files changed, 67 insertions, 25 deletions
diff --git a/sys/dev/advansys/advansys.c b/sys/dev/advansys/advansys.c index f0588f8..83c8613 100644 --- a/sys/dev/advansys/advansys.c +++ b/sys/dev/advansys/advansys.c @@ -32,7 +32,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: advansys.c,v 1.6 1998/12/04 22:54:44 archie Exp $ + * $Id: advansys.c,v 1.7 1998/12/22 18:12:09 gibbs Exp $ */ /* * Ported from: @@ -1029,6 +1029,9 @@ adv_done(struct adv_softc *adv, union ccb *ccb, u_int done_stat, * ccb to be completed twice. */ ccb->ccb_h.ccb_cinfo_ptr = NULL; + + LIST_REMOVE(&ccb->ccb_h, sim_links.le); + untimeout(adv_timeout, ccb, ccb->ccb_h.timeout_ch); if ((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE) { bus_dmasync_op_t op; @@ -1042,25 +1045,42 @@ adv_done(struct adv_softc *adv, union ccb *ccb, u_int done_stat, switch (done_stat) { case QD_NO_ERROR: - switch (host_stat) { - case QHSTA_NO_ERROR: + if (host_stat == QHSTA_NO_ERROR) { ccb->ccb_h.status = CAM_REQ_CMP; break; - case QHSTA_M_SEL_TIMEOUT: - ccb->ccb_h.status = CAM_SEL_TIMEOUT; - break; - default: - xpt_print_path(ccb->ccb_h.path); - printf("adv_done - queue done without error, " - "unknown host status %x\n", host_stat); - /* XXX Can I get more explicit information here? */ - ccb->ccb_h.status = CAM_REQ_CMP_ERR; - break; } - break; - + xpt_print_path(ccb->ccb_h.path); + printf("adv_done - queue done without error, " + "but host status non-zero(%x)\n", host_stat); + /*FALLTHROUGH*/ case QD_WITH_ERROR: switch (host_stat) { + case QHSTA_M_TARGET_STATUS_BUSY: + case QHSTA_M_BAD_QUEUE_FULL_OR_BUSY: + /* + * Assume that if we were a tagged transaction + * the target reported queue full. Otherwise, + * report busy. The firmware really should just + * pass the original status back up to us even + * if it thinks the target was in error for + * returning this status as no other transactions + * from this initiator are in effect, but this + * ignores multi-initiator setups and there is + * evidence that the firmware gets its per-device + * transaction counts screwed up occassionally. + */ + ccb->ccb_h.status |= CAM_SCSI_STATUS_ERROR; + if ((ccb->ccb_h.flags & CAM_TAG_ACTION_VALID) != 0 + && host_stat != QHSTA_M_TARGET_STATUS_BUSY) + scsi_status = SCSI_STATUS_QUEUE_FULL; + else + scsi_status = SCSI_STATUS_BUSY; + adv_abort_ccb(adv, ccb->ccb_h.target_id, + ccb->ccb_h.target_lun, + /*ccb*/NULL, CAM_REQUEUE_REQ, + /*queued_only*/TRUE); + /*FALLTHROUGH*/ + case QHSTA_M_NO_AUTO_REQ_SENSE: case QHSTA_NO_ERROR: ccb->csio.scsi_status = scsi_status; switch (scsi_status) { @@ -1087,13 +1107,39 @@ adv_done(struct adv_softc *adv, union ccb *ccb, u_int done_stat, case QHSTA_M_SEL_TIMEOUT: ccb->ccb_h.status = CAM_SEL_TIMEOUT; break; - default: - xpt_print_path(ccb->ccb_h.path); - printf("adv_done - queue done with error, " - "unknown host status %x\n", host_stat); - /* XXX Can I get more explicit information here? */ - ccb->ccb_h.status = CAM_REQ_CMP_ERR; + case QHSTA_M_DATA_OVER_RUN: + ccb->ccb_h.status = CAM_DATA_RUN_ERR; + break; + case QHSTA_M_UNEXPECTED_BUS_FREE: + ccb->ccb_h.status = CAM_UNEXP_BUSFREE; + break; + case QHSTA_M_BAD_BUS_PHASE_SEQ: + ccb->ccb_h.status = CAM_SEQUENCE_FAIL; break; + case QHSTA_M_BAD_CMPL_STATUS_IN: + /* No command complete after a status message */ + ccb->ccb_h.status = CAM_SEQUENCE_FAIL; + break; + case QHSTA_D_EXE_SCSI_Q_BUSY_TIMEOUT: + case QHSTA_M_WTM_TIMEOUT: + case QHSTA_M_HUNG_REQ_SCSI_BUS_RESET: + /* The SCSI bus hung in a phase */ + ccb->ccb_h.status = CAM_SEQUENCE_FAIL; + adv_reset_bus(adv); + break; + case QHSTA_D_QDONE_SG_LIST_CORRUPTED: + case QHSTA_D_ASC_DVC_ERROR_CODE_SET: + case QHSTA_D_HOST_ABORT_FAILED: + case QHSTA_D_EXE_SCSI_Q_FAILED: + case QHSTA_D_ASPI_NO_BUF_POOL: + case QHSTA_M_AUTO_REQ_SENSE_FAIL: + case QHSTA_M_BAD_TAG_CODE: + case QHSTA_D_LRAM_CMP_ERROR: + case QHSTA_M_MICRO_CODE_ERROR_HALT: + default: + panic("%s: Unhandled Host status error %x", + adv_name(adv), host_stat); + /* NOTREACHED */ } break; @@ -1121,10 +1167,6 @@ adv_done(struct adv_softc *adv, union ccb *ccb, u_int done_stat, adv->openings_needed = 0; } } - /* Remove from the pending list */ - LIST_REMOVE(&ccb->ccb_h, sim_links.le); - - untimeout(adv_timeout, ccb, ccb->ccb_h.timeout_ch); if ((cinfo->state & ACCB_RECOVERY_CCB) != 0) { /* * We now traverse our list of pending CCBs and reinstate |