Be more careful about how SCBs are cleaned up during error recovery.

Add some more diagnostic information to timeouts.
This commit is contained in:
Justin T. Gibbs 1997-04-14 02:27:50 +00:00
parent 45b7cf8750
commit 085059c3ea

View File

@ -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 @@ ahc_timeout(arg)
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 @@ ahc_timeout(arg)
} 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,14 +3292,86 @@ 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.
*/
@ -3339,74 +3429,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);