]> git.neil.brown.name Git - history.git/commitdiff
aic7xxx_old: multiple updates and fixes, driver ported to scsi
authorDoug Ledford <dledford@aladin.rdu.redhat.com>
Wed, 6 Nov 2002 11:40:20 +0000 (06:40 -0500)
committerDoug Ledford <dledford@aladin.rdu.redhat.com>
Wed, 6 Nov 2002 11:40:20 +0000 (06:40 -0500)
mid-layer new error handling scheme

drivers/scsi/aic7xxx_old.c
drivers/scsi/aic7xxx_old/aic7xxx.h
drivers/scsi/aic7xxx_old/aic7xxx_proc.c

index 0048af0cf7d8111fa0dfdd17a7c4e36dccf79834..0c48b35eda9e1bf28a3928fa960ec62351b5abbd 100644 (file)
 
 #include <linux/config.h>        /* for CONFIG_PCI */
 
-#define AIC7XXX_C_VERSION  "5.2.5"
+#define AIC7XXX_C_VERSION  "5.2.6"
 
 #define NUMBER(arr)     (sizeof(arr) / sizeof(arr[0]))
 #define MIN(a,b)        (((a) < (b)) ? (a) : (b))
 #endif
 
 /*
- * You can try raising me if tagged queueing is enabled, or lowering
- * me if you only have 4 SCBs.
+ * You can try raising me for better performance or lowering me if you have
+ * flaky devices that go off the scsi bus when hit with too many tagged
+ * commands (like some IBM SCSI-3 LVD drives).
  */
 #define AIC7XXX_CMDS_PER_DEVICE 32
 
-/*
- * *** Determining commands per LUN ***
- * 
- * When AIC7XXX_CMDS_PER_DEVICE is not defined, the driver will use its
- * own algorithm to determine the commands/LUN.  If SCB paging is
- * enabled, which is always now, the default is 8 commands per lun
- * that indicates it supports tagged queueing.  All non-tagged devices
- * use an internal queue depth of 3, with no more than one of those
- * three commands active at one time.
- */
-
 typedef struct
 {
   unsigned char tag_commands[16];   /* Allow for wide/twin adapters. */
 } adapter_tag_info_t;
 
 /*
- * Make a define that will tell the driver not to use tagged queueing
- * by default.
+ * Make a define that will tell the driver not to the default tag depth
+ * everywhere.
  */
 #define DEFAULT_TAG_COMMANDS {0, 0, 0, 0, 0, 0, 0, 0,\
                               0, 0, 0, 0, 0, 0, 0, 0}
