diff -u --recursive --new-file v1.3.89/linux/CREDITS linux/CREDITS
--- v1.3.89/linux/CREDITS	Mon Apr 15 12:20:16 1996
+++ linux/CREDITS	Tue Apr 16 10:27:27 1996
@@ -202,6 +202,14 @@
 S: Bellevue, Washington 98007
 S: USA
 
+N: Stuart Cheshire
+E: cheshire@cs.stanford.edu
+D: Author of Starmode Radio IP (STRIP) driver
+D: Originator of design for new combined interrupt handlers
+S: William Gates Department
+S: Stanford University
+S: Stanford, California 94305, USA
+
 N: Juan Jose Ciarlante
 E: jjciarla@raiz.uncu.edu.ar
 D: Network driver alias support
@@ -264,10 +272,9 @@
 D: Second extended file system co-designer
 
 N: Terry Dawson
-E: terryd@extro.ucc.su.oz.au
-E: vk2ktj@gw.vk2ktj.ampr.org (Amateur Radio use only)
-D: NET-2-HOWTO author.
-D: RADIOLINUX Amateur Radio software for Linux list collator.
+E: terry@perf.no.itg.telecom.com.au
+E: terry@albert.vk2ktj.ampr.org (Amateur Radio use only)
+D: AX25-HOWTO, HAM-HOWTO, IPX-HOWTO, NET-2-HOWTO
 
 N: Todd J. Derr
 E: tjd@fore.com
diff -u --recursive --new-file v1.3.89/linux/Documentation/Changes linux/Documentation/Changes
--- v1.3.89/linux/Documentation/Changes	Mon Apr 15 12:20:16 1996
+++ linux/Documentation/Changes	Tue Apr 16 15:45:02 1996
@@ -1,23 +1,24 @@
 This document contains a list of the latest releases of the most
-important packages for Linux.
+important packages for Linux as well as instructions for newcomers to
+the 1.3.x series of kernels.
 
-The installation information were removed because redundant.
-
-Last updated: 14 Apr 1996.
-Author: Alessandro Sigala (ssigala@globalnet.it).
-
-Section "Upgrading to 1.3.x kernel" contributed by Chris Ricker
+Last updated: Apr 15, 1996.
+Authors: Alessandro Sigala (ssigala@globalnet.it) and Chris Ricker
 (gt1355b@prism.gatech.edu).
 
+Note:  Due to time constraints, Alessandro is getting out of the Changes
+business and I'll be picking up the job.  Be gentle while I get my feet
+wet ;-).
+
 Current Releases
 ****************
 
-- Kernel modules	 Broken: 1.3.57, Exp: 1.3.69f
+- Kernel modules	 Stable: 1.3.57, Exp: 1.3.69f
 - PPP daemon		 Stable: 2.2.0e, Exp: 2.2.0f-BETA6
 - Dynamic linker (ld.so) 1.7.14
 - GNU CC		 2.7.2
 - Binutils		 2.6.0.12
-- Linux C Library	 Latest: 5.3.9, Stable: 5.2.18
+- Linux C Library	 Stable:  5.2.18, Exp:  5.3.9
 - Linux C++ Library	 2.7.1.4
 - Termcap		 2.0.7
 - Procps		 0.99a
@@ -31,14 +32,14 @@
 Dynamic linker
 ==============
 
-   You may upgrade the dynamic linker to the latest release only to
-solve some bugs.
+   You might upgrade the dynamic linker to the latest release, but only
+to solve some bugs.
 
 SysVinit
 ========
 
-   The FIFO behavior is changed in latest 1.3.x kernel releases. You may
-upgrade to 2.60 if the older version breaks.
+   The FIFO behavior is changed in the latest 1.3.x kernel releases.
+Upgrade to 2.60 if the older version seems broken.
 
 PPP daemon and utilities
 ========================
@@ -65,12 +66,22 @@
 The Linux C Library
 ===================
 
-   The current Linux C Library release is 5.3.9. In this release there
-are some important changes that may break other programs, then read the
-`release.libc-5.3.9' file carefully!  You need to patch and recompile
-the `make' utility, or get it precompiled from the address written at
-the end of this file.  If you don't want to patch and recompile the
-binaries you may try the release 5.2.18.
+   The latest stable Linux C Library release is 5.2.18.  If you upgrade
+to this from 5.0.9 or earlier, be sure to read the `release.libc-5.2.18'
+file, since GNU make and a few other fairly important utils can be
+broken by the upgrade.
+
+   The current (beta) Linux C Library release is 5.3.9. In this release
+there are some important changes that may cause troubles to buggy
+programs (programs that call free() on a pointer not returned by
+malloc() work with previous libc, but not with this release) then read
+the `release.libc-5.3.9' file carefully!  In the latest libc releases a
+dirent bug, which erroneously defined d->reclen to d->namlen if USE_GNU
+was defined, has been fixed.  Unfortunately, some GNU packages depend on
+this bug.  GNU make 3.xx is one of them.  To fix that you need to patch
+and recompile those programs (a patch for make is included in the file
+`release.libc-.5.3.9', and the address to obtain a precompiled binary
+is at the end of this file).
 
 The Termcap Library
 ===================
@@ -79,68 +90,71 @@
 read the `README' file contained into the package to get some important
 information about the `tgetent' function changes!
 
-Upgrading to 1.3.x kernel
-*************************
+Upgrading to 1.3.x kernel from 1.2.13
+*************************************
 
-   This section was prepared by Chris Ricker and based on material from
-the linux-kernel mailing list, Jared Mauch's web page "Software Victims
-of the 1.3 Kernel Development"
-(http://www2.nether.net/~jared/victim.html), and Axel Boldt's
-(boldt@math.ucsb.edu) Configure.help file, among other sources.
+   This section was based on material from the linux-kernel mailing
+list, Jared Mauch's web page "Software Victims of the 1.3 Kernel
+Development" (http://www2.nether.net/~jared/victim.html), and Axel
+Boldt's (boldt@math.ucsb.edu) Configure.help file, among other sources.
 
    This section is intended primarily to help those that are new to the
 1.3.x series of Linux kernels.  In the ongoing effort to make a faster,
-better kernel and eventually achieve complete world domination, several
-features of the kernel have changed.  As a result, when you first
-upgrade to 1.3.x from 1.2.13, you will also have to upgrade several
-utilities that are closely associated with the kernel.
+better kernel and eventually achieve total world domination, several
+features of the Linux kernel have been improved.  As a result, when you
+first upgrade to 1.3.x from 1.2.13, you will also have to upgrade
+several utilities that are closely associated with the kernel and make
+use of these features.
 
 Proc filesystem
 ===============
 
-   Various changes in the /proc filesystem have been made, affecting ps,
-top, etc.  Running `top' or `ps -auwwx' will now give you a floating
+   Various changes in the /proc filesystem have been made, affecting
+ps, top, etc.  Running `top' or `ps -auwwx' will now give you a floating
 point exception. To fix the problem, upgrade to procps-0.99a.tar.gz,
 available at
-ftp://tsx-11.mit.edu/pub/linux/BETA/procps/procps-0.99a.tar.gz (or else
-don't run top or ps with fancy switches and live with the incorrect
-values ;-).
+ftp://tsx-11.mit.edu/pub/linux/BETA/procps/procps-0.99a.tar.gz.
 
 Modules
 =======
 
    1.3.x is almost completely modularized, and kerneld is now
 incorporated into the kernel.  To take advantage of this, you'll need
-the latest version of the module support apps.  The latest non-beta
-(works fine for me) is modules-1.3.57.tar.gz, and the latest beta is
-modules-1.3.69f.tar.gz.  These should be available at the same place
-you picked up your kernel (ftp://ftp.cc.gatech.edu/pub/linux/kernel/)
-and the home page is http://www.pi.se/blox/modules/index.html.  Note:
-If you try to load a module and get a message like
-
-   gcc2_compiled, undefined Failed to load module!  The symbols from
-kernel 1.3.foo don't match 1.3.foo
-
-   where foo is a number between 1 and 87, then it's time to upgrade
-module utilities from 1.3.57 to 1.3.69f (you'll only get this if you're
-running the latest binutils as well).  Another little tip:  you can't
-have both a.out *and* ELF support compiled as modules.  Otherwise, you
-get a nice Catch-22 when you try to run insmod to install a.out/ELF
-support so you can run insmod ;-).  If you have an all-ELF system, but
-need a.out for the occasional Netscape session, then you can do a.out
-support as a module.  Otherwise, you should probably leave it in the
-kernel.  Similarly, any partitions that you have to mount at startup
-have to have their necessary drivers compiled into the kernel, so don't
-get grandiose ideas about going completely modular and then forget to
-compile ext2fs support into your kernel ;-).
+the latest version of the module support apps.  The latest non-beta is
+modules-1.3.57.tar.gz, and the latest beta is modules-1.3.69f.tar.gz.
+These should be available at the same place you picked up your kernel
+(ftp://ftp.cc.gatech.edu/pub/linux/kernel/) and the home page is
+http://www.pi.se/blox/modules/index.html.  Note:  If you try to load a
+module and get a message like
+
+   `gcc2_compiled, undefined Failed to load module!  The symbols from
+kernel 1.3.foo don't match 1.3.foo'
+
+   where `foo' is a number between 1 and 89, then it's time to upgrade
+module utilities from 1.3.57 to 1.3.69f; you'll only get this error if
+you're running the latest binutils, so most people don't need to
+upgrade.
+
+   Another little tip:  you can't have both a.out *and* ELF support
+compiled as modules.  Otherwise, you get a nice Catch-22 when you try
+to run insmod to install a.out/ELF support so you can run insmod ;-).
+If you have an all-ELF system, but need a.out for the occasional
+Netscape session, then you can do a.out support as a module.
+Otherwise, you should probably leave it in the kernel, and if you
+haven't gone ELF yet, you can probably say no to ELF support.
+Similarly, any partitions that you have to mount at startup have to
+have their necessary file system and device drivers compiled into the
+kernel, so don't get grandiose ideas about going completely modular and
+then forget to compile ext2fs support and ide drive support into your
+kernel ;-).
 
 PPP driver
 ==========
 
-   The PPP driver was upgraded (and is still in somewhat of a a state of
-flux).  You need to be running a pppd from ppp-2.2.0.tar.gz or greater.
-The latest is 2.2.0e and is available at
-ftp://sunsite.unc.edu/pub/Linux/system/Network/serial/ppp-2.2.0e.tar.gz.
+   You need to be running a pppd from ppp-2.2.0.tar.gz or greater.  The
+latest stable release is 2.2.0e and is available at
+ftp://sunsite.unc.edu/pub/Linux/system/Network/serial/ppp-2.2.0e.tar.gz,
+along with a patch necessary to make it compile.
 
 Named pipes
 ===========
@@ -148,7 +162,7 @@
    Linux's handling of named pipes changed (it now does it The Right Way
 instead of the SunOS way ;-).  This broke some programs that depended
 on the SunOS behavior, most notably SysVinit.  If you're running 2.59
-or earlier, you will probably get a wierd error on shutdown in which
+or earlier, you will probably get a weird error on shutdown in which
 your computer shuts down fine but "INIT: error reading initrequest" or
 words to that effect scroll across your screen hundreds of times.  To
 fix, upgrade to
@@ -173,11 +187,14 @@
 
    ln -s /usr/lib/terminfo/l/linux /usr/lib/terminfo/c/console
 
+   Better yet, just get the latest official Linux termcap from
+ftp://sunsite.unc.edu/pub/Linux/GCC/termcap-2.0.7.tar.gz
+
 Hdparm
 ======
 
    Hdparm has been upgraded to take advantage of the latest features of
-the kernel drivers.  The latest can be found at
+the kernel drivers.  The latest non-beta version can be found at
 ftp://sunsite.unc.edu/pub/Linux/kernel/patches/diskdrives/hdparm-2.7.tar.gz.
 
 IP Accounting
