From bccca068ce4b917fcd77265d75700e5a2c75070b Mon Sep 17 00:00:00 2001 From: "Justin T. Gibbs" Date: Fri, 28 Feb 1997 03:58:21 +0000 Subject: [PATCH] Functionalize some code that was repeated throughout the driver. Fix a bug in the initialization of the busreset_args that left the B channel args unitialized and the A channel ones initialized to B's vales. Oops. If we get a NO_IDENT sequencer interrupt (the reconnecting target didn't issue an identify or botched it), reset the bus instead of panicing. We should be able to recover from this error. In the AWAITING_MSG handler, order messages by severity. Since the message we send is based on a flag on the SCB, it is possible, during error recovery, to get more than one flag set. This is fine since any time a new flag is set, it is meant to take us to a more draconian level of recovery. This also ensures that we don't lose any "history" of what the command has gone through. When we reset the bus, reset the "send ordered tag" bitmask. Clear some additional interrupt status when we perform a bus reset. --- sys/i386/scsi/aic7xxx.c | 127 ++++++++++++++++++++++++---------------- 1 file changed, 77 insertions(+), 50 deletions(-) diff --git a/sys/i386/scsi/aic7xxx.c b/sys/i386/scsi/aic7xxx.c index 3b2cf43097c3..0f974523bbae 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.102 1997/02/22 09:38:40 peter Exp $ + * $Id: aic7xxx.c,v 1.103 1997/02/25 03:05:31 gibbs Exp $ */ /* * TODO: @@ -289,7 +289,9 @@ static int ahc_reset_device __P((struct ahc_softc *ahc, int target, char channel, int lun, u_int8_t tag, u_int32_t xs_error)); static u_int8_t ahc_rem_scb_from_disc_list __P((struct ahc_softc *ahc, - u_int8_t scbptr)); + u_int8_t scbptr)); +static void ahc_add_curscb_to_free_list __P((struct ahc_softc *ahc)); +static void ahc_clear_intstat __P((struct ahc_softc *ahc)); static void ahc_reset_current_bus __P((struct ahc_softc *ahc)); static void ahc_run_done_queue __P((struct ahc_softc *ahc)); static void ahc_scsirate __P((struct ahc_softc* ahc, u_int8_t *scsirate, @@ -472,8 +474,8 @@ ahc_construct(ahc, bc, ioh, maddr, type, flags) ahc->busreset_args.ahc = ahc; ahc->busreset_args.bus = 'A'; - ahc->busreset_args.ahc = ahc; - ahc->busreset_args.bus = 'B'; + ahc->busreset_args_b.ahc = ahc; + ahc->busreset_args_b.bus = 'B'; #if defined(__FreeBSD__) return (ahc); @@ -940,18 +942,25 @@ ahc_handle_seqint(ahc, intstat) break; } case NO_IDENT: + { /* - * XXX - We probably need to do something very carefully to - * make sure we don't lock up the bus or transfer data to a - * bogus area of memory. Perhaps a bus reset is the best - * alternative??? + * The reconnecting target either did not send an identify + * message, or did, but we didn't find and SCB to match and + * before it could respond to our ATN/abort, it hit a dataphase. + * The only safe thing to do is to blow it away with a bus + * reset. */ - panic("%s:%c:%d: Target did not send an IDENTIFY message. " - "LASTPHASE = 0x%x, SAVED_TCL == 0x%x\n", - ahc_name(ahc), channel, target, - ahc_inb(ahc, LASTPHASE), - ahc_inb(ahc, SAVED_TCL)); - break; + int found; + + printf("%s:%c:%d: Target did not send an IDENTIFY message. " + "LASTPHASE = 0x%x, SAVED_TCL == 0x%x\n", + ahc_name(ahc), channel, target, ahc_inb(ahc, LASTPHASE), + ahc_inb(ahc, SAVED_TCL)); + found = ahc_reset_channel(ahc, channel, XS_TIMEOUT, + /*initiate reset*/TRUE); + printf("%s: Issued Channel %c Bus Reset. " + "%d SCBs aborted\n", ahc_name(ahc), channel, found); + } case BAD_PHASE: printf("%s:%c:%d: unknown scsi bus phase. Attempting to " "continue\n", ahc_name(ahc), channel, target); @@ -1340,6 +1349,15 @@ ahc_handle_seqint(ahc, intstat) ahc_outb(ahc, MSG_LEN, 1); sc_print_addr(scb->xs->sc_link); printf("Bus Device Reset Message Sent\n"); + } else if (scb->flags & SCB_ABORT) { + if ((scb->hscb->control & TAG_ENB) != 0) + ahc_outb(ahc, MSG0 + message_offset, + MSG_ABORT_TAG); + else + ahc_outb(ahc, MSG0 + message_offset, MSG_ABORT); + ahc_outb(ahc, MSG_LEN, message_offset + 1); + sc_print_addr(scb->xs->sc_link); + printf("Abort Message Sent\n"); } else if (scb->flags & SCB_MSGOUT_WDTR) { ahc_construct_wdtr(ahc, message_offset, BUS_16_BIT); } else if (scb->flags & SCB_MSGOUT_SDTR) { @@ -1368,15 +1386,6 @@ ahc_handle_seqint(ahc, intstat) ahc_syncrates[i].period, (target_scratch & WIDEXFER) ? MAX_OFFSET_16BIT : MAX_OFFSET_8BIT); - } else if (scb->flags & SCB_ABORT) { - if ((scb->hscb->control & TAG_ENB) != 0) - ahc_outb(ahc, MSG0 + message_offset, - MSG_ABORT_TAG); - else - ahc_outb(ahc, MSG0 + message_offset, MSG_ABORT); - ahc_outb(ahc, MSG_LEN, message_offset + 1); - sc_print_addr(scb->xs->sc_link); - printf("Abort Message Sent\n"); } else panic("ahc_intr: AWAITING_MSG for an SCB that " "does not have a waiting message"); @@ -1554,11 +1563,7 @@ ahc_handle_scsiint(ahc, intstat) if ((scb_control & DISCONNECTED) != 0) ahc_rem_scb_from_disc_list(ahc, scbptr); else { - /* Put us on the free list */ - ahc_outb(ahc, SCB_NEXT, - ahc_inb(ahc, FREE_SCBH)); - ahc_outb(ahc, FREE_SCBH, - ahc_inb(ahc, SCBPTR)); + ahc_add_curscb_to_free_list(ahc); } /* Did we ask for this?? */ @@ -1734,10 +1739,8 @@ ahc_handle_scsiint(ahc, intstat) nextscb = ahc_inb(ahc, SCB_NEXT); ahc_outb(ahc, WAITING_SCBH, nextscb); - /* Put this SCB back on the free list */ - nextscb = ahc_inb(ahc, FREE_SCBH); - ahc_outb(ahc, SCB_NEXT, nextscb); - ahc_outb(ahc, FREE_SCBH, scbptr); + ahc_add_curscb_to_free_list(ahc); + restart_sequencer(ahc); } else { sc_print_addr(scb->xs->sc_link); @@ -1974,6 +1977,9 @@ ahc_init(ahc) /* Set the next pointer */ ahc_outb(ahc, SCB_NEXT, i+1); + /* Make the tag number invalid */ + ahc_outb(ahc, SCB_TAG, SCB_LIST_NULL); + /* No Busy non-tagged targets yet */ ahc_outb(ahc, SCB_ACTIVE0, SCB_LIST_NULL); ahc_outb(ahc, SCB_ACTIVE1, SCB_LIST_NULL); @@ -2975,9 +2981,14 @@ ahc_timeout(arg) ahc_outb(ahc, SCBPTR, saved_scbptr); /* ahc_run_waiting_queue will unpause us */ ahc_run_waiting_queue(ahc); - } else + } else { /* Go "immediatly" to the bus reset */ + sc_print_addr(scb->xs->sc_link); + printf("SCB %d: Immediate reset. " + "Flags = 0x%x\n", scb->hscb->tag, + scb->flags); goto bus_reset; + } } } splx(s); @@ -3257,9 +3268,7 @@ ahc_rem_scb_from_disc_list(ahc, scbptr) ahc_outb(ahc, SCB_CONTROL, 0); - /* Add this SCB to the free list */ - ahc_outb(ahc, SCB_NEXT, ahc_inb(ahc, FREE_SCBH)); - ahc_outb(ahc, FREE_SCBH, scbptr); + ahc_add_curscb_to_free_list(ahc); if (prev != SCB_LIST_NULL) { ahc_outb(ahc, SCBPTR, prev); @@ -3274,6 +3283,17 @@ ahc_rem_scb_from_disc_list(ahc, scbptr) return next; } +static void +ahc_add_curscb_to_free_list(ahc) + struct ahc_softc *ahc; +{ + /* Invalidate the tag so that ahc_find_scb doesn't think it's active */ + ahc_outb(ahc, SCB_TAG, SCB_LIST_NULL); + + ahc_outb(ahc, SCB_NEXT, ahc_inb(ahc, FREE_SCBH)); + ahc_outb(ahc, FREE_SCBH, ahc_inb(ahc, SCBPTR)); +} + /* * Manipulate the waiting for selection list and return the * scb that follows the one that we remove. @@ -3301,9 +3321,7 @@ ahc_abort_wscb (ahc, scbp, scbpos, prev, xs_error) ahc_outb(ahc, SCB_CONTROL, 0); ahc_index_busy_target(ahc, target, channel, /*unbusy*/TRUE); - /* Add this SCB to the free list */ - ahc_outb(ahc, SCB_NEXT, ahc_inb(ahc, FREE_SCBH)); - ahc_outb(ahc, FREE_SCBH, scbpos); + ahc_add_curscb_to_free_list(ahc); /* update the waiting list */ if (prev == SCB_LIST_NULL) @@ -3355,6 +3373,18 @@ ahc_index_busy_target(ahc, target, channel, unbusy) return busy_scbid; } +static void +ahc_clear_intstat(ahc) + struct ahc_softc *ahc; +{ + /* Clear any interrupt conditions this may have caused */ + ahc_outb(ahc, CLRSINT0, CLRSELDO|CLRSELDI|CLRSELINGO); + ahc_outb(ahc, CLRSINT1, CLRSELTIMEO|CLRATNO|CLRSCSIRSTI + |CLRBUSFREE|CLRSCSIPERR|CLRPHASECHG| + CLRREQINIT); + ahc_outb(ahc, CLRINT, CLRSCSIINT); +} + static void ahc_reset_current_bus(ahc) struct ahc_softc *ahc; @@ -3380,9 +3410,7 @@ ahc_reset_current_bus(ahc) /* Turn off the bus reset */ ahc_outb(ahc, SCSISEQ, scsiseq & ~SCSIRSTO); - /* Clear any interrupt conditions this may have caused */ - ahc_outb(ahc, CLRSINT1, CLRSCSIRSTI|CLRSELTIMEO|CLRBUSFREE); - ahc_outb(ahc, CLRINT, CLRSCSIINT); + ahc_clear_intstat(ahc); /* Re-enable reset interrupts */ ahc_outb(ahc, SIMODE1, ahc_inb(ahc, SIMODE1) | ENSCSIRST); @@ -3422,9 +3450,7 @@ ahc_busreset_complete(arg) scsiseq = ahc_inb(ahc, SCSISEQ); ahc_outb(ahc, SCSISEQ, scsiseq & ~SCSIRSTO); - /* Clear any interrupt conditions this may have caused */ - ahc_outb(ahc, CLRSINT1, CLRSCSIRSTI|CLRSELTIMEO|CLRBUSFREE); - ahc_outb(ahc, CLRINT, CLRSCSIINT); + ahc_clear_intstat(ahc); /* Re-enable reset interrupts */ ahc_outb(ahc, SIMODE1, ahc_inb(ahc, SIMODE1) | ENSCSIRST); @@ -3484,11 +3510,13 @@ ahc_reset_channel(ahc, channel, xs_error, initiate_reset) if (channel == 'B') { ahc->needsdtr |= (ahc->needsdtr_orig & 0xff00); ahc->sdtrpending &= 0x00ff; + ahc->orderedtag &= 0x00ff; offset = TARG_SCRATCH + 8; offset_max = TARG_SCRATCH + 16; } else if (ahc->type & AHC_WIDE){ ahc->needsdtr = ahc->needsdtr_orig; ahc->needwdtr = ahc->needwdtr_orig; + ahc->orderedtag = 0; ahc->sdtrpending = 0; ahc->wdtrpending = 0; offset = TARG_SCRATCH; @@ -3496,6 +3524,7 @@ ahc_reset_channel(ahc, channel, xs_error, initiate_reset) } else { ahc->needsdtr |= (ahc->needsdtr_orig & 0x00ff); ahc->sdtrpending &= 0xff00; + ahc->orderedtag &= 0xff00; offset = TARG_SCRATCH; offset_max = TARG_SCRATCH + 8; } @@ -3527,17 +3556,15 @@ ahc_reset_channel(ahc, channel, xs_error, initiate_reset) ahc_outb(ahc, SIMODE1, ahc_inb(ahc, SIMODE1) & ~ENBUSFREE); if (initiate_reset) ahc_reset_current_bus(ahc); - ahc_outb(ahc, CLRSINT1, CLRSCSIRSTI|CLRSELTIMEO|CLRBUSFREE); - ahc_outb(ahc, CLRINT, CLRSCSIINT); + ahc_clear_intstat(ahc); ahc_outb(ahc, SBLKCTL, sblkctl); - unpause_sequencer(ahc, /*unpause_always*/TRUE); + unpause_sequencer(ahc, /*unpause_always*/FALSE); } else { /* Case 2: A command from this bus is active or we're idle */ ahc_outb(ahc, SIMODE1, ahc_inb(ahc, SIMODE1) & ~ENBUSFREE); if (initiate_reset) ahc_reset_current_bus(ahc); - ahc_outb(ahc, CLRSINT1, CLRSCSIRSTI|CLRSELTIMEO|CLRBUSFREE); - ahc_outb(ahc, CLRINT, CLRSCSIINT); + ahc_clear_intstat(ahc); restart_sequencer(ahc); } ahc_run_done_queue(ahc);