summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
authorgibbs <gibbs@FreeBSD.org>2000-10-08 03:37:52 +0000
committergibbs <gibbs@FreeBSD.org>2000-10-08 03:37:52 +0000
commit90447d2ee78c3d0cd03cbe5a31ab1929f48c5131 (patch)
treeda95b48e989720dc5c7825de8a095127fdcb2734 /sys
parent4ccc7578a7644c9c3f5255474c19ac7538a5ef0d (diff)
downloadFreeBSD-src-90447d2ee78c3d0cd03cbe5a31ab1929f48c5131.zip
FreeBSD-src-90447d2ee78c3d0cd03cbe5a31ab1929f48c5131.tar.gz
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.
Diffstat (limited to 'sys')
-rw-r--r--sys/dev/aic7xxx/aic7xxx.c77
-rw-r--r--sys/dev/aic7xxx/aic7xxx.h4
-rw-r--r--sys/dev/aic7xxx/aic7xxx_freebsd.c61
-rw-r--r--sys/dev/aic7xxx/aic7xxx_osm.c61
4 files changed, 106 insertions, 97 deletions
diff --git a/sys/dev/aic7xxx/aic7xxx.c b/sys/dev/aic7xxx/aic7xxx.c
index 0cf506d..8c709db 100644
--- a/sys/dev/aic7xxx/aic7xxx.c
+++ b/sys/dev/aic7xxx/aic7xxx.c
@@ -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->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_scb = ahc_lookup_scb(ahc, scb_tag);
+ if (pending_scb == NULL)
+ continue;
+
+ 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)
{
@@ -4199,6 +4184,19 @@ ahc_qinfifo_requeue(struct ahc_softc *ahc, struct scb *prev_scb,
}
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,
ahc_search_action action)
@@ -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;
}
diff --git a/sys/dev/aic7xxx/aic7xxx.h b/sys/dev/aic7xxx/aic7xxx.h
index 5dd129a..7e2bd94 100644
--- a/sys/dev/aic7xxx/aic7xxx.h
+++ b/sys/dev/aic7xxx/aic7xxx.h
@@ -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 *);
diff --git a/sys/dev/aic7xxx/aic7xxx_freebsd.c b/sys/dev/aic7xxx/aic7xxx_freebsd.c
index 9799211..988ebb4 100644
--- a/sys/dev/aic7xxx/aic7xxx_freebsd.c
+++ b/sys/dev/aic7xxx/aic7xxx_freebsd.c
@@ -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 @@ bus_reset:
* 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 @@ bus_reset:
}
if (disconnected) {
- u_int active_scb;
+ struct scb *prev_scb;
ahc_set_recoveryscb(ahc, scb);
/*
@@ -1585,28 +1588,25 @@ bus_reset:
| 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 @@ bus_reset:
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);
diff --git a/sys/dev/aic7xxx/aic7xxx_osm.c b/sys/dev/aic7xxx/aic7xxx_osm.c
index 9799211..988ebb4 100644
--- a/sys/dev/aic7xxx/aic7xxx_osm.c
+++ b/sys/dev/aic7xxx/aic7xxx_osm.c
@@ -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 @@ bus_reset:
* 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 @@ bus_reset:
}
if (disconnected) {
- u_int active_scb;
+ struct scb *prev_scb;
ahc_set_recoveryscb(ahc, scb);
/*
@@ -1585,28 +1588,25 @@ bus_reset:
| 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 @@ bus_reset:
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);
OpenPOWER on IntegriCloud