@@ -228,17 +245,19 @@
 ftp://tsx-11.mit.edu/pub/linux/packages/GCC.  This is NOT an error due
 to the kernel, though many people have mistakenly thought it is.  When
 you upgrade to libc-5.3.9, you have to patch make to get it to work.
-All of this is documented in the release notes with libc.
+All of this is documented in the release notes with libc.  Upgrading
+libc can also break xterm support.  If it does, you need to recompile
+xterm.
 
 Loop device
 ===========
 
-   1.3.x kernels include loop device support which lets you mount a file
-as a file system, which can allow for all sorts of cool things like
-encrypted file systems and such.  To use it, you'll need a modified
-version of mount from
-ftp://ftp.win.tue.nl:/pub/linux/util/mount-2.5X.tar.gz and work on
-encrypted file system support can be found in
+   1.3.x kernels include loop device support which lets you mount a
+file as a file system, which can allow for all sorts of cool things
+like encrypted file systems and such.  To use it, you'll need a
+modified version of mount from
+ftp://ftp.win.tue.nl:/pub/linux/util/mount-2.5X.tar.gz and preliminary
+work on encrypted file system support can be found in
 ftp.funet.fi:/pub/OS/Linux/BETA/loop/des.1.tar.gz.
 
 Multiple device
@@ -259,15 +278,14 @@
 Quota
 =====
 
-   Quota support has also been added.  You need to get
-ftp://sunsite.unc.edu/pub/Linux/system/Admin/quota_acct.tar.gz to enable
-quotas.  The version that is currently there does not compile, though I
-am uploading a patched one that will compile as I write this (look for
-quota-acct-modified.tgz).
+   Quota support has also been added.  You need to get quotas-1.51 from
+ftp://ftp.funet.fi/pub/Linux/PEOPLE/Linus/subsystems/quota/all.tar.gz.
+This will compile just fine after you copy its mntent.h over to
+/usr/include/mntent.h.
 
 Please send info about any other packages that 1.3.x "broke" or about
 any new features of 1.3.x that require extra packages for use to Chris
-Ricker (gt1355b@prism.gatech.edu) or me.
+Ricker (gt1355b@prism.gatech.edu).
 
 How to know the version of the installed programs
 *************************************************
@@ -310,6 +328,8 @@
 Installation notes:
 ftp://sunsite.unc.edu/pub/Linux/GCC/release.libc-5.3.9
 
+Patched make sources:
+ftp://sunsite.unc.edu/pub/Linux/devel/make/make-3.74.patched.tar.gz
 Patched make binary:
 ftp://sunsite.unc.edu/pub/Linux/devel/make/make-3.74-direntfix-elf.tgz
 
diff -u --recursive --new-file v1.3.89/linux/Documentation/Configure.help linux/Documentation/Configure.help
--- v1.3.89/linux/Documentation/Configure.help	Mon Apr 15 12:20:16 1996
+++ linux/Documentation/Configure.help	Tue Apr 16 10:27:27 1996
@@ -1241,7 +1241,7 @@
 CONFIG_SCSI_EATA_PIO
   This driver supports all EATA-PIO protocol compliant SCSI Host Adaptors
   like the DPT PM2001 and the PM2012A. EATA-DMA compliant HBAs can also use 
-  this driver but are discuraged from doing so, since this driver only 
+  this driver but are discouraged from doing so, since this driver only 
   supports harddisks and lacks numerous features.
   You might want to have a look at the SCSI-HOWTO, available via ftp 
   (user: anonymous) at sunsite.unc.edu:/pub/Linux/docs/HOWTO.  
@@ -1334,9 +1334,12 @@
   explained in section 3.6 of the SCSI-HOWTO, available via ftp (user:
   anonymous) at sunsite.unc.edu:/pub/Linux/docs/HOWTO. If it doesn't
   work out of the box, you may have to change some settings in
-  drivers/scsi/inn2000.h.  If you want to compile this as a module ( =
-  code which can be inserted in and removed from the running kernel
-  whenever you want), say M here and read Documentation/modules.txt.
+  drivers/scsi/in2000.h.  You may also want to drop in a rewritten,
+  and probably more reliable, driver from John Shifflett, which you
+  can get from ftp://ftp.netcom.com/pub/js/jshiffle/in2000/ .  If you
+  want to compile this as a module ( = code which can be inserted in
+  and removed from the running kernel whenever you want), say M here
+  and read Documentation/modules.txt.
 
 PAS16 SCSI support
 CONFIG_SCSI_PAS16
@@ -1357,7 +1360,7 @@
 
 Seagate ST-02 and Future Domain TMC-8xx SCSI support
 CONFIG_SCSI_SEAGATE
-  These are 8-bit SCSI controller; the ST-01 is also supported by this
+  These are 8-bit SCSI controllers; the ST-01 is also supported by this
   driver.  It is explained in section 3.9 of the SCSI-HOWTO, available
   via ftp (user: anonymous) at
   sunsite.unc.edu:/pub/Linux/docs/HOWTO. If it doesn't work out of the
diff -u --recursive --new-file v1.3.89/linux/Documentation/mandatory.txt linux/Documentation/mandatory.txt
--- v1.3.89/linux/Documentation/mandatory.txt	Fri Apr 12 15:51:44 1996
+++ linux/Documentation/mandatory.txt	Tue Apr 16 10:27:10 1996
@@ -2,7 +2,7 @@
 
 		Andy Walker <andy@lysaker.kvaerner.no>
 
-			   06 April 1996
+			   15 April 1996
 
 
 What is  mandatory locking?
@@ -41,7 +41,8 @@
 granularity.
 
 Note 2: POSIX.1 does not specify any scheme for mandatory locking, despite
-borrowing the fcntl() locking scheme from System V.
+borrowing the fcntl() locking scheme from System V. The mandatory locking
+scheme is defined by the System V Interface Definition (SVID) Version 3.
 
 Marking a file for mandatory locking
 ------------------------------------
@@ -66,25 +67,23 @@
 Generally I have tried to make the most sense out of the behaviour exhibited
 by these three reference systems. There are many anomalies.
 
-Originally I wrote (about SunOS):
- "For one thing, calls to open() for a file fail with EAGAIN if another
-  process holds a mandatory lock on the file. However, processes already
-  holding open file descriptors can carry on using them. Weird!"
-
-Well, all my reference systems do it, so I decided to go with the flow.
-My gut feeling is that only calls to open() and creat() with O_TRUNC should be
-rejected, as these are the only ones that try to modify the file contents as
-part of the open() call.
+All the reference systems reject all calls to open() for a file on which
+another process has outstanding mandatory locks. This is in direct
+contravention of SVID 3, which states that only calls to open() with the
+O_TRUNC flag set should be rejected. The Linux implementation follows the SVID
+definition, which is the "Right Thing", since only calls with O_TRUNC can
+modify the contents of the file.
 
 HP-UX even disallows open() with O_TRUNC for a file with advisory locks, not
-just mandatory locks. That to me contravenes POSIX.1.
+just mandatory locks. That would appear to contravene POSIX.1.
 
 mmap() is another interesting case. All the operating systems mentioned
 prevent mandatory locks from being applied to an mmap()'ed file, but  HP-UX
-also disallows advisory locks for such a file.
+also disallows advisory locks for such a file. SVID actually specifies the
+paranoid HP-UX behaviour.
 
-My opinion is that only MAP_SHARED mappings should be immune from locking, and
-then only from mandatory locks - that is what is currently implemented.
+In my opinion only MAP_SHARED mappings should be immune from locking, and then
+only from mandatory locks - that is what is currently implemented.
 
 SunOS is so hopeless that it doesn't even honour the O_NONBLOCK flag for
 mandatory locks, so reads and writes to locked files always block when they
@@ -113,8 +112,9 @@
    unless a process has opened the file with the O_NONBLOCK flag in which case
    the system call will return immediately with the error status EAGAIN.
 
-4. Calls to open() or creat() on a file that has any mandatory locks owned
-   by other processes will be rejected with the error status EAGAIN.
+4. Calls to open() with O_TRUNC, or to creat(), on a existing file that has
+   any mandatory locks owned by other processes will be rejected with the
+   error status EAGAIN.
 
 5. Attempts to apply a mandatory lock to a file that is memory mapped and
    shared (via mmap() with MAP_SHARED) will be rejected with the error status
diff -u --recursive --new-file v1.3.89/linux/MAINTAINERS linux/MAINTAINERS
--- v1.3.89/linux/MAINTAINERS	Mon Apr 15 12:20:17 1996
+++ linux/MAINTAINERS	Tue Apr 16 10:27:10 1996
@@ -193,6 +193,12 @@
 L:	linux-ppp@vger.rutgers.edu
 S:	Maintained
 
+STARMODE RADIO IP (STRIP) PROTOCOL DRIVER
+P:     Stuart Cheshire
+M:     cheshire@cs.stanford.edu
+W:     http://mosquitonet.Stanford.EDU/strip.html
+S:     Maintained
+
 SMB FILESYSTEM:
 P:	Volker Lendecke
 M:	lendecke@namu01.gwdg.de
diff -u --recursive --new-file v1.3.89/linux/arch/i386/kernel/signal.c linux/arch/i386/kernel/signal.c
--- v1.3.89/linux/arch/i386/kernel/signal.c	Mon Mar 11 09:39:33 1996
+++ linux/arch/i386/kernel/signal.c	Tue Apr 16 16:41:45 1996
@@ -82,55 +82,98 @@
  * Set up a signal frame... Make the stack look the way iBCS2 expects
  * it to look.
  */
