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