patch-2.1.98 linux/drivers/scsi/eata.c

Next file: linux/drivers/scsi/eata.h
Previous file: linux/drivers/scsi/BusLogic.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.97/linux/drivers/scsi/eata.c linux/drivers/scsi/eata.c
@@ -1,6 +1,14 @@
 /*
  *      eata.c - Low-level driver for EATA/DMA SCSI host adapters.
- *   
+ *
+ *      18 Apr 1998 Rev. 4.20 for linux 2.0.33 and 2.1.97
+ *          Reworked interrupt handler.
+ *
+ *      11 Apr 1998 rev. 4.05 for linux 2.0.33 and 2.1.95
+ *          Major reliability improvement: when a batch with overlapping
+ *          requests is detected, requests are queued one at a time
+ *          eliminating any possible board or drive reordering.
+ *
  *      10 Apr 1998 rev. 4.04 for linux 2.0.33 and 2.1.95
  *          Improved SMP support (if linux version >= 2.1.95).
  *
@@ -8,7 +16,7 @@
  *          Added support for new PCI code and IO-APIC remapping of irqs.
  *          Performance improvement: when sequential i/o is detected,
  *          always use direct sort instead of reverse sort.
- *   
+ *
  *       4 Apr 1998 rev. 4.02 for linux 2.0.33 and 2.1.92
  *          io_port is now unsigned long.
  *
@@ -16,7 +24,6 @@
  *          Use new scsi error handling code (if linux version >= 2.1.88).
  *          Use new interrupt code.
  *
- *
  *      12 Sep 1997 rev. 3.11 for linux 2.0.30 and 2.1.55
  *          Use of udelay inside the wait loops to avoid timeout
  *          problems with fast cpus.
@@ -30,7 +37,7 @@
  *          Use of serial_number_at_timeout in abort and reset processing.
  *          Use of the __initfunc and __initdata macro in setup code.
  *          Minor cleanups in the list_statistics code.
- *          Increased controller busy timeout in order to better support 
+ *          Increased controller busy timeout in order to better support
  *          slow SCSI devices.
  *
  *      24 Feb 1997 rev. 3.00 for linux 2.0.29 and 2.1.26
@@ -39,7 +46,7 @@
  *          Fixed data transfer direction for some SCSI opcodes.
  *          Immediate acknowledge to request sense commands.
  *          Linked commands to each disk device are now reordered by elevator
- *          sorting. Rare cases in which reordering of write requests could 
+ *          sorting. Rare cases in which reordering of write requests could
  *          cause wrong results are managed.
  *          Fixed spurious timeouts caused by long simple queue tag sequences.
  *          New command line option (tm:[0-3]) to choose the type of tags:
@@ -105,7 +112,7 @@
  *
  *      28 Jan 1995 rev. 1.14 for linux 1.1.86
  *          Added module support.
- *          Log and do a retry when a disk drive returns a target status 
+ *          Log and do a retry when a disk drive returns a target status
  *          different from zero on a recovered error.
  *
  *      24 Jan 1995 rev. 1.13 for linux 1.1.85
@@ -119,7 +126,7 @@
  *
  *      17 Dec 1994 rev. 1.11 for linux 1.1.74
  *          Use the scsicam_bios_param routine. This allows an easy
- *          migration path from disk partition tables created using 
+ *          migration path from disk partition tables created using
  *          different SCSI drivers and non optimal disk geometry.
  *
  *      15 Dec 1994 rev. 1.10 for linux 1.1.74
@@ -173,10 +180,10 @@
  *  The DPT PM2001 provides only the EATA/PIO interface and hence is not
  *  supported by this driver.
  *
- *  This code has been tested with up to 3 Distributed Processing Technology 
+ *  This code has been tested with up to 3 Distributed Processing Technology
  *  PM2122A/9X (DPT SCSI BIOS v002.D1, firmware v05E.0) EISA controllers,
  *  in any combination of private and shared IRQ.
- *  PCI support has been tested using up to 2 DPT PM3224W (DPT SCSI BIOS 
+ *  PCI support has been tested using up to 2 DPT PM3224W (DPT SCSI BIOS
  *  v003.D0, firmware v07G.0).
  *
  *  DPT SmartRAID boards support "Hardware Array" - a group of disk drives
@@ -186,14 +193,14 @@
  *
  *  WARNING: to create a RAID-0 "Hardware Array" you must select "Other Unix"
  *  as the current OS in the DPTMGR "Initial System Installation" menu.
- *  Otherwise RAID-0 is generated as an "Array Group" (i.e. software RAID-0), 
+ *  Otherwise RAID-0 is generated as an "Array Group" (i.e. software RAID-0),
  *  which is not supported by the actual SCSI subsystem.
  *  To get the "Array Group" functionality, the Linux MD driver must be used
  *  instead of the DPT "Array Group" feature.
  *
  *  Multiple ISA, EISA and PCI boards can be configured in the same system.
  *  It is suggested to put all the EISA boards on the same IRQ level, all
- *  the PCI  boards on another IRQ level, while ISA boards cannot share 
+ *  the PCI  boards on another IRQ level, while ISA boards cannot share
  *  interrupts.
  *
  *  If you configure multiple boards on the same IRQ, the interrupt must
@@ -206,7 +213,7 @@
  *  bus, or even if you system has no EISA bus at all.
  *  Do not force any ISA address on EATA PCI boards.
  *
- *  If PCI bios support is configured into the kernel, BIOS32 is used to 
+ *  If PCI bios support is configured into the kernel, BIOS32 is used to
  *  include in the list of i/o ports to be probed all the PCI SCSI controllers.
  *
  *  Due to a DPT BIOS "feature", it might not be possible to force an EISA
@@ -215,11 +222,11 @@
  *
  *  The sequence of detection probes is:
  *
- *  - ISA 0x1F0; 
+ *  - ISA 0x1F0;
  *  - PCI SCSI controllers (only if BIOS32 is available);
  *  - EISA/PCI 0x1C88 through 0xFC88 (corresponding to EISA slots 1 to 15);
  *  - ISA  0x170, 0x230, 0x330.
- * 
+ *
  *  The above list of detection probes can be totally replaced by the
  *  boot command line option: "eata=port0,port1,port2,...", where the
  *  port0, port1... arguments are ISA/EISA/PCI addresses to be probed.
@@ -261,15 +268,15 @@
  *  in the elevator sorting queue. When the active command completes, the
  *  commands in this queue are sorted by sector address. The sort is chosen
  *  between increasing or decreasing by minimizing the seek distance between
- *  the sector of the commands just completed and the sector of the first 
- *  command in the list to be sorted. 
+ *  the sector of the commands just completed and the sector of the first
+ *  command in the list to be sorted.
  *  Trivial math assures that the unsorted average seek distance when doing
  *  random seeks over S sectors is S/3.
  *  When (Q-1) requests are uniformly distributed over S sectors, the average
  *  distance between two adjacent requests is S/((Q-1) + 1), so the sorted
  *  average seek distance for (Q-1) random requests over S sectors is S/Q.
  *  The elevator sorting hence divides the seek distance by a factor Q/3.
- *  The above pure geometric remarks are valid in all cases and the 
+ *  The above pure geometric remarks are valid in all cases and the
  *  driver effectively reduces the seek distance by the predicted factor
  *  when there are Q concurrent read i/o operations on the device, but this
  *  does not necessarily results in a noticeable performance improvement:
@@ -277,7 +284,7 @@
  *
  *  Note: command reordering inside a batch of queued commands could cause
  *        wrong results only if there is at least one write request and the
- *        intersection (sector-wise) of all requests is not empty. 
+ *        intersection (sector-wise) of all requests is not empty.
  *        When the driver detects a batch including overlapping requests
  *        (a really rare event) strict serial (pid) order is enforced.
  *  ----------------------------------------------------------------------------
@@ -316,7 +323,11 @@
 #include <asm/io.h>
 #include <asm/system.h>
 #include <asm/byteorder.h>
+
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,95)
 #include <asm/spinlock.h>
+#endif
+
 #include <linux/proc_fs.h>
 #include <linux/blk.h>
 #include "scsi.h"
@@ -359,7 +370,7 @@
 #undef  DEBUG_RESET
 
 #define MAX_ISA 4
-#define MAX_VESA 0 
+#define MAX_VESA 0
 #define MAX_EISA 15
 #define MAX_PCI 16
 #define MAX_BOARDS (MAX_ISA + MAX_VESA + MAX_EISA + MAX_PCI)
@@ -499,7 +510,7 @@
    unchar target_status;       /* SCSI status received after data transfer */
    unchar unused[2];
    ulong inv_res_len;          /* Number of bytes not transferred */
