diff --git a/sys/cam/scsi/scsi_sa.c b/sys/cam/scsi/scsi_sa.c index ae1ed5757ea8..d22cf0c5ba4f 100644 --- a/sys/cam/scsi/scsi_sa.c +++ b/sys/cam/scsi/scsi_sa.c @@ -928,8 +928,6 @@ saioctl(dev_t dev, u_long cmd, caddr_t arg, int flag, struct proc *p) } } } - if (g->mt_resid) { - } error = 0; break; } @@ -1063,17 +1061,20 @@ saioctl(dev_t dev, u_long cmd, caddr_t arg, int flag, struct proc *p) /* see above */ softc->flags &= ~(SA_FLAG_TAPE_WRITTEN|SA_FLAG_TAPE_FROZEN); + softc->flags &= ~SA_FLAG_ERR_PENDING; softc->filemarks = 0; break; case MTERASE: /* erase */ error = saerase(periph, count); softc->flags &= ~(SA_FLAG_TAPE_WRITTEN|SA_FLAG_TAPE_FROZEN); + softc->flags &= ~SA_FLAG_ERR_PENDING; break; case MTRETENS: /* re-tension tape */ error = saretension(periph); softc->flags &= ~(SA_FLAG_TAPE_WRITTEN|SA_FLAG_TAPE_FROZEN); + softc->flags &= ~SA_FLAG_ERR_PENDING; break; case MTOFFL: /* rewind and put the drive offline */ @@ -1555,23 +1556,33 @@ sastart(struct cam_periph *periph, union ccb *start_ccb) xpt_release_ccb(start_ccb); } else if ((softc->flags & SA_FLAG_ERR_PENDING) != 0) { struct bio *done_bp; +again: softc->queue_count--; bioq_remove(&softc->bio_queue, bp); bp->bio_resid = bp->bio_bcount; - bp->bio_flags |= BIO_ERROR; - if ((softc->flags & SA_FLAG_EOM_PENDING) != 0) { - if (bp->bio_cmd == BIO_WRITE) - bp->bio_error = ENOSPC; - else - bp->bio_error = EIO; - } - if ((softc->flags & SA_FLAG_EOF_PENDING) != 0) { - bp->bio_error = EIO; - } - if ((softc->flags & SA_FLAG_EIO_PENDING) != 0) { - bp->bio_error = EIO; - } done_bp = bp; + if ((softc->flags & SA_FLAG_EOM_PENDING) != 0) { + /* + * We now just clear errors in this case + * and let the residual be the notifier. + */ + bp->bio_error = 0; + } else if ((softc->flags & SA_FLAG_EOF_PENDING) != 0) { + /* + * This can only happen if we're reading + * in fixed length mode. In this case, + * we dump the rest of the list the + * same way. + */ + bp->bio_error = 0; + if (bioq_first(&softc->bio_queue) != NULL) { + biodone(done_bp); + goto again; + } + } else if ((softc->flags & SA_FLAG_EIO_PENDING) != 0) { + bp->bio_error = EIO; + bp->bio_flags |= BIO_ERROR; + } bp = bioq_first(&softc->bio_queue); /* * Only if we have no other buffers queued up @@ -2231,6 +2242,12 @@ samount(struct cam_periph *periph, int oflags, dev_t dev) */ if (error != 0) { (void) sareservereleaseunit(periph, FALSE); + } else { + /* + * Clear I/O residual. + */ + softc->last_io_resid = 0; + softc->last_ctl_resid = 0; } return (error); } @@ -2286,20 +2303,25 @@ saerror(union ccb *ccb, u_int32_t cflgs, u_int32_t sflgs) struct scsi_sense_data *sense; u_int32_t resid = 0; int32_t info = 0; - int error_code, sense_key, asc, ascq; - int error, defer_action, no_actual_error = FALSE; + cam_status status; + int error_code, sense_key, asc, ascq, error, aqvalid; periph = xpt_path_periph(ccb->ccb_h.path); softc = (struct sa_softc *)periph->softc; csio = &ccb->csio; sense = &csio->sense_data; scsi_extract_sense(sense, &error_code, &sense_key, &asc, &ascq); + aqvalid = sense->extra_len >= 6; error = 0; + status = csio->ccb_h.status & CAM_STATUS_MASK; + /* - * Calculate/latch up, any residuals... + * Calculate/latch up, any residuals... We do this in a funny 2-step + * so we can print stuff here if we have CAM_DEBUG enabled for this + * unit. */ - if ((csio->ccb_h.status & CAM_STATUS_MASK) == CAM_SCSI_STATUS_ERROR) { + if (status == CAM_SCSI_STATUS_ERROR) { if ((sense->error_code & SSD_ERRCODE_VALID) != 0) { info = (int32_t) scsi_4btoul(sense->info); resid = info; @@ -2328,33 +2350,56 @@ saerror(union ccb *ccb, u_int32_t cflgs, u_int32_t sflgs) softc->last_ctl_resid = resid; softc->last_resid_was_io = 0; } - CAM_DEBUG(periph->path, CAM_DEBUG_INFO, ("Key 0x%x ASC/ASCQ " - "0x%x 0x%x flags 0x%x resid %d dxfer_len %d\n", sense_key, - asc, ascq, sense->flags & ~SSD_KEY_RESERVED, resid, - csio->dxfer_len)); + CAM_DEBUG(periph->path, CAM_DEBUG_INFO, ("CDB[0]=0x%x Key 0x%x " + "ASC/ASCQ 0x%x/0x%x CAM STATUS 0x%x flags 0x%x resid %d " + "dxfer_len %d\n", csio->cdb_io.cdb_bytes[0] & 0xff, + sense_key, asc, ascq, status, + sense->flags & ~SSD_KEY_RESERVED, resid, csio->dxfer_len)); } else { - CAM_DEBUG(periph->path, CAM_DEBUG_INFO, ("Cam Status 0x%x\n", - csio->ccb_h.status & CAM_STATUS_MASK)); + CAM_DEBUG(periph->path, CAM_DEBUG_INFO, + ("Cam Status 0x%x\n", status)); } - /* - * If it's neither a SCSI Check Condition Error nor a non-read/write - * command, let the common code deal with it the error setting. - */ - if ((csio->ccb_h.status & CAM_STATUS_MASK) != CAM_SCSI_STATUS_ERROR || - (CCB_Type(csio) == SA_CCB_WAITING)) { + switch (status) { + case CAM_REQ_CMP: + return (0); + case CAM_SCSI_STATUS_ERROR: + /* + * If a read/write command, we handle it here. + */ + if (CCB_Type(csio) != SA_CCB_WAITING) { + break; + } + /* + * If this was just EOM/EOP, Filemark, Setmark or ILI detected + * on a non read/write command, we assume it's not an error + * and propagate the residule and return. + */ + if ((aqvalid && asc == 0 && ascq > 0 && ascq <= 5) || + (aqvalid == 0 && sense_key == SSD_KEY_NO_SENSE)) { + csio->resid = resid; + QFRLS(ccb); + return (0); + } + /* + * Otherwise, we let the common code handle this. + */ return (cam_periph_error(ccb, cflgs, sflgs, &softc->saved_ccb)); - } /* - * Calculate whether we'll defer action. + * XXX: To Be Fixed + * We cannot depend upon CAM honoring retry counts for these. */ - - if (resid > 0 && resid < csio->dxfer_len && - (softc->flags & SA_FLAG_FIXED) != 0) { - defer_action = TRUE; - } else { - defer_action = FALSE; + case CAM_SCSI_BUS_RESET: + case CAM_BDR_SENT: + case CAM_REQUEUE_REQ: + if (ccb->ccb_h.retry_count <= 0) { + return (EIO); + break; + } + /* FALLTHROUGH */ + default: + return (cam_periph_error(ccb, cflgs, sflgs, &softc->saved_ccb)); } /* @@ -2362,43 +2407,34 @@ saerror(union ccb *ccb, u_int32_t cflgs, u_int32_t sflgs) * From this point out, we're only handling read/write cases. * Handle writes && reads differently. */ - + if (csio->cdb_io.cdb_bytes[0] == SA_WRITE) { - if (sense->flags & SSD_FILEMARK) { - xpt_print_path(csio->ccb_h.path); - printf("filemark detected on write?\n"); - if (softc->fileno != (daddr_t) -1) { - softc->fileno++; - softc->blkno = 0; - csio->ccb_h.ccb_pflags |= SA_POSITION_UPDATED; - } - } - if (sense->flags & SSD_EOM) { + if (sense_key == SSD_KEY_VOLUME_OVERFLOW) { csio->resid = resid; - if (defer_action) { - error = -1; - softc->flags |= SA_FLAG_EOM_PENDING; - } else { - error = ENOSPC; - } + error = ENOSPC; + } else if (sense->flags & SSD_EOM) { + softc->flags |= SA_FLAG_EOM_PENDING; + /* + * Grotesque as it seems, the few times + * I've actually seen a non-zero resid, + * the tape drive actually lied and had + * writtent all the data!. + */ + csio->resid = 0; } } else { + csio->resid = resid; if (sense_key == SSD_KEY_BLANK_CHECK) { - csio->resid = resid; - if (defer_action) { - error = -1; + if (softc->quirks & SA_QUIRK_1FM) { + error = 0; softc->flags |= SA_FLAG_EOM_PENDING; } else { error = EIO; } - } - if (sense->flags & SSD_FILEMARK) { - csio->resid = resid; - if (defer_action) { + } else if (sense->flags & SSD_FILEMARK) { + if (softc->flags & SA_FLAG_FIXED) { error = -1; softc->flags |= SA_FLAG_EOF_PENDING; - } else { - no_actual_error = TRUE; } /* * Unconditionally, if we detected a filemark on a read, @@ -2411,6 +2447,7 @@ saerror(union ccb *ccb, u_int32_t cflgs, u_int32_t sflgs) } } } + /* * Incorrect Length usually applies to read, but can apply to writes. */ @@ -2422,13 +2459,8 @@ saerror(union ccb *ccb, u_int32_t cflgs, u_int32_t sflgs) error = EIO; } else { csio->resid = resid; - if ((softc->flags & SA_FLAG_FIXED) != 0) { - if (defer_action) - softc->flags |= SA_FLAG_EIO_PENDING; - else - error = EIO; - } else { - no_actual_error = TRUE; + if (softc->flags & SA_FLAG_FIXED) { + softc->flags |= SA_FLAG_EIO_PENDING; } /* * Bump the block number if we hadn't seen a filemark. @@ -2443,22 +2475,17 @@ saerror(union ccb *ccb, u_int32_t cflgs, u_int32_t sflgs) } } } - if (error == 0 && !no_actual_error) - return (cam_periph_error(ccb, cflgs, sflgs, &softc->saved_ccb)); - if (no_actual_error) { - if ((ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) - cam_release_devq(ccb->ccb_h.path, - /* relsim_flags */0, - /* openings */0, - /* timeout */0, - /* getcount_only */ FALSE); - return (0); - } - if (error == -1) - return (0); - else - return (error); + if (error <= 0) { + /* + * Unfreeze the queue if frozen as we're not returning anything + * to our waiters that would indicate an I/O error has occurred + * (yet). + */ + QFRLS(ccb); + error = 0; + } + return (error); } static int