summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
authorgibbs <gibbs@FreeBSD.org>1997-04-14 02:27:50 +0000
committergibbs <gibbs@FreeBSD.org>1997-04-14 02:27:50 +0000
commit9fb6b2694974886a6b3fb578d2389d904865c614 (patch)
treebc2d6d2d20c6d9f33be64042b6f2a4cfc4b27283 /sys
parent5b06a0324017e693dc131b617db2ea92a00ee759 (diff)
downloadFreeBSD-src-9fb6b2694974886a6b3fb578d2389d904865c614.zip
FreeBSD-src-9fb6b2694974886a6b3fb578d2389d904865c614.tar.gz
Be more careful about how SCBs are cleaned up during error recovery.
Add some more diagnostic information to timeouts.
Diffstat (limited to 'sys')
-rw-r--r--sys/i386/scsi/aic7xxx.c286
1 files changed, 173 insertions, 113 deletions
diff --git a/sys/i386/scsi/aic7xxx.c b/sys/i386/scsi/aic7xxx.c
index 0c0fcc7..52ca36f 100644
--- a/sys/i386/scsi/aic7xxx.c
+++ b/sys/i386/scsi/aic7xxx.c
@@ -32,7 +32,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $Id: aic7xxx.c,v 1.114 1997/04/07 18:32:47 gibbs Exp $
+ * $Id: aic7xxx.c,v 1.115 1997/04/10 19:14:58 gibbs Exp $
*/
/*
* TODO:
@@ -264,6 +264,13 @@ restart_sequencer(ahc)
((sc_link)->scsibus == (ahc)->sc_link_b.scsibus)
#endif
+#define SCB_TARGET(scb) \
+ (((scb)->hscb->tcl & TID) >> 4)
+#define SCB_LUN(scb) \
+ ((scb)->hscb->tcl & LID)
+#define SCB_IS_SCSIBUS_B(scb) \
+ (((scb)->hscb->tcl & SELBUSB) != 0)
+
static u_int8_t ahc_abort_wscb __P((struct ahc_softc *ahc, struct scb *scbp,
u_int8_t scbpos, u_int8_t prev,
u_int32_t xs_error));
@@ -917,14 +924,21 @@ ahc_handle_seqint(ahc, intstat)
break;
}
}
+
/*
* We expect a busfree to occur, so don't bother to interrupt
* when it happens - we've already handled the error.
*/
ahc_outb(ahc, SIMODE1, ahc_inb(ahc, SIMODE1) & ~ENBUSFREE);
- busy_scbid = ahc_index_busy_target(ahc, target, channel,
- /*unbusy*/TRUE);
+ /*
+ * XXX Don't know why this happened or which transaction
+ * caused it, so we have to be pretty heavy handed. We should
+ * probably modify the sequencer so we can issue a bus device
+ * reset instead of an abort in this case.
+ */
+ ahc_reset_device(ahc, target, channel, ALL_LUNS, SCB_LIST_NULL,
+ XS_TIMEOUT);
printf("%s:%c:%d: no active SCB for reconnecting "
"target - issuing ABORT\n",
@@ -1587,12 +1601,6 @@ ahc_handle_scsiint(ahc, intstat)
/*
* We have an SCB we have to clean up.
*/
- if ((scb_control & DISCONNECTED) != 0)
- ahc_rem_scb_from_disc_list(ahc, scbptr);
- else {
- ahc_add_curscb_to_free_list(ahc);
- }
-
/* Did we ask for this?? */
if ((lastphase == P_MESGOUT
|| lastphase == P_MESGIN) && scb != NULL) {
@@ -1603,11 +1611,9 @@ ahc_handle_scsiint(ahc, intstat)
} else if (scb->flags & SCB_ABORT) {
struct hardware_scb *hscb;
u_int8_t tag;
- int lun;
hscb = scb->hscb;
tag = SCB_LIST_NULL;
- lun = hscb->tcl & 0x7;
sc_print_addr(scb->xs->sc_link);
printf("SCB %d - Abort "
"Completed.\n",
@@ -1617,7 +1623,7 @@ ahc_handle_scsiint(ahc, intstat)
ahc_reset_device(ahc,
target,
channel,
- lun,
+ SCB_LUN(scb),
tag,
XS_TIMEOUT);
ahc_run_done_queue(ahc);
@@ -1629,30 +1635,22 @@ ahc_handle_scsiint(ahc, intstat)
}
if (printerror != 0) {
if (scb != NULL) {
- u_int8_t next;
- ahc_index_busy_target(ahc, target,
- channel, /*unbusy*/TRUE);
-
- ahc_outb(ahc, SCBPTR, scbptr);
- next = ahc_inb(ahc, SCB_LINKED_NEXT);
- if (next != SCB_LIST_NULL) {
- /*
- * Re-queue the waiting SCB via the
- * waiting list.
- */
- struct scb *next_scb;
+ u_int8_t tag;
- next_scb =
- ahc->scb_data->scbarray[next];
- STAILQ_INSERT_HEAD(&ahc->waiting_scbs,
- next_scb, links);
- next_scb->flags |= SCB_WAITINGQ;
- }
- scb->xs->error = XS_TIMEOUT;
- sc_print_addr(scb->xs->sc_link);
- ahc_done(ahc, scb);
- scb = NULL;
+ if ((scb->hscb->control & TAG_ENB) != 0)
+ tag = scb->hscb->tag;
+ else
+ tag = SCB_LIST_NULL;
+ ahc_reset_device(ahc, target, channel,
+ SCB_LUN(scb), tag, XS_TIMEOUT);
} else {
+ /*
+ * XXX can we handle this better?
+ * Reset the bus? Send a Bus Device Reset?
+ */
+ ahc_reset_device(ahc, target, channel,
+ ALL_LUNS, SCB_LIST_NULL,
+ XS_TIMEOUT);
printf("%s: ", ahc_name(ahc));
}
printf("Unexpected busfree. LASTPHASE == 0x%x\n"
@@ -1684,6 +1682,12 @@ ahc_handle_scsiint(ahc, intstat)
printf("%s: ahc_intr - referenced scb not "
"valid during SELTO scb(%d)\n",
ahc_name(ahc), scb_index);
+ printf("SEQADDR = 0x%x SCSISEQ = 0x%x "
+ "SSTAT0 = 0x%x SSTAT1 = 0x%x\n",
+ ahc_inb(ahc, SEQADDR0)
+ | (ahc_inb(ahc, SEQADDR1) << 8),
+ ahc_inb(ahc, SCSISEQ), ahc_inb(ahc, SSTAT0),
+ ahc_inb(ahc,SSTAT1));
} else {
/*
* XXX If we queued an abort tag, go clean up the
@@ -1696,9 +1700,9 @@ ahc_handle_scsiint(ahc, intstat)
* target, and mark the target as free
*/
ahc_outb(ahc, MSG_LEN, 0);
- ahc_index_busy_target(ahc, xs->sc_link->target,
- IS_SCSIBUS_B(ahc, xs->sc_link) ? 'B' : 'A',
- /*unbusy*/TRUE);
+ ahc_index_busy_target(ahc, SCB_TARGET(scb),
+ SCB_IS_SCSIBUS_B(scb) ? 'B' : 'A',
+ /*unbusy*/TRUE);
ahc_outb(ahc, SCB_CONTROL, 0);
/* Shift the waiting Q forward. */
@@ -1827,7 +1831,6 @@ ahc_handle_devreset(ahc, scb)
* Go back to async/narrow transfers and
* renegotiate.
*/
- ahc_index_busy_target(ahc, target, channel, /*unbusy*/TRUE);
ahc->needsdtr |= ahc->needsdtr_orig & targ_mask;
ahc->needwdtr |= ahc->needwdtr_orig & targ_mask;
ahc->sdtrpending &= ~targ_mask;
@@ -3015,8 +3018,10 @@ ahc_timeout(arg)
printf(", SCSISIGI == 0x%x\n", ahc_inb(ahc, SCSISIGI));
- printf("SEQADDR == 0x%x\n", ahc_inb(ahc, SEQADDR0)
- | (ahc_inb(ahc, SEQADDR1) << 8));
+ printf("SEQADDR = 0x%x SCSISEQ = 0x%x SSTAT0 = 0x%x SSTAT1 = 0x%x\n",
+ ahc_inb(ahc, SEQADDR0) | (ahc_inb(ahc, SEQADDR1) << 8),
+ ahc_inb(ahc, SCSISEQ), ahc_inb(ahc, SSTAT0),
+ ahc_inb(ahc,SSTAT1));
/* Decide our course of action */
channel = (scb->hscb->tcl & SELBUSB) ? 'B': 'A';
@@ -3074,6 +3079,10 @@ bus_reset:
if (bus_state != P_BUSFREE) {
if (active_scb_index >= ahc->scb_data->numscbs) {
/* Go "immediatly" to the bus reset */
+ /*
+ * XXX queue an abort for the timedout SCB
+ * instead.
+ */
sc_print_addr(scb->xs->sc_link);
printf("SCB %d: Yucky Immediate reset. "
"Flags = 0x%x\n", scb->hscb->tag,
@@ -3102,20 +3111,29 @@ bus_reset:
} else {
int disconnected;
u_int8_t hscb_index;
+ u_int8_t linked_next;
disconnected = FALSE;
hscb_index = ahc_find_scb(ahc, scb);
if (hscb_index == SCB_LIST_NULL) {
disconnected = TRUE;
+ linked_next = (scb->hscb->datalen >> 24)
+ & 0xFF000000;
} else {
ahc_outb(ahc, SCBPTR, hscb_index);
if (ahc_inb(ahc, SCB_CONTROL) & DISCONNECTED)
disconnected = TRUE;
+ linked_next = ahc_inb(ahc, SCB_LINKED_NEXT);
}
if (disconnected) {
- /* Simply set the ABORT_SCB control bit */
+ /*
+ * Simply set the ABORT_SCB control bit
+ * and preserve the linked next pointer
+ */
scb->hscb->control |= ABORT_SCB|MK_MESSAGE;
+ scb->hscb->datalen &= ~0xFF000000;
+ scb->hscb->datalen |= linked_next << 24;
if ((ahc->flags & AHC_PAGESCBS) == 0)
scb->hscb->control &= ~DISCONNECTED;
scb->flags |= SCB_QUEUED_ABORT
@@ -3274,15 +3292,87 @@ ahc_reset_device(ahc, target, channel, lun, tag, xs_error)
u_int8_t tag;
u_int32_t xs_error;
{
- struct scb *scbp;
- u_char active_scb;
- int i = 0;
+ struct scb *scbp;
+ u_int8_t active_scb;
+ int i;
int found;
/* restore this when we're done */
active_scb = ahc_inb(ahc, SCBPTR);
/*
+ * Deal with the busy target and linked next issues.
+ */
+ {
+ int min_target, max_target;
+ u_int8_t busy_scbid;
+
+ /* Make all targets 'relative' to bus A */
+ if (target == ALL_TARGETS) {
+ switch (channel) {
+ case 'A':
+ min_target = 0;
+ max_target = ahc->type & AHC_WIDE ? 15 : 7;
+ break;
+ case 'B':
+ min_target = 8;
+ max_target = 15;
+ break;
+ case ALL_CHANNELS:
+ min_target = 0;
+ max_target = ahc->type & AHC_WIDE|AHC_TWIN
+ ? 15 : 7;
+ break;
+ }
+ } else {
+ min_target = max_target = target
+ + channel == 'B' ? 8 : 0;
+ }
+
+ for (i = min_target; i <= max_target; i++) {
+ busy_scbid = ahc_index_busy_target(ahc, i, 'A',
+ /*unbusy*/FALSE);
+ if (busy_scbid < ahc->scb_data->numscbs) {
+ struct scb *busy_scb;
+ struct scb *next_scb;
+ u_int8_t next_scbid;
+
+ busy_scb = ahc->scb_data->scbarray[busy_scbid];
+
+ next_scbid = busy_scb->hscb->datalen >> 24;
+
+ if (next_scbid == SCB_LIST_NULL) {
+ busy_scbid = ahc_find_scb(ahc,
+ busy_scb);
+
+ if (busy_scbid != SCB_LIST_NULL) {
+ ahc_outb(ahc, SCBPTR,
+ busy_scbid);
+ next_scbid = ahc_inb(ahc,
+ SCB_LINKED_NEXT);
+ }
+ }
+
+ if (ahc_match_scb(busy_scb, target, channel,
+ lun, tag)) {
+ ahc_index_busy_target(ahc, i, 'A',
+ /*unbusy*/TRUE);
+ }
+
+ if (next_scbid != SCB_LIST_NULL) {
+ next_scb = ahc->scb_data->scbarray[next_scbid];
+ if (ahc_match_scb(next_scb, target,
+ channel, lun, tag))
+ continue;
+ /* Requeue for later processing */
+ STAILQ_INSERT_HEAD(&ahc->waiting_scbs,
+ next_scb, links);
+ next_scb->flags |= SCB_WAITINGQ;
+ }
+ }
+ }
+ }
+ /*
* Remove any entries from the Queue-In FIFO.
*/
found = ahc_search_qinfifo(ahc, target, channel, lun, tag,
@@ -3340,74 +3430,6 @@ ahc_reset_device(ahc, target, channel, lun, tag, xs_error)
}
}
/*
- * Go through the entire SCB array now and look for
- * commands for this target that are active. These
- * are other (most likely tagged) commands that
- * were disconnected when the reset occured.
- */
- for (i = 0; i < ahc->scb_data->numscbs; i++) {
- scbp = ahc->scb_data->scbarray[i];
- if ((scbp->flags & SCB_ACTIVE) != 0
- && (scbp->flags & SCB_QUEUED_FOR_DONE) == 0
- && ahc_match_scb(scbp, target, channel, lun, tag)) {
- u_int8_t busy_scbid;
-
- scbp->flags |= SCB_ABORTED|SCB_QUEUED_FOR_DONE;
- scbp->flags &= ~SCB_ACTIVE;
- scbp->xs->error = xs_error;
- found++;
-
- if ((scbp->flags & SCB_WAITINGQ) != 0) {
- STAILQ_REMOVE(&ahc->waiting_scbs, scbp, scb,
- links);
- scbp->flags &= ~SCB_WAITINGQ;
- }
- /* Ensure the target is "free" */
- busy_scbid = ahc_index_busy_target(ahc, target,
- (scbp->hscb->tcl & SELBUSB) ?
- 'B' : 'A', /*unbusy*/FALSE);
- if (busy_scbid != SCB_LIST_NULL) {
- struct scb *busy_scb;
- struct scb *next_scb;
- u_int8_t next_scbid;
-
- busy_scb = ahc->scb_data->scbarray[busy_scbid];
-
- if (!ahc_match_scb(busy_scb, target,
- channel, lun, tag))
- continue;
-
- ahc_index_busy_target(ahc, target,
- (scbp->hscb->tcl & SELBUSB) ?
- 'B' : 'A', /*unbusy*/TRUE);
-
- next_scbid = busy_scb->hscb->datalen >> 24;
-
- if (next_scbid == SCB_LIST_NULL) {
- busy_scbid = ahc_find_scb(ahc, busy_scb);
-
- if (busy_scbid == SCB_LIST_NULL)
- panic("Couldn't find busy SCB");
-
- ahc_outb(ahc, SCBPTR, busy_scbid);
- next_scbid = ahc_inb(ahc,
- SCB_LINKED_NEXT);
- }
-
- if (next_scbid != SCB_LIST_NULL) {
- next_scb = ahc->scb_data->scbarray[next_scbid];
- if (!ahc_match_scb(next_scb, target,
- channel, lun, tag)) {
- STAILQ_INSERT_HEAD(&ahc->waiting_scbs,
- next_scb, links);
- next_scb->flags |= SCB_WAITINGQ;
- }
- }
- }
- }
- }
-
- /*
* Go through the disconnected list and remove any entries we
* have queued for completion, 0'ing their control byte too.
*/
@@ -3436,6 +3458,45 @@ ahc_reset_device(ahc, target, channel, lun, tag, xs_error)
}
}
}
+ /*
+ * Go through the hardware SCB array looking for commands that
+ * were active but not on any list.
+ */
+ for(i = 0; i < ahc->scb_data->maxhscbs; i++) {
+ u_int8_t scbid;
+
+ ahc_outb(ahc, SCBPTR, i);
+ scbid = ahc_inb(ahc, SCB_TAG);
+ if (scbid < ahc->scb_data->numscbs) {
+ scbp = ahc->scb_data->scbarray[scbid];
+ if (ahc_match_scb(scbp, target, channel, lun, tag)) {
+ ahc_add_curscb_to_free_list(ahc);
+ }
+ }
+ }
+ /*
+ * Go through the entire SCB array now and look for
+ * commands for this target that are still active. These
+ * are other tagged commands that were disconnected when
+ * the reset occured or untagged commands that were linked
+ * to the command that preceeded it.
+ */
+ for (i = 0; i < ahc->scb_data->numscbs; i++) {
+ scbp = ahc->scb_data->scbarray[i];
+ if ((scbp->flags & SCB_ACTIVE) != 0
+ && ahc_match_scb(scbp, target, channel, lun, tag)) {
+ scbp->flags |= SCB_ABORTED|SCB_QUEUED_FOR_DONE;
+ scbp->flags &= ~SCB_ACTIVE;
+ scbp->xs->error = xs_error;
+ found++;
+
+ if ((scbp->flags & SCB_WAITINGQ) != 0) {
+ STAILQ_REMOVE(&ahc->waiting_scbs, scbp, scb,
+ links);
+ scbp->flags &= ~SCB_WAITINGQ;
+ }
+ }
+ }
ahc_outb(ahc, SCBPTR, active_scb);
return found;
}
@@ -3505,7 +3566,6 @@ ahc_abort_wscb (ahc, scbp, scbpos, prev, xs_error)
/* Clear the necessary fields */
ahc_outb(ahc, SCB_CONTROL, 0);
- ahc_index_busy_target(ahc, target, channel, /*unbusy*/TRUE);
ahc_add_curscb_to_free_list(ahc);
OpenPOWER on IntegriCloud