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:
parent
e3dfcf49c2
commit
039c6f3637
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=66800
@ -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;
|
||||
}
|
||||
|
@ -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 *);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user