@@ -878,7 +868,6 @@ struct aic_dev_data {
   volatile unsigned short  temp_q_depth;
   unsigned short           max_q_depth;
   volatile unsigned char   active_cmds;
-  unsigned char            cmds_sent;
   /*
    * Statistics Kept:
    *
@@ -891,6 +880,9 @@ struct aic_dev_data {
    */
   long w_total;                          /* total writes */
   long r_total;                          /* total reads */
+  long barrier_total;                   /* total num of REQ_BARRIER commands */
+  long ordered_total;                   /* How many REQ_BARRIER commands we
+                                           used ordered tags to satisfy */
   long w_bins[6];                       /* binned write */
   long r_bins[6];                       /* binned reads */
   transinfo_type       cur;
@@ -998,7 +990,7 @@ struct aic7xxx_host {
   unsigned short           sc_size;
   struct aic7xxx_host     *next;             /* allow for multiple IRQs */
   struct Scsi_Host        *host;             /* pointer to scsi host */
-  struct list_head        aic_devs;         /* all aic devs on host */
+  struct list_head        aic_devs;         /* all aic_dev structs on host */
   int                      host_no;          /* SCSI host number */
   unsigned long            mbase;            /* I/O memory address */
   ahc_chip                 chip;             /* chip type */
@@ -1066,6 +1058,10 @@ static struct aic7xxx_syncrate {
  *       rule are noted in the comments.
  */
 
+/*
+ * Use this as the default queue depth when setting tagged queueing on.
+ */
+static unsigned int aic7xxx_default_queue_depth = AIC7XXX_CMDS_PER_DEVICE;
 
 /*
  * Skip the scsi bus reset.  Non 0 make us skip the reset at startup.  This
@@ -1369,6 +1365,7 @@ aic7xxx_setup(char *s)
     { "pci_parity", &aic7xxx_pci_parity },
     { "dump_card", &aic7xxx_dump_card },
     { "dump_sequencer", &aic7xxx_dump_sequencer },
+    { "default_queue_depth", &aic7xxx_default_queue_depth },
     { "scbram", &aic7xxx_scbram },
     { "seltime", &aic7xxx_seltime },
     { "tag_info",    NULL }
@@ -2636,6 +2633,7 @@ aic7xxx_allocate_scb(struct aic7xxx_host *p)
 static void
 aic7xxx_queue_cmd_complete(struct aic7xxx_host *p, Scsi_Cmnd *cmd)
 {
+  aic7xxx_position(cmd) = SCB_LIST_NULL;
   cmd->host_scribble = (char *)p->completeq.head;
   p->completeq.head = cmd;
 }
@@ -2869,6 +2867,12 @@ aic7xxx_done(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
       aic_dev->r_total++;
       ptr = aic_dev->r_bins;
     }
+    if(cmd->device->simple_tags && cmd->request->flags & REQ_BARRIER)
+    {
+      aic_dev->barrier_total++;
+      if(scb->tag_action == MSG_ORDERED_Q_TAG)
+        aic_dev->ordered_total++;
+    }
     x = scb->sg_length;
     x >>= 10;
     for(i=0; i<6; i++)
@@ -3130,23 +3134,41 @@ aic7xxx_reset_device(struct aic7xxx_host *p, int target, int channel,
 {
   struct aic7xxx_scb *scbp, *prev_scbp;
   struct scsi_device *sd;
-  unsigned char active_scb, tcl;
+  unsigned char active_scb, tcl, scb_tag;
   int i = 0, init_lists = FALSE;
-  struct list_head *list_item, *list_head;
-  struct aic_dev_data *aic_dev;
+  struct aic_dev_data *aic_dev, *remove_aic_dev = NULL;
 
   /*
    * Restore this when we're done
    */
   active_scb = aic_inb(p, SCBPTR);
+  /*
+   * If we are still scanning devices, this device may not be linked into
+   * our host controller yet.  So, we grab the temporary aic_dev that we
+   * allocate during the device scan (by getting device->hostdata for the
+   * active device) and if the list struct in the aic_dev is empty, we
+   * add it to p->aic_devs.  Then at the end of the function, if we added
+   * the aic_dev to our host struct so our loop would work, then we just
+   * remove it.
+   */
+  scb_tag = aic_inb(p, SCB_TAG);
+  if(scb_tag < p->scb_data->numscbs)
+  {
+    scbp = p->scb_data->scb_array[scb_tag];
+    remove_aic_dev = (struct aic_dev_data *)scbp->cmd->device->hostdata;
+    if(list_empty(&remove_aic_dev->list))
+      list_add_tail(&remove_aic_dev->list, &p->aic_devs);
+    else
+      remove_aic_dev = NULL;
+  }
 
   if (aic7xxx_verbose & (VERBOSE_RESET_PROCESS | VERBOSE_ABORT_PROCESS))
   {
-    printk(INFO_LEAD "Reset device, active_scb %d\n",
-         p->host_no, channel, target, lun, active_scb);
-    printk(INFO_LEAD "Current scb_tag %d, SEQADDR 0x%x, LASTPHASE "
+    printk(INFO_LEAD "Reset device, hardware_scb %d, aic_dev %p,\n",
+         p->host_no, channel, target, lun, active_scb, remove_aic_dev);
+    printk(INFO_LEAD "Current scb %d, SEQADDR 0x%x, LASTPHASE "
            "0x%x\n",
-         p->host_no, channel, target, lun, aic_inb(p, SCB_TAG),
+         p->host_no, channel, target, lun, scb_tag,
          aic_inb(p, SEQADDR0) | (aic_inb(p, SEQADDR1) << 8),
          aic_inb(p, LASTPHASE));
     printk(INFO_LEAD "SG_CACHEPTR 0x%x, SG_COUNT %d, SCSISIGI 0x%x\n",
@@ -3157,13 +3179,15 @@ aic7xxx_reset_device(struct aic7xxx_host *p, int target, int channel,
          p->host_no, channel, target, lun, aic_inb(p, SSTAT0),
          aic_inb(p, SSTAT1), aic_inb(p, SSTAT2));
   }
+
   /*
    * Deal with the busy target and linked next issues.
    */
-  list_head = &p->aic_devs;
-  list_for_each(list_item, list_head)
+  list_for_each_entry(aic_dev, &p->aic_devs, list)
   {
-    aic_dev = list_entry(list_item, struct aic_dev_data, list);
+    if (aic7xxx_verbose & (VERBOSE_RESET_PROCESS | VERBOSE_ABORT_PROCESS))
+      printk(INFO_LEAD "processing aic_dev %p\n", p->host_no, channel, target,
+                   lun, aic_dev);
     sd = aic_dev->SDptr;
 
     if((target != ALL_TARGETS && target != sd->id) ||
@@ -3205,6 +3229,8 @@ aic7xxx_reset_device(struct aic7xxx_host *p, int target, int channel,
       }
     }
   }
+  if (remove_aic_dev)
+    list_del(&remove_aic_dev->list);
 
   if (aic7xxx_verbose & (VERBOSE_ABORT_PROCESS | VERBOSE_RESET_PROCESS))
     printk(INFO_LEAD "Cleaning QINFIFO.\n", p->host_no, channel, target, lun );
@@ -6526,22 +6552,19 @@ do_aic7xxx_isr(int irq, void *dev_id, struct pt_regs *regs)
 
 /*+F*************************************************************************
  * Function:
- *   aic7xxx_init_aic_dev
+ *   aic7xxx_init_transinfo
  *
  * Description:
- *   Set up the initial aic_dev values from the BIOS settings and data
- *   we got back from INQUIRY commands.
+ *   Set up the initial aic_dev values from the BIOS settings and from
+ *   INQUIRY results
  *-F*************************************************************************/
 static void
-aic7xxx_init_aic_dev(struct aic7xxx_host *p, struct aic_dev_data *aic_dev)
+aic7xxx_init_transinfo(struct aic7xxx_host *p, struct aic_dev_data *aic_dev)
 {
-  struct scsi_device *sdpnt = aic_dev->SDptr;
+  Scsi_Device *sdpnt = aic_dev->SDptr;
   unsigned char tindex;
 
   tindex = sdpnt->id | (sdpnt->channel << 3);
-
-  scbq_init(&aic_dev->delayed_scbs);
-
   if (!(aic_dev->flags & DEVICE_DTR_SCANNED))
   {
     aic_dev->flags |= DEVICE_DTR_SCANNED;
@@ -6597,6 +6620,31 @@ aic7xxx_init_aic_dev(struct aic7xxx_host *p, struct aic_dev_data *aic_dev)
   }
 }
 
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_alloc_aic_dev
+ *
+ * Description:
+ *   Set up the initial aic_dev struct pointers
+ *-F*************************************************************************/
+static struct aic_dev_data *
+aic7xxx_alloc_aic_dev(struct aic7xxx_host *p, Scsi_Device *SDptr)
+{
+  struct aic_dev_data *aic_dev;
+
+  aic_dev = kmalloc(sizeof(struct aic_dev_data), GFP_ATOMIC | GFP_KERNEL);
+  if(!aic_dev)
+    return aic_dev;
+  memset(aic_dev, 0, sizeof(struct aic_dev_data));
+  SDptr->hostdata = aic_dev;
+  aic_dev->SDptr = SDptr;
+  aic_dev->max_q_depth = 1;
+  aic_dev->temp_q_depth = 1;
+  scbq_init(&aic_dev->delayed_scbs);
+  INIT_LIST_HEAD(&aic_dev->list);
+  return aic_dev;
+}
+
 /*+F*************************************************************************
  * Function:
  *   aic7xxx_device_queue_depth
@@ -6605,7 +6653,11 @@ aic7xxx_init_aic_dev(struct aic7xxx_host *p, struct aic_dev_data *aic_dev)
  *   Determines the queue depth for a given device.  There are two ways
  *   a queue depth can be obtained for a tagged queueing device.  One
  *   way is the default queue depth which is determined by whether
- *   AIC7XXX_CMDS_PER_DEVICE is defined.  If it is defined, then it is used
+ *   aic7xxx_default_queue_depth.  The other is by the aic7xxx_tag_info
+ *   array.
+ *
+ *   If tagged queueing isn't supported on the device, then we set the
+ *   depth to p->host->hostt->cmd_per_lun for internal driver queueing.
  *   as the default queue depth.  Otherwise, we use either 4 or 8 as the
  *   default queue depth (dependent on the number of hardware SCBs).
  *   The other way we determine queue depth is through the use of the
@@ -6617,7 +6669,7 @@ aic7xxx_init_aic_dev(struct aic7xxx_host *p, struct aic_dev_data *aic_dev)
 static void
 aic7xxx_device_queue_depth(struct aic7xxx_host *p, Scsi_Device *device)
 {
-  int default_depth = p->host->hostt->cmd_per_lun;
+  int tag_enabled = FALSE;
   struct aic_dev_data *aic_dev = device->hostdata;
   unsigned char tindex;
 
@@ -6626,14 +6678,9 @@ aic7xxx_device_queue_depth(struct aic7xxx_host *p, Scsi_Device *device)
   if (device->simple_tags)
     return; // We've already enabled this device
 
-  aic_dev->temp_q_depth = 1;
-  aic_dev->max_q_depth = 1;
-
   if (device->tagged_supported)
   {
-    int tag_enabled = TRUE;
-
-    default_depth = AIC7XXX_CMDS_PER_DEVICE;
+    tag_enabled = TRUE;
 
     if (!(p->discenable & (1 << tindex)))
     {
@@ -6641,6 +6688,7 @@ aic7xxx_device_queue_depth(struct aic7xxx_host *p, Scsi_Device *device)
         printk(INFO_LEAD "Disconnection disabled, unable to "
              "enable tagged queueing.\n",
              p->host_no, device->channel, device->id, device->lun);
+      tag_enabled = FALSE;
     }
     else
     {
@@ -6655,7 +6703,8 @@ aic7xxx_device_queue_depth(struct aic7xxx_host *p, Scsi_Device *device)
                            " the aic7xxx.c source file.\n");
           print_warning = FALSE;
         }
-        aic_dev->max_q_depth = aic_dev->temp_q_depth = default_depth;
+        aic_dev->max_q_depth = aic_dev->temp_q_depth =
+               aic7xxx_default_queue_depth;
       }
       else
       {
@@ -6666,7 +6715,8 @@ aic7xxx_device_queue_depth(struct aic7xxx_host *p, Scsi_Device *device)
         }
         else if (aic7xxx_tag_info[p->instance].tag_commands[tindex] == 0)
         {
-          aic_dev->max_q_depth = aic_dev->temp_q_depth = default_depth;
+          aic_dev->max_q_depth = aic_dev->temp_q_depth =
+                 aic7xxx_default_queue_depth;
         }
         else
         {
@@ -6674,18 +6724,28 @@ aic7xxx_device_queue_depth(struct aic7xxx_host *p, Scsi_Device *device)
             aic7xxx_tag_info[p->instance].tag_commands[tindex];
         }
       }
-      if (tag_enabled)
-      {
-        if (aic7xxx_verbose & VERBOSE_NEGOTIATION2)
-        {
-              printk(INFO_LEAD "Enabled tagged queuing, queue depth %d.\n",
-                p->host_no, device->channel, device->id,
-                device->lun, aic_dev->max_q_depth);
-        }
-       scsi_adjust_queue_depth(device, MSG_ORDERED_TAG, aic_dev->max_q_depth);
-      }
     }
   }
+  if (tag_enabled)
+  {
+    if (aic7xxx_verbose & VERBOSE_NEGOTIATION2)
+    {
+          printk(INFO_LEAD "Tagged queuing enabled, queue depth %d.\n",
+            p->host_no, device->channel, device->id,
+            device->lun, aic_dev->max_q_depth);
+    }
+    scsi_adjust_queue_depth(device, MSG_ORDERED_TAG, aic_dev->max_q_depth);
+  }
+  else
+  {
+    if (aic7xxx_verbose & VERBOSE_NEGOTIATION2)
+    {
+          printk(INFO_LEAD "Tagged queuing disabled, queue depth %d.\n",
+            p->host_no, device->channel, device->id,
+            device->lun, device->host->cmd_per_lun);
+    }
+    scsi_adjust_queue_depth(device, 0, device->host->cmd_per_lun);
+  }
   return;
 }
 
@@ -6722,28 +6782,22 @@ aic7xxx_slave_attach(Scsi_Device *sdpnt)
   struct aic7xxx_host *p = (struct aic7xxx_host *) sdpnt->host->hostdata;
   struct aic_dev_data *aic_dev;
   int scbnum;
-  struct list_head *list_ptr, *list_head;
 
-  if(!sdpnt->hostdata) {
-    sdpnt->hostdata = kmalloc(sizeof(struct aic_dev_data), GFP_ATOMIC);
-    if(!sdpnt->hostdata)
-      return 1;
-    memset(sdpnt->hostdata, 0, sizeof(struct aic_dev_data));
-  }
+  aic_dev = (struct aic_dev_data *)sdpnt->hostdata;
 
-  aic_dev = sdpnt->hostdata;
-  aic_dev->SDptr = sdpnt;
-
-  aic7xxx_init_aic_dev(p, aic_dev);
+  if(!aic_dev) {
+    aic_dev = aic7xxx_alloc_aic_dev(p, sdpnt);
+  }
+  if(!aic_dev)
+    return 1;
   
-  list_add(&aic_dev->list, &p->aic_devs);
-
+  aic7xxx_init_transinfo(p, aic_dev);
   aic7xxx_device_queue_depth(p, sdpnt);
+  if(list_empty(&aic_dev->list))
+    list_add_tail(&aic_dev->list, &p->aic_devs);
 
   scbnum = 0;
-  list_head = &p->aic_devs;
-  list_for_each(list_ptr, list_head) {
-    aic_dev = list_entry(list_ptr, struct aic_dev_data, list);
+  list_for_each_entry(aic_dev, &p->aic_devs, list) {
     scbnum += aic_dev->max_q_depth;
   }
   while (scbnum > p->scb_data->numscbs)
@@ -7878,6 +7932,7 @@ aic7xxx_register(Scsi_Host_Template *template, struct aic7xxx_host *p,
   p->completeq.tail = NULL;
   scbq_init(&p->scb_data->free_scbs);
   scbq_init(&p->waiting_scbs);
+  INIT_LIST_HEAD(&p->aic_devs);
 
   /*
    * We currently have no commands of any type
@@ -10117,30 +10172,6 @@ aic7xxx_detect(Scsi_Host_Template *template)
   return (found);
 }
 
-#ifdef AIC7XXX_VERBOSE_DEBUGGING
-/*+F*************************************************************************
- * Function:
- *   aic7xxx_print_scb
- *
- * Description:
- *   Dump the byte codes for an about to be sent SCB.
- *-F*************************************************************************/
-static void
-aic7xxx_print_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
-{
-  int i;
-  unsigned char *x;  
-
-  x = (unsigned char *)&scb->hscb->control;
-
-  for(i=0; i<32; i++)
-  {
-    printk("%02x ", x[i]);
-  }
-  printk("\n");
-}
-#endif
-
 /*+F*************************************************************************
  * Function:
  *   aic7xxx_buildscb
@@ -10157,6 +10188,7 @@ aic7xxx_buildscb(struct aic7xxx_host *p, Scsi_Cmnd *cmd,
   struct aic_dev_data *aic_dev = cmd->device->hostdata;
   struct scsi_device *sdptr = cmd->device;
   unsigned char tindex = TARGET_INDEX(cmd);
+  struct request *req = cmd->request;
 
   mask = (0x01 << tindex);
   hscb = scb->hscb;
@@ -10171,15 +10203,16 @@ aic7xxx_buildscb(struct aic7xxx_host *p, Scsi_Cmnd *cmd,
   if (p->discenable & mask)
   {
     hscb->control |= DISCENB;
-    if ( (sdptr->simple_tags) &&
-         (cmd->cmnd[0] != TEST_UNIT_READY) )
+    /* We always force TEST_UNIT_READY to untagged */
+    if (cmd->cmnd[0] != TEST_UNIT_READY && sdptr->simple_tags)
     {
-      aic_dev->cmds_sent++;
-      if (aic_dev->cmds_sent >= 254 && sdptr->ordered_tags)
+      if (req->flags & REQ_BARRIER)
       {
-        aic_dev->cmds_sent = 0;
-        hscb->control |= MSG_ORDERED_Q_TAG;
-        scb->tag_action = MSG_ORDERED_Q_TAG;
+       if(sdptr->ordered_tags)
+       {
+          hscb->control |= MSG_ORDERED_Q_TAG;
+          scb->tag_action = MSG_ORDERED_Q_TAG;
+       }
       }
       else
       {
@@ -10306,19 +10339,25 @@ aic7xxx_queue(Scsi_Cmnd *cmd, void (*fn)(Scsi_Cmnd *))
   struct aic7xxx_host *p;
   struct aic7xxx_scb *scb;
   struct aic_dev_data *aic_dev;
-#ifdef AIC7XXX_VERBOSE_DEBUGGING
-  int tindex = TARGET_INDEX(cmd);
-#endif
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,95)
-  unsigned long cpu_flags = 0;
-#endif
 
   p = (struct aic7xxx_host *) cmd->host->hostdata;
+
+  aic_dev = cmd->device->hostdata;  
+  if(!aic_dev) {
+    aic_dev = aic7xxx_alloc_aic_dev(p, cmd->device);
+    if(!aic_dev)
+      return 1;
+  }
+  else
+  {
+    aic_dev = AIC_DEV(cmd);
+  }
+
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
   /*
    * Check to see if channel was scanned.
    */
   
-#ifdef AIC7XXX_VERBOSE_DEBUGGING
   if (!(p->flags & AHC_A_SCANNED) && (cmd->channel == 0))
   {
     if (aic7xxx_verbose & VERBOSE_PROBE2)
@@ -10337,25 +10376,15 @@ aic7xxx_queue(Scsi_Cmnd *cmd, void (*fn)(Scsi_Cmnd *))
     }
   }
 
-  if (p->dev_active_cmds[tindex] > (cmd->device->queue_depth + 1))
+  if (aic_dev->active_cmds > aic_dev->max_q_depth)
   {
     printk(WARN_LEAD "Commands queued exceeds queue "
            "depth, active=%d\n",
            p->host_no, CTL_OF_CMD(cmd), 
-           p->dev_active_cmds[tindex]);
-    if ( p->dev_active_cmds[tindex] > 220 )
-      p->dev_active_cmds[tindex] = 0;
+           aic_dev->active_cmds);
   }
 #endif
 
-  if(!cmd->device->hostdata) {
-    aic_dev = kmalloc(sizeof(struct aic_dev_data), GFP_ATOMIC);
-    if(!aic_dev)
-      return 1;
-    memset(aic_dev, 0, sizeof(struct aic_dev_data));
-    cmd->device->hostdata = aic_dev;
-  }
-
   scb = scbq_remove_head(&p->scb_data->free_scbs);
   if (scb == NULL)
   {
@@ -10369,13 +10398,13 @@ aic7xxx_queue(Scsi_Cmnd *cmd, void (*fn)(Scsi_Cmnd *))
     }
   }
   scb->cmd = cmd;
-  aic7xxx_position(cmd) = scb->hscb->tag;
 
   /*
    * Make sure the Scsi_Cmnd pointer is saved, the struct it points to
    * is set up properly, and the parity error flag is reset, then send
    * the SCB to the sequencer and watch the fun begin.
    */
+  aic7xxx_position(cmd) = scb->hscb->tag;
   cmd->scsi_done = fn;
   cmd->result = DID_OK;
   memset(cmd->sense_buffer, 0, sizeof(cmd->sense_buffer));
@@ -10392,10 +10421,7 @@ aic7xxx_queue(Scsi_Cmnd *cmd, void (*fn)(Scsi_Cmnd *))
   scb->flags |= SCB_ACTIVE | SCB_WAITINGQ;
 
   scbq_insert_tail(&p->waiting_scbs, scb);
-  if ( (p->flags & (AHC_IN_ISR | AHC_IN_ABORT | AHC_IN_RESET)) == 0)
-  {
-    aic7xxx_run_waiting_queues(p);
-  }
+  aic7xxx_run_waiting_queues(p);
   return (0);
 }
 
@@ -10410,21 +10436,40 @@ aic7xxx_queue(Scsi_Cmnd *cmd, void (*fn)(Scsi_Cmnd *))
  *   aborted, then we will reset the channel and have all devices renegotiate.
  *   Returns an enumerated type that indicates the status of the operation.
  *-F*************************************************************************/
-static int
-aic7xxx_bus_device_reset(struct aic7xxx_host *p, Scsi_Cmnd *cmd)
+int
+aic7xxx_bus_device_reset(Scsi_Cmnd *cmd)
 {
+  struct aic7xxx_host  *p;
   struct aic7xxx_scb   *scb;
   struct aic7xxx_hwscb *hscb;
-  int result = -1;
   int channel;
   unsigned char saved_scbptr, lastphase;
   unsigned char hscb_index;
   int disconnected;
-  struct aic_dev_data *aic_dev = AIC_DEV(cmd);
+  struct aic_dev_data *aic_dev;
+
+  if(cmd == NULL)
+  {
+    printk(KERN_ERR "aic7xxx_bus_device_reset: called with NULL cmd!\n");
+    return FAILED;
+  }
+  p = (struct aic7xxx_host *)cmd->host->hostdata;
+  aic_dev = AIC_DEV(cmd);
+  if(aic7xxx_position(cmd) < p->scb_data->numscbs)
+    scb = (p->scb_data->scb_array[aic7xxx_position(cmd)]);
+  else
+    return FAILED;
 
-  scb = (p->scb_data->scb_array[aic7xxx_position(cmd)]);
   hscb = scb->hscb;
 
+  aic7xxx_isr(p->irq, (void *)p, NULL);
+  aic7xxx_done_cmds_complete(p);
+  /* If the command was already complete or just completed, then we didn't
+   * do a reset, return FAILED */
+  if(!scb->flags & SCB_ACTIVE)
+    return FAILED;
+
+  pause_sequencer(p);
   lastphase = aic_inb(p, LASTPHASE);
   if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
   {
@@ -10494,101 +10539,95 @@ aic7xxx_bus_device_reset(struct aic7xxx_host *p, Scsi_Cmnd *cmd)
       printk(WARN_LEAD "Invalid SCB ID %d is active, "
              "SCB flags = 0x%x.\n", p->host_no,
             CTL_OF_CMD(cmd), scb->hscb->tag, scb->flags);
-      return(SCSI_RESET_ERROR);
+      unpause_sequencer(p, FALSE);
+      return FAILED;
     }
     if (scb->hscb->tag == aic_inb(p, SCB_TAG))
     { 
-      if ( (lastphase != P_MESGOUT) && (lastphase != P_MESGIN) )
-      {
-        if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
-          printk(INFO_LEAD "Device reset message in "
-                "message buffer\n", p->host_no, CTL_OF_SCB(scb));
-        scb->flags |= SCB_RESET | SCB_DEVICE_RESET;
-        aic7xxx_error(cmd) = DID_RESET;
-        aic_dev->flags |= BUS_DEVICE_RESET_PENDING;
-        /* Send the abort message to the active SCB. */
-        aic_outb(p, HOST_MSG, MSG_OUT);
-        aic_outb(p, lastphase | ATNO, SCSISIGO);
-        return(SCSI_RESET_PENDING);
-      }
-      else
+      if ( (lastphase == P_MESGOUT) || (lastphase == P_MESGIN) )
       {
-        /* We want to send out the message, but it could screw an already */
-        /* in place and being used message.  Instead, we return an error  */
-        /* to try and start the bus reset phase since this command is     */
-        /* probably hung (aborts failed, and now reset is failing).  We   */
-        /* also make sure to set BUS_DEVICE_RESET_PENDING so we won't try */
-        /* any more on this device, but instead will escalate to a bus or */
-        /* host reset (additionally, we won't try to abort any more).     */
         printk(WARN_LEAD "Device reset, Message buffer "
                 "in use\n", p->host_no, CTL_OF_SCB(scb));
-        scb->flags |= SCB_RESET | SCB_DEVICE_RESET;
-        aic7xxx_error(cmd) = DID_RESET;
-        aic_dev->flags |= BUS_DEVICE_RESET_PENDING;
-        return(SCSI_RESET_ERROR);
-      }
+        unpause_sequencer(p, FALSE);
+       return FAILED;
+      }
+       
+      if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
+        printk(INFO_LEAD "Device reset message in "
+              "message buffer\n", p->host_no, CTL_OF_SCB(scb));
+      scb->flags |= SCB_RESET | SCB_DEVICE_RESET;
+      aic7xxx_error(cmd) = DID_RESET;
+      aic_dev->flags |= BUS_DEVICE_RESET_PENDING;
+      /* Send the abort message to the active SCB. */
+      aic_outb(p, HOST_MSG, MSG_OUT);
+      aic_outb(p, lastphase | ATNO, SCSISIGO);
+      unpause_sequencer(p, FALSE);
+      spin_unlock_irq(p->host->host_lock);
+      scsi_sleep(HZ);
+      spin_lock_irq(p->host->host_lock);
+      if(aic_dev->flags & BUS_DEVICE_RESET_PENDING)
+        return FAILED;
+      else
+        return SUCCESS;
     }
   } /* if (last_phase != P_BUSFREE).....indicates we are idle and can work */
-  hscb_index = aic7xxx_find_scb(p, scb);
-  if (hscb_index == SCB_LIST_NULL)
-  {
-    disconnected = (aic7xxx_scb_on_qoutfifo(p, scb)) ? FALSE : TRUE;
-  }
-  else
-  {
-    aic_outb(p, hscb_index, SCBPTR);
-    if (aic_inb(p, SCB_CONTROL) & DISCONNECTED)
-    {
-      disconnected = TRUE;
-    }
-  }
-  if (disconnected)
+  /*
+   * Simply set the MK_MESSAGE flag and the SEQINT handler will do
+   * the rest on a reconnect/connect.
+   */
+  scb->hscb->control |= MK_MESSAGE;
+  scb->flags |= SCB_RESET | SCB_DEVICE_RESET;
+  aic_dev->flags |= BUS_DEVICE_RESET_PENDING;
+  /*
+   * Check to see if the command is on the qinfifo.  If it is, then we will
+   * not need to queue the command again since the card should start it soon
+   */
+  if (aic7xxx_search_qinfifo(p, cmd->channel, cmd->target, cmd->lun, hscb->tag,
+                         0, TRUE, NULL) == 0)
   {
-        /*
-         * Simply set the MK_MESSAGE flag and the SEQINT handler will do
-         * the rest on a reconnect.
-         */
-    scb->hscb->control |= MK_MESSAGE;
-    scb->flags |= SCB_RESET | SCB_DEVICE_RESET;
-    aic_dev->flags |= BUS_DEVICE_RESET_PENDING;
-    if (hscb_index != SCB_LIST_NULL)
+    disconnected = TRUE;
+    if ((hscb_index = aic7xxx_find_scb(p, scb)) != SCB_LIST_NULL)
     {
       unsigned char scb_control;
 
       aic_outb(p, hscb_index, SCBPTR);
       scb_control = aic_inb(p, SCB_CONTROL);
+      /*
+       * If the DISCONNECTED bit is not set in SCB_CONTROL, then we are
+       * actually on the waiting list, not disconnected, and we don't
+       * need to requeue the command.
+       */
+      disconnected = (scb_control & DISCONNECTED);
       aic_outb(p, scb_control | MK_MESSAGE, SCB_CONTROL);
     }
-        /*
-         * Actually requeue this SCB in case we can select the
-         * device before it reconnects.  If the transaction we
-         * want to abort is not tagged, then this will be the only
-         * outstanding command and we can simply shove it on the
-         * qoutfifo and be done.  If it is tagged, then it goes right
-         * in with all the others, no problem :)  We need to add it
-         * to the qinfifo and let the sequencer know it is there.
-         * Now, the only problem left to deal with is, *IF* this
-         * command completes, in spite of the MK_MESSAGE bit in the
-         * control byte, then we need to pick that up in the interrupt
-         * routine and clean things up.  This *shouldn't* ever happen.
-         */
-    if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
-      printk(INFO_LEAD "Queueing device reset "
-           "command.\n", p->host_no, CTL_OF_SCB(scb));
-    p->qinfifo[p->qinfifonext++] = scb->hscb->tag;
-    if (p->features & AHC_QUEUE_REGS)
-      aic_outb(p, p->qinfifonext, HNSCB_QOFF);
-    else
-      aic_outb(p, p->qinfifonext, KERNEL_QINPOS);
-    scb->flags |= SCB_QUEUED_ABORT;
-    result = SCSI_RESET_PENDING;
-  }
-  else if (result == -1)
-  {
-    result = SCSI_RESET_ERROR;
+    if (disconnected)
+    {
+      /*
+       * Actually requeue this SCB in case we can select the
+       * device before it reconnects.  This can result in the command
+       * being on the qinfifo twice, but we don't care because it will
+       * all get cleaned up if/when the reset takes place.
+       */
+      if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
+        printk(INFO_LEAD "Queueing device reset command.\n", p->host_no,
+                     CTL_OF_SCB(scb));
+      p->qinfifo[p->qinfifonext++] = scb->hscb->tag;
+      if (p->features & AHC_QUEUE_REGS)
+        aic_outb(p, p->qinfifonext, HNSCB_QOFF);
+      else
+        aic_outb(p, p->qinfifonext, KERNEL_QINPOS);
+      scb->flags |= SCB_QUEUED_ABORT;
+    }
   }
   aic_outb(p, saved_scbptr, SCBPTR);
-  return (result);
+  unpause_sequencer(p, FALSE);
+  spin_unlock_irq(p->host->host_lock);
+  scsi_sleep(HZ/4);
+  spin_lock_irq(p->host->host_lock);
+  if(aic_dev->flags & BUS_DEVICE_RESET_PENDING)
+    return FAILED;
+  else
+    return SUCCESS;
 }
 
 