-void setup_frame(struct sigaction * sa, unsigned long ** fp, unsigned long eip,
-	struct pt_regs * regs, int signr, unsigned long oldmask)
+static void setup_frame(struct sigaction * sa,
+	struct pt_regs * regs, int signr,
+	unsigned long oldmask)
 {
 	unsigned long * frame;
 
-#define __CODE ((unsigned long)(frame+24))
-#define CODE(x) ((unsigned long *) ((x)+__CODE))
-	frame = *fp;
+	frame = (unsigned long *) regs->esp;
 	if (regs->ss != USER_DS && sa->sa_restorer)
 		frame = (unsigned long *) sa->sa_restorer;
 	frame -= 32;
 	if (verify_area(VERIFY_WRITE,frame,32*4))
 		do_exit(SIGSEGV);
+
 /* set up the "normal" stack seen by the signal handler (iBCS2) */
-	put_fs_long(__CODE,frame);
+#define __CODE ((unsigned long)(frame+24))
+#define CODE(x) ((unsigned long *) ((x)+__CODE))
+	put_user(__CODE,frame);
 	if (current->exec_domain && current->exec_domain->signal_invmap)
-		put_fs_long(current->exec_domain->signal_invmap[signr], frame+1);
+		put_user(current->exec_domain->signal_invmap[signr], frame+1);
 	else
-		put_fs_long(signr, frame+1);
-	put_fs_long(regs->gs, frame+2);
-	put_fs_long(regs->fs, frame+3);
-	put_fs_long(regs->es, frame+4);
-	put_fs_long(regs->ds, frame+5);
-	put_fs_long(regs->edi, frame+6);
-	put_fs_long(regs->esi, frame+7);
-	put_fs_long(regs->ebp, frame+8);
-	put_fs_long((long)*fp, frame+9);
-	put_fs_long(regs->ebx, frame+10);
-	put_fs_long(regs->edx, frame+11);
-	put_fs_long(regs->ecx, frame+12);
-	put_fs_long(regs->eax, frame+13);
-	put_fs_long(current->tss.trap_no, frame+14);
-	put_fs_long(current->tss.error_code, frame+15);
-	put_fs_long(eip, frame+16);
-	put_fs_long(regs->cs, frame+17);
-	put_fs_long(regs->eflags, frame+18);
-	put_fs_long(regs->esp, frame+19);
-	put_fs_long(regs->ss, frame+20);
-	put_fs_long(0,frame+21);		/* 387 state pointer - not implemented*/
+		put_user(signr, frame+1);
+	put_user(regs->gs, frame+2);
+	put_user(regs->fs, frame+3);
+	put_user(regs->es, frame+4);
+	put_user(regs->ds, frame+5);
+	put_user(regs->edi, frame+6);
+	put_user(regs->esi, frame+7);
+	put_user(regs->ebp, frame+8);
+	put_user(regs->esp, frame+9);
+	put_user(regs->ebx, frame+10);
+	put_user(regs->edx, frame+11);
+	put_user(regs->ecx, frame+12);
+	put_user(regs->eax, frame+13);
+	put_user(current->tss.trap_no, frame+14);
+	put_user(current->tss.error_code, frame+15);
+	put_user(regs->eip, frame+16);
+	put_user(regs->cs, frame+17);
+	put_user(regs->eflags, frame+18);
+	put_user(regs->esp, frame+19);
+	put_user(regs->ss, frame+20);
+	put_user(NULL,frame+21);
 /* non-iBCS2 extensions.. */
-	put_fs_long(oldmask, frame+22);
-	put_fs_long(current->tss.cr2, frame+23);
+	put_user(oldmask, frame+22);
+	put_user(current->tss.cr2, frame+23);
 /* set up the return code... */
-	put_fs_long(0x0000b858, CODE(0));	/* popl %eax ; movl $,%eax */
-	put_fs_long(0x80cd0000, CODE(4));	/* int $0x80 */
-	put_fs_long(__NR_sigreturn, CODE(2));
-	*fp = frame;
+	put_user(0x0000b858, CODE(0));	/* popl %eax ; movl $,%eax */
+	put_user(0x80cd0000, CODE(4));	/* int $0x80 */
+	put_user(__NR_sigreturn, CODE(2));
 #undef __CODE
 #undef CODE
+
+	/* Set up registers for signal handler */
+	regs->esp = (unsigned long) frame;
+	regs->eip = (unsigned long) sa->sa_handler;
+	regs->cs = USER_CS; regs->ss = USER_DS;
+	regs->ds = USER_DS; regs->es = USER_DS;
+	regs->gs = USER_DS; regs->fs = USER_DS;
+	regs->eflags &= ~TF_MASK;
+}
+
+/*
+ * OK, we're invoking a handler
+ */	
+static void handle_signal(unsigned long signr, struct sigaction *sa,
+	unsigned long oldmask, struct pt_regs * regs)
+{
+	/* are we from a system call? */
+	if (regs->orig_eax >= 0) {
+		/* If so, check system call restarting.. */
+		switch (regs->eax) {
+			case -ERESTARTNOHAND:
+				regs->eax = -EINTR;
+				break;
+
+			case -ERESTARTSYS:
+				if (!(sa->sa_flags & SA_RESTART)) {
+					regs->eax = -EINTR;
+					break;
+				}
+			/* fallthrough */
+			case -ERESTARTNOINTR:
+				regs->eax = regs->orig_eax;
+				regs->eip -= 2;
+		}
+	}
+
+	/* set up the stack frame */
+	setup_frame(sa, regs, signr, oldmask);
+
+	if (sa->sa_flags & SA_ONESHOT)
+		sa->sa_handler = NULL;
+	current->blocked |= sa->sa_mask;
 }
 
 /*
@@ -145,9 +188,6 @@
 asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs)
 {
 	unsigned long mask = ~current->blocked;
-	unsigned long handler_signal = 0;
-	unsigned long *frame = NULL;
-	unsigned long eip = 0;
 	unsigned long signr;
 	struct sigaction * sa;
 
@@ -219,48 +259,19 @@
 				do_exit(signr);
 			}
 		}
-		/*
-		 * OK, we're invoking a handler
-		 */
-		if (regs->orig_eax >= 0) {
-			if (regs->eax == -ERESTARTNOHAND ||
-			   (regs->eax == -ERESTARTSYS && !(sa->sa_flags & SA_RESTART)))
-				regs->eax = -EINTR;
-		}
-		handler_signal |= 1 << (signr-1);
-		mask &= ~sa->sa_mask;
+		handle_signal(signr, sa, oldmask, regs);
+		return 1;
 	}
-	if (regs->orig_eax >= 0 &&
-	    (regs->eax == -ERESTARTNOHAND ||
-	     regs->eax == -ERESTARTSYS ||
-	     regs->eax == -ERESTARTNOINTR)) {
-		regs->eax = regs->orig_eax;
-		regs->eip -= 2;
-	}
-	if (!handler_signal)		/* no handler will be called - return 0 */
-		return 0;
-	eip = regs->eip;
-	frame = (unsigned long *) regs->esp;
-	signr = 1;
-	sa = current->sig->action;
-	for (mask = 1 ; mask ; sa++,signr++,mask += mask) {
-		if (mask > handler_signal)
-			break;
-		if (!(mask & handler_signal))
-			continue;
-		setup_frame(sa,&frame,eip,regs,signr,oldmask);
-		eip = (unsigned long) sa->sa_handler;
-		if (sa->sa_flags & SA_ONESHOT)
-			sa->sa_handler = NULL;
-		regs->cs = USER_CS; regs->ss = USER_DS;
-		regs->ds = USER_DS; regs->es = USER_DS;
-		regs->gs = USER_DS; regs->fs = USER_DS;
-		current->blocked |= sa->sa_mask;
-		oldmask |= sa->sa_mask;
+
+	/* Did we come from a system call? */
+	if (regs->orig_eax >= 0) {
+		/* Restart the system call - no handlers present */
+		if (regs->eax == -ERESTARTNOHAND ||
+		    regs->eax == -ERESTARTSYS ||
+		    regs->eax == -ERESTARTNOINTR) {
+			regs->eax = regs->orig_eax;
+			regs->eip -= 2;
+		}
 	}
-	regs->esp = (unsigned long) frame;
-	regs->eip = eip;		/* "return" to the first handler */
-	regs->eflags &= ~TF_MASK;
-	current->tss.trap_no = current->tss.error_code = 0;
-	return 1;
+	return 0;
 }
diff -u --recursive --new-file v1.3.89/linux/drivers/block/ll_rw_blk.c linux/drivers/block/ll_rw_blk.c
--- v1.3.89/linux/drivers/block/ll_rw_blk.c	Mon Apr 15 12:20:17 1996
+++ linux/drivers/block/ll_rw_blk.c	Tue Apr 16 15:43:19 1996
@@ -442,39 +442,6 @@
 
 #endif
 
-/*
- * Swap partitions are now read via brw_page.  ll_rw_page is an
- * asynchronous function now --- we must call wait_on_page afterwards
- * if synchronous IO is required.  
- */
-void ll_rw_page(int rw, kdev_t dev, unsigned long page, char * buffer)
-{
-	unsigned int major = MAJOR(dev);
-	int block = page;
-	
-	if (major >= MAX_BLKDEV || !(blk_dev[major].request_fn)) {
-		printk("Trying to read nonexistent block-device %s (%ld)\n",
-		       kdevname(dev), page);
-		return;
-	}
-	switch (rw) {
-		case READ:
-			break;
-		case WRITE:
-			if (is_read_only(dev)) {
-				printk("Can't page to read-only device %s\n",
-					kdevname(dev));
-				return;
-			}
-			break;
-		default:
-			panic("ll_rw_page: bad block dev cmd, must be R/W");
-	}
-	if (set_bit(PG_locked, &mem_map[MAP_NR(buffer)].flags))
-		panic ("ll_rw_page: page already locked");
-	brw_page(rw, (unsigned long) buffer, dev, &block, PAGE_SIZE, 0);
-}
-
 /* This function can be used to request a number of buffers from a block
    device. Currently the only restriction is that all buffers must belong to
    the same device */
diff -u --recursive --new-file v1.3.89/linux/drivers/net/3c59x.c linux/drivers/net/3c59x.c
--- v1.3.89/linux/drivers/net/3c59x.c	Fri Apr 12 15:51:54 1996
+++ linux/drivers/net/3c59x.c	Tue Apr 16 15:37:55 1996
@@ -692,7 +692,7 @@
 			dev->tbusy = 0;
 		} else
 			/* Interrupt us when the FIFO has room for max-sized packet. */
-			outw(SetTxThreshold + 1536, ioaddr + EL3_CMD);
+			outw(SetTxThreshold + (1536>>2), ioaddr + EL3_CMD);
 	}
 #else
 	/* ... and the packet rounded to a doubleword. */
@@ -702,7 +702,7 @@
 		dev->tbusy = 0;
 	} else
 		/* Interrupt us when the FIFO has room for max-sized packet. */
-		outw(SetTxThreshold + 1536, ioaddr + EL3_CMD);
+		outw(SetTxThreshold + (1536>>2), ioaddr + EL3_CMD);
 #endif  /* bus master */
 
 	dev->trans_start = jiffies;
diff -u --recursive --new-file v1.3.89/linux/drivers/net/dlci.c linux/drivers/net/dlci.c
--- v1.3.89/linux/drivers/net/dlci.c	Mon Apr 15 12:20:18 1996
+++ linux/drivers/net/dlci.c	Tue Apr 16 07:59:04 1996
@@ -626,7 +626,7 @@
 }
 
 #ifdef MODULE
