aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/scsi/NCR5380.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/scsi/NCR5380.c')
-rw-r--r--drivers/scsi/NCR5380.c76
1 files changed, 57 insertions, 19 deletions
diff --git a/drivers/scsi/NCR5380.c b/drivers/scsi/NCR5380.c
index d24852b2a7f3..32355b62808b 100644
--- a/drivers/scsi/NCR5380.c
+++ b/drivers/scsi/NCR5380.c
@@ -912,9 +912,9 @@ static void NCR5380_main(struct work_struct *work)
* entire unit.
*/
- if (!NCR5380_select(instance, cmd)) {
- dsprintk(NDEBUG_MAIN, instance, "main: selected target %d for command %p\n",
- scmd_id(cmd), cmd);
+ cmd = NCR5380_select(instance, cmd);
+ if (!cmd) {
+ dsprintk(NDEBUG_MAIN, instance, "main: select complete\n");
} else {
dsprintk(NDEBUG_MAIN | NDEBUG_QUEUES, instance,
"main: select failed, returning %p to queue\n", cmd);
@@ -1061,9 +1061,9 @@ static irqreturn_t NCR5380_intr(int irq, void *dev_id)
* Inputs : instance - instantiation of the 5380 driver on which this
* target lives, cmd - SCSI command to execute.
*
- * Returns : -1 if selection failed but should be retried.
- * 0 if selection failed and should not be retried.
- * 0 if selection succeeded completely (hostdata->connected == cmd).
+ * Returns cmd if selection failed but should be retried,
+ * NULL if selection failed and should not be retried, or
+ * NULL if selection succeeded (hostdata->connected == cmd).
*
* Side effects :
* If bus busy, arbitration failed, etc, NCR5380_select() will exit
@@ -1081,7 +1081,8 @@ static irqreturn_t NCR5380_intr(int irq, void *dev_id)
* Locks: caller holds hostdata lock in IRQ mode
*/
-static int NCR5380_select(struct Scsi_Host *instance, struct scsi_cmnd *cmd)
+static struct scsi_cmnd *NCR5380_select(struct Scsi_Host *instance,
+ struct scsi_cmnd *cmd)
{
struct NCR5380_hostdata *hostdata = shost_priv(instance);
unsigned char tmp[3], phase;
@@ -1092,6 +1093,15 @@ static int NCR5380_select(struct Scsi_Host *instance, struct scsi_cmnd *cmd)
NCR5380_dprint(NDEBUG_ARBITRATION, instance);
dprintk(NDEBUG_ARBITRATION, "scsi%d : starting arbitration, id = %d\n", instance->host_no, instance->this_id);
+ /*
+ * Arbitration and selection phases are slow and involve dropping the
+ * lock, so we have to watch out for EH. An exception handler may
+ * change 'selecting' to NULL. This function will then return NULL
+ * so that the caller will forget about 'cmd'. (During information
+ * transfer phases, EH may change 'connected' to NULL.)
+ */
+ hostdata->selecting = cmd;
+
/*
* Set the phase bits to 0, otherwise the NCR5380 won't drive the
* data bus during SELECTION.
@@ -1117,13 +1127,13 @@ static int NCR5380_select(struct Scsi_Host *instance, struct scsi_cmnd *cmd)
spin_lock_irq(&hostdata->lock);
if (!(NCR5380_read(MODE_REG) & MR_ARBITRATE)) {
/* Reselection interrupt */
- return -1;
+ goto out;
}
if (err < 0) {
NCR5380_write(MODE_REG, MR_BASE);
shost_printk(KERN_ERR, instance,
"select: arbitration timeout\n");
- return -1;
+ goto out;
}
spin_unlock_irq(&hostdata->lock);
@@ -1135,7 +1145,7 @@ static int NCR5380_select(struct Scsi_Host *instance, struct scsi_cmnd *cmd)
NCR5380_write(MODE_REG, MR_BASE);
dprintk(NDEBUG_ARBITRATION, "scsi%d : lost arbitration, deasserting MR_ARBITRATE\n", instance->host_no);
spin_lock_irq(&hostdata->lock);
- return -1;
+ goto out;
}
/* After/during arbitration, BSY should be asserted.
@@ -1159,7 +1169,13 @@ static int NCR5380_select(struct Scsi_Host *instance, struct scsi_cmnd *cmd)
/* NCR5380_reselect() clears MODE_REG after a reselection interrupt */
if (!(NCR5380_read(MODE_REG) & MR_ARBITRATE))
- return -1;
+ goto out;
+
+ if (!hostdata->selecting) {
+ NCR5380_write(MODE_REG, MR_BASE);
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ goto out;
+ }
dprintk(NDEBUG_ARBITRATION, "scsi%d : won arbitration\n", instance->host_no);
@@ -1232,18 +1248,21 @@ static int NCR5380_select(struct Scsi_Host *instance, struct scsi_cmnd *cmd)
if (!hostdata->connected)
NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
printk("scsi%d : reselection after won arbitration?\n", instance->host_no);
- return -1;
+ goto out;
}
if (err < 0) {
spin_lock_irq(&hostdata->lock);
NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
- cmd->result = DID_BAD_TARGET << 16;
- complete_cmd(instance, cmd);
NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
- dprintk(NDEBUG_SELECTION, "scsi%d : target did not respond within 250ms\n",
- instance->host_no);
- return 0;
+ /* Can't touch cmd if it has been reclaimed by the scsi ML */
+ if (hostdata->selecting) {
+ cmd->result = DID_BAD_TARGET << 16;
+ complete_cmd(instance, cmd);
+ dsprintk(NDEBUG_SELECTION, instance, "target did not respond within 250ms\n");
+ cmd = NULL;
+ }
+ goto out;
}
/*
@@ -1279,7 +1298,11 @@ static int NCR5380_select(struct Scsi_Host *instance, struct scsi_cmnd *cmd)
shost_printk(KERN_ERR, instance, "select: REQ timeout\n");
NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
- return -1;
+ goto out;
+ }
+ if (!hostdata->selecting) {
+ do_abort(instance);
+ goto out;
}
dprintk(NDEBUG_SELECTION, "scsi%d : target %d selected, going into MESSAGE OUT phase.\n", instance->host_no, cmd->device->id);
@@ -1300,7 +1323,13 @@ static int NCR5380_select(struct Scsi_Host *instance, struct scsi_cmnd *cmd)
initialize_SCp(cmd);
- return 0;
+ cmd = NULL;
+
+out:
+ if (!hostdata->selecting)
+ return NULL;
+ hostdata->selecting = NULL;
+ return cmd;
}
/*
@@ -2352,6 +2381,15 @@ static int NCR5380_abort(struct scsi_cmnd *cmd)
cmd->scsi_done(cmd); /* No tag or busy flag to worry about */
}
+ if (hostdata->selecting == cmd) {
+ dsprintk(NDEBUG_ABORT, instance,
+ "abort: cmd %p == selecting\n", cmd);
+ hostdata->selecting = NULL;
+ cmd->result = DID_ABORT << 16;
+ complete_cmd(instance, cmd);
+ goto out;
+ }
+
if (list_del_cmd(&hostdata->disconnected, cmd)) {
dsprintk(NDEBUG_ABORT, instance,
"abort: removed %p from disconnected list\n", cmd);