Fix the `tape drive spinning indefinately upon mt stat' problem.

With the recent changes in the CAM error handling, some problems in
the error handling of sa(4) have been uncovered.  Basically, a number
of conditions that are not actually errors have been mistreated as
genuine errors.  In particular:

. Trying to read in variable length mode with a mismatched blocksize
  between the on-tape (virtual) blocks and the read(2) supplied buffer
  size, causing an ILI SCSI condition, have caused an attempt to retry
  the supposedly `errored' transfer, causing the tape to be read
  continuously until it eventually hit EOM.  Since by default any
  simple mt(1) operation does an initial test read, an `mt stat' was
  sufficient to trigger this bug.

  Note that it's Justin's opinion that treating a NO SENSE as an EIO
  is another bug in CAM.  I feel not authorized to fix cam_periph.c
  without another confirmation that i'm on the right track, however.

. Hitting a filemark caused the read(2) syscall to return EIO, instead
  of returning a `short read'.  Note that the current fix only solves
  this problem in variable length mode.  Fixed length mode uses a
  different code path, and since i didn't grok all the intentions behind
  that handling, i did not touch it (IOW: it's still broken, and you get
  an EIO upon hitting a filemark).

The solution is to keep track of those conditions inside saerror(),
and upon completion to not call cam_periph_error() in that case.  We
need to make sure that the device gets unfrozen if needed though (in
case of actual errors, cam_periph_error() does this on our behalf).

Not objected by:       mjacob (who currently doesn't have the time to
			      review the patch)
This commit is contained in:
joerg 2001-04-22 20:13:28 +00:00
parent 08518eb71a
commit 80f71bb682

View File

@ -2286,7 +2286,7 @@ saerror(union ccb *ccb, u_int32_t cflgs, u_int32_t sflgs)
u_int32_t resid = 0;
int32_t info = 0;
int error_code, sense_key, asc, ascq;
int error, defer_action;
int error, defer_action, no_actual_error = FALSE;
periph = xpt_path_periph(ccb->ccb_h.path);
softc = (struct sa_softc *)periph->softc;
@ -2396,6 +2396,8 @@ saerror(union ccb *ccb, u_int32_t cflgs, u_int32_t sflgs)
if (defer_action) {
error = -1;
softc->flags |= SA_FLAG_EOF_PENDING;
} else {
no_actual_error = TRUE;
}
/*
* Unconditionally, if we detected a filemark on a read,
@ -2424,6 +2426,8 @@ saerror(union ccb *ccb, u_int32_t cflgs, u_int32_t sflgs)
softc->flags |= SA_FLAG_EIO_PENDING;
else
error = EIO;
} else {
no_actual_error = TRUE;
}
/*
* Bump the block number if we hadn't seen a filemark.
@ -2438,8 +2442,17 @@ saerror(union ccb *ccb, u_int32_t cflgs, u_int32_t sflgs)
}
}
}
if (error == 0)
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);