Correct corruption of the qinfifo in ahc_search_qinififo() for all

non-LVD controllers.  We only need to take special action on the qinfifo
if we have dectected the case of an SCB that has been removed from the
qinfifo but has not been fully DMAed to the controller.  A missing
conditional caused this code to be executed every time an SCB was
aborted from the queue

Don't attempt to print the path of an SCB that has been freed.

Clean up the traversal of the pending scb list in
ahc_update_pending_syncrates().  This has no functional change.

Correct ahc_timeout()'s requeing of a timedout SCB to effect a
recovery action.  We now use ahc_qinfifo_requeue() and a
new function ahc_qinfifo_count() instead of performing the
requeue inline.  The old code did not conform to the new qinfifo
method.

Clear the timedout SCB from the disconnected list.  This ensures
that the SCB_NEXT field is free to be used for queuing us to
the qinfifo.
This commit is contained in:
Justin T. Gibbs 2000-10-08 03:37:52 +00:00
parent e3dfcf49c2
commit 039c6f3637
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=66800
4 changed files with 105 additions and 96 deletions

View File

@ -134,9 +134,6 @@ static struct tmode_tstate*
u_int scsi_id, char channel);
static void ahc_free_tstate(struct ahc_softc *ahc,
u_int scsi_id, char channel, int force);
static void ahc_qinfifo_requeue(struct ahc_softc *ahc,
struct scb *prev_scb,
struct scb *scb);
static struct ahc_syncrate*
ahc_devlimited_syncrate(struct ahc_softc *ahc,
u_int *period,
@ -973,11 +970,11 @@ ahc_handle_scsiint(struct ahc_softc *ahc, u_int intstat)
tag = scb->hscb->tag;
else
tag = SCB_LIST_NULL;
ahc_print_path(ahc, scb);
ahc_abort_scbs(ahc, target, channel,
SCB_GET_LUN(scb), tag,
ROLE_INITIATOR,
CAM_UNEXP_BUSFREE);
ahc_print_path(ahc, scb);
} else {
/*
* We had not fully identified this connection,
@ -1643,9 +1640,8 @@ ahc_update_pending_syncrates(struct ahc_softc *ahc)
* Traverse the pending SCB list and ensure that all of the
* SCBs there have the proper settings.
*/
pending_scb = LIST_FIRST(&ahc->pending_scbs);
pending_scb_count = 0;
while (pending_scb != NULL) {
LIST_FOREACH(pending_scb, &ahc->pending_scbs, pending_links) {
struct ahc_devinfo devinfo;
struct hardware_scb *pending_hscb;
struct ahc_initiator_tinfo *tinfo;
@ -1662,7 +1658,6 @@ ahc_update_pending_syncrates(struct ahc_softc *ahc)
pending_hscb->scsirate = tinfo->scsirate;
pending_hscb->scsioffset = tinfo->current.offset;
pending_scb_count++;
pending_scb = LIST_NEXT(pending_scb, pending_links);
}
if (pending_scb_count == 0)
@ -1671,34 +1666,24 @@ ahc_update_pending_syncrates(struct ahc_softc *ahc)
saved_scbptr = ahc_inb(ahc, SCBPTR);
/* Ensure that the hscbs down on the card match the new information */
for (i = 0; i < ahc->scb_data->maxhscbs; i++) {
u_int scb_tag;
struct hardware_scb *pending_hscb;
u_int control;
u_int scb_tag;
ahc_outb(ahc, SCBPTR, i);
scb_tag = ahc_inb(ahc, SCB_TAG);
if (scb_tag != SCB_LIST_NULL) {
struct ahc_devinfo devinfo;
struct scb *pending_scb;
struct hardware_scb *pending_hscb;
struct ahc_initiator_tinfo *tinfo;
struct tmode_tstate *tstate;
u_int control;
pending_scb = ahc_lookup_scb(ahc, scb_tag);
if (pending_scb == NULL)
continue;
pending_scb = ahc_lookup_scb(ahc, scb_tag);
if (pending_scb->flags == SCB_FREE)
continue;
pending_hscb = pending_scb->hscb;
ahc_scb_devinfo(ahc, &devinfo, pending_scb);
tinfo = ahc_fetch_transinfo(ahc, devinfo.channel,
devinfo.our_scsiid,
devinfo.target, &tstate);
control = ahc_inb(ahc, SCB_CONTROL);
control &= ~ULTRAENB;
if ((tstate->ultraenb & devinfo.target_mask) != 0)
control |= ULTRAENB;
ahc_outb(ahc, SCB_CONTROL, control);
ahc_outb(ahc, SCB_SCSIRATE, tinfo->scsirate);
ahc_outb(ahc, SCB_SCSIOFFSET, tinfo->current.offset);
}
pending_hscb = pending_scb->hscb;
control = ahc_inb(ahc, SCB_CONTROL);
control &= ~ULTRAENB;
if ((pending_hscb->control & ULTRAENB) != 0)
control |= ULTRAENB;
ahc_outb(ahc, SCB_CONTROL, control);
ahc_outb(ahc, SCB_SCSIRATE, pending_hscb->scsirate);
ahc_outb(ahc, SCB_SCSIOFFSET, pending_hscb->scsioffset);
}
ahc_outb(ahc, SCBPTR, saved_scbptr);
}
@ -4186,7 +4171,7 @@ ahc_freeze_devq(struct ahc_softc *ahc, struct scb *scb)
ahc_platform_freeze_devq(ahc, scb);
}
static void
void
ahc_qinfifo_requeue(struct ahc_softc *ahc, struct scb *prev_scb,
struct scb *scb)
{
@ -4198,6 +4183,19 @@ ahc_qinfifo_requeue(struct ahc_softc *ahc, struct scb *prev_scb,
scb->hscb->next = ahc->next_queued_scb->hscb->tag;
}
int
ahc_qinfifo_count(struct ahc_softc *ahc)
{
u_int8_t qinpos;
if ((ahc->features & AHC_QUEUE_REGS) != 0) {
qinpos = ahc_inb(ahc, SNSCB_QOFF);
ahc_outb(ahc, SNSCB_QOFF, qinpos);
} else
qinpos = ahc_inb(ahc, QINPOS);
return (ahc->qinfifonext - qinpos);
}
int
ahc_search_qinfifo(struct ahc_softc *ahc, int target, char channel,
int lun, u_int tag, role_t role, uint32_t status,
@ -4213,9 +4211,11 @@ ahc_search_qinfifo(struct ahc_softc *ahc, int target, char channel,
int found;
int maxtarget;
int i;
int have_qregs;
qintail = ahc->qinfifonext;
if ((ahc->features & AHC_QUEUE_REGS) != 0) {
have_qregs = (ahc->features & AHC_QUEUE_REGS) != 0;
if (have_qregs) {
qinstart = ahc_inb(ahc, SNSCB_QOFF);
ahc_outb(ahc, SNSCB_QOFF, qinstart);
} else
@ -4291,9 +4291,12 @@ ahc_search_qinfifo(struct ahc_softc *ahc, int target, char channel,
* back as well.
*/
if (qinpos == (qinstart - 1)) {
ahc_outb(ahc, SNSCB_QOFF, qinpos);
} else {
ahc_outb(ahc, QINPOS, qinpos);
if (have_qregs) {
ahc_outb(ahc, SNSCB_QOFF,
qinpos);
} else {
ahc_outb(ahc, QINPOS, qinpos);
}
}
break;
}

View File

@ -1011,6 +1011,10 @@ int ahc_probe_scbs(struct ahc_softc *);
void ahc_run_untagged_queues(struct ahc_softc *ahc);
void ahc_run_untagged_queue(struct ahc_softc *ahc,
struct scb_tailq *queue);
void ahc_qinfifo_requeue(struct ahc_softc *ahc,
struct scb *prev_scb,
struct scb *scb);
int ahc_qinfifo_count(struct ahc_softc *ahc);
/****************************** Initialization ********************************/
void ahc_init_probe_config(struct ahc_probe_config *);

View File

@ -1412,6 +1412,9 @@ ahc_timeout(void *arg)
pause_sequencer(ahc);
} while (ahc_inb(ahc, INTSTAT) & INT_PEND);
/* Make sure the sequencer is in a safe location. */
ahc_clear_critical_section(ahc);
ahc_print_path(ahc, scb);
if ((scb->flags & SCB_ACTIVE) == 0) {
/* Previous timeout took care of me already */
@ -1447,7 +1450,7 @@ ahc_timeout(void *arg)
printf("sg[%d] - Addr 0x%x : Length %d\n",
i,
scb->sg_list[i].addr,
scb->sg_list[i].len);
scb->sg_list[i].len & AHC_SG_LEN_MASK);
}
}
if (scb->flags & (SCB_DEVICE_RESET|SCB_ABORT)) {
@ -1497,7 +1500,7 @@ ahc_timeout(void *arg)
* and wait for it's timeout to expire before
* taking additional action.
*/
active_scb = &ahc->scb_data->scbarray[active_scb_index];
active_scb = ahc_lookup_scb(ahc, active_scb_index);
if (active_scb->hscb->scsiid != scb->hscb->scsiid
|| active_scb->hscb->lun != scb->hscb->lun) {
struct ccb_hdr *ccbh;
@ -1574,7 +1577,7 @@ ahc_timeout(void *arg)
}
if (disconnected) {
u_int active_scb;
struct scb *prev_scb;
ahc_set_recoveryscb(ahc, scb);
/*
@ -1585,28 +1588,25 @@ ahc_timeout(void *arg)
| SCB_DEVICE_RESET;
/*
* Mark the cached copy of this SCB in the
* disconnected list too, so that a reconnect
* at this point causes a BDR or abort.
* Actually re-queue this SCB in an attempt
* to select the device before it reconnects.
* In either case (selection or reselection),
* we will now issue a target reset to the
* timed-out device.
*
* Remove any cached copy of this SCB in the
* disconnected list in preparation for the
* queuing of our abort SCB. We use the
* same element in the SCB, SCB_NEXT, for
* both the qinfifo and the disconnected list.
*/
active_scb = ahc_inb(ahc, SCBPTR);
if (ahc_search_disc_list(ahc, target,
channel, lun,
scb->hscb->tag,
/*stop_on_first*/TRUE,
/*remove*/FALSE,
/*save_state*/FALSE)) {
u_int scb_control;
scb_control = ahc_inb(ahc, SCB_CONTROL);
scb_control |= MK_MESSAGE;
ahc_outb(ahc, SCB_CONTROL, scb_control);
}
ahc_outb(ahc, SCBPTR, active_scb);
ahc_search_disc_list(ahc, target, channel,
lun, scb->hscb->tag,
/*stop_on_first*/TRUE,
/*remove*/TRUE,
/*save_state*/TRUE);
/*
* Actually re-queue this SCB in case we can
* select the device before it reconnects.
* Clear out any entries in the QINFIFO first
* so we are the next SCB for this target
* to run.
@ -1620,15 +1620,16 @@ ahc_timeout(void *arg)
SEARCH_COMPLETE);
ahc_print_path(ahc, scb);
printf("Queuing a BDR SCB\n");
ahc->qinfifo[ahc->qinfifonext++] =
scb->hscb->tag;
if ((ahc->features & AHC_QUEUE_REGS) != 0) {
ahc_outb(ahc, HNSCB_QOFF,
ahc->qinfifonext);
} else {
ahc_outb(ahc, KERNEL_QINPOS,
ahc->qinfifonext);
prev_scb = NULL;
if (ahc_qinfifo_count(ahc) != 0) {
u_int prev_tag;
prev_tag =
ahc->qinfifo[ahc->qinfifonext - 1];
prev_scb = ahc_lookup_scb(ahc,
prev_tag);
}
ahc_qinfifo_requeue(ahc, prev_scb, scb);
scb->io_ctx->ccb_h.timeout_ch =
timeout(ahc_timeout, (caddr_t)scb, 2 * hz);
unpause_sequencer(ahc);

View File

@ -1412,6 +1412,9 @@ ahc_timeout(void *arg)
pause_sequencer(ahc);
} while (ahc_inb(ahc, INTSTAT) & INT_PEND);
/* Make sure the sequencer is in a safe location. */
ahc_clear_critical_section(ahc);
ahc_print_path(ahc, scb);
if ((scb->flags & SCB_ACTIVE) == 0) {
/* Previous timeout took care of me already */
@ -1447,7 +1450,7 @@ ahc_timeout(void *arg)
printf("sg[%d] - Addr 0x%x : Length %d\n",
i,
scb->sg_list[i].addr,
scb->sg_list[i].len);
scb->sg_list[i].len & AHC_SG_LEN_MASK);
}
}
if (scb->flags & (SCB_DEVICE_RESET|SCB_ABORT)) {
@ -1497,7 +1500,7 @@ ahc_timeout(void *arg)
* and wait for it's timeout to expire before
* taking additional action.
*/
active_scb = &ahc->scb_data->scbarray[active_scb_index];
active_scb = ahc_lookup_scb(ahc, active_scb_index);
if (active_scb->hscb->scsiid != scb->hscb->scsiid
|| active_scb->hscb->lun != scb->hscb->lun) {
struct ccb_hdr *ccbh;
@ -1574,7 +1577,7 @@ ahc_timeout(void *arg)
}
if (disconnected) {
u_int active_scb;
struct scb *prev_scb;
ahc_set_recoveryscb(ahc, scb);
/*
@ -1585,28 +1588,25 @@ ahc_timeout(void *arg)
| SCB_DEVICE_RESET;
/*
* Mark the cached copy of this SCB in the
* disconnected list too, so that a reconnect
* at this point causes a BDR or abort.
* Actually re-queue this SCB in an attempt
* to select the device before it reconnects.
* In either case (selection or reselection),
* we will now issue a target reset to the
* timed-out device.
*
* Remove any cached copy of this SCB in the
* disconnected list in preparation for the
* queuing of our abort SCB. We use the
* same element in the SCB, SCB_NEXT, for
* both the qinfifo and the disconnected list.
*/
active_scb = ahc_inb(ahc, SCBPTR);
if (ahc_search_disc_list(ahc, target,
channel, lun,
scb->hscb->tag,
/*stop_on_first*/TRUE,
/*remove*/FALSE,
/*save_state*/FALSE)) {
u_int scb_control;
scb_control = ahc_inb(ahc, SCB_CONTROL);
scb_control |= MK_MESSAGE;
ahc_outb(ahc, SCB_CONTROL, scb_control);
}
ahc_outb(ahc, SCBPTR, active_scb);
ahc_search_disc_list(ahc, target, channel,
lun, scb->hscb->tag,
/*stop_on_first*/TRUE,
/*remove*/TRUE,
/*save_state*/TRUE);
/*
* Actually re-queue this SCB in case we can
* select the device before it reconnects.
* Clear out any entries in the QINFIFO first
* so we are the next SCB for this target
* to run.
@ -1620,15 +1620,16 @@ ahc_timeout(void *arg)
SEARCH_COMPLETE);
ahc_print_path(ahc, scb);
printf("Queuing a BDR SCB\n");
ahc->qinfifo[ahc->qinfifonext++] =
scb->hscb->tag;
if ((ahc->features & AHC_QUEUE_REGS) != 0) {
ahc_outb(ahc, HNSCB_QOFF,
ahc->qinfifonext);
} else {
ahc_outb(ahc, KERNEL_QINPOS,
ahc->qinfifonext);
prev_scb = NULL;
if (ahc_qinfifo_count(ahc) != 0) {
u_int prev_tag;
prev_tag =
ahc->qinfifo[ahc->qinfifonext - 1];
prev_scb = ahc_lookup_scb(ahc,
prev_tag);
}
ahc_qinfifo_requeue(ahc, prev_scb, scb);
scb->io_ctx->ccb_h.timeout_ch =
timeout(ahc_timeout, (caddr_t)scb, 2 * hz);
unpause_sequencer(ahc);