-   Scsi_Cmnd *SCpnt;           /* Address set in cp */
+   struct mscp *cpp;           /* Address set in cp */
    char mess[12];
    };
 
@@ -515,7 +526,7 @@
            reqsen:1,     /* Transfer Request Sense Data to addr using DMA */
                sg:1,     /* Use Scatter/Gather */
                  :1,
-           interp:1,     /* The controller interprets cp, not the target */ 
+           interp:1,     /* The controller interprets cp, not the target */
              dout:1,     /* Direction of Transfer is Out (Host to Target) */
               din:1;     /* Direction of Transfer is In (Target to Host) */
    unchar sense_len;     /* Request Sense Length */
@@ -535,10 +546,11 @@
    unchar mess[3];       /* Massage to/from Target */
    unchar cdb[12];       /* Command Descriptor Block */
    ulong  data_len;      /* If sg=0 Data Length, if sg=1 sglist length */
-   Scsi_Cmnd *SCpnt;     /* Address to be returned in sp */
+   struct mscp *cpp;     /* Address to be returned in sp */
    ulong  data_address;  /* If sg=0 Data Address, if sg=1 sglist address */
    ulong  sp_addr;       /* Address where sp is DMA'ed when cp completes */
    ulong  sense_addr;    /* Address where Sense Data is DMA'ed on error */
+   Scsi_Cmnd *SCpnt;
    unsigned int index;   /* cp index */
    struct sg_list *sglist;
    };
@@ -558,7 +570,7 @@
    unsigned long last_retried_pid;      /* Pid of last retried command */
    unsigned char subversion;            /* Bus type, either ISA or EISA/PCI */
    unsigned char protocol_rev;          /* EATA 2.0 rev., 'A' or 'B' or 'C' */
-   struct mssp sp[MAX_MAILBOXES];       /* Returned status for this board */
+   struct mssp sp[2];                   /* Returned status for this board */
    };
 
 static struct Scsi_Host *sh[MAX_BOARDS + 1];
@@ -568,7 +580,7 @@
 /* Initialize num_boards so that ihdlr can work while detect is in progress */
 static unsigned int num_boards = MAX_BOARDS;
 
