Clear SA_FLAG_ERR_PENDING for MTREW, MTERASE and MTRETENS ioctl cases.
Clear residual counts after a successful samount (the user doesn't care that we got an N-kbyte residual on our test read). Change a lot of error handling code. 1. If we end up in saerror, check more carefully about the kind of error. If it is a CAM_SCSI_STATUS_ERROR and it is a read/write command, we'll be handling this in saerror. If it isn't a read/write command, check to see whether this is just an EOM/EOP check condition- if it is, just set residual and return normally. A residual and then a NO SENSE check condiftion with the ASC of 0 and ASCQ of between 1 and 4 are normal 'signifying' events, not errors per se, and we shouldn't give the command to cam_periph_error to do something relatively unpredictable with. 2. If we get a Bus Reset, had a BDR sent, or get the cam status of CAM_REQUEUE_REQ, check the retry count on the command. The default error handler, cam_periph_error, doesn't honor retry count in these cases. This may change in the future, but for now, make sure we set EIO and return without calling cam_periph_error if the retry count for the command with an error is zero. 3. Clean up the pending error case goop and handle cases more sensibly. The rules are: If command was a Write: If we got a SSD_KEY_VOLUME_OVERFLOW, the resid is propagated and we set ENOSPC as the error. Else if we got an EOM condition- just mark EOM pending. And set a residual of zero. For the longest time I was just propagating residual from the sense data- but my tape comparison tests were always failing because all drives I tested with actually *do* write the data anyway- the EOM (early warning) condition occurred *prior* to all of the data going out to media- that is, it was still buffered by the drive. This case is described in SCSI-2, 10.2.14, paragraph #d for the meaning of 'information field'. A better fix for this would be to issue a WFM command of zero to cause the drive to flush any buffered data, but this would require a fairly extensive rewrite. Else if the command was a READ: If we got a SSD_KEY_BLANK_CHECK- If we have a One Filemark EOT model- mark EOM as pending, otherwise set EIO as the erorr. Else if we found a Filemark- If we're in Fixed Block mode- mark EOF pending. If we had an ILI (Incorrect Length Indicator)- If the residual is less than zero, whine about tape record being too big for user's buffer, otherwise if we were in Fixed Block mode, mark EIO as pending. All 'pending' conditions mean that the command in question completes without error indication. It had succeeded, but a signifying event occurred during its execution which will apply to the *next* command that would be exexcuted. Except for the one EOM case above, we always propagate residual. Now, way back in sastart- if we notice any of the PENDING bits set, we don't run the command we've just pulled off the wait queue. Instead, we then figure out it's disposition based upon a previous command's association with a signifying event. If SA_FLAG_EOM_PENDING is set, we don't set an error. We just complete the command with residual set to the request count (not data moved, but no error). We continue on. If SA_FLAG_EOF_PENDING- if we have this, it's only because we're in Fixed Block mode- in which case we traverse all waiting buffers (which we can get in fixed block mode because physio has split things up) and mark them all as no error, but no data moved and complete them. If SA_FLAG_EIO_PENDING, just mark the buffer with an EIO error and complete it. Then we clear all of the pending state bits- we're done. MFC after: 4 weeks
This commit is contained in:
parent
7e59bf6765
commit
83e9f270b3
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user