@@ -10630,12 +10669,30 @@ aic7xxx_abort(Scsi_Cmnd *cmd)
 {
   struct aic7xxx_scb  *scb = NULL;
   struct aic7xxx_host *p;
-  struct aic_dev_data *aic_dev = AIC_DEV(cmd);
-  int    result, found=0;
-  unsigned char tmp_char, saved_hscbptr, next_hscbptr, prev_hscbptr;
+  int    found=0, disconnected;
+  unsigned char saved_hscbptr, hscbptr, scb_control;
+  struct aic_dev_data *aic_dev;
 
-  p = (struct aic7xxx_host *) cmd->host->hostdata;
-  scb = (p->scb_data->scb_array[aic7xxx_position(cmd)]);
+  if(cmd == NULL)
+  {
+    printk(KERN_ERR "aic7xxx_abort: called with NULL cmd!\n");
+    return FAILED;
+  }
+  p = (struct aic7xxx_host *)cmd->host->hostdata;
+  aic_dev = AIC_DEV(cmd);
+  if(aic7xxx_position(cmd) < p->scb_data->numscbs)
+    scb = (p->scb_data->scb_array[aic7xxx_position(cmd)]);
+  else
+    return FAILED;
+
+  aic7xxx_isr(p->irq, (void *)p, NULL);
+  aic7xxx_done_cmds_complete(p);
+  /* If the command was already complete or just completed, then we didn't
+   * do a reset, return FAILED */
+  if(!scb->flags & SCB_ACTIVE)
+    return FAILED;
+
+  pause_sequencer(p);
 
   /*
    * I added a new config option to the driver: "panic_on_abort" that will
@@ -10648,92 +10705,6 @@ aic7xxx_abort(Scsi_Cmnd *cmd)
   if (aic7xxx_panic_on_abort)
     aic7xxx_panic_abort(p, cmd);
 
-/*
- *  Run the isr to grab any command in the QOUTFIFO and any other misc.
- *  assundry tasks.  This should also set up the bh handler if there is
- *  anything to be done, but it won't run until we are done here since
- *  we are following a straight code path without entering the scheduler
- *  code.
- */
-  pause_sequencer(p);
-  while ( (aic_inb(p, INTSTAT) & INT_PEND) && !(p->flags & AHC_IN_ISR))
-  {
-    aic7xxx_isr(p->irq, p, (void *)NULL);
-    pause_sequencer(p);
-  }
-  aic7xxx_done_cmds_complete(p);
-
-  if (scb == NULL)
-  {
-    if (aic7xxx_verbose & VERBOSE_ABORT_MID)
-      printk(INFO_LEAD "Abort called with bogus Scsi_Cmnd "
-        "pointer.\n", p->host_no, CTL_OF_CMD(cmd));
-    unpause_sequencer(p, FALSE);
-    return(SCSI_ABORT_NOT_RUNNING);
-  }
-  if (scb->cmd != cmd)  /*  Hmmm...either this SCB is currently free with a */
-  {                     /*  NULL cmd pointer (NULLed out when freed) or it  */
-                        /*  has already been recycled for another command   */
-                        /*  Either way, this SCB has nothing to do with this*/
-                        /*  command and we need to deal with cmd without    */
-                        /*  touching the SCB.                               */
-                        /*  The theory here is to return a value that will  */
-                        /*  make the queued for complete command actually   */
-                        /*  finish successfully, or to indicate that we     */
-                        /*  don't have this cmd any more and the mid level  */
-                        /*  code needs to find it.                          */
-    if (aic7xxx_verbose & VERBOSE_ABORT_MID)
-      printk(INFO_LEAD "Abort called for already completed"
-        " command.\n", p->host_no, CTL_OF_CMD(cmd));
-    unpause_sequencer(p, FALSE);
-    return(SCSI_ABORT_NOT_RUNNING);
-  }
-    
-/*   At this point we know the following:
- *     the SCB pointer is valid
- *     the command pointer passed in to us and the scb->cmd pointer match
- *     this then means that the command we need to abort is the same as the
- *     command held by the scb pointer and is a valid abort request.
- *   Now, we just have to figure out what to do from here.  Current plan is:
- *     if we have already been here on this command, escalate to a reset
- *     if scb is on waiting list or QINFIFO, send it back as aborted, but
- *       we also need to be aware of the possibility that we could be using
- *       a faked negotiation command that is holding this command up,  if
- *       so we need to take care of that command instead, which means we
- *       would then treat this one like it was sitting around disconnected
- *       instead.
- *     if scb is on WAITING_SCB list in sequencer, free scb and send back
- *     if scb is disconnected and not completed, abort with abort message
- *     if scb is currently running, then it may be causing the bus to hang
- *       so we want a return value that indicates a reset would be appropriate
- *       if the command does not finish shortly
- *     if scb is already complete but not on completeq, we're screwed because
- *       this can't happen (except if the command is in the QOUTFIFO, in which
- *       case we would like it to complete successfully instead of having to
- *       to be re-done)
- *   All other scenarios already dealt with by previous code.
- */
-
-  if ( scb->flags & (SCB_ABORT | SCB_RESET | SCB_QUEUED_ABORT) )
-  {
-    if (aic7xxx_verbose & VERBOSE_ABORT_PROCESS)
-      printk(INFO_LEAD "SCB aborted once already, "
-        "escalating.\n", p->host_no, CTL_OF_SCB(scb));
-    unpause_sequencer(p, FALSE);
-    return(SCSI_ABORT_SNOOZE);
-  }
-  if ( (p->flags & (AHC_RESET_PENDING | AHC_ABORT_PENDING)) || 
-          (aic_dev->flags & BUS_DEVICE_RESET_PENDING) )
-  {
-    if (aic7xxx_verbose & VERBOSE_ABORT_PROCESS)
-      printk(INFO_LEAD "Reset/Abort pending for this "
-        "device, not wasting our time.\n", p->host_no, CTL_OF_SCB(scb));
-    unpause_sequencer(p, FALSE);
-    return(SCSI_ABORT_PENDING);
-  }
-
-  found = 0;
-  p->flags |= AHC_IN_ABORT;
   if (aic7xxx_verbose & VERBOSE_ABORT)
   {
     printk(INFO_LEAD "Aborting scb %d, flags 0x%x, SEQADDR 0x%x, LASTPHASE "
@@ -10750,56 +10721,8 @@ aic7xxx_abort(Scsi_Cmnd *cmd)
          aic_inb(p, SSTAT1), aic_inb(p, SSTAT2));
   }
 
-/*
- *   First, let's check to see if the currently running command is our target
- *    since if it is, the return is fairly easy and quick since we don't want
- *    to touch the command in case it might complete, but we do want a timeout
- *    in case it's actually hung, so we really do nothing, but tell the mid
- *    level code to reset the timeout.
- */
-
-  if ( scb->hscb->tag == aic_inb(p, SCB_TAG) )
-  {
-   /*
-    *  Check to see if the sequencer is just sitting on this command, or
-    *   if it's actively being run.
-    */
-    result = aic_inb(p, LASTPHASE);
-    switch (result)
-    {
-      case P_DATAOUT:    /*    For any of these cases, we can assume we are */
-      case P_DATAIN:     /*    an active command and act according.  For    */
-      case P_COMMAND:    /*    anything else we are going to fall on through*/
-      case P_STATUS:     /*    The SCSI_ABORT_SNOOZE will give us two abort */
-      case P_MESGOUT:    /*    chances to finish and then escalate to a     */
-      case P_MESGIN:     /*    reset call                                   */
-        if (aic7xxx_verbose & VERBOSE_ABORT_PROCESS)
-          printk(INFO_LEAD "SCB is currently active.  "
-                "Waiting on completion.\n", p->host_no, CTL_OF_SCB(scb));
-        printk(INFO_LEAD "SCSISIGI 0x%x, SEQADDR 0x%x, SSTAT0 0x%x, SSTAT1 "
-           "0x%x\n", p->host_no, CTL_OF_SCB(scb),
-           aic_inb(p, SCSISIGI),
-           aic_inb(p, SEQADDR0) | (aic_inb(p, SEQADDR1) << 8),
-           aic_inb(p, SSTAT0), aic_inb(p, SSTAT1));
-        printk(INFO_LEAD "SG_CACHEPTR 0x%x, SSTAT2 0x%x, STCNT 0x%x\n",
-           p->host_no, CTL_OF_SCB(scb),
-           (p->features & AHC_ULTRA2) ? aic_inb(p, SG_CACHEPTR) : 0,
-           aic_inb(p, SSTAT2), aic_inb(p, STCNT + 2) << 16 |
-           aic_inb(p, STCNT + 1) << 8 | aic_inb(p, STCNT));
-        unpause_sequencer(p, FALSE);
-        p->flags &= ~AHC_IN_ABORT;
-        scb->flags |= SCB_RECOVERY_SCB; /*  Note the fact that we've been  */
-        p->flags |= AHC_ABORT_PENDING;  /*  here so we will know not to    */
-        return(SCSI_ABORT_PENDING);     /*  one doesn't complete and clear */
-        break;                          /*  out.                           */
-      default:
-        break;
-    }
-  }
-
-  if ((found == 0) && (scb->flags & SCB_WAITINGQ))
+  if (scb->flags & SCB_WAITINGQ)
   {
-
     if (aic7xxx_verbose & VERBOSE_ABORT_PROCESS) 
       printk(INFO_LEAD "SCB found on waiting list and "
           "aborted.\n", p->host_no, CTL_OF_SCB(scb));
@@ -10809,95 +10732,123 @@ aic7xxx_abort(Scsi_Cmnd *cmd)
     p->activescbs++;
     scb->flags &= ~(SCB_WAITINGQ | SCB_ACTIVE);
     scb->flags |= SCB_ABORT | SCB_QUEUED_FOR_DONE;
-    found = 1;
+    goto success;
   }
 
 /*
  *  We just checked the waiting_q, now for the QINFIFO
  */
-  if ( found == 0 )
-  {
-    if ( ((found = aic7xxx_search_qinfifo(p, cmd->target, 
-                     cmd->channel,
+  if ( ((found = aic7xxx_search_qinfifo(p, cmd->target, cmd->channel,
                      cmd->lun, scb->hscb->tag, SCB_ABORT | SCB_QUEUED_FOR_DONE,
                      FALSE, NULL)) != 0) &&
                     (aic7xxx_verbose & VERBOSE_ABORT_PROCESS))
-      printk(INFO_LEAD "SCB found in QINFIFO and "
-        "aborted.\n", p->host_no, CTL_OF_SCB(scb));
+  {
+    printk(INFO_LEAD "SCB found in QINFIFO and aborted.\n", p->host_no,
+                   CTL_OF_SCB(scb));
+    goto success;
   }
 
 /*
  *  QINFIFO, waitingq, completeq done.  Next, check WAITING_SCB list in card
  */
 
-  if ( found == 0 )
+  saved_hscbptr = aic_inb(p, SCBPTR);
+  if ((hscbptr = aic7xxx_find_scb(p, scb)) != SCB_LIST_NULL)
   {
-    unsigned char scb_next_ptr;
-    prev_hscbptr = SCB_LIST_NULL;
-    saved_hscbptr = aic_inb(p, SCBPTR);
-    next_hscbptr = aic_inb(p, WAITING_SCBH);
-    while ( next_hscbptr != SCB_LIST_NULL )
-    {
-      aic_outb(p,  next_hscbptr, SCBPTR );
-      if ( scb->hscb->tag == aic_inb(p, SCB_TAG) )
+    aic_outb(p, hscbptr, SCBPTR);
+    scb_control = aic_inb(p, SCB_CONTROL);
+    disconnected = scb_control & DISCONNECTED;
+    /*
+     * If the DISCONNECTED bit is not set in SCB_CONTROL, then we are
+     * either currently active or on the waiting list.
+     */
+    if(!disconnected && aic_inb(p, LASTPHASE) == P_BUSFREE) {
+      if (aic7xxx_verbose & VERBOSE_ABORT_PROCESS)
+        printk(INFO_LEAD "SCB found on hardware waiting"
+          " list and aborted.\n", p->host_no, CTL_OF_SCB(scb));
+      /* If we are the only waiting command, stop the selection engine */
+      if (aic_inb(p, WAITING_SCBH) == hscbptr && aic_inb(p, SCB_NEXT) ==
+                       SCB_LIST_NULL)
+      {
+        aic_outb(p, aic_inb(p, SCSISEQ) & ~ENSELO, SCSISEQ);
+        aic_outb(p, CLRSELTIMEO, CLRSINT1);
+       aic_outb(p, SCB_LIST_NULL, WAITING_SCBH);
+      }
+      else
       {
-        found = 1;
-        if (aic7xxx_verbose & VERBOSE_ABORT_PROCESS)
-          printk(INFO_LEAD "SCB found on hardware waiting"
-            " list and aborted.\n", p->host_no, CTL_OF_SCB(scb));
-        if ( prev_hscbptr == SCB_LIST_NULL )
-        {
-            aic_outb(p, aic_inb(p, SCB_NEXT), WAITING_SCBH);
-            /* stop the selection since we just
-             * grabbed the scb out from under the
-             * card
-             */
-            aic_outb(p, aic_inb(p, SCSISEQ) & ~ENSELO, SCSISEQ);
-            aic_outb(p, CLRSELTIMEO, CLRSINT1);
-        }
-        else
-        {
-            scb_next_ptr = aic_inb(p, SCB_NEXT);
-            aic_outb(p, prev_hscbptr, SCBPTR);
-            aic_outb(p, scb_next_ptr, SCB_NEXT);
-            aic_outb(p, next_hscbptr, SCBPTR);
-        }
-        aic_outb(p, SCB_LIST_NULL, SCB_TAG);
-        aic_outb(p, 0, SCB_CONTROL);
-        aic7xxx_add_curscb_to_free_list(p);
-        scb->flags = SCB_ABORT | SCB_QUEUED_FOR_DONE;
-        break;
+       unsigned char prev, next;
+       prev = SCB_LIST_NULL;
+       next = aic_inb(p, WAITING_SCBH);
+       while(next != SCB_LIST_NULL)
+       {
+         aic_outb(p, next, SCBPTR);
+         if (next == hscbptr)
+         {
+           next = aic_inb(p, SCB_NEXT);
+           if (prev != SCB_LIST_NULL)
+           {
+             aic_outb(p, prev, SCBPTR);
+             aic_outb(p, next, SCB_NEXT);
+           }
+           else
+             aic_outb(p, next, WAITING_SCBH);
+           aic_outb(p, hscbptr, SCBPTR);
+           next = SCB_LIST_NULL;
+         }
+         else
+         {
+           prev = next;
+           next = aic_inb(p, SCB_NEXT);
+         }
+       }
       }
-      prev_hscbptr = next_hscbptr;
-      next_hscbptr = aic_inb(p, SCB_NEXT);
+      aic_outb(p, SCB_LIST_NULL, SCB_TAG);
+      aic_outb(p, 0, SCB_CONTROL);
+      aic7xxx_add_curscb_to_free_list(p);
+      scb->flags = SCB_ABORT | SCB_QUEUED_FOR_DONE;
+      goto success;
+    }
+    else if (!disconnected)
+    {
+      /*
+       * We are the currently active command
+       */
+      if((aic_inb(p, LASTPHASE) == P_MESGIN) ||
+        (aic_inb(p, LASTPHASE) == P_MESGOUT))
+      {
+       /*
+        * Message buffer busy, unable to abort
+        */
+       printk(INFO_LEAD "message buffer busy, unable to abort.\n",
+                         p->host_no, CTL_OF_SCB(scb));
+       unpause_sequencer(p, FALSE);
+       return FAILED;
+      }
+      /* Fallthrough to below, set ATNO after we set SCB_CONTROL */
+    } 
+    aic_outb(p,  scb_control | MK_MESSAGE, SCB_CONTROL);
+    if(!disconnected)
+    {
+      aic_outb(p, HOST_MSG, MSG_OUT);
+      aic_outb(p, aic_inb(p, SCSISIGI) | ATNO, SCSISIGO);
     }
-    aic_outb(p,  saved_hscbptr, SCBPTR );
+    aic_outb(p, saved_hscbptr, SCBPTR);
+  } 
+  else
+  {
+    /*
+     * The scb isn't in the card at all and it is active and it isn't in
+     * any of the queues, so it must be disconnected and paged out.  Fall
+     * through to the code below.
+     */
+    disconnected = 1;
   }
         
-/*
- *  Hmmm...completeq, QOUTFIFO, QINFIFO, WAITING_SCBH, waitingq all checked.
- *  OK...the sequencer's paused, interrupts are off, and we haven't found the
- *  command anyplace where it could be easily aborted.  Time for the hard
- *  work.  We also know the command is valid.  This essentially means the
- *  command is disconnected, or connected but not into any phases yet, which
- *  we know due to the tests we ran earlier on the current active scb phase.
- *  At this point we can queue the abort tag and go on with life.
- */
-
-  if ( found == 0 )
+  p->flags |= AHC_ABORT_PENDING;
+  scb->flags |= SCB_QUEUED_ABORT | SCB_ABORT | SCB_RECOVERY_SCB;
+  scb->hscb->control |= MK_MESSAGE;
+  if(disconnected)
   {
-    p->flags |= AHC_ABORT_PENDING;
-    scb->flags |= SCB_QUEUED_ABORT | SCB_ABORT | SCB_RECOVERY_SCB;
-    scb->hscb->control |= MK_MESSAGE;
-    result=aic7xxx_find_scb(p, scb);
-    if ( result != SCB_LIST_NULL ) 
-    {
-      saved_hscbptr = aic_inb(p, SCBPTR);
-      aic_outb(p, result, SCBPTR);
-      tmp_char = aic_inb(p, SCB_CONTROL);
-      aic_outb(p,  tmp_char | MK_MESSAGE, SCB_CONTROL);
-      aic_outb(p, saved_hscbptr, SCBPTR);
-    }
     if (aic7xxx_verbose & VERBOSE_ABORT_PROCESS)
       printk(INFO_LEAD "SCB disconnected.  Queueing Abort"
         " SCB.\n", p->host_no, CTL_OF_SCB(scb));
@@ -10907,31 +10858,30 @@ aic7xxx_abort(Scsi_Cmnd *cmd)
     else
       aic_outb(p, p->qinfifonext, KERNEL_QINPOS);
   }
-  if (found)
+  unpause_sequencer(p, FALSE);
+  spin_unlock_irq(p->host->host_lock);
+  scsi_sleep(HZ/4);
+  spin_lock_irq(p->host->host_lock);
+  if (p->flags & AHC_ABORT_PENDING)
   {
-    aic7xxx_run_done_queue(p, TRUE);
-    aic7xxx_run_waiting_queues(p);
+    if (aic7xxx_verbose & VERBOSE_ABORT_RETURN)
+      printk(INFO_LEAD "Abort never delivered, returning FAILED\n", p->host_no,
+                   CTL_OF_CMD(cmd));
+    p->flags &= ~AHC_ABORT_PENDING;
+    return FAILED;
   }
-  p->flags &= ~AHC_IN_ABORT;
-  unpause_sequencer(p, FALSE);
+  if (aic7xxx_verbose & VERBOSE_ABORT_RETURN)
+    printk(INFO_LEAD "Abort successful.\n", p->host_no, CTL_OF_CMD(cmd));
+  return SUCCESS;
 
-/*
- *  On the return value.  If we found the command and aborted it, then we know
- *  it's already sent back and there is no reason for a further timeout, so
- *  we use SCSI_ABORT_SUCCESS.  On the queued abort side, we aren't so certain
- *  there hasn't been a bus hang or something that might keep the abort from
- *  from completing.  Therefore, we use SCSI_ABORT_PENDING.  The first time this
- *  is passed back, the timeout on the command gets extended, the second time
- *  we pass this back, the mid level SCSI code calls our reset function, which
- *  would shake loose a hung bus.
- */
-  if ( found != 0 )
-    return(SCSI_ABORT_SUCCESS);
-  else
-    return(SCSI_ABORT_PENDING); 
+success:
+  if (aic7xxx_verbose & VERBOSE_ABORT_RETURN)
+    printk(INFO_LEAD "Abort successful.\n", p->host_no, CTL_OF_CMD(cmd));
+  aic7xxx_run_done_queue(p, TRUE);
+  unpause_sequencer(p, FALSE);
+  return SUCCESS;
 }
 
-
 /*+F*************************************************************************
  * Function:
  *   aic7xxx_reset
@@ -10943,38 +10893,24 @@ aic7xxx_abort(Scsi_Cmnd *cmd)
  *   the SCSI bus reset line.
  *-F*************************************************************************/
 int
-aic7xxx_reset(Scsi_Cmnd *cmd, unsigned int flags)
+aic7xxx_reset(Scsi_Cmnd *cmd)
 {
-  struct aic7xxx_scb *scb = NULL;
+  struct aic7xxx_scb *scb;
   struct aic7xxx_host *p;
   struct aic_dev_data *aic_dev;
-  int    tindex;
-  int    result = -1;
-#define DEVICE_RESET 0x01
-#define BUS_RESET    0x02
-#define HOST_RESET   0x04
-#define RESET_DELAY  0x08
-  int        action;
-
-
-  if ( cmd == NULL )
-  {
-    printk(KERN_WARNING "(scsi?:?:?:?) Reset called with NULL Scsi_Cmnd "
-      "pointer, failing.\n");
-    return(SCSI_RESET_SNOOZE);
-  }
 
   p = (struct aic7xxx_host *) cmd->host->hostdata;
   aic_dev = AIC_DEV(cmd);
-  if(aic_dev)
+  if(aic7xxx_position(cmd) < p->scb_data->numscbs)
   {
     scb = (p->scb_data->scb_array[aic7xxx_position(cmd)]);
+    if (scb->cmd != cmd)
+      scb = NULL;
   }
   else
   {
     scb = NULL;
   }
-  tindex = TARGET_INDEX(cmd);
 
   /*
    * I added a new config option to the driver: "panic_on_abort" that will
@@ -10989,44 +10925,6 @@ aic7xxx_reset(Scsi_Cmnd *cmd, unsigned int flags)
 
   pause_sequencer(p);
 
-  if(flags & SCSI_RESET_SYNCHRONOUS)
-  {
-    if (aic7xxx_verbose & VERBOSE_RESET_MID)
-      printk(INFO_LEAD "Reset called for a SYNCHRONOUS reset, flags 0x%x, "
-           "cmd->result 0x%x.\n", p->host_no, CTL_OF_CMD(cmd), flags,
-           cmd->result);
-    scb = NULL;
-    action = HOST_RESET;
-  }
-  else if ((scb == NULL) || (scb->cmd != cmd))
-  {
-    if (aic7xxx_verbose & VERBOSE_RESET_MID)
-      printk(INFO_LEAD "Reset called with bogus Scsi_Cmnd"
-           "->SCB mapping, failing.\n", p->host_no, CTL_OF_CMD(cmd));
-    aic7xxx_done_cmds_complete(p);
-    aic7xxx_run_waiting_queues(p);
-    unpause_sequencer(p, FALSE);
-    return(SCSI_RESET_NOT_RUNNING);
-  }
-  else
-  {
-    if (aic7xxx_verbose & VERBOSE_RESET_MID)
-      printk(INFO_LEAD "Reset called, scb %d, flags "
-        "0x%x\n", p->host_no, CTL_OF_SCB(scb), scb->hscb->tag, scb->flags);
-    if ( flags & SCSI_RESET_SUGGEST_HOST_RESET )
-    {
-      action = HOST_RESET;
-    }
-    else if ( flags & SCSI_RESET_SUGGEST_BUS_RESET )
-    {
-      action = BUS_RESET;
-    }
-    else 
-    {
-      action = DEVICE_RESET;
-    }
-  }
-
   while((aic_inb(p, INTSTAT) & INT_PEND) && !(p->flags & AHC_IN_ISR))
   {
     aic7xxx_isr(p->irq, p, (void *)NULL );
@@ -11040,134 +10938,32 @@ aic7xxx_reset(Scsi_Cmnd *cmd, unsigned int flags)
      * We just completed the command when we ran the isr stuff, so we no
      * longer have it.
      */
-    aic7xxx_run_waiting_queues(p);
     unpause_sequencer(p, FALSE);
-    return(SCSI_RESET_SUCCESS);
+    return SUCCESS;
   }
     
-  if ( (action & DEVICE_RESET) && 
-        (aic_dev->flags & BUS_DEVICE_RESET_PENDING) )
-  {
-    if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
-      printk(INFO_LEAD "Bus device reset already sent to "
-        "device, escalating.\n", p->host_no, CTL_OF_CMD(cmd));
-    action = BUS_RESET;
-  }
-  if ( (action & DEVICE_RESET) &&
-       (scb->flags & SCB_QUEUED_ABORT) )
-  {
-    if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
-    {
-      printk(INFO_LEAD "Have already attempted to reach "
-        "device with queued\n", p->host_no, CTL_OF_CMD(cmd));
-      printk(INFO_LEAD "message, will escalate to bus "
-        "reset.\n", p->host_no, CTL_OF_CMD(cmd));
-    }
-    action = BUS_RESET;
-  }
-  if ( (action & DEVICE_RESET) && 
-       (p->flags & (AHC_RESET_PENDING | AHC_ABORT_PENDING)) )
-  {
-    if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
-     printk(INFO_LEAD "Bus device reset stupid when "
-        "other action has failed.\n", p->host_no, CTL_OF_CMD(cmd));
-    action = BUS_RESET;
-  }
-  if ( (action & BUS_RESET) && !(p->features & AHC_TWIN) )
-  {
-    action = HOST_RESET;
-  }
-  if ( (aic_dev->flags & DEVICE_RESET_DELAY) &&
-       !(action & (HOST_RESET | BUS_RESET)))
-  {
-    if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
-    {
-      printk(INFO_LEAD "Reset called too soon after last "
-        "reset without requesting\n", p->host_no, CTL_OF_CMD(cmd));
-      printk(INFO_LEAD "bus or host reset, escalating.\n", p->host_no,
-        CTL_OF_CMD(cmd));
-    }
-    action = BUS_RESET;
-  }
-  if ( (p->flags & AHC_RESET_DELAY) &&
-       (action & (HOST_RESET | BUS_RESET)) )
-  {
-    if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
-      printk(INFO_LEAD "Reset called too soon after "
-        "last bus reset, delaying.\n", p->host_no, CTL_OF_CMD(cmd));
-    action = RESET_DELAY;
-  }
 /*
  *  By this point, we want to already know what we are going to do and
  *  only have the following code implement our course of action.
  */
-  switch (action)
+  aic7xxx_reset_channel(p, cmd->channel, TRUE);
+  if (p->features & AHC_TWIN)
   {
-    case RESET_DELAY:
-      aic7xxx_run_waiting_queues(p);
-      unpause_sequencer(p, FALSE);
-      if(scb == NULL)
-        return(SCSI_RESET_PUNT);
-      else
-        return(SCSI_RESET_PENDING);
-      break;
-    case DEVICE_RESET:
-      p->flags |= AHC_IN_RESET;
-      result = aic7xxx_bus_device_reset(p, cmd);
-      aic7xxx_run_done_queue(p, TRUE);
-      /*  We can't rely on run_waiting_queues to unpause the sequencer for
-       *  PCI based controllers since we use AAP */
-      aic7xxx_run_waiting_queues(p);
-      unpause_sequencer(p, FALSE);
-      p->flags &= ~AHC_IN_RESET;
-      return(result);
-      break;
-    case BUS_RESET:
-    case HOST_RESET:
-    default:
-      p->flags |= AHC_IN_RESET | AHC_RESET_DELAY;
-      aic7xxx_reset_channel(p, cmd->channel, TRUE);
-      if ( (p->features & AHC_TWIN) && (action & HOST_RESET) )
-      {
-        aic7xxx_reset_channel(p, cmd->channel ^ 0x01, TRUE);
-        restart_sequencer(p);
-      }
-      if (action != HOST_RESET)
-        result = SCSI_RESET_SUCCESS | SCSI_RESET_BUS_RESET;
-      else
-      {
-        result = SCSI_RESET_SUCCESS | SCSI_RESET_HOST_RESET;
-        aic_outb(p,  aic_inb(p, SIMODE1) & ~(ENREQINIT|ENBUSFREE),
-          SIMODE1);
-        aic7xxx_clear_intstat(p);
-        p->flags &= ~AHC_HANDLING_REQINITS;
-        p->msg_type = MSG_TYPE_NONE;
-        p->msg_index = 0;
-        p->msg_len = 0;
-      }
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,95)
-      if(flags & SCSI_RESET_SYNCHRONOUS)
-      {
-        cmd->result = DID_RESET << 16;
-        cmd->done(cmd);
-      }
-#endif
-      aic7xxx_run_done_queue(p, TRUE);
-      p->flags &= ~AHC_IN_RESET;
-      /*
-       * We can't rely on run_waiting_queues to unpause the sequencer for
-       * PCI based controllers since we use AAP.  NOTE: this also sets
-       * the timer for the one command we might have queued in the case
-       * of a synch reset.
-       */
-      aic7xxx_run_waiting_queues(p);
-      unpause_sequencer(p, FALSE);
-      if(scb == NULL)
-        return(SCSI_RESET_SUCCESS|SCSI_RESET_HOST_RESET);
-      else
-        return(result);
-      break;
+    aic7xxx_reset_channel(p, cmd->channel ^ 0x01, TRUE);
+    restart_sequencer(p);
   }
+  aic_outb(p,  aic_inb(p, SIMODE1) & ~(ENREQINIT|ENBUSFREE), SIMODE1);
+  aic7xxx_clear_intstat(p);
+  p->flags &= ~AHC_HANDLING_REQINITS;
+  p->msg_type = MSG_TYPE_NONE;
+  p->msg_index = 0;
+  p->msg_len = 0;
+  aic7xxx_run_done_queue(p, TRUE);
+  unpause_sequencer(p, FALSE);
+  spin_unlock_irq(p->host->host_lock);
+  scsi_sleep(2 * HZ);
+  spin_lock_irq(p->host->host_lock);
+  return SUCCESS;
 }
 
 /*+F*************************************************************************
index c7773bead1a522d9885e1f7e95b857878ad005ba..266c716db1187d63de43be19eed2064bf8f91e98 100644 (file)
@@ -38,6 +38,9 @@
        slave_attach: aic7xxx_slave_attach,                     \
        slave_detach: aic7xxx_slave_detach,                     \
        bios_param: aic7xxx_biosparam,                          \
+       eh_abort_handler: aic7xxx_abort,                        \
+       eh_device_reset_handler: aic7xxx_bus_device_reset,      \
+       eh_host_reset_handler: aic7xxx_reset,                   \
        can_queue: 255,         /* max simultaneous cmds      */\
        this_id: -1,            /* scsi id of host adapter    */\
        sg_tablesize: 0,        /* max scatter-gather cmds    */\
@@ -55,6 +58,9 @@ extern int aic7xxx_command(Scsi_Cmnd *);
 extern int aic7xxx_release(struct Scsi_Host *);
 extern int aic7xxx_slave_attach(Scsi_Device *);
 extern void aic7xxx_slave_detach(Scsi_Device *);
+extern int aic7xxx_abort(Scsi_Cmnd *);
+extern int aic7xxx_bus_device_reset(Scsi_Cmnd *);
+extern int aic7xxx_reset(Scsi_Cmnd *);
 
 extern const char *aic7xxx_info(struct Scsi_Host *);
 
index 886b84a21641fb0b189ae342d86f0d492ea800d4..838d145781224c38d2bf137be5ad7645700581e1 100644 (file)
@@ -33,7 +33,7 @@
 
 #define        BLS     (&aic7xxx_buffer[size])
 #define HDRB \
-"             0 - 4K     4 - 16K    16 - 64K   64 - 256K   256K - 1M    1M+"
+"               0 - 4K   4 - 16K   16 - 64K  64 - 256K  256K - 1M        1M+"
 
 #ifdef PROC_DEBUG
 extern int vsprintf(char *, const char *, va_list);
@@ -90,7 +90,6 @@ aic7xxx_proc_info ( char *buffer, char **start, off_t offset, int length,
   int    size = 0;
   unsigned char i;
   unsigned char tindex;
-  struct list_head *list_item;
 
   HBAptr = NULL;
 
@@ -132,10 +131,8 @@ aic7xxx_proc_info ( char *buffer, char **start, off_t offset, int length,
    */
 
   size = 4096;
-  list_for_each(list_item, &p->aic_devs)
-  {
+  list_for_each_entry(aic_dev, &p->aic_devs, list)
     size += 512;
-  }
   if (aic7xxx_buffer_size != size)
   {
     if (aic7xxx_buffer != NULL) 
@@ -157,13 +154,6 @@ aic7xxx_proc_info ( char *buffer, char **start, off_t offset, int length,
   size += sprintf(BLS, "Adaptec AIC7xxx driver version: ");
   size += sprintf(BLS, "%s/", AIC7XXX_C_VERSION);
   size += sprintf(BLS, "%s", AIC7XXX_H_VERSION);
-  size += sprintf(BLS, "\n");
-  size += sprintf(BLS, "Compile Options:\n");
-#ifdef CONFIG_AIC7XXX_OLD_TCQ_ON_BY_DEFAULT
-  size += sprintf(BLS, "  TCQ Enabled By Default : Enabled\n");
-#else
-  size += sprintf(BLS, "  TCQ Enabled By Default : Disabled\n");
-#endif
   size += sprintf(BLS, "\n");
   size += sprintf(BLS, "Adapter Configuration:\n");
   size += sprintf(BLS, "           SCSI Adapter: %s\n",
@@ -263,7 +253,7 @@ aic7xxx_proc_info ( char *buffer, char **start, off_t offset, int length,
   {
     size += sprintf(BLS, "     Ultra Enable Flags: 0x%04x\n", p->ultraenb);
   }
-  size += sprintf(BLS, "Default Tag Queue Depth: %d\n", AIC7XXX_CMDS_PER_DEVICE);
+  size += sprintf(BLS, "Default Tag Queue Depth: %d\n", aic7xxx_default_queue_depth);
   size += sprintf(BLS, "    Tagged Queue By Device array for aic7xxx host "
                        "instance %d:\n", p->instance);
   size += sprintf(BLS, "      {");
@@ -273,9 +263,8 @@ aic7xxx_proc_info ( char *buffer, char **start, off_t offset, int length,
 
   size += sprintf(BLS, "\n");
   size += sprintf(BLS, "Statistics:\n\n");
-  list_for_each(list_item, &p->aic_devs)
+  list_for_each_entry(aic_dev, &p->aic_devs, list)
   {
-    aic_dev = list_entry(list_item, struct aic_dev_data, list);
     sdptr = aic_dev->SDptr;
     tindex = sdptr->channel << 3 | sdptr->id;
     size += sprintf(BLS, "(scsi%d:%d:%d:%d)\n",
@@ -322,24 +311,32 @@ aic7xxx_proc_info ( char *buffer, char **start, off_t offset, int length,
                     p->user[tindex].offset,
                     p->user[tindex].width,
                     p->user[tindex].options);
-    size += sprintf(BLS, "  Total transfers %ld (%ld reads and %ld writes)\n",
-      aic_dev->r_total + aic_dev->w_total, aic_dev->r_total, aic_dev->w_total);
+    if(sdptr->simple_tags)
+    {
+      size += sprintf(BLS, "  Tagged Command Queueing Enabled, Ordered Tags %s\n", sdptr->ordered_tags ? "Enabled" : "Disabled");
+    }
+    if(aic_dev->barrier_total)
+      size += sprintf(BLS, "  Total transfers %ld:\n    (%ld/%ld/%ld/%ld reads/writes/REQ_BARRIER/Ordered Tags)\n",
+        aic_dev->r_total+aic_dev->w_total, aic_dev->r_total, aic_dev->w_total,
+        aic_dev->barrier_total, aic_dev->ordered_total);
+    else
+      size += sprintf(BLS, "  Total transfers %ld:\n    (%ld/%ld reads/writes)\n",
+        aic_dev->r_total+aic_dev->w_total, aic_dev->r_total, aic_dev->w_total);
     size += sprintf(BLS, "%s\n", HDRB);
     size += sprintf(BLS, "   Reads:");
     for (i = 0; i < NUMBER(aic_dev->r_bins); i++)
     {
-      size += sprintf(BLS, " %7ld", aic_dev->r_bins[i]);
+      size += sprintf(BLS, " %10ld", aic_dev->r_bins[i]);
     }
     size += sprintf(BLS, "\n");
     size += sprintf(BLS, "  Writes:");
     for (i = 0; i < NUMBER(aic_dev->w_bins); i++)
     {
-      size += sprintf(BLS, " %7ld", aic_dev->w_bins[i]);
+      size += sprintf(BLS, " %10ld", aic_dev->w_bins[i]);
     }
     size += sprintf(BLS, "\n");
     size += sprintf(BLS, "\n\n");
   }
-
   if (size >= aic7xxx_buffer_size)
   {
     printk(KERN_WARNING "aic7xxx: Overflow in aic7xxx_proc.c\n");