diff -u --recursive --new-file v2.1.88/linux/CREDITS linux/CREDITS
--- v2.1.88/linux/CREDITS	Mon Feb 23 18:12:01 1998
+++ linux/CREDITS	Fri Mar  6 10:03:04 1998
@@ -37,7 +37,8 @@
 E: cananian@alumni.princeton.edu
 W: http://www.pdos.lcs.mit.edu/~cananian
 P: 1024/85AD9EED AD C0 49 08 91 67 DF D7  FA 04 1A EE 09 E8 44 B0
-D: pty improvements.
+D: Unix98 pty support.
+D: APM update to 1.2 spec.
 
 N: Erik Andersen
 E: andersee@debian.org
@@ -210,6 +211,14 @@
 S: Columbus, Ohio 43210
 S: USA
 
+N: Peter Braam
+E: braam@cs.cmu.edu
+W: http://coda.cs.cmu.edu/~braam
+D: Coda Filesystem
+S: Dept of Computer Science
+S: 5000 Forbes Ave
+S: Pittsburgh PA 15213
+
 N: Andries Brouwer
 E: aeb@cwi.nl
 D: random Linux hacker
@@ -559,11 +568,11 @@
 S: Australia
 
 N: Dmitry S. Gorodchanin
-E: begemot@bgm.rosprint.net
+E: pgmdsg@ibi.com
 D: RISCom/8 driver, misc kernel fixes.
-S: 6/1 M.Koneva bl, apt #125 
-S: Poltava 314023
-S: Ukraine
+S: 4 Main Street
+S: Woodbridge, Connecticut 06525
+S: USA
 
 N: Paul Gortmaker
 E: gpg109@rsphy1.anu.edu.au
@@ -930,8 +939,8 @@
 N: Bas Laarhoven
 E: bas@vimec.nl
 D: Loadable modules and ftape driver
-S: Mr. v. Boemellaan 39
-S: NL-5237 KA 's-Hertogenbosch
+S: J. Obrechtstr 23
+S: NL-5216 GP 's-Hertogenbosch
 S: The Netherlands
 
 N: Savio Lam
@@ -1133,8 +1142,8 @@
 N: Dirk Melchers
 E: dirk@merlin.nbg.sub.org
 D: 8 bit XT hard disk driver for OMTI5520
-S: Heidackerstrass 19
-S: D-91056 Erlangen
+S: Schloessleinsgasse 31
+S: D-90453 Nuernberg
 S: Germany
 
 N: Michael Meskes
@@ -1730,6 +1739,13 @@
 S: 90491 Nuernberg
 S: Germany
 
+N: Petr Vandrovec
+E: vandrove@vc.cvut.cz
+D: Small contributions to ncpfs
+S: Chudenicka 8
+S: 10200 Prague 10, Hostivar
+S: Czech Republic
+
 N: Dirk Verworner
 D: Co-author of german book ``Linux-Kernel-Programmierung''
 D: Co-founder of Berlin Linux User Group
@@ -1841,10 +1857,12 @@
 S: The Netherlands
 
 N: David Woodhouse
-E: dwmw2@cam.ac.uk
+E: Dave@imladris.demon.co.uk
 D: Extensive ARCnet rewrite
 D: ARCnet COM20020, COM90xx IO-MAP drivers
 D: SO_BINDTODEVICE in 2.1.x (from Elliot Poger's code in 2.0.31)
+D: Contributed to NCPFS rewrite for 2.1.x dcache
+D: Alpha platforms: SX164, LX164 and Ruffian ported to 2.1.x
 S: Robinson College, Grange Road
 S: Cambridge. CB3 9AN
 S: England
diff -u --recursive --new-file v2.1.88/linux/Documentation/00-INDEX linux/Documentation/00-INDEX
--- v2.1.88/linux/Documentation/00-INDEX	Fri Jan 23 18:10:31 1998
+++ linux/Documentation/00-INDEX	Thu Feb 26 11:01:24 1998
@@ -6,6 +6,8 @@
 
 00-INDEX
 	- this file.
+ARM-README
+	- information for using Linux on the ARM architecture.
 BUG-HUNTING
 	- brute force method of doing binary search of patches to find bug.
 Changes
@@ -14,10 +16,14 @@
 	- how the boss likes the C code in the kernel to look.
 Configure.help
 	- text file that is used for help when you run "make config"
+IO-APIC.txt
+	- info on using the enhanced interrupt hardware on SMP boards.
 IO-mapping.txt
 	- how to access I/O mapped memory from within device drivers.
 SMP.txt
 	- notes, and "To Fix" list for multi-processor Linux. (see smp.tex)
+VGA-softcursor.txt
+	- how to change your VGA cursor from a blinking underscore.
 binfmt_misc.txt
 	- info on the kernel support for extra binary formats.
 cdrom/
@@ -36,6 +42,8 @@
 	- directory with info on the various filesystems that Linux supports.
 ftape.txt
 	- notes about the floppy tape device driver
+hayes-esp.txt
+	- info on using the Hayes ESP serial driver.
 ide.txt
 	- important info for users of ATA devices (IDE/EIDE disks and CD-ROMS)
 initrd.txt
@@ -46,32 +54,44 @@
 	- directory with info on the linux ISDN support, and supported cards.
 java.txt
 	- info on the in-kernel binary support for Java(tm)
+joystick.txt
+	- info on using joystick devices (and driver) with linux.
 locks.txt
 	- info on file locking implementations, flock() vs. fcntl(), etc.
 logo.gif
 	- Full colour GIF image of Linux logo (penguin)
 logo.txt
 	- Info on creator of above logo & site to get additional images from.
+m68k/
+	- directory with info about Linux on Motorola 68k archtecture.
 magic-number.txt
 	- list of magic numbers used to mark/protect kernel data structures.
 mandatory.txt
 	- info on the linux implementation of Sys V mandatory file locking.
+md.txt
+	- info on boot arguments for the multiple devices driver
 memory.txt
 	- info on typical Linux memory problems.
 mca.txt
 	- info on supporting Micro Channel Architecture (e.g. PS/2) systems.
 modules.txt
 	- short guide on how to make kernel parts into loadable modules
+nbd.txt
+	- info on a TCP implementation of a network block device.
 networking/
 	- directory with info on various linux networking aspects.
 nfsroot.txt
 	- short guide on setting up a diskless box with NFS root filesystem
 oops-tracing.txt
 	- how to decode those nasty internal kernel error dump messages.
+pcwd-watchdog.txt
+	- info and sample code for using with the PC Watchdog reset card.
 paride.txt
 	- information about the parallel port IDE subsystem.
 parport.txt
 	- how to use the parallel-port driver.
+powerpc/
+	- directory with info on using linux with the PowerPC.
 ramdisk.txt
 	- short guide on how to set up and use the RAM disk.
 riscom8.txt
@@ -80,12 +100,22 @@
 	- notes on how to use the Real Time Clock (aka CMOS clock) driver.
 scsi.txt
 	- short blurb on using SCSI support as a module.
+serial-console.txt
+	- how to set up linux with a serial line console as the default.
 smart-config.txt
 	- description of the Smart Config makefile feature.
 smp.tex
 	- TeX document describing implementation of Multiprocessor Linux
+specialix.txt
+	- info on hardware/driver for specialix IO8+ multiport serial card.
+spinlocks.txt
+	- info on using spinlocks to provide exclusive access in kernel.
+stallion.txt
+	- info on using the Stallion multiport serial driver.
 svga.txt
 	- short guide on selecting video modes at boot via VGA BIOS.
+transname.txt
+	- how to use name translation to ease use of diskless systems.
 unicode.txt
 	- info on the Unicode character/font mapping used in Linux.
 watchdog.txt
diff -u --recursive --new-file v2.1.88/linux/Documentation/Changes linux/Documentation/Changes
--- v2.1.88/linux/Documentation/Changes	Sun Nov 30 12:23:16 1997
+++ linux/Documentation/Changes	Tue Feb 24 23:49:20 1998
@@ -14,10 +14,10 @@
 therefore owes credit to the same people as that file (Jared Mauch,
 Axel Boldt, Alessandro Sigala, and countless other users all over the
 'net).  Please feel free to submit changes, corrections, gripes,
-flames, money, etc. to me (gt1355b@prism.gatech.edu).  If you do so,
-you don't need to bother doing so in the form of a diff, as this is
-generated by texinfo so a diff is useless anyway (though I can
-incorporate one by hand if you insist upon sending it that way ;-).
+flames, money, etc. to me (kaboom@gatech.edu).  If you do so, you don't
+need to bother doing so in the form of a diff, as this is generated by
+texinfo so a diff is useless anyway (though I can incorporate one by
+hand if you insist upon sending it that way ;-).
 
    Check out http://www.cviog.uga.edu/Misc/info/LinuxBleed.html for an
 HTML-ized shopping list.
@@ -26,8 +26,14 @@
 http://www.datanet.hu/generations/linux/Changes2.html is an
 English-language HTML version.
 
-Last updated: September 13. 1997
-Current Author: Chris Ricker (gt1355b@prism.gatech.edu).
+   The most current version should always be available from
+http://cyberbuzz.gatech.edu/kaboom/linux/ as well.
+
+   Also, don't forget http://www.linuxhq.com/ for all your Linux kernel
+needs.
+
+Last updated: February 16. 1998
+Current Author: Chris Ricker (kaboom@gatech.edu).
 
 Current Minimal Requirements
 ****************************
@@ -36,21 +42,24 @@
 encountered a bug!  If you're unsure what version you're currently
 running, the suggested command should tell you.
 
-- Kernel modules	 modutils-2.1.55         ; insmod -V
+- Kernel modules	 modutils-2.1.85         ; insmod -V
 - Gnu C 		 2.7.2.3                 ; gcc --version
 - Binutils		 2.8.1.0.1               ; ld -v
 - Linux C Library	 5.4.38                  ; ls -l /lib/libc.so.*
 - Dynamic Linker (ld.so) 1.9.5                   ; ldd -v
 - Linux C++ Library	 2.7.2.8                 ; ls -l /usr/lib/libg++.so.*
-- Procps		 1.2                    ; ps --version
+- Procps		 1.2.5                   ; ps --version
 - Procinfo               0.11                    ; procinfo -v
-- Mount                  2.6h                    ; mount --version
+- Mount                  2.7l                    ; mount --version
 - Net-tools              1.41                    ; hostname -V
 - Loadlin                1.6a
 - Sh-utils               1.16                    ; expr --v
-- Autofs                 0.3.11                   ; automount --version
+- Autofs                 0.3.11                  ; automount --version
 - NFS                    0.4.21                  ; showmount --version
 - Bash                   1.14.7                  ; bash -version
+- Ncpfs                  2.1.1                   ; ncpmount -v
+- Pcmcia-cs              2.9.12
+- PPP                    2.3.3                   ; pppd -v
 
 Upgrade notes
 *************
@@ -76,7 +85,7 @@
    For modules to work, you need to be running libc-5.4.x or greater.
 Since updates to libc fix other problems as well (security flaws, for
 example) and since 5.4.7 is missing a few needed symbols, try to get
-the latest 5.4.x you can.  Currently, libc-5.4.38 is the latest public
+the latest 5.4.x you can.  Currently, libc-5.4.44 is the latest public
 release.
 
    If you upgrade to libc-5.4.x, you also have to upgrade your dynamic
@@ -90,10 +99,14 @@
    If you upgrade to libc-5.4.x, you may also need to upgrade ypbind if
 you're using NIS.
 
+   If you upgrade to libc-5.4.44, please read and pay attention to its
+accompanying release notes.  The section about it breaking make is not
+a joke.
+
 Modules
 =======
 
-   You need to upgrade to modutils-2.1.55 for kernels 2.1.55 and later.
+   You need to upgrade to modutils-2.1.85 for kernels 2.1.85 and later.
 This version will also work with 2.0.x kernels.
 
 Binutils
@@ -108,9 +121,13 @@
 
    You need at least GCC 2.7.2 to compile the kernel.  If you're
 upgrading from an earlier release, you might as well get GCC 2.7.2.3,
-the latest public release.  If you already have GCC 2.7.2 on your
-system, you don't have to upgrade just so the kernel will work (though
-feel free to upgrade if you want the gcc bug fixes).
+the latest stable public release.  If you already have GCC 2.7.2 on
+your system, you don't have to upgrade just so the kernel will work
+(though feel free to upgrade if you want the gcc bug fixes).
+
+   Note that the latest compilers (egcs, pgcc, gcc 2.8) may do Bad
+Things while compiling your kernel, particularly if absurd
+optimizations (like -O9) are used.  Caveat emptor.
 
 Networking Changes
 ==================
@@ -190,18 +207,13 @@
 /dev/lp0 with the new Plug-and-Play driver.  If printing breaks with
 the new driver, try checking your lpd configuration.
 
-pppd
-====
-This kernel version needs a minor bugfix to pppd. See 
-Documentation/networking/ppp.txt for more information.
-
 Syncookies
 ==========
-When you build your kernel with Syncookie support (CONFIG_SYN_COOKIES)
-the syncookie code still defaults to off (unlike the 2.0.30+ behaviour).
-You have to explicitely enable it by add a line like
-echo 1 >/proc/sys/net/ipv4/tcp_syncookies 
-to one of your startup scripts (e.g. /etc/rc.d/rc.local on a redhat system)
+
+   When you build your kernel with Syncookie support
+(CONFIG_SYN_COOKIES) the syncookie code still defaults to off (unlike
+the 2.0.30+ behavior).  You have to explicitly enable it by issuing the
+following command:      echo 1 > /proc/sys/net/ipv4/tcp_syncookies
 
 Bash
 ====
@@ -210,6 +222,24 @@
 cause problems when compiling modules.  Upgrade to at least 1.14 to fix
 this problem.
 
+Ncpfs
+=====
+
+   To mount NetWare shares, you'll need to upgrade to a more recent
+version of the ncpfs utils.
+
+Pcmcia-cs
+=========
+
+   If you use pcmcia cards, you'll need to upgrade the daemon and
+support utils to the latest release of pcmcia-cs.
+
+PPP
+===
+
+   Due to changes in the routing code, those of you using PPP
+networking will need to upgrade your pppd.
+
 Where to get the files
 **********************
 
@@ -263,16 +293,16 @@
 Modules utilities
 =================
 
-The 2.1.55 release:
-ftp://ftp.redhat.com/pub/alphabits/modutils/modutils-2.1.55.tar.gz
-ftp://ftp.kernel.org/pub/linux/kernel/v2.1/modutils-2.1.55.tar.gz
+The 2.1.85 release:
+ftp://ftp.redhat.com/pub/alphabits/modutils/modutils-2.1.85.tar.gz
+ftp://ftp.kernel.org/pub/linux/kernel/v2.1/modutils-2.1.85.tar.gz
 
 Procps utilities
 ================
 
 The 1.2 release:
-ftp://tsx-11.mit.edu/pub/linux/sources/usr.bin/procps-1.2.tar.gz
-ftp://sunsite.unc.edu/pub/Linux/system/status/ps/procps-1.2.tgz
+ftp://tsx-11.mit.edu/pub/linux/sources/usr.bin/procps-1.2.5.tar.gz
+ftp://sunsite.unc.edu/pub/Linux/system/status/ps/procps-1.2.5.tgz
 
 Procinfo utilities
 ==================
@@ -317,8 +347,8 @@
 Mount
 =====
 
-The 2.6h release:
-ftp://ftp.win.tue.nl/pub/linux/util/mount-2.6h.tar.gz
+The 2.7l release:
+ftp://ftp.win.tue.nl/pub/linux/util/mount/mount-2.7l.tar.gz
 
 Autofs
 ======
@@ -336,9 +366,8 @@
 Net-tools
 =========
 
-The 1.41 release:
-ftp://ftp.london.uk.eu.org/pub/ipv6/net-tools-1.41.tar.gz
-ftp://ftp.cs-ipv6.lancs.ac.uk/pub/Code/Linux/Net_Tools/net-tools-1.41.tar.gz
+The 1.432 release:
+ftp://ftp.cs-ipv6.lancs.ac.uk/pub/Code/Linux/Net_Tools/net-tools-1.432.tar.gz
 
 Ypbind
 ======
@@ -352,6 +381,25 @@
 The 1.14.7 release:
 ftp://prep.ai.mit.edu/pub/gnu/bash-1.14.7.tar.gz
 
+Ncpfs
+=====
+
+The 2.1.1 release:
+ftp://ftp.gwdg.de/pub/linux/misc/ncpfs/ncpfs-2.1.1.tgz
+ftp://sunsite.unc.edu/pub/Linux/system/Filesystems/ncpfs/ncpfs-2.1.1.tgz
+
+Pcmcia-cs
+=========
+
+The 1.9.12 release:
+ftp://hyper.stanford.edu/pub/pcmcia/pcmcia-cs-2.9.12.tar.gz
+
+PPP
+===
+
+The 2.3.3 release:
+ftp://cs.anu.edu.au/pub/software/ppp/ppp-2.3.3.tar.gz
+
 Other Info
 ==========
 
@@ -380,5 +428,5 @@
 
 Please send info about any other packages that 2.1.x "broke" or about
 any new features of 2.1.x that require extra or new packages for use to
-Chris Ricker (gt1355b@prism.gatech.edu).
+Chris Ricker (kaboom@gatech.edu).
 
diff -u --recursive --new-file v2.1.88/linux/Documentation/Configure.help linux/Documentation/Configure.help
--- v2.1.88/linux/Documentation/Configure.help	Wed Feb  4 11:35:59 1998
+++ linux/Documentation/Configure.help	Fri Mar  6 10:01:50 1998
@@ -2193,10 +2193,9 @@
   (user: anonymous) from shadow.cabi.net in /pub/Linux. Note that if
   your box acts as a bridge, it probably contains several ethernet
   devices, but the kernel is not able to recognize more than one at
-  boot time without help; for details read the
-  Multiple-Ethernet-mini-HOWTO, available via ftp (user: anonymous) in
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. The Bridging code is
-  still in test. If unsure, say N.
+  boot time without help; for details read the Ethernet-HOWTO, available 
+  via ftp (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO/. 
+  The Bridging code is still in test. If unsure, say N.
 
 Packet socket
 CONFIG_PACKET
@@ -3052,10 +3051,8 @@
   the WWW, you need to have access to a machine on the Internet that
   has a program like lynx or netscape)).  You'll have to say Y if your
   computer contains a network card that you want to use under linux
-  (make sure you know its name because you will be asked for it and
-  read the Ethernet-HOWTO; also, if you plan to use more than one
-  network card under linux, read the Multiple-Ethernet-mini-HOWTO,
-  available from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini) or if you
+  (make sure you know its name because you will be asked for it and read
+  the Ethernet-HOWTO in sunsite.unc.edu:/pub/Linux/docs/HOWTO; or if you
   want to use SLIP (Serial Line Internet Protocol is the protocol used
   to send Internet traffic over telephone lines or nullmodem cables)
   or CSLIP (compressed SLIP) or PPP (Point to Point Protocol, a better
@@ -3796,10 +3793,8 @@
 CONFIG_NET_VENDOR_SMC
   If you have a network (ethernet) card belonging to this class, say Y
   and read the Ethernet-HOWTO, available via ftp (user: anonymous) in
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO. If you plan to use more than
-  one network card under linux, read the Multiple-Ethernet-mini-HOWTO,
-  available from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. Note that
-  the answer to this question doesn't directly affect the kernel:
+  sunsite.unc.edu:/pub/Linux/docs/HOWTO. Note that the
+  answer to this question doesn't directly affect the kernel:
   saying N will just cause this configure script to skip all the
   questions about Western Digital cards. If you say Y, you will be
   asked for your specific card in the following questions.
@@ -3813,10 +3808,7 @@
   running kernel whenever you want). The module will be called
   wd.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. If you plan to use more
-  than one network card under linux, read the
-  Multiple-Ethernet-mini-HOWTO, available from
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
+  Documentation/networking/net-modules.txt.
 
 SMC Ultra support
 CONFIG_ULTRA
@@ -3827,13 +3819,23 @@
   from the running kernel whenever you want). The module will be
   called smc-ultra.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. If you plan to use more
-  than one network card under linux, read the
-  Multiple-Ethernet-mini-HOWTO, available from
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.  Important: There have
-  been many reports that, with some motherboards mixing an SMC Ultra
-  and an Adaptec AHA1542 SCSI card causes corruption problems with
-  many operating systems.
+  Documentation/networking/net-modules.txt. 
+  Important: There have been many reports that, with some motherboards 
+  mixing an SMC Ultra and an Adaptec AHA154x SCSI card (or compatible,
+  such as some BusLogic models) causes corruption problems with many 
+  operating systems. The linux smc-ultra driver has a work-around for this
+  but keep it in mind if you have such a SCSI card and have problems.
+
+SMC Ultra32 EISA support
+CONFIG_ULTRA
+  If you have a network (ethernet) card of this type, say Y and read
+  the Ethernet-HOWTO, available via ftp (user: anonymous) in
+  sunsite.unc.edu:/pub/Linux/docs/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 smc-ultra32.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. 
 
 SMC 9194 Support
 CONFIG_SMC9194
@@ -3847,20 +3849,15 @@
   inserted in and removed from the running kernel whenever you
   want). The module will be called smc9194.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. If you plan to use
-  more than one network card under linux, read the
-  Multiple-Ethernet-mini-HOWTO, available from
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
+  well as Documentation/networking/net-modules.txt.
 
 Racal-Interlan (Micom) NI cards
 CONFIG_NET_VENDOR_RACAL
   If you have a network (ethernet) card belonging to this class, such
   as the NI5010, NI5210 or NI6210, say Y and read the Ethernet-HOWTO,
   available via ftp (user: anonymous) in
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO. If you plan to use more than
-  one network card under linux, read the Multiple-Ethernet-mini-HOWTO,
-  available from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. Note that
-  the answer to this question doesn't directly affect the kernel:
+  sunsite.unc.edu:/pub/Linux/docs/HOWTO. Note that the answer 
+  to this question doesn't directly affect the kernel:
   saying N will just cause this configure script to skip all the
   questions about NI cards. If you say Y, you will be asked for your
   specific card in the following questions.
@@ -3875,10 +3872,7 @@
   running kernel whenever you want). The module will be called
   ni5010.o. If you want to compile it as a module, say M here and read
   Documentation/modules.txt as well as
-xIO  Documentation/networking/net-modules.txt. If you plan to use more
-  than one network card under linux, read the
-  Multiple-Ethernet-mini-HOWTO, available from
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
+  Documentation/networking/net-modules.txt.
 
 NI5210 support
 CONFIG_NI52
@@ -3889,10 +3883,7 @@
   running kernel whenever you want). The module will be called
   ni52.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. If you plan to use more
-  than one network card under linux, read the
-  Multiple-Ethernet-mini-HOWTO, available from
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
+  Documentation/networking/net-modules.txt. 
 
 NI6510 support
 CONFIG_NI65
@@ -3903,19 +3894,14 @@
   running kernel whenever you want). The module will be called
   ni65.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. If you plan to use more
-  than one network card under linux, read the
-  Multiple-Ethernet-mini-HOWTO, available from
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
+  Documentation/networking/net-modules.txt. 
 
 AMD LANCE and PCnet (AT1500 and NE2100) support
 CONFIG_LANCE
   If you have a network (ethernet) card of this type, say Y and read
   the Ethernet-HOWTO, available via ftp (user: anonymous) in
   sunsite.unc.edu:/pub/Linux/docs/HOWTO. Some LinkSys cards are of
-  this type. If you plan to use more than one network card under
-  linux, read the Multiple-Ethernet-mini-HOWTO, available from
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
+  this type.
 
 3COM cards
 CONFIG_NET_VENDOR_3COM
@@ -3925,9 +3911,7 @@
   this question doesn't directly affect the kernel: saying N will just
   cause this configure script to skip all the questions about 3COM
   cards. If you say Y, you will be asked for your specific card in the
-  following questions. If you plan to use more than one network card
-  under linux, read the Multiple-Ethernet-mini-HOWTO, available from
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. 
+  following questions. 
 
 3c501 support
 CONFIG_EL1
@@ -3941,10 +3925,7 @@
   from the running kernel whenever you want). The module will be
   called 3c501.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. If you plan to use more
-  than one network card under linux, read the
-  Multiple-Ethernet-mini-HOWTO, available from
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini - and don't use 3c501s.
+  Documentation/networking/net-modules.txt.
 
 3c503 support
 CONFIG_EL2
@@ -3955,10 +3936,7 @@
   running kernel whenever you want). The module will be called
   3c503.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. If you plan to use more
-  than one network card under linux, read the
-  Multiple-Ethernet-mini-HOWTO, available from
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
+  Documentation/networking/net-modules.txt.
 
 3c505 support
 CONFIG_ELPLUS
@@ -3970,9 +3948,7 @@
   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
-  3c505.o. If you plan to use more than one network card under linux,
-  read the Multiple-Ethernet-mini-HOWTO, available from
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
+  3c505.o.
 
 3c507 support
 CONFIG_EL16
@@ -3983,10 +3959,7 @@
   running kernel whenever you want). The module will be called
   3c507.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. If you plan to use more
-  than one network card under linux, read the
-  Multiple-Ethernet-mini-HOWTO, available from
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
+  Documentation/networking/net-modules.txt.
 
 3c523 support 
 CONFIG_ELMC
@@ -3997,10 +3970,7 @@
   running kernel whenever you want). The module will be called
   3c523.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. If you plan to use more
-  than one network card under linux, read the
-  Multiple-Ethernet-mini-HOWTO, available from
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
+  Documentation/networking/net-modules.txt.
 
 3c509/3c579 support
 CONFIG_EL3
@@ -4011,11 +3981,8 @@
   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
-  3c509.o. If you plan to use more than one network card under linux,
-  read the Multiple-Ethernet-mini-HOWTO, available from
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. If your card is not
-  working you may need to use the DOS setup disk to disable Plug &
-  Play mode, and to select the default media type.
+  3c509.o. If your card is not working you may need to use the DOS setup
+  disk to disable Plug & Play mode, and to select the default media type.
 
 3c590 series (592/595/597) "Vortex" support
 CONFIG_VORTEX
@@ -4028,9 +3995,7 @@
   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
-  3c59x.o. If you plan to use more than one network card under linux,
-  read the Multiple-Ethernet-mini-HOWTO, available from
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini
+  3c59x.o.
 
 Other ISA cards
 CONFIG_NET_ISA
@@ -4043,9 +4008,7 @@
   directly affect the kernel: saying N will just cause this configure
   script to skip all the remaining ISA network card questions.  If you
   say Y, you will be asked for your specific card in the following
-  questions.  If you plan to use more than one network card under
-  linux, read the Multiple-Ethernet-mini-HOWTO, available from
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
+  questions. 
 
 Generic ARCnet support
 CONFIG_ARCNET
@@ -4062,10 +4025,7 @@
   from the running kernel whenever you want). The module will be
   called arcnet.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. If you plan to use more
-  than one network card under linux, read the
-  Multiple-Ethernet-mini-HOWTO, available from
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
+  Documentation/networking/net-modules.txt.
 
 Enable arc0e (ARCnet "ether-encap" packet format)
 CONFIG_ARCNET_ETH
@@ -4146,10 +4106,7 @@
   running kernel whenever you want). The module will be called
   e2100.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. If you plan to use more
-  than one network card under linux, read the
-  Multiple-Ethernet-mini-HOWTO, available from
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
+  Documentation/networking/net-modules.txt.
 
 CS89x0 support
 CONFIG_CS89x0
@@ -4162,9 +4119,7 @@
   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
-  cs89x.o. If you plan to use more than one network card under linux,
-  read the Multiple-Ethernet-mini-HOWTO, available from
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
+  cs89x.o.
 
 DEPCA support
 CONFIG_DEPCA
@@ -4175,9 +4130,7 @@
   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 depca.o. If you plan to use more than one network card under
-  linux, read the Multiple-Ethernet-mini-HOWTO, available from
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
+  called depca.o.
 
 EtherWorks 3 support
 CONFIG_EWRK3
@@ -4190,18 +4143,13 @@
   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
-  ewrk3.o. If you plan to use more than one network card under linux,
-  read the Multiple-Ethernet-mini-HOWTO, available from
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
+  ewrk3.o.
 
 SEEQ8005 support
 CONFIG_SEEQ8005
   This is a driver for the SEEQ 8005 network (ethernet) card. If this
   is for you, read the Ethernet-HOWTO, available via ftp (user:
-  anonymous) from sunsite.unc.edu:/pub/Linux/docs/HOWTO. If you plan
-  to use more than one network card under linux, read the
-  Multiple-Ethernet-mini-HOWTO, available from
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
+  anonymous) from sunsite.unc.edu:/pub/Linux/docs/HOWTO.
 
 AT1700 support
 CONFIG_AT1700
@@ -4212,9 +4160,7 @@
   running kernel whenever you want). 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. The module will be called
-  at1700.o. If you plan to use more than one network card under linux,
-  read the Multiple-Ethernet-mini-HOWTO, available from
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
+  at1700.o.
 
 FMV-181/182/183/184 support
 CONFIG_FMV18X
@@ -4225,10 +4171,7 @@
   removed from the running kernel whenever you want). The module will
   be called fmv18x.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. If you plan to use more
-  than one network card under linux, read the
-  Multiple-Ethernet-mini-HOWTO, available from
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. If you use FMV-183 or
+  Documentation/networking/net-modules.txt.  If you use FMV-183 or
   FMV-184 and it is not working, you may need to disable Plug & Play
   mode of the card.
 
@@ -4242,10 +4185,7 @@
   in and removed from the running kernel whenever you want). The
   module will be called eepro.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. If you plan to use more
-  than one network card under linux, read the
-  Multiple-Ethernet-mini-HOWTO, available from
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
+  Documentation/networking/net-modules.txt.
 
 EtherExpress support
 CONFIG_EEXPRESS
@@ -4259,9 +4199,7 @@
   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
-  eexpress.o. If you plan to use more than one network card under
-  linux, read the Multiple-Ethernet-mini-HOWTO, available from
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
+  eexpress.o.
 
 AT&T WaveLAN & DEC RoamAbout DS support
 CONFIG_WAVELAN
@@ -4282,10 +4220,7 @@
   inserted in and removed from the running kernel whenever you
   want). The module will be called wavelan.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. If you plan to use
-  more than one network card under linux, read the
-  Multiple-Ethernet-mini-HOWTO, available from
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
+  well as Documentation/networking/net-modules.txt.
 
 HP PCLAN+ (27247B and 27252A) support
 CONFIG_HPLAN_PLUS
@@ -4296,10 +4231,7 @@
   running kernel whenever you want). The module will be called
   hp-plus.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. If you plan to use more
-  than one network card under linux, read the
-  Multiple-Ethernet-mini-HOWTO, available from
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
+  Documentation/networking/net-modules.txt.
 
 HP PCLAN (27245 and other 27xxx series) support
 CONFIG_HPLAN
@@ -4310,10 +4242,7 @@
   running kernel whenever you want). The module will be called
   hp.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. If you plan to use more
-  than one network card under linux, read the
-  Multiple-Ethernet-mini-HOWTO, available from
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
+  Documentation/networking/net-modules.txt.
 
 HP 10/100VG PCLAN (ISA, EISA, PCI) support
 CONFIG_HP100
@@ -4324,9 +4253,7 @@
   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
-  hp100.o. If you plan to use more than one network card under linux,
-  read the Multiple-Ethernet-mini-HOWTO, available from
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
+  hp100.o.
 
 NE2000/NE1000 support
 CONFIG_NE2000
@@ -4338,28 +4265,21 @@
   from the running kernel whenever you want). The module will be
   called ne.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. If you plan to use more
-  than one network card under linux, read the
-  Multiple-Ethernet-mini-HOWTO, available from
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
+  Documentation/networking/net-modules.txt.
 
 SK_G16 support
 CONFIG_SK_G16
   If you have a network (ethernet) card of this type, say Y and read
   the Ethernet-HOWTO, available via ftp (user: anonymous) in
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO. If you plan to use more than
-  one network card under linux, read the Multiple-Ethernet-mini-HOWTO,
-  available from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
+  sunsite.unc.edu:/pub/Linux/docs/HOWTO.
 
 EISA, VLB, PCI and on board controllers
 CONFIG_NET_EISA
   This is another class of network cards which attach directly to the
   bus. If you have one of those, say Y and read the Ethernet-HOWTO,
   available via ftp (user: anonymous) from
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO.  If you plan to use more than
-  one network card under linux, read the Multiple-Ethernet-mini-HOWTO,
-  available from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. If you
-  are unsure, say Y. Note that the answer to this question doesn't
+  sunsite.unc.edu:/pub/Linux/docs/HOWTO. If you are unsure, say Y. 
+  Note that the answer to this question doesn't
   directly affect the kernel: saying N will just cause this configure
   script to skip all the questions about this class of network
   cards. If you say Y, you will be asked for your specific card in the
@@ -4369,10 +4289,7 @@
 CONFIG_PCNET32
   If you have a PCnet32 or PCnetPCI based network (ethernet) card, say
   Y here and read the Ethernet-HOWTO, available via ftp (user:
-  anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO.  If you plan to
-  use more than one network card under linux, read the
-  Multiple-Ethernet-mini-HOWTO, available from
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
+  anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. 
 
 Ansel Communications EISA 3200 support
 CONFIG_AC3200
@@ -4383,10 +4300,7 @@
   running kernel whenever you want). The module will be called
   ac3200.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. If you plan to use more
-  than one network card under linux, read the
-  Multiple-Ethernet-mini-HOWTO, available from
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
+  Documentation/networking/net-modules.txt.
 
 Racal-Interlan EISA ES3210 support
 CONFIG_ES3210
@@ -4397,10 +4311,7 @@
   running kernel whenever you want). The module will be called
   es3210.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. If you plan to use more
-  than one network card under linux, read the
-  Multiple-Ethernet-mini-HOWTO, available from
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
+  Documentation/networking/net-modules.txt.
 
 Apricot Xen-II on board ethernet
 CONFIG_APRICOT
@@ -4411,9 +4322,7 @@
   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
-  apricot.o. If you plan to use more than one network card under
-  linux, read the Multiple-Ethernet-mini-HOWTO, available from
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
+  apricot.o.
 
 Generic DECchip & DIGITAL EtherWORKS PCI/EISA
 CONFIG_DE4X5
@@ -4427,10 +4336,7 @@
   from the running kernel whenever you want). The module will be
   called de4x5.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. If you plan to use more
-  than one network card under linux, read the
-  Multiple-Ethernet-mini-HOWTO, available from
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
+  Documentation/networking/net-modules.txt.
 
 DECchip Tulip (dc21x4x) PCI support
 CONFIG_DEC_ELCP
@@ -4462,10 +4368,7 @@
   from the running kernel whenever you want). The module will be
   called dgrs.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. If you plan to use more
-  than one network card under linux, read the
-  Multiple-Ethernet-mini-HOWTO, available from
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
+  Documentation/networking/net-modules.txt.
 
 EtherExpressPro/100 support
 CONFIG_EEXPRESS_PRO100
@@ -4476,10 +4379,7 @@
   removed from the running kernel whenever you want). The module will
   be called eepro100.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. If you plan to use more
-  than one network card under linux, read the
-  Multiple-Ethernet-mini-HOWTO, available from
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
+  Documentation/networking/net-modules.txt.
 
 ICL EtherTeam 16i/32 support
 CONFIG_ETH16I
@@ -4490,10 +4390,7 @@
   running kernel whenever you want). The module will be called
   eth16i.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. If you plan to use more
-  than one network card under linux, read the
-  Multiple-Ethernet-mini-HOWTO, available from
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
+  Documentation/networking/net-modules.txt.
 
 TI ThunderLAN support (EXPERIMENTAL)
 CONFIG_TLAN
@@ -4553,9 +4450,7 @@
   want to compile this driver as a module however ( = code which can
   be inserted in and removed from the running kernel whenever you
   want), say M here and read Documentation/modules.txt. The module
-  will be called de600.o. If you plan to use more than one network
-  card under linux, read the Multiple-Ethernet-mini-HOWTO, available
-  from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
+  will be called de600.o.
 
 D-Link DE620 pocket adapter support
 CONFIG_DE620
@@ -4568,9 +4463,7 @@
   want to compile this driver as a module however ( = code which can
   be inserted in and removed from the running kernel whenever you
   want), say M here and read Documentation/modules.txt. The module
-  will be called de620.o. If you plan to use more than one network
-  card under linux, read the Multiple-Ethernet-mini-HOWTO, available
-  from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
+  will be called de620.o.
 
 Token Ring driver support
 CONFIG_TR
@@ -5437,6 +5330,55 @@
   read Documentation/modules.txt. The module will be called
   ncpfs.o. Say N unless you are connected to a Novell network.
 
+Packet signatures
+CONFIG_NCPFS_PACKET_SIGNING
+  NCP allows to sign packets for stronger security. If you want
+  security, say Y. Normal users can leave it off. To be able to use
+  packet signing you must use ncpfs > 2.0.12.
+
+Proprietary file locking
+CONFIG_NCPFS_IOCTL_LOCKING
+  Allows locking of records on remote volumes. Say N unless you have special
+  applications which are able to utilize this locking scheme.
+
+Clear remove/delete inhibit when needed
+CONFIG_NCPFS_STRONG
+  Allows manipulation of files flagged as Delete or Rename Inhibit.
+  To use this feature you must mount volumes with the ncpmount parameter
+  "-s" (ncpfs-2.0.12 and newer). Say Y unless you are not mounting
+  volumes with -f 444.
+
+Use NFS namespace when available
+CONFIG_NCPFS_NFS_NS
+  Allows you to utilize NFS namespace on NetWare servers. It brings you
+  case sensitive filesystems. Say Y. You can disable it at mount-time with
+  the -N nfs parameter of ncpmount.
+
+Use OS2/LONG namespace when available
+CONFIG_NCPFS_OS2_NS
+  Allows you to utilize OS2/LONG namespace on NetWare servers. Filenames 
+  in this namespace are limited to 255 characters, they are case 
+  insensitive, and case in names is preserved. 
+  Say Y. You can disable it at mount time with the -N os2 parameter of 
+  ncpmount.
+
+Allow mounting of volume subdirectories
+CONFIG_NCPFS_MOUNT_SUBDIR
+  Allows you to mount not only whole servers or whole volumes, but also
+  subdirectory from a volume. It can be used to reexport data and so on.
+  There is no reason why to say N, so Y is recommended unless you count
+  every byte. 
+  To utilize this feature you must use ncpfs-2.0.12 or newer.
+
+NDS interserver authentication domains
+CONFIG_NCPFS_NDS_DOMAINS
+  This allows storing NDS private keys into kernel space where it can be
+  used to authenticate another server as interserver NDS accesses need
+  it. You must use ncpfs-2.0.12.1 or newer to utilize this feature.
+  Say Y if you are using NDS connections to NetWare servers. Do not say Y
+  if security is primary for you because root can read your session
+  key (from /proc/kcore).
+
 Amiga FFS filesystem support
 CONFIG_AFFS_FS
   The Fast File System (FFS) is the common filesystem used on
@@ -6476,6 +6418,13 @@
   anonymous) contain support for this ("halt -p" shuts down Linux and
   powers off the computer).  As with the other APM options, this
   option may not work reliably with some APM BIOS implementations.
+
+Ignore multiple suspend/standby events
+CONFIG_APM_IGNORE_MULTIPLE_SUSPEND
+  This option is necessary on the Thinkpad 560, but should work on all
+  other laptops.  When the APM BIOS returns multiple suspend or standby
+  events while one is already being processed they will be ignored.
+  Without this the Thinkpad 560 has troubles with apmd, and pcmcia-cs.
 
 Watchdog Timer Support 
 CONFIG_WATCHDOG
diff -u --recursive --new-file v2.1.88/linux/Documentation/filesystems/affs.txt linux/Documentation/filesystems/affs.txt
--- v2.1.88/linux/Documentation/filesystems/affs.txt	Tue Dec  2 22:25:07 1997
+++ linux/Documentation/filesystems/affs.txt	Mon Feb 23 22:01:26 1998
@@ -65,6 +65,11 @@
 verbose		The volume name, file system type and block size will
 		be written to the syslog when the filesystem is mounted.
 
+mufs		The filesystem is really a muFS, also it doesn't
+		identify itself as one. This option is neccessary if
+		the filesystem wasn't formatted as muFS, but is used
+		as one.
+
 prefix=path	Path will be prefixed to every absolute path name of
 		symbolic links on an AFFS partition. Default = /
 
diff -u --recursive --new-file v2.1.88/linux/Documentation/filesystems/coda.txt linux/Documentation/filesystems/coda.txt
--- v2.1.88/linux/Documentation/filesystems/coda.txt	Sun Dec 21 14:45:14 1997
+++ linux/Documentation/filesystems/coda.txt	Wed Mar  4 15:14:32 1998
@@ -1,3 +1,27 @@
+
+NOTE: 
+This is one of the technical documents describing a component of
+Coda -- this document describes the client kernel-Venus interface.
+
+For more information:
+  http://www.coda.cs.cmu.edu
+For user level software needed to run Coda:
+  ftp://ftp.coda.cs.cmu.edu
+
+To run Coda you need to get a user level cache manager for the client,
+named Venus, as well as tools to manipulate ACL's, to log in etc.  The
+client needs to have the Coda filesystem selected in the kernel
+configuration.
+
+The server needs a user level server and at present does not depend on
+kernel support.
+
+
+
+
+
+
+
   The Venus kernel interface
   Peter J. Braam
   v1.0, Nov 9, 1997
diff -u --recursive --new-file v2.1.88/linux/Documentation/filesystems/vfs.txt linux/Documentation/filesystems/vfs.txt
--- v2.1.88/linux/Documentation/filesystems/vfs.txt	Sun Feb  2 05:18:29 1997
+++ linux/Documentation/filesystems/vfs.txt	Tue Feb 24 22:08:00 1998
@@ -8,7 +8,7 @@
 The VFS relatively simple, but it is nice not to have to browse through
 pages of code to determine what is expected when writing a filesystem.
 Hopefully this helps anyone attempting such a feat, as well as clearing up
-a few important points/dependancies.
+a few important points/dependencies.
 
 
 register_filesystem (struct file_system_type *fstype)
diff -u --recursive --new-file v2.1.88/linux/Documentation/isdn/README linux/Documentation/isdn/README
--- v2.1.88/linux/Documentation/isdn/README	Thu May 29 21:53:03 1997
+++ linux/Documentation/isdn/README	Tue Feb 24 22:08:00 1998
@@ -285,7 +285,7 @@
 5. Application
 
    a) For some card-types, firmware has to be loaded into the cards, before
-      proceeding with device-independant setup. See README.<yourDriver>
+      proceeding with device-independent setup. See README.<yourDriver>
       for how to do that.
 
    b) If you only intend to use ttys, you are nearly ready now.
diff -u --recursive --new-file v2.1.88/linux/Documentation/m68k/kernel-options.txt linux/Documentation/m68k/kernel-options.txt
--- v2.1.88/linux/Documentation/m68k/kernel-options.txt	Tue Feb 17 13:12:43 1998
+++ linux/Documentation/m68k/kernel-options.txt	Tue Feb 24 22:08:01 1998
@@ -567,7 +567,7 @@
    type.
 
    The second parameter <trackbuffer> tells the kernel whether to use
-   track buffering (1) or not (0). The default is machine dependant:
+   track buffering (1) or not (0). The default is machine-dependent:
    no for the Medusa and yes for all others.
 
    With the two following parameters, you can change the default
diff -u --recursive --new-file v2.1.88/linux/Documentation/nbd.txt linux/Documentation/nbd.txt
--- v2.1.88/linux/Documentation/nbd.txt	Sat Nov 29 10:33:18 1997
+++ linux/Documentation/nbd.txt	Thu Feb 26 11:01:24 1998
@@ -4,19 +4,19 @@
    means, that it works on my computer, and it worked on one of school
    computers.
    
-   What is it: With this think compiled in kernel, linux can use remote
-   server as one of its block devices. So every time client computer
-   wants to read /dev/nd0, it will send request over TCP to server, which
-   will reply with data readed. This can be used for stations with
-   low-disk space (or even disklesses - if you boot from floppy) to
-   borrow disk space from other computer. Unlike NFS, it is possible to
-   put any filesystem on it etc. It is impossible to use NBD as root
-   filesystem, since it requires user-level program to start. It also
+   What is it: With this compiled in the kernel, linux can use a remote
+   server as one of its block devices. So every time the client computer
+   wants to read /dev/nd0, it sends a request over TCP to the server, which
+   will reply with the data read. This can be used for stations with
+   low-disk space (or even diskless - if you boot from floppy) to
+   borrow disk space from another computer. Unlike NFS, it is possible to
+   put any filesystem on it etc. It is impossible to use NBD as a root
+   filesystem, since it requires a user-level program to start. It also
    allows you to run block-device in user land (making server and client
-   physicaly same computer, communicating using loopback).
+   physically the same computer, communicating using loopback).
    
    Current state: It currently works. Network block device looks like
-   being pretty stable. I originaly thought that it is impossible to swap
+   being pretty stable. I originally thought that it is impossible to swap
    over TCP. It turned out not to be true - swapping over TCP now works
    and seems to be deadlock-free, but it requires heavy patches into
    Linux's network layer.
@@ -30,8 +30,8 @@
 ...
 
    Protocol: Userland program passes file handle with connected TCP
-   socket to actuall kernel driver. This way, kernel does not have to
-   care about connecting etc. Protocol is rather simple: If driver is
+   socket to actual kernel driver. This way, the kernel does not have to
+   care about connecting etc. Protocol is rather simple: If the driver is
    asked to read from block device, it sends packet of following form
    "request" (all data are in network byte order):
    
@@ -48,7 +48,7 @@
    structure "reply":
    
   __u32 magic;        must be equal to
-  __u64 handle;       handle copyied from request
+  __u64 handle;       handle copied from request
   __u32 error;        0 = operation completed successfully,
                       else error code
   ...                 in case of read operation with no error,
diff -u --recursive --new-file v2.1.88/linux/Documentation/networking/alias.txt linux/Documentation/networking/alias.txt
--- v2.1.88/linux/Documentation/networking/alias.txt	Wed Jan  3 10:36:23 1996
+++ linux/Documentation/networking/alias.txt	Sun Mar  1 14:40:39 1998
@@ -1,32 +1,10 @@
-NET_ALIAS device aliasing v0.4x
-===============================
-  The main step taken in versions 0.40+ is the implementation of a
-  device aliasing mechanism that creates *actual* devices.
-  This development includes NET_ALIAS (generic aliasing) plus IP_ALIAS
-  (specific IP) support.
-
-Features
---------
-o  ACTUAL alias devices created & inserted in dev chain
-o  AF_ independent: net_alias_type objects. Generic aliasing engine.
-o  AF_INET optimized
-o  hashed alias address lookup
-o  net_alias_type objs registration/unreg., module-ables.
-o  /proc/net/aliases & /proc/net/alias_types entries
-
-o IP alias implementation: static or runtime module.
-
-Usage (IP aliasing)
--------------------
-  A very first step to test if you are running a net_alias-ed kernel
-  is to check /proc/net/aliases & /proc/net/alias_types entries:
-     # cat /proc/net/alias*
-
-  For IP aliasing you must have IP_ALIAS support included by
-  static linking ('y' to 2nd question above), or runtime module
-  insertion ('m' to 2nd q. above):
-     # insmod /usr/src/linux/modules/ip_alias.o (1.3.xx)
-     # insmod /usr/src/ip_alias/ip_alias.o      (1.2.xx) see above.
+
+IP-Aliasing:
+============
+
+
+o For IP aliasing you must have IP_ALIAS support included by static
+  linking.
 
 o Alias creation.
   Alias creation is done by 'magic' iface naming: eg. to create a
@@ -42,50 +20,30 @@
 					    for eth0:0)
 
 o Alias deletion.
-  Also done by magic naming, eg:
+  Also done by shutting the interface down:
+
+    # ifconfig eth0:0 down
+                 ~~~~~~~~~~ -> will delete alias
 
-    # ifconfig eth0:0-  0  (maybe any address)
-                   ~~~ -> will delete alias (note '-' after dev name)
-  alias device is closed before deletion, so all network stuff that
-  points to it (routes, arp entries, ...) will be released.
   		   		   
 Alias (re-)configuring
-  Aliases *are* devices, so you configure and refer to them as usual (ifconfig,
-  route, etc).
-  
-o Procfs entries
-  2 entries are added to help fetching alias runtime configuration:
-  a) /proc/net/alias_types
-     Will show you alias_types registered (ie. address families that
-     can be aliased).
-     eg. for IP aliasing with 1 alias configured:
-
-     # cat /proc/net/alias_types
-     type    name            n_attach
-     2       ip              1      
-     
-  b) /proc/net/aliases
-     Will show aliased devices info, eg (same as above):
-
-     # cat /proc/net/aliases
-     device           family address                                
-     eth0:0           2      200.1.1.1
+
+  Aliases are no real devices, but should be able to configure and
+  refer to them as usual (ifconfig, route, etc).
 
 Relationship with main device
 -----------------------------
-  - On main device closing, all aliases will be closed and freed.
-  - Each new alias created is inserted in dev_chain just before next
-    main device (aliases get 'stacked' after main_dev), eg:
-      lo->eth0->eth0:0->eth0:2->eth1->0
-    If eth0 is unregistered, all it aliases will also be:
-      lo->eth1->0     
+
+  - the main device is an alias itself like additional aliases and can
+    be shut down without deleting other aliases.
 
 Contact
 -------
 Please finger or e-mail me:
    Juan Jose Ciarlante <jjciarla@raiz.uncu.edu.ar>
-   
-		   		
+
+Updated by Erik Schoenfelder <schoenfr@gaertner.DE>
+
 ; local variables:
 ; mode: indented-text
 ; mode: auto-fill
diff -u --recursive --new-file v2.1.88/linux/Documentation/riscom8.txt linux/Documentation/riscom8.txt
--- v2.1.88/linux/Documentation/riscom8.txt	Fri Apr 26 02:12:36 1996
+++ linux/Documentation/riscom8.txt	Thu Mar  5 11:55:06 1998
@@ -1,5 +1,5 @@
 	This is the README for RISCom/8 multi-port serial driver
-	(C) 1994-1996 D.Gorodchanin (begemot@bgm.rosrpint.net)
+	(C) 1994-1996 D.Gorodchanin (pgmdsg@ibi.com)
 	See file LICENSE for terms and conditions.
 
 NOTE: English is not my native language. 
diff -u --recursive --new-file v2.1.88/linux/Documentation/sysctl/README linux/Documentation/sysctl/README
--- v2.1.88/linux/Documentation/sysctl/README	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/sysctl/README	Thu Feb 26 11:10:37 1998
@@ -0,0 +1,74 @@
+
+Documentation for /proc/sys/*/*		version 0.1
+	(c) 1998, Rik van Riel <H.H.vanRiel@fys.ruu.nl>
+
+'Why', I hear you ask, 'would anyone even _want_ documentation
+for them sysctl files? If anybody really needs it, it's all in
+the source...'
+
+Well, this documentation is written because some people either
+don't know they need to tweak something, or because they don't
+have the time or knowledge to read the source code.
+
+Furthermore, the programmers who built sysctl have built it to
+be actually used, not just for the fun of programming it :-)
+
+==============================================================
+
+Legal blurb:
+
+As usual, there are two main things to consider:
+1. you get what you pay for
+2. it's free
+
+The consequences are that I won't guarantee the correctness of
+this document, and if you come to me complaining about how you
+screwed up your system because of wrong documentation, I won't
+feel sorry for you. I might even laugh at you...
+
+But of course, if you _do_ manage to screw up your system using
+only the sysctl options used in this file, I'd like to hear of
+it. Not only to have a great laugh, but also to make sure that
+you're the last RTFMing person to screw up.
+
+In short, e-mail your suggestions, corrections and / or horror
+stories to: <H.H.vanRiel@fys.ruu.nl>
+
+Rik van Riel.
+
+==============================================================
+
+Introduction:
+
+Sysctl is a means of configuring certain aspects of the kernel
+at run-time, and the /proc/sys/ directory is there so that you
+don't even need special tools to do it!
+In fact, there are only four things needed to use these config
+facilities:
+- a running Linux system
+- root access
+- common sense (this is especially hard to come by these days)
+- knowledge of what all those values mean
+
+As a quick 'ls /proc/sys' will show, the directory consists of
+several (arch-dependent?) subdirs. Each subdir is mainly about
+one part of the kernel, so you can do configuration on a piece
+by piece basis, or just some 'thematic frobbing'.
+
+The subdirs are about:
+debug/		<empty>
+dev/		device specific information (eg dev/cdrom/info)
+fs/		specific filesystems
+		binfmt_misc <linux/Documentation/binfmt_misc.txt>
+kernel/		global kernel info / tuning
+		open file / inode tuning
+		miscellaneous stuff
+net/		networking stuff, for documentation look in:
+		<linux/Documentation/networking/>
+proc/		<empty>
+vm/		memory management tuning
+		buffer and cache management
+
+These are the subdirs I have on my system. There might be more
+or other subdirs in another setup. If you see another dir, I'd
+really like to hear about it :-)
diff -u --recursive --new-file v2.1.88/linux/Documentation/sysctl/kernel.txt linux/Documentation/sysctl/kernel.txt
--- v2.1.88/linux/Documentation/sysctl/kernel.txt	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/sysctl/kernel.txt	Thu Feb 26 11:09:16 1998
@@ -0,0 +1,208 @@
+
+Documentation for /proc/sys/kernel/*	version 0.1
+	(c) 1998, Rik van Riel <H.H.vanRiel@fys.ruu.nl>
+
+For general info and legal blurb, please look in README.
+
+==============================================================
+
+This file contains documentation for the sysctl files in
+/proc/sys/kernel/ and is valid for Linux kernel version 2.1.
+
+The files in this directory can be used to tune and monitor
+miscellaneous and general things in the operation of the Linux
+kernel. Since some of the files _can_ be used to screw up your
+system, it is advisable to read both documentation and source
+before actually making adjustments.
+
+Currently, these files are in /proc/sys/kernel:
+- ctrl-alt-del
+- dentry-state
+- domainname
+- file-max
+- file-nr
+- hostname
+- inode-max
+- inode-nr
+- inode-state
+- osrelease
+- ostype
+- panic
+- printk
+- securelevel
+- version
+
+==============================================================
+
+ctrl-alt-del:
+
+When the value in this file is 0, ctrl-alt-del is trapped and
+sent to the init(1) program to handle a graceful restart.
+When, however, the value is > 0, Linux's reaction to a Vulcan
+Nerve Pinch (tm) will be an immediate reboot, without even
+syncing it's dirty buffers.
+
+Note: when a program (like dosemu) has the keyboard in 'raw'
+mode, the ctrl-alt-del is intercepted by the program before it
+ever reaches the kernel tty layer, and it's up to the program
+to decide what to do with it.
+
+==============================================================
+
+dentry-state:
+
+From linux/fs/dentry.c:
+--------------------------------------------------------------
+struct {
+        int nr_dentry;
+        int nr_unused;
+        int age_limit;         /* age in seconds */
+        int want_pages;        /* pages requested by system */
+        int dummy[2];
+} dentry_stat = {0, 0, 45, 0,};
+-------------------------------------------------------------- 
+
+Dentries are dynamically allocated and deallocated, and
+nr_dentry seems to be 0 all the time. Hence it's safe to
+assume that only nr_unused, age_limit and want_pages are
+used. Nr_unused seems to be exactly what it's name says.
+Age_limit is the age in seconds after which dcache entries
+can be reclaimed when memory is short and want_pages is
+nonzero when shrink_dcache_pages() has been called and the
+dcache isn't pruned yet.
+
+==============================================================
+
+domainname & hostname:
+
+These files can be controlled to set the domainname and
+hostname of your box. For the classic darkstar.frop.org
+a simple:
+# echo "darkstar" > /proc/sys/kernel/hostname
+# echo "frop.org" > /proc/sys/kernel/domainname
+would suffice to set your hostname and domainname.
+
+==============================================================
+
+file-max & file-nr:
+
+The kernel allocates filehandles dynamically, but as yet it
+doesn't free them again...
+
+The value in file-max denotes the maximum number of file-
+handles that the Linux kernel will allocate. When you get lots
+of error messages about running out of file handles, you might
+want to increase this limit.
+
+The three values in file-nr denote the number of allocated
+file handles, the number of used file handles and the maximum
+number of file handles. When the allocated filehandles come
+close to the maximum, but the number of actually used ones is
+far behind, you've encountered a peek in your filehandle usage
+and you don't need to increase the maximum.
+
+==============================================================
+
+inode-max, inode-nr & inode-state:
+
+As with filehandles, the kernel allocates the inode structures
+dynamically, but can't free them yet...
+
+The value in inode-max denotes the maximum number of inode
+handlers. This value should be 3-4 times larger as the value
+in file-max, since stdin, stdout and network sockets also
+need an inode struct to handle them. When you regularly run
+out of inodes, you need to increase this value.
+
+The file inode-nr contains the first two items from
+inode-state, so we'll skip to that file...
+
+Inode-state contains three actual numbers and four dummies.
+The actual numbers are, in order of appearance, nr_inodes,
+nr_free_inodes and preshrink.
+
+Nr_inodes stands for the number of inodes the system has
+allocated, this can be slightly more than inode-max because
+Linux allocates them one pagefull at a time.
+
+Nr_free_inodes represents the number of free inodes (?) and
+preshrink is nonzero when the nr_inodes > inode-max and the
+system needs to prune the inode list instead of allocating
+more.
+
+==============================================================
+
+osrelease, ostype & version:
+
+# cat osrelease
+2.1.88
+# cat ostype
+Linux
+# cat version
+#5 Wed Feb 25 21:49:24 MET 1998
+
+The files osrelease and ostype should be clear enough. Version
+needs a little more clarification however. The '#5' means that
+this is the fifth kernel built from this source base and the
+date behind it indicates the time the kernel was built.
+The only way to tune these values is to rebuild the kernel :-)
+
+==============================================================
+
+panic:
+
+The value in this file represents the number of seconds the
+kernel waits before rebooting on a panic. When you use the
+software watchdog, the recommended setting is 60.
+
+==============================================================
+
+printk:
+
+The four values in printk denote: console_loglevel,
+default_message_loglevel, minimum_console_level and
+default_console_loglevel respectively.
+
+These values influence printk() behavior when printing or
+logging error messages. See 'man 2 syslog' for more info on
+the different loglevels.
+
+- console_loglevel: messages with a higher priority than
+  this will be printed to the console
+- default_message_level: messages without an explicit priority
+  will be printed with this priority
+- minimum_console_loglevel: minimum (highest) value to which
+  console_loglevel can be set
+- default_console_loglevel: default value for console_loglevel
+
+Note: a quick look in linux/kernel/printk.c will reveal that
+these variables aren't put inside a structure, so their order
+in-core isn't formally guaranteed and garbage values _might_
+occur when the compiler changes. (???)
+
+==============================================================
+
+securelevel:
+
+When the value in this file is nonzero, root is prohibited
+from:
+- changing the immutable and append-only flags on files
+- changing sysctl things (limited ???)
+
+==============================================================
+
+real-root-dev: (CONFIG_INITRD only)
+
+This file is used to configure the real root device when using
+an initial ramdisk to configure the system before switching to
+the 'real' root device. See linux/Documentation/initrd.txt for
+more info.
+
+==============================================================
+
+reboot-cmd: (Sparc only)
+
+??? This seems to be a way to give an argument to the Sparc
+ROM/Flash boot loader. Maybe to tell it what to do after
+rebooting. ???
+
diff -u --recursive --new-file v2.1.88/linux/Documentation/sysctl/vm.txt linux/Documentation/sysctl/vm.txt
--- v2.1.88/linux/Documentation/sysctl/vm.txt	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/sysctl/vm.txt	Thu Feb 26 11:09:16 1998
@@ -0,0 +1,238 @@
+
+Documentation for /proc/sys/vm/*	version 0.1
+	(c) 1998, Rik van Riel <H.H.vanRiel@fys.ruu.nl>
+
+For general info and legal blurb, please look in README.
+
+==============================================================
+
+This file contains the documentation for the sysctl files in
+/proc/sys/vm and is valid for Linux kernel version 2.1.
+
+The files in this directory can be used to tune the operation
+of the virtual memory (VM) subsystem of the Linux kernel, and
+one of the files (bdflush) also has a little influence on disk
+usage.
+
+Currently, these files are in /proc/sys/vm:
+- bdflush
+- freepages
+- overcommit_memory
+- swapctl
+- swapout_interval
+
+==============================================================
+
+bdflush:
+
+This file controls the operation of the bdflush kernel
+daemon. The source code to this struct can be found in
+linux/mm/buffer.c. It currently contains 9 integer values,
+of which 6 are actually used by the kernel.
+
+From linux/fs/buffer.c:
+--------------------------------------------------------------
+union bdflush_param{
+    struct {
+        int nfract;      /* Percentage of buffer cache dirty to
+                            activate bdflush */
+        int ndirty;      /* Maximum number of dirty blocks to
+                            write out per wake-cycle */
+        int nrefill;     /* Number of clean buffers to try to
+                            obtain each time we call refill */
+        int nref_dirt;   /* Dirty buffer threshold for activating
+                            bdflush when trying to refill buffers. */
+        int dummy1;      /* unused */
+        int age_buffer;  /* Time for normal buffer to age before
+                            we flush it */
+        int age_super;   /* Time for superblock to age before we
+                            flush it */
+        int dummy2;      /* unused */
+        int dummy3;      /* unused */
+    } b_un;
+    unsigned int data[N_PARAM];
+} bdf_prm = {{40, 500, 64, 256, 15, 30*HZ, 5*HZ, 1884, 2}};
+--------------------------------------------------------------
+
+The first parameter governs the maximum number of of dirty
+buffers in the buffer cache. Dirty means that the contents
+of the buffer still have to be written to disk (as opposed
+to a clean buffer, which can just be forgotten about).
+Setting this to a high value means that Linux can delay disk
+writes for a long time, but it also means that it will have
+to do a lot I/O at once when memory becomes short. A low
+value will spread out disk I/O more evenly.
+
+The second parameter (ndirty) gives the maximum number of
+dirty buffers that bdflush can write to the disk in one time.
+A high value will mean delayed, bursty I/O, while a small
+value can lead to memory shortage when bdflush isn't woken
+up often enough...
+
+The third parameter (nrefill) is the number of buffers that
+bdflush will add to the list of free buffers when
+refill_freelist() is called. It is necessary to allocate free
+buffers beforehand, since the buffers often are of a different
+size than memory pages and some bookkeeping needs to be done
+beforehand. The higher the number, the more memory will be
+wasted and the less often refill_freelist() will need to run.
+
+When refill_freelist() comes across more than nref_dirt dirty
+buffers, it will wake up bdflush.
+
+Finally, the age_buffer and age_super parameters govern the
+maximum time Linux waits before writing out a dirty buffer
+to disk. The value is expressed in jiffies (clockticks), the
+number of jiffies per second is 100, except on Alpha machines
+(1024). Age_buffer is the maximum age for data blocks, while
+age_super is for filesystem metadata.
+
+==============================================================
+
+freepages:
+
+This file contains three values: min_free_pages, free_pages_low
+and free_pages_high in order.
+
+These numbers are used by the VM subsystem to keep a reasonable
+number of pages on the free page list, so that programs can
+allocate new pages without having to wait for the system to
+free used pages first. The actual freeing of pages is done
+by kswapd, a kernel daemon.
+
+min_free_pages  -- when the number of free pages reaches this
+                   level, only the kernel can allocate memory
+                   for _critical_ tasks only
+free_pages_low  -- when the number of free pages drops below
+                   this level, kswapd is woken up immediately
+free_pages_high -- this is kswapd's target, when more than
+                   free_pages_high pages are free, kswapd will
+                   stop swapping.
+
+When the number of free pages is between free_pages_low and
+free_pages_high, and kswapd hasn't run for swapout_interval
+jiffies, then kswapd is woken up too. See swapout_interval
+for more info.
+
+When free memory is always low on your system, and kswapd has
+trouble keeping up with allocations, you might want to
+increase these values, especially free_pages_high and perhaps
+free_pages_low. I've found that a 1:2:4 relation for these
+values tend to work rather well in a heavily loaded system.
+
+==============================================================
+
+overcommit_memory:
+
+This file contains only one value. The following algorithm
+is used to decide if there's enough memory. If the value
+of overcommit_memory > 0, then there's always enough
+memory :-). This is a useful feature, since programs often
+malloc() huge amounts of memory 'just in case', while they
+only use a small part of it. Leaving this value at 0 will
+lead to the failure of such a huge malloc(), when in fact
+the system has enough memory for the program to run...
+On the other hand, enabling this feature can cause you to
+run out of memory and thrash the system to death, so large
+and/or important servers will want to set this value to 0.
+
+From linux/mm/mmap.c:
+--------------------------------------------------------------
+static inline int vm_enough_memory(long pages)
+{
+    /* Stupid algorithm to decide if we have enough memory: while
+     * simple, it hopefully works in most obvious cases.. Easy to
+     * fool it, but this should catch most mistakes.
+     */
+    long freepages;
+
+    /* Sometimes we want to use more memory than we have. */
+    if (sysctl_overcommit_memory)
+        return 1;
+
+    freepages = buffermem >> PAGE_SHIFT;
+    freepages += page_cache_size;
+    freepages >>= 1;
+    freepages += nr_free_pages;
+    freepages += nr_swap_pages;
+    freepages -= num_physpages >> 4;
+    return freepages > pages;
+}
+
+==============================================================
+
+swapctl:
+
+This file contains no less than 16 variables, of which about
+half is actually used :-) In the listing below, the unused
+variables are marked as such.
+All of these values are used by kswapd, and the usage can be
+found in linux/mm/vmscan.c.
+
+From linux/include/linux/swapctl.h:
+--------------------------------------------------------------
+typedef struct swap_control_v5
+{
+    unsigned int    sc_max_page_age;
+    unsigned int    sc_page_advance;
+    unsigned int    sc_page_decline;
+    unsigned int    sc_page_initial_age;
+    unsigned int    sc_max_buff_age;      /* unused */
+    unsigned int    sc_buff_advance;      /* unused */
+    unsigned int    sc_buff_decline;      /* unused */
+    unsigned int    sc_buff_initial_age;  /* unused */
+    unsigned int    sc_age_cluster_fract;
+    unsigned int    sc_age_cluster_min;
+    unsigned int    sc_pageout_weight;
+    unsigned int    sc_bufferout_weight;
+    unsigned int    sc_buffer_grace;      /* unused */
+    unsigned int    sc_nr_buffs_to_free;  /* unused */
+    unsigned int    sc_nr_pages_to_free;  /* unused */
+    enum RCL_POLICY sc_policy;            /* RCL_PERSIST hardcoded */
+} swap_control_v5;
+--------------------------------------------------------------
+
+The first four variables are used to keep track of Linux'
+page aging. Page aging is a bookkeeping method to keep track
+of which pages of memory are used often, and which pages can
+be swapped out without consequences.
+
+When a page is swapped in, it starts at sc_page_initial_age
+(default 3) and when the page is scanned by kswapd, it's age
+is adjusted according to the following scheme:
+- if the page was used since the last time we scanned, it's
+  age is increased sc_page_advance (default 3) up to a maximum
+  of sc_max_page_age (default 20)
+- else (it wasn't used) it's age is decreased sc_page_decline
+  (default 1)
+And when a page reaches age 0, it's ready to be swapped out.
+
+The variables sc_age_cluster_fract till sc_bufferout_weight
+have to do with the amount of scanning kswapd is doing on
+each call to try_to_swap_out().
+
+sc_age_cluster_fract is used to calculate how many pages from
+a process are to be scanned by kswapd. The formula used is
+sc_age_cluster_fract/1024 * RSS, so if you want kswapd to scan
+the whole process, sc_age_cluster_fract needs to have a value
+of 1024. The minimum number of pages kswapd will scan is
+represented by sc_age_cluster_min, this is done so kswapd will
+also scan small processes.
+
+The values of sc_pageout_weight and sc_bufferout_weight are
+used to control the how many tries kswapd will do in order
+to swapout one page / buffer. As with sc_age_cluster_fract,
+the actual value is calculated by several more or less complex
+formulae and the default value is good for every purpose.
+
+==============================================================
+
+swapout_interval:
+
+The single value in this file controls the amount of time
+between successive wakeups of kswapd when nr_free_pages is
+between free_pages_low and free_pages_high. The default value
+of HZ/4 is usually right, but when kswapd can't keep up with
+the number of allocations in your system, you might want to
+decrease this number. 
+
diff -u --recursive --new-file v2.1.88/linux/Documentation/transname.txt linux/Documentation/transname.txt
--- v2.1.88/linux/Documentation/transname.txt	Mon Jun 16 16:35:53 1997
+++ linux/Documentation/transname.txt	Thu Feb 26 11:01:24 1998
@@ -20,7 +20,7 @@
 filesystems at all clients, not at the server.
 
 Linux transname overcomes this problem by allowing filenames
-to be context-dependend. For example, if you have a file /etc/config
+to be context-dependent. For example, if you have a file /etc/config
 that should differ on the hosts "myserver" and "myclient", you just
 create two different files named /etc/config#host=myserver# and
 /etc/config#host=myclient# . On host "myserver", the file
diff -u --recursive --new-file v2.1.88/linux/MAINTAINERS linux/MAINTAINERS
--- v2.1.88/linux/MAINTAINERS	Tue Feb 17 13:12:43 1998
+++ linux/MAINTAINERS	Fri Mar  6 10:03:04 1998
@@ -373,6 +373,8 @@
 S:	Maintained
 
 NCP FILESYSTEM:
+P:	Petr Vandrovec
+M:	vandrove@vc.cvut.cz
 P:	Volker Lendecke
 M:	lendecke@Math.Uni-Goettingen.de
 L:	linware@sh.cvut.cz
@@ -478,7 +480,7 @@
 
 RISCOM8 DRIVER:
 P:	Dmitry Gorodchanin
-M:	begemot@bgm.rosprint.net
+M:	pgmdsg@ibi.com
 L:	linux-kernel@vger.rutgers.edu
 S:	Maintained
 
diff -u --recursive --new-file v2.1.88/linux/Makefile linux/Makefile
--- v2.1.88/linux/Makefile	Mon Feb 23 18:12:01 1998
+++ linux/Makefile	Mon Feb 23 18:10:59 1998
@@ -1,6 +1,6 @@
 VERSION = 2
 PATCHLEVEL = 1
-SUBLEVEL = 88
+SUBLEVEL = 89
 
 ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/)
 
diff -u --recursive --new-file v2.1.88/linux/arch/alpha/config.in linux/arch/alpha/config.in
--- v2.1.88/linux/arch/alpha/config.in	Tue Feb 17 13:12:44 1998
+++ linux/arch/alpha/config.in	Mon Feb 23 10:25:10 1998
@@ -46,6 +46,8 @@
 	 EB64+		CONFIG_ALPHA_EB64P		\
 	 EB164		CONFIG_ALPHA_EB164		\
 	 PC164		CONFIG_ALPHA_PC164		\
+	 LX164		CONFIG_ALPHA_LX164		\
+	 SX164		CONFIG_ALPHA_SX164		\
 	 Jensen		CONFIG_ALPHA_JENSEN		\
 	 Noname		CONFIG_ALPHA_NONAME		\
 	 Mikasa		CONFIG_ALPHA_MIKASA		\
@@ -54,6 +56,7 @@
 	 Miata		CONFIG_ALPHA_MIATA		\
 	 Sable		CONFIG_ALPHA_SABLE		\
 	 AlphaBook1	CONFIG_ALPHA_BOOK1		\
+	 Ruffian	CONFIG_ALPHA_RUFFIAN		\
 	 Platform2000	CONFIG_ALPHA_P2K" Cabriolet
 
 if [ "$CONFIG_ALPHA_BOOK1" = "y" ]
@@ -102,7 +105,8 @@
 	define_bool CONFIG_ALPHA_EV4 y
 	define_bool CONFIG_ALPHA_T2 y
 fi
-if [ "$CONFIG_ALPHA_MIATA" = "y" ]
+if [ "$CONFIG_ALPHA_MIATA" = "y" -o "$CONFIG_ALPHA_LX164" = "y" \
+	-o "$CONFIG_ALPHA_SX164" = "y" -o "$CONFIG_ALPHA_RUFFIAN" = "y" ]
 then
 	define_bool CONFIG_PCI y
 	define_bool CONFIG_ALPHA_EV5 y
@@ -122,7 +126,8 @@
 	-o "$CONFIG_ALPHA_EB64P" = "y" -o "$CONFIG_ALPHA_JENSEN" = "y" \
 	-o "$CONFIG_ALPHA_MIKASA" = "y" -o "$CONFIG_ALPHA_ALCOR" = "y" \
 	-o "$CONFIG_ALPHA_SABLE" = "y" -o "$CONFIG_ALPHA_MIATA" = "y" \
-	-o "$CONFIG_ALPHA_NORITAKE" = "y" -o "$CONFIG_ALPHA_PC164" = "y" ]
+	-o "$CONFIG_ALPHA_NORITAKE" = "y" -o "$CONFIG_ALPHA_PC164" = "y" \
+	-o "$CONFIG_ALPHA_LX164" = "y" -o "$CONFIG_ALPHA_SX164" = "y" ]
 then
 	bool 'Using SRM as bootloader' CONFIG_ALPHA_SRM
 fi
diff -u --recursive --new-file v2.1.88/linux/arch/alpha/kernel/Makefile linux/arch/alpha/kernel/Makefile
--- v2.1.88/linux/arch/alpha/kernel/Makefile	Tue Feb 17 13:12:44 1998
+++ linux/arch/alpha/kernel/Makefile	Mon Feb 23 10:25:10 1998
@@ -35,6 +35,13 @@
 ifdef CONFIG_ALPHA_T2
 O_OBJS   += t2.o
 endif
+ifneq ($(CONFIG_ALPHA_PC164)$(CONFIG_ALPHA_LX164),nn)
+O_OBJS   += smc37c93x.o
+endif
+ifdef CONFIG_ALPHA_SX164
+O_OBJS   += smc37c669.o
+endif
+
 
 all: kernel.o head.o
 
diff -u --recursive --new-file v2.1.88/linux/arch/alpha/kernel/bios32.c linux/arch/alpha/kernel/bios32.c
--- v2.1.88/linux/arch/alpha/kernel/bios32.c	Mon Jan 12 14:51:14 1998
+++ linux/arch/alpha/kernel/bios32.c	Mon Feb 23 10:25:10 1998
@@ -27,6 +27,8 @@
 #include <linux/kernel.h>
 #include <linux/smp.h>
 #include <linux/smp_lock.h>
+#include <linux/init.h>
+#include <asm/dma.h>
 
 #if 0
 # define DBG_DEVS(args)		printk args
@@ -98,8 +100,11 @@
 extern struct hwrpb_struct *hwrpb;
 
 /* Forward declarations for some extra fixup routines for specific hardware. */
-#ifdef CONFIG_ALPHA_PC164
-static int SMCInit(void);
+#if defined(CONFIG_ALPHA_PC164) || defined(CONFIG_ALPHA_LX164)
+extern int SMC93x_Init(void);
+#endif
+#ifdef CONFIG_ALPHA_SX164
+extern int SMC669_Init(void);
 #endif
 #ifdef CONFIG_ALPHA_MIATA
 static int es1888_init(void);
@@ -174,16 +179,25 @@
 	struct pci_bus *bus;
 	unsigned short cmd;
 
-#if defined(CONFIG_ALPHA_EISA)
+#ifdef CONFIG_ALPHA_EISA
 	/*
 	 * HACK: the PCI-to-EISA bridge does not seem to identify
 	 *       itself as a bridge... :-(
 	 */
-	if (dev->vendor == 0x8086 && dev->device == 0x0482) {
+	if (dev->vendor == PCI_VENDOR_ID_INTEL &&
+	    dev->device == PCI_DEVICE_ID_INTEL_82375) {
 		DBG_DEVS(("disable_dev: ignoring PCEB...\n"));
 		return;
 	}
 #endif
+#ifdef CONFIG_ALPHA_SX164
+	if (dev->vendor == PCI_VENDOR_ID_CONTAQ &&
+	    /* FIXME: We want a symbolic device name here.  */
+	    dev->device == 0xc693) {
+		DBG_DEVS(("disable_dev: ignoring CYPRESS bridge...\n"));
+		return;
+	}
+#endif
 
 	bus = dev->bus;
 	pcibios_read_config_word(bus->number, dev->devfn, PCI_COMMAND, &cmd);
@@ -206,6 +220,7 @@
 	unsigned int base, mask, size, reg;
 	unsigned int alignto;
 
+#ifdef CONFIG_ALPHA_EISA
 	/*
 	 * HACK: the PCI-to-EISA bridge does not seem to identify
 	 *       itself as a bridge... :-(
@@ -215,6 +230,14 @@
 		DBG_DEVS(("layout_dev: ignoring PCEB...\n"));
 		return;
 	}
+#endif
+#ifdef CONFIG_ALPHA_SX164
+	if (dev->vendor == PCI_VENDOR_ID_CONTAQ &&
+	    dev->device == 0xc693) {
+		DBG_DEVS(("layout_dev: ignoring CYPRESS bridge...\n"));
+		return;
+	}
+#endif
 
 	bus = dev->bus;
 	pcibios_read_config_word(bus->number, dev->devfn, PCI_COMMAND, &cmd);
@@ -729,6 +752,46 @@
 							   0x0000000);
 			}
 		}
+#ifdef CONFIG_ALPHA_SX164
+		/* If it the CYPRESS PCI-ISA bridge, disable IDE
+		   interrupt routing through PCI (ie do through PIC).  */
+		else if (dev->vendor == PCI_VENDOR_ID_CONTAQ &&
+			 dev->device == 0xc693 &&
+			 PCI_FUNC(dev->devfn) == 0) {
+			pcibios_write_config_word(dev->bus->number,
+						  dev->devfn, 0x04, 0x0007);
+
+			pcibios_write_config_byte(dev->bus->number,
+						  dev->devfn, 0x40, 0x80);
+			pcibios_write_config_byte(dev->bus->number,
+						  dev->devfn, 0x41, 0x80);
+			pcibios_write_config_byte(dev->bus->number,
+						  dev->devfn, 0x42, 0x80);
+			pcibios_write_config_byte(dev->bus->number,
+						  dev->devfn, 0x43, 0x80);
+			pcibios_write_config_byte(dev->bus->number,
+						  dev->devfn, 0x44, 0x27);
+			pcibios_write_config_byte(dev->bus->number,
+						  dev->devfn, 0x45, 0xe0);
+			pcibios_write_config_byte(dev->bus->number,
+						  dev->devfn, 0x48, 0xf0);
+			pcibios_write_config_byte(dev->bus->number,
+						  dev->devfn, 0x49, 0x40);
+			pcibios_write_config_byte(dev->bus->number,
+						  dev->devfn, 0x4a, 0x00);
+			pcibios_write_config_byte(dev->bus->number,
+						  dev->devfn, 0x4b, 0x80);
+			pcibios_write_config_byte(dev->bus->number,
+						  dev->devfn, 0x4c, 0x80);
+			pcibios_write_config_byte(dev->bus->number,
+						  dev->devfn, 0x4d, 0x70);
+
+			outb(0, DMA1_RESET_REG);
+			outb(0, DMA2_RESET_REG);
+			outb(DMA_MODE_CASCADE, DMA2_MODE_REG);
+			outb(0, DMA2_MASK_REG);
+		}
+#endif /* SX164 */
 	}
 	if (ide_base) {
 		enable_ide(ide_base);
@@ -750,7 +813,7 @@
  */
 static inline void eb66p_fixup(void)
 {
-	static char irq_tab[5][5] = {
+	static char irq_tab[5][5] __initdata = {
 		{16+0, 16+0, 16+5,  16+9, 16+13},  /* IdSel 6,  slot 0, J25 */
 		{16+1, 16+1, 16+6, 16+10, 16+14},  /* IdSel 7,  slot 1, J26 */
 		{  -1,   -1,   -1,    -1,    -1},  /* IdSel 8,  SIO         */
@@ -762,7 +825,7 @@
 
 
 /*
- * The PC164 has 19 PCI interrupts, four from each of the four PCI
+ * The PC164/LX164 has 19 PCI interrupts, four from each of the four PCI
  * slots, the SIO, PCI/IDE, and USB.
  * 
  * Each of the interrupts can be individually masked. This is
@@ -803,10 +866,10 @@
  * 
  */
 
-#ifdef CONFIG_ALPHA_PC164
+#if defined(CONFIG_ALPHA_PC164) || defined(CONFIG_ALPHA_LX164)
 static inline void alphapc164_fixup(void)
 {
-	static char irq_tab[7][5] = {
+	static char irq_tab[7][5] __initdata = {
 		/*INT   INTA  INTB   INTC   INTD */
 		{ 16+2, 16+2, 16+9,  16+13, 16+17}, /* IdSel  5, slot 2, J20 */
 		{ 16+0, 16+0, 16+7,  16+11, 16+15}, /* IdSel  6, slot 0, J29 */
@@ -818,7 +881,7 @@
 	};
 
 	common_fixup(5, 11, 5, irq_tab, 0);
-	SMCInit();
+	SMC93x_Init();
 }
 #endif
 
@@ -837,7 +900,7 @@
  */
 static inline void cabriolet_fixup(void)
 {
-	static char irq_tab[5][5] = {
+	static char irq_tab[5][5] __initdata = {
 		{ 16+2, 16+2, 16+7, 16+11, 16+15}, /* IdSel 5,  slot 2, J21 */
 		{ 16+0, 16+0, 16+5,  16+9, 16+13}, /* IdSel 6,  slot 0, J19 */
 		{ 16+1, 16+1, 16+6, 16+10, 16+14}, /* IdSel 7,  slot 1, J20 */
@@ -893,7 +956,7 @@
  */
 static inline void eb66_and_eb64p_fixup(void)
 {
-	static char irq_tab[5][5] = {
+	static char irq_tab[5][5] __initdata = {
 		{16+7, 16+7, 16+7, 16+7,  16+7},  /* IdSel 5,  slot ?, ?? */
 		{16+0, 16+0, 16+2, 16+4,  16+9},  /* IdSel 6,  slot ?, ?? */
 		{16+1, 16+1, 16+3, 16+8, 16+10},  /* IdSel 7,  slot ?, ?? */
@@ -942,7 +1005,7 @@
  */
 static inline void mikasa_fixup(void)
 {
-	static char irq_tab[8][5] = {
+	static char irq_tab[8][5] __initdata = {
 		/*INT    INTA   INTB   INTC   INTD */
 		{16+12, 16+12, 16+12, 16+12, 16+12},	/* IdSel 17,  SCSI */
 		{   -1,    -1,    -1,    -1,    -1},	/* IdSel 18,  PCEB */
@@ -1013,7 +1076,7 @@
  */
 static inline void noritake_fixup(void)
 {
-	static char irq_tab[13][5] = {
+	static char irq_tab[13][5] __initdata = {
 		/*INT    INTA   INTB   INTC   INTD */
 		{   -1,    -1,    -1,    -1,    -1},  /* IdSel 18,  PCEB */
 		{   -1,    -1,    -1,    -1,    -1},  /* IdSel 19,  PPB  */
@@ -1077,7 +1140,7 @@
  */
 static inline void alcor_fixup(void)
 {
-	static char irq_tab[6][5] = {
+	static char irq_tab[6][5] __initdata = {
 		/*INT    INTA   INTB   INTC   INTD */
 		{ 16+8,  16+8,  16+9, 16+10, 16+11},	/* IdSel 18,  slot 0 */
 		{16+16, 16+16, 16+17, 16+18, 16+19},	/* IdSel 19,  slot 3 */
@@ -1132,7 +1195,7 @@
  */
 static inline void xlt_fixup(void)
 {
-	static char irq_tab[7][5] = {
+	static char irq_tab[7][5] __initdata = {
 		/*INT    INTA   INTB   INTC   INTD */
 		{16+13, 16+13, 16+13, 16+13, 16+13},	/* IdSel 17,  TULIP  */
 		{ 16+8,  16+8,  16+9, 16+10, 16+11},	/* IdSel 18,  slot 0 */
@@ -1194,13 +1257,16 @@
  * above for PCI interrupts.  The IRQ relates to which bit the interrupt
  * comes in on.  This makes interrupt processing much easier.
  */
-/* NOTE: the IRQ assignments below are arbitrary, but need to be consistent
-   with the values in the sable_irq_to_mask[] and sable_mask_to_irq[] tables
-   in irq.c
+/*
+ * NOTE: the IRQ assignments below are arbitrary, but need to be consistent
+ * with the values in the sable_irq_to_mask[] and sable_mask_to_irq[] tables
+ * in irq.c
  */
+
+#ifdef CONFIG_ALPHA_SABLE
 static inline void sable_fixup(void)
 {
-        static char irq_tab[9][5] = {
+        static char irq_tab[9][5] __initdata = {
 		/*INT    INTA   INTB   INTC   INTD */
 		{ 32+0,  32+0,  32+0,  32+0,  32+0},  /* IdSel 0,  TULIP  */
 		{ 32+1,  32+1,  32+1,  32+1,  32+1},  /* IdSel 1,  SCSI   */
@@ -1214,6 +1280,7 @@
         };
         common_fixup(0, 8, 5, irq_tab, 0);
 }
+#endif
 
 /*
  * Fixup configuration for MIATA (EV56+PYXIS)
@@ -1282,7 +1349,7 @@
 #ifdef CONFIG_ALPHA_MIATA
 static inline void miata_fixup(void)
 {
-        static char irq_tab[18][5] = {
+        static char irq_tab[18][5] __initdata = {
 		/*INT    INTA   INTB   INTC   INTD */
 		{16+ 8, 16+ 8, 16+ 8, 16+ 8, 16+ 8},  /* IdSel 14,  DC21142  */
 		{   -1,    -1,    -1,    -1,    -1},  /* IdSel 15,  EIDE   */
@@ -1311,6 +1378,64 @@
 #endif
 
 /*
+ * Fixup configuration for SX164 (PCA56+PYXIS)
+ *
+ * Summary @ PYXIS_INT_REQ:
+ * Bit      Meaning
+ * 0        RSVD
+ * 1        NMI
+ * 2        Halt/Reset switch
+ * 3        MBZ
+ * 4        RAZ
+ * 5        RAZ
+ * 6        Interval timer (RTC)
+ * 7        PCI-ISA Bridge
+ * 8        Interrupt Line A from slot 3
+ * 9        Interrupt Line A from slot 2
+ *10        Interrupt Line A from slot 1
+ *11        Interrupt Line A from slot 0
+ *12        Interrupt Line B from slot 3
+ *13        Interrupt Line B from slot 2
+ *14        Interrupt Line B from slot 1
+ *15        Interrupt line B from slot 0
+ *16        Interrupt Line C from slot 3
+
+ *17        Interrupt Line C from slot 2
+ *18        Interrupt Line C from slot 1
+ *19        Interrupt Line C from slot 0
+ *20        Interrupt Line D from slot 3
+ *21        Interrupt Line D from slot 2
+ *22        Interrupt Line D from slot 1
+ *23        Interrupt Line D from slot 0
+ *
+ * IdSel       
+ *   5  32 bit PCI option slot 2
+ *   6  64 bit PCI option slot 0
+ *   7  64 bit PCI option slot 1
+ *   8  Cypress I/O
+ *   9  32 bit PCI option slot 3
+ * 
+ */
+
+#ifdef CONFIG_ALPHA_SX164
+static inline void sx164_fixup(void)
+{
+	static char irq_tab[5][5] __initdata = {
+		/*INT    INTA   INTB   INTC   INTD */
+		{ 16+ 9, 16+ 9, 16+13, 16+17, 16+21}, /* IdSel 5 slot 2 J17 */
+		{ 16+11, 16+11, 16+15, 16+19, 16+23}, /* IdSel 6 slot 0 J19 */
+		{ 16+10, 16+10, 16+14, 16+18, 16+22}, /* IdSel 7 slot 1 J18 */
+		{    -1,    -1,    -1,	  -1,    -1}, /* IdSel 8 SIO        */
+		{ 16+ 8, 16+ 8, 16+12, 16+16, 16+20}  /* IdSel 9 slot 3 J15 */
+	};
+
+	common_fixup(5, 9, 5, irq_tab, 0);
+
+	SMC669_Init();
+}
+#endif
+
+/*
  * Fixup configuration for all boards that route the PCI interrupts
  * through the SIO PCI/ISA bridge.  This includes Noname (AXPpci33),
  * Avanti (AlphaStation) and Kenetics's Platform 2000.
@@ -1336,7 +1461,7 @@
 	 * that they use the default INTA line, if they are interrupt
 	 * driven at all).
 	 */
-	static const char pirq_tab[][5] = {
+	static const char pirq_tab[][5] __initdata = {
 #ifdef CONFIG_ALPHA_P2K
 		{ 0,  0, -1, -1, -1}, /* idsel  6 (53c810) */
 		{-1, -1, -1, -1, -1}, /* idsel  7 (SIO: PCI/ISA bridge) */
@@ -1560,9 +1685,10 @@
 extern void tga_console_init(void);
 #endif /* CONFIG_TGA_CONSOLE */
 
-unsigned long pcibios_fixup(unsigned long mem_start, unsigned long mem_end)
+unsigned long __init
+pcibios_fixup(unsigned long mem_start, unsigned long mem_end)
 {
-#if PCI_MODIFY
+#if PCI_MODIFY && !defined(CONFIG_ALPHA_RUFFIAN)
 	/*
 	 * Scan the tree, allocating PCI memory and I/O space.
 	 */
@@ -1577,7 +1703,7 @@
 	sio_fixup();
 #elif defined(CONFIG_ALPHA_CABRIOLET) || defined(CONFIG_ALPHA_EB164)
 	cabriolet_fixup();
-#elif defined(CONFIG_ALPHA_PC164)
+#elif defined(CONFIG_ALPHA_PC164) || defined(CONFIG_ALPHA_LX164)
 	alphapc164_fixup();
 #elif defined(CONFIG_ALPHA_EB66P)
 	eb66p_fixup();
@@ -1597,6 +1723,10 @@
 	miata_fixup();
 #elif defined(CONFIG_ALPHA_NORITAKE)
 	noritake_fixup();
+#elif defined(CONFIG_ALPHA_SX164)
+	sx164_fixup();
+#elif defined(CONFIG_ALPHA_RUFFIAN)
+	/* no fixup needed */
 #else
 # error "You must tell me what kind of platform you want."
 #endif
@@ -1702,251 +1832,12 @@
 }
 
 
-#ifdef CONFIG_ALPHA_PC164
-/* device "activate" register contents */
-#define DEVICE_ON		1
-#define DEVICE_OFF		0
-
-/* configuration on/off keys */
-#define CONFIG_ON_KEY		0x55
-#define CONFIG_OFF_KEY		0xaa
-
-/* configuration space device definitions */
-#define FDC			0
-#define IDE1			1
-#define IDE2			2
-#define PARP			3
-#define SER1			4
-#define SER2			5
-#define RTCL			6
-#define KYBD			7
-#define AUXIO			8
-
-/* Chip register offsets from base */
-#define CONFIG_CONTROL		0x02
-#define INDEX_ADDRESS		0x03
-#define LOGICAL_DEVICE_NUMBER	0x07
-#define DEVICE_ID		0x20
-#define DEVICE_REV		0x21
-#define POWER_CONTROL		0x22
-#define POWER_MGMT		0x23
-#define OSC			0x24
-
-#define ACTIVATE		0x30
-#define ADDR_HI			0x60
-#define ADDR_LO			0x61
-#define INTERRUPT_SEL		0x70
-#define INTERRUPT_SEL_2		0x72 /* KYBD/MOUS only */
-#define DMA_CHANNEL_SEL		0x74 /* FDC/PARP only */
-
-#define FDD_MODE_REGISTER	0x90
-#define FDD_OPTION_REGISTER	0x91
-
-/* values that we read back that are expected ... */
-#define VALID_DEVICE_ID		2
-
-/* default device addresses */
-#define KYBD_INTERRUPT		1
-#define MOUS_INTERRUPT		12
-#define COM2_BASE		0x2f8
-#define COM2_INTERRUPT		3
-#define COM1_BASE		0x3f8
-#define COM1_INTERRUPT		4
-#define PARP_BASE		0x3bc
-#define PARP_INTERRUPT		7
-
-#define SMC_DEBUG 0
-
-static unsigned long SMCConfigState(unsigned long baseAddr)
-{
-	unsigned char devId;
-	unsigned char devRev;
-
-	unsigned long configPort;
-	unsigned long indexPort;
-	unsigned long dataPort;
-
-	configPort = indexPort = baseAddr;
-	dataPort = configPort + 1;
-
-	outb(CONFIG_ON_KEY, configPort);
-	outb(CONFIG_ON_KEY, configPort);
-	outb(DEVICE_ID, indexPort);
-	devId = inb(dataPort);
-	if ( devId == VALID_DEVICE_ID ) {
-		outb(DEVICE_REV, indexPort);
-		devRev = inb(dataPort);
-	}
-	else {
-		baseAddr = 0;
-	}
-	return baseAddr;
-}
-
-static void SMCRunState(unsigned long baseAddr)
-{
-	outb(CONFIG_OFF_KEY, baseAddr);
-}
-
-static unsigned long SMCDetectUltraIO(void)
-{
-	unsigned long baseAddr;
-
-	baseAddr = 0x3F0;
-	if ( ( baseAddr = SMCConfigState( baseAddr ) ) == 0x3F0 ) {
-		return( baseAddr );
-	}
-	baseAddr = 0x370;
-	if ( ( baseAddr = SMCConfigState( baseAddr ) ) == 0x370 ) {
-		return( baseAddr );
-	}
-	return( ( unsigned long )0 );
-}
-
-static void SMCEnableDevice(unsigned long baseAddr,
-			    unsigned long device,
-			    unsigned long portaddr,
-			    unsigned long interrupt)
-{
-	unsigned long indexPort;
-	unsigned long dataPort;
-
-	indexPort = baseAddr;
-	dataPort = baseAddr + 1;
-
-	outb(LOGICAL_DEVICE_NUMBER, indexPort);
-	outb(device, dataPort);
-
-	outb(ADDR_LO, indexPort);
-	outb(( portaddr & 0xFF ), dataPort);
-
-	outb(ADDR_HI, indexPort);
-	outb((portaddr >> 8) & 0xFF, dataPort);
-
-	outb(INTERRUPT_SEL, indexPort);
-	outb(interrupt, dataPort);
-
-	outb(ACTIVATE, indexPort);
-	outb(DEVICE_ON, dataPort);
-}
-
-static void SMCEnableKYBD(unsigned long baseAddr)
-{
-	unsigned long indexPort;
-	unsigned long dataPort;
-
-	indexPort = baseAddr;
-	dataPort = baseAddr + 1;
-
-	outb(LOGICAL_DEVICE_NUMBER, indexPort);
-	outb(KYBD, dataPort);
-
-	outb(INTERRUPT_SEL, indexPort); /* Primary interrupt select */
-	outb(KYBD_INTERRUPT, dataPort);
-
-	outb(INTERRUPT_SEL_2, indexPort); /* Secondary interrupt select */
-	outb(MOUS_INTERRUPT, dataPort);
-
-	outb(ACTIVATE, indexPort);
-	outb(DEVICE_ON, dataPort);
-}
-
-static void SMCEnableFDC(unsigned long baseAddr)
-{
-	unsigned long indexPort;
-	unsigned long dataPort;
-
-	unsigned char oldValue;
-
-	indexPort = baseAddr;
-	dataPort = baseAddr + 1;
-
-	outb(LOGICAL_DEVICE_NUMBER, indexPort);
-	outb(FDC, dataPort);
-
-	outb(FDD_MODE_REGISTER, indexPort);
-	oldValue = inb(dataPort);
-
-	oldValue |= 0x0E;                   /* Enable burst mode */
-	outb(oldValue, dataPort);
-
-	outb(INTERRUPT_SEL, indexPort);	    /* Primary interrupt select */
-	outb(0x06, dataPort );
-
-	outb(DMA_CHANNEL_SEL, indexPort);   /* DMA channel select */
-	outb(0x02, dataPort);
-
-	outb(ACTIVATE, indexPort);
-	outb(DEVICE_ON, dataPort);
-}
-
-#if SMC_DEBUG
-static void SMCReportDeviceStatus(unsigned long baseAddr)
-{
-	unsigned long indexPort;
-	unsigned long dataPort;
-	unsigned char currentControl;
-
-	indexPort = baseAddr;
-	dataPort = baseAddr + 1;
-
-	outb(POWER_CONTROL, indexPort);
-	currentControl = inb(dataPort);
-
-	printk(currentControl & (1 << FDC)
-	       ? "\t+FDC Enabled\n" : "\t-FDC Disabled\n");
-	printk(currentControl & (1 << IDE1)
-	       ? "\t+IDE1 Enabled\n" : "\t-IDE1 Disabled\n");
-	printk(currentControl & (1 << IDE2)
-	       ? "\t+IDE2 Enabled\n" : "\t-IDE2 Disabled\n");
-	printk(currentControl & (1 << PARP)
-	       ? "\t+PARP Enabled\n" : "\t-PARP Disabled\n");
-	printk(currentControl & (1 << SER1)
-	       ? "\t+SER1 Enabled\n" : "\t-SER1 Disabled\n");
-	printk(currentControl & (1 << SER2)
-	       ? "\t+SER2 Enabled\n" : "\t-SER2 Disabled\n");
-
-	printk( "\n" );
-}
-#endif
-
-static int SMCInit(void)
-{
-	unsigned long SMCUltraBase;
-
-	if ((SMCUltraBase = SMCDetectUltraIO()) != 0UL) {
-		printk("SMC FDC37C93X Ultra I/O Controller found @ 0x%lx\n",
-		       SMCUltraBase);
-#if SMC_DEBUG
-		SMCReportDeviceStatus(SMCUltraBase);
-#endif
-		SMCEnableDevice(SMCUltraBase, SER1, COM1_BASE, COM1_INTERRUPT);
-		SMCEnableDevice(SMCUltraBase, SER2, COM2_BASE, COM2_INTERRUPT);
-		SMCEnableDevice(SMCUltraBase, PARP, PARP_BASE, PARP_INTERRUPT);
-		/* On PC164, IDE on the SMC is not enabled;
-		   CMD646 (PCI) on MB */
-		SMCEnableKYBD(SMCUltraBase);
-		SMCEnableFDC(SMCUltraBase);
-#if SMC_DEBUG
-		SMCReportDeviceStatus(SMCUltraBase);
-#endif
-		SMCRunState(SMCUltraBase);
-		return 1;
-	}
-	else {
-#if SMC_DEBUG
-		printk("No SMC FDC37C93X Ultra I/O Controller found\n");
-#endif
-		return 0;
-	}
-}
-#endif /* CONFIG_ALPHA_PC164 */
-
 #ifdef CONFIG_ALPHA_MIATA
 /*
  * Init the built-in ES1888 sound chip (SB16 compatible)
  */
-static int es1888_init(void)
+static int __init
+es1888_init(void)
 {
 	/* Sequence of IO reads to init the audio controller */
 	inb(0x0229);
diff -u --recursive --new-file v2.1.88/linux/arch/alpha/kernel/entry.S linux/arch/alpha/kernel/entry.S
--- v2.1.88/linux/arch/alpha/kernel/entry.S	Tue Feb 17 13:12:44 1998
+++ linux/arch/alpha/kernel/entry.S	Sun Mar  1 10:23:16 1998
@@ -318,6 +318,7 @@
 	stt $f29,296($30)
 	stt $f30,304($30)
 	stt $f0,312($30)	# save fpcr in slot of $f31
+	ldt $f0,64($30)		# don't let "do_switch_stack" change any fp state.
 	ret $31,($1),1
 .end do_switch_stack
 
@@ -1109,7 +1110,7 @@
 	.quad sys_utimes
 	.quad sys_getrusage
 	.quad sys_wait4				/* 365 */
-	.quad sys_ni_syscall
+	.quad sys_adjtimex
 	.quad sys_ni_syscall
 	.quad sys_ni_syscall
 	.quad sys_ni_syscall			/* 369 */
diff -u --recursive --new-file v2.1.88/linux/arch/alpha/kernel/irq.c linux/arch/alpha/kernel/irq.c
--- v2.1.88/linux/arch/alpha/kernel/irq.c	Mon Feb 23 18:12:01 1998
+++ linux/arch/alpha/kernel/irq.c	Thu Feb 26 11:05:49 1998
@@ -19,6 +19,7 @@
 #include <linux/interrupt.h>
 #include <linux/malloc.h>
 #include <linux/random.h>
+#include <linux/init.h>
 
 #include <asm/system.h>
 #include <asm/io.h>
@@ -47,6 +48,9 @@
 #elif defined(CONFIG_ALPHA_ALCOR)
   /* always mask out unused timer irq 0, "irqs" 20-30, and the EISA cascade: */
 # define PROBE_MASK (((1UL << NR_IRQS) - 1) & ~0xfff000000001UL)
+#elif defined(CONFIG_ALPHA_RUFFIAN)
+  /* must leave timer irq 0 in the mask */
+# define PROBE_MASK ((1UL << NR_IRQS) - 1)
 #else
   /* always mask out unused timer irq 0: */
 # define PROBE_MASK (((1UL << NR_IRQS) - 1) & ~1UL)
@@ -210,6 +214,53 @@
 	}
 }
 
+#ifdef CONFIG_ALPHA_RUFFIAN
+static inline void
+ruffian_update_hw(unsigned long irq, unsigned long mask)
+{
+	switch (irq) {
+	case 16 ... 47:
+		/* Note inverted sense of mask bits: */
+		/* Make CERTAIN none of the bogus ints get enabled... */
+		*(vulp)PYXIS_INT_MASK =
+			~((long)mask >> 16) & 0x00000000ffffffbfUL;
+		mb();
+		/* ... and read it back to make sure it got written.  */
+		*(vulp)PYXIS_INT_MASK;
+		break;
+	case  8 ... 15: /* ISA PIC2 */
+		outb(mask >> 8, 0xA1);
+		break;
+	case  0 ... 7: /* ISA PIC1 */
+		outb(mask, 0x21);
+		break;
+	}
+}
+#endif
+
+#ifdef CONFIG_ALPHA_SX164
+static inline void
+sx164_update_hw(unsigned long irq, unsigned long mask)
+{
+	switch (irq) {
+	case 16 ... 39:
+		/* Make CERTAIN none of the bogus ints get enabled */
+		*(vulp)PYXIS_INT_MASK =
+			~((long)mask >> 16) & ~0x000000000000003bUL;
+		mb();
+		/* ... and read it back to make sure it got written.  */
+		*(vulp)PYXIS_INT_MASK;
+		break;
+	case  8 ... 15: /* ISA PIC2 */
+		outb(mask >> 8, 0xA1);
+		break;
+	case  0 ... 7: /* ISA PIC1 */
+		outb(mask, 0x21);
+		break;
+	}
+}
+#endif
+
 /* Unlabeled mechanisms based on the number of irqs.  Someone should
    probably document and name these.  */
 
@@ -262,7 +313,8 @@
 	}
 }
 
-#if defined(CONFIG_ALPHA_PC164) && defined(CONFIG_ALPHA_SRM)
+#if (defined(CONFIG_ALPHA_PC164) || defined(CONFIG_ALPHA_LX164)) \
+	&& defined(CONFIG_ALPHA_SRM)
 /*
  * On the pc164, we cannot take over the IRQs from the SRM, 
  * so we call down to do our dirty work.  Too bad the SRM
@@ -313,6 +365,10 @@
 	alcor_and_xlt_update_hw(irq, mask);
 #elif defined(CONFIG_ALPHA_MIKASA)
 	mikasa_update_hw(irq, mask);
+#elif defined(CONFIG_ALPHA_SX164)
+	sx164_update_hw(irq, mask);
+#elif defined(CONFIG_ALPHA_RUFFIAN)
+	ruffian_update_hw(irq, mask);
 #elif NR_IRQS == 33
 	update_hw_33(irq, mask);
 #elif NR_IRQS == 32
@@ -355,7 +411,7 @@
 	unmask_irq(IRQ_TO_MASK(irq_nr));
 	restore_flags(flags);
 }
-#endif /* PC164 && SRM */
+#endif /* (PC164 || LX164) && SRM */
 
 /*
  * Initial irq handlers.
@@ -404,9 +460,23 @@
 		outb(0xE0 | 4, 0x534); /* slave 2 */
 		break;
 	}
-#else /* CONFIG_ALPHA_SABLE */
+#elif defined(CONFIG_ALPHA_RUFFIAN)
 	if (irq < 16) {
-		/* ACK the interrupt making it the lowest priority */
+		/* Ack PYXIS ISA interrupt.  */
+		*(vulp)PYXIS_INT_REQ = 1 << 7;
+		mb();
+		if (irq > 7) {
+			outb(0x20, 0xa0);
+		}
+		outb(0x20, 0x20);
+	} else {
+		/* Ack PYXIS interrupt.  */
+		*(vulp)PYXIS_INT_REQ = (1UL << (irq - 16));
+		mb();
+	}
+#else
+	if (irq < 16) {
+		/* Ack the interrupt making it the lowest priority */
 		/*  First the slave .. */
 		if (irq > 7) {
 			outb(0xE0 | (irq - 8), 0xa0);
@@ -418,9 +488,9 @@
 		/* on ALCOR/XLT, need to dismiss interrupt via GRU */
 		*(vuip)GRU_INT_CLEAR = 0x80000000; mb();
 		*(vuip)GRU_INT_CLEAR = 0x00000000; mb();
-#endif /* ALCOR || XLT */
+#endif
 	}
-#endif /* CONFIG_ALPHA_SABLE */
+#endif
 }
 
 int check_irq(unsigned int irq)
@@ -830,7 +900,7 @@
 	restore_flags(flags);
 }
 
-#if defined(CONFIG_ALPHA_MIATA)
+#if defined(CONFIG_ALPHA_MIATA) || defined(CONFIG_ALPHA_SX164)
 /* We have to conditionally compile this because of PYXIS_xxx symbols */
 static inline void 
 miata_device_interrupt(unsigned long vector, struct pt_regs *regs)
@@ -850,15 +920,19 @@
 	       *(vulp)PYXIS_INT_MASK, inb(0x20) | (inb(0xA0) << 8));
 #endif
 
-#if 1
-	/*
-	 * For now, AND off any bits we are not interested in:
-	 *   HALT (2), timer (6), ISA Bridge (7), 21142/3 (8)
-	 * then all the PCI slots/INTXs (12-31).
-	 */
+	/* For now, AND off any bits we are not interested in.  */
+#if defined(CONFIG_ALPHA_MIATA)
+	/* HALT (2), timer (6), ISA Bridge (7), 21142/3 (8),
+	   then all the PCI slots/INTXs (12-31).  */
 	/* Maybe HALT should only be used for SRM console boots? */
 	pld &= 0x00000000fffff1c4UL;
 #endif
+#if defined(CONFIG_ALPHA_SX164)
+	/* HALT (2), timer (6), ISA Bridge (7),
+	   then all the PCI slots/INTXs (8-23).  */
+	/* HALT should only be used for SRM console boots.  */
+	pld &= 0x0000000000ffffc0UL;
+#endif
 
 	/*
 	 * Now for every possible bit set, work through them and call
@@ -879,7 +953,7 @@
 	}
 	restore_flags(flags);
 }
-#endif /* MIATA */
+#endif /* MIATA || SX164 */
 
 static inline void 
 noritake_device_interrupt(unsigned long vector, struct pt_regs *regs)
@@ -917,6 +991,69 @@
 	restore_flags(flags);
 }
 
+#if defined(CONFIG_ALPHA_RUFFIAN)
+static inline void
+ruffian_device_interrupt(unsigned long vector, struct pt_regs *regs)
+{
+	unsigned long pld;
+	unsigned int i;
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+
+	/* Read the interrupt summary register of PYXIS */
+	pld = *(vulp)PYXIS_INT_REQ;
+
+	/* For now, AND off any bits we are not interested in:
+	 * HALT (2), timer (6), ISA Bridge (7), 21142 (8)
+	 * then all the PCI slots/INTXs (12-31) 
+	 * flash(5) :DWH:
+	 */
+	pld &= 0x00000000ffffff9fUL;
+
+	/*
+	 * Now for every possible bit set, work through them and call
+	 * the appropriate interrupt handler.
+	 */
+	while (pld) {
+		i = ffz(~pld);
+		pld &= pld - 1; /* clear least bit set */
+
+		if (i == 7) {
+			/* Copy this bit from isa_device_interrupt cause
+			   we need to hook into int 0 for the timer.  I
+			   refuse to soil device_interrupt with ifdefs.  */
+
+			/* Generate a PCI interrupt acknowledge cycle.
+			   The PIC will respond with the interrupt
+			   vector of the highest priority interrupt
+			   that is pending.  The PALcode sets up the
+			   interrupts vectors such that irq level L
+			   generates vector L.  */
+
+			unsigned int j = *(vuip)PYXIS_IACK_SC & 0xff;
+			if (j == 7 && !(inb(0x20) & 0x80)) {
+				/* It's only a passive release... */
+			} else if (j == 0) {
+				timer_interrupt(regs);
+				ack_irq(0);
+			} else {
+				device_interrupt(j, j, regs);
+			}
+		} else {
+			device_interrupt(16 + i, 16 + i, regs);
+		}
+
+		*(vulp)PYXIS_INT_REQ = 1UL << i;
+		mb();
+		*(vulp)PYXIS_INT_REQ;
+	}
+
+	restore_flags(flags);
+}
+#endif /* RUFFIAN */
+
 #endif /* CONFIG_PCI */
 
 /*
@@ -1119,16 +1256,18 @@
 #if defined(CONFIG_ALPHA_JENSEN) || defined(CONFIG_ALPHA_NONAME) || \
     defined(CONFIG_ALPHA_P2K) || defined(CONFIG_ALPHA_SRM)
 		srm_device_interrupt(vector, &regs);
-#elif defined(CONFIG_ALPHA_MIATA)
+#elif defined(CONFIG_ALPHA_MIATA) || defined(CONFIG_ALPHA_SX164)
 		miata_device_interrupt(vector, &regs);
 #elif defined(CONFIG_ALPHA_NORITAKE)
 		noritake_device_interrupt(vector, &regs);
 #elif defined(CONFIG_ALPHA_ALCOR) || defined(CONFIG_ALPHA_XLT)
 		alcor_and_xlt_device_interrupt(vector, &regs);
-#elif NR_IRQS == 33
-		cabriolet_and_eb66p_device_interrupt(vector, &regs);
+#elif defined(CONFIG_ALPHA_RUFFIAN)
+		ruffian_device_interrupt(vector, &regs);
 #elif defined(CONFIG_ALPHA_MIKASA)
 		mikasa_device_interrupt(vector, &regs);
+#elif NR_IRQS == 33
+		cabriolet_and_eb66p_device_interrupt(vector, &regs);
 #elif NR_IRQS == 32
 		eb66_and_eb64p_device_interrupt(vector, &regs);
 #elif NR_IRQS == 16
@@ -1154,6 +1293,57 @@
 	outb(0x44, 0x535);		/* enable cascades in master */
 }
 
+#ifdef CONFIG_ALPHA_SX164
+static inline void sx164_init_IRQ(void)
+{
+	/* note invert on MASK bits */
+	*(vulp)PYXIS_INT_MASK  = ~((long)irq_mask >> 16); mb();
+#if 0
+	*(vulp)PYXIS_INT_HILO  = 0x000000B2UL; mb(); /* ISA/NMI HI */
+	*(vulp)PYXIS_RT_COUNT  = 0UL; mb(); /* clear count */
+#endif
+	enable_irq(16 + 6);	/* enable timer */
+	enable_irq(16 + 7);	/* enable ISA PIC cascade */
+	enable_irq(2);		/* enable cascade */
+}
+#endif /* SX164 */
+
+#ifdef CONFIG_ALPHA_RUFFIAN
+static inline void ruffian_init_IRQ(void)
+{
+	/* invert 6&7 for i82371 */
+	*(vulp)PYXIS_INT_HILO  = 0x000000c0UL; mb();
+	*(vulp)PYXIS_INT_CNFG  = 0x00002064UL; mb();	 /* all clear */
+	*(vulp)PYXIS_INT_MASK  = 0x00000000UL; mb();
+	*(vulp)PYXIS_INT_REQ   = 0xffffffffUL; mb();
+
+	outb(0x11,0xA0);
+	outb(0x08,0xA1);
+	outb(0x02,0xA1);
+	outb(0x01,0xA1);
+	outb(0xFF,0xA1);
+	
+	outb(0x11,0x20);
+	outb(0x00,0x21);
+	outb(0x04,0x21);
+	outb(0x01,0x21);
+	outb(0xFF,0x21);
+	
+	/* Send -INTA pulses to clear any pending interrupts ...*/
+	*(vuip) IACK_SC;
+
+	/* Finish writing the 82C59A PIC Operation Control Words */
+	outb(0x20,0xA0);
+	outb(0x20,0x20);
+	
+	/* Turn on the interrupt controller, the timer interrupt  */
+	enable_irq(16 + 7);	/* enable ISA PIC cascade */
+	enable_irq(0);		/* enable timer */
+	enable_irq(2);		/* enable 2nd PIC cascade */
+}
+#endif /* RUFFIAN */
+
+
 #ifdef CONFIG_ALPHA_MIATA
 static inline void miata_init_IRQ(void)
 {
@@ -1219,27 +1409,36 @@
 	enable_irq(2);			/* enable cascade */
 }
 
-void init_IRQ(void)
+void __init
+init_IRQ(void)
 {
 	wrent(entInt, 0);
 	dma_outb(0, DMA1_RESET_REG);
 	dma_outb(0, DMA2_RESET_REG);
+#ifndef CONFIG_ALPHA_SX164
 	dma_outb(0, DMA1_CLR_MASK_REG);
+	/* We need to figure out why this fails on the SX164.  */
 	dma_outb(0, DMA2_CLR_MASK_REG);
+#endif
 
 #if defined(CONFIG_ALPHA_SABLE)
 	sable_init_IRQ();
 #elif defined(CONFIG_ALPHA_MIATA)
 	miata_init_IRQ();
+#elif defined(CONFIG_ALPHA_SX164)
+	sx164_init_IRQ();
 #elif defined(CONFIG_ALPHA_NORITAKE)
 	noritake_init_IRQ();
 #elif defined(CONFIG_ALPHA_ALCOR) || defined(CONFIG_ALPHA_XLT)
 	alcor_and_xlt_init_IRQ();
-#elif defined(CONFIG_ALPHA_PC164) && defined(CONFIG_ALPHA_SRM)
+#elif (defined(CONFIG_ALPHA_PC164) || defined(CONFIG_ALPHA_LX164)) \
+	&& defined(CONFIG_ALPHA_SRM)
 	/* Disable all the PCI interrupts?  Otherwise, everthing was
 	   done by SRM already.  */
 #elif defined(CONFIG_ALPHA_MIKASA)
 	mikasa_init_IRQ();
+#elif defined(CONFIG_ALPHA_RUFFIAN)
+	ruffian_init_IRQ();
 #elif NR_IRQS == 33
 	init_IRQ_33();
 #elif NR_IRQS == 32
diff -u --recursive --new-file v2.1.88/linux/arch/alpha/kernel/osf_sys.c linux/arch/alpha/kernel/osf_sys.c
--- v2.1.88/linux/arch/alpha/kernel/osf_sys.c	Tue Feb 17 13:12:44 1998
+++ linux/arch/alpha/kernel/osf_sys.c	Tue Feb 24 18:04:07 1998
@@ -872,7 +872,7 @@
 		   software but have not been seen, enable the exception in
 		   hardware so that we can update our software status mask.  */
 		fpcr = rdfpcr() & (~FPCR_MASK | FPCR_DYN_MASK);
-		fpcr = ieee_swcr_to_fpcr(swcr | (~swcr & IEEE_STATUS_MASK)>>16);
+		fpcr |= ieee_swcr_to_fpcr(swcr | (~swcr & IEEE_STATUS_MASK)>>16);
 		wrfpcr(fpcr);
 		   
 		return 0;
diff -u --recursive --new-file v2.1.88/linux/arch/alpha/kernel/pyxis.c linux/arch/alpha/kernel/pyxis.c
--- v2.1.88/linux/arch/alpha/kernel/pyxis.c	Fri Jan 30 11:28:05 1998
+++ linux/arch/alpha/kernel/pyxis.c	Thu Feb 26 14:43:37 1998
@@ -4,6 +4,7 @@
  * Based on code written by David A Rusling (david.rusling@reo.mts.dec.com).
  *
  */
+#include <linux/config.h> /* CONFIG_ALPHA_RUFFIAN. */
 #include <linux/kernel.h>
 #include <linux/types.h>
 #include <linux/bios32.h>
@@ -375,6 +376,10 @@
 	mb() ;
 	pyxis_err = *(vuip)PYXIS_ERR ;
 
+#ifdef CONFIG_ALPHA_RUFFIAN
+	printk("pyxis_init: Skipping window register rewrites --"
+	       " trust DeskStation firmware!\n");
+#else
 	/*
 	 * Set up the PCI->physical memory translation windows.
 	 * For now, windows 1,2 and 3 are disabled.  In the future, we may
@@ -390,6 +395,7 @@
 	*(vuip)PYXIS_W2_BASE = 0x0 ;
 	*(vuip)PYXIS_W3_BASE = 0x0 ;
 	mb();
+#endif
 
 	/*
 	 * check ASN in HWRPB for validity, report if bad
@@ -510,3 +516,42 @@
 	}
 #endif
 }
+
+#if defined(CONFIG_ALPHA_RUFFIAN)
+/* Note: This is only used by MILO, AFAIK... */
+/*
+ * The DeskStation Ruffian motherboard firmware does not place
+ * the memory size in the PALimpure area.  Therefore, we use
+ * the Bank Configuration Registers in PYXIS to obtain the size.
+ */
+unsigned long pyxis_get_bank_size(unsigned long offset)
+{
+	unsigned long bank_addr, bank, ret = 0;
+  
+	/* Valid offsets are: 0x800, 0x840 and 0x880
+	   since Ruffian only uses three banks.  */
+	bank_addr = (unsigned long)PYXIS_MCR + offset;
+	bank = *(vulp)bank_addr;
+    
+	/* Check BANK_ENABLE */
+	if (bank & 0x01) {
+		static unsigned long size[] = {
+			0x40000000UL, /* 0x00,   1G */ 
+			0x20000000UL, /* 0x02, 512M */
+			0x10000000UL, /* 0x04, 256M */
+			0x08000000UL, /* 0x06, 128M */
+			0x04000000UL, /* 0x08,  64M */
+			0x02000000UL, /* 0x0a,  32M */
+			0x01000000UL, /* 0x0c,  16M */
+			0x00800000UL, /* 0x0e,   8M */
+			0x80000000UL, /* 0x10,   2G */
+		};
+
+		bank = (bank & 0x1e) >> 1;
+		if (bank < sizeof(size)/sizeof(*size))
+			ret = size[bank];
+	}
+
+	return ret;
+}
+#endif /* CONFIG_ALPHA_RUFFIAN */
diff -u --recursive --new-file v2.1.88/linux/arch/alpha/kernel/setup.c linux/arch/alpha/kernel/setup.c
--- v2.1.88/linux/arch/alpha/kernel/setup.c	Mon Jan 12 14:51:14 1998
+++ linux/arch/alpha/kernel/setup.c	Mon Feb 23 10:25:10 1998
@@ -107,11 +107,13 @@
     outb(LATCH >> 8, 0x40); /* MSB */
     request_region(0x40, 0x20, "timer"); /* reserve pit */
 #else
+#ifndef CONFIG_ALPHA_RUFFIAN
     outb(0x36, 0x43);	/* counter 0: system timer */
     outb(0x00, 0x40);
     outb(0x00, 0x40);
-    request_region(0x70, 0x10, "timer"); /* reserve rtc */
 #endif
+    request_region(0x70, 0x10, "timer"); /* reserve rtc */
+#endif /* RTC */
 
     outb(0xb6, 0x43);	/* counter 2: speaker */
     outb(0x31, 0x42);
@@ -186,38 +188,115 @@
 #endif
 }
 
+
+#define N(a) (sizeof(a)/sizeof(a[0]))
+
+
+static void
+get_sysnames(long type, long variation,
+	     char **type_name, char **variation_name)
+{
+	static char *sys_unknown = "Unknown";
+	static char *systype_names[] = {
+		"0",
+		"ADU", "Cobra", "Ruby", "Flamingo", "Mannequin", "Jensen",
+		"Pelican", "Morgan", "Sable", "Medulla", "Noname",
+		"Turbolaser", "Avanti", "Mustang", "Alcor", "Tradewind",
+		"Mikasa", "EB64", "EB66", "EB64+", "AlphaBook1",
+		"Rawhide", "K2", "Lynx", "XL", "EB164", "Noritake",
+		"Cortex", "29", "Miata", "XXM", "Takara", "Yukon",
+		"Tsunami", "Wildfire", "CUSCO"
+	};
+
+	static char *unofficial_names[] = {"100", "Ruffian"};
+
+	static char * eb164_names[] = {"EB164", "PC164", "LX164", "SX164"};
+	static int eb164_indices[] = {0,0,0,1,1,1,1,1,2,2,2,2,3,3,3,3};
+
+	static char * alcor_names[] = {"Alcor", "Maverick", "Bret"};
+	static int alcor_indices[] = {0,0,0,1,1,1,0,0,0,0,0,0,2,2,2,2,2,2};
+
+	static char * eb64p_names[] = {"EB64+", "Cabriolet", "AlphaPCI64"};
+	static int eb64p_indices[] = {0,0,1.2};
+
+	static char * eb66_names[] = {"EB66", "EB66+"};
+	static int eb66_indices[] = {0,0,1};
+
+	long member;
+
+	/* Restore real CABRIO and EB66+ family names, ie EB64+ and EB66 */
+	if (type < 0)
+		type = -type;
+
+	/* If not in the tables, make it UNKNOWN,
+	   else set type name to family */
+	if (type < N(systype_names)) {
+		*type_name = systype_names[type];
+	} else if ((type > ST_UNOFFICIAL_BIAS) &&
+		   (type - ST_UNOFFICIAL_BIAS) < N(unofficial_names)) {
+		*type_name = unofficial_names[type - ST_UNOFFICIAL_BIAS];
+	} else {
+		*type_name = sys_unknown;
+		*variation_name = sys_unknown;
+		return;
+	}
+
+	/* Set variation to "0"; if variation is zero, done */
+	*variation_name = systype_names[0];
+	if (variation == 0) {
+		return;
+	}
+
+	member = (variation >> 10) & 0x3f; /* member ID is a bit-field */
+
+	switch (type) {
+	case ST_DEC_EB164:
+		if (member < N(eb164_indices))
+			*variation_name = eb164_names[eb164_indices[member]];
+		break;
+	case ST_DEC_ALCOR:
+		if (member < N(alcor_indices))
+			*variation_name = alcor_names[alcor_indices[member]];
+		break;
+	case ST_DEC_EB64P:
+		if (member < N(eb64p_indices))
+			*variation_name = eb64p_names[eb64p_indices[member]];
+		break;
+	case ST_DEC_EB66:
+		if (member < N(eb66_indices))
+			*variation_name = eb66_names[eb66_indices[member]];
+		break;
+	}
+}
+
 /*
  * BUFFER is PAGE_SIZE bytes long.
  */
 int get_cpuinfo(char *buffer)
 {
-	const char *cpu_name[] = {
-		"EV3", "EV4", "Unknown 1", "LCA4", "EV5", "EV45", "EV56",
-		"EV6", "PCA56"
+	static char *cpu_names[] = {
+		"EV3", "EV4", "Unknown", "LCA4", "EV5", "EV45", "EV56",
+		"EV6", "PCA56", "PCA57"
 	};
-#	define SYSTYPE_NAME_BIAS	20
-	const char *systype_name[] = {
-		"Cabriolet", "EB66P", "-18", "-17", "-16", "-15",
-		"-14", "-13", "-12", "-11", "-10", "-9", "-8",
-		"-7", "-6", "-5", "-4", "-3", "-2", "-1", "0",
-		"ADU", "Cobra", "Ruby", "Flamingo", "5", "Jensen",
-		"Pelican", "8", "Sable", "AXPvme", "Noname",
-		"Turbolaser", "Avanti", "Mustang", "Alcor", "16",
-		"Mikasa", "18", "EB66", "EB64+", "AlphaBook1",
-		"Rawhide", "Lego", "Lynx", "25", "EB164", "Noritake",
-		"Cortex", "29", "Miata", "31", "Takara", "Yukon"
-	};
-	struct percpu_struct *cpu;
-	unsigned int cpu_index;
-	long sysname_index;
+
 	extern struct unaligned_stat {
 		unsigned long count, va, pc;
 	} unaligned[2];
-#	define N(a)	(sizeof(a)/sizeof(a[0]))
+
+	struct percpu_struct *cpu;
+	unsigned int cpu_index;
+	char *cpu_name;
+	char *systype_name;
+	char *sysvariation_name;
 
 	cpu = (struct percpu_struct*)((char*)hwrpb + hwrpb->processor_offset);
 	cpu_index = (unsigned) (cpu->type - 1);
-	sysname_index = hwrpb->sys_type + SYSTYPE_NAME_BIAS;
+	cpu_name = "Unknown";
+	if (cpu_index < N(cpu_names))
+		cpu_name = cpu_names[cpu_index];
+
+	get_sysnames(hwrpb->sys_type, hwrpb->sys_variation,
+		     &systype_name, &sysvariation_name);
 
 	return sprintf(buffer,
 		       "cpu\t\t\t: Alpha\n"
@@ -226,7 +305,7 @@
 		       "cpu revision\t\t: %ld\n"
 		       "cpu serial number\t: %s\n"
 		       "system type\t\t: %s\n"
-		       "system variation\t: %ld\n"
+		       "system variation\t: %s\n"
 		       "system revision\t\t: %ld\n"
 		       "system serial number\t: %s\n"
 		       "cycle frequency [Hz]\t: %lu\n"
@@ -238,12 +317,9 @@
 		       "kernel unaligned acc\t: %ld (pc=%lx,va=%lx)\n"
 		       "user unaligned acc\t: %ld (pc=%lx,va=%lx)\n",
 
-		       (cpu_index < N(cpu_name)
-			? cpu_name[cpu_index] : "Unknown"),
-		       cpu->variation, cpu->revision, (char*)cpu->serial_no,
-		       (sysname_index < N(systype_name)
-			? systype_name[sysname_index] : "Unknown"),
-		       hwrpb->sys_variation, hwrpb->sys_revision,
+		       cpu_name, cpu->variation, cpu->revision,
+		       (char*)cpu->serial_no,
+		       systype_name, sysvariation_name, hwrpb->sys_revision,
 		       (char*)hwrpb->ssn,
 		       hwrpb->cycle_freq,
 		       hwrpb->intr_freq / 4096,
@@ -254,5 +330,4 @@
 		       loops_per_sec / 500000, (loops_per_sec / 5000) % 100,
 		       unaligned[0].count, unaligned[0].pc, unaligned[0].va,
 		       unaligned[1].count, unaligned[1].pc, unaligned[1].va);
-#       undef N
 }
diff -u --recursive --new-file v2.1.88/linux/arch/alpha/kernel/smc37c669.c linux/arch/alpha/kernel/smc37c669.c
--- v2.1.88/linux/arch/alpha/kernel/smc37c669.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/alpha/kernel/smc37c669.c	Thu Feb 26 14:43:37 1998
@@ -0,0 +1,2583 @@
+/*
+ * SMC 37C669 initialization code
+ */
+#include <linux/kernel.h>
+
+#include <linux/bios32.h>
+#include <linux/pci.h>
+#include <linux/malloc.h>
+#include <linux/mm.h>
+#include <linux/init.h>
+
+#include <asm/hwrpb.h>
+#include <asm/io.h>
+#include <asm/segment.h>
+
+#if 0
+# define DBG_DEVS(args)         printk args
+#else
+# define DBG_DEVS(args)
+#endif
+
+#define KB              1024
+#define MB              (1024*KB)
+#define GB              (1024*MB)
+
+#define SMC_DEBUG   0
+
+/* File:	smcc669_def.h
+ *
+ * Copyright (C) 1997 by
+ * Digital Equipment Corporation, Maynard, Massachusetts.
+ * All rights reserved.
+ *
+ * This software is furnished under a license and may be used and copied
+ * only  in  accordance  of  the  terms  of  such  license  and with the
+ * inclusion of the above copyright notice. This software or  any  other
+ * copies thereof may not be provided or otherwise made available to any
+ * other person.  No title to and  ownership of the  software is  hereby
+ * transferred.
+ *
+ * The information in this software is  subject to change without notice
+ * and  should  not  be  construed  as a commitment by Digital Equipment
+ * Corporation.
+ *
+ * Digital assumes no responsibility for the use  or  reliability of its
+ * software on equipment which is not supplied by Digital.
+ *
+ *
+ * Abstract:	
+ *
+ *	This file contains header definitions for the SMC37c669 
+ *	Super I/O controller. 
+ *
+ * Author:	
+ *
+ *	Eric Rasmussen
+ *
+ * Modification History:
+ *
+ *	er	28-Jan-1997	Initial Entry
+ */
+
+#ifndef __SMC37c669_H
+#define __SMC37c669_H
+
+/*
+** Macros for handling device IRQs
+**
+** The mask acts as a flag used in mapping actual ISA IRQs (0 - 15) 
+** to device IRQs (A - H).
+*/
+#define SMC37c669_DEVICE_IRQ_MASK	0x80000000
+#define SMC37c669_DEVICE_IRQ( __i )	\
+	((SMC37c669_DEVICE_IRQ_MASK) | (__i))
+#define SMC37c669_IS_DEVICE_IRQ(__i)	\
+	(((__i) & (SMC37c669_DEVICE_IRQ_MASK)) == (SMC37c669_DEVICE_IRQ_MASK))
+#define SMC37c669_RAW_DEVICE_IRQ(__i)	\
+	((__i) & ~(SMC37c669_DEVICE_IRQ_MASK))
+
+/*
+** Macros for handling device DRQs
+**
+** The mask acts as a flag used in mapping actual ISA DMA
+** channels to device DMA channels (A - C).
+*/
+#define SMC37c669_DEVICE_DRQ_MASK	0x80000000
+#define SMC37c669_DEVICE_DRQ(__d)	\
+	((SMC37c669_DEVICE_DRQ_MASK) | (__d))
+#define SMC37c669_IS_DEVICE_DRQ(__d)	\
+	(((__d) & (SMC37c669_DEVICE_DRQ_MASK)) == (SMC37c669_DEVICE_DRQ_MASK))
+#define SMC37c669_RAW_DEVICE_DRQ(__d)	\
+	((__d) & ~(SMC37c669_DEVICE_DRQ_MASK))
+
+#define SMC37c669_DEVICE_ID	0x3
+
+/*
+** SMC37c669 Device Function Definitions
+*/
+#define SERIAL_0	0
+#define SERIAL_1	1
+#define PARALLEL_0	2
+#define FLOPPY_0	3
+#define IDE_0		4
+#define NUM_FUNCS	5
+
+/*
+** Default Device Function Mappings
+*/
+#define COM1_BASE	0x3F8
+#define COM1_IRQ	4
+#define COM2_BASE	0x2F8
+#define COM2_IRQ	3
+#define PARP_BASE	0x3BC
+#define PARP_IRQ	7
+#define PARP_DRQ	3
+#define FDC_BASE	0x3F0
+#define FDC_IRQ		6
+#define FDC_DRQ		2
+
+/*
+** Configuration On/Off Key Definitions
+*/
+#define SMC37c669_CONFIG_ON_KEY		0x55
+#define SMC37c669_CONFIG_OFF_KEY	0xAA
+
+/*
+** SMC 37c669 Device IRQs
+*/
+#define SMC37c669_DEVICE_IRQ_A	    ( SMC37c669_DEVICE_IRQ( 0x01 ) )
+#define SMC37c669_DEVICE_IRQ_B	    ( SMC37c669_DEVICE_IRQ( 0x02 ) )
+#define SMC37c669_DEVICE_IRQ_C	    ( SMC37c669_DEVICE_IRQ( 0x03 ) )
+#define SMC37c669_DEVICE_IRQ_D	    ( SMC37c669_DEVICE_IRQ( 0x04 ) )
+#define SMC37c669_DEVICE_IRQ_E	    ( SMC37c669_DEVICE_IRQ( 0x05 ) )
+#define SMC37c669_DEVICE_IRQ_F	    ( SMC37c669_DEVICE_IRQ( 0x06 ) )
+/*      SMC37c669_DEVICE_IRQ_G	    *** RESERVED ***/
+#define SMC37c669_DEVICE_IRQ_H	    ( SMC37c669_DEVICE_IRQ( 0x08 ) )
+
+/*
+** SMC 37c669 Device DMA Channel Definitions
+*/
+#define SMC37c669_DEVICE_DRQ_A		    ( SMC37c669_DEVICE_DRQ( 0x01 ) )
+#define SMC37c669_DEVICE_DRQ_B		    ( SMC37c669_DEVICE_DRQ( 0x02 ) )
+#define SMC37c669_DEVICE_DRQ_C		    ( SMC37c669_DEVICE_DRQ( 0x03 ) )
+
+/*
+** Configuration Register Index Definitions
+*/
+#define SMC37c669_CR00_INDEX	    0x00
+#define SMC37c669_CR01_INDEX	    0x01
+#define SMC37c669_CR02_INDEX	    0x02
+#define SMC37c669_CR03_INDEX	    0x03
+#define SMC37c669_CR04_INDEX	    0x04
+#define SMC37c669_CR05_INDEX	    0x05
+#define SMC37c669_CR06_INDEX	    0x06
+#define SMC37c669_CR07_INDEX	    0x07
+#define SMC37c669_CR08_INDEX	    0x08
+#define SMC37c669_CR09_INDEX	    0x09
+#define SMC37c669_CR0A_INDEX	    0x0A
+#define SMC37c669_CR0B_INDEX	    0x0B
+#define SMC37c669_CR0C_INDEX	    0x0C
+#define SMC37c669_CR0D_INDEX	    0x0D
+#define SMC37c669_CR0E_INDEX	    0x0E
+#define SMC37c669_CR0F_INDEX	    0x0F
+#define SMC37c669_CR10_INDEX	    0x10
+#define SMC37c669_CR11_INDEX	    0x11
+#define SMC37c669_CR12_INDEX	    0x12
+#define SMC37c669_CR13_INDEX	    0x13
+#define SMC37c669_CR14_INDEX	    0x14
+#define SMC37c669_CR15_INDEX	    0x15
+#define SMC37c669_CR16_INDEX	    0x16
+#define SMC37c669_CR17_INDEX	    0x17
+#define SMC37c669_CR18_INDEX	    0x18
+#define SMC37c669_CR19_INDEX	    0x19
+#define SMC37c669_CR1A_INDEX	    0x1A
+#define SMC37c669_CR1B_INDEX	    0x1B
+#define SMC37c669_CR1C_INDEX	    0x1C
+#define SMC37c669_CR1D_INDEX	    0x1D
+#define SMC37c669_CR1E_INDEX	    0x1E
+#define SMC37c669_CR1F_INDEX	    0x1F
+#define SMC37c669_CR20_INDEX	    0x20
+#define SMC37c669_CR21_INDEX	    0x21
+#define SMC37c669_CR22_INDEX	    0x22
+#define SMC37c669_CR23_INDEX	    0x23
+#define SMC37c669_CR24_INDEX	    0x24
+#define SMC37c669_CR25_INDEX	    0x25
+#define SMC37c669_CR26_INDEX	    0x26
+#define SMC37c669_CR27_INDEX	    0x27
+#define SMC37c669_CR28_INDEX	    0x28
+#define SMC37c669_CR29_INDEX	    0x29
+
+/*
+** Configuration Register Alias Definitions
+*/
+#define SMC37c669_DEVICE_ID_INDEX		    SMC37c669_CR0D_INDEX
+#define SMC37c669_DEVICE_REVISION_INDEX		    SMC37c669_CR0E_INDEX
+#define SMC37c669_FDC_BASE_ADDRESS_INDEX	    SMC37c669_CR20_INDEX
+#define SMC37c669_IDE_BASE_ADDRESS_INDEX	    SMC37c669_CR21_INDEX
+#define SMC37c669_IDE_ALTERNATE_ADDRESS_INDEX	    SMC37c669_CR22_INDEX
+#define SMC37c669_PARALLEL0_BASE_ADDRESS_INDEX	    SMC37c669_CR23_INDEX
+#define SMC37c669_SERIAL0_BASE_ADDRESS_INDEX	    SMC37c669_CR24_INDEX
+#define SMC37c669_SERIAL1_BASE_ADDRESS_INDEX	    SMC37c669_CR25_INDEX
+#define SMC37c669_PARALLEL_FDC_DRQ_INDEX	    SMC37c669_CR26_INDEX
+#define SMC37c669_PARALLEL_FDC_IRQ_INDEX	    SMC37c669_CR27_INDEX
+#define SMC37c669_SERIAL_IRQ_INDEX		    SMC37c669_CR28_INDEX
+
+/*
+** Configuration Register Definitions
+**
+** The INDEX (write only) and DATA (read/write) ports are effective 
+** only when the chip is in the Configuration State.
+*/
+typedef struct _SMC37c669_CONFIG_REGS {
+    unsigned char index_port;
+    unsigned char data_port;
+} SMC37c669_CONFIG_REGS;
+
+/*
+** CR00 - default value 0x28
+**
+**  IDE_EN (CR00<1:0>):
+**	0x - 30ua pull-ups on nIDEEN, nHDCS0, NHDCS1
+**	11 - IRQ_H available as IRQ output,
+**	     IRRX2, IRTX2 available as alternate IR pins
+**	10 - nIDEEN, nHDCS0, nHDCS1 used to control IDE
+**
+**  VALID (CR00<7>):
+**	A high level on this software controlled bit can
+**	be used to indicate that a valid configuration
+**	cycle has occurred.  The control software must
+**	take care to set this bit at the appropriate times.
+**	Set to zero after power up.  This bit has no
+**	effect on any other hardware in the chip.
+**
+*/
+typedef union _SMC37c669_CR00 {
+    unsigned char as_uchar;
+    struct {
+    	unsigned ide_en : 2;	    /* See note above		*/
+	unsigned reserved1 : 1;	    /* RAZ			*/
+	unsigned fdc_pwr : 1;	    /* 1 = supply power to FDC  */
+	unsigned reserved2 : 3;	    /* Read as 010b		*/
+	unsigned valid : 1;	    /* See note above		*/
+    }	by_field;
+} SMC37c669_CR00;
+
+/*
+** CR01 - default value 0x9C
+*/
+typedef union _SMC37c669_CR01 {
+    unsigned char as_uchar;
+    struct {
+    	unsigned reserved1 : 2;	    /* RAZ			    */
+	unsigned ppt_pwr : 1;	    /* 1 = supply power to PPT	    */
+	unsigned ppt_mode : 1;	    /* 1 = Printer mode, 0 = EPP    */
+	unsigned reserved2 : 1;	    /* Read as 1		    */
+	unsigned reserved3 : 2;	    /* RAZ			    */
+	unsigned lock_crx: 1;	    /* Lock CR00 - CR18		    */
+    }	by_field;
+} SMC37c669_CR01;
+
+/*
+** CR02 - default value 0x88
+*/
+typedef union _SMC37c669_CR02 {
+    unsigned char as_uchar;
+    struct {
+    	unsigned reserved1 : 3;	    /* RAZ			    */
+	unsigned uart1_pwr : 1;	    /* 1 = supply power to UART1    */
+	unsigned reserved2 : 3;	    /* RAZ			    */
+	unsigned uart2_pwr : 1;	    /* 1 = supply power to UART2    */
+    }	by_field;
+} SMC37c669_CR02;
+
+/*
+** CR03 - default value 0x78
+**
+**  CR03<7>	CR03<2>	    Pin 94
+**  -------	-------	    ------
+**     0	   X	    DRV2 (input)
+**     1	   0	    ADRX
+**     1	   1	    IRQ_B
+**
+**  CR03<6>	CR03<5>	    Op Mode
+**  -------	-------	    -------
+**     0	   0	    Model 30
+**     0	   1	    PS/2
+**     1	   0	    Reserved
+**     1	   1	    AT Mode
+*/
+typedef union _SMC37c669_CR03 {
+    unsigned char as_uchar;
+    struct {
+    	unsigned pwrgd_gamecs : 1;  /* 1 = PWRGD, 0 = GAMECS	    */
+	unsigned fdc_mode2 : 1;	    /* 1 = Enhanced Mode 2	    */
+	unsigned pin94_0 : 1;	    /* See note above		    */
+	unsigned reserved1 : 1;	    /* RAZ			    */
+	unsigned drvden : 1;	    /* 1 = high, 0 - output	    */
+	unsigned op_mode : 2;	    /* See note above		    */
+	unsigned pin94_1 : 1;	    /* See note above		    */
+    }	by_field;
+} SMC37c669_CR03;
+
+/*
+** CR04 - default value 0x00
+**
+**  PP_EXT_MODE:
+**	If CR01<PP_MODE> = 0 and PP_EXT_MODE =
+**	    00 - Standard and Bidirectional
+**	    01 - EPP mode and SPP
+**	    10 - ECP mode
+**		 In this mode, 2 drives can be supported
+**		 directly, 3 or 4 drives must use external
+**		 4 drive support.  SPP can be selected
+**		 through the ECR register of ECP as mode 000.
+**	    11 - ECP mode and EPP mode
+**		 In this mode, 2 drives can be supported
+**		 directly, 3 or 4 drives must use external
+**		 4 drive support.  SPP can be selected
+**		 through the ECR register of ECP as mode 000.
+**		 In this mode, EPP can be selected through
+**		 the ECR register of ECP as mode 100.
+**
+**  PP_FDC:
+**	00 - Normal
+**	01 - PPFD1
+**	10 - PPFD2
+**	11 - Reserved
+**
+**  MIDI1:
+**	Serial Clock Select: 
+**	    A low level on this bit disables MIDI support,
+**	    clock = divide by 13.  A high level on this 
+**	    bit enables MIDI support, clock = divide by 12.
+**
+**	MIDI operates at 31.25 Kbps which can be derived 
+**	from 125 KHz (24 MHz / 12 = 2 MHz, 2 MHz / 16 = 125 KHz)
+**
+**  ALT_IO:
+**	0 - Use pins IRRX, IRTX
+**	1 - Use pins IRRX2, IRTX2
+**
+**	If this bit is set, the IR receive and transmit
+**	functions will not be available on pins 25 and 26
+**	unless CR00<IDE_EN> = 11.
+*/
+typedef union _SMC37c669_CR04 {
+    unsigned char as_uchar;
+    struct {
+    	unsigned ppt_ext_mode : 2;  /* See note above		    */
+	unsigned ppt_fdc : 2;	    /* See note above		    */
+	unsigned midi1 : 1;	    /* See note above		    */
+	unsigned midi2 : 1;	    /* See note above		    */
+	unsigned epp_type : 1;	    /* 0 = EPP 1.9, 1 = EPP 1.7	    */
+	unsigned alt_io : 1;	    /* See note above		    */
+    }	by_field;
+} SMC37c669_CR04;
+
+/*
+** CR05 - default value 0x00
+**
+**  DEN_SEL:
+**	00 - Densel output normal
+**	01 - Reserved
+**	10 - Densel output 1
+**	11 - Densel output 0
+**
+*/
+typedef union _SMC37c669_CR05 {
+    unsigned char as_uchar;
+    struct {
+    	unsigned reserved1 : 2;	    /* RAZ					*/
+	unsigned fdc_dma_mode : 1;  /* 0 = burst, 1 = non-burst			*/
+	unsigned den_sel : 2;	    /* See note above				*/
+	unsigned swap_drv : 1;	    /* Swap the FDC motor selects		*/
+	unsigned extx4 : 1;	    /* 0 = 2 drive, 1 = external 4 drive decode	*/
+	unsigned reserved2 : 1;	    /* RAZ					*/
+    }	by_field;
+} SMC37c669_CR05;
+
+/*
+** CR06 - default value 0xFF
+*/
+typedef union _SMC37c669_CR06 {
+    unsigned char as_uchar;
+    struct {
+    	unsigned floppy_a : 2;	    /* Type of floppy drive A	    */
+	unsigned floppy_b : 2;	    /* Type of floppy drive B	    */
+	unsigned floppy_c : 2;	    /* Type of floppy drive C	    */
+	unsigned floppy_d : 2;	    /* Type of floppy drive D	    */
+    }	by_field;
+} SMC37c669_CR06;
+
+/*
+** CR07 - default value 0x00
+**
+**  Auto Power Management CR07<7:4>:
+**	0 - Auto Powerdown disabled (default)
+**	1 - Auto Powerdown enabled
+**
+**	This bit is reset to the default state by POR or
+**	a hardware reset.
+**
+*/
+typedef union _SMC37c669_CR07 {
+    unsigned char as_uchar;
+    struct {
+    	unsigned floppy_boot : 2;   /* 0 = A:, 1 = B:		    */
+	unsigned reserved1 : 2;	    /* RAZ			    */
+	unsigned ppt_en : 1;	    /* See note above		    */
+	unsigned uart1_en : 1;	    /* See note above		    */
+	unsigned uart2_en : 1;	    /* See note above		    */
+	unsigned fdc_en : 1;	    /* See note above		    */
+    }	by_field;
+} SMC37c669_CR07;
+
+/*
+** CR08 - default value 0x00
+*/
+typedef union _SMC37c669_CR08 {
+    unsigned char as_uchar;
+    struct {
+    	unsigned zero : 4;	    /* 0			    */
+	unsigned addrx7_4 : 4;	    /* ADR<7:3> for ADRx decode	    */
+    }	by_field;
+} SMC37c669_CR08;
+
+/*
+** CR09 - default value 0x00
+**
+**  ADRx_CONFIG:
+**	00 - ADRx disabled
+**	01 - 1 byte decode A<3:0> = 0000b
+**	10 - 8 byte block decode A<3:0> = 0XXXb
+**	11 - 16 byte block decode A<3:0> = XXXXb
+**
+*/
+typedef union _SMC37c669_CR09 {
+    unsigned char as_uchar;
+    struct {
+    	unsigned adra8 : 3;	    /* ADR<10:8> for ADRx decode    */
+	unsigned reserved1 : 3;
+	unsigned adrx_config : 2;   /* See note above		    */
+    }	by_field;
+} SMC37c669_CR09;
+
+/*
+** CR0A - default value 0x00
+*/
+typedef union _SMC37c669_CR0A {
+    unsigned char as_uchar;
+    struct {
+    	unsigned ecp_fifo_threshold : 4;
+	unsigned reserved1 : 4;
+    }	by_field;
+} SMC37c669_CR0A;
+
+/*
+** CR0B - default value 0x00
+*/
+typedef union _SMC37c669_CR0B {
+    unsigned char as_uchar;
+    struct {
+    	unsigned fdd0_drtx : 2;	    /* FDD0 Data Rate Table	    */
+	unsigned fdd1_drtx : 2;	    /* FDD1 Data Rate Table	    */
+	unsigned fdd2_drtx : 2;	    /* FDD2 Data Rate Table	    */
+	unsigned fdd3_drtx : 2;	    /* FDD3 Data Rate Table	    */
+    }	by_field;
+} SMC37c669_CR0B;
+
+/*
+** CR0C - default value 0x00
+**
+**  UART2_MODE:
+**	000 - Standard (default)
+**	001 - IrDA (HPSIR)
+**	010 - Amplitude Shift Keyed IR @500 KHz
+**	011 - Reserved
+**	1xx - Reserved
+**
+*/
+typedef union _SMC37c669_CR0C {
+    unsigned char as_uchar;
+    struct {
+    	unsigned uart2_rcv_polarity : 1;    /* 1 = invert RX		*/
+	unsigned uart2_xmit_polarity : 1;   /* 1 = invert TX		*/
+	unsigned uart2_duplex : 1;	    /* 1 = full, 0 = half	*/
+	unsigned uart2_mode : 3;	    /* See note above		*/
+	unsigned uart1_speed : 1;	    /* 1 = high speed enabled	*/
+	unsigned uart2_speed : 1;	    /* 1 = high speed enabled	*/
+    }	by_field;
+} SMC37c669_CR0C;
+
+/*
+** CR0D - default value 0x03
+**
+**  Device ID Register - read only
+*/
+typedef union _SMC37c669_CR0D {
+    unsigned char as_uchar;
+    struct {
+    	unsigned device_id : 8;	    /* Returns 0x3 in this field    */
+    }	by_field;
+} SMC37c669_CR0D;
+
+/*
+** CR0E - default value 0x02
+**
+**  Device Revision Register - read only
+*/
+typedef union _SMC37c669_CR0E {
+    unsigned char as_uchar;
+    struct {
+    	unsigned device_rev : 8;    /* Returns 0x2 in this field    */
+    }	by_field;
+} SMC37c669_CR0E;
+
+/*
+** CR0F - default value 0x00
+*/
+typedef union _SMC37c669_CR0F {
+    unsigned char as_uchar;
+    struct {
+    	unsigned test0 : 1;	    /* Reserved - set to 0	    */
+	unsigned test1 : 1;	    /* Reserved - set to 0	    */
+	unsigned test2 : 1;	    /* Reserved - set to 0	    */
+	unsigned test3 : 1;	    /* Reserved - set t0 0	    */
+	unsigned test4 : 1;	    /* Reserved - set to 0	    */
+	unsigned test5 : 1;	    /* Reserved - set t0 0	    */
+	unsigned test6 : 1;	    /* Reserved - set t0 0	    */
+	unsigned test7 : 1;	    /* Reserved - set to 0	    */
+    }	by_field;
+} SMC37c669_CR0F;
+
+/*
+** CR10 - default value 0x00
+*/
+typedef union _SMC37c669_CR10 {
+    unsigned char as_uchar;
+    struct {
+    	unsigned reserved1 : 3;	     /* RAZ			    */
+	unsigned pll_gain : 1;	     /* 1 = 3V, 2 = 5V operation    */
+	unsigned pll_stop : 1;	     /* 1 = stop PLLs		    */
+	unsigned ace_stop : 1;	     /* 1 = stop UART clocks	    */
+	unsigned pll_clock_ctrl : 1; /* 0 = 14.318 MHz, 1 = 24 MHz  */
+	unsigned ir_test : 1;	     /* Enable IR test mode	    */
+    }	by_field;
+} SMC37c669_CR10;
+
+/*
+** CR11 - default value 0x00
+*/
+typedef union _SMC37c669_CR11 {
+    unsigned char as_uchar;
+    struct {
+    	unsigned ir_loopback : 1;   /* Internal IR loop back		    */
+	unsigned test_10ms : 1;	    /* Test 10ms autopowerdown FDC timeout  */
+	unsigned reserved1 : 6;	    /* RAZ				    */
+    }	by_field;
+} SMC37c669_CR11;
+
+/*
+** CR12 - CR1D are reserved registers
+*/
+
+/*
+** CR1E - default value 0x80
+**
+**  GAMECS:
+**	00 - GAMECS disabled
+**	01 - 1 byte decode ADR<3:0> = 0001b
+**	10 - 8 byte block decode ADR<3:0> = 0XXXb
+**	11 - 16 byte block decode ADR<3:0> = XXXXb
+**
+*/
+typedef union _SMC37c66_CR1E {
+    unsigned char as_uchar;
+    struct {
+    	unsigned gamecs_config: 2;   /* See note above		    */
+	unsigned gamecs_addr9_4 : 6; /* GAMECS Addr<9:4>	    */
+    }	by_field;
+} SMC37c669_CR1E;
+
+/*
+** CR1F - default value 0x00
+**
+**  DT0 DT1 DRVDEN0 DRVDEN1 Drive Type
+**  --- --- ------- ------- ----------
+**   0   0  DENSEL  DRATE0  4/2/1 MB 3.5"
+**                          2/1 MB 5.25"
+**                          2/1.6/1 MB 3.5" (3-mode)
+**   0   1  DRATE1  DRATE0
+**   1   0  nDENSEL DRATE0  PS/2
+**   1   1  DRATE0  DRATE1
+**
+**  Note: DENSEL, DRATE1, and DRATE0 map onto two output
+**	  pins - DRVDEN0 and DRVDEN1.
+**
+*/
+typedef union _SMC37c669_CR1F {
+    unsigned char as_uchar;
+    struct {
+    	unsigned fdd0_drive_type : 2;	/* FDD0 drive type	    */
+	unsigned fdd1_drive_type : 2;	/* FDD1 drive type	    */
+	unsigned fdd2_drive_type : 2;	/* FDD2 drive type	    */
+	unsigned fdd3_drive_type : 2;	/* FDD3 drive type	    */
+    }	by_field;
+} SMC37c669_CR1F;
+
+/*
+** CR20 - default value 0x3C
+**
+**  FDC Base Address Register
+**	- To disable this decode set Addr<9:8> = 0
+**	- A<10> = 0, A<3:0> = 0XXXb to access.
+**
+*/
+typedef union _SMC37c669_CR20 {
+    unsigned char as_uchar;
+    struct {
+    	unsigned zero : 2;	    /* 0			    */
+	unsigned addr9_4 : 6;	    /* FDC Addr<9:4>		    */
+    }	by_field;
+} SMC37c669_CR20;
+
+/*
+** CR21 - default value 0x3C
+**
+**  IDE Base Address Register
+**	- To disable this decode set Addr<9:8> = 0
+**	- A<10> = 0, A<3:0> = 0XXXb to access.
+**
+*/
+typedef union _SMC37c669_CR21 {
+    unsigned char as_uchar;
+    struct {
+    	unsigned zero : 2;	    /* 0			    */
+	unsigned addr9_4 : 6;	    /* IDE Addr<9:4>		    */
+    }	by_field;
+} SMC37c669_CR21;
+
+/*
+** CR22 - default value 0x3D
+**
+**  IDE Alternate Status Base Address Register
+**	- To disable this decode set Addr<9:8> = 0
+**	- A<10> = 0, A<3:0> = 0110b to access.
+**
+*/
+typedef union _SMC37c669_CR22 {
+    unsigned char as_uchar;
+    struct {
+    	unsigned zero : 2;	    /* 0			    */
+	unsigned addr9_4 : 6;	    /* IDE Alt Status Addr<9:4>	    */
+    }	by_field;
+} SMC37c669_CR22;
+
+/*
+** CR23 - default value 0x00
+**
+**  Parallel Port Base Address Register
+**	- To disable this decode set Addr<9:8> = 0
+**	- A<10> = 0 to access.
+**	- If EPP is enabled, A<2:0> = XXXb to access.
+**	  If EPP is NOT enabled, A<1:0> = XXb to access
+**
+*/
+typedef union _SMC37c669_CR23 {
+    unsigned char as_uchar;
+    struct {
+	unsigned addr9_2 : 8;	    /* Parallel Port Addr<9:2>	    */
+    }	by_field;
+} SMC37c669_CR23;
+
+/*
+** CR24 - default value 0x00
+**
+**  UART1 Base Address Register
+**	- To disable this decode set Addr<9:8> = 0
+**	- A<10> = 0, A<2:0> = XXXb to access.
+**
+*/
+typedef union _SMC37c669_CR24 {
+    unsigned char as_uchar;
+    struct {
+    	unsigned zero : 1;	    /* 0			    */
+	unsigned addr9_3 : 7;	    /* UART1 Addr<9:3>		    */
+    }	by_field;
+} SMC37c669_CR24;
+
+/*
+** CR25 - default value 0x00
+**
+**  UART2 Base Address Register
+**	- To disable this decode set Addr<9:8> = 0
+**	- A<10> = 0, A<2:0> = XXXb to access.
+**
+*/
+typedef union _SMC37c669_CR25 {
+    unsigned char as_uchar;
+    struct {
+    	unsigned zero : 1;	    /* 0			    */
+	unsigned addr9_3 : 7;	    /* UART2 Addr<9:3>		    */
+    }	by_field;
+} SMC37c669_CR25;
+
+/*
+** CR26 - default value 0x00
+**
+**  Parallel Port / FDC DMA Select Register
+**
+**  D3 - D0	  DMA
+**  D7 - D4	Selected
+**  -------	--------
+**   0000	 None
+**   0001	 DMA_A
+**   0010	 DMA_B
+**   0011	 DMA_C
+**
+*/
+typedef union _SMC37c669_CR26 {
+    unsigned char as_uchar;
+    struct {
+    	unsigned ppt_drq : 4;	    /* See note above		    */
+	unsigned fdc_drq : 4;	    /* See note above		    */
+    }	by_field;
+} SMC37c669_CR26;
+
+/*
+** CR27 - default value 0x00
+**
+**  Parallel Port / FDC IRQ Select Register
+**
+**  D3 - D0	  IRQ
+**  D7 - D4	Selected
+**  -------	--------
+**   0000	 None
+**   0001	 IRQ_A
+**   0010	 IRQ_B
+**   0011	 IRQ_C
+**   0100	 IRQ_D
+**   0101	 IRQ_E
+**   0110	 IRQ_F
+**   0111	 Reserved
+**   1000	 IRQ_H
+**
+**  Any unselected IRQ REQ is in tristate
+**
+*/
+typedef union _SMC37c669_CR27 {
+    unsigned char as_uchar;
+    struct {
+    	unsigned ppt_irq : 4;	    /* See note above		    */
+	unsigned fdc_irq : 4;	    /* See note above		    */
+    }	by_field;
+} SMC37c669_CR27;
+
+/*
+** CR28 - default value 0x00
+**
+**  UART IRQ Select Register
+**
+**  D3 - D0	  IRQ
+**  D7 - D4	Selected
+**  -------	--------
+**   0000	 None
+**   0001	 IRQ_A
+**   0010	 IRQ_B
+**   0011	 IRQ_C
+**   0100	 IRQ_D
+**   0101	 IRQ_E
+**   0110	 IRQ_F
+**   0111	 Reserved
+**   1000	 IRQ_H
+**   1111	 share with UART1 (only for UART2)
+**
+**  Any unselected IRQ REQ is in tristate
+**
+**  To share an IRQ between UART1 and UART2, set
+**  UART1 to use the desired IRQ and set UART2 to
+**  0xF to enable sharing mechanism.
+**
+*/
+typedef union _SMC37c669_CR28 {
+    unsigned char as_uchar;
+    struct {
+    	unsigned uart2_irq : 4;	    /* See note above		    */
+	unsigned uart1_irq : 4;	    /* See note above		    */
+    }	by_field;
+} SMC37c669_CR28;
+
+/*
+** CR29 - default value 0x00
+**
+**  IRQIN IRQ Select Register
+**
+**  D3 - D0	  IRQ
+**  D7 - D4	Selected
+**  -------	--------
+**   0000	 None
+**   0001	 IRQ_A
+**   0010	 IRQ_B
+**   0011	 IRQ_C
+**   0100	 IRQ_D
+**   0101	 IRQ_E
+**   0110	 IRQ_F
+**   0111	 Reserved
+**   1000	 IRQ_H
+**
+**  Any unselected IRQ REQ is in tristate
+**
+*/
+typedef union _SMC37c669_CR29 {
+    unsigned char as_uchar;
+    struct {
+    	unsigned irqin_irq : 4;	    /* See note above		    */
+	unsigned reserved1 : 4;	    /* RAZ			    */
+    }	by_field;
+} SMC37c669_CR29;
+
+/*
+** Aliases of Configuration Register formats (should match
+** the set of index aliases).
+**
+** Note that CR24 and CR25 have the same format and are the
+** base address registers for UART1 and UART2.  Because of
+** this we only define 1 alias here - for CR24 - as the serial
+** base address register.
+**
+** Note that CR21 and CR22 have the same format and are the
+** base address and alternate status address registers for
+** the IDE controller.  Because of this we only define 1 alias
+** here - for CR21 - as the IDE address register.
+**
+*/
+typedef SMC37c669_CR0D SMC37c669_DEVICE_ID_REGISTER;
+typedef SMC37c669_CR0E SMC37c669_DEVICE_REVISION_REGISTER;
+typedef SMC37c669_CR20 SMC37c669_FDC_BASE_ADDRESS_REGISTER;
+typedef SMC37c669_CR21 SMC37c669_IDE_ADDRESS_REGISTER;
+typedef SMC37c669_CR23 SMC37c669_PARALLEL_BASE_ADDRESS_REGISTER;
+typedef SMC37c669_CR24 SMC37c669_SERIAL_BASE_ADDRESS_REGISTER;
+typedef SMC37c669_CR26 SMC37c669_PARALLEL_FDC_DRQ_REGISTER;
+typedef SMC37c669_CR27 SMC37c669_PARALLEL_FDC_IRQ_REGISTER;
+typedef SMC37c669_CR28 SMC37c669_SERIAL_IRQ_REGISTER;
+
+/*
+** ISA/Device IRQ Translation Table Entry Definition
+*/
+typedef struct _SMC37c669_IRQ_TRANSLATION_ENTRY {
+    int device_irq;
+    int isa_irq;
+} SMC37c669_IRQ_TRANSLATION_ENTRY;
+
+/*
+** ISA/Device DMA Translation Table Entry Definition
+*/
+typedef struct _SMC37c669_DRQ_TRANSLATION_ENTRY {
+    int device_drq;
+    int isa_drq;
+} SMC37c669_DRQ_TRANSLATION_ENTRY;
+
+/*
+** External Interface Function Prototype Declarations
+*/
+
+SMC37c669_CONFIG_REGS *SMC37c669_detect( 
+    void 
+);
+
+unsigned int SMC37c669_enable_device( 
+    unsigned int func 
+);
+
+unsigned int SMC37c669_disable_device( 
+    unsigned int func 
+);
+
+unsigned int SMC37c669_configure_device( 
+    unsigned int func, 
+    int port, 
+    int irq, 
+    int drq 
+);
+
+void SMC37c669_display_device_info( 
+    void 
+);
+
+#endif	/* __SMC37c669_H */
+
+/* file:	smcc669.c
+ *
+ * Copyright (C) 1997 by
+ * Digital Equipment Corporation, Maynard, Massachusetts.
+ * All rights reserved.
+ *
+ * This software is furnished under a license and may be used and copied
+ * only  in  accordance  of  the  terms  of  such  license  and with the
+ * inclusion of the above copyright notice. This software or  any  other
+ * copies thereof may not be provided or otherwise made available to any
+ * other person.  No title to and  ownership of the  software is  hereby
+ * transferred.
+ *
+ * The information in this software is  subject to change without notice
+ * and  should  not  be  construed  as a commitment by digital equipment
+ * corporation.
+ *
+ * Digital assumes no responsibility for the use  or  reliability of its
+ * software on equipment which is not supplied by digital.
+ */
+
+/*
+ *++
+ *  FACILITY:
+ *
+ *      Alpha SRM Console Firmware
+ *
+ *  MODULE DESCRIPTION:
+ *
+ *	SMC37c669 Super I/O controller configuration routines.
+ *
+ *  AUTHORS:
+ *
+ *	Eric Rasmussen
+ *
+ *  CREATION DATE:
+ *  
+ *	28-Jan-1997
+ *
+ *  MODIFICATION HISTORY:
+ *	
+ *	er	01-May-1997	Fixed pointer conversion errors in 
+ *				SMC37c669_get_device_config().
+ *      er	28-Jan-1997	Initial version.
+ *
+ *--
+ */
+#if 0
+/* $INCLUDE_OPTIONS$ */
+#include    "cp$inc:platform_io.h"
+/* $INCLUDE_OPTIONS_END$ */
+#include    "cp$src:common.h"
+#include    "cp$inc:prototypes.h"
+#include    "cp$src:kernel_def.h"
+#include    "cp$src:msg_def.h"
+#include    "cp$src:smcc669_def.h"
+/* Platform-specific includes */
+#include    "cp$src:platform.h"
+#endif
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#define wb( _x_, _y_ )	outb( _y_, (unsigned int)((unsigned long)_x_) )
+#define rb( _x_ )	inb( (unsigned int)((unsigned long)_x_) )
+
+/*
+** Local storage for device configuration information.
+**
+** Since the SMC37c669 does not provide an explicit
+** mechanism for enabling/disabling individual device 
+** functions, other than unmapping the device, local 
+** storage for device configuration information is 
+** allocated here for use in implementing our own 
+** function enable/disable scheme.
+*/
+static struct DEVICE_CONFIG {
+    unsigned int port1;
+    unsigned int port2;
+    unsigned int irq;
+    unsigned int drq;
+} local_config [NUM_FUNCS];
+
+/*
+** List of all possible addresses for the Super I/O chip
+*/
+static unsigned long SMC37c669_Addresses[] __initdata =
+    {
+	0x3F0UL,	    /* Primary address	    */
+	0x370UL,	    /* Secondary address    */
+	0UL		    /* End of list	    */
+    };
+
+/*
+** Global Pointer to the Super I/O device
+*/
+static SMC37c669_CONFIG_REGS *SMC37c669 __initdata = NULL;
+
+/*
+** IRQ Translation Table
+**
+** The IRQ translation table is a list of SMC37c669 device 
+** and standard ISA IRQs.
+**
+*/
+static SMC37c669_IRQ_TRANSLATION_ENTRY *SMC37c669_irq_table __initdata; 
+
+/*
+** The following definition is for the default IRQ 
+** translation table.
+*/
+static SMC37c669_IRQ_TRANSLATION_ENTRY SMC37c669_default_irq_table[]
+__initdata = 
+    { 
+	{ SMC37c669_DEVICE_IRQ_A, -1 }, 
+	{ SMC37c669_DEVICE_IRQ_B, -1 }, 
+	{ SMC37c669_DEVICE_IRQ_C, 7 }, 
+	{ SMC37c669_DEVICE_IRQ_D, 6 }, 
+	{ SMC37c669_DEVICE_IRQ_E, 4 }, 
+	{ SMC37c669_DEVICE_IRQ_F, 3 }, 
+	{ SMC37c669_DEVICE_IRQ_H, -1 }, 
+	{ -1, -1 } /* End of table */
+    };
+
+/*
+** DRQ Translation Table
+**
+** The DRQ translation table is a list of SMC37c669 device and
+** ISA DMA channels.
+**
+*/
+static SMC37c669_DRQ_TRANSLATION_ENTRY *SMC37c669_drq_table __initdata; 
+
+/*
+** The following definition is the default DRQ
+** translation table.
+*/
+static SMC37c669_DRQ_TRANSLATION_ENTRY SMC37c669_default_drq_table[]
+__initdata = 
+    { 
+	{ SMC37c669_DEVICE_DRQ_A, 2 }, 
+	{ SMC37c669_DEVICE_DRQ_B, 3 }, 
+	{ SMC37c669_DEVICE_DRQ_C, -1 }, 
+	{ -1, -1 } /* End of table */
+    };
+
+/*
+** Local Function Prototype Declarations
+*/
+
+static unsigned int SMC37c669_is_device_enabled( 
+    unsigned int func 
+);
+
+#if 0
+static unsigned int SMC37c669_get_device_config( 
+    unsigned int func, 
+    int *port, 
+    int *irq, 
+    int *drq 
+);
+#endif
+
+static void SMC37c669_config_mode( 
+    unsigned int enable 
+);
+
+static unsigned char SMC37c669_read_config( 
+    unsigned char index 
+);
+
+static void SMC37c669_write_config( 
+    unsigned char index, 
+    unsigned char data 
+);
+
+static void SMC37c669_init_local_config( void );
+
+static struct DEVICE_CONFIG *SMC37c669_get_config(
+    unsigned int func
+);
+
+static int SMC37c669_xlate_irq(
+    unsigned int irq 
+);
+
+static int SMC37c669_xlate_drq(
+    unsigned int drq 
+);
+
+#if 0
+/*
+** External Data Declarations
+*/
+
+extern struct LOCK spl_atomic;
+
+/*
+** External Function Prototype Declarations
+*/
+
+/* From kernel_alpha.mar */
+extern spinlock( 
+    struct LOCK *spl 
+);
+
+extern spinunlock( 
+    struct LOCK *spl 
+);
+
+/* From filesys.c */
+int allocinode(
+    char *name, 
+    int can_create, 
+    struct INODE **ipp
+);
+
+extern int null_procedure( void );
+
+int smcc669_init( void );
+int smcc669_open( struct FILE *fp, char *info, char *next, char *mode );
+int smcc669_read( struct FILE *fp, int size, int number, unsigned char *buf );
+int smcc669_write( struct FILE *fp, int size, int number, unsigned char *buf );
+int smcc669_close( struct FILE *fp );
+
+struct DDB smc_ddb = {
+	"smc",			/* how this routine wants to be called	*/
+	smcc669_read,		/* read routine				*/
+	smcc669_write,		/* write routine			*/
+	smcc669_open,		/* open routine				*/
+	smcc669_close,		/* close routine			*/
+	null_procedure,		/* name expansion routine		*/
+	null_procedure,		/* delete routine			*/
+	null_procedure,		/* create routine			*/
+	null_procedure,		/* setmode				*/
+	null_procedure,		/* validation routine			*/
+	0,			/* class specific use			*/
+	1,			/* allows information			*/
+	0,			/* must be stacked			*/
+	0,			/* is a flash update driver		*/
+	0,			/* is a block device			*/
+	0,			/* not seekable				*/
+	0,			/* is an ethernet device		*/
+	0,			/* is a filesystem driver		*/
+};
+#endif
+
+#define spinlock(x)
+#define spinunlock(x)
+
+
+/*
+**++
+**  FUNCTIONAL DESCRIPTION:
+**
+**      This function detects the presence of an SMC37c669 Super I/O
+**	controller.
+**
+**  FORMAL PARAMETERS:
+**
+**	None
+**
+**  RETURN VALUE:
+**
+**      Returns a pointer to the device if found, otherwise,
+**	the NULL pointer is returned.
+**
+**  SIDE EFFECTS:
+**
+**      None
+**
+**--
+*/
+SMC37c669_CONFIG_REGS * __init SMC37c669_detect( void )
+{
+    int i;
+    SMC37c669_DEVICE_ID_REGISTER id;
+
+    for ( i = 0;  SMC37c669_Addresses[i] != 0;  i++ ) {
+/*
+** Initialize the device pointer even though we don't yet know if
+** the controller is at this address.  The support functions access
+** the controller through this device pointer so we need to set it
+** even when we are looking ...
+*/
+    	SMC37c669 = ( SMC37c669_CONFIG_REGS * )SMC37c669_Addresses[i];
+/*
+** Enter configuration mode
+*/
+	SMC37c669_config_mode( TRUE );
+/*
+** Read the device id
+*/
+	id.as_uchar = SMC37c669_read_config( SMC37c669_DEVICE_ID_INDEX );
+/*
+** Exit configuration mode
+*/
+	SMC37c669_config_mode( FALSE );
+/*
+** Does the device id match?  If so, assume we have found an
+** SMC37c669 controller at this address.
+*/
+	if ( id.by_field.device_id == SMC37c669_DEVICE_ID ) {
+/*
+** Initialize the IRQ and DRQ translation tables.
+*/
+    	    SMC37c669_irq_table = SMC37c669_default_irq_table;
+	    SMC37c669_drq_table = SMC37c669_default_drq_table;
+/*
+** erfix
+**
+** If the platform can't use the IRQ and DRQ defaults set up in this 
+** file, it should call a platform-specific external routine at this 
+** point to reset the IRQ and DRQ translation table pointers to point 
+** at the appropriate tables for the platform.  If the defaults are 
+** acceptable, then the external routine should do nothing.
+*/
+
+/*
+** Put the chip back into configuration mode
+*/
+	    SMC37c669_config_mode( TRUE );
+/*
+** Initialize local storage for configuration information
+*/
+	    SMC37c669_init_local_config( );
+/*
+** Exit configuration mode
+*/
+	    SMC37c669_config_mode( FALSE );
+/*
+** SMC37c669 controller found, break out of search loop
+*/
+	    break;
+	}
+	else {
+/*
+** Otherwise, we did not find an SMC37c669 controller at this
+** address so set the device pointer to NULL.
+*/
+	    SMC37c669 = NULL;
+	}
+    }
+    return SMC37c669;
+}
+
+
+/*
+**++
+**  FUNCTIONAL DESCRIPTION:
+**
+**      This function enables an SMC37c669 device function.
+**
+**  FORMAL PARAMETERS:
+**
+**      func:
+**          Which device function to enable
+**
+**  RETURN VALUE:
+**
+**      Returns TRUE is the device function was enabled, otherwise, FALSE
+**
+**  SIDE EFFECTS:
+**
+**      {@description or none@}
+**
+**  DESIGN:
+**
+**      Enabling a device function in the SMC37c669 controller involves
+**	setting all of its mappings (port, irq, drq ...).  A local 
+**	"shadow" copy of the device configuration is kept so we can
+**	just set each mapping to what the local copy says.
+**
+**	This function ALWAYS updates the local shadow configuration of
+**	the device function being enabled, even if the device is always
+**	enabled.  To avoid replication of code, functions such as
+**	configure_device set up the local copy and then call this 
+**	function to the update the real device.
+**
+**--
+*/
+unsigned int __init SMC37c669_enable_device ( unsigned int func )
+{
+    unsigned int ret_val = FALSE;
+/*
+** Put the device into configuration mode
+*/
+    SMC37c669_config_mode( TRUE );
+    switch ( func ) {
+    	case SERIAL_0:
+	    {
+	    	SMC37c669_SERIAL_BASE_ADDRESS_REGISTER base_addr;
+		SMC37c669_SERIAL_IRQ_REGISTER irq;
+/*
+** Enable the serial 1 IRQ mapping
+*/
+	    	irq.as_uchar = 
+		    SMC37c669_read_config( SMC37c669_SERIAL_IRQ_INDEX );
+
+		irq.by_field.uart1_irq =
+		    SMC37c669_RAW_DEVICE_IRQ(
+			SMC37c669_xlate_irq( local_config[ func ].irq )
+		    );
+
+		SMC37c669_write_config( SMC37c669_SERIAL_IRQ_INDEX, irq.as_uchar );
+/*
+** Enable the serial 1 port base address mapping
+*/
+		base_addr.as_uchar = 0;
+		base_addr.by_field.addr9_3 = local_config[ func ].port1 >> 3;
+
+		SMC37c669_write_config( 
+		    SMC37c669_SERIAL0_BASE_ADDRESS_INDEX,
+		    base_addr.as_uchar
+		);
+		ret_val = TRUE;
+		break;
+	    }
+	case SERIAL_1:
+	    {
+	    	SMC37c669_SERIAL_BASE_ADDRESS_REGISTER base_addr;
+		SMC37c669_SERIAL_IRQ_REGISTER irq;
+/*
+** Enable the serial 2 IRQ mapping
+*/
+	    	irq.as_uchar = 
+		    SMC37c669_read_config( SMC37c669_SERIAL_IRQ_INDEX );
+
+		irq.by_field.uart2_irq =
+		    SMC37c669_RAW_DEVICE_IRQ(
+			SMC37c669_xlate_irq( local_config[ func ].irq )
+		    );
+
+		SMC37c669_write_config( SMC37c669_SERIAL_IRQ_INDEX, irq.as_uchar );
+/*
+** Enable the serial 2 port base address mapping
+*/
+		base_addr.as_uchar = 0;
+		base_addr.by_field.addr9_3 = local_config[ func ].port1 >> 3;
+
+		SMC37c669_write_config( 
+		    SMC37c669_SERIAL1_BASE_ADDRESS_INDEX,
+		    base_addr.as_uchar
+		);
+		ret_val = TRUE;
+		break;
+	    }
+	case PARALLEL_0:
+	    {
+	    	SMC37c669_PARALLEL_BASE_ADDRESS_REGISTER base_addr;
+		SMC37c669_PARALLEL_FDC_IRQ_REGISTER irq;
+		SMC37c669_PARALLEL_FDC_DRQ_REGISTER drq;
+/*
+** Enable the parallel port DMA channel mapping
+*/
+	    	drq.as_uchar =
+		    SMC37c669_read_config( SMC37c669_PARALLEL_FDC_DRQ_INDEX );
+
+		drq.by_field.ppt_drq = 
+		    SMC37c669_RAW_DEVICE_DRQ(
+			SMC37c669_xlate_drq( local_config[ func ].drq )
+		    );
+
+		SMC37c669_write_config(
+		    SMC37c669_PARALLEL_FDC_DRQ_INDEX,
+		    drq.as_uchar
+		);
+/*
+** Enable the parallel port IRQ mapping
+*/
+		irq.as_uchar = 
+		    SMC37c669_read_config( SMC37c669_PARALLEL_FDC_IRQ_INDEX );
+
+		irq.by_field.ppt_irq =
+		    SMC37c669_RAW_DEVICE_IRQ(
+			SMC37c669_xlate_irq( local_config[ func ].irq )
+		    );
+
+		SMC37c669_write_config( 
+		    SMC37c669_PARALLEL_FDC_IRQ_INDEX,
+		    irq.as_uchar
+		);
+/*
+** Enable the parallel port base address mapping
+*/
+		base_addr.as_uchar = 0;
+		base_addr.by_field.addr9_2 = local_config[ func ].port1 >> 2;
+
+		SMC37c669_write_config(
+		    SMC37c669_PARALLEL0_BASE_ADDRESS_INDEX,
+		    base_addr.as_uchar
+		);
+		ret_val = TRUE;
+		break;
+	    }
+	case FLOPPY_0:
+	    {
+	    	SMC37c669_FDC_BASE_ADDRESS_REGISTER base_addr;
+		SMC37c669_PARALLEL_FDC_IRQ_REGISTER irq;
+		SMC37c669_PARALLEL_FDC_DRQ_REGISTER drq;
+/*
+** Enable the floppy controller DMA channel mapping
+*/
+	    	drq.as_uchar =
+		    SMC37c669_read_config( SMC37c669_PARALLEL_FDC_DRQ_INDEX );
+		 
+		drq.by_field.fdc_drq =
+		    SMC37c669_RAW_DEVICE_DRQ(
+			SMC37c669_xlate_drq( local_config[ func ].drq )
+		    );
+		 
+		SMC37c669_write_config( 
+		    SMC37c669_PARALLEL_FDC_DRQ_INDEX,
+		    drq.as_uchar
+		);
+/*
+** Enable the floppy controller IRQ mapping
+*/
+		irq.as_uchar =
+		    SMC37c669_read_config( SMC37c669_PARALLEL_FDC_IRQ_INDEX );
+		 
+		irq.by_field.fdc_irq =
+		    SMC37c669_RAW_DEVICE_IRQ(
+			SMC37c669_xlate_irq( local_config[ func ].irq )
+		    );
+		 
+		SMC37c669_write_config(
+		    SMC37c669_PARALLEL_FDC_IRQ_INDEX,
+		    irq.as_uchar
+		);
+/*
+** Enable the floppy controller base address mapping
+*/
+		base_addr.as_uchar = 0;
+		base_addr.by_field.addr9_4 = local_config[ func ].port1 >> 4;
+		 
+		SMC37c669_write_config(
+		    SMC37c669_FDC_BASE_ADDRESS_INDEX,
+		    base_addr.as_uchar
+		);
+		ret_val = TRUE;
+		break;
+	    }
+	case IDE_0:
+	    {
+	    	SMC37c669_IDE_ADDRESS_REGISTER ide_addr;
+/*
+** Enable the IDE alternate status base address mapping
+*/
+	    	ide_addr.as_uchar = 0;
+		ide_addr.by_field.addr9_4 = local_config[ func ].port2 >> 4;
+		 
+		SMC37c669_write_config(
+		    SMC37c669_IDE_ALTERNATE_ADDRESS_INDEX,
+		    ide_addr.as_uchar
+		);
+/*
+** Enable the IDE controller base address mapping
+*/
+		ide_addr.as_uchar = 0;
+		ide_addr.by_field.addr9_4 = local_config[ func ].port1 >> 4;
+		 
+		SMC37c669_write_config(
+		    SMC37c669_IDE_BASE_ADDRESS_INDEX,
+		    ide_addr.as_uchar
+		);
+		ret_val = TRUE;
+		break;
+	    }
+    }
+/*
+** Exit configuration mode and return
+*/
+    SMC37c669_config_mode( FALSE );
+
+    return ret_val;
+}
+
+
+/*
+**++
+**  FUNCTIONAL DESCRIPTION:
+**
+**      This function disables a device function within the
+**	SMC37c669 Super I/O controller.
+**
+**  FORMAL PARAMETERS:
+**
+**      func:
+**          Which function to disable
+**
+**  RETURN VALUE:
+**
+**      Return TRUE if the device function was disabled, otherwise, FALSE
+**
+**  SIDE EFFECTS:
+**
+**      {@description or none@}
+**
+**  DESIGN:
+**
+**      Disabling a function in the SMC37c669 device involves
+**	disabling all the function's mappings (port, irq, drq ...).
+**	A shadow copy of the device configuration is maintained
+**	in local storage so we won't worry aboving saving the
+**	current configuration information.
+**
+**--
+*/
+unsigned int __init SMC37c669_disable_device ( unsigned int func )
+{
+    unsigned int ret_val = FALSE;
+
+/*
+** Put the device into configuration mode
+*/
+    SMC37c669_config_mode( TRUE );
+    switch ( func ) {
+    	case SERIAL_0:
+	    {
+	    	SMC37c669_SERIAL_BASE_ADDRESS_REGISTER base_addr;
+		SMC37c669_SERIAL_IRQ_REGISTER irq;
+/*
+** Disable the serial 1 IRQ mapping
+*/
+	    	irq.as_uchar = 
+		    SMC37c669_read_config( SMC37c669_SERIAL_IRQ_INDEX );
+
+		irq.by_field.uart1_irq = 0;
+
+		SMC37c669_write_config( SMC37c669_SERIAL_IRQ_INDEX, irq.as_uchar );
+/*
+** Disable the serial 1 port base address mapping
+*/
+		base_addr.as_uchar = 0;
+		SMC37c669_write_config( 
+		    SMC37c669_SERIAL0_BASE_ADDRESS_INDEX,
+		    base_addr.as_uchar
+		);
+		ret_val = TRUE;
+		break;
+	    }
+	case SERIAL_1:
+	    {
+	    	SMC37c669_SERIAL_BASE_ADDRESS_REGISTER base_addr;
+		SMC37c669_SERIAL_IRQ_REGISTER irq;
+/*
+** Disable the serial 2 IRQ mapping
+*/
+	    	irq.as_uchar = 
+		    SMC37c669_read_config( SMC37c669_SERIAL_IRQ_INDEX );
+
+		irq.by_field.uart2_irq = 0;
+
+		SMC37c669_write_config( SMC37c669_SERIAL_IRQ_INDEX, irq.as_uchar );
+/*
+** Disable the serial 2 port base address mapping
+*/
+		base_addr.as_uchar = 0;
+
+		SMC37c669_write_config( 
+		    SMC37c669_SERIAL1_BASE_ADDRESS_INDEX,
+		    base_addr.as_uchar
+		);
+		ret_val = TRUE;
+		break;
+	    }
+	case PARALLEL_0:
+	    {
+	    	SMC37c669_PARALLEL_BASE_ADDRESS_REGISTER base_addr;
+		SMC37c669_PARALLEL_FDC_IRQ_REGISTER irq;
+		SMC37c669_PARALLEL_FDC_DRQ_REGISTER drq;
+/*
+** Disable the parallel port DMA channel mapping
+*/
+	    	drq.as_uchar =
+		    SMC37c669_read_config( SMC37c669_PARALLEL_FDC_DRQ_INDEX );
+
+		drq.by_field.ppt_drq = 0;
+
+		SMC37c669_write_config(
+		    SMC37c669_PARALLEL_FDC_DRQ_INDEX,
+		    drq.as_uchar
+		);
+/*
+** Disable the parallel port IRQ mapping
+*/
+		irq.as_uchar = 
+		    SMC37c669_read_config( SMC37c669_PARALLEL_FDC_IRQ_INDEX );
+
+		irq.by_field.ppt_irq = 0;
+
+		SMC37c669_write_config( 
+		    SMC37c669_PARALLEL_FDC_IRQ_INDEX,
+		    irq.as_uchar
+		);
+/*
+** Disable the parallel port base address mapping
+*/
+		base_addr.as_uchar = 0;
+
+		SMC37c669_write_config(
+		    SMC37c669_PARALLEL0_BASE_ADDRESS_INDEX,
+		    base_addr.as_uchar
+		);
+		ret_val = TRUE;
+		break;
+	    }
+	case FLOPPY_0:
+	    {
+	    	SMC37c669_FDC_BASE_ADDRESS_REGISTER base_addr;
+		SMC37c669_PARALLEL_FDC_IRQ_REGISTER irq;
+		SMC37c669_PARALLEL_FDC_DRQ_REGISTER drq;
+/*
+** Disable the floppy controller DMA channel mapping
+*/
+	    	drq.as_uchar =
+		    SMC37c669_read_config( SMC37c669_PARALLEL_FDC_DRQ_INDEX );
+		 
+		drq.by_field.fdc_drq = 0;
+		 
+		SMC37c669_write_config( 
+		    SMC37c669_PARALLEL_FDC_DRQ_INDEX,
+		    drq.as_uchar
+		);
+/*
+** Disable the floppy controller IRQ mapping
+*/
+		irq.as_uchar =
+		    SMC37c669_read_config( SMC37c669_PARALLEL_FDC_IRQ_INDEX );
+		 
+		irq.by_field.fdc_irq = 0;
+		 
+		SMC37c669_write_config(
+		    SMC37c669_PARALLEL_FDC_IRQ_INDEX,
+		    irq.as_uchar
+		);
+/*
+** Disable the floppy controller base address mapping
+*/
+		base_addr.as_uchar = 0;
+		 
+		SMC37c669_write_config(
+		    SMC37c669_FDC_BASE_ADDRESS_INDEX,
+		    base_addr.as_uchar
+		);
+		ret_val = TRUE;
+		break;
+	    }
+	case IDE_0:
+	    {
+	    	SMC37c669_IDE_ADDRESS_REGISTER ide_addr;
+/*
+** Disable the IDE alternate status base address mapping
+*/
+	    	ide_addr.as_uchar = 0;
+		 
+		SMC37c669_write_config(
+		    SMC37c669_IDE_ALTERNATE_ADDRESS_INDEX,
+		    ide_addr.as_uchar
+		);
+/*
+** Disable the IDE controller base address mapping
+*/
+		ide_addr.as_uchar = 0;
+		 
+		SMC37c669_write_config(
+		    SMC37c669_IDE_BASE_ADDRESS_INDEX,
+		    ide_addr.as_uchar
+		);
+		ret_val = TRUE;
+		break;
+	    }
+    }
+/*
+** Exit configuration mode and return
+*/
+    SMC37c669_config_mode( FALSE );
+
+    return ret_val;
+}
+
+
+/*
+**++
+**  FUNCTIONAL DESCRIPTION:
+**
+**      This function configures a device function within the 
+**	SMC37c669 Super I/O controller.
+**
+**  FORMAL PARAMETERS:
+**
+**      func:
+**          Which device function
+**       
+**      port:
+**          I/O port for the function to use
+**	 
+**      irq:
+**          IRQ for the device function to use
+**	 
+**      drq:
+**          DMA channel for the device function to use
+**
+**  RETURN VALUE:
+**
+**      Returns TRUE if the device function was configured, 
+**	otherwise, FALSE.
+**
+**  SIDE EFFECTS:
+**
+**      {@description or none@}
+**
+**  DESIGN:
+**
+**	If this function returns TRUE, the local shadow copy of
+**	the configuration is also updated.  If the device function
+**	is currently disabled, only the local shadow copy is 
+**	updated and the actual device function will be updated
+**	if/when is is enabled.
+**
+**--
+*/
+unsigned int __init SMC37c669_configure_device (
+    unsigned int func,
+    int port,
+    int irq,
+    int drq )
+{
+    struct DEVICE_CONFIG *cp;
+
+/*
+** Check for a valid configuration
+*/
+    if ( ( cp = SMC37c669_get_config ( func ) ) != NULL ) {
+/*
+** Configuration is valid, update the local shadow copy
+*/
+    	if ( ( drq & ~0xFF ) == 0 ) {
+	    cp->drq = drq;
+	}
+	if ( ( irq & ~0xFF ) == 0 ) {
+	    cp->irq = irq;
+	}
+	if ( ( port & ~0xFFFF ) == 0 ) {
+	    cp->port1 = port;
+	}
+/*
+** If the device function is enabled, update the actual
+** device configuration.
+*/
+	if ( SMC37c669_is_device_enabled( func ) ) {
+	    SMC37c669_enable_device( func );
+	}
+	return TRUE;
+    }
+    return FALSE;
+}
+
+
+/*
+**++
+**  FUNCTIONAL DESCRIPTION:
+**
+**      This function determines whether a device function
+**	within the SMC37c669 controller is enabled.
+**
+**  FORMAL PARAMETERS:
+**
+**      func:
+**          Which device function
+**
+**  RETURN VALUE:
+**
+**      Returns TRUE if the device function is enabled, otherwise, FALSE
+**
+**  SIDE EFFECTS:
+**
+**      {@description or none@}
+**
+**  DESIGN:
+**
+**      To check whether a device is enabled we will only look at 
+**	the port base address mapping.  According to the SMC37c669
+**	specification, all of the port base address mappings are
+**	disabled if the addr<9:8> (bits <7:6> of the register) are
+**	zero.
+**
+**--
+*/
+static unsigned int __init SMC37c669_is_device_enabled ( unsigned int func )
+{
+    unsigned char base_addr = 0;
+    unsigned int dev_ok = FALSE;
+    unsigned int ret_val = FALSE;
+/*
+** Enter configuration mode
+*/
+    SMC37c669_config_mode( TRUE );
+     
+    switch ( func ) {
+    	case SERIAL_0:
+	    base_addr =
+		SMC37c669_read_config( SMC37c669_SERIAL0_BASE_ADDRESS_INDEX );
+	    dev_ok = TRUE;
+	    break;
+	case SERIAL_1:
+	    base_addr =
+		SMC37c669_read_config( SMC37c669_SERIAL1_BASE_ADDRESS_INDEX );
+	    dev_ok = TRUE;
+	    break;
+	case PARALLEL_0:
+	    base_addr =
+		SMC37c669_read_config( SMC37c669_PARALLEL0_BASE_ADDRESS_INDEX );
+	    dev_ok = TRUE;
+	    break;
+	case FLOPPY_0:
+	    base_addr =
+		SMC37c669_read_config( SMC37c669_FDC_BASE_ADDRESS_INDEX );
+	    dev_ok = TRUE;
+	    break;
+	case IDE_0:
+	    base_addr =
+		SMC37c669_read_config( SMC37c669_IDE_BASE_ADDRESS_INDEX );
+	    dev_ok = TRUE;
+	    break;
+    }
+/*
+** If we have a valid device, check base_addr<7:6> to see if the
+** device is enabled (mapped).
+*/
+    if ( ( dev_ok ) && ( ( base_addr & 0xC0 ) != 0 ) ) {
+/*
+** The mapping is not disabled, so assume that the function is 
+** enabled.
+*/
+    	ret_val = TRUE;
+    }
+/*
+** Exit configuration mode 
+*/
+    SMC37c669_config_mode( FALSE );
+
+    return ret_val;
+}
+
+
+#if 0
+/*
+**++
+**  FUNCTIONAL DESCRIPTION:
+**
+**      This function retrieves the configuration information of a 
+**	device function within the SMC37c699 Super I/O controller.
+**
+**  FORMAL PARAMETERS:
+**
+**      func:
+**          Which device function
+**       
+**      port:
+**          I/O port returned
+**	 
+**      irq:
+**          IRQ returned
+**	 
+**      drq:
+**          DMA channel returned
+**
+**  RETURN VALUE:
+**
+**      Returns TRUE if the device configuration was successfully
+**	retrieved, otherwise, FALSE.
+**
+**  SIDE EFFECTS:
+**
+**      The data pointed to by the port, irq, and drq parameters
+**	my be modified even if the configuration is not successfully
+**	retrieved.
+**
+**  DESIGN:
+**
+**      The device configuration is fetched from the local shadow
+**	copy.  Any unused parameters will be set to -1.  Any
+**	parameter which is not desired can specify the NULL
+**	pointer.
+**
+**--
+*/
+static unsigned int __init SMC37c669_get_device_config (
+    unsigned int func,
+    int *port,
+    int *irq,
+    int *drq )
+{
+    struct DEVICE_CONFIG *cp;
+    unsigned int ret_val = FALSE;
+/*
+** Check for a valid device configuration
+*/
+    if ( ( cp = SMC37c669_get_config( func ) ) != NULL ) {
+    	if ( drq != NULL ) {
+	    *drq = cp->drq;
+	    ret_val = TRUE;
+	}
+	if ( irq != NULL ) {
+	    *irq = cp->irq;
+	    ret_val = TRUE;
+	}
+	if ( port != NULL ) {
+	    *port = cp->port1;
+	    ret_val = TRUE;
+	}
+    }
+    return ret_val;
+}
+#endif
+
+
+/*
+**++
+**  FUNCTIONAL DESCRIPTION:
+**
+**      This function displays the current state of the SMC37c699
+**	Super I/O controller's device functions.
+**
+**  FORMAL PARAMETERS:
+**
+**      None
+**
+**  RETURN VALUE:
+**
+**      None
+**
+**  SIDE EFFECTS:
+**
+**      None
+**
+**--
+*/
+void __init SMC37c669_display_device_info ( void )
+{
+    if ( SMC37c669_is_device_enabled( SERIAL_0 ) ) {
+    	printk( "  Serial 0:    Enabled [ Port 0x%x, IRQ %d ]\n",
+		 local_config[ SERIAL_0 ].port1,
+		 local_config[ SERIAL_0 ].irq
+	);
+    }
+    else {
+    	printk( "  Serial 0:    Disabled\n" );
+    }
+
+    if ( SMC37c669_is_device_enabled( SERIAL_1 ) ) {
+    	printk( "  Serial 1:    Enabled [ Port 0x%x, IRQ %d ]\n",
+		 local_config[ SERIAL_1 ].port1,
+		 local_config[ SERIAL_1 ].irq
+	);
+    }
+    else {
+    	printk( "  Serial 1:    Disabled\n" );
+    }
+
+    if ( SMC37c669_is_device_enabled( PARALLEL_0 ) ) {
+    	printk( "  Parallel:    Enabled [ Port 0x%x, IRQ %d/%d ]\n",
+		 local_config[ PARALLEL_0 ].port1,
+		 local_config[ PARALLEL_0 ].irq,
+		 local_config[ PARALLEL_0 ].drq
+	);
+    }
+    else {
+    	printk( "  Parallel:    Disabled\n" );
+    }
+
+    if ( SMC37c669_is_device_enabled( FLOPPY_0 ) ) {
+    	printk( "  Floppy Ctrl: Enabled [ Port 0x%x, IRQ %d/%d ]\n",
+		 local_config[ FLOPPY_0 ].port1,
+		 local_config[ FLOPPY_0 ].irq,
+		 local_config[ FLOPPY_0 ].drq
+	);
+    }
+    else {
+    	printk( "  Floppy Ctrl: Disabled\n" );
+    }
+
+    if ( SMC37c669_is_device_enabled( IDE_0 ) ) {
+    	printk( "  IDE 0:       Enabled [ Port 0x%x, IRQ %d ]\n",
+		 local_config[ IDE_0 ].port1,
+		 local_config[ IDE_0 ].irq
+	);
+    }
+    else {
+    	printk( "  IDE 0:       Disabled\n" );
+    }
+}
+
+
+/*
+**++
+**  FUNCTIONAL DESCRIPTION:
+**
+**      This function puts the SMC37c669 Super I/O controller into,
+**	and takes it out of, configuration mode.
+**
+**  FORMAL PARAMETERS:
+**
+**      enable:
+**          TRUE to enter configuration mode, FALSE to exit.
+**
+**  RETURN VALUE:
+**
+**      None
+**
+**  SIDE EFFECTS:
+**
+**      The SMC37c669 controller may be left in configuration mode.
+**
+**--
+*/
+static void __init SMC37c669_config_mode( 
+    unsigned int enable )
+{
+    if ( enable ) {
+/*
+** To enter configuration mode, two writes in succession to the index
+** port are required.  If a write to another address or port occurs
+** between these two writes, the chip does not enter configuration
+** mode.  Therefore, a spinlock is placed around the two writes to 
+** guarantee that they complete uninterrupted.
+*/
+	spinlock( &spl_atomic );
+    	wb( &SMC37c669->index_port, SMC37c669_CONFIG_ON_KEY );
+    	wb( &SMC37c669->index_port, SMC37c669_CONFIG_ON_KEY );
+	spinunlock( &spl_atomic );
+    }
+    else {
+    	wb( &SMC37c669->index_port, SMC37c669_CONFIG_OFF_KEY );
+    }
+}
+
+/*
+**++
+**  FUNCTIONAL DESCRIPTION:
+**
+**      This function reads an SMC37c669 Super I/O controller
+**	configuration register.  This function assumes that the
+**	device is already in configuration mode.
+**
+**  FORMAL PARAMETERS:
+**
+**      index:
+**          Index value of configuration register to read
+**
+**  RETURN VALUE:
+**
+**      Data read from configuration register
+**
+**  SIDE EFFECTS:
+**
+**      None
+**
+**--
+*/
+static unsigned char __init SMC37c669_read_config( 
+    unsigned char index )
+{
+    unsigned char data;
+
+    wb( &SMC37c669->index_port, index );
+    data = rb( &SMC37c669->data_port );
+    return data;
+}
+
+/*
+**++
+**  FUNCTIONAL DESCRIPTION:
+**
+**      This function writes an SMC37c669 Super I/O controller
+**	configuration register.  This function assumes that the
+**	device is already in configuration mode.
+**
+**  FORMAL PARAMETERS:
+**
+**      index:
+**          Index of configuration register to write
+**       
+**      data:
+**          Data to be written
+**
+**  RETURN VALUE:
+**
+**      None
+**
+**  SIDE EFFECTS:
+**
+**      None
+**
+**--
+*/
+static void __init SMC37c669_write_config( 
+    unsigned char index, 
+    unsigned char data )
+{
+    wb( &SMC37c669->index_port, index );
+    wb( &SMC37c669->data_port, data );
+}
+
+
+/*
+**++
+**  FUNCTIONAL DESCRIPTION:
+**
+**      This function initializes the local device
+**	configuration storage.  This function assumes
+**	that the device is already in configuration
+**	mode.
+**
+**  FORMAL PARAMETERS:
+**
+**      None
+**
+**  RETURN VALUE:
+**
+**      None
+**
+**  SIDE EFFECTS:
+**
+**      Local storage for device configuration information
+**	is initialized.
+**
+**--
+*/
+static void __init SMC37c669_init_local_config ( void )
+{
+    SMC37c669_SERIAL_BASE_ADDRESS_REGISTER uart_base;
+    SMC37c669_SERIAL_IRQ_REGISTER uart_irqs;
+    SMC37c669_PARALLEL_BASE_ADDRESS_REGISTER ppt_base;
+    SMC37c669_PARALLEL_FDC_IRQ_REGISTER ppt_fdc_irqs;
+    SMC37c669_PARALLEL_FDC_DRQ_REGISTER ppt_fdc_drqs;
+    SMC37c669_FDC_BASE_ADDRESS_REGISTER fdc_base;
+    SMC37c669_IDE_ADDRESS_REGISTER ide_base;
+    SMC37c669_IDE_ADDRESS_REGISTER ide_alt;
+
+/*
+** Get serial port 1 base address 
+*/
+    uart_base.as_uchar = 
+	SMC37c669_read_config( SMC37c669_SERIAL0_BASE_ADDRESS_INDEX );
+/*
+** Get IRQs for serial ports 1 & 2
+*/
+    uart_irqs.as_uchar = 
+	SMC37c669_read_config( SMC37c669_SERIAL_IRQ_INDEX );
+/*
+** Store local configuration information for serial port 1
+*/
+    local_config[SERIAL_0].port1 = uart_base.by_field.addr9_3 << 3;
+    local_config[SERIAL_0].irq = 
+	SMC37c669_xlate_irq( 
+	    SMC37c669_DEVICE_IRQ( uart_irqs.by_field.uart1_irq ) 
+	);
+/*
+** Get serial port 2 base address
+*/
+    uart_base.as_uchar = 
+	SMC37c669_read_config( SMC37c669_SERIAL1_BASE_ADDRESS_INDEX );
+/*
+** Store local configuration information for serial port 2
+*/
+    local_config[SERIAL_1].port1 = uart_base.by_field.addr9_3 << 3;
+    local_config[SERIAL_1].irq = 
+	SMC37c669_xlate_irq( 
+	    SMC37c669_DEVICE_IRQ( uart_irqs.by_field.uart2_irq ) 
+	);
+/*
+** Get parallel port base address
+*/
+    ppt_base.as_uchar =
+	SMC37c669_read_config( SMC37c669_PARALLEL0_BASE_ADDRESS_INDEX );
+/*
+** Get IRQs for parallel port and floppy controller
+*/
+    ppt_fdc_irqs.as_uchar =
+	SMC37c669_read_config( SMC37c669_PARALLEL_FDC_IRQ_INDEX );
+/*
+** Get DRQs for parallel port and floppy controller
+*/
+    ppt_fdc_drqs.as_uchar =
+	SMC37c669_read_config( SMC37c669_PARALLEL_FDC_DRQ_INDEX );
+/*
+** Store local configuration information for parallel port
+*/
+    local_config[PARALLEL_0].port1 = ppt_base.by_field.addr9_2 << 2;
+    local_config[PARALLEL_0].irq =
+	SMC37c669_xlate_irq(
+	    SMC37c669_DEVICE_IRQ( ppt_fdc_irqs.by_field.ppt_irq )
+	);
+    local_config[PARALLEL_0].drq =
+	SMC37c669_xlate_drq(
+	    SMC37c669_DEVICE_DRQ( ppt_fdc_drqs.by_field.ppt_drq )
+	);
+/*
+** Get floppy controller base address
+*/
+    fdc_base.as_uchar = 
+	SMC37c669_read_config( SMC37c669_FDC_BASE_ADDRESS_INDEX );
+/*
+** Store local configuration information for floppy controller
+*/
+    local_config[FLOPPY_0].port1 = fdc_base.by_field.addr9_4 << 4;
+    local_config[FLOPPY_0].irq =
+	SMC37c669_xlate_irq(
+	    SMC37c669_DEVICE_IRQ( ppt_fdc_irqs.by_field.fdc_irq )
+	);
+    local_config[FLOPPY_0].drq =
+	SMC37c669_xlate_drq(
+	    SMC37c669_DEVICE_DRQ( ppt_fdc_drqs.by_field.fdc_drq )
+	);
+/*
+** Get IDE controller base address
+*/
+    ide_base.as_uchar =
+	SMC37c669_read_config( SMC37c669_IDE_BASE_ADDRESS_INDEX );
+/*
+** Get IDE alternate status base address
+*/
+    ide_alt.as_uchar =
+	SMC37c669_read_config( SMC37c669_IDE_ALTERNATE_ADDRESS_INDEX );
+/*
+** Store local configuration information for IDE controller
+*/
+    local_config[IDE_0].port1 = ide_base.by_field.addr9_4 << 4;
+    local_config[IDE_0].port2 = ide_alt.by_field.addr9_4 << 4;
+    local_config[IDE_0].irq = 14;
+}
+
+
+/*
+**++
+**  FUNCTIONAL DESCRIPTION:
+**
+**      This function returns a pointer to the local shadow
+**	configuration of the requested device function.
+**
+**  FORMAL PARAMETERS:
+**
+**      func:
+**          Which device function
+**
+**  RETURN VALUE:
+**
+**      Returns a pointer to the DEVICE_CONFIG structure for the
+**	requested function, otherwise, NULL.
+**
+**  SIDE EFFECTS:
+**
+**      {@description or none@}
+**
+**--
+*/
+static struct DEVICE_CONFIG * __init SMC37c669_get_config( unsigned int func )
+{
+    struct DEVICE_CONFIG *cp = NULL;
+
+    switch ( func ) {
+    	case SERIAL_0:
+	    cp = &local_config[ SERIAL_0 ];
+	    break;
+	case SERIAL_1:
+	    cp = &local_config[ SERIAL_1 ];
+	    break;
+	case PARALLEL_0:
+	    cp = &local_config[ PARALLEL_0 ];
+	    break;
+	case FLOPPY_0:
+	    cp = &local_config[ FLOPPY_0 ];
+	    break;
+	case IDE_0:
+	    cp = &local_config[ IDE_0 ];
+	    break;
+    }
+    return cp;
+}
+
+/*
+**++
+**  FUNCTIONAL DESCRIPTION:
+**
+**      This function translates IRQs back and forth between ISA
+**	IRQs and SMC37c669 device IRQs.
+**
+**  FORMAL PARAMETERS:
+**
+**      irq:
+**          The IRQ to translate
+**
+**  RETURN VALUE:
+**
+**      Returns the translated IRQ, otherwise, returns -1.
+**
+**  SIDE EFFECTS:
+**
+**      {@description or none@}
+**
+**--
+*/
+static int __init SMC37c669_xlate_irq ( unsigned int irq )
+{
+    int i, translated_irq = -1;
+
+    if ( SMC37c669_IS_DEVICE_IRQ( irq ) ) {
+/*
+** We are translating a device IRQ to an ISA IRQ
+*/
+    	for ( i = 0; ( SMC37c669_irq_table[i].device_irq != -1 ) || ( SMC37c669_irq_table[i].isa_irq != -1 ); i++ ) {
+	    if ( irq == SMC37c669_irq_table[i].device_irq ) {
+	    	translated_irq = SMC37c669_irq_table[i].isa_irq;
+		break;
+	    }
+	}
+    }
+    else {
+/*
+** We are translating an ISA IRQ to a device IRQ
+*/
+    	for ( i = 0; ( SMC37c669_irq_table[i].isa_irq != -1 ) || ( SMC37c669_irq_table[i].device_irq != -1 ); i++ ) {
+	    if ( irq == SMC37c669_irq_table[i].isa_irq ) {
+	    	translated_irq = SMC37c669_irq_table[i].device_irq;
+		break;
+	    }
+	}
+    }
+    return translated_irq;
+}
+
+
+/*
+**++
+**  FUNCTIONAL DESCRIPTION:
+**
+**      This function translates DMA channels back and forth between
+**	ISA DMA channels and SMC37c669 device DMA channels.
+**
+**  FORMAL PARAMETERS:
+**
+**      drq:
+**          The DMA channel to translate
+**
+**  RETURN VALUE:
+**
+**      Returns the translated DMA channel, otherwise, returns -1
+**
+**  SIDE EFFECTS:
+**
+**      {@description or none@}
+**
+**--
+*/
+static int __init SMC37c669_xlate_drq ( unsigned int drq )
+{
+    int i, translated_drq = -1;
+
+    if ( SMC37c669_IS_DEVICE_DRQ( drq ) ) {
+/*
+** We are translating a device DMA channel to an ISA DMA channel
+*/
+    	for ( i = 0; ( SMC37c669_drq_table[i].device_drq != -1 ) || ( SMC37c669_drq_table[i].isa_drq != -1 ); i++ ) {
+	    if ( drq == SMC37c669_drq_table[i].device_drq ) {
+	    	translated_drq = SMC37c669_drq_table[i].isa_drq;
+		break;
+	    }
+	}
+    }
+    else {
+/*
+** We are translating an ISA DMA channel to a device DMA channel
+*/
+    	for ( i = 0; ( SMC37c669_drq_table[i].isa_drq != -1 ) || ( SMC37c669_drq_table[i].device_drq != -1 ); i++ ) {
+	    if ( drq == SMC37c669_drq_table[i].isa_drq ) {
+	    	translated_drq = SMC37c669_drq_table[i].device_drq;
+		break;
+	    }
+	}
+    }
+    return translated_drq;
+}
+
+#if 0
+int __init smcc669_init ( void )
+{
+    struct INODE *ip;
+
+    allocinode( smc_ddb.name, 1, &ip );
+    ip->dva = &smc_ddb;
+    ip->attr = ATTR$M_WRITE | ATTR$M_READ;
+    ip->len[0] = 0x30;
+    ip->misc = 0;
+    INODE_UNLOCK( ip );
+
+    return msg_success;
+}
+
+int __init smcc669_open( struct FILE *fp, char *info, char *next, char *mode )
+{
+    struct INODE *ip;
+/*
+** Allow multiple readers but only one writer.  ip->misc keeps track
+** of the number of writers
+*/
+    ip = fp->ip;
+    INODE_LOCK( ip );
+    if ( fp->mode & ATTR$M_WRITE ) {
+	if ( ip->misc ) {
+	    INODE_UNLOCK( ip );
+	    return msg_failure;	    /* too many writers */
+	}
+	ip->misc++;
+    }
+/*
+** Treat the information field as a byte offset
+*/
+    *fp->offset = xtoi( info );
+    INODE_UNLOCK( ip );
+
+    return msg_success;
+}
+
+int __init smcc669_close( struct FILE *fp )
+{
+    struct INODE *ip;
+
+    ip = fp->ip;
+    if ( fp->mode & ATTR$M_WRITE ) {
+	INODE_LOCK( ip );
+	ip->misc--;
+	INODE_UNLOCK( ip );
+    }
+    return msg_success;
+}
+
+int __init smcc669_read( struct FILE *fp, int size, int number, unsigned char *buf )
+{
+    int i;
+    int length;
+    int nbytes;
+    struct INODE *ip;
+
+/*
+** Always access a byte at a time
+*/
+    ip = fp->ip;
+    length = size * number;
+    nbytes = 0;
+
+    SMC37c669_config_mode( TRUE );
+    for ( i = 0; i < length; i++ ) {
+	if ( !inrange( *fp->offset, 0, ip->len[0] ) ) 
+	    break;
+	*buf++ = SMC37c669_read_config( *fp->offset );
+	*fp->offset += 1;
+	nbytes++;
+    }
+    SMC37c669_config_mode( FALSE );
+    return nbytes;
+}
+
+int __init smcc669_write( struct FILE *fp, int size, int number, unsigned char *buf )
+{
+    int i;
+    int length;
+    int nbytes;
+    struct INODE *ip;
+/*
+** Always access a byte at a time
+*/
+    ip = fp->ip;
+    length = size * number;
+    nbytes = 0;
+
+    SMC37c669_config_mode( TRUE );
+    for ( i = 0; i < length; i++ ) {
+	if ( !inrange( *fp->offset, 0, ip->len[0] ) ) 
+	    break;
+	SMC37c669_write_config( *fp->offset, *buf );
+	*fp->offset += 1;
+	buf++;
+	nbytes++;
+    }
+    SMC37c669_config_mode( FALSE );
+    return nbytes;
+}
+#endif
+
+void __init
+SMC37c669_dump_registers(void)
+{
+  int i;
+  for (i = 0; i <= 0x29; i++)
+    printk("-- CR%02x : %02x\n", i, SMC37c669_read_config(i));
+}
+/*+
+ * ============================================================================
+ * = SMC_init - SMC37c669 Super I/O controller initialization                 =
+ * ============================================================================
+ *
+ * OVERVIEW:
+ *
+ *      This routine configures and enables device functions on the
+ *      SMC37c669 Super I/O controller.
+ *
+ * FORM OF CALL:
+ *
+ *      SMC_init( );
+ *
+ * RETURNS:
+ *
+ *      Nothing
+ *
+ * ARGUMENTS:
+ *
+ *      None
+ *
+ * SIDE EFFECTS:
+ *
+ *      None
+ *
+ */
+void __init SMC669_Init ( void )
+{
+    SMC37c669_CONFIG_REGS *SMC_base;
+
+    if ( ( SMC_base = SMC37c669_detect( ) ) != NULL ) {
+        printk( "SMC37c669 Super I/O Controller found @ 0x%lx\n",
+		(unsigned long) SMC_base );
+#if SMC_DEBUG
+	SMC37c669_config_mode( TRUE );
+	SMC37c669_dump_registers( );
+	SMC37c669_config_mode( FALSE );
+        SMC37c669_display_device_info( );
+#endif
+        SMC37c669_disable_device( SERIAL_0 );
+        SMC37c669_configure_device(
+            SERIAL_0,
+            COM1_BASE,
+            COM1_IRQ,
+            -1
+        );
+        SMC37c669_enable_device( SERIAL_0 );
+
+        SMC37c669_disable_device( SERIAL_1 );
+        SMC37c669_configure_device(
+            SERIAL_1,
+            COM2_BASE,
+            COM2_IRQ,
+            -1
+        );
+        SMC37c669_enable_device( SERIAL_1 );
+
+        SMC37c669_disable_device( PARALLEL_0 );
+        SMC37c669_configure_device(
+            PARALLEL_0,
+            PARP_BASE,
+            PARP_IRQ,
+            PARP_DRQ
+        );
+        SMC37c669_enable_device( PARALLEL_0 );
+
+        SMC37c669_disable_device( FLOPPY_0 );
+        SMC37c669_configure_device(
+            FLOPPY_0,
+            FDC_BASE,
+            FDC_IRQ,
+            FDC_DRQ
+        );
+        SMC37c669_enable_device( FLOPPY_0 );
+          
+        SMC37c669_disable_device( IDE_0 );
+
+#if SMC_DEBUG
+	SMC37c669_config_mode( TRUE );
+	SMC37c669_dump_registers( );
+	SMC37c669_config_mode( FALSE );
+        SMC37c669_display_device_info( );
+#endif
+    }
+    else {
+#if SMC_DEBUG
+        printk( "No SMC37c669 Super I/O Controller found\n" );
+#endif
+    }
+}
diff -u --recursive --new-file v2.1.88/linux/arch/alpha/kernel/smc37c93x.c linux/arch/alpha/kernel/smc37c93x.c
--- v2.1.88/linux/arch/alpha/kernel/smc37c93x.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/alpha/kernel/smc37c93x.c	Mon Feb 23 10:25:10 1998
@@ -0,0 +1,264 @@
+/*
+ * SMC 37C93X initialization code
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+
+#include <linux/bios32.h>
+#include <linux/pci.h>
+#include <linux/malloc.h>
+#include <linux/mm.h>
+#include <linux/init.h>
+
+#include <asm/hwrpb.h>
+#include <asm/io.h>
+#include <asm/segment.h>
+
+#if 0
+# define DBG_DEVS(args)         printk args
+#else
+# define DBG_DEVS(args)
+#endif
+
+#define KB              1024
+#define MB              (1024*KB)
+#define GB              (1024*MB)
+
+/* device "activate" register contents */
+#define DEVICE_ON		1
+#define DEVICE_OFF		0
+
+/* configuration on/off keys */
+#define CONFIG_ON_KEY		0x55
+#define CONFIG_OFF_KEY		0xaa
+
+/* configuration space device definitions */
+#define FDC			0
+#define IDE1			1
+#define IDE2			2
+#define PARP			3
+#define SER1			4
+#define SER2			5
+#define RTCL			6
+#define KYBD			7
+#define AUXIO			8
+
+/* Chip register offsets from base */
+#define CONFIG_CONTROL		0x02
+#define INDEX_ADDRESS		0x03
+#define LOGICAL_DEVICE_NUMBER	0x07
+#define DEVICE_ID		0x20
+#define DEVICE_REV		0x21
+#define POWER_CONTROL		0x22
+#define POWER_MGMT		0x23
+#define OSC			0x24
+
+#define ACTIVATE		0x30
+#define ADDR_HI			0x60
+#define ADDR_LO			0x61
+#define INTERRUPT_SEL		0x70
+#define INTERRUPT_SEL_2		0x72 /* KYBD/MOUS only */
+#define DMA_CHANNEL_SEL		0x74 /* FDC/PARP only */
+
+#define FDD_MODE_REGISTER	0x90
+#define FDD_OPTION_REGISTER	0x91
+
+/* values that we read back that are expected ... */
+#define VALID_DEVICE_ID		2
+
+/* default device addresses */
+#define KYBD_INTERRUPT		1
+#define MOUS_INTERRUPT		12
+#define COM2_BASE		0x2f8
+#define COM2_INTERRUPT		3
+#define COM1_BASE		0x3f8
+#define COM1_INTERRUPT		4
+#define PARP_BASE		0x3bc
+#define PARP_INTERRUPT		7
+
+#define SMC_DEBUG 0
+
+static unsigned long __init SMCConfigState(unsigned long baseAddr)
+{
+	unsigned char devId;
+	unsigned char devRev;
+
+	unsigned long configPort;
+	unsigned long indexPort;
+	unsigned long dataPort;
+
+	configPort = indexPort = baseAddr;
+	dataPort = configPort + 1;
+
+	outb(CONFIG_ON_KEY, configPort);
+	outb(CONFIG_ON_KEY, configPort);
+	outb(DEVICE_ID, indexPort);
+	devId = inb(dataPort);
+	if ( devId == VALID_DEVICE_ID ) {
+		outb(DEVICE_REV, indexPort);
+		devRev = inb(dataPort);
+	}
+	else {
+		baseAddr = 0;
+	}
+	return baseAddr;
+}
+
+static void __init SMCRunState(unsigned long baseAddr)
+{
+	outb(CONFIG_OFF_KEY, baseAddr);
+}
+
+static unsigned long __init SMCDetectUltraIO(void)
+{
+	unsigned long baseAddr;
+
+	baseAddr = 0x3F0;
+	if ( ( baseAddr = SMCConfigState( baseAddr ) ) == 0x3F0 ) {
+		return( baseAddr );
+	}
+	baseAddr = 0x370;
+	if ( ( baseAddr = SMCConfigState( baseAddr ) ) == 0x370 ) {
+		return( baseAddr );
+	}
+	return( ( unsigned long )0 );
+}
+
+static void __init SMCEnableDevice(unsigned long baseAddr,
+			    unsigned long device,
+			    unsigned long portaddr,
+			    unsigned long interrupt)
+{
+	unsigned long indexPort;
+	unsigned long dataPort;
+
+	indexPort = baseAddr;
+	dataPort = baseAddr + 1;
+
+	outb(LOGICAL_DEVICE_NUMBER, indexPort);
+	outb(device, dataPort);
+
+	outb(ADDR_LO, indexPort);
+	outb(( portaddr & 0xFF ), dataPort);
+
+	outb(ADDR_HI, indexPort);
+	outb((portaddr >> 8) & 0xFF, dataPort);
+
+	outb(INTERRUPT_SEL, indexPort);
+	outb(interrupt, dataPort);
+
+	outb(ACTIVATE, indexPort);
+	outb(DEVICE_ON, dataPort);
+}
+
+static void __init SMCEnableKYBD(unsigned long baseAddr)
+{
+	unsigned long indexPort;
+	unsigned long dataPort;
+
+	indexPort = baseAddr;
+	dataPort = baseAddr + 1;
+
+	outb(LOGICAL_DEVICE_NUMBER, indexPort);
+	outb(KYBD, dataPort);
+
+	outb(INTERRUPT_SEL, indexPort); /* Primary interrupt select */
+	outb(KYBD_INTERRUPT, dataPort);
+
+	outb(INTERRUPT_SEL_2, indexPort); /* Secondary interrupt select */
+	outb(MOUS_INTERRUPT, dataPort);
+
+	outb(ACTIVATE, indexPort);
+	outb(DEVICE_ON, dataPort);
+}
+
+static void __init SMCEnableFDC(unsigned long baseAddr)
+{
+	unsigned long indexPort;
+	unsigned long dataPort;
+
+	unsigned char oldValue;
+
+	indexPort = baseAddr;
+	dataPort = baseAddr + 1;
+
+	outb(LOGICAL_DEVICE_NUMBER, indexPort);
+	outb(FDC, dataPort);
+
+	outb(FDD_MODE_REGISTER, indexPort);
+	oldValue = inb(dataPort);
+
+	oldValue |= 0x0E;                   /* Enable burst mode */
+	outb(oldValue, dataPort);
+
+	outb(INTERRUPT_SEL, indexPort);	    /* Primary interrupt select */
+	outb(0x06, dataPort );
+
+	outb(DMA_CHANNEL_SEL, indexPort);   /* DMA channel select */
+	outb(0x02, dataPort);
+
+	outb(ACTIVATE, indexPort);
+	outb(DEVICE_ON, dataPort);
+}
+
+#if SMC_DEBUG
+static void __init SMCReportDeviceStatus(unsigned long baseAddr)
+{
+	unsigned long indexPort;
+	unsigned long dataPort;
+	unsigned char currentControl;
+
+	indexPort = baseAddr;
+	dataPort = baseAddr + 1;
+
+	outb(POWER_CONTROL, indexPort);
+	currentControl = inb(dataPort);
+
+	printk(currentControl & (1 << FDC)
+	       ? "\t+FDC Enabled\n" : "\t-FDC Disabled\n");
+	printk(currentControl & (1 << IDE1)
+	       ? "\t+IDE1 Enabled\n" : "\t-IDE1 Disabled\n");
+	printk(currentControl & (1 << IDE2)
+	       ? "\t+IDE2 Enabled\n" : "\t-IDE2 Disabled\n");
+	printk(currentControl & (1 << PARP)
+	       ? "\t+PARP Enabled\n" : "\t-PARP Disabled\n");
+	printk(currentControl & (1 << SER1)
+	       ? "\t+SER1 Enabled\n" : "\t-SER1 Disabled\n");
+	printk(currentControl & (1 << SER2)
+	       ? "\t+SER2 Enabled\n" : "\t-SER2 Disabled\n");
+
+	printk( "\n" );
+}
+#endif
+
+int __init SMC93x_Init(void)
+{
+	unsigned long SMCUltraBase;
+
+	if ((SMCUltraBase = SMCDetectUltraIO()) != 0UL) {
+		printk("SMC FDC37C93X Ultra I/O Controller found @ 0x%lx\n",
+		       SMCUltraBase);
+#if SMC_DEBUG
+		SMCReportDeviceStatus(SMCUltraBase);
+#endif
+		SMCEnableDevice(SMCUltraBase, SER1, COM1_BASE, COM1_INTERRUPT);
+		SMCEnableDevice(SMCUltraBase, SER2, COM2_BASE, COM2_INTERRUPT);
+		SMCEnableDevice(SMCUltraBase, PARP, PARP_BASE, PARP_INTERRUPT);
+		/* On PC164, IDE on the SMC is not enabled;
+		   CMD646 (PCI) on MB */
+		SMCEnableKYBD(SMCUltraBase);
+		SMCEnableFDC(SMCUltraBase);
+#if SMC_DEBUG
+		SMCReportDeviceStatus(SMCUltraBase);
+#endif
+		SMCRunState(SMCUltraBase);
+		return 1;
+	}
+	else {
+#if SMC_DEBUG
+		printk("No SMC FDC37C93X Ultra I/O Controller found\n");
+#endif
+		return 0;
+	}
+}
diff -u --recursive --new-file v2.1.88/linux/arch/alpha/math-emu/ieee-math.c linux/arch/alpha/math-emu/ieee-math.c
--- v2.1.88/linux/arch/alpha/math-emu/ieee-math.c	Tue Feb 17 13:12:44 1998
+++ linux/arch/alpha/math-emu/ieee-math.c	Sat Feb 28 22:11:02 1998
@@ -776,25 +776,25 @@
 			midway = (temp.f[0] & 0x003fffffffffffff) == 0;
 			if ((midway && (temp.f[0] & 0x0080000000000000)) ||
 			    !midway)
-				++b;
+				++*b;
 		}
 		break;
 
 	      case ROUND_PINF:
-		if ((temp.f[0] & 0x003fffffffffffff) != 0)
-			++b;
+		if ((temp.f[0] & 0x007fffffffffffff) != 0)
+			++*b;
 		break;
 
 	      case ROUND_NINF:
-		if ((temp.f[0] & 0x003fffffffffffff) != 0)
-			--b;
+		if ((temp.f[0] & 0x007fffffffffffff) != 0)
+			--*b;
 		break;
 
 	      case ROUND_CHOP:
 		/* no action needed */
 		break;
 	}
-	if ((temp.f[0] & 0x003fffffffffffff) != 0)
+	if ((temp.f[0] & 0x007fffffffffffff) != 0)
 		res |= FPCR_INE;
 
 	if (temp.s) {
diff -u --recursive --new-file v2.1.88/linux/arch/i386/kernel/irq.c linux/arch/i386/kernel/irq.c
--- v2.1.88/linux/arch/i386/kernel/irq.c	Mon Feb 23 18:12:02 1998
+++ linux/arch/i386/kernel/irq.c	Thu Feb 26 11:14:04 1998
@@ -82,20 +82,16 @@
  * but we have _much_ higher compatibility and robustness this way.
  */
 
-#ifndef __SMP__
-  static const unsigned int io_apic_irqs = 0;
-#else
-  /*
-   * Default to all normal IRQ's _not_ using the IO APIC.
-   *
-   * To get IO-APIC interrupts you should either:
-   *  - turn some of them into IO-APIC interrupts at runtime
-   *    with some magic system call interface.
-   *  - explicitly use irq 16-19 depending on which PCI irq
-   *    line your PCI controller uses.
-   */
-  unsigned int io_apic_irqs = 0;
-#endif
+/*
+ * Default to all normal IRQ's _not_ using the IO APIC.
+ *
+ * To get IO-APIC interrupts you should either:
+ *  - turn some of them into IO-APIC interrupts at runtime
+ *    with some magic system call interface.
+ *  - explicitly use irq 16-19 depending on which PCI irq
+ *    line your PCI controller uses.
+ */
+unsigned int io_apic_irqs = 0;
 
 struct hw_interrupt_type {
 	void (*handle)(unsigned int irq, int cpu, struct pt_regs * regs);
diff -u --recursive --new-file v2.1.88/linux/arch/i386/kernel/irq.h linux/arch/i386/kernel/irq.h
--- v2.1.88/linux/arch/i386/kernel/irq.h	Thu Feb 12 20:56:04 1998
+++ linux/arch/i386/kernel/irq.h	Sun Mar  1 18:22:22 1998
@@ -20,13 +20,7 @@
 int IO_APIC_get_PCI_irq_vector (int bus, int slot, int fn);
 void make_8259A_irq (unsigned int irq);
 
-#ifdef __SMP__
- extern unsigned int io_apic_irqs;
-#else
- extern const unsigned int io_apic_irqs;
-#endif
-
-#define IO_APIC_IRQ(x) ((1<<x) & io_apic_irqs)
+extern unsigned int io_apic_irqs;
 
 #define MAX_IRQ_SOURCES 128
 #define MAX_MP_BUSSES 32
@@ -62,10 +56,18 @@
 	release_irqlock(cpu);
 }
 
+#define IO_APIC_IRQ(x) ((1<<x) & io_apic_irqs)
+
 #else
 
 #define irq_enter(cpu, irq)	(++local_irq_count[cpu])
 #define irq_exit(cpu, irq)	(--local_irq_count[cpu])
+
+/* Make these no-ops when not using SMP */
+#define enable_IO_APIC_irq(x)	do { } while (0)
+#define disable_IO_APIC_irq(x)	do { } while (0)
+
+#define IO_APIC_IRQ(x)	(0)
 
 #endif
 
diff -u --recursive --new-file v2.1.88/linux/arch/i386/kernel/ldt.c linux/arch/i386/kernel/ldt.c
--- v2.1.88/linux/arch/i386/kernel/ldt.c	Fri Nov 14 18:28:48 1997
+++ linux/arch/i386/kernel/ldt.c	Sat Feb 21 12:42:28 1998
@@ -33,31 +33,6 @@
 	return copy_to_user(ptr, address, size) ? -EFAULT : size;
 }
 
-static inline int limits_ok(struct modify_ldt_ldt_s *ldt_info)
-{
-	unsigned long base, limit;
-	/* linear address of first and last accessible byte */
-	unsigned long first, last;
-
-	base = ldt_info->base_addr;
-	limit = ldt_info->limit;
-	if (ldt_info->limit_in_pages)
-		limit = limit * PAGE_SIZE + PAGE_SIZE - 1;
-
-	first = base;
-	last = limit + base;
-
-	/* segment grows down? */
-	if (ldt_info->contents == 1) {
-		/* data segment grows down */
-		first = base+limit+1;
-		last = base+65535;
-		if (ldt_info->seg_32bit)
-			last = base-1;
-	}
-	return (last >= first && last < TASK_SIZE);
-}
-
 static int write_ldt(void * ptr, unsigned long bytecount, int oldmode)
 {
 	struct modify_ldt_ldt_s ldt_info;
@@ -71,9 +46,6 @@
 		return -EFAULT; 	
 
 	if ((ldt_info.contents == 3 && (oldmode || ldt_info.seg_not_present == 0)) || ldt_info.entry_number >= LDT_ENTRIES)
-		return -EINVAL;
-
-	if (!limits_ok(&ldt_info) && (oldmode || ldt_info.seg_not_present == 0))
 		return -EINVAL;
 
 	if (!current->ldt) {
diff -u --recursive --new-file v2.1.88/linux/arch/i386/kernel/setup.c linux/arch/i386/kernel/setup.c
--- v2.1.88/linux/arch/i386/kernel/setup.c	Wed Feb  4 11:36:00 1998
+++ linux/arch/i386/kernel/setup.c	Wed Feb 25 15:30:22 1998
@@ -331,17 +331,18 @@
 
 static struct cpu_model_info cpu_models[] __initdata = {
 	{ X86_VENDOR_INTEL,	4,
-		{ "486 DX-25/33", "486 DX-50", "486 SX", "486 DX/2", "486 SL", "486 SX/2",
-		  NULL, "486 DX/2-WB", "486 DX/4", "486 DX/4-WB", NULL, NULL, NULL, NULL,
-		  NULL, NULL }},
+	  { "486 DX-25/33", "486 DX-50", "486 SX", "486 DX/2", "486 SL", 
+	    "486 SX/2", NULL, "486 DX/2-WB", "486 DX/4", "486 DX/4-WB", NULL, 
+	    NULL, NULL, NULL, NULL, NULL }},
 	{ X86_VENDOR_INTEL,	5,
 	  { "Pentium 60/66 A-step", "Pentium 60/66", "Pentium 75+",
 	    "OverDrive PODP5V83", "Pentium MMX", NULL, NULL,
 	    "Mobile Pentium 75+", "Mobile Pentium MMX", NULL, NULL, NULL,
 	    NULL, NULL, NULL, NULL }},
 	{ X86_VENDOR_INTEL,	6,
-	  { "Pentium Pro A-step", "Pentium Pro", NULL, "Pentium II", NULL,
-	    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }},
+	  { "Pentium Pro A-step", "Pentium Pro", NULL, "Pentium II (Klamath)", 
+	    NULL, "Pentium II (Deschutes)", NULL, NULL, NULL, NULL, NULL, NULL,
+	    NULL, NULL, NULL, NULL }},
 	{ X86_VENDOR_CYRIX,	4,
 	  { NULL, NULL, NULL, NULL, "MediaGX", NULL, NULL, NULL, NULL, "5x86",
 	    NULL, NULL, NULL, NULL, NULL, NULL }},
@@ -349,8 +350,8 @@
 	  { NULL, NULL, "6x86", NULL, "GXm", NULL, NULL, NULL, NULL,
 	    NULL, NULL, NULL, NULL, NULL, NULL, NULL }},
 	{ X86_VENDOR_CYRIX,	6,
-	  { "6x86MX", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-	    NULL, NULL, NULL, NULL, NULL }},
+	  { "6x86MX", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 
+	    NULL, NULL, NULL, NULL, NULL, NULL }},
 	{ X86_VENDOR_AMD,	4,
 	  { NULL, NULL, NULL, "DX/2", NULL, NULL, NULL, "DX/2-WB", "DX/4",
 	    "DX/4-WB", NULL, NULL, NULL, NULL, "Am5x86-WT", "Am5x86-WB" }},
@@ -383,8 +384,7 @@
 	    c->cpuid_level < 0)
 		return;
 
-	if ((c->x86_vendor == X86_VENDOR_AMD && amd_model(c)) ||
-	    (c->x86_vendor == X86_VENDOR_CYRIX && cyrix_model(c)))
+	if (c->x86_vendor == X86_VENDOR_CYRIX && cyrix_model(c))
 		return;
 
 	if (c->x86_model < 16)
@@ -394,10 +394,15 @@
 				p = cpu_models[i].model_names[c->x86_model];
 				break;
 			}
-	if (p)
+	if (p) {
 		strcpy(c->x86_model_id, p);
-	else
-		sprintf(c->x86_model_id, "%02x/%02x", c->x86_vendor, c->x86_model);
+		return;
+	}
+
+	if (c->x86_vendor == X86_VENDOR_AMD && amd_model(c))
+		return;
+
+	sprintf(c->x86_model_id, "%02x/%02x", c->x86_vendor, c->x86_model);
 }
 
 static char *cpu_vendor_names[] __initdata = {
@@ -438,7 +443,7 @@
                 "fpu", "vme", "de", "pse", "tsc", "msr", "pae", "mce",
                 "cx8", "apic", "10", "sep", "mtrr", "pge", "mca", "cmov",
                 "fcmov", "17", "18", "19", "20", "21", "22", "mmx",
-                "cxmmx", "25", "26", "27", "28", "29", "30", "amd3d"
+                "osfxsr", "25", "26", "27", "28", "29", "30", "amd3d"
         };
 	struct cpuinfo_x86 *c = cpu_data;
 	int i, n;
diff -u --recursive --new-file v2.1.88/linux/arch/i386/kernel/sys_i386.c linux/arch/i386/kernel/sys_i386.c
--- v2.1.88/linux/arch/i386/kernel/sys_i386.c	Mon Dec  8 14:58:44 1997
+++ linux/arch/i386/kernel/sys_i386.c	Fri Feb 27 15:34:40 1998
@@ -65,14 +65,17 @@
 
 	lock_kernel();
 	if (copy_from_user(&a, arg, sizeof(a)))
-			goto out;
+		goto out;
 	if (!(a.flags & MAP_ANONYMOUS)) {
 		error = -EBADF;
-		if (a.fd >= NR_OPEN || !(file = current->files->fd[a.fd]))
+		file = fget(a.fd);
+		if (!file)
 			goto out;
 	}
 	a.flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
 	error = do_mmap(file, a.addr, a.len, a.prot, a.flags, a.offset);
+	if (file)
+		fput(file);
 out:
 	unlock_kernel();
 	return error;
diff -u --recursive --new-file v2.1.88/linux/arch/m68k/atari/joystick.c linux/arch/m68k/atari/joystick.c
--- v2.1.88/linux/arch/m68k/atari/joystick.c	Tue Feb 17 13:12:44 1998
+++ linux/arch/m68k/atari/joystick.c	Fri Feb 27 09:10:33 1998
@@ -112,7 +112,7 @@
 {
     int minor = DEVICE_NR(file->f_dentry->d_inode->i_rdev);
 
-    poll_wait(&joystick[minor].wait, wait);
+    poll_wait(file, &joystick[minor].wait, wait);
     if (joystick[minor].ready)
 	return POLLIN | POLLRDNORM;
     return 0;
diff -u --recursive --new-file v2.1.88/linux/arch/m68k/defconfig linux/arch/m68k/defconfig
--- v2.1.88/linux/arch/m68k/defconfig	Tue Feb 17 13:12:44 1998
+++ linux/arch/m68k/defconfig	Tue Feb 24 22:08:01 1998
@@ -15,7 +15,7 @@
 # CONFIG_KERNELD is not set
 
 #
-# Platform dependant setup
+# Platform-dependent setup
 #
 CONFIG_AMIGA=y
 # CONFIG_ATARI is not set
diff -u --recursive --new-file v2.1.88/linux/arch/mips/Makefile linux/arch/mips/Makefile
--- v2.1.88/linux/arch/mips/Makefile	Wed Dec 10 10:31:09 1997
+++ linux/arch/mips/Makefile	Tue Feb 24 22:08:01 1998
@@ -65,7 +65,7 @@
 endif
 
 #
-# CPU dependand compiler/assembler options for optimization.
+# CPU-dependent compiler/assembler options for optimization.
 #
 ifdef CONFIG_CPU_R3000
 CFLAGS		:= $(CFLAGS) -mcpu=r3000 -mips1
@@ -93,7 +93,7 @@
 endif
 
 #
-# Board dependand options and extra files
+# Board-dependent options and extra files
 #
 ifdef CONFIG_ALGOR_P4032
 CORE_FILES    += arch/mips/algor/algor.o
diff -u --recursive --new-file v2.1.88/linux/arch/mips/dec/setup.c linux/arch/mips/dec/setup.c
--- v2.1.88/linux/arch/mips/dec/setup.c	Thu Jun 26 12:33:37 1997
+++ linux/arch/mips/dec/setup.c	Tue Feb 24 22:08:01 1998
@@ -1,5 +1,5 @@
 /*
- * Setup pointers to hardware dependand routines.
+ * Setup pointers to hardware-dependent routines.
  *
  * This file is subject to the terms and conditions of the GNU General Public
  * License.  See the file "COPYING" in the main directory of this archive
diff -u --recursive --new-file v2.1.88/linux/arch/mips/deskstation/setup.c linux/arch/mips/deskstation/setup.c
--- v2.1.88/linux/arch/mips/deskstation/setup.c	Wed Dec 10 10:31:09 1997
+++ linux/arch/mips/deskstation/setup.c	Tue Feb 24 22:08:01 1998
@@ -1,5 +1,5 @@
 /*
- * Setup pointers to hardware dependand routines.
+ * Setup pointers to hardware-dependent routines.
  *
  * This file is subject to the terms and conditions of the GNU General Public
  * License.  See the file "COPYING" in the main directory of this archive
diff -u --recursive --new-file v2.1.88/linux/arch/ppc/kernel/residual.c linux/arch/ppc/kernel/residual.c
--- v2.1.88/linux/arch/ppc/kernel/residual.c	Mon Feb 23 18:12:02 1998
+++ linux/arch/ppc/kernel/residual.c	Tue Feb 24 22:08:01 1998
@@ -300,10 +300,10 @@
 #undef p
 	  break;
 	case StartDepFunc:
-	  printk("Start dependant function:\n");
+	  printk("Start dependent function:\n");
 	  break;
 	case EndDepFunc:
-	  printk("End dependant function\n");
+	  printk("End dependent function\n");
 	  break;
 	case IOPort:
 #define p pkt->S8_Pack
diff -u --recursive --new-file v2.1.88/linux/arch/sparc/mm/srmmu.c linux/arch/sparc/mm/srmmu.c
--- v2.1.88/linux/arch/sparc/mm/srmmu.c	Fri Jan 16 20:38:45 1998
+++ linux/arch/sparc/mm/srmmu.c	Fri Feb 27 11:01:52 1998
@@ -2101,8 +2101,8 @@
 {
 	if((vma->vm_flags & (VM_WRITE|VM_SHARED)) == (VM_WRITE|VM_SHARED)) {
 		struct vm_area_struct *vmaring;
-		struct dentry *dentry;
-		struct inode *inode = NULL;
+		struct file *file;
+		struct inode *inode;
 		unsigned long flags, offset, vaddr, start;
 		int alias_found = 0;
 		pgd_t *pgdp;
@@ -2111,11 +2111,10 @@
 
 		save_and_cli(flags);
 
-		dentry = vma->vm_dentry;
-		if(dentry)
-			inode = dentry->d_inode;
-		if (!inode)
+		file = vma->vm_file;
+		if (!file)
 			goto done;
+		inode = file->f_dentry->d_inode;
 		offset = (address & PAGE_MASK) - vma->vm_start;
 		vmaring = inode->i_mmap; 
 		do {
diff -u --recursive --new-file v2.1.88/linux/arch/sparc64/config.in linux/arch/sparc64/config.in
--- v2.1.88/linux/arch/sparc64/config.in	Thu Feb 12 20:56:04 1998
+++ linux/arch/sparc64/config.in	Thu Feb 26 19:35:33 1998
@@ -158,7 +158,7 @@
 	  dep_tristate 'Adaptec AIC7xxx support' CONFIG_SCSI_AIC7XXX $CONFIG_SCSI
 	  if [ "$CONFIG_SCSI_AIC7XXX" != "n" ]; then
 	    bool '   Enable tagged command queueing' CONFIG_AIC7XXX_TAGGED_QUEUEING Y
-	    dep_tristate '   Override driver defaults for commands per LUN' CONFIG_OVERRIDE_CMDS N
+	    bool '   Override driver defaults for commands per LUN' CONFIG_OVERRIDE_CMDS N
 	    if [ "$CONFIG_OVERRIDE_CMDS" != "n" ]; then
 	      int  '   Maximum number of commands per LUN' CONFIG_AIC7XXX_CMDS_PER_LUN 8
 	    fi
diff -u --recursive --new-file v2.1.88/linux/drivers/acorn/block/fd1772.c linux/drivers/acorn/block/fd1772.c
--- v2.1.88/linux/drivers/acorn/block/fd1772.c	Mon Feb 23 18:12:03 1998
+++ linux/drivers/acorn/block/fd1772.c	Sat Feb 21 13:25:15 1998
@@ -116,7 +116,6 @@
  *     16/11/96 - Fiddled and frigged for 2.0.18
  */
 
-#include <linux/config.h>
 #include <linux/sched.h>
 #include <linux/fs.h>
 #include <linux/fcntl.h>
diff -u --recursive --new-file v2.1.88/linux/drivers/acorn/scsi/cumana_1.c linux/drivers/acorn/scsi/cumana_1.c
--- v2.1.88/linux/drivers/acorn/scsi/cumana_1.c	Mon Feb 23 18:12:03 1998
+++ linux/drivers/acorn/scsi/cumana_1.c	Sat Feb 21 13:25:15 1998
@@ -36,7 +36,6 @@
  * $Log: cumana_NCR5380.c,v $
  */
 
-#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/signal.h>
 #include <linux/sched.h>
diff -u --recursive --new-file v2.1.88/linux/drivers/acorn/scsi/ecoscsi.c linux/drivers/acorn/scsi/ecoscsi.c
--- v2.1.88/linux/drivers/acorn/scsi/ecoscsi.c	Mon Feb 23 18:12:03 1998
+++ linux/drivers/acorn/scsi/ecoscsi.c	Sat Feb 21 13:25:15 1998
@@ -35,7 +35,6 @@
  * $Log: ecoscsi_NCR5380.c,v $
  */
 
-#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/signal.h>
 #include <linux/sched.h>
diff -u --recursive --new-file v2.1.88/linux/drivers/acorn/scsi/oak.c linux/drivers/acorn/scsi/oak.c
--- v2.1.88/linux/drivers/acorn/scsi/oak.c	Mon Feb 23 18:12:03 1998
+++ linux/drivers/acorn/scsi/oak.c	Sat Feb 21 13:25:15 1998
@@ -35,7 +35,6 @@
  * $Log: oak.c,v $
  */
 
-#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/signal.h>
 #include <linux/sched.h>
diff -u --recursive --new-file v2.1.88/linux/drivers/block/Makefile linux/drivers/block/Makefile
--- v2.1.88/linux/drivers/block/Makefile	Wed Jan 14 13:48:38 1998
+++ linux/drivers/block/Makefile	Fri Mar  6 10:48:00 1998
@@ -20,10 +20,10 @@
 
 
 L_TARGET := block.a
-L_OBJS   := ll_rw_blk.o genhd.o
+L_OBJS   := genhd.o
 M_OBJS   :=
 MOD_LIST_NAME := BLOCK_MODULES
-LX_OBJS :=
+LX_OBJS := ll_rw_blk.o
 MX_OBJS :=
 
 ifeq ($(CONFIG_MAC_FLOPPY),y)
diff -u --recursive --new-file v2.1.88/linux/drivers/block/floppy.c linux/drivers/block/floppy.c
--- v2.1.88/linux/drivers/block/floppy.c	Tue Feb 17 13:12:46 1998
+++ linux/drivers/block/floppy.c	Sun Feb 22 10:48:45 1998
@@ -148,7 +148,7 @@
 #include <asm/io.h>
 #include <asm/uaccess.h>
 
-static int can_use_virtual_dma=0;
+static int can_use_virtual_dma=2;
 /* =======
  * can use virtual DMA:
  * 0 = use of virtual DMA disallowed by config
@@ -2083,7 +2083,7 @@
 
 #define CODE2SIZE (ssize = ((1 << SIZECODE) + 3) >> 2)
 #define FM_MODE(x,y) ((y) & ~(((x)->rate & 0x80) >>1))
-#define CT(x) ((x) | 0x40)
+#define CT(x) ((x) | 0xc0)
 static void setup_format_params(int track)
 {
 	struct fparm {
@@ -2465,6 +2465,32 @@
 }
 #endif
 
+/* work around a bug in pseudo DMA
+ * (on some FDCs) pseudo DMA does not stop when the CPU stops
+ * sending data.  Hence we need a different way to signal the
+ * transfer length:  We use SECT_PER_TRACK.  Unfortunately, this
+ * does not work with MT, hence we can only transfer one head at
+ * a time
+ */
+static int virtualdmabug_workaround() {
+	int hard_sectors, end_sector;
+	if(CT(COMMAND) == FD_WRITE) {
+		COMMAND &= ~0x80; /* switch off multiple track mode */
+	   
+		hard_sectors = raw_cmd->length >> (7 + SIZECODE);
+		end_sector = SECTOR + hard_sectors - 1;
+#ifdef FLOPPY_SANITY_CHECK
+		if(end_sector > SECT_PER_TRACK) {
+			printk("too many sectors %d > %d\n", 
+				   end_sector, SECT_PER_TRACK);
+			return 0;
+		}
+#endif
+		SECT_PER_TRACK = end_sector; /* make sure SECT_PER_TRACK points
+									  * to end of transfer */
+	}
+}
+
 /*
  * Formulate a read/write request.
  * this routine decides where to load the data (directly to buffer, or to
@@ -2541,11 +2567,17 @@
 	CODE2SIZE;
 	SECT_PER_TRACK = _floppy->sect << 2 >> SIZECODE;
 	SECTOR = ((sector_t % _floppy->sect) << 2 >> SIZECODE) + 1;
+
+	/* tracksize describes the size which can be filled up with sectors
+	 * of size ssize.
+	 */
 	tracksize = _floppy->sect - _floppy->sect % ssize;
 	if (tracksize < _floppy->sect){
 		SECT_PER_TRACK ++;
 		if (tracksize <= sector_t % _floppy->sect)
 			SECTOR--;
+
+		/* if we are beyond tracksize, fill up using smaller sectors */
 		while (tracksize <= sector_t % _floppy->sect){
 			while(tracksize + ssize > _floppy->sect){
 				SIZECODE--;
@@ -2555,8 +2587,12 @@
 			tracksize += ssize;
 		}
 		max_sector = HEAD * _floppy->sect + tracksize;
-	} else if (!TRACK && !HEAD && !(_floppy->rate & FD_2M) && probing)
+	} else if (!TRACK && !HEAD && !(_floppy->rate & FD_2M) && probing) {
+		max_sector = _floppy->sect;
+	} else if (!HEAD && CT(COMMAND) == FD_WRITE) {
+		/* for virtual DMA bug workaround */
 		max_sector = _floppy->sect;
+	}
 
 	aligned_sector_t = sector_t - (sector_t % _floppy->sect) % ssize;
 	max_size = CURRENT->nr_sectors;
@@ -2625,6 +2661,8 @@
 /*			check_dma_crossing(raw_cmd->kernel_data, 
 					   raw_cmd->length, 
 					   "end of make_raw_request [1]");*/
+
+			virtualdmabug_workaround();
 			return 2;
 		}
 	}
@@ -2730,6 +2768,8 @@
 		return 0;
 	}
 #endif
+
+	virtualdmabug_workaround();
 	return 2;
 }
 
diff -u --recursive --new-file v2.1.88/linux/drivers/block/ide-proc.c linux/drivers/block/ide-proc.c
--- v2.1.88/linux/drivers/block/ide-proc.c	Fri Jan 30 11:28:06 1998
+++ linux/drivers/block/ide-proc.c	Sun Mar  1 10:20:27 1998
@@ -613,8 +613,12 @@
 	if (!drive->proc || !p)
 		return;
 	while (p->name != NULL) {
-		ent = create_proc_entry(p->name, 0, drive->proc);
+		mode_t mode = S_IFREG|S_IRUSR;
+		if (!strcmp(p->name,"settings"))
+			mode |= S_IWUSR;
+		ent = create_proc_entry(p->name, mode, drive->proc);
 		if (!ent) return;
+		ent->nlink = 1;
 		ent->data = drive;
 		ent->read_proc = p->read_proc;
 		ent->write_proc = p->write_proc;
@@ -674,8 +678,9 @@
 		if (!hwif_ent) return;
 #ifdef CONFIG_PCI
 		if (!IDE_PCI_DEVID_EQ(hwif->pci_devid, IDE_PCI_DEVID_NULL)) {
-			ent = create_proc_entry("config", 0, hwif_ent);
+			ent = create_proc_entry("config", S_IFREG|S_IRUSR|S_IWUSR, hwif_ent);
 			if (!ent) return;
+			ent->nlink = 1;
 			ent->data = hwif;
 			ent->read_proc  = proc_ide_read_config;
 			ent->write_proc = proc_ide_write_config;;
diff -u --recursive --new-file v2.1.88/linux/drivers/block/ide.c linux/drivers/block/ide.c
--- v2.1.88/linux/drivers/block/ide.c	Mon Feb 23 18:12:04 1998
+++ linux/drivers/block/ide.c	Sun Mar  1 10:16:33 1998
@@ -2826,6 +2826,7 @@
 EXPORT_SYMBOL(ide_end_request);
 EXPORT_SYMBOL(ide_revalidate_disk);
 EXPORT_SYMBOL(ide_cmd);
+EXPORT_SYMBOL(ide_wait_cmd);
 EXPORT_SYMBOL(ide_stall_queue);
 EXPORT_SYMBOL(ide_add_proc_entries);
 EXPORT_SYMBOL(ide_remove_proc_entries);
diff -u --recursive --new-file v2.1.88/linux/drivers/block/ll_rw_blk.c linux/drivers/block/ll_rw_blk.c
--- v2.1.88/linux/drivers/block/ll_rw_blk.c	Mon Feb 23 18:12:04 1998
+++ linux/drivers/block/ll_rw_blk.c	Fri Mar  6 10:48:55 1998
@@ -22,6 +22,8 @@
 #include <asm/io.h>
 #include <linux/blk.h>
 
+#include <linux/module.h>
+
 #define ATOMIC_ON()	do { } while (0)
 #define ATOMIC_OFF()	do { } while (0)
 
@@ -797,4 +799,6 @@
 	ddv_init();
 #endif
 	return 0;
-}
+};
+
+EXPORT_SYMBOL(io_request_lock);
diff -u --recursive --new-file v2.1.88/linux/drivers/char/Config.in linux/drivers/char/Config.in
--- v2.1.88/linux/drivers/char/Config.in	Thu Feb 12 20:56:05 1998
+++ linux/drivers/char/Config.in	Tue Feb 24 22:37:02 1998
@@ -89,6 +89,7 @@
   bool '   Make CPU Idle calls when idle' CONFIG_APM_CPU_IDLE
   bool '   Enable console blanking using APM' CONFIG_APM_DISPLAY_BLANK
   bool '   Power off on shutdown' CONFIG_APM_POWER_OFF
+  bool '   Ignore multiple suspend' CONFIG_APM_IGNORE_MULTIPLE_SUSPEND
 fi
 bool 'Watchdog Timer Support'	CONFIG_WATCHDOG
 if [ "$CONFIG_WATCHDOG" != "n" ]; then
diff -u --recursive --new-file v2.1.88/linux/drivers/char/amigamouse.c linux/drivers/char/amigamouse.c
--- v2.1.88/linux/drivers/char/amigamouse.c	Mon Feb 23 18:12:04 1998
+++ linux/drivers/char/amigamouse.c	Thu Feb 26 19:58:31 1998
@@ -284,7 +284,7 @@
 
 static unsigned int mouse_poll(struct file *file, poll_table * wait)
 {
-	poll_wait(&mouse.wait, wait);
+	poll_wait(file, &mouse.wait, wait);
 	if (mouse.ready)
 		return POLLIN | POLLRDNORM;
 	return 0;
diff -u --recursive --new-file v2.1.88/linux/drivers/char/apm_bios.c linux/drivers/char/apm_bios.c
--- v2.1.88/linux/drivers/char/apm_bios.c	Mon Feb 23 18:12:04 1998
+++ linux/drivers/char/apm_bios.c	Thu Feb 26 19:58:40 1998
@@ -27,6 +27,7 @@
  *    Version 1.0 and 1.1
  * May 1996, Version 1.2
  * Feb 1998, Version 1.3
+ * Feb 1998, Version 1.4
  *
  * History:
  *    0.6b: first version in official kernel, Linux 1.3.46
@@ -46,8 +47,11 @@
  *         The new replacment for it is, but Linux doesn't yet support this.
  *         Alan Cox Linux 2.1.55
  *    1.3: Set up a valid data descriptor 0x40 for buggy BIOS's
+ *    1.4: Upgraded to support APM 1.2. Integrated ThinkPad suspend patch by 
+ *         Dean Gaudet <dgaudet@arctic.org>.
+ *         C. Scott Ananian <cananian@alumni.princeton.edu> Linux 2.1.87
  *
- * Reference:
+ * APM 1.1 Reference:
  *
  *   Intel Corporation, Microsoft Corporation. Advanced Power Management
  *   (APM) BIOS Interface Specification, Revision 1.1, September 1993.
@@ -58,6 +62,15 @@
  * ftp://ftp.intel.com/pub/IAL/software_specs/apmv11.doc.  It is also
  * available from Microsoft by calling 206.882.8080.]
  *
+ * APM 1.2 Reference:
+ *   Intel Corporation, Microsoft Corporation. Advanced Power Management
+ *   (APM) BIOS Interface Specification, Revision 1.2, February 1996.
+ *
+ * [This document is available from Intel at:
+ *    http://www.intel.com/IAL/powermgm
+ *  or Microsoft at
+ *    http://www.microsoft.com/windows/thirdparty/hardware/pcfuture.htm
+ * ]
  */
 
 #include <linux/config.h>
@@ -128,6 +141,11 @@
  * problems have been reported when using this option with gpm (if you'd
  * like to debug this, please do so).
  *
+ * CONFIG_APM_IGNORE_MULTIPLE_SUSPEND: The IBM TP560 bios seems to insist
+ * on returning multiple suspend/standby events whenever one occurs.  We
+ * really only need one at a time, so just ignore any beyond the first.
+ * This is probably safe on most laptops.
+ *
  * If you are debugging the APM support for your laptop, note that code for
  * all of these options is contained in this file, so you can #define or
  * #undef these on the next line to avoid recompiling the whole kernel.
@@ -341,6 +359,9 @@
 #endif
 static int			suspends_pending = 0;
 static int			standbys_pending = 0;
+#ifdef CONFIG_APM_IGNORE_MULTIPLE_SUSPEND
+static int			waiting_for_resume = 0;
+#endif
 
 static long			clock_cmos_diff;
 static int			got_clock_diff = 0;
@@ -350,7 +371,7 @@
 
 static struct timer_list	apm_timer;
 
-static char			driver_version[] = "1.3";	/* no spaces */
+static char			driver_version[] = "1.4";	/* no spaces */
 
 #ifdef APM_DEBUG
 static char *	apm_event_name[] = {
@@ -614,8 +635,15 @@
 		if (as == sender)
 			continue;
 		as->event_head = (as->event_head + 1) % APM_MAX_EVENTS;
-		if (as->event_head == as->event_tail)
+		if (as->event_head == as->event_tail) {
+			static int notified;
+
+			if (notified == 0) {
+			    printk( "apm_bios: an event queue overflowed\n" );
+			    notified = 1;
+			}
 			as->event_tail = (as->event_tail + 1) % APM_MAX_EVENTS;
+		}
 		as->events[as->event_head] = event;
 		if (!as->suser)
 			continue;
@@ -722,9 +750,23 @@
 	apm_event_t	event;
 
 	while ((event = get_event()) != 0) {
+#ifdef APM_DEBUG
+		if (event <= NR_APM_EVENT_NAME)
+			printk(KERN_DEBUG "APM BIOS received %s notify\n",
+			       apm_event_name[event - 1]);
+		else
+			printk(KERN_DEBUG "APM BIOS received unknown "
+			       "event 0x%02x\n", event);
+#endif
 		switch (event) {
 		case APM_SYS_STANDBY:
 		case APM_USER_STANDBY:
+#ifdef CONFIG_APM_IGNORE_MULTIPLE_SUSPEND
+			if (waiting_for_resume) {
+			    return;
+			}
+			waiting_for_resume = 1;
+#endif
 			send_event(event, APM_STANDBY_RESUME, NULL);
 			if (standbys_pending <= 0)
 				standby();
@@ -737,6 +779,12 @@
 			break;
 #endif
 		case APM_SYS_SUSPEND:
+#ifdef CONFIG_APM_IGNORE_MULTIPLE_SUSPEND
+			if (waiting_for_resume) {
+			    return;
+			}
+			waiting_for_resume = 1;
+#endif
 			send_event(event, APM_NORMAL_RESUME, NULL);
 			if (suspends_pending <= 0)
 				suspend();
@@ -745,6 +793,9 @@
 		case APM_NORMAL_RESUME:
 		case APM_CRITICAL_RESUME:
 		case APM_STANDBY_RESUME:
+#ifdef CONFIG_APM_IGNORE_MULTIPLE_SUSPEND
+			waiting_for_resume = 0;
+#endif
 			set_time();
 			send_event(event, 0, NULL);
 			break;
@@ -763,14 +814,6 @@
 			suspend();
 			break;
 		}
-#ifdef APM_DEBUG
-		if (event <= NR_APM_EVENT_NAME)
-			printk(KERN_DEBUG "APM BIOS received %s notify\n",
-			       apm_event_name[event - 1]);
-		else
-			printk(KERN_DEBUG "APM BIOS received unknown event 0x%02x\n",
-			       event);
-#endif
 	}
 }
 
@@ -905,7 +948,7 @@
 	as = fp->private_data;
 	if (check_apm_bios_struct(as, "select"))
 		return 0;
-	poll_wait(&process_list, wait);
+	poll_wait(fp, &process_list, wait);
 	if (!queue_empty(as))
 		return POLLIN | POLLRDNORM;
 	return 0;
diff -u --recursive --new-file v2.1.88/linux/drivers/char/atarimouse.c linux/drivers/char/atarimouse.c
--- v2.1.88/linux/drivers/char/atarimouse.c	Mon Feb 23 18:12:04 1998
+++ linux/drivers/char/atarimouse.c	Thu Feb 26 19:58:52 1998
@@ -134,7 +134,7 @@
 
 static unsigned int mouse_poll(struct file *file, poll_table *wait)
 {
-	poll_wait(&mouse.wait, wait);
+	poll_wait(file, &mouse.wait, wait);
 	if (mouse.ready)
 		return POLLIN | POLLRDNORM;
 	return 0;
diff -u --recursive --new-file v2.1.88/linux/drivers/char/atixlmouse.c linux/drivers/char/atixlmouse.c
--- v2.1.88/linux/drivers/char/atixlmouse.c	Wed Nov 12 20:28:26 1997
+++ linux/drivers/char/atixlmouse.c	Thu Feb 26 19:59:02 1998
@@ -176,7 +176,7 @@
 
 static unsigned int mouse_poll(struct file *file, poll_table * wait)
 {
-	poll_wait(&mouse.wait, wait);
+	poll_wait(file, &mouse.wait, wait);
 	if (mouse.ready)
 		return POLLIN | POLLRDNORM;
 	return 0;
diff -u --recursive --new-file v2.1.88/linux/drivers/char/busmouse.c linux/drivers/char/busmouse.c
--- v2.1.88/linux/drivers/char/busmouse.c	Fri Jan 23 18:10:31 1998
+++ linux/drivers/char/busmouse.c	Thu Feb 26 19:59:20 1998
@@ -223,7 +223,7 @@
  */
 static unsigned int mouse_poll(struct file *file, poll_table * wait)
 {
-	poll_wait(&mouse.wait, wait);
+	poll_wait(file, &mouse.wait, wait);
 	if (mouse.ready)
 		return POLLIN | POLLRDNORM;
 	return 0;
diff -u --recursive --new-file v2.1.88/linux/drivers/char/cd1865.h linux/drivers/char/cd1865.h
--- v2.1.88/linux/drivers/char/cd1865.h	Tue Dec  2 09:19:03 1997
+++ linux/drivers/char/cd1865.h	Thu Mar  5 11:55:06 1998
@@ -3,7 +3,7 @@
  *                          for the Specialix IO8+ multiport serial driver.
  *
  *      Copyright (C) 1997 Roger Wolff (R.E.Wolff@BitWizard.nl)
- *      Copyright (C) 1994-1996  Dmitry Gorodchanin (begemot@bgm.rosprint.net)
+ *      Copyright (C) 1994-1996  Dmitry Gorodchanin (pgmdsg@ibi.com)
  *
  *      Specialix pays for the development and support of this driver.
  *      Please DO contact io8-linux@specialix.co.uk if you require
diff -u --recursive --new-file v2.1.88/linux/drivers/char/epca.c linux/drivers/char/epca.c
--- v2.1.88/linux/drivers/char/epca.c	Thu Jan 15 14:33:06 1998
+++ linux/drivers/char/epca.c	Tue Feb 24 22:08:01 1998
@@ -185,7 +185,7 @@
 
 /* ----------------------------------------------------------------------
 	Begin generic memory functions.  These functions will be alias
-	(point at) more specific functions dependant on the board being
+	(point at) more specific functions dependent on the board being
 	configured.
 ----------------------------------------------------------------------- */
 	
@@ -461,7 +461,7 @@
 	cards (Such as PCI) needs no windowing routines at all.  We provide
 	these do nothing routines so that the same code base can be used.
 	The driver will ALWAYS call a windowing routine if it thinks it needs
-	to; regardless of the card.  However, dependant on the card the routine
+	to; regardless of the card.  However, dependent on the card the routine
 	may or may not do anything.
 ---------------------------------------------------------------------------*/
 
@@ -4066,7 +4066,7 @@
                                       base_addr5);
 
 	/* ------------------------------------------------------------------------
-			 NOTE - The code below mask out either the 2 or 4 bits dependant on the
+			 NOTE - The code below mask out either the 2 or 4 bits dependent on the
 				space being addressed. (base_addr value reflecting io space, have their
 				first 2 bits mask out, while base_addr value reflecting mem space, have
 				their first 4 bits mask out.)  These bits are flag bits and should always
diff -u --recursive --new-file v2.1.88/linux/drivers/char/fbmem.c linux/drivers/char/fbmem.c
--- v2.1.88/linux/drivers/char/fbmem.c	Sat Sep 13 11:07:27 1997
+++ linux/drivers/char/fbmem.c	Fri Feb 27 10:56:38 1998
@@ -220,7 +220,8 @@
 	if (remap_page_range(vma->vm_start, vma->vm_offset,
 			     vma->vm_end - vma->vm_start, vma->vm_page_prot))
 		return -EAGAIN;
-	vma->vm_dentry = dget(file->f_dentry);
+	vma->vm_file = file;
+	file->f_count++;
 	return 0;
 }
 
diff -u --recursive --new-file v2.1.88/linux/drivers/char/ftape/zftape/zftape-init.c linux/drivers/char/ftape/zftape/zftape-init.c
--- v2.1.88/linux/drivers/char/ftape/zftape/zftape-init.c	Fri Jan 23 18:10:31 1998
+++ linux/drivers/char/ftape/zftape/zftape-init.c	Sun Mar  1 14:45:21 1998
@@ -279,12 +279,8 @@
 		static struct vm_operations_struct dummy = { NULL, };
 		vma->vm_ops = &dummy;
 #endif
-#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,45)
-		vma->vm_dentry = dget(filep->f_dentry);
-#else
-		vma_set_inode (vma, ino);
-		inode_inc_count (ino);
-#endif
+		vma->vm_file = filep;
+		filep->f_count++;
 	}
 	current->blocked = old_sigmask; /* restore mask */
 	TRACE_EXIT result;
diff -u --recursive --new-file v2.1.88/linux/drivers/char/hfmodem/modem.c linux/drivers/char/hfmodem/modem.c
--- v2.1.88/linux/drivers/char/hfmodem/modem.c	Tue Aug  5 09:48:55 1997
+++ linux/drivers/char/hfmodem/modem.c	Fri Feb 27 09:11:09 1998
@@ -561,6 +561,7 @@
 	unsigned long flags;
 	int i, cnt1, cnt2;
 	
+	poll_wait(file, &dev->wait, wait);
 	save_flags(flags);
 	cli();
 	for (i = cnt1 = cnt2 = 0; i < HFMODEM_NUMTXSLOTS; i++) {
@@ -576,7 +577,6 @@
 			cnt2++;
 	}
 	restore_flags(flags);
-	poll_wait(&dev->wait, wait);
         if (cnt1 || !cnt2)
                 return POLLIN | POLLRDNORM;
         return 0;
diff -u --recursive --new-file v2.1.88/linux/drivers/char/joystick.c linux/drivers/char/joystick.c
--- v2.1.88/linux/drivers/char/joystick.c	Mon Feb 23 18:12:04 1998
+++ linux/drivers/char/joystick.c	Thu Feb 26 19:59:33 1998
@@ -594,7 +594,7 @@
 	unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev);
  	curl = file->private_data;
 
-	poll_wait(&jsd[minor].wait, wait);
+	poll_wait(file, &jsd[minor].wait, wait);
 	if (GOF(curl->tail) != jsd[minor].ahead) 
 		return POLLIN | POLLRDNORM;
         return 0;
diff -u --recursive --new-file v2.1.88/linux/drivers/char/lp.c linux/drivers/char/lp.c
--- v2.1.88/linux/drivers/char/lp.c	Thu Feb 12 20:56:06 1998
+++ linux/drivers/char/lp.c	Thu Feb 26 12:42:38 1998
@@ -128,7 +128,7 @@
 
 static __inline__ void lp_yield (int minor)
 {
-	if (parport_yield (lp_table[minor].dev, 1) == 1 && need_resched)
+	if (!parport_yield_blocking (lp_table[minor].dev) && need_resched)
 		schedule ();
 }
 
@@ -475,6 +475,7 @@
 		return -ENXIO;
 	if (LP_F(minor) & LP_BUSY)
 		return -EBUSY;
+	LP_F(minor) |= LP_BUSY;
 
 	MOD_INC_USE_COUNT;
 
@@ -491,23 +492,26 @@
 		if (status & LP_POUTPA) {
 			printk(KERN_INFO "lp%d out of paper\n", minor);
 			MOD_DEC_USE_COUNT;
+			LP_F(minor) &= ~LP_BUSY;
 			return -ENOSPC;
 		} else if (!(status & LP_PSELECD)) {
 			printk(KERN_INFO "lp%d off-line\n", minor);
 			MOD_DEC_USE_COUNT;
+			LP_F(minor) &= ~LP_BUSY;
 			return -EIO;
 		} else if (!(status & LP_PERRORP)) {
 			printk(KERN_ERR "lp%d printer error\n", minor);
 			MOD_DEC_USE_COUNT;
+			LP_F(minor) &= ~LP_BUSY;
 			return -EIO;
 		}
 	}
 	lp_table[minor].lp_buffer = (char *) kmalloc(LP_BUFFER_SIZE, GFP_KERNEL);
 	if (!lp_table[minor].lp_buffer) {
 		MOD_DEC_USE_COUNT;
+		LP_F(minor) &= ~LP_BUSY;
 		return -ENOMEM;
 	}
-	LP_F(minor) |= LP_BUSY;
 	return 0;
 }
 
@@ -654,7 +658,14 @@
 
 __initfunc(void lp_setup(char *str, int *ints))
 {
-	if (!strncmp(str, "parport", 7)) {
+	if (!str) {
+		if (ints[0] == 0 || ints[1] == 0) {
+			/* disable driver on "lp=" or "lp=0" */
+			parport[0] = LP_PARPORT_OFF;
+		} else {
+			printk(KERN_WARNING "warning: 'lp=0x%x' is deprecated, ignored\n", ints[1]);
+		}
+	} else if (!strncmp(str, "parport", 7)) {
 		int n = simple_strtoul(str+7, NULL, 10);
 		if (parport_ptr < LP_NO)
 			parport[parport_ptr++] = n;
@@ -667,13 +678,6 @@
 		parport[parport_ptr++] = LP_PARPORT_NONE;
 	} else if (!strcmp(str, "reset")) {
 		reset = 1;
-	} else {
-		if (ints[0] == 0 || ints[1] == 0) {
-			/* disable driver on "lp=" or "lp=0" */
-			parport[0] = LP_PARPORT_OFF;
-		} else {
-			printk(KERN_WARNING "warning: 'lp=0x%x' is deprecated, ignored\n", ints[1]);
-		}
 	}
 }
 
diff -u --recursive --new-file v2.1.88/linux/drivers/char/mem.c linux/drivers/char/mem.c
--- v2.1.88/linux/drivers/char/mem.c	Fri Jan 23 18:10:31 1998
+++ linux/drivers/char/mem.c	Fri Feb 27 10:56:58 1998
@@ -142,7 +142,8 @@
 	if (remap_page_range(vma->vm_start, offset, vma->vm_end-vma->vm_start,
 			     vma->vm_page_prot))
 		return -EAGAIN;
-	vma->vm_dentry = dget(file->f_dentry);
+	vma->vm_file = file;
+	file->f_count++;
 	return 0;
 }
 
diff -u --recursive --new-file v2.1.88/linux/drivers/char/msbusmouse.c linux/drivers/char/msbusmouse.c
--- v2.1.88/linux/drivers/char/msbusmouse.c	Thu Nov 13 07:29:28 1997
+++ linux/drivers/char/msbusmouse.c	Thu Feb 26 19:59:43 1998
@@ -159,7 +159,7 @@
 
 static unsigned int mouse_poll(struct file *file, poll_table * wait)
 {
-	poll_wait(&mouse.wait, wait);
+	poll_wait(file, &mouse.wait, wait);
 	if (mouse.ready) 
 		return POLLIN | POLLRDNORM;
 	return 0;
diff -u --recursive --new-file v2.1.88/linux/drivers/char/n_tty.c linux/drivers/char/n_tty.c
--- v2.1.88/linux/drivers/char/n_tty.c	Sat Dec  6 11:26:26 1997
+++ linux/drivers/char/n_tty.c	Thu Feb 26 19:53:49 1998
@@ -1102,8 +1102,8 @@
 {
 	unsigned int mask = 0;
 
-	poll_wait(&tty->read_wait, wait);
-	poll_wait(&tty->write_wait, wait);
+	poll_wait(file, &tty->read_wait, wait);
+	poll_wait(file, &tty->write_wait, wait);
 	if (input_available_p(tty, TIME_CHAR(tty) ? 0 : MIN_CHAR(tty)))
 		mask |= POLLIN | POLLRDNORM;
 	if (tty->packet && tty->link->ctrl_status)
diff -u --recursive --new-file v2.1.88/linux/drivers/char/pc110pad.c linux/drivers/char/pc110pad.c
--- v2.1.88/linux/drivers/char/pc110pad.c	Mon Feb 23 18:12:04 1998
+++ linux/drivers/char/pc110pad.c	Thu Feb 26 19:59:56 1998
@@ -575,7 +575,7 @@
 
 static unsigned int pad_poll(struct file *file, poll_table * wait)
 {
-	poll_wait(&queue, wait);
+	poll_wait(file, &queue, wait);
     	if(button_pending || xy_pending)
 		return POLLIN | POLLRDNORM;
 	return 0;
diff -u --recursive --new-file v2.1.88/linux/drivers/char/psaux.c linux/drivers/char/psaux.c
--- v2.1.88/linux/drivers/char/psaux.c	Mon Jan 12 15:13:24 1998
+++ linux/drivers/char/psaux.c	Thu Feb 26 19:54:02 1998
@@ -567,7 +567,7 @@
 
 static unsigned int aux_poll(struct file *file, poll_table * wait)
 {
-	poll_wait(&queue->proc_list, wait);
+	poll_wait(file, &queue->proc_list, wait);
 	if (aux_ready)
 		return POLLIN | POLLRDNORM;
 	return 0;
diff -u --recursive --new-file v2.1.88/linux/drivers/char/random.c linux/drivers/char/random.c
--- v2.1.88/linux/drivers/char/random.c	Sun Dec 21 17:27:18 1997
+++ linux/drivers/char/random.c	Thu Feb 26 20:00:12 1998
@@ -1140,8 +1140,8 @@
 {
 	unsigned int mask;
 
-	poll_wait(&random_read_wait, wait);
-	poll_wait(&random_write_wait, wait);
+	poll_wait(file, &random_read_wait, wait);
+	poll_wait(file, &random_write_wait, wait);
 	mask = 0;
 	if (random_state.entropy_count >= WAIT_INPUT_BITS)
 		mask |= POLLIN | POLLRDNORM;
diff -u --recursive --new-file v2.1.88/linux/drivers/char/riscom8.c linux/drivers/char/riscom8.c
--- v2.1.88/linux/drivers/char/riscom8.c	Sat Sep 20 14:51:54 1997
+++ linux/drivers/char/riscom8.c	Thu Mar  5 11:55:06 1998
@@ -1,7 +1,7 @@
 /*
  *      linux/drivers/char/riscom.c  -- RISCom/8 multiport serial driver.
  *
- *      Copyright (C) 1994-1996  Dmitry Gorodchanin (begemot@bgm.rosprint.net)
+ *      Copyright (C) 1994-1996  Dmitry Gorodchanin (pgmdsg@ibi.com)
  *
  *      This code is loosely based on the Linux serial driver, written by
  *      Linus Torvalds, Theodore T'so and others. The RISCom/8 card 
diff -u --recursive --new-file v2.1.88/linux/drivers/char/riscom8.h linux/drivers/char/riscom8.h
--- v2.1.88/linux/drivers/char/riscom8.h	Mon Sep 30 00:47:08 1996
+++ linux/drivers/char/riscom8.h	Thu Mar  5 11:55:06 1998
@@ -1,7 +1,7 @@
 /*
  *      linux/drivers/char/riscom8.h  -- RISCom/8 multiport serial driver.
  *
- *      Copyright (C) 1994-1996  Dmitry Gorodchanin (begemot@bgm.rosprint.net)
+ *      Copyright (C) 1994-1996  Dmitry Gorodchanin (pgmdsg@ibi.com)
  *
  *      This code is loosely based on the Linux serial driver, written by
  *      Linus Torvalds, Theodore T'so and others. The RISCom/8 card 
diff -u --recursive --new-file v2.1.88/linux/drivers/char/rtc.c linux/drivers/char/rtc.c
--- v2.1.88/linux/drivers/char/rtc.c	Mon Dec  8 23:58:04 1997
+++ linux/drivers/char/rtc.c	Thu Feb 26 20:00:25 1998
@@ -28,9 +28,14 @@
  *	Based on other minimal char device drivers, like Alan's
  *	watchdog, Ted's random, etc. etc.
  *
+ *	1.07	Paul Gortmaker.
+ *	1.08	Miquel van Smoorenburg: disallow certain things on the
+ *		DEC Alpha as the CMOS clock is also used for other things.
+ *	1.09	Nikita Schmidt: epoch support and some Alpha cleanup.
+ *
  */
 
-#define RTC_VERSION		"1.07"
+#define RTC_VERSION		"1.09"
 
 #define RTC_IRQ 	8	/* Can't see this changing soon.	*/
 #define RTC_IO_EXTENT	0x10	/* Only really two ports, but...	*/
@@ -82,7 +87,7 @@
 			size_t count, loff_t *ppos);
 
 static int rtc_ioctl(struct inode *inode, struct file *file,
-			unsigned int cmd, unsigned long arg);
+		     unsigned int cmd, unsigned long arg);
 
 static unsigned int rtc_poll(struct file *file, poll_table *wait);
 
@@ -106,16 +111,27 @@
 unsigned long rtc_freq = 0;		/* Current periodic IRQ rate	*/
 unsigned long rtc_irq_data = 0;		/* our output to the world	*/
 
+/*
+ *	If this driver ever becomes modularised, it will be really nice
+ *	to make the epoch retain its value across module reload...
+ */
+
+static unsigned long epoch = 1900;	/* year corresponding to 0x00	*/
+
 unsigned char days_in_mo[] = 
-		{0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+{0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
 
 /*
  *	A very tiny interrupt handler. It runs with SA_INTERRUPT set,
  *	so that there is no possibility of conflicting with the
  *	set_rtc_mmss() call that happens during some timer interrupts.
  *	(See ./arch/XXXX/kernel/time.c for the set_rtc_mmss() function.)
+ *
+ *	On Alpha we won't get any interrupts anyway, as they all end up
+ *	in the system timer code.
  */
 
+#ifndef __alpha__
 static void rtc_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 {
 	/*
@@ -136,9 +152,11 @@
 		add_timer(&rtc_irq_timer);
 	}
 }
+#endif
 
 /*
  *	Now all the various file operations that we export.
+ *      They are all useless on Alpha...  *sigh*.
  */
 
 static long long rtc_llseek(struct file *file, loff_t offset, int origin)
@@ -149,6 +167,9 @@
 static ssize_t rtc_read(struct file *file, char *buf,
 			size_t count, loff_t *ppos)
 {
+#ifdef __alpha__
+	return -EIO;
+#else
 	struct wait_queue wait = { current, NULL };
 	unsigned long data;
 	ssize_t retval;
@@ -175,239 +196,269 @@
 	retval = put_user(data, (unsigned long *)buf); 
 	if (!retval)
 		retval = sizeof(unsigned long); 
-out:
+ out:
 	current->state = TASK_RUNNING;
 	remove_wait_queue(&rtc_wait, &wait);
 
 	return retval;
+#endif
 }
 
 static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
-	unsigned long arg)
+		     unsigned long arg)
 {
 
 	unsigned long flags;
 	struct rtc_time wtime; 
 
 	switch (cmd) {
-		case RTC_AIE_OFF:	/* Mask alarm int. enab. bit	*/
-		{
-			mask_rtc_irq_bit(RTC_AIE);
-			return 0;
-		}
-		case RTC_AIE_ON:	/* Allow alarm interrupts.	*/
-		{
-			set_rtc_irq_bit(RTC_AIE);
-			return 0;
-		}
-		case RTC_PIE_OFF:	/* Mask periodic int. enab. bit	*/
-		{
-			mask_rtc_irq_bit(RTC_PIE);
-			if (rtc_status & RTC_TIMER_ON) {
-				del_timer(&rtc_irq_timer);
-				rtc_status &= ~RTC_TIMER_ON;
-			}
-			return 0;
+#ifndef __alpha__
+	case RTC_AIE_OFF:	/* Mask alarm int. enab. bit	*/
+	{
+		mask_rtc_irq_bit(RTC_AIE);
+		return 0;
+	}
+	case RTC_AIE_ON:	/* Allow alarm interrupts.	*/
+	{
+		set_rtc_irq_bit(RTC_AIE);
+		return 0;
+	}
+	case RTC_PIE_OFF:	/* Mask periodic int. enab. bit	*/
+	{
+		mask_rtc_irq_bit(RTC_PIE);
+		if (rtc_status & RTC_TIMER_ON) {
+			del_timer(&rtc_irq_timer);
+			rtc_status &= ~RTC_TIMER_ON;
 		}
-		case RTC_PIE_ON:	/* Allow periodic ints		*/
-		{
+		return 0;
+	}
+	case RTC_PIE_ON:	/* Allow periodic ints		*/
+	{
 
-			/*
-			 * We don't really want Joe User enabling more
-			 * than 64Hz of interrupts on a multi-user machine.
-			 */
-			if ((rtc_freq > 64) && (!suser()))
-				return -EACCES;
-
-			if (!(rtc_status & RTC_TIMER_ON)) {
-				rtc_status |= RTC_TIMER_ON;
-				rtc_irq_timer.expires = jiffies + HZ/rtc_freq + 2*HZ/100;
-				add_timer(&rtc_irq_timer);
-			}
-			set_rtc_irq_bit(RTC_PIE);
-			return 0;
-		}
-		case RTC_UIE_OFF:	/* Mask ints from RTC updates.	*/
-		{
-			mask_rtc_irq_bit(RTC_UIE);
-			return 0;
+		/*
+		 * We don't really want Joe User enabling more
+		 * than 64Hz of interrupts on a multi-user machine.
+		 */
+		if ((rtc_freq > 64) && (!suser()))
+			return -EACCES;
+
+		if (!(rtc_status & RTC_TIMER_ON)) {
+			rtc_status |= RTC_TIMER_ON;
+			rtc_irq_timer.expires = jiffies + HZ/rtc_freq + 2*HZ/100;
+			add_timer(&rtc_irq_timer);
 		}
-		case RTC_UIE_ON:	/* Allow ints for RTC updates.	*/
-		{
-			set_rtc_irq_bit(RTC_UIE);
-			return 0;
-		}
-		case RTC_ALM_READ:	/* Read the present alarm time */
-		{
-			/*
-			 * This returns a struct rtc_time. Reading >= 0xc0
-			 * means "don't care" or "match all". Only the tm_hour,
-			 * tm_min, and tm_sec values are filled in.
-			 */
+		set_rtc_irq_bit(RTC_PIE);
+		return 0;
+	}
+	case RTC_UIE_OFF:	/* Mask ints from RTC updates.	*/
+	{
+		mask_rtc_irq_bit(RTC_UIE);
+		return 0;
+	}
+	case RTC_UIE_ON:	/* Allow ints for RTC updates.	*/
+	{
+		set_rtc_irq_bit(RTC_UIE);
+		return 0;
+	}
+#endif
+	case RTC_ALM_READ:	/* Read the present alarm time */
+	{
+		/*
+		 * This returns a struct rtc_time. Reading >= 0xc0
+		 * means "don't care" or "match all". Only the tm_hour,
+		 * tm_min, and tm_sec values are filled in.
+		 */
 
-			get_rtc_alm_time(&wtime);
-			break; 
-		}
-		case RTC_ALM_SET:	/* Store a time into the alarm */
-		{
-			/*
-			 * This expects a struct rtc_time. Writing 0xff means
-			 * "don't care" or "match all". Only the tm_hour,
-			 * tm_min and tm_sec are used.
-			 */
-			unsigned char hrs, min, sec;
-			struct rtc_time alm_tm;
-
-			if (copy_from_user(&alm_tm, (struct rtc_time*)arg, sizeof(struct rtc_time)))
-				return -EFAULT;
-
-			hrs = alm_tm.tm_hour;
-			min = alm_tm.tm_min;
-			sec = alm_tm.tm_sec;
-
-			if (hrs >= 24)
-				hrs = 0xff;
-
-			if (min >= 60)
-				min = 0xff;
-
-			if (sec >= 60)
-				sec = 0xff;
-
-			save_flags(flags);
-			cli();
-			if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY) ||
-							RTC_ALWAYS_BCD)
-			{
-				BIN_TO_BCD(sec);
-				BIN_TO_BCD(min);
-				BIN_TO_BCD(hrs);
-			}
-			CMOS_WRITE(hrs, RTC_HOURS_ALARM);
-			CMOS_WRITE(min, RTC_MINUTES_ALARM);
-			CMOS_WRITE(sec, RTC_SECONDS_ALARM);
-			restore_flags(flags);
+		get_rtc_alm_time(&wtime);
+		break; 
+	}
+	case RTC_ALM_SET:	/* Store a time into the alarm */
+	{
+		/*
+		 * This expects a struct rtc_time. Writing 0xff means
+		 * "don't care" or "match all". Only the tm_hour,
+		 * tm_min and tm_sec are used.
+		 */
+		unsigned char hrs, min, sec;
+		struct rtc_time alm_tm;
+
+		if (copy_from_user(&alm_tm, (struct rtc_time*)arg,
+				   sizeof(struct rtc_time)))
+			return -EFAULT;
+
+		hrs = alm_tm.tm_hour;
+		min = alm_tm.tm_min;
+		sec = alm_tm.tm_sec;
+
+		if (hrs >= 24)
+			hrs = 0xff;
+
+		if (min >= 60)
+			min = 0xff;
+
+		if (sec >= 60)
+			sec = 0xff;
+
+		save_flags(flags);
+		cli();
+		if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY) ||
+		    RTC_ALWAYS_BCD)
+		{
+			BIN_TO_BCD(sec);
+			BIN_TO_BCD(min);
+			BIN_TO_BCD(hrs);
+		}
+		CMOS_WRITE(hrs, RTC_HOURS_ALARM);
+		CMOS_WRITE(min, RTC_MINUTES_ALARM);
+		CMOS_WRITE(sec, RTC_SECONDS_ALARM);
+		restore_flags(flags);
 
-			return 0;
-		}
-		case RTC_RD_TIME:	/* Read the time/date from RTC	*/
-		{
-			get_rtc_time(&wtime);
-			break;
-		}
-		case RTC_SET_TIME:	/* Set the RTC */
-		{
-			struct rtc_time rtc_tm;
-			unsigned char mon, day, hrs, min, sec, leap_yr;
-			unsigned char save_control, save_freq_select;
-			unsigned int yrs;
-			unsigned long flags;
+		return 0;
+	}
+	case RTC_RD_TIME:	/* Read the time/date from RTC	*/
+	{
+		get_rtc_time(&wtime);
+		break;
+	}
+	case RTC_SET_TIME:	/* Set the RTC */
+	{
+		struct rtc_time rtc_tm;
+		unsigned char mon, day, hrs, min, sec, leap_yr;
+		unsigned char save_control, save_freq_select;
+		unsigned int yrs;
+		unsigned long flags;
 			
-			if (!suser())
-				return -EACCES;
-
-			if (copy_from_user(&rtc_tm, (struct rtc_time*)arg, sizeof(struct rtc_time)))
-				return -EFAULT;
+		if (!suser())
+			return -EACCES;
 
-			yrs = rtc_tm.tm_year + 1900 + ARCFUDGE;
-			mon = rtc_tm.tm_mon + 1;   /* tm_mon starts at zero */
-			day = rtc_tm.tm_mday;
-			hrs = rtc_tm.tm_hour;
-			min = rtc_tm.tm_min;
-			sec = rtc_tm.tm_sec;
+		if (copy_from_user(&rtc_tm, (struct rtc_time*)arg,
+				   sizeof(struct rtc_time)))
+			return -EFAULT;
+
+		yrs = rtc_tm.tm_year + 1900 + ARCFUDGE;
+		mon = rtc_tm.tm_mon + 1;   /* tm_mon starts at zero */
+		day = rtc_tm.tm_mday;
+		hrs = rtc_tm.tm_hour;
+		min = rtc_tm.tm_min;
+		sec = rtc_tm.tm_sec;
 
-			if ((yrs < 1970) || (yrs > 2069))
-				return -EINVAL;
+		if (yrs < 1970)
+			return -EINVAL;
 
-			leap_yr = ((!(yrs % 4) && (yrs % 100)) || !(yrs % 400));
+		leap_yr = ((!(yrs % 4) && (yrs % 100)) || !(yrs % 400));
 
-			if ((mon > 12) || (day == 0))
-				return -EINVAL;
+		if ((mon > 12) || (day == 0))
+			return -EINVAL;
 
-			if (day > (days_in_mo[mon] + ((mon == 2) && leap_yr)))
-				return -EINVAL;
+		if (day > (days_in_mo[mon] + ((mon == 2) && leap_yr)))
+			return -EINVAL;
 			
-			if ((hrs >= 24) || (min >= 60) || (sec >= 60))
-				return -EINVAL;
+		if ((hrs >= 24) || (min >= 60) || (sec >= 60))
+			return -EINVAL;
 
-			if (yrs >= 2000)
-				yrs -= 2000;	/* RTC (0, 1, ... 69) */
-			else
-				yrs -= 1900;	/* RTC (70, 71, ... 99) */
-
-			save_flags(flags);
-			cli();
-			if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY) ||
-							RTC_ALWAYS_BCD)
-			{
-				BIN_TO_BCD(sec);
-				BIN_TO_BCD(min);
-				BIN_TO_BCD(hrs);
-				BIN_TO_BCD(day);
-				BIN_TO_BCD(mon);
-				BIN_TO_BCD(yrs);
+		if ((yrs -= epoch) > 255)    /* They are unsigned */
+			return -EINVAL;
+
+		save_flags(flags);
+		cli();
+		if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY)
+		    || RTC_ALWAYS_BCD) {
+			if (yrs > 169) {
+				restore_flags(flags);
+				return -EINVAL;
 			}
+			if (yrs >= 100)
+				yrs -= 100;
 
-			save_control = CMOS_READ(RTC_CONTROL);
-			CMOS_WRITE((save_control|RTC_SET), RTC_CONTROL);
-			save_freq_select = CMOS_READ(RTC_FREQ_SELECT);
-			CMOS_WRITE((save_freq_select|RTC_DIV_RESET2), RTC_FREQ_SELECT);
-
-			CMOS_WRITE(yrs, RTC_YEAR);
-			CMOS_WRITE(mon, RTC_MONTH);
-			CMOS_WRITE(day, RTC_DAY_OF_MONTH);
-			CMOS_WRITE(hrs, RTC_HOURS);
-			CMOS_WRITE(min, RTC_MINUTES);
-			CMOS_WRITE(sec, RTC_SECONDS);
+			BIN_TO_BCD(sec);
+			BIN_TO_BCD(min);
+			BIN_TO_BCD(hrs);
+			BIN_TO_BCD(day);
+			BIN_TO_BCD(mon);
+			BIN_TO_BCD(yrs);
+		}
+
+		save_control = CMOS_READ(RTC_CONTROL);
+		CMOS_WRITE((save_control|RTC_SET), RTC_CONTROL);
+		save_freq_select = CMOS_READ(RTC_FREQ_SELECT);
+		CMOS_WRITE((save_freq_select|RTC_DIV_RESET2), RTC_FREQ_SELECT);
+
+		CMOS_WRITE(yrs, RTC_YEAR);
+		CMOS_WRITE(mon, RTC_MONTH);
+		CMOS_WRITE(day, RTC_DAY_OF_MONTH);
+		CMOS_WRITE(hrs, RTC_HOURS);
+		CMOS_WRITE(min, RTC_MINUTES);
+		CMOS_WRITE(sec, RTC_SECONDS);
 
-			CMOS_WRITE(save_control, RTC_CONTROL);
-			CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT);
+		CMOS_WRITE(save_control, RTC_CONTROL);
+		CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT);
 
-			restore_flags(flags);
-			return 0;
-		}
-		case RTC_IRQP_READ:	/* Read the periodic IRQ rate.	*/
-		{
-			return put_user(rtc_freq, (unsigned long *)arg);
-		}
-		case RTC_IRQP_SET:	/* Set periodic IRQ rate.	*/
-		{
-			int tmp = 0;
-			unsigned char val;
+		restore_flags(flags);
+		return 0;
+	}
+	case RTC_IRQP_READ:	/* Read the periodic IRQ rate.	*/
+	{
+		return put_user(rtc_freq, (unsigned long *)arg);
+	}
+#ifndef __alpha__
+	case RTC_IRQP_SET:	/* Set periodic IRQ rate.	*/
+	{
+		int tmp = 0;
+		unsigned char val;
 
-			/* 
-			 * The max we can do is 8192Hz.
-			 */
-			if ((arg < 2) || (arg > 8192))
-				return -EINVAL;
-			/*
-			 * We don't really want Joe User generating more
-			 * than 64Hz of interrupts on a multi-user machine.
-			 */
-			if ((arg > 64) && (!suser()))
-				return -EACCES;
-
-			while (arg > (1<<tmp))
-				tmp++;
-
-			/*
-			 * Check that the input was really a power of 2.
-			 */
-			if (arg != (1<<tmp))
-				return -EINVAL;
+		/* 
+		 * The max we can do is 8192Hz.
+		 */
+		if ((arg < 2) || (arg > 8192))
+			return -EINVAL;
+		/*
+		 * We don't really want Joe User generating more
+		 * than 64Hz of interrupts on a multi-user machine.
+		 */
+		if ((arg > 64) && (!suser()))
+			return -EACCES;
+
+		while (arg > (1<<tmp))
+			tmp++;
+
+		/*
+		 * Check that the input was really a power of 2.
+		 */
+		if (arg != (1<<tmp))
+			return -EINVAL;
 
-			rtc_freq = arg;
+		rtc_freq = arg;
 
-			save_flags(flags);
-			cli();
-			val = CMOS_READ(RTC_FREQ_SELECT) & 0xf0;
-			val |= (16 - tmp);
-			CMOS_WRITE(val, RTC_FREQ_SELECT);
-			restore_flags(flags);
-			return 0;
-		}
-		default:
+		save_flags(flags);
+		cli();
+		val = CMOS_READ(RTC_FREQ_SELECT) & 0xf0;
+		val |= (16 - tmp);
+		CMOS_WRITE(val, RTC_FREQ_SELECT);
+		restore_flags(flags);
+		return 0;
+	}
+#else /* __alpha__ */
+	case RTC_EPOCH_READ:	/* Read the epoch.	*/
+	{
+		return put_user (epoch, (unsigned long *)arg);
+	}
+	case RTC_EPOCH_SET:	/* Set the epoch.	*/
+	{
+		/* 
+		 * There were no RTC clocks before 1900.
+		 */
+		if (arg < 1900)
 			return -EINVAL;
+
+		if (!suser())
+			return -EACCES;
+
+		epoch = arg;
+		return 0;
+	}
+#endif
+	default:
+		return -EINVAL;
 	}
 	return copy_to_user((void *)arg, &wtime, sizeof wtime) ? -EFAULT : 0;
 }
@@ -416,27 +467,29 @@
  *	We enforce only one user at a time here with the open/close.
  *	Also clear the previous interrupt data on an open, and clean
  *	up things on a close.
+ *	On Alpha we just open, for we don't mess with interrups anyway.
  */
 
 static int rtc_open(struct inode *inode, struct file *file)
 {
-
+#ifndef __alpha__
 	if(rtc_status & RTC_IS_OPEN)
 		return -EBUSY;
 
 	rtc_status |= RTC_IS_OPEN;
 	rtc_irq_data = 0;
+#endif
 	return 0;
 }
 
 static int rtc_release(struct inode *inode, struct file *file)
 {
-
 	/*
 	 * Turn off all interrupts once the device is no longer
 	 * in use, and clear the data.
 	 */
 
+#ifndef __alpha__
 	unsigned char tmp;
 	unsigned long flags;
 
@@ -457,16 +510,19 @@
 
 	rtc_irq_data = 0;
 	rtc_status &= ~RTC_IS_OPEN;
+#endif
 	return 0;
 }
 
+#ifndef __alpha__
 static unsigned int rtc_poll(struct file *file, poll_table *wait)
 {
-	poll_wait(&rtc_wait, wait);
+	poll_wait(file, &rtc_wait, wait);
 	if (rtc_irq_data != 0)
 		return POLLIN | POLLRDNORM;
 	return 0;
 }
+#endif
 
 /*
  *	The various file operations we support.
@@ -477,7 +533,11 @@
 	rtc_read,
 	NULL,		/* No write */
 	NULL,		/* No readdir */
+#ifdef __alpha__
+	NULL,		/* No select on Alpha */
+#else
 	rtc_poll,
+#endif
 	rtc_ioctl,
 	NULL,		/* No mmap */
 	rtc_open,
@@ -494,17 +554,53 @@
 __initfunc(int rtc_init(void))
 {
 	unsigned long flags;
-
+#ifdef __alpha__
+	unsigned int year, ctrl;
+	unsigned long uip_watchdog;
+	char *guess = NULL;
+#endif
 	printk(KERN_INFO "Real Time Clock Driver v%s\n", RTC_VERSION);
+#ifndef __alpha__
 	if(request_irq(RTC_IRQ, rtc_interrupt, SA_INTERRUPT, "rtc", NULL))
 	{
 		/* Yeah right, seeing as irq 8 doesn't even hit the bus. */
 		printk(KERN_ERR "rtc: IRQ %d is not free.\n", RTC_IRQ);
 		return -EIO;
 	}
+#endif
 	misc_register(&rtc_dev);
 	/* Check region? Naaah! Just snarf it up. */
 	request_region(RTC_PORT(0), RTC_IO_EXTENT, "rtc");
+#ifdef __alpha__
+	rtc_freq = HZ;
+	
+	/* Each operating system on an Alpha uses its own epoch.
+	   Let's try to guess which one we are using now. */
+	
+	uip_watchdog = jiffies;
+	if (rtc_is_updating() != 0)
+		while (jiffies - uip_watchdog < 2*HZ/100)
+			barrier();
+	
+	save_flags(flags);
+	cli();
+	year = CMOS_READ(RTC_YEAR);
+	ctrl = CMOS_READ(RTC_CONTROL);
+	restore_flags(flags);
+	
+	if (!(ctrl & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
+		BCD_TO_BIN(year);       /* This should never happen... */
+	
+	if (year > 10 && year < 44) {
+		epoch = 1980;
+		guess = "ARC console";
+	} else if (year < 96) {
+		epoch = 1952;
+		guess = "Digital UNIX";
+	}
+	if (guess)
+		printk("rtc: %s epoch (%ld) detected\n", guess, epoch);
+#else
 	init_timer(&rtc_irq_timer);
 	rtc_irq_timer.function = rtc_dropped_irq;
 	rtc_wait = NULL;
@@ -514,6 +610,7 @@
 	CMOS_WRITE(((CMOS_READ(RTC_FREQ_SELECT) & 0xF0) | 0x06), RTC_FREQ_SELECT);
 	restore_flags(flags);
 	rtc_freq = 1024;
+#endif
 	return 0;
 }
 
@@ -529,6 +626,7 @@
  *	for something that requires a steady > 1KHz signal anyways.)
  */
 
+#ifndef __alpha__
 void rtc_dropped_irq(unsigned long data)
 {
 	unsigned long flags;
@@ -545,6 +643,7 @@
 	rtc_irq_data |= (CMOS_READ(RTC_INTR_FLAGS) & 0xF0);	/* restart */
 	restore_flags(flags);
 }
+#endif
 
 /*
  *	Info exported via "/proc/rtc".
@@ -572,10 +671,10 @@
 	 * time or for Universal Standard Time (GMT). Probably local though.
 	 */
 	p += sprintf(p,
-		"rtc_time\t: %02d:%02d:%02d\n"
-		"rtc_date\t: %04d-%02d-%02d\n",
-		tm.tm_hour, tm.tm_min, tm.tm_sec,
-		tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
+		     "rtc_time\t: %02d:%02d:%02d\n"
+		     "rtc_date\t: %04d-%02d-%02d\n",
+		     tm.tm_hour, tm.tm_min, tm.tm_sec,
+		     tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
 
 	get_rtc_alm_time(&tm);
 
@@ -601,24 +700,24 @@
 		p += sprintf(p, "**\n");
 
 	p += sprintf(p,
-		"DST_enable\t: %s\n"
-		"BCD\t\t: %s\n"
-		"24hr\t\t: %s\n"
-		"square_wave\t: %s\n"
-		"alarm_IRQ\t: %s\n"
-		"update_IRQ\t: %s\n"
-		"periodic_IRQ\t: %s\n"
-		"periodic_freq\t: %ld\n"
-		"batt_status\t: %s\n",
-		(ctrl & RTC_DST_EN) ? "yes" : "no",
-		(ctrl & RTC_DM_BINARY) ? "no" : "yes",
-		(ctrl & RTC_24H) ? "yes" : "no",
-		(ctrl & RTC_SQWE) ? "yes" : "no",
-		(ctrl & RTC_AIE) ? "yes" : "no",
-		(ctrl & RTC_UIE) ? "yes" : "no",
-		(ctrl & RTC_PIE) ? "yes" : "no",
-		rtc_freq,
-		batt ? "okay" : "dead");
+		     "DST_enable\t: %s\n"
+		     "BCD\t\t: %s\n"
+		     "24hr\t\t: %s\n"
+		     "square_wave\t: %s\n"
+		     "alarm_IRQ\t: %s\n"
+		     "update_IRQ\t: %s\n"
+		     "periodic_IRQ\t: %s\n"
+		     "periodic_freq\t: %ld\n"
+		     "batt_status\t: %s\n",
+		     (ctrl & RTC_DST_EN) ? "yes" : "no",
+		     (ctrl & RTC_DM_BINARY) ? "no" : "yes",
+		     (ctrl & RTC_24H) ? "yes" : "no",
+		     (ctrl & RTC_SQWE) ? "yes" : "no",
+		     (ctrl & RTC_AIE) ? "yes" : "no",
+		     (ctrl & RTC_UIE) ? "yes" : "no",
+		     (ctrl & RTC_PIE) ? "yes" : "no",
+		     rtc_freq,
+		     batt ? "okay" : "dead");
 
 	return  p - buf;
 }
@@ -689,7 +788,7 @@
 	 * Account for differences between how the RTC uses the values
 	 * and how they are defined in a struct rtc_time;
 	 */
-	if (rtc_tm->tm_year <= 69)
+	if ((rtc_tm->tm_year += epoch - 1900) <= 69)
 		rtc_tm->tm_year += 100;
 
 	/* if ARCFUDGE == 0, the optimizer should do away with this */
@@ -732,6 +831,8 @@
  * We also clear out any old irq data after an ioctl() that
  * meddles with the interrupt enable/disable bits.
  */
+
+#ifndef __alpha__
 void mask_rtc_irq_bit(unsigned char bit)
 {
 	unsigned char val;
@@ -761,4 +862,4 @@
 	rtc_irq_data = 0;
 	restore_flags(flags);
 }
-
+#endif
diff -u --recursive --new-file v2.1.88/linux/drivers/char/specialix.c linux/drivers/char/specialix.c
--- v2.1.88/linux/drivers/char/specialix.c	Wed Dec  3 17:36:26 1997
+++ linux/drivers/char/specialix.c	Thu Mar  5 11:55:06 1998
@@ -2,7 +2,7 @@
  *      specialix.c  -- specialix IO8+ multiport serial driver.
  *
  *      Copyright (C) 1997  Roger Wolff (R.E.Wolff@BitWizard.nl)
- *      Copyright (C) 1994-1996  Dmitry Gorodchanin (begemot@bgm.rosprint.net)
+ *      Copyright (C) 1994-1996  Dmitry Gorodchanin (pgmdsg@ibi.com)
  *
  *      Specialix pays for the development and support of this driver.
  *      Please DO contact io8-linux@specialix.co.uk if you require
diff -u --recursive --new-file v2.1.88/linux/drivers/char/specialix_io8.h linux/drivers/char/specialix_io8.h
--- v2.1.88/linux/drivers/char/specialix_io8.h	Tue Dec  2 09:19:03 1997
+++ linux/drivers/char/specialix_io8.h	Thu Mar  5 11:55:06 1998
@@ -3,7 +3,7 @@
  *                                   Specialix IO8+ multiport serial driver.
  *
  *      Copyright (C) 1997 Roger Wolff (R.E.Wolff@BitWizard.nl)
- *      Copyright (C) 1994-1996  Dmitry Gorodchanin (begemot@bgm.rosprint.net)
+ *      Copyright (C) 1994-1996  Dmitry Gorodchanin (pgmdsg@ibi.com)
  *
  *
  *      Specialix pays for the development and support of this driver.
diff -u --recursive --new-file v2.1.88/linux/drivers/char/tty_io.c linux/drivers/char/tty_io.c
--- v2.1.88/linux/drivers/char/tty_io.c	Tue Feb 17 13:12:46 1998
+++ linux/drivers/char/tty_io.c	Tue Feb 24 22:37:02 1998
@@ -1961,8 +1961,10 @@
 	return kmem_start;
 }
 
-static struct tty_driver dev_tty_driver, dev_console_driver,
-	dev_syscons_driver, dev_ptmx_driver;
+static struct tty_driver dev_tty_driver, dev_syscons_driver, dev_ptmx_driver;
+#ifdef CONFIG_VT
+static struct tty_driver dev_console_driver;
+#endif
 
 /*
  * Ok, now we can initialize the rest of the tty devices and can count
diff -u --recursive --new-file v2.1.88/linux/drivers/isdn/avmb1/capi.c linux/drivers/isdn/avmb1/capi.c
--- v2.1.88/linux/drivers/isdn/avmb1/capi.c	Thu Feb 12 20:56:06 1998
+++ linux/drivers/isdn/avmb1/capi.c	Fri Feb 27 09:11:27 1998
@@ -207,7 +207,7 @@
 		return POLLERR;
 
 	cdev = &capidevs[minor];
-	poll_wait(&(cdev->recv_wait), wait);
+	poll_wait(file, &(cdev->recv_wait), wait);
 	mask = POLLOUT | POLLWRNORM;
 	if (!skb_queue_empty(&cdev->recv_queue))
 		mask |= POLLIN | POLLRDNORM;
diff -u --recursive --new-file v2.1.88/linux/drivers/isdn/isdn_common.c linux/drivers/isdn/isdn_common.c
--- v2.1.88/linux/drivers/isdn/isdn_common.c	Tue Feb 17 13:12:46 1998
+++ linux/drivers/isdn/isdn_common.c	Thu Feb 26 20:00:42 1998
@@ -1015,7 +1015,7 @@
 	int drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL);
 
 	if (minor == ISDN_MINOR_STATUS) {
-		poll_wait(&(dev->info_waitq), wait);
+		poll_wait(file, &(dev->info_waitq), wait);
 		/* mask = POLLOUT | POLLWRNORM; */
 		if (file->private_data) {
 			mask |= POLLIN | POLLRDNORM;
@@ -1023,7 +1023,7 @@
 		return mask;
 	}
 	if (minor >= ISDN_MINOR_CTRL && minor <= ISDN_MINOR_CTRLMAX) {
-		poll_wait(&(dev->drv[drvidx]->st_waitq), wait);
+		poll_wait(file, &(dev->drv[drvidx]->st_waitq), wait);
 		if (drvidx < 0) {
 			printk(KERN_ERR "isdn_common: isdn_poll 1 -> what the hell\n");
 			return POLLERR;
diff -u --recursive --new-file v2.1.88/linux/drivers/isdn/isdn_ppp.c linux/drivers/isdn/isdn_ppp.c
--- v2.1.88/linux/drivers/isdn/isdn_ppp.c	Thu Feb 12 20:56:06 1998
+++ linux/drivers/isdn/isdn_ppp.c	Thu Feb 26 20:00:52 1998
@@ -702,7 +702,7 @@
 	if (is->debug & 0x2)
 		printk(KERN_DEBUG "isdn_ppp_poll: minor: %d\n", MINOR(file->f_dentry->d_inode->i_rdev));
 
-	poll_wait(&is->wq, wait);
+	poll_wait(file, &is->wq, wait);
 
 	if (!(is->state & IPPP_OPEN)) {
 		printk(KERN_DEBUG "isdn_ppp: device not open\n");
diff -u --recursive --new-file v2.1.88/linux/drivers/macintosh/aty.c linux/drivers/macintosh/aty.c
--- v2.1.88/linux/drivers/macintosh/aty.c	Mon Feb 23 18:12:05 1998
+++ linux/drivers/macintosh/aty.c	Tue Feb 24 22:08:01 1998
@@ -39,11 +39,11 @@
 typedef struct aty_regvals {
 	int offset[3];		/* first pixel address */
 
-	int crtc_h_sync_strt_wid[3];	/* depth dependant */
+	int crtc_h_sync_strt_wid[3];	/* depth dependent */
 	int crtc_gen_cntl[3];
 	int mem_cntl[3];
 
-	int crtc_h_tot_disp;	/* mode dependant */
+	int crtc_h_tot_disp;	/* mode dependent */
 	int crtc_v_tot_disp;
 	int crtc_v_sync_strt_wid;
 	int crtc_off_pitch;
diff -u --recursive --new-file v2.1.88/linux/drivers/misc/parport_arc.c linux/drivers/misc/parport_arc.c
--- v2.1.88/linux/drivers/misc/parport_arc.c	Fri Jan 30 11:28:07 1998
+++ linux/drivers/misc/parport_arc.c	Tue Feb 24 22:33:03 1998
@@ -31,15 +31,15 @@
 #define DATA_LATCH    0x3350010
 
 /* ARC can't read from the data latch, so we must use a soft copy. */
-static unsigned int data_copy;
+static unsigned char data_copy;
 
-static void arc_write_data(struct parport *p, unsigned int data)
+static void arc_write_data(struct parport *p, unsigned char data)
 {
 	data_copy = data;
 	outb(data, DATA_LATCH);
 }
 
-static unsigned int arc_read_data(struct parport *p)
+static unsigned char arc_read_data(struct parport *p)
 {
 	return data_copy;
 }
diff -u --recursive --new-file v2.1.88/linux/drivers/misc/parport_ax.c linux/drivers/misc/parport_ax.c
--- v2.1.88/linux/drivers/misc/parport_ax.c	Mon Feb 23 18:12:05 1998
+++ linux/drivers/misc/parport_ax.c	Tue Feb 24 22:33:03 1998
@@ -55,83 +55,83 @@
 }
 
 void
-parport_ax_write_epp(struct parport *p, unsigned int d)
+parport_ax_write_epp(struct parport *p, unsigned char d)
 {
 	outb(d, p->base + EPPREG);
 }
 
-unsigned int
+unsigned char
 parport_ax_read_epp(struct parport *p)
 {
-	return (unsigned int)inb(p->base + EPPREG);
+	return inb(p->base + EPPREG);
 }
 
-unsigned int
+unsigned char
 parport_ax_read_configb(struct parport *p)
 {
-	return (unsigned int)inb(p->base + CONFIGB);
+	return inb(p->base + CONFIGB);
 }
 
 void
-parport_ax_write_data(struct parport *p, unsigned int d)
+parport_ax_write_data(struct parport *p, unsigned char d)
 {
 	outb(d, p->base + DATA);
 }
 
-unsigned int
+unsigned char
 parport_ax_read_data(struct parport *p)
 {
-	return (unsigned int)inb(p->base + DATA);
+	return inb(p->base + DATA);
 }
 
 void
-parport_ax_write_control(struct parport *p, unsigned int d)
+parport_ax_write_control(struct parport *p, unsigned char d)
 {
 	outb(d, p->base + CONTROL);
 }
 
-unsigned int
+unsigned char
 parport_ax_read_control(struct parport *p)
 {
-	return (unsigned int)inb(p->base + CONTROL);
+	return inb(p->base + CONTROL);
 }
 
-unsigned int
-parport_ax_frob_control(struct parport *p, unsigned int mask,  unsigned int val)
+unsigned char
+parport_ax_frob_control(struct parport *p, unsigned char mask,  unsigned char val)
 {
-	unsigned int old = (unsigned int)inb(p->base + CONTROL);
+	unsigned char old = inb(p->base + CONTROL);
 	outb(((old & ~mask) ^ val), p->base + CONTROL);
 	return old;
 }
 
 void
-parport_ax_write_status(struct parport *p, unsigned int d)
+parport_ax_write_status(struct parport *p, unsigned char d)
 {
 	outb(d, p->base + STATUS);
 }
 
-unsigned int
+unsigned char
 parport_ax_read_status(struct parport *p)
 {
-	return (unsigned int)inb(p->base + STATUS);
+	return inb(p->base + STATUS);
 }
 
 void
-parport_ax_write_econtrol(struct parport *p, unsigned int d)
+parport_ax_write_econtrol(struct parport *p, unsigned char d)
 {
 	outb(d, p->base + ECONTROL);
 }
 
-unsigned int
+unsigned char
 parport_ax_read_econtrol(struct parport *p)
 {
-	return (unsigned int)inb(p->base + ECONTROL);
+	return inb(p->base + ECONTROL);
 }
 
-unsigned int
-parport_ax_frob_econtrol(struct parport *p, unsigned int mask, unsigned int val)
+unsigned char
+parport_ax_frob_econtrol(struct parport *p, unsigned char mask, unsigned char val)
 {
-	unsigned int old = (unsigned int)inb(p->base + ECONTROL);
+	unsigned char old = inb(p->base + ECONTROL);
 	outb(((old & ~mask) ^ val), p->base + ECONTROL);
 	return old;
 }
@@ -143,12 +143,12 @@
 }
 
 void
-parport_ax_write_fifo(struct parport *p, unsigned int v)
+parport_ax_write_fifo(struct parport *p, unsigned char v)
 {
 	outb(v, p->base + DFIFO);
 }
 
-unsigned int
+unsigned char
 parport_ax_read_fifo(struct parport *p)
 {
 	return inb(p->base + DFIFO);
@@ -221,29 +221,29 @@
 	parport_ax_write_econtrol(p, s->u.pc.ecr);
 }
 
-unsigned int
-parport_ax_epp_read_block(struct parport *p, void *buf, unsigned  int length)
+size_t
+parport_ax_epp_read_block(struct parport *p, void *buf, size_t length)
 {
 	return 0; /* FIXME */
 }
 
-unsigned int
-parport_ax_epp_write_block(struct parport *p, void *buf, unsigned  int length)
+size_t
+parport_ax_epp_write_block(struct parport *p, void *buf, size_t length)
 {
 	return 0; /* FIXME */
 }
 
-unsigned int
-parport_ax_ecp_read_block(struct parport *p, void *buf, unsigned  int length,
-			  void (*fn)(struct parport *, void *, unsigned int),
+int
+parport_ax_ecp_read_block(struct parport *p, void *buf, size_t length,
+			  void (*fn)(struct parport *, void *, size_t),
 			  void *handle)
 {
 	return 0; /* FIXME */
 }
 
-unsigned int
-parport_ax_ecp_write_block(struct parport *p, void *buf, unsigned  int length,
-			   void (*fn)(struct parport *, void *, unsigned int),
+int
+parport_ax_ecp_write_block(struct parport *p, void *buf, size_t length,
+			   void (*fn)(struct parport *, void *, size_t),
 			   void *handle)
 {
 	return 0; /* FIXME */
@@ -331,7 +331,8 @@
  */
 static int parport_ECR_present(struct parport *pb)
 {
-	unsigned int r, octr = pb->ops->read_control(pb), 
+	unsigned int r;
+	unsigned char octr = pb->ops->read_control(pb), 
 	  oecr = pb->ops->read_econtrol(pb);
 
 	r = pb->ops->read_control(pb);	
@@ -360,7 +361,8 @@
 
 static int parport_ECP_supported(struct parport *pb)
 {
-	int i, oecr = pb->ops->read_econtrol(pb);
+	int i;
+	unsigned char oecr = pb->ops->read_econtrol(pb);
 	
 	/* If there is no ECONTROL, we have no hope of supporting ECP. */
 	if (!(pb->modes & PARPORT_MODE_PCECR))
@@ -398,7 +400,8 @@
 
 static int parport_PS2_supported(struct parport *pb)
 {
-	int ok = 0, octr = pb->ops->read_control(pb);
+	int ok = 0;
+	unsigned char octr = pb->ops->read_control(pb);
   
 	pb->ops->write_control(pb, octr | 0x20);  /* try to tri-state buffer */
 	
@@ -415,7 +418,8 @@
 
 static int parport_ECPPS2_supported(struct parport *pb)
 {
-	int mode, oecr = pb->ops->read_econtrol(pb);
+	int mode;
+	unsigned char oecr = pb->ops->read_econtrol(pb);
 
 	if (!(pb->modes & PARPORT_MODE_PCECR))
 		return 0;
diff -u --recursive --new-file v2.1.88/linux/drivers/misc/parport_pc.c linux/drivers/misc/parport_pc.c
--- v2.1.88/linux/drivers/misc/parport_pc.c	Mon Feb 23 18:12:05 1998
+++ linux/drivers/misc/parport_pc.c	Tue Feb 24 22:33:03 1998
@@ -58,71 +58,71 @@
 	/* Null function - does nothing */
 }
 
-void parport_pc_write_epp(struct parport *p, unsigned int d)
+void parport_pc_write_epp(struct parport *p, unsigned char d)
 {
 	outb(d, p->base+EPPREG);
 }
 
-unsigned int parport_pc_read_epp(struct parport *p)
+unsigned char parport_pc_read_epp(struct parport *p)
 {
-	return (unsigned int)inb(p->base+EPPREG);
+	return inb(p->base+EPPREG);
 }
 
-unsigned int parport_pc_read_configb(struct parport *p)
+unsigned char parport_pc_read_configb(struct parport *p)
 {
-	return (unsigned int)inb(p->base+CONFIGB);
+	return inb(p->base+CONFIGB);
 }
 
-void parport_pc_write_data(struct parport *p, unsigned int d)
+void parport_pc_write_data(struct parport *p, unsigned char d)
 {
 	outb(d, p->base+DATA);
 }
 
-unsigned int parport_pc_read_data(struct parport *p)
+unsigned char parport_pc_read_data(struct parport *p)
 {
-	return (unsigned int)inb(p->base+DATA);
+	return inb(p->base+DATA);
 }
 
-void parport_pc_write_control(struct parport *p, unsigned int d)
+void parport_pc_write_control(struct parport *p, unsigned char d)
 {
 	outb(d, p->base+CONTROL);
 }
 
-unsigned int parport_pc_read_control(struct parport *p)
+unsigned char parport_pc_read_control(struct parport *p)
 {
-	return (unsigned int)inb(p->base+CONTROL);
+	return inb(p->base+CONTROL);
 }
 
-unsigned int parport_pc_frob_control(struct parport *p, unsigned int mask,  unsigned int val)
+unsigned char parport_pc_frob_control(struct parport *p, unsigned char mask,  unsigned char val)
 {
-	unsigned int old = (unsigned int)inb(p->base+CONTROL);
+	unsigned char old = inb(p->base+CONTROL);
 	outb(((old & ~mask) ^ val), p->base+CONTROL);
 	return old;
 }
 
-void parport_pc_write_status(struct parport *p, unsigned int d)
+void parport_pc_write_status(struct parport *p, unsigned char d)
 {
 	outb(d, p->base+STATUS);
 }
 
-unsigned int parport_pc_read_status(struct parport *p)
+unsigned char parport_pc_read_status(struct parport *p)
 {
-	return (unsigned int)inb(p->base+STATUS);
+	return inb(p->base+STATUS);
 }
 
-void parport_pc_write_econtrol(struct parport *p, unsigned int d)
+void parport_pc_write_econtrol(struct parport *p, unsigned char d)
 {
 	outb(d, p->base+ECONTROL);
 }
 
-unsigned int parport_pc_read_econtrol(struct parport *p)
+unsigned char parport_pc_read_econtrol(struct parport *p)
 {
-	return (unsigned int)inb(p->base+ECONTROL);
+	return inb(p->base+ECONTROL);
 }
 
-unsigned int parport_pc_frob_econtrol(struct parport *p, unsigned int mask,  unsigned int val)
+unsigned char parport_pc_frob_econtrol(struct parport *p, unsigned char mask,  unsigned char val)
 {
-	unsigned int old = (unsigned int)inb(p->base+ECONTROL);
+	unsigned char old = inb(p->base+ECONTROL);
 	outb(((old & ~mask) ^ val), p->base+ECONTROL);
 	return old;
 }
@@ -132,12 +132,12 @@
 	/* FIXME */
 }
 
-void parport_pc_write_fifo(struct parport *p, unsigned int v)
+void parport_pc_write_fifo(struct parport *p, unsigned char v)
 {
 	/* FIXME */
 }
 
-unsigned int parport_pc_read_fifo(struct parport *p)
+unsigned char parport_pc_read_fifo(struct parport *p)
 {
 	return 0; /* FIXME */
 }
@@ -184,22 +184,22 @@
 	parport_pc_write_econtrol(p, s->u.pc.ecr);
 }
 
-unsigned int parport_pc_epp_read_block(struct parport *p, void *buf, unsigned  int length)
+size_t parport_pc_epp_read_block(struct parport *p, void *buf, size_t length)
 {
 	return 0; /* FIXME */
 }
 
-unsigned int parport_pc_epp_write_block(struct parport *p, void *buf, unsigned  int length)
+size_t parport_pc_epp_write_block(struct parport *p, void *buf, size_t length)
 {
 	return 0; /* FIXME */
 }
 
-unsigned int parport_pc_ecp_read_block(struct parport *p, void *buf, unsigned  int length, void (*fn)(struct parport *, void *, unsigned int), void *handle)
+int parport_pc_ecp_read_block(struct parport *p, void *buf, size_t length, void (*fn)(struct parport *, void *, size_t), void *handle)
 {
 	return 0; /* FIXME */
 }
 
-unsigned int parport_pc_ecp_write_block(struct parport *p, void *buf, unsigned  int length, void (*fn)(struct parport *, void *, unsigned int), void *handle)
+int parport_pc_ecp_write_block(struct parport *p, void *buf, size_t length, void (*fn)(struct parport *, void *, size_t), void *handle)
 {
 	return 0; /* FIXME */
 }
@@ -337,7 +337,7 @@
 /* Only if supports ECP mode */
 static int programmable_dma_support(struct parport *pb)
 {
-	int dma, oldstate = parport_pc_read_econtrol(pb);
+	unsigned char dma, oldstate = parport_pc_read_econtrol(pb);
 
 	parport_pc_write_econtrol(pb, 0xe0); /* Configuration MODE */
 	
@@ -375,7 +375,7 @@
 static int parport_dma_probe(struct parport *pb)
 {
 	int dma,retv;
-	int dsr,dsr_read;
+	unsigned char dsr,dsr_read;
 	char *buff;
 
 	retv = programmable_dma_support(pb);
@@ -424,7 +424,7 @@
  */
 static int epp_clear_timeout(struct parport *pb)
 {
-	int r;
+	unsigned char r;
 
 	if (!(parport_pc_read_status(pb) & 0x01))
 		return 1;
@@ -470,7 +470,7 @@
  */
 static int parport_ECR_present(struct parport *pb)
 {
-	unsigned int r, octr = parport_pc_read_control(pb), 
+	unsigned char r, octr = parport_pc_read_control(pb), 
 	  oecr = parport_pc_read_econtrol(pb);
 
 	r = parport_pc_read_control(pb);	
@@ -499,7 +499,8 @@
 
 static int parport_ECP_supported(struct parport *pb)
 {
-	int i, oecr = parport_pc_read_econtrol(pb);
+	int i;
+	unsigned char oecr = parport_pc_read_econtrol(pb);
 	
 	/* If there is no ECR, we have no hope of supporting ECP. */
 	if (!(pb->modes & PARPORT_MODE_PCECR))
@@ -553,7 +554,8 @@
 
 static int parport_ECPEPP_supported(struct parport *pb)
 {
-	int mode, oecr = parport_pc_read_econtrol(pb);
+	int mode;
+	unsigned char oecr = parport_pc_read_econtrol(pb);
 
 	if (!(pb->modes & PARPORT_MODE_PCECR))
 		return 0;
@@ -587,7 +589,8 @@
 
 static int parport_PS2_supported(struct parport *pb)
 {
-	int ok = 0, octr = parport_pc_read_control(pb);
+	int ok = 0;
+	unsigned char octr = parport_pc_read_control(pb);
   
 	epp_clear_timeout(pb);
 
@@ -606,7 +609,8 @@
 
 static int parport_ECPPS2_supported(struct parport *pb)
 {
-	int mode, oecr = parport_pc_read_econtrol(pb);
+	int mode;
+	unsigned char oecr = parport_pc_read_econtrol(pb);
 
 	if (!(pb->modes & PARPORT_MODE_PCECR))
 		return 0;
@@ -676,33 +680,25 @@
 /* Only if supports ECP mode */
 static int programmable_irq_support(struct parport *pb)
 {
-	int irq, oecr = parport_pc_read_econtrol(pb);
+	int irq, intrLine;
+	unsigned char oecr = parport_pc_read_econtrol(pb);
+	static const int lookup[8] = {
+		PARPORT_IRQ_NONE, 7, 9, 10, 11, 14, 15, 5
+	};
 
 	parport_pc_write_econtrol(pb,0xE0); /* Configuration MODE */
 	
-	irq = (parport_pc_read_configb(pb) >> 3) & 0x07;
+	intrLine = (parport_pc_read_configb(pb) >> 3) & 0x07;
+	irq = lookup[intrLine];
 
-	switch(irq){
-	case 2:
-		irq = 9;
-		break;
-	case 7:
-		irq = 5;
-		break;
-	case 0:
-		irq = PARPORT_IRQ_NONE;
-		break;
-	default:
-		irq += 7;
-	}
-	
 	parport_pc_write_econtrol(pb, oecr);
 	return irq;
 }
 
 static int irq_probe_ECP(struct parport *pb)
 {
-	int irqs, i, oecr = parport_pc_read_econtrol(pb);
+	int irqs, i;
+	unsigned char oecr = parport_pc_read_econtrol(pb);
 		
 	probe_irq_off(probe_irq_on());	/* Clear any interrupts */
 	irqs = open_intr_election();
@@ -725,7 +721,8 @@
  */
 static int irq_probe_EPP(struct parport *pb)
 {
-	int irqs, octr = parport_pc_read_control(pb);
+	int irqs;
+	unsigned char octr = parport_pc_read_control(pb);
 
 #ifndef ADVANCED_DETECT
 	return PARPORT_IRQ_NONE;
@@ -755,7 +752,8 @@
 
 static int irq_probe_SPP(struct parport *pb)
 {
-	int irqs, octr = parport_pc_read_control(pb);
+	int irqs;
+	unsigned char octr = parport_pc_read_control(pb);
 
 #ifndef ADVANCED_DETECT
 	return PARPORT_IRQ_NONE;
diff -u --recursive --new-file v2.1.88/linux/drivers/misc/parport_share.c linux/drivers/misc/parport_share.c
--- v2.1.88/linux/drivers/misc/parport_share.c	Fri Jan 30 11:28:07 1998
+++ linux/drivers/misc/parport_share.c	Tue Feb 24 22:40:44 1998
@@ -90,6 +90,7 @@
 	tmp->devices = tmp->cad = NULL;
 	tmp->flags = 0;
 	tmp->ops = ops;
+	tmp->number = portcount;
 	spin_lock_init (&tmp->lock);
 
 	tmp->name = kmalloc(15, GFP_KERNEL);
diff -u --recursive --new-file v2.1.88/linux/drivers/net/CONFIG linux/drivers/net/CONFIG
--- v2.1.88/linux/drivers/net/CONFIG	Mon Jan  5 01:41:01 1998
+++ linux/drivers/net/CONFIG	Wed Dec 31 16:00:00 1969
@@ -1,79 +0,0 @@
-#
-# This file is used for selecting non-standard netcard options, and
-# need not be modified for typical use.
-#
-# Drivers are *not* selected in this file, but rather with files
-# automatically generated during the top-level kernel configuration.
-#
-# Special options supported, indexed by their 'config' name:
-#
-#  CONFIG_WD80x3	The Western Digital (SMC) WD80x3 driver
-#	WD_SHMEM=xxx	Forces the address of the shared memory
-#  CONFIG_NE2000	The NE-[12]000 clone driver.
-#	PACKETBUF_MEMSIZE  Allows an extra-large packet buffer to be
-#			used.  Usually pointless under Linux.
-#	show_all_SAPROM  Show the entire address PROM, not just the
-#			ethernet address, during boot.
-#	CONFIG_NE_RW_BUGFIX  Patch an obscure bug with a version of the 8390.
-#	CONFIG_NE_SANITY Double check the internal card xfer address
-#			against the driver's value. Useful for debugging.
-#  CONFIG_HPLAN		The HP-LAN driver (for 8390-based boards only).
-#	rw_bugfix	Fix the same obscure bug.
-#  CONFIG_EL2		The 3c503 EtherLink II driver
-#	EL2_AUI		Default to the AUI port instead of the BNC port
-#	no_probe_nonshared_memory  Don't probe for programmed-I/O boards.
-#	EL2MEMTEST	Test shared memory at boot-time.
-#  CONFIG_PLIP		The Crynwr-protocol PL/IP driver
-#	INITIALTIMEOUTFACTOR	Timing parameters.
-#	MAXTIMEOUTFACTOR
-#  DE600		The D-Link DE-600 Portable Ethernet Adaptor.
-#	  DE600_IO	The DE600 I/O-port address (0x378 == default)
-#	  DE600_IRQ	The DE600 IRQ number to use (IRQ7 == default)
-#	  DE600_DEBUG	Enable or disable DE600 debugging (default off)
-#  DE620		The D-Link DE-600 Portable Ethernet Adaptor.
-#	  DE620_IO	The DE620 I/O-port address (0x378 == default)
-#	  DE620_IRQ	The DE620 IRQ number to use (IRQ7 == default)
-#	  DE620_DEBUG	Enable or disable DE600 debugging (default off)
-#  DEPCA		The DIGITAL series of LANCE based Ethernet Cards
-#			(DEPCA, DE100, DE200/1/2, DE210, DE422 (EISA))
-#  EWRK3		The DIGITAL series of AT Ethernet Cards (DE203/4/5)
-#       EWRK3_DEBUG	Set the desired debug level
-#
-#  DE4x5		The DIGITAL series of PCI/EISA Ethernet Cards,
-#			DE425, DE434, DE435, DE450, DE500
-#     DE4X5_DEBUG	Set the desired debug level
-#     DEC_ONLY          Allows driver to work with DIGITAL cards only - 
-#			see linux/drivers/net/README.de4x5
-#     DE4X5_DO_MEMCPY   Forces the Intels to use memory copies into sk_buffs
-#	                rather than straight DMA.
-#     DE4X5_PARM	See linux/Documentation/networking/de4x5.txt or the
-#			driver source code for detailed information on setting
-#			duplex and speed/media on individual adapters.
-#
-#  DEFXX		The DIGITAL series of FDDI EISA (DEFEA) and PCI (DEFPA)
-#			controllers
-#       DEFXX_DEBUG	Set the desired debug level
-#
-#  TULIP		Tulip (dc21040/dc21041/ds21140) driver
-#	TULIP_PORT	specify default if_port
-#			0: 10TP
-#			1: 100Tx(ds21140)/AUI(dc2104x)
-#			2: BNC(dc2104x)
-#	TULIP_FIX_PORT	don't change if_port automatically if defined
-#	TULIP_MAX_CARDS	maximum number of probed card
-#
-
-# The following options exist, but cannot be set in this file.
-#  lance.c
-#	LANCE_DMA	Change the default DMA to other than DMA5.
-#  8390.c
-#	NO_PINGPONG	Disable ping-pong transmit buffers.
-
-
-# Most drivers also have a *_DEBUG setting that may be adjusted.
-# The 8390 drivers share the EI_DEBUG setting.
-
-# General options for Space.c
-CONFIG_Space.o	= # -DETH0_ADDR=0x300 -DETH0_IRQ=11
-CONFIG_3c503.o	= # -DEL2_AUI
-CONFIG_wd.o	= # -DWD_SHMEM=0xDD000
diff -u --recursive --new-file v2.1.88/linux/drivers/net/Config.in linux/drivers/net/Config.in
--- v2.1.88/linux/drivers/net/Config.in	Thu Jan 15 14:33:06 1998
+++ linux/drivers/net/Config.in	Sun Mar  1 14:40:39 1998
@@ -15,8 +15,8 @@
 tristate 'Dummy net driver support' CONFIG_DUMMY
 tristate 'EQL (serial line load balancing) support' CONFIG_EQUALIZER
 if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
-  if [ "$CONFIG_NETLINK_DEV" = "y" -o "$CONFIG_NETLINK_DEV" = "m" ]; then
-     dep_tristate 'Ethertap network tap' CONFIG_ETHERTAP $CONFIG_NETLINK_DEV
+  if [ "$CONFIG_NETLINK" = "y" ]; then
+     tristate 'Ethertap network tap' CONFIG_ETHERTAP
   fi
 fi
 #
@@ -53,6 +53,7 @@
     else
 		tristate 'SMC Ultra support' CONFIG_ULTRA
     fi
+    tristate 'SMC Ultra32 EISA support' CONFIG_ULTRA32
     tristate 'SMC 9194 support' CONFIG_SMC9194
   fi
   bool 'Racal-Interlan (Micom) NI cards' CONFIG_NET_VENDOR_RACAL
diff -u --recursive --new-file v2.1.88/linux/drivers/net/Makefile linux/drivers/net/Makefile
--- v2.1.88/linux/drivers/net/Makefile	Mon Feb 23 18:12:05 1998
+++ linux/drivers/net/Makefile	Mon Mar  2 11:55:41 1998
@@ -3,10 +3,6 @@
 # Makefile for the Linux network (ethercard) device drivers.
 #
 
-# This will go away in some future future: hidden configuration files
-# are difficult for users to deal with.
-include CONFIG
-
 SUB_DIRS     := 
 MOD_SUB_DIRS := $(SUB_DIRS)
 ALL_SUB_DIRS := $(SUB_DIRS) hamradio
@@ -169,6 +165,16 @@
   ifeq ($(CONFIG_ULTRA),m)
   CONFIG_8390_MODULE = y
   M_OBJS += smc-ultra.o
+  endif
+endif
+
+ifeq ($(CONFIG_ULTRA32),y)
+L_OBJS += smc-ultra32.o
+CONFIG_8390_BUILTIN = y
+else
+  ifeq ($(CONFIG_ULTRA32),m)
+  CONFIG_8390_MODULE = y
+  M_OBJS += smc-ultra32.o
   endif
 endif
 
diff -u --recursive --new-file v2.1.88/linux/drivers/net/Space.c linux/drivers/net/Space.c
--- v2.1.88/linux/drivers/net/Space.c	Mon Feb 23 18:12:05 1998
+++ linux/drivers/net/Space.c	Thu Feb 26 11:01:24 1998
@@ -138,6 +138,9 @@
 #if defined(CONFIG_MCA)
         && ultramca_probe(dev)
 #endif
+#if defined(CONFIG_ULTRA32)
+	&& ultra32_probe(dev)
+#endif
 #endif
 #if defined(CONFIG_SMC9194)
 	&& smc_init(dev)
diff -u --recursive --new-file v2.1.88/linux/drivers/net/cops.c linux/drivers/net/cops.c
--- v2.1.88/linux/drivers/net/cops.c	Mon Feb 23 18:12:05 1998
+++ linux/drivers/net/cops.c	Mon Mar  2 11:55:41 1998
@@ -113,8 +113,8 @@
  *	the same and just have different names or only have minor differences
  *	such as more IO ports. As this driver is tested it will
  *	become more clear on exactly what cards are supported. The driver
- *	defaults to using Dayna mode. To change the drivers mode adjust
- *	drivers/net/CONFIG, and the line COPS_OPTS = -DDAYNA to -DTANGENT.
+ *	defaults to using Dayna mode. To change the drivers mode, simply
+ *	select Dayna or Tangent mode when configuring the kernel.
  *
  *      This driver should support:
  *      TANGENT driver mode:
diff -u --recursive --new-file v2.1.88/linux/drivers/net/de4x5.c linux/drivers/net/de4x5.c
--- v2.1.88/linux/drivers/net/de4x5.c	Thu Feb 12 20:56:07 1998
+++ linux/drivers/net/de4x5.c	Mon Mar  2 11:55:41 1998
@@ -213,8 +213,8 @@
 
         insmod de4x5 args='eth1:fdx autosense=BNC eth0:autosense=100Mb'.
 
-    For a compiled in driver, in linux/drivers/net/CONFIG, place e.g.
-	DE4X5_OPTS = -DDE4X5_PARM='"eth0:fdx autosense=AUI eth2:autosense=TP"' 
+    For a compiled in driver, somewhere in this file, place e.g.
+	#define DE4X5_PARM "eth0:fdx autosense=AUI eth2:autosense=TP"
 
     Yes,  I know full duplex  isn't permissible on BNC  or AUI; they're just
     examples. By default, full duplex is turned  off and AUTO is the default
@@ -538,8 +538,8 @@
 ** insmod de4x5 args='eth1:fdx autosense=BNC eth0:autosense=100Mb'.
 **
 ** For a compiled in driver, place e.g.
-** DE4X5_OPTS = -DDE4X5_PARM='"eth0:fdx autosense=AUI eth2:autosense=TP"' 
-** in linux/drivers/net/CONFIG
+** #define DE4X5_PARM "eth0:fdx autosense=AUI eth2:autosense=TP"
+** somewhere in this file above this point.
 */
 #ifdef DE4X5_PARM
 static char *args = DE4X5_PARM;
diff -u --recursive --new-file v2.1.88/linux/drivers/net/de620.c linux/drivers/net/de620.c
--- v2.1.88/linux/drivers/net/de620.c	Thu Feb 12 20:56:08 1998
+++ linux/drivers/net/de620.c	Mon Mar  2 11:55:41 1998
@@ -100,7 +100,6 @@
 
 /*
  * Enable debugging by "-DDE620_DEBUG=3" when compiling,
- * OR in "./CONFIG"
  * OR by enabling the following #define
  *
  * use 0 for production, 1 for verification, >2 for debug
diff -u --recursive --new-file v2.1.88/linux/drivers/net/dgrs.c linux/drivers/net/dgrs.c
--- v2.1.88/linux/drivers/net/dgrs.c	Thu Feb 12 20:56:08 1998
+++ linux/drivers/net/dgrs.c	Tue Feb 24 22:08:01 1998
@@ -21,7 +21,7 @@
  *	When compiled as a loadable module, this driver can operate
  *	the board as either a 4/6 port switch with a 5th or 7th port
  *	that is a conventional NIC interface as far as the host is
- *	concerned, OR as 4/6 independant NICs.  To select multi-NIC
+ *	concerned, OR as 4/6 independent NICs.  To select multi-NIC
  *	mode, add "nicmode=1" on the insmod load line for the driver.
  *
  *	This driver uses the "dev" common ethernet device structure
diff -u --recursive --new-file v2.1.88/linux/drivers/net/ethertap.c linux/drivers/net/ethertap.c
--- v2.1.88/linux/drivers/net/ethertap.c	Thu Feb 12 20:56:08 1998
+++ linux/drivers/net/ethertap.c	Sun Mar  1 14:40:39 1998
@@ -11,6 +11,7 @@
  *	even for building bridging tunnels.
  */
  
+#include <linux/config.h>
 #include <linux/module.h>
 
 #include <linux/kernel.h>
@@ -25,6 +26,7 @@
 #include <linux/skbuff.h>
 #include <linux/init.h>
 
+#include <net/sock.h>
 #include <linux/netlink.h>
 
 /*
@@ -36,7 +38,10 @@
 static int  ethertap_start_xmit(struct sk_buff *skb, struct device *dev);
 static int  ethertap_close(struct device *dev);
 static struct net_device_stats *ethertap_get_stats(struct device *dev);
-static int ethertap_rx(int id, struct sk_buff *skb);
+static void ethertap_rx(struct sock *sk, int len);
+#ifdef CONFIG_ETHERTAP_MC
+static void set_multicast_list(struct device *dev);
+#endif
 
 static int ethertap_debug = 0;
 
@@ -48,6 +53,10 @@
 
 struct net_local
 {
+	struct sock	*nl;
+#ifdef CONFIG_ETHERTAP_MC
+	__u32		groups;
+#endif
 	struct net_device_stats stats;
 };
 
@@ -58,7 +67,7 @@
  
 __initfunc(int ethertap_probe(struct device *dev))
 {
-	memcpy(dev->dev_addr, "\xFD\xFD\x00\x00\x00\x00", 6);
+	memcpy(dev->dev_addr, "\xFE\xFD\x00\x00\x00\x00", 6);
 	if (dev->mem_start & 0xf)
 		ethertap_debug = dev->mem_start & 0x7;
 
@@ -79,6 +88,9 @@
 	dev->hard_start_xmit = ethertap_start_xmit;
 	dev->stop = ethertap_close;
 	dev->get_stats = ethertap_get_stats;
+#ifdef CONFIG_ETHERTAP_MC
+	dev->set_multicast_list = set_multicast_list;
+#endif
 
 	/*
 	 *	Setup the generic properties
@@ -86,10 +98,10 @@
 
 	ether_setup(dev);
 
-	dev->flags|=IFF_NOARP;	/* Need to set ARP - looks like there is a bug
-				   in the 2.1.x hard header code currently */
+	dev->tx_queue_len = 0;
+	dev->flags|=IFF_NOARP;
 	tap_map[dev->base_addr]=dev;
-	
+
 	return 0;
 }
 
@@ -99,28 +111,58 @@
 
 static int ethertap_open(struct device *dev)
 {
-	struct in_device *in_dev;
+	struct net_local *lp = (struct net_local*)dev->priv;
+
 	if (ethertap_debug > 2)
 		printk("%s: Doing ethertap_open()...", dev->name);
-	netlink_attach(dev->base_addr, ethertap_rx);
+
+	MOD_INC_USE_COUNT;
+
+	lp->nl = netlink_kernel_create(dev->base_addr, ethertap_rx);
+	if (lp->nl == NULL) {
+		MOD_DEC_USE_COUNT;
+		return -ENOBUFS;
+	}
+
 	dev->start = 1;
 	dev->tbusy = 0;
+	return 0;
+}
 
-	/* Fill in the MAC based on the IP address. We do the same thing
-	   here as PLIP does */
-	
-	if((in_dev=dev->ip_ptr)!=NULL)
-	{
-		/*
-		 *	Any address wil do - we take the first
-		 */
-		struct in_ifaddr *ifa=in_dev->ifa_list;
-		if(ifa!=NULL)
-			memcpy(dev->dev_addr+2,&ifa->ifa_local,4);
+#ifdef CONFIG_ETHERTAP_MC
+static unsigned ethertap_mc_hash(__u8 *dest)
+{
+	unsigned idx = 0;
+	idx ^= dest[0];
+	idx ^= dest[1];
+	idx ^= dest[2];
+	idx ^= dest[3];
+	idx ^= dest[4];
+	idx ^= dest[5];
+	return 1U << (idx&0x1F);
+}
+
+static void set_multicast_list(struct device *dev)
+{
+	unsigned groups = ~0;
+	struct net_local *lp = (struct net_local *)dev->priv;
+
+	if (!(dev->flags&(IFF_NOARP|IFF_PROMISC|IFF_ALLMULTI))) {
+		struct dev_mc_list *dmi;
+
+		groups = ethertap_mc_hash(dev->broadcast);
+
+		for (dmi=dev->mc_list; dmi; dmi=dmi->next) {
+			if (dmi->dmi_addrlen != 6)
+				continue;
+			groups |= ethertap_mc_hash(dmi->dmi_addr);
+		}
 	}
-	MOD_INC_USE_COUNT;
-	return 0;
+	lp->groups = groups;
+	if (lp->nl)
+		lp->nl->protinfo.af_netlink.groups = groups;
 }
+#endif
 
 /*
  *	We transmit by throwing the packet at netlink. We have to clone
@@ -130,20 +172,110 @@
 static int ethertap_start_xmit(struct sk_buff *skb, struct device *dev)
 {
 	struct net_local *lp = (struct net_local *)dev->priv;
-	struct sk_buff *tmp;
-	/* copy buffer to tap */
-	tmp=skb_clone(skb, GFP_ATOMIC);
-	if(tmp)
-	{
-		if(netlink_post(dev->base_addr, tmp)<0)
-			kfree_skb(tmp);
-		lp->stats.tx_bytes+=skb->len;
-		lp->stats.tx_packets++;
+#ifdef CONFIG_ETHERTAP_MC
+	struct ethhdr *eth = (struct ethhdr*)skb->data;
+#endif
+
+	if (skb_headroom(skb) < 2) {
+		printk(KERN_DEBUG "%s : bug --- xmit with head<2\n", dev->name);
+		dev_kfree_skb(skb);
+		return 0;
+	}
+	skb_push(skb, 2);
+
+	/* Make the same thing, which loopback does. */
+	if (skb_shared(skb)) {
+	  	struct sk_buff *skb2 = skb;
+	  	skb = skb_clone(skb, GFP_ATOMIC);	/* Clone the buffer */
+	  	if (skb==NULL) {
+			dev_kfree_skb(skb2);
+			return 0;
+		}
+	  	dev_kfree_skb(skb2);
 	}
-	dev_kfree_skb (skb);
+	/* ... but do not orphan it here, netlink does it in any case. */
+
+	lp->stats.tx_bytes+=skb->len;
+	lp->stats.tx_packets++;
+
+#ifndef CONFIG_ETHERTAP_MC
+	netlink_broadcast(lp->nl, skb, 0, ~0, GFP_ATOMIC);
+#else
+	if (dev->flags&IFF_NOARP) {
+		netlink_broadcast(lp->nl, skb, 0, ~0, GFP_ATOMIC);
+		return 0;
+	}
+
+	if (!(eth->h_dest[0]&1)) {
+		/* Unicast packet */
+		__u32 pid;
+		memcpy(&pid, eth->h_dest+2, 4);
+		netlink_unicast(lp->nl, skb, ntohl(pid), MSG_DONTWAIT);
+	} else
+		netlink_broadcast(lp->nl, skb, 0, ethertap_mc_hash(eth->h_dest), GFP_ATOMIC);
+#endif
 	return 0;
 }
 
+static __inline__ int ethertap_rx_skb(struct sk_buff *skb, struct device *dev)
+{
+	struct net_local *lp = (struct net_local *)dev->priv;
+#ifdef CONFIG_ETHERTAP_MC
+	struct ethhdr *eth = (struct ethhdr*)(skb->data + 2);
+#endif
+	int len = skb->len;
+
+	if (len < 16) {
+		printk(KERN_DEBUG "%s : rx len = %d\n", dev->name, len);
+		kfree_skb(skb);
+		return -EINVAL;
+	}
+	if (NETLINK_CREDS(skb)->uid) {
+		printk(KERN_INFO "%s : user %d\n", dev->name, NETLINK_CREDS(skb)->uid);
+		kfree_skb(skb);
+		return -EPERM;
+	}
+
+#ifdef CONFIG_ETHERTAP_MC
+	if (!(dev->flags&(IFF_NOARP|IFF_PROMISC))) {
+		int drop = 0;
+
+		if (eth->h_dest[0]&1) {
+			if (!(ethertap_mc_hash(eth->h_dest)&lp->groups))
+				drop = 1;
+		} else if (memcmp(eth->h_dest, dev->dev_addr, 6) != 0)
+			drop = 1;
+
+		if (drop) {
+			if (ethertap_debug > 3)
+				printk(KERN_DEBUG "%s : not for us\n", dev->name);
+			kfree_skb(skb);
+			return -EINVAL;
+		}
+	}
+#endif
+
+	if (skb_shared(skb)) {
+	  	struct sk_buff *skb2 = skb;
+	  	skb = skb_clone(skb, GFP_KERNEL);	/* Clone the buffer */
+	  	if (skb==NULL) {
+			kfree_skb(skb2);
+			return -ENOBUFS;
+		}
+	  	kfree_skb(skb2);
+	} else
+		skb_orphan(skb);
+
+	skb_pull(skb, 2);
+	skb->dev = dev;
+	skb->protocol=eth_type_trans(skb,dev);
+	memset(skb->cb, 0, sizeof(skb->cb));
+	lp->stats.rx_packets++;
+	lp->stats.rx_bytes+=len;
+	netif_rx(skb);
+	return len;
+}
+
 /*
  *	The typical workload of the driver:
  *	Handle the ether interface interrupts.
@@ -151,37 +283,40 @@
  *	(In this case handle the packets posted from user space..)
  */
 
-static int ethertap_rx(int id, struct sk_buff *skb)
+static void ethertap_rx(struct sock *sk, int len)
 {
-	struct device *dev = (struct device *)(tap_map[id]);
-	struct net_local *lp;
-	int len=skb->len;
-	
-	if(dev==NULL)
-	{
-		printk("ethertap: bad unit!\n");
-		kfree_skb(skb);
-		return -ENXIO;
+	struct device *dev = tap_map[sk->protocol];
+	struct sk_buff *skb;
+
+	if (dev==NULL) {
+		printk(KERN_CRIT "ethertap: bad unit!\n");
+		skb_queue_purge(&sk->receive_queue);
+		return;
 	}
-	lp = (struct net_local *)dev->priv;
 
 	if (ethertap_debug > 3)
 		printk("%s: ethertap_rx()\n", dev->name);
-	skb->dev = dev;
-	skb->protocol=eth_type_trans(skb,dev);
-	lp->stats.rx_packets++;
-	lp->stats.rx_bytes+=len;
-	netif_rx(skb);
-	return len;
+
+	while ((skb = skb_dequeue(&sk->receive_queue)) != NULL)
+		ethertap_rx_skb(skb, dev);
 }
 
 static int ethertap_close(struct device *dev)
 {
+	struct net_local *lp = (struct net_local *)dev->priv;
+	struct sock *sk = lp->nl;
+
 	if (ethertap_debug > 2)
-		printk("%s: Shutting down tap %ld.\n", dev->name, dev->base_addr);
+		printk("%s: Shutting down.\n", dev->name);
 
 	dev->tbusy = 1;
 	dev->start = 0;
+
+	if (sk) {
+		lp->nl = NULL;
+		sock_release(sk->socket);
+	}
+
 	MOD_DEC_USE_COUNT;
 	return 0;
 }
@@ -213,7 +348,7 @@
 	sprintf(devicename,"tap%d",unit);
 	if (dev_get(devicename))
 	{
-		printk(KERN_INFO "ethertap: tap %d already loaded.\n", unit);
+		printk(KERN_INFO "%s already loaded.\n", devicename);
 		return -EBUSY;
 	}
 	if (register_netdev(&dev_ethertap) != 0)
diff -u --recursive --new-file v2.1.88/linux/drivers/net/ppp.c linux/drivers/net/ppp.c
--- v2.1.88/linux/drivers/net/ppp.c	Mon Feb 23 18:12:05 1998
+++ linux/drivers/net/ppp.c	Sun Mar  1 14:40:39 1998
@@ -684,13 +684,6 @@
 	if (tty != NULL && tty->disc_data == ppp)
 		tty->disc_data = NULL;	/* Break the tty->ppp link */
 
-	rtnl_lock();
-	/* Strong layering violation. */
-	if (dev && dev->flags & IFF_UP) {
-		dev_close (dev); /* close the device properly */
-	}
-	rtnl_unlock();
-
 	ppp_free_buf (ppp->rbuf);
 	ppp_free_buf (ppp->wbuf);
 	ppp_free_buf (ppp->cbuf);
@@ -2564,8 +2557,8 @@
 	if (ppp && ppp->magic == PPP_MAGIC && tty == ppp->tty) {
 		CHECK_PPP (0);
 
-		poll_wait(&ppp->read_wait, wait);
-		poll_wait(&ppp->write_wait, wait);
+		poll_wait(filp, &ppp->read_wait, wait);
+		poll_wait(filp, &ppp->write_wait, wait);
 
 		/* Must lock the user buffer area while checking. */
 		CHECK_BUF_MAGIC(ppp->ubuf);
@@ -3021,6 +3014,25 @@
 	return ppp;
 }
 
+/* Collect hanged up channels */
+
+static void ppp_sync(void)
+{
+	struct device	*dev;
+	struct ppp	*ppp;
+
+	rtnl_lock();
+	for (ppp = ppp_list; ppp != 0; ppp = ppp->next) {
+		if (!ppp->inuse) {
+			dev = ppp2dev(ppp);
+			if (dev->flags&IFF_UP)
+				dev_close(dev);
+		}
+	}
+	rtnl_unlock();
+}
+
+
 /* allocate or create a PPP channel */
 static struct ppp *
 ppp_alloc (void)
@@ -3030,11 +3042,24 @@
 	struct device	*dev;
 	struct ppp	*ppp;
 
+	ppp_sync();
+
 	/* try to find an free device */
 	if_num = 0;
 	for (ppp = ppp_list; ppp != 0; ppp = ppp->next) {
-		if (!test_and_set_bit(0, &ppp->inuse))
+		if (!test_and_set_bit(0, &ppp->inuse)) {
+
+			/* Reregister device */
+
+			dev = ppp2dev(ppp);
+			unregister_netdev (dev);
+
+			if (register_netdev (dev)) {
+				printk(KERN_DEBUG "cannot reregister ppp device\n");
+				return NULL;
+			}
 			return ppp;
+		}
 		++if_num;
 	}
 /*
diff -u --recursive --new-file v2.1.88/linux/drivers/net/shaper.c linux/drivers/net/shaper.c
--- v2.1.88/linux/drivers/net/shaper.c	Thu Feb 12 20:56:09 1998
+++ linux/drivers/net/shaper.c	Sun Mar  1 14:40:39 1998
@@ -70,6 +70,7 @@
 #include <linux/if_arp.h>
 #include <linux/init.h>
 #include <net/dst.h>
+#include <net/arp.h>
 #include <linux/if_shaper.h>
 
 int sh_debug;		/* Debug flag */
@@ -447,6 +448,7 @@
 	return v;
 }
 
+#if 0
 static int shaper_cache(struct neighbour *neigh, struct hh_cache *hh)
 {
 	struct shaper *sh=neigh->dev->priv;
@@ -469,6 +471,26 @@
 		printk("Shaper cache update\n");
 	sh->header_cache_update(hh, sh->dev, haddr);
 }
+#endif
+
+static int shaper_neigh_setup(struct neighbour *n)
+{
+	if (n->nud_state == NUD_NONE) {
+		n->ops = &arp_broken_ops;
+		n->output = n->ops->output;
+	}
+	return 0;
+}
+
+static int shaper_neigh_setup_dev(struct device *dev, struct neigh_parms *p)
+{
+	if (p->tbl->family == AF_INET) {
+		p->neigh_setup = shaper_neigh_setup;
+		p->ucast_probes = 0;
+		p->mcast_probes = 0;
+	}
+	return 0;
+}
 
 static int shaper_attach(struct device *shdev, struct shaper *sh, struct device *dev)
 {
@@ -512,7 +534,8 @@
 #else
 	shdev->header_cache_update = NULL;
 	shdev->hard_header_cache = NULL;
-#endif		
+#endif
+	shdev->neigh_setup = shaper_neigh_setup_dev;
 	
 	shdev->hard_header_len=dev->hard_header_len;
 	shdev->type=dev->type;
@@ -587,8 +610,11 @@
 
 	dev->hard_header 	= shaper_header;
 	dev->rebuild_header 	= shaper_rebuild_header;
+#if 0
 	dev->hard_header_cache	= shaper_cache;
 	dev->header_cache_update= shaper_cache_update;
+#endif
+	dev->neigh_setup	= shaper_neigh_setup_dev;
 	dev->do_ioctl		= shaper_ioctl;
 	dev->hard_header_len	= 0;
 	dev->type		= ARPHRD_ETHER;	/* initially */
diff -u --recursive --new-file v2.1.88/linux/drivers/net/slip.c linux/drivers/net/slip.c
--- v2.1.88/linux/drivers/net/slip.c	Mon Feb 23 18:12:06 1998
+++ linux/drivers/net/slip.c	Sun Mar  1 14:40:39 1998
@@ -50,6 +50,8 @@
  *					from multislip BSDI driver which was written
  *					by Igor Chechik, RELCOM Corp. Only algorithms
  * 					have been ported to Linux SLIP driver.
+ *	Vitaly E. Lavrov	:	Sane behaviour on tty hangup.
+ *	Alexey Kuznetsov	:	Cleanup interfaces to tty&netdevice modules.
  */
 
 #define SL_CHECK_TRANSMIT
@@ -110,133 +112,139 @@
 static int sl_ioctl(struct device *dev,struct ifreq *rq,int cmd);
 #endif
 
-/* Find a free SLIP channel, and link in this `tty' line. */
-static inline struct slip *
-sl_alloc(void)
-{
-	slip_ctrl_t *slp = NULL;
-	int i;
-
-	if (slip_ctrls == NULL) return NULL;	/* Master array missing ! */
-
-	for (i = 0; i < slip_maxdev; i++) {
-	  slp = slip_ctrls[i];
-	  /* Not allocated ? */
-	  if (slp == NULL)
-	    break;
-	  /* Not in use ? */
-	  if (!test_and_set_bit(SLF_INUSE, &slp->ctrl.flags))
-	    break;
-	}
-	/* SLP is set.. */
-
-	/* Sorry, too many, all slots in use */
-	if (i >= slip_maxdev) return NULL;
-
-	/* If no channels are available, allocate one */
-	if (!slp &&
-	    (slip_ctrls[i] = (slip_ctrl_t *)kmalloc(sizeof(slip_ctrl_t),
-						    GFP_KERNEL)) != NULL) {
-	  slp = slip_ctrls[i];
-	  memset(slp, 0, sizeof(slip_ctrl_t));
-
-	  /* Initialize channel control data */
-	  set_bit(SLF_INUSE, &slp->ctrl.flags);
-	  slp->ctrl.tty         = NULL;
-	  sprintf(slp->if_name, "sl%d", i);
-	  slp->dev.name         = slp->if_name;
-	  slp->dev.base_addr    = i;
-	  slp->dev.priv         = (void*)&(slp->ctrl);
-	  slp->dev.next         = NULL;
-	  slp->dev.init         = slip_init;
-/* printk(KERN_INFO "slip: kmalloc()ed SLIP control node for line %s\n",
-   slp->if_name); */
-	}
-	if (slp != NULL) {
+/********************************
+*  Buffer administration routines:
+*	sl_alloc_bufs()
+*	sl_free_bufs()
+*	sl_realloc_bufs()
+*
+* NOTE: sl_realloc_bufs != sl_free_bufs + sl_alloc_bufs, because
+*	sl_realloc_bufs provides strong atomicity and reallocation
+*	on actively running device.
+*********************************/
 
-	  /* register device so that it can be ifconfig'ed       */
-	  /* slip_init() will be called as a side-effect         */
-	  /* SIDE-EFFECT WARNING: slip_init() CLEARS slp->ctrl ! */
-
-	  if (register_netdev(&(slp->dev)) == 0) {
-	    /* (Re-)Set the INUSE bit.   Very Important! */
-	    set_bit(SLF_INUSE, &slp->ctrl.flags);
-	    slp->ctrl.dev = &(slp->dev);
-	    slp->dev.priv = (void*)&(slp->ctrl);
+/* 
+   Allocate channel buffers.
+ */
 
-/* printk(KERN_INFO "slip: linked in netdev %s for active use\n",
-   slp->if_name); */
+static int
+sl_alloc_bufs(struct slip *sl, int mtu)
+{
+	int err = -ENOBUFS;
+	unsigned long len;
+	char * rbuff = NULL;
+	char * xbuff = NULL;
+#ifdef SL_INCLUDE_CSLIP
+	char * cbuff = NULL;
+	struct slcompress *slcomp = NULL;
+#endif
 
-	    return (&(slp->ctrl));
+	/*
+	 * Allocate the SLIP frame buffers:
+	 *
+	 * rbuff	Receive buffer.
+	 * xbuff	Transmit buffer.
+	 * cbuff        Temporary compression buffer.
+	 */
+	len = mtu * 2;
 
-	  } else {
-	    clear_bit(SLF_INUSE,&(slp->ctrl.flags));
-	    printk("sl_alloc() - register_netdev() failure.\n");
-	  }
+	/*
+	 * allow for arrival of larger UDP packets, even if we say not to
+	 * also fixes a bug in which SunOS sends 512-byte packets even with
+	 * an MSS of 128
+	 */
+	if (len < 576 * 2)
+		len = 576 * 2;
+	rbuff = kmalloc(len + 4, GFP_KERNEL);
+	if (rbuff == NULL)
+		goto err_exit;
+	xbuff = kmalloc(len + 4, GFP_KERNEL);
+	if (xbuff == NULL)
+		goto err_exit;
+#ifdef SL_INCLUDE_CSLIP
+	cbuff = kmalloc(len + 4, GFP_KERNEL);
+	if (cbuff == NULL)
+		goto err_exit;
+	slcomp = slhc_init(16, 16);
+	if (slcomp == NULL)
+		goto err_exit;
+#endif
+	start_bh_atomic();
+	if (sl->tty == NULL) {
+		end_bh_atomic();
+		err = -ENODEV;
+		goto err_exit;
 	}
+	sl->mtu	     = mtu;
+	sl->buffsize = len;
+	sl->rcount   = 0;
+	sl->xleft    = 0;
+	rbuff = xchg(&sl->rbuff, rbuff);
+	xbuff = xchg(&sl->xbuff, xbuff);
+#ifdef CONFIG_SLIP_MODE_SLIP6
+	cbuff = xchg(&sl->cbuff, cbuff);
+	slcomp = xchg(&sl->slcomp, slcomp);
+	sl->xdata    = 0;
+	sl->xbits    = 0;
+#endif
+	end_bh_atomic();
+	err = 0;
 
-	return NULL;
+	/* Cleanup */
+err_exit:
+#ifdef SL_INCLUDE_CSLIP
+	if (cbuff)
+		kfree(cbuff);
+	if (slcomp)
+		slhc_free(slcomp);
+#endif
+	if (xbuff)
+		kfree(xbuff);
+	if (rbuff)
+		kfree(rbuff);
+	return err;
 }
 
-
-/* Free a SLIP channel. */
-static inline void
-sl_free(struct slip *sl)
+/* Free a SLIP channel buffers. */
+static void
+sl_free_bufs(struct slip *sl)
 {
+	void * tmp;
+
 	/* Free all SLIP frame buffers. */
-	if (sl->rbuff)  {
-		kfree(sl->rbuff);
-	}
-	sl->rbuff = NULL;
-	if (sl->xbuff)  {
-		kfree(sl->xbuff);
-	}
-	sl->xbuff = NULL;
+	if ((tmp = xchg(&sl->rbuff, NULL)) != NULL)
+		kfree(tmp);
+	if ((tmp = xchg(&sl->xbuff, NULL)) != NULL)
+		kfree(tmp);
 #ifdef SL_INCLUDE_CSLIP
-	/* Save CSLIP statistics */
-	if (sl->slcomp)  {
-		sl->rx_compressed += sl->slcomp->sls_i_compressed;
-		sl->rx_dropped    += sl->slcomp->sls_i_tossed;
-		sl->tx_compressed += sl->slcomp->sls_o_compressed;
-		sl->tx_misses     += sl->slcomp->sls_o_misses;
-	}
-	if (sl->cbuff)  {
-		kfree(sl->cbuff);
-	}
-	sl->cbuff = NULL;
-	if(sl->slcomp)
-		slhc_free(sl->slcomp);
-	sl->slcomp = NULL;
+	if ((tmp = xchg(&sl->cbuff, NULL)) != NULL)
+		kfree(tmp);
+	if ((tmp = xchg(&sl->slcomp, NULL)) != NULL)
+		slhc_free(tmp);
 #endif
-
-	if (!test_and_clear_bit(SLF_INUSE, &sl->flags)) {
-		printk("%s: sl_free for already free unit.\n", sl->dev->name);
-	}
 }
 
-/* MTU has been changed by the IP layer. Unfortunately we are not told
-   about this, but we spot it ourselves and fix things up. We could be
-   in an upcall from the tty driver, or in an ip packet queue. */
+/* 
+   Reallocate slip channel buffers.
+ */
 
-static void sl_changedmtu(struct slip *sl)
+static int sl_realloc_bufs(struct slip *sl, int mtu)
 {
+	int err = 0;
 	struct device *dev = sl->dev;
-	unsigned char *xbuff, *rbuff, *oxbuff, *orbuff;
+	unsigned char *xbuff, *rbuff;
 #ifdef SL_INCLUDE_CSLIP
-	unsigned char *cbuff, *ocbuff;
+	unsigned char *cbuff;
 #endif
-	int len;
-	unsigned long flags;
+	int len = mtu * 2;
 
-	len = dev->mtu * 2;
 /*
  * allow for arrival of larger UDP packets, even if we say not to
  * also fixes a bug in which SunOS sends 512-byte packets even with
  * an MSS of 128
  */
-	if (len < 576 * 2)  {
+	if (len < 576 * 2)
 		len = 576 * 2;
-	}
 
 	xbuff = (unsigned char *) kmalloc (len + 4, GFP_ATOMIC);
 	rbuff = (unsigned char *) kmalloc (len + 4, GFP_ATOMIC);
@@ -244,37 +252,30 @@
 	cbuff = (unsigned char *) kmalloc (len + 4, GFP_ATOMIC);
 #endif
 
+
 #ifdef SL_INCLUDE_CSLIP
 	if (xbuff == NULL || rbuff == NULL || cbuff == NULL)  {
 #else
 	if (xbuff == NULL || rbuff == NULL)  {
 #endif
-		printk("%s: unable to grow slip buffers, MTU change cancelled.\n",
-		       sl->dev->name);
-		dev->mtu = sl->mtu;
-		if (xbuff != NULL)  {
-			kfree(xbuff);
-		}
-		if (rbuff != NULL)  {
-			kfree(rbuff);
+		if (mtu >= sl->mtu) {
+			printk("%s: unable to grow slip buffers, MTU change cancelled.\n",
+			       dev->name);
+			err = -ENOBUFS;
 		}
-#ifdef SL_INCLUDE_CSLIP
-		if (cbuff != NULL)  {
-			kfree(cbuff);
-		}
-#endif
-		return;
+		goto done;
 	}
 
-	save_flags(flags); cli();
+	start_bh_atomic();
+
+	err = -ENODEV;
+	if (sl->tty == NULL)
+		goto done_on_bh;
 
-	oxbuff    = sl->xbuff;
-	sl->xbuff = xbuff;
-	orbuff    = sl->rbuff;
-	sl->rbuff = rbuff;
+	xbuff    = xchg(&sl->xbuff, xbuff);
+	rbuff    = xchg(&sl->rbuff, rbuff);
 #ifdef SL_INCLUDE_CSLIP
-	ocbuff    = sl->cbuff;
-	sl->cbuff = cbuff;
+	cbuff    = xchg(&sl->cbuff, cbuff);
 #endif
 	if (sl->xleft)  {
 		if (sl->xleft <= len)  {
@@ -288,30 +289,31 @@
 
 	if (sl->rcount)  {
 		if (sl->rcount <= len) {
-			memcpy(sl->rbuff, orbuff, sl->rcount);
+			memcpy(sl->rbuff, rbuff, sl->rcount);
 		} else  {
 			sl->rcount = 0;
 			sl->rx_over_errors++;
 			set_bit(SLF_ERROR, &sl->flags);
 		}
 	}
-	sl->mtu      = dev->mtu;
-
+	sl->mtu      = mtu;
+	dev->mtu      = mtu;
 	sl->buffsize = len;
+	err = 0;
 
-	restore_flags(flags);
+done_on_bh:
+	end_bh_atomic();
 
-	if (oxbuff != NULL)   {
-		kfree(oxbuff);
-	}
-	if (orbuff != NULL)    {
-		kfree(orbuff);
-	}
+done:
+	if (xbuff)
+		kfree(xbuff);
+	if (rbuff)
+		kfree(rbuff);
 #ifdef SL_INCLUDE_CSLIP
-	if (ocbuff != NULL)  {
-		kfree(ocbuff);
-	}
+	if (cbuff)
+		kfree(cbuff);
 #endif
+	return err;
 }
 
 
@@ -398,14 +400,7 @@
 	unsigned char *p;
 	int actual, count;
 
-
-	if (sl->mtu != sl->dev->mtu) {	/* Someone has been ifconfigging */
-
-		sl_changedmtu(sl);
-	}
-
 	if (len > sl->mtu) {		/* Sigh, shouldn't occur BUT ... */
-		len = sl->mtu;
 		printk ("%s: truncating oversized transmit packet!\n", sl->dev->name);
 		sl->tx_dropped++;
 		sl_unlock(sl);
@@ -440,8 +435,10 @@
 #endif
 	sl->xleft = count - actual;
 	sl->xhead = sl->xbuff + actual;
+#ifdef CONFIG_SLIP_SMART
 	/* VSV */
 	clear_bit(SLF_OUTWAIT, &sl->flags);	/* reset outfill flag */
+#endif
 }
 
 /*
@@ -480,8 +477,14 @@
 
 	if (!dev->start)  {
 		printk("%s: xmit call when iface is down\n", dev->name);
-		return 1;
+		dev_kfree_skb(skb);
+		return 0;
 	}
+	if (sl->tty == NULL) {
+		dev_kfree_skb(skb);
+		return 0;
+	}
+
 	/*
 	 * If we are busy already- too bad.  We ought to be able
 	 * to queue things at this point, to allow for a little
@@ -524,107 +527,137 @@
 }
 
 
-/* Return the frame type ID.  This is normally IP but maybe be AX.25. */
+/******************************************
+ *   Routines looking at netdevice side.
+ ******************************************/
+
+/* Netdevice UP -> DOWN routine */
 
-/* Open the low-level part of the SLIP channel. Easy! */
 static int
-sl_open(struct device *dev)
+sl_close(struct device *dev)
 {
 	struct slip *sl = (struct slip*)(dev->priv);
-	unsigned long len;
 
-	if (sl->tty == NULL) {
-		return -ENODEV;
-	}
-
-	/*
-	 * Allocate the SLIP frame buffers:
-	 *
-	 * rbuff	Receive buffer.
-	 * xbuff	Transmit buffer.
-	 * cbuff        Temporary compression buffer.
-	 */
-	len = dev->mtu * 2;
-	/*
-	 * allow for arrival of larger UDP packets, even if we say not to
-	 * also fixes a bug in which SunOS sends 512-byte packets even with
-	 * an MSS of 128
-	 */
-	if (len < 576 * 2)  {
-		len = 576 * 2;
-	}
-	sl->rbuff = (unsigned char *) kmalloc(len + 4, GFP_KERNEL);
-	if (sl->rbuff == NULL)   {
-		goto norbuff;
-	}
-	sl->xbuff = (unsigned char *) kmalloc(len + 4, GFP_KERNEL);
-	if (sl->xbuff == NULL)   {
-		goto noxbuff;
-	}
-#ifdef SL_INCLUDE_CSLIP
-	sl->cbuff = (unsigned char *) kmalloc(len + 4, GFP_KERNEL);
-	if (sl->cbuff == NULL)   {
-		goto nocbuff;
-	}
-	sl->slcomp = slhc_init(16, 16);
-	if (sl->slcomp == NULL)  {
-		goto noslcomp;
+	start_bh_atomic();
+	if (sl->tty) {
+		/* TTY discipline is running. */
+		sl->tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
 	}
-#endif
-	sl->mtu	     = dev->mtu;
-	sl->buffsize = len;
+	dev->tbusy = 1;
+	dev->start = 0;
 	sl->rcount   = 0;
 	sl->xleft    = 0;
-#ifdef CONFIG_SLIP_MODE_SLIP6
-	sl->xdata    = 0;
-	sl->xbits    = 0;
-#endif
-	sl->flags   &= (1 << SLF_INUSE);      /* Clear ESCAPE & ERROR flags */
-#ifdef CONFIG_SLIP_SMART
-	sl->keepalive=0;		/* no keepalive by default = VSV */
-	init_timer(&sl->keepalive_timer);	/* initialize timer_list struct */
-	sl->keepalive_timer.data=(unsigned long)sl;
-	sl->keepalive_timer.function=sl_keepalive;
-	sl->outfill=0;			/* & outfill too */
-	init_timer(&sl->outfill_timer);
-	sl->outfill_timer.data=(unsigned long)sl;
-	sl->outfill_timer.function=sl_outfill;
-#endif
-	dev->tbusy  = 0;
-	dev->start  = 1;
+	end_bh_atomic();
 
+	MOD_DEC_USE_COUNT;
 	return 0;
+}
 
-	/* Cleanup */
+/* Netdevice DOWN -> UP routine */
+
+static int sl_open(struct device *dev)
+{
+	struct slip *sl = (struct slip*)(dev->priv);
+
+	if (sl->tty==NULL)
+		return -ENODEV;
+
+	sl->flags &= (1 << SLF_INUSE);
+	dev->start = 1;
+	dev->tbusy = 0;
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+/* Netdevice change MTU request */
+
+static int sl_change_mtu(struct device *dev, int new_mtu)
+{
+	struct slip *sl = (struct slip*)(dev->priv);
+
+	if (new_mtu < 68 || new_mtu > 65534)
+		return -EINVAL;
+
+	if (new_mtu != dev->mtu)
+		return sl_realloc_bufs(sl, new_mtu);
+	return 0;
+}
+
+/* Netdevice get statistics request */
+
+static struct net_device_stats *
+sl_get_stats(struct device *dev)
+{
+	static struct net_device_stats stats;
+	struct slip *sl = (struct slip*)(dev->priv);
 #ifdef SL_INCLUDE_CSLIP
-noslcomp:
-	kfree(sl->cbuff);
-nocbuff:
+	struct slcompress *comp;
 #endif
-	kfree(sl->xbuff);
-noxbuff:
-	kfree(sl->rbuff);
-norbuff:
-	return -ENOMEM;
+
+	memset(&stats, 0, sizeof(struct net_device_stats));
+
+	stats.rx_packets     = sl->rx_packets;
+	stats.tx_packets     = sl->tx_packets;
+	stats.rx_bytes	     = sl->rx_bytes;
+	stats.tx_bytes	     = sl->tx_bytes;
+	stats.rx_dropped     = sl->rx_dropped;
+	stats.tx_dropped     = sl->tx_dropped;
+	stats.tx_errors      = sl->tx_errors;
+	stats.rx_errors      = sl->rx_errors;
+	stats.rx_over_errors = sl->rx_over_errors;
+#ifdef SL_INCLUDE_CSLIP
+	stats.rx_fifo_errors = sl->rx_compressed;
+	stats.tx_fifo_errors = sl->tx_compressed;
+	stats.collisions     = sl->tx_misses;
+	comp = sl->slcomp;
+	if (comp) {
+		stats.rx_fifo_errors += comp->sls_i_compressed;
+		stats.rx_dropped     += comp->sls_i_tossed;
+		stats.tx_fifo_errors += comp->sls_o_compressed;
+		stats.collisions     += comp->sls_o_misses;
+	}
+#endif /* CONFIG_INET */
+	return (&stats);
 }
 
+/* Netdevice register callback */
 
-/* Close the low-level part of the SLIP channel. Easy! */
-static int
-sl_close(struct device *dev)
+static int sl_init(struct device *dev)
 {
 	struct slip *sl = (struct slip*)(dev->priv);
 
-	if (sl->tty == NULL) {
-		return -EBUSY;
-	}
-	sl->tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
-	dev->tbusy = 1;
-	dev->start = 0;
+	/*
+	 *	Finish setting up the DEVICE info. 
+	 */
+
+	dev->mtu		= sl->mtu;
+	dev->hard_start_xmit	= sl_xmit;
+	dev->open		= sl_open;
+	dev->stop		= sl_close;
+	dev->get_stats	        = sl_get_stats;
+	dev->change_mtu		= sl_change_mtu;
+#ifdef CONFIG_SLIP_SMART
+	dev->do_ioctl		= sl_ioctl;
+#endif
+	dev->hard_header_len	= 0;
+	dev->addr_len		= 0;
+	dev->type		= ARPHRD_SLIP + sl->mode;
+	dev->tx_queue_len	= 10;
+
+	dev_init_buffers(dev);
+
+	/* New-style flags. */
+	dev->flags		= IFF_NOARP|IFF_POINTOPOINT|IFF_MULTICAST;
 
 	return 0;
 }
 
+
+/******************************************
+  Routines looking at TTY side.
+ ******************************************/
+
+
 static int slip_receive_room(struct tty_struct *tty)
 {
 	return 65536;  /* We can handle an infinite amount of data. :-) */
@@ -644,15 +677,6 @@
 	if (!sl || sl->magic != SLIP_MAGIC || !sl->dev->start)
 		return;
 
-	/*
-	 * Argh! mtu change time! - costs us the packet part received
-	 * at the change
-	 */
-	if (sl->mtu != sl->dev->mtu)  {
-
-		sl_changedmtu(sl);
-	}
-
 	/* Read the characters out of the buffer */
 	while (count--) {
 		if (fp && *fp++) {
@@ -671,6 +695,127 @@
 	}
 }
 
+/************************************
+ *  slip_open helper routines.
+ ************************************/
+
+/* Collect hanged up channels */
+
+static void sl_sync(void)
+{
+	int i;
+
+	for (i = 0; i < slip_maxdev; i++) {
+		slip_ctrl_t *slp = slip_ctrls[i];
+		if (slp == NULL)
+			break;
+		if (slp->ctrl.tty || slp->ctrl.leased)
+			continue;
+		if (slp->dev.flags&IFF_UP)
+			dev_close(&slp->dev);
+	}
+}
+
+/* Find a free SLIP channel, and link in this `tty' line. */
+static struct slip *
+sl_alloc(kdev_t line)
+{
+	struct slip *sl;
+	slip_ctrl_t *slp = NULL;
+	int i;
+	int sel = -1;
+	int score = -1;
+
+	if (slip_ctrls == NULL)
+		return NULL;	/* Master array missing ! */
+
+	for (i = 0; i < slip_maxdev; i++) {
+		slp = slip_ctrls[i];
+		if (slp == NULL)
+			break;
+
+		if (slp->ctrl.leased) {
+			if (slp->ctrl.line != line)
+				continue;
+			if (slp->ctrl.tty)
+				return NULL;
+
+			/* Clear ESCAPE & ERROR flags */
+			slp->ctrl.flags &= (1 << SLF_INUSE);
+			return &slp->ctrl;
+		}
+
+		if (slp->ctrl.tty)
+			continue;
+
+		if (current->pid == slp->ctrl.pid) {
+			if (slp->ctrl.line == line && score < 3) {
+				sel = i;
+				score = 3;
+				continue;
+			}
+			if (score < 2) {
+				sel = i;
+				score = 2;
+			}
+			continue;
+		}
+		if (slp->ctrl.line == line && score < 1) {
+			sel = i;
+			score = 1;
+			continue;
+		}
+		if (score < 0) {
+			sel = i;
+			score = 0;
+		}
+	}
+
+	if (sel >= 0) {
+		i = sel;
+		slp = slip_ctrls[i];
+		if (score > 1) {
+			slp->ctrl.flags &= (1 << SLF_INUSE);
+			return &slp->ctrl;
+		}
+	}
+
+	/* Sorry, too many, all slots in use */
+	if (i >= slip_maxdev)
+		return NULL;
+
+	if (slp) {
+		if (test_bit(SLF_INUSE, &slp->ctrl.flags)) {
+			unregister_netdevice(&slp->dev);
+			sl_free_bufs(&slp->ctrl);
+		}
+	} else if ((slp = (slip_ctrl_t *)kmalloc(sizeof(slip_ctrl_t),GFP_KERNEL)) == NULL)
+		return NULL;
+
+	memset(slp, 0, sizeof(slip_ctrl_t));
+
+	sl = &slp->ctrl;
+	/* Initialize channel control data */
+	sl->magic       = SLIP_MAGIC;
+	sl->dev	      	= &slp->dev;
+	sl->mode        = SL_MODE_DEFAULT;
+	sprintf(slp->if_name, "sl%d", i);
+	slp->dev.name         = slp->if_name;
+	slp->dev.base_addr    = i;
+	slp->dev.priv         = (void*)sl;
+	slp->dev.init         = sl_init;
+#ifdef CONFIG_SLIP_SMART
+	init_timer(&sl->keepalive_timer);	/* initialize timer_list struct */
+	sl->keepalive_timer.data=(unsigned long)sl;
+	sl->keepalive_timer.function=sl_keepalive;
+	init_timer(&sl->outfill_timer);
+	sl->outfill_timer.data=(unsigned long)sl;
+	sl->outfill_timer.function=sl_outfill;
+#endif
+	slip_ctrls[i] 	      = slp;
+	return &slp->ctrl;
+}
+
 /*
  * Open the high-level part of the SLIP channel.
  * This function is called by the TTY module when the
@@ -681,42 +826,98 @@
 static int
 slip_open(struct tty_struct *tty)
 {
-	struct slip *sl = (struct slip *) tty->disc_data;
+	struct slip *sl;
 	int err;
 
+	MOD_INC_USE_COUNT;
+
+	/* RTnetlink lock is misused here to serialize concurrent
+	   opens of slip channels. There are better ways, but it is
+	   the simplest one.
+	 */
+	rtnl_lock();
+
+	/* Collect hanged up channels. */
+	sl_sync();
+
+	sl = (struct slip *) tty->disc_data;
+
+	err = -EEXIST;
 	/* First make sure we're not already connected. */
-	if (sl && sl->magic == SLIP_MAGIC) {
-		return -EEXIST;
-	}
+	if (sl && sl->magic == SLIP_MAGIC)
+		goto err_exit;
 
 	/* OK.  Find a free SLIP channel to use. */
-	if ((sl = sl_alloc()) == NULL) {
-		return -ENFILE;
-	}
+	err = -ENFILE;
+	if ((sl = sl_alloc(tty->device)) == NULL)
+		goto err_exit;
 
 	sl->tty = tty;
 	tty->disc_data = sl;
-	if (tty->driver.flush_buffer)  {
+	sl->line = tty->device;
+	sl->pid = current->pid;
+	if (tty->driver.flush_buffer)
 		tty->driver.flush_buffer(tty);
-	}
-	if (tty->ldisc.flush_buffer)  {
+	if (tty->ldisc.flush_buffer)
 		tty->ldisc.flush_buffer(tty);
-	}
 
-	/* Restore default settings */
-	sl->mode      = SL_MODE_DEFAULT;
-	sl->dev->type = ARPHRD_SLIP + sl->mode;
-	/* Perform the low-level SLIP initialization. */
-	if ((err = sl_open(sl->dev)))  {
-		return err;
+	if (!test_bit(SLF_INUSE, &sl->flags)) {
+		/* Perform the low-level SLIP initialization. */
+		if ((err = sl_alloc_bufs(sl, SL_MTU)) != 0)
+			goto err_free_chan;
+
+		if (register_netdevice(sl->dev)) {
+			sl_free_bufs(sl);
+			goto err_free_chan;
+		}
+
+		set_bit(SLF_INUSE, &sl->flags);
 	}
 
-	MOD_INC_USE_COUNT;
+#ifdef CONFIG_SLIP_SMART
+	if (sl->keepalive) {
+		sl->keepalive_timer.expires=jiffies+sl->keepalive*HZ;
+		add_timer (&sl->keepalive_timer);
+	}
+	if (sl->outfill) {
+		sl->outfill_timer.expires=jiffies+sl->outfill*HZ;
+		add_timer (&sl->outfill_timer);
+	}
+#endif
 
 	/* Done.  We have linked the TTY line to a channel. */
+	rtnl_unlock();
 	return sl->dev->base_addr;
+
+err_free_chan:
+	sl->tty = NULL;
+	tty->disc_data = NULL;
+	clear_bit(SLF_INUSE, &sl->flags);
+
+err_exit:
+	rtnl_unlock();
+
+	/* Count references from TTY module */
+	MOD_DEC_USE_COUNT;
+	return err;
 }
 
+/*
+   Let me to blame a bit.
+   1. TTY module calls this funstion on soft interrupt.
+   2. TTY module calls this function WITH MASKED INTERRUPTS!
+   3. TTY module does not notify us about line discipline
+      shutdown,
+
+   Seems, now it is clean. The solution is to consider netdevice and
+   line discipline sides as two independant threads.
+
+   By-product (not desired): sl? does not feel hangups and remains open.
+   It is supposed, that user level program (dip, diald, slattach...)
+   will catch SIGHUP and make the rest of work. 
+
+   I see no way to make more with current tty code. --ANK
+ */
 
 /*
  * Close down a SLIP channel.
@@ -730,69 +931,26 @@
 	struct slip *sl = (struct slip *) tty->disc_data;
 
 	/* First make sure we're connected. */
-	if (!sl || sl->magic != SLIP_MAGIC) {
+	if (!sl || sl->magic != SLIP_MAGIC || sl->tty != tty)
 		return;
-	}
-
-	rtnl_lock();
-	if (sl->dev->flags & IFF_UP)
-	{
-		/* STRONG layering violation! --ANK */
-		(void) dev_close(sl->dev);
-	}
 
 	tty->disc_data = 0;
 	sl->tty = NULL;
+	if (!sl->leased)
+		sl->line = 0;
+
 	/* VSV = very important to remove timers */
 #ifdef CONFIG_SLIP_SMART
 	if (sl->keepalive)
-		(void)del_timer (&sl->keepalive_timer);
+		del_timer (&sl->keepalive_timer);
 	if (sl->outfill)
-		(void)del_timer (&sl->outfill_timer);
-#endif
-	sl_free(sl);
-	unregister_netdevice(sl->dev);
-	rtnl_unlock();
-	MOD_DEC_USE_COUNT;
-}
-
-
-static struct net_device_stats *
-sl_get_stats(struct device *dev)
-{
-	static struct net_device_stats stats;
-	struct slip *sl = (struct slip*)(dev->priv);
-#ifdef SL_INCLUDE_CSLIP
-	struct slcompress *comp;
+		del_timer (&sl->outfill_timer);
 #endif
 
-	memset(&stats, 0, sizeof(struct net_device_stats));
-
-	stats.rx_packets     = sl->rx_packets;
-	stats.tx_packets     = sl->tx_packets;
-	stats.rx_bytes	     = sl->rx_bytes;
-	stats.tx_bytes	     = sl->tx_bytes;
-	stats.rx_dropped     = sl->rx_dropped;
-	stats.tx_dropped     = sl->tx_dropped;
-	stats.tx_errors      = sl->tx_errors;
-	stats.rx_errors      = sl->rx_errors;
-	stats.rx_over_errors = sl->rx_over_errors;
-#ifdef SL_INCLUDE_CSLIP
-	stats.rx_fifo_errors = sl->rx_compressed;
-	stats.tx_fifo_errors = sl->tx_compressed;
-	stats.collisions     = sl->tx_misses;
-	comp = sl->slcomp;
-	if (comp) {
-		stats.rx_fifo_errors += comp->sls_i_compressed;
-		stats.rx_dropped     += comp->sls_i_tossed;
-		stats.tx_fifo_errors += comp->sls_o_compressed;
-		stats.collisions     += comp->sls_o_misses;
-	}
-#endif /* CONFIG_INET */
-	return (&stats);
+	/* Count references from TTY module */
+	MOD_DEC_USE_COUNT;
 }
 
-
  /************************************************************************
   *			STANDARD SLIP ENCAPSULATION		  	 *
   ************************************************************************/
@@ -840,9 +998,11 @@
 
 	switch(s) {
 	 case END:
+#ifdef CONFIG_SLIP_SMART
 		/* drop keeptest bit = VSV */
 		if (test_bit(SLF_KEEPTEST, &sl->flags))
 			clear_bit(SLF_KEEPTEST, &sl->flags);
+#endif
 
 		if (!test_and_clear_bit(SLF_ERROR, &sl->flags) && (sl->rcount > 2))  {
 			sl_bump(sl);
@@ -925,9 +1085,11 @@
 	unsigned char c;
 
 	if (s == 0x70) {
+#ifdef CONFIG_SLIP_SMART
 		/* drop keeptest bit = VSV */
 		if (test_bit(SLF_KEEPTEST, &sl->flags))
 			clear_bit(SLF_KEEPTEST, &sl->flags);
+#endif
 
 		if (!test_and_clear_bit(SLF_ERROR, &sl->flags) && (sl->rcount > 2))  {
 			sl_bump(sl);
@@ -959,7 +1121,6 @@
 slip_ioctl(struct tty_struct *tty, void *file, int cmd, void *arg)
 {
 	struct slip *sl = (struct slip *) tty->disc_data;
-	int err;
 	unsigned int tmp;
 
 	/* First make sure we're connected. */
@@ -978,19 +1139,13 @@
 		return 0;
 
 	case SIOCGIFENCAP:
-		err = verify_area(VERIFY_WRITE, arg, sizeof(int));
-		if (err)  {
-			return err;
-		}
-		put_user(sl->mode, (int *)arg);
+		if (put_user(sl->mode, (int *)arg))
+			return -EFAULT;
 		return 0;
 
 	case SIOCSIFENCAP:
-		err = verify_area(VERIFY_READ, arg, sizeof(int));
-		if (err)  {
-			return err;
-		}
-		get_user(tmp,(int *)arg);
+		if (get_user(tmp,(int *)arg))
+			return -EFAULT;
 #ifndef SL_INCLUDE_CSLIP
 		if (tmp & (SL_MODE_CSLIP|SL_MODE_ADAPTIVE))  {
 			return -EINVAL;
@@ -1017,13 +1172,16 @@
 #ifdef CONFIG_SLIP_SMART
 	/* VSV changes start here */
         case SIOCSKEEPALIVE:
-		err = verify_area(VERIFY_READ, arg, sizeof(int));
-		if (err)  {
-			return -err;
-		}
-		get_user(tmp,(int *)arg);
+		if (get_user(tmp,(int *)arg))
+			return -EFAULT;
                 if (tmp > 255) /* max for unchar */
 			return -EINVAL;
+
+		start_bh_atomic();
+		if (!sl->tty) {
+			end_bh_atomic();
+			return -ENODEV;
+		}
                 if (sl->keepalive)
                         (void)del_timer (&sl->keepalive_timer);
 		if ((sl->keepalive = (unchar) tmp) != 0) {
@@ -1031,24 +1189,25 @@
 			add_timer(&sl->keepalive_timer);
 			set_bit(SLF_KEEPTEST, &sl->flags);
                 }
+	        end_bh_atomic();
+	
 		return 0;
 
         case SIOCGKEEPALIVE:
-		err = verify_area(VERIFY_WRITE, arg, sizeof(int));
-		if (err)  {
-			return -err;
-		}
-		put_user(sl->keepalive, (int *)arg);
+		if (put_user(sl->keepalive, (int *)arg))
+			return -EFAULT;
 		return 0;
 
         case SIOCSOUTFILL:
-		err = verify_area(VERIFY_READ, arg, sizeof(int));
-		if (err)  {
-			return -err;
-		}
-		get_user(tmp,(int *)arg);
+		if (get_user(tmp,(int *)arg))
+			return -EFAULT;
                 if (tmp > 255) /* max for unchar */
 			return -EINVAL;
+		start_bh_atomic();
+		if (!sl->tty) {
+			end_bh_atomic();
+			return -ENODEV;
+		}
                 if (sl->outfill)
                          (void)del_timer (&sl->outfill_timer);
                 if ((sl->outfill = (unchar) tmp) != 0){
@@ -1056,14 +1215,12 @@
 			add_timer(&sl->outfill_timer);
 			set_bit(SLF_OUTWAIT, &sl->flags);
 		}
+		end_bh_atomic();
                 return 0;
 
         case SIOCGOUTFILL:
-		err = verify_area(VERIFY_WRITE, arg, sizeof(int));
-		if (err)  {
-			return -err;
-		}
-		put_user(sl->outfill, (int *)arg);
+		if (put_user(sl->outfill, (int *)arg))
+			return -EFAULT;
 		return 0;
 	/* VSV changes end */
 #endif
@@ -1091,6 +1248,13 @@
 	if (sl == NULL)		/* Allocation failed ?? */
 		return -ENODEV;
 
+	start_bh_atomic(); /* Hangup would kill us */
+
+	if (!sl->tty) {
+		end_bh_atomic();
+		return -ENODEV;
+	}
+
 	switch(cmd){
         case SIOCSKEEPALIVE:
 		/* max for unchar */
@@ -1124,20 +1288,30 @@
 
         case SIOCGOUTFILL:
 		rq->ifr_data=(caddr_t)((unsigned long)sl->outfill);
+		break;
+
+        case SIOCSLEASE:
+		/* Resolve race condition, when ioctl'ing hanged up 
+		   and opened by another process device.
+		 */
+		if (sl->tty != current->tty && sl->pid != current->pid) {
+			end_bh_atomic();
+			return -EPERM;
+		}
+		sl->leased = 0;
+                if ((unsigned long)rq->ifr_data)
+			sl->leased = 1;
+                break;
+
+        case SIOCGLEASE:
+		rq->ifr_data=(caddr_t)((unsigned long)sl->leased);
 	};
+	end_bh_atomic();
 	return 0;
 }
 #endif
 /* VSV changes end */
 
-static int sl_open_dev(struct device *dev)
-{
-	struct slip *sl = (struct slip*)(dev->priv);
-	if(sl->tty==NULL)
-		return -ENODEV;
-	return 0;
-}
-
 /* Initialize SLIP control device -- register SLIP line discipline */
 #ifdef MODULE
 static int slip_init_ctrl_dev(void)
@@ -1203,45 +1377,7 @@
       }
 
 
-/* Initialise the SLIP driver.  Called by the device init code */
-
-int slip_init(struct device *dev)
-{
-	struct slip *sl = (struct slip*)(dev->priv);
-
-	if (sl == NULL)		/* Allocation failed ?? */
-	  return -ENODEV;
-
-	/* Set up the "SLIP Control Block". (And clear statistics) */
 
-	memset(sl, 0, sizeof (struct slip));
-	sl->magic  = SLIP_MAGIC;
-	sl->dev	   = dev;
-
-	/*
-	 *	Finish setting up the DEVICE info. 
-	 */
-	 
-	dev->mtu		= SL_MTU;
-	dev->hard_start_xmit	= sl_xmit;
-	dev->open		= sl_open_dev;
-	dev->stop		= sl_close;
-	dev->get_stats	        = sl_get_stats;
-#ifdef CONFIG_SLIP_SMART
-	dev->do_ioctl		= sl_ioctl;
-#endif
-	dev->hard_header_len	= 0;
-	dev->addr_len		= 0;
-	dev->type		= ARPHRD_SLIP + SL_MODE_DEFAULT;
-	dev->tx_queue_len	= 10;
-
-	dev_init_buffers(dev);
-	
-	/* New-style flags. */
-	dev->flags		= IFF_NOARP|IFF_POINTOPOINT|IFF_MULTICAST;
-
-	return 0;
-}
 #ifdef MODULE
 
 int
@@ -1255,25 +1391,51 @@
 {
 	int i;
 
-	if (slip_ctrls != NULL)
-	{
-		for (i = 0; i < slip_maxdev; i++)
-		{
-			if (slip_ctrls[i])
-			{
-				/*
-				 * VSV = if dev->start==0, then device
-				 * unregistered while close proc.
-				 */
-				if (slip_ctrls[i]->dev.start)
-					unregister_netdev(&(slip_ctrls[i]->dev));
+	if (slip_ctrls != NULL) {
+		unsigned long start = jiffies;
+		int busy = 0;
+
+		/* First of all: check for active disciplines and hangup them.
+		 */
+		do {
+			if (busy) {
+				current->counter = 0;
+				schedule();
+			}
+
+			busy = 0;
+			start_bh_atomic();
+			for (i = 0; i < slip_maxdev; i++) {
+				struct slip_ctrl *slc = slip_ctrls[i];
+				if (slc && slc->ctrl.tty) {
+					busy++;
+					tty_hangup(slc->ctrl.tty);
+				}
+			}
+			end_bh_atomic();
+		} while (busy && jiffies - start < 1*HZ);
 
-				kfree(slip_ctrls[i]);
+		busy = 0;
+		for (i = 0; i < slip_maxdev; i++) {
+			struct slip_ctrl *slc = slip_ctrls[i];
+			if (slc) {
+				unregister_netdev(&slc->dev);
+				if (slc->ctrl.tty) {
+					printk("%s: tty discipline is still running\n", slc->dev.name);
+					/* Pin module forever */
+					MOD_INC_USE_COUNT;
+					busy++;
+					continue;
+				}
+				sl_free_bufs(&slc->ctrl);
+				kfree(slc);
 				slip_ctrls[i] = NULL;
 			}
 		}
-		kfree(slip_ctrls);
-		slip_ctrls = NULL;
+		if (!busy) {
+			kfree(slip_ctrls);
+			slip_ctrls = NULL;
+		}
 	}
 	if ((i = tty_register_ldisc(N_SLIP, NULL)))
 	{
@@ -1292,7 +1454,7 @@
 {
 	struct slip *sl=(struct slip *)sls;
 
-	if(sls==0L)
+	if (sl==NULL || sl->tty == NULL)
 		return;
 
 	if(sl->outfill)
@@ -1318,15 +1480,13 @@
 		sl->outfill_timer.expires=jiffies+sl->outfill*HZ;
 		add_timer(&sl->outfill_timer);
 	}
-	else
-		del_timer(&sl->outfill_timer);
 }
 
 static void sl_keepalive(unsigned long sls)
 {
 	struct slip *sl=(struct slip *)sls;
 
-	if(sl == NULL)
+	if (sl == NULL || sl->tty == NULL)
 		return;
 
 	if( sl->keepalive)
@@ -1334,7 +1494,6 @@
 		if(test_bit(SLF_KEEPTEST, &sl->flags))
 		{
 			/* keepalive still high :(, we must hangup */
-			(void)del_timer(&sl->keepalive_timer);
 			if( sl->outfill ) /* outfill timer must be deleted too */
 				(void)del_timer(&sl->outfill_timer);
 			printk("%s: no packets received during keepalive timeout, hangup.\n", sl->dev->name);
@@ -1344,12 +1503,9 @@
 		}
 		else
 			set_bit(SLF_KEEPTEST, &sl->flags);
-		(void)del_timer(&sl->keepalive_timer);
-		 sl->keepalive_timer.expires=jiffies+sl->keepalive*HZ;
+		sl->keepalive_timer.expires=jiffies+sl->keepalive*HZ;
 		add_timer(&sl->keepalive_timer);
 	}
-	else
-		(void)del_timer(&sl->keepalive_timer);
 }
 
 #endif
diff -u --recursive --new-file v2.1.88/linux/drivers/net/slip.h linux/drivers/net/slip.h
--- v2.1.88/linux/drivers/net/slip.h	Sun Feb  2 05:18:42 1997
+++ linux/drivers/net/slip.h	Sun Mar  1 14:40:39 1998
@@ -97,6 +97,9 @@
 #define SLF_OUTWAIT	4		/* is outpacket was flag	*/
 
   unsigned char		mode;		/* SLIP mode			*/
+  unsigned char		leased;
+  kdev_t		line;
+  pid_t			pid;
 #define SL_MODE_SLIP	0
 #define SL_MODE_CSLIP	1
 #define SL_MODE_SLIP6	2		/* Matt Dillon's printable slip */
diff -u --recursive --new-file v2.1.88/linux/drivers/net/smc-ultra32.c linux/drivers/net/smc-ultra32.c
--- v2.1.88/linux/drivers/net/smc-ultra32.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/smc-ultra32.c	Thu Feb 26 11:01:24 1998
@@ -0,0 +1,413 @@
+/* 	smc-ultra32.c: An SMC Ultra32 EISA ethernet driver for linux.
+
+Sources:
+
+	This driver is based on (cloned from) the ISA SMC Ultra driver
+	written by Donald Becker. Modifications to support the EISA
+	version of the card by Paul Gortmaker and Leonard N. Zubkoff.
+
+	This software may be used and distributed according to the terms
+	of the GNU Public License, incorporated herein by reference.
+
+Theory of Operation:
+
+	The SMC Ultra32C card uses the SMC 83c790 chip which is also
+	found on the ISA SMC Ultra cards. It has a shared memory mode of
+	operation that makes it similar to the ISA version of the card.
+	The main difference is that the EISA card has 32KB of RAM, but
+	only an 8KB window into that memory. The EISA card also can be
+	set for a bus-mastering mode of operation via the ECU, but that
+	is not (and probably will never be) supported by this driver.
+	The ECU should be run to enable shared memory and to disable the
+	bus-mastering feature for use with linux.
+
+	By programming the 8390 to use only 8KB RAM, the modifications
+	to the ISA driver can be limited to the probe and initialization
+	code. This allows easy integration of EISA support into the ISA
+	driver. However, the driver development kit from SMC provided the
+	register information for sliding the 8KB window, and hence the 8390
+	is programmed to use the full 32KB RAM.
+
+	Unfortunately this required code changes outside the probe/init
+	routines, and thus we decided to separate the EISA driver from
+	the ISA one. In this way, ISA users don't end up with a larger
+	driver due to the EISA code, and EISA users don't end up with a
+	larger driver due to the ISA EtherEZ PIO code. The driver is
+	similar to the 3c503/16 driver, in that the window must be set
+	back to the 1st 8KB of space for access to the two 8390 Tx slots.
+
+	In testing, using only 8KB RAM (3 Tx / 5 Rx) didn't appear to
+	be a limiting factor, since the EISA bus could get packets off
+	the card fast enough, but having the use of lots of RAM as Rx
+	space is extra insurance if interrupt latencies become excessive.
+
+*/
+
+static const char *version = "smc-ultra32.c: 06/97 v1.00\n";
+
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/init.h>
+
+#include <asm/io.h>
+#include <asm/system.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include "8390.h"
+
+int ultra32_probe(struct device *dev);
+int ultra32_probe1(struct device *dev, int ioaddr);
+static int ultra32_open(struct device *dev);
+static void ultra32_reset_8390(struct device *dev);
+static void ultra32_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr,
+				 int ring_page);
+static void ultra32_block_input(struct device *dev, int count,
+				struct sk_buff *skb, int ring_offset);
+static void ultra32_block_output(struct device *dev, int count,
+				 const unsigned char *buf, const start_page);
+static int ultra32_close(struct device *dev);
+
+#define ULTRA32_CMDREG	0	/* Offset to ASIC command register. */
+#define	 ULTRA32_RESET	0x80	/* Board reset, in ULTRA32_CMDREG. */
+#define	 ULTRA32_MEMENB	0x40	/* Enable the shared memory. */
+#define ULTRA32_NIC_OFFSET 16	/* NIC register offset from the base_addr. */
+#define ULTRA32_IO_EXTENT 32
+#define EN0_ERWCNT		0x08	/* Early receive warning count. */
+
+/*
+ * Defines that apply only to the Ultra32 EISA card. Note that
+ * "smc" = 10011 01101 00011 = 0x4da3, and hence !smc8010.cfg translates
+ * into an EISA ID of 0x1080A34D
+ */
+#define ULTRA32_BASE	0xca0
+#define ULTRA32_ID	0x1080a34d
+#define ULTRA32_IDPORT	(-0x20)	/* 0xc80 */
+/* Config regs 1->7 from the EISA !SMC8010.CFG file. */
+#define ULTRA32_CFG1	0x04	/* 0xca4 */
+#define ULTRA32_CFG2	0x05	/* 0xca5 */
+#define ULTRA32_CFG3	(-0x18)	/* 0xc88 */
+#define ULTRA32_CFG4	(-0x17)	/* 0xc89 */
+#define ULTRA32_CFG5	(-0x16)	/* 0xc8a */
+#define ULTRA32_CFG6	(-0x15)	/* 0xc8b */
+#define ULTRA32_CFG7	0x0d	/* 0xcad */
+
+
+/*	Probe for the Ultra32.  This looks like a 8013 with the station
+	address PROM at I/O ports <base>+8 to <base>+13, with a checksum
+	following.
+*/
+
+__initfunc(int ultra32_probe(struct device *dev))
+{
+	const char *ifmap[] = {"UTP No Link", "", "UTP/AUI", "UTP/BNC"};
+	int ioaddr, edge, media;
+
+	if (!EISA_bus) return ENODEV;
+
+	/* EISA spec allows for up to 16 slots, but 8 is typical. */
+	for (ioaddr = 0x1000 + ULTRA32_BASE; ioaddr < 0x9000; ioaddr += 0x1000)
+	if (check_region(ioaddr, ULTRA32_IO_EXTENT) == 0 &&
+	    inb(ioaddr + ULTRA32_IDPORT) != 0xff &&
+	    inl(ioaddr + ULTRA32_IDPORT) == ULTRA32_ID) {
+		media = inb(ioaddr + ULTRA32_CFG7) & 0x03;
+		edge = inb(ioaddr + ULTRA32_CFG5) & 0x08;
+		printk("SMC Ultra32 in EISA Slot %d, Media: %s, %s IRQs.\n",
+		       ioaddr >> 12, ifmap[media],
+		       (edge ? "Edge Triggered" : "Level Sensitive"));
+		if (ultra32_probe1(dev, ioaddr) == 0)
+		  return 0;
+	}
+	return ENODEV;
+}
+
+__initfunc(int ultra32_probe1(struct device *dev, int ioaddr))
+{
+	int i;
+	int checksum = 0;
+	const char *model_name;
+	static unsigned version_printed = 0;
+	/* Values from various config regs. */
+	unsigned char idreg = inb(ioaddr + 7);
+	unsigned char reg4 = inb(ioaddr + 4) & 0x7f;
+
+	/* Check the ID nibble. */
+	if ((idreg & 0xf0) != 0x20) 			/* SMC Ultra */
+		return ENODEV;
+
+	/* Select the station address register set. */
+	outb(reg4, ioaddr + 4);
+
+	for (i = 0; i < 8; i++)
+		checksum += inb(ioaddr + 8 + i);
+	if ((checksum & 0xff) != 0xff)
+		return ENODEV;
+
+	/* We should have a "dev" from Space.c or the static module table. */
+	if (dev == NULL) {
+		printk("smc-ultra32.c: Passed a NULL device.\n");
+		dev = init_etherdev(0, 0);
+	}
+
+	if (ei_debug  &&  version_printed++ == 0)
+		printk(version);
+
+	model_name = "SMC Ultra32";
+
+	printk("%s: %s at 0x%X,", dev->name, model_name, ioaddr);
+
+	for (i = 0; i < 6; i++)
+		printk(" %2.2X", dev->dev_addr[i] = inb(ioaddr + 8 + i));
+
+	/* Switch from the station address to the alternate register set and
+	   read the useful registers there. */
+	outb(0x80 | reg4, ioaddr + 4);
+
+	/* Enable FINE16 mode to avoid BIOS ROM width mismatches @ reboot. */
+	outb(0x80 | inb(ioaddr + 0x0c), ioaddr + 0x0c);
+
+	/* Reset RAM addr. */
+	outb(0x00, ioaddr + 0x0b);
+
+	/* Switch back to the station address register set so that the
+	   MS-DOS driver can find the card after a warm boot. */
+	outb(reg4, ioaddr + 4);
+
+	if ((inb(ioaddr + ULTRA32_CFG5) & 0x40) == 0) {
+		printk("\nsmc-ultra32: Card RAM is disabled!  "
+		       "Run EISA config utility.\n");
+		return ENODEV;
+	}
+	if ((inb(ioaddr + ULTRA32_CFG2) & 0x04) == 0)
+		printk("\nsmc-ultra32: Ignoring Bus-Master enable bit.  "
+		       "Run EISA config utility.\n");
+
+	if (dev->irq < 2) {
+		unsigned char irqmap[] = {0, 9, 3, 5, 7, 10, 11, 15};
+		int irq = irqmap[inb(ioaddr + ULTRA32_CFG5) & 0x07];
+		if (irq == 0) {
+			printk(", failed to detect IRQ line.\n");
+			return -EAGAIN;
+		}
+		dev->irq = irq;
+	}
+
+	/* Allocate dev->priv and fill in 8390 specific dev fields. */
+	if (ethdev_init(dev)) {
+		printk (", no memory for dev->priv.\n");
+                return -ENOMEM;
+        }
+
+	/* OK, we are certain this is going to work.  Setup the device. */
+	request_region(ioaddr, ULTRA32_IO_EXTENT, model_name);
+
+	/* The 8390 isn't at the base address, so fake the offset */
+	dev->base_addr = ioaddr + ULTRA32_NIC_OFFSET;
+
+	/* Save RAM address in the unused reg0 to avoid excess inb's. */
+	ei_status.reg0 = inb(ioaddr + ULTRA32_CFG3) & 0xfc;
+
+	dev->mem_start =  0xc0000 + ((ei_status.reg0 & 0x7c) << 11);
+
+	ei_status.name = model_name;
+	ei_status.word16 = 1;
+	ei_status.tx_start_page = 0;
+	ei_status.rx_start_page = TX_PAGES;
+	/* All Ultra32 cards have 32KB memory with an 8KB window. */
+	ei_status.stop_page = 128;
+
+	dev->rmem_start = dev->mem_start + TX_PAGES*256;
+	dev->mem_end = dev->rmem_end = dev->mem_start + 0x1fff;
+
+	printk(", IRQ %d, 32KB memory, 8KB window at 0x%lx-0x%lx.\n",
+	       dev->irq, dev->mem_start, dev->mem_end);
+	ei_status.block_input = &ultra32_block_input;
+	ei_status.block_output = &ultra32_block_output;
+	ei_status.get_8390_hdr = &ultra32_get_8390_hdr;
+	ei_status.reset_8390 = &ultra32_reset_8390;
+	dev->open = &ultra32_open;
+	dev->stop = &ultra32_close;
+	NS8390_init(dev, 0);
+
+	return 0;
+}
+
+static int ultra32_open(struct device *dev)
+{
+	int ioaddr = dev->base_addr - ULTRA32_NIC_OFFSET; /* ASIC addr */
+
+	if (request_irq(dev->irq, ei_interrupt, 0, ei_status.name, dev))
+		return -EAGAIN;
+
+	outb(ULTRA32_MEMENB, ioaddr); /* Enable Shared Memory. */
+	outb(0x80, ioaddr + ULTRA32_CFG6); /* Enable Interrupts. */
+	outb(0x84, ioaddr + 5);	/* Enable MEM16 & Disable Bus Master. */
+	outb(0x01, ioaddr + 6);	/* Enable Interrupts. */
+	/* Set the early receive warning level in window 0 high enough not
+	   to receive ERW interrupts. */
+	outb_p(E8390_NODMA+E8390_PAGE0, dev->base_addr);
+	outb(0xff, dev->base_addr + EN0_ERWCNT);
+	ei_open(dev);
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static int ultra32_close(struct device *dev)
+{
+	int ioaddr = dev->base_addr - ULTRA32_NIC_OFFSET; /* CMDREG */
+
+	dev->start = 0;
+	dev->tbusy = 1;
+
+	if (ei_debug > 1)
+		printk("%s: Shutting down ethercard.\n", dev->name);
+
+	outb(0x00, ioaddr + ULTRA32_CFG6); /* Disable Interrupts. */
+	outb(0x00, ioaddr + 6);		/* Disable interrupts. */
+	free_irq(dev->irq, dev);
+
+	NS8390_init(dev, 0);
+
+	MOD_DEC_USE_COUNT;
+
+	return 0;
+}
+
+static void ultra32_reset_8390(struct device *dev)
+{
+	int ioaddr = dev->base_addr - ULTRA32_NIC_OFFSET; /* ASIC base addr */
+
+	outb(ULTRA32_RESET, ioaddr);
+	if (ei_debug > 1) printk("resetting Ultra32, t=%ld...", jiffies);
+	ei_status.txing = 0;
+
+	outb(ULTRA32_MEMENB, ioaddr); /* Enable Shared Memory. */
+	outb(0x80, ioaddr + ULTRA32_CFG6); /* Enable Interrupts. */
+	outb(0x84, ioaddr + 5);	/* Enable MEM16 & Disable Bus Master. */
+	outb(0x01, ioaddr + 6);	/* Enable Interrupts. */
+	if (ei_debug > 1) printk("reset done\n");
+	return;
+}
+
+/* Grab the 8390 specific header. Similar to the block_input routine, but
+   we don't need to be concerned with ring wrap as the header will be at
+   the start of a page, so we optimize accordingly. */
+
+static void ultra32_get_8390_hdr(struct device *dev,
+				 struct e8390_pkt_hdr *hdr,
+				 int ring_page)
+{
+	unsigned long hdr_start = dev->mem_start + ((ring_page & 0x1f) << 8);
+	unsigned int RamReg = dev->base_addr - ULTRA32_NIC_OFFSET + ULTRA32_CFG3;
+
+	/* Select correct 8KB Window. */
+	outb(ei_status.reg0 | ((ring_page & 0x60) >> 5), RamReg);
+
+#ifdef notdef
+	/* Officially this is what we are doing, but the readl() is faster */
+	memcpy_fromio(hdr, hdr_start, sizeof(struct e8390_pkt_hdr));
+#else
+	((unsigned int*)hdr)[0] = readl(hdr_start);
+#endif
+}
+
+/* Block input and output are easy on shared memory ethercards, the only
+   complication is when the ring buffer wraps, or in this case, when a
+   packet spans an 8KB boundary. Note that the current 8KB segment is
+   already set by the get_8390_hdr routine. */
+
+static void ultra32_block_input(struct device *dev,
+				int count,
+				struct sk_buff *skb,
+				int ring_offset)
+{
+	unsigned long xfer_start = dev->mem_start + (ring_offset & 0x1fff);
+	unsigned int RamReg = dev->base_addr - ULTRA32_NIC_OFFSET + ULTRA32_CFG3;
+
+	if ((ring_offset & ~0x1fff) != ((ring_offset + count - 1) & ~0x1fff)) {
+		int semi_count = 8192 - (ring_offset & 0x1FFF);
+		memcpy_fromio(skb->data, xfer_start, semi_count);
+		count -= semi_count;
+		if (ring_offset < 96*256) {
+			/* Select next 8KB Window. */
+			ring_offset += semi_count;
+			outb(ei_status.reg0 | ((ring_offset & 0x6000) >> 13), RamReg);
+			memcpy_fromio(skb->data + semi_count, dev->mem_start, count);
+		} else {
+			/* Select first 8KB Window. */
+			outb(ei_status.reg0, RamReg);
+			memcpy_fromio(skb->data + semi_count, dev->rmem_start, count);
+		}
+	} else {
+		/* Packet is in one chunk -- we can copy + cksum. */
+		eth_io_copy_and_sum(skb, xfer_start, count, 0);
+	}
+}
+
+static void ultra32_block_output(struct device *dev,
+				 int count,
+				 const unsigned char *buf,
+				 int start_page)
+{
+	unsigned long xfer_start = dev->mem_start + (start_page<<8);
+	unsigned int RamReg = dev->base_addr - ULTRA32_NIC_OFFSET + ULTRA32_CFG3;
+
+	/* Select first 8KB Window. */
+	outb(ei_status.reg0, RamReg);
+
+	memcpy_toio(xfer_start, buf, count);
+}
+
+#ifdef MODULE
+#define MAX_ULTRA32_CARDS   4	/* Max number of Ultra cards per module */
+#define NAMELEN		    8	/* # of chars for storing dev->name */
+static char namelist[NAMELEN * MAX_ULTRA32_CARDS] = { 0, };
+static struct device dev_ultra[MAX_ULTRA32_CARDS] = {
+	{
+		NULL,		/* assign a chunk of namelist[] below */
+		0, 0, 0, 0,
+		0, 0,
+		0, 0, 0, NULL, NULL
+	},
+};
+
+int init_module(void)
+{
+	int this_dev, found = 0;
+
+	for (this_dev = 0; this_dev < MAX_ULTRA32_CARDS; this_dev++) {
+		struct device *dev = &dev_ultra[this_dev];
+		dev->name = namelist+(NAMELEN*this_dev);
+		dev->init = ultra32_probe;
+		if (register_netdev(dev) != 0) {
+			if (found > 0) return 0; /* Got at least one. */
+			printk(KERN_WARNING "smc-ultra32.c: No SMC Ultra32 found.\n");
+			return -ENXIO;
+		}
+		found++;
+	}
+
+	return 0;
+}
+
+void cleanup_module(void)
+{
+	int this_dev;
+
+	for (this_dev = 0; this_dev < MAX_ULTRA32_CARDS; this_dev++) {
+		struct device *dev = &dev_ultra[this_dev];
+		if (dev->priv != NULL) {
+			/* NB: ultra32_close_card() does free_irq + irq2dev */
+			int ioaddr = dev->base_addr - ULTRA32_NIC_OFFSET;
+			kfree(dev->priv);
+			dev->priv = NULL;
+			release_region(ioaddr, ULTRA32_IO_EXTENT);
+			unregister_netdev(dev);
+		}
+	}
+}
+#endif /* MODULE */
diff -u --recursive --new-file v2.1.88/linux/drivers/pci/oldproc.c linux/drivers/pci/oldproc.c
--- v2.1.88/linux/drivers/pci/oldproc.c	Tue Feb 17 13:12:46 1998
+++ linux/drivers/pci/oldproc.c	Wed Mar  4 15:31:34 1998
@@ -383,6 +383,7 @@
 	DEVICE( CYCLADES,	CYCLOM_Z_Hi,	"Cyclom-Z above 1Mbyte"),
 	DEVICE( O2,		O2_6832,	"6832"),
 	DEVICE( 3DFX,		3DFX_VOODOO,	"Voodoo"),
+	DEVICE( 3DFX,		3DFX_VOODOO2,	"Voodoo2"),	
 	DEVICE( SIGMADES,	SIGMADES_6425,	"REALmagic64/GX"),
 	DEVICE( STALLION,	STALLION_ECHPCI832,"EasyConnection 8/32"),
 	DEVICE( STALLION,	STALLION_ECHPCI864,"EasyConnection 8/64"),
diff -u --recursive --new-file v2.1.88/linux/drivers/sbus/audio/Makefile linux/drivers/sbus/audio/Makefile
--- v2.1.88/linux/drivers/sbus/audio/Makefile	Mon Jan 12 15:15:45 1998
+++ linux/drivers/sbus/audio/Makefile	Tue Feb 24 22:43:58 1998
@@ -16,37 +16,37 @@
 M_OBJS   :=
 
 ifeq ($(CONFIG_SPARCAUDIO),y)
-M=y
+SBUS_AUDIO=y
 else
   ifeq ($(CONFIG_SPARCAUDIO),m)
-  MM=y
+  SBUS_AUDIO_MODULE=y
   endif
 endif
 
 ifeq ($(CONFIG_SPARCAUDIO_AMD7930),y)
-M=y
+SBUS_AUDIO=y
 OX_OBJS += amd7930.o
 else
   ifeq ($(CONFIG_SPARCAUDIO_AMD7930),m)
-  MM=y
+  SBUS_AUDIO_MODULE=y
   MX_OBJS += amd7930.o
   endif
 endif
 
 ifeq ($(CONFIG_SPARCAUDIO_CS4231),y)
-M=y
+SBUS_AUDIO=y
 O_OBJS += cs4231.o
 else
   ifeq ($(CONFIG_SPARCAUDIO_CS4231),m)
-  MM=y
+  SBUS_AUDIO_MODULE=y
   M_OBJS += cs4231.o
   endif
 endif
 
-ifdef M
+ifdef SBUS_AUDIO
 OX_OBJS += audio.o
 else
-  ifdef MM
+  ifdef SBUS_AUDIO_MODULE
   MX_OBJS += audio.o
   endif
 endif
diff -u --recursive --new-file v2.1.88/linux/drivers/sbus/char/flash.c linux/drivers/sbus/char/flash.c
--- v2.1.88/linux/drivers/sbus/char/flash.c	Mon Jan 12 15:15:45 1998
+++ linux/drivers/sbus/char/flash.c	Tue Feb 24 22:08:01 1998
@@ -134,7 +134,7 @@
 	flash_llseek,
 	flash_read,
 	NULL,		/* no write to the Flash, use mmap
-			 * and play flash dependant tricks.
+			 * and play flash dependent tricks.
 			 */
 	NULL,		/* readdir */
 	NULL,		/* poll */
diff -u --recursive --new-file v2.1.88/linux/drivers/sbus/char/pcikbd.c linux/drivers/sbus/char/pcikbd.c
--- v2.1.88/linux/drivers/sbus/char/pcikbd.c	Mon Jan 12 15:15:45 1998
+++ linux/drivers/sbus/char/pcikbd.c	Fri Feb 27 09:11:40 1998
@@ -846,7 +846,7 @@
 
 static unsigned int aux_poll(struct file *file, poll_table * wait)
 {
-	poll_wait(&queue->proc_list, wait);
+	poll_wait(file, &queue->proc_list, wait);
 	if (aux_ready)
 		return POLLIN | POLLRDNORM;
 	return 0;
diff -u --recursive --new-file v2.1.88/linux/drivers/sbus/char/sunkbd.c linux/drivers/sbus/char/sunkbd.c
--- v2.1.88/linux/drivers/sbus/char/sunkbd.c	Mon Jan 12 15:15:45 1998
+++ linux/drivers/sbus/char/sunkbd.c	Fri Feb 27 09:12:00 1998
@@ -1317,7 +1317,7 @@
 
 static unsigned int kbd_poll (struct file *f, poll_table *wait)
 {
-	poll_wait(&kbd_wait, wait);
+	poll_wait(f, &kbd_wait, wait);
 	if (kbd_head != kbd_tail)
 		return POLLIN | POLLRDNORM;
 	return 0;
diff -u --recursive --new-file v2.1.88/linux/drivers/sbus/char/sunmouse.c linux/drivers/sbus/char/sunmouse.c
--- v2.1.88/linux/drivers/sbus/char/sunmouse.c	Mon Jan 12 15:15:45 1998
+++ linux/drivers/sbus/char/sunmouse.c	Fri Feb 27 09:12:09 1998
@@ -420,7 +420,7 @@
 
 static unsigned int sun_mouse_poll(struct file *file, poll_table *wait)
 {
-	poll_wait(&sunmouse.proc_list, wait);
+	poll_wait(file, &sunmouse.proc_list, wait);
 	if(sunmouse.ready)
 		return POLLIN | POLLRDNORM;
 	return 0;
diff -u --recursive --new-file v2.1.88/linux/drivers/scsi/Config.in linux/drivers/scsi/Config.in
--- v2.1.88/linux/drivers/scsi/Config.in	Thu Feb 12 20:56:09 1998
+++ linux/drivers/scsi/Config.in	Thu Feb 26 19:35:33 1998
@@ -25,7 +25,7 @@
 dep_tristate 'Adaptec AIC7xxx support' CONFIG_SCSI_AIC7XXX $CONFIG_SCSI
 if [ "$CONFIG_SCSI_AIC7XXX" != "n" ]; then
     bool '   Enable tagged command queueing' CONFIG_AIC7XXX_TAGGED_QUEUEING Y
-    dep_tristate '   Override driver defaults for commands per LUN' CONFIG_OVERRIDE_CMDS N
+    bool '   Override driver defaults for commands per LUN' CONFIG_OVERRIDE_CMDS N
     if [ "$CONFIG_OVERRIDE_CMDS" != "n" ]; then
       int  '   Maximum number of commands per LUN' CONFIG_AIC7XXX_CMDS_PER_LUN 8
     fi
@@ -123,7 +123,7 @@
 # The actual configuration in any kernel release could change at any time as I hack it to
 # simulate various conditions that I am testing.
 #
-if [ "`whoami`" = "eric" ]; then
+if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
   dep_tristate 'SCSI debugging host adapter' CONFIG_SCSI_DEBUG $CONFIG_SCSI
 fi
 if [ "$CONFIG_PPC" = "y" ]; then
diff -u --recursive --new-file v2.1.88/linux/drivers/scsi/README.ncr53c8xx linux/drivers/scsi/README.ncr53c8xx
--- v2.1.88/linux/drivers/scsi/README.ncr53c8xx	Mon Jan 12 15:05:27 1998
+++ linux/drivers/scsi/README.ncr53c8xx	Tue Feb 24 22:08:01 1998
@@ -941,7 +941,7 @@
 		Change to linux source directory
 		Configure with NCR53C7,8XX support = N
 		Configure with NCR53C8XX   support = Y (or m)
-		Make dependancies
+		Make dependencies
 		Make the kernel (use make zdisk first)
 		Make and install modules if you have configured with 'm'
 
diff -u --recursive --new-file v2.1.88/linux/drivers/scsi/dc390.h linux/drivers/scsi/dc390.h
--- v2.1.88/linux/drivers/scsi/dc390.h	Fri Feb  6 15:33:40 1998
+++ linux/drivers/scsi/dc390.h	Fri Mar  6 21:03:20 1998
@@ -5,19 +5,7 @@
  *		     Bus Master Host Adapter			       *
  ***********************************************************************/
 
-/* Kernel version autodetection */
-
 #include <linux/version.h>
-/* Convert Linux Version, Patch-level, Sub-level to LINUX_VERSION_CODE. */
-#define ASC_LINUX_VERSION(V, P, S)	(((V) * 65536) + ((P) * 256) + (S))
-
-#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(1,3,50)
-#define VERSION_ELF_1_2_13
-#elseif LINUX_VERSION_CODE < ASC_LINUX_VERSION(1,3,95)
-#define VERSION_1_3_85
-#else
-#define VERSION_2_0_0
-#endif
 
 /*
  * AMD 53C974 driver, header file
@@ -28,45 +16,27 @@
 
 #if defined(HOSTS_C) || defined(MODULE)
 
-#ifdef	VERSION_2_0_0
 #include <scsi/scsicam.h>
-#else
-#include <linux/scsicam.h>
-#endif
 
 extern int DC390_detect(Scsi_Host_Template *psht);
 extern int DC390_queue_command(Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *));
 extern int DC390_abort(Scsi_Cmnd *cmd);
-
-#ifdef	VERSION_2_0_0
 extern int DC390_reset(Scsi_Cmnd *cmd, unsigned int resetFlags);
-#else
-extern int DC390_reset(Scsi_Cmnd *cmd);
-#endif
-
-#ifdef	VERSION_ELF_1_2_13
-extern int DC390_bios_param(Disk *disk, int devno, int geom[]);
-#else
 extern int DC390_bios_param(Disk *disk, kdev_t devno, int geom[]);
-#endif
 
 #ifdef MODULE
 static int DC390_release(struct Scsi_Host *);
 #else
-#define DC390_release NULL
+# define DC390_release NULL
 #endif
 
-#ifndef VERSION_ELF_1_2_13
 extern struct proc_dir_entry proc_scsi_tmscsim;
 extern int tmscsim_proc_info(char *buffer, char **start, off_t offset, int length, int hostno, int inout);
-#endif
-
-#ifdef	VERSION_2_0_0
 
 #define DC390_T    {			          \
    proc_dir:       &proc_scsi_tmscsim,            \
-   proc_info:      tmscsim_proc_info,		  \
-   name:           "Tekram DC390(T) V1.10 Dec-05-1996",\
+   proc_info:      tmscsim_proc_info,             \
+   name:           "Tekram DC390(T) V1.12 Feb-25-1998",\
    detect:         DC390_detect,   		  \
    release:        DC390_release,		  \
    queuecommand:   DC390_queue_command,	          \
@@ -79,61 +49,6 @@
    cmd_per_lun:    2,                 		  \
    use_clustering: DISABLE_CLUSTERING 		  \
    }
-#endif
-
-
-#ifdef	VERSION_1_3_85
-
-#define DC390_T    {			\
-	NULL,	/* *next */		\
-	NULL,	/* *usage_count */	\
-	&proc_scsi_tmscsim,	/* *proc_dir */ 	\
-	tmscsim_proc_info,	/* (*proc_info)() */	\
-	"Tekram DC390(T) V1.10 Dec-05-1996",  /* *name */ \
-	DC390_detect,			\
-	DC390_release,	/* (*release)() */	\
-	NULL,	/* *(*info)() */	\
-	NULL,	/* (*command)() */	\
-	DC390_queue_command,	\
-	DC390_abort,		\
-	DC390_reset,		\
-	NULL, /* slave attach */\
-	DC390_bios_param,	\
-	10,/* can queue(-1) */	\
-	7, /* id(-1) */ 	\
-	SG_ALL, 		\
-	2, /* cmd per lun(2) */ \
-	0, /* present */	\
-	0, /* unchecked isa dma */ \
-	DISABLE_CLUSTERING	\
-	}
-#endif
-
-
-#ifdef	VERSION_ELF_1_2_13
-
-#define DC390_T     {		\
-	NULL,			\
-	NULL,			\
-	"Tekram DC390(T) V1.10 Dec-05-1996",\
-	DC390_detect,		\
-	DC390_release,			\
-	NULL, /* info */	\
-	NULL, /* command, deprecated */ \
-	DC390_queue_command,	\
-	DC390_abort,		\
-	DC390_reset,		\
-	NULL, /* slave attach */\
-	DC390_bios_param,	\
-	10,/* can queue(-1) */	\
-	7, /* id(-1) */ 	\
-	16,/* old (SG_ALL) */	\
-	2, /* cmd per lun(2) */ \
-	0, /* present */	\
-	0, /* unchecked isa dma */ \
-	DISABLE_CLUSTERING	\
-	}
-#endif
 
 #endif /* defined(HOSTS_C) || defined(MODULE) */
 
diff -u --recursive --new-file v2.1.88/linux/drivers/scsi/scsi.c linux/drivers/scsi/scsi.c
--- v2.1.88/linux/drivers/scsi/scsi.c	Mon Feb 23 18:12:07 1998
+++ linux/drivers/scsi/scsi.c	Sun Mar  1 18:24:00 1998
@@ -217,6 +217,7 @@
  */
 static struct dev_info device_list[] =
 {
+{"Aashima","IMAGERY 2400SP","1.03",BLIST_NOLUN},/* Locks up if polled for lun != 0 */
 {"CHINON","CD-ROM CDS-431","H42", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */
 {"CHINON","CD-ROM CDS-535","Q14", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */
 {"DENON","DRD-25X","V", BLIST_NOLUN},           /* Locks up if probed for lun != 0 */
diff -u --recursive --new-file v2.1.88/linux/drivers/scsi/scsiiom.c linux/drivers/scsi/scsiiom.c
--- v2.1.88/linux/drivers/scsi/scsiiom.c	Tue May 13 22:41:13 1997
+++ linux/drivers/scsi/scsiiom.c	Wed Feb 25 19:50:22 1998
@@ -175,13 +175,8 @@
 }
 
 
-#ifndef  VERSION_ELF_1_2_13
 static void
 DC390_Interrupt( int irq, void *dev_id, struct pt_regs *regs)
-#else
-static void
-DC390_Interrupt( int irq, struct pt_regs *regs)
-#endif
 {
     PACB   pACB;
     PDCB   pDCB;
@@ -303,11 +298,7 @@
 		pSRB->pSegmentList++;
 		psgl = pSRB->pSegmentList;
 
-#ifndef VERSION_ELF_1_2_13
 		pSRB->SGPhysAddr = virt_to_phys( psgl->address );
-#else
-		pSRB->SGPhysAddr = (ULONG) psgl->address;
-#endif
 		pSRB->SGToBeXferLen = (ULONG) psgl->length;
 	    }
 	    else
@@ -368,11 +359,7 @@
 		pSRB->pSegmentList++;
 		psgl = pSRB->pSegmentList;
 
-#ifndef VERSION_ELF_1_2_13
 		pSRB->SGPhysAddr = virt_to_phys( psgl->address );
-#else
-		pSRB->SGPhysAddr = (ULONG) psgl->address;
-#endif
 		pSRB->SGToBeXferLen = (ULONG) psgl->length;
 	    }
 	    else
@@ -427,11 +414,7 @@
 	    if( residual )
 	    {
 		bval = inb(ioport+ScsiFifo);	    /* get residual byte */
-#ifndef VERSION_ELF_1_2_13
 		ptr = (PUCHAR) phys_to_virt( pSRB->SGPhysAddr );
-#else
-		ptr = (PUCHAR) pSRB->SGPhysAddr;
-#endif
 		*ptr = bval;
 		pSRB->SGPhysAddr++;
 		pSRB->TotalXferredLen++;
@@ -643,11 +626,7 @@
 	if( !pSRB->SGToBeXferLen )
 	{
 	    psgl = pSRB->pSegmentList;
-#ifndef VERSION_ELF_1_2_13
 	    pSRB->SGPhysAddr = virt_to_phys( psgl->address );
-#else
-	    pSRB->SGPhysAddr = (ULONG) psgl->address;
-#endif
 	    pSRB->SGToBeXferLen = (ULONG) psgl->length;
 	}
 	lval = pSRB->SGToBeXferLen;
diff -u --recursive --new-file v2.1.88/linux/drivers/scsi/sg.c linux/drivers/scsi/sg.c
--- v2.1.88/linux/drivers/scsi/sg.c	Sun Dec 21 17:04:49 1997
+++ linux/drivers/scsi/sg.c	Thu Feb 26 19:55:28 1998
@@ -547,8 +547,8 @@
         struct scsi_generic *device = &scsi_generics[dev];
         unsigned int mask = 0;
 
-        poll_wait(&scsi_generics[dev].read_wait, wait);
-        poll_wait(&scsi_generics[dev].write_wait, wait);
+        poll_wait(file, &scsi_generics[dev].read_wait, wait);
+        poll_wait(file, &scsi_generics[dev].write_wait, wait);
         if(device->pending && device->complete)
                 mask |= POLLIN | POLLRDNORM;
         if(!device->pending)
diff -u --recursive --new-file v2.1.88/linux/drivers/scsi/sr.c linux/drivers/scsi/sr.c
--- v2.1.88/linux/drivers/scsi/sr.c	Mon Feb 23 18:12:08 1998
+++ linux/drivers/scsi/sr.c	Sat Feb 21 12:34:38 1998
@@ -471,7 +471,7 @@
  	    {
 		spin_unlock_irqrestore(&io_request_lock, flags);
 		scsi_ioctl(SDev, SCSI_IOCTL_DOORLOCK, 0);
-		/* scsi_ioctl may allow CURRENT to change, so start over. *
+		/* scsi_ioctl may allow CURRENT to change, so start over. */
 		SDev->was_reset = 0;
 		continue;
  	    }
diff -u --recursive --new-file v2.1.88/linux/drivers/scsi/st.c linux/drivers/scsi/st.c
--- v2.1.88/linux/drivers/scsi/st.c	Mon Dec 22 13:47:24 1997
+++ linux/drivers/scsi/st.c	Sun Mar  1 14:46:04 1998
@@ -2846,11 +2846,18 @@
 	 }
        }
 
-       i = flush_buffer(inode, file, /* mtc.mt_op == MTSEEK || */
-			mtc.mt_op == MTREW || mtc.mt_op == MTOFFL ||
-			mtc.mt_op == MTRETEN || mtc.mt_op == MTEOM ||
-			mtc.mt_op == MTLOCK || mtc.mt_op == MTLOAD ||
-			mtc.mt_op == MTCOMPRESSION);
+       if (mtc.mt_op == MTSEEK) {
+	   /* Old position must be restored if partition will be changed */
+	   i = !STp->can_partitions ||
+	       (STp->new_partition != STp->partition);
+       }
+       else {
+	   i = mtc.mt_op == MTREW || mtc.mt_op == MTOFFL ||
+	       mtc.mt_op == MTRETEN || mtc.mt_op == MTEOM ||
+	       mtc.mt_op == MTLOCK || mtc.mt_op == MTLOAD ||
+	       mtc.mt_op == MTCOMPRESSION;
+       }
+       i = flush_buffer(inode, file, i);
        if (i < 0)
 	 return i;
      }
diff -u --recursive --new-file v2.1.88/linux/drivers/scsi/tmscsim.c linux/drivers/scsi/tmscsim.c
--- v2.1.88/linux/drivers/scsi/tmscsim.c	Thu Jul 10 07:55:30 1997
+++ linux/drivers/scsi/tmscsim.c	Wed Feb 25 19:50:22 1998
@@ -24,6 +24,9 @@
  *	1.09  11/30/96	KG	Added register the allocated IO space	*
  *	1.10  12/05/96	CLH	Modified tmscsim_proc_info(), and reset *
  *				pending interrupt in DC390_detect()	*
+ * 	1.11  02/05/97	KG/CLH	Fixeds problem with partitions greater	*
+ * 				than 1GB				*
+ * 	1.12  25/02/98	KG	Cleaned up ifdefs for 2.1 kernel	*
  ***********************************************************************/
 
 
@@ -32,7 +35,7 @@
 #define SCSI_MALLOC
 
 #ifdef MODULE
-#include <linux/module.h>
+# include <linux/module.h>
 #endif
 
 #include <asm/dma.h>
@@ -677,11 +680,11 @@
     sectors = 32;
     cylinders = disk->capacity / (heads * sectors);
 
-    if ( cylinders > 1024)
+    if ( (pACB->Gmode2 & GREATER_1G) && (cylinders > 1024) )
     {
       heads = 255;
       sectors = 63;
-      cylinders = disk->capacity / (255 * 63);
+      cylinders = disk->capacity / (heads * sectors);
     }
 
     geom[0] = heads;
@@ -898,11 +901,7 @@
  * Returns : 0 on success.
  ***********************************************************************/
 
-#ifdef	VERSION_2_0_0
 int DC390_reset(Scsi_Cmnd *cmd, unsigned int resetFlags)
-#else
-int DC390_reset (Scsi_Cmnd *cmd)
-#endif
 {
     USHORT   ioport;
     unsigned long flags;
@@ -1161,7 +1160,7 @@
 
     if( !used_irq )
     {
-	if( request_irq(Irq, DC390_Interrupt, SA_INTERRUPT, "tmscsim", NULL))
+	if( request_irq(Irq, DC390_Interrupt, SA_INTERRUPT | SA_SHIRQ, "tmscsim", NULL))
 	{
 	    printk("DC390: register IRQ error!\n");
 	    return( -1 );
@@ -1763,11 +1762,11 @@
   if (acbpnt == (PACB)-1) return(-ESRCH);
   if(!shpnt) return(-ESRCH);
 
-  if(inout) // Has data been written to the file ?
+  if(inout) /* Has data been written to the file ? */
     return(tmscsim_set_info(buffer, length, shpnt));
 
   SPRINTF("Tekram DC390(T) PCI SCSI Host Adadpter, ");
-  SPRINTF("Driver Version 1.10, 1996/12/05\n");
+  SPRINTF("Driver Version 1.12, 1998/02/25\n");
 
   save_flags(flags);
   cli();
diff -u --recursive --new-file v2.1.88/linux/drivers/scsi/tmscsim.h linux/drivers/scsi/tmscsim.h
--- v2.1.88/linux/drivers/scsi/tmscsim.h	Tue May 13 22:41:13 1997
+++ linux/drivers/scsi/tmscsim.h	Wed Feb 25 19:50:22 1998
@@ -4,8 +4,8 @@
 ;*		    Device Driver				       *
 ;***********************************************************************/
 
-#ifndef TMSCSIM_H
-#define TMSCSIM_H
+#ifndef _TMSCSIM_H
+#define _TMSCSIM_H
 
 #define IRQ_NONE 255
 
@@ -677,4 +677,4 @@
 	(PCI_CFG2_ENABLE | (((function) << 1) & 0xe))
 
 
-#endif /* TMSCSIM_H */
+#endif /* _TMSCSIM_H */
diff -u --recursive --new-file v2.1.88/linux/drivers/sgi/char/shmiq.c linux/drivers/sgi/char/shmiq.c
--- v2.1.88/linux/drivers/sgi/char/shmiq.c	Wed Dec 10 10:31:11 1997
+++ linux/drivers/sgi/char/shmiq.c	Fri Feb 27 09:12:29 1998
@@ -365,7 +365,7 @@
 	if (!shmiqs [minor].mapped)
 		return 0;
 	
-	poll_wait (&shmiqs [minor].proc_list, wait);
+	poll_wait (filp, &shmiqs [minor].proc_list, wait);
 	s = shmiqs [minor].shmiq_vaddr;
 	if (s->head != s->tail)
 		return POLLIN | POLLRDNORM;
diff -u --recursive --new-file v2.1.88/linux/drivers/sound/dmabuf.c linux/drivers/sound/dmabuf.c
--- v2.1.88/linux/drivers/sound/dmabuf.c	Mon Feb 23 18:12:08 1998
+++ linux/drivers/sound/dmabuf.c	Thu Feb 26 19:56:57 1998
@@ -1150,7 +1150,7 @@
 	}
 }
 
-static unsigned int poll_input(int dev, poll_table *wait)
+static unsigned int poll_input(struct file * file, int dev, poll_table *wait)
 {
 	struct audio_operations *adev = audio_devs[dev];
 	struct dma_buffparms *dmap = adev->dmap_in;
@@ -1158,7 +1158,7 @@
 	if (!(adev->open_mode & OPEN_READ))
 		return 0;
 	if (dmap->mapping_flags & DMA_MAP_MAPPED) {
-		poll_wait(&adev->in_sleeper, wait);
+		poll_wait(file, &adev->in_sleeper, wait);
 		if (dmap->qlen)
 			return POLLIN | POLLRDNORM;
 		return 0;
@@ -1169,7 +1169,7 @@
 		    !dmap->qlen && adev->go) {
 			unsigned long flags;
 			
-			poll_wait(&adev->in_sleeper, wait);
+			poll_wait(file, &adev->in_sleeper, wait);
 			save_flags(flags);
 			cli();
 			DMAbuf_activate_recording(dev, dmap);
@@ -1177,13 +1177,13 @@
 		}
 		return 0;
 	}
-	poll_wait(&adev->in_sleeper, wait);
+	poll_wait(file, &adev->in_sleeper, wait);
 	if (!dmap->qlen)
 		return 0;
 	return POLLIN | POLLRDNORM;
 }
 
-static unsigned int poll_output(int dev, poll_table *wait)
+static unsigned int poll_output(struct file * file, int dev, poll_table *wait)
 {
 	struct audio_operations *adev = audio_devs[dev];
 	struct dma_buffparms *dmap = adev->dmap_out;
@@ -1191,14 +1191,14 @@
 	if (!(adev->open_mode & OPEN_WRITE))
 		return 0;
 	if (dmap->mapping_flags & DMA_MAP_MAPPED) {
-		poll_wait(&adev->out_sleeper, wait);
+		poll_wait(file, &adev->out_sleeper, wait);
 		if (dmap->qlen)
 			return POLLOUT | POLLWRNORM;
 		return 0;
 	}
 	if (dmap->dma_mode == DMODE_INPUT)
 		return 0;
-	poll_wait(&adev->out_sleeper, wait);
+	poll_wait(file, &adev->out_sleeper, wait);
 	if (dmap->dma_mode == DMODE_NONE)
 		return POLLOUT | POLLWRNORM;
 	if (!DMAbuf_space_in_queue(dev))
@@ -1206,9 +1206,9 @@
 	return POLLOUT | POLLWRNORM;
 }
 
-unsigned int DMAbuf_poll(int dev, poll_table *wait)
+unsigned int DMAbuf_poll(struct file * file, int dev, poll_table *wait)
 {
-	return poll_input(dev, wait) | poll_output(dev, wait);
+	return poll_input(file, dev, wait) | poll_output(file, dev, wait);
 }
 
 void DMAbuf_deinit(int dev)
diff -u --recursive --new-file v2.1.88/linux/drivers/sound/local.h.master linux/drivers/sound/local.h.master
--- v2.1.88/linux/drivers/sound/local.h.master	Fri Jan 30 15:50:57 1998
+++ linux/drivers/sound/local.h.master	Thu Feb 26 17:28:09 1998
@@ -114,119 +114,5 @@
 
 #define SM_WAVE
 #define __SGNXPRO__
-#define SM_GAMES
-#define DESKPROXL
-/* Computer generated file. Please don't edit! */
-
-#define KERNEL_COMPATIBLE_CONFIG
-
-#define SELECTED_SOUND_OPTIONS	0x00000000
-
-#if \
-  defined(CONFIG_PSS) || defined(CONFIG_SSCAPE) || \
-  defined(CONFIG_CS4232) || defined(CONFIG_MAUI) || \
-  defined(CONFIG_PSS_MODULE) || defined(CONFIG_SSCAPE_MODULE) || \
-  defined(CONFIG_CS4232_MODULE) || defined(CONFIG_MAUI_MODULE)
-#	define CONFIG_MPU_EMU
-#endif
-
-#if \
-  defined(CONFIG_PSS) || defined(CONFIG_GUS16) || \
-  defined(CONFIG_GUSMAX) || defined(CONFIG_MSS) || \
-  defined(CONFIG_SSCAPE) || defined(CONFIG_TRIX) || \
-  defined(CONFIG_MAD16) || defined(CONFIG_CS4232) || \
-  defined(CONFIG_PSS_MODULE) || defined(CONFIG_GUS16_MODULE) || \
-  defined(CONFIG_GUSMAX_MODULE) || defined(CONFIG_MSS_MODULE) || \
-  defined(CONFIG_SSCAPE_MODULE) || defined(CONFIG_TRIX_MODULE) || \
-  defined(CONFIG_MAD16_MODULE) || defined(CONFIG_CS4232_MODULE)
-#	define CONFIG_AD1848
-#endif
-
-#if \
-  defined(CONFIG_PAS) || defined(CONFIG_SB) || \
-  defined(CONFIG_GUS) || defined(CONFIG_PSS) || \
-  defined(CONFIG_GUS16) || defined(CONFIG_GUSMAX) || \
-  defined(CONFIG_MSS) || defined(CONFIG_SSCAPE) || \
-  defined(CONFIG_TRIX) || defined(CONFIG_MAD16) || \
-  defined(CONFIG_CS4232) || defined(CONFIG_OPL3SA1) || \
-  defined(CONFIG_SOFTOSS) || \
-  defined(CONFIG_PAS_MODULE) || defined(CONFIG_SB_MODULE) || \
-  defined(CONFIG_GUS_MODULE) || defined(CONFIG_PSS_MODULE) || \
-  defined(CONFIG_GUS16_MODULE) || defined(CONFIG_GUSMAX_MODULE) || \
-  defined(CONFIG_MSS_MODULE) || defined(CONFIG_SSCAPE_MODULE) || \
-  defined(CONFIG_TRIX_MODULE) || defined(CONFIG_MAD16_MODULE) || \
-  defined(CONFIG_CS4232_MODULE) || defined(CONFIG_OPL3SA1_MODULE) || \
-  defined(CONFIG_SOFTOSS_MODULE)
-#       define CONFIG_AUDIO
-#endif
-
-#if \
-  defined(CONFIG_PAS) || defined(CONFIG_SB) || \
-  defined(CONFIG_GUS) || defined(CONFIG_MPU401) || \
-  defined(CONFIG_PSS) || defined(CONFIG_GUS16) || \
-  defined(CONFIG_GUSMAX) || defined(CONFIG_SSCAPE) || \
-  defined(CONFIG_TRIX) || defined(CONFIG_MAD16) || \
-  defined(CONFIG_CS4232) || defined(CONFIG_MAUI) || \
-  defined(CONFIG_OPL3SA1) || defined(CONFIG_SOFTOSS) || \
-  defined(CONFIG_PAS_MODULE) || defined(CONFIG_SB_MODULE) || \
-  defined(CONFIG_GUS_MODULE) || defined(CONFIG_MPU401_MODULE) || \
-  defined(CONFIG_PSS_MODULE) || defined(CONFIG_GUS16_MODULE) || \
-  defined(CONFIG_GUSMAX_MODULE) || defined(CONFIG_SSCAPE_MODULE) || \
-  defined(CONFIG_TRIX_MODULE) || defined(CONFIG_MAD16_MODULE) || \
-  defined(CONFIG_CS4232_MODULE) || defined(CONFIG_MAUI_MODULE) || \
-  defined(CONFIG_OPL3SA1_MODULE) || defined(CONFIG_SOFTOSS_MODULE)
-#       define CONFIG_MIDI
-#endif
-
-#if \
-  defined(CONFIG_SB) || defined(CONFIG_TRIX) || \
-  defined(CONFIG_MAD16) || \
-  defined(CONFIG_SB_MODULE) || defined(CONFIG_TRIX_MODULE) || \
-  defined(CONFIG_MAD16_MODULE)
-#	define CONFIG_SBDSP
-#endif
-#if \
-  defined(CONFIG_SB_MODULE) || defined(CONFIG_TRIX_MODULE) || \
-  defined(CONFIG_MAD16_MODULE)
-#	define CONFIG_SBDSP_MODULE
-#endif
-
-#if \
-  defined(CONFIG_SB) || defined(CONFIG_TRIX) || \
-  defined(CONFIG_MAD16) ||  defined(CONFIG_SB_MODULE) || \
-  defined(CONFIG_TRIX_MODULE) || defined(CONFIG_MAD16_MODULE) 
-#	define CONFIG_UART401
-#endif
-
-#if \
-  defined(CONFIG_SB_MODULE) || defined(CONFIG_TRIX_MODULE) || \
-  defined(CONFIG_MAD16_MODULE) 
-#ifndef CONFIG_UART401_MODULE
-#define CONFIG_UART401_MODULE
-#endif
-#endif 
-
-#if \
-  defined(CONFIG_PAS) || defined(CONFIG_SB) || \
-  defined(CONFIG_ADLIB) || defined(CONFIG_GUS) || \
-  defined(CONFIG_MPU401) || defined(CONFIG_PSS) || \
-  defined(CONFIG_SSCAPE) || defined(CONFIG_TRIX) || \
-  defined(CONFIG_MAD16) || defined(CONFIG_CS4232) || \
-  defined(CONFIG_MAUI) || \
-  defined(CONFIG_PAS_MODULE) || defined(CONFIG_SB_MODULE) || \
-  defined(CONFIG_ADLIB_MODULE) || defined(CONFIG_GUS_MODULE) || \
-  defined(CONFIG_MPU401_MODULE) || defined(CONFIG_PSS_MODULE) || \
-  defined(CONFIG_SSCAPE_MODULE) || defined(CONFIG_TRIX_MODULE) || \
-  defined(CONFIG_MAD16_MODULE) || defined(CONFIG_CS4232_MODULE) || \
-  defined(CONFIG_MAUI_MODULE)
-#	define CONFIG_SEQUENCER
-#endif
-
-/*
- *	Force on additional support
- */
-
-#define SM_WAVE
-#define __SGNXPRO__
-#define SM_GAMES
+/* #define SM_GAMES */
 #define DESKPROXL
diff -u --recursive --new-file v2.1.88/linux/drivers/sound/maui.c linux/drivers/sound/maui.c
--- v2.1.88/linux/drivers/sound/maui.c	Mon Feb 23 18:12:09 1998
+++ linux/drivers/sound/maui.c	Fri Feb 27 09:15:23 1998
@@ -211,7 +211,7 @@
 	outb((0x80), HOST_CTRL_PORT);	/* Leave reset */
 	outb((0xD0), HOST_CTRL_PORT);	/* Cause interrupt */
 
-#ifndef __SMP__
+#ifdef __SMP__
 	for (i = 0; i < 1000000 && !irq_ok; i++);
 
 	if (!irq_ok)
diff -u --recursive --new-file v2.1.88/linux/drivers/sound/midibuf.c linux/drivers/sound/midibuf.c
--- v2.1.88/linux/drivers/sound/midibuf.c	Mon Feb 23 18:12:09 1998
+++ linux/drivers/sound/midibuf.c	Thu Feb 26 20:01:26 1998
@@ -396,12 +396,12 @@
 	dev = dev >> 4;
 
 	/* input */
-	poll_wait(&input_sleeper[dev], wait);
+	poll_wait(file, &input_sleeper[dev], wait);
 	if (DATA_AVAIL(midi_in_buf[dev]))
 		mask |= POLLIN | POLLRDNORM;
 
 	/* output */
-	poll_wait(&midi_sleeper[dev], wait);
+	poll_wait(file, &midi_sleeper[dev], wait);
 	if (!SPACE_AVAIL(midi_out_buf[dev]))
 		mask |= POLLOUT | POLLWRNORM;
 	
diff -u --recursive --new-file v2.1.88/linux/drivers/sound/sequencer.c linux/drivers/sound/sequencer.c
--- v2.1.88/linux/drivers/sound/sequencer.c	Mon Feb 23 18:12:09 1998
+++ linux/drivers/sound/sequencer.c	Thu Feb 26 20:01:37 1998
@@ -1578,12 +1578,12 @@
 	save_flags(flags);
 	cli();
 	/* input */
-	poll_wait(&midi_sleeper, wait);
+	poll_wait(file, &midi_sleeper, wait);
 	if (iqlen)
 		mask |= POLLIN | POLLRDNORM;
 
 	/* output */
-	poll_wait(&seq_sleeper, wait);
+	poll_wait(file, &seq_sleeper, wait);
 	if ((SEQ_MAX_QUEUE - qlen) >= output_threshold) 
 		mask |= POLLOUT | POLLWRNORM;
 	restore_flags(flags);
diff -u --recursive --new-file v2.1.88/linux/drivers/sound/sound_calls.h linux/drivers/sound/sound_calls.h
--- v2.1.88/linux/drivers/sound/sound_calls.h	Mon Feb 23 18:12:09 1998
+++ linux/drivers/sound/sound_calls.h	Thu Feb 26 19:58:15 1998
@@ -22,11 +22,10 @@
 int DMAbuf_activate_recording (int dev, struct dma_buffparms *dmap);
 int DMAbuf_get_buffer_pointer (int dev, struct dma_buffparms *dmap, int direction);
 void DMAbuf_launch_output(int dev, struct dma_buffparms *dmap);
-unsigned int DMAbuf_poll(int dev, poll_table *wait);
+unsigned int DMAbuf_poll(struct file *file, int dev, poll_table *wait);
 void DMAbuf_start_devices(unsigned int devmask);
 void DMAbuf_reset (int dev);
 int DMAbuf_sync (int dev);
-unsigned int DMAbuf_poll(int dev, poll_table *wait);
 
 /*
  *	System calls for /dev/dsp and /dev/audio (audio.c)
diff -u --recursive --new-file v2.1.88/linux/drivers/sound/sound_firmware.c linux/drivers/sound/sound_firmware.c
--- v2.1.88/linux/drivers/sound/sound_firmware.c	Mon Dec  1 10:34:11 1997
+++ linux/drivers/sound/sound_firmware.c	Fri Feb 27 09:14:00 1998
@@ -6,7 +6,6 @@
 #include <asm/uaccess.h>
 
 static int errno;
-
 static int do_mod_firmware_load(const char *fn, char **fp)
 {
 	int fd;
diff -u --recursive --new-file v2.1.88/linux/drivers/sound/soundcard.c linux/drivers/sound/soundcard.c
--- v2.1.88/linux/drivers/sound/soundcard.c	Mon Feb 23 18:12:09 1998
+++ linux/drivers/sound/soundcard.c	Fri Feb 27 10:57:24 1998
@@ -670,7 +670,7 @@
 	case SND_DEV_DSP:
 	case SND_DEV_DSP16:
 	case SND_DEV_AUDIO:
-		return DMAbuf_poll(dev >> 4, wait);
+		return DMAbuf_poll(file, dev >> 4, wait);
 #endif
 	}
 	return 0;
@@ -732,7 +732,8 @@
 		vma->vm_page_prot))
 		return -EAGAIN;
 
-	vma->vm_dentry = dget(file->f_dentry);
+	vma->vm_file = file;
+	file->f_count++;
 
 	dmap->mapping_flags |= DMA_MAP_MAPPED;
 
diff -u --recursive --new-file v2.1.88/linux/fs/Config.in linux/fs/Config.in
--- v2.1.88/linux/fs/Config.in	Tue Jan 20 16:44:57 1998
+++ linux/fs/Config.in	Fri Mar  6 10:03:04 1998
@@ -47,6 +47,9 @@
 fi
 if [ "$CONFIG_IPX" != "n" ]; then
   tristate 'NCP filesystem support (to mount NetWare volumes)' CONFIG_NCP_FS
+  if [ "$CONFIG_NCP_FS" != "n" ]; then
+    source fs/ncpfs/Config.in
+  fi
 fi
 tristate 'OS/2 HPFS filesystem support (read only)' CONFIG_HPFS_FS
 
diff -u --recursive --new-file v2.1.88/linux/fs/affs/Changes linux/fs/affs/Changes
--- v2.1.88/linux/fs/affs/Changes	Tue Jan  6 13:33:29 1998
+++ linux/fs/affs/Changes	Mon Feb 23 22:01:26 1998
@@ -28,6 +28,44 @@
 
 Please direct bug reports to: hjw@zvw.de
 
+Version 3.8
+-----------
+Bill Hawes kindly reviewed the affs and sent me the
+patches he did. They're marked (BH). Thanks, Bill!
+
+- Cleanup of error handling in read_super().
+  Didn't release all ressources in case of an
+  error. (BH)
+
+- put_inode() releases the ext cache only if it's
+  no longer needed. (BH)
+
+- One set of dentry callbacks is enough. (BH)
+
+- Cleanup of error handling in namei.c. (BH)
+
+- Cleanup of error handling in file.c. (BH)
+
+- The original blocksize of the device is
+  restored when the fs is unmounted. (BH)
+
+- getblock() did not invalidate the key cache
+  when it allocated a new block.
+
+- Removed some unneccessary locks as Bill
+  suggested.
+
+- Simplified match_name(), changed all hashing
+  and case insensitive name comparisons to use
+  uppercase. This makes the tolower() routines
+  obsolete.
+
+- Added mount option 'mufs' to force muFS
+  uid/gid interpretation.
+
+- File mode changes were not updated on disk.
+  This was fixed before, but somehow got lost.
+
 Version 3.7
 -----------
 
diff -u --recursive --new-file v2.1.88/linux/fs/affs/bitmap.c linux/fs/affs/bitmap.c
--- v2.1.88/linux/fs/affs/bitmap.c	Tue Dec  2 22:25:07 1997
+++ linux/fs/affs/bitmap.c	Mon Feb 23 22:01:26 1998
@@ -7,6 +7,7 @@
  *  block allocation, deallocation, calculation of free space.
  */
 
+#define DEBUG 0
 #include <linux/sched.h>
 #include <linux/affs_fs.h>
 #include <linux/stat.h>
diff -u --recursive --new-file v2.1.88/linux/fs/affs/file.c linux/fs/affs/file.c
--- v2.1.88/linux/fs/affs/file.c	Tue Jan  6 13:33:29 1998
+++ linux/fs/affs/file.c	Mon Feb 23 22:01:26 1998
@@ -309,7 +309,7 @@
 
 	for (;;) {
 		bh = affs_bread(inode->i_dev,key,AFFS_I2BSIZE(inode));
-		if (!bh)
+		if (!bh) 
 			return 0;
 		index = seqnum_to_index(ext);
 		if (index > inode->u.affs_i.i_ec->max_ext &&
@@ -360,57 +360,57 @@
 static struct buffer_head *
 affs_getblock(struct inode *inode, s32 block)
 {
-	struct buffer_head	*bh;
-	struct buffer_head	*ebh;
-	struct buffer_head	*pbh;
+	struct super_block	*sb = inode->i_sb;
+	int			 ofs = sb->u.affs_sb.s_flags & SF_OFS;
+	int			 ext = block / AFFS_I2HSIZE(inode);
+	struct buffer_head	*bh, *ebh, *pbh = NULL;
 	struct key_cache	*kc;
 	s32			 key, nkey;
-	int			 ext;
 	int			 cf, j, pt;
 	int			 index;
-	int			 ofs;
+	int			 err;
 
 	pr_debug("AFFS: getblock(%lu,%d)\n",inode->i_ino,block);
 
 	if (block < 0)
-		return NULL;
+		goto out_fail;
 
-	/* Writers always use cache line 3. In almost all cases, files
-	 * will be written by only one process at the same time, and
-	 * they also will be written in strict sequential order. Thus
-	 * there is not much sense in looking whether the key of the
-	 * requested block is available - it won't be there.
-	 */
-	kc     = &inode->u.affs_i.i_ec->kc[3];
-	ofs    = inode->i_sb->u.affs_sb.s_flags & SF_OFS;
-	ext    = block / AFFS_I2HSIZE(inode);
 	key    = calc_key(inode,&ext);
 	block -= ext * AFFS_I2HSIZE(inode);
 	pt     = ext ? T_LIST : T_SHORT;
-	pbh    = NULL;
 
+	/* Key refers now to the last known extension block,
+	 * ext is its sequence number (if 0, key refers to the
+	 * header block), and block is the block number relative
+	 * to the first block stored in that extension block.
+	 */
 	for (;;) {	/* Loop over header block and extension blocks */
+		struct file_front *fdp;
+
 		bh = affs_bread(inode->i_dev,key,AFFS_I2BSIZE(inode));
 		if (!bh)
-			return NULL;
-		if (affs_checksum_block(AFFS_I2BSIZE(inode),bh->b_data,&cf,&j) ||
-		    cf != pt || j != ST_FILE) {
-		    	affs_error(inode->i_sb,"getblock","Inode %d is not a valid %s",key,
-			       pt == T_SHORT ? "file header" : "extension block");
-			affs_brelse(bh);
-			return NULL;
+			goto out_fail;
+		fdp = (struct file_front *) bh->b_data;
+		err = affs_checksum_block(AFFS_I2BSIZE(inode),bh->b_data,&cf,&j);
+		if (err || cf != pt || j != ST_FILE) {
+		    	affs_error(sb, "getblock",
+				"Block %d is not a valid %s", key,
+				pt == T_SHORT ? "file header" : "ext block");
+			goto out_free_bh;
 		}
 		j  = be32_to_cpu(((struct file_front *)bh->b_data)->block_count);
-		cf = 0;
-		while (j < AFFS_I2HSIZE(inode) && j <= block) {
+		for (cf = 0; j < AFFS_I2HSIZE(inode) && j <= block; j++) {
 			if (ofs && !pbh && inode->u.affs_i.i_lastblock >= 0) {
-				if (j > 0)
-					pbh = affs_bread(inode->i_dev,cpu_to_be32(AFFS_BLOCK(bh->b_data,inode,j - 1)),
-							 AFFS_I2BSIZE(inode));
-				else
+				if (j > 0) {
+					s32 k = AFFS_BLOCK(bh->b_data, inode,
+								j - 1);
+					pbh = affs_bread(inode->i_dev,
+							be32_to_cpu(k),
+							AFFS_I2BSIZE(inode));
+				} else
 					pbh = affs_getblock(inode,inode->u.affs_i.i_lastblock);
 				if (!pbh) {
-					affs_error(inode->i_sb,"getblock",
+					affs_error(sb,"getblock",
 						"Cannot get last block in file");
 					break;
 				}
@@ -419,46 +419,44 @@
 			if (!nkey)
 				break;
 			inode->u.affs_i.i_lastblock++;
-			lock_super(inode->i_sb);
 			if (AFFS_BLOCK(bh->b_data,inode,j)) {
-				unlock_super(inode->i_sb);
-				affs_warning(inode->i_sb,"getblock","Block already allocated");
-				affs_free_block(inode->i_sb,nkey);
-				j++;
+				affs_warning(sb,"getblock","Block already allocated");
+				affs_free_block(sb,nkey);
 				continue;
 			}
-			unlock_super(inode->i_sb);
 			AFFS_BLOCK(bh->b_data,inode,j) = cpu_to_be32(nkey);
 			if (ofs) {
 				ebh = affs_bread(inode->i_dev,nkey,AFFS_I2BSIZE(inode));
 				if (!ebh) {
-					affs_error(inode->i_sb,"getblock",
+					affs_error(sb,"getblock",
 						   "Cannot get block %d",nkey);
-					affs_free_block(inode->i_sb,nkey);
+					affs_free_block(sb,nkey);
 					AFFS_BLOCK(bh->b_data,inode,j) = 0;
 					break;
 				}
 				DATA_FRONT(ebh)->primary_type    = cpu_to_be32(T_DATA);
 				DATA_FRONT(ebh)->header_key      = cpu_to_be32(inode->i_ino);
 				DATA_FRONT(ebh)->sequence_number = cpu_to_be32(inode->u.affs_i.i_lastblock + 1);
+				affs_fix_checksum(AFFS_I2BSIZE(inode),
+							ebh->b_data, 5);
+				mark_buffer_dirty(ebh, 0);
 				if (pbh) {
 					DATA_FRONT(pbh)->data_size = cpu_to_be32(AFFS_I2BSIZE(inode) - 24);
 					DATA_FRONT(pbh)->next_data = cpu_to_be32(nkey);
 					affs_fix_checksum(AFFS_I2BSIZE(inode),pbh->b_data,5);
 					mark_buffer_dirty(pbh,0);
-					mark_buffer_dirty(ebh,0);
 					affs_brelse(pbh);
 				}
 				pbh = ebh;
 			}
-			j++;
 			cf = 1;
 		}
+		/* N.B. May need to release pbh after here */
+
 		if (cf) {
 			if (pt == T_SHORT)
-				((struct file_front *)bh->b_data)->first_data =
-								AFFS_BLOCK(bh->b_data,inode,0);
-			((struct file_front *)bh->b_data)->block_count = cpu_to_be32(j);
+				fdp->first_data = AFFS_BLOCK(bh->b_data,inode,0);
+			fdp->block_count = cpu_to_be32(j);
 			affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5);
 			mark_buffer_dirty(bh,1);
 		}
@@ -469,52 +467,63 @@
 			break;
 		}
 		if (j < AFFS_I2HSIZE(inode)) {
-			affs_brelse(bh);
-			return NULL;
+			/* N.B. What about pbh here? */
+			goto out_free_bh;
 		}
 
 		block -= AFFS_I2HSIZE(inode);
 		key    = be32_to_cpu(FILE_END(bh->b_data,inode)->extension);
 		if (!key) {
 			key = affs_new_header(inode);
-			if (!key) {
-				affs_brelse(bh);
-				return NULL;
-			}
+			if (!key)
+				goto out_free_bh;
 			ebh = affs_bread(inode->i_dev,key,AFFS_I2BSIZE(inode));
 			if (!ebh) {
-				affs_free_block(inode->i_sb,key);
-				return NULL;
+				/* N.B. must free bh here */
+				goto out_free_block;
 			}
 			((struct file_front *)ebh->b_data)->primary_type = cpu_to_be32(T_LIST);
 			((struct file_front *)ebh->b_data)->own_key      = cpu_to_be32(key);
 			FILE_END(ebh->b_data,inode)->secondary_type      = cpu_to_be32(ST_FILE);
 			FILE_END(ebh->b_data,inode)->parent              = cpu_to_be32(inode->i_ino);
 			affs_fix_checksum(AFFS_I2BSIZE(inode),ebh->b_data,5);
+			mark_buffer_dirty(ebh, 1);
 			FILE_END(bh->b_data,inode)->extension = cpu_to_be32(key);
 			affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5);
 			mark_buffer_dirty(bh,1);
 			affs_brelse(bh);
 			bh = ebh;
 		}
-		affs_brelse(bh);
 		pt = T_LIST;
 		ext++;
-		if ((index = seqnum_to_index(ext)) > inode->u.affs_i.i_ec->max_ext &&
-		    AFFS_ISINDEX(ext) && inode->u.affs_i.i_ec) {
+		index = seqnum_to_index(ext);
+		if (index > inode->u.affs_i.i_ec->max_ext &&
+		    AFFS_ISINDEX(ext)) {
 			inode->u.affs_i.i_ec->ec[index] = key;
 			inode->u.affs_i.i_ec->max_ext   = index;
 		}
+		affs_brelse(bh);
+	}
+
+	/* Invalidate key cache */
+	for (j = 0; j < 4; j++) {
+		kc = &inode->u.affs_i.i_ec->kc[j];
+		kc->kc_last = -1;
 	}
-	kc->kc_this_key = key;
-	kc->kc_this_seq = ext;
-	kc->kc_next_key = be32_to_cpu(FILE_END(bh->b_data,inode)->extension);
 	key = be32_to_cpu(AFFS_BLOCK(bh->b_data,inode,block));
 	affs_brelse(bh);
 	if (!key)
-		return NULL;
-	
-	return affs_bread(inode->i_dev,key,AFFS_I2BSIZE(inode));
+		goto out_fail;
+
+	bh = affs_bread(inode->i_dev, key, AFFS_I2BSIZE(inode));
+	return bh;
+
+out_free_block:
+	affs_free_block(sb, key);
+out_free_bh:
+	affs_brelse(bh);
+out_fail:
+	return NULL;
 }
 
 static ssize_t
@@ -592,14 +601,11 @@
 			   inode->i_mode);
 		return -EINVAL;
 	}
-	if (!inode->u.affs_i.i_ec) {
-		if (alloc_ext_cache(inode)) {
-			return -ENOMEM;
-		}
-	}
-	if (filp->f_flags & O_APPEND) {
+	if (!inode->u.affs_i.i_ec && alloc_ext_cache(inode))
+		return -ENOMEM;
+	if (filp->f_flags & O_APPEND)
 		pos = inode->i_size;
-	} else
+	else
 		pos = *ppos;
 	written   = 0;
 	blocksize = AFFS_I2BSIZE(inode);
@@ -734,6 +740,23 @@
 	return written;
 }
 
+/* Free any preallocated blocks */
+void
+affs_free_prealloc(struct inode *inode)
+{
+	struct super_block *sb = inode->i_sb;
+	int block;
+
+	pr_debug("AFFS: free_prealloc(ino=%lu)\n", inode->i_ino);
+
+	while (inode->u.affs_i.i_pa_cnt) {	
+		block = inode->u.affs_i.i_data[inode->u.affs_i.i_pa_next++];
+		inode->u.affs_i.i_pa_next &= AFFS_MAX_PREALLOC - 1;
+		inode->u.affs_i.i_pa_cnt--;
+		affs_free_block(sb, block);
+	}
+}
+
 void
 affs_truncate(struct inode *inode)
 {
@@ -764,12 +787,7 @@
 		}
 		bh = affs_getblock(inode,first - 1);
 
-		while (inode->u.affs_i.i_pa_cnt) {		/* Free any preallocated blocks */
-			affs_free_block(inode->i_sb,
-					inode->u.affs_i.i_data[inode->u.affs_i.i_pa_next++]);
-			inode->u.affs_i.i_pa_next &= AFFS_MAX_PREALLOC - 1;
-			inode->u.affs_i.i_pa_cnt--;
-		}
+		affs_free_prealloc(inode);
 		if (inode->u.affs_i.i_zone) {
 			lock_super(inode->i_sb);
 			zone = &inode->i_sb->u.affs_sb.s_zones[inode->u.affs_i.i_zone];
@@ -868,56 +886,66 @@
 static int
 affs_release_file(struct inode *inode, struct file *filp)
 {
+	struct super_block *sb = inode->i_sb;
 	struct affs_zone	*zone;
 
 	pr_debug("AFFS: release_file(ino=%lu)\n",inode->i_ino);
 
 	if (filp->f_mode & 2) {		/* Free preallocated blocks */
-		while (inode->u.affs_i.i_pa_cnt) {
-			affs_free_block(inode->i_sb,
-					inode->u.affs_i.i_data[inode->u.affs_i.i_pa_next++]);
-			inode->u.affs_i.i_pa_next &= AFFS_MAX_PREALLOC - 1;
-			inode->u.affs_i.i_pa_cnt--;
-		}
+		affs_free_prealloc(inode);
 		if (inode->u.affs_i.i_zone) {
-			lock_super(inode->i_sb);
-			zone = &inode->i_sb->u.affs_sb.s_zones[inode->u.affs_i.i_zone];
+			zone = &sb->u.affs_sb.s_zones[inode->u.affs_i.i_zone];
 			if (zone->z_ino == inode->i_ino)
 				zone->z_ino = 0;
-			unlock_super(inode->i_sb);
 		}
 	}
 	return 0;
 }
 
+/*
+ * Called only when we need to allocate the extension cache.
+ */
 static int
 alloc_ext_cache(struct inode *inode)
 {
 	s32	 key;
 	int	 i;
+	unsigned long cache_page;
+	int      error = 0;
 
 	pr_debug("AFFS: alloc_ext_cache(ino=%lu)\n",inode->i_ino);
 
-	lock_super(inode->i_sb);
-	if (!inode->u.affs_i.i_ec) {
-		inode->u.affs_i.i_ec = (struct ext_cache *)get_free_page(GFP_KERNEL);
-		if (inode->u.affs_i.i_ec) {
-			/* We only have to initialize non-zero values.
-			 * get_free_page() zeroed the page already.
-			 */
-			key = inode->u.affs_i.i_original ? inode->u.affs_i.i_original : inode->i_ino;
-			inode->u.affs_i.i_ec->ec[0] = key;
-			for (i = 0; i < 4; i++) {
-				inode->u.affs_i.i_ec->kc[i].kc_this_key = key;
-				inode->u.affs_i.i_ec->kc[i].kc_last     = -1;
-			}
-		}
-	}
-	unlock_super(inode->i_sb);
-
-	if (!inode->u.affs_i.i_ec) {
-		affs_error(inode->i_sb,"alloc_ext_cache","Cache allocation failed");
-		return -ENOMEM;
-	}
-	return 0;
+	cache_page = get_free_page(GFP_KERNEL);
+	/*
+	 * Check whether somebody else allocated it for us ...
+	 */
+	if (inode->u.affs_i.i_ec)
+		goto out_free;
+	if (!cache_page)
+		goto out_error;
+
+	inode->u.affs_i.i_ec = (struct ext_cache *) cache_page;
+	/* We only have to initialize non-zero values.
+	 * get_free_page() zeroed the page already.
+	 */
+	key = inode->u.affs_i.i_original;
+	if (!inode->u.affs_i.i_original)
+		key = inode->i_ino;
+	inode->u.affs_i.i_ec->ec[0] = key;
+	for (i = 0; i < 4; i++) {
+		inode->u.affs_i.i_ec->kc[i].kc_this_key = key;
+		inode->u.affs_i.i_ec->kc[i].kc_last     = -1;
+	}
+out:
+	return error;
+
+out_free:
+	if (cache_page)
+		free_page(cache_page);
+	goto out;
+
+out_error:
+	affs_error(inode->i_sb,"alloc_ext_cache","Cache allocation failed");
+	error = -ENOMEM;
+	goto out;
 }
diff -u --recursive --new-file v2.1.88/linux/fs/affs/inode.c linux/fs/affs/inode.c
--- v2.1.88/linux/fs/affs/inode.c	Sun Jan  4 00:53:42 1998
+++ linux/fs/affs/inode.c	Mon Feb 23 22:01:26 1998
@@ -229,36 +229,40 @@
 
 	error = inode_change_ok(inode,attr);
 	if (error)
-		return error;
+		goto out;
 
 	if (((attr->ia_valid & ATTR_UID) && (inode->i_sb->u.affs_sb.s_flags & SF_SETUID)) ||
 	    ((attr->ia_valid & ATTR_GID) && (inode->i_sb->u.affs_sb.s_flags & SF_SETGID)) ||
 	    ((attr->ia_valid & ATTR_MODE) &&
-	     (inode->i_sb->u.affs_sb.s_flags & (SF_SETMODE | SF_IMMUTABLE))))
-		error = -EPERM;
-
-	if (error)
-		return (inode->i_sb->u.affs_sb.s_flags & SF_QUIET) ? 0 : error;
+	     (inode->i_sb->u.affs_sb.s_flags & (SF_SETMODE | SF_IMMUTABLE)))) {
+		if (!(inode->i_sb->u.affs_sb.s_flags & SF_QUIET))
+			error = -EPERM;
+		goto out;
+	}
 
 	if (attr->ia_valid & ATTR_MODE)
 		inode->u.affs_i.i_protect = mode_to_prot(attr->ia_mode);
 
-	inode_setattr(inode,attr);
-
-	return 0;
+	inode_setattr(inode, attr);
+	mark_inode_dirty(inode);
+	error = 0;
+out:
+	return error;
 }
 
 void
 affs_put_inode(struct inode *inode)
 {
-	pr_debug("AFFS: put_inode(ino=%lu, nlink=%u)\n",inode->i_ino,inode->i_nlink);
-	lock_super(inode->i_sb);
-	if (inode->u.affs_i.i_ec) {
-		pr_debug("AFFS: freeing ext cache\n");
-		free_page((unsigned long)inode->u.affs_i.i_ec);
-		inode->u.affs_i.i_ec = NULL;
+	pr_debug("AFFS: put_inode(ino=%lu, nlink=%u)\n",
+		inode->i_ino,inode->i_nlink);
+	if (inode->i_count == 1) {
+		unsigned long cache_page = (unsigned long) inode->u.affs_i.i_ec;
+		if (cache_page) {
+			pr_debug("AFFS: freeing ext cache\n");
+			inode->u.affs_i.i_ec = NULL;
+			free_page(cache_page);
+		}
 	}
-	unlock_super(inode->i_sb);
 }
 
 void
diff -u --recursive --new-file v2.1.88/linux/fs/affs/namei.c linux/fs/affs/namei.c
--- v2.1.88/linux/fs/affs/namei.c	Thu Feb 12 20:56:10 1998
+++ linux/fs/affs/namei.c	Mon Feb 23 22:01:26 1998
@@ -21,23 +21,17 @@
 
 #include <linux/errno.h>
 
-/* Simple toupper()/tolower() for DOS\1 */
+/* Simple toupper() for DOS\1 */
 
-static inline unsigned int
+static unsigned int
 affs_toupper(unsigned int ch)
 {
 	return ch >= 'a' && ch <= 'z' ? ch -= ('a' - 'A') : ch;
 }
 
-static inline unsigned int
-affs_tolower(unsigned int ch)
-{
-	return ch >= 'A' && ch <= 'Z' ? ch + ('a' - 'A') : ch;
-}
+/* International toupper() for DOS\3 ("international") */
 
-/* International toupper()/tolower() for DOS\3 ("international") */
-
-static inline unsigned int
+static unsigned int
 affs_intl_toupper(unsigned int ch)
 {
 	return (ch >= 'a' && ch <= 'z') || (ch >= 0xE0
@@ -45,23 +39,8 @@
 		ch - ('a' - 'A') : ch;
 }
 
-static inline unsigned int
-affs_intl_tolower(unsigned int ch)
-{
-	return (ch >= 'A' && ch <= 'Z') || (ch >= 0xC0
-		&& ch <= 0xDE && ch != 0xD7) ?
-		ch + ('a' - 'A') : ch;
-}
-
-/* We need 2 sets of dentry operations, since we cannot
- * determine the fs flavour in the callback routines.
- */
-
 static int	 affs_hash_dentry(struct dentry *, struct qstr *);
 static int       affs_compare_dentry(struct dentry *, struct qstr *, struct qstr *);
-static int	 affs_hash_dentry_intl(struct dentry *, struct qstr *);
-static int       affs_compare_dentry_intl(struct dentry *, struct qstr *, struct qstr *);
-
 struct dentry_operations affs_dentry_operations = {
 	NULL,			/* d_validate	*/
 	affs_hash_dentry,	/* d_hash	*/
@@ -69,24 +48,25 @@
 	NULL			/* d_delete	*/
 };
 
-struct dentry_operations affs_dentry_operations_intl = {
-	NULL,				/* d_validate	*/
-	affs_hash_dentry_intl,		/* d_hash	*/
-	affs_compare_dentry_intl,	/* d_compare	*/
-	NULL				/* d_delete	*/
-};
-
+/*
+ * Note: the dentry argument is the parent dentry.
+ */
 static int
 affs_hash_dentry(struct dentry *dentry, struct qstr *qstr)
 {
+	unsigned int (*toupper)(unsigned int) = affs_toupper;
 	unsigned long	 hash;
 	int		 i;
 
 	if ((i = affs_check_name(qstr->name,qstr->len)))
 		return i;
+
+	/* Check whether to use the international 'toupper' routine */
+	if (AFFS_I2FSTYPE(dentry->d_inode))
+		toupper = affs_intl_toupper;
 	hash = init_name_hash();
 	for (i = 0; i < qstr->len && i < 30; i++)
-		hash = partial_name_hash(affs_tolower(qstr->name[i]),hash);
+		hash = partial_name_hash(toupper(qstr->name[i]), hash);
 	qstr->hash = end_name_hash(hash);
 
 	return 0;
@@ -95,6 +75,9 @@
 static int
 affs_compare_dentry(struct dentry *dentry, struct qstr *a, struct qstr *b)
 {
+	unsigned int (*toupper)(unsigned int) = affs_toupper;
+	int	 alen = a->len;
+	int      blen = b->len;
 	int	 i;
 
 	/* 'a' is the qstr of an already existing dentry, so the name
@@ -107,45 +90,19 @@
 	/* If the names are longer than the allowed 30 chars,
 	 * the excess is ignored, so their length may differ.
 	 */
-
-	if ((a->len < 30 || b->len < 30) && a->len != b->len)
+	if (alen > 30)
+		alen = 30;
+	if (blen > 30)
+		blen = 30;
+	if (alen != blen)
 		return 1;
 
-	for (i = 0; i < a->len && i < 30; i++)
-		if (affs_tolower(a->name[i]) != affs_tolower(b->name[i]))
-			return 1;
-	
-	return 0;
-}
-
-static int
-affs_hash_dentry_intl(struct dentry *dentry, struct qstr *qstr)
-{
-	unsigned long	 hash;
-	int		 i;
+	/* Check whether to use the international 'toupper' routine */
+	if (AFFS_I2FSTYPE(dentry->d_inode))
+		toupper = affs_intl_toupper;
 
-	if ((i = affs_check_name(qstr->name,qstr->len)))
-		return i;
-	hash = init_name_hash();
-	for (i = 0; i < qstr->len && i < 30; i++)
-		hash = partial_name_hash(affs_intl_tolower(qstr->name[i]),hash);
-	qstr->hash = end_name_hash(hash);
-
-	return 0;
-}
-
-static int
-affs_compare_dentry_intl(struct dentry *dentry, struct qstr *a, struct qstr *b)
-{
-	int	 i;
-
-	if (affs_check_name(b->name,b->len))
-		return 1;
-	if ((a->len < 30 || b->len < 30) && a->len != b->len)
-		return 1;
-
-	for (i = 0; i < a->len && i < 30; i++)
-		if (affs_intl_tolower(a->name[i]) != affs_intl_tolower(b->name[i]))
+	for (i = 0; i < alen; i++)
+		if (toupper(a->name[i]) != toupper(b->name[i]))
 			return 1;
 	
 	return 0;
@@ -158,6 +115,9 @@
 static int
 affs_match(const unsigned char *name, int len, const unsigned char *compare, int dlen, int intl)
 {
+	unsigned int	(*toupper)(unsigned int) = intl ? affs_intl_toupper : affs_toupper;
+	int		  i;
+
 	if (!compare)
 		return 0;
 
@@ -171,21 +131,9 @@
 		return 1;
 	if (dlen != len)
 		return 0;
-	if (intl) {
-		while (dlen--) {
-			if (affs_intl_toupper(*name) != affs_intl_toupper(*compare))
-				return 0;
-			name++;
-			compare++;
-		}
-	} else {
-		while (dlen--) {
-			if (affs_toupper(*name) != affs_toupper(*compare))
-				return 0;
-			name++;
-			compare++;
-		}
-	}
+	for (i = 0; i < len; i++)
+		if (toupper(name[i]) != toupper(compare[i]))
+			return 0;
 	return 1;
 }
 
@@ -211,23 +159,22 @@
 affs_find_entry(struct inode *dir, struct dentry *dentry, unsigned long *ino)
 {
 	struct buffer_head	*bh;
-	int			 intl;
+	int			 intl = AFFS_I2FSTYPE(dir);
 	s32			 key;
 	const char		*name = dentry->d_name.name;
 	int			 namelen = dentry->d_name.len;
 
 	pr_debug("AFFS: find_entry(\"%.*s\")\n",namelen,name);
 
-	intl = AFFS_I2FSTYPE(dir);
-	bh   = affs_bread(dir->i_dev,dir->i_ino,AFFS_I2BSIZE(dir));
+	bh = affs_bread(dir->i_dev,dir->i_ino,AFFS_I2BSIZE(dir));
 	if (!bh)
 		return NULL;
 
-	if (affs_match(name,namelen,".",1,intl)) {
+	if (namelen == 1 && name[0] == '.') {
 		*ino = dir->i_ino;
 		return bh;
 	}
-	if (affs_match(name,namelen,"..",2,intl)) {
+	if (namelen == 2 && name[0] == '.' && name[1] == '.') {
 		*ino = affs_parent_ino(dir);
 		return bh;
 	}
@@ -239,10 +186,9 @@
 		int cnamelen;
 
 		affs_brelse(bh);
-		if (key == 0) {
-			bh = NULL;
+		bh = NULL;
+		if (key == 0)
 			break;
-		}
 		bh = affs_bread(dir->i_dev,key,AFFS_I2BSIZE(dir));
 		if (!bh)
 			break;
@@ -274,8 +220,7 @@
 		if (!inode)
 			return -EACCES;
 	}
-	dentry->d_op = AFFS_I2FSTYPE(dir) ? &affs_dentry_operations_intl
-					  : &affs_dentry_operations;
+	dentry->d_op = &affs_dentry_operations;
 	d_add(dentry,inode);
 	return 0;
 }
@@ -291,10 +236,7 @@
 	pr_debug("AFFS: unlink(dir=%ld,\"%.*s\")\n",dir->i_ino,
 		 (int)dentry->d_name.len,dentry->d_name.name);
 
-	bh      = NULL;
 	retval  = -ENOENT;
-	if (!dir)
-		goto unlink_done;
 	if (!(bh = affs_find_entry(dir,dentry,&ino)))
 		goto unlink_done;
 
@@ -312,8 +254,10 @@
 	inode->i_nlink = retval;
 	inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
 	mark_inode_dirty(inode);
-	retval = 0;
 	d_delete(dentry);
+	mark_inode_dirty(dir);
+	retval = 0;
+
 unlink_done:
 	affs_brelse(bh);
 	return retval;
@@ -328,11 +272,10 @@
 	pr_debug("AFFS: create(%lu,\"%.*s\",0%o)\n",dir->i_ino,(int)dentry->d_name.len,
 		 dentry->d_name.name,mode);
 
-	if (!dir)
-		return -ENOENT;
+	error = -ENOSPC;
 	inode = affs_new_inode(dir);
 	if (!inode)
-		return -ENOSPC;
+		goto out;
 
 	pr_debug(" -- ino=%lu\n",inode->i_ino);
 	if (dir->i_sb->u.affs_sb.s_flags & SF_OFS)
@@ -341,19 +284,22 @@
 		inode->i_op = &affs_file_inode_operations;
 
 	error = affs_add_entry(dir,NULL,inode,dentry,ST_FILE);
-	if (error) {
-		inode->i_nlink = 0;
-		mark_inode_dirty(inode);
-		iput(inode);
-		return error;
-	}
+	if (error)
+		goto out_iput;
 	inode->i_mode = mode;
 	inode->u.affs_i.i_protect = mode_to_prot(inode->i_mode);
+	d_instantiate(dentry,inode);
+	mark_inode_dirty(inode);
 	dir->i_version = ++event;
 	mark_inode_dirty(dir);
-	d_instantiate(dentry,inode);
+out:
+	return error;
 
-	return 0;
+out_iput:
+	inode->i_nlink = 0;
+	mark_inode_dirty(inode);
+	iput(inode);
+	goto out;
 }
 
 int
@@ -365,25 +311,29 @@
 	pr_debug("AFFS: mkdir(%lu,\"%.*s\",0%o)\n",dir->i_ino,
 		 (int)dentry->d_name.len,dentry->d_name.name,mode);
 
+	error = -ENOSPC;
 	inode = affs_new_inode(dir);
 	if (!inode)
-		return -ENOSPC;
+		goto out;
 
 	inode->i_op = &affs_dir_inode_operations;
 	error       = affs_add_entry(dir,NULL,inode,dentry,ST_USERDIR);
-	if (error) {
-		inode->i_nlink = 0;
-		mark_inode_dirty(inode);
-		iput(inode);
-		return error;
-	}
+	if (error)
+		goto out_iput;
 	inode->i_mode = S_IFDIR | (mode & 0777 & ~current->fs->umask);
 	inode->u.affs_i.i_protect = mode_to_prot(inode->i_mode);
+	d_instantiate(dentry,inode);
+	mark_inode_dirty(inode);
 	dir->i_version = ++event;
 	mark_inode_dirty(dir);
-	d_instantiate(dentry,inode);
+out:
+	return error;
 
-	return 0;
+out_iput:
+	inode->i_nlink = 0;
+	mark_inode_dirty(inode);
+	iput(inode);
+	goto out;
 }
 
 static int
@@ -399,24 +349,18 @@
 int
 affs_rmdir(struct inode *dir, struct dentry *dentry)
 {
+	struct inode		*inode = dentry->d_inode;
 	int			 retval;
 	unsigned long		 ino;
-	struct inode		*inode;
 	struct buffer_head	*bh;
 
 	pr_debug("AFFS: rmdir(dir=%lu,\"%.*s\")\n",dir->i_ino,
 		 (int)dentry->d_name.len,dentry->d_name.name);
 
-	inode  = NULL;
-	bh     = NULL;
 	retval = -ENOENT;
-	if (!dir)
-		goto rmdir_done;
 	if (!(bh = affs_find_entry(dir,dentry,&ino)))
 		goto rmdir_done;
 
-	inode = dentry->d_inode;
-
 	retval = -EPERM;
         if (current->fsuid != inode->i_uid &&
             current->fsuid != dir->i_uid && !fsuser())
@@ -425,31 +369,31 @@
 		goto rmdir_done;
 	if (inode == dir)	/* we may not delete ".", but "../dir" is ok */
 		goto rmdir_done;
-	if (!S_ISDIR(inode->i_mode)) {
-		retval = -ENOTDIR;
+	retval = -ENOTDIR;
+	if (!S_ISDIR(inode->i_mode))
 		goto rmdir_done;
-	}
-	down(&inode->i_sem);
-	if (dentry->d_count > 1) {
+	/*
+	 * Make sure the directory is empty and the dentry isn't busy.
+	 */
+	if (dentry->d_count > 1)
 		shrink_dcache_parent(dentry);
-	}
-	up(&inode->i_sem);
-	if (!empty_dir(bh,AFFS_I2HSIZE(inode))) {
-		retval = -ENOTEMPTY;
+	retval = -ENOTEMPTY;
+	if (!empty_dir(bh,AFFS_I2HSIZE(inode)))
 		goto rmdir_done;
-	}
-	if (inode->i_count > 1) {
-		retval = -EBUSY;
+	retval = -EBUSY;
+	if (dentry->d_count > 1)
 		goto rmdir_done;
-	}
+
 	if ((retval = affs_remove_header(bh,inode)) < 0)
 		goto rmdir_done;
 	
 	inode->i_nlink = retval;
 	inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
 	retval         = 0;
+	mark_inode_dirty(dir);
 	mark_inode_dirty(inode);
 	d_delete(dentry);
+
 rmdir_done:
 	affs_brelse(bh);
 	return retval;
@@ -462,27 +406,25 @@
 	struct inode		*inode;
 	char			*p;
 	unsigned long		 tmp;
-	int			 i, maxlen;
+	int			 i, maxlen, error;
 	char			 c, lc;
 
 	pr_debug("AFFS: symlink(%lu,\"%.*s\" -> \"%s\")\n",dir->i_ino,
 		 (int)dentry->d_name.len,dentry->d_name.name,symname);
 	
 	maxlen = 4 * AFFS_I2HSIZE(dir) - 1;
+	error = -ENOSPC;
 	inode  = affs_new_inode(dir);
 	if (!inode)
-		return -ENOSPC;
+		goto out;
 
 	inode->i_op   = &affs_symlink_inode_operations;
 	inode->i_mode = S_IFLNK | 0777;
 	inode->u.affs_i.i_protect = mode_to_prot(inode->i_mode);
+	error = -EIO;
 	bh = affs_bread(inode->i_dev,inode->i_ino,AFFS_I2BSIZE(inode));
-	if (!bh) {
-		inode->i_nlink = 0;
-		mark_inode_dirty(inode);
-		iput(inode);
-		return -EIO;
-	}
+	if (!bh)
+		goto out_iput;
 	i  = 0;
 	p  = ((struct slink_front *)bh->b_data)->symname;
 	lc = '/';
@@ -514,25 +456,30 @@
 	mark_buffer_dirty(bh,1);
 	affs_brelse(bh);
 	mark_inode_dirty(inode);
+
+	/* N.B. This test shouldn't be necessary ... dentry must be negative */
+	error = -EEXIST;
 	bh = affs_find_entry(dir,dentry,&tmp);
-	if (bh) {
-		inode->i_nlink = 0;
-		iput(inode);
-		affs_brelse(bh);
-		return -EEXIST;
-	}
-	i = affs_add_entry(dir,NULL,inode,dentry,ST_SOFTLINK);
-	if (i) {
-		inode->i_nlink = 0;
-		mark_inode_dirty(inode);
-		iput(inode);
-		affs_brelse(bh);
-		return i;
-	}
-	dir->i_version = ++event;
+	if (bh)
+		goto out_release;
+	/* N.B. Shouldn't we add the entry before dirtying the buffer? */
+	error = affs_add_entry(dir,NULL,inode,dentry,ST_SOFTLINK);
+	if (error)
+		goto out_release;
 	d_instantiate(dentry,inode);
-	
-	return 0;
+	dir->i_version = ++event;
+	mark_inode_dirty(dir);
+
+out:
+	return error;
+
+out_release:
+	affs_brelse(bh);
+out_iput:
+	inode->i_nlink = 0;
+	mark_inode_dirty(inode);
+	iput(inode);
+	goto out;
 }
 
 int
@@ -547,6 +494,7 @@
 	pr_debug("AFFS: link(%lu,%lu,\"%.*s\")\n",oldinode->i_ino,dir->i_ino,
 		 (int)dentry->d_name.len,dentry->d_name.name);
 
+	/* N.B. Do we need this test? The dentry must be negative ... */
 	bh = affs_find_entry(dir,dentry,&i);
 	if (bh) {
 		affs_brelse(bh);
@@ -556,8 +504,9 @@
 		affs_warning(dir->i_sb,"link","Impossible link to link");
 		return -EINVAL;
 	}
+	error = -ENOSPC;
 	if (!(inode = affs_new_inode(dir)))
-		return -ENOSPC;
+		goto out;
 
 	inode->i_op                = oldinode->i_op;
 	inode->u.affs_i.i_protect  = mode_to_prot(oldinode->i_mode);
@@ -573,6 +522,7 @@
 		inode->i_nlink = 0;
 	else {
 		dir->i_version = ++event;
+		mark_inode_dirty(dir);
 		mark_inode_dirty(oldinode);
 		oldinode->i_count++;
 		d_instantiate(dentry,oldinode);
@@ -580,6 +530,7 @@
 	mark_inode_dirty(inode);
 	iput(inode);
 
+out:
 	return error;
 }
 
@@ -600,7 +551,7 @@
 		 new_dir->i_ino,new_dentry->d_name.len,new_dentry->d_name.name,new_inode);
 	
 	if ((retval = affs_check_name(new_dentry->d_name.name,new_dentry->d_name.len)))
-		return retval;
+		goto out;
 
 	new_bh = NULL;
 	retval = -ENOENT;
@@ -630,8 +581,10 @@
 		if (!S_ISDIR(old_inode->i_mode))
 			goto end_rename;
 		retval = -EINVAL;
-		if (is_subdir(new_dentry,old_dentry))
+		if (is_subdir(new_dentry, old_dentry))
 			goto end_rename;
+		if (new_dentry->d_count > 1)
+			shrink_dcache_parent(new_dentry);
 		retval = -ENOTEMPTY;
 		if (!empty_dir(new_bh,AFFS_I2HSIZE(new_inode)))
 			goto end_rename;
@@ -644,7 +597,7 @@
 		if (new_inode && !S_ISDIR(new_inode->i_mode))
 			goto end_rename;
 		retval = -EINVAL;
-		if (is_subdir(new_dentry,old_dentry))
+		if (is_subdir(new_dentry, old_dentry))
 			goto end_rename;
 		if (affs_parent_ino(old_inode) != old_dir->i_ino)
 			goto end_rename;
@@ -681,6 +634,6 @@
 end_rename:
 	affs_brelse(old_bh);
 	affs_brelse(new_bh);
-
+out:
 	return retval;
 }
diff -u --recursive --new-file v2.1.88/linux/fs/affs/super.c linux/fs/affs/super.c
--- v2.1.88/linux/fs/affs/super.c	Tue Jan  6 13:33:29 1998
+++ linux/fs/affs/super.c	Mon Feb 23 22:01:26 1998
@@ -62,12 +62,10 @@
 	kfree(sb->u.affs_sb.s_bitmap);
 	affs_brelse(sb->u.affs_sb.s_root_bh);
 
-	/* I'm not happy with this. It would be better to save the previous
-	 * value of this devices blksize_size[][] in the super block and
-	 * restore it here, but with the affs superblock being quite large
-	 * already ...
+	/*
+	 * Restore the previous value of this device's blksize_size[][]
 	 */
-	set_blocksize(sb->s_dev,BLOCK_SIZE);
+	set_blocksize(sb->s_dev, sb->u.affs_sb.s_blksize);
 
 	sb->s_dev = 0;
 	unlock_super(sb);
@@ -100,7 +98,7 @@
 	} else
 		sb->s_dirt = 0;
 
-	pr_debug("AFFS: write_super() at %d, clean=%d\n",CURRENT_TIME,clean);
+	pr_debug("AFFS: write_super() at %lu, clean=%d\n", CURRENT_TIME, clean);
 }
 
 static struct super_operations affs_sops = {
@@ -119,7 +117,7 @@
 parse_options(char *options, uid_t *uid, gid_t *gid, int *mode, int *reserved, s32 *root,
 		int *blocksize, char **prefix, char *volume, unsigned long *mount_opts)
 {
-	char	*this_char, *value;
+	char	*this_char, *value, *optn;
 	int	 f;
 
 	/* Fill in defaults */
@@ -138,18 +136,18 @@
 		f = 0;
 		if ((value = strchr(this_char,'=')) != NULL)
 			*value++ = 0;
-		if (!strcmp(this_char,"protect")) {
-			if (value) {
-				printk("AFFS: Option protect does not take an argument\n");
-				return 0;
-			}
+		if ((optn = "protect") && !strcmp(this_char, optn)) {
+			if (value)
+				goto out_inv_arg;
 			*mount_opts |= SF_IMMUTABLE;
-		} else if (!strcmp(this_char,"verbose")) {
-			if (value) {
-				printk("AFFS: Option verbose does not take an argument\n");
-				return 0;
-			}
+		} else if ((optn = "verbose") && !strcmp(this_char, optn)) {
+			if (value)
+				goto out_inv_arg;
 			*mount_opts |= SF_VERBOSE;
+		} else if ((optn = "mufs") && !strcmp(this_char, optn)) {
+			if (value)
+				goto out_inv_arg;
+			*mount_opts |= SF_MUFS;
 		} else if ((f = !strcmp(this_char,"setuid")) || !strcmp(this_char,"setgid")) {
 			if (value) {
 				if (!*value) {
@@ -165,55 +163,51 @@
 				}
 			}
 		} else if (!strcmp(this_char,"prefix")) {
-			if (!value || !*value) {
-				printk("AFFS: The prefix option requires an argument\n");
-				return 0;
-			}
-			if (*prefix)		/* Free any previous prefix */
+			optn = "prefix";
+			if (!value || !*value)
+				goto out_no_arg;
+			if (*prefix) {		/* Free any previous prefix */
 				kfree(*prefix);
+				*prefix = NULL;
+			}
 			*prefix = kmalloc(strlen(value) + 1,GFP_KERNEL);
 			if (!*prefix)
 				return 0;
 			strcpy(*prefix,value);
 			*mount_opts |= SF_PREFIX;
 		} else if (!strcmp(this_char,"volume")) {
-			if (!value || !*value) {
-				printk("AFFS: The volume option requires an argument\n");
-				return 0;
-			}
+			optn = "volume";
+			if (!value || !*value)
+				goto out_no_arg;
 			if (strlen(value) > 30)
 				value[30] = 0;
 			strncpy(volume,value,30);
 		} else if (!strcmp(this_char,"mode")) {
-			if (!value || !*value) {
-				printk("AFFS: The mode option requires an argument\n");
-				return 0;
-			}
+			optn = "mode";
+			if (!value || !*value)
+				goto out_no_arg;
 			*mode = simple_strtoul(value,&value,8) & 0777;
 			if (*value)
 				return 0;
 			*mount_opts |= SF_SETMODE;
 		} else if (!strcmp(this_char,"reserved")) {
-			if (!value || !*value) {
-				printk("AFFS: The reserved option requires an argument\n");
-				return 0;
-			}
+			optn = "reserved";
+			if (!value || !*value)
+				goto out_no_arg;
 			*reserved = simple_strtoul(value,&value,0);
 			if (*value)
 				return 0;
 		} else if (!strcmp(this_char,"root")) {
-			if (!value || !*value) {
-				printk("AFFS: The root option requires an argument\n");
-				return 0;
-			}
+			optn = "root";
+			if (!value || !*value)
+				goto out_no_arg;
 			*root = simple_strtoul(value,&value,0);
 			if (*value)
 				return 0;
 		} else if (!strcmp(this_char,"bs")) {
-			if (!value || !*value) {
-				printk("AFFS: The bs option requires an argument\n");
-				return 0;
-			}
+			optn = "bs";
+			if (!value || !*value)
+				goto out_no_arg;
 			*blocksize = simple_strtoul(value,&value,0);
 			if (*value)
 				return 0;
@@ -234,6 +228,13 @@
 		}
 	}
 	return 1;
+
+out_no_arg:
+	printk("AFFS: The %s option requires an argument\n", optn);
+	return 0;
+out_inv_arg:
+	printk("AFFS: Option %s does not take an argument\n", optn);
+	return 0;
 }
 
 /* This function definitely needs to be split up. Some fine day I'll
@@ -241,14 +242,14 @@
  */
 
 static struct super_block *
-affs_read_super(struct super_block *s,void *data, int silent)
+affs_read_super(struct super_block *s, void *data, int silent)
 {
 	struct buffer_head	*bh = NULL;
 	struct buffer_head	*bb;
 	struct inode		*root_inode;
 	kdev_t			 dev = s->s_dev;
 	s32			 root_block;
-	int			 size;
+	int			 blocks, size, blocksize;
 	u32			 chksum;
 	u32			*bm;
 	s32			 ptype, stype;
@@ -256,7 +257,6 @@
 	int			 num_bm;
 	int			 i, j;
 	s32			 key;
-	int			 blocksize;
 	uid_t			 uid;
 	gid_t			 gid;
 	int			 reserved;
@@ -268,57 +268,55 @@
 	pr_debug("affs_read_super(%s)\n",data ? (const char *)data : "no options");
 
 	MOD_INC_USE_COUNT;
-
-	s->u.affs_sb.s_prefix = NULL;
-	if (!parse_options(data,&uid,&gid,&i,&reserved,&root_block,
-	    &blocksize,&s->u.affs_sb.s_prefix,s->u.affs_sb.s_volume,&mount_flags)) {
-		s->s_dev = 0;
-		printk(KERN_ERR "AFFS: Error parsing options\n");
-		MOD_DEC_USE_COUNT;
-		return NULL;
-	}
 	lock_super(s);
-
-	/* Get the size of the device in 512-byte blocks.
-	 * If we later see that the partition uses bigger
-	 * blocks, we will have to change it.
-	 */
-
-	size = blksize_size[MAJOR(dev)][MINOR(dev)];
-	size = (size ? size : BLOCK_SIZE) / 512 * blk_size[MAJOR(dev)][MINOR(dev)];
-
+	s->s_magic             = AFFS_SUPER_MAGIC;
+	s->s_op                = &affs_sops;
 	s->u.affs_sb.s_bitmap  = NULL;
 	s->u.affs_sb.s_root_bh = NULL;
+	s->u.affs_sb.s_prefix  = NULL;
+	s->u.affs_sb.s_hashsize= 0;
+
+	if (!parse_options(data,&uid,&gid,&i,&reserved,&root_block,
+				&blocksize,&s->u.affs_sb.s_prefix,
+				s->u.affs_sb.s_volume, &mount_flags))
+		goto out_bad_opts;
+	/* N.B. after this point s_prefix must be released */
+
 	s->u.affs_sb.s_flags   = mount_flags;
 	s->u.affs_sb.s_mode    = i;
 	s->u.affs_sb.s_uid     = uid;
 	s->u.affs_sb.s_gid     = gid;
+	s->u.affs_sb.s_reserved= reserved;
 
-	if (size == 0) {
-		s->s_dev = 0;
-		unlock_super(s);
-		printk(KERN_ERR "AFFS: Could not determine device size\n");
-		goto out;
-	}
-	s->u.affs_sb.s_partition_size = size;
-	s->u.affs_sb.s_reserved       = reserved;
+	/* Get the size of the device in 512-byte blocks.
+	 * If we later see that the partition uses bigger
+	 * blocks, we will have to change it.
+	 */
+
+	blocks = blk_size[MAJOR(dev)][MINOR(dev)];
+	if (blocks == 0)
+		goto out_bad_size;
+	s->u.affs_sb.s_blksize = blksize_size[MAJOR(dev)][MINOR(dev)];
+	if (!s->u.affs_sb.s_blksize)
+		s->u.affs_sb.s_blksize = BLOCK_SIZE;
+	size = (s->u.affs_sb.s_blksize / 512) * blocks;
+	pr_debug("AFFS: initial blksize=%d, blocks=%d\n",
+		s->u.affs_sb.s_blksize, blocks);
 
 	/* Try to find root block. Its location depends on the block size. */
 
-	s->u.affs_sb.s_hashsize = 0;
+	i = 512;
+	j = 4096;
 	if (blocksize > 0) {
-		i = blocksize;
-		j = blocksize;
-	} else {
-		i = 512;
-		j = 4096;
+		i = j = blocksize;
+		size = size / (blocksize / 512);
 	}
 	for (blocksize = i, key = 0; blocksize <= j; blocksize <<= 1, size >>= 1) {
+		s->u.affs_sb.s_root_block = root_block;
 		if (root_block < 0)
 			s->u.affs_sb.s_root_block = (reserved + size - 1) / 2;
-		else
-			s->u.affs_sb.s_root_block = root_block;
-		set_blocksize(dev,blocksize);
+		pr_debug("AFFS: setting blocksize to %d\n", blocksize);
+		set_blocksize(dev, blocksize);
 
 		/* The root block location that was calculated above is not
 		 * correct if the partition size is an odd number of 512-
@@ -331,35 +329,31 @@
 		 * block behind the calculated one. So we check this one, too.
 		 */
 		for (num_bm = 0; num_bm < 2; num_bm++) {
-			pr_debug("AFFS: Dev %s - trying bs=%d bytes, root at %u, "
-				 "size=%d blocks, %d reserved\n",kdevname(dev),blocksize,
-				 s->u.affs_sb.s_root_block + num_bm,size,reserved);
-			bh = affs_bread(dev,s->u.affs_sb.s_root_block + num_bm,blocksize);
-			if (!bh) {
-				printk(KERN_ERR "AFFS: Cannot read root block\n");
-				goto out;
-			}
+			pr_debug("AFFS: Dev %s, trying root=%u, bs=%d, "
+				"size=%d, reserved=%d\n",
+				kdevname(dev),
+				s->u.affs_sb.s_root_block + num_bm,
+				blocksize, size, reserved);
+			bh = affs_bread(dev, s->u.affs_sb.s_root_block + num_bm,
+					blocksize);
+			if (!bh)
+				continue;
 			if (!affs_checksum_block(blocksize,bh->b_data,&ptype,&stype) &&
 			    ptype == T_SHORT && stype == ST_ROOT) {
 				s->s_blocksize             = blocksize;
 				s->u.affs_sb.s_hashsize    = blocksize / 4 - 56;
 				s->u.affs_sb.s_root_block += num_bm;
 				key                        = 1;
-				break;
+				goto got_root;
 			}
+			affs_brelse(bh);
+			bh = NULL;
 		}
-		if (key)
-			break;
-		affs_brelse(bh);
-		bh = NULL;
-	}
-	if (!key) {
-		affs_brelse(bh);
-		if (!silent)
-			printk(KERN_ERR "AFFS: Cannot find a valid root block on device %s\n",
-				kdevname(dev));
-		goto out;
 	}
+	goto out_no_valid_block;
+
+	/* N.B. after this point bh must be released */
+got_root:
 	root_block = s->u.affs_sb.s_root_block;
 
 	s->u.affs_sb.s_partition_size   = size;
@@ -369,59 +363,54 @@
 
 	/* Find out which kind of FS we have */
 	bb = affs_bread(dev,0,s->s_blocksize);
-	if (bb) {
-		chksum = be32_to_cpu(*(u32 *)bb->b_data);
-
-		/* Dircache filesystems are compatible with non-dircache ones
-		 * when reading. As long as they aren't supported, writing is
-		 * not recommended.
-		 */
-		if ((chksum == FS_DCFFS || chksum == MUFS_DCFFS || chksum == FS_DCOFS
-		     || chksum == MUFS_DCOFS) && !(s->s_flags & MS_RDONLY)) {
-			printk(KERN_NOTICE "AFFS: Dircache FS - mounting %s read only\n",
-				kdevname(dev));
-			s->s_flags |= MS_RDONLY;
-			s->u.affs_sb.s_flags |= SF_READONLY;
-		}
-		switch (chksum) {
-			case MUFS_FS:
-			case MUFS_INTLFFS:
-				s->u.affs_sb.s_flags |= SF_MUFS;
-				/* fall thru */
-			case FS_INTLFFS:
-				s->u.affs_sb.s_flags |= SF_INTL;
-				break;
-			case MUFS_DCFFS:
-			case MUFS_FFS:
-				s->u.affs_sb.s_flags |= SF_MUFS;
-				break;
-			case FS_DCFFS:
-			case FS_FFS:
-				break;
-			case MUFS_OFS:
-				s->u.affs_sb.s_flags |= SF_MUFS;
-				/* fall thru */
-			case FS_OFS:
-				s->u.affs_sb.s_flags |= SF_OFS;
-				break;
-			case MUFS_DCOFS:
-			case MUFS_INTLOFS:
-				s->u.affs_sb.s_flags |= SF_MUFS;
-			case FS_DCOFS:
-			case FS_INTLOFS:
-				s->u.affs_sb.s_flags |= SF_INTL | SF_OFS;
-				break;
-			default:
-				printk(KERN_ERR "AFFS: Unknown filesystem on device %s: %08X\n",
-					kdevname(dev),chksum);
-				affs_brelse(bb);
-				goto out;
-		}
-		affs_brelse(bb);
-	} else {
-		printk(KERN_ERR "AFFS: Cannot read boot block\n");
-		goto out;
+	if (!bb)
+		goto out_no_root_block;
+	chksum = be32_to_cpu(*(u32 *)bb->b_data);
+	affs_brelse(bb);
+
+	/* Dircache filesystems are compatible with non-dircache ones
+	 * when reading. As long as they aren't supported, writing is
+	 * not recommended.
+	 */
+	if ((chksum == FS_DCFFS || chksum == MUFS_DCFFS || chksum == FS_DCOFS
+	     || chksum == MUFS_DCOFS) && !(s->s_flags & MS_RDONLY)) {
+		printk(KERN_NOTICE "AFFS: Dircache FS - mounting %s read only\n",
+			kdevname(dev));
+		s->s_flags |= MS_RDONLY;
+		s->u.affs_sb.s_flags |= SF_READONLY;
+	}
+	switch (chksum) {
+		case MUFS_FS:
+		case MUFS_INTLFFS:
+			s->u.affs_sb.s_flags |= SF_MUFS;
+			/* fall thru */
+		case FS_INTLFFS:
+			s->u.affs_sb.s_flags |= SF_INTL;
+			break;
+		case MUFS_DCFFS:
+		case MUFS_FFS:
+			s->u.affs_sb.s_flags |= SF_MUFS;
+			break;
+		case FS_DCFFS:
+		case FS_FFS:
+			break;
+		case MUFS_OFS:
+			s->u.affs_sb.s_flags |= SF_MUFS;
+			/* fall thru */
+		case FS_OFS:
+			s->u.affs_sb.s_flags |= SF_OFS;
+			break;
+		case MUFS_DCOFS:
+		case MUFS_INTLOFS:
+			s->u.affs_sb.s_flags |= SF_MUFS;
+		case FS_DCOFS:
+		case FS_INTLOFS:
+			s->u.affs_sb.s_flags |= SF_INTL | SF_OFS;
+			break;
+		default:
+			goto out_unknown_fs;
 	}
+
 	if (mount_flags & SF_VERBOSE) {
 		chksum = cpu_to_be32(chksum);
 		printk(KERN_NOTICE "AFFS: Mounting volume \"%*s\": Type=%.3s\\%c, Blocksize=%d\n",
@@ -430,14 +419,14 @@
 			(char *)&chksum,((char *)&chksum)[3] + '0',blocksize);
 	}
 
-	s->s_magic  = AFFS_SUPER_MAGIC;
 	s->s_flags |= MS_NODEV | MS_NOSUID;
 
 	/* Keep super block in cache */
-	if (!(s->u.affs_sb.s_root_bh = affs_bread(dev,root_block,s->s_blocksize))) {
-		printk(KERN_ERR "AFFS: Cannot read root block\n");
-		goto out;
-	}
+	bb = affs_bread(dev,root_block,s->s_blocksize);
+	if (!bb)
+		goto out_no_root_block;
+	s->u.affs_sb.s_root_bh = bb;
+	/* N.B. after this point s_root_bh must be released */
 
 	/* Allocate space for bitmaps, zones and others */
 
@@ -448,11 +437,10 @@
 		 az_no * sizeof(struct affs_alloc_zone) +
 		 MAX_ZONES * sizeof(struct affs_zone);
 	pr_debug("num_bm=%d, az_no=%d, sum=%d\n",num_bm,az_no,ptype);
-	if (!(s->u.affs_sb.s_bitmap = kmalloc(ptype,GFP_KERNEL))) {
-		printk(KERN_ERR "AFFS: Not enough memory\n");
-		goto out;
-	}
+	if (!(s->u.affs_sb.s_bitmap = kmalloc(ptype, GFP_KERNEL)))
+		goto out_no_bitmap;
 	memset(s->u.affs_sb.s_bitmap,0,ptype);
+	/* N.B. after the point s_bitmap must be released */
 
 	s->u.affs_sb.s_zones   = (struct affs_zone *)&s->u.affs_sb.s_bitmap[num_bm];
 	s->u.affs_sb.s_alloc   = (struct affs_alloc_zone *)&s->u.affs_sb.s_zones[MAX_ZONES];
@@ -490,91 +478,79 @@
 				s->u.affs_sb.s_flags |= SF_READONLY;
 				continue;
 			}
-			bb = affs_bread(s->s_dev,be32_to_cpu(bm[i]),s->s_blocksize);
-			if (bb) {
-				if (affs_checksum_block(s->s_blocksize,bb->b_data,NULL,NULL) &&
-				    !(s->s_flags & MS_RDONLY)) {
-					printk(KERN_WARNING "AFFS: Bitmap (%d,key=%u) invalid - "
-					       "mounting %s read only.\n",mapidx,be32_to_cpu(bm[i]),
-						kdevname(dev));
-					s->s_flags |= MS_RDONLY;
-					s->u.affs_sb.s_flags |= SF_READONLY;
-				}
-				/* Mark unused bits in the last word as allocated */
-				if (size <= s->s_blocksize * 8 - 32) {	/* last bitmap */
-					ptype = size / 32 + 1;		/* word number */
-					key   = size & 0x1F;		/* used bits */
-					if (key && !(s->s_flags & MS_RDONLY)) {
-						chksum = cpu_to_be32(0x7FFFFFFF >> (31 - key));
-						((u32 *)bb->b_data)[ptype] &= chksum;
-						affs_fix_checksum(s->s_blocksize,bb->b_data,0);
-						mark_buffer_dirty(bb,1);
-						bmalt = 1;
-					}
-					ptype = (size + 31) & ~0x1F;
-					size  = 0;
-					s->u.affs_sb.s_flags |= SF_BM_VALID;
-				} else {
-					ptype = s->s_blocksize * 8 - 32;
-					size -= ptype;
-				}
-				s->u.affs_sb.s_bitmap[mapidx].bm_firstblk = offset;
-				s->u.affs_sb.s_bitmap[mapidx].bm_bh       = NULL;
-				s->u.affs_sb.s_bitmap[mapidx].bm_key      = be32_to_cpu(bm[i]);
-				s->u.affs_sb.s_bitmap[mapidx].bm_count    = 0;
-				offset += ptype;
-
-				for (j = 0; ptype > 0; j++, az_no++, ptype -= key) {
-					key = MIN(ptype,AFFS_ZONE_SIZE);	/* size in bits */
-					s->u.affs_sb.s_alloc[az_no].az_size = key / 32;
-					s->u.affs_sb.s_alloc[az_no].az_free =
-						affs_count_free_bits(key / 8,bb->b_data +
-								     j * (AFFS_ZONE_SIZE / 8) + 4);
+			bb = affs_bread(dev,be32_to_cpu(bm[i]),s->s_blocksize);
+			if (!bb)
+				goto out_no_read_bm;
+			if (affs_checksum_block(s->s_blocksize,bb->b_data,NULL,NULL) &&
+			    !(s->s_flags & MS_RDONLY)) {
+				printk(KERN_WARNING "AFFS: Bitmap (%d,key=%u) invalid - "
+				       "mounting %s read only.\n",mapidx,be32_to_cpu(bm[i]),
+					kdevname(dev));
+				s->s_flags |= MS_RDONLY;
+				s->u.affs_sb.s_flags |= SF_READONLY;
+			}
+			/* Mark unused bits in the last word as allocated */
+			if (size <= s->s_blocksize * 8 - 32) {	/* last bitmap */
+				ptype = size / 32 + 1;		/* word number */
+				key   = size & 0x1F;		/* used bits */
+				if (key && !(s->s_flags & MS_RDONLY)) {
+					chksum = cpu_to_be32(0x7FFFFFFF >> (31 - key));
+					((u32 *)bb->b_data)[ptype] &= chksum;
+					affs_fix_checksum(s->s_blocksize,bb->b_data,0);
+					mark_buffer_dirty(bb,1);
+					bmalt = 1;
 				}
-				affs_brelse(bb);
+				ptype = (size + 31) & ~0x1F;
+				size  = 0;
+				s->u.affs_sb.s_flags |= SF_BM_VALID;
 			} else {
-				printk(KERN_ERR "AFFS: Cannot read bitmap\n");
-				goto out;
+				ptype = s->s_blocksize * 8 - 32;
+				size -= ptype;
+			}
+			s->u.affs_sb.s_bitmap[mapidx].bm_firstblk = offset;
+			s->u.affs_sb.s_bitmap[mapidx].bm_bh       = NULL;
+			s->u.affs_sb.s_bitmap[mapidx].bm_key      = be32_to_cpu(bm[i]);
+			s->u.affs_sb.s_bitmap[mapidx].bm_count    = 0;
+			offset += ptype;
+
+			for (j = 0; ptype > 0; j++, az_no++, ptype -= key) {
+				key = MIN(ptype,AFFS_ZONE_SIZE);	/* size in bits */
+				s->u.affs_sb.s_alloc[az_no].az_size = key / 32;
+				s->u.affs_sb.s_alloc[az_no].az_free =
+					affs_count_free_bits(key / 8,bb->b_data +
+					     j * (AFFS_ZONE_SIZE / 8) + 4);
 			}
+			affs_brelse(bb);
 		}
 		key   = be32_to_cpu(bm[stype]);		/* Next block of bitmap pointers	*/
 		ptype = 0;
 		stype = s->s_blocksize / 4 - 1;
 		affs_brelse(bh);
+		bh = NULL;
 		if (key) {
-			if (!(bh = affs_bread(s->s_dev,key,s->s_blocksize))) {
-				printk(KERN_ERR "AFFS: Cannot read bitmap extension\n");
-				goto out;
-			}
-		} else
-			bh = NULL;
-	}
-	if (mapidx < num_bm) {
-		printk(KERN_ERR "AFFS: Got only %d bitmap blocks, expected %d\n",mapidx,num_bm);
-		goto out;
+			bh = affs_bread(dev,key,s->s_blocksize);
+			if (!bh)
+				goto out_no_bm_ext;
+		}
 	}
+	if (mapidx < num_bm)
+		goto out_bad_num;
+
 nobitmap:
 	s->u.affs_sb.s_bm_count = num_bm;
 
 	/* set up enough so that it can read an inode */
 
-	s->s_dev   = dev;
-	s->s_op    = &affs_sops;
 	s->s_dirt  = 1;
 	root_inode = iget(s,root_block);
-	s->s_root  = d_alloc_root(root_inode,NULL);
-	unlock_super(s);
-
-	if (!(s->s_root)) {
-		s->s_dev = 0;
-		affs_brelse(s->u.affs_sb.s_root_bh);
-		printk(KERN_ERR "AFFS: get root inode failed\n");
-		MOD_DEC_USE_COUNT;
-		return NULL;
-	}
-	s->s_root->d_op = (s->u.affs_sb.s_flags & SF_INTL) ? &affs_dentry_operations_intl
-							   : &affs_dentry_operations;
+	if (!root_inode)
+		goto out_no_root;
+	s->s_root  = d_alloc_root(root_inode, NULL);
+	if (!s->s_root)
+		goto out_no_root;
+	s->s_root->d_op = &affs_dentry_operations;
 
+	unlock_super(s);
 	/* Record date of last change if the bitmap was truncated and
 	 * create data zones if the volume is writable.
 	 */
@@ -592,12 +568,56 @@
 	pr_debug("AFFS: s_flags=%lX\n",s->s_flags);
 	return s;
 
- out: /* Kick out for various error conditions */
-	affs_brelse (bh);
+out_bad_opts:
+	printk(KERN_ERR "AFFS: Error parsing options\n");
+	goto out_fail;
+out_bad_size:
+	printk(KERN_ERR "AFFS: Could not determine device size\n");
+	goto out_free_prefix;
+out_no_valid_block:
+	if (!silent)
+		printk(KERN_ERR "AFFS: No valid root block on device %s\n",
+			kdevname(dev));
+	goto out_restore;
+out_unknown_fs:
+	printk(KERN_ERR "AFFS: Unknown filesystem on device %s: %08X\n",
+		kdevname(dev), chksum);
+	goto out_free_bh;
+out_no_root_block:
+	printk(KERN_ERR "AFFS: Cannot read root block\n");
+	goto out_free_bh;
+out_no_bitmap:
+	printk(KERN_ERR "AFFS: Bitmap allocation failed\n");
+	goto out_free_root_block;
+out_no_read_bm:
+	printk(KERN_ERR "AFFS: Cannot read bitmap\n");
+	goto out_free_bitmap;
+out_no_bm_ext:
+	printk(KERN_ERR "AFFS: Cannot read bitmap extension\n");
+	goto out_free_bitmap;
+out_bad_num:
+	printk(KERN_ERR "AFFS: Got only %d bitmap blocks, expected %d\n",
+		mapidx, num_bm);
+	goto out_free_bitmap;
+out_no_root:
+	printk(KERN_ERR "AFFS: get root inode failed\n");
+
+	/*
+	 * Begin the cascaded cleanup ...
+	 */
+	iput(root_inode);
+out_free_bitmap:
+	kfree(s->u.affs_sb.s_bitmap);
+out_free_root_block:
 	affs_brelse(s->u.affs_sb.s_root_bh);
-	if (s->u.affs_sb.s_bitmap)
-		kfree(s->u.affs_sb.s_bitmap);
-	set_blocksize(dev,BLOCK_SIZE);
+out_free_bh:
+	affs_brelse(bh);
+out_restore:
+	set_blocksize(dev, s->u.affs_sb.s_blksize);
+out_free_prefix:
+	if (s->u.affs_sb.s_prefix)
+		kfree(s->u.affs_sb.s_prefix);
+out_fail:
 	s->s_dev = 0;
 	unlock_super(s);
 	MOD_DEC_USE_COUNT;
diff -u --recursive --new-file v2.1.88/linux/fs/binfmt_elf.c linux/fs/binfmt_elf.c
--- v2.1.88/linux/fs/binfmt_elf.c	Thu Feb 12 20:56:10 1998
+++ linux/fs/binfmt_elf.c	Sat Feb 28 20:43:52 1998
@@ -21,6 +21,7 @@
 #include <linux/signal.h>
 #include <linux/binfmts.h>
 #include <linux/string.h>
+#include <linux/file.h>
 #include <linux/fcntl.h>
 #include <linux/ptrace.h>
 #include <linux/malloc.h>
@@ -217,76 +218,60 @@
 				     unsigned long *interp_load_addr)
 {
 	struct file * file;
-	struct elf_phdr *elf_phdata  =  NULL;
+	struct elf_phdr *elf_phdata;
 	struct elf_phdr *eppnt;
-	unsigned long load_addr;
+	unsigned long load_addr = 0;
 	int load_addr_set = 0;
+	unsigned long last_bss = 0, elf_bss = 0;
+	unsigned long error = ~0UL;
 	int elf_exec_fileno;
-	int retval;
-	unsigned long last_bss, elf_bss;
-	unsigned long error;
-	int i;
-
-	elf_bss = 0;
-	last_bss = 0;
-	error = load_addr = 0;
+	int retval, i, size;
 
 	/* First of all, some simple consistency checks */
-	if ((interp_elf_ex->e_type != ET_EXEC &&
-	    interp_elf_ex->e_type != ET_DYN) ||
-	   !elf_check_arch(interp_elf_ex->e_machine) ||
-	   (!interpreter_dentry->d_inode->i_op ||
-	    !interpreter_dentry->d_inode->i_op->default_file_ops->mmap)){
-		return ~0UL;
-	}
-
-	/* Now read in all of the header information */
-
-	if (sizeof(struct elf_phdr) * interp_elf_ex->e_phnum > ELF_EXEC_PAGESIZE) {
-	    return ~0UL;
-	}
-
-	elf_phdata =  (struct elf_phdr *)
-		kmalloc(sizeof(struct elf_phdr) * interp_elf_ex->e_phnum,
-			GFP_KERNEL);
-	if (!elf_phdata) {
-	  return ~0UL;
-	}
+	if (interp_elf_ex->e_type != ET_EXEC &&
+	    interp_elf_ex->e_type != ET_DYN)
+		goto out;
+	if (!elf_check_arch(interp_elf_ex->e_machine))
+		goto out;
+	if (!interpreter_dentry->d_inode->i_op ||
+	    !interpreter_dentry->d_inode->i_op->default_file_ops->mmap)
+		goto out;
 
 	/*
 	 * If the size of this structure has changed, then punt, since
 	 * we will be doing the wrong thing.
 	 */
 	if (interp_elf_ex->e_phentsize != sizeof(struct elf_phdr))
-	  {
-	    kfree(elf_phdata);
-	    return ~0UL;
-	  }
+		goto out;
 
-	retval = read_exec(interpreter_dentry, interp_elf_ex->e_phoff,
-			   (char *) elf_phdata,
-			   sizeof(struct elf_phdr) * interp_elf_ex->e_phnum, 1);
+	/* Now read in all of the header information */
 
-	if (retval < 0) {
-		kfree (elf_phdata);
-		return retval;
- 	}
+	size = sizeof(struct elf_phdr) * interp_elf_ex->e_phnum;
+	if (size > ELF_EXEC_PAGESIZE)
+		goto out;
+	elf_phdata = (struct elf_phdr *) kmalloc(size, GFP_KERNEL);
+	if (!elf_phdata)
+		goto out;
 
-	elf_exec_fileno = open_dentry(interpreter_dentry, O_RDONLY);
-	if (elf_exec_fileno < 0) {
-	  kfree(elf_phdata);
-	  return ~0UL;
-	}
+	retval = read_exec(interpreter_dentry, interp_elf_ex->e_phoff,
+			   (char *) elf_phdata, size, 1);
+	error = retval;
+	if (retval < 0)
+		goto out_free;
 
-	file = current->files->fd[elf_exec_fileno];
+	error = ~0UL;
+	elf_exec_fileno = open_dentry(interpreter_dentry, O_RDONLY);
+	if (elf_exec_fileno < 0)
+		goto out_free;
+	file = fget(elf_exec_fileno);
 
 	eppnt = elf_phdata;
-	for(i=0; i<interp_elf_ex->e_phnum; i++, eppnt++)
+	for (i=0; i<interp_elf_ex->e_phnum; i++, eppnt++) {
 	  if (eppnt->p_type == PT_LOAD) {
 	    int elf_type = MAP_PRIVATE | MAP_DENYWRITE;
 	    int elf_prot = 0;
 	    unsigned long vaddr = 0;
-	    unsigned long k;
+	    unsigned long k, map_addr;
 
 	    if (eppnt->p_flags & PF_R) elf_prot =  PROT_READ;
 	    if (eppnt->p_flags & PF_W) elf_prot |= PROT_WRITE;
@@ -301,44 +286,40 @@
 #endif
 	    }
 
-	    error = do_mmap(file,
+	    map_addr = do_mmap(file,
 			    load_addr + ELF_PAGESTART(vaddr),
 			    eppnt->p_filesz + ELF_PAGEOFFSET(eppnt->p_vaddr),
 			    elf_prot,
 			    elf_type,
 			    eppnt->p_offset - ELF_PAGEOFFSET(eppnt->p_vaddr));
-
-	    if (error > -1024UL) {
-	      /* Real error */
-	      sys_close(elf_exec_fileno);
-	      kfree(elf_phdata);
-	      return ~0UL;
-	    }
+	    if (map_addr > -1024UL) /* Real error */
+		goto out_close;
 
 	    if (!load_addr_set && interp_elf_ex->e_type == ET_DYN) {
-	      load_addr = error;
-	      load_addr_set = 1;
+		load_addr = map_addr;
+		load_addr_set = 1;
 	    }
 
 	    /*
-	     * Find the end of the file  mapping for this phdr, and keep
+	     * Find the end of the file mapping for this phdr, and keep
 	     * track of the largest address we see for this.
 	     */
 	    k = load_addr + eppnt->p_vaddr + eppnt->p_filesz;
-	    if (k > elf_bss) elf_bss = k;
+	    if (k > elf_bss)
+		elf_bss = k;
 
 	    /*
 	     * Do the same thing for the memory mapping - between
 	     * elf_bss and last_bss is the bss section.
 	     */
 	    k = load_addr + eppnt->p_memsz + eppnt->p_vaddr;
-	    if (k > last_bss) last_bss = k;
+	    if (k > last_bss)
+		last_bss = k;
 	  }
+	}
 
 	/* Now use mmap to map the library into memory. */
 
-	sys_close(elf_exec_fileno);
-
 	/*
 	 * Now fill out the bss section.  First pad the last page up
 	 * to the page boundary, and then perform a mmap to make sure
@@ -350,53 +331,61 @@
 
 	/* Map the last of the bss segment */
 	if (last_bss > elf_bss)
-	  do_mmap(NULL, elf_bss, last_bss-elf_bss,
-		  PROT_READ|PROT_WRITE|PROT_EXEC,
-		  MAP_FIXED|MAP_PRIVATE, 0);
-	kfree(elf_phdata);
+		do_mmap(NULL, elf_bss, last_bss - elf_bss,
+			PROT_READ|PROT_WRITE|PROT_EXEC,
+			MAP_FIXED|MAP_PRIVATE, 0);
 
 	*interp_load_addr = load_addr;
-	return ((unsigned long) interp_elf_ex->e_entry) + load_addr;
+	error = ((unsigned long) interp_elf_ex->e_entry) + load_addr;
+
+out_close:
+	fput(file);
+	sys_close(elf_exec_fileno);
+out_free:
+	kfree(elf_phdata);
+out:
+	return error;
 }
 
 static unsigned long load_aout_interp(struct exec * interp_ex,
 			     struct dentry * interpreter_dentry)
 {
-  int retval;
-  unsigned long elf_entry;
+	unsigned long text_data, offset, elf_entry = ~0UL;
+	char * addr;
+	int retval;
+
+	current->mm->end_code = interp_ex->a_text;
+	text_data = interp_ex->a_text + interp_ex->a_data;
+	current->mm->end_data = text_data;
+	current->mm->brk = interp_ex->a_bss + text_data;
+
+	switch (N_MAGIC(*interp_ex)) {
+	case OMAGIC:
+		offset = 32;
+		addr = (char *) 0;
+		break;
+	case ZMAGIC:
+	case QMAGIC:
+		offset = N_TXTOFF(*interp_ex);
+		addr = (char *) N_TXTADDR(*interp_ex);
+		break;
+	default:
+		goto out;
+	}
+
+	do_mmap(NULL, 0, text_data,
+		PROT_READ|PROT_WRITE|PROT_EXEC, MAP_FIXED|MAP_PRIVATE, 0);
+	retval = read_exec(interpreter_dentry, offset, addr, text_data, 0);
+	if (retval < 0)
+		goto out;
+
+	do_mmap(NULL, ELF_PAGESTART(text_data + ELF_EXEC_PAGESIZE - 1),
+		interp_ex->a_bss,
+		PROT_READ|PROT_WRITE|PROT_EXEC, MAP_FIXED|MAP_PRIVATE, 0);
+	elf_entry = interp_ex->a_entry;
 
-  current->mm->brk = interp_ex->a_bss +
-    (current->mm->end_data = interp_ex->a_data +
-     (current->mm->end_code = interp_ex->a_text));
-  elf_entry = interp_ex->a_entry;
-
-
-  if (N_MAGIC(*interp_ex) == OMAGIC) {
-    do_mmap(NULL, 0, interp_ex->a_text+interp_ex->a_data,
-	    PROT_READ|PROT_WRITE|PROT_EXEC,
-	    MAP_FIXED|MAP_PRIVATE, 0);
-    retval = read_exec(interpreter_dentry, 32, (char *) 0,
-		       interp_ex->a_text+interp_ex->a_data, 0);
-  } else if (N_MAGIC(*interp_ex) == ZMAGIC || N_MAGIC(*interp_ex) == QMAGIC) {
-    do_mmap(NULL, 0, interp_ex->a_text+interp_ex->a_data,
-	    PROT_READ|PROT_WRITE|PROT_EXEC,
-	    MAP_FIXED|MAP_PRIVATE, 0);
-    retval = read_exec(interpreter_dentry,
-		       N_TXTOFF(*interp_ex) ,
-		       (char *) N_TXTADDR(*interp_ex),
-		       interp_ex->a_text+interp_ex->a_data, 0);
-  } else
-    retval = -1;
-
-  if (retval >= 0)
-    do_mmap(NULL, ELF_PAGESTART(interp_ex->a_text + interp_ex->a_data + ELF_EXEC_PAGESIZE - 1),
-	    interp_ex->a_bss,
-	    PROT_READ|PROT_WRITE|PROT_EXEC,
-	    MAP_FIXED|MAP_PRIVATE, 0);
-  if (retval < 0) {
-  	return ~0UL;
-  }
-  return elf_entry;
+out:
+	return elf_entry;
 }
 
 /*
@@ -412,174 +401,158 @@
 static inline int
 do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
 {
-	struct elfhdr elf_ex;
-	struct elfhdr interp_elf_ex;
 	struct file * file;
-  	struct exec interp_ex;
 	struct dentry *interpreter_dentry = NULL; /* to shut gcc up */
- 	unsigned long load_addr, load_bias;
+ 	unsigned long load_addr = 0, load_bias;
 	int load_addr_set = 0;
+	char * elf_interpreter = NULL;
 	unsigned int interpreter_type = INTERPRETER_NONE;
-	unsigned char ibcs2_interpreter;
-	int i;
+	unsigned char ibcs2_interpreter = 0;
 	mm_segment_t old_fs;
 	unsigned long error;
 	struct elf_phdr * elf_ppnt, *elf_phdata;
-	int elf_exec_fileno;
 	unsigned long elf_bss, k, elf_brk;
-	int retval;
-	char * elf_interpreter;
+	int elf_exec_fileno;
+	int retval, size, i;
 	unsigned long elf_entry, interp_load_addr = 0;
-	int status;
 	unsigned long start_code, end_code, end_data;
+	struct elfhdr elf_ex;
+	struct elfhdr interp_elf_ex;
+  	struct exec interp_ex;
 	char passed_fileno[6];
 
-	ibcs2_interpreter = 0;
-	status = 0;
-	load_addr = 0;
-	elf_ex = *((struct elfhdr *) bprm->buf);	  /* exec-header */
+	/* Get the exec-header */
+	elf_ex = *((struct elfhdr *) bprm->buf);
 
+	retval = -ENOEXEC;
+	/* First of all, some simple consistency checks */
 	if (elf_ex.e_ident[0] != 0x7f ||
-	    strncmp(&elf_ex.e_ident[1], "ELF",3) != 0) {
-		return  -ENOEXEC;
-	}
+	    strncmp(&elf_ex.e_ident[1], "ELF", 3) != 0)
+		goto out;
 
-
-	/* First of all, some simple consistency checks */
-	if ((elf_ex.e_type != ET_EXEC &&
-	    elf_ex.e_type != ET_DYN) ||
-	   (! elf_check_arch(elf_ex.e_machine)) ||
-	   (!bprm->dentry->d_inode->i_op || !bprm->dentry->d_inode->i_op->default_file_ops ||
-	    !bprm->dentry->d_inode->i_op->default_file_ops->mmap)){
-		return -ENOEXEC;
-	}
+	if (elf_ex.e_type != ET_EXEC && elf_ex.e_type != ET_DYN)
+		goto out;
+	if (!elf_check_arch(elf_ex.e_machine))
+		goto out;
+	if (!bprm->dentry->d_inode->i_op		   ||
+	    !bprm->dentry->d_inode->i_op->default_file_ops ||
+	    !bprm->dentry->d_inode->i_op->default_file_ops->mmap)
+		goto out;
 
 	/* Now read in all of the header information */
 
-	elf_phdata = (struct elf_phdr *) kmalloc(elf_ex.e_phentsize *
-						 elf_ex.e_phnum, GFP_KERNEL);
-	if (elf_phdata == NULL) {
-		return -ENOMEM;
-	}
-
-	retval = read_exec(bprm->dentry, elf_ex.e_phoff, (char *) elf_phdata,
-			   elf_ex.e_phentsize * elf_ex.e_phnum, 1);
-	if (retval < 0) {
-		kfree (elf_phdata);
-		return retval;
-	}
+	retval = -ENOMEM;
+	size = elf_ex.e_phentsize * elf_ex.e_phnum;
+	elf_phdata = (struct elf_phdr *) kmalloc(size, GFP_KERNEL);
+	if (!elf_phdata)
+		goto out;
+
+	retval = read_exec(bprm->dentry, elf_ex.e_phoff,
+				(char *) elf_phdata, size, 1);
+	if (retval < 0)
+		goto out_free_ph;
+
+	retval = open_dentry(bprm->dentry, O_RDONLY);
+	if (retval < 0)
+		goto out_free_ph;
+	elf_exec_fileno = retval;
+	file = fget(elf_exec_fileno);
 
 	elf_ppnt = elf_phdata;
-
 	elf_bss = 0;
 	elf_brk = 0;
 
-	elf_exec_fileno = open_dentry(bprm->dentry, O_RDONLY);
-
-	if (elf_exec_fileno < 0) {
-		kfree (elf_phdata);
-		return elf_exec_fileno;
-	}
-
-	file = current->files->fd[elf_exec_fileno];
-
-	elf_interpreter = NULL;
 	start_code = ~0UL;
 	end_code = 0;
 	end_data = 0;
 
-	for(i=0;i < elf_ex.e_phnum; i++){
+	for (i = 0; i < elf_ex.e_phnum; i++) {
 		if (elf_ppnt->p_type == PT_INTERP) {
-		  	if ( elf_interpreter != NULL )
-			{
-				kfree (elf_phdata);
-				kfree(elf_interpreter);
-				sys_close(elf_exec_fileno);
-				return -EINVAL;
-			}
+			retval = -EINVAL;
+		  	if (elf_interpreter)
+				goto out_free_interp;
 
 			/* This is the program interpreter used for
 			 * shared libraries - for now assume that this
 			 * is an a.out format binary
 			 */
 
+			retval = -ENOMEM;
 			elf_interpreter = (char *) kmalloc(elf_ppnt->p_filesz,
 							   GFP_KERNEL);
-			if (elf_interpreter == NULL) {
-				kfree (elf_phdata);
-				sys_close(elf_exec_fileno);
-				return -ENOMEM;
-			}
+			if (!elf_interpreter)
+				goto out_free_file;
 
-			retval = read_exec(bprm->dentry,elf_ppnt->p_offset,
+			retval = read_exec(bprm->dentry, elf_ppnt->p_offset,
 					   elf_interpreter,
 					   elf_ppnt->p_filesz, 1);
+			if (retval < 0)
+				goto out_free_interp;
 			/* If the program interpreter is one of these two,
-			   then assume an iBCS2 image. Otherwise assume
-			   a native linux image. */
+			 * then assume an iBCS2 image. Otherwise assume
+			 * a native linux image.
+			 */
 			if (strcmp(elf_interpreter,"/usr/lib/libc.so.1") == 0 ||
 			    strcmp(elf_interpreter,"/usr/lib/ld.so.1") == 0)
-			  ibcs2_interpreter = 1;
+				ibcs2_interpreter = 1;
 #if 0
 			printk("Using ELF interpreter %s\n", elf_interpreter);
 #endif
-			if (retval >= 0) {
-				old_fs = get_fs(); /* This could probably be optimized */
-				set_fs(get_ds());
+			old_fs = get_fs(); /* This could probably be optimized */
+			set_fs(get_ds());
 #ifdef __sparc__
-				if (ibcs2_interpreter) {
-					unsigned long old_pers = current->personality;
+			if (ibcs2_interpreter) {
+				unsigned long old_pers = current->personality;
 					
-					current->personality = PER_SVR4;
-					interpreter_dentry = open_namei(elf_interpreter, 0, 0);
-					current->personality = old_pers;
-				} else
+				current->personality = PER_SVR4;
+				interpreter_dentry = open_namei(elf_interpreter,
+								0, 0);
+				current->personality = old_pers;
+			} else
 #endif					
-					interpreter_dentry = open_namei(elf_interpreter, 0, 0);
-				set_fs(old_fs);
-				if (IS_ERR(interpreter_dentry))
-					retval = PTR_ERR(interpreter_dentry);
-			}
-
-			if (retval >= 0)
-				retval = read_exec(interpreter_dentry,0,bprm->buf,128, 1);
-
-			if (retval >= 0) {
-				interp_ex = *((struct exec *) bprm->buf);		/* exec-header */
-				interp_elf_ex = *((struct elfhdr *) bprm->buf);	  /* exec-header */
-
-			}
-			if (retval < 0) {
-				kfree (elf_phdata);
-				kfree(elf_interpreter);
-				sys_close(elf_exec_fileno);
-				return retval;
-			}
+				interpreter_dentry = open_namei(elf_interpreter,
+								0, 0);
+			set_fs(old_fs);
+			retval = PTR_ERR(interpreter_dentry);
+			if (IS_ERR(interpreter_dentry))
+				goto out_free_interp;
+
+			retval = read_exec(interpreter_dentry, 0, bprm->buf, 
+						128, 1);
+			if (retval < 0)
+				goto out_free_dentry;
+
+			/* Get the exec headers */
+			interp_ex = *((struct exec *) bprm->buf);
+			interp_elf_ex = *((struct elfhdr *) bprm->buf);
 		}
 		elf_ppnt++;
 	}
 
 	/* Some simple consistency checks for the interpreter */
-	if (elf_interpreter){
+	if (elf_interpreter) {
 		interpreter_type = INTERPRETER_ELF | INTERPRETER_AOUT;
 
 		/* Now figure out which format our binary is */
 		if ((N_MAGIC(interp_ex) != OMAGIC) &&
 		    (N_MAGIC(interp_ex) != ZMAGIC) &&
 		    (N_MAGIC(interp_ex) != QMAGIC))
-		  interpreter_type = INTERPRETER_ELF;
+			interpreter_type = INTERPRETER_ELF;
 
 		if (interp_elf_ex.e_ident[0] != 0x7f ||
-		    strncmp(&interp_elf_ex.e_ident[1], "ELF",3) != 0)
-		  interpreter_type &= ~INTERPRETER_ELF;
+		    strncmp(&interp_elf_ex.e_ident[1], "ELF", 3) != 0)
+			interpreter_type &= ~INTERPRETER_ELF;
 
+		retval = -ELIBBAD;
 		if (!interpreter_type)
-		  {
-		    kfree(elf_interpreter);
-		    kfree(elf_phdata);
-		    sys_close(elf_exec_fileno);
-		    return -ELIBBAD;
-		  }
+			goto out_free_dentry;
+
+		/* Make sure only one type was selected */
+		if ((interpreter_type & INTERPRETER_ELF) &&
+		     interpreter_type != INTERPRETER_ELF) {
+			printk(KERN_WARNING "ELF: Ambiguous type, using ELF\n");
+			interpreter_type = INTERPRETER_ELF;
+		}
 	}
 
 	/* OK, we are done with that, now set up the arg stuff,
@@ -597,20 +570,15 @@
 		    bprm->argc++;
 		  }
 		}
-		if (!bprm->p) {
-			if (elf_interpreter) {
-			      kfree(elf_interpreter);
-			}
-			kfree (elf_phdata);
-			sys_close(elf_exec_fileno);
-			return -E2BIG;
-		}
+		retval = -E2BIG;
+		if (!bprm->p)
+			goto out_free_dentry;
 	}
 
 	/* Flush all traces of the currently running executable */
 	retval = flush_old_exec(bprm);
 	if (retval)
-		return retval;
+		goto out_free_dentry;
 
 	/* OK, This is the point of no return */
 	current->mm->end_data = 0;
@@ -677,15 +645,19 @@
 			k = elf_ppnt->p_vaddr;
 			if (k < start_code) start_code = k;
 			k = elf_ppnt->p_vaddr + elf_ppnt->p_filesz;
-			if (k > elf_bss) elf_bss = k;
+			if (k > elf_bss)
+				elf_bss = k;
 			if ((elf_ppnt->p_flags & PF_X) && end_code <  k)
 				end_code = k;
-			if (end_data < k) end_data = k;
+			if (end_data < k)
+				end_data = k;
 			k = elf_ppnt->p_vaddr + elf_ppnt->p_memsz;
-			if (k > elf_brk) elf_brk = k;
+			if (k > elf_brk)
+				elf_brk = k;
 		}
 	}
 	set_fs(old_fs);
+	fput(file); /* all done with the file */
 
 	elf_entry += load_bias;
 	elf_bss += load_bias;
@@ -695,10 +667,10 @@
 	end_data += load_bias;
 
 	if (elf_interpreter) {
-		if (interpreter_type & 1)
+		if (interpreter_type == INTERPRETER_AOUT)
 			elf_entry = load_aout_interp(&interp_ex,
 						     interpreter_dentry);
-		else if (interpreter_type & 2)
+		else
 			elf_entry = load_elf_interp(&interp_elf_ex,
 						    interpreter_dentry,
 						    &interp_load_addr);
@@ -707,7 +679,7 @@
 		kfree(elf_interpreter);
 
 		if (elf_entry == ~0UL) {
-			printk("Unable to load interpreter\n");
+			printk(KERN_ERR "Unable to load interpreter\n");
 			kfree(elf_phdata);
 			send_sig(SIGSEGV, current, 0);
 			return 0;
@@ -744,16 +716,18 @@
 			load_addr,
 			interp_load_addr,
 			(interpreter_type == INTERPRETER_AOUT ? 0 : 1));
+	/* N.B. passed_fileno might not be initialized? */
 	if (interpreter_type == INTERPRETER_AOUT)
-	  current->mm->arg_start += strlen(passed_fileno) + 1;
+		current->mm->arg_start += strlen(passed_fileno) + 1;
 	current->mm->start_brk = current->mm->brk = elf_brk;
 	current->mm->end_code = end_code;
 	current->mm->start_code = start_code;
 	current->mm->end_data = end_data;
 	current->mm->start_stack = bprm->p;
 
-	/* Calling set_brk effectively mmaps the pages that we need for the bss and break
-	   sections */
+	/* Calling set_brk effectively mmaps the pages that we need
+	 * for the bss and break sections
+	 */
 	set_brk(elf_bss, elf_brk);
 
 	padzero(elf_bss);
@@ -773,6 +747,7 @@
 		   and some applications "depend" upon this behavior.
 		   Since we do not have the power to recompile these, we
 		   emulate the SVr4 behavior.  Sigh.  */
+		/* N.B. Shouldn't the size here be PAGE_SIZE?? */
 		error = do_mmap(NULL, 0, 4096, PROT_READ | PROT_EXEC,
 				MAP_FIXED | MAP_PRIVATE, 0);
 	}
@@ -787,11 +762,25 @@
 	ELF_PLAT_INIT(regs);
 #endif
 
-
 	start_thread(regs, elf_entry, bprm->p);
 	if (current->flags & PF_PTRACED)
 		send_sig(SIGTRAP, current, 0);
-	return 0;
+	retval = 0;
+out:
+	return retval;
+
+	/* error cleanup */
+out_free_dentry:
+	dput(interpreter_dentry);
+out_free_interp:
+	if (elf_interpreter)
+		kfree(elf_interpreter);
+out_free_file:
+	fput(file);
+	sys_close(elf_exec_fileno);
+out_free_ph:
+	kfree(elf_phdata);
+	goto out;
 }
 
 static int
@@ -809,75 +798,66 @@
    a.out library that is given an ELF header. */
 
 static inline int
-do_load_elf_library(int fd){
+do_load_elf_library(int fd)
+{
 	struct file * file;
-	struct elfhdr elf_ex;
-	struct elf_phdr *elf_phdata  =  NULL;
 	struct dentry * dentry;
 	struct inode * inode;
-	unsigned long len;
-	int elf_bss;
-	int retval;
-	unsigned long bss;
-	int error;
-	int i,j, k;
+	struct elf_phdr *elf_phdata;
+	unsigned long elf_bss = 0, bss, len, k;
+	int retval, error, i, j;
+	struct elfhdr elf_ex;
+	loff_t offset = 0;
 
-	len = 0;
-	file = current->files->fd[fd];
+	error = -EACCES;
+	file = fget(fd);
+	if (!file || !file->f_op)
+		goto out;
 	dentry = file->f_dentry;
 	inode = dentry->d_inode;
-	elf_bss = 0;
-
-	if (!file || !file->f_op)
-		return -EACCES;
 
 	/* seek to the beginning of the file */
-	if (file->f_op->llseek) {
-		if ((error = file->f_op->llseek(file, 0, 0)) != 0)
-			return -ENOEXEC;
-	} else
-		file->f_pos = 0;
+	error = -ENOEXEC;
 
+	/* N.B. save current DS?? */
 	set_fs(KERNEL_DS);
-	error = file->f_op->read(file, (char *) &elf_ex,
-				 sizeof(elf_ex), &file->f_pos);
+	retval = file->f_op->read(file, (char *) &elf_ex, sizeof(elf_ex), &offset);
 	set_fs(USER_DS);
-	if (error != sizeof(elf_ex))
-		return -ENOEXEC;
+	if (retval != sizeof(elf_ex))
+		goto out_putf;
 
 	if (elf_ex.e_ident[0] != 0x7f ||
-	    strncmp(&elf_ex.e_ident[1], "ELF",3) != 0)
-		return -ENOEXEC;
+	    strncmp(&elf_ex.e_ident[1], "ELF", 3) != 0)
+		goto out_putf;
 
 	/* First of all, some simple consistency checks */
 	if (elf_ex.e_type != ET_EXEC || elf_ex.e_phnum > 2 ||
 	   !elf_check_arch(elf_ex.e_machine) ||
 	   (!inode->i_op || !inode->i_op->default_file_ops->mmap))
-		return -ENOEXEC;
+		goto out_putf;
 
 	/* Now read in all of the header information */
 
-	if (sizeof(struct elf_phdr) * elf_ex.e_phnum > ELF_EXEC_PAGESIZE)
-		return -ENOEXEC;
-
-	elf_phdata =  (struct elf_phdr *)
-		kmalloc(sizeof(struct elf_phdr) * elf_ex.e_phnum, GFP_KERNEL);
-	if (elf_phdata == NULL)
-		return -ENOMEM;
+	j = sizeof(struct elf_phdr) * elf_ex.e_phnum;
+	if (j > ELF_EXEC_PAGESIZE)
+		goto out_putf;
+
+	error = -ENOMEM;
+	elf_phdata = (struct elf_phdr *) kmalloc(j, GFP_KERNEL);
+	if (!elf_phdata)
+		goto out_putf;
 
+	/* N.B. check for error return?? */
 	retval = read_exec(dentry, elf_ex.e_phoff, (char *) elf_phdata,
 			   sizeof(struct elf_phdr) * elf_ex.e_phnum, 1);
 
-	j = 0;
-	for(i=0; i<elf_ex.e_phnum; i++)
+	error = -ENOEXEC;
+	for (j = 0, i = 0; i<elf_ex.e_phnum; i++)
 		if ((elf_phdata + i)->p_type == PT_LOAD) j++;
+	if (j != 1)
+		goto out_free_ph;
 
-	if (j != 1)  {
-		kfree(elf_phdata);
-		return -ENOEXEC;
-	}
-
-	while(elf_phdata->p_type != PT_LOAD) elf_phdata++;
+	while (elf_phdata->p_type != PT_LOAD) elf_phdata++;
 
 	/* Now use mmap to map the library into memory. */
 	error = do_mmap(file,
@@ -888,25 +868,29 @@
 			MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE,
 			(elf_phdata->p_offset -
 			 ELF_PAGEOFFSET(elf_phdata->p_vaddr)));
+	if (error != ELF_PAGESTART(elf_phdata->p_vaddr))
+		goto out_free_ph;
 
 	k = elf_phdata->p_vaddr + elf_phdata->p_filesz;
-	if (k > elf_bss) elf_bss = k;
-
-	if (error != ELF_PAGESTART(elf_phdata->p_vaddr)) {
-		kfree(elf_phdata);
-		return error;
-	}
-
+	if (k > elf_bss)
+		elf_bss = k;
 	padzero(elf_bss);
 
-	len = ELF_PAGESTART(elf_phdata->p_filesz + elf_phdata->p_vaddr+ ELF_EXEC_PAGESIZE - 1);
+	len = ELF_PAGESTART(elf_phdata->p_filesz + elf_phdata->p_vaddr + 
+				ELF_EXEC_PAGESIZE - 1);
 	bss = elf_phdata->p_memsz + elf_phdata->p_vaddr;
 	if (bss > len)
-	  do_mmap(NULL, len, bss-len,
-		  PROT_READ|PROT_WRITE|PROT_EXEC,
-		  MAP_FIXED|MAP_PRIVATE, 0);
+		do_mmap(NULL, len, bss - len,
+			PROT_READ|PROT_WRITE|PROT_EXEC,
+			MAP_FIXED|MAP_PRIVATE, 0);
+	error = 0;
+
+out_free_ph:
 	kfree(elf_phdata);
-	return 0;
+out_putf:
+	fput(file);
+out:
+	return error;
 }
 
 static int load_elf_library(int fd)
diff -u --recursive --new-file v2.1.88/linux/fs/buffer.c linux/fs/buffer.c
--- v2.1.88/linux/fs/buffer.c	Mon Feb 23 18:12:10 1998
+++ linux/fs/buffer.c	Tue Mar  3 21:22:19 1998
@@ -37,6 +37,7 @@
 #include <linux/vmalloc.h>
 #include <linux/blkdev.h>
 #include <linux/sysrq.h>
+#include <linux/file.h>
 
 #include <asm/system.h>
 #include <asm/uaccess.h>
@@ -332,31 +333,29 @@
 
 	lock_kernel();
 	err = -EBADF;
-
-	if (fd >= NR_OPEN)
-		goto out;
-
-	file = current->files->fd[fd];
+	file = fget(fd);
 	if (!file)
 		goto out;
 
 	dentry = file->f_dentry;
 	if (!dentry)
-		goto out;
+		goto out_putf;
 
 	inode = dentry->d_inode;
 	if (!inode)
-		goto out;
+		goto out_putf;
 
 	err = -EINVAL;
 	if (!file->f_op || !file->f_op->fsync)
-		goto out;
+		goto out_putf;
 
 	/* We need to protect against concurrent writers.. */
 	down(&inode->i_sem);
-	err = file->f_op->fsync(file, file->f_dentry);
+	err = file->f_op->fsync(file, dentry);
 	up(&inode->i_sem);
 
+out_putf:
+	fput(file);
 out:
 	unlock_kernel();
 	return err;
@@ -371,29 +370,27 @@
 
 	lock_kernel();
 	err = -EBADF;
-
-	if (fd >= NR_OPEN)
-		goto out;
-
-	file = current->files->fd[fd];
+	file = fget(fd);
 	if (!file)
 		goto out;
 
 	dentry = file->f_dentry;
 	if (!dentry)
-		goto out;
+		goto out_putf;
 
 	inode = dentry->d_inode;
 	if (!inode)
-		goto out;
+		goto out_putf;
 
 	err = -EINVAL;
 	if (!file->f_op || !file->f_op->fsync)
-		goto out;
+		goto out_putf;
 
 	/* this needs further work, at the moment it is identical to fsync() */
-	err = file->f_op->fsync(file, file->f_dentry);
+	err = file->f_op->fsync(file, dentry);
 
+out_putf:
+	fput(file);
 out:
 	unlock_kernel();
 	return err;
@@ -870,8 +867,7 @@
 	/*
 	 * Convert a reserved page into buffers ... should happen only rarely.
 	 */
-	if (nr_free_pages > (min_free_pages >> 1) &&
-	    grow_buffers(GFP_ATOMIC, size)) {
+	if (grow_buffers(GFP_ATOMIC, size)) {
 #ifdef BUFFER_DEBUG
 printk("refill_freelist: used reserve page\n");
 #endif
@@ -1321,14 +1317,13 @@
 /* Run the hooks that have to be done when a page I/O has completed. */
 static inline void after_unlock_page (struct page * page)
 {
-	if (test_and_clear_bit(PG_decr_after, &page->flags))
+	if (test_and_clear_bit(PG_decr_after, &page->flags)) {
 		atomic_dec(&nr_async_pages);
-	if (test_and_clear_bit(PG_swap_unlock_after, &page->flags)) {
-		/*
-		 * We're doing a swap, so check that this page is
-		 * swap-cached and do the necessary cleanup. 
-		 */
-		swap_after_unlock_page(page->offset);
+#ifdef DEBUG_SWAP
+		printk ("DebugVM: Finished IO on page %p, nr_async_pages %d\n",
+			(char *) page_address(page), 
+			atomic_read(&nr_async_pages));
+#endif
 	}
 	if (test_and_clear_bit(PG_free_after, &page->flags))
 		__free_page(page);
@@ -1531,8 +1526,9 @@
  * mark_buffer_uptodate() functions propagate buffer state into the
  * page struct once IO has completed.
  */
-int generic_readpage(struct dentry * dentry, struct page * page)
+int generic_readpage(struct file * file, struct page * page)
 {
+	struct dentry *dentry = file->f_dentry;
 	struct inode *inode = dentry->d_inode;
 	unsigned long block;
 	int *p, nr[PAGE_SIZE/512];
diff -u --recursive --new-file v2.1.88/linux/fs/coda/cache.c linux/fs/coda/cache.c
--- v2.1.88/linux/fs/coda/cache.c	Tue Jan  6 10:00:21 1998
+++ linux/fs/coda/cache.c	Wed Mar  4 15:14:32 1998
@@ -21,7 +21,7 @@
 #include <linux/coda.h>
 #include <linux/coda_linux.h>
 #include <linux/coda_psdev.h>
-#include <linux/coda_cnode.h>
+#include <linux/coda_fs_i.h>
 #include <linux/coda_cache.h>
 
 /* Keep various stats */
@@ -42,7 +42,7 @@
 	list_add(&el->cc_cclist, &sbi->sbi_cchead);
 }
 
-void coda_cninsert(struct coda_cache *el, struct cnode *cnp)
+void coda_cninsert(struct coda_cache *el, struct coda_inode_info *cnp)
 {
 ENTRY;
 	if ( !cnp ||  !el) {
@@ -54,23 +54,29 @@
 
 void coda_ccremove(struct coda_cache *el)
 {
-ENTRY;
-	list_del(&el->cc_cclist);
+	ENTRY;
+        if (el->cc_cclist.next && el->cc_cclist.prev)
+	        list_del(&el->cc_cclist);
+	else
+		printk("coda_cnremove: trying to remove 0 entry!");
 }
 
 void coda_cnremove(struct coda_cache *el)
 {
-ENTRY;
-	list_del(&el->cc_cnlist);
+	ENTRY;
+	if (el->cc_cnlist.next && el->cc_cnlist.prev)
+		list_del(&el->cc_cnlist);
+	else
+		printk("coda_cnremove: trying to remove 0 entry!");
 }
 
 
 void coda_cache_create(struct inode *inode, int mask)
 {
-	struct cnode *cnp = ITOC(inode);
+	struct coda_inode_info *cnp = ITOC(inode);
 	struct super_block *sb = inode->i_sb;
 	struct coda_cache *cc = NULL;
-ENTRY;
+	ENTRY;
 	CODA_ALLOC(cc, struct coda_cache *, sizeof(*cc));
 
 	if ( !cc ) {
@@ -85,7 +91,7 @@
 
 struct coda_cache * coda_cache_find(struct inode *inode)
 {
-	struct cnode *cnp = ITOC(inode);
+	struct coda_inode_info *cnp = ITOC(inode);
 	struct list_head *lh, *le;
 	struct coda_cache *cc = NULL;
 	
@@ -114,7 +120,7 @@
 	}
 }
 
-void coda_cache_clear_cnp(struct cnode *cnp)
+void coda_cache_clear_cnp(struct coda_inode_info *cnp)
 {
 	struct list_head *lh, *le;
 	struct coda_cache *cc;
@@ -178,7 +184,7 @@
 
 int coda_cache_check(struct inode *inode, int mask)
 {
-	struct cnode *cnp = ITOC(inode);
+	struct coda_inode_info *cnp = ITOC(inode);
 	struct list_head *lh, *le;
 	struct coda_cache *cc = NULL;
 	
@@ -207,9 +213,8 @@
 static void coda_flag_children(struct dentry *parent)
 {
 	struct list_head *child;
-	struct cnode *cnp;
+	struct coda_inode_info *cnp;
 	struct dentry *de;
-	char str[50];
 
 	child = parent->d_subdirs.next;
 	while ( child != &parent->d_subdirs ) {
@@ -217,7 +222,7 @@
 		cnp = ITOC(de->d_inode);
 		if (cnp) 
 			cnp->c_flags |= C_ZAPFID;
-		CDEBUG(D_CACHE, "ZAPFID for %s\n", coda_f2s(&cnp->c_fid, str));
+		CDEBUG(D_CACHE, "ZAPFID for %s\n", coda_f2s(&cnp->c_fid));
 		
 		child = child->next;
 	}
@@ -228,7 +233,7 @@
 void  coda_dentry_delete(struct dentry *dentry)
 {
 	struct inode *inode = dentry->d_inode;
-	struct cnode *cnp = NULL;
+	struct coda_inode_info *cnp = NULL;
 	ENTRY;
 
 	if (inode) { 
@@ -254,7 +259,7 @@
 	return;
 }
 
-static void coda_zap_cnode(struct cnode *cnp, int flags)
+static void coda_zap_cnode(struct coda_inode_info *cnp, int flags)
 {
 	cnp->c_flags |= flags;
 	coda_cache_clear_cnp(cnp);
@@ -267,7 +272,7 @@
 void coda_zapfid(struct ViceFid *fid, struct super_block *sb, int flag)
 {
 	struct inode *inode = NULL;
-	struct cnode *cnp;
+	struct coda_inode_info *cnp;
 
 	ENTRY;
  
@@ -286,7 +291,7 @@
 		struct coda_sb_info *sbi = coda_sbp(sb);
 		le = lh = &sbi->sbi_volroothead;
 		while ( (le = le->next) != lh ) {
-			cnp = list_entry(le, struct cnode, c_volrootlist);
+			cnp = list_entry(le, struct coda_inode_info, c_volrootlist);
 			if ( cnp->c_fid.Volume == fid->Volume) 
 				coda_zap_cnode(cnp, flag);
 		}
@@ -300,11 +305,6 @@
 		return;
 	}
 	cnp = ITOC(inode);
-	CHECK_CNODE(cnp);
-	if ( !cnp ) {
-		printk("coda_zapfid: no cnode!\n");
-		return;
-	}
 	coda_zap_cnode(cnp, flag);
 }
 		
diff -u --recursive --new-file v2.1.88/linux/fs/coda/cnode.c linux/fs/coda/cnode.c
--- v2.1.88/linux/fs/coda/cnode.c	Tue Jan  6 10:00:21 1998
+++ linux/fs/coda/cnode.c	Wed Mar  4 15:14:32 1998
@@ -1,5 +1,5 @@
 /* cnode related routines for the coda kernel code
-   Peter Braam, Sep 1996.
+   (C) 1996 Peter Braam
    */
 
 #include <linux/types.h>
@@ -7,37 +7,14 @@
 
 #include <linux/coda.h>
 #include <linux/coda_linux.h>
-#include <linux/coda_cnode.h>
+#include <linux/coda_fs_i.h>
 #include <linux/coda_psdev.h>
 
 extern int coda_debug;
 extern int coda_print_entry;
 
 /* cnode.c */
-static struct cnode *coda_cnode_alloc(void);
 
-/* return pointer to new empty cnode */
-static struct cnode *coda_cnode_alloc(void)
-{
-        struct cnode *result = NULL;
-
-        CODA_ALLOC(result, struct cnode *, sizeof(struct cnode));
-        if ( !result ) {
-                printk("coda_cnode_alloc: kmalloc returned NULL.\n");
-                return result;
-        }
-
-        memset(result, 0, (int) sizeof(struct cnode));
-        INIT_LIST_HEAD(&(result->c_cnhead));
-	INIT_LIST_HEAD(&(result->c_volrootlist));
-	return result;
-}
-
-/* release cnode memory */
-void coda_cnode_free(struct cnode *cinode)
-{
-        CODA_FREE(cinode, sizeof(struct cnode));
-}
 
               
 static void coda_fill_inode (struct inode *inode, struct coda_vattr *attr)
@@ -70,12 +47,11 @@
 */
 int coda_cnode_make(struct inode **inode, ViceFid *fid, struct super_block *sb)
 {
-        struct cnode *cnp;
+        struct coda_inode_info *cnp;
 	struct coda_sb_info *sbi= coda_sbp(sb);
         struct coda_vattr attr;
         int error;
 	ino_t ino;
-	char str[50];
         
         ENTRY;
 
@@ -86,7 +62,7 @@
 	error = venus_getattr(sb, fid, &attr);
 	if ( error ) {
 	    printk("coda_cnode_make: coda_getvattr returned %d for %s.\n", 
-		   error, coda_f2s(fid, str));
+		   error, coda_f2s(fid));
 	    *inode = NULL;
 	    return error;
 	} 
@@ -98,27 +74,19 @@
                 return -ENOMEM;
         }
 
-	/* link the cnode and the vfs inode 
-	   if this inode is not linked yet
-	*/
-	if ( !(*inode)->u.generic_ip ) {
-        	cnp = coda_cnode_alloc();
-        	if ( !cnp ) {
-               		printk("coda_cnode_make: coda_cnode_alloc failed.\n");
-			clear_inode(*inode);
-                	return -ENOMEM;
-        	}
-        	cnp->c_fid = *fid;
-        	cnp->c_magic = CODA_CNODE_MAGIC;
+	cnp = ITOC(*inode);
+	if  ( cnp->c_magic == 0 ) {
+		memset(cnp, 0, (int) sizeof(struct coda_inode_info));
+		cnp->c_fid = *fid;
+		cnp->c_magic = CODA_CNODE_MAGIC;
 		cnp->c_flags = C_VATTR;
-        	cnp->c_vnode = *inode;
-        	(*inode)->u.generic_ip = (void *) cnp;
-		CDEBUG(D_CNODE, "LINKING: ino %ld, count %d  at 0x%x with cnp 0x%x, cnp->c_vnode 0x%x, in->u.generic_ip 0x%x\n", (*inode)->i_ino, (*inode)->i_count, (int) (*inode), (int) cnp, (int)cnp->c_vnode, (int) (*inode)->u.generic_ip);
+		cnp->c_vnode = *inode;
+		INIT_LIST_HEAD(&(cnp->c_cnhead));
+		INIT_LIST_HEAD(&(cnp->c_volrootlist));
 	} else {
-	    cnp = (struct cnode *)(*inode)->u.generic_ip;
-	    CDEBUG(D_CNODE, "FOUND linked: ino %ld, count %d, at 0x%x with cnp 0x%x, cnp->c_vnode 0x%x\n", (*inode)->i_ino, (*inode)->i_count, (int) (*inode), (int) cnp, (int)cnp->c_vnode);
+		printk("coda_cnode make on initialized inode %ld, %s!\n",
+		       (*inode)->i_ino, coda_f2s(&cnp->c_fid));
 	}
-	CHECK_CNODE(cnp);
 
 	/* fill in the inode attributes */
 	if ( coda_fid_is_volroot(fid) ) 
@@ -145,14 +113,14 @@
 
 /* convert a fid to an inode. Avoids having a hash table
    such as present in the Mach minicache */
-struct inode *coda_fid_to_inode(ViceFid *fid, struct super_block *sb) {
+struct inode *coda_fid_to_inode(ViceFid *fid, struct super_block *sb) 
+{
 	ino_t nr;
 	struct inode *inode;
-	struct cnode *cnp;
-	char str[50];
+	struct coda_inode_info *cnp;
 ENTRY;
 
-	CDEBUG(D_INODE, "%s\n", coda_f2s(fid, str));
+	CDEBUG(D_INODE, "%s\n", coda_f2s(fid));
 
 	nr = coda_f2i(fid);
 	inode = iget(sb, nr);
@@ -164,12 +132,13 @@
 	}
 
 	/* check if this inode is linked to a cnode */
-	cnp = (struct cnode *) inode->u.generic_ip;
-	if ( cnp == NULL ) {
+	cnp = ITOC(inode);
+
+	if ( cnp->c_magic != CODA_CNODE_MAGIC ) {
 		iput(inode);
-		EXIT;
 		return NULL;
 	}
+
 	/* make sure fid is the one we want */
 	if ( !coda_fideq(fid, &(cnp->c_fid)) ) {
 		printk("coda_fid2inode: bad cnode! Tell Peter.\n");
diff -u --recursive --new-file v2.1.88/linux/fs/coda/coda_linux.c linux/fs/coda/coda_linux.c
--- v2.1.88/linux/fs/coda/coda_linux.c	Tue Jan  6 10:00:21 1998
+++ linux/fs/coda/coda_linux.c	Wed Mar  4 15:14:32 1998
@@ -21,7 +21,7 @@
 #include <linux/coda.h>
 #include <linux/coda_linux.h>
 #include <linux/coda_psdev.h>
-#include <linux/coda_cnode.h>
+#include <linux/coda_fs_i.h>
 #include <linux/coda_cache.h>
 
 /* initialize the debugging variables */
@@ -30,10 +30,11 @@
 int coda_access_cache = 1;
 
 /* caller must allocate 36 byte string ! */
-char * coda_f2s(ViceFid *f, char *s)
+char * coda_f2s(ViceFid *f)
 {
+	static char s[50];
 	if ( f ) {
-		sprintf(s, "(%-#10lx,%-#10lx,%-#10lx)", 
+		sprintf(s, "(%10lx,%10lx,%10lx)", 
 			 f->Volume, f->Vnode, f->Unique);
 	}
 	return s;
diff -u --recursive --new-file v2.1.88/linux/fs/coda/dir.c linux/fs/coda/dir.c
--- v2.1.88/linux/fs/coda/dir.c	Tue Jan  6 10:00:21 1998
+++ linux/fs/coda/dir.c	Wed Mar  4 15:14:32 1998
@@ -21,7 +21,7 @@
 #include <linux/coda.h>
 #include <linux/coda_linux.h>
 #include <linux/coda_psdev.h>
-#include <linux/coda_cnode.h>
+#include <linux/coda_fs_i.h>
 #include <linux/coda_cache.h>
 
 /* dir inode-ops */
@@ -98,7 +98,7 @@
 /* acces routines: lookup, readlink, permission */
 static int coda_lookup(struct inode *dir, struct dentry *entry)
 {
-        struct cnode *dircnp;
+        struct coda_inode_info *dircnp;
 	struct inode *res_inode = NULL;
 	struct ViceFid resfid;
 	int dropme = 0; /* to indicate entry should not be cached */
@@ -106,7 +106,6 @@
 	int error = 0;
 	const char *name = entry->d_name.name;
 	size_t length = entry->d_name.len;
-	char str[50];
 	
         ENTRY;
         CDEBUG(D_INODE, "name %s, len %d in ino %ld\n", 
@@ -122,12 +121,12 @@
 
 	if ( length > CFS_MAXNAMLEN ) {
 	        printk("name too long: lookup, %s (%*s)\n", 
-		       coda_f2s(&dircnp->c_fid, str), length, name);
+		       coda_f2s(&dircnp->c_fid), length, name);
 		return -ENAMETOOLONG;
 	}
 	
 	CDEBUG(D_INODE, "lookup: %*s in %s\n", length, name, 
-	       coda_f2s(&dircnp->c_fid, str));
+	       coda_f2s(&dircnp->c_fid));
 
         /* control object, create inode on the fly */
         if (coda_isroot(dir) && coda_iscontrol(name, length)) {
@@ -150,11 +149,11 @@
 			return -error;
 	} else if (error != -ENOENT) {
 	        CDEBUG(D_INODE, "error for %s(%*s)%d\n",
-		       coda_f2s(&dircnp->c_fid, str), length, name, error);
+		       coda_f2s(&dircnp->c_fid), length, name, error);
 		return error;
 	}
 	CDEBUG(D_INODE, "lookup: %s is (%s) type %d result %d, dropme %d\n",
-	       name, coda_f2s(&resfid, str), type, error, dropme);
+	       name, coda_f2s(&resfid), type, error, dropme);
 
 exit:
 	entry->d_time = 0;
@@ -169,9 +168,8 @@
 
 int coda_permission(struct inode *inode, int mask)
 {
-        struct cnode *cp;
+        struct coda_inode_info *cp;
         int error;
-	char str[50];
  
         ENTRY;
 
@@ -193,7 +191,7 @@
         error = venus_access(inode->i_sb, &(cp->c_fid), mask);
     
         CDEBUG(D_INODE, "fid: %s, ino: %ld (mask: %o) error: %d\n", 
-	       coda_f2s(&(cp->c_fid), str), inode->i_ino, mask, error);
+	       coda_f2s(&(cp->c_fid)), inode->i_ino, mask, error);
 
 	if ( error == 0 ) {
 		coda_cache_enter(inode, mask);
@@ -209,7 +207,7 @@
 static int coda_create(struct inode *dir, struct dentry *de, int mode)
 {
         int error=0;
-        struct cnode *dircnp;
+        struct coda_inode_info *dircnp;
 	const char *name=de->d_name.name;
 	int length=de->d_name.len;
 	struct inode *result = NULL;
@@ -232,7 +230,7 @@
         if ( length > CFS_MAXNAMLEN ) {
 		char str[50];
 		printk("name too long: create, %s(%s)\n", 
-		       coda_f2s(&dircnp->c_fid, str), name);
+		       coda_f2s(&dircnp->c_fid), name);
 		return -ENAMETOOLONG;
         }
 
@@ -242,7 +240,7 @@
         if ( error ) {
 		char str[50];
 		CDEBUG(D_INODE, "create: %s, result %d\n",
-		       coda_f2s(&newfid, str), error); 
+		       coda_f2s(&newfid), error); 
 		d_drop(de);
 		return error;
 	}
@@ -262,14 +260,13 @@
 
 static int coda_mkdir(struct inode *dir, struct dentry *de, int mode)
 {
-        struct cnode *dircnp;
+        struct coda_inode_info *dircnp;
 	struct inode *inode;
 	struct coda_vattr attr;
 	const char *name = de->d_name.name;
 	int len = de->d_name.len;
 	int error;
 	struct ViceFid newfid;
-	char fidstr[50];
 
 
 	if (!dir || !S_ISDIR(dir->i_mode)) {
@@ -287,7 +284,7 @@
         CHECK_CNODE(dircnp);
 
 	CDEBUG(D_INODE, "mkdir %s (len %d) in %s, mode %o.\n", 
-	       name, len, coda_f2s(&(dircnp->c_fid), fidstr), mode);
+	       name, len, coda_f2s(&(dircnp->c_fid)), mode);
 
 	attr.va_mode = mode;
 	error = venus_mkdir(dir->i_sb, &(dircnp->c_fid), 
@@ -295,13 +292,13 @@
         
         if ( error ) {
 	        CDEBUG(D_INODE, "mkdir error: %s result %d\n", 
-		       coda_f2s(&newfid, fidstr), error); 
+		       coda_f2s(&newfid), error); 
 		d_drop(de);
                 return error;
         }
          
 	CDEBUG(D_INODE, "mkdir: new dir has fid %s.\n", 
-	       coda_f2s(&newfid, fidstr)); 
+	       coda_f2s(&newfid)); 
 
 	error = coda_cnode_make(&inode, &newfid, dir->i_sb);
 	if ( error ) {
@@ -323,7 +320,7 @@
 	struct inode *inode = source_de->d_inode;
         const char * name = de->d_name.name;
 	int len = de->d_name.len;
-        struct cnode *dir_cnp, *cnp;
+        struct coda_inode_info *dir_cnp, *cnp;
 	char str[50];
 	int error;
 
@@ -337,8 +334,8 @@
         cnp = ITOC(inode);
         CHECK_CNODE(cnp);
 
-	CDEBUG(D_INODE, "old: fid: %s\n", coda_f2s(&(cnp->c_fid), str));
-	CDEBUG(D_INODE, "directory: %s\n", coda_f2s(&(dir_cnp->c_fid), str));
+	CDEBUG(D_INODE, "old: fid: %s\n", coda_f2s(&(cnp->c_fid)));
+	CDEBUG(D_INODE, "directory: %s\n", coda_f2s(&(dir_cnp->c_fid)));
 
         if ( len > CFS_MAXNAMLEN ) {
                 printk("coda_link: name too long. \n");
@@ -367,7 +364,7 @@
 {
         const char *name = de->d_name.name;
 	int len = de->d_name.len;
-        struct cnode *dir_cnp = ITOC(dir_inode);
+        struct coda_inode_info *dir_cnp = ITOC(dir_inode);
 	int symlen;
         int error=0;
         
@@ -407,7 +404,7 @@
 
 int coda_unlink(struct inode *dir, struct dentry *de)
 {
-        struct cnode *dircnp;
+        struct coda_inode_info *dircnp;
         int error;
 	const char *name = de->d_name.name;
 	int len = de->d_name.len;
@@ -419,7 +416,7 @@
         CHECK_CNODE(dircnp);
 
         CDEBUG(D_INODE, " %s in %s, ino %ld\n", name , 
-	       coda_f2s(&(dircnp->c_fid), fidstr), dir->i_ino);
+	       coda_f2s(&(dircnp->c_fid)), dir->i_ino);
 
         /* this file should no longer be in the namecache! */
 
@@ -441,7 +438,7 @@
 
 int coda_rmdir(struct inode *dir, struct dentry *de)
 {
-        struct cnode *dircnp;
+        struct coda_inode_info *dircnp;
 	const char *name = de->d_name.name;
 	int len = de->d_name.len;
         int error, rehash = 0;
@@ -499,7 +496,7 @@
 	int new_length = new_dentry->d_name.len;
 	struct inode *old_inode = old_dentry->d_inode;
 	struct inode *new_inode = new_dentry->d_inode;
-        struct cnode *new_cnp, *old_cnp;
+        struct coda_inode_info *new_cnp, *old_cnp;
         int error, rehash = 0, update = 1;
 ENTRY;
         old_cnp = ITOC(old_dir);
@@ -559,7 +556,7 @@
 int coda_readdir(struct file *file, void *dirent,  filldir_t filldir)
 {
         int result = 0;
-        struct cnode *cnp;
+        struct coda_inode_info *cnp;
         struct file open_file;
 	struct dentry open_dentry;
 	struct inode *inode=file->f_dentry->d_inode;
@@ -599,7 +596,7 @@
 {
         ino_t ino;
 	dev_t dev;
-        struct cnode *cnp;
+        struct coda_inode_info *cnp;
         int error = 0;
         struct inode *cont_inode = NULL;
         unsigned short flags = f->f_flags;
@@ -653,7 +650,7 @@
 
 int coda_release(struct inode *i, struct file *f)
 {
-        struct cnode *cnp;
+        struct coda_inode_info *cnp;
         int error;
         unsigned short flags = f->f_flags;
 	unsigned short cflags = coda_flags_to_cflags(flags);
diff -u --recursive --new-file v2.1.88/linux/fs/coda/file.c linux/fs/coda/file.c
--- v2.1.88/linux/fs/coda/file.c	Thu Feb 12 20:56:11 1998
+++ linux/fs/coda/file.c	Wed Mar  4 15:14:32 1998
@@ -20,12 +20,12 @@
 
 #include <linux/coda.h>
 #include <linux/coda_linux.h>
-#include <linux/coda_cnode.h>
+#include <linux/coda_fs_i.h>
 #include <linux/coda_psdev.h>
 #include <linux/coda_cache.h>
 
 /* file operations */
-static int coda_readpage(struct dentry * dentry, struct page * page);
+static int coda_readpage(struct file *file, struct page * page);
 static ssize_t coda_file_read(struct file *f, char *buf, size_t count, loff_t *off);
 static ssize_t coda_file_write(struct file *f, const char *buf, size_t count, loff_t *off);
 static int coda_file_mmap(struct file * file, struct vm_area_struct * vma);
@@ -74,12 +74,13 @@
 };
 
 /*  File file operations */
-static int coda_readpage(struct dentry *de, struct page * page)
+static int coda_readpage(struct file * file, struct page * page)
 {
+	struct dentry *de = file->f_dentry;
 	struct inode *inode = de->d_inode;
 	struct dentry cont_dentry;
         struct inode *cont_inode;
-        struct cnode *cnp;
+        struct coda_inode_info *cnp;
 
         ENTRY;
         
@@ -103,7 +104,7 @@
 
 static int coda_file_mmap(struct file * file, struct vm_area_struct * vma)
 {
-        struct cnode *cnp;
+        struct coda_inode_info *cnp;
 	cnp = ITOC(file->f_dentry->d_inode);
 	cnp->c_mmcount++;
   
@@ -113,7 +114,7 @@
 static ssize_t coda_file_read(struct file *coda_file, char *buff, 
 			   size_t count, loff_t *ppos)
 {
-        struct cnode *cnp;
+        struct coda_inode_info *cnp;
 	struct inode *coda_inode = coda_file->f_dentry->d_inode;
         struct inode *cont_inode = NULL;
         struct file  cont_file;
@@ -153,7 +154,7 @@
 static ssize_t coda_file_write(struct file *coda_file, const char *buff, 
 			    size_t count, loff_t *ppos)
 {
-        struct cnode *cnp;
+        struct coda_inode_info *cnp;
 	struct inode *coda_inode = coda_file->f_dentry->d_inode;
         struct inode *cont_inode = NULL;
         struct file  cont_file;
@@ -192,7 +193,7 @@
 
 int coda_fsync(struct file *coda_file, struct dentry *coda_dentry)
 {
-        struct cnode *cnp;
+        struct coda_inode_info *cnp;
 	struct inode *coda_inode = coda_dentry->d_inode;
         struct inode *cont_inode = NULL;
         struct file  cont_file;
@@ -254,7 +255,7 @@
 {
         coda_file->f_pos = open_file->f_pos;
 	/* XXX what about setting the mtime here too? */
-	coda_inode->i_mtime = open_inode->i_mtime;
+	/* coda_inode->i_mtime = open_inode->i_mtime; */
 	coda_inode->i_size = open_inode->i_size;
         return;
 }
@@ -264,7 +265,7 @@
 {
         struct super_block *sbptr;
 
-        sbptr = get_super(to_kdev_t(dev));
+        sbptr = get_super(dev);
 
         if ( !sbptr ) {
                 printk("coda_inode_grab: coda_find_super returns NULL.\n");
diff -u --recursive --new-file v2.1.88/linux/fs/coda/pioctl.c linux/fs/coda/pioctl.c
--- v2.1.88/linux/fs/coda/pioctl.c	Thu Feb 12 20:56:11 1998
+++ linux/fs/coda/pioctl.c	Wed Mar  4 15:14:32 1998
@@ -20,7 +20,7 @@
 
 #include <linux/coda.h>
 #include <linux/coda_linux.h>
-#include <linux/coda_cnode.h>
+#include <linux/coda_fs_i.h>
 #include <linux/coda_cache.h>
 #include <linux/coda_psdev.h>
 
@@ -103,7 +103,7 @@
         int error;
 	struct PioctlData data;
         struct inode *target_inode = NULL;
-        struct cnode *cnp;
+        struct coda_inode_info *cnp;
 
         ENTRY;
         /* get the Pioctl data arguments from user space */
@@ -115,22 +115,23 @@
          * Look up the pathname. Note that the pathname is in 
          * user memory, and namei takes care of this
          */
-	CDEBUG(D_PIOCTL, "namei, data.follow = %d\n", data.follow);
+	CDEBUG(D_PIOCTL, "namei, data.follow = %d\n", 
+	       data.follow);
         if ( data.follow ) {
                 target_de = namei(data.path);
 	} else {
 	        target_de = lnamei(data.path);
 	}
-
-	if (!target_de) {
+		
+	if ( PTR_ERR(target_de) == -ENOENT ) {
                 CDEBUG(D_PIOCTL, "error: lookup fails.\n");
-		return -EINVAL;
+		return PTR_ERR(target_de);
         } else {
 	        target_inode = target_de->d_inode;
 	}
 	
-	CDEBUG(D_PIOCTL, "target ino: 0x%ld, dev: %s\n",
-	       target_inode->i_ino, kdevname(target_inode->i_dev));
+	CDEBUG(D_PIOCTL, "target ino: 0x%ld, dev: 0x%d\n",
+	       target_inode->i_ino, target_inode->i_dev);
 
 	/* return if it is not a Coda inode */
 	if ( target_inode->i_sb != inode->i_sb ) {
diff -u --recursive --new-file v2.1.88/linux/fs/coda/psdev.c linux/fs/coda/psdev.c
--- v2.1.88/linux/fs/coda/psdev.c	Mon Feb 23 18:12:10 1998
+++ linux/fs/coda/psdev.c	Wed Mar  4 15:14:32 1998
@@ -41,7 +41,7 @@
 
 #include <linux/coda.h>
 #include <linux/coda_linux.h>
-#include <linux/coda_cnode.h>
+#include <linux/coda_fs_i.h>
 #include <linux/coda_psdev.h>
 #include <linux/coda_cache.h>
 #include <linux/coda_sysctl.h>
@@ -129,7 +129,7 @@
 	if ( !vcp ) 
 	        return -ENXIO;
 
-	poll_wait(&(vcp->vc_waitq), wait);
+	poll_wait(file, &(vcp->vc_waitq), wait);
 	if (!q_empty(&(vcp->vc_pending)))
                 mask |= POLLIN | POLLRDNORM;
 
@@ -347,7 +347,8 @@
 	   can profit from setting the C_DYING flag on the root 
 	   cnode of Coda filesystems */
         if (coda_super_info[minor].sbi_root) {
-                struct cnode *cnp = ITOC(coda_super_info[minor].sbi_root);
+                struct coda_inode_info *cnp = 
+			ITOC(coda_super_info[minor].sbi_root);
                 cnp->c_flags |= C_DYING;
         } else 
 		vcp->vc_inuse = 0;	
diff -u --recursive --new-file v2.1.88/linux/fs/coda/super.c linux/fs/coda/super.c
--- v2.1.88/linux/fs/coda/super.c	Mon Feb 23 18:12:10 1998
+++ linux/fs/coda/super.c	Wed Mar  4 15:14:32 1998
@@ -33,7 +33,7 @@
 #include <linux/coda.h>
 #include <linux/coda_linux.h>
 #include <linux/coda_psdev.h>
-#include <linux/coda_cnode.h>
+#include <linux/coda_fs_i.h>
 #include <linux/coda_cache.h>
 
 
@@ -119,7 +119,7 @@
 	        unlock_super(sb);
 		goto error;
 	}	  
-	printk("coda_read_super: rootfid is %s\n", coda_f2s(&fid, str));
+	printk("coda_read_super: rootfid is %s\n", coda_f2s(&fid));
 	
 	/* make root inode */
         error = coda_cnode_make(&root, &fid, sb);
@@ -130,8 +130,8 @@
 	    goto error;
 	} 
 
-	printk("coda_read_super: rootinode is %ld dev %s\n", 
-	       root->i_ino, kdevname(root->i_dev));
+	printk("coda_read_super: rootinode is %ld dev %d\n", 
+	       root->i_ino, root->i_dev);
 	sbi->sbi_root = root;
 	sb->s_root = d_alloc_root(root, NULL);
 	unlock_super(sb);
@@ -151,7 +151,6 @@
 	}
         if (root) {
                 iput(root);
-                coda_cnode_free(ITOC(root));
         }
         sb->s_dev = 0;
         return NULL;
@@ -181,8 +180,10 @@
 /* all filling in of inodes postponed until lookup */
 static void coda_read_inode(struct inode *inode)
 {
+	struct coda_inode_info *cnp;
 	ENTRY;
-	inode->u.generic_ip =  NULL;
+	cnp = ITOC(inode);
+	cnp->c_magic = 0;
 	return;
 }
 
@@ -190,24 +191,28 @@
 {
 	ENTRY;
 
-        CDEBUG(D_INODE,"ino: %ld, cnp: %p\n", in->i_ino, in->u.generic_ip);
+        CDEBUG(D_INODE,"ino: %ld, count %d\n", in->i_ino, in->i_count);
+
+	if ( in->i_count == 1 ) 
+		in->i_nlink = 0;
+		
 }
 
 static void coda_delete_inode(struct inode *inode)
 {
-        struct cnode *cnp;
+        struct coda_inode_info *cnp;
         struct inode *open_inode;
 
         ENTRY;
         CDEBUG(D_SUPER, " inode->ino: %ld, count: %d\n", 
 	       inode->i_ino, inode->i_count);        
 
-	if ( inode->i_ino == CTL_INO ) {
+        cnp = ITOC(inode);
+	if ( inode->i_ino == CTL_INO || cnp->c_magic != CODA_CNODE_MAGIC ) {
 	        clear_inode(inode);
 		return;
 	}
 
-        cnp = ITOC(inode);
 
 	if ( coda_fid_is_volroot(&cnp->c_fid) )
 		list_del(&cnp->c_volrootlist);
@@ -223,7 +228,6 @@
 	coda_cache_clear_cnp(cnp);
 
 	inode->u.generic_ip = NULL;
-        coda_cnode_free(cnp);
         clear_inode(inode);
 	EXIT;
 }
@@ -231,7 +235,7 @@
 static int  coda_notify_change(struct dentry *de, struct iattr *iattr)
 {
 	struct inode *inode = de->d_inode;
-        struct cnode *cnp;
+        struct coda_inode_info *cnp;
         struct coda_vattr vattr;
         int error;
 	
@@ -350,7 +354,7 @@
 
         if (MINOR(psdev->i_rdev) >= MAX_CODADEVS) { 
 		printk("minor %d not an allocated Coda PSDEV\n", 
-		       MINOR(psdev->i_rdev));
+		       psdev->i_rdev);
 		return 1;
         }
 
diff -u --recursive --new-file v2.1.88/linux/fs/coda/symlink.c linux/fs/coda/symlink.c
--- v2.1.88/linux/fs/coda/symlink.c	Thu Feb 12 20:56:12 1998
+++ linux/fs/coda/symlink.c	Wed Mar  4 15:14:32 1998
@@ -21,7 +21,7 @@
 #include <linux/coda.h>
 #include <linux/coda_linux.h>
 #include <linux/coda_psdev.h>
-#include <linux/coda_cnode.h>
+#include <linux/coda_fs_i.h>
 #include <linux/coda_cache.h>
 
 static int coda_readlink(struct dentry *de, char *buffer, int length);
@@ -39,41 +39,41 @@
 	NULL,			/* mknod */
 	NULL,			/* rename */
 	coda_readlink,		/* readlink */
-	coda_follow_link,	/* follow_link */
+	coda_follow_link,     	/* follow_link */
 	NULL,			/* readpage */
 	NULL,			/* writepage */
 	NULL,			/* bmap */
 	NULL,			/* truncate */
-	NULL,			/* permission */
-	NULL,			/* smap */
-	NULL,			/* update page */
-	NULL			/* revalidate */
+	NULL,            	/* permission */
+	NULL,                   /* smap */
+	NULL,                   /* update page */
+        NULL                    /* revalidate */
 };
 
 static int coda_readlink(struct dentry *de, char *buffer, int length)
 {
 	struct inode *inode = de->d_inode;
-	int len;
+        int len;
 	int error;
-	char *buf;
-	struct cnode *cp;
-	ENTRY;
-
-	cp = ITOC(inode);
-	CHECK_CNODE(cp);
-
-	/* the maximum length we receive is len */
-	if ( length > CFS_MAXPATHLEN ) 
-		len = CFS_MAXPATHLEN;
+        char *buf;
+	struct coda_inode_info *cp;
+        ENTRY;
+
+        cp = ITOC(inode);
+        CHECK_CNODE(cp);
+
+        /* the maximum length we receive is len */
+        if ( length > CFS_MAXPATHLEN ) 
+	        len = CFS_MAXPATHLEN;
 	else
-		len = length;
+	        len = length;
 	CODA_ALLOC(buf, char *, len);
 	if ( !buf ) 
-		return -ENOMEM;
+	        return -ENOMEM;
 	
 	error = venus_readlink(inode->i_sb, &(cp->c_fid), buf, &len);
 
-	CDEBUG(D_INODE, "result %s\n", buf);
+        CDEBUG(D_INODE, "result %s\n", buf);
 	if (! error) {
 		copy_to_user(buffer, buf, len);
 		put_user('\0', buffer + len);
@@ -89,15 +89,15 @@
 {
 	struct inode *inode = de->d_inode;
 	int error;
-	struct cnode *cnp;
+	struct coda_inode_info *cnp;
 	unsigned int len;
 	char mem[CFS_MAXPATHLEN];
 	char *path;
 ENTRY;
-	CDEBUG(D_INODE, "(%s/%ld)\n", kdevname(inode->i_dev), inode->i_ino);
+	CDEBUG(D_INODE, "(%x/%ld)\n", inode->i_dev, inode->i_ino);
 	
-	cnp = ITOC(inode);
-	CHECK_CNODE(cnp);
+        cnp = ITOC(inode);
+        CHECK_CNODE(cnp);
 
 	len = CFS_MAXPATHLEN;
 	error = venus_readlink(inode->i_sb, &(cnp->c_fid), mem, &len);
diff -u --recursive --new-file v2.1.88/linux/fs/coda/sysctl.c linux/fs/coda/sysctl.c
--- v2.1.88/linux/fs/coda/sysctl.c	Mon Feb 23 18:12:10 1998
+++ linux/fs/coda/sysctl.c	Wed Mar  4 15:14:32 1998
@@ -23,7 +23,7 @@
 
 #include <linux/coda.h>
 #include <linux/coda_linux.h>
-#include <linux/coda_cnode.h>
+#include <linux/coda_fs_i.h>
 #include <linux/coda_psdev.h>
 #include <linux/coda_cache.h>
 #include <linux/coda_sysctl.h>
diff -u --recursive --new-file v2.1.88/linux/fs/coda/upcall.c linux/fs/coda/upcall.c
--- v2.1.88/linux/fs/coda/upcall.c	Sun Dec 21 14:45:14 1997
+++ linux/fs/coda/upcall.c	Wed Mar  4 15:14:32 1998
@@ -16,7 +16,8 @@
 
 #include <asm/system.h>
 #include <asm/segment.h>
-
+#include <asm/signal.h>
+#include <linux/signal.h>
 
 #include <linux/types.h>
 #include <linux/kernel.h>
@@ -34,7 +35,7 @@
 #include <linux/coda.h>
 #include <linux/coda_linux.h>
 #include <linux/coda_psdev.h>
-#include <linux/coda_cnode.h>
+#include <linux/coda_fs_i.h>
 #include <linux/coda_cache.h>
 
 #define UPARG(op)\
@@ -538,7 +539,7 @@
         
         if (error) {
 	        printk("coda_pioctl: Venus returns: %d for %s\n", 
-		       error, coda_f2s(fid, str));
+		       error, coda_f2s(fid));
 		goto exit; 
 	}
         
@@ -586,6 +587,7 @@
 static inline void coda_waitfor_upcall(struct vmsg *vmp)
 {
 	struct wait_queue	wait = { current, NULL };
+	old_sigset_t pending;
 
 	vmp->vm_posttime = jiffies;
 
@@ -596,12 +598,26 @@
 		else
 			current->state = TASK_UNINTERRUPTIBLE;
 
+		/* got a reply */
 		if ( vmp->vm_flags & VM_WRITE )
 			break;
-		if (signal_pending(current) &&
-		    (jiffies > vmp->vm_posttime + coda_timeout * HZ) )
+
+		if ( ! signal_pending(current) )
+			schedule();
+		/* signal is present: after timeout always return */
+		if ( jiffies > vmp->vm_posttime + coda_timeout * HZ )
+			break; 
+				
+		spin_lock_irq(&current->sigmask_lock);
+		pending = current->blocked.sig[0] & current->signal.sig[0];
+		spin_unlock_irq(&current->sigmask_lock);
+
+		/* if this process really wants to die, let it go */
+		if ( sigismember(&pending, SIGKILL) ||
+		     sigismember(&pending, SIGINT) )
 			break;
-		schedule();
+		else 
+			schedule();
 	}
 	remove_wait_queue(&vmp->vm_sleep, &wait);
 	current->state = TASK_RUNNING;
@@ -793,7 +809,7 @@
 		    printk("ZAPDIR: Null fid\n");
 		    return 0;
 	    }
-	    CDEBUG(D_DOWNCALL, "zapdir: fid = %s\n", coda_f2s(fid, str));
+	    CDEBUG(D_DOWNCALL, "zapdir: fid = %s\n", coda_f2s(fid));
 	    clstats(CFS_ZAPDIR);
 	    coda_zapfid(fid, sb, C_ZAPDIR);
 	    return(0);
@@ -806,7 +822,7 @@
 		    printk("ZAPVNODE: Null fid or cred\n");
 		    return 0;
 	    }
-	    CDEBUG(D_DOWNCALL, "zapvnode: fid = %s\n", coda_f2s(fid, str));
+	    CDEBUG(D_DOWNCALL, "zapvnode: fid = %s\n", coda_f2s(fid));
 	    coda_zapfid(fid, sb, C_ZAPFID);
 	    coda_cache_clear_cred(sb, cred);
 	    clstats(CFS_ZAPVNODE);
@@ -820,7 +836,7 @@
 		    printk("ZAPFILE: Null fid\n");
 		    return 0;
 	    }
-	    CDEBUG(D_DOWNCALL, "zapfile: fid = %s\n", coda_f2s(fid, str));
+	    CDEBUG(D_DOWNCALL, "zapfile: fid = %s\n", coda_f2s(fid));
 	    coda_zapfid(fid, sb, C_ZAPFID);
 	    return 0;
     }
@@ -831,7 +847,7 @@
 		    printk("PURGEFID: Null fid\n");
 		    return 0;
 	    }
-	    CDEBUG(D_DOWNCALL, "purgefid: fid = %s\n", coda_f2s(fid, str));
+	    CDEBUG(D_DOWNCALL, "purgefid: fid = %s\n", coda_f2s(fid));
 	    clstats(CFS_PURGEFID);
 	    coda_zapfid(fid, sb, C_ZAPDIR);
 	    return 0;
diff -u --recursive --new-file v2.1.88/linux/fs/dquot.c linux/fs/dquot.c
--- v2.1.88/linux/fs/dquot.c	Mon Jan 12 14:46:24 1998
+++ linux/fs/dquot.c	Thu Mar  5 11:55:06 1998
@@ -17,7 +17,7 @@
  * 
  * Author:  Marco van Wieringen <mvw@mcs.ow.nl> <mvw@tnix.net>
  * 
- * Fixes:   Dmitry Gorodchanin <begemot@bgm.rosprint.net>, 11 Feb 96
+ * Fixes:   Dmitry Gorodchanin <pgmdsg@ibi.com>, 11 Feb 96
  *	    removed race conditions in dqput(), dqget() and iput(). 
  *          Andi Kleen removed all verify_area() calls, 31 Dec 96  
  *          Nick Kralevich <nickkral@cal.alumni.berkeley.edu>, 21 Jul 97
@@ -938,7 +938,7 @@
 		vfsmnt->mnt_sb->dq_op = (struct dquot_operations *)NULL;
 		reset_dquot_ptrs(dev, cnt);
 		invalidate_dquots(dev, cnt);
-		close_fp(vfsmnt->mnt_quotas[cnt]);
+		fput(vfsmnt->mnt_quotas[cnt]);
 		vfsmnt->mnt_quotas[cnt] = (struct file *)NULL;
 		vfsmnt->mnt_iexp[cnt] = vfsmnt->mnt_bexp[cnt] = (time_t)NULL;
 	}
diff -u --recursive --new-file v2.1.88/linux/fs/exec.c linux/fs/exec.c
--- v2.1.88/linux/fs/exec.c	Fri Jan  2 11:01:40 1998
+++ linux/fs/exec.c	Fri Feb 27 10:42:58 1998
@@ -330,7 +330,7 @@
 		mpnt->vm_flags = VM_STACK_FLAGS;
 		mpnt->vm_ops = NULL;
 		mpnt->vm_offset = 0;
-		mpnt->vm_dentry = NULL;
+		mpnt->vm_file = NULL;
 		mpnt->vm_pte = 0;
 		insert_vm_struct(current->mm, mpnt);
 		current->mm->total_vm = (mpnt->vm_end - mpnt->vm_start) >> PAGE_SHIFT;
diff -u --recursive --new-file v2.1.88/linux/fs/fat/file.c linux/fs/fat/file.c
--- v2.1.88/linux/fs/fat/file.c	Fri Jan 23 18:10:32 1998
+++ linux/fs/fat/file.c	Fri Mar  6 10:27:27 1998
@@ -105,7 +105,7 @@
 	NULL,			/* rename */
 	NULL,			/* readlink */
 	NULL,			/* follow_link */
-	NULL,			/* readpage */
+	generic_readpage,	/* readpage */
 	NULL,			/* writepage */
 	NULL,			/* bmap */
 	fat_truncate,		/* truncate */
@@ -317,7 +317,13 @@
 		return MSDOS_SB(inode->i_sb)->cvf_format
 			->cvf_file_read(filp,buf,count,ppos);
 
-	if (!MSDOS_I(inode)->i_binary)
+	/*
+	 * MS-DOS filesystems with a blocksize > 512 may have blocks
+	 * spread over several hardware sectors (unaligned), which
+	 * is not something the generic routines can (or would want
+	 * to) handle).
+	 */
+	if (!MSDOS_I(inode)->i_binary || inode->i_sb->s_blocksize > 512)
 		return fat_file_read_text(filp, buf, count, ppos);
 	return generic_file_read(filp, buf, count, ppos);
 }
diff -u --recursive --new-file v2.1.88/linux/fs/fat/mmap.c linux/fs/fat/mmap.c
--- v2.1.88/linux/fs/fat/mmap.c	Fri Jan 23 18:10:32 1998
+++ linux/fs/fat/mmap.c	Sat Feb 28 13:19:14 1998
@@ -32,7 +32,7 @@
 	unsigned long address,
 	int error_code)
 {
-	struct inode * inode = area->vm_dentry->d_inode;
+	struct inode * inode = area->vm_file->f_dentry->d_inode;
 	unsigned long page;
 	unsigned int clear;
 	int pos;
@@ -112,14 +112,16 @@
 		mark_inode_dirty(inode);
 	}
 
-	vma->vm_dentry = dget(file->f_dentry);
+	vma->vm_file = file;
+	file->f_count++;
 	vma->vm_ops = &fat_file_mmap;
 	return 0;
 }
 
 
-int fat_readpage(struct dentry * dentry, struct page * page)
+int fat_readpage(struct file *file, struct page * page)
 {
+	struct dentry * dentry = file->f_dentry;
 	struct inode * inode = dentry->d_inode;
 	if (MSDOS_SB(inode->i_sb)->cvf_format &&
 	    MSDOS_SB(inode->i_sb)->cvf_format->cvf_readpage)
diff -u --recursive --new-file v2.1.88/linux/fs/inode.c linux/fs/inode.c
--- v2.1.88/linux/fs/inode.c	Thu Feb 12 20:56:12 1998
+++ linux/fs/inode.c	Mon Feb 23 12:56:37 1998
@@ -701,19 +701,22 @@
 				list_add(&inode->i_list, inode_in_use.prev);
 			}
 #ifdef INODE_PARANOIA
+if (inode->i_flock)
+printk(KERN_ERR "iput: inode %s/%ld still has locks!\n",
+kdevname(inode->i_dev), inode->i_ino);
 if (!list_empty(&inode->i_dentry))
-printk("iput: device %s inode %ld still has aliases!\n",
+printk(KERN_ERR "iput: device %s inode %ld still has aliases!\n",
 kdevname(inode->i_dev), inode->i_ino);
 if (inode->i_count)
-printk("iput: device %s inode %ld count changed, count=%d\n",
+printk(KERN_ERR "iput: device %s inode %ld count changed, count=%d\n",
 kdevname(inode->i_dev), inode->i_ino, inode->i_count);
 if (atomic_read(&inode->i_sem.count) != 1)
-printk("iput: Aieee, semaphore in use device %s, count=%d\n",
+printk(KERN_ERR "iput: Aieee, semaphore in use device %s, count=%d\n",
 kdevname(inode->i_dev), atomic_read(&inode->i_sem.count));
 #endif
 		}
-		if (inode->i_count > (1<<15)) {
-			printk("iput: device %s inode %ld count wrapped\n",
+		if (inode->i_count > (1<<31)) {
+			printk(KERN_ERR "iput: inode %s/%ld count wrapped\n",
 				kdevname(inode->i_dev), inode->i_ino);
 		}
 		spin_unlock(&inode_lock);
diff -u --recursive --new-file v2.1.88/linux/fs/ioctl.c linux/fs/ioctl.c
--- v2.1.88/linux/fs/ioctl.c	Sun Jul 13 21:20:10 1997
+++ linux/fs/ioctl.c	Mon Feb 23 12:55:58 1998
@@ -13,6 +13,7 @@
 #include <linux/smp.h>
 #include <linux/smp_lock.h>
 #include <linux/fcntl.h> /* for f_flags values */
+#include <linux/file.h>
 
 #include <asm/uaccess.h>
 
@@ -52,7 +53,8 @@
 	int on, error = -EBADF;
 
 	lock_kernel();
-	if (fd >= NR_OPEN || !(filp = current->files->fd[fd]))
+	filp = fget(fd);
+	if (!filp)
 		goto out;
 	error = 0;
 	switch (cmd) {
@@ -90,13 +92,16 @@
 			break;
 
 		default:
-			if (filp->f_dentry && filp->f_dentry->d_inode && S_ISREG(filp->f_dentry->d_inode->i_mode))
+			error = -ENOTTY;
+			if (!filp->f_dentry || !filp->f_dentry->d_inode)
+				error = -ENOENT;
+			else if (S_ISREG(filp->f_dentry->d_inode->i_mode))
 				error = file_ioctl(filp, cmd, arg);
 			else if (filp->f_op && filp->f_op->ioctl)
 				error = filp->f_op->ioctl(filp->f_dentry->d_inode, filp, cmd, arg);
-			else
-				error = -ENOTTY;
 	}
+	fput(filp);
+
 out:
 	unlock_kernel();
 	return error;
diff -u --recursive --new-file v2.1.88/linux/fs/isofs/dir.c linux/fs/isofs/dir.c
--- v2.1.88/linux/fs/isofs/dir.c	Thu Dec 18 16:37:02 1997
+++ linux/fs/isofs/dir.c	Sat Feb 21 13:19:29 1998
@@ -101,11 +101,12 @@
 	unsigned long bufsize = ISOFS_BUFFER_SIZE(inode);
 	unsigned char bufbits = ISOFS_BUFFER_BITS(inode);
 	unsigned int block, offset;
-	int inode_number;
+	int inode_number = 0;	/* Quiet GCC */
 	struct buffer_head *bh;
 	int len;
 	int map;
-	int high_sierra = 0;
+	int high_sierra;
+	int first_de = 1;
 	char *p = NULL;		/* Quiet GCC */
 	struct iso_directory_record *de;
 
@@ -114,6 +115,7 @@
  
 	offset = filp->f_pos & (bufsize - 1);
 	block = isofs_bmap(inode, filp->f_pos >> bufbits);
+	high_sierra = inode->i_sb->u.isofs_sb.s_high_sierra;
 
 	if (!block)
 		return 0;
@@ -129,7 +131,7 @@
 	        printk("inode->i_size = %x\n",inode->i_size);
 #endif
 		de = (struct iso_directory_record *) (bh->b_data + offset);
-		inode_number = (block << bufbits) + (offset & (bufsize - 1));
+		if(first_de) inode_number = (block << bufbits) + (offset & (bufsize - 1));
 
 		de_len = *(unsigned char *) de;
 #ifdef DEBUG
@@ -177,6 +179,13 @@
 			break;
 		}
 
+		if(de->flags[-high_sierra] & 0x80) {
+			first_de = 0;
+			filp->f_pos += de_len;
+			continue;
+		}
+		first_de = 1;
+
 		/* Handle the case of the '.' directory */
 		if (de->name_len[0] == 1 && de->name[0] == 0) {
 			if (filldir(dirent, ".", 1, filp->f_pos, inode->i_ino) < 0)
@@ -200,7 +209,6 @@
 		   is no Rock Ridge NM field. */
 		if (inode->i_sb->u.isofs_sb.s_unhide == 'n') {
 			/* Do not report hidden or associated files */
-			high_sierra = inode->i_sb->u.isofs_sb.s_high_sierra;
 			if (de->flags[-high_sierra] & 5) {
 				filp->f_pos += de_len;
 				continue;
diff -u --recursive --new-file v2.1.88/linux/fs/isofs/inode.c linux/fs/isofs/inode.c
--- v2.1.88/linux/fs/isofs/inode.c	Thu Feb 12 20:56:12 1998
+++ linux/fs/isofs/inode.c	Sat Feb 21 13:19:29 1998
@@ -5,7 +5,8 @@
  *      1992, 1993, 1994  Eric Youngdale Modified for ISO9660 filesystem.
  *      1994  Eberhard Moenkeberg - multi session handling.
  *      1995  Mark Dobie - allow mounting of some weird VideoCDs and PhotoCDs.
- *
+ *	1997  Gordon Chaffee - Joliet CDs
+ *	1998  Eric Lammerts - ISO9660 Level 3
  */
 
 #include <linux/config.h>
@@ -655,17 +656,24 @@
 
 int isofs_bmap(struct inode * inode,int block)
 {
+	off_t b_off, offset, size;
+	struct inode *ino;
+	unsigned int firstext;
+	unsigned long nextino;
+	int i;
 
 	if (block<0) {
 		printk("_isofs_bmap: block<0");
 		return 0;
 	}
 
+	b_off = block << ISOFS_BUFFER_BITS(inode);
+
 	/*
 	 * If we are beyond the end of this file, don't give out any
 	 * blocks.
 	 */
-	if( (block << ISOFS_BUFFER_BITS(inode)) >= inode->i_size )
+	if( b_off > inode->i_size )
 	  {
 	    off_t	max_legal_read_offset;
 
@@ -679,7 +687,7 @@
 	     */
 	    max_legal_read_offset = (inode->i_size + PAGE_SIZE - 1)
 	      & ~(PAGE_SIZE - 1);
-	    if( (block << ISOFS_BUFFER_BITS(inode)) >= max_legal_read_offset )
+	    if( b_off >= max_legal_read_offset )
 	      {
 
 		printk("_isofs_bmap: block>= EOF(%d, %ld)\n", block,
@@ -688,7 +696,42 @@
 	    return 0;
 	  }
 
-	return (inode->u.isofs_i.i_first_extent >> ISOFS_BUFFER_BITS(inode)) + block;
+	offset = 0;
+	firstext = inode->u.isofs_i.i_first_extent;
+	size = inode->u.isofs_i.i_section_size;
+	nextino = inode->u.isofs_i.i_next_section_ino;
+#ifdef DEBUG
+	printk("first inode: inode=%lu nextino=%lu firstext=%u size=%lu\n",
+		inode->i_ino, nextino, firstext, size);
+#endif
+	i = 0;
+	while(b_off >= offset + size) {
+		offset += size;
+
+		if(nextino == 0) return 0;
+		ino = iget(inode->i_sb, nextino);
+		if(!ino) return 0;
+		firstext = ino->u.isofs_i.i_first_extent;
+		size = ino->u.isofs_i.i_section_size;
+#ifdef DEBUG
+		printk("read inode: inode=%lu ino=%lu nextino=%lu firstext=%u size=%lu\n",
+			inode->i_ino, nextino, ino->u.isofs_i.i_next_section_ino, firstext, size);
+#endif
+		nextino = ino->u.isofs_i.i_next_section_ino;
+		iput(ino);
+		
+		if(++i > 100) {
+			printk("isofs_bmap: More than 100 file sections ?!?, aborting...\n");
+			printk("isofs_bmap: ino=%lu block=%d firstext=%u size=%u nextino=%lu\n",
+				inode->i_ino, block, firstext, (unsigned)size, nextino);
+			return 0;
+		}
+	}
+#ifdef DEBUG
+	printk("isofs_bmap: mapped inode:block %lu:%d to block %lu\n",
+		inode->i_ino, block, (b_off - offset + firstext) >> ISOFS_BUFFER_BITS(inode));
+#endif
+	return (b_off - offset + firstext) >> ISOFS_BUFFER_BITS(inode);
 }
 
 
@@ -702,6 +745,82 @@
 	}
 }
 
+static int isofs_read_level3_size(struct inode * inode)
+{
+	unsigned long bufsize = ISOFS_BUFFER_SIZE(inode);
+	struct buffer_head * bh = NULL;
+	struct iso_directory_record * raw_inode = NULL;		/* quiet gcc */
+	unsigned char *pnt = NULL;
+	void *cpnt = NULL;
+	int block = 0;			/* Quiet GCC */
+	unsigned long ino;
+	int i;
+
+	inode->i_size = 0;
+	inode->u.isofs_i.i_next_section_ino = 0;
+	ino = inode->i_ino;
+	i = 0;
+	do {
+		if(i > 100) {
+			printk("isofs_read_level3_size: More than 100 file sections ?!?, aborting...\n"
+			       "isofs_read_level3_size: inode=%lu ino=%lu\n", inode->i_ino, ino);
+			return 0;
+		}
+
+		if(bh == NULL || block != ino >> ISOFS_BUFFER_BITS(inode)) {
+			if(bh) brelse(bh);
+			block = ino >> ISOFS_BUFFER_BITS(inode);
+			if (!(bh=bread(inode->i_dev,block, bufsize))) {
+				printk("unable to read i-node block");
+				return 1;
+			}
+		}
+		pnt = ((unsigned char *) bh->b_data
+		       + (ino & (bufsize - 1)));
+	
+		if ((ino & (bufsize - 1)) + *pnt > bufsize){
+		        int frag1, offset;
+	
+			offset = (ino & (bufsize - 1));
+			frag1 = bufsize - offset;
+		        cpnt = kmalloc(*pnt,GFP_KERNEL);
+			if (cpnt == NULL) {
+				printk(KERN_INFO "NoMem ISO inode %lu\n",inode->i_ino);
+				brelse(bh);
+				return 1;
+			}
+			memcpy(cpnt, bh->b_data + offset, frag1);
+			brelse(bh);
+			if (!(bh = bread(inode->i_dev,++block, bufsize))) {
+				kfree(cpnt);
+				printk("unable to read i-node block");
+				return 1;
+			}
+			offset += *pnt - bufsize;
+			memcpy((char *)cpnt+frag1, bh->b_data, offset);
+			pnt = ((unsigned char *) cpnt);
+		}
+		
+		if(*pnt == 0) {
+			ino = (ino & ~(ISOFS_BLOCK_SIZE - 1)) + ISOFS_BLOCK_SIZE;
+			continue;
+		}
+		raw_inode = ((struct iso_directory_record *) pnt);
+
+		inode->i_size += isonum_733 (raw_inode->size);
+		if(i == 1) inode->u.isofs_i.i_next_section_ino = ino;
+
+		ino += *pnt;
+		if (cpnt) {
+			kfree (cpnt);
+			cpnt = NULL;
+		}
+		i++;
+	} while(raw_inode->flags[-inode->i_sb->u.isofs_sb.s_high_sierra] & 0x80);
+	brelse(bh);
+	return 0;
+}
+
 void isofs_read_inode(struct inode * inode)
 {
 	unsigned long bufsize = ISOFS_BUFFER_SIZE(inode);
@@ -744,8 +863,15 @@
 	}
 	inode->i_uid = inode->i_sb->u.isofs_sb.s_uid;
 	inode->i_gid = inode->i_sb->u.isofs_sb.s_gid;
-	inode->i_size = isonum_733 (raw_inode->size);
 	inode->i_blocks = inode->i_blksize = 0;
+
+
+	inode->u.isofs_i.i_section_size = isonum_733 (raw_inode->size);
+	if(raw_inode->flags[-high_sierra] & 0x80) {
+		if(isofs_read_level3_size(inode)) goto fail;
+	} else {
+		inode->i_size = isonum_733 (raw_inode->size);
+	}
 
 	/* There are defective discs out there - we do this to protect
 	   ourselves.  A cdrom will never contain more than 800Mb */
diff -u --recursive --new-file v2.1.88/linux/fs/lockd/svcproc.c linux/fs/lockd/svcproc.c
--- v2.1.88/linux/fs/lockd/svcproc.c	Mon Apr  7 11:35:30 1997
+++ linux/fs/lockd/svcproc.c	Sat Feb 28 11:19:57 1998
@@ -63,7 +63,7 @@
 
 		/* Set up the missing parts of the file_lock structure */
 		lock->fl.fl_file  = &file->f_file;
-		lock->fl.fl_owner = host;
+		lock->fl.fl_owner = (fl_owner_t) host;
 	}
 
 	return 0;
diff -u --recursive --new-file v2.1.88/linux/fs/lockd/xdr.c linux/fs/lockd/xdr.c
--- v2.1.88/linux/fs/lockd/xdr.c	Mon Apr  7 11:35:30 1997
+++ linux/fs/lockd/xdr.c	Sat Feb 28 11:20:34 1998
@@ -128,7 +128,7 @@
 		return NULL;
 
 	memset(fl, 0, sizeof(*fl));
-	fl->fl_owner = current;
+	fl->fl_owner = current->files;
 	fl->fl_pid   = ntohl(*p++);
 	fl->fl_flags = FL_POSIX;
 	fl->fl_type  = F_RDLCK;		/* as good as anything else */
diff -u --recursive --new-file v2.1.88/linux/fs/locks.c linux/fs/locks.c
--- v2.1.88/linux/fs/locks.c	Mon Feb 23 18:12:10 1998
+++ linux/fs/locks.c	Thu Mar  5 11:55:06 1998
@@ -51,7 +51,7 @@
  *
  *  Removed some race conditions in flock_lock_file(), marked other possible
  *  races. Just grep for FIXME to see them. 
- *  Dmitry Gorodchanin (begemot@bgm.rosprint.net), February 09, 1996.
+ *  Dmitry Gorodchanin (pgmdsg@ibi.com), February 09, 1996.
  *
  *  Addressed Dmitry's concerns. Deadlock checking no longer recursive.
  *  Lock allocation changed to GFP_ATOMIC as we can't afford to sleep
@@ -111,6 +111,7 @@
 #include <linux/errno.h>
 #include <linux/stat.h>
 #include <linux/fcntl.h>
+#include <linux/file.h>
 #include <linux/smp.h>
 #include <linux/smp_lock.h>
 
@@ -176,7 +177,10 @@
 		(fl2->fl_end >= fl1->fl_start));
 }
 
-/* Check whether two locks have the same owner
+/*
+ * Check whether two locks have the same owner
+ * N.B. Do we need the test on PID as well as owner?
+ * (Clone tasks should be considered as one "owner".)
  */
 static inline int
 locks_same_owner(struct file_lock *fl1, struct file_lock *fl2)
@@ -289,15 +293,21 @@
 	int error;
 
 	lock_kernel();
-	if ((fd >= NR_OPEN) || !(filp = current->files->fd[fd]))
-		error = -EBADF;
-	else if (!flock_make_lock(filp, &file_lock, cmd))
-		error = -EINVAL;
-	else if ((file_lock.fl_type != F_UNLCK) && !(filp->f_mode & 3))
-		error = -EBADF;
-	else
-		error = flock_lock_file(filp, &file_lock,
-					(cmd & (LOCK_UN | LOCK_NB)) ? 0 : 1);
+	error = -EBADF;
+	filp = fget(fd);
+	if (!filp)
+		goto out;
+	error = -EINVAL;
+	if (!flock_make_lock(filp, &file_lock, cmd))
+		goto out_putf;
+	error = -EBADF;
+	if ((file_lock.fl_type != F_UNLCK) && !(filp->f_mode & 3))
+		goto out_putf;
+	error = flock_lock_file(filp, &file_lock,
+				(cmd & (LOCK_UN | LOCK_NB)) ? 0 : 1);
+out_putf:
+	fput(filp);
+out:
 	unlock_kernel();
 	return (error);
 }
@@ -307,36 +317,34 @@
  */
 int fcntl_getlk(unsigned int fd, struct flock *l)
 {
-	struct flock flock;
 	struct file *filp;
-	struct dentry *dentry;
-	struct inode *inode;
 	struct file_lock *fl,file_lock;
+	struct flock flock;
 	int error;
 
-	if ((fd >= NR_OPEN) || !(filp = current->files->fd[fd]))
-		return -EBADF;
+	error = -EFAULT;
 	if (copy_from_user(&flock, l, sizeof(flock)))
-		return -EFAULT;
-
+		goto out;
+	error = -EINVAL;
 	if ((flock.l_type != F_RDLCK) && (flock.l_type != F_WRLCK))
-		return -EINVAL;
+		goto out;
 
-	dentry = filp->f_dentry;
-	if (!dentry)
-		return -EINVAL;
-
-	inode = dentry->d_inode;
-	if (!inode)
-		return -EINVAL;
+	error = -EBADF;
+	filp = fget(fd);
+	if (!filp)
+		goto out;
+
+	error = -EINVAL;
+	if (!filp->f_dentry || !filp->f_dentry->d_inode)
+		goto out_putf;
 
 	if (!posix_make_lock(filp, &file_lock, &flock))
-		return -EINVAL;
+		goto out_putf;
 
 	if (filp->f_op->lock) {
 		error = filp->f_op->lock(filp, F_GETLK, &file_lock);
 		if (error < 0)
-			return error;
+			goto out_putf;
 		fl = &file_lock;
 	} else {
 		fl = posix_test_lock(filp, &file_lock);
@@ -351,8 +359,14 @@
 		flock.l_whence = 0;
 		flock.l_type = fl->fl_type;
 	}
+	error = -EFAULT;
+	if (!copy_to_user(l, &flock, sizeof(flock)))
+		error = 0;
   
-	return (copy_to_user(l, &flock, sizeof(flock)) ? -EFAULT : 0);
+out_putf:
+	fput(filp);
+out:
+	return error;
 }
 
 /* Apply the lock described by l to an open file descriptor.
@@ -367,22 +381,26 @@
 	struct inode *inode;
 	int error;
 
+	/*
+	 * This might block, so we do it before checking the inode.
+	 */
+	error = -EFAULT;
+	if (copy_from_user(&flock, l, sizeof(flock)))
+		goto out;
+
 	/* Get arguments and validate them ...
 	 */
 
-	if ((fd >= NR_OPEN) || !(filp = current->files->fd[fd]))
-		return -EBADF;
+	error = -EBADF;
+	filp = fget(fd);
+	if (!filp)
+		goto out;
 
+	error = -EINVAL;
 	if (!(dentry = filp->f_dentry))
-		return -EINVAL;
-
+		goto out_putf;
 	if (!(inode = dentry->d_inode))
-		return -EINVAL;
-	/*
-	 * This might block, so we do it before checking the inode.
-	 */
-	if (copy_from_user(&flock, l, sizeof(flock)))
-		return (-EFAULT);
+		goto out_putf;
 
 	/* Don't allow mandatory locks on files that may be memory mapped
 	 * and shared.
@@ -391,23 +409,26 @@
 	    (inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID &&
 	    inode->i_mmap) {
 		struct vm_area_struct *vma = inode->i_mmap;
+		error = -EAGAIN;
 		do {
 			if (vma->vm_flags & VM_MAYSHARE)
-				return (-EAGAIN);
+				goto out_putf;
 		} while ((vma = vma->vm_next_share) != NULL);
 	}
 
+	error = -EINVAL;
 	if (!posix_make_lock(filp, &file_lock, &flock))
-		return (-EINVAL);
+		goto out_putf;
 	
+	error = -EBADF;
 	switch (flock.l_type) {
 	case F_RDLCK:
 		if (!(filp->f_mode & FMODE_READ))
-			return (-EBADF);
+			goto out_putf;
 		break;
 	case F_WRLCK:
 		if (!(filp->f_mode & FMODE_WRITE))
-			return (-EBADF);
+			goto out_putf;
 		break;
 	case F_UNLCK:
 		break;
@@ -425,27 +446,32 @@
 	}
 }
 		if (!(filp->f_mode & 3))
-			return (-EBADF);
+			goto out_putf;
 		break;
 #endif
 	default:
-		return (-EINVAL);
+		error = -EINVAL;
+		goto out_putf;
 	}
 
 	if (filp->f_op->lock != NULL) {
 		error = filp->f_op->lock(filp, cmd, &file_lock);
 		if (error < 0)
-			return (error);
+			goto out_putf;
 	}
+	error = posix_lock_file(filp, &file_lock, cmd == F_SETLKW);
 
-	return (posix_lock_file(filp, &file_lock, cmd == F_SETLKW));
+out_putf:
+	fput(filp);
+out:
+	return error;
 }
 
 /*
  * This function is called when the file is being removed
  * from the task's fd array.
  */
-void locks_remove_posix(struct task_struct *task, struct file *filp)
+void locks_remove_posix(struct file *filp, fl_owner_t owner)
 {
 	struct inode * inode = filp->f_dentry->d_inode;
 	struct file_lock file_lock, *fl;
@@ -457,7 +483,7 @@
 repeat:
 	before = &inode->i_flock;
 	while ((fl = *before) != NULL) {
-		if ((fl->fl_flags & FL_POSIX) && fl->fl_owner == task) {
+		if ((fl->fl_flags & FL_POSIX) && fl->fl_owner == owner) {
 			int (*lock)(struct file *, int, struct file_lock *);
 			lock = filp->f_op->lock;
 			if (lock) {
@@ -548,6 +574,7 @@
 
 int locks_mandatory_locked(struct inode *inode)
 {
+	fl_owner_t owner = current->files;
 	struct file_lock *fl;
 
 	/* Search the lock list for this inode for any POSIX locks.
@@ -555,7 +582,7 @@
 	for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
 		if (!(fl->fl_flags & FL_POSIX))
 			continue;
-		if (fl->fl_owner != current)
+		if (fl->fl_owner != owner)
 			return (-EAGAIN);
 	}
 	return (0);
@@ -572,7 +599,7 @@
 
 	tfl.fl_file = filp;
 	tfl.fl_flags = FL_POSIX | FL_ACCESS;
-	tfl.fl_owner = current;
+	tfl.fl_owner = current->files;
 	tfl.fl_pid = current->pid;
 	tfl.fl_type = (read_write == FLOCK_VERIFY_WRITE) ? F_WRLCK : F_RDLCK;
 	tfl.fl_start = offset;
@@ -656,7 +683,7 @@
 		fl->fl_end = OFFSET_MAX;
 	
 	fl->fl_file = filp;
-	fl->fl_owner = current;
+	fl->fl_owner = current->files;
 	fl->fl_pid = current->pid;
 
 	return (1);
diff -u --recursive --new-file v2.1.88/linux/fs/ncpfs/Config.in linux/fs/ncpfs/Config.in
--- v2.1.88/linux/fs/ncpfs/Config.in	Wed Dec 31 16:00:00 1969
+++ linux/fs/ncpfs/Config.in	Fri Mar  6 10:03:04 1998
@@ -0,0 +1,10 @@
+#
+# NCP Filesystem configuration
+#
+# bool     '   Packet singatures' CONFIG_NCPFS_PACKET_SIGNING
+bool     '   Proprietary file locking' CONFIG_NCPFS_IOCTL_LOCKING
+bool     '   Clear remove/delete inhibit when needed' CONFIG_NCPFS_STRONG
+bool     '   Use NFS namespace if available' CONFIG_NCPFS_NFS_NS
+bool     '   Use LONG (OS/2) namespace if available' CONFIG_NCPFS_OS2_NS
+bool     '   Allow mounting of volume subdirectories' CONFIG_NCPFS_MOUNT_SUBDIR
+# bool     '   NDS interserver authentication support' CONFIG_NCPFS_NDS_DOMAINS
diff -u --recursive --new-file v2.1.88/linux/fs/ncpfs/dir.c linux/fs/ncpfs/dir.c
--- v2.1.88/linux/fs/ncpfs/dir.c	Sun Jan  4 00:53:42 1998
+++ linux/fs/ncpfs/dir.c	Fri Mar  6 10:03:04 1998
@@ -7,6 +7,8 @@
  *
  */
 
+#include <linux/config.h>
+
 #include <linux/sched.h>
 #include <linux/errno.h>
 #include <linux/stat.h>
@@ -247,6 +249,98 @@
  * This is the callback when the dcache has a lookup hit.
  */
 
+
+#ifdef CONFIG_NCPFS_STRONG
+/* try to delete a readonly file (NW R bit set) */
+
+static int
+ncp_force_unlink(struct inode *dir, struct dentry* dentry)
+{
+        int res=0x9c,res2;
+        struct iattr ia;
+
+        /* remove the Read-Only flag on the NW server */
+
+        memset(&ia,0,sizeof(struct iattr));
+        ia.ia_mode = dentry->d_inode->i_mode;
+        ia.ia_mode |= NCP_SERVER(dir)->m.file_mode & 0222;  /* set write bits */
+        ia.ia_valid = ATTR_MODE;
+
+        res2=ncp_notify_change(dentry, &ia);
+        if (res2)
+        {
+                goto leave_me;
+        }
+
+        /* now try again the delete operation */
+
+        res = ncp_del_file_or_subdir2(NCP_SERVER(dir), dentry);
+
+        if (res)  /* delete failed, set R bit again */
+        {
+                memset(&ia,0,sizeof(struct iattr));
+                ia.ia_mode = dentry->d_inode->i_mode;
+                ia.ia_mode &= ~(NCP_SERVER(dir)->m.file_mode & 0222);  /* clear write bits */
+                ia.ia_valid = ATTR_MODE;
+
+                res2=ncp_notify_change(dentry, &ia);
+                if (res2)
+                {
+                        goto leave_me;
+                }
+        }
+leave_me:
+        return(res);
+}
+#endif	/* CONFIG_NCPFS_STRONG */
+
+#ifdef CONFIG_NCPFS_STRONG
+static int
+ncp_force_rename(struct inode *old_dir, struct dentry* old_dentry, char *_old_name,
+                 struct inode *new_dir, struct dentry* new_dentry, char *_new_name)
+{
+        int res=0x90,res2;
+        struct iattr ia;
+
+        /* remove the Read-Only flag on the NW server */
+
+        memset(&ia,0,sizeof(struct iattr));
+        ia.ia_mode = old_dentry->d_inode->i_mode;
+        ia.ia_mode |= NCP_SERVER(old_dir)->m.file_mode & 0222;  /* set write bits */
+        ia.ia_valid = ATTR_MODE;
+
+        res2=ncp_notify_change(old_dentry, &ia);
+        if (res2)
+        {
+                goto leave_me;
+        }
+
+        /* now try again the rename operation */
+        res = ncp_ren_or_mov_file_or_subdir(NCP_SERVER(old_dir),
+                                            old_dir, _old_name,
+                                            new_dir, _new_name);
+
+        memset(&ia,0,sizeof(struct iattr));
+        ia.ia_mode = old_dentry->d_inode->i_mode;
+        ia.ia_mode &= ~(NCP_SERVER(old_dentry->d_inode)->m.file_mode & 0222);  /* clear write bits */
+        ia.ia_valid = ATTR_MODE;
+
+	/* FIXME: uses only inode info, no dentry info... so it is safe to call */
+	/* it now with old dentry. If we use name (in future), we have to move */
+	/* it after dentry_move in caller */
+        res2=ncp_notify_change(old_dentry, &ia);
+        if (res2)
+        {
+                printk(KERN_INFO "ncpfs: ncp_notify_change (2) failed: %08x\n",res2);
+                goto leave_me;
+        }
+
+ leave_me:
+        return(res);
+}
+#endif	/* CONFIG_NCPFS_STRONG */
+
+
 static int
 ncp_lookup_validate(struct dentry * dentry)
 {
@@ -314,13 +408,15 @@
 		 * If we didn't find it, or if it has a different dirEntNum to
 		 * what we remember, it's not valid any more.
 		 */
-	   	if (!res)
+	   	if (!res) {
 		  if (finfo.nw_info.i.dirEntNum == NCP_FINFO(dentry->d_inode)->dirEntNum)
 		    val=1;
 #ifdef NCPFS_PARANOIA
 		  else
 		    printk(KERN_DEBUG "ncp_lookup_validate: found, but dirEntNum changed\n");
 #endif
+ 		  ncp_update_inode2(dentry->d_inode, &finfo.nw_info);
+		}
 		if (!val) ncp_invalid_dir_cache(dir);
 
 finished:
@@ -617,6 +713,8 @@
 	int result;
 
 	if (ncp_single_volume(server)) {
+		struct dentry* dent;
+
 		result = -ENOENT;
 		str_upper(server->m.mounted_vol);
 		if (ncp_lookup_volume(server, server->m.mounted_vol,
@@ -627,6 +725,19 @@
 			goto out;
 		}
 		str_lower(server->root.finfo.i.entryName);
+		dent = server->root_dentry;
+		if (dent) {
+			struct inode* ino = dent->d_inode;
+			if (ino) {
+				NCP_FINFO(ino)->volNumber = server->root.finfo.i.volNumber;
+				NCP_FINFO(ino)->dirEntNum = server->root.finfo.i.dirEntNum;
+				NCP_FINFO(ino)->DosDirNum = server->root.finfo.i.DosDirNum;
+			} else {
+				DPRINTK(KERN_DEBUG "ncpfs: sb->root_dentry->d_inode == NULL!\n");
+			}
+		} else {
+			DPRINTK(KERN_DEBUG "ncpfs: sb->root_dentry == NULL!\n");
+		}
 	}
 	result = 0;
 
@@ -814,6 +925,7 @@
 		finfo.nw_info.access = O_RDWR;
 		error = ncp_instantiate(dir, dentry, &finfo);
 	} else {
+		if (result == 0x87) error = -ENAMETOOLONG;
 		DPRINTK(KERN_DEBUG "ncp_create: %s/%s failed\n",
 			dentry->d_parent->d_name.name, dentry->d_name.name);
 	}
@@ -904,8 +1016,7 @@
 static int ncp_unlink(struct inode *dir, struct dentry *dentry)
 {
 	struct inode *inode = dentry->d_inode;
-	int error, result;
-	__u8 _name[dentry->d_name.len + 1];
+	int error;
 
 	DPRINTK(KERN_DEBUG "ncp_unlink: unlinking %s/%s\n",
 		dentry->d_parent->d_name.name, dentry->d_name.name);
@@ -929,21 +1040,23 @@
 		ncp_make_closed(inode);
 	}
 
-	strncpy(_name, dentry->d_name.name, dentry->d_name.len);
-	_name[dentry->d_name.len] = '\0';
-	if (!ncp_preserve_case(dir))
-	{
-		str_upper(_name);
+	error = ncp_del_file_or_subdir2(NCP_SERVER(dir), dentry);
+#ifdef CONFIG_NCPFS_STRONG
+	if (error == 0x9C && NCP_SERVER(dir)->m.flags & NCP_MOUNT_STRONG) { /* R/O */
+		error = ncp_force_unlink(dir, dentry);
 	}
-	error = -EACCES;
-	result = ncp_del_file_or_subdir(NCP_SERVER(dir), dir, _name);
-	if (!result) {
+#endif
+	if (!error) {
 		DPRINTK(KERN_DEBUG "ncp: removed %s/%s\n",
 			dentry->d_parent->d_name.name, dentry->d_name.name);
 		ncp_invalid_dir_cache(dir);
 		d_delete(dentry);
-		error = 0;
+	} else if (error == 0xFF) {
+		error = -ENOENT;
+	} else {
+		error = -EACCES;
 	}
+		
 out:
 	return error;
 }
@@ -953,7 +1066,7 @@
 {
 	int old_len = old_dentry->d_name.len;
 	int new_len = new_dentry->d_name.len;
-	int error, result;
+	int error;
 	char _old_name[old_dentry->d_name.len + 1];
 	char _new_name[new_dentry->d_name.len + 1];
 
@@ -986,18 +1099,28 @@
 		str_upper(_new_name);
 	}
 
-	error = -EACCES;
-	result = ncp_ren_or_mov_file_or_subdir(NCP_SERVER(old_dir),
+	error = ncp_ren_or_mov_file_or_subdir(NCP_SERVER(old_dir),
 					    old_dir, _old_name,
 					    new_dir, _new_name);
-	if (result == 0)
+#ifdef CONFIG_NCPFS_STRONG
+	if (error == 0x90 && NCP_SERVER(old_dir)->m.flags & NCP_MOUNT_STRONG) {	/* RO */
+		error = ncp_force_rename(old_dir, old_dentry, _old_name, new_dir, new_dentry, _new_name);
+	}
+#endif
+	if (error == 0)
 	{
 		DPRINTK(KERN_DEBUG "ncp renamed %s -> %s.\n",
 			old_dentry->d_name.name,new_dentry->d_name.name);
 		ncp_invalid_dir_cache(old_dir);
 		ncp_invalid_dir_cache(new_dir);
 		d_move(old_dentry,new_dentry);
-		error = 0;
+	} else {
+		if (error == 0x9E)
+			error = -ENAMETOOLONG;
+		else if (error == 0xFF)
+			error = -ENOENT;
+		else
+			error = -EACCES;
 	}
 out:
 	return error;
diff -u --recursive --new-file v2.1.88/linux/fs/ncpfs/inode.c linux/fs/ncpfs/inode.c
--- v2.1.88/linux/fs/ncpfs/inode.c	Sun Jan  4 00:53:42 1998
+++ linux/fs/ncpfs/inode.c	Fri Mar  6 17:48:43 1998
@@ -32,12 +32,9 @@
 #include <linux/ncp_fs.h>
 #include "ncplib_kernel.h"
 
-extern int close_fp(struct file *filp);
-
 static void ncp_read_inode(struct inode *);
 static void ncp_put_inode(struct inode *);
 static void ncp_delete_inode(struct inode *);
-static int  ncp_notify_change(struct dentry *, struct iattr *);
 static void ncp_put_super(struct super_block *);
 static int  ncp_statfs(struct super_block *, struct statfs *, int);
 
@@ -54,6 +51,8 @@
 	NULL                    /* remount */
 };
 
+extern struct dentry_operations ncp_dentry_operations;
+
 static struct nw_file_info *read_nwinfo = NULL;
 static struct semaphore read_sem = MUTEX;
 
@@ -77,6 +76,31 @@
 #endif
 }
 
+void ncp_update_inode2(struct inode* inode, struct nw_file_info *nwinfo)
+{
+	struct nw_info_struct *nwi = &nwinfo->i;
+	struct ncp_server *server = NCP_SERVER(inode);
+
+	if (!NCP_FINFO(inode)->opened) {
+		if (nwi->attributes & aDIR) {
+			inode->i_mode = server->m.dir_mode;
+			inode->i_size = 512;
+		} else {
+			inode->i_mode = server->m.file_mode;
+			inode->i_size = le32_to_cpu(nwi->dataStreamSize);
+		}
+		if (nwi->attributes & aRONLY) inode->i_mode &= ~0222;
+	}
+	inode->i_blocks = 0;
+	if ((inode->i_size)&&(inode->i_blksize)) {
+		inode->i_blocks = (inode->i_size-1)/(inode->i_blksize)+1;
+	}
+	/* TODO: times? I'm not sure... */
+	NCP_FINFO(inode)->DosDirNum = nwinfo->i.DosDirNum;
+	NCP_FINFO(inode)->dirEntNum = nwinfo->i.dirEntNum;
+	NCP_FINFO(inode)->volNumber = nwinfo->i.volNumber;
+}
+
 /*
  * Fill in the inode based on the nw_file_info structure.
  */
@@ -94,6 +118,7 @@
 		inode->i_mode = server->m.file_mode;
 		inode->i_size = le32_to_cpu(nwi->dataStreamSize);
 	}
+	if (nwi->attributes & aRONLY) inode->i_mode &= ~0222;
 
 	DDPRINTK(KERN_DEBUG "ncp_read_inode: inode->i_mode = %u\n", inode->i_mode);
 
@@ -212,6 +237,7 @@
 	root->finfo.opened= 0;
 	info->ino = 2; /* tradition */
 	info->nw_info = root->finfo;
+	return;
 }
 
 struct super_block *
@@ -223,6 +249,9 @@
 	struct inode *root_inode;
 	kdev_t dev = sb->s_dev;
 	int error;
+#ifdef CONFIG_NCPFS_PACKET_SIGNING
+	int options;
+#endif
 	struct ncpfs_inode_info finfo;
 
 	MOD_INC_USE_COUNT;
@@ -230,13 +259,13 @@
 		goto out_no_data;
 	if (data->version != NCP_MOUNT_VERSION)
 		goto out_bad_mount;
-	if ((data->ncp_fd >= NR_OPEN) ||
-	    ((ncp_filp = current->files->fd[data->ncp_fd]) == NULL) ||
-	    !S_ISSOCK(ncp_filp->f_dentry->d_inode->i_mode))
+	ncp_filp = fget(data->ncp_fd);
+	if (!ncp_filp)
 		goto out_bad_file;
+	if (!S_ISSOCK(ncp_filp->f_dentry->d_inode->i_mode))
+		goto out_bad_file2;
 
 	lock_super(sb);
-	ncp_filp->f_count++;
 
 	sb->s_blocksize = 1024;	/* Eh...  Is this correct? */
 	sb->s_blocksize_bits = 10;
@@ -257,6 +286,17 @@
 	server->packet = NULL;
 	server->buffer_size = 0;
 	server->conn_status = 0;
+	server->root_dentry = NULL;
+#ifdef CONFIG_NCPFS_PACKET_SIGNING
+	server->sign_wanted = 0;
+	server->sign_active = 0;
+#endif
+	server->auth.auth_type = NCP_AUTH_NONE;
+	server->auth.object_name_len = 0;
+	server->auth.object_name = NULL;
+	server->auth.object_type = 0;
+	server->priv.len = 0;
+	server->priv.data = NULL;
 
 	server->m = *data;
 	/* Althought anything producing this is buggy, it happens
@@ -282,21 +322,41 @@
 		goto out_no_connect;
 	DPRINTK(KERN_DEBUG "ncp_read_super: NCP_SBP(sb) = %x\n", (int) NCP_SBP(sb));
 
-	error = ncp_negotiate_buffersize(server, NCP_DEFAULT_BUFSIZE,
-				    		&(server->buffer_size));
-	if (error)
+#ifdef CONFIG_NCPFS_PACKET_SIGNING
+	if (ncp_negotiate_size_and_options(server, NCP_DEFAULT_BUFSIZE,
+		NCP_DEFAULT_OPTIONS, &(server->buffer_size), &options) == 0)
+	{
+		if (options != NCP_DEFAULT_OPTIONS)
+		{
+			if (ncp_negotiate_size_and_options(server, 
+				NCP_DEFAULT_BUFSIZE,
+				options & 2, 
+				&(server->buffer_size), &options) != 0)
+				
+			{
+				goto out_no_bufsize;
+			}
+		}
+		if (options & 2)
+			server->sign_wanted = 1;
+	}
+	else 
+#endif	/* CONFIG_NCPFS_PACKET_SIGNING */
+	if (ncp_negotiate_buffersize(server, NCP_DEFAULT_BUFSIZE,
+  				     &(server->buffer_size)) != 0)
 		goto out_no_bufsize;
 	DPRINTK(KERN_DEBUG "ncpfs: bufsize = %d\n", server->buffer_size);
 
 	ncp_init_root(server, &finfo);
+	server->name_space[finfo.nw_info.i.volNumber] = NW_NS_DOS;
         root_inode = ncp_iget(sb, &finfo);
         if (!root_inode)
 		goto out_no_root;
 	DPRINTK(KERN_DEBUG "ncp_read_super: root vol=%d\n", NCP_FINFO(root_inode)->volNumber);
-        sb->s_root = d_alloc_root(root_inode, NULL);
+        server->root_dentry = sb->s_root = d_alloc_root(root_inode, NULL);
         if (!sb->s_root)
 		goto out_no_root;
-
+	server->root_dentry->d_op = &ncp_dentry_operations;
 	unlock_super(sb);
 	return sb;
 
@@ -328,6 +388,8 @@
 	unlock_super(sb);
 	goto out;
 
+out_bad_file2:
+	fput(ncp_filp);
 out_bad_file:
 	printk(KERN_ERR "ncp_read_super: invalid ncp socket\n");
 	goto out;
@@ -353,9 +415,13 @@
 	ncp_disconnect(server);
 	ncp_unlock_server(server);
 
-	close_fp(server->ncp_filp);
+	fput(server->ncp_filp);
 	kill_proc(server->m.wdog_pid, SIGTERM, 1);
 
+	if (server->priv.data) 
+		ncp_kfree_s(server->priv.data, server->priv.len);
+	if (server->auth.object_name)
+		ncp_kfree_s(server->auth.object_name, server->auth.object_name_len);
 	ncp_kfree_s(server->packet, server->packet_size);
 
 	ncp_kfree_s(NCP_SBP(sb), sizeof(struct ncp_server));
@@ -387,7 +453,7 @@
 	return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0;
 }
 
-static int ncp_notify_change(struct dentry *dentry, struct iattr *attr)
+int ncp_notify_change(struct dentry *dentry, struct iattr *attr)
 {
 	struct inode *inode = dentry->d_inode;
 	int result = 0;
@@ -418,6 +484,33 @@
 
 	info_mask = 0;
 	memset(&info, 0, sizeof(info));
+
+#if 1 
+        if ((attr->ia_valid & ATTR_MODE) != 0)
+        {
+                if (!S_ISREG(inode->i_mode))
+                {
+                        return -EPERM;
+                }
+                else
+                {
+			umode_t newmode;
+
+                        info_mask |= DM_ATTRIBUTES;
+                        newmode=attr->ia_mode;
+                        newmode &= NCP_SERVER(inode)->m.file_mode;
+
+                        if (newmode & 0222) /* any write bit set */
+                        {
+                                info.attributes &= ~0x60001;
+                        }
+                        else
+                        {
+                                info.attributes |= 0x60001;
+                        }
+                }
+        }
+#endif
 
 	if ((attr->ia_valid & ATTR_CTIME) != 0) {
 		info_mask |= (DM_CREATE_TIME | DM_CREATE_DATE);
diff -u --recursive --new-file v2.1.88/linux/fs/ncpfs/ioctl.c linux/fs/ncpfs/ioctl.c
--- v2.1.88/linux/fs/ncpfs/ioctl.c	Wed Nov 12 11:10:40 1997
+++ linux/fs/ncpfs/ioctl.c	Fri Mar  6 10:03:04 1998
@@ -6,6 +6,8 @@
  *
  */
 
+#include <linux/config.h>
+
 #include <asm/uaccess.h>
 #include <linux/errno.h>
 #include <linux/fs.h>
@@ -15,6 +17,12 @@
 
 #include <linux/ncp.h>
 #include <linux/ncp_fs.h>
+#include "ncplib_kernel.h"
+
+/* maximum limit for ncp_objectname_ioctl */
+#define NCP_OBJECT_NAME_MAX_LEN	4096
+/* maximum limit for ncp_privatedata_ioctl */
+#define NCP_PRIVATE_DATA_MAX_LEN 8192
 
 int ncp_ioctl(struct inode *inode, struct file *filp,
 	      unsigned int cmd, unsigned long arg)
@@ -113,9 +121,364 @@
 		put_user(server->m.mounted_uid, (uid_t *) arg);
 		return 0;
 
+	case NCP_IOC_GETMOUNTUID_INT:
+		if (   (permission(inode, MAY_READ) != 0)
+		    && (current->uid != server->m.mounted_uid))
+		{
+			return -EACCES;
+		}
+
+		{
+			unsigned int tmp=server->m.mounted_uid;
+			if (put_user(tmp, (unsigned long*) arg)) return -EFAULT;
+		}
+		return 0;
+
+#ifdef CONFIG_NCPFS_MOUNT_SUBDIR
+	case NCP_IOC_GETROOT:
+		{
+			struct ncp_setroot_ioctl sr;
+
+			if (   (permission(inode, MAY_READ) != 0)
+			    && (current->uid != server->m.mounted_uid))
+			{
+				return -EACCES;
+			}
+			if (server->m.mounted_vol[0]) {
+				sr.volNumber = server->root.finfo.i.volNumber;
+				sr.dirEntNum = server->root.finfo.i.dirEntNum;
+				sr.namespace = server->name_space[sr.volNumber];
+			} else {
+				sr.volNumber = -1;
+				sr.namespace = 0;
+				sr.dirEntNum = 0;
+			}
+			if (copy_to_user((struct ncp_setroot_ioctl*)arg, 
+				    	  &sr, 
+					  sizeof(sr))) return -EFAULT;
+			return 0;
+		}
+	case NCP_IOC_SETROOT:
+		{
+			struct ncp_setroot_ioctl sr;
+			struct dentry* dentry;
+
+			if (   (permission(inode, MAY_WRITE) != 0)
+			    && (current->uid != server->m.mounted_uid))
+			{
+				return -EACCES;
+			}
+			if (copy_from_user(&sr,
+					   (struct ncp_setroot_ioctl*)arg, 
+					   sizeof(sr))) return -EFAULT;
+			if (sr.volNumber < 0) {
+				server->m.mounted_vol[0] = 0;
+				server->root.finfo.i.volNumber = NCP_NUMBER_OF_VOLUMES + 1;
+				server->root.finfo.i.dirEntNum = 0;
+				server->root.finfo.i.DosDirNum = 0;
+			} else if (sr.volNumber >= NCP_NUMBER_OF_VOLUMES) {
+				return -EINVAL;
+			} else {
+				if (ncp_mount_subdir(server, sr.volNumber, sr.namespace, sr.dirEntNum)) {
+					return -ENOENT;
+				}
+			}
+			dentry = server->root_dentry;
+			if (dentry) {
+				struct inode* inode = dentry->d_inode;
+				
+				if (inode) {
+					NCP_FINFO(inode)->volNumber = server->root.finfo.i.volNumber;
+					NCP_FINFO(inode)->dirEntNum = server->root.finfo.i.dirEntNum;
+					NCP_FINFO(inode)->DosDirNum = server->root.finfo.i.DosDirNum;
+				} else {
+					DPRINTK(KERN_DEBUG "ncpfs: root_dentry->d_inode==NULL\n");
+				}
+			} else {
+				DPRINTK(KERN_DEBUG "ncpfs: root_dentry==NULL\n");
+			}
+			return 0;
+		}
+#endif	/* CONFIG_NCPFS_MOUNT_SUBDIR */
+
+#ifdef CONFIG_NCPFS_PACKET_SIGNING	
+	case NCP_IOC_SIGN_INIT:
+		if ((permission(inode, MAY_WRITE) != 0)
+		    && (current->uid != server->m.mounted_uid))
+		{
+			return -EACCES;
+		}
+		if (server->sign_active)
+		{
+			return -EINVAL;
+		}
+		if (server->sign_wanted)
+		{
+			struct ncp_sign_init sign;
+
+			if (copy_from_user(&sign, (struct ncp_sign_init *) arg,
+			      sizeof(sign))) return -EFAULT;
+			memcpy(server->sign_root,sign.sign_root,8);
+			memcpy(server->sign_last,sign.sign_last,16);
+			server->sign_active = 1;
+		}
+		/* ignore when signatures not wanted */
+		return 0;		
+		
+        case NCP_IOC_SIGN_WANTED:
+		if (   (permission(inode, MAY_READ) != 0)
+		    && (current->uid != server->m.mounted_uid))
+		{
+			return -EACCES;
+		}
+		
+                if (put_user(server->sign_wanted, (int*) arg))
+			return -EFAULT;
+                return 0;
+	case NCP_IOC_SET_SIGN_WANTED:
+		{
+			int newstate;
+
+			if (   (permission(inode, MAY_WRITE) != 0)
+			    && (current->uid != server->m.mounted_uid))
+			{
+				return -EACCES;
+			}
+			/* get only low 8 bits... */
+			get_user_ret(newstate, (unsigned char*)arg, -EFAULT);
+			if (server->sign_active) {
+				/* cannot turn signatures OFF when active */
+				if (!newstate) return -EINVAL;
+			} else {
+				server->sign_wanted = newstate != 0;
+			}
+			return 0;
+		}
+
+#endif /* CONFIG_NCPFS_PACKET_SIGNING */
+
+#ifdef CONFIG_NCPFS_IOCTL_LOCKING
+	case NCP_IOC_LOCKUNLOCK:
+		if (   (permission(inode, MAY_WRITE) != 0)
+		    && (current->uid != server->m.mounted_uid))
+		{
+			return -EACCES;
+		}
+		{
+			struct ncp_lock_ioctl	 rqdata;
+			int result;
+
+			if (copy_from_user(&rqdata, (struct ncp_lock_ioctl*)arg,
+				sizeof(rqdata))) return -EFAULT;
+			if (rqdata.origin != 0)
+				return -EINVAL;
+			/* check for cmd */
+			switch (rqdata.cmd) {
+				case NCP_LOCK_EX:
+				case NCP_LOCK_SH:
+						if (rqdata.timeout == 0)
+							rqdata.timeout = NCP_LOCK_DEFAULT_TIMEOUT;
+						else if (rqdata.timeout > NCP_LOCK_MAX_TIMEOUT)
+							rqdata.timeout = NCP_LOCK_MAX_TIMEOUT;
+						break;
+				case NCP_LOCK_LOG:
+						rqdata.timeout = NCP_LOCK_DEFAULT_TIMEOUT;	/* has no effect */
+				case NCP_LOCK_CLEAR:
+						break;
+				default:
+						return -EINVAL;
+			}
+			if ((result = ncp_make_open(inode, O_RDWR)) != 0)
+			{
+				return result;
+			}
+			if (!ncp_conn_valid(server))
+			{
+				return -EIO;
+			}
+			if (!S_ISREG(inode->i_mode))
+			{
+				return -EISDIR;
+			}
+			if (!NCP_FINFO(inode)->opened)
+			{
+				return -EBADFD;
+			}
+			if (rqdata.cmd == NCP_LOCK_CLEAR)
+			{
+				result = ncp_ClearPhysicalRecord(NCP_SERVER(inode),
+							NCP_FINFO(inode)->file_handle, 
+							rqdata.offset,
+							rqdata.length);
+				if (result > 0) result = 0;	/* no such lock */
+			}
+			else
+			{
+				int lockcmd;
+
+				switch (rqdata.cmd)
+				{
+					case NCP_LOCK_EX:  lockcmd=1; break;
+					case NCP_LOCK_SH:  lockcmd=3; break;
+					default:	   lockcmd=0; break;
+				}
+				result = ncp_LogPhysicalRecord(NCP_SERVER(inode),
+							NCP_FINFO(inode)->file_handle,
+							lockcmd,
+							rqdata.offset,
+							rqdata.length,
+							rqdata.timeout);
+				if (result > 0) result = -EAGAIN;
+			}
+			return result;
+		}
+#endif	/* CONFIG_NCPFS_IOCTL_LOCKING */
+
+#ifdef CONFIG_NCPFS_NDS_DOMAINS
+	case NCP_IOC_GETOBJECTNAME:
+		if (   (permission(inode, MAY_READ) != 0)
+		    && (current->uid != server->m.mounted_uid)) {
+			return -EACCES;
+		}
+		{
+			struct ncp_objectname_ioctl user;
+			int outl;
+
+			if ((result = verify_area(VERIFY_WRITE,
+					   (struct ncp_objectname_ioctl*)arg,
+					   sizeof(user))) != 0) {
+				return result;
+			}
+			if (copy_from_user(&user, 
+					   (struct ncp_objectname_ioctl*)arg,
+					   sizeof(user))) return -EFAULT;
+			user.auth_type = server->auth.auth_type;
+			outl = user.object_name_len;
+			user.object_name_len = server->auth.object_name_len;
+			if (outl > user.object_name_len)
+				outl = user.object_name_len;
+			if (outl) {
+				if (copy_to_user(user.object_name,
+						 server->auth.object_name,
+						 outl)) return -EFAULT;
+			}
+			if (copy_to_user((struct ncp_objectname_ioctl*)arg,
+					 &user,
+					 sizeof(user))) return -EFAULT;
+			return 0;
+		}
+	case NCP_IOC_SETOBJECTNAME:
+		if (   (permission(inode, MAY_WRITE) != 0)
+		    && (current->uid != server->m.mounted_uid)) {
+			return -EACCES;
+		}
+		{
+			struct ncp_objectname_ioctl user;
+			void* newname;
+			void* oldname;
+			size_t oldnamelen;
+			void* oldprivate;
+			size_t oldprivatelen;
+
+			if (copy_from_user(&user, 
+					   (struct ncp_objectname_ioctl*)arg,
+					   sizeof(user))) return -EFAULT;
+			if (user.object_name_len > NCP_OBJECT_NAME_MAX_LEN)
+				return -ENOMEM;
+			if (user.object_name_len) {
+				newname = ncp_kmalloc(user.object_name_len, GFP_USER);
+				if (!newname) return -ENOMEM;
+				if (copy_from_user(newname, user.object_name, sizeof(user))) {
+					ncp_kfree_s(newname, user.object_name_len);
+					return -EFAULT;
+				}
+			} else {
+				newname = NULL;
+			}
+			/* enter critical section */
+			/* maybe that kfree can sleep so do that this way */
+			/* it is at least more SMP friendly (in future...) */
+			oldname = server->auth.object_name;
+			oldnamelen = server->auth.object_name_len;
+			oldprivate = server->priv.data;
+			oldprivatelen = server->priv.len;
+			server->auth.auth_type = user.auth_type;
+			server->auth.object_name_len = user.object_name_len;
+			server->auth.object_name = user.object_name;
+			server->priv.len = 0;
+			server->priv.data = NULL;
+			/* leave critical section */
+			if (oldprivate) ncp_kfree_s(oldprivate, oldprivatelen);
+			if (oldname) ncp_kfree_s(oldname, oldnamelen);
+			return 0;
+		}
+	case NCP_IOC_GETPRIVATEDATA:
+		if (   (permission(inode, MAY_READ) != 0)
+		    && (current->uid != server->m.mounted_uid)) {
+			return -EACCES;
+		}
+		{
+			struct ncp_privatedata_ioctl user;
+			int outl;
+
+			if ((result = verify_area(VERIFY_WRITE,
+					   (struct ncp_privatedata_ioctl*)arg,
+					   sizeof(user))) != 0) {
+				return result;
+			}
+			if (copy_from_user(&user, 
+					   (struct ncp_privatedata_ioctl*)arg,
+					   sizeof(user))) return -EFAULT;
+			outl = user.len;
+			user.len = server->priv.len;
+			if (outl > user.len) outl = user.len;
+			if (outl) {
+				if (copy_to_user(user.data,
+						 server->priv.data,
+						 outl)) return -EFAULT;
+			}
+			if (copy_to_user((struct ncp_privatedata_ioctl*)arg,
+					 &user,
+					 sizeof(user))) return -EFAULT;
+			return 0;
+		}
+	case NCP_IOC_SETPRIVATEDATA:
+		if (   (permission(inode, MAY_WRITE) != 0)
+		    && (current->uid != server->m.mounted_uid)) {
+			return -EACCES;
+		}
+		{
+			struct ncp_privatedata_ioctl user;
+			void* new;
+			void* old;
+			size_t oldlen;
+
+			if (copy_from_user(&user, 
+					   (struct ncp_privatedata_ioctl*)arg,
+					   sizeof(user))) return -EFAULT;
+			if (user.len > NCP_PRIVATE_DATA_MAX_LEN)
+				return -ENOMEM;
+			if (user.len) {
+				new = ncp_kmalloc(user.len, GFP_USER);
+				if (!new) return -ENOMEM;
+				if (copy_from_user(new, user.data, user.len)) {
+					ncp_kfree_s(new, user.len);
+					return -EFAULT;
+				}
+			} else {
+				new = NULL;
+			}
+			/* enter critical section */
+			old = server->priv.data;
+			oldlen = server->priv.len;
+			server->priv.len = user.len;
+			server->priv.data = new;
+			/* leave critical section */
+			if (old) ncp_kfree_s(old, oldlen);
+			return 0;
+		}
+#endif	/* CONFIG_NCPFS_NDS_DOMAINS */
 	default:
 		return -EINVAL;
 	}
-
-	return -EINVAL;
 }
diff -u --recursive --new-file v2.1.88/linux/fs/ncpfs/mmap.c linux/fs/ncpfs/mmap.c
--- v2.1.88/linux/fs/ncpfs/mmap.c	Mon Dec  1 10:34:11 1997
+++ linux/fs/ncpfs/mmap.c	Fri Feb 27 10:58:34 1998
@@ -33,7 +33,8 @@
 static unsigned long ncp_file_mmap_nopage(struct vm_area_struct *area,
 				     unsigned long address, int no_share)
 {
-	struct dentry *dentry = area->vm_dentry;
+	struct file *file = area->vm_file;
+	struct dentry *dentry = file->f_dentry;
 	struct inode *inode = dentry->d_inode;
 	unsigned long page;
 	unsigned int clear;
@@ -136,7 +137,8 @@
 		inode->i_atime = CURRENT_TIME;
 	}
 
-	vma->vm_dentry = dget(file->f_dentry);
+	vma->vm_file = file;
+	file->f_count++;
 	vma->vm_ops = &ncp_file_mmap;
 	return 0;
 }
diff -u --recursive --new-file v2.1.88/linux/fs/ncpfs/ncplib_kernel.c linux/fs/ncpfs/ncplib_kernel.c
--- v2.1.88/linux/fs/ncpfs/ncplib_kernel.c	Wed Nov 12 11:10:40 1997
+++ linux/fs/ncpfs/ncplib_kernel.c	Fri Mar  6 10:03:04 1998
@@ -8,6 +8,8 @@
  */
 
 
+#include <linux/config.h>
+
 #include "ncplib_kernel.h"
 
 static inline int min(int a, int b)
@@ -135,6 +137,33 @@
 	return 0;
 }
 
+
+/* options: 
+ *	bit 0	ipx checksum
+ *	bit 1	packet signing
+ */
+int
+ncp_negotiate_size_and_options(struct ncp_server *server, 
+	int size, int options, int *ret_size, int *ret_options) {
+	int result;
+
+	ncp_init_request(server);
+	ncp_add_word(server, htons(size));
+	ncp_add_byte(server, options);
+	
+	if ((result = ncp_request(server, 0x61)) != 0)
+	{
+		ncp_unlock_server(server);
+		return result;
+	}
+
+	*ret_size = min(ntohs(ncp_reply_word(server, 0)), size);
+	*ret_options = ncp_reply_byte(server, 4);
+
+	ncp_unlock_server(server);
+	return 0;
+}
+
 int
 ncp_get_volume_info_with_number(struct ncp_server *server, int n,
 				    struct ncp_volume_info *target)
@@ -252,7 +281,7 @@
 	ncp_add_byte(server, 6);	/* subfunction */
 	ncp_add_byte(server, server->name_space[volnum]);
 	ncp_add_byte(server, server->name_space[volnum]); /* N.B. twice ?? */
-	ncp_add_word(server, htons(0xff00));	/* get all */
+	ncp_add_word(server, htons(0x0680));	/* get all */
 	ncp_add_dword(server, RIM_ALL);
 	ncp_add_handle_path(server, volnum, dirent, 1, path);
 
@@ -265,9 +294,34 @@
 	return result;
 }
 
+static int
+ncp_obtain_DOS_dir_base(struct ncp_server *server,
+		__u8 volnum, __u32 dirent,
+		char *path, /* At most 1 component */
+		__u32 *DOS_dir_base)
+{
+	int result;
+
+	ncp_init_request(server);
+	ncp_add_byte(server, 6); /* subfunction */
+	ncp_add_byte(server, server->name_space[volnum]);
+	ncp_add_byte(server, server->name_space[volnum]);
+	ncp_add_word(server, htons(0x0680)); /* get all */
+	ncp_add_dword(server, RIM_DIRECTORY);
+	ncp_add_handle_path(server, volnum, dirent, 1, path);
+
+	if ((result = ncp_request(server, 87)) == 0)
+	{
+	   	if (DOS_dir_base) *DOS_dir_base=ncp_reply_dword(server, 0x34);
+	}
+	ncp_unlock_server(server);
+	return result;
+}
+
 static inline int
-ncp_has_os2_namespace(struct ncp_server *server, __u8 volume)
+ncp_get_known_namespace(struct ncp_server *server, __u8 volume)
 {
+#if defined(CONFIG_NCPFS_OS2_NS) || defined(CONFIG_NCPFS_NFS_NS)
 	int result;
 	__u8 *namespace;
 	__u16 no_namespaces;
@@ -279,26 +333,92 @@
 
 	if ((result = ncp_request(server, 87)) != 0) {
 		ncp_unlock_server(server);
-		return 0; /* not result ?? */
+		return NW_NS_DOS; /* not result ?? */
 	}
+
+	result = NW_NS_DOS;
 	no_namespaces = ncp_reply_word(server, 0);
 	namespace = ncp_reply_data(server, 2);
 
-	result = 1;
 	while (no_namespaces > 0) {
 		DPRINTK(KERN_DEBUG "get_namespaces: found %d on %d\n", *namespace, volume);
 
-		if (*namespace == 4) {
-			DPRINTK(KERN_DEBUG "get_namespaces: found OS2\n");
-			goto out;
+#ifdef CONFIG_NCPFS_NFS_NS
+		if ((*namespace == NW_NS_NFS) && !(server->m.flags&NCP_MOUNT_NO_NFS)) 
+		{
+			result = NW_NS_NFS;
+			break;
+		}
+#endif	/* CONFIG_NCPFS_NFS_NS */
+#ifdef CONFIG_NCPFS_OS2_NS
+		if ((*namespace == NW_NS_OS2) && !(server->m.flags&NCP_MOUNT_NO_OS2))
+		{
+			result = NW_NS_OS2;
 		}
+#endif	/* CONFIG_NCPFS_OS2_NS */
 		namespace += 1;
 		no_namespaces -= 1;
 	}
-	result = 0;
-out:
 	ncp_unlock_server(server);
 	return result;
+#else	/* neither OS2 nor NFS - only DOS */
+	return NW_NS_DOS;
+#endif	/* defined(CONFIG_NCPFS_OS2_NS) || defined(CONFIG_NCPFS_NFS_NS) */
+}
+
+static int
+ncp_ObtainSpecificDirBase(struct ncp_server *server,
+		__u8 nsSrc, __u8 nsDst, __u8 vol_num, __u32 dir_base,
+		char *path, /* At most 1 component */
+		__u32 *dirEntNum, __u32 *DosDirNum)
+{
+	int result;
+
+	ncp_init_request(server);
+	ncp_add_byte(server, 6); /* subfunction */
+	ncp_add_byte(server, nsSrc);
+	ncp_add_byte(server, nsDst);
+	ncp_add_word(server, 0x8006); /* get all */
+	ncp_add_dword(server, RIM_ALL);
+	ncp_add_handle_path(server, vol_num, dir_base, 1, path);
+
+	if ((result = ncp_request(server, 87)) != 0)
+	{
+		ncp_unlock_server(server);
+		return result;
+	}
+
+	if (dirEntNum)
+		*dirEntNum = ncp_reply_dword(server, 0x30);
+	if (DosDirNum)
+		*DosDirNum = ncp_reply_dword(server, 0x34);
+	ncp_unlock_server(server);
+	return 0;
+}
+
+int
+ncp_mount_subdir(struct ncp_server *server,
+		__u8 volNumber,
+		__u8 srcNS, __u32 dirEntNum)
+{
+	int dstNS;
+	int result;
+	__u32 newDirEnt;
+	__u32 newDosEnt;
+	
+	dstNS = ncp_get_known_namespace(server, volNumber);
+	if ((result = ncp_ObtainSpecificDirBase(server, srcNS, dstNS, volNumber, 
+				      dirEntNum, NULL, &newDirEnt, &newDosEnt)) != 0)
+	{
+		return result;
+	}
+	server->name_space[volNumber] = dstNS;
+	server->root.finfo.i.volNumber = volNumber;
+	server->root.finfo.i.dirEntNum = newDirEnt;
+	server->root.finfo.i.DosDirNum = newDosEnt;
+	server->m.mounted_vol[1] = 0;
+	server->m.mounted_vol[0] = 'X';
+	return 0;
 }
 
 int 
@@ -332,7 +452,7 @@
 	target->volNumber = volnum = ncp_reply_byte(server, 8);
 	ncp_unlock_server(server);
 
-	server->name_space[volnum] = ncp_has_os2_namespace(server, volnum) ? 4 : 0;
+	server->name_space[volnum] = ncp_get_known_namespace(server, volnum);
 
 	DPRINTK(KERN_DEBUG "lookup_vol: namespace[%d] = %d\n",
 		volnum, server->name_space[volnum]);
@@ -366,25 +486,65 @@
 	return result;
 }
 
-int ncp_del_file_or_subdir(struct ncp_server *server,
-			   struct inode *dir, char *name)
+static int
+ncp_DeleteNSEntry(struct ncp_server *server,
+		  __u8 have_dir_base, __u8 volnum, __u32 dirent,
+		  char* name, __u8 ns, int attr)
 {
-	__u8  volnum = NCP_FINFO(dir)->volNumber;
-	__u32 dirent = NCP_FINFO(dir)->dirEntNum;
 	int result;
 
 	ncp_init_request(server);
 	ncp_add_byte(server, 8);	/* subfunction */
-	ncp_add_byte(server, server->name_space[volnum]);
+	ncp_add_byte(server, ns);
 	ncp_add_byte(server, 0);	/* reserved */
-	ncp_add_word(server, ntohs(0x0680));	/* search attribs: all */
-	ncp_add_handle_path(server, volnum, dirent, 1, name);
+	ncp_add_word(server, attr);	/* search attribs: all */
+	ncp_add_handle_path(server, volnum, dirent, have_dir_base, name);
 
 	result = ncp_request(server, 87);
 	ncp_unlock_server(server);
 	return result;
 }
 
+int
+ncp_del_file_or_subdir2(struct ncp_server *server,
+			struct dentry *dentry)
+{
+	struct inode *inode = dentry->d_inode;
+	__u8  volnum;
+	__u32 dirent;
+
+	if (!inode) {
+#if CONFIG_NCPFS_DEBUGDENTRY
+		printk(KERN_DEBUG "ncpfs: ncpdel2: dentry->d_inode == NULL\n");
+#endif
+		return 0xFF;	/* Any error */
+	}
+	volnum = NCP_FINFO(inode)->volNumber;
+	dirent = NCP_FINFO(inode)->DosDirNum;
+	return ncp_DeleteNSEntry(server, 1, volnum, dirent, NULL, NW_NS_DOS, htons(0x0680));
+}
+
+int
+ncp_del_file_or_subdir(struct ncp_server *server,
+		       struct inode *dir, char *name)
+{
+	__u8  volnum = NCP_FINFO(dir)->volNumber;
+	__u32 dirent = NCP_FINFO(dir)->dirEntNum;
+
+#ifdef CONFIG_NCPFS_NFS_NS
+	if (server->name_space[volnum]==NW_NS_NFS)
+ 	{
+ 		int result;
+ 
+ 		result=ncp_obtain_DOS_dir_base(server, volnum, dirent, name, &dirent);
+ 		if (result) return result;
+ 		return ncp_DeleteNSEntry(server, 1, volnum, dirent, NULL, NW_NS_DOS, htons(0x0680));
+ 	}
+ 	else
+#endif	/* CONFIG_NCPFS_NFS_NS */
+ 		return ncp_DeleteNSEntry(server, 1, volnum, dirent, name, server->name_space[volnum], htons(0x0680));
+}
+
 static inline void ConvertToNWfromDWORD(__u32 sfd, __u8 ret[6])
 {
 	__u16 *dest = (__u16 *) ret;
@@ -480,13 +640,20 @@
 	ncp_add_byte(server, 3);	/* subfunction */
 	ncp_add_byte(server, server->name_space[seq->volNumber]);
 	ncp_add_byte(server, 0);	/* data stream (???) */
-	ncp_add_word(server, 0xffff);	/* Search attribs */
+	ncp_add_word(server, htons(0x0680));	/* Search attribs */
 	ncp_add_dword(server, RIM_ALL);		/* return info mask */
 	ncp_add_mem(server, seq, 9);
-	ncp_add_byte(server, 2);	/* 2 byte pattern */
-	ncp_add_byte(server, 0xff);	/* following is a wildcard */
-	ncp_add_byte(server, '*');
-
+#ifdef CONFIG_NCPFS_NFS_NS
+	if (server->name_space[seq->volNumber] == NW_NS_NFS) {
+		ncp_add_byte(server, 0);	/* 0 byte pattern */
+	} else 
+#endif
+	{
+		ncp_add_byte(server, 2);	/* 2 byte pattern */
+		ncp_add_byte(server, 0xff);	/* following is a wildcard */
+		ncp_add_byte(server, '*');
+	}
+	
 	if ((result = ncp_request(server, 87)) != 0)
 		goto out;
 	memcpy(seq, ncp_reply_data(server, 0), sizeof(*seq));
@@ -497,9 +664,10 @@
 	return result;
 }
 
-int ncp_ren_or_mov_file_or_subdir(struct ncp_server *server,
-				struct inode *old_dir, char *old_name,
-				struct inode *new_dir, char *new_name)
+int
+ncp_RenameNSEntry(struct ncp_server *server,
+		  struct inode *old_dir, char *old_name, int old_type,
+		  struct inode *new_dir, char *new_name)
 {
 	int result = -EINVAL;
 
@@ -511,7 +679,7 @@
 	ncp_add_byte(server, 4);	/* subfunction */
 	ncp_add_byte(server, server->name_space[NCP_FINFO(old_dir)->volNumber]);
 	ncp_add_byte(server, 1);	/* rename flag */
-	ncp_add_word(server, ntohs(0x0680));	/* search attributes */
+	ncp_add_word(server, old_type);	/* search attributes */
 
 	/* source Handle Path */
 	ncp_add_byte(server, NCP_FINFO(old_dir)->volNumber);
@@ -536,6 +704,30 @@
 	return result;
 }
 
+int ncp_ren_or_mov_file_or_subdir(struct ncp_server *server,
+				struct inode *old_dir, char *old_name,
+				struct inode *new_dir, char *new_name)
+{
+        int result;
+        int old_type = htons(0x0600);
+
+/* If somebody can do it atomic, call me... vandrove@vc.cvut.cz */
+	result = ncp_RenameNSEntry(server, old_dir, old_name, old_type,
+	                                   new_dir, new_name);
+        if (result == 0xFF)	/* File Not Found, try directory */
+	{
+		old_type = htons(0x1600);
+		result = ncp_RenameNSEntry(server, old_dir, old_name, old_type,
+						   new_dir, new_name);
+	}
+	if (result != 0x92) return result;	/* All except NO_FILES_RENAMED */
+	result = ncp_del_file_or_subdir(server, new_dir, new_name);
+	if (result != 0) return -EACCES;
+	result = ncp_RenameNSEntry(server, old_dir, old_name, old_type,
+					   new_dir, new_name);
+	return result;
+}
+	
 
 /* We have to transfer to/from user space */
 int
@@ -587,3 +779,49 @@
 	ncp_unlock_server(server);
 	return result;
 }
+
+#ifdef CONFIG_NCPFS_IOCTL_LOCKING
+int
+ncp_LogPhysicalRecord(struct ncp_server *server, const char *file_id,
+	  __u8 locktype, __u32 offset, __u32 length, __u16 timeout)
+{
+	int result;
+
+	ncp_init_request(server);
+	ncp_add_byte(server, locktype);
+	ncp_add_mem(server, file_id, 6);
+	ncp_add_dword(server, htonl(offset));
+	ncp_add_dword(server, htonl(length));
+	ncp_add_word(server, htons(timeout));
+
+	if ((result = ncp_request(server, 0x1A)) != 0)
+	{
+		ncp_unlock_server(server);
+		return result;
+	}
+	ncp_unlock_server(server);
+	return 0;
+}
+
+int
+ncp_ClearPhysicalRecord(struct ncp_server *server, const char *file_id,
+	  __u32 offset, __u32 length)
+{
+	int result;
+
+	ncp_init_request(server);
+	ncp_add_byte(server, 0);	/* who knows... lanalyzer says that */
+	ncp_add_mem(server, file_id, 6);
+	ncp_add_dword(server, htonl(offset));
+	ncp_add_dword(server, htonl(length));
+
+	if ((result = ncp_request(server, 0x1E)) != 0)
+	{
+		ncp_unlock_server(server);
+		return result;
+	}
+	ncp_unlock_server(server);
+	return 0;
+}
+#endif	/* CONFIG_NCPFS_IOCTL_LOCKING */
+
diff -u --recursive --new-file v2.1.88/linux/fs/ncpfs/ncplib_kernel.h linux/fs/ncpfs/ncplib_kernel.h
--- v2.1.88/linux/fs/ncpfs/ncplib_kernel.h	Wed Nov 12 11:10:40 1997
+++ linux/fs/ncpfs/ncplib_kernel.h	Fri Mar  6 10:03:04 1998
@@ -10,6 +10,8 @@
 #ifndef _NCPLIB_H
 #define _NCPLIB_H
 
+#include <linux/config.h>
+
 #include <linux/fs.h>
 #include <linux/types.h>
 #include <linux/errno.h>
@@ -26,6 +28,8 @@
 #include <linux/ncp_fs_sb.h>
 
 int ncp_negotiate_buffersize(struct ncp_server *, int, int *);
+int ncp_negotiate_size_and_options(struct ncp_server *server, int size,
+  			  int options, int *ret_size, int *ret_options);
 int ncp_get_volume_info_with_number(struct ncp_server *, int,
 				struct ncp_volume_info *);
 int ncp_close_file(struct ncp_server *, const char *);
@@ -39,6 +43,7 @@
 int ncp_modify_file_or_subdir_dos_info(struct ncp_server *, struct inode *,
 			 __u32, struct nw_modify_dos_info *info);
 
+int ncp_del_file_or_subdir2(struct ncp_server *, struct dentry*);
 int ncp_del_file_or_subdir(struct ncp_server *, struct inode *, char *);
 int ncp_open_create_file_or_subdir(struct ncp_server *, struct inode *, char *,
 			       int, __u32, int, struct nw_file_info *);
@@ -53,4 +58,21 @@
 			      struct inode *, char *, struct inode *, char *);
 
 
+int
+ncp_LogPhysicalRecord(struct ncp_server *server,
+		      const char *file_id, __u8 locktype,
+		      __u32 offset, __u32 length, __u16 timeout);
+
+#ifdef CONFIG_NCPFS_IOCTL_LOCKING
+int
+ncp_ClearPhysicalRecord(struct ncp_server *server,
+			const char *file_id,
+			__u32 offset, __u32 length);
+#endif	/* CONFIG_NCPFS_IOCTL_LOCKING */
+
+#ifdef CONFIG_NCPFS_MOUNT_SUBDIR
+int
+ncp_mount_subdir(struct ncp_server* server, __u8 volNumber, 
+		 __u8 srcNS, __u32 srcDirEntNum);
+#endif	/* CONFIG_NCPFS_MOUNT_SUBDIR */
 #endif /* _NCPLIB_H */
diff -u --recursive --new-file v2.1.88/linux/fs/ncpfs/sock.c linux/fs/ncpfs/sock.c
--- v2.1.88/linux/fs/ncpfs/sock.c	Tue Dec 23 13:52:02 1997
+++ linux/fs/ncpfs/sock.c	Fri Mar  6 17:37:42 1998
@@ -8,6 +8,8 @@
  *
  */
 
+#include <linux/config.h>
+
 #include <linux/sched.h>
 #include <linux/errno.h>
 #include <linux/socket.h>
@@ -23,11 +25,16 @@
 #include <net/sock.h>
 #include <linux/ipx.h>
 #include <linux/poll.h>
+#include <linux/file.h>
 
 #include <linux/ncp.h>
 #include <linux/ncp_fs.h>
 #include <linux/ncp_fs_sb.h>
 
+#ifdef CONFIG_NCPFS_PACKET_SIGNING
+#include "ncpsign_kernel.h"
+#endif
+
 static int _recv(struct socket *sock, unsigned char *ubuf, int size,
 		 unsigned flags)
 {
@@ -178,6 +185,7 @@
 			current->timeout = jiffies + timeout;
 			schedule();
 			remove_wait_queue(entry.wait_address, &entry.wait);
+			fput(file);
 			current->state = TASK_RUNNING;
 			if (signal_pending(current)) {
 				current->timeout = 0;
@@ -202,8 +210,10 @@
 				continue;
 			} else
 				current->timeout = 0;
-		} else if (wait_table.nr)
+		} else if (wait_table.nr) {
 			remove_wait_queue(entry.wait_address, &entry.wait);
+			fput(file);
+		}
 		current->state = TASK_RUNNING;
 
 		/* Get the header from the next packet using a peek, so keep it
@@ -302,6 +312,12 @@
 	if (!ncp_conn_valid(server)) {
 		return -EIO;
 	}
+#ifdef CONFIG_NCPFS_PACKET_SIGNING
+	if (server->sign_active)
+	{
+		sign_packet(server, &size);
+	}
+#endif /* CONFIG_NCPFS_PACKET_SIGNING */
 	result = do_ncp_rpc_call(server, size);
 
 	DDPRINTK(KERN_DEBUG "do_ncp_rpc_call returned %d\n", result);
diff -u --recursive --new-file v2.1.88/linux/fs/nfs/read.c linux/fs/nfs/read.c
--- v2.1.88/linux/fs/nfs/read.c	Mon Jan 19 21:13:59 1998
+++ linux/fs/nfs/read.c	Fri Feb 27 17:54:28 1998
@@ -221,8 +221,9 @@
  *  -	The server is congested.
  */
 int
-nfs_readpage(struct dentry *dentry, struct page *page)
+nfs_readpage(struct file *file, struct page *page)
 {
+	struct dentry *dentry = file->f_dentry;
 	struct inode *inode = dentry->d_inode;
 	int		error = -1;
 
diff -u --recursive --new-file v2.1.88/linux/fs/nfs/write.c linux/fs/nfs/write.c
--- v2.1.88/linux/fs/nfs/write.c	Fri Jan 30 11:28:08 1998
+++ linux/fs/nfs/write.c	Fri Feb 27 17:55:28 1998
@@ -493,8 +493,9 @@
  * (for now), and we currently do this synchronously only.
  */
 int
-nfs_writepage(struct dentry *dentry, struct page *page)
+nfs_writepage(struct file * file, struct page *page)
 {
+	struct dentry *dentry = file->f_dentry;
 	return nfs_writepage_sync(dentry, dentry->d_inode, page, 0, PAGE_SIZE);
 }
 
@@ -505,9 +506,10 @@
  * things with a page scheduled for an RPC call (e.g. invalidate it).
  */
 int
-nfs_updatepage(struct dentry *dentry, struct page *page, const char *buffer,
+nfs_updatepage(struct file *file, struct page *page, const char *buffer,
 			unsigned long offset, unsigned int count, int sync)
 {
+	struct dentry	*dentry = file->f_dentry;
 	struct inode	*inode = dentry->d_inode;
 	u8		*page_addr = (u8 *) page_address(page);
 	struct nfs_wreq	*req;
diff -u --recursive --new-file v2.1.88/linux/fs/ntfs/fs.c linux/fs/ntfs/fs.c
--- v2.1.88/linux/fs/ntfs/fs.c	Fri Jan 23 18:10:32 1998
+++ linux/fs/ntfs/fs.c	Tue Feb 24 23:24:00 1998
@@ -668,7 +668,11 @@
 
 static void ntfs_put_inode(struct inode *ino)
 {
-	ntfs_debug(DEBUG_OTHER, "ntfs_put_inode %lx\n",ino->i_ino);
+}
+
+static void _ntfs_clear_inode(struct inode *ino)
+{
+	ntfs_debug(DEBUG_OTHER, "ntfs_clear_inode %lx\n",ino->i_ino);
 #ifdef NTFS_IN_LINUX_KERNEL
 	if(ino->i_ino!=FILE_MFT)
 		ntfs_clear_inode(&ino->u.ntfs_i);
@@ -680,7 +684,7 @@
 		ino->u.generic_ip=0;
 	}
 #endif
-	clear_inode(ino);
+	return;
 }
 
 /* Called when umounting a filesystem by do_umount() in fs/super.c */
@@ -753,6 +757,7 @@
 	NULL, /* write_super */
 	ntfs_statfs,
 	ntfs_remount_fs, /* remount */
+	_ntfs_clear_inode, /* clear_inode */ 
 };
 
 /* Called to mount a filesystem by read_super() in fs/super.c
diff -u --recursive --new-file v2.1.88/linux/fs/ntfs/inode.c linux/fs/ntfs/inode.c
--- v2.1.88/linux/fs/ntfs/inode.c	Fri Jan  2 01:42:59 1998
+++ linux/fs/ntfs/inode.c	Tue Feb 24 23:24:00 1998
@@ -289,8 +289,14 @@
 void ntfs_clear_inode(ntfs_inode *ino)
 {
 	int i;
+	if(!ino->attr){
+		ntfs_error("ntfs_clear_inode: double free\n");
+		return;
+	}
 	ntfs_free(ino->attr);
+	ino->attr=0;
 	ntfs_free(ino->records);
+	ino->records=0;
 	for(i=0;i<ino->attr_count;i++)
 	{
 		if(ino->attrs[i].name)
@@ -305,6 +311,7 @@
 		}
 	}
 	ntfs_free(ino->attrs);
+	ino->attrs=0;
 }
 
 /* Check and fixup a MFT record */
diff -u --recursive --new-file v2.1.88/linux/fs/open.c linux/fs/open.c
--- v2.1.88/linux/fs/open.c	Mon Feb 23 18:12:10 1998
+++ linux/fs/open.c	Sat Feb 28 10:47:59 1998
@@ -47,24 +47,31 @@
 
 asmlinkage int sys_fstatfs(unsigned int fd, struct statfs * buf)
 {
+	struct file * file;
 	struct inode * inode;
 	struct dentry * dentry;
-	struct file * file;
+	struct super_block * sb;
 	int error;
 
 	lock_kernel();
-	if (fd >= NR_OPEN || !(file = current->files->fd[fd]))
-		error = -EBADF;
-	else if (!(dentry = file->f_dentry))
-		error = -ENOENT;
-	else if (!(inode = dentry->d_inode))
-		error = -ENOENT;
-	else if (!inode->i_sb)
-		error = -ENODEV;
-	else if (!inode->i_sb->s_op->statfs)
-		error = -ENOSYS;
-	else
-		error = inode->i_sb->s_op->statfs(inode->i_sb, buf, sizeof(struct statfs));
+	error = -EBADF;
+	file = fget(fd);
+	if (!file)
+		goto out;
+	error = -ENOENT;
+	if (!(dentry = file->f_dentry))
+		goto out_putf;
+	if (!(inode = dentry->d_inode))
+		goto out_putf;
+	error = -ENODEV;
+	if (!(sb = inode->i_sb))
+		goto out_putf;
+	error = -ENOSYS;
+	if (sb->s_op->statfs)
+		error = sb->s_op->statfs(sb, buf, sizeof(struct statfs));
+out_putf:
+	fput(file);
+out:
 	unlock_kernel();
 	return error;
 }
@@ -147,23 +154,29 @@
 	int error;
 
 	lock_kernel();
-	if (fd >= NR_OPEN || !(file = current->files->fd[fd]))
-		error = -EBADF;
-	else if (!(dentry = file->f_dentry))
-		error = -ENOENT;
-	else if (!(inode = dentry->d_inode))
-		error = -ENOENT;
-	else if (S_ISDIR(inode->i_mode) || !(file->f_mode & FMODE_WRITE))
-		error = -EACCES;
-	else if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
-		error = -EPERM;
-	else {
-		error = locks_verify_area(FLOCK_VERIFY_WRITE, inode, file,
-					  length<inode->i_size ? length : inode->i_size,
-					  abs(inode->i_size - length));
-		if (!error)
-			error = do_truncate(dentry, length);
-	}
+	error = -EBADF;
+	file = fget(fd);
+	if (!file)
+		goto out;
+	error = -ENOENT;
+	if (!(dentry = file->f_dentry))
+		goto out_putf;
+	if (!(inode = dentry->d_inode))
+		goto out_putf;
+	error = -EACCES;
+	if (S_ISDIR(inode->i_mode) || !(file->f_mode & FMODE_WRITE))
+		goto out_putf;
+	error = -EPERM;
+	if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
+		goto out_putf;
+	error = locks_verify_area(FLOCK_VERIFY_WRITE, inode, file,
+				  length<inode->i_size ? length : inode->i_size,
+				  abs(inode->i_size - length));
+	if (!error)
+		error = do_truncate(dentry, length);
+out_putf:
+	fput(file);
+out:
 	unlock_kernel();
 	return error;
 }
@@ -347,30 +360,28 @@
 	lock_kernel();
 
 	error = -EBADF;
-	if (fd >= NR_OPEN || !(file = current->files->fd[fd]))
+	file = fget(fd);
+	if (!file)
 		goto out;
 
 	error = -ENOENT;
 	if (!(dentry = file->f_dentry))
-		goto out;
+		goto out_putf;
 	if (!(inode = dentry->d_inode))
-		goto out;
+		goto out_putf;
 
 	error = -ENOTDIR;
 	if (!S_ISDIR(inode->i_mode))
-		goto out;
-
-	error = permission(inode,MAY_EXEC);
-	if (error)
-		goto out;
+		goto out_putf;
 
-	{
-		struct dentry *tmp;
-	
-		tmp = current->fs->pwd;
+	error = permission(inode, MAY_EXEC);
+	if (!error) {
+		struct dentry *tmp = current->fs->pwd;
 		current->fs->pwd = dget(dentry);
 		dput(tmp);
 	}
+out_putf:
+	fput(file);
 out:
 	unlock_kernel();
 	return error;
@@ -421,28 +432,34 @@
 	struct inode * inode;
 	struct dentry * dentry;
 	struct file * file;
-	struct iattr newattrs;
 	int err = -EBADF;
+	struct iattr newattrs;
 
 	lock_kernel();
-	if (fd >= NR_OPEN || !(file = current->files->fd[fd]))
+	file = fget(fd);
+	if (!file)
 		goto out;
+
 	err = -ENOENT;
 	if (!(dentry = file->f_dentry))
-		goto out;
+		goto out_putf;
 	if (!(inode = dentry->d_inode))
-		goto out;
+		goto out_putf;
+
 	err = -EROFS;
 	if (IS_RDONLY(inode))
-		goto out;
+		goto out_putf;
 	err = -EPERM;
 	if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
-		goto out;
+		goto out_putf;
 	if (mode == (mode_t) -1)
 		mode = inode->i_mode;
 	newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);
 	newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
 	err = notify_change(dentry, &newattrs);
+
+out_putf:
+	fput(file);
 out:
 	unlock_kernel();
 	return err;
@@ -487,8 +504,8 @@
 static int chown_common(struct dentry * dentry, uid_t user, gid_t group)
 {
 	struct inode * inode;
-	struct iattr newattrs;
 	int error;
+	struct iattr newattrs;
 
 	error = -ENOENT;
 	if (!(inode = dentry->d_inode)) {
@@ -581,13 +598,13 @@
 	int error = -EBADF;
 
 	lock_kernel();
-	if (fd >= NR_OPEN || !(file = current->files->fd[fd]))
+	file = fget(fd);
+	if (!file)
 		goto out;
 	error = -ENOENT;
-	if (!(dentry = file->f_dentry))
-		goto out;
-
-	error = chown_common(dentry, user, group);
+	if ((dentry = file->f_dentry) != NULL)
+		error = chown_common(dentry, user, group);
+	fput(file);
 
 out:
 	unlock_kernel();
@@ -608,16 +625,17 @@
  * for the internal routines (ie open_namei()/follow_link() etc). 00 is
  * used by symlinks.
  */
-static int do_open(const char * filename,int flags,int mode, int fd)
+static int do_open(const char * filename, int flags, int mode, int fd)
 {
 	struct inode * inode;
 	struct dentry * dentry;
 	struct file * f;
 	int flag,error;
 
+	error = -ENFILE;
 	f = get_empty_filp();
 	if (!f)
-		return -ENFILE;
+		goto out;
 	f->f_flags = flag = flags;
 	f->f_mode = (flag+1) & O_ACCMODE;
 	if (f->f_mode)
@@ -648,7 +666,7 @@
 	}
 	f->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC);
 
-	current->files->fd[fd] = f;
+	fd_install(fd, f);
 	return 0;
 
 cleanup_all:
@@ -658,6 +676,7 @@
 	dput(dentry);
 cleanup_file:
 	put_filp(f);
+out:
 	return error;
 }
 
@@ -670,6 +689,10 @@
 	struct files_struct * files = current->files;
 
 	fd = find_first_zero_bit(&files->open_fds, NR_OPEN);
+	/*
+	 * N.B. For clone tasks sharing a files structure, this test
+	 * will limit the total number of files that can be opened.
+	 */
 	if (fd < current->rlim[RLIMIT_NOFILE].rlim_cur) {
 		FD_SET(fd, &files->open_fds);
 		FD_CLR(fd, &files->close_on_exec);
@@ -678,36 +701,37 @@
 	return -EMFILE;
 }
 
-inline void put_unused_fd(int fd)
+inline void put_unused_fd(unsigned int fd)
 {
 	FD_CLR(fd, &current->files->open_fds);
 }
 
-asmlinkage int sys_open(const char * filename,int flags,int mode)
+asmlinkage int sys_open(const char * filename, int flags, int mode)
 {
 	char * tmp;
 	int fd, error;
 
 	lock_kernel();
-	error = get_unused_fd();
-	if (error < 0)
+	fd = get_unused_fd();
+	if (fd < 0)
 		goto out;
 
-	fd = error;
 	tmp = getname(filename);
 	error = PTR_ERR(tmp);
-	if (!IS_ERR(tmp)) {
-		error = do_open(tmp,flags,mode,fd);
-		putname(tmp);
-		if (!error) {
-			error = fd;
-			goto out;
-		}
-	}
-	put_unused_fd(fd);
+	if (IS_ERR(tmp))
+		goto out_fail;
+	error = do_open(tmp, flags, mode, fd);
+	putname(tmp);
+	if (error)
+		goto out_fail;
 out:
 	unlock_kernel();
-	return error;
+	return fd;
+
+out_fail:
+	put_unused_fd(fd);
+	fd = error;
+	goto out;
 }
 
 #ifndef __alpha__
@@ -728,11 +752,14 @@
 
 #endif
 
+/*
+ * Called when retiring the last use of a file pointer.
+ */
 int __fput(struct file *filp)
 {
-	int	error = 0;
 	struct dentry * dentry = filp->f_dentry;
 	struct inode * inode = dentry->d_inode;
+	int	error = 0;
 
 	if (filp->f_op && filp->f_op->release)
 		error = filp->f_op->release(inode, filp);
@@ -743,7 +770,11 @@
 	return error;
 }
 
-int close_fp(struct file *filp)
+/*
+ * "id" is the POSIX thread ID. We use the
+ * files pointer for this..
+ */
+int close_fp(struct file *filp, fl_owner_t id)
 {
 	struct dentry *dentry = filp->f_dentry;
 
@@ -752,10 +783,15 @@
 		return 0;
 	}
 	if (dentry->d_inode)
-		locks_remove_posix(current, filp);
+		locks_remove_posix(filp, id);
 	return fput(filp);
 }
 
+/*
+ * Careful here! We test whether the file pointer is NULL before
+ * releasing the fd. This ensures that one clone task can't release
+ * an fd while another clone is opening it.
+ */
 asmlinkage int sys_close(unsigned int fd)
 {
 	int error;
@@ -769,7 +805,7 @@
 		put_unused_fd(fd);
 		FD_CLR(fd, &files->close_on_exec);
 		files->fd[fd] = NULL;
-		error = close_fp(filp);
+		error = close_fp(filp, files);
 	}
 	unlock_kernel();
 	return error;
diff -u --recursive --new-file v2.1.88/linux/fs/pipe.c linux/fs/pipe.c
--- v2.1.88/linux/fs/pipe.c	Mon Dec  8 23:58:04 1997
+++ linux/fs/pipe.c	Thu Feb 26 19:53:06 1998
@@ -174,7 +174,7 @@
 	unsigned int mask;
 	struct inode * inode = filp->f_dentry->d_inode;
 
-	poll_wait(&PIPE_WAIT(*inode), wait);
+	poll_wait(filp, &PIPE_WAIT(*inode), wait);
 	mask = POLLIN | POLLRDNORM;
 	if (PIPE_EMPTY(*inode))
 		mask = POLLOUT | POLLWRNORM;
@@ -195,7 +195,7 @@
 	unsigned int mask;
 	struct inode * inode = filp->f_dentry->d_inode;
 
-	poll_wait(&PIPE_WAIT(*inode), wait);
+	poll_wait(filp, &PIPE_WAIT(*inode), wait);
 	mask = POLLIN | POLLRDNORM;
 	if (PIPE_EMPTY(*inode))
 		mask = POLLOUT | POLLWRNORM;
@@ -228,7 +228,7 @@
 {
 	struct inode * inode = filp->f_dentry->d_inode;
 
-	poll_wait(&PIPE_WAIT(*inode), wait);
+	poll_wait(filp, &PIPE_WAIT(*inode), wait);
 	if (!PIPE_EMPTY(*inode)) {
 		filp->f_op = &read_fifo_fops;
 		return POLLIN | POLLRDNORM;
diff -u --recursive --new-file v2.1.88/linux/fs/proc/array.c linux/fs/proc/array.c
--- v2.1.88/linux/fs/proc/array.c	Fri Jan 30 11:28:09 1998
+++ linux/fs/proc/array.c	Sat Feb 28 22:32:30 1998
@@ -610,30 +610,47 @@
 	return buf+1;
 }
 
+/*
+ * The task state array is a strange "bitmap" of
+ * reasons to sleep. Thus "running" is zero, and
+ * you can test for combinations of others with
+ * simple bit tests.
+ */
+static const char *task_state_array[] = {
+	"R (running)",		/*  0 */
+	"S (sleeping)",		/*  1 */
+	"D (disk sleep)",	/*  2 */
+	"Z (zombie)",		/*  4 */
+	"T (stopped)",		/*  8 */
+	"W (paging)"		/* 16 */
+};
+
+static inline const char * get_task_state(struct task_struct *tsk)
+{
+	unsigned int state = tsk->state & (TASK_RUNNING |
+					   TASK_INTERRUPTIBLE |
+					   TASK_UNINTERRUPTIBLE |
+					   TASK_ZOMBIE |
+					   TASK_STOPPED |
+					   TASK_SWAPPING);
+	const char **p = &task_state_array[0];
+
+	while (state) {
+		p++;
+		state >>= 1;
+	}
+	return *p;
+}
+
 static inline char * task_state(struct task_struct *p, char *buffer)
 {
-#define NR_STATES (sizeof(states)/sizeof(const char *))
-	unsigned int n = p->state;
-	static const char * states[] = {
-		"R (running)",
-		"S (sleeping)",
-		"D (disk sleep)",
-		"Z (zombie)",
-		"T (stopped)",
-		"W (paging)",
-		". Huh?"
-	};
-
-	if (n >= NR_STATES)
-		n = NR_STATES-1;
-
 	buffer += sprintf(buffer,
 		"State:\t%s\n"
 		"Pid:\t%d\n"
 		"PPid:\t%d\n"
 		"Uid:\t%d\t%d\t%d\t%d\n"
 		"Gid:\t%d\t%d\t%d\t%d\n",
-		states[n],
+		get_task_state(p),
 		p->pid, p->p_pptr->pid,
 		p->uid, p->euid, p->suid, p->fsuid,
 		p->gid, p->egid, p->sgid, p->fsgid);
@@ -651,7 +668,7 @@
 
 		for (vma = mm->mmap; vma; vma = vma->vm_next) {
 			unsigned long len = (vma->vm_end - vma->vm_start) >> 10;
-			if (!vma->vm_dentry) {
+			if (!vma->vm_file) {
 				data += len;
 				if (vma->vm_flags & VM_GROWSDOWN)
 					stack += len;
@@ -769,10 +786,7 @@
 
 	if (!tsk)
 		return 0;
-	if (tsk->state < 0 || tsk->state > 5)
-		state = '.';
-	else
-		state = "RSDZTW"[tsk->state];
+	state = *get_task_state(tsk);
 	vsize = eip = esp = 0;
 	if (tsk->mm && tsk->mm != &init_mm) {
 		struct vm_area_struct *vma = tsk->mm->mmap;
@@ -1049,10 +1063,10 @@
 
 		dev = 0;
 		ino = 0;
-		if (map->vm_dentry != NULL) {
-			dev = map->vm_dentry->d_inode->i_dev;
-			ino = map->vm_dentry->d_inode->i_ino;
-			line = d_path(map->vm_dentry, buffer, PAGE_SIZE);
+		if (map->vm_file != NULL) {
+			dev = map->vm_file->f_dentry->d_inode->i_dev;
+			ino = map->vm_file->f_dentry->d_inode->i_ino;
+			line = d_path(map->vm_file->f_dentry, buffer, PAGE_SIZE);
 			buffer[PAGE_SIZE-1] = '\n';
 			line -= maxlen;
 			if(line < buffer)
@@ -1065,7 +1079,7 @@
 			      map->vm_start, map->vm_end, str, map->vm_offset,
 			      kdevname(dev), ino);
 
-		if(map->vm_dentry) {
+		if(map->vm_file) {
 			for(i = len; i < maxlen; i++)
 				line[i] = ' ';
 			len = buffer + PAGE_SIZE - line;
diff -u --recursive --new-file v2.1.88/linux/fs/proc/kmsg.c linux/fs/proc/kmsg.c
--- v2.1.88/linux/fs/proc/kmsg.c	Tue Oct 21 08:57:29 1997
+++ linux/fs/proc/kmsg.c	Thu Feb 26 19:54:49 1998
@@ -38,7 +38,7 @@
 
 static unsigned int kmsg_poll(struct file *file, poll_table * wait)
 {
-	poll_wait(&log_wait, wait);
+	poll_wait(file, &log_wait, wait);
 	if (log_size)
 		return POLLIN | POLLRDNORM;
 	return 0;
diff -u --recursive --new-file v2.1.88/linux/fs/proc/link.c linux/fs/proc/link.c
--- v2.1.88/linux/fs/proc/link.c	Tue Jan  6 12:33:11 1998
+++ linux/fs/proc/link.c	Fri Feb 27 17:50:25 1998
@@ -105,8 +105,8 @@
 				break;
 			vma = p->mm->mmap;
 			while (vma) {
-				if (vma->vm_flags & VM_EXECUTABLE)
-					return dget(vma->vm_dentry);
+				if ((vma->vm_flags & VM_EXECUTABLE) && vma->vm_file)
+					return dget(vma->vm_file->f_dentry);
 
 				vma = vma->vm_next;
 			}
diff -u --recursive --new-file v2.1.88/linux/fs/read_write.c linux/fs/read_write.c
--- v2.1.88/linux/fs/read_write.c	Tue Oct 21 08:57:29 1997
+++ linux/fs/read_write.c	Mon Feb 23 12:55:58 1998
@@ -62,14 +62,18 @@
 
 	lock_kernel();
 	retval = -EBADF;
-	if (fd >= NR_OPEN ||
-	    !(file = current->files->fd[fd]) ||
-	    !(dentry = file->f_dentry) ||
-	    !(inode = dentry->d_inode))
+	file = fget(fd);
+	if (!file)
 		goto bad;
+	/* N.B. Shouldn't this be ENOENT?? */
+	if (!(dentry = file->f_dentry) ||
+	    !(inode = dentry->d_inode))
+		goto out_putf;
 	retval = -EINVAL;
 	if (origin <= 2)
 		retval = llseek(file, offset, origin);
+out_putf:
+	fput(file);
 bad:
 	unlock_kernel();
 	return retval;
@@ -88,24 +92,28 @@
 
 	lock_kernel();
 	retval = -EBADF;
-	if (fd >= NR_OPEN ||
-	    !(file = current->files->fd[fd]) ||
-	    !(dentry = file->f_dentry) ||
-	    !(inode = dentry->d_inode))
+	file = fget(fd);
+	if (!file)
 		goto bad;
+	/* N.B. Shouldn't this be ENOENT?? */
+	if (!(dentry = file->f_dentry) ||
+	    !(inode = dentry->d_inode))
+		goto out_putf;
 	retval = -EINVAL;
 	if (origin > 2)
-		goto bad;
+		goto out_putf;
 
 	offset = llseek(file, ((loff_t) offset_high << 32) | offset_low,
 			origin);
 
 	retval = (int)offset & INT_MAX;
 	if (offset >= 0) {
-		retval = copy_to_user(result, &offset, sizeof(offset));
-		if (retval)
-			retval = -EFAULT;
+		retval = -EFAULT;
+		if (!copy_to_user(result, &offset, sizeof(offset)))
+			retval = 0;
 	}
+out_putf:
+	fput(file);
 bad:
 	unlock_kernel();
 	return retval;
@@ -201,9 +209,10 @@
 	if (count > UIO_MAXIOV)
 		goto out_nofree;
 	if (count > UIO_FASTIOV) {
-		iov = kmalloc(count*sizeof(struct iovec), GFP_KERNEL);
 		ret = -ENOMEM;
-		if (!iov) goto out_nofree;
+		iov = kmalloc(count*sizeof(struct iovec), GFP_KERNEL);
+		if (!iov)
+			goto out_nofree;
 	}
 	ret = -EFAULT;
 	if (copy_from_user(iov, vector, count*sizeof(*vector)))
@@ -280,11 +289,10 @@
 	file = fget(fd);
 	if (!file)
 		goto bad_file;
-	if (!(file->f_mode & FMODE_READ))
-		goto out;
-	ret = do_readv_writev(VERIFY_WRITE, file, vector, count);
-out:
+	if (file->f_mode & FMODE_READ)
+		ret = do_readv_writev(VERIFY_WRITE, file, vector, count);
 	fput(file);
+
 bad_file:
 	unlock_kernel();
 	return ret;
@@ -302,15 +310,13 @@
 	file = fget(fd);
 	if (!file)
 		goto bad_file;
-	if (!(file->f_mode & FMODE_WRITE))
-		goto out;
-
-	down(&file->f_dentry->d_inode->i_sem);
-	ret = do_readv_writev(VERIFY_READ, file, vector, count);
-	up(&file->f_dentry->d_inode->i_sem);
-
-out:
+	if (file->f_mode & FMODE_WRITE) {
+		down(&file->f_dentry->d_inode->i_sem);
+		ret = do_readv_writev(VERIFY_READ, file, vector, count);
+		up(&file->f_dentry->d_inode->i_sem);
+	}
 	fput(file);
+
 bad_file:
 	unlock_kernel();
 	return ret;
diff -u --recursive --new-file v2.1.88/linux/fs/readdir.c linux/fs/readdir.c
--- v2.1.88/linux/fs/readdir.c	Sat Sep 20 12:21:34 1997
+++ linux/fs/readdir.c	Mon Feb 23 12:55:58 1998
@@ -4,12 +4,13 @@
  *  Copyright (C) 1995  Linus Torvalds
  */
 
+#include <linux/sched.h>
+#include <linux/mm.h>
 #include <linux/types.h>
 #include <linux/errno.h>
 #include <linux/stat.h>
+#include <linux/file.h>
 #include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/mm.h>
 #include <linux/smp.h>
 #include <linux/smp_lock.h>
 
@@ -65,27 +66,24 @@
 
 	lock_kernel();
 	error = -EBADF;
-	if (fd >= NR_OPEN)
-		goto out;
-
-	file = current->files->fd[fd];
+	file = fget(fd);
 	if (!file)
 		goto out;
 
 	dentry = file->f_dentry;
 	if (!dentry)
-		goto out;
+		goto out_putf;
 
 	inode = dentry->d_inode;
 	if (!inode)
-		goto out;
+		goto out_putf;
 
 	buf.count = 0;
 	buf.dirent = dirent;
 
 	error = -ENOTDIR;
 	if (!file->f_op || !file->f_op->readdir)
-		goto out;
+		goto out_putf;
 
 	/*
 	 * Get the inode's semaphore to prevent changes
@@ -95,8 +93,11 @@
 	error = file->f_op->readdir(file, &buf, fillonedir);
 	up(&inode->i_sem);
 	if (error < 0)
-		goto out;
+		goto out_putf;
 	error = buf.count;
+
+out_putf:
+	fput(file);
 out:
 	unlock_kernel();
 	return error;
@@ -155,20 +156,17 @@
 
 	lock_kernel();
 	error = -EBADF;
-	if (fd >= NR_OPEN)
-		goto out;
-
-	file = current->files->fd[fd];
+	file = fget(fd);
 	if (!file)
 		goto out;
 
 	dentry = file->f_dentry;
 	if (!dentry)
-		goto out;
+		goto out_putf;
 
 	inode = dentry->d_inode;
 	if (!inode)
-		goto out;
+		goto out_putf;
 
 	buf.current_dir = (struct linux_dirent *) dirent;
 	buf.previous = NULL;
@@ -177,7 +175,7 @@
 
 	error = -ENOTDIR;
 	if (!file->f_op || !file->f_op->readdir)
-		goto out;
+		goto out_putf;
 
 	/*
 	 * Get the inode's semaphore to prevent changes
@@ -187,13 +185,16 @@
 	error = file->f_op->readdir(file, &buf, filldir);
 	up(&inode->i_sem);
 	if (error < 0)
-		goto out;
-	lastdirent = buf.previous;
+		goto out_putf;
 	error = buf.error;
+	lastdirent = buf.previous;
 	if (lastdirent) {
 		put_user(file->f_pos, &lastdirent->d_off);
 		error = count - buf.count;
 	}
+
+out_putf:
+	fput(file);
 out:
 	unlock_kernel();
 	return error;
diff -u --recursive --new-file v2.1.88/linux/fs/romfs/inode.c linux/fs/romfs/inode.c
--- v2.1.88/linux/fs/romfs/inode.c	Mon Feb 23 18:12:10 1998
+++ linux/fs/romfs/inode.c	Sat Feb 28 13:31:07 1998
@@ -390,8 +390,9 @@
  */
 
 static int
-romfs_readpage(struct dentry * dentry, struct page * page)
+romfs_readpage(struct file * file, struct page * page)
 {
+	struct dentry *dentry = file->f_dentry;
 	struct inode *inode = dentry->d_inode;
 	unsigned long buf;
 	unsigned long offset, avail, readlen;
diff -u --recursive --new-file v2.1.88/linux/fs/select.c linux/fs/select.c
--- v2.1.88/linux/fs/select.c	Fri Dec 19 15:52:10 1997
+++ linux/fs/select.c	Thu Feb 26 20:14:52 1998
@@ -25,6 +25,7 @@
 #include <linux/smp.h>
 #include <linux/smp_lock.h>
 #include <linux/poll.h>
+#include <linux/file.h>
 
 #include <asm/uaccess.h>
 #include <asm/system.h>
@@ -59,6 +60,7 @@
 		p->nr--;
 		entry--;
 		remove_wait_queue(entry->wait_address,&entry->wait);
+		fput(entry->filp);
 	}
 }
 
diff -u --recursive --new-file v2.1.88/linux/fs/smbfs/file.c linux/fs/smbfs/file.c
--- v2.1.88/linux/fs/smbfs/file.c	Fri Jan 23 18:10:32 1998
+++ linux/fs/smbfs/file.c	Sun Mar  1 10:56:18 1998
@@ -102,8 +102,9 @@
 }
 
 int
-smb_readpage(struct dentry *dentry, struct page *page)
+smb_readpage(struct file *file, struct page *page)
 {
+	struct dentry *dentry = file->f_dentry;
 	int		error;
 
 	pr_debug("SMB: smb_readpage %08lx\n", page_address(page));
@@ -177,8 +178,9 @@
  * (for now), and we currently do this synchronously only.
  */
 static int
-smb_writepage(struct dentry *dentry, struct page *page)
+smb_writepage(struct file *file, struct page *page)
 {
+	struct dentry *dentry = file->f_dentry;
 	int 	result;
 
 #ifdef SMBFS_PARANOIA
@@ -193,9 +195,10 @@
 }
 
 static int
-smb_updatepage(struct dentry *dentry, struct page *page, const char *buffer,
+smb_updatepage(struct file *file, struct page *page, const char *buffer,
 	       unsigned long offset, unsigned int count, int sync)
 {
+	struct dentry *dentry = file->f_dentry;
 	unsigned long page_addr = page_address(page);
 	int result;
 
diff -u --recursive --new-file v2.1.88/linux/fs/smbfs/inode.c linux/fs/smbfs/inode.c
--- v2.1.88/linux/fs/smbfs/inode.c	Fri Jan 23 18:10:32 1998
+++ linux/fs/smbfs/inode.c	Sat Feb 28 13:15:17 1998
@@ -18,6 +18,7 @@
 #include <linux/locks.h>
 #include <linux/malloc.h>
 #include <linux/init.h>
+#include <linux/file.h>
 #include <linux/dcache.h>
 
 #include <linux/smb_fs.h>
@@ -326,7 +327,7 @@
 	if (server->sock_file) {
 		smb_proc_disconnect(server);
 		smb_dont_catch_keepalive(server);
-		close_fp(server->sock_file);
+		fput(server->sock_file);
 	}
 
 	if (server->conn_pid)
diff -u --recursive --new-file v2.1.88/linux/fs/smbfs/proc.c linux/fs/smbfs/proc.c
--- v2.1.88/linux/fs/smbfs/proc.c	Fri Jan 23 18:10:32 1998
+++ linux/fs/smbfs/proc.c	Mon Mar  2 12:37:44 1998
@@ -9,10 +9,11 @@
  *             by Riccardo Facchetti
  */
 
-#include <linux/fs.h>
 #include <linux/types.h>
 #include <linux/errno.h>
 #include <linux/malloc.h>
+#include <linux/fs.h>
+#include <linux/file.h>
 #include <linux/stat.h>
 #include <linux/fcntl.h>
 #include <linux/dcache.h>
@@ -621,31 +622,26 @@
 #ifdef SMBFS_DEBUG_VERBOSE
 printk("smb_newconn: fd=%d, pid=%d\n", opt->fd, current->pid);
 #endif
-	error = -EBADF;
-	if (opt->fd < 0 || opt->fd >= NR_OPEN)
-		goto out;
-	if (!(filp = current->files->fd[opt->fd]))
-		goto out;
-	if (!smb_valid_socket(filp->f_dentry->d_inode))
-		goto out;
-
-	error = -EACCES;
-	if ((current->uid != server->mnt->mounted_uid) && !suser())
-		goto out;
-
 	/*
 	 * Make sure we don't already have a pid ...
 	 */
 	error = -EINVAL;
 	if (server->conn_pid)
-	{
-		printk("SMBFS: invalid ioctl call\n");
 		goto out;
-	}
-	server->conn_pid = current->pid;
 
-	filp->f_count += 1;
+	error = -EACCES;
+	if (current->uid != server->mnt->mounted_uid && !suser())
+		goto out;
+
+	error = -EBADF;
+	filp = fget(opt->fd);
+	if (!filp)
+		goto out;
+	if (!smb_valid_socket(filp->f_dentry->d_inode))
+		goto out_putf;
+
 	server->sock_file = filp;
+	server->conn_pid = current->pid;
 	smb_catch_keepalive(server);
 	server->opt = *opt;
 	server->generation += 1;
@@ -659,6 +655,10 @@
 out:
 	wake_up_interruptible(&server->wait);
 	return error;
+
+out_putf:
+	fput(filp);
+	goto out;
 }
 
 /* smb_setup_header: We completely set up the packet. You only have to
diff -u --recursive --new-file v2.1.88/linux/fs/smbfs/sock.c linux/fs/smbfs/sock.c
--- v2.1.88/linux/fs/smbfs/sock.c	Thu Dec 11 11:25:54 1997
+++ linux/fs/smbfs/sock.c	Sat Feb 28 13:29:35 1998
@@ -10,6 +10,7 @@
 #include <linux/errno.h>
 #include <linux/socket.h>
 #include <linux/fcntl.h>
+#include <linux/file.h>
 #include <linux/in.h>
 #include <linux/net.h>
 #include <linux/mm.h>
@@ -259,7 +260,7 @@
 printk("smb_close_socket: still catching keepalives!\n");
 #endif
 		server->sock_file = NULL;
-		close_fp(file);
+		fput(file);
 	}
 }
 
diff -u --recursive --new-file v2.1.88/linux/fs/stat.c linux/fs/stat.c
--- v2.1.88/linux/fs/stat.c	Sun Jan  4 00:53:41 1998
+++ linux/fs/stat.c	Mon Feb 23 12:55:58 1998
@@ -4,13 +4,14 @@
  *  Copyright (C) 1991, 1992  Linus Torvalds
  */
 
+#include <linux/sched.h>
+#include <linux/mm.h>
 #include <linux/errno.h>
 #include <linux/string.h>
 #include <linux/stat.h>
 #include <linux/fs.h>
-#include <linux/sched.h>
+#include <linux/file.h>
 #include <linux/kernel.h>
-#include <linux/mm.h>
 #include <linux/smp.h>
 #include <linux/smp_lock.h>
 
@@ -220,12 +221,14 @@
 	int err = -EBADF;
 
 	lock_kernel();
-	if (fd < NR_OPEN && (f = current->files->fd[fd]) != NULL) {
+	f = fget(fd);
+	if (f) {
 		struct dentry * dentry = f->f_dentry;
 
 		err = do_revalidate(dentry);
 		if (!err)
 			err = cp_old_stat(dentry->d_inode, statbuf);
+		fput(f);
 	}
 	unlock_kernel();
 	return err;
@@ -239,12 +242,14 @@
 	int err = -EBADF;
 
 	lock_kernel();
-	if (fd < NR_OPEN && (f = current->files->fd[fd]) != NULL) {
+	f = fget(fd);
+	if (f) {
 		struct dentry * dentry = f->f_dentry;
 
 		err = do_revalidate(dentry);
 		if (!err)
 			err = cp_new_stat(dentry->d_inode, statbuf);
+		fput(f);
 	}
 	unlock_kernel();
 	return err;
diff -u --recursive --new-file v2.1.88/linux/include/asm-alpha/hwrpb.h linux/include/asm-alpha/hwrpb.h
--- v2.1.88/linux/include/asm-alpha/hwrpb.h	Mon Jan 12 14:51:14 1998
+++ linux/include/asm-alpha/hwrpb.h	Mon Feb 23 10:25:10 1998
@@ -15,6 +15,8 @@
 #define EV45_CPU                6       /* EV4.5 (21064/xxx)    */
 #define EV56_CPU		7	/* EV5.6 (21164)	*/
 #define EV6_CPU			8	/* EV6 (21164)		*/
+#define PCA56_CPU		9	/* PCA56 (21164PC)	*/
+#define PCA57_CPU		10	/* PCA57 (??)		*/
 
 /*
  * DEC system types for Alpha systems.  Found in HWRPB.
@@ -39,8 +41,26 @@
 #define ST_DEC_EB64P		 20	/* EB64+ systype	*/
 #define ST_DEC_EB66P		-19	/* EB66 systype		*/
 #define ST_DEC_EBPC64		-20	/* Cabriolet (AlphaPC64) systype */
+#define ST_DEC_BURNS		 21	/* Laptop systype	*/
+#define ST_DEC_RAWHIDE		 22	/* Rawhide systype	*/
+#define ST_DEC_K2		 23	/* K2 systype		*/
+#define ST_DEC_LYNX		 24	/* Lynx systype		*/
+#define ST_DEC_XL		 25	/* Alpha XL systype	*/
 #define ST_DEC_EB164		 26	/* EB164 systype	*/
+#define ST_DEC_NORITAKE		 27	/* Noritake systype	*/
+#define ST_DEC_CORTEX		 28	/* Cortex systype	*/
 #define ST_DEC_MIATA		 30	/* MIATA systype        */
+#define ST_DEC_XXM		 31	/* XXM systype		*/
+#define ST_DEC_TAKARA		 32	/* Takara systype	*/
+#define ST_DEC_YUKON		 33	/* Yukon systype	*/
+#define ST_DEC_TSUNAMI		 34	/* Tsunami systype	*/
+#define ST_DEC_WILDFIRE		 35	/* Wildfire systype	*/
+#define ST_DEC_CUSCO		 36	/* CUSCO systype	*/
+
+/* UNOFFICIAL!!! */
+#define ST_UNOFFICIAL_BIAS	100
+#define ST_DTI_RUFFIAN		101	/* RUFFIAN systype	*/
+
 
 struct pcb_struct {
 	unsigned long ksp;
diff -u --recursive --new-file v2.1.88/linux/include/asm-alpha/irq.h linux/include/asm-alpha/irq.h
--- v2.1.88/linux/include/asm-alpha/irq.h	Mon Jan 12 14:51:14 1998
+++ linux/include/asm-alpha/irq.h	Mon Feb 23 10:25:10 1998
@@ -10,13 +10,18 @@
 #include <linux/linkage.h>
 #include <linux/config.h>
 
-#if defined(CONFIG_ALPHA_CABRIOLET) || defined(CONFIG_ALPHA_EB66P) || defined(CONFIG_ALPHA_EB164) || defined(CONFIG_ALPHA_PC164)
+#if defined(CONFIG_ALPHA_CABRIOLET) || defined(CONFIG_ALPHA_EB66P) || \
+	defined(CONFIG_ALPHA_EB164) || defined(CONFIG_ALPHA_PC164) || \
+	defined(CONFIG_ALPHA_LX164)
 # define NR_IRQS	33
-#elif defined(CONFIG_ALPHA_EB66) || defined(CONFIG_ALPHA_EB64P) || defined(CONFIG_ALPHA_MIKASA)
+#elif defined(CONFIG_ALPHA_EB66) || defined(CONFIG_ALPHA_EB64P) || \
+	defined(CONFIG_ALPHA_MIKASA)
 # define NR_IRQS	32
-#elif defined(CONFIG_ALPHA_ALCOR) || defined(CONFIG_ALPHA_XLT) || defined(CONFIG_ALPHA_MIATA) || defined(CONFIG_ALPHA_NORITAKE)
+#elif defined(CONFIG_ALPHA_ALCOR) || defined(CONFIG_ALPHA_XLT) || \
+	defined(CONFIG_ALPHA_MIATA) || defined(CONFIG_ALPHA_NORITAKE) || \
+	defined(CONFIG_ALPHA_RUFFIAN)
 # define NR_IRQS	48
-#elif defined(CONFIG_ALPHA_SABLE)
+#elif defined(CONFIG_ALPHA_SABLE) || defined(CONFIG_ALPHA_SX164)
 # define NR_IRQS	40
 #else
 # define NR_IRQS	16
diff -u --recursive --new-file v2.1.88/linux/include/asm-alpha/pyxis.h linux/include/asm-alpha/pyxis.h
--- v2.1.88/linux/include/asm-alpha/pyxis.h	Mon Jan 12 14:51:14 1998
+++ linux/include/asm-alpha/pyxis.h	Thu Feb 26 14:43:37 1998
@@ -1,6 +1,7 @@
 #ifndef __ALPHA_PYXIS__H__
 #define __ALPHA_PYXIS__H__
 
+#include <linux/config.h> /* CONFIG_ALPHA_RUFFIAN. */
 #include <linux/types.h>
 
 /*
@@ -178,6 +179,20 @@
  * Translate physical memory address as seen on (PCI) bus into
  * a kernel virtual address and vv.
  */
+
+#if defined(CONFIG_ALPHA_RUFFIAN)
+/* Ruffian doesn't do 1G PCI window.  */
+
+extern inline unsigned long virt_to_bus(void * address)
+{
+	return virt_to_phys(address);
+}
+
+extern inline void * bus_to_virt(unsigned long address)
+{
+	return phys_to_virt(address);
+}
+#else
 extern inline unsigned long virt_to_bus(void * address)
 {
 	return virt_to_phys(address) + PYXIS_DMA_WIN_BASE;
@@ -187,6 +202,7 @@
 {
 	return phys_to_virt(address - PYXIS_DMA_WIN_BASE);
 }
+#endif /* RUFFIAN */
 
 /*
  * I/O functions:
diff -u --recursive --new-file v2.1.88/linux/include/asm-alpha/unistd.h linux/include/asm-alpha/unistd.h
--- v2.1.88/linux/include/asm-alpha/unistd.h	Tue Feb 17 13:12:48 1998
+++ linux/include/asm-alpha/unistd.h	Sun Mar  1 10:23:16 1998
@@ -303,7 +303,7 @@
 #define __NR_utimes			363
 #define __NR_getrusage			364
 #define __NR_wait4			365
-
+#define __NR_adjtimex			366
 
 
 #if defined(__LIBRARY__) && defined(__GNUC__)
diff -u --recursive --new-file v2.1.88/linux/include/asm-i386/floppy.h linux/include/asm-i386/floppy.h
--- v2.1.88/linux/include/asm-i386/floppy.h	Mon Feb 23 18:12:11 1998
+++ linux/include/asm-i386/floppy.h	Fri Mar  6 21:02:28 1998
@@ -29,7 +29,6 @@
 
 #define SW fd_routine[use_virtual_dma&1]
 #define CSW fd_routine[can_use_virtual_dma & 1]
-#define NCSW fd_routine[(can_use_virtual_dma >> 1)& 1]
 
 
 #define fd_inb(port)			inb_p(port)
@@ -39,9 +38,6 @@
 #define fd_free_dma()           CSW._free_dma(FLOPPY_DMA)
 #define fd_enable_irq()         enable_irq(FLOPPY_IRQ)
 #define fd_disable_irq()        disable_irq(FLOPPY_IRQ)
-#define fd_request_irq()        NCSW._request_irq(FLOPPY_IRQ, floppy_interrupt,\
-						 SA_INTERRUPT|SA_SAMPLE_RANDOM,\
-						 "floppy", NULL)
 #define fd_free_irq()		free_irq(FLOPPY_IRQ, NULL)
 #define fd_get_dma_residue()    SW._get_dma_residue(FLOPPY_DMA)
 #define fd_dma_mem_alloc(size)	SW._dma_mem_alloc(size)
@@ -180,14 +176,15 @@
 }
 
 
-static int vdma_request_irq(unsigned int irq,
-			    void (*handler)(int, void *, struct pt_regs *),
-			    unsigned long flags, 
-			    const char *device,
-			    void *dev_id)
+static int fd_request_irq(void)
 {
-	return request_irq(irq, floppy_hardint,SA_INTERRUPT,device, 
-			   dev_id);
+	if(can_use_virtual_dma)
+		return request_irq(FLOPPY_IRQ, floppy_hardint,SA_INTERRUPT,
+						   "floppy", NULL);
+	else
+		return request_irq(FLOPPY_IRQ, floppy_interrupt,
+						   SA_INTERRUPT|SA_SAMPLE_RANDOM,
+						   "floppy", NULL);	
 
 }
 
@@ -265,11 +262,6 @@
 	int (*_request_dma)(unsigned int dmanr, const char * device_id);
 	void (*_free_dma)(unsigned int dmanr);
 	int (*_get_dma_residue)(unsigned int dummy);
-	int (*_request_irq)(unsigned int irq,
-			   void (*handler)(int, void *, struct pt_regs *),
-			   unsigned long flags, 
-			   const char *device,
-			   void *dev_id);
 	unsigned long (*_dma_mem_alloc) (unsigned long size);
 	int (*_dma_setup)(char *addr, unsigned long size, int mode, int io);
 } fd_routine[] = {
@@ -277,7 +269,6 @@
 		request_dma,
 		free_dma,
 		get_dma_residue,
-		request_irq,
 		dma_mem_alloc,
 		hard_dma_setup
 	},
@@ -285,7 +276,6 @@
 		vdma_request_dma,
 		vdma_nop,
 		vdma_get_dma_residue,
-		vdma_request_irq,
 		vdma_mem_alloc,
 		vdma_dma_setup
 	}
diff -u --recursive --new-file v2.1.88/linux/include/asm-mips/ng1.h linux/include/asm-mips/ng1.h
--- v2.1.88/linux/include/asm-mips/ng1.h	Tue Dec 16 12:46:10 1997
+++ linux/include/asm-mips/ng1.h	Tue Feb 24 22:39:30 1998
@@ -46,29 +46,3 @@
         unsigned char green [256];
         unsigned char blue  [256];
 };
-
-
-
-#define GFX_NAME_NEWPORT "NG1"
-
-/* ioctls */
-#define NG1_SET_CURSOR_HOTSPOT 21001
-struct ng1_set_cursor_hotspot {
-	unsigned short xhot;
-        unsigned short yhot;
-};
-
-#define NG1_SETDISPLAYMODE     21006
-struct ng1_setdisplaymode_args {
-        int wid;
-        unsigned int mode;
-};
-
-#define NG1_SETGAMMARAMP0      21007
-struct ng1_setgammaramp_args {
-        unsigned char red   [256];
-        unsigned char green [256];
-        unsigned char blue  [256];
-};
-
-
diff -u --recursive --new-file v2.1.88/linux/include/asm-mips/prctl.h linux/include/asm-mips/prctl.h
--- v2.1.88/linux/include/asm-mips/prctl.h	Tue Dec 16 12:46:11 1997
+++ linux/include/asm-mips/prctl.h	Tue Feb 24 22:39:30 1998
@@ -3,48 +3,6 @@
  *
  * The IRIX kernel maps a page at PRDA_ADDRESS with the
  * contents of prda and fills it the bits on prda_sys.
- * $Id: prctl.h,v 1.1 1997/09/21 22:27:19 miguel Exp $
- */
-
-#ifndef __PRCTL_H__
-#define __PRCTL_H__
-
-#define PRDA_ADDRESS 0x200000L
-#define PRDA ((struct prda *) PRDA_ADDRESS)
-
-struct prda_sys {
-	pid_t t_pid; 
-        u32   t_hint;
-        u32   t_dlactseq;
-        u32   t_fpflags;
-        u32   t_prid;		/* processor type, $prid CP0 register */
-        u32   t_dlendseq;
-        u64   t_unused1[5];
-        pid_t t_rpid;   
-        s32   t_resched;
-        u32   t_unused[8];
-        u32   t_cpu;		/* current/last cpu */
-
-	/* FIXME: The signal information, not supported by Linux now */
-	u32   t_flags;		/* if true, then the sigprocmask is in userspace */
-	u32   t_sigprocmask [1]; /* the sigprocmask */
-};
-
-struct prda {
-	char fill [0xe00];
-	struct prda_sys prda_sys;
-};
-
-#define t_sys           prda_sys
-
-ptrdiff_t prctl (int op, int v1, int v2);
-
-#endif
-/*
- * IRIX prctl interface
- *
- * The IRIX kernel maps a page at PRDA_ADDRESS with the
- * contents of prda and fills it the bits on prda_sys.
  * $Id: prctl.h,v 1.1 1997/12/02 02:28:28 ralf Exp $
  */
 
diff -u --recursive --new-file v2.1.88/linux/include/asm-sparc/svr4.h linux/include/asm-sparc/svr4.h
--- v2.1.88/linux/include/asm-sparc/svr4.h	Sat Nov  9 00:30:13 1996
+++ linux/include/asm-sparc/svr4.h	Tue Feb 24 22:08:01 1998
@@ -69,7 +69,7 @@
 	caddr_t ptr;
 } svr4_xrs_t;
 
-/* Machine dependant context */
+/* Machine dependent context */
 typedef struct {
 	svr4_gregset_t   greg;	/* registers 0..19 (see top) */
 	svr4_gwindows_t  *gwin;	/* may point to register windows */
diff -u --recursive --new-file v2.1.88/linux/include/asm-sparc64/svr4.h linux/include/asm-sparc64/svr4.h
--- v2.1.88/linux/include/asm-sparc64/svr4.h	Thu Sep  4 12:54:49 1997
+++ linux/include/asm-sparc64/svr4.h	Tue Feb 24 22:08:02 1998
@@ -70,7 +70,7 @@
 	u32    ptr;
 } svr4_xrs_t;
 
-/* Machine dependant context */
+/* Machine dependent context */
 typedef struct {
 	svr4_gregset_t   greg;	/* registers 0..19 (see top) */
 	u32		 gwin;	/* may point to register windows */
diff -u --recursive --new-file v2.1.88/linux/include/linux/affs_fs_sb.h linux/include/linux/affs_fs_sb.h
--- v2.1.88/linux/include/linux/affs_fs_sb.h	Tue Dec  2 22:25:07 1997
+++ linux/include/linux/affs_fs_sb.h	Mon Feb 23 22:01:26 1998
@@ -37,6 +37,7 @@
 
 struct affs_sb_info {
 	int s_partition_size;		/* Partition size in blocks. */
+	int s_blksize;			/* Initial device blksize */
 	s32 s_root_block;		/* FFS root block number. */
 	int s_hashsize;			/* Size of hash table. */
 	unsigned long s_flags;		/* See below. */
diff -u --recursive --new-file v2.1.88/linux/include/linux/coda.h linux/include/linux/coda.h
--- v2.1.88/linux/include/linux/coda.h	Tue Jan  6 10:00:22 1998
+++ linux/include/linux/coda.h	Wed Mar  4 15:14:32 1998
@@ -15,16 +15,48 @@
 #include <sys/types.h>
 #endif 
 
-#ifdef __linux__
+#ifdef DJGPP
+#ifdef KERNEL
+typedef unsigned long u_long;
+typedef unsigned int u_int;
+typedef unsigned short u_short;
+typedef u_long ino_t;
+typedef u_long dev_t;
+typedef void * caddr_t;
+typedef u_long u_quad_t;
+
+#define inline
+
+struct timespec {
+        long       ts_sec;
+        long       ts_nsec;
+};
+#else  /* DJGPP but not KERNEL */
+#include <sys/types.h>
+#include <sys/time.h>
+typedef u_long u_quad_t;
+#endif /* !KERNEL */
+#endif /* !DJGPP */
+
+
+#if defined(__linux__) || defined(__CYGWIN32__)
 #define cdev_t u_quad_t
 #if !defined(_UQUAD_T_) && (!defined(__GLIBC__) || __GLIBC__ < 2)
 #define _UQUAD_T_ 1
 typedef unsigned long long u_quad_t;
-#endif 
+#endif
 #else
 #define cdev_t dev_t
 #endif
 
+#ifdef __CYGWIN32__
+typedef unsigned char u_int8_t;
+struct timespec {
+        time_t  tv_sec;         /* seconds */
+        long    tv_nsec;        /* nanoseconds */
+};
+#endif
+
 
 /*
  * Cfs constants
@@ -69,21 +101,21 @@
 /*
  * File types
  */
-#define	DT_UNKNOWN	 0
-#define	DT_FIFO		 1
-#define	DT_CHR		 2
-#define	DT_DIR		 4
-#define	DT_BLK		 6
-#define	DT_REG		 8
-#define	DT_LNK		10
-#define	DT_SOCK		12
-#define	DT_WHT		14
+#define	CDT_UNKNOWN	 0
+#define	CDT_FIFO		 1
+#define	CDT_CHR		 2
+#define	CDT_DIR		 4
+#define	CDT_BLK		 6
+#define	CDT_REG		 8
+#define	CDT_LNK		10
+#define	CDT_SOCK		12
+#define	CDT_WHT		14
 
 /*
  * Convert between stat structure types and directory types.
  */
-#define	IFTODT(mode)	(((mode) & 0170000) >> 12)
-#define	DTTOIF(dirtype)	((dirtype) << 12)
+#define	IFTOCDT(mode)	(((mode) & 0170000) >> 12)
+#define	CDTTOIF(dirtype)	((dirtype) << 12)
 
 #endif
 
@@ -124,7 +156,11 @@
 #define _CODACRED_T_
 struct coda_cred {
     vuid_t cr_uid, cr_euid, cr_suid, cr_fsuid; /* Real, efftve, set, fs uid*/
-    vgid_t cr_gid, cr_egid, cr_sgid, cr_fsgid; /* same for groups */
+#if	defined(__NetBSD__) || defined(__FreeBSD__)
+    vgid_t cr_groupid, cr_egid, cr_sgid, cr_fsgid; /* same for groups */
+#else
+    vgid_t cr_gid,     cr_egid, cr_sgid, cr_fsgid; /* same for groups */
+#endif
 };
 #endif 
 
@@ -189,7 +225,8 @@
 #define CFS_ZAPDIR      ((u_long) 28)
 #define CFS_ZAPVNODE    ((u_long) 29)
 #define CFS_PURGEFID    ((u_long) 30)
-#define CFS_NCALLS 31
+#define CFS_OPEN_BY_PATH ((u_long) 31)
+#define CFS_NCALLS 32
 
 #define DOWNCALL(opcode) (opcode >= CFS_REPLACE && opcode <= CFS_PURGEFID)
 
@@ -537,6 +574,18 @@
     ViceFid OldFid;
 };
 
+/* cfs_open_by_path: */
+struct cfs_open_by_path_in {
+    struct cfs_in_hdr ih;
+    ViceFid	VFid;
+    int	flags;
+};
+
+struct cfs_open_by_path_out {
+    struct cfs_out_hdr oh;
+	int path;
+};
+
 /* 
  * Occasionally, don't cache the fid returned by CFS_LOOKUP. For instance, if
  * the fid is inconsistent. This case is handled by setting the top bit of the
@@ -566,6 +615,7 @@
     struct cfs_inactive_in cfs_inactive;
     struct cfs_vget_in cfs_vget;
     struct cfs_rdwr_in cfs_rdwr;
+	struct cfs_open_by_path_in cfs_open_by_path;
 };
 
 union outputArgs {
@@ -587,6 +637,7 @@
     struct cfs_purgefid_out cfs_purgefid;
     struct cfs_rdwr_out cfs_rdwr;
     struct cfs_replace_out cfs_replace;
+	struct cfs_open_by_path_out cfs_open_by_path;
 };    
 
 union cfs_downcalls {
diff -u --recursive --new-file v2.1.88/linux/include/linux/coda_cache.h linux/include/linux/coda_cache.h
--- v2.1.88/linux/include/linux/coda_cache.h	Mon Dec 22 08:40:59 1997
+++ linux/include/linux/coda_cache.h	Wed Mar  4 15:14:32 1998
@@ -22,13 +22,13 @@
 };
 
 void coda_ccinsert(struct coda_cache *el, struct super_block *sb);
-void coda_cninsert(struct coda_cache *el, struct cnode *cnp);
+void coda_cninsert(struct coda_cache *el, struct coda_inode_info *cnp);
 void coda_ccremove(struct coda_cache *el);
 void coda_cnremove(struct coda_cache *el);
 void coda_cache_create(struct inode *inode, int mask);
 struct coda_cache *coda_cache_find(struct inode *inode);
 void coda_cache_enter(struct inode *inode, int mask);
-void coda_cache_clear_cnp(struct cnode *cnp);
+void coda_cache_clear_cnp(struct coda_inode_info *cnp);
 void coda_cache_clear_all(struct super_block *sb);
 void coda_cache_clear_cred(struct super_block *sb, struct coda_cred *cred);
 int coda_cache_check(struct inode *inode, int mask);
diff -u --recursive --new-file v2.1.88/linux/include/linux/coda_cnode.h linux/include/linux/coda_cnode.h
--- v2.1.88/linux/include/linux/coda_cnode.h	Tue Jan  6 10:00:22 1998
+++ linux/include/linux/coda_cnode.h	Wed Dec 31 16:00:00 1969
@@ -1,57 +0,0 @@
-/*
- * Cnode definitions for Coda.
- * Original version: (C) 1996 Peter Braam 
- * Rewritten for Linux 2.1: (C) 1997 Carnegie Mellon University
- *
- * Carnegie Mellon encourages users of this code to contribute improvements
- * to the Coda project. Contact Peter Braam <coda@cs.cmu.edu>.
- */
-
-
-#ifndef	_CNODE_H_
-#define	_CNODE_H_
-#include <linux/coda.h>
-
-
-#define CODA_CNODE_MAGIC        0x47114711
-
-/* defintion of cnode, which combines ViceFid with inode information */
-struct cnode {
-        struct inode      *c_vnode;     /*  inode associated with cnode */
-        ViceFid	           c_fid;	/* Coda identifier */
-        u_short	           c_flags;     /* flags (see below) */
-        int                c_magic;     /* to verify the data structure */
-        u_short	           c_ocount;    /* count of openers */
-        u_short            c_owrite;    /* count of open for write */
-        u_short            c_mmcount;   /* count of mmappers */
-        struct inode      *c_ovp;       /* open vnode pointer */
-        struct list_head   c_cnhead;    /* head of cache entries */
-	struct list_head   c_volrootlist; /* list of volroot cnoddes */
-};
-
-/* flags */
-#define C_VATTR       0x1         /* Validity of vattr in the cnode */
-#define C_SYMLINK     0x2         /* Validity of symlink pointer in the cnode */
-#define C_DYING       0x4	  /* Set for outstanding cnodes from venus (which died) */
-#define C_ZAPFID      0x8
-#define C_ZAPDIR      0x10
-
-void coda_cnode_free(struct cnode *);
-int coda_cnode_make(struct inode **, struct ViceFid *, struct super_block *);
-int coda_cnode_makectl(struct inode **inode, struct super_block *sb);
-struct inode *coda_fid_to_inode(ViceFid *fid, struct super_block *sb);
-
-/* inode to cnode */
-static inline struct cnode *ITOC(struct inode *inode)
-{
-	return ((struct cnode *)inode->u.generic_ip);
-}
-
-/* cnode to inode */
-static inline struct inode *CTOI(struct cnode *cnode)
-{
-	return (cnode->c_vnode);
-}
-
-#endif	
-
diff -u --recursive --new-file v2.1.88/linux/include/linux/coda_fs_i.h linux/include/linux/coda_fs_i.h
--- v2.1.88/linux/include/linux/coda_fs_i.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/coda_fs_i.h	Wed Mar  4 15:14:32 1998
@@ -0,0 +1,52 @@
+/*
+ *  coda_fs_i.h
+ *
+ *  Copyright (C) 1998 Carnegie Mellon University
+ *
+ */
+
+#ifndef _LINUX_CODA_FS_I
+#define _LINUX_CODA_FS_I
+
+#ifdef __KERNEL__
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/coda.h>
+
+
+
+#define CODA_CNODE_MAGIC        0x47114711
+/*
+ * smb fs inode data (in memory only)
+ */
+struct coda_inode_info {
+        struct ViceFid     c_fid;	/* Coda identifier */
+        u_short	           c_flags;     /* flags (see below) */
+        u_short	           c_ocount;    /* count of openers */
+        u_short            c_owrite;    /* count of open for write */
+        u_short            c_mmcount;   /* count of mmappers */
+        struct inode      *c_ovp;       /* open inode  pointer */
+        struct list_head   c_cnhead;    /* head of cache entries */
+	struct list_head   c_volrootlist; /* list of volroot cnoddes */
+        struct inode      *c_vnode;     /*  inode associated with cnode */
+        int                c_magic;     /* to verify the data structure */
+};
+
+/* flags */
+#define C_VATTR       0x1         /* Validity of vattr in the cnode */
+#define C_SYMLINK     0x2         /* Validity of symlink pointer in the cnode */
+#define C_DYING       0x4	  /* Set for outstanding cnodes from venus (which died) */
+#define C_ZAPFID      0x8
+#define C_ZAPDIR      0x10
+#define C_INITED      0x20
+
+int coda_cnode_make(struct inode **, struct ViceFid *, struct super_block *);
+int coda_cnode_makectl(struct inode **inode, struct super_block *sb);
+struct inode *coda_fid_to_inode(ViceFid *fid, struct super_block *sb);
+
+/* inode to cnode */
+#define ITOC(inode) ((struct coda_inode_info *)&((inode)->u.coda_i))
+
+
+#endif
+#endif
diff -u --recursive --new-file v2.1.88/linux/include/linux/coda_linux.h linux/include/linux/coda_linux.h
--- v2.1.88/linux/include/linux/coda_linux.h	Tue Jan  6 10:00:22 1998
+++ linux/include/linux/coda_linux.h	Wed Mar  4 15:14:32 1998
@@ -43,7 +43,7 @@
 extern int coda_access_cache;
 
 /* this file:  heloers */
-char *coda_f2s(ViceFid *f, char *s);
+char *coda_f2s(ViceFid *f);
 int coda_isroot(struct inode *i);
 int coda_fid_is_volroot(struct ViceFid *);
 int coda_iscontrol(const char *name, size_t length);
@@ -93,22 +93,7 @@
 #define EXIT    \
     if(coda_print_entry) printk("Process %d leaving %s\n",current->pid,__FUNCTION__)
 
-
-
-#define CHECK_CNODE(c)                                                \
-do {                                                                  \
-  if ( coda_debug ) {\
-    struct cnode *cnode = (c);                                          \
-  if (!cnode)                                                         \
-    printk ("%s(%d): cnode is null\n", __FUNCTION__, __LINE__);        \
-  if (cnode->c_magic != CODA_CNODE_MAGIC)                             \
-    printk ("%s(%d): cnode magic wrong\n", __FUNCTION__, __LINE__);    \
-  if (!cnode->c_vnode)                                                \
-    printk ("%s(%d): cnode has null inode\n", __FUNCTION__, __LINE__); \
-  if ( (struct cnode *)cnode->c_vnode->u.generic_ip != cnode )           \
-    printk("AAooh, %s(%d) cnode doesn't link right!\n", __FUNCTION__,__LINE__);\
-}} while (0);
-
+#define CHECK_CNODE(c) do {  } while (0);
 
 #define CODA_ALLOC(ptr, cast, size)                                       \
 do {                                                                      \
diff -u --recursive --new-file v2.1.88/linux/include/linux/elf.h linux/include/linux/elf.h
--- v2.1.88/linux/include/linux/elf.h	Thu Feb 12 20:56:13 1998
+++ linux/include/linux/elf.h	Tue Feb 24 22:08:02 1998
@@ -128,7 +128,7 @@
 #define AT_GID    13	/* real gid */
 #define AT_EGID   14	/* effective gid */
 #define AT_PLATFORM 15  /* string identifying cpu for optimizations */
-#define AT_HWCAP  16    /* arch dependant hints at cpu capabilities */
+#define AT_HWCAP  16    /* arch dependent hints at cpu capabilities */
 
 typedef struct dynamic{
   Elf32_Sword d_tag;
diff -u --recursive --new-file v2.1.88/linux/include/linux/etherdevice.h linux/include/linux/etherdevice.h
--- v2.1.88/linux/include/linux/etherdevice.h	Fri Feb  6 15:33:34 1998
+++ linux/include/linux/etherdevice.h	Fri Mar  6 21:03:10 1998
@@ -42,7 +42,7 @@
 extern struct device	* init_etherdev(struct device *, int);
 
 #ifdef CONFIG_IP_ROUTER
-static void inline eth_copy_and_sum (struct sk_buff *dest, unsigned char *src, int len, int base)
+static __inline__ void eth_copy_and_sum (struct sk_buff *dest, unsigned char *src, int len, int base)
 {
     memcpy (dest->data, src, len);
 }
diff -u --recursive --new-file v2.1.88/linux/include/linux/file.h linux/include/linux/file.h
--- v2.1.88/linux/include/linux/file.h	Mon Feb 23 18:12:11 1998
+++ linux/include/linux/file.h	Mon Feb 23 12:55:58 1998
@@ -47,4 +47,12 @@
 	}
 }
 
+/*
+ * Install a file pointer in the files structure.
+ */
+extern inline void fd_install(unsigned long fd, struct file *file)
+{
+	current->files->fd[fd] = file;
+}
+
 #endif
diff -u --recursive --new-file v2.1.88/linux/include/linux/fs.h linux/include/linux/fs.h
--- v2.1.88/linux/include/linux/fs.h	Mon Feb 23 18:12:12 1998
+++ linux/include/linux/fs.h	Fri Mar  6 21:02:11 1998
@@ -44,8 +44,8 @@
 /* And dynamically-tunable limits and defaults: */
 extern int max_inodes;
 extern int max_files, nr_files, nr_free_files;
-#define NR_INODE 4096	/* this should be bigger than NR_FILE */
-#define NR_FILE 1024	/* this can well be larger on a larger system */
+#define NR_INODE 4096	/* This should no longer be bigger than NR_FILE */
+#define NR_FILE  4096	/* this can well be larger on a larger system */
 #define NR_RESERVED_FILES 10 /* reserved for root */
 
 #define MAY_EXEC 1
@@ -264,6 +264,7 @@
 #include <linux/sysv_fs_i.h>
 #include <linux/affs_fs_i.h>
 #include <linux/ufs_fs_i.h>
+#include <linux/coda_fs_i.h>
 #include <linux/romfs_fs_i.h>
 #include <linux/smb_fs_i.h>
 #include <linux/hfs_fs_i.h>
@@ -369,6 +370,7 @@
 		struct affs_inode_info		affs_i;
 		struct ufs_inode_info		ufs_i;
 		struct romfs_inode_info		romfs_i;
+		struct coda_inode_info		coda_i;
 		struct smb_inode_info		smbfs_i;
 		struct hfs_inode_info		hfs_i;
 		struct adfs_inode_info		adfs_i;
@@ -418,13 +420,22 @@
 #define FL_ACCESS	8	/* for processes suspended by mandatory locking */
 #define FL_LOCKD	16	/* lock held by rpc.lockd */
 
+/*
+ * The POSIX file lock owner is determined by
+ * the "struct files_struct" in the thread group
+ * (or NULL for no owner - BSD locks).
+ *
+ * Lockd stuffs a "host" pointer into this.
+ */
+typedef struct files_struct *fl_owner_t;
+
 struct file_lock {
 	struct file_lock *fl_next;	/* singly linked list for this inode  */
 	struct file_lock *fl_nextlink;	/* doubly linked list of all locks */
 	struct file_lock *fl_prevlink;	/* used to simplify lock removal */
 	struct file_lock *fl_nextblock; /* circular list of blocked processes */
 	struct file_lock *fl_prevblock;
-	void *fl_owner;			/* usu. the process' task_struct */
+	fl_owner_t fl_owner;
 	unsigned int fl_pid;
 	struct wait_queue *fl_wait;
 	struct file *fl_file;
@@ -448,7 +459,7 @@
 extern int fcntl_setlk(unsigned int fd, unsigned int cmd, struct flock *l);
 
 /* fs/locks.c */
-extern void locks_remove_posix(struct task_struct *, struct file *);
+extern void locks_remove_posix(struct file *, fl_owner_t id);
 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);
@@ -594,13 +605,13 @@
 			struct inode *, struct dentry *);
 	int (*readlink) (struct dentry *, char *,int);
 	struct dentry * (*follow_link) (struct dentry *, struct dentry *);
-	int (*readpage) (struct dentry *, struct page *);
-	int (*writepage) (struct dentry *, struct page *);
+	int (*readpage) (struct file *, struct page *);
+	int (*writepage) (struct file *, struct page *);
 	int (*bmap) (struct inode *,int);
 	void (*truncate) (struct inode *);
 	int (*permission) (struct inode *, int);
 	int (*smap) (struct inode *,int);
-	int (*updatepage) (struct dentry *, struct page *, const char *,
+	int (*updatepage) (struct file *, struct page *, const char *,
 				unsigned long, unsigned int, int);
 	int (*revalidate) (struct dentry *);
 };
@@ -638,14 +649,20 @@
 extern int register_filesystem(struct file_system_type *);
 extern int unregister_filesystem(struct file_system_type *);
 
+/* fs/open.c */
+
 asmlinkage int sys_open(const char *, int, int);
 asmlinkage int sys_close(unsigned int);		/* yes, it's really unsigned */
-
-extern void kill_fasync(struct fasync_struct *fa, int sig);
+extern int do_truncate(struct dentry *, unsigned long);
+extern int get_unused_fd(void);
+extern void put_unused_fd(unsigned int);
+extern int __fput(struct file *);
+extern int close_fp(struct file *, fl_owner_t id);
 
 extern char * getname(const char * filename);
 extern void putname(char * name);
-extern int do_truncate(struct dentry *, unsigned long);
+
+extern void kill_fasync(struct fasync_struct *fa, int sig);
 extern int register_blkdev(unsigned int, const char *, struct file_operations *);
 extern int unregister_blkdev(unsigned int major, const char * name);
 extern int blkdev_open(struct inode * inode, struct file * filp);
@@ -755,32 +772,6 @@
 #define namei(pathname)		__namei(pathname, 1)
 #define lnamei(pathname)	__namei(pathname, 0)
 
-#include <asm/semaphore.h>
-
-/* Intended for short locks of the global data structures in inode.c.
- * Could be replaced with spinlocks completely, since there is
- * no blocking during manipulation of the static data; however the
- * lock in invalidate_inodes() may last relatively long.
- */
-extern struct semaphore vfs_sem;
-extern inline void vfs_lock(void)
-{
-#if 0
-#ifdef __SMP__
-	down(&vfs_sem);
-#endif
-#endif
-}
-
-extern inline void vfs_unlock(void)
-{
-#if 0
-#ifdef __SMP__
-	up(&vfs_sem);
-#endif
-#endif
-}
-
 extern void iput(struct inode *);
 extern struct inode * iget(struct super_block *, unsigned long);
 extern void clear_inode(struct inode *);
@@ -788,10 +779,7 @@
 
 extern void insert_inode_hash(struct inode *);
 extern void remove_inode_hash(struct inode *);
-extern int get_unused_fd(void);
-extern void put_unused_fd(int);
 extern struct file * get_empty_filp(void);
-extern int close_fp(struct file *);
 extern struct buffer_head * get_hash_table(kdev_t, int, int);
 extern struct buffer_head * getblk(kdev_t, int, int);
 extern struct buffer_head * find_buffer(kdev_t dev, int block, int size);
@@ -819,7 +807,7 @@
 
 extern int brw_page(int, struct page *, kdev_t, int [], int, int);
 
-extern int generic_readpage(struct dentry *, struct page *);
+extern int generic_readpage(struct file *, struct page *);
 extern int generic_file_mmap(struct file *, struct vm_area_struct *);
 extern ssize_t generic_file_read(struct file *, char *, size_t, loff_t *);
 extern ssize_t generic_file_write(struct file *, const char*, size_t, loff_t*);
diff -u --recursive --new-file v2.1.88/linux/include/linux/if_shaper.h linux/include/linux/if_shaper.h
--- v2.1.88/linux/include/linux/if_shaper.h	Mon Jan 12 15:28:18 1998
+++ linux/include/linux/if_shaper.h	Tue Feb 24 22:08:02 1998
@@ -5,7 +5,7 @@
 
 #define SHAPER_QLEN	10
 /*
- *	This is a bit speed dependant (read it shouldnt be a constant!)
+ *	This is a bit speed dependent (read it shouldn't be a constant!)
  *
  *	5 is about right for 28.8 upwards. Below that double for every
  *	halving of speed or so. - ie about 20 for 9600 baud.
diff -u --recursive --new-file v2.1.88/linux/include/linux/if_slip.h linux/include/linux/if_slip.h
--- v2.1.88/linux/include/linux/if_slip.h	Wed Feb 14 23:21:36 1996
+++ linux/include/linux/if_slip.h	Sun Mar  1 14:40:39 1998
@@ -23,6 +23,8 @@
 #define SIOCGKEEPALIVE	(SIOCDEVPRIVATE+1)		/* Get keepalive timeout */
 #define SIOCSOUTFILL	(SIOCDEVPRIVATE+2)		/* Set outfill timeout */
 #define	SIOCGOUTFILL	(SIOCDEVPRIVATE+3)		/* Get outfill timeout */
+#define SIOCSLEASE	(SIOCDEVPRIVATE+4)		/* Set "leased" line type */
+#define	SIOCGLEASE	(SIOCDEVPRIVATE+5)		/* Get line type */
 
 
 #endif
diff -u --recursive --new-file v2.1.88/linux/include/linux/in_route.h linux/include/linux/in_route.h
--- v2.1.88/linux/include/linux/in_route.h	Mon Feb 23 18:12:12 1998
+++ linux/include/linux/in_route.h	Sun Mar  1 14:40:40 1998
@@ -6,6 +6,7 @@
 #define RTCF_DEAD	RTNH_F_DEAD
 #define RTCF_ONLINK	RTNH_F_ONLINK
 
+/* Obsolete flag. About to be deleted */
 #define RTCF_NOPMTUDISC RTM_F_NOPMTUDISC
 
 #define RTCF_NOTIFY	0x00010000
diff -u --recursive --new-file v2.1.88/linux/include/linux/inetdevice.h linux/include/linux/inetdevice.h
--- v2.1.88/linux/include/linux/inetdevice.h	Mon Jan 12 15:28:18 1998
+++ linux/include/linux/inetdevice.h	Sun Mar  1 14:40:40 1998
@@ -1,20 +1,26 @@
 #ifndef _LINUX_INETDEVICE_H
 #define _LINUX_INETDEVICE_H
 
-/* IPv4 specific flags. They are initialized from global sysctl variables,
-   when IPv4 is initialized.
- */
-
-#define IFF_IP_FORWARD		1
-#define IFF_IP_PROXYARP		2
-#define IFF_IP_RXREDIRECTS	4
-#define IFF_IP_TXREDIRECTS	8
-#define IFF_IP_SHAREDMEDIA	0x10
-#define IFF_IP_MFORWARD		0x20
-#define IFF_IP_RPFILTER		0x40
-
 #ifdef __KERNEL__
 
+struct ipv4_devconf
+{
+	int	accept_redirects;
+	int	send_redirects;
+	int	secure_redirects;
+	int	shared_media;
+	int	accept_source_route;
+	int	rp_filter;
+	int	proxy_arp;
+	int	bootp_relay;
+	int	log_martians;
+	int	forwarding;
+	int	mc_forwarding;
+	void	*sysctl;
+};
+
+extern struct ipv4_devconf ipv4_devconf;
+
 struct in_device
 {
 	struct device		*dev;
@@ -23,16 +29,26 @@
 	unsigned long		mr_v1_seen;
 	unsigned		flags;
 	struct neigh_parms	*arp_parms;
+	struct ipv4_devconf	cnf;
 };
 
-
-#define IN_DEV_RPFILTER(in_dev)	(ipv4_config.rfc1812_filter && ((in_dev)->flags&IFF_IP_RPFILTER))
-#define IN_DEV_MFORWARD(in_dev)	(ipv4_config.multicast_route && ((in_dev)->flags&IFF_IP_MFORWARD))
-#define IN_DEV_PROXY_ARP(in_dev)	(ipv4_config.proxy_arp || (in_dev)->flags&IFF_IP_PROXYARP)
-#define IN_DEV_FORWARD(in_dev)		(IS_ROUTER || ((in_dev)->flags&IFF_IP_FORWARD))
-#define IN_DEV_SHARED_MEDIA(in_dev)	(ipv4_config.rfc1620_redirects || (in_dev)->flags&IFF_IP_SHAREDMEDIA)
-#define IN_DEV_RX_REDIRECTS(in_dev)	(ipv4_config.accept_redirects || (in_dev)->flags&IFF_IP_RXREDIRECTS)
-#define IN_DEV_TX_REDIRECTS(in_dev)	(/*ipv4_config.send_redirects ||*/ (in_dev)->flags&IFF_IP_TXREDIRECTS)
+#define IN_DEV_FORWARD(in_dev)		((in_dev)->cnf.forwarding)
+#define IN_DEV_MFORWARD(in_dev)		(ipv4_devconf.mc_forwarding && (in_dev)->cnf.mc_forwarding)
+#define IN_DEV_RPFILTER(in_dev)		(ipv4_devconf.rp_filter && (in_dev)->cnf.rp_filter)
+#define IN_DEV_SOURCE_ROUTE(in_dev)	(ipv4_devconf.accept_source_route && (in_dev)->cnf.accept_source_route)
+#define IN_DEV_BOOTP_RELAY(in_dev)	(ipv4_devconf.bootp_relay && (in_dev)->cnf.bootp_relay)
+
+#define IN_DEV_LOG_MARTIANS(in_dev)	(ipv4_devconf.log_martians || (in_dev)->cnf.log_martians)
+#define IN_DEV_PROXY_ARP(in_dev)	(ipv4_devconf.proxy_arp || (in_dev)->cnf.proxy_arp)
+#define IN_DEV_SHARED_MEDIA(in_dev)	(ipv4_devconf.shared_media || (in_dev)->cnf.shared_media)
+#define IN_DEV_TX_REDIRECTS(in_dev)	(ipv4_devconf.send_redirects || (in_dev)->cnf.send_redirects)
+#define IN_DEV_SEC_REDIRECTS(in_dev)	(ipv4_devconf.secure_redirects || (in_dev)->cnf.secure_redirects)
+
+#define IN_DEV_RX_REDIRECTS(in_dev) \
+	((IN_DEV_FORWARD(in_dev) && \
+	  (ipv4_devconf.accept_redirects && (in_dev)->cnf.accept_redirects)) \
+	 || (!IN_DEV_FORWARD(in_dev) && \
+	  (ipv4_devconf.accept_redirects || (in_dev)->cnf.accept_redirects)))
 
 struct in_ifaddr
 {
@@ -62,6 +78,7 @@
 extern struct in_ifaddr *inet_ifa_byprefix(struct in_device *in_dev, u32 prefix, u32 mask);
 extern int		inet_add_bootp_addr(struct device *dev);
 extern void		inet_del_bootp_addr(struct device *dev);
+extern void		inet_forward_change(void);
 
 extern __inline__ int inet_ifa_match(u32 addr, struct in_ifaddr *ifa)
 {
diff -u --recursive --new-file v2.1.88/linux/include/linux/ipv6_route.h linux/include/linux/ipv6_route.h
--- v2.1.88/linux/include/linux/ipv6_route.h	Mon Jan 12 15:28:19 1998
+++ linux/include/linux/ipv6_route.h	Sun Mar  1 14:40:40 1998
@@ -13,6 +13,14 @@
 #ifndef _LINUX_IPV6_ROUTE_H
 #define _LINUX_IPV6_ROUTE_H
 
+enum
+{
+	RTA_IPV6_UNSPEC,
+	RTA_IPV6_HOPLIMIT,
+};
+
+#define	RTA_IPV6_MAX RTA_IPV6_HOPLIMIT
+
 
 #define RTF_DEFAULT	0x00010000	/* default - learned via ND	*/
 #define RTF_ALLONLINK	0x00020000	/* fallback, no routers on link	*/
diff -u --recursive --new-file v2.1.88/linux/include/linux/iso_fs_i.h linux/include/linux/iso_fs_i.h
--- v2.1.88/linux/include/linux/iso_fs_i.h	Thu Feb 12 20:56:13 1998
+++ linux/include/linux/iso_fs_i.h	Sat Feb 21 13:19:29 1998
@@ -6,6 +6,9 @@
  */
 struct iso_inode_info {
 	unsigned int i_first_extent;
+	unsigned char i_file_format;
+	unsigned long i_next_section_ino;
+	off_t i_section_size;
 };
 
 #endif
diff -u --recursive --new-file v2.1.88/linux/include/linux/kerneld.h linux/include/linux/kerneld.h
--- v2.1.88/linux/include/linux/kerneld.h	Sat Nov 29 10:33:21 1997
+++ linux/include/linux/kerneld.h	Mon Feb 23 10:25:15 1998
@@ -48,8 +48,10 @@
 };
 
 #ifdef __KERNEL__
+#include <linux/string.h>
+
 extern int kerneld_send(int msgtype, int ret_size, int msgsz,
-		const char *text, const char *ret_val);
+			const char *text, const char *ret_val);
 
 /*
  * Request that a module should be loaded.
@@ -59,8 +61,8 @@
 static inline int request_module(const char *name)
 {
 	return kerneld_send(KERNELD_REQUEST_MODULE,
-			0 | KERNELD_WAIT,
-			strlen(name), name, NULL);
+			    0 | KERNELD_WAIT,
+			    strlen(name), name, NULL);
 }
 
 /*
@@ -70,8 +72,8 @@
 static inline int release_module(const char *name, int waitflag)
 {
 	return kerneld_send(KERNELD_RELEASE_MODULE,
-			0 | (waitflag?KERNELD_WAIT:KERNELD_NOWAIT),
-			strlen(name), name, NULL);
+			    0 | (waitflag?KERNELD_WAIT:KERNELD_NOWAIT),
+			    strlen(name), name, NULL);
 }
 
 /*
@@ -81,8 +83,8 @@
 static inline int delayed_release_module(const char *name)
 {
 	return kerneld_send(KERNELD_DELAYED_RELEASE_MODULE,
-			0 | KERNELD_NOWAIT,
-			strlen(name), name, NULL);
+			    0 | KERNELD_NOWAIT,
+			    strlen(name), name, NULL);
 }
 
 /*
@@ -94,8 +96,8 @@
 static inline int cancel_release_module(const char *name)
 {
 	return kerneld_send(KERNELD_CANCEL_RELEASE_MODULE,
-			0 | KERNELD_NOWAIT,
-			strlen(name), name, NULL);
+			    0 | KERNELD_NOWAIT,
+			    strlen(name), name, NULL);
 }
 
 /*
@@ -104,8 +106,8 @@
 static inline int ksystem(const char *cmd, int waitflag)
 {
 	return kerneld_send(KERNELD_SYSTEM,
-			0 | (waitflag?KERNELD_WAIT:KERNELD_NOWAIT),
-			strlen(cmd), cmd, NULL);
+			    0 | (waitflag?KERNELD_WAIT:KERNELD_NOWAIT),
+			    strlen(cmd), cmd, NULL);
 }
 
 /*
@@ -114,18 +116,19 @@
 static inline int kerneld_route(const char *ip_route)
 {
 	return kerneld_send(KERNELD_REQUEST_ROUTE,
-			0 | KERNELD_WAIT,
-			strlen(ip_route), ip_route, NULL);
+			    0 | KERNELD_WAIT,
+			    strlen(ip_route), ip_route, NULL);
 }
 
 /*
  * Handle an external screen blanker
  */
-static inline int kerneld_blanker(int on_off) /* 0 => "off", else "on" */
+static inline int kerneld_blanker(int on_off)
 {
+	char *s = on_off ? "on" : "off";
 	return kerneld_send(KERNELD_BLANKER,
-			0 | (on_off?KERNELD_NOWAIT:KERNELD_WAIT),
-			strlen(on_off?"on":"off"), on_off?"on":"off", NULL);
+			    0 | (on_off ? KERNELD_NOWAIT : KERNELD_WAIT),
+			    strlen(s), s, NULL);
 }
 
 #endif /* __KERNEL__ */
diff -u --recursive --new-file v2.1.88/linux/include/linux/mc146818rtc.h linux/include/linux/mc146818rtc.h
--- v2.1.88/linux/include/linux/mc146818rtc.h	Fri Feb  6 15:33:06 1998
+++ linux/include/linux/mc146818rtc.h	Fri Mar  6 21:02:28 1998
@@ -142,6 +142,8 @@
 #define RTC_SET_TIME	_IOW('p', 0x0a, struct rtc_time) /* Set RTC time    */
 #define RTC_IRQP_READ	_IOR('p', 0x0b, unsigned long)	 /* Read IRQ rate   */
 #define RTC_IRQP_SET	_IOW('p', 0x0c, unsigned long)	 /* Set IRQ rate    */
+#define RTC_EPOCH_READ	_IOR('p', 0x0d, unsigned long)	 /* Read epoch      */
+#define RTC_EPOCH_SET	_IOW('p', 0x0e, unsigned long)	 /* Set epoch       */
 
 
 #endif /* _MC146818RTC_H */
diff -u --recursive --new-file v2.1.88/linux/include/linux/mm.h linux/include/linux/mm.h
--- v2.1.88/linux/include/linux/mm.h	Fri Feb  6 15:32:54 1998
+++ linux/include/linux/mm.h	Fri Mar  6 21:02:12 1998
@@ -48,7 +48,7 @@
 
 	struct vm_operations_struct * vm_ops;
 	unsigned long vm_offset;
-	struct dentry * vm_dentry;
+	struct file * vm_file;
 	unsigned long vm_pte;			/* shared mem */
 };
 
@@ -133,7 +133,7 @@
 #define PG_uptodate		 3
 #define PG_free_after		 4
 #define PG_decr_after		 5
-#define PG_swap_unlock_after	 6
+/* Unused			 6 */
 #define PG_DMA			 7
 #define PG_Slab			 8
 #define PG_swap_cache		 9
@@ -146,7 +146,6 @@
 #define PageUptodate(page)	(test_bit(PG_uptodate, &(page)->flags))
 #define PageFreeAfter(page)	(test_bit(PG_free_after, &(page)->flags))
 #define PageDecrAfter(page)	(test_bit(PG_decr_after, &(page)->flags))
-#define PageSwapUnlockAfter(page) (test_bit(PG_swap_unlock_after, &(page)->flags))
 #define PageDMA(page)		(test_bit(PG_DMA, &(page)->flags))
 #define PageSlab(page)		(test_bit(PG_Slab, &(page)->flags))
 #define PageSwapCache(page)	(test_bit(PG_swap_cache, &(page)->flags))
@@ -252,6 +251,7 @@
 }
 
 /* memory.c & swap.c*/
+extern int free_memory_available(void);
 
 #define free_page(addr) free_pages((addr),0)
 extern void FASTCALL(free_pages(unsigned long addr, unsigned long order));
diff -u --recursive --new-file v2.1.88/linux/include/linux/msdos_fs.h linux/include/linux/msdos_fs.h
--- v2.1.88/linux/include/linux/msdos_fs.h	Fri Feb  6 15:34:49 1998
+++ linux/include/linux/msdos_fs.h	Fri Mar  6 21:05:13 1998
@@ -255,7 +255,7 @@
 
 /* mmap.c */
 extern int fat_mmap(struct file *, struct vm_area_struct *);
-extern int fat_readpage(struct dentry *, struct page *);
+extern int fat_readpage(struct file *, struct page *);
 
 
 /* vfat.c */
diff -u --recursive --new-file v2.1.88/linux/include/linux/ncp_fs.h linux/include/linux/ncp_fs.h
--- v2.1.88/linux/include/linux/ncp_fs.h	Fri Feb  6 15:34:54 1998
+++ linux/include/linux/ncp_fs.h	Fri Mar  6 21:05:21 1998
@@ -29,7 +29,7 @@
 struct ncp_fs_info {
 	int version;
 	struct sockaddr_ipx addr;
-	uid_t mounted_uid;
+	__kernel_uid_t mounted_uid;
 	int connection;		/* Connection number the server assigned us */
 	int buffer_size;	/* The negotiated buffer size, to be
 				   used for read/write requests! */
@@ -38,13 +38,72 @@
 	__u32 directory_id;
 };
 
+struct ncp_sign_init
+{
+	char sign_root[8];
+	char sign_last[16];
+};
+
+struct ncp_lock_ioctl
+{
+#define NCP_LOCK_LOG	0
+#define NCP_LOCK_SH	1
+#define NCP_LOCK_EX	2
+#define NCP_LOCK_CLEAR	256
+	int		cmd;
+	int		origin;
+	unsigned int	offset;
+	unsigned int	length;
+#define NCP_LOCK_DEFAULT_TIMEOUT	18
+#define NCP_LOCK_MAX_TIMEOUT		180
+	int		timeout;
+};
+
+struct ncp_setroot_ioctl
+{
+	int		volNumber;
+	int		namespace;
+	__u32		dirEntNum;
+};
+
+struct ncp_objectname_ioctl
+{
+#define NCP_AUTH_NONE	0x00
+#define NCP_AUTH_BIND	0x31
+#define NCP_AUTH_NDS	0x32
+	int		auth_type;
+	size_t		object_name_len;
+	void*		object_name;	/* an userspace data, in most cases user name */
+};
+
+struct ncp_privatedata_ioctl
+{
+	size_t		len;
+	void*		data;		/* ~1000 for NDS */
+};
+
 #define	NCP_IOC_NCPREQUEST		_IOR('n', 1, struct ncp_ioctl_request)
 #define	NCP_IOC_GETMOUNTUID		_IOW('n', 2, uid_t)
+#define NCP_IOC_GETMOUNTUID_INT		_IOW('n', 2, unsigned int)
 #define NCP_IOC_CONN_LOGGED_IN          _IO('n', 3)
 
 #define NCP_GET_FS_INFO_VERSION (1)
 #define NCP_IOC_GET_FS_INFO             _IOWR('n', 4, struct ncp_fs_info)
 
+#define NCP_IOC_SIGN_INIT		_IOR('n', 5, struct ncp_sign_init)
+#define NCP_IOC_SIGN_WANTED		_IOR('n', 6, int)
+#define NCP_IOC_SET_SIGN_WANTED		_IOW('n', 6, int)
+
+#define NCP_IOC_LOCKUNLOCK		_IOR('n', 7, struct ncp_lock_ioctl)
+
+#define NCP_IOC_GETROOT			_IOW('n', 8, struct ncp_setroot_ioctl)
+#define NCP_IOC_SETROOT			_IOR('n', 8, struct ncp_setroot_ioctl)
+
+#define NCP_IOC_GETOBJECTNAME		_IOWR('n', 9, struct ncp_objectname_ioctl)
+#define NCP_IOC_SETOBJECTNAME		_IOR('n', 9, struct ncp_objectname_ioctl)
+#define NCP_IOC_GETPRIVATEDATA		_IOWR('n', 10, struct ncp_privatedata_ioctl)
+#define NCP_IOC_SETPRIVATEDATA		_IOR('n', 10, struct ncp_privatedata_ioctl)
+
 /*
  * The packet size to allocate. One page should be enough.
  */
@@ -55,8 +114,12 @@
 
 #ifdef __KERNEL__
 
+#include <linux/config.h>
+
 #undef NCPFS_PARANOIA
+#ifndef DEBUG_NCP
 #define DEBUG_NCP 0
+#endif
 #if DEBUG_NCP > 0
 #define DPRINTK(format, args...) printk(format , ## args)
 #else
@@ -140,9 +203,11 @@
 #endif				/* DEBUG_NCP_MALLOC */
 
 /* linux/fs/ncpfs/inode.c */
+int ncp_notify_change(struct dentry *, struct iattr *attr);
 struct super_block *ncp_read_super(struct super_block *, void *, int);
 struct inode *ncp_iget(struct super_block *, struct ncpfs_inode_info *);
 void ncp_update_inode(struct inode *, struct nw_file_info *);
+void ncp_update_inode2(struct inode *, struct nw_file_info *);
 extern int init_ncp_fs(void);
 
 /* linux/fs/ncpfs/dir.c */
@@ -202,12 +267,26 @@
 
 static inline int ncp_preserve_case(struct inode *i)
 {
-       	return (ncp_namespace(i) == NW_NS_OS2);
+#if defined(CONFIG_NCPFS_NFS_NS) || defined(CONFIG_NCPFS_OS2_NS)
+	int ns = ncp_namespace(i);
+#endif
+	return
+#ifdef CONFIG_NCPFS_OS2_NS
+	(ns == NW_NS_OS2) ||
+#endif	/* CONFIG_NCPFS_OS2_NS */
+#ifdef CONFIG_NCPFS_NFS_NS
+	(ns == NW_NS_NFS) ||
+#endif	/* CONFIG_NCPFS_NFS_NS */
+	0;
 }
 
 static inline int ncp_case_sensitive(struct inode *i)
 {
+#ifdef CONFIG_NCPFS_NFS_NS
+	return ncp_namespace(i) == NW_NS_NFS;
+#else
 	return 0;
+#endif	/* CONFIG_NCPFS_NFS_NS */
 } 
 
 #endif				/* __KERNEL__ */
diff -u --recursive --new-file v2.1.88/linux/include/linux/ncp_fs_sb.h linux/include/linux/ncp_fs_sb.h
--- v2.1.88/linux/include/linux/ncp_fs_sb.h	Fri Feb  6 15:34:54 1998
+++ linux/include/linux/ncp_fs_sb.h	Fri Mar  6 21:05:21 1998
@@ -14,6 +14,7 @@
 #ifdef __KERNEL__
 
 #define NCP_DEFAULT_BUFSIZE 1024
+#define NCP_DEFAULT_OPTIONS 0		/* 2 for packet signatures */
 
 struct ncp_server {
 
@@ -21,7 +22,7 @@
 					   interest for us later, so we store
 					   it completely. */
 
-	__u8 name_space[NCP_NUMBER_OF_VOLUMES];
+	__u8 name_space[NCP_NUMBER_OF_VOLUMES + 2];
 
 	struct file *ncp_filp;	/* File pointer to ncp socket */
 
@@ -50,7 +51,30 @@
 	int ncp_reply_size;
 
 	struct ncp_inode_info root;
+#if 0
 	char root_path;		/* '\0' */
+#else
+	struct dentry* root_dentry;
+#endif
+
+/* info for packet signing */
+	int sign_wanted;        /* 1=Server needs signed packets */
+	int sign_active;        /* 0=don't do signing, 1=do */
+	char sign_root[8];	/* generated from password and encr. key */
+	char sign_last[16];	
+
+	/* Authentication info: NDS or BINDERY, username */
+	struct {
+		int    auth_type;
+		size_t object_name_len;
+		void*  object_name;
+		int    object_type;
+	} auth;
+	/* Password info */
+	struct {
+		size_t len;
+		void*  data;
+	} priv;
 };
 
 static inline int ncp_conn_valid(struct ncp_server *server)
@@ -66,3 +90,4 @@
 #endif				/* __KERNEL__ */
 
 #endif
+ 
diff -u --recursive --new-file v2.1.88/linux/include/linux/ncp_mount.h linux/include/linux/ncp_mount.h
--- v2.1.88/linux/include/linux/ncp_mount.h	Fri Feb  6 15:34:54 1998
+++ linux/include/linux/ncp_mount.h	Fri Mar  6 21:05:21 1998
@@ -18,12 +18,15 @@
 /* Values for flags */
 #define NCP_MOUNT_SOFT 0x0001
 #define NCP_MOUNT_INTR 0x0002
+#define NCP_MOUNT_STRONG 0x0004   /* enable delete/rename of r/o files */
+#define NCP_MOUNT_NO_OS2 0x0008
+#define NCP_MOUNT_NO_NFS 0x0010
 
 struct ncp_mount_data {
 	int version;
 	unsigned int ncp_fd;	/* The socket to the ncp port */
-	uid_t mounted_uid;	/* Who may umount() this filesystem? */
-	pid_t wdog_pid;		/* Who cares for our watchdog packets? */
+	__kernel_uid_t mounted_uid;	/* Who may umount() this filesystem? */
+	__kernel_pid_t wdog_pid;		/* Who cares for our watchdog packets? */
 
 	unsigned char mounted_vol[NCP_VOLNAME_LEN + 1];
 	unsigned int time_out;	/* How long should I wait after
@@ -31,10 +34,10 @@
 	unsigned int retry_count;	/* And how often should I retry? */
 	unsigned int flags;
 
-	uid_t uid;
-	gid_t gid;
-	mode_t file_mode;
-	mode_t dir_mode;
+	__kernel_uid_t uid;
+	__kernel_gid_t gid;
+	__kernel_mode_t file_mode;
+	__kernel_mode_t dir_mode;
 };
 
 #endif
diff -u --recursive --new-file v2.1.88/linux/include/linux/net.h linux/include/linux/net.h
--- v2.1.88/linux/include/linux/net.h	Fri Feb  6 15:32:54 1998
+++ linux/include/linux/net.h	Thu Feb 26 20:19:31 1998
@@ -93,7 +93,7 @@
 			 int flags);
   int	(*getname)	(struct socket *sock, struct sockaddr *uaddr,
 			 int *usockaddr_len, int peer);
-  unsigned int (*poll)	(struct socket *sock, struct poll_table_struct *wait);
+  unsigned int (*poll)	(struct file *file, struct socket *sock, struct poll_table_struct *wait);
   int	(*ioctl)	(struct socket *sock, unsigned int cmd,
 			 unsigned long arg);
   int	(*listen)	(struct socket *sock, int len);
diff -u --recursive --new-file v2.1.88/linux/include/linux/netdevice.h linux/include/linux/netdevice.h
--- v2.1.88/linux/include/linux/netdevice.h	Fri Feb  6 15:33:34 1998
+++ linux/include/linux/netdevice.h	Fri Mar  6 21:03:10 1998
@@ -66,6 +66,7 @@
 #endif
 
 struct neighbour;
+struct neigh_parms;
 struct sk_buff;
 
 /*
@@ -288,7 +289,7 @@
 
 	int			(*hard_header_parse)(struct sk_buff *skb,
 						     unsigned char *haddr);
-	int			(*neigh_setup)(struct neighbour *n);
+	int			(*neigh_setup)(struct device *dev, struct neigh_parms *);
 	int			(*accept_fastpath)(struct device *, struct dst_entry*);
 
 #ifdef CONFIG_NET_FASTROUTE
@@ -339,8 +340,14 @@
 extern int		unregister_netdevice_notifier(struct notifier_block *nb);
 extern int		dev_new_index(void);
 extern struct device	*dev_get_by_index(int ifindex);
-extern int		register_gifconf(int family, int (*func)(struct device *dev, char *bufptr, int len));
 extern int		dev_restart(struct device *dev);
+
+typedef int gifconf_func_t(struct device * dev, char * bufptr, int len);
+extern int		register_gifconf(unsigned int family, gifconf_func_t * gifconf);
+extern __inline__ int unregister_gifconf(unsigned int family)
+{
+	return register_gifconf(family, 0);
+}
 
 #define HAVE_NETIF_RX 1
 extern void		netif_rx(struct sk_buff *skb);
diff -u --recursive --new-file v2.1.88/linux/include/linux/netlink.h linux/include/linux/netlink.h
--- v2.1.88/linux/include/linux/netlink.h	Mon Jan 12 15:28:19 1998
+++ linux/include/linux/netlink.h	Sun Mar  1 14:40:40 1998
@@ -46,6 +46,7 @@
 #define NLM_F_REPLACE	0x100	/* Override existing		*/
 #define NLM_F_EXCL	0x200	/* Do not touch, if it exists	*/
 #define NLM_F_CREATE	0x400	/* Create, if it does not exist	*/
+#define NLM_F_APPEND	0x800	/* Add to end of list		*/
 
 /*
    4.4BSD ADD		NLM_F_CREATE|NLM_F_EXCL
@@ -65,6 +66,7 @@
 				  (struct nlmsghdr*)(((char*)(nlh)) + NLMSG_ALIGN((nlh)->nlmsg_len)))
 #define NLMSG_OK(nlh,len) ((nlh)->nlmsg_len >= sizeof(struct nlmsghdr) && \
 			   (nlh)->nlmsg_len <= (len))
+#define NLMSG_PAYLOAD(nlh,len) ((nlh)->nlmsg_len - NLMSG_SPACE((len)))
 
 #define NLMSG_NOOP		0x1	/* Nothing.		*/
 #define NLMSG_ERROR		0x2	/* Error		*/
diff -u --recursive --new-file v2.1.88/linux/include/linux/nfs_fs.h linux/include/linux/nfs_fs.h
--- v2.1.88/linux/include/linux/nfs_fs.h	Fri Feb  6 15:33:04 1998
+++ linux/include/linux/nfs_fs.h	Fri Mar  6 21:02:12 1998
@@ -210,20 +210,20 @@
 /*
  * linux/fs/nfs/write.c
  */
-extern int  nfs_writepage(struct dentry *, struct page *);
+extern int  nfs_writepage(struct file *, struct page *);
 extern int  nfs_find_dentry_request(struct inode *, struct dentry *);
 extern int  nfs_check_failed_request(struct inode *);
 extern int  nfs_check_error(struct inode *);
 extern int  nfs_flush_dirty_pages(struct inode *, pid_t, off_t, off_t);
 extern int  nfs_truncate_dirty_pages(struct inode *, unsigned long);
 extern void nfs_invalidate_pages(struct inode *);
-extern int  nfs_updatepage(struct dentry *, struct page *, const char *,
+extern int  nfs_updatepage(struct file *, struct page *, const char *,
 			unsigned long, unsigned int, int);
 
 /*
  * linux/fs/nfs/read.c
  */
-extern int  nfs_readpage(struct dentry *, struct page *);
+extern int  nfs_readpage(struct file *, struct page *);
 
 /*
  * linux/fs/mount_clnt.c
diff -u --recursive --new-file v2.1.88/linux/include/linux/parport.h linux/include/linux/parport.h
--- v2.1.88/linux/include/linux/parport.h	Fri Jan 30 11:28:10 1998
+++ linux/include/linux/parport.h	Tue Feb 24 22:40:45 1998
@@ -3,6 +3,64 @@
 #ifndef _PARPORT_H_
 #define _PARPORT_H_
 
+/* Start off with user-visible constants */
+
+/* Maximum of 8 ports per machine */
+#define PARPORT_MAX  8 
+
+/* Magic numbers */
+#define PARPORT_IRQ_NONE  -1
+#define PARPORT_DMA_NONE  -1
+#define PARPORT_IRQ_AUTO  -2
+#define PARPORT_DMA_AUTO  -2
+#define PARPORT_DISABLE   -2
+
+#define PARPORT_CONTROL_STROBE    0x1
+#define PARPORT_CONTROL_AUTOFD    0x2
+#define PARPORT_CONTROL_INIT      0x4
+#define PARPORT_CONTROL_SELECT    0x8
+#define PARPORT_CONTROL_INTEN     0x10
+#define PARPORT_CONTROL_DIRECTION 0x20
+
+#define PARPORT_STATUS_ERROR      0x8
+#define PARPORT_STATUS_SELECT     0x10
+#define PARPORT_STATUS_PAPEROUT   0x20
+#define PARPORT_STATUS_ACK        0x40
+#define PARPORT_STATUS_BUSY       0x80
+
+/* Type classes for Plug-and-Play probe.  */
+typedef enum {
+	PARPORT_CLASS_LEGACY = 0,       /* Non-IEEE1284 device */
+	PARPORT_CLASS_PRINTER,
+	PARPORT_CLASS_MODEM,
+	PARPORT_CLASS_NET,
+	PARPORT_CLASS_HDC,              /* Hard disk controller */
+	PARPORT_CLASS_PCMCIA,
+	PARPORT_CLASS_MEDIA,            /* Multimedia device */
+	PARPORT_CLASS_FDC,              /* Floppy disk controller */
+	PARPORT_CLASS_PORTS,
+	PARPORT_CLASS_SCANNER,
+	PARPORT_CLASS_DIGCAM,
+	PARPORT_CLASS_OTHER,            /* Anything else */
+	PARPORT_CLASS_UNSPEC            /* No CLS field in ID */
+} parport_device_class;
+
+/* The "modes" entry in parport is a bit field representing the following
+ * modes.
+ * Note that PARPORT_MODE_PCECPEPP is for the SMC EPP+ECP mode which is NOT
+ * 100% compatible with EPP.
+ */
+#define PARPORT_MODE_PCSPP	        0x0001
+#define PARPORT_MODE_PCPS2		0x0002
+#define PARPORT_MODE_PCEPP		0x0004
+#define PARPORT_MODE_PCECP		0x0008
+#define PARPORT_MODE_PCECPEPP		0x0010
+#define PARPORT_MODE_PCECR		0x0020  /* ECR Register Exists */
+#define PARPORT_MODE_PCECPPS2		0x0040
+
+/* The rest is for the kernel only */
+#ifdef __KERNEL__
+
 #include <asm/system.h>
 #include <asm/ptrace.h>
 #include <asm/spinlock.h>
@@ -11,16 +69,6 @@
 
 #define PARPORT_NEED_GENERIC_OPS
 
-/* Maximum of 8 ports per machine */
-#define PARPORT_MAX  8 
-
-/* Magic numbers */
-#define PARPORT_IRQ_NONE  -2
-#define PARPORT_DMA_NONE  -2
-#define PARPORT_IRQ_AUTO  -1
-#define PARPORT_DMA_AUTO  -1
-#define PARPORT_DISABLE   -2
-
 /* Define this later. */
 struct parport;
 
@@ -39,29 +87,29 @@
 };
 
 struct parport_operations {
-	void (*write_data)(struct parport *, unsigned int);
-	unsigned int (*read_data)(struct parport *);
-	void (*write_control)(struct parport *, unsigned int);
-	unsigned int (*read_control)(struct parport *);
-	unsigned int (*frob_control)(struct parport *, unsigned int mask, unsigned int val);
-	void (*write_econtrol)(struct parport *, unsigned int);
-	unsigned int (*read_econtrol)(struct parport *);
-	unsigned int (*frob_econtrol)(struct parport *, unsigned int mask, unsigned int val);
-	void (*write_status)(struct parport *, unsigned int);
-	unsigned int (*read_status)(struct parport *);
-	void (*write_fifo)(struct parport *, unsigned int);
-	unsigned int (*read_fifo)(struct parport *);
+	void (*write_data)(struct parport *, unsigned char);
+	unsigned char (*read_data)(struct parport *);
+	void (*write_control)(struct parport *, unsigned char);
+	unsigned char (*read_control)(struct parport *);
+	unsigned char (*frob_control)(struct parport *, unsigned char mask, unsigned char val);
+	void (*write_econtrol)(struct parport *, unsigned char);
+	unsigned char (*read_econtrol)(struct parport *);
+	unsigned char (*frob_econtrol)(struct parport *, unsigned char mask, unsigned char val);
+	void (*write_status)(struct parport *, unsigned char);
+	unsigned char (*read_status)(struct parport *);
+	void (*write_fifo)(struct parport *, unsigned char);
+	unsigned char (*read_fifo)(struct parport *);
 
 	void (*change_mode)(struct parport *, int);
 
 	void (*release_resources)(struct parport *);
 	int (*claim_resources)(struct parport *);
 
-	unsigned int (*epp_write_block)(struct parport *, void *, unsigned int);
-	unsigned int (*epp_read_block)(struct parport *, void *, unsigned int);
+	size_t (*epp_write_block)(struct parport *, void *, size_t);
+	size_t (*epp_read_block)(struct parport *, void *, size_t);
 
-	unsigned int (*ecp_write_block)(struct parport *, void *, unsigned int, void (*fn)(struct parport *, void *, unsigned int), void *);
-	unsigned int (*ecp_read_block)(struct parport *, void *, unsigned int, void (*fn)(struct parport *, void *, unsigned int), void *);
+	int (*ecp_write_block)(struct parport *, void *, size_t, void (*fn)(struct parport *, void *, size_t), void *);
+	int (*ecp_read_block)(struct parport *, void *, size_t, void (*fn)(struct parport *, void *, size_t), void *);
 
 	void (*save_state)(struct parport *, struct parport_state *);
 	void (*restore_state)(struct parport *, struct parport_state *);
@@ -74,36 +122,6 @@
 	void (*dec_use_count)(void);
 };
 
-#define PARPORT_CONTROL_STROBE    0x1
-#define PARPORT_CONTROL_AUTOFD    0x2
-#define PARPORT_CONTROL_INIT      0x4
-#define PARPORT_CONTROL_SELECT    0x8
-#define PARPORT_CONTROL_INTEN     0x10
-#define PARPORT_CONTROL_DIRECTION 0x20
-
-#define PARPORT_STATUS_ERROR      0x8
-#define PARPORT_STATUS_SELECT     0x10
-#define PARPORT_STATUS_PAPEROUT   0x20
-#define PARPORT_STATUS_ACK        0x40
-#define PARPORT_STATUS_BUSY       0x80
-
-/* Type classes for Plug-and-Play probe.  */
-typedef enum {
-	PARPORT_CLASS_LEGACY = 0,       /* Non-IEEE1284 device */
-	PARPORT_CLASS_PRINTER,
-	PARPORT_CLASS_MODEM,
-	PARPORT_CLASS_NET,
-	PARPORT_CLASS_HDC,              /* Hard disk controller */
-	PARPORT_CLASS_PCMCIA,
-	PARPORT_CLASS_MEDIA,            /* Multimedia device */
-	PARPORT_CLASS_FDC,              /* Floppy disk controller */
-	PARPORT_CLASS_PORTS,
-	PARPORT_CLASS_SCANNER,
-	PARPORT_CLASS_DIGCAM,
-	PARPORT_CLASS_OTHER,            /* Anything else */
-	PARPORT_CLASS_UNSPEC            /* No CLS field in ID */
-} parport_device_class;
-
 struct parport_device_info {
 	parport_device_class class;
 	char *mfr;
@@ -178,7 +196,7 @@
 	struct parport_operations *ops;
 	void *private_data;     /* for lowlevel driver */
 
-	  
+	int number;		/* port index - the `n' in `parportn' */
 	spinlock_t lock;
 };
 
@@ -243,28 +261,29 @@
 
 extern void parport_release(struct pardevice *dev);
 
-extern __inline__ unsigned int parport_yield(struct pardevice *dev, 
-					     unsigned int block)
+/* parport_yield relinquishes the port if it would be helpful to other
+ * drivers.  The return value is the same as for parport_claim.
+ */
+extern __inline__ unsigned int parport_yield(struct pardevice *dev)
 {
 	unsigned long int timeslip = (jiffies - dev->time);
 	if ((dev->port->waithead == NULL) || (timeslip < dev->timeslice))
-		return 1;
+		return 0;
 	parport_release(dev);
-	return (block)?parport_claim_or_block(dev):parport_claim(dev);
+	return parport_claim(dev);
 }
 
-/* The "modes" entry in parport is a bit field representing the following
- * modes.
- * Note that LP_ECPEPP is for the SMC EPP+ECP mode which is NOT
- * 100% compatible with EPP.
+/* parport_yield_blocking is the same but uses parport_claim_or_block
+ * instead of parport_claim.
  */
-#define PARPORT_MODE_PCSPP	        0x0001
-#define PARPORT_MODE_PCPS2		0x0002
-#define PARPORT_MODE_PCEPP		0x0004
-#define PARPORT_MODE_PCECP		0x0008
-#define PARPORT_MODE_PCECPEPP		0x0010
-#define PARPORT_MODE_PCECR		0x0020  /* ECR Register Exists */
-#define PARPORT_MODE_PCECPPS2		0x0040
+extern __inline__ unsigned int parport_yield_blocking(struct pardevice *dev)
+{
+	unsigned long int timeslip = (jiffies - dev->time);
+	if ((dev->port->waithead == NULL) || (timeslip < dev->timeslice))
+		return 0;
+	parport_release(dev);
+	return parport_claim_or_block(dev);
+}
 
 /* Flags used to identify what a device does. */
 #define PARPORT_DEV_TRAN	        0x0000  /* We're transient. */
@@ -329,4 +348,5 @@
 #define parport_claim_resources(p)         (p)->ops->claim_resources(p)
 #endif
 
+#endif /* __KERNEL__ */
 #endif /* _PARPORT_H_ */
diff -u --recursive --new-file v2.1.88/linux/include/linux/parport_pc.h linux/include/linux/parport_pc.h
--- v2.1.88/linux/include/linux/parport_pc.h	Fri Jan 30 11:28:10 1998
+++ linux/include/linux/parport_pc.h	Tue Feb 24 22:33:04 1998
@@ -13,80 +13,80 @@
 #define STATUS   0x1
 #define DATA     0
 
-extern __inline__ void parport_pc_write_epp(struct parport *p, unsigned int d)
+extern __inline__ void parport_pc_write_epp(struct parport *p, unsigned char d)
 {
 	outb(d, p->base+EPPREG);
 }
 
-extern __inline__ unsigned int parport_pc_read_epp(struct parport *p)
+extern __inline__ unsigned char parport_pc_read_epp(struct parport *p)
 {
-	return (unsigned int)inb(p->base+EPPREG);
+	return inb(p->base+EPPREG);
 }
 
-extern __inline__ unsigned int parport_pc_read_configb(struct parport *p)
+extern __inline__ unsigned char parport_pc_read_configb(struct parport *p)
 {
-	return (unsigned int)inb(p->base+CONFIGB);
+	return inb(p->base+CONFIGB);
 }
 
-extern __inline__ void parport_pc_write_data(struct parport *p, unsigned int d)
+extern __inline__ void parport_pc_write_data(struct parport *p, unsigned char d)
 {
 	outb(d, p->base+DATA);
 }
 
-extern __inline__ unsigned int parport_pc_read_data(struct parport *p)
+extern __inline__ unsigned char parport_pc_read_data(struct parport *p)
 {
-	return (unsigned int)inb(p->base+DATA);
+	return inb(p->base+DATA);
 }
 
-extern __inline__ void parport_pc_write_control(struct parport *p, unsigned int d)
+extern __inline__ void parport_pc_write_control(struct parport *p, unsigned char d)
 {
 	outb(d, p->base+CONTROL);
 }
 
-extern __inline__ unsigned int parport_pc_read_control(struct parport *p)
+extern __inline__ unsigned char parport_pc_read_control(struct parport *p)
 {
-	return (unsigned int)inb(p->base+CONTROL);
+	return inb(p->base+CONTROL);
 }
 
-extern __inline__ unsigned int parport_pc_frob_control(struct parport *p, unsigned int mask,  unsigned int val)
+extern __inline__ unsigned char parport_pc_frob_control(struct parport *p, unsigned char mask,  unsigned char val)
 {
-	unsigned int old = (unsigned int)inb(p->base+CONTROL);
+	unsigned char old = inb(p->base+CONTROL);
 	outb(((old & ~mask) ^ val), p->base+CONTROL);
 	return old;
 }
 
-extern __inline__ void parport_pc_write_status(struct parport *p, unsigned int d)
+extern __inline__ void parport_pc_write_status(struct parport *p, unsigned char d)
 {
 	outb(d, p->base+STATUS);
 }
 
-extern __inline__ unsigned int parport_pc_read_status(struct parport *p)
+extern __inline__ unsigned char parport_pc_read_status(struct parport *p)
 {
-	return (unsigned int)inb(p->base+STATUS);
+	return inb(p->base+STATUS);
 }
 
-extern __inline__ void parport_pc_write_econtrol(struct parport *p, unsigned int d)
+extern __inline__ void parport_pc_write_econtrol(struct parport *p, unsigned char d)
 {
 	outb(d, p->base+ECONTROL);
 }
 
-extern __inline__ unsigned int parport_pc_read_econtrol(struct parport *p)
+extern __inline__ unsigned char parport_pc_read_econtrol(struct parport *p)
 {
-	return (unsigned int)inb(p->base+ECONTROL);
+	return inb(p->base+ECONTROL);
 }
 
-extern __inline__ unsigned int parport_pc_frob_econtrol(struct parport *p, unsigned int mask,  unsigned int val)
+extern __inline__ unsigned char parport_pc_frob_econtrol(struct parport *p, unsigned char mask,  unsigned char val)
 {
-	unsigned int old = (unsigned int)inb(p->base+ECONTROL);
+	unsigned char old = inb(p->base+ECONTROL);
 	outb(((old & ~mask) ^ val), p->base+ECONTROL);
 	return old;
 }
 
 extern void parport_pc_change_mode(struct parport *p, int m);
 
-extern void parport_pc_write_fifo(struct parport *p, unsigned int v);
+extern void parport_pc_write_fifo(struct parport *p, unsigned char v);
 
-extern unsigned int parport_pc_read_fifo(struct parport *p);
+extern unsigned char parport_pc_read_fifo(struct parport *p);
 
 extern void parport_pc_disable_irq(struct parport *p);
 
@@ -100,13 +100,13 @@
 
 extern void parport_pc_restore_state(struct parport *p, struct parport_state *s);
 
-extern unsigned int parport_pc_epp_read_block(struct parport *p, void *buf, unsigned  int length);
+extern size_t parport_pc_epp_read_block(struct parport *p, void *buf, size_t length);
 
-extern unsigned int parport_pc_epp_write_block(struct parport *p, void *buf, unsigned  int length);
+extern size_t parport_pc_epp_write_block(struct parport *p, void *buf, size_t length);
 
-extern unsigned int parport_pc_ecp_read_block(struct parport *p, void *buf, unsigned  int length, void (*fn)(struct parport *, void *, unsigned int), void *handle);
+extern int parport_pc_ecp_read_block(struct parport *p, void *buf, size_t length, void (*fn)(struct parport *, void *, size_t), void *handle);
 
-extern unsigned int parport_pc_ecp_write_block(struct parport *p, void *buf, unsigned  int length, void (*fn)(struct parport *, void *, unsigned int), void *handle);
+extern int parport_pc_ecp_write_block(struct parport *p, void *buf, size_t length, void (*fn)(struct parport *, void *, size_t), void *handle);
 
 extern int parport_pc_examine_irq(struct parport *p);
 
diff -u --recursive --new-file v2.1.88/linux/include/linux/pci.h linux/include/linux/pci.h
--- v2.1.88/linux/include/linux/pci.h	Tue Feb 17 13:12:49 1998
+++ linux/include/linux/pci.h	Wed Mar  4 15:31:34 1998
@@ -503,6 +503,7 @@
 
 #define PCI_VENDOR_ID_CONTAQ		0x1080
 #define PCI_DEVICE_ID_CONTAQ_82C599	0x0600
+/* ??? Alpha SX164 has reference to device nr 0xc693 as a CYPRESS bridge.  */
 
 #define PCI_VENDOR_ID_FOREX		0x1083
 
@@ -787,6 +788,7 @@
 
 #define PCI_VENDOR_ID_3DFX		0x121a
 #define PCI_DEVICE_ID_3DFX_VOODOO	0x0001
+#define PCI_DEVICE_ID_3DFX_VOODOO2	0x0002
 
 #define PCI_VENDOR_ID_SIGMADES		0x1236
 #define PCI_DEVICE_ID_SIGMADES_6425	0x6401
diff -u --recursive --new-file v2.1.88/linux/include/linux/poll.h linux/include/linux/poll.h
--- v2.1.88/linux/include/linux/poll.h	Fri Feb  6 15:33:19 1998
+++ linux/include/linux/poll.h	Fri Mar  6 21:02:48 1998
@@ -11,6 +11,7 @@
 
 
 struct poll_table_entry {
+	struct file * filp;
 	struct wait_queue wait;
 	struct wait_queue ** wait_address;
 };
@@ -22,7 +23,7 @@
 
 #define __MAX_POLL_TABLE_ENTRIES (PAGE_SIZE / sizeof (struct poll_table_entry))
 
-extern inline void poll_wait(struct wait_queue ** wait_address, poll_table *p)
+extern inline void poll_wait(struct file * filp, struct wait_queue ** wait_address, poll_table *p)
 {
 	struct poll_table_entry * entry;
 
@@ -31,6 +32,8 @@
 	if (p->nr >= __MAX_POLL_TABLE_ENTRIES)
 		return;
  	entry = p->entry + p->nr;
+ 	entry->filp = filp;
+ 	filp->f_count++;
 	entry->wait_address = wait_address;
 	entry->wait.task = current;
 	entry->wait.next = NULL;
diff -u --recursive --new-file v2.1.88/linux/include/linux/route.h linux/include/linux/route.h
--- v2.1.88/linux/include/linux/route.h	Fri Feb  6 15:34:56 1998
+++ linux/include/linux/route.h	Sun Mar  1 14:40:40 1998
@@ -12,6 +12,9 @@
  *
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
  *
+ * Changes:
+ *              Mike McLagan    :       Routing by source
+ *
  *		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
diff -u --recursive --new-file v2.1.88/linux/include/linux/rtnetlink.h linux/include/linux/rtnetlink.h
--- v2.1.88/linux/include/linux/rtnetlink.h	Mon Jan 12 15:28:19 1998
+++ linux/include/linux/rtnetlink.h	Sun Mar  1 14:40:40 1998
@@ -5,6 +5,7 @@
 #include <linux/netlink.h>
 
 #define RTNL_DEBUG 1
+#define CONFIG_RTNL_OLD_IFINFO 1
 
 
 /****
@@ -35,19 +36,24 @@
 #define	RTM_DELRULE	(RTM_BASE+17)
 #define	RTM_GETRULE	(RTM_BASE+18)
 
-#define	RTM_MAX		(RTM_BASE+19)
-
-
-/* Generic structure for encapsulation optional route
-   information. It is reminiscent of sockaddr, but with sa_family
-   replaced with attribute type.
-   It would be good, if constructions of sort:
-   struct something {
-     struct rtattr rta;
-     struct a_content a;
-   }
-   had correct alignment. It is true for x86, but I have no idea
-   how to make it on 64bit architectures. Please, teach me. --ANK
+#define	RTM_NEWQDISC	(RTM_BASE+20)
+#define	RTM_DELQDSIC	(RTM_BASE+21)
+#define	RTM_GETQDISC	(RTM_BASE+22)
+
+#define	RTM_NEWTFLOW	(RTM_BASE+24)
+#define	RTM_DELTFLOW	(RTM_BASE+25)
+#define	RTM_GETTFLOW	(RTM_BASE+26)
+
+#define	RTM_NEWTFILTER	(RTM_BASE+28)
+#define	RTM_DELTFILTER	(RTM_BASE+29)
+#define	RTM_GETTFILTER	(RTM_BASE+30)
+
+#define	RTM_MAX		(RTM_BASE+31)
+
+/* 
+   Generic structure for encapsulation optional route information.
+   It is reminiscent of sockaddr, but with sa_family replaced
+   with attribute type.
  */
 
 struct rtattr
@@ -56,25 +62,6 @@
 	unsigned short	rta_type;
 };
 
-enum rtattr_type_t
-{
-	RTA_UNSPEC,
-	RTA_DST,
-	RTA_SRC,
-	RTA_IIF,
-	RTA_OIF,
-	RTA_GATEWAY,
-	RTA_PRIORITY,
-	RTA_PREFSRC,
-	RTA_WINDOW,
-	RTA_RTT,
-	RTA_MTU,
-	RTA_IFNAME,
-	RTA_CACHEINFO
-};
-
-#define RTA_MAX RTA_CACHEINFO
-
 /* Macros to handle rtattributes */
 
 #define RTA_ALIGNTO	4
@@ -86,49 +73,14 @@
 #define RTA_LENGTH(len)	(RTA_ALIGN(sizeof(struct rtattr)) + (len))
 #define RTA_SPACE(len)	RTA_ALIGN(RTA_LENGTH(len))
 #define RTA_DATA(rta)   ((void*)(((char*)(rta)) + RTA_LENGTH(0)))
+#define RTA_PAYLOAD(rta) ((rta)->rta_len - RTA_LENGTH(0))
 
-struct rta_cacheinfo
-{
-	__u32	rta_clntref;
-	__u32	rta_lastuse;
-	__s32	rta_expires;
-	__u32	rta_error;
-	__u32	rta_used;
-};
-
-/*
- * "struct rtnexthop" describres all necessary nexthop information,
- * i.e. parameters of path to a destination via this nextop.
- *
- * At the moment it is impossible to set different prefsrc, mtu, window
- * and rtt for different paths from multipath.
- */
-
-struct rtnexthop
-{
-	unsigned short		rtnh_len;
-	unsigned char		rtnh_flags;
-	unsigned char		rtnh_hops;
-	int			rtnh_ifindex;
-};
-
-/* rtnh_flags */
-
-#define RTNH_F_DEAD		1	/* Nexthop is dead (used by multipath)	*/
-#define RTNH_F_PERVASIVE	2	/* Do recursive gateway lookup	*/
-#define RTNH_F_ONLINK		4	/* Gateway is forced on link	*/
 
-/* Macros to handle hexthops */
 
-#define RTNH_ALIGNTO	4
-#define RTNH_ALIGN(len) ( ((len)+RTNH_ALIGNTO-1) & ~(RTNH_ALIGNTO-1) )
-#define RTNH_OK(rtnh,len) ((rtnh)->rtnh_len >= sizeof(struct rtnexthop) && \
-			   (rtnh)->rtnh_len <= (len))
-#define RTNH_NEXT(rtnh)	((struct rtnexthop*)(((char*)(rtnh)) + RTNH_ALIGN((rtnh)->rtnh_len)))
-#define RTNH_LENGTH(len) (RTNH_ALIGN(sizeof(struct rtnexthop)) + (len))
-#define RTNH_SPACE(len)	RTNH_ALIGN(RTNH_LENGTH(len))
-#define RTNH_DATA(rtnh)   ((struct rtattr*)(((char*)(rtnh)) + RTNH_LENGTH(0)))
 
+/******************************************************************************
+ *		Definitions used in routing table administation.
+ ****/
 
 struct rtmsg
 {
@@ -136,21 +88,24 @@
 	unsigned char		rtm_dst_len;
 	unsigned char		rtm_src_len;
 	unsigned char		rtm_tos;
+
 	unsigned char		rtm_table;	/* Routing table id */
 	unsigned char		rtm_protocol;	/* Routing protocol; see below	*/
+#ifdef CONFIG_RTNL_OLD_IFINFO
 	unsigned char		rtm_nhs;	/* Number of nexthops */
+#else
+	unsigned char		rtm_scope;	/* See below */	
+#endif
 	unsigned char		rtm_type;	/* See below	*/
+
+#ifdef CONFIG_RTNL_OLD_IFINFO
 	unsigned short		rtm_optlen;	/* Byte length of rtm_opt */
 	unsigned char		rtm_scope;	/* See below */	
 	unsigned char		rtm_whatsit;	/* Unused byte */
+#endif
 	unsigned		rtm_flags;
 };
 
-#define RTM_RTA(r)  ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct rtmsg))))
-#define RTM_RTNH(r) ((struct rtnexthop*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct rtmsg)) \
-					   + NLMSG_ALIGN((r)->rtm_optlen)))
-#define RTM_NHLEN(nlh,r) ((nlh)->nlmsg_len - NLMSG_SPACE(sizeof(struct rtmsg)) - NLMSG_ALIGN((r)->rtm_optlen))
-
 /* rtm_type */
 
 enum
@@ -173,6 +128,7 @@
 
 #define RTN_MAX RTN_XRESOLVE
 
+
 /* rtm_protocol */
 
 #define RTPROT_UNSPEC	0
@@ -198,8 +154,8 @@
 
    Really it is not scope, but sort of distance to the destination.
    NOWHERE are reserved for not existing destinations, HOST is our
-   local addresses, LINK are destinations, locate on directly attached
-   link and UNIVERSE is everywhere in the Universe :-)
+   local addresses, LINK are destinations, located on directly attached
+   link and UNIVERSE is everywhere in the Universe.
 
    Intermediate values are also possible f.e. interior routes
    could be assigned a value between UNIVERSE and LINK.
@@ -208,7 +164,7 @@
 enum rt_scope_t
 {
 	RT_SCOPE_UNIVERSE=0,
-/* User defined values f.e. "site" */
+/* User defined values  */
 	RT_SCOPE_SITE=200,
 	RT_SCOPE_LINK=253,
 	RT_SCOPE_HOST=254,
@@ -219,8 +175,10 @@
 
 #define RTM_F_NOTIFY		0x100	/* Notify user of route change	*/
 #define RTM_F_CLONED		0x200	/* This route is cloned		*/
-#define RTM_F_NOPMTUDISC	0x400	/* Do not make PMTU discovery	*/
-#define RTM_F_EQUALIZE		0x800	/* Multipath equalizer: NI	*/
+#define RTM_F_EQUALIZE		0x400	/* Multipath equalizer: NI	*/
+#ifdef CONFIG_RTNL_OLD_IFINFO
+#define RTM_F_NOPMTUDISC	0x800	/* Do not make PMTU discovery	*/
+#endif
 
 /* Reserved table identifiers */
 
@@ -235,6 +193,107 @@
 #define RT_TABLE_MAX RT_TABLE_LOCAL
 
 
+
+/* Routing message attributes */
+
+enum rtattr_type_t
+{
+	RTA_UNSPEC,
+	RTA_DST,
+	RTA_SRC,
+	RTA_IIF,
+	RTA_OIF,
+	RTA_GATEWAY,
+	RTA_PRIORITY,
+	RTA_PREFSRC,
+#ifndef CONFIG_RTNL_OLD_IFINFO
+	RTA_METRICS,
+	RTA_MULTIPATH,
+	RTA_PROTOINFO,
+	RTA_FLOW,
+#else
+	RTA_WINDOW,
+	RTA_RTT,
+	RTA_MTU,
+	RTA_IFNAME,
+#endif
+	RTA_CACHEINFO
+};
+
+#define RTA_MAX RTA_CACHEINFO
+
+#define RTM_RTA(r)  ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct rtmsg))))
+#define RTM_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct rtmsg))
+
+/* RTM_MULTIPATH --- array of struct rtnexthop.
+ *
+ * "struct rtnexthop" describres all necessary nexthop information,
+ * i.e. parameters of path to a destination via this nextop.
+ *
+ * At the moment it is impossible to set different prefsrc, mtu, window
+ * and rtt for different paths from multipath.
+ */
+
+struct rtnexthop
+{
+	unsigned short		rtnh_len;
+	unsigned char		rtnh_flags;
+	unsigned char		rtnh_hops;
+	int			rtnh_ifindex;
+};
+
+/* rtnh_flags */
+
+#define RTNH_F_DEAD		1	/* Nexthop is dead (used by multipath)	*/
+#define RTNH_F_PERVASIVE	2	/* Do recursive gateway lookup	*/
+#define RTNH_F_ONLINK		4	/* Gateway is forced on link	*/
+
+/* Macros to handle hexthops */
+
+#define RTNH_ALIGNTO	4
+#define RTNH_ALIGN(len) ( ((len)+RTNH_ALIGNTO-1) & ~(RTNH_ALIGNTO-1) )
+#define RTNH_OK(rtnh,len) ((rtnh)->rtnh_len >= sizeof(struct rtnexthop) && \
+			   (rtnh)->rtnh_len <= (len))
+#define RTNH_NEXT(rtnh)	((struct rtnexthop*)(((char*)(rtnh)) + RTNH_ALIGN((rtnh)->rtnh_len)))
+#define RTNH_LENGTH(len) (RTNH_ALIGN(sizeof(struct rtnexthop)) + (len))
+#define RTNH_SPACE(len)	RTNH_ALIGN(RTNH_LENGTH(len))
+#define RTNH_DATA(rtnh)   ((struct rtattr*)(((char*)(rtnh)) + RTNH_LENGTH(0)))
+
+#ifdef CONFIG_RTNL_OLD_IFINFO
+#define RTM_RTNH(r) ((struct rtnexthop*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct rtmsg)) \
+					   + NLMSG_ALIGN((r)->rtm_optlen)))
+#define RTM_NHLEN(nlh,r) ((nlh)->nlmsg_len - NLMSG_SPACE(sizeof(struct rtmsg)) - NLMSG_ALIGN((r)->rtm_optlen))
+#endif
+
+/* RTM_CACHEINFO */
+
+struct rta_cacheinfo
+{
+	__u32	rta_clntref;
+	__u32	rta_lastuse;
+	__s32	rta_expires;
+	__u32	rta_error;
+	__u32	rta_used;
+};
+
+/* RTM_METRICS --- array of struct rtattr with types of RTAX_* */
+
+enum
+{
+	RTAX_UNSPEC,
+	RTAX_LOCK,
+	RTAX_MTU,
+	RTAX_WINDOW,
+	RTAX_RTT,
+	RTAX_HOPS,
+	RTAX_SSTHRESH,
+	RTAX_CWND,
+};
+
+#define RTAX_MAX RTAX_CWND
+
+
+
 /*********************************************************
  *		Interface address.
  ****/
@@ -277,6 +336,7 @@
 
 
 #define IFA_RTA(r)  ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifaddrmsg))))
+#define IFA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ifaddrmsg))
 
 /*
    Important comment:
@@ -293,6 +353,8 @@
 struct ndmsg
 {
 	unsigned char	ndm_family;
+	unsigned char	ndm_pad1;
+	unsigned short	ndm_pad2;
 	int		ndm_ifindex;	/* Link index			*/
 	__u16		ndm_state;
 	__u8		ndm_flags;
@@ -310,6 +372,31 @@
 #define NDA_MAX NDA_CACHEINFO
 
 #define NDA_RTA(r)  ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ndmsg))))
+#define NDA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ndmsg))
+
+/*
+ *	Neighbor Cache Entry Flags
+ */
+
+#define NTF_PROXY	0x08	/* == ATF_PUBL */
+#define NTF_ROUTER	0x80
+
+/*
+ *	Neighbor Cache Entry States.
+ */
+
+#define NUD_INCOMPLETE	0x01
+#define NUD_REACHABLE	0x02
+#define NUD_STALE	0x04
+#define NUD_DELAY	0x08
+#define NUD_PROBE	0x10
+#define NUD_FAILED	0x20
+
+/* Dummy states */
+#define NUD_NOARP	0x40
+#define NUD_PERMANENT	0x80
+#define NUD_NONE	0x00
+
 
 struct nda_cacheinfo
 {
@@ -337,6 +424,7 @@
  * on network protocol.
  */
 
+#ifdef CONFIG_RTNL_OLD_IFINFO
 struct ifinfomsg
 {
 	unsigned char	ifi_family;		/* Dummy	*/
@@ -363,9 +451,37 @@
 	IFLA_STATS
 };
 
+#else
+
+struct ifinfomsg
+{
+	unsigned char	ifi_family;
+	unsigned char	__ifi_pad;
+	unsigned short	ifi_type;		/* ARPHRD_* */
+	int		ifi_index;		/* Link index	*/
+	unsigned	ifi_flags;		/* IFF_* flags	*/
+	unsigned	ifi_change;		/* IFF_* change mask */
+};
+
+enum
+{
+	IFLA_UNSPEC,
+	IFLA_ADDRESS,
+	IFLA_BROADCAST,
+	IFLA_IFNAME,
+	IFLA_MTU,
+	IFLA_LINK,
+	IFLA_QDISC,
+	IFLA_STATS
+};
+
+#endif
+
+
 #define IFLA_MAX IFLA_STATS
 
 #define IFLA_RTA(r)  ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifinfomsg))))
+#define IFLA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ifinfomsg))
 
 /* ifi_flags.
 
@@ -396,6 +512,42 @@
    for IPIP tunnels, when route to endpoint is allowed to change)
  */
 
+/*****************************************************************
+ *		Traffic control messages.
+ ****/
+
+struct tcmsg
+{
+	unsigned char	tcm_family;
+	unsigned char	tcm__pad1;
+	unsigned short	tcm__pad2;
+	int		tcm_ifindex;
+	__u32		tcm_handle;
+	__u32		tcm_parent;
+	__u32		tcm_info;
+};
+
+enum
+{
+	TCA_UNSPEC,
+	TCA_KIND,
+	TCA_OPTIONS,
+	TCA_STATS,
+	TCA_XSTATS
+};
+
+#define TCA_MAX TCA_XSTATS
+
+#define TCA_RTA(r)  ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct tcmsg))))
+#define TCA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct tcmsg))
+
+
+/* SUMMARY: maximal rtattr understood by kernel */
+
+#define RTATTR_MAX		RTA_MAX
+
+/* RTnetlink multicast groups */
+
 #define RTMGRP_LINK		1
 #define RTMGRP_NOTIFY		2
 #define RTMGRP_NEIGH		4
@@ -408,34 +560,9 @@
 #define RTMGRP_IPV6_MROUTE	0x200
 #define RTMGRP_IPV6_ROUTE	0x400
 
+/* End of information exported to user level */
 
 #ifdef __KERNEL__
-
-struct kern_rta
-{
-	void		*rta_dst;
-	void		*rta_src;
-	int		*rta_iif;
-	int		*rta_oif;
-	void		*rta_gw;
-	u32		*rta_priority;
-	void		*rta_prefsrc;
-	unsigned	*rta_window;
-	unsigned	*rta_rtt;
-	unsigned	*rta_mtu;
-	unsigned char	*rta_ifname;
-	struct rta_cacheinfo *rta_ci;
-};
-
-struct kern_ifa
-{
-	void		*ifa_address;
-	void		*ifa_local;
-	unsigned char	*ifa_label;
-	void		*ifa_broadcast;
-	void		*ifa_anycast;
-};
-
 
 extern atomic_t rtnl_rlockct;
 extern struct wait_queue *rtnl_wait;
diff -u --recursive --new-file v2.1.88/linux/include/linux/sdla_x25.h linux/include/linux/sdla_x25.h
--- v2.1.88/linux/include/linux/sdla_x25.h	Mon Jan 12 14:46:26 1998
+++ linux/include/linux/sdla_x25.h	Tue Feb 24 22:08:02 1998
@@ -159,7 +159,7 @@
 #define X25RES_PROTO_VIOLATION	0x41	/* protocol violation occured */
 #define X25RES_PKT_TIMEOUT	0x42	/* X.25 packet time out */
 #define X25RES_PKT_RETRY_LIMIT	0x43	/* X.25 packet retry limit exceeded */
-/*----- Command-dependant results -----*/
+/*----- Command-dependent results -----*/
 #define X25RES_LINK_DISC	0x00	/* HDLC_LINK_STATUS */
 #define X25RES_LINK_IN_ABM	0x01	/* HDLC_LINK_STATUS */
 #define X25RES_NO_DATA		0x01	/* HDLC_READ/READ_TRACE_DATA*/
diff -u --recursive --new-file v2.1.88/linux/include/linux/skbuff.h linux/include/linux/skbuff.h
--- v2.1.88/linux/include/linux/skbuff.h	Thu Feb 12 20:56:13 1998
+++ linux/include/linux/skbuff.h	Fri Mar  6 21:02:21 1998
@@ -538,7 +538,7 @@
 }
 
 extern struct sk_buff *		skb_recv_datagram(struct sock *sk,unsigned flags,int noblock, int *err);
-extern unsigned int		datagram_poll(struct socket *sock, struct poll_table_struct *wait);
+extern unsigned int		datagram_poll(struct file *file, struct socket *sock, struct poll_table_struct *wait);
 extern int			skb_copy_datagram(struct sk_buff *from, int offset, char *to,int size);
 extern int			skb_copy_datagram_iovec(struct sk_buff *from, int offset, struct iovec *to,int size);
 extern void			skb_free_datagram(struct sock * sk, struct sk_buff *skb);
diff -u --recursive --new-file v2.1.88/linux/include/linux/swap.h linux/include/linux/swap.h
--- v2.1.88/linux/include/linux/swap.h	Mon Feb 23 18:12:12 1998
+++ linux/include/linux/swap.h	Mon Feb 23 15:24:32 1998
@@ -9,6 +9,8 @@
 
 #ifdef __KERNEL__
 
+#undef DEBUG_SWAP
+
 #include <asm/atomic.h>
 
 #define SWP_USED	1
@@ -21,7 +23,6 @@
 	kdev_t swap_device;
 	struct dentry * swap_file;
 	unsigned char * swap_map;
-	unsigned char * swap_lockmap;
 	unsigned int lowest_bit;
 	unsigned int highest_bit;
 	unsigned int cluster_next;
@@ -53,11 +54,7 @@
 
 /* linux/mm/page_io.c */
 extern void rw_swap_page(int, unsigned long, char *, int);
-#define read_swap_page(nr,buf) \
-	rw_swap_page(READ,(nr),(buf),1)
-#define write_swap_page(nr,buf) \
-	rw_swap_page(WRITE,(nr),(buf),1)
-extern void swap_after_unlock_page (unsigned long entry);
+extern void rw_swap_page_nocache(int, unsigned long, char *);
 
 /* linux/mm/page_alloc.c */
 extern void swap_in(struct task_struct *, struct vm_area_struct *,
@@ -68,6 +65,8 @@
 extern void show_swap_cache_info(void);
 extern int add_to_swap_cache(struct page *, unsigned long);
 extern void swap_duplicate(unsigned long);
+extern struct page * read_swap_cache_async(unsigned long, int);
+#define read_swap_cache(entry) read_swap_cache_async(entry, 1);
 
 /* linux/mm/swapfile.c */
 extern unsigned int nr_swapfiles;
@@ -107,12 +106,18 @@
 
 /*
  * Work out if there are any other processes sharing this page, ignoring
- * any page reference coming from the page or swap cache.  
+ * any page reference coming from the swap cache, or from outstanding
+ * swap IO on this page.  (The page cache _does_ count as another valid
+ * reference to the page, however.)
  */
 static inline int is_page_shared(struct page *page)
 {
 	int count = atomic_read(&page->count);
-	if (page->inode)
+	if (PageReserved(page))
+		return 1;
+	if (page->inode == &swapper_inode)
+		count--;
+	if (PageFreeAfter(page))
 		count--;
 	return (count > 1);
 }
diff -u --recursive --new-file v2.1.88/linux/include/linux/sysctl.h linux/include/linux/sysctl.h
--- v2.1.88/linux/include/linux/sysctl.h	Mon Jan 12 15:28:19 1998
+++ linux/include/linux/sysctl.h	Sun Mar  1 14:40:40 1998
@@ -116,6 +116,8 @@
 	NET_CORE_DESTROY_DELAY,
 	NET_CORE_MAX_BACKLOG,
 	NET_CORE_FASTROUTE,
+	NET_CORE_MSG_COST,
+	NET_CORE_MSG_BURST,
 };
 
 /* /proc/sys/net/ethernet */
@@ -133,26 +135,24 @@
 /* /proc/sys/net/ipv4 */
 enum
 {
-	NET_IPV4_TCP_HOE_RETRANSMITS=1,
+	/* v2.0 compatibile variables */
+	NET_IPV4_FORWARD = 8,
+	NET_IPV4_DYNADDR = 9,
+
+	NET_IPV4_CONF = 16,
+	NET_IPV4_NEIGH = 17,
+	NET_IPV4_ROUTE = 18,
+	NET_IPV4_FIB_HASH = 19,
+
+	NET_IPV4_TCP_HOE_RETRANSMITS=32,
 	NET_IPV4_TCP_SACK,
 	NET_IPV4_TCP_TSACK,
 	NET_IPV4_TCP_TIMESTAMPS,
 	NET_IPV4_TCP_WINDOW_SCALING,
 	NET_IPV4_TCP_VEGAS_CONG_AVOID,
-	NET_IPV4_FORWARDING,
 	NET_IPV4_DEFAULT_TTL,
-	NET_IPV4_RFC1812_FILTER,
-	NET_IPV4_LOG_MARTIANS,
-	NET_IPV4_SOURCE_ROUTE,
-	NET_IPV4_SEND_REDIRECTS,
 	NET_IPV4_AUTOCONFIG,
-	NET_IPV4_BOOTP_RELAY,
-	NET_IPV4_PROXY_ARP,
 	NET_IPV4_NO_PMTU_DISC,
-	NET_IPV4_ACCEPT_REDIRECTS,
-	NET_IPV4_SECURE_REDIRECTS,
-	NET_IPV4_RFC1620_REDIRECTS,
-	NET_IPV4_RTCACHE_FLUSH,
 	NET_IPV4_TCP_SYN_RETRIES,
 	NET_IPV4_IPFRAG_HIGH_THRESH,
 	NET_IPV4_IPFRAG_LOW_THRESH,
@@ -164,10 +164,6 @@
 	NET_IPV4_TCP_RETRIES2,
 	NET_IPV4_TCP_MAX_DELAY_ACKS,
 	NET_IPV4_TCP_FIN_TIMEOUT,
-	NET_IPV4_IGMP_MAX_HOST_REPORT_DELAY,
-	NET_IPV4_IGMP_TIMER_SCALE,
-	NET_IPV4_IGMP_AGE_THRESHOLD,
-	NET_IPV4_IP_DYNADDR,
 	NET_IPV4_IP_MASQ_DEBUG,
 	NET_TCP_SYNCOOKIES,
 	NET_TCP_STDURG,
@@ -181,26 +177,74 @@
 	NET_IPV4_ICMP_TIMEEXCEED_RATE,
 	NET_IPV4_ICMP_PARAMPROB_RATE,
 	NET_IPV4_ICMP_ECHOREPLY_RATE,
-	NET_IPV4_NEIGH,
 };
 
+enum {
+	NET_IPV4_ROUTE_FLUSH = 1,
+	NET_IPV4_ROUTE_MIN_DELAY,
+	NET_IPV4_ROUTE_MAX_DELAY,
+	NET_IPV4_ROUTE_GC_THRESH,
+	NET_IPV4_ROUTE_MAX_SIZE,
+	NET_IPV4_ROUTE_GC_MIN_INTERVAL,
+	NET_IPV4_ROUTE_GC_TIMEOUT,
+	NET_IPV4_ROUTE_GC_INTERVAL,
+	NET_IPV4_ROUTE_REDIRECT_LOAD,
+	NET_IPV4_ROUTE_REDIRECT_NUMBER,
+	NET_IPV4_ROUTE_REDIRECT_SILENCE,
+	NET_IPV4_ROUTE_ERROR_COST,
+	NET_IPV4_ROUTE_ERROR_BURST,
+};
+
+enum
+{
+	NET_PROTO_CONF_ALL = -2,
+	NET_PROTO_CONF_DEFAULT = -3,
+
+	/* And device ifindices ... */
+};
+
+enum
+{
+	NET_IPV4_CONF_FORWARDING = 1,
+	NET_IPV4_CONF_MC_FORWARDING,
+	NET_IPV4_CONF_PROXY_ARP,
+	NET_IPV4_CONF_ACCEPT_REDIRECTS,
+	NET_IPV4_CONF_SECURE_REDIRECTS,
+	NET_IPV4_CONF_SEND_REDIRECTS,
+	NET_IPV4_CONF_SHARED_MEDIA,
+	NET_IPV4_CONF_RP_FILTER,
+	NET_IPV4_CONF_ACCEPT_SOURCE_ROUTE,
+	NET_IPV4_CONF_BOOTP_RELAY,
+	NET_IPV4_CONF_LOG_MARTIANS,
+};
 
 /* /proc/sys/net/ipv6 */
 enum {
-	NET_IPV6_FORWARDING = 1,
-	NET_IPV6_HOPLIMIT,
+	NET_IPV6_CONF = 16,
+	NET_IPV6_NEIGH = 17,
+	NET_IPV6_ROUTE = 18,
+};
+
+enum {
+	NET_IPV6_ROUTE_FLUSH = 1,
+	NET_IPV6_ROUTE_GC_THRESH,
+	NET_IPV6_ROUTE_MAX_SIZE,
+	NET_IPV6_ROUTE_GC_MIN_INTERVAL,
+	NET_IPV6_ROUTE_GC_TIMEOUT,
+	NET_IPV6_ROUTE_GC_INTERVAL,
+};
 
+enum {
+	NET_IPV6_FORWARDING = 1,
+	NET_IPV6_HOP_LIMIT,
+	NET_IPV6_MTU,
 	NET_IPV6_ACCEPT_RA,
 	NET_IPV6_ACCEPT_REDIRECTS,
-
 	NET_IPV6_AUTOCONF,
 	NET_IPV6_DAD_TRANSMITS,
 	NET_IPV6_RTR_SOLICITS,
 	NET_IPV6_RTR_SOLICIT_INTERVAL,
 	NET_IPV6_RTR_SOLICIT_DELAY,
-
-	NET_IPV6_ICMPV6_TIME,
-	NET_IPV6_NEIGH,
 };
 
 /* /proc/sys/net/<protocol>/neigh/<dev> */
diff -u --recursive --new-file v2.1.88/linux/include/linux/ufs_fs.h linux/include/linux/ufs_fs.h
--- v2.1.88/linux/include/linux/ufs_fs.h	Tue Feb 17 13:12:49 1998
+++ linux/include/linux/ufs_fs.h	Sat Feb 28 13:14:45 1998
@@ -294,12 +294,12 @@
 		} ui_addr;
 		__u8	ui_symlink[4*(UFS_NDADDR+UFS_NINDIR)];/* 0x28 fast symlink */
 	} ui_u2;
-	__u32	ui_flags;		/* 0x64 unused -- "status flags (chflags)" ??? */
+	__u32	ui_flags;		/* 0x64 immutable, append-only... */
 	__u32	ui_blocks;		/* 0x68 blocks in use */
-	__u32	ui_gen;			/* 0x6c generation number XXX - what is this? */
+	__u32	ui_gen;			/* 0x6c like ext2 i_version, for NFS support */
 	union {
 		struct {
-			__u32	ui_shadow;/* 0x70 shadow inode XXX - what is this?*/
+			__u32	ui_shadow;/* 0x70 shadow inode with security data */
 			__u32	ui_uid;	/* 0x74 long EFT version of uid */
 			__u32	ui_gid;	/* 0x78 long EFT version of gid */
 			__u32	ui_oeftflag;/* 0x7c reserved */
@@ -318,6 +318,22 @@
 		} ui_hurd;
 	} ui_u3;
 };
+
+/* FreeBSD has these in sys/stat.h */
+/* ui_flags that can be set by a file owner */
+#define UFS_UF_SETTABLE   0x0000ffff
+#define UFS_UF_NODUMP     0x00000001  /* do not dump */
+#define UFS_UF_IMMUTABLE  0x00000002  /* immutable (can't "change") */
+#define UFS_UF_APPEND     0x00000004  /* append-only */
+#define UFS_UF_OPAQUE     0x00000008  /* directory is opaque (unionfs) */
+#define UFS_UF_NOUNLINK   0x00000010  /* can't be removed or renamed */
+/* ui_flags that only root can set */
+#define UFS_SF_SETTABLE   0xffff0000
+#define UFS_SF_ARCHIVED   0x00010000  /* archived */
+#define UFS_SF_IMMUTABLE  0x00020000  /* immutable (can't "change") */
+#define UFS_SF_APPEND     0x00040000  /* append-only */
+#define UFS_SF_NOUNLINK   0x00100000  /* can't be removed or renamed */
+    
  
 #ifdef __KERNEL__
 /*
diff -u --recursive --new-file v2.1.88/linux/include/linux/wireless.h linux/include/linux/wireless.h
--- v2.1.88/linux/include/linux/wireless.h	Fri Feb  6 15:34:56 1998
+++ linux/include/linux/wireless.h	Tue Feb 24 22:08:02 1998
@@ -213,7 +213,7 @@
 struct	iw_statistics
 {
 	__u8		status;		/* Status
-					 * - device dependant for now */
+					 * - device dependent for now */
 
 	struct iw_quality	qual;		/* Quality of the link
 						 * (instant/mean/max) */
diff -u --recursive --new-file v2.1.88/linux/include/net/addrconf.h linux/include/net/addrconf.h
--- v2.1.88/linux/include/net/addrconf.h	Mon Jan 12 15:28:19 1998
+++ linux/include/net/addrconf.h	Sun Mar  1 14:40:40 1998
@@ -148,6 +148,10 @@
 		      __constant_htonl(0x2));
 }
 
+extern __inline__ int ipv6_addr_is_multicast(struct in6_addr *addr)
+{
+	return (addr->s6_addr32[0] & __constant_htonl(0xFF000000)) == __constant_htonl(0xFF000000);
+}
 
 #endif
 #endif
diff -u --recursive --new-file v2.1.88/linux/include/net/arp.h linux/include/net/arp.h
--- v2.1.88/linux/include/net/arp.h	Fri Feb  6 15:34:56 1998
+++ linux/include/net/arp.h	Fri Mar  6 21:05:25 1998
@@ -18,4 +18,7 @@
 extern int	arp_bind_neighbour(struct dst_entry *dst);
 extern int	arp_mc_map(u32 addr, u8 *haddr, struct device *dev, int dir);
 extern void	arp_ifdown(struct device *dev);
+
+extern struct neigh_ops arp_broken_ops;
+
 #endif	/* _ARP_H */
diff -u --recursive --new-file v2.1.88/linux/include/net/dst.h linux/include/net/dst.h
--- v2.1.88/linux/include/net/dst.h	Fri Feb  6 15:34:56 1998
+++ linux/include/net/dst.h	Fri Mar  6 21:05:24 1998
@@ -36,6 +36,7 @@
 	int			obsolete;
 	__u32			priority;
 	unsigned long		lastuse;
+	unsigned		mxlock;
 	unsigned		window;
 	unsigned		pmtu;
 	unsigned		rtt;
@@ -60,11 +61,17 @@
 {
 	unsigned short		family;
 	unsigned short		protocol;
+	unsigned		gc_thresh;
+
+	int			(*gc)(void);
 	struct dst_entry *	(*check)(struct dst_entry *, __u32 cookie);
 	struct dst_entry *	(*reroute)(struct dst_entry *,
 					   struct sk_buff *);
 	void			(*destroy)(struct dst_entry *);
 	struct dst_entry *	(*negative_advice)(struct dst_entry *);
+	void			(*link_failure)(struct sk_buff *);
+
+	atomic_t		entries;
 };
 
 #ifdef __KERNEL__
@@ -131,6 +138,13 @@
 	struct dst_entry * dst = *dst_p;
 	if (dst && dst->ops->negative_advice)
 		*dst_p = dst->ops->negative_advice(dst);
+}
+
+extern __inline__ void dst_link_failure(struct sk_buff *skb)
+{
+	struct dst_entry * dst = skb->dst;
+	if (dst && dst->ops && dst->ops->link_failure)
+		dst->ops->link_failure(skb);
 }
 #endif
 
diff -u --recursive --new-file v2.1.88/linux/include/net/if_inet6.h linux/include/net/if_inet6.h
--- v2.1.88/linux/include/net/if_inet6.h	Mon Jan 12 15:28:19 1998
+++ linux/include/net/if_inet6.h	Sun Mar  1 14:40:40 1998
@@ -86,21 +86,36 @@
 #define	IFA_SITE	IPV6_ADDR_SITELOCAL
 #define	IFA_GLOBAL	0x0000U
 
+struct ipv6_devconf
+{
+	int		forwarding;
+	int		hop_limit;
+	int		mtu6;
+	int		accept_ra;
+	int		accept_redirects;
+	int		autoconf;
+	int		dad_transmits;
+	int		rtr_solicits;
+	int		rtr_solicit_interval;
+	int		rtr_solicit_delay;
+
+	void		*sysctl;
+};
+
 struct inet6_dev 
 {
 	struct device		*dev;
 
 	struct inet6_ifaddr	*addr_list;
 	struct ifmcaddr6	*mc_list;
-
 	__u32			if_flags;
-	__u32			router:1,
-				unused:31;
 
 	struct neigh_parms	*nd_parms;
 	struct inet6_dev	*next;
+	struct ipv6_devconf	cnf;
 };
 
+extern struct ipv6_devconf ipv6_devconf;
 
 extern __inline__ void ipv6_eth_mc_map(struct in6_addr *addr, char *buf)
 {
diff -u --recursive --new-file v2.1.88/linux/include/net/inet_common.h linux/include/net/inet_common.h
--- v2.1.88/linux/include/net/inet_common.h	Sun Nov 30 14:00:39 1997
+++ linux/include/net/inet_common.h	Thu Feb 26 20:17:58 1998
@@ -28,7 +28,7 @@
 					     struct msghdr *msg, 
 					     int size, struct scm_cookie *scm);
 extern int			inet_shutdown(struct socket *sock, int how);
-extern unsigned int		inet_poll(struct socket *sock, struct poll_table_struct *wait);
+extern unsigned int		inet_poll(struct file * file, struct socket *sock, struct poll_table_struct *wait);
 extern int			inet_setsockopt(struct socket *sock, int level,
 						int optname, char *optval, 
 						int optlen);
diff -u --recursive --new-file v2.1.88/linux/include/net/ip.h linux/include/net/ip.h
--- v2.1.88/linux/include/net/ip.h	Thu Feb 12 20:56:13 1998
+++ linux/include/net/ip.h	Fri Mar  6 21:05:26 1998
@@ -11,6 +11,9 @@
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
  *		Alan Cox, <gw4pts@gw4pts.ampr.org>
  *
+ * Changes:
+ *		Mike McLagan    :       Routing by source
+ *
  *		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
@@ -117,29 +120,17 @@
 
 extern int __ip_finish_output(struct sk_buff *skb);
 
-
-extern struct ip_mib	ip_statistics;
-
 struct ipv4_config
 {
-	int	accept_redirects;
-	int	secure_redirects;
-	int	rfc1620_redirects;
-	int	rfc1812_filter;
-	int	send_redirects;
 	int	log_martians;
-	int	source_route;
-	int	multicast_route;
-	int	proxy_arp;
-	int	bootp_relay;
 	int	autoconfig;
 	int	no_pmtu_disc;
 };
 
 extern struct ipv4_config ipv4_config;
-extern int sysctl_local_port_range[2];
+extern struct ip_mib	ip_statistics;
 
-#define IS_ROUTER	(ip_statistics.IpForwarding == 1)
+extern int sysctl_local_port_range[2];
 
 extern __inline__ int ip_finish_output(struct sk_buff *skb)
 {
diff -u --recursive --new-file v2.1.88/linux/include/net/ip6_fib.h linux/include/net/ip6_fib.h
--- v2.1.88/linux/include/net/ip6_fib.h	Mon Jan 12 15:28:19 1998
+++ linux/include/net/ip6_fib.h	Sun Mar  1 14:40:40 1998
@@ -69,6 +69,7 @@
 
 	u32				rt6i_flags;
 	u32				rt6i_metric;
+	u8				rt6i_hoplimit;
 	unsigned long			rt6i_expires;
 
 	union {
@@ -139,6 +140,8 @@
 extern int			fib6_del(struct rt6_info *rt);
 
 extern void			inet6_rt_notify(int event, struct rt6_info *rt);
+
+extern	void			fib6_run_gc(unsigned long dummy);
 
 #endif
 #endif
diff -u --recursive --new-file v2.1.88/linux/include/net/ip6_route.h linux/include/net/ip6_route.h
--- v2.1.88/linux/include/net/ip6_route.h	Mon Jan 12 15:28:19 1998
+++ linux/include/net/ip6_route.h	Sun Mar  1 14:40:40 1998
@@ -38,6 +38,11 @@
 
 extern struct rt6_info	ip6_null_entry;
 
+extern int ip6_rt_max_size;
+extern int ip6_rt_gc_min;
+extern int ip6_rt_gc_timeout;
+extern int ip6_rt_gc_interval;
+
 extern void			ip6_route_input(struct sk_buff *skb);
 
 extern struct dst_entry *	ip6_route_output(struct sock *sk,
diff -u --recursive --new-file v2.1.88/linux/include/net/ip_fib.h linux/include/net/ip_fib.h
--- v2.1.88/linux/include/net/ip_fib.h	Mon Jan 12 15:28:19 1998
+++ linux/include/net/ip_fib.h	Sun Mar  1 14:40:40 1998
@@ -18,6 +18,29 @@
 
 #include <linux/config.h>
 
+struct kern_rta
+{
+	void		*rta_dst;
+	void		*rta_src;
+	int		*rta_iif;
+	int		*rta_oif;
+	void		*rta_gw;
+	u32		*rta_priority;
+	void		*rta_prefsrc;
+#ifdef CONFIG_RTNL_OLD_IFINFO
+	unsigned	*rta_window;
+	unsigned	*rta_rtt;
+	unsigned	*rta_mtu;
+	unsigned char	*rta_ifname;
+#else
+	struct rtattr	*rta_mx;
+	struct rtattr	*rta_mp;
+	unsigned char	*rta_protoinfo;
+	unsigned char	*rta_flow;
+#endif
+	struct rta_cacheinfo *rta_ci;
+};
+
 struct fib_nh
 {
 	struct device		*nh_dev;
@@ -43,9 +66,17 @@
 	unsigned		fib_flags;
 	int			fib_protocol;
 	u32			fib_prefsrc;
+#ifdef CONFIG_RTNL_OLD_IFINFO
 	unsigned		fib_mtu;
 	unsigned		fib_rtt;
 	unsigned		fib_window;
+#else
+#define FIB_MAX_METRICS RTAX_RTT
+	unsigned		fib_metrics[FIB_MAX_METRICS];
+#define fib_mtu fib_metrics[RTAX_MTU-1]
+#define fib_window fib_metrics[RTAX_WINDOW-1]
+#define fib_rtt fib_metrics[RTAX_RTT-1]
+#endif
 	int			fib_nhs;
 #ifdef CONFIG_IP_ROUTE_MULTIPATH
 	int			fib_power;
diff -u --recursive --new-file v2.1.88/linux/include/net/ipv6.h linux/include/net/ipv6.h
--- v2.1.88/linux/include/net/ipv6.h	Fri Feb  6 15:35:04 1998
+++ linux/include/net/ipv6.h	Fri Mar  6 21:05:36 1998
@@ -88,24 +88,6 @@
 
 extern struct ipv6_mib	ipv6_statistics;
 
-struct ipv6_config {
-	int		forwarding;
-	int		hop_limit;
-	int		accept_ra;
-	int		accept_redirects;
-	
-	int		autoconf;
-	int		dad_transmits;
-	int		rtr_solicits;
-	int		rtr_solicit_interval;
-	int		rtr_solicit_delay;
-
-	int		rt_cache_timeout;
-	int		rt_gc_period;
-};
-
-extern struct ipv6_config ipv6_config;
-
 struct ipv6_frag {
 	__u16			offset;
 	__u16			len;
diff -u --recursive --new-file v2.1.88/linux/include/net/neighbour.h linux/include/net/neighbour.h
--- v2.1.88/linux/include/net/neighbour.h	Fri Feb  6 15:34:56 1998
+++ linux/include/net/neighbour.h	Fri Mar  6 21:05:24 1998
@@ -53,7 +53,13 @@
 struct neigh_parms
 {
 	struct neigh_parms *next;
+	int	(*neigh_setup)(struct neighbour *);
+	struct neigh_table *tbl;
+	int	entries;
+	void	*priv;
+
 	void	*sysctl_table;
+
 	int	base_reachable_time;
 	int	retrans_time;
 	int	gc_staletime;
@@ -173,7 +179,7 @@
 						u8 *lladdr, void *saddr,
 						struct device *dev);
 
-extern struct neigh_parms	*neigh_parms_alloc(struct neigh_table *tbl);
+extern struct neigh_parms	*neigh_parms_alloc(struct device *dev, struct neigh_table *tbl);
 extern void			neigh_parms_release(struct neigh_table *tbl, struct neigh_parms *parms);
 extern unsigned long		neigh_rand_reach_time(unsigned long base);
 
@@ -189,8 +195,8 @@
 extern int neigh_delete(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg);
 extern void neigh_app_ns(struct neighbour *n);
 
-extern void 			*neigh_sysctl_register(struct device *dev, struct neigh_parms *p,
-						       int p_id, int pdev_id, char *p_name);
+extern int			neigh_sysctl_register(struct device *dev, struct neigh_parms *p,
+						      int p_id, int pdev_id, char *p_name);
 extern void			neigh_sysctl_unregister(struct neigh_parms *p);
 
 /*
@@ -249,12 +255,12 @@
 	return 0;
 }
 
-extern __inline__ int neigh_table_lock(struct neigh_table *tbl)
+extern __inline__ void neigh_table_lock(struct neigh_table *tbl)
 {
 	atomic_inc(&tbl->lock);
 }
 
-extern __inline__ int neigh_table_unlock(struct neigh_table *tbl)
+extern __inline__ void neigh_table_unlock(struct neigh_table *tbl)
 {
 	atomic_dec(&tbl->lock);
 }
diff -u --recursive --new-file v2.1.88/linux/include/net/route.h linux/include/net/route.h
--- v2.1.88/linux/include/net/route.h	Mon Feb 23 18:12:12 1998
+++ linux/include/net/route.h	Fri Mar  6 21:05:25 1998
@@ -13,6 +13,7 @@
  *		Alan Cox	:	Reformatted. Added ip_rt_local()
  *		Alan Cox	:	Support for TCP parameters.
  *		Alexey Kuznetsov:	Major changes for new routing code.
+ *		Mike McLagan    :	Routing by source
  *
  *		This program is free software; you can redistribute it and/or
  *		modify it under the terms of the GNU General Public License
@@ -28,30 +29,6 @@
 #include <linux/rtnetlink.h>
 
 #define RT_HASH_DIVISOR	    	256
-#define RT_CACHE_MAX_SIZE    	256
-
-/*
- * Maximal time to live for unused entry.
- */
-#define RT_CACHE_TIMEOUT		(HZ*300)
-
-/*
- * Periodic timer frequency
- */
-#define RT_GC_INTERVAL			(HZ*60)
-
-/*
- * Cache invalidations can be delayed by:
- */
-#define RT_FLUSH_DELAY (5*HZ)
-
-#define RT_REDIRECT_NUMBER		9
-#define RT_REDIRECT_LOAD		(HZ/50)	/* 20 msec */
-#define RT_REDIRECT_SILENCE		(RT_REDIRECT_LOAD<<(RT_REDIRECT_NUMBER+1))
-/* 20sec */
-
-#define RT_ERROR_LOAD			(1*HZ)
-
 
 /*
  * Prevents LRU trashing, entries considered equivalent,
@@ -64,6 +41,12 @@
 #define RTO_ONLINK	0x01
 #define RTO_TPROXY	0x80000000
 
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
+#define RTO_CONN	RTO_TPROXY
+#else
+#define RTO_CONN	0
+#endif
+
 struct rt_key
 {
 	__u32			dst;
@@ -102,10 +85,6 @@
 	__u32			rt_src_map;
 	__u32			rt_dst_map;
 #endif
-
-	/* ICMP statistics */
-	unsigned long		last_error;
-	unsigned long		errors;
 };
 
 #ifdef __KERNEL__
diff -u --recursive --new-file v2.1.88/linux/include/net/scm.h linux/include/net/scm.h
--- v2.1.88/linux/include/net/scm.h	Sun Jan 19 05:47:27 1997
+++ linux/include/net/scm.h	Mon Mar  2 12:02:26 1998
@@ -17,6 +17,7 @@
 	struct ucred		creds;		/* Skb credentials	*/
 	struct scm_fp_list	*fp;		/* Passed files		*/
 	unsigned long		seq;		/* Connection seqno	*/
+	struct file 		*file;		/* file for socket	*/
 	struct socket 		*sock;		/* Passed socket	*/
 };
 
diff -u --recursive --new-file v2.1.88/linux/include/net/sock.h linux/include/net/sock.h
--- v2.1.88/linux/include/net/sock.h	Fri Feb  6 15:33:35 1998
+++ linux/include/net/sock.h	Fri Mar  6 21:03:13 1998
@@ -148,10 +148,10 @@
 	struct in6_addr		daddr;
 
 	__u32			flow_lbl;
+	int			hop_limit;
+	int			mcast_hops;
 	__u8			priority;
-	__u8			hop_limit;
 
-	__u8			mcast_hops;
 
 	/* sockopt flags */
 
@@ -572,7 +572,7 @@
 	void			(*write_wakeup)(struct sock *sk);
 	void			(*read_wakeup)(struct sock *sk);
 
-	unsigned int		(*poll)(struct socket *sock,
+	unsigned int		(*poll)(struct file * file, struct socket *sock,
 					struct poll_table_struct *wait);
 
 	int			(*ioctl)(struct sock *sk, int cmd,
@@ -798,7 +798,7 @@
 					       struct socket *, int);
 extern int                      sock_no_getname(struct socket *,
 						struct sockaddr *, int *, int);
-extern unsigned int             sock_no_poll(struct socket *,
+extern unsigned int             sock_no_poll(struct file *, struct socket *,
 					     struct poll_table_struct *);
 extern int                      sock_no_ioctl(struct socket *, unsigned int,
 					      unsigned long);
@@ -879,7 +879,10 @@
 
 extern __inline__ int sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
 {
-	if (atomic_read(&sk->rmem_alloc) + skb->truesize >= sk->rcvbuf)
+	/* Cast skb->rcvbuf to unsigned... It's pointless, but reduces
+	   number of warnings when compiling with -W --ANK
+	 */
+	if (atomic_read(&sk->rmem_alloc) + skb->truesize >= (unsigned)sk->rcvbuf)
                 return -ENOMEM;
         skb_set_owner_r(skb, sk);
 
@@ -899,7 +902,10 @@
 
 extern __inline__ int __sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
 {
-	if (atomic_read(&sk->rmem_alloc) + skb->truesize >= sk->rcvbuf)
+	/* Cast skb->rcvbuf to unsigned... It's pointless, but reduces
+	   number of warnings when compiling with -W --ANK
+	 */
+	if (atomic_read(&sk->rmem_alloc) + skb->truesize >= (unsigned)sk->rcvbuf)
 		return -ENOMEM;
 	skb_set_owner_r(skb, sk);
 	__skb_queue_tail(&sk->receive_queue,skb);
@@ -910,7 +916,10 @@
 
 extern __inline__ int sock_queue_err_skb(struct sock *sk, struct sk_buff *skb)
 {
-	if (atomic_read(&sk->rmem_alloc) + skb->truesize >= sk->rcvbuf)
+	/* Cast skb->rcvbuf to unsigned... It's pointless, but reduces
+	   number of warnings when compiling with -W --ANK
+	 */
+	if (atomic_read(&sk->rmem_alloc) + skb->truesize >= (unsigned)sk->rcvbuf)
 		return -ENOMEM;
 	skb_set_owner_r(skb, sk);
 	__skb_queue_tail(&sk->error_queue,skb);
diff -u --recursive --new-file v2.1.88/linux/include/net/tcp.h linux/include/net/tcp.h
--- v2.1.88/linux/include/net/tcp.h	Mon Feb 23 18:12:12 1998
+++ linux/include/net/tcp.h	Fri Mar  6 21:05:27 1998
@@ -54,7 +54,7 @@
 extern int sysctl_tcp_timestamps;
 extern int sysctl_tcp_window_scaling;
 
-/* These are AF independant. */
+/* These are AF independent. */
 static __inline__ int tcp_bhashfn(__u16 lport)
 {
 	return (lport ^ (lport >> 7)) & (TCP_BHTABLE_SIZE - 1);
@@ -65,7 +65,7 @@
  * break TCP port selection. This function must also NOT wrap around
  * when the next number exceeds the largest possible port (2^16-1).
  */
-static __inline__ int tcp_bhashnext(__u16 short lport, __u16 h)
+static __inline__ int tcp_bhashnext(__u16 lport, __u16 h)
 {
         __u32 s;	/* don't change this to a smaller type! */
 
@@ -393,7 +393,7 @@
 extern void			tcp_close(struct sock *sk, 
 					  unsigned long timeout);
 extern struct sock *		tcp_accept(struct sock *sk, int flags);
-extern unsigned int		tcp_poll(struct socket *sock, struct poll_table_struct *wait);
+extern unsigned int		tcp_poll(struct file * file, struct socket *sock, struct poll_table_struct *wait);
 extern int			tcp_getsockopt(struct sock *sk, int level, 
 					       int optname, char *optval, 
 					       int *optlen);
diff -u --recursive --new-file v2.1.88/linux/ipc/shm.c linux/ipc/shm.c
--- v2.1.88/linux/ipc/shm.c	Tue Dec  9 09:49:59 1997
+++ linux/ipc/shm.c	Fri Feb 27 10:52:09 1998
@@ -561,7 +561,7 @@
 	shmd->vm_flags = VM_SHM | VM_MAYSHARE | VM_SHARED
 			 | VM_MAYREAD | VM_MAYEXEC | VM_READ | VM_EXEC
 			 | ((shmflg & SHM_RDONLY) ? 0 : VM_MAYWRITE | VM_WRITE);
-	shmd->vm_dentry = NULL;
+	shmd->vm_file = NULL;
 	shmd->vm_offset = 0;
 	shmd->vm_ops = &shm_vm_ops;
 
@@ -689,7 +689,7 @@
 			goto done;
 		}
 		if (!pte_none(pte)) {
-			read_swap_page(pte_val(pte), (char *) page);
+			rw_swap_page_nocache(READ, pte_val(pte), (char *)page);
 			pte = __pte(shp->shm_pages[idx]);
 			if (pte_present(pte))  {
 				free_page (page); /* doesn't sleep */
@@ -820,7 +820,7 @@
 	if (atomic_read(&mem_map[MAP_NR(pte_page(page))].count) != 1)
 		goto check_table;
 	shp->shm_pages[idx] = swap_nr;
-	write_swap_page (swap_nr, (char *) pte_page(page));
+	rw_swap_page_nocache (WRITE, swap_nr, (char *) pte_page(page));
 	free_page(pte_page(page));
 	swap_successes++;
 	shm_swp++;
diff -u --recursive --new-file v2.1.88/linux/kernel/acct.c linux/kernel/acct.c
--- v2.1.88/linux/kernel/acct.c	Thu Feb 12 20:56:13 1998
+++ linux/kernel/acct.c	Sat Feb 28 13:33:49 1998
@@ -36,6 +36,7 @@
 #include <linux/major.h>
 #include <linux/smp.h>
 #include <linux/smp_lock.h>
+#include <linux/file.h>
 
 #include <asm/uaccess.h>
 
@@ -52,8 +53,6 @@
 /*
  * External references and all of the globals.
  */
-extern int close_fp(struct file *);
-
 void acct_timeout(unsigned long);
 
 static volatile int acct_active = 0;
@@ -129,7 +128,7 @@
 		        del_timer(&acct_timer);
 			acct_active = 0;
 			acct_needcheck = 0;
-			close_fp(acct_file);
+			fput(acct_file);
 		}
 		error = 0;
 		goto out;
diff -u --recursive --new-file v2.1.88/linux/kernel/exit.c linux/kernel/exit.c
--- v2.1.88/linux/kernel/exit.c	Sat Jan 10 15:21:42 1998
+++ linux/kernel/exit.c	Sat Feb 28 10:47:39 1998
@@ -164,7 +164,7 @@
 				struct file * file = files->fd[i];
 				if (file) {
 					files->fd[i] = NULL;
-					close_fp(file);
+					close_fp(file, files);
 				}
 			}
 			i++;
diff -u --recursive --new-file v2.1.88/linux/kernel/fork.c linux/kernel/fork.c
--- v2.1.88/linux/kernel/fork.c	Fri Jan 23 18:10:32 1998
+++ linux/kernel/fork.c	Fri Feb 27 10:44:16 1998
@@ -211,7 +211,7 @@
 	flush_cache_mm(current->mm);
 	pprev = &mm->mmap;
 	for (mpnt = current->mm->mmap ; mpnt ; mpnt = mpnt->vm_next) {
-		struct dentry *dentry;
+		struct file *file;
 
 		retval = -ENOMEM;
 		tmp = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);
@@ -222,11 +222,11 @@
 		tmp->vm_mm = mm;
 		mm->map_count++;
 		tmp->vm_next = NULL;
-		dentry = tmp->vm_dentry;
-		if (dentry) {
-			dget(dentry);
+		file = tmp->vm_file;
+		if (file) {
+			file->f_count++;
 			if (tmp->vm_flags & VM_DENYWRITE)
-				dentry->d_inode->i_writecount--;
+				file->f_dentry->d_inode->i_writecount--;
       
 			/* insert tmp into the share list, just after mpnt */
 			if((tmp->vm_next_share = mpnt->vm_next_share) != NULL)
diff -u --recursive --new-file v2.1.88/linux/kernel/ksyms.c linux/kernel/ksyms.c
--- v2.1.88/linux/kernel/ksyms.c	Thu Feb 12 20:56:13 1998
+++ linux/kernel/ksyms.c	Fri Mar  6 21:10:16 1998
@@ -70,7 +70,7 @@
 #endif
 
 extern char *get_options(char *str, int *ints);
-extern void set_device_ro(int dev,int flag);
+extern void set_device_ro(kdev_t dev,int flag);
 extern struct file_operations * get_blkfops(unsigned int);
 extern int blkdev_release(struct inode * inode);
 #if !defined(CONFIG_NFSD) && defined(CONFIG_NFSD_MODULE)
@@ -154,7 +154,6 @@
 EXPORT_SYMBOL(lookup_dentry);
 EXPORT_SYMBOL(open_namei);
 EXPORT_SYMBOL(sys_close);
-EXPORT_SYMBOL(close_fp);
 EXPORT_SYMBOL(d_alloc_root);
 EXPORT_SYMBOL(d_delete);
 EXPORT_SYMBOL(d_validate);
@@ -197,6 +196,7 @@
 EXPORT_SYMBOL(posix_test_lock);
 EXPORT_SYMBOL(posix_block_lock);
 EXPORT_SYMBOL(posix_unblock_lock);
+EXPORT_SYMBOL(locks_remove_flock);
 EXPORT_SYMBOL(dput);
 EXPORT_SYMBOL(get_cached_page);
 EXPORT_SYMBOL(put_cached_page);
diff -u --recursive --new-file v2.1.88/linux/kernel/module.c linux/kernel/module.c
--- v2.1.88/linux/kernel/module.c	Thu Feb 12 20:56:13 1998
+++ linux/kernel/module.c	Tue Feb 24 22:08:02 1998
@@ -305,17 +305,17 @@
 	for (i = 0, dep = mod->deps; i < mod->ndeps; ++i, ++dep) {
 		struct module *o, *d = dep->dep;
 
-		/* Make sure the indicated dependancies are really modules.  */
+		/* Make sure the indicated dependencies are really modules.  */
 		if (d == mod) {
 			printk(KERN_ERR "init_module: self-referential "
-					"dependancy in mod->deps.\n");
+					"dependency in mod->deps.\n");
 			goto err3;
 		}
 
 		for (o = module_list; o != &kernel_module; o = o->next)
 			if (o == d) goto found_dep;
 
-		printk(KERN_ERR "init_module: found dependancy that is "
+		printk(KERN_ERR "init_module: found dependency that is "
 				"(no longer?) a module.\n");
 		goto err3;
 		
@@ -790,7 +790,7 @@
 		mod->flags &= ~MOD_RUNNING;
 	}
 
-	/* Remove the module from the dependancy lists.  */
+	/* Remove the module from the dependency lists.  */
 
 	for (i = 0, dep = mod->deps; i < mod->ndeps; ++i, ++dep) {
 		struct module_ref **pp;
diff -u --recursive --new-file v2.1.88/linux/kernel/panic.c linux/kernel/panic.c
--- v2.1.88/linux/kernel/panic.c	Mon Jun 16 16:36:01 1997
+++ linux/kernel/panic.c	Fri Feb 27 13:57:44 1998
@@ -17,6 +17,7 @@
 #include <linux/reboot.h>
 #include <linux/init.h>
 #include <linux/sysrq.h>
+#include <linux/interrupt.h>
 
 asmlinkage void sys_sync(void);	/* it's really int */
 extern void unblank_console(void);
@@ -42,6 +43,8 @@
 	printk(KERN_EMERG "Kernel panic: %s\n",buf);
 	if (current == task[0])
 		printk(KERN_EMERG "In swapper task - not syncing\n");
+	else if (in_interrupt())
+		printk(KERN_EMERG "In interrupt handler - not syncing\n");
 	else
 		sys_sync();
 
diff -u --recursive --new-file v2.1.88/linux/kernel/signal.c linux/kernel/signal.c
--- v2.1.88/linux/kernel/signal.c	Fri Jan 30 11:28:10 1998
+++ linux/kernel/signal.c	Fri Feb 27 13:58:32 1998
@@ -758,6 +758,7 @@
 	   Nor can they impersonate a kill(), which adds source info.  */
 	if (info.si_code >= 0)
 		return -EPERM;
+	info.si_signo = sig;
 
 	/* POSIX.1b doesn't mention process groups.  */
 	return kill_proc_info(sig, &info, pid);
@@ -860,7 +861,7 @@
 			sigdelsetmask(&current->blocked, new_set);
 			break;
 		case SIG_SETMASK:
-			siginitset(&current->blocked, new_set);
+			current->blocked.sig[0] = new_set;
 			break;
 		}
 
diff -u --recursive --new-file v2.1.88/linux/kernel/sysctl.c linux/kernel/sysctl.c
--- v2.1.88/linux/kernel/sysctl.c	Sat Jan 10 15:15:21 1998
+++ linux/kernel/sysctl.c	Sat Feb 21 13:27:56 1998
@@ -523,9 +523,7 @@
 			proc_unregister(root, de->low_ino);
 			table->de = NULL;
 			kfree(de);
-		} else
-			printk("unregister_proc_table: %s not empty!\n",
-				table->procname);
+		} 
 	}
 }
 
diff -u --recursive --new-file v2.1.88/linux/mm/filemap.c linux/mm/filemap.c
--- v2.1.88/linux/mm/filemap.c	Tue Feb  3 18:26:30 1998
+++ linux/mm/filemap.c	Tue Mar  3 10:32:38 1998
@@ -25,6 +25,8 @@
 #include <linux/smp.h>
 #include <linux/smp_lock.h>
 #include <linux/blkdev.h>
+#include <linux/file.h>
+#include <linux/swapctl.h>
 
 #include <asm/system.h>
 #include <asm/pgtable.h>
@@ -158,19 +160,28 @@
 
 		switch (atomic_read(&page->count)) {
 			case 1:
-				/* If it has been referenced recently, don't free it */
-				if (test_and_clear_bit(PG_referenced, &page->flags))
-					break;
-
-				/* is it a page cache page? */
+				/* is it a swap-cache or page-cache page? */
 				if (page->inode) {
-					if (page->inode == &swapper_inode)
-						panic ("Shrinking a swap cache page");
+					if (test_and_clear_bit(PG_referenced, &page->flags)) {
+						touch_page(page);
+						break;
+					}
+					age_page(page);
+					if (page->age)
+						break;
+					if (PageSwapCache(page)) {
+						delete_from_swap_cache(page);
+						return 1;
+					}
 					remove_page_from_hash_queue(page);
 					remove_page_from_inode_queue(page);
 					__free_page(page);
 					return 1;
 				}
+				/* It's not a cache page, so we don't do aging.
+				 * If it has been referenced recently, don't free it */
+				if (test_and_clear_bit(PG_referenced, &page->flags))
+					break;
 
 				/* is it a buffer cache page? */
 				if ((gfp_mask & __GFP_IO) && bh && try_to_free_buffer(bh, &bh, 6))
@@ -264,10 +275,10 @@
  * that we could use for the cache (if it is 0 we can try to create one,
  * this is all overlapped with the IO on the previous page finishing anyway)
  */
-static unsigned long try_to_read_ahead(struct dentry * dentry,
+static unsigned long try_to_read_ahead(struct file * file,
 				unsigned long offset, unsigned long page_cache)
 {
-	struct inode *inode = dentry->d_inode;
+	struct inode *inode = file->f_dentry->d_inode;
 	struct page * page;
 	struct page ** hash;
 
@@ -288,7 +299,7 @@
 			 */
 			page = mem_map + MAP_NR(page_cache);
 			add_to_page_cache(page, inode, offset, hash);
-			inode->i_op->readpage(dentry, page);
+			inode->i_op->readpage(file, page);
 			page_cache = 0;
 		}
 		release_page(page);
@@ -517,7 +528,7 @@
 	ahead = 0;
 	while (ahead < max_ahead) {
 		ahead += PAGE_SIZE;
-		page_cache = try_to_read_ahead(filp->f_dentry, raend + ahead,
+		page_cache = try_to_read_ahead(filp, raend + ahead,
 						page_cache);
 	}
 /*
@@ -724,7 +735,7 @@
 		if (reada_ok && filp->f_ramax > MIN_READAHEAD)
 			filp->f_ramax = MIN_READAHEAD;
 
-		error = inode->i_op->readpage(dentry, page);
+		error = inode->i_op->readpage(filp, page);
 		if (!error)
 			goto found_page;
 		release_page(page);
@@ -736,7 +747,7 @@
 		 * Try to re-read it _once_. We do this synchronously,
 		 * because this happens only if there were errors.
 		 */
-		error = inode->i_op->readpage(dentry, page);
+		error = inode->i_op->readpage(filp, page);
 		if (!error) {
 			wait_on_page(page);
 			if (PageUptodate(page) && !PageError(page))
@@ -771,7 +782,8 @@
  */
 static unsigned long filemap_nopage(struct vm_area_struct * area, unsigned long address, int no_share)
 {
-	struct dentry * dentry = area->vm_dentry;
+	struct file * file = area->vm_file;
+	struct dentry * dentry = file->f_dentry;
 	struct inode * inode = dentry->d_inode;
 	unsigned long offset;
 	struct page * page, **hash;
@@ -855,14 +867,14 @@
 	new_page = 0;
 	add_to_page_cache(page, inode, offset, hash);
 
-	if (inode->i_op->readpage(dentry, page) != 0)
+	if (inode->i_op->readpage(file, page) != 0)
 		goto failure;
 
 	/*
 	 * Do a very limited read-ahead if appropriate
 	 */
 	if (PageLocked(page))
-		new_page = try_to_read_ahead(dentry, offset + PAGE_SIZE, 0);
+		new_page = try_to_read_ahead(file, offset + PAGE_SIZE, 0);
 	goto found_page;
 
 page_locked_wait:
@@ -877,7 +889,7 @@
 	 * because there really aren't any performance issues here
 	 * and we need to check for errors.
 	 */
-	if (inode->i_op->readpage(dentry, page) != 0)
+	if (inode->i_op->readpage(file, page) != 0)
 		goto failure;
 	wait_on_page(page);
 	if (PageError(page))
@@ -906,6 +918,7 @@
 {
 	int retval;
 	unsigned long size;
+	loff_t loff = offset;
 	mm_segment_t old_fs;
 
 	size = offset + PAGE_SIZE;
@@ -921,8 +934,7 @@
 	old_fs = get_fs();
 	set_fs(KERNEL_DS);
 	retval = -EIO;
-	if (size == file->f_op->write(file, (const char *) page,
-				      size, &file->f_pos))
+	if (size == file->f_op->write(file, (const char *) page, size, &loff))
 		retval = 0;
 	set_fs(old_fs);
 	return retval;
@@ -933,7 +945,7 @@
 	unsigned long page)
 {
 	int result;
-	struct file file;
+	struct file * file;
 	struct dentry * dentry;
 	struct inode * inode;
 	struct buffer_head * bh;
@@ -953,27 +965,21 @@
 		return 0;
 	}
 
-	dentry = vma->vm_dentry;
+	file = vma->vm_file;
+	dentry = file->f_dentry;
 	inode = dentry->d_inode;
-	file.f_op = inode->i_op->default_file_ops;
-	if (!file.f_op->write)
+	if (!file->f_op->write)
 		return -EIO;
-	file.f_mode = 3;
-	file.f_flags = 0;
-	file.f_count = 1;
-	file.f_dentry = dentry;
-	file.f_pos = offset;
-	file.f_reada = 0;
 
 	/*
 	 * If a task terminates while we're swapping the page, the vma and
-	 * and dentry could be released ... increment the count to be safe.
+	 * and file could be released ... increment the count to be safe.
 	 */
-	dget(dentry);
+	file->f_count++;
 	down(&inode->i_sem);
-	result = do_write_page(inode, &file, (const char *) page, offset);
+	result = do_write_page(inode, file, (const char *) page, offset);
 	up(&inode->i_sem);
-	dput(dentry);
+	fput(file);
 	return result;
 }
 
@@ -1208,7 +1214,8 @@
 	if (!inode->i_op || !inode->i_op->readpage)
 		return -ENOEXEC;
 	UPDATE_ATIME(inode);
-	vma->vm_dentry = dget(file->f_dentry);
+	vma->vm_file = file;
+	file->f_count++;
 	vma->vm_ops = ops;
 	return 0;
 }
@@ -1221,15 +1228,16 @@
 static int msync_interval(struct vm_area_struct * vma,
 	unsigned long start, unsigned long end, int flags)
 {
-	if (vma->vm_dentry && vma->vm_ops && vma->vm_ops->sync) {
+	if (vma->vm_file && vma->vm_ops && vma->vm_ops->sync) {
 		int error;
 		error = vma->vm_ops->sync(vma, start, end-start, flags);
 		if (!error && (flags & MS_SYNC)) {
-			struct dentry * dentry = vma->vm_dentry;
-			if (dentry) {
+			struct file * file = vma->vm_file;
+			if (file) {
+				struct dentry * dentry = file->f_dentry;
 				struct inode * inode = dentry->d_inode;
 				down(&inode->i_sem);
-				error = file_fsync(NULL,dentry);
+				error = file_fsync(file, dentry);
 				up(&inode->i_sem);
 			}
 		}
@@ -1380,7 +1388,7 @@
 				status = -EIO;
 				if (didread >= 2)
 					goto done_with_page;
-				status = inode->i_op->readpage(dentry, page);
+				status = inode->i_op->readpage(file, page);
 				if (status < 0)
 					goto done_with_page;
 				didread++;
@@ -1400,7 +1408,7 @@
 
 do_update_page:
 		/* Alright, the page is there.  Now update it. */
-		status = inode->i_op->updatepage(dentry, page, buf,
+		status = inode->i_op->updatepage(file, page, buf,
 							offset, bytes, sync);
 done_with_page:
 		__free_page(page);
diff -u --recursive --new-file v2.1.88/linux/mm/memory.c linux/mm/memory.c
--- v2.1.88/linux/mm/memory.c	Tue Feb 17 13:12:49 1998
+++ linux/mm/memory.c	Mon Feb 23 15:24:32 1998
@@ -267,8 +267,10 @@
 				}
 				if (cow)
 					pte = pte_wrprotect(pte);
+#if 0	/* No longer needed with the new swap cache code */
 				if (delete_from_swap_cache(&mem_map[page_nr]))
 					pte = pte_mkdirty(pte);
+#endif
 				set_pte(dst_pte, pte_mkold(pte));
 				set_pte(src_pte, pte);
 				atomic_inc(&mem_map[page_nr].count);
@@ -616,8 +618,11 @@
 	unsigned long old_page, new_page;
 	struct page * page_map;
 	
-	new_page = __get_free_page(GFP_KERNEL);
 	pte = *page_table;
+	new_page = __get_free_page(GFP_KERNEL);
+	/* Did someone else copy this page for us while we slept? */
+	if (pte_val(*page_table) != pte_val(pte))
+		goto end_wp_page;
 	if (!pte_present(pte))
 		goto end_wp_page;
 	if (pte_write(pte))
@@ -626,15 +631,12 @@
 	if (MAP_NR(old_page) >= max_mapnr)
 		goto bad_wp_page;
 	tsk->min_flt++;
-
 	page_map = mem_map + MAP_NR(old_page);
-	if (PageSwapCache(page_map))
-		delete_from_swap_cache(page_map);
 	
 	/*
 	 * Do we need to copy?
 	 */
-	if (atomic_read(&mem_map[MAP_NR(old_page)].count) != 1) {
+	if (is_page_shared(page_map)) {
 		if (new_page) {
 			if (PageReserved(mem_map + MAP_NR(old_page)))
 				++vma->vm_mm->rss;
@@ -654,6 +656,8 @@
 		oom(tsk);
 		return;
 	}
+	if (PageSwapCache(page_map))
+		delete_from_swap_cache(page_map);
 	flush_cache_page(vma, address);
 	set_pte(page_table, pte_mkdirty(pte_mkwrite(pte)));
 	flush_tlb_page(vma, address);
diff -u --recursive --new-file v2.1.88/linux/mm/mlock.c linux/mm/mlock.c
--- v2.1.88/linux/mm/mlock.c	Sun Jul 13 21:20:11 1997
+++ linux/mm/mlock.c	Fri Feb 27 10:53:07 1998
@@ -38,7 +38,8 @@
 	n->vm_end = end;
 	vma->vm_offset += vma->vm_start - n->vm_start;
 	n->vm_flags = newflags;
-	n->vm_dentry = dget(vma->vm_dentry);
+	if (n->vm_file)
+		n->vm_file->f_count++;
 	if (n->vm_ops && n->vm_ops->open)
 		n->vm_ops->open(n);
 	insert_vm_struct(current->mm, n);
@@ -58,7 +59,8 @@
 	n->vm_start = start;
 	n->vm_offset += n->vm_start - vma->vm_start;
 	n->vm_flags = newflags;
-	n->vm_dentry = dget(vma->vm_dentry);
+	if (n->vm_file)
+		n->vm_file->f_count++;
 	if (n->vm_ops && n->vm_ops->open)
 		n->vm_ops->open(n);
 	insert_vm_struct(current->mm, n);
@@ -87,8 +89,8 @@
 	vma->vm_offset += vma->vm_start - left->vm_start;
 	right->vm_offset += right->vm_start - left->vm_start;
 	vma->vm_flags = newflags;
-	if (vma->vm_dentry)
-		vma->vm_dentry->d_count += 2;
+	if (vma->vm_file)
+		vma->vm_file->f_count += 2;
 
 	if (vma->vm_ops && vma->vm_ops->open) {
 		vma->vm_ops->open(left);
diff -u --recursive --new-file v2.1.88/linux/mm/mmap.c linux/mm/mmap.c
--- v2.1.88/linux/mm/mmap.c	Wed Jan 14 14:52:44 1998
+++ linux/mm/mmap.c	Fri Feb 27 14:02:06 1998
@@ -17,6 +17,7 @@
 #include <linux/smp.h>
 #include <linux/smp_lock.h>
 #include <linux/init.h>
+#include <linux/file.h>
 
 #include <asm/uaccess.h>
 #include <asm/system.h>
@@ -74,11 +75,11 @@
 /* Remove one vm structure from the inode's i_mmap ring. */
 static inline void remove_shared_vm_struct(struct vm_area_struct *vma)
 {
-	struct dentry * dentry = vma->vm_dentry;
+	struct file * file = vma->vm_file;
 
-	if (dentry) {
+	if (file) {
 		if (vma->vm_flags & VM_DENYWRITE)
-			dentry->d_inode->i_writecount++;
+			file->f_dentry->d_inode->i_writecount++;
 		if(vma->vm_next_share)
 			vma->vm_next_share->vm_pprev_share = vma->vm_pprev_share;
 		*vma->vm_pprev_share = vma->vm_next_share;
@@ -261,7 +262,7 @@
 	vma->vm_page_prot = protection_map[vma->vm_flags & 0x0f];
 	vma->vm_ops = NULL;
 	vma->vm_offset = off;
-	vma->vm_dentry = NULL;
+	vma->vm_file = NULL;
 	vma->vm_pte = 0;
 
 	/* Clear old maps */
@@ -394,8 +395,8 @@
 	if (addr == area->vm_start && end == area->vm_end) {
 		if (area->vm_ops && area->vm_ops->close)
 			area->vm_ops->close(area);
-		if (area->vm_dentry)
-			dput(area->vm_dentry);
+		if (area->vm_file)
+			fput(area->vm_file);
 		return 0;
 	}
 
@@ -418,7 +419,9 @@
 		mpnt->vm_flags = area->vm_flags;
 		mpnt->vm_ops = area->vm_ops;
 		mpnt->vm_offset = area->vm_offset + (end - area->vm_start);
-		mpnt->vm_dentry = dget(area->vm_dentry);
+		mpnt->vm_file = area->vm_file;
+		if (mpnt->vm_file)
+			mpnt->vm_file->f_count++;
 		if (mpnt->vm_ops && mpnt->vm_ops->open)
 			mpnt->vm_ops->open(mpnt);
 		area->vm_end = addr;	/* Truncate area */
@@ -577,8 +580,8 @@
 		mm->map_count--;
 		remove_shared_vm_struct(mpnt);
 		zap_page_range(mm, start, size);
-		if (mpnt->vm_dentry)
-			dput(mpnt->vm_dentry);
+		if (mpnt->vm_file)
+			fput(mpnt->vm_file);
 		kmem_cache_free(vm_area_cachep, mpnt);
 		mpnt = next;
 	}
@@ -594,7 +597,7 @@
 void insert_vm_struct(struct mm_struct *mm, struct vm_area_struct *vmp)
 {
 	struct vm_area_struct **pprev = &mm->mmap;
-	struct dentry * dentry;
+	struct file * file;
 
 	mm->map_count++;
 
@@ -608,9 +611,9 @@
 	*pprev = vmp;
 	vmp->vm_pprev = pprev;
 
-	dentry = vmp->vm_dentry;
-	if (dentry) {
-		struct inode * inode = dentry->d_inode;
+	file = vmp->vm_file;
+	if (file) {
+		struct inode * inode = file->f_dentry->d_inode;
 		if (vmp->vm_flags & VM_DENYWRITE)
 			inode->i_writecount--;
       
@@ -657,8 +660,8 @@
 	for ( ; mpnt && prev->vm_start < end_addr ; prev = mpnt, mpnt = next) {
 		next = mpnt->vm_next;
 
-		/* To share, we must have the same dentry, operations.. */
-		if ((mpnt->vm_dentry != prev->vm_dentry)||
+		/* To share, we must have the same file, operations.. */
+		if ((mpnt->vm_file != prev->vm_file)||
 		    (mpnt->vm_pte != prev->vm_pte)	||
 		    (mpnt->vm_ops != prev->vm_ops)	||
 		    (mpnt->vm_flags != prev->vm_flags)	||
@@ -666,10 +669,10 @@
 			continue;
 
 		/*
-		 * If we have a dentry or it's a shared memory area
+		 * If we have a file or it's a shared memory area
 		 * the offsets must be contiguous..
 		 */
-		if ((mpnt->vm_dentry != NULL) || (mpnt->vm_flags & VM_SHM)) {
+		if ((mpnt->vm_file != NULL) || (mpnt->vm_flags & VM_SHM)) {
 			unsigned long off = prev->vm_offset+prev->vm_end-prev->vm_start;
 			if (off != mpnt->vm_offset)
 				continue;
@@ -691,8 +694,8 @@
 		}
 		mm->map_count--;
 		remove_shared_vm_struct(mpnt);
-		if (mpnt->vm_dentry)
-			dput(mpnt->vm_dentry);
+		if (mpnt->vm_file)
+			fput(mpnt->vm_file);
 		kmem_cache_free(vm_area_cachep, mpnt);
 		mpnt = prev;
 	}
diff -u --recursive --new-file v2.1.88/linux/mm/mprotect.c linux/mm/mprotect.c
--- v2.1.88/linux/mm/mprotect.c	Sun Jul 13 21:20:11 1997
+++ linux/mm/mprotect.c	Fri Feb 27 10:51:11 1998
@@ -110,7 +110,8 @@
 	vma->vm_offset += vma->vm_start - n->vm_start;
 	n->vm_flags = newflags;
 	n->vm_page_prot = prot;
-	n->vm_dentry = dget(n->vm_dentry);
+	if (n->vm_file)
+		n->vm_file->f_count++;
 	if (n->vm_ops && n->vm_ops->open)
 		n->vm_ops->open(n);
 	insert_vm_struct(current->mm, n);
@@ -132,7 +133,8 @@
 	n->vm_offset += n->vm_start - vma->vm_start;
 	n->vm_flags = newflags;
 	n->vm_page_prot = prot;
-	n->vm_dentry = dget(n->vm_dentry);
+	if (n->vm_file)
+		n->vm_file->f_count++;
 	if (n->vm_ops && n->vm_ops->open)
 		n->vm_ops->open(n);
 	insert_vm_struct(current->mm, n);
@@ -163,8 +165,8 @@
 	right->vm_offset += right->vm_start - left->vm_start;
 	vma->vm_flags = newflags;
 	vma->vm_page_prot = prot;
-	if (vma->vm_dentry)
-		vma->vm_dentry->d_count += 2;
+	if (vma->vm_file)
+		vma->vm_file->f_count += 2;
 	if (vma->vm_ops && vma->vm_ops->open) {
 		vma->vm_ops->open(left);
 		vma->vm_ops->open(right);
diff -u --recursive --new-file v2.1.88/linux/mm/mremap.c linux/mm/mremap.c
--- v2.1.88/linux/mm/mremap.c	Sun Jul 13 21:20:11 1997
+++ linux/mm/mremap.c	Fri Feb 27 10:51:49 1998
@@ -140,7 +140,9 @@
 			new_vma->vm_start = new_addr;
 			new_vma->vm_end = new_addr+new_len;
 			new_vma->vm_offset = vma->vm_offset + (addr - vma->vm_start);
-			new_vma->vm_dentry = dget(vma->vm_dentry);
+			new_vma->vm_file = vma->vm_file;
+			if (new_vma->vm_file)
+				new_vma->vm_file->f_count++;
 			if (new_vma->vm_ops && new_vma->vm_ops->open)
 				new_vma->vm_ops->open(new_vma);
 			insert_vm_struct(current->mm, new_vma);
diff -u --recursive --new-file v2.1.88/linux/mm/page_alloc.c linux/mm/page_alloc.c
--- v2.1.88/linux/mm/page_alloc.c	Thu Jan 15 21:35:49 1998
+++ linux/mm/page_alloc.c	Tue Mar  3 10:38:00 1998
@@ -19,6 +19,7 @@
 #include <linux/swapctl.h>
 #include <linux/interrupt.h>
 #include <linux/init.h>
+#include <linux/pagemap.h>
 
 #include <asm/dma.h>
 #include <asm/system.h> /* for cli()/sti() */
@@ -101,6 +102,46 @@
 static spinlock_t page_alloc_lock;
 #endif
 
+/*
+ * This routine is used by the kernel swap deamon to determine
+ * whether we have "enough" free pages. It is fairly arbitrary,
+ * but this had better return false if any reasonable "get_free_page()"
+ * allocation could currently fail..
+ *
+ * Currently we approve of the following situations:
+ * - the highest memory order has two entries
+ * - the highest memory order has one free entry and:
+ *	- the next-highest memory order has two free entries
+ * - the highest memory order has one free entry and:
+ *	- the next-highest memory order has one free entry
+ *	- the next-next-highest memory order has two free entries
+ *
+ * [previously, there had to be two entries of the highest memory
+ *  order, but this lead to problems on large-memory machines.]
+ */
+int free_memory_available(void)
+{
+	int i, retval = 0;
+	unsigned long flags;
+	struct free_area_struct * list = NULL;
+
+	spin_lock_irqsave(&page_alloc_lock, flags);
+	/* We fall through the loop if the list contains one
+	 * item. -- thanks to Colin Plumb <colin@nyx.net>
+	 */
+	for (i = 1; i < 4; ++i) {
+		list = free_area + NR_MEM_LISTS - i;
+		if (list->next == memory_head(list))
+			break;
+		if (list->next->next == memory_head(list))
+			continue;
+		retval = 1;
+		break;
+	}
+	spin_unlock_irqrestore(&page_alloc_lock, flags);
+	return retval;
+}
+
 static inline void free_pages_ok(unsigned long map_nr, unsigned long order)
 {
 	struct free_area_struct *area = free_area + order;
@@ -328,31 +369,38 @@
 void swap_in(struct task_struct * tsk, struct vm_area_struct * vma,
 	pte_t * page_table, unsigned long entry, int write_access)
 {
-	unsigned long page = __get_free_page(GFP_KERNEL);
+	unsigned long page;
+	struct page *page_map;
+	
+	page_map = read_swap_cache(entry);
 
 	if (pte_val(*page_table) != entry) {
-		free_page(page);
+		if (page_map)
+			free_page_and_swap_cache(page_address(page_map));
 		return;
 	}
-	if (!page) {
+	if (!page_map) {
 		set_pte(page_table, BAD_PAGE);
 		swap_free(entry);
 		oom(tsk);
 		return;
 	}
-	read_swap_page(entry, (char *) page);
-	if (pte_val(*page_table) != entry) {
-		free_page(page);
-		return;
-	}
+
+	page = page_address(page_map);
 	vma->vm_mm->rss++;
-	tsk->maj_flt++;
-	if (!write_access && add_to_swap_cache(&mem_map[MAP_NR(page)], entry)) {
-		/* keep swap page allocated for the moment (swap cache) */
+	tsk->min_flt++;
+	swap_free(entry);
+
+	if (!write_access || is_page_shared(page_map)) {
 		set_pte(page_table, mk_pte(page, vma->vm_page_prot));
 		return;
 	}
+
+	/* The page is unshared, and we want write access.  In this
+	   case, it is safe to tear down the swap cache and give the
+	   page over entirely to this process. */
+		
+	delete_from_swap_cache(page_map);
 	set_pte(page_table, pte_mkwrite(pte_mkdirty(mk_pte(page, vma->vm_page_prot))));
-  	swap_free(entry);
   	return;
 }
diff -u --recursive --new-file v2.1.88/linux/mm/page_io.c linux/mm/page_io.c
--- v2.1.88/linux/mm/page_io.c	Mon Jan 12 14:33:20 1998
+++ linux/mm/page_io.c	Tue Feb 24 10:55:35 1998
@@ -6,6 +6,7 @@
  *  Swap reorganised 29.12.95, 
  *  Asynchronous swapping added 30.12.95. Stephen Tweedie
  *  Removed race in async swapping. 14.4.1996. Bruno Haible
+ *  Add swap of shared pages through the page cache. 20.2.1998. Stephen Tweedie
  */
 
 #include <linux/mm.h>
@@ -27,26 +28,36 @@
 #include <asm/bitops.h>
 #include <asm/pgtable.h>
 
-static struct wait_queue * lock_queue = NULL;
-
 /*
  * Reads or writes a swap page.
  * wait=1: start I/O and wait for completion. wait=0: start asynchronous I/O.
+ * All IO to swap files (as opposed to swap partitions) is done
+ * synchronously.
  *
- * Important prevention of race condition: The first thing we do is set a lock
- * on this swap page, which lasts until I/O completes. This way a
- * write_swap_page(entry) immediately followed by a read_swap_page(entry)
- * on the same entry will first complete the write_swap_page(). Fortunately,
- * not more than one write_swap_page() request can be pending per entry. So
- * all races the caller must catch are: multiple read_swap_page() requests
- * on the same entry.
+ * Important prevention of race condition: the caller *must* atomically 
+ * create a unique swap cache entry for this swap page before calling
+ * rw_swap_page, and must lock that page.  By ensuring that there is a
+ * single page of memory reserved for the swap entry, the normal VM page
+ * lock on that page also doubles as a lock on swap entries.  Having only
+ * one lock to deal with per swap entry (rather than locking swap and memory
+ * independently) also makes it easier to make certain swapping operations
+ * atomic, which is particularly important when we are trying to ensure 
+ * that shared pages stay shared while being swapped.
  */
+
 void rw_swap_page(int rw, unsigned long entry, char * buf, int wait)
 {
 	unsigned long type, offset;
 	struct swap_info_struct * p;
 	struct page *page = mem_map + MAP_NR(buf);
 
+#ifdef DEBUG_SWAP
+	printk ("DebugVM: %s_swap_page entry %08lx, page %p (count %d), %s\n",
+		(rw == READ) ? "read" : "write", 
+		entry, buf, atomic_read(&page->count),
+		wait ? "wait" : "nowait");
+#endif
+
 	if (page->inode && page->inode != &swapper_inode)
 		panic ("Tried to swap a non-swapper page");
 	type = SWP_TYPE(entry);
@@ -61,31 +72,28 @@
 		return;
 	}
 	if (p->swap_map && !p->swap_map[offset]) {
-		printk("Hmm.. Trying to use unallocated swap (%08lx)\n", entry);
+		printk("Hmm.. Trying to %s unallocated swap (%08lx)\n", 
+		       (rw == READ) ? "read" : "write", 
+		       entry);
 		return;
 	}
 	if (!(p->flags & SWP_USED)) {
 		printk("Trying to swap to unused swap-device\n");
 		return;
 	}
-	/*
-	 * For now, this is not legal!
-	 */
-	if (PageSwapCache(page))
-		panic ("Trying to swap a swap-cache page!");
 
-	/* Make sure we are the only process doing I/O with this swap page. */
-	while (test_and_set_bit(offset,p->swap_lockmap)) {
-		run_task_queue(&tq_disk);
-		sleep_on(&lock_queue);
+	if (!PageLocked(page)) {
+		printk("VM: swap page is unlocked\n");
+		return;
 	}
-
-	if (rw == READ)
+	
+	if (rw == READ) {
+		clear_bit(PG_uptodate, &page->flags);
 		kstat.pswpin++;
-	else
+	} else
 		kstat.pswpout++;
+
 	atomic_inc(&page->count);
-	wait_on_page(page);
 	/* 
 	 * Make sure that we have a swap cache association for this
 	 * page.  We need this to find which swap page to unlock once
@@ -94,17 +102,19 @@
 	 * as if it were: we are not allowed to manipulate the inode
 	 * hashing for locked pages.
 	 */
-	if (PageSwapCache(page)) {
-		if (page->offset != entry)
-			panic ("swap entry mismatch");
-	} else 
-		page->offset = entry;
+	if (!PageSwapCache(page)) {
+		printk("VM: swap page is not in swap cache\n");
+		return;
+	}
+	if (page->offset != entry) {
+		printk ("swap entry mismatch");
+		return;
+	}
 
 	if (p->swap_device) {
 		if (!wait) {
 			set_bit(PG_free_after, &page->flags);
 			set_bit(PG_decr_after, &page->flags);
-			set_bit(PG_swap_unlock_after, &page->flags);
 			atomic_inc(&nr_async_pages);
 		}
 		ll_rw_page(rw,p->swap_device,offset,buf);
@@ -155,38 +165,51 @@
 				}
 		}
 		ll_rw_swap_file(rw,swapf->i_dev, zones, i,buf);
+		/* Unlike ll_rw_page, ll_rw_swap_file won't unlock the
+		   page for us. */
+		clear_bit(PG_locked, &page->flags);
+		wake_up(&page->wait);
 	} else
 		printk("rw_swap_page: no swap file or device\n");
 
 	atomic_dec(&page->count);
-	if (offset && !test_and_clear_bit(offset,p->swap_lockmap))
-		printk("rw_swap_page: lock already cleared\n");
-	wake_up(&lock_queue);
+#ifdef DEBUG_SWAP
+	printk ("DebugVM: %s_swap_page finished on page %p (count %d)\n",
+		(rw == READ) ? "read" : "write", 
+		buf, atomic_read(&page->count));
+#endif
 }
 
-
-/* This is run when asynchronous page I/O has completed. */
-void swap_after_unlock_page (unsigned long entry)
+/*
+ * Setting up a new swap file needs a simple wrapper just to read the 
+ * swap signature.  SysV shared memory also needs a simple wrapper.
+ */
+void rw_swap_page_nocache(int rw, unsigned long entry, char *buffer)
 {
-	unsigned long type, offset;
-	struct swap_info_struct * p;
-
-	type = SWP_TYPE(entry);
-	if (type >= nr_swapfiles) {
-		printk("swap_after_unlock_page: bad swap-device\n");
+	struct page *page;
+	
+	page = mem_map + MAP_NR((unsigned long) buffer);
+	wait_on_page(page);
+	set_bit(PG_locked, &page->flags);
+	if (test_and_set_bit(PG_swap_cache, &page->flags)) {
+		printk ("VM: read_swap_page: page already in swap cache!\n");
 		return;
 	}
-	p = &swap_info[type];
-	offset = SWP_OFFSET(entry);
-	if (offset >= p->max) {
-		printk("swap_after_unlock_page: weirdness\n");
+	if (page->inode) {
+		printk ("VM: read_swap_page: page already in page cache!\n");
 		return;
 	}
-	if (!test_and_clear_bit(offset,p->swap_lockmap))
-		printk("swap_after_unlock_page: lock already cleared\n");
-	wake_up(&lock_queue);
+	page->inode = &swapper_inode;
+	page->offset = entry;
+	atomic_inc(&page->count);	/* Protect from shrink_mmap() */
+	rw_swap_page(rw, entry, buffer, 1);
+	atomic_dec(&page->count);
+	page->inode = 0;
+	clear_bit(PG_swap_cache, &page->flags);
 }
 
+
+
 /*
  * Swap partitions are now read via brw_page.  ll_rw_page is an
  * asynchronous function now --- we must call wait_on_page afterwards
@@ -211,7 +234,7 @@
 			panic("ll_rw_page: bad block dev cmd, must be R/W");
 	}
 	page = mem_map + MAP_NR(buffer);
-	if (test_and_set_bit(PG_locked, &page->flags))
-		panic ("ll_rw_page: page already locked");
+	if (!PageLocked(page))
+		panic ("ll_rw_page: page not already locked");
 	brw_page(rw, page, dev, &block, PAGE_SIZE, 0);
 }
diff -u --recursive --new-file v2.1.88/linux/mm/swap_state.c linux/mm/swap_state.c
--- v2.1.88/linux/mm/swap_state.c	Mon Jan 12 14:33:20 1998
+++ linux/mm/swap_state.c	Mon Feb 23 15:24:32 1998
@@ -55,29 +55,33 @@
 
 int add_to_swap_cache(struct page *page, unsigned long entry)
 {
-	struct swap_info_struct * p = &swap_info[SWP_TYPE(entry)];
-
 #ifdef SWAP_CACHE_INFO
 	swap_cache_add_total++;
 #endif
-	if (PageLocked(page))
-		panic("Adding page cache to locked page");
-	if ((p->flags & SWP_WRITEOK) == SWP_WRITEOK) {
-		if (PageTestandSetSwapCache(page))
-			panic("swap_cache: replacing non-empty entry");
-		if (page->inode)
-			panic("swap_cache: replacing page-cached entry");
-		atomic_inc(&page->count);
-		page->inode = &swapper_inode;
-		page->offset = entry;
-		add_page_to_hash_queue(page, &swapper_inode, entry);
-		add_page_to_inode_queue(&swapper_inode, page);
-#ifdef SWAP_CACHE_INFO
-		swap_cache_add_success++;
+#ifdef DEBUG_SWAP
+	printk("DebugVM: add_to_swap_cache(%08lx count %d, entry %08lx)\n",
+	       page_address(page), atomic_read(&page->count), entry);
 #endif
-		return 1;
+	if (PageTestandSetSwapCache(page)) {
+		printk("swap_cache: replacing non-empty entry %08lx "
+		       "on page %08lx",
+		       page->offset, page_address(page));
+		return 0;
 	}
-	return 0;
+	if (page->inode) {
+		printk("swap_cache: replacing page-cached entry "
+		       "on page %08lx", page_address(page));
+		return 0;
+	}
+	atomic_inc(&page->count);
+	page->inode = &swapper_inode;
+	page->offset = entry;
+	add_page_to_hash_queue(page, &swapper_inode, entry);
+	add_page_to_inode_queue(&swapper_inode, page);
+#ifdef SWAP_CACHE_INFO
+	swap_cache_add_success++;
+#endif
+	return 1;
 }
 
 /*
@@ -110,6 +114,10 @@
 				entry, p->swap_map[offset]);
 		p->swap_map[offset] = 127;
 	}
+#ifdef DEBUG_SWAP
+	printk("DebugVM: swap_duplicate(entry %08lx, count now %d)\n",
+	       entry, p->swap_map[offset]);
+#endif
 out:
 	return;
 
@@ -120,25 +128,37 @@
 	printk("swap_duplicate: offset exceeds max\n");
 	goto out;
 bad_unused:
-	printk("swap_duplicate: unused page\n");
+	printk("swap_duplicate at %8p: unused page\n", 
+	       __builtin_return_address(0));
 	goto out;
 }
 
 
 void remove_from_swap_cache(struct page *page)
 {
-	if (!page->inode)
-		panic ("Removing swap cache page with zero inode hash");
-	if (page->inode != &swapper_inode)
-		panic ("Removing swap cache page with wrong inode hash");
-	if (PageLocked(page))
-		panic ("Removing swap cache from locked page");
+	if (!page->inode) {
+		printk ("VM: Removing swap cache page with zero inode hash "
+			"on page %08lx", page_address(page));
+		return;
+	}
+	if (page->inode != &swapper_inode) {
+		printk ("VM: Removing swap cache page with wrong inode hash "
+			"on page %08lx", page_address(page));
+	}
 	/*
 	 * This will be a legal case once we have a more mature swap cache.
 	 */
-	if (atomic_read(&page->count) == 1)
-		panic ("Removing page cache on unshared page");
+	if (atomic_read(&page->count) == 1) {
+		printk ("VM: Removing page cache on unshared page %08lx", 
+			page_address(page));
+		return;
+	}
+
 	
+#ifdef DEBUG_SWAP
+	printk("DebugVM: remove_from_swap_cache(%08lx count %d)\n",
+	       page_address(page), atomic_read(&page->count));
+#endif
 	remove_page_from_hash_queue (page);
 	remove_page_from_inode_queue (page);
 	PageClearSwapCache (page);
@@ -172,6 +192,11 @@
 #ifdef SWAP_CACHE_INFO
 		swap_cache_del_success++;
 #endif
+#ifdef DEBUG_SWAP
+		printk("DebugVM: delete_from_swap_cache(%08lx count %d, "
+		       "entry %08lx)\n",
+		       page_address(page), atomic_read(&page->count), entry);
+#endif
 		remove_from_swap_cache (page);
 		swap_free (entry);
 		return 1;
@@ -190,8 +215,86 @@
 	/* 
 	 * If we are the only user, then free up the swap cache. 
 	 */
-	if (PageSwapCache(page) && !is_page_shared(page))
+	if (PageSwapCache(page) && !is_page_shared(page)) {
 		delete_from_swap_cache(page);
+	}
 	
 	free_page(addr);
 }
+
+
+/*
+ * Lookup a swap entry in the swap cache.  We need to be careful about
+ * locked pages.  A found page will be returned with its refcount
+ * incremented.
+ */
+
+static struct page * lookup_swap_cache(unsigned long entry)
+{
+	struct page *found;
+	
+	while (1) {
+		found = find_page(&swapper_inode, entry);
+		if (!found)
+			return 0;
+		if (found->inode != &swapper_inode 
+		    || !PageSwapCache(found)) {
+			__free_page(found);
+			printk ("VM: Found a non-swapper swap page!\n");
+			return 0;
+		}
+		if (!PageLocked(found))
+			return found;
+		__free_page(found);
+		__wait_on_page(found);
+	}
+}
+
+/* 
+ * Locate a page of swap in physical memory, reserving swap cache space
+ * and reading the disk if it is not already cached.  If wait==0, we are
+ * only doing readahead, so don't worry if the page is already locked.
+ */
+
+struct page * read_swap_cache_async(unsigned long entry, int wait)
+{
+	struct page *found_page, *new_page = 0;
+	unsigned long new_page_addr = 0;
+	
+#ifdef DEBUG_SWAP
+	printk("DebugVM: read_swap_cache_async entry %08lx%s\n",
+	       entry, wait ? ", wait" : "");
+#endif
+repeat:
+	found_page = lookup_swap_cache(entry);
+	if (found_page) {
+		if (new_page)
+			__free_page(new_page);
+		return found_page;
+	}
+
+	/* The entry is not present.  Lock down a new page, add it to
+	 * the swap cache and read its contents. */
+	if (!new_page) {
+		new_page_addr = __get_free_page(GFP_KERNEL);
+		if (!new_page_addr)
+			return 0;	/* Out of memory */
+		new_page = mem_map + MAP_NR(new_page_addr);
+		goto repeat;		/* We might have stalled */
+	}
+	
+	if (!add_to_swap_cache(new_page, entry)) {
+		free_page(new_page_addr);
+		return 0;
+	}
+	swap_duplicate(entry);		/* Account for the swap cache */
+	set_bit(PG_locked, &new_page->flags);
+	rw_swap_page(READ, entry, (char *) new_page_addr, wait);
+#ifdef DEBUG_SWAP
+	printk("DebugVM: read_swap_cache_async created "
+	       "entry %08lx at %p\n",
+	       entry, (char *) page_address(new_page));
+#endif
+	return new_page;
+}
+
diff -u --recursive --new-file v2.1.88/linux/mm/swapfile.c linux/mm/swapfile.c
--- v2.1.88/linux/mm/swapfile.c	Mon Jan 12 14:33:20 1998
+++ linux/mm/swapfile.c	Mon Feb 23 15:24:32 1998
@@ -21,6 +21,7 @@
 #include <linux/malloc.h>
 #include <linux/blkdev.h> /* for blk_size */
 #include <linux/vmalloc.h>
+#include <linux/pagemap.h>
 
 #include <asm/bitops.h>
 #include <asm/pgtable.h>
@@ -51,8 +52,6 @@
 			offset = si->cluster_next++;
 			if (si->swap_map[offset])
 				continue;
-			if (test_bit(offset, si->swap_lockmap))
-				continue;
 			si->cluster_nr--;
 			goto got_page;
 		}
@@ -61,8 +60,6 @@
 	for (offset = si->lowest_bit; offset <= si->highest_bit ; offset++) {
 		if (si->swap_map[offset])
 			continue;
-		if (test_bit(offset, si->swap_lockmap))
-			continue;
 		si->lowest_bit = offset;
 got_page:
 		si->swap_map[offset] = 1;
@@ -153,6 +150,10 @@
 		if (!--p->swap_map[offset])
 			nr_swap_pages++;
 	}
+#ifdef DEBUG_SWAP
+	printk("DebugVM: swap_free(entry %08lx, count now %d)\n",
+	       entry, p->swap_map[offset]);
+#endif
 out:
 	return;
 
@@ -173,42 +174,38 @@
 /*
  * The swap entry has been read in advance, and we return 1 to indicate
  * that the page has been used or is no longer needed.
+ *
+ * Always set the resulting pte to be nowrite (the same as COW pages
+ * after one process has exited).  We don't know just how many ptes will
+ * share this swap entry, so be cautious and let do_wp_page work out
+ * what to do if a write is requested later.
  */
-static inline int unuse_pte(struct vm_area_struct * vma, unsigned long address,
+static inline void unuse_pte(struct vm_area_struct * vma, unsigned long address,
 	pte_t *dir, unsigned long entry, unsigned long page)
 {
 	pte_t pte = *dir;
 
 	if (pte_none(pte))
-		return 0;
+		return;
 	if (pte_present(pte)) {
-		struct page *pg;
-		unsigned long page_nr = MAP_NR(pte_page(pte));
-		unsigned long pg_swap_entry;
-
-		if (page_nr >= max_mapnr)
-			return 0;
-		pg = mem_map + page_nr;
-		if (!(pg_swap_entry = in_swap_cache(pg)))
-			return 0;
-		if (SWP_TYPE(pg_swap_entry) != SWP_TYPE(entry))
-			return 0;
-		delete_from_swap_cache(pg);
+		/* If this entry is swap-cached, then page must already
+                   hold the right address for any copies in physical
+                   memory */
+		if (pte_page(pte) != page)
+			return;
+		/* We will be removing the swap cache in a moment, so... */
 		set_pte(dir, pte_mkdirty(pte));
-		if (pg_swap_entry != entry)
-			return 0;
-		free_page(page);
-		return 1;
+		return;
 	}
 	if (pte_val(pte) != entry)
-		return 0;
-	set_pte(dir, pte_mkwrite(pte_mkdirty(mk_pte(page, vma->vm_page_prot))));
-	++vma->vm_mm->rss;
+		return;
+	set_pte(dir, pte_mkdirty(mk_pte(page, vma->vm_page_prot)));
 	swap_free(entry);
-	return 1;
+	atomic_inc(&mem_map[MAP_NR(page)].count);
+	++vma->vm_mm->rss;
 }
 
-static inline int unuse_pmd(struct vm_area_struct * vma, pmd_t *dir,
+static inline void unuse_pmd(struct vm_area_struct * vma, pmd_t *dir,
 	unsigned long address, unsigned long size, unsigned long offset,
 	unsigned long entry, unsigned long page)
 {
@@ -216,11 +213,11 @@
 	unsigned long end;
 
 	if (pmd_none(*dir))
-		return 0;
+		return;
 	if (pmd_bad(*dir)) {
 		printk("unuse_pmd: bad pmd (%08lx)\n", pmd_val(*dir));
 		pmd_clear(dir);
-		return 0;
+		return;
 	}
 	pte = pte_offset(dir, address);
 	offset += address & PMD_MASK;
@@ -229,16 +226,13 @@
 	if (end > PMD_SIZE)
 		end = PMD_SIZE;
 	do {
-		if (unuse_pte(vma, offset+address-vma->vm_start, pte, entry, 
-				page))
-			return 1;
+		unuse_pte(vma, offset+address-vma->vm_start, pte, entry, page);
 		address += PAGE_SIZE;
 		pte++;
 	} while (address < end);
-	return 0;
 }
 
-static inline int unuse_pgd(struct vm_area_struct * vma, pgd_t *dir,
+static inline void unuse_pgd(struct vm_area_struct * vma, pgd_t *dir,
 	unsigned long address, unsigned long size,
 	unsigned long entry, unsigned long page)
 {
@@ -246,11 +240,11 @@
 	unsigned long offset, end;
 
 	if (pgd_none(*dir))
-		return 0;
+		return;
 	if (pgd_bad(*dir)) {
 		printk("unuse_pgd: bad pgd (%08lx)\n", pgd_val(*dir));
 		pgd_clear(dir);
-		return 0;
+		return;
 	}
 	pmd = pmd_offset(dir, address);
 	offset = address & PGDIR_MASK;
@@ -259,30 +253,26 @@
 	if (end > PGDIR_SIZE)
 		end = PGDIR_SIZE;
 	do {
-		if (unuse_pmd(vma, pmd, address, end - address, offset, entry,
-				 page))
-			return 1;
+		unuse_pmd(vma, pmd, address, end - address, offset, entry,
+			  page);
 		address = (address + PMD_SIZE) & PMD_MASK;
 		pmd++;
 	} while (address < end);
-	return 0;
 }
 
-static int unuse_vma(struct vm_area_struct * vma, pgd_t *pgdir,
+static void unuse_vma(struct vm_area_struct * vma, pgd_t *pgdir,
 			unsigned long entry, unsigned long page)
 {
 	unsigned long start = vma->vm_start, end = vma->vm_end;
 
 	while (start < end) {
-		if (unuse_pgd(vma, pgdir, start, end - start, entry, page))
-			return 1;
+		unuse_pgd(vma, pgdir, start, end - start, entry, page);
 		start = (start + PGDIR_SIZE) & PGDIR_MASK;
 		pgdir++;
 	}
-	return 0;
 }
 
-static int unuse_process(struct mm_struct * mm, unsigned long entry, 
+static void unuse_process(struct mm_struct * mm, unsigned long entry, 
 			unsigned long page)
 {
 	struct vm_area_struct* vma;
@@ -291,13 +281,12 @@
 	 * Go through process' page directory.
 	 */
 	if (!mm || mm == &init_mm)
-		return 0;
+		return;
 	for (vma = mm->mmap; vma; vma = vma->vm_next) {
 		pgd_t * pgd = pgd_offset(mm, vma->vm_start);
-		if (unuse_vma(vma, pgd, entry, page))
-			return 1;
+		unuse_vma(vma, pgd, entry, page);
 	}
-	return 0;
+	return;
 }
 
 /*
@@ -310,19 +299,14 @@
 	struct swap_info_struct * si = &swap_info[type];
 	struct task_struct *p;
 	unsigned long page = 0;
+	struct page *page_map;
 	unsigned long entry;
 	int i;
 
 	while (1) {
-		if (!page) {
-			page = __get_free_page(GFP_KERNEL);
-			if (!page)
-				return -ENOMEM;
-		}
-
 		/*
-	 	* Find a swap page in use and read it in.
-	 	*/
+		 * Find a swap page in use and read it in.
+		 */
 		for (i = 1 , entry = 0; i < si->max ; i++) {
 			if (si->swap_map[i] > 0 && si->swap_map[i] != 0x80) {
 				entry = SWP_ENTRY(type, i);
@@ -331,36 +315,31 @@
 		}
 		if (!entry)
 			break;
-		read_swap_page(entry, (char *) page);
 
+		/* Get a page for the entry, using the existing swap
+                   cache page if there is one.  Otherwise, get a clean
+                   page and read the swap into it. */
+		page_map = read_swap_cache(entry);
+		if (!page_map)
+			return -ENOMEM;
+		page = page_address(page_map);
 		read_lock(&tasklist_lock);
-		for_each_task(p) {
-			if (unuse_process(p->mm, entry, page)) {
-				page = 0;
-				goto unlock;
-			}
-		}
-	unlock:
+		for_each_task(p)
+			unuse_process(p->mm, entry, page);
 		read_unlock(&tasklist_lock);
-		if (page) {
-			/*
-			 * If we couldn't find an entry, there are several
-			 * possible reasons: someone else freed it first,
-			 * we freed the last reference to an overflowed entry,
-			 * or the system has lost track of the use counts.
-			 */
-			if (si->swap_map[i] != 0) {
-				if (si->swap_map[i] != 127)
-					printk("try_to_unuse: entry %08lx "
-					       "not in use\n", entry);
-				si->swap_map[i] = 0;
-				nr_swap_pages++;
-			}
+		/* Now get rid of the extra reference to the temporary
+                   page we've been using. */
+		if (PageSwapCache(page_map))
+			delete_from_swap_cache(page_map);
+		free_page(page);
+		if (si->swap_map[i] != 0) {
+			if (si->swap_map[i] != 127)
+				printk("try_to_unuse: entry %08lx "
+				       "not in use\n", entry);
+			si->swap_map[i] = 0;
+			nr_swap_pages++;
 		}
 	}
-
-	if (page)
-		free_page(page);
 	return 0;
 }
 
@@ -371,7 +350,7 @@
 	struct file filp;
 	int i, type, prev;
 	int err = -EPERM;
-
+	
 	lock_kernel();
 	if (!suser())
 		goto out;
@@ -445,8 +424,6 @@
 	p->swap_device = 0;
 	vfree(p->swap_map);
 	p->swap_map = NULL;
-	free_page((long) p->swap_lockmap);
-	p->swap_lockmap = NULL;
 	p->flags = 0;
 	err = 0;
 out:
@@ -506,6 +483,7 @@
 	int error = -EPERM;
 	struct file filp;
 	static int least_priority = 0;
+	unsigned char *avail_map = 0;
 
 	lock_kernel();
 	if (!suser())
@@ -523,7 +501,6 @@
 	p->swap_file = NULL;
 	p->swap_device = 0;
 	p->swap_map = NULL;
-	p->swap_lockmap = NULL;
 	p->lowest_bit = 0;
 	p->highest_bit = 0;
 	p->cluster_nr = 0;
@@ -566,24 +543,24 @@
 		}
 	} else if (!S_ISREG(swap_dentry->d_inode->i_mode))
 		goto bad_swap;
-	p->swap_lockmap = (unsigned char *) get_free_page(GFP_USER);
-	if (!p->swap_lockmap) {
+	avail_map = (unsigned char *) get_free_page(GFP_USER);
+	if (!avail_map) {
 		printk("Unable to start swapping: out of memory :-)\n");
 		error = -ENOMEM;
 		goto bad_swap;
 	}
-	read_swap_page(SWP_ENTRY(type,0), (char *) p->swap_lockmap);
-	if (memcmp("SWAP-SPACE",p->swap_lockmap+PAGE_SIZE-10,10)) {
+	rw_swap_page_nocache(READ, SWP_ENTRY(type,0), (char *) avail_map);
+	if (memcmp("SWAP-SPACE",avail_map+PAGE_SIZE-10,10)) {
 		printk("Unable to find swap-space signature\n");
 		error = -EINVAL;
 		goto bad_swap;
 	}
-	memset(p->swap_lockmap+PAGE_SIZE-10,0,10);
+	memset(avail_map+PAGE_SIZE-10,0,10);
 	j = 0;
 	p->lowest_bit = 0;
 	p->highest_bit = 0;
 	for (i = 1 ; i < 8*PAGE_SIZE ; i++) {
-		if (test_bit(i,p->swap_lockmap)) {
+		if (test_bit(i,avail_map)) {
 			if (!p->lowest_bit)
 				p->lowest_bit = i;
 			p->highest_bit = i;
@@ -602,13 +579,12 @@
 		goto bad_swap;
 	}
 	for (i = 1 ; i < p->max ; i++) {
-		if (test_bit(i,p->swap_lockmap))
+		if (test_bit(i,avail_map))
 			p->swap_map[i] = 0;
 		else
 			p->swap_map[i] = 0x80;
 	}
 	p->swap_map[0] = 0x80;
-	memset(p->swap_lockmap,0,PAGE_SIZE);
 	p->flags = SWP_WRITEOK;
 	p->pages = j;
 	nr_swap_pages += j;
@@ -635,15 +611,15 @@
 	if(filp.f_op && filp.f_op->release)
 		filp.f_op->release(filp.f_dentry->d_inode,&filp);
 bad_swap_2:
-	free_page((long) p->swap_lockmap);
 	vfree(p->swap_map);
 	dput(p->swap_file);
 	p->swap_device = 0;
 	p->swap_file = NULL;
 	p->swap_map = NULL;
-	p->swap_lockmap = NULL;
 	p->flags = 0;
 out:
+	if (avail_map)
+		free_page((long) avail_map);
 	unlock_kernel();
 	return error;
 }
diff -u --recursive --new-file v2.1.88/linux/mm/vmscan.c linux/mm/vmscan.c
--- v2.1.88/linux/mm/vmscan.c	Thu Jan 15 21:07:58 1998
+++ linux/mm/vmscan.c	Wed Mar  4 18:00:09 1998
@@ -7,7 +7,7 @@
  *  kswapd added: 7.1.96  sct
  *  Removed kswapd_ctl limits, and swap out as many pages as needed
  *  to bring the system back to free_pages_high: 2.4.97, Rik van Riel.
- *  Version: $Id: vmscan.c,v 1.23 1997/04/12 04:31:05 davem Exp $
+ *  Version: $Id: vmscan.c,v 1.5 1998/02/23 22:14:28 sct Exp $
  */
 
 #include <linux/mm.h>
@@ -80,73 +80,149 @@
 	    || PageLocked(page_map)
 	    || ((gfp_mask & __GFP_DMA) && !PageDMA(page_map)))
 		return 0;
-	/* Deal with page aging.  Pages age from being unused; they
-	 * rejuvenate on being accessed.  Only swap old pages (age==0
-	 * is oldest).
+
+	/* 
+	 * Deal with page aging.  There are several special cases to
+	 * consider:
+	 * 
+	 * Page has been accessed, but is swap cached.  If the page is
+	 * getting sufficiently "interesting" --- its age is getting
+	 * high --- then if we are sufficiently short of free swap
+	 * pages, then delete the swap cache.  We can only do this if
+	 * the swap page's reference count is one: ie. there are no
+	 * other references to it beyond the swap cache (as there must
+	 * still be pte's pointing to it if count > 1).
+	 * 
+	 * If the page has NOT been touched, and its age reaches zero,
+	 * then we are swapping it out:
 	 *
-	 * This test will no longer work once swap cached pages can be
-	 * shared!  
-	 */
-	if ((pte_dirty(pte) && delete_from_swap_cache(page_map)) 
-	    || pte_young(pte))  {
+	 *   If there is already a swap cache page for this page, then
+	 *   another process has already allocated swap space, so just
+	 *   dereference the physical page and copy in the swap entry
+	 *   from the swap cache.  
+	 * 
+	 * Note, we rely on all pages read in from swap either having
+	 * the swap cache flag set, OR being marked writable in the pte,
+	 * but NEVER BOTH.  (It IS legal to be neither cached nor dirty,
+	 * however.)
+	 *
+	 * -- Stephen Tweedie 1998 */
+
+	if (PageSwapCache(page_map)) {
+		if (pte_write(pte)) {
+			printk ("VM: Found a writable swap-cached page!\n");
+			return 0;
+		}
+	}
+	
+	if (pte_young(pte)) {
 		set_pte(page_table, pte_mkold(pte));
 		touch_page(page_map);
+		/* 
+		 * We should test here to see if we want to recover any
+		 * swap cache page here.  We do this if the page seeing
+		 * enough activity, AND we are sufficiently low on swap
+		 *
+		 * We need to track both the number of available swap
+		 * pages and the total number present before we can do
+		 * this...  
+		 */
 		return 0;
 	}
+
 	age_page(page_map);
 	if (page_map->age)
 		return 0;
+
 	if (pte_dirty(pte)) {
-		if (PageSwapCache(page_map))
-			panic ("Can't still be swap cached!!!");
 		if (vma->vm_ops && vma->vm_ops->swapout) {
 			pid_t pid = tsk->pid;
 			vma->vm_mm->rss--;
 			if (vma->vm_ops->swapout(vma, address - vma->vm_start + vma->vm_offset, page_table))
 				kill_proc(pid, SIGBUS, 1);
 		} else {
-			if (!(entry = get_swap_page()))
-				return 0;
+			/*
+			 * This is a dirty, swappable page.  First of all,
+			 * get a suitable swap entry for it, and make sure
+			 * we have the swap cache set up to associate the
+			 * page with that swap entry.
+			 */
+			if (PageSwapCache(page_map)) {
+				entry = page_map->offset;
+			} else {
+				entry = get_swap_page();
+				if (!entry)
+					return 0; /* No swap space left */
+			}
+			
 			vma->vm_mm->rss--;
+			tsk->nswap++;
 			flush_cache_page(vma, address);
 			set_pte(page_table, __pte(entry));
 			flush_tlb_page(vma, address);
-			tsk->nswap++;
+			swap_duplicate(entry);
+
+			/* Now to write back the page.  We have two
+			 * cases: if the page is already part of the
+			 * swap cache, then it is already on disk.  Just
+			 * free the page and return (we release the swap
+			 * cache on the last accessor too).
+			 *
+			 * If we have made a new swap entry, then we
+			 * start the write out to disk.  If the page is
+			 * shared, however, we still need to keep the
+			 * copy in memory, so we add it to the swap
+			 * cache. */
+			if (PageSwapCache(page_map)) {
+				free_page_and_swap_cache(page);
+				return (atomic_read(&page_map->count) == 0);
+			}
+			add_to_swap_cache(page_map, entry);
+			/* We checked we were unlocked way up above, and we
+			   have been careful not to stall until here */
+			set_bit(PG_locked, &page_map->flags);
+			/* OK, do a physical write to swap.  */
 			rw_swap_page(WRITE, entry, (char *) page, (gfp_mask & __GFP_WAIT));
 		}
-		/* 
-		 * For now, this is safe, because the test above makes
-		 * sure that this page is currently not swap-cached.  
-		 */
-		if (PageSwapCache(page_map))
-			panic ("Page became cached after IO");
-		free_page(page);
+		/* Now we can free the current physical page.  We also
+		 * free up the swap cache if this is the last use of the
+		 * page.  Note that there is a race here: the page may
+		 * still be shared COW by another process, but that
+		 * process may exit while we are writing out the page
+		 * asynchronously.  That's no problem, shrink_mmap() can
+		 * correctly clean up the occassional unshared page
+		 * which gets left behind in the swap cache. */
+		free_page_and_swap_cache(page);
 		return 1;	/* we slept: the process may not exist any more */
 	}
-	/* 
-	 * Eventually, find_in_swap_cache will be able to return true
-	 * even for pages shared with other processes.  
-	 */
-        if ((entry = find_in_swap_cache(page_map)))  {
-		if (atomic_read(&page_map->count) != 1) {
-			set_pte(page_table, pte_mkdirty(pte));
-			printk("Aiee.. duplicated cached swap-cache entry\n");
-			return 0;
-		}
+
+	/* The page was _not_ dirty, but still has a zero age.  It must
+	 * already be uptodate on disk.  If it is in the swap cache,
+	 * then we can just unlink the page now.  Remove the swap cache
+	 * too if this is the last user.  */
+        if ((entry = in_swap_cache(page_map)))  {
 		vma->vm_mm->rss--;
 		flush_cache_page(vma, address);
 		set_pte(page_table, __pte(entry));
 		flush_tlb_page(vma, address);
-		free_page(page);
-		return 1;
+		swap_duplicate(entry);
+		free_page_and_swap_cache(page);
+		return (atomic_read(&page_map->count) == 0);
 	} 
+	/* 
+	 * A clean page to be discarded?  Must be mmap()ed from
+	 * somewhere.  Unlink the pte, and tell the filemap code to
+	 * discard any cached backing page if this is the last user.
+	 */
+	if (PageSwapCache(page_map)) {
+		printk ("VM: How can this page _still_ be cached?");
+		return 0;
+	}
 	vma->vm_mm->rss--;
 	flush_cache_page(vma, address);
 	pte_clear(page_table);
 	flush_tlb_page(vma, address);
 	entry = page_unuse(page);
-	if (PageSwapCache(page_map))
-		panic ("How can this page _still_ be cached?");
 	free_page(page);
 	return entry;
 }
@@ -257,14 +333,15 @@
 	 * Go through process' page directory.
 	 */
 	address = p->swap_address;
-	p->swap_address = 0;
 
 	/*
 	 * Find the proper vm-area
 	 */
 	vma = find_vma(p->mm, address);
-	if (!vma)
+	if (!vma) {
+		p->swap_address = 0;
 		return 0;
+	}
 	if (address < vma->vm_start)
 		address = vma->vm_start;
 
@@ -424,7 +501,7 @@
 void kswapd_setup(void)
 {
        int i;
-       char *revision="$Revision: 1.23 $", *s, *e;
+       char *revision="$Revision: 1.5 $", *s, *e;
 
        if ((s = strchr(revision, ':')) &&
            (e = strchr(s, '$')))
@@ -441,6 +518,7 @@
  */
 int kswapd(void *unused)
 {
+	struct wait_queue wait = { current, NULL };
 	current->session = 1;
 	current->pgrp = 1;
 	sprintf(current->comm, "kswapd");
@@ -460,44 +538,63 @@
 				    priorities.  */
 
 	init_swap_timer();
-	
+	add_wait_queue(&kswapd_wait, &wait);
 	while (1) {
-		int fail;
+		int tries;
 
 		kswapd_awake = 0;
 		flush_signals(current);
 		run_task_queue(&tq_disk);
-		interruptible_sleep_on(&kswapd_wait);
+		schedule();
+		current->state = TASK_INTERRUPTIBLE;
 		kswapd_awake = 1;
 		swapstats.wakeups++;
 		/* Do the background pageout: 
-		 * We now only swap out as many pages as needed.
-		 * When we are truly low on memory, we swap out
-		 * synchronously (WAIT == 1).  -- Rik.
-		 * If we've had too many consecutive failures,
-		 * go back to sleep to let other tasks run.
+		 * When we've got loads of memory, we try
+		 * (free_pages_high - nr_free_pages) times to
+		 * free memory. As memory gets tighter, kswapd
+		 * gets more and more agressive. -- Rik.
 		 */
-		for (fail = 0; fail++ < MAX_SWAP_FAIL;) {
-			int pages, gfp_mask;
+		tries = free_pages_high - nr_free_pages;
+		if (tries < min_free_pages) {
+			tries = min_free_pages;
+		}
+		else if (nr_free_pages < (free_pages_high + free_pages_low) / 2) {
+			tries <<= 1;
+			if (nr_free_pages < free_pages_low) {
+				tries <<= 1;
+				if (nr_free_pages <= min_free_pages) {
+					tries <<= 1;
+				}
+			}
+		}
+		while (tries--) {
+			int gfp_mask;
 
-			pages = nr_free_pages;
-			if (nr_free_pages >= min_free_pages)
-				pages += atomic_read(&nr_async_pages);
-			if (pages >= free_pages_high)
+			if (free_memory_available())
 				break;
 			gfp_mask = __GFP_IO;
-			if (pages < free_pages_low)
-				gfp_mask |= __GFP_WAIT;
-			if (try_to_free_page(gfp_mask))
-				fail = 0;
+			try_to_free_page(gfp_mask);
+			/*
+			 * Syncing large chunks is faster than swapping
+			 * synchronously (less head movement). -- Rik.
+			 */
+			if (atomic_read(&nr_async_pages) >= SWAP_CLUSTER_MAX)
+				run_task_queue(&tq_disk);
+
 		}
-		/*
-		 * Report failure if we couldn't reach the minimum goal.
-		 */
-		if (nr_free_pages < min_free_pages)
-			printk("kswapd: failed, got %d of %d\n",
-				nr_free_pages, min_free_pages);
+#if 0
+	/*
+	 * Report failure if we couldn't even reach min_free_pages.
+	 */
+	if (nr_free_pages < min_free_pages)
+		printk("kswapd: failed, got %d of %d\n",
+			nr_free_pages, min_free_pages);
+#endif
 	}
+	/* As if we could ever get here - maybe we want to make this killable */
+	remove_wait_queue(&kswapd_wait, &wait);
+	return 0;
 }
 
 /* 
diff -u --recursive --new-file v2.1.88/linux/net/core/datagram.c linux/net/core/datagram.c
--- v2.1.88/linux/net/core/datagram.c	Thu Feb 12 20:56:14 1998
+++ linux/net/core/datagram.c	Thu Feb 26 20:02:53 1998
@@ -195,12 +195,12 @@
  *	is only ever holding data ready to receive.
  */
 
-unsigned int datagram_poll(struct socket *sock, poll_table *wait)
+unsigned int datagram_poll(struct file * file, struct socket *sock, poll_table *wait)
 {
 	struct sock *sk = sock->sk;
 	unsigned int mask;
 
-	poll_wait(sk->sleep, wait);
+	poll_wait(file, sk->sleep, wait);
 	mask = 0;
 
 	/* exceptional events? */
diff -u --recursive --new-file v2.1.88/linux/net/core/dev.c linux/net/core/dev.c
--- v2.1.88/linux/net/core/dev.c	Tue Feb 17 13:12:50 1998
+++ linux/net/core/dev.c	Sun Mar  1 14:40:40 1998
@@ -49,7 +49,7 @@
  *	    Thomas Bogendoerfer :	Return ENODEV for dev_open, if there
  *					is no device open function.
  *		Andi Kleen	:	Fix error reporting for SIOCGIFCONF
- *		Régis Duchesne	:	Fix the argument check in dev_ioctl()
+ *	    Michael Chastain	:	Fix signed/unsigned for SIOCGIFCONF
  *
  */
 
@@ -81,9 +81,7 @@
 #include <net/pkt_sched.h>
 #include <net/profile.h>
 #include <linux/init.h>
-#ifdef CONFIG_KERNELD
 #include <linux/kerneld.h>
-#endif
 #ifdef CONFIG_NET_RADIO
 #include <linux/wireless.h>
 #endif	/* CONFIG_NET_RADIO */
@@ -322,14 +320,17 @@
 
 void dev_load(const char *name)
 {
-	if(!dev_get(name))
+	if(!dev_get(name) && suser())
 		request_module(name);
 }
 
+#else
+
+extern inline void dev_load(const char *unused){;}
+
 #endif
 
-static int
-default_rebuild_header(struct sk_buff *skb)
+static int default_rebuild_header(struct sk_buff *skb)
 {
 	printk(KERN_DEBUG "%s: default_rebuild_header called -- BUG!\n", skb->dev ? skb->dev->name : "NULL!!!");
 	kfree_skb(skb);
@@ -965,13 +966,13 @@
 
 /* Protocol dependent address dumping routines */
 
-static int (*gifconf[NPROTO])(struct device *dev, char *bufptr, int len);
+static gifconf_func_t * gifconf_list [NPROTO];
 
-int register_gifconf(int family, int (*func)(struct device *dev, char *bufptr, int len))
+int register_gifconf(unsigned int family, gifconf_func_t * gifconf)
 {
-	if (family<0 || family>=NPROTO)
+	if (family>=NPROTO)
 		return -EINVAL;
-	gifconf[family] = func;
+	gifconf_list[family] = gifconf;
 	return 0;
 }
 
@@ -1058,49 +1059,45 @@
 	struct ifconf ifc;
 	struct device *dev;
 	char *pos;
-	unsigned int len;
-	int err;
+	int len;
+	int total;
+	int i;
 
 	/*
 	 *	Fetch the caller's info block. 
 	 */
 	
-	err = copy_from_user(&ifc, arg, sizeof(struct ifconf));
-	if (err)
+	if (copy_from_user(&ifc, arg, sizeof(struct ifconf)))
 		return -EFAULT;
 
 	pos = ifc.ifc_buf;
-	if (pos==NULL)
-		ifc.ifc_len=0;
 	len = ifc.ifc_len;
 
 	/*
 	 *	Loop over the interfaces, and write an info block for each. 
 	 */
 
+	total = 0;
 	for (dev = dev_base; dev != NULL; dev = dev->next) {
-		int i;
 		for (i=0; i<NPROTO; i++) {
-			int done;
-
-			if (gifconf[i] == NULL)
-				continue;
-
-			done = gifconf[i](dev, pos, len);
-
-			if (done<0)
-				return -EFAULT;
-
-			len -= done;
-			if (pos)
-				pos += done;
+			if (gifconf_list[i]) {
+				int done;
+				if (pos==NULL) {
+					done = gifconf_list[i](dev, NULL, 0);
+				} else {
+					done = gifconf_list[i](dev, pos+total, len-total);
+				}
+				if (done<0)
+					return -EFAULT;
+				total += done;
+			}
 		}
   	}
 
 	/*
 	 *	All done.  Write the updated control block back to the caller. 
 	 */
-	ifc.ifc_len -= len;
+	ifc.ifc_len = total;
 
 	if (copy_to_user(arg, &ifc, sizeof(struct ifconf)))
 		return -EFAULT; 
@@ -1535,6 +1532,7 @@
 	return -EINVAL;
 }
 
+
 /*
  *	This function handles all "interface"-type I/O control requests. The actual
  *	'doing' part of this is dev_ifsioc above.
@@ -1544,9 +1542,7 @@
 {
 	struct ifreq ifr;
 	int ret;
-#ifdef CONFIG_NET_ALIAS
 	char *colon;
-#endif
 
 	/* One special case: SIOCGIFCONF takes ifconf argument
 	   and requires shared lock, because it sleeps writing
@@ -1566,19 +1562,19 @@
 		return dev_ifname((struct ifreq *)arg);
 	}
 
-	/*
-	 *	Fetch the interface name from the info block. 
-	 */
-
 	if (copy_from_user(&ifr, arg, sizeof(struct ifreq)))
 		return -EFAULT;
+
 	ifr.ifr_name[IFNAMSIZ-1] = 0;
-#ifdef CONFIG_NET_ALIAS
+
 	colon = strchr(ifr.ifr_name, ':');
 	if (colon)
 		*colon = 0;
-#endif
 
+	/*
+	 *	See which interface the caller is talking about. 
+	 */
+	 
 	switch(cmd) 
 	{
 		/*
@@ -1596,15 +1592,11 @@
 		case SIOCGIFMAP:
 		case SIOCGIFINDEX:
 		case SIOCGIFTXQLEN:
-#ifdef CONFIG_KERNELD
 			dev_load(ifr.ifr_name);
-#endif	
 			ret = dev_ifsioc(&ifr, cmd);
 			if (!ret) {
-#ifdef CONFIG_NET_ALIAS
 				if (colon)
 					*colon = ':';
-#endif
 				if (copy_to_user(arg, &ifr, sizeof(struct ifreq)))
 					return -EFAULT;
 			}
@@ -1629,9 +1621,7 @@
 		case SIOCSIFTXQLEN:
 			if (!suser())
 				return -EPERM;
-#ifdef CONFIG_KERNELD
 			dev_load(ifr.ifr_name);
-#endif	
 			rtnl_lock();
 			ret = dev_ifsioc(&ifr, cmd);
 			rtnl_unlock();
@@ -1652,9 +1642,7 @@
 		default:
 			if (cmd >= SIOCDEVPRIVATE &&
 			    cmd <= SIOCDEVPRIVATE + 15) {
-#ifdef CONFIG_KERNELD
 				dev_load(ifr.ifr_name);
-#endif	
 				rtnl_lock();
 				ret = dev_ifsioc(&ifr, cmd);
 				rtnl_unlock();
@@ -1664,18 +1652,12 @@
 			}
 #ifdef CONFIG_NET_RADIO
 			if (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) {
+				dev_load(ifr.ifr_name);
 				if (IW_IS_SET(cmd)) {
 					if (!suser())
 						return -EPERM;
-#ifdef CONFIG_KERNELD
-					dev_load(ifr.ifr_name);
-#endif	
 					rtnl_lock();
 				}
-#ifdef CONFIG_KERNELD
-				else
-					dev_load(ifr.ifr_name);
-#endif	
 				ret = dev_ifsioc(&ifr, cmd);
 				if (IW_IS_SET(cmd))
 					rtnl_unlock();
@@ -1689,7 +1671,7 @@
 	}
 }
 
-int dev_new_index()
+int dev_new_index(void)
 {
 	static int ifindex;
 	for (;;) {
@@ -1938,7 +1920,7 @@
 
 #ifdef CONFIG_PROC_FS
 	proc_net_register(&proc_net_dev);
-	if (1) {
+	{
 		struct proc_dir_entry *ent = create_proc_entry("net/dev_stat", 0, 0);
 		ent->read_proc = dev_proc_stats;
 	}
diff -u --recursive --new-file v2.1.88/linux/net/core/dst.c linux/net/core/dst.c
--- v2.1.88/linux/net/core/dst.c	Thu Feb 12 20:56:14 1998
+++ linux/net/core/dst.c	Sun Mar  1 14:40:40 1998
@@ -79,6 +79,11 @@
 void * dst_alloc(int size, struct dst_ops * ops)
 {
 	struct dst_entry * dst;
+
+	if (ops->gc && atomic_read(&ops->entries) > ops->gc_thresh) {
+		if (ops->gc())
+			return NULL;
+	}
 	dst = kmalloc(size, GFP_ATOMIC);
 	if (!dst)
 		return NULL;
@@ -89,6 +94,7 @@
 	dst->input = dst_discard;
 	dst->output = dst_blackhole;
 	atomic_inc(&dst_total);
+	atomic_inc(&ops->entries);
 	return dst;
 }
 
@@ -121,6 +127,8 @@
 		dst->neighbour = NULL;
 		neigh_release(neigh);
 	}
+
+	atomic_dec(&dst->ops->entries);
 
 	if (dst->ops->destroy)
 		dst->ops->destroy(dst);
diff -u --recursive --new-file v2.1.88/linux/net/core/iovec.c linux/net/core/iovec.c
--- v2.1.88/linux/net/core/iovec.c	Mon Jan 12 15:28:25 1998
+++ linux/net/core/iovec.c	Fri Mar  6 09:56:41 1998
@@ -38,9 +38,8 @@
 
 int verify_iovec(struct msghdr *m, struct iovec *iov, char *address, int mode)
 {
-	int err=0;
-	int len=0;
-	int ct;
+	int size = m->msg_iovlen * sizeof(struct iovec);
+	int err, ct;
 	
 	if(m->msg_namelen)
 	{
@@ -48,7 +47,7 @@
 		{
 			err=move_addr_to_kernel(m->msg_name, m->msg_namelen, address);
 			if(err<0)
-				return err;
+				goto out;
 		}
 		
 		m->msg_name = address;
@@ -57,24 +56,26 @@
 
 	if (m->msg_iovlen > UIO_FASTIOV)
 	{
-		iov = kmalloc(m->msg_iovlen*sizeof(struct iovec), GFP_KERNEL);
+		err = -ENOMEM;
+		iov = kmalloc(size, GFP_KERNEL);
 		if (!iov)
-			return -ENOMEM;
+			goto out;
 	}
 	
-	err = copy_from_user(iov, m->msg_iov, sizeof(struct iovec)*m->msg_iovlen);
-	if (err)
-	{
-		if (m->msg_iovlen > UIO_FASTIOV)
-			kfree(iov);
-		return -EFAULT;
-	}
+	if (copy_from_user(iov, m->msg_iov, size))
+		goto out_free;
+	m->msg_iov=iov;
 
-	for(ct=0;ct<m->msg_iovlen;ct++)
-		len+=iov[ct].iov_len;
+	for (err = 0, ct = 0; ct < m->msg_iovlen; ct++)
+		err += iov[ct].iov_len;
+out:
+	return err;
 
-	m->msg_iov=iov;
-	return len;
+out_free:
+	err = -EFAULT;
+	if (m->msg_iovlen > UIO_FASTIOV)
+		kfree(iov);
+	goto out;
 }
 
 /*
@@ -83,15 +84,15 @@
  
 int memcpy_toiovec(struct iovec *iov, unsigned char *kdata, int len)
 {
-	int err; 
+	int err = -EFAULT; 
+
 	while(len>0)
 	{
 		if(iov->iov_len)
 		{
-			int copy = min(iov->iov_len,len);
-			err = copy_to_user(iov->iov_base,kdata,copy);
-			if (err) 
-			    return err;
+			int copy = min(iov->iov_len, len);
+			if (copy_to_user(iov->iov_base, kdata, copy))
+				goto out;
 			kdata+=copy;
 			len-=copy;
 			iov->iov_len-=copy;
@@ -99,7 +100,9 @@
 		}
 		iov++;
 	}
-	return 0; 
+	err = 0;
+out:
+	return err; 
 }
 
 /*
@@ -108,17 +111,15 @@
  
 int memcpy_fromiovec(unsigned char *kdata, struct iovec *iov, int len)
 {
-	int err; 
+	int err = -EFAULT; 
+
 	while(len>0)
 	{
 		if(iov->iov_len)
 		{
-			int copy=min(len,iov->iov_len);
-			err = copy_from_user(kdata, iov->iov_base, copy);
-			if (err)
-			{
-				return -EFAULT;
-			}
+			int copy = min(len, iov->iov_len);
+			if (copy_from_user(kdata, iov->iov_base, copy))
+				goto out;
 			len-=copy;
 			kdata+=copy;
 			iov->iov_base+=copy;
@@ -126,7 +127,9 @@
 		}
 		iov++;
 	}
-	return 0; 
+	err = 0;
+out:
+	return err; 
 }
 
 
@@ -137,28 +140,23 @@
 int memcpy_fromiovecend(unsigned char *kdata, struct iovec *iov, int offset,
 			int len)
 {
-	int err; 
+	int err = -EFAULT;
+
 	while(offset>0)
 	{
 		if (offset > iov->iov_len)
 		{
 			offset -= iov->iov_len;
-
 		}
 		else
 		{
-			u8 *base;
-			int copy;
+			u8 *base = iov->iov_base + offset;
+			int copy = min(len, iov->iov_len - offset);
 
-			base = iov->iov_base + offset;
-			copy = min(len, iov->iov_len - offset);
 			offset = 0;
 
-			err = copy_from_user(kdata, base, copy);
-			if (err)
-			{
-				return -EFAULT;
-			}
+			if (copy_from_user(kdata, base, copy))
+				goto out;
 			len-=copy;
 			kdata+=copy;
 		}
@@ -167,17 +165,17 @@
 
 	while (len>0)
 	{
-		int copy=min(len, iov->iov_len);
-		err = copy_from_user(kdata, iov->iov_base, copy);
-		if (err)
-		{
-			return -EFAULT;
-		}
+		int copy = min(len, iov->iov_len);
+
+		if (copy_from_user(kdata, iov->iov_base, copy))
+			goto out;
 		len-=copy;
 		kdata+=copy;
 		iov++;
 	}
-	return 0;
+	err = 0;
+out:
+	return err;
 }
 
 /*
@@ -200,25 +198,28 @@
 	do {
 		int copy = iov->iov_len - offset;
 
-		if (copy >= 0) {
+		if (copy > 0) {
 			u8 *base = iov->iov_base + offset;
 
 			/* Normal case (single iov component) is fastly detected */
 			if (len <= copy) {
 				*csump = csum_and_copy_from_user(base, kdata, 
 								 len, *csump, &err);
-				return err;
+				goto out;
 			}
 
 			partial_cnt = copy % 4;
 			if (partial_cnt) {
 				copy -= partial_cnt;
-				err |= copy_from_user(kdata+copy, base+copy, partial_cnt);
+				if (copy_from_user(kdata + copy, base + copy,
+						partial_cnt))
+					goto out_fault;
 			}
 
-			*csump = csum_and_copy_from_user(base, kdata, 
-							 copy, *csump, &err);
-
+			*csump = csum_and_copy_from_user(base, kdata, copy,
+							 *csump, &err);
+			if (err)
+				goto out;
 			len   -= copy + partial_cnt;
 			kdata += copy + partial_cnt;
 			iov++;
@@ -230,7 +231,7 @@
 
 	csum = *csump;
 
-	while (len>0)
+	while (len > 0)
 	{
 		u8 *base = iov->iov_base;
 		unsigned int copy = min(len, iov->iov_len);
@@ -242,23 +243,26 @@
 
 			/* iov component is too short ... */
 			if (par_len > copy) {
-				err |= copy_from_user(kdata, base, copy);
+				if (copy_from_user(kdata, base, copy))
+					goto out_fault;
+				kdata += copy;
 				base += copy;
 				partial_cnt += copy;
-				kdata += copy;
 				len -= copy;
 				iov++;
 				if (len)
 					continue;
-				*csump = csum_partial(kdata-partial_cnt, partial_cnt, csum);
-				return err;
+				*csump = csum_partial(kdata - partial_cnt,
+							 partial_cnt, csum);
+				goto out;
 			}
-			err |= copy_from_user(kdata, base, par_len);
-			csum = csum_partial(kdata-partial_cnt, 4, csum);
+			if (copy_from_user(kdata, base, par_len))
+				goto out_fault;
+			csum = csum_partial(kdata - partial_cnt, 4, csum);
+			kdata += par_len;
 			base += par_len;
 			copy -= par_len;
 			len -= par_len;
-			kdata += par_len;
 			partial_cnt = 0;
 		}
 
@@ -268,18 +272,31 @@
 			if (partial_cnt)
 			{
 				copy -= partial_cnt;
-				err |= copy_from_user(kdata+copy, base + copy, partial_cnt);
+				if (copy_from_user(kdata + copy, base + copy,
+				 		partial_cnt))
+					goto out_fault;
 			}
 		}
 
-		if (copy == 0)
+		/* Why do we want to break?? There may be more to copy ... */
+		if (copy == 0) {
+if (len > partial_cnt)
+printk("csum_iovec: early break? len=%d, partial=%d\n", len, partial_cnt);
 			break;
+		}
 
 		csum = csum_and_copy_from_user(base, kdata, copy, csum, &err);
+		if (err)
+			goto out;
 		len   -= copy + partial_cnt;
 		kdata += copy + partial_cnt;
 		iov++;
 	}
         *csump = csum;
+out:
 	return err;
+
+out_fault:
+	err = -EFAULT;
+	goto out;
 }
diff -u --recursive --new-file v2.1.88/linux/net/core/neighbour.c linux/net/core/neighbour.c
--- v2.1.88/linux/net/core/neighbour.c	Tue Feb 17 13:12:50 1998
+++ linux/net/core/neighbour.c	Sun Mar  1 14:40:40 1998
@@ -170,7 +170,7 @@
 		return NULL;
 
 	memset(n, 0, tbl->entry_size);
-	
+
 	skb_queue_head_init(&n->arp_queue);
 	n->updated = n->used = jiffies;
 	n->nud_state = NUD_NONE;
@@ -222,7 +222,7 @@
 	}
 
 	/* Device specific setup. */
-	if (dev->neigh_setup && dev->neigh_setup(n) < 0) {
+	if (n->parms && n->parms->neigh_setup && n->parms->neigh_setup(n) < 0) {
 		neigh_destroy(n);
 		return NULL;
 	}
@@ -632,7 +632,6 @@
 		lladdr = neigh->ha;
 	}
 
-	neigh->used = jiffies;
 	neigh_sync(neigh);
 	old = neigh->nud_state;
 	if (new&NUD_CONNECTED)
@@ -839,17 +838,23 @@
 }
 
 
-struct neigh_parms *neigh_parms_alloc(struct neigh_table *tbl)
+struct neigh_parms *neigh_parms_alloc(struct device *dev, struct neigh_table *tbl)
 {
 	struct neigh_parms *p;
 	p = kmalloc(sizeof(*p), GFP_KERNEL);
 	if (p) {
 		memcpy(p, &tbl->parms, sizeof(*p));
+		p->tbl = tbl;
 		p->reachable_time = neigh_rand_reach_time(p->base_reachable_time);
-		start_bh_atomic();
+		if (dev && dev->neigh_setup) {
+			if (dev->neigh_setup(dev, p)) {
+				kfree(p);
+				return NULL;
+			}
+		}
 		p->next = tbl->parms.next;
+		/* ATOMIC_SET */
 		tbl->parms.next = p;
-		end_bh_atomic();
 	}
 	return p;
 }
@@ -862,9 +867,8 @@
 		return;
 	for (p = &tbl->parms.next; *p; p = &(*p)->next) {
 		if (*p == parms) {
-			start_bh_atomic();
+			/* ATOMIC_SET */
 			*p = parms->next;
-			end_bh_atomic();
 #ifdef CONFIG_SYSCTL
 			neigh_sysctl_unregister(parms);
 #endif
@@ -1055,7 +1059,7 @@
 
 nlmsg_failure:
 rtattr_failure:
-	skb_put(skb, b - skb->tail);
+	skb_trim(skb, b - skb->data);
 	return -1;
 }
 
@@ -1188,13 +1192,13 @@
          &proc_dointvec},
 	{NET_NEIGH_REACHABLE_TIME, "base_reachable_time",
          NULL, sizeof(int), 0644, NULL,
-         &proc_dointvec},
+         &proc_dointvec_jiffies},
 	{NET_NEIGH_DELAY_PROBE_TIME, "delay_first_probe_time",
          NULL, sizeof(int), 0644, NULL,
-         &proc_dointvec},
+         &proc_dointvec_jiffies},
 	{NET_NEIGH_GC_STALE_TIME, "gc_stale_time",
          NULL, sizeof(int), 0644, NULL,
-         &proc_dointvec},
+         &proc_dointvec_jiffies},
 	{NET_NEIGH_UNRES_QLEN, "unres_qlen",
          NULL, sizeof(int), 0644, NULL,
          &proc_dointvec},
@@ -1212,7 +1216,7 @@
          &proc_dointvec},
 	{NET_NEIGH_GC_INTERVAL, "gc_interval",
          NULL, sizeof(int), 0644, NULL,
-         &proc_dointvec},
+         &proc_dointvec_jiffies},
 	{NET_NEIGH_GC_THRESH1, "gc_thresh1",
          NULL, sizeof(int), 0644, NULL,
          &proc_dointvec},
@@ -1230,16 +1234,15 @@
 	{{CTL_NET, "net", NULL, 0, 0555, NULL},{0}}
 };
 
-void * neigh_sysctl_register(struct device *dev, struct neigh_parms *p,
-			     int p_id, int pdev_id, char *p_name)
+int neigh_sysctl_register(struct device *dev, struct neigh_parms *p,
+			  int p_id, int pdev_id, char *p_name)
 {
 	struct neigh_sysctl_table *t;
 
 	t = kmalloc(sizeof(*t), GFP_KERNEL);
 	if (t == NULL)
-		return NULL;
+		return -ENOBUFS;
 	memcpy(t, &neigh_sysctl_template, sizeof(*t));
-	t->neigh_vars[0].data = &p->mcast_probes;
 	t->neigh_vars[1].data = &p->ucast_probes;
 	t->neigh_vars[2].data = &p->app_probes;
 	t->neigh_vars[3].data = &p->retrans_time;
@@ -1274,9 +1277,10 @@
 	t->sysctl_header = register_sysctl_table(t->neigh_root_dir, 0);
 	if (t->sysctl_header == NULL) {
 		kfree(t);
-		return NULL;
+		return -ENOBUFS;
 	}
-	return t;
+	p->sysctl_table = t;
+	return 0;
 }
 
 void neigh_sysctl_unregister(struct neigh_parms *p)
diff -u --recursive --new-file v2.1.88/linux/net/core/rtnetlink.c linux/net/core/rtnetlink.c
--- v2.1.88/linux/net/core/rtnetlink.c	Thu Feb 12 20:56:14 1998
+++ linux/net/core/rtnetlink.c	Sun Mar  1 14:40:40 1998
@@ -74,81 +74,29 @@
 #define _X	2	/* exclusive access to tables required */
 #define _G	4	/* GET request */
 
-static unsigned char rtm_properties[RTM_MAX-RTM_BASE+1] =
+static const int rtm_min[(RTM_MAX+1-RTM_BASE)/4] =
 {
-	_S|_X,		/* RTM_NEWLINK */
-	_S|_X,		/* RTM_DELLINK */
-	_G,		/* RTM_GETLINK */
-	0,
-
-	_S|_X,		/* RTM_NEWADDR */
-	_S|_X,		/* RTM_DELADDR */
-	_G,		/* RTM_GETADDR */
-	0,
-
-	_S|_X,		/* RTM_NEWROUTE */
-	_S|_X,		/* RTM_DELROUTE */
-	_G,		/* RTM_GETROUTE */
-	0,
-
-	_S|_X,		/* RTM_NEWNEIGH */
-	_S|_X,		/* RTM_DELNEIGH */
-	_G,		/* RTM_GETNEIGH */
-	0,
-
-	_S|_X, 		/* RTM_NEWRULE */
-	_S|_X,		/* RTM_DELRULE */
-	_G,		/* RTM_GETRULE */
-	0
+	NLMSG_LENGTH(sizeof(struct ifinfomsg)),
+	NLMSG_LENGTH(sizeof(struct ifaddrmsg)),
+	NLMSG_LENGTH(sizeof(struct rtmsg)),
+	NLMSG_LENGTH(sizeof(struct ndmsg)),
+	NLMSG_LENGTH(sizeof(struct rtmsg)),
+	NLMSG_LENGTH(sizeof(struct tcmsg)),
+	NLMSG_LENGTH(sizeof(struct tcmsg)),
+	NLMSG_LENGTH(sizeof(struct tcmsg))
 };
 
-static int rtnetlink_get_rta(struct kern_rta *rta, struct rtattr *attr, int attrlen)
+static const int rta_max[(RTM_MAX+1-RTM_BASE)/4] =
 {
-	void **rta_data = (void**)rta;
-
-	while (RTA_OK(attr, attrlen)) {
-		int type = attr->rta_type;
-		if (type != RTA_UNSPEC) {
-			if (type > RTA_MAX)
-				return -EINVAL;
-			rta_data[type-1] = RTA_DATA(attr);
-		}
-		attr = RTA_NEXT(attr, attrlen);
-	}
-	return 0;
-}
-
-static int rtnetlink_get_ifa(struct kern_ifa *ifa, struct rtattr *attr, int attrlen)
-{
-	void **ifa_data = (void**)ifa;
-
-	while (RTA_OK(attr, attrlen)) {
-		int type = attr->rta_type;
-		if (type != IFA_UNSPEC) {
-			if (type > IFA_MAX)
-				return -EINVAL;
-			ifa_data[type-1] = RTA_DATA(attr);
-		}
-		attr = RTA_NEXT(attr, attrlen);
-	}
-	return 0;
-}
-
-static int rtnetlink_get_ga(struct rtattr **rta, int sz,
-			    struct rtattr *attr, int attrlen)
-{
-	while (RTA_OK(attr, attrlen)) {
-		int type = attr->rta_type;
-		if (type > 0) {
-			if (type > sz)
-				return -EINVAL;
-			rta[type-1] = attr;
-		}
-		attr = RTA_NEXT(attr, attrlen);
-	}
-	return 0;
-}
-
+	IFLA_MAX,
+	IFA_MAX,
+	RTA_MAX,
+	NDA_MAX,
+	RTA_MAX,
+	TCA_MAX,
+	TCA_MAX,
+	TCA_MAX
+};
 
 void __rta_fill(struct sk_buff *skb, int attrtype, int attrlen, const void *data)
 {
@@ -161,6 +109,7 @@
 	memcpy(RTA_DATA(rta), data, attrlen);
 }
 
+#ifdef CONFIG_RTNL_OLD_IFINFO
 static int rtnetlink_fill_ifinfo(struct sk_buff *skb, struct device *dev,
 				 int type, pid_t pid, u32 seq)
 {
@@ -195,9 +144,55 @@
 
 nlmsg_failure:
 rtattr_failure:
-	skb_put(skb, b - skb->tail);
+	skb_trim(skb, b - skb->data);
 	return -1;
 }
+#else
+static int rtnetlink_fill_ifinfo(struct sk_buff *skb, struct device *dev,
+				 int type, pid_t pid, u32 seq)
+{
+	struct ifinfomsg *r;
+	struct nlmsghdr  *nlh;
+	unsigned char	 *b = skb->tail;
+
+	nlh = NLMSG_PUT(skb, pid, seq, type, sizeof(*r));
+	if (pid) nlh->nlmsg_flags |= NLM_F_MULTI;
+	r = NLMSG_DATA(nlh);
+	r->ifi_family = AF_UNSPEC;
+	r->ifi_type = dev->type;
+	r->ifi_index = dev->ifindex;
+	r->ifi_flags = dev->flags;
+	r->ifi_change = ~0U;
+
+	RTA_PUT(skb, IFLA_IFNAME, strlen(dev->name)+1, dev->name);
+	if (dev->addr_len) {
+		RTA_PUT(skb, IFLA_ADDRESS, dev->addr_len, dev->dev_addr);
+		RTA_PUT(skb, IFLA_BROADCAST, dev->addr_len, dev->broadcast);
+	}
+	if (1) {
+		unsigned mtu = dev->mtu;
+		RTA_PUT(skb, IFLA_MTU, sizeof(mtu), &mtu);
+	}
+	if (dev->ifindex != dev->iflink)
+		RTA_PUT(skb, IFLA_LINK, sizeof(int), &dev->iflink);
+	if (dev->qdisc_sleeping->ops)
+		RTA_PUT(skb, IFLA_QDISC,
+			strlen(dev->qdisc_sleeping->ops->id) + 1,
+			dev->qdisc_sleeping->ops->id);
+	if (dev->get_stats) {
+		struct net_device_stats *stats = dev->get_stats(dev);
+		if (stats)
+			RTA_PUT(skb, IFLA_STATS, sizeof(*stats), stats);
+	}
+	nlh->nlmsg_len = skb->tail - b;
+	return skb->len;
+
+nlmsg_failure:
+rtattr_failure:
+	skb_trim(skb, b - skb->data);
+	return -1;
+}
+#endif
 
 int rtnetlink_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb)
 {
@@ -242,12 +237,15 @@
 	return skb->len;
 }
 
-
 void rtmsg_ifinfo(int type, struct device *dev)
 {
 	struct sk_buff *skb;
+#ifdef CONFIG_RTNL_OLD_IFINFO
 	int size = NLMSG_SPACE(sizeof(struct ifinfomsg)+
 			       RTA_LENGTH(sizeof(struct net_device_stats)));
+#else
+	int size = NLMSG_GOODSIZE;
+#endif
 
 	skb = alloc_skb(size, GFP_KERNEL);
 	if (!skb)
@@ -273,49 +271,68 @@
 extern __inline__ int
 rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, int *errp)
 {
-	union {
-		struct kern_rta rta;
-		struct kern_ifa ifa;
-		struct rtattr	*ga[RTA_MAX-1];
-	} u;
-	struct rtmsg *rtm;
-	struct ifaddrmsg *ifm;
-	struct ndmsg *ndm;
+	struct rtnetlink_link *link;
+	struct rtnetlink_link *link_tab;
+	struct rtattr	*rta[RTATTR_MAX];
+
 	int exclusive = 0;
+	int sz_idx, kind;
+	int min_len;
 	int family;
 	int type;
 	int err;
 
+	/* Only requests are handled by kernel now */
 	if (!(nlh->nlmsg_flags&NLM_F_REQUEST))
 		return 0;
+
 	type = nlh->nlmsg_type;
+
+	/* A control message: ignore them */
 	if (type < RTM_BASE)
 		return 0;
+
+	/* Unknown message: reply with EINVAL */
 	if (type > RTM_MAX)
 		goto err_inval;
 
+	type -= RTM_BASE;
+
+	/* All the messages must have at least 1 byte length */
 	if (nlh->nlmsg_len < NLMSG_LENGTH(sizeof(struct rtgenmsg)))
 		return 0;
+
 	family = ((struct rtgenmsg*)NLMSG_DATA(nlh))->rtgen_family;
-	if (family > NPROTO || rtnetlink_links[family] == NULL) {
+	if (family > NPROTO) {
 		*errp = -EAFNOSUPPORT;
 		return -1;
 	}
-	if (rtm_properties[type-RTM_BASE]&_S) {
-		if (NETLINK_CREDS(skb)->uid) {
-			*errp = -EPERM;
-			return -1;
-		}
+
+	link_tab = rtnetlink_links[family];
+	if (link_tab == NULL)
+		link_tab = rtnetlink_links[AF_UNSPEC];
+	link = &link_tab[type];
+
+	sz_idx = type>>2;
+	kind = type&3;
+
+	if (kind != 2 && NETLINK_CREDS(skb)->uid) {
+		*errp = -EPERM;
+		return -1;
 	}
-	if (rtm_properties[type-RTM_BASE]&_G && nlh->nlmsg_flags&NLM_F_DUMP) {
-		if (rtnetlink_links[family][type-RTM_BASE].dumpit == NULL)
+
+	if (kind == 2 && nlh->nlmsg_flags&NLM_F_DUMP) {
+		if (link->dumpit == NULL)
+			link = &(rtnetlink_links[AF_UNSPEC][type]);
+
+		if (link->dumpit == NULL)
 			goto err_inval;
 
 		/* Super-user locks all the tables to get atomic snapshot */
 		if (NETLINK_CREDS(skb)->uid == 0 && nlh->nlmsg_flags&NLM_F_ATOMIC)
 			atomic_inc(&rtnl_rlockct);
 		if ((*errp = netlink_dump_start(rtnl, skb, nlh,
-						rtnetlink_links[family][type-RTM_BASE].dumpit,
+						link->dumpit,
 						rtnetlink_done)) != 0) {
 			if (NETLINK_CREDS(skb)->uid == 0 && nlh->nlmsg_flags&NLM_F_ATOMIC)
 				atomic_dec(&rtnl_rlockct);
@@ -324,68 +341,41 @@
 		skb_pull(skb, NLMSG_ALIGN(nlh->nlmsg_len));
 		return -1;
 	}
-	if (rtm_properties[type-RTM_BASE]&_X) {
+
+	if (kind != 2) {
 		if (rtnl_exlock_nowait()) {
 			*errp = 0;
 			return -1;
 		}
 		exclusive = 1;
 	}
-	
-	memset(&u, 0, sizeof(u));
 
-	switch (nlh->nlmsg_type) {
-	case RTM_NEWROUTE:
-	case RTM_DELROUTE:
-	case RTM_GETROUTE:
-	case RTM_NEWRULE:
-	case RTM_DELRULE:
-	case RTM_GETRULE:
-		rtm = NLMSG_DATA(nlh);
-		if (nlh->nlmsg_len < sizeof(*rtm))
-			goto err_inval;
-
-		if (rtm->rtm_optlen &&
-		    rtnetlink_get_rta(&u.rta, RTM_RTA(rtm), rtm->rtm_optlen) < 0)
-			goto err_inval;
-		break;
-
-	case RTM_NEWADDR:
-	case RTM_DELADDR:
-	case RTM_GETADDR:
-		ifm = NLMSG_DATA(nlh);
-		if (nlh->nlmsg_len < sizeof(*ifm))
-			goto err_inval;
+	memset(&rta, 0, sizeof(rta));
 
-		if (nlh->nlmsg_len > NLMSG_LENGTH(sizeof(*ifm)) &&
-		    rtnetlink_get_ifa(&u.ifa, IFA_RTA(ifm),
-				      nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*ifm))) < 0)
-			goto err_inval;
-		break;
-	case RTM_NEWNEIGH:
-	case RTM_DELNEIGH:
-	case RTM_GETNEIGH:
-		ndm = NLMSG_DATA(nlh);
-		if (nlh->nlmsg_len < sizeof(*ndm))
-			goto err_inval;
-
-		if (nlh->nlmsg_len > NLMSG_LENGTH(sizeof(*ndm)) &&
-		    rtnetlink_get_ga(u.ga, NDA_MAX, NDA_RTA(ndm),
-				      nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*ndm))) < 0)
-			goto err_inval;
-		break;
-
-	case RTM_NEWLINK:
-	case RTM_DELLINK:
-	case RTM_GETLINK:
-		/* Not urgent and even not necessary */
-	default:
+	min_len = rtm_min[sz_idx];
+	if (nlh->nlmsg_len < min_len)
 		goto err_inval;
+
+	if (nlh->nlmsg_len > min_len) {
+		int attrlen = nlh->nlmsg_len - NLMSG_ALIGN(min_len);
+		struct rtattr *attr = (void*)nlh + NLMSG_ALIGN(min_len);
+
+		while (RTA_OK(attr, attrlen)) {
+			unsigned flavor = attr->rta_type;
+			if (flavor) {
+				if (flavor > rta_max[sz_idx])
+					goto err_inval;
+				rta[flavor-1] = attr;
+			}
+			attr = RTA_NEXT(attr, attrlen);
+		}
 	}
 
-	if (rtnetlink_links[family][type-RTM_BASE].doit == NULL)
+	if (link->doit == NULL)
+		link = &(rtnetlink_links[AF_UNSPEC][type]);
+	if (link->doit == NULL)
 		goto err_inval;
-	err = rtnetlink_links[family][type-RTM_BASE].doit(skb, nlh, (void *)&u);
+	err = link->doit(skb, nlh, (void *)&rta);
 
 	if (exclusive)
 		rtnl_exunlock();
@@ -480,8 +470,8 @@
 	{ NULL,			rtnetlink_dump_all,	},
 	{ NULL,			NULL,			},
 
-	{ NULL,			NULL,			},
-	{ NULL,			NULL,			},
+	{ neigh_add,		NULL,			},
+	{ neigh_delete,		NULL,			},
 	{ NULL,			neigh_dump_info,	},
 	{ NULL,			NULL,			},
 
diff -u --recursive --new-file v2.1.88/linux/net/core/scm.c linux/net/core/scm.c
--- v2.1.88/linux/net/core/scm.c	Wed Feb  4 11:36:02 1998
+++ linux/net/core/scm.c	Mon Mar  2 12:02:26 1998
@@ -106,14 +106,22 @@
 void __scm_destroy(struct scm_cookie *scm)
 {
 	struct scm_fp_list *fpl = scm->fp;
+	struct file *file;
 	int i;
 
 	if (fpl) {
 		scm->fp = NULL;
 		for (i=fpl->count-1; i>=0; i--)
-			close_fp(fpl->fp[i]);
+			fput(fpl->fp[i]);
 		kfree(fpl);
 	}
+
+	file = scm->file;
+	if (file) {
+		scm->sock = NULL;
+		scm->file = NULL;
+		fput(file);
+	}
 }
 
 
@@ -126,11 +134,10 @@
 
 int __scm_send(struct socket *sock, struct msghdr *msg, struct scm_cookie *p)
 {
-	int err;
 	struct cmsghdr *cmsg;
 	struct file *file;
-	int acc_fd;
-	unsigned scm_flags=0;
+	int acc_fd, err;
+	unsigned int scm_flags=0;
 
 	for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg))
 	{
@@ -162,14 +169,19 @@
 			memcpy(&acc_fd, CMSG_DATA(cmsg), sizeof(int));
 			p->sock = NULL;
 			if (acc_fd != -1) {
-				if (acc_fd < 0 || acc_fd >= NR_OPEN ||
-				    (file=current->files->fd[acc_fd])==NULL)
-					return -EBADF;
-				if (!file->f_dentry->d_inode || !file->f_dentry->d_inode->i_sock)
-					return -ENOTSOCK;
+				err = -EBADF;
+				file = fget(acc_fd);
+				if (!file)
+					goto error;
+				p->file = file;
+				err = -ENOTSOCK;
+				if (!file->f_dentry->d_inode ||
+				    !file->f_dentry->d_inode->i_sock)
+					goto error;
 				p->sock = &file->f_dentry->d_inode->u.socket_i;
+				err = -EINVAL;
 				if (p->sock->state != SS_UNCONNECTED) 
-					return -EINVAL;
+					goto error;
 			}
 			scm_flags |= MSG_SYN;
 			break;
diff -u --recursive --new-file v2.1.88/linux/net/core/skbuff.c linux/net/core/skbuff.c
--- v2.1.88/linux/net/core/skbuff.c	Thu Feb 12 20:56:14 1998
+++ linux/net/core/skbuff.c	Tue Feb 24 22:08:02 1998
@@ -128,7 +128,7 @@
 	}
 
 	/*
-	 *	FIXME: We could do with an architecture dependant
+	 *	FIXME: We could do with an architecture dependent
 	 *	'alignment mask'.
 	 */
 	 
diff -u --recursive --new-file v2.1.88/linux/net/core/sock.c linux/net/core/sock.c
--- v2.1.88/linux/net/core/sock.c	Thu Feb 12 20:56:14 1998
+++ linux/net/core/sock.c	Thu Mar  5 12:09:58 1998
@@ -298,31 +298,19 @@
 				sk->bound_dev_if = 0;
 			}
 			else {
-				if (copy_from_user(&req, optval, sizeof(req)) < 0)
+				if (copy_from_user(&req, optval, sizeof(req)))
 					return -EFAULT;
 
 				/* Remove any cached route for this socket. */
-				if (sk->dst_cache) {
-					ip_rt_put((struct rtable*)sk->dst_cache);
-					sk->dst_cache = NULL;
-				}
+				dst_release(xchg(&sk->dst_cache, NULL));
 
 				if (req.ifr_ifrn.ifrn_name[0] == '\0') {
 					sk->bound_dev_if = 0;
-				}
-				else {
+				} else {
 					struct device *dev = dev_get(req.ifr_ifrn.ifrn_name);
 					if (!dev)
 						return -EINVAL;
 					sk->bound_dev_if = dev->ifindex;
-					if (sk->daddr) {
-						int ret;
-						ret = ip_route_output((struct rtable**)&sk->dst_cache,
-								     sk->daddr, sk->saddr,
-								     sk->ip_tos, sk->bound_dev_if);
-						if (ret)
-							return ret;
-					}
 				}
 			}
 			return 0;
@@ -580,12 +568,8 @@
 		 */
 		atomic_add(size, &sk->wmem_alloc);
 		mem = kmalloc(size, priority);
-		if (mem) {
-			/* Recheck because kmalloc might sleep */
-			if (atomic_read(&sk->wmem_alloc)+size < sk->sndbuf)
-				return mem; 
-			kfree_s(mem, size);
-		} 
+		if (mem)
+			return mem; 
 		atomic_sub(size, &sk->wmem_alloc);
 	}
 	return mem;
@@ -910,7 +894,7 @@
 	return -EOPNOTSUPP;
 }
 
-unsigned int sock_no_poll(struct socket *sock, poll_table *pt)
+unsigned int sock_no_poll(struct file * file, struct socket *sock, poll_table *pt)
 {
 	return -EOPNOTSUPP;
 }
diff -u --recursive --new-file v2.1.88/linux/net/core/sysctl_net_core.c linux/net/core/sysctl_net_core.c
--- v2.1.88/linux/net/core/sysctl_net_core.c	Mon Jan 12 15:28:25 1998
+++ linux/net/core/sysctl_net_core.c	Sun Mar  1 14:40:40 1998
@@ -13,6 +13,8 @@
 
 extern int netdev_max_backlog;
 extern int netdev_fastroute;
+extern int net_msg_cost;
+extern int net_msg_burst;
 
 extern __u32 sysctl_wmem_max;
 extern __u32 sysctl_rmem_max;
@@ -45,6 +47,12 @@
 	 &netdev_fastroute, sizeof(int), 0644, NULL,
 	 &proc_dointvec},
 #endif
+	{NET_CORE_MSG_COST, "message_cost",
+	 &net_msg_cost, sizeof(int), 0644, NULL,
+	 &proc_dointvec_jiffies},
+	{NET_CORE_MSG_BURST, "message_burst",
+	 &net_msg_burst, sizeof(int), 0644, NULL,
+	 &proc_dointvec_jiffies},
 	{ 0 }
 };
 #endif
diff -u --recursive --new-file v2.1.88/linux/net/core/utils.c linux/net/core/utils.c
--- v2.1.88/linux/net/core/utils.c	Mon Jan 12 15:28:25 1998
+++ linux/net/core/utils.c	Sun Mar  1 14:40:40 1998
@@ -35,6 +35,8 @@
 	net_random();
 }
 
+int net_msg_cost = 5*HZ;
+int net_msg_burst = 10*5*HZ;
 
 /* 
  * This enforces a rate limit: not more than one kernel message
@@ -44,16 +46,21 @@
  */ 
 int net_ratelimit(void)
 {
+	static unsigned long toks = 10*5*HZ;
 	static unsigned long last_msg; 
 	static int missed;
-	
-	if ((jiffies - last_msg) >= 5*HZ) {
-		if (missed)	
-			printk(KERN_WARNING "ipv4: (%d messages suppressed. Flood?)\n", missed);
-		missed = 0; 
-		last_msg = jiffies;
+	unsigned long now = jiffies;
+
+	toks += now - xchg(&last_msg, now);
+	if (toks > net_msg_burst)
+		toks = net_msg_burst;
+	if (toks >= net_msg_cost) {
+		toks -= net_msg_cost;
+		if (missed)
+			printk(KERN_WARNING "NET: %d messages suppressed.\n", missed);
+		missed = 0;
 		return 1;
 	}
 	missed++; 
-	return 0; 
+	return 0;
 }
diff -u --recursive --new-file v2.1.88/linux/net/ipv4/af_inet.c linux/net/ipv4/af_inet.c
--- v2.1.88/linux/net/ipv4/af_inet.c	Thu Feb 12 20:56:14 1998
+++ linux/net/ipv4/af_inet.c	Thu Feb 26 20:17:27 1998
@@ -836,13 +836,13 @@
 }
 
 
-unsigned int inet_poll(struct socket *sock, poll_table *wait)
+unsigned int inet_poll(struct file * file, struct socket *sock, poll_table *wait)
 {
 	struct sock *sk = sock->sk;
 
 	if (sk->prot->poll == NULL)
 		return(0);
-	return sk->prot->poll(sock, wait);
+	return sk->prot->poll(file, sock, wait);
 }
 
 /*
diff -u --recursive --new-file v2.1.88/linux/net/ipv4/arp.c linux/net/ipv4/arp.c
--- v2.1.88/linux/net/ipv4/arp.c	Mon Feb 23 18:12:12 1998
+++ linux/net/ipv4/arp.c	Sun Mar  1 14:43:44 1998
@@ -53,6 +53,7 @@
  *              Jonathan Layes  :       Added arpd support through kerneld 
  *                                      message queue (960314)
  *		Mike Shaver	:	/proc/sys/net/ipv4/arp_* support
+ *		Mike McLagan    :	Routing by source
  *		Stuart Cheshire	:	Metricom and grat arp fixes
  *					*** FOR 2.1 clean this up ***
  *		Lawrence V. Stefani: (08/12/96) Added FDDI support.
@@ -162,17 +163,18 @@
 	ip_acct_output
 };
 
-#if defined(CONFIG_AX25) || defined(CONFIG_AX25)
-static struct neigh_ops arp_broken_ops =
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25) || \
+    defined(CONFIG_SHAPER) || defined(CONFIG_SHAPER_MODULE)
+struct neigh_ops arp_broken_ops =
 {
 	AF_INET,
 	NULL,
-	NULL,
-	NULL,
-	neigh_compat_output,
-	neigh_compat_output,
+	arp_solicit,
+	arp_error_report,
 	neigh_compat_output,
 	neigh_compat_output,
+	ip_acct_output,
+	ip_acct_output,
 };
 #endif
 
@@ -186,7 +188,8 @@
 	NULL,
 	NULL,
 	parp_redo,
-        { NULL, NULL, 30*HZ, 1*HZ, 60*HZ, 30*HZ, 5*HZ, 3, 3, 0, 3, 1*HZ, (8*HZ)/10, 1*HZ, 64 },
+        { NULL, NULL, &arp_tbl, 0, NULL, NULL,
+		  30*HZ, 1*HZ, 60*HZ, 30*HZ, 5*HZ, 3, 3, 0, 3, 1*HZ, (8*HZ)/10, 1*HZ, 64 },
 	30*HZ, 128, 512, 1024,
 };
 
@@ -542,7 +545,8 @@
  *	is not from an IP number.  We can't currently handle this, so toss
  *	it. 
  */  
-	if (arp->ar_hln != dev->addr_len    || 
+	if (in_dev == NULL ||
+	    arp->ar_hln != dev->addr_len    || 
 	    dev->flags & IFF_NOARP          ||
 	    skb->pkt_type == PACKET_OTHERHOST ||
 	    skb->pkt_type == PACKET_LOOPBACK ||
@@ -588,6 +592,12 @@
 #endif
 	}
 
+	/* Undertsand only these message types */
+
+	if (arp->ar_op != __constant_htons(ARPOP_REPLY) &&
+	    arp->ar_op != __constant_htons(ARPOP_REQUEST))
+		goto out;
+
 /*
  *	Extract fields
  */
@@ -621,21 +631,29 @@
  *  and in the case of requests for us we add the requester to the arp 
  *  cache.
  */
-	switch (arp->ar_op) {
-	case __constant_htons(ARPOP_REQUEST):
-		if (ip_route_input(skb, tip, sip, 0, dev))
-			goto out;
+
+	/* Special case: IPv4 duplicate address detection packet (RFC2131) */
+	if (sip == 0) {
+		if (arp->ar_op == __constant_htons(ARPOP_REQUEST) &&
+		    inet_addr_type(tip) == RTN_LOCAL)
+			arp_send(ARPOP_REPLY,ETH_P_ARP,tip,dev,tip,sha,dev->dev_addr,dev->dev_addr);
+		goto out;
+	}
+
+	if (arp->ar_op == __constant_htons(ARPOP_REQUEST) &&
+	    ip_route_input(skb, tip, sip, 0, dev) == 0) {
+
 		rt = (struct rtable*)skb->dst;
 		addr_type = rt->rt_type;
 
 		if (addr_type == RTN_LOCAL) {
-			struct neighbour *n;
 			n = neigh_event_ns(&arp_tbl, sha, &sip, dev);
 			if (n) {
 				arp_send(ARPOP_REPLY,ETH_P_ARP,sip,dev,tip,sha,dev->dev_addr,sha);
 				neigh_release(n);
 			}
-		} else if (in_dev && IN_DEV_FORWARD(in_dev)) {
+			goto out;
+		} else if (IN_DEV_FORWARD(in_dev)) {
 			if ((rt->rt_flags&RTCF_DNAT) ||
 			    (addr_type == RTN_UNICAST  && rt->u.dst.dev != dev &&
 			     (IN_DEV_PROXY_ARP(in_dev) || pneigh_lookup(&arp_tbl, &tip, dev, 0)))) {
@@ -650,26 +668,46 @@
 					pneigh_enqueue(&arp_tbl, in_dev->arp_parms, skb);
 					return 0;
 				}
+				goto out;
 			}
 		}
-		break;
+	}
 
-	case __constant_htons(ARPOP_REPLY):
-		if (inet_addr_type(sip) != RTN_UNICAST)
-			goto out;
+	/* Update our ARP tables */
+
+	n = __neigh_lookup(&arp_tbl, &sip, dev, 0);
+
+#ifdef CONFIG_IP_ACCEPT_UNSOLICITED_ARP
+	/* Unsolicited ARP is not accepted by default.
+	   It is possible, that this option should be enabled for some
+	   devices (strip is candidate)
+	 */
+	if (n == NULL &&
+	    arp->ar_op == __constant_htons(ARPOP_REPLY) &&
+	    inet_addr_type(sip) == RTN_UNICAST)
 		n = __neigh_lookup(&arp_tbl, &sip, dev, -1);
-		if (n) {
-			int state = NUD_REACHABLE;
-			int override = 1;
-			if (jiffies - n->updated < n->parms->locktime &&
-			    jiffies - n->updated >= 0)
-				override = 0;
-			if (skb->pkt_type != PACKET_HOST)
-				state = NUD_STALE;
-			neigh_update(n, sha, state, override, 1);
-			neigh_release(n);
-		}
-		break;
+#endif
+
+	if (n) {
+		int state = NUD_REACHABLE;
+		int override = 0;
+
+		/* If several different ARP replies follows back-to-back,
+		   use the FIRST one. It is possible, if several proxy
+		   agents are active. Taking the first reply prevents
+		   arp trashing and chooses the fastest router.
+		 */
+		if (jiffies - n->updated >= n->parms->locktime)
+			override = 1;
+
+		/* Broadcast replies and request packets
+		   do not assert neighbour reachability.
+		 */
+		if (arp->ar_op != __constant_htons(ARPOP_REPLY) ||
+		    skb->pkt_type != PACKET_HOST)
+			state = NUD_STALE;
+		neigh_update(n, sha, state, override, 1);
+		neigh_release(n);
 	}
 
 out:
@@ -708,11 +746,11 @@
 			return 0;
 		}
 		if (dev == NULL) {
-			ipv4_config.proxy_arp = 1;
+			ipv4_devconf.proxy_arp = 1;
 			return 0;
 		}
 		if (dev->ip_ptr) {
-			((struct in_device*)dev->ip_ptr)->flags |= IFF_IP_PROXYARP;
+			((struct in_device*)dev->ip_ptr)->cnf.proxy_arp = 1;
 			return 0;
 		}
 		return -ENXIO;
@@ -751,7 +789,7 @@
 {
 	unsigned flags = 0;
 	if (neigh->nud_state&NUD_PERMANENT)
-		flags = ATF_PERM;
+		flags = ATF_PERM|ATF_COM;
 	else if (neigh->nud_state&NUD_VALID)
 		flags = ATF_COM;
 	return flags;
@@ -793,11 +831,11 @@
 			return pneigh_delete(&arp_tbl, &ip, dev);
 		if (mask == 0) {
 			if (dev == NULL) {
-				ipv4_config.proxy_arp = 0;
+				ipv4_devconf.proxy_arp = 0;
 				return 0;
 			}
 			if (dev->ip_ptr) {
-				((struct in_device*)dev->ip_ptr)->flags &= ~IFF_IP_PROXYARP;
+				((struct in_device*)dev->ip_ptr)->cnf.proxy_arp = 0;
 				return 0;
 			}
 			return -ENXIO;
@@ -1049,7 +1087,7 @@
 	proc_net_register(&proc_net_arp);
 #endif
 #ifdef CONFIG_SYSCTL
-	arp_tbl.parms.sysctl_table = neigh_sysctl_register(NULL, &arp_tbl.parms, NET_IPV4, NET_IPV4_NEIGH, "ipv4");
+	neigh_sysctl_register(NULL, &arp_tbl.parms, NET_IPV4, NET_IPV4_NEIGH, "ipv4");
 #endif
 }
 
diff -u --recursive --new-file v2.1.88/linux/net/ipv4/devinet.c linux/net/ipv4/devinet.c
--- v2.1.88/linux/net/ipv4/devinet.c	Mon Feb 23 18:12:12 1998
+++ linux/net/ipv4/devinet.c	Sun Mar  1 14:40:40 1998
@@ -57,6 +57,9 @@
 #include <net/route.h>
 #include <net/ip_fib.h>
 
+struct ipv4_devconf ipv4_devconf = { 1, 1, 1, 1, 0, };
+static struct ipv4_devconf ipv4_devconf_dflt = { 1, 1, 1, 1, 1, };
+
 #ifdef CONFIG_RTNETLINK
 static void rtmsg_ifa(int event, struct in_ifaddr *);
 #else
@@ -65,7 +68,10 @@
 
 static struct notifier_block *inetaddr_chain;
 static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, int destroy);
-
+#ifdef CONFIG_SYSCTL
+static void devinet_sysctl_register(struct in_device *in_dev, struct ipv4_devconf *p);
+static void devinet_sysctl_unregister(struct ipv4_devconf *p);
+#endif
 
 int inet_ifa_count;
 int inet_dev_count;
@@ -98,15 +104,20 @@
 		return NULL;
 	inet_dev_count++;
 	memset(in_dev, 0, sizeof(*in_dev));
+	memcpy(&in_dev->cnf, &ipv4_devconf_dflt, sizeof(in_dev->cnf));
+	in_dev->cnf.sysctl = NULL;
 	in_dev->dev = dev;
-	if ((in_dev->arp_parms = neigh_parms_alloc(&arp_tbl)) == NULL)
-		in_dev->arp_parms = &arp_tbl.parms;
+	if ((in_dev->arp_parms = neigh_parms_alloc(dev, &arp_tbl)) == NULL) {
+		kfree(in_dev);
+		return NULL;
+	}
 #ifdef CONFIG_SYSCTL
-	else
-		in_dev->arp_parms->sysctl_table =
-			neigh_sysctl_register(dev, in_dev->arp_parms, NET_IPV4, NET_IPV4_NEIGH, "ipv4");
+	neigh_sysctl_register(dev, in_dev->arp_parms, NET_IPV4, NET_IPV4_NEIGH, "ipv4");
 #endif
 	dev->ip_ptr = in_dev;
+#ifdef CONFIG_SYSCTL
+	devinet_sysctl_register(in_dev, &in_dev->cnf);
+#endif
 	if (dev->flags&IFF_UP)
 		ip_mc_up(in_dev);
 	return in_dev;
@@ -123,6 +134,9 @@
 		inet_free_ifa(ifa);
 	}
 
+#ifdef CONFIG_SYSCTL
+	devinet_sysctl_unregister(&in_dev->cnf);
+#endif
 	in_dev->dev->ip_ptr = NULL;
 	neigh_parms_release(&arp_tbl, in_dev->arp_parms);
 	kfree(in_dev);
@@ -277,7 +291,7 @@
 int
 inet_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
 {
-	struct kern_ifa  *k_ifa = arg;
+	struct rtattr  **rta = arg;
 	struct in_device *in_dev;
 	struct ifaddrmsg *ifm = NLMSG_DATA(nlh);
 	struct in_ifaddr *ifa, **ifap;
@@ -286,11 +300,11 @@
 		return -EADDRNOTAVAIL;
 
 	for (ifap=&in_dev->ifa_list; (ifa=*ifap)!=NULL; ifap=&ifa->ifa_next) {
-		if ((k_ifa->ifa_local && memcmp(k_ifa->ifa_local, &ifa->ifa_local, 4)) ||
-		    (k_ifa->ifa_label && strcmp(k_ifa->ifa_label, ifa->ifa_label)) ||
-		    (k_ifa->ifa_address &&
+		if ((rta[IFA_LOCAL-1] && memcmp(RTA_DATA(rta[IFA_LOCAL-1]), &ifa->ifa_local, 4)) ||
+		    (rta[IFA_LABEL-1] && strcmp(RTA_DATA(rta[IFA_LABEL-1]), ifa->ifa_label)) ||
+		    (rta[IFA_ADDRESS-1] &&
 		     (ifm->ifa_prefixlen != ifa->ifa_prefixlen ||
-		      !inet_ifa_match(*(u32*)k_ifa->ifa_address, ifa))))
+		      !inet_ifa_match(*(u32*)RTA_DATA(rta[IFA_ADDRESS-1]), ifa))))
 			continue;
 		inet_del_ifa(in_dev, ifap, 1);
 		return 0;
@@ -302,13 +316,13 @@
 int
 inet_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
 {
-	struct kern_ifa *k_ifa = arg;
+	struct rtattr **rta = arg;
 	struct device *dev;
 	struct in_device *in_dev;
 	struct ifaddrmsg *ifm = NLMSG_DATA(nlh);
 	struct in_ifaddr *ifa;
 
-	if (ifm->ifa_prefixlen > 32 || k_ifa->ifa_local == NULL)
+	if (ifm->ifa_prefixlen > 32 || rta[IFA_LOCAL-1] == NULL)
 		return -EINVAL;
 
 	if ((dev = dev_get_by_index(ifm->ifa_index)) == NULL)
@@ -323,21 +337,21 @@
 	if ((ifa = inet_alloc_ifa()) == NULL)
 		return -ENOBUFS;
 
-	if (k_ifa->ifa_address == NULL)
-		k_ifa->ifa_address = k_ifa->ifa_local;
-	memcpy(&ifa->ifa_local, k_ifa->ifa_local, 4);
-	memcpy(&ifa->ifa_address, k_ifa->ifa_address, 4);
+	if (rta[IFA_ADDRESS-1] == NULL)
+		rta[IFA_ADDRESS-1] = rta[IFA_LOCAL-1];
+	memcpy(&ifa->ifa_local, RTA_DATA(rta[IFA_LOCAL-1]), 4);
+	memcpy(&ifa->ifa_address, RTA_DATA(rta[IFA_ADDRESS-1]), 4);
 	ifa->ifa_prefixlen = ifm->ifa_prefixlen;
 	ifa->ifa_mask = inet_make_mask(ifm->ifa_prefixlen);
-	if (k_ifa->ifa_broadcast)
-		memcpy(&ifa->ifa_broadcast, k_ifa->ifa_broadcast, 4);
-	if (k_ifa->ifa_anycast)
-		memcpy(&ifa->ifa_anycast, k_ifa->ifa_anycast, 4);
+	if (rta[IFA_BROADCAST-1])
+		memcpy(&ifa->ifa_broadcast, RTA_DATA(rta[IFA_BROADCAST-1]), 4);
+	if (rta[IFA_ANYCAST-1])
+		memcpy(&ifa->ifa_anycast, RTA_DATA(rta[IFA_ANYCAST-1]), 4);
 	ifa->ifa_flags = ifm->ifa_flags;
 	ifa->ifa_scope = ifm->ifa_scope;
 	ifa->ifa_dev = in_dev;
-	if (k_ifa->ifa_label)
-		memcpy(ifa->ifa_label, k_ifa->ifa_label, IFNAMSIZ);
+	if (rta[IFA_LABEL-1])
+		memcpy(ifa->ifa_label, RTA_DATA(rta[IFA_LABEL-1]), IFNAMSIZ);
 	else
 		memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
 
@@ -408,7 +422,6 @@
 	case SIOCGIFBRDADDR:	/* Get the broadcast address */
 	case SIOCGIFDSTADDR:	/* Get the destination address */
 	case SIOCGIFNETMASK:	/* Get the netmask for the interface */
-	case SIOCGIFPFLAGS:	/* Get per device sysctl controls */	
 		/* Note that this ioctls will not sleep,
 		   so that we do not impose a lock.
 		   One day we will be forced to put shlock here (I mean SMP)
@@ -418,7 +431,6 @@
 		break;
 
 	case SIOCSIFFLAGS:
-	case SIOCSIFPFLAGS:	/* Set per device sysctl controls */	
 		if (!suser())
 			return -EACCES;
 		rtnl_lock();
@@ -478,10 +490,6 @@
 			sin->sin_addr.s_addr = ifa->ifa_mask;
 			goto rarok;
 
-		case SIOCGIFPFLAGS:
-			ifr.ifr_flags = in_dev->flags;
-			goto rarok;
-
 		case SIOCSIFFLAGS:
 #ifdef CONFIG_IP_ALIAS
 			if (colon) {
@@ -497,10 +505,6 @@
 			ret = dev_change_flags(dev, ifr.ifr_flags);
 			break;
 	
-		case SIOCSIFPFLAGS:
-			in_dev->flags = ifr.ifr_flags;
-			break;
-
 		case SIOCSIFADDR:	/* Set interface address (and family) */
 			if (inet_abc_len(sin->sin_addr.s_addr) < 0) {
 				ret = -EINVAL;
@@ -606,7 +610,7 @@
 			done += sizeof(ifr);
 			continue;
 		}
-		if (len < sizeof(ifr))
+		if (len < (int) sizeof(ifr))
 			return done;
 		memset(&ifr, 0, sizeof(struct ifreq));
 		if (ifa->ifa_label)
@@ -736,7 +740,7 @@
 
 nlmsg_failure:
 rtattr_failure:
-	skb_put(skb, b - skb->tail);
+	skb_trim(skb, b - skb->data);
 	return -1;
 }
 
@@ -797,7 +801,7 @@
 {
 	{ NULL,			NULL,			},
 	{ NULL,			NULL,			},
-	{ NULL,			rtnetlink_dump_ifinfo,	},
+	{ NULL,			NULL,			},
 	{ NULL,			NULL,			},
 
 	{ inet_rtm_newaddr,	NULL,			},
@@ -810,9 +814,9 @@
 	{ inet_rtm_getroute,	inet_dump_fib,		},
 	{ NULL,			NULL,			},
 
-	{ neigh_add,		NULL,			},
-	{ neigh_delete,		NULL,			},
-	{ NULL,			neigh_dump_info,	},
+	{ NULL,			NULL,			},
+	{ NULL,			NULL,			},
+	{ NULL,			NULL,			},
 	{ NULL,			NULL,			},
 
 #ifdef CONFIG_IP_MULTIPLE_TABLES
@@ -830,6 +834,145 @@
 
 #endif /* CONFIG_RTNETLINK */
 
+
+#ifdef CONFIG_SYSCTL
+
+void inet_forward_change()
+{
+	struct device *dev;
+	int on = ipv4_devconf.forwarding;
+
+	ipv4_devconf.accept_redirects = !on;
+	ipv4_devconf_dflt.forwarding = on;
+
+	for (dev = dev_base; dev; dev = dev->next) {
+		struct in_device *in_dev = dev->ip_ptr;
+		if (in_dev)
+			in_dev->cnf.forwarding = on;
+	}
+
+	rt_cache_flush(0);
+
+	ip_statistics.IpForwarding = on ? 1 : 2;
+}
+
+static
+int devinet_sysctl_forward(ctl_table *ctl, int write, struct file * filp,
+			   void *buffer, size_t *lenp)
+{
+	int *valp = ctl->data;
+	int val = *valp;
+	int ret;
+
+	ret = proc_dointvec(ctl, write, filp, buffer, lenp);
+
+	if (write && *valp != val) {
+		if (valp == &ipv4_devconf.forwarding)
+			inet_forward_change();
+		else if (valp != &ipv4_devconf_dflt.forwarding)
+			rt_cache_flush(0);
+	}
+
+        return ret;
+}
+
+static struct devinet_sysctl_table
+{
+	struct ctl_table_header *sysctl_header;
+	ctl_table devinet_vars[12];
+	ctl_table devinet_dev[2];
+	ctl_table devinet_conf_dir[2];
+	ctl_table devinet_proto_dir[2];
+	ctl_table devinet_root_dir[2];
+} devinet_sysctl = {
+	NULL,
+	{{NET_IPV4_CONF_FORWARDING, "forwarding",
+         &ipv4_devconf.forwarding, sizeof(int), 0644, NULL,
+         &devinet_sysctl_forward},
+	{NET_IPV4_CONF_MC_FORWARDING, "mc_forwarding",
+         &ipv4_devconf.mc_forwarding, sizeof(int), 0444, NULL,
+         &proc_dointvec},
+	{NET_IPV4_CONF_ACCEPT_REDIRECTS, "accept_redirects",
+         &ipv4_devconf.accept_redirects, sizeof(int), 0644, NULL,
+         &proc_dointvec},
+	{NET_IPV4_CONF_SECURE_REDIRECTS, "secure_redirects",
+         &ipv4_devconf.secure_redirects, sizeof(int), 0644, NULL,
+         &proc_dointvec},
+	{NET_IPV4_CONF_SHARED_MEDIA, "shared_media",
+         &ipv4_devconf.shared_media, sizeof(int), 0644, NULL,
+         &proc_dointvec},
+	{NET_IPV4_CONF_RP_FILTER, "rp_filter",
+         &ipv4_devconf.rp_filter, sizeof(int), 0644, NULL,
+         &proc_dointvec},
+	{NET_IPV4_CONF_SEND_REDIRECTS, "send_redirects",
+         &ipv4_devconf.send_redirects, sizeof(int), 0644, NULL,
+         &proc_dointvec},
+	{NET_IPV4_CONF_ACCEPT_SOURCE_ROUTE, "accept_source_route",
+         &ipv4_devconf.accept_source_route, sizeof(int), 0644, NULL,
+         &proc_dointvec},
+	{NET_IPV4_CONF_PROXY_ARP, "proxy_arp",
+         &ipv4_devconf.proxy_arp, sizeof(int), 0644, NULL,
+         &proc_dointvec},
+	{NET_IPV4_CONF_BOOTP_RELAY, "bootp_relay",
+         &ipv4_devconf.bootp_relay, sizeof(int), 0644, NULL,
+         &proc_dointvec},
+        {NET_IPV4_CONF_LOG_MARTIANS, "log_martians",
+         &ipv4_devconf.log_martians, sizeof(int), 0644, NULL,
+         &proc_dointvec},
+	 {0}},
+
+	{{NET_PROTO_CONF_ALL, "all", NULL, 0, 0555, devinet_sysctl.devinet_vars},{0}},
+	{{NET_IPV4_CONF, "conf", NULL, 0, 0555, devinet_sysctl.devinet_dev},{0}},
+	{{NET_IPV4, "ipv4", NULL, 0, 0555, devinet_sysctl.devinet_conf_dir},{0}},
+	{{CTL_NET, "net", NULL, 0, 0555, devinet_sysctl.devinet_proto_dir},{0}}
+};
+
+static void devinet_sysctl_register(struct in_device *in_dev, struct ipv4_devconf *p)
+{
+	int i;
+	struct device *dev = in_dev ? in_dev->dev : NULL;
+	struct devinet_sysctl_table *t;
+
+	t = kmalloc(sizeof(*t), GFP_KERNEL);
+	if (t == NULL)
+		return;
+	memcpy(t, &devinet_sysctl, sizeof(*t));
+	for (i=0; i<sizeof(t->devinet_vars)/sizeof(t->devinet_vars[0])-1; i++) {
+		t->devinet_vars[i].data += (char*)p - (char*)&ipv4_devconf;
+		t->devinet_vars[i].de = NULL;
+	}
+	if (dev) {
+		t->devinet_dev[0].procname = dev->name;
+		t->devinet_dev[0].ctl_name = dev->ifindex;
+	} else {
+		t->devinet_dev[0].procname = "default";
+		t->devinet_dev[0].ctl_name = NET_PROTO_CONF_DEFAULT;
+	}
+	t->devinet_dev[0].child = t->devinet_vars;
+	t->devinet_dev[0].de = NULL;
+	t->devinet_conf_dir[0].child = t->devinet_dev;
+	t->devinet_conf_dir[0].de = NULL;
+	t->devinet_proto_dir[0].child = t->devinet_conf_dir;
+	t->devinet_proto_dir[0].de = NULL;
+	t->devinet_root_dir[0].child = t->devinet_proto_dir;
+	t->devinet_root_dir[0].de = NULL;
+
+	t->sysctl_header = register_sysctl_table(t->devinet_root_dir, 0);
+	if (t->sysctl_header == NULL)
+		kfree(t);
+}
+
+static void devinet_sysctl_unregister(struct ipv4_devconf *p)
+{
+	if (p->sysctl) {
+		struct devinet_sysctl_table *t = p->sysctl;
+		p->sysctl = NULL;
+		unregister_sysctl_table(t->sysctl_header);
+		kfree(t);
+	}
+}
+#endif
+
 #ifdef CONFIG_IP_PNP_BOOTP
 
 /*
@@ -869,5 +1012,10 @@
 	register_netdevice_notifier(&ip_netdev_notifier);
 #ifdef CONFIG_RTNETLINK
 	rtnetlink_links[AF_INET] = inet_rtnetlink_table;
+#endif
+#ifdef CONFIG_SYSCTL
+	devinet_sysctl.sysctl_header =
+		register_sysctl_table(devinet_sysctl.devinet_root_dir, 0);
+	devinet_sysctl_register(NULL, &ipv4_devconf_dflt);
 #endif
 }
diff -u --recursive --new-file v2.1.88/linux/net/ipv4/fib_frontend.c linux/net/ipv4/fib_frontend.c
--- v2.1.88/linux/net/ipv4/fib_frontend.c	Mon Feb 23 18:12:12 1998
+++ linux/net/ipv4/fib_frontend.c	Sun Mar  1 14:40:40 1998
@@ -93,7 +93,7 @@
 #endif /* CONFIG_IP_MULTIPLE_TABLES */
 
 	if (flushed)
-		rt_cache_flush(RT_FLUSH_DELAY);
+		rt_cache_flush(-1);
 }
 
 
@@ -290,27 +290,51 @@
 
 #ifdef CONFIG_RTNETLINK
 
+static int inet_check_attr(struct rtmsg *r, struct rtattr **rta)
+{
+	int i;
+
+	for (i=1; i<=RTA_MAX; i++) {
+		struct rtattr *attr = rta[i-1];
+		if (attr) {
+			if (RTA_PAYLOAD(attr) < 4)
+				return -EINVAL;
+#ifndef	CONFIG_RTNL_OLD_IFINFO
+			if (i != RTA_MULTIPATH && i != RTA_METRICS)
+#endif
+			rta[i-1] = (struct rtattr*)RTA_DATA(attr);
+		}
+	}
+	return 0;
+}
+
 int inet_rtm_delroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
 {
 	struct fib_table * tb;
-	struct kern_rta *rta = arg;
+	struct rtattr **rta = arg;
 	struct rtmsg *r = NLMSG_DATA(nlh);
 
+	if (inet_check_attr(r, rta))
+		return -EINVAL;
+
 	tb = fib_get_table(r->rtm_table);
 	if (tb)
-		return tb->tb_delete(tb, r, rta, nlh, &NETLINK_CB(skb));
+		return tb->tb_delete(tb, r, (struct kern_rta*)rta, nlh, &NETLINK_CB(skb));
 	return -ESRCH;
 }
 
 int inet_rtm_newroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
 {
 	struct fib_table * tb;
-	struct kern_rta *rta = arg;
+	struct rtattr **rta = arg;
 	struct rtmsg *r = NLMSG_DATA(nlh);
 
+	if (inet_check_attr(r, rta))
+		return -EINVAL;
+
 	tb = fib_new_table(r->rtm_table);
 	if (tb)
-		return tb->tb_insert(tb, r, rta, nlh, &NETLINK_CB(skb));
+		return tb->tb_insert(tb, r, (struct kern_rta*)rta, nlh, &NETLINK_CB(skb));
 	return -ENOBUFS;
 }
 
@@ -370,7 +394,7 @@
 
 	req.nlh.nlmsg_len = sizeof(req);
 	req.nlh.nlmsg_type = cmd;
-	req.nlh.nlmsg_flags = NLM_F_REQUEST|NLM_F_CREATE;
+	req.nlh.nlmsg_flags = NLM_F_REQUEST|NLM_F_CREATE|NLM_F_APPEND;
 	req.nlh.nlmsg_pid = 0;
 	req.nlh.nlmsg_seq = 0;
 
@@ -494,11 +518,11 @@
 	switch (event) {
 	case NETDEV_UP:
 		fib_add_ifaddr(ifa);
-		rt_cache_flush(2*HZ);
+		rt_cache_flush(-1);
 		break;
 	case NETDEV_DOWN:
 		fib_del_ifaddr(ifa);
-		rt_cache_flush(1*HZ);
+		rt_cache_flush(-1);
 		break;
 	}
 	return NOTIFY_DONE;
@@ -520,7 +544,7 @@
 #ifdef CONFIG_IP_ROUTE_MULTIPATH
 		fib_sync_up(dev);
 #endif
-		rt_cache_flush(2*HZ);
+		rt_cache_flush(-1);
 		break;
 	case NETDEV_DOWN:
 		if (fib_sync_down(0, dev, 0))
@@ -538,7 +562,7 @@
 		break;
 	case NETDEV_CHANGEMTU:
 	case NETDEV_CHANGE:
-		rt_cache_flush(1*HZ);
+		rt_cache_flush(0);
 		break;
 	}
 	return NOTIFY_DONE;
diff -u --recursive --new-file v2.1.88/linux/net/ipv4/fib_hash.c linux/net/ipv4/fib_hash.c
--- v2.1.88/linux/net/ipv4/fib_hash.c	Tue Feb 17 13:12:50 1998
+++ linux/net/ipv4/fib_hash.c	Sun Mar  1 14:40:40 1998
@@ -394,6 +394,8 @@
 	    && f->fn_tos == tos
 #endif
 	    ) {
+		struct fib_node **ins_fp;
+
 		state = f->fn_state;
 		if (n->nlmsg_flags&NLM_F_EXCL && !(state&FN_S_ZOMBIE))
 			return -EEXIST;
@@ -412,9 +414,12 @@
 			f->fn_state = 0;
 			fib_release_info(old_fi);
 			if (state&FN_S_ACCESSED)
-				rt_cache_flush(RT_FLUSH_DELAY);
+				rt_cache_flush(-1);
 			return 0;
 		}
+
+		ins_fp = fp;
+
 		for ( ; (f = *fp) != NULL && fn_key_eq(f->fn_key, key)
 #ifdef CONFIG_IP_ROUTE_TOS
 		     && f->fn_tos == tos
@@ -428,12 +433,16 @@
 					f->fn_state = 0;
 					rtmsg_fib(RTM_NEWROUTE, f, z, tb->tb_id, n, req);
 					if (state&FN_S_ACCESSED)
-						rt_cache_flush(RT_FLUSH_DELAY);
+						rt_cache_flush(-1);
 					return 0;
 				}
 				return -EEXIST;
 			}
 		}
+		if (!(n->nlmsg_flags&NLM_F_APPEND)) {
+			fp = ins_fp;
+			f = *fp;
+		}
 	} else {
 		if (!(n->nlmsg_flags&NLM_F_CREATE))
 			return -ENOENT;
@@ -459,14 +468,13 @@
 	 * Insert new entry to the list.
 	 */
 
-	start_bh_atomic();
 	new_f->fn_next = f;
+	/* ATOMIC_SET */
 	*fp = new_f;
-	end_bh_atomic();
 	fz->fz_nent++;
 
 	rtmsg_fib(RTM_NEWROUTE, new_f, z, tb->tb_id, n, req);
-	rt_cache_flush(RT_FLUSH_DELAY);
+	rt_cache_flush(-1);
 	return 0;
 }
 
@@ -541,7 +549,7 @@
 	rtmsg_fib(RTM_DELROUTE, f, z, tb->tb_id, n, req);
 	if (f->fn_state&FN_S_ACCESSED) {
 		f->fn_state &= ~FN_S_ACCESSED;
-		rt_cache_flush(RT_FLUSH_DELAY);
+		rt_cache_flush(-1);
 	}
 	if (++fib_hash_zombies > 128)
 		fib_flush();
diff -u --recursive --new-file v2.1.88/linux/net/ipv4/fib_rules.c linux/net/ipv4/fib_rules.c
--- v2.1.88/linux/net/ipv4/fib_rules.c	Sun Nov 30 14:00:39 1997
+++ linux/net/ipv4/fib_rules.c	Sun Mar  1 14:40:40 1998
@@ -45,10 +45,14 @@
 
 #define FRprintk(a...)
 
+#ifndef CONFIG_RTNL_OLD_IFINFO
+#define RTA_IFNAME RTA_IIF
+#endif
+
 struct fib_rule
 {
 	struct fib_rule *r_next;
-	unsigned	r_preference;
+	u32		r_preference;
 	unsigned char	r_table;
 	unsigned char	r_action;
 	unsigned char	r_dst_len;
@@ -72,19 +76,19 @@
 
 int inet_rtm_delrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
 {
-	struct kern_rta *rta = arg;
+	struct rtattr **rta = arg;
 	struct rtmsg *rtm = NLMSG_DATA(nlh);
 	struct fib_rule *r, **rp;
 
 	for (rp=&fib_rules; (r=*rp) != NULL; rp=&r->r_next) {
-		if ((!rta->rta_src || memcmp(rta->rta_src, &r->r_src, 4) == 0) &&
+		if ((!rta[RTA_SRC-1] || memcmp(RTA_DATA(rta[RTA_SRC-1]), &r->r_src, 4) == 0) &&
 		    rtm->rtm_src_len == r->r_src_len &&
 		    rtm->rtm_dst_len == r->r_dst_len &&
-		    (!rta->rta_dst || memcmp(rta->rta_dst, &r->r_dst, 4) == 0) &&
+		    (!rta[RTA_DST-1] || memcmp(RTA_DATA(rta[RTA_DST-1]), &r->r_dst, 4) == 0) &&
 		    rtm->rtm_tos == r->r_tos &&
 		    rtm->rtm_type == r->r_action &&
-		    (!rta->rta_priority || *rta->rta_priority == r->r_preference) &&
-		    (!rta->rta_ifname || strcmp(rta->rta_ifname, r->r_ifname) == 0) &&
+		    (!rta[RTA_PRIORITY-1] || memcmp(RTA_DATA(rta[RTA_PRIORITY-1]), &r->r_preference, 4) == 0) &&
+		    (!rta[RTA_IFNAME-1] || strcmp(RTA_DATA(rta[RTA_IFNAME-1]), r->r_ifname) == 0) &&
 		    (!rtm->rtm_table || (r && rtm->rtm_table == r->r_table))) {
 			*rp = r->r_next;
 			if (r != &default_rule && r != &main_rule && r != &local_rule)
@@ -110,7 +114,7 @@
 
 int inet_rtm_newrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
 {
-	struct kern_rta *rta = arg;
+	struct rtattr **rta = arg;
 	struct rtmsg *rtm = NLMSG_DATA(nlh);
 	struct fib_rule *r, *new_r, **rp;
 	unsigned char table_id;
@@ -119,6 +123,9 @@
 	    (rtm->rtm_tos & ~IPTOS_TOS_MASK))
 		return -EINVAL;
 
+	if (rta[RTA_IFNAME-1] && RTA_PAYLOAD(rta[RTA_IFNAME-1]) > IFNAMSIZ)
+		return -EINVAL;
+
 	table_id = rtm->rtm_table;
 	if (table_id == RT_TABLE_UNSPEC) {
 		struct fib_table *table;
@@ -133,12 +140,12 @@
 	if (!new_r)
 		return -ENOMEM;
 	memset(new_r, 0, sizeof(*new_r));
-	if (rta->rta_src)
-		memcpy(&new_r->r_src, rta->rta_src, 4);
-	if (rta->rta_dst)
-		memcpy(&new_r->r_dst, rta->rta_dst, 4);
-	if (rta->rta_gw)
-		memcpy(&new_r->r_srcmap, rta->rta_gw, 4);
+	if (rta[RTA_SRC-1])
+		memcpy(&new_r->r_src, RTA_DATA(rta[RTA_SRC-1]), 4);
+	if (rta[RTA_DST-1])
+		memcpy(&new_r->r_dst, RTA_DATA(rta[RTA_DST-1]), 4);
+	if (rta[RTA_GATEWAY-1])
+		memcpy(&new_r->r_srcmap, RTA_DATA(rta[RTA_GATEWAY-1]), 4);
 	new_r->r_src_len = rtm->rtm_src_len;
 	new_r->r_dst_len = rtm->rtm_dst_len;
 	new_r->r_srcmask = inet_make_mask(rtm->rtm_src_len);
@@ -146,14 +153,15 @@
 	new_r->r_tos = rtm->rtm_tos;
 	new_r->r_action = rtm->rtm_type;
 	new_r->r_flags = rtm->rtm_flags;
-	if (rta->rta_priority)
-		new_r->r_preference = *rta->rta_priority;
+	if (rta[RTA_PRIORITY-1])
+		memcpy(&new_r->r_preference, RTA_DATA(rta[RTA_PRIORITY-1]), 4);
 	new_r->r_table = table_id;
-	if (rta->rta_ifname) {
+	if (rta[RTA_IFNAME-1]) {
 		struct device *dev;
-		memcpy(new_r->r_ifname, rta->rta_ifname, IFNAMSIZ);
+		memcpy(new_r->r_ifname, RTA_DATA(rta[RTA_IFNAME-1]), IFNAMSIZ);
+		new_r->r_ifname[IFNAMSIZ-1] = 0;
 		new_r->r_ifindex = -1;
-		dev = dev_get(rta->rta_ifname);
+		dev = dev_get(new_r->r_ifname);
 		if (dev)
 			new_r->r_ifindex = dev->ifindex;
 	}
@@ -314,9 +322,11 @@
 	rtm->rtm_table = r->r_table;
 	rtm->rtm_protocol = 0;
 	rtm->rtm_scope = 0;
+#ifdef CONFIG_RTNL_OLD_IFINFO
 	rtm->rtm_nhs = 0;
-	rtm->rtm_type = r->r_action;
 	rtm->rtm_optlen = 0;
+#endif
+	rtm->rtm_type = r->r_action;
 	rtm->rtm_flags = r->r_flags;
 
 	if (r->r_dst_len)
diff -u --recursive --new-file v2.1.88/linux/net/ipv4/fib_semantics.c linux/net/ipv4/fib_semantics.c
--- v2.1.88/linux/net/ipv4/fib_semantics.c	Mon Jan 12 15:28:27 1998
+++ linux/net/ipv4/fib_semantics.c	Sun Mar  1 14:40:40 1998
@@ -178,11 +178,37 @@
 	return 0;
 }
 
+#ifndef CONFIG_RTNL_OLD_IFINFO
+static int
+fib_count_nexthops(struct rtattr *rta)
+{
+	int nhs = 0;
+	struct rtnexthop *nhp = RTA_DATA(rta);
+	int nhlen = RTA_PAYLOAD(rta);
+
+	while (nhlen >= sizeof(struct rtnexthop)) {
+		if ((nhlen -= nhp->rtnh_len) < 0)
+			return 0;
+		nhs++;
+		nhp = RTNH_NEXT(nhp);
+	};
+	return nhs;
+}
+#endif
+
+#ifdef CONFIG_RTNL_OLD_IFINFO
 static int
 fib_get_nhs(struct fib_info *fi, const struct nlmsghdr *nlh, const struct rtmsg *r)
 {
 	struct rtnexthop *nhp = RTM_RTNH(r);
 	int nhlen = RTM_NHLEN(nlh, r);
+#else
+static int
+fib_get_nhs(struct fib_info *fi, const struct rtattr *rta, const struct rtmsg *r)
+{
+	struct rtnexthop *nhp = RTA_DATA(rta);
+	int nhlen = RTA_PAYLOAD(rta);
+#endif
 
 	change_nexthops(fi) {
 		int attrlen = nhlen - sizeof(struct rtnexthop);
@@ -216,11 +242,18 @@
 	}
 
 #ifdef CONFIG_IP_ROUTE_MULTIPATH
+#ifdef CONFIG_RTNL_OLD_IFINFO
 	if (r->rtm_nhs == 0)
 		return 0;
 
 	nhp = RTM_RTNH(r);
 	nhlen = RTM_NHLEN(nlh, r);
+#else
+	if (rta->rta_mp == NULL)
+		return 0;
+	nhp = RTA_DATA(rta->rta_mp);
+	nhlen = RTA_PAYLOAD(rta->rta_mp);
+#endif
 	
 	for_nexthops(fi) {
 		int attrlen = nhlen - sizeof(struct rtnexthop);
@@ -352,16 +385,28 @@
 	struct fib_info *fi = NULL;
 	struct fib_info *ofi;
 #ifdef CONFIG_IP_ROUTE_MULTIPATH
+#ifdef CONFIG_RTNL_OLD_IFINFO
 	int nhs = r->rtm_nhs ? : 1;
 #else
+	int nhs = 1;
+#endif
+#else
 	const int nhs = 1;
 #endif
 
 	/* Fast check to catch the most weird cases */
-	if (fib_props[r->rtm_type].scope > r->rtm_scope) {
-		printk("Einval 1\n");
+	if (fib_props[r->rtm_type].scope > r->rtm_scope)
 		goto err_inval;
+
+#ifdef CONFIG_IP_ROUTE_MULTIPATH
+#ifndef CONFIG_RTNL_OLD_IFINFO
+	if (rta->rta_mp) {
+		nhs = fib_count_nexthops(rta->rta_mp);
+		if (nhs == 0)
+			goto err_inval;
 	}
+#endif
+#endif
 
 	fi = kmalloc(sizeof(*fi)+nhs*sizeof(struct fib_nh), GFP_KERNEL);
 	err = -ENOBUFS;
@@ -372,18 +417,43 @@
 	fi->fib_protocol = r->rtm_protocol;
 	fi->fib_nhs = nhs;
 	fi->fib_flags = r->rtm_flags;
+#ifdef CONFIG_RTNL_OLD_IFINFO
 	if (rta->rta_mtu)
 		fi->fib_mtu = *rta->rta_mtu;
 	if (rta->rta_rtt)
 		fi->fib_rtt = *rta->rta_rtt;
 	if (rta->rta_window)
 		fi->fib_window = *rta->rta_window;
+#else
+	if (rta->rta_mx) {
+		int attrlen = RTA_PAYLOAD(rta->rta_mx);
+		struct rtattr *attr = RTA_DATA(rta->rta_mx);
+
+		while (RTA_OK(attr, attrlen)) {
+			unsigned flavor = attr->rta_type;
+			if (flavor) {
+				if (flavor > FIB_MAX_METRICS)
+					goto failure;
+				fi->fib_metrics[flavor-1] = *(unsigned*)RTA_DATA(attr);
+			}
+			attr = RTA_NEXT(attr, attrlen);
+		}
+	}
+#endif
 	if (rta->rta_prefsrc)
 		memcpy(&fi->fib_prefsrc, rta->rta_prefsrc, 4);
 
+#ifndef CONFIG_RTNL_OLD_IFINFO
+	if (rta->rta_mp) {
+#else
 	if (r->rtm_nhs) {
+#endif
 #ifdef CONFIG_IP_ROUTE_MULTIPATH
+#ifdef CONFIG_RTNL_OLD_IFINFO
 		if ((err = fib_get_nhs(fi, nlh, r)) != 0)
+#else
+		if ((err = fib_get_nhs(fi, rta->rta_mp, r)) != 0)
+#endif
 			goto failure;
 		if (rta->rta_oif && fi->fib_nh->nh_oif != *rta->rta_oif)
 			goto err_inval;
@@ -414,7 +484,11 @@
 #endif
 
 	if (fib_props[r->rtm_type].error) {
+#ifndef CONFIG_RTNL_OLD_IFINFO
+		if (rta->rta_gw || rta->rta_oif || rta->rta_mp)
+#else
 		if (rta->rta_gw || rta->rta_oif || r->rtm_nhs)
+#endif
 			goto err_inval;
 		goto link_it;
 	}
@@ -550,7 +624,9 @@
 	struct rtmsg *rtm;
 	struct nlmsghdr  *nlh;
 	unsigned char	 *b = skb->tail;
+#ifdef CONFIG_RTNL_OLD_IFINFO
 	unsigned char 	 *o;
+#endif
 
 	nlh = NLMSG_PUT(skb, pid, seq, event, sizeof(*rtm));
 	rtm = NLMSG_DATA(nlh);
@@ -562,18 +638,33 @@
 	rtm->rtm_type = type;
 	rtm->rtm_flags = fi->fib_flags;
 	rtm->rtm_scope = scope;
+#ifdef CONFIG_RTNL_OLD_IFINFO
 	rtm->rtm_nhs = 0;
 
 	o = skb->tail;
+#endif
 	if (rtm->rtm_dst_len)
 		RTA_PUT(skb, RTA_DST, 4, dst);
 	rtm->rtm_protocol = fi->fib_protocol;
+#ifdef CONFIG_RTNL_OLD_IFINFO
 	if (fi->fib_mtu)
 		RTA_PUT(skb, RTA_MTU, sizeof(unsigned), &fi->fib_mtu);
 	if (fi->fib_window)
 		RTA_PUT(skb, RTA_WINDOW, sizeof(unsigned), &fi->fib_window);
 	if (fi->fib_rtt)
 		RTA_PUT(skb, RTA_RTT, sizeof(unsigned), &fi->fib_rtt);
+#else
+	if (fi->fib_mtu || fi->fib_window || fi->fib_rtt) {
+		int i;
+		struct rtattr *mx = (struct rtattr *)skb->tail;
+		RTA_PUT(skb, RTA_METRICS, 0, NULL);
+		for (i=0; i<FIB_MAX_METRICS; i++) {
+			if (fi->fib_metrics[i])
+				RTA_PUT(skb, i+1, sizeof(unsigned), fi->fib_metrics + i);
+		}
+		mx->rta_len = skb->tail - (u8*)mx;
+	}
+#endif
 	if (fi->fib_prefsrc)
 		RTA_PUT(skb, RTA_PREFSRC, 4, &fi->fib_prefsrc);
 	if (fi->fib_nhs == 1) {
@@ -582,10 +673,18 @@
 		if (fi->fib_nh->nh_oif)
 			RTA_PUT(skb, RTA_OIF, sizeof(int), &fi->fib_nh->nh_oif);
 	}
+#ifdef CONFIG_RTNL_OLD_IFINFO
 	rtm->rtm_optlen = skb->tail - o;
+#endif
 #ifdef CONFIG_IP_ROUTE_MULTIPATH
 	if (fi->fib_nhs > 1) {
 		struct rtnexthop *nhp;
+#ifndef CONFIG_RTNL_OLD_IFINFO
+		struct rtattr *mp_head;
+		if (skb_tailroom(skb) <= RTA_SPACE(0))
+			goto rtattr_failure;
+		mp_head = (struct rtattr*)skb_put(skb, RTA_SPACE(0));
+#endif
 		for_nexthops(fi) {
 			if (skb_tailroom(skb) < RTA_ALIGN(RTA_ALIGN(sizeof(*nhp)) + 4))
 				goto rtattr_failure;
@@ -596,8 +695,14 @@
 			if (nh->nh_gw)
 				RTA_PUT(skb, RTA_GATEWAY, 4, &nh->nh_gw);
 			nhp->rtnh_len = skb->tail - (unsigned char*)nhp;
+#ifdef CONFIG_RTNL_OLD_IFINFO
 			rtm->rtm_nhs++;
+#endif
 		} endfor_nexthops(fi);
+#ifndef CONFIG_RTNL_OLD_IFINFO
+		mp_head->rta_type = RTA_MULTIPATH;
+		mp_head->rta_len = skb->tail - (u8*)mp_head;
+#endif
 	}
 #endif
 	nlh->nlmsg_len = skb->tail - b;
@@ -605,7 +710,7 @@
 
 nlmsg_failure:
 rtattr_failure:
-	skb_put(skb, b - skb->tail);
+	skb_trim(skb, b - skb->data);
 	return -1;
 }
 
@@ -655,10 +760,8 @@
 		nl->nlmsg_flags = 0;
 	} else {
 		nl->nlmsg_type = RTM_NEWROUTE;
-		nl->nlmsg_flags = NLM_F_CREATE;
+		nl->nlmsg_flags = NLM_F_REQUEST|NLM_F_CREATE;
 		rtm->rtm_protocol = RTPROT_BOOT;
-		if (plen != 0)
-			nl->nlmsg_flags |= NLM_F_REPLACE;
 	}
 
 	rtm->rtm_dst_len = plen;
@@ -711,7 +814,7 @@
 	ptr = &((struct sockaddr_in*)&r->rt_gateway)->sin_addr.s_addr;
 	if (r->rt_gateway.sa_family == AF_INET && *ptr) {
 		rta->rta_gw = ptr;
-		if (r->rt_flags&RTF_GATEWAY)
+		if (r->rt_flags&RTF_GATEWAY && inet_addr_type(*ptr) == RTN_UNICAST)
 			rtm->rtm_scope = RT_SCOPE_UNIVERSE;
 	}
 
@@ -721,6 +824,7 @@
 	if (r->rt_flags&RTF_GATEWAY && rta->rta_gw == NULL)
 		return -EINVAL;
 
+#ifdef CONFIG_RTNL_OLD_IFINFO
 	/* Ugly conversion from rtentry types to unsigned */
 
 	if (r->rt_flags&RTF_IRTT) {
@@ -737,6 +841,10 @@
 		if (sizeof(*rta->rta_mtu) != sizeof(r->rt_mtu))
 			*rta->rta_mtu = r->rt_mtu;
 	}
+#else
+	if (r->rt_flags&(RTF_MTU|RTF_WINDOW|RTF_IRTT))
+	    printk(KERN_DEBUG "SIOCRT*: mtu/window/irtt are not implemnted.\n");
+#endif
 	return 0;
 }
 
diff -u --recursive --new-file v2.1.88/linux/net/ipv4/icmp.c linux/net/ipv4/icmp.c
--- v2.1.88/linux/net/ipv4/icmp.c	Thu Feb 12 20:56:14 1998
+++ linux/net/ipv4/icmp.c	Sun Mar  1 14:40:40 1998
@@ -877,9 +877,9 @@
 	struct in_ifaddr *ifa;
 	u32 mask;
 
-	if (!ipv4_config.log_martians ||
-	    !IS_ROUTER ||
-	    !in_dev || !in_dev->ifa_list ||
+	if (!in_dev || !in_dev->ifa_list ||
+	    !IN_DEV_LOG_MARTIANS(in_dev) ||
+	    !IN_DEV_FORWARD(in_dev) ||
 	    len < 4 ||
 	    !(rt->rt_flags&RTCF_DIRECTSRC))
 		return;
diff -u --recursive --new-file v2.1.88/linux/net/ipv4/igmp.c linux/net/ipv4/igmp.c
--- v2.1.88/linux/net/ipv4/igmp.c	Thu Feb 12 20:56:14 1998
+++ linux/net/ipv4/igmp.c	Sun Mar  1 14:40:40 1998
@@ -117,7 +117,7 @@
  * contradict to specs provided this delay is small enough.
  */
 
-#define IGMP_V1_SEEN(in_dev) ((in_dev)->mr_v1_seen && jiffies - (in_dev)->mr_v1_seen < 0)
+#define IGMP_V1_SEEN(in_dev) ((in_dev)->mr_v1_seen && (long)(jiffies - (in_dev)->mr_v1_seen) < 0)
 
 /*
  *	Timer management
@@ -286,7 +286,7 @@
 		if (LOCAL_MCAST(im->multiaddr))
 			continue;
 		im->unsolicit_count = 0;
-		if (im->tm_running && im->timer.expires-jiffies > max_delay)
+		if (im->tm_running && (long)(im->timer.expires-jiffies) > max_delay)
 			igmp_stop_timer(im);
 		igmp_start_timer(im, max_delay);
 	}
diff -u --recursive --new-file v2.1.88/linux/net/ipv4/ip_forward.c linux/net/ipv4/ip_forward.c
--- v2.1.88/linux/net/ipv4/ip_forward.c	Thu Feb 12 20:56:14 1998
+++ linux/net/ipv4/ip_forward.c	Sun Mar  1 14:40:40 1998
@@ -18,6 +18,7 @@
  *					use output device for accounting.
  *		Jos Vos		:	Call forward firewall after routing
  *					(always use output device).
+ *		Mike McLagan	:	Routing by source
  */
 
 #include <linux/config.h>
diff -u --recursive --new-file v2.1.88/linux/net/ipv4/ip_fragment.c linux/net/ipv4/ip_fragment.c
--- v2.1.88/linux/net/ipv4/ip_fragment.c	Thu Feb 12 20:56:14 1998
+++ linux/net/ipv4/ip_fragment.c	Sun Mar  1 14:40:40 1998
@@ -263,6 +263,7 @@
 	qp->dev = skb->dev;
 
 	/* Start a timer for this entry. */
+	init_timer(&qp->timer);
 	qp->timer.expires = jiffies + sysctl_ipfrag_time;	/* about 30 seconds	*/
 	qp->timer.data = (unsigned long) qp;		/* pointer to queue	*/
 	qp->timer.function = ip_expire;			/* expire function	*/
diff -u --recursive --new-file v2.1.88/linux/net/ipv4/ip_gre.c linux/net/ipv4/ip_gre.c
--- v2.1.88/linux/net/ipv4/ip_gre.c	Mon Feb 23 18:12:12 1998
+++ linux/net/ipv4/ip_gre.c	Sun Mar  1 14:40:40 1998
@@ -554,7 +554,7 @@
 		}
 		if (tunnel->parms.i_flags&GRE_SEQ) {
 			if (!(flags&GRE_SEQ) ||
-			    (tunnel->i_seqno && seqno - tunnel->i_seqno < 0)) {
+			    (tunnel->i_seqno && (s32)(seqno - tunnel->i_seqno) < 0)) {
 				tunnel->stat.rx_fifo_errors++;
 				tunnel->stat.rx_errors++;
 				goto drop;
@@ -704,12 +704,7 @@
 		if (jiffies - tunnel->err_time < IPTUNNEL_ERR_TIMEO) {
 			tunnel->err_count--;
 
-			if (skb->protocol == __constant_htons(ETH_P_IP))
-				icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0);
-#ifdef CONFIG_IPV6
-			else if (skb->protocol == __constant_htons(ETH_P_IPV6))
-				icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0, dev);
-#endif
+			dst_link_failure(skb);
 		} else
 			tunnel->err_count = 0;
 	}
@@ -792,12 +787,7 @@
 	return 0;
 
 tx_error_icmp:
-	if (skb->protocol == __constant_htons(ETH_P_IP))
-		icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0);
-#ifdef CONFIG_IPV6
-	else if (skb->protocol == __constant_htons(ETH_P_IPV6))
-		icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0, dev);
-#endif
+	dst_link_failure(skb);
 
 tx_error:
 	stats->tx_errors++;
diff -u --recursive --new-file v2.1.88/linux/net/ipv4/ip_input.c linux/net/ipv4/ip_input.c
--- v2.1.88/linux/net/ipv4/ip_input.c	Thu Feb 12 20:56:14 1998
+++ linux/net/ipv4/ip_input.c	Sun Mar  1 14:40:40 1998
@@ -97,6 +97,7 @@
  *		Alan Cox	:	Multicast routing hooks
  *		Jos Vos		:	Do accounting *before* call_in_firewall
  *	Willy Konynenberg	:	Transparent proxying support
+ *	Mike McLagan		:	Routing by source
  *
  *  
  *
@@ -464,18 +465,18 @@
 
 		opt = &(IPCB(skb)->opt);
 		if (opt->srr) {
-			if (!ipv4_config.source_route) {
-				if (ipv4_config.log_martians && net_ratelimit())
+			struct in_device *in_dev = dev->ip_ptr;
+			if (in_dev && !IN_DEV_SOURCE_ROUTE(in_dev)) {
+				if (IN_DEV_LOG_MARTIANS(in_dev) && net_ratelimit())
 					printk(KERN_INFO "source route option %08lx -> %08lx\n",
 					       ntohl(iph->saddr), ntohl(iph->daddr));
 				goto drop;
 			}
-			if (((struct rtable*)skb->dst)->rt_type == RTN_LOCAL &&
-			    ip_options_rcv_srr(skb))
+			if (ip_options_rcv_srr(skb))
 				goto drop;
 		}
 	}
-	
+
 	/*
 	 *	See if the firewall wants to dispose of the packet. 
 	 */
diff -u --recursive --new-file v2.1.88/linux/net/ipv4/ip_output.c linux/net/ipv4/ip_output.c
--- v2.1.88/linux/net/ipv4/ip_output.c	Mon Feb 23 18:12:12 1998
+++ linux/net/ipv4/ip_output.c	Sun Mar  1 14:40:40 1998
@@ -26,6 +26,7 @@
  *		Alexander Demenshin:	Missing sk/skb free in ip_queue_xmit
  *					(in case if packet not accepted by
  *					output firewall rules)
+ *		Mike McLagan	:	Routing by source
  *		Alexey Kuznetsov:	use new route cache
  *		Andi Kleen:		Fix broken PMTU recovery and remove
  *					some redundant tests.
@@ -92,14 +93,7 @@
 		daddr = opt->faddr;
 
 	err = ip_route_output(&rt, daddr, saddr, RT_TOS(sk->ip_tos) |
-#ifdef CONFIG_IP_TRANSPARENT_PROXY
-			      /* Rationale: this routine is used only
-				 by TCP, so that validity of saddr is already
-				 checked and we can safely use RTO_TPROXY.
-			       */
-			      RTO_TPROXY |
-#endif
-			      (sk->localroute||0), sk->bound_dev_if);
+			      RTO_CONN | sk->localroute, sk->bound_dev_if);
 	if (err)
 	{
 		ip_statistics.IpOutNoRoutes++;
@@ -133,7 +127,7 @@
 	iph->tos      = sk->ip_tos;
 	iph->frag_off = 0;
 	if (sk->ip_pmtudisc == IP_PMTUDISC_WANT && 
-		!(rt->rt_flags & RTCF_NOPMTUDISC))
+		!(rt->u.dst.mxlock&(1<<RTAX_MTU)))
 		iph->frag_off |= htons(IP_DF);
 	iph->ttl      = sk->ip_ttl;
 	iph->daddr    = rt->rt_dst;
@@ -175,7 +169,7 @@
 		sk->dst_cache = NULL;
 		ip_rt_put(rt);
 		err = ip_route_output(&rt, daddr, sk->saddr, RT_TOS(sk->ip_tos) |
-				      (sk->localroute||0), sk->bound_dev_if);
+				      RTO_CONN | sk->localroute, sk->bound_dev_if);
 		if (err)
 			return err;
 		sk->dst_cache = &rt->u.dst;
@@ -209,7 +203,7 @@
 	iph->tos      = sk->ip_tos;
 	iph->frag_off = 0;
 	if (sk->ip_pmtudisc == IP_PMTUDISC_WANT &&
-		!(rt->rt_flags & RTCF_NOPMTUDISC))
+		!(rt->u.dst.mxlock&(1<<RTAX_MTU)))
 		iph->frag_off |= htons(IP_DF);
 	iph->ttl      = sk->ip_ttl;
 	iph->daddr    = rt->rt_dst;
@@ -249,6 +243,7 @@
 #endif
 
 	skb->dev = dev;
+	skb->protocol = __constant_htons(ETH_P_IP);
 
 	/*
 	 *	Multicasts are looped back for other local users
@@ -362,7 +357,8 @@
 		   Essentially it is "ip_reroute_output" function. --ANK
 		*/
 		struct rtable *nrt;
-		if (ip_route_output(&nrt, rt->key.dst, rt->key.src, rt->key.tos, 
+		if (ip_route_output(&nrt, rt->key.dst, rt->key.src,
+				    rt->key.tos | RTO_CONN, 
 				    sk?sk->bound_dev_if:0)) 
 			goto drop;
 		skb->dst = &nrt->u.dst;
@@ -488,7 +484,7 @@
 #endif	
 
 	if (sk->ip_pmtudisc == IP_PMTUDISC_DONT ||
-	     rt->rt_flags&RTCF_NOPMTUDISC)
+	     (rt->u.dst.mxlock&(1<<RTAX_MTU)))
 		df = 0;
 
 	 
diff -u --recursive --new-file v2.1.88/linux/net/ipv4/ip_sockglue.c linux/net/ipv4/ip_sockglue.c
--- v2.1.88/linux/net/ipv4/ip_sockglue.c	Thu Feb 12 20:56:14 1998
+++ linux/net/ipv4/ip_sockglue.c	Sun Mar  1 14:40:40 1998
@@ -14,6 +14,7 @@
  *		Martin Mares	:	TOS setting fixed.
  *		Alan Cox	:	Fixed a couple of oopses in Martin's 
  *					TOS tweaks.
+ *		Mike McLagan	:	Routing by source
  */
 
 #include <linux/config.h>
@@ -313,14 +314,9 @@
 			if (IPTOS_PREC(val) >= IPTOS_PREC_CRITIC_ECP && !suser())
 				return -EPERM;
 			if (sk->ip_tos != val) {
-				start_bh_atomic(); 
 				sk->ip_tos=val;
 				sk->priority = rt_tos2priority(val);
-				if (sk->dst_cache) {
-					dst_release(sk->dst_cache); 
-					sk->dst_cache = NULL;
-				}
-				end_bh_atomic();
+				dst_release(xchg(&sk->dst_cache, NULL)); 
 			}
 			sk->priority = rt_tos2priority(val);
 			return 0;
diff -u --recursive --new-file v2.1.88/linux/net/ipv4/ipip.c linux/net/ipv4/ipip.c
--- v2.1.88/linux/net/ipv4/ipip.c	Mon Feb 23 18:12:13 1998
+++ linux/net/ipv4/ipip.c	Sun Mar  1 14:40:41 1998
@@ -530,7 +530,7 @@
 	if (tunnel->err_count > 0) {
 		if (jiffies - tunnel->err_time < IPTUNNEL_ERR_TIMEO) {
 			tunnel->err_count--;
-			icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0);
+			dst_link_failure(skb);
 		} else
 			tunnel->err_count = 0;
 	}
@@ -587,7 +587,7 @@
 	return 0;
 
 tx_error_icmp:
-	icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0);
+	dst_link_failure(skb);
 tx_error:
 	stats->tx_errors++;
 	dev_kfree_skb(skb);
diff -u --recursive --new-file v2.1.88/linux/net/ipv4/ipmr.c linux/net/ipv4/ipmr.c
--- v2.1.88/linux/net/ipv4/ipmr.c	Thu Feb 12 20:56:14 1998
+++ linux/net/ipv4/ipmr.c	Sun Mar  1 14:40:41 1998
@@ -15,6 +15,7 @@
  *	Michael Chastain	:	Incorrect size of copying.
  *	Alan Cox		:	Added the cache manager code
  *	Alan Cox		:	Fixed the clone/copy bug and device race.
+ *	Mike McLagan		:	Routing by source
  *	Malcolm Beattie		:	Buffer handling fixes.
  *	Alexey Kuznetsov	:	Double buffer free and other fixes.
  *	SVR Anand		:	Fixed several multicast bugs and problems.
@@ -113,6 +114,7 @@
 			in_dev = dev->ip_ptr;
 			if (in_dev == NULL && (in_dev = inetdev_init(dev)) == NULL)
 				goto failure;
+			in_dev->cnf.rp_filter = 0;
 
 			if (dev_open(dev))
 				goto failure;
@@ -181,6 +183,8 @@
 	if ((in_dev = inetdev_init(dev)) == NULL)
 		goto failure;
 
+	in_dev->cnf.rp_filter = 0;
+
 	if (dev_open(dev))
 		goto failure;
 
@@ -216,7 +220,7 @@
 	vifc_map &= ~(1<<vifi);
 
 	if ((in_dev = dev->ip_ptr) != NULL)
-		in_dev->flags &= ~IFF_IP_MFORWARD;
+		in_dev->cnf.mc_forwarding = 0;
 
 	dev_set_allmulti(dev, -1);
 	ip_rt_multicast_event(in_dev);
@@ -652,7 +656,7 @@
 static void mrtsock_destruct(struct sock *sk)
 {
 	if (sk == mroute_socket) {
-		ipv4_config.multicast_route = 0;
+		ipv4_devconf.mc_forwarding = 0;
 		mroute_socket=NULL;
 		mroute_close(sk);
 	}
@@ -693,7 +697,7 @@
 			if(mroute_socket)
 				return -EADDRINUSE;
 			mroute_socket=sk;
-			ipv4_config.multicast_route = 1;
+			ipv4_devconf.mc_forwarding = 1;
 			if (ip_ra_control(sk, 1, mrtsock_destruct) == 0)
 				return 0;
 			mrtsock_destruct(sk);
@@ -754,9 +758,9 @@
 
 				if ((in_dev = dev->ip_ptr) == NULL)
 					return -EADDRNOTAVAIL;
-				if (in_dev->flags & IFF_IP_MFORWARD)
+				if (in_dev->cnf.mc_forwarding)
 					return -EADDRINUSE;
-				in_dev->flags |= IFF_IP_MFORWARD;
+				in_dev->cnf.mc_forwarding = 1;
 				dev_set_allmulti(dev, +1);
 				ip_rt_multicast_event(in_dev);
 
@@ -1348,11 +1352,20 @@
 	struct rtnexthop *nhp;
 	struct device *dev = vif_table[c->mfc_parent].dev;
 
+#ifdef CONFIG_RTNL_OLD_IFINFO
 	if (dev) {
 		u8 *o = skb->tail;
 		RTA_PUT(skb, RTA_IIF, 4, &dev->ifindex);
 		rtm->rtm_optlen += skb->tail - o;
 	}
+#else
+	struct rtattr *mp_head;
+
+	if (dev)
+		RTA_PUT(skb, RTA_IIF, 4, &dev->ifindex);
+
+	mp_head = (struct rtattr*)skb_put(skb, RTA_LENGTH(0));
+#endif
 
 	for (ct = c->mfc_minvif; ct < c->mfc_maxvif; ct++) {
 		if (c->mfc_ttls[ct] < 255) {
@@ -1363,9 +1376,15 @@
 			nhp->rtnh_hops = c->mfc_ttls[ct];
 			nhp->rtnh_ifindex = vif_table[ct].dev->ifindex;
 			nhp->rtnh_len = sizeof(*nhp);
+#ifdef CONFIG_RTNL_OLD_IFINFO
 			rtm->rtm_nhs++;
+#endif
 		}
 	}
+#ifndef CONFIG_RTNL_OLD_IFINFO
+	mp_head->rta_type = RTA_MULTIPATH;
+	mp_head->rta_len = skb->tail - (u8*)mp_head;
+#endif
 	rtm->rtm_type = RTN_MULTICAST;
 	return 1;
 
diff -u --recursive --new-file v2.1.88/linux/net/ipv4/rarp.c linux/net/ipv4/rarp.c
--- v2.1.88/linux/net/ipv4/rarp.c	Thu Feb 12 20:56:14 1998
+++ linux/net/ipv4/rarp.c	Sun Mar  1 14:40:41 1998
@@ -30,6 +30,7 @@
  * Fixes
  *	Alan Cox	:	Rarp delete on device down needed as
  *				reported by Walter Wolfgang.
+ *	Mike McLagan	:	Routing by source
  *
  */
 
diff -u --recursive --new-file v2.1.88/linux/net/ipv4/raw.c linux/net/ipv4/raw.c
--- v2.1.88/linux/net/ipv4/raw.c	Thu Feb 12 20:56:14 1998
+++ linux/net/ipv4/raw.c	Sun Mar  1 14:40:41 1998
@@ -342,7 +342,9 @@
 			daddr = ipc.opt->faddr;
 		}
 	}
-	tos = RT_TOS(sk->ip_tos) | (sk->localroute || (msg->msg_flags&MSG_DONTROUTE));
+	tos = RT_TOS(sk->ip_tos) | sk->localroute;
+	if (msg->msg_flags&MSG_DONTROUTE)
+		tos |= RTO_ONLINK;
 
 	if (MULTICAST(daddr)) {
 		if (!ipc.oif)
@@ -403,8 +405,7 @@
 	sk->rcv_saddr = sk->saddr = addr->sin_addr.s_addr;
 	if(chk_addr_ret == RTN_MULTICAST || chk_addr_ret == RTN_BROADCAST)
 		sk->saddr = 0;  /* Use device */
-	dst_release(sk->dst_cache);
-	sk->dst_cache = NULL;
+	dst_release(xchg(&sk->dst_cache, NULL));
 	return 0;
 }
 
@@ -453,6 +454,9 @@
 	}
 	
 	err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
+	if (err)
+		goto done;
+
 	sk->stamp=skb->stamp;
 
 	/* Copy the address. */
@@ -462,8 +466,9 @@
 	}
 	if (sk->ip_cmsg_flags)
 		ip_cmsg_recv(msg, skb);
+done:
 	skb_free_datagram(sk, skb);
-	return err ? err : (copied);
+	return (err ? : copied);
 }
 
 static int raw_init(struct sock *sk)
diff -u --recursive --new-file v2.1.88/linux/net/ipv4/route.c linux/net/ipv4/route.c
--- v2.1.88/linux/net/ipv4/route.c	Mon Feb 23 18:12:13 1998
+++ linux/net/ipv4/route.c	Sun Mar  1 14:40:41 1998
@@ -43,6 +43,7 @@
  *		Bjorn Ekwall	:	Kerneld route support.
  *		Alan Cox	:	Multicast fixed (I hope)
  * 		Pavel Krauz	:	Limited broadcast fixed
+ *		Mike McLagan	:	Routing by source
  *	Alexey Kuznetsov	:	End of old history. Splitted to fib.c and
  *					route.c and rewritten from scratch.
  *		Andi Kleen	:	Load-limit warning messages.
@@ -85,11 +86,33 @@
 #include <net/arp.h>
 #include <net/tcp.h>
 #include <net/icmp.h>
+#ifdef CONFIG_SYSCTL
+#include <linux/sysctl.h>
+#endif
+
+#define RT_GC_TIMEOUT (300*HZ)
+
+int ip_rt_min_delay = 2*HZ;
+int ip_rt_max_delay = 10*HZ;
+int ip_rt_gc_thresh = RT_HASH_DIVISOR;
+int ip_rt_max_size = RT_HASH_DIVISOR*16;
+int ip_rt_gc_timeout = RT_GC_TIMEOUT;
+int ip_rt_gc_interval = 60*HZ;
+int ip_rt_gc_min_interval = 5*HZ;
+int ip_rt_redirect_number = 9;
+int ip_rt_redirect_load = HZ/50;
+int ip_rt_redirect_silence = ((HZ/50) << (9+1));
+int ip_rt_error_cost = HZ;
+int ip_rt_error_burst = 5*HZ;
+
+static unsigned long rt_deadline = 0;
 
 #define RTprint(a...)	printk(KERN_DEBUG a)
 
+static void rt_run_flush(unsigned long dummy);
+
 static struct timer_list rt_flush_timer =
-	{ NULL, NULL, 0, 0L, NULL };
+	{ NULL, NULL, 0, 0L, rt_run_flush };
 static struct timer_list rt_periodic_timer =
 	{ NULL, NULL, 0, 0L, NULL };
 
@@ -101,16 +124,22 @@
 static struct dst_entry * ipv4_dst_reroute(struct dst_entry * dst,
 					   struct sk_buff *);
 static struct dst_entry * ipv4_negative_advice(struct dst_entry *);
+static void		  ipv4_link_failure(struct sk_buff *skb);
+static int rt_garbage_collect(void);
 
 
 struct dst_ops ipv4_dst_ops =
 {
 	AF_INET,
 	__constant_htons(ETH_P_IP),
+	RT_HASH_DIVISOR,
+
+	rt_garbage_collect,
 	ipv4_dst_check,
 	ipv4_dst_reroute,
 	NULL,
-	ipv4_negative_advice
+	ipv4_negative_advice,
+	ipv4_link_failure,
 };
 
 __u8 ip_tos2prio[16] = {
@@ -136,7 +165,6 @@
  * Route cache.
  */
 
-static atomic_t		 rt_cache_size = ATOMIC_INIT(0);
 static struct rtable 	*rt_hash_table[RT_HASH_DIVISOR];
 
 static struct rtable * rt_intern_hash(unsigned hash, struct rtable * rth, u16 protocol);
@@ -212,7 +240,7 @@
 }
 #endif
   
-static void __inline__ rt_free(struct rtable *rt)
+static __inline__ void rt_free(struct rtable *rt)
 {
 	dst_free(&rt->u.dst);
 }
@@ -237,9 +265,8 @@
 			 */
 
 			if (!atomic_read(&rth->u.dst.use) &&
-			    (now - rth->u.dst.lastuse > RT_CACHE_TIMEOUT)) {
+			    (now - rth->u.dst.lastuse > ip_rt_gc_timeout)) {
 				*rthp = rth_next;
-				atomic_dec(&rt_cache_size);
 #if RT_CACHE_DEBUG >= 2
 				printk("rt_check_expire clean %02x@%08x\n", rover, rth->rt_dst);
 #endif
@@ -250,8 +277,8 @@
 			if (!rth_next)
 				break;
 
-			if ( rth_next->u.dst.lastuse - rth->u.dst.lastuse > RT_CACHE_BUBBLE_THRESHOLD ||
-			    (rth->u.dst.lastuse - rth_next->u.dst.lastuse < 0 &&
+			if ( (long)(rth_next->u.dst.lastuse - rth->u.dst.lastuse) > RT_CACHE_BUBBLE_THRESHOLD ||
+			    ((long)(rth->u.dst.lastuse - rth_next->u.dst.lastuse) < 0 &&
 			     atomic_read(&rth->u.dst.refcnt) < atomic_read(&rth_next->u.dst.refcnt))) {
 #if RT_CACHE_DEBUG >= 2
 				printk("rt_check_expire bubbled %02x@%08x<->%08x\n", rover, rth->rt_dst, rth_next->rt_dst);
@@ -265,7 +292,7 @@
 			rthp = &rth->u.rt_next;
 		}
 	}
-	rt_periodic_timer.expires = now + RT_GC_INTERVAL;
+	rt_periodic_timer.expires = now + ip_rt_gc_interval;
 	add_timer(&rt_periodic_timer);
 }
 
@@ -282,7 +309,6 @@
 
 		for (; rth; rth=next) {
 			next = rth->u.rt_next;
-			atomic_dec(&rt_cache_size);
 			nr++;
 			rth->u.rt_next = NULL;
 			rt_free(rth);
@@ -296,48 +322,57 @@
   
 void rt_cache_flush(int delay)
 {
+	if (delay < 0)
+		delay = ip_rt_min_delay;
+
 	start_bh_atomic();
-	if (delay && rt_flush_timer.function &&
-	    rt_flush_timer.expires - jiffies < delay) {
-		end_bh_atomic();
-		return;
-	}
-	if (rt_flush_timer.function) {
-		del_timer(&rt_flush_timer);
-		rt_flush_timer.function = NULL;
+
+	if (del_timer(&rt_flush_timer) && delay > 0 && rt_deadline) {
+		long tmo = (long)(rt_deadline - rt_flush_timer.expires);
+
+		/* If flush timer is already running
+		   and flush request is not immediate (delay > 0):
+
+		   if deadline is not achieved, prolongate timer to "dealy",
+		   otherwise fire it at deadline time.
+		 */
+
+		if (delay > tmo)
+			delay = tmo;
 	}
-	if (delay == 0) {
+
+	if (delay <= 0) {
+		rt_deadline = 0;
 		end_bh_atomic();
+
 		rt_run_flush(0);
 		return;
 	}
-	rt_flush_timer.function = rt_run_flush;
+
+	if (rt_deadline == 0)
+		rt_deadline = jiffies + ip_rt_max_delay;
+
 	rt_flush_timer.expires = jiffies + delay;
 	add_timer(&rt_flush_timer);
 	end_bh_atomic();
 }
 
-
-static void rt_garbage_collect(void)
+static int rt_garbage_collect(void)
 {
 	int i;
-	static unsigned expire = RT_CACHE_TIMEOUT>>1;
+	static unsigned expire = RT_GC_TIMEOUT>>1;
 	static unsigned long last_gc;
 	struct rtable *rth, **rthp;
-	unsigned long now;
+	unsigned long now = jiffies;
 
 	start_bh_atomic();
-	now = jiffies;
 
 	/*
 	 * Garbage collection is pretty expensive,
 	 * do not make it too frequently, but just increase expire strength.
 	 */
-	if (now - last_gc < 1*HZ) {
-		expire >>= 1;
-		end_bh_atomic();
-		return;
-	}
+	if (now - last_gc < ip_rt_gc_min_interval)
+		goto out;
 
 	expire++;
 
@@ -348,7 +383,6 @@
 			if (atomic_read(&rth->u.dst.use) ||
 			    now - rth->u.dst.lastuse < expire)
 				continue;
-			atomic_dec(&rt_cache_size);
 			*rthp = rth->u.rt_next;
 			rth->u.rt_next = NULL;
 			rt_free(rth);
@@ -357,11 +391,13 @@
 	}
 
 	last_gc = now;
-	if (atomic_read(&rt_cache_size) < RT_CACHE_MAX_SIZE)
-		expire = RT_CACHE_TIMEOUT>>1;
-	else
-		expire >>= 1;
+	if (atomic_read(&ipv4_dst_ops.entries) < ipv4_dst_ops.gc_thresh)
+		expire = ip_rt_gc_timeout;
+
+out:
+	expire >>= 1;
 	end_bh_atomic();
+	return (atomic_read(&ipv4_dst_ops.entries) > ip_rt_max_size);
 }
 
 static struct rtable *rt_intern_hash(unsigned hash, struct rtable * rt, u16 protocol)
@@ -401,9 +437,6 @@
 	if (rt->rt_type == RTN_UNICAST || rt->key.iif == 0)
 		arp_bind_neighbour(&rt->u.dst);
 
-	if (atomic_read(&rt_cache_size) >= RT_CACHE_MAX_SIZE)
-		rt_garbage_collect();
-
 	rt->u.rt_next = rt_hash_table[hash];
 #if RT_CACHE_DEBUG >= 2
 	if (rt->u.rt_next) {
@@ -415,7 +448,6 @@
 	}
 #endif
 	rt_hash_table[hash] = rt;
-	atomic_inc(&rt_cache_size);
 
 	end_bh_atomic();
 	return rt;
@@ -432,7 +464,10 @@
 
 	tos &= IPTOS_TOS_MASK;
 
-	if (!in_dev || new_gw == old_gw || !IN_DEV_RX_REDIRECTS(in_dev)
+	if (!in_dev)
+		return;
+
+	if (new_gw == old_gw || !IN_DEV_RX_REDIRECTS(in_dev)
 	    || MULTICAST(new_gw) || BADCLASS(new_gw) || ZERONET(new_gw))
 		goto reject_redirect;
 
@@ -512,7 +547,7 @@
 
 reject_redirect:
 #ifdef CONFIG_IP_ROUTE_VERBOSE
-	if (ipv4_config.log_martians && net_ratelimit())
+	if (IN_DEV_LOG_MARTIANS(in_dev) && net_ratelimit())
 		printk(KERN_INFO "Redirect from %lX/%s to %lX ignored."
 		       "Path = %lX -> %lX, tos %02x\n",
 		       ntohl(old_gw), dev->name, ntohl(new_gw),
@@ -539,11 +574,11 @@
 
 /*
  * Algorithm:
- *	1. The first RT_REDIRECT_NUMBER redirects are sent
+ *	1. The first ip_rt_redirect_number redirects are sent
  *	   with exponential backoff, then we stop sending them at all,
  *	   assuming that the host ignores our redirects.
  *	2. If we did not see packets requiring redirects
- *	   during RT_REDIRECT_SILENCE, we assume that the host
+ *	   during ip_rt_redirect_silence, we assume that the host
  *	   forgot redirected route and start to send redirects again.
  *
  * This algorithm is much cheaper and more intelligent than dumb load limiting
@@ -557,29 +592,30 @@
 {
 	struct rtable *rt = (struct rtable*)skb->dst;
 
-	/* No redirected packets during RT_REDIRECT_SILENCE;
+	/* No redirected packets during ip_rt_redirect_silence;
 	 * reset the algorithm.
 	 */
-	if (jiffies - rt->last_error > RT_REDIRECT_SILENCE)
-		rt->errors = 0;
+	if (jiffies - rt->u.dst.rate_last > ip_rt_redirect_silence)
+		rt->u.dst.rate_tokens = 0;
 
 	/* Too many ignored redirects; do not send anything
-	 * set last_error to the last seen redirected packet.
+	 * set u.dst.rate_last to the last seen redirected packet.
 	 */
-	if (rt->errors >= RT_REDIRECT_NUMBER) {
-		rt->last_error = jiffies;
+	if (rt->u.dst.rate_tokens >= ip_rt_redirect_number) {
+		rt->u.dst.rate_last = jiffies;
 		return;
 	}
 
-	/* Check for load limit; set last_error to the latest sent
+	/* Check for load limit; set rate_last to the latest sent
 	 * redirect.
 	 */
-	if (jiffies - rt->last_error > (RT_REDIRECT_LOAD<<rt->errors)) {
+	if (jiffies - rt->u.dst.rate_last > (ip_rt_redirect_load<<rt->u.dst.rate_tokens)) {
 		icmp_send(skb, ICMP_REDIRECT, ICMP_REDIR_HOST, rt->rt_gateway);
-		rt->last_error = jiffies;
-		++rt->errors;
+		rt->u.dst.rate_last = jiffies;
+		++rt->u.dst.rate_tokens;
 #ifdef CONFIG_IP_ROUTE_VERBOSE
-		if (ipv4_config.log_martians && rt->errors == RT_REDIRECT_NUMBER && net_ratelimit())
+		if (skb->dev->ip_ptr && IN_DEV_LOG_MARTIANS((struct in_device*)skb->dev->ip_ptr) &&
+		    rt->u.dst.rate_tokens == ip_rt_redirect_number && net_ratelimit())
 			printk(KERN_WARNING "host %08x/if%d ignores redirects for %08x to %08x.\n",
 			       rt->rt_src, rt->rt_iif, rt->rt_dst, rt->rt_gateway);
 #endif
@@ -589,6 +625,7 @@
 static int ip_error(struct sk_buff *skb)
 {
 	struct rtable *rt = (struct rtable*)skb->dst;
+	unsigned long now;
 	int code;
 
 	switch (rt->u.dst.error) {
@@ -606,10 +643,16 @@
 		code = ICMP_PKT_FILTERED;
 		break;
 	}
-	if (jiffies - rt->last_error > RT_ERROR_LOAD) {
+
+	now = jiffies;
+	if ((rt->u.dst.rate_tokens += now - rt->u.dst.rate_last) > ip_rt_error_burst)
+		rt->u.dst.rate_tokens = ip_rt_error_burst;
+	if (rt->u.dst.rate_tokens >= ip_rt_error_cost) {
+		rt->u.dst.rate_tokens -= ip_rt_error_cost;
 		icmp_send(skb, ICMP_DEST_UNREACH, code, 0);
-		rt->last_error = jiffies;
+		rt->u.dst.rate_last = now;
 	}
+
 	kfree_skb(skb);
 	return 0;
 } 
@@ -655,7 +698,7 @@
 			    rth->rt_src == iph->saddr &&
 			    rth->key.tos == tos &&
 			    rth->key.iif == 0 &&
-			    !(rth->rt_flags&RTCF_NOPMTUDISC)) {
+			    !(rth->u.dst.mxlock&(1<<RTAX_MTU))) {
 				unsigned short mtu = new_mtu;
 
 				if (new_mtu < 68 || new_mtu >= old_mtu) {
@@ -692,6 +735,11 @@
 	return NULL;
 }
 
+static void ipv4_link_failure(struct sk_buff *skb)
+{
+	icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0);
+}
+
 static int ip_rt_bug(struct sk_buff *skb)
 {
 	printk(KERN_DEBUG "ip_rt_bug: %08x -> %08x, %s\n", skb->nh.iph->saddr,
@@ -945,7 +993,9 @@
 	rth->u.dst.pmtu	= res.fi->fib_mtu ? : out_dev->dev->mtu;
 	rth->u.dst.window=res.fi->fib_window ? : 0;
 	rth->u.dst.rtt	= res.fi->fib_rtt ? : TCP_TIMEOUT_INIT;
-	rth->u.dst.rate_last = rth->u.dst.rate_tokens = 0; 
+#ifndef CONFIG_RTNL_OLD_IFINFO
+	rth->u.dst.mxlock = res.fi->fib_metrics[RTAX_LOCK-1];
+#endif
 
 	if (FIB_RES_GW(res) && FIB_RES_NH(res).nh_scope == RT_SCOPE_LINK)
 		rth->rt_gateway	= FIB_RES_GW(res);
@@ -1025,14 +1075,14 @@
 	 */
 martian_destination:
 #ifdef CONFIG_IP_ROUTE_VERBOSE
-	if (ipv4_config.log_martians && net_ratelimit())
+	if (IN_DEV_LOG_MARTIANS(in_dev) && net_ratelimit())
 		printk(KERN_WARNING "martian destination %08x from %08x, dev %s\n", daddr, saddr, dev->name);
 #endif
 	return -EINVAL;
 
 martian_source:
 #ifdef CONFIG_IP_ROUTE_VERBOSE
-	if (ipv4_config.log_martians && net_ratelimit()) {
+	if (IN_DEV_LOG_MARTIANS(in_dev) && net_ratelimit()) {
 		/*
 		 *	RFC1812 recommenadtion, if source is martian,
 		 *	the only hint is MAC header.
@@ -1283,14 +1333,17 @@
 	else if (BADCLASS(key.dst) || ZERONET(key.dst))
 		return -EINVAL;
 
+	if (dev_out->flags&IFF_LOOPBACK)
+		flags |= RTCF_LOCAL;
+
 	if (res.type == RTN_BROADCAST) {
 		flags |= RTCF_BROADCAST;
-		if (!(dev_out->flags&IFF_LOOPBACK) && dev_out->flags&IFF_BROADCAST)
+		if (dev_out->flags&IFF_BROADCAST)
 			flags |= RTCF_LOCAL;
 	} else if (res.type == RTN_MULTICAST) {
-		flags |= RTCF_MULTICAST;
-		if (ip_check_mc(dev_out, daddr))
-			flags |= RTCF_LOCAL;
+		flags |= RTCF_MULTICAST|RTCF_LOCAL;
+		if (!ip_check_mc(dev_out, daddr))
+			flags &= ~RTCF_LOCAL;
 	}
 
 	rth = dst_alloc(sizeof(struct rtable), &ipv4_dst_ops);
@@ -1341,12 +1394,14 @@
 		rth->u.dst.pmtu	= res.fi->fib_mtu ? : dev_out->mtu;
 		rth->u.dst.window=res.fi->fib_window ? : 0;
 		rth->u.dst.rtt	= res.fi->fib_rtt ? : TCP_TIMEOUT_INIT;
+#ifndef CONFIG_RTNL_OLD_IFINFO
+		rth->u.dst.mxlock = res.fi->fib_metrics[RTAX_LOCK-1];
+#endif
 	} else {
 		rth->u.dst.pmtu	= dev_out->mtu;
 		rth->u.dst.window=0;
 		rth->u.dst.rtt	= TCP_TIMEOUT_INIT;
 	}
-	rth->u.dst.rate_last = rth->u.dst.rate_tokens = 0; 
 	rth->rt_flags = flags;
         rth->rt_type = res.type;
 	hash = rt_hash_code(daddr, saddr^(oif<<5), tos);
@@ -1391,15 +1446,20 @@
 
 int inet_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void *arg)
 {
-	struct kern_rta *rta = arg;
+	struct rtattr **rta = arg;
 	struct rtmsg *rtm = NLMSG_DATA(nlh);
 	struct rtable *rt = NULL;
 	u32 dst = 0;
 	u32 src = 0;
+	int iif = 0;
 	int err;
 	struct sk_buff *skb;
 	struct rta_cacheinfo ci;
-	u8  *o;
+#ifdef CONFIG_RTNL_OLD_IFINFO
+	unsigned char 	 *o;
+#else
+	struct rtattr *mx;
+#endif
 
 	skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
 	if (skb == NULL)
@@ -1411,14 +1471,16 @@
 	skb->mac.raw = skb->data;
 	skb_reserve(skb, MAX_HEADER + sizeof(struct iphdr));
 
-	if (rta->rta_dst)
-		memcpy(&dst, rta->rta_dst, 4);
-	if (rta->rta_src)
-		memcpy(&src, rta->rta_src, 4);
+	if (rta[RTA_SRC-1])
+		memcpy(&src, RTA_DATA(rta[RTA_SRC-1]), 4);
+	if (rta[RTA_DST-1])
+		memcpy(&dst, RTA_DATA(rta[RTA_DST-1]), 4);
+	if (rta[RTA_IIF-1])
+		memcpy(&iif, RTA_DATA(rta[RTA_IIF-1]), sizeof(int));
 
-	if (rta->rta_iif) {
+	if (iif) {
 		struct device *dev;
-		dev = dev_get_by_index(*rta->rta_iif);
+		dev = dev_get_by_index(iif);
 		if (!dev)
 			return -ENODEV;
 		skb->protocol = __constant_htons(ETH_P_IP);
@@ -1430,8 +1492,10 @@
 		if (!err && rt->u.dst.error)
 			err = rt->u.dst.error;
 	} else {
-		err = ip_route_output(&rt, dst, src, rtm->rtm_tos,
-				      rta->rta_oif ? *rta->rta_oif : 0);
+		int oif = 0;
+		if (rta[RTA_OIF-1])
+			memcpy(&oif, RTA_DATA(rta[RTA_OIF-1]), sizeof(int));
+		err = ip_route_output(&rt, dst, src, rtm->rtm_tos, oif);
 	}
 	if (err) {
 		kfree_skb(skb);
@@ -1455,18 +1519,34 @@
 	rtm->rtm_scope = RT_SCOPE_UNIVERSE;
 	rtm->rtm_protocol = RTPROT_UNSPEC;
 	rtm->rtm_flags = (rt->rt_flags&~0xFFFF) | RTM_F_CLONED;
+#ifdef CONFIG_RTNL_OLD_IFINFO
 	rtm->rtm_nhs = 0;
 
 	o = skb->tail;
+#endif
 	RTA_PUT(skb, RTA_DST, 4, &rt->rt_dst);
 	RTA_PUT(skb, RTA_SRC, 4, &rt->rt_src);
 	if (rt->u.dst.dev)
 		RTA_PUT(skb, RTA_OIF, sizeof(int), &rt->u.dst.dev->ifindex);
 	if (rt->rt_dst != rt->rt_gateway)
 		RTA_PUT(skb, RTA_GATEWAY, 4, &rt->rt_gateway);
+#ifdef CONFIG_RTNL_OLD_IFINFO
 	RTA_PUT(skb, RTA_MTU, sizeof(unsigned), &rt->u.dst.pmtu);
 	RTA_PUT(skb, RTA_WINDOW, sizeof(unsigned), &rt->u.dst.window);
 	RTA_PUT(skb, RTA_RTT, sizeof(unsigned), &rt->u.dst.rtt);
+#else
+	mx = (struct rtattr*)skb->tail;
+	RTA_PUT(skb, RTA_METRICS, 0, NULL);
+	if (rt->u.dst.mxlock)
+		RTA_PUT(skb, RTAX_LOCK, sizeof(unsigned), &rt->u.dst.mxlock);
+	if (rt->u.dst.pmtu)
+		RTA_PUT(skb, RTAX_MTU, sizeof(unsigned), &rt->u.dst.pmtu);
+	if (rt->u.dst.window)
+		RTA_PUT(skb, RTAX_WINDOW, sizeof(unsigned), &rt->u.dst.window);
+	if (rt->u.dst.rtt)
+		RTA_PUT(skb, RTAX_RTT, sizeof(unsigned), &rt->u.dst.rtt);
+	mx->rta_len = skb->tail - (u8*)mx;
+#endif
 	RTA_PUT(skb, RTA_PREFSRC, 4, &rt->rt_spec_dst);
 	ci.rta_lastuse = jiffies - rt->u.dst.lastuse;
 	ci.rta_used = atomic_read(&rt->u.dst.refcnt);
@@ -1474,10 +1554,12 @@
 	ci.rta_expires = 0;
 	ci.rta_error = rt->u.dst.error;
 	RTA_PUT(skb, RTA_CACHEINFO, sizeof(ci), &ci);
+#ifdef CONFIG_RTNL_OLD_IFINFO
 	rtm->rtm_optlen = skb->tail - o;
-	if (rta->rta_iif) {
+#endif
+	if (iif) {
 #ifdef CONFIG_IP_MROUTE
-		if (MULTICAST(dst) && !LOCAL_MCAST(dst) && ipv4_config.multicast_route) {
+		if (MULTICAST(dst) && !LOCAL_MCAST(dst) && ipv4_devconf.mc_forwarding) {
 			NETLINK_CB(skb).pid = NETLINK_CB(in_skb).pid;
 			err = ipmr_get_route(skb, rtm);
 			if (err <= 0)
@@ -1485,8 +1567,10 @@
 		} else
 #endif
 		{
-			RTA_PUT(skb, RTA_IIF, 4, rta->rta_iif);
+			RTA_PUT(skb, RTA_IIF, sizeof(int), &iif);
+#ifdef CONFIG_RTNL_OLD_IFINFO
 			rtm->rtm_optlen = skb->tail - o;
+#endif
 		}
 	}
 	nlh->nlmsg_len = skb->tail - (u8*)nlh;
@@ -1505,9 +1589,71 @@
 
 void ip_rt_multicast_event(struct in_device *in_dev)
 {
-	rt_cache_flush(1*HZ);
+	rt_cache_flush(0);
 }
 
+
+
+#ifdef CONFIG_SYSCTL
+
+static int flush_delay;
+
+static
+int ipv4_sysctl_rtcache_flush(ctl_table *ctl, int write, struct file * filp,
+			      void *buffer, size_t *lenp)
+{
+	if (write) {
+		proc_dointvec(ctl, write, filp, buffer, lenp);
+		rt_cache_flush(flush_delay);
+		return 0;
+	} else
+		return -EINVAL;
+}
+
+ctl_table ipv4_route_table[] = {
+        {NET_IPV4_ROUTE_FLUSH, "flush",
+         &flush_delay, sizeof(int), 0644, NULL,
+         &ipv4_sysctl_rtcache_flush},
+	{NET_IPV4_ROUTE_MIN_DELAY, "min_delay",
+         &ip_rt_min_delay, sizeof(int), 0644, NULL,
+         &proc_dointvec_jiffies},
+	{NET_IPV4_ROUTE_MAX_DELAY, "max_delay",
+         &ip_rt_max_delay, sizeof(int), 0644, NULL,
+         &proc_dointvec_jiffies},
+	{NET_IPV4_ROUTE_GC_THRESH, "gc_thresh",
+         &ipv4_dst_ops.gc_thresh, sizeof(int), 0644, NULL,
+         &proc_dointvec},
+	{NET_IPV4_ROUTE_MAX_SIZE, "max_size",
+         &ip_rt_max_size, sizeof(int), 0644, NULL,
+         &proc_dointvec},
+	{NET_IPV4_ROUTE_GC_MIN_INTERVAL, "gc_min_interval",
+         &ip_rt_gc_min_interval, sizeof(int), 0644, NULL,
+         &proc_dointvec_jiffies},
+	{NET_IPV4_ROUTE_GC_TIMEOUT, "gc_timeout",
+         &ip_rt_gc_timeout, sizeof(int), 0644, NULL,
+         &proc_dointvec_jiffies},
+	{NET_IPV4_ROUTE_GC_INTERVAL, "gc_interval",
+         &ip_rt_gc_interval, sizeof(int), 0644, NULL,
+         &proc_dointvec_jiffies},
+	{NET_IPV4_ROUTE_REDIRECT_LOAD, "redirect_load",
+         &ip_rt_redirect_load, sizeof(int), 0644, NULL,
+         &proc_dointvec},
+	{NET_IPV4_ROUTE_REDIRECT_NUMBER, "redirect_number",
+         &ip_rt_redirect_number, sizeof(int), 0644, NULL,
+         &proc_dointvec},
+	{NET_IPV4_ROUTE_REDIRECT_SILENCE, "redirect_silence",
+         &ip_rt_redirect_silence, sizeof(int), 0644, NULL,
+         &proc_dointvec},
+	{NET_IPV4_ROUTE_ERROR_COST, "error_cost",
+         &ip_rt_error_cost, sizeof(int), 0644, NULL,
+         &proc_dointvec},
+	{NET_IPV4_ROUTE_ERROR_BURST, "error_burst",
+         &ip_rt_error_burst, sizeof(int), 0644, NULL,
+         &proc_dointvec},
+	 {0}
+};
+#endif
+
 __initfunc(void ip_rt_init(void))
 {
 	devinet_init();
@@ -1516,7 +1662,8 @@
 	/* All the timers, started at system startup tend
 	   to synchronize. Perturb it a bit.
 	 */
-	rt_periodic_timer.expires = jiffies + net_random()%RT_GC_INTERVAL + RT_GC_INTERVAL;
+	rt_periodic_timer.expires = jiffies + net_random()%ip_rt_gc_interval
+		+ ip_rt_gc_interval;
 	add_timer(&rt_periodic_timer);
 
 #ifdef CONFIG_PROC_FS
diff -u --recursive --new-file v2.1.88/linux/net/ipv4/syncookies.c linux/net/ipv4/syncookies.c
--- v2.1.88/linux/net/ipv4/syncookies.c	Sun Nov 30 14:00:39 1997
+++ linux/net/ipv4/syncookies.c	Sun Mar  1 14:40:41 1998
@@ -92,7 +92,7 @@
 	return isn; 
 }
 
-/* This value should be dependant on TCP_TIMEOUT_INIT and 
+/* This value should be dependent on TCP_TIMEOUT_INIT and 
  * sysctl_tcp_retries1. It's a rather complicated formula 
  * (exponential backoff) to compute at runtime so it's currently hardcoded
  * here.
@@ -203,7 +203,7 @@
 			    opt && 
 			    opt->srr ? opt->faddr : req->af.v4_req.rmt_addr,
 			    req->af.v4_req.loc_addr,
-			    sk->ip_tos, 
+			    sk->ip_tos | RTO_CONN,
 			    0)) { 
 	    tcp_openreq_free(req);
 	    return NULL; 
diff -u --recursive --new-file v2.1.88/linux/net/ipv4/sysctl_net_ipv4.c linux/net/ipv4/sysctl_net_ipv4.c
--- v2.1.88/linux/net/ipv4/sysctl_net_ipv4.c	Sun Jan 18 12:28:35 1998
+++ linux/net/ipv4/sysctl_net_ipv4.c	Sun Mar  1 14:40:41 1998
@@ -73,42 +73,27 @@
 extern int tcp_sysctl_congavoid(ctl_table *ctl, int write, struct file * filp,
 				void *buffer, size_t *lenp);
 
-struct ipv4_config ipv4_config = { 1, 1, 1, 0, };
+struct ipv4_config ipv4_config;
 
-#ifdef CONFIG_SYSCTL
+extern ctl_table ipv4_route_table[];
 
-struct ipv4_config ipv4_def_router_config = { 0, 1, 1, 1, 1, 1, 1, };
-struct ipv4_config ipv4_def_host_config = { 1, 1, 1, 0, };
+#ifdef CONFIG_SYSCTL
 
 static
-int ipv4_sysctl_forwarding(ctl_table *ctl, int write, struct file * filp,
-			   void *buffer, size_t *lenp)
+int ipv4_sysctl_forward(ctl_table *ctl, int write, struct file * filp,
+			void *buffer, size_t *lenp)
 {
-	int val = IS_ROUTER;
+	int val = ipv4_devconf.forwarding;
 	int ret;
 
 	ret = proc_dointvec(ctl, write, filp, buffer, lenp);
 
-	if (write && IS_ROUTER != val) {
-		if (IS_ROUTER)
-			ipv4_config = ipv4_def_router_config;
-		else
-			ipv4_config = ipv4_def_host_config;
-		rt_cache_flush(0);
-        }
+	if (write && ipv4_devconf.forwarding != val)
+		inet_forward_change();
+
         return ret;
 }
 
-static
-int ipv4_sysctl_rtcache_flush(ctl_table *ctl, int write, struct file * filp,
-			      void *buffer, size_t *lenp)
-{
-	if (write) {
-		rt_cache_flush(0);
-		return 0;
-	} else
-		return -EINVAL;
-}
 
 ctl_table ipv4_table[] = {
         {NET_IPV4_TCP_HOE_RETRANSMITS, "tcp_hoe_retransmits",
@@ -129,55 +114,25 @@
 	{NET_IPV4_TCP_VEGAS_CONG_AVOID, "tcp_vegas_cong_avoid",
 	 &sysctl_tcp_cong_avoidance, sizeof(int), 0644,
 	 NULL, &tcp_sysctl_congavoid },
-        {NET_IPV4_FORWARDING, "ip_forwarding",
-         &ip_statistics.IpForwarding, sizeof(int), 0644, NULL,
-         &ipv4_sysctl_forwarding},
+        {NET_IPV4_FORWARD, "ip_forward",
+         &ipv4_devconf.forwarding, sizeof(int), 0644, NULL,
+         &ipv4_sysctl_forward},
         {NET_IPV4_DEFAULT_TTL, "ip_default_ttl",
          &ip_statistics.IpDefaultTTL, sizeof(int), 0644, NULL,
          &proc_dointvec},
-        {NET_IPV4_RFC1812_FILTER, "ip_rfc1812_filter",
-         &ipv4_config.rfc1812_filter, sizeof(int), 0644, NULL,
-         &proc_dointvec},
-        {NET_IPV4_LOG_MARTIANS, "ip_log_martians",
-         &ipv4_config.log_martians, sizeof(int), 0644, NULL,
-         &proc_dointvec},
-        {NET_IPV4_SOURCE_ROUTE, "ip_source_route",
-         &ipv4_config.source_route, sizeof(int), 0644, NULL,
-         &proc_dointvec},
-        {NET_IPV4_SEND_REDIRECTS, "ip_send_redirects",
-         &ipv4_config.send_redirects, sizeof(int), 0644, NULL,
-         &proc_dointvec},
         {NET_IPV4_AUTOCONFIG, "ip_autoconfig",
          &ipv4_config.autoconfig, sizeof(int), 0644, NULL,
          &proc_dointvec},
-        {NET_IPV4_BOOTP_RELAY, "ip_bootp_relay",
-         &ipv4_config.bootp_relay, sizeof(int), 0644, NULL,
-         &proc_dointvec},
-        {NET_IPV4_PROXY_ARP, "ip_proxy_arp",
-         &ipv4_config.proxy_arp, sizeof(int), 0644, NULL,
-         &proc_dointvec},
         {NET_IPV4_NO_PMTU_DISC, "ip_no_pmtu_disc",
          &ipv4_config.no_pmtu_disc, sizeof(int), 0644, NULL,
          &proc_dointvec},
-        {NET_IPV4_ACCEPT_REDIRECTS, "ip_accept_redirects",
-         &ipv4_config.accept_redirects, sizeof(int), 0644, NULL,
-         &proc_dointvec},
-        {NET_IPV4_SECURE_REDIRECTS, "ip_secure_redirects",
-         &ipv4_config.secure_redirects, sizeof(int), 0644, NULL,
-         &proc_dointvec},
-        {NET_IPV4_RFC1620_REDIRECTS, "ip_rfc1620_redirects",
-         &ipv4_config.rfc1620_redirects, sizeof(int), 0644, NULL,
-         &proc_dointvec},
-        {NET_IPV4_RTCACHE_FLUSH, "ip_rtcache_flush",
-         NULL, sizeof(int), 0644, NULL,
-         &ipv4_sysctl_rtcache_flush},
 	{NET_IPV4_TCP_SYN_RETRIES, "tcp_syn_retries",
 	 &sysctl_tcp_syn_retries, sizeof(int), 0644, NULL, &proc_dointvec},
 	{NET_IPV4_IPFRAG_HIGH_THRESH, "ipfrag_high_thresh",
 	 &sysctl_ipfrag_high_thresh, sizeof(int), 0644, NULL, &proc_dointvec},
 	{NET_IPV4_IPFRAG_LOW_THRESH, "ipfrag_low_thresh",
 	 &sysctl_ipfrag_low_thresh, sizeof(int), 0644, NULL, &proc_dointvec},
-	{NET_IPV4_IP_DYNADDR, "ip_dynaddr",
+	{NET_IPV4_DYNADDR, "ip_dynaddr",
 	 &sysctl_ip_dynaddr, sizeof(int), 0644, NULL, &proc_dointvec},
 #ifdef CONFIG_IP_MASQUERADE
 	{NET_IPV4_IP_MASQ_DEBUG, "ip_masq_debug",
@@ -230,6 +185,7 @@
 	 &sysctl_icmp_paramprob_time, sizeof(int), 0644, NULL, &proc_dointvec},
 	{NET_IPV4_ICMP_ECHOREPLY_RATE, "icmp_echoreply_rate",
 	 &sysctl_icmp_echoreply_time, sizeof(int), 0644, NULL, &proc_dointvec},
+	{NET_IPV4_ROUTE, "route", NULL, 0, 0555, ipv4_route_table},
 	{0}
 };
 
diff -u --recursive --new-file v2.1.88/linux/net/ipv4/tcp.c linux/net/ipv4/tcp.c
--- v2.1.88/linux/net/ipv4/tcp.c	Thu Feb 12 20:56:14 1998
+++ linux/net/ipv4/tcp.c	Sun Mar  1 14:40:41 1998
@@ -196,6 +196,7 @@
  *					improvement.
  *	Stefan Magdalinski	:	adjusted tcp_readable() to fix FIONREAD
  *	Willy Konynenberg	:	Transparent proxying support.
+ *	Mike McLagan		:	Routing by source
  *		Keith Owens	:	Do proper meging with partial SKB's in
  *					tcp_do_sendmsg to avoid burstiness.
  *		Eric Schenk	:	Fix fast close down bug with
@@ -585,13 +586,13 @@
  *	take care of normal races (between the test and the event) and we don't
  *	go look at any of the socket buffers directly.
  */
-unsigned int tcp_poll(struct socket *sock, poll_table *wait)
+unsigned int tcp_poll(struct file * file, struct socket *sock, poll_table *wait)
 {
 	unsigned int mask;
 	struct sock *sk = sock->sk;
 	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
 
-	poll_wait(sk->sleep, wait);
+	poll_wait(file, sk->sleep, wait);
 	if (sk->state == TCP_LISTEN)
 		return tcp_listen_poll(sk, wait);
 
diff -u --recursive --new-file v2.1.88/linux/net/ipv4/tcp_input.c linux/net/ipv4/tcp_input.c
--- v2.1.88/linux/net/ipv4/tcp_input.c	Thu Feb 12 20:56:14 1998
+++ linux/net/ipv4/tcp_input.c	Sun Mar  1 14:40:41 1998
@@ -214,7 +214,7 @@
 	/* FIXME: must check that ts_recent is not
  	 * more than 24 days old here. Yuck.
  	 */
-	return (tp->rcv_tsval-tp->ts_recent < 0);
+	return ((s32)(tp->rcv_tsval-tp->ts_recent) < 0);
 }
 
 
diff -u --recursive --new-file v2.1.88/linux/net/ipv4/tcp_ipv4.c linux/net/ipv4/tcp_ipv4.c
--- v2.1.88/linux/net/ipv4/tcp_ipv4.c	Mon Feb 23 18:12:13 1998
+++ linux/net/ipv4/tcp_ipv4.c	Sun Mar  1 14:40:41 1998
@@ -40,6 +40,7 @@
  *					Added tail drop and some other bugfixes.
  *					Added new listen sematics (ifdefed by
  *					NEW_LISTEN for now)
+ *		Mike McLagan	:	Routing by source
  *	Juan Jose Ciarlante:		ip_dynaddr bits
  *		Andi Kleen:		various fixes.
  *	Vitaly E. Lavrov	:	Transparent proxy revived after year coma.
@@ -174,7 +175,7 @@
 	return 0;
 }
 
-/* Find a "good" local port, this is family independant.
+/* Find a "good" local port, this is family independent.
  * There are several strategies working in unison here to
  * get the best possible performance.  The current socket
  * load is kept track of, if it is zero there is a strong
@@ -565,13 +566,10 @@
 			printk(KERN_DEBUG "%s forgot to set AF_INET in " __FUNCTION__ "\n", current->comm);
 	}
 
-	if (sk->dst_cache) {
-		dst_release(sk->dst_cache);
-		sk->dst_cache = NULL;
-	}
+	dst_release(xchg(&sk->dst_cache, NULL));
 
 	tmp = ip_route_connect(&rt, usin->sin_addr.s_addr, sk->saddr,
-			       RT_TOS(sk->ip_tos)|(sk->localroute || 0), sk->bound_dev_if);
+			       RT_TOS(sk->ip_tos)|sk->localroute, sk->bound_dev_if);
 	if (tmp < 0)
 		return tmp;
 
@@ -651,7 +649,7 @@
 	sk->mtu = rt->u.dst.pmtu;
 	if ((sk->ip_pmtudisc == IP_PMTUDISC_DONT ||
 	     (sk->ip_pmtudisc == IP_PMTUDISC_WANT &&
-	      rt->rt_flags&RTCF_NOPMTUDISC)) &&
+	      (rt->u.dst.mxlock&(1<<RTAX_MTU)))) &&
 	    rt->u.dst.pmtu > 576)
 		sk->mtu = 576;
 
@@ -1403,7 +1401,7 @@
 		if (ip_route_output(&rt,
 				    newsk->opt && newsk->opt->srr ? 
 				    newsk->opt->faddr : newsk->daddr,
-				    newsk->saddr, newsk->ip_tos, 0)) {
+				    newsk->saddr, newsk->ip_tos|RTO_CONN, 0)) {
 			sk_free(newsk);
 			return NULL;
 		}
@@ -1668,7 +1666,7 @@
 
 		/* Query new route */
 		tmp = ip_route_connect(&rt, rt->rt_dst, 0, 
-					RT_TOS(sk->ip_tos)|(sk->localroute||0),
+					RT_TOS(sk->ip_tos)|sk->localroute,
 					sk->bound_dev_if);
 
 		/* Only useful if different source addrs */
@@ -1682,7 +1680,7 @@
 	} else 
 	if (rt->u.dst.obsolete) {
 		int err;
-		err = ip_route_output(&rt, rt->rt_dst, rt->rt_src, rt->key.tos, rt->key.oif);
+		err = ip_route_output(&rt, rt->rt_dst, rt->rt_src, rt->key.tos|RTO_CONN, rt->key.oif);
 		if (err) {
 			sk->err_soft=-err;
 			sk->error_report(skb->sk);
diff -u --recursive --new-file v2.1.88/linux/net/ipv4/udp.c linux/net/ipv4/udp.c
--- v2.1.88/linux/net/ipv4/udp.c	Mon Feb 23 18:12:13 1998
+++ linux/net/ipv4/udp.c	Fri Mar  6 20:49:37 1998
@@ -49,6 +49,7 @@
  *		Mike Shaver	:	RFC1122 checks.
  *		Alan Cox	:	Nonblocking error fix.
  *	Willy Konynenberg	:	Transparent proxying support.
+ *		Mike McLagan	:	Routing by source
  *		David S. Miller	:	New socket lookup architecture.
  *					Last socket cache retained as it
  *					does have a high hit rate.
@@ -816,8 +817,10 @@
 			struct sk_buff *skb;
 			unsigned long amount;
 
-			if (sk->state == TCP_LISTEN) return(-EINVAL);
+			if (sk->state == TCP_LISTEN)
+				return(-EINVAL);
 			amount = 0;
+			/* N.B. Is this interrupt safe?? */
 			skb = skb_peek(&sk->receive_queue);
 			if (skb != NULL) {
 				/*
@@ -843,13 +846,11 @@
  */
 
 int udp_recvmsg(struct sock *sk, struct msghdr *msg, int len,
-	     int noblock, int flags,int *addr_len)
+	     int noblock, int flags, int *addr_len)
 {
-  	int copied = 0;
-  	int truesize;
+  	struct sockaddr_in *sin = (struct sockaddr_in *)msg->msg_name;
   	struct sk_buff *skb;
-  	int er;
-  	struct sockaddr_in *sin=(struct sockaddr_in *)msg->msg_name;
+  	int copied, err;
 
 	/*
 	 *	Check any passed addresses
@@ -859,14 +860,12 @@
   		*addr_len=sizeof(*sin);
 
 	if (sk->ip_recverr && (skb = skb_dequeue(&sk->error_queue)) != NULL) {
-		er = sock_error(sk);
-		if (msg->msg_controllen == 0) {
-			skb_free_datagram(sk, skb);
-			return er;
+		err = sock_error(sk);
+		if (msg->msg_controllen != 0) {
+			put_cmsg(msg, SOL_IP, IP_RECVERR, skb->len, skb->data);
+			err = 0;
 		}
-		put_cmsg(msg, SOL_IP, IP_RECVERR, skb->len, skb->data);
-		skb_free_datagram(sk, skb);
-		return 0;
+		goto out_free;
 	}
   
 	/*
@@ -874,25 +873,25 @@
 	 *	the finished NET3, it will do _ALL_ the work!
 	 */
 	 	
-	skb=skb_recv_datagram(sk,flags,noblock,&er);
-	if(skb==NULL)
-  		return er;
+	skb = skb_recv_datagram(sk, flags, noblock, &err);
+	if (!skb)
+		goto out;
   
-  	truesize = skb->len - sizeof(struct udphdr);
-	copied = truesize;
-	if (len < truesize)
+  	copied = skb->len - sizeof(struct udphdr);
+	if (copied > len)
 	{
-		msg->msg_flags |= MSG_TRUNC;
 		copied = len;
+		msg->msg_flags |= MSG_TRUNC;
 	}
 
   	/*
   	 *	FIXME : should use udp header size info value 
   	 */
   	 
-	er = skb_copy_datagram_iovec(skb,sizeof(struct udphdr),msg->msg_iov,copied);
-	if (er)
-		return er; 
+	err = skb_copy_datagram_iovec(skb, sizeof(struct udphdr), msg->msg_iov,
+					copied);
+	if (err)
+		goto out_free;
 	sk->stamp=skb->stamp;
 
 	/* Copy the address. */
@@ -921,9 +920,12 @@
   	}
 	if (sk->ip_cmsg_flags)
 		ip_cmsg_recv(msg, skb);
+	err = copied;
   
+out_free:
   	skb_free_datagram(sk, skb);
-  	return(copied);
+out:
+  	return err;
 }
 
 int udp_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
@@ -954,8 +956,7 @@
 	if (usin->sin_family && usin->sin_family != AF_INET) 
 	  	return(-EAFNOSUPPORT);
 
-	dst_release(sk->dst_cache);
-	sk->dst_cache = NULL;
+	dst_release(xchg(&sk->dst_cache, NULL));
 
 	err = ip_route_connect(&rt, usin->sin_addr.s_addr, sk->saddr,
 			       sk->ip_tos|sk->localroute, sk->bound_dev_if);
diff -u --recursive --new-file v2.1.88/linux/net/ipv6/addrconf.c linux/net/ipv6/addrconf.c
--- v2.1.88/linux/net/ipv6/addrconf.c	Mon Feb 23 18:12:13 1998
+++ linux/net/ipv6/addrconf.c	Sun Mar  1 14:40:41 1998
@@ -63,6 +63,11 @@
 #define ADBG(x)
 #endif
 
+#ifdef CONFIG_SYSCTL
+static void addrconf_sysctl_register(struct inet6_dev *idev, struct ipv6_devconf *p);
+static void addrconf_sysctl_unregister(struct ipv6_devconf *p);
+#endif
+
 /*
  *	Configured unicast address list
  */
@@ -90,6 +95,34 @@
 static void addrconf_rs_timer(unsigned long data);
 static void ipv6_ifa_notify(int event, struct inet6_ifaddr *ifa);
 
+struct ipv6_devconf ipv6_devconf =
+{
+	0,				/* forwarding		*/
+	IPV6_DEFAULT_HOPLIMIT,		/* hop limit		*/
+	576,				/* mtu			*/
+	1,				/* accept RAs		*/
+	1,				/* accept redirects	*/
+	1,				/* autoconfiguration	*/
+	1,				/* dad transmits	*/
+	MAX_RTR_SOLICITATIONS,		/* router solicits	*/
+	RTR_SOLICITATION_INTERVAL,	/* rtr solicit interval	*/
+	MAX_RTR_SOLICITATION_DELAY,	/* rtr solicit delay	*/
+};
+
+static struct ipv6_devconf ipv6_devconf_dflt =
+{
+	0,				/* forwarding		*/
+	IPV6_DEFAULT_HOPLIMIT,		/* hop limit		*/
+	576,				/* mtu			*/
+	1,				/* accept RAs		*/
+	1,				/* accept redirects	*/
+	1,				/* autoconfiguration	*/
+	1,				/* dad transmits	*/
+	MAX_RTR_SOLICITATIONS,		/* router solicits	*/
+	RTR_SOLICITATION_INTERVAL,	/* rtr solicit interval	*/
+	MAX_RTR_SOLICITATION_DELAY,	/* rtr solicit delay	*/
+};
+
 int ipv6_addr_type(struct in6_addr *addr)
 {
 	u32 st;
@@ -151,19 +184,26 @@
 	struct inet6_dev *ndev, **bptr, *iter;
 	int hash;
 
+	if (dev->mtu < 576)
+		return NULL;
+
 	ndev = kmalloc(sizeof(struct inet6_dev), gfp_any());
 
 	if (ndev) {
 		memset(ndev, 0, sizeof(struct inet6_dev));
 
 		ndev->dev = dev;
-		ndev->nd_parms = neigh_parms_alloc(&nd_tbl);
-		if (ndev->nd_parms == NULL)
-			ndev->nd_parms = &nd_tbl.parms;
+		memcpy(&ndev->cnf, &ipv6_devconf_dflt, sizeof(ndev->cnf));
+		ndev->cnf.mtu6 = dev->mtu;
+		ndev->cnf.sysctl = NULL;
+		ndev->nd_parms = neigh_parms_alloc(dev, &nd_tbl);
+		if (ndev->nd_parms == NULL) {
+			kfree(ndev);
+			return NULL;
+		}
 #ifdef CONFIG_SYSCTL
-		else
-			ndev->nd_parms->sysctl_table =
-				neigh_sysctl_register(dev, ndev->nd_parms, NET_IPV6, NET_IPV6_NEIGH, "ipv6");
+		neigh_sysctl_register(dev, ndev->nd_parms, NET_IPV6, NET_IPV6_NEIGH, "ipv6");
+		addrconf_sysctl_register(ndev, &ndev->cnf);
 #endif
 		hash = ipv6_devindex_hash(dev->ifindex);
 		bptr = &inet6_dev_lst[hash];
@@ -192,19 +232,16 @@
 	return idev;
 }
 
-void addrconf_forwarding_on(void)
+static void addrconf_forward_change(struct inet6_dev *idev)
 {
-	struct inet6_dev *idev;
 	int i;
 
-	for (i = 0; i < IN6_ADDR_HSIZE; i++) {
-		for (idev = inet6_dev_lst[i]; idev; idev = idev->next) {
-#if ACONF_DEBUG >= 2
-			printk(KERN_DEBUG "dev %s\n", idev->dev->name);
-#endif
+	if (idev)
+		return;
 
-			idev->router = 1;
-		}
+	for (i = 0; i < IN6_ADDR_HSIZE; i++) {
+		for (idev = inet6_dev_lst[i]; idev; idev = idev->next)
+			idev->cnf.forwarding = ipv6_devconf.forwarding;
 	}
 }
 
@@ -597,6 +634,12 @@
 	__u32 prefered_lft;
 	int addr_type;
 	unsigned long rt_expires;
+	struct inet6_dev *in6_dev = ipv6_get_idev(dev);
+
+	if (in6_dev == NULL) {
+		printk(KERN_DEBUG "addrconf: device %s not configured\n", dev->name);
+		return;
+	}
 
 	pinfo = (struct prefix_info *) opt;
 	
@@ -661,7 +704,7 @@
 
 	/* Try to figure out our local address for this prefix */
 
-	if (pinfo->autoconf && ipv6_config.autoconf) {
+	if (pinfo->autoconf && in6_dev->cnf.autoconf) {
 		struct inet6_ifaddr * ifp;
 		struct in6_addr addr;
 		int plen;
@@ -691,12 +734,6 @@
 		ifp = ipv6_chk_addr(&addr, dev, 1);
 
 		if ((ifp == NULL || (ifp->flags&ADDR_INVALID)) && valid_lft) {
-			struct inet6_dev *in6_dev = ipv6_get_idev(dev);
-
-			if (in6_dev == NULL) {
-				printk(KERN_DEBUG "addrconf: device %s not configured\n", dev->name);
-				return;
-			}
 
 			if (ifp == NULL)
 				ifp = ipv6_add_addr(in6_dev, &addr, addr_type & IPV6_ADDR_SCOPE_MASK);
@@ -873,12 +910,12 @@
 	
 	if (!suser())
 		return -EPERM;
-	
+
 	if (copy_from_user(&ireq, arg, sizeof(struct in6_ifreq)))
 		return -EFAULT;
 
 	rtnl_lock();
-	err = inet6_addr_add(ireq.ifr6_ifindex, &ireq.ifr6_addr, ireq.ifr6_prefixlen);
+	err = inet6_addr_del(ireq.ifr6_ifindex, &ireq.ifr6_addr, ireq.ifr6_prefixlen);
 	rtnl_unlock();
 	return err;
 }
@@ -1021,9 +1058,6 @@
 	       dev->dev_addr, dev->addr_len);
 	addrconf_add_linklocal(idev, &addr);
 #endif
-
-	if (ipv6_config.forwarding)
-		idev->router = 1;
 }
 
 static void addrconf_sit_config(struct device *dev)
@@ -1079,21 +1113,26 @@
 #endif
 		break;
 
+	case NETDEV_CHANGEMTU:
+		/* BUGGG... Should scan FIB to change pmtu on routes. --ANK */
+		if (dev->mtu >= 576)
+			break;
+
+		/* MTU falled under 576. Stop IPv6 on this interface. */
+
 	case NETDEV_DOWN:
 	case NETDEV_UNREGISTER:
 		/*
 		 *	Remove all addresses from this interface.
 		 */
-		if (addrconf_ifdown(dev, event == NETDEV_UNREGISTER) == 0) {
+		if (addrconf_ifdown(dev, event != NETDEV_DOWN) == 0) {
 #ifdef CONFIG_IPV6_NETLINK
 			rt6_sndmsg(RTMSG_DELDEVICE, NULL, NULL, NULL, dev, 0, 0, 0, 0);
 #endif
 		}
 
 		break;
-	case NETDEV_CHANGEMTU:
 	case NETDEV_CHANGE:
-		/* BUGGG... Should scan FIB to change pmtu on routes. --ANK */
 		break;
 	};
 
@@ -1154,6 +1193,9 @@
 			if (idev->dev == dev) {
 				*bidev = idev->next;
 				neigh_parms_release(&nd_tbl, idev->nd_parms);
+#ifdef CONFIG_SYSCTL
+				addrconf_sysctl_unregister(&idev->cnf);
+#endif
 				kfree(idev);
 				break;
 			}
@@ -1170,7 +1212,7 @@
 
 	ifp = (struct inet6_ifaddr *) data;
 
-	if (ipv6_config.forwarding)
+	if (ifp->idev->cnf.forwarding)
 		return;
 
 	if (ifp->idev->if_flags & IF_RA_RCVD) {
@@ -1181,7 +1223,7 @@
 		return;
 	}
 
-	if (ifp->probes++ <= ipv6_config.rtr_solicits) {
+	if (ifp->probes++ <= ifp->idev->cnf.rtr_solicits) {
 		struct in6_addr all_routers;
 
 		ipv6_addr_all_routers(&all_routers);
@@ -1190,7 +1232,7 @@
 		
 		ifp->timer.function = addrconf_rs_timer;
 		ifp->timer.expires = (jiffies + 
-				      ipv6_config.rtr_solicit_interval);
+				      ifp->idev->cnf.rtr_solicit_interval);
 		add_timer(&ifp->timer);
 	} else {
 		struct in6_rtmsg rtmsg;
@@ -1236,10 +1278,10 @@
 
 	net_srandom(ifp->addr.s6_addr32[3]);
 
-	ifp->probes = ipv6_config.dad_transmits;
+	ifp->probes = ifp->idev->cnf.dad_transmits;
 	ifp->flags |= DAD_INCOMPLETE;
 
-	rand_num = net_random() % ipv6_config.rtr_solicit_delay;
+	rand_num = net_random() % ifp->idev->cnf.rtr_solicit_delay;
 
 	ifp->timer.function = addrconf_dad_timer;
 	ifp->timer.expires = jiffies + rand_num;
@@ -1278,7 +1320,7 @@
 	ndisc_send_ns(ifp->idev->dev, NULL, &ifp->addr, &mcaddr, &unspec);
 #endif
 
-	ifp->timer.expires = jiffies + ipv6_config.rtr_solicit_interval;
+	ifp->timer.expires = jiffies + ifp->idev->cnf.rtr_solicit_interval;
 	add_timer(&ifp->timer);
 }
 
@@ -1296,7 +1338,7 @@
 	   start sending router solicitations.
 	 */
 
-	if (ipv6_config.forwarding == 0 &&
+	if (ifp->idev->cnf.forwarding == 0 &&
 	    (dev->flags&(IFF_NOARP|IFF_LOOPBACK)) == 0 &&
 	    (ipv6_addr_type(&ifp->addr) & IPV6_ADDR_LINKLOCAL)) {
 		struct in6_addr all_routers;
@@ -1313,7 +1355,7 @@
 		ifp->probes = 1;
 		ifp->timer.function = addrconf_rs_timer;
 		ifp->timer.expires = (jiffies +
-				      ipv6_config.rtr_solicit_interval);
+				      ifp->idev->cnf.rtr_solicit_interval);
 		ifp->idev->if_flags |= IF_RS_SENT;
 		add_timer(&ifp->timer);
 	}
@@ -1409,20 +1451,24 @@
 }
 
 #ifdef CONFIG_RTNETLINK
+
 static int
 inet6_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
 {
-	struct kern_ifa  *k_ifa = arg;
+	struct rtattr **rta = arg;
 	struct ifaddrmsg *ifm = NLMSG_DATA(nlh);
 	struct in6_addr *pfx;
 
 	pfx = NULL;
-	if (k_ifa->ifa_address)
-		pfx = k_ifa->ifa_address;
-	if (k_ifa->ifa_local) {
-		if (pfx && memcmp(pfx, k_ifa->ifa_local, sizeof(*pfx)))
+	if (rta[IFA_ADDRESS-1]) {
+		if (RTA_PAYLOAD(rta[IFA_ADDRESS-1]) < sizeof(*pfx))
 			return -EINVAL;
-		pfx = k_ifa->ifa_local;
+		pfx = RTA_DATA(rta[IFA_ADDRESS-1]);
+	}
+	if (rta[IFA_LOCAL-1]) {
+		if (pfx && memcmp(pfx, RTA_DATA(rta[IFA_LOCAL-1]), sizeof(*pfx)))
+			return -EINVAL;
+		pfx = RTA_DATA(rta[IFA_LOCAL-1]);
 	}
 
 	return inet6_addr_del(ifm->ifa_index, pfx, ifm->ifa_prefixlen);
@@ -1431,17 +1477,20 @@
 static int
 inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
 {
-	struct kern_ifa  *k_ifa = arg;
+	struct rtattr  **rta = arg;
 	struct ifaddrmsg *ifm = NLMSG_DATA(nlh);
 	struct in6_addr *pfx;
 
 	pfx = NULL;
-	if (k_ifa->ifa_address)
-		pfx = k_ifa->ifa_address;
-	if (k_ifa->ifa_local) {
-		if (pfx && memcmp(pfx, k_ifa->ifa_local, sizeof(*pfx)))
+	if (rta[IFA_ADDRESS-1]) {
+		if (RTA_PAYLOAD(rta[IFA_ADDRESS-1]) < sizeof(*pfx))
+			return -EINVAL;
+		pfx = RTA_DATA(rta[IFA_ADDRESS-1]);
+	}
+	if (rta[IFA_LOCAL-1]) {
+		if (pfx && memcmp(pfx, RTA_DATA(rta[IFA_LOCAL-1]), sizeof(*pfx)))
 			return -EINVAL;
-		pfx = k_ifa->ifa_local;
+		pfx = RTA_DATA(rta[IFA_LOCAL-1]);
 	}
 
 	return inet6_addr_add(ifm->ifa_index, pfx, ifm->ifa_prefixlen);
@@ -1485,7 +1534,7 @@
 
 nlmsg_failure:
 rtattr_failure:
-	skb_put(skb, b - skb->tail);
+	skb_trim(skb, b - skb->data);
 	return -1;
 }
 
@@ -1528,7 +1577,7 @@
 	struct sk_buff *skb;
 	int size = NLMSG_SPACE(sizeof(struct ifaddrmsg)+128);
 
-	skb = alloc_skb(size, GFP_KERNEL);
+	skb = alloc_skb(size, GFP_ATOMIC);
 	if (!skb) {
 		netlink_set_err(rtnl, 0, RTMGRP_IPV6_IFADDR, ENOBUFS);
 		return;
@@ -1546,7 +1595,7 @@
 {
 	{ NULL,			NULL,			},
 	{ NULL,			NULL,			},
-	{ NULL,			rtnetlink_dump_ifinfo,	},
+	{ NULL,			NULL,			},
 	{ NULL,			NULL,			},
 
 	{ inet6_rtm_newaddr,	NULL,			},
@@ -1558,16 +1607,6 @@
 	{ inet6_rtm_delroute,	NULL,			},
 	{ NULL,			inet6_dump_fib,		},
 	{ NULL,			NULL,			},
-
-	{ neigh_add,		NULL,			},
-	{ neigh_delete,		NULL,			},
-	{ NULL,			neigh_dump_info,	},
-	{ NULL,			NULL,			},
-
-	{ NULL,			NULL,			},
-	{ NULL,			NULL,			},
-	{ NULL,			NULL,			},
-	{ NULL,			NULL,			},
 };
 #endif
 
@@ -1590,6 +1629,144 @@
 	}
 }
 
+#ifdef CONFIG_SYSCTL
+
+static
+int addrconf_sysctl_forward(ctl_table *ctl, int write, struct file * filp,
+			   void *buffer, size_t *lenp)
+{
+	int *valp = ctl->data;
+	int val = *valp;
+	int ret;
+
+	ret = proc_dointvec(ctl, write, filp, buffer, lenp);
+
+	if (write && *valp != val && valp != &ipv6_devconf_dflt.forwarding) {
+		struct inet6_dev *idev = NULL;
+
+		if (valp != &ipv6_devconf.forwarding) {
+			struct device *dev = dev_get_by_index(ctl->ctl_name);
+			if (dev)
+				idev = ipv6_get_idev(dev);
+			if (idev == NULL)
+				return ret;
+		} else
+			ipv6_devconf_dflt.forwarding = ipv6_devconf.forwarding;
+
+		addrconf_forward_change(idev);
+
+		if (*valp)
+			rt6_purge_dflt_routers(0);
+	}
+
+        return ret;
+}
+
+static struct addrconf_sysctl_table
+{
+	struct ctl_table_header *sysctl_header;
+	ctl_table addrconf_vars[11];
+	ctl_table addrconf_dev[2];
+	ctl_table addrconf_conf_dir[2];
+	ctl_table addrconf_proto_dir[2];
+	ctl_table addrconf_root_dir[2];
+} addrconf_sysctl = {
+	NULL,
+        {{NET_IPV6_FORWARDING, "forwarding",
+         &ipv6_devconf.forwarding, sizeof(int), 0644, NULL,
+         &addrconf_sysctl_forward},
+
+	{NET_IPV6_HOP_LIMIT, "hop_limit",
+         &ipv6_devconf.hop_limit, sizeof(int), 0644, NULL,
+         &proc_dointvec},
+
+	{NET_IPV6_MTU, "mtu",
+         &ipv6_devconf.mtu6, sizeof(int), 0644, NULL,
+         &proc_dointvec},
+
+	{NET_IPV6_ACCEPT_RA, "accept_ra",
+         &ipv6_devconf.accept_ra, sizeof(int), 0644, NULL,
+         &proc_dointvec},
+
+	{NET_IPV6_ACCEPT_REDIRECTS, "accept_redirects",
+         &ipv6_devconf.accept_redirects, sizeof(int), 0644, NULL,
+         &proc_dointvec},
+
+	{NET_IPV6_AUTOCONF, "autoconf",
+         &ipv6_devconf.autoconf, sizeof(int), 0644, NULL,
+         &proc_dointvec},
+
+	{NET_IPV6_DAD_TRANSMITS, "dad_transmits",
+         &ipv6_devconf.dad_transmits, sizeof(int), 0644, NULL,
+         &proc_dointvec},
+
+	{NET_IPV6_RTR_SOLICITS, "router_solicitations",
+         &ipv6_devconf.rtr_solicits, sizeof(int), 0644, NULL,
+         &proc_dointvec},
+
+	{NET_IPV6_RTR_SOLICIT_INTERVAL, "router_solicitation_interval",
+         &ipv6_devconf.rtr_solicit_interval, sizeof(int), 0644, NULL,
+         &proc_dointvec_jiffies},
+
+	{NET_IPV6_RTR_SOLICIT_DELAY, "router_solicitation_delay",
+         &ipv6_devconf.rtr_solicit_delay, sizeof(int), 0644, NULL,
+         &proc_dointvec_jiffies},
+
+	{0}},
+
+	{{NET_PROTO_CONF_ALL, "all", NULL, 0, 0555, addrconf_sysctl.addrconf_vars},{0}},
+	{{NET_IPV6_CONF, "conf", NULL, 0, 0555, addrconf_sysctl.addrconf_dev},{0}},
+	{{NET_IPV6, "ipv6", NULL, 0, 0555, addrconf_sysctl.addrconf_conf_dir},{0}},
+	{{CTL_NET, "net", NULL, 0, 0555, addrconf_sysctl.addrconf_proto_dir},{0}}
+};
+
+static void addrconf_sysctl_register(struct inet6_dev *idev, struct ipv6_devconf *p)
+{
+	int i;
+	struct device *dev = idev ? idev->dev : NULL;
+	struct addrconf_sysctl_table *t;
+
+	t = kmalloc(sizeof(*t), GFP_KERNEL);
+	if (t == NULL)
+		return;
+	memcpy(t, &addrconf_sysctl, sizeof(*t));
+	for (i=0; i<sizeof(t->addrconf_vars)/sizeof(t->addrconf_vars[0])-1; i++) {
+		t->addrconf_vars[i].data += (char*)p - (char*)&ipv6_devconf;
+		t->addrconf_vars[i].de = NULL;
+	}
+	if (dev) {
+		t->addrconf_dev[0].procname = dev->name;
+		t->addrconf_dev[0].ctl_name = dev->ifindex;
+	} else {
+		t->addrconf_dev[0].procname = "default";
+		t->addrconf_dev[0].ctl_name = NET_PROTO_CONF_DEFAULT;
+	}
+	t->addrconf_dev[0].child = t->addrconf_vars;
+	t->addrconf_dev[0].de = NULL;
+	t->addrconf_conf_dir[0].child = t->addrconf_dev;
+	t->addrconf_conf_dir[0].de = NULL;
+	t->addrconf_proto_dir[0].child = t->addrconf_conf_dir;
+	t->addrconf_proto_dir[0].de = NULL;
+	t->addrconf_root_dir[0].child = t->addrconf_proto_dir;
+	t->addrconf_root_dir[0].de = NULL;
+
+	t->sysctl_header = register_sysctl_table(t->addrconf_root_dir, 0);
+	if (t->sysctl_header == NULL)
+		kfree(t);
+}
+
+static void addrconf_sysctl_unregister(struct ipv6_devconf *p)
+{
+	if (p->sysctl) {
+		struct addrconf_sysctl_table *t = p->sysctl;
+		p->sysctl = NULL;
+		unregister_sysctl_table(t->sysctl_header);
+		kfree(t);
+	}
+}
+
+
+#endif
 
 /*
  *	Init / cleanup code
@@ -1628,6 +1805,11 @@
 #ifdef CONFIG_RTNETLINK
 	rtnetlink_links[AF_INET6] = inet6_rtnetlink_table;
 #endif
+#ifdef CONFIG_SYSCTL
+	addrconf_sysctl.sysctl_header =
+		register_sysctl_table(addrconf_sysctl.addrconf_root_dir, 0);
+	addrconf_sysctl_register(NULL, &ipv6_devconf_dflt);
+#endif
 }
 
 #ifdef MODULE
@@ -1640,7 +1822,10 @@
 #ifdef CONFIG_RTNETLINK
 	rtnetlink_links[AF_INET6] = NULL;
 #endif
-	start_bh_atomic();
+#ifdef CONFIG_SYSCTL
+	addrconf_sysctl_unregister(&ipv6_devconf_dflt);
+	addrconf_sysctl_unregister(&ipv6_devconf);
+#endif
 
 	del_timer(&addr_chk_timer);
 
@@ -1656,6 +1841,7 @@
 		}
 	}
 
+	start_bh_atomic();
 	/*
 	 *	clean addr_list
 	 */
diff -u --recursive --new-file v2.1.88/linux/net/ipv6/af_inet6.c linux/net/ipv6/af_inet6.c
--- v2.1.88/linux/net/ipv6/af_inet6.c	Mon Jan 12 15:28:28 1998
+++ linux/net/ipv6/af_inet6.c	Sun Mar  1 14:40:41 1998
@@ -116,8 +116,8 @@
 	sk->timer.data		= (unsigned long)sk;
 	sk->timer.function	= &net_timer;
 
-	sk->net_pinfo.af_inet6.hop_limit  = ipv6_config.hop_limit;
-	sk->net_pinfo.af_inet6.mcast_hops = IPV6_DEFAULT_MCASTHOPS;
+	sk->net_pinfo.af_inet6.hop_limit  = -1;
+	sk->net_pinfo.af_inet6.mcast_hops = -1;
 	sk->net_pinfo.af_inet6.mc_loop	  = 1;
 
 	/* Init the ipv4 part of the socket since we can have sockets
diff -u --recursive --new-file v2.1.88/linux/net/ipv6/ip6_fib.c linux/net/ipv6/ip6_fib.c
--- v2.1.88/linux/net/ipv6/ip6_fib.c	Mon Jan 12 15:28:28 1998
+++ linux/net/ipv6/ip6_fib.c	Sun Mar  1 14:40:41 1998
@@ -45,8 +45,6 @@
 
 static __u32	rt_sernum	= 0;
 
-static void fib6_run_gc(unsigned long);
-
 static struct timer_list ip6_fib_timer = {
 	NULL, NULL,
 	0,
@@ -421,7 +419,7 @@
 			    (ipv6_addr_cmp(&iter->rt6i_gateway,
 					   &rt->rt6i_gateway) == 0)) {
 				if (rt->rt6i_expires == 0 ||
-				    rt->rt6i_expires - iter->rt6i_expires > 0)
+				    (long)(rt->rt6i_expires - iter->rt6i_expires) > 0)
 					rt->rt6i_expires = iter->rt6i_expires;
 				return -EEXIST;
 			}
@@ -457,7 +455,8 @@
 {
 	if ((ip6_fib_timer.expires == 0) &&
 	    (rt->rt6i_flags & (RTF_ADDRCONF | RTF_CACHE))) {
-		ip6_fib_timer.expires = jiffies + ipv6_config.rt_gc_period;
+		del_timer(&ip6_fib_timer);
+		ip6_fib_timer.expires = jiffies + ip6_rt_gc_interval;
 		add_timer(&ip6_fib_timer);
 	}
 }
@@ -904,7 +903,7 @@
 
 	for (rt = fn->leaf; rt;) {
 		if ((rt->rt6i_flags & RTF_CACHE) && atomic_read(&rt->rt6i_use) == 0) {
-			if (now - rt->rt6i_tstamp > timeout) {
+			if ((long)(now - rt->rt6i_tstamp) >= timeout) {
 				struct rt6_info *old;
 
 				old = rt;
@@ -932,7 +931,7 @@
 		 *	Seems, radix tree walking is absolutely broken,
 		 *	but we will try in any case --ANK
 		 */
-		if (rt->rt6i_expires && now - rt->rt6i_expires < 0) {
+		if (rt->rt6i_expires && (long)(now - rt->rt6i_expires) < 0) {
 			struct rt6_info *old;
 
 			old = rt;
@@ -1042,20 +1041,25 @@
 	}
 }
 
-static void fib6_run_gc(unsigned long dummy)
+void fib6_run_gc(unsigned long dummy)
 {
 	struct fib6_gc_args arg = {
-		ipv6_config.rt_cache_timeout,
+		ip6_rt_gc_timeout,
 		0
 	};
 
+	del_timer(&ip6_fib_timer);
+
+	if (dummy)
+		arg.timeout = dummy;
+
 	if (fib6_walk_count == 0)
 		fib6_walk_tree(&ip6_routing_table, fib6_garbage_collect, &arg, 0);
 	else
 		arg.more = 1;
 
 	if (arg.more) {
-		ip6_fib_timer.expires = jiffies + ipv6_config.rt_gc_period;
+		ip6_fib_timer.expires = jiffies + ip6_rt_gc_interval;
 		add_timer(&ip6_fib_timer);
 	} else {
 		ip6_fib_timer.expires = 0;
diff -u --recursive --new-file v2.1.88/linux/net/ipv6/ip6_output.c linux/net/ipv6/ip6_output.c
--- v2.1.88/linux/net/ipv6/ip6_output.c	Thu Feb 12 20:56:14 1998
+++ linux/net/ipv6/ip6_output.c	Sun Mar  1 14:40:41 1998
@@ -44,6 +44,22 @@
 	skb->protocol = __constant_htons(ETH_P_IPV6);
 	skb->dev = dev;
 
+	if (ipv6_addr_is_multicast(&skb->nh.ipv6h->daddr)) {
+		if (!(dev->flags&IFF_LOOPBACK) &&
+		    (skb->sk == NULL || skb->sk->net_pinfo.af_inet6.mc_loop) &&
+		    ipv6_chk_mcast_addr(dev, &skb->nh.ipv6h->daddr)) {
+			/* Do not check for IFF_ALLMULTI; multicast routing
+			   is not supported in any case.
+			 */
+			dev_loopback_xmit(skb);
+
+			if (skb->nh.ipv6h->hop_limit == 0) {
+				kfree_skb(skb);
+				return 0;
+			}
+		}
+	}
+
 	if (hh) {
 #ifdef __alpha__
 		/* Alpha has disguisting memcpy. Help it. */
@@ -120,8 +136,11 @@
 
 	hdr->payload_len = htons(seg_len - sizeof(struct ipv6hdr));
 	hdr->nexthdr = fl->proto;
-	hdr->hop_limit = np ? np->hop_limit : ipv6_config.hop_limit;
-	
+	if (np == NULL || np->hop_limit < 0)
+		hdr->hop_limit = ((struct rt6_info*)dst)->rt6i_hoplimit;
+	else
+		hdr->hop_limit = np->hop_limit;
+
 	ipv6_addr_copy(&hdr->saddr, fl->nl_u.ip6_u.saddr);
 	ipv6_addr_copy(&hdr->daddr, fl->nl_u.ip6_u.daddr);
 
@@ -424,8 +443,14 @@
 	
 	pktlength = length;
 
-	if (hlimit < 0)
-		hlimit = np->hop_limit;
+	if (hlimit < 0) {
+		if (ipv6_addr_is_multicast(fl->nl_u.ip6_u.daddr))
+			hlimit = np->mcast_hops;
+		else
+			hlimit = np->hop_limit;
+		if (hlimit < 0)
+			hlimit = ((struct rt6_info*)dst)->rt6i_hoplimit;
+	}
 
 	if (!sk->ip_hdrincl) {
 		pktlength += sizeof(struct ipv6hdr);
@@ -466,7 +491,7 @@
 
 		hdr = (struct ipv6hdr *) skb->tail;
 		skb->nh.ipv6h = hdr;
-		
+
 		if (!sk->ip_hdrincl) {
 			ip6_bld_1(sk, skb, fl, hlimit, pktlength);
 #if 0
@@ -528,7 +553,7 @@
 	struct ipv6hdr *hdr = skb->nh.ipv6h;
 	int size;
 	
-	if (ipv6_config.forwarding == 0) {
+	if (ipv6_devconf.forwarding == 0) {
 		kfree_skb(skb);
 		return -EINVAL;
 	}
diff -u --recursive --new-file v2.1.88/linux/net/ipv6/ipv6_sockglue.c linux/net/ipv6/ipv6_sockglue.c
--- v2.1.88/linux/net/ipv6/ipv6_sockglue.c	Mon Jan 12 15:28:28 1998
+++ linux/net/ipv6/ipv6_sockglue.c	Sun Mar  1 14:40:41 1998
@@ -132,7 +132,7 @@
 		break;
 
 	case IPV6_UNICAST_HOPS:
-		if (val > 255)
+		if (val > 255 || val < -1)
 			retv = -EINVAL;
 		else {
 			np->hop_limit = val;
@@ -141,16 +141,18 @@
 		break;
 
 	case IPV6_MULTICAST_HOPS:
-		if (val > 255)
+		if (val > 255 || val < -1)
 			retv = -EINVAL;
 		else {
 			np->mcast_hops = val;
 			retv = 0;
 		}
 		break;
+		break;
 
 	case IPV6_MULTICAST_LOOP:
-		np->mc_loop = val;
+		np->mc_loop = (val != 0);
+		retv = 0;
 		break;
 
 	case IPV6_MULTICAST_IF:
diff -u --recursive --new-file v2.1.88/linux/net/ipv6/ndisc.c linux/net/ipv6/ndisc.c
--- v2.1.88/linux/net/ipv6/ndisc.c	Thu Feb 12 20:56:14 1998
+++ linux/net/ipv6/ndisc.c	Sun Mar  1 14:40:41 1998
@@ -131,7 +131,8 @@
 	pndisc_constructor,
 	pndisc_destructor,
 	pndisc_redo,
-        { NULL, NULL, 30*HZ, 1*HZ, 60*HZ, 30*HZ, 5*HZ, 3, 3, 0, 3, 1*HZ, (8*HZ)/10, 0, 64 },
+        { NULL, NULL, &nd_tbl, 0, NULL, NULL,
+		  30*HZ, 1*HZ, 60*HZ, 30*HZ, 5*HZ, 3, 3, 0, 3, 1*HZ, (8*HZ)/10, 0, 64 },
 	30*HZ, 128, 512, 1024,
 };
 
@@ -558,7 +559,9 @@
 		ND_PRINTK1("RA: can't find in6 device\n");
 		return;
 	}
-	
+	if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_ra)
+		return;
+
 	if (in6_dev->if_flags & IF_RS_SENT) {
 		/*
 		 *	flag that an RA was received after an RS was sent
@@ -602,7 +605,7 @@
 		rt->rt6i_expires = jiffies + (HZ * lifetime);
 
 	if (ra_msg->icmph.icmp6_hop_limit)
-		ipv6_config.hop_limit = ra_msg->icmph.icmp6_hop_limit;
+		in6_dev->cnf.hop_limit = ra_msg->icmph.icmp6_hop_limit;
 
 	/*
 	 *	Update Reachable Time and Retrans Timer
@@ -651,32 +654,29 @@
                         break;
 
                 case ND_OPT_MTU:
-			if (rt) {
+			{
 				int mtu;
-				struct device *dev;
 				
 				mtu = htonl(*(__u32 *)(opt+4));
-				dev = rt->rt6i_dev;
-
-				if (dev == NULL)
-					break;
 
-				if (mtu < 576) {
+				if (mtu < 576 || mtu > skb->dev->mtu) {
 					ND_PRINTK0("NDISC: router "
 						   "announcement with mtu = %d\n",
 						   mtu);
 					break;
 				}
 
-#if 0
-				/* Bad idea. Sorry, this thing is not
-				   so easy to implement		--ANK
-				 */
-				if (dev->change_mtu)
-					dev->change_mtu(dev, mtu);
-				else
-					dev->mtu = mtu;
-#endif
+				if (in6_dev->cnf.mtu6 != mtu) {
+					in6_dev->cnf.mtu6 = mtu;
+
+					if (rt)
+						rt->u.dst.pmtu = mtu;
+
+					/* BUGGG... Scan routing tables and
+					   adjust mtu on routes going
+					   via this device
+					 */
+				}
 			}
                         break;
 
@@ -692,25 +692,9 @@
         }
 }
 
-void ndisc_forwarding_on(void)
-{
-
-	/*
-	 *	Forwarding was turned on.
-	 */
-
-	rt6_purge_dflt_routers(0);
-}
-
-void ndisc_forwarding_off(void)
-{
-	/*
-	 *	Forwarding was turned off.
-	 */
-}
-
 static void ndisc_redirect_rcv(struct sk_buff *skb)
 {
+	struct inet6_dev *in6_dev;
 	struct icmp6hdr *icmph;
 	struct in6_addr *dest;
 	struct in6_addr *target;	/* new first hop to destination */
@@ -753,6 +737,10 @@
 		return;
 	}
 
+	in6_dev = ipv6_get_idev(skb->dev);
+	if (!in6_dev || in6_dev->cnf.forwarding || !in6_dev->cnf.accept_redirects)
+		return;
+
 	/* passed validation tests
 
 	   NOTE We should not install redirect if sender did not supply
@@ -943,7 +931,7 @@
 
 				ipv6_addr_all_nodes(&maddr);
 				ndisc_send_na(dev, NULL, &maddr, &ifp->addr, 
-					      ifp->idev->router, 0, 1, 1);
+					      ifp->idev->cnf.forwarding, 0, 1, 1);
 				return 0;
 			}
 
@@ -964,7 +952,7 @@
 
 				if (neigh) {
 					ndisc_send_na(dev, neigh, saddr, &ifp->addr, 
-						      ifp->idev->router, 1, inc, inc);
+						      ifp->idev->cnf.forwarding, 1, inc, inc);
 					neigh_release(neigh);
 				}
 			}
@@ -972,7 +960,7 @@
 			struct inet6_dev *in6_dev = ipv6_get_idev(dev);
 			int addr_type = ipv6_addr_type(saddr);
 
-			if (in6_dev && in6_dev->router &&
+			if (in6_dev && in6_dev->cnf.forwarding &&
 			    (addr_type & IPV6_ADDR_UNICAST) &&
 			    pneigh_lookup(&nd_tbl, &msg->target, dev, 0)) {
 				int inc = ipv6_addr_type(daddr)&IPV6_ADDR_MULTICAST;
@@ -1053,21 +1041,15 @@
 			neigh_release(neigh);
 		}
 		break;
-	};
 
-	if (ipv6_config.forwarding == 0) {
-		switch (msg->icmph.icmp6_type) {
-		case NDISC_ROUTER_ADVERTISEMENT:
-			if (ipv6_config.accept_ra)
-				ndisc_router_discovery(skb);
-			break;
+	case NDISC_ROUTER_ADVERTISEMENT:
+		ndisc_router_discovery(skb);
+		break;
 
-		case NDISC_REDIRECT:
-			if (ipv6_config.accept_redirects)
-				ndisc_redirect_rcv(skb);
-			break;
-		};
-	}
+	case NDISC_REDIRECT:
+		ndisc_redirect_rcv(skb);
+		break;
+	};
 
 	return 0;
 }
@@ -1177,6 +1159,8 @@
 	sk->allocation = GFP_ATOMIC;
 	sk->net_pinfo.af_inet6.hop_limit = 255;
 	sk->net_pinfo.af_inet6.priority  = 15;
+	/* Do not loopback ndisc messages */
+	sk->net_pinfo.af_inet6.mc_loop = 0;
 	sk->num = 256;
 
         /*
@@ -1191,7 +1175,7 @@
 #endif
 #endif
 #ifdef CONFIG_SYSCTL
-	nd_tbl.parms.sysctl_table = neigh_sysctl_register(NULL, &nd_tbl.parms, NET_IPV6, NET_IPV6_NEIGH, "ipv6");
+	neigh_sysctl_register(NULL, &nd_tbl.parms, NET_IPV6, NET_IPV6_NEIGH, "ipv6");
 #endif
 }
 
diff -u --recursive --new-file v2.1.88/linux/net/ipv6/raw.c linux/net/ipv6/raw.c
--- v2.1.88/linux/net/ipv6/raw.c	Thu Feb 12 20:56:14 1998
+++ linux/net/ipv6/raw.c	Fri Mar  6 20:53:35 1998
@@ -99,7 +99,7 @@
 	SOCKHASH_UNLOCK();
 }
 
-static int __inline__ inet6_mc_check(struct sock *sk, struct in6_addr *addr)
+static __inline__ int inet6_mc_check(struct sock *sk, struct in6_addr *addr)
 {
 	struct ipv6_mc_socklist *mc;
 		
@@ -236,13 +236,11 @@
  */
 
 int rawv6_recvmsg(struct sock *sk, struct msghdr *msg, int len,
-		  int noblock, int flags,int *addr_len)
+		  int noblock, int flags, int *addr_len)
 {
-	struct sockaddr_in6 *sin6=(struct sockaddr_in6 *)msg->msg_name;
+	struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)msg->msg_name;
 	struct sk_buff *skb;
-	int copied=0;
-	int err;
-
+	int copied, err;
 
 	if (flags & MSG_OOB)
 		return -EOPNOTSUPP;
@@ -253,32 +251,32 @@
 	if (addr_len) 
 		*addr_len=sizeof(*sin6);
 
-	skb=skb_recv_datagram(sk, flags, noblock, &err);
-	if(skb==NULL)
- 		return err;
+	skb = skb_recv_datagram(sk, flags, noblock, &err);
+	if (!skb)
+		goto out;
 	
 	copied = min(len, skb->tail - skb->h.raw);
 	
 	err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
 	sk->stamp=skb->stamp;
-
 	if (err)
-		return err;
+		goto out_free;
 
 	/* Copy the address. */
 	if (sin6) {
 		sin6->sin6_family = AF_INET6;
 		memcpy(&sin6->sin6_addr, &skb->nh.ipv6h->saddr, 
 		       sizeof(struct in6_addr));
-
-		*addr_len = sizeof(struct sockaddr_in6);
 	}
 
 	if (msg->msg_controllen)
 		datagram_recv_ctl(sk, msg, skb);
+	err = copied;
 
+out_free:
 	skb_free_datagram(sk, skb);
-	return (copied);
+out:
+	return err;
 }
 
 /*
diff -u --recursive --new-file v2.1.88/linux/net/ipv6/route.c linux/net/ipv6/route.c
--- v2.1.88/linux/net/ipv6/route.c	Mon Feb 23 18:12:14 1998
+++ linux/net/ipv6/route.c	Sun Mar  1 14:40:41 1998
@@ -41,6 +41,10 @@
 
 #include <asm/uaccess.h>
 
+#ifdef CONFIG_SYSCTL
+#include <linux/sysctl.h>
+#endif
+
 #undef CONFIG_RT6_POLICY
 
 /* Set to 3 to get tracing. */
@@ -52,30 +56,41 @@
 #define RDBG(x)
 #endif
 
+int ip6_rt_max_size = 4096;
+int ip6_rt_gc_min_interval = 5*HZ;
+int ip6_rt_gc_timeout = 60*HZ;
+int ip6_rt_gc_interval = 30*HZ;
+
 static struct rt6_info * ip6_rt_copy(struct rt6_info *ort);
 static struct dst_entry	*ip6_dst_check(struct dst_entry *dst, u32 cookie);
 static struct dst_entry	*ip6_dst_reroute(struct dst_entry *dst,
 					 struct sk_buff *skb);
 static struct dst_entry *ip6_negative_advice(struct dst_entry *);
+static int		 ip6_dst_gc(void);
 
 static int		ip6_pkt_discard(struct sk_buff *skb);
+static void		ip6_link_failure(struct sk_buff *skb);
 
 struct dst_ops ip6_dst_ops = {
 	AF_INET6,
 	__constant_htons(ETH_P_IPV6),
+	1024,
+
+        ip6_dst_gc,
 	ip6_dst_check,
 	ip6_dst_reroute,
 	NULL,
-	ip6_negative_advice
+	ip6_negative_advice,
+	ip6_link_failure,
 };
 
 struct rt6_info ip6_null_entry = {
 	{{NULL, ATOMIC_INIT(0), ATOMIC_INIT(0), NULL,
-	  -1, 0, 0, 0, 0, 0, 0, 0,
+	  -1, 0, 0, 0, 0, 0, 0, 0, 0,
 	  -ENETUNREACH, NULL, NULL,
 	  ip6_pkt_discard, ip6_pkt_discard, &ip6_dst_ops}},
 	NULL, {{{0}}}, 256, RTF_REJECT|RTF_NONEXTHOP, ~0U,
-	0, {NULL}, {{{{0}}}, 128}, {{{{0}}}, 128}
+	0, 255, {NULL}, {{{{0}}}, 128}, {{{{0}}}, 128}
 };
 
 struct fib6_node ip6_routing_table = {
@@ -193,6 +208,7 @@
 						    struct device *dev,
 						    int strict)
 {
+	struct rt6_info *local = NULL;
 	struct rt6_info *sprt;
 
 	RDBG(("rt6_device_match: (%p,%p,%d) ", rt, dev, strict));
@@ -202,8 +218,13 @@
 				RDBG(("match --> %p\n", sprt));
 				return sprt;
 			}
+			if (sprt->rt6i_dev && (sprt->rt6i_dev->flags&IFF_LOOPBACK))
+				local = sprt;
 		}
 
+		if (local)
+			return local;
+
 		if (strict) {
 			RDBG(("nomatch & STRICT --> ip6_null_entry\n"));
 			return &ip6_null_entry;
@@ -383,6 +404,8 @@
 	struct dst_entry *dst;
 
 	RDBG(("ip6_route_input(%p) from %p\n", skb, __builtin_return_address(0)));
+	if ((dst = skb->dst) != NULL)
+		goto looped_back;
 	rt6_lock();
 	fn = fib6_lookup(&ip6_routing_table, &skb->nh.ipv6h->daddr,
 			 &skb->nh.ipv6h->saddr);
@@ -428,6 +451,7 @@
 	rt6_unlock();
 
 	skb->dst = dst;
+looped_back:
 	dst->input(skb);
 }
 
@@ -595,6 +619,32 @@
 	return NULL;
 }
 
+static void ip6_link_failure(struct sk_buff *skb)
+{
+	icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0, skb->dev);
+}
+
+static int ip6_dst_gc()
+{
+	static unsigned expire = 30*HZ;
+	static unsigned long last_gc;
+	unsigned long now = jiffies;
+
+	start_bh_atomic();
+	if ((long)(now - last_gc) < ip6_rt_gc_min_interval)
+		goto out;
+
+	expire++;
+	fib6_run_gc(expire);
+	last_gc = now;
+	if (atomic_read(&ip6_dst_ops.entries) < ip6_dst_ops.gc_thresh)
+		expire = ip6_rt_gc_timeout;
+
+out:
+	expire >>= 1;
+	end_bh_atomic();
+	return (atomic_read(&ip6_dst_ops.entries) > ip6_rt_max_size);
+}
 
 /* Clean host part of a prefix. Not necessary in radix tree,
    but results in cleaner routing tables.
@@ -613,6 +663,28 @@
 		pfx->s6_addr[plen>>3] &= (0xFF<<(8-b));
 }
 
+static int ipv6_get_mtu(struct device *dev)
+{
+	struct inet6_dev *idev;
+
+	idev = ipv6_get_idev(dev);
+	if (idev)
+		return idev->cnf.mtu6;
+	else
+		return 576;
+}
+
+static int ipv6_get_hoplimit(struct device *dev)
+{
+	struct inet6_dev *idev;
+
+	idev = ipv6_get_idev(dev);
+	if (idev)
+		return idev->cnf.hop_limit;
+	else
+		return ipv6_devconf.hop_limit;
+}
+
 /*
  *
  */
@@ -726,7 +798,11 @@
 	rt->rt6i_metric = rtmsg->rtmsg_metric;
 
 	rt->rt6i_dev = dev;
-	rt->u.dst.pmtu = dev->mtu;
+	rt->u.dst.pmtu = ipv6_get_mtu(dev);
+	if (ipv6_addr_is_multicast(&rt->rt6i_dst.addr))
+		rt->rt6i_hoplimit = IPV6_DEFAULT_MCASTHOPS;
+	else
+		rt->rt6i_hoplimit = ipv6_get_hoplimit(dev);
 	rt->rt6i_flags = rtmsg->rtmsg_flags;
 
 	RDBG(("rt6ins(%p) ", rt));
@@ -803,21 +879,12 @@
 
 int ip6_route_del(struct in6_rtmsg *rtmsg)
 {
+	struct fib6_node *fn;
 	struct rt6_info *rt;
-	struct device *dev=NULL;
 
-	/*
-	 *	Find device
-	 */
-	if(rtmsg->rtmsg_ifindex) {
-		dev=dev_get_by_index(rtmsg->rtmsg_ifindex);
-		if (dev == NULL)
-			return -ENODEV;
-	}
-	/*
-	 *	Find route
-	 */
-	rt=rt6_lookup(&rtmsg->rtmsg_dst, &rtmsg->rtmsg_src, dev, RTF_LINKRT);
+	rt6_lock();
+	fn = fib6_lookup(&ip6_routing_table, &rtmsg->rtmsg_dst, &rtmsg->rtmsg_src);
+	rt = fn->leaf;
 
 	/*
 	 *	Blow it away
@@ -836,16 +903,31 @@
 				if (fn->fn_flags & RTN_ROOT)
 					break;
 				if (fn->fn_flags & RTN_RTINFO) {
-					rt = rt6_device_match(fn->leaf, dev, RTF_LINKRT);
+					rt = fn->leaf;
 					goto restart;
 				}
 			}
 		}
+
 		if (rt->rt6i_dst.plen == rtmsg->rtmsg_dst_len) {
-			ip6_del_rt(rt);
-			return 0;
+			for ( ; rt; rt = rt->u.next) {
+				if (rtmsg->rtmsg_ifindex &&
+				    (rt->rt6i_dev == NULL ||
+				     rt->rt6i_dev->ifindex != rtmsg->rtmsg_ifindex))
+					continue;
+				if (rtmsg->rtmsg_flags&RTF_GATEWAY &&
+				     ipv6_addr_cmp(&rtmsg->rtmsg_gateway, &rt->rt6i_gateway))
+					continue;
+				if (rtmsg->rtmsg_metric &&
+				    rtmsg->rtmsg_metric != rt->rt6i_metric)
+					continue;
+				ip6_del_rt(rt);
+				rt6_unlock();
+				return 0;
+			}
 		}
 	}
+	rt6_unlock();
 
 	return -ESRCH;
 }
@@ -1073,7 +1155,9 @@
 	ipv6_addr_copy(&nrt->rt6i_gateway, target);
 	nrt->rt6i_nexthop = ndisc_get_neigh(nrt->rt6i_dev, target);
 	nrt->rt6i_dev = dev;
-	nrt->u.dst.pmtu = dev->mtu;
+	nrt->u.dst.pmtu = ipv6_get_mtu(dev);
+	if (!ipv6_addr_is_multicast(&nrt->rt6i_dst.addr))
+		nrt->rt6i_hoplimit = ipv6_get_hoplimit(dev);
 
 	rt6_lock();
 	rt6_ins(nrt);
@@ -1168,8 +1252,9 @@
 		rt->u.dst.output = ort->u.dst.output;
 
 		rt->u.dst.pmtu = ort->u.dst.pmtu;
+		rt->rt6i_hoplimit = ort->rt6i_hoplimit;
 		rt->rt6i_dev = ort->rt6i_dev;
-		
+
 		ipv6_addr_copy(&rt->rt6i_gateway, &ort->rt6i_gateway);
 		rt->rt6i_keylen = ort->rt6i_keylen;
 		rt->rt6i_flags = ort->rt6i_flags;
@@ -1370,7 +1455,8 @@
 	rt->u.dst.input = ip6_input;
 	rt->u.dst.output = ip6_output;
 	rt->rt6i_dev = dev_get("lo");
-	rt->u.dst.pmtu = rt->rt6i_dev->mtu;
+	rt->u.dst.pmtu = ipv6_get_mtu(rt->rt6i_dev);
+	rt->rt6i_hoplimit = ipv6_get_hoplimit(rt->rt6i_dev);
 	rt->u.dst.obsolete = -1;
 
 	rt->rt6i_flags = RTF_UP | RTF_NONEXTHOP;
@@ -1577,46 +1663,63 @@
 
 #ifdef CONFIG_RTNETLINK
 
-static void inet6_rtm_to_rtmsg(struct rtmsg *r, struct kern_rta *rta,
-			       struct in6_rtmsg *rtmsg)
+static int inet6_rtm_to_rtmsg(struct rtmsg *r, struct rtattr **rta,
+			      struct in6_rtmsg *rtmsg)
 {
 	memset(rtmsg, 0, sizeof(*rtmsg));
+
 	rtmsg->rtmsg_dst_len = r->rtm_dst_len;
 	rtmsg->rtmsg_src_len = r->rtm_src_len;
 	rtmsg->rtmsg_flags = RTF_UP;
 	rtmsg->rtmsg_metric = IP6_RT_PRIO_USER;
-	if (rta->rta_gw) {
-		memcpy(&rtmsg->rtmsg_gateway, rta->rta_gw, 16);
+
+	if (rta[RTA_GATEWAY-1]) {
+		if (rta[RTA_GATEWAY-1]->rta_len != RTA_LENGTH(16))
+			return -EINVAL;
+		memcpy(&rtmsg->rtmsg_gateway, RTA_DATA(rta[RTA_GATEWAY-1]), 16);
 		rtmsg->rtmsg_flags |= RTF_GATEWAY;
 	}
-	if (rta->rta_dst)
-		memcpy(&rtmsg->rtmsg_dst, rta->rta_dst, 16);
-	if (rta->rta_src)
-		memcpy(&rtmsg->rtmsg_src, rta->rta_src, 16);
-	if (rta->rta_oif)
-		memcpy(&rtmsg->rtmsg_ifindex, rta->rta_oif, 4);
-	if (rta->rta_priority)
-		memcpy(&rtmsg->rtmsg_metric, rta->rta_priority, 4);
+	if (rta[RTA_DST-1]) {
+		if (RTA_PAYLOAD(rta[RTA_DST-1]) < ((r->rtm_dst_len+7)>>3))
+			return -EINVAL;
+		memcpy(&rtmsg->rtmsg_dst, RTA_DATA(rta[RTA_DST-1]), ((r->rtm_dst_len+7)>>3));
+	}
+	if (rta[RTA_SRC-1]) {
+		if (RTA_PAYLOAD(rta[RTA_SRC-1]) < ((r->rtm_src_len+7)>>3))
+			return -EINVAL;
+		memcpy(&rtmsg->rtmsg_src, RTA_DATA(rta[RTA_SRC-1]), ((r->rtm_src_len+7)>>3));
+	}
+	if (rta[RTA_OIF-1]) {
+		if (rta[RTA_OIF-1]->rta_len != RTA_LENGTH(sizeof(int)))
+			return -EINVAL;
+		memcpy(&rtmsg->rtmsg_ifindex, RTA_DATA(rta[RTA_OIF-1]), sizeof(int));
+	}
+	if (rta[RTA_PRIORITY-1]) {
+		if (rta[RTA_PRIORITY-1]->rta_len != RTA_LENGTH(4))
+			return -EINVAL;
+		memcpy(&rtmsg->rtmsg_metric, RTA_DATA(rta[RTA_PRIORITY-1]), 4);
+	}
+	return 0;
 }
 
 int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
 {
-	struct kern_rta *rta = arg;
 	struct rtmsg *r = NLMSG_DATA(nlh);
 	struct in6_rtmsg rtmsg;
 
-	inet6_rtm_to_rtmsg(r, rta, &rtmsg);
+	if (inet6_rtm_to_rtmsg(r, arg, &rtmsg))
+		return -EINVAL;
 	return ip6_route_del(&rtmsg);
 }
 
 int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
 {
-	struct kern_rta *rta = arg;
 	struct rtmsg *r = NLMSG_DATA(nlh);
 	struct in6_rtmsg rtmsg;
 	int err = 0;
 
-	inet6_rtm_to_rtmsg(r, rta, &rtmsg);
+	if (inet6_rtm_to_rtmsg(r, arg, &rtmsg))
+		return -EINVAL;
 	ip6_route_add(&rtmsg, &err);
 	return err;
 }
@@ -1637,7 +1740,11 @@
 	struct rtmsg *rtm;
 	struct nlmsghdr  *nlh;
 	unsigned char	 *b = skb->tail;
+#ifdef CONFIG_RTNL_OLD_IFINFO
 	unsigned char 	 *o;
+#else
+	struct rtattr *mx;
+#endif
 	struct rta_cacheinfo ci;
 
 	nlh = NLMSG_PUT(skb, pid, seq, type, sizeof(*rtm));
@@ -1650,7 +1757,9 @@
 	rtm->rtm_type = RTN_UNICAST;
 	rtm->rtm_flags = 0;
 	rtm->rtm_scope = RT_SCOPE_UNIVERSE;
+#ifdef CONFIG_RTNL_OLD_IFINFO
 	rtm->rtm_nhs = 0;
+#endif
 	rtm->rtm_protocol = RTPROT_BOOT;
 	if (rt->rt6i_flags&RTF_DYNAMIC)
 		rtm->rtm_protocol = RTPROT_REDIRECT;
@@ -1662,17 +1771,31 @@
 	if (rt->rt6i_flags&RTF_CACHE)
 		rtm->rtm_flags |= RTM_F_CLONED;
 
+#ifdef CONFIG_RTNL_OLD_IFINFO
 	o = skb->tail;
+#endif
 	if (rtm->rtm_dst_len)
 		RTA_PUT(skb, RTA_DST, 16, &rt->rt6i_dst.addr);
 	if (rtm->rtm_src_len)
 		RTA_PUT(skb, RTA_SRC, 16, &rt->rt6i_src.addr);
+#ifdef CONFIG_RTNL_OLD_IFINFO
 	if (rt->u.dst.pmtu)
 		RTA_PUT(skb, RTA_MTU, sizeof(unsigned), &rt->u.dst.pmtu);
 	if (rt->u.dst.window)
 		RTA_PUT(skb, RTA_WINDOW, sizeof(unsigned), &rt->u.dst.window);
 	if (rt->u.dst.rtt)
 		RTA_PUT(skb, RTA_RTT, sizeof(unsigned), &rt->u.dst.rtt);
+#else
+	mx = (struct rtattr*)skb->tail;
+	RTA_PUT(skb, RTA_METRICS, 0, NULL);
+	if (rt->u.dst.pmtu)
+		RTA_PUT(skb, RTAX_MTU, sizeof(unsigned), &rt->u.dst.pmtu);
+	if (rt->u.dst.window)
+		RTA_PUT(skb, RTAX_WINDOW, sizeof(unsigned), &rt->u.dst.window);
+	if (rt->u.dst.rtt)
+		RTA_PUT(skb, RTAX_RTT, sizeof(unsigned), &rt->u.dst.rtt);
+	mx->rta_len = skb->tail - (u8*)mx;
+#endif
 	if (rt->u.dst.neighbour)
 		RTA_PUT(skb, RTA_GATEWAY, 16, &rt->u.dst.neighbour->primary_key);
 	if (rt->u.dst.dev)
@@ -1687,13 +1810,15 @@
 	ci.rta_clntref = atomic_read(&rt->u.dst.use);
 	ci.rta_error = rt->u.dst.error;
 	RTA_PUT(skb, RTA_CACHEINFO, sizeof(ci), &ci);
+#ifdef CONFIG_RTNL_OLD_IFINFO
 	rtm->rtm_optlen = skb->tail - o;
+#endif
 	nlh->nlmsg_len = skb->tail - b;
 	return skb->len;
 
 nlmsg_failure:
 rtattr_failure:
-	skb_put(skb, b - skb->tail);
+	skb_trim(skb, b - skb->data);
 	return -1;
 }
 
@@ -1968,6 +2093,51 @@
 	rt6_proc_stats
 };
 #endif	/* CONFIG_PROC_FS */
+
+#ifdef CONFIG_SYSCTL
+
+static int flush_delay;
+
+static
+int ipv6_sysctl_rtcache_flush(ctl_table *ctl, int write, struct file * filp,
+			      void *buffer, size_t *lenp)
+{
+	if (write) {
+		proc_dointvec(ctl, write, filp, buffer, lenp);
+		if (flush_delay < 0)
+			flush_delay = 0;
+		start_bh_atomic();
+		fib6_run_gc((unsigned long)flush_delay);
+		end_bh_atomic();
+		return 0;
+	} else
+		return -EINVAL;
+}
+
+ctl_table ipv6_route_table[] = {
+        {NET_IPV6_ROUTE_FLUSH, "flush",
+         &flush_delay, sizeof(int), 0644, NULL,
+         &ipv6_sysctl_rtcache_flush},
+	{NET_IPV6_ROUTE_GC_THRESH, "gc_thresh",
+         &ip6_dst_ops.gc_thresh, sizeof(int), 0644, NULL,
+         &proc_dointvec},
+	{NET_IPV6_ROUTE_MAX_SIZE, "max_size",
+         &ip6_rt_max_size, sizeof(int), 0644, NULL,
+         &proc_dointvec},
+	{NET_IPV6_ROUTE_GC_MIN_INTERVAL, "gc_min_interval",
+         &ip6_rt_gc_min_interval, sizeof(int), 0644, NULL,
+         &proc_dointvec_jiffies},
+	{NET_IPV6_ROUTE_GC_TIMEOUT, "gc_timeout",
+         &ip6_rt_gc_timeout, sizeof(int), 0644, NULL,
+         &proc_dointvec_jiffies},
+	{NET_IPV6_ROUTE_GC_INTERVAL, "gc_interval",
+         &ip6_rt_gc_interval, sizeof(int), 0644, NULL,
+         &proc_dointvec_jiffies},
+	 {0}
+};
+
+#endif
+
 
 __initfunc(void ip6_route_init(void))
 {
diff -u --recursive --new-file v2.1.88/linux/net/ipv6/sit.c linux/net/ipv6/sit.c
--- v2.1.88/linux/net/ipv6/sit.c	Mon Feb 23 18:12:14 1998
+++ linux/net/ipv6/sit.c	Sun Mar  1 14:40:42 1998
@@ -454,7 +454,7 @@
 	if (tunnel->err_count > 0) {
 		if (jiffies - tunnel->err_time < IPTUNNEL_ERR_TIMEO) {
 			tunnel->err_count--;
-			icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0, skb->dev);
+			dst_link_failure(skb);
 		} else
 			tunnel->err_count = 0;
 	}
@@ -516,7 +516,7 @@
 	return 0;
 
 tx_error_icmp:
-	icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0, dev);
+	dst_link_failure(skb);
 tx_error:
 	stats->tx_errors++;
 	dev_kfree_skb(skb);
diff -u --recursive --new-file v2.1.88/linux/net/ipv6/sysctl_net_ipv6.c linux/net/ipv6/sysctl_net_ipv6.c
--- v2.1.88/linux/net/ipv6/sysctl_net_ipv6.c	Mon Jan 12 15:28:28 1998
+++ linux/net/ipv6/sysctl_net_ipv6.c	Sun Mar  1 14:40:42 1998
@@ -11,83 +11,12 @@
 #include <net/ipv6.h>
 #include <net/addrconf.h>
 
-struct ipv6_config ipv6_config =
-{
-	0,				/* forwarding		*/
-	IPV6_DEFAULT_HOPLIMIT,		/* hop limit		*/
-	1,				/* accept RAs		*/
-	1,				/* accept redirects	*/
-
-	1,				/* autoconfiguration	*/
-	1,				/* dad transmits	*/
-	MAX_RTR_SOLICITATIONS,		/* router solicits	*/
-	RTR_SOLICITATION_INTERVAL,	/* rtr solicit interval	*/
-	MAX_RTR_SOLICITATION_DELAY,	/* rtr solicit delay	*/
-
-	60*HZ,				/* rt cache timeout	*/
-	30*HZ,				/* rt gc period		*/
-};
+extern ctl_table ipv6_route_table[];
 
 #ifdef CONFIG_SYSCTL
 
-int ipv6_sysctl_forwarding(ctl_table *ctl, int write, struct file * filp,
-			   void *buffer, size_t *lenp)
-{
-	int val = ipv6_config.forwarding;
-	int retv;
-
-	retv = proc_dointvec(ctl, write, filp, buffer, lenp);
-
-	if (write) {
-		if (ipv6_config.forwarding && val == 0) {
-			printk(KERN_DEBUG "sysctl: IPv6 forwarding enabled\n");
-			ndisc_forwarding_on();
-			addrconf_forwarding_on();		       
-		}
-
-		if (ipv6_config.forwarding == 0 && val)
-			ndisc_forwarding_off();
-	}
-	return retv;
-}
-
 ctl_table ipv6_table[] = {
-        {NET_IPV6_FORWARDING, "forwarding",
-         &ipv6_config.forwarding, sizeof(int), 0644, NULL,
-         &ipv6_sysctl_forwarding},
-
-	{NET_IPV6_HOPLIMIT, "hop_limit",
-         &ipv6_config.hop_limit, sizeof(int), 0644, NULL,
-         &proc_dointvec},
-
-	{NET_IPV6_ACCEPT_RA, "accept_ra",
-         &ipv6_config.accept_ra, sizeof(int), 0644, NULL,
-         &proc_dointvec},
-
-	{NET_IPV6_ACCEPT_REDIRECTS, "accept_redirects",
-         &ipv6_config.accept_redirects, sizeof(int), 0644, NULL,
-         &proc_dointvec},
-
-	{NET_IPV6_AUTOCONF, "autoconf",
-         &ipv6_config.autoconf, sizeof(int), 0644, NULL,
-         &proc_dointvec},
-
-	{NET_IPV6_DAD_TRANSMITS, "dad_transmits",
-         &ipv6_config.dad_transmits, sizeof(int), 0644, NULL,
-         &proc_dointvec},
-
-	{NET_IPV6_RTR_SOLICITS, "router_solicitations",
-         &ipv6_config.rtr_solicits, sizeof(int), 0644, NULL,
-         &proc_dointvec},
-
-	{NET_IPV6_RTR_SOLICIT_INTERVAL, "router_solicitation_interval",
-         &ipv6_config.rtr_solicit_interval, sizeof(int), 0644, NULL,
-         &proc_dointvec},
-
-	{NET_IPV6_RTR_SOLICIT_DELAY, "router_solicitation_delay",
-         &ipv6_config.rtr_solicit_delay, sizeof(int), 0644, NULL,
-         &proc_dointvec},
-
+	{NET_IPV6_ROUTE, "route", NULL, 0, 0555, ipv6_route_table},
 	{0}
 };
 
diff -u --recursive --new-file v2.1.88/linux/net/ipv6/udp.c linux/net/ipv6/udp.c
--- v2.1.88/linux/net/ipv6/udp.c	Thu Feb 12 20:56:15 1998
+++ linux/net/ipv6/udp.c	Fri Mar  6 20:55:16 1998
@@ -300,10 +300,8 @@
 int udpv6_recvmsg(struct sock *sk, struct msghdr *msg, int len,
 		  int noblock, int flags, int *addr_len)
 {
-  	int copied = 0;
-  	int truesize;
   	struct sk_buff *skb;
-  	int err;
+  	int copied, err;
 
 	/*
 	 *	Check any passed addresses
@@ -318,16 +316,13 @@
 	 */
 	 	
 	skb = skb_recv_datagram(sk, flags, noblock, &err);
-	if(skb==NULL)
-  		return err;
+	if (!skb)
+		goto out;
   
- 	truesize=ntohs(((struct udphdr *)skb->h.raw)->len) - sizeof(struct udphdr);
-  	
-  	copied=truesize;
-
-  	if(copied>len) {
-  		copied=len;
-  		msg->msg_flags|=MSG_TRUNC;
+ 	copied = ntohs(((struct udphdr *)skb->h.raw)->len) - sizeof(struct udphdr);
+  	if (copied > len) {
+  		copied = len;
+  		msg->msg_flags |= MSG_TRUNC;
   	}
 
   	/*
@@ -337,7 +332,7 @@
 	err = skb_copy_datagram_iovec(skb, sizeof(struct udphdr), 
 				      msg->msg_iov, copied);
 	if (err)
-		return err; 
+		goto out_free;
 	
 	sk->stamp=skb->stamp;
 
@@ -346,7 +341,6 @@
 		struct sockaddr_in6 *sin6;
 	  
 		sin6 = (struct sockaddr_in6 *) msg->msg_name;
-		
 		sin6->sin6_family = AF_INET6;
 		sin6->sin6_port = skb->h.uh->source;
 
@@ -361,9 +355,12 @@
 				datagram_recv_ctl(sk, msg, skb);
 		}
   	}
-	
-  	skb_free_datagram(sk, skb);
-  	return(copied);
+	err = copied;
+
+out_free:
+	skb_free_datagram(sk, skb);
+out:
+	return err;
 }
 
 void udpv6_err(int type, int code, unsigned char *buff, __u32 info,
@@ -409,7 +406,7 @@
 	return 0;
 }
 
-static int __inline__ inet6_mc_check(struct sock *sk, struct in6_addr *addr)
+static __inline__ int inet6_mc_check(struct sock *sk, struct in6_addr *addr)
 {
 	struct ipv6_mc_socklist *mc;
 		
@@ -457,6 +454,7 @@
 {
 	struct sock *sk, *sk2;
 
+	SOCKHASH_LOCK();
 	sk = udp_hash[ntohs(uh->dest) & (UDP_HTABLE_SIZE - 1)];
 	sk = udp_v6_mcast_next(sk, uh->dest, daddr, uh->source, saddr);
 	if(sk) {
@@ -465,7 +463,7 @@
 					       uh->dest, saddr,
 					       uh->source, daddr))) {
 			struct sk_buff *buff = skb_clone(skb, GFP_ATOMIC);
-			if(sock_queue_rcv_skb(sk, buff) < 0) {
+			if (buff && sock_queue_rcv_skb(sk2, buff) < 0) {
 				buff->sk = NULL;
 				kfree_skb(buff);
 			}
@@ -475,6 +473,7 @@
 		skb->sk = NULL;
 		kfree_skb(skb);
 	}
+	SOCKHASH_UNLOCK();
 }
 
 int udpv6_rcv(struct sk_buff *skb, struct device *dev,
diff -u --recursive --new-file v2.1.88/linux/net/ipx/af_ipx.c linux/net/ipx/af_ipx.c
--- v2.1.88/linux/net/ipx/af_ipx.c	Mon Feb 23 18:12:14 1998
+++ linux/net/ipx/af_ipx.c	Fri Mar  6 20:46:20 1998
@@ -998,7 +998,8 @@
 		return -EPROTONOSUPPORT;
 
 	dev=dev_get(idef->ipx_device);
-	if(dev==NULL) return -ENODEV;
+	if (dev==NULL)
+		return -ENODEV;
 
 	intrfc = ipxitf_find_using_phys(dev, dlink_type);
 	if (intrfc != NULL) {
@@ -1107,9 +1108,9 @@
 			sipx->sipx_family=AF_IPX;
 			sipx->sipx_network=ipxif->if_netnum;
 			memcpy(sipx->sipx_node, ipxif->if_node, sizeof(sipx->sipx_node));
-			err = copy_to_user(arg,&ifr,sizeof(ifr));
-			if (err)
-				return -EFAULT;
+			err = -EFAULT;
+			if (!copy_to_user(arg, &ifr, sizeof(ifr)))
+				err = 0;
 			return err;
 		}
 		case SIOCAIPXITFCRT: 
@@ -2134,32 +2135,28 @@
 	struct sock *sk=sock->sk;
 	struct sockaddr_ipx *sipx=(struct sockaddr_ipx *)msg->msg_name;
 	struct ipxhdr *ipx = NULL;
-	int copied = 0;
-	int truesize;
 	struct sk_buff *skb;
-	int err;
+	int copied, err;
 
 	if (sk->zapped)
 		return -ENOTCONN;
 
 	skb=skb_recv_datagram(sk,flags&~MSG_DONTWAIT,flags&MSG_DONTWAIT,&err);
-	if(skb==NULL)
-		return err;
+	if (!skb)
+		goto out;
 
 	ipx = skb->nh.ipxh;
-	truesize=ntohs(ipx->ipx_pktsize) - sizeof(struct ipxhdr);
-	
-	copied = truesize;
+	copied = ntohs(ipx->ipx_pktsize) - sizeof(struct ipxhdr);
 	if(copied > size)
 	{
 		copied=size;
 		msg->msg_flags|=MSG_TRUNC;
 	}
 
-	err = skb_copy_datagram_iovec(skb,sizeof(struct ipxhdr),msg->msg_iov,copied);
-	
+	err = skb_copy_datagram_iovec(skb, sizeof(struct ipxhdr), msg->msg_iov,
+					copied);
 	if (err)
-		return err;
+		goto out_free;
 
 	msg->msg_namelen = sizeof(*sipx);
 
@@ -2171,9 +2168,12 @@
 		sipx->sipx_network=ipx->ipx_source.net;
 		sipx->sipx_type = ipx->ipx_type;
 	}
-	skb_free_datagram(sk, skb);
+	err = copied;
 
-	return(copied);
+out_free:
+	skb_free_datagram(sk, skb);
+out:
+	return err;
 }
 
 /*
@@ -2228,11 +2228,12 @@
 			{
 				if(sk->stamp.tv_sec==0)
 					return -ENOENT;
-				ret = copy_to_user((void *)arg,&sk->stamp,sizeof(struct timeval));
-				if (ret)
-					ret = -EFAULT;
+				ret = -EFAULT;
+				if (!copy_to_user((void *)arg, &sk->stamp,
+						sizeof(struct timeval)))
+					ret = 0;
 			}
-			return 0;
+			return ret;
 		}
 		case SIOCGIFDSTADDR:
 		case SIOCSIFDSTADDR:
diff -u --recursive --new-file v2.1.88/linux/net/netlink/af_netlink.c linux/net/netlink/af_netlink.c
--- v2.1.88/linux/net/netlink/af_netlink.c	Tue Feb 17 13:12:50 1998
+++ linux/net/netlink/af_netlink.c	Sun Mar  1 14:40:42 1998
@@ -359,6 +359,7 @@
 
 #ifdef NL_EMULATE_DEV
 		if (sk->protinfo.af_netlink.handler) {
+			skb_orphan(skb);
 			len = sk->protinfo.af_netlink.handler(protocol, skb);
 			netlink_unlock(sk);
 			return len;
@@ -400,6 +401,7 @@
 {
 #ifdef NL_EMULATE_DEV
 	if (sk->protinfo.af_netlink.handler) {
+		skb_orphan(skb);
 		sk->protinfo.af_netlink.handler(sk->protocol, skb);
 		return 0;
 	} else
@@ -758,15 +760,12 @@
 int netlink_post(int unit, struct sk_buff *skb)
 {
 	if (netlink_kernel[unit]) {
+		memset(skb->cb, 0, sizeof(skb->cb));
 		netlink_broadcast(netlink_kernel[unit]->sk, skb, 0, ~0, GFP_ATOMIC);
 		return 0;
 	}
 	return -EUNATCH;;
 }
-
-EXPORT_SYMBOL(netlink_attach);
-EXPORT_SYMBOL(netlink_detach);
-EXPORT_SYMBOL(netlink_post);
 
 #endif
 
diff -u --recursive --new-file v2.1.88/linux/net/netlink/netlink_dev.c linux/net/netlink/netlink_dev.c
--- v2.1.88/linux/net/netlink/netlink_dev.c	Sun Nov 30 14:00:40 1997
+++ linux/net/netlink/netlink_dev.c	Fri Feb 27 08:27:00 1998
@@ -42,7 +42,7 @@
 
 	if (sock->ops->poll==NULL)
 		return 0;
-	return sock->ops->poll(sock, wait);
+	return sock->ops->poll(file, sock, wait);
 }
 
 /*
diff -u --recursive --new-file v2.1.88/linux/net/netsyms.c linux/net/netsyms.c
--- v2.1.88/linux/net/netsyms.c	Tue Jan 20 16:45:15 1998
+++ linux/net/netsyms.c	Sun Mar  1 14:40:42 1998
@@ -198,6 +198,9 @@
 EXPORT_SYMBOL(icmp_send);
 EXPORT_SYMBOL(ip_options_compile);
 EXPORT_SYMBOL(arp_send);
+#ifdef CONFIG_SHAPER_MODULE
+EXPORT_SYMBOL(arp_broken_ops);
+#endif
 EXPORT_SYMBOL(ip_id_count);
 EXPORT_SYMBOL(ip_send_check);
 EXPORT_SYMBOL(ip_fragment);
@@ -206,6 +209,7 @@
 EXPORT_SYMBOL(ip_mc_inc_group);
 EXPORT_SYMBOL(ip_mc_dec_group);
 EXPORT_SYMBOL(__ip_finish_output);
+EXPORT_SYMBOL(inet_dgram_ops);
 
 /* needed for ip_gre -cw */
 EXPORT_SYMBOL(ip_statistics);
@@ -213,7 +217,6 @@
 #ifdef CONFIG_IPV6_MODULE
 /* inet functions common to v4 and v6 */
 EXPORT_SYMBOL(inet_stream_ops);
-EXPORT_SYMBOL(inet_dgram_ops);
 EXPORT_SYMBOL(inet_release);
 EXPORT_SYMBOL(inet_stream_connect);
 EXPORT_SYMBOL(inet_dgram_connect);
@@ -289,12 +292,24 @@
 EXPORT_SYMBOL(xrlim_allow);
 #endif
 
+#ifdef CONFIG_NETLINK
+EXPORT_SYMBOL(netlink_set_err);
+EXPORT_SYMBOL(netlink_broadcast);
+EXPORT_SYMBOL(netlink_unicast);
+EXPORT_SYMBOL(netlink_kernel_create);
+EXPORT_SYMBOL(netlink_dump_start);
+EXPORT_SYMBOL(netlink_ack);
+#if defined(CONFIG_NETLINK_DEV) || defined(CONFIG_NETLINK_DEV_MODULE)
+EXPORT_SYMBOL(netlink_attach);
+EXPORT_SYMBOL(netlink_detach);
+EXPORT_SYMBOL(netlink_post);
+#endif
+#endif
+
 #ifdef CONFIG_RTNETLINK
 EXPORT_SYMBOL(rtnetlink_links);
 EXPORT_SYMBOL(__rta_fill);
 EXPORT_SYMBOL(rtnetlink_dump_ifinfo);
-EXPORT_SYMBOL(netlink_set_err);
-EXPORT_SYMBOL(netlink_broadcast);
 EXPORT_SYMBOL(rtnl_wlockct);
 EXPORT_SYMBOL(rtnl);
 EXPORT_SYMBOL(neigh_delete);
@@ -375,9 +390,15 @@
 EXPORT_SYMBOL(dev_alloc_name);
 EXPORT_SYMBOL(dev_ioctl);
 EXPORT_SYMBOL(dev_queue_xmit);
+EXPORT_SYMBOL(netdev_dropping);
 #ifdef CONFIG_NET_FASTROUTE
 EXPORT_SYMBOL(dev_fastroute_stat);
 #endif
+#ifdef CONFIG_NET_HW_FLOWCONTROL
+EXPORT_SYMBOL(netdev_register_fc);
+EXPORT_SYMBOL(netdev_unregister_fc);
+EXPORT_SYMBOL(netdev_fc_xoff);
+#endif
 #ifdef CONFIG_IP_ACCT
 EXPORT_SYMBOL(ip_acct_output);
 #endif
@@ -415,5 +436,7 @@
 EXPORT_SYMBOL(register_qdisc);
 EXPORT_SYMBOL(unregister_qdisc);
 EXPORT_SYMBOL(noop_qdisc);
+
+EXPORT_SYMBOL(register_gifconf);
 
 #endif  /* CONFIG_NET */
diff -u --recursive --new-file v2.1.88/linux/net/packet/af_packet.c linux/net/packet/af_packet.c
--- v2.1.88/linux/net/packet/af_packet.c	Thu Feb 12 20:56:15 1998
+++ linux/net/packet/af_packet.c	Fri Mar  6 20:57:11 1998
@@ -66,8 +66,8 @@
 #include <linux/module.h>
 #include <linux/init.h>
 
-#if defined(CONFIG_DLCI) || defined(CONFIG_DLCI_MODULE)
-#include <linux/if_frad.h>
+#ifdef CONFIG_INET
+#include <net/inet_common.h>
 #endif
 
 #ifdef CONFIG_BRIDGE
@@ -778,9 +778,8 @@
 			  int flags, struct scm_cookie *scm)
 {
 	struct sock *sk = sock->sk;
-	int copied=0;
 	struct sk_buff *skb;
-	int err;
+	int copied, err;
 
 #if 0
 	/* What error should we return now? EUNATTACH? */
@@ -816,7 +815,7 @@
 	 */
 
 	if(skb==NULL)
-		return err;
+		goto out;
 
 	/*
 	 *	You lose any data beyond the buffer you gave. If it worries a
@@ -824,7 +823,7 @@
 	 */
 
 	copied = skb->len;
-	if(copied>len)
+	if (copied > len)
 	{
 		copied=len;
 		msg->msg_flags|=MSG_TRUNC;
@@ -833,9 +832,7 @@
 	/* We can't use skb_copy_datagram here */
 	err = memcpy_toiovec(msg->msg_iov, skb->data, copied);
 	if (err)
-	{
-		return -EFAULT;
-	}
+		goto out_free;
 
 	sk->stamp=skb->stamp;
 
@@ -843,13 +840,15 @@
 		memcpy(msg->msg_name, skb->cb, msg->msg_namelen);
 
 	/*
-	 *	Free or return the buffer as appropriate. Again this hides all the
-	 *	races and re-entrancy issues from us.
+	 *	Free or return the buffer as appropriate. Again this
+	 *	hides all the races and re-entrancy issues from us.
 	 */
+	err = copied;
 
+out_free:
 	skb_free_datagram(sk, skb);
-
-	return(copied);
+out:
+	return err;
 }
 
 #ifdef CONFIG_SOCK_PACKET
@@ -1117,7 +1116,9 @@
 				err = -EFAULT;
 			return err;
 		case SIOCGIFFLAGS:
+#ifndef CONFIG_INET
 		case SIOCSIFFLAGS:
+#endif
 		case SIOCGIFCONF:
 		case SIOCGIFMETRIC:
 		case SIOCSIFMETRIC:
@@ -1146,23 +1147,28 @@
 			return -ENOPKG;
 #endif						
 			
+#ifdef CONFIG_INET
+		case SIOCADDRT:
+		case SIOCDELRT:
+		case SIOCDARP:
+		case SIOCGARP:
+		case SIOCSARP:
+		case SIOCDRARP:
+		case SIOCGRARP:
+		case SIOCSRARP:
+		case SIOCGIFADDR:
+		case SIOCSIFADDR:
+		case SIOCGIFBRDADDR:
+		case SIOCSIFBRDADDR:
+		case SIOCGIFNETMASK:
+		case SIOCSIFNETMASK:
+		case SIOCGIFDSTADDR:
+		case SIOCSIFDSTADDR:
+		case SIOCSIFFLAGS:
 		case SIOCADDDLCI:
 		case SIOCDELDLCI:
-#ifdef CONFIG_DLCI
-			return(dlci_ioctl(cmd, (void *) arg));
-#endif
-
-#ifdef CONFIG_DLCI_MODULE
-
-#ifdef CONFIG_KERNELD
-			if (dlci_ioctl_hook == NULL)
-				request_module("dlci");
+			return inet_dgram_ops.ioctl(sock, cmd, arg);
 #endif
-
-			if (dlci_ioctl_hook)
-				return((*dlci_ioctl_hook)(cmd, (void *) arg));
-#endif
-			return -ENOPKG;
 
 		default:
 			if ((cmd >= SIOCDEVPRIVATE) &&
diff -u --recursive --new-file v2.1.88/linux/net/socket.c linux/net/socket.c
--- v2.1.88/linux/net/socket.c	Fri Jan 30 11:28:10 1998
+++ linux/net/socket.c	Thu Mar  5 11:49:55 1998
@@ -11,7 +11,7 @@
  *		Anonymous	:	NOTSOCK/BADF cleanup. Error fix in
  *					shutdown()
  *		Alan Cox	:	verify_area() fixes
- *		Alan Cox	: 	Removed DDI
+ *		Alan Cox	:	Removed DDI
  *		Jonathan Kamens	:	SOCK_DGRAM reconnect bug
  *		Alan Cox	:	Moved a load of checks to the very
  *					top level.
@@ -152,7 +152,7 @@
 					   the AF_UNIX size (see net/unix/af_unix.c
 					   :unix_mkname()).  
 					 */
- 
+
 int move_addr_to_kernel(void *uaddr, int ulen, void *kaddr)
 {
 	if(ulen<0||ulen>MAX_SOCK_ADDR)
@@ -184,7 +184,7 @@
 	 *	"fromlen shall refer to the value before truncation.."
 	 *			1003.1g
 	 */
- 	return __put_user(klen, ulen);
+	return __put_user(klen, ulen);
 }
 
 /*
@@ -221,7 +221,7 @@
 		 */
 		inode->i_count++;
 
-		current->files->fd[fd] = file;
+		fd_install(fd, file);
 		file->f_op = &socket_file_ops;
 		file->f_mode = 3;
 		file->f_flags = O_RDWR;
@@ -239,10 +239,11 @@
  *	Go from a file number to its socket slot.
  */
 
-extern __inline__ struct socket *sockfd_lookup(int fd, int *err)
+extern struct socket *sockfd_lookup(int fd, int *err)
 {
 	struct file *file;
 	struct inode *inode;
+	struct socket *sock;
 
 	if (!(file = fget(fd)))
 	{
@@ -251,14 +252,18 @@
 	}
 
 	inode = file->f_dentry->d_inode;
-	if (!inode || !inode->i_sock || !socki_lookup(inode))
+	if (!inode || !inode->i_sock || !(sock = socki_lookup(inode)))
 	{
 		*err = -ENOTSOCK;
 		fput(file);
 		return NULL;
 	}
 
-	return socki_lookup(inode);
+	if (sock->file != file) {
+		printk(KERN_ERR "socki_lookup: socket file changed!\n");
+		sock->file = file;
+	}
+	return sock;
 }
 
 extern __inline__ void sockfd_put(struct socket *sock)
@@ -301,14 +306,15 @@
 
 void sock_release(struct socket *sock)
 {
-	int oldstate;
-
-	if ((oldstate = sock->state) != SS_UNCONNECTED)
+	if (sock->state != SS_UNCONNECTED)
 		sock->state = SS_DISCONNECTING;
 
 	if (sock->ops) 
 		sock->ops->release(sock, NULL);
 
+	if (sock->fasync_list)
+		printk(KERN_ERR "sock_release: fasync list not empty!\n");
+
 	--sockets_in_use;	/* Bookkeeping.. */
 	sock->file=NULL;
 	iput(sock->inode);
@@ -320,13 +326,10 @@
 	struct scm_cookie scm;
 
 	err = scm_send(sock, msg, &scm);
-	if (err < 0)
-		return err;
-
-	err = sock->ops->sendmsg(sock, msg, size, &scm);
-
-	scm_destroy(&scm);
-
+	if (err >= 0) {
+		err = sock->ops->sendmsg(sock, msg, size, &scm);
+		scm_destroy(&scm);
+	}
 	return err;
 }
 
@@ -337,11 +340,8 @@
 	memset(&scm, 0, sizeof(scm));
 
 	size = sock->ops->recvmsg(sock, msg, size, flags, &scm);
-
-	if (size < 0)
-		return size;
-
-	scm_recv(sock, msg, &scm, flags);
+	if (size >= 0)
+		scm_recv(sock, msg, &scm, flags);
 
 	return size;
 }
@@ -453,7 +453,7 @@
 	   unsigned long arg)
 {
 	struct socket *sock = socki_lookup(inode);
-  	return sock->ops->ioctl(sock, cmd, arg);
+	return sock->ops->ioctl(sock, cmd, arg);
 }
 
 
@@ -467,7 +467,7 @@
 	 *	We can't return errors to poll, so it's either yes or no. 
 	 */
 
-	return sock->ops->poll(sock, wait);
+	return sock->ops->poll(file, sock, wait);
 }
 
 
@@ -491,7 +491,7 @@
 /*
  *	Update the socket async list
  */
- 
+
 static int sock_fasync(struct file *filp, int on)
 {
 	struct fasync_struct *fa, *fna=NULL, **prev;
@@ -571,12 +571,12 @@
 	int i;
 	struct socket *sock;
 
- 	/*
- 	 *	Check protocol is in range
- 	 */
- 	if(family<0||family>=NPROTO)
+	/*
+	 *	Check protocol is in range
+	 */
+	if(family<0||family>=NPROTO)
 		return -EINVAL;
- 		
+		
 #if defined(CONFIG_KERNELD) && defined(CONFIG_NET)
 	/* Attempt to load a protocol module if the find failed. 
 	 * 
@@ -593,14 +593,14 @@
 #endif
 
 	if (net_families[family]==NULL)
-  		return -EINVAL;
+		return -EINVAL;
 
 /*
  *	Check that this is a type that we know how to manipulate and
  *	the protocol makes sense here. The family can still reject the
  *	protocol later.
  */
-  
+ 
 	if ((type != SOCK_STREAM && type != SOCK_DGRAM &&
 	     type != SOCK_SEQPACKET && type != SOCK_RAW && type != SOCK_RDM &&
 #ifdef CONFIG_XTP
@@ -663,9 +663,8 @@
 
 asmlinkage int sys_socketpair(int family, int type, int protocol, int usockvec[2])
 {
-	int fd1, fd2, i;
-	struct socket *sock1=NULL, *sock2=NULL;
-	int err;
+	struct socket *sock1, *sock2;
+	int fd1, fd2, err;
 
 	lock_kernel();
 
@@ -674,48 +673,51 @@
 	 * supports the socketpair call.
 	 */
 
-	if ((fd1 = sys_socket(family, type, protocol)) < 0) {
-		err = fd1;
+	err = sys_socket(family, type, protocol);
+	if (err < 0)
 		goto out;
-	}
+	fd1 = err;
 
-	sock1 = sockfd_lookup(fd1, &err);
-	if (!sock1)
-		goto out;
 	/*
-	 *	Now grab another socket and try to connect the two together. 
+	 * Now grab another socket
 	 */
 	err = -EINVAL;
-	if ((fd2 = sys_socket(family, type, protocol)) < 0) 
-	{
-		sys_close(fd1);
-		goto out;
-	}
+	fd2 = sys_socket(family, type, protocol);
+	if (fd2 < 0) 
+		goto out_close1;
 
-	sock2 = sockfd_lookup(fd2,&err);
+	/*
+	 * Get the sockets for the two fd's
+	 */
+	sock1 = sockfd_lookup(fd1, &err);
+	if (!sock1)
+		goto out_close2;
+	sock2 = sockfd_lookup(fd2, &err);
 	if (!sock2)
-		goto out;
-	if ((i = sock1->ops->socketpair(sock1, sock2)) < 0) 
-	{
-		sys_close(fd1);
+		goto out_put1;
+
+	/* try to connect the two sockets together */ 
+	err = sock1->ops->socketpair(sock1, sock2);
+	if (err < 0) 
+		goto out_put2;
+
+	err = put_user(fd1, &usockvec[0]); 
+	if (err) 
+		goto out_put2;
+	err = put_user(fd2, &usockvec[1]);
+
+out_put2:
+	sockfd_put(sock2);
+out_put1:
+	sockfd_put(sock1);
+
+	if (err) {
+	out_close2:
 		sys_close(fd2);
-		err = i;
-	}
-	else
-	{
-		err = put_user(fd1, &usockvec[0]); 
-		if (!err) 
-			err = put_user(fd2, &usockvec[1]);
-		if (err) {
-			sys_close(fd1);
-			sys_close(fd2);
-		}
+	out_close1:
+		sys_close(fd1);
 	}
 out:
-	if(sock1)
-		sockfd_put(sock1);
-	if(sock2)
-		sockfd_put(sock2);
 	unlock_kernel();
 	return err;
 }
@@ -728,7 +730,7 @@
  *	We move the socket address to kernel space before we call
  *	the protocol layer (having also checked the address is ok).
  */
- 
+
 asmlinkage int sys_bind(int fd, struct sockaddr *umyaddr, int addrlen)
 {
 	struct socket *sock;
@@ -790,58 +792,54 @@
 	int len;
 
 	lock_kernel();
-restart:
-  	if ((sock = sockfd_lookup(fd, &err))!=NULL)
-  	{
-		if (!(newsock = sock_alloc())) 
-		{
-			err=-EMFILE;
-			goto out;
-		}
+	sock = sockfd_lookup(fd, &err);
+	if (!sock)
+		goto out;
 
-		inode = newsock->inode;
-		newsock->type = sock->type;
+restart:
+	err = -EMFILE;
+	if (!(newsock = sock_alloc())) 
+		goto out_put;
 
-		if ((err = sock->ops->dup(newsock, sock)) < 0) 
-		{
-			sock_release(newsock);
-			goto out;
-		}
+	inode = newsock->inode;
+	newsock->type = sock->type;
 
-		err = newsock->ops->accept(sock, newsock, current->files->fd[fd]->f_flags);
+	err = sock->ops->dup(newsock, sock);
+	if (err < 0) 
+		goto out_release;
 
-		if (err < 0)
-		{
-			sock_release(newsock);
-			goto out;
-		}
-		newsock = socki_lookup(inode);
+	err = newsock->ops->accept(sock, newsock, sock->file->f_flags);
+	if (err < 0)
+		goto out_release;
+	newsock = socki_lookup(inode);
 
-		if ((err = get_fd(inode)) < 0) 
+	if ((err = get_fd(inode)) < 0) 
+		goto out_inval;
+	newsock->file = current->files->fd[err];
+
+	if (upeer_sockaddr)
+	{
+		/* Handle the race where the accept works and we
+		   then getname after it has closed again */
+		if(newsock->ops->getname(newsock, (struct sockaddr *)address, &len, 1)<0)
 		{
-			sock_release(newsock);
-			err=-EINVAL;
-			goto out;
+			sys_close(err);
+			goto restart;
 		}
+		move_addr_to_user(address, len, upeer_sockaddr, upeer_addrlen);
+	}
 
-		newsock->file = current->files->fd[err];
-	
-		if (upeer_sockaddr)
-		{
-			/* Handle the race where the accept works and we
-			   then getname after it has closed again */
-			if(newsock->ops->getname(newsock, (struct sockaddr *)address, &len, 1)<0)
-			{
-				sys_close(err);
-				goto restart;
-			}
-			move_addr_to_user(address,len, upeer_sockaddr, upeer_addrlen);
-		}
+out_put:
+	sockfd_put(sock);
 out:
-		sockfd_put(sock);		
-	}
 	unlock_kernel();
 	return err;
+
+out_inval:
+	err = -EINVAL;
+out_release:
+	sock_release(newsock);
+	goto out_put;
 }
 
 
@@ -856,7 +854,7 @@
  *	other SEQPACKET protocols that take time to connect() as it doesn't
  *	include the -EINPROGRESS status for such sockets.
  */
- 
+
 asmlinkage int sys_connect(int fd, struct sockaddr *uservaddr, int addrlen)
 {
 	struct socket *sock;
@@ -864,13 +862,17 @@
 	int err;
 
 	lock_kernel();
-	if ((sock = sockfd_lookup(fd,&err))!=NULL)
-	{
-		if((err=move_addr_to_kernel(uservaddr,addrlen,address))>=0)
-			err = sock->ops->connect(sock, (struct sockaddr *)address, addrlen,
-			     current->files->fd[fd]->f_flags);
-		sockfd_put(sock);
-	}
+	sock = sockfd_lookup(fd, &err);
+	if (!sock)
+		goto out;
+	err = move_addr_to_kernel(uservaddr, addrlen, address);
+	if (err < 0)
+		goto out_put;
+	err = sock->ops->connect(sock, (struct sockaddr *) address, addrlen,
+				 sock->file->f_flags);
+out_put:
+	sockfd_put(sock);
+out:
 	unlock_kernel();
 	return err;
 }
@@ -884,16 +886,20 @@
 {
 	struct socket *sock;
 	char address[MAX_SOCK_ADDR];
-	int len;
-	int err;
+	int len, err;
 	
 	lock_kernel();
-	if ((sock = sockfd_lookup(fd, &err))!=NULL)
-	{
-		if((err=sock->ops->getname(sock, (struct sockaddr *)address, &len, 0))==0)
-			err=move_addr_to_user(address,len, usockaddr, usockaddr_len);
-		sockfd_put(sock);
-	}
+	sock = sockfd_lookup(fd, &err);
+	if (!sock)
+		goto out;
+	err = sock->ops->getname(sock, (struct sockaddr *)address, &len, 0);
+	if (err)
+		goto out_put;
+	err = move_addr_to_user(address, len, usockaddr, usockaddr_len);
+
+out_put:
+	sockfd_put(sock);
+out:
 	unlock_kernel();
 	return err;
 }
@@ -902,7 +908,7 @@
  *	Get the remote address ('name') of a socket object. Move the obtained
  *	name to user space.
  */
- 
+
 asmlinkage int sys_getpeername(int fd, struct sockaddr *usockaddr, int *usockaddr_len)
 {
 	struct socket *sock;
@@ -934,27 +940,29 @@
 	struct iovec iov;
 
 	lock_kernel();
-	if ((sock = sockfd_lookup(fd, &err))!=NULL)
-	{
-		if(len>=0)
-		{
-			iov.iov_base=buff;
-			iov.iov_len=len;
-			msg.msg_name=NULL;
-			msg.msg_namelen=0;
-			msg.msg_iov=&iov;
-			msg.msg_iovlen=1;
-			msg.msg_control=NULL;
-			msg.msg_controllen=0;
-			if (current->files->fd[fd]->f_flags & O_NONBLOCK)
-				flags |= MSG_DONTWAIT;
-			msg.msg_flags=flags;
-			err=sock_sendmsg(sock, &msg, len);
-		}
-		else
-			err=-EINVAL;
-		sockfd_put(sock);
-	}
+	sock = sockfd_lookup(fd, &err);
+	if (!sock)
+		goto out;
+	err = -EINVAL;
+	if (len < 0)
+		goto out_put;
+
+	iov.iov_base=buff;
+	iov.iov_len=len;
+	msg.msg_name=NULL;
+	msg.msg_namelen=0;
+	msg.msg_iov=&iov;
+	msg.msg_iovlen=1;
+	msg.msg_control=NULL;
+	msg.msg_controllen=0;
+	if (sock->file->f_flags & O_NONBLOCK)
+		flags |= MSG_DONTWAIT;
+	msg.msg_flags = flags;
+	err = sock_sendmsg(sock, &msg, len);
+
+out_put:
+	sockfd_put(sock);
+out:
 	unlock_kernel();
 	return err;
 }
@@ -975,36 +983,37 @@
 	struct iovec iov;
 	
 	lock_kernel();
-	if ((sock = sockfd_lookup(fd,&err))!=NULL)
+	sock = sockfd_lookup(fd, &err);
+	if (!sock)
+		goto out;
+	iov.iov_base=buff;
+	iov.iov_len=len;
+	msg.msg_name=NULL;
+	msg.msg_iov=&iov;
+	msg.msg_iovlen=1;
+	msg.msg_control=NULL;
+	msg.msg_controllen=0;
+	msg.msg_namelen=addr_len;
+	if(addr)
 	{
-		iov.iov_base=buff;
-		iov.iov_len=len;
-		msg.msg_name=NULL;
-		msg.msg_iov=&iov;
-		msg.msg_iovlen=1;
-		msg.msg_control=NULL;
-		msg.msg_controllen=0;
-		msg.msg_namelen=addr_len;
-		if(addr)
-		{
-			err=move_addr_to_kernel(addr,addr_len,address);
-			if (err < 0)
-				goto bad;
-			msg.msg_name=address;
-		}
-		if (current->files->fd[fd]->f_flags & O_NONBLOCK)
-			flags |= MSG_DONTWAIT;
-		msg.msg_flags=flags;
-		err=sock_sendmsg(sock, &msg, len);
-bad:		
-		sockfd_put(sock);
+		err = move_addr_to_kernel(addr, addr_len, address);
+		if (err < 0)
+			goto out_put;
+		msg.msg_name=address;
 	}
+	if (sock->file->f_flags & O_NONBLOCK)
+		flags |= MSG_DONTWAIT;
+	msg.msg_flags = flags;
+	err = sock_sendmsg(sock, &msg, len);
+
+out_put:		
+	sockfd_put(sock);
+out:
 	unlock_kernel();
 	return err;
 }
 
 
-
 /*
  *	Receive a frame from the socket and optionally record the address of the 
  *	sender. We verify the buffers are writable and if needed move the
@@ -1021,26 +1030,30 @@
 	int err,err2;
 
 	lock_kernel();
-	if ((sock = sockfd_lookup(fd, &err))!=NULL)
-	{  
-  		msg.msg_control=NULL;
-  		msg.msg_controllen=0;
-  		msg.msg_iovlen=1;
-  		msg.msg_iov=&iov;
-  		iov.iov_len=size;
-  		iov.iov_base=ubuf;
-  		msg.msg_name=address;
-  		msg.msg_namelen=MAX_SOCK_ADDR;
-		err=sock_recvmsg(sock, &msg, size,
-			  (current->files->fd[fd]->f_flags & O_NONBLOCK) ? (flags | MSG_DONTWAIT) : flags);
-		if(err>=0 && addr!=NULL)
-		{
-			err2=move_addr_to_user(address, msg.msg_namelen, addr, addr_len);
-			if(err2<0)
-				err=err2;
-		}
-		sockfd_put(sock);			
-	}		
+	sock = sockfd_lookup(fd, &err);
+	if (!sock)
+		goto out;
+
+	msg.msg_control=NULL;
+	msg.msg_controllen=0;
+	msg.msg_iovlen=1;
+	msg.msg_iov=&iov;
+	iov.iov_len=size;
+	iov.iov_base=ubuf;
+	msg.msg_name=address;
+	msg.msg_namelen=MAX_SOCK_ADDR;
+	if (sock->file->f_flags & O_NONBLOCK)
+		flags |= MSG_DONTWAIT;
+	err=sock_recvmsg(sock, &msg, size, flags);
+
+	if(err >= 0 && addr != NULL)
+	{
+		err2=move_addr_to_user(address, msg.msg_namelen, addr, addr_len);
+		if(err2<0)
+			err=err2;
+	}
+	sockfd_put(sock);			
+out:
 	unlock_kernel();
 	return err;
 }
@@ -1058,7 +1071,7 @@
  *	Set a socket option. Because we don't know the option lengths we have
  *	to pass the user mode parameter for the protocols to sort out.
  */
- 
+
 asmlinkage int sys_setsockopt(int fd, int level, int optname, char *optval, int optlen)
 {
 	int err;
@@ -1104,7 +1117,7 @@
 /*
  *	Shutdown a socket.
  */
- 
+
 asmlinkage int sys_shutdown(int fd, int how)
 {
 	int err;
@@ -1123,7 +1136,7 @@
 /*
  *	BSD sendmsg interface
  */
- 
+
 asmlinkage int sys_sendmsg(int fd, struct msghdr *msg, unsigned flags)
 {
 	struct socket *sock;
@@ -1137,11 +1150,9 @@
 	
 	lock_kernel();
 
+	err=-EFAULT;
 	if (copy_from_user(&msg_sys,msg,sizeof(struct msghdr)))
-	{
-		err=-EFAULT;
 		goto out; 
-	}
 	/* do not move before msg_sys is valid */
 	if (msg_sys.msg_iovlen>UIO_MAXIOV)
 		goto out;
@@ -1166,26 +1177,24 @@
 			/* Note - when this code becomes multithreaded on
 			 * SMP machines you have a race to fix here.
 			 */
+			err = -ENOBUFS;
 			ctl_buf = sock_kmalloc(sock->sk, msg_sys.msg_controllen, 
 					       GFP_KERNEL);
 			if (ctl_buf == NULL) 
-			{
-				err = -ENOBUFS;
 				goto failed2;
-			}
 		}
+		err = -EFAULT;
 		if (copy_from_user(ctl_buf, msg_sys.msg_control, 
-					    msg_sys.msg_controllen)) {
-			err = -EFAULT;
+					    msg_sys.msg_controllen))
 			goto failed;
-		}
 		msg_sys.msg_control = ctl_buf;
 	}
 	msg_sys.msg_flags = flags;
 
-	if (current->files->fd[fd]->f_flags & O_NONBLOCK)
+	if (sock->file->f_flags & O_NONBLOCK)
 		msg_sys.msg_flags |= MSG_DONTWAIT;
 	err = sock_sendmsg(sock, &msg_sys, total_len);
+
 failed:
 	if (ctl_buf != ctl)    
 		sock_kfree_s(sock->sk, ctl_buf, msg_sys.msg_controllen);
@@ -1201,7 +1210,7 @@
 /*
  *	BSD recvmsg interface
  */
- 
+
 asmlinkage int sys_recvmsg(int fd, struct msghdr *msg, unsigned int flags)
 {
 	struct socket *sock;
@@ -1250,7 +1259,7 @@
 	
 	if ((sock = sockfd_lookup(fd, &err))!=NULL)
 	{
-		if (current->files->fd[fd]->f_flags&O_NONBLOCK)
+		if (sock->file->f_flags & O_NONBLOCK)
 			flags |= MSG_DONTWAIT;
 		err=sock_recvmsg(sock, &msg_sys, total_len, flags);
 		if(err>=0)
@@ -1262,12 +1271,13 @@
 
 	if (uaddr != NULL && err>=0)
 		err = move_addr_to_user(addr, msg_sys.msg_namelen, uaddr, uaddr_len);
-	if (err>=0) {
-		err = __put_user(msg_sys.msg_flags, &msg->msg_flags);
-		if (!err)
-			err = __put_user((unsigned long)msg_sys.msg_control-cmsg_ptr, 
+	if (err < 0)
+		goto out;
+	err = __put_user(msg_sys.msg_flags, &msg->msg_flags);
+	if (err)
+		goto out;
+	err = __put_user((unsigned long)msg_sys.msg_control-cmsg_ptr, 
 							 &msg->msg_controllen);
-	}
 out:
 	unlock_kernel();
 	if(err<0)
@@ -1390,7 +1400,7 @@
  *	advertise its address family, and have it linked into the
  *	SOCKET module.
  */
- 
+
 int sock_register(struct net_proto_family *ops)
 {
 	if (ops->family >= NPROTO) {
@@ -1406,7 +1416,7 @@
  *	remove its address family, and have it unlinked from the
  *	SOCKET module.
  */
- 
+
 int sock_unregister(int family)
 {
 	if (family < 0 || family >= NPROTO)
diff -u --recursive --new-file v2.1.88/linux/net/sunrpc/xprt.c linux/net/sunrpc/xprt.c
--- v2.1.88/linux/net/sunrpc/xprt.c	Sat Jan 17 12:33:41 1998
+++ linux/net/sunrpc/xprt.c	Sat Feb 28 11:26:29 1998
@@ -48,11 +48,11 @@
 #include <linux/udp.h>
 #include <linux/unistd.h>
 #include <linux/sunrpc/clnt.h>
+#include <linux/file.h>
+
 #include <net/sock.h>
 
-#if LINUX_VERSION_CODE >= 0x020100
 #include <asm/uaccess.h>
-#endif
 
 #define SOCK_HAS_USER_DATA
 
@@ -319,7 +319,7 @@
 	sk->write_space  = xprt->old_write_space;
 
 	if (xprt->file)
-		close_fp(xprt->file);
+		fput(xprt->file);
 	else
 		sock_release(xprt->sock);
 }
diff -u --recursive --new-file v2.1.88/linux/net/unix/af_unix.c linux/net/unix/af_unix.c
--- v2.1.88/linux/net/unix/af_unix.c	Thu Feb 12 20:56:15 1998
+++ linux/net/unix/af_unix.c	Fri Mar  6 20:46:20 1998
@@ -1033,8 +1033,9 @@
 		 
 		size=len-sent;
 
-		if (size>(sk->sndbuf-sizeof(struct sk_buff))/2)	/* Keep two messages in the pipe so it schedules better */
-			size=(sk->sndbuf-sizeof(struct sk_buff))/2;
+		/* Keep two messages in the pipe so it schedules better */
+		if (size > (sk->sndbuf - sizeof(struct sk_buff)) / 2)
+			size = (sk->sndbuf - sizeof(struct sk_buff)) / 2;
 
 		/*
 		 *	Keep to page sized kmalloc()'s as various people
@@ -1056,7 +1057,7 @@
 		if (skb==NULL)
 		{
 			if (sent)
-				return sent;
+				goto out;
 			return err;
 		}
 
@@ -1074,6 +1075,7 @@
 		if (scm->fp)
 			unix_attach_fds(scm, skb);
 
+		/* N.B. this could fail with -EFAULT */
 		memcpy_fromiovec(skb_put(skb,size), msg->msg_iov, size);
 
 		other=unix_peer(sk);
@@ -1082,7 +1084,7 @@
 		{
 			kfree_skb(skb);
 			if(sent)
-				return sent;
+				goto out;
 			send_sig(SIGPIPE,current,0);
 			return -EPIPE;
 		}
@@ -1091,6 +1093,7 @@
 		other->data_ready(other,size);
 		sent+=size;
 	}
+out:
 	return sent;
 }
 
@@ -1121,20 +1124,20 @@
 
 	msg->msg_namelen = 0;
 
-	skb=skb_recv_datagram(sk, flags, noblock, &err);
-	if(skb==NULL)
-		return err;
+	skb = skb_recv_datagram(sk, flags, noblock, &err);
+	if (!skb)
+		goto out;
 
 	if (msg->msg_name)
 	{
+		msg->msg_namelen = sizeof(short);
 		if (skb->sk->protinfo.af_unix.addr)
 		{
-			memcpy(msg->msg_name, skb->sk->protinfo.af_unix.addr->name,
-			       skb->sk->protinfo.af_unix.addr->len);
 			msg->msg_namelen=skb->sk->protinfo.af_unix.addr->len;
+			memcpy(msg->msg_name,
+				skb->sk->protinfo.af_unix.addr->name,
+				skb->sk->protinfo.af_unix.addr->len);
 		}
-		else
-			msg->msg_namelen=sizeof(short);
 	}
 
 	if (size > skb->len)
@@ -1142,8 +1145,9 @@
 	else if (size < skb->len)
 		msg->msg_flags |= MSG_TRUNC;
 
-	if (skb_copy_datagram_iovec(skb, 0, msg->msg_iov, size))
-		return -EFAULT;
+	err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, size);
+	if (err)
+		goto out_free;
 
 	scm->creds = *UNIXCREDS(skb);
 
@@ -1169,8 +1173,12 @@
 		if (UNIXCB(skb).fp)
 			scm->fp = scm_fp_dup(UNIXCB(skb).fp);
 	}
+	err = size;
+
+out_free:
 	skb_free_datagram(sk,skb);
-	return size;
+out:
+	return err;
 }
 
 
@@ -1189,7 +1197,7 @@
 
 	if (flags&MSG_OOB)
 		return -EOPNOTSUPP;
-	if(flags&MSG_WAITALL)
+	if (flags&MSG_WAITALL)
 		target = size;
 		
 		
@@ -1245,18 +1253,19 @@
 		/* Copy address just once */
 		if (sunaddr)
 		{
+			msg->msg_namelen = sizeof(short);
 			if (skb->sk->protinfo.af_unix.addr)
 			{
-				memcpy(sunaddr, skb->sk->protinfo.af_unix.addr->name,
-				       skb->sk->protinfo.af_unix.addr->len);
 				msg->msg_namelen=skb->sk->protinfo.af_unix.addr->len;
+				memcpy(sunaddr,
+					skb->sk->protinfo.af_unix.addr->name,
+					skb->sk->protinfo.af_unix.addr->len);
 			}
-			else
-				msg->msg_namelen=sizeof(short);
 			sunaddr = NULL;
 		}
 
 		chunk = min(skb->len, size);
+		/* N.B. This could fail with -EFAULT */
 		memcpy_toiovec(msg->msg_iov, skb->data, chunk);
 		copied += chunk;
 		size -= chunk;
diff -u --recursive --new-file v2.1.88/linux/net/unix/garbage.c linux/net/unix/garbage.c
--- v2.1.88/linux/net/unix/garbage.c	Sat Sep 13 11:07:30 1997
+++ linux/net/unix/garbage.c	Sat Feb 28 11:25:08 1998
@@ -44,11 +44,13 @@
 #include <linux/malloc.h>
 #include <linux/skbuff.h>
 #include <linux/netdevice.h>
+#include <linux/file.h>
+#include <linux/proc_fs.h>
+#include <linux/vmalloc.h>
+
 #include <net/sock.h>
 #include <net/tcp.h>
 #include <net/af_unix.h>
-#include <linux/proc_fs.h>
-#include <linux/vmalloc.h>
 #include <net/scm.h>
 
 /* Internal data structures and random procedures: */
@@ -275,7 +277,7 @@
 			 */
 			 
 			if(s->socket && s->socket->file && s->socket->file->f_count)
-				close_fp(s->socket->file);
+				fput(s->socket->file);
 		}
 		else
 			s->protinfo.af_unix.marksweep&=~MARKED;	/* unmark everything for next collection */
diff -u --recursive --new-file v2.1.88/linux/net/x25/af_x25.c linux/net/x25/af_x25.c
--- v2.1.88/linux/net/x25/af_x25.c	Thu Feb 12 20:56:15 1998
+++ linux/net/x25/af_x25.c	Fri Mar  6 10:38:12 1998
@@ -714,6 +714,8 @@
 
 	newsk = skb->sk;
 	newsk->pair = NULL;
+	newsk->socket = newsock;
+	newsk->sleep = &newsock->wait;
 	sti();
 
 	/* Now attach up the new socket */
diff -u --recursive --new-file v2.1.88/linux/scripts/lxdialog/checklist.c linux/scripts/lxdialog/checklist.c
--- v2.1.88/linux/scripts/lxdialog/checklist.c	Sat Mar  2 23:22:25 1996
+++ linux/scripts/lxdialog/checklist.c	Mon Feb 23 12:31:02 1998
@@ -188,9 +188,16 @@
     check_x = (list_width - check_x) / 2;
     item_x = check_x + 4;
 
+    if (choice >= list_height) {
+	scroll = choice - list_height + 1;
+	choice -= scroll;
+    }
+
     /* Print the list */
-    for (i = 0; i < max_choice; i++)
-	print_item (list, items[i * 3 + 1], status[i], i, i == choice);
+    for (i = 0; i < max_choice; i++) {
+	print_item (list, items[(scroll+i) * 3 + 1],
+		    status[i+scroll], i, i == choice);
+    }
 
     wnoutrefresh (list);
 