-static struct device dlci = {devname, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, dlci_init, };
+static struct device dlci = {"dlci", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, dlci_init, };
 
 int init_module(void)
 {
diff -u --recursive --new-file v1.3.89/linux/drivers/net/eexpress.c linux/drivers/net/eexpress.c
--- v1.3.89/linux/drivers/net/eexpress.c	Fri Apr 12 15:51:55 1996
+++ linux/drivers/net/eexpress.c	Tue Apr 16 10:31:11 1996
@@ -1,31 +1,12 @@
-/*
- * eexpress2.c: Intel EtherExpress device driver for Linux
+/* $Id: eexpress.c,v 1.12 1996/04/15 17:27:30 phil Exp $
+ *
+ * Intel EtherExpress device driver for Linux
  *
  * Original version written 1993 by Donald Becker
  * Modularized by Pauline Middelink <middelin@polyware.iaf.nl>
  * Changed to support io= irq= by Alan Cox <Alan.Cox@linux.org>
  * Reworked 1995 by John Sullivan <js10039@cam.ac.uk>
- *
- *  06mar96 Philip Blundell <pjb27@cam.ac.uk>
- *     - move started, buffer sizes, and so on into private data area.
- *     - fix module loading for multiple cards
- * 
- *  31jan96 Philip Blundell <pjb27@cam.ac.uk>
- *     - Tidy up
- *     - Some debugging.  Now works with 1.3 kernels.
- *
- *     Still to do:
- *     - rationalise debugging
- *     - fix detect/autoprobe and module routines
- *     - test under high load, try to chase CU lockups
- *     - look at RAM size check
- *
- * ToDo:
- *   Multicast/Promiscuous mode handling
- *   Put back debug reporting?
- *   More documentation
- *   Some worry about whether statistics are reported accurately
- *
+ * More fixes by Philip Blundell <pjb27@cam.ac.uk>
  */
 
 /*
@@ -76,6 +57,9 @@
  * occasionally (or buy a new NIC). By the way, this looks like a 
  * definite card bug, since Intel's own driver for DOS does exactly the
  * same.
+ *
+ * This bug makes switching in and out of promiscuous mode a risky
+ * business, since we must do a 586 reset each time.
  */
 
 /*
@@ -101,8 +85,8 @@
  */
 
 static char version[] = 
-"eexpress.c: v0.07 1/19/94 Donald Becker <becker@super.org>\n"
-"            v0.10 4th May 1995 John Sullivan <js10039@cam.ac.uk>\n";
+"eexpress.c: v0.10 04-May-95 John Sullivan <js10039@cam.ac.uk>\n"
+"            v0.13 10-Apr-96 Philip Blundell <phil@tazenda.demon.co.uk>\n";
 
 #include <linux/module.h>
 
@@ -141,8 +125,6 @@
  *	7		Report function entry/exit
  */
 
-#undef NET_DEBUG
-
 #ifndef NET_DEBUG
 #define NET_DEBUG 4
 #endif
@@ -171,6 +153,7 @@
 	unsigned short tx_link;         /* last known-executing tx buf */
 	unsigned short last_tx_restart; /* set to tx_link when we restart the CU */
 	unsigned char started;
+	unsigned char promisc;
 	unsigned short rx_buf_start;
 	unsigned short rx_buf_end;
 	unsigned short num_tx_bufs;
@@ -230,6 +213,7 @@
 static int                     eexp_xmit (struct sk_buff *buf, struct device *dev);
 
 static void                    eexp_irq  (int irq, void *dev_addr, struct pt_regs *regs);
+static void                    eexp_set_multicast(struct device *dev);
 
 /*
  * Prototypes for hardware access functions
@@ -245,7 +229,6 @@
 
 static void           eexp_hw_txinit    (struct device *dev);
 static void           eexp_hw_rxinit    (struct device *dev);
-static void           eexp_hw_rxmap     (struct device *dev,unsigned short rx_buf);
 
 static void           eexp_hw_init586   (struct device *dev);
 static void           eexp_hw_ASICrst   (struct device *dev);
@@ -294,7 +277,7 @@
 	unsigned short ioaddr = dev->base_addr;
 
 #if NET_DEBUG > 6
-	printk("%s: eexp_open()\n", dev->name);
+	printk(KERN_DEBUG "%s: eexp_open()\n", dev->name);
 #endif
 
 	if (!irq || !irqrmap[irq]) 
@@ -311,6 +294,10 @@
 	dev->interrupt = 0;
 	eexp_hw_init586(dev);
 	dev->start = 1;
+	MOD_INC_USE_COUNT;
+#if NET_DEBUG > 6
+	printk(KERN_DEBUG "%s: leaving eexp_open()\n", dev->name);
+#endif
 	return 0;
 }
 
@@ -334,6 +321,7 @@
 	irq2dev_map[irq] = NULL;
 	outb(i586_RST,ioaddr+EEPROM_Ctrl);
 	release_region(ioaddr,16);
+	MOD_DEC_USE_COUNT;
 	return 0;
 }
 
@@ -365,7 +353,7 @@
 	unsigned short ioaddr = dev->base_addr;
 
 #if NET_DEBUG > 6
-	printk("%s: eexp_xmit()\n", dev->name);
+	printk(KERN_DEBUG "%s: eexp_xmit()\n", dev->name);
 #endif
 
 	outb(SIRQ_dis|irqrmap[dev->irq],ioaddr+SET_IRQ);
@@ -382,7 +370,7 @@
 				if (lp->tx_link==lp->last_tx_restart) 
 				{
 					unsigned short boguscount=200,rsst;
-					printk("%s: Retransmit timed out, status %04x, resetting...\n",
+					printk(KERN_WARNING "%s: Retransmit timed out, status %04x, resetting...\n",
 						dev->name,inw(ioaddr+SCB_STATUS));
 					eexp_hw_txinit(dev);
 					lp->last_tx_restart = 0;
@@ -395,7 +383,7 @@
 						if (!--boguscount) 
 						{
 							boguscount=200;
-							printk("%s: Reset timed out status %04x, retrying...\n",
+							printk(KERN_WARNING "%s: Reset timed out status %04x, retrying...\n",
 								dev->name,rsst);
 							outw(lp->tx_link,ioaddr+SCB_CBL);
 							outw(0,ioaddr+SCB_STATUS);
@@ -412,7 +400,7 @@
 					if (SCB_CUdead(status)) 
 					{
 						unsigned short txstatus = eexp_hw_lasttxstat(dev);
-						printk("%s: Transmit timed out, CU not active status %04x %04x, restarting...\n",
+						printk(KERN_WARNING "%s: Transmit timed out, CU not active status %04x %04x, restarting...\n",
 							dev->name, status, txstatus);
 						eexp_hw_txrestart(dev);
 					}
@@ -421,7 +409,7 @@
 						unsigned short txstatus = eexp_hw_lasttxstat(dev);
 						if (dev->tbusy && !txstatus) 
 						{
-							printk("%s: CU wedged, status %04x %04x, resetting...\n",
+							printk(KERN_WARNING "%s: CU wedged, status %04x %04x, resetting...\n",
 								dev->name,status,txstatus);
 							eexp_hw_init586(dev); 
 							dev->tbusy = 0;
@@ -436,7 +424,7 @@
 			if ((jiffies-lp->init_time)>10)
 			{
 				unsigned short status = inw(ioaddr+SCB_STATUS);
-				printk("%s: i82586 startup timed out, status %04x, resetting...\n",
+				printk(KERN_WARNING "%s: i82586 startup timed out, status %04x, resetting...\n",
 					dev->name, status);
 				eexp_hw_init586(dev);
 				dev->tbusy = 0;
@@ -451,7 +439,7 @@
 		unsigned short txstatus = eexp_hw_lasttxstat(dev);
 		if (SCB_CUdead(status)) 
 		{
-			printk("%s: CU has died! status %04x %04x, attempting to restart...\n",
+			printk(KERN_WARNING "%s: CU has died! status %04x %04x, attempting to restart...\n",
 				dev->name, status, txstatus);
 			lp->stats.tx_errors++;
 			eexp_hw_txrestart(dev);
@@ -463,7 +451,6 @@
 
 	if (set_bit(0,(void *)&dev->tbusy)) 
 	{
-/*    printk("%s: Transmitter busy or access conflict\n",dev->name); */
 		lp->stats.tx_dropped++;
 	}
 	else
@@ -496,12 +483,12 @@
 
 	if (dev==NULL) 
 	{
-		printk("net_interrupt(): irq %d for unknown device caught by EExpress\n",irq);
+		printk(KERN_WARNING "net_interrupt(): irq %d for unknown device caught by EExpress\n",irq);
 		return;
 	}
 
 #if NET_DEBUG > 6
-	printk("%s: interrupt\n", dev->name);
+	printk(KERN_DEBUG "%s: interrupt\n", dev->name);
 #endif
 
 	dev->interrupt = 1; /* should this be reset on exit? */
@@ -517,11 +504,17 @@
 
 	if (PRIV(dev)->started==0 && SCB_complete(status)) 
 	{
-		if (SCB_CUstat(status)==2) 
-			while (SCB_CUstat(inw(ioaddr+SCB_STATUS))==2);
+#if NET_DEBUG > 4
+		printk(KERN_DEBUG "%s: SCBcomplete event received\n", dev->name);
+#endif
+		while (SCB_CUstat(status)==2)
+			status = inw_p(ioaddr+SCB_STATUS);
+#if NET_DEBUG > 4
+                printk(KERN_DEBUG "%s: CU went non-active (status = %08x)\n", dev->name, status);
+#endif
 		PRIV(dev)->started=1;
-		outw(lp->tx_link,ioaddr+SCB_CBL);
-		outw(PRIV(dev)->rx_buf_start,ioaddr+SCB_RFA);
+		outw_p(lp->tx_link,ioaddr+SCB_CBL);
+		outw_p(PRIV(dev)->rx_buf_start,ioaddr+SCB_RFA);
 		ack_cmd |= SCB_CUstart | SCB_RUstart;
 	}
 	else if (PRIV(dev)->started) 
