diff -u --recursive --new-file v2.0.32/linux/Documentation/Configure.help linux/Documentation/Configure.help
--- v2.0.32/linux/Documentation/Configure.help	Fri Sep  5 20:43:58 1997
+++ linux/Documentation/Configure.help	Wed Dec 10 18:21:47 1997
@@ -1401,15 +1401,77 @@
   whenever you want). If you want to compile it as a module, say M
   here and read Documentation/modules.txt.
 
-Adaptec AHA274X/284X/294X support
+Adaptec AIC7xxx chipset SCSI controller support
 CONFIG_SCSI_AIC7XXX
-  Information about this SCSI host adapter is contained in
-  drivers/scsi/README.aic7xxx and in the SCSI-HOWTO, available via ftp
-  (user: anonymous) at sunsite.unc.edu:/pub/Linux/docs/HOWTO. If it
-  doesn't work out of the box, you may have to change some settings in
-  drivers/scsi/aic7xxx.h.  If you want to compile this as a module ( =
-  code which can be inserted in and removed from the running kernel
-  whenever you want), say M here and read Documentation/modules.txt.
+  This is support for the various aic7xxx based Adaptec SCSI controllers.
+  These include the 274x EISA cards, 284x VLB cards, 294x PCI cards,
+  394x PCI cards, 3985 PCI card, and several versions of the Adaptec
+  built-in SCSI controllers on various PC motherboards.  Information on
+  the configuration options for this controller can be found by checking
+  the help file for each of the available configuration options.
+
+Enable tagged command queueing
+CONFIG_AIC7XXX_TAGGED_QUEUEING
+  Tagged command queueing is used to allow acceptable devices to have more
+  than one SCSI command active on the SCSI bus at the same time.  Regardless
+  of this option setting, only devices that report they are capable of
+  tagged queueing will use this support.  This option is highly recommended
+  if you use any SCSI hard drives on your aic7xxx SCSI controller for
+  performance reasons.  Default: Y
+
+Override driver defaults for commands per LUN
+CONFIG_OVERRIDE_CMDS
+  Use this option to allow you to override the default maximum number of
+  commands that a single device on the aic7xxx controller is allowed to have
+  active at one time.  This option only effects tagged queueing capable
+  devices.  The driver uses a "failsafe" value of 8 by default.  This is
+  much lower than many devices can handle, but left in place for safety sake.
+  Default: N
+
+Maximum number of commands per LUN
+CONFIG_AIC7XXX_CMDS_PER_LUN
+  Specify the maximum number of commands per lun you would like to allocate
+  per device.  Reasonable figures are in the range of 14 to 32 commands per
+  device, but depending on hardware could be increased or decreased from
+  that figure.  If the number is too high for any particular device, the
+  driver will automatically compensate usually after only 10 minutes of
+  uptime and will issue a message to alert you to the fact that the number
+  of commands for that device has been reduced.  It will not hinder
+  performance if a portion of your devices eventually have their commands
+  per lun reduced, but is a waste of memory if all of your devices end
+  up reducing this number down to a more reasonable figure.  Default: 24
+
+Enable SCB paging
+CONFIG_AIC7XXX_PAGE_ENABLE
+  This option allows the driver to issue more commands to the controller
+  than it has physical space to store.  Since some aic7xxx chipsets can only
+  store 3 commands, and the majority can only store 16, not enabling this
+  capability can effectively negate any performance increase you might get
+  from enabling Tagged Queueing.  Default: Y
+
+Collect statistics to report in /proc
+CONFIG_AIC7XXX_PROC_STATS
+  This option tells the driver to keep track of how many commands have been
+  sent to each particular device and report that information to the user
+  via the /proc/scsi/aic7xxx/x file, where x is the number of the aic7xxx
+  controller you want the information on.  This adds a small amount of
+  overhead to each and every SCSI command the aic7xxx driver handles, so if
+  you aren't really interested in this information, it is best to leave it
+  disabled.  Default: N
+
+Delay in seconds after SCSI bus reset
+CONFIG_AIC7XXX_RESET_DELAY
+  This sets how long the driver will wait after resetting the SCSI bus before
+  attempting to communicate with the devices on the SCSI bus again.  This
+  delay will be used during the reset phase at bootup time as well as after
+  any reset that might occur during normal operation.  Reasonable numbers
+  range anywhere from 5 to 15 seconds depending on your devices.  DAT tape
+  drives are notorious for needing more time after a bus reset to be
+  ready for the next command, but most hard drives and CD-ROM devices are
+  ready in only a few seconds.  This option has a maximum upper limit of
+  20 seconds to avoid bad interactions between the aic7xxx driver and the
+  rest of the linux kernel.  The default value is a "failsafe" value that
+  should work with just about any device.  Default: 15
 
 BusLogic SCSI support
 CONFIG_SCSI_BUSLOGIC
diff -u --recursive --new-file v2.0.32/linux/Makefile linux/Makefile
--- v2.0.32/linux/Makefile	Tue Dec  2 13:52:30 1997
+++ linux/Makefile	Wed Nov 26 15:31:03 1997
@@ -1,6 +1,6 @@
 VERSION = 2
 PATCHLEVEL = 0
-SUBLEVEL = 32
+SUBLEVEL = 33
 
 ARCH = i386
 
diff -u --recursive --new-file v2.0.32/linux/arch/i386/kernel/head.S linux/arch/i386/kernel/head.S
--- v2.0.32/linux/arch/i386/kernel/head.S	Tue Dec  2 13:52:31 1997
+++ linux/arch/i386/kernel/head.S	Tue Dec  2 13:58:53 1997
@@ -260,7 +260,7 @@
 	movw %dx,%ax		/* selector = 0x0010 = cs */
 	movw $0x8E00,%dx	/* interrupt gate - dpl=0, present */
 
-	lea SYMBOL_NAME(__idt),%edi
+	lea SYMBOL_NAME(idt),%edi
 	mov $256,%ecx
 rp_sidt:
 	movl %eax,(%edi)
@@ -342,6 +342,11 @@
 	.long SYMBOL_NAME(init_user_stack)+4096
 	.long KERNEL_DS
 
+/* NOTE: keep the idt short behind the above '.org 0x6000'
+	 It must fit completely within _one_ page */
+ENTRY(idt)
+	.fill 256,8,0		# idt is uninitialized
+
 /* This is the default interrupt "handler" :-) */
 int_msg:
 	.asciz "Unknown interrupt\n"
@@ -376,10 +381,7 @@
 .word 0
 idt_descr:
 	.word 256*8-1		# idt contains 256 entries
-	.long 0xc0000000+SYMBOL_NAME(__idt)
-
-ENTRY(__idt)
-	.fill 256,8,0		# idt is uninitialized
+	.long 0xc0000000+SYMBOL_NAME(idt)
 
 	ALIGN
 .word 0
diff -u --recursive --new-file v2.0.32/linux/arch/i386/kernel/traps.c linux/arch/i386/kernel/traps.c
--- v2.0.32/linux/arch/i386/kernel/traps.c	Tue Dec  2 13:52:31 1997
+++ linux/arch/i386/kernel/traps.c	Tue Dec  2 13:58:53 1997
@@ -337,7 +337,6 @@
 
 #endif /* CONFIG_MATH_EMULATION */
 
-struct desc_struct *idt = __idt+0;
 struct {
 	unsigned short limit;
 	unsigned long addr __attribute__((packed));
@@ -348,33 +347,35 @@
 	pgd_t * pgd;
 	pmd_t * pmd;
 	pte_t * pte;
-	unsigned long twopage;
-	struct desc_struct *new_idt;
+	unsigned long page;
+	unsigned long idtpage = (unsigned long)idt;
+	struct desc_struct *alias_idt;
 
-	printk("moving IDT ... ");
+	printk("alias mapping IDT readonly ... ");
 
-	twopage = (unsigned long) vmalloc (2*PAGE_SIZE);
-
-	new_idt = (void *)(twopage + 4096-7*8);
-
-	memcpy(new_idt,idt,256*8);
+		/* just to get free address space */
+	page = (unsigned long) vmalloc (PAGE_SIZE);
 
+	alias_idt = (void *)(page + (idtpage & ~PAGE_MASK));
 	idt_descriptor.limit = 256*8-1;
-	idt_descriptor.addr = VMALLOC_VMADDR(new_idt);
-
-	 __asm__ __volatile__("\tlidt %0": "=m" (idt_descriptor));
-	 idt = new_idt;
+	idt_descriptor.addr = VMALLOC_VMADDR(alias_idt);
 
 	/*
-	 * Unmap lower page:
+	 * alias map the original idt to the alias page:
 	 */
-	twopage = VMALLOC_VMADDR(twopage);
-	pgd = pgd_offset(current->mm, twopage);
-	pmd = pmd_offset(pgd, twopage);
-	pte = pte_offset(pmd, twopage);
-
-	pte_clear(pte);
+	page = VMALLOC_VMADDR(page);
+	pgd = pgd_offset(&init_mm, page);
+	pmd = pmd_offset(pgd, page);
+	pte = pte_offset(pmd, page);
+		/* give memory back to the pool, don't need it */
+	free_page(pte_page(*pte));
+		/* ... and set the readonly alias */
+	set_pte(pte, mk_pte(idtpage  & PAGE_MASK, PAGE_KERNEL));
+	*pte = pte_wrprotect(*pte);
 	flush_tlb_all();
+
+		/* now we have the mapping ok, we can do LIDT */
+	 __asm__ __volatile__("\tlidt %0": "=m" (idt_descriptor));
 
 	printk(" ... done\n");
 }
diff -u --recursive --new-file v2.0.32/linux/arch/i386/mm/fault.c linux/arch/i386/mm/fault.c
--- v2.0.32/linux/arch/i386/mm/fault.c	Tue Dec  2 13:52:31 1997
+++ linux/arch/i386/mm/fault.c	Tue Dec  2 13:58:53 1997
@@ -21,128 +21,10 @@
 
 extern void die_if_kernel(const char *,struct pt_regs *,long);
 
-asmlinkage void do_divide_error (struct pt_regs *, unsigned long);
-asmlinkage void do_debug (struct pt_regs *, unsigned long);
-asmlinkage void do_nmi (struct pt_regs *, unsigned long);
-asmlinkage void do_int3 (struct pt_regs *, unsigned long);
-asmlinkage void do_overflow (struct pt_regs *, unsigned long);
-asmlinkage void do_bounds (struct pt_regs *, unsigned long);
 asmlinkage void do_invalid_op (struct pt_regs *, unsigned long);
-asmlinkage void do_general_protection (struct pt_regs *, unsigned long);
 
 extern int pentium_f00f_bug;
 
-static int handle_intx_eip_adjust(struct pt_regs *regs)
-{
-	unsigned char *addr, *csp = 0;
-	int wrap = 0;
-	int count = 8; /* only check for reasonable number of bytes
-			 * else we do it the save 'simple way' */
-	unsigned long _eip;
-#define XX_WRAP(x) (wrap ? *((unsigned short *)&x) : x)
-
-	/* We rely on being able to access the memory pointed to by cs:eip
-	 * and the bytes behind it up to the faulting instruction,
-	 * because we just got an exception for this instruction and
-	 * hence the memory should just be successfully accessed.
-	 * In case of crossing a page boundary or when accessing kernel space
-	 * we just do the simple fix (increase eip by one).
-	 * This assumption also obsoletes checking of segment limit.
-	 * ( should be veryfied, however, if this assumption is true )
-	 */
-
-	if (regs->cs == KERNEL_CS) {
-		/* not what we expect */
-		regs->eip++;
-		return 0;
-	}
-
-	if (regs->eflags & VM_MASK) {
-		/* we have real mode type selector */
-		wrap = 1;
-		csp = (unsigned char *)((unsigned long)regs->cs << 4);
-	}
-	else if (regs->cs & 4) {
-		/* we have a LDT selector */
-		struct desc_struct *p, *ldt = current->ldt;
-		if (!ldt)
-			ldt = (struct desc_struct*) &default_ldt;
-		p = ldt + (regs->cs >> 3);
-		csp = (unsigned char *)((p->a >> 16) | ((p->b & 0xff) << 16) | (p->b & 0xFF000000));
-		if (!(p->b & 0x400000))
-			wrap = 1;	/* 16-bit segment */
-	}
-
-	_eip = regs->eip;
-	addr = csp+XX_WRAP(_eip);
-	while (count-- > 0) {
-		if ((unsigned long)addr >= TASK_SIZE) {
-			/* accessing kernel space, do the simple case */
-			regs->eip++;
-			return 0;
-		}
-		switch (get_user(addr)) {
-
-			case 0xCC:	/* single byte INT3 */
-				XX_WRAP(_eip)++;
-				regs->eip = _eip;
-				return 0;
-
-			case 0xCD:	/* two byte INT 3 */
-				XX_WRAP(_eip)++;
-				/* fall through */
-			case 0xCE:	/* INTO, single byte */
-				XX_WRAP(_eip)++;
-				if ( (regs->eflags & VM_MASK)
-					&& ((regs->eflags & IOPL_MASK) != IOPL_MASK)) {
-					/* not allowed, do GP0 fault */
-					do_general_protection(regs, 0);
-					return -1;
-				}
-				regs->eip = _eip;
-				return 0;
-
-					/* the prefixes from the Intel patch */
-			case 0xF2 ... 0xF3:
-			case 0x2E:
-			case 0x36:
-			case 0x3E:
-			case 0x26:
-			case 0x64 ... 0x67:
-				break;	/* just skipping them */
-
-			default:
-				/* not what we handle here,
-				 * just doing the simple fix
-				 */
-				regs->eip++;
-				return 0;
-		}
-
-		if ( !(++XX_WRAP(_eip)) ) {
-			/* we wrapped around */
-			regs->eip++;
-			return 0;
-		}
-
-		addr = csp+XX_WRAP(_eip);
-		if ( !((unsigned long)addr & ~(PAGE_SIZE -1)) ) {
-			/* we would cross page boundary, not good,
-			 * doing the simple fix
-			 */
-			regs->eip++;
-			return 0;
-		}
-	}
-
-	/* if we come here something weird happened,
-	 * just doing the simple fix
-	 */
-	regs->eip++;
-	return 0;
-}
-
-
 /*
  * This routine handles page faults.  It determines the address,
  * and the problem, and then passes it off to one of the appropriate
@@ -245,22 +127,15 @@
 	 */
 	if ( pentium_f00f_bug ) {
 		unsigned long nr;
+		extern struct {
+			unsigned short limit;
+			unsigned long addr __attribute__((packed));
+		} idt_descriptor;
 
-		nr = (address - TASK_SIZE - (unsigned long) idt) >> 3;
+		nr = (address - idt_descriptor.addr) >> 3;
 
-		if (nr < 7) {
-			static void (*handler[])(struct pt_regs *, unsigned long) = {
-				do_divide_error,	/* 0 - divide overflow */
-				do_debug,		/* 1 - debug trap */
-				do_nmi,			/* 2 - NMI */
-				do_int3,		/* 3 - int 3 */
-				do_overflow,		/* 4 - overflow */
-				do_bounds,		/* 5 - bound range */
-				do_invalid_op };	/* 6 - invalid opcode */
-			if ((nr == 3) || (nr == 4))
-				if (handle_intx_eip_adjust(regs))
-					return;
-			handler[nr](regs, error_code);
+		if (nr == 6) {
+			do_invalid_op(regs, 0);
 			return;
 		}
 	}
diff -u --recursive --new-file v2.0.32/linux/drivers/char/ftape/ftape-ctl.c linux/drivers/char/ftape/ftape-ctl.c
--- v2.0.32/linux/drivers/char/ftape/ftape-ctl.c	Fri May 31 03:54:08 1996
+++ linux/drivers/char/ftape/ftape-ctl.c	Thu Dec 11 11:38:20 1997
@@ -371,7 +371,7 @@
 		TRACEx1(-1, "   Vendor id     : 0x%04x", drive_type.vendor_id);
 		TRACEx1(-1, "   Wakeup method : %s", methods[drive_type.wake_up].name);
 		TRACE(-1, "And a description of your tape drive to:");
-		TRACE(-1, "Kai Harrekilde-Petersen <khp@dolphinics.no>");
+		TRACE(-1, "Claus Heine <claus@momo.math.rwth-aachen.de>");
 		TRACE(-1, "==========================================");
 		drive_type.speed = 500;		/* deci-ips: very safe value */
 	} else {
@@ -393,7 +393,7 @@
 			TRACEx2(-1, "found: %s, expected: %s",
 				methods[drive_type.wake_up].name,
 			    methods[vendors[vendor_index].wake_up].name);
-			TRACE(-1, "please report this to <khp@dolphinics.no>");
+			TRACE(-1, "please report this to <claus@momo.math.rwth-aachen.de>");
 			TRACE(-1, "==========================================");
 		}
 	}
@@ -456,7 +456,7 @@
 		TRACEx1(-1, "drive : %s", drive_type.name);
 		TRACEx2(-1, "delta time = %d, length = %d", dt, length);
 		TRACEx1(-1, "has max tape speed of %d ips", drive_type.speed);
-		TRACE(-1, "please report this to <khp@dolphinics.no>");
+		TRACE(-1, "please report this to <claus@momo.math.rwth-aachen.de>");
 		TRACE(-1, "==========================================");
 	}
 	/*  time to go from bot to eot at normal speed (data rate):
diff -u --recursive --new-file v2.0.32/linux/drivers/char/ftape/kernel-interface.c linux/drivers/char/ftape/kernel-interface.c
--- v2.0.32/linux/drivers/char/ftape/kernel-interface.c	Fri May 31 03:54:08 1996
+++ linux/drivers/char/ftape/kernel-interface.c	Thu Dec 11 11:38:20 1997
@@ -128,6 +128,7 @@
 	printk(KERN_INFO "ftape-2.08 960314\n"
 	       KERN_INFO " (c) 1993-1995 Bas Laarhoven (bas@vimec.nl)\n"
 	       KERN_INFO " (c) 1995-1996 Kai Harrekilde-Petersen (khp@dolphinics.no)\n"
+	       KERN_INFO " (c) 1996-1997 Claus Heine (claus@momo.math.rwth-aachen.de)\n"
 	KERN_INFO " QIC-117 driver for QIC-40/80/3010/3020 tape drives\n"
 	       KERN_INFO " Compiled for kernel version %s"
 #ifdef MODVERSIONS
diff -u --recursive --new-file v2.0.32/linux/drivers/scsi/aic7xxx/aic7xxx.seq linux/drivers/scsi/aic7xxx/aic7xxx.seq
--- v2.0.32/linux/drivers/scsi/aic7xxx/aic7xxx.seq	Wed Aug 13 09:53:45 1997
+++ linux/drivers/scsi/aic7xxx/aic7xxx.seq	Wed Dec 10 18:21:47 1997
@@ -1121,15 +1121,10 @@
 dequeue_disc_scb:
 	mov	SCBPTR, DISCONNECTED_SCBH;
 /*
- * If we have a residual, then we are in the middle of some I/O
- * and we have to send this SCB back up to the kernel so that the
- * saved data pointers and residual information isn't lost.
+ * Whenever we are going to dequeue a disconnected SCB and wipe it out
+ * with a new SCB, we always send the SCB back to the kernel to make sure
+ * any residual info and any save/restore pointers are saved for later use..
  */
-	test	SCB_CONTROL, MUST_DMAUP_SCB jz . + 3;
-	and	SCB_CONTROL, ~MUST_DMAUP_SCB;
-	jmp	dma_up_scb;
-	test	SCB_RESID_SGCNT,0xff	jnz dma_up_scb;
-	cmp	SCB_LINKED_NEXT, SCB_LIST_NULL je unlink_disc_scb;
 dma_up_scb:
 	mvi	DMAPARAMS, FIFORESET;
 	mov	SCB_TAG		call dma_scb;
diff -u --recursive --new-file v2.0.32/linux/drivers/scsi/aic7xxx.c linux/drivers/scsi/aic7xxx.c
--- v2.0.32/linux/drivers/scsi/aic7xxx.c	Tue Dec  2 13:52:32 1997
+++ linux/drivers/scsi/aic7xxx.c	Fri Dec 12 09:19:52 1997
@@ -139,7 +139,7 @@
     0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL
 };
 
-#define AIC7XXX_C_VERSION  "$Revision: 4.1 $"
+#define AIC7XXX_C_VERSION  "$Revision: 4.1.1 $"
 
 #define NUMBER(arr)     (sizeof(arr) / sizeof(arr[0]))
 #define MIN(a,b)        (((a) < (b)) ? (a) : (b))
@@ -681,7 +681,7 @@
 /*
  * Maximum number of SG segments these cards can support.
  */
-#define	AIC7XXX_MAX_SG 27
+#define	AIC7XXX_MAX_SG 122
 
 /*
  * The maximum number of SCBs we could have for ANY type
@@ -737,7 +737,9 @@
 	SCB_MSGOUT_SDTR		= 0x0400,
 	SCB_MSGOUT_WDTR		= 0x0800,
 	SCB_ABORT		= 0x1000,
-	SCB_QUEUED_ABORT	= 0x2000
+	SCB_QUEUED_ABORT	= 0x2000,
+	SCB_RESET		= 0x4000,
+	SCB_WAS_BUSY		= 0x8000
 } scb_flag_type;
 
 struct aic7xxx_scb {
@@ -810,10 +812,14 @@
 #define USE_DEFAULTS            0x0080
 #define BIOS_ENABLED            0x0100
 #define IN_ISR                  0x0200
-#define IN_TIMEOUT              0x0400
+#define ABORT_PENDING           0x0400
 #define SHARED_SCBDATA          0x0800
 #define HAVE_SEEPROM            0x1000
+#define RESET_PENDING           0x2000
+#define IN_ABORT		0x4000
   unsigned int             flags;
+  unsigned long		   last_reset;
+  unsigned long		   reset_start;
   unsigned int             isr_count;        /* Interrupt count */
   unsigned short           needsdtr_copy;    /* default config */
   unsigned short           needsdtr;
