Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/thorpej_scsipi]: src/sys/dev/scsipi Handle CHECK CONDITION status in mid...



details:   https://anonhg.NetBSD.org/src/rev/068314a3f8c3
branches:  thorpej_scsipi
changeset: 477360:068314a3f8c3
user:      bouyer <bouyer%NetBSD.org@localhost>
date:      Mon Jan 15 09:22:12 2001 +0000

description:
Handle CHECK CONDITION status in mid-layer:
the REQUEST_SENSE command is generated from scsipi_complete() so it can
tsleep() (we use a regular scsipi_command() call for this). Add a new
periph_flag, PERIPH_SENSE, and a new xs_control, XS_CTL_REQSENSE.
When PERIPH_SENSE is set only xfer with XS_CTL_REQSENSE may be sent
(same logic as PERIPH_RECOVERING but with higther priority). xfer with
XS_CTL_REQSENSE need to have XS_CTL_URGENT urgent too.
XS_CTL_USERCMD xfers are now handled in scsipi_complete().

We need to pay special attention to SCSI resets, as we may have:
- an aborted REQUEST_SENSE. In this case we need to requeue the original
  command, not the request sense.
- sense pending for a command no longer in the queue but for which a
  request sense has not yet been queued. In this case we should not issue
  the request sense but requeue the original command instead.

For this:
- the xfer with the CHECK CONDITION status is stored in the scsipi_periph
  (periph_xscheck); the CHECK CONDITION is tested in scsipi_done, PERIPH_SENSE
  and periph_xscheck is set here.
- XS_CTL_REQSENSE xfers are not allowed to be requeued, and are terminated
  with EINTR in case of reset.
- we have a new async event, "ASYNC_EVENT_RESET", which cleanup the
  xfer in periph_xscheck.
