More specifically, make handling of resid consistant and at least
superficially correct. Triggered by the hack I had to commit to fix
a problem with a large un-tar from my ahci DAT drive.

A cookie for those who can show any semantic change I made outside
of the calculation and usage of resid's.

Any big tape users out there want to give it a whirl and ensure I
didn't break anything while trying to make the code easier to read?

OK's also accepted. :-).

.... Ken

Index: st.c
===================================================================
RCS file: /cvs/src/sys/scsi/st.c,v
retrieving revision 1.109
diff -u -p -r1.109 st.c
--- st.c        1 Sep 2010 23:24:03 -0000       1.109
+++ st.c        7 Sep 2010 01:23:34 -0000
@@ -1952,7 +1952,8 @@ st_interpret_sense(struct scsi_xfer *xs)
        struct st_softc *st = sc_link->device_softc;
        u_int8_t serr = sense->error_code & SSD_ERRCODE;
        u_int8_t skey = sense->flags & SSD_KEY;
-       int32_t info;
+       int32_t resid;
+       int datalen;
 
        if (((sc_link->flags & SDEV_OPEN) == 0) ||
            (serr != SSD_ERRCODE_CURRENT && serr != SSD_ERRCODE_DEFERRED))
@@ -1996,88 +1997,76 @@ st_interpret_sense(struct scsi_xfer *xs)
        }
 
        /*
-        * Get the sense fields and work out what code
+        * 'resid' can be in units of st->blksize or bytes. xs->resid and
+        * xs->datalen are always in units of bytes. So we need a variable
+        * to store datalen in the same units as resid and to adjust
+        * xs->resid to be in bytes.
         */
-       if (sense->error_code & SSD_ERRCODE_VALID)
-               info = _4btol(sense->info);
-       else
-               info = xs->datalen;     /* bad choice if fixed blocks */
-       if (st->flags & ST_FIXEDBLOCKS) {
-               xs->resid = info * st->blksize;
-               if (sense->flags & SSD_EOM) {
-                       st->flags |= ST_EIO_PENDING;
+       datalen = xs->datalen;
+       if (sense->error_code & SSD_ERRCODE_VALID) {
+               xs->resid = resid = (int32_t)_4btol(sense->info);
+               if (st->flags & ST_FIXEDBLOCKS) {
+                       xs->resid *= st->blksize;
+                       datalen /= st->blksize;
                }
-               if (sense->flags & SSD_FILEMARK) {
-                       st->flags |= ST_AT_FILEMARK;
-                       if (st->media_fileno != -1) {
-                               st->media_fileno++;
-                               st->media_blkno = 0;
-                       }
+               if (xs->resid < 0 || xs->resid > xs->datalen)
+                       xs->resid = xs->datalen;
+       } else {
+               xs->resid = resid = xs->datalen;
+               if (st->flags & ST_FIXEDBLOCKS) {
+                       resid /= st->blksize;
+                       datalen /= st->blksize;
                }
-               if (sense->flags & SSD_ILI) {
-                       st->flags |= ST_EIO_PENDING;
-                       if (sense->error_code & SSD_ERRCODE_VALID &&
-                           (xs->flags & SCSI_SILENT) == 0)
-                               printf("%s: block wrong size, %d blocks 
residual\n",
-                                   st->sc_dev.dv_xname, 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_SENSE_HELP) &&
-                           !(sc_link->flags & SDEV_MEDIA_LOADED))
-                               st->blksize -= 512;
+       }
+
+       if (sense->flags & SSD_FILEMARK) {
+               if (st->media_fileno != -1) {
+                       st->media_fileno++;
+                       st->media_blkno = 0;
                }
+               if ((st->flags & ST_FIXEDBLOCKS) == 0)
+                       return 0;
+               st->flags |= ST_AT_FILEMARK;
+       }
+
+       if (sense->flags & SSD_EOM) {
+               if ((st->flags & ST_FIXEDBLOCKS) == 0)
+                       return EIO;
+               st->flags |= ST_EIO_PENDING;
+       }
+
+       if (sense->flags & SSD_ILI) {
+               if ((st->flags & ST_FIXEDBLOCKS) == 0) {
+                       if (resid >= 0 && resid <= datalen)
+                               return (0);
+                       if ((xs->flags & SCSI_SILENT) == 0)
+                               printf( "%s: bad residual %d out of "
+                                   "%d\n", st->sc_dev.dv_xname, resid,
+                                   datalen);
+                       return (EIO);
+               }
+
+               /* Fixed size blocks. */
+               if (sense->error_code & SSD_ERRCODE_VALID)
+                       if ((xs->flags & SCSI_SILENT) == 0)
+                               printf("%s: block wrong size, %d blocks "
+                                   "residual\n", st->sc_dev.dv_xname, resid);
+               st->flags |= ST_EIO_PENDING;
                /*
-                * If no data was transferred, return immediately
+                 * 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 (xs->resid >= xs->datalen) {
-                       xs->resid = xs->datalen;
-                       if (st->flags & ST_EIO_PENDING)
-                               return EIO;
-                       if (st->flags & ST_AT_FILEMARK) {
-                               return 0;
-                       }
-               }
-       } else {                /* must be variable mode */
-               xs->resid = xs->datalen;        /* to be sure */
-               if (sense->flags & SSD_EOM)
+               if ((st->quirks & ST_Q_SENSE_HELP) &&
+                   !(sc_link->flags & SDEV_MEDIA_LOADED))
+                       st->blksize -= 512;
+       }
+
+       if ((st->flags & ST_FIXEDBLOCKS) && xs->resid == xs->datalen) {
+               if (st->flags & ST_EIO_PENDING)
                        return EIO;
-               if (sense->flags & SSD_FILEMARK) {
-                       if (st->media_fileno != -1) {
-                               st->media_fileno++;
-                               st->media_blkno = 0;
-                       }
+               if (st->flags & ST_AT_FILEMARK)
                        return 0;
-               }
-               if (sense->flags & SSD_ILI) {
-                       if (info < 0) {
-                               /*
-                                * the record was bigger than the read
-                                */
-                               if ((xs->flags & SCSI_SILENT) == 0)
-                                       printf("%s: %d-byte record too big\n",
-                                           st->sc_dev.dv_xname,
-                                           xs->datalen - info);
-                               return (EIO);
-                       } else if (info > xs->datalen) {
-                               /*
-                                * huh? the residual is bigger than the request
-                                */
-                               if ((xs->flags & SCSI_SILENT) == 0)
-                                       printf(
-                                           "%s: bad residual %d out of %d\n",
-                                           st->sc_dev.dv_xname, info,
-                                           xs->datalen);
-                               return (EIO);
-                       }
-                       xs->resid = info;
-                       return (0);
-               }
        }
 
        if (skey == SKEY_BLANK_CHECK) {

Reply via email to