Date: Sat, 25 Mar 95 01:03 PST From: julian@tfs.com (Julian Elischer) To: hackers@FreeBSD.org Cc: julian@tfs.com Subject: help needed with fix to scsi tape. Message-ID: <m0rsRkP-0003vvC@TFS.COM>
next in thread | raw e-mail | index | archive | help
I have here a fix for the scsi tape driver..
The fix was actually simpler, but I needed toclean up some things
while I was in there..
trouble is:
I don't HAVE a scsi tape drive at the moment...
In particular I need to check that the following works..
write to a variable length block drive in 10K records
read from it with 30K records.. (and get 10K in each read)
check what happens when writing (and reading) of the end of a tape
(both fixed and variable type devices..)
on a fixed block type tape.. write 5 records.
then do a read that will be blocked to a 7 record read
and check that it gets 5 records..
(e.g. if the tape has 1K blocksize:
dd if=/etc/termcap of=/dev/rst0 bs=5k count=1
followed by:
dd if=/dev/rst0 of=/tmp/xxx bs=7k
and check that we get /tmp/xxx being 5k, and no error, just EOF..
enough:
here is the patch:
I need feedback on this because it's biting people apparently..
FreeBSD's tape driver as severely broken with regard to
reading a different size than was written....
julian
*** 1.4 1995/03/25 01:22:38
--- st.c 1995/03/25 06:10:01
***************
*** 1862,1868 ****
#ifdef NETBSD
#define SIGNAL_SHORT_READ
#else
! #define SIGNAL_SHORT_READ bp->b_flags |= B_ERROR;
#endif
/*
--- 1862,1868 ----
#ifdef NETBSD
#define SIGNAL_SHORT_READ
#else
! #define SIGNAL_SHORT_READ if(bp) bp->b_flags |= B_ERROR;
#endif
/*
***************
*** 1889,1981 ****
if (sense->error_code & SSD_ERRCODE_VALID) {
info = ntohl(*((int32 *) sense->ext.extended.info));
} else {
! info = xs->datalen; /* bad choice if fixed blocks */
}
if ((sense->error_code & SSD_ERRCODE) != 0x70) {
! return SCSIRET_CONTINUE; /* let the generic code handle it */
}
! if (st->flags & ST_FIXEDBLOCKS) {
! xs->resid = info * st->blksiz;
! if (sense->ext.extended.flags & SSD_EOM) {
! st->flags |= ST_EIO_PENDING;
! if (bp) {
! bp->b_resid = xs->resid;
! SIGNAL_SHORT_READ
! }
! }
! if (sense->ext.extended.flags & SSD_FILEMARK) {
! st->flags |= ST_AT_FILEMARK;
! if (bp) {
! bp->b_resid = xs->resid;
! SIGNAL_SHORT_READ
}
- }
- if (sense->ext.extended.flags & SSD_ILI) {
- st->flags |= ST_EIO_PENDING;
- if (bp) {
- bp->b_resid = xs->resid;
- SIGNAL_SHORT_READ
- }
- if (sense->error_code & SSD_ERRCODE_VALID &&
- !silent)
- printf("st%d: block wrong size"
- ", %d blocks residual\n", unit
- ,info);
-
/*
! * This quirk code helps the drive read
! * the first tape block, regardless of
! * format. That is required for these
! * drives to return proper MODE SENSE
! * information.
*/
! if ((st->quirks & ST_Q_SNS_HLP) &&
! !(sc_link->flags & SDEV_MEDIA_LOADED)) {
! st->blksiz -= 512;
! }
! }
! /*
! * If no data was tranfered, do it immediatly
! */
! if (xs->resid >= xs->datalen) {
! if (st->flags & ST_EIO_PENDING) {
! return EIO;
! }
! if (st->flags & ST_AT_FILEMARK) {
! if (bp) {
! bp->b_resid = xs->resid;
! SIGNAL_SHORT_READ
}
- return 0;
}
- }
- } else { /* must be variable mode */
- xs->resid = xs->datalen; /* to be sure */
- if (sense->ext.extended.flags & SSD_EOM) {
- return (EIO);
- }
- if (sense->ext.extended.flags & SSD_FILEMARK) {
- if (bp)
- bp->b_resid = bp->b_bcount;
return 0;
! }
! if (sense->ext.extended.flags & SSD_ILI) {
! if (info < 0) {
! /*
! * the record was bigger than the read
! */
! if (!silent)
! printf("st%d: %d-byte record "
! "too big\n", unit,
! xs->datalen - info);
return (EIO);
}
! xs->resid = info;
! if (bp) {
! bp->b_resid = info;
! SIGNAL_SHORT_READ
}
}
}
key = sense->ext.extended.flags & SSD_KEY;
--- 1889,1975 ----
if (sense->error_code & SSD_ERRCODE_VALID) {
info = ntohl(*((int32 *) sense->ext.extended.info));
} else {
! if (st->flags & ST_FIXEDBLOCKS) {
! info = xs->datalen / st->blksiz;
! } else {
! info = xs->datalen;
! }
}
if ((sense->error_code & SSD_ERRCODE) != 0x70) {
! return SCSIRET_CONTINUE;/* let the generic code handle it */
}
! if(st->flags & (SSD_EOM|SSD_FILEMARK|SSD_ILI)) {
! if (st->flags & ST_FIXEDBLOCKS) {
! xs->resid = info * st->blksiz;
! xs->flags |= SCSI_RESID_VALID;
! if (sense->ext.extended.flags & SSD_EOM) {
! st->flags |= ST_EIO_PENDING;
! }
! if (sense->ext.extended.flags & SSD_FILEMARK) {
! st->flags |= ST_AT_FILEMARK;
! }
! if (sense->ext.extended.flags & SSD_ILI) {
! st->flags |= ST_EIO_PENDING;
! if (sense->error_code & SSD_ERRCODE_VALID &&
! !silent)
! printf("st%d: block wrong size"
! ", %d blocks residual\n", unit
! ,info);
! /*XXX*/ /* is this how it works ? */
! /* check def of ILI for fixed blk tapes */
!
! /*
! * This quirk code helps the drive read
! * the first tape block, regardless of
! * format. That is required for these
! * drives to return proper MODE SENSE
! * information.
! */
! if ((st->quirks & ST_Q_SNS_HLP) &&
! !(sc_link->flags & SDEV_MEDIA_LOADED)) {
! st->blksiz -= 512;
! }
}
/*
! * If no data was tranfered, do it immediatly
*/
! if (xs->resid >= xs->datalen) {
! xs->flags &= ~SCSI_RESID_VALID;
! if (st->flags & ST_AT_FILEMARK) {
! xs->flags |= SCSI_EOF;
! st->flags &= ~ST_AT_FILEMARK;
! return 0;
! }
! if (st->flags & ST_EIO_PENDING) {
! st->flags &= ~ST_EIO_PENDING;
! return EIO;
}
}
return 0;
! } else { /* must be variable mode */
! xs->resid = xs->datalen; /* to be sure */
! if (sense->ext.extended.flags & SSD_EOM) {
return (EIO);
}
! if (sense->ext.extended.flags & SSD_FILEMARK) {
! xs->flags |= SCSI_EOF;
! }
! if (sense->ext.extended.flags & SSD_ILI) {
! if (info < 0) {
! /*
! * the record was bigger than the read
! */
! if (!silent)
! printf("st%d: %d-byte record "
! "too big\n", unit,
! xs->datalen - info);
! return (EIO);
! }
! xs->resid = info;
! xs->flags |= SCSI_RESID_VALID;
}
}
+ return 0;
}
key = sense->ext.extended.flags & SSD_KEY;
***************
*** 1987,2005 ****
* MODE SENSE information.
*/
if ((st->quirks & ST_Q_SNS_HLP) &&
! !(sc_link->flags & SDEV_MEDIA_LOADED)) { /* still starting */
st->blksiz -= 512;
} else if (!(st->flags & (ST_2FM_AT_EOD | ST_BLANK_READ))) {
st->flags |= ST_BLANK_READ;
! xs->resid = xs->datalen;
! if (bp) {
! bp->b_resid = xs->resid;
! /*return an EOF */
! }
return (ESUCCESS);
}
}
! return SCSIRET_CONTINUE; /* let the default/generic handler handle it */
}
/*
--- 1981,1996 ----
* MODE SENSE information.
*/
if ((st->quirks & ST_Q_SNS_HLP) &&
! !(sc_link->flags & SDEV_MEDIA_LOADED)) {
! /* still starting */
st->blksiz -= 512;
} else if (!(st->flags & (ST_2FM_AT_EOD | ST_BLANK_READ))) {
st->flags |= ST_BLANK_READ;
! xs->flags |= SCSI_EOF;
return (ESUCCESS);
}
}
! return SCSIRET_CONTINUE; /* Use the the generic handler */
}
/*
*** 1.4 1995/03/25 01:22:38
--- scsi_base.c 1995/03/25 08:46:33
***************
*** 17,22 ****
--- 17,23 ----
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
+ #include <sys/kernel.h>
#include <sys/buf.h>
#include <sys/uio.h>
#include <sys/malloc.h>
***************
*** 61,66 ****
--- 62,68 ----
sc_link->flags |= SDEV_WAITING;
tsleep((caddr_t)sc_link, PRIBIO, "scsiget", 0);
}
+ sc_link->active++;
sc_link->opennings--;
if (xs = next_free_xs) {
next_free_xs = xs->next;
***************
*** 97,102 ****
--- 99,105 ----
SC_DEBUG(sc_link, SDEV_DB3, ("free_xs\n"));
/* if was 0 and someone waits, wake them up */
+ sc_link->active--;
if ((!sc_link->opennings++) && (sc_link->flags & SDEV_WAITING)) {
sc_link->flags &= ~SDEV_WAITING;
wakeup((caddr_t)sc_link); /* remember, it wakes them ALL up */
***************
*** 356,361 ****
--- 359,367 ----
}
/* BUG: This isn't used anywhere. Do you have plans for it,
* Julian? (dufault@hda.com).
+ * This allows a private 'done' handler to
+ * resubmit the command if it wants to retry,
+ * In this case the xs must NOT be freed. (julian)
*/
if (retval == -2) {
return; /* it did it all, finish up */
***************
*** 427,433 ****
if (bp && !(flags & SCSI_USER)) flags |= SCSI_NOSLEEP;
SC_DEBUG(sc_link, SDEV_DB2, ("scsi_cmd\n"));
! xs = get_xs(sc_link, flags); /* should wait unless booting */
if (!xs) return (ENOMEM);
/*
* Fill out the scsi_xfer structure. We don't know whose context
--- 433,439 ----
if (bp && !(flags & SCSI_USER)) flags |= SCSI_NOSLEEP;
SC_DEBUG(sc_link, SDEV_DB2, ("scsi_cmd\n"));
! xs = get_xs(sc_link, flags);
if (!xs) return (ENOMEM);
/*
* Fill out the scsi_xfer structure. We don't know whose context
***************
*** 442,448 ****
xs->cmdlen = cmdlen;
xs->data = data_addr;
xs->datalen = datalen;
! xs->resid = datalen;
xs->bp = bp;
/*XXX*/ /*use constant not magic number */
if (datalen && ((caddr_t) data_addr < (caddr_t) KERNBASE)) {
--- 448,454 ----
xs->cmdlen = cmdlen;
xs->data = data_addr;
xs->datalen = datalen;
! xs->resid = datalen; /* just to be safe, but don't mark as VALID */
xs->bp = bp;
/*XXX*/ /*use constant not magic number */
if (datalen && ((caddr_t) data_addr < (caddr_t) KERNBASE)) {
***************
*** 503,510 ****
switch (retval) {
case SUCCESSFULLY_QUEUED:
! if (bp)
! return retval; /* will sleep (or not) elsewhere */
s = splbio();
while (!(xs->flags & ITSDONE)) {
tsleep((caddr_t)xs, PRIBIO + 1, "scsicmd", 0);
--- 509,517 ----
switch (retval) {
case SUCCESSFULLY_QUEUED:
! if (bp) {
! return 0; /* will sleep (or not) elsewhere */
! }
s = splbio();
while (!(xs->flags & ITSDONE)) {
tsleep((caddr_t)xs, PRIBIO + 1, "scsicmd", 0);
***************
*** 561,566 ****
--- 568,616 ----
return (retval);
}
+ /*
+ * submit a scsi command, given the command.. used for retries
+ * and callable from timeout()
+ */
+ #ifdef NOTYET
+ errval scsi_submit(xs)
+ struct scsi_xfer *xs;
+ {
+ struct scsi_link *sc_link = xs->sc_link;
+ int retval;
+
+ retval = (*(sc_link->adapter->scsi_cmd)) (xs);
+
+ return retval;
+ }
+
+ /*
+ * Retry a scsi command, given the command, and a delay.
+ */
+ errval scsi_retry(xs,delay)
+ struct scsi_xfer *xs;
+ int delay;
+ {
+ if(delay)
+ {
+ timeout(((void())*)scsi_submit,xs,hz*delay);
+ return(0);
+ }
+ else
+ {
+ return(scsi_submit(xs));
+ }
+ }
+ #endif
+
+ /*
+ * handle checking for errors..
+ * called at interrupt time from scsi_done() and
+ * at user time from scsi_scsi_cmd(), depending on whether
+ * there was a bp (basically, if there is a bp, there may be no
+ * associated process at the time. (it could be an async operation))
+ * lower level routines shouldn't know about xs->bp.. we are the lowest.
+ */
static errval
sc_err1(xs)
struct scsi_xfer *xs;
***************
*** 581,591 ****
retval = ESUCCESS;
if (bp) {
bp->b_error = 0;
! bp->b_resid = 0;
}
break;
case XS_SENSE:
retval = scsi_interpret_sense(xs);
if (retval == SCSIRET_DO_RETRY) {
if (xs->retries--) {
--- 631,659 ----
retval = ESUCCESS;
if (bp) {
bp->b_error = 0;
! bp->b_resid = 0; /* assume all data transferred */
! }
! /*
! * if it appears untouched,
! * assume done
! * (don't we trust the adapter drivers to do this?)
! */
! if(!(xs->flags & SCSI_RESID_VALID)
! && (xs->resid == xs->datalen)) {
! xs->resid = 0;
}
break;
case XS_SENSE:
+ /*
+ * The return here is tricky.. a device might
+ * return a sense which is not an error. A tape will return
+ * for a short read or EOF (for example) with no error but
+ * with sense info. The private error handler may have
+ * set the residual values up correctly, and should have
+ * set the B_ERROR flag, telling physio not to retry for
+ * more data. We mustn't clobber this setup.
+ */
retval = scsi_interpret_sense(xs);
if (retval == SCSIRET_DO_RETRY) {
if (xs->retries--) {
***************
*** 593,609 ****
xs->flags &= ~ITSDONE;
goto retry;
}
}
- retval = EIO; /* Too many retries */
if (bp) {
! bp->b_error = 0;
! bp->b_resid = 0;
if (retval) {
bp->b_flags |= B_ERROR;
- bp->b_error = retval;
bp->b_resid = bp->b_bcount;
}
SC_DEBUG(xs->sc_link, SDEV_DB3,
("scsi_interpret_sense (bp) returned %d\n", retval));
} else {
--- 661,705 ----
xs->flags &= ~ITSDONE;
goto retry;
}
+ retval = EIO; /* Too many retries */
+ }
+ /*
+ * an EOF condition results in a VALID resid..
+ */
+ if(xs->flags & SCSI_EOF) {
+ xs->resid = xs->datalen;
+ xs->flags |= SCSI_RESID_VALID;
}
if (bp) {
! bp->b_error = retval;
if (retval) {
bp->b_flags |= B_ERROR;
bp->b_resid = bp->b_bcount;
+ } else {
+ /*
+ * apparently it was not an error.
+ * The trick is that in this case, the error
+ * interpretting code could have set xs->resid
+ * to be something useful.
+ * if not, maybe the adapter driver did..
+ *
+ * It may have been an EOF (resid == datalen)
+ * or a short read, ( 0 < resid < datalen )
+ * or a corrected soft_error (resid == 0)
+ */
+ if(xs->flags & SCSI_EOF) {
+ bp->b_resid = bp->b_bcount;
+ } else {
+ if( xs->flags & SCSI_RESID_VALID) {
+ bp->b_resid = xs->resid;
+ } else {
+ bp->b_resid = 0;
+ }
+ }
+
}
+
SC_DEBUG(xs->sc_link, SDEV_DB3,
("scsi_interpret_sense (bp) returned %d\n", retval));
} else {
***************
*** 612,621 ****
}
break;
! case XS_BUSY:
! /*should somehow arange for a 1 sec delay here (how?) */
! /* XXX tsleep(&localvar, priority, "foo", hz);
! that's how! */
case XS_TIMEOUT:
/*
* If we can, resubmit it to the adapter.
--- 708,720 ----
}
break;
! case XS_BUSY: /*XXX*/
! /* should somehow arange for a 1 sec delay here (how?)[jre]
! * tsleep(&localvar, priority, "foo", hz);
! * that's how! [unknown]
! * no, we could be at interrupt context.. use
! * timeout(scsi_resubmit,xs,hz); [jre] (not implimenteed yet)
! */
case XS_TIMEOUT:
/*
* If we can, resubmit it to the adapter.
***************
*** 827,842 ****
errcode = (*sc_link->device->err_handler) (xs);
if (errcode >= 0)
return errcode; /* valid errno value */
switch(errcode) {
! case SCSIRET_DO_RETRY: /* Requested a retry */
return errcode;
! case SCSIRET_CONTINUE: /* Continue with default sense processing */
break;
! default:
sc_print_addr(xs->sc_link);
printf("unknown return code %d from sense handler.\n",
errcode);
--- 926,944 ----
errcode = (*sc_link->device->err_handler) (xs);
if (errcode >= 0)
+ if(xs->flags & SCSI_EOF) {
+ xs->resid = xs->datalen;
+ }
return errcode; /* valid errno value */
switch(errcode) {
! case SCSIRET_DO_RETRY: /* Requested a retry */
return errcode;
! case SCSIRET_CONTINUE: /* Continue with default sense processing */
break;
! default:
sc_print_addr(xs->sc_link);
printf("unknown return code %d from sense handler.\n",
errcode);
***************
*** 890,895 ****
--- 992,1001 ----
switch ((int)key) {
case 0x0: /* NO SENSE */
case 0x1: /* RECOVERED ERROR */
+ if(xs->flags & SCSI_EOF) {
+ xs->resid = xs->datalen;
+ return (ESUCCESS);
+ }
if (xs->resid == xs->datalen)
xs->resid = 0; /* not short read */
case 0xc: /* EQUAL */
*** 1.4 1995/03/25 01:22:38
--- scsiconf.h 1995/03/25 05:39:29
***************
*** 406,412 ****
--- 406,414 ----
#define SCSI_NOMASK 0x02 /* dont allow interrupts.. booting */
#define SCSI_NOSTART 0x04 /* left over from ancient history */
#define SCSI_USER 0x08 /* Is a user cmd, call scsi_user_done */
+ #define SCSI_ITSDONE 0x10 /* the transfer is as done as it gets */
#define ITSDONE 0x10 /* the transfer is as done as it gets */
+ #define SCSI_INUSE 0x20 /* The scsi_xfer block is in use */
#define INUSE 0x20 /* The scsi_xfer block is in use */
#define SCSI_SILENT 0x40 /* Don't report errors to console */
#define SCSI_ERR_OK 0x80 /* An error on this operation is OK. */
***************
*** 416,421 ****
--- 418,425 ----
#define SCSI_DATA_OUT 0x800 /* expect data to flow OUT of memory */
#define SCSI_TARGET 0x1000 /* This defines a TARGET mode op. */
#define SCSI_ESCAPE 0x2000 /* Escape operation */
+ #define SCSI_EOF 0x4000 /* The operation should return EOF */
+ #define SCSI_RESID_VALID 0x8000 /* The resid field contains valid data */
/*
* Escape op codes. This provides an extensible setup for operations
+----------------------------------+ ______ _ __
| __--_|\ Julian Elischer | \ U \/ / On assignment
| / \ julian@tfs.com +------>x USA \ in a very strange
| ( OZ ) 300 lakeside Dr. oakland CA. \___ ___ | country !
+- X_.---._/ USA+(510) 645-3137(wk) \_/ \\
v
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?m0rsRkP-0003vvC>