- appropriate splbio/splx to avoid race condition (especially,
  scsipi_request_sense() runs at splbio. Should not be a real problem as it
  doesn't happen often.

While I'm there kill req_sense_length from struct scsipi_xfer, it's always set
to 0 and only checked by a few drivers.

diffstat:

 sys/dev/scsipi/scsipi_base.c |  207 ++++++++++++++++++++++++++++++++++++------
 sys/dev/scsipi/scsipi_base.h |    3 +-
 sys/dev/scsipi/scsipiconf.h  |   19 ++-
 3 files changed, 189 insertions(+), 40 deletions(-)

diffs (truncated from 403 to 300 lines):

diff -r 6f9a73536841 -r 068314a3f8c3 sys/dev/scsipi/scsipi_base.c
--- a/sys/dev/scsipi/scsipi_base.c      Sat Jan 13 17:02:38 2001 +0000
+++ b/sys/dev/scsipi/scsipi_base.c      Mon Jan 15 09:22:12 2001 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: scsipi_base.c,v 1.26.2.8 2000/11/20 09:59:26 bouyer Exp $      */
+/*     $NetBSD: scsipi_base.c,v 1.26.2.9 2001/01/15 09:22:12 bouyer Exp $      */
 
 /*-
  * Copyright (c) 1998, 1999, 2000 The NetBSD Foundation, Inc.
@@ -61,6 +61,7 @@
 #include <dev/scsipi/scsi_message.h>
 
 int    scsipi_complete __P((struct scsipi_xfer *));
+void   scsipi_request_sense __P((struct scsipi_xfer *));
 int    scsipi_enqueue __P((struct scsipi_xfer *));
 void   scsipi_run_queue __P((struct scsipi_channel *chan));
 
@@ -77,6 +78,7 @@
            struct scsipi_max_openings *));
 void   scsipi_async_event_xfer_mode __P((struct scsipi_channel *,
            struct scsipi_xfer_mode *));
+void   scsipi_async_event_channel_reset __P((struct scsipi_channel *));
 
 struct pool scsipi_xfer_pool;
 
@@ -388,6 +390,8 @@
         *        Exception: URGENT xfers can proceed when
         *        active == openings, because we use the opening
         *        of the command we're recovering for.
+        *      - if the periph has sense pending, only URGENT & REQSENSE
+        *        xfers may proceed.
         *
         *      - If the periph is recovering, only URGENT xfers may
         *        proceed.
@@ -398,11 +402,17 @@
         */
        for (;;) {
                if (flags & XS_CTL_URGENT) {
-                       if (periph->periph_active > periph->periph_openings ||
-                           (periph->periph_flags &
-                            PERIPH_RECOVERY_ACTIVE) != 0)
+                       if (periph->periph_active > periph->periph_openings)
                                goto wait_for_opening;
-                       periph->periph_flags |= PERIPH_RECOVERY_ACTIVE;
+                       if (periph->periph_flags & PERIPH_SENSE) {
+                               if ((flags & XS_CTL_REQSENSE) == 0)
+                                       goto wait_for_opening;
+                       } else {
+                               if ((periph->periph_flags &
+                                   PERIPH_RECOVERY_ACTIVE) != 0)
+                                       goto wait_for_opening;
+                               periph->periph_flags |= PERIPH_RECOVERY_ACTIVE;
+                       }
                        break;
                }
                if (periph->periph_active >= periph->periph_openings ||
@@ -424,9 +434,10 @@
        xs = pool_get(&scsipi_xfer_pool,
            ((flags & XS_CTL_NOSLEEP) != 0 ? PR_NOWAIT : PR_WAITOK));
        if (xs == NULL) {
-               if (flags & XS_CTL_URGENT)
-                       periph->periph_flags &= ~PERIPH_RECOVERY_ACTIVE;
-               else
+               if (flags & XS_CTL_URGENT) {
+                       if ((flags & XS_CTL_REQSENSE) == 0)
+                               periph->periph_flags &= ~PERIPH_RECOVERY_ACTIVE;
+               } else
                        periph->periph_active--;
                scsipi_printaddr(periph);
                printf("unable to allocate %sscsipi_xfer\n",
@@ -480,9 +491,10 @@
        }
 #endif
 
-       if (flags & XS_CTL_URGENT)
-               periph->periph_flags &= ~PERIPH_RECOVERY_ACTIVE;
-       else
+       if (flags & XS_CTL_URGENT) {
+               if ((flags & XS_CTL_REQSENSE) == 0)
+                       periph->periph_flags &= ~PERIPH_RECOVERY_ACTIVE;
+       } else
                periph->periph_active--;
        if (periph->periph_active == 0 &&
            (periph->periph_flags & PERIPH_WAITDRAIN) != 0) {
@@ -1065,20 +1077,6 @@
        /* Mark the command as `done'. */
        xs->xs_status |= XS_STS_DONE;
 
-       /*
-        * If it's a user level request, bypass all usual completion
-        * processing, let the user work it out..  We take reponsibility
-        * for freeing the xs (and restarting the device's queue) when
-        * the user returns.
-        */
-       if ((xs->xs_control & XS_CTL_USERCMD) != 0) {
-               splx(s);
-               SC_DEBUG(periph, SCSIPI_DB3, ("calling user done()\n"));
-               scsipi_user_done(xs);
-               SC_DEBUG(periph, SCSIPI_DB3, ("returned from user done()\n "));
-               goto out;
-       }
-
 #ifdef DIAGNOSTIC
        if ((xs->xs_control & (XS_CTL_ASYNC|XS_CTL_POLL)) ==
            (XS_CTL_ASYNC|XS_CTL_POLL))
@@ -1099,6 +1097,15 @@
                scsipi_periph_freeze(periph, freezecnt);
 
        /*
+        * record the xfer with a pending sense, in case a SCSI reset is
+        * received before the thread is waked up.
+        */
+       if (xs->error == XS_BUSY && xs->status == SCSI_CHECK) {
+               periph->periph_flags |= PERIPH_SENSE;
+               periph->periph_xscheck = xs;
+       }
+
+       /*
         * If this was an xfer that was not to complete asynchrnously,
         * let the requesting thread perform error checking/handling
         * in its context.
@@ -1188,6 +1195,35 @@
        if ((xs->xs_control & XS_CTL_ASYNC) != 0 && xs->bp == NULL)
                panic("scsipi_complete: XS_CTL_ASYNC but no buf");
 #endif
+       /*
+        * If command terminated with a CHECK CONDITION, we need to issue a
+        * REQUEST_SENSE command. Once the REQUEST_SENSE has been processed
+        * we'll have the real status.
+        * Must be processed at splbio() to avoid missing a SCSI bus reset
+        * for this command.
+        */
+       s = splbio();
+       if (xs->error == XS_BUSY && xs->status == SCSI_CHECK) {
+               /* request sense for a request sense ? */
+               if (xs->xs_control & XS_CTL_REQSENSE) {
+                       scsipi_printaddr(periph);
+                       printf("request sense for request sense\n");
+                       return EIO;
+               }
+               scsipi_request_sense(xs);
+       }
+       splx(s);
+       /*
+        * If it's a user level request, bypass all usual completion
+        * processing, let the user work it out..  
+        */
+       if ((xs->xs_control & XS_CTL_USERCMD) != 0) {
+               SC_DEBUG(periph, SCSIPI_DB3, ("calling user done()\n"));
+               scsipi_user_done(xs);
+               SC_DEBUG(periph, SCSIPI_DB3, ("returned from user done()\n "));
+               return 0;
+       }
+
 
        switch (xs->error) {
        case XS_NOERROR:
@@ -1265,11 +1301,19 @@
                break;
 
        case XS_RESET:
-               if (xs->xs_retries != 0) {
-                       xs->xs_retries--;
-                       error = ERESTART;
-               } else
-                       error = EIO;
+               if (xs->xs_control & XS_CTL_REQSENSE) {
+                       /*
+                        * request sense interrupted by reset: signal it
+                        * with EINTR return code.
+                        */
+                       error = EINTR;
+               } else {
+                       if (xs->xs_retries != 0) {
+                               xs->xs_retries--;
+                               error = ERESTART;
+                       } else
+                               error = EIO;
+               }
                break;
 
        default:
@@ -1328,6 +1372,59 @@
 }
 
 /*
+ * Issue a request sense for the given scsipi_xfer. Called when the xfer
+ * returns with a CHECK_CONDITION status. Must be called in valid thread
+ * context and at splbio().
+ */
+
+void
+scsipi_request_sense(xs)
+       struct scsipi_xfer *xs;
+{
+       struct scsipi_periph *periph = xs->xs_periph;
+       int flags, error;
+       struct scsipi_sense cmd;
+
+       periph->periph_flags |= PERIPH_SENSE;
+
+       /* if command was polling, request sense will too */
+       flags = xs->xs_control & XS_CTL_POLL;
+       /* Polling commands can't sleep */
+       if (flags)
+               flags |= XS_CTL_NOSLEEP;
+
+       flags |= XS_CTL_REQSENSE | XS_CTL_URGENT | XS_CTL_DATA_IN |
+           XS_CTL_THAW_PERIPH | XS_CTL_FREEZE_PERIPH;
+
+       bzero(&cmd, sizeof(cmd));
+       cmd.opcode = REQUEST_SENSE;
+       cmd.length = sizeof(struct scsipi_sense_data);
+
+       error = scsipi_command(periph,
+           (struct scsipi_generic *) &cmd, sizeof(cmd),
+           (u_char*)&xs->sense.scsi_sense, sizeof(struct scsipi_sense_data),
+           0, 1000, NULL, flags);
+       periph->periph_flags &= ~PERIPH_SENSE;
+       periph->periph_xscheck = NULL;
+       switch(error) {
+       case 0:
+               /* we have a valid sense */
+               xs->error = XS_SENSE;
+               return;
+       case EINTR:
+               /* REQUEST_SENSE interrupted by bus reset. */
+               xs->error = XS_RESET;
+               return;
+       default:
+                /* Notify that request sense failed. */
+               xs->error = XS_DRIVER_STUFFUP;
+               scsipi_printaddr(periph);
+               printf("request sense failed with error %d\n", error);
+               return;
+       }
+}
+
+/*
  * scsipi_enqueue:
  *
  *     Enqueue an xfer on a channel.
@@ -1428,7 +1525,8 @@
                        if ((periph->periph_active > periph->periph_openings) ||                            periph->periph_qfreeze != 0)
                                continue;
 
-                       if ((periph->periph_flags & PERIPH_RECOVERING) != 0 &&
+                       if ((periph->periph_flags &
+                           (PERIPH_RECOVERING | PERIPH_SENSE)) != 0 &&
                            (xs->xs_control & XS_CTL_URGENT) == 0)
                                continue;
 
@@ -1532,7 +1630,8 @@
         *        the xfer to the appropriate byte for the tag
         *        message.
         */
-       if ((PERIPH_XFER_MODE(periph) & PERIPH_CAP_TQING) == 0) {
+       if ((PERIPH_XFER_MODE(periph) & PERIPH_CAP_TQING) == 0 ||
+               (xs->xs_control & XS_CTL_REQSENSE)) {
                xs->xs_control &= ~XS_CTL_TAGMASK;
                xs->xs_tag_type = 0;
        } else {
@@ -1757,6 +1856,9 @@
                scsipi_async_event_xfer_mode(chan,
                    (struct scsipi_xfer_mode *)arg);
                break;
+       case ASYNC_EVENT_RESET:
+               scsipi_async_event_channel_reset(chan);
+               break;
        }
        splx(s);
 }
@@ -1954,6 +2056,47 @@
 }
 
 /*
+ * scsipi_channel_reset:
+ *
+ *     handle scsi bus reset
+ */
+void
+scsipi_async_event_channel_reset(chan)
+       struct scsipi_channel *chan;
+{
+       struct scsipi_xfer *xs, *xs_next;
+       struct scsipi_periph *periph;
+       int target, lun;
+
+       /*
+        * Channel has been reset. Also mark as reset pending REQUEST_SENSE
+        * commands; as the sense is not available any more.
+        */
+
+       for (xs = TAILQ_FIRST(&chan->chan_queue); xs != NULL; xs = xs_next) {
+               xs_next = TAILQ_NEXT(xs, channel_q);
+               if (xs->xs_control & XS_CTL_REQSENSE) {
+                       xs->error = XS_RESET;
+                       scsipi_done(xs);
+               }
+       }



Home | Main Index | Thread Index | Old Index