-static unsigned long io_port[] __initdata = { 
+static unsigned long io_port[] __initdata = {
 
    /* Space for MAX_INT_PARAM ports usable while loading as a module */
    SKIP,    SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
@@ -583,11 +595,11 @@
 
    /* MAX_EISA ports */
    0x1c88, 0x2c88, 0x3c88, 0x4c88, 0x5c88, 0x6c88, 0x7c88, 0x8c88,
-   0x9c88, 0xac88, 0xbc88, 0xcc88, 0xdc88, 0xec88, 0xfc88, 
+   0x9c88, 0xac88, 0xbc88, 0xcc88, 0xdc88, 0xec88, 0xfc88,
 
    /* Other (MAX_ISA - 1) ports */
    0x170,  0x230,  0x330,
- 
+
    /* End of list */
    0x0
    };
@@ -627,7 +639,7 @@
 
 static void select_queue_depths(struct Scsi_Host *host, Scsi_Device *devlist) {
    Scsi_Device *dev;
-   int j, ntag = 0, nuntag = 0, tqd, utqd; 
+   int j, ntag = 0, nuntag = 0, tqd, utqd;
    unsigned long flags;
 
    save_flags(flags);
@@ -792,14 +804,14 @@
    if (do_dma(port_base, 0, READ_CONFIG_PIO)) return FALSE;
 
    /* Read the info structure */
-   if (read_pio(port_base, (ushort *)&info, (ushort *)&info.ipad[0])) 
+   if (read_pio(port_base, (ushort *)&info, (ushort *)&info.ipad[0]))
       return FALSE;
 
    /* Check the controller "EATA" signature */
    if (info.sign != EATA_SIGNATURE) return FALSE;
 
    if (DEV2H(info.data_len) < EATA_2_0A_SIZE) {
-      printk("%s: config structure size (%ld bytes) too short, detaching.\n", 
+      printk("%s: config structure size (%ld bytes) too short, detaching.\n",
              name, DEV2H(info.data_len));
       return FALSE;
       }
@@ -956,7 +968,7 @@
 
    /* DPT PM2012 does not allow to detect can_queue correctly */
    if (sh[j]->can_queue > MAX_MAILBOXES || sh[j]->can_queue  < 2) {
-      printk("%s: detect, wrong n. of Mbox %d, fixed.\n",
+      printk("%s: detect, wrong n. of mbox %d, fixed.\n",
              BN(j), sh[j]->can_queue);
       sh[j]->can_queue = MAX_MAILBOXES;
       }
@@ -984,14 +996,14 @@
 
    for (i = 0; i < sh[j]->can_queue; i++)
       if (! ((&HD(j)->cp[i])->sglist = kmalloc(
-            sh[j]->sg_tablesize * sizeof(struct sg_list), 
+            sh[j]->sg_tablesize * sizeof(struct sg_list),
             (sh[j]->unchecked_isa_dma ? GFP_DMA : 0) | GFP_ATOMIC))) {
          printk("%s: kmalloc SGlist failed, mbox %d, detaching.\n", BN(j), i);
          eata2x_release(sh[j]);
          return FALSE;
          }
-      
-   if (max_queue_depth > MAX_TAGGED_CMD_PER_LUN) 
+
+   if (max_queue_depth > MAX_TAGGED_CMD_PER_LUN)
        max_queue_depth = MAX_TAGGED_CMD_PER_LUN;
 
    if (max_queue_depth < MAX_CMD_PER_LUN) max_queue_depth = MAX_CMD_PER_LUN;
@@ -1032,7 +1044,7 @@
 
    if (protocol_rev == 'C')
       printk("%s: max_lun %u, m1 %u, idquest %u, pci %u, eisa %u, "\
-             "raidnum %u.\n", name, info.max_lun, info.m1, info.idquest, 
+             "raidnum %u.\n", name, info.max_lun, info.m1, info.idquest,
              info.pci, info.eisa, info.raidnum);
 #endif
 
@@ -1047,8 +1059,8 @@
 
       if (argc > MAX_INT_PARAM) argc = MAX_INT_PARAM;
 
-      for (i = 0; i < argc; i++) io_port[i] = ints[i + 1]; 
-   
+      for (i = 0; i < argc; i++) io_port[i] = ints[i + 1];
+
       io_port[i] = 0;
       setup_done = TRUE;
       }
@@ -1099,7 +1111,7 @@
              continue;
 
       /* Reverse the returned address order */
-      io_port[MAX_INT_PARAM + MAX_PCI - k] = 
+      io_port[MAX_INT_PARAM + MAX_PCI - k] =
              (addr & PCI_BASE_ADDRESS_IO_MASK) + PCI_BASE_ADDRESS_0;
       }
 
@@ -1127,7 +1139,7 @@
              continue;
 
       /* Reverse the returned address order */
-      io_port[MAX_INT_PARAM + MAX_PCI - k] = 
+      io_port[MAX_INT_PARAM + MAX_PCI - k] =
              (addr & PCI_BASE_ADDRESS_IO_MASK) + PCI_BASE_ADDRESS_0;
       }
 
@@ -1166,7 +1178,7 @@
       if (j < MAX_BOARDS && port_detect(io_port[k], j, tpnt)) j++;
       }
 
-   if (j > 0) 
+   if (j > 0)
       printk("EATA/DMA 2.0x: Copyright (C) 1994-1998 Dario Ballabio.\n");
 
    num_boards = j;
@@ -1197,7 +1209,7 @@
 
    static const unsigned char data_out_cmds[] = {
       0x0a, 0x2a, 0x15, 0x55, 0x04, 0x07, 0x18, 0x1d, 0x24, 0x2e,
-      0x30, 0x31, 0x32, 0x38, 0x39, 0x3a, 0x3b, 0x3d, 0x3f, 0x40, 
+      0x30, 0x31, 0x32, 0x38, 0x39, 0x3a, 0x3b, 0x3d, 0x3f, 0x40,
       0x41, 0x4c, 0xaa, 0xae, 0xb0, 0xb1, 0xb2, 0xb6, 0xea, 0x1b
       };
 
