* 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);
  * 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 
  *     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;
        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.
        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);
 
                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.
 
        /* 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);
 
                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;
        }
 
        /* 
                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);
 
        initialize_SCp(cmd);
 
-       return 0;
+       cmd = NULL;
+
+out:
+       if (!hostdata->selecting)
+               return NULL;
+       hostdata->selecting = NULL;
+       return 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);
 
 #endif
        unsigned char last_message;             /* last message OUT */
        struct scsi_cmnd *connected;            /* currently connected cmnd */
+       struct scsi_cmnd *selecting;            /* cmnd to be connected */
        struct list_head unissued;              /* waiting to be issued */
        struct list_head autosense;             /* priority issue queue */
        struct list_head disconnected;          /* waiting for reconnect */
        char info[256];
        int read_overruns;                /* number of bytes to cut from a
                                           * transfer to handle chip overruns */
-       int retain_dma_intr;
        struct work_struct main_task;
 #ifdef SUPPORT_TAGS
        struct tag_alloc TagAlloc[8][8];        /* 8 targets and 8 LUNs */
 static void NCR5380_main(struct work_struct *work);
 static const char *NCR5380_info(struct Scsi_Host *instance);
 static void NCR5380_reselect(struct Scsi_Host *instance);
-static int NCR5380_select(struct Scsi_Host *instance, struct scsi_cmnd *cmd);
+static struct scsi_cmnd *NCR5380_select(struct Scsi_Host *, struct scsi_cmnd *);
 #if defined(PSEUDO_DMA) || defined(REAL_DMA) || defined(REAL_DMA_POLL)
 static int NCR5380_transfer_dma(struct Scsi_Host *instance, unsigned char *phase, int *count, unsigned char **data);
 #endif
 
            list_empty(&hostdata->unissued) &&
            list_empty(&hostdata->autosense) &&
            !hostdata->connected &&
-           !hostdata->retain_dma_intr)
+           !hostdata->selecting)
                NCR5380_release_dma_irq(instance);
 }
 
 #ifdef SUPPORT_TAGS
                        cmd_get_tag(cmd, cmd->cmnd[0] != REQUEST_SENSE);
 #endif
-                       hostdata->retain_dma_intr++;
-                       if (!NCR5380_select(instance, cmd)) {
-                               dsprintk(NDEBUG_MAIN, instance, "main: selected target %d for command %p\n",
-                                        scmd_id(cmd), cmd);
-                               hostdata->retain_dma_intr--;
+                       cmd = NCR5380_select(instance, cmd);
+                       if (!cmd) {
+                               dsprintk(NDEBUG_MAIN, instance, "main: select complete\n");
                                maybe_release_dma_irq(instance);
                        } else {
-                               hostdata->retain_dma_intr--;
                                dsprintk(NDEBUG_MAIN | NDEBUG_QUEUES, instance,
                                         "main: select failed, returning %p to queue\n", cmd);
                                requeue_cmd(instance, cmd);
  * 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
  *             cmd->result host byte set to DID_BAD_TARGET.
  */
 
-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;
        dprintk(NDEBUG_ARBITRATION, "scsi%d: starting arbitration, id = %d\n", HOSTNO,
                   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.
        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);
 
                dprintk(NDEBUG_ARBITRATION, "scsi%d: lost arbitration, deasserting MR_ARBITRATE\n",
                           HOSTNO);
                spin_lock_irq(&hostdata->lock);
-               return -1;
+               goto out;
        }
 
        /* After/during arbitration, BSY should be asserted.
 
        /* 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", HOSTNO);
 
                        NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
                printk(KERN_ERR "scsi%d: reselection after won arbitration?\n",
                       HOSTNO);
-               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", HOSTNO);
-               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;
        }
 
        /*
                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",
 
        initialize_SCp(cmd);
 
-       return 0;
+       cmd = NULL;
+
+out:
+       if (!hostdata->selecting)
+               return NULL;
+       hostdata->selecting = NULL;
+       return 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);
         * commands!
         */
 
+       hostdata->selecting = NULL;
+
        if (hostdata->connected)
                dsprintk(NDEBUG_ABORT, instance, "reset aborted a connected command\n");
        hostdata->connected = NULL;