@@ -846,12 +852,21 @@
     Scsi_Cmnd *tail;
   } completeq;
   struct aic7xxx_device_status {
+    unsigned char active_cmds;
+    unsigned char max_queue_depth;
+    unsigned char temp_queue_depth;
+    unsigned char last_queue_full;
+    unsigned char last_queue_full_count;
+    struct timer_list timer;
     long last_reset;
 #define  DEVICE_SUCCESS                 0x01
 #define  BUS_DEVICE_RESET_PENDING       0x02
+#define  DEVICE_TIMEOUT                 0x04
     int  flags;
     int  commands_sent;
-    int  active_cmds;
+    scb_queue_type delayed_scbs;
+    Scsi_Cmnd *scsi_cmnd0;
+    Scsi_Cmnd *scsi_cmnd1;
   } device_status[16];
 #ifdef AIC7XXX_PROC_STATS
   /*
@@ -939,14 +954,50 @@
 #  define debug_scb(x)
 #endif AIC7XXX_DEBUG
 
-#define TCL_OF_SCB(scb) (((scb->hscb)->target_channel_lun >> 4) & 0xf),  \
-                        (((scb->hscb)->target_channel_lun >> 3) & 0x01), \
+#define CTL_OF_SCB(scb) (((scb->hscb)->target_channel_lun >> 3) & 0x1),  \
+                        (((scb->hscb)->target_channel_lun >> 4) & 0xf), \
                         ((scb->hscb)->target_channel_lun & 0x07)
 
-#define TC_OF_SCB(scb) (((scb->hscb)->target_channel_lun >> 4) & 0xf),  \
-                       (((scb->hscb)->target_channel_lun >> 3) & 0x01)
+#define CTL_OF_CMD(cmd) ((cmd->channel) & 0x01),  \
+                        ((cmd->target) & 0x0f), \
+                        ((cmd->lun) & 0x07)
+
+static inline int
+CHAN_TO_INT(char chan)
+{
+  switch(chan)
+  {
+    case ALL_CHANNELS:
+	return(-1);
+    case 'B':
+    case 'b':
+        return(1);
+    case 'A':
+    case 'a':
+        return(0);
+    default:
+        printk(KERN_WARNING "aic7xxx: Bad usage of char channel.\n");
+        return(0);
+  }
+}
+
+static inline char
+INT_TO_CHAN(int chan)
+{
+  switch(chan)
+  {
+    case -1:
+	return(ALL_CHANNELS);
+    case 1:
+        return('B');
+    case 0:
+        return('A');
+    default:
+        printk(KERN_WARNING "aic7xxx: Bad usage of int channel.\n");
+        return('A');
+  }
+}
 
-#define CHAN_TO_INT(chan) ((chan) == 'A' ? 0 : 1)
 
 #define TARGET_INDEX(cmd)  ((cmd)->target | ((cmd)->channel << 3))
 
@@ -1060,7 +1111,7 @@
         }
         else
         {
-          *(options[i].flag) = !0;
+          *(options[i].flag) += 1;
         }
       }
     }
@@ -1448,10 +1499,10 @@
   unsigned char ultra_enb, sxfrctl0;
 
   /*
-   * If the period is 0, then the device is requesting asynchronous
+   * If the offset is 0, then the device is requesting asynchronous
    * transfers.
    */
-  if (*period != 0)
+  if ((*period != 0) && (*offset != 0))
   {
     for (i = 0; i < num_aic7xxx_syncrates; i++)
     {
@@ -1476,8 +1527,8 @@
 
         if (aic7xxx_verbose)
         {
-          printk("scsi%d: Target %d, channel %c, now synchronous at %sMHz, "
-                 "offset %d.\n", p->host_no, target, channel,
+          printk("(scsi%d:%d:%d:%d) Synchronous at %sMHz, "
+                 "offset %d.\n", p->host_no, CHAN_TO_INT(channel), target, 0,
                  aic7xxx_syncrates[i].english, *offset);
         }
         break;
@@ -1495,8 +1546,8 @@
     *offset = 0;
     if (aic7xxx_verbose)
     {
-      printk("scsi%d: Target %d, channel %c, using asynchronous transfers.\n",
-             p->host_no, target, channel);
+      printk("(scsi%d:%d:%d:%d) Using asynchronous transfers.\n",
+             p->host_no, CHAN_TO_INT(channel), target, 0);
     }
   }
 
@@ -1550,10 +1601,14 @@
 static inline void
 scbq_insert_head(scb_queue_type *queue, struct aic7xxx_scb *scb)
 {
+  unsigned long processor_flags;
+  save_flags(processor_flags);
+  cli();
   scb->q_next = queue->head;
   queue->head = scb;
   if (queue->tail == NULL)       /* If list was empty, update tail. */
     queue->tail = queue->head;
+  restore_flags(processor_flags);
 }
 
 /*+F*************************************************************************
@@ -1567,10 +1622,14 @@
 static inline void
 scbq_remove_head(scb_queue_type *queue)
 {
+  unsigned long processor_flags;
+  save_flags(processor_flags);
+  cli();
   if (queue->head != NULL)
     queue->head = queue->head->q_next;
   if (queue->head == NULL)       /* If list is now empty, update tail. */
     queue->tail = NULL;
+  restore_flags(processor_flags);
 }
 
 /*+F*************************************************************************
@@ -1584,6 +1643,9 @@
 static inline void
 scbq_remove(scb_queue_type *queue, struct aic7xxx_scb *scb)
 {
+  unsigned long processor_flags;
+  save_flags(processor_flags);
+  cli();
   if (queue->head == scb)
   {
     /* At beginning of queue, remove from head. */
@@ -1612,6 +1674,7 @@
       }
     }
   }
+  restore_flags(processor_flags);
 }
 
 /*+F*************************************************************************
@@ -1625,6 +1688,9 @@
 static inline void
 scbq_insert_tail(scb_queue_type *queue, struct aic7xxx_scb *scb)
 {
+  unsigned long processor_flags;
+  save_flags(processor_flags);
+  cli();
   scb->q_next = NULL;
   if (queue->tail != NULL)       /* Add the scb at the end of the list. */
     queue->tail->q_next = scb;
@@ -1632,6 +1698,7 @@
   queue->tail = scb;             /* Update the tail. */
   if (queue->head == NULL)       /* If list was empty, update head. */
     queue->head = queue->tail;
+  restore_flags(processor_flags);
 }
 
 /*+F*************************************************************************
@@ -1653,10 +1720,6 @@
   int slun = scb->hscb->target_channel_lun & 0x07;
   int match;
 
-#ifdef AIC7XXX_DEBUG_ABORT
-  printk("scsi%d: (targ %d/chan %c) matching scb to (targ %d/chan %c)\n",
-         scb->cmd->device->host->host_no, target, channel, targ, chan);
-#endif
   match = ((chan == channel) || (channel == ALL_CHANNELS));
   if (match != 0)
     match = ((targ == target) || (target == ALL_TARGETS));
@@ -1665,6 +1728,24 @@
   if (match != 0)
     match = ((tag == scb->hscb->tag) || (tag == SCB_LIST_NULL));
 
+  if (aic7xxx_verbose > 4)
+  {
+    if (match)
+    {
+      printk(KERN_INFO "(scsi%d:%d:%d:%d:tag%d) matches search criteria"
+        " (scsi%d:%d:%d:%d:tag%d)\n", scb->cmd->device->host->host_no,
+        CTL_OF_SCB(scb), scb->hscb->tag, scb->cmd->device->host->host_no,
+        CHAN_TO_INT(channel), target, lun, tag);
+    }
+    else
+    {
+      printk(KERN_INFO "(scsi%d:%d:%d:%d:tag%d) doesn't match search criteria"
+        " (scsi%d:%d:%d:%d:tag%d)\n", scb->cmd->device->host->host_no,
+        CTL_OF_SCB(scb), scb->hscb->tag, scb->cmd->device->host->host_no,
+        CHAN_TO_INT(channel), target, lun, tag);
+    }
+  }
+
   return (match);
 }
 
@@ -1707,6 +1788,7 @@
   prev = inb(p->base + SCB_PREV);
 
   outb(0, p->base + SCB_CONTROL);
+  outb(SCB_LIST_NULL, p->base + SCB_TAG);
 
   aic7xxx_add_curscb_to_free_list(p);
 
@@ -1834,12 +1916,6 @@
 {
   struct aic7xxx_scb   *scbp = NULL;
   struct aic7xxx_hwscb *hscbp = NULL;
-#ifdef AGRESSIVE
-  long processor_flags;
-
-  save_flags(processor_flags);
-  cli();
-#endif
 
   scbp = p->scb_data->free_scbs.head;
   if (scbp != NULL)
@@ -1871,16 +1947,7 @@
       }
     }
   }
-#ifdef AIC7XXX_DEBUG
-  if (scbp != NULL)
-  {
-    p->activescbs++;
-  }
-#endif
 
-#ifdef AGRESSIVE
-  restore_flags(processor_flags);
-#endif
   return (scbp);
 }
 
@@ -1896,11 +1963,12 @@
 static inline void
 aic7xxx_queue_cmd_complete(struct aic7xxx_host *p, Scsi_Cmnd *cmd)
 {
-  if (p->completeq.tail == NULL)
-    p->completeq.head = cmd;
-  else
-    p->completeq.tail->host_scribble = (char *) cmd;
-  p->completeq.tail = cmd;
+  unsigned int flags;
+  save_flags(flags);
+  cli();
+  cmd->host_scribble = (char *)p->completeq.head;
+  p->completeq.head = cmd;
+  restore_flags(flags);
 }
 
 /*+F*************************************************************************
@@ -1914,16 +1982,20 @@
 aic7xxx_done_cmds_complete(struct aic7xxx_host *p)
 {
   Scsi_Cmnd *cmd;
-
+  unsigned int processor_flags;
+  
+  save_flags(processor_flags);
+  cli();
   while (p->completeq.head != NULL)
   {
     cmd = p->completeq.head;
     p->completeq.head = (Scsi_Cmnd *)cmd->host_scribble;
     cmd->host_scribble = NULL;
-    p->device_status[TARGET_INDEX(cmd)].active_cmds--;
+    restore_flags(processor_flags);
     cmd->scsi_done(cmd);
+    cli();
   }
-  p->completeq.tail = NULL;
+  restore_flags(processor_flags);
 }
 
 /*+F*************************************************************************
@@ -1937,11 +2009,8 @@
 aic7xxx_free_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
 {
   struct aic7xxx_hwscb *hscb;
-  long flags;
 
   hscb = scb->hscb;
-  save_flags(flags);
-  cli();
 
   scb->flags = SCB_FREE;
   scb->cmd = NULL;
@@ -1949,11 +2018,6 @@
   hscb->target_status = 0;
 
   scbq_insert_head(&p->scb_data->free_scbs, scb);
-#ifdef AIC7XXX_DEBUG
-  p->activescbs--;  /* For debugging purposes. */
-#endif
-
-  restore_flags(flags);
 }
 
 /*+F*************************************************************************
@@ -1967,17 +2031,23 @@
 aic7xxx_done(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
 {
   Scsi_Cmnd *cmd = scb->cmd;
+  int tindex = TARGET_INDEX(cmd);
+  struct aic7xxx_scb *scbp;
+  unsigned char queue_depth;
 
   if (scb->flags & SCB_RECOVERY_SCB)
   {
-    p->flags &= ~IN_TIMEOUT;
+    p->flags &= ~ABORT_PENDING;
   }
-  if (cmd->result == DID_OK)
+  if (scb->flags & SCB_RESET)
   {
-    if (scb->flags & SCB_ABORTED)
-    {
-      cmd->result = (DID_RESET << 16);
-    }
+      cmd->result = (DID_RESET << 16) | (SUGGEST_RETRY << 24) | 
+	(cmd->result & 0xffff);
+  }
+  else if (scb->flags & SCB_ABORT)
+  {
+      cmd->result = (DID_RESET << 16) | (SUGGEST_RETRY << 24) | 
+	(cmd->result & 0xffff);
   }
   if ((scb->flags & (SCB_MSGOUT_WDTR | SCB_MSGOUT_SDTR)) != 0)
   {
@@ -1985,7 +2055,7 @@
     int message_error = FALSE;
 
     mask = 0x01 << TARGET_INDEX(scb->cmd);
-
+ 
     /*
      * Check to see if we get an invalid message or a message error
      * after failing to negotiate a wide or sync transfer message.
@@ -2016,6 +2086,34 @@
       }
     }
   }
+  queue_depth = (p->device_status[tindex].timer.expires) ?
+		 p->device_status[tindex].temp_queue_depth :
+		 p->device_status[tindex].max_queue_depth;
+  scbp = p->device_status[tindex].delayed_scbs.head;
+  if ( (scbp != NULL) && 
+       (queue_depth > p->device_status[tindex].active_cmds) )
+  {
+    scbq_remove_head(&p->device_status[tindex].delayed_scbs);
+    scbq_insert_tail(&p->waiting_scbs, scbp);
+    scbp = p->device_status[tindex].delayed_scbs.head;
+    if ( (scbp != NULL) && 
+         (queue_depth > (p->device_status[tindex].active_cmds + 1)) )
+    {
+      scbq_remove_head(&p->device_status[tindex].delayed_scbs);
+      scbq_insert_tail(&p->waiting_scbs, scbp);
+    }
+  }
+  if ( (p->device_status[tindex].timer.expires) &&
+      ((p->device_status[tindex].active_cmds == 1) ||
+       (p->device_status[tindex].max_queue_depth ==
+	p->device_status[tindex].temp_queue_depth)) )
+  {
+    del_timer(&p->device_status[tindex].timer);
+    p->device_status[tindex].timer.expires = 0;
+    p->device_status[tindex].temp_queue_depth = 
+          p->device_status[tindex].max_queue_depth;
+  }
+  p->device_status[tindex].active_cmds--;
   aic7xxx_free_scb(p, scb);
   aic7xxx_queue_cmd_complete(p, cmd);
 
@@ -2030,8 +2128,7 @@
      * XXX: for each command, but apparently that's too difficult.
      */
     actual = aic7xxx_length(cmd, 0);
-    if (!(scb->flags & (SCB_ABORTED | SCB_SENSE)) && (actual > 0)
-        && (aic7xxx_error(cmd) == 0))
+    if ((actual > 0) && (((cmd->result >> 16) & 0xf) == DID_OK))
     {
       struct aic7xxx_xferstats *sp;
       long *ptr;
@@ -2082,20 +2179,25 @@
 aic7xxx_run_done_queue(struct aic7xxx_host *p, /*complete*/ int complete)
 {
   struct aic7xxx_scb *scb;
-  int i;
+  int i, found = 0;
 
   for (i = 0; i < p->scb_data->numscbs; i++)
   {
     scb = p->scb_data->scb_array[i];
     if (scb->flags & SCB_QUEUED_FOR_DONE)
     {
-#ifdef AIC7XXX_DEBUG_ABORT
-      printk("(scsi%d:%d:%d) Aborting scb %d\n",
-             p->host_no, TC_OF_SCB(scb), scb->hscb->tag);
-#endif
+      if (aic7xxx_verbose > 3)
+        printk("(scsi%d:%d:%d:%d) Aborting scb %d\n",
+             p->host_no, CTL_OF_SCB(scb), scb->hscb->tag);
+      found++;
       aic7xxx_done(p, scb);
     }
   }
+  if (aic7xxx_verbose > 1)
+  {
+    printk(KERN_WARNING "(scsi%d:-1:-1:-1) %d commands found and queued for "
+	"completion.\n", p->host_no, found);
+  }
   if (complete)
   {
     aic7xxx_done_cmds_complete(p);
@@ -2127,6 +2229,7 @@
    * Clear the necessary fields
    */
   outb(0, p->base + SCB_CONTROL);
+  outb(SCB_LIST_NULL, p->base + SCB_TAG);
 
   aic7xxx_add_curscb_to_free_list(p);
 
@@ -2153,9 +2256,8 @@
    * system that the command has been aborted.
    */
   outb(curscb, p->base + SCBPTR);
-  scb->flags |= SCB_ABORTED | SCB_QUEUED_FOR_DONE;
+  scb->flags |= SCB_RESET | SCB_QUEUED_FOR_DONE;
   scb->flags &= ~SCB_ACTIVE;
-  scb->cmd->result = (DID_RESET << 16);
 
   return (next);
 }
@@ -2170,7 +2272,7 @@
  *-F*************************************************************************/
 static int
 aic7xxx_search_qinfifo(struct aic7xxx_host *p, int target, char channel,
-    int lun, unsigned char tag, int flags, int requeue)
+    int lun, unsigned char tag, int flags, int requeue, scb_queue_type *queue)
 {
   unsigned char saved_queue[AIC7XXX_MAXSCB];
   int      queued = inb(p->base + QINCNT) & p->qcntmask;
@@ -2196,12 +2298,7 @@
        }
        else
        {
-         scbp->flags = flags;
-         scbp->flags &= ~SCB_ACTIVE;
-         /*
-          * XXX - Don't know what error to use here.
-          */
-         aic7xxx_error(scbp->cmd) = DID_RESET;
+         scbp->flags = flags | (scbp->flags & SCB_RECOVERY_SCB);
        }
        i--;
        found++;
@@ -2220,8 +2317,12 @@
       /*
        * XXX - Shouldn't we be adding this to the free list?
        */
-      scbq_insert_head(&p->waiting_scbs, scbp);
-      scbp->flags |= SCB_WAITINGQ;
+      if ( !(scbp->flags & SCB_WAITINGQ) )
+      {    /*  OK...we aren't already on a queue, so put us on there  */
+        p->device_status[TARGET_INDEX(scbp->cmd)].active_cmds--;
+        scbq_insert_head(queue, scbp);
+        scbp->flags |= SCB_WAITINGQ;
+      }
       scbp = removed_scbs.head;
     }
   }
@@ -2235,33 +2336,38 @@
  *
  * Description:
  *   The device at the given target/channel has been reset.  Abort
- *   all active and queued scbs for that target/channel.
+ *   all active and queued scbs for that target/channel.  This function
+ *   need not worry about linked next pointers because if was a MSG_ABORT_TAG
+ *   then we had a tagged command (no linked next), if it was MSG_ABORT or
+ *   MSG_BUS_DEV_RESET then the device won't know about any commands any more
+ *   and no busy commands will exist, and if it was a bus reset, then nothing
+ *   knows about any linked next commands any more.  In all cases, we don't
+ *   need to worry about the linked next or busy scb, we just need to clear
+ *   them.
  *-F*************************************************************************/
-static int
+static void
 aic7xxx_reset_device(struct aic7xxx_host *p, int target, char channel,
                      int lun, unsigned char tag)
 {
   struct aic7xxx_scb *scbp;
   unsigned char active_scb;
-  int i = 0;
-  int found;
+  int i = 0, j, init_lists = FALSE;
 
   /*
    * Restore this when we're done
    */
   active_scb = inb(p->base + SCBPTR);
 
-#ifdef AIC7XXX_DEBUG_ABORT
-  printk("(scsi%d:%d:%d) Reset device, active_scb %d\n",
-         p->host_no, target, CHAN_TO_INT(channel), active_scb);
-#endif
-
+  if (aic7xxx_verbose > 2)
+    printk("(scsi%d:%d:%d:%d) Reset device, active_scb %d\n",
+         p->host_no, CHAN_TO_INT(channel), target, lun, active_scb);
   /*
    * Deal with the busy target and linked next issues.
    */
   {
     int min_target, max_target;
     unsigned char busy_scbid;
+    struct aic7xxx_scb *scbp, *prev_scbp;
 
     /* Make all targets 'relative' to bus A. */
     if (target == ALL_TARGETS)
@@ -2285,103 +2391,166 @@
     }
     else
     { 
-      min_target = target + channel == 'B' ? 8 : 0;
+      min_target = target + ((channel == 'B') ? 8 : 0);
       max_target = min_target;
     }
 
+
     for (i = min_target; i <= max_target; i++)
     {
-      busy_scbid = aic7xxx_index_busy_target(p, i, 'A', /*unbusy*/FALSE);
-      if (busy_scbid < p->scb_data->numscbs)
+      if (aic7xxx_verbose > 2)
+	printk(KERN_WARNING "(scsi%d:%d:%d:%d) Cleaning up status information "
+	  "and delayed_scbs.\n", p->host_no, CHAN_TO_INT(channel), i, lun);
+      busy_scbid = aic7xxx_index_busy_target(p, i, 'A', /*unbusy*/ TRUE);
+      p->device_status[i].flags &= ~BUS_DEVICE_RESET_PENDING;
+      p->device_status[i].last_reset = jiffies;
+      p->device_status[i].last_queue_full_count = 0;
+      p->device_status[i].last_queue_full = 0;
+      p->device_status[i].temp_queue_depth =
+        p->device_status[i].max_queue_depth;
+      j = 0; 
+      prev_scbp = NULL; 
+      scbp = p->device_status[i].delayed_scbs.head;
+      while ( (scbp != NULL) && (j++ <= p->scb_data->numscbs) )
+      {
+        prev_scbp = scbp;
+        scbp = scbp->q_next;
+        if ( prev_scbp == scbp )
+        {
+	  if (aic7xxx_verbose)
+	    printk(KERN_WARNING "(scsi%d:%d:%d:%d) Yikes!! scb->q_next == scb "
+	      "in the delayed_scbs queue!\n", p->host_no, CHAN_TO_INT(channel),
+              i, lun);
+	  scbp = NULL;
+	  prev_scbp->q_next = NULL;
+	  p->device_status[i].delayed_scbs.tail = prev_scbp;
+        }
+        if (aic7xxx_match_scb(prev_scbp, target, channel, lun, tag))
+        {
+	  scbq_remove(&p->device_status[i].delayed_scbs, prev_scbp);
+	  if ( prev_scbp->flags & SCB_WAITINGQ )
+	    p->device_status[i].active_cmds++;
+	  prev_scbp->flags &= ~(SCB_ACTIVE | SCB_WAITINGQ);
+	  prev_scbp->flags |= SCB_RESET | SCB_QUEUED_FOR_DONE;
+        }
+      }
+      if ( j > p->scb_data->numscbs )
       {
-  	struct aic7xxx_scb *busy_scb;
-  	struct aic7xxx_scb *next_scb;
-  	unsigned char next_scbid;
-
-  	busy_scb = p->scb_data->scb_array[busy_scbid];
-  
-  	next_scbid = busy_scb->hscb->data_count >> 24;
-
-  	if (next_scbid == SCB_LIST_NULL)
-        {
-  	  busy_scbid = aic7xxx_find_scb(p, busy_scb);
-
-  	  if (busy_scbid != SCB_LIST_NULL)
-          {
-  	    outb(busy_scbid, p->base + SCBPTR);
-  	    next_scbid = inb(p->base + SCB_LINKED_NEXT);
-  	  }
-  	}
+        if (aic7xxx_verbose)
+	  printk(KERN_WARNING "(scsi%d:%d:%d:%d) Yikes!! There's a loop in the "
+	    "delayed_scbs queue!\n", p->host_no, CHAN_TO_INT(channel),
+            i, lun);
+        scbq_init(&p->device_status[i].delayed_scbs);
+      }
+      if ( (p->device_status[i].delayed_scbs.head == NULL) &&
+           (p->device_status[i].timer.expires) )
+      {
+        del_timer(&p->device_status[i].timer);
+        p->device_status[i].timer.expires = 0;
+      }
+    }
+  }
 
-  	if (aic7xxx_match_scb(busy_scb, target, channel, lun, tag))
-        {
-  	  aic7xxx_index_busy_target(p, i, 'A', /*unbusy*/TRUE);
-  	}
+  if (aic7xxx_verbose > 2)
+    printk(KERN_WARNING "(scsi%d:%d:%d:%d) Cleaning QINFIFO.\n", p->host_no,
+	CHAN_TO_INT(channel), target, lun );
+  aic7xxx_search_qinfifo(p, target, channel, lun, tag,
+      SCB_RESET | SCB_QUEUED_FOR_DONE, /* requeue */ FALSE, NULL);
 