@@ -1215,16 +1227,16 @@
    if (!done) panic("%s: qcomm, pid %ld, null done.\n", BN(j), SCpnt->pid);
 
    if (SCpnt->cmnd[0] == REQUEST_SENSE && SCpnt->sense_buffer[0]) {
-      SCpnt->result = DID_OK << 16; 
+      SCpnt->result = DID_OK << 16;
       SCpnt->host_scribble = NULL;
       printk("%s: qcomm, target %d.%d:%d, pid %ld, request sense ignored.\n",
              BN(j), SCpnt->channel, SCpnt->target, SCpnt->lun, SCpnt->pid);
       restore_flags(flags);
-      done(SCpnt);    
+      done(SCpnt);
       return 0;
       }
 
-   /* i is the mailbox number, look for the first free mailbox 
+   /* i is the mailbox number, look for the first free mailbox
       starting from last_cp_used */
    i = HD(j)->last_cp_used + 1;
 
@@ -1241,17 +1253,17 @@
    if (k == sh[j]->can_queue) {
       printk("%s: qcomm, no free mailbox, resetting.\n", BN(j));
 
-      if (HD(j)->in_reset) 
+      if (HD(j)->in_reset)
          printk("%s: qcomm, already in reset.\n", BN(j));
       else if (eata2x_reset(SCpnt, SCSI_RESET_SUGGEST_BUS_RESET)
-               == SCSI_RESET_SUCCESS) 
+               == SCSI_RESET_SUCCESS)
          panic("%s: qcomm, SCSI_RESET_SUCCESS.\n", BN(j));
 
-      SCpnt->result = DID_BUS_BUSY << 16; 
+      SCpnt->result = DID_BUS_BUSY << 16;
       SCpnt->host_scribble = NULL;
       printk("%s: qcomm, pid %ld, DID_BUS_BUSY, done.\n", BN(j), SCpnt->pid);
       restore_flags(flags);
-      done(SCpnt);    
+      done(SCpnt);
       return 1;
       }
 
@@ -1261,13 +1273,12 @@
    memset(cpp, 0, sizeof(struct mscp) - sizeof(struct sg_list *));
 
    /* Set pointer to status packet structure */
-   spp = &HD(j)->sp[i];
-
-   memset(spp, 0, sizeof(struct mssp));
+   spp = &HD(j)->sp[0];
 
    /* The EATA protocol uses Big Endian format */
    cpp->sp_addr = V2DEV(spp);
 
+   cpp->cpp = cpp;
    SCpnt->scsi_done = done;
    cpp->index = i;
    SCpnt->host_scribble = (unsigned char *) &cpp->index;
@@ -1297,11 +1308,11 @@
    cpp->one = TRUE;
    cpp->channel = SCpnt->channel;
    cpp->target = SCpnt->target;
-   cpp->lun = SCpnt->lun;  
+   cpp->lun = SCpnt->lun;
    cpp->SCpnt = SCpnt;
-   cpp->sense_addr = V2DEV(SCpnt->sense_buffer); 
+   cpp->sense_addr = V2DEV(SCpnt->sense_buffer);
    cpp->sense_len = sizeof SCpnt->sense_buffer;