@@ -537,7 +530,7 @@
 
 	if ((PRIV(dev)->started&2)!=0 && SCB_RUstat(status)!=4) 
 	{
-		printk("%s: RU stopped status %04x, restarting...\n",
+		printk(KERN_WARNING "%s: RU stopped status %04x, restarting...\n",
 			dev->name,status);
 		lp->stats.rx_errors++;
 		eexp_hw_rxinit(dev);
@@ -552,6 +545,10 @@
 	outw(old_rp,ioaddr+READ_PTR);
 	outw(old_wp,ioaddr+WRITE_PTR);
 	outb(SIRQ_en|irqrmap[irq],ioaddr+SET_IRQ);
+	dev->interrupt = 0;
+#if NET_DEBUG > 6
+        printk(KERN_DEBUG "%s: leaving eexp_irq()\n", dev->name);
+#endif
 	return;
 }
 
@@ -575,7 +572,7 @@
 	unsigned short boguscount = lp->num_rx_bufs;
 
 #if NET_DEBUG > 6
-	printk("%s: eexp_hw_rx()\n", dev->name);
+	printk(KERN_DEBUG "%s: eexp_hw_rx()\n", dev->name);
 #endif
 
 	while (outw(rx_block,ioaddr+READ_PTR),boguscount--) 
@@ -594,10 +591,9 @@
 			if (rfd_cmd!=0x0000 || pbuf!=rx_block+0x16
 				|| (pkt_len & 0xc000)!=0xc000) 
 			{
-				printk("%s: Rx frame at %04x corrupted, status %04x, cmd %04x, "
+				printk(KERN_WARNING "%s: Rx frame at %04x corrupted, status %04x, cmd %04x, "
 					"next %04x, pbuf %04x, len %04x\n",dev->name,rx_block,
 					status,rfd_cmd,rx_next,pbuf,pkt_len);
-				eexp_hw_rxmap(dev,rx_block);
 				boguscount++;
 				continue;
 			}
@@ -622,7 +618,7 @@
 				skb = dev_alloc_skb(pkt_len+16);
 				if (skb == NULL) 
 				{
-					printk("%s: Memory squeeze, dropping packet\n",dev->name);
+					printk(KERN_WARNING "%s: Memory squeeze, dropping packet\n",dev->name);
 					lp->stats.rx_dropped++;
 					break;
 				}
@@ -793,6 +789,7 @@
 	dev->stop = eexp_close;
 	dev->hard_start_xmit = eexp_xmit;
 	dev->get_stats = eexp_stats;
+	dev->set_multicast_list = &eexp_set_multicast;
 	ether_setup(dev);
 	return 0;
 }
@@ -930,7 +927,7 @@
 			{
 				if (--failcount) 
 				{
-					printk("%s: CU start timed out, status %04x, cmd %04x\n",
+					printk(KERN_WARNING "%s: CU start timed out, status %04x, cmd %04x\n",
 						dev->name, inw(ioaddr+SCB_STATUS), inw(ioaddr+SCB_CMD));
 					outw(lp->tx_link,ioaddr+SCB_CBL);
 					outw(0,ioaddr+SCB_STATUS);
@@ -940,7 +937,7 @@
 				}
 				else
 				{
-					printk("%s: Failed to restart CU, resetting board...\n",dev->name);
+					printk(KERN_WARNING "%s: Failed to restart CU, resetting board...\n",dev->name);
 					eexp_hw_init586(dev);
 					dev->tbusy = 0;
 					mark_bh(NET_BH);
@@ -1046,32 +1043,6 @@
 }
 
 /*
- * This really ought not to be necessary now. Repairs a single
- * damaged receive buffer. If buffer memory is getting bashed
- * enough to call this, we probably have bigger problems that can
- * be fixed here.
- */
-static void eexp_hw_rxmap(struct device *dev, unsigned short rx_buf)
-{
-	struct net_local *lp = (struct net_local *)dev->priv;
-	unsigned short ioaddr = dev->base_addr;
-	unsigned short old_wp = inw(ioaddr+WRITE_PTR);
-
-	outw(rx_buf,ioaddr+WRITE_PTR);
-	outw(0x0000,ioaddr);
-	outw(0x0000,ioaddr);
-	outw((rx_buf==lp->rx_last)?lp->rx_first:(rx_buf+RX_BUF_SIZE),ioaddr);
-	outw(rx_buf+0x16,ioaddr);
-	outsw(ioaddr, rx_words, sizeof(rx_words)>>1);
-	outw(0x8000,ioaddr);
-	outw(-1,ioaddr);
-	outw(rx_buf+0x20,ioaddr);
-	outw(0x0000,ioaddr);
-	outw(0x8000|(RX_BUF_SIZE-0x20),ioaddr);
-	outw(old_wp,ioaddr+WRITE_PTR);
-}
-
-/*
  * Reset the 586, fill memory (including calls to
  * eexp_hw_[(rx)(tx)]init()) unreset, and start
  * the configuration sequence. We don't wait for this
@@ -1085,21 +1056,20 @@
 	struct net_local *lp = (struct net_local *)dev->priv;
 	unsigned short ioaddr = dev->base_addr;
 
+#if NET_DEBUG > 6
+        printk("%s: eexp_hw_init586()\n", dev->name);
+#endif
+
 	PRIV(dev)->started = 0;
 	set_loopback;
 
 	outb(SIRQ_dis|irqrmap[dev->irq],ioaddr+SET_IRQ);
-	outb(i586_RST,ioaddr+EEPROM_Ctrl);
+	outb_p(i586_RST,ioaddr+EEPROM_Ctrl);
 
-	{
-		unsigned short wcnt;
-		wcnt = 0;
-		outw(0,ioaddr+WRITE_PTR);
-		while ((wcnt+=2) != lp->rx_buf_end+12) 
-			outw(0,ioaddr);
-	} 
-  
-	outw(lp->rx_buf_end,ioaddr+WRITE_PTR);
+	outw_p(lp->rx_buf_end,ioaddr+WRITE_PTR);
+	start_code[28] = (dev->flags & IFF_PROMISC)?(start_code[28] | 1):(start_code[28] & ~1);
+	PRIV(dev)->promisc = dev->flags & IFF_PROMISC;
+	/* We may die here */
 	outsw(ioaddr, start_code, sizeof(start_code)>>1);
 	outw(CONF_HW_ADDR,ioaddr+WRITE_PTR);
 	outsw(ioaddr,dev->dev_addr,3);
@@ -1116,14 +1086,14 @@
 		{
 			if (!--rboguscount) 
 			{
-				printk("%s: i82586 reset timed out, kicking...\n",
+				printk(KERN_WARNING "%s: i82586 reset timed out, kicking...\n",
 					dev->name);
 				outw(0,ioaddr+SCB_CMD);
 				outb(0,ioaddr+SIGNAL_CA);
 				rboguscount = 100;
 				if (!--rfailcount) 
 				{
-					printk("%s: i82586 not responding, giving up.\n",
+					printk(KERN_WARNING "%s: i82586 not responding, giving up.\n",
 						dev->name);
 					return;
 				}
@@ -1143,7 +1113,7 @@
 			{
 				if (--ifailcount) 
 				{
-					printk("%s: i82586 initialization timed out, status %04x, cmd %04x\n",
+					printk(KERN_WARNING "%s: i82586 initialization timed out, status %04x, cmd %04x\n",
 						dev->name, inw(ioaddr+SCB_STATUS), inw(ioaddr+SCB_CMD));
 					outw(CONF_LINK,ioaddr+SCB_CBL);
 					outw(0,ioaddr+SCB_STATUS);
@@ -1153,7 +1123,7 @@
 				}
 				else 
 				{
-					printk("%s: Failed to initialize i82586, giving up.\n",dev->name);
+					printk(KERN_WARNING "%s: Failed to initialize i82586, giving up.\n",dev->name);
 					return;
 				}
 			}
@@ -1163,8 +1133,9 @@
 	outb(SIRQ_en|irqrmap[dev->irq],ioaddr+SET_IRQ);
 	clear_loopback;
 	lp->init_time = jiffies;
-	if (PRIV(dev)->started) 
-		printk("%s: Uh? We haven't started yet\n",dev->name);
+#if NET_DEBUG > 6
+        printk("%s: leaving eexp_hw_init586()\n", dev->name);
+#endif
 	return;
 }
 
@@ -1181,9 +1152,6 @@
 
 	outb(SIRQ_dis|irqrmap[dev->irq],ioaddr+SET_IRQ);
 
-	set_loopback;  /* yet more paranoia - since we're resetting the ASIC
-			* that controls this function, how can it possibly work?
-			*/
 	PRIV(dev)->started = 0;
 	outb(ASIC_RST|i586_RST,ioaddr+EEPROM_Ctrl);
 	while (succount<20) 
@@ -1211,6 +1179,20 @@
 	outb(i586_RST,ioaddr+EEPROM_Ctrl);
 }
 
+
+/*
+ * Set or clear the multicast filter for this adaptor.
+ * We have to do a complete 586 restart for this to take effect.
+ * At the moment only promiscuous mode is supported.
+ */
+static void
+eexp_set_multicast(struct device *dev)
+{
+	if ((dev->flags & IFF_PROMISC) != PRIV(dev)->promisc)
+		eexp_hw_init586(dev);
+}
+
+
 /*
  * MODULE stuff
  */
@@ -1272,3 +1254,11 @@
 	}
 }
 #endif
+
+/*
+ * Local Variables:
+ *  c-file-style: "linux"
+ *  tab-width: 8
+ *  compile-command: "gcc -D__KERNEL__ -I/discs/bibble/src/linux-1.3.69/include  -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -fno-strength-reduce -pipe -m486 -DCPU=486 -DMODULE  -c 3c505.c"
+ * End:
+ */
diff -u --recursive --new-file v1.3.89/linux/drivers/net/sdla.c linux/drivers/net/sdla.c
--- v1.3.89/linux/drivers/net/sdla.c	Mon Apr 15 12:20:18 1996
+++ linux/drivers/net/sdla.c	Tue Apr 16 10:27:10 1996
@@ -797,7 +797,7 @@
 
       if (i == CONFIG_DLCI_MAX)
       {
-         printk(KERN_NOTICE "%s: Recieved packet from invalid DLCI %i, ignoring.", dev->name, dlci);
+         printk(KERN_NOTICE "%s: Received packet from invalid DLCI %i, ignoring.", dev->name, dlci);
          flp->stats.rx_errors++;
          success = 0;
       }
@@ -876,7 +876,7 @@
 
    if (!flp->initialized)
    {
-      printk(KERN_WARNING "%s: irq %d for unintialiazed device.\n", dev->name, irq);
+      printk(KERN_WARNING "%s: irq %d for uninitialized device.\n", dev->name, irq);
       return;
    }
 
@@ -1176,7 +1176,7 @@
       if (err)
          return(err);
 
-      /* no sense reading if the CPU isnt' started */
+      /* no sense reading if the CPU isn't started */
       if (dev->start)
       {
          size = sizeof(data);
diff -u --recursive --new-file v1.3.89/linux/drivers/net/sk_g16.c linux/drivers/net/sk_g16.c
--- v1.3.89/linux/drivers/net/sk_g16.c	Fri Apr 12 15:51:57 1996
+++ linux/drivers/net/sk_g16.c	Tue Apr 16 07:59:43 1996
@@ -1534,7 +1534,7 @@
 	    if (rmdstat & RX_STP) 
 	    {
 		p->stats.rx_errors++;        /* bad packet received */
-		p->stats.rx_length_errors++; /* packet to long */
+		p->stats.rx_length_errors++; /* packet too long */
 
 		printk("%s: packet too long\n", dev->name);
 	    }
diff -u --recursive --new-file v1.3.89/linux/drivers/scsi/53c7,8xx.c linux/drivers/scsi/53c7,8xx.c
--- v1.3.89/linux/drivers/scsi/53c7,8xx.c	Fri Apr 12 15:51:58 1996
+++ linux/drivers/scsi/53c7,8xx.c	Tue Apr 16 10:28:30 1996
@@ -24,7 +24,7 @@
  * in the text.
  *
  *
- *   OPTION_NOASYNC
+ *   OPTION_NO_ASYNC
  *	Don't negotiate for asynchronous transfers on the first command 
  *	when OPTION_ALWAYS_SYNCHRONOUS is set.  Useful for dain bramaged
  *	devices which do something bad rather than sending a MESSAGE 
diff -u --recursive --new-file v1.3.89/linux/drivers/scsi/BusLogic.c linux/drivers/scsi/BusLogic.c
--- v1.3.89/linux/drivers/scsi/BusLogic.c	Mon Apr 15 12:20:19 1996
+++ linux/drivers/scsi/BusLogic.c	Tue Apr 16 10:27:10 1996
@@ -639,7 +639,7 @@
 	  if (AutoSCSIByte45.ForceBusDeviceScanningOrder)
 	    {
 	      /*
-		Sort the I/O Addresses such that the corrseponding PCI devices
+		Sort the I/O Addresses such that the corresponding PCI devices
 		are in ascending order by Bus Number and Device Number.
 	      */
 	      int LastInterchange = DestinationIndex-1, Bound, j;
diff -u --recursive --new-file v1.3.89/linux/drivers/scsi/ppa.c linux/drivers/scsi/ppa.c
--- v1.3.89/linux/drivers/scsi/ppa.c	Mon Apr  8 19:01:44 1996
+++ linux/drivers/scsi/ppa.c	Tue Apr 16 16:08:21 1996
@@ -6,8 +6,6 @@
         (c) 1995,1996 Grant R. Guenther, grant@torque.net,
                       under the terms of the GNU Public License.
 
-        THIS IS BETA SOFTWARE - PLEASE TAKE ALL NECESSARY PRECAUTIONS
-
 */
 
 /*      This driver was developed without the benefit of any technical
diff -u --recursive --new-file v1.3.89/linux/drivers/scsi/scsi.c linux/drivers/scsi/scsi.c
--- v1.3.89/linux/drivers/scsi/scsi.c	Mon Apr 15 12:20:20 1996
+++ linux/drivers/scsi/scsi.c	Tue Apr 16 10:27:10 1996
@@ -2207,7 +2207,7 @@
      * called, and again when scsi_done completes the command.  To limit
      * the load this routine can cause, we shortcut processing if no clock
      * ticks have occurred since the last time it was called.  This may
-     * cause the computation of least below to be inaccurrate, but it will
+     * cause the computation of least below to be inaccurate, but it will
      * be corrected after the next clock tick.
      */
 
diff -u --recursive --new-file v1.3.89/linux/drivers/scsi/scsi.h linux/drivers/scsi/scsi.h
--- v1.3.89/linux/drivers/scsi/scsi.h	Mon Apr 15 12:20:20 1996
+++ linux/drivers/scsi/scsi.h	Tue Apr 16 10:27:10 1996
@@ -394,7 +394,7 @@
       passes it to the driver's queue command function.  The serial_number
       is cleared when scsi_done is entered indicating that the command has
       been completed.  If a timeout occurs, the serial number at the moment
-      of timeout is copied into serial_number_at_timeout.  By subseuqently
+      of timeout is copied into serial_number_at_timeout.  By subsequently
       comparing the serial_number and serial_number_at_timeout fields
       during abort or reset processing, we can detect whether the command
       has already completed.  This also detects cases where the command has
diff -u --recursive --new-file v1.3.89/linux/drivers/scsi/sd.c linux/drivers/scsi/sd.c
--- v1.3.89/linux/drivers/scsi/sd.c	Mon Apr 15 12:20:20 1996
+++ linux/drivers/scsi/sd.c	Tue Apr 16 16:08:21 1996
@@ -147,7 +147,7 @@
 static void sd_release(struct inode * inode, struct file * file)
 {
     int target;
-    sync_dev(inode->i_rdev);
+    fsync_dev(inode->i_rdev);
     
     target =  DEVICE_NR(inode->i_rdev);
     
diff -u --recursive --new-file v1.3.89/linux/fs/buffer.c linux/fs/buffer.c
--- v1.3.89/linux/fs/buffer.c	Mon Apr 15 12:20:20 1996
+++ linux/fs/buffer.c	Tue Apr 16 15:43:19 1996
@@ -803,6 +803,10 @@
 }
 
 
+/*
+ * A buffer may need to be moved from one buffer list to another
+ * (e.g. in case it is not shared any more). Handle this.
+ */
 void refile_buffer(struct buffer_head * buf)
 {
 	int dispose;
@@ -1088,6 +1092,46 @@
 	return NULL;
 }
 
+/* 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 (clear_bit(PG_decr_after, &page->flags))
+		nr_async_pages--;
+	if (clear_bit(PG_free_after, &page->flags))
+		free_page(page_address(page));
+	if (clear_bit(PG_swap_unlock_after, &page->flags))
+		swap_after_unlock_page(page->swap_unlock_entry);
+}
+
+/* Free all temporary buffers belonging to a page. */
+static inline void free_async_buffers (struct buffer_head * bh)
+{
+	struct buffer_head * tmp;
+	unsigned long flags;
+
+	tmp = bh;
+	save_flags(flags);
+	cli();
+	do {
+		if (!test_bit(BH_FreeOnIO, &tmp->b_state)) {
+			printk ("Whoops: unlock_buffer: "
+				"async IO mismatch on page.\n");
+			restore_flags(flags);
+			return;
+		}
+		tmp->b_next_free = reuse_list;
+		reuse_list = tmp;
+		clear_bit(BH_FreeOnIO, &tmp->b_state);
+		tmp = tmp->b_this_page;
+	} while (tmp != bh);
+	restore_flags(flags);
+}
+
+/*
+ * Start I/O on a page.
+ * This function expects the page to be locked and may return before I/O is complete.
+ * You then have to check page->locked, page->uptodate, and maybe wait on page->wait.
+ */
 int brw_page(int rw, unsigned long address, kdev_t dev, int b[], int size, int bmap)
 {
 	struct buffer_head *bh, *prev, *next, *arr[MAX_BUF_PER_PAGE];
@@ -1095,10 +1139,20 @@
 	struct page *page;
 
 	page = mem_map + MAP_NR(address);
+	if (!PageLocked(page))
+		panic("brw_page: page not locked for I/O");
 	clear_bit(PG_uptodate, &page->flags);
+	/*
+	 * Allocate buffer heads pointing to this page, just for I/O.
+	 * They do _not_ show up in the buffer hash table!
+	 * They are _not_ registered in page->buffers either!
+	 */
 	bh = create_buffers(address, size);
-	if (!bh)
+	if (!bh) {
+		clear_bit(PG_locked, &page->flags);
+		wake_up(&page->wait);
 		return -ENOMEM;
+	}
 	nr = 0;
 	next = bh;
 	do {
@@ -1148,33 +1202,32 @@
 	} while (prev = next, (next = next->b_this_page) != NULL);
 	prev->b_this_page = bh;
 	
-	if (nr)
+	if (nr) {
 		ll_rw_block(rw, nr, arr);
-	else {
-		unsigned long flags;
+		/* The rest of the work is done in mark_buffer_uptodate()
+		 * and unlock_buffer(). */
+	} else {
 		clear_bit(PG_locked, &page->flags);
 		set_bit(PG_uptodate, &page->flags);
 		wake_up(&page->wait);
-		next = bh;
-		save_flags(flags);
-		cli();
-		do {
-			next->b_next_free = reuse_list;
-			reuse_list = next;
-			next = next->b_this_page;
-		} while (next != bh);
-		restore_flags(flags);
+		free_async_buffers(bh);
+		after_unlock_page(page);
 	}
 	++current->maj_flt;
 	return 0;
 }
 
+/*
+ * This is called by end_request() when I/O has completed.
+ */
 void mark_buffer_uptodate(struct buffer_head * bh, int on)
 {
 	if (on) {
 		struct buffer_head *tmp = bh;
 		int page_uptodate = 1;
 		set_bit(BH_Uptodate, &bh->b_state);
+		/* If a page has buffers and all these buffers are uptodate,
+		 * then the page is uptodate. */
 		do {
 			if (!test_bit(BH_Uptodate, &tmp->b_state)) {
 				page_uptodate = 0;
@@ -1188,10 +1241,12 @@
 		clear_bit(BH_Uptodate, &bh->b_state);
 }
 
+/*
+ * This is called by end_request() when I/O has completed.
+ */
 void unlock_buffer(struct buffer_head * bh)
 {
 	struct buffer_head *tmp;
-	unsigned long flags;
 	struct page *page;
 
 	clear_bit(BH_Lock, &bh->b_state);
@@ -1199,6 +1254,7 @@
 
 	if (!test_bit(BH_FreeOnIO, &bh->b_state))
 		return;
+	/* This is a temporary buffer used for page I/O. */
 	page = mem_map + MAP_NR(bh->b_data);
 	if (!PageLocked(page)) {
 		printk ("Whoops: unlock_buffer: "
@@ -1218,31 +1274,11 @@
 		if (test_bit(BH_Lock, &tmp->b_state) || tmp->b_count)
 			return;
 	}
-
-	/* OK, go ahead and complete the async IO on this page. */
-	save_flags(flags);
+	/* OK, the async IO on this page is complete. */
 	clear_bit(PG_locked, &page->flags);
 	wake_up(&page->wait);
-	cli();
-	tmp = bh;
-	do {
-		if (!test_bit(BH_FreeOnIO, &tmp->b_state)) {
-			printk ("Whoops: unlock_buffer: "
-				"async IO mismatch on page.\n");
-			restore_flags(flags);
-			return;
-		}
-		tmp->b_next_free = reuse_list;
-		reuse_list = tmp;
-		clear_bit(BH_FreeOnIO, &tmp->b_state);
-		tmp = tmp->b_this_page;
-	} while (tmp != bh);
-	restore_flags(flags);
-	if (clear_bit(PG_freeafter, &page->flags)) {
-		extern int nr_async_pages;
-		nr_async_pages--;
-		free_page(page_address(page));
-	}
+	free_async_buffers(bh);
+	after_unlock_page(page);
 	wake_up(&buffer_wait);
 }
 
@@ -1262,6 +1298,7 @@
 	address = page_address(page);
 	page->count++;
 	set_bit(PG_locked, &page->flags);
+	set_bit(PG_free_after, &page->flags);
 	
 	i = PAGE_SIZE >> inode->i_sb->s_blocksize_bits;
 	block = page->offset >> inode->i_sb->s_blocksize_bits;
@@ -1275,7 +1312,6 @@
 
 	/* IO start */
 	brw_page(READ, address, inode->i_dev, nr, inode->i_sb->s_blocksize, 1);
-	free_page(address);
 	return 0;
 }
 
diff -u --recursive --new-file v1.3.89/linux/fs/dquot.c linux/fs/dquot.c
--- v1.3.89/linux/fs/dquot.c	Fri Apr 12 15:52:04 1996
+++ linux/fs/dquot.c	Tue Apr 16 07:59:13 1996
@@ -390,7 +390,7 @@
 	   (dquot->dq_curinodes + inodes) > dquot->dq_isoftlimit &&
 	    dquot->dq_itime && CURRENT_TIME >= dquot->dq_itime && !fsuser()) {
                 if (need_print_warning(type, dquot)) {
-			sprintf(quotamessage, "%s: warning, %s file quota exceeded to long.\r\n",
+			sprintf(quotamessage, "%s: warning, %s file quota exceeded too long.\r\n",
 		        	dquot->dq_mnt->mnt_dirname, quotatypes[type]);
 			tty_write_message(current->tty, quotamessage);
 		}
@@ -428,7 +428,7 @@
 	   (dquot->dq_curblocks + blocks) > dquot->dq_bsoftlimit &&
 	    dquot->dq_btime && CURRENT_TIME >= dquot->dq_btime && !fsuser()) {
                 if (need_print_warning(type, dquot)) {
-			sprintf(quotamessage, "%s: write failed, %s disk quota exceeded to long.\r\n",
+			sprintf(quotamessage, "%s: write failed, %s disk quota exceeded too long.\r\n",
 		        	dquot->dq_mnt->mnt_dirname, quotatypes[type]);
 			tty_write_message(current->tty, quotamessage);
 		}
diff -u --recursive --new-file v1.3.89/linux/fs/namei.c linux/fs/namei.c
--- v1.3.89/linux/fs/namei.c	Mon Apr 15 12:20:21 1996
+++ linux/fs/namei.c	Tue Apr 16 10:27:10 1996
@@ -385,14 +385,6 @@
 		iput(dir);
 		return error;
 	}
-	/* SunOS, Solaris 2.x and HPUX all deny open() on
-	 * an existing file with mandatory locks.
-	 */
-	error = locks_verify_locked(inode);
-	if (error) {
-		iput(inode);
-		return error;
-	}
 	error = follow_link(dir,inode,flag,mode,&inode);
 	if (error)
 		return error;
@@ -438,18 +430,14 @@
 			iput(inode);
 			return error;
 		}
-#if 0
 		/*
-		 * In my opinion the mandatory lock check should really be
-		 * here. Only O_TRUNC calls can modify the file contents -
-		 * but none of the commercial OS'es seem to do it this way.
+		 * Refuse to truncate files with mandatory locks held on them
 		 */
 		error = locks_verify_locked(inode);
 		if (error) {
 			iput(inode);
 			return error;
 		}
-#endif
 		if (inode->i_sb && inode->i_sb->dq_op)
 			inode->i_sb->dq_op->initialize(inode, -1);
 			
diff -u --recursive --new-file v1.3.89/linux/fs/nfs/nfsroot.c linux/fs/nfs/nfsroot.c
--- v1.3.89/linux/fs/nfs/nfsroot.c	Fri Apr 12 15:52:04 1996
+++ linux/fs/nfs/nfsroot.c	Tue Apr 16 10:27:09 1996
@@ -47,6 +47,10 @@
  *				from being used (thanks to Leo Spiekman)
  *	Andy Walker	:	Allow to specify the NFS server in nfs_root
  *				without giving a path name
+ *	Swen Th=FCmmler	:	Allow to specify the NFS options in nfs_root
+ *				without giving a path name. Fix BOOTP request
+ *				for domainname (domainname is NIS domain, not
+ *				DNS domain!). Skip dummy devices for BOOTP.
  *
  */
 
@@ -168,6 +172,7 @@
 		if (dev->type < ARPHRD_SLIP &&
 		    dev->family == AF_INET &&
 		    !(dev->flags & (IFF_LOOPBACK | IFF_POINTOPOINT)) &&
+		    (0 != strncmp(dev->name, "dummy", 5)) &&
 		    (!user_dev_name[0] || !strcmp(dev->name, user_dev_name))) {
 			/* First up the interface */
 			old_flags = dev->flags;
@@ -622,7 +627,7 @@
 	*e++ = 12;		/* Host name request */
 	*e++ = 32;
 	e += 32;
-	*e++ = 15;		/* Domain name request */
+	*e++ = 40;		/* NIS Domain name request */
 	*e++ = 32;
 	e += 32;
 	*e++ = 17;		/* Boot path */
@@ -756,7 +761,6 @@
 static void root_do_bootp_ext(u8 *ext)
 {
 	u8 *c;
-	static int got_bootp_domain = 0;
 
 #ifdef NFSROOT_BOOTP_DEBUG
 	printk("BOOTP: Got extension %02x",*ext);
@@ -775,20 +779,9 @@
 				memcpy(&gateway.sin_addr.s_addr, ext+1, 4);
 			break;
 		case 12:	/* Host name */
-			if (root_bootp_string(system_utsname.nodename, ext+1, *ext, __NEW_UTS_LEN)) {
-				c = strchr(system_utsname.nodename, '.');
-				if (c) {
-					*c++ = 0;
-					if (!system_utsname.domainname[0]) {
-						strcpy(system_utsname.domainname, c);
-						got_bootp_domain = 1;
-					}
-				}
-			}
+			root_bootp_string(system_utsname.nodename, ext+1, *ext, __NEW_UTS_LEN);
 			break;
-		case 15:	/* Domain name */
-			if (got_bootp_domain && *ext && ext[1])
-				system_utsname.domainname[0] = '\0';
+		case 40:	/* NIS Domain name */
 			root_bootp_string(system_utsname.domainname, ext+1, *ext, __NEW_UTS_LEN);
 			break;
 		case 17:	/* Root path */
@@ -1094,7 +1087,9 @@
 		printk(KERN_ERR "Root-NFS: Pathname for remote directory too long.\n");
 		return -1;
 	}
-	sprintf(nfs_path, buf, cp);
+	/* update nfs_path with path from nfsroot=... command line parameter */
+	if (*buf)
+		sprintf(nfs_path, buf, cp);
 
 	/* Set some default values */
 	nfs_port          = -1;
diff -u --recursive --new-file v1.3.89/linux/include/linux/mm.h linux/include/linux/mm.h
--- v1.3.89/linux/include/linux/mm.h	Fri Apr 12 15:52:07 1996
+++ linux/include/linux/mm.h	Tue Apr 16 16:43:54 1996
@@ -117,30 +117,31 @@
  */
 typedef struct page {
 	atomic_t count;
+	unsigned dirty:16,
+		 age:8;
 	unsigned flags;	/* atomic flags, some possibly updated asynchronously */
 	struct wait_queue *wait;
 	struct page *next;
-
 	struct page *next_hash;
 	unsigned long offset;
 	struct inode *inode;
-	struct page *write_list;
-
 	struct page *prev;
 	struct page *prev_hash;
 	struct buffer_head * buffers;
-	unsigned dirty:16,
-		 age:8;
+	unsigned long swap_unlock_entry;
+	unsigned long map_nr;	/* page->map_nr == page - mem_map */
 } mem_map_t;
 
 /* Page flag bit values */
-#define PG_locked	 0
-#define PG_error	 1
-#define PG_referenced	 2
-#define PG_uptodate	 3
-#define PG_freeafter	 4
-#define PG_DMA		 5
-#define PG_reserved	31
+#define PG_locked		 0
+#define PG_error		 1
+#define PG_referenced		 2
+#define PG_uptodate		 3
+#define PG_free_after		 4
+#define PG_decr_after		 5
+#define PG_swap_unlock_after	 6
+#define PG_DMA			 7
+#define PG_reserved		31
 
 /* Make it prettier to test the above... */
 #define PageLocked(page)	(test_bit(PG_locked, &(page)->flags))
@@ -148,9 +149,78 @@
 #define PageReferenced(page)	(test_bit(PG_referenced, &(page)->flags))
 #define PageDirty(page)		(test_bit(PG_dirty, &(page)->flags))
 #define PageUptodate(page)	(test_bit(PG_uptodate, &(page)->flags))
-#define PageFreeafter(page)	(test_bit(PG_freeafter, &(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 PageReserved(page)	(test_bit(PG_reserved, &(page)->flags))
+
+/*
+ * page->reserved denotes a page which must never be accessed (which
+ * may not even be present).
+ *
+ * page->dma is set for those pages which lie in the range of
+ * physical addresses capable of carrying DMA transfers.
+ *
+ * Multiple processes may "see" the same page. E.g. for untouched
+ * mappings of /dev/null, all processes see the same page full of
+ * zeroes, and text pages of executables and shared libraries have
+ * only one copy in memory, at most, normally.
+ *
+ * For the non-reserved pages, page->count denotes a reference count.
+ *   page->count == 0 means the page is free.
+ *   page->count == 1 means the page is used for exactly one purpose
+ *   (e.g. a private data page of one process).
+ *
+ * A page may be used for kmalloc() or anyone else who does a
+ * get_free_page(). In this case the page->count is at least 1, and
+ * all other fields are unused but should be 0 or NULL. The
+ * managament of this page is the responsibility of the one who uses
+ * it.
+ *
+ * The other pages (we may call them "process pages") are completely
+ * managed by the Linux memory manager: I/O, buffers, swapping etc.
+ * The following discussion applies only to them.
+ *
+ * A page may belong to an inode's memory mapping. In this case,
+ * page->inode is the inode, and page->offset is the file offset
+ * of the page (not necessarily a multiple of PAGE_SIZE).
+ *
+ * A page may have buffers allocated to it. In this case,
+ * page->buffers is a circular list of these buffer heads. Else,
+ * page->buffers == NULL.
+ *
+ * For pages belonging to inodes, the page->count is the number of
+ * attaches, plus 1 if buffers are allocated to the page.
+ *
+ * All pages belonging to an inode make up a doubly linked list
+ * inode->i_pages, using the fields page->next and page->prev. (These
+ * fields are also used for freelist management when page->count==0.)
+ * There is also a hash table mapping (inode,offset) to the page
+ * in memory if present. The lists for this hash table use the fields
+ * page->next_hash and page->prev_hash.
+ *
+ * All process pages can do I/O:
+ * - inode pages may need to be read from disk,
+ * - inode pages which have been modified and are MAP_SHARED may need
+ *   to be written to disk,
+ * - private pages which have been modified may need to be swapped out
+ *   to swap space and (later) to be read back into memory.
+ * During disk I/O, page->locked is true. This bit is set before I/O
+ * and reset when I/O completes. page->wait is a wait queue of all
+ * tasks waiting for the I/O on this page to complete.
+ * page->uptodate tells whether the page's contents is valid.
+ * When a read completes, the page becomes uptodate, unless a disk I/O
+ * error happened.
+ * When a write completes, and page->free_after is true, the page is
+ * freed without any further delay.
+ *
+ * For choosing which pages to swap out, inode pages carry a
+ * page->referenced bit, which is set any time the system accesses
+ * that page through the (inode,offset) hash table.
+ * There is also the page->age counter, which implements a linear
+ * decay (why not an exponential decay?), see swapctl.h.
+ */
 
 extern mem_map_t * mem_map;
 
diff -u --recursive --new-file v1.3.89/linux/include/linux/pagemap.h linux/include/linux/pagemap.h
--- v1.3.89/linux/include/linux/pagemap.h	Fri Apr 12 15:52:08 1996
+++ linux/include/linux/pagemap.h	Tue Apr 16 16:43:54 1996
@@ -14,7 +14,7 @@
 
 static inline unsigned long page_address(struct page * page)
 {
-	return PAGE_OFFSET + PAGE_SIZE*(page - mem_map);
+	return PAGE_OFFSET + PAGE_SIZE * page->map_nr;
 }
 
 #define PAGE_HASH_BITS 10
@@ -22,7 +22,7 @@
 
 #define PAGE_AGE_VALUE 16
 
-extern unsigned long page_cache_size;
+extern unsigned long page_cache_size; /* # of pages currently in the hash table */
 extern struct page * page_hash_table[PAGE_HASH_SIZE];
 
 /*
@@ -33,7 +33,7 @@
  */
 static inline unsigned long _page_hashfn(struct inode * inode, unsigned long offset)
 {
-#define i (((unsigned long) inode)/sizeof(unsigned long))
+#define i (((unsigned long) inode)/(sizeof(struct inode) & ~ (sizeof(struct inode) - 1)))
 #define o (offset >> PAGE_SHIFT)
 #define s(x) ((x)+((x)>>PAGE_HASH_BITS))
 	return s(i+o) & (PAGE_HASH_SIZE-1);
diff -u --recursive --new-file v1.3.89/linux/include/linux/swap.h linux/include/linux/swap.h
--- v1.3.89/linux/include/linux/swap.h	Wed Mar 13 15:18:33 1996
+++ linux/include/linux/swap.h	Tue Apr 16 15:43:19 1996
@@ -54,6 +54,7 @@
 	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);
 
 /* linux/mm/page_alloc.c */
 extern void swap_in(struct task_struct *, struct vm_area_struct *,
diff -u --recursive --new-file v1.3.89/linux/init/main.c linux/init/main.c
--- v1.3.89/linux/init/main.c	Fri Apr 12 15:52:09 1996
+++ linux/init/main.c	Tue Apr 16 10:27:09 1996
@@ -521,7 +521,7 @@
 			int n;
 			line += 8;
 			ROOT_DEV = MKDEV(UNNAMED_MAJOR, 255);
-			if (line[0] == '/' || (line[0] >= '0' && line[0] <= '9')) {
+			if (line[0] == '/' || line[0] == ',' || (line[0] >= '0' && line[0] <= '9')) {
 				strncpy(nfs_root_name, line, sizeof(nfs_root_name));
 				nfs_root_name[sizeof(nfs_root_name)-1] = '\0';
 				continue;
diff -u --recursive --new-file v1.3.89/linux/ipc/shm.c linux/ipc/shm.c
--- v1.3.89/linux/ipc/shm.c	Fri Apr 12 15:52:09 1996
+++ linux/ipc/shm.c	Tue Apr 16 15:43:20 1996
@@ -421,6 +421,7 @@
 	pmd_t *page_middle;
 	pte_t *page_table;
 	unsigned long tmp, shm_sgn;
+	int error;
 
 	/* clear old mappings */
 	do_munmap(shmd->vm_start, shmd->vm_end - shmd->vm_start);
@@ -431,6 +432,7 @@
 	merge_segments(current, shmd->vm_start, shmd->vm_end);
 
 	/* map page range */
+	error = 0;
 	shm_sgn = shmd->vm_pte +
 	  SWP_ENTRY(0, (shmd->vm_offset >> PAGE_SHIFT) << SHM_IDX_SHIFT);
 	flush_cache_range(shmd->vm_mm, shmd->vm_start, shmd->vm_end);
@@ -440,11 +442,15 @@
 	{
 		page_dir = pgd_offset(shmd->vm_mm,tmp);
 		page_middle = pmd_alloc(page_dir,tmp);
-		if (!page_middle)
-			return -ENOMEM;
+		if (!page_middle) {
+			error = -ENOMEM;
+			break;
+		}
 		page_table = pte_alloc(page_middle,tmp);
-		if (!page_table)
-			return -ENOMEM;
+		if (!page_table) {
+			error = -ENOMEM;
+			break;
+		}
 		set_pte(page_table, __pte(shm_sgn));
 	}
 	flush_tlb_range(shmd->vm_mm, shmd->vm_start, shmd->vm_end);
@@ -712,7 +718,7 @@
 	pte_val(page) = shp->shm_pages[idx];
 	if (!pte_present(page))
 		goto check_table;
-	if (dma && !PageDMA(MAP_NR(pte_page(page)) + mem_map))
+	if (dma && !PageDMA(&mem_map[MAP_NR(pte_page(page))]))
 		goto check_table;
 	swap_attempts++;
 
diff -u --recursive --new-file v1.3.89/linux/kernel/sys.c linux/kernel/sys.c
--- v1.3.89/linux/kernel/sys.c	Fri Apr 12 15:52:10 1996
+++ linux/kernel/sys.c	Tue Apr 16 17:21:57 1996
@@ -246,7 +246,7 @@
 		    (current->egid == egid) ||
 		    (current->sgid == egid) ||
 		    suser())
-			current->egid = egid;
+			current->fsgid = current->egid = egid;
 		else {
 			current->gid = old_rgid;
 			return(-EPERM);
@@ -455,7 +455,7 @@
 		    (current->euid == euid) ||
 		    (current->suid == euid) ||
 		    suser())
-			current->euid = euid;
+			current->fsuid = current->euid = euid;
 		else {
 			current->uid = old_ruid;
 			return(-EPERM);
diff -u --recursive --new-file v1.3.89/linux/mm/filemap.c linux/mm/filemap.c
--- v1.3.89/linux/mm/filemap.c	Mon Apr 15 12:20:21 1996
+++ linux/mm/filemap.c	Tue Apr 16 10:27:10 1996
@@ -464,7 +464,7 @@
  * Will try asynchronous read-ahead.
  * Double the max read ahead size each time.
  *   That heuristic avoid to do some large IO for files that are not really
- *   accessed sequentialy.
+ *   accessed sequentially.
  */
 	} else {
 		try_async = 1;
diff -u --recursive --new-file v1.3.89/linux/mm/memory.c linux/mm/memory.c
--- v1.3.89/linux/mm/memory.c	Mon Apr 15 12:20:22 1996
+++ linux/mm/memory.c	Tue Apr 16 15:43:19 1996
@@ -569,7 +569,7 @@
 		return 0;
 	}
 	set_pte(pte, pte_mkwrite(pte_mkdirty(mk_pte(page, PAGE_COPY))));
-/* no need for invalidate */
+/* no need for flush_tlb */
 	return page;
 }
 
diff -u --recursive --new-file v1.3.89/linux/mm/page_alloc.c linux/mm/page_alloc.c
--- v1.3.89/linux/mm/page_alloc.c	Fri Apr 12 15:52:10 1996
+++ linux/mm/page_alloc.c	Tue Apr 16 15:43:19 1996
@@ -37,7 +37,7 @@
 
 struct free_area_struct {
 	struct page list;
-	unsigned int *  map;
+	unsigned int * map;
 };
 
 static struct free_area_struct free_area[NR_MEM_LISTS];
@@ -143,7 +143,7 @@
 	do { struct page *prev = &area->list, *ret; \
 		while (&area->list != (ret = prev->next)) { \
 			if (!dma || CAN_DMA(ret)) { \
-				unsigned long map_nr = ret - mem_map; \
+				unsigned long map_nr = ret->map_nr; \
 				(prev->next = ret->next)->prev = prev; \
 				MARK_USED(map_nr, new_order, area); \
 				nr_free_pages -= 1 << order; \
@@ -263,6 +263,7 @@
 	do {
 		--p;
 		p->flags = (1 << PG_DMA) | (1 << PG_reserved);
+		p->map_nr = p - mem_map;
 	} while (p > mem_map);
 
 	for (i = 0 ; i < NR_MEM_LISTS ; i++) {
@@ -310,6 +311,7 @@
 	vma->vm_mm->rss++;
 	tsk->maj_flt++;
 	if (!write_access && add_to_swap_cache(MAP_NR(page), entry)) {
+		/* keep swap page allocated for the moment (swap cache) */
 		set_pte(page_table, mk_pte(page, vma->vm_page_prot));
 		return;
 	}
diff -u --recursive --new-file v1.3.89/linux/mm/page_io.c linux/mm/page_io.c
--- v1.3.89/linux/mm/page_io.c	Fri Apr 12 15:52:10 1996
+++ linux/mm/page_io.c	Tue Apr 16 15:43:19 1996
@@ -5,6 +5,7 @@
  *
  *  Swap reorganised 29.12.95, 
  *  Asynchronous swapping added 30.12.95. Stephen Tweedie
+ *  Removed race in async swapping. 14.4.1996. Bruno Haible
  */
 
 #include <linux/mm.h>
@@ -28,6 +29,18 @@
 
 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.
+ *
+ * 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.
+ */
 void rw_swap_page(int rw, unsigned long entry, char * buf, int wait)
 {
 	unsigned long type, offset;
@@ -53,6 +66,7 @@
 		printk("Trying to swap to unused swap-device\n");
 		return;
 	}
+	/* Make sure we are the only process doing I/O with this swap page. */
 	while (set_bit(offset,p->swap_lockmap))
 		sleep_on(&lock_queue);
 	if (rw == READ)
@@ -64,12 +78,16 @@
 	if (p->swap_device) {
 		if (!wait) {
 			page->count++;
-			set_bit(PG_freeafter, &page->flags);
+			set_bit(PG_free_after, &page->flags);
+			set_bit(PG_decr_after, &page->flags);
+			set_bit(PG_swap_unlock_after, &page->flags);
+			page->swap_unlock_entry = entry;
 			nr_async_pages++;
 		}
 		ll_rw_page(rw,p->swap_device,offset,buf);
-		if (wait)
-			wait_on_page(page);
+		if (!wait)
+			return;
+		wait_on_page(page);
 	} else if (p->swap_file) {
 		struct inode *swapf = p->swap_file;
 		unsigned int zones[PAGE_SIZE/512];
@@ -113,4 +131,53 @@
 	if (offset && !clear_bit(offset,p->swap_lockmap))
 		printk("rw_swap_page: lock already cleared\n");
 	wake_up(&lock_queue);
+}
+
+/* This is run when asynchronous page I/O has completed. */
+void swap_after_unlock_page (unsigned long entry)
+{
+	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");
+		return;
+	}
+	p = &swap_info[type];
+	offset = SWP_OFFSET(entry);
+	if (offset >= p->max) {
+		printk("swap_after_unlock_page: weirdness\n");
+		return;
+	}
+	if (!clear_bit(offset,p->swap_lockmap))
+		printk("swap_after_unlock_page: lock already cleared\n");
+	wake_up(&lock_queue);
+}
+
+/*
+ * Swap partitions are now read via brw_page.  ll_rw_page is an
+ * asynchronous function now --- we must call wait_on_page afterwards
+ * if synchronous IO is required.  
+ */
+void ll_rw_page(int rw, kdev_t dev, unsigned long page, char * buffer)
+{
+	int block = page;
+
+	switch (rw) {
+		case READ:
+			break;
+		case WRITE:
+			if (is_read_only(dev)) {
+				printk("Can't page to read-only device %s\n",
+					kdevname(dev));
+				return;
+			}
+			break;
+		default:
+			panic("ll_rw_page: bad block dev cmd, must be R/W");
+	}
+	if (set_bit(PG_locked, &mem_map[MAP_NR(buffer)].flags))
+		panic ("ll_rw_page: page already locked");
+	brw_page(rw, (unsigned long) buffer, dev, &block, PAGE_SIZE, 0);
 }
diff -u --recursive --new-file v1.3.89/linux/net/ipv4/ip_masq.c linux/net/ipv4/ip_masq.c
--- v1.3.89/linux/net/ipv4/ip_masq.c	Wed Apr 10 17:02:27 1996
+++ linux/net/ipv4/ip_masq.c	Tue Apr 16 15:33:47 1996
@@ -636,46 +636,58 @@
 static int ip_msqhst_procinfo(char *buffer, char **start, off_t offset,
 			      int length, int unused)
 {
-	off_t pos=0, begin=0;
+	off_t pos=0, begin;
 	struct ip_masq *ms;
 	unsigned long flags;
+	char temp[129];
         int idx = 0;
 	int len=0;
 	
-	len=sprintf(buffer,"Prc FromIP   FPrt ToIP     TPrt Masq Init-seq  Delta PDelta Expires (free=%d,%d)\n",
-                    ip_masq_free_ports[0], ip_masq_free_ports[1]); 
+	if (offset < 128) 
+	{
+		sprintf(temp,
+			"Prc FromIP   FPrt ToIP     TPrt Masq Init-seq  Delta PDelta Expires (free=%d,%d)",
+			ip_masq_free_ports[0], ip_masq_free_ports[1]); 
+		len = sprintf(buffer, "%-127s\n", temp);
+	}
+	pos = 128;
 	save_flags(flags);
 	cli();
         
         for(idx = 0; idx < IP_MASQ_TAB_SIZE; idx++)
         for(ms = ip_masq_m_tab[idx]; ms ; ms = ms->m_link)
 	{
-		int timer_active = del_timer(&ms->timer);
+		int timer_active;
+		pos += 128;
+		if (pos <= offset)
+			continue;
+
+		timer_active = del_timer(&ms->timer);
 		if (!timer_active)
 			ms->timer.expires = jiffies;
-		len+=sprintf(buffer+len,"%s %08lX:%04X %08lX:%04X %04X %08X %6d %6d %lu\n",
+		sprintf(temp,"%s %08lX:%04X %08lX:%04X %04X %08X %6d %6d %7lu",
 			masq_proto_name(ms->protocol),
-			ntohl(ms->saddr),ntohs(ms->sport),
-			ntohl(ms->daddr),ntohs(ms->dport),
+			ntohl(ms->saddr), ntohs(ms->sport),
+			ntohl(ms->daddr), ntohs(ms->dport),
 			ntohs(ms->mport),
-			ms->out_seq.init_seq,ms->out_seq.delta,ms->out_seq.previous_delta,ms->timer.expires-jiffies);
+			ms->out_seq.init_seq,
+			ms->out_seq.delta,
+			ms->out_seq.previous_delta,
+			ms->timer.expires-jiffies);
 		if (timer_active)
 			add_timer(&ms->timer);
+		len += sprintf(buffer+len, "%-127s\n", temp);
 
-		pos=begin+len;
-		if(pos<offset) 
-		{
- 			len=0;
-			begin=pos;
-		}
-		if(pos>offset+length)
-			break;
+		if(len >= length)
+			goto done;
         }
+done:
 	restore_flags(flags);
-	*start=buffer+(offset-begin);
-	len-=(offset-begin);
+	begin = len - (pos - offset);
+	*start = buffer + begin;
+	len -= begin;
 	if(len>length)
-		len=length;
+		len = length;
 	return len;
 }
 
diff -u --recursive --new-file v1.3.89/linux/net/ipv4/tcp_input.c linux/net/ipv4/tcp_input.c
--- v1.3.89/linux/net/ipv4/tcp_input.c	Mon Apr 15 12:20:22 1996
+++ linux/net/ipv4/tcp_input.c	Tue Apr 16 10:27:27 1996
@@ -394,7 +394,7 @@
 	skb_queue_head_init(&newsk->back_log);
 	newsk->rtt = 0;		/*TCP_CONNECT_TIME<<3*/
 	newsk->rto = TCP_TIMEOUT_INIT;
-	newsk->mdev = 0;
+	newsk->mdev = TCP_TIMEOUT_INIT<<1;
 	newsk->max_window = 0;
 	newsk->cong_window = 1;
 	newsk->cong_count = 0;
@@ -598,7 +598,6 @@
 	 *     in shutdown state
 	 * 2 - data from retransmit queue was acked and removed
 	 * 4 - window shrunk or data from retransmit queue was acked and removed
-	 * 8 - we want to do a fast retransmit. One packet only.
 	 */
 
 	if(sk->zapped)
@@ -709,18 +708,52 @@
 	 *	This will allow us to do fast retransmits.
 	 */
 
-	if (sk->rcv_ack_seq == ack && sk->window_seq == window_seq && !(flag&1))
+	/* We are looking for duplicate ACKs here.
+	 * An ACK is a duplicate if:
+	 * (1) it has the same sequence number as the largest number we've seen,
+	 * (2) it has the same window as the last ACK,
+	 * (3) we have outstanding data that has not been ACKed
+	 * (4) The packet was not carrying any data.
+	 * I've tried to order these in occurance of most likely to fail
+	 * to least likely to fail.
+	 * [These are the rules BSD stacks use to determine if an ACK is a
+	 *  duplicate.]
+	 */
+
+	if (sk->rcv_ack_seq == ack
+		&& sk->window_seq == window_seq
+		&& !(flag&1)
+		&& before(ack, sk->sent_seq))
 	{
-		/*
-		 * We only want to short cut this once, many
-		 * ACKs may still come, we'll do a normal transmit
-		 * for these ACKs.
+		/* See draft-stevens-tcpca-spec-01 for explanation
+		 * of what we are doing here.
 		 */
-		if (++sk->rcv_ack_cnt == MAX_DUP_ACKS+1)
-			flag |= 8;	/* flag for a fast retransmit */
+		sk->rcv_ack_cnt++;
+		if (sk->rcv_ack_cnt == MAX_DUP_ACKS+1) {
+			sk->ssthresh = max(sk->cong_window >> 1, 2);
+			sk->cong_window = sk->ssthresh+MAX_DUP_ACKS+1;
+			tcp_do_retransmit(sk,0);
+			/* reduce the count. We don't want to be
+			* seen to be in "retransmit" mode if we
+			* are doing a fast retransmit.
+			*/
+			sk->retransmits--;
+		} else if (sk->rcv_ack_cnt > MAX_DUP_ACKS+1) {
+			sk->cong_window++;
+			/*
+			* At this point we are suppose to transmit a NEW
+			* packet (not retransmit the missing packet,
+			* this would only get us into a retransmit war.)
+			* I think that having just adjusted cong_window
+			* we will transmit the new packet below.
+			*/
+		}
 	}
 	else
 	{
+		if (sk->rcv_ack_cnt > MAX_DUP_ACKS) {
+			sk->cong_window = sk->ssthresh;
+		}
 		sk->window_seq = window_seq;
 		sk->rcv_ack_seq = ack;
 		sk->rcv_ack_cnt = 1;
@@ -1046,8 +1079,7 @@
 	
 	if (((!flag) || (flag&4)) && sk->send_head != NULL &&
 	       (((flag&2) && sk->retransmits) ||
-		(flag&8) ||
-	       (sk->send_head->when + sk->rto < jiffies))) 
+	       (sk->send_head->when + sk->rto < jiffies)))
 	{
 		if(sk->send_head->when + sk->rto < jiffies)
 			tcp_retransmit(sk,0);	
