Skip site navigation (1)Skip section navigation (2)
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>