-  	if (next_scbid != SCB_LIST_NULL)
-        {
-  	  next_scb = p->scb_data->scb_array[next_scbid];
-  	  if (aic7xxx_match_scb(next_scb, target, channel, lun, tag))
-          {
-  	    continue;
-          }
-  	  /* Requeue for later processing */
-  	  scbq_insert_head(&p->waiting_scbs, next_scb);
-  	  next_scb->flags |= SCB_WAITINGQ;
-  	}
+/*
+ *  Search the waiting_scbs queue for matches, this catches any SCB_QUEUED
+ *  ABORT/RESET commands.
+ */
+  if (aic7xxx_verbose > 2)
+    printk(KERN_WARNING "(scsi%d:%d:%d:%d) Cleaning waiting_scbs.\n",
+	p->host_no, CHAN_TO_INT(channel), target, lun );
+  {
+    struct aic7xxx_scb *scbp, *prev_scbp;
+
+    j = 0; 
+    prev_scbp = NULL; 
+    scbp = p->waiting_scbs.head;
+    while ( (scbp != NULL) && (j++ <= p->scb_data->numscbs) )
+    {
+      prev_scbp = scbp;
+      scbp = scbp->q_next;
+      if ( prev_scbp == scbp )
+      {
+	if (aic7xxx_verbose)
+	  printk(KERN_WARNING "(scsi%d:-1:-1:-1) Yikes!! scb->q_next == scb "
+	    "in the waiting_scbs queue!\n", p->host_no);
+	scbp = NULL;
+	prev_scbp->q_next = NULL;
+	p->waiting_scbs.tail = prev_scbp;
+      }
+      if (aic7xxx_match_scb(prev_scbp, target, channel, lun, tag))
+      {
+	scbq_remove(&p->waiting_scbs, prev_scbp);
+	if ( prev_scbp->flags & SCB_WAITINGQ )
+	  p->device_status[TARGET_INDEX(prev_scbp->cmd)].active_cmds++;
+	prev_scbp->flags &= ~(SCB_ACTIVE | SCB_WAITINGQ);
+	prev_scbp->flags |= SCB_RESET | SCB_QUEUED_FOR_DONE;
       }
     }
+    if ( j > p->scb_data->numscbs )
+    {
+      if (aic7xxx_verbose)
+	printk(KERN_WARNING "(scsi%d:-1:-1:-1) Yikes!! There's a loop in the "
+	  "waiting_scbs queue!\n", p->host_no);
+      scbq_init(&p->waiting_scbs);
+    }
   }
 
-  found = aic7xxx_search_qinfifo(p, target, channel, lun, tag,
-      SCB_ABORTED | SCB_QUEUED_FOR_DONE, /* requeue */ FALSE);
 
   /*
    * Search waiting for selection list.
    */
+  if (aic7xxx_verbose > 2)
+    printk(KERN_WARNING "(scsi%d:%d:%d:%d) Cleaning waiting for selection "
+	"list.\n", p->host_no, CHAN_TO_INT(channel), target, lun);
   {
     unsigned char next, prev, scb_index;
 
     next = inb(p->base + WAITING_SCBH);  /* Start at head of list. */
     prev = SCB_LIST_NULL;
-
-    while (next != SCB_LIST_NULL)
+    j = 0;
+    while ( (next != SCB_LIST_NULL) && (j++ <= p->scb_data->maxhscbs) )
     {
       outb(next, p->base + SCBPTR);
       scb_index = inb(p->base + SCB_TAG);
       if (scb_index >= p->scb_data->numscbs)
       {
-        panic("aic7xxx: Waiting List inconsistency; SCB index=%d, numscbs=%d\n",
-              scb_index, p->scb_data->numscbs);
+	if (aic7xxx_verbose)
+          printk(KERN_WARNING "(scsi%d:%d:%d:%d) Waiting List inconsistency; "
+		"SCB index=%d, numscbs=%d\n", p->host_no,
+                CHAN_TO_INT(channel), target, lun, scb_index,
+                p->scb_data->numscbs);
+	next = inb(p->base + SCB_NEXT);
+	aic7xxx_add_curscb_to_free_list(p);
       }
-      scbp = p->scb_data->scb_array[scb_index];
-      if (aic7xxx_match_scb(scbp, target, channel, lun, tag))
+      else
       {
-        unsigned char linked_next;
-
-        next = aic7xxx_abort_waiting_scb(p, scbp, next, prev);
-        linked_next = inb(p->base + SCB_LINKED_NEXT);
-        if (linked_next != SCB_LIST_NULL)
+        scbp = p->scb_data->scb_array[scb_index];
+        if (aic7xxx_match_scb(scbp, target, channel, lun, tag))
         {
-          struct aic7xxx_scb *next_scb;
-          /*
-           * Requeue the waiting SCB via the waiting list.
-           */
-          next_scb = p->scb_data->scb_array[linked_next];
-          if (! aic7xxx_match_scb(next_scb, target, channel, lun, tag))
-          {
-            scbq_insert_head(&p->waiting_scbs, next_scb);
-            next_scb->flags |= SCB_WAITINGQ;
-          }
+          next = aic7xxx_abort_waiting_scb(p, scbp, next, prev);
+	  scbp->flags &= ~SCB_ACTIVE;
+	  scbp->flags |= SCB_RESET | SCB_QUEUED_FOR_DONE;
+        }
+        else
+        {
+          prev = next;
+          next = inb(p->base + SCB_NEXT);
         }
-        found++;
-      }
-      else
-      {
-        prev = next;
-        next = inb(p->base + SCB_NEXT);
       }
     }
+    if ( j > p->scb_data->maxhscbs )
+    {
+      if (aic7xxx_verbose)
+	printk(KERN_WARNING "(scsi%d:-1:-1:-1) Yikes!!  There is a loop in the "
+	  "waiting for selection list!\n", p->host_no);
+      init_lists = TRUE;
+    }
   }
 
   /*
@@ -2393,45 +2562,106 @@
 
     next = inb(p->base + DISCONNECTED_SCBH);
     prev = SCB_LIST_NULL;
-
-    while (next != SCB_LIST_NULL)
+    j = 0;
+    while ( (next != SCB_LIST_NULL) && (j++ <= p->scb_data->maxhscbs) )
     {
       outb(next, p->base + SCBPTR);
       scb_index = inb(p->base + SCB_TAG);
       if (scb_index > p->scb_data->numscbs)
       {
-        panic("aic7xxx: Disconnected List inconsistency, SCB index = %d, "
-              "num scbs = %d.\n", scb_index, p->scb_data->numscbs);
-      }
-      scbp = p->scb_data->scb_array[scb_index];
-      if (aic7xxx_match_scb(scbp, target, channel, lun, tag))
-      {
-        next = aic7xxx_rem_scb_from_disc_list(p, next);
+	if (aic7xxx_verbose)
+          printk(KERN_WARNING "(scsi%d:%d:%d:%d) Waiting List inconsistency; "
+		"SCB index=%d, numscbs=%d\n", p->host_no,
+                CHAN_TO_INT(channel), target, lun, scb_index,
+                p->scb_data->numscbs);
+	next = aic7xxx_rem_scb_from_disc_list(p, next);
       }
       else
       {
-        prev = next;
-        next = inb(p->base + SCB_NEXT);
+        scbp = p->scb_data->scb_array[scb_index];
+        if (aic7xxx_match_scb(scbp, target, channel, lun, tag))
+        {
+          next = aic7xxx_rem_scb_from_disc_list(p, next);
+	  scbp->flags = SCB_RESET | SCB_QUEUED_FOR_DONE;
+	  scbp->hscb->control = 0;
+        }
+        else
+        {
+          prev = next;
+          next = inb(p->base + SCB_NEXT);
+        }
       }
     }
+    if ( j > p->scb_data->maxhscbs )
+    {
+      if (aic7xxx_verbose)
+	printk(KERN_WARNING "(scsi%d:-1:-1:-1) Yikes!!  There is a loop in the "
+	  "disconnected list!\n", p->host_no);
+      init_lists = TRUE;
+    }
+  }
+
+  /*
+   * Walk the free list making sure no entries on the free list have
+   * a valid SCB_TAG value or SCB_CONTROL byte.
+   */
+  {
+    unsigned char next;
+
+    j = 0;
+    next = inb(p->base + FREE_SCBH);
+    while ( (next != SCB_LIST_NULL) && (j++ < p->scb_data->maxhscbs) )
+    {
+      outb(next, p->base + SCBPTR);
+      outb(SCB_LIST_NULL, p->base + SCB_TAG);
+      outb(0, p->base + SCB_CONTROL);
+      next = inb(p->base + SCB_NEXT);
+    }
+    if ( j > p->scb_data->maxhscbs )
+    {
+      if (aic7xxx_verbose)
+	printk(KERN_WARNING "(scsi%d:-1:-1:-1) Yikes!!  There is a loop in the "
+	  "free list!\n", p->host_no);
+      init_lists = TRUE;
+    }
   }
 
   /*
    * Go through the hardware SCB array looking for commands that
    * were active but not on any list.
    */
-  for (i = 0; i < p->scb_data->maxhscbs; i++)
+  if (init_lists)
+  {
+    outb(SCB_LIST_NULL, p->base + FREE_SCBH);
+    outb(SCB_LIST_NULL, p->base + WAITING_SCBH);
+    outb(SCB_LIST_NULL, p->base + DISCONNECTED_SCBH);
+  }
+  for (i = p->scb_data->maxhscbs; i >= 0; --i)
   {
     unsigned char scbid;
 
-    outb(i, p->base + SCBPTR);
-    scbid = inb(p->base + SCB_TAG);
-    if (scbid < p->scb_data->numscbs)
+    if (init_lists)
+    {
+      outb(SCB_LIST_NULL, p->base + SCB_TAG);
+      outb(SCB_LIST_NULL, p->base + SCB_NEXT);
+      outb(SCB_LIST_NULL, p->base + SCB_PREV);
+      outb(SCB_LIST_NULL, p->base + SCB_LINKED_NEXT);
+      outb(0, p->base + SCB_CONTROL);
+      aic7xxx_add_curscb_to_free_list(p);
+    }
+    else
     {
-      scbp = p->scb_data->scb_array[scbid];
-      if (aic7xxx_match_scb(scbp, target, channel, lun, tag))
+      outb(i, p->base + SCBPTR);
+      scbid = inb(p->base + SCB_TAG);
+      if (scbid < p->scb_data->numscbs)
       {
-        aic7xxx_add_curscb_to_free_list(p);
+        scbp = p->scb_data->scb_array[scbid];
+        if (aic7xxx_match_scb(scbp, target, channel, lun, tag))
+        {
+	  outb(0, p->base + SCB_CONTROL);
+	  outb(SCB_LIST_NULL, p->base + SCB_TAG);
+          aic7xxx_add_curscb_to_free_list(p);
+        }
       }
     }
   }
@@ -2447,24 +2677,24 @@
     if (((scbp->flags & SCB_ACTIVE) != 0) &&
         aic7xxx_match_scb(scbp, target, channel, lun, tag))
     {
-      scbp->flags |= SCB_ABORTED | SCB_QUEUED_FOR_DONE;
+      scbp->flags |= SCB_RESET | SCB_QUEUED_FOR_DONE;
       scbp->flags &= ~SCB_ACTIVE;
-      aic7xxx_error(scbp->cmd) = DID_RESET;
-
-      found++;
 
       if ((scbp->flags & SCB_WAITINGQ) != 0)
       {
         scbq_remove(&p->waiting_scbs, scbp);
+        scbq_remove(&p->device_status[TARGET_INDEX(scbp->cmd)].delayed_scbs,
+	  scbp);
         scbp->flags &= ~SCB_WAITINGQ;
+	p->device_status[TARGET_INDEX(scbp->cmd)].active_cmds++;
       }
     }
   }
 
   outb(active_scb, p->base + SCBPTR);
-  return (found);
 }
 
+
 /*+F*************************************************************************
  * Function:
  *   aic7xxx_clear_intstat
@@ -2521,20 +2751,18 @@
  * Description:
  *   Reset the channel.
  *-F*************************************************************************/
-static int
+static void
 aic7xxx_reset_channel(struct aic7xxx_host *p, char channel, int initiate_reset)
 {
   unsigned long offset, offset_max;
-  int found;
   unsigned char sblkctl;
   char cur_channel;
 
-  pause_sequencer(p);
-  /*
-   * Clean up all the state information for the pending transactions
-   * on this bus.
-   */
-  found = aic7xxx_reset_device(p, ALL_TARGETS, channel, ALL_LUNS, SCB_LIST_NULL);
+  if (aic7xxx_verbose > 1)
+    printk(KERN_WARNING "(scsi%d:%d:-1:-1) Reset channel called, %s initiate "
+	"reset.\n", p->host_no, CHAN_TO_INT(channel),
+	(initiate_reset == TRUE) ? "will" : "won't" );
+
 
   if (channel == 'B')
   {
@@ -2587,10 +2815,9 @@
     /*
      * Case 1: Command for another bus is active
      */
-#ifdef AIC7XXX_DEBUG_ABORT
-    printk("scsi%d: Stealthily resetting channel %c\n",
-           p->host_no, channel);
-#endif
+    if (aic7xxx_verbose > 2)
+      printk(KERN_WARNING "(scsi%d:%d:-1:-1) Stealthily resetting idle "
+           "channel.\n", p->host_no, CHAN_TO_INT(channel));
     /*
      * Stealthily reset the other bus without upsetting the current bus.
      */
@@ -2599,52 +2826,50 @@
     if (initiate_reset)
     {
       aic7xxx_reset_current_bus(p);
-      /*
-       * Cause the mid-level SCSI code to delay any further 
-       * queueing by the bus settle time for us.
-       */
-      p->host->last_reset = (jiffies + (AIC7XXX_RESET_DELAY * HZ));
     }
     outb(0, p->base + SCSISEQ);
     aic7xxx_clear_intstat(p);
     outb(sblkctl, p->base + SBLKCTL);
-    unpause_sequencer(p, /* unpause_always */ FALSE);
   }
   else
   {
     /*
      * Case 2: A command from this bus is active or we're idle.
      */
-#ifdef AIC7XXX_DEBUG_ABORT
-    printk("scsi%d: Resetting current channel %c\n",
-           p->host_no, channel);
-#endif
+    if (aic7xxx_verbose > 2)
+      printk(KERN_WARNING "(scsi%d:%d:-1:-1) Resetting currently active "
+	"channel.\n", p->host_no, CHAN_TO_INT(channel));
     outb(inb(p->base + SIMODE1) & ~ENBUSFREE, p->base + SIMODE1);
     if (initiate_reset)
     {
       aic7xxx_reset_current_bus(p);
-      /*
-       * Cause the mid-level SCSI code to delay any further 
-       * queueing by the bus settle time for us.
-       */
-#if 0
-      p->host->last_reset = (jiffies + (AIC7XXX_RESET_DELAY * HZ));
-#endif
     }
     outb(0, p->base + SCSISEQ);
     aic7xxx_clear_intstat(p);
+  }
+  if (aic7xxx_verbose)
+    printk(KERN_WARNING "(scsi%d:%d:-1:-1) Channel reset\n",
+	p->host_no, CHAN_TO_INT(channel));
+  /*
+   * Clean up all the state information for the pending transactions
+   * on this bus.
+   */
+  aic7xxx_reset_device(p, ALL_TARGETS, channel, ALL_LUNS, SCB_LIST_NULL);
+
+  if ( p->bus_type != AIC_TWIN )
+  {
     restart_sequencer(p);
-#ifdef AIC7XXX_DEBUG_ABORT
-    printk("scsi%d: Channel reset, sequencer restarted\n", p->host_no);
-#endif
+    pause_sequencer(p);
   }
 
+  p->host->last_reset = jiffies + (HZ * AIC7XXX_RESET_DELAY);
+
   /*
    * Now loop through all the SCBs that have been marked for abortion,
    * and call the scsi_done routines.
    */
   aic7xxx_run_done_queue(p, /*complete*/ TRUE);
-  return (found);
+  return;
 }
 
 /*+F*************************************************************************
@@ -2659,14 +2884,16 @@
 aic7xxx_run_waiting_queues(struct aic7xxx_host *p)
 {
   struct aic7xxx_scb *scb;
+  int tindex;
+
 
   if (p->waiting_scbs.head == NULL)
     return;
 
-  pause_sequencer(p);
   /*
    * First handle SCBs that are waiting but have been assigned a slot.
    */
+  pause_sequencer(p);
   scb = p->waiting_scbs.head;
   while (scb != NULL)
   {
@@ -2682,26 +2909,73 @@
     /*
      * We have some space.
      */
-    scbq_remove_head(&(p->waiting_scbs));
-    scb->flags &= ~SCB_WAITINGQ;
-
-    outb(scb->hscb->tag, p->base + QINFIFO);
-
-    if ((p->flags & PAGE_ENABLED) != 0)
+    scbq_remove_head(&p->waiting_scbs);
+    tindex = TARGET_INDEX(scb->cmd);
+    if ( p->device_status[tindex].active_cmds >=
+         p->device_status[tindex].temp_queue_depth )
     {
-      /*
-       * We only care about this statistic when paging
-       * since it's impossible to overflow the qinfifo
-       * in the non-paging case.
-       */
-      p->curqincnt++;
+        scbq_insert_tail(&p->device_status[tindex].delayed_scbs, scb);
+    }
+    else
+    {
+        scb->flags &= ~SCB_WAITINGQ;
+        p->device_status[tindex].active_cmds++;
+        outb(scb->hscb->tag, p->base + QINFIFO);
+        p->curqincnt++;
     }
     scb = p->waiting_scbs.head;
   }
-
   unpause_sequencer(p, FALSE);
 }
 
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_timer
+ *
+ * Description:
+ *   Take expired extries off of delayed queues and place on waiting queue
+ *   then run waiting queue to start commands.
+ ***************************************************************************/
+static void
+aic7xxx_timer(struct aic7xxx_host *p)
+{
+   int i;
+   unsigned long processor_flags;
+   struct aic7xxx_scb *scb;
+   
+   save_flags(processor_flags);
+   cli();
+   if (aic7xxx_verbose > 3)
+     printk(KERN_WARNING "(scsi%d:-1:-1:-1) Timer running.\n", p->host_no);
+   for(i=0; i<MAX_TARGETS; i++)
+   {
+     if ( (p->device_status[i].timer.expires) && 
+          (p->device_status[i].timer.expires <= jiffies) )
+     {
+       p->device_status[i].timer.expires = 0;
+       if ( (p->device_status[i].timer.prev != NULL) ||
+            (p->device_status[i].timer.next != NULL) )
+       {
+         del_timer(&p->device_status[i].timer);
+       }
+       p->device_status[i].temp_queue_depth = 
+	 p->device_status[i].max_queue_depth;
+       scb = p->device_status[i].delayed_scbs.head;
+       while ( scb != NULL )
+       {
+         scbq_remove_head(&p->device_status[i].delayed_scbs);
+         scbq_insert_tail(&p->waiting_scbs, scb);
+         scb = p->device_status[i].delayed_scbs.head;
+       }
+     }
+   }
+   aic7xxx_run_waiting_queues(p);
+   unpause_sequencer(p, FALSE);
+   restore_flags(processor_flags);
+}
+ 
+
 /*+F*************************************************************************
  * Function:
  *   aic7xxx_construct_sdtr
@@ -2777,11 +3051,11 @@
               (hscb->residual_data_count[1] <<  8) |
               hscb->residual_data_count[0];
 
-    if (actual < cmd->underflow)
+    if ( (actual < cmd->underflow) && (aic7xxx_verbose) )
     {
-      printk(KERN_WARNING "(scsi%d:%d:%d) Underflow - "
+      printk(KERN_WARNING "(scsi%d:%d:%d:%d) Underflow - "
              "Wanted at least %u, got %u, residual SG count %d.\n",
-             p->host_no, TC_OF_SCB(scb), cmd->underflow, actual,
+             p->host_no, CTL_OF_SCB(scb), cmd->underflow, actual,
              hscb->residual_SG_segment_count);
       aic7xxx_error(cmd) = DID_RETRY_COMMAND;
       aic7xxx_status(cmd) = hscb->target_status;
@@ -2811,7 +3085,6 @@
   unsigned short targ_mask;
   unsigned char  targ_scratch;
   int scratch_offset = target;
-  int found;
 
   if (channel == 'B')
   {
@@ -2828,9 +3101,10 @@
   targ_scratch = inb(p->base + TARG_SCRATCH + scratch_offset);
   targ_scratch &= SXFR;
   outb(targ_scratch, p->base + TARG_SCRATCH + scratch_offset);
-  found = aic7xxx_reset_device(p, target, channel, ALL_LUNS, SCB_LIST_NULL);
-  printk(KERN_WARNING "(scsi%d:%d:%d) Bus Device Reset delivered, "
-         "%d SCBs aborted.\n", p->host_no, target, CHAN_TO_INT(channel), found);
+  aic7xxx_reset_device(p, target, channel, ALL_LUNS, SCB_LIST_NULL);
+  if (aic7xxx_verbose)
+    printk(KERN_WARNING "(scsi%d:%d:%d:-1) Bus Device Reset delivered.\n",
+         p->host_no, CHAN_TO_INT(channel), target);
   aic7xxx_run_done_queue(p, /*complete*/ TRUE);
 }
 
@@ -2846,7 +3120,8 @@
 {
   struct aic7xxx_scb *scb;
   unsigned short target_mask;
-  unsigned char target, scratch_offset;
+  unsigned char target, scratch_offset, lun;
+  unsigned char queue_flag = FALSE;
   char channel;
 
   if ((inb(p->base + SEQ_FLAGS) & RESELECTED) != 0)
@@ -2859,6 +3134,7 @@
   }
   scratch_offset = target;
   channel = 'A';
+  lun = inb(p->base + SAVED_TCL) & 0x07;
   if (inb(p->base + SBLKCTL) & SELBUSB)
   {
     channel = 'B';
@@ -2900,19 +3176,20 @@
           {
             /*
              * We expected this.  Let the busfree handler take care
-             * of this when we the abort is finially sent.  Set
+             * of this when we the abort is finally sent.  Set
              * IDENTIFY_SEEN so that the busfree handler knows that
              * there is an SCB to cleanup.
              */
             outb(inb(p->base + SEQ_FLAGS) | IDENTIFY_SEEN, p->base + SEQ_FLAGS);
-            printk(KERN_INFO "(scsi%d:%d:%d) reconnect SCB abort successful\n",
-                   p->host_no, TC_OF_SCB(scb));
+	    if (aic7xxx_verbose > 1)
+              printk(KERN_WARNING "(scsi%d:%d:%d:%d) Reconnect SCB abort "
+		"successful\n", p->host_no, CTL_OF_SCB(scb));
             break;
           }
         }
-        printk(KERN_WARNING "(scsi%d:%d:%d) No active SCB for reconnecting "
+        printk(KERN_WARNING "(scsi%d:%d:%d:%d) No active SCB for reconnecting "
                "target - Issuing BUS DEVICE RESET.\n",
-               p->host_no, target, CHAN_TO_INT(channel));
+               p->host_no, CHAN_TO_INT(channel), target, lun);
 
         printk(KERN_WARNING "      SAVED_TCL=0x%x, ARG_1=0x%x, SEQADDR=0x%x\n",
                inb(p->base + SAVED_TCL), arg1,
@@ -2932,9 +3209,9 @@
         scb_index = inb(p->base + CUR_SCBID);
         scb = p->scb_data->scb_array[scb_index];
 
-        panic("scsi%d:  Target %d, channel %c, Target busy link failure, "
+        panic("(scsi%d:%d:%d:%d) Target busy link failure, "
               "but busy SCB exists!\n",
-              p->host_no, target, channel);
+              p->host_no, CHAN_TO_INT(channel), target, lun );
       }
       break;
 
@@ -2943,10 +3220,11 @@
         unsigned char rej_byte;
 
         rej_byte = inb(p->base + REJBYTE);
-        printk(KERN_WARNING "(scsi%d:%d:%d) Rejecting unknown message (0x%x) "
-               "received from target, SEQ_FLAGS=0x%x\n",
-               p->host_no, target, CHAN_TO_INT(channel), rej_byte,
-               inb(p->base + SEQ_FLAGS));
+	if (aic7xxx_verbose > 1)
+          printk(KERN_WARNING "(scsi%d:%d:%d:%d) Rejecting unknown message "
+               "(0x%x) received from target, SEQ_FLAGS=0x%x\n",
+               p->host_no, CHAN_TO_INT(channel), target, lun,
+               rej_byte, inb(p->base + SEQ_FLAGS));
       }
       break;
 
@@ -2959,31 +3237,28 @@
          * The only safe thing to do is to blow it away with a bus
          * reset.
          */
-        int found;
-
-        printk(KERN_WARNING "(scsi%d:%d:%d): Target did not send an IDENTIFY "
-               "message; LASTPHASE 0x%x, SAVED_TCL 0x%x\n",
-               p->host_no, target, CHAN_TO_INT(channel),
+        if (aic7xxx_verbose)
+          printk(KERN_WARNING "(scsi%d:%d:%d:%d) Target did not send an "
+               "IDENTIFY message; LASTPHASE 0x%x, SAVED_TCL 0x%x\n",
+               p->host_no, CHAN_TO_INT(channel), target, lun,
                inb(p->base + LASTPHASE), inb(p->base + SAVED_TCL));
 
-        found = aic7xxx_reset_channel(p, channel, /*initiate reset*/ TRUE);
+        aic7xxx_reset_channel(p, channel, /*initiate reset*/ TRUE);
 
-        printk(KERN_WARNING "scsi%d: Issued channel %c bus reset; "
-     	       "%d SCBs aborted\n", p->host_no, channel, found);
       }
       break;
 
     case BAD_PHASE:
       if (inb(p->base + LASTPHASE) == P_BUSFREE)
       {
-        printk(KERN_WARNING "(scsi%d:%d:%d): Missed busfree.\n",
-               p->host_no, CHAN_TO_INT(channel), target);
+        printk(KERN_WARNING "(scsi%d:%d:%d:%d) Missed busfree.\n",
+               p->host_no, CHAN_TO_INT(channel), target, lun);
         restart_sequencer(p);
       }
       else
       {
-        printk(KERN_WARNING "(scsi%d:%d:%d): Unknown scsi bus phase, attempting "
-               "to continue\n", p->host_no, CHAN_TO_INT(channel), target);
+        printk(KERN_WARNING "(scsi%d:%d:%d:%d) Unknown scsi bus phase, "
+               "continuing\n", p->host_no, CHAN_TO_INT(channel), target, lun);
       }
       break;
 
@@ -3054,11 +3329,15 @@
               }
               else
               {
-        	/* We went too low - force async. */
+        	/*
+                 * We went too low - force async and disable future
+                 * sync negotiation
+                 */
+                p->needsdtr_copy &= ~target_mask;
         	outb(SEND_REJ, p->base + RETURN_1);
               }
             }
-            else
+            else if ( offset != 0 )
             {
               /*
                * Send our own SDTR in reply.
@@ -3066,14 +3345,26 @@
                * We want to see this message as we don't expect a target
                * to send us a SDTR request first.
                */
-              printk(KERN_WARNING "scsi%d: Sending SDTR!!\n", p->host_no);
+              printk(KERN_WARNING "(scsi%d:%d:%d:%d) Sending reply SDTR.\n",
+                p->host_no, CTL_OF_SCB(scb));
               aic7xxx_construct_sdtr(p, /* start byte */ 0, period, offset);
               outb(SEND_MSG, p->base + RETURN_1);
             }
+            else
+            {
+              /*
+               * The incoming SDTR was too low, reject it.
+               */
+              printk(KERN_WARNING "(scsi%d:%d:%d:%d) Rejecting SDTR request.\n",
+                p->host_no, CTL_OF_SCB(scb));
+              outb(SEND_REJ, p->base + RETURN_1);
+              p->needsdtr_copy &= ~target_mask;
+            }
             /*
              * Clear the flags.
              */
             p->needsdtr &= ~target_mask;
+            p->sdtr_pending &= ~target_mask;
             break;
           }
 
@@ -3099,14 +3390,15 @@
               switch (bus_width)
               {
         	case BUS_8_BIT:
+                  p->needwdtr_copy &= ~target_mask;
         	  scratch &= 0x7F;
         	  break;
 
         	case BUS_16_BIT:
                   if (aic7xxx_verbose)
                   {
-        	    printk(KERN_INFO "scsi%d: Target %d, channel %c, using 16 "
-  	  	         "bit transfers.\n", p->host_no, target, channel);
+        	    printk(KERN_INFO "(scsi%d:%d:%d:%d) Using 16 bit (Wide)"
+  	  	         "transfers.\n", p->host_no, CTL_OF_SCB(scb));
                   }
         	  scratch |= WIDEXFER;
         	  break;
@@ -3114,9 +3406,9 @@
         	case BUS_32_BIT:
         	  outb(SEND_REJ, p->base + RETURN_1);
                   /* No verbose here!  We want to see this condition. */
-        	  printk(KERN_WARNING "scsi%d: Target %d, channel %c, "
+        	  printk(KERN_WARNING "(scsi%d:%d:%d:%d) "
   			"requesting 32 bit transfers, rejecting...\n",
-                	 p->host_no, target, channel);
+                	 p->host_no, CTL_OF_SCB(scb));
         	  break;
 
         	default:
@@ -3133,6 +3425,7 @@
               switch (bus_width)
               {
         	case BUS_8_BIT:
+                  p->needwdtr_copy &= ~target_mask;
         	  scratch &= 0x7F;
         	  break;
 
@@ -3140,8 +3433,8 @@
         	case BUS_16_BIT:
         	  if (p->bus_type == AIC_WIDE)
         	  {
-                    printk(KERN_INFO "scsi%d: Target %d, channel %c, using 16 "
-  			   "bit transfers.\n", p->host_no, target, channel);
+                    printk(KERN_INFO "(scsi%d:%d:%d:%d) Using 16 bit (Wide)"
+  			   "transfers.\n", p->host_no, CTL_OF_SCB(scb));
                     bus_width = BUS_16_BIT;
                     scratch |= WIDEXFER;
         	  }
@@ -3159,9 +3452,9 @@
               if (send_reject)
               {
                 outb(SEND_REJ, p->base + RETURN_1);
-                printk(KERN_WARNING "scsi%d: Target %d, channel %c, initiating "
-                       "wide negotiation on a narrow bus - rejecting!\n",
-                       p->host_no, target, channel);
+                printk(KERN_WARNING "(scsi%d:%d:%d:%d) Target initiated "
+                       "wide negotiation on a narrow bus - rejecting!",
+                       p->host_no, CTL_OF_SCB(scb));
               }
               else
               {
@@ -3170,6 +3463,7 @@
               }
             }
             p->needwdtr &= ~target_mask;
+            p->wdtr_pending &= ~target_mask;
             outb(scratch, p->base + TARG_SCRATCH + scratch_offset);
             outb(scratch, p->base + SCSIRATE);
             break;
@@ -3206,9 +3500,12 @@
            */
           targ_scratch &= 0x7F;
           p->needwdtr &= ~target_mask;
-          printk(KERN_WARNING "scsi%d: Target %d, channel %c, refusing WIDE "
+          p->needwdtr_copy &= ~target_mask;
+          p->wdtr_pending &= ~target_mask;
+          if (aic7xxx_verbose)
+            printk(KERN_WARNING "(scsi%d:%d:%d:%d) Refusing WIDE "
   		 "negotiation; using 8 bit transfers.\n",
-  		 p->host_no, target, channel);
+  		 p->host_no, CTL_OF_SCB(scb));
 	}
 	else
 	{
@@ -3219,9 +3516,12 @@
              */
             targ_scratch &= 0xF0;
             p->needsdtr &= ~target_mask;
-            printk(KERN_WARNING "scsi%d: Target %d, channel %c, refusing "
+            p->needsdtr_copy &= ~target_mask;
+            p->sdtr_pending &= ~target_mask;
+ 	    if (aic7xxx_verbose)
+              printk(KERN_WARNING "(scsi%d:%d:%d:%d) Refusing "
   		   "synchronous negotiation; using asynchronous transfers.\n",
-  		   p->host_no, target, channel);
+  		   p->host_no, CTL_OF_SCB(scb));
           }
           /*
            * Otherwise, we ignore it.
@@ -3256,9 +3556,10 @@
 	outb(0, p->base + RETURN_1);
 	if (!(scb->flags & SCB_ACTIVE) || (scb->cmd == NULL))
 	{
-          printk(KERN_WARNING "scsi%d: Referenced SCB not valid during "
+          printk(KERN_WARNING "(scsi%d:%d:%d:%d) Invalid SCB during "
         	 "SEQINT 0x%x, scb %d, flags 0x%x, cmd 0x%x.\n", p->host_no,
-        	 intstat, scb_index, scb->flags, (unsigned int) scb->cmd);
+        	 CHAN_TO_INT(channel), target, lun, intstat, scb_index,
+                 scb->flags, (unsigned int) scb->cmd);
 	}
 	else
 	{
@@ -3271,8 +3572,8 @@
           switch (status_byte(hscb->target_status))
           {
             case GOOD:
-  	      printk(KERN_WARNING "(scsi%d:%d:%d) Interrupted for status of "
-                     "GOOD???\n", p->host_no, TC_OF_SCB(scb));
+  	      printk(KERN_WARNING "(scsi%d:%d:%d:%d) Interrupted for status of "
+                     "GOOD???\n", p->host_no, CTL_OF_SCB(scb));
               break;
 
             case CHECK_CONDITION:
@@ -3331,7 +3632,7 @@
                 aic7xxx_busy_target(p, target, channel, hscb->tag);
         	outb(SEND_SENSE, p->base + RETURN_1);
               }  /* first time sense, no errors */
