aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/scsi/aic7xxx/aic79xx_osm.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/scsi/aic7xxx/aic79xx_osm.c')
-rw-r--r--drivers/scsi/aic7xxx/aic79xx_osm.c62
1 files changed, 51 insertions, 11 deletions
diff --git a/drivers/scsi/aic7xxx/aic79xx_osm.c b/drivers/scsi/aic7xxx/aic79xx_osm.c
index 1c8f872e2dd4..2567e29960bd 100644
--- a/drivers/scsi/aic7xxx/aic79xx_osm.c
+++ b/drivers/scsi/aic7xxx/aic79xx_osm.c
@@ -1468,6 +1468,30 @@ ahd_linux_run_command(struct ahd_softc *ahd, struct ahd_linux_device *dev,
if ((tstate->auto_negotiate & mask) != 0) {
scb->flags |= SCB_AUTO_NEGOTIATE;
scb->hscb->control |= MK_MESSAGE;
+ } else if (cmd->cmnd[0] == INQUIRY
+ && (tinfo->curr.offset != 0
+ || tinfo->curr.width != MSG_EXT_WDTR_BUS_8_BIT
+ || tinfo->curr.ppr_options != 0)
+ && (tinfo->curr.ppr_options & MSG_EXT_PPR_IU_REQ)==0) {
+ /*
+ * The SCSI spec requires inquiry
+ * commands to complete without
+ * reporting unit attention conditions.
+ * Because of this, an inquiry command
+ * that occurs just after a device is
+ * reset will result in a data phase
+ * with mismatched negotiated rates.
+ * The core already forces a renegotiation
+ * for reset events that are visible to
+ * our controller or that we initiate,
+ * but a third party device reset or a
+ * hot-plug insertion can still cause this
+ * issue. Therefore, we force a re-negotiation
+ * for every inquiry command unless we
+ * are async.
+ */
+ scb->flags |= SCB_NEGOTIATE;
+ scb->hscb->control |= MK_MESSAGE;
}
if ((dev->flags & (AHD_DEV_Q_TAGGED|AHD_DEV_Q_BASIC)) != 0) {
@@ -2058,6 +2082,7 @@ ahd_linux_queue_recovery_cmd(struct scsi_cmnd *cmd, scb_flag flag)
int paused;
int wait;
int disconnected;
+ int found;
ahd_mode_state saved_modes;
unsigned long flags;
@@ -2176,7 +2201,8 @@ ahd_linux_queue_recovery_cmd(struct scsi_cmnd *cmd, scb_flag flag)
last_phase = ahd_inb(ahd, LASTPHASE);
saved_scbptr = ahd_get_scbptr(ahd);
active_scbptr = saved_scbptr;
- if (disconnected && (ahd_inb(ahd, SEQ_FLAGS) & NOT_IDENTIFIED) == 0) {
+ if (disconnected && ((last_phase != P_BUSFREE) ||
+ (ahd_inb(ahd, SEQ_FLAGS) & NOT_IDENTIFIED) == 0)) {
struct scb *bus_scb;
bus_scb = ahd_lookup_scb(ahd, active_scbptr);
@@ -2194,28 +2220,41 @@ ahd_linux_queue_recovery_cmd(struct scsi_cmnd *cmd, scb_flag flag)
* bus or is in the disconnected state.
*/
saved_scsiid = ahd_inb(ahd, SAVED_SCSIID);
- if (last_phase != P_BUSFREE
- && (SCB_GET_TAG(pending_scb) == active_scbptr
+ if (SCB_GET_TAG(pending_scb) == active_scbptr
|| (flag == SCB_DEVICE_RESET
- && SCSIID_TARGET(ahd, saved_scsiid) == scmd_id(cmd)))) {
+ && SCSIID_TARGET(ahd, saved_scsiid) == scmd_id(cmd))) {
/*
* We're active on the bus, so assert ATN
* and hope that the target responds.
*/
pending_scb = ahd_lookup_scb(ahd, active_scbptr);
- pending_scb->flags |= SCB_RECOVERY_SCB|flag;
+ pending_scb->flags |= SCB_RECOVERY_SCB|SCB_DEVICE_RESET;
ahd_outb(ahd, MSG_OUT, HOST_MSG);
ahd_outb(ahd, SCSISIGO, last_phase|ATNO);
- scmd_printk(KERN_INFO, cmd, "Device is active, asserting ATN\n");
+ scmd_printk(KERN_INFO, cmd, "BDR message in message buffer\n");
wait = TRUE;
+ } else if (last_phase != P_BUSFREE
+ && ahd_inb(ahd, SCSIPHASE) == 0) {
+ /*
+ * SCB is not identified, there
+ * is no pending REQ, and the sequencer
+ * has not seen a busfree. Looks like
+ * a stuck connection waiting to
+ * go busfree. Reset the bus.
+ */
+ found = ahd_reset_channel(ahd, cmd->device->channel + 'A',
+ /*Initiate Reset*/TRUE);
+ printf("%s: Issued Channel %c Bus Reset. "
+ "%d SCBs aborted\n", ahd_name(ahd),
+ cmd->device->channel + 'A', found);
} else if (disconnected) {
/*
* Actually re-queue this SCB in an attempt
* to select the device before it reconnects.
*/
- pending_scb->flags |= SCB_RECOVERY_SCB|SCB_ABORT;
+ pending_scb->flags |= SCB_RECOVERY_SCB|flag;
ahd_set_scbptr(ahd, SCB_GET_TAG(pending_scb));
pending_scb->hscb->cdb_len = 0;
pending_scb->hscb->task_attribute = 0;
@@ -2296,16 +2335,17 @@ done:
timer.expires = jiffies + (5 * HZ);
timer.function = ahd_linux_sem_timeout;
add_timer(&timer);
- printf("Recovery code sleeping\n");
+ printf("%s: Recovery code sleeping\n", ahd_name(ahd));
down(&ahd->platform_data->eh_sem);
- printf("Recovery code awake\n");
+ printf("%s: Recovery code awake\n", ahd_name(ahd));
ret = del_timer_sync(&timer);
if (ret == 0) {
- printf("Timer Expired\n");
+ printf("%s: Timer Expired (active %d)\n",
+ ahd_name(ahd), dev->active);
retval = FAILED;
}
}
- ahd_unlock(ahd, &flags);
+ ahd_unlock(ahd, &flags);
return (retval);
}