-   
+
    if (SCpnt->device->tagged_queue) {
 
       if (HD(j)->target_redo[SCpnt->target][SCpnt->channel] ||
@@ -1341,13 +1352,13 @@
 
    /* Send control packet to the board */
    if (do_dma(sh[j]->io_port, (unsigned int) cpp, SEND_CP_DMA)) {
-      SCpnt->result = DID_ERROR << 16; 
+      SCpnt->result = DID_ERROR << 16;
       SCpnt->host_scribble = NULL;
       printk("%s: qcomm, target %d.%d:%d, pid %ld, adapter busy, DID_ERROR,"\
              " done.\n", BN(j), SCpnt->channel, SCpnt->target, SCpnt->lun,
              SCpnt->pid);
       restore_flags(flags);
-      done(SCpnt);    
+      done(SCpnt);
       return 1;
       }
 
@@ -1373,7 +1384,7 @@
       }
 
    i = *(unsigned int *)SCarg->host_scribble;
-   printk("%s: abort, mbox %d, target %d.%d:%d, pid %ld.\n", 
+   printk("%s: abort, mbox %d, target %d.%d:%d, pid %ld.\n",
           BN(j), i, SCarg->channel, SCarg->target, SCarg->lun, SCarg->pid);
 
    if (i >= sh[j]->can_queue)
@@ -1441,7 +1452,7 @@
    save_flags(flags);
    cli();
    j = ((struct hostdata *) SCarg->host->hostdata)->board_number;
-   printk("%s: reset, enter, target %d.%d:%d, pid %ld, reset_flags %u.\n", 
+   printk("%s: reset, enter, target %d.%d:%d, pid %ld, reset_flags %u.\n",
           BN(j), SCarg->channel, SCarg->target, SCarg->lun, SCarg->pid,
           reset_flags);
 
@@ -1502,10 +1513,10 @@
       if (SCpnt->host_scribble == NULL)
          panic("%s: reset, mbox %d, garbled SCpnt.\n", BN(j), i);
 
-      if (*(unsigned int *)SCpnt->host_scribble != i) 
+      if (*(unsigned int *)SCpnt->host_scribble != i)
          panic("%s: reset, mbox %d, index mismatch.\n", BN(j), i);
 
-      if (SCpnt->scsi_done == NULL) 
+      if (SCpnt->scsi_done == NULL)
          panic("%s: reset, mbox %d, SCpnt->scsi_done == NULL.\n", BN(j), i);
 
       if (SCpnt == SCarg) arg_done = TRUE;
@@ -1588,7 +1599,7 @@
    for (i = 0; i < n - 1; i++) {
       k = i;
 
-      for (j = k + 1; j < n; j++) 
+      for (j = k + 1; j < n; j++)
          if (rev) {
             if (sk[j] > sk[k]) k = j;
             }
@@ -1605,7 +1616,7 @@
    return;
    }
 
-static inline void reorder(unsigned int j, unsigned long cursec,
+static inline int reorder(unsigned int j, unsigned long cursec,
                  unsigned int ihdlr, unsigned int il[], unsigned int n_ready) {
    Scsi_Cmnd *SCpnt;
    struct mscp *cpp;
@@ -1625,10 +1636,10 @@
       printk("fc %d bc %d ic %d oc %d rc %d rs %d sc %d re %d"\
              " av %ldK as %ldK.\n", flushcount, batchcount, inputcount,
              ovlcount, readycount, readysorted, sortcount, revcount,
-             seeknosort / (readycount + 1), 
+             seeknosort / (readycount + 1),
              seeksorted / (readycount + 1));
 
-   if (n_ready <= 1) return;
+   if (n_ready <= 1) return FALSE;
 
    for (n = 0; n < n_ready; n++) {
       k = il[n]; cpp = &HD(j)->cp[k]; SCpnt = cpp->SCpnt;
@@ -1645,7 +1656,7 @@
 
       if (sl[n] < sl[n - 1]) s = FALSE;
       if (sl[n] > sl[n - 1]) r = FALSE;
-   
+
       if (link_statistics) {
          if (sl[n] > sl[n - 1])
             seek += sl[n] - sl[n - 1];
@@ -1670,7 +1681,7 @@
       ll[n] = SCpnt->request.nr_sectors; pl[n] = SCpnt->pid;
 
       if (!n) continue;
- 
+
       if ((sl[n] == sl[n - 1]) || (!rev && ((sl[n - 1] + ll[n - 1]) > sl[n]))
           || (rev && ((sl[n] + ll[n]) > sl[n - 1]))) overlap = TRUE;
       }
@@ -1679,9 +1690,9 @@
 
    if (link_statistics) {
       if (cursec > sl[0]) iseek = cursec - sl[0]; else iseek = sl[0] - cursec;
-      batchcount++; readycount += n_ready, seeknosort += seek / 1024; 
+      batchcount++; readycount += n_ready, seeknosort += seek / 1024;
       if (input_only) inputcount++;
-      if (overlap) { ovlcount++; seeksorted += seek / 1024; }
+      if (overlap) { ovlcount++; seeksorted += iseek / 1024; }
       else seeksorted += (iseek + maxsec - minsec) / 1024;
       if (rev && !r)     {  revcount++; readysorted += n_ready; }
       if (!rev && !s)    { sortcount++; readysorted += n_ready; }
@@ -1696,10 +1707,11 @@
                 (ihdlr ? "ihdlr" : "qcomm"), SCpnt->channel, SCpnt->target,
                 SCpnt->lun, SCpnt->pid, k, flushcount, n_ready,
                 SCpnt->request.sector, SCpnt->request.nr_sectors, cursec,
-                YESNO(s), YESNO(r), YESNO(rev), YESNO(input_only), 
+                YESNO(s), YESNO(r), YESNO(rev), YESNO(input_only),
                 YESNO(overlap), cpp->din);
          }
 #endif
+   return overlap;
 }
 
 static void flush_dev(Scsi_Device *dev, unsigned long cursec, unsigned int j,
@@ -1721,13 +1733,13 @@
       il[n_ready++] = k;
       }
 
-   reorder(j, cursec, ihdlr, il, n_ready);
-  
+   if (reorder(j, cursec, ihdlr, il, n_ready)) n_ready = 1;
+
    for (n = 0; n < n_ready; n++) {
       k = il[n]; cpp = &HD(j)->cp[k]; SCpnt = cpp->SCpnt;
 
       if (do_dma(sh[j]->io_port, (unsigned int) cpp, SEND_CP_DMA)) {
-         printk("%s: %s, target %d.%d:%d, pid %ld, Mbox %d, adapter"\
+         printk("%s: %s, target %d.%d:%d, pid %ld, mbox %d, adapter"\
                 " busy, will abort.\n", BN(j), (ihdlr ? "ihdlr" : "qcomm"),
                 SCpnt->channel, SCpnt->target, SCpnt->lun, SCpnt->pid, k);
          HD(j)->cp_stat[k] = ABORTING;
@@ -1739,196 +1751,201 @@
 
 }
 
-static inline void ihdlr(int irq, void *shap, struct pt_regs *regs) {
+static inline void ihdlr(int irq, unsigned int j) {
    Scsi_Cmnd *SCpnt;
-   unsigned int i, j, k, c, status, tstatus, reg;
-   unsigned int n, n_ready, il[MAX_MAILBOXES];
-   struct mssp *spp;
+   unsigned int i, k, c, status, tstatus, reg;
+   struct mssp *dspp, *spp;
    struct mscp *cpp;
 
-   /* Check if the interrupt must be processed by this handler */
-   if ((j = (unsigned int)((char *)shap - sha)) >= num_boards) return;
- 
    if (sh[j]->irq != irq)
        panic("%s: ihdlr, irq %d, sh[j]->irq %d.\n", BN(j), irq, sh[j]->irq);
- 
-   if (do_trace) printk("%s: ihdlr, enter, irq %d, count %d.\n", BN(j), irq,
-                        HD(j)->iocount);
- 
+
    /* Check if this board need to be serviced */
    if (!(inb(sh[j]->io_port + REG_AUX_STATUS) & IRQ_ASSERTED)) return;
 
-   n_ready = 0;
-
-   /* Find the mailboxes to be serviced on this board */
-   for (i = 0; i < sh[j]->can_queue; i++) {
-      spp = &HD(j)->sp[i];
+   HD(j)->iocount++;
 
-      /* Check if this mailbox has completed the operation */
-      if (spp->eoc == FALSE) continue;
+   if (do_trace) printk("%s: ihdlr, enter, irq %d, count %d.\n", BN(j), irq,
+                        HD(j)->iocount);
 
-      spp->eoc = FALSE;
-      il[n_ready++] = i;
+   /* Check if this board is still busy */
+   if (wait_on_busy(sh[j]->io_port, MAXLOOP)) {
+      reg = inb(sh[j]->io_port + REG_STATUS);
+      printk("%s: ihdlr, busy timeout error,  irq %d, reg 0x%x, count %d.\n",
+             BN(j), irq, reg, HD(j)->iocount);
+      return;
       }
 
+   dspp = &HD(j)->sp[0];
+   spp  = &HD(j)->sp[1];
+
+   /* Make a local copy just before clearing the interrupt indication */
+   memcpy(spp, dspp, sizeof(struct mssp));
+
+   /* Clear the completion flag and cp pointer on the dynamic copy of sp */
+   memset(dspp, 0, sizeof(struct mssp));
+
    /* Read the status register to clear the interrupt indication */
    reg = inb(sh[j]->io_port + REG_STATUS);
 
-   /* Mailbox service loop */
-   for (n = 0; n < n_ready; n++) {
-      i = il[n];
-      spp = &HD(j)->sp[i];
-
-      if (HD(j)->cp_stat[i] == IGNORE) {
-         HD(j)->cp_stat[i] = FREE;
-         return;
-         }
-      else if (HD(j)->cp_stat[i] == LOCKED) {
-         HD(j)->cp_stat[i] = FREE;
-         printk("%s: ihdlr, mbox %d unlocked, count %d.\n", BN(j), i,
-                HD(j)->iocount);
-         return;
-         }
-      else if (HD(j)->cp_stat[i] == FREE) {
-         printk("%s: ihdlr, mbox %d is free, count %d.\n", BN(j), i,
-                HD(j)->iocount);
-         return;
-         }
-      else if (HD(j)->cp_stat[i] == IN_RESET)
-         printk("%s: ihdlr, mbox %d is in reset.\n", BN(j), i);
-      else if (HD(j)->cp_stat[i] != IN_USE) 
-         panic("%s: ihdlr, mbox %d, invalid cp_stat: %d.\n", 
-               BN(j), i, HD(j)->cp_stat[i]);
+   /* Reject any sp with supspect data */
+   if (spp->eoc == FALSE) 
+      printk("%s: ihdlr, spp->eoc == FALSE, irq %d, reg 0x%x, count %d.\n",
+             BN(j), irq, reg, HD(j)->iocount);
+   if (spp->cpp == NULL) 
+      printk("%s: ihdlr, spp->cpp == NULL,  irq %d, reg 0x%x, count %d.\n",
+             BN(j), irq, reg, HD(j)->iocount);
+   if (spp->eoc == FALSE || spp->cpp == NULL) return;
+
+   cpp = spp->cpp;
+
+   /* Find the mailbox to be serviced on this board */
+   i = cpp - HD(j)->cp;
+
+   if (cpp < HD(j)->cp || cpp >= HD(j)->cp + sh[j]->can_queue
+                                     || i >= sh[j]->can_queue)
+      panic("%s: ihdlr, invalid mscp bus address %p, cp0 %p.\n", BN(j),
+            cpp, HD(j)->cp);
 
+   if (HD(j)->cp_stat[i] == IGNORE) {
+      HD(j)->cp_stat[i] = FREE;
+      return;
+      }
+   else if (HD(j)->cp_stat[i] == LOCKED) {
       HD(j)->cp_stat[i] = FREE;
-      cpp = &HD(j)->cp[i];
-      SCpnt = spp->SCpnt;
+      printk("%s: ihdlr, mbox %d unlocked, count %d.\n", BN(j), i,
+             HD(j)->iocount);
+      return;
+      }
+   else if (HD(j)->cp_stat[i] == FREE) {
+      printk("%s: ihdlr, mbox %d is free, count %d.\n", BN(j), i,
+             HD(j)->iocount);
+      return;
+      }
+   else if (HD(j)->cp_stat[i] == IN_RESET)
+      printk("%s: ihdlr, mbox %d is in reset.\n", BN(j), i);
+   else if (HD(j)->cp_stat[i] != IN_USE)
+      panic("%s: ihdlr, mbox %d, invalid cp_stat: %d.\n",
+            BN(j), i, HD(j)->cp_stat[i]);
+
+   HD(j)->cp_stat[i] = FREE;
+   SCpnt = cpp->SCpnt;
+
+   if (SCpnt == NULL) panic("%s: ihdlr, mbox %d, SCpnt == NULL.\n", BN(j), i);
+
+   if (SCpnt->host_scribble == NULL)
+      panic("%s: ihdlr, mbox %d, pid %ld, SCpnt %p garbled.\n", BN(j), i,
+            SCpnt->pid, SCpnt);
+
+   if (*(unsigned int *)SCpnt->host_scribble != i)
+      panic("%s: ihdlr, mbox %d, pid %ld, index mismatch %d.\n",
+            BN(j), i, SCpnt->pid, *(unsigned int *)SCpnt->host_scribble);
 
-      if(SCpnt == NULL) panic("%s: ihdlr, mbox %d, SCpnt == NULL.\n", BN(j), i);
+   if (linked_comm && SCpnt->device->queue_depth > 2
+                                     && TLDEV(SCpnt->device->type))
+      flush_dev(SCpnt->device, SCpnt->request.sector, j, TRUE);
 
-      if (SCpnt != cpp->SCpnt)
-         panic("%s: ihdlr, mbox %d, sp SCpnt %p, cp SCpnt %p.\n", BN(j), i,
-               SCpnt, cpp->SCpnt);
+   tstatus = status_byte(spp->target_status);
 
-      if (SCpnt->host_scribble == NULL)
-         panic("%s: ihdlr, mbox %d, pid %ld, SCpnt %p garbled.\n", BN(j), i,
-               SCpnt->pid, SCpnt);
+   switch (spp->adapter_status) {
+      case ASOK:     /* status OK */
 
-      if (*(unsigned int *)SCpnt->host_scribble != i) 
-         panic("%s: ihdlr, mbox %d, pid %ld, index mismatch %d, irq %d.\n",
-              BN(j), i, SCpnt->pid, *(unsigned int *)SCpnt->host_scribble, irq);
+         /* Forces a reset if a disk drive keeps returning BUSY */
+         if (tstatus == BUSY && SCpnt->device->type != TYPE_TAPE)
+            status = DID_ERROR << 16;
 
-      if (linked_comm && SCpnt->device->queue_depth > 2
-                                        && TLDEV(SCpnt->device->type))
-      flush_dev(SCpnt->device, SCpnt->request.sector, j, TRUE);
+         /* If there was a bus reset, redo operation on each target */
+         else if (tstatus != GOOD && SCpnt->device->type == TYPE_DISK
+                  && HD(j)->target_redo[SCpnt->target][SCpnt->channel])
+            status = DID_BUS_BUSY << 16;
+
+         /* Works around a flaw in scsi.c */
+         else if (tstatus == CHECK_CONDITION
+                  && SCpnt->device->type == TYPE_DISK
+                  && (SCpnt->sense_buffer[2] & 0xf) == RECOVERED_ERROR)
+            status = DID_BUS_BUSY << 16;
 
-      tstatus = status_byte(spp->target_status);
+         else
+            status = DID_OK << 16;
 
-      switch (spp->adapter_status) {
-         case ASOK:     /* status OK */
+         if (tstatus == GOOD)
+            HD(j)->target_redo[SCpnt->target][SCpnt->channel] = FALSE;
+
+         if (spp->target_status && SCpnt->device->type == TYPE_DISK)
+            printk("%s: ihdlr, target %d.%d:%d, pid %ld, "\
+                   "target_status 0x%x, sense key 0x%x.\n", BN(j),
+                   SCpnt->channel, SCpnt->target, SCpnt->lun,
+                   SCpnt->pid, spp->target_status,
+                   SCpnt->sense_buffer[2]);
+
+         HD(j)->target_to[SCpnt->target][SCpnt->channel] = 0;
+
+         if (HD(j)->last_retried_pid == SCpnt->pid) HD(j)->retries = 0;
+
+         break;
+      case ASST:     /* Selection Time Out */
+      case 0x02:     /* Command Time Out   */
 
-            /* Forces a reset if a disk drive keeps returning BUSY */
-            if (tstatus == BUSY && SCpnt->device->type != TYPE_TAPE) 
-               status = DID_ERROR << 16;
-
-            /* If there was a bus reset, redo operation on each target */
-            else if (tstatus != GOOD && SCpnt->device->type == TYPE_DISK
-                     && HD(j)->target_redo[SCpnt->target][SCpnt->channel])
-               status = DID_BUS_BUSY << 16;
-
-            /* Works around a flaw in scsi.c */
-            else if (tstatus == CHECK_CONDITION
-                     && SCpnt->device->type == TYPE_DISK
-                     && (SCpnt->sense_buffer[2] & 0xf) == RECOVERED_ERROR)
-               status = DID_BUS_BUSY << 16;
-
-            else
-               status = DID_OK << 16;
-
-            if (tstatus == GOOD)
-               HD(j)->target_redo[SCpnt->target][SCpnt->channel] = FALSE;
-
-            if (spp->target_status && SCpnt->device->type == TYPE_DISK)
-               printk("%s: ihdlr, target %d.%d:%d, pid %ld, "\
-                      "target_status 0x%x, sense key 0x%x.\n", BN(j), 
-                      SCpnt->channel, SCpnt->target, SCpnt->lun, 
-                      SCpnt->pid, spp->target_status, 
-                      SCpnt->sense_buffer[2]);
-
-            HD(j)->target_to[SCpnt->target][SCpnt->channel] = 0;
-
-            if (HD(j)->last_retried_pid == SCpnt->pid) HD(j)->retries = 0;
-
-            break;
-         case ASST:     /* Selection Time Out */
-         case 0x02:     /* Command Time Out   */
-
-            if (HD(j)->target_to[SCpnt->target][SCpnt->channel] > 1)
-               status = DID_ERROR << 16;
-            else {
-               status = DID_TIME_OUT << 16;
-               HD(j)->target_to[SCpnt->target][SCpnt->channel]++;
-               }
-
-            break;
-
-         /* Perform a limited number of internal retries */
-         case 0x03:     /* SCSI Bus Reset Received */
-         case 0x04:     /* Initial Controller Power-up */
-
-            for (c = 0; c <= sh[j]->max_channel; c++) 
-               for (k = 0; k < sh[j]->max_id; k++) 
-                  HD(j)->target_redo[k][c] = TRUE;
-
-            if (SCpnt->device->type != TYPE_TAPE
-                && HD(j)->retries < MAX_INTERNAL_RETRIES) {
-               status = DID_BUS_BUSY << 16;
-               HD(j)->retries++;
-               HD(j)->last_retried_pid = SCpnt->pid;
-               }
-            else 
-               status = DID_ERROR << 16;
-
-            break;
-         case 0x05:     /* Unexpected Bus Phase */
-         case 0x06:     /* Unexpected Bus Free */
-         case 0x07:     /* Bus Parity Error */
-         case 0x08:     /* SCSI Hung */
-         case 0x09:     /* Unexpected Message Reject */
-         case 0x0a:     /* SCSI Bus Reset Stuck */
-         case 0x0b:     /* Auto Request-Sense Failed */
-         case 0x0c:     /* Controller Ram Parity Error */
-         default:
+         if (HD(j)->target_to[SCpnt->target][SCpnt->channel] > 1)
             status = DID_ERROR << 16;
-            break;
-         }
+         else {
+            status = DID_TIME_OUT << 16;
+            HD(j)->target_to[SCpnt->target][SCpnt->channel]++;
+            }
+
+         break;
 
-      SCpnt->result = status | spp->target_status;
-      HD(j)->iocount++;
+      /* Perform a limited number of internal retries */
+      case 0x03:     /* SCSI Bus Reset Received */
+      case 0x04:     /* Initial Controller Power-up */
+
+         for (c = 0; c <= sh[j]->max_channel; c++)
+            for (k = 0; k < sh[j]->max_id; k++)
+               HD(j)->target_redo[k][c] = TRUE;
+
+         if (SCpnt->device->type != TYPE_TAPE
+             && HD(j)->retries < MAX_INTERNAL_RETRIES) {
+            status = DID_BUS_BUSY << 16;
+            HD(j)->retries++;
+            HD(j)->last_retried_pid = SCpnt->pid;
+            }
+         else
+            status = DID_ERROR << 16;
+
+         break;
+      case 0x05:     /* Unexpected Bus Phase */
+      case 0x06:     /* Unexpected Bus Free */
+      case 0x07:     /* Bus Parity Error */
+      case 0x08:     /* SCSI Hung */
+      case 0x09:     /* Unexpected Message Reject */
+      case 0x0a:     /* SCSI Bus Reset Stuck */
+      case 0x0b:     /* Auto Request-Sense Failed */
+      case 0x0c:     /* Controller Ram Parity Error */
+      default:
+         status = DID_ERROR << 16;
+         break;
+      }
+
+   SCpnt->result = status | spp->target_status;
 
 #if defined (DEBUG_INTERRUPT)
-      if (SCpnt->result || do_trace)
+   if (SCpnt->result || do_trace)
 #else
-      if ((spp->adapter_status != ASOK && HD(j)->iocount >  1000) ||
-          (spp->adapter_status != ASOK && 
-          spp->adapter_status != ASST && HD(j)->iocount <= 1000) ||
-          do_trace || msg_byte(spp->target_status))
+   if ((spp->adapter_status != ASOK && HD(j)->iocount >  1000) ||
+       (spp->adapter_status != ASOK &&
+        spp->adapter_status != ASST && HD(j)->iocount <= 1000) ||
+        do_trace || msg_byte(spp->target_status))
 #endif
-         printk("%s: ihdlr, mbox %2d, err 0x%x:%x,"\
-                " target %d.%d:%d, pid %ld, reg 0x%x, count %d.\n",
-                BN(j), i, spp->adapter_status, spp->target_status,
-                SCpnt->channel, SCpnt->target, SCpnt->lun, SCpnt->pid,
-                reg, HD(j)->iocount);
-
-      /* Set the command state to inactive */
-      SCpnt->host_scribble = NULL;
+      printk("%s: ihdlr, mbox %2d, err 0x%x:%x,"\
+             " target %d.%d:%d, pid %ld, reg 0x%x, count %d.\n",
+             BN(j), i, spp->adapter_status, spp->target_status,
+             SCpnt->channel, SCpnt->target, SCpnt->lun, SCpnt->pid,
+             reg, HD(j)->iocount);
 
-      SCpnt->scsi_done(SCpnt);
-
-      }   /* Mailbox loop */
+   /* Set the command state to inactive */
+   SCpnt->host_scribble = NULL;
 
-   if (n_ready > 1)
-      printk("%s: ihdlr, multiple commands (%d) completed.\n", BN(j), n_ready);
+   SCpnt->scsi_done(SCpnt);
 
    if (do_trace) printk("%s: ihdlr, exit, irq %d, count %d.\n", BN(j), irq,
                         HD(j)->iocount);
@@ -1938,18 +1955,22 @@
 
 static void do_interrupt_handler(int irq, void *shap, struct pt_regs *regs) {
 
-#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,95)
+   unsigned int j;
+
+   /* Check if the interrupt must be processed by this handler */
+   if ((j = (unsigned int)((char *)shap - sha)) >= num_boards) return;
 
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,95)
+   {
    unsigned long flags;
    spin_lock_irqsave(&io_request_lock, flags);
-   ihdlr(irq, shap, regs);
+   ihdlr(irq, j);
    spin_unlock_irqrestore(&io_request_lock, flags);
-
+   }
 #else
-
-   ihdlr(irq, shap, regs);
-
+   ihdlr(irq, j);
 #endif
+
 }
 
 int eata2x_release(struct Scsi_Host *shpnt) {
@@ -1960,11 +1981,11 @@
    cli();
 
    for (j = 0; sh[j] != NULL && sh[j] != shpnt; j++);
-    
+
    if (sh[j] == NULL) panic("%s: release, invalid Scsi_Host pointer.\n",
                             driver_name);
 
-   for (i = 0; i < sh[j]->can_queue; i++) 
+   for (i = 0; i < sh[j]->can_queue; i++)
       if ((&HD(j)->cp[i])->sglist) kfree((&HD(j)->cp[i])->sglist);
 
    free_irq(sh[j]->irq, &sha[j]);

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov