]> git.neil.brown.name Git - history.git/commitdiff
Import 2.3.6 2.3.6
authorLinus Torvalds <torvalds@linuxfoundation.org>
Fri, 23 Nov 2007 20:25:23 +0000 (15:25 -0500)
committerLinus Torvalds <torvalds@linuxfoundation.org>
Fri, 23 Nov 2007 20:25:23 +0000 (15:25 -0500)
128 files changed:
Documentation/Configure.help
arch/sparc/Makefile
arch/sparc/kernel/ebus.c
arch/sparc/kernel/entry.S
arch/sparc/kernel/head.S
arch/sparc/kernel/pcic.c
arch/sparc/kernel/setup.c
arch/sparc/kernel/sys_sunos.c
arch/sparc64/Makefile
arch/sparc64/defconfig
arch/sparc64/kernel/binfmt_aout32.c
arch/sparc64/kernel/ioctl32.c
arch/sparc64/kernel/signal.c
arch/sparc64/kernel/sys_sparc.c
arch/sparc64/kernel/sys_sparc32.c
arch/sparc64/kernel/sys_sunos32.c
arch/sparc64/kernel/systbls.S
arch/sparc64/kernel/traps.c
arch/sparc64/math-emu/sfp-util.h
drivers/block/floppy.c
drivers/char/misc.c
drivers/char/rtc.c
drivers/misc/parport_ax.c
drivers/net/myri_sbus.c
drivers/net/ptifddi.c
drivers/pci/oldproc.c
drivers/sbus/char/Config.in
drivers/sbus/char/aurora.c
drivers/sbus/char/bpp.c
drivers/sbus/char/pcikbd.c
drivers/sbus/char/rtc.c
drivers/sbus/char/su.c
drivers/sbus/char/vfc_dev.c
drivers/scsi/Config.in
drivers/scsi/README.aic7xxx
drivers/scsi/aic7xxx.c
drivers/scsi/aic7xxx/aic7xxx.reg
drivers/scsi/aic7xxx/aic7xxx.seq
drivers/scsi/aic7xxx/scsi_message.h
drivers/scsi/aic7xxx_proc.c
drivers/scsi/aic7xxx_reg.h
drivers/scsi/aic7xxx_seq.c
drivers/scsi/scsi.h
drivers/scsi/scsi_proc.c
drivers/scsi/scsi_syms.c
drivers/scsi/scsicam.c
drivers/video/atyfb.c
drivers/video/igafb.c
fs/Config.in
fs/binfmt_aout.c
fs/ext2/symlink.c
include/asm-mips/namei.h
include/asm-sparc/io.h
include/asm-sparc/namei.h
include/asm-sparc/pcic.h
include/asm-sparc64/namei.h
include/linux/igmp.h
include/linux/inet.h
include/linux/inetdevice.h
include/linux/netdevice.h
include/linux/pci.h
include/linux/pkt_sched.h
include/linux/rtnetlink.h
include/net/addrconf.h
include/net/dst.h
include/net/if_inet6.h
include/net/ip.h
include/net/neighbour.h
include/net/pkt_cls.h
include/net/pkt_sched.h
include/net/route.h
include/scsi/scsicam.h
net/core/dev.c
net/core/dev_mcast.c
net/core/dst.c
net/core/neighbour.c
net/core/rtnetlink.c
net/decnet/dn_neigh.c
net/ethernet/eth.c
net/ipv4/af_inet.c
net/ipv4/arp.c
net/ipv4/devinet.c
net/ipv4/fib_frontend.c
net/ipv4/fib_hash.c
net/ipv4/fib_rules.c
net/ipv4/icmp.c
net/ipv4/igmp.c
net/ipv4/ip_input.c
net/ipv4/ip_masq_vdolive.c
net/ipv4/ip_options.c
net/ipv4/ipconfig.c
net/ipv4/ipmr.c
net/ipv4/route.c
net/ipv4/tcp_input.c
net/ipv4/tcp_ipv4.c
net/ipv4/udp.c
net/ipv4/utils.c
net/ipv6/addrconf.c
net/ipv6/af_inet6.c
net/ipv6/ip6_fw.c
net/ipv6/ip6_output.c
net/ipv6/mcast.c
net/ipv6/ndisc.c
net/ipv6/raw.c
net/ipv6/reassembly.c
net/ipv6/route.c
net/ipv6/tcp_ipv6.c
net/ipv6/udp.c
net/netrom/nr_route.c
net/netsyms.c
net/packet/af_packet.c
net/rose/rose_route.c
net/sched/cls_api.c
net/sched/cls_fw.c
net/sched/cls_route.c
net/sched/cls_rsvp.h
net/sched/cls_u32.c
net/sched/estimator.c
net/sched/police.c
net/sched/sch_api.c
net/sched/sch_cbq.c
net/sched/sch_csz.c
net/sched/sch_generic.c
net/sched/sch_prio.c
net/sched/sch_sfq.c
net/sched/sch_tbf.c
net/sched/sch_teql.c
net/x25/af_x25.c

index 277640070341794fc15b432632be7b77b4d95075..6bb6e2e11b7982fcd55f6a6a2cdd6b4fb822a701 100644 (file)
@@ -3958,34 +3958,49 @@ CONFIG_SCSI_AIC7XXX
   say M here and read Documentation/modules.txt. The module will be
   called aic7xxx.o.
 
-Override driver defaults for commands per LUN
-CONFIG_OVERRIDE_CMDS
-  Say Y here if you want 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 affects tagged queueing
-  capable devices. The driver uses a value of 24 by default.
-  If you say Y here, you can adjust the number of commands per LUN
-  with the following configuration option.
+Enable or Disable Tagged Command Queueing by default
+CONFIG_AIC7XXX_TCQ_ON_BY_DEFAULT
+  This option causes the aic7xxx driver to attempt to use tagged command
+  queueing on any devices that claim to support it.  If this is set to yes,
+  you can still turn off TCQ on troublesome devices with the use of the
+  tag_info boot parameter.  See /usr/src/linux/drivers/scsi/README.aic7xxx
+  for more information on that and other aic7xxx setup commands.  If this
+  option is turned off, you may still enable TCQ on known good devices by
+  use of the tag_info boot parameter.
   
-  If unsure, say N.
-
-Maximum number of commands per LUN
-CONFIG_AIC7XXX_CMDS_PER_LUN
-  Specify the maximum number of commands you would like to allocate
-  per LUN (a LUN is a Logical Unit Number -- some physical SCSI
-  devices, e.g. CD jukeboxes, act logically as several separate units,
-  each of which gets its own number).
-
-  Reasonable figures are in the range of 14 to 32 commands per device,
+  If you are unsure about your devices then it is safest to say N here.
+  
+  However, TCQ can increase performance on some hard drives by as much
+  as 50% or more, so I would recommend that if you say N here, that you
+  at least read the README.aic7xxx file so you will know how to enable
+  this option manually should your drives prove to be safe in regards
+  to TCQ.
+
+  Conversely, certain drives are known to lock up or cause bus resets when
+  TCQ is enabled on them.  If you have a Western Digital Enterprise SCSI
+  drive for instance, then don't even bother to enable TCQ on it as the
+  drive will become unreliable, and it will actually reduce performance.
+
+Default number of TCQ commands per device
+CONFIG_AIC7XXX_CMDS_PER_DEVICE
+  Specify the number of commands you would like to allocate per SCSI
+  device when Tagged Command Queueing (TCQ) is enabled on that device.
+
+  Reasonable figures are in the range of 8 to 24 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 some 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
+  of uptime. It will not hinder performance if some of your devices
+  eventually have their command depth reduced, but is a waste of memory
+  if all of your devices end up reducing this number down to a more
+  reasonable figure.
+  
+  NOTE: Certain very broken drives are known to lock up when given more
+  commands than they like to deal with.  Quantum Fireball drives are the
+  most common in this category.  For the Quantum Fireball drives I would
+  suggest no more than 8 commands per device.
+
+  Default: 8
 
 Collect statistics to report in /proc
 CONFIG_AIC7XXX_PROC_STATS
@@ -8950,8 +8965,8 @@ CONFIG_FT_ALPHA_CLOCK
 
 Zilog serial support
 CONFIG_SUN_ZS
-  This driver does not exist at this point, so you might as well 
-  say N.
+  If you are asked this question, something is wrong with config scripts.
+  Zilog serial driver is always enabled in sparc architecture.
 
 Double Talk PC internal speech card support
 CONFIG_DTLK
@@ -10352,8 +10367,14 @@ CONFIG_SUN_OPENPROMIO
   inserted in and removed from the running kernel whenever you want),
   say M and read Documentation/modules.txt. If unsure, say Y.
 
-#Mostek real time clock support
-#CONFIG_SUN_MOSTEK_RTC
+Mostek real time clock support
+CONFIG_SUN_MOSTEK_RTC
+  The Mostek RTC chip is used on all knows Sun computers except
+  some JavaStation-s. For a JavaStation you need to say Y both here
+  and to CONFIG_RTC.
+
+  Say Y here unless you are building a special purpose kernel.
+
 #
 #Siemens SAB82532 serial support
 #CONFIG_SAB82532
index 45bec8353f9a545a2a5eb67067d03ddb2ab06615..4905b111d681ed588005ec7205cfe1faa3800315 100644 (file)
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.39 1998/09/16 12:31:31 jj Exp $
+# $Id: Makefile,v 1.41 1999/06/04 13:29:05 jj Exp $
 # sparc/Makefile
 #
 # Makefile for the architecture dependent flags and dependencies on the
@@ -15,7 +15,7 @@ SHELL  =/bin/bash
 # Uncomment the first CFLAGS if you are doing kgdb source level
 # debugging of the kernel to get the proper debugging information.
 
-IS_EGCS := $(shell if $(CC) --version 2>&1 | grep 'egcs' > /dev/null; then echo y; else echo n; fi)
+IS_EGCS := $(shell if $(CC) -c -m32 -o _tmp.o arch/sparc/math-emu/fnegs.c >/dev/null 2>&1; then echo y; else echo n; fi; rm -f _tmp.o)
 NEW_GAS := $(shell if $(LD) --version 2>&1 | grep 'elf64_sparc' > /dev/null; then echo y; else echo n; fi)
 
 ifeq ($(NEW_GAS),y)
index 7c3eda88ea28d312a3326483357d17ca645a0548..ae84dde6bdc06b381134f0d3b0471693cbe443b0 100644 (file)
@@ -1,9 +1,10 @@
-/* $Id: ebus.c,v 1.2 1998/10/07 11:35:16 jj Exp $
+/* $Id: ebus.c,v 1.3 1999/06/03 15:02:09 davem Exp $
  * ebus.c: PCI to EBus bridge device.
  *
  * Copyright (C) 1997  Eddie C. Dost  (ecd@skynet.be)
  *
  * Adopted for sparc by V. Roganov and G. Raiko.
+ * Fixes for different platforms by Pete Zaitcev.
  */
 
 #include <linux/config.h>
@@ -25,9 +26,9 @@
 #undef DEBUG_FILL_EBUS_DEV
 
 #ifdef PROM_DEBUG
-#define dprintf prom_printf
+#define dprintk prom_printf
 #else
-#define dprintf printk
+#define dprintk printk
 #endif
 
 struct linux_ebus *ebus_chain = 0;
@@ -48,6 +49,9 @@ extern int flash_init(void);
 extern int envctrl_init(void);
 #endif
 
+/* We are together with pcic.c under CONFIG_PCI. */
+extern unsigned int pcic_pin_to_irq(unsigned int, char *name);
+
 static inline unsigned long ebus_alloc(size_t size)
 {
        return (unsigned long)kmalloc(size, GFP_ATOMIC);
@@ -66,6 +70,7 @@ __initfunc(void fill_ebus_child(int node, struct linux_prom_registers *preg,
        strcpy(dev->prom_name, lbuf);
 
        len = prom_getproperty(node, "reg", (void *)regs, sizeof(regs));
+       if (len == -1) len = 0;
        dev->num_addrs = len / sizeof(regs[0]);
 
        for (i = 0; i < dev->num_addrs; i++) {
@@ -77,22 +82,36 @@ __initfunc(void fill_ebus_child(int node, struct linux_prom_registers *preg,
                dev->base_address[i] = dev->parent->base_address[regs[i]];
        }
 
+       /*
+        * Houston, we have a problem...
+        * Sometimes PROM supplies absolutely meaningless properties.
+        * Still, we take what it gives since we have nothing better.
+        * Children of ebus may be wired on any input pin of PCIC.
+        */
        len = prom_getproperty(node, "interrupts", (char *)&irqs, sizeof(irqs));
        if ((len == -1) || (len == 0)) {
                dev->num_irqs = 0;
-               /*
-                * Oh, well, some PROMs don't export interrupts
-                * property to children of EBus devices...
-                *
-                * Be smart about PS/2 keyboard and mouse.
-                */
-               if (!strcmp(dev->parent->prom_name, "8042")) {
+               dev->irqs[0] = 0;
+               if (dev->parent->num_irqs != 0) {
                        dev->num_irqs = 1;
                        dev->irqs[0] = dev->parent->irqs[0];
+/* P3 remove */ printk("EBUS: dev %s irq %d from parent\n", dev->prom_name, dev->irqs[0]);
                }
        } else {
                dev->num_irqs = len / sizeof(irqs[0]);
-               printk("FIXME: %s irq(%d)\n", dev->prom_name, irqs[0]);
+               if (irqs[0] == 0 || irqs[0] >= 8) {
+                       /*
+                        * XXX Zero is a valid pin number...
+                        * This works as long as Ebus is not wired to INTA#.
+                        */
+                       printk("EBUS: %s got bad irq %d from PROM\n",
+                           dev->prom_name, irqs[0]);
+                       dev->num_irqs = 0;
+                       dev->irqs[0] = 0;
+               } else {
+                       dev->irqs[0] = pcic_pin_to_irq(irqs[0], dev->prom_name);
+/* P3 remove */ printk("EBUS: dev %s irq %d from PROM\n", dev->prom_name, dev->irqs[0]);
+               }
        }
 
 #ifdef DEBUG_FILL_EBUS_DEV
@@ -131,7 +150,30 @@ __initfunc(void fill_ebus_device(int node, struct linux_ebus_device *dev))
        dev->num_addrs = len / sizeof(struct linux_prom_registers);
 
        for (i = 0; i < dev->num_addrs; i++) {
-               n = (regs[i].which_io - 0x10) >> 2;
+               /*
+                * XXX Collect JE-1 PROM
+                * 
+                * Example - JS-E with 3.11:
+                *  /ebus
+                *      regs 
+                *        0x00000000, 0x0, 0x00000000, 0x0, 0x00000000,
+                *        0x82000010, 0x0, 0xf0000000, 0x0, 0x01000000,
+                *        0x82000014, 0x0, 0x38800000, 0x0, 0x00800000,
+                *      ranges
+                *        0x00, 0x00000000, 0x02000010, 0x0, 0x0, 0x01000000,
+                *        0x01, 0x01000000, 0x02000014, 0x0, 0x0, 0x00800000,
+                *  /ebus/8042
+                *      regs
+                *        0x00000001, 0x00300060, 0x00000008,
+                *        0x00000001, 0x00300060, 0x00000008,
+                */
+               n = regs[i].which_io;
+               if (n >= 4) {
+                       /* XXX This is copied from old JE-1 by Gleb. */
+                       n = (regs[i].which_io - 0x10) >> 2;
+               } else {
+                       ;
+               }
 
                dev->base_address[i] = dev->bus->self->base_address[n];
                dev->base_address[i] += regs[i].phys_addr;
@@ -141,8 +183,14 @@ __initfunc(void fill_ebus_device(int node, struct linux_ebus_device *dev))
                       (unsigned long)sparc_alloc_io (dev->base_address[i], 0,
                                                      regs[i].reg_size,
                                                      dev->prom_name, 0, 0);
+#if 0
+/*
+ * This release_region() screwes those who do sparc_alloc_io().
+ * Change drivers which do check_region(). See drivers/block/floppy.c.
+ */
                    /* Some drivers call 'check_region', so we release it */
                     release_region(dev->base_address[i] & PAGE_MASK, PAGE_SIZE);
+#endif
 
                    if (dev->base_address[i] == 0 ) {
                        panic("ebus: unable sparc_alloc_io for dev %s",
@@ -154,12 +202,22 @@ __initfunc(void fill_ebus_device(int node, struct linux_ebus_device *dev))
        len = prom_getproperty(node, "interrupts", (char *)&irqs, sizeof(irqs));
        if ((len == -1) || (len == 0)) {
                dev->num_irqs = 0;
+               if ((dev->irqs[0] = dev->bus->self->irq) != 0) {
+                        dev->num_irqs = 1;
+/* P3 remove */ printk("EBUS: child %s irq %d from parent\n", dev->prom_name, dev->irqs[0]);
+               }
        } else {
-               dev->num_irqs = len / sizeof(irqs[0]);
-
-#define IRQ_8042 7
-               if (irqs[0] == 4) dev->irqs[0] = IRQ_8042;
-               printk("FIXME: %s irq(%d)\n", dev->prom_name, irqs[0]);
+               dev->num_irqs = 1;  /* dev->num_irqs = len / sizeof(irqs[0]); */
+               if (irqs[0] == 0 || irqs[0] >= 8) {
+                       /* See above for the parent. XXX */
+                       printk("EBUS: %s got bad irq %d from PROM\n",
+                           dev->prom_name, irqs[0]);
+                       dev->num_irqs = 0;
+                       dev->irqs[0] = 0;
+               } else {
+                       dev->irqs[0] = pcic_pin_to_irq(irqs[0], dev->prom_name);
+/* P3 remove */ printk("EBUS: child %s irq %d from PROM\n", dev->prom_name, dev->irqs[0]);
+               }
        }
 
 #ifdef DEBUG_FILL_EBUS_DEV
index 8eeac72b0cd2486f749933ee94899c107b3d22ab..ff1ac2497775cf1096b737efd1ccb44b0fbe838a 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: entry.S,v 1.159 1999/05/08 03:00:03 davem Exp $
+/* $Id: entry.S,v 1.160 1999/06/03 15:02:11 davem Exp $
  * arch/sparc/kernel/entry.S:  Sparc trap low-level entry points.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -1889,4 +1889,52 @@ C_LABEL(restore_current):
        retl
         nop
 
+#ifdef CONFIG_PCI
+#include <asm/pcic.h>
+
+       .align  4
+       .globl  linux_trap_ipi15_pcic
+linux_trap_ipi15_pcic:
+       rd      %wim, %l3
+       SAVE_ALL
+
+       /*
+        * First deactivate NMI
+        * or we cannot drop ET, cannot get window spill traps.
+        * The busy loop is necessary because the PIO error
+        * sometimes does not go away quickly and we trap again.
+        */
+       sethi   %hi(C_LABEL(pcic_regs)), %o1
+       ld      [%o1 + %lo(C_LABEL(pcic_regs))], %o2
+
+       ! Get pending status for printouts later.
+       ld      [%o2 + PCI_SYS_INT_PENDING], %o0
+
+       mov     PCI_SYS_INT_PENDING_CLEAR_ALL, %o1
+       stb     %o1, [%o2 + PCI_SYS_INT_PENDING_CLEAR]
+1:
+       ld      [%o2 + PCI_SYS_INT_PENDING], %o1
+       andcc   %o1, ((PCI_SYS_INT_PENDING_PIO|PCI_SYS_INT_PENDING_PCI)>>24), %g0
+       bne     1b
+        nop
+
+       or      %l0, PSR_PIL, %l4
+       wr      %l4, 0x0, %psr
+       WRITE_PAUSE
+       wr      %l4, PSR_ET, %psr
+       WRITE_PAUSE
+
+       call    C_LABEL(pcic_nmi)
+        add    %sp, REGWIN_SZ, %o1     ! struct pt_regs *regs
+       RESTORE_ALL
+
+       .globl  C_LABEL(pcic_nmi_trap_patch)
+C_LABEL(pcic_nmi_trap_patch):
+       sethi   %hi(linux_trap_ipi15_pcic), %l3
+       jmpl    %l3 + %lo(linux_trap_ipi15_pcic), %g0
+        rd     %psr, %l0
+       .word   0
+
+#endif /* CONFIG_PCI */
+
 /* End of entry.S */
index 0020770e090b42d756cafc797d6d12091b95f20d..ac78b14077374fc1b35a14c9932a658ec8d81071 100644 (file)
@@ -1,11 +1,13 @@
-/* $Id: head.S,v 1.95 1999/04/13 07:40:34 anton Exp $
+/* $Id: head.S,v 1.96 1999/06/03 15:02:15 davem Exp $
  * head.S: The initial boot code for the Sparc port of Linux.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
- * Copyright (C) 1995 Peter Zaitcev   (Zaitcev@ipmce.su)
+ * Copyright (C) 1995,1999 Pete Zaitcev   (zaitcev@metabyte.com)
  * Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx)
  * Copyright (C) 1997 Jakub Jelinek   (jj@sunsite.mff.cuni.cz)
  * Copyright (C) 1997 Michael A. Griffith (grif@acm.org)
+ *
+ * CompactPCI platform by Eric Brower, 1999.
  */
 
 #include <linux/version.h>
@@ -116,10 +118,10 @@ t_irq11:TRAP_ENTRY_INTERRUPT(11)            /* IRQ Floppy Intr.              */
 t_irq12:TRAP_ENTRY_INTERRUPT(12)            /* IRQ Zilog serial chip         */
 t_irq13:TRAP_ENTRY_INTERRUPT(13)            /* IRQ Audio Intr.               */
 t_irq14:TRAP_ENTRY_INTERRUPT(14)            /* IRQ Timer #2                  */
+       .globl  t_nmi
 #ifndef __SMP__
 t_nmi: NMI_TRAP                            /* Level 15 (NMI)                */
 #else
-       .globl  t_nmi
 t_nmi: TRAP_ENTRY(0x1f, linux_trap_ipi15_sun4m)
 #endif
 t_racc:        TRAP_ENTRY(0x20, do_reg_access)     /* General Register Access Error */
@@ -841,6 +843,8 @@ got_prop:
                 cmp    %l1, 'c'
                be      1f
                 cmp    %l1, 'm'
+               be      1f
+                cmp    %l1, 's'
                be      1f
                 cmp    %l1, 'd'
                be      1f
@@ -853,6 +857,8 @@ got_prop:
 1:             set     C_LABEL(cputypval), %l1
                ldub    [%l1 + 0x4], %l1
                cmp     %l1, 'm'                ! Test for sun4d, sun4e ?
+               be      sun4m_init
+                cmp    %l1, 's'                ! Treat sun4s as sun4m
                be      sun4m_init
                 cmp    %l1, 'd'                ! Let us see how the beast will die
                be      sun4d_init
index a2beedbf19b098a610f31d1510230906add53173..a4ae9497e7049b5525dcb780c424239bc0916f5a 100644 (file)
@@ -1,10 +1,13 @@
-/* $Id: pcic.c,v 1.5 1999/03/16 00:15:20 davem Exp $
+/* $Id: pcic.c,v 1.6 1999/06/03 15:02:18 davem Exp $
  * pcic.c: Sparc/PCI controller support
  *
  * Copyright (C) 1998 V. Roganov and G. Raiko
  *
  * Code is derived from Ultra/PCI PSYCHO controller support, see that
  * for author info.
+ *
+ * Support for diverse IIep based platforms by Pete Zaitcev.
+ * CP-1200 by Eric Brower.
  */
 
 #include <linux/config.h>
@@ -16,6 +19,7 @@
 
 #include <asm/ebus.h>
 #include <asm/sbus.h> /* for sanity check... */
+#include <asm/swift.h> /* for cache flushing. */
 
 #include <asm/io.h>
 
@@ -69,9 +73,99 @@ asmlinkage int sys_pciconfig_write(unsigned long bus,
 
 #else
 
+unsigned int pcic_pin_to_irq(unsigned int pin, char *name);
+
+/*
+ * I studied different documents and many live PROMs both from 2.30
+ * family and 3.xx versions. I came to the amazing conclusion: there is
+ * absolutely no way to route interrupts in IIep systems relying on
+ * information which PROM presents. We must hardcode interrupt routing
+ * schematics. And this actually sucks.   -- zaitcev 1999/05/12
+ *
+ * To find irq for a device we determine which routing map
+ * is in effect or, in other words, on which machine we are running.
+ * We use PROM name for this although other techniques may be used
+ * in special cases (Gleb reports a PROMless IIep based system).
+ * Once we know the map we take device configuration address and
+ * find PCIC pin number where INT line goes. Then we may either program
+ * preferred irq into the PCIC or supply the preexisting irq to the device.
+ *
+ * XXX Entries for JE-1 are completely bogus. Gleb, Vladimir, please fill them.
+ */
+struct pcic_ca2irq {
+       unsigned char busno;            /* PCI bus number */
+       unsigned char devfn;            /* Configuration address */
+       unsigned char pin;              /* PCIC external interrupt pin */
+       unsigned char irq;              /* Preferred IRQ (mappable in PCIC) */
+       unsigned int force;             /* Enforce preferred IRQ */
+};
+
+struct pcic_sn2list {
+       char *sysname;
+       struct pcic_ca2irq *intmap;
+       int mapdim;
+};
+
+/*
+ * XXX JE-1 is a little known beast.
+ * One rumor has the map this way: pin 0 - parallel, audio;
+ * pin 1 - Ethernet; pin 2 - su; pin 3 - PS/2 kbd and mouse.
+ * All other comparable systems tie serial and keyboard together,
+ * so we do not code this rumor just yet.
+ */
+static struct pcic_ca2irq pcic_i_je1[] = {
+       { 0, 0x01, 1,  6, 1 },          /* Happy Meal */
+};
+
+/* XXX JS-E entry is incomplete - PCI Slot 2 address (pin 7)? */
+static struct pcic_ca2irq pcic_i_jse[] = {
+       { 0, 0x00, 0, 13, 0 },          /* Ebus - serial and keyboard */
+       { 0, 0x01, 1,  6, 0 },          /* hme */
+       { 0, 0x08, 2,  9, 0 },          /* VGA - we hope not used :) */
+       { 0, 0x18, 6,  8, 0 },          /* PCI INTA# in Slot 1 */
+       { 0, 0x38, 4,  9, 0 },          /* All ISA devices. Read 8259. */
+       { 0, 0x80, 5, 11, 0 },          /* EIDE */
+       /* {0,0x88, 0,0,0} - unknown device... PMU? Probably no interrupt. */
+       { 0, 0xA0, 4,  9, 0 },          /* USB */
+       /*
+        * Some pins belong to non-PCI devices, we hardcode them in drivers.
+        * sun4m timers - irq 10, 14
+        * PC style RTC - pin 7, irq 4 ?
+        * Smart card, Parallel - pin 4 shared with USB, ISA
+        * audio - pin 3, irq 5 ?
+        */
+};
+
+/* SPARCengine-6 was the original release name of CP1200.
+ * The documentation differs between the two versions
+ */
+static struct pcic_ca2irq pcic_i_se6[] = {
+       { 0, 0x08, 0,  2, 0 },          /* SCSI */
+       { 0, 0x01, 1,  6, 0 },          /* HME  */
+       { 0, 0x00, 3, 13, 0 },          /* EBus */
+};
+
+/*
+ * Several entries in this list may point to the same routing map
+ * as several PROMs may be installed on the same physical board.
+ */
+#define SN2L_INIT(name, map)   \
+  { name, map, sizeof(map)/sizeof(struct pcic_ca2irq) }
+
+static struct pcic_sn2list pcic_known_sysnames[] = {
+       SN2L_INIT("JE-1-name", pcic_i_je1),  /* XXX Gleb, put name here, pls */
+       SN2L_INIT("SUNW,JS-E", pcic_i_jse),     /* PROLL JavaStation-E */
+       SN2L_INIT("SUNW,SPARCengine-6", pcic_i_se6), /* SPARCengine-6/CP-1200 */
+       { NULL, NULL, 0 }
+};
+
 static struct linux_pcic PCIC;
 static struct linux_pcic *pcic = NULL;
 
+unsigned int pcic_regs;
+volatile int pcic_speculative;
+volatile int pcic_trapped;
+
 static void pci_do_gettimeofday(struct timeval *tv);
 static void pci_do_settimeofday(struct timeval *tv);
 
@@ -149,6 +243,37 @@ __initfunc(void pcic_probe(void))
        pbm->prom_node = node;
        prom_getstring(node, "name", namebuf, sizeof(namebuf));
        strcpy(pbm->prom_name, namebuf);
+
+       {
+               extern volatile int t_nmi[1];
+               extern int pcic_nmi_trap_patch[1];
+
+               t_nmi[0] = pcic_nmi_trap_patch[0];
+               t_nmi[1] = pcic_nmi_trap_patch[1];
+               t_nmi[2] = pcic_nmi_trap_patch[2];
+               t_nmi[3] = pcic_nmi_trap_patch[3];
+               swift_flush_dcache();
+               pcic_regs = pcic->pcic_regs;
+       }
+
+       prom_getstring(prom_root_node, "name", namebuf, sizeof(namebuf));
+       {
+               struct pcic_sn2list *p;
+
+               for (p = pcic_known_sysnames; p->sysname != NULL; p++) {
+                       if (strcmp(namebuf, p->sysname) == 0)
+                               break;
+               }
+               pcic->pcic_imap = p->intmap;
+               pcic->pcic_imdim = p->mapdim;
+       }
+       if (pcic->pcic_imap == NULL) {
+               /*
+                * We do not panic here for the sake of embedded systems.
+                */
+               printk("PCIC: System %s is unknown, cannot route interrupts\n",
+                   namebuf);
+       }
 }
 
 __initfunc(void pcibios_init(void))
@@ -166,20 +291,15 @@ __initfunc(void pcibios_init(void))
               pcic->pcic_regs, pcic->pcic_io);
 
        /*
-        * FIXME:
         *      Switch off IOTLB translation.
-        *      It'll be great to use IOMMU to handle HME's rings
-        *      but we couldn't. Thus, we have to flush CPU cache
-        *      in HME.
         */
        writeb(PCI_DVMA_CONTROL_IOTLB_DISABLE, 
               pcic->pcic_regs+PCI_DVMA_CONTROL);
 
        /*
-        * FIXME:
         *      Increase mapped size for PCI memory space (DMA access).
         *      Should be done in that order (size first, address second).
-        *      Why we couldn't set up 4GB and forget about it ?
+        *      Why we couldn't set up 4GB and forget about it? XXX
         */
        writel(0xF0000000UL, pcic->pcic_regs+PCI_SIZE_0);
        writel(0+PCI_BASE_ADDRESS_SPACE_MEMORY, 
@@ -204,7 +324,7 @@ __initfunc(static int pdev_to_pnode(struct linux_pbm_info *pbm,
                if(err != 0 && err != -1) {
                        unsigned long devfn = (regs[0].which_io >> 8) & 0xff;
                        if(devfn == pdev->devfn)
-                               return node; /* Match */
+                               return node;
                }
                node = prom_getsibling(node);
        }
@@ -216,9 +336,9 @@ static inline struct pcidev_cookie *pci_devcookie_alloc(void)
        return kmalloc(sizeof(struct pcidev_cookie), GFP_ATOMIC);
 }
 
-
-static void pcic_map_pci_device (struct pci_dev *dev) {
-       int node, pcinode;
+static void pcic_map_pci_device (struct pci_dev *dev, int node) {
+       struct linux_prom_pci_assigned_addresses addrs[6];
+       int addrlen;
        int i, j;
 
        /* Is any valid address present ? */
@@ -227,74 +347,132 @@ static void pcic_map_pci_device (struct pci_dev *dev) {
                if (dev->base_address[j]) i++;
        if (!i) return; /* nothing to do */
 
+       if (node == 0 || node == -1) {
+               printk("PCIC: no prom node for device ID (%x,%x)\n",
+                   dev->device, dev->vendor);
+               return;
+       }
+
        /*
         * find related address and get it's window length
         */
-       pcinode = prom_getchild(prom_root_node);
-       pcinode = prom_searchsiblings(pcinode, "pci");
-       if (!pcinode)
-               panic("PCIC: failed to locate 'pci' node");
-
-
-       for (node = prom_getchild(pcinode); node;
-            node = prom_getsibling(node)) {
-               struct linux_prom_pci_assigned_addresses addrs[6];
-               int addrlen = prom_getproperty(node,"assigned-addresses",
+       addrlen = prom_getproperty(node,"assigned-addresses",
                                               (char*)addrs, sizeof(addrs));
-               if (addrlen == -1)
-                       continue;
+       if (addrlen == -1) {
+               printk("PCIC: no \"assigned-addresses\" for device (%x,%x)\n",
+                   dev->device, dev->vendor);
+               return;
+       }
 
-               addrlen /= sizeof(struct linux_prom_pci_assigned_addresses);
-               for (i = 0; i < addrlen; i++ )
-                   for (j = 0; j < 6; j++) {
-                       if (!dev->base_address[j] || !addrs[i].phys_lo)
-                               continue;
-                       if (addrs[i].phys_lo == dev->base_address[j]) {
-                           unsigned long address = dev->base_address[j];
-                           int length  = addrs[i].size_lo;
-                           char namebuf[128] = { 0, };
-                           unsigned long mapaddr, addrflags;
-           
-                           prom_getstring(node, "name",
-                                          namebuf,  sizeof(namebuf));
-
-                           /* FIXME:
-                            *      failure in allocation too large space
-                            */
-                           if (length > 0x200000) {
+       addrlen /= sizeof(struct linux_prom_pci_assigned_addresses);
+       for (i = 0; i < addrlen; i++ )
+           for (j = 0; j < 6; j++) {
+               if (!dev->base_address[j] || !addrs[i].phys_lo)
+                       continue;
+               if (addrs[i].phys_lo == dev->base_address[j]) {
+                       unsigned long address = dev->base_address[j];
+                       int length  = addrs[i].size_lo;
+                       char namebuf[128] = { 0, };
+                       unsigned long mapaddr, addrflags;
+
+                       prom_getstring(node, "name", namebuf, sizeof(namebuf));
+
+                       /*
+                        *      failure in allocation too large space
+                        */
+                       if (length > 0x200000) {
                                length = 0x200000;
                                prom_printf("PCIC: map window for device '%s' "
                                            "reduced to 2MB !\n", namebuf);
-                           }
+                       }
 
-                           /*
-                            *  Be careful with MEM/IO address flags
-                            */
-                           if ((address & PCI_BASE_ADDRESS_SPACE) ==
+                       /*
+                        *  Be careful with MEM/IO address flags
+                        */
+                       if ((address & PCI_BASE_ADDRESS_SPACE) ==
                                 PCI_BASE_ADDRESS_SPACE_IO) {
                                mapaddr = address & PCI_BASE_ADDRESS_IO_MASK;
-                           } else {
+                       } else {
                                mapaddr = address & PCI_BASE_ADDRESS_MEM_MASK;
-                           }
-                           addrflags = address ^ mapaddr;
+                       }
+                       addrflags = address ^ mapaddr;
 
-                           dev->base_address[j] =
+                       dev->base_address[j] =
                                (unsigned long)sparc_alloc_io(address, 0, 
                                                              length,
                                                              namebuf, 0, 0);
-                           if ( dev->base_address[j] == 0 )
+                       if ( dev->base_address[j] == 0 )
                                panic("PCIC: failed make mapping for "
                                      "pci device '%s' with address %lx\n",
                                       namebuf, address);
 
-                           dev->base_address[j] ^= addrflags;
-                           return;
-                       }
+                       dev->base_address[j] ^= addrflags;
+                       return;
                }
+           }
+
+       printk("PCIC: unable to match addresses for device (%x,%x)\n",
+           dev->device, dev->vendor);
+}
+
+static void pcic_fill_irq(struct pci_dev *dev, int node) {
+       struct pcic_ca2irq *p;
+       int i, ivec;
+       char namebuf[64];  /* P3 remove */
+
+       if (node == -1) {
+               strcpy(namebuf, "???");
+       } else {
+               prom_getstring(node, "name", namebuf, sizeof(namebuf)); /* P3 remove */
        }
 
-       panic("PCIC: unable to locate prom node for pci device (%x,%x) \n",
-             dev->device, dev->vendor);
+       if ((p = pcic->pcic_imap) == 0) {
+               dev->irq = 0;
+               return;
+       }
+       for (i = 0; i < pcic->pcic_imdim; i++) {
+               if (p->busno == dev->bus->number && p->devfn == dev->devfn)
+                       break;
+               p++;
+       }
+       if (i >= pcic->pcic_imdim) {
+               printk("PCIC: device %s devfn %02x:%02x not found in %d\n",
+                   namebuf, dev->bus->number, dev->devfn, pcic->pcic_imdim);
+               dev->irq = 0;
+               return;
+       }
+
+       i = p->pin;
+       if (i >= 0 && i < 4) {
+               ivec = readw(pcic->pcic_regs+PCI_INT_SELECT_LO);
+               dev->irq = ivec >> (i << 2) & 0xF;
+       } else if (i >= 4 && i < 8) {
+               ivec = readw(pcic->pcic_regs+PCI_INT_SELECT_HI);
+               dev->irq = ivec >> ((i-4) << 2) & 0xF;
+       } else {                                        /* Corrupted map */
+               printk("PCIC: BAD PIN %d\n", i); for (;;) {}
+       }
+/* P3 remove later */ printk("PCIC: device %s pin %d ivec 0x%x irq %x\n", namebuf, i, ivec, dev->irq);
+
+       /*
+        * dev->irq=0 means PROM did not bothered to program the upper
+        * half of PCIC. This happens on JS-E with PROM 3.11, for instance.
+        */
+       if (dev->irq == 0 || p->force) {
+               if (p->irq == 0 || p->irq >= 15) {      /* Corrupted map */
+                       printk("PCIC: BAD IRQ %d\n", p->irq); for (;;) {}
+               }
+               printk("PCIC: setting irq %x for device (%x,%x)\n",
+                   p->irq, dev->device, dev->vendor);
+               dev->irq = p->irq;
+
+               ivec = readw(pcic->pcic_regs+PCI_INT_SELECT_HI);
+               ivec &= ~(0xF << ((p->pin - 4) << 2));
+               ivec |= p->irq << ((p->pin - 4) << 2);
+               writew(ivec, pcic->pcic_regs+PCI_INT_SELECT_HI);
+       }
+
+       return;
 }
 
 /*
@@ -317,9 +495,10 @@ unsigned long pcic_alloc_io( unsigned long* addr )
                writeb((pcic->pcic_io_phys>>24) & PCI_SIBAR_ADDRESS_MASK,
                       pcic->pcic_regs+PCI_SIBAR);
                writeb(PCI_ISIZE_16M, pcic->pcic_regs+PCI_ISIZE);
+
        }
        if(paddr < pcic->pcic_mapped_io ||
-          paddr > pcic->pcic_mapped_io + PCI_SPACE_SIZE)
+          paddr >= pcic->pcic_mapped_io + 0x10000)
                return 0;
        offset = paddr - pcic->pcic_mapped_io;
        *addr = pcic->pcic_io_phys + offset;
@@ -334,6 +513,9 @@ __initfunc(void pcibios_fixup(void))
   struct pci_dev *dev;
   int i, has_io, has_mem;
   unsigned short cmd;
+       struct linux_pbm_info* pbm = &pcic->pbm;
+       int node;
+       struct pcidev_cookie *pcp;
 
        if(pcic == NULL) {
                prom_printf("PCI: Error, PCIC not found.\n");
@@ -359,47 +541,61 @@ __initfunc(void pcibios_fixup(void))
                }
                pci_read_config_word(dev, PCI_COMMAND, &cmd);
                if (has_io && !(cmd & PCI_COMMAND_IO)) {
-                       printk("PCI: Enabling I/O for device %02x:%02x\n",
+                       printk("PCIC: Enabling I/O for device %02x:%02x\n",
                                dev->bus->number, dev->devfn);
                        cmd |= PCI_COMMAND_IO;
                        pci_write_config_word(dev, PCI_COMMAND, cmd);
                }
                if (has_mem && !(cmd & PCI_COMMAND_MEMORY)) {
-                       printk("PCI: Enabling memory for device %02x:%02x\n",
+                       printk("PCIC: Enabling memory for device %02x:%02x\n",
                                dev->bus->number, dev->devfn);
                        cmd |= PCI_COMMAND_MEMORY;
                        pci_write_config_word(dev, PCI_COMMAND, cmd);
                }    
 
+               node = pdev_to_pnode(pbm, dev);
+               if(node == 0)
+                       node = -1;
+
                /* cookies */
-               {
-                       struct pcidev_cookie *pcp;
-                       struct linux_pbm_info* pbm = &pcic->pbm;
-                       int node = pdev_to_pnode(pbm, dev);
-
-                       if(node == 0)
-                               node = -1;
-                       pcp = pci_devcookie_alloc();
-                       pcp->pbm = pbm;
-                       pcp->prom_node = node;
-                       dev->sysdata = pcp;
-               }
+               pcp = pci_devcookie_alloc();
+               pcp->pbm = pbm;
+               pcp->prom_node = node;
+               dev->sysdata = pcp;
 
                /* memory mapping */
-               if (!(dev->vendor == PCI_VENDOR_ID_SUN &&
-                     dev->device == PCI_DEVICE_ID_SUN_EBUS)) {
-                       pcic_map_pci_device(dev);
-               }
-
-               /* irq */
-#define SETIRQ(vend,devid,irqn) \
-       if (dev->vendor==vend && dev->device==devid) dev->irq = irqn;
+               if ((dev->class>>16) != PCI_BASE_CLASS_BRIDGE)
+                       pcic_map_pci_device(dev, node);
 
-               SETIRQ(PCI_VENDOR_ID_SUN,PCI_DEVICE_ID_SUN_HAPPYMEAL,3);
+               pcic_fill_irq(dev, node);
        }
+
        ebus_init();
 }
 
+/*
+ * pcic_pin_to_irq() is exported to ebus.c.
+ */
+unsigned int
+pcic_pin_to_irq(unsigned int pin, char *name)
+{
+       unsigned int irq;
+       unsigned int ivec;
+
+       if (pin < 4) {
+               ivec = readw(pcic->pcic_regs+PCI_INT_SELECT_LO);
+               irq = ivec >> (pin << 2) & 0xF;
+       } else if (pin < 8) {
+               ivec = readw(pcic->pcic_regs+PCI_INT_SELECT_HI);
+               irq = ivec >> ((pin-4) << 2) & 0xF;
+       } else {                                        /* Corrupted map */
+               printk("PCIC: BAD PIN %d FOR %s\n", pin, name);
+               for (;;) {}     /* XXX Cannot panic properly in case of PROLL */
+       }
+/* P3 remove later */ printk("PCIC: dev %s pin %d ivec 0x%x irq %x\n", name, pin, ivec, irq);
+       return irq;
+}
+
 /* Makes compiler happy */
 static volatile int pcic_timer_dummy;
 
@@ -539,26 +735,38 @@ int pcibios_read_config_dword (unsigned char bus, unsigned char device_fn,
                               unsigned char where, unsigned int *value)
 {
        unsigned long flags;
-       if (where&3) return PCIBIOS_BAD_REGISTER_NUMBER;
-       if (bus != 0 || 
-           (device_fn != 0 && device_fn != 1 && device_fn != 0x80)) {
-               *value = 0xffffffff;
-               return PCIBIOS_SUCCESSFUL;
-       }
 
-       /* FIXME: IGA haven't got high config memory addresses !!! */
-       if (device_fn == 0x80 && where > PCI_INTERRUPT_LINE) {
-               *value = 0xffffffff;
-               return PCIBIOS_SUCCESSFUL;
-       }
+       if (where&3) return PCIBIOS_BAD_REGISTER_NUMBER;
 
        save_and_cli(flags);
+#if 0
+       pcic_speculative = 1;
+       pcic_trapped = 0;
+#endif
        writel(CONFIG_CMD(bus,device_fn,where), pcic->pcic_config_space_addr);
+#if 0
+       nop();
+       if (pcic_trapped) {
+               restore_flags(flags);
+               *value = ~0;
+               return PCIBIOS_SUCCESSFUL;
+       }
+#endif
+       pcic_speculative = 2;
+       pcic_trapped = 0;
        *value = readl(pcic->pcic_config_space_data + (where&4));
+       nop();
+       if (pcic_trapped) {
+               pcic_speculative = 0;
+               restore_flags(flags);
+               *value = ~0;
+               return PCIBIOS_SUCCESSFUL;
+       }
+       pcic_speculative = 0;
        restore_flags(flags);
        return PCIBIOS_SUCCESSFUL;
 }
-    
+
 int pcibios_write_config_byte (unsigned char bus, unsigned char devfn,
                               unsigned char where, unsigned char value)
 {
@@ -586,8 +794,8 @@ int pcibios_write_config_dword (unsigned char bus, unsigned char devfn,
                                unsigned char where, unsigned int value)
 {
        unsigned long flags;
-       if ((where&3) || bus != 0 || (devfn != 0 && devfn != 1 && devfn != 0x80))
-               return PCIBIOS_BAD_REGISTER_NUMBER;
+
+       if (where&3) return PCIBIOS_BAD_REGISTER_NUMBER;
 
        save_and_cli(flags);
        writel(CONFIG_CMD(bus,devfn,where),pcic->pcic_config_space_addr);
@@ -601,6 +809,29 @@ __initfunc(char *pcibios_setup(char *str))
        return str;
 }
 
+/*
+ * NMI
+ */
+void pcic_nmi(unsigned int pend, struct pt_regs *regs)
+{
+
+       pend = flip_dword(pend);
+
+       if (!pcic_speculative || (pend & PCI_SYS_INT_PENDING_PIO) == 0) {
+               /*
+                * XXX On CP-1200 PCI #SERR may happen, we do not know
+                * what to do about it yet.
+                */
+               printk("Aiee, NMI pend 0x%x pc 0x%x spec %d, hanging\n",
+                   pend, (int)regs->pc, pcic_speculative);
+               for (;;) { }
+       }
+       pcic_speculative = 0;
+       pcic_trapped = 1;
+       regs->pc = regs->npc;
+       regs->npc += 4;
+}
+
 /*
  *  Following code added to handle extra PCI-related system calls 
  */
index 51ece9a39da009b9f24b709089d6da74575028c9..1e22e086e48d95e249f5c66b8828c45b85feffc8 100644 (file)
@@ -1,4 +1,4 @@
-/*  $Id: setup.c,v 1.106 1999/05/28 16:03:18 anton Exp $
+/*  $Id: setup.c,v 1.107 1999/06/03 15:02:20 davem Exp $
  *  linux/arch/sparc/kernel/setup.c
  *
  *  Copyright (C) 1995  David S. Miller (davem@caip.rutgers.edu)
@@ -313,6 +313,7 @@ __initfunc(void setup_arch(char **cmdline_p,
        if(!strcmp(&cputypval,"sun4 ")) { sparc_cpu_model=sun4; }
        if(!strcmp(&cputypval,"sun4c")) { sparc_cpu_model=sun4c; }
        if(!strcmp(&cputypval,"sun4m")) { sparc_cpu_model=sun4m; }
+       if(!strcmp(&cputypval,"sun4s")) { sparc_cpu_model=sun4m; }  /* CP-1200 with PROM 2.30 -E */
        if(!strcmp(&cputypval,"sun4d")) { sparc_cpu_model=sun4d; }
        if(!strcmp(&cputypval,"sun4e")) { sparc_cpu_model=sun4e; }
        if(!strcmp(&cputypval,"sun4u")) { sparc_cpu_model=sun4u; }
index 695ba390568ffbaff462fe40c5aa24bc6716dab2..050ba65dbf2b19792e14f64c000978390e59db8c 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: sys_sunos.c,v 1.97 1999/05/24 19:40:39 davem Exp $
+/* $Id: sys_sunos.c,v 1.98 1999/06/09 08:23:39 davem Exp $
  * sys_sunos.c: SunOS specific syscall compatibility support.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
index b76cd05b93f596bc4e7f5551010fb7621479f1d0..4df9bc0f5a581714b8957cb40a714c5e0c6e19b3 100644 (file)
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.35 1999/01/02 16:45:50 davem Exp $
+# $Id: Makefile,v 1.37 1999/06/04 13:29:10 jj Exp $
 # sparc64/Makefile
 #
 # Makefile for the architecture dependent flags and dependencies on the
@@ -15,7 +15,7 @@ SHELL  =/bin/bash
 CC             := sparc64-linux-gcc -D__KERNEL__ -I$(TOPDIR)/include
 
 CC_HAS_ARGS := $(shell if echo "$(CC)" | grep '\(__KERNEL__\|  \)' > /dev/null; then echo y; else echo n; fi)
-IS_EGCS := $(shell if $(CC) --version 2>&1 | grep 'egcs' > /dev/null; then echo y; else echo n; fi)
+IS_EGCS := $(shell if $(CC) -c -m64 -mcmodel=medlow -o _tmp.o arch/sparc64/math-emu/fnegq.c >/dev/null 2>&1; then echo y; else echo n; fi; rm -f _tmp.o)
 NEW_GAS := $(shell if $(LD) --version 2>&1 | grep 'elf64_sparc' > /dev/null; then echo y; else echo n; fi)
 
 ifneq ($(CC_HAS_ARGS),y)
index 738f1338ef0e9b96c386cba4cfff3fc7ef538a46..051c6560ac583d2cdc80b4ddad3db6b1d9a5b2b9 100644 (file)
@@ -69,6 +69,8 @@ CONFIG_OBP_FLASH=m
 # CONFIG_SUN_BPP is not set
 # CONFIG_SUN_VIDEOPIX is not set
 CONFIG_SUN_AURORA=m
+CONFIG_APM_RTC_IS_GMT=y
+# CONFIG_RTC is not set
 
 #
 # Linux/SPARC audio subsystem (EXPERIMENTAL)
index 652fc5871166172497090c8d000122a6415d078a..e852ca8f822740667b172ced3f35461d550f3832 100644 (file)
@@ -349,11 +349,16 @@ beyond_if:
 
        set_brk(current->mm->start_brk, current->mm->brk);
 
-       p = setup_arg_pages(p, bprm);
+       retval = setup_arg_pages(bprm);
+       if (retval < 0) { 
+               /* Someone check-me: is this error path enough? */ 
+               send_sig(SIGKILL, current, 0); 
+               return retval;
+       }
 
-       p = (unsigned long) create_aout32_tables((char *)p, bprm);
-       current->mm->start_stack = p;
-       start_thread32(regs, ex.a_entry, p);
+       current->mm->start_stack =
+               (unsigned long) create_aout32_tables((char *)bprm->p, bprm);
+       start_thread32(regs, ex.a_entry, current->mm->start_stack);
        if (current->flags & PF_PTRACED)
                send_sig(SIGTRAP, current, 0);
        return 0;
index 5caa558b949825571f625149568af686d2d25edf..6759f74d452707b62762e31f79619baa9d4b4fc1 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: ioctl32.c,v 1.62.2.1 1999/06/09 04:53:03 davem Exp $
+/* $Id: ioctl32.c,v 1.63 1999/06/09 04:56:14 davem Exp $
  * ioctl32.c: Conversion between 32bit and 64bit native ioctls.
  *
  * Copyright (C) 1997  Jakub Jelinek  (jj@sunsite.mff.cuni.cz)
index 91dc7224d8c7bf5398ecbcb3b89ce33c38f9ea70..e0ba8aa1e2b016ad80c914afe130c086a451f223 100644 (file)
@@ -1,4 +1,4 @@
-/*  $Id: signal.c,v 1.38 1998/10/16 03:19:04 davem Exp $
+/*  $Id: signal.c,v 1.40 1999/06/02 19:19:52 jj Exp $
  *  arch/sparc64/kernel/signal.c
  *
  *  Copyright (C) 1991, 1992  Linus Torvalds
@@ -491,7 +491,7 @@ segv:
 /* Checks if the fp is valid */
 static int invalid_frame_pointer(void *fp, int fplen)
 {
-       if ((((unsigned long) fp) & 7) || ((unsigned long)fp) > 0x80000000000ULL - fplen)
+       if (((unsigned long) fp) & 7)
                return 1;
        return 0;
 }
@@ -554,8 +554,10 @@ new_setup_frame(struct k_sigaction *ka, struct pt_regs *regs,
                goto sigill;
 
        if (current->tss.w_saved != 0) {
+#ifdef DEBUG_SIGNALS
                printk ("%s[%d]: Invalid user stack frame for "
                        "signal delivery.\n", current->comm, current->pid);
+#endif
                goto sigill;
        }
 
@@ -590,35 +592,7 @@ new_setup_frame(struct k_sigaction *ka, struct pt_regs *regs,
        regs->tnpc = (regs->tpc + 4);
 
        /* 4. return to kernel instructions */
-       if (ka->ka_restorer)
-               regs->u_regs[UREG_I7] = (unsigned long)ka->ka_restorer;
-       else {
-               /* Flush instruction space. */
-               unsigned long address = ((unsigned long)&(sf->insns[0]));
-               pgd_t *pgdp = pgd_offset(current->mm, address);
-               pmd_t *pmdp = pmd_offset(pgdp, address);
-               pte_t *ptep = pte_offset(pmdp, address);
-
-               regs->u_regs[UREG_I7] = (unsigned long) (&(sf->insns[0]) - 2);
-               
-               /* mov __NR_sigreturn, %g1 */
-               err |= __put_user(0x821020d8, &sf->insns[0]);
-
-               /* t 0x6d */
-               err |= __put_user(0x91d0206d, &sf->insns[1]);
-               if (err)
-                       goto sigsegv;
-
-               if(pte_present(*ptep)) {
-                       unsigned long page = pte_page(*ptep);
-
-                       __asm__ __volatile__("
-                       membar  #StoreStore
-                       flush   %0 + %1"
-                       : : "r" (page), "r" (address & (PAGE_SIZE - 1))
-                       : "memory");
-               }
-       }
+       regs->u_regs[UREG_I7] = (unsigned long)ka->ka_restorer;
        return;
 
 sigill:
@@ -650,8 +624,10 @@ setup_rt_frame(struct k_sigaction *ka, struct pt_regs *regs,
                goto sigill;
 
        if (current->tss.w_saved != 0) {
+#ifdef DEBUG_SIGNALS
                printk ("%s[%d]: Invalid user stack frame for "
                        "signal delivery.\n", current->comm, current->pid);
+#endif
                goto sigill;
        }
 
@@ -690,35 +666,7 @@ setup_rt_frame(struct k_sigaction *ka, struct pt_regs *regs,
        regs->tnpc = (regs->tpc + 4);
 
        /* 4. return to kernel instructions */
-       if (ka->ka_restorer)
-               regs->u_regs[UREG_I7] = (unsigned long)ka->ka_restorer;
-       else {
-               /* Flush instruction space. */
-               unsigned long address = ((unsigned long)&(sf->insns[0]));
-               pgd_t *pgdp = pgd_offset(current->mm, address);
-               pmd_t *pmdp = pmd_offset(pgdp, address);
-               pte_t *ptep = pte_offset(pmdp, address);
-
-               regs->u_regs[UREG_I7] = (unsigned long) (&(sf->insns[0]) - 2);
-               
-               /* mov __NR_rt_sigreturn, %g1 */
-               err |= __put_user(0x82102065, &sf->insns[0]);
-
-               /* t 0x6d */
-               err |= __put_user(0x91d0206d, &sf->insns[1]);
-               if (err)
-                       goto sigsegv;
-
-               if(pte_present(*ptep)) {
-                       unsigned long page = pte_page(*ptep);
-
-                       __asm__ __volatile__("
-                       membar  #StoreStore
-                       flush   %0 + %1"
-                       : : "r" (page), "r" (address & (PAGE_SIZE - 1))
-                       : "memory");
-               }
-       }
+       regs->u_regs[UREG_I7] = (unsigned long)ka->ka_restorer;
        return;
 
 sigill:
index 8d11f10b8fbcf0a1ebef115fe61ad8524244ccea..4906cea93a5924f648a1c18502673110e70e637e 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: sys_sparc.c,v 1.26 1999/01/07 19:07:01 jj Exp $
+/* $Id: sys_sparc.c,v 1.27 1999/06/02 12:06:34 jj Exp $
  * linux/arch/sparc64/kernel/sys_sparc.c
  *
  * This file contains various random system calls that
@@ -327,39 +327,6 @@ long sparc_memory_ordering(unsigned long model, struct pt_regs *regs)
        return 0;
 }
 
-asmlinkage int
-sys_sigaction(int sig, const struct old_sigaction *act,
-             struct old_sigaction *oact)
-{
-       struct k_sigaction new_ka, old_ka;
-       int ret;
-
-       if (act) {
-               old_sigset_t mask;
-               if (verify_area(VERIFY_READ, act, sizeof(*act)) ||
-                   __get_user(new_ka.sa.sa_handler, &act->sa_handler) ||
-                   __get_user(new_ka.sa.sa_restorer, &act->sa_restorer))
-                       return -EFAULT;
-               __get_user(new_ka.sa.sa_flags, &act->sa_flags);
-               __get_user(mask, &act->sa_mask);
-               siginitset(&new_ka.sa.sa_mask, mask);
-               new_ka.ka_restorer = NULL;
-       }
-
-       ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL);
-
-       if (!ret && oact) {
-               if (verify_area(VERIFY_WRITE, oact, sizeof(*oact)) ||
-                   __put_user(old_ka.sa.sa_handler, &oact->sa_handler) ||
-                   __put_user(old_ka.sa.sa_restorer, &oact->sa_restorer))
-                       return -EFAULT;
-               __put_user(old_ka.sa.sa_flags, &oact->sa_flags);
-               __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask);
-       }
-
-       return ret;
-}
-
 asmlinkage int
 sys_rt_sigaction(int sig, const struct sigaction *act, struct sigaction *oact,
                 void *restorer, size_t sigsetsize)
index 0aec81915897987f02f6747dde3aaef75382cd79..79ce3db45b63c906f10d95f54e8e4bd337d9888d 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: sys_sparc32.c,v 1.108 1999/05/16 10:50:32 davem Exp $
+/* $Id: sys_sparc32.c,v 1.109 1999/06/03 07:11:31 davem Exp $
  * sys_sparc32.c: Conversion between 32bit and 64bit native syscalls.
  *
  * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
@@ -2776,42 +2776,46 @@ static int count32(u32 * argv)
  * memory to free pages in kernel mem. These are in a format ready
  * to be put directly into the top of new user memory.
  */
-static unsigned long
-copy_strings32(int argc,u32 * argv,unsigned long *page,
-              unsigned long p)
+static int copy_strings32(int argc, u32 * argv, struct linux_binprm *bprm)
 {
-       u32 str;
-
-       if (!p) return 0;       /* bullet-proofing */
        while (argc-- > 0) {
+               u32 str;
                int len;
                unsigned long pos;
 
-               get_user(str, argv+argc);
-               if (!str) panic("VFS: argc is wrong");
-               len = strlen_user((char *)A(str));      /* includes the '\0' */
-               if (p < len)    /* this shouldn't happen - 128kB */
-                       return 0;
-               p -= len; pos = p;
+               if (get_user(str, argv + argc) ||
+                   !str ||
+                   !(len = strlen_user((char *)A(str))))
+                       return -EFAULT;
+               if (bprm->p < len)
+                       return -E2BIG;
+
+               bprm->p -= len;
+
+               pos = bprm->p;
                while (len) {
                        char *pag;
                        int offset, bytes_to_copy;
 
                        offset = pos % PAGE_SIZE;
-                       if (!(pag = (char *) page[pos/PAGE_SIZE]) &&
-                           !(pag = (char *) page[pos/PAGE_SIZE] =
+                       if (!(pag = (char *) bprm->page[pos/PAGE_SIZE]) &&
+                           !(pag = (char *) bprm->page[pos/PAGE_SIZE] =
                              (unsigned long *) get_free_page(GFP_USER)))
-                               return 0;
+                               return -ENOMEM;
+
                        bytes_to_copy = PAGE_SIZE - offset;
                        if (bytes_to_copy > len)
                                bytes_to_copy = len;
-                       copy_from_user(pag + offset, (char *)A(str), bytes_to_copy);
+
+                       if (copy_from_user(pag + offset, (char *)A(str), bytes_to_copy))
+                               return -EFAULT;
+
                        pos += bytes_to_copy;
                        str += bytes_to_copy;
                        len -= bytes_to_copy;
                }
        }
-       return p;
+       return 0;
 }
 
 /*
@@ -2850,29 +2854,36 @@ do_execve32(char * filename, u32 * argv, u32 * envp, struct pt_regs * regs)
        }
 
        retval = prepare_binprm(&bprm);
+       if (retval < 0)
+               goto out;
        
-       if(retval>=0) {
-               bprm.p = copy_strings(1, &bprm.filename, bprm.page, bprm.p, 2);
-               bprm.exec = bprm.p;
-               bprm.p = copy_strings32(bprm.envc,envp,bprm.page,bprm.p);
-               bprm.p = copy_strings32(bprm.argc,argv,bprm.page,bprm.p);
-               if (!bprm.p)
-                       retval = -E2BIG;
-       }
-
-       if(retval>=0)
-               retval = search_binary_handler(&bprm,regs);
-       if(retval>=0)
+       retval = copy_strings_kernel(1, &bprm.filename, &bprm);
+       if (retval < 0)
+               goto out;
+
+       bprm.exec = bprm.p;
+       retval = copy_strings32(bprm.envc, envp, &bprm);
+       if (retval < 0)
+               goto out;
+
+       retval = copy_strings32(bprm.argc, argv, &bprm);
+       if (retval < 0)
+               goto out;
+
+       retval = search_binary_handler(&bprm, regs);
+       if (retval >= 0)
                /* execve success */
                return retval;
 
+out:
        /* Something went wrong, return the inode and free the argument pages*/
-       if(bprm.dentry)
+       if (bprm.dentry)
                dput(bprm.dentry);
 
        for (i=0 ; i<MAX_ARG_PAGES ; i++)
                free_page(bprm.page[i]);
-       return(retval);
+
+       return retval;
 }
 
 /*
index e9f117f5f3992c2b74164c27196116c7b8299fd8..99e010e78a29e88b2ec14cc90823f65054dc971d 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: sys_sunos32.c,v 1.25 1999/05/24 19:40:44 davem Exp $
+/* $Id: sys_sunos32.c,v 1.26 1999/06/09 08:23:54 davem Exp $
  * sys_sunos32.c: SunOS binary compatability layer on sparc64.
  *
  * Copyright (C) 1995, 1996, 1997 David S. Miller (davem@caip.rutgers.edu)
index d2a75033abd06be3eee886cdd6fd46920f15c9d7..e99ae05325dc3f1c9247ec1af8cbcd094091eb39 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: systbls.S,v 1.53 1999/04/07 17:14:11 davem Exp $
+/* $Id: systbls.S,v 1.54 1999/06/02 12:06:31 jj Exp $
  * systbls.S: System call entry point tables for OS compatibility.
  *            The native Linux system call table lives here also.
  *
@@ -115,7 +115,7 @@ sys_call_table:
 /*180*/        .word sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_sigpending, sys_query_module
        .word sys_setpgid, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_newuname
 /*190*/        .word sys_init_module, sys_personality, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall
-       .word sys_nis_syscall, sys_nis_syscall, sys_getppid, sys_sigaction, sys_sgetmask
+       .word sys_nis_syscall, sys_nis_syscall, sys_getppid, sys_nis_syscall, sys_sgetmask
 /*200*/        .word sys_ssetmask, sys_sigsuspend, sys_newlstat, sys_uselib, sys_nis_syscall
        .word sys_nis_syscall, sys_socketcall, sys_syslog, sys_nis_syscall, sys_nis_syscall
 /*210*/        .word sys_idle, sys_nis_syscall, sys_waitpid, sys_swapoff, sys_sysinfo
index 3244a16fe6c639337a7e51fa08302de58d03a8c9..86ee5b661ce7fc2ed7c47cfaaed6f7acfec4b023 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: traps.c,v 1.59 1999/05/18 16:57:10 jj Exp $
+/* $Id: traps.c,v 1.60 1999/06/02 19:19:55 jj Exp $
  * arch/sparc64/kernel/traps.c
  *
  * Copyright (C) 1995,1997 David S. Miller (davem@caip.rutgers.edu)
@@ -282,11 +282,16 @@ void instruction_access_exception (struct pt_regs *regs,
                                   unsigned long sfsr, unsigned long sfar)
 {
        lock_kernel();
+       if (regs->tstate & TSTATE_PRIV) {
 #if 1
-       printk("instruction_access_exception: Shit SFSR[%016lx] SFAR[%016lx], going.\n",
-              sfsr, sfar);
+               printk("instruction_access_exception: Shit SFSR[%016lx] SFAR[%016lx], going.\n",
+                      sfsr, sfar);
 #endif
-       die_if_kernel("Iax", regs);
+               die_if_kernel("Iax", regs);
+       }
+       current->tss.sig_desc = SUBSIG_ILLINST;
+       current->tss.sig_address = regs->tpc;
+       force_sig(SIGILL, current);
        unlock_kernel();
 }
 
index 1bf0ef259b5ead395e145b05f5a77f7bd26d6e1f..2440b26d253718c607d1d66b639c94bf3602e42e 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: sfp-util.h,v 1.1 1999/05/28 13:43:07 jj Exp $
+/* $Id: sfp-util.h,v 1.2 1999/06/07 18:24:15 jj Exp $
  * arch/sparc64/math-emu/sfp-util.h
  *
  * Copyright (C) 1999 Jakub Jelinek (jj@ultra.linux.cz)
@@ -55,7 +55,7 @@
                    srlx %7,32,%5
                    mulx %3,%5,%3
                    mulx %2,%5,%5
-                   sethi 0x80000000,%2
+                   sethi %%hi(0x80000000),%2
                    addcc %4,%3,%4
                    srlx %4,32,%4
                    add %2,%2,%2
index d91a0a86bcb75e858b0180aeb861ca4a29400397..7cfb115b93e96a456f53d568ea0cec7b6c4405c6 100644 (file)
@@ -1927,8 +1927,6 @@ static int start_motor(void (*function)(void) )
 
 static void floppy_ready(void)
 {
-       unsigned long flags;
-       
        CHECK_RESET;
        if (start_motor(floppy_ready)) return;
        if (fdc_dtr()) return;
@@ -1948,7 +1946,7 @@ static void floppy_ready(void)
        if ((raw_cmd->flags & FD_RAW_READ) || 
            (raw_cmd->flags & FD_RAW_WRITE))
        {
-               flags=claim_dma_lock();
+               unsigned long flags = claim_dma_lock();
                fd_chose_dma_mode(raw_cmd->kernel_data,
                                  raw_cmd->length);
                release_dma_lock(flags);
index 0102ce5ccb24efcfa6dc79b4cc560d4bf1213020..ebe17535117c5b6c3efd111c27ea2f21ffb6e7a4 100644 (file)
@@ -76,6 +76,7 @@ extern void acq_init(void);
 extern void dtlk_init(void);
 extern void pcwatchdog_init(void);
 extern int rtc_init(void);
+extern int rtc_sun_init(void);         /* Combines MK48T02 and MK48T08 */
 extern int rtc_DP8570A_init(void);
 extern int rtc_MK48T08_init(void);
 extern int dsp56k_init(void);
@@ -247,7 +248,10 @@ int __init misc_init(void)
 #ifdef CONFIG_BVME6000
        rtc_DP8570A_init();
 #endif
-#if defined(CONFIG_RTC) || defined(CONFIG_SUN_MOSTEK_RTC)
+#if defined(CONFIG_SUN_MOSTEK_RTC)
+       rtc_sun_init();
+#endif
+#if defined(CONFIG_RTC)
        rtc_init();
 #endif
 #ifdef CONFIG_ATARI_DSP56K
index d47ae941f470b5e9e352b63b25865b6cead3f6ab..7a744d37860e593db70a59f8418b90565772dafe 100644 (file)
  *     1.08    Miquel van Smoorenburg: disallow certain things on the
  *             DEC Alpha as the CMOS clock is also used for other things.
  *     1.09    Nikita Schmidt: epoch support and some Alpha cleanup.
+ *     1.09a   Pete Zaitcev: Sun SPARC
  *
  */
 
-#define RTC_VERSION            "1.09"
+#define RTC_VERSION            "1.09a"
 
 #define RTC_IRQ        8       /* Can't see this changing soon.        */
 #define RTC_IO_EXTENT  0x10    /* Only really two ports, but...        */
 #include <asm/uaccess.h>
 #include <asm/system.h>
 
+#ifdef __sparc__
+#include <asm/ebus.h>
+
+static unsigned long rtc_port;
+#endif
+
 /*
  *     We sponge a minor off of the misc major. No need slurping
  *     up another valuable major dev number for this. If you add
@@ -83,12 +90,12 @@ static int rtc_ioctl(struct inode *inode, struct file *file,
 
 static unsigned int rtc_poll(struct file *file, poll_table *wait);
 
-void get_rtc_time (struct rtc_time *rtc_tm);
-void get_rtc_alm_time (struct rtc_time *alm_tm);
-void rtc_dropped_irq(unsigned long data);
+static void get_rtc_time (struct rtc_time *rtc_tm);
+static void get_rtc_alm_time (struct rtc_time *alm_tm);
+static void rtc_dropped_irq(unsigned long data);
 
-void set_rtc_irq_bit(unsigned char bit);
-void mask_rtc_irq_bit(unsigned char bit);
+static void set_rtc_irq_bit(unsigned char bit);
+static void mask_rtc_irq_bit(unsigned char bit);
 
 static inline unsigned char rtc_is_updating(void);
 
@@ -525,7 +532,42 @@ __initfunc(int rtc_init(void))
        unsigned long uip_watchdog;
        char *guess = NULL;
 #endif
+#ifdef __sparc__
+       struct linux_ebus *ebus;
+       struct linux_ebus_device *edev;
+       int rtc_irq;
+#endif
+
        printk(KERN_INFO "Real Time Clock Driver v%s\n", RTC_VERSION);
+#ifdef __sparc__
+       for_each_ebus(ebus) {
+               for_each_ebusdev(edev, ebus) {
+                       if(strcmp(edev->prom_name, "rtc") == 0) {
+                               goto found;
+                       }
+               }
+       }
+       printk("rtc_init: no PC rtc found\n");
+       return -EIO;
+
+found:
+       rtc_port = edev->base_address[0];
+       rtc_irq = edev->irqs[0];
+       /*
+        * XXX Interrupt pin #7 in Espresso is shared between RTC and
+        * PCI Slot 2 INTA# (and some INTx# in Slot 1). SA_INTERRUPT here
+        * is asking for trouble with add-on boards. Change to SA_SHIRQ.
+        */
+       if(request_irq(rtc_irq, rtc_interrupt, SA_INTERRUPT, "rtc", (void *)&rtc_port)) {
+               /*
+                * Standard way for sparc to print irq's is to use
+                * __irq_itoa(). I think for EBus it's ok to use %d.
+                */
+               printk("rtc: cannot register IRQ %d\n", rtc_irq);
+               return -EIO;
+       }
+       misc_register(&rtc_dev);
+#else
        if(request_irq(RTC_IRQ, rtc_interrupt, SA_INTERRUPT, "rtc", NULL))
        {
                /* Yeah right, seeing as irq 8 doesn't even hit the bus. */
@@ -535,6 +577,7 @@ __initfunc(int rtc_init(void))
        misc_register(&rtc_dev);
        /* Check region? Naaah! Just snarf it up. */
        request_region(RTC_PORT(0), RTC_IO_EXTENT, "rtc");
+#endif /* __sparc__ vs. others */
 #ifdef __alpha__
        rtc_freq = HZ;
        
@@ -588,7 +631,7 @@ __initfunc(int rtc_init(void))
  *     for something that requires a steady > 1KHz signal anyways.)
  */
 
-void rtc_dropped_irq(unsigned long data)
+static void rtc_dropped_irq(unsigned long data)
 {
        unsigned long flags;
 
@@ -696,7 +739,7 @@ static inline unsigned char rtc_is_updating(void)
        return uip;
 }
 
-void get_rtc_time(struct rtc_time *rtc_tm)
+static void get_rtc_time(struct rtc_time *rtc_tm)
 {
 
        unsigned long flags, uip_watchdog = jiffies;
@@ -753,7 +796,7 @@ void get_rtc_time(struct rtc_time *rtc_tm)
        rtc_tm->tm_mon--;
 }
 
-void get_rtc_alm_time(struct rtc_time *alm_tm)
+static void get_rtc_alm_time(struct rtc_time *alm_tm)
 {
        unsigned long flags;
        unsigned char ctrl;
@@ -803,7 +846,7 @@ void mask_rtc_irq_bit(unsigned char bit)
        rtc_irq_data = 0;
 }
 
-void set_rtc_irq_bit(unsigned char bit)
+static void set_rtc_irq_bit(unsigned char bit)
 {
        unsigned char val;
        unsigned long flags;
index f61aae3b86474e9a8490edb3c76914a78f747d76..5baf7d25e49602dbe7beed01dce873c176b46d3f 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: parport_ax.c,v 1.17 1999/01/20 06:18:54 davem Exp $
+/* $Id: parport_ax.c,v 1.19 1999/06/09 08:24:40 davem Exp $
  * Parallel-port routines for Sun Ultra/AX architecture
  * 
  * Author: Eddie C. Dost <ecd@skynet.be>
index 659824e92e3e04d8158218b7749b88d3eb3c584a..6772139acc9f98323599bffedd351489432b51c9 100644 (file)
@@ -756,6 +756,7 @@ int myri_header_cache(struct neighbour *neigh, struct hh_cache *hh)
        eth->h_proto = type;
        memcpy(eth->h_source, dev->dev_addr, dev->addr_len);
        memcpy(eth->h_dest, neigh->ha, dev->addr_len);
+       hh->hh_len = 16;
        return 0;
 }
 
index 7c427b1dcd9d166b39596a98246e0c76a2d3e63c..978ee37ef8922b3cf3e1c8ee494b6e425bb8fb3b 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: ptifddi.c,v 1.5 1997/04/16 10:27:27 jj Exp $
+/* $Id: ptifddi.c,v 1.7 1999/06/09 08:19:01 davem Exp $
  * ptifddi.c: Network driver for Performance Technologies single-attach
  *            and dual-attach FDDI sbus cards.
  *
index e21d3ec3b56c18447a5c43ef722342882c14c496..b34d74e96d74e0e6f5fbda09207216ed9e4ea940 100644 (file)
@@ -540,9 +540,11 @@ struct pci_dev_info dev_info[] = {
        DEVICE( INTEL,          INTEL_82450GX,  "82450GX Orion P6"),
        DEVICE( KTI,            KTI_ET32P2,     "ET32P2"),
        DEVICE( ADAPTEC,        ADAPTEC_7810,   "AIC-7810 RAID"),
+       DEVICE( ADAPTEC,        ADAPTEC_7821,   "AIC-7860"),
        DEVICE( ADAPTEC,        ADAPTEC_7850,   "AIC-7850"),
        DEVICE( ADAPTEC,        ADAPTEC_7855,   "AIC-7855"),
        DEVICE( ADAPTEC,        ADAPTEC_5800,   "AIC-5800"),
+       DEVICE( ADAPTEC,        ADAPTEC_3860,   "AIC-7860"),
        DEVICE( ADAPTEC,        ADAPTEC_7860,   "AIC-7860"),
        DEVICE( ADAPTEC,        ADAPTEC_7861,   "AIC-7861"),
        DEVICE( ADAPTEC,        ADAPTEC_7870,   "AIC-7870"),
@@ -556,13 +558,26 @@ struct pci_dev_info dev_info[] = {
        DEVICE( ADAPTEC,        ADAPTEC_7882,   "AIC-7882U"),
        DEVICE( ADAPTEC,        ADAPTEC_7883,   "AIC-7883U"),
        DEVICE( ADAPTEC,        ADAPTEC_7884,   "AIC-7884U"),
+       DEVICE( ADAPTEC,        ADAPTEC_7885,   "AIC-7885U"),
+       DEVICE( ADAPTEC,        ADAPTEC_7886,   "AIC-7886U"),
+       DEVICE( ADAPTEC,        ADAPTEC_7887,   "AIC-7887U"),
+       DEVICE( ADAPTEC,        ADAPTEC_7888,   "AIC-7888U"),
        DEVICE( ADAPTEC,        ADAPTEC_1030,   "ABA-1030 DVB receiver"),
        DEVICE( ADAPTEC2,       ADAPTEC2_2940U2,"AHA-2940U2"),
-       DEVICE( ADAPTEC2,       ADAPTEC2_78902, "AIC-7890/1"),
+       DEVICE( ADAPTEC2,       ADAPTEC2_2930U2,"AHA-2930U2"),
+       DEVICE( ADAPTEC2,       ADAPTEC2_7890B, "AIC-7890/1"),
        DEVICE( ADAPTEC2,       ADAPTEC2_7890,  "AIC-7890/1"),
        DEVICE( ADAPTEC2,       ADAPTEC2_3940U2,"AHA-3940U2"),
        DEVICE( ADAPTEC2,       ADAPTEC2_3950U2D,"AHA-3950U2D"),
        DEVICE( ADAPTEC2,       ADAPTEC2_7896,  "AIC-7896/7"),
+       DEVICE( ADAPTEC2,       ADAPTEC2_7892A, "AIC-7892"),
+       DEVICE( ADAPTEC2,       ADAPTEC2_7892B, "AIC-7892"),
+       DEVICE( ADAPTEC2,       ADAPTEC2_7892D, "AIC-7892"),
+       DEVICE( ADAPTEC2,       ADAPTEC2_7892P, "AIC-7892"),
+       DEVICE( ADAPTEC2,       ADAPTEC2_7899A, "AIC-7899"),
+       DEVICE( ADAPTEC2,       ADAPTEC2_7899B, "AIC-7899"),
+       DEVICE( ADAPTEC2,       ADAPTEC2_7899D, "AIC-7899"),
+       DEVICE( ADAPTEC2,       ADAPTEC2_7899P, "AIC-7899"),
        DEVICE( ATRONICS,       ATRONICS_2015,  "IDE-2015PL"),
        DEVICE( TIGERJET,       TIGERJET_300,   "Tiger300 ISDN"),
        DEVICE( ARK,            ARK_STING,      "Stingray"),
index c15bdcbab26ca218a7bdf8678a0056f50d5e1200..5882a2bb44724ac0aeeccb7c7398d9c30ddb5ce0 100644 (file)
@@ -12,4 +12,10 @@ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
   tristate 'Bidirectional parallel port support (EXPERIMENTAL)' CONFIG_SUN_BPP
   tristate 'Videopix Frame Grabber (EXPERIMENTAL)' CONFIG_SUN_VIDEOPIX
   tristate 'Aurora Multiboard 1600se (EXPERIMENTAL)' CONFIG_SUN_AURORA
+
+  # XXX Why don't we do "source drivers/char/Config.in" somewhere?
+  if [ "$CONFIG_PCI" = "y" ]; then
+    define_bool CONFIG_APM_RTC_IS_GMT y                # no shit
+    bool 'PC-style RTC' CONFIG_RTC
+  fi
 fi
index 6009cb677dda591d0ed8e03bf763e23007347e1f..607b0f230c7c3ff4e8741329765b20ea9676616c 100644 (file)
@@ -53,6 +53,7 @@
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/tqueue.h>
+#include <linux/delay.h>
 
 #include <asm/io.h>
 #include <asm/irq.h>
@@ -60,7 +61,6 @@
 #include <asm/system.h>
 #include <asm/segment.h>
 #include <asm/bitops.h>
-#include <asm/delay.h>
 #include <asm/kdebug.h>
 #include <asm/sbus.h>
 #include <asm/uaccess.h>
index 7f2bf172f02419770631a8abcde5b96d1eb23658..86914758ba0f83e87f814b89cc41b117a86c8087 100644 (file)
@@ -997,7 +997,7 @@ static void probeLptPort(unsigned idx)
       instances[idx].enhanced = 0;
       instances[idx].direction = 0;
       instances[idx].mode = COMPATIBILITY;
-      instances[idx].wait_queue = 0;
+      init_waitqueue_head(&instances[idx].wait_queue);
       instances[idx].run_length = 0;
       instances[idx].run_flag = 0;
       init_timer(&instances[idx].timer_list);
index 7905ceda02b0cdaf0eb8216c79629a542be9ece6..d5a15a61270ac318c862b15850264669ef433726 100644 (file)
@@ -1,8 +1,8 @@
-/* $Id: pcikbd.c,v 1.29 1999/05/16 13:47:53 ecd Exp $
+/* $Id: pcikbd.c,v 1.30 1999/06/03 15:02:36 davem Exp $
  * pcikbd.c: Ultra/AX PC keyboard support.
  *
  * Copyright (C) 1997  Eddie C. Dost  (ecd@skynet.be)
- * JavaStation(MrCoffee) support by Pete A. Zaitcev.
+ * JavaStation support by Pete A. Zaitcev.
  *
  * This code is mainly put together from various places in
  * drivers/char, please refer to these sources for credits
 #include <asm/io.h>
 #include <asm/uaccess.h>
 
-#ifdef __sparc_v9__
-#define        PCI_KB_NAME     "kb_ps2"
-#define PCI_MS_NAME    "kdmouse"
-#else
-#define PCI_KB_NAME    "keyboard"
-#define PCI_MS_NAME    "mouse"
-#endif
+/*
+ * Different platforms provide different permutations of names.
+ * AXi - kb_ps2, kdmouse.
+ * MrCoffee - keyboard, mouse.
+ * Espresso - keyboard, kdmouse.
+ */
+#define        PCI_KB_NAME1    "kb_ps2"
+#define PCI_KB_NAME2   "keyboard"
+#define PCI_MS_NAME1   "kdmouse"
+#define PCI_MS_NAME2   "mouse"
 
 #include "pcikbd.h"
 #include "sunserial.h"
@@ -502,7 +505,8 @@ __initfunc(void pcikbd_init_hw(void))
                        for_each_ebusdev(edev, ebus) {
                                if(!strcmp(edev->prom_name, "8042")) {
                                        for_each_edevchild(edev, child) {
-                                                if (!strcmp(child->prom_name, PCI_KB_NAME))
+                                                if (strcmp(child->prom_name, PCI_KB_NAME1) == 0 ||
+                                                   strcmp(child->prom_name, PCI_KB_NAME2) == 0)
                                                        goto found;
                                        }
                                }
@@ -513,12 +517,14 @@ __initfunc(void pcikbd_init_hw(void))
 
 found:
                pcikbd_iobase = child->base_address[0];
+#ifdef __sparc_v9__
                if (check_region(pcikbd_iobase, sizeof(unsigned long))) {
                        printk("8042: can't get region %lx, %d\n",
                               pcikbd_iobase, (int)sizeof(unsigned long));
                        return;
                }
                request_region(pcikbd_iobase, sizeof(unsigned long), "8042 controller");
+#endif
 
                pcikbd_irq = child->irqs[0];
                if (request_irq(pcikbd_irq, &pcikbd_interrupt,
@@ -548,7 +554,7 @@ ebus_done:
         * XXX: my 3.1.3 PROM does not give me the beeper node for the audio
         *      auxio register, though I know it is there... (ecd)
         *
-        * Both JE1 & MrCoffe have no beeper. How about Krups? --zaitcev
+        * JavaStations appear not to have beeper. --zaitcev
         */
        if (!edev)
                pcibeep_iobase = (pcikbd_iobase & ~(0xffffff)) | 0x722000;
@@ -575,7 +581,6 @@ ebus_done:
 }
 
 
-
 /*
  * Here begins the Mouse Driver.
  */
@@ -955,7 +960,8 @@ __initfunc(int pcimouse_init(void))
                        for_each_ebusdev(edev, ebus) {
                                if(!strcmp(edev->prom_name, "8042")) {
                                        for_each_edevchild(edev, child) {
-                                                       if (!strcmp(child->prom_name, PCI_MS_NAME))
+                                                       if (strcmp(child->prom_name, PCI_MS_NAME1) == 0 ||
+                                                           strcmp(child->prom_name, PCI_MS_NAME2) == 0)
                                                        goto found;
                                        }
                                }
@@ -1023,7 +1029,7 @@ found:
 
 __initfunc(int ps2kbd_probe(unsigned long *memory_start))
 {
-       int pnode, enode, node, dnode;
+       int pnode, enode, node, dnode, xnode;
        int kbnode = 0, msnode = 0, bnode = 0;
        int devices = 0;
        char prop[128];
@@ -1103,18 +1109,20 @@ __initfunc(int ps2kbd_probe(unsigned long *memory_start))
                         * For each '8042' on this EBus...
                         */
                        while (node) {
+                               dnode = prom_getchild(node);
+
                                /*
                                 * Does it match?
                                 */
-                               dnode = prom_getchild(node);
-                               dnode = prom_searchsiblings(dnode, PCI_KB_NAME);
-                               if (dnode == kbnode) {
+                               if ((xnode = prom_searchsiblings(dnode, PCI_KB_NAME1)) == kbnode) {
+                                       ++devices;
+                               } else if ((xnode = prom_searchsiblings(dnode, PCI_KB_NAME2)) == kbnode) {
                                        ++devices;
                                }
 
-                               dnode = prom_getchild(node);
-                               dnode = prom_searchsiblings(dnode, PCI_MS_NAME);
-                               if (dnode == msnode) {
+                               if ((xnode = prom_searchsiblings(dnode, PCI_MS_NAME1)) == msnode) {
+                                       ++devices;
+                               } else if ((xnode = prom_searchsiblings(dnode, PCI_MS_NAME2)) == msnode) {
                                        ++devices;
                                }
 
index 1576a4a0b167e87e4ff0eaeeb9b8117b41e0fc25..a86a53411b759583eac5f5217c1be335571b22d2 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: rtc.c,v 1.13 1998/08/26 10:29:44 davem Exp $
+/* $Id: rtc.c,v 1.14 1999/06/03 15:02:38 davem Exp $
  *
  * Linux/SPARC Real Time Clock Driver
  * Copyright (C) 1996 Thomas K. Dyas (tdyas@eden.rutgers.edu)
@@ -107,6 +107,7 @@ static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
 
 static int rtc_open(struct inode *inode, struct file *file)
 {
+
        if (rtc_busy)
                return -EBUSY;
 
@@ -144,14 +145,20 @@ EXPORT_NO_SYMBOLS;
 #ifdef MODULE
 int init_module(void)
 #else
-__initfunc(int rtc_init(void))
+__initfunc(int rtc_sun_init(void))
 #endif
 {
        int error;
 
+       if (mstk48t02_regs == 0) {
+               /* This diagnostic is a debugging aid... But a useful one. */
+               printk(KERN_ERR "rtc: no Mostek in this computer\n");
+               return -ENODEV;
+       }
+
        error = misc_register(&rtc_dev);
        if (error) {
-               printk(KERN_ERR "rtc: unable to get misc minor\n");
+               printk(KERN_ERR "rtc: unable to get misc minor for Mostek\n");
                return error;
        }
 
index 5c2d38f72c67378f9576615d0ae8dcc0b75e8d8b..a79dc6b6f6bdc964ba1721caccd036cbd855303c 100644 (file)
@@ -1,8 +1,8 @@
-/* $Id: su.c,v 1.19 1999/05/12 11:15:14 davem Exp $
+/* $Id: su.c,v 1.20 1999/06/03 15:02:40 davem Exp $
  * su.c: Small serial driver for keyboard/mouse interface on sparc32/PCI
  *
  * Copyright (C) 1997  Eddie C. Dost  (ecd@skynet.be)
- * Coypright (C) 1998  Pete Zaitcev   (zaitcev@metabyte.com)
+ * Copyright (C) 1998-1999  Pete Zaitcev   (zaitcev@metabyte.com)
  *
  * This is mainly a variation of drivers/char/serial.c,
  * credits go to authors mentioned therein.
@@ -92,6 +92,11 @@ static struct console sercons;
 int su_serial_console_init(void);
 #endif
 
+enum su_type { SU_PORT_NONE, SU_PORT_MS, SU_PORT_KBD, SU_PORT_PORT };
+static char *su_typev[] = { "???", "mouse", "kbd", "serial" };
+
+#define SU_PROPSIZE    128
+
 /*
  * serial.c saves memory when it allocates async_info upon first open.
  * We have parts of state structure together because we do call startup
@@ -107,8 +112,7 @@ struct su_struct {
        int              line;
        int              cflag;
 
-       int              kbd_node;
-       int              ms_node;
+       enum su_type     port_type;     /* Hookup type: e.g. mouse */
        int              port_node;
 
        char             name[16];
@@ -145,6 +149,18 @@ struct su_struct {
        unsigned long           last_active;    /* For async_struct, to be */
 };
 
+/*
+ * Scan status structure.
+ * "prop" is a local variable but it eats stack to keep it in each
+ * stack frame of a recursive procedure.
+ */
+struct su_probe_scan {
+       int msnode, kbnode;     /* PROM nodes for mouse and keyboard */
+       int msx, kbx;           /* minors for mouse and keyboard */
+       int devices;            /* scan index */
+       char prop[SU_PROPSIZE];
+};
+
 static char *serial_name = "PCIO serial driver";
 static char serial_version[16];
 
@@ -223,8 +239,6 @@ static inline int serial_paranoia_check(struct su_struct *info,
        return 0;
 }
 
-#ifdef __sparc_v9__
-
 static inline
 unsigned int su_inb(struct su_struct *info, unsigned long offset)
 {
@@ -234,20 +248,7 @@ unsigned int su_inb(struct su_struct *info, unsigned long offset)
 static inline void
 su_outb(struct su_struct *info, unsigned long offset, int value)
 {
-       outb(value, info->port + offset);
-}
-
-#else
-
-static inline
-unsigned int su_inb(struct su_struct *info, unsigned long offset)
-{
-       return (unsigned int)(*(volatile unsigned char *)(info->port + offset));
-}
-
-static inline void
-su_outb(struct su_struct *info, unsigned long offset, int value)
-{
+#ifndef __sparc_v9__
        /*
         * MrCoffee has weird schematics: IRQ4 & P10(?) pins of SuperIO are
         * connected with a gate then go to SlavIO. When IRQ4 goes tristated
@@ -257,10 +258,9 @@ su_outb(struct su_struct *info, unsigned long offset, int value)
         * This problem is similar to what Alpha people suffer, see serial.c.
         */
        if (offset == UART_MCR) value |= UART_MCR_OUT2;
-       *(volatile unsigned char *)(info->port + offset) = value;
-}
-
 #endif
+       outb(value, info->port + offset);
+}
 
 #define serial_in(info, off)           su_inb(info, off)
 #define serial_inp(info, off)          su_inb(info, off)
@@ -348,7 +348,7 @@ receive_kbd_ms_chars(struct su_struct *info, struct pt_regs *regs)
 
        do {
                ch = serial_inp(info, UART_RX);
-               if (info->kbd_node) {
+               if (info->port_type == SU_PORT_KBD) {
                        if(ch == SUNKBD_RESET) {
                                l1a_state.kbd_id = 1;
                                l1a_state.l1_down = 0;
@@ -529,7 +529,7 @@ check_modem_status(struct su_struct *info)
                            (status & UART_MSR_DCD))
                                hardpps();
 #endif
-       }
+               }
                if (status & UART_MSR_DCTS)
                        icount->cts++;
                wake_up_interruptible(&info->delta_msr_wait);
@@ -775,7 +775,7 @@ startup(struct su_struct *info)
        /*
         * Allocate the IRQ if necessary
         */
-       if (info->kbd_node || info->ms_node) {
+       if (info->port_type != SU_PORT_PORT) {
                retval = request_irq(info->irq, su_kbd_ms_interrupt,
                                     SA_SHIRQ, info->name, info);
        } else {
@@ -956,7 +956,7 @@ change_speed(struct su_struct *info,
        int             bits;
        unsigned long   flags;
 
-       if (!info->kbd_node && !info->ms_node) {
+       if (info->port_type == SU_PORT_PORT) {
                if (!info->tty || !info->tty->termios)
                        return;
                if (!info->port)
@@ -1133,9 +1133,9 @@ static void su_put_char_kbd(unsigned char c)
        struct su_struct *info = su_table;
        int lsr;
 
-       if (!info->kbd_node)
+       if (!info->port_type != SU_PORT_KBD)
                ++info;
-       if (!info)
+       if (!info->port_type != SU_PORT_KBD)
                return;
 
        do {
@@ -1151,9 +1151,9 @@ su_change_mouse_baud(int baud)
 {
        struct su_struct *info = su_table;
 
-       if (!info->ms_node)
+       if (!info->port_type != SU_PORT_MS)
                ++info;
-       if (!info)
+       if (!info->port_type != SU_PORT_MS)
                return;
 
        info->cflag &= ~(CBAUDEX | CBAUD);
@@ -2202,9 +2202,9 @@ done:
 
 /*
  * ---------------------------------------------------------------------
- * su_init() and friends
+ * su_XXX_init() and friends
  *
- * su_init() is called at boot-time to initialize the serial driver.
+ * su_XXX_init() is called at boot-time to initialize the serial driver.
  * ---------------------------------------------------------------------
  */
 
@@ -2215,7 +2215,7 @@ done:
  */
 __initfunc(static __inline__ void show_su_version(void))
 {
-       char *revision = "$Revision: 1.19 $";
+       char *revision = "$Revision: 1.20 $";
        char *version, *p;
 
        version = strchr(revision, ' ');
@@ -2226,8 +2226,8 @@ __initfunc(static __inline__ void show_su_version(void))
 }
 
 /*
- * This routine is called by su_init() to initialize a specific serial
- * port.  It determines what type of UART chip this serial port is
+ * This routine is called by su_{serial|kbd_ms}_init() to initialize a specific
+ * serial port.  It determines what type of UART chip this serial port is
  * using: 8250, 16450, 16550, 16550A.  The important question is
  * whether or not this UART is a 16550A, since this will determine
  * whether or not we can use its FIFO features.
@@ -2236,38 +2236,42 @@ static void
 autoconfig(struct su_struct *info)
 {
        unsigned char status1, status2, scratch, scratch2;
-#ifdef __sparc_v9__
        struct linux_ebus_device *dev = 0;
        struct linux_ebus *ebus;
-#else
+#ifndef __sparc_v9__
        struct linux_prom_registers reg0;
 #endif
        unsigned long flags;
 
-#ifdef __sparc_v9__
+       if (!info->port_node || !info->port_type)
+               return;
+
+       /*
+        * First we look for Ebus-bases su's
+        */
        for_each_ebus(ebus) {
                for_each_ebusdev(dev, ebus) {
-                       if (!strncmp(dev->prom_name, "su", 2)) {
-                               if (dev->prom_node == info->kbd_node)
-                                       goto ebus_done;
-                               if (dev->prom_node == info->ms_node)
-                                       goto ebus_done;
+                       if (dev->prom_node == info->port_node) {
+                               info->port = dev->base_address[0];
+#ifdef __sparc_v9__
+                               if (check_region(info->port, 8))
+                                       return;
+#endif
+                               info->irq = dev->irqs[0];
+                               goto ebus_done;
                        }
                }
        }
-ebus_done:
-       if (!dev)
-               return;
-
-       info->port = dev->base_address[0];
-       if (check_region(info->port, 8))
-               return;
 
-       info->irq = dev->irqs[0];
+#ifdef __sparc_v9__
+       /*
+        * Not on Ebus, bailing.
+        */
+       return;
 #else
-       if (!info->port_node)
-               return;
-
+       /*
+        * Not on Ebus, must be OBIO.
+        */
        if (prom_getproperty(info->port_node, "reg",
            (char *)&reg0, sizeof(reg0)) == -1) {
                prom_printf("su: no \"reg\" property\n");
@@ -2279,21 +2283,24 @@ ebus_done:
                prom_printf("su: cannot map\n");
                return;
        }
+
        /*
-        * There is no intr property on MrCoffee, so hardwire it. Krups?
+        * There is no intr property on MrCoffee, so hardwire it.
         */
        info->irq = IRQ_4M(13);
 #endif
 
-#ifdef DEBUG_SERIAL_OPEN
-       printk("Found 'su' at %016lx IRQ %s\n", dev->base_address[0],
-              __irq_itoa(dev->irqs[0]));
+ebus_done:
+
+#ifdef SERIAL_DEBUG_OPEN
+       printk("Found 'su' at %016lx IRQ %s\n", info->port,
+               __irq_itoa(info->irq));
 #endif
 
        info->magic = SERIAL_MAGIC;
 
        save_flags(flags); cli();
-       
+
        /*
         * Do a simple existence test first; if we fail this, there's
         * no point trying anything else.
@@ -2312,17 +2319,20 @@ ebus_done:
                return;         /* We failed; there's nothing here */
        }
 
-#if 0 /* P3: This does not work on MrCoffee. OUT2 is 0x80 - should work... */
        scratch = serial_inp(info, UART_MCR);
        serial_outp(info, UART_MCR, UART_MCR_LOOP | scratch);
        serial_outp(info, UART_MCR, UART_MCR_LOOP | 0x0A);
        status1 = serial_inp(info, UART_MSR) & 0xF0;
        serial_outp(info, UART_MCR, scratch);
        if (status1 != 0x90) {
+               /*
+                * This code fragment used to fail, now it fixed itself.
+                * We keep the printout for a case.
+                */
+               printk("su: loopback returned status 0x%02x\n", status1);
                restore_flags(flags);
                return;
        } 
-#endif
 
        scratch2 = serial_in(info, UART_LCR);
        serial_outp(info, UART_LCR, 0xBF);      /* set up for StarTech test */
@@ -2389,10 +2399,7 @@ ebus_done:
                return;
        }
 
-       if (info->kbd_node || info->ms_node)
-               sprintf(info->name, "su(%s)", info->ms_node ? "mouse" : "kbd");
-       else
-               strcpy(info->name, "su(serial)");
+       sprintf(info->name, "su(%s)", su_typev[info->port_type]);
 
 #ifdef __sparc_v9__
        request_region(info->port, 8, info->name);
@@ -2494,13 +2501,16 @@ __initfunc(int su_serial_init(void))
                info->tqueue.routine = do_softint;
                info->tqueue.data = info;
                info->cflag = serial_driver.init_termios.c_cflag;
+               init_waitqueue_head(&info->open_wait);
+               init_waitqueue_head(&info->close_wait);
+               init_waitqueue_head(&info->delta_msr_wait);
 
                autoconfig(info);
                if (info->type == PORT_UNKNOWN)
                        continue;
 
-               printk(KERN_INFO "%s at %16lx (irq = %s) is a %s\n",
-                      info->name, info->port, __irq_itoa(info->irq),
+               printk(KERN_INFO "%s at 0x%lx (tty %d irq %s) is a %s\n",
+                      info->name, (long)info->port, i, __irq_itoa(info->irq),
                       uart_config[info->type].name);
        }
 
@@ -2519,11 +2529,15 @@ __initfunc(int su_kbd_ms_init(void))
                info->type = PORT_UNKNOWN;
                info->baud_base = BAUD_BASE;
 
-               if (info->kbd_node)
+               if (info->port_type == SU_PORT_KBD)
                        info->cflag = B1200 | CS8 | CLOCAL | CREAD;
                else
                        info->cflag = B4800 | CS8 | CLOCAL | CREAD;
 
+               init_waitqueue_head(&info->open_wait);
+               init_waitqueue_head(&info->close_wait);
+               init_waitqueue_head(&info->delta_msr_wait);
+
                autoconfig(info);
                if (info->type == PORT_UNKNOWN)
                        continue;
@@ -2533,7 +2547,7 @@ __initfunc(int su_kbd_ms_init(void))
                       uart_config[info->type].name);
 
                startup(info);
-               if (info->kbd_node)
+               if (info->port_type == SU_PORT_KBD)
                        keyboard_zsinit(su_put_char_kbd);
                else
                        sun_mouse_zsinit();
@@ -2541,154 +2555,126 @@ __initfunc(int su_kbd_ms_init(void))
        return 0;
 }
 
-__initfunc(int su_probe (unsigned long *memory_start))
+/*
+ * We got several platforms which present 'su' in different parts
+ * of device tree. 'su' may be found under obio, ebus, isa and pci.
+ * We walk over the tree and find them wherever PROM hides them.
+ */
+__initfunc(void su_probe_any(struct su_probe_scan *t, int sunode))
 {
-       struct su_struct *info = su_table;
-        int node, enode, tnode, sunode;
-       int kbnode = 0, msnode = 0;
-       int devices = 0;
-       char prop[128];
+       struct su_struct *info;
        int len;
 
-       /*
-        * Find su on MrCoffee. We return OK code if find any.
-        * Then su_init finds every one and initializes them.
-        * We do this early because MrCoffee got no aliases.
-        */
-       node = prom_getchild(prom_root_node);
-       if ((node = prom_searchsiblings(node, "obio")) != 0) {
-               if ((sunode = prom_getchild(node)) != 0) {
-                       if ((sunode = prom_searchsiblings(sunode, "su")) != 0) {
-                               info->port_node = sunode;
-#ifdef CONFIG_SERIAL_CONSOLE
-                               /*
-                                * Console must be initiated after the generic
-                                * initialization.
-                                * sunserial_setinitfunc inverts order, so
-                                * call this before next one.
-                                */
-                               sunserial_setinitfunc(memory_start,
-                                                     su_serial_console_init);
-#endif
-                               sunserial_setinitfunc(memory_start,
-                                                     su_serial_init);
-                               return 0;
+       if (t->devices >= NR_PORTS) return;
+
+       for (; sunode != 0; sunode = prom_getsibling(sunode)) {
+               len = prom_getproperty(sunode, "name", t->prop, SU_PROPSIZE);
+               if (len <= 1) continue;         /* Broken PROM node */
+               if (strncmp(t->prop, "su", len) == 0 ||
+                   strncmp(t->prop, "serial", len) == 0 ||
+                   strncmp(t->prop, "su_pnp", len) == 0) {
+                       info = &su_table[t->devices];
+                       if (t->kbnode != 0 && sunode == t->kbnode) {
+                               t->kbx = t->devices;
+                               info->port_type = SU_PORT_KBD;
+                       } else if (t->msnode != 0 && sunode == t->msnode) {
+                               t->msx = t->devices;
+                               info->port_type = SU_PORT_MS;
+                       } else {
+                               info->port_type = SU_PORT_PORT;
                        }
+                       info->port_node = sunode;
+                       ++t->devices;
+               } else {
+                       su_probe_any(t, prom_getchild(sunode));
                }
        }
+}
+
+__initfunc(int su_probe (unsigned long *memory_start))
+{
+       int node;
+       int len;
+       struct su_probe_scan scan;
+
+       /*
+        * First, we scan the tree.
+        */
+       scan.devices = 0;
+       scan.msx = -1;
+       scan.kbx = -1;
+       scan.kbnode = 0;
+       scan.msnode = 0;
 
        /*
         * Get the nodes for keyboard and mouse from 'aliases'...
         */
         node = prom_getchild(prom_root_node);
        node = prom_searchsiblings(node, "aliases");
-       if (!node)
-               return -ENODEV;
+       if (node != 0) {
 
-       len = prom_getproperty(node, "keyboard", prop, sizeof(prop));
-       if (len > 0) {
-               prop[len] = 0;
-               kbnode = prom_finddevice(prop);
-       }
-       if (!kbnode)
-               return -ENODEV;
+               len = prom_getproperty(node, "keyboard", scan.prop,SU_PROPSIZE);
+               if (len > 0) {
+                       scan.prop[len] = 0;
+                       scan.kbnode = prom_finddevice(scan.prop);
+               }
 
-       len = prom_getproperty(node, "mouse", prop, sizeof(prop));
-       if (len > 0) {
-               prop[len] = 0;
-               msnode = prom_finddevice(prop);
+               len = prom_getproperty(node, "mouse", scan.prop, SU_PROPSIZE);
+               if (len > 0) {
+                       scan.prop[len] = 0;
+                       scan.msnode = prom_finddevice(scan.prop);
+               }
        }
-       if (!msnode)
-               return -ENODEV;
 
-       /*
-        * Find matching EBus nodes...
-        */
-        node = prom_getchild(prom_root_node);
-       if ((node = prom_searchsiblings(node, "pci")) == 0) {
-               return -ENODEV;         /* Plain sparc */
-       }
+       su_probe_any(&scan, prom_getchild(prom_root_node));
 
        /*
-        * Check for SUNW,sabre on Ultra 5/10/AXi.
+        * Second, we process the special case of keyboard and mouse.
+        *
+        * Currently if we got keyboard and mouse hooked to "su" ports
+        * we do not use any possible remaining "su" as a serial port.
+        * Thus, we ignore values of .msx and .kbx, then compact ports.
+        * Those who want to address this issue need to merge
+        * su_serial_init() and su_ms_kbd_init().
         */
-       len = prom_getproperty(node, "model", prop, sizeof(prop));
-       if ((len > 0) && !strncmp(prop, "SUNW,sabre", len)) {
-               node = prom_getchild(node);
-               node = prom_searchsiblings(node, "pci");
+       if (scan.msx != -1 && scan.kbx != -1) {
+               su_table[0].port_type = SU_PORT_MS;
+               su_table[0].port_node = scan.msnode;
+               su_table[1].port_type = SU_PORT_KBD;
+               su_table[1].port_node = scan.kbnode;
+
+               sunserial_setinitfunc(memory_start, su_kbd_ms_init);
+               rs_ops.rs_change_mouse_baud = su_change_mouse_baud;
+               sunkbd_setinitfunc(memory_start, sun_kbd_init);
+               kbd_ops.compute_shiftstate = sun_compute_shiftstate;
+               kbd_ops.setledstate = sun_setledstate;
+               kbd_ops.getledstate = sun_getledstate;
+               kbd_ops.setkeycode = sun_setkeycode;
+               kbd_ops.getkeycode = sun_getkeycode;
+#ifdef CONFIG_PCI
+               sunkbd_install_keymaps(memory_start, sun_key_maps,
+                   sun_keymap_count, sun_func_buf, sun_func_table,
+                   sun_funcbufsize, sun_funcbufleft,
+                   sun_accent_table, sun_accent_table_size);
+#endif
+               return 0;
        }
+       if (scan.msx != -1 || scan.kbx != -1) {
+               printk("su_probe: cannot match keyboard and mouse, confused\n");
+               return -ENODEV;
+       }
+
+       if (scan.devices == 0)
+               return -ENODEV;
 
+#ifdef CONFIG_SERIAL_CONSOLE
        /*
-        * For each PCI bus...
+        * Console must be initiated after the generic initialization.
+        * sunserial_setinitfunc inverts order, so call this before next one.
         */
-       while (node) {
-               enode = prom_getchild(node);
-               enode = prom_searchsiblings(enode, "ebus");
-
-               /*
-                * For each EBus on this PCI...
-                */
-               while (enode) {
-                       sunode = prom_getchild(enode);
-                       tnode = prom_searchsiblings(sunode, "su");
-                       if (!tnode)
-                               tnode = prom_searchsiblings(sunode, "su_pnp");
-                       sunode = tnode;
-
-                       /*
-                        * For each 'su' on this EBus...
-                        */
-                       while (sunode) {
-                               /*
-                                * Does it match?
-                                */
-                               if (sunode == kbnode) {
-                                       info->kbd_node = sunode;
-                                       ++info;
-                                       ++devices;
-                               }
-                               if (sunode == msnode) {
-                                       info->ms_node = sunode;
-                                       ++info;
-                                       ++devices;
-                               }
-
-                               /*
-                                * Found everything we need?
-                                */
-                               if (devices == 2)
-                                       goto found;
-
-                               sunode = prom_getsibling(sunode);
-                               tnode = prom_searchsiblings(sunode, "su");
-                               if (!tnode)
-                                       tnode = prom_searchsiblings(sunode,
-                                                                   "su_pnp");
-                               sunode = tnode;
-                       }
-                       enode = prom_getsibling(enode);
-                       enode = prom_searchsiblings(enode, "ebus");
-               }
-               node = prom_getsibling(node);
-               node = prom_searchsiblings(node, "pci");
-       }
-       return -ENODEV;
-
-found:
-        sunserial_setinitfunc(memory_start, su_kbd_ms_init);
-        rs_ops.rs_change_mouse_baud = su_change_mouse_baud;
-       sunkbd_setinitfunc(memory_start, sun_kbd_init);
-       kbd_ops.compute_shiftstate = sun_compute_shiftstate;
-       kbd_ops.setledstate = sun_setledstate;
-       kbd_ops.getledstate = sun_getledstate;
-       kbd_ops.setkeycode = sun_setkeycode;
-       kbd_ops.getkeycode = sun_getkeycode;
-#ifdef CONFIG_PCI
-       sunkbd_install_keymaps(memory_start, sun_key_maps, sun_keymap_count,
-                              sun_func_buf, sun_func_table,
-                              sun_funcbufsize, sun_funcbufleft,
-                              sun_accent_table, sun_accent_table_size);
+       sunserial_setinitfunc(memory_start, su_serial_console_init);
 #endif
+               sunserial_setinitfunc(memory_start, su_serial_init);
        return 0;
 }
 
index 9ebc20c7189842f02ecf3882a4f6905e2e20ca93..721fed188cbc17cab3b33aa1ab2bd15da6b0dd83 100644 (file)
@@ -129,9 +129,9 @@ int init_vfc_hw(struct vfc_dev *dev)
 int init_vfc_devstruct(struct vfc_dev *dev, int instance) 
 {
        dev->instance=instance;
-       dev->device_lock_sem=MUTEX;
+       init_MUTEX(&dev->device_lock_sem);
        dev->control_reg=0;
-       dev->poll_wait=NULL;
+       init_waitqueue_head(&dev->poll_wait);
        dev->busy=0;
        return 0;
 }
@@ -203,7 +203,6 @@ static int vfc_debug(struct vfc_dev *dev, int cmd, unsigned long arg)
 {
        struct vfc_debug_inout inout;
        unsigned char *buffer;
-       int ret;
 
        if(!capable(CAP_SYS_ADMIN)) return -EPERM;
 
index 531222b5c81a6845b815e8dfb5642d64aed2d5c7..e5c31c787e25ee8aeabb2b465d887f82e57d3836 100644 (file)
@@ -25,10 +25,8 @@ dep_tristate 'Adaptec AHA1542 support' CONFIG_SCSI_AHA1542 $CONFIG_SCSI
 dep_tristate 'Adaptec AHA1740 support' CONFIG_SCSI_AHA1740 $CONFIG_SCSI
 dep_tristate 'Adaptec AIC7xxx support' CONFIG_SCSI_AIC7XXX $CONFIG_SCSI
 if [ "$CONFIG_SCSI_AIC7XXX" != "n" ]; then
-    bool '   Override driver defaults for commands per LUN' CONFIG_OVERRIDE_CMDS N
-    if [ "$CONFIG_OVERRIDE_CMDS" != "n" ]; then
-      int  '   Maximum number of commands per LUN' CONFIG_AIC7XXX_CMDS_PER_LUN 24
-    fi
+    bool '   Enable Tagged Command Queueing (TCQ) by default' CONFIG_AIC7XXX_TCQ_ON_BY_DEFAULT
+    int  '   Maximum number of TCQ commands per device' CONFIG_AIC7XXX_CMDS_PER_DEVICE 8
     bool '   Collect statistics to report in /proc' CONFIG_AIC7XXX_PROC_STATS N
     int  '   Delay in seconds after SCSI bus reset' CONFIG_AIC7XXX_RESET_DELAY 5
 fi
index bbeeeb2f634fa267e72a555bfa026f75e399ccd7..0497bbb10f115f6af2f70bbc193059302d7aa75f 100644 (file)
@@ -17,6 +17,10 @@ linux-1.1.x and fairly stable since linux-1.2.x, and are also in FreeBSD
     AHA-274xT               
     AHA-2842
     AHA-2910B               
+    AHA-2920C
+    AHA-2930
+    AHA-2930U
+    AHA-2930U2
     AHA-2940               
     AHA-2940W              
     AHA-2940U              
@@ -77,8 +81,8 @@ linux-1.1.x and fairly stable since linux-1.2.x, and are also in FreeBSD
     Adaptec Cards
     ----------------------------
     AHA-2920 (Only the cards that use the Future Domain chipset are not
-              supported, any 2920 cards based on Adaptec AIC chipsets are
-             supported)
+              supported, any 2920 cards based on Adaptec AIC chipsets,
+             such as the 2920C, are supported)
     AAA-13x Raid Adapters
     AAA-113x Raid Port Card
 
@@ -108,7 +112,7 @@ linux-1.1.x and fairly stable since linux-1.2.x, and are also in FreeBSD
     Jess Johnson    jester@frenzy.com
       (AIC7xxx FAQ author)
     Doug Ledford    dledford@redhat.com
-      (Current Linux aic7xxx-5.x.x Driver/Patch/FTP/FAQ maintainer)
+      (Current Linux aic7xxx-5.x.x Driver/Patch/FTP maintainer)
     
     Special thanks go to John Aycock (aycock@cpsc.ucalgary.ca), the original
     author of the driver. John has since retired from the project. Thanks
@@ -325,11 +329,12 @@ linux-1.1.x and fairly stable since linux-1.2.x, and are also in FreeBSD
        list and someone can help you out.
 
     "aic7xxx=tag_info:{{8,8..},{8,8..},..}" - This option is used to disable
-        tagged queueing on specific devices.  As of driver version 5.1.8, we
-        now globally enable tagged queueing by default.  In order to
-        disable tagged queueing for certian devices at boot time, a user may
-        use this boot param.  The driver will then parse this message out
-        and disable the specific device entries that are present based upon
+        or enable Tagged Command Queueing (TCQ) on specific devices.  As of
+       driver version 5.1.11, TCQ is now either on or off by default
+       according to the setting you choose during the make config process.
+       In order to en/disable TCQ for certian devices at boot time, a user
+       may use this boot param.  The driver will then parse this message out
+        and en/disable the specific device entries that are present based upon
         the value given.  The param line is parsed in the following manner:
 
           { - first instance indicates the start of this parameter values
@@ -419,10 +424,10 @@ linux-1.1.x and fairly stable since linux-1.2.x, and are also in FreeBSD
     see this documentation, you need to use one of the advanced configuration
     programs (menuconfig and xconfig).  If you are using the "make menuconfig"
     method of configuring your kernel, then you would simply highlight the
-    option in question and hit the F1 key.  If you are using the "make xconfig"
-    method of configuring your kernel, then simply click on the help button next
-    to the option you have questions about.  The help information from the
-    Configure.help file will then get automatically displayed.
+    option in question and hit the ? key.  If you are using the "make xconfig"
+    method of configuring your kernel, then simply click on the help button
+    next to the option you have questions about.  The help information from
+    the Configure.help file will then get automatically displayed.
 
   /proc support
   ------------------------------
index e5907b67ae4e95104be344f99eebd9506bec98fd..df7fa30e63a8e23eac1d76ba1d546ed466d542df 100644 (file)
  * under normal conditions.
  */
 
-/*
- * AIC7XXX_FAKE_NEGOTIATION_CMDS
- *   We now have two distinctly different methods of device negotiation
- *   in this code.  The two methods are selected by either defining or not
- *   defining this option.  The difference is as follows:
- *
- *   With AIC7XXX_FAKE_NEGOTIATION_CMDS not set (commented out)
- *     When the driver is in need of issuing a negotiation command for any
- *     given device, it will add the negotiation message on to part of a
- *     regular SCSI command for the device.  In the process, if the device
- *     is configured for and using tagged queueing, then the code will
- *     also issue that single command as a non-tagged command, attach the
- *     negotiation message to that one command, and use a temporary
- *     queue depth of one to keep the untagged and tagged commands from
- *     overlapping.
- *       Pros: This doesn't use any extra SCB structures, it's simple, it
- *         works most of the time (if not all of the time now), and
- *         since we get the device capability info frmo the INQUIRY data
- *         now, shouldn't cause any problems.
- *       Cons: When we need to send a negotiation command to a device, we
- *         must use a command that is being sent to LUN 0 of the device.
- *         If we try sending one to high LUN numbers, then some devices
- *         get noticeably upset.  Since we have to wait for a command with
- *         LUN == 0 to come along, we may not be able to renegotiate when
- *         we want if the user is actually using say LUN 1 of a CD Changer
- *         instead of using LUN 0 for an extended period of time.
- *
- *   With AIC7XXX_FAKE_NEGOTIATION_CMDS defined
- *     When we need to negotiate with a device, instead of attaching our
- *     negotiation message to an existing command, we insert our own
- *     fictional Scsi_Cmnd into the chain that has the negotiation message
- *     attached to it.  We send this one command as untagged regardless
- *     of the device type, and we fiddle with the queue depth the same as
- *     we would with the option unset to avoid overlapping commands.  The
- *     primary difference between this and the unset option is that the
- *     negotiation message is no longer attached to a specific command,
- *     instead it is its own command and is merely triggered by a
- *     combination of both A) We need to negotiate and B) The mid level
- *     SCSI code has sent us a command.  We still don't do any negotiation
- *     unless there is a valid SCSI command to be processed.
- *       Pros: This fixes the problem above in the Cons section.  Since we
- *         issue our own fake command, we can set the LUN to 0 regardless
- *         of what the LUN is in the real command.  It also means that if
- *         the device get's nasty over negotiation issues, it won't be
- *         showing up on a regular command, so we won't get any SENSE buffer
- *         data or STATUS_BYTE returns to the mid level code that are caused
- *         by snits in the negotiation code.
- *       Cons: We add more code, and more complexity.  This means more ways
- *         in which things could break.  It means a larger driver.  It means
- *         more resource consumption for the fake commands.  However, the
- *         biggest problem is this.  Take a system where there is a CD-ROM
- *         on the SCSI bus.  Someone has a CD in the CD-ROM and is using it.
- *         For some reason the SCSI bus gets reset.  We don't touch the
- *         CD-ROM again for quite a period of time (so we don't renegotiate
- *         after the reset until we do touch the CD-ROM again).  In the
- *         time while we aren't using the CD-ROM, the current disc is
- *         removed and a new one put in.  When we go to check that disc, we
- *         will first have to renegotiate.  In so doing, we issue our fake
- *         SCSI command, which happens to be TEST_UNIT_READY.  The CD-ROM
- *         negotiates with us, then responds to our fake command with a
- *         CHECK_CONDITION status.  We REQUEST_SENSE from the CD-ROM, it
- *         then sends the SENSE data to our fake command to tell it that
- *         it has been through a disc change.  There, now we've cleared out
- *         the SENSE data along with our negotiation command, and when the
- *         real command executes, it won't pick up that the CD was changed.
- *         That's the biggest Con to this approach.  In the future, I could
- *         probably code around this problem though, so this option is still
- *         viable.
- *
- *  So, which command style should you use?  I would appreciate it if people
- *  could try out both types.  I want to know about any cases where one
- *  method works and the other doesn't.  If one method works on significantly
- *  more systems than another, then it will become the default.  If the second
- *  option turns out to work best, then I'll find a way to work around that
- *  big con I listed.
- *
- *  -- July 7, 02:33
- *    OK...I just added some code that should make the Con listed for the
- *    fake commands a non issue now.  However, it needs testing.  For now,
- *    I'm going to make the default to use the fake commands, we'll see how
- *    it goes.
- */
-#define AIC7XXX_FAKE_NEGOTIATION_CMDS
-
 /*
  * AIC7XXX_STRICT_PCI_SETUP
  *   Should we assume the PCI config options on our controllers are set with
 #include "aic7xxx/sequencer.h"
 #include "aic7xxx/scsi_message.h"
 #include "aic7xxx_reg.h"
+#include <scsi/scsicam.h>
 
 #include <linux/stat.h>
 #include <linux/malloc.h>        /* for kmalloc() */
@@ -354,7 +270,7 @@ struct proc_dir_entry proc_scsi_aic7xxx = {
     0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL
 };
 
-#define AIC7XXX_C_VERSION  "5.1.10"
+#define AIC7XXX_C_VERSION  "5.1.17"
 
 #define NUMBER(arr)     (sizeof(arr) / sizeof(arr[0]))
 #define MIN(a,b)        (((a) < (b)) ? (a) : (b))
@@ -447,10 +363,10 @@ struct proc_dir_entry proc_scsi_aic7xxx = {
  * You can try raising me if tagged queueing is enabled, or lowering
  * me if you only have 4 SCBs.
  */
-#ifdef CONFIG_AIC7XXX_CMDS_PER_LUN
-#define AIC7XXX_CMDS_PER_LUN CONFIG_AIC7XXX_CMDS_PER_LUN
+#ifdef CONFIG_AIC7XXX_CMDS_PER_DEVICE
+#define AIC7XXX_CMDS_PER_DEVICE CONFIG_AIC7XXX_CMDS_PER_DEVICE
 #else
-#define AIC7XXX_CMDS_PER_LUN 24
+#define AIC7XXX_CMDS_PER_DEVICE 8
 #endif
 
 /* Set this to the delay in seconds after SCSI bus reset. */
@@ -495,7 +411,7 @@ struct proc_dir_entry proc_scsi_aic7xxx = {
  *
  * *** Determining commands per LUN ***
  * 
- * When AIC7XXX_CMDS_PER_LUN is not defined, the driver will use its
+ * When AIC7XXX_CMDS_PER_DEVICE is not defined, the driver will use its
  * own algorithm to determine the commands/LUN.  If SCB paging is
  * enabled, which is always now, the default is 8 commands per lun
  * that indicates it supports tagged queueing.  All non-tagged devices
@@ -513,8 +429,13 @@ typedef struct
  * Make a define that will tell the driver not to use tagged queueing
  * by default.
  */
+#ifdef CONFIG_AIC7XXX_TCQ_ON_BY_DEFAULT
+#define DEFAULT_TAG_COMMANDS {0, 0, 0, 0, 0, 0, 0, 0,\
+                              0, 0, 0, 0, 0, 0, 0, 0}
+#else
 #define DEFAULT_TAG_COMMANDS {255, 255, 255, 255, 255, 255, 255, 255,\
                               255, 255, 255, 255, 255, 255, 255, 255}
+#endif
 
 /*
  * Modify this as you see fit for your system.  By setting tag_commands
@@ -553,6 +474,27 @@ adapter_tag_info_t aic7xxx_tag_info[] =
 };
 */
 
+static adapter_tag_info_t aic7xxx_tag_info[] =
+{
+  {DEFAULT_TAG_COMMANDS},
+  {DEFAULT_TAG_COMMANDS},
+  {DEFAULT_TAG_COMMANDS},
+  {DEFAULT_TAG_COMMANDS},
+  {DEFAULT_TAG_COMMANDS},
+  {DEFAULT_TAG_COMMANDS},
+  {DEFAULT_TAG_COMMANDS},
+  {DEFAULT_TAG_COMMANDS},
+  {DEFAULT_TAG_COMMANDS},
+  {DEFAULT_TAG_COMMANDS},
+  {DEFAULT_TAG_COMMANDS},
+  {DEFAULT_TAG_COMMANDS},
+  {DEFAULT_TAG_COMMANDS},
+  {DEFAULT_TAG_COMMANDS},
+  {DEFAULT_TAG_COMMANDS},
+  {DEFAULT_TAG_COMMANDS}
+};
+
+
 /*
  * Define an array of board names that can be indexed by aha_type.
  * Don't forget to change this when changing the types!
@@ -579,11 +521,14 @@ static const char *board_names[] = {
   "Adaptec AHA-2944 Ultra SCSI host adapter",           /* AIC_7884 */
   "Adaptec AIC-7895 Ultra SCSI host adapter",           /* AIC_7895 */
   "Adaptec AIC-7890/1 Ultra2 SCSI host adapter",        /* AIC_7890 */
+  "Adaptec AHA-293X Ultra2 SCSI host adapter",          /* AIC_7890 */
   "Adaptec AHA-294X Ultra2 SCSI host adapter",          /* AIC_7890 */
   "Adaptec AIC-7896/7 Ultra2 SCSI host adapter",        /* AIC_7896 */
   "Adaptec AHA-394X Ultra2 SCSI host adapter",          /* AIC_7897 */
   "Adaptec AHA-395X Ultra2 SCSI host adapter",          /* AIC_7897 */
   "Adaptec PCMCIA SCSI controller",                     /* card bus stuff */
+  "Adaptec AIC-7892 Ultra 160/m SCSI host adapter",     /* AIC_7892 */
+  "Adaptec AIC-7899 Ultra 160/m SCSI host adapter",     /* AIC_7899 */
 };
 
 /*
@@ -851,15 +796,17 @@ typedef enum {
         SCB_DEVICE_RESET        = 0x0020,
         SCB_RESET               = 0x0040,
         SCB_RECOVERY_SCB        = 0x0080,
-        SCB_WAS_BUSY            = 0x0100,
+        SCB_MSGOUT_PPR          = 0x0100,
         SCB_MSGOUT_SENT         = 0x0200,
         SCB_MSGOUT_SDTR         = 0x0400,
         SCB_MSGOUT_WDTR         = 0x0800,
-        SCB_MSGOUT_BITS         = SCB_MSGOUT_SENT | 
+        SCB_MSGOUT_BITS         = SCB_MSGOUT_PPR |
+                                  SCB_MSGOUT_SENT | 
                                   SCB_MSGOUT_SDTR |
                                   SCB_MSGOUT_WDTR,
         SCB_QUEUED_ABORT        = 0x1000,
-        SCB_QUEUED_FOR_DONE     = 0x2000
+        SCB_QUEUED_FOR_DONE     = 0x2000,
+        SCB_WAS_BUSY            = 0x4000
 } scb_flag_type;
 
 typedef enum {
@@ -913,6 +860,8 @@ typedef enum {
   AHC_AIC7890          = 0x0006,
   AHC_AIC7895          = 0x0007,
   AHC_AIC7896          = 0x0008,
+  AHC_AIC7892          = 0x0009,
+  AHC_AIC7899          = 0x000a,
   AHC_VL               = 0x0100,
   AHC_EISA             = 0x0200,
   AHC_PCI              = 0x0400,
@@ -929,6 +878,7 @@ typedef enum {
   AHC_QUEUE_REGS       = 0x0040,
   AHC_SG_PRELOAD       = 0x0080,
   AHC_SPIOCAP          = 0x0100,
+  AHC_ULTRA3           = 0x0200,
   AHC_AIC7770_FE       = AHC_FENONE,
   AHC_AIC7850_FE       = AHC_SPIOCAP,
   AHC_AIC7860_FE       = AHC_ULTRA|AHC_SPIOCAP,
@@ -938,6 +888,8 @@ typedef enum {
                          AHC_QUEUE_REGS|AHC_SG_PRELOAD,
   AHC_AIC7895_FE       = AHC_MORE_SRAM|AHC_CMD_CHAN|AHC_ULTRA,
   AHC_AIC7896_FE       = AHC_AIC7890_FE,
+  AHC_AIC7892_FE       = AHC_AIC7890_FE|AHC_ULTRA3,
+  AHC_AIC7899_FE       = AHC_AIC7890_FE|AHC_ULTRA3,
 } ahc_feature;
 
 struct aic7xxx_scb {
@@ -1014,9 +966,12 @@ typedef struct {
   unsigned char goal_period;
   unsigned char cur_offset;
   unsigned char goal_offset;
+  unsigned char cur_options;
+  unsigned char goal_options;
   unsigned char user_width;
   unsigned char user_period;
   unsigned char user_offset;
+  unsigned char user_options;
 } transinfo_type;
 
 /*
@@ -1045,10 +1000,11 @@ struct aic7xxx_host {
   unsigned long            isr_count;        /* Interrupt count */
   unsigned long            spurious_int;
   scb_data_type           *scb_data;
+  volatile unsigned short  needdv;
+  volatile unsigned short  needppr;
   volatile unsigned short  needsdtr;
-  volatile unsigned short  sdtr_pending;
   volatile unsigned short  needwdtr;
-  volatile unsigned short  wdtr_pending;
+  volatile unsigned short  dtr_pending;
   struct aic7xxx_cmd_queue {
     Scsi_Cmnd *head;
     Scsi_Cmnd *tail;
@@ -1071,9 +1027,10 @@ struct aic7xxx_host {
 #define  DEVICE_PRESENT                 0x01
 #define  BUS_DEVICE_RESET_PENDING       0x02
 #define  DEVICE_RESET_DELAY             0x04
-#define  DEVICE_PRINT_SDTR              0x08
-#define  DEVICE_PRINT_WDTR              0x10
+#define  DEVICE_PRINT_DTR               0x08
+#define  DEVICE_PARITY_ERROR            0x10
 #define  DEVICE_WAS_BUSY                0x20
+#define  DEVICE_SCSI_3                  0x40
 #define  DEVICE_SCANNED                 0x80
   volatile unsigned char   dev_flags[MAX_TARGETS];
   volatile unsigned char   dev_active_cmds[MAX_TARGETS];
@@ -1090,11 +1047,10 @@ struct aic7xxx_host {
 #endif
 
 
-#ifdef AIC7XXX_FAKE_NEGOTIATION_CMDS
-  Scsi_Cmnd               *dev_wdtr_cmnd[MAX_TARGETS];
-  Scsi_Cmnd               *dev_sdtr_cmnd[MAX_TARGETS];
-#endif
+  Scsi_Cmnd               *dev_dtr_cmnd[MAX_TARGETS];
 
+  unsigned int             dev_checksum[MAX_TARGETS];
+  
   unsigned char            dev_last_queue_full[MAX_TARGETS];
   unsigned char            dev_last_queue_full_count[MAX_TARGETS];
   unsigned char            dev_max_queue_depth[MAX_TARGETS];
@@ -1102,7 +1058,7 @@ struct aic7xxx_host {
   volatile scb_queue_type  delayed_scbs[MAX_TARGETS];
 
 
-  unsigned char            msg_buf[9];       /* The message for the target */
+  unsigned char            msg_buf[13];      /* The message for the target */
   unsigned char            msg_type;
 #define MSG_TYPE_NONE              0x00
 #define MSG_TYPE_INITIATOR_MSGOUT  0x01
@@ -1132,6 +1088,7 @@ struct aic7xxx_host {
   int                      scsi_id_b;        /* channel B for twin adapters */
   unsigned int             bios_address;
   int                      board_name_index;
+  unsigned short           needppr_copy;     /* default config */
   unsigned short           needsdtr_copy;    /* default config */
   unsigned short           needwdtr_copy;    /* default config */
   unsigned short           ultraenb;         /* Ultra mode target list */
@@ -1192,9 +1149,12 @@ struct aic7xxx_host {
  * Provides a mapping of transfer periods in ns/4 to the proper value to
  * stick in the SCSIRATE reg to use that transfer rate.
  */
-#define AHC_SYNCRATE_ULTRA2 0
-#define AHC_SYNCRATE_ULTRA  2
-#define AHC_SYNCRATE_FAST   5
+#define AHC_SYNCRATE_ULTRA3 0
+#define AHC_SYNCRATE_ULTRA2 1
+#define AHC_SYNCRATE_ULTRA  3
+#define AHC_SYNCRATE_FAST   6
+#define AHC_SYNCRATE_CRC 0x40
+#define AHC_SYNCRATE_SE  0x10
 static struct aic7xxx_syncrate {
   /* Rates in Ultra mode have bit 8 of sxfr set */
 #define                ULTRA_SXFR 0x100
@@ -1203,6 +1163,7 @@ static struct aic7xxx_syncrate {
   unsigned char period;
   const char *rate[2];
 } aic7xxx_syncrates[] = {
+  { 0x42,  0x000,   9,  {"80.0", "160.0"} },
   { 0x13,  0x000,  10,  {"40.0", "80.0"} },
   { 0x14,  0x000,  11,  {"33.0", "66.6"} },
   { 0x15,  0x100,  12,  {"20.0", "40.0"} },
@@ -1410,35 +1371,6 @@ static char dummy_buffer[60] = "Please don't trounce on me insmod!!\n";
 
 #endif
 
-/*
- * See the comments earlier in the file for what this item is all about
- * If you have more than 4 controllers, you will need to increase the
- * the number of items in the array below.  Additionally, if you don't
- * want to have lilo pass a humongous config line to the aic7xxx driver,
- * then you can get in and manually adjust these instead of leaving them
- * at the default.  Pay attention to the comments earlier in this file
- * concerning this array if you are going to hand modify these values.
- */
-static adapter_tag_info_t aic7xxx_tag_info[] =
-{
-  {DEFAULT_TAG_COMMANDS},
-  {DEFAULT_TAG_COMMANDS},
-  {DEFAULT_TAG_COMMANDS},
-  {DEFAULT_TAG_COMMANDS},
-  {DEFAULT_TAG_COMMANDS},
-  {DEFAULT_TAG_COMMANDS},
-  {DEFAULT_TAG_COMMANDS},
-  {DEFAULT_TAG_COMMANDS},
-  {DEFAULT_TAG_COMMANDS},
-  {DEFAULT_TAG_COMMANDS},
-  {DEFAULT_TAG_COMMANDS},
-  {DEFAULT_TAG_COMMANDS},
-  {DEFAULT_TAG_COMMANDS},
-  {DEFAULT_TAG_COMMANDS},
-  {DEFAULT_TAG_COMMANDS},
-  {DEFAULT_TAG_COMMANDS}
-};
-
 #define VERBOSE_NORMAL         0x0000
 #define VERBOSE_NEGOTIATION    0x0001
 #define VERBOSE_SEQINT         0x0002
@@ -1920,6 +1852,7 @@ aic7xxx_download_instr(struct aic7xxx_host *p, int instrptr,
       aic_outb(p, ((instr.integer >> 8) & 0xff), SEQRAM);
       aic_outb(p, ((instr.integer >> 16) & 0xff), SEQRAM);
       aic_outb(p, ((instr.integer >> 24) & 0xff), SEQRAM);
+      udelay(15);
       break;
 
     default:
@@ -2084,28 +2017,101 @@ aic7xxx_info(struct Scsi_Host *dooh)
  *-F*************************************************************************/
 static struct aic7xxx_syncrate *
 aic7xxx_find_syncrate(struct aic7xxx_host *p, unsigned int *period,
-  unsigned int maxsync)
+  unsigned int maxsync, unsigned char *options)
 {
   struct aic7xxx_syncrate *syncrate;
+  int done = FALSE;
 
+  switch(*options)
+  {
+    case MSG_EXT_PPR_OPTION_DT_CRC:
+    case MSG_EXT_PPR_OPTION_DT_UNITS:
+      if(!(p->features & AHC_ULTRA3))
+      {
+        *options = 0;
+        maxsync = MAX(maxsync, AHC_SYNCRATE_ULTRA2);
+      }
+      break;
+    case MSG_EXT_PPR_OPTION_DT_CRC_QUICK:
+    case MSG_EXT_PPR_OPTION_DT_UNITS_QUICK:
+      if(!(p->features & AHC_ULTRA3))
+      {
+        *options = 0;
+        maxsync = MAX(maxsync, AHC_SYNCRATE_ULTRA2);
+      }
+      else
+      {
+        /*
+         * we don't support the Quick Arbitration variants of dual edge
+         * clocking.  As it turns out, we want to send back the
+         * same basic option, but without the QA attribute.
+         * We know that we are responding because we would never set
+         * these options ourself, we would only respond to them.
+         */
+        switch(*options)
+        {
+          case MSG_EXT_PPR_OPTION_DT_CRC_QUICK:
+            *options = MSG_EXT_PPR_OPTION_DT_CRC;
+            break;
+          case MSG_EXT_PPR_OPTION_DT_UNITS_QUICK:
+            *options = MSG_EXT_PPR_OPTION_DT_UNITS;
+            break;
+        }
+      }
+      break;
+    default:
+      *options = 0;
+      maxsync = MAX(maxsync, AHC_SYNCRATE_ULTRA2);
+      break;
+  }
   syncrate = &aic7xxx_syncrates[maxsync];
   while ( (syncrate->rate[0] != NULL) &&
          (!(p->features & AHC_ULTRA2) || syncrate->sxfr_ultra2) )
   {
-    if ( *period <= syncrate->period )
+    if (*period <= syncrate->period) 
     {
-      /*
-       * When responding to a target that requests sync, the requested rate
-       * may fall between two rates that we can output, but still be a rate
-       * that we can receive.  Because of this, we want to respond with the
-       * same rate that it sent to us even if the persiod we use to send
-       * data to it is lower.  Only lower the response period if we must.
-       */
-      if(syncrate == &aic7xxx_syncrates[maxsync])
+      switch(*options)
+      {
+        case MSG_EXT_PPR_OPTION_DT_CRC:
+        case MSG_EXT_PPR_OPTION_DT_UNITS:
+          if(!(syncrate->sxfr_ultra2 & AHC_SYNCRATE_CRC))
+          {
+            done = TRUE;
+            /*
+             * oops, we went too low for the CRC/DualEdge signalling, so
+             * clear the options byte
+             */
+            *options = 0;
+            /*
+             * We'll be sending a reply to this packet to set the options
+             * properly, so unilaterally set the period as well.
+             */
+            *period = syncrate->period;
+          }
+          else
+          {
+            done = TRUE;
+            if(syncrate == &aic7xxx_syncrates[maxsync])
+            {
+              *period = syncrate->period;
+            }
+          }
+          break;
+        default:
+          if(!(syncrate->sxfr_ultra2 & AHC_SYNCRATE_CRC))
+          {
+            done = TRUE;
+            if(syncrate == &aic7xxx_syncrates[maxsync])
+            {
+              *period = syncrate->period;
+            }
+          }
+          break;
+      }
+      if(done)
       {
-        *period = syncrate->period;
+        break;
       }
-      break;
     }
     syncrate++;
   }
@@ -2115,6 +2121,7 @@ aic7xxx_find_syncrate(struct aic7xxx_host *p, unsigned int *period,
     /*
      * Use async transfers for this target
      */
+    *options = 0;
     *period = 0;
     syncrate = NULL;
   }
@@ -2135,7 +2142,7 @@ aic7xxx_find_period(struct aic7xxx_host *p, unsigned int scsirate,
 {
   struct aic7xxx_syncrate *syncrate;
 
-  if ((p->features & AHC_ULTRA2) != 0)
+  if (p->features & AHC_ULTRA2)
   {
     scsirate &= SXFR_ULTRA2;
   }
@@ -2147,12 +2154,14 @@ aic7xxx_find_period(struct aic7xxx_host *p, unsigned int scsirate,
   syncrate = &aic7xxx_syncrates[maxsync];
   while (syncrate->rate[0] != NULL)
   {
-    if ((p->features & AHC_ULTRA2) != 0)
+    if (p->features & AHC_ULTRA2)
     {
       if (syncrate->sxfr_ultra2 == 0)
         break;
       else if (scsirate == syncrate->sxfr_ultra2)
         return (syncrate->period);
+      else if (scsirate == (syncrate->sxfr_ultra2 & ~AHC_SYNCRATE_CRC))
+        return (syncrate->period);
     }
     else if (scsirate == (syncrate->sxfr & ~ULTRA_SXFR))
     {
@@ -2206,11 +2215,11 @@ aic7xxx_validate_offset(struct aic7xxx_host *p,
 static void
 aic7xxx_set_syncrate(struct aic7xxx_host *p, struct aic7xxx_syncrate *syncrate,
     int target, int channel, unsigned int period, unsigned int offset,
-    unsigned int type)
+    unsigned char options, unsigned int type)
 {
   unsigned char tindex;
   unsigned short target_mask;
-  unsigned char lun;
+  unsigned char lun, old_options;
   unsigned int old_period, old_offset;
 
   tindex = target | (channel << 3);
@@ -2225,6 +2234,7 @@ aic7xxx_set_syncrate(struct aic7xxx_host *p, struct aic7xxx_syncrate *syncrate,
 
   old_period = p->transinfo[tindex].cur_period;
   old_offset = p->transinfo[tindex].cur_offset;
+  old_options = p->transinfo[tindex].cur_options;
 
   
   if (type & AHC_TRANS_CUR)
@@ -2237,7 +2247,18 @@ aic7xxx_set_syncrate(struct aic7xxx_host *p, struct aic7xxx_syncrate *syncrate,
       scsirate &= ~SXFR_ULTRA2;
       if (syncrate != NULL)
       {
-        scsirate |= syncrate->sxfr_ultra2;
+        switch(options)
+        {
+          case MSG_EXT_PPR_OPTION_DT_UNITS:
+            /*
+             * mask off the CRC bit in the xfer settings
+             */
+            scsirate |= (syncrate->sxfr_ultra2 & ~AHC_SYNCRATE_CRC);
+            break;
+          default:
+            scsirate |= syncrate->sxfr_ultra2;
+            break;
+        }
       }
       if (type & AHC_TRANS_ACTIVE)
       {
@@ -2278,9 +2299,10 @@ aic7xxx_set_syncrate(struct aic7xxx_host *p, struct aic7xxx_syncrate *syncrate,
     aic_outb(p, scsirate, TARG_SCSIRATE + tindex);
     p->transinfo[tindex].cur_period = period;
     p->transinfo[tindex].cur_offset = offset;
+    p->transinfo[tindex].cur_options = options;
     if ( !(type & AHC_TRANS_QUITE) &&
          (aic7xxx_verbose & VERBOSE_NEGOTIATION) &&
-         (p->dev_flags[tindex] & DEVICE_PRINT_SDTR) )
+         (p->dev_flags[tindex] & DEVICE_PRINT_DTR) )
     {
       if (offset)
       {
@@ -2295,7 +2317,7 @@ aic7xxx_set_syncrate(struct aic7xxx_host *p, struct aic7xxx_syncrate *syncrate,
         printk(INFO_LEAD "Using asynchronous transfers.\n",
                p->host_no, channel, target, lun);
       }
-      p->dev_flags[tindex] &= ~DEVICE_PRINT_SDTR;
+      p->dev_flags[tindex] &= ~DEVICE_PRINT_DTR;
     }
   }
 
@@ -2303,12 +2325,14 @@ aic7xxx_set_syncrate(struct aic7xxx_host *p, struct aic7xxx_syncrate *syncrate,
   {
     p->transinfo[tindex].goal_period = period;
     p->transinfo[tindex].goal_offset = offset;
+    p->transinfo[tindex].goal_options = options;
   }
 
   if (type & AHC_TRANS_USER)
   {
     p->transinfo[tindex].user_period = period;
     p->transinfo[tindex].user_offset = offset;
+    p->transinfo[tindex].user_options = options;
   }
 }
 
@@ -2325,20 +2349,13 @@ aic7xxx_set_width(struct aic7xxx_host *p, int target, int channel, int lun,
 {
   unsigned char tindex;
   unsigned short target_mask;
-  unsigned int old_width, new_offset;
+  unsigned int old_width;
 
   tindex = target | (channel << 3);
   target_mask = 1 << tindex;
   
   old_width = p->transinfo[tindex].cur_width;
 
-  if (p->features & AHC_ULTRA2)
-    new_offset = MAX_OFFSET_ULTRA2;
-  else if (width == MSG_EXT_WDTR_BUS_16_BIT)
-    new_offset = MAX_OFFSET_16BIT;
-  else
-    new_offset = MAX_OFFSET_8BIT;
-  
   if (type & AHC_TRANS_CUR) 
   {
     unsigned char scsirate;
@@ -2356,12 +2373,12 @@ aic7xxx_set_width(struct aic7xxx_host *p, int target, int channel, int lun,
 
     p->transinfo[tindex].cur_width = width;
 
-    if ((aic7xxx_verbose & VERBOSE_NEGOTIATION2) && 
-        (p->dev_flags[tindex] & DEVICE_PRINT_WDTR))
+    if ( !(type & AHC_TRANS_QUITE) &&
+          (aic7xxx_verbose & VERBOSE_NEGOTIATION2) && 
+          (p->dev_flags[tindex] & DEVICE_PRINT_DTR) )
     {
       printk(INFO_LEAD "Using %s transfers\n", p->host_no, channel, target,
         lun, (scsirate & WIDEXFER) ? "Wide(16bit)" : "Narrow(8bit)" );
-      p->dev_flags[tindex] &= ~DEVICE_PRINT_WDTR;
     }
   }
 
@@ -2370,14 +2387,21 @@ aic7xxx_set_width(struct aic7xxx_host *p, int target, int channel, int lun,
   if (type & AHC_TRANS_USER)
     p->transinfo[tindex].user_width = width;
 
-  /*
-   * Having just set the width, the SDTR should come next, and we need a valid
-   * offset for the SDTR.  So, we make sure we put a valid one in here now as
-   * the goal_offset.
-   */
   if (p->transinfo[tindex].goal_offset)
-    p->transinfo[tindex].goal_offset = new_offset;
-
+  {
+    if (p->features & AHC_ULTRA2)
+    {
+      p->transinfo[tindex].goal_offset = MAX_OFFSET_ULTRA2;
+    }
+    else if (width == MSG_EXT_WDTR_BUS_16_BIT)
+    {
+      p->transinfo[tindex].goal_offset = MAX_OFFSET_16BIT;
+    }
+    else
+    {
+      p->transinfo[tindex].goal_offset = MAX_OFFSET_8BIT;
+    }
+  }
 }
       
 /*+F*************************************************************************
@@ -2543,14 +2567,6 @@ aic7xxx_match_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb,
   if (match != 0)
     match = ((tag == scb->hscb->tag) || (tag == SCB_LIST_NULL));
 
-  if (aic7xxx_verbose & (VERBOSE_ABORT_PROCESS | VERBOSE_RESET_PROCESS))
-  {
-    printk(KERN_INFO "(scsi%d:%d:%d:%d:tag%d) %s search criteria"
-      " (scsi%d:%d:%d:%d:tag%d)\n", p->host_no, CTL_OF_SCB(scb),
-      scb->hscb->tag, (match) ? "matches" : "doesn't match",
-      p->host_no, channel, target, lun, tag);
-  }
-
   return (match);
 }
 
@@ -2584,14 +2600,13 @@ aic7xxx_add_curscb_to_free_list(struct aic7xxx_host *p)
  *   to the free list.
  *-F*************************************************************************/
 static unsigned char
-aic7xxx_rem_scb_from_disc_list(struct aic7xxx_host *p, unsigned char scbptr)
+aic7xxx_rem_scb_from_disc_list(struct aic7xxx_host *p, unsigned char scbptr,
+                               unsigned char prev)
 {
   unsigned char next;
-  unsigned char prev;
 
   aic_outb(p, scbptr, SCBPTR);
   next = aic_inb(p, SCB_NEXT);
-  prev = aic_inb(p, SCB_PREV);
   aic7xxx_add_curscb_to_free_list(p);
 
   if (prev != SCB_LIST_NULL)
@@ -2604,11 +2619,6 @@ aic7xxx_rem_scb_from_disc_list(struct aic7xxx_host *p, unsigned char scbptr)
     aic_outb(p, next, DISCONNECTED_SCBH);
   }
 
-  if (next != SCB_LIST_NULL)
-  {
-    aic_outb(p, next, SCBPTR);
-    aic_outb(p, prev, SCB_PREV);
-  }
   return next;
 }
 
@@ -2755,7 +2765,7 @@ aic7xxx_allocate_scb(struct aic7xxx_host *p)
          * Place in the scb array; never is removed
          */
         p->scb_data->scb_array[p->scb_data->numscbs++] = scbp;
-        scbq_insert_head(&p->scb_data->free_scbs, scbp);
+        scbq_insert_tail(&p->scb_data->free_scbs, scbp);
       }
       scbp->kmalloc_ptr = scb_ap;
     }
@@ -2796,6 +2806,7 @@ aic7xxx_done_cmds_complete(struct aic7xxx_host *p)
   Scsi_Cmnd *cmd;
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,95)
   unsigned int cpu_flags = 0;
+#endif
   
   DRIVER_LOCK
   while (p->completeq.head != NULL)
@@ -2803,20 +2814,9 @@ aic7xxx_done_cmds_complete(struct aic7xxx_host *p)
     cmd = p->completeq.head;
     p->completeq.head = (Scsi_Cmnd *)cmd->host_scribble;
     cmd->host_scribble = NULL;
-    sti();
     cmd->scsi_done(cmd);
-    cli();
   }
   DRIVER_UNLOCK
-#else
-  while (p->completeq.head != NULL)
-  {
-    cmd = p->completeq.head;
-    p->completeq.head = (Scsi_Cmnd *)cmd->host_scribble;
-    cmd->host_scribble = NULL;
-    cmd->scsi_done(cmd);
-  }
-#endif
 }
 
 /*+F*************************************************************************
@@ -2889,16 +2889,13 @@ aic7xxx_done(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
       }
 #define WIDE_INQUIRY_BITS 0x60
 #define SYNC_INQUIRY_BITS 0x10
+#define SCSI_VERSION_BITS 0x07
       if ( (buffer[7] & WIDE_INQUIRY_BITS) &&
            (p->features & AHC_WIDE) )
       {
         p->needwdtr |= (1<<tindex);
         p->needwdtr_copy |= (1<<tindex);
-        if ( (p->flags & AHC_SEEPROM_FOUND) &&
-             (p->transinfo[tindex].user_width != MSG_EXT_WDTR_BUS_16_BIT) )
-          p->transinfo[tindex].goal_width = MSG_EXT_WDTR_BUS_8_BIT;
-        else
-          p->transinfo[tindex].goal_width = MSG_EXT_WDTR_BUS_16_BIT;
+        p->transinfo[tindex].goal_width = p->transinfo[tindex].user_width;
       }
       else
       {
@@ -2916,28 +2913,10 @@ aic7xxx_done(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
         p->needsdtr |= (1<<tindex);
         p->needsdtr_copy |= (1<<tindex);
 
-        if (p->flags & AHC_SEEPROM_FOUND)
+        p->transinfo[tindex].goal_period = p->transinfo[tindex].user_period;
+        p->transinfo[tindex].goal_options = p->transinfo[tindex].user_options;
+        if (p->transinfo[tindex].user_offset)
         {
-          p->transinfo[tindex].goal_period = p->transinfo[tindex].user_period;
-          p->transinfo[tindex].goal_offset = p->transinfo[tindex].user_offset;
-        }
-        else
-        {
-          if (p->features & AHC_ULTRA2)
-          {
-            p->transinfo[tindex].goal_period =
-              aic7xxx_syncrates[AHC_SYNCRATE_ULTRA2].period;
-          }
-          else if (p->features & AHC_ULTRA)
-          {
-            p->transinfo[tindex].goal_period =
-              aic7xxx_syncrates[AHC_SYNCRATE_ULTRA].period;
-          }
-          else
-          {
-            p->transinfo[tindex].goal_period =
-              aic7xxx_syncrates[AHC_SYNCRATE_FAST].period;
-          }
           if (p->features & AHC_ULTRA2)
             p->transinfo[tindex].goal_offset = MAX_OFFSET_ULTRA2;
           else if (p->transinfo[tindex].goal_width == MSG_EXT_WDTR_BUS_16_BIT)
@@ -2952,14 +2931,57 @@ aic7xxx_done(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
         p->needsdtr_copy &= ~(1<<tindex);
         p->transinfo[tindex].goal_period = 0;
         p->transinfo[tindex].goal_offset = 0;
+        p->transinfo[tindex].goal_options = 0;
+      }
+      if ( (buffer[2] & SCSI_VERSION_BITS) == 3 )
+      {
+        p->dev_flags[tindex] |= DEVICE_SCSI_3;
+        /*
+         * OK, we are a SCSI 3 device and we are in need of negotiation.
+         * Use PPR messages instead of WDTR and SDTR messages.
+         */
+        if ( (p->needsdtr & (1<<tindex)) ||
+             (p->needwdtr & (1<<tindex)) )
+        {
+          p->needppr |= (1<<tindex);
+          p->needppr_copy |= (1<<tindex);
+        }
+        p->needwdtr &= ~(1<<tindex);
+        p->needwdtr_copy &= ~(1<<tindex);
+        p->needsdtr &= ~(1<<tindex);
+        p->needsdtr_copy &= ~(1<<tindex);
+      }
+      /*
+       * Get the INQUIRY checksum.  We use this on Ultra 160/m
+       * and older devices both.  It allows us to drop speed on any bus type
+       * while at the same time giving us the needed domain validation for
+       * Ultra 160/m
+       *
+       * Note: We only get the checksum and set the SCANNED bit if this is
+       * one of our dtr commands.  If we don't do this, then we end up
+       * getting bad checksum results on the mid-level SCSI code's INQUIRY
+       * commands.
+       */
+      if(p->dev_dtr_cmnd[tindex] == cmd) {
+        unsigned int checksum = 0;
+        int *ibuffer;
+        int i=0;
+
+        ibuffer = (int *)buffer;
+        for( i = 0; i < (cmd->request_bufflen >> 2); i++)
+        {
+          checksum += ibuffer[i];
+        }
+        p->dev_checksum[tindex] = checksum;
+        p->dev_flags[tindex] |= DEVICE_SCANNED;
+        p->dev_flags[tindex] |= DEVICE_PRINT_DTR;
       }
-      p->dev_flags[tindex] |= DEVICE_SCANNED;
-      p->dev_flags[tindex] |= DEVICE_PRINT_WDTR | DEVICE_PRINT_SDTR;
 #undef WIDE_INQUIRY_BITS
 #undef SYNC_INQUIRY_BITS
+#undef SCSI_VERSION_BITS
     }
   }
-  else if ((scb->flags & (SCB_MSGOUT_WDTR | SCB_MSGOUT_SDTR)) != 0)
+  else if ((scb->flags & SCB_MSGOUT_BITS) != 0)
   {
     unsigned short mask;
     int message_error = FALSE;
@@ -2979,11 +3001,11 @@ aic7xxx_done(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
 
     if (scb->flags & SCB_MSGOUT_WDTR)
     {
-      p->wdtr_pending &= ~mask;
+      p->dtr_pending &= ~mask;
       if (message_error)
       {
         if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION2) &&
-             (p->dev_flags[tindex] & DEVICE_PRINT_WDTR) )
+             (p->dev_flags[tindex] & DEVICE_PRINT_DTR) )
         {
           printk(INFO_LEAD "Device failed to complete Wide Negotiation "
             "processing and\n", p->host_no, CTL_OF_SCB(scb));
@@ -2991,7 +3013,6 @@ aic7xxx_done(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
             "disabling future\n", p->host_no, CTL_OF_SCB(scb));
           printk(INFO_LEAD "Wide negotiation to this device.\n", p->host_no,
             CTL_OF_SCB(scb));
-          p->dev_flags[tindex] &= ~DEVICE_PRINT_WDTR;
         }
         p->needwdtr &= ~mask;
         p->needwdtr_copy &= ~mask;
@@ -2999,11 +3020,11 @@ aic7xxx_done(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
     }
     if (scb->flags & SCB_MSGOUT_SDTR)
     {
-      p->sdtr_pending &= ~mask;
+      p->dtr_pending &= ~mask;
       if (message_error)
       {
         if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION2) &&
-             (p->dev_flags[tindex] & DEVICE_PRINT_SDTR) )
+             (p->dev_flags[tindex] & DEVICE_PRINT_DTR) )
         {
           printk(INFO_LEAD "Device failed to complete Sync Negotiation "
             "processing and\n", p->host_no, CTL_OF_SCB(scb));
@@ -3011,12 +3032,38 @@ aic7xxx_done(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
             "disabling future\n", p->host_no, CTL_OF_SCB(scb));
           printk(INFO_LEAD "Sync negotiation to this device.\n", p->host_no,
             CTL_OF_SCB(scb));
-          p->dev_flags[tindex] &= ~DEVICE_PRINT_SDTR;
+          p->dev_flags[tindex] &= ~DEVICE_PRINT_DTR;
         }
         p->needsdtr &= ~mask;
         p->needsdtr_copy &= ~mask;
       }
     }
+    if (scb->flags & SCB_MSGOUT_PPR)
+    {
+      p->dtr_pending &= ~mask;
+      if(message_error)
+      {
+        if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION2) &&
+             (p->dev_flags[tindex] & DEVICE_PRINT_DTR) )
+        {
+          printk(INFO_LEAD "Device failed to complete Parallel Protocol "
+            "Request processing and\n", p->host_no, CTL_OF_SCB(scb));
+          printk(INFO_LEAD "returned a sense error code for invalid message, "
+            "disabling future\n", p->host_no, CTL_OF_SCB(scb));
+          printk(INFO_LEAD "Parallel Protocol Request negotiation to this "
+            "device.\n", p->host_no, CTL_OF_SCB(scb));
+        }
+        /*
+         * Disable PPR negotiation and revert back to WDTR and SDTR setup
+         */
+        p->needppr &= ~mask;
+        p->needppr_copy &= ~mask;
+        p->needsdtr |= mask;
+        p->needsdtr_copy |= mask;
+        p->needwdtr |= mask;
+        p->needwdtr_copy |= mask;
+      }
+    }
   }
   queue_depth = p->dev_temp_queue_depth[tindex];
   if (queue_depth >= p->dev_active_cmds[tindex])
@@ -3058,16 +3105,6 @@ aic7xxx_done(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
   p->dev_active_cmds[tindex]--;
   p->activescbs--;
 
-  /*
-   * If this was an untagged I/O, unbusy the target so the sequencer won't
-   * mistake things later
-   */
-  if (aic7xxx_index_busy_target(p, scb->hscb->target_channel_lun, FALSE) ==
-      scb->hscb->tag)
-  {
-    aic7xxx_index_busy_target(p, scb->hscb->target_channel_lun, TRUE);
-  }
-
   {
     int actual;
 
@@ -3122,7 +3159,7 @@ aic7xxx_done(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
 #endif /* AIC7XXX_PROC_STATS */
       }
 #ifdef AIC7XXX_PROC_STATS
-      x = -10;
+      x = -11;
       while(actual)
       {
         actual >>= 1;
@@ -3429,11 +3466,10 @@ aic7xxx_reset_device(struct aic7xxx_host *p, int target, int channel,
       if (aic7xxx_verbose & (VERBOSE_ABORT_PROCESS | VERBOSE_RESET_PROCESS))
         printk(INFO_LEAD "Cleaning up status information "
           "and delayed_scbs.\n", p->host_no, channel, i, lun);
-      p->dev_flags[i] &= ~BUS_DEVICE_RESET_PENDING;
+      p->dev_flags[i] &= ~(BUS_DEVICE_RESET_PENDING | DEVICE_PARITY_ERROR);
       if ( tag == SCB_LIST_NULL )
       {
-        p->dev_flags[i] |= DEVICE_PRINT_WDTR | DEVICE_PRINT_SDTR |
-                           DEVICE_RESET_DELAY;
+        p->dev_flags[i] |= DEVICE_PRINT_DTR | DEVICE_RESET_DELAY;
         p->dev_expires[i] = jiffies + (4 * HZ);
         p->dev_timer_active |= (0x01 << i);
         p->dev_last_queue_full_count[i] = 0;
@@ -3625,7 +3661,7 @@ aic7xxx_reset_device(struct aic7xxx_host *p, int target, int channel,
   if (aic7xxx_verbose & (VERBOSE_ABORT_PROCESS | VERBOSE_RESET_PROCESS))
     printk(INFO_LEAD "Cleaning disconnected scbs "
       "list.\n", p->host_no, channel, target, lun);
-  if (p->features & AHC_PAGESCBS)
+  if (p->flags & AHC_PAGESCBS)
   {
     unsigned char next, prev, scb_index;
 
@@ -3641,14 +3677,14 @@ aic7xxx_reset_device(struct aic7xxx_host *p, int target, int channel,
         printk(WARN_LEAD "Disconnected List inconsistency; SCB index=%d, "
           "numscbs=%d\n", p->host_no, channel, target, lun, scb_index,
           p->scb_data->numscbs);
-        next = aic7xxx_rem_scb_from_disc_list(p, next);
+        next = aic7xxx_rem_scb_from_disc_list(p, next, prev);
       }
       else
       {
         scbp = p->scb_data->scb_array[scb_index];
         if (aic7xxx_match_scb(p, scbp, target, channel, lun, tag))
         {
-          next = aic7xxx_rem_scb_from_disc_list(p, next);
+          next = aic7xxx_rem_scb_from_disc_list(p, next, prev);
           if (scbp->flags & SCB_WAITINGQ)
           {
             p->dev_active_cmds[TARGET_INDEX(scbp->cmd)]++;
@@ -3677,7 +3713,7 @@ aic7xxx_reset_device(struct aic7xxx_host *p, int target, int channel,
    * Walk the free list making sure no entries on the free list have
    * a valid SCB_TAG value or SCB_CONTROL byte.
    */
-  if (p->features & AHC_PAGESCBS)
+  if (p->flags & AHC_PAGESCBS)
   {
     unsigned char next;
 
@@ -3734,7 +3770,6 @@ aic7xxx_reset_device(struct aic7xxx_host *p, int target, int channel,
     {
       aic_outb(p, SCB_LIST_NULL, SCB_TAG);
       aic_outb(p, SCB_LIST_NULL, SCB_NEXT);
-      aic_outb(p, SCB_LIST_NULL, SCB_PREV);
       aic_outb(p, 0, SCB_CONTROL);
       aic7xxx_add_curscb_to_free_list(p);
     }
@@ -3863,28 +3898,35 @@ aic7xxx_reset_channel(struct aic7xxx_host *p, int channel, int initiate_reset)
   if (channel == 1)
   {
     p->needsdtr |= (p->needsdtr_copy & 0xFF00);
-    p->sdtr_pending &= 0x00FF;
+    p->dtr_pending &= 0x00FF;
     offset_min = 8;
     offset_max = 16;
   }
   else
   {
-    if (p->features & AHC_WIDE)
+    if (p->features & AHC_TWIN)
     {
-      p->needsdtr = p->needsdtr_copy;
-      p->needwdtr = p->needwdtr_copy;
-      p->sdtr_pending = 0x0;
-      p->wdtr_pending = 0x0;
+      /* Channel A */
+      p->needsdtr |= (p->needsdtr_copy & 0x00FF);
+      p->dtr_pending &= 0xFF00;
       offset_min = 0;
-      offset_max = 16;
+      offset_max = 8;
     }
     else
     {
-      /* Channel A */
-      p->needsdtr |= (p->needsdtr_copy & 0x00FF);
-      p->sdtr_pending &= 0xFF00;
+      p->needppr = p->needppr_copy;
+      p->needsdtr = p->needsdtr_copy;
+      p->needwdtr = p->needwdtr_copy;
+      p->dtr_pending = 0x0;
       offset_min = 0;
-      offset_max = 8;
+      if (p->features & AHC_WIDE)
+      {
+        offset_max = 16;
+      }
+      else
+      {
+        offset_max = 8;
+      }
     }
   }
 
@@ -4186,6 +4228,30 @@ aic7xxx_timer(struct aic7xxx_host *p)
 #endif
 }
 
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_construct_ppr
+ *
+ * Description:
+ *   Build up a Parallel Protocol Request message for use with SCSI-3
+ *   devices.
+ *-F*************************************************************************/
+static void
+aic7xxx_construct_ppr(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
+{
+  int tindex = TARGET_INDEX(scb->cmd);
+
+  p->msg_buf[p->msg_index++] = MSG_EXTENDED;
+  p->msg_buf[p->msg_index++] = MSG_EXT_PPR_LEN;
+  p->msg_buf[p->msg_index++] = MSG_EXT_PPR;
+  p->msg_buf[p->msg_index++] = p->transinfo[tindex].goal_period;
+  p->msg_buf[p->msg_index++] = 0;
+  p->msg_buf[p->msg_index++] = p->transinfo[tindex].goal_offset;
+  p->msg_buf[p->msg_index++] = p->transinfo[tindex].goal_width;
+  p->msg_buf[p->msg_index++] = p->transinfo[tindex].goal_options;
+  p->msg_len += 8;
+}
+
 /*+F*************************************************************************
  * Function:
  *   aic7xxx_construct_sdtr
@@ -4304,10 +4370,10 @@ aic7xxx_handle_device_reset(struct aic7xxx_host *p, int target, int channel)
   /*
    * Go back to async/narrow transfers and renegotiate.
    */
+  p->needppr |= (p->needppr_copy & targ_mask);
   p->needsdtr |= (p->needsdtr_copy & targ_mask);
   p->needwdtr |= (p->needwdtr_copy & targ_mask);
-  p->sdtr_pending &= ~targ_mask;
-  p->wdtr_pending &= ~targ_mask;
+  p->dtr_pending &= ~targ_mask;
   aic_outb(p, 0, TARG_SCSIRATE + tindex);
   if (p->features & AHC_ULTRA2)
     aic_outb(p, 0, TARG_OFFSET + tindex);
@@ -4315,7 +4381,7 @@ aic7xxx_handle_device_reset(struct aic7xxx_host *p, int target, int channel)
   if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
     printk(INFO_LEAD "Bus Device Reset delivered.\n", p->host_no, channel,
       target, -1);
-  aic7xxx_run_done_queue(p, /*complete*/ FALSE);
+  aic7xxx_run_done_queue(p, /*complete*/ TRUE);
 }
 
 /*+F*************************************************************************
@@ -4360,6 +4426,8 @@ aic7xxx_handle_seqint(struct aic7xxx_host *p, unsigned char intstat)
                p->host_no, channel, target, lun,
                aic_inb(p, SAVED_TCL), aic_inb(p, ARG_1),
                (aic_inb(p, SEQADDR1) << 8) | aic_inb(p, SEQADDR0));
+        if (aic7xxx_panic_on_abort)
+          aic7xxx_panic_abort(p, NULL);
       }
       break;
 
@@ -4387,7 +4455,7 @@ aic7xxx_handle_seqint(struct aic7xxx_host *p, unsigned char intstat)
             lun, aic_inb(p, LASTPHASE), aic_inb(p, SAVED_TCL));
 
         aic7xxx_reset_channel(p, channel, /*initiate reset*/ TRUE);
-        aic7xxx_run_done_queue(p, FALSE);
+        aic7xxx_run_done_queue(p, TRUE);
 
       }
       break;
@@ -4517,7 +4585,7 @@ aic7xxx_handle_seqint(struct aic7xxx_host *p, unsigned char intstat)
                   aic7xxx_reset_device(p, target, channel, lun, i);
                   reset++;
                 }
-                aic7xxx_run_done_queue(p, FALSE);
+                aic7xxx_run_done_queue(p, TRUE);
               }
             }
             aic7xxx_verbose = old_verbose;
@@ -4533,6 +4601,51 @@ aic7xxx_handle_seqint(struct aic7xxx_host *p, unsigned char intstat)
             aic_outb(p, aic_inb(p, SCSISIGI) | ATNO, SCSISIGO);
           }
         }
+        else if (scb->flags & SCB_MSGOUT_PPR)
+        {
+          /*
+           * As per the draft specs, any device capable of supporting any of
+           * the option values other than 0 are not allowed to reject the
+           * PPR message.  Instead, they must negotiate out what they do
+           * support instead of rejecting our offering.
+           */
+          p->needppr &= ~target_mask;
+          p->needppr_copy &= ~target_mask;
+          aic7xxx_set_width(p, target, channel, lun, MSG_EXT_WDTR_BUS_8_BIT,
+            (AHC_TRANS_ACTIVE|AHC_TRANS_CUR|AHC_TRANS_QUITE));
+          aic7xxx_set_syncrate(p, NULL, target, channel, 0, 0, 0,
+                               AHC_TRANS_ACTIVE|AHC_TRANS_CUR|AHC_TRANS_QUITE);
+          p->transinfo[tindex].goal_options = 0;
+          p->dtr_pending &= ~target_mask;
+          scb->flags &= ~SCB_MSGOUT_BITS;
+          if(aic7xxx_verbose & VERBOSE_NEGOTIATION2)
+          {
+            printk(INFO_LEAD "Device is rejecting PPR messages, falling "
+              "back.\n", p->host_no, channel, target, lun);
+          }
+          if ( p->transinfo[tindex].goal_width )
+          {
+            p->needwdtr |= target_mask;
+            p->needwdtr_copy |= target_mask;
+            p->dtr_pending |= target_mask;
+            scb->flags |= SCB_MSGOUT_WDTR;
+          }
+          if ( p->transinfo[tindex].goal_offset )
+          {
+            p->needsdtr |= target_mask;
+            p->needsdtr_copy |= target_mask;
+            if( !(p->dtr_pending & target_mask) )
+            {
+              p->dtr_pending |= target_mask;
+              scb->flags |= SCB_MSGOUT_SDTR;
+            }
+          }
+          if ( p->dtr_pending & target_mask )
+          {
+            aic_outb(p, HOST_MSG, MSG_OUT);
+            aic_outb(p, aic_inb(p, SCSISIGI) | ATNO, SCSISIGO);
+          }
+        }
         else if (scb->flags & SCB_MSGOUT_WDTR)
         {
           /*
@@ -4540,20 +4653,18 @@ aic7xxx_handle_seqint(struct aic7xxx_host *p, unsigned char intstat)
            */
           p->needwdtr &= ~target_mask;
           p->needwdtr_copy &= ~target_mask;
-          p->wdtr_pending &= ~target_mask;
+          p->dtr_pending &= ~target_mask;
           scb->flags &= ~SCB_MSGOUT_BITS;
           aic7xxx_set_width(p, target, channel, lun, MSG_EXT_WDTR_BUS_8_BIT,
             (AHC_TRANS_ACTIVE|AHC_TRANS_GOAL|AHC_TRANS_CUR));
-          aic7xxx_set_syncrate(p, NULL, target, channel, 0, 0,
+          aic7xxx_set_syncrate(p, NULL, target, channel, 0, 0, 0,
                                AHC_TRANS_ACTIVE|AHC_TRANS_CUR|AHC_TRANS_QUITE);
-          if ( (p->needsdtr_copy & target_mask) &&
-               !(p->sdtr_pending & target_mask) )
+          if(aic7xxx_verbose & VERBOSE_NEGOTIATION2)
           {
-            p->sdtr_pending |= target_mask;
-            scb->flags |= SCB_MSGOUT_SDTR;
-            aic_outb(p, HOST_MSG, MSG_OUT);
-            aic_outb(p, aic_inb(p, SCSISIGO) | ATNO, SCSISIGO);
+            printk(INFO_LEAD "Device is rejecting WDTR messages, using "
+              "narrow transfers.\n", p->host_no, channel, target, lun);
           }
+          p->needsdtr |= (p->needsdtr_copy & target_mask);
         }
         else if (scb->flags & SCB_MSGOUT_SDTR)
         {
@@ -4562,10 +4673,15 @@ aic7xxx_handle_seqint(struct aic7xxx_host *p, unsigned char intstat)
           */
           p->needsdtr &= ~target_mask;
           p->needsdtr_copy &= ~target_mask;
-          p->sdtr_pending &= ~target_mask;
+          p->dtr_pending &= ~target_mask;
           scb->flags &= ~SCB_MSGOUT_SDTR;
-          aic7xxx_set_syncrate(p, NULL, target, channel, 0, 0,
+          aic7xxx_set_syncrate(p, NULL, target, channel, 0, 0, 0,
             (AHC_TRANS_CUR|AHC_TRANS_ACTIVE|AHC_TRANS_GOAL));
+          if(aic7xxx_verbose & VERBOSE_NEGOTIATION2)
+          {
+            printk(INFO_LEAD "Device is rejecting SDTR messages, using "
+              "async transfers.\n", p->host_no, channel, target, lun);
+          }
         }
         else if (aic7xxx_verbose & VERBOSE_SEQINT)
         {
@@ -4681,41 +4797,24 @@ aic7xxx_handle_seqint(struct aic7xxx_host *p, unsigned char intstat)
                  * However, if this SCB already was attempting to negotiate,
                  * then we assume this isn't the problem and skip this part.
                  */
-#ifdef AIC7XXX_FAKE_NEGOTIATION_CMDS
                 if ( (scb->cmd->cmnd[0] != TEST_UNIT_READY) &&
                      (p->dev_flags[tindex] & DEVICE_SCANNED) &&
-                     !(p->wdtr_pending & target_mask) && 
-                     !(p->sdtr_pending & target_mask) )
+                     !(p->dtr_pending & target_mask) ) 
                 {
+                  p->needppr |= (p->needppr_copy & target_mask);
                   p->needwdtr |= (p->needwdtr_copy & target_mask);
                   p->needsdtr |= (p->needsdtr_copy & target_mask);
                 }
-                else if ( (scb->cmd == p->dev_wdtr_cmnd[tindex]) ||
-                          (scb->cmd == p->dev_sdtr_cmnd[tindex]) )
+                else if ( scb->cmd == p->dev_dtr_cmnd[tindex] )
                 {
                   /*
                    * This is already a negotiation command, so we must have
-                   * already done either WDTR or SDTR (or maybe both).  So
-                   * we simply check sdtr_pending and needsdtr to see if we
-                   * should throw out SDTR on this command.
-                   *
-                   * Note: Don't check the needsdtr_copy here, instead just
-                   * check to see if WDTR wiped out our SDTR and set needsdtr.
-                   * Even if WDTR did wipe out SDTR and set needsdtr, if
-                   * parse_msg() then turned around and started our SDTR
-                   * in back to back fasion, then conclusion of that should
-                   * have negated any needsdtr setting.  That's why we only
-                   * check needsdtr and sdtr_pending.
+                   * already done PPR, WDTR or SDTR.  Since our negotiation
+                   * could have gotten rejected, we don't really know the
+                   * full state of things.  Don't do anything here, and allow
+                   * the negotiation_complete() handler to do the right
+                   * thing.
                    */
-                  scb->flags &= ~SCB_MSGOUT_BITS;
-                  if ( (scb->cmd == p->dev_wdtr_cmnd[tindex]) &&
-                       !(p->sdtr_pending & target_mask) &&
-                       (p->needsdtr & target_mask) )
-                  {
-                    p->sdtr_pending |= target_mask;
-                    hscb->control |= MK_MESSAGE;
-                    scb->flags |= SCB_MSGOUT_SDTR;
-                  }
 
                   /*
                    * This is the important part though.  We are getting sense
@@ -4736,43 +4835,13 @@ aic7xxx_handle_seqint(struct aic7xxx_host *p, unsigned char intstat)
                     hscb->data_pointer = scb->sg_list[0].address;
                   }
                 }
-#else
-                if (  (scb->cmd->cmnd[0] != TEST_UNIT_READY) &&
-                     !(scb->flags & SCB_MSGOUT_BITS) && 
-                      (scb->cmd->lun == 0) &&
-                      (p->dev_flags[TARGET_INDEX(scb->cmd)] & DEVICE_SCANNED) )
-                {
-                  if ( (p->needwdtr_copy & target_mask) &&
-                      !(p->wdtr_pending & target_mask) &&
-                      !(p->sdtr_pending & target_mask) )
-                  {
-                    p->needwdtr |= target_mask;
-                    p->wdtr_pending |= target_mask;
-                    hscb->control |= MK_MESSAGE;
-                    scb->flags |= SCB_MSGOUT_WDTR;
-                  }
-                  if ( p->needsdtr_copy & target_mask )
-                  {
-                    p->needsdtr |= target_mask;
-                    if ( !(p->wdtr_pending & target_mask) &&
-                         !(p->sdtr_pending & target_mask) )
-                    {
-                      p->sdtr_pending |= target_mask;
-                      hscb->control |= MK_MESSAGE;
-                      scb->flags |= SCB_MSGOUT_SDTR;
-                    }
-                  }
-                }
-                else
-                  scb->flags &= ~SCB_MSGOUT_BITS;
-#endif /* AIC7XXX_FAKE_NEGOTIATION_CMDS */
                 scb->flags |= SCB_SENSE;
                 /*
                  * Ensure the target is busy since this will be an
                  * an untagged request.
                  */
 #ifdef AIC7XXX_VERBOSE_DEBUGGING
-                if (aic7xxx_verbose > 0xffff)
+                if (aic7xxx_verbose & VERBOSE_NEGOTIATION2)
                 {
                   if (scb->flags & SCB_MSGOUT_BITS)
                     printk(INFO_LEAD "Requesting SENSE with %s\n", p->host_no,
@@ -4914,7 +4983,8 @@ aic7xxx_handle_seqint(struct aic7xxx_host *p, unsigned char intstat)
                 }
               }
 #ifdef AIC7XXX_VERBOSE_DEBUGGING
-              if (aic7xxx_verbose & VERBOSE_MINOR_ERROR)
+              if( (aic7xxx_verbose & VERBOSE_MINOR_ERROR) ||
+                  (aic7xxx_verbose > 0xffff) )
               {
                 if (queue_flag)
                   printk(INFO_LEAD "Queue full received; queue depth %d, "
@@ -4928,8 +4998,6 @@ aic7xxx_handle_seqint(struct aic7xxx_host *p, unsigned char intstat)
 #endif
               if (queue_flag)
               {
-                p->dev_temp_queue_depth[tindex] = 
-                  p->dev_active_cmds[tindex];
                 if ( p->dev_last_queue_full[tindex] !=
                      p->dev_active_cmds[tindex] )
                 {
@@ -4951,11 +5019,29 @@ aic7xxx_handle_seqint(struct aic7xxx_host *p, unsigned char intstat)
                       p->dev_active_cmds[tindex];
                   p->dev_last_queue_full[tindex] = 0;
                   p->dev_last_queue_full_count[tindex] = 0;
+                  p->dev_temp_queue_depth[tindex] = 
+                    p->dev_active_cmds[tindex];
                 }
-                else
+                else if (p->dev_active_cmds[tindex] == 0)
                 {
-                  p->dev_flags[tindex] |= DEVICE_WAS_BUSY;
-                }
+                  if (aic7xxx_verbose & VERBOSE_NEGOTIATION)
+                  {
+                    printk(INFO_LEAD "QUEUE_FULL status received with 0 "
+                           "commands active.\n", p->host_no, CTL_OF_SCB(scb));
+                    printk(INFO_LEAD "Tagged Command Queueing disabled\n",
+                           p->host_no, CTL_OF_SCB(scb));
+                  }
+                  p->dev_max_queue_depth[tindex] = 1;
+                  p->dev_temp_queue_depth[tindex] = 1;
+                  scb->tag_action = 0;
+                  scb->hscb->control &= ~(MSG_ORDERED_Q_TAG|MSG_SIMPLE_Q_TAG);
+                }
+                else
+                {
+                  p->dev_flags[tindex] |= DEVICE_WAS_BUSY;
+                  p->dev_temp_queue_depth[tindex] = 
+                    p->dev_active_cmds[tindex];
+                }
               }
               break;
             }
@@ -4989,7 +5075,7 @@ aic7xxx_handle_seqint(struct aic7xxx_host *p, unsigned char intstat)
          */
 
         if ( !(scb->flags & SCB_DEVICE_RESET) &&
-              (aic_inb(p, MSG_OUT) == MSG_IDENTIFYFLAG) &&
+              (msg_out == MSG_IDENTIFYFLAG) &&
               (scb->hscb->control & TAG_ENB) )
         {
           p->msg_buf[p->msg_index++] = scb->tag_action;
@@ -5020,34 +5106,68 @@ aic7xxx_handle_seqint(struct aic7xxx_host *p, unsigned char intstat)
             printk(INFO_LEAD "Abort message mailed.\n", p->host_no,
               CTL_OF_SCB(scb));
         }
+        else if (scb->flags & SCB_MSGOUT_PPR)
+        {
+          unsigned int max_sync, period;
+          unsigned char options = p->transinfo[tindex].goal_options;
+
+          if (p->features & AHC_ULTRA2)
+          {
+            if ( (aic_inb(p, SBLKCTL) & ENAB40) &&
+                !(aic_inb(p, SSTAT2) & EXP_ACTIVE) )
+            {
+              if( (p->features & AHC_ULTRA3) &&
+                  (p->dev_flags[tindex] & DEVICE_SCSI_3) &&
+                  (p->transinfo[tindex].goal_width ==
+                   MSG_EXT_WDTR_BUS_16_BIT) &&
+                  (options != 0) )
+              {
+                max_sync = AHC_SYNCRATE_ULTRA3;
+              }
+              else
+              {
+                max_sync = AHC_SYNCRATE_ULTRA2;
+              }
+            }
+            else
+            {
+              max_sync = AHC_SYNCRATE_ULTRA;
+            }
+          }
+          else if (p->features & AHC_ULTRA)
+          {
+            max_sync = AHC_SYNCRATE_ULTRA;
+          }
+          else
+          {
+            max_sync = AHC_SYNCRATE_FAST;
+          }
+          period = p->transinfo[tindex].goal_period;
+          aic7xxx_find_syncrate(p, &period, max_sync, &options);
+          p->transinfo[tindex].goal_period = period;
+          p->transinfo[tindex].goal_options = options;
+          if (aic7xxx_verbose & VERBOSE_NEGOTIATION2)
+          {
+            printk(INFO_LEAD "Sending PPR (%d/%d/%d/%d) message.\n",
+                   p->host_no, CTL_OF_SCB(scb), period,
+                   p->transinfo[tindex].goal_offset,
+                   p->transinfo[tindex].goal_width, options);
+          }
+          aic7xxx_construct_ppr(p, scb);
+        }
         else if (scb->flags & SCB_MSGOUT_WDTR)
         {
-#ifdef AIC7XXX_VERBOSE_DEBUGGING
-          if (aic7xxx_verbose > 0xffff)
+          if (aic7xxx_verbose & VERBOSE_NEGOTIATION2)
+          {
             printk(INFO_LEAD "Sending WDTR message.\n", p->host_no,
                    CTL_OF_SCB(scb));
-#endif
-          aic7xxx_construct_wdtr(p,
-            p->transinfo[TARGET_INDEX(scb->cmd)].goal_width);
+          }
+          aic7xxx_construct_wdtr(p, p->transinfo[tindex].goal_width);
         }
         else if (scb->flags & SCB_MSGOUT_SDTR)
         {
           unsigned int max_sync, period;
-          /*
-           * We need to set an accurate goal_offset instead of
-           * the ridiculously high one we default to.  We should
-           * now know if we are wide.  Plus, the WDTR code will 
-           * set our goal_offset for us as well.
-           */
-          if (p->transinfo[tindex].goal_offset)
-          {
-            if (p->features & AHC_ULTRA2)
-              p->transinfo[tindex].goal_offset = MAX_OFFSET_ULTRA2;
-            else if (p->transinfo[tindex].cur_width == MSG_EXT_WDTR_BUS_16_BIT)
-              p->transinfo[tindex].goal_offset = MAX_OFFSET_16BIT;
-            else
-              p->transinfo[tindex].goal_offset = MAX_OFFSET_8BIT;
-          }
+          unsigned char options = 0;
           /*
            * Now that the device is selected, use the bits in SBLKCTL and
            * SSTAT2 to determine the max sync rate for this device.
@@ -5073,14 +5193,14 @@ aic7xxx_handle_seqint(struct aic7xxx_host *p, unsigned char intstat)
             max_sync = AHC_SYNCRATE_FAST;
           }
           period = p->transinfo[tindex].goal_period;
-          aic7xxx_find_syncrate(p, &period, max_sync);
-#ifdef AIC7XXX_VERBOSE_DEBUGGING
-          if (aic7xxx_verbose > 0xffff)
+          aic7xxx_find_syncrate(p, &period, max_sync, &options);
+          if (aic7xxx_verbose & VERBOSE_NEGOTIATION2)
+          {
             printk(INFO_LEAD "Sending SDTR %d/%d message.\n", p->host_no,
                    CTL_OF_SCB(scb),
                    p->transinfo[tindex].goal_period,
                    p->transinfo[tindex].goal_offset);
-#endif
+          }
           aic7xxx_construct_sdtr(p, period,
             p->transinfo[tindex].goal_offset);
         }
@@ -5154,15 +5274,15 @@ aic7xxx_handle_seqint(struct aic7xxx_host *p, unsigned char intstat)
 #if AIC7XXX_NOT_YET 
     case TRACEPOINT:
       {
-        printk(INFO_LEAD "Tracepoint #1 reached.\n", p->host_no, channel,
-          target, lun);
+        printk(INFO_LEAD "Tracepoint #1 reached.\n", p->host_no,
+               channel, target, lun);
       }
       break;
 
     case TRACEPOINT2:
       {
-        printk(INFO_LEAD "Tracepoint #2 reached.\n", p->host_no, channel,
-          target, lun);
+        printk(INFO_LEAD "Tracepoint #2 reached.\n", p->host_no,
+               channel, target, lun);
       }
       break;
 
@@ -5237,7 +5357,7 @@ aic7xxx_parse_msg(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
       case MSG_EXT_SDTR:
       {
         unsigned int period, offset;
-        unsigned char maxsync, saved_offset;
+        unsigned char maxsync, saved_offset, options;
         struct aic7xxx_syncrate *syncrate;
         
         if (p->msg_buf[1] != MSG_EXT_SDTR_LEN)
@@ -5253,7 +5373,13 @@ aic7xxx_parse_msg(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
 
         period = p->msg_buf[3];
         saved_offset = offset = p->msg_buf[4];
+        options = 0;
 
+        /*
+         * Even if we are an Ultra3 card, don't allow Ultra3 sync rates when
+         * using the SDTR messages.  We need the PPR messages to enable the
+         * higher speeds that include things like Dual Edge clocking.
+         */
         if (p->features & AHC_ULTRA2)
         {
           if ( (aic_inb(p, SBLKCTL) & ENAB40) &&
@@ -5283,7 +5409,9 @@ aic7xxx_parse_msg(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
         if ( (scb->flags & (SCB_MSGOUT_SENT|SCB_MSGOUT_SDTR)) !=
              (SCB_MSGOUT_SENT|SCB_MSGOUT_SDTR) )
         {
-          if (!(p->dev_flags[tindex] & DEVICE_SCANNED))
+          if (!(p->dev_flags[tindex] & DEVICE_SCANNED) &&
+              !(p->needsdtr_copy & target_mask) &&
+               (p->transinfo[tindex].user_offset) )
           {
             /*
              * Not only is the device starting this up, but it also hasn't
@@ -5295,38 +5423,49 @@ aic7xxx_parse_msg(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
              */
             p->transinfo[tindex].goal_period =
               p->transinfo[tindex].user_period;
-            p->transinfo[tindex].goal_offset =
-              p->transinfo[tindex].user_offset;
+            if(p->features & AHC_ULTRA2)
+            {
+              p->transinfo[tindex].goal_offset = MAX_OFFSET_ULTRA2;
+            }
+            else if (p->transinfo[tindex].cur_width)
+            {
+              p->transinfo[tindex].goal_offset = MAX_OFFSET_16BIT;
+            }
+            else
+            {
+              p->transinfo[tindex].goal_offset = MAX_OFFSET_8BIT;
+            }
             p->needsdtr_copy |= target_mask;
           }
+          if (aic7xxx_verbose & VERBOSE_NEGOTIATION2)
+          {
+            printk(INFO_LEAD "Received pre-emptive SDTR message from "
+                   "target.\n", p->host_no, CTL_OF_SCB(scb));
+          }
           if ( !p->transinfo[tindex].goal_offset )
             period = 255;
           if ( p->transinfo[tindex].goal_period > period )
             period = p->transinfo[tindex].goal_period;
         }
   
-        syncrate = aic7xxx_find_syncrate(p, &period, maxsync);
+        syncrate = aic7xxx_find_syncrate(p, &period, maxsync, &options);
         aic7xxx_validate_offset(p, syncrate, &offset,
                                 target_scsirate & WIDEXFER);
         aic7xxx_set_syncrate(p, syncrate, target, channel, period,
-                             offset, AHC_TRANS_ACTIVE|AHC_TRANS_CUR);
+                             offset, options, AHC_TRANS_ACTIVE|AHC_TRANS_CUR);
 
         /*
-         * Did we drop to async?  If so, are we sending a reply?  If we are,
+         * Did we drop to async?  Or are we sending a reply?  If we are,
          * then we have to make sure that the reply value reflects the proper
          * settings so we need to set the goal values according to what
          * we need to send.
          */
-        if ( (offset == 0) || (offset != saved_offset) ||
+        if ( (offset != saved_offset) ||
              ((scb->flags & (SCB_MSGOUT_SENT|SCB_MSGOUT_SDTR)) !=
               (SCB_MSGOUT_SENT|SCB_MSGOUT_SDTR) ) )
         {
-          aic7xxx_set_syncrate(p, syncrate, target, channel, period,
-                               offset, AHC_TRANS_GOAL|AHC_TRANS_QUITE);
-          if ( offset == 0 )
-          {
-            p->needsdtr_copy &= ~target_mask;
-          }
+          aic7xxx_set_syncrate(p, syncrate, target, channel, period, offset,
+                               options, AHC_TRANS_GOAL|AHC_TRANS_QUITE);
         }
         
         /*
@@ -5334,15 +5473,13 @@ aic7xxx_parse_msg(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
          * go async, then send an SDTR back to the target
          */
         p->needsdtr &= ~target_mask;
-        p->sdtr_pending &= ~target_mask;
-        if ( ((scb->flags & (SCB_MSGOUT_SENT|SCB_MSGOUT_SDTR)) ==
-              (SCB_MSGOUT_SENT|SCB_MSGOUT_SDTR)) &&
-             (offset == saved_offset) )
-        {
-          scb->flags &= ~SCB_MSGOUT_BITS;
-        }
-        else
+        p->dtr_pending &= ~target_mask;
+        if ( ((scb->flags & (SCB_MSGOUT_SENT|SCB_MSGOUT_SDTR)) !=
+              (SCB_MSGOUT_SENT|SCB_MSGOUT_SDTR)) ||
+             (offset != saved_offset) )
         {
+          reply = TRUE;
+          p->dtr_pending |= target_mask;
           scb->flags &= ~SCB_MSGOUT_BITS;
           scb->flags |= SCB_MSGOUT_SDTR;
           aic_outb(p, HOST_MSG, MSG_OUT);
@@ -5376,12 +5513,11 @@ aic7xxx_parse_msg(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
             {
               reject = TRUE;
               if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION2) &&
-                   ((p->dev_flags[tindex] & DEVICE_PRINT_WDTR) ||
+                   ((p->dev_flags[tindex] & DEVICE_PRINT_DTR) ||
                     (aic7xxx_verbose > 0xffff)) )
               {
                 printk(INFO_LEAD "Requesting %d bit transfers, rejecting.\n",
                   p->host_no, CTL_OF_SCB(scb), 8 * (0x01 << bus_width));
-                p->dev_flags[tindex] &= ~DEVICE_PRINT_WDTR;
               }
             } /* We fall through on purpose */
             case MSG_EXT_WDTR_BUS_8_BIT:
@@ -5395,15 +5531,11 @@ aic7xxx_parse_msg(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
               break;
             }
           }
-          scb->flags &= ~SCB_MSGOUT_BITS;
-          p->wdtr_pending &= ~target_mask;
+          p->dtr_pending &= ~target_mask;
           p->needwdtr &= ~target_mask;
         }
         else
         {
-          scb->flags &= ~SCB_MSGOUT_BITS;
-          scb->flags |= SCB_MSGOUT_WDTR;
-          reply = TRUE;
           if ( !(p->dev_flags[tindex] & DEVICE_SCANNED) )
           {
             /* 
@@ -5413,13 +5545,33 @@ aic7xxx_parse_msg(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
              */
             p->transinfo[tindex].goal_period =
               p->transinfo[tindex].user_period;
-            p->transinfo[tindex].goal_offset =
-              p->transinfo[tindex].user_offset;
+            if(p->transinfo[tindex].user_offset)
+            {
+              if(p->features & AHC_ULTRA2)
+              {
+                p->transinfo[tindex].goal_offset = MAX_OFFSET_ULTRA2;
+              }
+              else if( p->transinfo[tindex].user_width &&
+                       (bus_width == MSG_EXT_WDTR_BUS_16_BIT) &&
+                       p->features & AHC_WIDE )
+              {
+                p->transinfo[tindex].goal_offset = MAX_OFFSET_16BIT;
+              }
+              else
+              {
+                p->transinfo[tindex].goal_offset = MAX_OFFSET_8BIT;
+              }
+            }
             p->transinfo[tindex].goal_width =
               p->transinfo[tindex].user_width;
             p->needwdtr_copy |= target_mask;
             p->needsdtr_copy |= target_mask;
           }
+          if (aic7xxx_verbose & VERBOSE_NEGOTIATION2)
+          {
+            printk(INFO_LEAD "Received pre-emptive WDTR message from "
+                   "target.\n", p->host_no, CTL_OF_SCB(scb));
+          }
           switch(bus_width)
           {
             default:
@@ -5441,8 +5593,11 @@ aic7xxx_parse_msg(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
               break;
             }
           }
+          reply = TRUE;
+          scb->flags &= ~SCB_MSGOUT_BITS;
+          scb->flags |= SCB_MSGOUT_WDTR;
           p->needwdtr &= ~target_mask;
-          p->wdtr_pending &= ~target_mask;
+          p->dtr_pending |= target_mask;
           aic_outb(p, HOST_MSG, MSG_OUT);
           aic_outb(p, aic_inb(p, SCSISIGO) | ATNO, SCSISIGO);
         }
@@ -5456,24 +5611,211 @@ aic7xxx_parse_msg(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
          * supports SDTR at all.  Therefore, we check needsdtr_copy instead
          * of needstr.
          */
-        aic7xxx_set_syncrate(p, NULL, target, channel, 0, 0,
+        aic7xxx_set_syncrate(p, NULL, target, channel, 0, 0, 0,
                              AHC_TRANS_ACTIVE|AHC_TRANS_CUR|AHC_TRANS_QUITE);
-        if ( (p->needsdtr_copy & target_mask) &&
-            !(p->sdtr_pending & target_mask))
+        p->needsdtr |= (p->needsdtr_copy & target_mask);
+        done = TRUE;
+        break;
+      }
+      case MSG_EXT_PPR:
+      {
+        unsigned char bus_width, trans_options, new_trans_options;
+        unsigned int period, offset;
+        unsigned char maxsync, saved_offset;
+        struct aic7xxx_syncrate *syncrate;
+        
+        if (p->msg_buf[1] != MSG_EXT_PPR_LEN)
+        {
+          reject = TRUE;
+          break;
+        }
+
+        /*
+         * If we aren't on one of the new Ultra3 cards, then reject any PPR
+         * message since we can't support any option field other than 0
+         */
+        if( !(p->features & AHC_ULTRA3) )
+        {
+          reject = TRUE;
+          break;
+        }
+
+        if (p->msg_len < (MSG_EXT_PPR_LEN + 2))
+        {
+          break;
+        }
+
+        period = p->msg_buf[3];
+        offset = saved_offset = p->msg_buf[5];
+        bus_width = p->msg_buf[6];
+        trans_options = new_trans_options = p->msg_buf[7] & 0xf;
+
+        if(aic7xxx_verbose & VERBOSE_NEGOTIATION2)
+        {
+          printk(INFO_LEAD "Parsing PPR message (%d/%d/%d/%d)\n",
+                 p->host_no, CTL_OF_SCB(scb), period, offset, bus_width,
+                 trans_options);
+        }
+
+        if ( (aic_inb(p, SBLKCTL) & ENAB40) &&
+            !(aic_inb(p, SSTAT2) & EXP_ACTIVE) )
+        {
+          if(p->features & AHC_ULTRA3)
+          {
+            maxsync = AHC_SYNCRATE_ULTRA3;
+          }
+          else
+          {
+            maxsync = AHC_SYNCRATE_ULTRA2;
+          }
+        }
+        else
         {
-          p->needsdtr |= target_mask;
-          if ( !reject && !reply )
+          maxsync = AHC_SYNCRATE_ULTRA;
+        }
+        /*
+         * We might have a device that is starting negotiation with us
+         * before we can start up negotiation with it....be prepared to
+         * have a device ask for a higher speed then we want to give it
+         * in that case
+         */
+        if ( (scb->flags & (SCB_MSGOUT_SENT|SCB_MSGOUT_PPR)) !=
+             (SCB_MSGOUT_SENT|SCB_MSGOUT_PPR) )
+        {
+          reply = TRUE;
+          scb->flags &= ~SCB_MSGOUT_BITS;
+          scb->flags |= SCB_MSGOUT_PPR;
+          if (!(p->dev_flags[tindex] & DEVICE_SCANNED))
           {
-            scb->flags &= ~SCB_MSGOUT_WDTR;
-            if (p->transinfo[tindex].goal_period)
+            /*
+             * Not only is the device starting this up, but it also hasn't
+             * been scanned yet, so this would likely be our TUR or our
+             * INQUIRY command at scan time, so we need to use the
+             * settings from the SEEPROM if they existed.  Of course, even
+             * if we didn't find a SEEPROM, we stuffed default values into
+             * the user settings anyway, so use those in all cases.
+             */
+            p->transinfo[tindex].goal_period =
+              p->transinfo[tindex].user_period;
+            if(p->transinfo[tindex].user_offset)
             {
-              p->sdtr_pending |= target_mask;
-              scb->flags |= SCB_MSGOUT_SDTR;
-              aic_outb(p, HOST_MSG, MSG_OUT);
-              aic_outb(p, aic_inb(p, SCSISIGO) | ATNO, SCSISIGO);
+              if(p->features & AHC_ULTRA2)
+              {
+                p->transinfo[tindex].goal_offset = MAX_OFFSET_ULTRA2;
+              }
+              else if( p->transinfo[tindex].user_width &&
+                       (bus_width == MSG_EXT_WDTR_BUS_16_BIT) &&
+                       p->features & AHC_WIDE )
+              {
+                p->transinfo[tindex].goal_offset = MAX_OFFSET_16BIT;
+              }
+              else
+              {
+                p->transinfo[tindex].goal_offset = MAX_OFFSET_8BIT;
+              }
+            }
+            p->transinfo[tindex].goal_width =
+              p->transinfo[tindex].user_width;
+            p->transinfo[tindex].goal_options =
+              p->transinfo[tindex].user_options;
+            p->needppr_copy |= target_mask;
+          }
+          if (aic7xxx_verbose & VERBOSE_NEGOTIATION2)
+          {
+            printk(INFO_LEAD "Received pre-emptive PPR message from "
+                   "target.\n", p->host_no, CTL_OF_SCB(scb));
+          }
+          if ( !p->transinfo[tindex].goal_offset )
+            period = 255;
+          if ( p->transinfo[tindex].goal_period > period )
+            period = p->transinfo[tindex].goal_period;
+          if ( p->transinfo[tindex].goal_options == 0 )
+            new_trans_options = 0;
+          switch(bus_width)
+          {
+            default:
+            {
+              if ( (p->features & AHC_WIDE) &&
+                   (p->transinfo[tindex].goal_width ==
+                    MSG_EXT_WDTR_BUS_16_BIT) )
+              {
+                bus_width = MSG_EXT_WDTR_BUS_16_BIT;
+                break;
+              }
+            } /* Fall through if we aren't a wide card */
+            case MSG_EXT_WDTR_BUS_8_BIT:
+            {
+              p->needwdtr_copy &= ~target_mask;
+              bus_width = MSG_EXT_WDTR_BUS_8_BIT;
+              aic7xxx_set_width(p, target, channel, lun, bus_width,
+                                AHC_TRANS_GOAL|AHC_TRANS_QUITE);
+              break;
             }
           }
         }
+        else
+        {
+          switch(bus_width)
+          {
+            default:
+            {
+              reply = TRUE;
+              if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION2) &&
+                   ((p->dev_flags[tindex] & DEVICE_PRINT_DTR) ||
+                    (aic7xxx_verbose > 0xffff)) )
+              {
+                printk(INFO_LEAD "Requesting %d bit transfers, rejecting.\n",
+                  p->host_no, CTL_OF_SCB(scb), 8 * (0x01 << bus_width));
+              }
+            } /* We fall through on purpose */
+            case MSG_EXT_WDTR_BUS_8_BIT:
+            {
+              /*
+               * According to the spec, if we aren't wide, we also can't be
+               * Dual Edge so clear the options byte
+               */
+              new_trans_options = 0;
+              bus_width = MSG_EXT_WDTR_BUS_8_BIT;
+              break;
+            }
+            case MSG_EXT_WDTR_BUS_16_BIT:
+            {
+              break;
+            }
+          }
+        }
+
+        aic7xxx_set_width(p, target, channel, lun, bus_width,
+                          AHC_TRANS_ACTIVE|AHC_TRANS_CUR);
+        syncrate = aic7xxx_find_syncrate(p, &period, maxsync,
+                                         &new_trans_options);
+        aic7xxx_validate_offset(p, syncrate, &offset, bus_width);
+        aic7xxx_set_syncrate(p, syncrate, target, channel, period,
+                             offset, new_trans_options,
+                             AHC_TRANS_ACTIVE|AHC_TRANS_CUR);
+
+        if( (offset != saved_offset) ||
+            (trans_options != new_trans_options) ||
+            ((scb->flags & (SCB_MSGOUT_SENT|SCB_MSGOUT_PPR)) !=
+             (SCB_MSGOUT_SENT|SCB_MSGOUT_PPR)) )
+        {
+          aic7xxx_set_width(p, target, channel, lun, bus_width,
+                            AHC_TRANS_GOAL|AHC_TRANS_QUITE);
+          aic7xxx_set_syncrate(p, syncrate, target, channel, period,
+                               offset, new_trans_options,
+                               AHC_TRANS_GOAL|AHC_TRANS_QUITE);
+          reply = TRUE;
+        }
+        p->dtr_pending &= ~target_mask;
+        p->needppr &= ~target_mask;
+        if(reply)
+        {
+          p->dtr_pending |= target_mask;
+          scb->flags &= ~SCB_MSGOUT_BITS;
+          scb->flags |= SCB_MSGOUT_PPR;
+          aic_outb(p, HOST_MSG, MSG_OUT);
+          aic_outb(p, aic_inb(p, SCSISIGO) | ATNO, SCSISIGO);
+        }
         done = TRUE;
         break;
       }
@@ -5485,7 +5827,7 @@ aic7xxx_parse_msg(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
     } /* end of switch(p->msg_type) */
   } /* end of if (!reject && (p->msg_len > 2)) */
 
-  if (reject)
+  if (!reply && reject)
   {
     aic_outb(p, MSG_MESSAGE_REJECT, MSG_OUT);
     aic_outb(p, aic_inb(p, SCSISIGO) | ATNO, SCSISIGO);
@@ -5657,12 +5999,14 @@ aic7xxx_handle_scsiint(struct aic7xxx_host *p, unsigned char intstat)
     if (aic7xxx_verbose & VERBOSE_RESET)
       printk(WARN_LEAD "Someone else reset the channel!!\n",
            p->host_no, channel, -1, -1);
+    if (aic7xxx_panic_on_abort)
+      aic7xxx_panic_abort(p, NULL);
     /*
      * Go through and abort all commands for the channel, but do not
      * reset the channel again.
      */
     aic7xxx_reset_channel(p, channel, /* Initiate Reset */ FALSE);
-    aic7xxx_run_done_queue(p, FALSE);
+    aic7xxx_run_done_queue(p, TRUE);
     scb = NULL;
   }
   else if ( ((status & BUSFREE) != 0) && ((status & SELTO) == 0) )
@@ -5698,7 +6042,7 @@ aic7xxx_handle_scsiint(struct aic7xxx_host *p, unsigned char intstat)
             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);
+        aic7xxx_run_done_queue(p, TRUE);
         scb = NULL;
         printerror = 0;
       }
@@ -5709,6 +6053,22 @@ aic7xxx_handle_scsiint(struct aic7xxx_host *p, unsigned char intstat)
         printerror = 0;
       }
     }
+    if ( (scb != NULL) &&
+         (scb->cmd == p->dev_dtr_cmnd[TARGET_INDEX(scb->cmd)]) )
+    {
+      /*
+       * This might be a SCSI-3 device that is dropping the bus due to
+       * errors and signalling that we should reduce the transfer speed.
+       * All we have to do is complete this command (since it's a negotiation
+       * command already) and the checksum routine should flag an error and
+       * reduce the speed setting and renegotiate.  We call the reset routing
+       * just to clean out the hardware from this scb.
+       */
+      printerror = 0;
+      aic7xxx_reset_device(p, target, channel, ALL_LUNS, scb->hscb->tag);
+      aic7xxx_run_done_queue(p, TRUE);
+      scb = NULL;
+    }
     if (printerror != 0)
     {
       if (scb != NULL)
@@ -5724,7 +6084,12 @@ aic7xxx_handle_scsiint(struct aic7xxx_host *p, unsigned char intstat)
           tag = SCB_LIST_NULL;
         }
         aic7xxx_reset_device(p, target, channel, ALL_LUNS, tag);
-        aic7xxx_run_done_queue(p, FALSE);
+        aic7xxx_run_done_queue(p, TRUE);
+      }
+      else
+      {
+        aic7xxx_reset_device(p, target, channel, ALL_LUNS, SCB_LIST_NULL);
+        aic7xxx_run_done_queue(p, TRUE);
       }
       printk(INFO_LEAD "Unexpected busfree, LASTPHASE = 0x%x, "
              "SEQADDR = 0x%x\n", p->host_no, channel, target, -1, lastphase,
@@ -5829,12 +6194,26 @@ aic7xxx_handle_scsiint(struct aic7xxx_host *p, unsigned char intstat)
         cmd->result = 0;
         scb = NULL;
       }
+      if (scb->cmd == p->dev_dtr_cmnd[TARGET_INDEX(scb->cmd)])
+      {
+        /*
+         * Turn off the needsdtr, needwdtr, and needppr bits since this device
+         * doesn't seem to exist.
+         */
+        p->needppr &= ~(0x01 << TARGET_INDEX(scb->cmd));
+        p->needppr_copy &= ~(0x01 << TARGET_INDEX(scb->cmd));
+        p->needsdtr &= ~(0x01 << TARGET_INDEX(scb->cmd));
+        p->needsdtr_copy &= ~(0x01 << TARGET_INDEX(scb->cmd));
+        p->needwdtr &= ~(0x01 << TARGET_INDEX(scb->cmd));
+        p->needwdtr_copy &= ~(0x01 << TARGET_INDEX(scb->cmd));
+      }
     }
     /*
      * Restarting the sequencer will stop the selection and make sure devices
      * are allowed to reselect in.
      */
     aic_outb(p, 0, SCSISEQ);
+    aic_outb(p, CLRSELINGO, CLRSINT0);
     aic_outb(p, aic_inb(p, SIMODE1) & ~(ENREQINIT|ENBUSFREE), SIMODE1);
     p->flags &= ~AHC_HANDLING_REQINITS;
     aic_outb(p, CLRSELTIMEO | CLRBUSFREE, CLRSINT1);
@@ -5868,6 +6247,8 @@ aic7xxx_handle_scsiint(struct aic7xxx_host *p, unsigned char intstat)
     Scsi_Cmnd *cmd;
     unsigned char mesg_out = MSG_NOOP;
     unsigned char lastphase = aic_inb(p, LASTPHASE);
+    unsigned char sstat2 = aic_inb(p, SSTAT2);
+    unsigned char tindex = TARGET_INDEX(scb->cmd);
 
     cmd = scb->cmd;
     switch (lastphase)
@@ -5898,12 +6279,81 @@ aic7xxx_handle_scsiint(struct aic7xxx_host *p, unsigned char intstat)
         break;
     }
 
-    /*
-     * A parity error has occurred during a data
-     * transfer phase. Flag it and continue.
-     */
-    printk(WARN_LEAD "Parity error during %s phase.\n",
-           p->host_no, CTL_OF_SCB(scb), phase);
+    /*
+     * A parity error has occurred during a data
+     * transfer phase. Flag it and continue.
+     */
+    if( (aic_inb(p, SCSIRATE) & AHC_SYNCRATE_CRC) && (lastphase == P_DATAIN) )
+    {
+      printk(WARN_LEAD "CRC error during %s phase.\n",
+             p->host_no, CTL_OF_SCB(scb), phase);
+      if(sstat2 & CRCVALERR)
+      {
+        printk(WARN_LEAD "  CRC error in intermediate CRC packet.\n",
+               p->host_no, CTL_OF_SCB(scb));
+      }
+      if(sstat2 & CRCENDERR)
+      {
+        printk(WARN_LEAD "  CRC error in ending CRC packet.\n",
+               p->host_no, CTL_OF_SCB(scb));
+      }
+      if(sstat2 & CRCREQERR)
+      {
+        printk(WARN_LEAD "  Target incorrectly requested a CRC packet.\n",
+               p->host_no, CTL_OF_SCB(scb));
+      }
+      if(sstat2 & DUAL_EDGE_ERROR)
+      {
+        printk(WARN_LEAD "  Dual Edge transmission error.\n",
+               p->host_no, CTL_OF_SCB(scb));
+      }
+    }
+    else
+    {
+      printk(WARN_LEAD "Parity error during %s phase.\n",
+             p->host_no, CTL_OF_SCB(scb), phase);
+    }
+
+    if(p->dev_flags[tindex] & DEVICE_PARITY_ERROR)
+    {
+      struct aic7xxx_syncrate *syncrate;
+      unsigned int period = p->transinfo[tindex].cur_period;
+      unsigned char options = p->transinfo[tindex].cur_options;
+      /*
+       * oops, we had a failure, lower the transfer rate and try again.  It's
+       * worth noting here that it might be wise to also check for typical
+       * wide setting on narrow cable type problems and try disabling wide
+       * instead of slowing down if those exist.  That's hard to do with simple
+       * checksums though.
+       */
+      if((syncrate = aic7xxx_find_syncrate(p, &period, 0, &options)) != NULL)
+      {
+        syncrate++;
+        if( (syncrate->rate[0] != NULL) &&
+            (!(p->features & AHC_ULTRA2) || (syncrate->sxfr_ultra2 == 0)) )
+        {
+          p->transinfo[tindex].goal_period = syncrate->period;
+          if( !(syncrate->sxfr_ultra2 & 0x40) )
+          {
+            p->transinfo[tindex].goal_options = 0;
+          }
+        }
+        else
+        {
+          p->transinfo[tindex].goal_offset = 0;
+          p->transinfo[tindex].goal_period = 0;
+          p->transinfo[tindex].goal_options = 0;
+        }
+        p->needppr |= (p->needppr_copy & (1<<tindex));
+        p->needsdtr |= (p->needsdtr_copy & (1<<tindex));
+        p->needwdtr |= (p->needwdtr_copy & (1<<tindex));
+      }
+      p->dev_flags[tindex] &= ~DEVICE_PARITY_ERROR;
+    }
+    else
+    {
+      p->dev_flags[tindex] |= DEVICE_PARITY_ERROR;
+    }
 
     /*
      * We've set the hardware to assert ATN if we get a parity
@@ -6072,13 +6522,6 @@ aic7xxx_check_scbs(struct aic7xxx_host *p, char *buffer)
       printk("HSCB %d bad, SCB_NEXT points to self.\n", i);
       bogus = TRUE;
     }
-    temp = aic_inb(p, SCB_PREV);
-    if ((temp != SCB_LIST_NULL) &&
-        (temp >= p->scb_data->maxhscbs))
-    {
-      printk("HSCB %d bad, SCB_PREV invalid(%d).\n", i, temp);
-      bogus = TRUE;
-    }
     if (scb_status[i] == 0)
       lost++;
     if (lost > 1)
@@ -6163,7 +6606,7 @@ aic7xxx_isr(int irq, void *dev_id, struct pt_regs *regs)
     unsigned char scb_index;
 
 #ifdef AIC7XXX_VERBOSE_DEBUGGING
-    if(aic7xxx_verbose > 0xffff)
+    if( (p->isr_count < 16) && (aic7xxx_verbose > 0xffff) )
       printk(INFO_LEAD "Command Complete Int.\n", p->host_no, -1, -1, -1);
 #endif
     
@@ -6368,7 +6811,7 @@ do_aic7xxx_isr(int irq, void *dev_id, struct pt_regs *regs)
  *   Determines the queue depth for a given device.  There are two ways
  *   a queue depth can be obtained for a tagged queueing device.  One
  *   way is the default queue depth which is determined by whether
- *   AIC7XXX_CMDS_PER_LUN is defined.  If it is defined, then it is used
+ *   AIC7XXX_CMDS_PER_DEVICE is defined.  If it is defined, then it is used
  *   as the default queue depth.  Otherwise, we use either 4 or 8 as the
  *   default queue depth (dependent on the number of hardware SCBs).
  *   The other way we determine queue depth is through the use of the
@@ -6396,7 +6839,7 @@ aic7xxx_device_queue_depth(struct aic7xxx_host *p, Scsi_Device *device)
   {
     int tag_enabled = TRUE;
 
-    default_depth = AIC7XXX_CMDS_PER_LUN;
+    default_depth = AIC7XXX_CMDS_PER_DEVICE;
  
     if (!(p->discenable & target_mask))
     {
@@ -6958,7 +7401,7 @@ read_seeprom(struct aic7xxx_host *p, int offset,
   }
   printk("\n");
 #endif
-  if (checksum != scarray[len - 1])
+  if ( (checksum != scarray[len - 1]) || (checksum == 0) )
   {
     return (0);
   }
@@ -7371,7 +7814,6 @@ detect_maxscb(struct aic7xxx_host *p)
       aic_outb(p, i, SCBPTR);
       aic_outb(p, 0, SCB_CONTROL);   /* Clear the control byte. */
       aic_outb(p, i + 1, SCB_NEXT);  /* Set the next pointer. */
-      aic_outb(p, i - 1, SCB_PREV);  /* Set the prev pointer. */
       aic_outb(p, SCB_LIST_NULL, SCB_TAG);  /* Make the tag invalid. */
       aic_outb(p, SCB_LIST_NULL, SCB_BUSYTARGETS);  /* no busy untagged */
       aic_outb(p, SCB_LIST_NULL, SCB_BUSYTARGETS+1);/* targets active yet */
@@ -7840,6 +8282,11 @@ aic7xxx_register(Scsi_Host_Template *template, struct aic7xxx_host *p,
    */
   aic7xxx_loadseq(p);
 
+  /*
+   * Make sure the AUTOFLUSHDIS bit is *not* set in the SBLKCTL register
+   */
+  aic_outb(p, aic_inb(p, SBLKCTL) & ~AUTOFLUSHDIS, SBLKCTL);
+
   if ( (p->chip & AHC_CHIPID_MASK) == AHC_AIC7770 )
   {
     aic_outb(p, ENABLE, BCTL);  /* Enable the boards BUS drivers. */
@@ -8035,6 +8482,7 @@ aic7xxx_alloc(Scsi_Host_Template *sht, struct aic7xxx_host *temp)
     {
       p->transinfo[i].goal_period = 0;
       p->transinfo[i].goal_offset = 0;
+      p->transinfo[i].goal_options = 0;
       p->transinfo[i].goal_width = MSG_EXT_WDTR_BUS_8_BIT;
     }
     DRIVER_LOCK_INIT
@@ -8090,10 +8538,14 @@ aic7xxx_free(struct aic7xxx_host *p)
    */
   for (i = 0; i < MAX_TARGETS; i++)
   {
-    if(p->dev_wdtr_cmnd[i])
-      kfree(p->dev_wdtr_cmnd[i]);
-    if(p->dev_sdtr_cmnd[i])
-      kfree(p->dev_sdtr_cmnd[i]);
+    if(p->dev_dtr_cmnd[i])
+    {
+      if(p->dev_dtr_cmnd[i]->request_buffer)
+      {
+        kfree(p->dev_dtr_cmnd[i]->request_buffer);
+      }
+      kfree(p->dev_dtr_cmnd[i]);
+    }
   }
 
 }
@@ -8184,14 +8636,16 @@ aic7xxx_load_seeprom(struct aic7xxx_host *p, unsigned char *sxfrctl1)
     {
       printk("aic7xxx: Using leftover BIOS values.\n");
     }
-    if ( *sxfrctl1 & STPWEN )
+    if ( ((p->chip & ~AHC_CHIPID_MASK) == AHC_PCI) && (*sxfrctl1 & STPWEN) )
     {
       p->flags |= AHC_TERM_ENB_SE_LOW | AHC_TERM_ENB_SE_HIGH;
       sc->adapter_control &= ~CFAUTOTERM;
       sc->adapter_control |= CFSTERM | CFWSTERM | CFLVDSTERM;
     }
     if (aic7xxx_extended)
-      p->flags |= AHC_EXTEND_TRANS_A | AHC_EXTEND_TRANS_B;
+      p->flags |= (AHC_EXTEND_TRANS_A | AHC_EXTEND_TRANS_B);
+    else
+      p->flags &= ~(AHC_EXTEND_TRANS_A | AHC_EXTEND_TRANS_B);
   }
   else
   {
@@ -8256,8 +8710,7 @@ aic7xxx_load_seeprom(struct aic7xxx_host *p, unsigned char *sxfrctl1)
    * Limit to 16 targets just in case.  The 2842 for one is known to
    * blow the max_targets setting, future cards might also.
    */
-  max_targets = MIN(sc->max_targets & CFMAXTARG,
-                   ((p->features & (AHC_TWIN | AHC_WIDE)) ? 16 : 8));
+  max_targets = ((p->features & (AHC_TWIN | AHC_WIDE)) ? 16 : 8);
 
   if (have_seeprom)
   {
@@ -8279,7 +8732,7 @@ aic7xxx_load_seeprom(struct aic7xxx_host *p, unsigned char *sxfrctl1)
     mask = (0x01 << i);
     if (!have_seeprom)
     {
-      if(aic_inb(p, SCSISEQ) != 0)
+      if (aic_inb(p, SCSISEQ) != 0)
       {
         /*
          * OK...the BIOS set things up and left behind the settings we need.
@@ -8323,7 +8776,9 @@ aic7xxx_load_seeprom(struct aic7xxx_host *p, unsigned char *sxfrctl1)
         sc->device_flags[i] = CFDISC;
         if (p->features & AHC_WIDE)
           sc->device_flags[i] |= CFWIDEB;
-        if (p->features & AHC_ULTRA2)
+        if (p->features & AHC_ULTRA3)
+          sc->device_flags[i] |= 2;
+        else if (p->features & AHC_ULTRA2)
           sc->device_flags[i] |= 3;
         else if (p->features & AHC_ULTRA)
           sc->device_flags[i] |= CFSYNCHISULTRA;
@@ -8339,20 +8794,30 @@ aic7xxx_load_seeprom(struct aic7xxx_host *p, unsigned char *sxfrctl1)
     }
     if (p->flags & AHC_NEWEEPROM_FMT)
     {
-      if (sc->device_flags[i] & CFSYNCHISULTRA)
-      {
-        p->ultraenb |= mask;
-      }
-      else if (sc->device_flags[i] & CFNEWULTRAFORMAT)
+      if ( (sc->device_flags[i] & CFNEWULTRAFORMAT) &&
+          !(p->features & AHC_ULTRA2) )
       {
-        if ( ((sc->device_flags[i] & (CFSYNCHISULTRA | CFXFER)) == 0x03) &&
-             !(p->features & AHC_ULTRA2) )
+        /*
+         * I know of two different Ultra BIOSes that do this differently.
+         * One on the Gigabyte 6BXU mb that wants flags[i] & CFXFER to
+         * be == to 0x03 and SYNCISULTRA to be true to mean 40MByte/s
+         * while on the IBM Netfinity 5000 they want the same thing
+         * to be something else, while flags[i] & CFXFER == 0x03 and
+         * SYNCISULTRA false should be 40MByte/s.  So, we set both to
+         * 40MByte/s and the lower speeds be damned.  People will have
+         * to select around the conversely mapped lower speeds in order
+         * to select lower speeds on these boards.
+         */
+        if ((sc->device_flags[i] & (CFXFER)) == 0x03)
         {
           sc->device_flags[i] &= ~CFXFER;
           sc->device_flags[i] |= CFSYNCHISULTRA;
-          p->ultraenb |= mask;
         }
       }
+      if (sc->device_flags[i] & CFSYNCHISULTRA)
+      {
+        p->ultraenb |= mask;
+      }
     }
     else if (sc->adapter_control & CFULTRAEN)
     {
@@ -8364,18 +8829,54 @@ aic7xxx_load_seeprom(struct aic7xxx_host *p, unsigned char *sxfrctl1)
       p->ultraenb &= ~mask;
       p->transinfo[i].user_offset = 0;
       p->transinfo[i].user_period = 0;
+      p->transinfo[i].user_options = 0;
       p->transinfo[i].cur_offset = 0;
       p->transinfo[i].cur_period = 0;
+      p->transinfo[i].cur_options = 0;
       p->needsdtr_copy &= ~mask;
     }
     else
     {
-      if (p->features & AHC_ULTRA2)
+      if (p->features & AHC_ULTRA3)
+      {
+        p->transinfo[i].user_offset = MAX_OFFSET_ULTRA2;
+        p->transinfo[i].cur_offset = aic_inb(p, TARG_OFFSET + i);
+        if( (sc->device_flags[i] & CFXFER) < 0x03 )
+        {
+          scsirate = (sc->device_flags[i] & CFXFER);
+          p->transinfo[i].user_options = MSG_EXT_PPR_OPTION_DT_CRC;
+          if( (aic_inb(p, TARG_SCSIRATE + i) & CFXFER) < 0x03 )
+          {
+            p->transinfo[i].cur_options = 
+              ((aic_inb(p, TARG_SCSIRATE + i) & 0x40) ?
+                 MSG_EXT_PPR_OPTION_DT_CRC : MSG_EXT_PPR_OPTION_DT_UNITS);
+          }
+          else
+          {
+            p->transinfo[i].cur_options = 0;
+          }
+        }
+        else
+        {
+          scsirate = (sc->device_flags[i] & CFXFER) |
+                     ((p->ultraenb & mask) ? 0x18 : 0x10);
+          p->transinfo[i].user_options = 0;
+          p->transinfo[i].cur_options = 0;
+        }
+        p->transinfo[i].user_period = aic7xxx_find_period(p, scsirate,
+                                       AHC_SYNCRATE_ULTRA3);
+        p->transinfo[i].cur_period = aic7xxx_find_period(p,
+                                       aic_inb(p, TARG_SCSIRATE + i),
+                                       AHC_SYNCRATE_ULTRA3);
+      }
+      else if (p->features & AHC_ULTRA2)
       {
         p->transinfo[i].user_offset = MAX_OFFSET_ULTRA2;
         p->transinfo[i].cur_offset = aic_inb(p, TARG_OFFSET + i);
         scsirate = (sc->device_flags[i] & CFXFER) |
                    ((p->ultraenb & mask) ? 0x18 : 0x10);
+        p->transinfo[i].user_options = 0;
+        p->transinfo[i].cur_options = 0;
         p->transinfo[i].user_period = aic7xxx_find_period(p, scsirate,
                                        AHC_SYNCRATE_ULTRA2);
         p->transinfo[i].cur_period = aic7xxx_find_period(p,
@@ -8385,10 +8886,9 @@ aic7xxx_load_seeprom(struct aic7xxx_host *p, unsigned char *sxfrctl1)
       else
       {
         scsirate = (sc->device_flags[i] & CFXFER) << 4;
-        if (sc->device_flags[i] & CFWIDEB)
-          p->transinfo[i].user_offset = MAX_OFFSET_16BIT;
-        else
-          p->transinfo[i].user_offset = MAX_OFFSET_8BIT;
+        p->transinfo[i].user_options = 0;
+        p->transinfo[i].cur_options = 0;
+        p->transinfo[i].user_offset = MAX_OFFSET_8BIT;
         if (p->features & AHC_ULTRA)
         {
           short ultraenb;
@@ -8427,9 +8927,10 @@ aic7xxx_load_seeprom(struct aic7xxx_host *p, unsigned char *sxfrctl1)
   }
   aic_outb(p, ~(p->discenable & 0xFF), DISC_DSB);
   aic_outb(p, ~((p->discenable >> 8) & 0xFF), DISC_DSB + 1);
+  p->needppr = p->needppr_copy = p->needdv = 0;
   p->needwdtr = p->needwdtr_copy;
   p->needsdtr = p->needsdtr_copy;
-  p->wdtr_pending = p->sdtr_pending = 0;
+  p->dtr_pending = 0;
 
   /*
    * We set the p->ultraenb from the SEEPROM to begin with, but now we make
@@ -8453,6 +8954,7 @@ aic7xxx_load_seeprom(struct aic7xxx_host *p, unsigned char *sxfrctl1)
     {
       case AHC_AIC7895:
       case AHC_AIC7896:
+      case AHC_AIC7899:
         if (p->adapter_control & CFBPRIMARY)
           p->flags |= AHC_CHANNEL_B_PRIMARY;
       default:
@@ -8783,6 +9285,14 @@ aic7xxx_detect(Scsi_Host_Template *template)
       {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7855, AHC_AIC7850,
        AHC_PAGESCBS, AHC_AIC7850_FE,                         6,
        32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7821, AHC_AIC7860,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
+       AHC_AIC7860_FE,                                       7,
+       32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_3860, AHC_AIC7860,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
+       AHC_AIC7860_FE,                                       7,
+       32, C46 },
       {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7860, AHC_AIC7860,
        AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
        AHC_AIC7860_FE,                                       7,
@@ -8825,6 +9335,18 @@ aic7xxx_detect(Scsi_Host_Template *template)
       {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7884, AHC_AIC7880,
        AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE,     18,
        32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7885, AHC_AIC7880,
+       AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE,     18,
+       32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7886, AHC_AIC7880,
+       AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE,     18,
+       32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7887, AHC_AIC7880,
+       AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE,     18,
+       32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7888, AHC_AIC7880,
+       AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE,     18,
+       32, C46 },
       {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7895, AHC_AIC7895,
        AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
        AHC_AIC7895_FE,                                      19,
@@ -8833,30 +9355,66 @@ aic7xxx_detect(Scsi_Host_Template *template)
        AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
        AHC_AIC7890_FE,                                      20,
        32, C46 },
-      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_78902, AHC_AIC7890,
+      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7890B, AHC_AIC7890,
        AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
        AHC_AIC7890_FE,                                      20,
        32, C46 },
-      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_2940U2, AHC_AIC7890,
+      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_2930U2, AHC_AIC7890,
        AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
        AHC_AIC7890_FE,                                      21,
        32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_2940U2, AHC_AIC7890,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
+       AHC_AIC7890_FE,                                      22,
+       32, C46 },
       {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7896, AHC_AIC7896,
        AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
-       AHC_AIC7896_FE,                                      22,
+       AHC_AIC7896_FE,                                      23,
        32, C56_66 },
       {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_3940U2, AHC_AIC7896,
        AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
-       AHC_AIC7896_FE,                                      23,
+       AHC_AIC7896_FE,                                      24,
        32, C56_66 },
       {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_3950U2D, AHC_AIC7896,
        AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
-       AHC_AIC7896_FE,                                      24,
+       AHC_AIC7896_FE,                                      25,
        32, C56_66 },
       {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_1480A, AHC_AIC7860,
        AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
-       AHC_AIC7860_FE,                                      25,
+       AHC_AIC7860_FE,                                      26,
+       32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7892A, AHC_AIC7892,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
+       AHC_AIC7892_FE,                                      27,
+       32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7892B, AHC_AIC7892,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
+       AHC_AIC7892_FE,                                      27,
        32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7892D, AHC_AIC7892,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
+       AHC_AIC7892_FE,                                      27,
+       32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7892P, AHC_AIC7892,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
+       AHC_AIC7892_FE,                                      27,
+       32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7899A, AHC_AIC7899,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
+       AHC_AIC7899_FE,                                      28,
+       32, C56_66 },
+      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7899B, AHC_AIC7899,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
+       AHC_AIC7899_FE,                                      28,
+       32, C56_66 },
+      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7899D, AHC_AIC7899,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
+       AHC_AIC7899_FE,                                      28,
+       32, C56_66 },
+      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7899P, AHC_AIC7899,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
+       AHC_AIC7899_FE,                                      28,
+       32, C56_66 },
     };
 
     unsigned short command;
@@ -8926,11 +9484,11 @@ aic7xxx_detect(Scsi_Host_Template *template)
           }
 #ifdef AIC7XXX_STRICT_PCI_SETUP
           command |= PCI_COMMAND_SERR | PCI_COMMAND_PARITY |
-            PCI_COMMAND_INVALIDATE | PCI_COMMAND_MASTER |
-            PCI_COMMAND_MEMORY | PCI_COMMAND_IO;
+            PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY | PCI_COMMAND_IO;
 #else
           command |= PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY | PCI_COMMAND_IO;
 #endif
+          command &= ~PCI_COMMAND_INVALIDATE;
           if (aic7xxx_pci_parity == 0)
             command &= ~(PCI_COMMAND_SERR | PCI_COMMAND_PARITY);
           pci_write_config_word(pdev, PCI_COMMAND, command);
@@ -8940,15 +9498,7 @@ aic7xxx_detect(Scsi_Host_Template *template)
           {
             printk("aic7xxx: Initial DEVCONFIG value was 0x%x\n", devconfig);
           }
-          devconfig |= 0x80000000;
-          if ((aic7xxx_pci_parity == 0) || (aic7xxx_pci_parity == -1))
-          {
-            devconfig &= ~(0x00000008);
-          }
-          else
-          {
-            devconfig |= 0x00000008;
-          }
+          devconfig |= 0x80000040;
           pci_write_config_dword(pdev, DEVCONFIG, devconfig);
 #endif /* AIC7XXX_STRICT_PCI_SETUP */
 #else  /* LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92) */
@@ -8976,11 +9526,11 @@ aic7xxx_detect(Scsi_Host_Template *template)
           }
 #ifdef AIC7XXX_STRICT_PCI_SETUP
           command |= PCI_COMMAND_SERR | PCI_COMMAND_PARITY |
-            PCI_COMMAND_INVALIDATE | PCI_COMMAND_MASTER |
-            PCI_COMMAND_MEMORY | PCI_COMMAND_IO;
+            PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY | PCI_COMMAND_IO;
 #else
           command |= PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY | PCI_COMMAND_IO;
 #endif
+          command &= ~PCI_COMMAND_INVALIDATE;
           if (aic7xxx_pci_parity == 0)
             command &= ~(PCI_COMMAND_SERR | PCI_COMMAND_PARITY);
           pcibios_write_config_word(pci_bus, pci_devfn, PCI_COMMAND, command);
@@ -8990,15 +9540,7 @@ aic7xxx_detect(Scsi_Host_Template *template)
           {
             printk("aic7xxx: Initial DEVCONFIG value was 0x%x\n", devconfig);
           }
-          devconfig |= 0x80000000;
-          if ((aic7xxx_pci_parity == 0) || (aic7xxx_pci_parity == -1))
-          {
-            devconfig &= ~(0x00000008);
-          }
-          else
-          {
-            devconfig |= 0x00000008;
-          }
+          devconfig |= 0x80000040;
           pcibios_write_config_dword(pci_bus, pci_devfn, DEVCONFIG, devconfig);
 #endif /* AIC7XXX_STRICT_PCI_SETUP */
 #endif /* LINUIX_VERSION_CODE > KERNEL_VERSION(2,1,92) */
@@ -9137,6 +9679,7 @@ aic7xxx_detect(Scsi_Host_Template *template)
 
             case AHC_AIC7895: /* 7895 */
             case AHC_AIC7896: /* 7896/7 */
+            case AHC_AIC7899: /* 7899 */
 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92)
               if (PCI_FUNC(temp_p->pdev->devfn) != 0)
               {
@@ -9185,43 +9728,38 @@ aic7xxx_detect(Scsi_Host_Template *template)
            */
           switch (temp_p->chip & AHC_CHIPID_MASK)
           {
-            case AHC_AIC7890:
-            case AHC_AIC7896:
+            case AHC_AIC7892:
+            case AHC_AIC7899:
               aic_outb(temp_p, 0, SCAMCTL);
               /*
-               * We used to set DPARCKEN in this register, but after talking
-               * to a tech from Adaptec, I found out they don't use that
-               * particular bit in their own register settings, and when you
-               * combine that with the fact that I determined that we were
-               * seeing Data-Path Parity Errors on things we shouldn't see
-               * them on, I think there is a bug in the silicon and the way
-               * to work around it is to disable this particular check.  Also
-               * This bug only showed up on certain commands, so it seems to
-               * be pattern related or some such.  The commands we would
-               * typically send as a linux TEST_UNIT_READY or INQUIRY command
-               * could cause it to be triggered, while regular commands that
-               * actually made reasonable use of the SG array capabilities
-               * seemed not to cause the problem.
+               * Switch to the alt mode of the chip...
                */
+              aic_outb(temp_p, aic_inb(temp_p, SFUNCT) | ALT_MODE, SFUNCT);
               /*
-              aic_outb(temp_p, aic_inb(temp_p, DSCOMMAND0) |
-                               CACHETHEN | DPARCKEN | MPARCKEN |
-                               USCBSIZE32 | CIOPARCKEN,
-                               DSCOMMAND0);
+               * Set our options...the last two items set our CRC after x byte
+              * count in target mode...
                */
+              aic_outb(temp_p, AUTO_MSGOUT_DE | DIS_MSGIN_DUALEDGE, OPTIONMODE);
+             aic_outb(temp_p, 0x00, 0x0b);
+             aic_outb(temp_p, 0x10, 0x0a);
+              /*
+               * switch back to normal mode...
+               */
+              aic_outb(temp_p, aic_inb(temp_p, SFUNCT) & ~ALT_MODE, SFUNCT);
+              aic_outb(temp_p, CRCVALCHKEN | CRCENDCHKEN | CRCREQCHKEN |
+                              TARGCRCENDEN | TARGCRCCNTEN,
+                       CRCCONTROL1);
+              aic_outb(temp_p, ((aic_inb(temp_p, DSCOMMAND0) | USCBSIZE32 |
+                                 MPARCKEN | CIOPARCKEN | CACHETHEN) & 
+                               ~DPARCKEN), DSCOMMAND0);
+              aic7xxx_load_seeprom(temp_p, &sxfrctl1);
+              break;
+            case AHC_AIC7890:
+            case AHC_AIC7896:
+              aic_outb(temp_p, 0, SCAMCTL);
               aic_outb(temp_p, (aic_inb(temp_p, DSCOMMAND0) |
                                 CACHETHEN | MPARCKEN | USCBSIZE32 |
                                 CIOPARCKEN) & ~DPARCKEN, DSCOMMAND0);
-              /* FALLTHROUGH */
-            default:
-              /*
-               * We attempt to read a SEEPROM on *everything*.  If we fail,
-               * then we fail, but this covers things like 2910c cards that
-               * now have SEEPROMs with their 7856 chipset that we would
-               * otherwise ignore.  They still don't have a BIOS, but they
-               * have a SEEPROM that the SCSISelect utility on the Adaptec
-               * diskettes can configure.
-               */
               aic7xxx_load_seeprom(temp_p, &sxfrctl1);
               break;
             case AHC_AIC7850:
@@ -9233,14 +9771,13 @@ aic7xxx_detect(Scsi_Host_Template *template)
               aic_outb(temp_p, (aic_inb(temp_p, DSCOMMAND0) |
                                 CACHETHEN | MPARCKEN) & ~DPARCKEN,
                        DSCOMMAND0);
+              /* FALLTHROUGH */
+            default:
               aic7xxx_load_seeprom(temp_p, &sxfrctl1);
               break;
             case AHC_AIC7880:
               /*
-               * Only set the DSCOMMAND0 register if this is a Rev B.
-               * chipset.  For those, we also enable Ultra mode by
-               * force due to brain-damage on the part of some BIOSes
-               * We overload the devconfig variable here since we can.
+               * Check the rev of the chipset before we change DSCOMMAND0
                */
 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92)
               pci_read_config_dword(pdev, DEVCONFIG, &devconfig);
@@ -9272,6 +9809,7 @@ aic7xxx_detect(Scsi_Host_Template *template)
           {
             case AHC_AIC7895:
             case AHC_AIC7896:
+            case AHC_AIC7899:
               current_p = list_p;
               while(current_p != NULL)
               {
@@ -9315,6 +9853,7 @@ aic7xxx_detect(Scsi_Host_Template *template)
               break;
             case AHC_AIC7895:
             case AHC_AIC7896:
+            case AHC_AIC7899:
 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92)
               pci_read_config_dword(pdev, DEVCONFIG, &devconfig);
 #else
@@ -9364,7 +9903,7 @@ aic7xxx_detect(Scsi_Host_Template *template)
            */
           if (temp_p->features & AHC_ULTRA2)
           {
-            aic_outb(temp_p, RD_DFTHRSH_75 | WR_DFTHRSH_75, DFF_THRSH);
+            aic_outb(temp_p, RD_DFTHRSH_MAX | WR_DFTHRSH_MAX, DFF_THRSH);
           }
           else
           {
@@ -9512,7 +10051,7 @@ aic7xxx_detect(Scsi_Host_Template *template)
               }
             }
             /*
-             * Are we dealing with a 7985 where we need to sort the
+             * Are we dealing with a 7895/6/7/9 where we need to sort the
              * channels as well, if so, the bios_address values should
              * be the same
              */
@@ -9603,7 +10142,54 @@ aic7xxx_detect(Scsi_Host_Template *template)
   return (found);
 }
 
-#ifdef AIC7XXX_FAKE_NEGOTIATION_CMDS
+static void aic7xxx_build_negotiation_cmnd(struct aic7xxx_host *p,
+                                           Scsi_Cmnd *old_cmd, int tindex);
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_allocate_negotiation_command
+ *
+ * Description:
+ *   allocate the actual command struct and fill in the gaps...
+ *-F*************************************************************************/
+static Scsi_Cmnd *
+aic7xxx_allocate_negotiation_command(struct aic7xxx_host *p,
+                                     Scsi_Cmnd *old_cmd, int tindex)
+{
+  Scsi_Cmnd *cmd;
+  char *buffer;
+
+  if (!(p->dev_dtr_cmnd[tindex] = kmalloc(sizeof(Scsi_Cmnd), GFP_ATOMIC)) )
+  {
+    return(NULL);
+  }
+  if (!(buffer = kmalloc(256, GFP_ATOMIC)))
+  {
+    kfree(p->dev_dtr_cmnd[tindex]);
+    p->dev_dtr_cmnd[tindex] = NULL;
+    return(NULL);
+  }
+  cmd = p->dev_dtr_cmnd[tindex];
+  memset(cmd, 0, sizeof(Scsi_Cmnd));
+  memcpy(cmd, old_cmd, sizeof(Scsi_Cmnd));
+  memset(&cmd->cmnd[0], 0, sizeof(cmd->cmnd));
+  memset(&cmd->data_cmnd[0], 0, sizeof(cmd->data_cmnd));
+  cmd->lun = 0;
+  cmd->request_bufflen = 255;
+  cmd->request_buffer = buffer;
+  cmd->use_sg = cmd->old_use_sg = cmd->sglist_len = 0;
+  cmd->bufflen = 0;
+  cmd->buffer = NULL;
+  cmd->underflow = 0;
+  cmd->cmd_len = 6;
+  cmd->cmnd[0] = cmd->data_cmnd[0] = INQUIRY;
+  cmd->cmnd[1] = cmd->data_cmnd[1] = 0;
+  cmd->cmnd[2] = cmd->data_cmnd[2] = 0;
+  cmd->cmnd[3] = cmd->data_cmnd[3] = 0;
+  cmd->cmnd[4] = cmd->data_cmnd[4] = 255; /* match what scsi.c does here */
+  cmd->cmnd[5] = cmd->data_cmnd[5] = 0;
+  return(cmd);
+}
 
 /*+F*************************************************************************
  * Function:
@@ -9616,6 +10202,117 @@ aic7xxx_detect(Scsi_Host_Template *template)
 static void
 aic7xxx_negotiation_complete(Scsi_Cmnd *cmd)
 {
+  unsigned int checksum;
+  int i;
+  int *ibuffer;
+  struct aic7xxx_host *p = (struct aic7xxx_host *)cmd->host->hostdata;
+  int tindex = TARGET_INDEX(cmd);
+  struct aic7xxx_syncrate *syncrate;
+
+  /*
+   * perform our minimalistic domain validation
+   */
+  if(p->dev_flags[tindex] & DEVICE_SCANNED)
+  {
+    ibuffer = (int *)cmd->request_buffer;
+    checksum = 0;
+    for(i = 0; i < (cmd->request_bufflen >> 2); i++)
+    {
+      checksum += ibuffer[i];
+    }
+    if( (checksum != p->dev_checksum[tindex]) &&
+        (p->transinfo[tindex].cur_offset != 0) )
+    {
+      unsigned int period = p->transinfo[tindex].cur_period;
+      unsigned char options = p->transinfo[tindex].cur_options;
+
+      if (p->needdv & (1<<tindex))
+      {
+        /*
+         * oops, we had a failure, lower the transfer rate and try again.  It's
+         * worth noting here that it might be wise to also check for typical
+         * wide setting on narrow cable type problems and try disabling wide
+         * instead of slowing down if those exist.  That's hard to do with simple
+         * checksums though.
+         */
+        if(aic7xxx_verbose & VERBOSE_NEGOTIATION) 
+        {
+          printk(INFO_LEAD "reducing SCSI transfer speed due to Domain "
+                 "validation failure.\n", p->host_no, CTL_OF_CMD(cmd));
+        }
+        if((syncrate = aic7xxx_find_syncrate(p, &period, 0, &options)) != NULL)
+        {
+          syncrate++;
+          if( (syncrate->rate[0] != NULL) &&
+              (!(p->features & AHC_ULTRA2) || (syncrate->sxfr_ultra2 == 0)) )
+          {
+            p->transinfo[tindex].goal_period = syncrate->period;
+            if( !(syncrate->sxfr_ultra2 & 0x40) )
+            {
+              p->transinfo[tindex].goal_options = 0;
+            }
+          }
+          else
+          {
+            p->transinfo[tindex].goal_offset = 0;
+            p->transinfo[tindex].goal_period = 0;
+            p->transinfo[tindex].goal_options = 0;
+          }
+          p->needppr |= (p->needppr_copy & (1<<tindex));
+          p->needsdtr |= (p->needsdtr_copy & (1<<tindex));
+          p->needwdtr |= (p->needwdtr_copy & (1<<tindex));
+        }
+        p->needdv &= ~(1<<tindex);
+      }
+      else
+      {
+        if(aic7xxx_verbose & VERBOSE_NEGOTIATION) 
+        {
+          printk(INFO_LEAD "Performing Domain validation.\n",
+                 p->host_no, CTL_OF_CMD(cmd));
+        }
+        /*
+         * Update the checksum in case the INQUIRY data has changed, maybe
+         * in relation to a change in the mode pages, or whatever.
+         */
+        p->dev_checksum[tindex] = checksum;
+        /*
+         * Signal that we are trying out the domain validation
+         */
+        p->needdv |= (1<<tindex);
+        /*
+         * Signal that we need to re-negotiate things, this also gets us our
+         * INQUIRY command to re-checksum off of.
+         */
+        p->needppr |= (p->needppr_copy & (1<<tindex));
+        p->needsdtr |= (p->needsdtr_copy & (1<<tindex));
+        p->needwdtr |= (p->needwdtr_copy & (1<<tindex));
+      }
+    } 
+    else
+    {
+      if( (aic7xxx_verbose & VERBOSE_NEGOTIATION) &&
+          (p->needdv & (1<<tindex)) )
+      {
+        printk(INFO_LEAD "Successfully completed Domain validation.\n",
+               p->host_no, CTL_OF_CMD(cmd));
+      }
+      /*
+       * We successfully did our checksum, so don't leave the needdv flag set
+       * in case we might have set it last time through.
+       */
+      p->needdv &= ~(1<<tindex);
+    }
+  }
+
+  p->dtr_pending &= ~(0x01 << tindex);
+  /*
+   * This looks recursive in the extreme, but if this was a WDTR negotiation
+   * and we didn't follow up with SDTR yet, then this will get it started.
+   * For all other cases, this should work out to be a no-op, unless we are
+   * doing domain validation and happen to need a new negotiation command.
+   */
+  aic7xxx_build_negotiation_cmnd(p, cmd->next, tindex);
   return;
 }
 
@@ -9632,81 +10329,63 @@ aic7xxx_build_negotiation_cmnd(struct aic7xxx_host *p, Scsi_Cmnd *old_cmd,
   int tindex)
 {
 
-  if ( (p->needwdtr & (1<<tindex)) && !(p->wdtr_pending & (1<<tindex)) )
+  if ( !(p->dtr_pending & (1<<tindex)) &&
+       ( (p->needppr & (1<<tindex)) ||
+         (p->needwdtr & (1<<tindex)) ||
+         (p->needsdtr & (1<<tindex)) ) )
   {
-    if(p->dev_wdtr_cmnd[tindex] == NULL)
+    if ( (p->dev_dtr_cmnd[tindex] == NULL) &&
+         (aic7xxx_allocate_negotiation_command(p, old_cmd, tindex) == NULL) )
     {
-      Scsi_Cmnd *cmd;
-
-      if (!(p->dev_wdtr_cmnd[tindex] = kmalloc(sizeof(Scsi_Cmnd), GFP_ATOMIC)) )
-      {
-        return;
-      }
-      cmd = p->dev_wdtr_cmnd[tindex];
-      memset(cmd, 0, sizeof(Scsi_Cmnd));
-      memcpy(cmd, old_cmd, sizeof(Scsi_Cmnd));
-      memset(&cmd->cmnd[0], 0, sizeof(cmd->cmnd));
-      memset(&cmd->data_cmnd[0], 0, sizeof(cmd->data_cmnd));
-      cmd->lun = 0;
-      cmd->request_bufflen = 0;
-      cmd->request_buffer = NULL;
-      cmd->use_sg = cmd->old_use_sg = cmd->sglist_len = 0;
-      cmd->bufflen = 0;
-      cmd->buffer = NULL;
-      cmd->underflow = 0;
-      cmd->cmd_len = 6;
+      return;
     }
     /*
-     * Before sending this thing out, we also amke the cmd->next pointer
+     * Before sending this thing out, we also make the cmd->next pointer
      * point to the real command so we can stuff any possible SENSE data
-     * intp the real command instead of this fake command.  This has to be
+     * into the real command instead of this fake command.  This has to be
      * done each time the command is built, not just the first time, hence
      * it's outside of the above if()...
      */
-    p->dev_wdtr_cmnd[tindex]->next = old_cmd;
-    aic7xxx_queue(p->dev_wdtr_cmnd[tindex], 
-                  aic7xxx_negotiation_complete);
-  }
-  else if ( (p->needsdtr & (1<<tindex)) && !(p->sdtr_pending & (1<<tindex)) &&
-            !(p->wdtr_pending & (1<<tindex)) )
-  {
-    if(p->dev_sdtr_cmnd[tindex] == NULL)
+    p->dev_dtr_cmnd[tindex]->next = old_cmd;
+    /*
+     * Clear the buffer so checksums come out right....
+     */
+    memset(p->dev_dtr_cmnd[tindex]->request_buffer, 0,
+           p->dev_dtr_cmnd[tindex]->request_bufflen);
+    /*
+     * Remove any commands for this particular device that might be on the
+     * waiting_scbs queue or qinfifo so that this command goes out first.
+     * This is vital for our implementation of domain validation.
+     */
+    pause_sequencer(p);
+    aic7xxx_search_qinfifo(p, old_cmd->target, old_cmd->channel, ALL_LUNS,
+                SCB_LIST_NULL, 0, TRUE, &p->delayed_scbs[tindex]);
+    unpause_sequencer(p, FALSE);
     {
-      Scsi_Cmnd *cmd;
+      struct aic7xxx_scb *scb, *next;
 
-      if (!(p->dev_sdtr_cmnd[tindex] = kmalloc(sizeof(Scsi_Cmnd), GFP_ATOMIC)) )
+      scb = p->waiting_scbs.head;
+      while(scb != NULL)
       {
-        return;
+        if( aic7xxx_match_scb(p, scb, old_cmd->target, old_cmd->channel,
+                              ALL_LUNS, SCB_LIST_NULL) )
+        {
+          next = scb->q_next;
+          scbq_remove(&p->waiting_scbs, scb);
+          scbq_insert_tail(&p->delayed_scbs[tindex], scb);
+          scb = next;
+        }
+        else
+        {
+          scb = scb->q_next;
+        }
       }
-      cmd = p->dev_sdtr_cmnd[tindex];
-      memset(cmd, 0, sizeof(Scsi_Cmnd));
-      memcpy(cmd, old_cmd, sizeof(Scsi_Cmnd));
-      memset(&cmd->cmnd[0], 0, sizeof(cmd->cmnd));
-      memset(&cmd->data_cmnd[0], 0, sizeof(cmd->data_cmnd));
-      cmd->lun = 0;
-      cmd->request_bufflen = 0;
-      cmd->request_buffer = NULL;
-      cmd->use_sg = cmd->old_use_sg = cmd->sglist_len = 0;
-      cmd->bufflen = 0;
-      cmd->buffer = NULL;
-      cmd->underflow = 0;
-      cmd->cmd_len = 6;
     }
-    /*
-     * Before sending this thing out, we also amke the cmd->next pointer
-     * point to the real command so we can stuff any possible SENSE data
-     * intp the real command instead of this fake command.  This has to be
-     * done each time the command is built, not just the first time, hence
-     * it's outside of the above if()...
-     */
-    p->dev_sdtr_cmnd[tindex]->next = old_cmd;
-    aic7xxx_queue(p->dev_sdtr_cmnd[tindex], 
+    aic7xxx_queue(p->dev_dtr_cmnd[tindex], 
                   aic7xxx_negotiation_complete);
   }
 }
 
-#endif
-
 #ifdef AIC7XXX_VERBOSE_DEBUGGING
 /*+F*************************************************************************
  * Function:
@@ -9744,8 +10423,9 @@ aic7xxx_buildscb(struct aic7xxx_host *p, Scsi_Cmnd *cmd,
 {
   unsigned short mask;
   struct aic7xxx_hwscb *hscb;
+  unsigned char tindex = TARGET_INDEX(cmd);
 
-  mask = (0x01 << TARGET_INDEX(cmd));
+  mask = (0x01 << tindex);
   hscb = scb->hscb;
 
   /*
@@ -9757,11 +10437,12 @@ aic7xxx_buildscb(struct aic7xxx_host *p, Scsi_Cmnd *cmd,
   if (p->discenable & mask)
   {
     hscb->control |= DISCENB;
-    if (p->tagenable & mask)
+    if ( (p->tagenable & mask) &&
+         (cmd->cmnd[0] != TEST_UNIT_READY) )
     {
       cmd->tag = hscb->tag;
-      p->dev_commands_sent[TARGET_INDEX(cmd)]++;
-      if (p->dev_commands_sent[TARGET_INDEX(cmd)] < 200)
+      p->dev_commands_sent[tindex]++;
+      if (p->dev_commands_sent[tindex] < 200)
       {
         hscb->control |= MSG_SIMPLE_Q_TAG;
         scb->tag_action = MSG_SIMPLE_Q_TAG;
@@ -9778,74 +10459,38 @@ aic7xxx_buildscb(struct aic7xxx_host *p, Scsi_Cmnd *cmd,
           hscb->control |= MSG_SIMPLE_Q_TAG;
           scb->tag_action = MSG_SIMPLE_Q_TAG;
         }
-        p->dev_commands_sent[TARGET_INDEX(cmd)] = 0;
+        p->dev_commands_sent[tindex] = 0;
       }
     }
   }
-  if (p->dev_flags[TARGET_INDEX(cmd)] & DEVICE_SCANNED)
+  if ( cmd == p->dev_dtr_cmnd[tindex] )
   {
-#ifdef AIC7XXX_FAKE_NEGOTIATION_CMDS
-    if ( (p->needwdtr & mask) && !(p->wdtr_pending & mask) )
+    p->dtr_pending |= mask;
+    scb->tag_action = 0;
+    if (p->dev_flags[tindex] & DEVICE_SCANNED)
     {
-      if (cmd == p->dev_wdtr_cmnd[TARGET_INDEX(cmd)])
+      hscb->control &= DISCENB;
+      hscb->control |= MK_MESSAGE;
+      if(p->needppr & mask)
       {
-        p->wdtr_pending |= mask;
-        scb->flags |= SCB_MSGOUT_WDTR;
-        hscb->control &= DISCENB;
-        hscb->control |= MK_MESSAGE;
-        scb->tag_action = 0;
+        scb->flags |= SCB_MSGOUT_PPR;
       }
-      else
+      else if(p->needwdtr & mask)
       {
-        aic7xxx_build_negotiation_cmnd(p, cmd, TARGET_INDEX(cmd));
+        scb->flags |= SCB_MSGOUT_WDTR;
       }
-    }
-    else if ( (p->needsdtr & mask) && !(p->sdtr_pending & mask) &&
-              !(p->wdtr_pending & mask) )
-    {
-      if (cmd == p->dev_sdtr_cmnd[TARGET_INDEX(cmd)])
+      else if(p->needsdtr & mask)
       {
-        p->sdtr_pending |= mask;
         scb->flags |= SCB_MSGOUT_SDTR;
-        hscb->control &= DISCENB;
-        hscb->control |= MK_MESSAGE;
-        scb->tag_action = 0;
       }
-      else if (cmd != p->dev_wdtr_cmnd[TARGET_INDEX(cmd)])
-      {
-        aic7xxx_build_negotiation_cmnd(p, cmd, TARGET_INDEX(cmd));
-      }
-    }
-#else
-    if ( (p->needwdtr & mask) && !(p->wdtr_pending & mask) &&
-         !(p->sdtr_pending & mask) && (cmd->lun == 0) )
-    {
-      p->wdtr_pending |= mask;
-      scb->flags |= SCB_MSGOUT_WDTR;
-      hscb->control &= DISCENB;
-      hscb->control |= MK_MESSAGE;
-      scb->tag_action = 0;
-#ifdef AIC7XXX_VERBOSE_DEBUGGING
-      if (aic7xxx_verbose > 0xffff)
-        printk(INFO_LEAD "Building WDTR command.\n", p->host_no,
-               CTL_OF_CMD(cmd));
-#endif
-    }
-    else if ( (p->needsdtr & mask) && !(p->wdtr_pending & mask) &&
-              !(p->sdtr_pending & mask) && (cmd->lun == 0) )
-    {
-      p->sdtr_pending |= mask;
-      scb->flags |= SCB_MSGOUT_SDTR;
-      hscb->control &= DISCENB;
-      hscb->control |= MK_MESSAGE;
-      scb->tag_action = 0;
-#ifdef AIC7XXX_VERBOSE_DEBUGGING
-      if (aic7xxx_verbose > 0xffff)
-        printk(INFO_LEAD "Building SDTR command.\n", p->host_no,
-               CTL_OF_CMD(cmd));
-#endif
     }
-#endif
+  }
+  if ( !(p->dtr_pending & mask) &&
+        ( (p->needppr & mask) ||
+          (p->needwdtr & mask) ||
+          (p->needsdtr & mask) ) )
+  {
+    aic7xxx_build_negotiation_cmnd(p, cmd, tindex);
   }
   hscb->target_channel_lun = ((cmd->target << 4) & 0xF0) |
         ((cmd->channel & 0x01) << 3) | (cmd->lun & 0x07);
@@ -9897,7 +10542,6 @@ aic7xxx_buildscb(struct aic7xxx_host *p, Scsi_Cmnd *cmd,
     scb->sg_count = cmd->use_sg;
     hscb->SG_segment_count = cmd->use_sg;
     hscb->SG_list_pointer = cpu_to_le32(VIRT_TO_BUS(&scb->sg_list[1]));
-
   }
   else
   {
@@ -9922,12 +10566,6 @@ aic7xxx_buildscb(struct aic7xxx_host *p, Scsi_Cmnd *cmd,
       hscb->data_pointer = 0;
     }
   }
-#ifdef AIC7XXX_VERBOSE_DEBUGGING
-  if((cmd->cmnd[0] == TEST_UNIT_READY) && (aic7xxx_verbose & VERBOSE_PROBE2))
-  {
-    aic7xxx_print_scb(p, scb);
-  }
-#endif
 }
 
 /*+F*************************************************************************
@@ -10262,13 +10900,14 @@ aic7xxx_panic_abort(struct aic7xxx_host *p, Scsi_Cmnd *cmd)
     if(p->dev_flags[i] & DEVICE_PRESENT)
     {
       mask = (0x01 << i);
-      printk(INFO_LEAD "dev_flags=0x%x, WDTR:%c/%c/%c, SDTR:%c/%c/%c,"
-             " q_depth=%d:%d\n",
+      printk(INFO_LEAD "dev_flags=0x%x, Pending:%c, PPR:%c/%c, WDTR:%c/%c, "
+             "SDTR:%c/%c, q_depth=%d:%d\n",
         p->host_no, 0, i, 0, p->dev_flags[i],
-        (p->wdtr_pending & mask) ? 'Y' : 'N',
+        (p->dtr_pending & mask) ? 'Y' : 'N',
+        (p->needppr & mask) ? 'Y' : 'N',
+        (p->needppr_copy & mask) ? 'Y' : 'N',
         (p->needwdtr & mask) ? 'Y' : 'N',
         (p->needwdtr_copy & mask) ? 'Y' : 'N',
-        (p->sdtr_pending & mask) ? 'Y' : 'N',
         (p->needsdtr & mask) ? 'Y' : 'N',
         (p->needsdtr_copy & mask) ? 'Y' : 'N',
         p->dev_active_cmds[i],
@@ -10347,13 +10986,13 @@ aic7xxx_panic_abort(struct aic7xxx_host *p, Scsi_Cmnd *cmd)
      * We haven't found the offending SCB yet, and it should be around
      * somewhere, so go look for it in the cards SCBs.
      */
-    printk("SCBPTR CONTROL TAG PREV NEXT\n");
+    printk("SCBPTR CONTROL TAG NEXT\n");
     for(i=0; i<p->scb_data->maxhscbs; i++)
     {
       aic_outb(p, i, SCBPTR);
-      printk("   %3d      %02x  %02x   %02x   %02x\n", i,
+      printk("   %3d      %02x  %02x   %02x\n", i,
              aic_inb(p, SCB_CONTROL), aic_inb(p, SCB_TAG),
-             aic_inb(p, SCB_PREV), aic_inb(p, SCB_NEXT));
+             aic_inb(p, SCB_NEXT));
     }
   }
   
@@ -10569,21 +11208,13 @@ aic7xxx_abort(Scsi_Cmnd *cmd)
   if ((found == 0) && (scb->flags & SCB_WAITINGQ))
   {
     int tindex = TARGET_INDEX(cmd);
-#ifdef AIC7XXX_FAKE_NEGOTIATION_CMDS
     unsigned short mask;
 
     mask = (1 << tindex);
 
-    if (p->wdtr_pending & mask)
-    {
-      if (p->dev_wdtr_cmnd[tindex]->next != cmd)
-        found = 1;
-      else
-        found = 0;
-    }
-    else if (p->sdtr_pending & mask)
+    if (p->dtr_pending & mask)
     {
-      if (p->dev_sdtr_cmnd[tindex]->next != cmd)
+      if (p->dev_dtr_cmnd[tindex]->next != cmd)
         found = 1;
       else
         found = 0;
@@ -10606,7 +11237,6 @@ aic7xxx_abort(Scsi_Cmnd *cmd)
       DRIVER_UNLOCK
       return(SCSI_ABORT_PENDING);
     }
-#endif
     if (aic7xxx_verbose & VERBOSE_ABORT_PROCESS) 
       printk(INFO_LEAD "SCB found on waiting list and "
           "aborted.\n", p->host_no, CTL_OF_SCB(scb));
@@ -10864,6 +11494,8 @@ aic7xxx_reset(Scsi_Cmnd *cmd, unsigned int flags)
       if(aic7xxx_verbose & VERBOSE_RESET_RETURN)
         printk(INFO_LEAD "SCB on qoutfifo, returning.\n", p->host_no,
           CTL_OF_SCB(scb));
+      aic7xxx_run_done_queue(p, TRUE);
+      aic7xxx_run_waiting_queues(p);
       unpause_sequencer(p, FALSE);
       DRIVER_UNLOCK
       return(SCSI_RESET_NOT_RUNNING);
@@ -11034,16 +11666,21 @@ aic7xxx_reset(Scsi_Cmnd *cmd, unsigned int flags)
 int
 aic7xxx_biosparam(Disk *disk, kdev_t dev, int geom[])
 {
-  int heads, sectors, cylinders;
+  int heads, sectors, cylinders, ret;
   struct aic7xxx_host *p;
+  struct buffer_head *bh;
 
   p = (struct aic7xxx_host *) disk->device->host->hostdata;
+  bh = bread(MKDEV(MAJOR(dev), MINOR(dev)&~0xf), 0, 1024);
 
-  /*
-   * XXX - if I could portably find the card's configuration
-   *       information, then this could be autodetected instead
-   *       of left to a boot-time switch.
-   */
+  if ( bh )
+  {
+    ret = scsi_partsize(bh, disk->capacity, &geom[2], &geom[0], &geom[1]);
+    brelse(bh);
+    if ( ret != -1 )
+      return(ret);
+  }
+  
   heads = 64;
   sectors = 32;
   cylinders = disk->capacity / (heads * sectors);
@@ -11150,6 +11787,12 @@ aic7xxx_print_card(struct aic7xxx_host *p)
           0x84, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9a, 0x9a, 0x9f, 0x9f,
           0xe0, 0xf1, 0xf4, 0xf4, 0xf6, 0xf6, 0xf8, 0xf8, 0xfa, 0xfc,
           0xfe, 0xff} },
+    {12, {0x00, 0x05, 0x08, 0x11, 0x18, 0x1f, 0x60, 0x60, 0x62, 0x66, /*7892*/
+          0x84, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9a, 0x9a, 0x9c, 0x9f,
+          0xe0, 0xf1, 0xf4, 0xfc} },
+    {12, {0x00, 0x05, 0x08, 0x11, 0x18, 0x1f, 0x60, 0x60, 0x62, 0x66, /*7899*/
+          0x84, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9a, 0x9a, 0x9c, 0x9f,
+          0xe0, 0xf1, 0xf4, 0xfc} },
   };
 #ifdef CONFIG_PCI
   static struct register_ranges cards_ns[] = {
@@ -11164,6 +11807,10 @@ aic7xxx_print_card(struct aic7xxx_host *p)
     { 5, {0x04, 0x08, 0x0c, 0x1b, 0x30, 0x34, 0x3c, 0x43, 0xdc, 0xe3} },
     { 6, {0x04, 0x08, 0x0c, 0x0e, 0x10, 0x17, 0x30, 0x34, 0x3c, 0x47,
           0xdc, 0xe3} },
+    { 6, {0x04, 0x08, 0x0c, 0x1b, 0x30, 0x34, 0x3c, 0x43, 0xdc, 0xe3,
+          0xff, 0xff} },
+    { 6, {0x04, 0x08, 0x0c, 0x1b, 0x30, 0x34, 0x3c, 0x43, 0xdc, 0xe3,
+          0xff, 0xff} },
     { 6, {0x04, 0x08, 0x0c, 0x1b, 0x30, 0x34, 0x3c, 0x43, 0xdc, 0xe3,
           0xff, 0xff} }
   };
index 945cefbcc8348dd7da6df244d9eea9af86751b9f..7f348aa9393ac25d0a8330da19069bc2772f0746 100644 (file)
@@ -213,6 +213,25 @@ register STCNT {
        access_mode RW
 }
 
+/*
+ * Option Mode Register (Alternate Mode) (p. 5-198)
+ * This register is used to set certain options on Ultra3 based chips.
+ * The chip must be in alternate mode (bit ALT_MODE in SFUNCT must be set)
+ */
+register OPTIONMODE {
+       address                 0x008
+       access_mode RW
+       bit     AUTORATEEN      0x80
+       bit     AUTOACKEN       0x40
+       bit     ATNMGMNTEN      0x20
+       bit     BUSFREEREV      0x10
+       bit     EXPPHASEDIS     0x08
+       bit     SCSIDATL_IMGEN  0x04
+       bit     AUTO_MSGOUT_DE  0x02
+       bit     DIS_MSGIN_DUALEDGE      0x01
+}
+
+
 /*
  * Clear SCSI Interrupt 0 (p. 3-20)
  * Writing a 1 to a bit clears the associated SCSI Interrupt in SSTAT0.
@@ -285,7 +304,13 @@ register SSTAT2 {
        address                 0x00d
        access_mode RO
        bit     OVERRUN         0x80
+       bit     SHVALID         0x40
+       bit     WIDE_RES        0x20
        bit     EXP_ACTIVE      0x10    /* SCSI Expander Active */
+       bit     CRCVALERR       0x08    /* CRC Value Error */
+       bit     CRCENDERR       0x04    /* CRC End Error */
+       bit     CRCREQERR       0x02    /* CRC REQ Error */
+       bit     DUAL_EDGE_ERROR 0x01    /* Invalid pins for Dual Edge phase */
        mask    SFCNT           0x1f
 }
 
@@ -709,6 +734,7 @@ register ERROR {
        bit     SQPARERR        0x08
        bit     ILLOPCODE       0x04
        bit     ILLSADDR        0x02
+       bit     DSCTMOUT        0x02    /* Ultra3 only */
        bit     ILLHADDR        0x01
 }
 
@@ -787,6 +813,17 @@ register QINCNT    {
        access_mode RO
 }
 
+/*
+ * SCSIDATL IMAGE Register (p. 5-104)
+ * Write to this register also go to SCSIDATL but this register will preserve
+ * the data for later reading as long as the SCSIDATL_IMGEN bit in the
+ * OPTIONMODE register is set.
+ */
+register SCSIDATL_IMG {
+       address                 0x09c
+       access_mode RW
+}
+
 /*
  * Queue Out FIFO (p. 3-61)
  * Queue of SCBs that have completed and await the host
@@ -796,6 +833,21 @@ register QOUTFIFO {
        access_mode WO
 }
 
+/*
+ * CRC Control 1 Register (p. 5-105)
+ * Control bits for the Ultra 160/m CRC facilities
+ */
+register CRCCONTROL1 {
+       address                 0x09d
+       access_mode RW
+       bit     CRCONSEEN       0x80 /* CRC ON Single Edge ENable */
+       bit     CRCVALCHKEN     0x40 /* CRC Value Check Enable */
+       bit     CRCENDCHKEN     0x20 /* CRC End Check Enable */
+       bit     CRCREQCHKEN     0x10
+       bit     TARGCRCENDEN    0x08 /* Enable End CRC transfer when target */
+       bit     TARGCRCCNTEN    0x40 /* Enable CRC transfer when target */
+}
+
 /*
  * Queue Out Count (p. 3-61)
  * Number of queued SCBs in the Out FIFO
@@ -805,12 +857,28 @@ register QOUTCNT {
        access_mode RO
 }
 
+/*
+ * SCSI Phase Register (p. 5-106)
+ * Current bus phase
+ */
+register SCSIPHASE {
+       address                 0x09e
+       access_mode RO
+       bit     SP_STATUS               0x20
+       bit     SP_COMMAND              0x10
+       bit     SP_MSG_IN               0x08
+       bit     SP_MSG_OUT              0x04
+       bit     SP_DATA_IN              0x02
+       bit     SP_DATA_OUT     0x01
+}
+
 /*
  * Special Function
  */
 register SFUNCT {
        address                 0x09f
        access_mode RW
+       bit     ALT_MODE        0x80
 }
 
 /*
@@ -960,19 +1028,29 @@ register HNSCB_QOFF {
        address                 0x0F4
 }
 
+register HESCB_QOFF {
+       address                 0x0F5
+}
+
 register SNSCB_QOFF {
        address                 0x0F6
 }
 
+register SESCB_QOFF {
+       address                 0x0F7
+}
+
 register SDSCB_QOFF {
        address                 0x0F8
 }
 
 register QOFF_CTLSTA {
        address                 0x0FA
+       bit     ESTABLISH_SCB_AVAIL     0x80
        bit     SCB_AVAIL       0x40
        bit     SNSCB_ROLLOVER  0x20
        bit     SDSCB_ROLLOVER  0x10
+       bit     SESCB_ROLLOVER  0x08
        mask    SCB_QSIZE       0x07
        mask    SCB_QSIZE_256   0x06
 }
index 60a718a3a6796abc4983efcc03d517e9db04e054..4cb8e82c04162b4d611da2c5b304e013145f96a5 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Adaptec 274x/284x/294x device driver firmware for Linux and FreeBSD.
  *
- * Copyright (c) 1994-1998 Justin Gibbs.
+ * Copyright (c) 1994-1999 Justin Gibbs.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -58,6 +58,7 @@
 
 reset:
        clr     SCSISIGO;               /* De-assert BSY */
+       and     SXFRCTL1, ~BITBUCKET;
        /* Always allow reselection */
        if ((p->flags & AHC_TARGETMODE) != 0) {
                mvi     SCSISEQ, ENSELI|ENRSELI|ENAUTOATNP;
@@ -72,8 +73,8 @@ reset:
        }
 
        call    clear_target_state;
-       and     SXFRCTL0, ~SPIOEN;
 poll_for_work:
+       and     SXFRCTL0, ~SPIOEN;
        if ((p->features & AHC_QUEUE_REGS) == 0) {
                mov     A, QINPOS;
        }
@@ -134,6 +135,21 @@ dma_queued_scb:
        mvi     DMAPARAMS, HDMAEN|DIRECTION|FIFORESET;
        mov     RETURN_2         call dma_scb;
 
+/*
+ * Preset the residual fields in case we never go through a data phase.
+ * This isn't done by the host so we can avoid a DMA to clear these
+ * fields for the normal case of I/O that completes without underrun
+ * or overrun conditions.
+ */
+       if ((p->features & AHC_CMD_CHAN) != 0) {
+               bmov    SCB_RESID_DCNT, SCB_DATACNT, 3;
+       } else {
+               mov     SCB_RESID_DCNT[0],SCB_DATACNT[0];
+               mov     SCB_RESID_DCNT[1],SCB_DATACNT[1];
+               mov     SCB_RESID_DCNT[2],SCB_DATACNT[2];
+       }
+       mov     SCB_RESID_SGCNT, SCB_SGCOUNT;
+
 start_scb:
        /*
         * Place us on the waiting list in case our selection
@@ -174,8 +190,7 @@ initialize_scsiid:
  * set in SXFRCTL0.
  */
 initialize_channel:
-       or      A, CLRSTCNT|CLRCHN, SINDEX;
-       or      SXFRCTL0, A;
+       or      SXFRCTL0, CLRSTCNT|CLRCHN, SINDEX;
        if ((p->features & AHC_ULTRA) != 0) {
 ultra:
                mvi     SINDEX, ULTRA_ENB+1;
@@ -402,29 +417,42 @@ await_busfree:
        mvi     INTSTAT, BAD_PHASE;
        
 clear_target_state:
-       clr     DFCNTRL;                /*
-                                        * We assume that the kernel driver
-                                        * may reset us at any time, even
-                                        * in the middle of a DMA, so clear
-                                        * DFCNTRL too.
-                                        */
-       clr     SCSIRATE;               /*
-                                        * We don't know the target we will
-                                        * connect to, so default to narrow
-                                        * transfers to avoid parity problems.
-                                        */
-       and     SXFRCTL0, ~(FAST20);
+       /*
+        * We assume that the kernel driver may reset us
+        * at any time, even in the middle of a DMA, so
+        * clear DFCNTRL too.
+        */
+       clr     DFCNTRL;
+
+       /*
+        * We don't know the target we will connect to,
+        * so default to narrow transfers to avoid
+        * parity problems.
+        */
+       if ((p->features & AHC_ULTRA2) != 0) {
+               bmov    SCSIRATE, ALLZEROS, 2;
+       } else {
+               clr     SCSIRATE;
+               and     SXFRCTL0, ~(FAST20);
+       }
        mvi     LASTPHASE, P_BUSFREE;
        /* clear target specific flags */
-       and     SEQ_FLAGS, (WIDE_BUS|TWIN_BUS) ret;
+       clr     SEQ_FLAGS ret;
 
 /*
  * If we re-enter the data phase after going through another phase, the
  * STCNT may have been cleared, so restore it from the residual field.
  */
 data_phase_reinit:
-       mvi     DINDEX, STCNT;
-       mvi     SCB_RESID_DCNT  call bcopy_3;
+       if ((p->features & AHC_CMD_CHAN) != 0) {
+               if ((p->features & AHC_ULTRA2) != 0) {
+                       bmov    HCNT, SCB_RESID_DCNT, 3;
+               }
+               bmov    STCNT, SCB_RESID_DCNT, 3;
+       } else {
+               mvi     DINDEX, STCNT;
+               mvi     SCB_RESID_DCNT  call bcopy_3;
+       }
        jmp     data_phase_loop;
 
 p_data:
@@ -455,20 +483,16 @@ p_data:
         */
        if ((p->features & AHC_CMD_CHAN) != 0) {
                bmov    HADDR, SCB_DATAPTR, 7;
+               bmov    STCNT, HCNT, 3;
+               bmov    SG_COUNT, SCB_SGCOUNT, 5;
        } else {
                mvi     DINDEX, HADDR;
                mvi     SCB_DATAPTR     call bcopy_7;
-       }
-
-       if ((p->features & AHC_ULTRA2) == 0) {
                call    set_stcnt_from_hcnt;
+               mvi     DINDEX, SG_COUNT;
+               mvi     SCB_SGCOUNT     call bcopy_5;
        }
 
-       mov     SG_COUNT,SCB_SGCOUNT;
-
-       mvi     DINDEX, SG_NEXT;
-       mvi     SCB_SGPTR       call bcopy_4;
-
 data_phase_loop:
 /* Guard against overruns */
        test    SG_COUNT, 0xff jnz data_phase_inbounds;
@@ -480,8 +504,11 @@ data_phase_loop:
  */
        or      SXFRCTL1,BITBUCKET;
        and     DMAPARAMS, ~(HDMAEN|SDMAEN);
-       if ((p->features & AHC_ULTRA2) != 0) {
-               bmov    HCNT, ALLONES, 3;
+       if ((p->features & AHC_CMD_CHAN) != 0) {
+               if ((p->features & AHC_ULTRA2) != 0) {
+                       bmov    HCNT, ALLONES, 3;
+               }
+               bmov    STCNT, ALLONES, 3;
        } else {
                mvi     STCNT[0], 0xFF;
                mvi     STCNT[1], 0xFF;
@@ -489,23 +516,21 @@ data_phase_loop:
        }
 data_phase_inbounds:
 /* If we are the last SG block, tell the hardware. */
+if ((p->features & AHC_ULTRA2) == 0) {
        cmp     SG_COUNT,0x01 jne data_phase_wideodd;
-       if ((p->features & AHC_ULTRA2) != 0) {
-               or      SG_CACHEPTR, LAST_SEG;
-       } else {
-               and     DMAPARAMS, ~WIDEODD;
-       }
+       and     DMAPARAMS, ~WIDEODD;
+}
 data_phase_wideodd:
        if ((p->features & AHC_ULTRA2) != 0) {
                mov     SINDEX, ALLONES;
                mov     DFCNTRL, DMAPARAMS;
-               test    SSTAT0, SDONE jnz .;/* Wait for preload to complete */
+               test    SSTAT0, SDONE jnz .;
 data_phase_dma_loop:
-               test    SSTAT0, SDONE jnz data_phase_dma_done;
+               test    SSTAT0, SDONE jnz data_phase_dma_done;
                test    SSTAT1,PHASEMIS jz data_phase_dma_loop; /* ie. underrun */
 data_phase_dma_phasemis:
                test    SSTAT0,SDONE    jnz . + 2;
-               mov     SINDEX,ALLZEROS;        /* Remeber the phasemiss */
+               clr     SINDEX;                 /* Remember the phasemiss */
        } else {
                mov     DMAPARAMS  call dma;
        }
@@ -554,6 +579,9 @@ sg_load:
                mvi     CCSGCTL, CCSGRESET;
 prefetched_segs_avail:
                bmov    HADDR, CCSGRAM, 8;
+               if ((p->features & AHC_ULTRA2) == 0) {
+                       bmov    STCNT, HCNT, 3;
+               }
        } else {
                mvi     DINDEX, HADDR;
                mvi     SG_NEXT call bcopy_4;
@@ -575,10 +603,6 @@ prefetched_segs_avail:
                 * };
                 */
                mvi     HADDR   call dfdat_in_7;
-       }
-
-       if ((p->features & AHC_ULTRA2) == 0) {
-               /* Load STCNT as well.  It is a mirror of HCNT */
                call    set_stcnt_from_hcnt;
        }
 
@@ -587,28 +611,33 @@ prefetched_segs_avail:
        add     SG_NEXT[0],SG_SIZEOF;
        adc     SG_NEXT[1],A;
 
+       test    SSTAT1, REQINIT jz .;
        test    SSTAT1,PHASEMIS jz data_phase_loop;
-       /* Ensure the last seg is visable at the shaddow layer */
+
        if ((p->features & AHC_ULTRA2) != 0) {
-               or      DFCNTRL, PRELOADEN;
+               mov     DFCNTRL, DMAPARAMS;
+               test    SSTAT0, SDONE jnz .;
        }
 
 data_phase_finish:
-       if ((p->features & AHC_ULTRA2) != 0) {
-               call    ultra2_dmafinish;
-       }
 /*
  * After a DMA finishes, save the SG and STCNT residuals back into the SCB
  * We use STCNT instead of HCNT, since it's a reflection of how many bytes 
  * were transferred on the SCSI (as opposed to the host) bus.
  */
-       mov     SCB_RESID_DCNT[0],STCNT[0];
-       mov     SCB_RESID_DCNT[1],STCNT[1];
-       mov     SCB_RESID_DCNT[2],STCNT[2];
-       mov     SCB_RESID_SGCNT, SG_COUNT;
-
        if ((p->features & AHC_ULTRA2) != 0) {
-               or      SXFRCTL0, CLRSTCNT|CLRCHN;
+               call    ultra2_dmafinish;
+       }
+       if ((p->features & AHC_ULTRA2) == 0) {
+               if ((p->features & AHC_CMD_CHAN) != 0) {
+                       bmov    SCB_RESID_DCNT, STCNT, 3;
+                       mov     SCB_RESID_SGCNT, SG_COUNT;
+               } else {
+                       mov     SCB_RESID_DCNT[0],STCNT[0];
+                       mov     SCB_RESID_DCNT[1],STCNT[1];
+                       mov     SCB_RESID_DCNT[2],STCNT[2];
+                       mov     SCB_RESID_SGCNT, SG_COUNT;
+               }
        }
 
        jmp     ITloop;
@@ -616,7 +645,6 @@ data_phase_finish:
 data_phase_overrun:
        if ((p->features & AHC_ULTRA2) != 0) {
                call    ultra2_dmafinish;
-               or      SXFRCTL0, CLRSTCNT|CLRCHN;
        }
 /*
  * Turn off BITBUCKET mode and notify the host
@@ -635,6 +663,9 @@ ultra2_dmafinish:
 ultra2_dmahalt:
                and     DFCNTRL, ~(SCSIEN|HDMAEN);
                test    DFCNTRL, HDMAEN jnz .;
+               bmov    SCB_RESID_DCNT, STCNT, 3;
+               mov     SCB_RESID_SGCNT, SG_COUNT;
+               or      SXFRCTL0, CLRSTCNT|CLRCHN;
                ret;
        }
 
@@ -647,25 +678,32 @@ p_command:
 /*
  * Load HADDR and HCNT.
  */
-       if ((p->features & AHC_ULTRA2) != 0) {
-               or      SG_CACHEPTR, LAST_SEG;
-       }
-
        if ((p->features & AHC_CMD_CHAN) != 0) {
                bmov    HADDR, SCB_CMDPTR, 5;
                bmov    HCNT[1], ALLZEROS, 2;
+               if ((p->features & AHC_ULTRA2) == 0) {
+                       bmov    STCNT, HCNT, 3;
+               }
        } else {
                mvi     DINDEX, HADDR;
                mvi     SCB_CMDPTR      call bcopy_5;
                clr     HCNT[1];
                clr     HCNT[2];
+               call    set_stcnt_from_hcnt;
        }
 
        if ((p->features & AHC_ULTRA2) == 0) {
-               call    set_stcnt_from_hcnt;
                mvi     (SCSIEN|SDMAEN|HDMAEN|DIRECTION|FIFORESET) call dma;
        } else {
-               mvi     (PRELOADEN|SCSIEN|HDMAEN|DIRECTION) call dma;
+               mvi     DFCNTRL, (PRELOADEN|SCSIEN|HDMAEN|DIRECTION);
+               test    SSTAT0, SDONE jnz .;
+p_command_dma_loop:
+               test    SSTAT0, DMADONE jnz p_command_ultra2_dma_done;
+               test    SSTAT1,PHASEMIS jz p_command_dma_loop;  /* ie. underrun */
+p_command_ultra2_dma_done:
+               and     DFCNTRL, ~(SCSIEN|HDMAEN);
+               test    DFCNTRL, HDMAEN jnz .;
+               or      SXFRCTL0, CLRSTCNT|CLRCHN;
        }
        jmp     ITloop;
 
@@ -698,6 +736,8 @@ p_status:
  * in case the target decides to put us in this phase for some strange
  * reason.
  */
+p_mesgout_retry:
+       or      SCSISIGO,ATNO,LASTPHASE;/* turn on ATN for the retry */
 p_mesgout:
        mov     SINDEX, MSG_OUT;
        cmp     SINDEX, MSG_IDENTIFYFLAG jne p_mesgout_from_host;
@@ -749,9 +789,7 @@ p_mesgout_onebyte:
  * that the target is requesting that the last message(s) be resent.
  */
        call    phase_lock;
-       cmp     LASTPHASE, P_MESGOUT    jne p_mesgout_done;
-       or      SCSISIGO,ATNO,LASTPHASE;/* turn on ATN for the retry */
-       jmp     p_mesgout;
+       cmp     LASTPHASE, P_MESGOUT    je p_mesgout_retry;
 
 p_mesgout_done:
        mvi     CLRSINT1,CLRATNO;       /* Be sure to turn ATNO off */
@@ -889,21 +927,23 @@ mesgin_disconnect:
  */
 mesgin_sdptrs:
        test    SEQ_FLAGS, DPHASE       jz mesgin_done;
-       mov     SCB_SGCOUNT,SG_COUNT;
-
-       /* The SCB SGPTR becomes the next one we'll download */
-       mvi     DINDEX, SCB_SGPTR;
-       mvi     SG_NEXT call bcopy_4;
-       
-       /* The SCB DATAPTR0 becomes the current SHADDR */
-       mvi     DINDEX, SCB_DATAPTR;
-       mvi     SHADDR          call bcopy_4;
-
-/*
- * Use the residual number since STCNT is corrupted by any message transfer.
- */
-       mvi     SCB_RESID_DCNT  call    bcopy_3;
-
+       /*
+        * The SCB SGPTR becomes the next one we'll download,
+        * and the SCB DATAPTR becomes the current SHADDR.
+        * Use the residual number since STCNT is corrupted by
+        * any message transfer.
+        */
+       if ((p->features & AHC_CMD_CHAN) != 0) {
+               bmov    SCB_SGCOUNT, SG_COUNT, 5;
+               bmov    SCB_DATAPTR, SHADDR, 4;
+               bmov    SCB_DATACNT, SCB_RESID_DCNT, 3;
+       } else {
+               mvi     DINDEX, SCB_SGCOUNT;
+               mvi     SG_COUNT        call bcopy_5;
+               mvi     DINDEX, SCB_DATAPTR;
+               mvi     SHADDR          call bcopy_4;
+               mvi     SCB_RESID_DCNT  call    bcopy_3;
+       }
        jmp     mesgin_done;
 
 /*
@@ -934,6 +974,7 @@ mesgin_identify:
        }
        or      SAVED_TCL,A;            /* SAVED_TCL should be complete now */
 
+       mvi     ARG_2, SCB_LIST_NULL;   /* SCBID of prev SCB in disc List */
        call    get_untagged_SCBID;
        cmp     ARG_1, SCB_LIST_NULL    je snoop_tag;
        if ((p->flags & AHC_PAGESCBS) != 0) {
@@ -964,19 +1005,8 @@ snoop_tag_loop:
 get_tag:
        mvi     ARG_1   call inb_next;  /* tag value */
 
-       if ((p->flags & AHC_PAGESCBS) == 0) {
-index_by_tag:
-               mov     SCBPTR,ARG_1;
-               test    SCB_CONTROL,TAG_ENB     jz  not_found;
-               mov     SCBPTR  call rem_scb_from_disc_list;
-       } else {
-               /*
-                * Ensure that the SCB the tag points to is for
-                * an SCB transaction to the reconnecting target.
-                */
 use_retrieveSCB:
-               call    retrieveSCB;
-       }
+       call    retrieveSCB;
 setup_SCB:
        mov     A, SAVED_TCL;
        cmp     SCB_TCL, A      jne not_found_cleanup_scb;
@@ -1079,6 +1109,7 @@ mesgin_phasemis:
  * host->scsi, or 0x39 for scsi->host.  The SCSI channel is cleared
  * during initialization.
  */
+if ((p->features & AHC_ULTRA2) == 0) {
 dma:
        mov     DFCNTRL,SINDEX;
 dma_loop:
@@ -1118,10 +1149,9 @@ dma_halt:
         * to drain the data fifo until there is space for the input
         * latch to drain and HDMAEN de-asserts.
         */
-       if ((p->features & AHC_ULTRA2) == 0) {
-               mov     NONE, DFDAT;
-       }
-       test    DFCNTRL, HDMAEN jnz dma_halt;
+       mov     NONE, DFDAT;
+       test    DFCNTRL, (SCSIEN|SDMAEN|HDMAEN) jnz dma_halt;
+}
 return:
        ret;
 
@@ -1150,6 +1180,7 @@ findSCB_by_SCBID:
        mov     A, ARG_1;                       /* Tag passed in ARG_1 */
        mvi     SCB_TAG jmp findSCB_loop;       /* &SCB_TAG -> SINDEX */
 findSCB_next:
+       mov     ARG_2, SCBPTR;
        cmp     SCB_NEXT, SCB_LIST_NULL je notFound;
        mov     SCBPTR,SCB_NEXT;
        dec     SINDEX;         /* Last comparison moved us too far */
@@ -1173,19 +1204,15 @@ retrieveSCB:
 
 /*
  * This routine expects SINDEX to contain the index of the SCB to be
- * removed and SCBPTR to be pointing to that SCB.
+ * removed, SCBPTR to be pointing to that SCB, and ARG_2 to be the
+ * SCBID of the SCB just previous to this one in the list or SCB_LIST_NULL
+ * if it is at the head.
  */
 rem_scb_from_disc_list:
 /* Remove this SCB from the disconnection list */
-       cmp     SCB_NEXT,SCB_LIST_NULL je unlink_prev;
-       mov     DINDEX, SCB_PREV;
-       mov     SCBPTR, SCB_NEXT;
-       mov     SCB_PREV, DINDEX;
-       mov     SCBPTR, SINDEX;
-unlink_prev:
-       cmp     SCB_PREV,SCB_LIST_NULL  je rHead;/* At the head of the list */
+       cmp     ARG_2, SCB_LIST_NULL    je rHead;
        mov     DINDEX, SCB_NEXT;
-       mov     SCBPTR, SCB_PREV;
+       mov     SCBPTR, ARG_2;
        mov     SCB_NEXT, DINDEX;
        mov     SCBPTR, SINDEX ret;
 rHead:
@@ -1285,9 +1312,10 @@ get_SCBID_from_host:
 phase_lock:     
        test    SSTAT1, REQINIT jz phase_lock;
        test    SSTAT1, SCSIPERR jnz phase_lock;
-       and     LASTPHASE, PHASE_MASK, SCSISIGI;
-       mov     SCSISIGO, LASTPHASE ret;
+       and     SCSISIGO, PHASE_MASK, SCSISIGI;
+       and     LASTPHASE, PHASE_MASK, SCSISIGI ret;
 
+if ((p->features & AHC_CMD_CHAN) == 0) {
 set_stcnt_from_hcnt:
        mov     STCNT[0], HCNT[0];
        mov     STCNT[1], HCNT[1];
@@ -1304,6 +1332,7 @@ bcopy_3:
        mov     DINDIR, SINDIR;
        mov     DINDIR, SINDIR;
        mov     DINDIR, SINDIR ret;
+}
 
 /*
  * Setup addr assuming that A is an index into
@@ -1407,12 +1436,14 @@ dfdat_in_7_continued:
  * Wait for DMA from host memory to data FIFO to complete, then disable
  * DMA and wait for it to acknowledge that it's off.
  */
+if ((p->features & AHC_CMD_CHAN) == 0) {
 dma_finish:
        test    DFSTATUS,HDONE  jz dma_finish;
        /* Turn off DMA */
        and     DFCNTRL, ~HDMAEN;
        test    DFCNTRL, HDMAEN jnz .;
        ret;
+}
 
 add_scb_to_free_list:
        if ((p->flags & AHC_PAGESCBS) != 0) {
@@ -1433,8 +1464,7 @@ dma_up_scb:
        mvi     DMAPARAMS, FIFORESET;
        mov     SCB_TAG         call dma_scb;
 unlink_disc_scb:
-       /* jmp instead of call since we want to return anyway */
-       mov     SCBPTR  jmp rem_scb_from_disc_list;
+       mov     DISCONNECTED_SCBH, SCB_NEXT ret;
 dequeue_free_scb:
        mov     SCBPTR, FREE_SCBH;
        mov     FREE_SCBH, SCB_NEXT ret;
@@ -1446,10 +1476,5 @@ add_scb_to_disc_list:
  * candidates for paging out an SCB if one is needed for a new command.
  * Modifying the disconnected list is a critical(pause dissabled) section.
  */
-       mvi     SCB_PREV, SCB_LIST_NULL;
        mov     SCB_NEXT, DISCONNECTED_SCBH;
-       mov     DISCONNECTED_SCBH, SCBPTR;
-       cmp     SCB_NEXT,SCB_LIST_NULL je return;
-       mov     SCBPTR,SCB_NEXT;
-       mov     SCB_PREV,DISCONNECTED_SCBH;
-       mov     SCBPTR,DISCONNECTED_SCBH ret;
+       mov     DISCONNECTED_SCBH, SCBPTR ret;
index 16c40138893204060a9e32e784639357f70e80af..a79f89c651732d5f4b466ae73ad35e14deac467d 100644 (file)
 #define MSG_EXT_WDTR_LEN       0x02
 #define MSG_EXT_WDTR_BUS_8_BIT 0x00
 #define MSG_EXT_WDTR_BUS_16_BIT        0x01
-#define MSG_EXT_WDTR_BUS_32_BIT        0x02 
+#define MSG_EXT_WDTR_BUS_32_BIT        0x02
+
+#define MSG_EXT_PPR     0x04
+#define MSG_EXT_PPR_LEN        0x06
+#define MSG_EXT_PPR_OPTION_ST 0x00
+#define MSG_EXT_PPR_OPTION_DT_CRC 0x02
+#define MSG_EXT_PPR_OPTION_DT_UNITS 0x03
+#define MSG_EXT_PPR_OPTION_DT_CRC_QUICK 0x04
+#define MSG_EXT_PPR_OPTION_DT_UNITS_QUICK 0x05
index 6e5eca864a8c6dc7ff469b371addf5b9e0350145..3314514f4f5f8f9b592a2bbb7e473c7d4c24967a 100644 (file)
@@ -160,21 +160,17 @@ aic7xxx_proc_info ( char *buffer, char **start, off_t offset, int length,
   size += sprintf(BLS, "%s", AIC7XXX_H_VERSION);
   size += sprintf(BLS, "\n");
   size += sprintf(BLS, "Compile Options:\n");
-#ifdef AIC7XXX_RESET_DELAY
-  size += sprintf(BLS, "  AIC7XXX_RESET_DELAY    : %d\n", AIC7XXX_RESET_DELAY);
+#ifdef CONFIG_AIC7XXX_TCQ_ON_BY_DEFAULT
+  size += sprintf(BLS, "  TCQ Enabled By Default : Enabled\n");
+#else
+  size += sprintf(BLS, "  TCQ Enabled By Default : Disabled\n");
 #endif
-  size += sprintf(BLS, "  AIC7XXX_TAGGED_QUEUEING: Adapter Support Enabled\n");
-  size += sprintf(BLS, "                             Check below to see "
-                       "which\n"
-                       "                             devices use tagged "
-                       "queueing\n");
-  size += sprintf(BLS, "  AIC7XXX_PAGE_ENABLE    : Enabled (This is no longer "
-                       "an option)\n");
 #ifdef AIC7XXX_PROC_STATS
   size += sprintf(BLS, "  AIC7XXX_PROC_STATS     : Enabled\n");
 #else
   size += sprintf(BLS, "  AIC7XXX_PROC_STATS     : Disabled\n");
 #endif
+  size += sprintf(BLS, "  AIC7XXX_RESET_DELAY    : %d\n", AIC7XXX_RESET_DELAY);
   size += sprintf(BLS, "\n");
   size += sprintf(BLS, "Adapter Configuration:\n");
   size += sprintf(BLS, "           SCSI Adapter: %s\n",
@@ -194,8 +190,21 @@ aic7xxx_proc_info ( char *buffer, char **start, off_t offset, int length,
     }
     if (p->features & AHC_WIDE)
       wide = "Wide ";
-    if (p->features & AHC_ULTRA2)
-      ultra = "Ultra2-LVD/SE ";
+    if (p->features & AHC_ULTRA3)
+    {
+      switch(p->chip & AHC_CHIPID_MASK)
+      {
+        case AHC_AIC7892:
+        case AHC_AIC7899:
+          ultra = "Ultra-160/m LVD/SE ";
+          break;
+        default:
+          ultra = "Ultra-3 LVD/SE ";
+          break;
+      }
+    }
+    else if (p->features & AHC_ULTRA2)
+      ultra = "Ultra-2 LVD/SE ";
     else if (p->features & AHC_ULTRA)
       ultra = "Ultra ";
     size += sprintf(BLS, "                           %s%sController%s\n",
@@ -250,11 +259,7 @@ aic7xxx_proc_info ( char *buffer, char **start, off_t offset, int length,
   }
   size += sprintf(BLS, " Tag Queue Enable Flags: 0x%04x\n", p->tagenable);
   size += sprintf(BLS, "Ordered Queue Tag Flags: 0x%04x\n", p->orderedtag);
-#ifdef AIC7XXX_CMDS_PER_LUN
-  size += sprintf(BLS, "Default Tag Queue Depth: %d\n", AIC7XXX_CMDS_PER_LUN);
-#else
-  size += sprintf(BLS, "Default Tag Queue Depth: %d\n", 8);
-#endif
+  size += sprintf(BLS, "Default Tag Queue Depth: %d\n", AIC7XXX_CMDS_PER_DEVICE);
   size += sprintf(BLS, "    Tagged Queue By Device array for aic7xxx host "
                        "instance %d:\n", p->instance);
   size += sprintf(BLS, "      {");
@@ -295,11 +300,12 @@ aic7xxx_proc_info ( char *buffer, char **start, off_t offset, int length,
     if (p->transinfo[target].cur_offset != 0)
     {
       struct aic7xxx_syncrate *sync_rate;
+      unsigned char options = p->transinfo[target].cur_options;
       int period = p->transinfo[target].cur_period;
       int rate = (p->transinfo[target].cur_width ==
                   MSG_EXT_WDTR_BUS_16_BIT) ? 1 : 0;
 
-      sync_rate = aic7xxx_find_syncrate(p, &period, AHC_SYNCRATE_ULTRA2);
+      sync_rate = aic7xxx_find_syncrate(p, &period, 0, &options);
       if (sync_rate != NULL)
       {
         size += sprintf(BLS, "%s MByte/sec, offset %d\n",
@@ -313,18 +319,21 @@ aic7xxx_proc_info ( char *buffer, char **start, off_t offset, int length,
       }
     }
     size += sprintf(BLS, "  Transinfo settings: ");
-    size += sprintf(BLS, "current(%d/%d/%d), ",
+    size += sprintf(BLS, "current(%d/%d/%d/%d), ",
                     p->transinfo[target].cur_period,
                     p->transinfo[target].cur_offset,
-                    p->transinfo[target].cur_width);
-    size += sprintf(BLS, "goal(%d/%d/%d), ",
+                    p->transinfo[target].cur_width,
+                    p->transinfo[target].cur_options);
+    size += sprintf(BLS, "goal(%d/%d/%d/%d), ",
                     p->transinfo[target].goal_period,
                     p->transinfo[target].goal_offset,
-                    p->transinfo[target].goal_width);
-    size += sprintf(BLS, "user(%d/%d/%d)\n",
+                    p->transinfo[target].goal_width,
+                    p->transinfo[target].goal_options);
+    size += sprintf(BLS, "user(%d/%d/%d/%d)\n",
                     p->transinfo[target].user_period,
                     p->transinfo[target].user_offset,
-                    p->transinfo[target].user_width);
+                    p->transinfo[target].user_width,
+                    p->transinfo[target].user_options);
 #ifdef AIC7XXX_PROC_STATS
     size += sprintf(BLS, "  Total transfers %ld (%ld reads and %ld writes)\n",
         sp->r_total + sp->w_total, sp->r_total, sp->w_total);
index d12d1b6e54a53ab30116bc1f6bb8c75a730915cc..b42750864288e66e2a1143273a5fca561c333229 100644 (file)
 
 #define        STCNT                           0x08
 
+#define        OPTIONMODE                      0x08
+#define                AUTORATEEN              0x80
+#define                AUTOACKEN               0x40
+#define                ATNMGMNTEN              0x20
+#define                BUSFREEREV              0x10
+#define                EXPPHASEDIS             0x08
+#define                SCSIDATL_IMGEN          0x04
+#define                AUTO_MSGOUT_DE          0x02
+#define                DIS_MSGIN_DUALEDGE      0x01
+
 #define        CLRSINT0                        0x0b
 #define                CLRSELDO                0x40
 #define                CLRSELDI                0x20
 
 #define        SSTAT2                          0x0d
 #define                OVERRUN                 0x80
+#define                SHVALID                 0x40
+#define                WIDE_RES                0x20
 #define                SFCNT                   0x1f
 #define                EXP_ACTIVE              0x10
+#define                CRCVALERR               0x08
+#define                CRCENDERR               0x04
+#define                CRCREQERR               0x02
+#define                DUAL_EDGE_ERROR         0x01
 
 #define        SSTAT3                          0x0e
 #define                SCSICNT                 0xf0
 #define                DPARERR                 0x10
 #define                SQPARERR                0x08
 #define                ILLOPCODE               0x04
+#define                DSCTMOUT                0x02
 #define                ILLSADDR                0x02
 #define                ILLHADDR                0x01
 
 
 #define        QINCNT                          0x9c
 
+#define        SCSIDATL_IMG                    0x9c
+
 #define        QOUTFIFO                        0x9d
 
+#define        CRCCONTROL1                     0x9d
+#define                CRCONSEEN               0x80
+#define                TARGCRCCNTEN            0x40
+#define                CRCVALCHKEN             0x40
+#define                CRCENDCHKEN             0x20
+#define                CRCREQCHKEN             0x10
+#define                TARGCRCENDEN            0x08
+
+#define        SCSIPHASE                       0x9e
+#define                SP_STATUS               0x20
+#define                SP_COMMAND              0x10
+#define                SP_MSG_IN               0x08
+#define                SP_MSG_OUT              0x04
+#define                SP_DATA_IN              0x02
+#define                SP_DATA_OUT             0x01
+
 #define        QOUTCNT                         0x9e
 
 #define        SFUNCT                          0x9f
+#define                ALT_MODE                0x80
 
 #define        SCB_CONTROL                     0xa0
 #define                MK_MESSAGE              0x80
 
 #define        HNSCB_QOFF                      0xf4
 
+#define        HESCB_QOFF                      0xf5
+
 #define        SNSCB_QOFF                      0xf6
 
+#define        SESCB_QOFF                      0xf7
+
 #define        SDSCB_QOFF                      0xf8
 
 #define        QOFF_CTLSTA                     0xfa
+#define                ESTABLISH_SCB_AVAIL     0x80
 #define                SCB_AVAIL               0x40
 #define                SNSCB_ROLLOVER          0x20
 #define                SDSCB_ROLLOVER          0x10
+#define                SESCB_ROLLOVER          0x08
 #define                SCB_QSIZE               0x07
 #define                SCB_QSIZE_256           0x06
 
index 9205cc4af388a6ee59534c06480694cab5ff6bbe..f11373fef1539ed88ee38b7cc9217d61379ffe27 100644 (file)
@@ -3,38 +3,44 @@
   */
 static unsigned char seqprog[] = {
        0xff, 0x6a, 0x06, 0x08,
+       0x7f, 0x02, 0x04, 0x08,
        0x32, 0x6a, 0x00, 0x00,
        0x12, 0x6a, 0x00, 0x00,
        0xff, 0x6a, 0xd6, 0x09,
        0xff, 0x6a, 0xdc, 0x09,
-       0x00, 0x65, 0x38, 0x59,
+       0x00, 0x65, 0x42, 0x59,
        0xf7, 0x01, 0x02, 0x08,
        0xff, 0x4e, 0xc8, 0x08,
        0xbf, 0x60, 0xc0, 0x08,
-       0x60, 0x0b, 0x7c, 0x68,
+       0x60, 0x0b, 0x86, 0x68,
        0x40, 0x00, 0x0e, 0x68,
        0x08, 0x1f, 0x3e, 0x10,
-       0x60, 0x0b, 0x7c, 0x68,
+       0x60, 0x0b, 0x86, 0x68,
        0x40, 0x00, 0x0e, 0x68,
        0x08, 0x1f, 0x3e, 0x10,
-       0xff, 0x3e, 0x3e, 0x60,
-       0x40, 0xfa, 0x10, 0x78,
+       0xff, 0x3e, 0x4a, 0x60,
+       0x40, 0xfa, 0x12, 0x78,
        0xff, 0xf6, 0xd4, 0x08,
        0x01, 0x4e, 0x9c, 0x18,
        0x40, 0x60, 0xc0, 0x00,
-       0x00, 0x4d, 0x10, 0x70,
+       0x00, 0x4d, 0x12, 0x70,
        0x01, 0x4e, 0x9c, 0x18,
        0xbf, 0x60, 0xc0, 0x08,
-       0x00, 0x6a, 0x72, 0x5c,
+       0x00, 0x6a, 0x92, 0x5c,
        0xff, 0x4e, 0xc8, 0x18,
-       0x02, 0x6a, 0x88, 0x5b,
+       0x02, 0x6a, 0xa8, 0x5b,
        0xff, 0x52, 0x20, 0x09,
        0x0d, 0x6a, 0x6a, 0x00,
-       0x00, 0x52, 0xfe, 0x5b,
+       0x00, 0x52, 0x1e, 0x5c,
+       0x03, 0xb0, 0x52, 0x31,
+       0xff, 0xb0, 0x52, 0x09,
+       0xff, 0xb1, 0x54, 0x09,
+       0xff, 0xb2, 0x56, 0x09,
+       0xff, 0xa3, 0x50, 0x09,
        0xff, 0x3e, 0x74, 0x09,
        0xff, 0x90, 0x7c, 0x08,
        0xff, 0x3e, 0x20, 0x09,
-       0x00, 0x65, 0x44, 0x58,
+       0x00, 0x65, 0x50, 0x58,
        0x00, 0x65, 0x0e, 0x40,
        0xf7, 0x1f, 0xca, 0x08,
        0x08, 0xa1, 0xc8, 0x08,
@@ -47,51 +53,50 @@ static unsigned char seqprog[] = {
        0x0f, 0x05, 0x0a, 0x08,
        0x00, 0x05, 0x0a, 0x00,
        0x5a, 0x6a, 0x00, 0x04,
-       0x12, 0x65, 0xc8, 0x00,
-       0x00, 0x01, 0x02, 0x00,
+       0x12, 0x65, 0x02, 0x00,
        0x31, 0x6a, 0xca, 0x00,
-       0x80, 0x37, 0x64, 0x68,
+       0x80, 0x37, 0x6e, 0x68,
        0xff, 0x65, 0xca, 0x18,
        0xff, 0x37, 0xdc, 0x08,
        0xff, 0x6e, 0xc8, 0x08,
-       0x00, 0x6c, 0x6c, 0x78,
+       0x00, 0x6c, 0x76, 0x78,
        0x20, 0x01, 0x02, 0x00,
        0x4c, 0x37, 0xc8, 0x28,
-       0x08, 0x1f, 0x74, 0x78,
+       0x08, 0x1f, 0x7e, 0x78,
        0x08, 0x37, 0x6e, 0x00,
        0x08, 0x64, 0xc8, 0x00,
        0x70, 0x64, 0xca, 0x18,
        0xff, 0x6c, 0x0a, 0x08,
        0x20, 0x64, 0xca, 0x18,
        0xff, 0x6c, 0x08, 0x0c,
-       0x40, 0x0b, 0x04, 0x69,
-       0x80, 0x0b, 0xf6, 0x78,
+       0x40, 0x0b, 0x0e, 0x69,
+       0x80, 0x0b, 0x00, 0x79,
        0xa4, 0x6a, 0x06, 0x00,
        0x40, 0x6a, 0x16, 0x00,
-       0x10, 0x03, 0xf2, 0x78,
+       0x10, 0x03, 0xfc, 0x78,
        0xff, 0x50, 0xc8, 0x08,
        0x88, 0x6a, 0xcc, 0x00,
-       0x49, 0x6a, 0xee, 0x5b,
+       0x49, 0x6a, 0x0e, 0x5c,
        0x01, 0x6a, 0x26, 0x01,
        0xff, 0x6a, 0xca, 0x08,
        0x08, 0x01, 0x02, 0x00,
-       0x02, 0x0b, 0x92, 0x78,
+       0x02, 0x0b, 0x9c, 0x78,
        0xf7, 0x01, 0x02, 0x08,
        0xff, 0x06, 0xcc, 0x08,
        0xff, 0x66, 0x32, 0x09,
        0x01, 0x65, 0xca, 0x18,
-       0x80, 0x66, 0xa0, 0x78,
+       0x80, 0x66, 0xaa, 0x78,
        0xff, 0x66, 0xa2, 0x08,
-       0x10, 0x03, 0x90, 0x68,
+       0x10, 0x03, 0x9a, 0x68,
        0xfc, 0x65, 0xc8, 0x18,
-       0x00, 0x65, 0xa8, 0x48,
+       0x00, 0x65, 0xb2, 0x48,
        0xff, 0x6a, 0x32, 0x01,
        0x01, 0x64, 0x18, 0x19,
        0xff, 0x6a, 0x1a, 0x09,
        0xff, 0x6a, 0x1c, 0x09,
        0x84, 0x6a, 0x06, 0x00,
        0x08, 0x01, 0x02, 0x00,
-       0x02, 0x0b, 0xb2, 0x78,
+       0x02, 0x0b, 0xbc, 0x78,
        0xff, 0x06, 0xc8, 0x08,
        0xff, 0x64, 0x32, 0x09,
        0xff, 0x6a, 0xca, 0x08,
@@ -105,33 +110,33 @@ static unsigned char seqprog[] = {
        0x0b, 0x65, 0xca, 0x18,
        0xff, 0x65, 0xc8, 0x08,
        0x00, 0x8c, 0x18, 0x19,
-       0x02, 0x0b, 0xce, 0x78,
-       0x01, 0x65, 0xd4, 0x60,
+       0x02, 0x0b, 0xd8, 0x78,
+       0x01, 0x65, 0xde, 0x60,
        0xf7, 0x01, 0x02, 0x08,
        0xff, 0x06, 0x32, 0x09,
        0xff, 0x65, 0xca, 0x18,
-       0xff, 0x65, 0xce, 0x68,
+       0xff, 0x65, 0xd8, 0x68,
        0x0a, 0x93, 0x26, 0x01,
-       0x00, 0x65, 0x64, 0x5c,
-       0x40, 0x51, 0xe6, 0x78,
+       0x00, 0x65, 0x84, 0x5c,
+       0x40, 0x51, 0xf0, 0x78,
        0xe4, 0x6a, 0x06, 0x00,
        0x08, 0x01, 0x02, 0x00,
-       0x04, 0x6a, 0x18, 0x5b,
+       0x04, 0x6a, 0x40, 0x5b,
        0x01, 0x50, 0xa0, 0x18,
-       0x00, 0x50, 0xec, 0xe0,
+       0x00, 0x50, 0xf6, 0xe0,
        0xff, 0x6a, 0xa0, 0x08,
        0xff, 0x6a, 0x3a, 0x01,
        0x02, 0x6a, 0x22, 0x01,
-       0x40, 0x51, 0xf2, 0x68,
+       0x40, 0x51, 0xfc, 0x68,
        0xff, 0x6a, 0x06, 0x08,
        0x00, 0x65, 0x0e, 0x40,
        0x20, 0x6a, 0x16, 0x00,
        0xf0, 0x19, 0x6e, 0x08,
        0x08, 0x6a, 0x18, 0x00,
        0x08, 0x11, 0x22, 0x00,
-       0x08, 0x6a, 0x5a, 0x58,
+       0x08, 0x6a, 0x66, 0x58,
        0x08, 0x6a, 0x68, 0x00,
-       0x00, 0x65, 0x18, 0x41,
+       0x00, 0x65, 0x22, 0x41,
        0x12, 0x6a, 0x00, 0x00,
        0x40, 0x6a, 0x16, 0x00,
        0xff, 0x3e, 0x20, 0x09,
@@ -139,362 +144,373 @@ static unsigned char seqprog[] = {
        0xff, 0xa1, 0x6e, 0x08,
        0x08, 0x6a, 0x18, 0x00,
        0x08, 0x11, 0x22, 0x00,
-       0x08, 0x6a, 0x5a, 0x58,
+       0x08, 0x6a, 0x66, 0x58,
        0x80, 0x6a, 0x68, 0x00,
        0x80, 0x36, 0x6c, 0x00,
-       0x00, 0x65, 0xd2, 0x5b,
+       0x00, 0x65, 0xf2, 0x5b,
        0xff, 0x3d, 0xc8, 0x08,
-       0xbf, 0x64, 0x48, 0x79,
-       0x80, 0x64, 0xf0, 0x71,
-       0xa0, 0x64, 0x0e, 0x72,
-       0xc0, 0x64, 0x08, 0x72,
-       0xe0, 0x64, 0x52, 0x72,
+       0xbf, 0x64, 0x58, 0x79,
+       0x80, 0x64, 0x0e, 0x72,
+       0xa0, 0x64, 0x3a, 0x72,
+       0xc0, 0x64, 0x32, 0x72,
+       0xe0, 0x64, 0x7a, 0x72,
        0x01, 0x6a, 0x22, 0x01,
-       0x00, 0x65, 0x18, 0x41,
+       0x00, 0x65, 0x22, 0x41,
        0xf7, 0x11, 0x22, 0x08,
-       0x00, 0x65, 0x38, 0x59,
+       0x00, 0x65, 0x42, 0x59,
        0xff, 0x06, 0xd4, 0x08,
        0xf7, 0x01, 0x02, 0x08,
-       0x09, 0x0c, 0x32, 0x79,
+       0x09, 0x0c, 0x3c, 0x79,
        0x08, 0x0c, 0x0e, 0x68,
        0x01, 0x6a, 0x22, 0x01,
        0xff, 0x6a, 0x26, 0x09,
+       0x02, 0x6a, 0x08, 0x30,
        0xff, 0x6a, 0x08, 0x08,
        0xdf, 0x01, 0x02, 0x08,
        0x01, 0x6a, 0x7a, 0x00,
-       0x03, 0x36, 0x6c, 0x0c,
+       0xff, 0x6a, 0x6c, 0x0c,
+       0x03, 0xa9, 0x18, 0x31,
+       0x03, 0xa9, 0x10, 0x30,
        0x08, 0x6a, 0xcc, 0x00,
-       0xa9, 0x6a, 0xe8, 0x5b,
-       0x00, 0x65, 0x66, 0x41,
+       0xa9, 0x6a, 0x08, 0x5c,
+       0x00, 0x65, 0x78, 0x41,
        0xa8, 0x6a, 0x6a, 0x00,
        0x79, 0x6a, 0x6a, 0x00,
-       0x40, 0x3d, 0x50, 0x69,
+       0x40, 0x3d, 0x60, 0x69,
        0x04, 0x35, 0x6a, 0x00,
-       0x00, 0x65, 0x3a, 0x5b,
+       0x00, 0x65, 0x62, 0x5b,
        0x80, 0x6a, 0xd4, 0x01,
-       0x10, 0x36, 0x42, 0x69,
+       0x10, 0x36, 0x4e, 0x69,
        0x10, 0x36, 0x6c, 0x00,
        0x07, 0xac, 0x10, 0x31,
+       0x03, 0x8c, 0x10, 0x30,
+       0x05, 0xa3, 0x70, 0x30,
        0x88, 0x6a, 0xcc, 0x00,
-       0xac, 0x6a, 0xe0, 0x5b,
-       0x00, 0x65, 0xda, 0x5b,
-       0xff, 0xa3, 0x70, 0x08,
-       0x39, 0x6a, 0xcc, 0x00,
-       0xa4, 0x6a, 0xe6, 0x5b,
-       0xff, 0x38, 0x74, 0x69,
+       0xac, 0x6a, 0x00, 0x5c,
+       0x00, 0x65, 0xfa, 0x5b,
+       0x38, 0x6a, 0xcc, 0x00,
+       0xa3, 0x6a, 0x04, 0x5c,
+       0xff, 0x38, 0x88, 0x69,
        0x80, 0x02, 0x04, 0x00,
        0xe7, 0x35, 0x6a, 0x08,
        0x03, 0x69, 0x18, 0x31,
+       0x03, 0x69, 0x10, 0x30,
        0xff, 0x6a, 0x10, 0x00,
        0xff, 0x6a, 0x12, 0x00,
        0xff, 0x6a, 0x14, 0x00,
-       0x01, 0x38, 0x7a, 0x61,
-       0x02, 0xfc, 0xf8, 0x01,
+       0x01, 0x38, 0x8c, 0x61,
        0xbf, 0x35, 0x6a, 0x08,
        0xff, 0x69, 0xca, 0x08,
        0xff, 0x35, 0x26, 0x09,
-       0x04, 0x0b, 0x7e, 0x69,
-       0x04, 0x0b, 0x8a, 0x69,
-       0x10, 0x0c, 0x80, 0x79,
-       0x04, 0x0b, 0x88, 0x69,
+       0x04, 0x0b, 0x90, 0x69,
+       0x04, 0x0b, 0x9c, 0x69,
+       0x10, 0x0c, 0x92, 0x79,
+       0x04, 0x0b, 0x9a, 0x69,
        0xff, 0x6a, 0xca, 0x08,
-       0x00, 0x35, 0x22, 0x5b,
-       0x80, 0x02, 0xd6, 0x69,
-       0xff, 0x65, 0xc8, 0x79,
+       0x00, 0x35, 0x4a, 0x5b,
+       0x80, 0x02, 0xf0, 0x69,
+       0xff, 0x65, 0xe0, 0x79,
        0xff, 0x38, 0x70, 0x18,
-       0xff, 0x38, 0xc8, 0x79,
-       0x80, 0xea, 0xaa, 0x61,
+       0xff, 0x38, 0xe0, 0x79,
+       0x80, 0xea, 0xbc, 0x61,
        0xef, 0x38, 0xc8, 0x18,
        0x80, 0x6a, 0xc8, 0x00,
-       0x00, 0x65, 0x9c, 0x49,
+       0x00, 0x65, 0xae, 0x49,
        0x33, 0x38, 0xc8, 0x28,
        0xff, 0x64, 0xd0, 0x09,
        0x04, 0x39, 0xc0, 0x31,
        0x09, 0x6a, 0xd6, 0x01,
-       0x80, 0xeb, 0xa2, 0x79,
+       0x80, 0xeb, 0xb4, 0x79,
        0xf7, 0xeb, 0xd6, 0x09,
-       0x08, 0xeb, 0xa6, 0x69,
+       0x08, 0xeb, 0xb8, 0x69,
        0x01, 0x6a, 0xd6, 0x01,
        0x08, 0xe9, 0x10, 0x31,
+       0x03, 0x8c, 0x10, 0x30,
        0x88, 0x6a, 0xcc, 0x00,
-       0x39, 0x6a, 0xe6, 0x5b,
+       0x39, 0x6a, 0x06, 0x5c,
        0x08, 0x6a, 0x18, 0x01,
        0xff, 0x6a, 0x1a, 0x09,
        0xff, 0x6a, 0x1c, 0x09,
        0x0d, 0x93, 0x26, 0x01,
-       0x00, 0x65, 0x64, 0x5c,
-       0x88, 0x6a, 0x54, 0x5c,
-       0x00, 0x65, 0xda, 0x5b,
+       0x00, 0x65, 0x84, 0x5c,
+       0x88, 0x6a, 0x74, 0x5c,
+       0x00, 0x65, 0xfa, 0x5b,
        0xff, 0x6a, 0xc8, 0x08,
        0x08, 0x39, 0x72, 0x18,
        0x00, 0x3a, 0x74, 0x20,
-       0x10, 0x0c, 0x66, 0x79,
-       0x80, 0x93, 0x26, 0x01,
-       0x00, 0x65, 0xe0, 0x59,
+       0x01, 0x0c, 0xd8, 0x79,
+       0x10, 0x0c, 0x78, 0x79,
+       0xff, 0x35, 0x26, 0x09,
+       0x04, 0x0b, 0xde, 0x69,
+       0x00, 0x65, 0xf8, 0x59,
+       0x03, 0x08, 0x52, 0x31,
+       0xff, 0x38, 0x50, 0x09,
        0xff, 0x08, 0x52, 0x09,
        0xff, 0x09, 0x54, 0x09,
        0xff, 0x0a, 0x56, 0x09,
        0xff, 0x38, 0x50, 0x09,
-       0x12, 0x01, 0x02, 0x00,
-       0x00, 0x65, 0x18, 0x41,
-       0x00, 0x65, 0xe0, 0x59,
-       0x12, 0x01, 0x02, 0x00,
+       0x00, 0x65, 0x22, 0x41,
+       0x00, 0x65, 0xf8, 0x59,
        0x7f, 0x02, 0x04, 0x08,
        0xe1, 0x6a, 0x22, 0x01,
-       0x00, 0x65, 0x18, 0x41,
-       0x04, 0x93, 0xea, 0x69,
+       0x00, 0x65, 0x22, 0x41,
+       0x04, 0x93, 0x02, 0x6a,
        0xdf, 0x93, 0x26, 0x09,
-       0x20, 0x93, 0xe4, 0x69,
+       0x20, 0x93, 0xfc, 0x69,
        0x02, 0x93, 0x26, 0x01,
-       0x01, 0x94, 0xe6, 0x79,
+       0x01, 0x94, 0xfe, 0x79,
        0xd7, 0x93, 0x26, 0x09,
-       0x08, 0x93, 0xec, 0x69,
+       0x08, 0x93, 0x04, 0x6a,
+       0x03, 0x08, 0x52, 0x31,
+       0xff, 0x38, 0x50, 0x09,
+       0x12, 0x01, 0x02, 0x00,
        0xff, 0x6a, 0xd4, 0x0c,
-       0x00, 0x65, 0x3a, 0x5b,
-       0x02, 0xfc, 0xf8, 0x01,
+       0x00, 0x65, 0x62, 0x5b,
        0x05, 0xb4, 0x10, 0x31,
        0x02, 0x6a, 0x1a, 0x31,
+       0x03, 0x8c, 0x10, 0x30,
        0x88, 0x6a, 0xcc, 0x00,
-       0xb4, 0x6a, 0xe4, 0x5b,
+       0xb4, 0x6a, 0x04, 0x5c,
        0xff, 0x6a, 0x1a, 0x09,
        0xff, 0x6a, 0x1c, 0x09,
-       0x00, 0x65, 0xda, 0x5b,
-       0x3d, 0x6a, 0x22, 0x5b,
-       0xac, 0x6a, 0x22, 0x5b,
-       0x00, 0x65, 0x18, 0x41,
-       0x00, 0x65, 0x3a, 0x5b,
+       0x00, 0x65, 0xfa, 0x5b,
+       0x3d, 0x6a, 0x4a, 0x5b,
+       0xac, 0x6a, 0x26, 0x01,
+       0x04, 0x0b, 0x24, 0x6a,
+       0x01, 0x0b, 0x2a, 0x6a,
+       0x10, 0x0c, 0x26, 0x7a,
+       0xd7, 0x93, 0x26, 0x09,
+       0x08, 0x93, 0x2c, 0x6a,
+       0x12, 0x01, 0x02, 0x00,
+       0x00, 0x65, 0x22, 0x41,
+       0x00, 0x65, 0x62, 0x5b,
        0xff, 0x06, 0x44, 0x09,
-       0x00, 0x65, 0x18, 0x41,
+       0x00, 0x65, 0x22, 0x41,
+       0x10, 0x3d, 0x06, 0x00,
        0xff, 0x34, 0xca, 0x08,
-       0x80, 0x65, 0x32, 0x62,
+       0x80, 0x65, 0x5e, 0x62,
        0x0f, 0xa1, 0xca, 0x08,
        0x07, 0xa1, 0xca, 0x08,
        0x40, 0xa0, 0xc8, 0x08,
        0x00, 0x65, 0xca, 0x00,
        0x80, 0x65, 0xca, 0x00,
-       0x80, 0xa0, 0x22, 0x7a,
+       0x80, 0xa0, 0x4e, 0x7a,
        0xff, 0x65, 0x0c, 0x08,
-       0x00, 0x65, 0x34, 0x42,
-       0x20, 0xa0, 0x3a, 0x7a,
+       0x00, 0x65, 0x60, 0x42,
+       0x20, 0xa0, 0x66, 0x7a,
        0xff, 0x65, 0x0c, 0x08,
-       0x00, 0x65, 0xd2, 0x5b,
-       0xa0, 0x3d, 0x46, 0x62,
+       0x00, 0x65, 0xf2, 0x5b,
+       0xa0, 0x3d, 0x6e, 0x62,
        0x23, 0xa0, 0x0c, 0x08,
-       0x00, 0x65, 0xd2, 0x5b,
-       0xa0, 0x3d, 0x46, 0x62,
-       0x00, 0xb9, 0x3a, 0x42,
-       0xff, 0x65, 0x3a, 0x62,
+       0x00, 0x65, 0xf2, 0x5b,
+       0xa0, 0x3d, 0x6e, 0x62,
+       0x00, 0xb9, 0x66, 0x42,
+       0xff, 0x65, 0x66, 0x62,
        0xa1, 0x6a, 0x22, 0x01,
        0xff, 0x6a, 0xd4, 0x08,
-       0x10, 0x51, 0x46, 0x72,
+       0x10, 0x51, 0x6e, 0x72,
        0x40, 0x6a, 0x18, 0x00,
        0xff, 0x65, 0x0c, 0x08,
-       0x00, 0x65, 0xd2, 0x5b,
-       0xa0, 0x3d, 0x46, 0x62,
-       0x10, 0x3d, 0x06, 0x00,
-       0x00, 0x65, 0x0e, 0x42,
+       0x00, 0x65, 0xf2, 0x5b,
+       0xa0, 0x3d, 0x38, 0x72,
        0x40, 0x6a, 0x18, 0x00,
        0xff, 0x34, 0xa6, 0x08,
-       0x80, 0x34, 0x4e, 0x62,
+       0x80, 0x34, 0x76, 0x62,
        0x7f, 0xa0, 0x40, 0x09,
        0x08, 0x6a, 0x68, 0x00,
-       0x00, 0x65, 0x18, 0x41,
-       0x64, 0x6a, 0x12, 0x5b,
-       0x80, 0x64, 0xbe, 0x6a,
-       0x04, 0x64, 0xa4, 0x72,
-       0x02, 0x64, 0xaa, 0x72,
-       0x00, 0x6a, 0x6c, 0x72,
-       0x03, 0x64, 0xba, 0x72,
-       0x01, 0x64, 0xa0, 0x72,
-       0x07, 0x64, 0x00, 0x73,
-       0x08, 0x64, 0x68, 0x72,
+       0x00, 0x65, 0x22, 0x41,
+       0x64, 0x6a, 0x3a, 0x5b,
+       0x80, 0x64, 0xea, 0x6a,
+       0x04, 0x64, 0xcc, 0x72,
+       0x02, 0x64, 0xd2, 0x72,
+       0x00, 0x6a, 0x94, 0x72,
+       0x03, 0x64, 0xe6, 0x72,
+       0x01, 0x64, 0xc8, 0x72,
+       0x07, 0x64, 0x28, 0x73,
+       0x08, 0x64, 0x90, 0x72,
        0x11, 0x6a, 0x22, 0x01,
-       0x07, 0x6a, 0x04, 0x5b,
+       0x07, 0x6a, 0x2c, 0x5b,
        0xff, 0x06, 0xd4, 0x08,
-       0x00, 0x65, 0x18, 0x41,
-       0xff, 0xa8, 0x70, 0x6a,
-       0xff, 0xa2, 0x88, 0x7a,
+       0x00, 0x65, 0x22, 0x41,
+       0xff, 0xa8, 0x98, 0x6a,
+       0xff, 0xa2, 0xb0, 0x7a,
        0x01, 0x6a, 0x6a, 0x00,
-       0x00, 0xb9, 0xfe, 0x5b,
-       0xff, 0xa2, 0x88, 0x7a,
+       0x00, 0xb9, 0x1e, 0x5c,
+       0xff, 0xa2, 0xb0, 0x7a,
        0x71, 0x6a, 0x22, 0x01,
        0xff, 0x6a, 0xd4, 0x08,
-       0x40, 0x51, 0x88, 0x62,
+       0x40, 0x51, 0xb0, 0x62,
        0x0d, 0x6a, 0x6a, 0x00,
-       0x00, 0xb9, 0xfe, 0x5b,
+       0x00, 0xb9, 0x1e, 0x5c,
        0xff, 0x3e, 0x74, 0x09,
        0xff, 0x90, 0x7c, 0x08,
-       0x00, 0x65, 0x44, 0x58,
-       0x00, 0x65, 0x2a, 0x41,
-       0x20, 0xa0, 0x90, 0x6a,
+       0x00, 0x65, 0x50, 0x58,
+       0x00, 0x65, 0x34, 0x41,
+       0x20, 0xa0, 0xb8, 0x6a,
        0xff, 0x37, 0xc8, 0x08,
-       0x00, 0x6a, 0xa8, 0x5b,
-       0xff, 0x6a, 0xbe, 0x5b,
+       0x00, 0x6a, 0xc8, 0x5b,
+       0xff, 0x6a, 0xde, 0x5b,
        0xff, 0xf8, 0xc8, 0x08,
        0xff, 0x4f, 0xc8, 0x08,
-       0x01, 0x6a, 0xa8, 0x5b,
-       0x00, 0xb9, 0xbe, 0x5b,
+       0x01, 0x6a, 0xc8, 0x5b,
+       0x00, 0xb9, 0xde, 0x5b,
        0x01, 0x4f, 0x9e, 0x18,
        0x02, 0x6a, 0x22, 0x01,
-       0x00, 0x65, 0x6c, 0x5c,
-       0x00, 0x65, 0x2a, 0x41,
+       0x00, 0x65, 0x8c, 0x5c,
+       0x00, 0x65, 0x34, 0x41,
        0x41, 0x6a, 0x22, 0x01,
-       0x00, 0x65, 0x18, 0x41,
+       0x00, 0x65, 0x22, 0x41,
        0x04, 0xa0, 0x40, 0x01,
-       0x00, 0x65, 0x84, 0x5c,
-       0x00, 0x65, 0x2a, 0x41,
-       0x10, 0x36, 0x68, 0x7a,
-       0xff, 0x38, 0x46, 0x09,
-       0xa4, 0x6a, 0xcc, 0x00,
-       0x39, 0x6a, 0xe6, 0x5b,
+       0x00, 0x65, 0xa4, 0x5c,
+       0x00, 0x65, 0x34, 0x41,
+       0x10, 0x36, 0x90, 0x7a,
+       0x05, 0x38, 0x46, 0x31,
+       0x04, 0x14, 0x58, 0x31,
+       0x03, 0xa9, 0x60, 0x31,
+       0xa3, 0x6a, 0xcc, 0x00,
+       0x38, 0x6a, 0x04, 0x5c,
        0xac, 0x6a, 0xcc, 0x00,
-       0x14, 0x6a, 0xe6, 0x5b,
-       0xa9, 0x6a, 0xe8, 0x5b,
-       0x00, 0x65, 0x68, 0x42,
+       0x14, 0x6a, 0x06, 0x5c,
+       0xa9, 0x6a, 0x08, 0x5c,
+       0x00, 0x65, 0x90, 0x42,
        0xef, 0x36, 0x6c, 0x08,
-       0x00, 0x65, 0x68, 0x42,
+       0x00, 0x65, 0x90, 0x42,
        0x0f, 0x64, 0xc8, 0x08,
        0x07, 0x64, 0xc8, 0x08,
        0x00, 0x37, 0x6e, 0x00,
-       0x00, 0x65, 0x78, 0x5b,
-       0xff, 0x51, 0xce, 0x72,
-       0x20, 0x36, 0xde, 0x7a,
-       0x00, 0x90, 0x5c, 0x5b,
-       0x00, 0x65, 0xe0, 0x42,
+       0xff, 0x6a, 0xa4, 0x00,
+       0x00, 0x65, 0x98, 0x5b,
+       0xff, 0x51, 0xfc, 0x72,
+       0x20, 0x36, 0x06, 0x7b,
+       0x00, 0x90, 0x86, 0x5b,
+       0x00, 0x65, 0x08, 0x43,
        0xff, 0x06, 0xd4, 0x08,
-       0x00, 0x65, 0xd2, 0x5b,
-       0xe0, 0x3d, 0xfa, 0x62,
-       0x20, 0x12, 0xfa, 0x62,
-       0x51, 0x6a, 0x08, 0x5b,
-       0xff, 0x51, 0x20, 0x09,
-       0x20, 0xa0, 0xfa, 0x7a,
-       0x00, 0x90, 0x5c, 0x5b,
-       0x00, 0x65, 0x56, 0x5b,
+       0x00, 0x65, 0xf2, 0x5b,
+       0xe0, 0x3d, 0x22, 0x63,
+       0x20, 0x12, 0x22, 0x63,
+       0x51, 0x6a, 0x30, 0x5b,
+       0x00, 0x65, 0x80, 0x5b,
        0xff, 0x37, 0xc8, 0x08,
-       0x00, 0xa1, 0xf2, 0x62,
-       0x04, 0xa0, 0xf2, 0x7a,
+       0x00, 0xa1, 0x1a, 0x63,
+       0x04, 0xa0, 0x1a, 0x7b,
        0xfb, 0xa0, 0x40, 0x09,
        0x80, 0x36, 0x6c, 0x00,
-       0x80, 0xa0, 0x68, 0x7a,
+       0x80, 0xa0, 0x90, 0x7a,
        0x7f, 0xa0, 0x40, 0x09,
-       0xff, 0x6a, 0x04, 0x5b,
-       0x00, 0x65, 0x68, 0x42,
-       0x04, 0xa0, 0xf8, 0x7a,
-       0x00, 0x65, 0x84, 0x5c,
-       0x00, 0x65, 0xfa, 0x42,
-       0x00, 0x65, 0x6c, 0x5c,
+       0xff, 0x6a, 0x2c, 0x5b,
+       0x00, 0x65, 0x90, 0x42,
+       0x04, 0xa0, 0x20, 0x7b,
+       0x00, 0x65, 0xa4, 0x5c,
+       0x00, 0x65, 0x22, 0x43,
+       0x00, 0x65, 0x8c, 0x5c,
        0x31, 0x6a, 0x22, 0x01,
-       0x0c, 0x6a, 0x04, 0x5b,
-       0x00, 0x65, 0x68, 0x42,
+       0x0c, 0x6a, 0x2c, 0x5b,
+       0x00, 0x65, 0x90, 0x42,
        0x61, 0x6a, 0x22, 0x01,
-       0x00, 0x65, 0x68, 0x42,
+       0x00, 0x65, 0x90, 0x42,
        0x10, 0x3d, 0x06, 0x00,
        0xff, 0x65, 0x68, 0x0c,
        0xff, 0x06, 0xd4, 0x08,
-       0x01, 0x0c, 0x0a, 0x7b,
-       0x04, 0x0c, 0x0a, 0x6b,
+       0x01, 0x0c, 0x32, 0x7b,
+       0x04, 0x0c, 0x32, 0x6b,
        0xe0, 0x03, 0x7a, 0x08,
-       0xe0, 0x3d, 0x1e, 0x63,
+       0xe0, 0x3d, 0x46, 0x63,
        0xff, 0x65, 0xcc, 0x08,
        0xff, 0x12, 0xda, 0x0c,
        0xff, 0x06, 0xd4, 0x0c,
        0xff, 0x65, 0x0c, 0x08,
-       0x02, 0x0b, 0x1a, 0x7b,
+       0x02, 0x0b, 0x42, 0x7b,
        0xff, 0x6a, 0xd4, 0x0c,
        0xd1, 0x6a, 0x22, 0x01,
-       0x00, 0x65, 0x18, 0x41,
+       0x00, 0x65, 0x22, 0x41,
        0xff, 0x65, 0x26, 0x09,
-       0x01, 0x0b, 0x32, 0x6b,
-       0x10, 0x0c, 0x24, 0x7b,
-       0x04, 0x0b, 0x2c, 0x6b,
+       0x01, 0x0b, 0x5a, 0x6b,
+       0x10, 0x0c, 0x4c, 0x7b,
+       0x04, 0x0b, 0x54, 0x6b,
        0xff, 0x6a, 0xca, 0x08,
-       0x04, 0x93, 0x30, 0x6b,
-       0x01, 0x94, 0x2e, 0x7b,
-       0x10, 0x94, 0x30, 0x6b,
+       0x04, 0x93, 0x58, 0x6b,
+       0x01, 0x94, 0x56, 0x7b,
+       0x10, 0x94, 0x58, 0x6b,
        0xc7, 0x93, 0x26, 0x09,
        0xff, 0x99, 0xd4, 0x08,
-       0x08, 0x93, 0x34, 0x6b,
+       0x38, 0x93, 0x5c, 0x6b,
        0xff, 0x6a, 0xd4, 0x0c,
-       0x80, 0x36, 0x38, 0x6b,
+       0x80, 0x36, 0x60, 0x6b,
        0x21, 0x6a, 0x22, 0x05,
        0xff, 0x65, 0x20, 0x09,
-       0xff, 0x51, 0x46, 0x63,
+       0xff, 0x51, 0x6e, 0x63,
        0xff, 0x37, 0xc8, 0x08,
-       0xa1, 0x6a, 0x50, 0x43,
+       0xa1, 0x6a, 0x7a, 0x43,
        0xff, 0x51, 0xc8, 0x08,
-       0xb9, 0x6a, 0x50, 0x43,
-       0xff, 0xba, 0x54, 0x73,
+       0xb9, 0x6a, 0x7a, 0x43,
+       0xff, 0x90, 0xa4, 0x08,
+       0xff, 0xba, 0x7e, 0x73,
        0xff, 0xba, 0x20, 0x09,
        0xff, 0x65, 0xca, 0x18,
-       0x00, 0x6c, 0x4a, 0x63,
+       0x00, 0x6c, 0x72, 0x63,
        0xff, 0x90, 0xca, 0x0c,
        0xff, 0x6a, 0xca, 0x04,
-       0x20, 0x36, 0x72, 0x7b,
-       0x00, 0x90, 0x3e, 0x5b,
-       0xff, 0x65, 0x72, 0x73,
-       0xff, 0xba, 0x66, 0x73,
-       0xff, 0xbb, 0xcc, 0x08,
-       0xff, 0xba, 0x20, 0x09,
-       0xff, 0x66, 0x76, 0x09,
-       0xff, 0x65, 0x20, 0x09,
-       0xff, 0xbb, 0x70, 0x73,
+       0x20, 0x36, 0x92, 0x7b,
+       0x00, 0x90, 0x66, 0x5b,
+       0xff, 0x65, 0x92, 0x73,
+       0xff, 0x52, 0x90, 0x73,
        0xff, 0xba, 0xcc, 0x08,
-       0xff, 0xbb, 0x20, 0x09,
+       0xff, 0x52, 0x20, 0x09,
        0xff, 0x66, 0x74, 0x09,
        0xff, 0x65, 0x20, 0x0d,
        0xff, 0xba, 0x7e, 0x0c,
-       0x00, 0x6a, 0x72, 0x5c,
+       0x00, 0x6a, 0x92, 0x5c,
        0x0d, 0x6a, 0x6a, 0x00,
-       0x00, 0x51, 0xfe, 0x43,
-       0xff, 0x3f, 0xcc, 0x73,
+       0x00, 0x51, 0x1e, 0x44,
+       0xff, 0x3f, 0xec, 0x73,
        0xff, 0x6a, 0xa2, 0x00,
-       0x00, 0x3f, 0x3e, 0x5b,
-       0xff, 0x65, 0xcc, 0x73,
+       0x00, 0x3f, 0x66, 0x5b,
+       0xff, 0x65, 0xec, 0x73,
        0x20, 0x36, 0x6c, 0x00,
-       0x20, 0xa0, 0x86, 0x6b,
+       0x20, 0xa0, 0xa6, 0x6b,
        0xff, 0xb9, 0xa2, 0x0c,
        0xff, 0x6a, 0xa2, 0x04,
        0xff, 0x65, 0xa4, 0x08,
        0xe0, 0x6a, 0xcc, 0x00,
-       0x45, 0x6a, 0xf2, 0x5b,
+       0x45, 0x6a, 0x12, 0x5c,
        0x01, 0x6a, 0xd0, 0x01,
        0x09, 0x6a, 0xd6, 0x01,
-       0x80, 0xeb, 0x92, 0x7b,
+       0x80, 0xeb, 0xb2, 0x7b,
        0x01, 0x6a, 0xd6, 0x01,
        0x01, 0xe9, 0xa4, 0x34,
        0x88, 0x6a, 0xcc, 0x00,
-       0x45, 0x6a, 0xf2, 0x5b,
+       0x45, 0x6a, 0x12, 0x5c,
        0x01, 0x6a, 0x18, 0x01,
        0xff, 0x6a, 0x1a, 0x09,
        0xff, 0x6a, 0x1c, 0x09,
        0x0d, 0x6a, 0x26, 0x01,
-       0x00, 0x65, 0x64, 0x5c,
+       0x00, 0x65, 0x84, 0x5c,
        0xff, 0x99, 0xa4, 0x0c,
        0xff, 0x65, 0xa4, 0x08,
        0xe0, 0x6a, 0xcc, 0x00,
-       0x45, 0x6a, 0xf2, 0x5b,
+       0x45, 0x6a, 0x12, 0x5c,
        0x01, 0x6a, 0xd0, 0x01,
        0x01, 0x6a, 0xdc, 0x05,
        0x88, 0x6a, 0xcc, 0x00,
-       0x45, 0x6a, 0xf2, 0x5b,
+       0x45, 0x6a, 0x12, 0x5c,
        0x01, 0x6a, 0x18, 0x01,
        0xff, 0x6a, 0x1a, 0x09,
        0xff, 0x6a, 0x1c, 0x09,
        0x01, 0x6a, 0x26, 0x05,
        0x01, 0x65, 0xd8, 0x31,
        0x09, 0xee, 0xdc, 0x01,
-       0x80, 0xee, 0xc2, 0x7b,
+       0x80, 0xee, 0xe2, 0x7b,
        0xff, 0x6a, 0xdc, 0x0d,
        0xff, 0x65, 0x32, 0x09,
        0x0a, 0x93, 0x26, 0x01,
-       0x00, 0x65, 0x64, 0x44,
+       0x00, 0x65, 0x84, 0x44,
        0xff, 0x37, 0xc8, 0x08,
-       0x00, 0x6a, 0x88, 0x5b,
+       0x00, 0x6a, 0xa8, 0x5b,
        0xff, 0x52, 0xa2, 0x0c,
-       0x01, 0x0c, 0xd2, 0x7b,
-       0x04, 0x0c, 0xd2, 0x6b,
-       0xe0, 0x03, 0x7a, 0x08,
-       0xff, 0x3d, 0x06, 0x0c,
+       0x01, 0x0c, 0xf2, 0x7b,
+       0x04, 0x0c, 0xf2, 0x6b,
+       0xe0, 0x03, 0x06, 0x08,
+       0xe0, 0x03, 0x7a, 0x0c,
        0xff, 0x8c, 0x10, 0x08,
        0xff, 0x8d, 0x12, 0x08,
        0xff, 0x8e, 0x14, 0x0c,
@@ -515,29 +531,29 @@ static unsigned char seqprog[] = {
        0x00, 0x6c, 0xda, 0x24,
        0xff, 0x65, 0xc8, 0x08,
        0xe0, 0x6a, 0xcc, 0x00,
-       0x41, 0x6a, 0xee, 0x5b,
+       0x41, 0x6a, 0x0e, 0x5c,
        0xff, 0x90, 0xe2, 0x09,
        0x20, 0x6a, 0xd0, 0x01,
-       0x04, 0x35, 0x10, 0x7c,
+       0x04, 0x35, 0x30, 0x7c,
        0x1d, 0x6a, 0xdc, 0x01,
-       0xdc, 0xee, 0x0c, 0x64,
-       0x00, 0x65, 0x1c, 0x44,
+       0xdc, 0xee, 0x2c, 0x64,
+       0x00, 0x65, 0x3c, 0x44,
        0x01, 0x6a, 0xdc, 0x01,
        0x20, 0xa0, 0xd8, 0x31,
        0x09, 0xee, 0xdc, 0x01,
-       0x80, 0xee, 0x16, 0x7c,
+       0x80, 0xee, 0x36, 0x7c,
        0x19, 0x6a, 0xdc, 0x01,
-       0xd8, 0xee, 0x1a, 0x64,
+       0xd8, 0xee, 0x3a, 0x64,
        0xff, 0x6a, 0xdc, 0x09,
-       0x18, 0xee, 0x1e, 0x6c,
+       0x18, 0xee, 0x3e, 0x6c,
        0xff, 0x6a, 0xd4, 0x0c,
        0x88, 0x6a, 0xcc, 0x00,
-       0x41, 0x6a, 0xee, 0x5b,
+       0x41, 0x6a, 0x0e, 0x5c,
        0x20, 0x6a, 0x18, 0x01,
        0xff, 0x6a, 0x1a, 0x09,
        0xff, 0x6a, 0x1c, 0x09,
        0xff, 0x35, 0x26, 0x09,
-       0x04, 0x35, 0x48, 0x6c,
+       0x04, 0x35, 0x68, 0x6c,
        0xa0, 0x6a, 0xca, 0x00,
        0x20, 0x65, 0xc8, 0x18,
        0xff, 0x6c, 0x32, 0x09,
@@ -548,14 +564,14 @@ static unsigned char seqprog[] = {
        0xff, 0x6c, 0x32, 0x09,
        0xff, 0x6c, 0x32, 0x09,
        0xff, 0x6c, 0x32, 0x09,
-       0x00, 0x65, 0x34, 0x64,
+       0x00, 0x65, 0x54, 0x64,
        0x0a, 0x93, 0x26, 0x01,
-       0x00, 0x65, 0x64, 0x5c,
-       0x04, 0x35, 0x38, 0x7b,
-       0xa0, 0x6a, 0x54, 0x5c,
-       0x00, 0x65, 0x56, 0x5c,
-       0x00, 0x65, 0x56, 0x5c,
-       0x00, 0x65, 0x56, 0x44,
+       0x00, 0x65, 0x84, 0x5c,
+       0x04, 0x35, 0x60, 0x7b,
+       0xa0, 0x6a, 0x74, 0x5c,
+       0x00, 0x65, 0x76, 0x5c,
+       0x00, 0x65, 0x76, 0x5c,
+       0x00, 0x65, 0x76, 0x44,
        0xff, 0x65, 0xcc, 0x08,
        0xff, 0x99, 0xda, 0x08,
        0xff, 0x99, 0xda, 0x08,
@@ -564,37 +580,40 @@ static unsigned char seqprog[] = {
        0xff, 0x99, 0xda, 0x08,
        0xff, 0x99, 0xda, 0x08,
        0xff, 0x99, 0xda, 0x0c,
-       0x08, 0x94, 0x64, 0x7c,
+       0x08, 0x94, 0x84, 0x7c,
        0xf7, 0x93, 0x26, 0x09,
-       0x08, 0x93, 0x68, 0x6c,
+       0x08, 0x93, 0x88, 0x6c,
        0xff, 0x6a, 0xd4, 0x0c,
        0xff, 0x40, 0x74, 0x09,
        0xff, 0x90, 0x80, 0x08,
        0xff, 0x6a, 0x72, 0x05,
-       0xff, 0x40, 0x80, 0x64,
-       0xff, 0x3f, 0x78, 0x64,
+       0xff, 0x40, 0xa0, 0x64,
+       0xff, 0x3f, 0x98, 0x64,
        0xff, 0x6a, 0xca, 0x04,
        0xff, 0x3f, 0x20, 0x09,
        0x01, 0x6a, 0x6a, 0x00,
-       0x00, 0xb9, 0xfe, 0x5b,
-       0x00, 0x90, 0x5c, 0x43,
+       0x00, 0xb9, 0x1e, 0x5c,
+       0xff, 0xba, 0x7e, 0x0c,
        0xff, 0x40, 0x20, 0x09,
        0xff, 0xba, 0x80, 0x0c,
-       0xff, 0x6a, 0x76, 0x01,
        0xff, 0x3f, 0x74, 0x09,
-       0xff, 0x90, 0x7e, 0x08,
-       0xff, 0xba, 0x38, 0x73,
-       0xff, 0xba, 0x20, 0x09,
-       0xff, 0x3f, 0x76, 0x09,
-       0xff, 0x3f, 0x20, 0x0d,
+       0xff, 0x90, 0x7e, 0x0c,
 };
 
+static int aic7xxx_patch13_func(struct aic7xxx_host *p);
+
+static int
+aic7xxx_patch13_func(struct aic7xxx_host *p)
+{
+       return ((p->chip & AHC_CHIPID_MASK) == AHC_AIC7895);
+}
+
 static int aic7xxx_patch12_func(struct aic7xxx_host *p);
 
 static int
 aic7xxx_patch12_func(struct aic7xxx_host *p)
 {
-       return ((p->chip & AHC_CHIPID_MASK) == AHC_AIC7895);
+       return ((p->features & AHC_CMD_CHAN) == 0);
 }
 
 static int aic7xxx_patch11_func(struct aic7xxx_host *p);
@@ -699,71 +718,81 @@ struct sequencer_patch {
                        skip_instr :10,
                        skip_patch :12;
 } sequencer_patches[] = {
-       { aic7xxx_patch1_func, 1, 1, 2 },
-       { aic7xxx_patch0_func, 2, 1, 1 },
-       { aic7xxx_patch2_func, 3, 2, 1 },
-       { aic7xxx_patch3_func, 7, 1, 1 },
+       { aic7xxx_patch1_func, 2, 1, 2 },
+       { aic7xxx_patch0_func, 3, 1, 1 },
+       { aic7xxx_patch2_func, 4, 2, 1 },
        { aic7xxx_patch3_func, 8, 1, 1 },
-       { aic7xxx_patch4_func, 11, 4, 1 },
-       { aic7xxx_patch5_func, 16, 3, 2 },
-       { aic7xxx_patch0_func, 19, 4, 1 },
-       { aic7xxx_patch6_func, 23, 1, 1 },
-       { aic7xxx_patch7_func, 26, 1, 1 },
-       { aic7xxx_patch4_func, 34, 4, 1 },
-       { aic7xxx_patch8_func, 38, 3, 2 },
-       { aic7xxx_patch0_func, 41, 3, 1 },
-       { aic7xxx_patch9_func, 47, 7, 1 },
-       { aic7xxx_patch4_func, 55, 3, 1 },
-       { aic7xxx_patch8_func, 58, 2, 1 },
-       { aic7xxx_patch1_func, 63, 60, 1 },
-       { aic7xxx_patch8_func, 164, 1, 2 },
-       { aic7xxx_patch0_func, 165, 1, 1 },
-       { aic7xxx_patch2_func, 169, 1, 1 },
-       { aic7xxx_patch2_func, 172, 1, 2 },
-       { aic7xxx_patch0_func, 173, 2, 1 },
-       { aic7xxx_patch10_func, 175, 1, 1 },
-       { aic7xxx_patch8_func, 182, 1, 2 },
-       { aic7xxx_patch0_func, 183, 3, 1 },
-       { aic7xxx_patch8_func, 187, 1, 2 },
-       { aic7xxx_patch0_func, 188, 1, 1 },
-       { aic7xxx_patch8_func, 189, 7, 2 },
-       { aic7xxx_patch0_func, 196, 1, 1 },
-       { aic7xxx_patch2_func, 201, 13, 2 },
-       { aic7xxx_patch0_func, 214, 8, 1 },
-       { aic7xxx_patch10_func, 222, 1, 1 },
-       { aic7xxx_patch8_func, 227, 1, 1 },
-       { aic7xxx_patch8_func, 228, 1, 1 },
-       { aic7xxx_patch8_func, 233, 1, 1 },
-       { aic7xxx_patch8_func, 235, 2, 1 },
-       { aic7xxx_patch8_func, 240, 8, 1 },
-       { aic7xxx_patch8_func, 249, 1, 1 },
-       { aic7xxx_patch2_func, 250, 2, 2 },
-       { aic7xxx_patch0_func, 252, 4, 1 },
-       { aic7xxx_patch10_func, 256, 2, 2 },
-       { aic7xxx_patch0_func, 258, 1, 1 },
-       { aic7xxx_patch11_func, 265, 1, 2 },
-       { aic7xxx_patch0_func, 266, 1, 1 },
-       { aic7xxx_patch5_func, 328, 1, 2 },
-       { aic7xxx_patch0_func, 329, 1, 1 },
-       { aic7xxx_patch3_func, 332, 1, 1 },
-       { aic7xxx_patch11_func, 351, 1, 2 },
-       { aic7xxx_patch0_func, 352, 1, 1 },
-       { aic7xxx_patch6_func, 356, 1, 1 },
-       { aic7xxx_patch7_func, 364, 3, 2 },
-       { aic7xxx_patch0_func, 367, 1, 1 },
-       { aic7xxx_patch1_func, 396, 3, 1 },
-       { aic7xxx_patch10_func, 410, 1, 1 },
-       { aic7xxx_patch2_func, 453, 7, 2 },
-       { aic7xxx_patch0_func, 460, 8, 1 },
-       { aic7xxx_patch2_func, 469, 4, 2 },
-       { aic7xxx_patch0_func, 473, 6, 1 },
-       { aic7xxx_patch2_func, 479, 4, 2 },
-       { aic7xxx_patch0_func, 483, 3, 1 },
-       { aic7xxx_patch2_func, 512, 17, 4 },
-       { aic7xxx_patch12_func, 520, 4, 2 },
-       { aic7xxx_patch0_func, 524, 2, 1 },
-       { aic7xxx_patch0_func, 529, 33, 1 },
-       { aic7xxx_patch6_func, 566, 2, 1 },
-       { aic7xxx_patch6_func, 569, 9, 1 },
+       { aic7xxx_patch3_func, 9, 1, 1 },
+       { aic7xxx_patch4_func, 12, 4, 1 },
+       { aic7xxx_patch5_func, 17, 3, 2 },
+       { aic7xxx_patch0_func, 20, 4, 1 },
+       { aic7xxx_patch6_func, 24, 1, 1 },
+       { aic7xxx_patch7_func, 27, 1, 1 },
+       { aic7xxx_patch2_func, 30, 1, 2 },
+       { aic7xxx_patch0_func, 31, 3, 1 },
+       { aic7xxx_patch4_func, 40, 4, 1 },
+       { aic7xxx_patch8_func, 44, 3, 2 },
+       { aic7xxx_patch0_func, 47, 3, 1 },
+       { aic7xxx_patch9_func, 52, 7, 1 },
+       { aic7xxx_patch4_func, 60, 3, 1 },
+       { aic7xxx_patch8_func, 63, 2, 1 },
+       { aic7xxx_patch1_func, 68, 60, 1 },
+       { aic7xxx_patch8_func, 162, 1, 2 },
+       { aic7xxx_patch0_func, 163, 2, 1 },
+       { aic7xxx_patch2_func, 167, 2, 3 },
+       { aic7xxx_patch8_func, 167, 1, 1 },
+       { aic7xxx_patch0_func, 169, 2, 1 },
+       { aic7xxx_patch8_func, 172, 1, 2 },
+       { aic7xxx_patch0_func, 173, 1, 1 },
+       { aic7xxx_patch2_func, 177, 1, 1 },
+       { aic7xxx_patch2_func, 180, 3, 2 },
+       { aic7xxx_patch0_func, 183, 5, 1 },
+       { aic7xxx_patch2_func, 191, 2, 3 },
+       { aic7xxx_patch8_func, 191, 1, 1 },
+       { aic7xxx_patch0_func, 193, 3, 1 },
+       { aic7xxx_patch10_func, 196, 2, 1 },
+       { aic7xxx_patch8_func, 198, 7, 2 },
+       { aic7xxx_patch0_func, 205, 1, 1 },
+       { aic7xxx_patch2_func, 210, 14, 3 },
+       { aic7xxx_patch10_func, 223, 1, 1 },
+       { aic7xxx_patch0_func, 224, 9, 1 },
+       { aic7xxx_patch8_func, 238, 2, 1 },
+       { aic7xxx_patch8_func, 240, 1, 1 },
+       { aic7xxx_patch10_func, 241, 6, 3 },
+       { aic7xxx_patch2_func, 241, 2, 2 },
+       { aic7xxx_patch0_func, 243, 4, 1 },
+       { aic7xxx_patch8_func, 248, 1, 1 },
+       { aic7xxx_patch8_func, 252, 11, 1 },
+       { aic7xxx_patch2_func, 264, 3, 3 },
+       { aic7xxx_patch10_func, 266, 1, 1 },
+       { aic7xxx_patch0_func, 267, 5, 1 },
+       { aic7xxx_patch10_func, 272, 1, 2 },
+       { aic7xxx_patch0_func, 273, 7, 1 },
+       { aic7xxx_patch11_func, 287, 1, 2 },
+       { aic7xxx_patch0_func, 288, 1, 1 },
+       { aic7xxx_patch5_func, 348, 1, 2 },
+       { aic7xxx_patch0_func, 349, 1, 1 },
+       { aic7xxx_patch3_func, 352, 1, 1 },
+       { aic7xxx_patch2_func, 362, 3, 2 },
+       { aic7xxx_patch0_func, 365, 5, 1 },
+       { aic7xxx_patch11_func, 373, 1, 2 },
+       { aic7xxx_patch0_func, 374, 1, 1 },
+       { aic7xxx_patch6_func, 379, 1, 1 },
+       { aic7xxx_patch1_func, 416, 3, 1 },
+       { aic7xxx_patch10_func, 421, 11, 1 },
+       { aic7xxx_patch2_func, 469, 7, 2 },
+       { aic7xxx_patch0_func, 476, 8, 1 },
+       { aic7xxx_patch2_func, 485, 4, 2 },
+       { aic7xxx_patch0_func, 489, 6, 1 },
+       { aic7xxx_patch2_func, 495, 4, 2 },
+       { aic7xxx_patch0_func, 499, 3, 1 },
+       { aic7xxx_patch12_func, 509, 10, 1 },
+       { aic7xxx_patch2_func, 528, 17, 4 },
+       { aic7xxx_patch13_func, 536, 4, 2 },
+       { aic7xxx_patch0_func, 540, 2, 1 },
+       { aic7xxx_patch0_func, 545, 33, 1 },
+       { aic7xxx_patch12_func, 578, 4, 1 },
+       { aic7xxx_patch6_func, 582, 2, 1 },
+       { aic7xxx_patch6_func, 585, 9, 1 },
 
 };
index aab3ff235d4e0cc06f77a65c65856b7db360d25d..639b0eae429f78b8986575daef413769a509a176 100644 (file)
@@ -296,6 +296,7 @@ extern const char *const scsi_device_types[MAX_SCSI_DEVICE_CODE];
 #define SCSI_1          1
 #define SCSI_1_CCS      2
 #define SCSI_2          3
+#define SCSI_3          4
 
 /*
  *  Every SCSI command starts with a one byte OP-code.
index 459a245d577bd58f7eeaa3aa47ef4ed5ef4a6834..2b0e98e3e8f90444b8702e6381fdcdbdf3980aff 100644 (file)
@@ -298,7 +298,7 @@ void proc_print_scsidevice(Scsi_Device *scd, char *buffer, int *size, int len)
                     scd->type < MAX_SCSI_DEVICE_CODE ? 
                     scsi_device_types[(int)scd->type] : "Unknown          " );
     y += sprintf(buffer + len + y, "               ANSI"
-                    " SCSI revision: %02x", (scd->scsi_level < 3)?1:2);
+                    " SCSI revision: %02x", (scd->scsi_level - 1)?scd->scsi_level - 1:1);
     if (scd->scsi_level == 2)
        y += sprintf(buffer + len + y, " CCS\n");
     else
index e73335a24566e7d26b67a321f6d5a043a9e8cee0..1d45432b5de7e5f8f0851075bb73af3d8b19eff1 100644 (file)
 #include "constants.h"
 
 #include "sd.h"
+#include <scsi/scsicam.h>
 /*
  * This source file contains the symbol table used by scsi loadable
  * modules.
  */
-extern int scsicam_bios_param (Disk * disk,
-                               int dev,        int *ip ); 
-
 
 extern void print_command (unsigned char *command);
 extern void print_sense(const char * devclass, Scsi_Cmnd * SCpnt);
@@ -47,6 +45,7 @@ EXPORT_SYMBOL(scsi_malloc);
 EXPORT_SYMBOL(scsi_register);
 EXPORT_SYMBOL(scsi_unregister);
 EXPORT_SYMBOL(scsicam_bios_param);
+EXPORT_SYMBOL(scsi_partsize);
 EXPORT_SYMBOL(scsi_allocate_device);
 EXPORT_SYMBOL(scsi_do_cmd);
 EXPORT_SYMBOL(scsi_command_size);
index 9aa0d8fbc1caa2bd08cf14e14c6b8c40887b1d5f..a3016e00e88b065d80d9a8d9382ffe460a04bcf4 100644 (file)
@@ -21,9 +21,8 @@
 #include "scsi.h"
 #include "hosts.h"
 #include "sd.h"
+#include <scsi/scsicam.h>
 
-static int partsize(struct buffer_head *bh, unsigned long capacity,
-    unsigned int  *cyls, unsigned int *hds, unsigned int *secs);
 static int setsize(unsigned long capacity,unsigned int *cyls,unsigned int *hds,
     unsigned int *secs);
 
@@ -51,7 +50,7 @@ int scsicam_bios_param (Disk *disk, /* SCSI disk */
        return -1;
 
     /* try to infer mapping from partition table */
-    ret_code = partsize (bh, (unsigned long) size, (unsigned int *) ip + 2, 
+    ret_code = scsi_partsize (bh, (unsigned long) size, (unsigned int *) ip + 2, 
        (unsigned int *) ip + 0, (unsigned int *) ip + 1);
     brelse (bh);
 
@@ -80,7 +79,7 @@ int scsicam_bios_param (Disk *disk, /* SCSI disk */
 }
 
 /*
- * Function : static int partsize(struct buffer_head *bh, unsigned long 
+ * Function : static int scsi_partsize(struct buffer_head *bh, unsigned long 
  *     capacity,unsigned int *cyls, unsigned int *hds, unsigned int *secs);
  *
  * Purpose : to determine the BIOS mapping used to create the partition
@@ -90,7 +89,7 @@ int scsicam_bios_param (Disk *disk, /* SCSI disk */
  *
  */
 
-static int partsize(struct buffer_head *bh, unsigned long capacity,
+int scsi_partsize(struct buffer_head *bh, unsigned long capacity,
     unsigned int  *cyls, unsigned int *hds, unsigned int *secs) {
     struct partition *p, *largest = NULL;
     int i, largest_cyl;
index 1f9e41ac8003c4d0d3cd2c16ead5711f4b1149ab..522b790edc59fa35c34220e52c311563392498e2 100644 (file)
@@ -1,4 +1,4 @@
-/*  $Id: atyfb.c,v 1.106 1999/04/16 11:20:49 geert Exp $
+/*  $Id: atyfb.c,v 1.107 1999/06/08 19:59:03 geert Exp $
  *  linux/drivers/video/atyfb.c -- Frame buffer device for ATI Mach64
  *
  *     Copyright (C) 1997-1998  Geert Uytterhoeven
@@ -200,6 +200,7 @@ struct fb_info_aty {
     struct atyfb_par default_par;
     struct atyfb_par current_par;
     u32 total_vram;
+    u32 ref_clk_per;
     u32 pll_per;
     u32 mclk_per;
     u16 chip_type;
@@ -326,9 +327,7 @@ static void reset_engine(const struct fb_info_aty *info);
 static void init_engine(const struct atyfb_par *par, struct fb_info_aty *info);
 static void aty_st_514(int offset, u8 val, const struct fb_info_aty *info);
 static void aty_st_pll(int offset, u8 val, const struct fb_info_aty *info);
-#if defined(__sparc__) || defined(DEBUG)
 static u8 aty_ld_pll(int offset, const struct fb_info_aty *info);
-#endif
 static void aty_set_crtc(const struct fb_info_aty *info,
                         const struct crtc *crtc);
 static int aty_var_to_crtc(const struct fb_info_aty *info,
@@ -341,7 +340,8 @@ static void aty_set_pll_gx(const struct fb_info_aty *info,
                           const struct pll_gx *pll);
 static int aty_var_to_pll_18818(u32 vclk_per, struct pll_gx *pll);
 static int aty_var_to_pll_514(u32 vclk_per, struct pll_gx *pll);
-static int aty_pll_gx_to_var(const struct pll_gx *pll, u32 *vclk_per);
+static int aty_pll_gx_to_var(const struct pll_gx *pll, u32 *vclk_per,
+                            const struct fb_info_aty *info);
 static void aty_set_pll_ct(const struct fb_info_aty *info,
                           const struct pll_ct *pll);
 static int aty_dsp_gt(const struct fb_info_aty *info, u8 mclk_fb_div,
@@ -349,7 +349,8 @@ static int aty_dsp_gt(const struct fb_info_aty *info, u8 mclk_fb_div,
                      u8 bpp, struct pll_ct *pll);
 static int aty_var_to_pll_ct(const struct fb_info_aty *info, u32 vclk_per,
                             u8 bpp, struct pll_ct *pll);
-static int aty_pll_ct_to_var(const struct pll_ct *pll, u32 *vclk_per);
+static int aty_pll_ct_to_var(const struct pll_ct *pll, u32 *vclk_per,
+                            const struct fb_info_aty *info);
 static void atyfb_set_par(const struct atyfb_par *par,
                          struct fb_info_aty *info);
 static int atyfb_decode_var(const struct fb_var_screeninfo *var,
@@ -405,8 +406,6 @@ static u32 default_vram __initdata = 0;
 static int default_pll __initdata = 0;
 static int default_mclk __initdata = 0;
 
-static const u32 ref_clk_per = 1000000000000ULL/14318180;
-
 #if defined(CONFIG_PPC)
 static int default_vmode __initdata = VMODE_NVRAM;
 static int default_cmode __initdata = CMODE_NVRAM;
@@ -689,7 +688,6 @@ static void aty_st_pll(int offset, u8 val, const struct fb_info_aty *info)
     aty_st_8(CLOCK_CNTL + 1, (offset << 2) & ~PLL_WR_EN, info);
 }
 
-#if defined(__sparc__) || defined(DEBUG)
 static u8 aty_ld_pll(int offset, const struct fb_info_aty *info)
 {
     u8 res;
@@ -702,7 +700,6 @@ static u8 aty_ld_pll(int offset, const struct fb_info_aty *info)
     eieio();
     return res;
 }
-#endif
 
 #if defined(CONFIG_PPC)
 
@@ -1455,7 +1452,8 @@ static int aty_var_to_pll_514(u32 vclk_per, struct pll_gx *pll)
 
     /* FIXME: ATI18818?? */
 
-static int aty_pll_gx_to_var(const struct pll_gx *pll, u32 *vclk_per)
+static int aty_pll_gx_to_var(const struct pll_gx *pll, u32 *vclk_per,
+                            const struct fb_info_aty *info)
 {
     u8 df, vco_div_count, ref_div_count;
 
@@ -1463,7 +1461,7 @@ static int aty_pll_gx_to_var(const struct pll_gx *pll, u32 *vclk_per)
     vco_div_count = pll->m & 0x3f;
     ref_div_count = pll->n;
 
-    *vclk_per = ((ref_clk_per*ref_div_count)<<(3-df))/(vco_div_count+65);
+    *vclk_per = ((info->ref_clk_per*ref_div_count)<<(3-df))/(vco_div_count+65);
 
     return 0;
 }
@@ -1579,10 +1577,10 @@ static int aty_var_to_pll_ct(const struct fb_info_aty *info, u32 vclk_per,
 
     pll->pll_vclk_cntl = 0x03; /* VCLK = PLL_VCLK/VCLKx_POST */
 
-    pll_ref_div = info->pll_per*2*255/ref_clk_per;
+    pll_ref_div = info->pll_per*2*255/info->ref_clk_per;
 
     /* FIXME: use the VTB/GTB /3 post divider if it's better suited */
-    q = ref_clk_per*pll_ref_div*4/info->mclk_per;      /* actually 8*q */
+    q = info->ref_clk_per*pll_ref_div*4/info->mclk_per;        /* actually 8*q */
     if (q < 16*8 || q > 255*8)
        FAIL("mclk out of range");
     else if (q < 32*8)
@@ -1596,7 +1594,7 @@ static int aty_var_to_pll_ct(const struct fb_info_aty *info, u32 vclk_per,
     mclk_fb_div = q*mclk_post_div/8;
 
     /* FIXME: use the VTB/GTB /{3,6,12} post dividers if they're better suited */
-    q = ref_clk_per*pll_ref_div*4/vclk_per;    /* actually 8*q */
+    q = info->ref_clk_per*pll_ref_div*4/vclk_per;      /* actually 8*q */
     if (q < 16*8 || q > 255*8)
        FAIL("vclk out of range");
     else if (q < 32*8)
@@ -1677,7 +1675,8 @@ static int aty_var_to_pll_ct(const struct fb_info_aty *info, u32 vclk_per,
     return 0;
 }
 
-static int aty_pll_ct_to_var(const struct pll_ct *pll, u32 *vclk_per)
+static int aty_pll_ct_to_var(const struct pll_ct *pll, u32 *vclk_per,
+                            const struct fb_info_aty *info)
 {
     u8 pll_ref_div = pll->pll_ref_div;
     u8 vclk_fb_div = pll->vclk_fb_div;
@@ -1691,7 +1690,7 @@ static int aty_pll_ct_to_var(const struct pll_ct *pll, u32 *vclk_per)
                                    (vclk_post_div & 3)];
     if (vpostdiv == 0)
        return -EINVAL;
-    *vclk_per = pll_ref_div*vpostdiv*ref_clk_per/vclk_fb_div/2;
+    *vclk_per = pll_ref_div*vpostdiv*info->ref_clk_per/vclk_fb_div/2;
     return 0;
 }
 
@@ -1845,9 +1844,9 @@ static int atyfb_encode_var(struct fb_var_screeninfo *var,
     if ((err = aty_crtc_to_var(&par->crtc, var)))
        return err;
     if ((Gx == GX_CHIP_ID) || (Gx == CX_CHIP_ID))
-       err = aty_pll_gx_to_var(&par->pll.gx, &var->pixclock);
+       err = aty_pll_gx_to_var(&par->pll.gx, &var->pixclock, info);
     else
-       err = aty_pll_ct_to_var(&par->pll.ct, &var->pixclock);
+       err = aty_pll_ct_to_var(&par->pll.ct, &var->pixclock, info);
     if (err)
        return err;
 
@@ -2432,11 +2431,12 @@ __initfunc(static int aty_init(struct fb_info_aty *info, const char *name))
     int j, k;
     struct fb_var_screeninfo var;
     struct display *disp;
-    const char *chipname = NULL, *ramname = NULL;
+    const char *chipname = NULL, *ramname = NULL, *xtal;
     int pll, mclk, gtb_memsize;
 #if defined(CONFIG_PPC)
     int sense;
 #endif
+    u8 pll_ref_div;
 
     info->aty_cmap_regs = (struct aty_cmap_regs *)(info->ati_regbase+0xc0);
     chip_id = aty_ld_le32(CONFIG_CHIP_ID, info);
@@ -2524,6 +2524,25 @@ __initfunc(static int aty_init(struct fb_info_aty *info, const char *name))
        }
     }
 
+    info->ref_clk_per = 1000000000000ULL/14318180;
+    xtal = "14.31818";
+    if (!(Gx == GX_CHIP_ID || Gx == CX_CHIP_ID || Gx == CT_CHIP_ID ||
+         Gx == ET_CHIP_ID ||
+         ((Gx == VT_CHIP_ID || Gx == GT_CHIP_ID) && !(Rev & 0x07))) &&
+       (pll_ref_div = aty_ld_pll(PLL_REF_DIV, info))) {
+       int diff1, diff2;
+       diff1 = 510*14/pll_ref_div-pll;
+       diff2 = 510*29/pll_ref_div-pll;
+       if (diff1 < 0)
+           diff1 = -diff1;
+       if (diff2 < 0)
+           diff2 = -diff2;
+       if (diff2 < diff1) {
+           info->ref_clk_per = 1000000000000ULL/29498928;
+           xtal = "29.498928";
+       }
+    }
+
     i = aty_ld_le32(MEM_CNTL, info);
     gtb_memsize = !(Gx == GX_CHIP_ID || Gx == CX_CHIP_ID || Gx == CT_CHIP_ID ||
                    Gx == ET_CHIP_ID ||
@@ -2602,9 +2621,9 @@ __initfunc(static int aty_init(struct fb_info_aty *info, const char *name))
     if (default_mclk)
        mclk = default_mclk;
 
-    printk("%d%c %s, %d MHz PLL, %d Mhz MCLK\n", 
+    printk("%d%c %s, %s MHz XTAL, %d MHz PLL, %d Mhz MCLK\n", 
           info->total_vram == 0x80000 ? 512 : (info->total_vram >> 20), 
-          info->total_vram == 0x80000 ? 'K' : 'M', ramname, pll, mclk);
+          info->total_vram == 0x80000 ? 'K' : 'M', ramname, xtal, pll, mclk);
 
     if (mclk < 44)
        info->mem_refresh_rate = 0;     /* 000 = 10 Mhz - 43 Mhz */
index d6173e77e7d6ee0cfcad736803491ed781708959..dbf56108cbcd5fbb7599e5cb08505ba0ff491c94 100644 (file)
@@ -630,6 +630,7 @@ __initfunc(void igafb_init(void))
         struct fb_info_iga *info;
         unsigned long addr;
         extern int con_is_present(void);
+       int iga2000 = 0;
 
         /* Do not attach when we have a serial console. */
         if (!con_is_present())
@@ -637,8 +638,13 @@ __initfunc(void igafb_init(void))
 
         pdev = pci_find_device(PCI_VENDOR_ID_INTERG, 
                                PCI_DEVICE_ID_INTERG_1682, 0);
-        if(pdev == NULL)
-                return;
+       if (pdev == NULL) {
+               pdev = pci_find_device(PCI_VENDOR_ID_INTERG, 
+                               0x2000, 0);
+               if(pdev == NULL)
+                       return;
+               iga2000 = 1;
+       }
 
         info = kmalloc(sizeof(struct fb_info_iga), GFP_ATOMIC);
         if (!info) {
@@ -648,8 +654,10 @@ __initfunc(void igafb_init(void))
         memset(info, 0, sizeof(struct fb_info_iga));
 
        info->frame_buffer = pdev->base_address[0];
-       if (!info->frame_buffer)
+       if (!info->frame_buffer) {
+               kfree(info);
                return;
+       }
 
         pcibios_read_config_dword(0, pdev->devfn,
                                   PCI_BASE_ADDRESS_0, 
@@ -659,12 +667,23 @@ __initfunc(void igafb_init(void))
        info->frame_buffer_phys = addr & PCI_BASE_ADDRESS_MEM_MASK;
 
 #ifdef __sparc__
-       
+
        info->io_base_phys = info->frame_buffer_phys;
-       
-       /* Obtain virtual address and correct physical by PCIC shift */
-       info->io_base = pcic_alloc_io(&info->io_base_phys);
+
+       /*
+        * The right test would be to look if there is a base I/O address.
+        * But it appears that IGA 1682 reuses _memory_ address as a base
+        * for I/O accesses.
+        */
+       if (iga2000) {
+               info->io_base = (int) sparc_alloc_io(info->frame_buffer_phys |
+                   0x00800000, NULL, 0x1000, "iga", 0, 0);
+       } else {
+               /* Obtain virtual address and correct physical by PCIC shift */
+               info->io_base = pcic_alloc_io(&info->io_base_phys);
+       }
        if (!info->io_base) {
+                kfree(info);
                return;
        }
 
@@ -679,6 +698,7 @@ __initfunc(void igafb_init(void))
        info->mmap_map = kmalloc(4 * sizeof(*info->mmap_map), GFP_ATOMIC);
        if (!info->mmap_map) {
                 printk("igafb_init: can't alloc mmap_map\n");
+               /* XXX Here we left I/O allocated */
                 kfree(info);
                return;
        }
@@ -730,11 +750,11 @@ __initfunc(void igafb_init(void))
             }
 
 #endif
-            if (!iga_init(info)) {
-                   if (info->mmap_map)
-                           kfree(info->mmap_map);
-                   kfree(info);
-            }
+        if (!iga_init(info)) {
+               if (info->mmap_map)
+                       kfree(info->mmap_map);
+               kfree(info);
+        }
 
 #ifdef __sparc__
            /*
index 0f73596ea3ee9a25994cd276512e81e6b9bacf0c..0d1c4ac0d4c07b272979ed3dfa2e19019d0279ea 100644 (file)
@@ -73,9 +73,9 @@ if [ "$CONFIG_INET" = "y" ]; then
   fi
   if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
     tristate 'NFS server support' CONFIG_NFSD
-  fi
-  if [ "$CONFIG_NFSD" != "n" ]; then
-    bool '   Emulate SUN NFS server' CONFIG_NFSD_SUN
+    if [ "$CONFIG_NFSD" != "n" ]; then
+      bool '   Emulate SUN NFS server' CONFIG_NFSD_SUN
+    fi
   fi
   if [ "$CONFIG_NFS_FS" = "y" -o "$CONFIG_NFSD" = "y" ]; then
     define_bool CONFIG_SUNRPC y
index 311b35c30060a326348942787ac357dd4b71bfdf..ae22c4900f0de1d07cd13a493bdc3ccbb2ab0eba 100644 (file)
@@ -383,7 +383,7 @@ static inline int do_load_aout_binary(struct linux_binprm * bprm, struct pt_regs
        if (N_MAGIC(ex) == OMAGIC) {
 #if defined(__alpha__) || defined(__sparc__)
                do_brk(N_TXTADDR(ex) & PAGE_MASK,
-                       ex.a_text+ex.a_data + PAGE_SIZE - 1)
+                       ex.a_text+ex.a_data + PAGE_SIZE - 1);
                read_exec(bprm->dentry, fd_offset, (char *) N_TXTADDR(ex),
                          ex.a_text+ex.a_data, 0);
 #else
index 361abd06821b1282cfaf6197c1cb85a92d2e056d..826cb41768e03d3e05c1f10dde027e5e8b54a111 100644 (file)
@@ -68,6 +68,7 @@ static struct dentry * ext2_follow_link(struct dentry * dentry,
                }
                link = bh->b_data;
        }
+       UPDATE_ATIME(inode);
        base = lookup_dentry(link, base, follow);
        if (bh)
                brelse(bh);
@@ -101,7 +102,6 @@ static int ext2_readlink (struct dentry * dentry, char * buffer, int buflen)
                i++;
        if (copy_to_user(buffer, link, i))
                i = -EFAULT;
-       UPDATE_ATIME(inode);
        if (bh)
                brelse (bh);
        return i;
index b1a6ca1bc80ac4b61fec9a41ab83c661328f6ec9..69d0bf6780593560ed738a4e037fee62ca5b5222 100644 (file)
@@ -23,7 +23,7 @@ __mips_lookup_dentry(const char *name, int lookup_flags)
 
        base = lookup_dentry (IRIX32_EMUL,
                        dget (current->fs->root), 
-                       (LOOKUP_FOLLOW | LOOKUP_DIRECTORY | LOOKUP_SLASHOK));
+                       (LOOKUP_FOLLOW | LOOKUP_DIRECTORY));
                        
        if (IS_ERR (base)) return base;
        
index 6e2b32c5add88375f2d0393e9970c6c102344c03..da901ab886e0aaeaa80fe2c39571150b37f7977b 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: io.h,v 1.19 1999/05/14 07:26:09 davem Exp $ */
+/* $Id: io.h,v 1.20 1999/06/03 15:02:50 davem Exp $ */
 #ifndef __SPARC_IO_H
 #define __SPARC_IO_H
 
@@ -162,6 +162,15 @@ static __inline__ void *sparc_dvma_malloc(int size, char *name, __u32 *dvmaaddr_
 #define virt_to_phys(x) __pa((unsigned long)(x))
 #define phys_to_virt(x) __va((unsigned long)(x))
 
+/*
+ * At the moment, we do not use CMOS_READ anywhere outside of rtc.c,
+ * so rtc_port is static in it. This should not change unless a new
+ * hardware pops up.
+ */
+
+#define RTC_PORT(x)   (rtc_port + (x))
+#define RTC_ALWAYS_BCD  0
+
 /* Nothing to do */
 
 #define dma_cache_inv(_start,_size)            do { } while (0)
index 8d6b911fc19869918769d44115b97631534385fe..e03f86c7585938dd56971ff1a7d56689e2de1963 100644 (file)
@@ -28,7 +28,7 @@ __sparc_lookup_dentry(const char *name, int lookup_flags)
 
        base = lookup_dentry (emul, 
                              dget (current->fs->root),
-                             (LOOKUP_FOLLOW | LOOKUP_DIRECTORY | LOOKUP_SLASHOK));
+                             (LOOKUP_FOLLOW | LOOKUP_DIRECTORY));
                        
        if (IS_ERR (base)) return NULL;
        
index c0d4dbbf8f92c17de986ab06036b55fb6e21c799..bad175c72dcc0142455722c46786c205085a8d5d 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: pcic.h,v 1.1 1998/09/22 05:54:39 jj Exp $
+/* $Id: pcic.h,v 1.2 1999/06/03 15:02:51 davem Exp $
  * pcic.h: JavaEngine 1 specific PCI definitions.
  *
  * Copyright (C) 1998 V. Roganov and G. Raiko
@@ -7,6 +7,8 @@
 #ifndef __SPARC_PCIC_H
 #define __SPARC_PCIC_H
 
+#ifndef __ASSEMBLY__
+
 #include <linux/types.h>
 #include <linux/smp.h>
 #include <linux/smp_lock.h>
@@ -21,13 +23,17 @@ struct linux_pcic {
         unsigned long           pcic_config_space_addr;
         unsigned long           pcic_config_space_data;
         struct linux_pbm_info   pbm;
+       struct pcic_ca2irq      *pcic_imap;
+       int                     pcic_imdim;
 };
 
 extern unsigned long pcic_alloc_io(unsigned long* addr);
 extern void pcic_probe(void);
 extern void sun4m_pci_init_IRQ(void);
 
-/* Size of PCI Space */
+#endif
+
+/* Size of PCI I/O space which we relocate. */
 #define PCI_SPACE_SIZE                  0x1000000       /* 16 MB */
 
 /* PCIC Register Set. */
@@ -50,10 +56,18 @@ extern void sun4m_pci_init_IRQ(void);
 #define PCI_SOFTWARE_INT_CLEAR          0x6a    /* 16 bits */
 #define PCI_SOFTWARE_INT_SET            0x6e    /* 16 bits */
 #define PCI_SYS_INT_PENDING             0x70    /* 32 bits */
+#define  PCI_SYS_INT_PENDING_PIO               0x40000000
+#define  PCI_SYS_INT_PENDING_DMA               0x20000000
+#define  PCI_SYS_INT_PENDING_PCI               0x10000000
+#define  PCI_SYS_INT_PENDING_APSR              0x08000000
 #define PCI_SYS_INT_TARGET_MASK         0x74    /* 32 bits */
 #define PCI_SYS_INT_TARGET_MASK_CLEAR   0x78    /* 32 bits */
 #define PCI_SYS_INT_TARGET_MASK_SET     0x7c    /* 32 bits */
 #define PCI_SYS_INT_PENDING_CLEAR       0x83    /* 8  bits */
+#define  PCI_SYS_INT_PENDING_CLEAR_ALL         0x80
+#define  PCI_SYS_INT_PENDING_CLEAR_PIO         0x40
+#define  PCI_SYS_INT_PENDING_CLEAR_DMA         0x20
+#define  PCI_SYS_INT_PENDING_CLEAR_PCI         0x10
 #define PCI_IOTLB_CONTROL               0x84    /* 8  bits */
 #define PCI_INT_SELECT_LO               0x88    /* 16 bits */
 #define PCI_ARBITRATION_SELECT          0x8a    /* 16 bits */
index cd24e96cf553cb020906e972ce81ebf98d7ed947..2dbcdedf3bb8939b271a9b622f81701335ae46a1 100644 (file)
@@ -28,7 +28,7 @@ __sparc64_lookup_dentry(const char *name, int lookup_flags)
 
        base = lookup_dentry (emul, 
                              dget (current->fs->root), 
-                             (LOOKUP_FOLLOW | LOOKUP_DIRECTORY | LOOKUP_SLASHOK));
+                             (LOOKUP_FOLLOW | LOOKUP_DIRECTORY));
                        
        if (IS_ERR (base)) return NULL;
        
index c13afde289a686eb870fc2555ba02be1f3bb1e3d..f3f6c236377648b278a19a278307d7cc6a30223f 100644 (file)
@@ -101,19 +101,7 @@ struct ip_mc_list
        char                    loaded;
 };
 
-extern __inline__ int ip_check_mc(struct device *dev, u32 mc_addr)
-{
-       struct in_device *in_dev = dev->ip_ptr;
-       struct ip_mc_list *im;
-
-       if (in_dev) {
-               for (im=in_dev->mc_list; im; im=im->next)
-                       if (im->multiaddr == mc_addr)
-                               return 1;
-       }
-       return 0;
-}
-
+extern int ip_check_mc(struct device *dev, u32 mc_addr);
 extern int igmp_rcv(struct sk_buff *, unsigned short);
 extern int ip_mc_join_group(struct sock *sk, struct ip_mreqn *imr);
 extern int ip_mc_leave_group(struct sock *sk, struct ip_mreqn *imr);
index acb93765fae4190a991d63477aaf7c0b20a46c9f..6a8e27bcd414a30c8593c427bddaed5c164ddbbd 100644 (file)
@@ -46,6 +46,7 @@
 
 extern void            inet_proto_init(struct net_proto *pro);
 extern char            *in_ntoa(__u32 in);
+extern char            *in_ntoa2(__u32 in, char *buf);
 extern __u32           in_aton(const char *str);
 
 #endif
index 25c4495980f8a68b52eb86de66aad54eec2d836d..d4de5b91889c7e2cef056612faf0cde664e9b367 100644 (file)
@@ -74,7 +74,7 @@ extern int            devinet_ioctl(unsigned int cmd, void *);
 extern void            devinet_init(void);
 extern struct in_device *inetdev_init(struct device *dev);
 extern struct in_device        *inetdev_by_index(int);
-extern u32             inet_select_addr(struct device *dev, u32 dst, int scope);
+extern u32             inet_select_addr(const struct device *dev, u32 dst, int scope);
 extern struct in_ifaddr *inet_ifa_byprefix(struct in_device *in_dev, u32 prefix, u32 mask);
 extern void            inet_forward_change(void);
 
index a222e8d92efc17f6cd136c15c37442f2fe059be0..5fbcbad86c6fa48d09161b157203141dc3eab609 100644 (file)
@@ -152,6 +152,7 @@ struct hh_cache
        struct hh_cache *hh_next;       /* Next entry                        */
        atomic_t        hh_refcnt;      /* number of users                   */
        unsigned short  hh_type;        /* protocol identifier, f.e ETH_P_IP */
+       int             hh_len;         /* length of header */
        int             (*hh_output)(struct sk_buff *skb);
        rwlock_t        hh_lock;
        /* cached hardware header; allow for machine alignment needs.        */
@@ -260,6 +261,7 @@ struct device
        void                    *atalk_ptr;     /* AppleTalk link       */
        void                    *ip_ptr;        /* IPv4 specific data   */  
        void                    *dn_ptr;        /* DECnet specific data */
+       void                    *ip6_ptr;       /* IPv6 specific data */
 
        struct Qdisc            *qdisc;
        struct Qdisc            *qdisc_sleeping;
@@ -268,6 +270,13 @@ struct device
 
        /* hard_start_xmit synchronizer */
        spinlock_t              xmit_lock;
+       /* cpu id of processor entered to hard_start_xmit or -1,
+          if nobody entered there.
+        */
+       int                     xmit_lock_owner;
+       /* device queue lock */
+       spinlock_t              queue_lock;
+       atomic_t                refcnt;
 
        /* Pointers to interface service routines.      */
        int                     (*open)(struct device *dev);
index 5f69e9451e76d3b07778eaf97d6120ca9f01fb7e..45bac06e5883de93ba625ab8d2a02396caf8aa34 100644 (file)
 
 #define PCI_VENDOR_ID_ADAPTEC          0x9004
 #define PCI_DEVICE_ID_ADAPTEC_7810     0x1078
+#define PCI_DEVICE_ID_ADAPTEC_7821     0x2178
 #define PCI_DEVICE_ID_ADAPTEC_7850     0x5078
 #define PCI_DEVICE_ID_ADAPTEC_7855     0x5578
 #define PCI_DEVICE_ID_ADAPTEC_5800     0x5800
+#define PCI_DEVICE_ID_ADAPTEC_3860     0x6038
 #define PCI_DEVICE_ID_ADAPTEC_1480A    0x6075
 #define PCI_DEVICE_ID_ADAPTEC_7860     0x6078
 #define PCI_DEVICE_ID_ADAPTEC_7861     0x6178
 #define PCI_DEVICE_ID_ADAPTEC_7882     0x8278
 #define PCI_DEVICE_ID_ADAPTEC_7883     0x8378
 #define PCI_DEVICE_ID_ADAPTEC_7884     0x8478
+#define PCI_DEVICE_ID_ADAPTEC_7885     0x8578
+#define PCI_DEVICE_ID_ADAPTEC_7886     0x8678
+#define PCI_DEVICE_ID_ADAPTEC_7887     0x8778
+#define PCI_DEVICE_ID_ADAPTEC_7888     0x8878
 #define PCI_DEVICE_ID_ADAPTEC_1030     0x8b78
 
 #define PCI_VENDOR_ID_ADAPTEC2         0x9005
 #define PCI_DEVICE_ID_ADAPTEC2_2940U2  0x0010
-#define PCI_DEVICE_ID_ADAPTEC2_78902   0x0013
+#define PCI_DEVICE_ID_ADAPTEC2_2930U2  0x0011
+#define PCI_DEVICE_ID_ADAPTEC2_7890B   0x0013
 #define PCI_DEVICE_ID_ADAPTEC2_7890    0x001f
 #define PCI_DEVICE_ID_ADAPTEC2_3940U2  0x0050
 #define PCI_DEVICE_ID_ADAPTEC2_3950U2D 0x0051
 #define PCI_DEVICE_ID_ADAPTEC2_7896    0x005f
+#define PCI_DEVICE_ID_ADAPTEC2_7892A   0x0080
+#define PCI_DEVICE_ID_ADAPTEC2_7892B   0x0081
+#define PCI_DEVICE_ID_ADAPTEC2_7892D   0x0083
+#define PCI_DEVICE_ID_ADAPTEC2_7892P   0x008f
+#define PCI_DEVICE_ID_ADAPTEC2_7899A   0x00c0
+#define PCI_DEVICE_ID_ADAPTEC2_7899B   0x00c1
+#define PCI_DEVICE_ID_ADAPTEC2_7899D   0x00c3
+#define PCI_DEVICE_ID_ADAPTEC2_7899P   0x00cf
 
 #define PCI_VENDOR_ID_ATRONICS         0x907f
 #define PCI_DEVICE_ID_ATRONICS_2015    0x2015
index 4ec170dbd27a9ec00e4f0bf137e5b9fa1dca7273..4f804d15f42bf9b2b739c3bb8bd30ec24f7025e9 100644 (file)
@@ -38,6 +38,9 @@ struct tc_stats
        __u32   pps;                    /* Current flow packet rate */
        __u32   qlen;
        __u32   backlog;
+#ifdef __KERNEL__
+       spinlock_t *lock;
+#endif
 };
 
 struct tc_estimator
index 7e6349a6ddd44631c6544e97632fdd7e99dbaa39..fdbdedfe248f3ae6bb378ff5be0ae098093309d8 100644 (file)
@@ -515,9 +515,6 @@ enum
 
 #ifdef __KERNEL__
 
-extern atomic_t rtnl_rlockct;
-extern wait_queue_head_t rtnl_wait;
-
 extern __inline__ int rtattr_strcmp(struct rtattr *rta, char *str)
 {
        int len = strlen(str) + 1;
@@ -544,128 +541,32 @@ extern void __rta_fill(struct sk_buff *skb, int attrtype, int attrlen, const voi
 #define RTA_PUT(skb, attrtype, attrlen, data) \
 ({ if (skb_tailroom(skb) < (int)RTA_SPACE(attrlen)) goto rtattr_failure; \
    __rta_fill(skb, attrtype, attrlen, data); })
-
-extern unsigned long rtnl_wlockct;
-
-/* NOTE: these locks are not interrupt safe, are not SMP safe,
- * they are even not atomic. 8)8)8) ... and it is not a bug.
- * Really, if these locks will be programmed correctly,
- * all the addressing/routing machine would become SMP safe,
- * but is absolutely useless at the moment, because all the kernel
- * is not reenterable in any case. --ANK
- *
- * Well, atomic_* and set_bit provide the only thing here:
- * gcc is confused not to overoptimize them, that's all.
- * I remember as gcc splitted ++ operation, but cannot reproduce
- * it with gcc-2.7.*. --ANK
- *
- * One more note: rwlock facility should be written and put
- * to a kernel wide location: f.e. current implementation of semaphores
- * (especially, for x86) looks like a wonder. It would be good
- * to have something similar for rwlock. Recursive lock could be also
- * useful thing. --ANK
- */
-
-extern __inline__ int rtnl_shlock_nowait(void)
-{
-       atomic_inc(&rtnl_rlockct);
-       if (test_bit(0, &rtnl_wlockct)) {
-               atomic_dec(&rtnl_rlockct);
-               return -EAGAIN;
-       }
-       return 0;
-}
-
-extern __inline__ void rtnl_shlock(void)
-{
-       while (rtnl_shlock_nowait())
-               sleep_on(&rtnl_wait);
-}
-
-/* Check for possibility to PROMOTE shared lock to exclusive.
-   Shared lock must be already grabbed with rtnl_shlock*().
- */
-
-extern __inline__ int rtnl_exlock_nowait(void)
-{
-       if (atomic_read(&rtnl_rlockct) > 1)
-               return -EAGAIN;
-       if (test_and_set_bit(0, &rtnl_wlockct))
-               return -EAGAIN;
-       return 0;
-}
-
-extern __inline__ void rtnl_exlock(void)
-{
-       while (rtnl_exlock_nowait())
-               sleep_on(&rtnl_wait);
-}
-
-#if 0
-extern __inline__ void rtnl_shunlock(void)
-{
-       atomic_dec(&rtnl_rlockct);
-       if (atomic_read(&rtnl_rlockct) <= 1) {
-               wake_up(&rtnl_wait);
-               if (rtnl && rtnl->receive_queue.qlen)
-                       rtnl->data_ready(rtnl, 0);
-       }
-}
-#else
-
-/* The problem: inline requires to include <net/sock.h> and, hence,
-   almost all of net includes :-(
- */
-
-#define rtnl_shunlock() ({ \
-       atomic_dec(&rtnl_rlockct); \
-       if (atomic_read(&rtnl_rlockct) <= 1) { \
-               wake_up(&rtnl_wait); \
-               if (rtnl && rtnl->receive_queue.qlen) \
-                       rtnl->data_ready(rtnl, 0); \
-       } \
-})
 #endif
 
-/* Release exclusive lock. Note, that we do not wake up rtnetlink socket,
- * it will be done later after releasing shared lock.
- */
-
-extern __inline__ void rtnl_exunlock(void)
-{
-       clear_bit(0, &rtnl_wlockct);
-       wake_up(&rtnl_wait);
-}
+extern struct semaphore rtnl_sem;
 
-#else
+#define rtnl_exlock()          do { } while(0)
+#define rtnl_exunlock()                do { } while(0)
+#define rtnl_exlock_nowait()   (0)
 
-extern __inline__ void rtnl_shlock(void)
-{
-       while (atomic_read(&rtnl_rlockct))
-               sleep_on(&rtnl_wait);
-       atomic_inc(&rtnl_rlockct);
-}
-
-extern __inline__ void rtnl_shunlock(void)
-{
-       if (atomic_dec_and_test(&rtnl_rlockct))
-               wake_up(&rtnl_wait);
-}
-
-extern __inline__ void rtnl_exlock(void)
-{
-}
-
-extern __inline__ void rtnl_exunlock(void)
-{
-}
+#define rtnl_shlock()          down(&rtnl_sem)
+#define rtnl_shlock_nowait()   down_trylock(&rtnl_sem)
 
+#ifndef CONFIG_RTNETLINK
+#define rtnl_shunlock()        up(&rtnl_sem)
+#else
+#define rtnl_shunlock()        do { up(&rtnl_sem); \
+                            if (rtnl && rtnl->receive_queue.qlen) \
+                                    rtnl->data_ready(rtnl, 0); \
+                       } while(0)
 #endif
 
 extern void rtnl_lock(void);
 extern void rtnl_unlock(void);
 extern void rtnetlink_init(void);
 
+
+
 #endif /* __KERNEL__ */
 
 
index d711d0d514a08acf026c67fd6eed2f9b0a28ea0f..730fe97b4dd2e2d44786376d746723562bee4a77 100644 (file)
@@ -56,7 +56,7 @@ extern struct inet6_ifaddr *  ipv6_chk_addr(struct in6_addr *addr,
 extern int                     ipv6_get_saddr(struct dst_entry *dst, 
                                               struct in6_addr *daddr,
                                               struct in6_addr *saddr);
-extern struct inet6_ifaddr *   ipv6_get_lladdr(struct device *dev);
+extern int                     ipv6_get_lladdr(struct device *dev, struct in6_addr *);
 
 /*
  *     multicast prototypes (mcast.c)
@@ -68,6 +68,7 @@ extern int                    ipv6_sock_mc_drop(struct sock *sk,
                                                  int ifindex, 
                                                  struct in6_addr *addr);
 extern void                    ipv6_sock_mc_close(struct sock *sk);
+extern int                     inet6_mc_check(struct sock *sk, struct in6_addr *addr);
 
 extern int                     ipv6_dev_mc_inc(struct device *dev,
                                                struct in6_addr *addr);
index 0c75539c36e306e6ccc24fcaf601a2ca45fbc190..d34eb5079ef53e336d6e042eba8bf9277d628bb9 100644 (file)
@@ -170,6 +170,9 @@ extern __inline__ void dst_set_expires(struct dst_entry *dst, int timeout)
        if (dst->expires == 0 || (long)(dst->expires - expires) > 0)
                dst->expires = expires;
 }
+
+extern void            dst_init(void);
+
 #endif
 
 #endif /* _NET_DST_H */
index 4e9ed9780a0ec1510416fccaf82b0ee6dbcfe31e..3702028b6419fae861b75aaa4aad944260781013 100644 (file)
@@ -44,6 +44,7 @@ struct inet6_ifaddr
        __u32                   valid_lft;
        __u32                   prefered_lft;
        unsigned long           tstamp;
+       atomic_t                refcnt;
 
        __u8                    probes;
        __u8                    flags;
@@ -108,6 +109,7 @@ struct inet6_dev
 
        struct inet6_ifaddr     *addr_list;
        struct ifmcaddr6        *mc_list;
+       rwlock_t                lock;
        __u32                   if_flags;
 
        struct neigh_parms      *nd_parms;
index 3874564855bdd073aea1c34c33547853fba2064e..c078484c3226c81cce1b32129a88f727659c67e9 100644 (file)
@@ -146,10 +146,10 @@ extern __inline__ int ip_finish_output(struct sk_buff *skb)
        skb->protocol = __constant_htons(ETH_P_IP);
 
        if (hh) {
-               read_lock_irq(&hh->hh_lock);
+               read_lock_bh(&hh->hh_lock);
                memcpy(skb->data - 16, hh->hh_data, 16);
-               read_unlock_irq(&hh->hh_lock);
-               skb_push(skb, dev->hard_header_len);
+               read_unlock_bh(&hh->hh_lock);
+               skb_push(skb, hh->hh_len);
                return hh->hh_output(skb);
        } else if (dst->neighbour)
                return dst->neighbour->output(skb);
index 5c5d90bb01c4c9851a26638353ac7ea1fcc7ca33..95803be465249fd3b4f212fd710339f9f4be7c8d 100644 (file)
@@ -96,7 +96,8 @@ struct neighbour
        __u8                    flags;
        __u8                    nud_state;
        __u8                    type;
-       __u8                    probes;
+       atomic_t                probes;
+       rwlock_t                lock;
        unsigned char           ha[MAX_ADDR_LEN];
        struct hh_cache         *hh;
        atomic_t                refcnt;
@@ -155,7 +156,7 @@ struct neigh_table
        struct timer_list       proxy_timer;
        struct sk_buff_head     proxy_queue;
        int                     entries;
-       atomic_t                lock;
+       rwlock_t                lock;
        unsigned long           last_rand;
        struct neigh_parms      *parms_list;
        struct neigh_statistics stats;
@@ -165,9 +166,12 @@ struct neigh_table
 
 extern void                    neigh_table_init(struct neigh_table *tbl);
 extern int                     neigh_table_clear(struct neigh_table *tbl);
-extern struct neighbour                *__neigh_lookup(struct neigh_table *tbl,
-                                              const void *pkey, struct device *dev,
-                                              int creat);
+extern struct neighbour *      neigh_lookup(struct neigh_table *tbl,
+                                            const void *pkey,
+                                            struct device *dev);
+extern struct neighbour *      neigh_create(struct neigh_table *tbl,
+                                            const void *pkey,
+                                            struct device *dev);
 extern void                    neigh_destroy(struct neighbour *neigh);
 extern int                     __neigh_event_send(struct neighbour *neigh, struct sk_buff *skb);
 extern int                     neigh_update(struct neighbour *neigh, u8 *lladdr, u8 new, int override, int arp);
@@ -226,16 +230,6 @@ extern __inline__ void neigh_confirm(struct neighbour *neigh)
                neigh->confirmed = jiffies;
 }
 
-extern __inline__ struct neighbour *
-neigh_lookup(struct neigh_table *tbl, const void *pkey, struct device *dev)
-{
-       struct neighbour *neigh;
-       start_bh_atomic();
-       neigh = __neigh_lookup(tbl, pkey, dev, 0);
-       end_bh_atomic();
-       return neigh;
-}
-
 extern __inline__ int neigh_is_connected(struct neighbour *neigh)
 {
        return neigh->nud_state&NUD_CONNECTED;
@@ -254,17 +248,16 @@ extern __inline__ int neigh_event_send(struct neighbour *neigh, struct sk_buff *
        return 0;
 }
 
-extern __inline__ void neigh_table_lock(struct neigh_table *tbl)
+extern __inline__ struct neighbour *
+__neigh_lookup(struct neigh_table *tbl, const void *pkey, struct device *dev, int creat)
 {
-       atomic_inc(&tbl->lock);
-       synchronize_bh();
-}
+       struct neighbour *n = neigh_lookup(tbl, pkey, dev);
 
-extern __inline__ void neigh_table_unlock(struct neigh_table *tbl)
-{
-       atomic_dec(&tbl->lock);
-}
+       if (n || !creat)
+               return n;
 
+       return neigh_create(tbl, pkey, dev);
+}
 
 #endif
 #endif
index 4c37d11ed2adac83c8ff23729aeaf96cafdf39f8..f9c80dd0fbbf0d8e87b684886b3d5009fa1b26d6 100644 (file)
@@ -77,14 +77,11 @@ extern __inline__ int tc_classify(struct sk_buff *skb, struct tcf_proto *tp, str
        return -1;
 }
 
-extern __inline__ unsigned long cls_set_class(unsigned long *clp, unsigned long cl)
-{
-       cl = xchg(clp, cl);
-       synchronize_bh();
-       return cl;
-}
+
 
 extern int register_tcf_proto_ops(struct tcf_proto_ops *ops);
 extern int unregister_tcf_proto_ops(struct tcf_proto_ops *ops);
 
+
+
 #endif
index 9911464fc65e5e24a06d02798696ba014710e956..46262159dedad96f88ee98e5e1e4544bf57630f5 100644 (file)
@@ -66,9 +66,12 @@ struct Qdisc_ops
 struct Qdisc_head
 {
        struct Qdisc_head *forw;
+       struct Qdisc_head *back;
 };
 
 extern struct Qdisc_head qdisc_head;
+extern spinlock_t qdisc_runqueue_lock;
+extern rwlock_t qdisc_tree_lock;
 
 struct Qdisc
 {
@@ -106,6 +109,46 @@ struct qdisc_rate_table
        int             refcnt;
 };
 
+extern __inline__ void sch_tree_lock(struct Qdisc *q)
+{
+       write_lock(&qdisc_tree_lock);
+       spin_lock_bh(&q->dev->queue_lock);
+}
+
+extern __inline__ void sch_tree_unlock(struct Qdisc *q)
+{
+       spin_unlock_bh(&q->dev->queue_lock);
+       write_unlock(&qdisc_tree_lock);
+}
+
+extern __inline__ void tcf_tree_lock(struct tcf_proto *tp)
+{
+       write_lock(&qdisc_tree_lock);
+       spin_lock_bh(&tp->q->dev->queue_lock);
+}
+
+extern __inline__ void tcf_tree_unlock(struct tcf_proto *tp)
+{
+       spin_unlock_bh(&tp->q->dev->queue_lock);
+       write_unlock(&qdisc_tree_lock);
+}
+
+
+extern __inline__ unsigned long
+cls_set_class(struct tcf_proto *tp, unsigned long *clp, unsigned long cl)
+{
+       tcf_tree_lock(tp);
+       cl = xchg(clp, cl);
+       tcf_tree_unlock(tp);
+       return cl;
+}
+
+extern __inline__ unsigned long
+__cls_set_class(unsigned long *clp, unsigned long cl)
+{
+       return xchg(clp, cl);
+}
+
 
 /* 
    Timer resolution MUST BE < 10% of min_schedulable_packet_size/bandwidth
@@ -343,12 +386,14 @@ struct tcf_police
        u32             toks;
        u32             ptoks;
        psched_time_t   t_c;
+       spinlock_t      lock;
        struct qdisc_rate_table *R_tab;
        struct qdisc_rate_table *P_tab;
 
        struct tc_stats stats;
 };
 
+extern int qdisc_copy_stats(struct sk_buff *skb, struct tc_stats *st);
 extern void tcf_police_destroy(struct tcf_police *p);
 extern struct tcf_police * tcf_police_locate(struct rtattr *rta, struct rtattr *est);
 extern int tcf_police_dump(struct sk_buff *skb, struct tcf_police *p);
@@ -384,20 +429,56 @@ int teql_init(void);
 int tc_filter_init(void);
 int pktsched_init(void);
 
-void qdisc_run_queues(void);
-int qdisc_restart(struct device *dev);
+extern void qdisc_run_queues(void);
+extern int qdisc_restart(struct device *dev);
+
+extern spinlock_t qdisc_runqueue_lock;
+
+/* Is it on run list? Reliable only under qdisc_runqueue_lock. */
+
+extern __inline__ int qdisc_on_runqueue(struct Qdisc *q)
+{
+       return q->h.forw != NULL;
+}
+
+/* Is run list not empty? Reliable only under qdisc_runqueue_lock. */
+
+extern __inline__ int qdisc_pending(void)
+{
+       return qdisc_head.forw != &qdisc_head;
+}
+
+/* Add qdisc to tail of run list. Called with BH, disabled on this CPU */
+
+extern __inline__ void qdisc_run(struct Qdisc *q)
+{
+       spin_lock(&qdisc_runqueue_lock);
+       if (!qdisc_on_runqueue(q)) {
+               q->h.forw = &qdisc_head;
+               q->h.back = qdisc_head.back;
+               qdisc_head.back->forw = &q->h;
+               qdisc_head.back = &q->h;
+       }
+       spin_unlock(&qdisc_runqueue_lock);
+}
+
+/* If the device is not throttled, restart it and add to run list.
+ * BH must be disabled on this CPU.
+ */
 
 extern __inline__ void qdisc_wakeup(struct device *dev)
 {
        if (!dev->tbusy) {
-               struct Qdisc *q = dev->qdisc;
-               if (qdisc_restart(dev) && q->h.forw == NULL) {
-                       q->h.forw = qdisc_head.forw;
-                       qdisc_head.forw = &q->h;
-               }
+               spin_lock(&dev->queue_lock);
+               if (qdisc_restart(dev))
+                       qdisc_run(dev->qdisc);
+               spin_unlock(&dev->queue_lock);
        }
 }
 
+/* Calculate maximal size of packet seen by hard_start_xmit
+   routine of this device.
+ */
 extern __inline__ unsigned psched_mtu(struct device *dev)
 {
        unsigned mtu = dev->mtu;
index 643898ced9b33653e69d615bfb0beda19f808bd9..e230f45fcc1312000de6ba52a9b2ccbd7838e881 100644 (file)
 
 #define RT_HASH_DIVISOR                256
 
-/*
- * Prevents LRU trashing, entries considered equivalent,
- * if the difference between last use times is less then this number.
- */
-#define RT_CACHE_BUBBLE_THRESHOLD      (5*HZ)
-
-
 #define RTO_ONLINK     0x01
 #define RTO_TPROXY     0x80000000
 
@@ -103,6 +96,7 @@ struct ip_rt_acct
 };
 
 extern struct ip_rt_acct ip_rt_acct[256];
+extern rwlock_t ip_rt_acct_lock;
 
 extern void            ip_rt_init(void);
 extern void            ip_rt_redirect(u32 old_gw, u32 dst, u32 new_gw,
index 954e1407ee549635e66eb8260fd2fcc6417cd37e..a9f00e229fddcaff65807dafd3aa428ccc48ebfa 100644 (file)
@@ -14,4 +14,6 @@
 #define SCSICAM_H
 #include <linux/kdev_t.h>
 extern int scsicam_bios_param (Disk *disk, kdev_t dev, int *ip);
+extern int scsi_partsize(struct buffer_head *bh, unsigned long capacity,
+           unsigned int  *cyls, unsigned int *hds, unsigned int *secs);
 #endif /* def SCSICAM_H */
index ca1fdea5f5279852afa6c14b42fbce8831c35244..b9bd18343cc7636660dac24ae323d49a18c83012 100644 (file)
@@ -263,13 +263,13 @@ struct device *dev_get(const char *name)
 {
        struct device *dev;
 
-       read_lock_bh(&dev_base_lock);
+       read_lock(&dev_base_lock);
        for (dev = dev_base; dev != NULL; dev = dev->next) {
                if (strcmp(dev->name, name) == 0)
                        goto out;
        }
 out:
-       read_unlock_bh(&dev_base_lock);
+       read_unlock(&dev_base_lock);
        return dev;
 }
 
@@ -277,13 +277,13 @@ struct device * dev_get_by_index(int ifindex)
 {
        struct device *dev;
 
-       read_lock_bh(&dev_base_lock);
+       read_lock(&dev_base_lock);
        for (dev = dev_base; dev != NULL; dev = dev->next) {
                if (dev->ifindex == ifindex)
                        goto out;
        }
 out:
-       read_unlock_bh(&dev_base_lock);
+       read_unlock(&dev_base_lock);
        return dev;
 }
 
@@ -291,14 +291,14 @@ struct device *dev_getbyhwaddr(unsigned short type, char *ha)
 {
        struct device *dev;
 
-       read_lock_bh(&dev_base_lock);
+       read_lock(&dev_base_lock);
        for (dev = dev_base; dev != NULL; dev = dev->next) {
                if (dev->type == type &&
                    memcmp(dev->dev_addr, ha, dev->addr_len) == 0)
                        goto out;
        }
 out:
-       read_unlock_bh(&dev_base_lock);
+       read_unlock(&dev_base_lock);
        return dev;
 }
 
@@ -321,7 +321,7 @@ int dev_alloc_name(struct device *dev, const char *name)
        }
        return -ENFILE; /* Over 100 of the things .. bail out! */
 }
+
 struct device *dev_alloc(const char *name, int *err)
 {
        struct device *dev=kmalloc(sizeof(struct device)+16, GFP_KERNEL);
@@ -387,9 +387,6 @@ int dev_open(struct device *dev)
        if (dev->flags&IFF_UP)
                return 0;
 
-       /* Setup the lock before we open the faucet. */
-       spin_lock_init(&dev->xmit_lock);
-
        /*
         *      Call device private open method
         */
@@ -452,10 +449,10 @@ void dev_clear_fastroute(struct device *dev)
        if (dev) {
                dev_do_clear_fastroute(dev);
        } else {
-               read_lock_bh(&dev_base_lock);
+               read_lock(&dev_base_lock);
                for (dev = dev_base; dev; dev = dev->next)
                        dev_do_clear_fastroute(dev);
-               read_unlock_bh(&dev_base_lock);
+               read_unlock(&dev_base_lock);
        }
 }
 #endif
@@ -596,59 +593,61 @@ int dev_queue_xmit(struct sk_buff *skb)
        struct device *dev = skb->dev;
        struct Qdisc  *q;
 
-#ifdef CONFIG_NET_PROFILE
-       start_bh_atomic();
-       NET_PROFILE_ENTER(dev_queue_xmit);
-#endif
-
-       spin_lock_bh(&dev->xmit_lock);
+       /* Grab device queue */
+       spin_lock_bh(&dev->queue_lock);
        q = dev->qdisc;
        if (q->enqueue) {
                q->enqueue(skb, q);
-               qdisc_wakeup(dev);
-               spin_unlock_bh(&dev->xmit_lock);
 
-#ifdef CONFIG_NET_PROFILE
-               NET_PROFILE_LEAVE(dev_queue_xmit);
-               end_bh_atomic();
-#endif
+               /* If the device is not busy, kick it.
+                * Otherwise or if queue is not empty after kick,
+                * add it to run list.
+                */
+               if (dev->tbusy || qdisc_restart(dev))
+                       qdisc_run(dev->qdisc);
 
+               spin_unlock_bh(&dev->queue_lock);
                return 0;
        }
+       spin_unlock_bh(&dev->queue_lock);
 
        /* The device has no queue. Common case for software devices:
           loopback, all the sorts of tunnels...
 
-          Really, it is unlikely that bh protection is necessary here:
-          virtual devices do not generate EOI events.
-          However, it is possible, that they rely on bh protection
+          Really, it is unlikely that xmit_lock protection is necessary here.
+          (f.e. loopback and IP tunnels are clean ignoring statistics counters.)
+          However, it is possible, that they rely on protection
           made by us here.
+
+          Check this and shot the lock. It is not prone from deadlocks.
+          Either shot noqueue qdisc, it is even simpler 8)
         */
        if (dev->flags&IFF_UP) {
                if (netdev_nit) 
                        dev_queue_xmit_nit(skb,dev);
-               if (dev->hard_start_xmit(skb, dev) == 0) {
-                       spin_unlock_bh(&dev->xmit_lock);
-
-#ifdef CONFIG_NET_PROFILE
-                       NET_PROFILE_LEAVE(dev_queue_xmit);
-                       end_bh_atomic();
-#endif
 
-                       return 0;
+               local_bh_disable();
+               if (dev->xmit_lock_owner != smp_processor_id()) {
+                       spin_lock(&dev->xmit_lock);
+                       dev->xmit_lock_owner = smp_processor_id();
+                       if (dev->hard_start_xmit(skb, dev) == 0) {
+                               dev->xmit_lock_owner = -1;
+                               spin_unlock_bh(&dev->xmit_lock);
+                               return 0;
+                       }
+                       dev->xmit_lock_owner = -1;
+                       spin_unlock_bh(&dev->xmit_lock);
+                       if (net_ratelimit())
+                               printk(KERN_DEBUG "Virtual device %s asks to queue packet!\n", dev->name);
+               } else {
+                       /* Recursion is detected! It is possible, unfortunately */
+                       local_bh_enable();
+                       if (net_ratelimit())
+                               printk(KERN_DEBUG "Dead loop on virtual device %s, fix it urgently!\n", dev->name);
                }
-               if (net_ratelimit())
-                       printk(KERN_DEBUG "Virtual device %s asks to queue packet!\n", dev->name);
        }
-       spin_unlock_bh(&dev->xmit_lock);
 
        kfree_skb(skb);
-
-#ifdef CONFIG_NET_PROFILE
-       NET_PROFILE_LEAVE(dev_queue_xmit);
-       end_bh_atomic();
-#endif
-
        return 0;
 }
 
@@ -660,9 +659,6 @@ int dev_queue_xmit(struct sk_buff *skb)
 int netdev_dropping = 0;
 int netdev_max_backlog = 300;
 atomic_t netdev_rx_dropped;
-#ifdef CONFIG_CPU_IS_SLOW
-int net_cpu_congestion;
-#endif
 
 #ifdef CONFIG_NET_HW_FLOWCONTROL
 int netdev_throttle_events;
@@ -852,14 +848,6 @@ void net_bh(void)
        struct packet_type *pt_prev;
        unsigned short type;
        unsigned long start_time = jiffies;
-#ifdef CONFIG_CPU_IS_SLOW
-       static unsigned long start_busy = 0;
-       static unsigned long ave_busy = 0;
-
-       if (start_busy == 0)
-               start_busy = start_time;
-       net_cpu_congestion = ave_busy>>8;
-#endif
 
        NET_PROFILE_ENTER(net_bh);
        /*
@@ -869,9 +857,9 @@ void net_bh(void)
         *      latency on a transmit interrupt bh.
         */
 
-       if (qdisc_head.forw != &qdisc_head)
+       if (qdisc_pending())
                qdisc_run_queues();
-  
+
        /*
         *      Any data left to process. This may occur because a
         *      mark_bh() is done after we empty the queue including
@@ -899,19 +887,6 @@ void net_bh(void)
                 */
                skb = skb_dequeue(&backlog);
 
-#ifdef CONFIG_CPU_IS_SLOW
-               if (ave_busy > 128*16) {
-                       kfree_skb(skb);
-                       while ((skb = skb_dequeue(&backlog)) != NULL)
-                               kfree_skb(skb);
-                       break;
-               }
-#endif
-
-
-#if 0
-               NET_PROFILE_SKB_PASSED(skb, net_bh_skb);
-#endif
 #ifdef CONFIG_NET_FASTROUTE
                if (skb->pkt_type == PACKET_FASTROUTE) {
                        dev_queue_xmit(skb);
@@ -1022,16 +997,9 @@ void net_bh(void)
         *      One last output flush.
         */
 
-       if (qdisc_head.forw != &qdisc_head)
+       if (qdisc_pending())
                qdisc_run_queues();
 
-#ifdef  CONFIG_CPU_IS_SLOW
-        if (1) {
-               unsigned long start_idle = jiffies;
-               ave_busy += ((start_idle - start_busy)<<3) - (ave_busy>>4);
-               start_busy = 0;
-       }
-#endif
 #ifdef CONFIG_NET_HW_FLOWCONTROL
        if (netdev_dropping)
                netdev_wakeup();
@@ -1065,14 +1033,6 @@ int register_gifconf(unsigned int family, gifconf_func_t * gifconf)
  */
 
 /*
- *     This call is useful, but I'd remove it too.
- *
- *     The reason is purely aestetical, it is the only call
- *     from SIOC* family using struct ifreq in reversed manner.
- *     Besides that, it is pretty silly to put "drawing" facility
- *     to kernel, it is useful only to print ifindices
- *     in readable form, is not it? --ANK
- *
  *     We need this ioctl for efficient implementation of the
  *     if_indextoname() function required by the IPv6 API.  Without
  *     it, we would have to search all the interfaces to find a
@@ -1138,7 +1098,7 @@ static int dev_ifconf(char *arg)
         */
 
        total = 0;
-       read_lock_bh(&dev_base_lock);
+       read_lock(&dev_base_lock);
        for (dev = dev_base; dev != NULL; dev = dev->next) {
                for (i=0; i<NPROTO; i++) {
                        if (gifconf_list[i]) {
@@ -1152,7 +1112,7 @@ static int dev_ifconf(char *arg)
                        }
                }
        }
-       read_unlock_bh(&dev_base_lock);
+       read_unlock(&dev_base_lock);
 
        if(pos != NULL) {
                int err = copy_to_user(ifc.ifc_buf, pos, total);
@@ -1232,7 +1192,7 @@ int dev_get_info(char *buffer, char **start, off_t offset, int length, int dummy
        len+=size;
        
 
-       read_lock_bh(&dev_base_lock);
+       read_lock(&dev_base_lock);
        for (dev = dev_base; dev != NULL; dev = dev->next) {
                size = sprintf_stats(buffer+len, dev);
                len+=size;
@@ -1245,7 +1205,7 @@ int dev_get_info(char *buffer, char **start, off_t offset, int length, int dummy
                if(pos>offset+length)
                        break;
        }
-       read_unlock_bh(&dev_base_lock);
+       read_unlock(&dev_base_lock);
        
        *start=buffer+(offset-begin);   /* Start of wanted data */
        len-=(offset-begin);            /* Start slop */
@@ -1347,7 +1307,7 @@ int dev_get_wireless_info(char * buffer, char **start, off_t offset,
        pos+=size;
        len+=size;
 
-       read_lock_bh(&dev_base_lock);
+       read_lock(&dev_base_lock);
        for(dev = dev_base; dev != NULL; dev = dev->next) {
                size = sprintf_wireless_stats(buffer+len, dev);
                len+=size;
@@ -1360,7 +1320,7 @@ int dev_get_wireless_info(char * buffer, char **start, off_t offset,
                if(pos > offset + length)
                        break;
        }
-       read_unlock_bh(&dev_base_lock);
+       read_unlock(&dev_base_lock);
 
        *start = buffer + (offset - begin);     /* Start of wanted data */
        len -= (offset - begin);                /* Start slop */
@@ -1736,11 +1696,10 @@ int dev_ioctl(unsigned int cmd, void *arg)
                                if (IW_IS_SET(cmd)) {
                                        if (!suser())
                                                return -EPERM;
-                                       rtnl_lock();
                                }
+                               rtnl_lock();
                                ret = dev_ifsioc(&ifr, cmd);
-                               if (IW_IS_SET(cmd))
-                                       rtnl_unlock();
+                               rtnl_unlock();
                                if (!ret && IW_IS_GET(cmd) &&
                                    copy_to_user(arg, &ifr, sizeof(struct ifreq)))
                                        return -EFAULT;
@@ -1769,6 +1728,10 @@ int register_netdevice(struct device *dev)
 {
        struct device *d, **dp;
 
+       spin_lock_init(&dev->queue_lock);
+       spin_lock_init(&dev->xmit_lock);
+       dev->xmit_lock_owner = -1;
+
        if (dev_boot_phase) {
                /* This is NOT bug, but I am not sure, that all the
                   devices, initialized before netdev module is started
@@ -1784,14 +1747,13 @@ int register_netdevice(struct device *dev)
                printk(KERN_INFO "early initialization of device %s is deferred\n", dev->name);
 
                /* Check for existence, and append to tail of chain */
-               write_lock_bh(&dev_base_lock);
                for (dp=&dev_base; (d=*dp) != NULL; dp=&d->next) {
                        if (d == dev || strcmp(d->name, dev->name) == 0) {
-                               write_unlock_bh(&dev_base_lock);
                                return -EEXIST;
                        }
                }
                dev->next = NULL;
+               write_lock_bh(&dev_base_lock);
                *dp = dev;
                write_unlock_bh(&dev_base_lock);
                return 0;
@@ -1803,24 +1765,22 @@ int register_netdevice(struct device *dev)
        if (dev->init && dev->init(dev) != 0)
                return -EIO;
 
+       dev->ifindex = dev_new_index();
+       if (dev->iflink == -1)
+               dev->iflink = dev->ifindex;
+
        /* Check for existence, and append to tail of chain */
-       write_lock_bh(&dev_base_lock);
        for (dp=&dev_base; (d=*dp) != NULL; dp=&d->next) {
                if (d == dev || strcmp(d->name, dev->name) == 0) {
-                       write_unlock_bh(&dev_base_lock);
                        return -EEXIST;
                }
        }
        dev->next = NULL;
        dev_init_scheduler(dev);
+       write_lock_bh(&dev_base_lock);
        *dp = dev;
        write_unlock_bh(&dev_base_lock);
 
-       dev->ifindex = -1;
-       dev->ifindex = dev_new_index();
-       if (dev->iflink == -1)
-               dev->iflink = dev->ifindex;
-
        /* Notify protocols, that a new device appeared. */
        notifier_call_chain(&netdev_chain, NETDEV_REGISTER, dev);
 
@@ -1831,15 +1791,35 @@ int unregister_netdevice(struct device *dev)
 {
        struct device *d, **dp;
 
-       if (dev_boot_phase == 0) {
-               /* If device is running, close it.
-                  It is very bad idea, really we should
-                  complain loudly here, but random hackery
-                  in linux/drivers/net likes it.
-                */
-               if (dev->flags & IFF_UP)
-                       dev_close(dev);
+       /* If device is running, close it first. */
+       if (dev->flags & IFF_UP)
+               dev_close(dev);
 
+       /* And unlink it from device chain. */
+       for (dp = &dev_base; (d=*dp) != NULL; dp=&d->next) {
+               if (d == dev) {
+                       write_lock_bh(&dev_base_lock);
+                       *dp = d->next;
+                       write_unlock_bh(&dev_base_lock);
+
+                       /* Sorry. It is known "feature". The race is clear.
+                          Keep it after device reference counting will
+                          be complete.
+                        */
+                       synchronize_bh();
+                       break;
+               }
+       }
+       if (d == NULL)
+               return -ENODEV;
+
+       /* It is "synchronize_bh" to those of guys, who overslept
+          in skb_alloc/page fault etc. that device is off-line.
+          Again, it can be removed only if devices are refcounted.
+        */
+       dev_lock_wait();
+
+       if (dev_boot_phase == 0) {
 #ifdef CONFIG_NET_FASTROUTE
                dev_clear_fastroute(dev);
 #endif
@@ -1856,27 +1836,11 @@ int unregister_netdevice(struct device *dev)
                 *      Flush the multicast chain
                 */
                dev_mc_discard(dev);
-
-               /* To avoid pointers looking to nowhere,
-                  we wait for end of critical section */
-               dev_lock_wait();
        }
 
-       /* And unlink it from device chain. */
-       write_lock_bh(&dev_base_lock);
-       for (dp = &dev_base; (d=*dp) != NULL; dp=&d->next) {
-               if (d == dev) {
-                       *dp = d->next;
-                       d->next = NULL;
-                       write_unlock_bh(&dev_base_lock);
-
-                       if (dev->destructor)
-                               dev->destructor(dev);
-                       return 0;
-               }
-       }
-       write_unlock_bh(&dev_base_lock);
-       return -ENODEV;
+       if (dev->destructor)
+               dev->destructor(dev);
+       return 0;
 }
 
 
@@ -2018,16 +1982,24 @@ __initfunc(int net_dev_init(void))
         *      If the call to dev->init fails, the dev is removed
         *      from the chain disconnecting the device until the
         *      next reboot.
+        *
+        *      NB At boot phase networking is dead. No locking is required.
+        *      But we still preserve dev_base_lock for sanity.
         */
 
        dp = &dev_base;
        while ((dev = *dp) != NULL) {
+               spin_lock_init(&dev->queue_lock);
+               spin_lock_init(&dev->xmit_lock);
+               dev->xmit_lock_owner = -1;
                dev->iflink = -1;
                if (dev->init && dev->init(dev)) {
                        /*
                         *      It failed to come up. Unhook it.
                         */
+                       write_lock_bh(&dev_base_lock);
                        *dp = dev->next;
+                       write_unlock_bh(&dev_base_lock);
                } else {
                        dp = &dev->next;
                        dev->ifindex = dev_new_index();
@@ -2055,6 +2027,7 @@ __initfunc(int net_dev_init(void))
 
        dev_boot_phase = 0;
 
+       dst_init();
        dev_mcast_init();
 
 #ifdef CONFIG_IP_PNP
index 3a27a7f786f7b41f078e55e0bd99cc70fbf56d7b..f7fcb1f8730179cda2c522e6078b0ec3537df997 100644 (file)
  *
  *     Device mc lists are changed by bh at least if IPv6 is enabled,
  *     so that it must be bh protected.
+ *
+ *     We protect all mc lists with global rw lock
+ *     and block accesses to device mc filters with dev->xmit_lock.
  */
+static rwlock_t dev_mc_lock = RW_LOCK_UNLOCKED;
 
 /*
  *     Update the multicast list into the physical NIC controller.
@@ -69,7 +73,7 @@ void dev_mc_upload(struct device *dev)
        /* Don't do anything till we up the interface
           [dev_open will call this function so the list will
            stay sane] */
-           
+
        if(!(dev->flags&IFF_UP))
                return;
 
@@ -80,11 +84,15 @@ void dev_mc_upload(struct device *dev)
        if(dev->set_multicast_list==NULL)
                return;
 
-       start_bh_atomic();
+       read_lock_bh(&dev_mc_lock);
+       spin_lock(&dev->xmit_lock);
+       dev->xmit_lock_owner = smp_processor_id();
        dev->set_multicast_list(dev);
-       end_bh_atomic();
+       dev->xmit_lock_owner = -1;
+       spin_unlock(&dev->xmit_lock);
+       read_unlock_bh(&dev_mc_lock);
 }
-  
+
 /*
  *     Delete a device level multicast
  */
@@ -94,7 +102,7 @@ int dev_mc_delete(struct device *dev, void *addr, int alen, int glbl)
        int err = 0;
        struct dev_mc_list *dmi, **dmip;
 
-       start_bh_atomic();
+       write_lock_bh(&dev_mc_lock);
        for (dmip=&dev->mc_list; (dmi=*dmip)!=NULL; dmip=&dmi->next) {
                /*
                 *      Find the entry we want to delete. The device could
@@ -120,14 +128,15 @@ int dev_mc_delete(struct device *dev, void *addr, int alen, int glbl)
                         *      We have altered the list, so the card
                         *      loaded filter is now wrong. Fix it
                         */
-                       end_bh_atomic();
+                       write_unlock_bh(&dev_mc_lock);
+
                        dev_mc_upload(dev);
                        return 0;
                }
        }
        err = -ENOENT;
 done:
-       end_bh_atomic();
+       write_unlock_bh(&dev_mc_lock);
        return err;
 }
 
@@ -140,9 +149,12 @@ int dev_mc_add(struct device *dev, void *addr, int alen, int glbl)
        int err = 0;
        struct dev_mc_list *dmi, *dmi1;
 
+       /* RED-PEN: does gfp_any() work now? It requires
+          true local_bh_disable rather than global.
+        */
        dmi1 = (struct dev_mc_list *)kmalloc(sizeof(*dmi), gfp_any());
 
-       start_bh_atomic();
+       write_lock_bh(&dev_mc_lock);
        for(dmi=dev->mc_list; dmi!=NULL; dmi=dmi->next) {
                if (memcmp(dmi->dmi_addr,addr,dmi->dmi_addrlen)==0 && dmi->dmi_addrlen==alen) {
                        if (glbl) {
@@ -156,8 +168,10 @@ int dev_mc_add(struct device *dev, void *addr, int alen, int glbl)
                }
        }
 
-       if ((dmi=dmi1)==NULL)
+       if ((dmi=dmi1)==NULL) {
+               write_unlock_bh(&dev_mc_lock);
                return -ENOMEM;
+       }
        memcpy(dmi->dmi_addr, addr, alen);
        dmi->dmi_addrlen=alen;
        dmi->next=dev->mc_list;
@@ -165,12 +179,12 @@ int dev_mc_add(struct device *dev, void *addr, int alen, int glbl)
        dmi->dmi_gusers=glbl ? 1 : 0;
        dev->mc_list=dmi;
        dev->mc_count++;
-       end_bh_atomic();
+       write_unlock_bh(&dev_mc_lock);
        dev_mc_upload(dev);
        return 0;
 
 done:
-       end_bh_atomic();
+       write_unlock_bh(&dev_mc_lock);
        if (dmi1)
                kfree(dmi1);
        return err;
@@ -182,7 +196,7 @@ done:
 
 void dev_mc_discard(struct device *dev)
 {
-       start_bh_atomic();
+       write_lock_bh(&dev_mc_lock);
        while (dev->mc_list!=NULL) {
                struct dev_mc_list *tmp=dev->mc_list;
                dev->mc_list=tmp->next;
@@ -191,7 +205,7 @@ void dev_mc_discard(struct device *dev)
                kfree_s(tmp,sizeof(*tmp));
        }
        dev->mc_count=0;
-       end_bh_atomic();
+       write_unlock_bh(&dev_mc_lock);
 }
 
 #ifdef CONFIG_PROC_FS
@@ -203,8 +217,9 @@ static int dev_mc_read_proc(char *buffer, char **start, off_t offset,
        int len=0;
        struct device *dev;
 
-       read_lock_bh(&dev_base_lock);
+       read_lock(&dev_base_lock);
        for (dev = dev_base; dev; dev = dev->next) {
+               read_lock_bh(&dev_mc_lock);
                for (m = dev->mc_list; m; m = m->next) {
                        int i;
 
@@ -221,14 +236,17 @@ static int dev_mc_read_proc(char *buffer, char **start, off_t offset,
                                len=0;
                                begin=pos;
                        }
-                       if (pos > offset+length)
+                       if (pos > offset+length) {
+                               read_unlock_bh(&dev_mc_lock);
                                goto done;
+                       }
                }
+               read_unlock_bh(&dev_mc_lock);
        }
        *eof = 1;
 
 done:
-       read_unlock_bh(&dev_base_lock);
+       read_unlock(&dev_base_lock);
        *start=buffer+(offset-begin);
        len-=(offset-begin);
        if(len>length)
index c1b488cdd1fb70b9bcb32d1328d03cf3f66233d9..f1695ca84451a0f83dbc3da3d7d2576777564f24 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/errno.h>
 #include <linux/netdevice.h>
 #include <linux/skbuff.h>
+#include <linux/init.h>
 
 #include <net/dst.h>
 
@@ -39,16 +40,16 @@ static void dst_run_gc(unsigned long);
 static struct timer_list dst_gc_timer =
        { NULL, NULL, DST_GC_MIN, 0L, dst_run_gc };
 
-#if RT_CACHE_DEBUG >= 2
-atomic_t hh_count;
-#endif
 
 static void dst_run_gc(unsigned long dummy)
 {
        int    delayed = 0;
        struct dst_entry * dst, **dstp;
 
-       spin_lock(&dst_lock);
+       if (!spin_trylock(&dst_lock)) {
+               mod_timer(&dst_gc_timer, jiffies + HZ/10);
+               return;
+       }
 
        del_timer(&dst_gc_timer);
        dstp = &dst_garbage_list;
@@ -160,3 +161,36 @@ void dst_destroy(struct dst_entry * dst)
        atomic_dec(&dst_total);
        kfree(dst);
 }
+
+static int dst_dev_event(struct notifier_block *this, unsigned long event, void *ptr)
+{
+       struct device *dev = ptr;
+       struct dst_entry *dst;
+
+       switch (event) {
+       case NETDEV_UNREGISTER:
+       case NETDEV_DOWN:
+               spin_lock_bh(&dst_lock);
+               for (dst = dst_garbage_list; dst; dst = dst->next) {
+                       if (dst->dev == dev) {
+                               dst->input = dst_discard;
+                               dst->output = dst_blackhole;
+                               dst->dev = &loopback_dev;
+                       }
+               }
+               spin_unlock_bh(&dst_lock);
+               break;
+       }
+       return NOTIFY_DONE;
+}
+
+struct notifier_block dst_dev_notifier = {
+       dst_dev_event,
+       NULL,
+       0
+};
+
+__initfunc(void dst_init(void))
+{
+       register_netdevice_notifier(&dst_dev_notifier);
+}
index 781791d08c05979dfa0f4662ef6d8293af7e3a5e..6124fcfc3fbf99a22edfc42c79b93f766b4db9bd 100644 (file)
  *
  *     Fixes:
  *     Vitaly E. Lavrov        releasing NULL neighbor in neigh_add.
- *      Horst von Brand        Add #include <linux/string.h>
  */
 
 #include <linux/config.h>
 #include <linux/types.h>
-#include <linux/string.h>
 #include <linux/kernel.h>
 #include <linux/socket.h>
 #include <linux/sched.h>
 #include <net/sock.h>
 #include <linux/rtnetlink.h>
 
-/*
-   NOTE. The most unpleasent question is serialization of
-   accesses to resolved addresses. The problem is that addresses
-   are modified by bh, but they are referenced from normal
-   kernel thread. Before today no locking was made.
-   My reasoning was that corrupted address token will be copied
-   to packet with cosmologically small probability
-   (it is even difficult to estimate such small number)
-   and it is very silly to waste cycles in fast path to lock them.
-
-   But now I changed my mind, but not because previous statement
-   is wrong. Actually, neigh->ha MAY BE not opaque byte array,
-   but reference to some private data. In this case even neglibible
-   corruption probability becomes bug.
-
-   - hh cache is protected by rwlock. It assumes that
-     hh cache update procedure is short and fast, and that
-     read_lock is cheaper than start_bh_atomic().
-   - ha tokens, saved in neighbour entries, are protected
-     by bh_atomic().
-   - no protection is made in /proc reading. It is OK, because
-     /proc is broken by design in any case, and
-     corrupted output is normal behaviour there.
-
-     --ANK (981025)
- */
-
 #define NEIGH_DEBUG 1
 
 #define NEIGH_PRINTK(x...) printk(x)
@@ -83,6 +54,46 @@ static int pneigh_ifdown(struct neigh_table *tbl, struct device *dev);
 static int neigh_glbl_allocs;
 static struct neigh_table *neigh_tables;
 
+#if defined(__i386__) && defined(__SMP__)
+#define ASSERT_WL(n) if ((int)((n)->lock.lock) >= 0) { printk("WL assertion failed at " __FILE__ "(%d):" __FUNCTION__ "\n", __LINE__); }
+#else
+#define ASSERT_WL(n) do { } while(0)
+#endif
+
+/*
+   Neighbour hash table buckets are protected with rwlock tbl->lock.
+
+   - All the scans/updates to hash buckets MUST be made under this lock.
+   - NOTHING clever should be made under this lock: no callbacks
+     to protocol backends, no attempts to send something to network.
+     It will result in deadlocks, if backend/driver wants to use neighbour
+     cache.
+   - If the entry requires some non-trivial actions, increase
+     its reference count and release table lock.
+   Neighbour entries are protected:
+   - with reference count.
+   - with rwlock neigh->lock
+
+   Reference count prevents destruction.
+
+   neigh->lock mainly serializes ll address data and its validity state.
+   However, the same lock is used to protect another entry fields:
+    - timer
+    - resolution queue
+
+   Again, nothing clever shall be made under neigh->lock,
+   the most complicated procedure, which we allow is dev->hard_header.
+   It is supposed, that dev->hard_header is simplistic and does
+   not make callbacks to neighbour tables.
+
+   The last lock is neigh_tbl_lock. It is pure SMP lock, protecting
+   list of neighbour tables. This list is used only in process context,
+   so that this lock is useless with big kernel lock.
+ */
+
+static rwlock_t neigh_tbl_lock = RW_LOCK_UNLOCKED;
+
 static int neigh_blackhole(struct sk_buff *skb)
 {
        kfree_skb(skb);
@@ -106,13 +117,11 @@ static int neigh_forced_gc(struct neigh_table *tbl)
        int shrunk = 0;
        int i;
 
-       if (atomic_read(&tbl->lock))
-               return 0;
-
        for (i=0; i<=NEIGH_HASHMASK; i++) {
                struct neighbour *n, **np;
 
                np = &tbl->hash_buckets[i];
+               write_lock_bh(&tbl->lock);
                while ((n = *np) != NULL) {
                        /* Neighbour record may be discarded if:
                           - nobody refers to it.
@@ -124,6 +133,7 @@ static int neigh_forced_gc(struct neigh_table *tbl)
                             It is not clear, what is better table overflow
                             or flooding.
                         */
+                       write_lock(&n->lock);
                        if (atomic_read(&n->refcnt) == 0 &&
                            !(n->nud_state&NUD_PERMANENT) &&
                            (n->nud_state != NUD_INCOMPLETE ||
@@ -132,11 +142,14 @@ static int neigh_forced_gc(struct neigh_table *tbl)
                                n->tbl = NULL;
                                tbl->entries--;
                                shrunk = 1;
+                               write_unlock(&n->lock);
                                neigh_destroy(n);
                                continue;
                        }
+                       write_unlock(&n->lock);
                        np = &n->next;
                }
+               write_unlock_bh(&tbl->lock);
        }
        
        tbl->last_flush = jiffies;
@@ -147,12 +160,8 @@ int neigh_ifdown(struct neigh_table *tbl, struct device *dev)
 {
        int i;
 
-       if (atomic_read(&tbl->lock)) {
-               NEIGH_PRINTK1("neigh_ifdown: impossible event 1763\n");
-               return -EBUSY;
-       }
+       write_lock_bh(&tbl->lock);
 
-       start_bh_atomic();
        for (i=0; i<=NEIGH_HASHMASK; i++) {
                struct neighbour *n, **np;
 
@@ -163,6 +172,7 @@ int neigh_ifdown(struct neigh_table *tbl, struct device *dev)
                                continue;
                        }
                        *np = n->next;
+                       write_lock(&n->lock);
                        n->tbl = NULL;
                        tbl->entries--;
                        if (atomic_read(&n->refcnt)) {
@@ -185,33 +195,32 @@ int neigh_ifdown(struct neigh_table *tbl, struct device *dev)
                                else
                                        n->nud_state = NUD_NONE;
                                NEIGH_PRINTK2("neigh %p is stray.\n", n);
-                       } else
+                               write_unlock(&n->lock);
+                       } else {
+                               write_unlock(&n->lock);
                                neigh_destroy(n);
+                       }
                }
        }
 
        del_timer(&tbl->proxy_timer);
        skb_queue_purge(&tbl->proxy_queue);
        pneigh_ifdown(tbl, dev);
-       end_bh_atomic();
+       write_unlock_bh(&tbl->lock);
        return 0;
 }
 
-static struct neighbour *neigh_alloc(struct neigh_table *tbl, int creat)
+static struct neighbour *neigh_alloc(struct neigh_table *tbl)
 {
        struct neighbour *n;
        unsigned long now = jiffies;
 
-       if (tbl->entries > tbl->gc_thresh1) {
-               if (creat < 0)
+       if (tbl->entries > tbl->gc_thresh3 ||
+           (tbl->entries > tbl->gc_thresh2 &&
+            now - tbl->last_flush > 5*HZ)) {
+               if (neigh_forced_gc(tbl) == 0 &&
+                   tbl->entries > tbl->gc_thresh3)
                        return NULL;
-               if (tbl->entries > tbl->gc_thresh3 ||
-                   (tbl->entries > tbl->gc_thresh2 &&
-                    now - tbl->last_flush > 5*HZ)) {
-                       if (neigh_forced_gc(tbl) == 0 &&
-                           tbl->entries > tbl->gc_thresh3)
-                               return NULL;
-               }
        }
 
        n = kmalloc(tbl->entry_size, GFP_ATOMIC);
@@ -221,6 +230,7 @@ static struct neighbour *neigh_alloc(struct neigh_table *tbl, int creat)
        memset(n, 0, tbl->entry_size);
 
        skb_queue_head_init(&n->arp_queue);
+       n->lock = RW_LOCK_UNLOCKED;
        n->updated = n->used = now;
        n->nud_state = NUD_NONE;
        n->output = neigh_blackhole;
@@ -233,9 +243,8 @@ static struct neighbour *neigh_alloc(struct neigh_table *tbl, int creat)
        return n;
 }
 
-
-struct neighbour * __neigh_lookup(struct neigh_table *tbl, const void *pkey,
-                                   struct device *dev, int creat)
+struct neighbour *neigh_lookup(struct neigh_table *tbl, const void *pkey,
+                              struct device *dev)
 {
        struct neighbour *n;
        u32 hash_val;
@@ -247,17 +256,26 @@ struct neighbour * __neigh_lookup(struct neigh_table *tbl, const void *pkey,
        hash_val ^= hash_val>>3;
        hash_val = (hash_val^dev->ifindex)&NEIGH_HASHMASK;
 
+       read_lock_bh(&tbl->lock);
        for (n = tbl->hash_buckets[hash_val]; n; n = n->next) {
                if (dev == n->dev &&
                    memcmp(n->primary_key, pkey, key_len) == 0) {
                        atomic_inc(&n->refcnt);
-                       return n;
+                       break;
                }
        }
-       if (!creat)
-               return NULL;
+       read_unlock_bh(&tbl->lock);
+       return n;
+}
+
+struct neighbour * neigh_create(struct neigh_table *tbl, const void *pkey,
+                               struct device *dev)
+{
+       struct neighbour *n, *n1;
+       u32 hash_val;
+       int key_len = tbl->key_len;
 
-       n = neigh_alloc(tbl, creat);
+       n = neigh_alloc(tbl);
        if (n == NULL)
                return NULL;
 
@@ -277,11 +295,30 @@ struct neighbour * __neigh_lookup(struct neigh_table *tbl, const void *pkey,
        }
 
        n->confirmed = jiffies - (n->parms->base_reachable_time<<1);
-       atomic_set(&n->refcnt, 1);
+
+       hash_val = *(u32*)(pkey + key_len - 4);
+       hash_val ^= (hash_val>>16);
+       hash_val ^= hash_val>>8;
+       hash_val ^= hash_val>>3;
+       hash_val = (hash_val^dev->ifindex)&NEIGH_HASHMASK;
+
+       write_lock_bh(&tbl->lock);
+       for (n1 = tbl->hash_buckets[hash_val]; n1; n1 = n1->next) {
+               if (dev == n1->dev &&
+                   memcmp(n1->primary_key, pkey, key_len) == 0) {
+                       atomic_inc(&n1->refcnt);
+                       write_unlock_bh(&tbl->lock);
+                       neigh_destroy(n);
+                       return n1;
+               }
+       }
+
        tbl->entries++;
+       n->tbl = tbl;
+       atomic_set(&n->refcnt, 1);
        n->next = tbl->hash_buckets[hash_val];
        tbl->hash_buckets[hash_val] = n;
-       n->tbl = tbl;
+       write_unlock_bh(&tbl->lock);
        NEIGH_PRINTK2("neigh %p is created.\n", n);
        return n;
 }
@@ -393,7 +430,9 @@ void neigh_destroy(struct neighbour *neigh)
        while ((hh = neigh->hh) != NULL) {
                neigh->hh = hh->hh_next;
                hh->hh_next = NULL;
+               write_lock_bh(&hh->hh_lock);
                hh->hh_output = neigh_blackhole;
+               write_unlock_bh(&hh->hh_lock);
                if (atomic_dec_and_test(&hh->hh_refcnt))
                        kfree(hh);
        }
@@ -411,6 +450,8 @@ void neigh_destroy(struct neighbour *neigh)
 
 /* Neighbour state is suspicious;
    disable fast path.
+
+   Called with write_locked neigh.
  */
 static void neigh_suspect(struct neighbour *neigh)
 {
@@ -418,6 +459,8 @@ static void neigh_suspect(struct neighbour *neigh)
 
        NEIGH_PRINTK2("neigh %p is suspecteded.\n", neigh);
 
+       ASSERT_WL(neigh);
+
        neigh->output = neigh->ops->output;
 
        for (hh = neigh->hh; hh; hh = hh->hh_next)
@@ -426,6 +469,8 @@ static void neigh_suspect(struct neighbour *neigh)
 
 /* Neighbour state is OK;
    enable fast path.
+
+   Called with write_locked neigh.
  */
 static void neigh_connect(struct neighbour *neigh)
 {
@@ -433,6 +478,8 @@ static void neigh_connect(struct neighbour *neigh)
 
        NEIGH_PRINTK2("neigh %p is connected.\n", neigh);
 
+       ASSERT_WL(neigh);
+
        neigh->output = neigh->ops->connected_output;
 
        for (hh = neigh->hh; hh; hh = hh->hh_next)
@@ -448,6 +495,8 @@ static void neigh_connect(struct neighbour *neigh)
 
    If a routine wants to know TRUE entry state, it calls
    neigh_sync before checking state.
+
+   Called with write_locked neigh.
  */
 
 static void neigh_sync(struct neighbour *n)
@@ -455,6 +504,7 @@ static void neigh_sync(struct neighbour *n)
        unsigned long now = jiffies;
        u8 state = n->nud_state;
 
+       ASSERT_WL(n);
        if (state&(NUD_NOARP|NUD_PERMANENT))
                return;
        if (state&NUD_REACHABLE) {
@@ -478,11 +528,8 @@ static void neigh_periodic_timer(unsigned long arg)
        unsigned long now = jiffies;
        int i;
 
-       if (atomic_read(&tbl->lock)) {
-               tbl->gc_timer.expires = now + 1*HZ;
-               add_timer(&tbl->gc_timer);
-               return;
-       }
+
+       write_lock(&tbl->lock);
 
        /*
         *      periodicly recompute ReachableTime from random function
@@ -500,10 +547,15 @@ static void neigh_periodic_timer(unsigned long arg)
 
                np = &tbl->hash_buckets[i];
                while ((n = *np) != NULL) {
-                       unsigned state = n->nud_state;
+                       unsigned state;
 
-                       if (state&(NUD_PERMANENT|NUD_IN_TIMER))
+                       write_lock(&n->lock);
+
+                       state = n->nud_state;
+                       if (state&(NUD_PERMANENT|NUD_IN_TIMER)) {
+                               write_unlock(&n->lock);
                                goto next_elt;
+                       }
 
                        if ((long)(n->used - n->confirmed) < 0)
                                n->used = n->confirmed;
@@ -514,6 +566,7 @@ static void neigh_periodic_timer(unsigned long arg)
                                n->tbl = NULL;
                                n->next = NULL;
                                tbl->entries--;
+                               write_unlock(&n->lock);
                                neigh_destroy(n);
                                continue;
                        }
@@ -523,6 +576,7 @@ static void neigh_periodic_timer(unsigned long arg)
                                n->nud_state = NUD_STALE;
                                neigh_suspect(n);
                        }
+                       write_unlock(&n->lock);
 
 next_elt:
                        np = &n->next;
@@ -531,6 +585,7 @@ next_elt:
 
        tbl->gc_timer.expires = now + tbl->gc_interval;
        add_timer(&tbl->gc_timer);
+       write_unlock(&tbl->lock);
 }
 
 static __inline__ int neigh_max_probes(struct neighbour *n)
@@ -546,11 +601,17 @@ static void neigh_timer_handler(unsigned long arg)
 {
        unsigned long now = jiffies;
        struct neighbour *neigh = (struct neighbour*)arg;
-       unsigned state = neigh->nud_state;
+       unsigned state;
+       int notify = 0;
+
+       write_lock(&neigh->lock);
+       atomic_inc(&neigh->refcnt);
+
+       state = neigh->nud_state;
 
        if (!(state&NUD_IN_TIMER)) {
                NEIGH_PRINTK1("neigh: timer & !nud_in_timer\n");
-               return;
+               goto out;
        }
 
        if ((state&NUD_VALID) &&
@@ -558,18 +619,19 @@ static void neigh_timer_handler(unsigned long arg)
                neigh->nud_state = NUD_REACHABLE;
                NEIGH_PRINTK2("neigh %p is still alive.\n", neigh);
                neigh_connect(neigh);
-               return;
+               goto out;
        }
        if (state == NUD_DELAY) {
                NEIGH_PRINTK2("neigh %p is probed.\n", neigh);
                neigh->nud_state = NUD_PROBE;
-               neigh->probes = 0;
+               atomic_set(&neigh->probes, 0);
        }
 
-       if (neigh->probes >= neigh_max_probes(neigh)) {
+       if (atomic_read(&neigh->probes) >= neigh_max_probes(neigh)) {
                struct sk_buff *skb;
 
                neigh->nud_state = NUD_FAILED;
+               notify = 1;
                neigh->tbl->stats.res_failed++;
                NEIGH_PRINTK2("neigh %p is failed.\n", neigh);
 
@@ -578,44 +640,60 @@ static void neigh_timer_handler(unsigned long arg)
                   
                   So that, we try to be accurate and avoid dead loop. --ANK
                 */
-               while(neigh->nud_state==NUD_FAILED && (skb=__skb_dequeue(&neigh->arp_queue)) != NULL)
+               while(neigh->nud_state==NUD_FAILED && (skb=__skb_dequeue(&neigh->arp_queue)) != NULL) {
+                       write_unlock(&neigh->lock);
                        neigh->ops->error_report(neigh, skb);
+                       write_lock(&neigh->lock);
+               }
                skb_queue_purge(&neigh->arp_queue);
-               return;
+               goto out;
        }
 
        neigh->timer.expires = now + neigh->parms->retrans_time;
        add_timer(&neigh->timer);
+       write_unlock(&neigh->lock);
 
        neigh->ops->solicit(neigh, skb_peek(&neigh->arp_queue));
-       neigh->probes++;
+       atomic_inc(&neigh->probes);
+       neigh_release(neigh);
+       return;
+
+out:
+       write_unlock(&neigh->lock);
+#ifdef CONFIG_ARPD
+       if (notify && neigh->parms->app_probes)
+               neigh_app_notify(neigh);
+#endif
+       neigh_release(neigh);
 }
 
 int __neigh_event_send(struct neighbour *neigh, struct sk_buff *skb)
 {
-       start_bh_atomic();
+       write_lock_bh(&neigh->lock);
        if (!(neigh->nud_state&(NUD_CONNECTED|NUD_DELAY|NUD_PROBE))) {
                if (!(neigh->nud_state&(NUD_STALE|NUD_INCOMPLETE))) {
                        if (neigh->tbl == NULL) {
                                NEIGH_PRINTK2("neigh %p used after death.\n", neigh);
                                if (skb)
                                        kfree_skb(skb);
-                               end_bh_atomic();
+                               write_unlock_bh(&neigh->lock);
                                return 1;
                        }
                        if (neigh->parms->mcast_probes + neigh->parms->app_probes) {
-                               neigh->probes = neigh->parms->ucast_probes;
+                               atomic_set(&neigh->probes, neigh->parms->ucast_probes);
                                neigh->nud_state = NUD_INCOMPLETE;
                                neigh->timer.expires = jiffies + neigh->parms->retrans_time;
                                add_timer(&neigh->timer);
-
+                               write_unlock_bh(&neigh->lock);
                                neigh->ops->solicit(neigh, skb);
-                               neigh->probes++;
+                               atomic_inc(&neigh->probes);
+                               write_lock_bh(&neigh->lock);
                        } else {
                                neigh->nud_state = NUD_FAILED;
+                               write_unlock_bh(&neigh->lock);
+
                                if (skb)
                                        kfree_skb(skb);
-                               end_bh_atomic();
                                return 1;
                        }
                }
@@ -629,7 +707,7 @@ int __neigh_event_send(struct neighbour *neigh, struct sk_buff *skb)
                                }
                                __skb_queue_head(&neigh->arp_queue, skb);
                        }
-                       end_bh_atomic();
+                       write_unlock_bh(&neigh->lock);
                        return 1;
                }
                if (neigh->nud_state == NUD_STALE) {
@@ -639,7 +717,7 @@ int __neigh_event_send(struct neighbour *neigh, struct sk_buff *skb)
                        add_timer(&neigh->timer);
                }
        }
-       end_bh_atomic();
+       write_unlock_bh(&neigh->lock);
        return 0;
 }
 
@@ -651,9 +729,9 @@ static __inline__ void neigh_update_hhs(struct neighbour *neigh)
 
        if (update) {
                for (hh=neigh->hh; hh; hh=hh->hh_next) {
-                       write_lock_irq(&hh->hh_lock);
+                       write_lock_bh(&hh->hh_lock);
                        update(hh, neigh->dev, neigh->ha);
-                       write_unlock_irq(&hh->hh_lock);
+                       write_unlock_bh(&hh->hh_lock);
                }
        }
 }
@@ -665,15 +743,23 @@ static __inline__ void neigh_update_hhs(struct neighbour *neigh)
    -- new    is new state.
    -- override==1 allows to override existing lladdr, if it is different.
    -- arp==0 means that the change is administrative.
+
+   Caller MUST hold reference count on the entry.
  */
 
 int neigh_update(struct neighbour *neigh, u8 *lladdr, u8 new, int override, int arp)
 {
-       u8 old = neigh->nud_state;
+       u8 old;
+       int err;
+       int notify = 0;
        struct device *dev = neigh->dev;
 
+       write_lock_bh(&neigh->lock);
+       old = neigh->nud_state;
+
+       err = -EPERM;
        if (arp && (old&(NUD_NOARP|NUD_PERMANENT)))
-               return -EPERM;
+               goto out;
 
        if (!(new&NUD_VALID)) {
                if (old&NUD_IN_TIMER)
@@ -681,7 +767,9 @@ int neigh_update(struct neighbour *neigh, u8 *lladdr, u8 new, int override, int
                if (old&NUD_CONNECTED)
                        neigh_suspect(neigh);
                neigh->nud_state = new;
-               return 0;
+               err = 0;
+               notify = old&NUD_VALID;
+               goto out;
        }
 
        /* Compare new lladdr with cached one */
@@ -698,14 +786,15 @@ int neigh_update(struct neighbour *neigh, u8 *lladdr, u8 new, int override, int
                        if (memcmp(lladdr, neigh->ha, dev->addr_len) == 0)
                                lladdr = neigh->ha;
                        else if (!override)
-                               return -EPERM;
+                               goto out;
                }
        } else {
                /* No address is supplied; if we know something,
                   use it, otherwise discard the request.
                 */
+               err = -EINVAL;
                if (!(old&NUD_VALID))
-                       return -EINVAL;
+                       goto out;
                lladdr = neigh->ha;
        }
 
@@ -718,10 +807,11 @@ int neigh_update(struct neighbour *neigh, u8 *lladdr, u8 new, int override, int
        /* If entry was valid and address is not changed,
           do not change entry state, if new one is STALE.
         */
+       err = 0;
        if (old&NUD_VALID) {
                if (lladdr == neigh->ha)
                        if (new == old || (new == NUD_STALE && (old&NUD_CONNECTED)))
-                               return 0;
+                               goto out;
        }
        if (old&NUD_IN_TIMER)
                del_timer(&neigh->timer);
@@ -731,12 +821,11 @@ int neigh_update(struct neighbour *neigh, u8 *lladdr, u8 new, int override, int
                neigh_update_hhs(neigh);
                neigh->confirmed = jiffies - (neigh->parms->base_reachable_time<<1);
 #ifdef CONFIG_ARPD
-               if (neigh->parms->app_probes)
-                       neigh_app_notify(neigh);
+               notify = 1;
 #endif
        }
        if (new == old)
-               return 0;
+               goto out;
        if (new&NUD_CONNECTED)
                neigh_connect(neigh);
        else
@@ -749,14 +838,22 @@ int neigh_update(struct neighbour *neigh, u8 *lladdr, u8 new, int override, int
                while (neigh->nud_state&NUD_VALID &&
                       (skb=__skb_dequeue(&neigh->arp_queue)) != NULL) {
                        struct neighbour *n1 = neigh;
+                       write_unlock_bh(&neigh->lock);
                        /* On shaper/eql skb->dst->neighbour != neigh :( */
                        if (skb->dst && skb->dst->neighbour)
                                n1 = skb->dst->neighbour;
                        n1->output(skb);
+                       write_lock_bh(&neigh->lock);
                }
                skb_queue_purge(&neigh->arp_queue);
        }
-       return 0;
+out:
+       write_unlock_bh(&neigh->lock);
+#ifdef CONFIG_ARPD
+       if (notify && neigh->parms->app_probes)
+               neigh_app_notify(neigh);
+#endif
+       return err;
 }
 
 struct neighbour * neigh_event_ns(struct neigh_table *tbl,
@@ -839,15 +936,15 @@ int neigh_resolve_output(struct sk_buff *skb)
                int err;
                struct device *dev = neigh->dev;
                if (dev->hard_header_cache && dst->hh == NULL) {
-                       start_bh_atomic();
+                       write_lock_bh(&neigh->lock);
                        if (dst->hh == NULL)
                                neigh_hh_init(neigh, dst, dst->ops->protocol);
                        err = dev->hard_header(skb, dev, ntohs(skb->protocol), neigh->ha, NULL, skb->len);
-                       end_bh_atomic();
+                       write_unlock_bh(&neigh->lock);
                } else {
-                       start_bh_atomic();
+                       read_lock_bh(&neigh->lock);
                        err = dev->hard_header(skb, dev, ntohs(skb->protocol), neigh->ha, NULL, skb->len);
-                       end_bh_atomic();
+                       read_unlock_bh(&neigh->lock);
                }
                if (err >= 0)
                        return neigh->ops->queue_xmit(skb);
@@ -873,9 +970,9 @@ int neigh_connected_output(struct sk_buff *skb)
 
        __skb_pull(skb, skb->nh.raw - skb->data);
 
-       start_bh_atomic();
+       read_lock_bh(&neigh->lock);
        err = dev->hard_header(skb, dev, ntohs(skb->protocol), neigh->ha, NULL, skb->len);
-       end_bh_atomic();
+       read_unlock_bh(&neigh->lock);
        if (err >= 0)
                return neigh->ops->queue_xmit(skb);
        kfree_skb(skb);
@@ -949,8 +1046,10 @@ struct neigh_parms *neigh_parms_alloc(struct device *dev, struct neigh_table *tb
                                return NULL;
                        }
                }
+               write_lock_bh(&tbl->lock);
                p->next = tbl->parms.next;
                tbl->parms.next = p;
+               write_unlock_bh(&tbl->lock);
        }
        return p;
 }
@@ -961,10 +1060,11 @@ void neigh_parms_release(struct neigh_table *tbl, struct neigh_parms *parms)
        
        if (parms == NULL || parms == &tbl->parms)
                return;
+       write_lock_bh(&tbl->lock);
        for (p = &tbl->parms.next; *p; p = &(*p)->next) {
                if (*p == parms) {
                        *p = parms->next;
-                       synchronize_bh();
+                       write_unlock_bh(&tbl->lock);
 #ifdef CONFIG_SYSCTL
                        neigh_sysctl_unregister(parms);
 #endif
@@ -972,6 +1072,7 @@ void neigh_parms_release(struct neigh_table *tbl, struct neigh_parms *parms)
                        return;
                }
        }
+       write_unlock_bh(&tbl->lock);
        NEIGH_PRINTK1("neigh_release_parms: not found\n");
 }
 
@@ -983,6 +1084,7 @@ void neigh_table_init(struct neigh_table *tbl)
        tbl->parms.reachable_time = neigh_rand_reach_time(tbl->parms.base_reachable_time);
 
        init_timer(&tbl->gc_timer);
+       tbl->lock = RW_LOCK_UNLOCKED;
        tbl->gc_timer.data = (unsigned long)tbl;
        tbl->gc_timer.function = neigh_periodic_timer;
        tbl->gc_timer.expires = now + tbl->gc_interval + tbl->parms.reachable_time;
@@ -995,29 +1097,30 @@ void neigh_table_init(struct neigh_table *tbl)
 
        tbl->last_flush = now;
        tbl->last_rand = now + tbl->parms.reachable_time*20;
+       write_lock(&neigh_tbl_lock);
        tbl->next = neigh_tables;
        neigh_tables = tbl;
+       write_unlock(&neigh_tbl_lock);
 }
 
 int neigh_table_clear(struct neigh_table *tbl)
 {
        struct neigh_table **tp;
 
-       start_bh_atomic();
        del_timer(&tbl->gc_timer);
        del_timer(&tbl->proxy_timer);
        skb_queue_purge(&tbl->proxy_queue);
        neigh_ifdown(tbl, NULL);
-       end_bh_atomic();
        if (tbl->entries)
                printk(KERN_CRIT "neighbour leakage\n");
+       write_lock(&neigh_tbl_lock);
        for (tp = &neigh_tables; *tp; tp = &(*tp)->next) {
                if (*tp == tbl) {
                        *tp = tbl->next;
-                       synchronize_bh();
                        break;
                }
        }
+       write_unlock(&neigh_tbl_lock);
 #ifdef CONFIG_SYSCTL
        neigh_sysctl_unregister(&tbl->parms);
 #endif
@@ -1039,12 +1142,14 @@ int neigh_delete(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
                        return -ENODEV;
        }
 
+       read_lock(&neigh_tbl_lock);
        for (tbl=neigh_tables; tbl; tbl = tbl->next) {
                int err = 0;
                struct neighbour *n;
 
                if (tbl->family != ndm->ndm_family)
                        continue;
+               read_unlock(&neigh_tbl_lock);
 
                if (nda[NDA_DST-1] == NULL ||
                    nda[NDA_DST-1]->rta_len != RTA_LENGTH(tbl->key_len))
@@ -1056,15 +1161,14 @@ int neigh_delete(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
                if (dev == NULL)
                        return -EINVAL;
 
-               start_bh_atomic();
-               n = __neigh_lookup(tbl, RTA_DATA(nda[NDA_DST-1]), dev, 0);
+               n = neigh_lookup(tbl, RTA_DATA(nda[NDA_DST-1]), dev);
                if (n) {
                        err = neigh_update(n, NULL, NUD_FAILED, 1, 0);
                        neigh_release(n);
                }
-               end_bh_atomic();
                return err;
        }
+       read_unlock(&neigh_tbl_lock);
 
        return -EADDRNOTAVAIL;
 }
@@ -1081,12 +1185,15 @@ int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
                        return -ENODEV;
        }
 
+       read_lock(&neigh_tbl_lock);
        for (tbl=neigh_tables; tbl; tbl = tbl->next) {
                int err = 0;
                struct neighbour *n;
 
                if (tbl->family != ndm->ndm_family)
                        continue;
+               read_unlock(&neigh_tbl_lock);
+
                if (nda[NDA_DST-1] == NULL ||
                    nda[NDA_DST-1]->rta_len != RTA_LENGTH(tbl->key_len))
                        return -EINVAL;
@@ -1100,8 +1207,7 @@ int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
                if (nda[NDA_LLADDR-1] != NULL &&
                    nda[NDA_LLADDR-1]->rta_len != RTA_LENGTH(dev->addr_len))
                        return -EINVAL;
-               start_bh_atomic();
-               n = __neigh_lookup(tbl, RTA_DATA(nda[NDA_DST-1]), dev, 0);
+               n = neigh_lookup(tbl, RTA_DATA(nda[NDA_DST-1]), dev);
                if (n) {
                        if (nlh->nlmsg_flags&NLM_F_EXCL)
                                err = -EEXIST;
@@ -1119,9 +1225,9 @@ int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
                }
                if (n)
                        neigh_release(n);
-               end_bh_atomic();
                return err;
        }
+       read_unlock(&neigh_tbl_lock);
 
        return -EADDRNOTAVAIL;
 }
@@ -1141,15 +1247,17 @@ static int neigh_fill_info(struct sk_buff *skb, struct neighbour *n,
        ndm->ndm_family = n->ops->family;
        ndm->ndm_flags = n->flags;
        ndm->ndm_type = n->type;
-       ndm->ndm_state = n->nud_state;
        ndm->ndm_ifindex = n->dev->ifindex;
        RTA_PUT(skb, NDA_DST, n->tbl->key_len, n->primary_key);
+       read_lock_bh(&n->lock);
+       ndm->ndm_state = n->nud_state;
        if (n->nud_state&NUD_VALID)
                RTA_PUT(skb, NDA_LLADDR, n->dev->addr_len, n->ha);
        ci.ndm_used = now - n->used;
        ci.ndm_confirmed = now - n->confirmed;
        ci.ndm_updated = now - n->updated;
        ci.ndm_refcnt = atomic_read(&n->refcnt);
+       read_unlock_bh(&n->lock);
        RTA_PUT(skb, NDA_CACHEINFO, sizeof(ci), &ci);
        nlh->nlmsg_len = skb->tail - b;
        return skb->len;
@@ -1173,20 +1281,20 @@ static int neigh_dump_table(struct neigh_table *tbl, struct sk_buff *skb, struct
                if (h < s_h) continue;
                if (h > s_h)
                        s_idx = 0;
-               start_bh_atomic();
+               read_lock_bh(&tbl->lock);
                for (n = tbl->hash_buckets[h], idx = 0; n;
                     n = n->next, idx++) {
                        if (idx < s_idx)
                                continue;
                        if (neigh_fill_info(skb, n, NETLINK_CB(cb->skb).pid,
                                            cb->nlh->nlmsg_seq, RTM_NEWNEIGH) <= 0) {
-                               end_bh_atomic();
+                               read_unlock_bh(&tbl->lock);
                                cb->args[1] = h;
                                cb->args[2] = idx;
                                return -1;
                        }
                }
-               end_bh_atomic();
+               read_unlock_bh(&tbl->lock);
        }
 
        cb->args[1] = h;
@@ -1203,6 +1311,7 @@ int neigh_dump_info(struct sk_buff *skb, struct netlink_callback *cb)
 
        s_t = cb->args[0];
 
+       read_lock(&neigh_tbl_lock);
        for (tbl=neigh_tables, t=0; tbl; tbl = tbl->next, t++) {
                if (t < s_t) continue;
                if (family && tbl->family != family)
@@ -1212,6 +1321,7 @@ int neigh_dump_info(struct sk_buff *skb, struct netlink_callback *cb)
                if (neigh_dump_table(tbl, skb, cb) < 0) 
                        break;
        }
+       read_unlock(&neigh_tbl_lock);
 
        cb->args[0] = t;
 
index 187a484cb4630edb2969370db5fb759d8be2cdf9..dad9ee252961fd3d00c371c04d961193ad707f6e 100644 (file)
 #include <net/sock.h>
 #include <net/pkt_sched.h>
 
-atomic_t rtnl_rlockct;
-DECLARE_WAIT_QUEUE_HEAD(rtnl_wait);
+DECLARE_MUTEX(rtnl_sem);
 
-
-void rtnl_lock()
+void rtnl_lock(void)
 {
        rtnl_shlock();
        rtnl_exlock();
 }
-
-void rtnl_unlock()
+void rtnl_unlock(void)
 {
        rtnl_exunlock();
        rtnl_shunlock();
 }
 
+
+
 int rtattr_parse(struct rtattr *tb[], int maxattr, struct rtattr *rta, int len)
 {
        memset(tb, 0, sizeof(struct rtattr*)*maxattr);
@@ -82,8 +82,6 @@ int rtattr_parse(struct rtattr *tb[], int maxattr, struct rtattr *rta, int len)
 #ifdef CONFIG_RTNETLINK
 struct sock *rtnl;
 
-unsigned long rtnl_wlockct;
-
 struct rtnetlink_link * rtnetlink_links[NPROTO];
 
 #define _S     1       /* superuser privileges required */
@@ -189,14 +187,14 @@ int rtnetlink_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb)
        int s_idx = cb->args[0];
        struct device *dev;
 
-       read_lock_bh(&dev_base_lock);
+       read_lock(&dev_base_lock);
        for (dev=dev_base, idx=0; dev; dev = dev->next, idx++) {
                if (idx < s_idx)
                        continue;
                if (rtnetlink_fill_ifinfo(skb, dev, RTM_NEWLINK, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq) <= 0)
                        break;
        }
-       read_unlock_bh(&dev_base_lock);
+       read_unlock(&dev_base_lock);
        cb->args[0] = idx;
 
        return skb->len;
@@ -218,9 +216,7 @@ int rtnetlink_dump_all(struct sk_buff *skb, struct netlink_callback *cb)
                        continue;
                if (idx > s_idx)
                        memset(&cb->args[0], 0, sizeof(cb->args));
-               if (rtnetlink_links[idx][type].dumpit(skb, cb) == 0)
-                       continue;
-               if (skb_tailroom(skb) < 256)
+               if (rtnetlink_links[idx][type].dumpit(skb, cb))
                        break;
        }
        cb->family = idx;
@@ -247,8 +243,6 @@ void rtmsg_ifinfo(int type, struct device *dev)
 
 static int rtnetlink_done(struct netlink_callback *cb)
 {
-       if (cap_raised(NETLINK_CB(cb->skb).eff_cap, CAP_NET_ADMIN) && cb->nlh->nlmsg_flags&NLM_F_ATOMIC)
-               rtnl_shunlock();
        return 0;
 }
 
@@ -316,15 +310,9 @@ rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, int *errp)
                if (link->dumpit == NULL)
                        goto err_inval;
 
-               /* Super-user locks all the tables to get atomic snapshot */
-               if (cap_raised(NETLINK_CB(skb).eff_cap, CAP_NET_ADMIN)
-                   && nlh->nlmsg_flags&NLM_F_ATOMIC)
-                       atomic_inc(&rtnl_rlockct);
                if ((*errp = netlink_dump_start(rtnl, skb, nlh,
                                                link->dumpit,
                                                rtnetlink_done)) != 0) {
-                       if (cap_raised(NETLINK_CB(skb).eff_cap, CAP_NET_ADMIN) && nlh->nlmsg_flags&NLM_F_ATOMIC)
-                               atomic_dec(&rtnl_rlockct);
                        return -1;
                }
                rlen = NLMSG_ALIGN(nlh->nlmsg_len);
index a9194fa781a4ed847d940759ec38232059fa5575..3ca6535be7d8bc0ada7542a5a671a5df509535e8 100644 (file)
@@ -561,12 +561,13 @@ int dn_neigh_get_info(char *buffer, char **start, off_t offset, int length, int
 
        len += sprintf(buffer + len, "Addr    Flags State Use Blksize Dev\n");
 
-       neigh_table_lock(&dn_neigh_table);
        for(i=0;i <= NEIGH_HASHMASK; i++) {
+               read_lock_bh(&dn_neigh_table.lock);
                n = dn_neigh_table.hash_buckets[i];
                for(; n != NULL; n = n->next) {
                        struct dn_neigh *dn = (struct dn_neigh *)n;
 
+                       read_lock(&n->lock);
                        len += sprintf(buffer+len, "%-7s %s%s%s   %02x    %02d  %07ld %-8s\n",
                                        dn_addr2asc(dn_ntohs(dn_eth2dn(dn->addr)), buf),
                                        (dn->flags&DN_NDFLAG_R1) ? "1" : "-",
@@ -576,6 +577,7 @@ int dn_neigh_get_info(char *buffer, char **start, off_t offset, int length, int
                                        atomic_read(&dn->n.refcnt),
                                        dn->blksize,
                                        (dn->n.dev) ? dn->n.dev->name : "?");
+                       read_unlock(&n->lock);
 
                        pos = begin + len;
 
@@ -584,11 +586,15 @@ int dn_neigh_get_info(char *buffer, char **start, off_t offset, int length, int
                                begin = pos;
                        }
 
-                       if (pos > offset + length)
-                                       break;
+                       if (pos > offset + length) {
+                               read_unlock_bh(&dn_neigh_table.lock);
+                                       goto done;
+                       }
                }
+               read_unlock_bh(&dn_neigh_table.lock);
        }
-       neigh_table_unlock(&dn_neigh_table);
+
+done:
 
         *start = buffer + (offset - begin);
         len   -= offset - begin;
index 1cee2791f4aa54eff76d0bb0c2377e923db65e27..128c2a5e959bb0d97867a08b55a49d788467b575 100644 (file)
@@ -248,6 +248,7 @@ int eth_header_cache(struct neighbour *neigh, struct hh_cache *hh)
        eth->h_proto = type;
        memcpy(eth->h_source, dev->dev_addr, dev->addr_len);
        memcpy(eth->h_dest, neigh->ha, dev->addr_len);
+       hh->hh_len = ETH_HLEN;
        return 0;
 }
 
index 6ca8acba2cb3654586b002379eebd08c5570af73..ca0f27d0cb3109b8d1df41e0b2124ece6e01c02c 100644 (file)
@@ -5,7 +5,7 @@
  *
  *             PF_INET protocol family socket handler.
  *
- * Version:    $Id: af_inet.c,v 1.90 1999/05/29 04:30:38 davem Exp $
+ * Version:    $Id: af_inet.c,v 1.91 1999/06/09 08:28:55 davem Exp $
  *
  * Authors:    Ross Biro, <bir7@leland.Stanford.Edu>
  *             Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
index 2c311f2334d7ccd92ece8ae4831dbfcb5676e9ae..a3ca8870108afc0a365ab8979f7df24edb394781 100644 (file)
@@ -1,6 +1,6 @@
 /* linux/net/inet/arp.c
  *
- * Version:    $Id: arp.c,v 1.77 1999/03/21 05:22:30 davem Exp $
+ * Version:    $Id: arp.c,v 1.78 1999/06/09 10:10:36 davem Exp $
  *
  * Copyright (C) 1994 by Florian  La Roche
  *
 #include <asm/system.h>
 #include <asm/uaccess.h>
 
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
+static char *ax2asc2(ax25_address *a, char *buf);
+#endif
+
+
 /*
  *     Interface to generic neighbour cache.
  */
@@ -304,7 +309,7 @@ static void arp_solicit(struct neighbour *neigh, struct sk_buff *skb)
        u8  *dst_ha = NULL;
        struct device *dev = neigh->dev;
        u32 target = *(u32*)neigh->primary_key;
-       int probes = neigh->probes;
+       int probes = atomic_read(&neigh->probes);
 
        if (skb && inet_addr_type(skb->nh.iph->saddr) == RTN_LOCAL)
                saddr = skb->nh.iph->saddr;
@@ -315,6 +320,7 @@ static void arp_solicit(struct neighbour *neigh, struct sk_buff *skb)
                if (!(neigh->nud_state&NUD_VALID))
                        printk(KERN_DEBUG "trying to ucast probe in NUD_INVALID\n");
                dst_ha = neigh->ha;
+               read_lock_bh(&neigh->lock);
        } else if ((probes -= neigh->parms->app_probes) < 0) {
 #ifdef CONFIG_ARPD
                neigh_app_ns(neigh);
@@ -324,6 +330,8 @@ static void arp_solicit(struct neighbour *neigh, struct sk_buff *skb)
 
        arp_send(ARPOP_REQUEST, ETH_P_ARP, target, dev, saddr,
                 dst_ha, dev->dev_addr, NULL);
+       if (dst_ha)
+               read_unlock_bh(&neigh->lock);
 }
 
 /* OBSOLETE FUNCTIONS */
@@ -372,29 +380,25 @@ int arp_find(unsigned char *haddr, struct sk_buff *skb)
        if (arp_set_predefined(inet_addr_type(paddr), haddr, paddr, dev))
                return 0;
 
-       start_bh_atomic();
        n = __neigh_lookup(&arp_tbl, &paddr, dev, 1);
 
        if (n) {
                n->used = jiffies;
                if (n->nud_state&NUD_VALID || neigh_event_send(n, skb) == 0) {
-                       memcpy(haddr, n->ha, dev->addr_len);
+                       read_lock_bh(&n->lock);
+                       memcpy(haddr, n->ha, dev->addr_len);
+                       read_unlock_bh(&n->lock);
                        neigh_release(n);
-                       end_bh_atomic();
                        return 0;
                }
+               neigh_release(n);
        } else
                kfree_skb(skb);
-       neigh_release(n);
-       end_bh_atomic();
        return 1;
 }
 
 /* END OF OBSOLETE FUNCTIONS */
 
-/*
- * Note: requires bh_atomic locking.
- */
 int arp_bind_neighbour(struct dst_entry *dst)
 {
        struct device *dev = dst->dev;
@@ -672,7 +676,8 @@ int arp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
                            (addr_type == RTN_UNICAST  && rt->u.dst.dev != dev &&
                             (IN_DEV_PROXY_ARP(in_dev) || pneigh_lookup(&arp_tbl, &tip, dev, 0)))) {
                                n = neigh_event_ns(&arp_tbl, sha, &sip, dev);
-                               neigh_release(n);
+                               if (n)
+                                       neigh_release(n);
 
                                if (skb->stamp.tv_sec == 0 ||
                                    skb->pkt_type == PACKET_HOST ||
@@ -785,7 +790,6 @@ int arp_req_set(struct arpreq *r, struct device * dev)
                return -EINVAL;
 
        err = -ENOBUFS;
-       start_bh_atomic();
        neigh = __neigh_lookup(&arp_tbl, &ip, dev, 1);
        if (neigh) {
                unsigned state = NUD_STALE;
@@ -795,7 +799,6 @@ int arp_req_set(struct arpreq *r, struct device * dev)
                                   r->arp_ha.sa_data : NULL, state, 1, 0);
                neigh_release(neigh);
        }
-       end_bh_atomic();
        return err;
 }
 
@@ -819,17 +822,17 @@ static int arp_req_get(struct arpreq *r, struct device *dev)
        struct neighbour *neigh;
        int err = -ENXIO;
 
-       start_bh_atomic();
-       neigh = __neigh_lookup(&arp_tbl, &ip, dev, 0);
+       neigh = neigh_lookup(&arp_tbl, &ip, dev);
        if (neigh) {
+               read_lock_bh(&neigh->lock);
                memcpy(r->arp_ha.sa_data, neigh->ha, dev->addr_len);
+               r->arp_flags = arp_state_to_flags(neigh);
+               read_unlock_bh(&neigh->lock);
                r->arp_ha.sa_family = dev->type;
                strncpy(r->arp_dev, dev->name, sizeof(r->arp_dev));
-               r->arp_flags = arp_state_to_flags(neigh);
                neigh_release(neigh);
                err = 0;
        }
-       end_bh_atomic();
        return err;
 }
 
@@ -867,14 +870,12 @@ int arp_req_delete(struct arpreq *r, struct device * dev)
                        return -EINVAL;
        }
        err = -ENXIO;
-       start_bh_atomic();
-       neigh = __neigh_lookup(&arp_tbl, &ip, dev, 0);
+       neigh = neigh_lookup(&arp_tbl, &ip, dev);
        if (neigh) {
                if (neigh->nud_state&~NUD_NOARP)
                        err = neigh_update(neigh, NULL, NUD_FAILED, 1, 0);
                neigh_release(neigh);
        }
-       end_bh_atomic();
        return err;
 }
 
@@ -961,16 +962,16 @@ int arp_get_info(char *buffer, char **start, off_t offset, int length, int dummy
        char hbuffer[HBUFFERLEN];
        int i,j,k;
        const char hexbuf[] =  "0123456789ABCDEF";
+       char abuf[16];
 
        size = sprintf(buffer,"IP address       HW type     Flags       HW address            Mask     Device\n");
 
        pos+=size;
        len+=size;
 
-       neigh_table_lock(&arp_tbl);
-
-       for(i=0; i<=NEIGH_HASHMASK; i++)        {
+       for(i=0; i<=NEIGH_HASHMASK; i++) {
                struct neighbour *n;
+               read_lock_bh(&arp_tbl.lock);
                for (n=arp_tbl.hash_buckets[i]; n; n=n->next) {
                        struct device *dev = n->dev;
                        int hatype = dev->type;
@@ -979,17 +980,14 @@ int arp_get_info(char *buffer, char **start, off_t offset, int length, int dummy
                        if (!(n->nud_state&~NUD_NOARP))
                                continue;
 
-                       /* I'd get great pleasure deleting
-                          this ugly code. Let's output it in hexadecimal format.
-                          "arp" utility will eventually repaired  --ANK
-                        */
-#if 1 /* UGLY CODE */
+                       read_lock(&n->lock);
+
 /*
  *     Convert hardware address to XX:XX:XX:XX ... form.
  */
 #if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
                        if (hatype == ARPHRD_AX25 || hatype == ARPHRD_NETROM)
-                            strcpy(hbuffer,ax2asc((ax25_address *)n->ha));
+                               ax2asc2((ax25_address *)n->ha, hbuffer);
                        else {
 #endif
                        for (k=0,j=0;k<HBUFFERLEN-3 && j<dev->addr_len;j++) {
@@ -998,37 +996,33 @@ int arp_get_info(char *buffer, char **start, off_t offset, int length, int dummy
                                hbuffer[k++]=':';
                        }
                        hbuffer[--k]=0;
-       
+
 #if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
                }
 #endif
-#else
-                       if ((neigh->nud_state&NUD_VALID) && dev->addr_len) {
-                               int j;
-                               for (j=0; j < dev->addr_len; j++)
-                                       sprintf(hbuffer+2*j, "%02x", neigh->ha[j]);
-                       } else
-                               sprintf(hbuffer, "0");
-#endif
 
                        size = sprintf(buffer+len,
                                "%-17s0x%-10x0x%-10x%s",
-                               in_ntoa(*(u32*)n->primary_key),
+                               in_ntoa2(*(u32*)n->primary_key, abuf),
                                hatype,
                                arp_state_to_flags(n), 
                                hbuffer);
                        size += sprintf(buffer+len+size,
                                 "     %-17s %s\n",
                                 "*", dev->name);
+                       read_unlock(&n->lock);
 
                        len += size;
                        pos += size;
                  
                        if (pos <= offset)
                                len=0;
-                       if (pos >= offset+length)
-                               goto done;
+                       if (pos >= offset+length) {
+                               read_unlock_bh(&arp_tbl.lock);
+                               goto done;
+                       }
                }
+               read_unlock_bh(&arp_tbl.lock);
        }
 
        for (i=0; i<=PNEIGH_HASHMASK; i++) {
@@ -1039,7 +1033,7 @@ int arp_get_info(char *buffer, char **start, off_t offset, int length, int dummy
 
                        size = sprintf(buffer+len,
                                "%-17s0x%-10x0x%-10x%s",
-                               in_ntoa(*(u32*)n->key),
+                               in_ntoa2(*(u32*)n->key, abuf),
                                hatype,
                                ATF_PUBL|ATF_PERM,
                                "00:00:00:00:00:00");
@@ -1058,7 +1052,6 @@ int arp_get_info(char *buffer, char **start, off_t offset, int length, int dummy
        }
 
 done:
-       neigh_table_unlock(&arp_tbl);
   
        *start = buffer+len-(pos-offset);       /* Start of wanted data */
        len = pos-offset;                       /* Start slop */
@@ -1117,14 +1110,13 @@ __initfunc(void arp_init (void))
 }
 
 
-#ifdef CONFIG_AX25_MODULE
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
 
 /*
  *     ax25 -> ASCII conversion
  */
-char *ax2asc(ax25_address *a)
+char *ax2asc2(ax25_address *a, char *buf)
 {
-       static char buf[11];
        char c, *s;
        int n;
 
index 1f540628da0bf0491ae19c97fb7667ccfde407f1..ff2c930d10c5c25b0f613da2fd4abd06136e7bd3 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *     NET3    IP device support routines.
  *
- *     Version: $Id: devinet.c,v 1.30 1999/06/01 07:49:59 davem Exp $
+ *     Version: $Id: devinet.c,v 1.32 1999/06/09 11:15:33 davem Exp $
  *
  *             This program is free software; you can redistribute it and/or
  *             modify it under the terms of the GNU General Public License
@@ -636,10 +636,10 @@ inet_gifconf(struct device *dev, char *buf, int len)
        return done;
 }
 
-u32 inet_select_addr(struct device *dev, u32 dst, int scope)
+u32 inet_select_addr(const struct device *dev, u32 dst, int scope)
 {
        u32 addr = 0;
-       struct in_device *in_dev = dev->ip_ptr;
+       const struct in_device *in_dev = dev->ip_ptr;
 
        if (in_dev == NULL)
                return 0;
@@ -659,19 +659,19 @@ u32 inet_select_addr(struct device *dev, u32 dst, int scope)
           in this case. It is importnat that lo is the first interface
           in dev_base list.
         */
-       read_lock_bh(&dev_base_lock);
+       read_lock(&dev_base_lock);
        for (dev=dev_base; dev; dev=dev->next) {
                if ((in_dev=dev->ip_ptr) == NULL)
                        continue;
 
                for_primary_ifa(in_dev) {
                        if (ifa->ifa_scope <= scope) {
-                               read_unlock_bh(&dev_base_lock);
+                               read_unlock(&dev_base_lock);
                                return ifa->ifa_local;
                        }
                } endfor_ifa(in_dev);
        }
-       read_unlock_bh(&dev_base_lock);
+       read_unlock(&dev_base_lock);
 
        return 0;
 }
@@ -792,7 +792,7 @@ static int inet_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb)
 
        s_idx = cb->args[0];
        s_ip_idx = ip_idx = cb->args[1];
-       read_lock_bh(&dev_base_lock);
+       read_lock(&dev_base_lock);
        for (dev=dev_base, idx=0; dev; dev = dev->next, idx++) {
                if (idx < s_idx)
                        continue;
@@ -810,7 +810,7 @@ static int inet_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb)
                }
        }
 done:
-       read_unlock_bh(&dev_base_lock);
+       read_unlock(&dev_base_lock);
        cb->args[0] = idx;
        cb->args[1] = ip_idx;
 
@@ -885,13 +885,13 @@ void inet_forward_change()
        ipv4_devconf.accept_redirects = !on;
        ipv4_devconf_dflt.forwarding = on;
 
-       read_lock_bh(&dev_base_lock);
+       read_lock(&dev_base_lock);
        for (dev = dev_base; dev; dev = dev->next) {
                struct in_device *in_dev = dev->ip_ptr;
                if (in_dev)
                        in_dev->cnf.forwarding = on;
        }
-       read_unlock_bh(&dev_base_lock);
+       read_unlock(&dev_base_lock);
 
        rt_cache_flush(0);
 
index a174704832d2ca19db2ca264de4503d9ec0f88fa..d57d4daa970bd072335a9f2c5303fca0f82a5195 100644 (file)
@@ -5,7 +5,7 @@
  *
  *             IPv4 Forwarding Information Base: FIB frontend.
  *
- * Version:    $Id: fib_frontend.c,v 1.15 1999/03/21 05:22:31 davem Exp $
+ * Version:    $Id: fib_frontend.c,v 1.16 1999/06/09 10:10:42 davem Exp $
  *
  * Authors:    Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
  *
@@ -123,13 +123,11 @@ fib_get_procinfo(char *buffer, char **start, off_t offset, int length, int dummy
                first = 0;
        }
 
-       /* rtnl_shlock(); -- it is pointless at the moment --ANK */
        if (main_table && count > 0) {
                int n = main_table->tb_get_info(main_table, ptr, first, count);
                count -= n;
                ptr += n*128;
        }
-       /* rtnl_shunlock(); */
        len = ptr - *start;
        if (len >= length)
                return length;
index a11427e4cc65f27a99307b3f972799a32fea0983..0472f61189b64a6e1818fccfe59b5eafe6408d0a 100644 (file)
@@ -5,7 +5,7 @@
  *
  *             IPv4 FIB: lookup engine and maintenance routines.
  *
- * Version:    $Id: fib_hash.c,v 1.9 1999/05/27 00:38:05 davem Exp $
+ * Version:    $Id: fib_hash.c,v 1.10 1999/06/09 10:10:45 davem Exp $
  *
  * Authors:    Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
  *
@@ -246,10 +246,10 @@ fn_new_zone(struct fn_hash *table, int z)
        fz->fz_mask = inet_make_mask(z);
 
        /* Find the first not empty zone with more specific mask */
-       write_lock_bh(&fib_hash_lock);
        for (i=z+1; i<=32; i++)
                if (table->fn_zones[i])
                        break;
+       write_lock_bh(&fib_hash_lock);
        if (i>32) {
                /* No more specific masks, we are the first. */
                fz->fz_next = table->fn_zone_list;
@@ -270,7 +270,7 @@ fn_hash_lookup(struct fib_table *tb, const struct rt_key *key, struct fib_result
        struct fn_zone *fz;
        struct fn_hash *t = (struct fn_hash*)tb->tb_data;
 
-       read_lock_bh(&fib_hash_lock);
+       read_lock(&fib_hash_lock);
        for (fz = t->fn_zone_list; fz; fz = fz->fz_next) {
                struct fib_node *f;
                fn_key_t k = fz_key(key->dst, fz);
@@ -307,7 +307,7 @@ fn_hash_lookup(struct fib_table *tb, const struct rt_key *key, struct fib_result
        }
        err = 1;
 out:
-       read_unlock_bh(&fib_hash_lock);
+       read_unlock(&fib_hash_lock);
        return err;
 }
 
@@ -353,7 +353,7 @@ fn_hash_select_default(struct fib_table *tb, const struct rt_key *key, struct fi
        last_resort = NULL;
        order = -1;
 
-       read_lock_bh(&fib_hash_lock);
+       read_lock(&fib_hash_lock);
        for (f = fz->fz_hash[0]; f; f = f->fn_next) {
                struct fib_info *next_fi = FIB_INFO(f);
 
@@ -395,7 +395,7 @@ fn_hash_select_default(struct fib_table *tb, const struct rt_key *key, struct fi
                res->fi = last_resort;
        fn_hash_last_dflt = last_idx;
 out:
-       read_unlock_bh(&fib_hash_lock);
+       read_unlock(&fib_hash_lock);
 }
 
 #define FIB_SCAN(f, fp) \
@@ -469,7 +469,6 @@ rta->rta_prefsrc ? *(u32*)rta->rta_prefsrc : 0);
 
        fp = fz_chain_p(key, fz);
 
-       write_lock_bh(&fib_hash_lock);
 
        /*
         * Scan list to find the first route with the same destination
@@ -574,12 +573,15 @@ replace:
         */
 
        new_f->fn_next = f;
+       write_lock_bh(&fib_hash_lock);
        *fp = new_f;
+       write_unlock_bh(&fib_hash_lock);
        fz->fz_nent++;
 
        if (del_fp) {
                f = *del_fp;
                /* Unlink replaced node */
+               write_lock_bh(&fib_hash_lock);
                *del_fp = f->fn_next;
                write_unlock_bh(&fib_hash_lock);
 
@@ -590,14 +592,12 @@ replace:
                fn_free_node(f);
                fz->fz_nent--;
        } else {
-               write_unlock_bh(&fib_hash_lock);
                rt_cache_flush(-1);
        }
        rtmsg_fib(RTM_NEWROUTE, new_f, z, tb->tb_id, n, req);
        return 0;
 
 out:
-       write_unlock_bh(&fib_hash_lock);
        fib_release_info(fi);
        return err;
 }
@@ -635,13 +635,11 @@ FTprint("tb(%d)_delete: %d %08x/%d %d\n", tb->tb_id, r->rtm_type, rta->rta_dst ?
 
        fp = fz_chain_p(key, fz);
 
-       write_lock_bh(&fib_hash_lock);
 
        FIB_SCAN(f, fp) {
                if (fn_key_eq(f->fn_key, key))
                        break;
                if (fn_key_leq(key, f->fn_key)) {
-                       write_unlock_bh(&fib_hash_lock);
                        return -ESRCH;
                }
        }
@@ -658,7 +656,6 @@ FTprint("tb(%d)_delete: %d %08x/%d %d\n", tb->tb_id, r->rtm_type, rta->rta_dst ?
                struct fib_info * fi = FIB_INFO(f);
 
                if (f->fn_state&FN_S_ZOMBIE) {
-                       write_unlock_bh(&fib_hash_lock);
                        return -ESRCH;
                }
                matched++;
@@ -676,6 +673,7 @@ FTprint("tb(%d)_delete: %d %08x/%d %d\n", tb->tb_id, r->rtm_type, rta->rta_dst ?
                rtmsg_fib(RTM_DELROUTE, f, z, tb->tb_id, n, req);
 
                if (matched != 1) {
+                       write_lock_bh(&fib_hash_lock);
                        *del_fp = f->fn_next;
                        write_unlock_bh(&fib_hash_lock);
 
@@ -684,7 +682,6 @@ FTprint("tb(%d)_delete: %d %08x/%d %d\n", tb->tb_id, r->rtm_type, rta->rta_dst ?
                        fn_free_node(f);
                        fz->fz_nent--;
                } else {
-                       write_unlock_bh(&fib_hash_lock);
                        f->fn_state |= FN_S_ZOMBIE;
                        if (f->fn_state&FN_S_ACCESSED) {
                                f->fn_state &= ~FN_S_ACCESSED;
@@ -696,7 +693,6 @@ FTprint("tb(%d)_delete: %d %08x/%d %d\n", tb->tb_id, r->rtm_type, rta->rta_dst ?
 
                return 0;
        }
-       write_unlock_bh(&fib_hash_lock);
        return -ESRCH;
 }
 
@@ -710,7 +706,9 @@ fn_flush_list(struct fib_node ** fp, int z, struct fn_hash *table)
                struct fib_info *fi = FIB_INFO(f);
 
                if (fi && ((f->fn_state&FN_S_ZOMBIE) || (fi->fib_flags&RTNH_F_DEAD))) {
+                       write_lock_bh(&fib_hash_lock);
                        *fp = f->fn_next;
+                       write_unlock_bh(&fib_hash_lock);
 
                        fn_free_node(f);
                        found++;
@@ -728,7 +726,6 @@ static int fn_hash_flush(struct fib_table *tb)
        int found = 0;
 
        fib_hash_zombies = 0;
-       write_lock_bh(&fib_hash_lock);
        for (fz = table->fn_zone_list; fz; fz = fz->fz_next) {
                int i;
                int tmp = 0;
@@ -737,7 +734,6 @@ static int fn_hash_flush(struct fib_table *tb)
                fz->fz_nent -= tmp;
                found += tmp;
        }
-       write_unlock_bh(&fib_hash_lock);
        return found;
 }
 
@@ -751,7 +747,7 @@ static int fn_hash_get_info(struct fib_table *tb, char *buffer, int first, int c
        int pos = 0;
        int n = 0;
 
-       read_lock_bh(&fib_hash_lock);
+       read_lock(&fib_hash_lock);
        for (fz=table->fn_zone_list; fz; fz = fz->fz_next) {
                int i;
                struct fib_node *f;
@@ -782,7 +778,7 @@ static int fn_hash_get_info(struct fib_table *tb, char *buffer, int first, int c
                }
        }
 out:
-       read_unlock_bh(&fib_hash_lock);
+       read_unlock(&fib_hash_lock);
        return n;
 }
 #endif
@@ -845,18 +841,18 @@ static int fn_hash_dump(struct fib_table *tb, struct sk_buff *skb, struct netlin
        struct fn_hash *table = (struct fn_hash*)tb->tb_data;
 
        s_m = cb->args[1];
-       read_lock_bh(&fib_hash_lock);
+       read_lock(&fib_hash_lock);
        for (fz = table->fn_zone_list, m=0; fz; fz = fz->fz_next, m++) {
                if (m < s_m) continue;
                if (m > s_m)
                        memset(&cb->args[2], 0, sizeof(cb->args) - 2*sizeof(cb->args[0]));
                if (fn_hash_dump_zone(skb, cb, tb, fz) < 0) {
                        cb->args[1] = m;
-                       read_unlock_bh(&fib_hash_lock);
+                       read_unlock(&fib_hash_lock);
                        return -1;
                }
        }
-       read_unlock_bh(&fib_hash_lock);
+       read_unlock(&fib_hash_lock);
        cb->args[1] = m;
        return skb->len;
 }
index bc4037c1eb7fb951f5f0fb7417279cb2e4ac2c88..97074198eecf77258010c14fcafce72743eb48ab 100644 (file)
@@ -5,7 +5,7 @@
  *
  *             IPv4 Forwarding Information Base: policy rules.
  *
- * Version:    $Id: fib_rules.c,v 1.10 1999/05/27 00:38:03 davem Exp $
+ * Version:    $Id: fib_rules.c,v 1.11 1999/06/09 10:10:47 davem Exp $
  *
  * Authors:    Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
  *
@@ -88,7 +88,6 @@ int inet_rtm_delrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
        struct fib_rule *r, **rp;
        int err = -ESRCH;
 
-       write_lock_bh(&fib_rules_lock);
        for (rp=&fib_rules; (r=*rp) != NULL; rp=&r->r_next) {
                if ((!rta[RTA_SRC-1] || memcmp(RTA_DATA(rta[RTA_SRC-1]), &r->r_src, 4) == 0) &&
                    rtm->rtm_src_len == r->r_src_len &&
@@ -106,14 +105,15 @@ int inet_rtm_delrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
                        if (r == &local_rule)
                                break;
 
+                       write_lock_bh(&fib_rules_lock);
                        *rp = r->r_next;
+                       write_unlock_bh(&fib_rules_lock);
                        if (r != &default_rule && r != &main_rule)
                                kfree(r);
                        err = 0;
                        break;
                }
        }
-       write_unlock_bh(&fib_rules_lock);
        return err;
 }
 
@@ -192,7 +192,6 @@ int inet_rtm_newrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
                memcpy(&new_r->r_tclassid, RTA_DATA(rta[RTA_FLOW-1]), 4);
 #endif
 
-       write_lock_bh(&fib_rules_lock);
        rp = &fib_rules;
        if (!new_r->r_preference) {
                r = fib_rules;
@@ -210,6 +209,7 @@ int inet_rtm_newrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
        }
 
        new_r->r_next = r;
+       write_lock_bh(&fib_rules_lock);
        *rp = new_r;
        write_unlock_bh(&fib_rules_lock);
        return 0;
@@ -255,24 +255,26 @@ static void fib_rules_detach(struct device *dev)
 {
        struct fib_rule *r;
 
-       write_lock_bh(&fib_rules_lock);
        for (r=fib_rules; r; r=r->r_next) {
-               if (r->r_ifindex == dev->ifindex)
+               if (r->r_ifindex == dev->ifindex) {
+                       write_lock_bh(&fib_rules_lock);
                        r->r_ifindex = -1;
+                       write_unlock_bh(&fib_rules_lock);
+               }
        }
-       write_unlock_bh(&fib_rules_lock);
 }
 
 static void fib_rules_attach(struct device *dev)
 {
        struct fib_rule *r;
 
-       write_lock_bh(&fib_rules_lock);
        for (r=fib_rules; r; r=r->r_next) {
-               if (r->r_ifindex == -1 && strcmp(dev->name, r->r_ifname) == 0)
+               if (r->r_ifindex == -1 && strcmp(dev->name, r->r_ifname) == 0) {
+                       write_lock_bh(&fib_rules_lock);
                        r->r_ifindex = dev->ifindex;
+                       write_unlock_bh(&fib_rules_lock);
+               }
        }
-       write_unlock_bh(&fib_rules_lock);
 }
 
 int fib_lookup(const struct rt_key *key, struct fib_result *res)
@@ -285,7 +287,7 @@ int fib_lookup(const struct rt_key *key, struct fib_result *res)
        u32 saddr = key->src;
 
 FRprintk("Lookup: %08x <- %08x ", key->dst, key->src);
-       read_lock_bh(&fib_rules_lock);
+       read_lock(&fib_rules_lock);
        for (r = fib_rules; r; r=r->r_next) {
                if (((saddr^r->r_src) & r->r_srcmask) ||
                    ((daddr^r->r_dst) & r->r_dstmask) ||
@@ -305,14 +307,14 @@ FRprintk("tb %d r %d ", r->r_table, r->r_action);
                        policy = r;
                        break;
                case RTN_UNREACHABLE:
-                       read_unlock_bh(&fib_rules_lock);
+                       read_unlock(&fib_rules_lock);
                        return -ENETUNREACH;
                default:
                case RTN_BLACKHOLE:
-                       read_unlock_bh(&fib_rules_lock);
+                       read_unlock(&fib_rules_lock);
                        return -EINVAL;
                case RTN_PROHIBIT:
-                       read_unlock_bh(&fib_rules_lock);
+                       read_unlock(&fib_rules_lock);
                        return -EACCES;
                }
 
@@ -322,16 +324,16 @@ FRprintk("tb %d r %d ", r->r_table, r->r_action);
                if (err == 0) {
 FRprintk("ok\n");
                        res->r = policy;
-                       read_unlock_bh(&fib_rules_lock);
+                       read_unlock(&fib_rules_lock);
                        return 0;
                }
                if (err < 0 && err != -EAGAIN) {
-                       read_unlock_bh(&fib_rules_lock);
+                       read_unlock(&fib_rules_lock);
                        return err;
                }
        }
 FRprintk("FAILURE\n");
-       read_unlock_bh(&fib_rules_lock);
+       read_unlock(&fib_rules_lock);
        return -ENETUNREACH;
 }
 
@@ -418,14 +420,14 @@ int inet_dump_rules(struct sk_buff *skb, struct netlink_callback *cb)
        int s_idx = cb->args[0];
        struct fib_rule *r;
 
-       read_lock_bh(&fib_rules_lock);
+       read_lock(&fib_rules_lock);
        for (r=fib_rules, idx=0; r; r = r->r_next, idx++) {
                if (idx < s_idx)
                        continue;
                if (inet_fill_rule(skb, r, cb) < 0)
                        break;
        }
-       read_unlock_bh(&fib_rules_lock);
+       read_unlock(&fib_rules_lock);
        cb->args[0] = idx;
 
        return skb->len;
index 0bc39f3efc5848f0e0ef1dbb8e2dcba362a539e4..9456c7f299ba931c5c65c93cd8411f5961f2b8eb 100644 (file)
@@ -3,7 +3,7 @@
  *     
  *             Alan Cox, <alan@redhat.com>
  *
- *     Version: $Id: icmp.c,v 1.54 1999/05/30 01:16:22 davem Exp $
+ *     Version: $Id: icmp.c,v 1.57 1999/06/09 10:10:50 davem Exp $
  *
  *     This program is free software; you can redistribute it and/or
  *     modify it under the terms of the GNU General Public License
@@ -699,8 +699,8 @@ static void icmp_unreach(struct icmphdr *icmph, struct sk_buff *skb, int len)
                        case ICMP_FRAG_NEEDED:
                                if (ipv4_config.no_pmtu_disc) {
                                        if (net_ratelimit())
-                                               printk(KERN_INFO "ICMP: %s: fragmentation needed and DF set.\n",
-                                              in_ntoa(iph->daddr));
+                                               printk(KERN_INFO "ICMP: %d.%d.%d.%d: fragmentation needed and DF set.\n",
+                                                      NIPQUAD(iph->daddr));
                                } else {
                                        unsigned short new_mtu;
                                        new_mtu = ip_rt_frag_needed(iph, ntohs(icmph->un.frag.mtu));
@@ -711,7 +711,7 @@ static void icmp_unreach(struct icmphdr *icmph, struct sk_buff *skb, int len)
                                break;
                        case ICMP_SR_FAILED:
                                if (net_ratelimit())
-                                       printk(KERN_INFO "ICMP: %s: Source Route Failed.\n", in_ntoa(iph->daddr));
+                                       printk(KERN_INFO "ICMP: %d.%d.%d.%d: Source Route Failed.\n", NIPQUAD(iph->daddr));
                                break;
                        default:
                                break;
@@ -741,8 +741,8 @@ static void icmp_unreach(struct icmphdr *icmph, struct sk_buff *skb, int len)
                if (inet_addr_type(iph->daddr) == RTN_BROADCAST)
                {
                        if (net_ratelimit())
-                               printk(KERN_WARNING "%s sent an invalid ICMP error to a broadcast.\n",
-                               in_ntoa(skb->nh.iph->saddr));
+                               printk(KERN_WARNING "%d.%d.%d.%d sent an invalid ICMP error to a broadcast.\n",
+                               NIPQUAD(skb->nh.iph->saddr));
                        return; 
                }
        }
index 1e1a2f9c2e3cb571cdb8ffb5b181ff39778906a1..61c53041871966860853041a4db628db388e81bc 100644 (file)
@@ -8,7 +8,7 @@
  *     the older version didn't come out right using gcc 2.5.8, the newer one
  *     seems to fall out with gcc 2.6.2.
  *
- *     Version: $Id: igmp.c,v 1.31 1999/05/27 00:37:59 davem Exp $
+ *     Version: $Id: igmp.c,v 1.32 1999/06/09 10:10:53 davem Exp $
  *
  *     Authors:
  *             Alan Cox <Alan.Cox@linux.org>
 #include <linux/mroute.h>
 #endif
 
+/* Big mc list lock for all the devices */
+static rwlock_t ip_mc_lock = RW_LOCK_UNLOCKED;
+/* Big mc list semaphore for all the sockets.
+   We do not refer to this list in IP data paths or from BH,
+   so that semaphore is OK.
+ */
+DECLARE_MUTEX(ip_sk_mc_sem);
+
+
 #define IP_MAX_MEMBERSHIPS 20
 
 #ifdef CONFIG_IP_MULTICAST
@@ -216,6 +225,8 @@ static void igmp_timer_expire(unsigned long data)
        struct in_device *in_dev = im->interface;
        int err;
 
+       read_lock(&ip_mc_lock);
+
        im->tm_running=0;
 
        if (IGMP_V1_SEEN(in_dev))
@@ -234,6 +245,7 @@ static void igmp_timer_expire(unsigned long data)
                igmp_start_timer(im, IGMP_Unsolicited_Report_Interval);
        }
        im->reporter = 1;
+       read_unlock(&ip_mc_lock);
 }
 
 static void igmp_heard_report(struct in_device *in_dev, u32 group)
@@ -245,14 +257,16 @@ static void igmp_heard_report(struct in_device *in_dev, u32 group)
        if (LOCAL_MCAST(group))
                return;
 
+       read_lock(&ip_mc_lock);
        for (im=in_dev->mc_list; im!=NULL; im=im->next) {
                if (im->multiaddr == group) {
                        igmp_stop_timer(im);
                        im->reporter = 0;
                        im->unsolicit_count = 0;
-                       return;
+                       break;
                }
        }
+       read_unlock(&ip_mc_lock);
 }
 
 static void igmp_heard_query(struct in_device *in_dev, unsigned char max_resp_time,
@@ -281,6 +295,7 @@ static void igmp_heard_query(struct in_device *in_dev, unsigned char max_resp_ti
         * - Use the igmp->igmp_code field as the maximum
         *   delay possible
         */
+       read_lock(&ip_mc_lock);
        for (im=in_dev->mc_list; im!=NULL; im=im->next) {
                if (group && group != im->multiaddr)
                        continue;
@@ -291,6 +306,7 @@ static void igmp_heard_query(struct in_device *in_dev, unsigned char max_resp_ti
                        igmp_stop_timer(im);
                igmp_start_timer(im, max_delay);
        }
+       read_unlock(&ip_mc_lock);
 }
 
 int igmp_rcv(struct sk_buff *skb, unsigned short len)
@@ -380,9 +396,7 @@ static void igmp_group_dropped(struct ip_mc_list *im)
        if (LOCAL_MCAST(im->multiaddr))
                return;
 
-       start_bh_atomic();
        igmp_stop_timer(im);
-       end_bh_atomic();
 
        if (im->reporter && !IGMP_V1_SEEN(im->interface))
                igmp_send_report(im->interface->dev, im->multiaddr, IGMP_HOST_LEAVE_MESSAGE);
@@ -400,9 +414,7 @@ static void igmp_group_added(struct ip_mc_list *im)
        if (LOCAL_MCAST(im->multiaddr))
                return;
 
-       start_bh_atomic();
        igmp_start_timer(im, IGMP_Initial_Report_Delay);
-       end_bh_atomic();
 #endif
 }
 
@@ -422,16 +434,17 @@ void ip_mc_inc_group(struct in_device *in_dev, u32 addr)
 
        im = (struct ip_mc_list *)kmalloc(sizeof(*im), GFP_KERNEL);
 
+       write_lock_bh(&ip_mc_lock);
        for (i=in_dev->mc_list; i; i=i->next) {
                if (i->multiaddr == addr) {
                        i->users++;
                        if (im)
                                kfree(im);
-                       return;
+                       goto out;
                }
        }
        if (!im)
-               return;
+               goto out;
        im->users=1;
        im->interface=in_dev;
        im->multiaddr=addr;
@@ -447,9 +460,13 @@ void ip_mc_inc_group(struct in_device *in_dev, u32 addr)
        im->next=in_dev->mc_list;
        in_dev->mc_list=im;
        igmp_group_added(im);
+       write_unlock_bh(&ip_mc_lock);
        if (in_dev->dev->flags & IFF_UP)
                ip_rt_multicast_event(in_dev);
        return;
+out:
+       write_unlock_bh(&ip_mc_lock);
+       return;
 }
 
 /*
@@ -458,22 +475,27 @@ void ip_mc_inc_group(struct in_device *in_dev, u32 addr)
 
 int ip_mc_dec_group(struct in_device *in_dev, u32 addr)
 {
+       int err = -ESRCH;
        struct ip_mc_list *i, **ip;
 
+       write_lock_bh(&ip_mc_lock);
        for (ip=&in_dev->mc_list; (i=*ip)!=NULL; ip=&i->next) {
                if (i->multiaddr==addr) {
                        if (--i->users == 0) {
                                *ip = i->next;
-                               synchronize_bh();
-
                                igmp_group_dropped(i);
+
+                               write_unlock_bh(&ip_mc_lock);
                                if (in_dev->dev->flags & IFF_UP)
                                        ip_rt_multicast_event(in_dev);
                                kfree_s(i, sizeof(*i));
+                               return 0;
                        }
-                       return 0;
+                       err = 0;
+                       break;
                }
        }
+       write_unlock_bh(&ip_mc_lock);
        return -ESRCH;
 }
 
@@ -483,8 +505,10 @@ void ip_mc_down(struct in_device *in_dev)
 {
        struct ip_mc_list *i;
 
+       read_lock_bh(&ip_mc_lock);
        for (i=in_dev->mc_list; i; i=i->next)
                igmp_group_dropped(i);
+       read_unlock_bh(&ip_mc_lock);
 
        ip_mc_dec_group(in_dev, IGMP_ALL_HOSTS);
 }
@@ -497,8 +521,10 @@ void ip_mc_up(struct in_device *in_dev)
 
        ip_mc_inc_group(in_dev, IGMP_ALL_HOSTS);
 
+       read_lock_bh(&ip_mc_lock);
        for (i=in_dev->mc_list; i; i=i->next)
                igmp_group_added(i);
+       read_unlock_bh(&ip_mc_lock);
 }
 
 /*
@@ -509,11 +535,13 @@ void ip_mc_destroy_dev(struct in_device *in_dev)
 {
        struct ip_mc_list *i;
 
+       write_lock_bh(&ip_mc_lock);
        while ((i = in_dev->mc_list) != NULL) {
                in_dev->mc_list = i->next;
                igmp_group_dropped(i);
                kfree_s(i, sizeof(*i));
        }
+       write_unlock_bh(&ip_mc_lock);
 }
 
 static struct in_device * ip_mc_find_dev(struct ip_mreqn *imr)
@@ -570,6 +598,7 @@ int ip_mc_join_group(struct sock *sk , struct ip_mreqn *imr)
        iml = (struct ip_mc_socklist *)sock_kmalloc(sk, sizeof(*iml), GFP_KERNEL);
 
        err = -EADDRINUSE;
+       down(&ip_sk_mc_sem);
        for (i=sk->ip_mc_list; i; i=i->next) {
                if (memcmp(&i->multi, imr, sizeof(*imr)) == 0) {
                        /* New style additions are reference counted */
@@ -577,13 +606,13 @@ int ip_mc_join_group(struct sock *sk , struct ip_mreqn *imr)
                                i->count++;
                                err = 0;
                        }
-                       goto done;
+                       goto done_unlock;
                }
                count++;
        }
        err = -ENOBUFS;
        if (iml == NULL || count >= sysctl_igmp_max_memberships)
-               goto done;
+               goto done_unlock;
        memcpy(&iml->multi, imr, sizeof(*imr));
        iml->next = sk->ip_mc_list;
        iml->count = 1;
@@ -591,6 +620,9 @@ int ip_mc_join_group(struct sock *sk , struct ip_mreqn *imr)
        ip_mc_inc_group(in_dev, addr);
        iml = NULL;
        err = 0;
+
+done_unlock:
+       up(&ip_sk_mc_sem);
 done:
        rtnl_shunlock();
        if (iml)
@@ -606,6 +638,7 @@ int ip_mc_leave_group(struct sock *sk, struct ip_mreqn *imr)
 {
        struct ip_mc_socklist *iml, **imlp;
 
+       down(&ip_sk_mc_sem);
        for (imlp=&sk->ip_mc_list; (iml=*imlp)!=NULL; imlp=&iml->next) {
                if (iml->multi.imr_multiaddr.s_addr==imr->imr_multiaddr.s_addr &&
                    iml->multi.imr_address.s_addr==imr->imr_address.s_addr &&
@@ -615,7 +648,7 @@ int ip_mc_leave_group(struct sock *sk, struct ip_mreqn *imr)
                                return 0;
 
                        *imlp = iml->next;
-                       synchronize_bh();
+                       up(&ip_sk_mc_sem);
 
                        in_dev = inetdev_by_index(iml->multi.imr_ifindex);
                        if (in_dev)
@@ -624,6 +657,7 @@ int ip_mc_leave_group(struct sock *sk, struct ip_mreqn *imr)
                        return 0;
                }
        }
+       up(&ip_sk_mc_sem);
        return -EADDRNOTAVAIL;
 }
 
@@ -635,13 +669,37 @@ void ip_mc_drop_socket(struct sock *sk)
 {
        struct ip_mc_socklist *iml;
 
+       down(&ip_sk_mc_sem);
        while ((iml=sk->ip_mc_list) != NULL) {
                struct in_device *in_dev;
                sk->ip_mc_list = iml->next;
+               up(&ip_sk_mc_sem);
+
                if ((in_dev = inetdev_by_index(iml->multi.imr_ifindex)) != NULL)
                        ip_mc_dec_group(in_dev, iml->multi.imr_multiaddr.s_addr);
                sock_kfree_s(sk, iml, sizeof(*iml));
+
+               down(&ip_sk_mc_sem);
        }
+       up(&ip_sk_mc_sem);
+}
+
+int ip_check_mc(struct device *dev, u32 mc_addr)
+{
+       struct in_device *in_dev = dev->ip_ptr;
+       struct ip_mc_list *im;
+
+       if (in_dev) {
+               read_lock(&ip_mc_lock);
+               for (im=in_dev->mc_list; im; im=im->next) {
+                       if (im->multiaddr == mc_addr) {
+                               read_unlock(&ip_mc_lock);
+                               return 1;
+                       }
+               }
+               read_unlock(&ip_mc_lock);
+       }
+       return 0;
 }
 
 
@@ -653,10 +711,10 @@ int ip_mc_procinfo(char *buffer, char **start, off_t offset, int length, int dum
        struct ip_mc_list *im;
        int len=0;
        struct device *dev;
-       
+
        len=sprintf(buffer,"Idx\tDevice    : Count Querier\tGroup    Users Timer\tReporter\n");  
-       
-       read_lock_bh(&dev_base_lock);
+
+       read_lock(&dev_base_lock);
        for(dev = dev_base; dev; dev = dev->next) {
                struct in_device *in_dev = dev->ip_ptr;
                char   *querier = "NONE";
@@ -669,6 +727,7 @@ int ip_mc_procinfo(char *buffer, char **start, off_t offset, int length, int dum
                len+=sprintf(buffer+len,"%d\t%-10s: %5d %7s\n",
                             dev->ifindex, dev->name, dev->mc_count, querier);
 
+               read_lock(&ip_mc_lock);
                for (im = in_dev->mc_list; im; im = im->next) {
                        len+=sprintf(buffer+len,
                                     "\t\t\t\t%08lX %5d %d:%08lX\t\t%d\n",
@@ -681,12 +740,15 @@ int ip_mc_procinfo(char *buffer, char **start, off_t offset, int length, int dum
                                len=0;
                                begin=pos;
                        }
-                       if(pos>offset+length)
+                       if(pos>offset+length) {
+                               read_unlock(&ip_mc_lock);
                                goto done;
+                       }
                }
+               read_unlock(&ip_mc_lock);
        }
 done:
-       read_unlock_bh(&dev_base_lock);
+       read_unlock(&dev_base_lock);
 
        *start=buffer+(offset-begin);
        len-=(offset-begin);
index ff0d1d40153005aa129ace13f1504e4960944899..107ccaa162e4274691f2d14f87f0ac910732b92b 100644 (file)
@@ -5,7 +5,7 @@
  *
  *             The Internet Protocol (IP) module.
  *
- * Version:    $Id: ip_input.c,v 1.39 1999/05/30 01:16:25 davem Exp $
+ * Version:    $Id: ip_input.c,v 1.40 1999/06/09 10:10:55 davem Exp $
  *
  * Authors:    Ross Biro, <bir7@leland.Stanford.Edu>
  *             Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -392,21 +392,17 @@ int ip_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
        if (skb->dst == NULL) {
                if (ip_route_input(skb, iph->daddr, iph->saddr, iph->tos, dev))
                        goto drop; 
-#ifdef CONFIG_CPU_IS_SLOW
-               if (net_cpu_congestion > 10 && !(iph->tos&IPTOS_RELIABILITY) &&
-                   IPTOS_PREC(iph->tos) < IPTOS_PREC_INTERNETCONTROL) {
-                       goto drop;
-               }
-#endif
        }
 
 #ifdef CONFIG_NET_CLS_ROUTE
        if (skb->dst->tclassid) {
                u32 idx = skb->dst->tclassid;
+               write_lock(&ip_rt_acct_lock);
                ip_rt_acct[idx&0xFF].o_packets++;
                ip_rt_acct[idx&0xFF].o_bytes+=skb->len;
                ip_rt_acct[(idx>>16)&0xFF].i_packets++;
                ip_rt_acct[(idx>>16)&0xFF].i_bytes+=skb->len;
+               write_unlock(&ip_rt_acct_lock);
        }
 #endif
 
index ecee09225b49dee55946d531f772fe2b85bea0fe..2d8d672cc6e9d869249ffc18a0fb4635c4932efb 100644 (file)
@@ -2,7 +2,7 @@
  *             IP_MASQ_VDOLIVE  - VDO Live masquerading module
  *
  *
- * Version:    @(#)$Id: ip_masq_vdolive.c,v 1.4 1998/10/06 04:49:07 davem Exp $
+ * Version:    @(#)$Id: ip_masq_vdolive.c,v 1.6 1999/06/09 08:29:03 davem Exp $
  *
  * Author:     Nigel Metheringham <Nigel.Metheringham@ThePLAnet.net>
  *             PLAnet Online Ltd
index 39f4b9e7b4fd53136d9b382b481392b00124bf20..359926a4c42ded4363ca53054ad6339efbe8735b 100644 (file)
@@ -5,7 +5,7 @@
  *
  *             The options processing module for ip.c
  *
- * Version:    $Id: ip_options.c,v 1.16 1999/03/21 05:22:40 davem Exp $
+ * Version:    $Id: ip_options.c,v 1.18 1999/06/09 08:29:06 davem Exp $
  *
  * Authors:    A.N.Kuznetsov
  *             
index 3a53ffd115a2f6ca317456d79d0fa59e6a79ce90..51e27ad67a146ab6e28cff3bcca2a5097e8d8a21 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *  $Id: ipconfig.c,v 1.21 1999/05/27 00:38:01 davem Exp $
+ *  $Id: ipconfig.c,v 1.22 1999/06/09 10:10:57 davem Exp $
  *
  *  Automatic Configuration of IP -- use BOOTP or RARP or user-supplied
  *  information to configure own IP address and routes.
@@ -112,7 +112,7 @@ static int __init ic_open_devs(void)
        unsigned short oflags;
 
        last = &ic_first_dev;
-       read_lock_bh(&dev_base_lock);
+       read_lock(&dev_base_lock);
        for (dev = dev_base; dev; dev = dev->next) {
                if (user_dev_name[0] ? !strcmp(dev->name, user_dev_name) :
                    (!(dev->flags & IFF_LOOPBACK) &&
@@ -144,7 +144,7 @@ static int __init ic_open_devs(void)
                        DBG(("IP-Config: Opened %s (able=%d)\n", dev->name, able));
                }
        }
-       read_unlock_bh(&dev_base_lock);
+       read_unlock(&dev_base_lock);
 
        *last = NULL;
 
index 63356bdec1f213ba99d835960e6a568d1a420f73..1034e0e7aa6b214db05096582592761db72a1d6e 100644 (file)
@@ -9,7 +9,7 @@
  *     as published by the Free Software Foundation; either version
  *     2 of the License, or (at your option) any later version.
  *
- *     Version: $Id: ipmr.c,v 1.40 1999/03/25 10:04:25 davem Exp $
+ *     Version: $Id: ipmr.c,v 1.43 1999/06/09 10:10:59 davem Exp $
  *
  *     Fixes:
  *     Michael Chastain        :       Incorrect size of copying.
@@ -23,6 +23,8 @@
  *     Brad Parker             :       Better behaviour on mrouted upcall
  *                                     overflow.
  *      Carlos Picoto           :       PIMv1 Support
+ *     Pavlin Ivanov Radoslavov:       PIMv2 Registers must checksum only PIM header
+ *                                     Relax this requrement to work with older peers.
  *
  */
 
@@ -431,7 +433,7 @@ static void ipmr_cache_resolve(struct mfc_cache *cache)
                                skb_trim(skb, nlh->nlmsg_len);
                                ((struct nlmsgerr*)NLMSG_DATA(nlh))->error = -EMSGSIZE;
                        }
-                       err = netlink_unicast(rtnl, skb, NETLINK_CB(skb).pid, MSG_DONTWAIT);
+                       err = netlink_unicast(rtnl, skb, NETLINK_CB(skb).dst_pid, MSG_DONTWAIT);
                } else
 #endif
                        ip_mr_forward(skb, cache, 0);
@@ -1343,7 +1345,8 @@ int pim_rcv(struct sk_buff * skb, unsigned short len)
            pim->type != ((PIM_VERSION<<4)|(PIM_REGISTER)) ||
            (pim->flags&PIM_NULL_REGISTER) ||
            reg_dev == NULL ||
-           ip_compute_csum((void *)pim, len)) {
+           (ip_compute_csum((void *)pim, sizeof(*pim)) != 0 &&
+            ip_compute_csum((void *)pim, len))) {
                kfree_skb(skb);
                 return -EINVAL;
         }
index a3ecbd82a6866c3f56be6feffbdec165500732dc..3d9e87de33f296768d9cf45887c2e258d95bd4e3 100644 (file)
@@ -5,7 +5,7 @@
  *
  *             ROUTE - implementation of the IP router.
  *
- * Version:    $Id: route.c,v 1.68 1999/05/27 00:37:54 davem Exp $
+ * Version:    $Id: route.c,v 1.69 1999/06/09 10:11:02 davem Exp $
  *
  * Authors:    Ross Biro, <bir7@leland.Stanford.Edu>
  *             Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -1996,6 +1996,7 @@ ctl_table ipv4_route_table[] = {
 
 #ifdef CONFIG_NET_CLS_ROUTE
 struct ip_rt_acct ip_rt_acct[256];
+rwlock_t ip_rt_acct_lock = RW_LOCK_UNLOCKED;
 
 #ifdef CONFIG_PROC_FS
 static int ip_rt_acct_read(char *buffer, char **start, off_t offset,
@@ -2008,9 +2009,9 @@ static int ip_rt_acct_read(char *buffer, char **start, off_t offset,
                *eof = 1;
        }
        if (length > 0) {
-               start_bh_atomic();
+               read_lock_bh(&ip_rt_acct_lock);
                memcpy(buffer, ((u8*)&ip_rt_acct)+offset, length);
-               end_bh_atomic();
+               read_unlock_bh(&ip_rt_acct_lock);
                return length;
        }
        return 0;
index 9a3ec92429f795d3582637c86b87af4e631774fc..af4165fce4c580e24a8829cc741e7d4f30f4f1dc 100644 (file)
@@ -5,7 +5,7 @@
  *
  *             Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:    $Id: tcp_input.c,v 1.167 1999/05/29 22:37:54 davem Exp $
+ * Version:    $Id: tcp_input.c,v 1.169 1999/06/09 08:29:13 davem Exp $
  *
  * Authors:    Ross Biro, <bir7@leland.Stanford.Edu>
  *             Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
index 9894a5b362d1f1b7dea688186ca3aaff43298227..564e859f259c0c5411b5088388a343f51ab90ef4 100644 (file)
@@ -5,7 +5,7 @@
  *
  *             Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:    $Id: tcp_ipv4.c,v 1.178 1999/05/30 01:16:27 davem Exp $
+ * Version:    $Id: tcp_ipv4.c,v 1.180 1999/06/09 08:29:19 davem Exp $
  *
  *             IPv4 specific functions
  *
index dd025bb388491bf9fd6e56a7f558d1926582fbff..320e5151eec1472099b7b30493bfd0ba2f2084d7 100644 (file)
@@ -5,7 +5,7 @@
  *
  *             The User Datagram Protocol (UDP).
  *
- * Version:    $Id: udp.c,v 1.67 1999/05/27 00:37:50 davem Exp $
+ * Version:    $Id: udp.c,v 1.69 1999/06/09 11:15:31 davem Exp $
  *
  * Authors:    Ross Biro, <bir7@leland.Stanford.Edu>
  *             Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -173,7 +173,7 @@ static inline int udp_lport_inuse(u16 num)
        return 0;
 }
 
-/* Shared by v4/v6 tcp. */
+/* Shared by v4/v6 udp. */
 unsigned short udp_good_socknum(void)
 {
        int result;
index ce74ade2a22b993000e9f6fba91fee607fa0087e..5992cbc5543e1aa350241387b4306ba45610fc2c 100644 (file)
@@ -6,7 +6,7 @@
  *             Various kernel-resident INET utility functions; mainly
  *             for format conversion and debugging output.
  *
- * Version:    $Id: utils.c,v 1.6 1997/12/13 21:53:03 kuznet Exp $
+ * Version:    $Id: utils.c,v 1.7 1999/06/09 10:11:05 davem Exp $
  *
  * Author:     Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
  *
@@ -57,6 +57,11 @@ char *in_ntoa(__u32 in)
        return(buff);
 }
 
+char *in_ntoa2(__u32 in, char *buff)
+{
+       sprintf(buff, "%d.%d.%d.%d", NIPQUAD(in));
+       return buff;
+}
 
 /*
  *     Convert an ASCII string to binary IP. 
index 79e84f3e8f5c74e51b9f051a9563f86922ef7154..9f71f7cdab7005b47bc7c5a59460588e83642354 100644 (file)
@@ -5,7 +5,7 @@
  *     Authors:
  *     Pedro Roque             <roque@di.fc.ul.pt>     
  *
- *     $Id: addrconf.c,v 1.49 1999/05/27 00:38:20 davem Exp $
+ *     $Id: addrconf.c,v 1.50 1999/06/09 10:11:09 davem Exp $
  *
  *     This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -100,9 +100,7 @@ static struct timer_list addr_chk_timer = {
    1. The result of inet6_add_addr() is used only inside lock
       or from bh_atomic context.
 
-   2. inet6_get_lladdr() is used only from bh protected context.
-
-   3. The result of ipv6_chk_addr() is not used outside of bh protected context.
+   2. The result of ipv6_chk_addr() is not used outside of bh protected context.
  */
 
 static __inline__ void addrconf_lock(void)
@@ -463,7 +461,7 @@ out:
        return err;
 }
 
-struct inet6_ifaddr * ipv6_get_lladdr(struct device *dev)
+int ipv6_get_lladdr(struct device *dev, struct in6_addr *addr)
 {
        struct inet6_ifaddr *ifp = NULL;
        struct inet6_dev *idev;
@@ -471,12 +469,15 @@ struct inet6_ifaddr * ipv6_get_lladdr(struct device *dev)
        if ((idev = ipv6_get_idev(dev)) != NULL) {
                addrconf_lock();
                for (ifp=idev->addr_list; ifp; ifp=ifp->if_next) {
-                       if (ifp->scope == IFA_LINK)
-                               break;
+                       if (ifp->scope == IFA_LINK) {
+                               ipv6_addr_copy(addr, &ifp->addr);
+                               addrconf_unlock();
+                               return 0;
+                       }
                }
                addrconf_unlock();
        }
-       return ifp;
+       return -EADDRNOTAVAIL;
 }
 
 /*
@@ -982,7 +983,7 @@ static void sit_add_v4_addrs(struct inet6_dev *idev)
                return;
        }
 
-       read_lock_bh(&dev_base_lock);
+       read_lock(&dev_base_lock);
         for (dev = dev_base; dev != NULL; dev = dev->next) {
                if (dev->ip_ptr && (dev->flags & IFF_UP)) {
                        struct in_device * in_dev = dev->ip_ptr;
@@ -1001,7 +1002,6 @@ static void sit_add_v4_addrs(struct inet6_dev *idev)
                                        flag |= IFA_HOST;
                                }
 
-                               read_unlock_bh(&dev_base_lock);
                                addrconf_lock();
                                ifp = ipv6_add_addr(idev, &addr, flag);
                                if (ifp) {
@@ -1013,11 +1013,10 @@ static void sit_add_v4_addrs(struct inet6_dev *idev)
                                        ipv6_ifa_notify(RTM_NEWADDR, ifp);
                                }
                                addrconf_unlock();
-                               read_lock_bh(&dev_base_lock);
                        }
                }
         }
-       read_unlock_bh(&dev_base_lock);
+       read_unlock(&dev_base_lock);
 }
 
 static void init_loopback(struct device *dev)
@@ -1846,12 +1845,11 @@ __initfunc(void addrconf_init(void))
        struct device *dev;
 
        /* This takes sense only during module load. */
-       read_lock_bh(&dev_base_lock);
+       read_lock(&dev_base_lock);
        for (dev = dev_base; dev; dev = dev->next) {
                if (!(dev->flags&IFF_UP))
                        continue;
 
-               read_unlock_bh(&dev_base_lock);
                switch (dev->type) {
                case ARPHRD_LOOPBACK:   
                        init_loopback(dev);
@@ -1862,9 +1860,8 @@ __initfunc(void addrconf_init(void))
                default:
                        /* Ignore all other */
                }
-               read_lock_bh(&dev_base_lock);
        }
-       read_unlock_bh(&dev_base_lock);
+       read_unlock(&dev_base_lock);
 #endif
        
 #ifdef CONFIG_PROC_FS
index 75c62e1d76abe8bc78dffc06363607990fc98df2..f7f50df869ae80c11491016d0eb927622134ff05 100644 (file)
@@ -7,7 +7,7 @@
  *
  *     Adapted from linux/net/ipv4/af_inet.c
  *
- *     $Id: af_inet6.c,v 1.43 1999/04/22 10:07:39 davem Exp $
+ *     $Id: af_inet6.c,v 1.44 1999/06/09 08:29:29 davem Exp $
  *
  *     This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
index fd116a39929b2f7cba1f5fb688817c74fa116944..a6263d41cef424378db8b8e91cd4e8cb38d71689 100644 (file)
@@ -5,7 +5,7 @@
  *     Authors:
  *     Pedro Roque             <roque@di.fc.ul.pt>     
  *
- *     $Id: ip6_fw.c,v 1.10 1998/08/26 12:04:57 davem Exp $
+ *     $Id: ip6_fw.c,v 1.12 1999/06/09 08:29:32 davem Exp $
  *
  *     This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
index 4f470c972a96ff00ae0b641295cc1511129a04ee..9a635f882758562cd091f8a0dd5405bfedba9439 100644 (file)
@@ -5,7 +5,7 @@
  *     Authors:
  *     Pedro Roque             <roque@di.fc.ul.pt>     
  *
- *     $Id: ip6_output.c,v 1.17 1999/04/22 10:07:42 davem Exp $
+ *     $Id: ip6_output.c,v 1.20 1999/06/09 10:11:12 davem Exp $
  *
  *     Based on linux/net/ipv4/ip_output.c
  *
@@ -75,19 +75,10 @@ int ip6_output(struct sk_buff *skb)
        }
 
        if (hh) {
-#ifdef __alpha__
-               /* Alpha has disguisting memcpy. Help it. */
-               u64 *aligned_hdr = (u64*)(skb->data - 16);
-               u64 *aligned_hdr0 = hh->hh_data;
-               read_lock_irq(&hh->hh_lock);
-               aligned_hdr[0] = aligned_hdr0[0];
-               aligned_hdr[1] = aligned_hdr0[1];
-#else
-               read_lock_irq(&hh->hh_lock);
+               read_lock_bh(&hh->hh_lock);
                memcpy(skb->data - 16, hh->hh_data, 16);
-#endif
-               read_unlock_irq(&hh->hh_lock);
-               skb_push(skb, dev->hard_header_len);
+               read_unlock_bh(&hh->hh_lock);
+               skb_push(skb, hh->hh_len);
                return hh->hh_output(skb);
        } else if (dst->neighbour)
                return dst->neighbour->output(skb);
index 2c836abba320758f0ef4f993b9fad3c7838f6365..cedf9e6916a75a773602c1ed3d5b40447593baa4 100644 (file)
@@ -5,7 +5,7 @@
  *     Authors:
  *     Pedro Roque             <roque@di.fc.ul.pt>     
  *
- *     $Id: mcast.c,v 1.20 1999/05/27 00:38:23 davem Exp $
+ *     $Id: mcast.c,v 1.23 1999/06/09 10:11:14 davem Exp $
  *
  *     Based on linux/ipv4/igmp.c and linux/ipv4/ip_sockglue.c 
  *
 #define MDBG(x)
 #endif
 
+/* Big mc list lock for all the devices */
+static rwlock_t ipv6_mc_lock = RW_LOCK_UNLOCKED;
+/* Big mc list lock for all the sockets */
+static rwlock_t ipv6_sk_mc_lock = RW_LOCK_UNLOCKED;
+
 static struct socket *igmp6_socket;
 
 static void igmp6_join_group(struct ifmcaddr6 *ma);
@@ -115,8 +120,10 @@ int ipv6_sock_mc_join(struct sock *sk, int ifindex, struct in6_addr *addr)
                return err;
        }
 
+       write_lock_bh(&ipv6_sk_mc_lock);
        mc_lst->next = np->ipv6_mc_list;
        np->ipv6_mc_list = mc_lst;
+       write_unlock_bh(&ipv6_sk_mc_lock);
 
        return 0;
 }
@@ -129,13 +136,14 @@ int ipv6_sock_mc_drop(struct sock *sk, int ifindex, struct in6_addr *addr)
        struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
        struct ipv6_mc_socklist *mc_lst, **lnk;
 
+       write_lock_bh(&ipv6_sk_mc_lock);
        for (lnk = &np->ipv6_mc_list; (mc_lst = *lnk) !=NULL ; lnk = &mc_lst->next) {
                if (mc_lst->ifindex == ifindex &&
                    ipv6_addr_cmp(&mc_lst->addr, addr) == 0) {
                        struct device *dev;
 
                        *lnk = mc_lst->next;
-                       synchronize_bh();
+                       write_unlock_bh(&ipv6_sk_mc_lock);
 
                        if ((dev = dev_get_by_index(ifindex)) != NULL)
                                ipv6_dev_mc_dec(dev, &mc_lst->addr);
@@ -143,6 +151,7 @@ int ipv6_sock_mc_drop(struct sock *sk, int ifindex, struct in6_addr *addr)
                        return 0;
                }
        }
+       write_unlock_bh(&ipv6_sk_mc_lock);
 
        return -ENOENT;
 }
@@ -152,15 +161,38 @@ void ipv6_sock_mc_close(struct sock *sk)
        struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
        struct ipv6_mc_socklist *mc_lst;
 
+       write_lock_bh(&ipv6_sk_mc_lock);
        while ((mc_lst = np->ipv6_mc_list) != NULL) {
-               struct device *dev = dev_get_by_index(mc_lst->ifindex);
+               struct device *dev;
+
+               np->ipv6_mc_list = mc_lst->next;
+               write_unlock_bh(&ipv6_sk_mc_lock);
 
+               dev = dev_get_by_index(mc_lst->ifindex);
                if (dev)
                        ipv6_dev_mc_dec(dev, &mc_lst->addr);
 
-               np->ipv6_mc_list = mc_lst->next;
                sock_kfree_s(sk, mc_lst, sizeof(*mc_lst));
+
+               write_lock_bh(&ipv6_sk_mc_lock);
        }
+       write_unlock_bh(&ipv6_sk_mc_lock);
+}
+
+int inet6_mc_check(struct sock *sk, struct in6_addr *addr)
+{
+       struct ipv6_mc_socklist *mc;
+
+       read_lock(&ipv6_sk_mc_lock);
+       for (mc = sk->net_pinfo.af_inet6.ipv6_mc_list; mc; mc=mc->next) {
+               if (ipv6_addr_cmp(&mc->addr, addr) == 0) {
+                       read_unlock(&ipv6_sk_mc_lock);
+                       return 1;
+               }
+       }
+       read_unlock(&ipv6_sk_mc_lock);
+
+       return 0;
 }
 
 static int igmp6_group_added(struct ifmcaddr6 *mc)
@@ -210,9 +242,11 @@ int ipv6_dev_mc_inc(struct device *dev, struct in6_addr *addr)
 
        hash = ipv6_addr_hash(addr);
 
+       write_lock_bh(&ipv6_mc_lock);
        for (mc = inet6_mcast_lst[hash]; mc; mc = mc->next) {
                if (ipv6_addr_cmp(&mc->mca_addr, addr) == 0 && mc->dev == dev) {
                        atomic_inc(&mc->mca_users);
+                       write_unlock_bh(&ipv6_mc_lock);
                        return 0;
                }
        }
@@ -223,8 +257,10 @@ int ipv6_dev_mc_inc(struct device *dev, struct in6_addr *addr)
 
        mc = kmalloc(sizeof(struct ifmcaddr6), GFP_ATOMIC);
 
-       if (mc == NULL)
+       if (mc == NULL) {
+               write_unlock_bh(&ipv6_mc_lock);
                return -ENOMEM;
+       }
 
        memset(mc, 0, sizeof(struct ifmcaddr6));
        mc->mca_timer.function = igmp6_timer_handler;
@@ -242,6 +278,8 @@ int ipv6_dev_mc_inc(struct device *dev, struct in6_addr *addr)
 
        igmp6_group_added(mc);
 
+       write_unlock_bh(&ipv6_mc_lock);
+
        return 0;
 }
 
@@ -257,7 +295,6 @@ static void ipv6_mca_remove(struct device *dev, struct ifmcaddr6 *ma)
                for (lnk = &idev->mc_list; (iter = *lnk) != NULL; lnk = &iter->if_next) {
                        if (iter == ma) {
                                *lnk = iter->if_next;
-                               synchronize_bh();
                                return;
                        }
                }
@@ -274,20 +311,22 @@ int ipv6_dev_mc_dec(struct device *dev, struct in6_addr *addr)
 
        hash = ipv6_addr_hash(addr);
 
+       write_lock_bh(&ipv6_mc_lock);
        for (lnk = &inet6_mcast_lst[hash]; (ma=*lnk) != NULL; lnk = &ma->next) {
                if (ipv6_addr_cmp(&ma->mca_addr, addr) == 0 && ma->dev == dev) {
                        if (atomic_dec_and_test(&ma->mca_users)) {
                                igmp6_group_dropped(ma);
 
                                *lnk = ma->next;
-                               synchronize_bh();
 
                                ipv6_mca_remove(dev, ma);
                                kfree(ma);
                        }
+                       write_unlock_bh(&ipv6_mc_lock);
                        return 0;
                }
        }
+       write_unlock_bh(&ipv6_mc_lock);
 
        return -ENOENT;
 }
@@ -302,10 +341,14 @@ int ipv6_chk_mcast_addr(struct device *dev, struct in6_addr *addr)
 
        hash = ipv6_addr_hash(addr);
 
+       read_lock_bh(&ipv6_mc_lock);
        for (mc = inet6_mcast_lst[hash]; mc; mc=mc->next) {
-               if (mc->dev == dev && ipv6_addr_cmp(&mc->mca_addr, addr) == 0)
+               if (mc->dev == dev && ipv6_addr_cmp(&mc->mca_addr, addr) == 0) {
+                       read_unlock_bh(&ipv6_mc_lock);
                        return 1;
+               }
        }
+       read_unlock_bh(&ipv6_mc_lock);
 
        return 0;
 }
@@ -364,11 +407,14 @@ int igmp6_event_query(struct sk_buff *skb, struct icmp6hdr *hdr, int len)
                if (idev == NULL)
                        return 0;
 
+               read_lock(&ipv6_mc_lock);
                for (ma = idev->mc_list; ma; ma=ma->if_next)
                        igmp6_group_queried(ma, resptime);
+               read_unlock(&ipv6_mc_lock);
        } else {
                int hash = ipv6_addr_hash(addrp);
 
+               read_lock(&ipv6_mc_lock);
                for (ma = inet6_mcast_lst[hash]; ma; ma=ma->next) {
                        if (ma->dev == skb->dev &&
                            ipv6_addr_cmp(addrp, &ma->mca_addr) == 0) {
@@ -376,6 +422,7 @@ int igmp6_event_query(struct sk_buff *skb, struct icmp6hdr *hdr, int len)
                                break;
                        }
                }
+               read_unlock(&ipv6_mc_lock);
        }
 
        return 0;
@@ -410,6 +457,7 @@ int igmp6_event_report(struct sk_buff *skb, struct icmp6hdr *hdr, int len)
 
        hash = ipv6_addr_hash(addrp);
 
+       read_lock(&ipv6_mc_lock);
        for (ma = inet6_mcast_lst[hash]; ma; ma=ma->next) {
                if ((ma->dev == dev) && ipv6_addr_cmp(&ma->mca_addr, addrp) == 0) {
                        if (ma->mca_flags & MAF_TIMER_RUNNING) {
@@ -421,6 +469,7 @@ int igmp6_event_report(struct sk_buff *skb, struct icmp6hdr *hdr, int len)
                        break;
                }
        }
+       read_unlock(&ipv6_mc_lock);
 
        return 0;
 }
@@ -430,9 +479,9 @@ void igmp6_send(struct in6_addr *addr, struct device *dev, int type)
        struct sock *sk = igmp6_socket->sk;
         struct sk_buff *skb;
         struct icmp6hdr *hdr;
-       struct inet6_ifaddr *ifp;
        struct in6_addr *snd_addr;
        struct in6_addr *addrp;
+       struct in6_addr addr_buf;
        struct in6_addr all_routers;
        int err, len, payload_len, full_len;
        u8 ra[8] = { IPPROTO_ICMPV6, 0,
@@ -461,9 +510,7 @@ void igmp6_send(struct in6_addr *addr, struct device *dev, int type)
                dev->hard_header(skb, dev, ETH_P_IPV6, ha, NULL, full_len);
        }
 
-       ifp = ipv6_get_lladdr(dev);
-
-       if (ifp == NULL) {
+       if (ipv6_get_lladdr(dev, &addr_buf)) {
 #if MCAST_DEBUG >= 1
                printk(KERN_DEBUG "igmp6: %s no linklocal address\n",
                       dev->name);
@@ -471,7 +518,7 @@ void igmp6_send(struct in6_addr *addr, struct device *dev, int type)
                return;
        }
 
-       ip6_nd_hdr(sk, skb, dev, &ifp->addr, snd_addr, NEXTHDR_HOP, payload_len);
+       ip6_nd_hdr(sk, skb, dev, &addr_buf, snd_addr, NEXTHDR_HOP, payload_len);
 
        memcpy(skb_put(skb, sizeof(ra)), ra, sizeof(ra));
 
@@ -482,7 +529,7 @@ void igmp6_send(struct in6_addr *addr, struct device *dev, int type)
        addrp = (struct in6_addr *) skb_put(skb, sizeof(struct in6_addr));
        ipv6_addr_copy(addrp, addr);
 
-       hdr->icmp6_cksum = csum_ipv6_magic(&ifp->addr, snd_addr, len,
+       hdr->icmp6_cksum = csum_ipv6_magic(&addr_buf, snd_addr, len,
                                           IPPROTO_ICMPV6,
                                           csum_partial((__u8 *) hdr, len, 0));
 
@@ -504,7 +551,6 @@ static void igmp6_join_group(struct ifmcaddr6 *ma)
        if ((addr_type & (IPV6_ADDR_LINKLOCAL|IPV6_ADDR_LOOPBACK)))
                return;
 
-       start_bh_atomic();
        igmp6_send(&ma->mca_addr, ma->dev, ICMPV6_MGM_REPORT);
 
        delay = net_random() % IGMP6_UNSOLICITED_IVAL;
@@ -515,7 +561,6 @@ static void igmp6_join_group(struct ifmcaddr6 *ma)
 
        add_timer(&ma->mca_timer);
        ma->mca_flags |= MAF_TIMER_RUNNING | MAF_LAST_REPORTER;
-       end_bh_atomic();
 }
 
 static void igmp6_leave_group(struct ifmcaddr6 *ma)
@@ -527,22 +572,22 @@ static void igmp6_leave_group(struct ifmcaddr6 *ma)
        if ((addr_type & IPV6_ADDR_LINKLOCAL))
                return;
 
-       start_bh_atomic();
        if (ma->mca_flags & MAF_LAST_REPORTER)
                igmp6_send(&ma->mca_addr, ma->dev, ICMPV6_MGM_REDUCTION);
 
        if (ma->mca_flags & MAF_TIMER_RUNNING)
                del_timer(&ma->mca_timer);
-       end_bh_atomic();
 }
 
 void igmp6_timer_handler(unsigned long data)
 {
        struct ifmcaddr6 *ma = (struct ifmcaddr6 *) data;
 
+       read_lock(&ipv6_mc_lock);
        ma->mca_flags |=  MAF_LAST_REPORTER;
        igmp6_send(&ma->mca_addr, ma->dev, ICMPV6_MGM_REPORT);
        ma->mca_flags &= ~MAF_TIMER_RUNNING;
+       read_unlock(&ipv6_mc_lock);
 }
 
 /* Device going down */
@@ -554,8 +599,10 @@ void ipv6_mc_down(struct inet6_dev *idev)
 
        /* Withdraw multicast list */
 
+       read_lock_bh(&ipv6_mc_lock);
        for (i = idev->mc_list; i; i=i->if_next)
                igmp6_group_dropped(i);
+       read_unlock_bh(&ipv6_mc_lock);
 
        /* Delete all-nodes address. */
 
@@ -577,8 +624,10 @@ void ipv6_mc_up(struct inet6_dev *idev)
 
        /* Install multicast list, except for all-nodes (already installed) */
 
+       read_lock(&ipv6_mc_lock);
        for (i = idev->mc_list; i; i=i->if_next)
                igmp6_group_added(i);
+       read_unlock(&ipv6_mc_lock);
 }
 
 /*
@@ -590,6 +639,7 @@ void ipv6_mc_destroy_dev(struct inet6_dev *idev)
        int hash;
        struct ifmcaddr6 *i, **lnk;
 
+       write_lock_bh(&ipv6_mc_lock);
        while ((i = idev->mc_list) != NULL) {
                idev->mc_list = i->if_next;
 
@@ -598,13 +648,13 @@ void ipv6_mc_destroy_dev(struct inet6_dev *idev)
                for (lnk = &inet6_mcast_lst[hash]; *lnk; lnk = &(*lnk)->next) {
                        if (*lnk == i) {
                                *lnk = i->next;
-                               synchronize_bh();
                                break;
                        }
                }
                igmp6_group_dropped(i);
                kfree(i);
        }
+       write_unlock_bh(&ipv6_mc_lock);
 }
 
 #ifdef CONFIG_PROC_FS
@@ -616,13 +666,14 @@ static int igmp6_read_proc(char *buffer, char **start, off_t offset,
        int len=0;
        struct device *dev;
        
-       read_lock_bh(&dev_base_lock);
+       read_lock(&dev_base_lock);
        for (dev = dev_base; dev; dev = dev->next) {
                struct inet6_dev *idev;
 
                if ((idev = ipv6_get_idev(dev)) == NULL)
                        continue;
 
+               read_lock_bh(&ipv6_mc_lock);
                for (im = idev->mc_list; im; im = im->if_next) {
                        int i;
 
@@ -642,14 +693,17 @@ static int igmp6_read_proc(char *buffer, char **start, off_t offset,
                                len=0;
                                begin=pos;
                        }
-                       if (pos > offset+length)
+                       if (pos > offset+length) {
+                               read_unlock_bh(&ipv6_mc_lock);
                                goto done;
+                       }
                }
+               read_unlock_bh(&ipv6_mc_lock);
        }
        *eof = 1;
 
 done:
-       read_unlock_bh(&dev_base_lock);
+       read_unlock(&dev_base_lock);
 
        *start=buffer+(offset-begin);
        len-=(offset-begin);
index bb5e083738d466f5d154d933f80fd14651a6d677..d0613056ac41bec5a2c144bce15cdb3ed4dcbaa5 100644 (file)
@@ -268,14 +268,21 @@ ndisc_build_ll_hdr(struct sk_buff *skb, struct device *dev,
                        ndisc_mc_map(daddr, ha, dev, 1);
                        h_dest = ha;
                } else if (neigh) {
-                       h_dest = neigh->ha;
+                       read_lock_bh(&neigh->lock);
+                       if (neigh->nud_state&NUD_VALID) {
+                               memcpy(ha, neigh->ha, dev->addr_len);
+                               h_dest = ha;
+                       }
+                       read_unlock_bh(&neigh->lock);
                } else {
                        neigh = neigh_lookup(&nd_tbl, daddr, dev);
                        if (neigh) {
+                               read_lock_bh(&neigh->lock);
                                if (neigh->nud_state&NUD_VALID) {
                                        memcpy(ha, neigh->ha, dev->addr_len);
                                        h_dest = ha;
                                }
+                               read_unlock_bh(&neigh->lock);
                                neigh_release(neigh);
                        }
                }
@@ -362,6 +369,7 @@ void ndisc_send_ns(struct device *dev, struct neighbour *neigh,
         struct sock *sk = ndisc_socket->sk;
         struct sk_buff *skb;
         struct nd_msg *msg;
+       struct in6_addr addr_buf;
         int len;
        int err;
 
@@ -377,13 +385,8 @@ void ndisc_send_ns(struct device *dev, struct neighbour *neigh,
        }
 
        if (saddr == NULL) {
-               struct inet6_ifaddr *ifa;
-
-               /* use link local address */
-               ifa = ipv6_get_lladdr(dev);
-
-               if (ifa)
-                       saddr = &ifa->addr;
+               if (!ipv6_get_lladdr(dev, &addr_buf))
+                       saddr = &addr_buf;
        }
 
        if (ndisc_build_ll_hdr(skb, dev, daddr, neigh, len) == 0) {
@@ -501,13 +504,15 @@ static void ndisc_error_report(struct neighbour *neigh, struct sk_buff *skb)
        kfree_skb(skb);
 }
 
+/* Called with locked neigh: either read or both */
+
 static void ndisc_solicit(struct neighbour *neigh, struct sk_buff *skb)
 {
        struct in6_addr *saddr = NULL;
        struct in6_addr mcaddr;
        struct device *dev = neigh->dev;
        struct in6_addr *target = (struct in6_addr *)&neigh->primary_key;
-       int probes = neigh->probes;
+       int probes = atomic_read(&neigh->probes);
 
        if (skb && ipv6_chk_addr(&skb->nh.ipv6h->saddr, dev, 0))
                saddr = &skb->nh.ipv6h->saddr;
@@ -774,8 +779,8 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh,
        struct sock *sk = ndisc_socket->sk;
        int len = sizeof(struct icmp6hdr) + 2 * sizeof(struct in6_addr);
        struct sk_buff *buff;
-       struct inet6_ifaddr *ifp;
        struct icmp6hdr *icmph;
+       struct in6_addr saddr_buf;
        struct in6_addr *addrp;
        struct device *dev;
        struct rt6_info *rt;
@@ -817,12 +822,10 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh,
        rd_len &= ~0x7;
        len += rd_len;
 
-       ifp = ipv6_get_lladdr(dev);
-
-       if (ifp == NULL) {
-               ND_PRINTK1("redirect: no link_local addr for dev\n");
-               return;
-       }
+       if (ipv6_get_lladdr(dev, &saddr_buf)) {
+               ND_PRINTK1("redirect: no link_local addr for dev\n");
+               return;
+       }
 
        buff = sock_alloc_send_skb(sk, MAX_HEADER + len + dev->hard_header_len + 15,
                                   0, 0, &err);
@@ -838,7 +841,7 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh,
                return;
        }
 
-       ip6_nd_hdr(sk, buff, dev, &ifp->addr, &skb->nh.ipv6h->saddr,
+       ip6_nd_hdr(sk, buff, dev, &saddr_buf, &skb->nh.ipv6h->saddr,
                   IPPROTO_ICMPV6, len);
 
        icmph = (struct icmp6hdr *) skb_put(buff, len);
@@ -875,7 +878,7 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh,
 
        memcpy(opt, skb->nh.ipv6h, rd_len - 8);
 
-       icmph->icmp6_cksum = csum_ipv6_magic(&ifp->addr, &skb->nh.ipv6h->saddr,
+       icmph->icmp6_cksum = csum_ipv6_magic(&saddr_buf, &skb->nh.ipv6h->saddr,
                                             len, IPPROTO_ICMPV6,
                                             csum_partial((u8 *) icmph, len, 0));
 
@@ -1034,7 +1037,7 @@ int ndisc_rcv(struct sk_buff *skb, unsigned long len)
                                   ifp->idev->dev->name);
                        return 0;
                }
-               neigh = __neigh_lookup(&nd_tbl, &msg->target, skb->dev, 0);
+               neigh = neigh_lookup(&nd_tbl, &msg->target, skb->dev);
 
                if (neigh) {
                        if (neigh->flags & NTF_ROUTER) {
@@ -1083,11 +1086,10 @@ int ndisc_get_info(char *buffer, char **start, off_t offset, int length, int dum
        unsigned long now = jiffies;
        int i;
 
-       neigh_table_lock(&nd_tbl);
-
        for (i = 0; i <= NEIGH_HASHMASK; i++) {
                struct neighbour *neigh;
 
+               read_lock_bh(&nd_tbl.lock);
                for (neigh = nd_tbl.hash_buckets[i]; neigh; neigh = neigh->next) {
                        int j;
 
@@ -1097,6 +1099,7 @@ int ndisc_get_info(char *buffer, char **start, off_t offset, int length, int dum
                                size += 2;
                        }
 
+                       read_lock(&neigh->lock);
                        size += sprintf(buffer+len+size,
                                       " %02x %02x %02x %02x %08lx %08lx %08x %04x %04x %04x %8s ", i,
                                       128,
@@ -1118,19 +1121,22 @@ int ndisc_get_info(char *buffer, char **start, off_t offset, int length, int dum
                        } else {
                                 size += sprintf(buffer+len+size, "000000000000");
                        }
+                       read_unlock(&neigh->lock);
                        size += sprintf(buffer+len+size, "\n");
                        len += size;
                        pos += size;
                  
                        if (pos <= offset)
                                len=0;
-                       if (pos >= offset+length)
+                       if (pos >= offset+length) {
+                               read_unlock_bh(&nd_tbl.lock);
                                goto done;
+                       }
                }
+               read_unlock_bh(&nd_tbl.lock);
        }
 
 done:
-       neigh_table_unlock(&nd_tbl);
 
        *start = buffer+len-(pos-offset);       /* Start of wanted data */
        len = pos-offset;                       /* Start slop */
index 136e5806c5a812ecacd86703431fb54cb35192c3..70394dc039b200b86210f91716eb6380bdfdee77 100644 (file)
@@ -7,7 +7,7 @@
  *
  *     Adapted from linux/net/ipv4/raw.c
  *
- *     $Id: raw.c,v 1.25 1999/05/27 00:38:16 davem Exp $
+ *     $Id: raw.c,v 1.26 1999/06/09 10:11:18 davem Exp $
  *
  *     This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -99,17 +99,6 @@ static void raw_v6_rehash(struct sock *sk)
        SOCKHASH_UNLOCK_WRITE();
 }
 
-static __inline__ int inet6_mc_check(struct sock *sk, struct in6_addr *addr)
-{
-       struct ipv6_mc_socklist *mc;
-               
-       for (mc = sk->net_pinfo.af_inet6.ipv6_mc_list; mc; mc=mc->next) {
-               if (ipv6_addr_cmp(&mc->addr, addr) == 0)
-                       return 1;
-       }
-
-       return 0;
-}
 
 /* Grumble... icmp and ip_input want to get at this... */
 struct sock *raw_v6_lookup(struct sock *sk, unsigned short num,
index dba43ae65822745d933419389f9cfd03d7952027..74cf4571b780af1dc9e20188306dbede7fc413e0 100644 (file)
@@ -5,7 +5,7 @@
  *     Authors:
  *     Pedro Roque             <roque@di.fc.ul.pt>     
  *
- *     $Id: reassembly.c,v 1.11 1998/08/26 12:05:16 davem Exp $
+ *     $Id: reassembly.c,v 1.13 1999/06/09 08:29:40 davem Exp $
  *
  *     Based on: net/ipv4/ip_fragment.c
  *
index 04b49d843497fa38b16eb8023890a1ae085e8007..5f8ff914b21cb1066578fe8135e619125cb5c230 100644 (file)
@@ -5,7 +5,7 @@
  *     Authors:
  *     Pedro Roque             <roque@di.fc.ul.pt>     
  *
- *     $Id: route.c,v 1.35 1999/03/21 05:22:57 davem Exp $
+ *     $Id: route.c,v 1.36 1999/06/09 10:11:21 davem Exp $
  *
  *     This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -1607,7 +1607,7 @@ static int fib6_dump_node(struct fib6_walker_t *w)
        return 0;
 }
 
-static int fib6_dump_done(struct netlink_callback *cb)
+static void fib6_dump_end(struct netlink_callback *cb)
 {
        struct fib6_walker_t *w = (void*)cb->args[0];
 
@@ -1622,6 +1622,11 @@ static int fib6_dump_done(struct netlink_callback *cb)
                cb->done = (void*)cb->args[1];
                cb->args[1] = 0;
        }
+}
+
+static int fib6_dump_done(struct netlink_callback *cb)
+{
+       fib6_dump_end(cb);
        return cb->done(cb);
 }
 
@@ -1668,11 +1673,15 @@ int inet6_dump_fib(struct sk_buff *skb, struct netlink_callback *cb)
        if (res <= 0 && skb->len == 0)
                RT6_TRACE("%p>dump end\n", w);
 #endif
+       res = res < 0 ? res : skb->len;
        /* res < 0 is an error. (really, impossible)
           res == 0 means that dump is complete, but skb still can contain data.
           res > 0 dump is not complete, but frame is full.
         */
-       return res < 0 ? res : skb->len;
+       /* Destroy walker, if dump of this table is complete. */
+       if (res <= 0)
+               fib6_dump_end(cb);
+       return res;
 }
 
 int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void *arg)
index a00b5a4d7b9f9057d5bf22240892da4f45a5324c..2164e245e547d8090acd503249812ed57601d81b 100644 (file)
@@ -5,7 +5,7 @@
  *     Authors:
  *     Pedro Roque             <roque@di.fc.ul.pt>     
  *
- *     $Id: tcp_ipv6.c,v 1.106 1999/05/27 01:12:44 davem Exp $
+ *     $Id: tcp_ipv6.c,v 1.108 1999/06/09 08:29:43 davem Exp $
  *
  *     Based on: 
  *     linux/net/ipv4/tcp.c
index 7928e8ca65f970b77c0e4c19a135c7b898d34e6f..da020d8fbee6f85cf23e34a3b58476a4643b98b4 100644 (file)
@@ -7,7 +7,7 @@
  *
  *     Based on linux/ipv4/udp.c
  *
- *     $Id: udp.c,v 1.41 1999/05/27 00:38:18 davem Exp $
+ *     $Id: udp.c,v 1.42 1999/06/09 10:11:24 davem Exp $
  *
  *     This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -502,18 +502,6 @@ static inline int udpv6_queue_rcv_skb(struct sock * sk, struct sk_buff *skb)
        return 0;
 }
 
-static __inline__ int inet6_mc_check(struct sock *sk, struct in6_addr *addr)
-{
-       struct ipv6_mc_socklist *mc;
-               
-       for (mc = sk->net_pinfo.af_inet6.ipv6_mc_list; mc; mc=mc->next) {
-               if (ipv6_addr_cmp(&mc->addr, addr) == 0)
-                       return 1;
-       }
-
-       return 0;
-}
-
 static struct sock *udp_v6_mcast_next(struct sock *sk,
                                      u16 loc_port, struct in6_addr *loc_addr,
                                      u16 rmt_port, struct in6_addr *rmt_addr,
index 4041967db4ba050e27435b04cf4b8a72de5254c3..fa2167a46c6f78147c38761094a62451c68fd7b5 100644 (file)
@@ -564,13 +564,13 @@ struct device *nr_dev_first(void)
 {
        struct device *dev, *first = NULL;
 
-       read_lock_bh(&dev_base_lock);
+       read_lock(&dev_base_lock);
        for (dev = dev_base; dev != NULL; dev = dev->next) {
                if ((dev->flags & IFF_UP) && dev->type == ARPHRD_NETROM)
                        if (first == NULL || strncmp(dev->name, first->name, 3) < 0)
                                first = dev;
        }
-       read_unlock_bh(&dev_base_lock);
+       read_unlock(&dev_base_lock);
 
        return first;
 }
@@ -582,13 +582,13 @@ struct device *nr_dev_get(ax25_address *addr)
 {
        struct device *dev;
 
-       read_lock_bh(&dev_base_lock);
+       read_lock(&dev_base_lock);
        for (dev = dev_base; dev != NULL; dev = dev->next) {
                if ((dev->flags & IFF_UP) && dev->type == ARPHRD_NETROM && ax25cmp(addr, (ax25_address *)dev->dev_addr) == 0)
                        goto out;
        }
 out:
-       read_unlock_bh(&dev_base_lock);
+       read_unlock(&dev_base_lock);
        return dev;
 }
 
index 28e20da124a12493f3e19c0385ffea4e536978aa..021e17ced6b03c91dca250066d08496366711daf 100644 (file)
@@ -170,7 +170,6 @@ EXPORT_SYMBOL(sk_run_filter);
 
 EXPORT_SYMBOL(neigh_table_init);
 EXPORT_SYMBOL(neigh_table_clear);
-EXPORT_SYMBOL(__neigh_lookup);
 EXPORT_SYMBOL(neigh_resolve_output);
 EXPORT_SYMBOL(neigh_connected_output);
 EXPORT_SYMBOL(neigh_update);
@@ -388,8 +387,7 @@ EXPORT_SYMBOL(neigh_dump_info);
 EXPORT_SYMBOL(dev_set_allmulti);
 EXPORT_SYMBOL(dev_set_promiscuity);
 EXPORT_SYMBOL(sklist_remove_socket);
-EXPORT_SYMBOL(rtnl_wait);
-EXPORT_SYMBOL(rtnl_rlockct);
+EXPORT_SYMBOL(rtnl_sem);
 EXPORT_SYMBOL(rtnl_lock);
 EXPORT_SYMBOL(rtnl_unlock);
 
index e78e41352f7ad68835f7b89a25f392676335abe3..fbfe95482656c717dba5dacc335edfe8f867036f 100644 (file)
@@ -5,7 +5,7 @@
  *
  *             PACKET - implements raw packet sockets.
  *
- * Version:    $Id: af_packet.c,v 1.19 1999/03/21 05:23:03 davem Exp $
+ * Version:    $Id: af_packet.c,v 1.20 1999/06/09 10:11:32 davem Exp $
  *
  * Authors:    Ross Biro, <bir7@leland.Stanford.Edu>
  *             Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -286,26 +286,27 @@ static int packet_sendmsg_spkt(struct socket *sock, struct msghdr *msg, int len,
        else
                return(-ENOTCONN);      /* SOCK_PACKET must be sent giving an address */
 
+       dev_lock_list();
+
        /*
         *      Find the device first to size check it 
         */
 
        saddr->spkt_device[13] = 0;
        dev = dev_get(saddr->spkt_device);
-       if (dev == NULL) 
-       {
-               return(-ENODEV);
-       }
+       err = -ENODEV;
+       if (dev == NULL)
+               goto out_unlock;
        
        /*
         *      You may not queue a frame bigger than the mtu. This is the lowest level
         *      raw protocol and you must do your own fragmentation at this level.
         */
         
-       if(len>dev->mtu+dev->hard_header_len)
-               return -EMSGSIZE;
+       err = -EMSGSIZE;
+       if(len>dev->mtu+dev->hard_header_len)
+               goto out_unlock;
 
-       dev_lock_list();
        err = -ENOBUFS;
        skb = sock_wmalloc(sk, len+dev->hard_header_len+15, 0, GFP_KERNEL);
 
@@ -351,8 +352,8 @@ static int packet_sendmsg_spkt(struct socket *sock, struct msghdr *msg, int len,
         *      Now send it
         */
 
-       dev_unlock_list();
        dev_queue_xmit(skb);
+       dev_unlock_list();
        return(len);
 
 out_free:
@@ -455,16 +456,18 @@ static int packet_sendmsg(struct socket *sock, struct msghdr *msg, int len,
                addr    = saddr->sll_addr;
        }
 
+       dev_lock_list();
        dev = dev_get_by_index(ifindex);
+       err = -ENXIO;
        if (dev == NULL)
-               return -ENXIO;
+               goto out_unlock;
        if (sock->type == SOCK_RAW)
                reserve = dev->hard_header_len;
 
+       err = -EMSGSIZE;
        if (len > dev->mtu+reserve)
-               return -EMSGSIZE;
+               goto out_unlock;
 
-       dev_lock_list();
 
        skb = sock_alloc_send_skb(sk, len+dev->hard_header_len+15, 0, 
                                msg->msg_flags & MSG_DONTWAIT, &err);
@@ -501,8 +504,8 @@ static int packet_sendmsg(struct socket *sock, struct msghdr *msg, int len,
         *      Now send it
         */
 
-       dev_unlock_list();
        dev_queue_xmit(skb);
+       dev_unlock_list();
        return(len);
 
 out_free:
index 668015246466a5b4d54ec0da279b94459cb321ab..28a6f8adbcb3f221563a3808aede5f79763ff80e 100644 (file)
@@ -543,13 +543,13 @@ struct device *rose_dev_first(void)
 {
        struct device *dev, *first = NULL;
 
-       read_lock_bh(&dev_base_lock);
+       read_lock(&dev_base_lock);
        for (dev = dev_base; dev != NULL; dev = dev->next) {
                if ((dev->flags & IFF_UP) && dev->type == ARPHRD_ROSE)
                        if (first == NULL || strncmp(dev->name, first->name, 3) < 0)
                                first = dev;
        }
-       read_unlock_bh(&dev_base_lock);
+       read_unlock(&dev_base_lock);
 
        return first;
 }
@@ -561,13 +561,13 @@ struct device *rose_dev_get(rose_address *addr)
 {
        struct device *dev;
 
-       read_lock_bh(&dev_base_lock);
+       read_lock(&dev_base_lock);
        for (dev = dev_base; dev != NULL; dev = dev->next) {
                if ((dev->flags & IFF_UP) && dev->type == ARPHRD_ROSE && rosecmp(addr, (rose_address *)dev->dev_addr) == 0)
                        goto out;
        }
 out:
-       read_unlock_bh(&dev_base_lock);
+       read_unlock(&dev_base_lock);
        return dev;
 }
 
index 6830631370113686272eb94ff08e480981e15787..9d2a95ea6316a20f6ec2d206fb073ee1d7ed9d48 100644 (file)
 
 static struct tcf_proto_ops *tcf_proto_base;
 
+/* Protects list of registered TC modules. It is pure SMP lock. */
+static rwlock_t cls_mod_lock = RW_LOCK_UNLOCKED;
 
 /* Find classifier type by string name */
 
 struct tcf_proto_ops * tcf_proto_lookup_ops(struct rtattr *kind)
 {
-       struct tcf_proto_ops *t;
+       struct tcf_proto_ops *t = NULL;
 
        if (kind) {
+               read_lock(&cls_mod_lock);
                for (t = tcf_proto_base; t; t = t->next) {
                        if (rtattr_strcmp(kind, t->kind) == 0)
-                               return t;
+                               break;
                }
+               read_unlock(&cls_mod_lock);
        }
-       return NULL;
+       return t;
 }
 
 /* Register(unregister) new classifier type */
@@ -61,12 +65,17 @@ int register_tcf_proto_ops(struct tcf_proto_ops *ops)
 {
        struct tcf_proto_ops *t, **tp;
 
-       for (tp = &tcf_proto_base; (t=*tp) != NULL; tp = &t->next)
-               if (strcmp(ops->kind, t->kind) == 0)
+       write_lock(&cls_mod_lock);
+       for (tp = &tcf_proto_base; (t=*tp) != NULL; tp = &t->next) {
+               if (strcmp(ops->kind, t->kind) == 0) {
+                       write_unlock(&cls_mod_lock);
                        return -EEXIST;
+               }
+       }
 
        ops->next = NULL;
        *tp = ops;
+       write_unlock(&cls_mod_lock);
        return 0;
 }
 
@@ -74,13 +83,17 @@ int unregister_tcf_proto_ops(struct tcf_proto_ops *ops)
 {
        struct tcf_proto_ops *t, **tp;
 
+       write_lock(&cls_mod_lock);
        for (tp = &tcf_proto_base; (t=*tp) != NULL; tp = &t->next)
                if (t == ops)
                        break;
 
-       if (!t)
+       if (!t) {
+               write_unlock(&cls_mod_lock);
                return -ENOENT;
+       }
        *tp = t->next;
+       write_unlock(&cls_mod_lock);
        return 0;
 }
 
@@ -217,8 +230,12 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n, void *arg)
                        kfree(tp);
                        goto errout;
                }
+               write_lock(&qdisc_tree_lock);
+               spin_lock_bh(&dev->queue_lock);
                tp->next = *back;
                *back = tp;
+               spin_unlock_bh(&dev->queue_lock);
+               write_unlock(&qdisc_tree_lock);
        } else if (tca[TCA_KIND-1] && rtattr_strcmp(tca[TCA_KIND-1], tp->ops->kind))
                goto errout;
 
@@ -226,8 +243,11 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n, void *arg)
 
        if (fh == 0) {
                if (n->nlmsg_type == RTM_DELTFILTER && t->tcm_handle == 0) {
+                       write_lock(&qdisc_tree_lock);
+                       spin_lock_bh(&dev->queue_lock);
                        *back = tp->next;
-                       synchronize_bh();
+                       spin_unlock_bh(&dev->queue_lock);
+                       write_unlock(&qdisc_tree_lock);
 
                        tp->ops->destroy(tp);
                        kfree(tp);
@@ -344,12 +364,16 @@ static int tc_dump_tfilter(struct sk_buff *skb, struct netlink_callback *cb)
                return skb->len;
        if ((dev = dev_get_by_index(tcm->tcm_ifindex)) == NULL)
                return skb->len;
+
+       read_lock(&qdisc_tree_lock);
        if (!tcm->tcm_parent)
                q = dev->qdisc_sleeping;
        else
                q = qdisc_lookup(dev, TC_H_MAJ(tcm->tcm_parent));
-       if (q == NULL)
+       if (q == NULL) {
+               read_unlock(&qdisc_tree_lock);
                return skb->len;
+       }
        if ((cops = q->ops->cl_ops) == NULL)
                goto errout;
        if (TC_H_MIN(tcm->tcm_parent)) {
@@ -400,6 +424,7 @@ errout:
        if (cl)
                cops->put(q, cl);
 
+       read_unlock(&qdisc_tree_lock);
        return skb->len;
 }
 
index e92b846ee08572fb66e718e43594f51d7289bcbd..598867187589a389f9521d6f6cec8db0f1e6d231 100644 (file)
@@ -136,7 +136,7 @@ static void fw_destroy(struct tcf_proto *tp)
                        unsigned long cl;
                        head->ht[h] = f->next;
 
-                       if ((cl = cls_set_class(&f->res.class, 0)) != 0)
+                       if ((cl = __cls_set_class(&f->res.class, 0)) != 0)
                                tp->q->ops->cl_ops->unbind_tcf(tp->q, cl);
 #ifdef CONFIG_NET_CLS_POLICE
                        tcf_police_release(f->police);
@@ -161,10 +161,11 @@ static int fw_delete(struct tcf_proto *tp, unsigned long arg)
                if (*fp == f) {
                        unsigned long cl;
 
+                       tcf_tree_lock(tp);
                        *fp = f->next;
-                       synchronize_bh();
+                       tcf_tree_unlock(tp);
 
-                       if ((cl = cls_set_class(&f->res.class, 0)) != 0)
+                       if ((cl = cls_set_class(tp, &f->res.class, 0)) != 0)
                                tp->q->ops->cl_ops->unbind_tcf(tp->q, cl);
 #ifdef CONFIG_NET_CLS_POLICE
                        tcf_police_release(f->police);
@@ -203,7 +204,7 @@ static int fw_change(struct tcf_proto *tp, unsigned long base,
 
                        f->res.classid = *(u32*)RTA_DATA(tb[TCA_FW_CLASSID-1]);
                        cl = tp->q->ops->cl_ops->bind_tcf(tp->q, base, f->res.classid);
-                       cl = cls_set_class(&f->res.class, cl);
+                       cl = cls_set_class(tp, &f->res.class, cl);
                        if (cl)
                                tp->q->ops->cl_ops->unbind_tcf(tp->q, cl);
                }
@@ -211,8 +212,9 @@ static int fw_change(struct tcf_proto *tp, unsigned long base,
                if (tb[TCA_FW_POLICE-1]) {
                        struct tcf_police *police = tcf_police_locate(tb[TCA_FW_POLICE-1], tca[TCA_RATE-1]);
 
+                       tcf_tree_lock(tp);
                        police = xchg(&f->police, police);
-                       synchronize_bh();
+                       tcf_tree_unlock(tp);
 
                        tcf_police_release(police);
                }
@@ -229,8 +231,9 @@ static int fw_change(struct tcf_proto *tp, unsigned long base,
                        return -ENOBUFS;
                memset(head, 0, sizeof(*head));
 
+               tcf_tree_lock(tp);
                tp->root = head;
-               synchronize_bh();
+               tcf_tree_unlock(tp);
        }
 
        f = kmalloc(sizeof(struct fw_filter), GFP_KERNEL);
@@ -245,7 +248,7 @@ static int fw_change(struct tcf_proto *tp, unsigned long base,
                if (RTA_PAYLOAD(tb[TCA_FW_CLASSID-1]) != 4)
                        goto errout;
                f->res.classid = *(u32*)RTA_DATA(tb[TCA_FW_CLASSID-1]);
-               cls_set_class(&f->res.class, tp->q->ops->cl_ops->bind_tcf(tp->q, base, f->res.classid));
+               cls_set_class(tp, &f->res.class, tp->q->ops->cl_ops->bind_tcf(tp->q, base, f->res.classid));
        }
 
 #ifdef CONFIG_NET_CLS_POLICE
@@ -254,8 +257,9 @@ static int fw_change(struct tcf_proto *tp, unsigned long base,
 #endif
 
        f->next = head->ht[fw_hash(handle)];
-       wmb();
+       tcf_tree_lock(tp);
        head->ht[fw_hash(handle)] = f;
+       tcf_tree_unlock(tp);
 
        *arg = (unsigned long)f;
        return 0;
@@ -335,7 +339,8 @@ static int fw_dump(struct tcf_proto *tp, unsigned long fh,
        rta->rta_len = skb->tail - b;
 #ifdef CONFIG_NET_CLS_POLICE
        if (f->police) {
-               RTA_PUT(skb, TCA_STATS, sizeof(struct tc_stats), &f->police->stats);
+               if (qdisc_copy_stats(skb, &f->police->stats))
+                       goto rtattr_failure;
        }
 #endif
        return skb->len;
index f83e79134fd6d366ca073f3412fd41f52c2789ce..cb0d21d0f89622f1a35a3c62611263d882381b27 100644 (file)
@@ -83,11 +83,11 @@ static __inline__ int route4_fastmap_hash(u32 id, int iif)
        return id&0xF;
 }
 
-static void route4_reset_fastmap(struct route4_head *head, u32 id)
+static void route4_reset_fastmap(struct device *dev, struct route4_head *head, u32 id)
 {
-       start_bh_atomic();
+       spin_lock_bh(&dev->queue_lock);
        memset(head->fastmap, 0, sizeof(head->fastmap));
-       end_bh_atomic();
+       spin_unlock_bh(&dev->queue_lock);
 }
 
 static void __inline__
@@ -297,7 +297,7 @@ static void route4_destroy(struct tcf_proto *tp)
                                        unsigned long cl;
 
                                        b->ht[h2] = f->next;
-                                       if ((cl = cls_set_class(&f->res.class, 0)) != 0)
+                                       if ((cl = __cls_set_class(&f->res.class, 0)) != 0)
                                                tp->q->ops->cl_ops->unbind_tcf(tp->q, cl);
 #ifdef CONFIG_NET_CLS_POLICE
                                        tcf_police_release(f->police);
@@ -329,12 +329,13 @@ static int route4_delete(struct tcf_proto *tp, unsigned long arg)
                if (*fp == f) {
                        unsigned long cl;
 
+                       tcf_tree_lock(tp);
                        *fp = f->next;
-                       synchronize_bh();
+                       tcf_tree_unlock(tp);
 
-                       route4_reset_fastmap(head, f->id);
+                       route4_reset_fastmap(tp->q->dev, head, f->id);
 
-                       if ((cl = cls_set_class(&f->res.class, 0)) != 0)
+                       if ((cl = cls_set_class(tp, &f->res.class, 0)) != 0)
                                tp->q->ops->cl_ops->unbind_tcf(tp->q, cl);
 
 #ifdef CONFIG_NET_CLS_POLICE
@@ -349,8 +350,9 @@ static int route4_delete(struct tcf_proto *tp, unsigned long arg)
                                        return 0;
 
                        /* OK, session has no flows */
+                       tcf_tree_lock(tp);
                        head->table[to_hash(h)] = NULL;
-                       synchronize_bh();
+                       tcf_tree_unlock(tp);
 
                        kfree(b);
                        return 0;
@@ -387,7 +389,7 @@ static int route4_change(struct tcf_proto *tp, unsigned long base,
                        unsigned long cl;
 
                        f->res.classid = *(u32*)RTA_DATA(tb[TCA_ROUTE4_CLASSID-1]);
-                       cl = cls_set_class(&f->res.class, tp->q->ops->cl_ops->bind_tcf(tp->q, base, f->res.classid));
+                       cl = cls_set_class(tp, &f->res.class, tp->q->ops->cl_ops->bind_tcf(tp->q, base, f->res.classid));
                        if (cl)
                                tp->q->ops->cl_ops->unbind_tcf(tp->q, cl);
                }
@@ -395,8 +397,9 @@ static int route4_change(struct tcf_proto *tp, unsigned long base,
                if (tb[TCA_ROUTE4_POLICE-1]) {
                        struct tcf_police *police = tcf_police_locate(tb[TCA_ROUTE4_POLICE-1], tca[TCA_RATE-1]);
 
+                       tcf_tree_lock(tp);
                        police = xchg(&f->police, police);
-                       synchronize_bh();
+                       tcf_tree_unlock(tp);
 
                        tcf_police_release(police);
                }
@@ -412,8 +415,9 @@ static int route4_change(struct tcf_proto *tp, unsigned long base,
                        return -ENOBUFS;
                memset(head, 0, sizeof(struct route4_head));
 
+               tcf_tree_lock(tp);
                tp->root = head;
-               synchronize_bh();
+               tcf_tree_unlock(tp);
        }
 
        f = kmalloc(sizeof(struct route4_filter), GFP_KERNEL);
@@ -475,8 +479,9 @@ static int route4_change(struct tcf_proto *tp, unsigned long base,
                        goto errout;
                memset(b, 0, sizeof(*b));
 
+               tcf_tree_lock(tp);
                head->table[h1] = b;
-               synchronize_bh();
+               tcf_tree_unlock(tp);
        }
        f->bkt = b;
 
@@ -489,17 +494,18 @@ static int route4_change(struct tcf_proto *tp, unsigned long base,
                        goto errout;
        }
 
-       cls_set_class(&f->res.class, tp->q->ops->cl_ops->bind_tcf(tp->q, base, f->res.classid));
+       cls_set_class(tp, &f->res.class, tp->q->ops->cl_ops->bind_tcf(tp->q, base, f->res.classid));
 #ifdef CONFIG_NET_CLS_POLICE
        if (tb[TCA_ROUTE4_POLICE-1])
                f->police = tcf_police_locate(tb[TCA_ROUTE4_POLICE-1], tca[TCA_RATE-1]);
 #endif
 
        f->next = f1;
-       wmb();
+       tcf_tree_lock(tp);
        *ins_f = f;
+       tcf_tree_unlock(tp);
 
-       route4_reset_fastmap(head, f->id);
+       route4_reset_fastmap(tp->q->dev, head, f->id);
        *arg = (unsigned long)f;
        return 0;
 
@@ -589,7 +595,8 @@ static int route4_dump(struct tcf_proto *tp, unsigned long fh,
        rta->rta_len = skb->tail - b;
 #ifdef CONFIG_NET_CLS_POLICE
        if (f->police) {
-               RTA_PUT(skb, TCA_STATS, sizeof(struct tc_stats), &f->police->stats);
+               if (qdisc_copy_stats(skb, &f->police->stats))
+                       goto rtattr_failure;
        }
 #endif
        return skb->len;
index 48142c6e7f69bb665c5d0266a00e2542fa896806..be4471d78c5b364bc81b61337223bed7bb83b3be 100644 (file)
@@ -282,7 +282,7 @@ static void rsvp_destroy(struct tcf_proto *tp)
                                        unsigned long cl;
 
                                        s->ht[h2] = f->next;
-                                       if ((cl = cls_set_class(&f->res.class, 0)) != 0)
+                                       if ((cl = __cls_set_class(&f->res.class, 0)) != 0)
                                                tp->q->ops->cl_ops->unbind_tcf(tp->q, cl);
 #ifdef CONFIG_NET_CLS_POLICE
                                        tcf_police_release(f->police);
@@ -310,10 +310,11 @@ static int rsvp_delete(struct tcf_proto *tp, unsigned long arg)
                        unsigned long cl;
 
 
+                       tcf_tree_lock(tp);
                        *fp = f->next;
-                       synchronize_bh();
+                       tcf_tree_unlock(tp);
 
-                       if ((cl = cls_set_class(&f->res.class, 0)) != 0)
+                       if ((cl = cls_set_class(tp, &f->res.class, 0)) != 0)
                                tp->q->ops->cl_ops->unbind_tcf(tp->q, cl);
 
 #ifdef CONFIG_NET_CLS_POLICE
@@ -332,8 +333,9 @@ static int rsvp_delete(struct tcf_proto *tp, unsigned long arg)
                        for (sp = &((struct rsvp_head*)tp->root)->ht[h&0xFF];
                             *sp; sp = &(*sp)->next) {
                                if (*sp == s) {
+                                       tcf_tree_lock(tp);
                                        *sp = s->next;
-                                       synchronize_bh();
+                                       tcf_tree_unlock(tp);
 
                                        kfree(s);
                                        return 0;
@@ -446,7 +448,7 @@ static int rsvp_change(struct tcf_proto *tp, unsigned long base,
                        unsigned long cl;
 
                        f->res.classid = *(u32*)RTA_DATA(tb[TCA_RSVP_CLASSID-1]);
-                       cl = cls_set_class(&f->res.class, tp->q->ops->cl_ops->bind_tcf(tp->q, base, f->res.classid));
+                       cl = cls_set_class(tp, &f->res.class, tp->q->ops->cl_ops->bind_tcf(tp->q, base, f->res.classid));
                        if (cl)
                                tp->q->ops->cl_ops->unbind_tcf(tp->q, cl);
                }
@@ -454,8 +456,9 @@ static int rsvp_change(struct tcf_proto *tp, unsigned long base,
                if (tb[TCA_RSVP_POLICE-1]) {
                        struct tcf_police *police = tcf_police_locate(tb[TCA_RSVP_POLICE-1], tca[TCA_RATE-1]);
 
+                       tcf_tree_lock(tp);
                        police = xchg(&f->police, police);
-                       synchronize_bh();
+                       tcf_tree_unlock(tp);
 
                        tcf_police_release(police);
                }
@@ -536,7 +539,7 @@ insert:
 
                        f->sess = s;
                        if (f->tunnelhdr == 0)
-                               cls_set_class(&f->res.class, tp->q->ops->cl_ops->bind_tcf(tp->q, base, f->res.classid));
+                               cls_set_class(tp, &f->res.class, tp->q->ops->cl_ops->bind_tcf(tp->q, base, f->res.classid));
 #ifdef CONFIG_NET_CLS_POLICE
                        if (tb[TCA_RSVP_POLICE-1])
                                f->police = tcf_police_locate(tb[TCA_RSVP_POLICE-1], tca[TCA_RATE-1]);
@@ -659,7 +662,8 @@ static int rsvp_dump(struct tcf_proto *tp, unsigned long fh,
        rta->rta_len = skb->tail - b;
 #ifdef CONFIG_NET_CLS_POLICE
        if (f->police) {
-               RTA_PUT(skb, TCA_STATS, sizeof(struct tc_stats), &f->police->stats);
+               if (qdisc_copy_stats(skb, &f->police->stats))
+                       goto rtattr_failure;
        }
 #endif
        return skb->len;
index 98d4e1f7b42f3f2c537e4c53e4dbe22de6740f50..f759d150ad7691531dab7a39783c32b275908d93 100644 (file)
@@ -307,7 +307,7 @@ static int u32_destroy_key(struct tcf_proto *tp, struct tc_u_knode *n)
 {
        unsigned long cl;
 
-       if ((cl = cls_set_class(&n->res.class, 0)) != 0)
+       if ((cl = __cls_set_class(&n->res.class, 0)) != 0)
                tp->q->ops->cl_ops->unbind_tcf(tp->q, cl);
 #ifdef CONFIG_NET_CLS_POLICE
        tcf_police_release(n->police);
@@ -326,8 +326,9 @@ static int u32_delete_key(struct tcf_proto *tp, struct tc_u_knode* key)
        if (ht) {
                for (kp = &ht->ht[TC_U32_HASH(key->handle)]; *kp; kp = &(*kp)->next) {
                        if (*kp == key) {
+                               tcf_tree_lock(tp);
                                *kp = key->next;
-                               synchronize_bh();
+                               tcf_tree_unlock(tp);
 
                                u32_destroy_key(tp, key);
                                return 0;
@@ -346,7 +347,6 @@ static void u32_clear_hnode(struct tcf_proto *tp, struct tc_u_hnode *ht)
        for (h=0; h<=ht->divisor; h++) {
                while ((n = ht->ht[h]) != NULL) {
                        ht->ht[h] = n->next;
-                       synchronize_bh();
 
                        u32_destroy_key(tp, n);
                }
@@ -465,8 +465,9 @@ static int u32_set_parms(struct Qdisc *q, unsigned long base,
                        ht_down->refcnt++;
                }
 
+               sch_tree_lock(q);
                ht_down = xchg(&n->ht_down, ht_down);
-               synchronize_bh();
+               sch_tree_unlock(q);
 
                if (ht_down)
                        ht_down->refcnt--;
@@ -475,7 +476,9 @@ static int u32_set_parms(struct Qdisc *q, unsigned long base,
                unsigned long cl;
 
                n->res.classid = *(u32*)RTA_DATA(tb[TCA_U32_CLASSID-1]);
-               cl = cls_set_class(&n->res.class, q->ops->cl_ops->bind_tcf(q, base, n->res.classid));
+               sch_tree_lock(q);
+               cl = __cls_set_class(&n->res.class, q->ops->cl_ops->bind_tcf(q, base, n->res.classid));
+               sch_tree_unlock(q);
                if (cl)
                        q->ops->cl_ops->unbind_tcf(q, cl);
        }
@@ -483,8 +486,9 @@ static int u32_set_parms(struct Qdisc *q, unsigned long base,
        if (tb[TCA_U32_POLICE-1]) {
                struct tcf_police *police = tcf_police_locate(tb[TCA_U32_POLICE-1], est);
 
+               sch_tree_lock(q);
                police = xchg(&n->police, police);
-               synchronize_bh();
+               sch_tree_lock(q);
 
                tcf_police_release(police);
        }
@@ -682,7 +686,8 @@ static int u32_dump(struct tcf_proto *tp, unsigned long fh,
        rta->rta_len = skb->tail - b;
 #ifdef CONFIG_NET_CLS_POLICE
        if (TC_U32_KEY(n->handle) && n->police) {
-               RTA_PUT(skb, TCA_STATS, sizeof(struct tc_stats), &n->police->stats);
+               if (qdisc_copy_stats(skb, &n->police->stats))
+                       goto rtattr_failure;
        }
 #endif
        return skb->len;
index d51017c844446249eca6968bd2b30dce60679ce9..e70066f9c1bbf31f722c1cdfd0a8b2e2d4431dc2 100644 (file)
@@ -97,29 +97,38 @@ struct qdisc_estimator_head
 
 static struct qdisc_estimator_head elist[EST_MAX_INTERVAL+1];
 
+/* Estimator array lock */
+static rwlock_t est_lock = RW_LOCK_UNLOCKED;
+
 static void est_timer(unsigned long arg)
 {
        int idx = (int)arg;
        struct qdisc_estimator *e;
 
+       read_lock(&est_lock);
        for (e = elist[idx].list; e; e = e->next) {
-               u64 nbytes = e->stats->bytes;
-               u32 npackets = e->stats->packets;
+               struct tc_stats *st = e->stats;
+               u64 nbytes;
+               u32 npackets;
                u32 rate;
-               
+
+               spin_lock(st->lock);
+               nbytes = st->bytes;
+               npackets = st->packets;
                rate = (nbytes - e->last_bytes)<<(7 - idx);
                e->last_bytes = nbytes;
                e->avbps += ((long)rate - (long)e->avbps) >> e->ewma_log;
-               e->stats->bps = (e->avbps+0xF)>>5;
+               st->bps = (e->avbps+0xF)>>5;
 
                rate = (npackets - e->last_packets)<<(12 - idx);
                e->last_packets = npackets;
                e->avpps += ((long)rate - (long)e->avpps) >> e->ewma_log;
                e->stats->pps = (e->avpps+0x1FF)>>10;
+               spin_unlock(st->lock);
        }
 
-       elist[idx].timer.expires = jiffies + ((HZ/4)<<idx);
-       add_timer(&elist[idx].timer);
+       mod_timer(&elist[idx].timer, jiffies + ((HZ/4)<<idx));
+       read_unlock(&est_lock);
 }
 
 int qdisc_new_estimator(struct tc_stats *stats, struct rtattr *opt)
@@ -154,7 +163,9 @@ int qdisc_new_estimator(struct tc_stats *stats, struct rtattr *opt)
                elist[est->interval].timer.function = est_timer;
                add_timer(&elist[est->interval].timer);
        }
+       write_lock_bh(&est_lock);
        elist[est->interval].list = est;
+       write_unlock_bh(&est_lock);
        return 0;
 }
 
@@ -172,8 +183,9 @@ void qdisc_kill_estimator(struct tc_stats *stats)
                                continue;
                        }
 
+                       write_lock_bh(&est_lock);
                        *pest = est->next;
-                       synchronize_bh();
+                       write_unlock_bh(&est_lock);
 
                        kfree(est);
                        killed++;
index 89e58d8be1248d24f30cc7b937530ce4414d06a9..f81f89aed272b8ed1febbd4188475ba7c0c28d28 100644 (file)
 
 static u32 idx_gen;
 static struct tcf_police *tcf_police_ht[16];
+/* Policer hash table lock */
+static rwlock_t police_lock = RW_LOCK_UNLOCKED;
+
+/* Each policer is serialized by its individual spinlock */
 
 static __inline__ unsigned tcf_police_hash(u32 index)
 {
@@ -48,11 +52,13 @@ static __inline__ struct tcf_police * tcf_police_lookup(u32 index)
 {
        struct tcf_police *p;
 
+       read_lock(&police_lock);
        for (p = tcf_police_ht[tcf_police_hash(index)]; p; p = p->next) {
                if (p->index == index)
-                       return p;
+                       break;
        }
-       return NULL;
+       read_unlock(&police_lock);
+       return p;
 }
 
 static __inline__ u32 tcf_police_new_index(void)
@@ -73,7 +79,9 @@ void tcf_police_destroy(struct tcf_police *p)
        
        for (p1p = &tcf_police_ht[h]; *p1p; p1p = &(*p1p)->next) {
                if (*p1p == p) {
+                       write_lock_bh(&police_lock);
                        *p1p = p->next;
+                       write_unlock_bh(&police_lock);
 #ifdef CONFIG_NET_ESTIMATOR
                        qdisc_kill_estimator(&p->stats);
 #endif
@@ -114,6 +122,8 @@ struct tcf_police * tcf_police_locate(struct rtattr *rta, struct rtattr *est)
 
        memset(p, 0, sizeof(*p));
        p->refcnt = 1;
+       spin_lock_init(&p->lock);
+       p->stats.lock = &p->lock;
        if (parm->rate.rate) {
                if ((p->R_tab = qdisc_get_rtab(&parm->rate, tb[TCA_POLICE_RATE-1])) == NULL)
                        goto failure;
@@ -144,8 +154,10 @@ struct tcf_police * tcf_police_locate(struct rtattr *rta, struct rtattr *est)
                qdisc_new_estimator(&p->stats, est);
 #endif
        h = tcf_police_hash(p->index);
+       write_lock_bh(&police_lock);
        p->next = tcf_police_ht[h];
        tcf_police_ht[h] = p;
+       write_unlock_bh(&police_lock);
        return p;
 
 failure:
@@ -161,19 +173,24 @@ int tcf_police(struct sk_buff *skb, struct tcf_police *p)
        long toks;
        long ptoks = 0;
 
+       spin_lock(&p->lock);
+
        p->stats.bytes += skb->len;
        p->stats.packets++;
 
 #ifdef CONFIG_NET_ESTIMATOR
        if (p->ewma_rate && p->stats.bps >= p->ewma_rate) {
                p->stats.overlimits++;
+               spin_unlock(&p->lock);
                return p->action;
        }
 #endif
 
        if (skb->len <= p->mtu) {
-               if (p->R_tab == NULL)
+               if (p->R_tab == NULL) {
+                       spin_unlock(&p->lock);
                        return p->result;
+               }
 
                PSCHED_GET_TIME(now);
 
@@ -194,11 +211,13 @@ int tcf_police(struct sk_buff *skb, struct tcf_police *p)
                        p->t_c = now;
                        p->toks = toks;
                        p->ptoks = ptoks;
+                       spin_unlock(&p->lock);
                        return p->result;
                }
        }
 
        p->stats.overlimits++;
+       spin_unlock(&p->lock);
        return p->action;
 }
 
index bb1bc418298a4c86b6f75ad61fdd13d161ff7114..fec6faefe009e3241c66cbb240892ad5e8fb4426 100644 (file)
@@ -124,6 +124,10 @@ static int tclass_notify(struct sk_buff *oskb, struct nlmsghdr *n,
    changes qdisc parameters.
  */
 
+/* Protects list of registered TC modules. It is pure SMP lock. */
+static rwlock_t qdisc_mod_lock = RW_LOCK_UNLOCKED;
+
+
 /************************************************
  *     Queueing disciplines manipulation.      *
  ************************************************/
@@ -139,9 +143,13 @@ int register_qdisc(struct Qdisc_ops *qops)
 {
        struct Qdisc_ops *q, **qp;
 
-       for (qp = &qdisc_base; (q=*qp)!=NULL; qp = &q->next)
-               if (strcmp(qops->id, q->id) == 0)
+       write_lock(&qdisc_mod_lock);
+       for (qp = &qdisc_base; (q=*qp)!=NULL; qp = &q->next) {
+               if (strcmp(qops->id, q->id) == 0) {
+                       write_unlock(&qdisc_mod_lock);
                        return -EEXIST;
+               }
+       }
 
        if (qops->enqueue == NULL)
                qops->enqueue = noop_qdisc_ops.enqueue;
@@ -152,20 +160,26 @@ int register_qdisc(struct Qdisc_ops *qops)
 
        qops->next = NULL;
        *qp = qops;
+       write_unlock(&qdisc_mod_lock);
        return 0;
 }
 
 int unregister_qdisc(struct Qdisc_ops *qops)
 {
        struct Qdisc_ops *q, **qp;
+       int err = -ENOENT;
+
+       write_lock(&qdisc_mod_lock);
        for (qp = &qdisc_base; (q=*qp)!=NULL; qp = &q->next)
                if (q == qops)
                        break;
-       if (!q)
-               return -ENOENT;
-       *qp = q->next;
-       q->next = NULL;
-       return 0;
+       if (q) {
+               *qp = q->next;
+               q->next = NULL;
+               err = 0;
+       }
+       write_unlock(&qdisc_mod_lock);
+       return err;
 }
 
 /* We know handle. Find qdisc among all qdisc's attached to device
@@ -203,15 +217,17 @@ struct Qdisc *qdisc_leaf(struct Qdisc *p, u32 classid)
 
 struct Qdisc_ops *qdisc_lookup_ops(struct rtattr *kind)
 {
-       struct Qdisc_ops *q;
+       struct Qdisc_ops *q = NULL;
 
        if (kind) {
+               read_lock(&qdisc_mod_lock);
                for (q = qdisc_base; q; q = q->next) {
                        if (rtattr_strcmp(kind, q->id) == 0)
-                               return q;
+                               break;
                }
+               read_unlock(&qdisc_mod_lock);
        }
-       return NULL;
+       return q;
 }
 
 static struct qdisc_rate_table *qdisc_rtab_list;
@@ -284,7 +300,8 @@ dev_graft_qdisc(struct device *dev, struct Qdisc *qdisc)
        if (dev->flags & IFF_UP)
                dev_deactivate(dev);
 
-       start_bh_atomic();
+       write_lock(&qdisc_tree_lock);
+       spin_lock_bh(&dev->queue_lock);
        oqdisc = dev->qdisc_sleeping;
 
        /* Prune old scheduler */
@@ -296,7 +313,8 @@ dev_graft_qdisc(struct device *dev, struct Qdisc *qdisc)
                qdisc = &noop_qdisc;
        dev->qdisc_sleeping = qdisc;
        dev->qdisc = &noop_qdisc;
-       end_bh_atomic();
+       spin_unlock_bh(&dev->queue_lock);
+       write_unlock(&qdisc_tree_lock);
 
        if (dev->flags & IFF_UP)
                dev_activate(dev);
@@ -376,7 +394,7 @@ qdisc_create(struct device *dev, u32 handle, struct rtattr **tca, int *errp)
                goto err_out;
 
        /* Grrr... Resolve race condition with module unload */
-       
+
        err = -EINVAL;
        if (ops != qdisc_lookup_ops(kind))
                goto err_out;
@@ -389,6 +407,7 @@ qdisc_create(struct device *dev, u32 handle, struct rtattr **tca, int *errp)
        sch->dequeue = ops->dequeue;
        sch->dev = dev;
        atomic_set(&sch->refcnt, 1);
+       sch->stats.lock = &dev->queue_lock;
        if (handle == 0) {
                handle = qdisc_alloc_handle(dev);
                err = -ENOMEM;
@@ -398,8 +417,10 @@ qdisc_create(struct device *dev, u32 handle, struct rtattr **tca, int *errp)
        sch->handle = handle;
 
        if (!ops->init || (err = ops->init(sch, tca[TCA_OPTIONS-1])) == 0) {
+               write_lock(&qdisc_tree_lock);
                sch->next = dev->qdisc_list;
                dev->qdisc_list = sch;
+               write_unlock(&qdisc_tree_lock);
 #ifdef CONFIG_NET_ESTIMATOR
                if (tca[TCA_RATE-1])
                        qdisc_new_estimator(&sch->stats, tca[TCA_RATE-1]);
@@ -521,7 +542,9 @@ static int tc_get_qdisc(struct sk_buff *skb, struct nlmsghdr *n, void *arg)
                        return err;
                if (q) {
                        qdisc_notify(skb, n, clid, q, NULL);
+                       spin_lock_bh(&dev->queue_lock);
                        qdisc_destroy(q);
+                       spin_unlock_bh(&dev->queue_lock);
                }
        } else {
                qdisc_notify(skb, n, clid, NULL, q);
@@ -637,17 +660,36 @@ graft:
                struct Qdisc *old_q = NULL;
                err = qdisc_graft(dev, p, clid, q, &old_q);
                if (err) {
-                       if (q)
+                       if (q) {
+                               spin_lock_bh(&dev->queue_lock);
                                qdisc_destroy(q);
+                               spin_unlock_bh(&dev->queue_lock);
+                       }
                        return err;
                }
                qdisc_notify(skb, n, clid, old_q, q);
-               if (old_q)
+               if (old_q) {
+                       spin_lock_bh(&dev->queue_lock);
                        qdisc_destroy(old_q);
+                       spin_unlock_bh(&dev->queue_lock);
+               }
        }
        return 0;
 }
 
+int qdisc_copy_stats(struct sk_buff *skb, struct tc_stats *st)
+{
+       spin_lock_bh(st->lock);
+       RTA_PUT(skb, TCA_STATS, (char*)&st->lock - (char*)st, st);
+       spin_unlock_bh(st->lock);
+       return 0;
+
+rtattr_failure:
+       spin_unlock_bh(st->lock);
+       return -1;
+}
+
+
 static int tc_fill_qdisc(struct sk_buff *skb, struct Qdisc *q, u32 clid,
                         u32 pid, u32 seq, unsigned flags, int event)
 {
@@ -667,7 +709,8 @@ static int tc_fill_qdisc(struct sk_buff *skb, struct Qdisc *q, u32 clid,
        if (q->ops->dump && q->ops->dump(q, skb) < 0)
                goto rtattr_failure;
        q->stats.qlen = q->q.qlen;
-       RTA_PUT(skb, TCA_STATS, sizeof(q->stats), &q->stats);
+       if (qdisc_copy_stats(skb, &q->stats))
+               goto rtattr_failure;
        nlh->nlmsg_len = skb->tail - b;
        return skb->len;
 
@@ -713,24 +756,28 @@ static int tc_dump_qdisc(struct sk_buff *skb, struct netlink_callback *cb)
 
        s_idx = cb->args[0];
        s_q_idx = q_idx = cb->args[1];
-       read_lock_bh(&dev_base_lock);
+       read_lock(&dev_base_lock);
        for (dev=dev_base, idx=0; dev; dev = dev->next, idx++) {
                if (idx < s_idx)
                        continue;
                if (idx > s_idx)
                        s_q_idx = 0;
+               read_lock(&qdisc_tree_lock);
                for (q = dev->qdisc_list, q_idx = 0; q;
                     q = q->next, q_idx++) {
                        if (q_idx < s_q_idx)
                                continue;
                        if (tc_fill_qdisc(skb, q, 0, NETLINK_CB(cb->skb).pid,
-                                         cb->nlh->nlmsg_seq, NLM_F_MULTI, RTM_NEWQDISC) <= 0)
+                                         cb->nlh->nlmsg_seq, NLM_F_MULTI, RTM_NEWQDISC) <= 0) {
+                               read_unlock(&qdisc_tree_lock);
                                goto done;
+                       }
                }
+               read_unlock(&qdisc_tree_lock);
        }
 
 done:
-       read_unlock_bh(&dev_base_lock);
+       read_unlock(&dev_base_lock);
 
        cb->args[0] = idx;
        cb->args[1] = q_idx;
@@ -936,6 +983,7 @@ static int tc_dump_tclass(struct sk_buff *skb, struct netlink_callback *cb)
 
        s_t = cb->args[0];
 
+       read_lock(&qdisc_tree_lock);
        for (q=dev->qdisc_list, t=0; q; q = q->next, t++) {
                if (t < s_t) continue;
                if (!q->ops->cl_ops) continue;
@@ -954,6 +1002,7 @@ static int tc_dump_tclass(struct sk_buff *skb, struct netlink_callback *cb)
                if (arg.w.stop)
                        break;
        }
+       read_unlock(&qdisc_tree_lock);
 
        cb->args[0] = t;
 
index c8094a882641ea9976158e1a80b625121faafd94..2244b68edd15c38e694e8e2e6937521cb89f9c44 100644 (file)
@@ -1417,6 +1417,7 @@ static int cbq_init(struct Qdisc *sch, struct rtattr *opt)
        q->link.ewma_log = TC_CBQ_DEF_EWMA;
        q->link.avpkt = q->link.allot/2;
        q->link.minidle = -0x7FFFFFFF;
+       q->link.stats.lock = &sch->dev->queue_lock;
 
        init_timer(&q->wd_timer);
        q->wd_timer.data = (unsigned long)sch;
@@ -1558,6 +1559,16 @@ static int cbq_dump_attr(struct sk_buff *skb, struct cbq_class *cl)
        return 0;
 }
 
+int cbq_copy_xstats(struct sk_buff *skb, struct tc_cbq_xstats *st)
+{
+       RTA_PUT(skb, TCA_STATS, sizeof(*st), st);
+       return 0;
+
+rtattr_failure:
+       return -1;
+}
+
+
 static int cbq_dump(struct Qdisc *sch, struct sk_buff *skb)
 {
        struct cbq_sched_data *q = (struct cbq_sched_data*)sch->data;
@@ -1569,8 +1580,13 @@ static int cbq_dump(struct Qdisc *sch, struct sk_buff *skb)
        if (cbq_dump_attr(skb, &q->link) < 0)
                goto rtattr_failure;
        rta->rta_len = skb->tail - b;
+       spin_lock_bh(&sch->dev->queue_lock);
        q->link.xstats.avgidle = q->link.avgidle;
-       RTA_PUT(skb, TCA_XSTATS, sizeof(q->link.xstats), &q->link.xstats);
+       if (cbq_copy_xstats(skb, &q->link.xstats)) {
+               spin_unlock_bh(&sch->dev->queue_lock);
+               goto rtattr_failure;
+       }
+       spin_unlock_bh(&sch->dev->queue_lock);
        return skb->len;
 
 rtattr_failure:
@@ -1600,12 +1616,19 @@ cbq_dump_class(struct Qdisc *sch, unsigned long arg,
                goto rtattr_failure;
        rta->rta_len = skb->tail - b;
        cl->stats.qlen = cl->q->q.qlen;
-       RTA_PUT(skb, TCA_STATS, sizeof(cl->stats), &cl->stats);
+       if (qdisc_copy_stats(skb, &cl->stats))
+               goto rtattr_failure;
+       spin_lock_bh(&sch->dev->queue_lock);
        cl->xstats.avgidle = cl->avgidle;
        cl->xstats.undertime = 0;
        if (!PSCHED_IS_PASTPERFECT(cl->undertime))
                cl->xstats.undertime = PSCHED_TDIFF(cl->undertime, q->now);
-       RTA_PUT(skb, TCA_XSTATS, sizeof(cl->xstats), &cl->xstats);
+       q->link.xstats.avgidle = q->link.avgidle;
+       if (cbq_copy_xstats(skb, &cl->xstats)) {
+               spin_unlock_bh(&sch->dev->queue_lock);
+               goto rtattr_failure;
+       }
+       spin_unlock_bh(&sch->dev->queue_lock);
 
        return skb->len;
 
@@ -1631,8 +1654,11 @@ static int cbq_graft(struct Qdisc *sch, unsigned long arg, struct Qdisc *new,
                                new->reshape_fail = cbq_reshape_fail;
 #endif
                }
-               if ((*old = xchg(&cl->q, new)) != NULL)
-                       qdisc_reset(*old);
+               sch_tree_lock(sch);
+               *old = cl->q;
+               cl->q = new;
+               qdisc_reset(*old);
+               sch_tree_unlock(sch);
 
                return 0;
        }
@@ -1710,16 +1736,16 @@ static void cbq_put(struct Qdisc *sch, unsigned long arg)
        struct cbq_sched_data *q = (struct cbq_sched_data *)sch->data;
        struct cbq_class *cl = (struct cbq_class*)arg;
 
-       start_bh_atomic();
        if (--cl->refcnt == 0) {
 #ifdef CONFIG_NET_CLS_POLICE
+               spin_lock_bh(&sch->dev->queue_lock);
                if (q->rx_class == cl)
                        q->rx_class = NULL;
+               spin_unlock_bh(&sch->dev->queue_lock);
 #endif
+
                cbq_destroy_class(cl);
        }
-       end_bh_atomic();
-       return;
 }
 
 static int
@@ -1780,7 +1806,7 @@ cbq_change_class(struct Qdisc *sch, u32 classid, u32 parentid, struct rtattr **t
                }
 
                /* Change class parameters */
-               start_bh_atomic();
+               sch_tree_lock(sch);
 
                if (cl->next_alive != NULL)
                        cbq_deactivate_class(cl);
@@ -1812,7 +1838,7 @@ cbq_change_class(struct Qdisc *sch, u32 classid, u32 parentid, struct rtattr **t
                if (cl->q->q.qlen)
                        cbq_activate_class(cl);
 
-               end_bh_atomic();
+               sch_tree_lock(sch);
 
 #ifdef CONFIG_NET_ESTIMATOR
                if (tca[TCA_RATE-1]) {
@@ -1878,8 +1904,9 @@ cbq_change_class(struct Qdisc *sch, u32 classid, u32 parentid, struct rtattr **t
        cl->allot = parent->allot;
        cl->quantum = cl->allot;
        cl->weight = cl->R_tab->rate.rate;
+       cl->stats.lock = &sch->dev->queue_lock;
 
-       start_bh_atomic();
+       sch_tree_lock(sch);
        cbq_link_class(cl);
        cl->borrow = cl->tparent;
        if (cl->tparent != &q->link)
@@ -1903,7 +1930,7 @@ cbq_change_class(struct Qdisc *sch, u32 classid, u32 parentid, struct rtattr **t
 #endif
        if (tb[TCA_CBQ_FOPT-1])
                cbq_set_fopt(cl, RTA_DATA(tb[TCA_CBQ_FOPT-1]));
-       end_bh_atomic();
+       sch_tree_unlock(sch);
 
 #ifdef CONFIG_NET_ESTIMATOR
        if (tca[TCA_RATE-1])
@@ -1926,7 +1953,7 @@ static int cbq_delete(struct Qdisc *sch, unsigned long arg)
        if (cl->filters || cl->children || cl == &q->link)
                return -EBUSY;
 
-       start_bh_atomic();
+       sch_tree_lock(sch);
 
        if (cl->next_alive)
                cbq_deactivate_class(cl);
@@ -1948,12 +1975,11 @@ static int cbq_delete(struct Qdisc *sch, unsigned long arg)
        cbq_sync_defmap(cl);
 
        cbq_rmprio(q, cl);
+       sch_tree_unlock(sch);
 
        if (--cl->refcnt == 0)
                cbq_destroy_class(cl);
 
-       end_bh_atomic();
-
        return 0;
 }
 
index 2202fd81afa2963676b6c1e33bc65b2c0a096c01..c1be3729e987f27f5567b800db7847825dc7a2aa 100644 (file)
@@ -885,7 +885,7 @@ static int csz_change(struct Qdisc *sch, u32 handle, u32 parent, struct rtattr *
 
                a = &q->flow[cl];
 
-               start_bh_atomic();      
+               spin_lock_bh(&sch->dev->queue_lock);
 #if 0
                a->rate_log = copt->rate_log;
 #endif
@@ -899,7 +899,7 @@ static int csz_change(struct Qdisc *sch, u32 handle, u32 parent, struct rtattr *
                if (tb[TCA_CSZ_RTAB-1])
                        memcpy(a->L_tab, RTA_DATA(tb[TCA_CSZ_RTAB-1]), 1024);
 
-               end_bh_atomic();
+               spin_unlock_bh(&sch->dev->queue_lock);
                return 0;
        }
        /* NI */
@@ -920,14 +920,14 @@ static int csz_delete(struct Qdisc *sch, unsigned long cl)
 
        a = &q->flow[cl];
 
-       start_bh_atomic();
+       spin_lock_bh(&sch->dev->queue_lock);
        a->fprev->fnext = a->fnext;
        a->fnext->fprev = a->fprev;
        a->sprev->snext = a->snext;
        a->snext->sprev = a->sprev;
        a->start = a->finish = 0;
        kfree(xchg(&q->flow[cl].L_tab, NULL));
-       end_bh_atomic();
+       spin_unlock_bh(&sch->dev->queue_lock);
 
        return 0;
 }
index 9d199cbdc9a33c86b4f8f843f087b966fc69aaeb..2dc1ed327372a6b89dc62c01afe799a326b0409f 100644 (file)
 
 /* Main transmission queue. */
 
-struct Qdisc_head qdisc_head = { &qdisc_head };
+struct Qdisc_head qdisc_head = { &qdisc_head, &qdisc_head };
+spinlock_t qdisc_runqueue_lock = SPIN_LOCK_UNLOCKED;
+
+/* Main qdisc structure lock. 
+
+   However, modifications
+   to data, participating in scheduling must be additionally
+   protected with dev->queue_lock spinlock.
+
+   The idea is the following:
+   - enqueue, dequeue are serialized via top level device
+     spinlock dev->queue_lock.
+   - tree walking is protected by read_lock(qdisc_tree_lock)
+     and this lock is used only in process context.
+   - updates to tree are made only under rtnl semaphore,
+     hence this lock may be made without local bh disabling.
+
+   qdisc_tree_lock must be grabbed BEFORE dev->queue_lock!
+ */
+rwlock_t qdisc_tree_lock = RW_LOCK_UNLOCKED;
+
+/* Anti deadlock rules:
+
+   qdisc_runqueue_lock protects main transmission list qdisc_head.
+   Run list is accessed only under this spinlock.
+
+   dev->queue_lock serializes queue accesses for this device
+   AND dev->qdisc pointer itself.
+
+   dev->xmit_lock serializes accesses to device driver.
+
+   dev->queue_lock and dev->xmit_lock are mutually exclusive,
+   if one is grabbed, another must be free.
+
+   qdisc_runqueue_lock may be requested under dev->queue_lock,
+   but neither dev->queue_lock nor dev->xmit_lock may be requested
+   under qdisc_runqueue_lock.
+ */
+
 
 /* Kick device.
    Note, that this procedure can be called by a watchdog timer, so that
@@ -44,7 +82,7 @@ struct Qdisc_head qdisc_head = { &qdisc_head };
             >0  - queue is not empty, but throttled.
            <0  - queue is not empty. Device is throttled, if dev->tbusy != 0.
 
-   NOTE: Called only from NET BH
+   NOTE: Called under dev->queue_lock with locally disabled BH.
 */
 
 int qdisc_restart(struct device *dev)
@@ -53,27 +91,97 @@ int qdisc_restart(struct device *dev)
        struct sk_buff *skb;
 
        if ((skb = q->dequeue(q)) != NULL) {
+               /* Dequeue packet and release queue */
+               spin_unlock(&dev->queue_lock);
+
                if (netdev_nit)
                        dev_queue_xmit_nit(skb, dev);
 
-               if (dev->hard_start_xmit(skb, dev) == 0) {
-                       q->tx_last = jiffies;
-                       return -1;
+               if (spin_trylock(&dev->xmit_lock)) {
+                       /* Remember that the driver is grabbed by us. */
+                       dev->xmit_lock_owner = smp_processor_id();
+                       if (dev->hard_start_xmit(skb, dev) == 0) {
+                               dev->xmit_lock_owner = -1;
+                               spin_unlock(&dev->xmit_lock);
+
+                               spin_lock(&dev->queue_lock);
+                               dev->qdisc->tx_last = jiffies;
+                               return -1;
+                       }
+                       /* Release the driver */
+                       dev->xmit_lock_owner = -1;
+                       spin_unlock(&dev->xmit_lock);
+               } else {
+                       /* So, someone grabbed the driver. */
+
+                       /* It may be transient configuration error,
+                          when hard_start_xmit() recurses. We detect
+                          it by checking xmit owner and drop the
+                          packet when deadloop is detected.
+                        */
+                       if (dev->xmit_lock_owner == smp_processor_id()) {
+                               kfree_skb(skb);
+                               if (net_ratelimit())
+                                       printk(KERN_DEBUG "Dead loop on virtual %s, fix it urgently!\n", dev->name);
+                               spin_lock(&dev->queue_lock);
+                               return -1;
+                       }
+
+                       /* Otherwise, packet is requeued
+                          and will be sent by the next net_bh run.
+                        */
+                       mark_bh(NET_BH);
                }
 
                /* Device kicked us out :(
                   This is possible in three cases:
 
+                  0. driver is locked
                   1. fastroute is enabled
                   2. device cannot determine busy state
                      before start of transmission (f.e. dialout)
                   3. device is buggy (ppp)
                 */
 
+               spin_lock(&dev->queue_lock);
+               q = dev->qdisc;
                q->ops->requeue(skb, q);
                return -1;
        }
-       return q->q.qlen;
+       return dev->qdisc->q.qlen;
+}
+
+static __inline__ void
+qdisc_stop_run(struct Qdisc *q)
+{
+       q->h.forw->back = q->h.back;
+       q->h.back->forw = q->h.forw;
+       q->h.forw = NULL;
+}
+
+extern __inline__ void
+qdisc_continue_run(struct Qdisc *q)
+{
+       if (!qdisc_on_runqueue(q) && q->dev) {
+               q->h.forw = &qdisc_head;
+               q->h.back = qdisc_head.back;
+               qdisc_head.back->forw = &q->h;
+               qdisc_head.back = &q->h;
+       }
+}
+
+static __inline__ int
+qdisc_init_run(struct Qdisc_head *lh)
+{
+       if (qdisc_head.forw != &qdisc_head) {
+               *lh = qdisc_head;
+               lh->forw->back = lh;
+               lh->back->forw = lh;
+               qdisc_head.forw = &qdisc_head;
+               qdisc_head.back = &qdisc_head;
+               return 1;
+       }
+       return 0;
 }
 
 /* Scan transmission queue and kick devices.
@@ -84,63 +192,90 @@ int qdisc_restart(struct device *dev)
    I have no idea how to solve it using only "anonymous" Linux mark_bh().
 
    To change queue from device interrupt? Ough... only not this...
+
+   This function is called only from net_bh.
  */
 
 void qdisc_run_queues(void)
 {
-       struct Qdisc_head **hp, *h;
+       struct Qdisc_head lh, *h;
 
-       hp = &qdisc_head.forw;
-       while ((h = *hp) != &qdisc_head) {
-               int res = -1;
+       spin_lock(&qdisc_runqueue_lock);
+       if (!qdisc_init_run(&lh))
+               goto out;
+
+       while ((h = lh.forw) != &lh) {
+               int res;
+               struct device *dev;
                struct Qdisc *q = (struct Qdisc*)h;
-               struct device *dev = q->dev;
-
-               spin_lock_bh(&dev->xmit_lock);
-               while (!dev->tbusy && (res = qdisc_restart(dev)) < 0)
-                       /* NOTHING */;
-               spin_unlock_bh(&dev->xmit_lock);
-
-               /* An explanation is necessary here.
-                  qdisc_restart called dev->hard_start_xmit,
-                  if device is virtual, it could trigger one more
-                  dev_queue_xmit and a new device could appear
-                  in the active chain. In this case we cannot unlink
-                  the empty queue, because we lost the back pointer.
-                  No problem, we will unlink it during the next round.
-                */
 
-               if (res == 0 && *hp == h) {
-                       *hp = h->forw;
-                       h->forw = NULL;
-                       continue;
+               qdisc_stop_run(q);
+
+               dev = q->dev;
+               spin_unlock(&qdisc_runqueue_lock);
+
+               res = -1;
+               if (spin_trylock(&dev->queue_lock)) {
+                       while (!dev->tbusy && (res = qdisc_restart(dev)) < 0)
+                               /* NOTHING */;
+                       spin_unlock(&dev->queue_lock);
                }
-               hp = &h->forw;
+
+               spin_lock(&qdisc_runqueue_lock);
+               /* If qdisc is not empty add it to the tail of list */
+               if (res)
+                       qdisc_continue_run(q);
        }
+out:
+       spin_unlock(&qdisc_runqueue_lock);
 }
 
-/* Periodic watchdoc timer to recover from hard/soft device bugs. */
+/* Periodic watchdog timer to recover from hard/soft device bugs. */
 
 static void dev_do_watchdog(unsigned long dummy);
 
 static struct timer_list dev_watchdog =
        { NULL, NULL, 0L, 0L, &dev_do_watchdog };
 
+/*   This function is called only from timer */
+
 static void dev_do_watchdog(unsigned long dummy)
 {
-       struct Qdisc_head *h;
+       struct Qdisc_head lh, *h;
+
+       if (!spin_trylock(&qdisc_runqueue_lock)) {
+               /* No hurry with watchdog. */
+               mod_timer(&dev_watchdog, jiffies + HZ/10);
+               return;
+       }
+
+       if (!qdisc_init_run(&lh))
+               goto out;
 
-       for (h = qdisc_head.forw; h != &qdisc_head; h = h->forw) {
+       while ((h = lh.forw) != &lh) {
+               struct device *dev;
                struct Qdisc *q = (struct Qdisc*)h;
-               struct device *dev = q->dev;
 
-               spin_lock_bh(&dev->xmit_lock);
-               if (dev->tbusy && jiffies - q->tx_last > q->tx_timeo)
-                       qdisc_restart(dev);
-               spin_unlock_bh(&dev->xmit_lock);
+               qdisc_stop_run(q);
+
+               dev = q->dev;
+               spin_unlock(&qdisc_runqueue_lock);
+
+               if (spin_trylock(&dev->queue_lock)) {
+                       q = dev->qdisc;
+                       if (dev->tbusy && jiffies - q->tx_last > q->tx_timeo)
+                               qdisc_restart(dev);
+                       spin_unlock(&dev->queue_lock);
+               }
+
+               spin_lock(&qdisc_runqueue_lock);
+
+               qdisc_continue_run(dev->qdisc);
        }
-       dev_watchdog.expires = jiffies + 5*HZ;
-       add_timer(&dev_watchdog);
+
+out:
+       mod_timer(&dev_watchdog, jiffies + 5*HZ);
+       spin_unlock(&qdisc_runqueue_lock);
 }
 
 
@@ -211,7 +346,7 @@ struct Qdisc noqueue_qdisc =
 {
         { NULL }, 
        NULL,
-       NULL,
+       noop_dequeue,
        TCQ_F_BUILTIN,
        &noqueue_qdisc_ops,
 };
@@ -327,6 +462,7 @@ struct Qdisc * qdisc_create_dflt(struct device *dev, struct Qdisc_ops *ops)
        sch->enqueue = ops->enqueue;
        sch->dequeue = ops->dequeue;
        sch->dev = dev;
+       sch->stats.lock = &dev->queue_lock;
        atomic_set(&sch->refcnt, 1);
        if (!ops->init || ops->init(sch, NULL) == 0)
                return sch;
@@ -335,42 +471,45 @@ struct Qdisc * qdisc_create_dflt(struct device *dev, struct Qdisc_ops *ops)
        return NULL;
 }
 
+/* Under dev->queue_lock and BH! */
+
 void qdisc_reset(struct Qdisc *qdisc)
 {
        struct Qdisc_ops *ops = qdisc->ops;
-       start_bh_atomic();
        if (ops->reset)
                ops->reset(qdisc);
-       end_bh_atomic();
 }
 
+/* Under dev->queue_lock and BH! */
+
 void qdisc_destroy(struct Qdisc *qdisc)
 {
        struct Qdisc_ops *ops = qdisc->ops;
+       struct device *dev;
 
        if (!atomic_dec_and_test(&qdisc->refcnt))
                return;
 
+       dev = qdisc->dev;
+
 #ifdef CONFIG_NET_SCHED
-       if (qdisc->dev) {
+       if (dev) {
                struct Qdisc *q, **qp;
-               for (qp = &qdisc->dev->qdisc_list; (q=*qp) != NULL; qp = &q->next)
+               for (qp = &qdisc->dev->qdisc_list; (q=*qp) != NULL; qp = &q->next) {
                        if (q == qdisc) {
                                *qp = q->next;
-                               q->next = NULL;
                                break;
                        }
+               }
        }
 #ifdef CONFIG_NET_ESTIMATOR
        qdisc_kill_estimator(&qdisc->stats);
 #endif
 #endif
-       start_bh_atomic();
        if (ops->reset)
                ops->reset(qdisc);
        if (ops->destroy)
                ops->destroy(qdisc);
-       end_bh_atomic();
        if (!(qdisc->flags&TCQ_F_BUILTIN))
                kfree(qdisc);
 }
@@ -385,19 +524,23 @@ void dev_activate(struct device *dev)
         */
 
        if (dev->qdisc_sleeping == &noop_qdisc) {
+               struct Qdisc *qdisc;
                if (dev->tx_queue_len) {
-                       struct Qdisc *qdisc;
                        qdisc = qdisc_create_dflt(dev, &pfifo_fast_ops);
                        if (qdisc == NULL) {
                                printk(KERN_INFO "%s: activation failed\n", dev->name);
                                return;
                        }
-                       dev->qdisc_sleeping = qdisc;
-               } else
-                       dev->qdisc_sleeping = &noqueue_qdisc;
+               } else {
+                       qdisc =  &noqueue_qdisc;
+               }
+               write_lock(&qdisc_tree_lock);
+               dev->qdisc_sleeping = qdisc;
+               write_unlock(&qdisc_tree_lock);
        }
 
-       start_bh_atomic();
+       spin_lock_bh(&dev->queue_lock);
+       spin_lock(&qdisc_runqueue_lock);
        if ((dev->qdisc = dev->qdisc_sleeping) != &noqueue_qdisc) {
                dev->qdisc->tx_timeo = 5*HZ;
                dev->qdisc->tx_last = jiffies - dev->qdisc->tx_timeo;
@@ -405,51 +548,50 @@ void dev_activate(struct device *dev)
                        dev_watchdog.expires = jiffies + 5*HZ;
                add_timer(&dev_watchdog);
        }
-       end_bh_atomic();
+       spin_unlock(&qdisc_runqueue_lock);
+       spin_unlock_bh(&dev->queue_lock);
 }
 
 void dev_deactivate(struct device *dev)
 {
        struct Qdisc *qdisc;
 
-       start_bh_atomic();
-
-       qdisc = xchg(&dev->qdisc, &noop_qdisc);
+       spin_lock_bh(&dev->queue_lock);
+       qdisc = dev->qdisc;
+       dev->qdisc = &noop_qdisc;
 
        qdisc_reset(qdisc);
 
-       if (qdisc->h.forw) {
-               struct Qdisc_head **hp, *h;
-
-               for (hp = &qdisc_head.forw; (h = *hp) != &qdisc_head; hp = &h->forw) {
-                       if (h == &qdisc->h) {
-                               *hp = h->forw;
-                               break;
-                       }
-               }
-       }
-
-       end_bh_atomic();
+       spin_lock(&qdisc_runqueue_lock);
+       if (qdisc_on_runqueue(qdisc))
+               qdisc_stop_run(qdisc);
+       spin_unlock(&qdisc_runqueue_lock);
+       spin_unlock_bh(&dev->queue_lock);
 }
 
 void dev_init_scheduler(struct device *dev)
 {
+       write_lock(&qdisc_tree_lock);
+       spin_lock_bh(&dev->queue_lock);
        dev->qdisc = &noop_qdisc;
+       spin_unlock_bh(&dev->queue_lock);
        dev->qdisc_sleeping = &noop_qdisc;
        dev->qdisc_list = NULL;
+       write_unlock(&qdisc_tree_lock);
 }
 
 void dev_shutdown(struct device *dev)
 {
        struct Qdisc *qdisc;
 
-       start_bh_atomic();
+       write_lock(&qdisc_tree_lock);
+       spin_lock_bh(&dev->queue_lock);
        qdisc = dev->qdisc_sleeping;
        dev->qdisc = &noop_qdisc;
        dev->qdisc_sleeping = &noop_qdisc;
        qdisc_destroy(qdisc);
        BUG_TRAP(dev->qdisc_list == NULL);
        dev->qdisc_list = NULL;
-       end_bh_atomic();
+       spin_unlock_bh(&dev->queue_lock);
+       write_unlock(&qdisc_tree_lock);
 }
-
index 5222d149dcbf940a2c487737d911bca1384ddebb..a7069dec7f3eca80c2cdfbf493b217afebb5ebf3 100644 (file)
@@ -178,7 +178,7 @@ static int prio_tune(struct Qdisc *sch, struct rtattr *opt)
                        return -EINVAL;
        }
 
-       start_bh_atomic();
+       sch_tree_lock(sch);
        q->bands = qopt->bands;
        memcpy(q->prio2band, qopt->priomap, TC_PRIO_MAX+1);
 
@@ -187,7 +187,7 @@ static int prio_tune(struct Qdisc *sch, struct rtattr *opt)
                if (child != &noop_qdisc)
                        qdisc_destroy(child);
        }
-       end_bh_atomic();
+       sch_tree_unlock(sch);
 
        for (i=0; i<=TC_PRIO_MAX; i++) {
                int band = q->prio2band[i];
@@ -195,11 +195,12 @@ static int prio_tune(struct Qdisc *sch, struct rtattr *opt)
                        struct Qdisc *child;
                        child = qdisc_create_dflt(sch->dev, &pfifo_qdisc_ops);
                        if (child) {
+                               sch_tree_lock(sch);
                                child = xchg(&q->queues[band], child);
-                               synchronize_bh();
 
                                if (child != &noop_qdisc)
                                        qdisc_destroy(child);
+                               sch_tree_unlock(sch);
                        }
                }
        }
@@ -265,7 +266,11 @@ static int prio_graft(struct Qdisc *sch, unsigned long arg, struct Qdisc *new,
        if (new == NULL)
                new = &noop_qdisc;
 
-       *old = xchg(&q->queues[band], new);
+       sch_tree_lock(sch);
+       *old = q->queues[band];
+       q->queues[band] = new;
+       qdisc_reset(*old);
+       sch_tree_unlock(sch);
 
        return 0;
 }
index 8baf254ebf8ac3538eae7a7d6805d89d1a9df1e0..dfde116f3a28534d1f47c9212705fdb8f178e82b 100644 (file)
@@ -387,7 +387,7 @@ static int sfq_change(struct Qdisc *sch, struct rtattr *opt)
        if (opt->rta_len < RTA_LENGTH(sizeof(*ctl)))
                return -EINVAL;
 
-       start_bh_atomic();
+       sch_tree_lock(sch);
        q->quantum = ctl->quantum ? : psched_mtu(sch->dev);
        q->perturb_period = ctl->perturb_period*HZ;
 
@@ -396,7 +396,7 @@ static int sfq_change(struct Qdisc *sch, struct rtattr *opt)
                q->perturb_timer.expires = jiffies + q->perturb_period;
                add_timer(&q->perturb_timer);
        }
-       end_bh_atomic();
+       sch_tree_unlock(sch);
        return 0;
 }
 
index a4d13b62848a5982fc50a91a21d15456f662dd48..90e469b02b919160d0d812ee9d442c9757c3d1a7 100644 (file)
@@ -308,7 +308,7 @@ static int tbf_change(struct Qdisc* sch, struct rtattr *opt)
        if (rtab->data[max_size>>qopt->rate.cell_log] > qopt->buffer)
                goto done;
 
-       start_bh_atomic();
+       sch_tree_lock(sch);
        q->limit = qopt->limit;
        q->mtu = qopt->mtu;
        q->max_size = max_size;
@@ -317,7 +317,7 @@ static int tbf_change(struct Qdisc* sch, struct rtattr *opt)
        q->ptokens = q->mtu;
        rtab = xchg(&q->R_tab, rtab);
        ptab = xchg(&q->P_tab, ptab);
-       end_bh_atomic();
+       sch_tree_unlock(sch);
        err = 0;
 done:
        if (rtab)
index 66040d5e9f4490a58e034c621b060b3970987e6a..ffed0de11d8ba420ec14fcd133b2889f8b50c58b 100644 (file)
@@ -125,9 +125,11 @@ teql_dequeue(struct Qdisc* sch)
        if (skb == NULL) {
                struct device *m = dat->m->dev.qdisc->dev;
                if (m) {
-                       m->tbusy = 0;
                        dat->m->slaves = sch;
+                       spin_lock(&m->queue_lock);
+                       m->tbusy = 0;
                        qdisc_restart(m);
+                       spin_unlock(&m->queue_lock);
                }
        }
        sch->q.qlen = dat->q.qlen + dat->m->dev.qdisc->q.qlen;
@@ -167,7 +169,9 @@ teql_destroy(struct Qdisc* sch)
                                        master->slaves = NEXT_SLAVE(q);
                                        if (q == master->slaves) {
                                                master->slaves = NULL;
+                                               spin_lock_bh(&master->dev.queue_lock);
                                                qdisc_reset(master->dev.qdisc);
+                                               spin_unlock_bh(&master->dev.queue_lock);
                                        }
                                }
                                skb_queue_purge(&dat->q);
@@ -190,6 +194,9 @@ static int teql_qdisc_init(struct Qdisc *sch, struct rtattr *opt)
        if (dev->hard_header_len > m->dev.hard_header_len)
                return -EINVAL;
 
+       if (&m->dev == dev)
+               return -ELOOP;
+
        q->m = m;
 
        skb_queue_head_init(&q->q);
@@ -244,7 +251,11 @@ __teql_resolve(struct sk_buff *skb, struct sk_buff *skb_res, struct device *dev)
                        return -ENOBUFS;
        }
        if (neigh_event_send(n, skb_res) == 0) {
-               if (dev->hard_header(skb, dev, ntohs(skb->protocol), n->ha, NULL, skb->len) < 0) {
+               int err;
+               read_lock(&n->lock);
+               err = dev->hard_header(skb, dev, ntohs(skb->protocol), n->ha, NULL, skb->len);
+               read_unlock(&n->lock);
+               if (err < 0) {
                        neigh_release(n);
                        return -EINVAL;
                }
@@ -295,19 +306,24 @@ restart:
                        continue;
                }
 
-               if (q->h.forw == NULL) {
-                       q->h.forw = qdisc_head.forw;
-                       qdisc_head.forw = &q->h;
-               }
+               if (!qdisc_on_runqueue(q))
+                       qdisc_run(q);
 
                switch (teql_resolve(skb, skb_res, slave)) {
                case 0:
-                       if (slave->hard_start_xmit(skb, slave) == 0) {
-                               master->slaves = NEXT_SLAVE(q);
-                               dev->tbusy = 0;
-                               master->stats.tx_packets++;
-                               master->stats.tx_bytes += len;
+                       if (spin_trylock(&slave->xmit_lock)) {
+                               slave->xmit_lock_owner = smp_processor_id();
+                               if (slave->hard_start_xmit(skb, slave) == 0) {
+                                       slave->xmit_lock_owner = -1;
+                                       spin_unlock(&slave->xmit_lock);
+                                       master->slaves = NEXT_SLAVE(q);
+                                       dev->tbusy = 0;
+                                       master->stats.tx_packets++;
+                                       master->stats.tx_bytes += len;
                                        return 0;
+                               }
+                               slave->xmit_lock_owner = -1;
+                               spin_unlock(&slave->xmit_lock);
                        }
                        if (dev->tbusy)
                                busy = 1;
index 632f994ad89f6d894383935f7bdc013facd72f15..a4f0700230f5a9ccec4bac73e9853c9a9cad81a5 100644 (file)
@@ -1336,7 +1336,7 @@ int init_module(void)
        /*
         *      Register any pre existing devices.
         */
-       read_lock_bh(&dev_base_lock);
+       read_lock(&dev_base_lock);
        for (dev = dev_base; dev != NULL; dev = dev->next) {
                if ((dev->flags & IFF_UP) && (dev->type == ARPHRD_X25
 #if defined(CONFIG_LLC) || defined(CONFIG_LLC_MODULE)
@@ -1345,7 +1345,7 @@ int init_module(void)
                        ))
                        x25_link_device_up(dev);
        }
-       read_unlock_bh(&dev_base_lock);
+       read_unlock(&dev_base_lock);
 
        return 0;
 }