-  	      else
+              else
   	      {
         	if (aic7xxx_error(cmd) == 0)
   		{
@@ -3341,56 +3642,94 @@
               break;
 
             case QUEUE_FULL:
-#ifdef NOT_YET
-              if (scb->hscb->control & TAG_ENB)
+              queue_flag = TRUE;    /* Mark that this is a QUEUE_FULL and */
+            case BUSY:              /* drop through to here */
+            {
+              struct aic7xxx_scb *next_scbp, *prev_scbp;
+              int ti = TARGET_INDEX(scb->cmd);
+              
+              aic7xxx_search_qinfifo(p, target, channel, lun,
+                SCB_LIST_NULL, 0, TRUE,
+                &p->device_status[ti].delayed_scbs);
+              next_scbp = p->waiting_scbs.head;
+              while ( next_scbp != NULL )
               {
-        	if (cmd->device->queue_depth > 2)
-        	{
-                  cmd->device->queue_depth--;  /* Not correct */
-                  printk(KERN_WARNING "(scsi%d:%d:%d) Tagged queue depth "
-                	 "reduced to %d\n", p->host_no,
-                	 TC_OF_SCB(scb), cmd->device->queue_depth);
-        	}
-        	/*
-        	 * XXX - Requeue this unconditionally?
-        	 */
-
-        	/*
-        	 * We'd like to be able to give the SCB some more time
-        	 * (untimeout, then timeout).
-        	 */
-        	break;
+                prev_scbp = next_scbp;
+                next_scbp = next_scbp->q_next;
+                if ( aic7xxx_match_scb(prev_scbp, target, channel, lun,
+                     SCB_LIST_NULL) )
+                {
+                  scbq_remove(&p->waiting_scbs, prev_scbp);
+                  scbq_insert_tail(&p->device_status[ti].delayed_scbs,
+                    prev_scbp);
+                }
               }
-#endif
-              printk(KERN_WARNING "(scsi%d:%d:%d) Queue full received; "
+	      scbq_insert_head(&p->device_status[ti].delayed_scbs, scb);
+	      p->device_status[ti].active_cmds--;
+	      scb->flags |= SCB_WAITINGQ | SCB_WAS_BUSY;
+                  
+              if (p->device_status[ti].timer.expires == 0) 
+              {
+		if ( p->device_status[ti].active_cmds )
+		{
+                  p->device_status[ti].timer.expires = jiffies + (HZ * 2);
+                  add_timer(&p->device_status[ti].timer);
+		}
+		else
+		{
+                  p->device_status[ti].timer.expires = jiffies + (HZ / 2);
+                  add_timer(&p->device_status[ti].timer);
+		}
+              }
+   	      if (aic7xxx_verbose > 1)
+              {
+                if (queue_flag)
+                  printk(KERN_WARNING "(scsi%d:%d:%d:%d) Queue full received; "
                      "queue depth %d, active %d\n", p->host_no,
-                     TC_OF_SCB(scb), cmd->device->queue_depth,
-                     p->device_status[TARGET_INDEX(cmd)].active_cmds);
+                     CTL_OF_SCB(scb), p->device_status[ti].max_queue_depth,
+                     p->device_status[ti].active_cmds);
+                else
+                  printk(KERN_WARNING "(scsi%d:%d:%d:%d) Target busy\n",
+                     p->host_no, CTL_OF_SCB(scb));
 
-              /* Else treat this as if it was a BUSY condition. */
-              scb->hscb->target_status = (BUSY << 1) |
-                  (scb->hscb->target_status & 0x01);
-              /* Fall through to the BUSY case. */
-
-            case BUSY:
-              printk(KERN_WARNING "(scsi%d:%d:%d) Target busy\n",
-                     p->host_no, TC_OF_SCB(scb));
-              if (!aic7xxx_error(cmd))
+              }
+              if (queue_flag)
               {
-  		/*
-        	 * The mid-level SCSI code should be fixed to
-        	 * retry the command at a later time instead of
-        	 * trying right away.
-        	 */
-        	aic7xxx_error(cmd) = DID_BUS_BUSY | (SUGGEST_RETRY << 8);
+	        p->device_status[ti].temp_queue_depth = 
+		  p->device_status[ti].active_cmds;
+                if ( (p->device_status[ti].last_queue_full <
+                     (p->device_status[ti].active_cmds - 1)) ||
+                     (p->device_status[ti].last_queue_full >
+                     (p->device_status[ti].active_cmds + 1)) )
+                {
+                  p->device_status[ti].last_queue_full = 
+                      p->device_status[ti].active_cmds;
+                  p->device_status[ti].last_queue_full_count = 0;
+                }
+                else
+                {
+                  p->device_status[ti].last_queue_full_count++;
+                }
+                if ( (p->device_status[ti].last_queue_full_count > 14) &&
+                     (p->device_status[ti].active_cmds > 4) )
+                {
+		  if (aic7xxx_verbose)
+                    printk(KERN_WARNING "(scsi%d:%d:%d:%d) Queue depth reduced "
+                      "to %d\n", p->host_no, CTL_OF_SCB(scb), 
+                      p->device_status[ti].active_cmds);
+                  p->device_status[ti].max_queue_depth = 
+                      p->device_status[ti].active_cmds;
+                  p->device_status[ti].last_queue_full = 0;
+                  p->device_status[ti].last_queue_full_count = 0;
+                }
               }
-              udelay(1000);  /*  A small pause (1ms) to help the drive */
               break;
-
+            }
+            
             default:
-              printk(KERN_WARNING "(scsi%d:%d:%d) Unexpected target "
+              printk(KERN_WARNING "(scsi%d:%d:%d:%d) Unexpected target "
                      "status 0x%x.\n", p->host_no,
-        	     TC_OF_SCB(scb), scb->hscb->target_status);
+        	     CTL_OF_SCB(scb), scb->hscb->target_status);
               if (!aic7xxx_error(cmd))
               {
         	aic7xxx_error(cmd) = DID_RETRY_COMMAND;
@@ -3419,8 +3758,9 @@
 	{
           outb(MSG_BUS_DEV_RESET, p->base + MSG_OUT);
           outb(1, p->base + MSG_LEN);
-          printk(KERN_INFO "(scsi%d:%d:%d) Bus device reset sent\n",
-        	 p->host_no, TC_OF_SCB(scb));
+          if (aic7xxx_verbose > 1)
+            printk(KERN_WARNING "(scsi%d:%d:%d:%d) Bus device reset mailed.\n",
+        	 p->host_no, CTL_OF_SCB(scb));
 	}
         else if (scb->flags & SCB_ABORT)
         {
@@ -3433,8 +3773,9 @@
             outb(MSG_ABORT, p->base + MSG_OUT + message_offset);
           }
           outb(message_offset + 1, p->base + MSG_LEN);
-          printk(KERN_WARNING "(scsi%d:%d:%d): Abort message sent.\n",
-                 p->host_no, TC_OF_SCB(scb));
+          if (aic7xxx_verbose > 1)
+            printk(KERN_WARNING "(scsi%d:%d:%d:%d) Abort message mailed.\n",
+                 p->host_no, CTL_OF_SCB(scb));
         }
 	else if (scb->flags & SCB_MSGOUT_WDTR)
 	{
@@ -3485,18 +3826,21 @@
 	overrun = inb(p->base + STCNT) | (inb(p->base + STCNT + 1) << 8) |
   		  (inb(p->base + STCNT + 2) << 16);
 	overrun = 0x00FFFFFF - overrun;
-	printk(KERN_WARNING "(scsi%d:%d:%d) Data overrun of %d bytes detected "
-               "in %s phase, tag %d; forcing a retry.\n",
-               p->host_no, TC_OF_SCB(scb), overrun,
+        if (aic7xxx_verbose)
+        {
+	  printk(KERN_WARNING "(scsi%d:%d:%d:%d) Data overrun of %d bytes "
+               "detected in %s phase, tag %d; forcing a retry.\n",
+               p->host_no, CTL_OF_SCB(scb), overrun,
                lastphase == P_DATAIN ? "Data-In" : "Data-Out",
                scb->hscb->tag);
-        printk(KERN_WARNING "%s seen Data Phase.  Length = %d, NumSGs = %d.\n",
+          printk(KERN_WARNING "%s seen Data Phase. Length=%d, NumSGs=%d.\n",
                inb(p->base + SEQ_FLAGS) & DPHASE ? "Have" : "Haven't",
                aic7xxx_length(scb->cmd, 0), scb->sg_count);
-        for (i = 0; i < scb->sg_count; i++)
-        {
-          printk(KERN_INFO "     sg[%d] - Addr 0x%x : Length %d\n",
-                 i, scb->sg_list[i].address, scb->sg_list[i].length);
+          for (i = 0; i < scb->sg_count; i++)
+          {
+            printk(KERN_WARNING "     sg[%d] - Addr 0x%x : Length %d\n",
+                   i, scb->sg_list[i].address, scb->sg_list[i].length);
+          }
         }
 	/*
 	 * XXX - What do we really want to do on an overrun?  The
@@ -3606,21 +3950,14 @@
       sindex = inb(p->base + SINDEX);
       message = inb(p->base + sindex - 1);
 
-      if (message == MSG_ABORT)
+      if ((message == MSG_ABORT) || (message == MSG_ABORT_TAG))
       {
-        printk(KERN_WARNING "(scsi%d:%d:%d) SCB %d abort completed.\n",
-                   p->host_no, TC_OF_SCB(scb), scb->hscb->tag);
-        aic7xxx_reset_device(p, target, channel, SCB_LUN(scb), SCB_LIST_NULL);
-        aic7xxx_run_done_queue(p, /* complete */ TRUE);
-        scb = NULL;
-        printerror = 0;
-      }
-      else if (message == MSG_ABORT_TAG)
-      {
-        printk(KERN_WARNING "(scsi%d:%d:%d) SCB %d abort Tag completed.\n",
-                   p->host_no, TC_OF_SCB(scb), scb->hscb->tag);
-        aic7xxx_reset_device(p, target, channel, SCB_LUN(scb), scb->hscb->tag);
-        aic7xxx_run_done_queue(p, /* complete */ TRUE);
+	if (aic7xxx_verbose > 1)
+          printk(KERN_WARNING "(scsi%d:%d:%d:%d) SCB %d abort delivered.\n",
+                   p->host_no, CTL_OF_SCB(scb), scb->hscb->tag);
+        aic7xxx_reset_device(p, target, channel, ALL_LUNS,
+		(message == MSG_ABORT) ? SCB_LIST_NULL : scb->hscb->tag );
+	aic7xxx_run_done_queue(p, FALSE);
         scb = NULL;
         printerror = 0;
       }
@@ -3645,15 +3982,18 @@
         {
           tag = SCB_LIST_NULL;
         }
-        aic7xxx_reset_device(p, target, channel, SCB_LUN(scb), tag);
+        aic7xxx_reset_device(p, target, channel, ALL_LUNS, tag);
+	aic7xxx_run_done_queue(p, FALSE);
       }
       else
-      {
-        aic7xxx_reset_device(p, target, channel, ALL_LUNS, SCB_LIST_NULL);
+      {  /* Since we don't really know what happened here, we'll wait */
+	 /* for the commands to timeout and get aborted if need be    */
+        aic7xxx_add_curscb_to_free_list(p);
       }
       printk(KERN_WARNING "scsi%d: Unexpected busfree, LASTPHASE = 0x%x, "
              "SEQADDR = 0x%x\n", p->host_no, lastphase,
              (inb(p->base + SEQADDR1) << 8) | inb(p->base + SEQADDR0));
+      scb = NULL;
     }
     outb(inb(p->base + SIMODE1) & ~ENBUSFREE, p->base + SIMODE1);
     outb(CLRBUSFREE, p->base + CLRSINT1);
@@ -3784,8 +4124,8 @@
      * A parity error has occurred during a data
      * transfer phase. Flag it and continue.
      */
-    printk(KERN_WARNING "(scsi%d:%d:%d) Parity error during phase %s.\n",
-           p->host_no, TC_OF_SCB(scb), phase);
+    printk(KERN_WARNING "(scsi%d:%d:%d:%d) Parity error during phase %s.\n",
+           p->host_no, CTL_OF_SCB(scb), phase);
 
     /*
      * We've set the hardware to assert ATN if we get a parity
@@ -3825,7 +4165,6 @@
   if (scb != NULL)
   {
     aic7xxx_done(p, scb);
-    aic7xxx_done_cmds_complete(p);
   }
 }
 
@@ -3845,6 +4184,7 @@
   struct aic7xxx_host *p;
   unsigned char intstat;
   unsigned long flags;
+  unsigned int interrupts_cleared = 0;
 
   p = (struct aic7xxx_host *) aic7xxx_boards[irq]->hostdata;
 
@@ -3897,7 +4237,7 @@
 
   if (p->flags & IN_ISR)
   {
-    printk(KERN_WARNING "scsi%d: Warning!! Interrupt routine called reentrantly!\n",
+    panic(KERN_WARNING "scsi%d: Warning!! Interrupt routine called reentrantly!\n",
            p->host_no);
     return;
   }
@@ -3906,7 +4246,6 @@
    * Indicate that we're in the interrupt handler.
    */
   save_flags(flags);
-  cli();
   p->flags |= IN_ISR;
 
   if (intstat & CMDCMPLT)
@@ -3915,31 +4254,28 @@
     Scsi_Cmnd *cmd;
     unsigned char qoutcnt;
     unsigned char scb_index;
-    int i, interrupts_cleared = 0;
+    int i;
 
     /*
      * The sequencer will continue running when it
      * issues this interrupt. There may be >1 commands
      * finished, so loop until we've processed them all.
      */
+    cli();
     qoutcnt = inb(p->base + QOUTCNT) & p->qcntmask;
 
 #if 1
   if (qoutcnt >= p->qfullcount - 1)
-    printk(KERN_WARNING "aic7xxx: Command complete near Qfull count, "
-           "qoutcnt = %d.\n", qoutcnt);
+    printk(KERN_WARNING "(scsi%d:-1:-1:-1) Command complete near Qfull count, "
+           "qoutcnt = %d.\n", p->host_no, qoutcnt);
 #endif
     while (qoutcnt > 0)
     {
-      if ((p->flags & PAGE_ENABLED) != 0)
+      if (p->flags & PAGE_ENABLED)
       {
         p->cmdoutcnt += qoutcnt;
-        if (p->cmdoutcnt >= p->qfullcount)
+        if ( p->cmdoutcnt >= p->qfullcount )
         {
-          /*
-           * Since paging only occurs on aic78x0 chips, we can use
-           * Auto Access Pause to clear the command count.
-           */
           outb(0, p->base + CMDOUTCNT);
           p->cmdoutcnt = 0;
         }
@@ -3947,56 +4283,43 @@
       for (i = 0; i < qoutcnt; i++)
       {
         scb_index = inb(p->base + QOUTFIFO);
-        scb = p->scb_data->scb_array[scb_index];
+	if ( scb_index >= p->scb_data->numscbs )
+	    scb = NULL;
+	else
+            scb = p->scb_data->scb_array[scb_index];
         if (scb == NULL)
         {
-	  printk(KERN_WARNING "scsi%d: CMDCMPLT with invalid SCB index %d, "
-	         "QOUTCNT %d, QINCNT %d\n", p->host_no, scb_index,
+	  printk(KERN_WARNING "(scsi%d:-1:-1:-1) CMDCMPLT with invalid SCB "
+	         "index %d, QOUTCNT %d, QINCNT %d\n", p->host_no, scb_index,
                  inb(p->base + QOUTCNT), inb(p->base + QINCNT));
           continue;
         }
         else if (!(scb->flags & SCB_ACTIVE) || (scb->cmd == NULL))
         {
-	  printk(KERN_WARNING "scsi%d: CMDCMPLT without command for SCB %d, "
-	         "QOUTCNT %d, QINCNT %d, SCB flags 0x%x, cmd 0x%lx\n",
+	  printk(KERN_WARNING "(scsi%d:-1:-1:-1) CMDCMPLT without command for "
+	         "SCB %d, QOUTCNT %d, QINCNT %d, SCB flags 0x%x, cmd 0x%lx\n",
                  p->host_no, scb_index, inb(p->base + QOUTCNT),
                  inb(p->base + QINCNT), scb->flags, (unsigned long) scb->cmd);
 	  continue;
         }
-        cmd = scb->cmd;
-        if (scb->hscb->residual_SG_segment_count != 0)
-        {
-          aic7xxx_calculate_residual(p, scb);
-        }
-        if ((scb->flags & SCB_QUEUED_ABORT) != 0)
-        {
-          /*
-           * Have to clean up any possible entries in the
-           * waiting queue and the QINFIFO.
-           */
-          int target;
-          char channel;
-          int lun;
-          unsigned char tag;
-
-          tag = SCB_LIST_NULL;
-          target = cmd->target;
-          lun = cmd->lun;
-          channel = (scb->hscb->target_channel_lun & SELBUSB) ? 'B' : 'A';
-          if (scb->hscb->control & TAG_ENB)
-          {
-            tag = scb->hscb->tag;
-          }
-          aic7xxx_reset_device(p, target, channel, lun, tag);
-          /*
-           * Run the done queue, but don't complete the commands; we
-           * do this once at the end of the loop.
-           */
-          aic7xxx_run_done_queue(p, /*complete*/ FALSE);
-        }
-        cmd->result |= (aic7xxx_error(cmd) << 16);
-        p->device_status[TARGET_INDEX(cmd)].flags |= DEVICE_SUCCESS;
-        aic7xxx_done(p, scb);
+	switch (status_byte(scb->hscb->target_status))
+	{
+	  case QUEUE_FULL:
+	  case BUSY:
+	    scb->hscb->target_status = 0;
+            scb->cmd->result = 0;
+            aic7xxx_error(scb->cmd) = DID_OK;
+	    break;
+	  default:
+            cmd = scb->cmd;
+            if (scb->hscb->residual_SG_segment_count != 0)
+            {
+              aic7xxx_calculate_residual(p, scb);
+            }
+            cmd->result |= (aic7xxx_error(cmd) << 16);
+            p->device_status[TARGET_INDEX(cmd)].flags |= DEVICE_SUCCESS;
+            aic7xxx_done(p, scb);
+	}
       }
       /*
        * Clear interrupt status before checking the output queue again.
@@ -4014,8 +4337,7 @@
     {
       outb(CLRCMDINT, p->base + CLRINT);
     }
-
-    aic7xxx_done_cmds_complete(p);
+    restore_flags(flags);
   }
 
   if (intstat & BRKADRINT)
@@ -4031,32 +4353,39 @@
         printk(KERN_ERR "  %s\n", hard_error[i].errmesg);
       }
     }
-    printk("scsi%d: BRKADRINT, error 0x%x, seqaddr 0x%x.\n", p->host_no,
-           inb(p->base + ERROR),
-           (inb(p->base + SEQADDR1) << 8) | inb(p->base + SEQADDR0));
-    aic7xxx_reset_device(p, ALL_TARGETS, ALL_CHANNELS, ALL_LUNS, SCB_LIST_NULL);
-    aic7xxx_run_done_queue(p, /*complete*/ TRUE);
+    aic7xxx_reset_channel(p, 'A', TRUE);
+    if ( p->bus_type == AIC_TWIN )
+    {
+      aic7xxx_reset_channel(p, 'B', TRUE);
+      restart_sequencer(p);
+      pause_sequencer(p);
+    }
+    outb(CLRBRKADRINT, p->base + CLRINT);
   }
 
   if (intstat & SEQINT)
   {
+    cli();
     aic7xxx_handle_seqint(p, intstat);
+    restore_flags(flags);
   }
 
   if (intstat & SCSIINT)
   {
+    cli();
     aic7xxx_handle_scsiint(p, intstat);
+    restore_flags(flags);
   }
 
-  if (p->waiting_scbs.head != NULL)
-  {
-    aic7xxx_run_waiting_queues(p);
-  }
-
+  aic7xxx_done_cmds_complete(p);
+  cli();
+  aic7xxx_run_waiting_queues(p);
+  unpause_sequencer(p, TRUE);
   p->flags &= ~IN_ISR;
   restore_flags(flags);
 }
 
+
 /*+F*************************************************************************
  * Function:
  *   aic7xxx_device_queue_depth
@@ -4078,6 +4407,9 @@
 aic7xxx_device_queue_depth(struct aic7xxx_host *p, Scsi_Device *device)
 {
   int default_depth = 2;
+  unsigned char tindex;
+
+  tindex = device->id | (device->channel << 3);
 
   device->queue_depth = default_depth;
 #ifdef AIC7XXX_TAGGED_QUEUEING
@@ -4103,9 +4435,9 @@
  
     if (!(p->discenable & target_mask))
     {
-      printk(KERN_INFO "(scsi%d:%d:%d) Disconnection disabled, unable to "
+      printk(KERN_INFO "(scsi%d:%d:%d:%d) Disconnection disabled, unable to "
              "enable tagged queueing.\n",
-             p->host_no, device->id, device->channel);
+             p->host_no, device->channel, device->id, device->lun);
     }
     else
     {
@@ -4118,9 +4450,7 @@
       }
       else
       {
-        unsigned char  tindex;
 
-        tindex = device->id | (device->channel << 3);
         if (aic7xxx_tag_info[p->instance].tag_commands[tindex] < 0)
         {
           tag_enabled = FALSE;
@@ -4141,10 +4471,12 @@
       {
         if (aic7xxx_verbose)
         {
-    	  printk(KERN_INFO "(scsi%d:%d:%d) Enabled tagged queuing, "
+    	  printk(KERN_INFO "(scsi%d:%d:%d:%d) Enabled tagged queuing, "
     	         "queue depth %d.\n", p->host_no,
-    	         device->id, device->channel, device->queue_depth);
+    	         device->channel, device->id, device->lun, device->queue_depth);
         }
+        p->device_status[tindex].max_queue_depth = device->queue_depth;
+        p->device_status[tindex].temp_queue_depth = device->queue_depth;
         device->tagged_queue = 1;
         device->current_tag = SCB_LIST_NULL;
       }
@@ -5064,6 +5396,7 @@
   }
 
   p->host = host;
+  p->last_reset = 0;
   p->host_no = host->host_no;
   p->isr_count = 0;
   p->next = NULL;
@@ -5078,6 +5411,15 @@
     p->device_status[i].flags = 0;
     p->device_status[i].active_cmds = 0;
     p->device_status[i].last_reset = 0;
+    p->device_status[i].last_queue_full = 0;
+    p->device_status[i].last_queue_full_count = 0;
+    p->device_status[i].max_queue_depth = 2;
+    p->device_status[i].temp_queue_depth = 2;
+    scbq_init(&p->device_status[i].delayed_scbs);
+    init_timer(&p->device_status[i].timer);
+    p->device_status[i].timer.expires = 0;
+    p->device_status[i].timer.data = (unsigned long)p;
+    p->device_status[i].timer.function = (void *)aic7xxx_timer;
   }
   if (aic7xxx_boards[p->irq] == NULL)
   {
@@ -5085,7 +5427,7 @@
     int irq_flags = 0;
 
 #ifdef AIC7XXX_OLD_ISR_TYPE
-    irg_flags = SA_INTERRUPT;
+    irq_flags = SA_INTERRUPT;
 #endif
     /*
      * Warning! This must be done before requesting the irq.  It is
@@ -5108,6 +5450,12 @@
     {
       result = (request_irq(p->irq, aic7xxx_isr, irq_flags | SA_SHIRQ,
                 "aic7xxx", NULL));
+      if (result < 0)
+      {
+        irq_flags = (irq_flags & SA_INTERRUPT) ? 0 : SA_INTERRUPT;
+        result = (request_irq(p->irq, aic7xxx_isr, irq_flags | SA_SHIRQ,
+                "aic7xxx", NULL));
+      }
     }
     if (result < 0)
     {
@@ -5365,6 +5713,7 @@
   {
     outb(p->qfullcount, p->base + FIFODEPTH);
     outb(0, p->base + CMDOUTCNT);
+    p->cmdoutcnt = 0;
   }
 
   /*
@@ -5849,7 +6198,6 @@
       else
         hcntrl = inb(base + HCNTRL) & IRQMS;  /* Default */
       outb(hcntrl | PAUSE, base + HCNTRL);
-
       p = aic7xxx_alloc(template, base, 0, chip_type, 0, NULL);
       if (p == NULL)
       {
@@ -6303,10 +6651,40 @@
     }
   }
 #endif CONFIG_PCI
-
   return (found);
 }
 
+static void
+aic7xxx_fake_scsi_done(Scsi_Cmnd *cmd)
+{
+  memset(&cmd->sense_buffer[0], '\0', sizeof(cmd->sense_buffer));
+}
+
+static void
+aic7xxx_make_fake_cmnd(struct aic7xxx_host *p, Scsi_Cmnd *cmd, int which)
+{
+  Scsi_Cmnd *cmd2;
+
+  if (which == 0)
+    p->device_status[TARGET_INDEX(cmd)].scsi_cmnd0 = cmd2 =
+	kmalloc(sizeof(Scsi_Cmnd), GFP_ATOMIC);
+  else
+    p->device_status[TARGET_INDEX(cmd)].scsi_cmnd1 = cmd2 =
+	kmalloc(sizeof(Scsi_Cmnd), GFP_ATOMIC);
+  if (cmd2 != NULL)
+  {
+    memcpy(cmd2, cmd, sizeof(Scsi_Cmnd));
+    memset(&cmd2->cmnd[0], '\0', sizeof(cmd2->cmnd));
+    cmd2->cmnd[0] = TEST_UNIT_READY;
+    cmd2->cmd_len = 6;
+    cmd2->bufflen = 0;
+    cmd2->request_bufflen = 0;
+    cmd2->buffer = NULL;
+    cmd2->request_buffer = NULL;
+    cmd2->use_sg = 0;
+    cmd2->underflow = 0;
+  }
+}
 
 /*+F*************************************************************************
  * Function:
@@ -6337,7 +6715,7 @@
     {
       cmd->tag = hscb->tag;
       p->device_status[TARGET_INDEX(cmd)].commands_sent++;
-      if (p->device_status[TARGET_INDEX(cmd)].commands_sent < 75)
+      if (p->device_status[TARGET_INDEX(cmd)].commands_sent < 200)
       {
         hscb->control |= MSG_SIMPLE_Q_TAG;
       }
@@ -6349,12 +6727,27 @@
     }
 #endif  /* Tagged queueing */
   }
-
   if ((p->needwdtr & mask) && !(p->wdtr_pending & mask))
   {
-    p->wdtr_pending |= mask;
-    hscb->control |= MK_MESSAGE;
-    scb->flags |= SCB_MSGOUT_WDTR;
+    if ( cmd->cmnd[0] == TEST_UNIT_READY )
+    {
+      p->wdtr_pending |= mask;
+      hscb->control |= MK_MESSAGE;
+      scb->flags |= SCB_MSGOUT_WDTR;
+    }
+    else
+    {
+      if ( p->device_status[TARGET_INDEX(cmd)].scsi_cmnd0 == NULL )
+        aic7xxx_make_fake_cmnd(p, cmd, 0);
+      if ( p->device_status[TARGET_INDEX(cmd)].scsi_cmnd1 == NULL )
+        aic7xxx_make_fake_cmnd(p, cmd, 1);
+      if ( p->device_status[TARGET_INDEX(cmd)].scsi_cmnd0 != NULL )
+        aic7xxx_queue(p->device_status[TARGET_INDEX(cmd)].scsi_cmnd0,
+          aic7xxx_fake_scsi_done);
+      if ( p->device_status[TARGET_INDEX(cmd)].scsi_cmnd1 != NULL )
+        aic7xxx_queue(p->device_status[TARGET_INDEX(cmd)].scsi_cmnd1,
+          aic7xxx_fake_scsi_done);
+    }
 #if 0
     printk("scsi%d: Sending WDTR request to target %d.\n",
            p->host_no, cmd->target);
@@ -6364,9 +6757,22 @@
   {
     if ((p->needsdtr & mask) && !(p->sdtr_pending & mask))
     {
-      p->sdtr_pending |= mask;
-      hscb->control |= MK_MESSAGE;
-      scb->flags |= SCB_MSGOUT_SDTR;
+      if ( cmd->cmnd[0] == TEST_UNIT_READY )
+      {
+        p->sdtr_pending |= mask;
+        hscb->control |= MK_MESSAGE;
+        scb->flags |= SCB_MSGOUT_SDTR;
+      }
+      else
+      {
+        if ( p->device_status[TARGET_INDEX(cmd)].scsi_cmnd0 == NULL )
+          aic7xxx_make_fake_cmnd(p, cmd, 0);
+        if ( p->device_status[TARGET_INDEX(cmd)].scsi_cmnd1 == NULL )
+          aic7xxx_make_fake_cmnd(p, cmd, 1);
+        if ( p->device_status[TARGET_INDEX(cmd)].scsi_cmnd1 != NULL )
+          aic7xxx_queue(p->device_status[TARGET_INDEX(cmd)].scsi_cmnd1,
+            aic7xxx_fake_scsi_done);
+      }
 #if 0
       printk("scsi%d: Sending SDTR request to target %d.\n",
              p->host_no, cmd->target);
@@ -6465,13 +6871,9 @@
   long processor_flags;
   struct aic7xxx_host *p;
   struct aic7xxx_scb *scb;
+  int tindex = TARGET_INDEX(cmd);
 
   p = (struct aic7xxx_host *) cmd->host->hostdata;
-  if (p->host != cmd->host)
-  {
-    printk(KERN_INFO "scsi%d: Internal host structure != scsi.c host "
-      "structure.\n", p->host_no);
-  }
 
   /*
    * Check to see if channel was scanned.
@@ -6496,11 +6898,14 @@
 	cmd->lun & 0x07);
 #endif
 
-  if (p->device_status[TARGET_INDEX(cmd)].active_cmds
-      > cmd->device->queue_depth)
-  {
-    printk(KERN_WARNING "(scsi%d:%d:%d) Commands queued exceeds queue depth\n",
-           p->host_no, cmd->target, cmd->channel);
+  if ( (p->device_status[tindex].active_cmds > cmd->device->queue_depth) &&
+      !(p->wdtr_pending & (0x1 << tindex)) && 
+      !(p->sdtr_pending & (0x1 << tindex)) )
+  {
+    printk(KERN_WARNING "(scsi%d:%d:%d:%d) Commands queued exceeds queue "
+	   "depth, active=%d\n",
+           p->host_no, CTL_OF_CMD(cmd), 
+	   p->device_status[tindex].active_cmds);
   }
   scb = aic7xxx_allocate_scb(p);
   if (scb == NULL)
@@ -6548,13 +6953,20 @@
 
     save_flags(processor_flags);
     cli();
-    scbq_insert_tail(&p->waiting_scbs, scb);
-    if ((p->flags & (IN_ISR | IN_TIMEOUT)) == 0)
+    if (p->device_status[tindex].delayed_scbs.head != NULL) 
+    {
+        scbq_insert_tail(&p->device_status[tindex].delayed_scbs, scb);
+    }
+    else
+    {
+        scbq_insert_tail(&p->waiting_scbs, scb);
+    }
+    if ((p->flags & (IN_ISR | IN_ABORT | RESET_PENDING)) == 0)
     {
       aic7xxx_run_waiting_queues(p);
     }
-
     restore_flags(processor_flags);
+
 #if 0
     printk("aic7xxx: (queue) After - cmd(0x%lx) scb->cmd(0x%lx) pos(%d).\n",
            (long) cmd, (long) scb->cmd, scb->hscb->tag);
@@ -6579,103 +6991,57 @@
 {
   struct aic7xxx_scb   *scb;
   struct aic7xxx_hwscb *hscb;
-  unsigned char bus_state;
   int result = -1;
   char channel;
+  unsigned char saved_scbptr, lastphase;
+  unsigned char hscb_index, linked_next;
+  int disconnected;
 
   scb = (p->scb_data->scb_array[aic7xxx_position(cmd)]);
   hscb = scb->hscb;
 
-  /*
-   * Ensure that the card doesn't do anything behind our back.
-   * Also make sure that we didn't just miss an interrupt that
-   * could affect this abort/reset.
-   */
-  pause_sequencer(p);
-  while (inb(p->base + INTSTAT) & INT_PEND);
-  {
-    aic7xxx_isr(p->irq, (void *) NULL, (void *) NULL);
-    pause_sequencer(p);
-  } 
-  if ((cmd != scb->cmd) || ((scb->flags & SCB_ACTIVE) == 0))
-  {
-    result = SCSI_RESET_NOT_RUNNING;
-    unpause_sequencer(p, /* unpause_always */ TRUE);
-    return(result);
-  }
-
-
-  printk(KERN_WARNING "(scsi%d:%d:%d) Abort_reset, scb flags 0x%x, ",
-         p->host_no, TC_OF_SCB(scb), scb->flags);
-  bus_state = inb(p->base + LASTPHASE);
-
-  switch (bus_state)
+  lastphase = inb(p->base + LASTPHASE);
+  if (aic7xxx_verbose > 1)
   {
-    case P_DATAOUT:
-      printk("Data-Out phase, ");
-      break;
-    case P_DATAIN:
-      printk("Data-In phase, ");
-      break;
-    case P_COMMAND:
-      printk("Command phase, ");
-      break;
-    case P_MESGOUT:
-      printk("Message-Out phase, ");
-      break;
-    case P_STATUS:
-      printk("Status phase, ");
-      break;
-    case P_MESGIN:
-      printk("Message-In phase, ");
-      break;
-    default:
+    printk(KERN_WARNING "(scsi%d:%d:%d:%d) Bus Device reset, scb flags 0x%x, ",
+         p->host_no, CTL_OF_SCB(scb), scb->flags);
+    switch (lastphase)
+    {
+      case P_DATAOUT:
+        printk("Data-Out phase, ");
+        break;
+      case P_DATAIN:
+        printk("Data-In phase, ");
+        break;
+      case P_COMMAND:
+        printk("Command phase, ");
+        break;
+      case P_MESGOUT:
+        printk("Message-Out phase, ");
+        break;
+      case P_STATUS:
+        printk("Status phase, ");
+        break;
+      case P_MESGIN:
+        printk("Message-In phase, ");
+        break;
+      default:
       /*
        * We're not in a valid phase, so assume we're idle.
        */
-      printk("while idle, LASTPHASE = 0x%x, ", bus_state);
-      break;
-  }
-  printk("SCSISIGI 0x%x, SEQADDR 0x%x, SSTAT0 0x%x, SSTAT1 0x%x\n",
+        printk("while idle, LASTPHASE = 0x%x, ", lastphase);
+        break;
+    }
+    printk("SCSISIGI 0x%x, SEQADDR 0x%x, SSTAT0 0x%x, SSTAT1 0x%x\n",
          inb(p->base + SCSISIGI),
          inb(p->base + SEQADDR0) | (inb(p->base + SEQADDR1) << 8),
          inb(p->base + SSTAT0), inb(p->base + SSTAT1));
+  }
 
   channel = hscb->target_channel_lun & SELBUSB ? 'B': 'A';
-  /*
-   * Determine our course of action.
-   */
-  if (scb->flags & SCB_ABORT)
-  {
-    /*
-     * Been down this road before; do a full bus reset.
-     */
-    scb->flags |= SCB_RECOVERY_SCB;
-    unpause_sequencer(p, /* unpause_always */ TRUE);
-    result = -1;
-  }
-#if 0
-  else if (hscb->control & TAG_ENB)
-    {
-      /*
-       * We could be starving this command; try sending and ordered tag
-       * command to the target we come from.
-       */
-      scb->flags |= SCB_SENTORDEREDTAG | SCB_RECOVERY_SCB;
-      p->orderedtag = p->orderedtag | 0xFF;
-      result = SCSI_RESET_PENDING;
-      unpause_sequencer(p, /* unpause_always */ TRUE);
-      printk(KERN_WARNING "scsi%d: Abort_reset, odered tag queued.\n",
-             p->host_no);
-    }
-#endif
-  else
-  {
-    unsigned char active_scb_index, saved_scbptr;
-    struct aic7xxx_scb *active_scb;
 
     /*
-     * Send an Abort Message:
+     * Send a Device Reset Message:
      * The target that is holding up the bus may not be the same as
      * the one that triggered this timeout (different commands have
      * different timeout lengths).  Our strategy here is to queue an
@@ -6686,148 +7052,136 @@
      * fails, we'll get another timeout a few seconds later which will
      * attempt a bus reset.
      */
-    saved_scbptr = inb(p->base + SCBPTR);
-    active_scb_index = inb(p->base + SCB_TAG);
-    active_scb = p->scb_data->scb_array[active_scb_index];
+  saved_scbptr = inb(p->base + SCBPTR);
+  disconnected = FALSE;
 
-    if (bus_state != P_BUSFREE)
+  if (lastphase != P_BUSFREE)
+  {
+    if (inb(p->base + SCB_TAG) >= p->scb_data->numscbs)
     {
-      if (active_scb_index >= p->scb_data->numscbs)
-      {
         /*
          * Perform a bus reset.
          *
          * XXX - We want to queue an abort for the timedout SCB
          *       instead.
          */
-        result = -1;
-        printk(KERN_WARNING "scsi%d: Invalid SCB ID %d is active, "
-               "SCB flags = 0x%x.\n", p->host_no, scb->hscb->tag, scb->flags);
-      }
-      else
+      printk(KERN_WARNING "scsi%d: Invalid SCB ID %d is active, "
+             "SCB flags = 0x%x.\n", p->host_no, scb->hscb->tag, scb->flags);
+      return(SCSI_RESET_ERROR);
+    }
+    if (scb->hscb->tag == inb(p->base + SCB_TAG))
+    { 
+      if ( (lastphase != P_MESGOUT) && (lastphase != P_MESGIN) )
       {
         /* Send the abort message to the active SCB. */
         outb(1, p->base + MSG_LEN);
-        if (active_scb->hscb->control & TAG_ENB)
-        {
-          outb(MSG_ABORT_TAG, p->base + MSG_OUT);
-        }
-        else
-        {
-          outb(MSG_ABORT, p->base + MSG_OUT);
-        }
-        outb(bus_state | ATNO, p->base + SCSISIGO);
-        printk(KERN_WARNING "scsi%d: abort message in message buffer\n",
-               p->host_no);
-        active_scb->flags |= SCB_ABORT | SCB_RECOVERY_SCB;
-        if (active_scb != scb)
-        {
-          /*
-           * XXX - We would like to increment the timeout on scb, but
-           *       access to that routine is denied because it is hidden
-           *       in scsi.c.  If we were able to do this, it would give
-           *       scb a new lease on life.
-           */
-          result = SCSI_RESET_PENDING;
-          aic7xxx_error(active_scb->cmd) = DID_RESET;
-        }
-        else
-        {
-          aic7xxx_error(scb->cmd) = DID_RESET;
-          result = SCSI_RESET_PENDING;
-        }
-        unpause_sequencer(p, /* unpause_always */ TRUE);
-      }
-    }
-    else
-    {
-      unsigned char hscb_index, linked_next;
-      int disconnected;
-
-      disconnected = FALSE;
-      hscb_index = aic7xxx_find_scb(p, scb);
-      if (hscb_index == SCB_LIST_NULL)
-      {
-        disconnected = TRUE;
-        linked_next = (scb->hscb->data_count >> 24) & 0xFF;
+        outb(MSG_BUS_DEV_RESET, p->base + MSG_OUT);
+        outb(lastphase | ATNO, p->base + SCSISIGO);
+	if (aic7xxx_verbose > 1)
+          printk(KERN_WARNING "(scsi%d:%d:%d:%d) Device reset message in "
+		"message buffer\n", p->host_no, CTL_OF_SCB(scb));
+        scb->flags |= SCB_RESET | SCB_DEVICE_RESET;
+        aic7xxx_error(scb->cmd) = DID_RESET;
+	p->device_status[TARGET_INDEX(scb->cmd)].flags &= 
+		~DEVICE_SUCCESS;
+	p->device_status[TARGET_INDEX(scb->cmd)].flags |= 
+		BUS_DEVICE_RESET_PENDING;
+        return(SCSI_RESET_PENDING);
       }
       else
       {
-        outb(hscb_index, p->base + SCBPTR);
-        if (inb(p->base + SCB_CONTROL) & DISCONNECTED)
-        {
-          disconnected = TRUE;
-        }
-        linked_next = inb(p->base + SCB_LINKED_NEXT);
+	/* 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(KERN_WARNING "(scsi%d:%d:%d:%d) Device reset, Message buffer "
+		"in use\n", p->host_no, CTL_OF_SCB(scb));
+	scb->flags |= SCB_RESET | SCB_DEVICE_RESET;
+        aic7xxx_error(scb->cmd) = DID_RESET;
+	p->device_status[TARGET_INDEX(scb->cmd)].flags &= 
+		~DEVICE_SUCCESS;
+	p->device_status[TARGET_INDEX(scb->cmd)].flags |= 
+		BUS_DEVICE_RESET_PENDING;
+        return(SCSI_RESET_ERROR);
       }
-      if (disconnected)
-      {
+    }
+  }
+  hscb_index = aic7xxx_find_scb(p, scb);
+  if (hscb_index == SCB_LIST_NULL)
+  {
+    disconnected = TRUE;
+    linked_next = (scb->hscb->data_count >> 24) & 0xFF;
+  }
+  else
+  {
+    outb(hscb_index, p->base + SCBPTR);
+    if (inb(p->base + SCB_CONTROL) & DISCONNECTED)
+    {
+      disconnected = TRUE;
+    }
+    linked_next = inb(p->base + SCB_LINKED_NEXT);
+  }
+  if (disconnected)
+  {
         /*
          * Simply set the ABORT_SCB control bit and preserve the
          * linked next pointer.
          */
-        scb->hscb->control |= ABORT_SCB | MK_MESSAGE;
-        scb->hscb->data_count &= ~0xFF000000;
-        scb->hscb->data_count |= linked_next << 24;
-        if ((p->flags & PAGE_ENABLED) == 0)
-        {
-          scb->hscb->control &= ~DISCONNECTED;
-        }
-        scb->flags |= SCB_QUEUED_ABORT | SCB_ABORT | SCB_RECOVERY_SCB;
-        if (hscb_index != SCB_LIST_NULL)
-        {
-          unsigned char scb_control;
+    scb->hscb->control |= ABORT_SCB | MK_MESSAGE;
+    scb->hscb->data_count &= ~0xFF000000;
+    scb->hscb->data_count |= linked_next << 24;
+    if ((p->flags & PAGE_ENABLED) == 0)
+    {
+      scb->hscb->control &= ~DISCONNECTED;
+    }
+    scb->flags |= SCB_RESET | SCB_DEVICE_RESET | SCB_QUEUED_ABORT;
+    p->device_status[TARGET_INDEX(scb->cmd)].flags &= ~DEVICE_SUCCESS;
+    p->device_status[TARGET_INDEX(scb->cmd)].flags |= 
+	BUS_DEVICE_RESET_PENDING;
+    if (hscb_index != SCB_LIST_NULL)
+    {
+      unsigned char scb_control;
 
-          scb_control = inb(p->base + SCB_CONTROL);
-          outb(scb_control | MK_MESSAGE| ABORT_SCB, p->base + SCB_CONTROL);
-        }
+      scb_control = inb(p->base + SCB_CONTROL);
+      outb(scb_control | MK_MESSAGE| ABORT_SCB, p->base + 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, unbusy it first so that
          * we don't get held back from sending the command.
          */
-        if ((scb->hscb->control & TAG_ENB) == 0)
-        {
-          unsigned char target;
-          int lun;
-
-          target = scb->cmd->target;
-          lun = scb->cmd->lun;
-          aic7xxx_search_qinfifo(p, target, channel, lun, SCB_LIST_NULL,
-              0, /* requeue */ TRUE);
-        }
-        printk(KERN_WARNING "(scsi%d:%d:%d) Queueing an Abort SCB.\n",
-               p->host_no, TC_OF_SCB(scb));
-        scbq_insert_head(&p->waiting_scbs, scb);
-        scb->flags |= SCB_WAITINGQ;
-        outb(saved_scbptr, p->base + SCBPTR);
-        if ((p->flags & IN_ISR) == 0)
-        {
-          /*
-           * Processing the waiting queue may unpause us.
-           */
-          aic7xxx_run_waiting_queues(p);
-          /*
-           * If we are using AAP, aic7xxx_run_waiting_queues() will not
-           * unpause us, so ensure we are unpaused.
-           */
-          unpause_sequencer(p, /*unpause_always*/ FALSE);
-        }
-        else
-        {
-          unpause_sequencer(p, /*unpause_always*/ TRUE);
-        }
-        result = SCSI_RESET_PENDING;
-      }
-      else
-      {
-        scb->flags |= SCB_RECOVERY_SCB;
-        unpause_sequencer(p, /* unpause_always */ TRUE);
-        result = -1;
-      }
+    if ((scb->hscb->control & TAG_ENB) == 0)
+    {
+      aic7xxx_search_qinfifo(p, cmd->target, channel, cmd->lun, 
+	     SCB_LIST_NULL, 0, TRUE, &p->waiting_scbs);
+    }
+    if (aic7xxx_verbose > 1)
+      printk(KERN_WARNING "(scsi%d:%d:%d:%d) Queueing device reset command.\n",
+           p->host_no, CTL_OF_SCB(scb));
+    if ( !(scb->flags & SCB_WAITINGQ) ) /* Make sure we don't already have   */
+    {					/* an abort scb queued, or else we   */
+					/* corrupt the waiting queue and     */
+					/* active_cmds counter by queueing   */
+					/* again.			     */
+      scbq_insert_head(&p->waiting_scbs, scb);
+      scb->flags |= SCB_WAITINGQ;
+      p->device_status[TARGET_INDEX(scb->cmd)].active_cmds--;
+    }
+    else
+    {
+      scb->flags &= ~SCB_ABORT;
     }
+    result = SCSI_RESET_PENDING;
+  }
+  else if (result == -1)
+  {
+    result = SCSI_RESET_ERROR;
   }
+  outb(saved_scbptr, p->base + SCBPTR);
   return (result);
 }
 
@@ -6842,115 +7196,297 @@
 int
 aic7xxx_abort(Scsi_Cmnd *cmd)
 {
-#if 0
   struct aic7xxx_scb  *scb = NULL;
-#endif
   struct aic7xxx_host *p;
-#if 0
-  int    base, result;
+  int    result, found=0;
+  unsigned char tmp_char, saved_hscbptr, next_hscbptr, prev_hscbptr;
   unsigned long processor_flags;
-#endif
+  Scsi_Cmnd *cmd_next, *cmd_prev;
 
   p = (struct aic7xxx_host *) cmd->host->hostdata;
-#if 0
   scb = (p->scb_data->scb_array[aic7xxx_position(cmd)]);
-  base = p->base;
 
   save_flags(processor_flags);
+  pause_sequencer(p);
   cli();
-#endif
 
-#if 1
-  switch (aic7xxx_reset(cmd, 0) & 0x0f)
-  {
-    case SCSI_RESET_SNOOZE:
-      return(SCSI_ABORT_SNOOZE);
-    case SCSI_RESET_PENDING:
-      return(SCSI_ABORT_PENDING);
-    case SCSI_RESET_SUCCESS:
-      return(SCSI_ABORT_SUCCESS);
-    case SCSI_RESET_NOT_RUNNING:
-      return(SCSI_ABORT_NOT_RUNNING);
-    case SCSI_RESET_ERROR:
-      return(SCSI_ABORT_ERROR);
-    default:
-      printk(KERN_WARNING "scsi%d Unknown abort/reset return state.\n",
-        p->host_no);
-      return(SCSI_ABORT_ERROR);
-  }  /*  NOT REACHED */
-#else
+/*
+ *  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.
+ */
 
-#ifdef AIC7XXX_DEBUG_ABORT
-  if (scb != NULL)
+  while ( (inb(p->base + INTSTAT) & INT_PEND) && !(p->flags & IN_ISR) )
   {
-    printk("(scsi%d:%d:%d) Aborting scb %d, flags 0x%x\n",
-           p->host_no, TC_OF_SCB(scb), scb->hscb->tag, scb->flags);
+    aic7xxx_isr(p->irq, (void *)NULL, (void *)NULL);
+    pause_sequencer(p);
   }
-  else
+
+  if (scb == NULL)    /*  Totally bogus cmd since it points beyond our  */
+  {                   /*  valid SCB range.  The suspect scb hasn't been */
+                      /*  allocated yet.                                */
+    printk(KERN_WARNING "(scsi%d:%d:%d:%d) Abort called with bogus Scsi_Cmnd->"
+	"SCB mapping.\n", p->host_no, CTL_OF_CMD(cmd));
+    unpause_sequencer(p, TRUE);
+    restore_flags(processor_flags);
+    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.                          */
+    cmd_next = p->completeq.head;
+    cmd_prev = NULL;
+    while (cmd_next != NULL) 
+    {
+      if (cmd_next == cmd) 
+      {
+	if (aic7xxx_verbose > 1)
+          printk(KERN_WARNING "(scsi%d:%d:%d:%d) Abort called for command "
+	  "on completeq, completing.\n", p->host_no, CTL_OF_CMD(cmd));
+	if ( cmd_prev == NULL )
+	  p->completeq.head = (Scsi_Cmnd *)cmd_next->host_scribble;
+	else
+	  cmd_prev->host_scribble = cmd_next->host_scribble;
+	cmd_next->done(cmd_next);
+        unpause_sequencer(p, TRUE);
+	restore_flags(processor_flags);
+	return(SCSI_ABORT_SUCCESS);
+      }				  
+      cmd_prev = cmd_next;
+      cmd_next = (Scsi_Cmnd *)cmd_next->host_scribble;
+    }
+    printk(KERN_WARNING "(scsi%d:%d:%d:%d) Abort called for already completed"
+	" command.\n", p->host_no, CTL_OF_CMD(cmd));
+    unpause_sequencer(p, TRUE);
+    restore_flags(processor_flags);
+    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
+ *     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) )
   {
-    printk("aic7xxx: Abort called with no SCB for cmd.\n");
+    if (aic7xxx_verbose > 2)
+      printk(KERN_WARNING "(scsi%d:%d:%d:%d) SCB aborted once already, "
+	"escalating.\n", p->host_no, CTL_OF_SCB(scb));
+    unpause_sequencer(p, TRUE);
+    restore_flags(processor_flags);
+    return(SCSI_ABORT_SNOOZE);
   }
-#endif
+  if ( (p->flags & (RESET_PENDING | ABORT_PENDING)) || 
+          (p->device_status[TARGET_INDEX(scb->cmd)].flags & 
+           BUS_DEVICE_RESET_PENDING) )
+  {
+    if (aic7xxx_verbose > 2)
+      printk(KERN_WARNING "(scsi%d:%d:%d:%d) Reset/Abort pending for this "
+	"device, not wasting our time.\n", p->host_no, CTL_OF_SCB(scb));
+    unpause_sequencer(p, TRUE);
+    restore_flags(processor_flags);
+    return(SCSI_ABORT_PENDING);
+  }
+
+  found = 0;
+  p->flags |= IN_ABORT;
+  if (aic7xxx_verbose)
+    printk(KERN_WARNING "(scsi%d:%d:%d:%d) Aborting scb %d, flags 0x%x\n",
+         p->host_no, CTL_OF_SCB(scb), scb->hscb->tag, scb->flags);
+
+/*
+ *   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 (p->flags & IN_TIMEOUT)
+  if ( scb->hscb->tag == inb(p->base + SCB_TAG) )
   {
-    /*
-     * We've already started a recovery operation.
-     */
-    if ((scb->flags & SCB_RECOVERY_SCB) == 0)
-    {
-      restore_flags(processor_flags);
-      return (SCSI_ABORT_PENDING);
-    }
-    else
+   /*
+    *  Check to see if the sequencer is just sitting on this command, or
+    *   if it's actively being run.
+    */
+    result = inb(p->base + LASTPHASE);
+    switch (result)
     {
-      /*
-       * This is the second time we've tried to abort the recovery
-       * SCB.  We want the mid-level SCSI code to call the reset
-       * function to reset the SCSI bus.
-       */
-      restore_flags(processor_flags);
-      return (SCSI_ABORT_NOT_RUNNING);
+      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 > 1)
+         printk(KERN_WARNING "(scsi%d:%d:%d:%d) SCB is currently active.  "
+		"Waiting on completion.\n", p->host_no, CTL_OF_SCB(scb));
+        unpause_sequencer(p, TRUE);
+	p->flags &= ~IN_ABORT;
+	scb->flags |= SCB_RECOVERY_SCB; /*  Note the fact that we've been  */
+	p->flags |= ABORT_PENDING;      /*  here so we will know not to    */
+        restore_flags(processor_flags); /*  muck with other SCBs if this   */
+        return(SCSI_ABORT_PENDING);      /*  one doesn't complete and clear */
+        break;                          /*  out.                           */
+      default:
+        break;
     }
   }
-  if (cmd->serial_number != cmd->serial_number_at_timeout)
+
+  if ((found == 0) && (scb->flags & SCB_WAITINGQ))
   {
-    result = SCSI_ABORT_NOT_RUNNING;
+      int tindex = TARGET_INDEX(cmd);
+     
+      if (aic7xxx_verbose > 1) 
+        printk(KERN_WARNING "(scsi%d:%d:%d:%d) SCB found on waiting list and "
+	    "aborted.\n", p->host_no, CTL_OF_SCB(scb));
+      scbq_remove(&p->waiting_scbs, scb);
+      scbq_remove(&p->device_status[tindex].delayed_scbs, scb);
+      p->device_status[tindex].active_cmds++;
+      scb->flags &= ~(SCB_WAITINGQ | SCB_ACTIVE);
+      scb->flags |= SCB_ABORT | SCB_QUEUED_FOR_DONE;
+      found = 1;
   }
-  else if (scb == NULL)
+
+/*
+ *  We just checked the waiting_q, now for the QINFIFO
+ */
+  if ( found == 0 )
+  {
+    if ( ((found = aic7xxx_search_qinfifo(p, cmd->target, 
+                     INT_TO_CHAN(cmd->channel),
+	             cmd->lun, scb->hscb->tag, SCB_ABORT | SCB_QUEUED_FOR_DONE,
+	             FALSE, NULL)) != 0) && (aic7xxx_verbose > 1))
+      printk(KERN_WARNING "(scsi%d:%d:%d:%d) SCB found in QINFIFO and "
+        "aborted.\n", p->host_no, CTL_OF_SCB(scb));
+  }
+
+/*
+ *  QINFIFO, waitingq, completeq done.  Next, check WAITING_SCB list in card
+ */
+
+  if ( found == 0 )
   {
-    result = SCSI_ABORT_NOT_RUNNING;
+    unsigned char scb_next_ptr;
+    prev_hscbptr = SCB_LIST_NULL;
+    saved_hscbptr = inb(p->base + SCBPTR);
+    next_hscbptr = inb(p->base + WAITING_SCBH);
+    while ( next_hscbptr != SCB_LIST_NULL )
+    {
+      outb( next_hscbptr, p->base + SCBPTR );
+      if ( scb->hscb->tag == inb(p->base + SCB_TAG) )
+      {
+        found = 1;
+	if (aic7xxx_verbose > 1)
+          printk(KERN_WARNING "(scsi%d:%d:%d:%d) SCB found on hardware waiting"
+	    " list and aborted.\n", p->host_no, CTL_OF_SCB(scb));
+	if ( prev_hscbptr == SCB_LIST_NULL )
+	    outb(inb(p->base + SCB_NEXT), p->base + WAITING_SCBH);
+	else
+	{
+	    scb_next_ptr = inb(p->base + SCB_NEXT);
+	    outb(prev_hscbptr, p->base + SCBPTR);
+	    outb(scb_next_ptr, p->base + SCB_NEXT);
+	    outb(next_hscbptr, p->base + SCBPTR);
+	}
+	outb(SCB_LIST_NULL, p->base + SCB_TAG);
+	aic7xxx_add_curscb_to_free_list(p);
+	scb->flags = SCB_ABORT | SCB_QUEUED_FOR_DONE;
+        break;
+      }
+      prev_hscbptr = next_hscbptr;
+      next_hscbptr = inb(p->base + SCB_NEXT);
+    }
+    outb( saved_hscbptr, p->base + SCBPTR );
   }
-  else if ((scb->cmd != cmd) || (!(scb->flags & SCB_ACTIVE)))
+        
+/*
+ *  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 )
   {
-    result = SCSI_ABORT_NOT_RUNNING;
+    p->flags |= ABORT_PENDING;
+    scb->flags |= SCB_QUEUED_ABORT | SCB_ABORT | SCB_RECOVERY_SCB;
+    scb->hscb->control |= ABORT_SCB | MK_MESSAGE;
+    result=aic7xxx_find_scb(p, scb);
+    if ( result != SCB_LIST_NULL ) 
+    {
+      saved_hscbptr = inb(p->base + SCBPTR);
+      outb(result, p->base + SCBPTR);
+      tmp_char = inb(p->base + SCB_CONTROL);
+      outb( tmp_char | MK_MESSAGE | ABORT_SCB, p->base + SCB_CONTROL);
+      outb(saved_hscbptr, p->base + SCBPTR);
+    }
+    if (aic7xxx_verbose > 1)
+      printk(KERN_WARNING "(scsi%d:%d:%d:%d) SCB disconnected.  Queueing Abort"
+        " SCB.\n", p->host_no, CTL_OF_SCB(scb));
+    if ( (scb->hscb->control & TAG_ENB) == 0 )
+    {
+      aic7xxx_search_qinfifo(p, cmd->target, INT_TO_CHAN(cmd->channel),
+	cmd->lun, SCB_LIST_NULL, 0, TRUE, &p->waiting_scbs);
+    }
+    if ( !(scb->flags & SCB_WAITINGQ) )
+    {
+      scbq_insert_head(&p->waiting_scbs, scb);
+      scb->flags |= SCB_WAITINGQ;
+      p->device_status[TARGET_INDEX(scb->cmd)].active_cmds--;
+    }
   }
   else
-  {
-    /*
-     * XXX - Check use of IN_TIMEOUT to see if we're Doing the
-     *       Right Thing with it.
-     */
-    p->flags |= IN_TIMEOUT;
-    result = aic7xxx_bus_device_reset(p, scb->cmd);
-    switch (result)
-    {
-      case SCSI_RESET_NOT_RUNNING:
-        p->flags &= ~IN_TIMEOUT;
-        result = SCSI_ABORT_NOT_RUNNING;
-        break;
-      case SCSI_RESET_PENDING:
-        result = SCSI_ABORT_PENDING;
-        break;
-      default:
-        p->flags &= ~IN_TIMEOUT;
-        result = SCSI_ABORT_SNOOZE;
-        break;
-     }
+  { 
+    scb->flags &= ~(SCB_ACTIVE | SCB_WAITINGQ);
+    scb->flags |= SCB_ABORT | SCB_QUEUED_FOR_DONE;
   }
+  aic7xxx_run_done_queue(p, TRUE);
+  aic7xxx_run_waiting_queues(p);
+  p->flags &= ~IN_ABORT;
   restore_flags(processor_flags);
-  return (result);
-#endif
+
+/*
+ *  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); 
 }
 
 
@@ -6969,189 +7505,225 @@
 {
   struct aic7xxx_scb *scb = NULL;
   struct aic7xxx_host *p;
-  int    base, found, tindex, min_target, max_target;
+  int    tindex;
   int    result = -1;
   char   channel = 'A';
   unsigned long processor_flags;
+#define	DEVICE_RESET 0x01
+#define BUS_RESET    0x02
+#define HOST_RESET   0x04
+#define FAIL         0x08
+#define	RESET_DELAY  0x10
+  int	action;
+  Scsi_Cmnd *cmd_prev, *cmd_next;
+
+
+  if ( cmd == NULL )
+  {
+    if (aic7xxx_verbose > 1)
+      printk(KERN_WARNING "(aic7xxx) Reset called with NULL Scsi_Cmnd "
+	"pointer, failing.\n");
+    return(SCSI_RESET_SNOOZE);
+  }
 
   p = (struct aic7xxx_host *) cmd->host->hostdata;
   scb = (p->scb_data->scb_array[aic7xxx_position(cmd)]);
-  base = p->base;
-  channel = cmd->channel ? 'B': 'A';
+  channel = INT_TO_CHAN(cmd->channel);
   tindex = TARGET_INDEX(cmd);
 
-#ifdef 0   /* AIC7XXX_DEBUG_ABORT */
-  if (scb != NULL)
-  {
-    printk("(scsi%d:%d:%d) Reset called, scb %d, flags 0x%x\n",
-           p->host_no, TC_OF_SCB(scb), scb->hscb->tag, scb->flags);
-  }
-  else
-  {
-    printk("aic7xxx: Reset called with no SCB for cmd.\n");
-  }
-#endif
-
-  /* 
-   * This routine is called by scsi.c, in which case the interrupts
-   * very well may be on when we are called.  As such, we need to save
-   * the flags to be sure, then turn interrupts off, and then call our
-   * various method funtions which all assume interrupts are off.
-   */
   save_flags(processor_flags);
+  pause_sequencer(p);
   cli();
+  while ( (inb(p->base + INTSTAT) & INT_PEND) && !(p->flags & IN_ISR) )
+  {
+    aic7xxx_isr(p->irq, (void *)NULL, (void *)NULL );
+    pause_sequencer(p);
+  }
 
-  if (scb->cmd != cmd)
-    scb = NULL;
-
-  if (p->flags & IN_TIMEOUT)
+  if (scb == NULL)
   {
-    /*
-     * We've already started a recovery operation.
-     */
-    if ((scb->flags & SCB_RECOVERY_SCB) == 0)
+    if (aic7xxx_verbose)
+      printk(KERN_WARNING "(scsi%d:%d:%d:%d) Reset called with bogus Scsi_Cmnd"
+	   "->SCB mapping, improvising.\n", p->host_no, CTL_OF_CMD(cmd));
+    if ( flags & SCSI_RESET_SUGGEST_HOST_RESET )
     {
-      restore_flags(processor_flags);
-      return (SCSI_RESET_PENDING);
+      action = HOST_RESET;
+    }
+    else
+    {
+      action = BUS_RESET;
     }
   }
-  else
+  else if (scb->cmd != cmd) 
   {
-    if (!(flags & (SCSI_RESET_SUGGEST_HOST_RESET | SCSI_RESET_SUGGEST_BUS_RESET))
-        && (scb != NULL))
+    if (aic7xxx_verbose > 1)
+    printk(KERN_WARNING "(scsi%d:%d:%d:%d) Reset called with recycled SCB "
+	"for cmd.\n", p->host_no, CTL_OF_CMD(cmd));
+    cmd_prev = NULL;
+    cmd_next = p->completeq.head;
+    while ( cmd_next != NULL )
     {
-      /*
-       * Attempt a bus device reset if commands have completed successfully
-       * since the last bus device reset, or it has been less than 100ms
-       * since the last reset.
-       */
-      if ((p->flags & DEVICE_SUCCESS) ||
-          ((jiffies - p->device_status[tindex].last_reset) < HZ/10))
+      if (cmd_next == cmd)
       {
-	if (cmd->serial_number != cmd->serial_number_at_timeout)
-	{
-          result = SCSI_RESET_NOT_RUNNING;
-	}
-	else if (scb == NULL)
-        {
-          result = SCSI_RESET_NOT_RUNNING;
-        }
-        else if (flags & SCSI_RESET_ASYNCHRONOUS)
-        {
-          if (scb->flags & SCB_ABORTED)
-          {
-            result = SCSI_RESET_PENDING;
-          }
-          else if (!(scb->flags & SCB_ACTIVE))
-          {
-            result = SCSI_RESET_NOT_RUNNING;
-          }
-        }
-
-        if (result == -1)
-        {
-          if ((flags & SCSI_RESET_SYNCHRONOUS) &&
-              (p->device_status[tindex].flags & BUS_DEVICE_RESET_PENDING))
-          {
-            scb->flags |= SCB_ABORTED;
-            result = SCSI_RESET_PENDING;
-          }
-          else
-          {
-            p->flags |= IN_TIMEOUT;
-            result = aic7xxx_bus_device_reset(p, cmd);
-            if (result == 0)
-            {
-              p->flags &= ~IN_TIMEOUT;
-              result = SCSI_RESET_PENDING;
-            }
-          }
-	}
+	if (aic7xxx_verbose > 1)
+	  printk(KERN_WARNING "(scsi%d:%d:%d:%d) Reset, found cmd on completeq"
+	  ", completing.\n", p->host_no, CTL_OF_CMD(cmd));
+	if ( cmd_prev == NULL )
+	  p->completeq.head = (Scsi_Cmnd *)cmd_next->host_scribble;
+	else
+	  cmd_prev->host_scribble = cmd_next->host_scribble;
+	cmd_next->done(cmd_next);
+	unpause_sequencer(p, TRUE);
+	restore_flags(processor_flags);
+	return(SCSI_RESET_SUCCESS);
       }
+      cmd_prev = cmd_next;
+      cmd_next = (Scsi_Cmnd *)cmd_next->host_scribble;
+    }
+    if ( !(flags & SCSI_RESET_SYNCHRONOUS) )
+    {
+      if (aic7xxx_verbose)
+        printk(KERN_WARNING "(scsi%d:%d:%d:%d) Reset, cmd not found,"
+	  " failing.\n", p->host_no, CTL_OF_CMD(cmd));
+      unpause_sequencer(p, TRUE);
+      restore_flags(processor_flags);
+      return(SCSI_RESET_NOT_RUNNING);
+    }
+    else
+    {
+      if (aic7xxx_verbose)
+        printk(KERN_WARNING "(scsi%d:%d:%d:%d) Reset called, no scb, "
+          "flags 0x%x\n", p->host_no, CTL_OF_CMD(cmd), flags);
+      scb = NULL;
+      action = HOST_RESET;
     }
   }
-  if (result == -1)
+  else
   {
-    /*
-     * The bus device reset failed; try resetting the channel.
-     */
-    if (!(flags & (SCSI_RESET_SUGGEST_BUS_RESET | SCSI_RESET_SUGGEST_HOST_RESET))
-        && (flags & SCSI_RESET_ASYNCHRONOUS))
+    if (aic7xxx_verbose)
+      printk(KERN_WARNING "(scsi%d:%d:%d:%d) 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 )
     {
-      if (scb == NULL)
-      {
-	result = SCSI_RESET_NOT_RUNNING;
-      }
-      else if (!(scb->flags & SCB_ACTIVE))
-      {
-	result = SCSI_RESET_NOT_RUNNING;
-      }
-      else if ((scb->flags & SCB_ABORTED) &&
-               (!(p->device_status[tindex].flags & BUS_DEVICE_RESET_PENDING)))
-      {
-	result = SCSI_RESET_PENDING;
-      }
+      action = HOST_RESET;
     }
-
-    if (result == -1)
+    else if ( flags & SCSI_RESET_SUGGEST_BUS_RESET )
     {
-      /*
-       * The reset channel function assumes that the sequencer is paused.
-       */
-      pause_sequencer(p);
-      found = aic7xxx_reset_channel(p, channel, TRUE);
-      p->flags = p->flags & ~IN_TIMEOUT;
-
-      /*
-       * If this is a synchronous reset and there is no SCB for this
-       * command, perform completion processing.
-       *
-       */
-      if ((flags & SCSI_RESET_SYNCHRONOUS) && (scb == NULL))
+      action = BUS_RESET;
+    }
+    else 
+    {
+      action = DEVICE_RESET;
+    }
+  }
+  if ( ((jiffies - p->last_reset) < (HZ * AIC7XXX_RESET_DELAY)) &&
+    (action & (HOST_RESET | BUS_RESET | DEVICE_RESET)) )
+  {
+    if (aic7xxx_verbose > 1)
+      printk(KERN_WARNING "(scsi%d:%d:%d:%d) Reset called too soon after "
+	"last bus reset, delaying.\n", p->host_no, CTL_OF_CMD(cmd));
+    action = RESET_DELAY;
+  }
+  if ( ((jiffies - p->device_status[tindex].last_reset) < 
+	(HZ * AIC7XXX_RESET_DELAY)) && !(action & (HOST_RESET | BUS_RESET)))
+  {
+    if (aic7xxx_verbose > 1)
+      printk(KERN_WARNING "(scsi%d:%d:%d:%d) Reset called too soon after last "
+	"reset without requesting\n"
+	"(scsi%d:%d:%d:%d) bus or host reset, escalating.\n", p->host_no,
+	CTL_OF_CMD(cmd), p->host_no, CTL_OF_CMD(cmd));
+    action = BUS_RESET;
+  }
+  if ( (action & DEVICE_RESET) && 
+	(p->device_status[tindex].flags & BUS_DEVICE_RESET_PENDING) )
+  {
+    if (aic7xxx_verbose > 2)
+      printk(KERN_WARNING "(scsi%d:%d:%d:%d) 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 > 2)
+      printk(KERN_WARNING "(scsi%d:%d:%d:%d) Have already attempted to reach "
+	"device with queued\n(scsi%d:%d:%d:%d) message, will escalate to bus "
+	"reset.\n", p->host_no, CTL_OF_CMD(cmd), p->host_no, CTL_OF_CMD(cmd));
+    action = BUS_RESET;
+  }
+  if ( (action & DEVICE_RESET) && (p->flags & (RESET_PENDING | ABORT_PENDING)) )
+  {
+    if (aic7xxx_verbose > 2)
+     printk(KERN_WARNING "(scsi%d:%d:%d:%d) 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->bus_type != AIC_TWIN) )
+  {
+    action = HOST_RESET;
+  }
+  if ( (action & (BUS_RESET | HOST_RESET)) && (p->flags & RESET_PENDING)
+	&& ((jiffies - p->reset_start) > (2 * HZ * AIC7XXX_RESET_DELAY)) )
+  {
+    printk(KERN_ERR "(scsi%d:%d:%d:%d) Yikes!!  Card must have left to go "
+	"back to Adaptec!!\n", p->host_no, CTL_OF_CMD(cmd));
+    restore_flags(processor_flags);
+    return(SCSI_RESET_SNOOZE);
+  }
+/*
+ *  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)
+  {
+    case RESET_DELAY:
+      return(SCSI_RESET_PENDING);
+      break;
+    case FAIL:
+      return(SCSI_RESET_ERROR);
+      break;
+    case DEVICE_RESET:
+      p->flags |= RESET_PENDING;
+      result = aic7xxx_bus_device_reset(p, cmd);
+      aic7xxx_run_done_queue(p, TRUE);
+      aic7xxx_run_waiting_queues(p);
+      p->flags &= ~RESET_PENDING;
+      restore_flags(processor_flags);
+      return(result);
+      break;
+    case BUS_RESET:
+    case HOST_RESET:
+    default:
+      p->reset_start = jiffies;
+      p->flags |= RESET_PENDING;
+      aic7xxx_reset_channel(p, channel, TRUE);
+      if ( (p->bus_type == AIC_TWIN) && (action & HOST_RESET) )
       {
-	cmd->result = DID_RESET << 16;
-	cmd->scsi_done(cmd);
+        aic7xxx_reset_channel(p, (channel == 'A') ? 'B' : 'A', TRUE);
+        restart_sequencer(p);
+	pause_sequencer(p);
       }
-
-      switch (p->bus_type)
+      if (scb == NULL)
       {
-	case AIC_TWIN:
-	  if (channel == 'B')
-	  {
-            min_target = 8;
-            max_target = 15;
-	  }
-	  else
-	  {
-            min_target = 0;
-            max_target = 7;
-	  }
-	  break;
-
-	case AIC_WIDE:
-	  min_target = 0;
-	  max_target = 15;
-	  break;
-
-	case AIC_SINGLE:
-        default:
-	  min_target = 0;
-	  max_target = 7;
-	  break;
+	cmd->result = DID_RESET << 16;
+        cmd->done(cmd);
       }
-
-      for (tindex = min_target; tindex <= max_target; tindex++)
+      p->last_reset = jiffies;
+      if (action != HOST_RESET)
+        result = SCSI_RESET_SUCCESS | SCSI_RESET_BUS_RESET;
+      else
       {
-	p->device_status[tindex].last_reset = jiffies;
+        result = SCSI_RESET_SUCCESS | SCSI_RESET_HOST_RESET;
+        while (inb(p->base + QOUTCNT)) inb(p->base + QOUTFIFO);
+        if (p->flags & PAGE_ENABLED) outb(0, p->base + CMDOUTCNT);
+        aic7xxx_clear_intstat(p);
       }
-
-      result = SCSI_RESET_SUCCESS | SCSI_RESET_HOST_RESET;
-      p->flags &= ~IN_TIMEOUT;
-    }
+      p->flags &= ~RESET_PENDING;
+      aic7xxx_run_waiting_queues(p);
+      restore_flags(processor_flags);
+      return(result);
+      break;
   }
-  aic7xxx_run_waiting_queues(p);
-  restore_flags(processor_flags);
-  return (result);
 }
 
 /*+F*************************************************************************
diff -u --recursive --new-file v2.0.32/linux/drivers/scsi/aic7xxx.h linux/drivers/scsi/aic7xxx.h
--- v2.0.32/linux/drivers/scsi/aic7xxx.h	Tue Dec  2 13:52:32 1997
+++ linux/drivers/scsi/aic7xxx.h	Wed Dec 10 18:21:47 1997
@@ -23,7 +23,7 @@
 #ifndef _aic7xxx_h
 #define _aic7xxx_h
 
-#define AIC7XXX_H_VERSION  "$Revision: 3.2 $"
+#define AIC7XXX_H_VERSION  "$Revision: 3.2.1 $"
 
 /*
  * Scsi_Host_Template (see hosts.h) for AIC-7xxx - some fields
@@ -57,8 +57,8 @@
 extern int aic7xxx_biosparam(Disk *, kdev_t, int[]);
 extern int aic7xxx_detect(Scsi_Host_Template *);
 extern int aic7xxx_command(Scsi_Cmnd *);
-extern int aic7xxx_abort(Scsi_Cmnd *);
 extern int aic7xxx_reset(Scsi_Cmnd *, unsigned int);
+extern int aic7xxx_abort(Scsi_Cmnd *);
 
 extern const char *aic7xxx_info(struct Scsi_Host *);
 
diff -u --recursive --new-file v2.0.32/linux/drivers/scsi/aic7xxx_reg.h linux/drivers/scsi/aic7xxx_reg.h
--- v2.0.32/linux/drivers/scsi/aic7xxx_reg.h	Tue Dec  2 13:52:32 1997
+++ linux/drivers/scsi/aic7xxx_reg.h	Wed Dec 10 18:21:47 1997
@@ -30,13 +30,6 @@
 #define		ACTNEGEN        	0x02
 #define		STPWEN          	0x01
 
-#define	SCSISIGI        		0x03
-#define		ATNI            	0x10
-#define		SELI            	0x08
-#define		BSYI            	0x04
-#define		REQI            	0x02
-#define		ACKI            	0x01
-
 #define	SCSISIGO        		0x03
 #define		CDO             	0x80
 #define		IOO             	0x40
@@ -47,6 +40,13 @@
 #define		REQO            	0x02
 #define		ACKO            	0x01
 
+#define	SCSISIGI        		0x03
+#define		ATNI            	0x10
+#define		SELI            	0x08
+#define		BSYI            	0x04
+#define		REQI            	0x02
+#define		ACKI            	0x01
+
 #define	SCSIRATE        		0x04
 #define		WIDEXFER        	0x80
 #define		SXFR            	0x70
@@ -262,16 +262,16 @@
 
 #define	CMDOUTCNT       		0x5a
 
+#define	FIFODEPTH       		0x5b
+
 #define	SCSICONF2       		0x5b
 #define		RESET_SCSI      	0x40
 
-#define	FIFODEPTH       		0x5b
-
 #define	HOSTCONF        		0x5d
 
 #define	HA_274_BIOSCTRL 		0x5f
-#define		BIOSDISABLED    	0x30
 #define		BIOSMODE        	0x30
+#define		BIOSDISABLED    	0x30
 #define		CHANNEL_B_PRIMARY	0x08
 
 #define	SEQCTL          		0x60
@@ -315,16 +315,16 @@
 
 #define	STACK           		0x6f
 
+#define	BCTL            		0x84
+#define		ACE             	0x08
+#define		ENABLE          	0x01
+
 #define	DSCOMMAND       		0x84
 #define		CACHETHEN       	0x80
 #define		DPARCKEN        	0x40
 #define		MPARCKEN        	0x20
 #define		EXTREQLCK       	0x10
 
-#define	BCTL            		0x84
-#define		ACE             	0x08
-#define		ENABLE          	0x01
-
 #define	BUSTIME         		0x85
 #define		BOFF            	0xf0
 #define		BON             	0x0f
@@ -374,18 +374,18 @@
 #define		BAD_PHASE       	0x01
 #define		SEQINT          	0x01
 
-#define	ERROR           		0x92
-#define		PARERR          	0x08
-#define		ILLOPCODE       	0x04
-#define		ILLSADDR        	0x02
-#define		ILLHADDR        	0x01
-
 #define	CLRINT          		0x92
 #define		CLRBRKADRINT    	0x08
 #define		CLRSCSIINT      	0x04
 #define		CLRCMDINT       	0x02
 #define		CLRSEQINT       	0x01
 
+#define	ERROR           		0x92
+#define		PARERR          	0x08
+#define		ILLOPCODE       	0x04
+#define		ILLSADDR        	0x02
+#define		ILLHADDR        	0x01
+
 #define	DFCNTRL         		0x93
 
 #define	DFSTATUS        		0x94
@@ -410,8 +410,6 @@
 
 #define	QOUTCNT         		0x9e
 
-#define	SCB_BASE        		0xa0
-
 #define	SCB_CONTROL     		0xa0
 #define		MK_MESSAGE      	0x80
 #define		DISCENB         	0x40
@@ -421,6 +419,8 @@
 #define		DISCONNECTED    	0x04
 #define		SCB_TAG_TYPE    	0x03
 
+#define	SCB_BASE        		0xa0
+
 #define	SCB_TCL         		0xa1
 #define		TID             	0xf0
 #define		SELBUSB         	0x08
@@ -466,10 +466,10 @@
 #define		DI_2840         	0x01
 
 
-#define	MAX_OFFSET_16BIT	0x08
 #define	BUS_8_BIT	0x00
+#define	MAX_OFFSET_8BIT	0x0f
+#define	BUS_16_BIT	0x01
+#define	MAX_OFFSET_16BIT	0x08
 #define	SCB_LIST_NULL	0xff
 #define	SG_SIZEOF	0x08
-#define	MAX_OFFSET_8BIT	0x0f
 #define	BUS_32_BIT	0x02
-#define	BUS_16_BIT	0x01
diff -u --recursive --new-file v2.0.32/linux/drivers/scsi/aic7xxx_seq.h linux/drivers/scsi/aic7xxx_seq.h
--- v2.0.32/linux/drivers/scsi/aic7xxx_seq.h	Mon Sep 22 14:53:09 1997
+++ linux/drivers/scsi/aic7xxx_seq.h	Wed Dec 10 18:21:47 1997
@@ -34,7 +34,7 @@
 	0xff, 0x65, 0x1f, 0x18,
 	0x51, 0x6a, 0x91, 0x00,
 	0xff, 0x58, 0xb3, 0x02,
-	0x00, 0x65, 0xbb, 0x17,
+	0x00, 0x65, 0xb6, 0x17,
 	0x10, 0x6a, 0x60, 0x00,
 	0x00, 0x65, 0x03, 0x10,
 	0xff, 0x59, 0x90, 0x02,
@@ -260,7 +260,7 @@
 	0x10, 0x4c, 0x03, 0x00,
 	0x00, 0x65, 0xcf, 0x10,
 	0x04, 0xa0, 0xa0, 0x00,
-	0x00, 0x65, 0xbb, 0x17,
+	0x00, 0x65, 0xb6, 0x17,
 	0x00, 0x65, 0x69, 0x10,
 	0x10, 0x41, 0xcf, 0x1e,
 	0xff, 0x43, 0xa3, 0x02,
@@ -431,15 +431,10 @@
 	0xff, 0x53, 0xba, 0x02,
 	0xff, 0x6a, 0xb9, 0x00,
 	0xff, 0x90, 0x53, 0x03,
-	0xff, 0x53, 0xb9, 0x19,
+	0xff, 0x53, 0xb4, 0x19,
 	0xff, 0x52, 0xb0, 0x19,
 	0xff, 0x6a, 0x65, 0x01,
 	0xff, 0x52, 0x90, 0x02,
-	0x10, 0xa0, 0xb4, 0x1f,
-	0xef, 0xa0, 0xa0, 0x02,
-	0x00, 0x65, 0xb6, 0x11,
-	0xff, 0xa8, 0xb6, 0x1b,
-	0xff, 0xb3, 0xb8, 0x1d,
 	0x01, 0x6a, 0x3d, 0x00,
 	0x00, 0xb9, 0x77, 0x17,
 	0x00, 0x90, 0x61, 0x11,
@@ -478,6 +473,6 @@
 	{ 0x00000004, 0, 0x128, 0x12a },
 	{ 0x00000004, 0, 0x152, 0x16c },
 	{ 0x00000004, 1, 0x16c, 0x16d },
-	{ 0x00000004, 0, 0x1ad, 0x1c2 },
+	{ 0x00000004, 0, 0x1ad, 0x1bd },
 	{ 0x00000000, 0, 0x000, 0x000 }
 };
diff -u --recursive --new-file v2.0.32/linux/fs/buffer.c linux/fs/buffer.c
--- v2.0.32/linux/fs/buffer.c	Tue Dec  2 13:52:32 1997
+++ linux/fs/buffer.c	Wed Dec 10 18:09:44 1997
@@ -601,6 +601,8 @@
 	nr_unused_buffer_heads++;
 	bh->b_next_free = unused_list;
 	unused_list = bh;
+	if (!waitqueue_active(&buffer_wait))
+		return;
 	wake_up(&buffer_wait);
 }
 
@@ -983,6 +985,7 @@
 
 static void get_more_buffer_heads(void)
 {
+	struct wait_queue wait = { current, NULL };
 	struct buffer_head * bh;
 
 	while (!unused_list) {
@@ -1011,11 +1014,18 @@
 		 * finishing IO..
 		 */
 		run_task_queue(&tq_disk);
-		sleep_on(&buffer_wait);
+
 		/*
-		 * After we wake up, check for released async buffer heads.
+		 * Set our state for sleeping, then check again for buffer heads.
+		 * This ensures we won't miss a wake_up from an interrupt.
 		 */
+		add_wait_queue(&buffer_wait, &wait);
+		current->state = TASK_UNINTERRUPTIBLE;
+		if (!unused_list && !reuse_list)
+			schedule();
 		recover_reusable_buffer_heads();
+		remove_wait_queue(&buffer_wait, &wait);
+		current->state = TASK_RUNNING;
 	}
 
 }
@@ -1192,13 +1202,13 @@
 		 * and unlock_buffer(). */
 	} else {
 		unsigned long flags;
-		clear_bit(PG_locked, &page->flags);
-		set_bit(PG_uptodate, &page->flags);
-		wake_up(&page->wait);
 		save_flags(flags);
 		cli();
 		free_async_buffers(bh);
 		restore_flags(flags);
+		clear_bit(PG_locked, &page->flags);
+		set_bit(PG_uptodate, &page->flags);
+		wake_up(&page->wait);
 		after_unlock_page(page);
 		if (waitqueue_active(&buffer_wait))
 			wake_up(&buffer_wait);
diff -u --recursive --new-file v2.0.32/linux/fs/exec.c linux/fs/exec.c
--- v2.0.32/linux/fs/exec.c	Tue Dec  2 13:52:32 1997
+++ linux/fs/exec.c	Wed Dec 10 18:09:44 1997
@@ -397,7 +397,12 @@
 
 		old_mm = current->mm;
 		current->mm = mm;
-		new_page_tables(current);
+		if (new_page_tables(current)) {
+			current->mm = old_mm;
+			exit_mmap(mm);
+			kfree(mm);
+			return -ENOMEM;
+		}
 
 		if ((old_mm != &init_mm) && (!--old_mm->count)) {
 			/*
diff -u --recursive --new-file v2.0.32/linux/include/asm-i386/locks.h linux/include/asm-i386/locks.h
--- v2.0.32/linux/include/asm-i386/locks.h	Mon May  6 02:26:15 1996
+++ linux/include/asm-i386/locks.h	Wed Dec 10 18:09:44 1997
@@ -38,7 +38,7 @@
 			 *	Wait for any invalidates to go off
 			 */
 			 
-			if(smp_invalidate_needed&(1<<processor));
+			if(smp_invalidate_needed&(1<<processor))
 				while(lock_clear_bit(processor,&smp_invalidate_needed))
 					local_flush_tlb();
 			sp->spins++;
diff -u --recursive --new-file v2.0.32/linux/include/asm-i386/pgtable.h linux/include/asm-i386/pgtable.h
--- v2.0.32/linux/include/asm-i386/pgtable.h	Wed Oct 15 15:22:05 1997
+++ linux/include/asm-i386/pgtable.h	Tue Dec  2 14:18:13 1997
@@ -42,11 +42,28 @@
 #define __flush_tlb() \
 do { unsigned long tmpreg; __asm__ __volatile__("movl %%cr3,%0\n\tmovl %0,%%cr3":"=r" (tmpreg) : :"memory"); } while (0)
 
+/*
+ * NOTE! The intel "invlpg" semantics are extremely strange. The
+ * chip will add the segment base to the memory address, even though
+ * no segment checking is done. We correct for this by using an
+ * offset of 0x40000000 that will wrap around the kernel segment base
+ * of 0xC0000000 to get the correct address (it will always be outside
+ * the kernel segment, but we're only interested in the final linear
+ * address.
+ */
+#define __invlpg_mem(addr) \
+	(((char *)(addr))[0x40000000])
+#define __invlpg(addr) \
+	__asm__ __volatile__("invlpg %0": :"m" (__invlpg_mem(addr)))
+
+/*
+ * The i386 doesn't have a page-granular invalidate. Invalidate
+ * everything for it.
+ */
 #ifdef CONFIG_M386
-#define __flush_tlb_one(addr) flush_tlb()
+  #define __flush_tlb_one(addr) __flush_tlb()
 #else
-#define __flush_tlb_one(addr) \
-__asm__ __volatile__("invlpg %0": :"m" (*(char *) addr))
+  #define __flush_tlb_one(addr) __invlpg(addr)
 #endif
  
 #ifndef __SMP__
diff -u --recursive --new-file v2.0.32/linux/include/linux/ax25.h linux/include/linux/ax25.h
--- v2.0.32/linux/include/linux/ax25.h	Sat Aug 10 00:03:15 1996
+++ linux/include/linux/ax25.h	Thu Dec  4 13:13:23 1997
@@ -6,7 +6,6 @@
 #ifndef	AX25_KERNEL_H
 #define	AX25_KERNEL_H
  
-#define PF_AX25		AF_AX25
 #define AX25_MTU	256
 #define AX25_MAX_DIGIS	6	/* This is wrong, should be 8 */
 
diff -u --recursive --new-file v2.0.32/linux/include/linux/head.h linux/include/linux/head.h
--- v2.0.32/linux/include/linux/head.h	Tue Dec  2 13:52:33 1997
+++ linux/include/linux/head.h	Tue Dec  2 13:58:53 1997
@@ -5,8 +5,7 @@
 	unsigned long a,b;
 } desc_table[256];
 
-extern desc_table __idt,gdt;
-extern struct desc_struct *idt;
+extern desc_table idt,gdt;
 
 #define GDT_NUL 0
 #define GDT_CODE 1
diff -u --recursive --new-file v2.0.32/linux/include/linux/netrom.h linux/include/linux/netrom.h
--- v2.0.32/linux/include/linux/netrom.h	Sat Aug 10 00:03:15 1996
+++ linux/include/linux/netrom.h	Thu Dec  4 13:13:23 1997
@@ -7,7 +7,6 @@
 #ifndef	NETROM_KERNEL_H
 #define	NETROM_KERNEL_H
 
-#define PF_NETROM	AF_NETROM
 #define NETROM_MTU	236
 
 #define NETROM_T1	1
diff -u --recursive --new-file v2.0.32/linux/include/net/route.h linux/include/net/route.h
--- v2.0.32/linux/include/net/route.h	Wed Oct 15 15:23:10 1997
+++ linux/include/net/route.h	Wed Dec 10 09:14:00 1997
@@ -14,6 +14,11 @@
  *		Alan Cox	:	Support for TCP parameters.
  *		Alexey Kuznetsov:	Major changes for new routing code.
  *              Elliot Poger    :       Added support for SO_BINDTODEVICE.
+ *		Wolfgang Walter,
+ *		Daniel Ryde,
+ *		Ingo Molinar	:	fixed bug in ip_rt_put introduced
+ *					by SO_BINDTODEVICE support causing
+ *					a memory leak
  *
  *	FIXME:
  *		Make atomic ops more generic and hide them in asm/...
@@ -98,6 +103,7 @@
 extern atomic_t	    	ip_rt_lock;
 extern unsigned		ip_rt_bh_mask;
 extern struct rtable 	*ip_rt_hash_table[RT_HASH_DIVISOR];
+extern void	 	rt_free(struct rtable * rt);
 
 extern __inline__ void ip_rt_fast_lock(void)
 {
@@ -127,6 +133,11 @@
 {
 	if (rt)
 		atomic_dec(&rt->rt_refcnt);
+
+	/* If this rtable entry is not in the cache, we'd better free it once the
+	 * refcnt goes to zero, because nobody else will... */
+	if ( rt && (rt->rt_flags & RTF_NOTCACHED) && (!rt->rt_refcnt) )
+		rt_free(rt);
 }
 #else
 ;
diff -u --recursive --new-file v2.0.32/linux/kernel/exit.c linux/kernel/exit.c
--- v2.0.32/linux/kernel/exit.c	Fri Aug 29 10:38:46 1997
+++ linux/kernel/exit.c	Wed Dec 10 18:09:44 1997
@@ -28,6 +28,7 @@
 
 static inline void generate(unsigned long sig, struct task_struct * p)
 {
+	unsigned long flags;
 	unsigned long mask = 1 << (sig-1);
 	struct sigaction * sa = sig + p->sig->action - 1;
 
@@ -36,18 +37,24 @@
 	 * be handled immediately (ie non-blocked and untraced)
 	 * and that is ignored (either explicitly or by default)
 	 */
+	save_flags(flags); cli();
 	if (!(mask & p->blocked) && !(p->flags & PF_PTRACED)) {
 		/* don't bother with ignored signals (but SIGCHLD is special) */
-		if (sa->sa_handler == SIG_IGN && sig != SIGCHLD)
+		if (sa->sa_handler == SIG_IGN && sig != SIGCHLD) {
+			restore_flags(flags);
 			return;
+		}
 		/* some signals are ignored by default.. (but SIGCONT already did its deed) */
 		if ((sa->sa_handler == SIG_DFL) &&
-		    (sig == SIGCONT || sig == SIGCHLD || sig == SIGWINCH || sig == SIGURG))
+		    (sig == SIGCONT || sig == SIGCHLD || sig == SIGWINCH || sig == SIGURG)) {
+			restore_flags(flags);
 			return;
+		}
 	}
 	p->signal |= mask;
 	if (p->state == TASK_INTERRUPTIBLE && (p->signal & ~p->blocked))
 		wake_up_process(p);
+	restore_flags(flags);
 }
 
 /*
@@ -58,20 +65,26 @@
 {
 	sig--;
 	if (p->sig) {
+		unsigned long flags;
 		unsigned long mask = 1UL << sig;
 		struct sigaction *sa = p->sig->action + sig;
+
+		save_flags(flags); cli();
 		p->signal |= mask;
 		p->blocked &= ~mask;
 		if (sa->sa_handler == SIG_IGN)
 			sa->sa_handler = SIG_DFL;
 		if (p->state == TASK_INTERRUPTIBLE)
 			wake_up_process(p);
+		restore_flags(flags);
 	}
 }
 		
 
 int send_sig(unsigned long sig,struct task_struct * p,int priv)
 {
+	unsigned long flags;
+
 	if (!p || sig > 32)
 		return -EINVAL;
 	if (!priv && ((sig != SIGCONT) || (current->session != p->session)) &&
@@ -86,6 +99,7 @@
 	 */
 	if (!p->sig)
 		return 0;
+	save_flags(flags); cli();
 	if ((sig == SIGKILL) || (sig == SIGCONT)) {
 		if (p->state == TASK_STOPPED)
 			wake_up_process(p);
@@ -95,6 +109,8 @@
 	}
 	if (sig == SIGSTOP || sig == SIGTSTP || sig == SIGTTIN || sig == SIGTTOU)
 		p->signal &= ~(1<<(SIGCONT-1));
+	restore_flags(flags);
+
 	/* Actually generate the signal */
 	generate(sig,p);
 	return 0;
@@ -402,8 +418,13 @@
 		if (i >= NR_OPEN)
 			break;
 		while (set) {
-			if (set & 1)
-				close_fp(files->fd[i]);
+			if (set & 1) {
+				struct file * file = files->fd[i];
+				if (file) {
+					files->fd[i] = NULL;
+					close_fp(file);
+				}
+			}
 			i++;
 			set >>= 1;
 		}
diff -u --recursive --new-file v2.0.32/linux/kernel/fork.c linux/kernel/fork.c
--- v2.0.32/linux/kernel/fork.c	Tue Dec  2 13:52:33 1997
+++ linux/kernel/fork.c	Wed Dec 10 18:09:44 1997
@@ -86,7 +86,7 @@
 	for (mpnt = current->mm->mmap ; mpnt ; mpnt = mpnt->vm_next) {
 		tmp = (struct vm_area_struct *) kmalloc(sizeof(struct vm_area_struct), GFP_KERNEL);
 		if (!tmp) {
-			exit_mmap(mm);
+			/* exit_mmap is called by the caller */
 			return -ENOMEM;
 		}
 		*tmp = *mpnt;
@@ -101,12 +101,10 @@
 			tmp->vm_prev_share = mpnt;
 		}
 		if (copy_page_range(mm, current->mm, tmp)) {
-			if (mpnt->vm_next_share == tmp) {
-				tmp->vm_prev_share->vm_next_share = tmp->vm_next_share;
-				tmp->vm_next_share->vm_prev_share = tmp->vm_prev_share;	
-			}
-			kfree(tmp);
-			exit_mmap(mm);
+			/* link into the linked list for exit_mmap */
+			*p = tmp;
+			p = &tmp->vm_next;
+			/* exit_mmap is called by the caller */
 			return -ENOMEM;
 		}
 		if (tmp->vm_ops && tmp->vm_ops->open)
diff -u --recursive --new-file v2.0.32/linux/mm/filemap.c linux/mm/filemap.c
--- v2.0.32/linux/mm/filemap.c	Tue Dec  2 13:52:33 1997
+++ linux/mm/filemap.c	Wed Dec 10 18:09:44 1997
@@ -1194,9 +1194,7 @@
 static int msync_interval(struct vm_area_struct * vma,
 	unsigned long start, unsigned long end, int flags)
 {
-	if (!vma->vm_inode)
-		return 0;
-	if (vma->vm_ops->sync) {
+	if (vma->vm_inode && vma->vm_ops && vma->vm_ops->sync) {
 		int error;
 		error = vma->vm_ops->sync(vma, start, end-start, flags);
 		if (error)
diff -u --recursive --new-file v2.0.32/linux/mm/page_alloc.c linux/mm/page_alloc.c
--- v2.0.32/linux/mm/page_alloc.c	Tue Dec  2 13:52:33 1997
+++ linux/mm/page_alloc.c	Wed Dec 10 18:09:44 1997
@@ -320,6 +320,7 @@
 		return;
 	}
 	if (!page) {
+		printk("swap_in:");
 		set_pte(page_table, BAD_PAGE);
 		swap_free(entry);
 		oom(tsk);
diff -u --recursive --new-file v2.0.32/linux/mm/vmalloc.c linux/mm/vmalloc.c
--- v2.0.32/linux/mm/vmalloc.c	Mon Aug  5 00:13:55 1996
+++ linux/mm/vmalloc.c	Wed Dec 10 18:09:44 1997
@@ -143,8 +143,10 @@
 		pte_t * pte = pte_alloc_kernel(pmd, address);
 		if (!pte)
 			return -ENOMEM;
-		if (alloc_area_pte(pte, address, end - address))
+		if (alloc_area_pte(pte, address, end - address)) {
+			pte_free_kernel(pte);
 			return -ENOMEM;
+		}
 		address = (address + PMD_SIZE) & PMD_MASK;
 		pmd++;
 	}
@@ -162,8 +164,10 @@
 		pmd_t *pmd = pmd_alloc_kernel(dir, address);
 		if (!pmd)
 			return -ENOMEM;
-		if (alloc_area_pmd(pmd, address, end - address))
+		if (alloc_area_pmd(pmd, address, end - address)) {
+			pmd_free_kernel(pmd);
 			return -ENOMEM;
+		}
 		set_pgdir(address, *dir);
 		address = (address + PGDIR_SIZE) & PGDIR_MASK;
 		dir++;
@@ -224,8 +228,10 @@
 		pmd_t *pmd = pmd_alloc_kernel(dir, address);
 		if (!pmd)
 			return -ENOMEM;
-		if (remap_area_pmd(pmd, address, end - address, offset + address))
+		if (remap_area_pmd(pmd, address, end - address, offset + address)) {
+			pmd_free_kernel(pmd);
 			return -ENOMEM;
+		}
 		set_pgdir(address, *dir);
 		address = (address + PGDIR_SIZE) & PGDIR_MASK;
 		dir++;
diff -u --recursive --new-file v2.0.32/linux/mm/vmscan.c linux/mm/vmscan.c
--- v2.0.32/linux/mm/vmscan.c	Tue Dec  2 13:52:33 1997
+++ linux/mm/vmscan.c	Wed Dec 10 18:09:44 1997
@@ -328,6 +328,11 @@
 			shfrv =  8;
 			break;
 	}
+	/*
+	 * kswapd should be more friendly to other processes.
+	 */
+	if (kswapd_awake)
+		shfrv = 10;
 #endif
 
 	counter = ((PAGEOUT_WEIGHT * nr_tasks) >> shfrv) >> priority;
@@ -453,7 +458,7 @@
  */
 int kswapd(void *unused)
 {
-	int i, j;
+	int i, reserved_pages;
 	
 	current->session = 1;
 	current->pgrp = 1;
@@ -491,12 +496,15 @@
 		swapstats.wakeups++;
 		/* Protect our reserved pages: */
 		i = 0;
-		j = (min_free_pages >= 48 ? min_free_pages-12 : min_free_pages);
-		if (nr_free_pages <= j)
-			i = (1+j) - nr_free_pages;
+		reserved_pages = min_free_pages;
+		if (min_free_pages >= 48)
+			reserved_pages -= (12 + (reserved_pages>>3));
+		if (nr_free_pages <= reserved_pages)
+			i = (1+reserved_pages) - nr_free_pages;
 		/* Do the background pageout: */
 		for (i += kswapd_ctl.maxpages; i > 0; i--)
-			try_to_free_page(GFP_KERNEL, 0, (nr_free_pages <= j));
+			try_to_free_page(GFP_KERNEL, 0,
+					 (nr_free_pages <= min_free_pages));
 	}
 }
 
diff -u --recursive --new-file v2.0.32/linux/net/ipv4/ip_forward.c linux/net/ipv4/ip_forward.c
--- v2.0.32/linux/net/ipv4/ip_forward.c	Tue Aug 12 11:30:25 1997
+++ linux/net/ipv4/ip_forward.c	Wed Dec 10 18:13:49 1997
@@ -16,6 +16,7 @@
  *					use output device for accounting.
  *		Jos Vos		:	Call forward firewall after routing
  *					(always use output device).
+ *		Philip Gladstone:	Add some missing ip_rt_put()
  */
 
 #include <linux/config.h>
@@ -113,7 +114,7 @@
 	struct device *dev2;	/* Output device */
 	struct iphdr *iph;	/* Our header */
 	struct sk_buff *skb2;	/* Output packet */
-	struct rtable *rt;	/* Route we use */
+	struct rtable *rt = NULL;	/* Route we use */
 	unsigned char *ptr;	/* Data pointer */
 	unsigned long raddr;	/* Router IP address */
 	struct   options * opt	= (struct options*)skb->proto_priv;
@@ -301,6 +302,8 @@
 			icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0, dev);
 			/* fall thru */
 		default:
+			if (rt)
+				ip_rt_put(rt);
 			return -1;
 		}
 
@@ -318,6 +321,17 @@
 	if (dev2->flags & IFF_UP)
 	{
 #ifdef CONFIG_IP_MASQUERADE
+		__u32	premasq_saddr = iph->saddr;
+		__u16	premasq_sport = 0;
+		__u16	*portptr;
+		long	premasq_len_diff = skb->len;
+
+		if (iph->protocol==IPPROTO_UDP ||
+                    iph->protocol==IPPROTO_TCP) {
+			portptr = (__u16 *)&(((char *)iph)[iph->ihl*4]);
+			premasq_sport = portptr[0];
+		}
+
 		/*
 		 * If this fragment needs masquerading, make it so...
 		 * (Don't masquerade de-masqueraded fragments)
@@ -338,6 +352,28 @@
 		if (skb->len+encap > dev2->mtu && (iph->frag_off & htons(IP_DF))) 
 		{
 			ip_statistics.IpFragFails++;
+#ifdef CONFIG_IP_MASQUERADE
+			/* If we're demasquerading, put the correct daddr back */
+			if (is_frag&IPFWD_MASQUERADED)
+				iph->daddr = dev->pa_addr;
+
+			/* If we're masquerading, put the correct source back */
+			else if (fw_res==FW_MASQUERADE) {
+				iph->saddr = premasq_saddr;
+				if (premasq_sport)
+					portptr[0] = premasq_sport;
+			}
+ 
+			/* If the packet has got larger and this has caused it to
+			   exceed the MTU, then we'll claim that our MTU just got
+			   smaller and hope it works */
+			premasq_len_diff -= skb->len;
+
+			if (premasq_len_diff < 0)
+				icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,
+					  htonl(dev2->mtu+premasq_len_diff), dev);
+			else
+#endif
 			icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, htonl(dev2->mtu), dev);
 			if(rt)
 				ip_rt_put(rt);
@@ -454,6 +490,8 @@
 				icmp_send(skb2, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0, dev);
 			if (skb != skb2)
 				kfree_skb(skb2,FREE_WRITE);
+			if (rt)
+				ip_rt_put(rt);
 			return -1;
 		}
 #endif
diff -u --recursive --new-file v2.0.32/linux/net/ipv4/route.c linux/net/ipv4/route.c
--- v2.0.32/linux/net/ipv4/route.c	Wed Sep 17 12:00:48 1997
+++ linux/net/ipv4/route.c	Wed Dec 10 09:14:01 1997
@@ -45,6 +45,7 @@
  *              Elliot Poger    :       Added support for SO_BINDTODEVICE.
  *		Andi Kleen	:	Don't send multicast addresses to
  *					kerneld.	
+ *		Wolfgang Walter	:	make rt_free() non-static
  *
  *	Juan Jose Ciarlante     :	Added ip_rt_dev 
  *		This program is free software; you can redistribute it and/or
@@ -891,7 +892,7 @@
 }
 
 
-static void rt_free(struct rtable * rt)
+void rt_free(struct rtable * rt)
 {
 	unsigned long flags;
 
diff -u --recursive --new-file v2.0.32/linux/net/ipv4/udp.c linux/net/ipv4/udp.c
--- v2.0.32/linux/net/ipv4/udp.c	Mon Sep 15 09:54:52 1997
+++ linux/net/ipv4/udp.c	Wed Dec 10 09:14:01 1997
@@ -55,6 +55,7 @@
  *              Elliot Poger    :       Added support for SO_BINDTODEVICE.
  *	Willy Konynenberg	:	Transparent proxy adapted to new
  *					socket hash code.
+ *		Philip Gladstone:	Added missing ip_rt_put
  *
  *
  *		This program is free software; you can redistribute it and/or
@@ -874,6 +875,8 @@
 	sk->daddr = usin->sin_addr.s_addr;
 	sk->dummy_th.dest = usin->sin_port;
 	sk->state = TCP_ESTABLISHED;
+	if (sk->ip_route_cache)
+	        ip_rt_put(sk->ip_route_cache);
 	sk->ip_route_cache = rt;
 	return(0);
 }
diff -u --recursive --new-file v2.0.32/linux/net/netsyms.c linux/net/netsyms.c
--- v2.0.32/linux/net/netsyms.c	Wed Sep 17 12:00:48 1997
+++ linux/net/netsyms.c	Wed Dec 10 18:14:27 1997
@@ -67,6 +67,9 @@
 	X(sock_register),
 	X(sock_unregister),
 
+	X(sock_alloc),
+	X(sock_release),
+
 	/* Socket layer support routines */
 	X(memcpy_fromiovec),
 	X(sock_setsockopt),
diff -u --recursive --new-file v2.0.32/linux/net/unix/garbage.c linux/net/unix/garbage.c
--- v2.0.32/linux/net/unix/garbage.c	Mon May 13 05:31:18 1996
+++ linux/net/unix/garbage.c	Thu Dec  4 10:16:31 1997
@@ -5,6 +5,16 @@
  *	Copyright (C) Barak A. Pearlmutter.
  *	Released under the GPL version 2 or later.
  *
+ * 12/3/97 -- Flood
+ * Internal stack is only allocated one page.  On systems with NR_FILE
+ * > 1024, this makes it quite easy for a user-space program to open
+ * a large number of AF_UNIX domain sockets, causing the garbage
+ * collection routines to run up against the wall (and panic).
+ * Changed the MAX_STACK to be associated to the system-wide open file
+ * maximum, and use vmalloc() instead of get_free_page() [as more than
+ * one page may be necessary].  As noted below, this should ideally be
+ * done with a linked list.  
+ *
  * Chopped about by Alan Cox 22/3/96 to make it fit the AF_UNIX socket problem.
  * If it doesn't work blame me, it worked when Barak sent it.
  *
@@ -59,10 +69,9 @@
 
 /* Internal data structures and random procedures: */
 
-#define MAX_STACK 1000		/* Maximum depth of tree (about 1 page) */
 static unix_socket **stack;	/* stack of objects to mark */
 static int in_stack = 0;	/* first free entry in stack */
-
+static int max_stack;		/* Calculated in unix_gc() */
 
 extern inline unix_socket *unix_get_socket(struct file *filp)
 {
@@ -110,7 +119,7 @@
  
 extern inline void push_stack(unix_socket *x)
 {
-	if (in_stack == MAX_STACK)
+	if (in_stack == max_stack)
 		panic("can't push onto full stack");
 	stack[in_stack++] = x;
 }
@@ -151,8 +160,14 @@
 	if(in_unix_gc)
 		return;
 	in_unix_gc=1;
-	
-	stack=(unix_socket **)get_free_page(GFP_KERNEL);
+
+	max_stack = max_files;
+
+	stack=(unix_socket **)vmalloc(max_stack * sizeof(unix_socket **));
+	if (!stack) {
+		in_unix_gc=0;
+		return;
+	}
 	
 	/*
 	 *	Assume everything is now unmarked 
@@ -276,5 +291,5 @@
 	
 	in_unix_gc=0;
 	
-	free_page((long)stack);
+	vfree(stack);
 }
