]> git.neil.brown.name Git - history.git/commitdiff
Linux 2.4.0-test9pre6 2.4.0-test9pre6
authorLinus Torvalds <torvalds@linuxfoundation.org>
Fri, 23 Nov 2007 20:39:05 +0000 (15:39 -0500)
committerLinus Torvalds <torvalds@linuxfoundation.org>
Fri, 23 Nov 2007 20:39:05 +0000 (15:39 -0500)
    - TUN/TAP driver: use proper device number (misc device, minor=200).
    - teach st.c about some SCSI tapes that really aren't SCSI tapes (OnStream)
    - samba 2.2 needs leases for efficient file sharing.  They are kind
      of like file locks with async IO notification.
    - broadcast I/O APIC interrupt MP-tables are legal..
    - alpha RTC year magic again..
    - careful memory ordering by Andrea..
    - make the scsi-generic module work properly again.
    - file locking fixes
    - update atp ISA net driver
    - VIA IDE driver bugfixes
    - more linux-2.2 driver sync-ups
    - new PCI ids
    - emu10k stereo sound fix.
    - makefile documentation update
    - USB uhci updates
    - networking updates
    - codafs fixups
    - VM UP deadlock fix
    - Add Camino chipset ID to eepro100 driver.

181 files changed:
Documentation/Configure.help
Documentation/DocBook/Makefile
Documentation/DocBook/kernel-api.tmpl
Documentation/kbuild/makefiles.txt
Documentation/networking/tuntap.txt
MAINTAINERS
Makefile
arch/alpha/Makefile
arch/alpha/kernel/pci_iommu.c
arch/alpha/kernel/smp.c
arch/alpha/kernel/time.c
arch/alpha/math-emu/Makefile
arch/alpha/math-emu/math.c
arch/alpha/math-emu/qrnnd.S [new file with mode: 0644]
arch/alpha/math-emu/sfp-util.h
arch/alpha/mm/extable.c
arch/i386/defconfig
arch/i386/kernel/io_apic.c
arch/i386/kernel/ptrace.c
arch/i386/kernel/smp.c
arch/i386/kernel/traps.c
drivers/acpi/os.c
drivers/block/cciss.c
drivers/block/cciss.h
drivers/block/cpqarray.c
drivers/block/cpqarray.h
drivers/block/ll_rw_blk.c
drivers/block/lvm-snap.c [deleted file]
drivers/block/lvm.c [deleted file]
drivers/block/paride/paride.h
drivers/cdrom/cdrom.c
drivers/char/ftape/lowlevel/ftape-calibr.c
drivers/char/hp600_keyb.c
drivers/char/joystick/pcigame.c
drivers/char/rtc.c
drivers/char/serial.c
drivers/ide/via82cxxx.c
drivers/md/Config.in
drivers/md/lvm-snap.c [new file with mode: 0644]
drivers/md/lvm.c [new file with mode: 0644]
drivers/md/md.c
drivers/mtd/cfi_cmdset_0002.c
drivers/mtd/mapped.c
drivers/net/Config.in
drivers/net/Makefile
drivers/net/Space.c
drivers/net/aironet4500_core.c
drivers/net/appletalk/Makefile
drivers/net/appletalk/ipddp.c
drivers/net/atp.c
drivers/net/atp.h
drivers/net/bonding.c
drivers/net/dummy.c
drivers/net/eepro100.c
drivers/net/eql.c
drivers/net/hamachi.c [new file with mode: 0644]
drivers/net/pppox.c
drivers/net/setup.c
drivers/net/sundance.c [new file with mode: 0644]
drivers/net/tokenring/tms380tr.c
drivers/net/tun.c
drivers/net/winbond-840.c [new file with mode: 0644]
drivers/pci/pci.ids
drivers/scsi/cpqfcTSi2c.c
drivers/scsi/ide-scsi.c
drivers/scsi/ips.h
drivers/scsi/sg.c
drivers/scsi/st.c
drivers/sound/emu10k1/audio.c
drivers/sound/emu10k1/ecard.h
drivers/sound/emu10k1/main.c
drivers/sound/emu10k1/midi.c
drivers/sound/emu10k1/mixer.c
drivers/usb/storage/freecom.c
drivers/usb/storage/transport.c
drivers/usb/storage/usb.c
drivers/usb/usb-ohci.c
drivers/usb/usb-uhci.c
drivers/usb/usb.c
fs/Makefile
fs/attr.c
fs/buffer.c
fs/coda/cache.c
fs/coda/cnode.c
fs/coda/inode.c
fs/dnotify.c [new file with mode: 0644]
fs/fcntl.c
fs/lockd/clntlock.c
fs/lockd/clntproc.c
fs/lockd/svclock.c
fs/lockd/svcshare.c
fs/lockd/xdr.c
fs/lockd/xdr4.c
fs/locks.c
fs/namei.c
fs/nfsd/export.c
fs/nfsd/nfs3xdr.c
fs/nfsd/nfsxdr.c
fs/nfsd/vfs.c
fs/open.c
fs/partitions/check.c
fs/read_write.c
include/asm-alpha/atomic.h
include/asm-alpha/bitops.h
include/asm-alpha/elf.h
include/asm-alpha/semaphore-helper.h
include/asm-alpha/spinlock.h
include/asm-alpha/system.h
include/asm-alpha/termios.h
include/asm-arm/fcntl.h
include/asm-arm/resource.h
include/asm-i386/atomic.h
include/asm-i386/bitops.h
include/asm-i386/fcntl.h
include/asm-i386/resource.h
include/asm-i386/rwlock.h
include/asm-i386/spinlock.h
include/asm-i386/system.h
include/asm-ia64/fcntl.h
include/asm-ia64/resource.h
include/asm-m68k/fcntl.h
include/asm-m68k/resource.h
include/asm-mips/fcntl.h
include/asm-mips/resource.h
include/asm-mips64/fcntl.h
include/asm-mips64/resource.h
include/asm-ppc/fcntl.h
include/asm-ppc/resource.h
include/asm-s390/fcntl.h
include/asm-s390/resource.h
include/asm-sh/fcntl.h
include/asm-sh/resource.h
include/asm-sparc/fcntl.h
include/asm-sparc/resource.h
include/asm-sparc64/fcntl.h
include/asm-sparc64/resource.h
include/asm-sparc64/system.h
include/linux/adfs_fs.h
include/linux/atalk.h
include/linux/brlock.h
include/linux/capability.h
include/linux/devpts_fs.h
include/linux/dnotify.h [new file with mode: 0644]
include/linux/fcntl.h
include/linux/fs.h
include/linux/gameport.h
include/linux/hdlcdrv.h
include/linux/hfs_fs.h
include/linux/hfs_sysdep.h
include/linux/highmem.h
include/linux/if_pppox.h
include/linux/kernel.h
include/linux/locks.h
include/linux/major.h
include/linux/miscdevice.h
include/linux/mm.h
include/linux/nfsd/export.h
include/linux/pci.h
include/linux/pci_ids.h
include/linux/sched.h
include/linux/swap.h
include/linux/sysctl.h
include/linux/timex.h
include/linux/tqueue.h
include/net/snmp.h
include/scsi/sg.h
init/main.c
kernel/ksyms.c
kernel/signal.c
kernel/softirq.c
kernel/sysctl.c
mm/filemap.c
mm/page_alloc.c
mm/swap.c
mm/vmscan.c
net/core/dev.c
net/ipv4/tcp_input.c
net/ipv4/udp.c
net/socket.c
net/sunrpc/clnt.c
scripts/Configure

index b7c7d6438f2dc7f41fd09e07c1b51cd91ef0e7f5..96521960e97ce332e177eef3a3fc4a163fac631e 100644 (file)
@@ -8159,8 +8159,10 @@ CONFIG_NI65
   module, say M here and read Documentation/modules.txt as well as
   Documentation/networking/net-modules.txt.
 
-RealTek 8129 (not 8019/8029!) support (EXPERIMENTAL)
+RealTek 8129 (not 8019/8029/8139!) support (EXPERIMENTAL)
 CONFIG_RTL8129
+  This is NOT for RTL-8139 cards.  Instead, select the 8139too driver
+  (CONFIG_8139TOO).
   This is a driver for the Fast Ethernet PCI network cards based on
   the RTL8129 chip. If you have one of those, say Y and
   read the Ethernet-HOWTO, available from
@@ -8694,6 +8696,18 @@ CONFIG_EEXPRESS
   Documentation/networking/net-modules.txt. The module will be called
   eexpress.o.
 
+Packet Engines Hamachi GNIC-II support
+CONFIG_HAMACHI
+  If you have a Gigabit Ethernet card of this type, say Y and read
+  the Ethernet-HOWTO, available from
+  http://www.linuxdoc.org/docs.html#howto .
+
+  If you want to compile this as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want),
+  say M here and read Documentation/modules.txt as well as
+  Documentation/networking/net-modules.txt. The module will be called
+  hamachi.o.
+
 HP PCLAN+ (27247B and 27252A) support
 CONFIG_HPLAN_PLUS
   If you have a network (Ethernet) card of this type, say Y and read
@@ -9017,23 +9031,31 @@ CONFIG_ES3210
   module, say M here and read Documentation/modules.txt as well as
   Documentation/networking/net-modules.txt.
 
-SMC EtherPower II (EXPERIMENTAL)
+SMC EtherPower II
 CONFIG_EPIC100
-  If you have an SMC EtherPower II 9432 PCI Ethernet network card
-  which is based on the SMC83c170, say Y and read the Ethernet-HOWTO,
-  available from http://www.linuxdoc.org/docs.html#howto .
-
-  This driver is also available as a module ( = code which can be
-  inserted in and removed from the running kernel whenever you want).
-  The module will be called epic100.o. If you want to compile it as a
-  module, say M here and read Documentation/modules.txt as well as
-  Documentation/networking/net-modules.txt.
+  This driver is for the SMC EtherPower II 9432 PCI Ethernet NIC,
+  which is based on the SMC83c17x (EPIC/100).
+  More specific information and updates are available from
+  http://www.scyld.com/network/epic100.html
 
 SGI Seeq ethernet controller support
 CONFIG_SGISEEQ
   Say Y here if you have an Seeq based Ethernet network card. This is
   used in many Silicon Graphics machines.
 
+Sundance "Alta" PCI Ethernet support
+CONFIG_SUNDANCE
+  This driver is for the Sundance "Alta" chip.
+  More specific information and updates are available from
+  http://www.scyld.com/network/sundance.html
+
+Winbond W89c840 PCI Ethernet support
+CONFIG_WINBOND_840
+  This driver is for the Winbond W89c840 chip.  It also works with
+  the TX9882 chip on the Compex RL100-ATX board.
+  More specific information and updates are available from
+  http://www.scyld.com/network/drivers.html
+
 Zenith Z-Note support (EXPERIMENTAL)
 CONFIG_ZNET
   The Zenith Z-Note notebook computer has a built-in network
index 855282be62b4bb34189d2eaa28607e9212f703c5..4efcca4466d74c31ec1073ae54907cd7f0e8ed23 100644 (file)
@@ -72,6 +72,7 @@ APISOURCES := $(TOPDIR)/drivers/char/videodev.c \
                $(TOPDIR)/drivers/sound/sound_firmware.c \
                $(TOPDIR)/drivers/net/wan/syncppp.c \
                $(TOPDIR)/drivers/net/wan/z85230.c \
+               $(TOPDIR)/fs/locks.c \
                $(TOPDIR)/fs/devfs/base.c \
                $(TOPDIR)/kernel/pm.c \
                $(TOPDIR)/kernel/ksyms.c \
index c5d7ca3fc4425170848e1d139119ff40a339fbfd..c453384bfcd625e7536ed27f520fdfc405573bf2 100644 (file)
      <sect1><title>Registration and Superblocks</title>
 !Efs/super.c
      </sect1>
+     <sect1><title>File Locks</title>
+!Efs/locks.c
+!Ifs/locks.c
+     </sect1>
   </chapter>
 
   <chapter id="netcore">
index 976f4d435db5e1ac7722ebf7f81240cea4867c8d..364d33c46e517055bac41b3f85e6f62a50f439c7 100644 (file)
@@ -1,13 +1,43 @@
 Linux Kernel Makefiles
-16 August 2000
-Michael Elizabeth Chastain, <chastain@redhat.com>
+2000-September-14
+Michael Elizabeth Chastain, <mec@shout.net>
 
 
 
-=== Introduction
+=== Table of Contents
 
 This document describes the Linux kernel Makefiles.
 
+  1  Overview
+  2  Who does what
+  3  Makefile language
+  4  Variables passed down from the top
+  5  The structure of an arch Makefile
+     5.1  Architecture-specific variables
+     5.2  Vmlinux build variables
+     5.3  Post-vmlinux goals
+     5.4  Mandatory arch-specific goals
+  6  The structure of a subdirectory Makefile
+     6.1  Comments
+     6.2  Goal definitions
+     6.3  Adapter section
+     6.4  Rules.make section
+     6.5  Special rules
+  7  Rules.make variables
+     7.1  Subdirectories
+     7.2  Object file goals
+     7.3  Library file goals
+     7.4  Loadable module goals
+     7.5  Multi-part modules
+     7.6  Compilation flags
+     7.7  Miscellaneous variables
+  8  New-style variables
+  9  Compatibility with Linux Kernel 2.2
+ 10  Credits
+
+
+=== 1 Overview
+
 The Makefiles have five parts:
 
     Makefile: the top Makefile.
@@ -21,7 +51,7 @@ kernel configuration process.
 
 The top Makefile is responsible for building two major products: vmlinux
 (the resident kernel image) and modules (any module files).  It builds
-these targets by recursively descending into the subdirectories of the
+these goals by recursively descending into the subdirectories of the
 kernel source tree.  The list of subdirectories which are visited depends
 upon the kernel configuration.
 
@@ -40,7 +70,7 @@ lists.  It then declares rules based on those lists.
 
 
 
-=== Who does what
+=== Who does what
 
 People have four different relationships with the kernel Makefiles.
 
@@ -66,7 +96,7 @@ This document is aimed towards normal developers and arch developers.
 
 
 
-=== Makefile language
+=== Makefile language
 
 The kernel Makefiles are designed to run with Gnu Make.  The Makefiles
 use only the documented features of Gnu Make, but they do use many
@@ -91,7 +121,7 @@ split), but are otherwise exactly the same.
 
 
 
-=== Variables passed down from the top
+=== Variables passed down from the top
 
 The top Makefile exports the following variables:
 
@@ -169,7 +199,7 @@ The top Makefile exports the following variables:
     GENKSYMS
 
        These variables specify the commands and flags that Rules.make
-       uses to build target files from source files.
+       uses to build goal files from source files.
 
        $(CFLAGS_KERNEL) contains extra C compiler flags used to compile
        resident kernel code.
@@ -267,7 +297,11 @@ The top Makefile exports the following variables:
 
 
 
-=== The structure of an arch Makefile
+=== 5 The structure of an arch Makefile
+
+
+
+--- 5.1 Architecture-specific variables
 
 The top Makefile includes one arch Makefile file, arch/$(ARCH)/Makefile.
 This section describes the functions of the arch Makefile.
@@ -347,6 +381,10 @@ architecture-specific values.
                LD=$(CROSS_COMPILE)ld -m elf_s390
                OBJCOPY=$(CROSS_COMPILE)objcopy -O binary -R .note -R .comment -S
 
+
+
+--- 5.2 Vmlinux build variables
+
 An arch Makefile co-operates with the top Makefile to define variables
 which specify how to build the vmlinux file.  Note that there is no
 corresponding arch-specific section for modules; the module-building
@@ -381,12 +419,16 @@ machinery is all architecture-independent.
                CORE_FILES := arch/m68k/kernel/kernel.o arch/m68k/mm/mm.o $(CORE_FILES)
                LIBS += arch/m68k/lib/lib.a
 
-An arch Makefile specifies targets that take the vmlinux file, compress
+
+
+--- 5.3 Post-vmlinux goals
+
+An arch Makefile specifies goals that take the vmlinux file, compress
 it, wrap it in bootstrapping code, and copy the resulting files somewhere.
 This includes various kinds of installation commands.
 
-These post-vmlinux targets are not standardized across different
-architectures.  Here is a list of these targets and the architectures
+These post-vmlinux goals are not standardized across different
+architectures.  Here is a list of these goals and the architectures
 that support each of them (as of kernel version 2.4.0-test6-pre5):
 
     balo               mips
@@ -417,8 +459,12 @@ that support each of them (as of kernel version 2.4.0-test6-pre5):
     zlilo              i386
     znetboot.initrd    ppc
 
-An arch Makefile must define the following auxiliary targets.  These
-targets provide arch-specific actions for the corresponding targets
+
+
+--- 5.4 Mandatory arch-specific goals
+
+An arch Makefile must define the following arch-specific goals.
+These goals provide arch-specific actions for the corresponding goals
 in the top Makefile:
 
     archclean          clean
@@ -427,16 +473,24 @@ in the top Makefile:
 
 
 
-=== The structure of a subdirectory Makefile
+=== The structure of a subdirectory Makefile
 
 A subdirectory Makefile has five sections.
 
+
+
+--- 6.1 Comments
+
 The first section is a comment header.  Just write what you would
 write if you were editing a C source file, but use "# ..." instead of
 "/* ... */".  Historically, many anonymous people have edited kernel
 Makefiles without leaving any change histories in the header; comments
 from them would have been valuable.
 
+
+
+--- 6.2 Goal definitions
+
 The second section is a bunch of definitions that are the heart of the
 subdirectory Makefile.  These lines define the files to be built, any
 special compilation options, and any subdirectories to be recursively
@@ -468,25 +522,41 @@ The new-style Makefiles are more compact and easier to get correct
 for certain features (such as CONFIG_* options that enable more than
 one file).  If you have a choice, please write a new-style Makefile.
 
+
+
+--- 6.3 Adapter section
+
 The third section is an adapter section.  In old-style Makefiles, this
 third section is not present.  In new-style Makefiles, the third section
 contains boilerplate code which converts from new-style variables to
 old-style variables.  This is because Rules.make processes only the
 old-style variables.
 
+
+
+--- 6.4 Rules.make section
+
 The fourth section is the single line:
 
        include $(TOPDIR)/Rules.make
 
+
+
+--- 6.5 Special rules
+
 The fifth section contains any special Makefile rules needed that are
 not available through the common rules in Rules.make.
 
 
 
-=== Rules.make variables
+=== Rules.make variables
 
 The public interface of Rules.make consists of the following variables:
 
+
+
+--- 7.1 Subdirectories
+
     ALL_SUB_DIRS, SUB_DIRS, MOD_IN_SUB_DIRS, MOD_SUB_DIRS
 
        $(ALL_SUB_DIRS) is an unconditional list of *all* the
@@ -561,6 +631,10 @@ The public interface of Rules.make consists of the following variables:
                  endif
                endif
 
+
+
+--- 7.2 Object file goals
+
     O_TARGET, O_OBJS, OX_OBJS
 
        The subdirectory Makefile specifies object files for vmlinux in
@@ -612,6 +686,8 @@ The public interface of Rules.make consists of the following variables:
 
 
 
+--- 7.3 Library file goals
+
     L_TARGET, L_OBJS, LX_OBJS
 
        These names are similar to the O_* names.  Once again, $(L_OBJS)
@@ -674,6 +750,10 @@ The public interface of Rules.make consists of the following variables:
        during development activity, but $(L_TARGET) gives better error
        messages for unresolved symbols.
 
+
+
+--- 7.4 Loadable module goals
+
     M_OBJS, MX_OBJS
 
        $(M_OBJS) and $(MX_OBJS) specify object files which are built
@@ -731,6 +811,10 @@ The public interface of Rules.make consists of the following variables:
        will build $(M_OBJS) instead.  (Yes, this is a little delicate
        and a little confusing).
 
+
+
+--- 7.5 Multi-part modules
+
     MI_OBJS, MIX_OBJS
 
        Some kernel modules are composed of several object files
@@ -785,14 +869,9 @@ The public interface of Rules.make consists of the following variables:
                sb.o: $(sb-objs)
                        $(LD) -r -o $@ $(sb-objs)
 
-    IGNORE_FLAGS_OBJS
 
-       $(IGNORE_FLAGS_OBJS) is a list of object files which will not have
-       their flag dependencies automatically tracked.  This is a hackish
-       feature, used to kludge around a problem in the implementation
-       of flag dependencies.  (The problem is that flag dependencies
-       assume that a %.o file is built from a matching %.S or %.c file.
-       This is sometimes not true).
+
+--- 7.6 Compilation flags
 
     EXTRA_CFLAGS, EXTRA_AFLAGS, EXTRA_LDFLAGS, EXTRA_ARFLAGS
 
@@ -866,6 +945,24 @@ The public interface of Rules.make consists of the following variables:
        for such a file, you will have to remove the object file in order
        to re-build from source.
 
+    LD_RFLAG
+
+       This variable is used, but never defined.  It appears to be a
+       vestige of some abandoned experiment.
+
+
+
+--- 7.7 Miscellaneous variables
+
+    IGNORE_FLAGS_OBJS
+
+       $(IGNORE_FLAGS_OBJS) is a list of object files which will not have
+       their flag dependencies automatically tracked.  This is a hackish
+       feature, used to kludge around a problem in the implementation
+       of flag dependencies.  (The problem is that flag dependencies
+       assume that a %.o file is built from a matching %.S or %.c file.
+       This is sometimes not true).
+
     USE_STANDARD_AS_RULE
 
        This is a transition variable.  If $(USE_STANDARD_AS_RULE)
@@ -888,7 +985,7 @@ The public interface of Rules.make consists of the following variables:
 
 
 
-=== New-style variables
+=== New-style variables
 
 The "new-style variables" are simpler and more powerful than the
 "old-style variables".  As a result, many subdirectory Makefiles shrank
@@ -1051,7 +1148,7 @@ people define most variables using "new style" but then fall back to
 
 
 
-=== Compatibility with Linux Kernel 2.2
+=== Compatibility with Linux Kernel 2.2
 
 Most of the information in this document also applies to 2.2, although
 there is no indication of which things have changed when.  Here are some
@@ -1080,7 +1177,8 @@ for each file by name.
 
 
 
-=== Credits
+=== 10 Credits
 
 Thanks to the members of the linux-kbuild mailing list for reviewing
-drafts of this document, with particular thanks to Peter Samuelson.
+drafts of this document, with particular thanks to Peter Samuelson
+and Thomas Molina.
index cbeb54cb8955e7a5ce991304c7523d079a4c3895..5fa8e379a4b17ee88101908dbdb569e03cc85ef6 100644 (file)
@@ -25,29 +25,24 @@ Copyright (C) 1999-2000 Maxim Krasnyansky <max_mk@yahoo.com>
   br_sigio.c  - bridge based on async io and SIGIO signal.
   However the best example is VTun http://vtun.sourceforge.net :))  
 
-2. Installation
-  Run './configure' to configure the driver.
-
-  Run 'make install' to compile and install driver module and to create 
-  /dev/net/tun device node.
-
-3. Loading driver module
-  Linux
-     To load TUN/TAP driver module run:
-       modprobe tun 
-     To configure automatic loading of the 'tun' module you have to add: 
-       alias char-major-195 tun
-     to the /etc/conf.modules, and run: 
+2. Configuration 
+  Create device node:
+     mknod /dev/net/tun c 10 200
+
+  Driver module autoloading
+     Make sure that "Kernel module loader" - module auto-loading support is enabled 
+     in your kernel. 
+
+     Add following line to the /etc/modules.conf:
+       alias char-major-10-200 tun
+     
+     Run:
         modprobe -a 
-     TUN/TAP driver will be automatically loaded when application access
-     /dev/net/tun.
-     If "Kernel module loader" - module auto-loading support is not enabled 
-     in your kernel then you can add 
-        modprobe tun 
-     to one of the startup rc files.
 
-4. Program interface 
-  4.1 Network device allocation:
+     Driver will be automatically loaded when application access /dev/net/tun.
+
+3. Program interface 
+  3.1 Network device allocation:
 
   int tun_alloc(char *dev)
   {
@@ -76,7 +71,7 @@ Copyright (C) 1999-2000 Maxim Krasnyansky <max_mk@yahoo.com>
       return fd;
   }              
  
-  4.2 Frame format:
+  3.2 Frame format:
   If flag IFF_NO_PI is not set each frame format is: 
      Flags [2 bytes]
      Proto [2 bytes]
@@ -120,7 +115,7 @@ Currently driver has been written for 3 Unices:
 
 4. What is TUN/TAP driver used for?
 As mentioned above, main purpose of TUN/TAP driver is tunneling. 
-It used by VTun (http://vtun.netpedia.net).
+It is used by VTun (http://vtun.sourceforge.net).
 
 5. How does Virtual network device actually work ? 
 Virtual network device can be viewed as a simple Point-to-Point or
@@ -146,5 +141,3 @@ to attach BPF to this interface.
 
 8. Does TAP driver support kernel Ethernet bridging?
 Yes. Linux and FreeBSD drivers support Ethernet bridging. 
-
-
index 2991c4860bf7bb27e6da609a4a628f1df4d3555d..e08a4d582e26bf5f4261545fce2635897ac810f2 100644 (file)
@@ -250,6 +250,7 @@ CONFIGURE, MENUCONFIG, XCONFIG
 P:     Michael Elizabeth Chastain
 M:     mec@shout.net
 L:     linux-kbuild@torque.net
+W:     http://www.kernel.org/pub/linux/kernel/projects/kbuild/
 S:     Maintained
 
 CONFIGURE.HELP
@@ -485,6 +486,15 @@ L: linux-hams@vger.kernel.org
 W:     http://www.nt.tuwien.ac.at/~kkudielk/Linux/
 S:     Maintained
 
+KERNEL BUILD (Makefile, Rules.make, scripts/*)
+P:     Keith Owens
+M:     kaos@ocs.com.au
+P:     Michael Elizabeth Chastain
+M:     mec@shout.net
+L:     linux-kbuild@torque.net
+W:     http://www.kernel.org/pub/linux/kernel/projects/kbuild/
+S:     Maintained 
+
 LOGICAL VOLUME MANAGER
 P:     Heinz Mauelshagen
 M:     linux-LVM@EZ-Darmstadt.Telekom.de
index d9bb0d0cced10a6feb8242733b0732ff42e9c48c..4ce5cde9b9ca072c65d86bbe2328e897b0bf01b4 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -138,7 +138,7 @@ DRIVERS-$(CONFIG_DRM) += drivers/char/drm/drm.o
 DRIVERS-$(CONFIG_NUBUS) += drivers/nubus/nubus.a
 DRIVERS-$(CONFIG_ISDN) += drivers/isdn/isdn.a
 DRIVERS-$(CONFIG_NET_FC) += drivers/net/fc/fc.o
-DRIVERS-$(CONFIG_APPLETALK) += drivers/net/appletalk/appletalk.a
+DRIVERS-$(CONFIG_APPLETALK) += drivers/net/appletalk/appletalk.o
 DRIVERS-$(CONFIG_TR) += drivers/net/tokenring/tr.a
 DRIVERS-$(CONFIG_WAN) += drivers/net/wan/wan.o
 DRIVERS-$(CONFIG_ARCNET) += drivers/net/arcnet/arcnet.a
index 4f6c6b65cad8f0759f198511793ba4c486622521..8316683f0c14163a4bcd4860bae8ec2dfc8cb467 100644 (file)
@@ -119,6 +119,10 @@ archmrproper:
 
 archdep:
        @$(MAKEBOOT) dep
+
+vmlinux: arch/alpha/vmlinux.lds
+
+arch/alpha/vmlinux.lds: arch/alpha/vmlinux.lds.in
        $(CPP) $(CPPFLAGS) -xc -P arch/alpha/vmlinux.lds.in -o arch/alpha/vmlinux.lds
 
 bootpfile:
index ccf0cefbbe94a3cd514515dd363bb6304a8b2961..3b727631a6b48b8fc57be6c35981358665575efc 100644 (file)
@@ -416,7 +416,9 @@ sg_fill(struct scatterlist *leader, struct scatterlist *end,
        ptes = &arena->ptes[dma_ofs];
        sg = leader;
        do {
+#if DEBUG_ALLOC > 0
                struct scatterlist *last_sg = sg;
+#endif
 
                size = sg->length;
                paddr = virt_to_phys(sg->address);
index 9e8483a1a126cf917013503f9d37225d78232659..dd882dc14f2a8548246cbec037bcc3df5a0e8ad0 100644 (file)
@@ -1046,8 +1046,8 @@ debug_spin_lock(spinlock_t * lock, const char *base_file, int line_no)
        "       blbs    %0,2b\n"
        "       br      1b\n"
        ".previous"
-       : "=r" (tmp), "=m" (__dummy_lock(lock)), "=r" (stuck)
-       : "1" (__dummy_lock(lock)), "2" (stuck));
+       : "=r" (tmp), "=m" (lock->lock), "=r" (stuck)
+       : "1" (lock->lock), "2" (stuck) : "memory");
 
        if (stuck < 0) {
                printk(KERN_WARNING
@@ -1124,9 +1124,9 @@ void write_lock(rwlock_t * lock)
        "       blt     %1,8b\n"
        "       br      1b\n"
        ".previous"
-       : "=m" (__dummy_lock(lock)), "=&r" (regx), "=&r" (regy),
+       : "=m" (*(volatile int *)lock), "=&r" (regx), "=&r" (regy),
          "=&r" (stuck_lock), "=&r" (stuck_reader)
-       : "0" (__dummy_lock(lock)), "3" (stuck_lock), "4" (stuck_reader));
+       : "0" (*(volatile int *)lock), "3" (stuck_lock), "4" (stuck_reader) : "memory");
 
        if (stuck_lock < 0) {
                printk(KERN_WARNING "write_lock stuck at %p\n", inline_pc);
@@ -1163,8 +1163,8 @@ void read_lock(rwlock_t * lock)
        "       blbs    %1,6b;"
        "       br      1b\n"
        ".previous"
-       : "=m" (__dummy_lock(lock)), "=&r" (regx), "=&r" (stuck_lock)
-       : "0" (__dummy_lock(lock)), "2" (stuck_lock));
+       : "=m" (*(volatile int *)lock), "=&r" (regx), "=&r" (stuck_lock)
+       : "0" (*(volatile int *)lock), "2" (stuck_lock) : "memory");
 
        if (stuck_lock < 0) {
                printk(KERN_WARNING "read_lock stuck at %p\n", inline_pc);
index 0032937cfabcbe2aa9d2d692c3cbc2f6a817691a..0edf60839c009a571247490a02c09063e4af8f16 100644 (file)
@@ -36,6 +36,7 @@
 #include <linux/ioport.h>
 #include <linux/irq.h>
 #include <linux/interrupt.h>
+#include <linux/init.h>
 
 #include <asm/uaccess.h>
 #include <asm/io.h>
@@ -168,7 +169,7 @@ common_init_rtc(void)
        init_rtc_irq();
 }
 
-void
+void __init
 time_init(void)
 {
        unsigned int year, mon, day, hour, min, sec, cc1, cc2, epoch;
@@ -235,17 +236,14 @@ time_init(void)
                BCD_TO_BIN(year);
        }
 
-       /* PC-like is standard; used for year <= 20 || year >= 100 */
+       /* PC-like is standard; used for year < 20 || year >= 70 */
        epoch = 1900;
-       if (year > 20 && year < 48)
-               /* ARC console, used on some not so old boards */
+       if (year >= 20 && year < 48)
+               /* NT epoch */
                epoch = 1980;
        else if (year >= 48 && year < 70)
-               /* Digital UNIX, used on older boards (eg. AXPpxi33) */
+               /* Digital UNIX epoch */
                epoch = 1952;
-       else if (year >= 70 && year < 100)
-               /* Digital DECstations, very old... */
-               epoch = 1928;
 
        printk(KERN_INFO "Using epoch = %d\n", epoch);
 
index b5fc3776568a7a6f8e1e8aade709a537d5ffddb0..91e5ba660ecc4f7829730b6639cb6206f956c849 100644 (file)
@@ -8,7 +8,7 @@
 # Note 2! The CFLAGS definition is now in the main makefile...
 
 O_TARGET := math-emu.o
-O_OBJS   := math.o
+O_OBJS   := math.o qrnnd.o
 CFLAGS += -I. -I$(TOPDIR)/include/math-emu -w
 
 ifeq ($(CONFIG_MATHEMU),m)
index ed2efe0871130e7cf4338934e5a9c07a958bf839..4e28b18cf57893fd7bc77c826ea1ddc26ec4ecaf 100644 (file)
@@ -84,66 +84,6 @@ void cleanup_module(void)
 
 #endif /* MODULE */
 
-/* For 128-bit division.  */
-
-void
-udiv128(unsigned long divisor_f0, unsigned long divisor_f1,
-       unsigned long dividend_f0, unsigned long dividend_f1,
-       unsigned long *quot, unsigned long *remd)
-{
-       _FP_FRAC_DECL_2(quo);
-       _FP_FRAC_DECL_2(rem);
-       _FP_FRAC_DECL_2(tmp);
-       unsigned long i, num_bits, bit;
-
-       _FP_FRAC_SET_2(rem, _FP_ZEROFRAC_2);
-       _FP_FRAC_SET_2(quo, _FP_ZEROFRAC_2);
-
-       if (_FP_FRAC_ZEROP_2(divisor))
-               goto out;
-
-       if (_FP_FRAC_GT_2(divisor, dividend)) {
-               _FP_FRAC_COPY_2(rem, dividend);
-               goto out;
-       }
-
-       if (_FP_FRAC_EQ_2(divisor, dividend)) {
-               __FP_FRAC_SET_2(quo, 0, 1);
-               goto out;
-       }
-
-       num_bits = 128;
-       while (1) {
-               bit = _FP_FRAC_NEGP_2(dividend);
-               _FP_FRAC_COPY_2(tmp, rem);
-               _FP_FRAC_SLL_2(tmp, 1);
-               _FP_FRAC_LOW_2(tmp) |= bit;
-               if (! _FP_FRAC_GE_2(tmp, divisor))
-                       break;
-               _FP_FRAC_COPY_2(rem, tmp);
-               _FP_FRAC_SLL_2(dividend, 1);
-               num_bits--;
-       }
-
-       for (i = 0; i < num_bits; i++) {
-               bit = _FP_FRAC_NEGP_2(dividend);
-               _FP_FRAC_SLL_2(rem, 1);
-               _FP_FRAC_LOW_2(rem) |= bit;
-               _FP_FRAC_SUB_2(tmp, rem, divisor);
-               bit = _FP_FRAC_NEGP_2(tmp);
-               _FP_FRAC_SLL_2(dividend, 1);
-               _FP_FRAC_SLL_2(quo, 1);
-               if (!bit) {
-                       _FP_FRAC_LOW_2(quo) |= 1;
-                       _FP_FRAC_COPY_2(rem, tmp);
-               }
-       }
-
-out:
-       *quot = quo_f1;
-       *remd = rem_f1;
-       return;
-}
 
 /*
  * Emulate the floating point instruction at address PC.  Returns 0 if
diff --git a/arch/alpha/math-emu/qrnnd.S b/arch/alpha/math-emu/qrnnd.S
new file mode 100644 (file)
index 0000000..d6373ec
--- /dev/null
@@ -0,0 +1,163 @@
+ # Alpha 21064 __udiv_qrnnd
+ # Copyright (C) 1992, 1994, 1995, 2000 Free Software Foundation, Inc.
+
+ # This file is part of GCC.
+
+ # The GNU MP Library is free software; you can redistribute it and/or modify
+ # it under the terms of the GNU General Public License as published by
+ # the Free Software Foundation; either version 2 of the License, or (at your
+ # option) any later version.
+
+ # In addition to the permissions in the GNU General Public License, the
+ # Free Software Foundation gives you unlimited permission to link the
+ # compiled version of this file with other programs, and to distribute
+ # those programs without any restriction coming from the use of this
+ # file.  (The General Public License restrictions do apply in other
+ # respects; for example, they cover modification of the file, and
+ # distribution when not linked into another program.)
+
+ # This file is distributed in the hope that it will be useful, but
+ # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ # or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Library General Public
+ # License for more details.
+
+ # You should have received a copy of the GNU General Public License
+ # along with GCC; see the file COPYING.  If not, write to the 
+ # Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ # MA 02111-1307, USA.
+
+        .set noreorder
+        .set noat
+
+       .text
+
+       .globl __udiv_qrnnd
+       .ent __udiv_qrnnd
+__udiv_qrnnd:
+       .frame $30,0,$26,0
+       .prologue 0
+
+#define cnt    $2
+#define tmp    $3
+#define rem_ptr        $16
+#define n1     $17
+#define n0     $18
+#define d      $19
+#define qb     $20
+#define AT     $at
+
+       ldiq    cnt,16
+       blt     d,$largedivisor
+
+$loop1:        cmplt   n0,0,tmp
+       addq    n1,n1,n1
+       bis     n1,tmp,n1
+       addq    n0,n0,n0
+       cmpule  d,n1,qb
+       subq    n1,d,tmp
+       cmovne  qb,tmp,n1
+       bis     n0,qb,n0
+       cmplt   n0,0,tmp
+       addq    n1,n1,n1
+       bis     n1,tmp,n1
+       addq    n0,n0,n0
+       cmpule  d,n1,qb
+       subq    n1,d,tmp
+       cmovne  qb,tmp,n1
+       bis     n0,qb,n0
+       cmplt   n0,0,tmp
+       addq    n1,n1,n1
+       bis     n1,tmp,n1
+       addq    n0,n0,n0
+       cmpule  d,n1,qb
+       subq    n1,d,tmp
+       cmovne  qb,tmp,n1
+       bis     n0,qb,n0
+       cmplt   n0,0,tmp
+       addq    n1,n1,n1
+       bis     n1,tmp,n1
+       addq    n0,n0,n0
+       cmpule  d,n1,qb
+       subq    n1,d,tmp
+       cmovne  qb,tmp,n1
+       bis     n0,qb,n0
+       subq    cnt,1,cnt
+       bgt     cnt,$loop1
+       stq     n1,0(rem_ptr)
+       bis     $31,n0,$0
+       ret     $31,($26),1
+
+$largedivisor:
+       and     n0,1,$4
+
+       srl     n0,1,n0
+       sll     n1,63,tmp
+       or      tmp,n0,n0
+       srl     n1,1,n1
+
+       and     d,1,$6
+       srl     d,1,$5
+       addq    $5,$6,$5
+
+$loop2:        cmplt   n0,0,tmp
+       addq    n1,n1,n1
+       bis     n1,tmp,n1
+       addq    n0,n0,n0
+       cmpule  $5,n1,qb
+       subq    n1,$5,tmp
+       cmovne  qb,tmp,n1
+       bis     n0,qb,n0
+       cmplt   n0,0,tmp
+       addq    n1,n1,n1
+       bis     n1,tmp,n1
+       addq    n0,n0,n0
+       cmpule  $5,n1,qb
+       subq    n1,$5,tmp
+       cmovne  qb,tmp,n1
+       bis     n0,qb,n0
+       cmplt   n0,0,tmp
+       addq    n1,n1,n1
+       bis     n1,tmp,n1
+       addq    n0,n0,n0
+       cmpule  $5,n1,qb
+       subq    n1,$5,tmp
+       cmovne  qb,tmp,n1
+       bis     n0,qb,n0
+       cmplt   n0,0,tmp
+       addq    n1,n1,n1
+       bis     n1,tmp,n1
+       addq    n0,n0,n0
+       cmpule  $5,n1,qb
+       subq    n1,$5,tmp
+       cmovne  qb,tmp,n1
+       bis     n0,qb,n0
+       subq    cnt,1,cnt
+       bgt     cnt,$loop2
+
+       addq    n1,n1,n1
+       addq    $4,n1,n1
+       bne     $6,$Odd
+       stq     n1,0(rem_ptr)
+       bis     $31,n0,$0
+       ret     $31,($26),1
+
+$Odd:
+       /* q' in n0. r' in n1 */
+       addq    n1,n0,n1
+
+       cmpult  n1,n0,tmp       # tmp := carry from addq
+       subq    n1,d,AT
+       addq    n0,tmp,n0
+       cmovne  tmp,AT,n1
+
+       cmpult  n1,d,tmp
+       addq    n0,1,AT
+       cmoveq  tmp,AT,n0
+       subq    n1,d,AT
+       cmoveq  tmp,AT,n1
+
+       stq     n1,0(rem_ptr)
+       bis     $31,n0,$0
+       ret     $31,($26),1
+
+       .end    __udiv_qrnnd
index 7a6a8cf45d49840dbdd8e007c6206f18fc684405..f53707f7745569c3a1ab48526ccf5d6753a1221e 100644 (file)
           : "r" ((UDItype)(u)),                \
             "r" ((UDItype)(v)))
 
-extern void udiv128(unsigned long, unsigned long,
-                   unsigned long, unsigned long,
-                   unsigned long *,
-                   unsigned long *);
-
-#define udiv_qrnnd(q, r, n1, n0, d)            \
-  do {                                         \
-    unsigned long xr, xi;                      \
-    udiv128((n0), (n1), 0, (d), &xr, &xi);     \
-    (q) = xr;                                  \
-    (r) = xi;                                  \
+#define udiv_qrnnd(q, r, n1, n0, d)                            \
+  do { unsigned long __r;                                      \
+    (q) = __udiv_qrnnd (&__r, (n1), (n0), (d));                        \
+    (r) = __r;                                                 \
   } while (0)
+extern unsigned long __udiv_qrnnd (unsigned long *, unsigned long,
+                                  unsigned long , unsigned long);
 
 #define UDIV_NEEDS_NORMALIZATION 1  
 
index d3a02fe272e0262f0d6eca0885797802b2850592..5390bd4ec4baf735d5781ed50b6502112b0b678b 100644 (file)
@@ -88,7 +88,7 @@ search_exception_table(unsigned long addr, unsigned long exc_gp)
         */
        ret = search_exception_table_without_gp(addr);
        if (ret) {
-               printk(KERN_ALERT, "%s: [%lx] EX_TABLE search fail with"
+               printk(KERN_ALERT "%s: [%lx] EX_TABLE search fail with"
                       "exc frame GP, success with raw GP\n",
                       current->comm, addr);
                return ret;
index 5d579ef5e3b6d86a278bd29da32c097266d54d4a..e0d490bb3558fc36ee6313b07aaf49ee8e729044 100644 (file)
@@ -129,6 +129,7 @@ CONFIG_BLK_DEV_FD=y
 # CONFIG_MD_RAID1 is not set
 # CONFIG_MD_RAID5 is not set
 # CONFIG_BLK_DEV_LVM is not set
+# CONFIG_LVM_PROC_FS is not set
 
 #
 # Networking options
@@ -363,14 +364,18 @@ CONFIG_EEPRO100=y
 # CONFIG_NE2K_PCI is not set
 # CONFIG_8139TOO is not set
 # CONFIG_SIS900 is not set
+# CONFIG_EPIC100 is not set
+# CONFIG_SUNDANCE is not set
 # CONFIG_TLAN is not set
 # CONFIG_VIA_RHINE is not set
+# CONFIG_WINBOND_840 is not set
 # CONFIG_NET_POCKET is not set
 
 #
 # Ethernet (1000 Mbit)
 #
 # CONFIG_ACENIC is not set
+# CONFIG_HAMACHI is not set
 # CONFIG_SK98LIN is not set
 # CONFIG_FDDI is not set
 # CONFIG_PPP is not set
@@ -590,7 +595,21 @@ CONFIG_VGA_CONSOLE=y
 #
 # Sound
 #
-# CONFIG_SOUND is not set
+CONFIG_SOUND=y
+# CONFIG_SOUND_CMPCI is not set
+# CONFIG_SOUND_EMU10K1 is not set
+# CONFIG_SOUND_FUSION is not set
+# CONFIG_SOUND_ES1370 is not set
+CONFIG_SOUND_ES1371=y
+# CONFIG_SOUND_ESSSOLO1 is not set
+# CONFIG_SOUND_MAESTRO is not set
+# CONFIG_SOUND_SONICVIBES is not set
+# CONFIG_SOUND_TRIDENT is not set
+# CONFIG_SOUND_MSNDCLAS is not set
+# CONFIG_SOUND_MSNDPIN is not set
+# CONFIG_SOUND_VIA82CXXX is not set
+# CONFIG_SOUND_OSS is not set
+# CONFIG_SOUND_TVMIXER is not set
 
 #
 # USB support
@@ -607,7 +626,6 @@ CONFIG_USB=y
 #
 # USB Controllers
 #
-# CONFIG_USB_UHCI is not set
 CONFIG_USB_UHCI_ALT=y
 # CONFIG_USB_OHCI is not set
 
index 66e1bd9c21a52db934f255490d491463bc38f9dd..d5f994a9f4d0984ca507f0ca272cd6ad162c3088 100644 (file)
@@ -219,10 +219,10 @@ static int __init find_irq_entry(int apic, int pin, int type)
        int i;
 
        for (i = 0; i < mp_irq_entries; i++)
-               if ( (mp_irqs[i].mpc_irqtype == type) &&
-                       (mp_irqs[i].mpc_dstapic == mp_ioapics[apic].mpc_apicid) &&
-                       (mp_irqs[i].mpc_dstirq == pin))
-
+               if (mp_irqs[i].mpc_irqtype == type &&
+                   (mp_irqs[i].mpc_dstapic == mp_ioapics[apic].mpc_apicid ||
+                    mp_irqs[i].mpc_dstapic == MP_APIC_ALL) &&
+                   mp_irqs[i].mpc_dstirq == pin)
                        return i;
 
        return -1;
@@ -262,7 +262,8 @@ int IO_APIC_get_PCI_irq_vector(int bus, int slot, int pci_pin)
                int lbus = mp_irqs[i].mpc_srcbus;
 
                for (apic = 0; apic < nr_ioapics; apic++)
-                       if (mp_ioapics[apic].mpc_apicid == mp_irqs[i].mpc_dstapic)
+                       if (mp_ioapics[apic].mpc_apicid == mp_irqs[i].mpc_dstapic ||
+                           mp_irqs[i].mpc_dstapic == MP_APIC_ALL)
                                break;
 
                if ((mp_bus_id_to_type[lbus] == MP_BUS_PCI) &&
index c2621dd3cdf0cbd9d229eea1f5964520f6fdf4e2..a9b11561dd62680f3f37bdd7124d9c6a7077d723 100644 (file)
@@ -100,10 +100,6 @@ static int putreg(struct task_struct *child,
                        value &= FLAG_MASK;
                        value |= get_stack_long(child, EFL_OFFSET) & ~FLAG_MASK;
                        break;
-               case EIP:
-                       /* Mark us as not being in a system call, so that no restart issues happen */ 
-                       put_stack_long(child, 4*ORIG_EAX - sizeof(struct pt_regs), -1);
-                       break;
        }
        if (regno > GS*4)
                regno -= 2*4;
index 4a118ab17a58ce82f295708dc66aa26812f9ddd6..124584fe7e45e0402f3ba22a0051e885245d88b5 100644 (file)
@@ -414,13 +414,15 @@ void smp_send_reschedule(int cpu)
  */
 static spinlock_t call_lock = SPIN_LOCK_UNLOCKED;
 
-static volatile struct call_data_struct {
+struct call_data_struct {
        void (*func) (void *info);
        void *info;
        atomic_t started;
        atomic_t finished;
        int wait;
-} *call_data = NULL;
+};
+
+static struct call_data_struct * call_data = NULL;
 
 /*
  * this function sends a 'generic call function' IPI to all other CPUs
index 5856bd54ac2b8c7a00be37bf1e034e9cc9b50c21..864ae41a5ffdecbdb29fed6275ff9641dbc6e46e 100644 (file)
@@ -521,6 +521,9 @@ asmlinkage void do_debug(struct pt_regs * regs, long error_code)
        if (regs->eflags & VM_MASK)
                goto debug_vm86;
 
+       /* Save debug status register where ptrace can see it */
+       tsk->thread.debugreg[6] = condition;
+
        /* Mask out spurious TF errors due to lazy TF clearing */
        if (condition & DR_STEP) {
                /*
index 5c73ac23f5dd333bf63c38a9f89ec9e6965b1c06..b4ae03d0541cdd3535cae735e002f5cafac3b12e 100644 (file)
@@ -24,8 +24,8 @@
 #include <linux/mm.h>
 #include <linux/pci.h>
 #include <linux/acpi.h>
+#include <linux/delay.h>
 #include <asm/io.h>
-#include <asm/delay.h>
 #include "acpi.h"
 #include "driver.h"
 
index ca884c158855ddf61f5dd4667943dd1592b45990..332bedab0dc7d6ae77383c135f9eda6419485252 100644 (file)
@@ -396,8 +396,8 @@ static int cciss_ioctl(struct inode *inode, struct file *filep,
                cciss_pci_info_struct pciinfo;
 
                if (!arg) return -EINVAL;
-               pciinfo.bus = hba[ctlr]->pci_bus;
-               pciinfo.dev_fn = hba[ctlr]->pci_dev_fn;
+               pciinfo.bus = hba[ctlr]->pci_dev->bus->number;
+               pciinfo.dev_fn = hba[ctlr]->pci_dev->devfn;
                pciinfo.board_id = hba[ctlr]->board_id;
                if(copy_to_user((void *) arg, &pciinfo, sizeof( cciss_pci_info_struct)))
                        return -EFAULT;
@@ -1016,13 +1016,13 @@ static int sendcmd(
 /*
  * Map (physical) PCI mem into (virtual) kernel space
  */
-static ulong remap_pci_mem(ulong base, ulong size)
+static void *remap_pci_mem(ulong base, ulong size)
 {
         ulong page_base        = ((ulong) base) & PAGE_MASK;
         ulong page_offs        = ((ulong) base) - page_base;
-        ulong page_remapped    = (ulong) ioremap(page_base, page_offs+size);
+        void *page_remapped    = ioremap(page_base, page_offs+size);
 
-        return (ulong) (page_remapped ? (page_remapped + page_offs) : 0UL);
+        return (page_remapped ? (page_remapped + page_offs) : NULL);
 }
 
 /*
@@ -1424,24 +1424,22 @@ static void print_cfg_table( CfgTable_struct *tb)
 }
 #endif /* CCISS_DEBUG */
 
-static int cciss_pci_init(ctlr_info_t *c, unchar bus, unchar device_fn)
+static int cciss_pci_init(ctlr_info_t *c, struct pci_dev *pdev)
 {
        ushort vendor_id, device_id, command;
        unchar cache_line_size, latency_timer;
        unchar irq, revision;
-       uint addr[6];
+       unsigned long addr[6];
        __u32 board_id;
-       struct pci_dev *pdev;
 
        int i;
 
-       pdev = pci_find_slot(bus, device_fn);
        vendor_id = pdev->vendor;
        device_id = pdev->device;
        irq = pdev->irq;
 
        for(i=0; i<6; i++)
-               addr[i] = pdev->resource[i].start;
+               addr[i] = pci_resource_start(pdev, i);
        (void) pci_read_config_word(pdev, PCI_COMMAND,&command);
        (void) pci_read_config_byte(pdev, PCI_CLASS_REVISION,&revision);
        (void) pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE, &cache_line_size);
@@ -1528,23 +1526,21 @@ static int cciss_pci_init(ctlr_info_t *c, unchar bus, unchar device_fn)
  */
 static int cciss_pci_detect(void)
 {
+       struct pci_dev *pdev;
 
-       int index;
-       unchar bus=0, dev_fn=0;
-       
-               for(index=0; ; index++) {
-                       if (pcibios_find_device(PCI_VENDOR_ID_COMPAQ,
-                               PCI_DEVICE_ID_COMPAQ_CISS, 
-                                       index, &bus, &dev_fn))
-                               break;
+       pdev = pci_find_device(PCI_VENDOR_ID_COMPAQ,
+                              PCI_DEVICE_ID_COMPAQ_CISS, NULL);
+       while (pdev) {
                        printk(KERN_DEBUG "cciss: Device %x has been found at %x %x\n",
-                               PCI_DEVICE_ID_COMPAQ_CISS, bus, dev_fn);
-                       if (index == 1000000) break;
+                               PCI_DEVICE_ID_COMPAQ_CISS,
+                               pdev->bus->number, pdev->devfn);
                        if (nr_ctlr == 8) {
                                printk(KERN_WARNING "cciss: This driver"
                                " supports a maximum of 8 controllers.\n");
                                break;
                        }
+                       if (pci_enable_device(pdev))
+                               continue;
                        hba[nr_ctlr] = kmalloc(sizeof(ctlr_info_t), GFP_KERNEL);
                        if(hba[nr_ctlr]==NULL)
                        {
@@ -1552,18 +1548,19 @@ static int cciss_pci_detect(void)
                                continue;
                        }
                        memset(hba[nr_ctlr], 0, sizeof(ctlr_info_t));
-                       if (cciss_pci_init(hba[nr_ctlr], bus, dev_fn) != 0)
+                       if (cciss_pci_init(hba[nr_ctlr], pdev) != 0)
                        {
                                kfree(hba[nr_ctlr]);
                                continue;
                        }
                        sprintf(hba[nr_ctlr]->devname, "cciss%d", nr_ctlr);
                        hba[nr_ctlr]->ctlr = nr_ctlr;
-                       hba[nr_ctlr]->pci_bus = bus;
-                       hba[nr_ctlr]->pci_dev_fn = dev_fn;
+                       hba[nr_ctlr]->pci_dev = pdev;
                        nr_ctlr++;
 
-               }
+               pdev = pci_find_device(PCI_VENDOR_ID_COMPAQ,
+                                      PCI_DEVICE_ID_COMPAQ_CISS, pdev);
+       }
        return nr_ctlr;
 
 }
@@ -1882,7 +1879,7 @@ void cleanup_module(void)
                /* Turn board interrupts off */ 
                hba[i]->access.set_intr_mask(hba[i], CCISS_INTR_OFF);
                free_irq(hba[i]->intr, hba[i]);
-               iounmap((void*)hba[i]->vaddr);
+               iounmap(hba[i]->vaddr);
                unregister_blkdev(MAJOR_NR+i, hba[i]->devname);
                blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR + i));
                remove_proc_entry(hba[i]->devname, proc_cciss); 
index 1053aefd3ae18a7c3b035302bf823c061985fc86..766761076dbb4daefd3bc723bf904d559ee17f9d 100644 (file)
@@ -42,11 +42,10 @@ struct ctlr_info
        char    devname[8];
        char    *product_name;
        char    firm_ver[4]; // Firmware version 
-       unchar  pci_bus;
-        unchar  pci_dev_fn;
+        struct pci_dev *pci_dev;
        __u32   board_id;
-       __u32   vaddr;
-       __u32   paddr;  
+       void   *vaddr;
+       unsigned long paddr;    
        CfgTable_struct *cfgtable;
        int     intr;
 
index ad7c35d00486a4281c27b7e815c37649825ed89e..f26611bc378e8a0fa3139028d75f81baeb2bd780 100644 (file)
@@ -116,8 +116,8 @@ static struct proc_dir_entry *proc_array = NULL;
 
 int cpqarray_init(void);
 static int cpqarray_pci_detect(void);
-static int cpqarray_pci_init(ctlr_info_t *c, unchar bus, unchar device_fn);
-static ulong remap_pci_mem(ulong base, ulong size);
+static int cpqarray_pci_init(ctlr_info_t *c, struct pci_dev *pdev);
+static void *remap_pci_mem(ulong base, ulong size);
 static int cpqarray_eisa_detect(void);
 static int pollcomplete(int ctlr);
 static void getgeometry(int ctlr);
@@ -335,7 +335,7 @@ void cleanup_module(void)
        for(i=0; i<nr_ctlr; i++) {
                hba[i]->access.set_intr_mask(hba[i], 0);
                free_irq(hba[i]->intr, hba[i]);
-               iounmap((void*)hba[i]->vaddr);
+               iounmap(hba[i]->vaddr);
                unregister_blkdev(MAJOR_NR+i, hba[i]->devname);
                del_timer(&hba[i]->timer);
                blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR + i));
@@ -542,8 +542,7 @@ int __init cpqarray_init(void)
  */
 static int cpqarray_pci_detect(void)
 {
-       int index;
-       unchar bus=0, dev_fn=0;
+       struct pci_dev *pdev;
 
 #define IDA_BOARD_TYPES 3
        static int ida_vendor_id[IDA_BOARD_TYPES] = { PCI_VENDOR_ID_DEC, 
@@ -554,29 +553,22 @@ static int cpqarray_pci_detect(void)
        /* search for all PCI board types that could be for this driver */
        for(brdtype=0; brdtype<IDA_BOARD_TYPES; brdtype++)
        {
-               for(index=0; ; index++) {
-                       if (pcibios_find_device(ida_vendor_id[brdtype],
-                               ida_device_id[brdtype], index, &bus, &dev_fn))
-                               break;
+               pdev = pci_find_device(ida_vendor_id[brdtype],
+                                      ida_device_id[brdtype], NULL);
+               while (pdev) {
                        printk(KERN_DEBUG "cpqarray: Device %x has been found at %x %x\n",
-                               ida_vendor_id[brdtype], bus, dev_fn);
-                       if (index == 1000000) break;
+                               ida_vendor_id[brdtype],
+                               pdev->bus->number, pdev->devfn);
                        if (nr_ctlr == 8) {
                                printk(KERN_WARNING "cpqarray: This driver"
                                " supports a maximum of 8 controllers.\n");
                                break;
                        }
-
+                       
 /* if it is a PCI_DEVICE_ID_NCR_53C1510, make sure it's                                the Compaq version of the chip */ 
 
                        if (ida_device_id[brdtype] == PCI_DEVICE_ID_NCR_53C1510)                        {       
-                               unsigned short subvendor=0;
-                               if(pcibios_read_config_word(bus, dev_fn, 
-                                       PCI_SUBSYSTEM_VENDOR_ID, &subvendor))
-                               {
-                                       printk(KERN_DEBUG "cpqarray: failed to read subvendor\n");
-                                       continue;
-                               }
+                               unsigned short subvendor=pdev->subsystem_vendor;
                                if(subvendor !=  PCI_VENDOR_ID_COMPAQ)
                                {
                                        printk(KERN_DEBUG 
@@ -591,7 +583,7 @@ static int cpqarray_pci_detect(void)
                                continue;
                        }
                        memset(hba[nr_ctlr], 0, sizeof(ctlr_info_t));
-                       if (cpqarray_pci_init(hba[nr_ctlr], bus, dev_fn) != 0)
+                       if (cpqarray_pci_init(hba[nr_ctlr], pdev) != 0)
                        {
                                kfree(hba[nr_ctlr]);
                                continue;
@@ -600,6 +592,8 @@ static int cpqarray_pci_detect(void)
                        hba[nr_ctlr]->ctlr = nr_ctlr;
                        nr_ctlr++;
 
+                       pdev = pci_find_device(ida_vendor_id[brdtype],
+                                              ida_device_id[brdtype], pdev);
                }
        }
 
@@ -610,26 +604,23 @@ static int cpqarray_pci_detect(void)
  * Find the IO address of the controller, its IRQ and so forth.  Fill
  * in some basic stuff into the ctlr_info_t structure.
  */
-static int cpqarray_pci_init(ctlr_info_t *c, unchar bus, unchar device_fn)
+static int cpqarray_pci_init(ctlr_info_t *c, struct pci_dev *pdev)
 {
        ushort vendor_id, device_id, command;
        unchar cache_line_size, latency_timer;
        unchar irq, revision;
-       uint addr[6];
+       unsigned long addr[6];
        __u32 board_id;
-       struct pci_dev *pdev;
 
        int i;
 
-       c->pci_bus = bus;
-       c->pci_dev_fn = device_fn;
-       pdev = pci_find_slot(bus, device_fn);
+       c->pci_dev = pdev;
        vendor_id = pdev->vendor;
        device_id = pdev->device;
        irq = pdev->irq;
 
        for(i=0; i<6; i++)
-               addr[i] = pdev->resource[i].flags;
+               addr[i] = pci_resource_start(pdev, i);
 
        if (pci_enable_device(pdev))
                return -1;
@@ -646,7 +637,7 @@ DBGINFO(
        printk("device_id = %x\n", device_id);
        printk("command = %x\n", command);
        for(i=0; i<6; i++)
-               printk("addr[%d] = %x\n", i, addr[i]);
+               printk("addr[%d] = %lx\n", i, addr[i]);
        printk("revision = %x\n", revision);
        printk("irq = %x\n", irq);
        printk("cache_line_size = %x\n", cache_line_size);
@@ -655,17 +646,19 @@ DBGINFO(
 );
 
        c->intr = irq;
-       c->ioaddr = addr[0] & ~0x1;
+       c->ioaddr = addr[0];
 
-       /*
-        * Memory base addr is first addr with the first bit _not_ set
-        */
+       c->paddr = 0;
        for(i=0; i<6; i++)
-               if (!(addr[i] & 0x1)) {
+               if (pci_resource_flags(pdev, i) & IORESOURCE_MEM) {
                        c->paddr = pci_resource_start (pdev, i);
                        break;
                }
+       if (!c->paddr)
+               return -1;
        c->vaddr = remap_pci_mem(c->paddr, 128);
+       if (!c->vaddr)
+               return -1;
        c->board_id = board_id;
 
        for(i=0; i<NR_PRODUCTS; i++) {
@@ -688,13 +681,13 @@ DBGINFO(
 /*
  * Map (physical) PCI mem into (virtual) kernel space
  */
-static ulong remap_pci_mem(ulong base, ulong size)
+static void *remap_pci_mem(ulong base, ulong size)
 {
         ulong page_base        = ((ulong) base) & PAGE_MASK;
         ulong page_offs        = ((ulong) base) - page_base;
-        ulong page_remapped    = (ulong) ioremap(page_base, page_offs+size);
+        void *page_remapped    = ioremap(page_base, page_offs+size);
 
-        return (ulong) (page_remapped ? (page_remapped + page_offs) : 0UL);
+        return (page_remapped ? (page_remapped + page_offs) : NULL);
 }
 
 #ifndef MODULE
@@ -778,8 +771,7 @@ static int cpqarray_eisa_detect(void)
                hba[nr_ctlr]->access = *(products[j].access);
                hba[nr_ctlr]->ctlr = nr_ctlr;
                hba[nr_ctlr]->board_id = board_id;
-               hba[nr_ctlr]->pci_bus = 0;  /* not PCI */
-               hba[nr_ctlr]->pci_dev_fn = 0; /* not PCI */
+               hba[nr_ctlr]->pci_dev = NULL; /* not PCI */
 
 DBGINFO(
        printk("i = %d, j = %d\n", i, j);
@@ -1205,8 +1197,8 @@ static int ida_ioctl(struct inode *inode, struct file *filep, unsigned int cmd,
                ida_pci_info_struct pciinfo;
 
                if (!arg) return -EINVAL;
-               pciinfo.bus = hba[ctlr]->pci_bus;
-               pciinfo.dev_fn = hba[ctlr]->pci_dev_fn;
+               pciinfo.bus = hba[ctlr]->pci_dev->bus->number;
+               pciinfo.dev_fn = hba[ctlr]->pci_dev->devfn;
                pciinfo.board_id = hba[ctlr]->board_id;
                if(copy_to_user((void *) arg, &pciinfo,  
                        sizeof( ida_pci_info_struct)))
index 8b57c46a821ea436d264175af7285da5d082305d..de569c7de2d053f85e48f228bb7c2cef5ffc3d9a 100644 (file)
@@ -87,14 +87,13 @@ struct ctlr_info {
        int     log_drives;
        int     phys_drives;
 
-       unsigned char  pci_bus;     /* 0 if EISA */
-        unsigned char  pci_dev_fn;  /* 0 if EISA */
+       struct pci_dev *pci_dev;    /* NULL if EISA */
        __u32   board_id;
        char    *product_name;  
 
-       __u32   vaddr;
-       __u32   paddr;
-       __u32   ioaddr;
+       void *vaddr;
+       unsigned long paddr;
+       unsigned long ioaddr;
        int     intr;
        int     usage_count;
        drv_info_t      drv[NWD];
index 6e877dcd515c8eff90c3cc2e2fe41dfaf2e7efac..07b7cef5a30972a6a65fc59a79afa127046ffab2 100644 (file)
@@ -1130,9 +1130,6 @@ int __init blk_dev_init(void)
 #ifdef CONFIG_SJCD
        sjcd_init();
 #endif CONFIG_SJCD
-#ifdef CONFIG_BLK_DEV_MD
-       md_init();
-#endif CONFIG_BLK_DEV_MD
 #ifdef CONFIG_APBLOCK
        ap_init();
 #endif
diff --git a/drivers/block/lvm-snap.c b/drivers/block/lvm-snap.c
deleted file mode 100644 (file)
index 04007c1..0000000
+++ /dev/null
@@ -1,436 +0,0 @@
-/*
- * kernel/lvm-snap.c
- *
- * Copyright (C) 2000 Andrea Arcangeli <andrea@suse.de> SuSE
- *
- * LVM snapshot driver is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- * 
- * LVM driver is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- * 
- * You should have received a copy of the GNU General Public License
- * along with GNU CC; see the file COPYING.  If not, write to
- * the Free Software Foundation, 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA. 
- *
- */
-
-#include <linux/kernel.h>
-#include <linux/vmalloc.h>
-#include <linux/blkdev.h>
-#include <linux/smp_lock.h>
-#include <linux/types.h>
-#include <linux/iobuf.h>
-#include <linux/lvm.h>
-
-
-static char *lvm_snap_version __attribute__ ((unused)) = "LVM 0.8final (15/02/2000)\n";
-
-extern const char *const lvm_name;
-extern int lvm_blocksizes[];
-
-void lvm_snapshot_release(lv_t *);
-
-#define hashfn(dev,block,mask,chunk_size) \
-       ((HASHDEV(dev)^((block)/(chunk_size))) & (mask))
-
-static inline lv_block_exception_t *
-lvm_find_exception_table(kdev_t org_dev, unsigned long org_start, lv_t * lv)
-{
-       struct list_head * hash_table = lv->lv_snapshot_hash_table, * next;
-       unsigned long mask = lv->lv_snapshot_hash_mask;
-       int chunk_size = lv->lv_chunk_size;
-       lv_block_exception_t * ret;
-       int i = 0;
-
-       hash_table = &hash_table[hashfn(org_dev, org_start, mask, chunk_size)];
-       ret = NULL;
-       for (next = hash_table->next; next != hash_table; next = next->next)
-       {
-               lv_block_exception_t * exception;
-
-               exception = list_entry(next, lv_block_exception_t, hash);
-               if (exception->rsector_org == org_start &&
-                   exception->rdev_org == org_dev)
-               {
-                       if (i)
-                       {
-                               /* fun, isn't it? :) */
-                               list_del(next);
-                               list_add(next, hash_table);
-                       }
-                       ret = exception;
-                       break;
-               }
-               i++;
-       }
-       return ret;
-}
-
-static inline void lvm_hash_link(lv_block_exception_t * exception,
-                                kdev_t org_dev, unsigned long org_start,
-                                lv_t * lv)
-{
-       struct list_head * hash_table = lv->lv_snapshot_hash_table;
-       unsigned long mask = lv->lv_snapshot_hash_mask;
-       int chunk_size = lv->lv_chunk_size;
-
-       hash_table = &hash_table[hashfn(org_dev, org_start, mask, chunk_size)];
-       list_add(&exception->hash, hash_table);
-}
-
-int lvm_snapshot_remap_block(kdev_t * org_dev, unsigned long * org_sector,
-                            unsigned long pe_start, lv_t * lv)
-{
-       int ret;
-       unsigned long pe_off, pe_adjustment, __org_start;
-       kdev_t __org_dev;
-       int chunk_size = lv->lv_chunk_size;
-       lv_block_exception_t * exception;
-
-       pe_off = pe_start % chunk_size;
-       pe_adjustment = (*org_sector-pe_off) % chunk_size;
-       __org_start = *org_sector - pe_adjustment;
-       __org_dev = *org_dev;
-
-       ret = 0;
-       exception = lvm_find_exception_table(__org_dev, __org_start, lv);
-       if (exception)
-       {
-               *org_dev = exception->rdev_new;
-               *org_sector = exception->rsector_new + pe_adjustment;
-               ret = 1;
-       }
-       return ret;
-}
-
-static void lvm_drop_snapshot(lv_t * lv_snap, const char * reason)
-{
-       kdev_t last_dev;
-       int i;
-
-       /* no exception storage space available for this snapshot
-          or error on this snapshot --> release it */
-       invalidate_buffers(lv_snap->lv_dev);
-
-       last_dev = 0;
-       for (i = 0; i < lv_snap->lv_remap_ptr; i++) {
-               if ( lv_snap->lv_block_exception[i].rdev_new != last_dev) {
-                       last_dev = lv_snap->lv_block_exception[i].rdev_new;
-                       invalidate_buffers(last_dev);
-               }
-       }
-
-       lvm_snapshot_release(lv_snap);
-
-       printk(KERN_INFO
-              "%s -- giving up to snapshot %s on %s due %s\n",
-              lvm_name, lv_snap->lv_snapshot_org->lv_name, lv_snap->lv_name,
-              reason);
-}
-
-static inline void lvm_snapshot_prepare_blocks(unsigned long * blocks,
-                                              unsigned long start,
-                                              int nr_sectors,
-                                              int blocksize)
-{
-       int i, sectors_per_block, nr_blocks;
-
-       sectors_per_block = blocksize >> 9;
-       nr_blocks = nr_sectors / sectors_per_block;
-       start /= sectors_per_block;
-
-       for (i = 0; i < nr_blocks; i++)
-               blocks[i] = start++;
-}
-
-static inline int get_blksize(kdev_t dev)
-{
-       int correct_size = BLOCK_SIZE, i, major;
-
-       major = MAJOR(dev);
-       if (blksize_size[major])
-       {
-               i = blksize_size[major][MINOR(dev)];
-               if (i)
-                       correct_size = i;
-       }
-       return correct_size;
-}
-
-#ifdef DEBUG_SNAPSHOT
-static inline void invalidate_snap_cache(unsigned long start, unsigned long nr,
-                                        kdev_t dev)
-{
-       struct buffer_head * bh;
-       int sectors_per_block, i, blksize, minor;
-
-       minor = MINOR(dev);
-       blksize = lvm_blocksizes[minor];
-       sectors_per_block = blksize >> 9;
-       nr /= sectors_per_block;
-       start /= sectors_per_block;
-
-       for (i = 0; i < nr; i++)
-       {
-               bh = get_hash_table(dev, start++, blksize);
-               if (bh)
-                       bforget(bh);
-       }
-}
-#endif
-
-/*
- * copy on write handler for one snapshot logical volume
- *
- * read the original blocks and store it/them on the new one(s).
- * if there is no exception storage space free any longer --> release snapshot.
- *
- * this routine gets called for each _first_ write to a physical chunk.
- */
-int lvm_snapshot_COW(kdev_t org_phys_dev,
-                    unsigned long org_phys_sector,
-                    unsigned long org_pe_start,
-                    unsigned long org_virt_sector,
-                    lv_t * lv_snap)
-{
-       const char * reason;
-       unsigned long org_start, snap_start, virt_start, pe_off;
-       int idx = lv_snap->lv_remap_ptr, chunk_size = lv_snap->lv_chunk_size;
-       kdev_t snap_phys_dev;
-       struct kiobuf * iobuf;
-       unsigned long blocks[KIO_MAX_SECTORS];
-       int blksize_snap, blksize_org, min_blksize, max_blksize;
-       int max_sectors, nr_sectors;
-
-       /* check if we are out of snapshot space */
-       if (idx >= lv_snap->lv_remap_end)
-               goto fail_out_of_space;
-
-       /* calculate physical boundaries of source chunk */
-       pe_off = org_pe_start % chunk_size;
-       org_start = org_phys_sector - ((org_phys_sector-pe_off) % chunk_size);
-       virt_start = org_virt_sector - (org_phys_sector - org_start);
-
-       /* calculate physical boundaries of destination chunk */
-       snap_phys_dev = lv_snap->lv_block_exception[idx].rdev_new;
-       snap_start = lv_snap->lv_block_exception[idx].rsector_new;
-
-#ifdef DEBUG_SNAPSHOT
-       printk(KERN_INFO
-              "%s -- COW: "
-              "org %02d:%02d faulting %lu start %lu, "
-              "snap %02d:%02d start %lu, "
-              "size %d, pe_start %lu pe_off %lu, virt_sec %lu\n",
-              lvm_name,
-              MAJOR(org_phys_dev), MINOR(org_phys_dev), org_phys_sector,
-              org_start,
-              MAJOR(snap_phys_dev), MINOR(snap_phys_dev), snap_start,
-              chunk_size,
-              org_pe_start, pe_off,
-              org_virt_sector);
-#endif
-
-       iobuf = lv_snap->lv_iobuf;
-
-       blksize_org = get_blksize(org_phys_dev);
-       blksize_snap = get_blksize(snap_phys_dev);
-       max_blksize = max(blksize_org, blksize_snap);
-       min_blksize = min(blksize_org, blksize_snap);
-       max_sectors = KIO_MAX_SECTORS * (min_blksize>>9);
-
-       if (chunk_size % (max_blksize>>9))
-               goto fail_blksize;
-
-       while (chunk_size)
-       {
-               nr_sectors = min(chunk_size, max_sectors);
-               chunk_size -= nr_sectors;
-
-               iobuf->length = nr_sectors << 9;
-
-               lvm_snapshot_prepare_blocks(blocks, org_start,
-                                           nr_sectors, blksize_org);
-               if (brw_kiovec(READ, 1, &iobuf, org_phys_dev,
-                              blocks, blksize_org) != (nr_sectors<<9))
-                       goto fail_raw_read;
-
-               lvm_snapshot_prepare_blocks(blocks, snap_start,
-                                           nr_sectors, blksize_snap);
-               if (brw_kiovec(WRITE, 1, &iobuf, snap_phys_dev,
-                              blocks, blksize_snap) != (nr_sectors<<9))
-                       goto fail_raw_write;
-       }
-
-#ifdef DEBUG_SNAPSHOT
-       /* invalidate the logcial snapshot buffer cache */
-       invalidate_snap_cache(virt_start, lv_snap->lv_chunk_size,
-                             lv_snap->lv_dev);
-#endif
-
-       /* the original chunk is now stored on the snapshot volume
-          so update the execption table */
-       lv_snap->lv_block_exception[idx].rdev_org = org_phys_dev;
-       lv_snap->lv_block_exception[idx].rsector_org = org_start;
-       lvm_hash_link(lv_snap->lv_block_exception + idx,
-                     org_phys_dev, org_start, lv_snap);
-       lv_snap->lv_remap_ptr = idx + 1;
-       return 1;
-
-       /* slow path */
- out:
-       lvm_drop_snapshot(lv_snap, reason);
-       return -1;
-
- fail_out_of_space:
-       reason = "out of space";
-       goto out;
- fail_raw_read:
-       reason = "read error";
-       goto out;
- fail_raw_write:
-       reason = "write error";
-       goto out;
- fail_blksize:
-       reason = "blocksize error";
-       goto out;
-}
-
-static int lvm_snapshot_alloc_iobuf_pages(struct kiobuf * iobuf, int sectors)
-{
-       int bytes, nr_pages, err, i;
-
-       bytes = sectors << 9;
-       nr_pages = (bytes + ~PAGE_MASK) >> PAGE_SHIFT;
-       err = expand_kiobuf(iobuf, nr_pages);
-       if (err)
-               goto out;
-
-       err = -ENOMEM;
-       iobuf->locked = 1;
-       iobuf->nr_pages = 0;
-       for (i = 0; i < nr_pages; i++)
-       {
-               struct page * page;
-
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,27)
-               page = alloc_page(GFP_KERNEL);
-               if (!page)
-                       goto out;
-#else
-               {
-                       unsigned long addr = __get_free_page(GFP_USER);
-                       if (!addr)
-                               goto out;
-                       iobuf->pagelist[i] = addr;
-                       page = virt_to_page(addr);
-               }
-#endif
-
-               iobuf->maplist[i] = page;
-               /* the only point to lock the page here is to be allowed
-                  to share unmap_kiobuf() in the fail-path */
-#ifndef LockPage
-#define LockPage(map) set_bit(PG_locked, &(map)->flags)
-#endif
-               LockPage(page);
-               iobuf->nr_pages++;
-       }
-       iobuf->offset = 0;
-
-       err = 0;
- out:
-       return err;
-}
-
-static int calc_max_buckets(void)
-{
-       unsigned long mem;
-
-       mem = num_physpages << PAGE_SHIFT;
-       mem /= 100;
-       mem *= 2;
-       mem /= sizeof(struct list_head);
-
-       return mem;
-}
-
-static int lvm_snapshot_alloc_hash_table(lv_t * lv)
-{
-       int err;
-       unsigned long buckets, max_buckets, size;
-       struct list_head * hash;
-
-       buckets = lv->lv_remap_end;
-       max_buckets = calc_max_buckets();
-       buckets = min(buckets, max_buckets);
-       while (buckets & (buckets-1))
-               buckets &= (buckets-1);
-
-       size = buckets * sizeof(struct list_head);
-
-       err = -ENOMEM;
-       hash = vmalloc(size);
-       lv->lv_snapshot_hash_table = hash;
-
-       if (!hash)
-               goto out;
-
-       lv->lv_snapshot_hash_mask = buckets-1;
-       while (buckets--)
-               INIT_LIST_HEAD(hash+buckets);
-       err = 0;
- out:
-       return err;
-}
-
-int lvm_snapshot_alloc(lv_t * lv_snap)
-{
-       int err, blocksize, max_sectors;
-
-       err = alloc_kiovec(1, &lv_snap->lv_iobuf);
-       if (err)
-               goto out;
-
-       blocksize = lvm_blocksizes[MINOR(lv_snap->lv_dev)];
-       max_sectors = KIO_MAX_SECTORS << (PAGE_SHIFT-9);
-
-       err = lvm_snapshot_alloc_iobuf_pages(lv_snap->lv_iobuf, max_sectors);
-       if (err)
-               goto out_free_kiovec;
-
-       err = lvm_snapshot_alloc_hash_table(lv_snap);
-       if (err)
-               goto out_free_kiovec;
- out:
-       return err;
-
- out_free_kiovec:
-       unmap_kiobuf(lv_snap->lv_iobuf);
-       free_kiovec(1, &lv_snap->lv_iobuf);
-       goto out;
-}
-
-void lvm_snapshot_release(lv_t * lv)
-{
-       if (lv->lv_block_exception)
-       {
-               vfree(lv->lv_block_exception);
-               lv->lv_block_exception = NULL;
-       }
-       if (lv->lv_snapshot_hash_table)
-       {
-               vfree(lv->lv_snapshot_hash_table);
-               lv->lv_snapshot_hash_table = NULL;
-       }
-       if (lv->lv_iobuf)
-       {
-               free_kiovec(1, &lv->lv_iobuf);
-               lv->lv_iobuf = NULL;
-       }
-}
diff --git a/drivers/block/lvm.c b/drivers/block/lvm.c
deleted file mode 100644 (file)
index 239ed99..0000000
+++ /dev/null
@@ -1,2567 +0,0 @@
-/*
- * kernel/lvm.c
- *
- * Copyright (C) 1997 - 2000  Heinz Mauelshagen, Germany
- *
- * February-November 1997
- * April-May,July-August,November 1998
- * January-March,May,July,September,October 1999
- * January,February 2000
- *
- *
- * LVM driver is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- * 
- * LVM driver is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- * 
- * You should have received a copy of the GNU General Public License
- * along with GNU CC; see the file COPYING.  If not, write to
- * the Free Software Foundation, 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA. 
- *
- */
-
-/*
- * Changelog
- *
- *    09/11/1997 - added chr ioctls VG_STATUS_GET_COUNT
- *                 and VG_STATUS_GET_NAMELIST
- *    18/01/1998 - change lvm_chr_open/close lock handling
- *    30/04/1998 - changed LV_STATUS ioctl to LV_STATUS_BYNAME and
- *               - added   LV_STATUS_BYINDEX ioctl
- *               - used lvm_status_byname_req_t and
- *                      lvm_status_byindex_req_t vars
- *    04/05/1998 - added multiple device support
- *    08/05/1998 - added support to set/clear extendable flag in volume group
- *    09/05/1998 - changed output of lvm_proc_get_info() because of
- *                 support for free (eg. longer) logical volume names
- *    12/05/1998 - added spin_locks (thanks to Pascal van Dam
- *                 <pascal@ramoth.xs4all.nl>)
- *    25/05/1998 - fixed handling of locked PEs in lvm_map() and lvm_chr_ioctl()
- *    26/05/1998 - reactivated verify_area by access_ok
- *    07/06/1998 - used vmalloc/vfree instead of kmalloc/kfree to go
- *                 beyond 128/256 KB max allocation limit per call
- *               - #ifdef blocked spin_lock calls to avoid compile errors
- *                 with 2.0.x
- *    11/06/1998 - another enhancement to spinlock code in lvm_chr_open()
- *                 and use of LVM_VERSION_CODE instead of my own macros
- *                 (thanks to  Michael Marxmeier <mike@msede.com>)
- *    07/07/1998 - added statistics in lvm_map()
- *    08/07/1998 - saved statistics in lvm_do_lv_extend_reduce()
- *    25/07/1998 - used __initfunc macro
- *    02/08/1998 - changes for official char/block major numbers
- *    07/08/1998 - avoided init_module() and cleanup_module() to be static
- *    30/08/1998 - changed VG lv_open counter from sum of LV lv_open counters
- *                 to sum of LVs open (no matter how often each is)
- *    01/09/1998 - fixed lvm_gendisk.part[] index error
- *    07/09/1998 - added copying of lv_current_pe-array
- *                 in LV_STATUS_BYINDEX ioctl
- *    17/11/1998 - added KERN_* levels to printk
- *    13/01/1999 - fixed LV index bug in lvm_do_lv_create() which hit lvrename
- *    07/02/1999 - fixed spinlock handling bug in case of LVM_RESET
- *                 by moving spinlock code from lvm_chr_open()
- *                 to lvm_chr_ioctl()
- *               - added LVM_LOCK_LVM ioctl to lvm_chr_ioctl()
- *               - allowed LVM_RESET and retrieval commands to go ahead;
- *                 only other update ioctls are blocked now
- *               - fixed pv->pe to NULL for pv_status
- *               - using lv_req structure in lvm_chr_ioctl() now
- *               - fixed NULL ptr reference bug in lvm_do_lv_extend_reduce()
- *                 caused by uncontiguous PV array in lvm_chr_ioctl(VG_REDUCE)
- *    09/02/1999 - changed BLKRASET and BLKRAGET in lvm_chr_ioctl() to
- *                 handle lgoical volume private read ahead sector
- *               - implemented LV read_ahead handling with lvm_blk_read()
- *                 and lvm_blk_write()
- *    10/02/1999 - implemented 2.[12].* support function lvm_hd_name()
- *                 to be used in drivers/block/genhd.c by disk_name()
- *    12/02/1999 - fixed index bug in lvm_blk_ioctl(), HDIO_GETGEO
- *               - enhanced gendisk insert/remove handling
- *    16/02/1999 - changed to dynamic block minor number allocation to
- *                 have as much as 99 volume groups with 256 logical volumes
- *                 as the grand total; this allows having 1 volume group with
- *                 up to 256 logical volumes in it
- *    21/02/1999 - added LV open count information to proc filesystem
- *               - substituted redundant LVM_RESET code by calls
- *                 to lvm_do_vg_remove()
- *    22/02/1999 - used schedule_timeout() to be more responsive
- *                 in case of lvm_do_vg_remove() with lots of logical volumes
- *    19/03/1999 - fixed NULL pointer bug in module_init/lvm_init
- *    17/05/1999 - used DECLARE_WAIT_QUEUE_HEAD macro (>2.3.0)
- *               - enhanced lvm_hd_name support
- *    03/07/1999 - avoided use of KERNEL_VERSION macro based ifdefs and
- *                 memcpy_tofs/memcpy_fromfs macro redefinitions
- *    06/07/1999 - corrected reads/writes statistic counter copy in case
- *                 of striped logical volume
- *    28/07/1999 - implemented snapshot logical volumes
- *                 - lvm_chr_ioctl
- *                   - LV_STATUS_BYINDEX
- *                   - LV_STATUS_BYNAME
- *                 - lvm_do_lv_create
- *                 - lvm_do_lv_remove
- *                 - lvm_map
- *                 - new lvm_snapshot_remap_block
- *                 - new lvm_snapshot_remap_new_block
- *    08/10/1999 - implemented support for multiple snapshots per
- *                 original logical volume
- *    12/10/1999 - support for 2.3.19
- *    11/11/1999 - support for 2.3.28
- *    21/11/1999 - changed lvm_map() interface to buffer_head based
- *    19/12/1999 - support for 2.3.33
- *    01/01/2000 - changed locking concept in lvm_map(),
- *                 lvm_do_vg_create() and lvm_do_lv_remove()
- *    15/01/2000 - fixed PV_FLUSH bug in lvm_chr_ioctl()
- *    24/01/2000 - ported to 2.3.40 including Alan Cox's pointer changes etc.
- *    29/01/2000 - used kmalloc/kfree again for all small structures
- *    20/01/2000 - cleaned up lvm_chr_ioctl by moving code
- *                 to seperated functions
- *               - avoided "/dev/" in proc filesystem output
- *               - avoided inline strings functions lvm_strlen etc.
- *    14/02/2000 - support for 2.3.43
- *               - integrated Andrea Arcangeli's snapshot code
- *
- */
-
-
-static char *lvm_version = "LVM version 0.8final  by Heinz Mauelshagen  (15/02/2000)\n";
-static char *lvm_short_version = "version 0.8final  (15/02/2000)";
-
-#define MAJOR_NR       LVM_BLK_MAJOR
-#define        DEVICE_OFF(device)
-
-#include <linux/config.h>
-#include <linux/version.h>
-
-#ifdef MODVERSIONS
-#undef MODULE
-#define MODULE
-#include <linux/modversions.h>
-#endif
-
-#include <linux/module.h>
-
-#include <linux/kernel.h>
-#include <linux/vmalloc.h>
-#include <linux/slab.h>
-#include <linux/init.h>
-
-#include <linux/hdreg.h>
-#include <linux/stat.h>
-#include <linux/fs.h>
-#include <linux/proc_fs.h>
-#include <linux/blkdev.h>
-#include <linux/genhd.h>
-#include <linux/locks.h>
-#include <linux/smp_lock.h>
-#include <asm/ioctl.h>
-#include <asm/segment.h>
-#include <asm/uaccess.h>
-
-#ifdef CONFIG_KERNELD
-#include <linux/kerneld.h>
-#endif
-
-#define LOCAL_END_REQUEST
-
-#include <linux/blk.h>
-#include <linux/blkpg.h>
-
-#include <linux/errno.h>
-#include <linux/lvm.h>
-
-#define        LVM_CORRECT_READ_AHEAD(a)                               \
-       (((a) < LVM_MIN_READ_AHEAD || (a) > LVM_MAX_READ_AHEAD) \
-        ? LVM_MAX_READ_AHEAD : (a))
-
-#ifndef WRITEA
-#  define WRITEA WRITE
-#endif
-
-/*
- * External function prototypes
- */
-#ifdef MODULE
-int init_module(void);
-void cleanup_module(void);
-#else
-extern int lvm_init(void);
-#endif
-
-static void lvm_dummy_device_request(request_queue_t *);
-#define        DEVICE_REQUEST  lvm_dummy_device_request
-
-static int lvm_make_request_fn(request_queue_t *, int, struct buffer_head*);
-static void lvm_plug_device_noop(request_queue_t *, kdev_t);
-
-static int lvm_blk_ioctl(struct inode *, struct file *, uint, ulong);
-static int lvm_blk_open(struct inode *, struct file *);
-
-static int lvm_chr_open(struct inode *, struct file *);
-
-static int lvm_chr_close(struct inode *, struct file *);
-static int lvm_blk_close(struct inode *, struct file *);
-
-static int lvm_chr_ioctl(struct inode *, struct file *, uint, ulong);
-
-#if defined CONFIG_LVM_PROC_FS && defined CONFIG_PROC_FS
-static int lvm_proc_get_info(char *, char **, off_t, int);
-static int (*lvm_proc_get_info_ptr) (char *, char **, off_t, int) =
-&lvm_proc_get_info;
-#endif
-
-#ifdef LVM_HD_NAME
-void lvm_hd_name(char *, int);
-#endif
-/* End external function prototypes */
-
-
-/*
- * Internal function prototypes
- */
-static void lvm_init_vars(void);
-
-/* external snapshot calls */
-int lvm_snapshot_remap_block(kdev_t *, ulong *, ulong, lv_t *);
-int lvm_snapshot_COW(kdev_t, ulong, ulong, ulong, lv_t *);
-int lvm_snapshot_alloc(lv_t *);
-void lvm_snapshot_release(lv_t *); 
-
-#ifdef LVM_HD_NAME
-extern void (*lvm_hd_name_ptr) (char *, int);
-#endif
-static int lvm_map(struct buffer_head *, int);
-static int lvm_do_lock_lvm(void);
-static int lvm_do_le_remap(vg_t *, void *);
-static int lvm_do_pe_lock_unlock(vg_t *r, void *);
-static int lvm_do_vg_create(int, void *);
-static int lvm_do_vg_extend(vg_t *, void *);
-static int lvm_do_vg_reduce(vg_t *, void *);
-static int lvm_do_vg_remove(int);
-static int lvm_do_lv_create(int, char *, lv_t *);
-static int lvm_do_lv_remove(int, char *, int);
-static int lvm_do_lv_extend_reduce(int, char *, lv_t *);
-static int lvm_do_lv_status_byname(vg_t *r, void *);
-static int lvm_do_lv_status_byindex(vg_t *, void *arg);
-static int lvm_do_pv_change(vg_t*, void*);
-static int lvm_do_pv_status(vg_t *, void *);
-static void lvm_geninit(struct gendisk *);
-#ifdef LVM_GET_INODE
-static struct inode *lvm_get_inode(kdev_t);
-void lvm_clear_inode(struct inode *);
-#endif
-/* END Internal function prototypes */
-
-
-/* volume group descriptor area pointers */
-static vg_t *vg[ABS_MAX_VG];
-static pv_t *pvp = NULL;
-static lv_t *lvp = NULL;
-static pe_t *pep = NULL;
-static pe_t *pep1 = NULL;
-
-
-/* map from block minor number to VG and LV numbers */
-typedef struct {
-       int vg_number;
-       int lv_number;
-} vg_lv_map_t;
-static vg_lv_map_t vg_lv_map[ABS_MAX_LV];
-
-
-/* Request structures (lvm_chr_ioctl()) */
-static pv_change_req_t pv_change_req;
-static pv_flush_req_t pv_flush_req;
-static pv_status_req_t pv_status_req;
-static pe_lock_req_t pe_lock_req;
-static le_remap_req_t le_remap_req;
-static lv_req_t lv_req;
-
-#ifdef LVM_TOTAL_RESET
-static int lvm_reset_spindown = 0;
-#endif
-
-static char pv_name[NAME_LEN];
-/* static char rootvg[NAME_LEN] = { 0, }; */
-static uint lv_open = 0;
-const char *const lvm_name = LVM_NAME;
-static int lock = 0;
-static int loadtime = 0;
-static uint vg_count = 0;
-static long lvm_chr_open_count = 0;
-static ushort lvm_iop_version = LVM_DRIVER_IOP_VERSION;
-static DECLARE_WAIT_QUEUE_HEAD(lvm_snapshot_wait);
-static DECLARE_WAIT_QUEUE_HEAD(lvm_wait);
-static DECLARE_WAIT_QUEUE_HEAD(lvm_map_wait);
-
-static spinlock_t lvm_lock = SPIN_LOCK_UNLOCKED;
-
-static devfs_handle_t lvm_devfs_handle;
-static devfs_handle_t vg_devfs_handle[MAX_VG];
-static devfs_handle_t ch_devfs_handle[MAX_VG];
-static devfs_handle_t lv_devfs_handle[MAX_LV];
-
-static struct file_operations lvm_chr_fops =
-{
-       owner:          THIS_MODULE,
-       open:           lvm_chr_open,
-       release:        lvm_chr_close,
-       ioctl:          lvm_chr_ioctl,
-};
-
-static struct block_device_operations lvm_blk_dops =
-{
-       open:           lvm_blk_open,
-       release:        lvm_blk_close,
-       ioctl:          lvm_blk_ioctl
-};
-
-/* gendisk structures */
-static struct hd_struct lvm_hd_struct[MAX_LV];
-static int lvm_blocksizes[MAX_LV] =
-{0,};
-static int lvm_size[MAX_LV] =
-{0,};
-static struct gendisk lvm_gendisk =
-{
-       MAJOR_NR,               /* major # */
-       LVM_NAME,               /* name of major */
-       0,                      /* number of times minor is shifted
-                                  to get real minor */
-       1,                      /* maximum partitions per device */
-       lvm_hd_struct,          /* partition table */
-       lvm_size,               /* device size in blocks, copied
-                                  to block_size[] */
-       MAX_LV,                 /* number or real devices */
-       NULL,                   /* internal */
-       NULL,                   /* pointer to next gendisk struct (internal) */
-};
-
-
-#ifdef MODULE
-/*
- * Module initialization...
- */
-int init_module(void)
-#else
-/*
- * Driver initialization...
- */
-#ifdef __initfunc
-__initfunc(int lvm_init(void))
-#else
-int __init lvm_init(void)
-#endif
-#endif                         /* #ifdef MODULE */
-{
-       struct gendisk *gendisk_ptr = NULL;
-
-       if (register_chrdev(LVM_CHAR_MAJOR, lvm_name, &lvm_chr_fops) < 0) {
-               printk(KERN_ERR "%s -- register_chrdev failed\n", lvm_name);
-               return -EIO;
-       }
-       if (register_blkdev(MAJOR_NR, lvm_name, &lvm_blk_dops) < 0) {
-               printk("%s -- register_blkdev failed\n", lvm_name);
-               if (unregister_chrdev(LVM_CHAR_MAJOR, lvm_name) < 0)
-                       printk(KERN_ERR "%s -- unregister_chrdev failed\n", lvm_name);
-               return -EIO;
-       }
-
-       lvm_devfs_handle = devfs_register(
-               0 , "lvm", 0, 0, LVM_CHAR_MAJOR,
-               S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP,
-               &lvm_chr_fops, NULL);
-
-#if defined CONFIG_LVM_PROC_FS && defined CONFIG_PROC_FS
-       create_proc_info_entry(LVM_NAME, S_IFREG | S_IRUGO,
-                              &proc_root, lvm_proc_get_info_ptr);
-#endif
-
-       lvm_init_vars();
-       lvm_geninit(&lvm_gendisk);
-
-       /* insert our gendisk at the corresponding major */
-       if (gendisk_head != NULL) {
-               gendisk_ptr = gendisk_head;
-               while (gendisk_ptr->next != NULL &&
-                      gendisk_ptr->major > lvm_gendisk.major) {
-                       gendisk_ptr = gendisk_ptr->next;
-               }
-               lvm_gendisk.next = gendisk_ptr->next;
-               gendisk_ptr->next = &lvm_gendisk;
-       } else {
-               gendisk_head = &lvm_gendisk;
-               lvm_gendisk.next = NULL;
-       }
-
-#ifdef LVM_HD_NAME
-       /* reference from drivers/block/genhd.c */
-       lvm_hd_name_ptr = lvm_hd_name;
-#endif
-
-       blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST);
-       blk_queue_make_request(BLK_DEFAULT_QUEUE(MAJOR_NR), lvm_make_request_fn);
-       blk_queue_pluggable(BLK_DEFAULT_QUEUE(MAJOR_NR), lvm_plug_device_noop);
-       /* optional read root VGDA */
-/*
-   if ( *rootvg != 0) vg_read_with_pv_and_lv ( rootvg, &vg);
-*/
-
-       printk(KERN_INFO
-              "%s%s -- "
-#ifdef MODULE
-              "Module"
-#else
-              "Driver"
-#endif
-              " successfully initialized\n",
-              lvm_version, lvm_name);
-
-       return 0;
-} /* init_module() / lvm_init() */
-
-
-#ifdef MODULE
-/*
- * Module cleanup...
- */
-void cleanup_module(void)
-{
-       struct gendisk *gendisk_ptr = NULL, *gendisk_ptr_prev = NULL;
-
-       devfs_unregister (lvm_devfs_handle);
-
-       if (unregister_chrdev(LVM_CHAR_MAJOR, lvm_name) < 0) {
-               printk(KERN_ERR "%s -- unregister_chrdev failed\n", lvm_name);
-       }
-       if (unregister_blkdev(MAJOR_NR, lvm_name) < 0) {
-               printk(KERN_ERR "%s -- unregister_blkdev failed\n", lvm_name);
-       }
-       blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
-
-       gendisk_ptr = gendisk_ptr_prev = gendisk_head;
-       while (gendisk_ptr != NULL) {
-               if (gendisk_ptr == &lvm_gendisk)
-                       break;
-               gendisk_ptr_prev = gendisk_ptr;
-               gendisk_ptr = gendisk_ptr->next;
-       }
-       /* delete our gendisk from chain */
-       if (gendisk_ptr == &lvm_gendisk)
-               gendisk_ptr_prev->next = gendisk_ptr->next;
-
-       blk_size[MAJOR_NR] = NULL;
-       blksize_size[MAJOR_NR] = NULL;
-
-#if defined CONFIG_LVM_PROC_FS && defined CONFIG_PROC_FS
-       remove_proc_entry(LVM_NAME, &proc_root);
-#endif
-
-#ifdef LVM_HD_NAME
-       /* reference from linux/drivers/block/genhd.c */
-       lvm_hd_name_ptr = NULL;
-#endif
-
-       printk(KERN_INFO "%s -- Module successfully deactivated\n", lvm_name);
-
-       return;
-}      /* void cleanup_module() */
-#endif /* #ifdef MODULE */
-
-
-/*
- * support function to initialize lvm variables
- */
-#ifdef __initfunc
-__initfunc(void lvm_init_vars(void))
-#else
-void __init lvm_init_vars(void)
-#endif
-{
-       int v;
-
-       loadtime = CURRENT_TIME;
-
-       pe_lock_req.lock = UNLOCK_PE;
-       pe_lock_req.data.lv_dev = pe_lock_req.data.pv_dev = 0;
-       pe_lock_req.data.pv_offset = 0;
-
-       /* Initialize VG pointers */
-       for (v = 0; v < ABS_MAX_VG; v++) vg[v] = NULL;
-
-       /* Initialize LV -> VG association */
-       for (v = 0; v < ABS_MAX_LV; v++) {
-               /* index ABS_MAX_VG never used for real VG */
-               vg_lv_map[v].vg_number = ABS_MAX_VG;
-               vg_lv_map[v].lv_number = -1;
-       }
-
-       return;
-} /* lvm_init_vars() */
-
-
-/********************************************************************
- *
- * Character device functions
- *
- ********************************************************************/
-
-/*
- * character device open routine
- */
-static int lvm_chr_open(struct inode *inode,
-                       struct file *file)
-{
-       int minor = MINOR(inode->i_rdev);
-
-#ifdef DEBUG
-       printk(KERN_DEBUG
-        "%s -- lvm_chr_open MINOR: %d  VG#: %d  mode: 0x%X  lock: %d\n",
-              lvm_name, minor, VG_CHR(minor), file->f_mode, lock);
-#endif
-
-       /* super user validation */
-       if (!capable(CAP_SYS_ADMIN)) return -EACCES;
-
-       /* Group special file open */
-       if (VG_CHR(minor) > MAX_VG) return -ENXIO;
-
-       lvm_chr_open_count++;
-       return 0;
-} /* lvm_chr_open() */
-
-
-/*
- * character device i/o-control routine
- *
- * Only one changing process can do changing ioctl at one time,
- * others will block.
- *
- */
-static int lvm_chr_ioctl(struct inode *inode, struct file *file,
-                        uint command, ulong a)
-{
-       int minor = MINOR(inode->i_rdev);
-       uint extendable, l, v;
-       void *arg = (void *) a;
-       lv_t lv;
-       vg_t* vg_ptr = vg[VG_CHR(minor)];
-
-       /* otherwise cc will complain about unused variables */
-       (void) lvm_lock;
-
-
-#ifdef DEBUG_IOCTL
-       printk(KERN_DEBUG
-              "%s -- lvm_chr_ioctl: command: 0x%X  MINOR: %d  "
-              "VG#: %d  mode: 0x%X\n",
-              lvm_name, command, minor, VG_CHR(minor), file->f_mode);
-#endif
-
-#ifdef LVM_TOTAL_RESET
-       if (lvm_reset_spindown > 0) return -EACCES;
-#endif
-
-       /* Main command switch */
-       switch (command) {
-       case LVM_LOCK_LVM:
-               /* lock the LVM */
-               return lvm_do_lock_lvm();
-
-       case LVM_GET_IOP_VERSION:
-               /* check lvm version to ensure driver/tools+lib
-                  interoperability */
-               if (copy_to_user(arg, &lvm_iop_version, sizeof(ushort)) != 0)
-                       return -EFAULT;
-               return 0;
-
-#ifdef LVM_TOTAL_RESET
-       case LVM_RESET:
-               /* lock reset function */
-               lvm_reset_spindown = 1;
-               for (v = 0; v < ABS_MAX_VG; v++) {
-                       if (vg[v] != NULL) lvm_do_vg_remove(v);
-               }
-
-#ifdef MODULE
-               while (GET_USE_COUNT(&__this_module) < 1)
-                       MOD_INC_USE_COUNT;
-               while (GET_USE_COUNT(&__this_module) > 1)
-                       MOD_DEC_USE_COUNT;
-#endif                         /* MODULE */
-               lock = 0;       /* release lock */
-               wake_up_interruptible(&lvm_wait);
-               return 0;
-#endif /* LVM_TOTAL_RESET */
-
-
-       case LE_REMAP:
-               /* remap a logical extent (after moving the physical extent) */
-               return lvm_do_le_remap(vg_ptr,arg);
-
-       case PE_LOCK_UNLOCK:
-               /* lock/unlock i/o to a physical extent to move it to another
-                  physical volume (move's done in user space's pvmove) */
-               return lvm_do_pe_lock_unlock(vg_ptr,arg);
-
-       case VG_CREATE:
-               /* create a VGDA */
-               return lvm_do_vg_create(minor, arg);
-
-       case VG_REMOVE:
-               /* remove an inactive VGDA */
-               return lvm_do_vg_remove(minor);
-
-       case VG_EXTEND:
-               /* extend a volume group */
-               return lvm_do_vg_extend(vg_ptr,arg);
-
-       case VG_REDUCE:
-               /* reduce a volume group */
-               return lvm_do_vg_reduce(vg_ptr,arg);
-
-
-       case VG_SET_EXTENDABLE:
-               /* set/clear extendability flag of volume group */
-               if (vg_ptr == NULL) return -ENXIO;
-               if (copy_from_user(&extendable, arg, sizeof(extendable)) != 0)
-                       return -EFAULT;
-
-               if (extendable == VG_EXTENDABLE ||
-                   extendable == ~VG_EXTENDABLE) {
-                       if (extendable == VG_EXTENDABLE)
-                               vg_ptr->vg_status |= VG_EXTENDABLE;
-                       else
-                               vg_ptr->vg_status &= ~VG_EXTENDABLE;
-               } else return -EINVAL;
-               return 0;
-
-
-       case VG_STATUS:
-               /* get volume group data (only the vg_t struct) */
-               if (vg_ptr == NULL) return -ENXIO;
-               if (copy_to_user(arg, vg_ptr, sizeof(vg_t)) != 0)
-                       return -EFAULT;
-               return 0;
-
-
-       case VG_STATUS_GET_COUNT:
-               /* get volume group count */
-               if (copy_to_user(arg, &vg_count, sizeof(vg_count)) != 0)
-                       return -EFAULT;
-               return 0;
-
-
-       case VG_STATUS_GET_NAMELIST:
-               /* get volume group count */
-               for (l = v = 0; v < ABS_MAX_VG; v++) {
-                       if (vg[v] != NULL) {
-                               if (copy_to_user(arg + l++ * NAME_LEN,
-                                                vg[v]->vg_name,
-                                                NAME_LEN) != 0)
-                                       return -EFAULT;
-                       }
-               }
-               return 0;
-
-
-       case LV_CREATE:
-       case LV_REMOVE:
-       case LV_EXTEND:
-       case LV_REDUCE:
-               /* create, remove, extend or reduce a logical volume */
-               if (vg_ptr == NULL) return -ENXIO;
-               if (copy_from_user(&lv_req, arg, sizeof(lv_req)) != 0)
-                       return -EFAULT;
-
-               if (command != LV_REMOVE) {
-                       if (copy_from_user(&lv, lv_req.lv, sizeof(lv_t)) != 0)
-                               return -EFAULT;
-               }
-               switch (command) {
-               case LV_CREATE:
-                       return lvm_do_lv_create(minor, lv_req.lv_name, &lv);
-
-               case LV_REMOVE:
-                       return lvm_do_lv_remove(minor, lv_req.lv_name, -1);
-
-               case LV_EXTEND:
-               case LV_REDUCE:
-                       return lvm_do_lv_extend_reduce(minor, lv_req.lv_name, &lv);
-               }
-
-
-       case LV_STATUS_BYNAME:
-               /* get status of a logical volume by name */
-               return lvm_do_lv_status_byname(vg_ptr,arg);
-
-       case LV_STATUS_BYINDEX:
-               /* get status of a logical volume by index */
-               return lvm_do_lv_status_byindex(vg_ptr,arg);
-
-       case PV_CHANGE:
-               /* change a physical volume */
-               return lvm_do_pv_change(vg_ptr,arg);
-
-       case PV_STATUS:
-               /* get physical volume data (pv_t structure only) */
-               return lvm_do_pv_status(vg_ptr,arg);
-
-       case PV_FLUSH:
-               /* physical volume buffer flush/invalidate */
-               if (copy_from_user(&pv_flush_req, arg,
-                                  sizeof(pv_flush_req)) != 0)
-                       return -EFAULT;
-
-               for ( v = 0; v < ABS_MAX_VG; v++) {
-                       unsigned int p;
-                       if ( vg[v] == NULL) continue;
-                       for ( p = 0; p < vg[v]->pv_max; p++) {
-                               if ( vg[v]->pv[p] != NULL &&
-                                    strcmp ( vg[v]->pv[p]->pv_name,
-                                             pv_flush_req.pv_name) == 0) {
-                                       fsync_dev ( vg[v]->pv[p]->pv_dev);
-                                       invalidate_buffers ( vg[v]->pv[p]->pv_dev);
-                                       return 0;
-                               }
-                       }
-               }
-               return 0;
-
-       default:
-               printk(KERN_WARNING
-                      "%s -- lvm_chr_ioctl: unknown command %x\n",
-                      lvm_name, command);
-               return -EINVAL;
-       }
-
-       return 0;
-} /* lvm_chr_ioctl */
-
-
-/*
- * character device close routine
- */
-static int lvm_chr_close(struct inode *inode, struct file *file)
-{
-#ifdef DEBUG
-       int minor = MINOR(inode->i_rdev);
-       printk(KERN_DEBUG
-            "%s -- lvm_chr_close   VG#: %d\n", lvm_name, VG_CHR(minor));
-#endif
-
-       lock_kernel();
-#ifdef LVM_TOTAL_RESET
-       if (lvm_reset_spindown > 0) {
-               lvm_reset_spindown = 0;
-               lvm_chr_open_count = 1;
-       }
-#endif
-
-       if (lvm_chr_open_count > 0) lvm_chr_open_count--;
-       if (lock == current->pid) {
-               lock = 0;       /* release lock */
-               wake_up_interruptible(&lvm_wait);
-       }
-       unlock_kernel();
-
-       return 0;
-} /* lvm_chr_close() */
-
-
-
-/********************************************************************
- *
- * Block device functions
- *
- ********************************************************************/
-
-/*
- * block device open routine
- */
-static int lvm_blk_open(struct inode *inode, struct file *file)
-{
-       int minor = MINOR(inode->i_rdev);
-       lv_t *lv_ptr;
-       vg_t *vg_ptr = vg[VG_BLK(minor)];
-
-#ifdef DEBUG_LVM_BLK_OPEN
-       printk(KERN_DEBUG
-         "%s -- lvm_blk_open MINOR: %d  VG#: %d  LV#: %d  mode: 0x%X\n",
-           lvm_name, minor, VG_BLK(minor), LV_BLK(minor), file->f_mode);
-#endif
-
-#ifdef LVM_TOTAL_RESET
-       if (lvm_reset_spindown > 0)
-               return -EPERM;
-#endif
-
-       if (vg_ptr != NULL &&
-           (vg_ptr->vg_status & VG_ACTIVE) &&
-           (lv_ptr = vg_ptr->lv[LV_BLK(minor)]) != NULL &&
-           LV_BLK(minor) >= 0 &&
-           LV_BLK(minor) < vg_ptr->lv_max) {
-
-               /* Check parallel LV spindown (LV remove) */
-               if (lv_ptr->lv_status & LV_SPINDOWN) return -EPERM;
-
-               /* Check inactive LV and open for read/write */
-               if (file->f_mode & O_RDWR) {
-                       if (!(lv_ptr->lv_status & LV_ACTIVE)) return -EPERM;
-                       if (!(lv_ptr->lv_access & LV_WRITE))  return -EACCES;
-               }
-
-                /* be sure to increment VG counter */
-               if (lv_ptr->lv_open == 0) vg_ptr->lv_open++;
-               lv_ptr->lv_open++;
-
-               MOD_INC_USE_COUNT;
-
-#ifdef DEBUG_LVM_BLK_OPEN
-               printk(KERN_DEBUG
-                      "%s -- lvm_blk_open MINOR: %d  VG#: %d  LV#: %d  size: %d\n",
-                      lvm_name, minor, VG_BLK(minor), LV_BLK(minor),
-                      lv_ptr->lv_size);
-#endif
-
-               return 0;
-       }
-       return -ENXIO;
-} /* lvm_blk_open() */
-
-
-/*
- * block device i/o-control routine
- */
-static int lvm_blk_ioctl(struct inode *inode, struct file *file,
-                        uint command, ulong a)
-{
-       int minor = MINOR(inode->i_rdev);
-       vg_t *vg_ptr = vg[VG_BLK(minor)];
-       lv_t *lv_ptr = vg_ptr->lv[LV_BLK(minor)];
-       void *arg = (void *) a;
-       struct hd_geometry *hd = (struct hd_geometry *) a;
-
-#ifdef DEBUG_IOCTL
-       printk(KERN_DEBUG
-              "%s -- lvm_blk_ioctl MINOR: %d  command: 0x%X  arg: %X  "
-              "VG#: %dl  LV#: %d\n",
-              lvm_name, minor, command, (ulong) arg,
-              VG_BLK(minor), LV_BLK(minor));
-#endif
-
-       switch (command) {
-       case BLKGETSIZE:
-               /* return device size */
-#ifdef DEBUG_IOCTL
-               printk(KERN_DEBUG
-                      "%s -- lvm_blk_ioctl -- BLKGETSIZE: %u\n",
-                      lvm_name, lv_ptr->lv_size);
-#endif
-               if (put_user(lv_ptr->lv_size, (long *)arg))
-                       return -EFAULT;
-               break;
-
-
-       case BLKFLSBUF:
-               /* flush buffer cache */
-               if (!capable(CAP_SYS_ADMIN)) return -EACCES;
-
-#ifdef DEBUG_IOCTL
-               printk(KERN_DEBUG
-                      "%s -- lvm_blk_ioctl -- BLKFLSBUF\n", lvm_name);
-#endif
-               fsync_dev(inode->i_rdev);
-               invalidate_buffers(inode->i_rdev);
-               break;
-
-
-       case BLKRASET:
-               /* set read ahead for block device */
-               if (!capable(CAP_SYS_ADMIN)) return -EACCES;
-
-#ifdef DEBUG_IOCTL
-               printk(KERN_DEBUG
-                      "%s -- lvm_blk_ioctl -- BLKRASET: %d sectors for %02X:%02X\n",
-                      lvm_name, (long) arg, MAJOR(inode->i_rdev), minor);
-#endif
-               if ((long) arg < LVM_MIN_READ_AHEAD ||
-                   (long) arg > LVM_MAX_READ_AHEAD)
-                       return -EINVAL;
-               read_ahead[MAJOR_NR] = lv_ptr->lv_read_ahead = (long) arg;
-               break;
-
-
-       case BLKRAGET:
-               /* get current read ahead setting */
-#ifdef DEBUG_IOCTL
-               printk(KERN_DEBUG
-                      "%s -- lvm_blk_ioctl -- BLKRAGET\n", lvm_name);
-#endif
-               if (put_user(lv_ptr->lv_read_ahead, (long *)arg))
-                       return -EFAULT;
-               break;
-
-
-       case HDIO_GETGEO:
-               /* get disk geometry */
-#ifdef DEBUG_IOCTL
-               printk(KERN_DEBUG
-                      "%s -- lvm_blk_ioctl -- HDIO_GETGEO\n", lvm_name);
-#endif
-               if (hd == NULL)
-                       return -EINVAL;
-               {
-                       unsigned char heads = 64;
-                       unsigned char sectors = 32;
-                       long start = 0;
-                       short cylinders = lv_ptr->lv_size / heads / sectors;
-
-                       if (copy_to_user((char *) &hd->heads, &heads,
-                                        sizeof(heads)) != 0 ||
-                           copy_to_user((char *) &hd->sectors, &sectors,
-                                        sizeof(sectors)) != 0 ||
-                           copy_to_user((short *) &hd->cylinders,
-                                  &cylinders, sizeof(cylinders)) != 0 ||
-                           copy_to_user((long *) &hd->start, &start,
-                                        sizeof(start)) != 0)
-                               return -EFAULT;
-               }
-
-#ifdef DEBUG_IOCTL
-               printk(KERN_DEBUG
-                      "%s -- lvm_blk_ioctl -- cylinders: %d\n",
-                      lvm_name, lv_ptr->lv_size / heads / sectors);
-#endif
-               break;
-
-
-       case LV_SET_ACCESS:
-               /* set access flags of a logical volume */
-               if (!capable(CAP_SYS_ADMIN)) return -EACCES;
-               lv_ptr->lv_access = (ulong) arg;
-               break;
-
-
-       case LV_SET_STATUS:
-               /* set status flags of a logical volume */
-               if (!capable(CAP_SYS_ADMIN)) return -EACCES;
-               if (!((ulong) arg & LV_ACTIVE) && lv_ptr->lv_open > 1)
-                       return -EPERM;
-               lv_ptr->lv_status = (ulong) arg;
-               break;
-
-
-       case LV_SET_ALLOCATION:
-               /* set allocation flags of a logical volume */
-               if (!capable(CAP_SYS_ADMIN)) return -EACCES;
-               lv_ptr->lv_allocation = (ulong) arg;
-               break;
-
-
-       default:
-               printk(KERN_WARNING
-                      "%s -- lvm_blk_ioctl: unknown command %d\n",
-                      lvm_name, command);
-               return -EINVAL;
-       }
-
-       return 0;
-} /* lvm_blk_ioctl() */
-
-
-/*
- * block device close routine
- */
-static int lvm_blk_close(struct inode *inode, struct file *file)
-{
-       int minor = MINOR(inode->i_rdev);
-       vg_t *vg_ptr = vg[VG_BLK(minor)];
-       lv_t *lv_ptr = vg_ptr->lv[LV_BLK(minor)];
-
-#ifdef DEBUG
-       printk(KERN_DEBUG
-              "%s -- lvm_blk_close MINOR: %d  VG#: %d  LV#: %d\n",
-              lvm_name, minor, VG_BLK(minor), LV_BLK(minor));
-#endif
-
-       sync_dev(inode->i_rdev);
-       if (lv_ptr->lv_open == 1) vg_ptr->lv_open--;
-       lv_ptr->lv_open--;
-
-       MOD_DEC_USE_COUNT;
-
-       return 0;
-} /* lvm_blk_close() */
-
-
-#if defined CONFIG_LVM_PROC_FS && defined CONFIG_PROC_FS
-/*
- * Support function /proc-Filesystem
- */
-#define  LVM_PROC_BUF   ( i == 0 ? dummy_buf : &buf[sz])
-
-static int lvm_proc_get_info(char *page, char **start, off_t pos, int count)
-{
-       int c, i, l, p, v, vg_counter, pv_counter, lv_counter, lv_open_counter,
-        lv_open_total, pe_t_bytes, lv_block_exception_t_bytes, seconds;
-       static off_t sz;
-       off_t sz_last;
-       char allocation_flag, inactive_flag, rw_flag, stripes_flag;
-       char *lv_name, *pv_name;
-       static char *buf = NULL;
-       static char dummy_buf[160];     /* sized for 2 lines */
-       vg_t *vg_ptr;
-       lv_t *lv_ptr;
-       pv_t *pv_ptr;
-
-
-#ifdef DEBUG_LVM_PROC_GET_INFO
-       printk(KERN_DEBUG
-              "%s - lvm_proc_get_info CALLED  pos: %lu  count: %d  whence: %d\n",
-              lvm_name, pos, count, whence);
-#endif
-
-       if (pos == 0 || buf == NULL) {
-               sz_last = vg_counter = pv_counter = lv_counter = lv_open_counter = \
-                   lv_open_total = pe_t_bytes = lv_block_exception_t_bytes = 0;
-
-               /* search for activity */
-               for (v = 0; v < ABS_MAX_VG; v++) {
-                       if ((vg_ptr = vg[v]) != NULL) {
-                               vg_counter++;
-                               pv_counter += vg_ptr->pv_cur;
-                               lv_counter += vg_ptr->lv_cur;
-                               if (vg_ptr->lv_cur > 0) {
-                                       for (l = 0; l < vg[v]->lv_max; l++) {
-                                               if ((lv_ptr = vg_ptr->lv[l]) != NULL) {
-                                                       pe_t_bytes += lv_ptr->lv_allocated_le;
-                                                       if (lv_ptr->lv_block_exception != NULL)
-                                                               lv_block_exception_t_bytes += lv_ptr->lv_remap_end;
-                                                       if (lv_ptr->lv_open > 0) {
-                                                               lv_open_counter++;
-                                                               lv_open_total += lv_ptr->lv_open;
-                                                       }
-                                               }
-                                       }
-                               }
-                       }
-               }
-               pe_t_bytes *= sizeof(pe_t);
-               lv_block_exception_t_bytes *= sizeof(lv_block_exception_t);
-
-               if (buf != NULL) {
-#ifdef DEBUG_KFREE
-                       printk(KERN_DEBUG
-                              "%s -- kfree %d\n", lvm_name, __LINE__);
-#endif
-                       kfree(buf);
-                       buf = NULL;
-               }
-               /* 2 times: first to get size to allocate buffer,
-                  2nd to fill the malloced buffer */
-               for (i = 0; i < 2; i++) {
-                       sz = 0;
-                       sz += sprintf(LVM_PROC_BUF,
-                                     "LVM "
-#ifdef MODULE
-                                     "module"
-#else
-                                     "driver"
-#endif
-                                     " %s\n\n"
-                                   "Total:  %d VG%s  %d PV%s  %d LV%s ",
-                                     lvm_short_version,
-                                 vg_counter, vg_counter == 1 ? "" : "s",
-                                 pv_counter, pv_counter == 1 ? "" : "s",
-                                lv_counter, lv_counter == 1 ? "" : "s");
-                       sz += sprintf(LVM_PROC_BUF,
-                                     "(%d LV%s open",
-                                     lv_open_counter,
-                                     lv_open_counter == 1 ? "" : "s");
-                       if (lv_open_total > 0)
-                               sz += sprintf(LVM_PROC_BUF,
-                                             " %d times)\n",
-                                             lv_open_total);
-                       else
-                               sz += sprintf(LVM_PROC_BUF, ")");
-                       sz += sprintf(LVM_PROC_BUF,
-                                     "\nGlobal: %lu bytes malloced   IOP version: %d   ",
-                                     vg_counter * sizeof(vg_t) +
-                                     pv_counter * sizeof(pv_t) +
-                                     lv_counter * sizeof(lv_t) +
-                       pe_t_bytes + lv_block_exception_t_bytes + sz_last,
-                                     lvm_iop_version);
-
-                       seconds = CURRENT_TIME - loadtime;
-                       if (seconds < 0)
-                               loadtime = CURRENT_TIME + seconds;
-                       if (seconds / 86400 > 0) {
-                               sz += sprintf(LVM_PROC_BUF, "%d day%s ",
-                                             seconds / 86400,
-                                             seconds / 86400 == 0 ||
-                                        seconds / 86400 > 1 ? "s" : "");
-                       }
-                       sz += sprintf(LVM_PROC_BUF, "%d:%02d:%02d active\n",
-                                     (seconds % 86400) / 3600,
-                                     (seconds % 3600) / 60,
-                                     seconds % 60);
-
-                       if (vg_counter > 0) {
-                               for (v = 0; v < ABS_MAX_VG; v++) {
-                                       /* volume group */
-                                       if ((vg_ptr = vg[v]) != NULL) {
-                                               inactive_flag = ' ';
-                                               if (!(vg_ptr->vg_status & VG_ACTIVE)) inactive_flag = 'I';
-                                               sz += sprintf(LVM_PROC_BUF,
-                                                             "\nVG: %c%s  [%d PV, %d LV/%d open] "
-                                                             " PE Size: %d KB\n"
-                                                             "  Usage [KB/PE]: %d /%d total  "
-                                                             "%d /%d used  %d /%d free",
-                                                             inactive_flag,
-                                                             vg_ptr->vg_name,
-                                                             vg_ptr->pv_cur,
-                                                             vg_ptr->lv_cur,
-                                                             vg_ptr->lv_open,
-                                                             vg_ptr->pe_size >> 1,
-                                                             vg_ptr->pe_size * vg_ptr->pe_total >> 1,
-                                                             vg_ptr->pe_total,
-                                                             vg_ptr->pe_allocated * vg_ptr->pe_size >> 1,
-                                                             vg_ptr->pe_allocated,
-                                                             (vg_ptr->pe_total - vg_ptr->pe_allocated) *
-                                                             vg_ptr->pe_size >> 1,
-                                                             vg_ptr->pe_total - vg_ptr->pe_allocated);
-
-                                               /* physical volumes */
-                                               sz += sprintf(LVM_PROC_BUF,
-                                                             "\n  PV%s ",
-                                                             vg_ptr->pv_cur == 1 ? ": " : "s:");
-                                               c = 0;
-                                               for (p = 0; p < vg_ptr->pv_max; p++) {
-                                                       if ((pv_ptr = vg_ptr->pv[p]) != NULL) {
-                                                               inactive_flag = 'A';
-                                                               if (!(pv_ptr->pv_status & PV_ACTIVE))
-                                                                       inactive_flag = 'I';
-                                                               allocation_flag = 'A';
-                                                               if (!(pv_ptr->pv_allocatable & PV_ALLOCATABLE))
-                                                                       allocation_flag = 'N';
-                                                               pv_name = strchr(pv_ptr->pv_name+1,'/');
-                                                               if ( pv_name == 0) pv_name = pv_ptr->pv_name;
-                                                               else               pv_name++;
-                                                               sz += sprintf(LVM_PROC_BUF,
-                                                                             "[%c%c] %-21s %8d /%-6d  "
-                                                                             "%8d /%-6d  %8d /%-6d",
-                                                                             inactive_flag,
-                                                                             allocation_flag,
-                                                                             pv_name,
-                                                                             pv_ptr->pe_total *
-                                                                             pv_ptr->pe_size >> 1,
-                                                                             pv_ptr->pe_total,
-                                                                             pv_ptr->pe_allocated *
-                                                                             pv_ptr->pe_size >> 1,
-                                                                             pv_ptr->pe_allocated,
-                                                                             (pv_ptr->pe_total -
-                                                                              pv_ptr->pe_allocated) *
-                                                                             pv_ptr->pe_size >> 1,
-                                                                             pv_ptr->pe_total -
-                                                                             pv_ptr->pe_allocated);
-                                                               c++;
-                                                               if (c < vg_ptr->pv_cur)
-                                                                       sz += sprintf(LVM_PROC_BUF,
-                                                                                     "\n       ");
-                                                       }
-                                               }
-
-                                               /* logical volumes */
-                                               sz += sprintf(LVM_PROC_BUF,
-                                                          "\n    LV%s ",
-                                                             vg_ptr->lv_cur == 1 ? ": " : "s:");
-                                               c = 0;
-                                               for (l = 0; l < vg[v]->lv_max; l++) {
-                                                       if ((lv_ptr = vg_ptr->lv[l]) != NULL) {
-                                                               inactive_flag = 'A';
-                                                               if (!(lv_ptr->lv_status & LV_ACTIVE))
-                                                                       inactive_flag = 'I';
-                                                               rw_flag = 'R';
-                                                               if (lv_ptr->lv_access & LV_WRITE)
-                                                                       rw_flag = 'W';
-                                                               allocation_flag = 'D';
-                                                               if (lv_ptr->lv_allocation & LV_CONTIGUOUS)
-                                                                       allocation_flag = 'C';
-                                                               stripes_flag = 'L';
-                                                               if (lv_ptr->lv_stripes > 1)
-                                                                       stripes_flag = 'S';
-                                                               sz += sprintf(LVM_PROC_BUF,
-                                                                             "[%c%c%c%c",
-                                                                             inactive_flag,
-                                                                rw_flag,
-                                                                             allocation_flag,
-                                                                             stripes_flag);
-                                                               if (lv_ptr->lv_stripes > 1)
-                                                                       sz += sprintf(LVM_PROC_BUF, "%-2d",
-                                                                                     lv_ptr->lv_stripes);
-                                                               else
-                                                                       sz += sprintf(LVM_PROC_BUF, "  ");
-                                                               lv_name = strrchr(lv_ptr->lv_name, '/');
-                                                               if ( lv_name == 0) lv_name = lv_ptr->lv_name;
-                                                               else               lv_name++;
-                                                               sz += sprintf(LVM_PROC_BUF, "] %-25s", lv_name);
-                                                               if (strlen(lv_name) > 25)
-                                                                       sz += sprintf(LVM_PROC_BUF,
-                                                                                     "\n                              ");
-                                                               sz += sprintf(LVM_PROC_BUF, "%9d /%-6d   ",
-                                                                             lv_ptr->lv_size >> 1,
-                                                                             lv_ptr->lv_size / vg[v]->pe_size);
-
-                                                               if (lv_ptr->lv_open == 0)
-                                                                       sz += sprintf(LVM_PROC_BUF, "close");
-                                                               else
-                                                                       sz += sprintf(LVM_PROC_BUF, "%dx open",
-                                                                                     lv_ptr->lv_open);
-                                                               c++;
-                                                               if (c < vg_ptr->lv_cur)
-                                                                       sz += sprintf(LVM_PROC_BUF,
-                                                                                     "\n         ");
-                                                       }
-                                               }
-                                               if (vg_ptr->lv_cur == 0) sz += sprintf(LVM_PROC_BUF, "none");
-                                               sz += sprintf(LVM_PROC_BUF, "\n");
-                                       }
-                               }
-                       }
-                       if (buf == NULL) {
-                               if ((buf = vmalloc(sz)) == NULL) {
-                                       sz = 0;
-                                       return sprintf(page, "%s - vmalloc error at line %d\n",
-                                                    lvm_name, __LINE__);
-                               }
-                       }
-                       sz_last = sz;
-               }
-       }
-       if (pos > sz - 1) {
-               vfree(buf);
-               buf = NULL;
-               return 0;
-       }
-       *start = &buf[pos];
-       if (sz - pos < count)
-               return sz - pos;
-       else
-               return count;
-} /* lvm_proc_get_info() */
-#endif /* #if defined CONFIG_LVM_PROC_FS && defined CONFIG_PROC_FS */
-
-
-/*
- * block device support function for /usr/src/linux/drivers/block/ll_rw_blk.c
- * (see init_module/lvm_init)
- */
-static int lvm_map(struct buffer_head *bh, int rw)
-{
-       int minor = MINOR(bh->b_rdev);
-       ulong index;
-       ulong pe_start;
-       ulong size = bh->b_size >> 9;
-       ulong rsector_tmp = bh->b_rsector;
-       ulong rsector_sav;
-       kdev_t rdev_tmp = bh->b_rdev;
-       kdev_t rdev_sav;
-       lv_t *lv = vg[VG_BLK(minor)]->lv[LV_BLK(minor)];
-
-
-       if (!(lv->lv_status & LV_ACTIVE)) {
-               printk(KERN_ALERT
-                      "%s - lvm_map: ll_rw_blk for inactive LV %s\n",
-                      lvm_name, lv->lv_name);
-               goto error;
-       }
-/*
-   if ( lv->lv_access & LV_SNAPSHOT)
-   printk ( "%s -- %02d:%02d  block: %lu  rw: %d\n", lvm_name, MAJOR ( bh->b_dev), MINOR ( bh->b_dev), bh->b_blocknr, rw);
- */
-
-       /* take care of snapshot chunk writes before
-          check for writable logical volume */
-       if ((lv->lv_access & LV_SNAPSHOT) &&
-           MAJOR(bh->b_rdev) != 0 &&
-           MAJOR(bh->b_rdev) != MAJOR_NR &&
-           (rw == WRITEA || rw == WRITE))
-       {
-   printk ( "%s -- doing snapshot write for %02d:%02d[%02d:%02d]  b_blocknr: %lu  b_rsector: %lu\n", lvm_name, MAJOR ( bh->b_dev), MINOR ( bh->b_dev), MAJOR ( bh->b_rdev), MINOR ( bh->b_rdev), bh->b_blocknr, bh->b_rsector);
-               goto error;
-       }
-
-       if ((rw == WRITE || rw == WRITEA) &&
-           !(lv->lv_access & LV_WRITE)) {
-               printk(KERN_CRIT
-                   "%s - lvm_map: ll_rw_blk write for readonly LV %s\n",
-                      lvm_name, lv->lv_name);
-               goto error;
-       }
-#ifdef DEBUG_MAP
-       printk(KERN_DEBUG
-              "%s - lvm_map minor:%d  *rdev: %02d:%02d  *rsector: %lu  "
-              "size:%lu\n",
-              lvm_name, minor,
-              MAJOR(rdev_tmp),
-              MINOR(rdev_tmp),
-              rsector_tmp, size);
-#endif
-
-       if (rsector_tmp + size > lv->lv_size) {
-               printk(KERN_ALERT
-                      "%s - lvm_map *rsector: %lu or size: %lu wrong for"
-                   " minor: %2d\n", lvm_name, rsector_tmp, size, minor);
-               goto error;
-       }
-       rsector_sav = rsector_tmp;
-       rdev_sav = rdev_tmp;
-
-lvm_second_remap:
-       /* linear mapping */
-       if (lv->lv_stripes < 2) {
-               /* get the index */
-               index = rsector_tmp / vg[VG_BLK(minor)]->pe_size;
-               pe_start = lv->lv_current_pe[index].pe;
-               rsector_tmp = lv->lv_current_pe[index].pe +
-                   (rsector_tmp % vg[VG_BLK(minor)]->pe_size);
-               rdev_tmp = lv->lv_current_pe[index].dev;
-
-#ifdef DEBUG_MAP
-               printk(KERN_DEBUG
-                      "lv_current_pe[%ld].pe: %ld  rdev: %02d:%02d  rsector:%ld\n",
-                      index,
-                      lv->lv_current_pe[index].pe,
-                      MAJOR(rdev_tmp),
-                      MINOR(rdev_tmp),
-                      rsector_tmp);
-#endif
-
-               /* striped mapping */
-       } else {
-               ulong stripe_index;
-               ulong stripe_length;
-
-               stripe_length = vg[VG_BLK(minor)]->pe_size * lv->lv_stripes;
-               stripe_index = (rsector_tmp % stripe_length) / lv->lv_stripesize;
-               index = rsector_tmp / stripe_length +
-                   (stripe_index % lv->lv_stripes) *
-                   (lv->lv_allocated_le / lv->lv_stripes);
-               pe_start = lv->lv_current_pe[index].pe;
-               rsector_tmp = lv->lv_current_pe[index].pe +
-                   (rsector_tmp % stripe_length) -
-                   (stripe_index % lv->lv_stripes) * lv->lv_stripesize -
-                   stripe_index / lv->lv_stripes *
-                   (lv->lv_stripes - 1) * lv->lv_stripesize;
-               rdev_tmp = lv->lv_current_pe[index].dev;
-       }
-
-#ifdef DEBUG_MAP
-       printk(KERN_DEBUG
-            "lv_current_pe[%ld].pe: %ld  rdev: %02d:%02d  rsector:%ld\n"
-              "stripe_length: %ld  stripe_index: %ld\n",
-              index,
-              lv->lv_current_pe[index].pe,
-              MAJOR(rdev_tmp),
-              MINOR(rdev_tmp),
-              rsector_tmp,
-              stripe_length,
-              stripe_index);
-#endif
-
-       /* handle physical extents on the move */
-       if (pe_lock_req.lock == LOCK_PE) {
-               if (rdev_tmp == pe_lock_req.data.pv_dev &&
-                   rsector_tmp >= pe_lock_req.data.pv_offset &&
-                   rsector_tmp < (pe_lock_req.data.pv_offset +
-                                  vg[VG_BLK(minor)]->pe_size)) {
-                       sleep_on(&lvm_map_wait);
-                       rsector_tmp = rsector_sav;
-                       rdev_tmp = rdev_sav;
-                       goto lvm_second_remap;
-               }
-       }
-       /* statistic */
-       if (rw == WRITE || rw == WRITEA)
-               lv->lv_current_pe[index].writes++;
-       else
-               lv->lv_current_pe[index].reads++;
-
-       /* snapshot volume exception handling on physical device address base */
-       if (lv->lv_access & (LV_SNAPSHOT | LV_SNAPSHOT_ORG)) {
-               /* original logical volume */
-               if (lv->lv_access & LV_SNAPSHOT_ORG) {
-                       if (rw == WRITE || rw == WRITEA)
-                       {
-                               lv_t *lv_ptr;
-
-                               /* start with first snapshot and loop thrugh all of them */
-                               for (lv_ptr = lv->lv_snapshot_next;
-                                    lv_ptr != NULL;
-                                    lv_ptr = lv_ptr->lv_snapshot_next) {
-                                       down(&lv->lv_snapshot_org->lv_snapshot_sem);
-                                       /* do we still have exception storage for this snapshot free? */
-                                       if (lv_ptr->lv_block_exception != NULL) {
-                                               rdev_sav = rdev_tmp;
-                                               rsector_sav = rsector_tmp;
-                                               if (!lvm_snapshot_remap_block(&rdev_tmp,
-                                                                             &rsector_tmp,
-                                                                             pe_start,
-                                                                             lv_ptr)) {
-                                                       /* create a new mapping */
-                                                       lvm_snapshot_COW(rdev_tmp,
-                                                                        rsector_tmp,
-                                                                        pe_start,
-                                                                        rsector_sav,
-                                                                        lv_ptr); 
-                                               }
-                                               rdev_tmp = rdev_sav;
-                                               rsector_tmp = rsector_sav;
-                                       }
-                                       up(&lv->lv_snapshot_org->lv_snapshot_sem);
-                               }
-                       }
-               } else {
-                       /* remap snapshot logical volume */
-                       down(&lv->lv_snapshot_sem);
-                       if (lv->lv_block_exception != NULL)
-                               lvm_snapshot_remap_block(&rdev_tmp, &rsector_tmp, pe_start, lv);
-                       up(&lv->lv_snapshot_sem);
-               }
-       }
-       bh->b_rdev = rdev_tmp;
-       bh->b_rsector = rsector_tmp;
-
-       return 1;
-
- error:
-       buffer_IO_error(bh);
-       return -1;
-} /* lvm_map() */
-
-
-/*
- * internal support functions
- */
-
-#ifdef LVM_HD_NAME
-/*
- * generate "hard disk" name
- */
-void lvm_hd_name(char *buf, int minor)
-{
-       int len = 0;
-       lv_t *lv_ptr;
-
-       if (vg[VG_BLK(minor)] == NULL ||
-           (lv_ptr = vg[VG_BLK(minor)]->lv[LV_BLK(minor)]) == NULL)
-               return;
-       len = strlen(lv_ptr->lv_name) - 5;
-       memcpy(buf, &lv_ptr->lv_name[5], len);
-       buf[len] = 0;
-       return;
-}
-#endif
-
-
-/*
- * this one never should be called...
- */
-static void lvm_dummy_device_request(request_queue_t * t)
-{
-       printk(KERN_EMERG
-            "%s -- oops, got lvm request for %02d:%02d [sector: %lu]\n",
-              lvm_name,
-              MAJOR(CURRENT->rq_dev),
-              MINOR(CURRENT->rq_dev),
-              CURRENT->sector);
-       return;
-}
-
-
-/*
- * make request function
- */
-static int lvm_make_request_fn(request_queue_t *q, int rw, struct buffer_head *bh)
-{
-       lvm_map(bh, rw);
-       return 1;
-}
-
-/*
- * plug device function is a noop because plugging has to happen
- * in the queue of the physical blockdevice to allow the
- * elevator to do a better job.
- */
-static void lvm_plug_device_noop(request_queue_t *q, kdev_t dev) { }
-
-/********************************************************************
- *
- * Character device support functions
- *
- ********************************************************************/
-/*
- * character device support function logical volume manager lock
- */
-static int lvm_do_lock_lvm(void)
-{
-lock_try_again:
-       spin_lock(&lvm_lock);
-       if (lock != 0 && lock != current->pid) {
-#ifdef DEBUG_IOCTL
-               printk(KERN_INFO "lvm_do_lock_lvm: %s is locked by pid %d ...\n",
-                      lvm_name, lock);
-#endif
-               spin_unlock(&lvm_lock);
-               interruptible_sleep_on(&lvm_wait);
-               if (current->sigpending != 0)
-                       return -EINTR;
-#ifdef LVM_TOTAL_RESET
-               if (lvm_reset_spindown > 0)
-                       return -EACCES;
-#endif
-               goto lock_try_again;
-       }
-       lock = current->pid;
-       spin_unlock(&lvm_lock);
-       return 0;
-} /* lvm_do_lock_lvm */
-
-
-/*
- * character device support function lock/unlock physical extend
- */
-static int lvm_do_pe_lock_unlock(vg_t *vg_ptr, void *arg)
-{
-       uint p;
-
-       if (vg_ptr == NULL) return -ENXIO;
-       if (copy_from_user(&pe_lock_req, arg,
-                          sizeof(pe_lock_req_t)) != 0) return -EFAULT;
-
-       switch (pe_lock_req.lock) {
-       case LOCK_PE:
-               for (p = 0; p < vg_ptr->pv_max; p++) {
-                       if (vg_ptr->pv[p] != NULL &&
-                           pe_lock_req.data.pv_dev ==
-                           vg_ptr->pv[p]->pv_dev)
-                               break;
-               }
-               if (p == vg_ptr->pv_max) return -ENXIO;
-
-               pe_lock_req.lock = UNLOCK_PE;
-               fsync_dev(pe_lock_req.data.lv_dev);
-               pe_lock_req.lock = LOCK_PE;
-               break;
-
-       case UNLOCK_PE:
-               pe_lock_req.lock = UNLOCK_PE;
-               pe_lock_req.data.lv_dev = pe_lock_req.data.pv_dev = 0;
-               pe_lock_req.data.pv_offset = 0;
-               wake_up(&lvm_map_wait);
-               break;
-
-       default:
-               return -EINVAL;
-       }
-       return 0;
-}
-
-
-/*
- * character device support function logical extend remap
- */
-static int lvm_do_le_remap(vg_t *vg_ptr, void *arg)
-{
-       uint l, le;
-       lv_t *lv_ptr;
-
-       if (vg_ptr == NULL) return -ENXIO;
-       if (copy_from_user(&le_remap_req, arg,
-                          sizeof(le_remap_req_t)) != 0)
-               return -EFAULT;
-
-       for (l = 0; l < vg_ptr->lv_max; l++) {
-               lv_ptr = vg_ptr->lv[l];
-               if (lv_ptr != NULL &&
-                   strcmp(lv_ptr->lv_name,
-                              le_remap_req.lv_name) == 0) {
-                       for (le = 0; le < lv_ptr->lv_allocated_le;
-                            le++) {
-                               if (lv_ptr->lv_current_pe[le].dev ==
-                                   le_remap_req.old_dev &&
-                                   lv_ptr->lv_current_pe[le].pe ==
-                                   le_remap_req.old_pe) {
-                                       lv_ptr->lv_current_pe[le].dev =
-                                           le_remap_req.new_dev;
-                                       lv_ptr->lv_current_pe[le].pe =
-                                           le_remap_req.new_pe;
-                                       return 0;
-                               }
-                       }
-                       return -EINVAL;
-               }
-       }
-       return -ENXIO;
-} /* lvm_do_le_remap() */
-
-
-/*
- * character device support function VGDA create
- */
-int lvm_do_vg_create(int minor, void *arg)
-{
-       int snaporg_minor = 0;
-       ulong l, p;
-       lv_t lv;
-       vg_t *vg_ptr;
-       pv_t *pv_ptr;
-       lv_t *lv_ptr;
-
-       if (vg[VG_CHR(minor)] != NULL) return -EPERM;
-
-       if ((vg_ptr = kmalloc(sizeof(vg_t),GFP_KERNEL)) == NULL) {
-               printk(KERN_CRIT
-                      "%s -- VG_CREATE: kmalloc error VG at line %d\n",
-                      lvm_name, __LINE__);
-               return -ENOMEM;
-       }
-       /* get the volume group structure */
-       if (copy_from_user(vg_ptr, arg, sizeof(vg_t)) != 0) {
-               kfree(vg_ptr);
-               return -EFAULT;
-       }
-
-       vg_devfs_handle[vg_ptr->vg_number] = devfs_mk_dir(0, vg_ptr->vg_name, NULL);
-       ch_devfs_handle[vg_ptr->vg_number] = devfs_register(
-               vg_devfs_handle[vg_ptr->vg_number] , "group",
-               DEVFS_FL_DEFAULT, LVM_CHAR_MAJOR, vg_ptr->vg_number,
-               S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP,
-               &lvm_chr_fops, NULL);
-
-       /* we are not that active so far... */
-       vg_ptr->vg_status &= ~VG_ACTIVE;
-       vg[VG_CHR(minor)] = vg_ptr;
-
-       vg[VG_CHR(minor)]->pe_allocated = 0;
-       if (vg_ptr->pv_max > ABS_MAX_PV) {
-               printk(KERN_WARNING
-                      "%s -- Can't activate VG: ABS_MAX_PV too small\n",
-                      lvm_name);
-               kfree(vg_ptr);
-               vg[VG_CHR(minor)] = NULL;
-               return -EPERM;
-       }
-       if (vg_ptr->lv_max > ABS_MAX_LV) {
-               printk(KERN_WARNING
-               "%s -- Can't activate VG: ABS_MAX_LV too small for %u\n",
-                      lvm_name, vg_ptr->lv_max);
-               kfree(vg_ptr);
-               vg_ptr = NULL;
-               return -EPERM;
-       }
-       /* get the physical volume structures */
-       vg_ptr->pv_act = vg_ptr->pv_cur = 0;
-       for (p = 0; p < vg_ptr->pv_max; p++) {
-               /* user space address */
-               if ((pvp = vg_ptr->pv[p]) != NULL) {
-                       pv_ptr = vg_ptr->pv[p] = kmalloc(sizeof(pv_t),GFP_KERNEL);
-                       if (pv_ptr == NULL) {
-                               printk(KERN_CRIT
-                                      "%s -- VG_CREATE: kmalloc error PV at line %d\n",
-                                      lvm_name, __LINE__);
-                               lvm_do_vg_remove(minor);
-                               return -ENOMEM;
-                       }
-                       if (copy_from_user(pv_ptr, pvp, sizeof(pv_t)) != 0) {
-                               lvm_do_vg_remove(minor);
-                               return -EFAULT;
-                       }
-                       /* We don't need the PE list
-                          in kernel space as with LVs pe_t list (see below) */
-                       pv_ptr->pe = NULL;
-                       pv_ptr->pe_allocated = 0;
-                       pv_ptr->pv_status = PV_ACTIVE;
-                       vg_ptr->pv_act++;
-                       vg_ptr->pv_cur++;
-
-#ifdef LVM_GET_INODE
-                       /* insert a dummy inode for fs_may_mount */
-                       pv_ptr->inode = lvm_get_inode(pv_ptr->pv_dev);
-#endif
-               }
-       }
-
-       /* get the logical volume structures */
-       vg_ptr->lv_cur = 0;
-       for (l = 0; l < vg_ptr->lv_max; l++) {
-               /* user space address */
-               if ((lvp = vg_ptr->lv[l]) != NULL) {
-                       if (copy_from_user(&lv, lvp, sizeof(lv_t)) != 0) {
-                               lvm_do_vg_remove(minor);
-                               return -EFAULT;
-                       }
-                       vg_ptr->lv[l] = NULL;
-                       if (lvm_do_lv_create(minor, lv.lv_name, &lv) != 0) {
-                               lvm_do_vg_remove(minor);
-                               return -EFAULT;
-                       }
-               }
-       }
-
-       /* Second path to correct snapshot logical volumes which are not
-          in place during first path above */
-       for (l = 0; l < vg_ptr->lv_max; l++) {
-               if ((lv_ptr = vg_ptr->lv[l]) != NULL &&
-                   vg_ptr->lv[l]->lv_access & LV_SNAPSHOT) {
-                       snaporg_minor = lv_ptr->lv_snapshot_minor;
-                       if (vg_ptr->lv[LV_BLK(snaporg_minor)] != NULL) {
-                               /* get pointer to original logical volume */
-                               lv_ptr = vg_ptr->lv[l]->lv_snapshot_org =
-                               vg_ptr->lv[LV_BLK(snaporg_minor)];
-
-                               /* set necessary fields of original logical volume */
-                               lv_ptr->lv_access |= LV_SNAPSHOT_ORG;
-                               lv_ptr->lv_snapshot_minor = 0;
-                               lv_ptr->lv_snapshot_org = lv_ptr;
-                               lv_ptr->lv_snapshot_prev = NULL;
-
-                               /* find last snapshot logical volume in the chain */
-                               while (lv_ptr->lv_snapshot_next != NULL)
-                                       lv_ptr = lv_ptr->lv_snapshot_next;
-
-                               /* set back pointer to this last one in our new logical volume */
-                               vg_ptr->lv[l]->lv_snapshot_prev = lv_ptr;
-
-                               /* last logical volume now points to our new snapshot volume */
-                               lv_ptr->lv_snapshot_next = vg_ptr->lv[l];
-
-                               /* now point to the new one */
-                               lv_ptr = lv_ptr->lv_snapshot_next;
-
-                               /* set necessary fields of new snapshot logical volume */
-                               lv_ptr->lv_snapshot_next = NULL;
-                               lv_ptr->lv_current_pe =
-                                   vg_ptr->lv[LV_BLK(snaporg_minor)]->lv_current_pe;
-                               lv_ptr->lv_allocated_le =
-                                   vg_ptr->lv[LV_BLK(snaporg_minor)]->lv_allocated_le;
-                               lv_ptr->lv_current_le =
-                                   vg_ptr->lv[LV_BLK(snaporg_minor)]->lv_current_le;
-                               lv_ptr->lv_size =
-                                   vg_ptr->lv[LV_BLK(snaporg_minor)]->lv_size;
-                       }
-               }
-       }
-
-       vg_count++;
-
-       /* let's go active */
-       vg_ptr->vg_status |= VG_ACTIVE;
-
-       MOD_INC_USE_COUNT;
-
-       return 0;
-} /* lvm_do_vg_create() */
-
-
-/*
- * character device support function VGDA extend
- */
-static int lvm_do_vg_extend(vg_t *vg_ptr, void *arg)
-{
-       uint p;
-       pv_t *pv_ptr;
-
-       if (vg_ptr == NULL) return -ENXIO;
-       if (vg_ptr->pv_cur < vg_ptr->pv_max) {
-               for (p = 0; p < vg_ptr->pv_max; p++) {
-                       if (vg_ptr->pv[p] == NULL) {
-                               if ((pv_ptr = vg_ptr->pv[p] = kmalloc(sizeof(pv_t),GFP_KERNEL)) == NULL) {
-                                       printk(KERN_CRIT
-                                              "%s -- VG_EXTEND: kmalloc error PV at line %d\n",
-                                            lvm_name, __LINE__);
-                                       return -ENOMEM;
-                               }
-                               if (copy_from_user(pv_ptr, arg, sizeof(pv_t)) != 0) {
-                                       kfree(pv_ptr);
-                                       vg_ptr->pv[p] = NULL;
-                                       return -EFAULT;
-                               }
-       
-                               pv_ptr->pv_status = PV_ACTIVE;
-                               /* We don't need the PE list
-                                  in kernel space like LVs pe_t list */
-                               pv_ptr->pe = NULL;
-                               vg_ptr->pv_cur++;
-                               vg_ptr->pv_act++;
-                               vg_ptr->pe_total +=
-                                   pv_ptr->pe_total;
-#ifdef LVM_GET_INODE
-                               /* insert a dummy inode for fs_may_mount */
-                               pv_ptr->inode = lvm_get_inode(pv_ptr->pv_dev);
-#endif
-                               return 0;
-                       }
-               }
-       }
-return -EPERM;
-} /* lvm_do_vg_extend() */
-
-
-/*
- * character device support function VGDA reduce
- */
-static int lvm_do_vg_reduce(vg_t *vg_ptr, void *arg)
-{
-       uint p;
-       pv_t *pv_ptr;
-
-       if (vg_ptr == NULL) return -ENXIO;
-       if (copy_from_user(pv_name, arg, sizeof(pv_name)) != 0)
-               return -EFAULT;
-
-       for (p = 0; p < vg_ptr->pv_max; p++) {
-               pv_ptr = vg_ptr->pv[p];
-               if (pv_ptr != NULL &&
-                   strcmp(pv_ptr->pv_name,
-                              pv_name) == 0) {
-                       if (pv_ptr->lv_cur > 0) return -EPERM;
-                       vg_ptr->pe_total -=
-                           pv_ptr->pe_total;
-                       vg_ptr->pv_cur--;
-                       vg_ptr->pv_act--;
-#ifdef LVM_GET_INODE
-                       lvm_clear_inode(pv_ptr->inode);
-#endif
-                       kfree(pv_ptr);
-                       /* Make PV pointer array contiguous */
-                       for (; p < vg_ptr->pv_max - 1; p++)
-                               vg_ptr->pv[p] = vg_ptr->pv[p + 1];
-                       vg_ptr->pv[p + 1] = NULL;
-                       return 0;
-               }
-       }
-       return -ENXIO;
-} /* lvm_do_vg_reduce */
-
-
-/*
- * character device support function VGDA remove
- */
-static int lvm_do_vg_remove(int minor)
-{
-       int i;
-       vg_t *vg_ptr = vg[VG_CHR(minor)];
-       pv_t *pv_ptr;
-
-       if (vg_ptr == NULL) return -ENXIO;
-
-#ifdef LVM_TOTAL_RESET
-       if (vg_ptr->lv_open > 0 && lvm_reset_spindown == 0)
-#else
-       if (vg_ptr->lv_open > 0)
-#endif
-               return -EPERM;
-
-       /* let's go inactive */
-       vg_ptr->vg_status &= ~VG_ACTIVE;
-
-       devfs_unregister (ch_devfs_handle[vg_ptr->vg_number]);
-       devfs_unregister (vg_devfs_handle[vg_ptr->vg_number]);
-
-       /* free LVs */
-       /* first free snapshot logical volumes */
-       for (i = 0; i < vg_ptr->lv_max; i++) {
-               if (vg_ptr->lv[i] != NULL &&
-                   vg_ptr->lv[i]->lv_access & LV_SNAPSHOT) {
-                       lvm_do_lv_remove(minor, NULL, i);
-                       current->state = TASK_UNINTERRUPTIBLE;
-                       schedule_timeout(1);
-               }
-       }
-       /* then free the rest of the LVs */
-       for (i = 0; i < vg_ptr->lv_max; i++) {
-               if (vg_ptr->lv[i] != NULL) {
-                       lvm_do_lv_remove(minor, NULL, i);
-                       current->state = TASK_UNINTERRUPTIBLE;
-                       schedule_timeout(1);
-               }
-       }
-
-       /* free PVs */
-       for (i = 0; i < vg_ptr->pv_max; i++) {
-               if ((pv_ptr = vg_ptr->pv[i]) != NULL) {
-#ifdef DEBUG_KFREE
-                       printk(KERN_DEBUG
-                              "%s -- kfree %d\n", lvm_name, __LINE__);
-#endif
-#ifdef LVM_GET_INODE
-                       lvm_clear_inode(pv_ptr->inode);
-#endif
-                       kfree(pv_ptr);
-                       vg[VG_CHR(minor)]->pv[i] = NULL;
-               }
-       }
-
-#ifdef DEBUG_KFREE
-       printk(KERN_DEBUG "%s -- kfree %d\n", lvm_name, __LINE__);
-#endif
-       kfree(vg_ptr);
-       vg[VG_CHR(minor)] = NULL;
-
-       vg_count--;
-
-       MOD_DEC_USE_COUNT;
-
-       return 0;
-} /* lvm_do_vg_remove() */
-
-
-/*
- * character device support function logical volume create
- */
-static int lvm_do_lv_create(int minor, char *lv_name, lv_t *lv)
-{
-       int l, le, l_new, p, size;
-       ulong lv_status_save;
-       char *lv_tmp, *lv_buf;
-       lv_block_exception_t *lvbe = lv->lv_block_exception;
-       vg_t *vg_ptr = vg[VG_CHR(minor)];
-       lv_t *lv_ptr = NULL;
-
-       if ((pep = lv->lv_current_pe) == NULL) return -EINVAL;
-       if (lv->lv_chunk_size > LVM_SNAPSHOT_MAX_CHUNK)
-               return -EINVAL;
-
-       for (l = 0; l < vg_ptr->lv_max; l++) {
-               if (vg_ptr->lv[l] != NULL &&
-                   strcmp(vg_ptr->lv[l]->lv_name, lv_name) == 0)
-                       return -EEXIST;
-       }
-
-       /* in case of lv_remove(), lv_create() pair; for eg. lvrename does this */
-       l_new = -1;
-       if (vg_ptr->lv[lv->lv_number] == NULL)
-               l_new = lv->lv_number;
-       else {
-               for (l = 0; l < vg_ptr->lv_max; l++) {
-                       if (vg_ptr->lv[l] == NULL)
-                               if (l_new == -1) l_new = l;
-               }
-       }
-       if (l_new == -1) return -EPERM;
-       else            l = l_new;
-
-       if ((lv_ptr = kmalloc(sizeof(lv_t),GFP_KERNEL)) == NULL) {;
-               printk(KERN_CRIT "%s -- LV_CREATE: kmalloc error LV at line %d\n",
-                      lvm_name, __LINE__);
-               return -ENOMEM;
-       }
-       /* copy preloaded LV */
-       memcpy((char *) lv_ptr, (char *) lv, sizeof(lv_t));
-
-       lv_status_save = lv_ptr->lv_status;
-       lv_ptr->lv_status &= ~LV_ACTIVE;
-       lv_ptr->lv_snapshot_org = \
-           lv_ptr->lv_snapshot_prev = \
-           lv_ptr->lv_snapshot_next = NULL;
-       lv_ptr->lv_block_exception = NULL;
-       init_MUTEX(&lv_ptr->lv_snapshot_sem);
-       vg_ptr->lv[l] = lv_ptr;
-
-       /* get the PE structures from user space if this
-          is no snapshot logical volume */
-       if (!(lv_ptr->lv_access & LV_SNAPSHOT)) {
-               size = lv_ptr->lv_allocated_le * sizeof(pe_t);
-               if ((lv_ptr->lv_current_pe = vmalloc(size)) == NULL) {
-                       printk(KERN_CRIT
-                              "%s -- LV_CREATE: vmalloc error LV_CURRENT_PE of %d Byte "
-                              "at line %d\n",
-                              lvm_name, size, __LINE__);
-#ifdef DEBUG_KFREE
-                       printk(KERN_DEBUG "%s -- kfree %d\n", lvm_name, __LINE__);
-#endif
-                       kfree(lv_ptr);
-                       vg[VG_CHR(minor)]->lv[l] = NULL;
-                       return -ENOMEM;
-               }
-               if (copy_from_user(lv_ptr->lv_current_pe, pep, size)) {
-                       vfree(lv_ptr->lv_current_pe);
-                       kfree(lv_ptr);
-                       vg_ptr->lv[l] = NULL;
-                       return -EFAULT;
-               }
-               /* correct the PE count in PVs */
-               for (le = 0; le < lv_ptr->lv_allocated_le; le++) {
-                       vg_ptr->pe_allocated++;
-                       for (p = 0; p < vg_ptr->pv_cur; p++) {
-                               if (vg_ptr->pv[p]->pv_dev ==
-                                   lv_ptr->lv_current_pe[le].dev)
-                                       vg_ptr->pv[p]->pe_allocated++;
-                       }
-               }
-       } else {
-               /* Get snapshot exception data and block list */
-               if (lvbe != NULL) {
-                       lv_ptr->lv_snapshot_org =
-                           vg_ptr->lv[LV_BLK(lv_ptr->lv_snapshot_minor)];
-                       if (lv_ptr->lv_snapshot_org != NULL) {
-                               size = lv_ptr->lv_remap_end * sizeof(lv_block_exception_t);
-                               if ((lv_ptr->lv_block_exception = vmalloc(size)) == NULL) {
-                                       printk(KERN_CRIT
-                                              "%s -- lvm_do_lv_create: vmalloc error LV_BLOCK_EXCEPTION "
-                                              "of %d byte at line %d\n",
-                                              lvm_name, size, __LINE__);
-#ifdef DEBUG_KFREE
-                                       printk(KERN_DEBUG "%s -- kfree %d\n", lvm_name, __LINE__);
-#endif
-                                       kfree(lv_ptr);
-                                       vg_ptr->lv[l] = NULL;
-                                       return -ENOMEM;
-                               }
-                               if (copy_from_user(lv_ptr->lv_block_exception, lvbe, size)) {
-                                       vfree(lv_ptr->lv_block_exception);
-                                       kfree(lv_ptr);
-                                       vg[VG_CHR(minor)]->lv[l] = NULL;
-                                       return -EFAULT;
-                               }
-                               /* get pointer to original logical volume */
-                               lv_ptr = lv_ptr->lv_snapshot_org;
-
-                               lv_ptr->lv_snapshot_minor = 0;
-                               lv_ptr->lv_snapshot_org = lv_ptr;
-                               lv_ptr->lv_snapshot_prev = NULL;
-                               /* walk thrugh the snapshot list */
-                               while (lv_ptr->lv_snapshot_next != NULL)
-                                       lv_ptr = lv_ptr->lv_snapshot_next;
-                               /* now lv_ptr points to the last existing snapshot in the chain */
-                               vg_ptr->lv[l]->lv_snapshot_prev = lv_ptr;
-                               /* our new one now back points to the previous last in the chain */
-                               lv_ptr = vg_ptr->lv[l];
-                               /* now lv_ptr points to our new last snapshot logical volume */
-                               lv_ptr->lv_snapshot_org = lv_ptr->lv_snapshot_prev->lv_snapshot_org;
-                               lv_ptr->lv_snapshot_next = NULL;
-                               lv_ptr->lv_current_pe = lv_ptr->lv_snapshot_org->lv_current_pe;
-                               lv_ptr->lv_allocated_le = lv_ptr->lv_snapshot_org->lv_allocated_le;
-                               lv_ptr->lv_current_le = lv_ptr->lv_snapshot_org->lv_current_le;
-                               lv_ptr->lv_size = lv_ptr->lv_snapshot_org->lv_size;
-                               lv_ptr->lv_stripes = lv_ptr->lv_snapshot_org->lv_stripes;
-                               lv_ptr->lv_stripesize = lv_ptr->lv_snapshot_org->lv_stripesize;
-                               {
-                                       int err = lvm_snapshot_alloc(lv_ptr);
-                                       if (err)
-                                       {
-                                               vfree(lv_ptr->lv_block_exception);
-                                               kfree(lv_ptr);
-                                               vg[VG_CHR(minor)]->lv[l] = NULL;
-                                                return err;
-                                       }
-                               }
-                       } else {
-                               vfree(lv_ptr->lv_block_exception);
-                               kfree(lv_ptr);
-                               vg_ptr->lv[l] = NULL;
-                               return -EFAULT;
-                       }
-               } else {
-                       kfree(vg_ptr->lv[l]);
-                       vg_ptr->lv[l] = NULL;
-                       return -EINVAL;
-               }
-       } /* if ( vg[VG_CHR(minor)]->lv[l]->lv_access & LV_SNAPSHOT) */
-
-       lv_ptr = vg_ptr->lv[l];
-       lvm_gendisk.part[MINOR(lv_ptr->lv_dev)].start_sect = 0;
-       lvm_gendisk.part[MINOR(lv_ptr->lv_dev)].nr_sects = lv_ptr->lv_size;
-       lvm_size[MINOR(lv_ptr->lv_dev)] = lv_ptr->lv_size >> 1;
-       vg_lv_map[MINOR(lv_ptr->lv_dev)].vg_number = vg_ptr->vg_number;
-       vg_lv_map[MINOR(lv_ptr->lv_dev)].lv_number = lv_ptr->lv_number;
-       read_ahead[MAJOR_NR] = lv_ptr->lv_read_ahead = LVM_CORRECT_READ_AHEAD(lv_ptr->lv_read_ahead);
-       vg_ptr->lv_cur++;
-       lv_ptr->lv_status = lv_status_save;
-
-       strtok(lv->lv_name, "/");       /* /dev */
-
-       while((lv_tmp = strtok(NULL, "/")) != NULL)
-               lv_buf = lv_tmp;
-
-       lv_devfs_handle[lv->lv_number] = devfs_register(
-               vg_devfs_handle[vg_ptr->vg_number], lv_buf,
-               DEVFS_FL_DEFAULT, LVM_BLK_MAJOR, lv->lv_number,
-               S_IFBLK | S_IRUSR | S_IWUSR | S_IRGRP,
-               &lvm_blk_dops, NULL);
-
-       /* optionally add our new snapshot LV */
-       if (lv_ptr->lv_access & LV_SNAPSHOT) {
-               /* sync the original logical volume */
-               fsync_dev(lv_ptr->lv_snapshot_org->lv_dev);
-               /* put ourselve into the chain */
-               lv_ptr->lv_snapshot_prev->lv_snapshot_next = lv_ptr;
-               lv_ptr->lv_snapshot_org->lv_access |= LV_SNAPSHOT_ORG;
-       }
-       return 0;
-} /* lvm_do_lv_create() */
-
-
-/*
- * character device support function logical volume remove
- */
-static int lvm_do_lv_remove(int minor, char *lv_name, int l)
-{
-       uint le, p;
-       vg_t *vg_ptr = vg[VG_CHR(minor)];
-       lv_t *lv_ptr;
-
-       if (l == -1) {
-               for (l = 0; l < vg_ptr->lv_max; l++) {
-                       if (vg_ptr->lv[l] != NULL &&
-                           strcmp(vg_ptr->lv[l]->lv_name, lv_name) == 0) {
-                               break;
-                       }
-               }
-       }
-       if (l == vg_ptr->lv_max) return -ENXIO;
-
-       lv_ptr = vg_ptr->lv[l];
-#ifdef LVM_TOTAL_RESET
-       if (lv_ptr->lv_open > 0 && lvm_reset_spindown == 0)
-#else
-       if (lv_ptr->lv_open > 0)
-#endif
-               return -EBUSY;
-
-       /* check for deletion of snapshot source while
-          snapshot volume still exists */
-       if ((lv_ptr->lv_access & LV_SNAPSHOT_ORG) &&
-           lv_ptr->lv_snapshot_next != NULL)
-               return -EPERM;
-
-       lv_ptr->lv_status |= LV_SPINDOWN;
-
-       /* sync the buffers */
-       fsync_dev(lv_ptr->lv_dev);
-
-       lv_ptr->lv_status &= ~LV_ACTIVE;
-
-       /* invalidate the buffers */
-       invalidate_buffers(lv_ptr->lv_dev);
-
-       /* reset generic hd */
-       lvm_gendisk.part[MINOR(lv_ptr->lv_dev)].start_sect = -1;
-       lvm_gendisk.part[MINOR(lv_ptr->lv_dev)].nr_sects = 0;
-       lvm_size[MINOR(lv_ptr->lv_dev)] = 0;
-
-       /* reset VG/LV mapping */
-       vg_lv_map[MINOR(lv_ptr->lv_dev)].vg_number = ABS_MAX_VG;
-       vg_lv_map[MINOR(lv_ptr->lv_dev)].lv_number = -1;
-
-       /* correct the PE count in PVs if this is no snapshot logical volume */
-       if (!(lv_ptr->lv_access & LV_SNAPSHOT)) {
-               /* only if this is no snapshot logical volume because
-                  we share the lv_current_pe[] structs with the
-                  original logical volume */
-               for (le = 0; le < lv_ptr->lv_allocated_le; le++) {
-                       vg_ptr->pe_allocated--;
-                       for (p = 0; p < vg_ptr->pv_cur; p++) {
-                               if (vg_ptr->pv[p]->pv_dev ==
-                                   lv_ptr->lv_current_pe[le].dev)
-                                       vg_ptr->pv[p]->pe_allocated--;
-                       }
-               }
-               vfree(lv_ptr->lv_current_pe);
-               /* LV_SNAPSHOT */
-       } else {
-               /* remove this snapshot logical volume from the chain */
-               lv_ptr->lv_snapshot_prev->lv_snapshot_next = lv_ptr->lv_snapshot_next;
-               if (lv_ptr->lv_snapshot_next != NULL) {
-                       lv_ptr->lv_snapshot_next->lv_snapshot_prev =
-                           lv_ptr->lv_snapshot_prev;
-               }
-               /* no more snapshots? */
-               if (lv_ptr->lv_snapshot_org->lv_snapshot_next == NULL)
-                       lv_ptr->lv_snapshot_org->lv_access &= ~LV_SNAPSHOT_ORG;
-               lvm_snapshot_release(lv_ptr);
-       }
-
-       devfs_unregister(lv_devfs_handle[lv_ptr->lv_number]);
-
-#ifdef DEBUG_KFREE
-       printk(KERN_DEBUG "%s -- kfree %d\n", lvm_name, __LINE__);
-#endif
-       kfree(lv_ptr);
-       vg_ptr->lv[l] = NULL;
-       vg_ptr->lv_cur--;
-       return 0;
-} /* lvm_do_lv_remove() */
-
-
-/*
- * character device support function logical volume extend / reduce
- */
-static int lvm_do_lv_extend_reduce(int minor, char *lv_name, lv_t *lv)
-{
-       int l, le, p, size, old_allocated_le;
-       uint32_t end, lv_status_save;
-       vg_t *vg_ptr = vg[VG_CHR(minor)];
-       lv_t *lv_ptr;
-       pe_t *pe;
-
-       if ((pep = lv->lv_current_pe) == NULL) return -EINVAL;
-
-       for (l = 0; l < vg_ptr->lv_max; l++) {
-               if (vg_ptr->lv[l] != NULL &&
-                   strcmp(vg_ptr->lv[l]->lv_name, lv_name) == 0)
-                       break;
-       }
-       if (l == vg_ptr->lv_max) return -ENXIO;
-       lv_ptr = vg_ptr->lv[l];
-
-       /* check for active snapshot */
-       if (lv->lv_access & (LV_SNAPSHOT | LV_SNAPSHOT_ORG)) return -EPERM;
-
-       if ((pe = vmalloc(size = lv->lv_current_le * sizeof(pe_t))) == NULL) {
-               printk(KERN_CRIT
-               "%s -- lvm_do_lv_extend_reduce: vmalloc error LV_CURRENT_PE "
-                      "of %d Byte at line %d\n",
-                      lvm_name, size, __LINE__);
-               return -ENOMEM;
-       }
-       /* get the PE structures from user space */
-       if (copy_from_user(pe, pep, size)) {
-               vfree(pe);
-               return -EFAULT;
-       }
-
-#ifdef DEBUG
-       printk(KERN_DEBUG
-              "%s -- fsync_dev and "
-              "invalidate_buffers for %s [%s] in %s\n",
-              lvm_name, lv_ptr->lv_name,
-              kdevname(lv_ptr->lv_dev),
-              vg_ptr->vg_name);
-#endif
-
-       lv_ptr->lv_status |= LV_SPINDOWN;
-       fsync_dev(lv_ptr->lv_dev);
-       lv_ptr->lv_status &= ~LV_ACTIVE;
-       invalidate_buffers(lv_ptr->lv_dev);
-
-       /* reduce allocation counters on PV(s) */
-       for (le = 0; le < lv_ptr->lv_allocated_le; le++) {
-               vg_ptr->pe_allocated--;
-               for (p = 0; p < vg_ptr->pv_cur; p++) {
-                       if (vg_ptr->pv[p]->pv_dev ==
-                       lv_ptr->lv_current_pe[le].dev) {
-                               vg_ptr->pv[p]->pe_allocated--;
-                               break;
-                       }
-               }
-       }
-
-
-       /* save pointer to "old" lv/pe pointer array */
-       pep1 = lv_ptr->lv_current_pe;
-       end = lv_ptr->lv_current_le;
-
-       /* save open counter */
-       lv_open = lv_ptr->lv_open;
-
-       /* save # of old allocated logical extents */
-       old_allocated_le = lv_ptr->lv_allocated_le;
-
-       /* copy preloaded LV */
-       lv_status_save = lv->lv_status;
-       lv->lv_status |= LV_SPINDOWN;
-       lv->lv_status &= ~LV_ACTIVE;
-       memcpy((char *) lv_ptr, (char *) lv, sizeof(lv_t));
-       lv_ptr->lv_current_pe = pe;
-       lv_ptr->lv_open = lv_open;
-
-       /* save availiable i/o statistic data */
-       /* linear logical volume */
-       if (lv_ptr->lv_stripes < 2) {
-               /* Check what last LE shall be used */
-               if (end > lv_ptr->lv_current_le) end = lv_ptr->lv_current_le;
-               for (le = 0; le < end; le++) {
-                       lv_ptr->lv_current_pe[le].reads  = pep1[le].reads;
-                       lv_ptr->lv_current_pe[le].writes = pep1[le].writes;
-               }
-               /* striped logical volume */
-       } else {
-               uint i, j, source, dest, end, old_stripe_size, new_stripe_size;
-
-               old_stripe_size = old_allocated_le / lv_ptr->lv_stripes;
-               new_stripe_size = lv_ptr->lv_allocated_le / lv_ptr->lv_stripes;
-               end = old_stripe_size;
-               if (end > new_stripe_size) end = new_stripe_size;
-               for (i = source = dest = 0;
-                    i < lv_ptr->lv_stripes; i++) {
-                       for (j = 0; j < end; j++) {
-                               lv_ptr->lv_current_pe[dest + j].reads =
-                                   pep1[source + j].reads;
-                               lv_ptr->lv_current_pe[dest + j].writes =
-                                   pep1[source + j].writes;
-                       }
-                       source += old_stripe_size;
-                       dest += new_stripe_size;
-               }
-       }
-       vfree(pep1);
-       pep1 = NULL;
-
-
-       /* extend the PE count in PVs */
-       for (le = 0; le < lv_ptr->lv_allocated_le; le++) {
-               vg_ptr->pe_allocated++;
-               for (p = 0; p < vg_ptr->pv_cur; p++) {
-                       if (vg_ptr->pv[p]->pv_dev ==
-                       vg_ptr->lv[l]->lv_current_pe[le].dev) {
-                               vg_ptr->pv[p]->pe_allocated++;
-                               break;
-                       }
-               }
-       }
-
-       lvm_gendisk.part[MINOR(lv_ptr->lv_dev)].start_sect = 0;
-       lvm_gendisk.part[MINOR(lv_ptr->lv_dev)].nr_sects = lv_ptr->lv_size;
-       lvm_size[MINOR(lv_ptr->lv_dev)] = lv_ptr->lv_size >> 1;
-       /* vg_lv_map array doesn't have to be changed here */
-
-       read_ahead[MAJOR_NR] = lv_ptr->lv_read_ahead = LVM_CORRECT_READ_AHEAD(lv_ptr->lv_read_ahead);
-       lv_ptr->lv_status = lv_status_save;
-
-       return 0;
-} /* lvm_do_lv_extend_reduce() */
-
-
-/*
- * character device support function logical volume status by name
- */
-static int lvm_do_lv_status_byname(vg_t *vg_ptr, void *arg)
-{
-       uint l;
-       ulong size;
-       lv_t lv;
-       lv_t *lv_ptr;
-       lv_status_byname_req_t lv_status_byname_req;
-
-       if (vg_ptr == NULL) return -ENXIO;
-       if (copy_from_user(&lv_status_byname_req, arg,
-                          sizeof(lv_status_byname_req_t)) != 0)
-               return -EFAULT;
-
-       if (lv_status_byname_req.lv == NULL) return -EINVAL;
-       if (copy_from_user(&lv, lv_status_byname_req.lv,
-                          sizeof(lv_t)) != 0)
-               return -EFAULT;
-
-       for (l = 0; l < vg_ptr->lv_max; l++) {
-               lv_ptr = vg_ptr->lv[l];
-               if (lv_ptr != NULL &&
-                   strcmp(lv_ptr->lv_name,
-                           lv_status_byname_req.lv_name) == 0) {
-                       if (copy_to_user(lv_status_byname_req.lv,
-                                        lv_ptr,
-                                        sizeof(lv_t)) != 0)
-                               return -EFAULT;
-
-                       if (lv.lv_current_pe != NULL) {
-                               size = lv_ptr->lv_allocated_le *
-                                      sizeof(pe_t);
-                               if (copy_to_user(lv.lv_current_pe,
-                                                lv_ptr->lv_current_pe,
-                                                size) != 0)
-                                       return -EFAULT;
-                       }
-                       return 0;
-               }
-       }
-       return -ENXIO;
-} /* lvm_do_lv_status_byname() */
-
-
-/*
- * character device support function logical volume status by index
- */
-static int lvm_do_lv_status_byindex(vg_t *vg_ptr,void *arg)
-{
-       ulong size;
-       lv_t lv;
-       lv_t *lv_ptr;
-       lv_status_byindex_req_t lv_status_byindex_req;
-
-       if (vg_ptr == NULL) return -ENXIO;
-       if (copy_from_user(&lv_status_byindex_req, arg,
-                          sizeof(lv_status_byindex_req)) != 0)
-               return -EFAULT;
-
-       if ((lvp = lv_status_byindex_req.lv) == NULL)
-               return -EINVAL;
-       if ( ( lv_ptr = vg_ptr->lv[lv_status_byindex_req.lv_index]) == NULL)
-               return -ENXIO;
-
-       if (copy_from_user(&lv, lvp, sizeof(lv_t)) != 0)
-               return -EFAULT;
-
-       if (copy_to_user(lvp, lv_ptr, sizeof(lv_t)) != 0)
-               return -EFAULT;
-
-       if (lv.lv_current_pe != NULL) {
-               size = lv_ptr->lv_allocated_le * sizeof(pe_t);
-               if (copy_to_user(lv.lv_current_pe,
-                                lv_ptr->lv_current_pe,
-                                size) != 0)
-                       return -EFAULT;
-       }
-       return 0;
-} /* lvm_do_lv_status_byindex() */
-
-
-/*
- * character device support function physical volume change
- */
-static int lvm_do_pv_change(vg_t *vg_ptr, void *arg)
-{
-       uint p;
-       pv_t *pv_ptr;
-#ifdef LVM_GET_INODE
-       struct inode *inode_sav;
-#endif
-
-       if (vg_ptr == NULL) return -ENXIO;
-       if (copy_from_user(&pv_change_req, arg,
-                          sizeof(pv_change_req)) != 0)
-               return -EFAULT;
-
-       for (p = 0; p < vg_ptr->pv_max; p++) {
-               pv_ptr = vg_ptr->pv[p];
-               if (pv_ptr != NULL &&
-                   strcmp(pv_ptr->pv_name,
-                              pv_change_req.pv_name) == 0) {
-#ifdef LVM_GET_INODE
-                       inode_sav = pv_ptr->inode;
-#endif
-                       if (copy_from_user(pv_ptr,
-                                          pv_change_req.pv,
-                                          sizeof(pv_t)) != 0)
-                               return -EFAULT;
-
-                       /* We don't need the PE list
-                          in kernel space as with LVs pe_t list */
-                       pv_ptr->pe = NULL;
-#ifdef LVM_GET_INODE
-                       pv_ptr->inode = inode_sav;
-#endif
-                       return 0;
-               }
-       }
-       return -ENXIO;
-} /* lvm_do_pv_change() */
-
-/*
- * character device support function get physical volume status
- */
-static int lvm_do_pv_status(vg_t *vg_ptr, void *arg)
-{
-       uint p;
-       pv_t *pv_ptr;
-
-       if (vg_ptr == NULL) return -ENXIO;
-       if (copy_from_user(&pv_status_req, arg,
-                          sizeof(pv_status_req)) != 0)
-               return -EFAULT;
-
-       for (p = 0; p < vg_ptr->pv_max; p++) {
-               pv_ptr = vg_ptr->pv[p];
-               if (pv_ptr != NULL &&
-                   strcmp(pv_ptr->pv_name,
-                              pv_status_req.pv_name) == 0) {
-                       if (copy_to_user(pv_status_req.pv,
-                                        pv_ptr,
-                                        sizeof(pv_t)) != 0)
-                               return -EFAULT;
-                       return 0;
-               }
-       }
-       return -ENXIO;
-} /* lvm_do_pv_status() */
-
-
-/*
- * support function initialize gendisk variables
- */
-#ifdef __initfunc
-__initfunc(void lvm_geninit(struct gendisk *lvm_gdisk))
-#else
-void __init
- lvm_geninit(struct gendisk *lvm_gdisk)
-#endif
-{
-       int i = 0;
-
-#ifdef DEBUG_GENDISK
-       printk(KERN_DEBUG "%s -- lvm_gendisk\n", lvm_name);
-#endif
-
-       for (i = 0; i < MAX_LV; i++) {
-               lvm_gendisk.part[i].start_sect = -1;    /* avoid partition check */
-               lvm_size[i] = lvm_gendisk.part[i].nr_sects = 0;
-               lvm_blocksizes[i] = BLOCK_SIZE;
-       }
-
-       blksize_size[MAJOR_NR] = lvm_blocksizes;
-       blk_size[MAJOR_NR] = lvm_size;
-
-       return;
-} /* lvm_gen_init() */
-
-
-#ifdef LVM_GET_INODE
-/*
- * support function to get an empty inode
- *
- * Gets an empty inode to be inserted into the inode hash,
- * so that a physical volume can't be mounted.
- * This is analog to drivers/block/md.c
- *
- * Is this the real thing?
- *
- *     No, it's bollocks. md.c tries to do a bit different thing that might
- * _somewhat_ work eons ago. Neither does any good these days. mount() couldn't
- * care less for icache (it cares only for ->s_root->d_count and if we want
- * loopback mounts even that will stop). BTW, with the form used here mount()
- * would have to scan the _whole_ icache to detect the attempt - how on the
- * Earth could it guess the i_ino of your dummy inode? Official line on the
- * exclusion between mount()/swapon()/open()/etc. is Just Don't Do It(tm).
- * If you can convince Linus that it's worth changing - fine, then you'll need
- * to do blkdev_get()/blkdev_put(). Until then...
- */
-struct inode *lvm_get_inode(kdev_t dev)
-{
-       struct inode *inode_this = NULL;
-
-       /* Lock the device by inserting a dummy inode. */
-       inode_this = get_empty_inode();
-       inode_this->i_dev = dev;
-       insert_inode_hash(inode_this);
-       return inode_this;
-}
-
-
-/*
- * support function to clear an inode
- *
- */
-void lvm_clear_inode(struct inode *inode)
-{
-#ifdef I_FREEING
-       inode->i_state |= I_FREEING;
-#endif
-       clear_inode(inode);
-       return;
-}
-#endif /* #ifdef LVM_GET_INODE */
index e08e07e65988d66d12ed719ebf5114de26f1f72d..c656858a7eb3937726f2c6e708604a307254fc9f 100644 (file)
@@ -1,3 +1,6 @@
+#ifndef __DRIVERS_PARIDE_H__
+#define __DRIVERS_PARIDE_H__
+
 /* 
        paride.h        (c) 1997-8  Grant R. Guenther <grant@torque.net>
                                    Under the terms of the GPL.
@@ -161,4 +164,5 @@ typedef struct pi_protocol PIP;
 extern int pi_register( PIP * );
 extern void pi_unregister ( PIP * );
 
+#endif /* __DRIVERS_PARIDE_H__ */
 /* end of paride.h */
index 9fb94b21a2b043f957313c0e01cb84f68f8ee3da..d91362d4e530b33b53d8d4cdc9fec4c8538235b0 100644 (file)
@@ -1118,6 +1118,7 @@ static int dvd_do_auth(struct cdrom_device_info *cdi, dvd_authinfo *ai)
                cdinfo(CD_DVD, "entering DVD_LU_SEND_RPC_STATE\n");
                setup_report_key(&cgc, 0, 8);
                memset(&rpc_state, 0, sizeof(rpc_state_t));
+               cgc.buffer = (char *) &rpc_state;
 
                if ((ret = cdo->generic_packet(cdi, &cgc)))
                        return ret;
index 77ee01ecf36eada2281cb998185fbd1bffb22916..d828e831d00f63e57d79a35d45666e441b85dea2 100644 (file)
@@ -183,7 +183,7 @@ static void init_clock(void)
        }
 #elif defined(__alpha__)
 #if CONFIG_FT_ALPHA_CLOCK == 0
-#error You must define and set CONFIG_FT_ALPHA_CLOCK in the Makefile !
+#error You must define and set CONFIG_FT_ALPHA_CLOCK in 'make config' !
 #endif
        extern struct hwrpb_struct *hwrpb;
        TRACE_FUN(ft_t_any);
index 4a109265a38f806dc0350879466cdbe4d984f12a..f2e15fc63e66f7be099fba3f4373ef830302820b 100644 (file)
@@ -7,7 +7,7 @@
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/init.h>
-#include <asm/delay.h>
+#include <linux/delay.h>
 #include <asm/io.h>
 #include "scan_keyb.h"
 
index 0348ba08b37306a0f7bc26891bbe8a65b43643fa..50ea4e44d10ac4f8e8e41443f0bb29c8d7c9592a 100644 (file)
@@ -40,8 +40,8 @@
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/pci.h>
-#include <linux/pci_ids.h>
 #include <linux/init.h>
+#include <linux/slab.h>
 #include <linux/gameport.h>
 
 #define PCI_VENDOR_ID_AUREAL   0x12eb
index 42be56d7a469d2d415161b232a10f15b7308f1de..05f97060b8028358aafe1970d90dc866834f2260 100644 (file)
@@ -693,7 +693,7 @@ found:
        if (!(ctrl & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
                BCD_TO_BIN(year);       /* This should never happen... */
        
-       if (year > 20 && year < 48) {
+       if (year >= 20 && year < 48) {
                epoch = 1980;
                guess = "ARC console";
        } else if (year >= 48 && year < 70) {
index 7b11c8f84001c7585a46033e74b1ddfb98eb320c..b5492def344d3f0dab87c22ec129fd3ea0e9cdee 100644 (file)
@@ -331,6 +331,10 @@ static int serial_pci_board_idx = 0;
 #define IS_PCI_REGION_IOPORT(dev, r) (pci_resource_flags((dev), (r)) & \
                                      IORESOURCE_IO)
 #endif
+#ifndef IS_PCI_REGION_IOMEM
+#define IS_PCI_REGION_IOMEM(dev, r) (pci_resource_flags((dev), (r)) & \
+                                     IORESOURCE_MEM)
+#endif
 #ifndef PCI_IRQ_RESOURCE
 #define PCI_IRQ_RESOURCE(dev, r) ((dev)->irq_resource[r].start)
 #endif
@@ -4185,7 +4189,7 @@ pci_timedia_fn(struct pci_dev *dev, struct pci_board *board, int enable)
  * This is the configuration table for all of the PCI serial boards
  * which we support.
  */
-static struct pci_board pci_boards[] __initdata = {
+static struct pci_board pci_boards[] __devinitdata = {
        /*
         * Vendor ID,   Device ID,
         * Subvendor ID,        Subdevice ID,
@@ -4606,9 +4610,9 @@ static int _INLINE_ serial_pci_guess_board(struct pci_dev *dev,
                        num_port++;
                        if (first_port == -1)
                                first_port = i;
-               } else {
-                       num_iomem++;
                }
+               if (IS_PCI_REGION_IOMEM(dev, i))
+                       num_iomem++;
        }
 
        /*
index 3780750a9d01c0418553c86541bd40d2d2c063bc..b33e7270c5dd70f1dfef7503ef2da2e1e592fcc9 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * $Id: via82cxxx.c,v 2.1 2000/08/29 01:34:60 vojtech Exp $
+ * $Id: via82cxxx.c,v 2.1b 2000/09/20 23:19:60 vojtech Exp $
  *
  *  Copyright (c) 2000 Vojtech Pavlik
  *
@@ -97,7 +97,6 @@ static const struct {
        { "vt82c586b",  PCI_DEVICE_ID_VIA_82C586_0, XFER_UDMA_2 },
        { "vt82c586a",  PCI_DEVICE_ID_VIA_82C586_0, XFER_UDMA_2 },
        { "vt82c586",   PCI_DEVICE_ID_VIA_82C586_0, XFER_MW_DMA_2 },
-       { "Unknown SouthBridge",        0,          XFER_UDMA_4 },
        { "Unknown SouthBridge",        0,          XFER_UDMA_2 },
 };
 
@@ -140,8 +139,8 @@ static const struct {
        { XFER_MW_DMA_1,  "MDMA1", 45,  80,  50, 150,   0 },
        { XFER_MW_DMA_0,  "MDMA0", 60, 215, 215, 480,   0 },
 
-       { XFER_SW_DMA_0,  "SDMA0", 60, 120, 120, 240,   0 },
-       { XFER_SW_DMA_0,  "SDMA0", 90, 240, 240, 480,   0 },
+       { XFER_SW_DMA_2,  "SDMA2", 60, 120, 120, 240,   0 },
+       { XFER_SW_DMA_1,  "SDMA1", 90, 240, 240, 480,   0 },
        { XFER_SW_DMA_0,  "SDMA0",120, 480, 480, 960,   0 },
 
        { XFER_PIO_5,     "PIO5",  20,  50,  30, 100,   0 },
@@ -193,7 +192,7 @@ static int via_get_info(char *buffer, char **addr, off_t offset, int count)
 
        via_print("----------VIA BusMastering IDE Configuration----------------");
 
-       via_print("Driver Version:                     2.1");
+       via_print("Driver Version:                     2.1b");
 
        pci_read_config_byte(isa_dev, PCI_REVISION_ID, &t);
        via_print("South Bridge:                       VIA %s rev %#x", via_isa_bridges[via_config].name, t);
@@ -213,9 +212,6 @@ static int via_get_info(char *buffer, char **addr, off_t offset, int count)
        via_print("FIFO Output Data 1/2 Clock Advance: %s", (t & 16) ? "on" : "off" );
        via_print("BM IDE Status Register Read Retry:  %s", (t & 8) ? "on" : "off" );
 
-       pci_read_config_byte(dev, VIA_MISC_2, &t);
-       sprintf(p, "Interrupt Steering Swap:           %s", (t & 64) ? "on" : "off");
-
        pci_read_config_byte(dev, VIA_MISC_3, &t);
        via_print("Max DRDY Pulse Width:               %s%s", via_control3[(t & 0x03)], (t & 0x03) ? "PCI clocks" : "");
 
@@ -337,15 +333,13 @@ static int via_set_speed(ide_drive_t *drive, byte speed)
  * UDMA cycle
  */
 
-       if (via_timing[i].udma) {
-               t = 0xe8;
-               if (via_isa_bridges[via_config].speed >= XFER_UDMA_4)
-                       t |= FIT(ENOUGH(via_timing[i].udma, T >> 1) - 2, 0, 7);
-               else
-                       t |= FIT(ENOUGH(via_timing[i].udma, T     ) - 2, 0, 3);
-       } else t = 0x0b;
+       switch(via_isa_bridges[via_config].speed) {
+               case XFER_UDMA_2: t = via_timing[i].udma ? (0x60 | (FIT(via_timing[i].udma, 2, 5) - 2)) : 0x03; break;
+               case XFER_UDMA_4: t = via_timing[i].udma ? (0xe8 | (FIT(via_timing[i].udma, 2, 9) - 2)) : 0x0f; break;
+        }
 
-       via_write_config_byte(dev, VIA_UDMA_TIMING + (3 - drive->dn), t);
+       if (via_isa_bridges[via_config].speed != XFER_MW_DMA_2)
+               via_write_config_byte(dev, VIA_UDMA_TIMING + (3 - drive->dn), t);
 
 /*
  * Drive init
@@ -511,6 +505,11 @@ unsigned int __init pci_init_via82cxxx(struct pci_dev *dev, const char *name)
                if (t < 0x20) via_config++;                     /* vt82c586 */
        }
 
+       if (via_isa_bridges[via_config].id == PCI_DEVICE_ID_VIA_82C596) {
+               pci_read_config_byte(isa, PCI_REVISION_ID, &t);
+               if (t < 0x10) via_config++;                     /* vt82c596a */
+       }
+
 /*
  * Check UDMA66 mode set by BIOS.
  */
@@ -530,13 +529,8 @@ unsigned int __init pci_init_via82cxxx(struct pci_dev *dev, const char *name)
 /*
  * Set UDMA66 double clock bits.
  */
-
-       pci_write_config_dword(dev, VIA_UDMA_TIMING, u | 0x80008);
-       pci_read_config_dword(dev, VIA_UDMA_TIMING, &u);
-
-       if ((via_isa_bridges[via_config].id == PCI_DEVICE_ID_VIA_82C596 || !isa)
-               && (u & 0x80008) != 0x80008)
-                       via_config++;                           /* vt82c596a / Unknown UDMA33 */
+       if (via_isa_bridges[via_config].speed == XFER_UDMA_4)
+               pci_write_config_dword(dev, VIA_UDMA_TIMING, u | 0x80008);
 
 /*
  * Set up FIFO, flush, prefetch and post-writes.
index fd284e718d5ca4d13c4f08d206c56983d83ab21e..f7c23cd87aa23928eac4db68be395961427ce706 100644 (file)
@@ -4,7 +4,7 @@
 mainmenu_option next_comment
 comment 'Multi-device support (RAID and LVM)'
 
-tristate 'Multiple devices driver support' CONFIG_BLK_DEV_MD
+tristate 'Multiple devices driver support (RAID and LVM)' CONFIG_BLK_DEV_MD
 dep_tristate '  Linear (append) mode' CONFIG_MD_LINEAR $CONFIG_BLK_DEV_MD
 dep_tristate '  RAID-0 (striping) mode' CONFIG_MD_RAID0 $CONFIG_BLK_DEV_MD
 dep_tristate '  RAID-1 (mirroring) mode' CONFIG_MD_RAID1 $CONFIG_BLK_DEV_MD
@@ -14,9 +14,7 @@ if [ "$CONFIG_MD_LINEAR" = "y" -o "$CONFIG_MD_RAID0" = "y" -o "$CONFIG_MD_RAID1"
         bool '  Auto Detect support' CONFIG_AUTODETECT_RAID
 fi
 
-tristate 'Logical volume manager (LVM) support' CONFIG_BLK_DEV_LVM N
-if [ "$CONFIG_BLK_DEV_LVM" != "n" ]; then
-   bool '   LVM information in proc filesystem' CONFIG_LVM_PROC_FS Y
-fi
+dep_tristate 'Logical volume manager (LVM) support' CONFIG_BLK_DEV_LVM $CONFIG_BLK_DEV_MD
+dep_mbool '   LVM information in proc filesystem' CONFIG_LVM_PROC_FS $CONFIG_BLK_DEV_LVM
 
 endmenu
diff --git a/drivers/md/lvm-snap.c b/drivers/md/lvm-snap.c
new file mode 100644 (file)
index 0000000..04007c1
--- /dev/null
@@ -0,0 +1,436 @@
+/*
+ * kernel/lvm-snap.c
+ *
+ * Copyright (C) 2000 Andrea Arcangeli <andrea@suse.de> SuSE
+ *
+ * LVM snapshot driver is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ * 
+ * LVM driver is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with GNU CC; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA. 
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/vmalloc.h>
+#include <linux/blkdev.h>
+#include <linux/smp_lock.h>
+#include <linux/types.h>
+#include <linux/iobuf.h>
+#include <linux/lvm.h>
+
+
+static char *lvm_snap_version __attribute__ ((unused)) = "LVM 0.8final (15/02/2000)\n";
+
+extern const char *const lvm_name;
+extern int lvm_blocksizes[];
+
+void lvm_snapshot_release(lv_t *);
+
+#define hashfn(dev,block,mask,chunk_size) \
+       ((HASHDEV(dev)^((block)/(chunk_size))) & (mask))
+
+static inline lv_block_exception_t *
+lvm_find_exception_table(kdev_t org_dev, unsigned long org_start, lv_t * lv)
+{
+       struct list_head * hash_table = lv->lv_snapshot_hash_table, * next;
+       unsigned long mask = lv->lv_snapshot_hash_mask;
+       int chunk_size = lv->lv_chunk_size;
+       lv_block_exception_t * ret;
+       int i = 0;
+
+       hash_table = &hash_table[hashfn(org_dev, org_start, mask, chunk_size)];
+       ret = NULL;
+       for (next = hash_table->next; next != hash_table; next = next->next)
+       {
+               lv_block_exception_t * exception;
+
+               exception = list_entry(next, lv_block_exception_t, hash);
+               if (exception->rsector_org == org_start &&
+                   exception->rdev_org == org_dev)
+               {
+                       if (i)
+                       {
+                               /* fun, isn't it? :) */
+                               list_del(next);
+                               list_add(next, hash_table);
+                       }
+                       ret = exception;
+                       break;
+               }
+               i++;
+       }
+       return ret;
+}
+
+static inline void lvm_hash_link(lv_block_exception_t * exception,
+                                kdev_t org_dev, unsigned long org_start,
+                                lv_t * lv)
+{
+       struct list_head * hash_table = lv->lv_snapshot_hash_table;
+       unsigned long mask = lv->lv_snapshot_hash_mask;
+       int chunk_size = lv->lv_chunk_size;
+
+       hash_table = &hash_table[hashfn(org_dev, org_start, mask, chunk_size)];
+       list_add(&exception->hash, hash_table);
+}
+
+int lvm_snapshot_remap_block(kdev_t * org_dev, unsigned long * org_sector,
+                            unsigned long pe_start, lv_t * lv)
+{
+       int ret;
+       unsigned long pe_off, pe_adjustment, __org_start;
+       kdev_t __org_dev;
+       int chunk_size = lv->lv_chunk_size;
+       lv_block_exception_t * exception;
+
+       pe_off = pe_start % chunk_size;
+       pe_adjustment = (*org_sector-pe_off) % chunk_size;
+       __org_start = *org_sector - pe_adjustment;
+       __org_dev = *org_dev;
+
+       ret = 0;
+       exception = lvm_find_exception_table(__org_dev, __org_start, lv);
+       if (exception)
+       {
+               *org_dev = exception->rdev_new;
+               *org_sector = exception->rsector_new + pe_adjustment;
+               ret = 1;
+       }
+       return ret;
+}
+
+static void lvm_drop_snapshot(lv_t * lv_snap, const char * reason)
+{
+       kdev_t last_dev;
+       int i;
+
+       /* no exception storage space available for this snapshot
+          or error on this snapshot --> release it */
+       invalidate_buffers(lv_snap->lv_dev);
+
+       last_dev = 0;
+       for (i = 0; i < lv_snap->lv_remap_ptr; i++) {
+               if ( lv_snap->lv_block_exception[i].rdev_new != last_dev) {
+                       last_dev = lv_snap->lv_block_exception[i].rdev_new;
+                       invalidate_buffers(last_dev);
+               }
+       }
+
+       lvm_snapshot_release(lv_snap);
+
+       printk(KERN_INFO
+              "%s -- giving up to snapshot %s on %s due %s\n",
+              lvm_name, lv_snap->lv_snapshot_org->lv_name, lv_snap->lv_name,
+              reason);
+}
+
+static inline void lvm_snapshot_prepare_blocks(unsigned long * blocks,
+                                              unsigned long start,
+                                              int nr_sectors,
+                                              int blocksize)
+{
+       int i, sectors_per_block, nr_blocks;
+
+       sectors_per_block = blocksize >> 9;
+       nr_blocks = nr_sectors / sectors_per_block;
+       start /= sectors_per_block;
+
+       for (i = 0; i < nr_blocks; i++)
+               blocks[i] = start++;
+}
+
+static inline int get_blksize(kdev_t dev)
+{
+       int correct_size = BLOCK_SIZE, i, major;
+
+       major = MAJOR(dev);
+       if (blksize_size[major])
+       {
+               i = blksize_size[major][MINOR(dev)];
+               if (i)
+                       correct_size = i;
+       }
+       return correct_size;
+}
+
+#ifdef DEBUG_SNAPSHOT
+static inline void invalidate_snap_cache(unsigned long start, unsigned long nr,
+                                        kdev_t dev)
+{
+       struct buffer_head * bh;
+       int sectors_per_block, i, blksize, minor;
+
+       minor = MINOR(dev);
+       blksize = lvm_blocksizes[minor];
+       sectors_per_block = blksize >> 9;
+       nr /= sectors_per_block;
+       start /= sectors_per_block;
+
+       for (i = 0; i < nr; i++)
+       {
+               bh = get_hash_table(dev, start++, blksize);
+               if (bh)
+                       bforget(bh);
+       }
+}
+#endif
+
+/*
+ * copy on write handler for one snapshot logical volume
+ *
+ * read the original blocks and store it/them on the new one(s).
+ * if there is no exception storage space free any longer --> release snapshot.
+ *
+ * this routine gets called for each _first_ write to a physical chunk.
+ */
+int lvm_snapshot_COW(kdev_t org_phys_dev,
+                    unsigned long org_phys_sector,
+                    unsigned long org_pe_start,
+                    unsigned long org_virt_sector,
+                    lv_t * lv_snap)
+{
+       const char * reason;
+       unsigned long org_start, snap_start, virt_start, pe_off;
+       int idx = lv_snap->lv_remap_ptr, chunk_size = lv_snap->lv_chunk_size;
+       kdev_t snap_phys_dev;
+       struct kiobuf * iobuf;
+       unsigned long blocks[KIO_MAX_SECTORS];
+       int blksize_snap, blksize_org, min_blksize, max_blksize;
+       int max_sectors, nr_sectors;
+
+       /* check if we are out of snapshot space */
+       if (idx >= lv_snap->lv_remap_end)
+               goto fail_out_of_space;
+
+       /* calculate physical boundaries of source chunk */
+       pe_off = org_pe_start % chunk_size;
+       org_start = org_phys_sector - ((org_phys_sector-pe_off) % chunk_size);
+       virt_start = org_virt_sector - (org_phys_sector - org_start);
+
+       /* calculate physical boundaries of destination chunk */
+       snap_phys_dev = lv_snap->lv_block_exception[idx].rdev_new;
+       snap_start = lv_snap->lv_block_exception[idx].rsector_new;
+
+#ifdef DEBUG_SNAPSHOT
+       printk(KERN_INFO
+              "%s -- COW: "
+              "org %02d:%02d faulting %lu start %lu, "
+              "snap %02d:%02d start %lu, "
+              "size %d, pe_start %lu pe_off %lu, virt_sec %lu\n",
+              lvm_name,
+              MAJOR(org_phys_dev), MINOR(org_phys_dev), org_phys_sector,
+              org_start,
+              MAJOR(snap_phys_dev), MINOR(snap_phys_dev), snap_start,
+              chunk_size,
+              org_pe_start, pe_off,
+              org_virt_sector);
+#endif
+
+       iobuf = lv_snap->lv_iobuf;
+
+       blksize_org = get_blksize(org_phys_dev);
+       blksize_snap = get_blksize(snap_phys_dev);
+       max_blksize = max(blksize_org, blksize_snap);
+       min_blksize = min(blksize_org, blksize_snap);
+       max_sectors = KIO_MAX_SECTORS * (min_blksize>>9);
+
+       if (chunk_size % (max_blksize>>9))
+               goto fail_blksize;
+
+       while (chunk_size)
+       {
+               nr_sectors = min(chunk_size, max_sectors);
+               chunk_size -= nr_sectors;
+
+               iobuf->length = nr_sectors << 9;
+
+               lvm_snapshot_prepare_blocks(blocks, org_start,
+                                           nr_sectors, blksize_org);
+               if (brw_kiovec(READ, 1, &iobuf, org_phys_dev,
+                              blocks, blksize_org) != (nr_sectors<<9))
+                       goto fail_raw_read;
+
+               lvm_snapshot_prepare_blocks(blocks, snap_start,
+                                           nr_sectors, blksize_snap);
+               if (brw_kiovec(WRITE, 1, &iobuf, snap_phys_dev,
+                              blocks, blksize_snap) != (nr_sectors<<9))
+                       goto fail_raw_write;
+       }
+
+#ifdef DEBUG_SNAPSHOT
+       /* invalidate the logcial snapshot buffer cache */
+       invalidate_snap_cache(virt_start, lv_snap->lv_chunk_size,
+                             lv_snap->lv_dev);
+#endif
+
+       /* the original chunk is now stored on the snapshot volume
+          so update the execption table */
+       lv_snap->lv_block_exception[idx].rdev_org = org_phys_dev;
+       lv_snap->lv_block_exception[idx].rsector_org = org_start;
+       lvm_hash_link(lv_snap->lv_block_exception + idx,
+                     org_phys_dev, org_start, lv_snap);
+       lv_snap->lv_remap_ptr = idx + 1;
+       return 1;
+
+       /* slow path */
+ out:
+       lvm_drop_snapshot(lv_snap, reason);
+       return -1;
+
+ fail_out_of_space:
+       reason = "out of space";
+       goto out;
+ fail_raw_read:
+       reason = "read error";
+       goto out;
+ fail_raw_write:
+       reason = "write error";
+       goto out;
+ fail_blksize:
+       reason = "blocksize error";
+       goto out;
+}
+
+static int lvm_snapshot_alloc_iobuf_pages(struct kiobuf * iobuf, int sectors)
+{
+       int bytes, nr_pages, err, i;
+
+       bytes = sectors << 9;
+       nr_pages = (bytes + ~PAGE_MASK) >> PAGE_SHIFT;
+       err = expand_kiobuf(iobuf, nr_pages);
+       if (err)
+               goto out;
+
+       err = -ENOMEM;
+       iobuf->locked = 1;
+       iobuf->nr_pages = 0;
+       for (i = 0; i < nr_pages; i++)
+       {
+               struct page * page;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,27)
+               page = alloc_page(GFP_KERNEL);
+               if (!page)
+                       goto out;
+#else
+               {
+                       unsigned long addr = __get_free_page(GFP_USER);
+                       if (!addr)
+                               goto out;
+                       iobuf->pagelist[i] = addr;
+                       page = virt_to_page(addr);
+               }
+#endif
+
+               iobuf->maplist[i] = page;
+               /* the only point to lock the page here is to be allowed
+                  to share unmap_kiobuf() in the fail-path */
+#ifndef LockPage
+#define LockPage(map) set_bit(PG_locked, &(map)->flags)
+#endif
+               LockPage(page);
+               iobuf->nr_pages++;
+       }
+       iobuf->offset = 0;
+
+       err = 0;
+ out:
+       return err;
+}
+
+static int calc_max_buckets(void)
+{
+       unsigned long mem;
+
+       mem = num_physpages << PAGE_SHIFT;
+       mem /= 100;
+       mem *= 2;
+       mem /= sizeof(struct list_head);
+
+       return mem;
+}
+
+static int lvm_snapshot_alloc_hash_table(lv_t * lv)
+{
+       int err;
+       unsigned long buckets, max_buckets, size;
+       struct list_head * hash;
+
+       buckets = lv->lv_remap_end;
+       max_buckets = calc_max_buckets();
+       buckets = min(buckets, max_buckets);
+       while (buckets & (buckets-1))
+               buckets &= (buckets-1);
+
+       size = buckets * sizeof(struct list_head);
+
+       err = -ENOMEM;
+       hash = vmalloc(size);
+       lv->lv_snapshot_hash_table = hash;
+
+       if (!hash)
+               goto out;
+
+       lv->lv_snapshot_hash_mask = buckets-1;
+       while (buckets--)
+               INIT_LIST_HEAD(hash+buckets);
+       err = 0;
+ out:
+       return err;
+}
+
+int lvm_snapshot_alloc(lv_t * lv_snap)
+{
+       int err, blocksize, max_sectors;
+
+       err = alloc_kiovec(1, &lv_snap->lv_iobuf);
+       if (err)
+               goto out;
+
+       blocksize = lvm_blocksizes[MINOR(lv_snap->lv_dev)];
+       max_sectors = KIO_MAX_SECTORS << (PAGE_SHIFT-9);
+
+       err = lvm_snapshot_alloc_iobuf_pages(lv_snap->lv_iobuf, max_sectors);
+       if (err)
+               goto out_free_kiovec;
+
+       err = lvm_snapshot_alloc_hash_table(lv_snap);
+       if (err)
+               goto out_free_kiovec;
+ out:
+       return err;
+
+ out_free_kiovec:
+       unmap_kiobuf(lv_snap->lv_iobuf);
+       free_kiovec(1, &lv_snap->lv_iobuf);
+       goto out;
+}
+
+void lvm_snapshot_release(lv_t * lv)
+{
+       if (lv->lv_block_exception)
+       {
+               vfree(lv->lv_block_exception);
+               lv->lv_block_exception = NULL;
+       }
+       if (lv->lv_snapshot_hash_table)
+       {
+               vfree(lv->lv_snapshot_hash_table);
+               lv->lv_snapshot_hash_table = NULL;
+       }
+       if (lv->lv_iobuf)
+       {
+               free_kiovec(1, &lv->lv_iobuf);
+               lv->lv_iobuf = NULL;
+       }
+}
diff --git a/drivers/md/lvm.c b/drivers/md/lvm.c
new file mode 100644 (file)
index 0000000..239ed99
--- /dev/null
@@ -0,0 +1,2567 @@
+/*
+ * kernel/lvm.c
+ *
+ * Copyright (C) 1997 - 2000  Heinz Mauelshagen, Germany
+ *
+ * February-November 1997
+ * April-May,July-August,November 1998
+ * January-March,May,July,September,October 1999
+ * January,February 2000
+ *
+ *
+ * LVM driver is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ * 
+ * LVM driver is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with GNU CC; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA. 
+ *
+ */
+
+/*
+ * Changelog
+ *
+ *    09/11/1997 - added chr ioctls VG_STATUS_GET_COUNT
+ *                 and VG_STATUS_GET_NAMELIST
+ *    18/01/1998 - change lvm_chr_open/close lock handling
+ *    30/04/1998 - changed LV_STATUS ioctl to LV_STATUS_BYNAME and
+ *               - added   LV_STATUS_BYINDEX ioctl
+ *               - used lvm_status_byname_req_t and
+ *                      lvm_status_byindex_req_t vars
+ *    04/05/1998 - added multiple device support
+ *    08/05/1998 - added support to set/clear extendable flag in volume group
+ *    09/05/1998 - changed output of lvm_proc_get_info() because of
+ *                 support for free (eg. longer) logical volume names
+ *    12/05/1998 - added spin_locks (thanks to Pascal van Dam
+ *                 <pascal@ramoth.xs4all.nl>)
+ *    25/05/1998 - fixed handling of locked PEs in lvm_map() and lvm_chr_ioctl()
+ *    26/05/1998 - reactivated verify_area by access_ok
+ *    07/06/1998 - used vmalloc/vfree instead of kmalloc/kfree to go
+ *                 beyond 128/256 KB max allocation limit per call
+ *               - #ifdef blocked spin_lock calls to avoid compile errors
+ *                 with 2.0.x
+ *    11/06/1998 - another enhancement to spinlock code in lvm_chr_open()
+ *                 and use of LVM_VERSION_CODE instead of my own macros
+ *                 (thanks to  Michael Marxmeier <mike@msede.com>)
+ *    07/07/1998 - added statistics in lvm_map()
+ *    08/07/1998 - saved statistics in lvm_do_lv_extend_reduce()
+ *    25/07/1998 - used __initfunc macro
+ *    02/08/1998 - changes for official char/block major numbers
+ *    07/08/1998 - avoided init_module() and cleanup_module() to be static
+ *    30/08/1998 - changed VG lv_open counter from sum of LV lv_open counters
+ *                 to sum of LVs open (no matter how often each is)
+ *    01/09/1998 - fixed lvm_gendisk.part[] index error
+ *    07/09/1998 - added copying of lv_current_pe-array
+ *                 in LV_STATUS_BYINDEX ioctl
+ *    17/11/1998 - added KERN_* levels to printk
+ *    13/01/1999 - fixed LV index bug in lvm_do_lv_create() which hit lvrename
+ *    07/02/1999 - fixed spinlock handling bug in case of LVM_RESET
+ *                 by moving spinlock code from lvm_chr_open()
+ *                 to lvm_chr_ioctl()
+ *               - added LVM_LOCK_LVM ioctl to lvm_chr_ioctl()
+ *               - allowed LVM_RESET and retrieval commands to go ahead;
+ *                 only other update ioctls are blocked now
+ *               - fixed pv->pe to NULL for pv_status
+ *               - using lv_req structure in lvm_chr_ioctl() now
+ *               - fixed NULL ptr reference bug in lvm_do_lv_extend_reduce()
+ *                 caused by uncontiguous PV array in lvm_chr_ioctl(VG_REDUCE)
+ *    09/02/1999 - changed BLKRASET and BLKRAGET in lvm_chr_ioctl() to
+ *                 handle lgoical volume private read ahead sector
+ *               - implemented LV read_ahead handling with lvm_blk_read()
+ *                 and lvm_blk_write()
+ *    10/02/1999 - implemented 2.[12].* support function lvm_hd_name()
+ *                 to be used in drivers/block/genhd.c by disk_name()
+ *    12/02/1999 - fixed index bug in lvm_blk_ioctl(), HDIO_GETGEO
+ *               - enhanced gendisk insert/remove handling
+ *    16/02/1999 - changed to dynamic block minor number allocation to
+ *                 have as much as 99 volume groups with 256 logical volumes
+ *                 as the grand total; this allows having 1 volume group with
+ *                 up to 256 logical volumes in it
+ *    21/02/1999 - added LV open count information to proc filesystem
+ *               - substituted redundant LVM_RESET code by calls
+ *                 to lvm_do_vg_remove()
+ *    22/02/1999 - used schedule_timeout() to be more responsive
+ *                 in case of lvm_do_vg_remove() with lots of logical volumes
+ *    19/03/1999 - fixed NULL pointer bug in module_init/lvm_init
+ *    17/05/1999 - used DECLARE_WAIT_QUEUE_HEAD macro (>2.3.0)
+ *               - enhanced lvm_hd_name support
+ *    03/07/1999 - avoided use of KERNEL_VERSION macro based ifdefs and
+ *                 memcpy_tofs/memcpy_fromfs macro redefinitions
+ *    06/07/1999 - corrected reads/writes statistic counter copy in case
+ *                 of striped logical volume
+ *    28/07/1999 - implemented snapshot logical volumes
+ *                 - lvm_chr_ioctl
+ *                   - LV_STATUS_BYINDEX
+ *                   - LV_STATUS_BYNAME
+ *                 - lvm_do_lv_create
+ *                 - lvm_do_lv_remove
+ *                 - lvm_map
+ *                 - new lvm_snapshot_remap_block
+ *                 - new lvm_snapshot_remap_new_block
+ *    08/10/1999 - implemented support for multiple snapshots per
+ *                 original logical volume
+ *    12/10/1999 - support for 2.3.19
+ *    11/11/1999 - support for 2.3.28
+ *    21/11/1999 - changed lvm_map() interface to buffer_head based
+ *    19/12/1999 - support for 2.3.33
+ *    01/01/2000 - changed locking concept in lvm_map(),
+ *                 lvm_do_vg_create() and lvm_do_lv_remove()
+ *    15/01/2000 - fixed PV_FLUSH bug in lvm_chr_ioctl()
+ *    24/01/2000 - ported to 2.3.40 including Alan Cox's pointer changes etc.
+ *    29/01/2000 - used kmalloc/kfree again for all small structures
+ *    20/01/2000 - cleaned up lvm_chr_ioctl by moving code
+ *                 to seperated functions
+ *               - avoided "/dev/" in proc filesystem output
+ *               - avoided inline strings functions lvm_strlen etc.
+ *    14/02/2000 - support for 2.3.43
+ *               - integrated Andrea Arcangeli's snapshot code
+ *
+ */
+
+
+static char *lvm_version = "LVM version 0.8final  by Heinz Mauelshagen  (15/02/2000)\n";
+static char *lvm_short_version = "version 0.8final  (15/02/2000)";
+
+#define MAJOR_NR       LVM_BLK_MAJOR
+#define        DEVICE_OFF(device)
+
+#include <linux/config.h>
+#include <linux/version.h>
+
+#ifdef MODVERSIONS
+#undef MODULE
+#define MODULE
+#include <linux/modversions.h>
+#endif
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+
+#include <linux/hdreg.h>
+#include <linux/stat.h>
+#include <linux/fs.h>
+#include <linux/proc_fs.h>
+#include <linux/blkdev.h>
+#include <linux/genhd.h>
+#include <linux/locks.h>
+#include <linux/smp_lock.h>
+#include <asm/ioctl.h>
+#include <asm/segment.h>
+#include <asm/uaccess.h>
+
+#ifdef CONFIG_KERNELD
+#include <linux/kerneld.h>
+#endif
+
+#define LOCAL_END_REQUEST
+
+#include <linux/blk.h>
+#include <linux/blkpg.h>
+
+#include <linux/errno.h>
+#include <linux/lvm.h>
+
+#define        LVM_CORRECT_READ_AHEAD(a)                               \
+       (((a) < LVM_MIN_READ_AHEAD || (a) > LVM_MAX_READ_AHEAD) \
+        ? LVM_MAX_READ_AHEAD : (a))
+
+#ifndef WRITEA
+#  define WRITEA WRITE
+#endif
+
+/*
+ * External function prototypes
+ */
+#ifdef MODULE
+int init_module(void);
+void cleanup_module(void);
+#else
+extern int lvm_init(void);
+#endif
+
+static void lvm_dummy_device_request(request_queue_t *);
+#define        DEVICE_REQUEST  lvm_dummy_device_request
+
+static int lvm_make_request_fn(request_queue_t *, int, struct buffer_head*);
+static void lvm_plug_device_noop(request_queue_t *, kdev_t);
+
+static int lvm_blk_ioctl(struct inode *, struct file *, uint, ulong);
+static int lvm_blk_open(struct inode *, struct file *);
+
+static int lvm_chr_open(struct inode *, struct file *);
+
+static int lvm_chr_close(struct inode *, struct file *);
+static int lvm_blk_close(struct inode *, struct file *);
+
+static int lvm_chr_ioctl(struct inode *, struct file *, uint, ulong);
+
+#if defined CONFIG_LVM_PROC_FS && defined CONFIG_PROC_FS
+static int lvm_proc_get_info(char *, char **, off_t, int);
+static int (*lvm_proc_get_info_ptr) (char *, char **, off_t, int) =
+&lvm_proc_get_info;
+#endif
+
+#ifdef LVM_HD_NAME
+void lvm_hd_name(char *, int);
+#endif
+/* End external function prototypes */
+
+
+/*
+ * Internal function prototypes
+ */
+static void lvm_init_vars(void);
+
+/* external snapshot calls */
+int lvm_snapshot_remap_block(kdev_t *, ulong *, ulong, lv_t *);
+int lvm_snapshot_COW(kdev_t, ulong, ulong, ulong, lv_t *);
+int lvm_snapshot_alloc(lv_t *);
+void lvm_snapshot_release(lv_t *); 
+
+#ifdef LVM_HD_NAME
+extern void (*lvm_hd_name_ptr) (char *, int);
+#endif
+static int lvm_map(struct buffer_head *, int);
+static int lvm_do_lock_lvm(void);
+static int lvm_do_le_remap(vg_t *, void *);
+static int lvm_do_pe_lock_unlock(vg_t *r, void *);
+static int lvm_do_vg_create(int, void *);
+static int lvm_do_vg_extend(vg_t *, void *);
+static int lvm_do_vg_reduce(vg_t *, void *);
+static int lvm_do_vg_remove(int);
+static int lvm_do_lv_create(int, char *, lv_t *);
+static int lvm_do_lv_remove(int, char *, int);
+static int lvm_do_lv_extend_reduce(int, char *, lv_t *);
+static int lvm_do_lv_status_byname(vg_t *r, void *);
+static int lvm_do_lv_status_byindex(vg_t *, void *arg);
+static int lvm_do_pv_change(vg_t*, void*);
+static int lvm_do_pv_status(vg_t *, void *);
+static void lvm_geninit(struct gendisk *);
+#ifdef LVM_GET_INODE
+static struct inode *lvm_get_inode(kdev_t);
+void lvm_clear_inode(struct inode *);
+#endif
+/* END Internal function prototypes */
+
+
+/* volume group descriptor area pointers */
+static vg_t *vg[ABS_MAX_VG];
+static pv_t *pvp = NULL;
+static lv_t *lvp = NULL;
+static pe_t *pep = NULL;
+static pe_t *pep1 = NULL;
+
+
+/* map from block minor number to VG and LV numbers */
+typedef struct {
+       int vg_number;
+       int lv_number;
+} vg_lv_map_t;
+static vg_lv_map_t vg_lv_map[ABS_MAX_LV];
+
+
+/* Request structures (lvm_chr_ioctl()) */
+static pv_change_req_t pv_change_req;
+static pv_flush_req_t pv_flush_req;
+static pv_status_req_t pv_status_req;
+static pe_lock_req_t pe_lock_req;
+static le_remap_req_t le_remap_req;
+static lv_req_t lv_req;
+
+#ifdef LVM_TOTAL_RESET
+static int lvm_reset_spindown = 0;
+#endif
+
+static char pv_name[NAME_LEN];
+/* static char rootvg[NAME_LEN] = { 0, }; */
+static uint lv_open = 0;
+const char *const lvm_name = LVM_NAME;
+static int lock = 0;
+static int loadtime = 0;
+static uint vg_count = 0;
+static long lvm_chr_open_count = 0;
+static ushort lvm_iop_version = LVM_DRIVER_IOP_VERSION;
+static DECLARE_WAIT_QUEUE_HEAD(lvm_snapshot_wait);
+static DECLARE_WAIT_QUEUE_HEAD(lvm_wait);
+static DECLARE_WAIT_QUEUE_HEAD(lvm_map_wait);
+
+static spinlock_t lvm_lock = SPIN_LOCK_UNLOCKED;
+
+static devfs_handle_t lvm_devfs_handle;
+static devfs_handle_t vg_devfs_handle[MAX_VG];
+static devfs_handle_t ch_devfs_handle[MAX_VG];
+static devfs_handle_t lv_devfs_handle[MAX_LV];
+
+static struct file_operations lvm_chr_fops =
+{
+       owner:          THIS_MODULE,
+       open:           lvm_chr_open,
+       release:        lvm_chr_close,
+       ioctl:          lvm_chr_ioctl,
+};
+
+static struct block_device_operations lvm_blk_dops =
+{
+       open:           lvm_blk_open,
+       release:        lvm_blk_close,
+       ioctl:          lvm_blk_ioctl
+};
+
+/* gendisk structures */
+static struct hd_struct lvm_hd_struct[MAX_LV];
+static int lvm_blocksizes[MAX_LV] =
+{0,};
+static int lvm_size[MAX_LV] =
+{0,};
+static struct gendisk lvm_gendisk =
+{
+       MAJOR_NR,               /* major # */
+       LVM_NAME,               /* name of major */
+       0,                      /* number of times minor is shifted
+                                  to get real minor */
+       1,                      /* maximum partitions per device */
+       lvm_hd_struct,          /* partition table */
+       lvm_size,               /* device size in blocks, copied
+                                  to block_size[] */
+       MAX_LV,                 /* number or real devices */
+       NULL,                   /* internal */
+       NULL,                   /* pointer to next gendisk struct (internal) */
+};
+
+
+#ifdef MODULE
+/*
+ * Module initialization...
+ */
+int init_module(void)
+#else
+/*
+ * Driver initialization...
+ */
+#ifdef __initfunc
+__initfunc(int lvm_init(void))
+#else
+int __init lvm_init(void)
+#endif
+#endif                         /* #ifdef MODULE */
+{
+       struct gendisk *gendisk_ptr = NULL;
+
+       if (register_chrdev(LVM_CHAR_MAJOR, lvm_name, &lvm_chr_fops) < 0) {
+               printk(KERN_ERR "%s -- register_chrdev failed\n", lvm_name);
+               return -EIO;
+       }
+       if (register_blkdev(MAJOR_NR, lvm_name, &lvm_blk_dops) < 0) {
+               printk("%s -- register_blkdev failed\n", lvm_name);
+               if (unregister_chrdev(LVM_CHAR_MAJOR, lvm_name) < 0)
+                       printk(KERN_ERR "%s -- unregister_chrdev failed\n", lvm_name);
+               return -EIO;
+       }
+
+       lvm_devfs_handle = devfs_register(
+               0 , "lvm", 0, 0, LVM_CHAR_MAJOR,
+               S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP,
+               &lvm_chr_fops, NULL);
+
+#if defined CONFIG_LVM_PROC_FS && defined CONFIG_PROC_FS
+       create_proc_info_entry(LVM_NAME, S_IFREG | S_IRUGO,
+                              &proc_root, lvm_proc_get_info_ptr);
+#endif
+
+       lvm_init_vars();
+       lvm_geninit(&lvm_gendisk);
+
+       /* insert our gendisk at the corresponding major */
+       if (gendisk_head != NULL) {
+               gendisk_ptr = gendisk_head;
+               while (gendisk_ptr->next != NULL &&
+                      gendisk_ptr->major > lvm_gendisk.major) {
+                       gendisk_ptr = gendisk_ptr->next;
+               }
+               lvm_gendisk.next = gendisk_ptr->next;
+               gendisk_ptr->next = &lvm_gendisk;
+       } else {
+               gendisk_head = &lvm_gendisk;
+               lvm_gendisk.next = NULL;
+       }
+
+#ifdef LVM_HD_NAME
+       /* reference from drivers/block/genhd.c */
+       lvm_hd_name_ptr = lvm_hd_name;
+#endif
+
+       blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST);
+       blk_queue_make_request(BLK_DEFAULT_QUEUE(MAJOR_NR), lvm_make_request_fn);
+       blk_queue_pluggable(BLK_DEFAULT_QUEUE(MAJOR_NR), lvm_plug_device_noop);
+       /* optional read root VGDA */
+/*
+   if ( *rootvg != 0) vg_read_with_pv_and_lv ( rootvg, &vg);
+*/
+
+       printk(KERN_INFO
+              "%s%s -- "
+#ifdef MODULE
+              "Module"
+#else
+              "Driver"
+#endif
+              " successfully initialized\n",
+              lvm_version, lvm_name);
+
+       return 0;
+} /* init_module() / lvm_init() */
+
+
+#ifdef MODULE
+/*
+ * Module cleanup...
+ */
+void cleanup_module(void)
+{
+       struct gendisk *gendisk_ptr = NULL, *gendisk_ptr_prev = NULL;
+
+       devfs_unregister (lvm_devfs_handle);
+
+       if (unregister_chrdev(LVM_CHAR_MAJOR, lvm_name) < 0) {
+               printk(KERN_ERR "%s -- unregister_chrdev failed\n", lvm_name);
+       }
+       if (unregister_blkdev(MAJOR_NR, lvm_name) < 0) {
+               printk(KERN_ERR "%s -- unregister_blkdev failed\n", lvm_name);
+       }
+       blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
+
+       gendisk_ptr = gendisk_ptr_prev = gendisk_head;
+       while (gendisk_ptr != NULL) {
+               if (gendisk_ptr == &lvm_gendisk)
+                       break;
+               gendisk_ptr_prev = gendisk_ptr;
+               gendisk_ptr = gendisk_ptr->next;
+       }
+       /* delete our gendisk from chain */
+       if (gendisk_ptr == &lvm_gendisk)
+               gendisk_ptr_prev->next = gendisk_ptr->next;
+
+       blk_size[MAJOR_NR] = NULL;
+       blksize_size[MAJOR_NR] = NULL;
+
+#if defined CONFIG_LVM_PROC_FS && defined CONFIG_PROC_FS
+       remove_proc_entry(LVM_NAME, &proc_root);
+#endif
+
+#ifdef LVM_HD_NAME
+       /* reference from linux/drivers/block/genhd.c */
+       lvm_hd_name_ptr = NULL;
+#endif
+
+       printk(KERN_INFO "%s -- Module successfully deactivated\n", lvm_name);
+
+       return;
+}      /* void cleanup_module() */
+#endif /* #ifdef MODULE */
+
+
+/*
+ * support function to initialize lvm variables
+ */
+#ifdef __initfunc
+__initfunc(void lvm_init_vars(void))
+#else
+void __init lvm_init_vars(void)
+#endif
+{
+       int v;
+
+       loadtime = CURRENT_TIME;
+
+       pe_lock_req.lock = UNLOCK_PE;
+       pe_lock_req.data.lv_dev = pe_lock_req.data.pv_dev = 0;
+       pe_lock_req.data.pv_offset = 0;
+
+       /* Initialize VG pointers */
+       for (v = 0; v < ABS_MAX_VG; v++) vg[v] = NULL;
+
+       /* Initialize LV -> VG association */
+       for (v = 0; v < ABS_MAX_LV; v++) {
+               /* index ABS_MAX_VG never used for real VG */
+               vg_lv_map[v].vg_number = ABS_MAX_VG;
+               vg_lv_map[v].lv_number = -1;
+       }
+
+       return;
+} /* lvm_init_vars() */
+
+
+/********************************************************************
+ *
+ * Character device functions
+ *
+ ********************************************************************/
+
+/*
+ * character device open routine
+ */
+static int lvm_chr_open(struct inode *inode,
+                       struct file *file)
+{
+       int minor = MINOR(inode->i_rdev);
+
+#ifdef DEBUG
+       printk(KERN_DEBUG
+        "%s -- lvm_chr_open MINOR: %d  VG#: %d  mode: 0x%X  lock: %d\n",
+              lvm_name, minor, VG_CHR(minor), file->f_mode, lock);
+#endif
+
+       /* super user validation */
+       if (!capable(CAP_SYS_ADMIN)) return -EACCES;
+
+       /* Group special file open */
+       if (VG_CHR(minor) > MAX_VG) return -ENXIO;
+
+       lvm_chr_open_count++;
+       return 0;
+} /* lvm_chr_open() */
+
+
+/*
+ * character device i/o-control routine
+ *
+ * Only one changing process can do changing ioctl at one time,
+ * others will block.
+ *
+ */
+static int lvm_chr_ioctl(struct inode *inode, struct file *file,
+                        uint command, ulong a)
+{
+       int minor = MINOR(inode->i_rdev);
+       uint extendable, l, v;
+       void *arg = (void *) a;
+       lv_t lv;
+       vg_t* vg_ptr = vg[VG_CHR(minor)];
+
+       /* otherwise cc will complain about unused variables */
+       (void) lvm_lock;
+
+
+#ifdef DEBUG_IOCTL
+       printk(KERN_DEBUG
+              "%s -- lvm_chr_ioctl: command: 0x%X  MINOR: %d  "
+              "VG#: %d  mode: 0x%X\n",
+              lvm_name, command, minor, VG_CHR(minor), file->f_mode);
+#endif
+
+#ifdef LVM_TOTAL_RESET
+       if (lvm_reset_spindown > 0) return -EACCES;
+#endif
+
+       /* Main command switch */
+       switch (command) {
+       case LVM_LOCK_LVM:
+               /* lock the LVM */
+               return lvm_do_lock_lvm();
+
+       case LVM_GET_IOP_VERSION:
+               /* check lvm version to ensure driver/tools+lib
+                  interoperability */
+               if (copy_to_user(arg, &lvm_iop_version, sizeof(ushort)) != 0)
+                       return -EFAULT;
+               return 0;
+
+#ifdef LVM_TOTAL_RESET
+       case LVM_RESET:
+               /* lock reset function */
+               lvm_reset_spindown = 1;
+               for (v = 0; v < ABS_MAX_VG; v++) {
+                       if (vg[v] != NULL) lvm_do_vg_remove(v);
+               }
+
+#ifdef MODULE
+               while (GET_USE_COUNT(&__this_module) < 1)
+                       MOD_INC_USE_COUNT;
+               while (GET_USE_COUNT(&__this_module) > 1)
+                       MOD_DEC_USE_COUNT;
+#endif                         /* MODULE */
+               lock = 0;       /* release lock */
+               wake_up_interruptible(&lvm_wait);
+               return 0;
+#endif /* LVM_TOTAL_RESET */
+
+
+       case LE_REMAP:
+               /* remap a logical extent (after moving the physical extent) */
+               return lvm_do_le_remap(vg_ptr,arg);
+
+       case PE_LOCK_UNLOCK:
+               /* lock/unlock i/o to a physical extent to move it to another
+                  physical volume (move's done in user space's pvmove) */
+               return lvm_do_pe_lock_unlock(vg_ptr,arg);
+
+       case VG_CREATE:
+               /* create a VGDA */
+               return lvm_do_vg_create(minor, arg);
+
+       case VG_REMOVE:
+               /* remove an inactive VGDA */
+               return lvm_do_vg_remove(minor);
+
+       case VG_EXTEND:
+               /* extend a volume group */
+               return lvm_do_vg_extend(vg_ptr,arg);
+
+       case VG_REDUCE:
+               /* reduce a volume group */
+               return lvm_do_vg_reduce(vg_ptr,arg);
+
+
+       case VG_SET_EXTENDABLE:
+               /* set/clear extendability flag of volume group */
+               if (vg_ptr == NULL) return -ENXIO;
+               if (copy_from_user(&extendable, arg, sizeof(extendable)) != 0)
+                       return -EFAULT;
+
+               if (extendable == VG_EXTENDABLE ||
+                   extendable == ~VG_EXTENDABLE) {
+                       if (extendable == VG_EXTENDABLE)
+                               vg_ptr->vg_status |= VG_EXTENDABLE;
+                       else
+                               vg_ptr->vg_status &= ~VG_EXTENDABLE;
+               } else return -EINVAL;
+               return 0;
+
+
+       case VG_STATUS:
+               /* get volume group data (only the vg_t struct) */
+               if (vg_ptr == NULL) return -ENXIO;
+               if (copy_to_user(arg, vg_ptr, sizeof(vg_t)) != 0)
+                       return -EFAULT;
+               return 0;
+
+
+       case VG_STATUS_GET_COUNT:
+               /* get volume group count */
+               if (copy_to_user(arg, &vg_count, sizeof(vg_count)) != 0)
+                       return -EFAULT;
+               return 0;
+
+
+       case VG_STATUS_GET_NAMELIST:
+               /* get volume group count */
+               for (l = v = 0; v < ABS_MAX_VG; v++) {
+                       if (vg[v] != NULL) {
+                               if (copy_to_user(arg + l++ * NAME_LEN,
+                                                vg[v]->vg_name,
+                                                NAME_LEN) != 0)
+                                       return -EFAULT;
+                       }
+               }
+               return 0;
+
+
+       case LV_CREATE:
+       case LV_REMOVE:
+       case LV_EXTEND:
+       case LV_REDUCE:
+               /* create, remove, extend or reduce a logical volume */
+               if (vg_ptr == NULL) return -ENXIO;
+               if (copy_from_user(&lv_req, arg, sizeof(lv_req)) != 0)
+                       return -EFAULT;
+
+               if (command != LV_REMOVE) {
+                       if (copy_from_user(&lv, lv_req.lv, sizeof(lv_t)) != 0)
+                               return -EFAULT;
+               }
+               switch (command) {
+               case LV_CREATE:
+                       return lvm_do_lv_create(minor, lv_req.lv_name, &lv);
+
+               case LV_REMOVE:
+                       return lvm_do_lv_remove(minor, lv_req.lv_name, -1);
+
+               case LV_EXTEND:
+               case LV_REDUCE:
+                       return lvm_do_lv_extend_reduce(minor, lv_req.lv_name, &lv);
+               }
+
+
+       case LV_STATUS_BYNAME:
+               /* get status of a logical volume by name */
+               return lvm_do_lv_status_byname(vg_ptr,arg);
+
+       case LV_STATUS_BYINDEX:
+               /* get status of a logical volume by index */
+               return lvm_do_lv_status_byindex(vg_ptr,arg);
+
+       case PV_CHANGE:
+               /* change a physical volume */
+               return lvm_do_pv_change(vg_ptr,arg);
+
+       case PV_STATUS:
+               /* get physical volume data (pv_t structure only) */
+               return lvm_do_pv_status(vg_ptr,arg);
+
+       case PV_FLUSH:
+               /* physical volume buffer flush/invalidate */
+               if (copy_from_user(&pv_flush_req, arg,
+                                  sizeof(pv_flush_req)) != 0)
+                       return -EFAULT;
+
+               for ( v = 0; v < ABS_MAX_VG; v++) {
+                       unsigned int p;
+                       if ( vg[v] == NULL) continue;
+                       for ( p = 0; p < vg[v]->pv_max; p++) {
+                               if ( vg[v]->pv[p] != NULL &&
+                                    strcmp ( vg[v]->pv[p]->pv_name,
+                                             pv_flush_req.pv_name) == 0) {
+                                       fsync_dev ( vg[v]->pv[p]->pv_dev);
+                                       invalidate_buffers ( vg[v]->pv[p]->pv_dev);
+                                       return 0;
+                               }
+                       }
+               }
+               return 0;
+
+       default:
+               printk(KERN_WARNING
+                      "%s -- lvm_chr_ioctl: unknown command %x\n",
+                      lvm_name, command);
+               return -EINVAL;
+       }
+
+       return 0;
+} /* lvm_chr_ioctl */
+
+
+/*
+ * character device close routine
+ */
+static int lvm_chr_close(struct inode *inode, struct file *file)
+{
+#ifdef DEBUG
+       int minor = MINOR(inode->i_rdev);
+       printk(KERN_DEBUG
+            "%s -- lvm_chr_close   VG#: %d\n", lvm_name, VG_CHR(minor));
+#endif
+
+       lock_kernel();
+#ifdef LVM_TOTAL_RESET
+       if (lvm_reset_spindown > 0) {
+               lvm_reset_spindown = 0;
+               lvm_chr_open_count = 1;
+       }
+#endif
+
+       if (lvm_chr_open_count > 0) lvm_chr_open_count--;
+       if (lock == current->pid) {
+               lock = 0;       /* release lock */
+               wake_up_interruptible(&lvm_wait);
+       }
+       unlock_kernel();
+
+       return 0;
+} /* lvm_chr_close() */
+
+
+
+/********************************************************************
+ *
+ * Block device functions
+ *
+ ********************************************************************/
+
+/*
+ * block device open routine
+ */
+static int lvm_blk_open(struct inode *inode, struct file *file)
+{
+       int minor = MINOR(inode->i_rdev);
+       lv_t *lv_ptr;
+       vg_t *vg_ptr = vg[VG_BLK(minor)];
+
+#ifdef DEBUG_LVM_BLK_OPEN
+       printk(KERN_DEBUG
+         "%s -- lvm_blk_open MINOR: %d  VG#: %d  LV#: %d  mode: 0x%X\n",
+           lvm_name, minor, VG_BLK(minor), LV_BLK(minor), file->f_mode);
+#endif
+
+#ifdef LVM_TOTAL_RESET
+       if (lvm_reset_spindown > 0)
+               return -EPERM;
+#endif
+
+       if (vg_ptr != NULL &&
+           (vg_ptr->vg_status & VG_ACTIVE) &&
+           (lv_ptr = vg_ptr->lv[LV_BLK(minor)]) != NULL &&
+           LV_BLK(minor) >= 0 &&
+           LV_BLK(minor) < vg_ptr->lv_max) {
+
+               /* Check parallel LV spindown (LV remove) */
+               if (lv_ptr->lv_status & LV_SPINDOWN) return -EPERM;
+
+               /* Check inactive LV and open for read/write */
+               if (file->f_mode & O_RDWR) {
+                       if (!(lv_ptr->lv_status & LV_ACTIVE)) return -EPERM;
+                       if (!(lv_ptr->lv_access & LV_WRITE))  return -EACCES;
+               }
+
+                /* be sure to increment VG counter */
+               if (lv_ptr->lv_open == 0) vg_ptr->lv_open++;
+               lv_ptr->lv_open++;
+
+               MOD_INC_USE_COUNT;
+
+#ifdef DEBUG_LVM_BLK_OPEN
+               printk(KERN_DEBUG
+                      "%s -- lvm_blk_open MINOR: %d  VG#: %d  LV#: %d  size: %d\n",
+                      lvm_name, minor, VG_BLK(minor), LV_BLK(minor),
+                      lv_ptr->lv_size);
+#endif
+
+               return 0;
+       }
+       return -ENXIO;
+} /* lvm_blk_open() */
+
+
+/*
+ * block device i/o-control routine
+ */
+static int lvm_blk_ioctl(struct inode *inode, struct file *file,
+                        uint command, ulong a)
+{
+       int minor = MINOR(inode->i_rdev);
+       vg_t *vg_ptr = vg[VG_BLK(minor)];
+       lv_t *lv_ptr = vg_ptr->lv[LV_BLK(minor)];
+       void *arg = (void *) a;
+       struct hd_geometry *hd = (struct hd_geometry *) a;
+
+#ifdef DEBUG_IOCTL
+       printk(KERN_DEBUG
+              "%s -- lvm_blk_ioctl MINOR: %d  command: 0x%X  arg: %X  "
+              "VG#: %dl  LV#: %d\n",
+              lvm_name, minor, command, (ulong) arg,
+              VG_BLK(minor), LV_BLK(minor));
+#endif
+
+       switch (command) {
+       case BLKGETSIZE:
+               /* return device size */
+#ifdef DEBUG_IOCTL
+               printk(KERN_DEBUG
+                      "%s -- lvm_blk_ioctl -- BLKGETSIZE: %u\n",
+                      lvm_name, lv_ptr->lv_size);
+#endif
+               if (put_user(lv_ptr->lv_size, (long *)arg))
+                       return -EFAULT;
+               break;
+
+
+       case BLKFLSBUF:
+               /* flush buffer cache */
+               if (!capable(CAP_SYS_ADMIN)) return -EACCES;
+
+#ifdef DEBUG_IOCTL
+               printk(KERN_DEBUG
+                      "%s -- lvm_blk_ioctl -- BLKFLSBUF\n", lvm_name);
+#endif
+               fsync_dev(inode->i_rdev);
+               invalidate_buffers(inode->i_rdev);
+               break;
+
+
+       case BLKRASET:
+               /* set read ahead for block device */
+               if (!capable(CAP_SYS_ADMIN)) return -EACCES;
+
+#ifdef DEBUG_IOCTL
+               printk(KERN_DEBUG
+                      "%s -- lvm_blk_ioctl -- BLKRASET: %d sectors for %02X:%02X\n",
+                      lvm_name, (long) arg, MAJOR(inode->i_rdev), minor);
+#endif
+               if ((long) arg < LVM_MIN_READ_AHEAD ||
+                   (long) arg > LVM_MAX_READ_AHEAD)
+                       return -EINVAL;
+               read_ahead[MAJOR_NR] = lv_ptr->lv_read_ahead = (long) arg;
+               break;
+
+
+       case BLKRAGET:
+               /* get current read ahead setting */
+#ifdef DEBUG_IOCTL
+               printk(KERN_DEBUG
+                      "%s -- lvm_blk_ioctl -- BLKRAGET\n", lvm_name);
+#endif
+               if (put_user(lv_ptr->lv_read_ahead, (long *)arg))
+                       return -EFAULT;
+               break;
+
+
+       case HDIO_GETGEO:
+               /* get disk geometry */
+#ifdef DEBUG_IOCTL
+               printk(KERN_DEBUG
+                      "%s -- lvm_blk_ioctl -- HDIO_GETGEO\n", lvm_name);
+#endif
+               if (hd == NULL)
+                       return -EINVAL;
+               {
+                       unsigned char heads = 64;
+                       unsigned char sectors = 32;
+                       long start = 0;
+                       short cylinders = lv_ptr->lv_size / heads / sectors;
+
+                       if (copy_to_user((char *) &hd->heads, &heads,
+                                        sizeof(heads)) != 0 ||
+                           copy_to_user((char *) &hd->sectors, &sectors,
+                                        sizeof(sectors)) != 0 ||
+                           copy_to_user((short *) &hd->cylinders,
+                                  &cylinders, sizeof(cylinders)) != 0 ||
+                           copy_to_user((long *) &hd->start, &start,
+                                        sizeof(start)) != 0)
+                               return -EFAULT;
+               }
+
+#ifdef DEBUG_IOCTL
+               printk(KERN_DEBUG
+                      "%s -- lvm_blk_ioctl -- cylinders: %d\n",
+                      lvm_name, lv_ptr->lv_size / heads / sectors);
+#endif
+               break;
+
+
+       case LV_SET_ACCESS:
+               /* set access flags of a logical volume */
+               if (!capable(CAP_SYS_ADMIN)) return -EACCES;
+               lv_ptr->lv_access = (ulong) arg;
+               break;
+
+
+       case LV_SET_STATUS:
+               /* set status flags of a logical volume */
+               if (!capable(CAP_SYS_ADMIN)) return -EACCES;
+               if (!((ulong) arg & LV_ACTIVE) && lv_ptr->lv_open > 1)
+                       return -EPERM;
+               lv_ptr->lv_status = (ulong) arg;
+               break;
+
+
+       case LV_SET_ALLOCATION:
+               /* set allocation flags of a logical volume */
+               if (!capable(CAP_SYS_ADMIN)) return -EACCES;
+               lv_ptr->lv_allocation = (ulong) arg;
+               break;
+
+
+       default:
+               printk(KERN_WARNING
+                      "%s -- lvm_blk_ioctl: unknown command %d\n",
+                      lvm_name, command);
+               return -EINVAL;
+       }
+
+       return 0;
+} /* lvm_blk_ioctl() */
+
+
+/*
+ * block device close routine
+ */
+static int lvm_blk_close(struct inode *inode, struct file *file)
+{
+       int minor = MINOR(inode->i_rdev);
+       vg_t *vg_ptr = vg[VG_BLK(minor)];
+       lv_t *lv_ptr = vg_ptr->lv[LV_BLK(minor)];
+
+#ifdef DEBUG
+       printk(KERN_DEBUG
+              "%s -- lvm_blk_close MINOR: %d  VG#: %d  LV#: %d\n",
+              lvm_name, minor, VG_BLK(minor), LV_BLK(minor));
+#endif
+
+       sync_dev(inode->i_rdev);
+       if (lv_ptr->lv_open == 1) vg_ptr->lv_open--;
+       lv_ptr->lv_open--;
+
+       MOD_DEC_USE_COUNT;
+
+       return 0;
+} /* lvm_blk_close() */
+
+
+#if defined CONFIG_LVM_PROC_FS && defined CONFIG_PROC_FS
+/*
+ * Support function /proc-Filesystem
+ */
+#define  LVM_PROC_BUF   ( i == 0 ? dummy_buf : &buf[sz])
+
+static int lvm_proc_get_info(char *page, char **start, off_t pos, int count)
+{
+       int c, i, l, p, v, vg_counter, pv_counter, lv_counter, lv_open_counter,
+        lv_open_total, pe_t_bytes, lv_block_exception_t_bytes, seconds;
+       static off_t sz;
+       off_t sz_last;
+       char allocation_flag, inactive_flag, rw_flag, stripes_flag;
+       char *lv_name, *pv_name;
+       static char *buf = NULL;
+       static char dummy_buf[160];     /* sized for 2 lines */
+       vg_t *vg_ptr;
+       lv_t *lv_ptr;
+       pv_t *pv_ptr;
+
+
+#ifdef DEBUG_LVM_PROC_GET_INFO
+       printk(KERN_DEBUG
+              "%s - lvm_proc_get_info CALLED  pos: %lu  count: %d  whence: %d\n",
+              lvm_name, pos, count, whence);
+#endif
+
+       if (pos == 0 || buf == NULL) {
+               sz_last = vg_counter = pv_counter = lv_counter = lv_open_counter = \
+                   lv_open_total = pe_t_bytes = lv_block_exception_t_bytes = 0;
+
+               /* search for activity */
+               for (v = 0; v < ABS_MAX_VG; v++) {
+                       if ((vg_ptr = vg[v]) != NULL) {
+                               vg_counter++;
+                               pv_counter += vg_ptr->pv_cur;
+                               lv_counter += vg_ptr->lv_cur;
+                               if (vg_ptr->lv_cur > 0) {
+                                       for (l = 0; l < vg[v]->lv_max; l++) {
+                                               if ((lv_ptr = vg_ptr->lv[l]) != NULL) {
+                                                       pe_t_bytes += lv_ptr->lv_allocated_le;
+                                                       if (lv_ptr->lv_block_exception != NULL)
+                                                               lv_block_exception_t_bytes += lv_ptr->lv_remap_end;
+                                                       if (lv_ptr->lv_open > 0) {
+                                                               lv_open_counter++;
+                                                               lv_open_total += lv_ptr->lv_open;
+                                                       }
+                                               }
+                                       }
+                               }
+                       }
+               }
+               pe_t_bytes *= sizeof(pe_t);
+               lv_block_exception_t_bytes *= sizeof(lv_block_exception_t);
+
+               if (buf != NULL) {
+#ifdef DEBUG_KFREE
+                       printk(KERN_DEBUG
+                              "%s -- kfree %d\n", lvm_name, __LINE__);
+#endif
+                       kfree(buf);
+                       buf = NULL;
+               }
+               /* 2 times: first to get size to allocate buffer,
+                  2nd to fill the malloced buffer */
+               for (i = 0; i < 2; i++) {
+                       sz = 0;
+                       sz += sprintf(LVM_PROC_BUF,
+                                     "LVM "
+#ifdef MODULE
+                                     "module"
+#else
+                                     "driver"
+#endif
+                                     " %s\n\n"
+                                   "Total:  %d VG%s  %d PV%s  %d LV%s ",
+                                     lvm_short_version,
+                                 vg_counter, vg_counter == 1 ? "" : "s",
+                                 pv_counter, pv_counter == 1 ? "" : "s",
+                                lv_counter, lv_counter == 1 ? "" : "s");
+                       sz += sprintf(LVM_PROC_BUF,
+                                     "(%d LV%s open",
+                                     lv_open_counter,
+                                     lv_open_counter == 1 ? "" : "s");
+                       if (lv_open_total > 0)
+                               sz += sprintf(LVM_PROC_BUF,
+                                             " %d times)\n",
+                                             lv_open_total);
+                       else
+                               sz += sprintf(LVM_PROC_BUF, ")");
+                       sz += sprintf(LVM_PROC_BUF,
+                                     "\nGlobal: %lu bytes malloced   IOP version: %d   ",
+                                     vg_counter * sizeof(vg_t) +
+                                     pv_counter * sizeof(pv_t) +
+                                     lv_counter * sizeof(lv_t) +
+                       pe_t_bytes + lv_block_exception_t_bytes + sz_last,
+                                     lvm_iop_version);
+
+                       seconds = CURRENT_TIME - loadtime;
+                       if (seconds < 0)
+                               loadtime = CURRENT_TIME + seconds;
+                       if (seconds / 86400 > 0) {
+                               sz += sprintf(LVM_PROC_BUF, "%d day%s ",
+                                             seconds / 86400,
+                                             seconds / 86400 == 0 ||
+                                        seconds / 86400 > 1 ? "s" : "");
+                       }
+                       sz += sprintf(LVM_PROC_BUF, "%d:%02d:%02d active\n",
+                                     (seconds % 86400) / 3600,
+                                     (seconds % 3600) / 60,
+                                     seconds % 60);
+
+                       if (vg_counter > 0) {
+                               for (v = 0; v < ABS_MAX_VG; v++) {
+                                       /* volume group */
+                                       if ((vg_ptr = vg[v]) != NULL) {
+                                               inactive_flag = ' ';
+                                               if (!(vg_ptr->vg_status & VG_ACTIVE)) inactive_flag = 'I';
+                                               sz += sprintf(LVM_PROC_BUF,
+                                                             "\nVG: %c%s  [%d PV, %d LV/%d open] "
+                                                             " PE Size: %d KB\n"
+                                                             "  Usage [KB/PE]: %d /%d total  "
+                                                             "%d /%d used  %d /%d free",
+                                                             inactive_flag,
+                                                             vg_ptr->vg_name,
+                                                             vg_ptr->pv_cur,
+                                                             vg_ptr->lv_cur,
+                                                             vg_ptr->lv_open,
+                                                             vg_ptr->pe_size >> 1,
+                                                             vg_ptr->pe_size * vg_ptr->pe_total >> 1,
+                                                             vg_ptr->pe_total,
+                                                             vg_ptr->pe_allocated * vg_ptr->pe_size >> 1,
+                                                             vg_ptr->pe_allocated,
+                                                             (vg_ptr->pe_total - vg_ptr->pe_allocated) *
+                                                             vg_ptr->pe_size >> 1,
+                                                             vg_ptr->pe_total - vg_ptr->pe_allocated);
+
+                                               /* physical volumes */
+                                               sz += sprintf(LVM_PROC_BUF,
+                                                             "\n  PV%s ",
+                                                             vg_ptr->pv_cur == 1 ? ": " : "s:");
+                                               c = 0;
+                                               for (p = 0; p < vg_ptr->pv_max; p++) {
+                                                       if ((pv_ptr = vg_ptr->pv[p]) != NULL) {
+                                                               inactive_flag = 'A';
+                                                               if (!(pv_ptr->pv_status & PV_ACTIVE))
+                                                                       inactive_flag = 'I';
+                                                               allocation_flag = 'A';
+                                                               if (!(pv_ptr->pv_allocatable & PV_ALLOCATABLE))
+                                                                       allocation_flag = 'N';
+                                                               pv_name = strchr(pv_ptr->pv_name+1,'/');
+                                                               if ( pv_name == 0) pv_name = pv_ptr->pv_name;
+                                                               else               pv_name++;
+                                                               sz += sprintf(LVM_PROC_BUF,
+                                                                             "[%c%c] %-21s %8d /%-6d  "
+                                                                             "%8d /%-6d  %8d /%-6d",
+                                                                             inactive_flag,
+                                                                             allocation_flag,
+                                                                             pv_name,
+                                                                             pv_ptr->pe_total *
+                                                                             pv_ptr->pe_size >> 1,
+                                                                             pv_ptr->pe_total,
+                                                                             pv_ptr->pe_allocated *
+                                                                             pv_ptr->pe_size >> 1,
+                                                                             pv_ptr->pe_allocated,
+                                                                             (pv_ptr->pe_total -
+                                                                              pv_ptr->pe_allocated) *
+                                                                             pv_ptr->pe_size >> 1,
+                                                                             pv_ptr->pe_total -
+                                                                             pv_ptr->pe_allocated);
+                                                               c++;
+                                                               if (c < vg_ptr->pv_cur)
+                                                                       sz += sprintf(LVM_PROC_BUF,
+                                                                                     "\n       ");
+                                                       }
+                                               }
+
+                                               /* logical volumes */
+                                               sz += sprintf(LVM_PROC_BUF,
+                                                          "\n    LV%s ",
+                                                             vg_ptr->lv_cur == 1 ? ": " : "s:");
+                                               c = 0;
+                                               for (l = 0; l < vg[v]->lv_max; l++) {
+                                                       if ((lv_ptr = vg_ptr->lv[l]) != NULL) {
+                                                               inactive_flag = 'A';
+                                                               if (!(lv_ptr->lv_status & LV_ACTIVE))
+                                                                       inactive_flag = 'I';
+                                                               rw_flag = 'R';
+                                                               if (lv_ptr->lv_access & LV_WRITE)
+                                                                       rw_flag = 'W';
+                                                               allocation_flag = 'D';
+                                                               if (lv_ptr->lv_allocation & LV_CONTIGUOUS)
+                                                                       allocation_flag = 'C';
+                                                               stripes_flag = 'L';
+                                                               if (lv_ptr->lv_stripes > 1)
+                                                                       stripes_flag = 'S';
+                                                               sz += sprintf(LVM_PROC_BUF,
+                                                                             "[%c%c%c%c",
+                                                                             inactive_flag,
+                                                                rw_flag,
+                                                                             allocation_flag,
+                                                                             stripes_flag);
+                                                               if (lv_ptr->lv_stripes > 1)
+                                                                       sz += sprintf(LVM_PROC_BUF, "%-2d",
+                                                                                     lv_ptr->lv_stripes);
+                                                               else
+                                                                       sz += sprintf(LVM_PROC_BUF, "  ");
+                                                               lv_name = strrchr(lv_ptr->lv_name, '/');
+                                                               if ( lv_name == 0) lv_name = lv_ptr->lv_name;
+                                                               else               lv_name++;
+                                                               sz += sprintf(LVM_PROC_BUF, "] %-25s", lv_name);
+                                                               if (strlen(lv_name) > 25)
+                                                                       sz += sprintf(LVM_PROC_BUF,
+                                                                                     "\n                              ");
+                                                               sz += sprintf(LVM_PROC_BUF, "%9d /%-6d   ",
+                                                                             lv_ptr->lv_size >> 1,
+                                                                             lv_ptr->lv_size / vg[v]->pe_size);
+
+                                                               if (lv_ptr->lv_open == 0)
+                                                                       sz += sprintf(LVM_PROC_BUF, "close");
+                                                               else
+                                                                       sz += sprintf(LVM_PROC_BUF, "%dx open",
+                                                                                     lv_ptr->lv_open);
+                                                               c++;
+                                                               if (c < vg_ptr->lv_cur)
+                                                                       sz += sprintf(LVM_PROC_BUF,
+                                                                                     "\n         ");
+                                                       }
+                                               }
+                                               if (vg_ptr->lv_cur == 0) sz += sprintf(LVM_PROC_BUF, "none");
+                                               sz += sprintf(LVM_PROC_BUF, "\n");
+                                       }
+                               }
+                       }
+                       if (buf == NULL) {
+                               if ((buf = vmalloc(sz)) == NULL) {
+                                       sz = 0;
+                                       return sprintf(page, "%s - vmalloc error at line %d\n",
+                                                    lvm_name, __LINE__);
+                               }
+                       }
+                       sz_last = sz;
+               }
+       }
+       if (pos > sz - 1) {
+               vfree(buf);
+               buf = NULL;
+               return 0;
+       }
+       *start = &buf[pos];
+       if (sz - pos < count)
+               return sz - pos;
+       else
+               return count;
+} /* lvm_proc_get_info() */
+#endif /* #if defined CONFIG_LVM_PROC_FS && defined CONFIG_PROC_FS */
+
+
+/*
+ * block device support function for /usr/src/linux/drivers/block/ll_rw_blk.c
+ * (see init_module/lvm_init)
+ */
+static int lvm_map(struct buffer_head *bh, int rw)
+{
+       int minor = MINOR(bh->b_rdev);
+       ulong index;
+       ulong pe_start;
+       ulong size = bh->b_size >> 9;
+       ulong rsector_tmp = bh->b_rsector;
+       ulong rsector_sav;
+       kdev_t rdev_tmp = bh->b_rdev;
+       kdev_t rdev_sav;
+       lv_t *lv = vg[VG_BLK(minor)]->lv[LV_BLK(minor)];
+
+
+       if (!(lv->lv_status & LV_ACTIVE)) {
+               printk(KERN_ALERT
+                      "%s - lvm_map: ll_rw_blk for inactive LV %s\n",
+                      lvm_name, lv->lv_name);
+               goto error;
+       }
+/*
+   if ( lv->lv_access & LV_SNAPSHOT)
+   printk ( "%s -- %02d:%02d  block: %lu  rw: %d\n", lvm_name, MAJOR ( bh->b_dev), MINOR ( bh->b_dev), bh->b_blocknr, rw);
+ */
+
+       /* take care of snapshot chunk writes before
+          check for writable logical volume */
+       if ((lv->lv_access & LV_SNAPSHOT) &&
+           MAJOR(bh->b_rdev) != 0 &&
+           MAJOR(bh->b_rdev) != MAJOR_NR &&
+           (rw == WRITEA || rw == WRITE))
+       {
+   printk ( "%s -- doing snapshot write for %02d:%02d[%02d:%02d]  b_blocknr: %lu  b_rsector: %lu\n", lvm_name, MAJOR ( bh->b_dev), MINOR ( bh->b_dev), MAJOR ( bh->b_rdev), MINOR ( bh->b_rdev), bh->b_blocknr, bh->b_rsector);
+               goto error;
+       }
+
+       if ((rw == WRITE || rw == WRITEA) &&
+           !(lv->lv_access & LV_WRITE)) {
+               printk(KERN_CRIT
+                   "%s - lvm_map: ll_rw_blk write for readonly LV %s\n",
+                      lvm_name, lv->lv_name);
+               goto error;
+       }
+#ifdef DEBUG_MAP
+       printk(KERN_DEBUG
+              "%s - lvm_map minor:%d  *rdev: %02d:%02d  *rsector: %lu  "
+              "size:%lu\n",
+              lvm_name, minor,
+              MAJOR(rdev_tmp),
+              MINOR(rdev_tmp),
+              rsector_tmp, size);
+#endif
+
+       if (rsector_tmp + size > lv->lv_size) {
+               printk(KERN_ALERT
+                      "%s - lvm_map *rsector: %lu or size: %lu wrong for"
+                   " minor: %2d\n", lvm_name, rsector_tmp, size, minor);
+               goto error;
+       }
+       rsector_sav = rsector_tmp;
+       rdev_sav = rdev_tmp;
+
+lvm_second_remap:
+       /* linear mapping */
+       if (lv->lv_stripes < 2) {
+               /* get the index */
+               index = rsector_tmp / vg[VG_BLK(minor)]->pe_size;
+               pe_start = lv->lv_current_pe[index].pe;
+               rsector_tmp = lv->lv_current_pe[index].pe +
+                   (rsector_tmp % vg[VG_BLK(minor)]->pe_size);
+               rdev_tmp = lv->lv_current_pe[index].dev;
+
+#ifdef DEBUG_MAP
+               printk(KERN_DEBUG
+                      "lv_current_pe[%ld].pe: %ld  rdev: %02d:%02d  rsector:%ld\n",
+                      index,
+                      lv->lv_current_pe[index].pe,
+                      MAJOR(rdev_tmp),
+                      MINOR(rdev_tmp),
+                      rsector_tmp);
+#endif
+
+               /* striped mapping */
+       } else {
+               ulong stripe_index;
+               ulong stripe_length;
+
+               stripe_length = vg[VG_BLK(minor)]->pe_size * lv->lv_stripes;
+               stripe_index = (rsector_tmp % stripe_length) / lv->lv_stripesize;
+               index = rsector_tmp / stripe_length +
+                   (stripe_index % lv->lv_stripes) *
+                   (lv->lv_allocated_le / lv->lv_stripes);
+               pe_start = lv->lv_current_pe[index].pe;
+               rsector_tmp = lv->lv_current_pe[index].pe +
+                   (rsector_tmp % stripe_length) -
+                   (stripe_index % lv->lv_stripes) * lv->lv_stripesize -
+                   stripe_index / lv->lv_stripes *
+                   (lv->lv_stripes - 1) * lv->lv_stripesize;
+               rdev_tmp = lv->lv_current_pe[index].dev;
+       }
+
+#ifdef DEBUG_MAP
+       printk(KERN_DEBUG
+            "lv_current_pe[%ld].pe: %ld  rdev: %02d:%02d  rsector:%ld\n"
+              "stripe_length: %ld  stripe_index: %ld\n",
+              index,
+              lv->lv_current_pe[index].pe,
+              MAJOR(rdev_tmp),
+              MINOR(rdev_tmp),
+              rsector_tmp,
+              stripe_length,
+              stripe_index);
+#endif
+
+       /* handle physical extents on the move */
+       if (pe_lock_req.lock == LOCK_PE) {
+               if (rdev_tmp == pe_lock_req.data.pv_dev &&
+                   rsector_tmp >= pe_lock_req.data.pv_offset &&
+                   rsector_tmp < (pe_lock_req.data.pv_offset +
+                                  vg[VG_BLK(minor)]->pe_size)) {
+                       sleep_on(&lvm_map_wait);
+                       rsector_tmp = rsector_sav;
+                       rdev_tmp = rdev_sav;
+                       goto lvm_second_remap;
+               }
+       }
+       /* statistic */
+       if (rw == WRITE || rw == WRITEA)
+               lv->lv_current_pe[index].writes++;
+       else
+               lv->lv_current_pe[index].reads++;
+
+       /* snapshot volume exception handling on physical device address base */
+       if (lv->lv_access & (LV_SNAPSHOT | LV_SNAPSHOT_ORG)) {
+               /* original logical volume */
+               if (lv->lv_access & LV_SNAPSHOT_ORG) {
+                       if (rw == WRITE || rw == WRITEA)
+                       {
+                               lv_t *lv_ptr;
+
+                               /* start with first snapshot and loop thrugh all of them */
+                               for (lv_ptr = lv->lv_snapshot_next;
+                                    lv_ptr != NULL;
+                                    lv_ptr = lv_ptr->lv_snapshot_next) {
+                                       down(&lv->lv_snapshot_org->lv_snapshot_sem);
+                                       /* do we still have exception storage for this snapshot free? */
+                                       if (lv_ptr->lv_block_exception != NULL) {
+                                               rdev_sav = rdev_tmp;
+                                               rsector_sav = rsector_tmp;
+                                               if (!lvm_snapshot_remap_block(&rdev_tmp,
+                                                                             &rsector_tmp,
+                                                                             pe_start,
+                                                                             lv_ptr)) {
+                                                       /* create a new mapping */
+                                                       lvm_snapshot_COW(rdev_tmp,
+                                                                        rsector_tmp,
+                                                                        pe_start,
+                                                                        rsector_sav,
+                                                                        lv_ptr); 
+                                               }
+                                               rdev_tmp = rdev_sav;
+                                               rsector_tmp = rsector_sav;
+                                       }
+                                       up(&lv->lv_snapshot_org->lv_snapshot_sem);
+                               }
+                       }
+               } else {
+                       /* remap snapshot logical volume */
+                       down(&lv->lv_snapshot_sem);
+                       if (lv->lv_block_exception != NULL)
+                               lvm_snapshot_remap_block(&rdev_tmp, &rsector_tmp, pe_start, lv);
+                       up(&lv->lv_snapshot_sem);
+               }
+       }
+       bh->b_rdev = rdev_tmp;
+       bh->b_rsector = rsector_tmp;
+
+       return 1;
+
+ error:
+       buffer_IO_error(bh);
+       return -1;
+} /* lvm_map() */
+
+
+/*
+ * internal support functions
+ */
+
+#ifdef LVM_HD_NAME
+/*
+ * generate "hard disk" name
+ */
+void lvm_hd_name(char *buf, int minor)
+{
+       int len = 0;
+       lv_t *lv_ptr;
+
+       if (vg[VG_BLK(minor)] == NULL ||
+           (lv_ptr = vg[VG_BLK(minor)]->lv[LV_BLK(minor)]) == NULL)
+               return;
+       len = strlen(lv_ptr->lv_name) - 5;
+       memcpy(buf, &lv_ptr->lv_name[5], len);
+       buf[len] = 0;
+       return;
+}
+#endif
+
+
+/*
+ * this one never should be called...
+ */
+static void lvm_dummy_device_request(request_queue_t * t)
+{
+       printk(KERN_EMERG
+            "%s -- oops, got lvm request for %02d:%02d [sector: %lu]\n",
+              lvm_name,
+              MAJOR(CURRENT->rq_dev),
+              MINOR(CURRENT->rq_dev),
+              CURRENT->sector);
+       return;
+}
+
+
+/*
+ * make request function
+ */
+static int lvm_make_request_fn(request_queue_t *q, int rw, struct buffer_head *bh)
+{
+       lvm_map(bh, rw);
+       return 1;
+}
+
+/*
+ * plug device function is a noop because plugging has to happen
+ * in the queue of the physical blockdevice to allow the
+ * elevator to do a better job.
+ */
+static void lvm_plug_device_noop(request_queue_t *q, kdev_t dev) { }
+
+/********************************************************************
+ *
+ * Character device support functions
+ *
+ ********************************************************************/
+/*
+ * character device support function logical volume manager lock
+ */
+static int lvm_do_lock_lvm(void)
+{
+lock_try_again:
+       spin_lock(&lvm_lock);
+       if (lock != 0 && lock != current->pid) {
+#ifdef DEBUG_IOCTL
+               printk(KERN_INFO "lvm_do_lock_lvm: %s is locked by pid %d ...\n",
+                      lvm_name, lock);
+#endif
+               spin_unlock(&lvm_lock);
+               interruptible_sleep_on(&lvm_wait);
+               if (current->sigpending != 0)
+                       return -EINTR;
+#ifdef LVM_TOTAL_RESET
+               if (lvm_reset_spindown > 0)
+                       return -EACCES;
+#endif
+               goto lock_try_again;
+       }
+       lock = current->pid;
+       spin_unlock(&lvm_lock);
+       return 0;
+} /* lvm_do_lock_lvm */
+
+
+/*
+ * character device support function lock/unlock physical extend
+ */
+static int lvm_do_pe_lock_unlock(vg_t *vg_ptr, void *arg)
+{
+       uint p;
+
+       if (vg_ptr == NULL) return -ENXIO;
+       if (copy_from_user(&pe_lock_req, arg,
+                          sizeof(pe_lock_req_t)) != 0) return -EFAULT;
+
+       switch (pe_lock_req.lock) {
+       case LOCK_PE:
+               for (p = 0; p < vg_ptr->pv_max; p++) {
+                       if (vg_ptr->pv[p] != NULL &&
+                           pe_lock_req.data.pv_dev ==
+                           vg_ptr->pv[p]->pv_dev)
+                               break;
+               }
+               if (p == vg_ptr->pv_max) return -ENXIO;
+
+               pe_lock_req.lock = UNLOCK_PE;
+               fsync_dev(pe_lock_req.data.lv_dev);
+               pe_lock_req.lock = LOCK_PE;
+               break;
+
+       case UNLOCK_PE:
+               pe_lock_req.lock = UNLOCK_PE;
+               pe_lock_req.data.lv_dev = pe_lock_req.data.pv_dev = 0;
+               pe_lock_req.data.pv_offset = 0;
+               wake_up(&lvm_map_wait);
+               break;
+
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+
+/*
+ * character device support function logical extend remap
+ */
+static int lvm_do_le_remap(vg_t *vg_ptr, void *arg)
+{
+       uint l, le;
+       lv_t *lv_ptr;
+
+       if (vg_ptr == NULL) return -ENXIO;
+       if (copy_from_user(&le_remap_req, arg,
+                          sizeof(le_remap_req_t)) != 0)
+               return -EFAULT;
+
+       for (l = 0; l < vg_ptr->lv_max; l++) {
+               lv_ptr = vg_ptr->lv[l];
+               if (lv_ptr != NULL &&
+                   strcmp(lv_ptr->lv_name,
+                              le_remap_req.lv_name) == 0) {
+                       for (le = 0; le < lv_ptr->lv_allocated_le;
+                            le++) {
+                               if (lv_ptr->lv_current_pe[le].dev ==
+                                   le_remap_req.old_dev &&
+                                   lv_ptr->lv_current_pe[le].pe ==
+                                   le_remap_req.old_pe) {
+                                       lv_ptr->lv_current_pe[le].dev =
+                                           le_remap_req.new_dev;
+                                       lv_ptr->lv_current_pe[le].pe =
+                                           le_remap_req.new_pe;
+                                       return 0;
+                               }
+                       }
+                       return -EINVAL;
+               }
+       }
+       return -ENXIO;
+} /* lvm_do_le_remap() */
+
+
+/*
+ * character device support function VGDA create
+ */
+int lvm_do_vg_create(int minor, void *arg)
+{
+       int snaporg_minor = 0;
+       ulong l, p;
+       lv_t lv;
+       vg_t *vg_ptr;
+       pv_t *pv_ptr;
+       lv_t *lv_ptr;
+
+       if (vg[VG_CHR(minor)] != NULL) return -EPERM;
+
+       if ((vg_ptr = kmalloc(sizeof(vg_t),GFP_KERNEL)) == NULL) {
+               printk(KERN_CRIT
+                      "%s -- VG_CREATE: kmalloc error VG at line %d\n",
+                      lvm_name, __LINE__);
+               return -ENOMEM;
+       }
+       /* get the volume group structure */
+       if (copy_from_user(vg_ptr, arg, sizeof(vg_t)) != 0) {
+               kfree(vg_ptr);
+               return -EFAULT;
+       }
+
+       vg_devfs_handle[vg_ptr->vg_number] = devfs_mk_dir(0, vg_ptr->vg_name, NULL);
+       ch_devfs_handle[vg_ptr->vg_number] = devfs_register(
+               vg_devfs_handle[vg_ptr->vg_number] , "group",
+               DEVFS_FL_DEFAULT, LVM_CHAR_MAJOR, vg_ptr->vg_number,
+               S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP,
+               &lvm_chr_fops, NULL);
+
+       /* we are not that active so far... */
+       vg_ptr->vg_status &= ~VG_ACTIVE;
+       vg[VG_CHR(minor)] = vg_ptr;
+
+       vg[VG_CHR(minor)]->pe_allocated = 0;
+       if (vg_ptr->pv_max > ABS_MAX_PV) {
+               printk(KERN_WARNING
+                      "%s -- Can't activate VG: ABS_MAX_PV too small\n",
+                      lvm_name);
+               kfree(vg_ptr);
+               vg[VG_CHR(minor)] = NULL;
+               return -EPERM;
+       }
+       if (vg_ptr->lv_max > ABS_MAX_LV) {
+               printk(KERN_WARNING
+               "%s -- Can't activate VG: ABS_MAX_LV too small for %u\n",
+                      lvm_name, vg_ptr->lv_max);
+               kfree(vg_ptr);
+               vg_ptr = NULL;
+               return -EPERM;
+       }
+       /* get the physical volume structures */
+       vg_ptr->pv_act = vg_ptr->pv_cur = 0;
+       for (p = 0; p < vg_ptr->pv_max; p++) {
+               /* user space address */
+               if ((pvp = vg_ptr->pv[p]) != NULL) {
+                       pv_ptr = vg_ptr->pv[p] = kmalloc(sizeof(pv_t),GFP_KERNEL);
+                       if (pv_ptr == NULL) {
+                               printk(KERN_CRIT
+                                      "%s -- VG_CREATE: kmalloc error PV at line %d\n",
+                                      lvm_name, __LINE__);
+                               lvm_do_vg_remove(minor);
+                               return -ENOMEM;
+                       }
+                       if (copy_from_user(pv_ptr, pvp, sizeof(pv_t)) != 0) {
+                               lvm_do_vg_remove(minor);
+                               return -EFAULT;
+                       }
+                       /* We don't need the PE list
+                          in kernel space as with LVs pe_t list (see below) */
+                       pv_ptr->pe = NULL;
+                       pv_ptr->pe_allocated = 0;
+                       pv_ptr->pv_status = PV_ACTIVE;
+                       vg_ptr->pv_act++;
+                       vg_ptr->pv_cur++;
+
+#ifdef LVM_GET_INODE
+                       /* insert a dummy inode for fs_may_mount */
+                       pv_ptr->inode = lvm_get_inode(pv_ptr->pv_dev);
+#endif
+               }
+       }
+
+       /* get the logical volume structures */
+       vg_ptr->lv_cur = 0;
+       for (l = 0; l < vg_ptr->lv_max; l++) {
+               /* user space address */
+               if ((lvp = vg_ptr->lv[l]) != NULL) {
+                       if (copy_from_user(&lv, lvp, sizeof(lv_t)) != 0) {
+                               lvm_do_vg_remove(minor);
+                               return -EFAULT;
+                       }
+                       vg_ptr->lv[l] = NULL;
+                       if (lvm_do_lv_create(minor, lv.lv_name, &lv) != 0) {
+                               lvm_do_vg_remove(minor);
+                               return -EFAULT;
+                       }
+               }
+       }
+
+       /* Second path to correct snapshot logical volumes which are not
+          in place during first path above */
+       for (l = 0; l < vg_ptr->lv_max; l++) {
+               if ((lv_ptr = vg_ptr->lv[l]) != NULL &&
+                   vg_ptr->lv[l]->lv_access & LV_SNAPSHOT) {
+                       snaporg_minor = lv_ptr->lv_snapshot_minor;
+                       if (vg_ptr->lv[LV_BLK(snaporg_minor)] != NULL) {
+                               /* get pointer to original logical volume */
+                               lv_ptr = vg_ptr->lv[l]->lv_snapshot_org =
+                               vg_ptr->lv[LV_BLK(snaporg_minor)];
+
+                               /* set necessary fields of original logical volume */
+                               lv_ptr->lv_access |= LV_SNAPSHOT_ORG;
+                               lv_ptr->lv_snapshot_minor = 0;
+                               lv_ptr->lv_snapshot_org = lv_ptr;
+                               lv_ptr->lv_snapshot_prev = NULL;
+
+                               /* find last snapshot logical volume in the chain */
+                               while (lv_ptr->lv_snapshot_next != NULL)
+                                       lv_ptr = lv_ptr->lv_snapshot_next;
+
+                               /* set back pointer to this last one in our new logical volume */
+                               vg_ptr->lv[l]->lv_snapshot_prev = lv_ptr;
+
+                               /* last logical volume now points to our new snapshot volume */
+                               lv_ptr->lv_snapshot_next = vg_ptr->lv[l];
+
+                               /* now point to the new one */
+                               lv_ptr = lv_ptr->lv_snapshot_next;
+
+                               /* set necessary fields of new snapshot logical volume */
+                               lv_ptr->lv_snapshot_next = NULL;
+                               lv_ptr->lv_current_pe =
+                                   vg_ptr->lv[LV_BLK(snaporg_minor)]->lv_current_pe;
+                               lv_ptr->lv_allocated_le =
+                                   vg_ptr->lv[LV_BLK(snaporg_minor)]->lv_allocated_le;
+                               lv_ptr->lv_current_le =
+                                   vg_ptr->lv[LV_BLK(snaporg_minor)]->lv_current_le;
+                               lv_ptr->lv_size =
+                                   vg_ptr->lv[LV_BLK(snaporg_minor)]->lv_size;
+                       }
+               }
+       }
+
+       vg_count++;
+
+       /* let's go active */
+       vg_ptr->vg_status |= VG_ACTIVE;
+
+       MOD_INC_USE_COUNT;
+
+       return 0;
+} /* lvm_do_vg_create() */
+
+
+/*
+ * character device support function VGDA extend
+ */
+static int lvm_do_vg_extend(vg_t *vg_ptr, void *arg)
+{
+       uint p;
+       pv_t *pv_ptr;
+
+       if (vg_ptr == NULL) return -ENXIO;
+       if (vg_ptr->pv_cur < vg_ptr->pv_max) {
+               for (p = 0; p < vg_ptr->pv_max; p++) {
+                       if (vg_ptr->pv[p] == NULL) {
+                               if ((pv_ptr = vg_ptr->pv[p] = kmalloc(sizeof(pv_t),GFP_KERNEL)) == NULL) {
+                                       printk(KERN_CRIT
+                                              "%s -- VG_EXTEND: kmalloc error PV at line %d\n",
+                                            lvm_name, __LINE__);
+                                       return -ENOMEM;
+                               }
+                               if (copy_from_user(pv_ptr, arg, sizeof(pv_t)) != 0) {
+                                       kfree(pv_ptr);
+                                       vg_ptr->pv[p] = NULL;
+                                       return -EFAULT;
+                               }
+       
+                               pv_ptr->pv_status = PV_ACTIVE;
+                               /* We don't need the PE list
+                                  in kernel space like LVs pe_t list */
+                               pv_ptr->pe = NULL;
+                               vg_ptr->pv_cur++;
+                               vg_ptr->pv_act++;
+                               vg_ptr->pe_total +=
+                                   pv_ptr->pe_total;
+#ifdef LVM_GET_INODE
+                               /* insert a dummy inode for fs_may_mount */
+                               pv_ptr->inode = lvm_get_inode(pv_ptr->pv_dev);
+#endif
+                               return 0;
+                       }
+               }
+       }
+return -EPERM;
+} /* lvm_do_vg_extend() */
+
+
+/*
+ * character device support function VGDA reduce
+ */
+static int lvm_do_vg_reduce(vg_t *vg_ptr, void *arg)
+{
+       uint p;
+       pv_t *pv_ptr;
+
+       if (vg_ptr == NULL) return -ENXIO;
+       if (copy_from_user(pv_name, arg, sizeof(pv_name)) != 0)
+               return -EFAULT;
+
+       for (p = 0; p < vg_ptr->pv_max; p++) {
+               pv_ptr = vg_ptr->pv[p];
+               if (pv_ptr != NULL &&
+                   strcmp(pv_ptr->pv_name,
+                              pv_name) == 0) {
+                       if (pv_ptr->lv_cur > 0) return -EPERM;
+                       vg_ptr->pe_total -=
+                           pv_ptr->pe_total;
+                       vg_ptr->pv_cur--;
+                       vg_ptr->pv_act--;
+#ifdef LVM_GET_INODE
+                       lvm_clear_inode(pv_ptr->inode);
+#endif
+                       kfree(pv_ptr);
+                       /* Make PV pointer array contiguous */
+                       for (; p < vg_ptr->pv_max - 1; p++)
+                               vg_ptr->pv[p] = vg_ptr->pv[p + 1];
+                       vg_ptr->pv[p + 1] = NULL;
+                       return 0;
+               }
+       }
+       return -ENXIO;
+} /* lvm_do_vg_reduce */
+
+
+/*
+ * character device support function VGDA remove
+ */
+static int lvm_do_vg_remove(int minor)
+{
+       int i;
+       vg_t *vg_ptr = vg[VG_CHR(minor)];
+       pv_t *pv_ptr;
+
+       if (vg_ptr == NULL) return -ENXIO;
+
+#ifdef LVM_TOTAL_RESET
+       if (vg_ptr->lv_open > 0 && lvm_reset_spindown == 0)
+#else
+       if (vg_ptr->lv_open > 0)
+#endif
+               return -EPERM;
+
+       /* let's go inactive */
+       vg_ptr->vg_status &= ~VG_ACTIVE;
+
+       devfs_unregister (ch_devfs_handle[vg_ptr->vg_number]);
+       devfs_unregister (vg_devfs_handle[vg_ptr->vg_number]);
+
+       /* free LVs */
+       /* first free snapshot logical volumes */
+       for (i = 0; i < vg_ptr->lv_max; i++) {
+               if (vg_ptr->lv[i] != NULL &&
+                   vg_ptr->lv[i]->lv_access & LV_SNAPSHOT) {
+                       lvm_do_lv_remove(minor, NULL, i);
+                       current->state = TASK_UNINTERRUPTIBLE;
+                       schedule_timeout(1);
+               }
+       }
+       /* then free the rest of the LVs */
+       for (i = 0; i < vg_ptr->lv_max; i++) {
+               if (vg_ptr->lv[i] != NULL) {
+                       lvm_do_lv_remove(minor, NULL, i);
+                       current->state = TASK_UNINTERRUPTIBLE;
+                       schedule_timeout(1);
+               }
+       }
+
+       /* free PVs */
+       for (i = 0; i < vg_ptr->pv_max; i++) {
+               if ((pv_ptr = vg_ptr->pv[i]) != NULL) {
+#ifdef DEBUG_KFREE
+                       printk(KERN_DEBUG
+                              "%s -- kfree %d\n", lvm_name, __LINE__);
+#endif
+#ifdef LVM_GET_INODE
+                       lvm_clear_inode(pv_ptr->inode);
+#endif
+                       kfree(pv_ptr);
+                       vg[VG_CHR(minor)]->pv[i] = NULL;
+               }
+       }
+
+#ifdef DEBUG_KFREE
+       printk(KERN_DEBUG "%s -- kfree %d\n", lvm_name, __LINE__);
+#endif
+       kfree(vg_ptr);
+       vg[VG_CHR(minor)] = NULL;
+
+       vg_count--;
+
+       MOD_DEC_USE_COUNT;
+
+       return 0;
+} /* lvm_do_vg_remove() */
+
+
+/*
+ * character device support function logical volume create
+ */
+static int lvm_do_lv_create(int minor, char *lv_name, lv_t *lv)
+{
+       int l, le, l_new, p, size;
+       ulong lv_status_save;
+       char *lv_tmp, *lv_buf;
+       lv_block_exception_t *lvbe = lv->lv_block_exception;
+       vg_t *vg_ptr = vg[VG_CHR(minor)];
+       lv_t *lv_ptr = NULL;
+
+       if ((pep = lv->lv_current_pe) == NULL) return -EINVAL;
+       if (lv->lv_chunk_size > LVM_SNAPSHOT_MAX_CHUNK)
+               return -EINVAL;
+
+       for (l = 0; l < vg_ptr->lv_max; l++) {
+               if (vg_ptr->lv[l] != NULL &&
+                   strcmp(vg_ptr->lv[l]->lv_name, lv_name) == 0)
+                       return -EEXIST;
+       }
+
+       /* in case of lv_remove(), lv_create() pair; for eg. lvrename does this */
+       l_new = -1;
+       if (vg_ptr->lv[lv->lv_number] == NULL)
+               l_new = lv->lv_number;
+       else {
+               for (l = 0; l < vg_ptr->lv_max; l++) {
+                       if (vg_ptr->lv[l] == NULL)
+                               if (l_new == -1) l_new = l;
+               }
+       }
+       if (l_new == -1) return -EPERM;
+       else            l = l_new;
+
+       if ((lv_ptr = kmalloc(sizeof(lv_t),GFP_KERNEL)) == NULL) {;
+               printk(KERN_CRIT "%s -- LV_CREATE: kmalloc error LV at line %d\n",
+                      lvm_name, __LINE__);
+               return -ENOMEM;
+       }
+       /* copy preloaded LV */
+       memcpy((char *) lv_ptr, (char *) lv, sizeof(lv_t));
+
+       lv_status_save = lv_ptr->lv_status;
+       lv_ptr->lv_status &= ~LV_ACTIVE;
+       lv_ptr->lv_snapshot_org = \
+           lv_ptr->lv_snapshot_prev = \
+           lv_ptr->lv_snapshot_next = NULL;
+       lv_ptr->lv_block_exception = NULL;
+       init_MUTEX(&lv_ptr->lv_snapshot_sem);
+       vg_ptr->lv[l] = lv_ptr;
+
+       /* get the PE structures from user space if this
+          is no snapshot logical volume */
+       if (!(lv_ptr->lv_access & LV_SNAPSHOT)) {
+               size = lv_ptr->lv_allocated_le * sizeof(pe_t);
+               if ((lv_ptr->lv_current_pe = vmalloc(size)) == NULL) {
+                       printk(KERN_CRIT
+                              "%s -- LV_CREATE: vmalloc error LV_CURRENT_PE of %d Byte "
+                              "at line %d\n",
+                              lvm_name, size, __LINE__);
+#ifdef DEBUG_KFREE
+                       printk(KERN_DEBUG "%s -- kfree %d\n", lvm_name, __LINE__);
+#endif
+                       kfree(lv_ptr);
+                       vg[VG_CHR(minor)]->lv[l] = NULL;
+                       return -ENOMEM;
+               }
+               if (copy_from_user(lv_ptr->lv_current_pe, pep, size)) {
+                       vfree(lv_ptr->lv_current_pe);
+                       kfree(lv_ptr);
+                       vg_ptr->lv[l] = NULL;
+                       return -EFAULT;
+               }
+               /* correct the PE count in PVs */
+               for (le = 0; le < lv_ptr->lv_allocated_le; le++) {
+                       vg_ptr->pe_allocated++;
+                       for (p = 0; p < vg_ptr->pv_cur; p++) {
+                               if (vg_ptr->pv[p]->pv_dev ==
+                                   lv_ptr->lv_current_pe[le].dev)
+                                       vg_ptr->pv[p]->pe_allocated++;
+                       }
+               }
+       } else {
+               /* Get snapshot exception data and block list */
+               if (lvbe != NULL) {
+                       lv_ptr->lv_snapshot_org =
+                           vg_ptr->lv[LV_BLK(lv_ptr->lv_snapshot_minor)];
+                       if (lv_ptr->lv_snapshot_org != NULL) {
+                               size = lv_ptr->lv_remap_end * sizeof(lv_block_exception_t);
+                               if ((lv_ptr->lv_block_exception = vmalloc(size)) == NULL) {
+                                       printk(KERN_CRIT
+                                              "%s -- lvm_do_lv_create: vmalloc error LV_BLOCK_EXCEPTION "
+                                              "of %d byte at line %d\n",
+                                              lvm_name, size, __LINE__);
+#ifdef DEBUG_KFREE
+                                       printk(KERN_DEBUG "%s -- kfree %d\n", lvm_name, __LINE__);
+#endif
+                                       kfree(lv_ptr);
+                                       vg_ptr->lv[l] = NULL;
+                                       return -ENOMEM;
+                               }
+                               if (copy_from_user(lv_ptr->lv_block_exception, lvbe, size)) {
+                                       vfree(lv_ptr->lv_block_exception);
+                                       kfree(lv_ptr);
+                                       vg[VG_CHR(minor)]->lv[l] = NULL;
+                                       return -EFAULT;
+                               }
+                               /* get pointer to original logical volume */
+                               lv_ptr = lv_ptr->lv_snapshot_org;
+
+                               lv_ptr->lv_snapshot_minor = 0;
+                               lv_ptr->lv_snapshot_org = lv_ptr;
+                               lv_ptr->lv_snapshot_prev = NULL;
+                               /* walk thrugh the snapshot list */
+                               while (lv_ptr->lv_snapshot_next != NULL)
+                                       lv_ptr = lv_ptr->lv_snapshot_next;
+                               /* now lv_ptr points to the last existing snapshot in the chain */
+                               vg_ptr->lv[l]->lv_snapshot_prev = lv_ptr;
+                               /* our new one now back points to the previous last in the chain */
+                               lv_ptr = vg_ptr->lv[l];
+                               /* now lv_ptr points to our new last snapshot logical volume */
+                               lv_ptr->lv_snapshot_org = lv_ptr->lv_snapshot_prev->lv_snapshot_org;
+                               lv_ptr->lv_snapshot_next = NULL;
+                               lv_ptr->lv_current_pe = lv_ptr->lv_snapshot_org->lv_current_pe;
+                               lv_ptr->lv_allocated_le = lv_ptr->lv_snapshot_org->lv_allocated_le;
+                               lv_ptr->lv_current_le = lv_ptr->lv_snapshot_org->lv_current_le;
+                               lv_ptr->lv_size = lv_ptr->lv_snapshot_org->lv_size;
+                               lv_ptr->lv_stripes = lv_ptr->lv_snapshot_org->lv_stripes;
+                               lv_ptr->lv_stripesize = lv_ptr->lv_snapshot_org->lv_stripesize;
+                               {
+                                       int err = lvm_snapshot_alloc(lv_ptr);
+                                       if (err)
+                                       {
+                                               vfree(lv_ptr->lv_block_exception);
+                                               kfree(lv_ptr);
+                                               vg[VG_CHR(minor)]->lv[l] = NULL;
+                                                return err;
+                                       }
+                               }
+                       } else {
+                               vfree(lv_ptr->lv_block_exception);
+                               kfree(lv_ptr);
+                               vg_ptr->lv[l] = NULL;
+                               return -EFAULT;
+                       }
+               } else {
+                       kfree(vg_ptr->lv[l]);
+                       vg_ptr->lv[l] = NULL;
+                       return -EINVAL;
+               }
+       } /* if ( vg[VG_CHR(minor)]->lv[l]->lv_access & LV_SNAPSHOT) */
+
+       lv_ptr = vg_ptr->lv[l];
+       lvm_gendisk.part[MINOR(lv_ptr->lv_dev)].start_sect = 0;
+       lvm_gendisk.part[MINOR(lv_ptr->lv_dev)].nr_sects = lv_ptr->lv_size;
+       lvm_size[MINOR(lv_ptr->lv_dev)] = lv_ptr->lv_size >> 1;
+       vg_lv_map[MINOR(lv_ptr->lv_dev)].vg_number = vg_ptr->vg_number;
+       vg_lv_map[MINOR(lv_ptr->lv_dev)].lv_number = lv_ptr->lv_number;
+       read_ahead[MAJOR_NR] = lv_ptr->lv_read_ahead = LVM_CORRECT_READ_AHEAD(lv_ptr->lv_read_ahead);
+       vg_ptr->lv_cur++;
+       lv_ptr->lv_status = lv_status_save;
+
+       strtok(lv->lv_name, "/");       /* /dev */
+
+       while((lv_tmp = strtok(NULL, "/")) != NULL)
+               lv_buf = lv_tmp;
+
+       lv_devfs_handle[lv->lv_number] = devfs_register(
+               vg_devfs_handle[vg_ptr->vg_number], lv_buf,
+               DEVFS_FL_DEFAULT, LVM_BLK_MAJOR, lv->lv_number,
+               S_IFBLK | S_IRUSR | S_IWUSR | S_IRGRP,
+               &lvm_blk_dops, NULL);
+
+       /* optionally add our new snapshot LV */
+       if (lv_ptr->lv_access & LV_SNAPSHOT) {
+               /* sync the original logical volume */
+               fsync_dev(lv_ptr->lv_snapshot_org->lv_dev);
+               /* put ourselve into the chain */
+               lv_ptr->lv_snapshot_prev->lv_snapshot_next = lv_ptr;
+               lv_ptr->lv_snapshot_org->lv_access |= LV_SNAPSHOT_ORG;
+       }
+       return 0;
+} /* lvm_do_lv_create() */
+
+
+/*
+ * character device support function logical volume remove
+ */
+static int lvm_do_lv_remove(int minor, char *lv_name, int l)
+{
+       uint le, p;
+       vg_t *vg_ptr = vg[VG_CHR(minor)];
+       lv_t *lv_ptr;
+
+       if (l == -1) {
+               for (l = 0; l < vg_ptr->lv_max; l++) {
+                       if (vg_ptr->lv[l] != NULL &&
+                           strcmp(vg_ptr->lv[l]->lv_name, lv_name) == 0) {
+                               break;
+                       }
+               }
+       }
+       if (l == vg_ptr->lv_max) return -ENXIO;
+
+       lv_ptr = vg_ptr->lv[l];
+#ifdef LVM_TOTAL_RESET
+       if (lv_ptr->lv_open > 0 && lvm_reset_spindown == 0)
+#else
+       if (lv_ptr->lv_open > 0)
+#endif
+               return -EBUSY;
+
+       /* check for deletion of snapshot source while
+          snapshot volume still exists */
+       if ((lv_ptr->lv_access & LV_SNAPSHOT_ORG) &&
+           lv_ptr->lv_snapshot_next != NULL)
+               return -EPERM;
+
+       lv_ptr->lv_status |= LV_SPINDOWN;
+
+       /* sync the buffers */
+       fsync_dev(lv_ptr->lv_dev);
+
+       lv_ptr->lv_status &= ~LV_ACTIVE;
+
+       /* invalidate the buffers */
+       invalidate_buffers(lv_ptr->lv_dev);
+
+       /* reset generic hd */
+       lvm_gendisk.part[MINOR(lv_ptr->lv_dev)].start_sect = -1;
+       lvm_gendisk.part[MINOR(lv_ptr->lv_dev)].nr_sects = 0;
+       lvm_size[MINOR(lv_ptr->lv_dev)] = 0;
+
+       /* reset VG/LV mapping */
+       vg_lv_map[MINOR(lv_ptr->lv_dev)].vg_number = ABS_MAX_VG;
+       vg_lv_map[MINOR(lv_ptr->lv_dev)].lv_number = -1;
+
+       /* correct the PE count in PVs if this is no snapshot logical volume */
+       if (!(lv_ptr->lv_access & LV_SNAPSHOT)) {
+               /* only if this is no snapshot logical volume because
+                  we share the lv_current_pe[] structs with the
+                  original logical volume */
+               for (le = 0; le < lv_ptr->lv_allocated_le; le++) {
+                       vg_ptr->pe_allocated--;
+                       for (p = 0; p < vg_ptr->pv_cur; p++) {
+                               if (vg_ptr->pv[p]->pv_dev ==
+                                   lv_ptr->lv_current_pe[le].dev)
+                                       vg_ptr->pv[p]->pe_allocated--;
+                       }
+               }
+               vfree(lv_ptr->lv_current_pe);
+               /* LV_SNAPSHOT */
+       } else {
+               /* remove this snapshot logical volume from the chain */
+               lv_ptr->lv_snapshot_prev->lv_snapshot_next = lv_ptr->lv_snapshot_next;
+               if (lv_ptr->lv_snapshot_next != NULL) {
+                       lv_ptr->lv_snapshot_next->lv_snapshot_prev =
+                           lv_ptr->lv_snapshot_prev;
+               }
+               /* no more snapshots? */
+               if (lv_ptr->lv_snapshot_org->lv_snapshot_next == NULL)
+                       lv_ptr->lv_snapshot_org->lv_access &= ~LV_SNAPSHOT_ORG;
+               lvm_snapshot_release(lv_ptr);
+       }
+
+       devfs_unregister(lv_devfs_handle[lv_ptr->lv_number]);
+
+#ifdef DEBUG_KFREE
+       printk(KERN_DEBUG "%s -- kfree %d\n", lvm_name, __LINE__);
+#endif
+       kfree(lv_ptr);
+       vg_ptr->lv[l] = NULL;
+       vg_ptr->lv_cur--;
+       return 0;
+} /* lvm_do_lv_remove() */
+
+
+/*
+ * character device support function logical volume extend / reduce
+ */
+static int lvm_do_lv_extend_reduce(int minor, char *lv_name, lv_t *lv)
+{
+       int l, le, p, size, old_allocated_le;
+       uint32_t end, lv_status_save;
+       vg_t *vg_ptr = vg[VG_CHR(minor)];
+       lv_t *lv_ptr;
+       pe_t *pe;
+
+       if ((pep = lv->lv_current_pe) == NULL) return -EINVAL;
+
+       for (l = 0; l < vg_ptr->lv_max; l++) {
+               if (vg_ptr->lv[l] != NULL &&
+                   strcmp(vg_ptr->lv[l]->lv_name, lv_name) == 0)
+                       break;
+       }
+       if (l == vg_ptr->lv_max) return -ENXIO;
+       lv_ptr = vg_ptr->lv[l];
+
+       /* check for active snapshot */
+       if (lv->lv_access & (LV_SNAPSHOT | LV_SNAPSHOT_ORG)) return -EPERM;
+
+       if ((pe = vmalloc(size = lv->lv_current_le * sizeof(pe_t))) == NULL) {
+               printk(KERN_CRIT
+               "%s -- lvm_do_lv_extend_reduce: vmalloc error LV_CURRENT_PE "
+                      "of %d Byte at line %d\n",
+                      lvm_name, size, __LINE__);
+               return -ENOMEM;
+       }
+       /* get the PE structures from user space */
+       if (copy_from_user(pe, pep, size)) {
+               vfree(pe);
+               return -EFAULT;
+       }
+
+#ifdef DEBUG
+       printk(KERN_DEBUG
+              "%s -- fsync_dev and "
+              "invalidate_buffers for %s [%s] in %s\n",
+              lvm_name, lv_ptr->lv_name,
+              kdevname(lv_ptr->lv_dev),
+              vg_ptr->vg_name);
+#endif
+
+       lv_ptr->lv_status |= LV_SPINDOWN;
+       fsync_dev(lv_ptr->lv_dev);
+       lv_ptr->lv_status &= ~LV_ACTIVE;
+       invalidate_buffers(lv_ptr->lv_dev);
+
+       /* reduce allocation counters on PV(s) */
+       for (le = 0; le < lv_ptr->lv_allocated_le; le++) {
+               vg_ptr->pe_allocated--;
+               for (p = 0; p < vg_ptr->pv_cur; p++) {
+                       if (vg_ptr->pv[p]->pv_dev ==
+                       lv_ptr->lv_current_pe[le].dev) {
+                               vg_ptr->pv[p]->pe_allocated--;
+                               break;
+                       }
+               }
+       }
+
+
+       /* save pointer to "old" lv/pe pointer array */
+       pep1 = lv_ptr->lv_current_pe;
+       end = lv_ptr->lv_current_le;
+
+       /* save open counter */
+       lv_open = lv_ptr->lv_open;
+
+       /* save # of old allocated logical extents */
+       old_allocated_le = lv_ptr->lv_allocated_le;
+
+       /* copy preloaded LV */
+       lv_status_save = lv->lv_status;
+       lv->lv_status |= LV_SPINDOWN;
+       lv->lv_status &= ~LV_ACTIVE;
+       memcpy((char *) lv_ptr, (char *) lv, sizeof(lv_t));
+       lv_ptr->lv_current_pe = pe;
+       lv_ptr->lv_open = lv_open;
+
+       /* save availiable i/o statistic data */
+       /* linear logical volume */
+       if (lv_ptr->lv_stripes < 2) {
+               /* Check what last LE shall be used */
+               if (end > lv_ptr->lv_current_le) end = lv_ptr->lv_current_le;
+               for (le = 0; le < end; le++) {
+                       lv_ptr->lv_current_pe[le].reads  = pep1[le].reads;
+                       lv_ptr->lv_current_pe[le].writes = pep1[le].writes;
+               }
+               /* striped logical volume */
+       } else {
+               uint i, j, source, dest, end, old_stripe_size, new_stripe_size;
+
+               old_stripe_size = old_allocated_le / lv_ptr->lv_stripes;
+               new_stripe_size = lv_ptr->lv_allocated_le / lv_ptr->lv_stripes;
+               end = old_stripe_size;
+               if (end > new_stripe_size) end = new_stripe_size;
+               for (i = source = dest = 0;
+                    i < lv_ptr->lv_stripes; i++) {
+                       for (j = 0; j < end; j++) {
+                               lv_ptr->lv_current_pe[dest + j].reads =
+                                   pep1[source + j].reads;
+                               lv_ptr->lv_current_pe[dest + j].writes =
+                                   pep1[source + j].writes;
+                       }
+                       source += old_stripe_size;
+                       dest += new_stripe_size;
+               }
+       }
+       vfree(pep1);
+       pep1 = NULL;
+
+
+       /* extend the PE count in PVs */
+       for (le = 0; le < lv_ptr->lv_allocated_le; le++) {
+               vg_ptr->pe_allocated++;
+               for (p = 0; p < vg_ptr->pv_cur; p++) {
+                       if (vg_ptr->pv[p]->pv_dev ==
+                       vg_ptr->lv[l]->lv_current_pe[le].dev) {
+                               vg_ptr->pv[p]->pe_allocated++;
+                               break;
+                       }
+               }
+       }
+
+       lvm_gendisk.part[MINOR(lv_ptr->lv_dev)].start_sect = 0;
+       lvm_gendisk.part[MINOR(lv_ptr->lv_dev)].nr_sects = lv_ptr->lv_size;
+       lvm_size[MINOR(lv_ptr->lv_dev)] = lv_ptr->lv_size >> 1;
+       /* vg_lv_map array doesn't have to be changed here */
+
+       read_ahead[MAJOR_NR] = lv_ptr->lv_read_ahead = LVM_CORRECT_READ_AHEAD(lv_ptr->lv_read_ahead);
+       lv_ptr->lv_status = lv_status_save;
+
+       return 0;
+} /* lvm_do_lv_extend_reduce() */
+
+
+/*
+ * character device support function logical volume status by name
+ */
+static int lvm_do_lv_status_byname(vg_t *vg_ptr, void *arg)
+{
+       uint l;
+       ulong size;
+       lv_t lv;
+       lv_t *lv_ptr;
+       lv_status_byname_req_t lv_status_byname_req;
+
+       if (vg_ptr == NULL) return -ENXIO;
+       if (copy_from_user(&lv_status_byname_req, arg,
+                          sizeof(lv_status_byname_req_t)) != 0)
+               return -EFAULT;
+
+       if (lv_status_byname_req.lv == NULL) return -EINVAL;
+       if (copy_from_user(&lv, lv_status_byname_req.lv,
+                          sizeof(lv_t)) != 0)
+               return -EFAULT;
+
+       for (l = 0; l < vg_ptr->lv_max; l++) {
+               lv_ptr = vg_ptr->lv[l];
+               if (lv_ptr != NULL &&
+                   strcmp(lv_ptr->lv_name,
+                           lv_status_byname_req.lv_name) == 0) {
+                       if (copy_to_user(lv_status_byname_req.lv,
+                                        lv_ptr,
+                                        sizeof(lv_t)) != 0)
+                               return -EFAULT;
+
+                       if (lv.lv_current_pe != NULL) {
+                               size = lv_ptr->lv_allocated_le *
+                                      sizeof(pe_t);
+                               if (copy_to_user(lv.lv_current_pe,
+                                                lv_ptr->lv_current_pe,
+                                                size) != 0)
+                                       return -EFAULT;
+                       }
+                       return 0;
+               }
+       }
+       return -ENXIO;
+} /* lvm_do_lv_status_byname() */
+
+
+/*
+ * character device support function logical volume status by index
+ */
+static int lvm_do_lv_status_byindex(vg_t *vg_ptr,void *arg)
+{
+       ulong size;
+       lv_t lv;
+       lv_t *lv_ptr;
+       lv_status_byindex_req_t lv_status_byindex_req;
+
+       if (vg_ptr == NULL) return -ENXIO;
+       if (copy_from_user(&lv_status_byindex_req, arg,
+                          sizeof(lv_status_byindex_req)) != 0)
+               return -EFAULT;
+
+       if ((lvp = lv_status_byindex_req.lv) == NULL)
+               return -EINVAL;
+       if ( ( lv_ptr = vg_ptr->lv[lv_status_byindex_req.lv_index]) == NULL)
+               return -ENXIO;
+
+       if (copy_from_user(&lv, lvp, sizeof(lv_t)) != 0)
+               return -EFAULT;
+
+       if (copy_to_user(lvp, lv_ptr, sizeof(lv_t)) != 0)
+               return -EFAULT;
+
+       if (lv.lv_current_pe != NULL) {
+               size = lv_ptr->lv_allocated_le * sizeof(pe_t);
+               if (copy_to_user(lv.lv_current_pe,
+                                lv_ptr->lv_current_pe,
+                                size) != 0)
+                       return -EFAULT;
+       }
+       return 0;
+} /* lvm_do_lv_status_byindex() */
+
+
+/*
+ * character device support function physical volume change
+ */
+static int lvm_do_pv_change(vg_t *vg_ptr, void *arg)
+{
+       uint p;
+       pv_t *pv_ptr;
+#ifdef LVM_GET_INODE
+       struct inode *inode_sav;
+#endif
+
+       if (vg_ptr == NULL) return -ENXIO;
+       if (copy_from_user(&pv_change_req, arg,
+                          sizeof(pv_change_req)) != 0)
+               return -EFAULT;
+
+       for (p = 0; p < vg_ptr->pv_max; p++) {
+               pv_ptr = vg_ptr->pv[p];
+               if (pv_ptr != NULL &&
+                   strcmp(pv_ptr->pv_name,
+                              pv_change_req.pv_name) == 0) {
+#ifdef LVM_GET_INODE
+                       inode_sav = pv_ptr->inode;
+#endif
+                       if (copy_from_user(pv_ptr,
+                                          pv_change_req.pv,
+                                          sizeof(pv_t)) != 0)
+                               return -EFAULT;
+
+                       /* We don't need the PE list
+                          in kernel space as with LVs pe_t list */
+                       pv_ptr->pe = NULL;
+#ifdef LVM_GET_INODE
+                       pv_ptr->inode = inode_sav;
+#endif
+                       return 0;
+               }
+       }
+       return -ENXIO;
+} /* lvm_do_pv_change() */
+
+/*
+ * character device support function get physical volume status
+ */
+static int lvm_do_pv_status(vg_t *vg_ptr, void *arg)
+{
+       uint p;
+       pv_t *pv_ptr;
+
+       if (vg_ptr == NULL) return -ENXIO;
+       if (copy_from_user(&pv_status_req, arg,
+                          sizeof(pv_status_req)) != 0)
+               return -EFAULT;
+
+       for (p = 0; p < vg_ptr->pv_max; p++) {
+               pv_ptr = vg_ptr->pv[p];
+               if (pv_ptr != NULL &&
+                   strcmp(pv_ptr->pv_name,
+                              pv_status_req.pv_name) == 0) {
+                       if (copy_to_user(pv_status_req.pv,
+                                        pv_ptr,
+                                        sizeof(pv_t)) != 0)
+                               return -EFAULT;
+                       return 0;
+               }
+       }
+       return -ENXIO;
+} /* lvm_do_pv_status() */
+
+
+/*
+ * support function initialize gendisk variables
+ */
+#ifdef __initfunc
+__initfunc(void lvm_geninit(struct gendisk *lvm_gdisk))
+#else
+void __init
+ lvm_geninit(struct gendisk *lvm_gdisk)
+#endif
+{
+       int i = 0;
+
+#ifdef DEBUG_GENDISK
+       printk(KERN_DEBUG "%s -- lvm_gendisk\n", lvm_name);
+#endif
+
+       for (i = 0; i < MAX_LV; i++) {
+               lvm_gendisk.part[i].start_sect = -1;    /* avoid partition check */
+               lvm_size[i] = lvm_gendisk.part[i].nr_sects = 0;
+               lvm_blocksizes[i] = BLOCK_SIZE;
+       }
+
+       blksize_size[MAJOR_NR] = lvm_blocksizes;
+       blk_size[MAJOR_NR] = lvm_size;
+
+       return;
+} /* lvm_gen_init() */
+
+
+#ifdef LVM_GET_INODE
+/*
+ * support function to get an empty inode
+ *
+ * Gets an empty inode to be inserted into the inode hash,
+ * so that a physical volume can't be mounted.
+ * This is analog to drivers/block/md.c
+ *
+ * Is this the real thing?
+ *
+ *     No, it's bollocks. md.c tries to do a bit different thing that might
+ * _somewhat_ work eons ago. Neither does any good these days. mount() couldn't
+ * care less for icache (it cares only for ->s_root->d_count and if we want
+ * loopback mounts even that will stop). BTW, with the form used here mount()
+ * would have to scan the _whole_ icache to detect the attempt - how on the
+ * Earth could it guess the i_ino of your dummy inode? Official line on the
+ * exclusion between mount()/swapon()/open()/etc. is Just Don't Do It(tm).
+ * If you can convince Linus that it's worth changing - fine, then you'll need
+ * to do blkdev_get()/blkdev_put(). Until then...
+ */
+struct inode *lvm_get_inode(kdev_t dev)
+{
+       struct inode *inode_this = NULL;
+
+       /* Lock the device by inserting a dummy inode. */
+       inode_this = get_empty_inode();
+       inode_this->i_dev = dev;
+       insert_inode_hash(inode_this);
+       return inode_this;
+}
+
+
+/*
+ * support function to clear an inode
+ *
+ */
+void lvm_clear_inode(struct inode *inode)
+{
+#ifdef I_FREEING
+       inode->i_state |= I_FREEING;
+#endif
+       clear_inode(inode);
+       return;
+}
+#endif /* #ifdef LVM_GET_INODE */
index 587bfcbf6adfe227cfc1d62dd94f77484320643b..fb13001af0e5ef6d79795b17fbb1ed1524a79861 100644 (file)
@@ -34,6 +34,8 @@
 #include <linux/raid/xor.h>
 #include <linux/devfs_fs_kernel.h>
 
+#include <linux/init.h>
+
 #ifdef CONFIG_KMOD
 #include <linux/kmod.h>
 #endif
@@ -3846,6 +3848,11 @@ void cleanup_module (void)
 }
 #endif
 
+__initcall(md_init);
+#ifdef CONFIG_AUTODETECT_RAID
+__initcall(md_run_setup);
+#endif
+
 MD_EXPORT_SYMBOL(md_size);
 MD_EXPORT_SYMBOL(register_md_personality);
 MD_EXPORT_SYMBOL(unregister_md_personality);
index d2736c024f36c1f56fc9308b2c313d5bc2b9b465..2797c768e50923dae8bf6b4d48261aa560e3b197 100644 (file)
@@ -437,7 +437,7 @@ static int cfi_amdext_erase_2_by_16 (struct mtd_info *mtd, struct erase_info *in
        adr = instr->addr - (chipnum << cfi->chipshift) * (cfi->interleave);
        len = instr->len;
 
-printk("erase : 0x%x 0x%x 0x%x\n", adr, len, chipnum, mtd->size);
+printk("erase : 0x%lx 0x%lx 0x%x 0x%x\n", adr, len, chipnum, mtd->size);
 
        while(len) {
 //printk("erase : 0x%x 0x%x 0x%x 0x%x\n", chipnum, adr, len, cfi->chipshift);
index 84a74036e8945e14f0cb9bae78e1f3235463545a..ca904a285e8a506f2db4c443802c85cfc625e63c 100644 (file)
@@ -20,8 +20,8 @@
 #include <linux/errno.h>
 #include <linux/delay.h>
 #include <linux/sched.h>
+#include <linux/delay.h>
 #include <asm/io.h>
-#include <asm/delay.h>
 
 struct JEDECTable mtd_JEDEC_table[] = 
   {{0x01AD,"AMD Am29F016",2*1024*1024,64*1024,MTD_CAP_NORFLASH},
index 8a2aff56d6091b78cc2924495275224026a254d2..df0caa6fc622ff09ad92812b5a606851bd99ef13 100644 (file)
@@ -150,18 +150,17 @@ if [ "$CONFIG_NET_ETHERNET" = "y" ]; then
       if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
         tristate '    Novell/Eagle/Microdyne NE3210 EISA support (EXPERIMENTAL)' CONFIG_NE3210
       fi
-      if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
-         tristate '    RealTek 8129 (not 8019/8029!) support (EXPERIMENTAL)' CONFIG_RTL8129
-      fi
-      tristate '    RealTek RTL-8139 PCI Fast Ethernet Adapter support' CONFIG_8139TOO
-      tristate '    SiS 900/7016 PCI Fast Ethernet Adapter support' CONFIG_SIS900
-      # tristate '    Sundance Alta support' CONFIG_ALTA
-      tristate '    TI ThunderLAN support' CONFIG_TLAN
-      tristate '    VIA Rhine support' CONFIG_VIA_RHINE
       if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
         tristate '    Racal-Interlan EISA ES3210 support (EXPERIMENTAL)' CONFIG_ES3210
-        tristate '    SMC EtherPower II (EXPERIMENTAL)' CONFIG_EPIC100
+         dep_tristate '    RealTek 8129 (not 8019/8029/8139!) support (EXPERIMENTAL)' CONFIG_RTL8129 $CONFIG_PCI
       fi
+      dep_tristate '    RealTek RTL-8139 PCI Fast Ethernet Adapter support' CONFIG_8139TOO $CONFIG_PCI
+      dep_tristate '    SiS 900/7016 PCI Fast Ethernet Adapter support' CONFIG_SIS900 $CONFIG_PCI
+      dep_tristate '    SMC EtherPower II' CONFIG_EPIC100 $CONFIG_PCI
+      dep_tristate '    Sundance Alta support' CONFIG_SUNDANCE $CONFIG_PCI
+      tristate '    TI ThunderLAN support' CONFIG_TLAN
+      dep_tristate '    VIA Rhine support' CONFIG_VIA_RHINE $CONFIG_PCI
+      dep_tristate '    Winbond W89c840 Ethernet support' CONFIG_WINBOND_840 $CONFIG_PCI
       if [ "$CONFIG_OBSOLETE" = "y" ]; then
         bool '    Zenith Z-Note support (EXPERIMENTAL)' CONFIG_ZNET
       fi
@@ -185,21 +184,21 @@ endmenu
 mainmenu_option next_comment
 comment 'Ethernet (1000 Mbit)'
 
-if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
-   # tristate 'Packet Engines Hamachi GNIC-II support (EXPERIMENTAL)' CONFIG_HAMACHI
-   tristate 'Packet Engines Yellowfin Gigabit-NIC support (EXPERIMENTAL)' CONFIG_YELLOWFIN
-fi
-tristate 'Alteon AceNIC/3Com 3C985/NetGear GA620 Gigabit support' CONFIG_ACENIC
+dep_tristate 'Alteon AceNIC/3Com 3C985/NetGear GA620 Gigabit support' CONFIG_ACENIC $CONFIG_PCI
 if [ "$CONFIG_ACENIC" != "n" ]; then
    bool '  Omit support for old Tigon I based AceNICs' CONFIG_ACENIC_OMIT_TIGON_I
 fi
+dep_tristate 'Packet Engines Hamachi GNIC-II support' CONFIG_HAMACHI $CONFIG_PCI
+if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+   dep_tristate 'Packet Engines Yellowfin Gigabit-NIC support (EXPERIMENTAL)' CONFIG_YELLOWFIN $CONFIG_PCI
+fi
 tristate 'SysKonnect SK-98xx support' CONFIG_SK98LIN
 
 endmenu
 
 bool 'FDDI driver support' CONFIG_FDDI
 if [ "$CONFIG_FDDI" = "y" ]; then
-   tristate '  Digital DEFEA and DEFPA adapter support' CONFIG_DEFXX
+   dep_tristate '  Digital DEFEA and DEFPA adapter support' CONFIG_DEFXX $CONFIG_PCI
    tristate '  SysKonnect FDDI PCI support' CONFIG_SKFP
 fi
 
index 80d4dceda2152a3ace40b68ced5d01ab471b78e2..61c14198b6aa8f3afb98f97abd02df7afd1e310f 100644 (file)
@@ -170,6 +170,9 @@ obj-$(CONFIG_AIRONET4500_NONCS)  += aironet4500_card.o
 obj-$(CONFIG_AIRONET4500_PROC) += aironet4500_proc.o
 obj-$(CONFIG_AIRONET4500_CS)   += aironet4500_proc.o
 
+obj-$(CONFIG_WINBOND_840) += winbond-840.o
+obj-$(CONFIG_SUNDANCE) += sundance.o
+obj-$(CONFIG_HAMACHI) += hamachi.o
 obj-$(CONFIG_NET) += Space.o setup.o net_init.o loopback.o
 obj-$(CONFIG_SEEQ8005) += seeq8005.o
 obj-$(CONFIG_ETHERTAP) += ethertap.o
index 8fbbbac6337990ffd6bc495ca3bebf2bee641af1..1f4e08936ed8d7bfbc598f9fbd68678acc9fe864 100644 (file)
@@ -678,14 +678,6 @@ static struct net_device tr0_dev = {
 #define NEXT_DEV        (&escon0_dev)                                  
 #endif  
 
-#ifdef CONFIG_TUN
-    extern int tun_init(struct net_device *dev);
-    static struct net_device tun_dev = {
-        "tun", 0, 0, 0, 0, 0, 0, 0, 0, 0, NEXT_DEV, tun_init };
-#   undef       NEXT_DEV
-#   define      NEXT_DEV        (&tun_dev)
-#endif    
-
 /*
  *     The loopback device is global so it can be directly referenced
  *     by the network code. Also, it must be first on device list.
index 321893b4be3e12bf0b27497d4274ae904d40e44a..b937af2b3f28910dcc75ddc01d50a122d409e1de 100644 (file)
@@ -2613,7 +2613,7 @@ EXPORT_SYMBOL(awc_unregister_proc);
 
        long long jiff;
 
-       DEBUG(2, " awc_reset dev %x \n", (int)dev);
+       DEBUG(2, " awc_reset dev %p \n", dev);
        DEBUG(2, "%s: awc_reset \n",  dev->name);
        
        awc_issue_soft_reset(dev);
index a59443c6128a417a347e077f72f67022d9bff650..4fde3cc479a409f61358972910b8345999da257f 100644 (file)
@@ -16,13 +16,13 @@ obj-m   :=
 obj-    :=
 export-objs :=
 
-obj-$(CONFIG_LTPC) += ltpc.o
-obj-$(CONFIG_COPS) += cops.o
 obj-$(CONFIG_IPDDP) += ipddp.o
+obj-$(CONFIG_COPS) += cops.o
+obj-$(CONFIG_LTPC) += ltpc.o
 
-L_TARGET := appletalk.a
-L_OBJS   := $(filter-out $(export-objs), $(obj-y))
-LX_OBJS  := $(filter     $(export-objs), $(obj-y))
+O_TARGET := appletalk.o
+O_OBJS   := $(filter-out $(export-objs), $(obj-y))
+OX_OBJS  := $(filter     $(export-objs), $(obj-y))
 M_OBJS   := $(sort $(filter-out $(export-objs), $(obj-m)))
 MX_OBJS  := $(sort $(filter     $(export-objs), $(obj-m)))
 
index 713e4e4e65cf647ca9c84f92cadb47437ea08170..503c66167b8d7cba9dc31f1eb25f8abfacde732c 100644 (file)
@@ -27,36 +27,15 @@ static const char *version =
        "ipddp.c:v0.01 8/28/97 Bradford W. Johnson <johns393@maroon.tc.umn.edu>\n";
 
 #include <linux/config.h>
-#ifdef MODULE
 #include <linux/module.h>
-#include <linux/version.h>
-#endif
-
 #include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/types.h>
-#include <linux/fcntl.h>
-#include <linux/interrupt.h>
-#include <linux/ptrace.h>
-#include <linux/ioport.h>
-#include <linux/in.h>
-#include <linux/malloc.h>
-#include <linux/string.h>
-#include <asm/uaccess.h>
-#include <asm/system.h>
-#include <asm/bitops.h>
-#include <asm/io.h>
-#include <asm/dma.h>
-#include <linux/errno.h>
+#include <linux/init.h>
 #include <linux/netdevice.h>
-#include <linux/inetdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/skbuff.h>
-#include <linux/if_arp.h>
-#include <linux/atalk.h>
 #include <linux/ip.h>
+#include <linux/atalk.h>
+#include <linux/if_arp.h>
 #include <net/route.h>
-#include <linux/inet.h>
+#include <asm/uaccess.h>
 
 #include "ipddp.h"             /* Our stuff */
 
@@ -85,23 +64,17 @@ static int ipddp_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);
 
 static int ipddp_open(struct net_device *dev)
 {
-#ifdef MODULE
         MOD_INC_USE_COUNT;
-#endif
-
         return 0;
 }
 
 static int ipddp_close(struct net_device *dev)
 {
-#ifdef MODULE
         MOD_DEC_USE_COUNT;
-#endif
-
         return 0;
 }
 
-int ipddp_init(struct net_device *dev)
+static int __init ipddp_init(struct net_device *dev)
 {
        static unsigned version_printed = 0;
 
@@ -151,7 +124,7 @@ int ipddp_init(struct net_device *dev)
  */
 static struct net_device_stats *ipddp_get_stats(struct net_device *dev)
 {
-        return (struct net_device_stats *)dev->priv;
+        return dev->priv;
 }
 
 /*
@@ -325,19 +298,11 @@ static int ipddp_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
         }
 }
 
-#ifdef MODULE  /* Module specific functions for ipddp.c */
-
-static struct net_device dev_ipddp=
-{
-        "ipddp0\0   ",
-                0, 0, 0, 0,
-                0x0, 0,
-                0, 0, 0, NULL, ipddp_init
-};
+static struct net_device dev_ipddp = { init: ipddp_init };
 
 MODULE_PARM(ipddp_mode, "i");
 
-int init_module(void)
+static int __init ipddp_init_module(void)
 {
        int err;
 
@@ -351,11 +316,14 @@ int init_module(void)
        return 0;
 }
 
-void cleanup_module(void)
+static void __exit ipddp_cleanup_module(void)
 {
        unregister_netdev(&dev_ipddp);
         kfree(dev_ipddp.priv);
-       dev_ipddp.priv = NULL;
+
+       memset(&dev_ipddp, 0, sizeof(dev_ipddp));
+       dev_ipddp.init = ipddp_init;
 }
 
-#endif /* MODULE */
+module_init(ipddp_init_module);
+module_exit(ipddp_cleanup_module);
index a898fbb02d8f5cadc89b7f2bfa29fda511dcd017..25d2dbd629ab486fa9e58f6fa2f54ccf63450302 100644 (file)
@@ -1,27 +1,59 @@
 /* atp.c: Attached (pocket) ethernet adapter driver for linux. */
 /*
-       This is a driver for a commonly OEMed pocket (parallel port)
-       ethernet adapter.  
+       This is a driver for commonly OEM pocket (parallel port)
+       ethernet adapters based on the Realtek RTL8002 and RTL8012 chips.
 
-       Written 1993,1994,1995 by Donald Becker.
+       Written 1993-2000 by Donald Becker.
 
-       Copyright 1993 United States Government as represented by the
-       Director, National Security Agency.
+       This software may be used and distributed according to the terms of
+       the GNU General Public License (GPL), incorporated herein by reference.
+       Drivers based on or derived from this code fall under the GPL and must
+       retain the authorship, copyright and license notice.  This file is not
+       a complete program and may only be used when the entire operating
+       system is licensed under the GPL.
 
-       This software may be used and distributed according to the terms
-       of the GNU Public License, incorporated herein by reference.
+       Copyright 1993 United States Government as represented by the Director,
+       National Security Agency.  Copyright 1994-2000 retained by the original
+       author, Donald Becker. The timer-based reset code was supplied in 1995
+       by Bill Carlson, wwc@super.org.
+
+       The author may be reached as becker@scyld.com, or C/O
+       Scyld Computing Corporation
+       410 Severn Ave., Suite 210
+       Annapolis MD 21403
+
+       Support information and updates available at
+       http://www.scyld.com/network/atp.html
 
-       The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O
-       Center of Excellence in Space Data and Information Sciences
-               Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
 
-       The timer-based reset code was written by Bill Carlson, wwc@super.org.
-       
        Modular support/softnet added by Alan Cox.
+
 */
 
-static const char *version =
-       "atp.c:v1.01 1/18/95 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
+static const char versionA[] =
+"atp.c:v1.09 8/9/2000 Donald Becker <becker@scyld.com>\n";
+static const char versionB[] =
+"  http://www.scyld.com/network/atp.html\n";
+
+/* The user-configurable values.
+   These may be modified when a driver module is loaded.*/
+
+static int debug = 1;                  /* 1 normal messages, 0 quiet .. 7 verbose. */
+#define net_debug debug
+
+/* Maximum events (Rx packets, etc.) to handle at each interrupt. */
+static int max_interrupt_work = 15;
+
+#define NUM_UNITS 2
+/* The standard set of ISA module parameters. */
+static int io[NUM_UNITS] = {0, 0};
+static int irq[NUM_UNITS] = {0, 0};
+static int xcvr[NUM_UNITS] = {0, 0};                   /* The data transfer mode. */
+
+/* Operational parameters that are set at compile time. */
+
+/* Time in jiffies before concluding the transmitter is hung. */
+#define TX_TIMEOUT  (400*HZ/1000)
 
 /*
        This file is a device driver for the RealTek (aka AT-Lan-Tec) pocket
@@ -35,8 +67,12 @@ static const char *version =
        description is written based on guesses and writing lots of special-purpose
        code to test my theorized operation.
 
+       In 1997 Realtek made available the documentation for the second generation
+       RTL8012 chip, which has lead to several driver improvements.
+         http://www.realtek.com.tw/cn/cn.html
+
                                        Theory of Operation
-       
+
        The RTL8002 adapter seems to be built around a custom spin of the SEEQ
        controller core.  It probably has a 16K or 64K internal packet buffer, of
        which the first 4K is devoted to transmit and the rest to receive.
@@ -60,6 +96,10 @@ static const char *version =
        timing and control bits.  The data is then read from status port or written
        to the data port.
 
+       Correction: the controller has two banks of 16 registers.  The second
+       bank contains only the multicast filter table (now used) and the EEPROM
+       access registers.
+
        Since the bulk data transfer of the actual packets through the slow
        parallel port dominates the driver's running time, four distinct data
        (non-register) transfer modes are provided by the adapter, two in each
@@ -105,18 +145,40 @@ static const char *version =
 #include <linux/etherdevice.h>
 #include <linux/skbuff.h>
 #include <linux/spinlock.h>
+#include <linux/delay.h>
 
 #include "atp.h"
 
-/* use 0 for production, 1 for verification, >2 for debug */
-#ifndef NET_DEBUG
-#define NET_DEBUG 1
-#endif
-static unsigned int net_debug = NET_DEBUG;
+MODULE_AUTHOR("Donald Becker <becker@scyld.com>");
+MODULE_DESCRIPTION("RealTek RTL8002/8012 parallel port Ethernet driver");
+MODULE_PARM(max_interrupt_work, "i");
+MODULE_PARM(debug, "i");
+MODULE_PARM(io, "1-" __MODULE_STRING(MAX_UNITS) "i");
+MODULE_PARM(irq, "1-" __MODULE_STRING(MAX_UNITS) "i");
+MODULE_PARM(xcvr, "1-" __MODULE_STRING(MAX_UNITS) "i");
+
+#define RUN_AT(x) (jiffies + (x))
 
 /* The number of low I/O ports used by the ethercard. */
 #define ETHERCARD_TOTAL_SIZE   3
 
+/* Sequence to switch an 8012 from printer mux to ethernet mode. */
+static char mux_8012[] = { 0xff, 0xf7, 0xff, 0xfb, 0xf3, 0xfb, 0xff, 0xf7,};
+
+struct net_local {
+    spinlock_t lock;
+    struct net_device *next_module;
+    struct net_device_stats stats;
+    struct timer_list timer;   /* Media selection timer. */
+    long last_rx_time;         /* Last Rx, in jiffies, to handle Rx hang. */
+    int saved_tx_size;
+    unsigned int tx_unit_busy:1;
+    unsigned char re_tx,       /* Number of packet retransmissions. */
+               addr_mode,              /* Current Rx filter e.g. promiscuous, etc. */
+               pac_cnt_in_tx_buf,
+               chip_type;
+};
+
 /* This code, written by wwc@super.org, resets the adapter every
    TIMED_CHECKER ticks.  This recovers from an unknown error which
    hangs the device. */
@@ -124,40 +186,41 @@ static unsigned int net_debug = NET_DEBUG;
 #ifdef TIMED_CHECKER
 #include <linux/timer.h>
 static void atp_timed_checker(unsigned long ignored);
-static struct net_device *atp_timed_dev;
-static struct timer_list atp_timer = { function: atp_timed_checker };
 #endif
 
 /* Index to functions, as function prototypes. */
 
-static int atp_probe1(struct net_device *dev, short ioaddr);
+static int atp_probe1(struct net_device *dev, long ioaddr);
 static void get_node_ID(struct net_device *dev);
-static unsigned short eeprom_op(short ioaddr, unsigned int cmd);
+static unsigned short eeprom_op(long ioaddr, unsigned int cmd);
 static int net_open(struct net_device *dev);
 static void hardware_init(struct net_device *dev);
-static void tx_timeout(struct net_device *dev);
-static void write_packet(short ioaddr, int length, unsigned char *packet, int mode);
-static void trigger_send(short ioaddr, int length);
-static int net_send_packet(struct sk_buff *skb, struct net_device *dev);
-static void net_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+static void write_packet(long ioaddr, int length, unsigned char *packet, int mode);
+static void trigger_send(long ioaddr, int length);
+static int     atp_send_packet(struct sk_buff *skb, struct net_device *dev);
+static void atp_interrupt(int irq, void *dev_id, struct pt_regs *regs);
 static void net_rx(struct net_device *dev);
-static void read_block(short ioaddr, int length, unsigned char *buffer, int data_mode);
+static void read_block(long ioaddr, int length, unsigned char *buffer, int data_mode);
 static int net_close(struct net_device *dev);
 static struct net_device_stats *net_get_stats(struct net_device *dev);
-static void set_multicast_list(struct net_device *dev);
+static void set_rx_mode_8002(struct net_device *dev);
+static void set_rx_mode_8012(struct net_device *dev);
+static void tx_timeout(struct net_device *dev);
+
+
+/* A list of all installed ATP devices, for removing the driver module. */
+static struct net_device *root_atp_dev = NULL;
 
-\f
 /* Check for a network adapter of this type, and return '0' iff one exists.
    If dev->base_addr == 0, probe all likely locations.
    If dev->base_addr == 1, always return failure.
    If dev->base_addr == 2, allocate space for the device and return success
    (detachable devices only).
    */
-
 static int __init atp_init(struct net_device *dev)
 {
        int *port, ports[] = {0x378, 0x278, 0x3bc, 0};
-       int base_addr = dev->base_addr;
+       int base_addr = dev ? dev->base_addr : io[0];
 
        if (base_addr > 0x1ff)          /* Check a single specified location. */
                return atp_probe1(dev, base_addr);
@@ -165,7 +228,7 @@ static int __init atp_init(struct net_device *dev)
                return -ENXIO;
 
        for (port = ports; *port; port++) {
-               int ioaddr = *port;
+               long ioaddr = *port;
                outb(0x57, ioaddr + PAR_DATA);
                if (inb(ioaddr + PAR_DATA) != 0x57)
                        continue;
@@ -176,36 +239,68 @@ static int __init atp_init(struct net_device *dev)
        return -ENODEV;
 }
 
-static int __init atp_probe1(struct net_device *dev, short ioaddr)
+static int __init atp_probe1(struct net_device *dev, long ioaddr)
 {
-       int saved_ctrl_reg, status;
+       struct net_local *lp;
+       int saved_ctrl_reg, status, i;
 
        outb(0xff, ioaddr + PAR_DATA);
        /* Save the original value of the Control register, in case we guessed
           wrong. */
        saved_ctrl_reg = inb(ioaddr + PAR_CONTROL);
+       if (net_debug > 3)
+               printk("atp: Control register was %#2.2x.\n", saved_ctrl_reg);
        /* IRQEN=0, SLCTB=high INITB=high, AUTOFDB=high, STBB=high. */
        outb(0x04, ioaddr + PAR_CONTROL);
+#ifndef final_version
+       if (net_debug > 3) {
+               /* Turn off the printer multiplexer on the 8012. */
+               for (i = 0; i < 8; i++)
+                       outb(mux_8012[i], ioaddr + PAR_DATA);
+               write_reg(ioaddr, MODSEL, 0x00);
+               printk("atp: Registers are ");
+               for (i = 0; i < 32; i++)
+                       printk(" %2.2x", read_nibble(ioaddr, i));
+               printk(".\n");
+       }
+#endif
+       /* Turn off the printer multiplexer on the 8012. */
+       for (i = 0; i < 8; i++)
+               outb(mux_8012[i], ioaddr + PAR_DATA);
        write_reg_high(ioaddr, CMR1, CMR1h_RESET);
-       udelay(100);
+       /* udelay() here? */
        status = read_nibble(ioaddr, CMR1);
 
+       if (net_debug > 3) {
+               printk(KERN_DEBUG "atp: Status nibble was %#2.2x..", status);
+               for (i = 0; i < 32; i++)
+                       printk(" %2.2x", read_nibble(ioaddr, i));
+               printk("\n");
+       }
+
        if ((status & 0x78) != 0x08) {
                /* The pocket adapter probe failed, restore the control register. */
                outb(saved_ctrl_reg, ioaddr + PAR_CONTROL);
-               return 1;
+               return -ENODEV;
        }
        status = read_nibble(ioaddr, CMR2_h);
        if ((status & 0x78) != 0x10) {
                outb(saved_ctrl_reg, ioaddr + PAR_CONTROL);
-               return 1;
+               return -ENODEV;
        }
+
+       dev = init_etherdev(dev, sizeof(struct net_local));
+       if (!dev)
+               return -ENOMEM;
+
        /* Find the IRQ used by triggering an interrupt. */
        write_reg_byte(ioaddr, CMR2, 0x01);                     /* No accept mode, IRQ out. */
        write_reg_high(ioaddr, CMR1, CMR1h_RxENABLE | CMR1h_TxENABLE);  /* Enable Tx and Rx. */
 
        /* Omit autoIRQ routine for now. Use "table lookup" instead.  Uhgggh. */
-       if (ioaddr == 0x378)
+       if (irq[0])
+               dev->irq = irq[0];
+       else if (ioaddr == 0x378)
                dev->irq = 7;
        else
                dev->irq = 5;
@@ -217,69 +312,73 @@ static int __init atp_probe1(struct net_device *dev, short ioaddr)
        /* Read the station address PROM.  */
        get_node_ID(dev);
 
-       printk("%s: Pocket adapter found at %#3lx, IRQ %d, SAPROM "
+#ifndef MODULE
+       if (net_debug)
+               printk(KERN_INFO "%s" KERN_INFO "%s", versionA, versionB);
+#endif
+
+       printk(KERN_NOTICE "%s: Pocket adapter found at %#3lx, IRQ %d, SAPROM "
                   "%02X:%02X:%02X:%02X:%02X:%02X.\n", dev->name, dev->base_addr,
                   dev->irq, dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2],
                   dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]);
 
-       /* Leave the hardware in a reset state. */
-       write_reg_high(ioaddr, CMR1, CMR1h_RESET);
-
-       if (net_debug)
-               printk(version);
+       /* Reset the ethernet hardware and activate the printer pass-through. */
+    write_reg_high(ioaddr, CMR1, CMR1h_RESET | CMR1h_MUX);
 
        /* Initialize the device structure. */
        ether_setup(dev);
-       dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL);
+       if (dev->priv == NULL)
+               dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL);
        if (dev->priv == NULL)
                return -ENOMEM;
        memset(dev->priv, 0, sizeof(struct net_local));
-       {
-               struct net_local *lp = (struct net_local *)dev->priv;
-               lp->addr_mode = CMR2h_Normal;
-               spin_lock_init(&lp->lock);
-       }
+
+       lp = (struct net_local *)dev->priv;
+       lp->chip_type = RTL8002;
+       lp->addr_mode = CMR2h_Normal;
+       spin_lock_init(&lp->lock);
+
+       lp->next_module = root_atp_dev;
+       root_atp_dev = dev;
 
        /* For the ATP adapter the "if_port" is really the data transfer mode. */
-       dev->if_port = (dev->mem_start & 0xf) ? dev->mem_start & 0x7 : 4;
+       if (xcvr[0])
+               dev->if_port = xcvr[0];
+       else
+               dev->if_port = (dev->mem_start & 0xf) ? (dev->mem_start & 0x7) : 4;
        if (dev->mem_end & 0xf)
                net_debug = dev->mem_end & 7;
 
        dev->open               = net_open;
        dev->stop               = net_close;
-       dev->hard_start_xmit    = net_send_packet;
+       dev->hard_start_xmit    = atp_send_packet;
        dev->get_stats          = net_get_stats;
-       dev->set_multicast_list = set_multicast_list;
+       dev->set_multicast_list =
+         lp->chip_type == RTL8002 ? &set_rx_mode_8002 : &set_rx_mode_8012;
        dev->tx_timeout         = tx_timeout;
-       dev->watchdog_timeo     = HZ/20;
+       dev->watchdog_timeo     = TX_TIMEOUT;
 
-#ifdef TIMED_CHECKER
-       del_timer(&atp_timer);
-       atp_timer.expires = jiffies + TIMED_CHECKER;
-       atp_timed_dev = dev;
-       add_timer(&atp_timer);
-#endif
        return 0;
 }
 
 /* Read the station address PROM, usually a word-wide EEPROM. */
 static void __init get_node_ID(struct net_device *dev)
 {
-       short ioaddr = dev->base_addr;
+       long ioaddr = dev->base_addr;
        int sa_offset = 0;
        int i;
-       
+
        write_reg(ioaddr, CMR2, CMR2_EEPROM);     /* Point to the EEPROM control registers. */
-       
+
        /* Some adapters have the station address at offset 15 instead of offset
           zero.  Check for it, and fix it if needed. */
        if (eeprom_op(ioaddr, EE_READ(0)) == 0xffff)
                sa_offset = 15;
-       
+
        for (i = 0; i < 3; i++)
-               ((unsigned short *)dev->dev_addr)[i] =
-                       ntohs(eeprom_op(ioaddr, EE_READ(sa_offset + i)));
-       
+               ((u16 *)dev->dev_addr)[i] =
+                       be16_to_cpu(eeprom_op(ioaddr, EE_READ(sa_offset + i)));
+
        write_reg(ioaddr, CMR2, CMR2_NULL);
 }
 
@@ -295,26 +394,24 @@ static void __init get_node_ID(struct net_device *dev)
  * DO :         _________X_______X
  */
 
-static unsigned short __init eeprom_op(short ioaddr, unsigned int cmd)
+static unsigned short __init eeprom_op(long ioaddr, unsigned int cmd)
 {
        unsigned eedata_out = 0;
        int num_bits = EE_CMD_SIZE;
-       
+
        while (--num_bits >= 0) {
                char outval = test_bit(num_bits, &cmd) ? EE_DATA_WRITE : 0;
                write_reg_high(ioaddr, PROM_CMD, outval | EE_CLK_LOW);
-               udelay(5);
                write_reg_high(ioaddr, PROM_CMD, outval | EE_CLK_HIGH);
                eedata_out <<= 1;
                if (read_nibble(ioaddr, PROM_DATA) & EE_DATA_READ)
                        eedata_out++;
-               udelay(5);
        }
        write_reg_high(ioaddr, PROM_CMD, EE_CLK_LOW & ~EE_CS);
        return eedata_out;
 }
 
-\f
+
 /* Open/initialize the board.  This is called (in the current kernel)
    sometime after booting when the 'ifconfig' program is run.
 
@@ -327,17 +424,25 @@ static unsigned short __init eeprom_op(short ioaddr, unsigned int cmd)
    */
 static int net_open(struct net_device *dev)
 {
+       struct net_local *lp = (struct net_local *)dev->priv;
+
+       MOD_INC_USE_COUNT;
 
        /* The interrupt line is turned off (tri-stated) when the device isn't in
           use.  That's especially important for "attached" interfaces where the
           port or interrupt may be shared. */
-          
-       if (request_irq(dev->irq, &net_interrupt, 0, "ATP", dev)) {
+       if (request_irq(dev->irq, &atp_interrupt, 0, "ATP Ethernet", dev)) {
+               MOD_DEC_USE_COUNT;
                return -EAGAIN;
        }
+
        hardware_init(dev);
 
-       MOD_INC_USE_COUNT;
+       init_timer(&lp->timer);
+       lp->timer.expires = RUN_AT(TIMED_CHECKER);
+       lp->timer.data = (unsigned long)dev;
+       lp->timer.function = &atp_timed_checker;    /* timer handler */
+       add_timer(&lp->timer);
 
        netif_start_queue(dev);
        return 0;
@@ -348,54 +453,57 @@ static int net_open(struct net_device *dev)
 static void hardware_init(struct net_device *dev)
 {
        struct net_local *lp = (struct net_local *)dev->priv;
-       int ioaddr = dev->base_addr;
-       int i;
+       long ioaddr = dev->base_addr;
+    int i;
 
+       /* Turn off the printer multiplexer on the 8012. */
+       for (i = 0; i < 8; i++)
+               outb(mux_8012[i], ioaddr + PAR_DATA);
        write_reg_high(ioaddr, CMR1, CMR1h_RESET);
-       
-       for (i = 0; i < 6; i++)
+
+    for (i = 0; i < 6; i++)
                write_reg_byte(ioaddr, PAR0 + i, dev->dev_addr[i]);
 
        write_reg_high(ioaddr, CMR2, lp->addr_mode);
 
        if (net_debug > 2) {
-               printk("%s: Reset: current Rx mode %d.\n", dev->name,
+               printk(KERN_DEBUG "%s: Reset: current Rx mode %d.\n", dev->name,
                           (read_nibble(ioaddr, CMR2_h) >> 3) & 0x0f);
        }
 
-       write_reg(ioaddr, CMR2, CMR2_IRQOUT);
-       write_reg_high(ioaddr, CMR1, CMR1h_RxENABLE | CMR1h_TxENABLE);
+    write_reg(ioaddr, CMR2, CMR2_IRQOUT);
+    write_reg_high(ioaddr, CMR1, CMR1h_RxENABLE | CMR1h_TxENABLE);
 
        /* Enable the interrupt line from the serial port. */
        outb(Ctrl_SelData + Ctrl_IRQEN, ioaddr + PAR_CONTROL);
 
        /* Unmask the interesting interrupts. */
-       write_reg(ioaddr, IMR, ISR_RxOK | ISR_TxErr | ISR_TxOK);
-       write_reg_high(ioaddr, IMR, ISRh_RxErr);
+    write_reg(ioaddr, IMR, ISR_RxOK | ISR_TxErr | ISR_TxOK);
+    write_reg_high(ioaddr, IMR, ISRh_RxErr);
 
        lp->tx_unit_busy = 0;
-       lp->pac_cnt_in_tx_buf = 0;
+    lp->pac_cnt_in_tx_buf = 0;
        lp->saved_tx_size = 0;
 }
 
-static void trigger_send(short ioaddr, int length)
+static void trigger_send(long ioaddr, int length)
 {
        write_reg_byte(ioaddr, TxCNT0, length & 0xff);
        write_reg(ioaddr, TxCNT1, length >> 8);
        write_reg(ioaddr, CMR1, CMR1_Xmit);
 }
 
-static void write_packet(short ioaddr, int length, unsigned char *packet, int data_mode)
+static void write_packet(long ioaddr, int length, unsigned char *packet, int data_mode)
 {
-       length = (length + 1) & ~1;             /* Round up to word length. */
-       outb(EOC+MAR, ioaddr + PAR_DATA);
-       if ((data_mode & 1) == 0) {
+    length = (length + 1) & ~1;                /* Round up to word length. */
+    outb(EOC+MAR, ioaddr + PAR_DATA);
+    if ((data_mode & 1) == 0) {
                /* Write the packet out, starting with the write addr. */
                outb(WrAddr+MAR, ioaddr + PAR_DATA);
                do {
                        write_byte_mode0(ioaddr, *packet++);
                } while (--length > 0) ;
-       } else {
+    } else {
                /* Write the packet out in slow mode. */
                unsigned char outbyte = *packet++;
 
@@ -409,77 +517,85 @@ static void write_packet(short ioaddr, int length, unsigned char *packet, int da
                outb(Ctrl_HNibWrite + Ctrl_IRQEN, ioaddr + PAR_CONTROL);
                while (--length > 0)
                        write_byte_mode1(ioaddr, *packet++);
-       }
-       /* Terminate the Tx frame.  End of write: ECB. */
-       outb(0xff, ioaddr + PAR_DATA);
-       outb(Ctrl_HNibWrite | Ctrl_SelData | Ctrl_IRQEN, ioaddr + PAR_CONTROL);
+    }
+    /* Terminate the Tx frame.  End of write: ECB. */
+    outb(0xff, ioaddr + PAR_DATA);
+    outb(Ctrl_HNibWrite | Ctrl_SelData | Ctrl_IRQEN, ioaddr + PAR_CONTROL);
 }
 
 static void tx_timeout(struct net_device *dev)
 {
-       struct net_local *lp = (struct net_local *)dev->priv;
-       int ioaddr = dev->base_addr;
-       /* If we get here, some higher level has decided we are broken. */
-       printk(KERN_WARNING "%s: transmit timed out, %s?\n", dev->name,
+       struct net_local *np = (struct net_local *)dev->priv;
+       long ioaddr = dev->base_addr;
+
+       printk(KERN_WARNING "%s: Transmit timed out, %s?\n", dev->name,
                   inb(ioaddr + PAR_CONTROL) & 0x10 ? "network cable problem"
                   :  "IRQ conflict");
-       lp->stats.tx_errors++;
+       np->stats.tx_errors++;
        /* Try to restart the adapter. */
        hardware_init(dev);
        dev->trans_start = jiffies;
        netif_wake_queue(dev);
+       np->stats.tx_errors++;
+       return;
 }
 
-static int net_send_packet(struct sk_buff *skb, struct net_device *dev)
+static int atp_send_packet(struct sk_buff *skb, struct net_device *dev)
 {
        struct net_local *lp = (struct net_local *)dev->priv;
-       int ioaddr = dev->base_addr;
-       short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
-       unsigned char *buf = skb->data;
-       unsigned long flags;
-       
+       long ioaddr = dev->base_addr;
+       int length;
+       long flags;
+
+       length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
+
        netif_stop_queue(dev);
-       
+
        /* Disable interrupts by writing 0x00 to the Interrupt Mask Register.
           This sequence must not be interrupted by an incoming packet. */
-          
+
        spin_lock_irqsave(&lp->lock, flags);
        write_reg(ioaddr, IMR, 0);
        write_reg_high(ioaddr, IMR, 0);
        spin_unlock_irqrestore(&lp->lock, flags);
-       
-       write_packet(ioaddr, length, buf, dev->if_port);
+
+       write_packet(ioaddr, length, skb->data, dev->if_port);
 
        lp->pac_cnt_in_tx_buf++;
        if (lp->tx_unit_busy == 0) {
                trigger_send(ioaddr, length);
                lp->saved_tx_size = 0;                          /* Redundant */
                lp->re_tx = 0;
-                       lp->tx_unit_busy = 1;
+               lp->tx_unit_busy = 1;
        } else
                lp->saved_tx_size = length;
-
-       dev->trans_start = jiffies;
        /* Re-enable the LPT interrupts. */
        write_reg(ioaddr, IMR, ISR_RxOK | ISR_TxErr | ISR_TxOK);
        write_reg_high(ioaddr, IMR, ISRh_RxErr);
+
+       dev->trans_start = jiffies;
        dev_kfree_skb (skb);
        return 0;
 }
-\f
+
+
 /* The typical workload of the driver:
    Handle the network interface interrupts. */
-static void
-net_interrupt(int irq, void *dev_id, struct pt_regs * regs)
+static void atp_interrupt(int irq, void *dev_instance, struct pt_regs * regs)
 {
-       struct net_device *dev = dev_id;
+       struct net_device *dev = (struct net_device *)dev_instance;
        struct net_local *lp;
-       int ioaddr, status, boguscount = 20;
+       long ioaddr;
        static int num_tx_since_rx = 0;
+       int boguscount = max_interrupt_work;
 
+       if (dev == NULL) {
+               printk(KERN_ERR "ATP_interrupt(): irq %d for unknown device.\n", irq);
+               return;
+       }
        ioaddr = dev->base_addr;
        lp = (struct net_local *)dev->priv;
-       
+
        spin_lock(&lp->lock);
 
        /* Disable additional spurious interrupts. */
@@ -489,14 +605,10 @@ net_interrupt(int irq, void *dev_id, struct pt_regs * regs)
        write_reg(ioaddr, CMR2, CMR2_NULL);
        write_reg(ioaddr, IMR, 0);
 
-       if (net_debug > 5)
-               printk("%s: In interrupt ", dev->name);
-
-       while (--boguscount > 0) 
-       {
-               status = read_nibble(ioaddr, ISR);
-               if (net_debug > 5)
-                       printk("loop status %02x..", status);
+       if (net_debug > 5) printk(KERN_DEBUG "%s: In interrupt ", dev->name);
+    while (--boguscount > 0) {
+               int status = read_nibble(ioaddr, ISR);
+               if (net_debug > 5) printk("loop status %02x..", status);
 
                if (status & (ISR_RxOK<<3)) {
                        write_reg(ioaddr, ISR, ISR_RxOK); /* Clear the Rx interrupt. */
@@ -534,8 +646,7 @@ net_interrupt(int irq, void *dev_id, struct pt_regs * regs)
                                        break;
                                }
                                /* Attempt to retransmit. */
-                               if (net_debug > 6)
-                                       printk("attempting to ReTx");
+                               if (net_debug > 6)  printk("attempting to ReTx");
                                write_reg(ioaddr, CMR1, CMR1_ReXmit + CMR1_Xmit);
                        } else {
                                /* Finish up the transmit. */
@@ -551,10 +662,10 @@ net_interrupt(int irq, void *dev_id, struct pt_regs * regs)
                        }
                        num_tx_since_rx++;
                } else if (num_tx_since_rx > 8
-                                  && jiffies - dev->last_rx > 100) {
+                                  && jiffies > dev->last_rx + HZ) {
                        if (net_debug > 2)
-                               printk("%s: Missed packet? No Rx after %d Tx and %ld jiffies"
-                                          " status %02x  CMR1 %02x.\n", dev->name,
+                               printk(KERN_DEBUG "%s: Missed packet? No Rx after %d Tx and "
+                                          "%ld jiffies status %02x  CMR1 %02x.\n", dev->name,
                                           num_tx_since_rx, jiffies - dev->last_rx, status,
                                           (read_nibble(ioaddr, CMR1) >> 3) & 15);
                        lp->stats.rx_missed_errors++;
@@ -563,7 +674,7 @@ net_interrupt(int irq, void *dev_id, struct pt_regs * regs)
                        break;
                } else
                        break;
-       }
+    }
 
        /* This following code fixes a rare (and very difficult to track down)
           problem where the adapter forgets its ethernet address. */
@@ -571,40 +682,62 @@ net_interrupt(int irq, void *dev_id, struct pt_regs * regs)
                int i;
                for (i = 0; i < 6; i++)
                        write_reg_byte(ioaddr, PAR0 + i, dev->dev_addr[i]);
-#ifdef TIMED_CHECKER
-               mod_timer(&atp_timer, jiffies+TIMED_CHECKER);
+#if 0 && defined(TIMED_CHECKER)
+               mod_timer(&lp->timer, RUN_AT(TIMED_CHECKER));
 #endif
        }
 
        /* Tell the adapter that it can go back to using the output line as IRQ. */
-       write_reg(ioaddr, CMR2, CMR2_IRQOUT);
+    write_reg(ioaddr, CMR2, CMR2_IRQOUT);
        /* Enable the physical interrupt line, which is sure to be low until.. */
        outb(Ctrl_SelData + Ctrl_IRQEN, ioaddr + PAR_CONTROL);
        /* .. we enable the interrupt sources. */
        write_reg(ioaddr, IMR, ISR_RxOK | ISR_TxErr | ISR_TxOK);
        write_reg_high(ioaddr, IMR, ISRh_RxErr);                        /* Hmmm, really needed? */
 
-       if (net_debug > 5)
-               printk("exiting interrupt.\n");
-               
        spin_unlock(&lp->lock);
+
+       if (net_debug > 5) printk("exiting interrupt.\n");
        return;
 }
 
 #ifdef TIMED_CHECKER
 /* This following code fixes a rare (and very difficult to track down)
    problem where the adapter forgets its ethernet address. */
-static void atp_timed_checker(unsigned long ignored)
+static void atp_timed_checker(unsigned long data)
 {
+       struct net_device *dev = (struct net_device *)data;
+       long ioaddr = dev->base_addr;
+       struct net_local *lp = (struct net_local *)dev->priv;
+       int tickssofar = jiffies - lp->last_rx_time;
        int i;
-       struct net_local *lp = (struct net_local *)atp_timed_dev->priv;
-       int ioaddr = atp_timed_dev->base_addr;
 
        spin_lock(&lp->lock);
-       for (i = 0; i < 6; i++)
-               write_reg_byte(ioaddr, PAR0 + i, atp_timed_dev->dev_addr[i]);
+       if (tickssofar > 2*HZ) {
+#if 1
+               for (i = 0; i < 6; i++)
+                       write_reg_byte(ioaddr, PAR0 + i, dev->dev_addr[i]);
+               lp->last_rx_time = jiffies;
+#else
+               for (i = 0; i < 6; i++)
+                       if (read_cmd_byte(ioaddr, PAR0 + i) != atp_timed_dev->dev_addr[i])
+                               {
+                       struct net_local *lp = (struct net_local *)atp_timed_dev->priv;
+                       write_reg_byte(ioaddr, PAR0 + i, atp_timed_dev->dev_addr[i]);
+                       if (i == 2)
+                         lp->stats.tx_errors++;
+                       else if (i == 3)
+                         lp->stats.tx_dropped++;
+                       else if (i == 4)
+                         lp->stats.collisions++;
+                       else
+                         lp->stats.rx_errors++;
+                 }
+#endif
+       }
        spin_unlock(&lp->lock);
-       mod_timer(&atp_timer, jiffies+TIMED_CHECKER);
+       lp->timer.expires = RUN_AT(TIMED_CHECKER);
+       add_timer(&lp->timer);
 }
 #endif
 
@@ -612,61 +745,57 @@ static void atp_timed_checker(unsigned long ignored)
 static void net_rx(struct net_device *dev)
 {
        struct net_local *lp = (struct net_local *)dev->priv;
-       int ioaddr = dev->base_addr;
-#ifdef notdef
-       ushort header[4];
-#else
+       long ioaddr = dev->base_addr;
        struct rx_header rx_head;
-#endif
 
        /* Process the received packet. */
        outb(EOC+MAR, ioaddr + PAR_DATA);
        read_block(ioaddr, 8, (unsigned char*)&rx_head, dev->if_port);
        if (net_debug > 5)
-               printk(" rx_count %04x %04x %04x %04x..", rx_head.pad,
+               printk(KERN_DEBUG " rx_count %04x %04x %04x %04x..", rx_head.pad,
                           rx_head.rx_count, rx_head.rx_status, rx_head.cur_addr);
        if ((rx_head.rx_status & 0x77) != 0x01) {
                lp->stats.rx_errors++;
-               /* Ackkk!  I don't have any documentation on what the error bits mean!
-                  The best I can do is slap the device around a bit. */
-               if (net_debug > 3) printk("%s: Unknown ATP Rx error %04x.\n",
-                                                                 dev->name, rx_head.rx_status);
-               hardware_init(dev);
+               if (rx_head.rx_status & 0x0004) lp->stats.rx_frame_errors++;
+               else if (rx_head.rx_status & 0x0002) lp->stats.rx_crc_errors++;
+               if (net_debug > 3)
+                       printk(KERN_DEBUG "%s: Unknown ATP Rx error %04x.\n",
+                                  dev->name, rx_head.rx_status);
+               if  (rx_head.rx_status & 0x0020) {
+                       lp->stats.rx_fifo_errors++;
+                       write_reg_high(ioaddr, CMR1, CMR1h_TxENABLE);
+                       write_reg_high(ioaddr, CMR1, CMR1h_RxENABLE | CMR1h_TxENABLE);
+               } else if (rx_head.rx_status & 0x0050)
+                       hardware_init(dev);
                return;
        } else {
-               /* Malloc up new buffer. */
-               int pkt_len = (rx_head.rx_count & 0x7ff) - 4;           /* The "-4" is omits the FCS (CRC). */
+               /* Malloc up new buffer. The "-4" omits the FCS (CRC). */
+               int pkt_len = (rx_head.rx_count & 0x7ff) - 4;
                struct sk_buff *skb;
-               
-               skb = dev_alloc_skb(pkt_len);
+
+               skb = dev_alloc_skb(pkt_len + 2);
                if (skb == NULL) {
-                       printk("%s: Memory squeeze, dropping packet.\n", dev->name);
+                       printk(KERN_ERR "%s: Memory squeeze, dropping packet.\n",
+                                  dev->name);
                        lp->stats.rx_dropped++;
                        goto done;
                }
                skb->dev = dev;
-               
-               read_block(ioaddr, pkt_len, skb_put(skb,pkt_len), dev->if_port);
 
-               if (net_debug > 6) {
-                       unsigned char *data = skb->data;
-                       printk(" data %02x%02x%02x %02x%02x%02x %02x%02x%02x"
-                                  "%02x%02x%02x %02x%02x..",
-                                  data[0], data[1], data[2], data[3], data[4], data[5],
-                                  data[6], data[7], data[8], data[9], data[10], data[11],
-                                  data[12], data[13]);
-               }
-               
-               skb->protocol=eth_type_trans(skb,dev);
+               skb_reserve(skb, 2);    /* Align IP on 16 byte boundaries */
+               read_block(ioaddr, pkt_len, skb_put(skb,pkt_len), dev->if_port);
+               skb->protocol = eth_type_trans(skb, dev);
                netif_rx(skb);
                lp->stats.rx_packets++;
+               lp->stats.rx_bytes += pkt_len;
        }
  done:
        write_reg(ioaddr, CMR1, CMR1_NextPkt);
+       lp->last_rx_time = jiffies;
        return;
 }
 
-static void read_block(short ioaddr, int length, unsigned char *p, int data_mode)
+static void read_block(long ioaddr, int length, unsigned char *p, int data_mode)
 {
 
        if (data_mode <= 3) { /* Mode 0 or 1 */
@@ -682,18 +811,21 @@ static void read_block(short ioaddr, int length, unsigned char *p, int data_mode
        else
                do      *p++ = read_byte_mode6(ioaddr);  while (--length > 0);
 
-       outb(EOC+HNib+MAR, ioaddr + PAR_DATA);
+    outb(EOC+HNib+MAR, ioaddr + PAR_DATA);
        outb(Ctrl_SelData, ioaddr + PAR_CONTROL);
 }
 
 /* The inverse routine to net_open(). */
-static int net_close(struct net_device *dev)
+static int
+net_close(struct net_device *dev)
 {
        struct net_local *lp = (struct net_local *)dev->priv;
-       int ioaddr = dev->base_addr;
+       long ioaddr = dev->base_addr;
 
        netif_stop_queue(dev);
 
+       del_timer_sync(&lp->timer);
+
        /* Flush the Tx and disable Rx here. */
        lp->addr_mode = CMR2h_OFF;
        write_reg_high(ioaddr, CMR2, CMR2h_OFF);
@@ -702,8 +834,8 @@ static int net_close(struct net_device *dev)
        outb(0x00, ioaddr + PAR_CONTROL);
        free_irq(dev->irq, dev);
 
-       /* Leave the hardware in a reset state. */
-       write_reg_high(ioaddr, CMR1, CMR1h_RESET);
+       /* Reset the ethernet hardware and activate the printer pass-through. */
+    write_reg_high(ioaddr, CMR1, CMR1h_RESET | CMR1h_MUX);
 
        MOD_DEC_USE_COUNT;
 
@@ -712,7 +844,8 @@ static int net_close(struct net_device *dev)
 
 /* Get the current statistics. This may be called with the card open or
    closed. */
-static struct net_device_stats *net_get_stats(struct net_device *dev)
+static struct net_device_stats *
+net_get_stats(struct net_device *dev)
 {
        struct net_local *lp = (struct net_local *)dev->priv;
        return &lp->stats;
@@ -721,46 +854,101 @@ static struct net_device_stats *net_get_stats(struct net_device *dev)
 /*
  *     Set or clear the multicast filter for this adapter.
  */
-static void set_multicast_list(struct net_device *dev)
+
+/* The little-endian AUTODIN32 ethernet CRC calculation.
+   This is common code and should be moved to net/core/crc.c */
+static unsigned const ethernet_polynomial_le = 0xedb88320U;
+static inline unsigned ether_crc_le(int length, unsigned char *data)
+{
+    unsigned int crc = 0xffffffff;     /* Initial value. */
+    while(--length >= 0) {
+               unsigned char current_octet = *data++;
+               int bit;
+               for (bit = 8; --bit >= 0; current_octet >>= 1) {
+                       if ((crc ^ current_octet) & 1) {
+                               crc >>= 1;
+                               crc ^= ethernet_polynomial_le;
+                       } else
+                               crc >>= 1;
+               }
+    }
+    return crc;
+}
+
+static void set_rx_mode_8002(struct net_device *dev)
 {
        struct net_local *lp = (struct net_local *)dev->priv;
-       short ioaddr = dev->base_addr;
-       int num_addrs=dev->mc_count;
-       
-       if(dev->flags&(IFF_ALLMULTI|IFF_PROMISC))
-               num_addrs=1;
-       /*
-        *      We must make the kernel realise we had to move
-        *      into promisc mode or we start all out war on
-        *      the cable. - AC
-        */
-       if(num_addrs)
-               dev->flags|=IFF_PROMISC;                
-       lp->addr_mode = num_addrs ? CMR2h_PROMISC : CMR2h_Normal;
+       long ioaddr = dev->base_addr;
+
+       if ( dev->mc_count > 0 || (dev->flags & (IFF_ALLMULTI|IFF_PROMISC))) {
+               /* We must make the kernel realise we had to move
+                *      into promisc mode or we start all out war on
+                *      the cable. - AC
+                */
+               dev->flags|=IFF_PROMISC;
+               lp->addr_mode = CMR2h_PROMISC;
+       } else
+               lp->addr_mode = CMR2h_Normal;
        write_reg_high(ioaddr, CMR2, lp->addr_mode);
 }
 
-/* module stuff */
-static int io;
-static struct net_device atp_dev = { init: atp_init };
-MODULE_AUTHOR("Donald Becker <becker@scyld.com>");
-MODULE_DESCRIPTION("Realtek 8002/8012 Pocket Lan Adapter");
-MODULE_PARM(io, "I/O port of the pocket adapter");
+static void set_rx_mode_8012(struct net_device *dev)
+{
+       struct net_local *lp = (struct net_local *)dev->priv;
+       long ioaddr = dev->base_addr;
+       unsigned char new_mode, mc_filter[8]; /* Multicast hash filter */
+       int i;
 
-static int __init atp_init_module(void) {
-       atp_dev.base_addr = io;
+       if (dev->flags & IFF_PROMISC) {                 /* Set promiscuous. */
+               new_mode = CMR2h_PROMISC;
+       } else if ((dev->mc_count > 1000)  ||  (dev->flags & IFF_ALLMULTI)) {
+               /* Too many to filter perfectly -- accept all multicasts. */
+               memset(mc_filter, 0xff, sizeof(mc_filter));
+               new_mode = CMR2h_Normal;
+       } else {
+               struct dev_mc_list *mclist;
+
+               memset(mc_filter, 0, sizeof(mc_filter));
+               for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count;
+                        i++, mclist = mclist->next)
+                       set_bit(ether_crc_le(ETH_ALEN, mclist->dmi_addr) & 0x3f,
+                                       mc_filter);
+               new_mode = CMR2h_Normal;
+       }
+       lp->addr_mode = new_mode;
+    write_reg(ioaddr, CMR2, CMR2_IRQOUT | 0x04); /* Switch to page 1. */
+    for (i = 0; i < 8; i++)
+               write_reg_byte(ioaddr, i, mc_filter[i]);
+       if (net_debug > 2 || 1) {
+               lp->addr_mode = 1;
+               printk(KERN_DEBUG "%s: Mode %d, setting multicast filter to",
+                          dev->name, lp->addr_mode);
+               for (i = 0; i < 8; i++)
+                       printk(" %2.2x", mc_filter[i]);
+               printk(".\n");
+       }
 
-       if (register_netdev(&atp_dev) != 0)
-               return -EIO;
+       write_reg_high(ioaddr, CMR2, lp->addr_mode);
+    write_reg(ioaddr, CMR2, CMR2_IRQOUT); /* Switch back to page 0 */
+}
 
-       return 0;
+static int __init atp_init_module(void) {
+       if (debug)                                      /* Emit version even if no cards detected. */
+               printk(KERN_INFO "%s" KERN_INFO "%s", versionA, versionB);
+       return atp_init(NULL);
 }
 
 static void __exit atp_cleanup_module(void) {
-       unregister_netdev(&atp_dev);
+       struct net_device *next_dev;
+
+       while (root_atp_dev) {
+               next_dev = ((struct net_local *)root_atp_dev->priv)->next_module;
+               unregister_netdev(root_atp_dev);
+               /* No need to release_region(), since we never snarf it. */
+               kfree(root_atp_dev);
+               root_atp_dev = next_dev;
+       }
 }
 
 module_init(atp_init_module);
 module_exit(atp_cleanup_module);
-
index 46801e4015cff6f3d1d0bc9d8c034cd4fc9e1ff6..0edc642c2c2f4c1d9a90d94d1db07c1a63c0a1bd 100644 (file)
@@ -1,30 +1,23 @@
+/* Linux header file for the ATP pocket ethernet adapter. */
+/* v1.09 8/9/2000 becker@scyld.com. */
+
 #include <linux/if_ether.h>
 #include <linux/types.h>
-#include <asm/io.h>
-
-struct net_local 
-{
-       struct net_device_stats stats;
-       ushort saved_tx_size;
-       unsigned char
-       re_tx,                  /* Number of packet retransmissions. */
-       tx_unit_busy,
-       addr_mode,              /* Current Rx filter e.g. promiscuous, etc. */
-       pac_cnt_in_tx_buf;
-       spinlock_t lock;                /* Safety lock */
-};
 
+/* The header prepended to received packets. */
 struct rx_header {
-       ushort pad;                     /* The first read is always corrupted. */
-       ushort rx_count;
-       ushort rx_status;               /* Unknown bit assignments :-<.  */
-       ushort cur_addr;                /* Apparently the current buffer address(?) */
+    ushort pad;                        /* Pad. */
+    ushort rx_count;
+    ushort rx_status;          /* Unknown bit assignments :-<.  */
+    ushort cur_addr;           /* Apparently the current buffer address(?) */
 };
 
 #define PAR_DATA       0
 #define PAR_STATUS     1
 #define PAR_CONTROL 2
 
+enum chip_type { RTL8002, RTL8012 };
+
 #define Ctrl_LNibRead  0x08    /* LP_PSELECP */
 #define Ctrl_HNibRead  0
 #define Ctrl_LNibWrite 0x08    /* LP_PSELECP */
@@ -40,18 +33,19 @@ struct rx_header {
 
 enum page0_regs
 {
-       /* The first six registers hold the ethernet physical station address. */
-       PAR0 = 0, PAR1 = 1, PAR2 = 2, PAR3 = 3, PAR4 = 4, PAR5 = 5,
-       TxCNT0 = 6, TxCNT1 = 7,         /* The transmit byte count. */
-       TxSTAT = 8, RxSTAT = 9,         /* Tx and Rx status. */
-       ISR = 10, IMR = 11,             /* Interrupt status and mask. */
-       CMR1 = 12,                      /* Command register 1. */
-       CMR2 = 13,                      /* Command register 2. */
-       MAR = 14,                       /* Memory address register. */
-       CMR2_h = 0x1d, 
-};
+    /* The first six registers hold the ethernet physical station address. */
+    PAR0 = 0, PAR1 = 1, PAR2 = 2, PAR3 = 3, PAR4 = 4, PAR5 = 5,
+    TxCNT0 = 6, TxCNT1 = 7,            /* The transmit byte count. */
+    TxSTAT = 8, RxSTAT = 9,            /* Tx and Rx status. */
+    ISR = 10, IMR = 11,                        /* Interrupt status and mask. */
+    CMR1 = 12,                         /* Command register 1. */
+    CMR2 = 13,                         /* Command register 2. */
+    MODSEL = 14,                       /* Mode select register. */
+    MAR = 14,                          /* Memory address register (?). */
+    CMR2_h = 0x1d, };
 
-enum eepage_regs { PROM_CMD = 6, PROM_DATA = 7 };      /* Note that PROM_CMD is in the "high" bits. */
+enum eepage_regs
+{ PROM_CMD = 6, PROM_DATA = 7 };       /* Note that PROM_CMD is in the "high" bits. */
 
 
 #define ISR_TxOK       0x01
@@ -59,6 +53,7 @@ enum eepage_regs { PROM_CMD = 6, PROM_DATA = 7 };     /* Note that PROM_CMD is in th
 #define ISR_TxErr      0x02
 #define ISRh_RxErr     0x11    /* ISR, high nibble */
 
+#define CMR1h_MUX      0x08    /* Select printer multiplexor on 8012. */
 #define CMR1h_RESET    0x04    /* Reset. */
 #define CMR1h_RxENABLE 0x02    /* Rx unit enable.  */
 #define CMR1h_TxENABLE 0x01    /* Tx unit enable.  */
@@ -81,139 +76,135 @@ enum eepage_regs { PROM_CMD = 6, PROM_DATA = 7 }; /* Note that PROM_CMD is in th
 
 /* An inline function used below: it differs from inb() by explicitly return an unsigned
    char, saving a truncation. */
-   
-extern inline unsigned char inbyte(unsigned short port)
+static inline unsigned char inbyte(unsigned short port)
 {
-       unsigned char _v;
-       __asm__ __volatile__ ("inb %w1,%b0" :"=a" (_v):"d" (port));
-       return _v;
+    unsigned char _v;
+    __asm__ __volatile__ ("inb %w1,%b0" :"=a" (_v):"d" (port));
+    return _v;
 }
 
 /* Read register OFFSET.
    This command should always be terminated with read_end(). */
-
-extern inline unsigned char read_nibble(short port, unsigned char offset)
+static inline unsigned char read_nibble(short port, unsigned char offset)
 {
-       unsigned char retval;
-       outb(EOC+offset, port + PAR_DATA);
-       outb(RdAddr+offset, port + PAR_DATA);
-       inbyte(port + PAR_STATUS);              /* Settling time delay */
-       retval = inbyte(port + PAR_STATUS);
-       outb(EOC+offset, port + PAR_DATA);
-
-       return retval;
+    unsigned char retval;
+    outb(EOC+offset, port + PAR_DATA);
+    outb(RdAddr+offset, port + PAR_DATA);
+    inbyte(port + PAR_STATUS);         /* Settling time delay */
+    retval = inbyte(port + PAR_STATUS);
+    outb(EOC+offset, port + PAR_DATA);
+
+    return retval;
 }
 
 /* Functions for bulk data read.  The interrupt line is always disabled. */
 /* Get a byte using read mode 0, reading data from the control lines. */
-
-extern inline unsigned char read_byte_mode0(short ioaddr)
+static inline unsigned char read_byte_mode0(short ioaddr)
 {
-       unsigned char low_nib;
-
-       outb(Ctrl_LNibRead, ioaddr + PAR_CONTROL);
-       inbyte(ioaddr + PAR_STATUS);
-       low_nib = (inbyte(ioaddr + PAR_STATUS) >> 3) & 0x0f;
-       outb(Ctrl_HNibRead, ioaddr + PAR_CONTROL);
-       inbyte(ioaddr + PAR_STATUS);    /* Settling time delay -- needed!  */
-       inbyte(ioaddr + PAR_STATUS);    /* Settling time delay -- needed!  */
-       return low_nib | ((inbyte(ioaddr + PAR_STATUS) << 1) & 0xf0);
+    unsigned char low_nib;
+
+    outb(Ctrl_LNibRead, ioaddr + PAR_CONTROL);
+    inbyte(ioaddr + PAR_STATUS);
+    low_nib = (inbyte(ioaddr + PAR_STATUS) >> 3) & 0x0f;
+    outb(Ctrl_HNibRead, ioaddr + PAR_CONTROL);
+    inbyte(ioaddr + PAR_STATUS);       /* Settling time delay -- needed!  */
+    inbyte(ioaddr + PAR_STATUS);       /* Settling time delay -- needed!  */
+    return low_nib | ((inbyte(ioaddr + PAR_STATUS) << 1) & 0xf0);
 }
 
 /* The same as read_byte_mode0(), but does multiple inb()s for stability. */
-
-extern inline unsigned char read_byte_mode2(short ioaddr)
+static inline unsigned char read_byte_mode2(short ioaddr)
 {
-       unsigned char low_nib;
-
-       outb(Ctrl_LNibRead, ioaddr + PAR_CONTROL);
-       inbyte(ioaddr + PAR_STATUS);
-       low_nib = (inbyte(ioaddr + PAR_STATUS) >> 3) & 0x0f;
-       outb(Ctrl_HNibRead, ioaddr + PAR_CONTROL);
-       inbyte(ioaddr + PAR_STATUS);    /* Settling time delay -- needed!  */
-       return low_nib | ((inbyte(ioaddr + PAR_STATUS) << 1) & 0xf0);
+    unsigned char low_nib;
+
+    outb(Ctrl_LNibRead, ioaddr + PAR_CONTROL);
+    inbyte(ioaddr + PAR_STATUS);
+    low_nib = (inbyte(ioaddr + PAR_STATUS) >> 3) & 0x0f;
+    outb(Ctrl_HNibRead, ioaddr + PAR_CONTROL);
+    inbyte(ioaddr + PAR_STATUS);       /* Settling time delay -- needed!  */
+    return low_nib | ((inbyte(ioaddr + PAR_STATUS) << 1) & 0xf0);
 }
 
 /* Read a byte through the data register. */
-
-extern inline unsigned char read_byte_mode4(short ioaddr)
+static inline unsigned char read_byte_mode4(short ioaddr)
 {
-       unsigned char low_nib;
+    unsigned char low_nib;
 
-       outb(RdAddr | MAR, ioaddr + PAR_DATA);
-       low_nib = (inbyte(ioaddr + PAR_STATUS) >> 3) & 0x0f;
-       outb(RdAddr | HNib | MAR, ioaddr + PAR_DATA);
-       return low_nib | ((inbyte(ioaddr + PAR_STATUS) << 1) & 0xf0);
+    outb(RdAddr | MAR, ioaddr + PAR_DATA);
+    low_nib = (inbyte(ioaddr + PAR_STATUS) >> 3) & 0x0f;
+    outb(RdAddr | HNib | MAR, ioaddr + PAR_DATA);
+    return low_nib | ((inbyte(ioaddr + PAR_STATUS) << 1) & 0xf0);
 }
 
 /* Read a byte through the data register, double reading to allow settling. */
-
-extern inline unsigned char read_byte_mode6(short ioaddr)
+static inline unsigned char read_byte_mode6(short ioaddr)
 {
-       unsigned char low_nib;
-
-       outb(RdAddr | MAR, ioaddr + PAR_DATA);
-       inbyte(ioaddr + PAR_STATUS);
-       low_nib = (inbyte(ioaddr + PAR_STATUS) >> 3) & 0x0f;
-       outb(RdAddr | HNib | MAR, ioaddr + PAR_DATA);
-       inbyte(ioaddr + PAR_STATUS);
-       return low_nib | ((inbyte(ioaddr + PAR_STATUS) << 1) & 0xf0);
+    unsigned char low_nib;
+
+    outb(RdAddr | MAR, ioaddr + PAR_DATA);
+    inbyte(ioaddr + PAR_STATUS);
+    low_nib = (inbyte(ioaddr + PAR_STATUS) >> 3) & 0x0f;
+    outb(RdAddr | HNib | MAR, ioaddr + PAR_DATA);
+    inbyte(ioaddr + PAR_STATUS);
+    return low_nib | ((inbyte(ioaddr + PAR_STATUS) << 1) & 0xf0);
 }
 
-extern inline void write_reg(short port, unsigned char reg, unsigned char value)
+static inline void
+write_reg(short port, unsigned char reg, unsigned char value)
 {
-       unsigned char outval;
-       outb(EOC | reg, port + PAR_DATA);
-       outval = WrAddr | reg;
-       outb(outval, port + PAR_DATA);
-       outb(outval, port + PAR_DATA);  /* Double write for PS/2. */
-
-       outval &= 0xf0;
-       outval |= value;
-       outb(outval, port + PAR_DATA);
-       outval &= 0x1f;
-       outb(outval, port + PAR_DATA);
-       outb(outval, port + PAR_DATA);
-
-       outb(EOC | outval, port + PAR_DATA);
+    unsigned char outval;
+    outb(EOC | reg, port + PAR_DATA);
+    outval = WrAddr | reg;
+    outb(outval, port + PAR_DATA);
+    outb(outval, port + PAR_DATA);     /* Double write for PS/2. */
+
+    outval &= 0xf0;
+    outval |= value;
+    outb(outval, port + PAR_DATA);
+    outval &= 0x1f;
+    outb(outval, port + PAR_DATA);
+    outb(outval, port + PAR_DATA);
+
+    outb(EOC | outval, port + PAR_DATA);
 }
 
-extern inline void write_reg_high(short port, unsigned char reg, unsigned char value)
+static inline void
+write_reg_high(short port, unsigned char reg, unsigned char value)
 {
-       unsigned char outval = EOC | HNib | reg;
+    unsigned char outval = EOC | HNib | reg;
 
-       outb(outval, port + PAR_DATA);
-       outval &= WrAddr | HNib | 0x0f;
-       outb(outval, port + PAR_DATA);
-       outb(outval, port + PAR_DATA);  /* Double write for PS/2. */
+    outb(outval, port + PAR_DATA);
+    outval &= WrAddr | HNib | 0x0f;
+    outb(outval, port + PAR_DATA);
+    outb(outval, port + PAR_DATA);     /* Double write for PS/2. */
 
-       outval = WrAddr | HNib | value;
-       outb(outval, port + PAR_DATA);
-       outval &= HNib | 0x0f;          /* HNib | value */
-       outb(outval, port + PAR_DATA);
-       outb(outval, port + PAR_DATA);
+    outval = WrAddr | HNib | value;
+    outb(outval, port + PAR_DATA);
+    outval &= HNib | 0x0f;             /* HNib | value */
+    outb(outval, port + PAR_DATA);
+    outb(outval, port + PAR_DATA);
 
-       outb(EOC | HNib | outval, port + PAR_DATA);
+    outb(EOC | HNib | outval, port + PAR_DATA);
 }
 
 /* Write a byte out using nibble mode.  The low nibble is written first. */
-
-extern inline void write_reg_byte(short port, unsigned char reg, unsigned char value)
+static inline void
+write_reg_byte(short port, unsigned char reg, unsigned char value)
 {
-       unsigned char outval;
-       outb(EOC | reg, port + PAR_DATA);       /* Reset the address register. */
-       outval = WrAddr | reg;
-       outb(outval, port + PAR_DATA);
-       outb(outval, port + PAR_DATA);          /* Double write for PS/2. */
-
-       outb((outval & 0xf0) | (value & 0x0f), port + PAR_DATA);
-       outb(value & 0x0f, port + PAR_DATA);
-       value >>= 4;
-       outb(value, port + PAR_DATA);
-       outb(0x10 | value, port + PAR_DATA);
-       outb(0x10 | value, port + PAR_DATA);
-
-       outb(EOC  | value, port + PAR_DATA);    /* Reset the address register. */
+    unsigned char outval;
+    outb(EOC | reg, port + PAR_DATA);  /* Reset the address register. */
+    outval = WrAddr | reg;
+    outb(outval, port + PAR_DATA);
+    outb(outval, port + PAR_DATA);     /* Double write for PS/2. */
+
+    outb((outval & 0xf0) | (value & 0x0f), port + PAR_DATA);
+    outb(value & 0x0f, port + PAR_DATA);
+    value >>= 4;
+    outb(value, port + PAR_DATA);
+    outb(0x10 | value, port + PAR_DATA);
+    outb(0x10 | value, port + PAR_DATA);
+
+    outb(EOC  | value, port + PAR_DATA);       /* Reset the address register. */
 }
 
 /*
@@ -223,32 +214,30 @@ extern inline void write_reg_byte(short port, unsigned char reg, unsigned char v
  * It should only be needed when there is skew between the individual data
  * lines.
  */
-
-extern inline void write_byte_mode0(short ioaddr, unsigned char value)
+static inline void write_byte_mode0(short ioaddr, unsigned char value)
 {
-       outb(value & 0x0f, ioaddr + PAR_DATA);
-       outb((value>>4) | 0x10, ioaddr + PAR_DATA);
+    outb(value & 0x0f, ioaddr + PAR_DATA);
+    outb((value>>4) | 0x10, ioaddr + PAR_DATA);
 }
 
-extern inline void write_byte_mode1(short ioaddr, unsigned char value)
+static inline void write_byte_mode1(short ioaddr, unsigned char value)
 {
-       outb(value & 0x0f, ioaddr + PAR_DATA);
-       outb(Ctrl_IRQEN | Ctrl_LNibWrite, ioaddr + PAR_CONTROL);
-       outb((value>>4) | 0x10, ioaddr + PAR_DATA);
-       outb(Ctrl_IRQEN | Ctrl_HNibWrite, ioaddr + PAR_CONTROL);
+    outb(value & 0x0f, ioaddr + PAR_DATA);
+    outb(Ctrl_IRQEN | Ctrl_LNibWrite, ioaddr + PAR_CONTROL);
+    outb((value>>4) | 0x10, ioaddr + PAR_DATA);
+    outb(Ctrl_IRQEN | Ctrl_HNibWrite, ioaddr + PAR_CONTROL);
 }
 
 /* Write 16bit VALUE to the packet buffer: the same as above just doubled. */
-
-extern inline void write_word_mode0(short ioaddr, unsigned short value)
+static inline void write_word_mode0(short ioaddr, unsigned short value)
 {
-       outb(value & 0x0f, ioaddr + PAR_DATA);
-       value >>= 4;
-       outb((value & 0x0f) | 0x10, ioaddr + PAR_DATA);
-       value >>= 4;
-       outb(value & 0x0f, ioaddr + PAR_DATA);
-       value >>= 4;
-       outb((value & 0x0f) | 0x10, ioaddr + PAR_DATA);
+    outb(value & 0x0f, ioaddr + PAR_DATA);
+    value >>= 4;
+    outb((value & 0x0f) | 0x10, ioaddr + PAR_DATA);
+    value >>= 4;
+    outb(value & 0x0f, ioaddr + PAR_DATA);
+    value >>= 4;
+    outb((value & 0x0f) | 0x10, ioaddr + PAR_DATA);
 }
 
 /*  EEPROM_Ctrl bits. */
@@ -259,6 +248,10 @@ extern inline void write_word_mode0(short ioaddr, unsigned short value)
 #define EE_DATA_WRITE  0x01    /* EEPROM chip data in. */
 #define EE_DATA_READ   0x08    /* EEPROM chip data out. */
 
+/* Delay between EEPROM clock transitions. */
+#define eeprom_delay(ticks) \
+do { int _i = 40; while (--_i > 0) { __SLOW_DOWN_IO; }} while (0)
+
 /* The EEPROM commands include the alway-set leading bit. */
 #define EE_WRITE_CMD(offset)   (((5 << 6) + (offset)) << 17)
 #define EE_READ(offset)        (((6 << 6) + (offset)) << 17)
index 0b9d33e1081f834869abb03475a849f59639d6b5..5bc9797a180603472d4421fa2bd3564e8ea499f4 100644 (file)
  * 
  */
 
-#include <linux/kernel.h>
 #include <linux/module.h>
-#include <linux/sched.h>
-#include <linux/types.h>
-#include <linux/fcntl.h>
-#include <linux/interrupt.h>
-#include <linux/ptrace.h>
-#include <linux/ioport.h>
-#include <linux/in.h>
-#include <linux/malloc.h>
-#include <linux/string.h>
-#include <linux/init.h>
-#include <asm/system.h>
-#include <asm/bitops.h>
-#include <asm/io.h>
-#include <asm/dma.h>
-#include <asm/uaccess.h>
-#include <linux/errno.h>
-
+#include <linux/kernel.h>
 #include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/skbuff.h>
-
+#include <linux/init.h>
 #include <linux/if_bonding.h>
 
 typedef struct slave
@@ -228,13 +209,13 @@ static int bond_event(struct notifier_block *this, unsigned long event, void *pt
        return NOTIFY_DONE;
 }
 
-struct notifier_block bond_netdev_notifier={
+static struct notifier_block bond_netdev_notifier={
        bond_event,
        NULL,
        0
 };
 
-int __init bond_init(struct net_device *dev)
+static int __init bond_init(struct net_device *dev)
 {
        bonding_t *bond;
 
@@ -308,15 +289,13 @@ static struct net_device_stats *bond_get_stats(struct net_device *dev)
        return &bond->stats;
 }
 
-#ifdef MODULE
-
 static struct net_device dev_bond = {
                "",
                0, 0, 0, 0,
                0x0, 0,
                0, 0, 0, NULL, bond_init };
 
-int init_module(void)
+static int __init bonding_init(void)
 {
        /* Find a name for this unit */
        int err=dev_alloc_name(&dev_bond,"bond%d");
@@ -330,7 +309,7 @@ int init_module(void)
        return 0;
 }
 
-void cleanup_module(void)
+static void __exit bonding_exit(void)
 {
        unregister_netdevice_notifier(&bond_netdev_notifier);
 
@@ -338,7 +317,9 @@ void cleanup_module(void)
 
        kfree(dev_bond.priv);
 }
-#endif /* MODULE */
+
+module_init(bonding_init);
+module_exit(bonding_exit);
 
 /*
  * Local variables:
index ccb0d42de0ac19ecae513bc2818da6e919ae88e8..bf3a376a2d8a1c3670b922686f20eb7db55af7fd 100644 (file)
 /* To have statistics (just packets sent) define this */
 
 #include <linux/config.h>
-#include <linux/kernel.h>
 #include <linux/module.h>
-#include <linux/sched.h>
-#include <linux/types.h>
-#include <linux/fcntl.h>
-#include <linux/interrupt.h>
-#include <linux/ptrace.h>
-#include <linux/ioport.h>
-#include <linux/in.h>
-#include <linux/malloc.h>
-#include <linux/string.h>
-#include <linux/init.h>
-#include <asm/system.h>
-#include <asm/bitops.h>
-#include <asm/io.h>
-#include <asm/dma.h>
-#include <linux/errno.h>
-
+#include <linux/kernel.h>
 #include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/skbuff.h>
+#include <linux/init.h>
 
 static int dummy_xmit(struct sk_buff *skb, struct net_device *dev);
 static struct net_device_stats *dummy_get_stats(struct net_device *dev);
@@ -80,7 +63,7 @@ static int dummy_accept_fastpath(struct net_device *dev, struct dst_entry *dst)
 }
 #endif
 
-int __init dummy_init(struct net_device *dev)
+static int __init dummy_init(struct net_device *dev)
 {
        /* Initialize the device structure. */
        dev->hard_start_xmit    = dummy_xmit;
@@ -121,25 +104,12 @@ static int dummy_xmit(struct sk_buff *skb, struct net_device *dev)
 
 static struct net_device_stats *dummy_get_stats(struct net_device *dev)
 {
-       struct net_device_stats *stats = (struct net_device_stats *) dev->priv;
-       return stats;
-}
-
-#ifdef MODULE
-
-static int __init dummy_probe(struct net_device *dev)
-{
-       dummy_init(dev);
-       return 0;
+       return dev->priv;
 }
 
-static struct net_device dev_dummy = {
-               "",
-               0, 0, 0, 0,
-               0x0, 0,
-               0, 0, 0, NULL, dummy_probe };
+static struct net_device dev_dummy = { init: dummy_init };
 
-int init_module(void)
+static int __init dummy_init_module(void)
 {
        /* Find a name for this unit */
        int err=dev_alloc_name(&dev_dummy,"dummy%d");
@@ -150,10 +120,14 @@ int init_module(void)
        return 0;
 }
 
-void cleanup_module(void)
+static void __exit dummy_cleanup_module(void)
 {
        unregister_netdev(&dev_dummy);
        kfree(dev_dummy.priv);
-       dev_dummy.priv = NULL;
+
+       memset(&dev_dummy, 0, sizeof(dev_dummy));
+       dev_dummy.init = dummy_init;
 }
-#endif /* MODULE */
+
+module_init(dummy_init_module);
+module_exit(dummy_cleanup_module);
index 485852c0edcaaad1b3791afabefa279c0aa3e4ef..af419c80e2148ad3196ea04d62bbc5c1ea337d84 100644 (file)
@@ -2273,6 +2273,8 @@ static struct pci_device_id eepro100_pci_tbl[] __devinitdata = {
                PCI_ANY_ID, PCI_ANY_ID, },
        { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ID1030,
                PCI_ANY_ID, PCI_ANY_ID, },
+       { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82820FW_4,
+               PCI_ANY_ID, PCI_ANY_ID, },
        { 0,}
 };
 MODULE_DEVICE_TABLE(pci, eepro100_pci_tbl);
index e9f0254a7c1a750f0e352e543b0bca46166e5f4f..112e0afbab4c3eca9418d85b1634e1f1911a01ec 100644 (file)
@@ -113,32 +113,18 @@ static const char *version =
  */
 
 #include <linux/module.h>
-
 #include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/types.h>
-#include <linux/fcntl.h>
-#include <linux/interrupt.h>
-#include <linux/ptrace.h>
-#include <linux/ioport.h>
-#include <linux/in.h>
-#include <linux/malloc.h>
-#include <linux/string.h>
-#include <asm/system.h>
-#include <asm/bitops.h>
-#include <asm/io.h>
-#include <asm/dma.h>
-#include <asm/uaccess.h>
-#include <linux/errno.h>              
 #include <linux/init.h>
-
+#include <linux/timer.h>
 #include <linux/netdevice.h>
+
 #include <linux/if.h>
 #include <linux/if_arp.h>
-#include <linux/timer.h>
-
 #include <linux/if_eql.h>
 
+#include <asm/uaccess.h>
+
+
 #ifndef EQL_DEBUG
 /* #undef EQL_DEBUG      -* print nothing at all, not even a boot-banner */
 /* #define EQL_DEBUG 1   -* print only the boot-banner */
@@ -150,7 +136,7 @@ static const char *version =
 #endif
 static unsigned int eql_debug = EQL_DEBUG;
 
-int        eql_init(struct net_device *dev); /*  */
+static int eql_init(struct net_device *dev); /*  */
 static int eql_open(struct net_device *dev); /*  */
 static int eql_close(struct net_device *dev); /*  */
 static int eql_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd); /*  */
@@ -209,7 +195,7 @@ static void eql_timer(unsigned long param); /*  */
    ---------------------------------------------------------
    */
 
-int __init eql_init(struct net_device *dev)
+static int __init eql_init(struct net_device *dev)
 {
        static unsigned version_printed = 0;
        /* static unsigned num_masters     = 0; */
@@ -273,6 +259,8 @@ static int eql_open(struct net_device *dev)
        equalizer_t *eql = (equalizer_t *) dev->priv;
        slave_queue_t *new_queue;
 
+       MOD_INC_USE_COUNT;
+
 #ifdef EQL_DEBUG
        if (eql_debug >= 5)
                printk ("%s: open\n", dev->name);
@@ -294,10 +282,10 @@ static int eql_open(struct net_device *dev)
                eql->timer_on = 1;
                add_timer (&eql->timer);
 
-               MOD_INC_USE_COUNT;
                return 0;
        }
-       return 1;
+       MOD_DEC_USE_COUNT;
+       return -ENOMEM;
 }
 
 
@@ -409,16 +397,14 @@ static int eql_enslave(struct net_device *dev, slaving_request_t *srqp)
        struct net_device *master_dev;
        struct net_device *slave_dev;
        slaving_request_t srq;
-       int err;
 
-       err = copy_from_user(&srq, srqp, sizeof (slaving_request_t));
-       if (err)  
+       if (copy_from_user(&srq, srqp, sizeof (slaving_request_t)))
          {
 #ifdef EQL_DEBUG
        if (eql_debug >= 20)
                printk ("EQL enslave: error detected by copy_from_user\n");
 #endif  
-               return err;
+               return -EFAULT;
          }
 
 #ifdef EQL_DEBUG
@@ -470,11 +456,9 @@ static int eql_emancipate(struct net_device *dev, slaving_request_t *srqp)
        struct net_device *master_dev;
        struct net_device *slave_dev;
        slaving_request_t srq;
-       int err;
 
-       err = copy_from_user(&srq, srqp, sizeof (slaving_request_t));
-       if (err) 
-               return err;
+       if (copy_from_user(&srq, srqp, sizeof (slaving_request_t)))
+               return -EFAULT;
 
 #ifdef EQL_DEBUG
        if (eql_debug >= 20)
@@ -500,11 +484,9 @@ static int eql_g_slave_cfg(struct net_device *dev, slave_config_t *scp)
        equalizer_t *eql;
        struct net_device *slave_dev;
        slave_config_t sc;
-       int err;
 
-       err = copy_from_user (&sc, scp, sizeof (slave_config_t));
-       if (err) 
-               return err;
+       if (copy_from_user (&sc, scp, sizeof (slave_config_t)))
+               return -EFAULT;
 
 #ifdef EQL_DEBUG
        if (eql_debug >= 20)
@@ -519,10 +501,8 @@ static int eql_g_slave_cfg(struct net_device *dev, slave_config_t *scp)
                if (slave != 0)
                {
                        sc.priority = slave->priority;
-                       err = verify_area(VERIFY_WRITE, (void *)scp, sizeof (slave_config_t));
-                       if (err) 
-                               return err;
-                       copy_to_user (scp, &sc, sizeof (slave_config_t));
+                       if (copy_to_user (scp, &sc, sizeof (slave_config_t)))
+                               return -EFAULT;
                        return 0;
                }
        }
@@ -536,11 +516,9 @@ static int eql_s_slave_cfg(struct net_device *dev, slave_config_t *scp)
        equalizer_t *eql;
        struct net_device *slave_dev;
        slave_config_t sc;
-       int err;
 
-       err = copy_from_user (&sc, scp, sizeof (slave_config_t));
-       if (err) 
-               return err;
+       if (copy_from_user (&sc, scp, sizeof (slave_config_t)))
+               return -EFAULT;
 
 #ifdef EQL_DEBUG
        if (eql_debug >= 20)
@@ -578,13 +556,11 @@ static int eql_g_master_cfg(struct net_device *dev, master_config_t *mcp)
 
        if ( eql_is_master (dev) )
        {
-               int err;
                eql = (equalizer_t *) dev->priv;
                mc.max_slaves = eql->max_slaves;
                mc.min_slaves = eql->min_slaves;
-               err = copy_to_user (mcp, &mc, sizeof (master_config_t));
-               if (err) 
-                       return err;
+               if (copy_to_user (mcp, &mc, sizeof (master_config_t)))
+                       return -EFAULT;
                return 0;
        }
        return -EINVAL;
@@ -595,11 +571,9 @@ static int eql_s_master_cfg(struct net_device *dev, master_config_t *mcp)
 {
        equalizer_t *eql;
        master_config_t mc;
-       int err;
 
-       err = copy_from_user (&mc, mcp, sizeof (master_config_t));
-       if (err)
-               return err;
+       if (copy_from_user (&mc, mcp, sizeof (master_config_t)))
+               return -EFAULT;
 #if EQL_DEBUG
        if (eql_debug >= 20)
                printk ("%s: set master config\n", dev->name);
@@ -646,11 +620,8 @@ static slave_t *eql_new_slave(void)
 
        slave = (slave_t *) kmalloc (sizeof (slave_t), GFP_KERNEL);
        if (slave)
-       {
                memset(slave, 0, sizeof (slave_t));
-               return slave;
-       }
-       return 0;
+       return slave;
 }
 
 
@@ -702,31 +673,32 @@ static slave_queue_t *eql_new_slave_queue(struct net_device *dev)
        slave_t *tail_slave;
 
        queue = (slave_queue_t *) kmalloc (sizeof (slave_queue_t), GFP_KERNEL);
-       if (queue == NULL)
-               return 0;
-       memset (queue, 0, sizeof (slave_queue_t));
+       if (!queue)
+               goto err_out;
+
        head_slave = eql_new_slave ();
+       if (!head_slave)
+               goto err_out_queue;
+
        tail_slave = eql_new_slave ();
+       if (!tail_slave)
+               goto err_out_hs;
 
-       if ( head_slave != 0 &&
-               tail_slave != 0 )
-       {
-               head_slave->next = tail_slave;
-               tail_slave->next = 0;
-               queue->head = head_slave;
-               queue->num_slaves = 0;
-               queue->master_dev = dev;
-       }
-       else
-       {
-               if (head_slave)
-                       kfree(head_slave);
-               if (tail_slave)
-                       kfree(tail_slave);
-               kfree (queue);
-               return 0;
-       }
+       memset (queue, 0, sizeof (slave_queue_t));
+
+       head_slave->next = tail_slave;
+       tail_slave->next = 0;
+       queue->head = head_slave;
+       queue->num_slaves = 0;
+       queue->master_dev = dev;
        return queue;
+
+err_out_hs:
+       kfree (head_slave);
+err_out_queue:
+       kfree (queue);
+err_out:
+       return NULL;
 }
 
 
@@ -1013,13 +985,13 @@ static void eql_timer(unsigned long param)
        }
 }
 
-#ifdef MODULE
 static struct net_device dev_eql = 
 {
-       "eql", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, eql_init 
+       name:   "eql",
+       init:   eql_init,
 };
 
-int init_module(void)
+static int __init eql_init_module(void)
 {
        if (register_netdev(&dev_eql) != 0) {
                printk("eql: register_netdev() returned non-zero.\n");
@@ -1028,13 +1000,15 @@ int init_module(void)
        return 0;
 }
 
-void cleanup_module(void)
+static void __exit eql_cleanup_module(void)
 {
        kfree(((equalizer_t *)dev_eql.priv)->stats );
        kfree(dev_eql.priv);
        unregister_netdev(&dev_eql);
 }
-#endif /* MODULE */
+
+module_init(eql_init_module);
+module_exit(eql_cleanup_module);
 
 /*
  * Local Variables: 
diff --git a/drivers/net/hamachi.c b/drivers/net/hamachi.c
new file mode 100644 (file)
index 0000000..a1f7c8c
--- /dev/null
@@ -0,0 +1,1910 @@
+/* hamachi.c: A Packet Engines GNIC-II Gigabit Ethernet driver for Linux. */
+/*
+       Written 1998-2000 by Donald Becker.
+       Updates 2000 by Keith Underwood.
+
+       This software may be used and distributed according to the terms of 
+       the GNU Public License (GPL), incorporated herein by reference.
+       Drivers based on or derived from this code fall under the GPL and must
+       retain the authorship, copyright and license notice.  This file is not
+       a complete program and may only be used when the entire operating
+       system is licensed under the GPL.
+
+       The author may be reached as becker@scyld.com, or C/O
+       Scyld Computing Corporation
+       410 Severn Ave., Suite 210
+       Annapolis MD 21403
+
+       This driver is for the Packet Engines GNIC-II PCI Gigabit Ethernet
+       adapter.
+
+       Support and updates available at
+       http://www.scyld.com/network/hamachi.html
+       or
+       http://www.parl.clemson.edu/~keithu/hamachi.html
+
+       For best viewing, set your tabs to 3.
+
+*/
+
+static const char *version =
+"hamachi.c:v1.01 5/16/2000  Written by Donald Becker\n"
+"   Some modifications by Eric kasten <kasten@nscl.msu.edu>\n"
+"   Further modifications by Keith Underwood <keithu@parl.clemson.edu>\n"
+"   Support by many others\n"
+"   http://www.scyld.com/network/hamachi.html\n"
+"   or\n"
+"   http://www.parl.clemson.edu/~keithu/drivers/hamachi.html\n";
+
+
+/* A few user-configurable values. */
+
+static int debug = 1;          /* 1 normal messages, 0 quiet .. 7 verbose.  */
+#define final_version
+#define hamachi_debug debug
+/* Maximum events (Rx packets, etc.) to handle at each interrupt. */
+static int max_interrupt_work = 40;
+static int mtu = 0;
+/* Default values selected by testing on a dual processor PIII-450 */
+/* These six interrupt control parameters may be set directly when loading the
+ * module, or through the rx_params and tx_params variables
+ */
+static int max_rx_latency = 0x11;
+static int max_rx_gap = 0x05;
+static int min_rx_pkt = 0x18;
+static int max_tx_latency = 0x00; 
+static int max_tx_gap = 0x00;
+static int min_tx_pkt = 0x30;
+
+/* Set the copy breakpoint for the copy-only-tiny-frames scheme.
+   -Setting to > 1518 causes all frames to be copied
+       -Setting to 0 disables copies
+*/
+static int rx_copybreak = 0;
+
+/* An override for the hardware detection of bus width.
+       Set to 1 to force 32 bit PCI bus detection.  Set to 4 to force 64 bit.
+       Add 2 to disable parity detection.
+*/
+static int force32 = 0;
+
+
+/* Used to pass the media type, etc.
+   These exist for driver interoperability.
+   No media types are currently defined.
+               - The lower 4 bits are reserved for the media type.
+               - The next three bits may be set to one of the following:
+                       0x00000000 : Autodetect PCI bus
+                       0x00000010 : Force 32 bit PCI bus
+                       0x00000020 : Disable parity detection
+                       0x00000040 : Force 64 bit PCI bus
+                       Default is autodetect
+               - The next bit can be used to force half-duplex.  This is a bad
+                 idea since no known implementations implement half-duplex, and,
+                 in general, half-duplex for gigabit ethernet is a bad idea.
+                       0x00000080 : Force half-duplex 
+                       Default is full-duplex.
+               - In the original driver, the ninth bit could be used to force
+                 full-duplex.  Maintain that for compatibility
+                  0x00000200 : Force full-duplex
+*/
+#define MAX_UNITS 8                            /* More are supported, limit only on options */
+static int options[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1};
+static int full_duplex[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1};
+/* The Hamachi chipset supports 3 parameters each for Rx and Tx
+ * interruput management.  Parameters will be loaded as specified into
+ * the TxIntControl and RxIntControl registers.  
+ *
+ * The registers are arranged as follows:
+ *     23 - 16   15 -  8   7    -    0
+ *    _________________________________
+ *   | min_pkt | max_gap | max_latency |
+ *    ---------------------------------
+ *   min_pkt      : The minimum number of packets processed between
+ *                  interrupts. 
+ *   max_gap      : The maximum inter-packet gap in units of 8.192 us
+ *   max_latency  : The absolute time between interrupts in units of 8.192 us
+ * 
+ */
+static int rx_params[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1};
+static int tx_params[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1};
+
+/* Operational parameters that are set at compile time. */
+
+/* Keep the ring sizes a power of two for compile efficiency.
+       The compiler will convert <unsigned>'%'<2^N> into a bit mask.
+   Making the Tx ring too large decreases the effectiveness of channel
+   bonding and packet priority.
+   There are no ill effects from too-large receive rings, except for
+       excessive memory usage */
+/* Empirically it appears that the Tx ring needs to be a little bigger
+   for these Gbit adapters or you get into an overrun condition really
+   easily.  Also, things appear to work a bit better in back-to-back
+   configurations if the Rx ring is 8 times the size of the Tx ring
+*/
+#define TX_RING_SIZE   64
+#define RX_RING_SIZE   512
+
+/*
+ * Enable mii_ioctl.  Added interrupt coalescing parameter adjustment.
+ * 2/19/99 Pete Wyckoff <wyckoff@ca.sandia.gov>
+ */
+#define HAVE_PRIVATE_IOCTL
+
+/* play with 64-bit addrlen; seems to be a teensy bit slower  --pw */
+/* #define ADDRLEN 64 */
+
+/*
+ * RX_CHECKSUM turns on card-generated receive checksum generation for
+ *   TCP and UDP packets.  Otherwise the upper layers do the calculation.
+ * TX_CHECKSUM won't do anything too useful, even if it works.  There's no
+ *   easy mechanism by which to tell the TCP/UDP stack that it need not
+ *   generate checksums for this device.  But if somebody can find a way
+ *   to get that to work, most of the card work is in here already.
+ * 3/10/1999 Pete Wyckoff <wyckoff@ca.sandia.gov>
+ */
+#undef  TX_CHECKSUM
+#define RX_CHECKSUM
+
+/* Operational parameters that usually are not changed. */
+/* Time in jiffies before concluding the transmitter is hung. */
+#define TX_TIMEOUT  (5*HZ)
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/time.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/malloc.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+
+#include <asm/processor.h>     /* Processor type for cache alignment. */
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/unaligned.h>
+#include <asm/cache.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/ip.h>
+#include <linux/delay.h>
+
+/* IP_MF appears to be only defined in <netinet/ip.h>, however,
+   we need it for hardware checksumming support.  FYI... some of
+   the definitions in <netinet/ip.h> conflict/duplicate those in
+   other linux headers causing many compiler warnings.
+*/
+#ifndef IP_MF
+  #define IP_MF 0x2000   /* IP more frags from <netinet/ip.h> */ 
+#endif
+
+/* Define IP_OFFSET to be IPOPT_OFFSET */
+#ifndef IP_OFFSET
+  #ifdef IPOPT_OFFSET
+    #define IP_OFFSET IPOPT_OFFSET
+  #else
+    #define IP_OFFSET 2
+  #endif
+#endif
+
+#define RUN_AT(x) (jiffies + (x))
+
+/* Condensed bus+endian portability operations. */
+#if ADDRLEN == 64
+#define virt_to_desc(addr)  cpu_to_le64(virt_to_bus(addr))
+#else 
+#define virt_to_desc(addr)  cpu_to_le32(virt_to_bus(addr))
+#define le32desc_to_virt(addr)  bus_to_virt(le32_to_cpu(addr))
+#endif   
+
+
+/*
+                               Theory of Operation
+
+I. Board Compatibility
+
+This device driver is designed for the Packet Engines "Hamachi"
+Gigabit Ethernet chip.  The only PCA currently supported is the GNIC-II 64-bit
+66Mhz PCI card.
+
+II. Board-specific settings
+
+No jumpers exist on the board.  The chip supports software correction of
+various motherboard wiring errors, however this driver does not support
+that feature.
+
+III. Driver operation
+
+IIIa. Ring buffers
+
+The Hamachi uses a typical descriptor based bus-master architecture.
+The descriptor list is similar to that used by the Digital Tulip.
+This driver uses two statically allocated fixed-size descriptor lists
+formed into rings by a branch from the final descriptor to the beginning of
+the list.  The ring sizes are set at compile time by RX/TX_RING_SIZE.
+
+This driver uses a zero-copy receive and transmit scheme similar my other
+network drivers.
+The driver allocates full frame size skbuffs for the Rx ring buffers at
+open() time and passes the skb->data field to the Hamachi as receive data
+buffers.  When an incoming frame is less than RX_COPYBREAK bytes long,
+a fresh skbuff is allocated and the frame is copied to the new skbuff.
+When the incoming frame is larger, the skbuff is passed directly up the
+protocol stack and replaced by a newly allocated skbuff.
+
+The RX_COPYBREAK value is chosen to trade-off the memory wasted by
+using a full-sized skbuff for small frames vs. the copying costs of larger
+frames.  Gigabit cards are typically used on generously configured machines
+and the underfilled buffers have negligible impact compared to the benefit of
+a single allocation size, so the default value of zero results in never
+copying packets.
+
+IIIb/c. Transmit/Receive Structure
+
+The Rx and Tx descriptor structure are straight-forward, with no historical
+baggage that must be explained.  Unlike the awkward DBDMA structure, there
+are no unused fields or option bits that had only one allowable setting.
+
+Two details should be noted about the descriptors: The chip supports both 32
+bit and 64 bit address structures, and the length field is overwritten on
+the receive descriptors.  The descriptor length is set in the control word
+for each channel. The development driver uses 32 bit addresses only, however
+64 bit addresses may be enabled for 64 bit architectures e.g. the Alpha.
+
+IIId. Synchronization
+
+This driver is very similar to my other network drivers.
+The driver runs as two independent, single-threaded flows of control.  One
+is the send-packet routine, which enforces single-threaded use by the
+dev->tbusy flag.  The other thread is the interrupt handler, which is single
+threaded by the hardware and other software.
+
+The send packet thread has partial control over the Tx ring and 'dev->tbusy'
+flag.  It sets the tbusy flag whenever it's queuing a Tx packet. If the next
+queue slot is empty, it clears the tbusy flag when finished otherwise it sets
+the 'hmp->tx_full' flag.
+
+The interrupt handler has exclusive control over the Rx ring and records stats
+from the Tx ring.  After reaping the stats, it marks the Tx queue entry as
+empty by incrementing the dirty_tx mark. Iff the 'hmp->tx_full' flag is set, it
+clears both the tx_full and tbusy flags.
+
+IV. Notes
+
+Thanks to Kim Stearns of Packet Engines for providing a pair of GNIC-II boards.
+
+IVb. References
+
+Hamachi Engineering Design Specification, 5/15/97
+(Note: This version was marked "Confidential".)
+
+IVc. Errata
+
+None noted.  
+
+V.  Recent Changes
+
+01/15/1999 EPK  Enlargement of the TX and RX ring sizes.  This appears 
+    to help avoid some stall conditions -- this needs further research.
+
+01/15/1999 EPK  Creation of the hamachi_tx function.  This function cleans 
+    the Tx ring and is called from hamachi_start_xmit (this used to be
+    called from hamachi_interrupt but it tends to delay execution of the
+    interrupt handler and thus reduce bandwidth by reducing the latency
+    between hamachi_rx()'s).  Notably, some modification has been made so 
+    that the cleaning loop checks only to make sure that the DescOwn bit 
+    isn't set in the status flag since the card is not required 
+    to set the entire flag to zero after processing.
+
+01/15/1999 EPK In the hamachi_start_tx function, the Tx ring full flag is 
+    checked before attempting to add a buffer to the ring.  If the ring is full
+    an attempt is made to free any dirty buffers and thus find space for
+    the new buffer or the function returns non-zero which should case the
+    scheduler to reschedule the buffer later.
+
+01/15/1999 EPK Some adjustments were made to the chip intialization.  
+    End-to-end flow control should now be fully active and the interrupt 
+    algorithm vars have been changed.  These could probably use further tuning.
+
+01/15/1999 EPK Added the max_{rx,tx}_latency options.  These are used to
+    set the rx and tx latencies for the Hamachi interrupts. If you're having
+    problems with network stalls, try setting these to higher values.
+    Valid values are 0x00 through 0xff.
+
+01/15/1999 EPK In general, the overall bandwidth has increased and 
+    latencies are better (sometimes by a factor of 2).  Stalls are rare at
+    this point, however there still appears to be a bug somewhere between the
+    hardware and driver.  TCP checksum errors under load also appear to be
+    eliminated at this point.
+
+01/18/1999 EPK Ensured that the DescEndRing bit was being set on both the
+    Rx and Tx rings.  This appears to have been affecting whether a particular
+    peer-to-peer connection would hang under high load.  I believe the Rx
+    rings was typically getting set correctly, but the Tx ring wasn't getting
+    the DescEndRing bit set during initialization. ??? Does this mean the
+    hamachi card is using the DescEndRing in processing even if a particular
+    slot isn't in use -- hypothetically, the card might be searching the 
+    entire Tx ring for slots with the DescOwn bit set and then processing
+    them.  If the DescEndRing bit isn't set, then it might just wander off
+    through memory until it hits a chunk of data with that bit set
+    and then looping back.
+
+02/09/1999 EPK Added Michel Mueller's TxDMA Interrupt and Tx-timeout 
+    problem (TxCmd and RxCmd need only to be set when idle or stopped.
+
+02/09/1999 EPK Added code to check/reset dev->tbusy in hamachi_interrupt.
+    (Michel Mueller pointed out the ``permanently busy'' potential 
+    problem here).
+
+02/22/1999 EPK Added Pete Wyckoff's ioctl to control the Tx/Rx latencies. 
+
+02/23/1999 EPK Verified that the interrupt status field bits for Tx were
+    incorrectly defined and corrected (as per Michel Mueller).
+
+02/23/1999 EPK Corrected the Tx full check to check that at least 4 slots
+    were available before reseting the tbusy and tx_full flags
+    (as per Michel Mueller).
+
+03/11/1999 EPK Added Pete Wyckoff's hardware checksumming support.
+
+12/31/1999 KDU Cleaned up assorted things and added Don's code to force
+32 bit.
+
+02/20/2000 KDU Some of the control was just plain odd.  Cleaned up the
+hamachi_start_xmit() and hamachi_interrupt() code.  There is still some
+re-structuring I would like to do.  
+
+03/01/2000 KDU Experimenting with a WIDE range of interrupt mitigation
+parameters on a dual P3-450 setup yielded the new default interrupt
+mitigation parameters.  Tx should interrupt VERY infrequently due to
+Eric's scheme.  Rx should be more often...
+
+03/13/2000 KDU Added a patch to make the Rx Checksum code interact
+nicely with non-linux machines.  
+
+03/13/2000 KDU Experimented with some of the configuration values:  
+
+       -It seems that enabling PCI performance commands for descriptors
+       (changing RxDMACtrl and TxDMACtrl lower nibble from 5 to D) has minimal 
+       performance impact for any of my tests. (ttcp, netpipe, netperf)  I will 
+       leave them that way until I hear further feedback.
+
+       -Increasing the PCI_LATENCY_TIMER to 130 
+       (2 + (burst size of 128 * (0 wait states + 1))) seems to slightly
+       degrade performance.  Leaving default at 64 pending further information.
+
+03/14/2000 KDU Further tuning:  
+
+       -adjusted boguscnt in hamachi_rx() to depend on interrupt
+       mitigation parameters chosen.
+
+       -Selected a set of interrupt parameters based on some extensive testing.  
+       These may change with more testing.
+
+TO DO:
+
+-Consider borrowing from the acenic driver code to check PCI_COMMAND for
+PCI_COMMAND_INVALIDATE.  Set maximum burst size to cache line size in
+that case.
+
+-fix the reset procedure.  It doesn't quite work.  
+*/
+
+/* A few values that may be tweaked. */
+/* Size of each temporary Rx buffer, calculated as:
+ * 1518 bytes (ethernet packet) + 2 bytes (to get 8 byte alignment for
+ * the card) + 8 bytes of status info + 8 bytes for the Rx Checksum +
+ * 2 more because we use skb_reserve.  
+ */
+#define PKT_BUF_SZ             1538
+
+/* For now, this is going to be set to the maximum size of an ethernet
+ * packet.  Eventually, we may want to make it a variable that is
+ * related to the MTU
+ */
+#define MAX_FRAME_SIZE  1518
+
+/* The rest of these values should never change. */
+
+static void hamachi_timer(unsigned long data);
+
+enum capability_flags {CanHaveMII=1, };
+static struct chip_info {
+       u16     vendor_id, device_id, device_id_mask, pad;
+       const char *name;
+       void (*media_timer)(unsigned long data);
+       int flags;
+} chip_tbl[] = {
+       {0x1318, 0x0911, 0xffff, 0, "Hamachi GNIC-II", hamachi_timer, 0},
+       {0,},
+};
+
+/* Offsets to the Hamachi registers.  Various sizes. */
+enum hamachi_offsets {
+       TxDMACtrl=0x00, TxCmd=0x04, TxStatus=0x06, TxPtr=0x08, TxCurPtr=0x10,
+       RxDMACtrl=0x20, RxCmd=0x24, RxStatus=0x26, RxPtr=0x28, RxCurPtr=0x30,
+       PCIClkMeas=0x060, MiscStatus=0x066, ChipRev=0x68, ChipReset=0x06B,
+       LEDCtrl=0x06C, VirtualJumpers=0x06D, GPIO=0x6E,
+       TxChecksum=0x074, RxChecksum=0x076,
+       TxIntrCtrl=0x078, RxIntrCtrl=0x07C,
+       InterruptEnable=0x080, InterruptClear=0x084, IntrStatus=0x088,
+       EventStatus=0x08C,
+       MACCnfg=0x0A0, FrameGap0=0x0A2, FrameGap1=0x0A4,
+       /* See enum MII_offsets below. */
+       MACCnfg2=0x0B0, RxDepth=0x0B8, FlowCtrl=0x0BC, MaxFrameSize=0x0CE,
+       AddrMode=0x0D0, StationAddr=0x0D2,
+       /* Gigabit AutoNegotiation. */
+       ANCtrl=0x0E0, ANStatus=0x0E2, ANXchngCtrl=0x0E4, ANAdvertise=0x0E8,
+       ANLinkPartnerAbility=0x0EA,
+       EECmdStatus=0x0F0, EEData=0x0F1, EEAddr=0x0F2,
+       FIFOcfg=0x0F8,
+};
+
+/* Offsets to the MII-mode registers. */
+enum MII_offsets {
+       MII_Cmd=0xA6, MII_Addr=0xA8, MII_Wr_Data=0xAA, MII_Rd_Data=0xAC,
+       MII_Status=0xAE,
+};
+
+/* Bits in the interrupt status/mask registers. */
+enum intr_status_bits {
+       IntrRxDone=0x01, IntrRxPCIFault=0x02, IntrRxPCIErr=0x04,
+       IntrTxDone=0x100, IntrTxPCIFault=0x200, IntrTxPCIErr=0x400,
+       LinkChange=0x10000, NegotiationChange=0x20000, StatsMax=0x40000, };
+
+/* The Hamachi Rx and Tx buffer descriptors. */
+struct hamachi_desc {
+       u32 status_n_length;                    
+#if ADDRLEN == 64
+       u32 pad;
+       u64 addr;
+#else
+       u32 addr;
+#endif
+};
+
+/* Bits in hamachi_desc.status_n_length */
+enum desc_status_bits {
+       DescOwn=0x80000000, DescEndPacket=0x40000000, DescEndRing=0x20000000, 
+       DescIntr=0x10000000,
+};
+
+#define PRIV_ALIGN   15                                /* Required alignment mask */
+struct hamachi_private {
+       /* Descriptor rings first for alignment.  Tx requires a second descriptor
+          for status. */
+       struct hamachi_desc rx_ring[RX_RING_SIZE];
+       struct hamachi_desc tx_ring[TX_RING_SIZE];
+       /* The addresses of receive-in-place skbuffs. */
+       struct sk_buff* rx_skbuff[RX_RING_SIZE];
+       /* The saved address of a sent-in-place packet/buffer, for skfree(). */
+       struct sk_buff* tx_skbuff[TX_RING_SIZE];
+       struct net_device_stats stats;
+       struct timer_list timer;                                /* Media selection timer. */
+       /* Frequently used and paired value: keep adjacent for cache effect. */
+       spinlock_t lock;
+       int chip_id;
+       struct hamachi_desc *rx_head_desc;
+       unsigned int cur_rx, dirty_rx;          /* Producer/consumer ring indices */
+       unsigned int cur_tx, dirty_tx;
+       unsigned int rx_buf_sz;                                 /* Based on MTU+slack. */
+       unsigned int tx_full:1;                                 /* The Tx queue is full. */
+       unsigned int full_duplex:1;                     /* Full-duplex operation requested. */
+       unsigned int duplex_lock:1;
+       unsigned int medialock:1;                               /* Do not sense media. */
+       unsigned int default_port:4;                    /* Last dev->if_port value. */
+       /* MII transceiver section. */
+       int mii_cnt;                                                            /* MII device addresses. */
+       u16 advertising;                                                        /* NWay media advertisement */
+       unsigned char phys[2];                                  /* MII device addresses. */
+       u_int32_t rx_int_var, tx_int_var;       /* interrupt control variables */
+       u_int32_t option;                                                       /* Hold on to a copy of the options */
+       u_int8_t pad[16];                                                       /* Used for 32-byte alignment */
+};
+
+MODULE_AUTHOR("Donald Becker <becker@scyld.com>, Eric Kasten <kasten@nscl.msu.edu>, Keith Underwood <keithu@parl.clemson.edu>");
+MODULE_DESCRIPTION("Packet Engines 'Hamachi' GNIC-II Gigabit Ethernet driver");
+MODULE_PARM(max_interrupt_work, "i");
+MODULE_PARM(mtu, "i");
+MODULE_PARM(debug, "i");
+MODULE_PARM(min_rx_pkt, "i");
+MODULE_PARM(max_rx_gap, "i");
+MODULE_PARM(max_rx_latency, "i");
+MODULE_PARM(min_tx_pkt, "i");
+MODULE_PARM(max_tx_gap, "i");
+MODULE_PARM(max_tx_latency, "i");
+MODULE_PARM(rx_copybreak, "i");
+MODULE_PARM(rx_params, "1-" __MODULE_STRING(MAX_UNITS) "i");
+MODULE_PARM(tx_params, "1-" __MODULE_STRING(MAX_UNITS) "i");
+MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i");
+MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i");
+MODULE_PARM(force32, "i");
+
+static int read_eeprom(long ioaddr, int location);
+static int mdio_read(long ioaddr, int phy_id, int location);
+static void mdio_write(long ioaddr, int phy_id, int location, int value);
+static int hamachi_open(struct net_device *dev);
+#ifdef HAVE_PRIVATE_IOCTL
+static int mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
+#endif
+static void hamachi_timer(unsigned long data);
+static void hamachi_tx_timeout(struct net_device *dev);
+static void hamachi_init_ring(struct net_device *dev);
+static int hamachi_start_xmit(struct sk_buff *skb, struct net_device *dev);
+static void hamachi_interrupt(int irq, void *dev_instance, struct pt_regs *regs);
+static inline int hamachi_rx(struct net_device *dev);
+static inline int hamachi_tx(struct net_device *dev);
+static void hamachi_error(struct net_device *dev, int intr_status);
+static int hamachi_close(struct net_device *dev);
+static struct net_device_stats *hamachi_get_stats(struct net_device *dev);
+static void set_rx_mode(struct net_device *dev);
+
+
+static int __init hamachi_init_one (struct pci_dev *pdev,
+                                   const struct pci_device_id *ent)
+{
+       static int did_version = 0;                     /* Already printed version info. */
+       struct hamachi_private *hmp;
+       int option, i, rx_int_var, tx_int_var, boguscnt;
+       int chip_id = ent->driver_data;
+       int irq = pdev->irq;
+       long ioaddr;
+       static int card_idx = 0;
+       struct net_device *dev;
+
+       if (hamachi_debug > 0  &&  did_version++ == 0)
+               printk(version);
+
+       ioaddr = pci_resource_start(pdev, 0);
+#ifdef __alpha__                               /* Really "64 bit addrs" */
+       ioaddr |= (pci_resource_start(pdev, 1) << 32);
+#endif
+
+       if (pci_enable_device(pdev))
+               return -EIO;
+       pci_set_master(pdev);
+
+       ioaddr = (long) ioremap(ioaddr, 0x400);
+       if (!ioaddr)
+               return -ENOMEM;
+
+       dev = init_etherdev(NULL, sizeof(struct hamachi_private));
+       if (!dev) {
+               iounmap((char *)ioaddr);
+               return -ENOMEM;
+       }
+
+#ifdef TX_CHECKSUM
+       printk("check that skbcopy in ip_queue_xmit isn't happening\n");
+       dev->hard_header_len += 8;  /* for cksum tag */
+#endif
+
+       printk(KERN_INFO "%s: %s type %x at 0x%lx, ",
+                  dev->name, chip_tbl[chip_id].name, readl(ioaddr + ChipRev),
+                  ioaddr);
+
+       for (i = 0; i < 6; i++)
+               dev->dev_addr[i] = 1 ? read_eeprom(ioaddr, 4 + i)
+                       : readb(ioaddr + StationAddr + i);
+       for (i = 0; i < 5; i++)
+                       printk("%2.2x:", dev->dev_addr[i]);
+       printk("%2.2x, IRQ %d.\n", dev->dev_addr[i], irq);
+
+#if ! defined(final_version)
+       if (hamachi_debug > 4)
+               for (i = 0; i < 0x10; i++)
+                       printk("%2.2x%s",
+                                  read_eeprom(ioaddr, i), i % 16 != 15 ? " " : "\n");
+#endif
+
+#if 0 /* Moving this until after the force 32 check and reset. */
+       i = readb(ioaddr + PCIClkMeas);
+       printk(KERN_INFO "%s:  %d-bit %d Mhz PCI bus (%d), Virtual Jumpers "
+                  "%2.2x, LPA %4.4x.\n",
+                  dev->name, readw(ioaddr + MiscStatus) & 1 ? 64 : 32,
+                  i ? 2000/(i&0x7f) : 0, i&0x7f, (int)readb(ioaddr + VirtualJumpers),
+                  readw(ioaddr + ANLinkPartnerAbility));
+#endif
+
+       hmp = dev->priv;
+       spin_lock_init(&hmp->lock);
+
+       /* Check for options being passed in */
+       option = card_idx < MAX_UNITS ? options[card_idx] : 0;
+       if (dev->mem_start)
+               option = dev->mem_start;
+
+       /* If the bus size is misidentified, do the following. */
+       force32 = force32 ? force32 : 
+               ((option  >= 0) ? ((option & 0x00000070) >> 4) : 0 );
+       if (force32)
+               writeb(force32, ioaddr + VirtualJumpers);
+
+       /* Hmmm, do we really need to reset the chip???. */
+       writeb(0x01, ioaddr + ChipReset);
+
+       /* After a reset, the clock speed measurement of the PCI bus will not
+        * be valid for a moment.  Wait for a little while until it is.  If
+        * it takes more than 10ms, forget it.
+        */
+       udelay(10);     
+       i = readb(ioaddr + PCIClkMeas);
+       for (boguscnt = 0; (!(i & 0x080)) && boguscnt < 1000; boguscnt++){
+               udelay(10);     
+               i = readb(ioaddr + PCIClkMeas); 
+       }
+
+       printk(KERN_INFO "%s:  %d-bit %d Mhz PCI bus (%d), Virtual Jumpers "
+                  "%2.2x, LPA %4.4x.\n",
+                  dev->name, readw(ioaddr + MiscStatus) & 1 ? 64 : 32,
+                  i ? 2000/(i&0x7f) : 0, i&0x7f, (int)readb(ioaddr + VirtualJumpers),
+                  readw(ioaddr + ANLinkPartnerAbility));
+
+       dev->base_addr = ioaddr;
+       dev->irq = irq;
+
+       hmp->chip_id = chip_id;
+
+       /* The lower four bits are the media type. */
+       if (option > 0) {
+               hmp->option = option;
+               if (option & 0x200)
+                       hmp->full_duplex = 1;
+               else if (option & 0x080)
+                       hmp->full_duplex = 0;
+               hmp->default_port = option & 15;
+               if (hmp->default_port)
+                       hmp->medialock = 1;
+       }
+       if (card_idx < MAX_UNITS  &&  full_duplex[card_idx] > 0)
+               hmp->full_duplex = 1;
+
+       /* lock the duplex mode if someone specified a value */
+       if (hmp->full_duplex || (option & 0x080))
+               hmp->duplex_lock = 1;
+
+       /* Set interrupt tuning parameters */
+       max_rx_latency = max_rx_latency & 0x00ff;
+       max_rx_gap = max_rx_gap & 0x00ff;
+       min_rx_pkt = min_rx_pkt & 0x00ff;
+       max_tx_latency = max_tx_latency & 0x00ff;
+       max_tx_gap = max_tx_gap & 0x00ff;
+       min_tx_pkt = min_tx_pkt & 0x00ff;
+
+       rx_int_var = card_idx < MAX_UNITS ? rx_params[card_idx] : -1;
+       tx_int_var = card_idx < MAX_UNITS ? tx_params[card_idx] : -1;
+       hmp->rx_int_var = rx_int_var >= 0 ? rx_int_var : 
+               (min_rx_pkt << 16 | max_rx_gap << 8 | max_rx_latency);
+       hmp->tx_int_var = tx_int_var >= 0 ? tx_int_var : 
+               (min_tx_pkt << 16 | max_tx_gap << 8 | max_tx_latency);
+
+
+       /* The Hamachi-specific entries in the device structure. */
+       dev->open = &hamachi_open;
+       dev->hard_start_xmit = &hamachi_start_xmit;
+       dev->stop = &hamachi_close;
+       dev->get_stats = &hamachi_get_stats;
+       dev->set_multicast_list = &set_rx_mode;
+#ifdef HAVE_PRIVATE_IOCTL
+       dev->do_ioctl = &mii_ioctl;
+#endif
+       dev->tx_timeout = &hamachi_tx_timeout;
+       dev->watchdog_timeo = TX_TIMEOUT;
+       if (mtu)
+               dev->mtu = mtu;
+
+       if (chip_tbl[hmp->chip_id].flags & CanHaveMII) {
+               int phy, phy_idx = 0;
+               for (phy = 0; phy < 32 && phy_idx < 4; phy++) {
+                       int mii_status = mdio_read(ioaddr, phy, 1);
+                       if (mii_status != 0xffff  &&
+                               mii_status != 0x0000) {
+                               hmp->phys[phy_idx++] = phy;
+                               hmp->advertising = mdio_read(ioaddr, phy, 4);
+                               printk(KERN_INFO "%s: MII PHY found at address %d, status "
+                                          "0x%4.4x advertising %4.4x.\n",
+                                          dev->name, phy, mii_status, hmp->advertising);
+                       }
+               }
+               hmp->mii_cnt = phy_idx;
+       }
+       /* Configure gigabit autonegotiation. */
+       writew(0x0400, ioaddr + ANXchngCtrl);   /* Enable legacy links. */
+       writew(0x08e0, ioaddr + ANAdvertise);   /* Set our advertise word. */
+       writew(0x1000, ioaddr + ANCtrl);                        /* Enable negotiation */
+
+       card_idx++;
+       return 0;
+}
+
+static int read_eeprom(long ioaddr, int location)
+{
+       int bogus_cnt = 1000;
+
+       /* We should check busy first - per docs -KDU */
+       while ((readb(ioaddr + EECmdStatus) & 0x40)  && --bogus_cnt > 0);
+       writew(location, ioaddr + EEAddr);
+       writeb(0x02, ioaddr + EECmdStatus);
+       bogus_cnt = 1000;
+       while ((readb(ioaddr + EECmdStatus) & 0x40)  && --bogus_cnt > 0);
+       if (hamachi_debug > 5)
+               printk("   EEPROM status is %2.2x after %d ticks.\n",
+                          (int)readb(ioaddr + EECmdStatus), 1000- bogus_cnt);
+       return readb(ioaddr + EEData);
+}
+
+/* MII Managemen Data I/O accesses.
+   These routines assume the MDIO controller is idle, and do not exit until
+   the command is finished. */
+
+static int mdio_read(long ioaddr, int phy_id, int location)
+{
+       int i;
+
+       /* We should check busy first - per docs -KDU */
+       for (i = 10000; i >= 0; i--)
+               if ((readw(ioaddr + MII_Status) & 1) == 0)
+                       break;
+       writew((phy_id<<8) + location, ioaddr + MII_Addr);
+       writew(0x0001, ioaddr + MII_Cmd);
+       for (i = 10000; i >= 0; i--)
+               if ((readw(ioaddr + MII_Status) & 1) == 0)
+                       break;
+       return readw(ioaddr + MII_Rd_Data);
+}
+
+static void mdio_write(long ioaddr, int phy_id, int location, int value)
+{
+       int i;
+
+       /* We should check busy first - per docs -KDU */
+       for (i = 10000; i >= 0; i--)
+               if ((readw(ioaddr + MII_Status) & 1) == 0)
+                       break;
+       writew((phy_id<<8) + location, ioaddr + MII_Addr);
+       writew(value, ioaddr + MII_Wr_Data);
+
+       /* Wait for the command to finish. */
+       for (i = 10000; i >= 0; i--)
+               if ((readw(ioaddr + MII_Status) & 1) == 0)
+                       break;
+       return;
+}
+
+\f
+static int hamachi_open(struct net_device *dev)
+{
+       struct hamachi_private *hmp = (struct hamachi_private *)dev->priv;
+       long ioaddr = dev->base_addr;
+       int i;
+       u_int32_t rx_int_var, tx_int_var;
+       u_int16_t fifo_info;
+
+       MOD_INC_USE_COUNT;
+
+       if (request_irq(dev->irq, &hamachi_interrupt, SA_SHIRQ, dev->name, dev)) {
+               MOD_DEC_USE_COUNT;
+               return -EAGAIN;
+       }
+
+       if (hamachi_debug > 1)
+               printk(KERN_DEBUG "%s: hamachi_open() irq %d.\n",
+                          dev->name, dev->irq);
+
+       hamachi_init_ring(dev);
+
+#if ADDRLEN == 64
+       writel(virt_to_bus(hmp->rx_ring), ioaddr + RxPtr);
+       writel(virt_to_bus(hmp->rx_ring) >> 32, ioaddr + RxPtr + 4);
+       writel(virt_to_bus(hmp->tx_ring), ioaddr + TxPtr);
+       writel(virt_to_bus(hmp->tx_ring) >> 32, ioaddr + TxPtr + 4);
+#else
+       writel(virt_to_bus(hmp->rx_ring), ioaddr + RxPtr);
+       writel(virt_to_bus(hmp->tx_ring), ioaddr + TxPtr);
+#endif
+
+       /* TODO:  It would make sense to organize this as words since the card 
+        * documentation does. -KDU
+        */
+       for (i = 0; i < 6; i++)
+               writeb(dev->dev_addr[i], ioaddr + StationAddr + i);
+
+       /* Initialize other registers: with so many this eventually this will
+          converted to an offset/value list. */
+
+       /* Configure the FIFO */
+       fifo_info = (readw(ioaddr + GPIO) & 0x00C0) >> 6;
+       switch (fifo_info){
+               case 0 : 
+                       /* No FIFO */
+                       writew(0x0000, ioaddr + FIFOcfg);
+                       break;
+               case 1 : 
+                       /* Configure the FIFO for 512K external, 16K used for Tx. */
+                       writew(0x0028, ioaddr + FIFOcfg);
+                       break;
+               case 2 : 
+                       /* Configure the FIFO for 1024 external, 32K used for Tx. */
+                       writew(0x004C, ioaddr + FIFOcfg);
+                       break;
+               case 3 : 
+                       /* Configure the FIFO for 2048 external, 32K used for Tx. */
+                       writew(0x006C, ioaddr + FIFOcfg);
+                       break;
+               default : 
+                       printk(KERN_WARNING "%s:  Unsupported external memory config!\n",
+                               dev->name);
+                       /* Default to no FIFO */
+                       writew(0x0000, ioaddr + FIFOcfg);
+                       break;
+       }
+       
+       if (dev->if_port == 0)
+               dev->if_port = hmp->default_port;
+
+
+       /* Setting the Rx mode will start the Rx process. */
+       /* If someone didn't choose a duplex, default to full-duplex */ 
+       if (hmp->duplex_lock != 1)
+               hmp->full_duplex = 1;
+
+       /* always 1, takes no more time to do it */
+       writew(0x0001, ioaddr + RxChecksum);
+#ifdef TX_CHECKSUM
+       writew(0x0001, ioaddr + TxChecksum);
+#else
+       writew(0x0000, ioaddr + TxChecksum);
+#endif
+       writew(0x8000, ioaddr + MACCnfg); /* Soft reset the MAC */
+       writew(0x215F, ioaddr + MACCnfg);
+       writew(0x000C, ioaddr + FrameGap0); 
+       /* WHAT?!?!?  Why isn't this documented somewhere? -KDU */
+       writew(0x1018, ioaddr + FrameGap1);
+       /* Why do we enable receives/transmits here? -KDU */
+       writew(0x0780, ioaddr + MACCnfg2); /* Upper 16 bits control LEDs. */
+       /* Enable automatic generation of flow control frames, period 0xffff. */
+       writel(0x0030FFFF, ioaddr + FlowCtrl);
+       writew(MAX_FRAME_SIZE, ioaddr + MaxFrameSize);  /* dev->mtu+14 ??? */
+
+       /* Enable legacy links. */
+       writew(0x0400, ioaddr + ANXchngCtrl);   /* Enable legacy links. */
+       /* Initial Link LED to blinking red. */
+       writeb(0x03, ioaddr + LEDCtrl);
+
+       /* Configure interrupt mitigation.  This has a great effect on
+          performance, so systems tuning should start here!. */
+
+       rx_int_var = hmp->rx_int_var;
+       tx_int_var = hmp->tx_int_var;
+
+       if (hamachi_debug > 1) {
+               printk("max_tx_latency: %d, max_tx_gap: %d, min_tx_pkt: %d\n",
+                       tx_int_var & 0x00ff, (tx_int_var & 0x00ff00) >> 8, 
+                       (tx_int_var & 0x00ff0000) >> 16);
+               printk("max_rx_latency: %d, max_rx_gap: %d, min_rx_pkt: %d\n",
+                       rx_int_var & 0x00ff, (rx_int_var & 0x00ff00) >> 8, 
+                       (rx_int_var & 0x00ff0000) >> 16);
+               printk("rx_int_var: %x, tx_int_var: %x\n", rx_int_var, tx_int_var);
+       }
+
+       writel(tx_int_var, ioaddr + TxIntrCtrl); 
+       writel(rx_int_var, ioaddr + RxIntrCtrl); 
+
+       set_rx_mode(dev);
+
+       netif_start_queue(dev);
+
+       /* Enable interrupts by setting the interrupt mask. */
+       writel(0x80878787, ioaddr + InterruptEnable);
+       writew(0x0000, ioaddr + EventStatus);   /* Clear non-interrupting events */
+
+       /* Configure and start the DMA channels. */
+       /* Burst sizes are in the low three bits: size = 4<<(val&7) */
+#if ADDRLEN == 64
+       writew(0x005D, ioaddr + RxDMACtrl);             /* 128 dword bursts */
+       writew(0x005D, ioaddr + TxDMACtrl);
+#else
+       writew(0x001D, ioaddr + RxDMACtrl);
+       writew(0x001D, ioaddr + TxDMACtrl);
+#endif
+       writew(0x0001, dev->base_addr + RxCmd);
+
+       if (hamachi_debug > 2) {
+               printk(KERN_DEBUG "%s: Done hamachi_open(), status: Rx %x Tx %x.\n",
+                          dev->name, readw(ioaddr + RxStatus), readw(ioaddr + TxStatus));
+       }
+       /* Set the timer to check for link beat. */
+       init_timer(&hmp->timer);
+       hmp->timer.expires = RUN_AT((24*HZ)/10);                        /* 2.4 sec. */
+       hmp->timer.data = (unsigned long)dev;
+       hmp->timer.function = &hamachi_timer;                           /* timer handler */
+       add_timer(&hmp->timer);
+
+       return 0;
+}
+
+static inline int hamachi_tx(struct net_device *dev)
+{
+       struct hamachi_private *hmp = (struct hamachi_private *)dev->priv;
+
+       /* Update the dirty pointer until we find an entry that is
+               still owned by the card */
+       for (; hmp->cur_tx - hmp->dirty_tx > 0; hmp->dirty_tx++) {
+               int entry = hmp->dirty_tx % TX_RING_SIZE;
+               if (hmp->tx_ring[entry].status_n_length & cpu_to_le32(DescOwn)) 
+                       break;
+               /* Free the original skb. */
+               if (hmp->tx_skbuff[entry] != 0) {
+                       dev_kfree_skb(hmp->tx_skbuff[entry]);
+                       hmp->tx_skbuff[entry] = 0;
+               }
+               hmp->tx_ring[entry].status_n_length = 0;
+               if (entry >= TX_RING_SIZE-1) 
+                       hmp->tx_ring[TX_RING_SIZE-1].status_n_length |=
+                               cpu_to_le32(DescEndRing);   
+               hmp->stats.tx_packets++;
+       }
+
+       return 0;
+}
+
+static void hamachi_timer(unsigned long data)
+{
+       struct net_device *dev = (struct net_device *)data;
+       struct hamachi_private *hmp = (struct hamachi_private *)dev->priv;
+       long ioaddr = dev->base_addr;
+       int next_tick = 10*HZ;
+
+       if (hamachi_debug > 2) {
+               printk(KERN_INFO "%s: Hamachi Autonegotiation status %4.4x, LPA "
+                          "%4.4x.\n", dev->name, readw(ioaddr + ANStatus),
+                          readw(ioaddr + ANLinkPartnerAbility));
+               printk(KERN_INFO "%s: Autonegotiation regs %4.4x %4.4x %4.4x "
+                      "%4.4x %4.4x %4.4x.\n", dev->name,
+                      readw(ioaddr + 0x0e0),
+                      readw(ioaddr + 0x0e2),
+                      readw(ioaddr + 0x0e4),
+                      readw(ioaddr + 0x0e6),
+                      readw(ioaddr + 0x0e8),
+                      readw(ioaddr + 0x0eA));
+       }
+       /* We could do something here... nah. */
+       hmp->timer.expires = RUN_AT(next_tick);
+       add_timer(&hmp->timer);
+}
+
+static void hamachi_tx_timeout(struct net_device *dev)
+{
+       int i;
+       struct hamachi_private *hmp = (struct hamachi_private *)dev->priv;
+       long ioaddr = dev->base_addr;
+
+       printk(KERN_WARNING "%s: Hamachi transmit timed out, status %8.8x,"
+                  " resetting...\n", dev->name, (int)readw(ioaddr + TxStatus));
+
+#ifndef __alpha__
+       {
+               int i;
+               printk(KERN_DEBUG "  Rx ring %8.8x: ", (int)hmp->rx_ring);
+               for (i = 0; i < RX_RING_SIZE; i++)
+                       printk(" %8.8x", (unsigned int)hmp->rx_ring[i].status_n_length);
+               printk("\n"KERN_DEBUG"  Tx ring %8.8x: ", (int)hmp->tx_ring);
+               for (i = 0; i < TX_RING_SIZE; i++)
+                       printk(" %4.4x", hmp->tx_ring[i].status_n_length);
+               printk("\n");
+       }
+#endif
+
+       /* Reinit the hardware and make sure the Rx and Tx processes 
+               are up and running.
+        */
+       dev->if_port = 0;
+       /* The right way to do Reset. -KDU
+        *              -Clear OWN bit in all Rx/Tx descriptors
+        *              -Wait 50 uS for channels to go idle
+        *              -Turn off MAC receiver
+        *              -Issue Reset
+        */
+       
+       for (i = 0; i < RX_RING_SIZE; i++)
+               hmp->rx_ring[i].status_n_length &= ~DescOwn;
+
+       /* Presume that all packets in the Tx queue are gone if we have to
+        * re-init the hardware.
+        */
+       for (i = 0; i < TX_RING_SIZE; i++){
+               if (i >= TX_RING_SIZE - 1)
+                       hmp->tx_ring[i].status_n_length = DescEndRing |
+                               (hmp->tx_ring[i].status_n_length & 0x0000FFFF);
+               else    
+                       hmp->tx_ring[i].status_n_length &= 0x0000ffff;
+               if (hmp->tx_skbuff[i]){
+                       dev_kfree_skb(hmp->tx_skbuff[i]);
+                       hmp->tx_skbuff[i] = 0;
+               }
+       }
+
+       udelay(60); /* Sleep 60 us just for safety sake */
+       writew(0x0002, dev->base_addr + RxCmd); /* STOP Rx */
+               
+       writeb(0x01, ioaddr + ChipReset);  /* Reinit the hardware */ 
+
+       hmp->tx_full = 0;
+       hmp->cur_rx = hmp->cur_tx = 0;
+       hmp->dirty_rx = hmp->dirty_tx = 0;
+       hmp->rx_head_desc = &hmp->rx_ring[0];
+       /* Rx packets are also presumed lost; however, we need to make sure a
+        * ring of buffers is in tact. -KDU
+        */ 
+       for (i = 0; i < RX_RING_SIZE; i++){
+               if (hmp->rx_skbuff[i]){
+                       dev_kfree_skb(hmp->rx_skbuff[i]);
+                       hmp->rx_skbuff[i] = 0;
+               }
+       }
+       /* Fill in the Rx buffers.  Handle allocation failure gracefully. */
+       for (i = 0; i < RX_RING_SIZE; i++) {
+               struct sk_buff *skb = dev_alloc_skb(hmp->rx_buf_sz);
+               hmp->rx_skbuff[i] = skb;
+               if (skb == NULL)
+                       break;
+               skb->dev = dev;         /* Mark as being used by this device. */
+               skb_reserve(skb, 2); /* 16 byte align the IP header. */
+               hmp->rx_ring[i].addr = virt_to_desc(skb->tail);
+               hmp->rx_ring[i].status_n_length =
+                       cpu_to_le32(DescOwn | DescEndPacket | DescIntr | (hmp->rx_buf_sz - 2));
+       }
+       hmp->dirty_rx = (unsigned int)(i - RX_RING_SIZE);
+       /* Mark the last entry as wrapping the ring. */
+       hmp->rx_ring[RX_RING_SIZE-1].status_n_length |= cpu_to_le32(DescEndRing);
+
+
+       /* Trigger an immediate transmit demand. */
+       dev->trans_start = jiffies;
+       hmp->stats.tx_errors++;
+
+       /* Restart the chip's Tx/Rx processes . */
+       writew(0x0002, dev->base_addr + TxCmd); /* STOP Tx */
+       writew(0x0001, dev->base_addr + TxCmd); /* START Tx */
+       writew(0x0001, dev->base_addr + RxCmd); /* START Rx */
+}
+
+
+/* Initialize the Rx and Tx rings, along with various 'dev' bits. */
+static void hamachi_init_ring(struct net_device *dev)
+{
+       struct hamachi_private *hmp = (struct hamachi_private *)dev->priv;
+       int i;
+
+       hmp->tx_full = 0;
+       hmp->cur_rx = hmp->cur_tx = 0;
+       hmp->dirty_rx = hmp->dirty_tx = 0;
+
+#if 0
+       /* This is wrong.  I'm not sure what the original plan was, but this
+        * is wrong.  An MTU of 1 gets you a buffer of 1536, while an MTU
+        * of 1501 gets a buffer of 1533? -KDU
+        */
+       hmp->rx_buf_sz = (dev->mtu <= 1500 ? PKT_BUF_SZ : dev->mtu + 32);
+#endif
+       /* My attempt at a reasonable correction */
+       /* +26 gets the maximum ethernet encapsulation, +7 & ~7 because the
+        * card needs room to do 8 byte alignment, +2 so we can reserve 
+        * the first 2 bytes, and +16 gets room for the status word from the 
+        * card.  -KDU
+        */
+       hmp->rx_buf_sz = (dev->mtu <= 1492 ? PKT_BUF_SZ : 
+               (((dev->mtu+26+7) & ~7) + 2 + 16));
+
+       hmp->rx_head_desc = &hmp->rx_ring[0];
+
+       /* Initialize all Rx descriptors. */
+       for (i = 0; i < RX_RING_SIZE; i++) {
+               hmp->rx_ring[i].status_n_length = 0;
+               hmp->rx_skbuff[i] = 0;
+       }
+       /* Fill in the Rx buffers.  Handle allocation failure gracefully. */
+       for (i = 0; i < RX_RING_SIZE; i++) {
+               struct sk_buff *skb = dev_alloc_skb(hmp->rx_buf_sz);
+               hmp->rx_skbuff[i] = skb;
+               if (skb == NULL)
+                       break;
+               skb->dev = dev;         /* Mark as being used by this device. */
+               skb_reserve(skb, 2); /* 16 byte align the IP header. */
+               hmp->rx_ring[i].addr = virt_to_desc(skb->tail);
+               /* -2 because it doesn't REALLY have that first 2 bytes -KDU */
+               hmp->rx_ring[i].status_n_length =
+                       cpu_to_le32(DescOwn | DescEndPacket | DescIntr | (hmp->rx_buf_sz -2));
+       }
+       hmp->dirty_rx = (unsigned int)(i - RX_RING_SIZE);
+       /* Mark the last entry as wrapping the ring. */
+       hmp->rx_ring[RX_RING_SIZE-1].status_n_length |= cpu_to_le32(DescEndRing);
+
+
+       /* Mark the last entry as wrapping the ring. */
+       hmp->rx_ring[RX_RING_SIZE-1].status_n_length |= DescEndRing;
+
+       for (i = 0; i < TX_RING_SIZE; i++) {
+               hmp->tx_skbuff[i] = 0;
+               hmp->tx_ring[i].status_n_length = 0;
+       }
+       /* Mark the last entry as wrapping the ring. */
+       hmp->tx_ring[TX_RING_SIZE-1].status_n_length |= cpu_to_le32(DescEndRing);
+
+       return;
+}
+
+
+#ifdef TX_CHECKSUM
+#define csum_add(it, val) \
+do { \
+    it += (u16) (val); \
+    if (it & 0xffff0000) { \
+       it &= 0xffff; \
+       ++it; \
+    } \
+} while (0)
+    /* printk("add %04x --> %04x\n", val, it); \ */
+
+/* uh->len already network format, do not swap */
+#define pseudo_csum_udp(sum,ih,uh) do { \
+    sum = 0; \
+    csum_add(sum, (ih)->saddr >> 16); \
+    csum_add(sum, (ih)->saddr & 0xffff); \
+    csum_add(sum, (ih)->daddr >> 16); \
+    csum_add(sum, (ih)->daddr & 0xffff); \
+    csum_add(sum, __constant_htons(IPPROTO_UDP)); \
+    csum_add(sum, (uh)->len); \
+} while (0)
+
+/* swap len */
+#define pseudo_csum_tcp(sum,ih,len) do { \
+    sum = 0; \
+    csum_add(sum, (ih)->saddr >> 16); \
+    csum_add(sum, (ih)->saddr & 0xffff); \
+    csum_add(sum, (ih)->daddr >> 16); \
+    csum_add(sum, (ih)->daddr & 0xffff); \
+    csum_add(sum, __constant_htons(IPPROTO_TCP)); \
+    csum_add(sum, htons(len)); \
+} while (0)
+#endif
+
+static int hamachi_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+       struct hamachi_private *hmp = (struct hamachi_private *)dev->priv;
+       unsigned entry;
+       u16 status;
+
+       /* Ok, now make sure that the queue has space before trying to 
+               add another skbuff.  if we return non-zero the scheduler
+               should interpret this as a queue full and requeue the buffer
+               for later.
+        */
+       if (hmp->tx_full) {
+               /* We should NEVER reach this point -KDU */
+               printk(KERN_WARNING "%s: Hamachi transmit queue full at slot %d.\n",dev->name, hmp->cur_tx);
+
+               /* Wake the potentially-idle transmit channel. */
+               /* If we don't need to read status, DON'T -KDU */
+               status=readw(dev->base_addr + TxStatus);
+               if( !(status & 0x0001) || (status & 0x0002))
+                       writew(0x0001, dev->base_addr + TxCmd);
+               return 1;
+       } 
+
+       /* Caution: the write order is important here, set the field
+          with the "ownership" bits last. */
+
+       /* Calculate the next Tx descriptor entry. */
+       entry = hmp->cur_tx % TX_RING_SIZE;
+
+       hmp->tx_skbuff[entry] = skb;
+
+#ifdef TX_CHECKSUM
+       {
+           /* tack on checksum tag */
+           u32 tagval = 0;
+           struct ethhdr *eh = (struct ethhdr *)skb->data;
+           if (eh->h_proto == __constant_htons(ETH_P_IP)) {
+               struct iphdr *ih = (struct iphdr *)((char *)eh + ETH_HLEN);
+               if (ih->protocol == IPPROTO_UDP) {
+                   struct udphdr *uh
+                     = (struct udphdr *)((char *)ih + ih->ihl*4);
+                   u32 offset = ((unsigned char *)uh + 6) - skb->data;
+                   u32 pseudo;
+                   pseudo_csum_udp(pseudo, ih, uh);
+                   pseudo = htons(pseudo);
+                   printk("udp cksum was %04x, sending pseudo %04x\n",
+                     uh->check, pseudo);
+                   uh->check = 0;  /* zero out uh->check before card calc */
+                   /*
+                    * start at 14 (skip ethhdr), store at offset (uh->check),
+                    * use pseudo value given.
+                    */
+                   tagval = (14 << 24) | (offset << 16) | pseudo;
+               } else if (ih->protocol == IPPROTO_TCP) {
+                   printk("tcp, no auto cksum\n");
+               }
+           }
+           *(u32 *)skb_push(skb, 8) = tagval;
+       }
+#endif
+
+       hmp->tx_ring[entry].addr = virt_to_desc(skb->data);
+    
+       /* Hmmmm, could probably put a DescIntr on these, but the way
+               the driver is currently coded makes Tx interrupts unnecessary
+               since the clearing of the Tx ring is handled by the start_xmit
+               routine.  This organization helps mitigate the interrupts a
+               bit and probably renders the max_tx_latency param useless.
+               
+               Update: Putting a DescIntr bit on all of the descriptors and
+               mitigating interrupt frequency with the tx_min_pkt parameter. -KDU
+       */
+       if (entry >= TX_RING_SIZE-1)             /* Wrap ring */
+               hmp->tx_ring[entry].status_n_length = 
+                       cpu_to_le32(DescOwn|DescEndPacket|DescEndRing|DescIntr | skb->len);
+       else
+               hmp->tx_ring[entry].status_n_length = 
+                       cpu_to_le32(DescOwn|DescEndPacket|DescIntr | skb->len);
+       hmp->cur_tx++;
+
+       /* Non-x86 Todo: explicitly flush cache lines here. */
+
+       /* Wake the potentially-idle transmit channel. */
+       /* If we don't need to read status, DON'T -KDU */
+       status=readw(dev->base_addr + TxStatus);
+       if( !(status & 0x0001) || (status & 0x0002))
+               writew(0x0001, dev->base_addr + TxCmd);
+
+       /* Immediately before returning, let's clear as many entries as we can. */
+       hamachi_tx(dev);
+
+       /* We should kick the bottom half here, since we are not accepting
+        * interrupts with every packet.  i.e. realize that Gigabit ethernet
+        * can transmit faster than ordinary machines can load packets;
+        * hence, any packet that got put off because we were in the transmit
+        * routine should IMMEDIATELY get a chance to be re-queued. -KDU
+        */
+       if ((hmp->cur_tx - hmp->dirty_tx) < (TX_RING_SIZE - 4)) 
+               netif_wake_queue(dev);  /* Typical path */
+       else {
+               hmp->tx_full = 1;
+               netif_stop_queue(dev);
+       }
+       dev->trans_start = jiffies;
+
+       if (hamachi_debug > 4) {
+               printk(KERN_DEBUG "%s: Hamachi transmit frame #%d queued in slot %d.\n",
+                          dev->name, hmp->cur_tx, entry);
+       }
+       return 0;
+}
+
+/* The interrupt handler does all of the Rx thread work and cleans up
+   after the Tx thread. */
+static void hamachi_interrupt(int irq, void *dev_instance, struct pt_regs *rgs)
+{
+       struct net_device *dev = (struct net_device *)dev_instance;
+       struct hamachi_private *hmp;
+       long ioaddr, boguscnt = max_interrupt_work;
+
+#ifndef final_version                  /* Can never occur. */
+       if (dev == NULL) {
+               printk (KERN_ERR "hamachi_interrupt(): irq %d for unknown device.\n", irq);
+               return;
+       }
+#endif
+
+       ioaddr = dev->base_addr;
+       hmp = (struct hamachi_private *)dev->priv;
+       spin_lock(&hmp->lock);
+
+       do {
+               u32 intr_status = readl(ioaddr + InterruptClear);
+
+               if (hamachi_debug > 4)
+                       printk(KERN_DEBUG "%s: Hamachi interrupt, status %4.4x.\n",
+                                  dev->name, intr_status);
+
+               if (intr_status == 0)
+                       break;
+
+               if (intr_status & IntrRxDone)
+                       hamachi_rx(dev);
+
+               if (intr_status & IntrTxDone){
+                       /* This code should RARELY need to execute. After all, this is
+                        * a gigabit link, it should consume packets as fast as we put
+                        * them in AND we clear the Tx ring in hamachi_start_xmit().
+                        */ 
+                       if (hmp->tx_full){
+                               for (; hmp->cur_tx - hmp->dirty_tx > 0; hmp->dirty_tx++){
+                                       int entry = hmp->dirty_tx % TX_RING_SIZE;
+                                       if (hmp->tx_ring[entry].status_n_length & cpu_to_le32(DescOwn)) 
+                                               break;
+                                       /* Free the original skb. */
+                                       if (hmp->tx_skbuff[entry]){
+                                               dev_kfree_skb_irq(hmp->tx_skbuff[entry]);
+                                               hmp->tx_skbuff[entry] = 0;
+                                       }
+                                       hmp->tx_ring[entry].status_n_length = 0;
+                                       if (entry >= TX_RING_SIZE-1)  
+                                               hmp->tx_ring[TX_RING_SIZE-1].status_n_length |= 
+                                                       cpu_to_le32(DescEndRing);
+                                       hmp->stats.tx_packets++;
+                               }
+                               if (hmp->cur_tx - hmp->dirty_tx < TX_RING_SIZE - 4){
+                                       /* The ring is no longer full */
+                                       hmp->tx_full = 0;
+                                       netif_wake_queue(dev);
+                               }
+                       } else {
+                               netif_wake_queue(dev);
+                       }
+               }
+
+
+               /* Abnormal error summary/uncommon events handlers. */
+               if (intr_status &
+                       (IntrTxPCIFault | IntrTxPCIErr | IntrRxPCIFault | IntrRxPCIErr |
+                        LinkChange | NegotiationChange | StatsMax))
+                       hamachi_error(dev, intr_status);
+
+               if (--boguscnt < 0) {
+                       printk(KERN_WARNING "%s: Too much work at interrupt, status=0x%4.4x.\n",
+                                  dev->name, intr_status);
+                       break;
+               }
+       } while (1);
+
+       if (hamachi_debug > 3)
+               printk(KERN_DEBUG "%s: exiting interrupt, status=%#4.4x.\n",
+                          dev->name, readl(ioaddr + IntrStatus));
+
+#ifndef final_version
+       /* Code that should never be run!  Perhaps remove after testing.. */
+       {
+               static int stopit = 10;
+               if (dev->start == 0  &&  --stopit < 0) {
+                       printk(KERN_ERR "%s: Emergency stop, looping startup interrupt.\n",
+                                  dev->name);
+                       free_irq(irq, dev);
+               }
+       }
+#endif
+
+       spin_unlock(&hmp->lock);
+}
+
+#ifdef TX_CHECKSUM
+/*
+ * Copied from eth_type_trans(), with reduced header, since we don't
+ * get it on RX, only on TX.
+ */
+static unsigned short hamachi_eth_type_trans(struct sk_buff *skb,
+  struct net_device *dev)
+{
+       struct ethhdr *eth;
+       unsigned char *rawp;
+       
+       skb->mac.raw=skb->data;
+       skb_pull(skb,dev->hard_header_len-8);  /* artificially enlarged on tx */
+       eth= skb->mac.ethernet;
+       
+       if(*eth->h_dest&1)
+       {
+               if(memcmp(eth->h_dest,dev->broadcast, ETH_ALEN)==0)
+                       skb->pkt_type=PACKET_BROADCAST;
+               else
+                       skb->pkt_type=PACKET_MULTICAST;
+       }
+       
+       /*
+        *      This ALLMULTI check should be redundant by 1.4
+        *      so don't forget to remove it.
+        *
+        *      Seems, you forgot to remove it. All silly devices
+        *      seems to set IFF_PROMISC.
+        */
+        
+       else if(dev->flags&(IFF_PROMISC/*|IFF_ALLMULTI*/))
+       {
+               if(memcmp(eth->h_dest,dev->dev_addr, ETH_ALEN))
+                       skb->pkt_type=PACKET_OTHERHOST;
+       }
+       
+       if (ntohs(eth->h_proto) >= 1536)
+               return eth->h_proto;
+               
+       rawp = skb->data;
+       
+       /*
+        *      This is a magic hack to spot IPX packets. Older Novell breaks
+        *      the protocol design and runs IPX over 802.3 without an 802.2 LLC
+        *      layer. We look for FFFF which isn't a used 802.2 SSAP/DSAP. This
+        *      won't work for fault tolerant netware but does for the rest.
+        */
+       if (*(unsigned short *)rawp == 0xFFFF)
+               return htons(ETH_P_802_3);
+               
+       /*
+        *      Real 802.2 LLC
+        */
+       return htons(ETH_P_802_2);
+}
+#endif  /* TX_CHECKSUM */
+
+/* This routine is logically part of the interrupt handler, but seperated
+   for clarity and better register allocation. */
+static int hamachi_rx(struct net_device *dev)
+{
+       struct hamachi_private *hmp = (struct hamachi_private *)dev->priv;
+       int entry = hmp->cur_rx % RX_RING_SIZE;
+       int boguscnt = (hmp->dirty_rx + RX_RING_SIZE) - hmp->cur_rx;
+
+       if (hamachi_debug > 4) {
+               printk(KERN_DEBUG " In hamachi_rx(), entry %d status %4.4x.\n",
+                          entry, hmp->rx_ring[entry].status_n_length);
+       }
+
+       /* If EOP is set on the next entry, it's a new packet. Send it up. */
+       while ( ! (hmp->rx_head_desc->status_n_length & cpu_to_le32(DescOwn))) {
+               struct hamachi_desc *desc = hmp->rx_head_desc;
+               u32 desc_status = le32_to_cpu(desc->status_n_length);
+               u16 data_size = desc_status;                    /* Implicit truncate */
+               u8 *buf_addr = le32desc_to_virt(desc->addr);
+               s32 frame_status = 
+                       le32_to_cpu(get_unaligned((s32*)&(buf_addr[data_size - 12])));
+               
+               if (hamachi_debug > 4)
+                       printk(KERN_DEBUG "  hamachi_rx() status was %8.8x.\n",
+                               frame_status);
+               if (--boguscnt < 0)
+                       break;
+               if ( ! (desc_status & DescEndPacket)) {
+                       printk(KERN_WARNING "%s: Oversized Ethernet frame spanned "
+                                  "multiple buffers, entry %#x length %d status %4.4x!\n",
+                                  dev->name, hmp->cur_rx, data_size, desc_status);
+                       printk(KERN_WARNING "%s: Oversized Ethernet frame %p vs %p.\n",
+                                  dev->name, desc, &hmp->rx_ring[hmp->cur_rx % RX_RING_SIZE]);
+                       printk(KERN_WARNING "%s: Oversized Ethernet frame -- next status %x/%x last status %x.\n",
+                                  dev->name,
+                                  hmp->rx_ring[(hmp->cur_rx+1) % RX_RING_SIZE].status_n_length & 0xffff0000,
+                                  hmp->rx_ring[(hmp->cur_rx+1) % RX_RING_SIZE].status_n_length & 0x0000ffff,
+                                  hmp->rx_ring[(hmp->cur_rx-1) % RX_RING_SIZE].status_n_length);
+                       hmp->stats.rx_length_errors++;
+               } /* else  Omit for prototype errata??? */
+               if (frame_status & 0x00380000) {
+                       /* There was an error. */
+                       if (hamachi_debug > 2)
+                               printk(KERN_DEBUG "  hamachi_rx() Rx error was %8.8x.\n",
+                                          frame_status);
+                       hmp->stats.rx_errors++;
+                       if (frame_status & 0x00600000) hmp->stats.rx_length_errors++;
+                       if (frame_status & 0x00080000) hmp->stats.rx_frame_errors++;
+                       if (frame_status & 0x00100000) hmp->stats.rx_crc_errors++;
+                       if (frame_status < 0) hmp->stats.rx_dropped++;
+               } else {
+                       struct sk_buff *skb;
+                       /* Omit CRC */
+                       u16 pkt_len = (frame_status & 0x07ff) - 4;      
+#ifdef RX_CHECKSUM
+                       u32 pfck = *(u32 *) &buf_addr[data_size - 8];
+#endif
+
+
+#ifndef final_version
+                       if (hamachi_debug > 4)
+                               printk(KERN_DEBUG "  hamachi_rx() normal Rx pkt length %d"
+                                          " of %d, bogus_cnt %d.\n",
+                                          pkt_len, data_size, boguscnt);
+                       if (hamachi_debug > 5)
+                               printk(KERN_DEBUG"%s:  rx status %8.8x %8.8x %8.8x %8.8x %8.8x.\n",
+                                          dev->name,
+                                          *(s32*)&(buf_addr[data_size - 20]),
+                                          *(s32*)&(buf_addr[data_size - 16]),
+                                          *(s32*)&(buf_addr[data_size - 12]),
+                                          *(s32*)&(buf_addr[data_size - 8]),
+                                          *(s32*)&(buf_addr[data_size - 4]));
+#endif
+                       /* Check if the packet is long enough to accept without copying
+                          to a minimally-sized skbuff. */
+                       if (pkt_len < rx_copybreak
+                               && (skb = dev_alloc_skb(pkt_len + 2)) != NULL) {
+#ifdef RX_CHECKSUM
+                               printk(KERN_ERR "%s: rx_copybreak non-zero "
+                                 "not good with RX_CHECKSUM\n", dev->name);
+#endif
+                               skb->dev = dev;
+                               skb_reserve(skb, 2);    /* 16 byte align the IP header */
+                               /* Call copy + cksum if available. */
+#if 1 || USE_IP_COPYSUM
+                               eth_copy_and_sum(skb, bus_to_virt(desc->addr), pkt_len, 0);
+                               skb_put(skb, pkt_len);
+#else
+                               memcpy(skb_put(skb, pkt_len), bus_to_virt(desc->addr),pkt_len);
+#endif
+                       } else {
+                               char *temp = skb_put(skb = hmp->rx_skbuff[entry], pkt_len);
+                               hmp->rx_skbuff[entry] = NULL;
+#ifndef final_version                          /* Remove after testing. */
+                               if (bus_to_virt(desc->addr) != temp)
+                                       printk(KERN_ERR "%s: Internal fault: The skbuff addresses "
+                                                  "do not match in hamachi_rx: %p vs. %p / %p.\n",
+                                                  dev->name, bus_to_virt(desc->addr),
+                                                  skb->head, temp);
+#else
+                               (void) temp;
+#endif
+                       }
+#ifdef TX_CHECKSUM
+                       /* account for extra TX hard_header bytes */
+                       skb->protocol = hamachi_eth_type_trans(skb, dev);
+#else
+                       skb->protocol = eth_type_trans(skb, dev);
+#endif
+
+
+#ifdef RX_CHECKSUM
+                       /* TCP or UDP on ipv4, DIX encoding */
+                       if (pfck>>24 == 0x91 || pfck>>24 == 0x51) {
+                               struct iphdr *ih = (struct iphdr *) skb->data;
+                               /* Check that IP packet is at least 46 bytes, otherwise,
+                                * there may be pad bytes included in the hardware checksum.
+                                * This wouldn't happen if everyone padded with 0.
+                                */
+                               if (ntohs(ih->tot_len) >= 46){
+                                       /* don't worry about frags */
+                                       if (!(ih->frag_off & __constant_htons(IP_MF|IP_OFFSET))) {
+                                               u32 inv = *(u32 *) &buf_addr[data_size - 16];
+                                               u32 *p = (u32 *) &buf_addr[data_size - 20];
+                                               register u32 crc, p_r, p_r1;
+
+                                               if (inv & 4) {
+                                                       inv &= ~4;
+                                                       --p;
+                                               }
+                                               p_r = *p;
+                                               p_r1 = *(p-1);
+                                               switch (inv) {
+                                                       case 0: 
+                                                                               crc = (p_r & 0xffff) + (p_r >> 16);
+                                                                               break;
+                                                       case 1: 
+                                                                               crc = (p_r >> 16) + (p_r & 0xffff)
+                                                                                       + (p_r1 >> 16 & 0xff00); 
+                                                                               break;
+                                                       case 2: 
+                                                                               crc = p_r + (p_r1 >> 16); 
+                                                                               break;
+                                                       case 3: 
+                                                                               crc = p_r + (p_r1 & 0xff00) + (p_r1 >> 16); 
+                                                                               break;
+                                                       default:        /*NOTREACHED*/ crc = 0;
+                                               }
+                                               if (crc & 0xffff0000) {
+                                                       crc &= 0xffff;
+                                                       ++crc;
+                                               }
+                                               /* tcp/udp will add in pseudo */
+                                               skb->csum = ntohs(pfck & 0xffff);
+                                               if (skb->csum > crc)
+                                                       skb->csum -= crc;
+                                               else
+                                                       skb->csum += (~crc & 0xffff);
+                                               /*
+                                               * could do the pseudo myself and return
+                                               * CHECKSUM_UNNECESSARY
+                                               */
+                                               skb->ip_summed = CHECKSUM_HW;
+                                       }
+                               }       
+                       }
+#endif  /* RX_CHECKSUM */
+
+                       netif_rx(skb);
+                       dev->last_rx = jiffies;
+                       hmp->stats.rx_packets++;
+               }
+               entry = (++hmp->cur_rx) % RX_RING_SIZE;
+               hmp->rx_head_desc = &hmp->rx_ring[entry];
+       }
+
+       /* Refill the Rx ring buffers. */
+       for (; hmp->cur_rx - hmp->dirty_rx > 0; hmp->dirty_rx++) {
+               struct sk_buff *skb;
+               entry = hmp->dirty_rx % RX_RING_SIZE;
+               if (hmp->rx_skbuff[entry] == NULL) {
+                       skb = dev_alloc_skb(hmp->rx_buf_sz);
+                       hmp->rx_skbuff[entry] = skb;
+                       if (skb == NULL)
+                               break;                                  /* Better luck next round. */
+                       skb->dev = dev;                 /* Mark as being used by this device. */
+                       skb_reserve(skb, 2);            /* Align IP on 16 byte boundaries */
+                       hmp->rx_ring[entry].addr = virt_to_desc(skb->tail);
+               }
+               hmp->rx_ring[entry].status_n_length = cpu_to_le32(hmp->rx_buf_sz);
+               if (entry >= RX_RING_SIZE-1)
+                       hmp->rx_ring[entry].status_n_length |=
+                               cpu_to_le32(DescOwn | DescEndPacket | DescEndRing | DescIntr);
+               else
+                       hmp->rx_ring[entry].status_n_length |= 
+                               cpu_to_le32(DescOwn | DescEndPacket | DescIntr);
+       }
+
+       /* Restart Rx engine if stopped. */
+       /* If we don't need to check status, don't. -KDU */
+       if (readw(dev->base_addr + RxStatus) & 0x0002)
+               writew(0x0001, dev->base_addr + RxCmd);
+
+       return 0;
+}
+
+/* This is more properly named "uncommon interrupt events", as it covers more
+   than just errors. */
+static void hamachi_error(struct net_device *dev, int intr_status)
+{
+       long ioaddr = dev->base_addr;
+       struct hamachi_private *hmp = (struct hamachi_private *)dev->priv;
+
+       if (intr_status & (LinkChange|NegotiationChange)) {
+               if (hamachi_debug > 1)
+                       printk(KERN_INFO "%s: Link changed: AutoNegotiation Ctrl"
+                                  " %4.4x, Status %4.4x %4.4x Intr status %4.4x.\n",
+                                  dev->name, readw(ioaddr + 0x0E0), readw(ioaddr + 0x0E2),
+                                  readw(ioaddr + ANLinkPartnerAbility),
+                                  readl(ioaddr + IntrStatus));
+               if (readw(ioaddr + ANStatus) & 0x20)
+                       writeb(0x01, ioaddr + LEDCtrl);
+               else
+                       writeb(0x03, ioaddr + LEDCtrl);
+       }
+       if (intr_status & StatsMax) {
+               hamachi_get_stats(dev);
+               /* Read the overflow bits to clear. */
+               readl(ioaddr + 0x370);
+               readl(ioaddr + 0x3F0);
+       }
+       if ((intr_status & ~(LinkChange|StatsMax|NegotiationChange|IntrRxDone|IntrTxDone))
+               && hamachi_debug)
+               printk(KERN_ERR "%s: Something Wicked happened! %4.4x.\n",
+                          dev->name, intr_status);
+       /* Hmmmmm, it's not clear how to recover from PCI faults. */
+       if (intr_status & (IntrTxPCIErr | IntrTxPCIFault))
+               hmp->stats.tx_fifo_errors++;
+       if (intr_status & (IntrRxPCIErr | IntrRxPCIFault))
+               hmp->stats.rx_fifo_errors++;
+}
+
+static int hamachi_close(struct net_device *dev)
+{
+       long ioaddr = dev->base_addr;
+       struct hamachi_private *hmp = (struct hamachi_private *)dev->priv;
+       int i;
+
+       netif_stop_queue(dev);
+
+       if (hamachi_debug > 1) {
+               printk(KERN_DEBUG "%s: Shutting down ethercard, status was Tx %4.4x Rx %4.4x Int %2.2x.\n",
+                          dev->name, readw(ioaddr + TxStatus),
+                          readw(ioaddr + RxStatus), readl(ioaddr + IntrStatus));
+               printk(KERN_DEBUG "%s: Queue pointers were Tx %d / %d,  Rx %d / %d.\n",
+                          dev->name, hmp->cur_tx, hmp->dirty_tx, hmp->cur_rx, hmp->dirty_rx);
+       }
+
+       /* Disable interrupts by clearing the interrupt mask. */
+       writel(0x0000, ioaddr + InterruptEnable);
+
+       /* Stop the chip's Tx and Rx processes. */
+       writel(2, ioaddr + RxCmd);
+       writew(2, ioaddr + TxCmd);
+
+#ifdef __i386__
+       if (hamachi_debug > 2) {
+               printk("\n"KERN_DEBUG"  Tx ring at %8.8x:\n",
+                          (int)virt_to_bus(hmp->tx_ring));
+               for (i = 0; i < TX_RING_SIZE; i++)
+                       printk(" %c #%d desc. %8.8x %8.8x.\n",
+                                  readl(ioaddr + TxCurPtr) == (long)&hmp->tx_ring[i] ? '>' : ' ',
+                                  i, hmp->tx_ring[i].status_n_length, hmp->tx_ring[i].addr);
+               printk("\n"KERN_DEBUG "  Rx ring %8.8x:\n",
+                          (int)virt_to_bus(hmp->rx_ring));
+               for (i = 0; i < RX_RING_SIZE; i++) {
+                       printk(KERN_DEBUG " %c #%d desc. %4.4x %8.8x\n",
+                                  readl(ioaddr + RxCurPtr) == (long)&hmp->rx_ring[i] ? '>' : ' ',
+                                  i, hmp->rx_ring[i].status_n_length, hmp->rx_ring[i].addr);
+                       if (hamachi_debug > 6) {
+                               if (*(u8*)bus_to_virt(hmp->rx_ring[i].addr) != 0x69) {
+                                       int j;
+                                       for (j = 0; j < 0x50; j++)
+                                               printk(" %4.4x",((u16*)le32desc_to_virt(hmp->rx_ring[i].addr))[j]);
+                                       printk("\n");
+                               }
+                       }
+               }
+       }
+#endif /* __i386__ debugging only */
+
+       free_irq(dev->irq, dev);
+
+       del_timer_sync(&hmp->timer);
+
+       /* Free all the skbuffs in the Rx queue. */
+       for (i = 0; i < RX_RING_SIZE; i++) {
+               hmp->rx_ring[i].status_n_length = 0;
+               hmp->rx_ring[i].addr = 0xBADF00D0; /* An invalid address. */
+               if (hmp->rx_skbuff[i]) {
+#if LINUX_VERSION_CODE < 0x20100
+                       hmp->rx_skbuff[i]->free = 1;
+#endif
+                       dev_kfree_skb(hmp->rx_skbuff[i]);
+               }
+               hmp->rx_skbuff[i] = 0;
+       }
+       for (i = 0; i < TX_RING_SIZE; i++) {
+               if (hmp->tx_skbuff[i])
+                       dev_kfree_skb(hmp->tx_skbuff[i]);
+               hmp->tx_skbuff[i] = 0;
+       }
+
+       writeb(0x00, ioaddr + LEDCtrl);
+
+       MOD_DEC_USE_COUNT;
+
+       return 0;
+}
+
+static struct net_device_stats *hamachi_get_stats(struct net_device *dev)
+{
+       long ioaddr = dev->base_addr;
+       struct hamachi_private *hmp = (struct hamachi_private *)dev->priv;
+
+       /* We should lock this segment of code for SMP eventually, although
+          the vulnerability window is very small and statistics are
+          non-critical. */
+        /* Ok, what goes here?  This appears to be stuck at 21 packets
+           according to ifconfig.  It does get incremented in hamachi_tx(),
+           so I think I'll comment it out here and see if better things
+           happen.
+        */ 
+       /* hmp->stats.tx_packets        = readl(ioaddr + 0x000); */
+
+#if LINUX_VERSION_CODE >= 0x20119
+       hmp->stats.rx_bytes = readl(ioaddr + 0x330); /* Total Uni+Brd+Multi */
+       hmp->stats.tx_bytes = readl(ioaddr + 0x3B0); /* Total Uni+Brd+Multi */
+#endif
+       hmp->stats.multicast            = readl(ioaddr + 0x320); /* Multicast Rx */
+
+       hmp->stats.rx_length_errors     = readl(ioaddr + 0x368); /* Over+Undersized */
+       hmp->stats.rx_over_errors       = readl(ioaddr + 0x35C); /* Jabber */
+       hmp->stats.rx_crc_errors        = readl(ioaddr + 0x360); /* Jabber */
+       hmp->stats.rx_frame_errors      = readl(ioaddr + 0x364); /* Symbol Errs */
+       hmp->stats.rx_missed_errors     = readl(ioaddr + 0x36C); /* Dropped */
+
+       return &hmp->stats;
+}
+
+static void set_rx_mode(struct net_device *dev)
+{
+       long ioaddr = dev->base_addr;
+
+       if (dev->flags & IFF_PROMISC) {                 /* Set promiscuous. */
+               /* Unconditionally log net taps. */
+               printk(KERN_NOTICE "%s: Promiscuous mode enabled.\n", dev->name);
+               writew(0x000F, ioaddr + AddrMode);
+       } else if ((dev->mc_count > 63)  ||  (dev->flags & IFF_ALLMULTI)) {
+               /* Too many to match, or accept all multicasts. */
+               writew(0x000B, ioaddr + AddrMode);
+       } else if (dev->mc_count > 0) { /* Must use the CAM filter. */
+               struct dev_mc_list *mclist;
+               int i;
+               for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count;
+                        i++, mclist = mclist->next) {
+                       writel(*(u32*)(mclist->dmi_addr), ioaddr + 0x100 + i*8);
+                       writel(0x20000 | (*(u16*)&mclist->dmi_addr[4]),
+                                  ioaddr + 0x104 + i*8);
+               }
+               /* Clear remaining entries. */
+               for (; i < 64; i++)
+                       writel(0, ioaddr + 0x104 + i*8);
+               writew(0x0003, ioaddr + AddrMode);
+       } else {                                        /* Normal, unicast/broadcast-only mode. */
+               writew(0x0001, ioaddr + AddrMode);
+       }
+}
+
+#ifdef HAVE_PRIVATE_IOCTL
+static int mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+       long ioaddr = dev->base_addr;
+       u16 *data = (u16 *)&rq->ifr_data;
+
+       switch(cmd) {
+       case SIOCDEVPRIVATE:            /* Get the address of the PHY in use. */
+               data[0] = ((struct hamachi_private *)dev->priv)->phys[0] & 0x1f;
+               /* Fall Through */
+       case SIOCDEVPRIVATE+1:          /* Read the specified MII register. */
+               data[3] = mdio_read(ioaddr, data[0] & 0x1f, data[1] & 0x1f);
+               return 0;
+       case SIOCDEVPRIVATE+2:          /* Write the specified MII register */
+               /* TODO: Check the sequencing of this.  Might need to stop and
+                * restart Rx and Tx engines. -KDU
+                */
+               if (!capable(CAP_NET_ADMIN))
+                       return -EPERM;
+               mdio_write(ioaddr, data[0] & 0x1f, data[1] & 0x1f, data[2]);
+               return 0;
+       case SIOCDEVPRIVATE+3: { /* set rx,tx intr params */
+               u32 *d = (u32 *)&rq->ifr_data;
+               /* Should add this check here or an ordinary user can do nasty
+                * things. -KDU
+                *
+                * TODO: Shut down the Rx and Tx engines while doing this.
+                */
+               if (!capable(CAP_NET_ADMIN))
+                       return -EPERM;
+               writel(d[0], dev->base_addr + TxIntrCtrl);
+               writel(d[1], dev->base_addr + RxIntrCtrl);
+               printk(KERN_NOTICE "%s: tx %08x, rx %08x intr\n", dev->name,
+                 (u32) readl(dev->base_addr + TxIntrCtrl),
+                 (u32) readl(dev->base_addr + RxIntrCtrl));
+               return 0;
+           }
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+#endif  /* HAVE_PRIVATE_IOCTL */
+
+
+static void __exit hamachi_remove_one (struct pci_dev *pdev)
+{
+       struct net_device *dev = pci_get_drvdata(pdev);
+
+       /* No need to check MOD_IN_USE, as sys_delete_module() checks. */
+       if (dev) {
+               unregister_netdev(dev);
+               iounmap((char *)dev->base_addr);
+               kfree(dev);
+               pci_set_drvdata(pdev, NULL);
+       }
+}
+
+static struct pci_device_id hamachi_pci_tbl[] __initdata = {
+       { 0x1318, 0x0911, PCI_ANY_ID, PCI_ANY_ID, },
+       { 0, }
+};
+MODULE_DEVICE_TABLE(pci, hamachi_pci_tbl);
+
+static struct pci_driver hamachi_driver = {
+       name:           "hamachi",
+       id_table:       hamachi_pci_tbl,
+       probe:          hamachi_init_one,
+       remove:         hamachi_remove_one,
+};
+
+static int __init hamachi_init (void)
+{
+       if (pci_register_driver(&hamachi_driver) > 0)
+               return 0;
+       pci_unregister_driver(&hamachi_driver);
+       return -ENODEV;
+}
+
+static void __exit hamachi_exit (void)
+{
+       pci_unregister_driver(&hamachi_driver);
+}
+
+
+module_init(hamachi_init);
+module_exit(hamachi_exit);
index 3be9272cb19e1808e00d4b6e6db31ec08a43797b..f460e16fd9b3fffef12680b441bd52dc16db1097 100644 (file)
@@ -145,17 +145,17 @@ extern int pppoe_init (void);
 #ifdef MODULE
 int init_module(void)
 #else
-void __init pppox_proto_init(struct net_proto *pro)
+int __init pppox_proto_init(struct net_proto *pro)
 #endif
 {
        int err = 0;
 
        err = sock_register(&pppox_proto_family);
 
-       if (err == 0)
+       if (err == 0) {
                printk(KERN_INFO "Registered PPPoX v0.5\n");
-
-       pppoe_init();
+               pppoe_init();
+       }
 
        return err;
 }
index 5d4f13375e003102ccdf191680c2118f746487b0..a8c9e5e46ff5bf5a8b6a3351542baa2b033482de 100644 (file)
@@ -1,3 +1,4 @@
+
 /*
  *     New style setup code for the network devices
  */
@@ -166,58 +167,8 @@ static void __init network_ldisc_init(void)
 }
 
 
-static void __init appletalk_device_init(void)
+static void __init special_device_init(void)
 {
-#if defined(CONFIG_IPDDP)
-       extern int ipddp_init(struct net_device *dev);
-       static struct net_device dev_ipddp = {
-                               "ipddp0"  __PAD6,
-                               0, 0, 0, 0,
-                               0x0, 0,
-                               0, 0, 0, NULL, ipddp_init 
-       };
-       
-       dev_ipddp.init(&dev_ipddp);
-#endif /* CONFIG_IPDDP */
-}
-
-static void special_device_init(void)
-{
-#ifdef CONFIG_DUMMY
-       {
-               extern int dummy_init(struct net_device *dev);
-               static struct net_device dummy_dev = {
-                       "dummy" __PAD5, 0x0, 0x0, 0x0, 0x0, 0, 0, 0, 0, 0, NULL, dummy_init, 
-               };
-               register_netdev(&dummy_dev);
-       }
-#endif 
-#ifdef CONFIG_EQUALIZER
-       {
-               extern int eql_init(struct net_device *dev);
-               static struct net_device eql_dev = 
-               {
-                       "eql" __PAD3,                   /* Master device for IP traffic load balancing */
-                       0x0, 0x0, 0x0, 0x0,             /* recv end/start; mem end/start */
-                       0,                              /* base I/O address */
-                       0,                              /* IRQ */
-                       0, 0, 0,                        /* flags */
-                       NULL,                           /* next device */
-                       eql_init                        /* set up the rest */
-               };
-               register_netdev(&eql_dev);
-       }
-#endif 
-#ifdef CONFIG_APBIF
-       {
-               extern int bif_init(struct net_device *dev);
-               static struct net_device bif_dev = 
-               {
-                       "bif" __PAD3, 0x0, 0x0, 0x0, 0x0, 0, 0, 0, 0, 0, NULL, bif_init 
-               };
-               register_netdev(&bif_dev);
-        }
-#endif
 #ifdef CONFIG_NET_SB1000
        {
                extern int sb1000_probe(struct net_device *dev);
@@ -228,15 +179,6 @@ static void special_device_init(void)
                register_netdev(&sb1000_dev);
        }
 #endif
-#ifdef CONFIG_BONDING
-       {
-               extern int bond_init(struct net_device *dev);
-               static struct net_device bond_dev = {
-                       "bond" __PAD4, 0x0, 0x0, 0x0, 0x0, 0, 0, 0, 0, 0, NULL, bond_init,
-               };
-               register_netdev(&bond_dev);
-       }
-#endif 
 }
 
 /*
@@ -249,8 +191,6 @@ void __init net_device_init(void)
        network_probe();
        /* Line disciplines */
        network_ldisc_init();
-       /* Appletalk */
-       appletalk_device_init();
        /* Special devices */
        special_device_init();
        /* That kicks off the legacy init functions */
diff --git a/drivers/net/sundance.c b/drivers/net/sundance.c
new file mode 100644 (file)
index 0000000..e4fb0ed
--- /dev/null
@@ -0,0 +1,1272 @@
+/* sundance.c: A Linux device driver for the Sundance ST201 "Alta". */
+/*
+       Written 1999-2000 by Donald Becker.
+
+       This software may be used and distributed according to the terms of
+       the GNU General Public License (GPL), incorporated herein by reference.
+       Drivers based on or derived from this code fall under the GPL and must
+       retain the authorship, copyright and license notice.  This file is not
+       a complete program and may only be used when the entire operating
+       system is licensed under the GPL.
+
+       The author may be reached as becker@scyld.com, or C/O
+       Scyld Computing Corporation
+       410 Severn Ave., Suite 210
+       Annapolis MD 21403
+
+       Support and updates available at
+       http://www.scyld.com/network/sundance.html
+*/
+
+/* These identify the driver base version and may not be removed. */
+static const char version1[] =
+"sundance.c:v1.01 4/09/00  Written by Donald Becker\n";
+static const char version2[] =
+"  http://www.scyld.com/network/sundance.html\n";
+
+/* The user-configurable values.
+   These may be modified when a driver module is loaded.*/
+
+static int debug = 1;                  /* 1 normal messages, 0 quiet .. 7 verbose. */
+/* Maximum events (Rx packets, etc.) to handle at each interrupt. */
+static int max_interrupt_work = 20;
+static int mtu = 0;
+/* Maximum number of multicast addresses to filter (vs. rx-all-multicast).
+   Typical is a 64 element hash table based on the Ethernet CRC.  */
+static int multicast_filter_limit = 32;
+
+/* Set the copy breakpoint for the copy-only-tiny-frames scheme.
+   Setting to > 1518 effectively disables this feature.
+   This chip can receive into offset buffers, so the Alpha does not
+   need a copy-align. */
+static int rx_copybreak = 0;
+
+/* Used to pass the media type, etc.
+   Both 'options[]' and 'full_duplex[]' should exist for driver
+   interoperability.
+   The media type is usually passed in 'options[]'.
+*/
+#define MAX_UNITS 8            /* More are supported, limit only on options */
+static int options[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1};
+static int full_duplex[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1};
+
+/* Operational parameters that are set at compile time. */
+
+/* Keep the ring sizes a power of two for compile efficiency.
+   The compiler will convert <unsigned>'%'<2^N> into a bit mask.
+   Making the Tx ring too large decreases the effectiveness of channel
+   bonding and packet priority, and more than 128 requires modifying the
+   Tx error recovery.
+   Large receive rings merely waste memory. */
+#define TX_RING_SIZE   16
+#define TX_QUEUE_LEN   10              /* Limit ring entries actually used.  */
+#define RX_RING_SIZE   32
+
+/* Operational parameters that usually are not changed. */
+/* Time in jiffies before concluding the transmitter is hung. */
+#define TX_TIMEOUT  (2*HZ)
+
+#define PKT_BUF_SZ             1536                    /* Size of each temporary Rx buffer.*/
+
+#ifndef __KERNEL__
+#define __KERNEL__
+#endif
+#if !defined(__OPTIMIZE__)
+#warning  You must compile this file with the correct options!
+#warning  See the last lines of the source file.
+#error You must compile this driver with "-O".
+#endif
+
+/* Include files, designed to support most kernel versions 2.0.0 and later. */
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/malloc.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/init.h>
+#include <asm/processor.h>             /* Processor type for cache alignment. */
+#include <asm/bitops.h>
+#include <asm/io.h>
+
+#include <linux/spinlock.h>
+
+
+/* Condensed operations for readability. */
+#define virt_to_le32desc(addr)  cpu_to_le32(virt_to_bus(addr))
+#define le32desc_to_virt(addr)  bus_to_virt(le32_to_cpu(addr))
+
+
+MODULE_AUTHOR("Donald Becker <becker@scyld.com>");
+MODULE_DESCRIPTION("Sundance Alta Ethernet driver");
+MODULE_PARM(max_interrupt_work, "i");
+MODULE_PARM(mtu, "i");
+MODULE_PARM(debug, "i");
+MODULE_PARM(rx_copybreak, "i");
+MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i");
+MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i");
+
+/*
+                               Theory of Operation
+
+I. Board Compatibility
+
+This driver is designed for the Sundance Technologies "Alta" ST201 chip.
+
+II. Board-specific settings
+
+III. Driver operation
+
+IIIa. Ring buffers
+
+This driver uses two statically allocated fixed-size descriptor lists
+formed into rings by a branch from the final descriptor to the beginning of
+the list.  The ring sizes are set at compile time by RX/TX_RING_SIZE.
+Some chips explicitly use only 2^N sized rings, while others use a
+'next descriptor' pointer that the driver forms into rings.
+
+IIIb/c. Transmit/Receive Structure
+
+This driver uses a zero-copy receive and transmit scheme.
+The driver allocates full frame size skbuffs for the Rx ring buffers at
+open() time and passes the skb->data field to the chip as receive data
+buffers.  When an incoming frame is less than RX_COPYBREAK bytes long,
+a fresh skbuff is allocated and the frame is copied to the new skbuff.
+When the incoming frame is larger, the skbuff is passed directly up the
+protocol stack.  Buffers consumed this way are replaced by newly allocated
+skbuffs in a later phase of receives.
+
+The RX_COPYBREAK value is chosen to trade-off the memory wasted by
+using a full-sized skbuff for small frames vs. the copying costs of larger
+frames.  New boards are typically used in generously configured machines
+and the underfilled buffers have negligible impact compared to the benefit of
+a single allocation size, so the default value of zero results in never
+copying packets.  When copying is done, the cost is usually mitigated by using
+a combined copy/checksum routine.  Copying also preloads the cache, which is
+most useful with small frames.
+
+A subtle aspect of the operation is that the IP header at offset 14 in an
+ethernet frame isn't longword aligned for further processing.
+Unaligned buffers are permitted by the Sundance hardware, so
+frames are received into the skbuff at an offset of "+2", 16-byte aligning
+the IP header.
+
+IIId. Synchronization
+
+The driver runs as two independent, single-threaded flows of control.  One
+is the send-packet routine, which enforces single-threaded use by the
+dev->tbusy flag.  The other thread is the interrupt handler, which is single
+threaded by the hardware and interrupt handling software.
+
+The send packet thread has partial control over the Tx ring and 'dev->tbusy'
+flag.  It sets the tbusy flag whenever it's queuing a Tx packet. If the next
+queue slot is empty, it clears the tbusy flag when finished otherwise it sets
+the 'lp->tx_full' flag.
+
+The interrupt handler has exclusive control over the Rx ring and records stats
+from the Tx ring.  After reaping the stats, it marks the Tx queue entry as
+empty by incrementing the dirty_tx mark. Iff the 'lp->tx_full' flag is set, it
+clears both the tx_full and tbusy flags.
+
+IV. Notes
+
+IVb. References
+
+The Sundance ST201 datasheet, preliminary version.
+http://cesdis.gsfc.nasa.gov/linux/misc/100mbps.html
+http://cesdis.gsfc.nasa.gov/linux/misc/NWay.html
+
+IVc. Errata
+
+*/
+
+\f
+
+enum pci_id_flags_bits {
+        /* Set PCI command register bits before calling probe1(). */
+        PCI_USES_IO=1, PCI_USES_MEM=2, PCI_USES_MASTER=4,
+        /* Read and map the single following PCI BAR. */
+        PCI_ADDR0=0<<4, PCI_ADDR1=1<<4, PCI_ADDR2=2<<4, PCI_ADDR3=3<<4,
+        PCI_ADDR_64BITS=0x100, PCI_NO_ACPI_WAKE=0x200, PCI_NO_MIN_LATENCY=0x400,
+};
+enum chip_capability_flags {CanHaveMII=1, };
+#ifdef USE_IO_OPS
+#define PCI_IOTYPE (PCI_USES_MASTER | PCI_USES_IO  | PCI_ADDR0)
+#else
+#define PCI_IOTYPE (PCI_USES_MASTER | PCI_USES_MEM | PCI_ADDR1)
+#endif
+
+static struct pci_device_id sundance_pci_tbl[] __devinitdata = {
+       { 0x1186, 0x1002, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+       { 0x13F0, 0x0201, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1 },
+       { 0, }
+};
+MODULE_DEVICE_TABLE(pci, sundance_pci_tbl);
+
+struct pci_id_info {
+        const char *name;
+        struct match_info {
+                int     pci, pci_mask, subsystem, subsystem_mask;
+                int revision, revision_mask;                            /* Only 8 bits. */
+        } id;
+        enum pci_id_flags_bits pci_flags;
+        int io_size;                            /* Needed for I/O region check or ioremap(). */
+        int drv_flags;                          /* Driver use, intended as capability flags. */
+};
+static struct pci_id_info pci_id_tbl[] = {
+       {"OEM Sundance Technology ST201", {0x10021186, 0xffffffff, },
+        PCI_IOTYPE, 128, CanHaveMII},
+       {"Sundance Technology Alta", {0x020113F0, 0xffffffff, },
+        PCI_IOTYPE, 128, CanHaveMII},
+       {0,},                                           /* 0 terminated list. */
+};
+
+/* This driver was written to use PCI memory space, however x86-oriented
+   hardware often uses I/O space accesses. */
+#ifdef USE_IO_OPS
+#undef readb
+#undef readw
+#undef readl
+#undef writeb
+#undef writew
+#undef writel
+#define readb inb
+#define readw inw
+#define readl inl
+#define writeb outb
+#define writew outw
+#define writel outl
+#endif
+
+/* Offsets to the device registers.
+   Unlike software-only systems, device drivers interact with complex hardware.
+   It's not useful to define symbolic names for every register bit in the
+   device.  The name can only partially document the semantics and make
+   the driver longer and more difficult to read.
+   In general, only the important configuration values or bits changed
+   multiple times should be defined symbolically.
+*/
+enum alta_offsets {
+       DMACtrl=0x00,     TxListPtr=0x04, TxDMACtrl=0x08, TxDescPoll=0x0a,
+       RxDMAStatus=0x0c, RxListPtr=0x10, RxDMACtrl=0x14, RxDescPoll=0x16,
+       LEDCtrl=0x1a, ASICCtrl=0x30,
+       EEData=0x34, EECtrl=0x36, TxThreshold=0x3c,
+       FlashAddr=0x40, FlashData=0x44, TxStatus=0x46, DownCounter=0x48,
+       IntrClear=0x4a, IntrEnable=0x4c, IntrStatus=0x4e,
+       MACCtrl0=0x50, MACCtrl1=0x52, StationAddr=0x54,
+       MaxTxSize=0x5A, RxMode=0x5c, MIICtrl=0x5e,
+       MulticastFilter0=0x60, MulticastFilter1=0x64,
+       RxOctetsLow=0x68, RxOctetsHigh=0x6a, TxOctetsLow=0x6c, TxOctetsHigh=0x6e,
+       TxFramesOK=0x70, RxFramesOK=0x72, StatsCarrierError=0x74,
+       StatsLateColl=0x75, StatsMultiColl=0x76, StatsOneColl=0x77,
+       StatsTxDefer=0x78, RxMissed=0x79, StatsTxXSDefer=0x7a, StatsTxAbort=0x7b,
+       StatsBcastTx=0x7c, StatsBcastRx=0x7d, StatsMcastTx=0x7e, StatsMcastRx=0x7f,
+       /* Aliased and bogus values! */
+       RxStatus=0x0c,
+};
+
+/* Bits in the interrupt status/mask registers. */
+enum intr_status_bits {
+       IntrSummary=0x0001, IntrPCIErr=0x0002, IntrMACCtrl=0x0008,
+       IntrTxDone=0x0004, IntrRxDone=0x0010, IntrRxStart=0x0020,
+       IntrDrvRqst=0x0040,
+       StatsMax=0x0080, LinkChange=0x0100,
+       IntrTxDMADone=0x0200, IntrRxDMADone=0x0400,
+};
+
+/* Bits in the RxMode register. */
+enum rx_mode_bits {
+       AcceptAllIPMulti=0x20, AcceptMultiHash=0x10, AcceptAll=0x08,
+       AcceptBroadcast=0x04, AcceptMulticast=0x02, AcceptMyPhys=0x01,
+};
+/* Bits in MACCtrl. */
+enum mac_ctrl0_bits {
+       EnbFullDuplex=0x20, EnbRcvLargeFrame=0x40,
+       EnbFlowCtrl=0x100, EnbPassRxCRC=0x200,
+};
+enum mac_ctrl1_bits {
+       StatsEnable=0x0020,     StatsDisable=0x0040, StatsEnabled=0x0080,
+       TxEnable=0x0100, TxDisable=0x0200, TxEnabled=0x0400,
+       RxEnable=0x0800, RxDisable=0x1000, RxEnabled=0x2000,
+};
+
+/* The Rx and Tx buffer descriptors. */
+/* Note that using only 32 bit fields simplifies conversion to big-endian
+   architectures. */
+struct netdev_desc {
+       u32 next_desc;
+       u32 status;
+       struct desc_frag { u32 addr, length; } frag[1];
+};
+
+/* Bits in netdev_desc.status */
+enum desc_status_bits {
+       DescOwn=0x8000, DescEndPacket=0x4000, DescEndRing=0x2000,
+       LastFrag=0x80000000, DescIntrOnTx=0x8000, DescIntrOnDMADone=0x80000000,
+};
+
+#define PRIV_ALIGN     15      /* Required alignment mask */
+/* Use  __attribute__((aligned (L1_CACHE_BYTES)))  to maintain alignment
+   within the structure. */
+struct netdev_private {
+       /* Descriptor rings first for alignment. */
+       struct netdev_desc rx_ring[RX_RING_SIZE];
+       struct netdev_desc tx_ring[TX_RING_SIZE];
+       /* The addresses of receive-in-place skbuffs. */
+       struct sk_buff* rx_skbuff[RX_RING_SIZE];
+       /* The saved address of a sent-in-place packet/buffer, for later free(). */
+       struct sk_buff* tx_skbuff[TX_RING_SIZE];
+       struct net_device_stats stats;
+       struct timer_list timer;        /* Media monitoring timer. */
+       /* Frequently used values: keep some adjacent for cache effect. */
+       spinlock_t lock;
+       int chip_id, drv_flags;
+       /* Note: Cache paragraph grouped variables. */
+       struct netdev_desc *rx_head_desc;
+       unsigned int cur_rx, dirty_rx;          /* Producer/consumer ring indices */
+       unsigned int rx_buf_sz;                         /* Based on MTU+slack. */
+       spinlock_t txlock;                                      /* Group with Tx control cache line. */
+       struct netdev_desc *last_tx;            /* Last Tx descriptor used. */
+       unsigned int cur_tx, dirty_tx;
+       unsigned int tx_full:1;                         /* The Tx queue is full. */
+       /* These values are keep track of the transceiver/media in use. */
+       unsigned int full_duplex:1;                     /* Full-duplex operation requested. */
+       unsigned int duplex_lock:1;
+       unsigned int medialock:1;                       /* Do not sense media. */
+       unsigned int default_port:4;            /* Last dev->if_port value. */
+       /* Multicast and receive mode. */
+       spinlock_t mcastlock;                           /* SMP lock multicast updates. */
+       u16 mcast_filter[4];
+       /* MII transceiver section. */
+       int mii_cnt;                                            /* MII device addresses. */
+       u16 advertising;                                        /* NWay media advertisement */
+       unsigned char phys[2];                          /* MII device addresses. */
+};
+
+/* The station address location in the EEPROM. */
+#define EEPROM_SA_OFFSET       0x10
+
+static int  eeprom_read(long ioaddr, int location);
+static int  mdio_read(struct net_device *dev, int phy_id, int location);
+static void mdio_write(struct net_device *dev, int phy_id, int location, int value);
+static int  netdev_open(struct net_device *dev);
+static void check_duplex(struct net_device *dev);
+static void netdev_timer(unsigned long data);
+static void tx_timeout(struct net_device *dev);
+static void init_ring(struct net_device *dev);
+static int  start_tx(struct sk_buff *skb, struct net_device *dev);
+static void intr_handler(int irq, void *dev_instance, struct pt_regs *regs);
+static void netdev_error(struct net_device *dev, int intr_status);
+static int  netdev_rx(struct net_device *dev);
+static void netdev_error(struct net_device *dev, int intr_status);
+static void set_rx_mode(struct net_device *dev);
+static struct net_device_stats *get_stats(struct net_device *dev);
+static int mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
+static int  netdev_close(struct net_device *dev);
+
+\f
+
+static int __devinit sundance_probe1 (struct pci_dev *pdev,
+                                     const struct pci_device_id *ent)
+{
+       struct net_device *dev;
+       struct netdev_private *np;
+       static int card_idx;
+       int chip_idx = ent->driver_data;
+       int irq = pdev->irq;
+       int i, option = card_idx < MAX_UNITS ? options[card_idx] : 0;
+       long ioaddr;
+
+       if (pci_enable_device(pdev))
+               return -EIO;
+       pci_set_master(pdev);
+
+       dev = init_etherdev(NULL, sizeof(*np));
+       if (!dev)
+               return -ENOMEM;
+
+#ifdef USE_IO_OPS
+       ioaddr = pci_resource_start(pdev, 0);
+       if (!request_region(ioaddr, pci_id_tbl[chip_idx].io_size, dev->name))
+               goto err_out_netdev;
+#else
+       ioaddr = pci_resource_start(pdev, 1);
+       if (!request_mem_region(ioaddr, pci_id_tbl[chip_idx].io_size, dev->name))
+               goto err_out_netdev;
+       ioaddr = (long) ioremap (ioaddr, pci_id_tbl[chip_idx].io_size);
+       if (!ioaddr)
+               goto err_out_iomem;
+#endif
+
+       printk(KERN_INFO "%s: %s at 0x%lx, ",
+                  dev->name, pci_id_tbl[chip_idx].name, ioaddr);
+
+       for (i = 0; i < 3; i++)
+               ((u16 *)dev->dev_addr)[i] =
+                       le16_to_cpu(eeprom_read(ioaddr, i + EEPROM_SA_OFFSET));
+       for (i = 0; i < 5; i++)
+                       printk("%2.2x:", dev->dev_addr[i]);
+       printk("%2.2x, IRQ %d.\n", dev->dev_addr[i], irq);
+
+       dev->base_addr = ioaddr;
+       dev->irq = irq;
+
+       np = dev->priv;
+       np->chip_id = chip_idx;
+       np->drv_flags = pci_id_tbl[chip_idx].drv_flags;
+       spin_lock_init(&np->lock);
+
+       if (dev->mem_start)
+               option = dev->mem_start;
+
+       /* The lower four bits are the media type. */
+       if (option > 0) {
+               if (option & 0x200)
+                       np->full_duplex = 1;
+               np->default_port = option & 15;
+               if (np->default_port)
+                       np->medialock = 1;
+       }
+       if (card_idx < MAX_UNITS  &&  full_duplex[card_idx] > 0)
+               np->full_duplex = 1;
+
+       if (np->full_duplex)
+               np->duplex_lock = 1;
+
+       /* The chip-specific entries in the device structure. */
+       dev->open = &netdev_open;
+       dev->hard_start_xmit = &start_tx;
+       dev->stop = &netdev_close;
+       dev->get_stats = &get_stats;
+       dev->set_multicast_list = &set_rx_mode;
+       dev->do_ioctl = &mii_ioctl;
+       dev->tx_timeout = &tx_timeout;
+       dev->watchdog_timeo = TX_TIMEOUT;
+
+       if (mtu)
+               dev->mtu = mtu;
+
+       if (1) {
+               int phy, phy_idx = 0;
+               np->phys[0] = 1;                /* Default setting */
+               for (phy = 0; phy < 32 && phy_idx < 4; phy++) {
+                       int mii_status = mdio_read(dev, phy, 1);
+                       if (mii_status != 0xffff  &&  mii_status != 0x0000) {
+                               np->phys[phy_idx++] = phy;
+                               np->advertising = mdio_read(dev, phy, 4);
+                               printk(KERN_INFO "%s: MII PHY found at address %d, status "
+                                          "0x%4.4x advertising %4.4x.\n",
+                                          dev->name, phy, mii_status, np->advertising);
+                       }
+               }
+               np->mii_cnt = phy_idx;
+               if (phy_idx == 0)
+                       printk(KERN_INFO "%s: No MII transceiver found!, ASIC status %x\n",
+                                  dev->name, readl(ioaddr + ASICCtrl));
+       }
+
+       /* Perhaps move the reset here? */
+       /* Reset the chip to erase previous misconfiguration. */
+       if (debug > 1)
+               printk("ASIC Control is %x.\n", readl(ioaddr + ASICCtrl));
+       writew(0x007f, ioaddr + ASICCtrl + 2);
+       if (debug > 1)
+               printk("ASIC Control is now %x.\n", readl(ioaddr + ASICCtrl));
+
+       card_idx++;
+       return 0;
+
+#ifndef USE_IO_OPS
+err_out_iomem:
+       release_mem_region(pci_resource_start(pdev, 1),
+                          pci_id_tbl[chip_idx].io_size);
+#endif
+err_out_netdev:
+       unregister_netdev (dev);
+       kfree (dev);
+       return -ENODEV;
+}
+
+\f
+/* Read the EEPROM and MII Management Data I/O (MDIO) interfaces. */
+static int eeprom_read(long ioaddr, int location)
+{
+       int boguscnt = 1000;            /* Typical 190 ticks. */
+       writew(0x0200 | (location & 0xff), ioaddr + EECtrl);
+       do {
+               if (! (readw(ioaddr + EECtrl) & 0x8000)) {
+                       return readw(ioaddr + EEData);
+               }
+       } while (--boguscnt > 0);
+       return 0;
+}
+
+/*  MII transceiver control section.
+       Read and write the MII registers using software-generated serial
+       MDIO protocol.  See the MII specifications or DP83840A data sheet
+       for details.
+
+       The maximum data clock rate is 2.5 Mhz.  The minimum timing is usually
+       met by back-to-back 33Mhz PCI cycles. */
+#define mdio_delay() readb(mdio_addr)
+
+/* Set iff a MII transceiver on any interface requires mdio preamble.
+   This only set with older tranceivers, so the extra
+   code size of a per-interface flag is not worthwhile. */
+static char mii_preamble_required = 0;
+
+enum mii_reg_bits {
+       MDIO_ShiftClk=0x0001, MDIO_Data=0x0002, MDIO_EnbOutput=0x0004,
+};
+#define MDIO_EnbIn  (0)
+#define MDIO_WRITE0 (MDIO_EnbOutput)
+#define MDIO_WRITE1 (MDIO_Data | MDIO_EnbOutput)
+
+/* Generate the preamble required for initial synchronization and
+   a few older transceivers. */
+static void mdio_sync(long mdio_addr)
+{
+       int bits = 32;
+
+       /* Establish sync by sending at least 32 logic ones. */
+       while (--bits >= 0) {
+               writeb(MDIO_WRITE1, mdio_addr);
+               mdio_delay();
+               writeb(MDIO_WRITE1 | MDIO_ShiftClk, mdio_addr);
+               mdio_delay();
+       }
+}
+
+static int mdio_read(struct net_device *dev, int phy_id, int location)
+{
+       long mdio_addr = dev->base_addr + MIICtrl;
+       int mii_cmd = (0xf6 << 10) | (phy_id << 5) | location;
+       int i, retval = 0;
+
+       if (mii_preamble_required)
+               mdio_sync(mdio_addr);
+
+       /* Shift the read command bits out. */
+       for (i = 15; i >= 0; i--) {
+               int dataval = (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0;
+
+               writeb(dataval, mdio_addr);
+               mdio_delay();
+               writeb(dataval | MDIO_ShiftClk, mdio_addr);
+               mdio_delay();
+       }
+       /* Read the two transition, 16 data, and wire-idle bits. */
+       for (i = 19; i > 0; i--) {
+               writeb(MDIO_EnbIn, mdio_addr);
+               mdio_delay();
+               retval = (retval << 1) | ((readb(mdio_addr) & MDIO_Data) ? 1 : 0);
+               writeb(MDIO_EnbIn | MDIO_ShiftClk, mdio_addr);
+               mdio_delay();
+       }
+       return (retval>>1) & 0xffff;
+}
+
+static void mdio_write(struct net_device *dev, int phy_id, int location, int value)
+{
+       long mdio_addr = dev->base_addr + MIICtrl;
+       int mii_cmd = (0x5002 << 16) | (phy_id << 23) | (location<<18) | value;
+       int i;
+
+       if (mii_preamble_required)
+               mdio_sync(mdio_addr);
+
+       /* Shift the command bits out. */
+       for (i = 31; i >= 0; i--) {
+               int dataval = (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0;
+
+               writeb(dataval, mdio_addr);
+               mdio_delay();
+               writeb(dataval | MDIO_ShiftClk, mdio_addr);
+               mdio_delay();
+       }
+       /* Clear out extra bits. */
+       for (i = 2; i > 0; i--) {
+               writeb(MDIO_EnbIn, mdio_addr);
+               mdio_delay();
+               writeb(MDIO_EnbIn | MDIO_ShiftClk, mdio_addr);
+               mdio_delay();
+       }
+       return;
+}
+
+\f
+static int netdev_open(struct net_device *dev)
+{
+       struct netdev_private *np = (struct netdev_private *)dev->priv;
+       long ioaddr = dev->base_addr;
+       int i;
+
+       /* Do we need to reset the chip??? */
+
+       MOD_INC_USE_COUNT;
+
+       if (request_irq(dev->irq, &intr_handler, SA_SHIRQ, dev->name, dev)) {
+               MOD_DEC_USE_COUNT;
+               return -EAGAIN;
+       }
+
+       if (debug > 1)
+               printk(KERN_DEBUG "%s: netdev_open() irq %d.\n",
+                          dev->name, dev->irq);
+
+       init_ring(dev);
+
+       writel(virt_to_bus(np->rx_ring), ioaddr + RxListPtr);
+       /* The Tx list pointer is written as packets are queued. */
+
+       for (i = 0; i < 6; i++)
+               writeb(dev->dev_addr[i], ioaddr + StationAddr + i);
+
+       /* Initialize other registers. */
+       /* Configure the PCI bus bursts and FIFO thresholds. */
+
+       if (dev->if_port == 0)
+               dev->if_port = np->default_port;
+
+       np->full_duplex = np->duplex_lock;
+       np->mcastlock = (spinlock_t) SPIN_LOCK_UNLOCKED;
+
+       set_rx_mode(dev);
+       writew(0, ioaddr + DownCounter);
+       /* Set the chip to poll every N*320nsec. */
+       writeb(100, ioaddr + RxDescPoll);
+       writeb(127, ioaddr + TxDescPoll);
+       netif_start_queue(dev);
+
+       /* Enable interrupts by setting the interrupt mask. */
+       writew(IntrRxDone | IntrRxDMADone | IntrPCIErr | IntrDrvRqst | IntrTxDone
+                  | StatsMax | LinkChange, ioaddr + IntrEnable);
+
+       writew(StatsEnable | RxEnable | TxEnable, ioaddr + MACCtrl1);
+
+       if (debug > 2)
+               printk(KERN_DEBUG "%s: Done netdev_open(), status: Rx %x Tx %x "
+                          "MAC Control %x, %4.4x %4.4x.\n",
+                          dev->name, readl(ioaddr + RxStatus), readb(ioaddr + TxStatus),
+                          readl(ioaddr + MACCtrl0),
+                          readw(ioaddr + MACCtrl1), readw(ioaddr + MACCtrl0));
+
+       /* Set the timer to check for link beat. */
+       init_timer(&np->timer);
+       np->timer.expires = jiffies + 3*HZ;
+       np->timer.data = (unsigned long)dev;
+       np->timer.function = &netdev_timer;                             /* timer handler */
+       add_timer(&np->timer);
+
+       return 0;
+}
+
+static void check_duplex(struct net_device *dev)
+{
+       struct netdev_private *np = (struct netdev_private *)dev->priv;
+       long ioaddr = dev->base_addr;
+       int mii_reg5 = mdio_read(dev, np->phys[0], 5);
+       int negotiated = mii_reg5 & np->advertising;
+       int duplex;
+
+       if (np->duplex_lock  ||  mii_reg5 == 0xffff)
+               return;
+       duplex = (negotiated & 0x0100) || (negotiated & 0x01C0) == 0x0040;
+       if (np->full_duplex != duplex) {
+               np->full_duplex = duplex;
+               if (debug)
+                       printk(KERN_INFO "%s: Setting %s-duplex based on MII #%d "
+                                  "negotiated capability %4.4x.\n", dev->name,
+                                  duplex ? "full" : "half", np->phys[0], negotiated);
+               writew(duplex ? 0x20 : 0, ioaddr + MACCtrl0);
+       }
+}
+
+static void netdev_timer(unsigned long data)
+{
+       struct net_device *dev = (struct net_device *)data;
+       struct netdev_private *np = (struct netdev_private *)dev->priv;
+       long ioaddr = dev->base_addr;
+       int next_tick = 10*HZ;
+
+       if (debug > 3) {
+               printk(KERN_DEBUG "%s: Media selection timer tick, intr status %4.4x, "
+                          "Tx %x Rx %x.\n",
+                          dev->name, readw(ioaddr + IntrEnable),
+                          readb(ioaddr + TxStatus), readl(ioaddr + RxStatus));
+       }
+       check_duplex(dev);
+       np->timer.expires = jiffies + next_tick;
+       add_timer(&np->timer);
+}
+
+static void tx_timeout(struct net_device *dev)
+{
+       struct netdev_private *np = (struct netdev_private *)dev->priv;
+       long ioaddr = dev->base_addr;
+
+       printk(KERN_WARNING "%s: Transmit timed out, status %2.2x,"
+                  " resetting...\n", dev->name, readb(ioaddr + TxStatus));
+
+#ifndef __alpha__
+       {
+               int i;
+               printk(KERN_DEBUG "  Rx ring %8.8x: ", (int)np->rx_ring);
+               for (i = 0; i < RX_RING_SIZE; i++)
+                       printk(" %8.8x", (unsigned int)np->rx_ring[i].status);
+               printk("\n"KERN_DEBUG"  Tx ring %8.8x: ", (int)np->tx_ring);
+               for (i = 0; i < TX_RING_SIZE; i++)
+                       printk(" %4.4x", np->tx_ring[i].status);
+               printk("\n");
+       }
+#endif
+
+       /* Perhaps we should reinitialize the hardware here. */
+       dev->if_port = 0;
+       /* Stop and restart the chip's Tx processes . */
+
+       /* Trigger an immediate transmit demand. */
+       writew(IntrRxDone | IntrRxDMADone | IntrPCIErr | IntrDrvRqst | IntrTxDone
+                  | StatsMax | LinkChange, ioaddr + IntrEnable);
+
+       dev->trans_start = jiffies;
+       np->stats.tx_errors++;
+       return;
+}
+
+
+/* Initialize the Rx and Tx rings, along with various 'dev' bits. */
+static void init_ring(struct net_device *dev)
+{
+       struct netdev_private *np = (struct netdev_private *)dev->priv;
+       int i;
+
+       np->tx_full = 0;
+       np->cur_rx = np->cur_tx = 0;
+       np->dirty_rx = np->dirty_tx = 0;
+
+       np->rx_buf_sz = (dev->mtu <= 1500 ? PKT_BUF_SZ : dev->mtu + 32);
+       np->rx_head_desc = &np->rx_ring[0];
+
+       /* Initialize all Rx descriptors. */
+       for (i = 0; i < RX_RING_SIZE; i++) {
+               np->rx_ring[i].next_desc = virt_to_le32desc(&np->rx_ring[i+1]);
+               np->rx_ring[i].status = 0;
+               np->rx_ring[i].frag[0].length = 0;
+               np->rx_skbuff[i] = 0;
+       }
+       /* Wrap the ring. */
+       np->rx_ring[i-1].next_desc = virt_to_le32desc(&np->rx_ring[0]);
+
+       /* Fill in the Rx buffers.  Handle allocation failure gracefully. */
+       for (i = 0; i < RX_RING_SIZE; i++) {
+               struct sk_buff *skb = dev_alloc_skb(np->rx_buf_sz);
+               np->rx_skbuff[i] = skb;
+               if (skb == NULL)
+                       break;
+               skb->dev = dev;                 /* Mark as being used by this device. */
+               skb_reserve(skb, 2);    /* 16 byte align the IP header. */
+               np->rx_ring[i].frag[0].addr = virt_to_le32desc(skb->tail);
+               np->rx_ring[i].frag[0].length = cpu_to_le32(np->rx_buf_sz | LastFrag);
+       }
+       np->dirty_rx = (unsigned int)(i - RX_RING_SIZE);
+
+       for (i = 0; i < TX_RING_SIZE; i++) {
+               np->tx_skbuff[i] = 0;
+               np->tx_ring[i].status = 0;
+       }
+       return;
+}
+
+static int start_tx(struct sk_buff *skb, struct net_device *dev)
+{
+       struct netdev_private *np = (struct netdev_private *)dev->priv;
+       struct netdev_desc *txdesc;
+       unsigned entry;
+
+       /* Note: Ordering is important here, set the field with the
+          "ownership" bit last, and only then increment cur_tx. */
+
+       /* Calculate the next Tx descriptor entry. */
+       entry = np->cur_tx % TX_RING_SIZE;
+       np->tx_skbuff[entry] = skb;
+       txdesc = &np->tx_ring[entry];
+
+       txdesc->next_desc = 0;
+       /* Note: disable the interrupt generation here before releasing. */
+       txdesc->status =
+               cpu_to_le32((entry<<2) | DescIntrOnDMADone | DescIntrOnTx);
+       txdesc->frag[0].addr = virt_to_le32desc(skb->data);
+       txdesc->frag[0].length = cpu_to_le32(skb->len | LastFrag);
+       if (np->last_tx)
+               np->last_tx->next_desc = virt_to_le32desc(txdesc);
+       np->last_tx = txdesc;
+       np->cur_tx++;
+
+       /* On some architectures: explicitly flush cache lines here. */
+
+       if (np->cur_tx - np->dirty_tx < TX_QUEUE_LEN - 1) {
+               /* do nothing */
+       } else {
+               np->tx_full = 1;
+               netif_stop_queue(dev);
+       }
+       /* Side effect: The read wakes the potentially-idle transmit channel. */
+       if (readl(dev->base_addr + TxListPtr) == 0)
+               writel(virt_to_bus(&np->tx_ring[entry]), dev->base_addr + TxListPtr);
+
+       dev->trans_start = jiffies;
+
+       if (debug > 4) {
+               printk(KERN_DEBUG "%s: Transmit frame #%d queued in slot %d.\n",
+                          dev->name, np->cur_tx, entry);
+       }
+       return 0;
+}
+
+/* The interrupt handler does all of the Rx thread work and cleans up
+   after the Tx thread. */
+static void intr_handler(int irq, void *dev_instance, struct pt_regs *rgs)
+{
+       struct net_device *dev = (struct net_device *)dev_instance;
+       struct netdev_private *np;
+       long ioaddr;
+       int boguscnt = max_interrupt_work;
+
+       ioaddr = dev->base_addr;
+       np = (struct netdev_private *)dev->priv;
+       spin_lock(&np->lock);
+
+       do {
+               int intr_status = readw(ioaddr + IntrStatus);
+               writew(intr_status & (IntrRxDone | IntrRxDMADone | IntrPCIErr |
+                                                         IntrDrvRqst |IntrTxDone|IntrTxDMADone |
+                                                         StatsMax | LinkChange),
+                                                         ioaddr + IntrStatus);
+
+               if (debug > 4)
+                       printk(KERN_DEBUG "%s: Interrupt, status %4.4x.\n",
+                                  dev->name, intr_status);
+
+               if (intr_status == 0)
+                       break;
+
+               if (intr_status & (IntrRxDone|IntrRxDMADone))
+                       netdev_rx(dev);
+
+               if (intr_status & IntrTxDone) {
+                       int boguscnt = 32;
+                       int tx_status = readw(ioaddr + TxStatus);
+                       while (tx_status & 0x80) {
+                               if (debug > 4)
+                                       printk("%s: Transmit status is %2.2x.\n",
+                                                  dev->name, tx_status);
+                               if (tx_status & 0x1e) {
+                                       np->stats.tx_errors++;
+                                       if (tx_status & 0x10)  np->stats.tx_fifo_errors++;
+#ifdef ETHER_STATS
+                                       if (tx_status & 0x08)  np->stats.collisions16++;
+#else
+                                       if (tx_status & 0x08)  np->stats.collisions++;
+#endif
+                                       if (tx_status & 0x04)  np->stats.tx_fifo_errors++;
+                                       if (tx_status & 0x02)  np->stats.tx_window_errors++;
+                                       /* This reset has not been verified!. */
+                                       if (tx_status & 0x10) {                 /* Reset the Tx. */
+                                               writew(0x001c, ioaddr + ASICCtrl + 2);
+#if 0                                  /* Do we need to reset the Tx pointer here? */
+                                               writel(virt_to_bus(&np->tx_ring[np->dirty_tx]),
+                                                          dev->base_addr + TxListPtr);
+#endif
+                                       }
+                                       if (tx_status & 0x1e)           /* Restart the Tx. */
+                                               writew(TxEnable, ioaddr + MACCtrl1);
+                               }
+                               /* Yup, this is a documentation bug.  It cost me *hours*. */
+                               writew(0, ioaddr + TxStatus);
+                               tx_status = readb(ioaddr + TxStatus);
+                               if (--boguscnt < 0)
+                                       break;
+                       }
+               }
+               for (; np->cur_tx - np->dirty_tx > 0; np->dirty_tx++) {
+                       int entry = np->dirty_tx % TX_RING_SIZE;
+                       if ( ! (np->tx_ring[entry].status & 0x00010000))
+                               break;
+                       /* Free the original skb. */
+                       dev_kfree_skb_irq(np->tx_skbuff[entry]);
+                       np->tx_skbuff[entry] = 0;
+               }
+               if (np->tx_full
+                       && np->cur_tx - np->dirty_tx < TX_QUEUE_LEN - 4) {
+                       /* The ring is no longer full, clear tbusy. */
+                       np->tx_full = 0;
+                       netif_wake_queue(dev);
+               }
+
+               /* Abnormal error summary/uncommon events handlers. */
+               if (intr_status & (IntrDrvRqst | IntrPCIErr | LinkChange | StatsMax))
+                       netdev_error(dev, intr_status);
+
+               if (--boguscnt < 0) {
+                       get_stats(dev);
+                       printk(KERN_WARNING "%s: Too much work at interrupt, "
+                                  "status=0x%4.4x / 0x%4.4x.\n",
+                                  dev->name, intr_status, readw(ioaddr + IntrClear));
+                       /* Re-enable us in 3.2msec. */
+                       writew(1000, ioaddr + DownCounter);
+                       writew(IntrDrvRqst, ioaddr + IntrEnable);
+                       break;
+               }
+       } while (1);
+
+       if (debug > 3)
+               printk(KERN_DEBUG "%s: exiting interrupt, status=%#4.4x.\n",
+                          dev->name, readw(ioaddr + IntrStatus));
+
+       spin_unlock(&np->lock);
+}
+
+/* This routine is logically part of the interrupt handler, but separated
+   for clarity and better register allocation. */
+static int netdev_rx(struct net_device *dev)
+{
+       struct netdev_private *np = (struct netdev_private *)dev->priv;
+       int entry = np->cur_rx % RX_RING_SIZE;
+       int boguscnt = np->dirty_rx + RX_RING_SIZE - np->cur_rx;
+
+       if (debug > 4) {
+               printk(KERN_DEBUG " In netdev_rx(), entry %d status %4.4x.\n",
+                          entry, np->rx_ring[entry].status);
+       }
+
+       /* If EOP is set on the next entry, it's a new packet. Send it up. */
+       while (np->rx_head_desc->status & DescOwn) {
+               struct netdev_desc *desc = np->rx_head_desc;
+               u32 frame_status = le32_to_cpu(desc->status);
+               int pkt_len = frame_status & 0x1fff;            /* Chip omits the CRC. */
+
+               if (debug > 4)
+                       printk(KERN_DEBUG "  netdev_rx() status was %8.8x.\n",
+                                  frame_status);
+               if (--boguscnt < 0)
+                       break;
+               if (frame_status & 0x001f4000) {
+                       /* There was a error. */
+                       if (debug > 2)
+                               printk(KERN_DEBUG "  netdev_rx() Rx error was %8.8x.\n",
+                                          frame_status);
+                       np->stats.rx_errors++;
+                       if (frame_status & 0x00100000) np->stats.rx_length_errors++;
+                       if (frame_status & 0x00010000) np->stats.rx_fifo_errors++;
+                       if (frame_status & 0x00060000) np->stats.rx_frame_errors++;
+                       if (frame_status & 0x00080000) np->stats.rx_crc_errors++;
+                       if (frame_status & 0x00100000) {
+                               printk(KERN_WARNING "%s: Oversized Ethernet frame,"
+                                          " status %8.8x.\n",
+                                          dev->name, frame_status);
+                       }
+               } else {
+                       struct sk_buff *skb;
+
+#ifndef final_version
+                       if (debug > 4)
+                               printk(KERN_DEBUG "  netdev_rx() normal Rx pkt length %d"
+                                          ", bogus_cnt %d.\n",
+                                          pkt_len, boguscnt);
+#endif
+                       /* Check if the packet is long enough to accept without copying
+                          to a minimally-sized skbuff. */
+                       if (pkt_len < rx_copybreak
+                               && (skb = dev_alloc_skb(pkt_len + 2)) != NULL) {
+                               skb->dev = dev;
+                               skb_reserve(skb, 2);    /* 16 byte align the IP header */
+                               eth_copy_and_sum(skb, np->rx_skbuff[entry]->tail, pkt_len, 0);
+                               skb_put(skb, pkt_len);
+                       } else {
+                               skb_put(skb = np->rx_skbuff[entry], pkt_len);
+                               np->rx_skbuff[entry] = NULL;
+                       }
+                       skb->protocol = eth_type_trans(skb, dev);
+                       /* Note: checksum -> skb->ip_summed = CHECKSUM_UNNECESSARY; */
+                       netif_rx(skb);
+                       dev->last_rx = jiffies;
+               }
+               entry = (++np->cur_rx) % RX_RING_SIZE;
+               np->rx_head_desc = &np->rx_ring[entry];
+       }
+
+       /* Refill the Rx ring buffers. */
+       for (; np->cur_rx - np->dirty_rx > 0; np->dirty_rx++) {
+               struct sk_buff *skb;
+               entry = np->dirty_rx % RX_RING_SIZE;
+               if (np->rx_skbuff[entry] == NULL) {
+                       skb = dev_alloc_skb(np->rx_buf_sz);
+                       np->rx_skbuff[entry] = skb;
+                       if (skb == NULL)
+                               break;                          /* Better luck next round. */
+                       skb->dev = dev;                 /* Mark as being used by this device. */
+                       skb_reserve(skb, 2);    /* Align IP on 16 byte boundaries */
+                       np->rx_ring[entry].frag[0].addr = virt_to_le32desc(skb->tail);
+               }
+               /* Perhaps we need not reset this field. */
+               np->rx_ring[entry].frag[0].length =
+                       cpu_to_le32(np->rx_buf_sz | LastFrag);
+               np->rx_ring[entry].status = 0;
+       }
+
+       /* No need to restart Rx engine, it will poll. */
+       return 0;
+}
+
+static void netdev_error(struct net_device *dev, int intr_status)
+{
+       long ioaddr = dev->base_addr;
+       struct netdev_private *np = (struct netdev_private *)dev->priv;
+
+       if (intr_status & IntrDrvRqst) {
+               /* Stop the down counter and turn interrupts back on. */
+               printk("%s: Turning interrupts back on.\n", dev->name);
+               writew(0, ioaddr + DownCounter);
+               writew(IntrRxDone | IntrRxDMADone | IntrPCIErr | IntrDrvRqst |
+                          IntrTxDone | StatsMax | LinkChange, ioaddr + IntrEnable);
+       }
+       if (intr_status & LinkChange) {
+               printk(KERN_ERR "%s: Link changed: Autonegotiation advertising"
+                          " %4.4x  partner %4.4x.\n", dev->name,
+                          mdio_read(dev, np->phys[0], 4),
+                          mdio_read(dev, np->phys[0], 5));
+               check_duplex(dev);
+       }
+       if (intr_status & StatsMax) {
+               get_stats(dev);
+       }
+       if (intr_status & IntrPCIErr) {
+               printk(KERN_ERR "%s: Something Wicked happened! %4.4x.\n",
+                          dev->name, intr_status);
+               /* We must do a global reset of DMA to continue. */
+       }
+}
+
+static struct net_device_stats *get_stats(struct net_device *dev)
+{
+       long ioaddr = dev->base_addr;
+       struct netdev_private *np = (struct netdev_private *)dev->priv;
+       int i;
+
+       /* We should lock this segment of code for SMP eventually, although
+          the vulnerability window is very small and statistics are
+          non-critical. */
+       /* The chip only need report frame silently dropped. */
+       np->stats.rx_missed_errors      += readb(ioaddr + RxMissed);
+       np->stats.tx_packets += readw(ioaddr + TxFramesOK);
+       np->stats.rx_packets += readw(ioaddr + RxFramesOK);
+       np->stats.collisions += readb(ioaddr + StatsLateColl);
+       np->stats.collisions += readb(ioaddr + StatsMultiColl);
+       np->stats.collisions += readb(ioaddr + StatsOneColl);
+       readb(ioaddr + StatsCarrierError);
+       readb(ioaddr + StatsTxDefer);
+       for (i = StatsTxDefer; i <= StatsMcastRx; i++)
+               readb(ioaddr + i);
+       np->stats.tx_bytes += readw(ioaddr + TxOctetsLow);
+       np->stats.tx_bytes += readw(ioaddr + TxOctetsHigh) << 16;
+       np->stats.rx_bytes += readw(ioaddr + RxOctetsLow);
+       np->stats.rx_bytes += readw(ioaddr + RxOctetsHigh) << 16;
+
+       return &np->stats;
+}
+
+/* The little-endian AUTODIN II ethernet CRC calculations.
+   A big-endian version is also available.
+   This is slow but compact code.  Do not use this routine for bulk data,
+   use a table-based routine instead.
+   This is common code and should be moved to net/core/crc.c.
+   Chips may use the upper or lower CRC bits, and may reverse and/or invert
+   them.  Select the endian-ness that results in minimal calculations.
+*/
+static unsigned const ethernet_polynomial_le = 0xedb88320U;
+static inline unsigned ether_crc_le(int length, unsigned char *data)
+{
+       unsigned int crc = 0xffffffff;  /* Initial value. */
+       while(--length >= 0) {
+               unsigned char current_octet = *data++;
+               int bit;
+               for (bit = 8; --bit >= 0; current_octet >>= 1) {
+                       if ((crc ^ current_octet) & 1) {
+                               crc >>= 1;
+                               crc ^= ethernet_polynomial_le;
+                       } else
+                               crc >>= 1;
+               }
+       }
+       return crc;
+}
+
+static void set_rx_mode(struct net_device *dev)
+{
+       long ioaddr = dev->base_addr;
+       u16 mc_filter[4];                       /* Multicast hash filter */
+       u32 rx_mode;
+       int i;
+
+       if (dev->flags & IFF_PROMISC) {                 /* Set promiscuous. */
+               /* Unconditionally log net taps. */
+               printk(KERN_NOTICE "%s: Promiscuous mode enabled.\n", dev->name);
+               memset(mc_filter, 0xff, sizeof(mc_filter));
+               rx_mode = AcceptBroadcast | AcceptMulticast | AcceptAll | AcceptMyPhys;
+       } else if ((dev->mc_count > multicast_filter_limit)
+                          ||  (dev->flags & IFF_ALLMULTI)) {
+               /* Too many to match, or accept all multicasts. */
+               memset(mc_filter, 0xff, sizeof(mc_filter));
+               rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys;
+       } else if (dev->mc_count) {
+               struct dev_mc_list *mclist;
+               memset(mc_filter, 0, sizeof(mc_filter));
+               for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count;
+                        i++, mclist = mclist->next) {
+                       set_bit(ether_crc_le(ETH_ALEN, mclist->dmi_addr) & 0x3f,
+                                       mc_filter);
+               }
+               rx_mode = AcceptBroadcast | AcceptMultiHash | AcceptMyPhys;
+       } else {
+               writeb(AcceptBroadcast | AcceptMyPhys, ioaddr + RxMode);
+               return;
+       }
+       for (i = 0; i < 4; i++)
+               writew(mc_filter[i], ioaddr + MulticastFilter0 + i*2);
+       writeb(rx_mode, ioaddr + RxMode);
+}
+
+static int mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+       u16 *data = (u16 *)&rq->ifr_data;
+
+       switch(cmd) {
+       case SIOCDEVPRIVATE:            /* Get the address of the PHY in use. */
+               data[0] = ((struct netdev_private *)dev->priv)->phys[0] & 0x1f;
+               /* Fall Through */
+       case SIOCDEVPRIVATE+1:          /* Read the specified MII register. */
+               data[3] = mdio_read(dev, data[0] & 0x1f, data[1] & 0x1f);
+               return 0;
+       case SIOCDEVPRIVATE+2:          /* Write the specified MII register */
+               if (!capable(CAP_NET_ADMIN))
+                       return -EPERM;
+               mdio_write(dev, data[0] & 0x1f, data[1] & 0x1f, data[2]);
+               return 0;
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
+static int netdev_close(struct net_device *dev)
+{
+       long ioaddr = dev->base_addr;
+       struct netdev_private *np = (struct netdev_private *)dev->priv;
+       int i;
+
+       netif_stop_queue(dev);
+
+       if (debug > 1) {
+               printk(KERN_DEBUG "%s: Shutting down ethercard, status was Tx %2.2x "
+                          "Rx %4.4x Int %2.2x.\n",
+                          dev->name, readb(ioaddr + TxStatus),
+                          readl(ioaddr + RxStatus), readw(ioaddr + IntrStatus));
+               printk(KERN_DEBUG "%s: Queue pointers were Tx %d / %d,  Rx %d / %d.\n",
+                          dev->name, np->cur_tx, np->dirty_tx, np->cur_rx, np->dirty_rx);
+       }
+
+       /* Disable interrupts by clearing the interrupt mask. */
+       writew(0x0000, ioaddr + IntrEnable);
+
+       /* Stop the chip's Tx and Rx processes. */
+       writew(TxDisable | RxDisable | StatsDisable, ioaddr + MACCtrl1);
+
+#ifdef __i386__
+       if (debug > 2) {
+               printk("\n"KERN_DEBUG"  Tx ring at %8.8x:\n",
+                          (int)virt_to_bus(np->tx_ring));
+               for (i = 0; i < TX_RING_SIZE; i++)
+                       printk(" #%d desc. %4.4x %8.8x %8.8x.\n",
+                                  i, np->tx_ring[i].status, np->tx_ring[i].frag[0].addr,
+                                  np->tx_ring[i].frag[0].length);
+               printk("\n"KERN_DEBUG "  Rx ring %8.8x:\n",
+                          (int)virt_to_bus(np->rx_ring));
+               for (i = 0; i < /*RX_RING_SIZE*/4 ; i++) {
+                       printk(KERN_DEBUG " #%d desc. %4.4x %4.4x %8.8x\n",
+                                  i, np->rx_ring[i].status, np->rx_ring[i].frag[0].addr,
+                                  np->rx_ring[i].frag[0].length);
+               }
+       }
+#endif /* __i386__ debugging only */
+
+       free_irq(dev->irq, dev);
+
+       del_timer_sync(&np->timer);
+
+       /* Free all the skbuffs in the Rx queue. */
+       for (i = 0; i < RX_RING_SIZE; i++) {
+               np->rx_ring[i].status = 0;
+               np->rx_ring[i].frag[0].addr = 0xBADF00D0; /* An invalid address. */
+               if (np->rx_skbuff[i]) {
+                       dev_kfree_skb(np->rx_skbuff[i]);
+               }
+               np->rx_skbuff[i] = 0;
+       }
+       for (i = 0; i < TX_RING_SIZE; i++) {
+               if (np->tx_skbuff[i])
+                       dev_kfree_skb(np->tx_skbuff[i]);
+               np->tx_skbuff[i] = 0;
+       }
+
+       MOD_DEC_USE_COUNT;
+
+       return 0;
+}
+
+static void __devexit sundance_remove1 (struct pci_dev *pdev)
+{
+       struct net_device *dev = pdev->driver_data;
+       
+       /* No need to check MOD_IN_USE, as sys_delete_module() checks. */
+       while (dev) {
+               struct netdev_private *np = (void *)(dev->priv);
+               unregister_netdev(dev);
+#ifdef USE_IO_OPS
+               release_region(dev->base_addr, pci_id_tbl[np->chip_id].io_size);
+#else
+               release_mem_region(pci_resource_start(pdev, 1),
+                                  pci_id_tbl[np->chip_id].io_size);
+               iounmap((char *)(dev->base_addr));
+#endif
+               kfree(dev);
+       }
+
+       pdev->driver_data = NULL;
+}
+
+static struct pci_driver sundance_driver = {
+       name:           "sundance",
+       id_table:       sundance_pci_tbl,
+       probe:          sundance_probe1,
+       remove:         sundance_remove1,
+};
+
+static int __init sundance_init(void)
+{
+       return pci_module_init(&sundance_driver);
+}
+
+static void __exit sundance_exit(void)
+{
+       pci_unregister_driver(&sundance_driver);
+}
+
+module_init(sundance_init);
+module_exit(sundance_exit);
index e6dcbf2d8f34f52dd6aa6a2ef1836b5c04006ea2..a85f2e6092284ef8afc02b4992a73b8472249d11 100644 (file)
@@ -96,6 +96,7 @@ static const char *version = "tms380tr.c: v1.07 21/01/2000 by Christoph Goos, Ad
 #include <linux/errno.h>
 #include <linux/init.h>
 #include <linux/pci.h>
+#include <linux/delay.h>
 
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
index 26282e38f6c7711a3182e2d71e6075df6ac2cce0..667b9cb6e23809203fa7b37c0f98a9be1158cb1a 100644 (file)
@@ -12,7 +12,7 @@
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  *  GNU General Public License for more details.
  *
- *  $Id: tun.c,v 1.1 2000/08/23 05:59:28 davem Exp $
+ *  $Id: tun.c,v 1.2 2000/09/22 12:40:31 maxk Exp $
  */
 
 /*
@@ -20,7 +20,7 @@
  *    Modifications for 2.3.99-pre5 kernel.
  */
 
-#define TUN_VER "1.1"
+#define TUN_VER "1.2"
 
 #include <linux/module.h>
 
 #include <linux/poll.h>
 #include <linux/fcntl.h>
 #include <linux/init.h>
-#include <linux/devfs_fs_kernel.h>
 #include <linux/random.h>
 
+#include <linux/skbuff.h>
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
-#include <linux/skbuff.h>
+#include <linux/miscdevice.h>
 #include <linux/rtnetlink.h>
 #include <linux/if.h>
 #include <linux/if_arp.h>
@@ -497,38 +497,29 @@ static struct file_operations tun_fops = {
        fasync: tun_chr_fasync          
 };
 
-static devfs_handle_t devfs_handle = NULL; 
+static struct miscdevice tun_miscdev=
+{
+        TUN_MINOR,
+        "net/tun",
+        &tun_fops
+};
 
 int __init tun_init(void)
 {
        printk(KERN_INFO "Universal TUN/TAP device driver %s " 
               "(C)1999-2000 Maxim Krasnyansky\n", TUN_VER);
 
-       if (devfs_register_chrdev(TUN_MAJOR, "tun", &tun_fops)) {
-               printk(KERN_ERR "tun: Can't register char device %d\n",
-                      TUN_MAJOR);
+       if (misc_register(&tun_miscdev)) {
+               printk(KERN_ERR "tun: Can't register misc device %d\n", TUN_MINOR);
                return -EIO;
        }
 
-       devfs_handle = devfs_register(NULL, "net/tun", DEVFS_FL_DEFAULT, 
-                                     TUN_MAJOR, 0,
-                                     S_IFCHR | S_IRUSR | S_IWUSR, 
-                                     &tun_fops, NULL);
-
-#ifdef MODULE
        return 0;
-#else
-       /* If driver is not module, tun_init will be called from Space.c.
-        * Return non-zero not to register fake device.
-        */
-       return 1;
-#endif
 }
 
 void tun_cleanup(void)
 {
-       devfs_unregister_chrdev(TUN_MAJOR,"tun");
-       devfs_unregister(devfs_handle);  
+       misc_deregister(&tun_miscdev);  
 }
 
 module_init(tun_init);
diff --git a/drivers/net/winbond-840.c b/drivers/net/winbond-840.c
new file mode 100644 (file)
index 0000000..a5cb2fb
--- /dev/null
@@ -0,0 +1,1353 @@
+/* winbond-840.c: A Linux PCI network adapter skeleton device driver. */
+/*
+       Written 1998-2000 by Donald Becker.
+
+       This software may be used and distributed according to the terms of
+       the GNU General Public License (GPL), incorporated herein by reference.
+       Drivers based on or derived from this code fall under the GPL and must
+       retain the authorship, copyright and license notice.  This file is not
+       a complete program and may only be used when the entire operating
+       system is licensed under the GPL.
+
+       The author may be reached as becker@scyld.com, or C/O
+       Scyld Computing Corporation
+       410 Severn Ave., Suite 210
+       Annapolis MD 21403
+
+       Support and updates available at
+       http://www.scyld.com/network/drivers.html
+
+       Do not remove the copyright infomation.
+       Do not change the version information unless an improvement has been made.
+       Merely removing my name, as Compex has done in the past, does not count
+       as an improvement.
+*/
+
+/* These identify the driver base version and may not be removed. */
+static const char version1[] =
+"winbond-840.c:v1.01 5/15/2000  Donald Becker <becker@scyld.com>\n";
+static const char version2[] =
+"  http://www.scyld.com/network/drivers.html\n";
+
+/* Automatically extracted configuration info:
+probe-func: winbond840_probe
+config-in: tristate 'Winbond W89c840 Ethernet support' CONFIG_WINBOND_840
+
+c-help-name: Winbond W89c840 PCI Ethernet support
+c-help-symbol: CONFIG_WINBOND_840
+c-help: This driver is for the Winbond W89c840 chip.  It also works with
+c-help: the TX9882 chip on the Compex RL100-ATX board.
+c-help: More specific information and updates are available from 
+c-help: http://www.scyld.com/network/drivers.html
+*/
+
+/* The user-configurable values.
+   These may be modified when a driver module is loaded.*/
+
+static int debug = 1;                  /* 1 normal messages, 0 quiet .. 7 verbose. */
+static int max_interrupt_work = 20;
+/* Maximum number of multicast addresses to filter (vs. Rx-all-multicast).
+   The '840 uses a 64 element hash table based on the Ethernet CRC.  */
+static int multicast_filter_limit = 32;
+
+/* Set the copy breakpoint for the copy-only-tiny-frames scheme.
+   Setting to > 1518 effectively disables this feature. */
+static int rx_copybreak = 0;
+
+/* Used to pass the media type, etc.
+   Both 'options[]' and 'full_duplex[]' should exist for driver
+   interoperability.
+   The media type is usually passed in 'options[]'.
+*/
+#define MAX_UNITS 8            /* More are supported, limit only on options */
+static int options[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1};
+static int full_duplex[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1};
+
+/* Operational parameters that are set at compile time. */
+
+/* Keep the ring sizes a power of two for compile efficiency.
+   The compiler will convert <unsigned>'%'<2^N> into a bit mask.
+   Making the Tx ring too large decreases the effectiveness of channel
+   bonding and packet priority.
+   There are no ill effects from too-large receive rings. */
+#define TX_RING_SIZE   16
+#define TX_QUEUE_LEN   10              /* Limit ring entries actually used.  */
+#define RX_RING_SIZE   32
+
+/* The presumed FIFO size for working around the Tx-FIFO-overflow bug.
+   To avoid overflowing we don't queue again until we have room for a
+   full-size packet.
+ */
+#define TX_FIFO_SIZE (2048)
+#define TX_BUG_FIFO_LIMIT (TX_FIFO_SIZE-1514-16)
+
+/* Operational parameters that usually are not changed. */
+/* Time in jiffies before concluding the transmitter is hung. */
+#define TX_TIMEOUT  (2*HZ)
+
+#define PKT_BUF_SZ             1536                    /* Size of each temporary Rx buffer.*/
+
+#ifndef __KERNEL__
+#define __KERNEL__
+#endif
+#if !defined(__OPTIMIZE__)
+#warning  You must compile this file with the correct options!
+#warning  See the last lines of the source file.
+#error You must compile this driver with "-O".
+#endif
+
+/* Include files, designed to support most kernel versions 2.0.0 and later. */
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/malloc.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/init.h>
+#include <asm/processor.h>             /* Processor type for cache alignment. */
+#include <asm/bitops.h>
+#include <asm/io.h>
+
+/* Condensed operations for readability.
+   The compatibility defines are in kern_compat.h */
+
+#define virt_to_le32desc(addr)  cpu_to_le32(virt_to_bus(addr))
+#define le32desc_to_virt(addr)  bus_to_virt(le32_to_cpu(addr))
+
+MODULE_AUTHOR("Donald Becker <becker@scyld.com>");
+MODULE_DESCRIPTION("Winbond W89c840 Ethernet driver");
+MODULE_PARM(max_interrupt_work, "i");
+MODULE_PARM(debug, "i");
+MODULE_PARM(rx_copybreak, "i");
+MODULE_PARM(multicast_filter_limit, "i");
+MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i");
+MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i");
+
+/*
+                               Theory of Operation
+
+I. Board Compatibility
+
+This driver is for the Winbond w89c840 chip.
+
+II. Board-specific settings
+
+None.
+
+III. Driver operation
+
+This chip is very similar to the Digital 21*4* "Tulip" family.  The first
+twelve registers and the descriptor format are nearly identical.  Read a
+Tulip manual for operational details.
+
+A significant difference is that the multicast filter and station address are
+stored in registers rather than loaded through a pseudo-transmit packet.
+
+Unlike the Tulip, transmit buffers are limited to 1KB.  To transmit a
+full-sized packet we must use both data buffers in a descriptor.  Thus the
+driver uses ring mode where descriptors are implicitly sequential in memory,
+rather than using the second descriptor address as a chain pointer to
+subsequent descriptors.
+
+IV. Notes
+
+If you are going to almost clone a Tulip, why not go all the way and avoid
+the need for a new driver?
+
+IVb. References
+
+http://www.scyld.com/expert/100mbps.html
+http://www.scyld.com/expert/NWay.html
+http://www.winbond.com.tw/
+
+IVc. Errata
+
+A horrible bug exists in the transmit FIFO.  Apparently the chip doesn't
+correctly detect a full FIFO, and queuing more than 2048 bytes may result in
+silent data corruption.
+
+*/
+
+\f
+
+/*
+  PCI probe table.
+*/
+enum pci_id_flags_bits {
+        /* Set PCI command register bits before calling probe1(). */
+        PCI_USES_IO=1, PCI_USES_MEM=2, PCI_USES_MASTER=4,
+        /* Read and map the single following PCI BAR. */
+        PCI_ADDR0=0<<4, PCI_ADDR1=1<<4, PCI_ADDR2=2<<4, PCI_ADDR3=3<<4,
+        PCI_ADDR_64BITS=0x100, PCI_NO_ACPI_WAKE=0x200, PCI_NO_MIN_LATENCY=0x400,
+};
+enum chip_capability_flags {CanHaveMII=1, HasBrokenTx=2};
+#ifdef USE_IO_OPS
+#define W840_FLAGS (PCI_USES_IO | PCI_ADDR0 | PCI_USES_MASTER)
+#else
+#define W840_FLAGS (PCI_USES_MEM | PCI_ADDR1 | PCI_USES_MASTER)
+#endif
+
+static struct pci_device_id w840_pci_tbl[] __devinitdata = {
+       { 0x1050, 0x0840, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+       { 0x11f6, 0x2011, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1 },
+       { 0, }
+};
+MODULE_DEVICE_TABLE(pci, w840_pci_tbl);
+
+struct pci_id_info {
+        const char *name;
+        struct match_info {
+                int     pci, pci_mask, subsystem, subsystem_mask;
+                int revision, revision_mask;                            /* Only 8 bits. */
+        } id;
+        enum pci_id_flags_bits pci_flags;
+        int io_size;                            /* Needed for I/O region check or ioremap(). */
+        int drv_flags;                          /* Driver use, intended as capability flags. */
+};
+static struct pci_id_info pci_id_tbl[] = {
+       {"Winbond W89c840", { 0x08401050, 0xffffffff, },
+        W840_FLAGS, 128, CanHaveMII | HasBrokenTx},
+       {"Compex RL100-ATX", { 0x201111F6, 0xffffffff,},
+        W840_FLAGS, 128, CanHaveMII | HasBrokenTx},
+       {0,},                                           /* 0 terminated list. */
+};
+
+/* This driver was written to use PCI memory space, however some x86 systems
+   work only with I/O space accesses.  Pass -DUSE_IO_OPS to use PCI I/O space
+   accesses instead of memory space. */
+
+#ifdef USE_IO_OPS
+#undef readb
+#undef readw
+#undef readl
+#undef writeb
+#undef writew
+#undef writel
+#define readb inb
+#define readw inw
+#define readl inl
+#define writeb outb
+#define writew outw
+#define writel outl
+#endif
+
+/* Offsets to the Command and Status Registers, "CSRs".
+   While similar to the Tulip, these registers are longword aligned.
+   Note: It's not useful to define symbolic names for every register bit in
+   the device.  The name can only partially document the semantics and make
+   the driver longer and more difficult to read.
+*/
+enum w840_offsets {
+       PCIBusCfg=0x00, TxStartDemand=0x04, RxStartDemand=0x08,
+       RxRingPtr=0x0C, TxRingPtr=0x10,
+       IntrStatus=0x14, NetworkConfig=0x18, IntrEnable=0x1C,
+       RxMissed=0x20, EECtrl=0x24, MIICtrl=0x24, BootRom=0x28, GPTimer=0x2C,
+       CurRxDescAddr=0x30, CurRxBufAddr=0x34,                  /* Debug use */
+       MulticastFilter0=0x38, MulticastFilter1=0x3C, StationAddr=0x40,
+       CurTxDescAddr=0x4C, CurTxBufAddr=0x50,
+};
+
+/* Bits in the interrupt status/enable registers. */
+/* The bits in the Intr Status/Enable registers, mostly interrupt sources. */
+enum intr_status_bits {
+       NormalIntr=0x10000, AbnormalIntr=0x8000,
+       IntrPCIErr=0x2000, TimerInt=0x800,
+       IntrRxDied=0x100, RxNoBuf=0x80, IntrRxDone=0x40,
+       TxFIFOUnderflow=0x20, RxErrIntr=0x10,
+       TxIdle=0x04, IntrTxStopped=0x02, IntrTxDone=0x01,
+};
+
+/* Bits in the NetworkConfig register. */
+enum rx_mode_bits {
+       AcceptErr=0x80, AcceptRunt=0x40,
+       AcceptBroadcast=0x20, AcceptMulticast=0x10,
+       AcceptAllPhys=0x08, AcceptMyPhys=0x02,
+};
+
+enum mii_reg_bits {
+       MDIO_ShiftClk=0x10000, MDIO_DataIn=0x80000, MDIO_DataOut=0x20000,
+       MDIO_EnbOutput=0x40000, MDIO_EnbIn = 0x00000,
+};
+
+/* The Tulip Rx and Tx buffer descriptors. */
+struct w840_rx_desc {
+       s32 status;
+       s32 length;
+       u32 buffer1;
+       u32 next_desc;
+};
+
+struct w840_tx_desc {
+       s32 status;
+       s32 length;
+       u32 buffer1, buffer2;                           /* We use only buffer 1.  */
+};
+
+/* Bits in network_desc.status */
+enum desc_status_bits {
+       DescOwn=0x80000000, DescEndRing=0x02000000, DescUseLink=0x01000000,
+       DescWholePkt=0x60000000, DescStartPkt=0x20000000, DescEndPkt=0x40000000,
+       DescIntr=0x80000000,
+};
+
+#define PRIV_ALIGN     15      /* Required alignment mask */
+struct netdev_private {
+       /* Descriptor rings first for alignment. */
+       struct w840_rx_desc rx_ring[RX_RING_SIZE];
+       struct w840_tx_desc tx_ring[TX_RING_SIZE];
+       /* The addresses of receive-in-place skbuffs. */
+       struct sk_buff* rx_skbuff[RX_RING_SIZE];
+       /* The saved address of a sent-in-place packet/buffer, for later free(). */
+       struct sk_buff* tx_skbuff[TX_RING_SIZE];
+       struct net_device_stats stats;
+       struct timer_list timer;        /* Media monitoring timer. */
+       /* Frequently used values: keep some adjacent for cache effect. */
+       spinlock_t lock;
+       int chip_id, drv_flags;
+       int csr6;
+       struct w840_rx_desc *rx_head_desc;
+       unsigned int cur_rx, dirty_rx;          /* Producer/consumer ring indices */
+       unsigned int rx_buf_sz;                         /* Based on MTU+slack. */
+       unsigned int cur_tx, dirty_tx;
+       int tx_q_bytes;
+       unsigned int tx_full:1;                         /* The Tx queue is full. */
+       /* These values are keep track of the transceiver/media in use. */
+       unsigned int full_duplex:1;                     /* Full-duplex operation requested. */
+       unsigned int duplex_lock:1;
+       unsigned int medialock:1;                       /* Do not sense media. */
+       unsigned int default_port:4;            /* Last dev->if_port value. */
+       /* MII transceiver section. */
+       int mii_cnt;                                            /* MII device addresses. */
+       u16 advertising;                                        /* NWay media advertisement */
+       unsigned char phys[2];                          /* MII device addresses. */
+};
+
+static int  eeprom_read(long ioaddr, int location);
+static int  mdio_read(struct net_device *dev, int phy_id, int location);
+static void mdio_write(struct net_device *dev, int phy_id, int location, int value);
+static int  netdev_open(struct net_device *dev);
+static void check_duplex(struct net_device *dev);
+static void netdev_timer(unsigned long data);
+static void tx_timeout(struct net_device *dev);
+static void init_ring(struct net_device *dev);
+static int  start_tx(struct sk_buff *skb, struct net_device *dev);
+static void intr_handler(int irq, void *dev_instance, struct pt_regs *regs);
+static void netdev_error(struct net_device *dev, int intr_status);
+static int  netdev_rx(struct net_device *dev);
+static void netdev_error(struct net_device *dev, int intr_status);
+static inline unsigned ether_crc(int length, unsigned char *data);
+static void set_rx_mode(struct net_device *dev);
+static struct net_device_stats *get_stats(struct net_device *dev);
+static int mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
+static int  netdev_close(struct net_device *dev);
+
+\f
+
+static int __devinit w840_probe1 (struct pci_dev *pdev,
+                                 const struct pci_device_id *ent)
+{
+       struct net_device *dev;
+       struct netdev_private *np;
+       static int find_cnt;
+       int chip_idx = ent->driver_data;
+       int irq = pdev->irq;
+       int i, option = find_cnt < MAX_UNITS ? options[find_cnt] : 0;
+       long ioaddr;
+
+       if (pci_enable_device(pdev))
+               return -EIO;
+       pci_set_master(pdev);
+
+       dev = init_etherdev(NULL, sizeof(*np));
+       if (!dev)
+               return -ENOMEM;
+
+#ifdef USE_IO_OPS
+       ioaddr = pci_resource_start(pdev, 0);
+       if (!request_region(ioaddr, pci_id_tbl[chip_idx].io_size, dev->name))
+               goto err_out_netdev;
+#else
+       ioaddr = pci_resource_start(pdev, 1);
+       if (!request_mem_region(ioaddr, pci_id_tbl[chip_idx].io_size, dev->name))
+               goto err_out_netdev;
+       ioaddr = (long) ioremap (ioaddr, pci_id_tbl[chip_idx].io_size);
+       if (!ioaddr)
+               goto err_out_iomem;
+#endif
+
+       printk(KERN_INFO "%s: %s at 0x%lx, ",
+                  dev->name, pci_id_tbl[chip_idx].name, ioaddr);
+
+       /* Warning: broken for big-endian machines. */
+       for (i = 0; i < 3; i++)
+               ((u16 *)dev->dev_addr)[i] = le16_to_cpu(eeprom_read(ioaddr, i));
+
+       for (i = 0; i < 5; i++)
+                       printk("%2.2x:", dev->dev_addr[i]);
+       printk("%2.2x, IRQ %d.\n", dev->dev_addr[i], irq);
+
+       /* Reset the chip to erase previous misconfiguration.
+          No hold time required! */
+       writel(0x00000001, ioaddr + PCIBusCfg);
+
+       dev->base_addr = ioaddr;
+       dev->irq = irq;
+
+       np = dev->priv;
+       np->chip_id = chip_idx;
+       np->drv_flags = pci_id_tbl[chip_idx].drv_flags;
+       spin_lock_init(&np->lock);
+       
+       pdev->driver_data = dev;
+
+       if (dev->mem_start)
+               option = dev->mem_start;
+
+       /* The lower four bits are the media type. */
+       if (option > 0) {
+               if (option & 0x200)
+                       np->full_duplex = 1;
+               np->default_port = option & 15;
+               if (np->default_port)
+                       np->medialock = 1;
+       }
+       if (find_cnt < MAX_UNITS  &&  full_duplex[find_cnt] > 0)
+               np->full_duplex = 1;
+
+       if (np->full_duplex)
+               np->duplex_lock = 1;
+
+       /* The chip-specific entries in the device structure. */
+       dev->open = &netdev_open;
+       dev->hard_start_xmit = &start_tx;
+       dev->stop = &netdev_close;
+       dev->get_stats = &get_stats;
+       dev->set_multicast_list = &set_rx_mode;
+       dev->do_ioctl = &mii_ioctl;
+       dev->tx_timeout = &tx_timeout;
+       dev->watchdog_timeo = TX_TIMEOUT;
+
+       if (np->drv_flags & CanHaveMII) {
+               int phy, phy_idx = 0;
+               for (phy = 1; phy < 32 && phy_idx < 4; phy++) {
+                       int mii_status = mdio_read(dev, phy, 1);
+                       if (mii_status != 0xffff  &&  mii_status != 0x0000) {
+                               np->phys[phy_idx++] = phy;
+                               np->advertising = mdio_read(dev, phy, 4);
+                               printk(KERN_INFO "%s: MII PHY found at address %d, status "
+                                          "0x%4.4x advertising %4.4x.\n",
+                                          dev->name, phy, mii_status, np->advertising);
+                       }
+               }
+               np->mii_cnt = phy_idx;
+               if (phy_idx == 0) {
+                               printk(KERN_WARNING "%s: MII PHY not found -- this device may "
+                                          "not operate correctly.\n", dev->name);
+               }
+       }
+
+       find_cnt++;
+       return 0;
+
+#ifndef USE_IO_OPS
+err_out_iomem:
+       release_mem_region(pci_resource_start(pdev, 1),
+                          pci_id_tbl[chip_idx].io_size);
+#endif
+err_out_netdev:
+       unregister_netdev (dev);
+       kfree (dev);
+       return -ENODEV;
+}
+
+\f
+/* Read the EEPROM and MII Management Data I/O (MDIO) interfaces.  These are
+   often serial bit streams generated by the host processor.
+   The example below is for the common 93c46 EEPROM, 64 16 bit words. */
+
+/* Delay between EEPROM clock transitions.
+   No extra delay is needed with 33Mhz PCI, but future 66Mhz access may need
+   a delay.  Note that pre-2.0.34 kernels had a cache-alignment bug that
+   made udelay() unreliable.
+   The old method of using an ISA access as a delay, __SLOW_DOWN_IO__, is
+   depricated.
+*/
+#define eeprom_delay(ee_addr)  readl(ee_addr)
+
+enum EEPROM_Ctrl_Bits {
+       EE_ShiftClk=0x02, EE_Write0=0x801, EE_Write1=0x805,
+       EE_ChipSelect=0x801, EE_DataIn=0x08,
+};
+
+/* The EEPROM commands include the alway-set leading bit. */
+enum EEPROM_Cmds {
+       EE_WriteCmd=(5 << 6), EE_ReadCmd=(6 << 6), EE_EraseCmd=(7 << 6),
+};
+
+static int eeprom_read(long addr, int location)
+{
+       int i;
+       int retval = 0;
+       int ee_addr = addr + EECtrl;
+       int read_cmd = location | EE_ReadCmd;
+       writel(EE_ChipSelect, ee_addr);
+
+       /* Shift the read command bits out. */
+       for (i = 10; i >= 0; i--) {
+               short dataval = (read_cmd & (1 << i)) ? EE_Write1 : EE_Write0;
+               writel(dataval, ee_addr);
+               eeprom_delay(ee_addr);
+               writel(dataval | EE_ShiftClk, ee_addr);
+               eeprom_delay(ee_addr);
+       }
+       writel(EE_ChipSelect, ee_addr);
+
+       for (i = 16; i > 0; i--) {
+               writel(EE_ChipSelect | EE_ShiftClk, ee_addr);
+               eeprom_delay(ee_addr);
+               retval = (retval << 1) | ((readl(ee_addr) & EE_DataIn) ? 1 : 0);
+               writel(EE_ChipSelect, ee_addr);
+               eeprom_delay(ee_addr);
+       }
+
+       /* Terminate the EEPROM access. */
+       writel(0, ee_addr);
+       return retval;
+}
+
+/*  MII transceiver control section.
+       Read and write the MII registers using software-generated serial
+       MDIO protocol.  See the MII specifications or DP83840A data sheet
+       for details.
+
+       The maximum data clock rate is 2.5 Mhz.  The minimum timing is usually
+       met by back-to-back 33Mhz PCI cycles. */
+#define mdio_delay(mdio_addr) readl(mdio_addr)
+
+/* Set iff a MII transceiver on any interface requires mdio preamble.
+   This only set with older tranceivers, so the extra
+   code size of a per-interface flag is not worthwhile. */
+static char mii_preamble_required = 1;
+
+#define MDIO_WRITE0 (MDIO_EnbOutput)
+#define MDIO_WRITE1 (MDIO_DataOut | MDIO_EnbOutput)
+
+/* Generate the preamble required for initial synchronization and
+   a few older transceivers. */
+static void mdio_sync(long mdio_addr)
+{
+       int bits = 32;
+
+       /* Establish sync by sending at least 32 logic ones. */
+       while (--bits >= 0) {
+               writel(MDIO_WRITE1, mdio_addr);
+               mdio_delay(mdio_addr);
+               writel(MDIO_WRITE1 | MDIO_ShiftClk, mdio_addr);
+               mdio_delay(mdio_addr);
+       }
+}
+
+static int mdio_read(struct net_device *dev, int phy_id, int location)
+{
+       long mdio_addr = dev->base_addr + MIICtrl;
+       int mii_cmd = (0xf6 << 10) | (phy_id << 5) | location;
+       int i, retval = 0;
+
+       if (mii_preamble_required)
+               mdio_sync(mdio_addr);
+
+       /* Shift the read command bits out. */
+       for (i = 15; i >= 0; i--) {
+               int dataval = (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0;
+
+               writel(dataval, mdio_addr);
+               mdio_delay(mdio_addr);
+               writel(dataval | MDIO_ShiftClk, mdio_addr);
+               mdio_delay(mdio_addr);
+       }
+       /* Read the two transition, 16 data, and wire-idle bits. */
+       for (i = 20; i > 0; i--) {
+               writel(MDIO_EnbIn, mdio_addr);
+               mdio_delay(mdio_addr);
+               retval = (retval << 1) | ((readl(mdio_addr) & MDIO_DataIn) ? 1 : 0);
+               writel(MDIO_EnbIn | MDIO_ShiftClk, mdio_addr);
+               mdio_delay(mdio_addr);
+       }
+       return (retval>>1) & 0xffff;
+}
+
+static void mdio_write(struct net_device *dev, int phy_id, int location, int value)
+{
+       struct netdev_private *np = (struct netdev_private *)dev->priv;
+       long mdio_addr = dev->base_addr + MIICtrl;
+       int mii_cmd = (0x5002 << 16) | (phy_id << 23) | (location<<18) | value;
+       int i;
+
+       if (location == 4  &&  phy_id == np->phys[0])
+               np->advertising = value;
+
+       if (mii_preamble_required)
+               mdio_sync(mdio_addr);
+
+       /* Shift the command bits out. */
+       for (i = 31; i >= 0; i--) {
+               int dataval = (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0;
+
+               writel(dataval, mdio_addr);
+               mdio_delay(mdio_addr);
+               writel(dataval | MDIO_ShiftClk, mdio_addr);
+               mdio_delay(mdio_addr);
+       }
+       /* Clear out extra bits. */
+       for (i = 2; i > 0; i--) {
+               writel(MDIO_EnbIn, mdio_addr);
+               mdio_delay(mdio_addr);
+               writel(MDIO_EnbIn | MDIO_ShiftClk, mdio_addr);
+               mdio_delay(mdio_addr);
+       }
+       return;
+}
+
+\f
+static int netdev_open(struct net_device *dev)
+{
+       struct netdev_private *np = (struct netdev_private *)dev->priv;
+       long ioaddr = dev->base_addr;
+       int i;
+
+       writel(0x00000001, ioaddr + PCIBusCfg);         /* Reset */
+
+       MOD_INC_USE_COUNT;
+
+       if (request_irq(dev->irq, &intr_handler, SA_SHIRQ, dev->name, dev)) {
+               MOD_DEC_USE_COUNT;
+               return -EAGAIN;
+       }
+
+       if (debug > 1)
+               printk(KERN_DEBUG "%s: w89c840_open() irq %d.\n",
+                          dev->name, dev->irq);
+
+       init_ring(dev);
+
+       writel(virt_to_bus(np->rx_ring), ioaddr + RxRingPtr);
+       writel(virt_to_bus(np->tx_ring), ioaddr + TxRingPtr);
+
+       for (i = 0; i < 6; i++)
+               writeb(dev->dev_addr[i], ioaddr + StationAddr + i);
+
+       /* Initialize other registers. */
+       /* Configure the PCI bus bursts and FIFO thresholds.
+          486: Set 8 longword cache alignment, 8 longword burst.
+          586: Set 16 longword cache alignment, no burst limit.
+          Cache alignment bits 15:14        Burst length 13:8
+               0000    <not allowed>           0000 align to cache     0800 8 longwords
+               4000    8  longwords            0100 1 longword         1000 16 longwords
+               8000    16 longwords            0200 2 longwords        2000 32 longwords
+               C000    32  longwords           0400 4 longwords
+          Wait the specified 50 PCI cycles after a reset by initializing
+          Tx and Rx queues and the address filter list. */
+#if defined(__powerpc__)               /* Big-endian */
+       writel(0x00100080 | 0xE010, ioaddr + PCIBusCfg);
+#elif defined(__alpha__)
+       writel(0xE010, ioaddr + PCIBusCfg);
+#elif defined(__i386__)
+#if defined(MODULE)
+       writel(0xE010, ioaddr + PCIBusCfg);
+#else
+       /* When not a module we can work around broken '486 PCI boards. */
+#define x86 boot_cpu_data.x86
+       writel((x86 <= 4 ? 0x4810 : 0xE010), ioaddr + PCIBusCfg);
+       if (x86 <= 4)
+               printk(KERN_INFO "%s: This is a 386/486 PCI system, setting cache "
+                          "alignment to %x.\n", dev->name,
+                          (x86 <= 4 ? 0x4810 : 0x8010));
+#endif
+#else
+       writel(0xE010, ioaddr + PCIBusCfg);
+#warning Processor architecture undefined!
+#endif
+
+       if (dev->if_port == 0)
+               dev->if_port = np->default_port;
+
+       writel(0, ioaddr + RxStartDemand);
+       np->csr6 = 0x20022002;
+       check_duplex(dev);
+       set_rx_mode(dev);
+
+       netif_start_queue(dev);
+
+       /* Clear and Enable interrupts by setting the interrupt mask. */
+       writel(0x1A0F5, ioaddr + IntrStatus);
+       writel(0x1A0F5, ioaddr + IntrEnable);
+
+       if (debug > 2)
+               printk(KERN_DEBUG "%s: Done netdev_open().\n", dev->name);
+
+       /* Set the timer to check for link beat. */
+       init_timer(&np->timer);
+       np->timer.expires = jiffies + 3*HZ;
+       np->timer.data = (unsigned long)dev;
+       np->timer.function = &netdev_timer;                             /* timer handler */
+       add_timer(&np->timer);
+
+       return 0;
+}
+
+static void check_duplex(struct net_device *dev)
+{
+       struct netdev_private *np = (struct netdev_private *)dev->priv;
+       int mii_reg5 = mdio_read(dev, np->phys[0], 5);
+       int negotiated =  mii_reg5 & np->advertising;
+       int duplex;
+
+       if (np->duplex_lock  ||  mii_reg5 == 0xffff)
+               return;
+       duplex = (negotiated & 0x0100) || (negotiated & 0x01C0) == 0x0040;
+       if (np->full_duplex != duplex) {
+               np->full_duplex = duplex;
+               if (debug)
+                       printk(KERN_INFO "%s: Setting %s-duplex based on MII #%d "
+                                  "negotiated capability %4.4x.\n", dev->name,
+                                  duplex ? "full" : "half", np->phys[0], negotiated);
+               np->csr6 &= ~0x200;
+               np->csr6 |= duplex ? 0x200 : 0;
+       }
+}
+
+static void netdev_timer(unsigned long data)
+{
+       struct net_device *dev = (struct net_device *)data;
+       struct netdev_private *np = (struct netdev_private *)dev->priv;
+       long ioaddr = dev->base_addr;
+       int next_tick = 10*HZ;
+       int old_csr6 = np->csr6;
+
+       if (debug > 2)
+               printk(KERN_DEBUG "%s: Media selection timer tick, status %8.8x "
+                          "config %8.8x.\n",
+                          dev->name, (int)readl(ioaddr + IntrStatus),
+                          (int)readl(ioaddr + NetworkConfig));
+       check_duplex(dev);
+       if (np->csr6 != old_csr6) {
+               writel(np->csr6 & ~0x0002, ioaddr + NetworkConfig);
+               writel(np->csr6 | 0x2002, ioaddr + NetworkConfig);
+       }
+       np->timer.expires = jiffies + next_tick;
+       add_timer(&np->timer);
+}
+
+static void tx_timeout(struct net_device *dev)
+{
+       struct netdev_private *np = (struct netdev_private *)dev->priv;
+       long ioaddr = dev->base_addr;
+
+       printk(KERN_WARNING "%s: Transmit timed out, status %8.8x,"
+                  " resetting...\n", dev->name, (int)readl(ioaddr + IntrStatus));
+
+#ifndef __alpha__
+       {
+               int i;
+               printk(KERN_DEBUG "  Rx ring %8.8x: ", (int)np->rx_ring);
+               for (i = 0; i < RX_RING_SIZE; i++)
+                       printk(" %8.8x", (unsigned int)np->rx_ring[i].status);
+               printk("\n"KERN_DEBUG"  Tx ring %8.8x: ", (int)np->tx_ring);
+               for (i = 0; i < TX_RING_SIZE; i++)
+                       printk(" %4.4x", np->tx_ring[i].status);
+               printk("\n");
+       }
+#endif
+
+       /* Perhaps we should reinitialize the hardware here.  Just trigger a
+          Tx demand for now. */
+       writel(0, ioaddr + TxStartDemand);
+       dev->if_port = 0;
+       /* Stop and restart the chip's Tx processes . */
+
+       dev->trans_start = jiffies;
+       np->stats.tx_errors++;
+       return;
+}
+
+
+/* Initialize the Rx and Tx rings, along with various 'dev' bits. */
+static void init_ring(struct net_device *dev)
+{
+       struct netdev_private *np = (struct netdev_private *)dev->priv;
+       int i;
+
+       np->tx_full = 0;
+       np->tx_q_bytes = np->cur_rx = np->cur_tx = 0;
+       np->dirty_rx = np->dirty_tx = 0;
+
+       np->rx_buf_sz = (dev->mtu <= 1500 ? PKT_BUF_SZ : dev->mtu + 32);
+       np->rx_head_desc = &np->rx_ring[0];
+
+       /* Initial all Rx descriptors. */
+       for (i = 0; i < RX_RING_SIZE; i++) {
+               np->rx_ring[i].length = cpu_to_le32(np->rx_buf_sz);
+               np->rx_ring[i].status = 0;
+               np->rx_ring[i].next_desc = virt_to_le32desc(&np->rx_ring[i+1]);
+               np->rx_skbuff[i] = 0;
+       }
+       /* Mark the last entry as wrapping the ring. */
+       np->rx_ring[i-1].length |= cpu_to_le32(DescEndRing);
+       np->rx_ring[i-1].next_desc = virt_to_le32desc(&np->rx_ring[0]);
+
+       /* Fill in the Rx buffers.  Handle allocation failure gracefully. */
+       for (i = 0; i < RX_RING_SIZE; i++) {
+               struct sk_buff *skb = dev_alloc_skb(np->rx_buf_sz);
+               np->rx_skbuff[i] = skb;
+               if (skb == NULL)
+                       break;
+               skb->dev = dev;                 /* Mark as being used by this device. */
+               np->rx_ring[i].buffer1 = virt_to_le32desc(skb->tail);
+               np->rx_ring[i].status = cpu_to_le32(DescOwn | DescIntr);
+       }
+       np->dirty_rx = (unsigned int)(i - RX_RING_SIZE);
+
+       for (i = 0; i < TX_RING_SIZE; i++) {
+               np->tx_skbuff[i] = 0;
+               np->tx_ring[i].status = 0;
+       }
+       return;
+}
+
+static int start_tx(struct sk_buff *skb, struct net_device *dev)
+{
+       struct netdev_private *np = (struct netdev_private *)dev->priv;
+       unsigned entry;
+
+       /* Caution: the write order is important here, set the field
+          with the "ownership" bits last. */
+
+       /* Calculate the next Tx descriptor entry. */
+       entry = np->cur_tx % TX_RING_SIZE;
+
+       np->tx_skbuff[entry] = skb;
+       np->tx_ring[entry].buffer1 = virt_to_le32desc(skb->data);
+
+#define one_buffer
+#define BPT 1022
+#if defined(one_buffer)
+       np->tx_ring[entry].length = cpu_to_le32(DescWholePkt | skb->len);
+       if (entry >= TX_RING_SIZE-1)             /* Wrap ring */
+               np->tx_ring[entry].length |= cpu_to_le32(DescIntr | DescEndRing);
+       np->tx_ring[entry].status = cpu_to_le32(DescOwn);
+       np->cur_tx++;
+#elif defined(two_buffer)
+       if (skb->len > BPT) {
+               unsigned int entry1 = ++np->cur_tx % TX_RING_SIZE;
+               np->tx_ring[entry].length = cpu_to_le32(DescStartPkt | BPT);
+               np->tx_ring[entry1].length = cpu_to_le32(DescEndPkt | (skb->len - BPT));
+               np->tx_ring[entry1].buffer1 = virt_to_le32desc((skb->data) + BPT);
+               np->tx_ring[entry1].status = cpu_to_le32(DescOwn);
+               np->tx_ring[entry].status = cpu_to_le32(DescOwn);
+               if (entry >= TX_RING_SIZE-1)
+                       np->tx_ring[entry].length |= cpu_to_le32(DescIntr|DescEndRing);
+               else if (entry1 >= TX_RING_SIZE-1)
+                       np->tx_ring[entry1].length |= cpu_to_le32(DescIntr|DescEndRing);
+               np->cur_tx++;
+       } else {
+               np->tx_ring[entry].length = cpu_to_le32(DescWholePkt | skb->len);
+               if (entry >= TX_RING_SIZE-1)             /* Wrap ring */
+                       np->tx_ring[entry].length |= cpu_to_le32(DescIntr | DescEndRing);
+               np->tx_ring[entry].status = cpu_to_le32(DescOwn);
+               np->cur_tx++;
+       }
+#elif defined(split_buffer)
+       {
+               /* Work around the Tx-FIFO-full bug by splitting our transmit packet
+                  into two pieces, the first which may be loaded without overflowing
+                  the FIFO, and the second which contains the remainder of the
+                  packet.  When we get a Tx-done interrupt that frees enough room
+                  in the FIFO we mark the remainder of the packet as loadable.
+
+                  This has the problem that the Tx descriptors are written both
+                  here and in the interrupt handler.
+               */
+
+               int buf1size = TX_FIFO_SIZE - np->tx_q_bytes;
+               int buf2size = skb->len - buf1size;
+
+               if (buf2size <= 0) {            /* We fit into one descriptor. */
+                       np->tx_ring[entry].length = cpu_to_le32(DescWholePkt | skb->len);
+               } else {                                /* We must use two descriptors. */
+                       unsigned int entry2;
+                       np->tx_ring[entry].length =
+                               cpu_to_le32(DescIntr | DescStartPkt | buf1size);
+                       if (entry >= TX_RING_SIZE-1) {           /* Wrap ring */
+                               np->tx_ring[entry].length |= cpu_to_le32(DescEndRing);
+                               entry2 = 0;
+                       } else
+                               entry2 = entry + 1;
+                       np->cur_tx++;
+                       np->tx_ring[entry2].buffer1 =
+                               virt_to_le32desc(skb->data + buf1size);
+                       np->tx_ring[entry2].length = cpu_to_le32(DescEndPkt | buf2size);
+                       if (entry2 >= TX_RING_SIZE-1)            /* Wrap ring */
+                               np->tx_ring[entry2].length |= cpu_to_le32(DescEndRing);
+               }
+               np->tx_ring[entry].status = cpu_to_le32(DescOwn);
+               np->cur_tx++;
+       }
+#endif
+       np->tx_q_bytes += skb->len;
+       writel(0, dev->base_addr + TxStartDemand);
+
+       /* Work around horrible bug in the chip by marking the queue as full
+          when we do not have FIFO room for a maximum sized packet. */
+       if (np->cur_tx - np->dirty_tx > TX_QUEUE_LEN)
+               np->tx_full = 1;
+       else if ((np->drv_flags & HasBrokenTx)
+                        && np->tx_q_bytes > TX_BUG_FIFO_LIMIT)
+               np->tx_full = 1;
+       if (np->tx_full)
+               netif_stop_queue(dev);
+
+       dev->trans_start = jiffies;
+
+       if (debug > 4) {
+               printk(KERN_DEBUG "%s: Transmit frame #%d queued in slot %d.\n",
+                          dev->name, np->cur_tx, entry);
+       }
+       return 0;
+}
+
+/* The interrupt handler does all of the Rx thread work and cleans up
+   after the Tx thread. */
+static void intr_handler(int irq, void *dev_instance, struct pt_regs *rgs)
+{
+       struct net_device *dev = (struct net_device *)dev_instance;
+       struct netdev_private *np = (struct netdev_private *)dev->priv;
+       long ioaddr = dev->base_addr;
+       int work_limit = max_interrupt_work;
+
+       spin_lock(&np->lock);
+
+       do {
+               u32 intr_status = readl(ioaddr + IntrStatus);
+
+               /* Acknowledge all of the current interrupt sources ASAP. */
+               writel(intr_status & 0x001ffff, ioaddr + IntrStatus);
+
+               if (debug > 4)
+                       printk(KERN_DEBUG "%s: Interrupt, status %4.4x.\n",
+                                  dev->name, intr_status);
+
+               if ((intr_status & (NormalIntr|AbnormalIntr)) == 0)
+                       break;
+
+               if (intr_status & (IntrRxDone | RxNoBuf))
+                       netdev_rx(dev);
+
+               for (; np->cur_tx - np->dirty_tx > 0; np->dirty_tx++) {
+                       int entry = np->dirty_tx % TX_RING_SIZE;
+                       int tx_status = le32_to_cpu(np->tx_ring[entry].status);
+
+                       if (tx_status < 0)
+                               break;
+                       if (tx_status & 0x8000) {               /* There was an error, log it. */
+#ifndef final_version
+                               if (debug > 1)
+                                       printk(KERN_DEBUG "%s: Transmit error, Tx status %8.8x.\n",
+                                                  dev->name, tx_status);
+#endif
+                               np->stats.tx_errors++;
+                               if (tx_status & 0x0104) np->stats.tx_aborted_errors++;
+                               if (tx_status & 0x0C80) np->stats.tx_carrier_errors++;
+                               if (tx_status & 0x0200) np->stats.tx_window_errors++;
+                               if (tx_status & 0x0002) np->stats.tx_fifo_errors++;
+                               if ((tx_status & 0x0080) && np->full_duplex == 0)
+                                       np->stats.tx_heartbeat_errors++;
+#ifdef ETHER_STATS
+                               if (tx_status & 0x0100) np->stats.collisions16++;
+#endif
+                       } else {
+#ifdef ETHER_STATS
+                               if (tx_status & 0x0001) np->stats.tx_deferred++;
+#endif
+                               np->stats.tx_bytes += np->tx_skbuff[entry]->len;
+                               np->stats.collisions += (tx_status >> 3) & 15;
+                               np->stats.tx_packets++;
+                       }
+                       /* Free the original skb. */
+                       np->tx_q_bytes -= np->tx_skbuff[entry]->len;
+                       dev_kfree_skb_irq(np->tx_skbuff[entry]);
+                       np->tx_skbuff[entry] = 0;
+               }
+               if (np->tx_full &&
+                       np->cur_tx - np->dirty_tx < TX_QUEUE_LEN - 4
+                       &&  np->tx_q_bytes < TX_BUG_FIFO_LIMIT) {
+                       /* The ring is no longer full, clear tbusy. */
+                       np->tx_full = 0;
+                       netif_wake_queue(dev);
+               }
+
+               /* Abnormal error summary/uncommon events handlers. */
+               if (intr_status & (AbnormalIntr | TxFIFOUnderflow | IntrPCIErr |
+                                                  TimerInt | IntrTxStopped))
+                       netdev_error(dev, intr_status);
+
+               if (--work_limit < 0) {
+                       printk(KERN_WARNING "%s: Too much work at interrupt, "
+                                  "status=0x%4.4x.\n", dev->name, intr_status);
+                       /* Set the timer to re-enable the other interrupts after
+                          10*82usec ticks. */
+                       writel(AbnormalIntr | TimerInt, ioaddr + IntrEnable);
+                       writel(10, ioaddr + GPTimer);
+                       break;
+               }
+       } while (1);
+
+       if (debug > 3)
+               printk(KERN_DEBUG "%s: exiting interrupt, status=%#4.4x.\n",
+                          dev->name, (int)readl(ioaddr + IntrStatus));
+
+       spin_unlock(&np->lock);
+}
+
+/* This routine is logically part of the interrupt handler, but separated
+   for clarity and better register allocation. */
+static int netdev_rx(struct net_device *dev)
+{
+       struct netdev_private *np = (struct netdev_private *)dev->priv;
+       int entry = np->cur_rx % RX_RING_SIZE;
+       int work_limit = np->dirty_rx + RX_RING_SIZE - np->cur_rx;
+
+       if (debug > 4) {
+               printk(KERN_DEBUG " In netdev_rx(), entry %d status %4.4x.\n",
+                          entry, np->rx_ring[entry].status);
+       }
+
+       /* If EOP is set on the next entry, it's a new packet. Send it up. */
+       while (--work_limit >= 0) {
+               struct w840_rx_desc *desc = np->rx_head_desc;
+               s32 status = le32_to_cpu(desc->status);
+
+               if (debug > 4)
+                       printk(KERN_DEBUG "  netdev_rx() status was %8.8x.\n",
+                                  status);
+               if (status < 0)
+                       break;
+               if ((status & 0x38008300) != 0x0300) {
+                       if ((status & 0x38000300) != 0x0300) {
+                               /* Ingore earlier buffers. */
+                               if ((status & 0xffff) != 0x7fff) {
+                                       printk(KERN_WARNING "%s: Oversized Ethernet frame spanned "
+                                                  "multiple buffers, entry %#x status %4.4x!\n",
+                                                  dev->name, np->cur_rx, status);
+                                       np->stats.rx_length_errors++;
+                               }
+                       } else if (status & 0x8000) {
+                               /* There was a fatal error. */
+                               if (debug > 2)
+                                       printk(KERN_DEBUG "%s: Receive error, Rx status %8.8x.\n",
+                                                  dev->name, status);
+                               np->stats.rx_errors++; /* end of a packet.*/
+                               if (status & 0x0890) np->stats.rx_length_errors++;
+                               if (status & 0x004C) np->stats.rx_frame_errors++;
+                               if (status & 0x0002) np->stats.rx_crc_errors++;
+                       }
+               } else {
+                       struct sk_buff *skb;
+                       /* Omit the four octet CRC from the length. */
+                       int pkt_len = ((status >> 16) & 0x7ff) - 4;
+
+#ifndef final_version
+                       if (debug > 4)
+                               printk(KERN_DEBUG "  netdev_rx() normal Rx pkt length %d"
+                                          " status %x.\n", pkt_len, status);
+#endif
+                       /* Check if the packet is long enough to accept without copying
+                          to a minimally-sized skbuff. */
+                       if (pkt_len < rx_copybreak
+                               && (skb = dev_alloc_skb(pkt_len + 2)) != NULL) {
+                               skb->dev = dev;
+                               skb_reserve(skb, 2);    /* 16 byte align the IP header */
+                               /* Call copy + cksum if available. */
+#if HAS_IP_COPYSUM
+                               eth_copy_and_sum(skb, np->rx_skbuff[entry]->tail, pkt_len, 0);
+                               skb_put(skb, pkt_len);
+#else
+                               memcpy(skb_put(skb, pkt_len), np->rx_skbuff[entry]->tail,
+                                          pkt_len);
+#endif
+                       } else {
+                               char *temp = skb_put(skb = np->rx_skbuff[entry], pkt_len);
+                               np->rx_skbuff[entry] = NULL;
+#ifndef final_version                          /* Remove after testing. */
+                               if (le32desc_to_virt(desc->buffer1) != temp)
+                                       printk(KERN_ERR "%s: Internal fault: The skbuff addresses "
+                                                  "do not match in netdev_rx: %p vs. %p / %p.\n",
+                                                  dev->name, le32desc_to_virt(desc->buffer1),
+                                                  skb->head, temp);
+#endif
+                       }
+#ifndef final_version                          /* Remove after testing. */
+                       /* You will want this info for the initial debug. */
+                       if (debug > 5)
+                               printk(KERN_DEBUG "  Rx data %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:"
+                                          "%2.2x %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x %2.2x%2.2x "
+                                          "%d.%d.%d.%d.\n",
+                                          skb->data[0], skb->data[1], skb->data[2], skb->data[3],
+                                          skb->data[4], skb->data[5], skb->data[6], skb->data[7],
+                                          skb->data[8], skb->data[9], skb->data[10],
+                                          skb->data[11], skb->data[12], skb->data[13],
+                                          skb->data[14], skb->data[15], skb->data[16],
+                                          skb->data[17]);
+#endif
+                       skb->protocol = eth_type_trans(skb, dev);
+                       netif_rx(skb);
+                       dev->last_rx = jiffies;
+                       np->stats.rx_packets++;
+                       np->stats.rx_bytes += pkt_len;
+               }
+               entry = (++np->cur_rx) % RX_RING_SIZE;
+               np->rx_head_desc = &np->rx_ring[entry];
+       }
+
+       /* Refill the Rx ring buffers. */
+       for (; np->cur_rx - np->dirty_rx > 0; np->dirty_rx++) {
+               struct sk_buff *skb;
+               entry = np->dirty_rx % RX_RING_SIZE;
+               if (np->rx_skbuff[entry] == NULL) {
+                       skb = dev_alloc_skb(np->rx_buf_sz);
+                       np->rx_skbuff[entry] = skb;
+                       if (skb == NULL)
+                               break;                  /* Better luck next round. */
+                       skb->dev = dev;                 /* Mark as being used by this device. */
+                       np->rx_ring[entry].buffer1 = virt_to_le32desc(skb->tail);
+               }
+               np->rx_ring[entry].status = cpu_to_le32(DescOwn);
+       }
+
+       return 0;
+}
+
+static void netdev_error(struct net_device *dev, int intr_status)
+{
+       long ioaddr = dev->base_addr;
+       struct netdev_private *np = (struct netdev_private *)dev->priv;
+
+       if (debug > 2)
+               printk(KERN_DEBUG "%s: Abnormal event, %8.8x.\n",
+                          dev->name, intr_status);
+       if (intr_status == 0xffffffff)
+               return;
+       if (intr_status & TxFIFOUnderflow) {
+               np->csr6 += 0x4000;     /* Bump up the Tx threshold */
+               printk(KERN_DEBUG "%s: Tx underflow, increasing threshold to %8.8x.\n",
+                          dev->name, np->csr6);
+               writel(np->csr6, ioaddr + NetworkConfig);
+       }
+       if (intr_status & IntrRxDied) {         /* Missed a Rx frame. */
+               np->stats.rx_errors++;
+       }
+       if (intr_status & TimerInt) {
+               /* Re-enable other interrupts. */
+               writel(0x1A0F5, ioaddr + IntrEnable);
+       }
+       np->stats.rx_missed_errors += readl(ioaddr + RxMissed) & 0xffff;
+       writel(0, ioaddr + RxStartDemand);
+}
+
+static struct net_device_stats *get_stats(struct net_device *dev)
+{
+       long ioaddr = dev->base_addr;
+       struct netdev_private *np = (struct netdev_private *)dev->priv;
+
+       /* The chip only need report frame silently dropped. */
+       if (netif_running(dev))
+               np->stats.rx_missed_errors += readl(ioaddr + RxMissed) & 0xffff;
+
+       return &np->stats;
+}
+
+static unsigned const ethernet_polynomial = 0x04c11db7U;
+static inline u32 ether_crc(int length, unsigned char *data)
+{
+    int crc = -1;
+
+    while(--length >= 0) {
+               unsigned char current_octet = *data++;
+               int bit;
+               for (bit = 0; bit < 8; bit++, current_octet >>= 1) {
+                       crc = (crc << 1) ^
+                               ((crc < 0) ^ (current_octet & 1) ? ethernet_polynomial : 0);
+               }
+    }
+    return crc;
+}
+
+static void set_rx_mode(struct net_device *dev)
+{
+       struct netdev_private *np = (struct netdev_private *)dev->priv;
+       long ioaddr = dev->base_addr;
+       u32 mc_filter[2];                       /* Multicast hash filter */
+       u32 rx_mode;
+
+       if (dev->flags & IFF_PROMISC) {                 /* Set promiscuous. */
+               /* Unconditionally log net taps. */
+               printk(KERN_NOTICE "%s: Promiscuous mode enabled.\n", dev->name);
+               memset(mc_filter, 0xff, sizeof(mc_filter));
+               rx_mode = AcceptBroadcast | AcceptMulticast | AcceptAllPhys
+                       | AcceptMyPhys;
+       } else if ((dev->mc_count > multicast_filter_limit)
+                          ||  (dev->flags & IFF_ALLMULTI)) {
+               /* Too many to match, or accept all multicasts. */
+               memset(mc_filter, 0xff, sizeof(mc_filter));
+               rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys;
+       } else {
+               struct dev_mc_list *mclist;
+               int i;
+               memset(mc_filter, 0, sizeof(mc_filter));
+               for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count;
+                        i++, mclist = mclist->next) {
+                       set_bit((ether_crc(ETH_ALEN, mclist->dmi_addr) >> 26) ^ 0x3F,
+                                       mc_filter);
+               }
+               rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys;
+       }
+       writel(mc_filter[0], ioaddr + MulticastFilter0);
+       writel(mc_filter[1], ioaddr + MulticastFilter1);
+       np->csr6 &= ~0x00F8;
+       np->csr6 |= rx_mode;
+       writel(np->csr6, ioaddr + NetworkConfig);
+}
+
+static int mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+       u16 *data = (u16 *)&rq->ifr_data;
+
+       switch(cmd) {
+       case SIOCDEVPRIVATE:            /* Get the address of the PHY in use. */
+               data[0] = ((struct netdev_private *)dev->priv)->phys[0] & 0x1f;
+               /* Fall Through */
+       case SIOCDEVPRIVATE+1:          /* Read the specified MII register. */
+               data[3] = mdio_read(dev, data[0] & 0x1f, data[1] & 0x1f);
+               return 0;
+       case SIOCDEVPRIVATE+2:          /* Write the specified MII register */
+               if (!capable(CAP_NET_ADMIN))
+                       return -EPERM;
+               mdio_write(dev, data[0] & 0x1f, data[1] & 0x1f, data[2]);
+               return 0;
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
+static int netdev_close(struct net_device *dev)
+{
+       long ioaddr = dev->base_addr;
+       struct netdev_private *np = (struct netdev_private *)dev->priv;
+       int i;
+
+       netif_stop_queue(dev);
+
+       if (debug > 1) {
+               printk(KERN_DEBUG "%s: Shutting down ethercard, status was %8.8x "
+                          "Config %8.8x.\n", dev->name, (int)readl(ioaddr + IntrStatus),
+                          (int)readl(ioaddr + NetworkConfig));
+               printk(KERN_DEBUG "%s: Queue pointers were Tx %d / %d,  Rx %d / %d.\n",
+                          dev->name, np->cur_tx, np->dirty_tx, np->cur_rx, np->dirty_rx);
+       }
+
+       /* Disable interrupts by clearing the interrupt mask. */
+       writel(0x0000, ioaddr + IntrEnable);
+
+       /* Stop the chip's Tx and Rx processes. */
+       writel(np->csr6 &= ~0x20FA, ioaddr + NetworkConfig);
+
+       if (readl(ioaddr + NetworkConfig) != 0xffffffff)
+               np->stats.rx_missed_errors += readl(ioaddr + RxMissed) & 0xffff;
+
+#ifdef __i386__
+       if (debug > 2) {
+               printk("\n"KERN_DEBUG"  Tx ring at %8.8x:\n",
+                          (int)virt_to_le32desc(np->tx_ring));
+               for (i = 0; i < TX_RING_SIZE; i++)
+                       printk(" #%d desc. %4.4x %4.4x %8.8x.\n",
+                                  i, np->tx_ring[i].length,
+                                  np->tx_ring[i].status, np->tx_ring[i].buffer1);
+               printk("\n"KERN_DEBUG "  Rx ring %8.8x:\n",
+                          (int)virt_to_le32desc(np->rx_ring));
+               for (i = 0; i < RX_RING_SIZE; i++) {
+                       printk(KERN_DEBUG " #%d desc. %4.4x %4.4x %8.8x\n",
+                                  i, np->rx_ring[i].length,
+                                  np->rx_ring[i].status, np->rx_ring[i].buffer1);
+               }
+       }
+#endif /* __i386__ debugging only */
+
+       free_irq(dev->irq, dev);
+
+       del_timer_sync(&np->timer);
+
+       /* Free all the skbuffs in the Rx queue. */
+       for (i = 0; i < RX_RING_SIZE; i++) {
+               np->rx_ring[i].status = 0;
+               if (np->rx_skbuff[i]) {
+                       dev_kfree_skb(np->rx_skbuff[i]);
+               }
+               np->rx_skbuff[i] = 0;
+       }
+       for (i = 0; i < TX_RING_SIZE; i++) {
+               if (np->tx_skbuff[i])
+                       dev_kfree_skb(np->tx_skbuff[i]);
+               np->tx_skbuff[i] = 0;
+       }
+
+       MOD_DEC_USE_COUNT;
+
+       return 0;
+}
+
+static void __devexit w840_remove1 (struct pci_dev *pdev)
+{
+       struct net_device *dev = pdev->driver_data;
+       
+       /* No need to check MOD_IN_USE, as sys_delete_module() checks. */
+       while (dev) {
+               struct netdev_private *np = (void *)(dev->priv);
+               unregister_netdev(dev);
+#ifdef USE_IO_OPS
+               release_region(dev->base_addr, pci_id_tbl[np->chip_id].io_size);
+#else
+               release_mem_region(pci_resource_start(pdev, 1),
+                                  pci_id_tbl[np->chip_id].io_size);
+               iounmap((char *)(dev->base_addr));
+#endif
+               kfree(dev);
+       }
+
+       pdev->driver_data = NULL;
+}
+
+static struct pci_driver w840_driver = {
+       name:           "winbond-840",
+       id_table:       w840_pci_tbl,
+       probe:          w840_probe1,
+       remove:         w840_remove1,
+};
+
+static int __init w840_init(void)
+{
+       return pci_module_init(&w840_driver);
+}
+
+static void __exit w840_exit(void)
+{
+       pci_unregister_driver(&w840_driver);
+}
+
+module_init(w840_init);
+module_exit(w840_exit);
index ee95e557c7cf74ab3df56a03e10468c84eb56313..7723913ba05f04e6d30852220ccf68900d838a16 100644 (file)
        0105  Cyclom_8Y above first megabyte
        0200  Cyclom_Z below first megabyte
        0201  Cyclom_Z above first megabyte
+       0300  PC300 RX 2
+       0301  PC300 RX 1
+       0310  PC300 TE 2
+       0311  PC300 TE 1
 120f  Essential Communications
        0001  Roadrunner serial HIPPI
 1210  Hyperparallel Technologies
                121a 0060  Voodoo3 3500 TV (NTSC)
                121a 0061  Voodoo3 3500 TV (PAL)
                121a 0062  Voodoo3 3500 TV (SECAM)
+       0009  Voodoo 4
 121b  Advanced Telecommunications Modules
 121c  Nippon Texaco., Ltd
 121d  Lippert Automationstechnik GmbH
index af9389d0796dca91fe0b76ff108546ae60126a62..b38a6a9a55a3d294298f78a9d0e7676fc2f2d2a3 100644 (file)
@@ -43,7 +43,6 @@ typedef __u32 ULONG;
 
 
 #include <linux/string.h>
-#include <linux/types.h>
 #include <linux/pci.h>
 #include <linux/delay.h>
 #include <linux/sched.h>
index ab84545c4f3e596b66263dba0fe70519b7de0ce4..a6f4f74ca0cada8edbe4073f36aeef3b8b9dfb30 100644 (file)
@@ -31,7 +31,6 @@
 
 #define IDESCSI_VERSION "0.9"
 
-#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/string.h>
index 5aaa15d0c711644c1d62c5d23b9997bc76dca26e..e55b26a0e72ddb1df19911257fa69d2e7b655b90 100644 (file)
@@ -47,7 +47,6 @@
 #ifndef _IPS_H_
    #define _IPS_H_
 
-   #include <linux/config.h>
    #include <asm/uaccess.h>
    #include <asm/io.h>
 
index f9c9f05d9fcc8d60f997aae767515df95049b588..2e512345b503ac94d794c407a748de95e2119401 100644 (file)
  * any later version.
  *
  */
- static char * sg_version_str = "Version: 3.1.16 (20000716)";
- static int sg_version_num = 30116; /* 2 digits for each component */
+#include <linux/config.h>
+#ifdef CONFIG_PROC_FS
+ static char * sg_version_str = "Version: 3.1.17 (20000921)";
+#endif
+ static int sg_version_num = 30117; /* 2 digits for each component */
 /*
  *  D. P. Gilbert (dgilbert@interlog.com, dougg@triode.net.au), notes:
  *      - scsi logging is available via SCSI_LOG_TIMEOUT macros. First
@@ -38,7 +41,6 @@
  *          # cat /proc/scsi/sg/debug
  *
  */
-#include <linux/config.h>
 #include <linux/module.h>
 
 #include <linux/fs.h>
@@ -235,10 +237,12 @@ static void sg_shorten_timeout(Scsi_Request * srpnt);
 static int sg_ms_to_jif(unsigned int msecs);
 static unsigned sg_jif_to_ms(int jifs);
 static int sg_allow_access(unsigned char opcode, char dev_type);
-static int sg_last_dev(void);
 static int sg_build_dir(Sg_request * srp, Sg_fd * sfp, int dxfer_len);
 static void sg_unmap_and(Sg_scatter_hold * schp, int free_also);
 static Sg_device * sg_get_dev(int dev);
+#ifdef CONFIG_PROC_FS
+static int sg_last_dev(void);
+#endif
 
 static Sg_device ** sg_dev_arr = NULL;
 
@@ -1298,18 +1302,20 @@ static void sg_detach(Scsi_Device * scsidp)
 }
 
 #ifdef MODULE
-
 MODULE_PARM(def_reserved_size, "i");
 MODULE_PARM_DESC(def_reserved_size, "size of buffer reserved for each fd");
+#endif /* MODULE */
 
-int init_module(void) {
+static int __init init_sg(void) {
+#ifdef MODULE
     if (def_reserved_size >= 0)
        sg_big_buff = def_reserved_size;
+#endif /* MODULE */
     sg_template.module = THIS_MODULE;
     return scsi_register_module(MODULE_SCSI_DEV, &sg_template);
 }
 
-void cleanup_module( void)
+static void __exit exit_sg( void)
 {
 #ifdef CONFIG_PROC_FS
     sg_proc_cleanup();
@@ -1324,7 +1330,6 @@ void cleanup_module( void)
     }
     sg_template.dev_max = 0;
 }
-#endif /* MODULE */
 
 
 #if 0
@@ -1972,6 +1977,7 @@ static Sg_request * sg_get_rq_mark(Sg_fd * sfp, int pack_id)
     return resp;
 }
 
+#ifdef CONFIG_PROC_FS
 static Sg_request * sg_get_nth_request(Sg_fd * sfp, int nth)
 {
     Sg_request * resp;
@@ -1985,6 +1991,7 @@ static Sg_request * sg_get_nth_request(Sg_fd * sfp, int nth)
     read_unlock_irqrestore(&sfp->rq_list_lock, iflags);
     return resp;
 }
+#endif
 
 /* always adds to end of list */
 static Sg_request * sg_add_request(Sg_fd * sfp)
@@ -2064,6 +2071,7 @@ static int sg_remove_request(Sg_fd * sfp, Sg_request * srp)
     return res;
 }
 
+#ifdef CONFIG_PROC_FS
 static Sg_fd * sg_get_nth_sfp(Sg_device * sdp, int nth)
 {
     Sg_fd * resp;
@@ -2077,6 +2085,7 @@ static Sg_fd * sg_get_nth_sfp(Sg_device * sdp, int nth)
     read_unlock_irqrestore(&sg_dev_arr_lock, iflags);
     return resp;
 }
+#endif
 
 static Sg_fd * sg_add_sfp(Sg_device * sdp, int dev)
 {
@@ -2410,6 +2419,7 @@ static int sg_allow_access(unsigned char opcode, char dev_type)
 }
 
 
+#ifdef CONFIG_PROC_FS
 static int sg_last_dev()
 {
     int k;
@@ -2421,6 +2431,7 @@ static int sg_last_dev()
     read_unlock_irqrestore(&sg_dev_arr_lock, iflags);
     return k + 1;   /* origin 1 */
 }
+#endif
 
 static Sg_device * sg_get_dev(int dev)
 {
@@ -2782,3 +2793,7 @@ static int sg_proc_version_info(char * buffer, int * len, off_t * begin,
     return 1;
 }
 #endif  /* CONFIG_PROC_FS */
+
+
+module_init(init_sg);
+module_exit(exit_sg);
index 3148edd42fe42044074e6f1f195d99dac4cf00f7..b0e5026fc7a5520c0c355ef231da3dba0906be74 100644 (file)
@@ -173,6 +173,47 @@ static int find_partition(Scsi_Tape *);
 static int update_partition(Scsi_Tape *);
 
 static int st_int_ioctl(Scsi_Tape *, unsigned int, unsigned long);
+
+\f
+/* #include "osst_detect.h" */
+#ifndef SIGS_FROM_OSST
+#define SIGS_FROM_OSST \
+       {"OnStream", "SC-", "", "osst"}, \
+       {"OnStream", "DI-", "", "osst"}, \
+       {"OnStream", "DP-", "", "osst"}, \
+       {"OnStream", "USB", "", "osst"}, \
+       {"OnStream", "FW-", "", "osst"}
+#endif
+
+struct st_reject_data {
+       char *vendor;
+       char *model;
+       char *rev;
+       char *driver_hint; /* Name of the correct driver, NULL if unknown */
+};
+
+static struct st_reject_data reject_list[] = {
+       /* {"XXX", "Yy-", "", NULL},  example */
+       SIGS_FROM_OSST,
+       {NULL, }};
+
+/* If the device signature is on the list of incompatible drives, the
+   function returns a pointer to the name of the correct driver (if known) */
+static char * st_incompatible(Scsi_Device* SDp)
+{
+       struct st_reject_data *rp;
+
+       for (rp=&(reject_list[0]); rp->vendor != NULL; rp++)
+               if (!strncmp(rp->vendor, SDp->vendor, strlen(rp->vendor)) &&
+                   !strncmp(rp->model, SDp->model, strlen(rp->model)) &&
+                   !strncmp(rp->rev, SDp->rev, strlen(rp->rev))) {
+                       if (rp->driver_hint)
+                               return rp->driver_hint;
+                       else
+                               return "unknown";
+               }
+       return NULL;
+}
 \f
 
 /* Convert the result to success code */
@@ -3460,9 +3501,17 @@ static int st_attach(Scsi_Device * SDp)
        ST_partstat *STps;
        int i, mode, target_nbr;
        unsigned long flags = 0;
+       char *stp;
 
        if (SDp->type != TYPE_TAPE)
                return 1;
+       if ((stp = st_incompatible(SDp))) {
+               printk(KERN_INFO
+                      "st: Found incompatible tape at scsi%d, channel %d, id %d, lun %d\n",
+                      SDp->host->host_no, SDp->channel, SDp->id, SDp->lun);
+               printk(KERN_INFO "st: The suggested driver is %s.\n", stp);
+               return 1;
+       }
 
        write_lock_irqsave(&st_dev_arr_lock, flags);
        if (st_template.nr_dev >= st_template.dev_max) {
@@ -3626,7 +3675,7 @@ static int st_attach(Scsi_Device * SDp)
 
 static int st_detect(Scsi_Device * SDp)
 {
-       if (SDp->type != TYPE_TAPE)
+       if (SDp->type != TYPE_TAPE || st_incompatible(SDp))
                return 0;
 
        printk(KERN_WARNING
index c06c5df2c0f3f8bf4f2f66bccd722e1f16544c56..9623dcb2e77d4ff0c1a21213736f3e873474ccb6 100644 (file)
@@ -363,6 +363,7 @@ static int emu10k1_audio_ioctl(struct inode *inode, struct file *file, unsigned
 
                if (get_user(val, (int *) arg))
                        return -EFAULT;
+
                DPD(2, "val is %d\n", val);
 
                if (val > 0) {
@@ -418,6 +419,7 @@ static int emu10k1_audio_ioctl(struct inode *inode, struct file *file, unsigned
 
                if (get_user(val, (int *) arg))
                        return -EFAULT;
+
                DPD(2, " val is %d\n", val);
 
                if (file->f_mode & FMODE_READ) {
@@ -464,6 +466,7 @@ static int emu10k1_audio_ioctl(struct inode *inode, struct file *file, unsigned
 
                if (get_user(val, (int *) arg))
                        return -EFAULT;
+
                DPD(2, " val is %d\n", val);
 
                if (val > 0) {
@@ -527,6 +530,7 @@ static int emu10k1_audio_ioctl(struct inode *inode, struct file *file, unsigned
 
                if (get_user(val, (int *) arg))
                        return -EFAULT;
+
                DPD(2, " val is %d\n", val);
 
                if (val != AFMT_QUERY) {
index 0d55960e143a2dbd3355dc6c9dccb2c649029663..c52ce3d850c2ce8898f338d2f5c3112fad9b3268 100644 (file)
@@ -29,6 +29,7 @@
 #include "8010.h"
 #include "hwaccess.h"
 #include <linux/init.h>
+#include <linux/sched.h>
 
 /* In A1 Silicon, these bits are in the HC register */
 #define HOOKN_BIT   (1L << 12)
index b7ad5b717f6fe0b927d535efe71f93fcaaef3997..20d35fa2039c114e51fb835f98e89c5d9e63ff1d 100644 (file)
@@ -121,14 +121,14 @@ static void __devinit audio_init(struct emu10k1_card *card)
 
        /* stereo voice */
        card->waveout.send_a[1] = 0x00;
-        card->waveout.send_b[1] = 0xff;
-        card->waveout.send_c[1] = 0x00;
+        card->waveout.send_b[1] = 0x00;
+        card->waveout.send_c[1] = 0xff;
         card->waveout.send_d[1] = 0x00;
         card->waveout.send_routing[1] = 0xd01c;
 
        card->waveout.send_a[2] = 0x00;
-        card->waveout.send_b[2] = 0x00;
-        card->waveout.send_c[2] = 0xff;
+        card->waveout.send_b[2] = 0xff;
+        card->waveout.send_c[2] = 0x00;
         card->waveout.send_d[2] = 0x00;
         card->waveout.send_routing[2] = 0xd01c;
 
index f94d3b6a480c92a75c8288e0f2ad2fbc1bb0c423..f1e69dc70f6ff48304199e716f50c7302e2fc0ec 100644 (file)
@@ -43,7 +43,7 @@
 #include "cardmi.h"
 #include "midi.h"
 
-static spinlock_t midi_spinlock = SPIN_LOCK_UNLOCKED;
+static spinlock_t midi_spinlock __attribute((unused)) = SPIN_LOCK_UNLOCKED;
 
 static void init_midi_hdr(struct midi_hdr *midihdr)
 {
index 296b5058cce261981afddc880b9ea7f94769accb..a80b9d19ac3c197e5c5a1a43c313d4547309079e 100644 (file)
@@ -1040,6 +1040,7 @@ static int emu10k1_mixer_ioctl(struct inode *inode, struct file *file, unsigned
 
                if (get_user(val, (int *) arg))
                        return -EFAULT;
+
                i = hweight32(val);
                if (i == 0)
                        return 0;       /* val = mixer_recmask(s); */
@@ -1065,6 +1066,7 @@ static int emu10k1_mixer_ioctl(struct inode *inode, struct file *file, unsigned
                        return -EINVAL;
                if (get_user(val, (int *) arg))
                        return -EFAULT;
+
                if (emu10k1_mixer_wrch(card, i, val))
                        return -EINVAL;
 
index 6a45764d1787537a92b6ed1bed69a82c4da9416a..63d21d275ac4c06f915183479552d56960e45791 100644 (file)
@@ -28,7 +28,6 @@
  * (http://www.freecom.de/)
  */
 
-#include <linux/config.h>
 #include "transport.h"
 #include "protocol.h"
 #include "usb.h"
index b15d7c5e887a1304c1e408063cdbec827c59e08e..8f581d9594fe6fdde6f410a77ff088bd6403fd66 100644 (file)
@@ -48,6 +48,7 @@
 #include "usb.h"
 #include "debug.h"
 
+#include <linux/config.h>
 #include <linux/sched.h>
 #include <linux/errno.h>
 #include <linux/malloc.h>
index bcdd9abeec8fae09e472031acedffcd8d3336a13..85dd8e9ca53584516c1c0c9c1e84ee4aa1b22b39 100644 (file)
@@ -43,6 +43,7 @@
  * 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+#include <linux/config.h>
 #include "usb.h"
 #include "scsiglue.h"
 #include "transport.h"
@@ -62,7 +63,6 @@
 #include "freecom.h"
 #endif
 
-#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/sched.h>
 #include <linux/errno.h>
index f2d8af0d7de2930935c266f96b4368cea57a15b7..339b93965e75dd0450c346639f6f0e8fe41aedf6 100644 (file)
@@ -1591,7 +1591,7 @@ static void rh_int_timer_do (unsigned long ptr)
 
        /* ignore timers firing during PM suspend, etc */
        if ((ohci->hc_control & OHCI_CTRL_HCFS) != OHCI_USB_OPER)
-               return;
+               goto out;
 
        if(ohci->rh.send) { 
                len = rh_send_irq (ohci, urb->transfer_buffer, urb->transfer_buffer_length);
@@ -1602,7 +1602,8 @@ static void rh_int_timer_do (unsigned long ptr)
 #endif
                        if (urb->complete) urb->complete (urb);
                }
-       }       
+       }
+ out:
        rh_init_int_timer (urb);
 }
 
index fdf5da239f130679a932a4e1d89f3e430147201d..881342af6201981bbca94fa6c2efa1d3a6be5d3e 100644 (file)
@@ -12,7 +12,7 @@
  * (C) Copyright 1999 Johannes Erdfelt
  * (C) Copyright 1999 Randy Dunlap
  *
- * $Id: usb-uhci.c,v 1.237 2000/08/08 14:58:17 acher Exp $
+ * $Id: usb-uhci.c,v 1.239 2000/09/19 20:15:12 acher Exp $
  */
 
 #include <linux/config.h>
@@ -48,7 +48,7 @@
 /* This enables an extra UHCI slab for memory debugging */
 #define DEBUG_SLAB
 
-#define VERSTR "$Revision: 1.237 $ time " __TIME__ " " __DATE__
+#define VERSTR "$Revision: 1.239 $ time " __TIME__ " " __DATE__
 
 #include <linux/usb.h>
 #include "usb-uhci.h"
@@ -1034,6 +1034,23 @@ _static void uhci_clean_transfer (uhci_t *s, urb_t *urb, uhci_desc_t *qh, int mo
        }
 }
 /*-------------------------------------------------------------------*/
+// Release bandwidth for Interrupt or Isoc. transfers 
+_static void uhci_release_bandwidth(urb_t *urb)
+{       
+       if (urb->bandwidth) {
+               switch (usb_pipetype(urb->pipe)) {
+               case PIPE_INTERRUPT:
+                       usb_release_bandwidth (urb->dev, urb, 0);
+                       break;
+               case PIPE_ISOCHRONOUS:
+                       usb_release_bandwidth (urb->dev, urb, 1);
+                       break;
+               default:
+                       break;
+               }
+       }       
+}
+/*-------------------------------------------------------------------*/
 // unlinks an urb by dequeuing its qh, waits some frames and forgets it
 _static int uhci_unlink_urb_sync (uhci_t *s, urb_t *urb)
 {
@@ -1055,6 +1072,7 @@ _static int uhci_unlink_urb_sync (uhci_t *s, urb_t *urb)
                if (!in_interrupt())    
                        spin_unlock(&urb->lock); 
 
+               uhci_release_bandwidth(urb);
                spin_unlock_irqrestore (&s->urb_list_lock, flags);              
                
                urb->status = -ENOENT;  // mark urb as killed           
@@ -1247,6 +1265,7 @@ _static int uhci_unlink_urb (urb_t *urb)
                if (!in_interrupt())
                        spin_lock(&urb->lock);
 
+               uhci_release_bandwidth(urb);
                ret = uhci_unlink_urb_async(s, urb);
 
                if (!in_interrupt())
@@ -1548,6 +1567,7 @@ _static int uhci_submit_urb (urb_t *urb)
        int ret = 0;
        unsigned long flags;
        urb_t *bulk_urb=NULL;
+       int bustime;
                
        if (!urb->dev || !urb->dev->bus)
                return -ENODEV;
@@ -1617,11 +1637,39 @@ _static int uhci_submit_urb (urb_t *urb)
        else {
                spin_unlock_irqrestore (&s->urb_list_lock, flags);
                switch (usb_pipetype (urb->pipe)) {
-               case PIPE_ISOCHRONOUS:
-                       ret = uhci_submit_iso_urb (urb);
+               case PIPE_ISOCHRONOUS:                  
+                       if (urb->bandwidth == 0) {      /* not yet checked/allocated */
+                               if (urb->number_of_packets <= 0) {
+                                       ret = -EINVAL;
+                                       break;
+                               }
+
+                               bustime = usb_check_bandwidth (urb->dev, urb);
+                               if (bustime < 0) {
+                                       ret = bustime;
+                                       break;
+                               }
+
+                               ret = uhci_submit_iso_urb(urb);
+                               if (ret == 0)
+                                       usb_claim_bandwidth (urb->dev, urb, bustime, 1);
+                       } else {        /* bandwidth is already set */
+                               ret = uhci_submit_iso_urb(urb);
+                       }
                        break;
                case PIPE_INTERRUPT:
-                       ret = uhci_submit_int_urb (urb);
+                       if (urb->bandwidth == 0) {      /* not yet checked/allocated */
+                               bustime = usb_check_bandwidth (urb->dev, urb);
+                               if (bustime < 0)
+                                       ret = bustime;
+                               else {
+                                       ret = uhci_submit_int_urb(urb);
+                                       if (ret == 0)
+                                               usb_claim_bandwidth (urb->dev, urb, bustime, 0);
+                               }
+                       } else {        /* bandwidth is already set */
+                               ret = uhci_submit_int_urb(urb);
+                       }
                        break;
                case PIPE_CONTROL:
                        ret = uhci_submit_control_urb (urb);
@@ -2461,6 +2509,14 @@ _static int process_urb (uhci_t *s, struct list_head *p)
        if (urb->status != -EINPROGRESS) {
                int proceed = 0;
 
+               /* Release bandwidth for Interrupt or Iso transfers */
+               if (urb->bandwidth) {
+                       if (usb_pipetype(urb->pipe)==PIPE_ISOCHRONOUS)
+                               usb_release_bandwidth (urb->dev, urb, 1);
+                       else if (usb_pipetype(urb->pipe)==PIPE_INTERRUPT && urb->interval)
+                               usb_release_bandwidth (urb->dev, urb, 0);
+               }
+
                dbg("dequeued urb: %p", urb);
                dequeue_urb (s, urb);
 
index 468cfe3f275ed72b63a365e716f6a1bf5e9e5ac1..2ebbc383e6fe754d6465d4bc8c5d0eb683b8aab6 100644 (file)
@@ -284,7 +284,7 @@ void usb_claim_bandwidth (struct usb_device *dev, struct urb *urb, int bustime,
                dev->bus->bandwidth_int_reqs++;
        urb->bandwidth = bustime;
        
-       dbg("bw_alloc increased by %d to %d for %d requesters",
+       dbg("bandwidth alloc increased by %d to %d for %d requesters",
                bustime,
                dev->bus->bandwidth_allocated,
                dev->bus->bandwidth_int_reqs + dev->bus->bandwidth_isoc_reqs);
@@ -303,7 +303,7 @@ void usb_release_bandwidth(struct usb_device *dev, struct urb *urb, int isoc)
        else
                dev->bus->bandwidth_int_reqs--;
 
-       dbg("bw_alloc reduced by %d to %d for %d requesters",
+       dbg("bandwidth alloc reduced by %d to %d for %d requesters",
                urb->bandwidth,
                dev->bus->bandwidth_allocated,
                dev->bus->bandwidth_int_reqs + dev->bus->bandwidth_isoc_reqs);
index 6abc44d603ad6f77e1885fa54ef5e23ea0c1c60f..c9c67ceca0738b51d2a7d9c3dcbc49a17f31d50d 100644 (file)
@@ -12,7 +12,7 @@ O_TARGET := fs.o
 O_OBJS    = open.o read_write.o devices.o file_table.o buffer.o \
                super.o  block_dev.o stat.o exec.o pipe.o namei.o fcntl.o \
                ioctl.o readdir.o select.o fifo.o locks.o \
-               dcache.o inode.o attr.o bad_inode.o file.o iobuf.o \
+               dcache.o inode.o attr.o bad_inode.o file.o iobuf.o dnotify.o \
                $(BINFMTS) $(FILESYSTEMS)
 OX_OBJS := filesystems.o
 
index 9af530c8bd682d8689b417b5ae27d21fde0f0e9b..c4849d32444148caff678aeaba6b46bc09132c69 100644 (file)
--- a/fs/attr.c
+++ b/fs/attr.c
@@ -9,6 +9,8 @@
 #include <linux/mm.h>
 #include <linux/string.h>
 #include <linux/smp_lock.h>
+#include <linux/dnotify.h>
+#include <linux/fcntl.h>
 
 /* Taken over from the old code... */
 
@@ -79,6 +81,28 @@ void inode_setattr(struct inode * inode, struct iattr * attr)
        mark_inode_dirty(inode);
 }
 
+static int setattr_mask(unsigned int ia_valid)
+{
+       unsigned long dn_mask = 0;
+
+       if (ia_valid & ATTR_UID)
+               dn_mask |= DN_ATTRIB;
+       if (ia_valid & ATTR_GID)
+               dn_mask |= DN_ATTRIB;
+       if (ia_valid & ATTR_SIZE)
+               dn_mask |= DN_MODIFY;
+       /* both times implies a utime(s) call */
+       if ((ia_valid & (ATTR_ATIME|ATTR_MTIME)) == (ATTR_ATIME|ATTR_MTIME))
+               dn_mask |= DN_ATTRIB;
+       else if (ia_valid & ATTR_ATIME)
+               dn_mask |= DN_ACCESS;
+       else if (ia_valid & ATTR_MTIME)
+               dn_mask |= DN_MODIFY;
+       if (ia_valid & ATTR_MODE)
+               dn_mask |= DN_ATTRIB;
+       return dn_mask;
+}
+
 int notify_change(struct dentry * dentry, struct iattr * attr)
 {
        struct inode *inode = dentry->d_inode;
@@ -101,5 +125,10 @@ int notify_change(struct dentry * dentry, struct iattr * attr)
                        inode_setattr(inode, attr);
        }
        unlock_kernel();
+       if (!error) {
+               unsigned long dn_mask = setattr_mask(ia_valid);
+               if (dn_mask)
+                       inode_dir_notify(dentry->d_parent->d_inode, dn_mask);
+       }
        return error;
 }
index fbb726a83e978cc62e516ba3685dd485afb232e3..f00018a3a772ecdf45e01d4113900a0bfcb59c17 100644 (file)
@@ -706,9 +706,7 @@ void set_blocksize(kdev_t dev, int size)
 static void refill_freelist(int size)
 {
        if (!grow_buffers(size)) {
-               balance_dirty(NODEV);
-               wakeup_kswapd(0); /* We can't wait because of __GFP_IO */
-               schedule();
+               try_to_free_pages(GFP_BUFFER);
        }
 }
 
index b1c1c1c94ffa44fefea0fecd7707db500b9c1d73..db7fbb4eca821e0f51e798e5c65cca555744c159 100644 (file)
@@ -59,7 +59,7 @@ void coda_cache_clear_all(struct super_block *sb, struct coda_cred *cred)
         list_for_each(tmp, &sbi->sbi_cihead)
         {
                cii = list_entry(tmp, struct coda_inode_info, c_cilist);
-                if ( cii->c_magic != CODA_CNODE_MAGIC ) continue;
+                if ( cii->c_magic != CODA_CNODE_MAGIC ) BUG();
 
                 if (!cred || coda_cred_eq(cred, &cii->c_cached_cred))
                         cii->c_cached_perm = 0;
index 5a38471ff8893f81f360fe55921440bf9d1d7cb3..c8dc3dd8fccb85ff476f9b1224f1d8a34d4aa384 100644 (file)
@@ -70,7 +70,7 @@ struct inode * coda_iget(struct super_block * sb, ViceFid * fid,
 
        /* check if the inode is already initialized */
        cii = ITOC(inode);
-       if (cii->c_magic == CODA_CNODE_MAGIC) {
+       if (cii->c_fid.Volume != 0 || cii->c_fid.Vnode != 0 || cii->c_fid.Unique != 0) {
                /* see if it is the right one (might have an inode collision) */
                if ( !coda_fideq(fid, &cii->c_fid) ) {
                        printk("coda_iget: initialized inode old %s new %s!\n",
@@ -85,7 +85,6 @@ struct inode * coda_iget(struct super_block * sb, ViceFid * fid,
        /* new, empty inode found... initializing */
 
        /* Initialize the Coda inode info structure */
-       cii->c_magic = CODA_CNODE_MAGIC;
        cii->c_fid   = *fid;
        cii->c_vnode = inode;
 
@@ -165,10 +164,6 @@ struct inode *coda_fid_to_inode(ViceFid *fid, struct super_block *sb)
                return NULL;
        }
 
-       if ( !fid ) {
-               printk("coda_fid_to_inode: no fid!\n");
-               return NULL;
-       }
        CDEBUG(D_INODE, "%s\n", coda_f2s(fid));
 
 
@@ -180,7 +175,7 @@ struct inode *coda_fid_to_inode(ViceFid *fid, struct super_block *sb)
                 list_for_each(le, &sbi->sbi_cihead)
                 {
                        cii = list_entry(le, struct coda_inode_info, c_cilist);
-                       if ( cii->c_magic != CODA_CNODE_MAGIC ) continue;
+                       if ( cii->c_magic != CODA_CNODE_MAGIC ) BUG();
 
                        CDEBUG(D_DOWNCALL, "iterating, now doing %s, ino %ld\n",
                               coda_f2s(&cii->c_fid), cii->c_vnode->i_ino);
@@ -208,7 +203,7 @@ struct inode *coda_fid_to_inode(ViceFid *fid, struct super_block *sb)
        cii = ITOC(inode);
 
        /* make sure this is the one we want */
-       if ( cii->c_magic == CODA_CNODE_MAGIC && coda_fideq(fid, &cii->c_fid) ) {
+       if ( coda_fideq(fid, &cii->c_fid) ) {
                 CDEBUG(D_INODE, "found %ld\n", inode->i_ino);
                 return inode;
         }
index 8c126838c4f06cf90272ecefcd43886d46895fad..94a3be38981b189a5dfff3a846130e120df960f4 100644 (file)
@@ -210,6 +210,7 @@ static void coda_read_inode(struct inode *inode)
 
        memset(cii, 0, sizeof(struct coda_inode_info));
        list_add(&cii->c_cilist, &sbi->sbi_cihead);
+        cii->c_magic = CODA_CNODE_MAGIC;
 }
 
 static void coda_clear_inode(struct inode *inode)
@@ -241,6 +242,7 @@ static void coda_clear_inode(struct inode *inode)
        coda_cache_clear_inode(inode);
 out:
        inode->u.coda_i.c_magic = 0;
+       memset(&inode->u.coda_i.c_fid, 0, sizeof(struct ViceFid));
        EXIT;
 }
 
diff --git a/fs/dnotify.c b/fs/dnotify.c
new file mode 100644 (file)
index 0000000..90fd86b
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ * Directory notifications for Linux.
+ *
+ * Copyright (C) 2000 Stephen Rothwell
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/dnotify.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+
+extern void send_sigio(struct fown_struct *fown, int fd, int band);
+
+int dir_notify_enable = 1;
+
+static rwlock_t dn_lock = RW_LOCK_UNLOCKED;
+static kmem_cache_t *dn_cache;
+
+static void redo_inode_mask(struct inode *inode)
+{
+       unsigned long new_mask;
+       struct dnotify_struct *dn;
+
+       new_mask = 0;
+       for (dn = inode->i_dnotify; dn != NULL; dn = dn->dn_next)
+               new_mask |= dn->dn_mask & ~DN_MULTISHOT;
+       inode->i_dnotify_mask = new_mask;
+}
+
+int fcntl_dirnotify(int fd, struct file *filp, unsigned long arg)
+{
+       struct dnotify_struct *dn = NULL;
+       struct dnotify_struct *odn;
+       struct dnotify_struct **prev;
+       struct inode *inode;
+       int turning_off = (arg & ~DN_MULTISHOT) == 0;
+
+       if (!turning_off && !dir_notify_enable)
+               return -EINVAL;
+       inode = filp->f_dentry->d_inode;
+       if (!S_ISDIR(inode->i_mode))
+               return -ENOTDIR;
+       if (!turning_off) {
+               dn = kmem_cache_alloc(dn_cache, SLAB_KERNEL);
+               if (dn == NULL)
+                       return -ENOMEM;
+       }
+       write_lock(&dn_lock);
+       prev = &inode->i_dnotify;
+       for (odn = *prev; odn != NULL; prev = &odn->dn_next, odn = *prev)
+               if (odn->dn_filp == filp)
+                       break;
+       if (odn != NULL) {
+               if (turning_off) {
+                       *prev = odn->dn_next;
+                       redo_inode_mask(inode);
+                       dn = odn;
+                       goto out_free;
+               }
+               odn->dn_fd = fd;
+               odn->dn_mask |= arg;
+               inode->i_dnotify_mask |= arg & ~DN_MULTISHOT;
+               goto out_free;
+       }
+       if (turning_off)
+               goto out;
+       filp->f_owner.pid = current->pid;
+       filp->f_owner.uid = current->uid;
+       filp->f_owner.euid = current->euid;
+       dn->dn_magic = DNOTIFY_MAGIC;
+       dn->dn_mask = arg;
+       dn->dn_fd = fd;
+       dn->dn_filp = filp;
+       inode->i_dnotify_mask |= arg & ~DN_MULTISHOT;
+       dn->dn_next = inode->i_dnotify;
+       inode->i_dnotify = dn;
+out:
+       write_unlock(&dn_lock);
+       return 0;
+out_free:
+       kmem_cache_free(dn_cache, dn);
+       goto out;
+}
+
+void __inode_dir_notify(struct inode *inode, unsigned long event)
+{
+       struct dnotify_struct * dn;
+       struct dnotify_struct **prev;
+       struct fown_struct *    fown;
+       int                     changed = 0;
+
+       write_lock(&dn_lock);
+       prev = &inode->i_dnotify;
+       while ((dn = *prev) != NULL) {
+               if ((dn->dn_mask & event) == 0) {
+                       prev = &dn->dn_next;
+                       continue;
+               }
+               if (dn->dn_magic != DNOTIFY_MAGIC) {
+                       printk(KERN_ERR "__inode_dir_notify: bad magic "
+                               "number in dnotify_struct!\n");
+                       return;
+               }
+               fown = &dn->dn_filp->f_owner;
+               if (fown->pid)
+                       send_sigio(fown, dn->dn_fd, POLL_MSG);
+               if (dn->dn_mask & DN_MULTISHOT)
+                       prev = &dn->dn_next;
+               else {
+                       *prev = dn->dn_next;
+                       changed = 1;
+                       kmem_cache_free(dn_cache, dn);
+               }
+       }
+       if (changed)
+               redo_inode_mask(inode);
+       write_unlock(&dn_lock);
+}
+
+static int __init dnotify_init(void)
+{
+       dn_cache = kmem_cache_create("dnotify cache",
+               sizeof(struct dnotify_struct), 0, 0, NULL, NULL);
+       if (!dn_cache)
+               panic("cannot create dnotify slab cache");
+       return 0;
+}
+
+module_init(dnotify_init)
index 7a3bcefb89e820ea1e3e6224d43fd7ae48a99093..d075c5c4e0c7b5f2ce4efae1dd66a0634edea447 100644 (file)
@@ -4,8 +4,10 @@
  *  Copyright (C) 1991, 1992  Linus Torvalds
  */
 
+#include <linux/init.h>
 #include <linux/mm.h>
 #include <linux/file.h>
+#include <linux/dnotify.h>
 #include <linux/smp_lock.h>
 #include <linux/slab.h>
 
@@ -14,6 +16,8 @@
 #include <asm/uaccess.h>
 
 extern int sock_fcntl (struct file *, unsigned int cmd, unsigned long arg);
+extern int fcntl_setlease(unsigned int fd, struct file *filp, long arg);
+extern int fcntl_getlease(struct file *filp);
 
 /* Expand files.  Return <0 on error; 0 nothing done; 1 files expanded,
  * we may have blocked. 
@@ -195,6 +199,7 @@ asmlinkage long sys_dup(unsigned int fildes)
 static int setfl(int fd, struct file * filp, unsigned long arg)
 {
        struct inode * inode = filp->f_dentry->d_inode;
+       int error;
 
        /*
         * In the case of an append-only file, O_APPEND
@@ -205,8 +210,11 @@ static int setfl(int fd, struct file * filp, unsigned long arg)
 
        /* Did FASYNC state change? */
        if ((arg ^ filp->f_flags) & FASYNC) {
-               if (filp->f_op && filp->f_op->fasync)
-                       filp->f_op->fasync(fd, filp, (arg & FASYNC) != 0);
+               if (filp->f_op && filp->f_op->fasync) {
+                       error = filp->f_op->fasync(fd, filp, (arg & FASYNC) != 0);
+                       if (error < 0)
+                               return error;
+               }
        }
 
        /* required for strict SunOS emulation */
@@ -221,11 +229,10 @@ static int setfl(int fd, struct file * filp, unsigned long arg)
 static long do_fcntl(unsigned int fd, unsigned int cmd,
                     unsigned long arg, struct file * filp)
 {
-       long err = 0;
+       long err = -EINVAL;
 
        switch (cmd) {
                case F_DUPFD:
-                       err = -EINVAL;
                        if (arg < NR_OPEN) {
                                get_file(filp);
                                err = dupfd(filp, arg);
@@ -235,20 +242,21 @@ static long do_fcntl(unsigned int fd, unsigned int cmd,
                        err = get_close_on_exec(fd);
                        break;
                case F_SETFD:
+                       err = 0;
                        set_close_on_exec(fd, arg&1);
                        break;
                case F_GETFL:
                        err = filp->f_flags;
                        break;
                case F_SETFL:
+                       lock_kernel();
                        err = setfl(fd, filp, arg);
+                       unlock_kernel();
                        break;
                case F_GETLK:
                        err = fcntl_getlk(fd, (struct flock *) arg);
                        break;
                case F_SETLK:
-                       err = fcntl_setlk(fd, cmd, (struct flock *) arg);
-                       break;
                case F_SETLKW:
                        err = fcntl_setlk(fd, cmd, (struct flock *) arg);
                        break;
@@ -263,11 +271,14 @@ static long do_fcntl(unsigned int fd, unsigned int cmd,
                        err = filp->f_owner.pid;
                        break;
                case F_SETOWN:
+                       lock_kernel();
                        filp->f_owner.pid = arg;
                        filp->f_owner.uid = current->uid;
                        filp->f_owner.euid = current->euid;
+                       err = 0;
                        if (S_ISSOCK (filp->f_dentry->d_inode->i_mode))
                                err = sock_fcntl (filp, F_SETOWN, arg);
+                       unlock_kernel();
                        break;
                case F_GETSIG:
                        err = filp->f_owner.signum;
@@ -275,12 +286,20 @@ static long do_fcntl(unsigned int fd, unsigned int cmd,
                case F_SETSIG:
                        /* arg == 0 restores default behaviour. */
                        if (arg < 0 || arg > _NSIG) {
-                               err = -EINVAL;
                                break;
                        }
                        err = 0;
                        filp->f_owner.signum = arg;
                        break;
+               case F_GETLEASE:
+                       err = fcntl_getlease(filp);
+                       break;
+               case F_SETLEASE:
+                       err = fcntl_setlease(fd, filp, arg);
+                       break;
+               case F_NOTIFY:
+                       err = fcntl_dirnotify(fd, filp, arg);
+                       break;
                default:
                        /* sockets need a few special fcntls. */
                        err = -EINVAL;
@@ -301,9 +320,7 @@ asmlinkage long sys_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg)
        if (!filp)
                goto out;
 
-       lock_kernel();
        err = do_fcntl(fd, cmd, arg, filp);
-       unlock_kernel();
 
        fput(filp);
 out:
@@ -356,7 +373,7 @@ static long band_table[NSIGPOLL] = {
 
 static void send_sigio_to_task(struct task_struct *p,
                               struct fown_struct *fown, 
-                              struct fasync_struct *fa,
+                              int fd,
                               int reason)
 {
        if ((fown->euid != 0) &&
@@ -384,7 +401,7 @@ static void send_sigio_to_task(struct task_struct *p,
                                si.si_band  = ~0L;
                        else
                                si.si_band = band_table[reason - POLL_IN];
-                       si.si_fd    = fa->fa_fd;
+                       si.si_fd    = fd;
                        if (!send_sig_info(fown->signum, &si, p))
                                break;
                /* fall-through: fall back on the old plain SIGIO signal */
@@ -393,15 +410,14 @@ static void send_sigio_to_task(struct task_struct *p,
        }
 }
 
-static void send_sigio(struct fown_struct *fown, struct fasync_struct *fa, 
-                      int band)
+void send_sigio(struct fown_struct *fown, int fd, int band)
 {
        struct task_struct * p;
        int   pid       = fown->pid;
        
        read_lock(&tasklist_lock);
        if ( (pid > 0) && (p = find_task_by_pid(pid)) ) {
-               send_sigio_to_task(p, fown, fa, band);
+               send_sigio_to_task(p, fown, fd, band);
                goto out;
        }
        for_each_task(p) {
@@ -410,18 +426,20 @@ static void send_sigio(struct fown_struct *fown, struct fasync_struct *fa,
                        match = -p->pgrp;
                if (pid != match)
                        continue;
-               send_sigio_to_task(p, fown, fa, band);
+               send_sigio_to_task(p, fown, fd, band);
        }
 out:
        read_unlock(&tasklist_lock);
 }
 
+static rwlock_t fasync_lock = RW_LOCK_UNLOCKED;
+static kmem_cache_t *fasync_cache;
+
 /*
  * fasync_helper() is used by some character device drivers (mainly mice)
  * to set up the fasync queue. It returns negative on error, 0 if it did
  * no changes and positive if it added/deleted the entry.
  */
-static rwlock_t fasync_lock = RW_LOCK_UNLOCKED;
 int fasync_helper(int fd, struct file * filp, int on, struct fasync_struct **fapp)
 {
        struct fasync_struct *fa, **fp;
@@ -429,7 +447,7 @@ int fasync_helper(int fd, struct file * filp, int on, struct fasync_struct **fap
        int result = 0;
 
        if (on) {
-               new = kmalloc(sizeof(struct fasync_struct), GFP_KERNEL);
+               new = kmem_cache_alloc(fasync_cache, SLAB_KERNEL);
                if (!new)
                        return -ENOMEM;
        }
@@ -438,10 +456,10 @@ int fasync_helper(int fd, struct file * filp, int on, struct fasync_struct **fap
                if (fa->fa_file == filp) {
                        if(on) {
                                fa->fa_fd = fd;
-                               kfree(new);
+                               kmem_cache_free(fasync_cache, new);
                        } else {
                                *fp = fa->fa_next;
-                               kfree(fa);
+                               kmem_cache_free(fasync_cache, fa);
                                result = 1;
                        }
                        goto out;
@@ -466,7 +484,7 @@ void __kill_fasync(struct fasync_struct *fa, int sig, int band)
        while (fa) {
                struct fown_struct * fown;
                if (fa->magic != FASYNC_MAGIC) {
-                       printk("kill_fasync: bad magic number in "
+                       printk(KERN_ERR "kill_fasync: bad magic number in "
                               "fasync_struct!\n");
                        return;
                }
@@ -475,7 +493,7 @@ void __kill_fasync(struct fasync_struct *fa, int sig, int band)
                   queued signum: SIGURG has its own default signalling
                   mechanism. */
                if (fown->pid && !(sig == SIGURG && fown->signum == 0))
-                       send_sigio(fown, fa, band);
+                       send_sigio(fown, fa->fa_fd, band);
                fa = fa->fa_next;
        }
 }
@@ -486,3 +504,14 @@ void kill_fasync(struct fasync_struct **fp, int sig, int band)
        __kill_fasync(*fp, sig, band);
        read_unlock(&fasync_lock);
 }
+
+static int __init fasync_init(void)
+{
+       fasync_cache = kmem_cache_create("fasync cache",
+               sizeof(struct fasync_struct), 0, 0, NULL, NULL);
+       if (!fasync_cache)
+               panic("cannot create fasync slab cache");
+       return 0;
+}
+
+module_init(fasync_init)
index a3a4f072f31ac550a23f5e5e8dbfbf4ad46fc69e..820bc4c7bd46ec66efdb4dda2aefa5c75433179e 100644 (file)
@@ -168,6 +168,7 @@ reclaimer(void *ptr)
         * reclaim is in progress */
        lock_kernel();
        lockd_up();
+       down(&file_lock_sem);
 
        /* First, reclaim all locks that have been granted previously. */
 restart:
@@ -185,6 +186,7 @@ restart:
                }
                tmp = tmp->next;
        }
+       up(&file_lock_sem);
 
        host->h_reclaiming = 0;
        wake_up(&host->h_gracewait);
index a5ec6c774962ecdade296aab906209af4e2957a6..c60f48da11aa839e5bd155ec88a90f71c38338ea 100644 (file)
@@ -47,7 +47,6 @@ nlmclnt_setlockargs(struct nlm_rqst *req, struct file_lock *fl)
        struct nlm_args *argp = &req->a_args;
        struct nlm_lock *lock = &argp->lock;
 
-       memset(argp, 0, sizeof(*argp));
        nlmclnt_next_cookie(&argp->cookie);
        argp->state   = nsm_local_state;
        memcpy(&lock->fh, NFS_FH(fl->fl_file->f_dentry), sizeof(struct nfs_fh));
@@ -55,7 +54,7 @@ nlmclnt_setlockargs(struct nlm_rqst *req, struct file_lock *fl)
        lock->oh.data = req->a_owner;
        lock->oh.len  = sprintf(req->a_owner, "%d@%s",
                                current->pid, system_utsname.nodename);
-       lock->fl      = *fl;
+       locks_copy_lock(&lock->fl, fl);
 }
 
 /*
@@ -157,7 +156,9 @@ nlmclnt_proc(struct inode *inode, int cmd, struct file_lock *fl)
                call->a_flags = RPC_TASK_ASYNC;
        } else {
                spin_unlock_irqrestore(&current->sigmask_lock, flags);
-               call->a_flags = 0;
+               memset(call, 0, sizeof(*call));
+               locks_init_lock(&call->a_args.lock.fl);
+               locks_init_lock(&call->a_res.lock.fl);
        }
        call->a_host = host;
 
@@ -214,8 +215,12 @@ nlmclnt_alloc_call(void)
 
        while (!signalled()) {
                call = (struct nlm_rqst *) kmalloc(sizeof(struct nlm_rqst), GFP_KERNEL);
-               if (call)
+               if (call) {
+                       memset(call, 0, sizeof(*call));
+                       locks_init_lock(&call->a_args.lock.fl);
+                       locks_init_lock(&call->a_res.lock.fl);
                        return call;
+               }
                printk("nlmclnt_alloc_call: failed, waiting for memory\n");
                current->state = TASK_INTERRUPTIBLE;
                schedule_timeout(5*HZ);
@@ -389,7 +394,7 @@ nlmclnt_test(struct nlm_rqst *req, struct file_lock *fl)
                 * Report the conflicting lock back to the application.
                 * FIXME: Is it OK to report the pid back as well?
                 */
-               memcpy(fl, &req->a_res.lock.fl, sizeof(*fl));
+               locks_copy_lock(fl, &req->a_res.lock.fl);
                /* fl->fl_pid = 0; */
        } else {
                return nlm_stat_to_errno(req->a_res.status);
@@ -476,6 +481,9 @@ nlmclnt_reclaim(struct nlm_host *host, struct file_lock *fl)
        int             status;
 
        req = &reqst;
+       memset(req, 0, sizeof(*req));
+       locks_init_lock(&req->a_args.lock.fl);
+       locks_init_lock(&req->a_res.lock.fl);
        req->a_host  = host;
        req->a_flags = 0;
 
index 56c8d8173902a2063af58b50437eb4c52f576b2b..a175d39eb029060d5555d658a9f3b65afc97cca3 100644 (file)
@@ -170,6 +170,8 @@ nlmsvc_create_block(struct svc_rqst *rqstp, struct nlm_file *file,
        if (!(block = (struct nlm_block *) kmalloc(sizeof(*block), GFP_KERNEL)))
                goto failed;
        memset(block, 0, sizeof(*block));
+       locks_init_lock(&block->b_call.a_args.lock.fl);
+       locks_init_lock(&block->b_call.a_res.lock.fl);
 
        /* Set notifier function for VFS, and init args */
        lock->fl.fl_notify = nlmsvc_notify_blocked;
@@ -347,7 +349,7 @@ again:
        /* Append to list of blocked */
        nlmsvc_insert_block(block, NLM_NEVER);
 
-       if (!list_empty(&block->b_call.a_args.lock.fl.fl_block)) {
+       if (list_empty(&block->b_call.a_args.lock.fl.fl_list)) {
                /* Now add block to block list of the conflicting lock
                   if we haven't done so. */
                dprintk("lockd: blocking on this lock.\n");
index 4b34b97862d6246aef8cb897144b6a63e5563d88..ab06a5d31c52d1902b17928438b2676147da9847 100644 (file)
@@ -54,7 +54,6 @@ nlmsvc_share_file(struct nlm_host *host, struct nlm_file *file,
        share->s_owner.len  = oh->len;
        share->s_next       = file->f_shares;
        file->f_shares      = share;
-       file->f_count      += 1;
 
 update:
        share->s_access = argp->fsm_access;
index d93ed2ac5e7a5831d035a89b66e1316484070602..062191b9a333ce2f84d589dbb0172be51d31dbc2 100644 (file)
@@ -129,7 +129,7 @@ nlm_decode_lock(u32 *p, struct nlm_lock *lock)
         || !(p = nlm_decode_oh(p, &lock->oh)))
                return NULL;
 
-       memset(fl, 0, sizeof(*fl));
+       locks_init_lock(fl);
        fl->fl_owner = current->files;
        fl->fl_pid   = ntohl(*p++);
        fl->fl_flags = FL_POSIX;
@@ -314,6 +314,7 @@ nlmsvc_decode_shareargs(struct svc_rqst *rqstp, u32 *p, nlm_args *argp)
        int             len;
 
        memset(lock, 0, sizeof(*lock));
+       locks_init_lock(&lock->fl);
        lock->fl.fl_pid = ~(u32) 0;
 
        if (!(p = nlm_decode_cookie(p, &argp->cookie))
@@ -430,6 +431,7 @@ nlmclt_decode_testres(struct rpc_rqst *req, u32 *p, struct nlm_res *resp)
                s32                     start, len, end;
 
                memset(&resp->lock, 0, sizeof(resp->lock));
+               locks_init_lock(fl);
                excl = ntohl(*p++);
                fl->fl_pid = ntohl(*p++);
                if (!(p = nlm_decode_oh(p, &resp->lock.oh)))
index 7ffa0a433104b1d243ceafba48a1d729c5dcb5b8..739387ab954504f2061233cb0def844c3b1128b6 100644 (file)
@@ -131,7 +131,7 @@ nlm4_decode_lock(u32 *p, struct nlm_lock *lock)
         || !(p = nlm4_decode_oh(p, &lock->oh)))
                return NULL;
 
-       memset(fl, 0, sizeof(*fl));
+       locks_init_lock(fl);
        fl->fl_owner = current->files;
        fl->fl_pid   = ntohl(*p++);
        fl->fl_flags = FL_POSIX;
@@ -322,6 +322,7 @@ nlm4svc_decode_shareargs(struct svc_rqst *rqstp, u32 *p, nlm_args *argp)
        int             len;
 
        memset(lock, 0, sizeof(*lock));
+       locks_init_lock(&lock->fl);
        lock->fl.fl_pid = ~(u32) 0;
 
        if (!(p = nlm4_decode_cookie(p, &argp->cookie))
@@ -438,6 +439,7 @@ nlm4clt_decode_testres(struct rpc_rqst *req, u32 *p, struct nlm_res *resp)
                s64                     start, end, len;
 
                memset(&resp->lock, 0, sizeof(resp->lock));
+               locks_init_lock(fl);
                excl = ntohl(*p++);
                fl->fl_pid = ntohl(*p++);
                if (!(p = nlm4_decode_oh(p, &resp->lock.oh)))
index 1aeb4d7ae27ce23c503d58f5eec4c73ad0cbaa6f..9c9cc1e41d999a057e0e036a747854bdf52c1cdc 100644 (file)
@@ -1,3 +1,4 @@
+#define MSNFS  /* HACK HACK */
 /*
  *  linux/fs/locks.c
  *
  *  Use generic list implementation from <linux/list.h>.
  *  Sped up posix_locks_deadlock by only considering blocked locks.
  *  Matthew Wilcox <willy@thepuffingroup.com>, March, 2000.
+ *
+ *  Leases and LOCK_MAND
+ *  Matthew Wilcox <willy@linuxcare.com>, June, 2000.
+ *  Stephen Rothwell <sfr@linuxcare.com>, June, 2000.
  */
 
 #include <linux/malloc.h>
 #include <linux/file.h>
 #include <linux/smp_lock.h>
 #include <linux/init.h>
+#include <linux/capability.h>
+#include <linux/sched.h>
 
+#include <asm/semaphore.h>
 #include <asm/uaccess.h>
 
+DECLARE_MUTEX(file_lock_sem);
+
+#define acquire_fl_sem()       down(&file_lock_sem)
+#define release_fl_sem()       up(&file_lock_sem)
+
+int leases_enable = 1;
+int lease_break_time = 45;
+
 LIST_HEAD(file_lock_list);
 static LIST_HEAD(blocked_list);
 
 static kmem_cache_t *filelock_cache;
 
 /* Allocate an empty lock structure. */
-static struct file_lock *locks_alloc_lock(void)
+static struct file_lock *locks_alloc_lock(int account)
 {
        struct file_lock *fl;
+       if (account && current->locks >= current->rlim[RLIMIT_LOCKS].rlim_cur)
+               return NULL;
        fl = kmem_cache_alloc(filelock_cache, SLAB_KERNEL);
+       if (fl)
+               current->locks++;
        return fl;
 }
 
@@ -137,19 +157,38 @@ static inline void locks_free_lock(struct file_lock *fl)
                BUG();
                return;
        }
-
+       current->locks--;
        if (waitqueue_active(&fl->fl_wait))
                panic("Attempting to free lock with active wait queue");
 
        if (!list_empty(&fl->fl_block))
                panic("Attempting to free lock with active block list");
 
-       if (!list_empty(&fl->fl_link))
+       if (!list_empty(&fl->fl_link) || !list_empty(&fl->fl_list))
                panic("Attempting to free lock on active lock list");
 
        kmem_cache_free(filelock_cache, fl);
 }
 
+void locks_init_lock(struct file_lock *fl)
+{
+       INIT_LIST_HEAD(&fl->fl_link);
+       INIT_LIST_HEAD(&fl->fl_block);
+       INIT_LIST_HEAD(&fl->fl_list);
+       init_waitqueue_head(&fl->fl_wait);
+       fl->fl_next = NULL;
+       fl->fl_fasync = NULL;
+       fl->fl_owner = 0;
+       fl->fl_pid = 0;
+       fl->fl_file = NULL;
+       fl->fl_flags = 0;
+       fl->fl_type = 0;
+       fl->fl_start = fl->fl_end = 0;
+       fl->fl_notify = NULL;
+       fl->fl_insert = NULL;
+       fl->fl_remove = NULL;
+}
+
 /*
  * Initialises the fields of the file lock which are invariant for
  * free file_locks.
@@ -162,16 +201,13 @@ static void init_once(void *foo, kmem_cache_t *cache, unsigned long flags)
                                        SLAB_CTOR_CONSTRUCTOR)
                return;
 
-       lock->fl_next = NULL;
-       INIT_LIST_HEAD(&lock->fl_link);
-       INIT_LIST_HEAD(&lock->fl_block);
-       init_waitqueue_head(&lock->fl_wait);
+       locks_init_lock(lock);
 }
 
 /*
  * Initialize a new lock from an existing file_lock structure.
  */
-static void locks_copy_lock(struct file_lock *new, struct file_lock *fl)
+void locks_copy_lock(struct file_lock *new, struct file_lock *fl)
 {
        new->fl_owner = fl->fl_owner;
        new->fl_pid = fl->fl_pid;
@@ -189,7 +225,7 @@ static void locks_copy_lock(struct file_lock *new, struct file_lock *fl)
 /* Fill in a file_lock structure with an appropriate FLOCK lock. */
 static struct file_lock *flock_make_lock(struct file *filp, unsigned int type)
 {
-       struct file_lock *fl = locks_alloc_lock();
+       struct file_lock *fl = locks_alloc_lock(1);
        if (fl == NULL)
                return NULL;
 
@@ -207,6 +243,20 @@ static struct file_lock *flock_make_lock(struct file *filp, unsigned int type)
        return fl;
 }
 
+static int assign_type(struct file_lock *fl, int type)
+{
+       switch (type) {
+       case F_RDLCK:
+       case F_WRLCK:
+       case F_UNLCK:
+               fl->fl_type = type;
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
 /* Verify a "struct flock" and copy it to a "struct file_lock" as a POSIX
  * style lock.
  */
@@ -234,6 +284,8 @@ static int flock_to_posix_lock(struct file *filp, struct file_lock *fl,
        fl->fl_end = start + l->l_len - 1;
        if (l->l_len > 0 && fl->fl_end < 0)
                return (0);
+       if (fl->fl_end > OFFT_OFFSET_MAX)
+               return 0;
        fl->fl_start = start;   /* we record the absolute position */
        if (l->l_len == 0)
                fl->fl_end = OFFSET_MAX;
@@ -246,17 +298,7 @@ static int flock_to_posix_lock(struct file *filp, struct file_lock *fl,
        fl->fl_insert = NULL;
        fl->fl_remove = NULL;
 
-       switch (l->l_type) {
-       case F_RDLCK:
-       case F_WRLCK:
-       case F_UNLCK:
-               fl->fl_type = l->l_type;
-               break;
-       default:
-               return (0);
-       }
-
-       return (1);
+       return (assign_type(fl, l->l_type) == 0);
 }
 
 #if BITS_PER_LONG == 32
@@ -310,6 +352,32 @@ static int flock64_to_posix_lock(struct file *filp, struct file_lock *fl,
 }
 #endif
 
+/* Allocate a file_lock initialised to this type of lease */
+static int lease_alloc(struct file *filp, int type, struct file_lock **flp)
+{
+       struct file_lock *fl = locks_alloc_lock(1);
+       if (fl == NULL)
+               return -ENOMEM;
+
+       fl->fl_owner = current->files;
+       fl->fl_pid = current->pid;
+
+       fl->fl_file = filp;
+       fl->fl_flags = FL_LEASE;
+       if (assign_type(fl, type) != 0) {
+               locks_free_lock(fl);
+               return -EINVAL;
+       }
+       fl->fl_start = 0;
+       fl->fl_end = OFFSET_MAX;
+       fl->fl_notify = NULL;
+       fl->fl_insert = NULL;
+       fl->fl_remove = NULL;
+
+       *flp = fl;
+       return 0;
+}
+
 /* Check if two locks overlap each other.
  */
 static inline int locks_overlap(struct file_lock *fl1, struct file_lock *fl2)
@@ -335,8 +403,8 @@ locks_same_owner(struct file_lock *fl1, struct file_lock *fl2)
  */
 static void locks_delete_block(struct file_lock *waiter)
 {
-       list_del(&waiter->fl_block);
-       INIT_LIST_HEAD(&waiter->fl_block);
+       list_del(&waiter->fl_list);
+       INIT_LIST_HEAD(&waiter->fl_list);
        list_del(&waiter->fl_link);
        INIT_LIST_HEAD(&waiter->fl_link);
 }
@@ -349,15 +417,14 @@ static void locks_delete_block(struct file_lock *waiter)
 static void locks_insert_block(struct file_lock *blocker, 
                               struct file_lock *waiter)
 {
-       if (!list_empty(&waiter->fl_block)) {
+       if (!list_empty(&waiter->fl_list)) {
                printk(KERN_ERR "locks_insert_block: removing duplicated lock "
                        "(pid=%d %Ld-%Ld type=%d)\n", waiter->fl_pid,
                        waiter->fl_start, waiter->fl_end, waiter->fl_type);
                locks_delete_block(waiter);
        }
-       list_add_tail(&waiter->fl_block, &blocker->fl_block);
+       list_add_tail(&waiter->fl_list, &blocker->fl_block);
        list_add(&waiter->fl_link, &blocked_list);
-       waiter->fl_next = blocker;
 }
 
 /* Wake up processes blocked waiting for blocker.
@@ -367,7 +434,7 @@ static void locks_insert_block(struct file_lock *blocker,
 static void locks_wake_up_blocks(struct file_lock *blocker, unsigned int wait)
 {
        while (!list_empty(&blocker->fl_block)) {
-               struct file_lock *waiter = list_entry(blocker->fl_block.next, struct file_lock, fl_block);
+               struct file_lock *waiter = list_entry(blocker->fl_block.next, struct file_lock, fl_list);
                /* N.B. Is it possible for the notify function to block?? */
                if (waiter->fl_notify)
                        waiter->fl_notify(waiter);
@@ -402,10 +469,10 @@ static void locks_insert_lock(struct file_lock **pos, struct file_lock *fl)
                fl->fl_insert(fl);
 }
 
-/* Delete a lock and free it.
- * First remove our lock from the active lock lists. Then call
- * locks_wake_up_blocks() to wake up processes that are blocked
- * waiting for this lock. Finally free the lock structure.
+/* Delete a lock and then free it.
+ * Remove our lock from the lock lists, wake up processes that are blocked
+ * waiting for this lock, notify the FS that the lock has been cleared and
+ * finally free the lock.
  */
 static void locks_delete_lock(struct file_lock **thisfl_p, unsigned int wait)
 {
@@ -418,6 +485,12 @@ static void locks_delete_lock(struct file_lock **thisfl_p, unsigned int wait)
        list_del(&fl->fl_link);
        INIT_LIST_HEAD(&fl->fl_link);
 
+       fasync_helper(0, fl->fl_file, 0, &fl->fl_fasync);
+       if (fl->fl_fasync != NULL){
+               printk("locks_delete_lock: fasync == %p\n", fl->fl_fasync);
+               fl->fl_fasync = NULL;
+       }
+
        if (fl->fl_remove)
                fl->fl_remove(fl);
 
@@ -431,17 +504,14 @@ static void locks_delete_lock(struct file_lock **thisfl_p, unsigned int wait)
 }
 
 /* Determine if lock sys_fl blocks lock caller_fl. Common functionality
- * checks for overlapping locks and shared/exclusive status.
+ * checks for shared/exclusive status of overlapping locks.
  */
 static int locks_conflict(struct file_lock *caller_fl, struct file_lock *sys_fl)
 {
-       if (!locks_overlap(caller_fl, sys_fl))
-               return (0);
-
        switch (caller_fl->fl_type) {
        case F_RDLCK:
                return (sys_fl->fl_type == F_WRLCK);
-               
+
        case F_WRLCK:
                return (1);
 
@@ -465,6 +535,10 @@ static int posix_locks_conflict(struct file_lock *caller_fl, struct file_lock *s
            locks_same_owner(caller_fl, sys_fl))
                return (0);
 
+       /* Check whether they overlap */
+       if (!locks_overlap(caller_fl, sys_fl))
+               return 0;
+
        return (locks_conflict(caller_fl, sys_fl));
 }
 
@@ -479,21 +553,66 @@ static int flock_locks_conflict(struct file_lock *caller_fl, struct file_lock *s
        if (!(sys_fl->fl_flags & FL_FLOCK) ||
            (caller_fl->fl_file == sys_fl->fl_file))
                return (0);
+#ifdef MSNFS
+       if ((caller_fl->fl_type & LOCK_MAND) || (sys_fl->fl_type & LOCK_MAND))
+               return 0;
+#endif
 
        return (locks_conflict(caller_fl, sys_fl));
 }
 
+int interruptible_sleep_on_locked(wait_queue_head_t *fl_wait, struct semaphore *sem, int timeout)
+{
+       int result = 0;
+       wait_queue_t wait;
+       init_waitqueue_entry(&wait, current);
+
+       __add_wait_queue(fl_wait, &wait);
+       current->state = TASK_INTERRUPTIBLE;
+       up(sem);
+       if (timeout == 0)
+               schedule();
+       else
+               result = schedule_timeout(timeout);
+       if (signal_pending(current))
+               result = -ERESTARTSYS;
+       down(sem);
+       remove_wait_queue(fl_wait, &wait);
+       current->state = TASK_RUNNING;
+       return result;
+}
+
+static int locks_block_on(struct file_lock *blocker, struct file_lock *waiter)
+{
+       int result;
+       locks_insert_block(blocker, waiter);
+       result = interruptible_sleep_on_locked(&waiter->fl_wait, &file_lock_sem, 0);
+       locks_delete_block(waiter);
+       return result;
+}
+
+static int locks_block_on_timeout(struct file_lock *blocker, struct file_lock *waiter, int time)
+{
+       int result;
+       locks_insert_block(blocker, waiter);
+       result = interruptible_sleep_on_locked(&waiter->fl_wait, &file_lock_sem, time);
+       locks_delete_block(waiter);
+       return result;
+}
+
 struct file_lock *
 posix_test_lock(struct file *filp, struct file_lock *fl)
 {
        struct file_lock *cfl;
 
+       acquire_fl_sem();
        for (cfl = filp->f_dentry->d_inode->i_flock; cfl; cfl = cfl->fl_next) {
                if (!(cfl->fl_flags & FL_POSIX))
                        continue;
                if (posix_locks_conflict(cfl, fl))
                        break;
        }
+       release_fl_sem();
 
        return (cfl);
 }
@@ -550,14 +669,14 @@ int locks_mandatory_locked(struct inode *inode)
        /*
         * Search the lock list for this inode for any POSIX locks.
         */
-       lock_kernel();
+       acquire_fl_sem();
        for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
                if (!(fl->fl_flags & FL_POSIX))
                        continue;
                if (fl->fl_owner != owner)
                        break;
        }
-       unlock_kernel();
+       release_fl_sem();
        return fl ? -EAGAIN : 0;
 }
 
@@ -566,7 +685,7 @@ int locks_mandatory_area(int read_write, struct inode *inode,
                         size_t count)
 {
        struct file_lock *fl;
-       struct file_lock *new_fl = locks_alloc_lock();
+       struct file_lock *new_fl = locks_alloc_lock(0);
        int error;
 
        new_fl->fl_owner = current->files;
@@ -578,36 +697,29 @@ int locks_mandatory_area(int read_write, struct inode *inode,
        new_fl->fl_end = offset + count - 1;
 
        error = 0;
-       lock_kernel();
+       acquire_fl_sem();
 
 repeat:
        /* Search the lock list for this inode for locks that conflict with
         * the proposed read/write.
         */
-       for (fl = inode->i_flock; ; fl = fl->fl_next) {
-               error = 0;
-               if (!fl)
-                       break;
+       for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
                if (!(fl->fl_flags & FL_POSIX))
                        continue;
-               /* Block for writes against a "read" lock,
-                * and both reads and writes against a "write" lock.
-                */
+               if (fl->fl_start > new_fl->fl_end)
+                       break;
                if (posix_locks_conflict(new_fl, fl)) {
                        error = -EAGAIN;
                        if (filp && (filp->f_flags & O_NONBLOCK))
                                break;
-                       error = -ERESTARTSYS;
-                       if (signal_pending(current))
-                               break;
                        error = -EDEADLK;
                        if (posix_locks_deadlock(new_fl, fl))
                                break;
-
-                       locks_insert_block(fl, new_fl);
-                       interruptible_sleep_on(&new_fl->fl_wait);
-                       locks_delete_block(new_fl);
-
+       
+                       error = locks_block_on(fl, new_fl);
+                       if (error != 0)
+                               break;
+       
                        /*
                         * If we've been sleeping someone might have
                         * changed the permissions behind our back.
@@ -617,14 +729,14 @@ repeat:
                        goto repeat;
                }
        }
-       unlock_kernel();
        locks_free_lock(new_fl);
+       release_fl_sem();
        return error;
 }
 
-/* Try to create a FLOCK lock on filp. We always insert new FLOCK locks at
- * the head of the list, but that's secret knowledge known only to the next
- * two functions.
+/* Try to create a FLOCK lock on filp. We always insert new FLOCK locks
+ * at the head of the list, but that's secret knowledge known only to
+ * flock_lock_file and posix_lock_file.
  */
 static int flock_lock_file(struct file *filp, unsigned int lock_type,
                           unsigned int wait)
@@ -643,7 +755,7 @@ static int flock_lock_file(struct file *filp, unsigned int lock_type,
                error = -ENOLCK;
                new_fl = flock_make_lock(filp, lock_type);
                if (!new_fl)
-                       goto out;
+                       return error;
        }
 
        error = 0;
@@ -659,7 +771,7 @@ search:
                }
                before = &fl->fl_next;
        }
-       /* change means that we are changing the type of an existing lock, or
+       /* change means that we are changing the type of an existing lock,
         * or else unlocking it.
         */
        if (change) {
@@ -675,10 +787,6 @@ search:
                goto out;
 
 repeat:
-       /* Check signals each time we start */
-       error = -ERESTARTSYS;
-       if (signal_pending(current))
-               goto out;
        for (fl = inode->i_flock; (fl != NULL) && (fl->fl_flags & FL_FLOCK);
             fl = fl->fl_next) {
                if (!flock_locks_conflict(new_fl, fl))
@@ -686,9 +794,9 @@ repeat:
                error = -EAGAIN;
                if (!wait)
                        goto out;
-               locks_insert_block(fl, new_fl);
-               interruptible_sleep_on(&new_fl->fl_wait);
-               locks_delete_block(new_fl);
+               error = locks_block_on(fl, new_fl);
+               if (error != 0)
+                       goto out;
                goto repeat;
        }
        locks_insert_lock(&inode->i_flock, new_fl);
@@ -701,7 +809,13 @@ out:
        return error;
 }
 
-/* Add a POSIX style lock to a file.
+/**
+ *     posix_lock_file:
+ *     @filp: The file to apply the lock to
+ *     @caller: The lock to be applied
+ *     @wait: 1 to retry automatically, 0 to return -EAGAIN
+ *
+ * Add a POSIX style lock to a file.
  * We merge adjacent locks whenever possible. POSIX locks are sorted by owner
  * task, then by starting address
  *
@@ -728,12 +842,13 @@ int posix_lock_file(struct file *filp, struct file_lock *caller,
         * We may need two file_lock structures for this operation,
         * so we get them in advance to avoid races.
         */
-       new_fl  = locks_alloc_lock();
-       new_fl2 = locks_alloc_lock();
+       new_fl = locks_alloc_lock(0);
+       new_fl2 = locks_alloc_lock(0);
        error = -ENOLCK; /* "no luck" */
        if (!(new_fl && new_fl2))
                goto out;
 
+       acquire_fl_sem();
        if (caller->fl_type != F_UNLCK) {
   repeat:
                for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
@@ -747,12 +862,10 @@ int posix_lock_file(struct file *filp, struct file_lock *caller,
                        error = -EDEADLK;
                        if (posix_locks_deadlock(caller, fl))
                                goto out;
-                       error = -ERESTARTSYS;
-                       if (signal_pending(current))
+
+                       error = locks_block_on(fl, caller);
+                       if (error != 0)
                                goto out;
-                       locks_insert_block(fl, caller);
-                       interruptible_sleep_on(&caller->fl_wait);
-                       locks_delete_block(caller);
                        goto repeat;
                }
        }
@@ -880,6 +993,7 @@ int posix_lock_file(struct file *filp, struct file_lock *caller,
                locks_wake_up_blocks(left, 0);
        }
 out:
+       release_fl_sem();
        /*
         * Free any unused locks.
         */
@@ -891,6 +1005,10 @@ out:
 }
 
 static inline int flock_translate_cmd(int cmd) {
+#ifdef MSNFS
+       if (cmd & LOCK_MAND)
+               return cmd & (LOCK_MAND | LOCK_RW);
+#endif
        switch (cmd &~ LOCK_NB) {
        case LOCK_SH:
                return F_RDLCK;
@@ -902,8 +1020,270 @@ static inline int flock_translate_cmd(int cmd) {
        return -EINVAL;
 }
 
-/* flock() system call entry point. Apply a FL_FLOCK style lock to
- * an open file descriptor.
+/**
+ *     __get_lease     -       revoke all outstanding leases on file
+ *     @inode: the inode of the file to return
+ *     @mode: the open mode (read or write)
+ *
+ *     get_lease (inlined for speed) has checked there already
+ *     is a lease on this file.  Leases are broken on a call to open()
+ *     or truncate().  This function can sleep unless you
+ *     specified %O_NONBLOCK to your open().
+ */
+int __get_lease(struct inode *inode, unsigned int mode)
+{
+       int error = 0, future;
+       struct file_lock *new_fl, *flock;
+       struct file_lock *fl;
+       int alloc_err;
+
+       alloc_err = lease_alloc(NULL, 0, &new_fl);
+
+       acquire_fl_sem();
+       flock = inode->i_flock;
+       if (flock->fl_type & F_INPROGRESS) {
+               if ((mode & O_NONBLOCK)
+                   || (flock->fl_owner == current->files)) {
+                       error = -EWOULDBLOCK;
+                       goto out;
+               }
+               if (alloc_err != 0) {
+                       error = alloc_err;
+                       goto out;
+               }
+               do {
+                       error = locks_block_on(flock, new_fl);
+                       if (error != 0)
+                               goto out;
+                       flock = inode->i_flock;
+                       if (!(flock && (flock->fl_flags & FL_LEASE)))
+                               goto out;
+               } while (flock->fl_type & F_INPROGRESS);
+       }
+
+       if (mode & FMODE_WRITE) {
+               /* If we want write access, we have to revoke any lease. */
+               future = F_UNLCK | F_INPROGRESS;
+       } else if (flock->fl_type & F_WRLCK) {
+               /* Downgrade the exclusive lease to a read-only lease. */
+               future = F_RDLCK | F_INPROGRESS;
+       } else {
+               /* the existing lease was read-only, so we can read too. */
+               goto out;
+       }
+
+       if (alloc_err && (flock->fl_owner != current->files)) {
+               error = alloc_err;
+               goto out;
+       }
+
+       fl = flock;
+       do {
+               fl->fl_type = future;
+               fl = fl->fl_next;
+       } while (fl != NULL && (fl->fl_flags & FL_LEASE));
+
+       kill_fasync(&flock->fl_fasync, SIGIO, POLL_MSG);
+
+       if ((mode & O_NONBLOCK) || (flock->fl_owner == current->files)) {
+               error = -EWOULDBLOCK;
+               goto out;
+       }
+
+       if (lease_break_time > 0)
+               error = lease_break_time * HZ;
+       else
+               error = 0;
+restart:
+       error = locks_block_on_timeout(flock, new_fl, error);
+       if (error == 0) {
+               /* We timed out.  Unilaterally break the lease. */
+               locks_delete_lock(&inode->i_flock, 0);
+               printk(KERN_WARNING "lease timed out\n");
+       } else if (error > 0) {
+               flock = inode->i_flock;
+               if (flock && (flock->fl_flags & FL_LEASE))
+                       goto restart;
+               error = 0;
+       }
+
+out:
+       release_fl_sem();
+       if (!alloc_err)
+               locks_free_lock(new_fl);
+       return error;
+}
+
+/**
+ *     lease_get_mtime
+ *     @inode: the inode
+ *
+ * This is to force NFS clients to flush their caches for files with
+ * exclusive leases.  The justification is that if someone has an
+ * exclusive lease, then they could be modifiying it.
+ */
+time_t lease_get_mtime(struct inode *inode)
+{
+       struct file_lock *flock = inode->i_flock;
+       if (flock && (flock->fl_flags & FL_LEASE) && (flock->fl_type & F_WRLCK))
+               return CURRENT_TIME;
+       return inode->i_mtime;
+}
+
+/**
+ *     fcntl_getlease - Enquire what lease is currently active
+ *     @filp: the file
+ *
+ *     The value returned by this function will be one of
+ *
+ *     %F_RDLCK to indicate a read-only (type II) lease is held.
+ *
+ *     %F_WRLCK to indicate an exclusive lease is held.
+ *
+ *     XXX: sfr & i disagree over whether F_INPROGRESS
+ *     should be returned to userspace.
+ */
+int fcntl_getlease(struct file *filp)
+{
+       struct file_lock *fl;
+       
+       fl = filp->f_dentry->d_inode->i_flock;
+       if ((fl == NULL) || ((fl->fl_flags & FL_LEASE) == 0))
+               return F_UNLCK;
+       return fl->fl_type & ~F_INPROGRESS;
+}
+
+/* We already had a lease on this file; just change its type */
+static int lease_modify(struct file_lock **before, int arg, int fd, struct file *filp)
+{
+       struct file_lock *fl = *before;
+       int error = assign_type(fl, arg);
+       if (error < 0)
+               goto out;
+
+       locks_wake_up_blocks(fl, 0);
+
+       if (arg == F_UNLCK) {
+               filp->f_owner.pid = 0;
+               filp->f_owner.uid = 0;
+               filp->f_owner.euid = 0;
+               filp->f_owner.signum = 0;
+               locks_delete_lock(before, 0);
+               fasync_helper(fd, filp, 0, &fl->fl_fasync);
+       }
+
+out:
+       return error;
+}
+
+/**
+ *     fcntl_setlease  -       sets a lease on an open file
+ *     @fd: open file descriptor
+ *     @filp: file pointer
+ *     @arg: type of lease to obtain
+ *
+ *     Call this fcntl to establish a lease on the file.
+ *     Note that you also need to call %F_SETSIG to
+ *     receive a signal when the lease is broken.
+ */
+int fcntl_setlease(unsigned int fd, struct file *filp, long arg)
+{
+       struct file_lock *fl, **before, **my_before = NULL;
+       struct dentry *dentry;
+       struct inode *inode;
+       int error, rdlease_count = 0, wrlease_count = 0;
+
+       dentry = filp->f_dentry;
+       inode = dentry->d_inode;
+
+       if ((current->fsuid != inode->i_uid) && !capable(CAP_LEASE))
+               return -EACCES;
+       if (!S_ISREG(inode->i_mode))
+               return -EINVAL;
+
+       /*
+        * FIXME: What about F_RDLCK and files open for writing?
+        */
+       if ((arg == F_WRLCK)
+           && ((atomic_read(&dentry->d_count) > 1)
+               || (atomic_read(&inode->i_count) > 1)))
+               return -EAGAIN;
+
+       before = &inode->i_flock;
+
+       acquire_fl_sem();
+
+       while ((fl = *before) != NULL) {
+               if (fl->fl_flags != FL_LEASE)
+                       break;
+               if (fl->fl_file == filp)
+                       my_before = before;
+               else if (fl->fl_type & F_WRLCK)
+                       wrlease_count++;
+               else
+                       rdlease_count++;
+               before = &fl->fl_next;
+       }
+
+       if ((arg == F_RDLCK && (wrlease_count > 0)) ||
+           (arg == F_WRLCK && ((rdlease_count + wrlease_count) > 0))) {
+               error = -EAGAIN;
+               goto out_unlock;
+       }
+
+       if (my_before != NULL) {
+               error = lease_modify(my_before, arg, fd, filp);
+               goto out_unlock;
+       }
+
+       if (arg == F_UNLCK) {
+               error = 0;
+               goto out_unlock;
+       }
+
+       if (!leases_enable) {
+               error = -EINVAL;
+               goto out_unlock;
+       }
+
+       error = lease_alloc(filp, arg, &fl);
+       if (error)
+               goto out_unlock;
+
+       error = fasync_helper(fd, filp, 1, &fl->fl_fasync);
+       if (error < 0) {
+               locks_free_lock(fl);
+               goto out_unlock;
+       }
+       fl->fl_next = *before;
+       *before = fl;
+       list_add(&fl->fl_link, &file_lock_list);
+       filp->f_owner.pid = current->pid;
+       filp->f_owner.uid = current->uid;
+       filp->f_owner.euid = current->euid;
+out_unlock:
+       release_fl_sem();
+       return error;
+}
+
+/**
+ *     sys_flock: - flock() system call.
+ *     @fd: the file descriptor to lock.
+ *     @cmd: the type of lock to apply.
+ *
+ *     Apply a %FL_FLOCK style lock to an open file descriptor.
+ *     The @cmd can be one of
+ *
+ *     %LOCK_SH -- a shared lock.
+ *
+ *     %LOCK_EX -- an exclusive lock.
+ *
+ *     %LOCK_UN -- remove an existing lock.
+ *
+ *     %LOCK_MAND -- a `mandatory' flock.  This exists to emulate Windows Share Modes.
+ *
+ *     %LOCK_MAND can be combined with %LOCK_READ or %LOCK_WRITE to allow other
+ *     processes read and write access respectively.
  */
 asmlinkage long sys_flock(unsigned int fd, unsigned int cmd)
 {
@@ -921,13 +1301,17 @@ asmlinkage long sys_flock(unsigned int fd, unsigned int cmd)
        type = error;
 
        error = -EBADF;
-       if ((type != F_UNLCK) && !(filp->f_mode & 3))
+       if ((type != F_UNLCK)
+#ifdef MSNFS
+               && !(type & LOCK_MAND)
+#endif
+               && !(filp->f_mode & 3))
                goto out_putf;
 
-       lock_kernel();
+       acquire_fl_sem();
        error = flock_lock_file(filp, type,
                                (cmd & (LOCK_UN | LOCK_NB)) ? 0 : 1);
-       unlock_kernel();
+       release_fl_sem();
 
 out_putf:
        fput(filp);
@@ -941,7 +1325,7 @@ out:
 int fcntl_getlk(unsigned int fd, struct flock *l)
 {
        struct file *filp;
-       struct file_lock *fl, *file_lock = locks_alloc_lock();
+       struct file_lock *fl, file_lock;
        struct flock flock;
        int error;
 
@@ -958,20 +1342,20 @@ int fcntl_getlk(unsigned int fd, struct flock *l)
                goto out;
 
        error = -EINVAL;
-       if (!flock_to_posix_lock(filp, file_lock, &flock))
+       if (!flock_to_posix_lock(filp, &file_lock, &flock))
                goto out_putf;
 
        if (filp->f_op->lock) {
-               error = filp->f_op->lock(filp, F_GETLK, file_lock);
+               error = filp->f_op->lock(filp, F_GETLK, &file_lock);
                if (error < 0)
                        goto out_putf;
                else if (error == LOCK_USE_CLNT)
                  /* Bypass for NFS with no locking - 2.0.36 compat */
-                 fl = posix_test_lock(filp, file_lock);
+                 fl = posix_test_lock(filp, &file_lock);
                else
-                 fl = (file_lock->fl_type == F_UNLCK ? NULL : file_lock);
+                 fl = (file_lock.fl_type == F_UNLCK ? NULL : &file_lock);
        } else {
-               fl = posix_test_lock(filp, file_lock);
+               fl = posix_test_lock(filp, &file_lock);
        }
  
        flock.l_type = F_UNLCK;
@@ -1002,7 +1386,6 @@ int fcntl_getlk(unsigned int fd, struct flock *l)
 out_putf:
        fput(filp);
 out:
-       locks_free_lock(file_lock);
        return error;
 }
 
@@ -1012,7 +1395,7 @@ out:
 int fcntl_setlk(unsigned int fd, unsigned int cmd, struct flock *l)
 {
        struct file *filp;
-       struct file_lock *file_lock = locks_alloc_lock();
+       struct file_lock *file_lock = locks_alloc_lock(0);
        struct flock flock;
        struct inode *inode;
        int error;
@@ -1107,7 +1490,7 @@ out:
 int fcntl_getlk64(unsigned int fd, struct flock64 *l)
 {
        struct file *filp;
-       struct file_lock *fl, *file_lock = locks_alloc_lock();
+       struct file_lock *fl, file_lock;
        struct flock64 flock;
        int error;
 
@@ -1124,20 +1507,20 @@ int fcntl_getlk64(unsigned int fd, struct flock64 *l)
                goto out;
 
        error = -EINVAL;
-       if (!flock64_to_posix_lock(filp, file_lock, &flock))
+       if (!flock64_to_posix_lock(filp, &file_lock, &flock))
                goto out_putf;
 
        if (filp->f_op->lock) {
-               error = filp->f_op->lock(filp, F_GETLK, file_lock);
+               error = filp->f_op->lock(filp, F_GETLK, &file_lock);
                if (error < 0)
                        goto out_putf;
                else if (error == LOCK_USE_CLNT)
                  /* Bypass for NFS with no locking - 2.0.36 compat */
-                 fl = posix_test_lock(filp, file_lock);
+                 fl = posix_test_lock(filp, &file_lock);
                else
-                 fl = (file_lock->fl_type == F_UNLCK ? NULL : file_lock);
+                 fl = (file_lock.fl_type == F_UNLCK ? NULL : &file_lock);
        } else {
-               fl = posix_test_lock(filp, file_lock);
+               fl = posix_test_lock(filp, &file_lock);
        }
  
        flock.l_type = F_UNLCK;
@@ -1156,7 +1539,6 @@ int fcntl_getlk64(unsigned int fd, struct flock64 *l)
 out_putf:
        fput(filp);
 out:
-       locks_free_lock(file_lock);
        return error;
 }
 
@@ -1166,7 +1548,7 @@ out:
 int fcntl_setlk64(unsigned int fd, unsigned int cmd, struct flock64 *l)
 {
        struct file *filp;
-       struct file_lock *file_lock = locks_alloc_lock();
+       struct file_lock *file_lock = locks_alloc_lock(0);
        struct flock64 flock;
        struct inode *inode;
        int error;
@@ -1262,17 +1644,16 @@ void locks_remove_posix(struct file *filp, fl_owner_t owner)
                 */
                return;
        }
-       lock_kernel();
-repeat:
+       acquire_fl_sem();
        before = &inode->i_flock;
        while ((fl = *before) != NULL) {
                if ((fl->fl_flags & FL_POSIX) && fl->fl_owner == owner) {
                        locks_delete_lock(before, 0);
-                       goto repeat;
+                       continue;
                }
                before = &fl->fl_next;
        }
-       unlock_kernel();
+       release_fl_sem();
 }
 
 /*
@@ -1281,76 +1662,104 @@ repeat:
 void locks_remove_flock(struct file *filp)
 {
        struct inode * inode = filp->f_dentry->d_inode; 
-       struct file_lock file_lock, *fl;
+       struct file_lock *fl;
        struct file_lock **before;
+
        if (!inode->i_flock)
                return;
 
-       lock_kernel();
-repeat:
+       acquire_fl_sem();
        before = &inode->i_flock;
+
        while ((fl = *before) != NULL) {
-               if ((fl->fl_flags & FL_FLOCK) && fl->fl_file == filp) {
-                       int (*lock)(struct file *, int, struct file_lock *);
-                       lock = NULL;
-                       if (filp->f_op)
-                               lock = filp->f_op->lock;
-                       if (lock) {
-                               file_lock = *fl;
-                               file_lock.fl_type = F_UNLCK;
-                       }
+               if ((fl->fl_flags & (FL_FLOCK|FL_LEASE))
+                   && (fl->fl_file == filp)) {
                        locks_delete_lock(before, 0);
-                       if (lock) {
-                               lock(filp, F_SETLK, &file_lock);
-                               /* List may have changed: */
-                               goto repeat;
-                       }
                        continue;
-               }
+               }
                before = &fl->fl_next;
        }
-       unlock_kernel();
+       release_fl_sem();
 }
 
-/* The following two are for the benefit of lockd.
+/**
+ *     posix_block_lock - blocks waiting for a file lock
+ *     @blocker: the lock which is blocking
+ *     @waiter: the lock which conflicts and has to wait
+ *
+ * lockd needs to block waiting for locks.
  */
 void
 posix_block_lock(struct file_lock *blocker, struct file_lock *waiter)
 {
-       lock_kernel();
+       acquire_fl_sem();
        locks_insert_block(blocker, waiter);
-       unlock_kernel();
+       release_fl_sem();
 }
 
+/**
+ *     posix_unblock_lock - stop waiting for a file lock
+ *     @waiter: the lock which was waiting
+ *
+ *     lockd needs to block waiting for locks.
+ */
 void
 posix_unblock_lock(struct file_lock *waiter)
 {
-       locks_delete_block(waiter);
-       return;
+       acquire_fl_sem();
+       if (!list_empty(&waiter->fl_list)) {
+               locks_delete_block(waiter);
+               wake_up(&waiter->fl_wait);
+       }
+       release_fl_sem();
 }
 
 static void lock_get_status(char* out, struct file_lock *fl, int id, char *pfx)
 {
-       struct inode *inode;
+       struct inode *inode = NULL;
 
-       inode = fl->fl_file->f_dentry->d_inode;
+       if (fl->fl_file != NULL)
+               inode = fl->fl_file->f_dentry->d_inode;
 
        out += sprintf(out, "%d:%s ", id, pfx);
        if (fl->fl_flags & FL_POSIX) {
                out += sprintf(out, "%6s %s ",
                             (fl->fl_flags & FL_ACCESS) ? "ACCESS" : "POSIX ",
+                            (inode == NULL) ? "*NOINODE*" :
                             (IS_MANDLOCK(inode) &&
                              (inode->i_mode & (S_IXGRP | S_ISGID)) == S_ISGID) ?
                             "MANDATORY" : "ADVISORY ");
+       } else if (fl->fl_flags & FL_FLOCK) {
+#ifdef MSNFS
+               if (fl->fl_type & LOCK_MAND) {
+                       out += sprintf(out, "FLOCK  MSNFS     ");
+               } else
+#endif
+                       out += sprintf(out, "FLOCK  ADVISORY  ");
+       } else if (fl->fl_flags & FL_LEASE) {
+               out += sprintf(out, "LEASE  MANDATORY ");
+       } else {
+               out += sprintf(out, "UNKNOWN UNKNOWN  ");
        }
-       else {
-               out += sprintf(out, "FLOCK  ADVISORY  ");
-       }
-       out += sprintf(out, "%s ", (fl->fl_type == F_RDLCK) ? "READ " : "WRITE");
-       out += sprintf(out, "%d %s:%ld %Ld %Ld ",
+#ifdef MSNFS
+       if (fl->fl_type & LOCK_MAND) {
+               out += sprintf(out, "%s ",
+                              (fl->fl_type & LOCK_READ)
+                              ? (fl->fl_type & LOCK_WRITE) ? "RW   " : "READ "
+                              : (fl->fl_type & LOCK_WRITE) ? "WRITE" : "NONE ");
+       } else
+#endif
+               out += sprintf(out, "%s ",
+                              (fl->fl_type & F_WRLCK) ? "WRITE" : "READ ");
+       out += sprintf(out, "%d %s:%ld ",
                     fl->fl_pid,
-                    kdevname(inode->i_dev), inode->i_ino,
-                    (long long)fl->fl_start, (long long)fl->fl_end);
+                    inode ? kdevname(inode->i_dev) : "<none>",
+                    inode ? inode->i_ino : 0);
+       out += sprintf(out, "%Ld ", fl->fl_start);
+       if (fl->fl_end == OFFSET_MAX)
+               out += sprintf(out, "EOF ");
+       else
+               out += sprintf(out, "%Ld ", fl->fl_end);
        sprintf(out, "%08lx %08lx %08lx %08lx %08lx\n",
                (long)fl, (long)fl->fl_link.prev, (long)fl->fl_link.next,
                (long)fl->fl_next, (long)fl->fl_block.next);
@@ -1378,6 +1787,14 @@ static void move_lock_status(char **p, off_t* pos, off_t offset)
        *pos += len;
 }
 
+/**
+ *     get_locks_status        -       reports lock usage in /proc/locks
+ *     @buffer: address in userspace to write into
+ *     @start: ?
+ *     @offset: how far we are through the buffer
+ *     @length: how much to read
+ */
+
 int get_locks_status(char *buffer, char **start, off_t offset, int length)
 {
        struct list_head *tmp;
@@ -1385,7 +1802,7 @@ int get_locks_status(char *buffer, char **start, off_t offset, int length)
        off_t pos = 0;
        int i = 0;
 
-       lock_kernel();
+       acquire_fl_sem();
        list_for_each(tmp, &file_lock_list) {
                struct list_head *btmp;
                struct file_lock *fl = list_entry(tmp, struct file_lock, fl_link);
@@ -1397,7 +1814,7 @@ int get_locks_status(char *buffer, char **start, off_t offset, int length)
 
                list_for_each(btmp, &fl->fl_block) {
                        struct file_lock *bfl = list_entry(btmp,
-                                       struct file_lock, fl_block);
+                                       struct file_lock, fl_list);
                        lock_get_status(q, bfl, i, " ->");
                        move_lock_status(&q, &pos, offset);
 
@@ -1406,13 +1823,89 @@ int get_locks_status(char *buffer, char **start, off_t offset, int length)
                }
        }
 done:
-       unlock_kernel();
+       release_fl_sem();
        *start = buffer;
        if(q-buffer < length)
                return (q-buffer);
        return length;
 }
 
+#ifdef MSNFS
+/**
+ *     lock_may_read - checks that the region is free of locks
+ *     @inode: the inode that is being read
+ *     @start: the first byte to read
+ *     @len: the number of bytes to read
+ *
+ *     Emulates Windows locking requirements.  Whole-file
+ *     mandatory locks (share modes) can prohibit a read and
+ *     byte-range POSIX locks can prohibit a read if they overlap.
+ *
+ *     N.B. this function is only ever called
+ *     from knfsd and ownership of locks is never checked.
+ */
+int lock_may_read(struct inode *inode, loff_t start, unsigned long len)
+{
+       struct file_lock *fl;
+       int result = 1;
+       acquire_fl_sem();
+       for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
+               if (fl->fl_flags == FL_POSIX) {
+                       if (fl->fl_type == F_RDLCK)
+                               continue;
+                       if ((fl->fl_end < start) || (fl->fl_start > (start + len)))
+                               continue;
+               } else if (fl->fl_flags == FL_FLOCK) {
+                       if (!(fl->fl_type & LOCK_MAND))
+                               continue;
+                       if (fl->fl_type & LOCK_READ)
+                               continue;
+               } else
+                       continue;
+               result = 0;
+               break;
+       }
+       release_fl_sem();
+       return result;
+}
+
+/**
+ *     lock_may_write - checks that the region is free of locks
+ *     @inode: the inode that is being written
+ *     @start: the first byte to write
+ *     @len: the number of bytes to write
+ *
+ *     Emulates Windows locking requirements.  Whole-file
+ *     mandatory locks (share modes) can prohibit a write and
+ *     byte-range POSIX locks can prohibit a write if they overlap.
+ *
+ *     N.B. this function is only ever called
+ *     from knfsd and ownership of locks is never checked.
+ */
+int lock_may_write(struct inode *inode, loff_t start, unsigned long len)
+{
+       struct file_lock *fl;
+       int result = 1;
+       acquire_fl_sem();
+       for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
+               if (fl->fl_flags == FL_POSIX) {
+                       if ((fl->fl_end < start) || (fl->fl_start > (start + len)))
+                               continue;
+               } else if (fl->fl_flags == FL_FLOCK) {
+                       if (!(fl->fl_type & LOCK_MAND))
+                               continue;
+                       if (fl->fl_type & LOCK_WRITE)
+                               continue;
+               } else
+                       continue;
+               result = 0;
+               break;
+       }
+       release_fl_sem();
+       return result;
+}
+#endif
+
 static int __init filelock_init(void)
 {
        filelock_cache = kmem_cache_create("file lock cache",
index 462df3b29c0abb0c51f61a3604c8881947e196ed..c22fc2ec31ca7948a254c26475bdda0226f779c3 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/quotaops.h>
 #include <linux/pagemap.h>
 #include <linux/dcache.h>
+#include <linux/dnotify.h>
 
 #include <asm/uaccess.h>
 #include <asm/unaligned.h>
@@ -912,6 +913,8 @@ int vfs_create(struct inode *dir, struct dentry *dentry, int mode)
        unlock_kernel();
 exit_lock:
        up(&dir->i_zombie);
+       if (!error)
+               inode_dir_notify(dir, DN_CREATE);
        return error;
 }
 
@@ -1066,6 +1069,13 @@ ok:
                        goto exit;
        }
 
+       /*
+        * Ensure there are no outstanding leases on the file.
+        */
+       error = get_lease(inode, flag);
+       if (error)
+               goto exit;
+
        if (flag & O_TRUNC) {
                error = get_write_access(inode);
                if (error)
@@ -1183,6 +1193,8 @@ int vfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
        unlock_kernel();
 exit_lock:
        up(&dir->i_zombie);
+       if (!error)
+               inode_dir_notify(dir, DN_CREATE);
        return error;
 }
 
@@ -1250,6 +1262,8 @@ int vfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
 
 exit_lock:
        up(&dir->i_zombie);
+       if (!error)
+               inode_dir_notify(dir, DN_CREATE);
        return error;
 }
 
@@ -1338,8 +1352,10 @@ int vfs_rmdir(struct inode *dir, struct dentry *dentry)
                        dentry->d_inode->i_flags |= S_DEAD;
        }
        double_up(&dir->i_zombie, &dentry->d_inode->i_zombie);
-       if (!error)
+       if (!error) {
+               inode_dir_notify(dir, DN_DELETE);
                d_delete(dentry);
+       }
        dput(dentry);
 
        return error;
@@ -1406,6 +1422,8 @@ int vfs_unlink(struct inode *dir, struct dentry *dentry)
                }
        }
        up(&dir->i_zombie);
+       if (!error)
+               inode_dir_notify(dir, DN_DELETE);
        return error;
 }
 
@@ -1472,6 +1490,8 @@ int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname)
 
 exit_lock:
        up(&dir->i_zombie);
+       if (!error)
+               inode_dir_notify(dir, DN_CREATE);
        return error;
 }
 
@@ -1544,6 +1564,8 @@ int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_de
 
 exit_lock:
        up(&dir->i_zombie);
+       if (!error)
+               inode_dir_notify(dir, DN_CREATE);
        return error;
 }
 
@@ -1749,10 +1771,20 @@ int vfs_rename_other(struct inode *old_dir, struct dentry *old_dentry,
 int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
               struct inode *new_dir, struct dentry *new_dentry)
 {
+       int error;
        if (S_ISDIR(old_dentry->d_inode->i_mode))
-               return vfs_rename_dir(old_dir,old_dentry,new_dir,new_dentry);
+               error = vfs_rename_dir(old_dir,old_dentry,new_dir,new_dentry);
        else
-               return vfs_rename_other(old_dir,old_dentry,new_dir,new_dentry);
+               error = vfs_rename_other(old_dir,old_dentry,new_dir,new_dentry);
+       if (!error) {
+               if (old_dir == new_dir)
+                       inode_dir_notify(old_dir, DN_RENAME);
+               else {
+                       inode_dir_notify(old_dir, DN_DELETE);
+                       inode_dir_notify(new_dir, DN_CREATE);
+               }
+       }
+       return error;
 }
 
 static inline int do_rename(const char * oldname, const char * newname)
index df25320488140aac4f4087ec6644e17180e4529c..5ee2f8bc079e57081a3d355d501110a3c11004c9 100644 (file)
@@ -1,3 +1,4 @@
+#define MSNFS  /* HACK HACK */
 /*
  * linux/fs/nfsd/export.c
  *
@@ -556,6 +557,9 @@ struct flags {
        { NFSEXP_CROSSMNT, {"nohide", ""}},
        { NFSEXP_NOSUBTREECHECK, {"no_subtree_check", ""}},
        { NFSEXP_NOAUTHNLM, {"insecure_locks", ""}},
+#ifdef NSMFS
+       { NFSEXP_MSNFS, {"msnfs", ""}},
+#endif
        { 0, {"", ""}}
 };
 
index 308db4cd528d5b07ab02c843fd541b46b485bf17..d97e1717dec387d42ebfe45eda0654a9eb27b09b 100644 (file)
@@ -193,7 +193,7 @@ encode_fattr3(struct svc_rqst *rqstp, u32 *p, struct dentry *dentry)
        p = xdr_encode_hyper(p, (u64) inode->i_dev);
        p = xdr_encode_hyper(p, (u64) inode->i_ino);
        p = encode_time3(p, inode->i_atime);
-       p = encode_time3(p, inode->i_mtime);
+       p = encode_time3(p, lease_get_mtime(inode));
        p = encode_time3(p, inode->i_ctime);
 
        return p;
index 9127e08696d7b5d19b6b9947888239e28602cc99..fcbddc5ed776592dcea35b957313237beaa1daab 100644 (file)
@@ -155,7 +155,7 @@ encode_fattr(struct svc_rqst *rqstp, u32 *p, struct inode *inode)
        *p++ = htonl((u32) inode->i_ino);
        *p++ = htonl((u32) inode->i_atime);
        *p++ = 0;
-       *p++ = htonl((u32) inode->i_mtime);
+       *p++ = htonl((u32) lease_get_mtime(inode));
        *p++ = 0;
        *p++ = htonl((u32) inode->i_ctime);
        *p++ = 0;
index bcd5281795f768f52159fa74eeece9c6d8d8afd0..4e5716b4f51995779c3e6a3c14575b6291987edc 100644 (file)
@@ -1,3 +1,4 @@
+#define MSNFS  /* HACK HACK */
 /*
  * linux/fs/nfsd/vfs.c
  *
@@ -249,6 +250,15 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap)
                        if (err)
                                goto out;
                }
+
+               /*
+                * If we are changing the size of the file, then
+                * we need to break all leases.
+                */
+               err = get_lease(inode, FMODE_WRITE);
+               if (err)
+                       goto out_nfserr;
+
                err = get_write_access(inode);
                if (err)
                        goto out_nfserr;
@@ -443,6 +453,14 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
        if (!inode->i_fop)
                goto out;
 
+       /*
+        * Check to see if there are any leases on this file.
+        * This may block while leases are broken.
+        */
+       err = get_lease(inode, (access & MAY_WRITE) ? FMODE_WRITE : 0);
+       if (err)
+               goto out_nfserr;
+
        if ((access & MAY_WRITE) && (err = get_write_access(inode)) != 0)
                goto out_nfserr;
 
@@ -577,6 +595,11 @@ nfsd_read(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset,
        err = nfserr_perm;
        if (!file.f_op->read)
                goto out_close;
+#ifdef MSNFS
+       if ((fhp->fh_export->ex_flags & NFSEXP_MSNFS) &&
+               (!lock_may_read(file.f_dentry->d_inode, offset, *count)))
+               goto out_close;
+#endif
 
        /* Get readahead parameters */
        ra = nfsd_get_raparms(fhp->fh_export->ex_dev, fhp->fh_dentry->d_inode->i_ino);
@@ -643,6 +666,11 @@ nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset,
        err = nfserr_perm;
        if (!file.f_op->write)
                goto out_close;
+#ifdef MSNFS
+       if ((fhp->fh_export->ex_flags & NFSEXP_MSNFS) &&
+               (!lock_may_write(file.f_dentry->d_inode, offset, cnt)))
+               goto out_close;
+#endif
 
        dentry = file.f_dentry;
        inode = dentry->d_inode;
@@ -1250,6 +1278,13 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen,
                goto out_dput_old;
 
 
+#ifdef MSNFS
+       if ((ffhp->fh_export->ex_flags & NFSEXP_MSNFS) &&
+               ((atomic_read(&odentry->d_count) > 1)
+                || (atomic_read(&ndentry->d_count) > 1))) {
+                       err = nfserr_perm;
+       } else
+#endif
        err = vfs_rename(fdir, odentry, tdir, ndentry);
        if (!err && EX_ISSYNC(tfhp->fh_export)) {
                nfsd_sync_dir(tdentry);
@@ -1311,6 +1346,12 @@ nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
        }
 
        if (type != S_IFDIR) { /* It's UNLINK */
+#ifdef MSNFS
+               if ((fhp->fh_export->ex_flags & NFSEXP_MSNFS) &&
+                       (atomic_read(&rdentry->d_count) > 1)) {
+                       err = nfserr_perm;
+               } else
+#endif
                err = vfs_unlink(dirp, rdentry);
        } else { /* It's RMDIR */
                err = vfs_rmdir(dirp, rdentry);
index 0f1787025c0f023933e74b1b7d55becb7b17ef05..dc3a2383171c6311beea4174a56f7947ebf7847f 100644 (file)
--- a/fs/open.c
+++ b/fs/open.c
@@ -10,6 +10,7 @@
 #include <linux/file.h>
 #include <linux/smp_lock.h>
 #include <linux/quotaops.h>
+#include <linux/dnotify.h>
 #include <linux/module.h>
 #include <linux/slab.h>
 
@@ -115,6 +116,13 @@ static inline long do_sys_truncate(const char * path, loff_t length)
        if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
                goto dput_and_out;
 
+       /*
+        * Make sure that there are no leases.
+        */
+       error = get_lease(inode, FMODE_WRITE);
+       if (error)
+               goto dput_and_out;
+
        error = get_write_access(inode);
        if (error)
                goto dput_and_out;
@@ -790,6 +798,7 @@ int filp_close(struct file *filp, fl_owner_t id)
                retval = filp->f_op->flush(filp);
                unlock_kernel();
        }
+       fcntl_dirnotify(0, filp, 0);
        locks_remove_posix(filp, id);
        fput(filp);
        return retval;
index 32ff58703b2791af061a3eaf7db2615fefcc822a..c542aad4f2a74cc4024708c73912e115e74c1252 100644 (file)
@@ -438,9 +438,6 @@ int __init partition_setup(void)
        else
 #endif
        rd_load();
-#endif
-#ifdef CONFIG_BLK_DEV_MD
-       md_run_setup();
 #endif
        return 0;
 }
index 3d3519146bc73e1ef621ae5a2f6f61f9a982797f..00b0daf7e4b208d316c0ca02d12f75a09d9b0347 100644 (file)
@@ -10,6 +10,7 @@
 #include <linux/file.h>
 #include <linux/uio.h>
 #include <linux/smp_lock.h>
+#include <linux/dnotify.h>
 
 #include <asm/uaccess.h>
 
@@ -132,6 +133,9 @@ asmlinkage ssize_t sys_read(unsigned int fd, char * buf, size_t count)
                                        ret = read(file, buf, count, &file->f_pos);
                        }
                }
+               if (ret > 0)
+                       inode_dir_notify(file->f_dentry->d_parent->d_inode,
+                               DN_ACCESS);
                fput(file);
        }
        return ret;
@@ -156,6 +160,9 @@ asmlinkage ssize_t sys_write(unsigned int fd, const char * buf, size_t count)
                                        ret = write(file, buf, count, &file->f_pos);
                        }
                }
+               if (ret > 0)
+                       inode_dir_notify(file->f_dentry->d_parent->d_inode,
+                               DN_MODIFY);
                fput(file);
        }
        return ret;
@@ -257,6 +264,10 @@ out:
        if (iov != iovstack)
                kfree(iov);
 out_nofree:
+       /* VERIFY_WRITE actually means a read, as we write to user space */
+       if ((ret + (type == VERIFY_WRITE)) > 0)
+               inode_dir_notify(file->f_dentry->d_parent->d_inode,
+                       (type == VERIFY_WRITE) ? DN_MODIFY : DN_ACCESS);
        return ret;
 }
 
@@ -327,6 +338,8 @@ asmlinkage ssize_t sys_pread(unsigned int fd, char * buf,
        if (pos < 0)
                goto out;
        ret = read(file, buf, count, &pos);
+       if (ret > 0)
+               inode_dir_notify(file->f_dentry->d_parent->d_inode, DN_ACCESS);
 out:
        fput(file);
 bad_file:
@@ -357,6 +370,8 @@ asmlinkage ssize_t sys_pwrite(unsigned int fd, const char * buf,
                goto out;
 
        ret = write(file, buf, count, &pos);
+       if (ret > 0)
+               inode_dir_notify(file->f_dentry->d_parent->d_inode, DN_MODIFY);
 out:
        fput(file);
 bad_file:
index cc8b6f278a594cc0d677c2037db7e1b8ca43c9aa..c4f1c9624de012e961b6e55582db63b25d771305 100644 (file)
  * than regular operations.
  */
 
-#ifdef CONFIG_SMP
+
+/*
+ * Counter is volatile to make sure gcc doesn't try to be clever
+ * and move things around on us. We need to use _exactly_ the address
+ * the user gave us, not some alias that contains the same information.
+ */
 typedef struct { volatile int counter; } atomic_t;
-#else
-typedef struct { int counter; } atomic_t;
-#endif
 
 #define ATOMIC_INIT(i) ( (atomic_t) { (i) } )
 
 #define atomic_read(v)         ((v)->counter)
 #define atomic_set(v,i)                ((v)->counter = (i))
 
-/*
- * Make sure gcc doesn't try to be clever and move things around
- * on us. We need to use _exactly_ the address the user gave us,
- * not some alias that contains the same information.
- */
-#define __atomic_fool_gcc(x) (*(struct { int a[100]; } *)x)
-
 /*
  * To get proper branch prediction for the main line, we must branch
  * forward to code at the end of this object's .text section, then
  * branch back to restart the operation.
  */
 
-extern __inline__ void atomic_add(int i, atomic_t * v)
+static __inline__ void atomic_add(int i, atomic_t * v)
 {
        unsigned long temp;
        __asm__ __volatile__(
@@ -46,11 +41,11 @@ extern __inline__ void atomic_add(int i, atomic_t * v)
        ".subsection 2\n"
        "2:     br 1b\n"
        ".previous"
-       :"=&r" (temp), "=m" (__atomic_fool_gcc(v))
-       :"Ir" (i), "m" (__atomic_fool_gcc(v)));
+       :"=&r" (temp), "=m" (v->counter)
+       :"Ir" (i), "m" (v->counter));
 }
 
-extern __inline__ void atomic_sub(int i, atomic_t * v)
+static __inline__ void atomic_sub(int i, atomic_t * v)
 {
        unsigned long temp;
        __asm__ __volatile__(
@@ -61,14 +56,14 @@ extern __inline__ void atomic_sub(int i, atomic_t * v)
        ".subsection 2\n"
        "2:     br 1b\n"
        ".previous"
-       :"=&r" (temp), "=m" (__atomic_fool_gcc(v))
-       :"Ir" (i), "m" (__atomic_fool_gcc(v)));
+       :"=&r" (temp), "=m" (v->counter)
+       :"Ir" (i), "m" (v->counter));
 }
 
 /*
  * Same as above, but return the result value
  */
-extern __inline__ long atomic_add_return(int i, atomic_t * v)
+static __inline__ long atomic_add_return(int i, atomic_t * v)
 {
        long temp, result;
        __asm__ __volatile__(
@@ -81,12 +76,12 @@ extern __inline__ long atomic_add_return(int i, atomic_t * v)
        ".subsection 2\n"
        "2:     br 1b\n"
        ".previous"
-       :"=&r" (temp), "=m" (__atomic_fool_gcc(v)), "=&r" (result)
-       :"Ir" (i), "m" (__atomic_fool_gcc(v)));
+       :"=&r" (temp), "=m" (v->counter), "=&r" (result)
+       :"Ir" (i), "m" (v->counter) : "memory");
        return result;
 }
 
-extern __inline__ long atomic_sub_return(int i, atomic_t * v)
+static __inline__ long atomic_sub_return(int i, atomic_t * v)
 {
        long temp, result;
        __asm__ __volatile__(
@@ -99,8 +94,8 @@ extern __inline__ long atomic_sub_return(int i, atomic_t * v)
        ".subsection 2\n"
        "2:     br 1b\n"
        ".previous"
-       :"=&r" (temp), "=m" (__atomic_fool_gcc(v)), "=&r" (result)
-       :"Ir" (i), "m" (__atomic_fool_gcc(v)));
+       :"=&r" (temp), "=m" (v->counter), "=&r" (result)
+       :"Ir" (i), "m" (v->counter) : "memory");
        return result;
 }
 
index c93491f1a366f591a9cd360cf7384310ac77f12c..89a566312a7347c6641960ed01b889b915638062 100644 (file)
@@ -1,6 +1,8 @@
 #ifndef _ALPHA_BITOPS_H
 #define _ALPHA_BITOPS_H
 
+#include <linux/config.h>
+
 /*
  * Copyright 1994, Linus Torvalds.
  */
  * bit 0 is the LSB of addr; bit 64 is the LSB of (addr+1).
  */
 
+#define BITOPS_NO_BRANCH
+
 extern __inline__ void set_bit(unsigned long nr, volatile void * addr)
 {
+#ifndef BITOPS_NO_BRANCH
        unsigned long oldbit;
+#endif
        unsigned long temp;
        unsigned int * m = ((unsigned int *) addr) + (nr >> 5);
 
+#ifndef BITOPS_NO_BRANCH
        __asm__ __volatile__(
-       "1:     ldl_l %0,%1\n"
+       "1:     ldl_l %0,%4\n"
        "       and %0,%3,%2\n"
        "       bne %2,2f\n"
        "       xor %0,%3,%0\n"
@@ -36,16 +43,57 @@ extern __inline__ void set_bit(unsigned long nr, volatile void * addr)
        ".previous"
        :"=&r" (temp), "=m" (*m), "=&r" (oldbit)
        :"Ir" (1UL << (nr & 31)), "m" (*m));
+#else
+       __asm__ __volatile__(
+       "1:     ldl_l %0,%3\n"
+       "       bis %0,%2,%0\n"
+       "       stl_c %0,%1\n"
+       "       beq %0,2f\n"
+       ".subsection 2\n"
+       "2:     br 1b\n"
+       ".previous"
+       :"=&r" (temp), "=m" (*m)
+       :"Ir" (1UL << (nr & 31)), "m" (*m));
+#endif
 }
 
+/*
+ * WARNING: non atomic version.
+ */
+extern __inline__ void __set_bit(unsigned long nr, volatile void * addr)
+{
+       unsigned int * m = ((unsigned int *) addr) + (nr >> 5);
+       /*
+        * Asm and C produces the same thing so let
+        * the compiler to do its good work.
+        */
+#if 0
+       int tmp;
+
+       __asm__ __volatile__(
+       "ldl %0,%3\n\t"
+       "bis %0,%2,%0\n\t"
+       "stl %0,%1"
+       : "=&r" (tmp), "=m" (*m)
+       : "Ir" (1UL << (nr & 31)), "m" (*m));
+#else
+       *m |= 1UL << (nr & 31);
+#endif
+}
+
+#define smp_mb__before_clear_bit()     smp_mb()
+#define smp_mb__after_clear_bit()      smp_mb()
 extern __inline__ void clear_bit(unsigned long nr, volatile void * addr)
 {
+#ifndef BITOPS_NO_BRANCH
        unsigned long oldbit;
+#endif
        unsigned long temp;
        unsigned int * m = ((unsigned int *) addr) + (nr >> 5);
 
+#ifndef BITOPS_NO_BRANCH
        __asm__ __volatile__(
-       "1:     ldl_l %0,%1\n"
+       "1:     ldl_l %0,%4\n"
        "       and %0,%3,%2\n"
        "       beq %2,2f\n"
        "       xor %0,%3,%0\n"
@@ -57,6 +105,18 @@ extern __inline__ void clear_bit(unsigned long nr, volatile void * addr)
        ".previous"
        :"=&r" (temp), "=m" (*m), "=&r" (oldbit)
        :"Ir" (1UL << (nr & 31)), "m" (*m));
+#else
+       __asm__ __volatile__(
+       "1:     ldl_l %0,%3\n"
+       "       and %0,%2,%0\n"
+       "       stl_c %0,%1\n"
+       "       beq %0,2f\n"
+       ".subsection 2\n"
+       "2:     br 1b\n"
+       ".previous"
+       :"=&r" (temp), "=m" (*m)
+       :"Ir" (~(1UL << (nr & 31))), "m" (*m));
+#endif
 }
 
 extern __inline__ void change_bit(unsigned long nr, volatile void * addr)
@@ -65,12 +125,12 @@ extern __inline__ void change_bit(unsigned long nr, volatile void * addr)
        unsigned int * m = ((unsigned int *) addr) + (nr >> 5);
 
        __asm__ __volatile__(
-       "1:     ldl_l %0,%1\n"
+       "1:     ldl_l %0,%3\n"
        "       xor %0,%2,%0\n"
        "       stl_c %0,%1\n"
-       "       beq %0,3f\n"
+       "       beq %0,2f\n"
        ".subsection 2\n"
-       "3:     br 1b\n"
+       "2:     br 1b\n"
        ".previous"
        :"=&r" (temp), "=m" (*m)
        :"Ir" (1UL << (nr & 31)), "m" (*m));
@@ -84,18 +144,43 @@ extern __inline__ int test_and_set_bit(unsigned long nr,
        unsigned int * m = ((unsigned int *) addr) + (nr >> 5);
 
        __asm__ __volatile__(
-       "1:     ldl_l %0,%1\n"
+       "1:     ldl_l %0,%4\n"
        "       and %0,%3,%2\n"
        "       bne %2,2f\n"
        "       xor %0,%3,%0\n"
        "       stl_c %0,%1\n"
        "       beq %0,3f\n"
+#ifdef CONFIG_SMP
        "       mb\n"
+#endif
        "2:\n"
        ".subsection 2\n"
        "3:     br 1b\n"
        ".previous"
        :"=&r" (temp), "=m" (*m), "=&r" (oldbit)
+       :"Ir" (1UL << (nr & 31)), "m" (*m) : "memory");
+
+       return oldbit != 0;
+}
+
+/*
+ * WARNING: non atomic version.
+ */
+extern __inline__ int __test_and_set_bit(unsigned long nr,
+                                        volatile void * addr)
+{
+       unsigned long oldbit;
+       unsigned long temp;
+       unsigned int * m = ((unsigned int *) addr) + (nr >> 5);
+
+       __asm__ __volatile__(
+       "       ldl %0,%4\n"
+       "       and %0,%3,%2\n"
+       "       bne %2,1f\n"
+       "       xor %0,%3,%0\n"
+       "       stl %0,%1\n"
+       "1:\n"
+       :"=&r" (temp), "=m" (*m), "=&r" (oldbit)
        :"Ir" (1UL << (nr & 31)), "m" (*m));
 
        return oldbit != 0;
@@ -109,18 +194,43 @@ extern __inline__ int test_and_clear_bit(unsigned long nr,
        unsigned int * m = ((unsigned int *) addr) + (nr >> 5);
 
        __asm__ __volatile__(
-       "1:     ldl_l %0,%1\n"
+       "1:     ldl_l %0,%4\n"
        "       and %0,%3,%2\n"
        "       beq %2,2f\n"
        "       xor %0,%3,%0\n"
        "       stl_c %0,%1\n"
        "       beq %0,3f\n"
+#ifdef CONFIG_SMP
        "       mb\n"
+#endif
        "2:\n"
        ".subsection 2\n"
        "3:     br 1b\n"
        ".previous"
        :"=&r" (temp), "=m" (*m), "=&r" (oldbit)
+       :"Ir" (1UL << (nr & 31)), "m" (*m) : "memory");
+
+       return oldbit != 0;
+}
+
+/*
+ * WARNING: non atomic version.
+ */
+extern __inline__ int __test_and_clear_bit(unsigned long nr,
+                                          volatile void * addr)
+{
+       unsigned long oldbit;
+       unsigned long temp;
+       unsigned int * m = ((unsigned int *) addr) + (nr >> 5);
+
+       __asm__ __volatile__(
+       "       ldl %0,%4\n"
+       "       and %0,%3,%2\n"
+       "       beq %2,1f\n"
+       "       xor %0,%3,%0\n"
+       "       stl %0,%1\n"
+       "1:\n"
+       :"=&r" (temp), "=m" (*m), "=&r" (oldbit)
        :"Ir" (1UL << (nr & 31)), "m" (*m));
 
        return oldbit != 0;
@@ -134,17 +244,19 @@ extern __inline__ int test_and_change_bit(unsigned long nr,
        unsigned int * m = ((unsigned int *) addr) + (nr >> 5);
 
        __asm__ __volatile__(
-       "1:     ldl_l %0,%1\n"
+       "1:     ldl_l %0,%4\n"
        "       and %0,%3,%2\n"
        "       xor %0,%3,%0\n"
        "       stl_c %0,%1\n"
        "       beq %0,3f\n"
+#ifdef CONFIG_SMP
        "       mb\n"
+#endif
        ".subsection 2\n"
        "3:     br 1b\n"
        ".previous"
        :"=&r" (temp), "=m" (*m), "=&r" (oldbit)
-       :"Ir" (1UL << (nr & 31)), "m" (*m));
+       :"Ir" (1UL << (nr & 31)), "m" (*m) : "memory");
 
        return oldbit != 0;
 }
@@ -279,16 +391,16 @@ found_middle:
 
 #ifdef __KERNEL__
 
-#define ext2_set_bit                 test_and_set_bit
-#define ext2_clear_bit               test_and_clear_bit
+#define ext2_set_bit                 __test_and_set_bit
+#define ext2_clear_bit               __test_and_clear_bit
 #define ext2_test_bit                test_bit
 #define ext2_find_first_zero_bit     find_first_zero_bit
 #define ext2_find_next_zero_bit      find_next_zero_bit
 
 /* Bitmap functions for the minix filesystem.  */
-#define minix_test_and_set_bit(nr,addr) test_and_set_bit(nr,addr)
-#define minix_set_bit(nr,addr) set_bit(nr,addr)
-#define minix_test_and_clear_bit(nr,addr) test_and_clear_bit(nr,addr)
+#define minix_test_and_set_bit(nr,addr) __test_and_set_bit(nr,addr)
+#define minix_set_bit(nr,addr) __set_bit(nr,addr)
+#define minix_test_and_clear_bit(nr,addr) __test_and_clear_bit(nr,addr)
 #define minix_test_bit(nr,addr) test_bit(nr,addr)
 #define minix_find_first_zero_bit(addr,size) find_first_zero_bit(addr,size)
 
index d031ef3c23c81cf94dc3c2d86df8ec6fc88b7311..7d5df3a44cbb2341330b0acca4e9396720cb5058 100644 (file)
@@ -127,7 +127,7 @@ typedef elf_fpreg_t elf_fpregset_t[ELF_NFPREG];
 
 #ifdef __KERNEL__
 #define SET_PERSONALITY(EX, IBCS2)                             \
-       set_personality((EX).e_flags & EF_ALPHA_32BIT           \
+       set_personality(((EX).e_flags & EF_ALPHA_32BIT)         \
           ? PER_LINUX_32BIT : (IBCS2) ? PER_SVR4 : PER_LINUX)
 #endif
 
index 2812510dba3fbc201975cad1dd0ae3c26de6cc6b..52d8fb5f4092e79f6db344ee7a542f6a0a6d6b46 100644 (file)
@@ -37,7 +37,7 @@ waking_non_zero(struct semaphore *sem)
                ".subsection 2\n"
                "3:     br      1b\n"
                ".previous"
-               : "=r"(ret), "=r"(tmp), "=m"(__atomic_fool_gcc(&sem->waking))
+               : "=r"(ret), "=r"(tmp), "=m"(sem->waking.counter)
                : "0"(0));
 
        return ret > 0;
index c14eb090947122a5ca90386ee636f4a6f0c84a7b..64e05d17b2bd5a37803123777e58ef2a6c1c4166 100644 (file)
@@ -5,8 +5,8 @@
 #include <linux/kernel.h>
 #include <asm/current.h>
 
-#define DEBUG_SPINLOCK 1
-#define DEBUG_RWLOCK 1
+#define DEBUG_SPINLOCK 0
+#define DEBUG_RWLOCK 0
 
 /*
  * Simple spin lock operations.  There are two variants, one clears IRQ's
@@ -38,9 +38,6 @@ typedef struct {
 #define spin_is_locked(x)      ((x)->lock != 0)
 #define spin_unlock_wait(x)    ({ do { barrier(); } while ((x)->lock); })
 
-typedef struct { unsigned long a[100]; } __dummy_lock_t;
-#define __dummy_lock(lock) (*(__dummy_lock_t *)(lock))
-
 #if DEBUG_SPINLOCK
 extern void spin_unlock(spinlock_t * lock);
 extern void debug_spin_lock(spinlock_t * lock, const char *, int);
@@ -83,8 +80,8 @@ static inline void spin_lock(spinlock_t * lock)
        "       blbs    %0,2b\n"
        "       br      1b\n"
        ".previous"
-       : "=r" (tmp), "=m" (__dummy_lock(lock))
-       : "m"(__dummy_lock(lock)));
+       : "=r" (tmp), "=m" (lock->lock)
+       : "m"(lock->lock) : "memory");
 }
 
 #define spin_trylock(lock) (!test_and_set_bit(0,(lock)))
@@ -119,9 +116,8 @@ static inline void write_lock(rwlock_t * lock)
        "       bne     %1,6b\n"
        "       br      1b\n"
        ".previous"
-       : "=m" (__dummy_lock(lock)), "=&r" (regx)
-       : "0" (__dummy_lock(lock))
-       );
+       : "=m" (*(volatile int *)lock), "=&r" (regx)
+       : "0" (*(volatile int *)lock) : "memory");
 }
 
 static inline void read_lock(rwlock_t * lock)
@@ -140,9 +136,8 @@ static inline void read_lock(rwlock_t * lock)
        "       blbs    %1,6b\n"
        "       br      1b\n"
        ".previous"
-       : "=m" (__dummy_lock(lock)), "=&r" (regx)
-       : "m" (__dummy_lock(lock))
-       );
+       : "=m" (*(volatile int *)lock), "=&r" (regx)
+       : "m" (*(volatile int *)lock) : "memory");
 }
 #endif /* DEBUG_RWLOCK */
 
@@ -156,6 +151,7 @@ static inline void read_unlock(rwlock_t * lock)
 {
        long regx;
        __asm__ __volatile__(
+       "       mb\n"
        "1:     ldl_l   %1,%0\n"
        "       addl    %1,2,%1\n"
        "       stl_c   %1,%0\n"
@@ -163,8 +159,8 @@ static inline void read_unlock(rwlock_t * lock)
        ".subsection 2\n"
        "6:     br      1b\n"
        ".previous"
-       : "=m" (__dummy_lock(lock)), "=&r" (regx)
-       : "m" (__dummy_lock(lock)));
+       : "=m" (*(volatile int *)lock), "=&r" (regx)
+       : "m" (*(volatile int *)lock) : "memory");
 }
 
 #endif /* _ALPHA_SPINLOCK_H */
index 6328750e1f358343fd44a1c9b13ccab1e6a9e544..98e6d3a33aebd54b767ec0cd270abbb63b61197d 100644 (file)
@@ -137,12 +137,19 @@ __asm__ __volatile__("mb": : :"memory")
 #define wmb() \
 __asm__ __volatile__("wmb": : :"memory")
 
+#ifdef __SMP__
+#define smp_mb()       mb()
+#define smp_rmb()      rmb()
+#define smp_wmb()      wmb()
+#else
+#define smp_mb()       barrier()
+#define smp_rmb()      barrier()
+#define smp_wmb()      barrier()
+#endif
+
 #define set_mb(var, value) \
 do { var = value; mb(); } while (0)
 
-#define set_rmb(var, value) \
-do { var = value; rmb(); } while (0)
-
 #define set_wmb(var, value) \
 do { var = value; wmb(); } while (0)
 
@@ -284,11 +291,11 @@ extern int __min_ipl;
 #define getipl()               (rdps() & 7)
 #define setipl(ipl)            ((void) swpipl(ipl))
 
-#define __cli()                        setipl(IPL_MAX)
-#define __sti()                        setipl(IPL_MIN)
+#define __cli()                        do { setipl(IPL_MAX); barrier(); } while(0)
+#define __sti()                        do { barrier(); setipl(IPL_MIN); } while(0)
 #define __save_flags(flags)    ((flags) = rdps())
-#define __save_and_cli(flags)  ((flags) = swpipl(IPL_MAX))
-#define __restore_flags(flags) setipl(flags)
+#define __save_and_cli(flags)  do { (flags) = swpipl(IPL_MAX); barrier(); } while(0)
+#define __restore_flags(flags) do { barrier(); setipl(flags); barrier(); } while(0)
 
 #define local_irq_save(flags)          __save_and_cli(flags)
 #define local_irq_restore(flags)       __restore_flags(flags)
@@ -344,6 +351,8 @@ extern void __global_restore_flags(unsigned long flags);
 
 /*
  * Atomic exchange.
+ * Since it can be used to implement critical sections
+ * it must clobber "memory" (also for interrupts in UP).
  */
 
 extern __inline__ unsigned long
@@ -352,16 +361,18 @@ __xchg_u32(volatile int *m, unsigned long val)
        unsigned long dummy;
 
        __asm__ __volatile__(
-       "1:     ldl_l %0,%2\n"
+       "1:     ldl_l %0,%4\n"
        "       bis $31,%3,%1\n"
        "       stl_c %1,%2\n"
        "       beq %1,2f\n"
+#ifdef CONFIG_SMP
        "       mb\n"
+#endif
        ".subsection 2\n"
        "2:     br 1b\n"
        ".previous"
        : "=&r" (val), "=&r" (dummy), "=m" (*m)
-       : "rI" (val), "m" (*m));
+       : "rI" (val), "m" (*m) : "memory");
 
        return val;
 }
@@ -372,16 +383,18 @@ __xchg_u64(volatile long *m, unsigned long val)
        unsigned long dummy;
 
        __asm__ __volatile__(
-       "1:     ldq_l %0,%2\n"
+       "1:     ldq_l %0,%4\n"
        "       bis $31,%3,%1\n"
        "       stq_c %1,%2\n"
        "       beq %1,2f\n"
+#ifdef CONFIG_SMP
        "       mb\n"
+#endif
        ".subsection 2\n"
        "2:     br 1b\n"
        ".previous"
        : "=&r" (val), "=&r" (dummy), "=m" (*m)
-       : "rI" (val), "m" (*m));
+       : "rI" (val), "m" (*m) : "memory");
 
        return val;
 }
@@ -416,6 +429,11 @@ __xchg(volatile void *ptr, unsigned long x, int size)
  * Atomic compare and exchange.  Compare OLD with MEM, if identical,
  * store NEW in MEM.  Return the initial value in MEM.  Success is
  * indicated by comparing RETURN with OLD.
+ *
+ * The memory barrier should be placed in SMP only when we actually
+ * make the change. If we don't change anything (so if the returned
+ * prev is equal to old) then we aren't acquiring anything new and
+ * we don't need any memory barrier as far I can tell.
  */
 
 #define __HAVE_ARCH_CMPXCHG 1
@@ -426,18 +444,21 @@ __cmpxchg_u32(volatile int *m, int old, int new)
        unsigned long prev, cmp;
 
        __asm__ __volatile__(
-       "1:     ldl_l %0,%2\n"
+       "1:     ldl_l %0,%5\n"
        "       cmpeq %0,%3,%1\n"
        "       beq %1,2f\n"
        "       mov %4,%1\n"
        "       stl_c %1,%2\n"
        "       beq %1,3f\n"
-       "2:     mb\n"
+#ifdef CONFIG_SMP
+       "       mb\n"
+#endif
+       "2:\n"
        ".subsection 2\n"
        "3:     br 1b\n"
        ".previous"
        : "=&r"(prev), "=&r"(cmp), "=m"(*m)
-       : "r"((long) old), "r"(new), "m"(*m));
+       : "r"((long) old), "r"(new), "m"(*m) : "memory");
 
        return prev;
 }
@@ -448,18 +469,21 @@ __cmpxchg_u64(volatile long *m, unsigned long old, unsigned long new)
        unsigned long prev, cmp;
 
        __asm__ __volatile__(
-       "1:     ldq_l %0,%2\n"
+       "1:     ldq_l %0,%5\n"
        "       cmpeq %0,%3,%1\n"
        "       beq %1,2f\n"
        "       mov %4,%1\n"
        "       stq_c %1,%2\n"
        "       beq %1,3f\n"
-       "2:     mb\n"
+#ifdef CONFIG_SMP
+       "       mb\n"
+#endif
+       "2:\n"
        ".subsection 2\n"
        "3:     br 1b\n"
        ".previous"
        : "=&r"(prev), "=&r"(cmp), "=m"(*m)
-       : "r"((long) old), "r"(new), "m"(*m));
+       : "r"((long) old), "r"(new), "m"(*m) : "memory");
 
        return prev;
 }
index 2c8b5288a2c0a7f267909813b57d36f6a26bcf5e..670576b84e8e2b265c7575d35c978fe399d17f88 100644 (file)
@@ -71,6 +71,7 @@ struct termio {
 #define N_SLIP         1
 #define N_MOUSE                2
 #define N_PPP          3
+#define N_STRIP                4
 #define N_AX25         5
 #define N_X25          6       /* X.25 async */
 #define N_6PACK                7
index b05970217e18c33e6bb0f5f82d36ec82303e0cf0..da2861ca37b0eb616f808de871df3ccafb5d212a 100644 (file)
@@ -51,6 +51,9 @@
 #define F_EXLCK                4       /* or 3 */
 #define F_SHLCK                8       /* or 4 */
 
+/* for leases */
+#define F_INPROGRESS   16
+
 /* operations for bsd flock(), also used by the kernel implementation */
 #define LOCK_SH                1       /* shared lock */
 #define LOCK_EX                2       /* exclusive lock */
                                   blocking */
 #define LOCK_UN                8       /* remove lock */
 
+#define LOCK_MAND      32      /* This is a mandatory flock */
+#define LOCK_READ      64      /* ... Which allows concurrent read operations */
+#define LOCK_WRITE     128     /* ... Which allows concurrent write operations */
+#define LOCK_RW                192     /* ... Which allows concurrent read & write ops */
+
 struct flock {
        short l_type;
        short l_whence;
@@ -74,4 +82,5 @@ struct flock64 {
        pid_t  l_pid;
 };
 
+#define F_LINUX_SPECIFIC_BASE  1024
 #endif
index 8a26bc85d1e526eb5df9b6fcb214f3bfd2a665a1..9da87b622a387135e4bae48caf0c865e550f0a12 100644 (file)
@@ -15,8 +15,9 @@
 #define RLIMIT_NOFILE  7               /* max number of open files */
 #define RLIMIT_MEMLOCK 8               /* max locked-in-memory address space */
 #define RLIMIT_AS      9               /* address space limit */
+#define RLIMIT_LOCKS   10              /* maximum file locks held */
 
-#define RLIM_NLIMITS   10
+#define RLIM_NLIMITS   11
 
 #ifdef __KERNEL__
 
@@ -38,6 +39,7 @@
        { INR_OPEN,      INR_OPEN      },       \
        { RLIM_INFINITY, RLIM_INFINITY },       \
        { RLIM_INFINITY, RLIM_INFINITY },       \
+       { RLIM_INFINITY, RLIM_INFINITY },       \
 }
 
 #endif /* __KERNEL__ */
index 945ab9b5d235cf1fcbe18738a353450f3a89ec82..94a7ea2648e170af042582a02f260b16224f5c4f 100644 (file)
  * on us. We need to use _exactly_ the address the user gave us,
  * not some alias that contains the same information.
  */
-#define __atomic_fool_gcc(x) (*(volatile struct { int a[100]; } *)x)
-
-#ifdef CONFIG_SMP
 typedef struct { volatile int counter; } atomic_t;
-#else
-typedef struct { int counter; } atomic_t;
-#endif
 
 #define ATOMIC_INIT(i) { (i) }
 
 #define atomic_read(v)         ((v)->counter)
 #define atomic_set(v,i)                (((v)->counter) = (i))
 
-static __inline__ void atomic_add(int i, volatile atomic_t *v)
+static __inline__ void atomic_add(int i, atomic_t *v)
 {
        __asm__ __volatile__(
                LOCK "addl %1,%0"
-               :"=m" (__atomic_fool_gcc(v))
-               :"ir" (i), "m" (__atomic_fool_gcc(v)));
+               :"=m" (v->counter)
+               :"ir" (i), "m" (v->counter));
 }
 
-static __inline__ void atomic_sub(int i, volatile atomic_t *v)
+static __inline__ void atomic_sub(int i, atomic_t *v)
 {
        __asm__ __volatile__(
                LOCK "subl %1,%0"
-               :"=m" (__atomic_fool_gcc(v))
-               :"ir" (i), "m" (__atomic_fool_gcc(v)));
+               :"=m" (v->counter)
+               :"ir" (i), "m" (v->counter));
 }
 
-static __inline__ int atomic_sub_and_test(int i, volatile atomic_t *v)
+static __inline__ int atomic_sub_and_test(int i, atomic_t *v)
 {
        unsigned char c;
 
        __asm__ __volatile__(
                LOCK "subl %2,%0; sete %1"
-               :"=m" (__atomic_fool_gcc(v)), "=qm" (c)
-               :"ir" (i), "m" (__atomic_fool_gcc(v)));
+               :"=m" (v->counter), "=qm" (c)
+               :"ir" (i), "m" (v->counter) : "memory");
        return c;
 }
 
-static __inline__ void atomic_inc(volatile atomic_t *v)
+static __inline__ void atomic_inc(atomic_t *v)
 {
        __asm__ __volatile__(
                LOCK "incl %0"
-               :"=m" (__atomic_fool_gcc(v))
-               :"m" (__atomic_fool_gcc(v)));
+               :"=m" (v->counter)
+               :"m" (v->counter));
 }
 
-static __inline__ void atomic_dec(volatile atomic_t *v)
+static __inline__ void atomic_dec(atomic_t *v)
 {
        __asm__ __volatile__(
                LOCK "decl %0"
-               :"=m" (__atomic_fool_gcc(v))
-               :"m" (__atomic_fool_gcc(v)));
+               :"=m" (v->counter)
+               :"m" (v->counter));
 }
 
-static __inline__ int atomic_dec_and_test(volatile atomic_t *v)
+static __inline__ int atomic_dec_and_test(atomic_t *v)
 {
        unsigned char c;
 
        __asm__ __volatile__(
                LOCK "decl %0; sete %1"
-               :"=m" (__atomic_fool_gcc(v)), "=qm" (c)
-               :"m" (__atomic_fool_gcc(v)));
+               :"=m" (v->counter), "=qm" (c)
+               :"m" (v->counter) : "memory");
        return c != 0;
 }
 
-static __inline__ int atomic_inc_and_test(volatile atomic_t *v)
+static __inline__ int atomic_inc_and_test(atomic_t *v)
 {
        unsigned char c;
 
        __asm__ __volatile__(
                LOCK "incl %0; sete %1"
-               :"=m" (__atomic_fool_gcc(v)), "=qm" (c)
-               :"m" (__atomic_fool_gcc(v)));
+               :"=m" (v->counter), "=qm" (c)
+               :"m" (v->counter) : "memory");
        return c != 0;
 }
 
-extern __inline__ int atomic_add_negative(int i, volatile atomic_t *v)
+static __inline__ int atomic_add_negative(int i, atomic_t *v)
 {
        unsigned char c;
 
        __asm__ __volatile__(
                LOCK "addl %2,%0; sets %1"
-               :"=m" (__atomic_fool_gcc(v)), "=qm" (c)
-               :"ir" (i), "m" (__atomic_fool_gcc(v)));
+               :"=m" (v->counter), "=qm" (c)
+               :"ir" (i), "m" (v->counter) : "memory");
        return c;
 }
 
 /* These are x86-specific, used by some header files */
 #define atomic_clear_mask(mask, addr) \
 __asm__ __volatile__(LOCK "andl %0,%1" \
-: : "r" (~(mask)),"m" (__atomic_fool_gcc(addr)) : "memory")
+: : "r" (~(mask)),"m" (*addr) : "memory")
 
 #define atomic_set_mask(mask, addr) \
 __asm__ __volatile__(LOCK "orl %0,%1" \
-: : "r" (mask),"m" (__atomic_fool_gcc(addr)) : "memory")
+: : "r" (mask),"m" (*addr) : "memory")
 
 #endif
index 18454e92966302bf60c2ff47808c30353c9442c3..444f247c323197353632f462e1e253543d5a8d4c 100644 (file)
 #define LOCK_PREFIX ""
 #endif
 
-/*
- * Function prototypes to keep gcc -Wall happy
- */
-extern void set_bit(int nr, volatile void * addr);
-extern void clear_bit(int nr, volatile void * addr);
-extern void change_bit(int nr, volatile void * addr);
-extern int test_and_set_bit(int nr, volatile void * addr);
-extern int test_and_clear_bit(int nr, volatile void * addr);
-extern int test_and_change_bit(int nr, volatile void * addr);
-extern int __constant_test_bit(int nr, const volatile void * addr);
-extern int __test_bit(int nr, volatile void * addr);
-extern int find_first_zero_bit(void * addr, unsigned size);
-extern int find_next_zero_bit (void * addr, int size, int offset);
-extern unsigned long ffz(unsigned long word);
-
-/*
- * Some hacks to defeat gcc over-optimizations..
- */
-struct __dummy { unsigned long a[100]; };
-#define ADDR (*(volatile struct __dummy *) addr)
-#define CONST_ADDR (*(volatile const struct __dummy *) addr)
+#define ADDR (*(volatile long *) addr)
 
-extern __inline__ void set_bit(int nr, volatile void * addr)
+static __inline__ void set_bit(int nr, volatile void * addr)
 {
        __asm__ __volatile__( LOCK_PREFIX
                "btsl %1,%0"
@@ -51,7 +31,21 @@ extern __inline__ void set_bit(int nr, volatile void * addr)
                :"Ir" (nr));
 }
 
-extern __inline__ void clear_bit(int nr, volatile void * addr)
+/* WARNING: non atomic and it can be reordered! */
+static __inline__ void __set_bit(int nr, volatile void * addr)
+{
+       __asm__(
+               "btsl %1,%0"
+               :"=m" (ADDR)
+               :"Ir" (nr));
+}
+
+/*
+ * clear_bit() doesn't provide any barrier for the compiler.
+ */
+#define smp_mb__before_clear_bit()     barrier()
+#define smp_mb__after_clear_bit()      barrier()
+static __inline__ void clear_bit(int nr, volatile void * addr)
 {
        __asm__ __volatile__( LOCK_PREFIX
                "btrl %1,%0"
@@ -59,7 +53,7 @@ extern __inline__ void clear_bit(int nr, volatile void * addr)
                :"Ir" (nr));
 }
 
-extern __inline__ void change_bit(int nr, volatile void * addr)
+static __inline__ void change_bit(int nr, volatile void * addr)
 {
        __asm__ __volatile__( LOCK_PREFIX
                "btcl %1,%0"
@@ -67,48 +61,77 @@ extern __inline__ void change_bit(int nr, volatile void * addr)
                :"Ir" (nr));
 }
 
-extern __inline__ int test_and_set_bit(int nr, volatile void * addr)
+/*
+ * It will also imply a memory barrier, thus it must clobber memory
+ * to make sure to reload anything that was cached into registers
+ * outside _this_ critical section.
+ */
+static __inline__ int test_and_set_bit(int nr, volatile void * addr)
 {
        int oldbit;
 
        __asm__ __volatile__( LOCK_PREFIX
+               "btsl %2,%1\n\tsbbl %0,%0"
+               :"=r" (oldbit),"=m" (ADDR)
+               :"Ir" (nr) : "memory");
+       return oldbit;
+}
+
+/* WARNING: non atomic and it can be reordered! */
+static __inline__ int __test_and_set_bit(int nr, volatile void * addr)
+{
+       int oldbit;
+
+       __asm__(
                "btsl %2,%1\n\tsbbl %0,%0"
                :"=r" (oldbit),"=m" (ADDR)
                :"Ir" (nr));
        return oldbit;
 }
 
-extern __inline__ int test_and_clear_bit(int nr, volatile void * addr)
+static __inline__ int test_and_clear_bit(int nr, volatile void * addr)
 {
        int oldbit;
 
        __asm__ __volatile__( LOCK_PREFIX
+               "btrl %2,%1\n\tsbbl %0,%0"
+               :"=r" (oldbit),"=m" (ADDR)
+               :"Ir" (nr) : "memory");
+       return oldbit;
+}
+
+/* WARNING: non atomic and it can be reordered! */
+static __inline__ int __test_and_clear_bit(int nr, volatile void * addr)
+{
+       int oldbit;
+
+       __asm__(
                "btrl %2,%1\n\tsbbl %0,%0"
                :"=r" (oldbit),"=m" (ADDR)
                :"Ir" (nr));
        return oldbit;
 }
 
-extern __inline__ int test_and_change_bit(int nr, volatile void * addr)
+static __inline__ int test_and_change_bit(int nr, volatile void * addr)
 {
        int oldbit;
 
        __asm__ __volatile__( LOCK_PREFIX
                "btcl %2,%1\n\tsbbl %0,%0"
                :"=r" (oldbit),"=m" (ADDR)
-               :"Ir" (nr));
+               :"Ir" (nr) : "memory");
        return oldbit;
 }
 
 /*
  * This routine doesn't need to be atomic.
  */
-extern __inline__ int __constant_test_bit(int nr, const volatile void * addr)
+static __inline__ int constant_test_bit(int nr, const volatile void * addr)
 {
        return ((1UL << (nr & 31)) & (((const volatile unsigned int *) addr)[nr >> 5])) != 0;
 }
 
-extern __inline__ int __test_bit(int nr, volatile void * addr)
+static __inline__ int variable_test_bit(int nr, volatile void * addr)
 {
        int oldbit;
 
@@ -121,13 +144,13 @@ extern __inline__ int __test_bit(int nr, volatile void * addr)
 
 #define test_bit(nr,addr) \
 (__builtin_constant_p(nr) ? \
__constant_test_bit((nr),(addr)) : \
__test_bit((nr),(addr)))
+ constant_test_bit((nr),(addr)) : \
variable_test_bit((nr),(addr)))
 
 /*
  * Find-bit routines..
  */
-extern __inline__ int find_first_zero_bit(void * addr, unsigned size)
+static __inline__ int find_first_zero_bit(void * addr, unsigned size)
 {
        int d0, d1, d2;
        int res;
@@ -151,7 +174,7 @@ extern __inline__ int find_first_zero_bit(void * addr, unsigned size)
        return res;
 }
 
-extern __inline__ int find_next_zero_bit (void * addr, int size, int offset)
+static __inline__ int find_next_zero_bit (void * addr, int size, int offset)
 {
        unsigned long * p = ((unsigned long *) addr) + (offset >> 5);
        int set = 0, bit = offset & 31, res;
@@ -182,7 +205,7 @@ extern __inline__ int find_next_zero_bit (void * addr, int size, int offset)
  * ffz = Find First Zero in word. Undefined if no zero exists,
  * so code should check against ~0UL first..
  */
-extern __inline__ unsigned long ffz(unsigned long word)
+static __inline__ unsigned long ffz(unsigned long word)
 {
        __asm__("bsfl %1,%0"
                :"=r" (word)
@@ -198,7 +221,7 @@ extern __inline__ unsigned long ffz(unsigned long word)
  * differs in spirit from the above ffz (man ffs).
  */
 
-extern __inline__ int ffs(int x)
+static __inline__ int ffs(int x)
 {
        int r;
 
@@ -222,16 +245,16 @@ extern __inline__ int ffs(int x)
 
 #ifdef __KERNEL__
 
-#define ext2_set_bit                 test_and_set_bit
-#define ext2_clear_bit               test_and_clear_bit
+#define ext2_set_bit                 __test_and_set_bit
+#define ext2_clear_bit               __test_and_clear_bit
 #define ext2_test_bit                test_bit
 #define ext2_find_first_zero_bit     find_first_zero_bit
 #define ext2_find_next_zero_bit      find_next_zero_bit
 
 /* Bitmap functions for the minix filesystem.  */
-#define minix_test_and_set_bit(nr,addr) test_and_set_bit(nr,addr)
-#define minix_set_bit(nr,addr) set_bit(nr,addr)
-#define minix_test_and_clear_bit(nr,addr) test_and_clear_bit(nr,addr)
+#define minix_test_and_set_bit(nr,addr) __test_and_set_bit(nr,addr)
+#define minix_set_bit(nr,addr) __set_bit(nr,addr)
+#define minix_test_and_clear_bit(nr,addr) __test_and_clear_bit(nr,addr)
 #define minix_test_bit(nr,addr) test_bit(nr,addr)
 #define minix_find_first_zero_bit(addr,size) find_first_zero_bit(addr,size)
 
index 5292b3798eb9f72b7ac545147bc696ccb81ef2b5..5637eca593e753409750a720ca25d4ae225263a4 100644 (file)
@@ -51,6 +51,9 @@
 #define F_EXLCK                4       /* or 3 */
 #define F_SHLCK                8       /* or 4 */
 
+/* for leases */
+#define F_INPROGRESS   16
+
 /* operations for bsd flock(), also used by the kernel implementation */
 #define LOCK_SH                1       /* shared lock */
 #define LOCK_EX                2       /* exclusive lock */
                                   blocking */
 #define LOCK_UN                8       /* remove lock */
 
+#define LOCK_MAND      32      /* This is a mandatory flock */
+#define LOCK_READ      64      /* ... Which allows concurrent read operations */
+#define LOCK_WRITE     128     /* ... Which allows concurrent write operations */
+#define LOCK_RW                192     /* ... Which allows concurrent read & write ops */
+
 struct flock {
        short l_type;
        short l_whence;
@@ -74,4 +82,6 @@ struct flock64 {
        pid_t  l_pid;
 };
 
+#define F_LINUX_SPECIFIC_BASE  1024
+
 #endif
index e49c5b8c3453347a30def0896a7b91dd1d3ff06a..e0da3ad1d05580996ab5296829d1718f6cfec6c1 100644 (file)
@@ -15,8 +15,9 @@
 #define RLIMIT_NOFILE  7               /* max number of open files */
 #define RLIMIT_MEMLOCK 8               /* max locked-in-memory address space */
 #define RLIMIT_AS      9               /* address space limit */
+#define RLIMIT_LOCKS   10              /* maximum file locks held */
 
-#define RLIM_NLIMITS   10
+#define RLIM_NLIMITS   11
 
 /*
  * SuS says limits have to be unsigned.
@@ -38,6 +39,7 @@
        {      INR_OPEN,     INR_OPEN  },               \
        { RLIM_INFINITY, RLIM_INFINITY },               \
        { RLIM_INFINITY, RLIM_INFINITY },               \
+        { RLIM_INFINITY, RLIM_INFINITY },              \
 }
 
 #endif /* __KERNEL__ */
index ac9e8f36bd9db88491f6672ee852b07b0d951212..9475419f9509f1a7d24ad027d719a8ca9a820c84 100644 (file)
@@ -17,9 +17,6 @@
 #ifndef _ASM_I386_RWLOCK_H
 #define _ASM_I386_RWLOCK_H
 
-typedef struct { unsigned long a[100]; } __dummy_lock_t;
-#define __dummy_lock(lock) (*(__dummy_lock_t *)(lock))
-
 #define RW_LOCK_BIAS            0x01000000
 #define RW_LOCK_BIAS_STR       "0x01000000"
 
@@ -44,7 +41,7 @@ typedef struct { unsigned long a[100]; } __dummy_lock_t;
                     "popl %%eax\n\t" \
                     "jmp 1b\n" \
                     ".previous" \
-                    :"=m" (__dummy_lock(rw)))
+                    :"=m" (*(volatile int *)rw) : : "memory")
 
 #define __build_read_lock(rw, helper)  do { \
                                                if (__builtin_constant_p(rw)) \
@@ -74,7 +71,7 @@ typedef struct { unsigned long a[100]; } __dummy_lock_t;
                     "popl %%eax\n\t" \
                     "jmp 1b\n" \
                     ".previous" \
-                    :"=m" (__dummy_lock(rw)))
+                    :"=m" (*(volatile int *)rw) : : "memory")
 
 #define __build_write_lock(rw, helper) do { \
                                                if (__builtin_constant_p(rw)) \
index a67aaf412b8d8fc47e386078b91854077c816652..4a92bb2a2a17fb5e61e5936369823fe51b8080a4 100644 (file)
@@ -70,13 +70,12 @@ static inline int spin_trylock(spinlock_t *lock)
        char oldval;
        __asm__ __volatile__(
                "xchgb %b0,%1"
-               :"=q" (oldval), "=m" (__dummy_lock(lock))
-               :"0" (0)
-               :"memory");
+               :"=q" (oldval), "=m" (lock->lock)
+               :"0" (0) : "memory");
        return oldval > 0;
 }
 
-extern inline void spin_lock(spinlock_t *lock)
+static inline void spin_lock(spinlock_t *lock)
 {
 #if SPINLOCK_DEBUG
        __label__ here;
@@ -88,11 +87,10 @@ printk("eip: %p\n", &&here);
 #endif
        __asm__ __volatile__(
                spin_lock_string
-               :"=m" (__dummy_lock(lock))
-               : :"memory");
+               :"=m" (lock->lock) : : "memory");
 }
 
-extern inline void spin_unlock(spinlock_t *lock)
+static inline void spin_unlock(spinlock_t *lock)
 {
 #if SPINLOCK_DEBUG
        if (lock->magic != SPINLOCK_MAGIC)
@@ -102,8 +100,7 @@ extern inline void spin_unlock(spinlock_t *lock)
 #endif
        __asm__ __volatile__(
                spin_unlock_string
-               :"=m" (__dummy_lock(lock))
-               : :"memory");
+               :"=m" (lock->lock) : : "memory");
 }
 
 /*
@@ -146,7 +143,7 @@ typedef struct {
  */
 /* the spinlock helpers are in arch/i386/kernel/semaphore.S */
 
-extern inline void read_lock(rwlock_t *rw)
+static inline void read_lock(rwlock_t *rw)
 {
 #if SPINLOCK_DEBUG
        if (rw->magic != RWLOCK_MAGIC)
@@ -155,7 +152,7 @@ extern inline void read_lock(rwlock_t *rw)
        __build_read_lock(rw, "__read_lock_failed");
 }
 
-extern inline void write_lock(rwlock_t *rw)
+static inline void write_lock(rwlock_t *rw)
 {
 #if SPINLOCK_DEBUG
        if (rw->magic != RWLOCK_MAGIC)
@@ -164,10 +161,10 @@ extern inline void write_lock(rwlock_t *rw)
        __build_write_lock(rw, "__write_lock_failed");
 }
 
-#define read_unlock(rw)                asm volatile("lock ; incl %0" :"=m" (__dummy_lock(&(rw)->lock)))
-#define write_unlock(rw)       asm volatile("lock ; addl $" RW_LOCK_BIAS_STR ",%0":"=m" (__dummy_lock(&(rw)->lock)))
+#define read_unlock(rw)                asm volatile("lock ; incl %0" :"=m" ((rw)->lock) : : "memory")
+#define write_unlock(rw)       asm volatile("lock ; addl $" RW_LOCK_BIAS_STR ",%0":"=m" ((rw)->lock) : : "memory")
 
-extern inline int write_trylock(rwlock_t *lock)
+static inline int write_trylock(rwlock_t *lock)
 {
        atomic_t *count = (atomic_t *)lock;
        if (atomic_sub_and_test(RW_LOCK_BIAS, count))
index 3ae0df6186b67dd24b12e3309f635cd81f838509..05e54c4009bbc3c7461dd14f17d7df8fbc632467 100644 (file)
@@ -278,11 +278,22 @@ static inline unsigned long __cmpxchg(volatile void *ptr, unsigned long old,
 #endif
 #define rmb()  mb()
 #define wmb()  __asm__ __volatile__ ("": : :"memory")
+
+#ifdef __SMP__
+#define smp_mb()       mb()
+#define smp_rmb()      rmb()
+#define smp_wmb()      wmb()
+#else
+#define smp_mb()       barrier()
+#define smp_rmb()      barrier()
+#define smp_wmb()      barrier()
+#endif
+
 #define set_mb(var, value) do { xchg(&var, value); } while (0)
 #define set_wmb(var, value) do { var = value; wmb(); } while (0)
 
 /* interrupt control.. */
-#define __save_flags(x)                __asm__ __volatile__("pushfl ; popl %0":"=g" (x): /* no input */ :"memory")
+#define __save_flags(x)                __asm__ __volatile__("pushfl ; popl %0":"=g" (x): /* no input */)
 #define __restore_flags(x)     __asm__ __volatile__("pushl %0 ; popfl": /* no output */ :"g" (x):"memory")
 #define __cli()                __asm__ __volatile__("cli": : :"memory")
 #define __sti()                        __asm__ __volatile__("sti": : :"memory")
@@ -291,9 +302,9 @@ static inline unsigned long __cmpxchg(volatile void *ptr, unsigned long old,
 
 /* For spinlocks etc */
 #define local_irq_save(x)      __asm__ __volatile__("pushfl ; popl %0 ; cli":"=g" (x): /* no input */ :"memory")
-#define local_irq_restore(x)   __asm__ __volatile__("pushl %0 ; popfl": /* no output */ :"g" (x):"memory")
-#define local_irq_disable()    __asm__ __volatile__("cli": : :"memory")
-#define local_irq_enable()     __asm__ __volatile__("sti": : :"memory")
+#define local_irq_restore(x)   __restore_flags(x)
+#define local_irq_disable()    __cli()
+#define local_irq_enable()     __sti()
 
 #ifdef CONFIG_SMP
 
index 175618aaa52753e0baed8e840849c6a49d37d5a7..c20ab10ec60053edaf4221f41f5e4996a337c1c3 100644 (file)
@@ -55,6 +55,9 @@
 #define F_EXLCK                4       /* or 3 */
 #define F_SHLCK                8       /* or 4 */
 
+/* for leases */
+#define F_INPROGRESS   16
+
 /* operations for bsd flock(), also used by the kernel implementation */
 #define LOCK_SH                1       /* shared lock */
 #define LOCK_EX                2       /* exclusive lock */
                                   blocking */
 #define LOCK_UN                8       /* remove lock */
 
+#define LOCK_MAND      32      /* This is a mandatory flock */
+#define LOCK_READ      64      /* ... Which allows concurrent read operations */
+#define LOCK_WRITE     128     /* ... Which allows concurrent write operations */
+#define LOCK_RW                192     /* ... Which allows concurrent read & write ops */
+
 struct flock {
        short l_type;
        short l_whence;
@@ -70,4 +78,5 @@ struct flock {
        pid_t l_pid;
 };
 
+#define F_LINUX_SPECIFIC_BASE  1024
 #endif /* _ASM_IA64_FCNTL_H */
index a8d75ecdde8d58da0f1649093f8e870d50ade98c..40223b9c327db85c6990ab4a15c988b1f4e2337c 100644 (file)
@@ -18,8 +18,9 @@
 #define RLIMIT_NOFILE  7               /* max number of open files */
 #define RLIMIT_MEMLOCK 8               /* max locked-in-memory address space */
 #define RLIMIT_AS      9               /* address space limit */
+#define RLIMIT_LOCKS   10              /* maximum file locks held */
 
-#define RLIM_NLIMITS   10
+#define RLIM_NLIMITS   11
 
 /*
  * SuS says limits have to be unsigned.
@@ -41,6 +42,7 @@
        {      INR_OPEN,     INR_OPEN  },               \
        { RLIM_INFINITY, RLIM_INFINITY },               \
        { RLIM_INFINITY, RLIM_INFINITY },               \
+       { RLIM_INFINITY, RLIM_INFINITY },               \
 }
 
 # endif /* __KERNEL__ */
index 63b22687adbadebe3acdc4b716f8f39ccf0ab8c9..9738061b764a973ee59b86db5d6e096b8b5a6765 100644 (file)
@@ -47,6 +47,9 @@
 #define F_EXLCK                4       /* or 3 */
 #define F_SHLCK                8       /* or 4 */
 
+/* for leases */
+#define F_INPROGRESS   16
+
 /* operations for bsd flock(), also used by the kernel implementation */
 #define LOCK_SH                1       /* shared lock */
 #define LOCK_EX                2       /* exclusive lock */
                                   blocking */
 #define LOCK_UN                8       /* remove lock */
 
+#define LOCK_MAND      32      /* This is a mandatory flock */
+#define LOCK_READ      64      /* ... Which allows concurrent read operations */
+#define LOCK_WRITE     128     /* ... Which allows concurrent write operations */
+#define LOCK_RW                192     /* ... Which allows concurrent read & write ops */
+
 struct flock {
        short l_type;
        short l_whence;
@@ -62,4 +70,5 @@ struct flock {
        pid_t l_pid;
 };
 
+#define F_LINUX_SPECIFIC_BASE  1024
 #endif /* _M68K_FCNTL_H */
index f2a6f0f410ad196b726b0b2e0f9124b20d42ca4b..29cc6e973d98e79ac08b9a021bfe96515717943b 100644 (file)
@@ -15,8 +15,9 @@
 #define RLIMIT_NOFILE  7               /* max number of open files */
 #define RLIMIT_MEMLOCK 8               /* max locked-in-memory address space*/
 #define RLIMIT_AS      9               /* address space limit */
+#define RLIMIT_LOCKS   10              /* maximum file locks held */
 
-#define RLIM_NLIMITS   10
+#define RLIM_NLIMITS   11
 
 /*
  * SuS says limits have to be unsigned.
@@ -37,6 +38,7 @@
   {0, 0},              \
   {INR_OPEN, INR_OPEN}, \
   {LONG_MAX, LONG_MAX}, \
+  {LONG_MAX, LONG_MAX}, \
   {LONG_MAX, LONG_MAX}  \
 }
 
index 87d52eb80b9355bbbdb02b1950f8b6cafe34377a..b14cc9063f14033afb9ffecdce3e1f819f33d926 100644 (file)
@@ -56,6 +56,9 @@
 #define F_EXLCK                4       /* or 3 */
 #define F_SHLCK                8       /* or 4 */
 
+/* for leases */
+#define F_INPROGRESS   16
+
 /* operations for bsd flock(), also used by the kernel implementation */
 #define LOCK_SH                1       /* shared lock */
 #define LOCK_EX                2       /* exclusive lock */
                                   blocking */
 #define LOCK_UN                8       /* remove lock */
 
+#define LOCK_MAND      32      /* This is a mandatory flock */
+#define LOCK_READ      64      /* ... Which allows concurrent read operations */
+#define LOCK_WRITE     128     /* ... Which allows concurrent write operations */
+#define LOCK_RW                192     /* ... Which allows concurrent read & write ops */
+
 typedef struct flock {
        short l_type;
        short l_whence;
@@ -73,4 +81,5 @@ typedef struct flock {
        long  pad[4];                   /* ZZZZZZZZZZZZZZZZZZZZZZZZZZ */
 } flock_t;
 
+#define F_LINUX_SPECIFIC_BASE  1024
 #endif /* __ASM_MIPS_FCNTL_H */
index af41618ba9962d968dd6d7c39d5e9b242b32991b..718e983e61f2a8a39a31166a94fc5defad16163d 100644 (file)
@@ -22,8 +22,9 @@
 #define RLIMIT_RSS 7                   /* max resident set size */
 #define RLIMIT_NPROC 8                 /* max number of processes */
 #define RLIMIT_MEMLOCK 9               /* max locked-in-memory address space */
+#define RLIMIT_LOCKS   10              /* maximum file locks held */
 
-#define RLIM_NLIMITS 10                        /* Number of limit flavors.  */
+#define RLIM_NLIMITS 11                        /* Number of limit flavors.  */
 
 /*
  * SuS says limits have to be unsigned.
@@ -45,6 +46,7 @@
        { RLIM_INFINITY, RLIM_INFINITY },               \
        { 0,             0             },               \
        { RLIM_INFINITY, RLIM_INFINITY },               \
+       { RLIM_INFINITY, RLIM_INFINITY },               \
 }
 
 #endif /* __KERNEL__ */
index a057c66103cae66890c611868e0be7cc7a43999c..a0b1a235c21fe3b9ad714fd1570f60ced38acf80 100644 (file)
@@ -56,6 +56,9 @@
 #define F_EXLCK                4       /* or 3 */
 #define F_SHLCK                8       /* or 4 */
 
+/* for leases */
+#define F_INPROGRESS   16
+
 /* operations for bsd flock(), also used by the kernel implementation */
 #define LOCK_SH                1       /* shared lock */
 #define LOCK_EX                2       /* exclusive lock */
                                   blocking */
 #define LOCK_UN                8       /* remove lock */
 
+#define LOCK_MAND      32      /* This is a mandatory flock */
+#define LOCK_READ      64      /* ... Which allows concurrent read operations */
+#define LOCK_WRITE     128     /* ... Which allows concurrent write operations */
+#define LOCK_RW                192     /* ... Which allows concurrent read & write ops */
+
 typedef struct flock {
        short l_type;
        short l_whence;
@@ -73,4 +81,5 @@ typedef struct flock {
        long  pad[4];                   /* ZZZZZZZZZZZZZZZZZZZZZZZZZZ */
 } flock_t;
 
+#define F_LINUX_SPECIFIC_BASE  1024
 #endif /* _ASM_FCNTL_H */
index b4af331448007392ee782ae40b4bd9d92bab02e1..448a8bb3a73bd2d0e1dd1052a7beb3cb57a167bd 100644 (file)
@@ -23,8 +23,9 @@
 #define RLIMIT_RSS 7                   /* max resident set size */
 #define RLIMIT_NPROC 8                 /* max number of processes */
 #define RLIMIT_MEMLOCK 9               /* max locked-in-memory address space */
+#define RLIMIT_LOCKS 10                        /* maximum file locks held */
 
-#define RLIM_NLIMITS 10                        /* Number of limit flavors.  */
+#define RLIM_NLIMITS 11                        /* Number of limit flavors.  */
 
 /*
  * SuS says limits have to be unsigned.
@@ -46,6 +47,7 @@
        { RLIM_INFINITY, RLIM_INFINITY },               \
        { 0,             0             },               \
        { RLIM_INFINITY, RLIM_INFINITY },               \
+       { RLIM_INFINITY, RLIM_INFINITY },               \
 }
 
 #endif /* __KERNEL__ */
index 81e57da5d3121fdaec768845fe206df283dd3a97..f8181d44d5393f1c5ace25e14e5ba496b73efb21 100644 (file)
@@ -51,6 +51,9 @@
 #define F_EXLCK                4       /* or 3 */
 #define F_SHLCK                8       /* or 4 */
 
+/* for leases */
+#define F_INPROGRESS   16
+
 /* operations for bsd flock(), also used by the kernel implementation */
 #define LOCK_SH                1       /* shared lock */
 #define LOCK_EX                2       /* exclusive lock */
                                   blocking */
 #define LOCK_UN                8       /* remove lock */
 
+#define LOCK_MAND      32      /* This is a mandatory flock */
+#define LOCK_READ      64      /* ... Which allows concurrent read operations */
+#define LOCK_WRITE     128     /* ... Which allows concurrent write operations */
+#define LOCK_RW                192     /* ... Which allows concurrent read & write ops */
+
 #ifdef __KERNEL__
 #define F_POSIX                1
 #define F_FLOCK                2
@@ -80,4 +88,5 @@ struct flock64 {
        pid_t  l_pid;
 };
 
+#define F_LINUX_SPECIFIC_BASE  1024
 #endif
index d31976a3d651af4a86c6c5ac4b5483c7a004b3d0..956b346026c084e19aad42d55219c466752c8692 100644 (file)
@@ -11,8 +11,9 @@
 #define RLIMIT_NOFILE  7               /* max number of open files */
 #define RLIMIT_MEMLOCK 8               /* max locked-in-memory address space */
 #define RLIMIT_AS      9               /* address space limit(?) */
+#define RLIMIT_LOCKS   10              /* maximum file locks held */
 
-#define RLIM_NLIMITS   10
+#define RLIM_NLIMITS   11
 
 #ifdef __KERNEL__
 
@@ -35,6 +36,7 @@
        {      INR_OPEN,     INR_OPEN  },               \
        { RLIM_INFINITY, RLIM_INFINITY },               \
        { RLIM_INFINITY, RLIM_INFINITY },               \
+       { RLIM_INFINITY, RLIM_INFINITY },               \
 }
 
 #endif /* __KERNEL__ */
index 3631627a4c02658679cd1637a3880538d0e61e5c..c1987889adb35d2b8b945068b26ce43ca28df172 100644 (file)
@@ -54,6 +54,9 @@
 #define F_EXLCK                4       /* or 3 */
 #define F_SHLCK                8       /* or 4 */
 
+/* for leases */
+#define F_INPROGRESS   16
+
 /* operations for bsd flock(), also used by the kernel implementation */
 #define LOCK_SH                1       /* shared lock */
 #define LOCK_EX                2       /* exclusive lock */
                                   blocking */
 #define LOCK_UN                8       /* remove lock */
 
+#define LOCK_MAND      32      /* This is a mandatory flock */
+#define LOCK_READ      64      /* ... Which allows concurrent read operations */
+#define LOCK_WRITE     128     /* ... Which allows concurrent write operations */
+#define LOCK_RW                192     /* ... Which allows concurrent read & write ops */
+
 struct flock {
        short l_type;
        short l_whence;
@@ -69,4 +77,5 @@ struct flock {
        pid_t l_pid;
 };
 
+#define F_LINUX_SPECIFIC_BASE  1024
 #endif
index 9cf7a4993281540968bf2e620e13059d73383b0c..4e5d9114396fedc10c13dd877ad5b96169ae48ed 100644 (file)
@@ -23,8 +23,9 @@
 #define RLIMIT_NOFILE  7               /* max number of open files */
 #define RLIMIT_MEMLOCK 8               /* max locked-in-memory address space */
 #define RLIMIT_AS      9               /* address space limit */
+#define RLIMIT_AS      10              /* maximum file locks held */
 
-#define RLIM_NLIMITS   10
+#define RLIM_NLIMITS   11
 
 /*
  * SuS says limits have to be unsigned.
@@ -46,6 +47,7 @@
        { INR_OPEN, INR_OPEN },                         \
        { LONG_MAX, LONG_MAX },                         \
        { LONG_MAX, LONG_MAX },                         \
+       { LONG_MAX, LONG_MAX },                         \
 }
 
 #endif /* __KERNEL__ */
index 7e8345ff6372cc0513c0d94be92774c6656c11bc..c2d0ea1c2a77664c764a5c8c2e7fa2f4b9ae40c4 100644 (file)
@@ -47,6 +47,9 @@
 #define F_EXLCK                4       /* or 3 */
 #define F_SHLCK                8       /* or 4 */
 
+/* for leases */
+#define F_INPROGRESS   16
+
 /* operations for bsd flock(), also used by the kernel implementation */
 #define LOCK_SH                1       /* shared lock */
 #define LOCK_EX                2       /* exclusive lock */
                                   blocking */
 #define LOCK_UN                8       /* remove lock */
 
+#define LOCK_MAND      32      /* This is a mandatory flock */
+#define LOCK_READ      64      /* ... Which allows concurrent read operations */
+#define LOCK_WRITE     128     /* ... Which allows concurrent write operations */
+#define LOCK_RW                192     /* ... Which allows concurrent read & write ops */
+
 struct flock {
        short l_type;
        short l_whence;
@@ -62,5 +70,6 @@ struct flock {
        pid_t l_pid;
 };
 
+#define F_LINUX_SPECIFIC_BASE  1024
 #endif /* __ASM_SH_FCNTL_H */
 
index 084ad61ecf068d20405d80348cee0ce860129d1a..574b64488d5c71750ffa692f8da1826db28feb5a 100644 (file)
@@ -15,8 +15,9 @@
 #define RLIMIT_NOFILE  7               /* max number of open files */
 #define RLIMIT_MEMLOCK 8               /* max locked-in-memory address space */
 #define RLIMIT_AS      9               /* address space limit */
+#define RLIMIT_LOCKS   10              /* maximum file locks held */
 
-#define RLIM_NLIMITS   10
+#define RLIM_NLIMITS   11
 
 #ifdef __KERNEL__
 
@@ -38,6 +39,7 @@
        {      INR_OPEN,     INR_OPEN  },               \
        { RLIM_INFINITY, RLIM_INFINITY },               \
        { RLIM_INFINITY, RLIM_INFINITY },               \
+       { RLIM_INFINITY, RLIM_INFINITY },               \
 }
 
 #endif /* __KERNEL__ */
index 3447659302cd4782227952c7c73fc4b690abd95f..d481e7b2714bcd5a8f9893640e9d09220b8de352 100644 (file)
@@ -50,6 +50,9 @@
 #define F_EXLCK                4       /* or 3 */
 #define F_SHLCK                8       /* or 4 */
 
+/* for leases */
+#define F_INPROGRESS   16
+
 /* operations for bsd flock(), also used by the kernel implementation */
 #define LOCK_SH                1       /* shared lock */
 #define LOCK_EX                2       /* exclusive lock */
                                   blocking */
 #define LOCK_UN                8       /* remove lock */
 
+#define LOCK_MAND      32      /* This is a mandatory flock */
+#define LOCK_READ      64      /* ... Which allows concurrent read operations */
+#define LOCK_WRITE     128     /* ... Which allows concurrent write operations */
+#define LOCK_RW                192     /* ... Which allows concurrent read & write ops */
+
 struct flock {
        short l_type;
        short l_whence;
@@ -75,4 +83,5 @@ struct flock64 {
        short __unused;
 };
 
+#define F_LINUX_SPECIFIC_BASE  1024
 #endif
index 5b654cdf9e71417169059d4bfa1f3bdd9783d656..24cca3ba1a7c6fddf9e493f332efca0704383d09 100644 (file)
@@ -21,8 +21,9 @@
 #define RLIMIT_NPROC   7               /* max number of processes */
 #define RLIMIT_MEMLOCK  8               /* max locked-in-memory address space */
 #define RLIMIT_AS       9               /* address space limit */
+#define RLIMIT_LOCKS   10              /* maximum file locks held */
 
-#define RLIM_NLIMITS   10
+#define RLIM_NLIMITS   11
 
 /*
  * SuS says limits have to be unsigned.
@@ -42,6 +43,7 @@
     {RLIM_INFINITY, RLIM_INFINITY},    \
     {INR_OPEN, INR_OPEN}, {0, 0},      \
     {RLIM_INFINITY, RLIM_INFINITY},    \
+    {RLIM_INFINITY, RLIM_INFINITY},    \
     {RLIM_INFINITY, RLIM_INFINITY}     \
 }
 
index bd087fc215011483e6e8fd708ff7745a5e189a70..e485ed45f100f17c95470cb3e2a1d440e6867d14 100644 (file)
@@ -52,6 +52,9 @@
 #define F_EXLCK                4       /* or 3 */
 #define F_SHLCK                8       /* or 4 */
 
+/* for leases */
+#define F_INPROGRESS   16
+
 /* operations for bsd flock(), also used by the kernel implementation */
 #define LOCK_SH                1       /* shared lock */
 #define LOCK_EX                2       /* exclusive lock */
                                   blocking */
 #define LOCK_UN                8       /* remove lock */
 
+#define LOCK_MAND      32      /* This is a mandatory flock */
+#define LOCK_READ      64      /* ... Which allows concurrent read operations */
+#define LOCK_WRITE     128     /* ... Which allows concurrent write operations */
+#define LOCK_RW                192     /* ... Which allows concurrent read & write ops */
+
 struct flock {
        short l_type;
        short l_whence;
@@ -83,4 +91,5 @@ struct flock32 {
 #define flock64        flock
 #endif
 
+#define F_LINUX_SPECIFIC_BASE  1024
 #endif /* !(_SPARC64_FCNTL_H) */
index 1d2b5eff028343488ed5408b696eecc00a18e491..658637fbc8f48743c161948a770c6ce04d38e85b 100644 (file)
@@ -21,8 +21,9 @@
 #define RLIMIT_NPROC   7               /* max number of processes */
 #define RLIMIT_MEMLOCK  8               /* max locked-in-memory address space */
 #define RLIMIT_AS       9               /* address space limit */
+#define RLIMIT_LOCKS   10              /* maximum file locks held */
 
-#define RLIM_NLIMITS   10
+#define RLIM_NLIMITS   11
 
 /*
  * SuS says limits have to be unsigned.
@@ -41,6 +42,7 @@
     {RLIM_INFINITY, RLIM_INFINITY},    \
     {INR_OPEN, INR_OPEN}, {0, 0},      \
     {RLIM_INFINITY, RLIM_INFINITY},    \
+    {RLIM_INFINITY, RLIM_INFINITY},    \
     {RLIM_INFINITY, RLIM_INFINITY}     \
 }
 
index 803a15a51f16246604ff2a536f37f8acbb7c600b..6235edd7c743e4deaeb22c1407603f6f30d8bb3b 100644 (file)
@@ -100,8 +100,8 @@ extern void __global_restore_flags(unsigned long flags);
 #define nop()          __asm__ __volatile__ ("nop")
 
 #define membar(type)   __asm__ __volatile__ ("membar " type : : : "memory");
-#define rmb()          membar("#LoadLoad | #LoadStore")
-#define wmb()          membar("#StoreLoad | #StoreStore")
+#define rmb()          membar("#LoadLoad")
+#define wmb()          membar("#StoreStore")
 #define set_mb(__var, __value) \
        do { __var = __value; membar("#StoreLoad | #StoreStore"); } while(0)
 #define set_wmb(__var, __value) \
index c881f0fd61cbfadf2e13b0a9d036f575437d63fc..43add21b55d8a02f167609a32c48293dd28e9056 100644 (file)
@@ -46,7 +46,7 @@ struct adfs_discrecord {
  * appear to be correct if the sector contains all zeros, so also check that
  * the disk size is non-zero!!!
  */
-extern inline int adfs_checkbblk(unsigned char *ptr)
+static inline int adfs_checkbblk(unsigned char *ptr)
 {
        unsigned int result = 0;
        unsigned char *p = ptr + 511;
index 5b1dafc1731f9b35d1196a6a088dacea21d74087..5c2c0c35550479ebeb806feacfe5583ea2ed0124 100644 (file)
@@ -161,7 +161,7 @@ extern void aarp_proto_init(void);
  *     Give a device find its atif control structure
  */
 
-extern __inline__ struct atalk_iface *atalk_find_dev(struct net_device *dev)
+static inline struct atalk_iface *atalk_find_dev(struct net_device *dev)
 {
        return dev->atalk_ptr;
 }
index 2fd39cac9ef4e573d97c15c1c4a918e029361240..c805304f11e6c17983e49890b0955af200093717 100644 (file)
@@ -114,10 +114,23 @@ static inline void br_read_lock (enum brlock_indices idx)
        lock = &__br_write_locks[idx].lock;
 again:
        (*ctr)++;
-       rmb();
+       mb();
        if (spin_is_locked(lock)) {
                (*ctr)--;
-               rmb();
+               wmb(); /*
+                       * The release of the ctr must become visible
+                       * to the other cpus eventually thus wmb(),
+                       * we don't care if spin_is_locked is reordered
+                       * before the releasing of the ctr.
+                       * However IMHO this wmb() is superflous even in theory.
+                       * It would not be superflous only if on the
+                       * other CPUs doing a ldl_l instead of an ldl
+                       * would make a difference and I don't think this is
+                       * the case.
+                       * I'd like to clarify this issue further
+                       * but for now this is a slow path so adding the
+                       * wmb() will keep us on the safe side.
+                       */
                while (spin_is_locked(lock))
                        barrier();
                goto again;
index a1e9dc3d436e24ad60add0a7907231828ab00d03..945fabb5e2a8e1f431aea39a45cf05099d7f6305 100644 (file)
@@ -273,6 +273,10 @@ typedef __u32 kernel_cap_t;
 
 #define CAP_MKNOD            27
 
+/* Allow taking of leases on files */
+
+#define CAP_LEASE            28
+
 #ifdef __KERNEL__
 /* 
  * Bounding set
index d9dfbb44060cccd324aef48a6fd44be1bca2d219..38164e6b2bbf794b1206a20295ed185630eac36d 100644 (file)
@@ -46,14 +46,14 @@ extern unsigned int unix98_max_ptys;
 #endif
 
 #ifndef BUILDING_DEVPTS
-extern inline void
+static inline void
 devpts_pty_new(int line, kdev_t device)
 {
        if ( devpts_upcall_new )
                return devpts_upcall_new(line,device);
 }
 
-extern inline void
+static inline void
 devpts_pty_kill(int line)
 {
        if ( devpts_upcall_kill )
@@ -63,10 +63,10 @@ devpts_pty_kill(int line)
 
 #else  /* No /dev/pts filesystem at all */
 
-extern inline void
+static inline void
 devpts_pty_new(int line, kdev_t device) { }
 
-extern inline void
+static inline void
 devpts_pty_kill(int line) { }
 
 #endif
diff --git a/include/linux/dnotify.h b/include/linux/dnotify.h
new file mode 100644 (file)
index 0000000..5e23146
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Directory notification for Linux
+ *
+ * Copyright 2000 (C) Stephen Rothwell
+ */
+
+struct dnotify_struct {
+       struct dnotify_struct * dn_next;
+       int                     dn_magic;
+       unsigned long           dn_mask;        /* Events to be notified
+                                                  see linux/fcntl.h */
+       int                     dn_fd;
+       struct file *           dn_filp;
+};
+
+#define DNOTIFY_MAGIC  0x444E4F54
+
+extern void __inode_dir_notify(struct inode *, unsigned long);
+extern int fcntl_dirnotify(int, struct file *, unsigned long);
+
+static inline void inode_dir_notify(struct inode *inode, unsigned long event)
+{
+       if ((inode)->i_dnotify_mask & (event))
+               __inode_dir_notify(inode, event);
+}
index 9de3512e8fbbe2e28a1d81d7a79c81200d4c78cb..bdcf78b74c3bae2e21d1b928868e1c487309463e 100644 (file)
@@ -3,4 +3,24 @@
 
 #include <asm/fcntl.h>
 
+#define F_SETLEASE     (F_LINUX_SPECIFIC_BASE+0)
+#define F_GETLEASE     (F_LINUX_SPECIFIC_BASE+1)
+
+/*
+ * Request nofications on a directory.
+ * See below for events that may be notified.
+ */
+#define F_NOTIFY       (F_LINUX_SPECIFIC_BASE+2)
+
+/*
+ * Types of directory notifications that may be requested.
+ */
+#define DN_ACCESS      0x00000001      /* File accessed */
+#define DN_MODIFY      0x00000002      /* File modified */
+#define DN_CREATE      0x00000004      /* File created */
+#define DN_DELETE      0x00000008      /* File removed */
+#define DN_RENAME      0x00000010      /* File renamed */
+#define DN_ATTRIB      0x00000020      /* File changed attibutes */
+#define DN_MULTISHOT   0x80000000      /* Don't remove notifier */
+
 #endif
index 368bafc85a58e546fb545f45963ed4e2ed80eb44..eb224122683c43ae9c3c5805d5373e533a1d6805 100644 (file)
@@ -54,6 +54,7 @@ struct files_stat_struct {
 };
 extern struct files_stat_struct files_stat;
 extern int max_super_blocks, nr_super_blocks;
+extern int leases_enable, dir_notify_enable, lease_break_time;
 
 #define NR_FILE  8192  /* this can well be larger on a larger system */
 #define NR_RESERVED_FILES 10 /* reserved for root */
@@ -410,6 +411,9 @@ struct inode {
        struct pipe_inode_info  *i_pipe;
        struct block_device     *i_bdev;
 
+       unsigned long           i_dnotify_mask; /* Directory notify events */
+       struct dnotify_struct   *i_dnotify; /* for directory notifications */
+
        unsigned long           i_state;
 
        unsigned int            i_flags;
@@ -499,6 +503,7 @@ extern int init_private_file(struct file *, struct dentry *, int);
 #define FL_BROKEN      4       /* broken flock() emulation */
 #define FL_ACCESS      8       /* for processes suspended by mandatory locking */
 #define FL_LOCKD       16      /* lock held by rpc.lockd */
+#define FL_LEASE       32      /* lease held on this file */
 
 /*
  * The POSIX file lock owner is determined by
@@ -513,6 +518,7 @@ struct file_lock {
        struct file_lock *fl_next;      /* singly linked list for this inode  */
        struct list_head fl_link;       /* doubly linked list of all locks */
        struct list_head fl_block; /* circular list of blocked processes */
+       struct list_head fl_list;       /* block list member */
        fl_owner_t fl_owner;
        unsigned int fl_pid;
        wait_queue_head_t fl_wait;
@@ -526,6 +532,8 @@ struct file_lock {
        void (*fl_insert)(struct file_lock *);  /* lock insertion callback */
        void (*fl_remove)(struct file_lock *);  /* lock removal callback */
 
+       struct fasync_struct *  fl_fasync; /* for lease break notifications */
+
        union {
                struct nfs_lock_info    nfs_fl;
        } fl_u;
@@ -539,6 +547,7 @@ struct file_lock {
 #endif
 
 extern struct list_head file_lock_list;
+extern struct semaphore file_lock_sem;
 
 #include <linux/fcntl.h>
 
@@ -549,12 +558,18 @@ extern int fcntl_getlk64(unsigned int, struct flock64 *);
 extern int fcntl_setlk64(unsigned int, unsigned int, struct flock64 *);
 
 /* fs/locks.c */
+extern void locks_init_lock(struct file_lock *);
+extern void locks_copy_lock(struct file_lock *, struct file_lock *);
 extern void locks_remove_posix(struct file *, fl_owner_t);
 extern void locks_remove_flock(struct file *);
 extern struct file_lock *posix_test_lock(struct file *, struct file_lock *);
 extern int posix_lock_file(struct file *, struct file_lock *, unsigned int);
 extern void posix_block_lock(struct file_lock *, struct file_lock *);
 extern void posix_unblock_lock(struct file_lock *);
+extern int __get_lease(struct inode *inode, unsigned int flags);
+extern time_t lease_get_mtime(struct inode *);
+extern int lock_may_read(struct inode *, loff_t start, unsigned long count);
+extern int lock_may_write(struct inode *, loff_t start, unsigned long count);
 
 struct fasync_struct {
        int     magic;
@@ -887,6 +902,12 @@ static inline int locks_verify_truncate(struct inode *inode,
        return 0;
 }
 
+extern inline int get_lease(struct inode *inode, unsigned int mode)
+{
+       if (inode->i_flock && (inode->i_flock->fl_flags & FL_LEASE))
+               return __get_lease(inode, mode);
+       return 0;
+}
 
 /* fs/open.c */
 
index 972dd0bfb727fa9e7c8218d5885a485405c4f625..457521681ab1c94b1229b36ffe31a7e99ff3199c 100644 (file)
@@ -29,6 +29,7 @@
  * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
  */
 
+#include <linux/sched.h>
 #include <asm/io.h>
 
 struct gameport;
index 10f3f71ee6c043b5236452fcad20be9dbb3dbe3d..2a55e6018b7ffd463577d7a8e12056f969c35d71 100644 (file)
@@ -132,7 +132,7 @@ struct hdlcdrv_bitbuffer {
        unsigned char buffer[HDLCDRV_BITBUFFER];
 };
 
-extern inline void hdlcdrv_add_bitbuffer(struct hdlcdrv_bitbuffer *buf, 
+static inline void hdlcdrv_add_bitbuffer(struct hdlcdrv_bitbuffer *buf, 
                                         unsigned int bit)
 {
        unsigned char new;
@@ -147,7 +147,7 @@ extern inline void hdlcdrv_add_bitbuffer(struct hdlcdrv_bitbuffer *buf,
        }
 }
 
-extern inline void hdlcdrv_add_bitbuffer_word(struct hdlcdrv_bitbuffer *buf, 
+static inline void hdlcdrv_add_bitbuffer_word(struct hdlcdrv_bitbuffer *buf, 
                                              unsigned int bits)
 {
        buf->buffer[buf->wr] = bits & 0xff;
@@ -250,7 +250,7 @@ struct hdlcdrv_state {
 
 /* -------------------------------------------------------------------- */
 
-extern inline int hdlcdrv_hbuf_full(struct hdlcdrv_hdlcbuffer *hb) 
+static inline int hdlcdrv_hbuf_full(struct hdlcdrv_hdlcbuffer *hb) 
 {
        unsigned long flags;
        int ret;
@@ -263,7 +263,7 @@ extern inline int hdlcdrv_hbuf_full(struct hdlcdrv_hdlcbuffer *hb)
 
 /* -------------------------------------------------------------------- */
 
-extern inline int hdlcdrv_hbuf_empty(struct hdlcdrv_hdlcbuffer *hb)
+static inline int hdlcdrv_hbuf_empty(struct hdlcdrv_hdlcbuffer *hb)
 {
        unsigned long flags;
        int ret;
@@ -276,7 +276,7 @@ extern inline int hdlcdrv_hbuf_empty(struct hdlcdrv_hdlcbuffer *hb)
 
 /* -------------------------------------------------------------------- */
 
-extern inline unsigned short hdlcdrv_hbuf_get(struct hdlcdrv_hdlcbuffer *hb)
+static inline unsigned short hdlcdrv_hbuf_get(struct hdlcdrv_hdlcbuffer *hb)
 {
        unsigned long flags;
        unsigned short val;
@@ -296,7 +296,7 @@ extern inline unsigned short hdlcdrv_hbuf_get(struct hdlcdrv_hdlcbuffer *hb)
 
 /* -------------------------------------------------------------------- */
 
-extern inline void hdlcdrv_hbuf_put(struct hdlcdrv_hdlcbuffer *hb, 
+static inline void hdlcdrv_hbuf_put(struct hdlcdrv_hdlcbuffer *hb, 
                                    unsigned short val)
 {
        unsigned newp;
@@ -313,12 +313,12 @@ extern inline void hdlcdrv_hbuf_put(struct hdlcdrv_hdlcbuffer *hb,
 
 /* -------------------------------------------------------------------- */
 
-extern inline void hdlcdrv_putbits(struct hdlcdrv_state *s, unsigned int bits)
+static inline void hdlcdrv_putbits(struct hdlcdrv_state *s, unsigned int bits)
 {
        hdlcdrv_hbuf_put(&s->hdlcrx.hbuf, bits);
 }
 
-extern inline unsigned int hdlcdrv_getbits(struct hdlcdrv_state *s)
+static inline unsigned int hdlcdrv_getbits(struct hdlcdrv_state *s)
 {
        unsigned int ret;
 
@@ -336,19 +336,19 @@ extern inline unsigned int hdlcdrv_getbits(struct hdlcdrv_state *s)
        return ret;
 }
 
-extern inline void hdlcdrv_channelbit(struct hdlcdrv_state *s, unsigned int bit)
+static inline void hdlcdrv_channelbit(struct hdlcdrv_state *s, unsigned int bit)
 {
 #ifdef HDLCDRV_DEBUG
        hdlcdrv_add_bitbuffer(&s->bitbuf_channel, bit);
 #endif /* HDLCDRV_DEBUG */
 }
 
-extern inline void hdlcdrv_setdcd(struct hdlcdrv_state *s, int dcd)
+static inline void hdlcdrv_setdcd(struct hdlcdrv_state *s, int dcd)
 {
        s->hdlcrx.dcd = !!dcd;
 }
 
-extern inline int hdlcdrv_ptt(struct hdlcdrv_state *s)
+static inline int hdlcdrv_ptt(struct hdlcdrv_state *s)
 {
        return s->hdlctx.ptt || (s->hdlctx.calibrate > 0);
 }
index 65392fc39d8aaec5473d2017fbe111853015f36d..0f619399e39505c518dc5b91415dc824c85dcce2 100644 (file)
@@ -320,12 +320,12 @@ extern void hfs_tolower(unsigned char *, int);
 #define        HFS_I(X)        (&((X)->u.hfs_i))
 #define        HFS_SB(X)       (&((X)->u.hfs_sb))
 
-extern __inline__ void hfs_nameout(struct inode *dir, struct hfs_name *out,
+static inline void hfs_nameout(struct inode *dir, struct hfs_name *out,
                                   const char *in, int len) {
        HFS_SB(dir->i_sb)->s_nameout(out, in, len);
 }
 
-extern __inline__ int hfs_namein(struct inode *dir, char *out,
+static inline int hfs_namein(struct inode *dir, char *out,
                                 const struct hfs_name *in) {
        int len = HFS_SB(dir->i_sb)->s_namein(out, in);
        if (HFS_SB(dir->i_sb)->s_lowercase) {
index a05e430146f55894f409a462ed2ce720c6c2845d..cefce233dc7968768dd0bde90877beae950dde03 100644 (file)
@@ -52,7 +52,7 @@ typedef unsigned char hfs_lword_t[4];
 extern long int hfs_alloc;
 #endif
 
-extern inline void *hfs_malloc(unsigned int size) {
+static inline void *hfs_malloc(unsigned int size) {
 #if defined(DEBUG_ALL) || defined(DEBUG_MEM)
        hfs_warn("%ld bytes allocation at %s:%u\n",
                 (hfs_alloc += size), __FILE__, __LINE__);
@@ -60,7 +60,7 @@ extern inline void *hfs_malloc(unsigned int size) {
        return kmalloc(size, GFP_KERNEL);
 }
 
-extern inline void hfs_free(void *ptr, unsigned int size) {
+static inline void hfs_free(void *ptr, unsigned int size) {
        kfree(ptr);
 #if defined(DEBUG_ALL) || defined(DEBUG_MEM)
        hfs_warn("%ld bytes allocation at %s:%u\n",
@@ -75,17 +75,17 @@ extern inline void hfs_free(void *ptr, unsigned int size) {
  *       not a good thing to do. instead, we depend upon tz_minuteswest
  *       having the correct daylight savings correction. 
  */
-extern inline hfs_u32 hfs_from_utc(hfs_s32 time)
+static inline hfs_u32 hfs_from_utc(hfs_s32 time)
 {
        return time - sys_tz.tz_minuteswest*60; 
 }
 
-extern inline hfs_s32 hfs_to_utc(hfs_u32 time)
+static inline hfs_s32 hfs_to_utc(hfs_u32 time)
 {
        return time + sys_tz.tz_minuteswest*60;
 }
 
-extern inline hfs_u32 hfs_time(void) {
+static inline hfs_u32 hfs_time(void) {
        return htonl(hfs_from_utc(CURRENT_TIME)+2082844800U);
 }
 
@@ -95,19 +95,19 @@ extern inline hfs_u32 hfs_time(void) {
  */
 typedef wait_queue_head_t hfs_wait_queue;
 
-extern inline void hfs_init_waitqueue(hfs_wait_queue *queue) {
+static inline void hfs_init_waitqueue(hfs_wait_queue *queue) {
         init_waitqueue_head(queue);
 }
 
-extern inline void hfs_sleep_on(hfs_wait_queue *queue) {
+static inline void hfs_sleep_on(hfs_wait_queue *queue) {
        sleep_on(queue);
 }
 
-extern inline void hfs_wake_up(hfs_wait_queue *queue) {
+static inline void hfs_wake_up(hfs_wait_queue *queue) {
        wake_up(queue);
 }
 
-extern inline void hfs_relinquish(void) {
+static inline void hfs_relinquish(void) {
        schedule();
 }
 
@@ -117,11 +117,11 @@ extern inline void hfs_relinquish(void) {
  */
 typedef struct super_block *hfs_sysmdb;
 
-extern inline void hfs_mdb_dirty(hfs_sysmdb sys_mdb) {
+static inline void hfs_mdb_dirty(hfs_sysmdb sys_mdb) {
        sys_mdb->s_dirt = 1;
 }
 
-extern inline const char *hfs_mdb_name(hfs_sysmdb sys_mdb) {
+static inline const char *hfs_mdb_name(hfs_sysmdb sys_mdb) {
        return kdevname(sys_mdb->s_dev);
 }
 
@@ -141,19 +141,19 @@ typedef struct buffer_head *hfs_buffer;
 /* In sysdep.c, since it needs HFS_SECTOR_SIZE */
 extern hfs_buffer hfs_buffer_get(hfs_sysmdb, int, int);
 
-extern inline int hfs_buffer_ok(hfs_buffer buffer) {
+static inline int hfs_buffer_ok(hfs_buffer buffer) {
        return (buffer != NULL);
 }
 
-extern inline void hfs_buffer_put(hfs_buffer buffer) {
+static inline void hfs_buffer_put(hfs_buffer buffer) {
        brelse(buffer);
 }
 
-extern inline void hfs_buffer_dirty(hfs_buffer buffer) {
+static inline void hfs_buffer_dirty(hfs_buffer buffer) {
        mark_buffer_dirty(buffer);
 }
 
-extern inline void hfs_buffer_sync(hfs_buffer buffer) {
+static inline void hfs_buffer_sync(hfs_buffer buffer) {
        while (buffer_locked(buffer)) {
                wait_on_buffer(buffer);
        }
@@ -163,7 +163,7 @@ extern inline void hfs_buffer_sync(hfs_buffer buffer) {
        }
 }
 
-extern inline void *hfs_buffer_data(const hfs_buffer buffer) {
+static inline void *hfs_buffer_data(const hfs_buffer buffer) {
        return buffer->b_data;
 }
 
@@ -199,15 +199,15 @@ extern inline void *hfs_buffer_data(const hfs_buffer buffer) {
 #      error "Don't know if bytes are big- or little-endian!"
 #endif
 
-extern inline int hfs_clear_bit(int bitnr, hfs_u32 *lword) {
+static inline int hfs_clear_bit(int bitnr, hfs_u32 *lword) {
        return test_and_clear_bit(BITNR(bitnr), lword);
 }
 
-extern inline int hfs_set_bit(int bitnr, hfs_u32 *lword) {
+static inline int hfs_set_bit(int bitnr, hfs_u32 *lword) {
        return test_and_set_bit(BITNR(bitnr), lword);
 }
 
-extern inline int hfs_test_bit(int bitnr, const hfs_u32 *lword) {
+static inline int hfs_test_bit(int bitnr, const hfs_u32 *lword) {
        /* the kernel should declare the second arg of test_bit as const */
        return test_bit(BITNR(bitnr), (void *)lword);
 }
index a804348231e2c55710224b0cf0a13f03287dc60f..184407e40ca22a7258ce55c95a05111d6a032c41 100644 (file)
@@ -19,7 +19,7 @@ extern struct buffer_head * create_bounce(int rw, struct buffer_head * bh_orig);
 
 #else /* CONFIG_HIGHMEM */
 
-extern inline unsigned int nr_free_highpages(void) { return 0; }
+static inline unsigned int nr_free_highpages(void) { return 0; }
 #define prepare_highmem_swapout(page) page
 #define replace_with_highmem(page) page
 
index 0129c67b9ecface56f8c75f074af96f5ee4f8d41..c8fa6b1996972af4a362e652cdc514cbb5b43d32 100644 (file)
@@ -130,7 +130,7 @@ enum {
 
 extern struct ppp_channel_ops pppoe_chan_ops;
 
-extern void  pppox_proto_init(struct net_proto *np);
+extern int pppox_proto_init(struct net_proto *np);
 
 #endif /* __KERNEL__ */
 
index ac6f029b99b529b35bd2d4356dcb76fe12a1f212..aa337fe6c3aa598cb551390859b63cfb1d50bd1e 100644 (file)
@@ -9,6 +9,7 @@
 
 #include <stdarg.h>
 #include <linux/linkage.h>
+#include <linux/stddef.h>
 
 /* Optimization barrier */
 /* The "volatile" is due to gcc bugs */
index 2094a4d19f7938ed95fd132f5d4c3bfe663ef077..9d342e6694041a8b63e942a9959a0a3f5cbc43b0 100644 (file)
@@ -29,7 +29,9 @@ extern inline void lock_buffer(struct buffer_head * bh)
 extern inline void unlock_buffer(struct buffer_head *bh)
 {
        clear_bit(BH_Lock, &bh->b_state);
-       wake_up(&bh->b_wait);
+       smp_mb__after_clear_bit();
+       if (waitqueue_active(&bh->b_wait))
+               wake_up(&bh->b_wait);
 }
 
 /*
@@ -55,7 +57,12 @@ extern inline void lock_super(struct super_block * sb)
 extern inline void unlock_super(struct super_block * sb)
 {
        sb->s_lock = 0;
-       wake_up(&sb->s_wait);
+       /*
+        * No need of any barrier, we're protected by
+        * the big kernel lock here... unfortunately :)
+        */
+       if (waitqueue_active(&sb->s_wait))
+               wake_up(&sb->s_wait);
 }
 
 #endif /* _LINUX_LOCKS_H */
index 89e06bb8a6fe0b6959f92364a0ef08dc01e2affe..1a3f0ddb5d52c8143600aab219fe536360351e1f 100644 (file)
 #define USB_ACM_AUX_MAJOR      167
 #define USB_CHAR_MAJOR         180
 
-#define TUN_MAJOR               195 
-
 #define UNIX98_PTY_MASTER_MAJOR        128
 #define UNIX98_PTY_MAJOR_COUNT 8
 #define UNIX98_PTY_SLAVE_MAJOR (UNIX98_PTY_MASTER_MAJOR+UNIX98_PTY_MAJOR_COUNT)
index f2bc32f72974b0965fe1675860ca81bda3e5e2dc..3532658c68d218e3518393372e23279f834e0cb6 100644 (file)
@@ -31,6 +31,8 @@
 /* drivers/sgi/char/usema.c */
 #define SGI_USEMACLONE      151
 
+#define TUN_MINOR           200
+
 extern int misc_init(void);
 
 struct miscdevice 
index 7612b0935ebd3a4a44252a69e6fc01d0e7114ffc..1890fd1f7095fcfbcc8dcc0dc0c607b033a8770f 100644 (file)
@@ -193,9 +193,18 @@ typedef struct page {
 #define PageLocked(page)       test_bit(PG_locked, &(page)->flags)
 #define LockPage(page)         set_bit(PG_locked, &(page)->flags)
 #define TryLockPage(page)      test_and_set_bit(PG_locked, &(page)->flags)
+/*
+ * The first mb is necessary to safely close the critical section opened by the
+ * TryLockPage(), the second mb is necessary to enforce ordering between
+ * the clear_bit and the read of the waitqueue (to avoid SMP races with a
+ * parallel wait_on_page).
+ */
 #define UnlockPage(page)       do { \
+                                       smp_mb__before_clear_bit(); \
                                        clear_bit(PG_locked, &(page)->flags); \
-                                       wake_up(&page->wait); \
+                                       smp_mb__after_clear_bit(); \
+                                       if (waitqueue_active(&page->wait)) \
+                                               wake_up(&page->wait); \
                                } while (0)
 #define PageError(page)                test_bit(PG_error, &(page)->flags)
 #define SetPageError(page)     set_bit(PG_error, &(page)->flags)
index 5fb55c73832344892eb8b5ed182ebf43f3a5594d..db948a701db777bf6d5d0b32b1a8cf65f3f23d0b 100644 (file)
@@ -38,7 +38,8 @@
 #define NFSEXP_CROSSMNT                0x0200
 #define NFSEXP_NOSUBTREECHECK  0x0400
 #define        NFSEXP_NOAUTHNLM        0x0800          /* Don't authenticate NLM requests - just trust */
-#define NFSEXP_ALLFLAGS                0x0FFF
+#define NFSEXP_MSNFS           0x1000  /* do silly things that MS clients expect */
+#define NFSEXP_ALLFLAGS                0x1FFF
 
 
 #ifdef __KERNEL__
index 5406f07d66c365388caebabe041413903a9dd50d..0c2dc46aeaf5662ce8c4bac7fdff8d70fef079d8 100644 (file)
 #define DEVICE_COUNT_DMA       2
 #define DEVICE_COUNT_RESOURCE  12
 
+#define PCI_ANY_ID (~0)
+
+#define pci_present pcibios_present
+
+#define pci_for_each_dev(dev) \
+       for(dev = pci_dev_g(pci_devices.next); dev != pci_dev_g(&pci_devices); dev = pci_dev_g(dev->global_list.next))
+
+#define pci_for_each_dev_reverse(dev) \
+       for(dev = pci_dev_g(pci_devices.prev); dev != pci_dev_g(&pci_devices); dev = pci_dev_g(dev->global_list.prev))
+
 /*
  * The pci_dev structure is used to describe both PCI and ISAPnP devices.
  */
@@ -429,6 +439,27 @@ struct pbus_set_ranges_data
        unsigned long mem_start, mem_end;
 };
 
+struct pci_device_id {
+       unsigned int vendor, device;            /* Vendor and device ID or PCI_ANY_ID */
+       unsigned int subvendor, subdevice;      /* Subsystem ID's or PCI_ANY_ID */
+       unsigned int class, class_mask;         /* (class,subclass,prog-if) triplet */
+       unsigned long driver_data;              /* Data private to the driver */
+};
+
+struct pci_driver {
+       struct list_head node;
+       char *name;
+       const struct pci_device_id *id_table;   /* NULL if wants all devices */
+       int (*probe)(struct pci_dev *dev, const struct pci_device_id *id);      /* New device inserted */
+       void (*remove)(struct pci_dev *dev);    /* Device removed (NULL if not a hot-plug capable driver) */
+       void (*suspend)(struct pci_dev *dev);   /* Device suspended */
+       void (*resume)(struct pci_dev *dev);    /* Device woken up */
+};
+
+
+/* these external functions are only available when PCI support is enabled */
+#ifdef CONFIG_PCI
+
 void pcibios_init(void);
 void pcibios_fixup_bus(struct pci_bus *);
 int pcibios_enable_device(struct pci_dev *);
@@ -444,7 +475,6 @@ void pcibios_fixup_pbus_ranges(struct pci_bus *, struct pbus_set_ranges_data *);
 /* Backward compatibility, don't use in new code! */
 
 int pcibios_present(void);
-#define pci_present pcibios_present
 int pcibios_read_config_byte (unsigned char bus, unsigned char dev_fn,
                              unsigned char where, unsigned char *val);
 int pcibios_read_config_word (unsigned char bus, unsigned char dev_fn,
@@ -488,8 +518,6 @@ struct pci_dev *pci_find_class (unsigned int class, const struct pci_dev *from);
 struct pci_dev *pci_find_slot (unsigned int bus, unsigned int devfn);
 int pci_find_capability (struct pci_dev *dev, int cap);
 
-#define PCI_ANY_ID (~0)
-
 int pci_read_config_byte(struct pci_dev *dev, int where, u8 *val);
 int pci_read_config_word(struct pci_dev *dev, int where, u16 *val);
 int pci_read_config_dword(struct pci_dev *dev, int where, u32 *val);
@@ -502,12 +530,6 @@ void pci_set_master(struct pci_dev *dev);
 int pci_set_power_state(struct pci_dev *dev, int state);
 int pci_assign_resource(struct pci_dev *dev, int i);
 
-#define pci_for_each_dev(dev) \
-       for(dev = pci_dev_g(pci_devices.next); dev != pci_dev_g(&pci_devices); dev = pci_dev_g(dev->global_list.next))
-
-#define pci_for_each_dev_reverse(dev) \
-       for(dev = pci_dev_g(pci_devices.prev); dev != pci_dev_g(&pci_devices); dev = pci_dev_g(dev->global_list.prev))
-
 /* Helper functions for low-level code (drivers/pci/setup.c) */
 
 int pci_claim_resource(struct pci_dev *, int);
@@ -518,24 +540,6 @@ void pci_fixup_irqs(u8 (*)(struct pci_dev *, u8 *),
                    int (*)(struct pci_dev *, u8, u8));
 
 /* New-style probing supporting hot-pluggable devices */
-
-struct pci_device_id {
-       unsigned int vendor, device;            /* Vendor and device ID or PCI_ANY_ID */
-       unsigned int subvendor, subdevice;      /* Subsystem ID's or PCI_ANY_ID */
-       unsigned int class, class_mask;         /* (class,subclass,prog-if) triplet */
-       unsigned long driver_data;              /* Data private to the driver */
-};
-
-struct pci_driver {
-       struct list_head node;
-       char *name;
-       const struct pci_device_id *id_table;   /* NULL if wants all devices */
-       int (*probe)(struct pci_dev *dev, const struct pci_device_id *id);      /* New device inserted */
-       void (*remove)(struct pci_dev *dev);    /* Device removed (NULL if not a hot-plug capable driver) */
-       void (*suspend)(struct pci_dev *dev);   /* Device suspended */
-       void (*resume)(struct pci_dev *dev);    /* Device woken up */
-};
-
 int pci_register_driver(struct pci_driver *);
 void pci_unregister_driver(struct pci_driver *);
 void pci_insert_device(struct pci_dev *, struct pci_bus *);
@@ -543,6 +547,8 @@ void pci_remove_device(struct pci_dev *);
 struct pci_driver *pci_dev_driver(const struct pci_dev *);
 const struct pci_device_id *pci_match_device(const struct pci_device_id *ids, const struct pci_dev *dev);
 
+#endif /* CONFIG_PCI */
+
 /* Include architecture-dependent settings and functions */
 
 #include <asm/pci.h>
index cff4c4fffadb533cd0c16245f1c7a067570c269d..d3a203d806b48bb793352ea07f93976fd58138ff 100644 (file)
 #define PCI_DEVICE_ID_SERVERWORKS_CMIC_HE  0x0011
 #define PCI_DEVICE_ID_SERVERWORKS_CSB5     0x0201
 
+#define PCI_VENDOR_ID_SBE              0x1176
+#define PCI_DEVICE_ID_SBE_WANXL100     0x0301
+#define PCI_DEVICE_ID_SBE_WANXL200     0x0302
+#define PCI_DEVICE_ID_SBE_WANXL400     0x0104
+
 #define PCI_VENDOR_ID_TOSHIBA          0x1179
 #define PCI_DEVICE_ID_TOSHIBA_601      0x0601
 #define PCI_DEVICE_ID_TOSHIBA_TOPIC95  0x060a
 #define PCI_DEVICE_ID_CYCLOM_8Y_Hi     0x0105
 #define PCI_DEVICE_ID_CYCLOM_Z_Lo      0x0200
 #define PCI_DEVICE_ID_CYCLOM_Z_Hi      0x0201
+#define PCI_DEVICE_ID_PC300_RX_2       0x0300
+#define PCI_DEVICE_ID_PC300_RX_1       0x0301
+#define PCI_DEVICE_ID_PC300_TE_2       0x0310
+#define PCI_DEVICE_ID_PC300_TE_1       0x0311
 
 #define PCI_VENDOR_ID_ESSENTIAL                0x120f
 #define PCI_DEVICE_ID_ESSENTIAL_ROADRUNNER     0x0001
index e52d024a7450cbbfdc5d64308219034eabda51bc..6b7811a7707f5248e53f1faa20ca7b9fe5009a9b 100644 (file)
@@ -298,6 +298,7 @@ struct task_struct {
         * that's just fine.)
         */
        struct list_head run_list;
+       unsigned long sleep_time;
 
        struct task_struct *next_task, *prev_task;
        struct mm_struct *active_mm;
@@ -356,6 +357,7 @@ struct task_struct {
 /* file system info */
        int link_count;
        struct tty_struct *tty; /* NULL if no tty */
+       unsigned int locks; /* How many file locks are being held */
 /* ipc stuff */
        struct sem_undo *semundo;
        struct sem_queue *semsleeping;
@@ -818,6 +820,7 @@ do {                                                                        \
 static inline void del_from_runqueue(struct task_struct * p)
 {
        nr_running--;
+       p->sleep_time = jiffies;
        list_del(&p->run_list);
        p->run_list.next = NULL;
 }
index 124d0ba4535b4cdd67e6e2cf544a2c5e86bff3b8..6bfff47a8db9d996db5d46aca25d8eae8d4ef1c9 100644 (file)
@@ -67,7 +67,6 @@ extern int nr_swap_pages;
 FASTCALL(unsigned int nr_free_pages(void));
 FASTCALL(unsigned int nr_inactive_clean_pages(void));
 FASTCALL(unsigned int nr_free_buffer_pages(void));
-FASTCALL(unsigned int nr_free_highpages(void));
 extern int nr_active_pages;
 extern int nr_inactive_dirty_pages;
 extern atomic_t nr_async_pages;
index 7c60862c390ad116d3532c1b9e59ae85ffc46ee8..c15bd0e5511966baf97018e23e5baf5507762b5a 100644 (file)
@@ -186,7 +186,8 @@ enum
        NET_CORE_MSG_COST=8,
        NET_CORE_MSG_BURST=9,
        NET_CORE_OPTMEM_MAX=10,
-       NET_CORE_HOT_LIST_LENGTH=11
+       NET_CORE_HOT_LIST_LENGTH=11,
+       NET_CORE_DIVERT_VERSION=12
 };
 
 /* /proc/sys/net/ethernet */
@@ -512,7 +513,10 @@ enum
        FS_NRSUPER=9,   /* int:current number of allocated super_blocks */
        FS_MAXSUPER=10, /* int:maximum number of super_blocks that can be allocated */
        FS_OVERFLOWUID=11,      /* int: overflow UID */
-       FS_OVERFLOWGID=12       /* int: overflow GID */
+       FS_OVERFLOWGID=12,      /* int: overflow GID */
+       FS_LEASES=13,   /* int: leases enabled */
+       FS_DIR_NOTIFY=14,       /* int: directory notification enabled */
+       FS_LEASE_TIME=15,       /* int: maximum time to wait for a lease break */
 };
 
 /* CTL_DEBUG names: */
index 653009adf15e27f791c05a34745ba64e7e63ee16..5e8c283da482d845c12ecefaf7c6fcbcbf1fab85 100644 (file)
@@ -51,6 +51,8 @@
 #ifndef _LINUX_TIMEX_H
 #define _LINUX_TIMEX_H
 
+#include <asm/param.h>
+
 /*
  * The following defines establish the engineering parameters of the PLL
  * model. The HZ variable establishes the timer interrupt frequency, 100 Hz
  * OSF/1 kernel. The SHIFT_HZ define expresses the same value as the
  * nearest power of two in order to avoid hardware multiply operations.
  */
-#ifdef __alpha__
-# define SHIFT_HZ 10           /* log2(HZ) */
+#if HZ >= 24 && HZ < 48
+# define SHIFT_HZ      5
+#elif HZ >= 48 && HZ < 96
+# define SHIFT_HZ      6
+#elif HZ >= 96 && HZ < 192
+# define SHIFT_HZ      7
+#elif HZ >= 192 && HZ < 384
+# define SHIFT_HZ      8
+#elif HZ >= 384 && HZ < 768
+# define SHIFT_HZ      9
+#elif HZ >= 768 && HZ < 1536
+# define SHIFT_HZ      10
 #else
-# define SHIFT_HZ 7            /* log2(HZ) */
+# error You lose.
 #endif
 
 /*
index 85517135d38060f0a86c4e7d3379c79edd9aded8..3eb8f14ae22f00b8eb3a696a77dfb3d12d025243 100644 (file)
@@ -114,7 +114,7 @@ static inline void run_task_queue(task_queue *list)
                        f      = p -> routine;
                        save_p = p;
                        p      = p -> next;
-                       mb();
+                       smp_mb();
                        save_p -> sync = 0;
                        if (f)
                                (*f)(arg);
index 2bd12729935b77f7dea1f4d3d3737963f3d534b0..03b0dfa99cf586a77120fd3cb880b4ffaeea1336 100644 (file)
  *             as published by the Free Software Foundation; either version
  *             2 of the License, or (at your option) any later version.
  *
+ *             $Id: snmp.h,v 1.17 2000/09/21 01:31:50 davem Exp $
+ *
  */
  
 #ifndef _SNMP_H
 #define _SNMP_H
+
+#include <linux/cache.h>
  
 /*
  *     We use all unsigned longs. Linux will soon be so reliable that even these
  *     will rapidly get too small 8-). Seriously consider the IpInReceives count
  *     on the 20Gb/s + networks people expect in a few years time!
  */
-  
+
+/* 
+ * The rule for padding: 
+ * Best is power of two because then the right structure can be found by a simple
+ * shift. The structure should be always cache line aligned.
+ * gcc needs n=alignto(cachelinesize, popcnt(sizeof(bla_mib))) shift/add instructions
+ * to emulate multiply in case it is not power-of-two. Currently n is always <=3 for
+ * all sizes so simple cache line alignment is enough. 
+ * 
+ * The best solution would be a global CPU local area , especially on 64 and 128byte 
+ * cacheline machine it makes a *lot* of sense -AK
+ */ 
+
 struct ip_mib
 {
        unsigned long   IpInReceives;
@@ -44,8 +61,8 @@ struct ip_mib
        unsigned long   IpFragOKs;
        unsigned long   IpFragFails;
        unsigned long   IpFragCreates;
-       unsigned long   __pad[32-19];
-};
+       unsigned long   __pad[0]; 
+} ____cacheline_aligned;
  
 struct ipv6_mib
 {
@@ -71,8 +88,8 @@ struct ipv6_mib
        unsigned long   Ip6FragCreates;
        unsigned long   Ip6InMcastPkts;
        unsigned long   Ip6OutMcastPkts;
-       unsigned long   __pad[32-22];
-};
+       unsigned long   __pad[0]; 
+} ____cacheline_aligned;
  
 struct icmp_mib
 {
@@ -102,8 +119,8 @@ struct icmp_mib
        unsigned long   IcmpOutTimestampReps;
        unsigned long   IcmpOutAddrMasks;
        unsigned long   IcmpOutAddrMaskReps;
-       unsigned long   __pad[32-26];
-};
+       unsigned long   __pad[0]; 
+} ____cacheline_aligned;
 
 struct icmpv6_mib
 {
@@ -140,8 +157,8 @@ struct icmpv6_mib
        unsigned long   Icmp6OutRedirects;
        unsigned long   Icmp6OutGroupMembResponses;
        unsigned long   Icmp6OutGroupMembReductions;
-       unsigned long   __pad[32-28];
-};
+       unsigned long   __pad[0]; 
+} ____cacheline_aligned;
  
 struct tcp_mib
 {
@@ -159,8 +176,8 @@ struct tcp_mib
        unsigned long   TcpRetransSegs;
        unsigned long   TcpInErrs;
        unsigned long   TcpOutRsts;
-       unsigned long   __pad[16-14];
-};
+       unsigned long   __pad[0]; 
+} ____cacheline_aligned;
  
 struct udp_mib
 {
@@ -168,8 +185,8 @@ struct udp_mib
        unsigned long   UdpNoPorts;
        unsigned long   UdpInErrors;
        unsigned long   UdpOutDatagrams;
-       unsigned long   __pad[0];
-};
+       unsigned long   __pad[0];
+} ____cacheline_aligned; 
 
 struct linux_mib 
 {
@@ -237,9 +254,15 @@ struct linux_mib
        unsigned long   TCPAbortOnLinger;
        unsigned long   TCPAbortFailed;
        unsigned long   TCPMemoryPressures;
-       unsigned long   __pad[64-64];
-};
+       unsigned long   __pad[0];
+} ____cacheline_aligned;
+
 
+/* 
+ * FIXME: On x86 and some other CPUs the split into user and softirq parts is not needed because 
+ * addl $1,memory is atomic against interrupts (but atomic_inc would be overkill because of the lock 
+ * cycles). Wants new nonlocked_atomic_inc() primitives -AK
+ */ 
 #define SNMP_INC_STATS(mib, field) ((mib)[2*smp_processor_id()+!in_softirq()].field++)
 #define SNMP_INC_STATS_BH(mib, field) ((mib)[2*smp_processor_id()].field++)
 #define SNMP_INC_STATS_USER(mib, field) ((mib)[2*smp_processor_id()+1].field++)
index 060d5de240f992c4c17fdce388174ca0751b7059..9423ba1dbfe457ad3effb093f90033cbf314ceaf 100644 (file)
@@ -11,9 +11,13 @@ Original driver (sg.h):
 Version 2 and 3 extensions to driver:
 *       Copyright (C) 1998 - 2000 Douglas Gilbert
 
-    Version: 3.1.16 (20000716)
-    This version is for 2.3/2.4 series kernels.
+    Version: 3.1.17 (20000921)
+    This version is for 2.4 series kernels.
 
+    Changes since 3.1.16 (20000716)
+       - changes for new scsi subsystem initialization
+       - change Scsi_Cmnd usage to Scsi_Request
+       - cleanup for no procfs
     Changes since 3.1.15 (20000528)
        - further (scatter gather) buffer length changes
     Changes since 3.1.14 (20000503)
index ab9fd0876a60638ceb24b965b18e0909f41a42b2..91b58e1804ed6e870e17720ba9b255b212e6ff09 100644 (file)
@@ -727,9 +727,6 @@ static void __init do_basic_setup(void)
                        while (pid != wait(&i));
                if (MAJOR(real_root_dev) != RAMDISK_MAJOR
                     || MINOR(real_root_dev) != 0) {
-#ifdef CONFIG_BLK_DEV_MD
-                       md_run_setup();
-#endif
                        error = change_root(real_root_dev,"/initrd");
                        if (error)
                                printk(KERN_ERR "Change root to /initrd: "
index b8a8e13df58caf632f493b5605a6a86107d02a9c..3d3e8dd8bedfc8e3e68e14499a3df775003d651e 100644 (file)
@@ -214,6 +214,7 @@ EXPORT_SYMBOL(generic_buffer_fdatasync);
 EXPORT_SYMBOL(page_hash_bits);
 EXPORT_SYMBOL(page_hash_table);
 EXPORT_SYMBOL(file_lock_list);
+EXPORT_SYMBOL(file_lock_sem);
 EXPORT_SYMBOL(posix_lock_file);
 EXPORT_SYMBOL(posix_test_lock);
 EXPORT_SYMBOL(posix_block_lock);
@@ -253,6 +254,10 @@ EXPORT_SYMBOL(page_follow_link);
 EXPORT_SYMBOL(page_symlink_inode_operations);
 EXPORT_SYMBOL(block_symlink);
 EXPORT_SYMBOL(vfs_readdir);
+EXPORT_SYMBOL(__get_lease);
+EXPORT_SYMBOL(lease_get_mtime);
+EXPORT_SYMBOL(lock_may_read);
+EXPORT_SYMBOL(lock_may_write);
 EXPORT_SYMBOL(dcache_readdir);
 
 /* for stackable file systems (lofs, wrapfs, cryptfs, etc.) */
index de6359b6492b068c2c46783b55dd58c97b88ae5a..a3105ec8e6e51a6b649ab1bf89f13f7d248799ce 100644 (file)
@@ -939,25 +939,28 @@ sys_rt_sigtimedwait(const sigset_t *uthese, siginfo_t *uinfo,
        spin_lock_irq(&current->sigmask_lock);
        sig = dequeue_signal(&these, &info);
        if (!sig) {
-               /* None ready -- temporarily unblock those we're interested
-                  in so that we'll be awakened when they arrive.  */
-               sigset_t oldblocked = current->blocked;
-               sigandsets(&current->blocked, &current->blocked, &these);
-               recalc_sigpending(current);
-               spin_unlock_irq(&current->sigmask_lock);
-
                timeout = MAX_SCHEDULE_TIMEOUT;
                if (uts)
                        timeout = (timespec_to_jiffies(&ts)
                                   + (ts.tv_sec || ts.tv_nsec));
 
-               current->state = TASK_INTERRUPTIBLE;
-               timeout = schedule_timeout(timeout);
+               if (timeout) {
+                       /* None ready -- temporarily unblock those we're
+                        * interested while we are sleeping in so that we'll
+                        * be awakened when they arrive.  */
+                       sigset_t oldblocked = current->blocked;
+                       sigandsets(&current->blocked, &current->blocked, &these);
+                       recalc_sigpending(current);
+                       spin_unlock_irq(&current->sigmask_lock);
 
-               spin_lock_irq(&current->sigmask_lock);
-               sig = dequeue_signal(&these, &info);
-               current->blocked = oldblocked;
-               recalc_sigpending(current);
+                       current->state = TASK_INTERRUPTIBLE;
+                       timeout = schedule_timeout(timeout);
+
+                       spin_lock_irq(&current->sigmask_lock);
+                       sig = dequeue_signal(&these, &info);
+                       current->blocked = oldblocked;
+                       recalc_sigpending(current);
+               }
        }
        spin_unlock_irq(&current->sigmask_lock);
 
index 12a82399da0564efd13aa5b02feb4b1339a5196f..f7be8abd3aea3ef81aa6d78a9d782d9252156575 100644 (file)
@@ -44,7 +44,7 @@
 irq_cpustat_t irq_stat[NR_CPUS];
 #endif /* CONFIG_ARCH_S390 */
 
-static struct softirq_action softirq_vec[32];
+static struct softirq_action softirq_vec[32] __cacheline_aligned;
 
 asmlinkage void do_softirq()
 {
@@ -140,6 +140,14 @@ static void tasklet_action(struct softirq_action *a)
                                clear_bit(TASKLET_STATE_SCHED, &t->state);
 
                                t->func(t->data);
+                               /*
+                                * talklet_trylock() uses test_and_set_bit that imply
+                                * an mb when it returns zero, thus we need the explicit
+                                * mb only here: while closing the critical section.
+                                */
+#ifdef CONFIG_SMP
+                               smp_mb__before_clear_bit();
+#endif
                                tasklet_unlock(t);
                                continue;
                        }
index 746374e1cbbc61f4604492ac04b147e33f3f1672..c320027fac44d931612f260a3c87ee3d7fe44d7e 100644 (file)
@@ -283,6 +283,12 @@ static ctl_table fs_table[] = {
        {FS_OVERFLOWGID, "overflowgid", &fs_overflowgid, sizeof(int), 0644, NULL,
         &proc_dointvec_minmax, &sysctl_intvec, NULL,
         &minolduid, &maxolduid},
+       {FS_LEASES, "leases-enable", &leases_enable, sizeof(int),
+        0644, NULL, &proc_dointvec},
+       {FS_DIR_NOTIFY, "dir-notify-enable", &dir_notify_enable,
+        sizeof(int), 0644, NULL, &proc_dointvec},
+       {FS_LEASE_TIME, "lease-break-time", &lease_break_time, sizeof(int),
+        0644, NULL, &proc_dointvec},
        {0}
 };
 
index 4347d9bdab825c1045708cc215c4879979c0113e..9b68883a63d695fc49ea13dc1236e7193f5c0619 100644 (file)
@@ -255,7 +255,7 @@ inside:
         * up kswapd.
         */
        age_page_up(page);
-       if (inactive_shortage() > (inactive_target * 3) / 4)
+       if (inactive_shortage() > inactive_target / 2 && free_shortage())
                        wakeup_kswapd(0);
 not_found:
        return page;
index 8ddba74e53ae84e3810ec78265da7b70603bec29..ada767e0302c7b9900c71ed60e35a6f535e32552 100644 (file)
@@ -444,7 +444,8 @@ try_again:
                 * processes, etc).
                 */
                if (gfp_mask & __GFP_WAIT) {
-                       wakeup_kswapd(1);
+                       try_to_free_pages(gfp_mask);
+                       memory_pressure++;
                        goto try_again;
                }
        }
index 86ca1843f267a699160395a7c27c8f81d14f715c..dbbd218f82810b1fd3d06f81001f1f466e8efe9f 100644 (file)
--- a/mm/swap.c
+++ b/mm/swap.c
@@ -233,27 +233,11 @@ void lru_cache_add(struct page * page)
        spin_lock(&pagemap_lru_lock);
        if (!PageLocked(page))
                BUG();
-       /*
-        * Heisenbug Compensator(tm)
-        * This bug shouldn't trigger, but for unknown reasons it
-        * sometimes does. If there are no signs of list corruption,
-        * we ignore the problem. Else we BUG()...
-        */
-       if (PageActive(page) || PageInactiveDirty(page) ||
-                                       PageInactiveClean(page)) {
-               struct list_head * page_lru = &page->lru;
-               if (page_lru->next->prev != page_lru) {
-                       printk("VM: lru_cache_add, bit or list corruption..\n");
-                       BUG();
-               }
-               printk("VM: lru_cache_add, page already in list!\n");
-               goto page_already_on_list;
-       }
+       DEBUG_ADD_PAGE
        add_page_to_active_list(page);
        /* This should be relatively rare */
        if (!page->age)
                deactivate_page_nolock(page);
-page_already_on_list:
        spin_unlock(&pagemap_lru_lock);
 }
 
index d562af48531ef0cbb88de898e59790324c7e3087..b11d8a1f392e373f54a2a94607bee1014e0d2537 100644 (file)
@@ -377,7 +377,7 @@ out_unlock:
 #define SWAP_SHIFT 5
 #define SWAP_MIN 8
 
-static int swap_out(unsigned int priority, int gfp_mask)
+static int swap_out(unsigned int priority, int gfp_mask, unsigned long idle_time)
 {
        struct task_struct * p;
        int counter;
@@ -407,6 +407,7 @@ static int swap_out(unsigned int priority, int gfp_mask)
                struct mm_struct *best = NULL;
                int pid = 0;
                int assign = 0;
+               int found_task = 0;
        select:
                read_lock(&tasklist_lock);
                p = init_task.next_task;
@@ -416,6 +417,11 @@ static int swap_out(unsigned int priority, int gfp_mask)
                                continue;
                        if (mm->rss <= 0)
                                continue;
+                       /* Skip tasks which haven't slept long enough yet when idle-swapping. */
+                       if (idle_time && !assign && (!(p->state & TASK_INTERRUPTIBLE) ||
+                                       time_before(p->sleep_time + idle_time * HZ, jiffies)))
+                               continue;
+                       found_task++;
                        /* Refresh swap_cnt? */
                        if (assign == 1) {
                                mm->swap_cnt = (mm->rss >> SWAP_SHIFT);
@@ -430,7 +436,7 @@ static int swap_out(unsigned int priority, int gfp_mask)
                }
                read_unlock(&tasklist_lock);
                if (!best) {
-                       if (!assign) {
+                       if (!assign && found_task > 0) {
                                assign = 1;
                                goto select;
                        }
@@ -691,9 +697,9 @@ dirty_page_rescan:
                         * Now the page is really freeable, so we
                         * move it to the inactive_clean list.
                         */
-                       UnlockPage(page);
                        del_page_from_inactive_dirty_list(page);
                        add_page_to_inactive_clean_list(page);
+                       UnlockPage(page);
                        cleaned_pages++;
                } else {
                        /*
@@ -701,9 +707,9 @@ dirty_page_rescan:
                         * It's no use keeping it here, so we move it to
                         * the active list.
                         */
-                       UnlockPage(page);
                        del_page_from_inactive_dirty_list(page);
                        add_page_to_active_list(page);
+                       UnlockPage(page);
                }
        }
        spin_unlock(&pagemap_lru_lock);
@@ -860,6 +866,7 @@ int inactive_shortage(void)
 static int refill_inactive(unsigned int gfp_mask, int user)
 {
        int priority, count, start_count, made_progress;
+       unsigned long idle_time;
 
        count = inactive_shortage() + free_shortage();
        if (user)
@@ -869,16 +876,28 @@ static int refill_inactive(unsigned int gfp_mask, int user)
        /* Always trim SLAB caches when memory gets low. */
        kmem_cache_reap(gfp_mask);
 
+       /*
+        * Calculate the minimum time (in seconds) a process must
+        * have slept before we consider it for idle swapping.
+        * This must be the number of seconds it takes to go through
+        * all of the cache. Doing this idle swapping makes the VM
+        * smoother once we start hitting swap.
+        */
+       idle_time = atomic_read(&page_cache_size);
+       idle_time += atomic_read(&buffermem_pages);
+       idle_time /= (inactive_target + 1);
+
        priority = 6;
        do {
                made_progress = 0;
 
-               if (current->need_resched) {
+               if (current->need_resched && (gfp_mask & __GFP_IO)) {
                        __set_current_state(TASK_RUNNING);
                        schedule();
                }
 
-               while (refill_inactive_scan(priority, 1)) {
+               while (refill_inactive_scan(priority, 1) ||
+                               swap_out(priority, gfp_mask, idle_time)) {
                        made_progress = 1;
                        if (!--count)
                                goto done;
@@ -913,7 +932,7 @@ static int refill_inactive(unsigned int gfp_mask, int user)
                /*
                 * Then, try to page stuff out..
                 */
-               while (swap_out(priority, gfp_mask)) {
+               while (swap_out(priority, gfp_mask, 0)) {
                        made_progress = 1;
                        if (!--count)
                                goto done;
@@ -963,7 +982,8 @@ static int do_try_to_free_pages(unsigned int gfp_mask, int user)
         * before we get around to moving them to the other
         * list, so this is a relatively cheap operation.
         */
-       if (free_shortage())
+       if (free_shortage() || nr_inactive_dirty_pages > nr_free_pages() +
+                       nr_inactive_clean_pages())
                ret += page_launder(gfp_mask, user);
 
        /*
@@ -1070,9 +1090,12 @@ int kswapd(void *unused)
                run_task_queue(&tq_disk);
 
                /* 
-                * If we've either completely gotten rid of the
-                * free page shortage or the inactive page shortage
-                * is getting low, then stop eating CPU time.
+                * We go to sleep if either the free page shortage
+                * or the inactive page shortage is gone. We do this
+                * because:
+                * 1) we need no more free pages   or
+                * 2) the inactive pages need to be flushed to disk,
+                *    it wouldn't help to eat CPU time now ...
                 *
                 * We go to sleep for one second, but if it's needed
                 * we'll be woken up earlier...
index 2a801868b02b2dd2e4b80001d0098d955b117d00..e6f440cf4cfbf52871dca4bca5b49b54934b4a24 100644 (file)
@@ -1141,6 +1141,7 @@ static void net_tx_action(struct softirq_action *h)
                        struct net_device *dev = head;
                        head = head->next_sched;
 
+                       smp_mb__before_clear_bit();
                        clear_bit(__LINK_STATE_SCHED, &dev->state);
 
                        if (spin_trylock(&dev->queue_lock)) {
index 78fffa9c075c2197b77354deac01e00917af1cdd..ea9d18d974e6c0ce70a56dd3643254ac987cf313 100644 (file)
@@ -5,7 +5,7 @@
  *
  *             Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:    $Id: tcp_input.c,v 1.201 2000/09/18 05:59:48 davem Exp $
+ * Version:    $Id: tcp_input.c,v 1.202 2000/09/21 01:05:38 davem Exp $
  *
  * Authors:    Ross Biro, <bir7@leland.Stanford.Edu>
  *             Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
index 203ae1dabd8ca6db9b2813dd2766bba0f3bcb0cf..e10b3a446d71492030d29b894f1113eafbc9b8db 100644 (file)
@@ -5,7 +5,7 @@
  *
  *             The User Datagram Protocol (UDP).
  *
- * Version:    $Id: udp.c,v 1.86 2000/09/18 05:59:48 davem Exp $
+ * Version:    $Id: udp.c,v 1.87 2000/09/20 02:11:34 davem Exp $
  *
  * Authors:    Ross Biro, <bir7@leland.Stanford.Edu>
  *             Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -494,8 +494,6 @@ int udp_sendmsg(struct sock *sk, struct msghdr *msg, int len)
                if (usin->sin_family != AF_INET) {
                        if (usin->sin_family != AF_UNSPEC)
                                return -EINVAL;
-                       if (net_ratelimit())
-                               printk("Remind Kuznetsov, he has to repair %s eventually\n", current->comm);
                }
 
                ufh.daddr = usin->sin_addr.s_addr;
@@ -679,6 +677,8 @@ int udp_recvmsg(struct sock *sk, struct msghdr *msg, int len,
        if (flags & MSG_ERRQUEUE)
                return ip_recv_error(sk, msg, len);
 
+
+ retry:
        /*
         *      From here the generic datagram does a lot of the work. Come
         *      the finished NET3, it will do _ALL_ the work!
@@ -734,26 +734,21 @@ out:
 csum_copy_err:
        UDP_INC_STATS_BH(UdpInErrors);
 
-       /* Clear queue. */
-       if (flags&MSG_PEEK) {
-               int clear = 0;
+       if (flags&(MSG_PEEK|MSG_DONTWAIT)) {
+               struct sk_buff *skb2; 
+
                spin_lock_irq(&sk->receive_queue.lock);
-               if (skb == skb_peek(&sk->receive_queue)) {
+               skb2 = skb_peek(&sk->receive_queue); 
+               if ((flags & MSG_PEEK) && skb == skb2) { 
                        __skb_unlink(skb, &sk->receive_queue);
-                       clear = 1;
                }
                spin_unlock_irq(&sk->receive_queue.lock);
-               if (clear)
-                       kfree_skb(skb);
-       }
-
-       skb_free_datagram(sk, skb);
-
-       /* 
-        * Error for blocking case is chosen to masquerade
-        * as some normal condition.
-        */
-       return (flags&MSG_DONTWAIT) ? -EAGAIN : -EHOSTUNREACH;  
+               skb_free_datagram(sk, skb); 
+               if ((flags & MSG_DONTWAIT) && !skb2) 
+                       return -EAGAIN; 
+       } else 
+               skb_free_datagram(sk, skb);
+       goto retry;             
 }
 
 int udp_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
index 5691718c9a131f1ab5ae448e24b2d2f782e9e5ea..9fdc84fb510a46b4274eb13da45cbddeb3d328cd 100644 (file)
@@ -822,7 +822,7 @@ int sock_create(int family, int type, int protocol, struct socket **res)
         *      Check protocol is in range
         */
        if(family<0 || family>=NPROTO)
-               return -EINVAL;
+               return -EAFNOSUPPORT;
 
        /* Compatibility.
 
index a908812c51cb4a073771108c68d5809b36cecda1..1e74af96b20fee3d54706d73ba55e82427c3403b 100644 (file)
@@ -824,6 +824,7 @@ call_verify(struct rpc_task *task)
                case RPC_AUTH_TOOWEAK:
                        printk(KERN_NOTICE "call_verify: server requires stronger "
                               "authentication.\n");
+                       break;
                default:
                        printk(KERN_WARNING "call_verify: unknown auth error: %x\n", n);
                        error = -EIO;
index 4cc31e42be8d07eb2d2b498a7b1ba06be80920f4..56c618697a866fae32f82604b5cd298ddfa10d73 100644 (file)
@@ -570,7 +570,7 @@ echo "*** Check the top-level Makefile for additional configuration."
 if [ ! -f .hdepend -o "$CONFIG_MODVERSIONS" = "y" ] ; then
     echo "*** Next, you must run 'make dep'."
 else
-    echo "*** Next, you may run 'make zImage', 'make zdisk', or 'make zlilo'."
+    echo "*** Next, you may run 'make bzImage', 'make bzdisk', or 'make install'."
 fi
 echo