summaryrefslogtreecommitdiffstats
path: root/sys/dev/aic7xxx
diff options
context:
space:
mode:
authorgibbs <gibbs@FreeBSD.org>2004-05-11 20:39:46 +0000
committergibbs <gibbs@FreeBSD.org>2004-05-11 20:39:46 +0000
commitf18f582b8f08a49281d83af19ae29774fdffd1f9 (patch)
tree746bcf97dc96edd5bc70a5f2ab5bc7ce886579aa /sys/dev/aic7xxx
parent82f4a65caf9e61c4e527c1a12289aa5d1baef4b9 (diff)
downloadFreeBSD-src-f18f582b8f08a49281d83af19ae29774fdffd1f9.zip
FreeBSD-src-f18f582b8f08a49281d83af19ae29774fdffd1f9.tar.gz
o When restarting the sequencer, clear any pending
sequencer interrupt codes. These codes are only relevant to the code that was last being executed and that context is cleared when we reset the program counter. This addresses a race condition between a sequencer interrupt and any SCSI event that causes us to restart the sequencer. o When running the untagged-Q, we must start the timer for any transaction we queue. o Give the firmware half a millisecond between pauses to flush work out. This should give us around half a second of total delay before flagging an issue with pausing and flushing controller work. Only attempt to clear critical sections if there are no pending interrupts in the pause and flush loop. If the sequencer has issued an INTSTAT, we may not be able to step out of the critical section. o Cancel pending transactions on devices that respond with a selection timeout. This decreases the duration of timeout recovery when a device disappears. Don't bother forcing renegotiation on a selection timeout now that we use the device reset handler to abort any pending commands on the target. The device reset handler already takes us down to async narrow and forces a renegotiation. o In the device reset handlers, only send a BDR sent async event if the status is not CAM_SEL_TIMEOUT. This avoids sending this event in the selection timeout case. o Modify the Core timeout handler to verify that another command has the potential to timeout before passing off a command timeout as due to some other command.
Diffstat (limited to 'sys/dev/aic7xxx')
-rw-r--r--sys/dev/aic7xxx/aic7xxx.c114
1 files changed, 88 insertions, 26 deletions
diff --git a/sys/dev/aic7xxx/aic7xxx.c b/sys/dev/aic7xxx/aic7xxx.c
index ea837a2..ef40938 100644
--- a/sys/dev/aic7xxx/aic7xxx.c
+++ b/sys/dev/aic7xxx/aic7xxx.c
@@ -37,7 +37,7 @@
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
- * $Id: //depot/aic7xxx/aic7xxx/aic7xxx.c#148 $
+ * $Id: //depot/aic7xxx/aic7xxx/aic7xxx.c#155 $
*/
#ifdef __linux__
@@ -231,6 +231,9 @@ static int ahc_check_patch(struct ahc_softc *ahc,
u_int start_instr, u_int *skip_addr);
static void ahc_download_instr(struct ahc_softc *ahc,
u_int instrptr, uint8_t *dconsts);
+static int ahc_other_scb_timeout(struct ahc_softc *ahc,
+ struct scb *scb,
+ struct scb *other_scb);
#ifdef AHC_TARGET_MODE
static void ahc_queue_lstate_event(struct ahc_softc *ahc,
struct ahc_tmode_lstate *lstate,
@@ -290,10 +293,19 @@ ahc_restart(struct ahc_softc *ahc)
ahc_outb(ahc, SEQ_FLAGS2,
ahc_inb(ahc, SEQ_FLAGS2) & ~SCB_DMA);
}
+
+ /*
+ * Clear any pending sequencer interrupt. It is no
+ * longer relevant since we're resetting the Program
+ * Counter.
+ */
+ ahc_outb(ahc, CLRINT, CLRSEQINT);
+
ahc_outb(ahc, MWI_RESIDUAL, 0);
ahc_outb(ahc, SEQCTL, ahc->seqctl);
ahc_outb(ahc, SEQADDR0, 0);
ahc_outb(ahc, SEQADDR1, 0);
+
ahc_unpause(ahc);
}
@@ -365,6 +377,7 @@ ahc_run_untagged_queue(struct ahc_softc *ahc, struct scb_tailq *queue)
if ((scb = TAILQ_FIRST(queue)) != NULL
&& (scb->flags & SCB_ACTIVE) == 0) {
scb->flags |= SCB_ACTIVE;
+ aic_scb_timer_start(scb);
ahc_queue_scb(ahc, scb);
}
}
@@ -1177,19 +1190,20 @@ ahc_handle_scsiint(struct ahc_softc *ahc, u_int intstat)
scb_index);
}
#endif
- /*
- * Force a renegotiation with this target just in
- * case the cable was pulled and will later be
- * re-attached. The target may forget its negotiation
- * settings with us should it attempt to reselect
- * during the interruption. The target will not issue
- * a unit attention in this case, so we must always
- * renegotiate.
- */
ahc_scb_devinfo(ahc, &devinfo, scb);
- ahc_force_renegotiation(ahc, &devinfo);
aic_set_transaction_status(scb, CAM_SEL_TIMEOUT);
ahc_freeze_devq(ahc, scb);
+
+ /*
+ * Cancel any pending transactions on the device
+ * now that it seems to be missing. This will
+ * also revert us to async/narrow transfers until
+ * we can renegotiate with the device.
+ */
+ ahc_handle_devreset(ahc, &devinfo,
+ CAM_SEL_TIMEOUT,
+ "Selection Timeout",
+ /*verbose_level*/1);
}
ahc_outb(ahc, CLRINT, CLRSCSIINT);
ahc_restart(ahc);
@@ -3766,8 +3780,9 @@ ahc_handle_devreset(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
/*period*/0, /*offset*/0, /*ppr_options*/0,
AHC_TRANS_CUR, /*paused*/TRUE);
- ahc_send_async(ahc, devinfo->channel, devinfo->target,
- CAM_LUN_WILDCARD, AC_SENT_BDR, NULL);
+ if (status != CAM_SEL_TIMEOUT)
+ ahc_send_async(ahc, devinfo->channel, devinfo->target,
+ CAM_LUN_WILDCARD, AC_SENT_BDR, NULL);
if (message != NULL
&& (verbose_level <= bootverbose))
@@ -5118,14 +5133,17 @@ ahc_pause_and_flushwork(struct ahc_softc *ahc)
* Give the sequencer some time to service
* any active selections.
*/
- aic_delay(200);
+ aic_delay(500);
}
ahc_intr(ahc);
ahc_pause(ahc);
paused = TRUE;
ahc_outb(ahc, SCSISEQ, ahc_inb(ahc, SCSISEQ) & ~ENSELO);
- ahc_clear_critical_section(ahc);
intstat = ahc_inb(ahc, INTSTAT);
+ if ((intstat & INT_PEND) == 0) {
+ ahc_clear_critical_section(ahc);
+ intstat = ahc_inb(ahc, INTSTAT);
+ }
} while (--maxloops
&& (intstat != 0xFF || (ahc->features & AHC_REMOVABLE) == 0)
&& ((intstat & INT_PEND) != 0
@@ -6849,6 +6867,58 @@ ahc_timeout(struct scb *scb)
}
/*
+ * Re-schedule a timeout for the passed in SCB if we determine that some
+ * other SCB is in the process of recovery or an SCB with a longer
+ * timeout is still pending. Limit our search to just "other_scb"
+ * if it is non-NULL.
+ */
+static int
+ahc_other_scb_timeout(struct ahc_softc *ahc, struct scb *scb,
+ struct scb *other_scb)
+{
+ u_int newtimeout;
+ int found;
+
+ ahc_print_path(ahc, scb);
+ printf("Other SCB Timeout%s",
+ (scb->flags & SCB_OTHERTCL_TIMEOUT) != 0
+ ? " again\n" : "\n");
+
+ newtimeout = aic_get_timeout(scb);
+ scb->flags |= SCB_OTHERTCL_TIMEOUT;
+ found = 0;
+ if (other_scb != NULL) {
+ if ((other_scb->flags
+ & (SCB_OTHERTCL_TIMEOUT|SCB_TIMEDOUT)) == 0
+ || (other_scb->flags & SCB_RECOVERY_SCB) != 0) {
+ found++;
+ newtimeout = MAX(aic_get_timeout(other_scb),
+ newtimeout);
+ }
+ } else {
+ LIST_FOREACH(other_scb, &ahc->pending_scbs, pending_links) {
+ if ((other_scb->flags
+ & (SCB_OTHERTCL_TIMEOUT|SCB_TIMEDOUT)) == 0
+ || (other_scb->flags & SCB_RECOVERY_SCB) != 0) {
+ found++;
+ newtimeout =
+ MAX(aic_get_timeout(other_scb),
+ newtimeout);
+ }
+ }
+ }
+
+ if (found != 0)
+ aic_scb_timer_reset(scb, newtimeout);
+ else {
+ ahc_print_path(ahc, scb);
+ printf("No other SCB worth waiting for...\n");
+ }
+
+ return (found != 0);
+}
+
+/*
* ahc_recover_commands determines if any of the commands that have currently
* timedout are the root cause for this timeout. Innocent commands are given
* a new timeout while we wait for the command executing on the bus to timeout.
@@ -6975,17 +7045,9 @@ bus_reset:
*/
active_scb = ahc_lookup_scb(ahc, active_scb_index);
if (active_scb != scb) {
- u_int newtimeout;
-
- ahc_print_path(ahc, scb);
- printf("Other SCB Timeout%s",
- (scb->flags & SCB_OTHERTCL_TIMEOUT) != 0
- ? " again\n" : "\n");
- scb->flags |= SCB_OTHERTCL_TIMEOUT;
- newtimeout =
- MAX(aic_get_timeout(active_scb),
- aic_get_timeout(scb));
- aic_scb_timer_reset(scb, newtimeout);
+ if (ahc_other_scb_timeout(ahc, scb,
+ active_scb) != 0)
+ goto bus_reset;
continue;
}
OpenPOWER on IntegriCloud