diff -u --recursive --new-file v1.3.56/linux/Documentation/Configure.help linux/Documentation/Configure.help
--- v1.3.56/linux/Documentation/Configure.help	Sat Jan  6 19:10:37 1996
+++ linux/Documentation/Configure.help	Wed Jan 10 09:27:38 1996
@@ -30,8 +30,8 @@
   Linux can emulate a math coprocessor (used for floating point
   operations) if you don't have one. 486DX and Pentium processors have
   a math coprocessor built in, 486SX and 386 do not, unless you added
-  a 487DX or 387, respectively.  (The messages during boot time can
-  give you some hints here.) Everyone needs either a coprocessor or
+  a 487DX or 387, respectively.  (The messages during boot time can give
+  you some hints here ["man dmesg"]) Everyone needs either a coprocessor or
   this emulation. If you enable this emulation even though you have a
   coprocessor, the coprocessor will be used nevertheless. (This
   behavior can be changed with the kernel command line option
@@ -63,7 +63,12 @@
   write to it and do all the other things that normal block devices can do.
   It is usually used to load and store a copy of a minimal root file
   system off of a floppy into RAM during the initial install of Linux.
-  Most normal users won't need this functionality, and can thus say N here.
+  Note that the kernel command line option "ramdisk=XX" is now obsolete.
+  For details, read Documentation/ramdisk.txt. 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 and read Documentation/modules.txt.
+  Most normal users won't need the RAM disk functionality, and can thus say 
+  N here.
 
 Normal (MFM/RLL) disk and IDE disk/cdrom support
 CONFIG_ST506   		           
@@ -90,7 +95,7 @@
   As you might have guessed, there are now two drivers for IDE
   harddrives around: the old one and the new improved one. The old one
   is not any longer more reliable than the new one. The new driver can
-  also handle IDE/ATAPI CDROM drives (ATAPI = AT Attachment Packet
+  also handle IDE/ATAPI CDROM and tape drives (ATAPI = AT Attachment Packet
   Interface is a new protocol currently used for controlling CDROM and
   tape drives, similar to the SCSI protocol. Some newer CDROM drives
   such as NEC 260 and MITSUMI triple/quad speed drives use it, but
@@ -164,9 +169,10 @@
 
 Generate code for R4x00
 CONFIG_R4X00
-#####
-##### Anyone have details? It's for the MIPS architecture.
-#####
+  If your computer uses the 64 bit R4X00 processor (as opposed to the
+  32 bit R3000), you need to say Y here, otherwise N. Note that these
+  processors are not compatible and the kernel can only work on the
+  processor type it was compiled for.
 
 Networking support
 CONFIG_NET
@@ -180,13 +186,23 @@
 Network aliasing
 CONFIG_NET_ALIAS
   This is for setting several network addresses on the same low-level network
-  device driver. Typically used for services that act different based
-  on the address they listen (eg. Apache httpd) or for connecting to
+  device driver. Typically used for services that act differently based
+  on the address they listen on (e.g. Apache httpd) or for connecting to
   different logical networks through the same physical interface.
   This is the generic part, later when configuring network protocol
   options you will be asked for protocol-specific aliasing support.
-  See net/core/README.alias for more info.
-  If you need this features (for any protocol, like IP) say Y.  
+  See Documentation/networking/alias.txt for more info.
+  If you need this features (for any protocol, like IP) say Y; if unsure, 
+  say N.  
+
+Network firewalls
+CONFIG_FIREWALL
+  A firewall is a computer which protects a local network from the
+  rest of the World: all traffic to and from computers on the local
+  net is inspected by the firewall first. If you want to configure
+  your Linux box as a firewall for a local network, say Y here. If
+  your local network is TCP/IP based, you will have to say Y to "IP:
+  firewalling", below. Chances are that you don't want this, so say N.
 
 Sun floppy controller support
 CONFIG_BLK_DEV_SUNFD
@@ -195,7 +211,7 @@
 
 Alpha system type
 CONFIG_ALPHA_AVANTI
-  Find out what type of Alpha system you are running. If you can't
+  Find out what type of Alpha motherboard you have. If you can't
   find one of the given names, then try "Noname". For this question,
   it suffices to give a unique prefix of the option you want to
   choose. 
@@ -205,7 +221,7 @@
   This is for some buggy motherboards which cannot properly deal with
   the memory above 16MB. If you have more than 16MB of RAM and
   experience weird problems, you might want to try Y, everyone else
-  says N. Note for machines with more that 64MB: in order for the
+  says N. Note for machines with more that 64MB of RAM: in order for the
   kernel to be able to use the memory above 64MB, pass the command
   line option "mem=XXXM" (where XXX is the memory size in
   megabytes) to your kernel. See the documentation of your boot loader
@@ -214,6 +230,12 @@
   (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO.
   You also need at least 512kB of RAM cache if you have more than 64MB
   of RAM.
+  Some other things to try when experiencing seemingly random, "weird"
+  problems: 1) passing the "no-hlt" option to the kernel 2) passing
+  the "no-387" option to the kernel 3) passing the "mem=4M" option to
+  the kernel (will disable all but the first 4M of RAM) 4) disabling
+  the cache from your BIOS settings 5) exchanging RAM chips 6)
+  exchanging the motherboard 7) committing suicide.
 
 Using SRM as bootloader
 CONFIG_ALPHA_SRM
@@ -244,9 +266,9 @@
   without this option). The PCI-HOWTO, available via ftp (user:
   anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO, contains
   valuable information about which PCI hardware works under Linux and
-  which doesn't.  If some of PCI devices don't work and you get a
-  warning during boot time, please follow the instructions at the top of
-  include/linux/pci.h. The buggy PCTech RZ 1000 IDE
+  which doesn't.  If some of your PCI devices don't work and you get a
+  warning during boot time ("man dmesg"), please follow the instructions 
+  at the top of include/linux/pci.h. The buggy PCTech RZ 1000 IDE
   harddrive controller which is used in some PCI systems is detected
   and correctly handled by this driver.
 
@@ -292,8 +314,8 @@
   (this does *not* mean that you will be able to run executables from
   different architectures or operating systems!) and makes building
   run-time libraries very easy. Many new executables are distributed
-  solely in ELF format. You definitely want to say Y here. Information
-  about ELF is on the WWW at http://sable.ox.ac.uk/~jo95004/elf.html
+  solely in ELF format. You definitely want to say Y here. Information about
+  ELF is on the WWW at http://www.sjc.ox.ac.uk/users/barlow/elf-howto.html
   (To browse the WWW, you need to have access to a machine on the
   Internet that has one of the programs lynx, netscape or Mosaic).  If
   you find that after upgrading to Linux kernel 1.3 and saying Y here,
@@ -320,22 +342,16 @@
   kernel in ELF by saying Y here and editing the variables CC
   and LD in the toplevel Makefile.
 
-Use 486-specific optimizations (does NOT work on i386)
-CONFIG_M486
-  If you have a 486 or better, as opposed to a 386, say Y here:
-  things will be slightly faster. However, it is not required: the
-  kernel will run on all CPUs without this option. If you are
-  not sure, say N; This option will make the kernel use some
-  instructions that are only available on 486+ machines.
-
-Use Pentium optimizations
-CONFIG_M586 
-  If you have a 586 or better, as opposed to a 486, or if you have a
-  486 and are very short on memory, say Y here.
-  Things will be slightly faster for a 586, and your kernel will be
-  smaller.  A kernel with this option enabled will still run on a 486,
-  although slightly slower.
-
+Processor type
+CONFIG_M386
+  This is the processor type of your CPU. It is used for optimizing
+  purposes. In order to compile a kernel that can run on all three CPU
+  types (albeit not optimally fast), you can specify "386" here.  If
+  you specify "486" or "Pentium", then the kernel will run on
+  both 486 and Pentium CPUs. In rare cases, it can make sense to
+  specify "Pentium" here even if running a 486: the kernel will be
+  smaller but slower.
+  
 Enable loadable module support
 CONFIG_MODULES
   Kernel modules are small pieces of compiled code which can be 
@@ -344,7 +360,7 @@
   Documentation/modules.txt. Modules can be device drivers, file 
   systems, binary executable formats, and so on. If you think that 
   you may want to make use of modules with this kernel in the future, 
-  then say Y here.
+  then say Y here. If unsure, say Y.
 
 Set version information on all symbols for modules
 CONFIG_MODVERSIONS
@@ -361,6 +377,19 @@
   option is to compile it as a module (PPP is a protocol for sending
   internet traffic over telephone lines). Therefore, N is a safe bet.
 
+Kernel daemon support
+CONFIG_KERNELD
+  Normally when you have seleceted some drivers and/or filesystems
+  to be created as loadable modules, you also have the responsibility
+  to load the corresponding module (via insmod/modprobe) before you
+  use it.  If you select Y here, the kernel will take care of this
+  all by itself, together with a user level daemon; "kerneld".
+  Note that "kerneld" will also automatically unload all unused
+  modules, so you don't have to use "rmmod" either.
+  There are some other "kernel callouts" that will be available
+  later on, such as a user level "beeper" and a generic screen blanker.
+  The "kerneld" daemon is included in "modules-1.2.8" and later.
+
 TCP/IP networking
 CONFIG_INET
   These are the protocols used on the Internet and on most local
@@ -425,10 +454,10 @@
 CONFIG_IP_FIREWALL
   A firewall is a computer which protects a local network from the
   rest of the internet: all traffic to and from computers on the local
-  net is inspected by the firewall first. If you want to enlarge your
-  kernel by about 2kB and configure your Linux box as a firewall for a
-  local network, say Y here. You will need to read the FIREWALL-HOWTO,
-  available via ftp (user: anonymous) in
+  net is inspected by the firewall first. If you want to configure
+  your Linux box as a firewall for a local TCP/IP based network, say Y
+  here. This will enlarge your kernel by about 2kB. You will need to
+  read the FIREWALL-HOWTO, available via ftp (user: anonymous) in
   sunsite.unc.edu:/pub/Linux/docs/HOWTO. Also, you will have to use
   the ipfw tool from the net-tools package, available via ftp (user:
   anonymous) from
@@ -436,10 +465,10 @@
   preferably ipfwadm from ftp.xos.nl:/pub/linux/ipfwadm/. These
   programs allow selective blocking of internet traffic based on type,
   origin and destination.  You need to enable IP firewalling in order
-  to be able to use IP masquerading (i.e. IP traffic from one of the
-  local computers and destined for an outside host is changed by your
-  box so that it appears to come from you). Chances are that you don't
-  want this, so say N.
+  to be able to use IP masquerading (i.e. local computers can chat
+  with an outside host, but that outside host is made to think that it
+  is talking to the firewall box. Makes the local network completely
+  invisible). Chances are that you don't want this, so say N.
 
 IP: accounting
 CONFIG_IP_ACCT
@@ -460,7 +489,7 @@
 
 IP: tunneling
 CONFIG_NET_IPIP
-  Tunneling means to encapsulating data of one protocol type within
+  Tunneling means encapsulating data of one protocol type within
   another protocol and sending it over a channel that understands the
   encapsulating protocol. This particular tunneling driver implements
   encapsulation of IP within IP, which sounds kind of pointless, but
@@ -485,7 +514,7 @@
   box acts as a firewall wants to send something to the outside, your
   box can "masquerade" as that host, i.e. it forwards the traffic to
   the intended destination, but makes it look like it came from the
-  firewall host itself. It works both ways: if the outside host
+  firewall box itself. It works both ways: if the outside host
   answers, the firewall will silently forward the traffic to the
   corresponding local computer. This way, the computers on your local
   net are completely invisible to the outside world, even though they
@@ -505,9 +534,19 @@
 
 IP: aliasing support
 CONFIG_IP_ALIAS
-  You need this to enable IP layer network aliasing. This will also
-  enable ARP resolution for alias devices. If you don't need several
-  IP addresses per interface, answer N.
+  Sometimes it is useful to give several addresses to a single network
+  interface (= serial port or ethernet card). The most common case is
+  that you want to serve different WWW documents to the outside
+  according to which of your host names they used to connect to
+  you. This is explained in detail on the WWW at
+  http://www.thesphere.com/~dlp/TwoServers/ (to browse the WWW, you
+  need to have access to a machine on the Internet that has one of the
+  programs lynx, netscape or Mosaic). Another scenario would be that
+  there are two logical networks living on your local ethernet and you
+  want to access them both with the same ethernet card. The
+  configuration of these alias addresses is done with a special name
+  syntax explained in Documentation/networking/alias.txt. If you want
+  this, say Y. Most people don't need it and say N.
 
 IP: multicast routing(in progress)
 CONFIG_IP_MROUTE
@@ -526,13 +565,15 @@
 
 Reverse ARP
 CONFIG_INET_RARP
-  Since you asked: if there are diskless machines on your network that
-  know their hardware ethernet address but don't know their IP
+  Since you asked: if there are diskless machines on your local network 
+  that know their hardware ethernet address but don't know their IP
   addresses upon startup, they send out a Reverse
   Address Resolution Protocol request to find out their own IP
   addresses. If you want your Linux box to be able to *answer* such
-  requests, say Y here; you'd use the program rarp ("man rarp"). If
-  you want to compile this as a module ( = code which can be inserted
+  requests, say Y here; you'd use the program rarp ("man rarp"). 
+  Superior solutions to the same problem are given by the 
+  protocols BOOTP and DHCP. If you want to compile RARP support 
+  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.  If you don't understand a
   word, say N and rest in peace.
@@ -557,10 +598,10 @@
 CONFIG_TCP_NAGLE_OFF
   The NAGLE algorithm works by requiring an acknowledgment before
   sending small IP frames (= packets).  This keeps tiny telnet and
-  rlogin packets from congesting Wide Area Networks.  Most people strongly
-  recommend to say N here, though, thereby leaving NAGLE enabled. Those
-  programs that benefit by disabling the facility should do it on a per
-  connection basis themselves anyway.
+  rlogin packets from congesting Wide Area Networks.  Most people
+  strongly recommend to say N here, thereby leaving NAGLE
+  enabled. Those programs that would benefit from disabling this
+  facility can do it on a per connection basis themselves.
 
 IP: Drop source routed frames
 CONFIG_IP_NOSR
@@ -581,8 +622,8 @@
   This option can speed up network performance. It works by increasing
   the size of socket buffers, thereby reducing overhead but increasing
   memory usage. Say N if you have less than 16Mb of RAM, otherwise Y.
-  Note for machines with more that 64MB: in order for the kernel to be
-  able to use the memory above 64MB, pass the command line option
+  Note for machines with more that 64MB of RAM: in order for the kernel 
+  to be able to use the memory above 64MB, pass the command line option
   "mem=XXXM" (where XXX is the memory size in megabytes) to your
   kernel. See the documentation of your boot loader (lilo or loadlin)
   about how to pass options to the kernel. The lilo procedure is also
@@ -593,9 +634,10 @@
 The IPX protocol
 CONFIG_IPX
   This is support for the Novell networking protocol, IPX. You need it
-  if you want to access Novell Netware servers by using ncpfs or from
-  within the Linux DOS emulator dosemu (read the DOSEMU-HOWTO,
-  available via ftp (user: anonymous) in
+  if you want to access Novell Netware servers by using the Linux
+  Novell client ncpfs (available via ftp (user: anonymous) from 
+  sunsite.unc.edu:/pub/Linux/system/Filesystem/) or from within the 
+  Linux DOS emulator dosemu (read the DOSEMU-HOWTO, available in
   sunsite.unc.edu:/pub/Linux/docs/HOWTO). To turn your Linux box into
   a fully featured Netware file server and IPX router, say Y here and
   fetch lwared from
@@ -606,8 +648,6 @@
   the programs lynx, netscape or Mosaic). This driver would enlarge
   your kernel by about 5 kB. Unless you have Novell computers on your
   local network, say N.
-  BTW: Although it still doesn't work with this release of the kernel you 
-  can also find ncpfs (a free Novell client) on linux01.gwdg.de.
 
 Appletalk DDP
 CONFIG_ATALK
@@ -668,6 +708,14 @@
   might want to get the very latest 1.3 kernel if you intend to use
   this.
 
+AX.25 over Ethernet
+CONFIG_BPQETHER
+  AX.25 is the protocol used for computer communication over amateur
+  radio. If you say Y here, you will be able to send and receive AX.25
+  traffic over ethernet (also called "BPQ AX.25"), which could be
+  useful if some other computer on your local network has a direct
+  amateur radio connection.
+
 Kernel/User network link driver(ALPHA)
 CONFIG_NETLINK
   This driver allows for two-way communication between certain parts
@@ -696,7 +744,8 @@
   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). If you want to compile it as a
-  module, say M here and read Documentation/modules.txt.
+  module, say M here and read Documentation/modules.txt and
+  Documentation/scsi.txt. 
 
 SCSI disk support
 CONFIG_BLK_DEV_SD
@@ -706,7 +755,7 @@
   CDROMs. This driver is also available as a module ( = code which can
   be inserted in and removed from the running kernel whenever you
   want). If you want to compile it as a module, say M here and read
-  Documentation/modules.txt.
+  Documentation/modules.txt and Documentation/scsi.txt.
   
 SCSI tape support
 CONFIG_CHR_DEV_ST
@@ -716,7 +765,8 @@
   the kernel source. This is NOT for SCSI CDROMs. This driver is also
   available as a module ( = code which can be inserted in and removed
   from the running kernel whenever you want). If you want to compile
-  it as a module, say M here and read Documentation/modules.txt.
+  it as a module, say M here and read Documentation/modules.txt and
+  Documentation/scsi.txt .
 
 SCSI CDROM support
 CONFIG_BLK_DEV_SR
@@ -726,7 +776,8 @@
   ISO9660 filesystem later. This driver is also available as a module
   ( = code which can be inserted in and removed from the running
   kernel whenever you want). If you want to compile it as a module,
-  say M here and read Documentation/modules.txt.
+  say M here and read Documentation/modules.txt and
+  Documentation/scsi.txt .
 
 SCSI generic support
 CONFIG_CHR_DEV_SG
@@ -740,7 +791,7 @@
   sunsite.unc.edu:/pub/Linux/docs/HOWTO. If you want to compile this
   as a module ( = code which can be inserted in and removed from the
   running kernel whenever you want), say M here and read
-  Documentation/modules.txt.
+  Documentation/modules.txt and Documentation/scsi.txt.
 
 Probe all LUNs on each SCSI device
 CONFIG_SCSI_MULTI_LUN
@@ -771,7 +822,9 @@
 CONFIG_SCSI_AHA1542
   This is support for an SCSI host adaptor. It is explained in section
   3.4 of the SCSI-HOWTO, available via ftp (user: anonymous) at
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO.  If it doesn't work out of
+  sunsite.unc.edu:/pub/Linux/docs/HOWTO.  Note that Trantor was
+  recently purchased by Adaptec, and some former Trantor products are
+  being sold under the Adaptec name. If it doesn't work out of
   the box, you may have to change some settings in
   drivers/scsi/aha1542.h.  If you want to compile this as a module ( =
   code which can be inserted in and removed from the running kernel
@@ -996,9 +1049,9 @@
 CONFIG_DUMMY
   This is essentially a loopback device (i.e. traffic you send to this
   device is immediately returned back to you) with a configurable IP
-  address different from the usual 127.0.0.1. Can be used to give you
-  more than one IP address or make your currently inactive SLIP
-  address seem like a real address. If you use SLIP or PPP, you might
+  address different from the usual 127.0.0.1. It is most commonly used
+  in order to make your currently inactive SLIP address seem like a
+  real address for local programs. If you use SLIP or PPP, you might
   want to enable it. Read about it in the Network Administrator's
   Guide, available via ftp (user: anonymous) from
   sunsite.unc.edu:/pub/Linux/docs/LDP. Since this thing comes often
@@ -1071,7 +1124,12 @@
   then you cannot compile the PPP driver into the kernel; you can only
   compile it as a module. 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 unsure, say N.
+  Documentation/networking/net-modules.txt. Note that, no matter what
+  you do, the BSD compression code (used to compress the IP packets
+  sent over the serial line; has to be supported at the other end as
+  well) can only be compiled as a module; it is called bsd_comp.o and
+  will show up in the directory modules once you have said "make
+  modules". If unsure, say N.
 
 16 channels instead of 4 
 CONFIG_PPP_LOTS
@@ -1083,7 +1141,7 @@
 CONFIG_SCC
   These cards are used to connect your Linux box to an amateur radio
   and communicate with other computers.  If you want to use this, read
-  drivers/char/README.scc and the HAM-HOWTO, available available via
+  Documentation/networking/z8530drv.txt and the HAM-HOWTO, available via
   ftp (user: anonymous) at sunsite.unc.edu:/pub/Linux/docs/HOWTO.
 
 PLIP (parallel port) support
@@ -1129,12 +1187,13 @@
 Sun LANCE Ethernet support
 CONFIG_SUN_LANCE
   This is support for lance ethernet cards on Sun workstations such as
-  the Sparcstation IPC (any Sparc with an 'le0' under SunOS basically).
+  the Sparcstation IPC (any Sparc with a network interface 'le0' under
+  SunOS basically).
 
 Sun Intel Ethernet support
 CONFIG_SUN_INTEL
   This is support for the intel ethernet cards on some Sun workstations
-  (all those with an ie0 interface under SunOS).
+  (all those with a network interface 'ie0' under SunOS).
 
 Do you want to be offered ALPHA test drivers
 CONFIG_NET_ALPHA
@@ -1426,8 +1485,8 @@
 Ottawa PI and PI/2 support
 CONFIG_PI
   This is a driver for the Ottawa Amateur Radio Club PI and PI2 cards,
-  which are commonly used to send internet traffic over radio. More
-  information about these cards is on the WWW at
+  which are commonly used to send internet traffic over amateur radio. 
+  More information about these cards is on the WWW at
   http://hydra.carleton.ca/info/pi2.html (To browse the WWW, you need
   to have access to a machine on the Internet that has one of the
   programs lynx, netscape or Mosaic). If you have one of these cards,
@@ -1439,10 +1498,10 @@
 Gracilis PackeTwin support
 CONFIG_PT
   This card is similar to the PI card (mentioned above).  It is used mainly
-  by amateur radio operators for packet radio.  You should of already said Y
-  to "AX.25 support" as this card uses that protocol. 
-  Other than the code and the PT user documentation, there is no other
-  information on this card.
+  by amateur radio operators for packet radio.  You should have already 
+  said Y to "AX.25 support" as this card uses that protocol. 
+  More information about this driver can be found in the file 
+  drivers/net/README.pt. 
   NOTE: The card is capable of DMA and full duplex but neither of these have
   been coded in the driver as yet.
 
@@ -1738,7 +1797,7 @@
   This driver supports most of the drives which use the Panasonic or
   SoundBlaster interface.
   The Matsushita CR-521, CR-522, CR-523, CR-562, CR-563 drives (sometimes
-  labelled "Creative"), the CreativeLabs CD200, the Longshine LCS-7260,
+  labeled "Creative"), the CreativeLabs CD200, the Longshine LCS-7260,
   the "IBM External ISA CDROM" (in fact a CR-56x model), the TEAC CD-55A
   fall under this category. Some other "electrically compatible" drives
   (Vertos, Genoa, some Funai models) are currently not supported; for the
@@ -1795,6 +1854,16 @@
 CONFIG_SJCD
   If this is your CDROM drive, say Y here.
 
+Quota support
+CONFIG_QUOTA
+  If you say Y here, you will be able to set per user limits for disk
+  usage (also called diskquotas). Currently, it works only for the
+  ext2 filesystem; you need the software available via ftp (user:
+  anonymous) from
+  sunsite.unc.edu:/pub/Linux/systm/Admin/quota_acct.tar.gz in order to
+  use it. Obviously, this is only useful for multi user systems. If
+  unsure, say N.
+
 Standard (minix) fs support
 CONFIG_MINIX_FS
   Minix is a simple operating system used in many classes about
@@ -1806,7 +1875,7 @@
   built-in restrictions. This option will enlarge your kernel by about
   25 kB. Everyone should say Y or M so that they are able to read this
   common floppy format.  If you want to compile this as a module
-  however ( = code which can be inserted in and removed from the
+  ( = code which can be inserted in and removed from the
   running kernel whenever you want), say M here and read
   Documentation/modules.txt. Note that the filesystem of your root
   partition cannot be compiled as a module.
@@ -1946,7 +2015,9 @@
 
 ISO9660 cdrom filesystem support
 CONFIG_ISO9660_FS
-  If you have a CDROM and want to do more with it than just listen to
+  This is the standard filesystem used on CDROMs. It was previously known 
+  as "High Sierra Filesystem" and is called "hsfs" on other Unix systems. 
+  If you have a CDROM drive and want to do more with it than just listen to
   audio CDs and watch its LEDs, say Y (and read the CDROM-HOWTO,
   available via ftp (user: anonymous) from
   sunsite.unc.edu:/pub/Linux/docs/HOWTO), thereby enlarging your
@@ -2171,24 +2242,20 @@
 
 QIC-117 tape support
 CONFIG_FTAPE
-  Most tape drives using the floppy disk controller will need
-  this. Colorado Jumbo, Conner Tape-Stor would be two models of this.
-  If you have a non-SCSI tape device like that, say Y. QIC-40 users
-  say Y too. And everyone read the Ftape-HOWTO, available via ftp
-  (user: anonymous) from sunsite.unc.edu:/pub/Linux/docs/HOWTO. Tape
-  drives that attach to the parallel port, like the Colorado Tracker,
-  are not yet supported by Linux.  Note that saying Y here will not
-  insert the code into the kernel: instead, a module will be compiled
-  ( = code which can be inserted in and removed from the running
-  kernel whenever you want). Read Documentation/modules.txt to find
-  out how to use it.
+  This option is obsolete as of Linux v1.3.34. If you would like to
+  use a tape drive that uses the floppy disk controller, like QIC-40,
+  QIC-80, QIC-117, QIC-3010 (examples: Colorado Jumbo or Conner
+  Tape-Stor), you want to read the Ftape-HOWTO, available via ftp
+  (user: anonymous) from sunsite.unc.edu:/pub/Linux/docs/HOWTO. Then
+  get the ftape distribution v2.04 or higher from
+  sunsite.unc.edu:/pub/Linux/kernel/tapes/. Tape drives that attach to
+  the parallel port, like the Colorado Tracker, are not yet supported
+  by Linux.
 
 number of ftape buffers
 NR_FTAPE_BUFFERS 3
-  The floppy tape drive needs some memory allocated in the kernel.
-  n buffers, each having 32 kB, will be allocated if you enter n
-  here. Consequently, your kernel size increases by n*32 kB. You
-  should accept the default unless you know what you're doing.
+  This option is obsolete since Linux v1.3.34. Upgrade your ftape
+  distribution to v2.04.
 
 Zilog serial support
 CONFIG_SUN_ZS
@@ -2197,21 +2264,20 @@
 
 Advanced Power Management
 CONFIG_APM
-  This driver provides APM support on machines with an APM-compliant 32-bit
-  BIOS.  Specifically, the time will be reset after a USER RESUME
-  operation, the /proc/apm device will provide battery status information,
-  and ioctls are provided to put the machine in STANDBY or SUSPEND mode.
-  This is most useful on laptops with a compliant BIOS.
+  APM is a BIOS standard for saving power using several different
+  techniques. This is mostly useful for battery powered laptops with
+  APM compliant BIOSes.  Specifically, the time will be reset after a
+  USER RESUME operation, the /proc/apm device will provide battery
+  status information, and ioctls are provided to put the machine in
+  STANDBY or SUSPEND mode.  This driver does not work for the TI 4000M
+  TravelMate and the ACER 486/DX4/75 because they don't follow the
+  standard. Say Y if you have a different laptop.
 
 Ignore USER SUSPEND
 CONFIG_APM_IGNORE_USER_SUSPEND
   This option will ignore USER SUSPEND requests.  On machines with a
-  compliant APM BIOS, this is never what you want to do.  However, this is
-  necessary on the NEC Versa M series, which generates these when resuming
-  from SYSTEM SUSPEND.  Enabling this on other laptops may cause the laptop
-  to generate a CRITICAL SUSPEND when an appropriate USER SUSPEND is
-  ignored -- this may prevent the APM driver from updating the system time
-  on a RESUME.
+  compliant APM BIOS, you want to say N.  However, on the NEC Versa M
+  series notebooks, it is necessary to say Y because of a BIOS bug.
 
 Enable APM features
 CONFIG_APM_DO_ENABLE
@@ -2227,6 +2293,17 @@
   this off if you have a NEC UltraLite Versa 33/C or a Toshiba T400CDT.
   This is off by default since most machines do fine without this feature.
 
+Watchdog Timer Support 
+CONFIG_WATCHDOG
+  If you enable this option and create a character special file
+  /dev/watchdog with major number 10 and minor number 130 using mknod
+  ("man mknod"), you will get a software watchdog, i.e.: subsequently
+  opening the file and failing to write to it for longer than 1 minute
+  will result in rebooting the machine. This could be useful for a
+  networked machine that needs to come back online as fast as possible
+  after a lock-up. For details, read Documentation/watchdog.txt in the
+  kernel source. If unsure, say N.
+
 Do CPU IDLE calls
 CONFIG_APM_CPU_IDLE
   Enable calls to APM CPU Idle/CPU Busy inside the kernel's idle loop.  On
@@ -2330,4 +2407,9 @@
 # LocalWords:  pppd Zilog ZS soundcards SRM bootloader ez mainmenu rarp ipfwadm
 # LocalWords:  RTNETLINK mknod xos MTU lwared Macs mac netatalk macs cs Wolff
 # LocalWords:  dartmouth flowerpt MultiMaster FlashPoint tudelft etherexpress
-# LocalWords:  ICL EtherTeam ETH IDESCSI TXC
+# LocalWords:  ICL EtherTeam ETH IDESCSI TXC dmesg httpd hlt sjc barlow dlp mtu
+# LocalWords:  thesphere TwoServers BOOTP DHCP ncpfs BPQETHER BPQ chipsets MG
+# LocalWords:  bsd comp Sparcstation le SunOS ie Gracilis PackeTwin PT pt LU FX
+# LocalWords:  FX TEAC SoundBlaster CR CreativeLabs LCS mS
+# LocalWords:  Vertos Genoa Funai hsfs NCP NetWare tgz APM apm ioctls UltraLite
+# LocalWords:  TravelMate CDT LCD backlight VC
diff -u --recursive --new-file v1.3.56/linux/Documentation/networking/arcnet-hardware.txt linux/Documentation/networking/arcnet-hardware.txt
--- v1.3.56/linux/Documentation/networking/arcnet-hardware.txt	Mon Dec 11 15:41:59 1995
+++ linux/Documentation/networking/arcnet-hardware.txt	Thu Jan 11 12:00:43 1996
@@ -1,7 +1,10 @@
 
 -----------------------------------------------------------------------------
-This file is a supplement to README.arcnet.  Please read that for general
-driver configuration help.
+1) This file is a supplement to arcnet.txt.  Please read that for general
+   driver configuration help.
+-----------------------------------------------------------------------------
+2) This file is no longer Linux-specific.  It should probably be moved out of
+   the kernel sources.  Ideas?
 -----------------------------------------------------------------------------
 
 Because so many people (myself included) seem to have obtained ARCnet cards
@@ -17,18 +20,19 @@
 ARCnet is a network type which works in a way similar to popular "ethernet"
 networks but which is also different in some very important ways.
 
-First of all, you can get ARCnet cards in two speeds: 2.5Mbps (slower than
-ethernet) and 100Mbps (faster than ethernet).  The two hardware types, as
-far as I'm aware, are not compatible and so you cannot wire a 100Mbps card
-to a 2.5Mbps card, and so on.  From what I hear, my driver does work with
-100Mbps cards, but I haven't been able to verify this myself, since I only
-have the 2.5Mbps variety.
+First of all, you can get ARCnet cards in at least two speeds: 2.5Mbps
+(slower than ethernet) and 100Mbps (faster than normal ethernet).  In fact,
+there are others as well, but these are less common.  The different hardware
+types, as far as I'm aware, are not compatible and so you cannot wire a
+100Mbps card to a 2.5Mbps card, and so on.  From what I hear, my driver does
+work with 100Mbps cards, but I haven't been able to verify this myself,
+since I only have the 2.5Mbps variety.
 
 You also cannot connect an ARCnet card to any kind of ethernet card and
 expect it to work.  
 
 There are two "types" of ARCnet - STAR topology and BUS topology.  This
-refers to how the cards are meant to be wired together.  According to all
+refers to how the cards are meant to be wired together.  According to most
 available documentation, you can only connect STAR cards to STAR cards and
 BUS cards to BUS cards.  That makes sense, right?  Well, it's not quite
 true; see below under "Cabling."
@@ -36,7 +40,7 @@
 Once you get past these little stumbling blocks, ARCnet is actually quite a
 well-designed standard.  It uses something called "modified token passing"
 which makes it completely incompatible with so-called "Token Ring" cards,
-but which makes transfers much more reliable than with ethernet.  In fact,
+but which makes transfers much more reliable than ethernet does.  In fact,
 ARCnet will guarantee that a packet arrives safely at the destination, and
 even if it can't possibly be delivered properly (ie. because of a cable
 break, or because the destination computer does not exist) it will at least
@@ -49,7 +53,7 @@
 sometimes very similar, ethernet drivers.  Of course, always using the same
 programming interface also means that when high-performance hardware
 facilities like PCI busmastering DMA appear, it's hard to take advantage of
-them.
+them.  Let's not go into that.
 
 One thing that makes ARCnet cards difficult to program for, however, is the
 limit on their packet sizes; standard ARCnet can only send packets that are
@@ -62,134 +66,176 @@
 
 CABLING ARCNET NETWORKS
 -----------------------
- - Information in this section is from several contributors, including:
- 	Stephen A. Wood <saw@hallc1.cebaf.gov>
- 	John Paul Morrison <jmorriso@bogomips.ee.ubc.ca>
- 	Joachim Koenig <jojo@repas.de>
- 	Vojtech Pavlik <vpav4328@Diana.troja.mff.cuni.cz>
-
- - I, Avery Pennarun, tried to arrange it into something that makes sense
-   when all put together.  All mistakes, then, are most likely my fault.
-   Bug me about them, and they will probably get fixed.
-
-Ideally, according to documentation, ARCnet networks should be connected
-with 93 Ohm cables with 93 Ohm resistors as terminators.  I use TV cable and
-no resistors to connect two STAR cards.  Blah.  Your mileage may vary.
-
-Here's some more specific information about cables, sent in by Joachim
-Koenig (slight touchups by me):
-
-	The following cables are valid for ARCnet:
-		RG-62  93 Ohm  up to 610 m
-		RG-59/U 75 Ohm up to 457 m
-		RG-11/U 75 Ohm up to 533 m
-		IBM Typ 1 150 Ohm up to 200 m
-		IBM Typ 3 100 Ohm up to 100 m
-		
-So you can see that while 93 Ohms is ideal, you can still go half a
-kilometer with 75 Ohm TV cable.  A rule of thumb might be handy here, to
-help you remember:
-	Basically any coax cable, up to a ridiculously large distance.
-
-The above applies to all known ARCnet cards.  Specific to STAR cards,
-though, Stephen A. Wood has some information:
-
-	When I bought my two cards at a garage sale, they came with little
-	box with four BNC connectors on the outside.  The shields of the
-	four connectors are all grounded together, and the center conductors
-	were connected by the following resistor network.
-
-	              |
-	              |
-        	      R
-	              |
-	         ---R-+-R---
-	              | 
-	              R
-	              |
-	              |
-
-	Where R is 47 Ohms.  A little math shows that if you terminate three
-	of the outputs with 90 ohms (A cable or a terminator), the remaining
-	input sees 90 Ohms.  Therefore this box is impedance matched to 90
-	Ohm cable.  So this box can be used to connect 2 to 4 nodes
-	together.
-
-If you really use your imagination, you can see how the above diagram kind
-of looks like a "star."  John Paul Morrison makes a few notes about the
-above:
-
-	The "little box with four BNC connectors on the outside" is an
-	Arcnet passive hub. They're worth about $5 (just so you don't get
-	ripped off).
-
-	I don't have specs at my fingertips. Basically, don't use too many
-	passive hubs (or none at all, apparently passive hubs are A Bad
-	Thing.  On the other hand, they're a really cheap way to do things.)
-
-	Arcnet active hubs are available; they are analogous to Ethernet
-	twisted pair hubs. You can plug either a single station or a passive
-	hub into each port on the active hub. If you plug in a passive hub,
-	that lets you connect three more stations.
-	
-According to Vojtech Pavlik, there shouldn't be more than one passive hub
-between two "active ends", an active end being an active hub or an ARCnet
-card.  That makes sense to me.
-	
-As for BUS cards, they're even easier (for more than two cards, anyway; you
-can't get much simpler than direct-connecting two STAR cards with a TV
-cable).  They work just like Thinnet ethernet; it looks like this:
-
-	R------+------+------+------R
-	       |      |      |
-	       NODE   NODE   NODE
-
-Where R is the terminator as usual, and '+' represents a T connector.
-
-Okay, then, what if you have a combination of BUS cards and STAR cards?  You
-probably can't do ANY combination you want, but Vojtech Pavlik explains what
-works for him (WARNING: I, Avery, haven't tried anything this weird myself):
-
-	All that I need is to terminate each end with a STAR card. I think I
-	can even connect the cards like this:
-
-	STAR------+-----+-----STAR
-	         BUS   BUS
-
-	Where "-" stands for coax, "+" for T connector, "STAR" for star-type
-	arcnet card and BUS for bus-type arcnet cards. I think there will be
-	no terminators necessary.
 
-He also explains (I paraphrase slightly here):
-
-	R-+----------STAR
-	 BUS
- 
-	In this case, I have to terminate the end with the bus card
-	using a terminator. ("R" - terminator, "+" - T connector "-" - coax,
-	everything other is probably clear)
-	
-	[...]
- 
-	And _MAYBE_ you can do even more complicated and insane combinations 
-	when just thinking of the STAR card as a BUS one with an inside 
-	installed terminator. (STAR cards are NOT bus ones with terminator, 
-	but I believe that they electrically act like them). 
-
-And finally, he gives a shortcut for BUS users that are low on supplies:
-
-	When you have really short cables (about four meters in total) and have lack
-	of terminators or T's you can leave one of the terminators out .... or put
-	it in the middle like this:
- 
-	BUS-----T-----BUS
-        	R
- 
-	It's ugly, but it works.
-	
-I don't pretend to understand what's happening there, but then again, I
-don't have BUS cards either.
+This section was rewritten by 
+        Vojtech Pavlik     <Vojtech.Pavlik@st.mff.cuni.cz>
+using information from several people, including:
+        Avery Pennraun     <apenwarr@foxnet.net>
+ 	Stephen A. Wood    <saw@hallc1.cebaf.gov>
+ 	John Paul Morrison <jmorriso@bogomips.ee.ubc.ca>
+ 	Joachim Koenig     <jojo@repas.de>
+and Avery touched it up a bit, at Vojtech's request.
 
+ARCnet (the classic 2.5 Mbps version) can be connected by two different
+types of cabling: coax and twisted pair.  The other ARCnet-type networks
+(100 Mbps TCNS and 320 kbps - 32 Mbps ARCnet Plus) use different types of
+cabling (Type1, Fiber, C1, C4, C5).
+
+For a coax network, you "should" use 93 Ohm RG-62 cable.  But other cables
+also work fine, because ARCnet is a very stable network. I personally use 75
+Ohm TV antenna cable.
+
+Cards for coax cabling are shipped in two different variants: for BUS and
+STAR network topologies.  They are mostly the same.  The only difference
+lies in the hybrid chip installed.  BUS cards use high impedance output,
+while STAR use low impedance.  Low impedance card (STAR) is electrically
+equal to a high impedance one with a terminator installed.
+
+Usually, the ARCnet networks are built up from STAR cards and hubs.  There
+are two types of hubs - active and passive.  Passive hubs are small boxes
+with four BNC connectors containing four 47 Ohm resistors:
+
+   |         | wires
+   R         + junction
+-R-+-R-      R 47 Ohm resistors
+   R
+   |
+
+The shielding is connected together.  Active hubs are much more complicated;
+they are powered and contain electronics to amplify the signal and send it
+to other segments of the net.  They usually have eight connectors.  Active
+hubs come in two variants - dumb and smart.  The dumb variant just
+amplifies, but the smart one decodes to digital and encodes back all packets
+coming through.  This is much better if you have several hubs in the net,
+since many dumb active hubs may worsen the signal quality.
+
+And now to the cabling.  What you can connect together:
+
+1. A card to a card.  This is the simplest way of creating a 2-computer
+   network.
+
+2. A card to a passive hub.  Remember that all unused connectors on the hub
+   must be properly terminated with 93 Ohm (or something else if you don't
+   have the right ones) terminators.
+   	(Avery's note: oops, I didn't know that.  Mine (TV cable) works
+	anyway, though.)
+
+3. A card to an active hub.  Here is no need to terminate the unused
+   connectors except some kind of aesthetic feeling.  But, there may not be
+   more than eleven active hubs between any two computers.  That of course
+   doesn't limit the number of active hubs on the network.
+   
+4. An active hub to another.
+
+5. An active hub to passive hub.
+
+Remember, that you can not connect two passive hubs together.  The power loss
+implied by such connection is too high for the net to operate reliably.
+
+An example of a typical ARCnet network:
+
+           R                     S - STAR type card              
+    S------H--------A-------S    R - Terminator
+           |        |            H - Hub                         
+           |        |            A - Active hub                  
+           |   S----H----S                                       
+           S        |                                            
+                    |                                            
+                    S                                            
+                                                                          
+The BUS topology is very similar to the one used by Ethernet.  The only
+difference is in cable and terminators: they should be 93 Ohm.  Ethernet
+uses 50 Ohm impedance. You use T connectors to put the computers on a single
+line of cable, the bus. You have to put terminators at both ends of the
+cable. A typical BUS ARCnet network looks like:
+
+    RT----T------T------T------T------TR
+     B    B      B      B      B      B
+
+  B - BUS type card
+  R - Terminator
+  T - T connector
+
+But that is not all! The two types can be connected together.  According to
+the official documentation the only way of conecting them is using an active
+hub:
+
+         A------T------T------TR
+         |      B      B      B
+     S---H---S
+         |
+         S
+
+The official docs also state that you can use STAR cards at the ends of
+BUS network in place of a BUS card and a terminator:
+
+     S------T------T------S
+            B      B
+
+But, according to my own experiments, you can simply hang a BUS type card
+anywhere in middle of a cable in a STAR topology network.  And more - you
+can use the bus card in place of any star card if you use a terminator. Then
+you can build very complicated networks fulfilling all your needs!  An
+example:
+
+                                  S
+                                  |
+           RT------T-------T------H------S
+            B      B       B      |
+                                  |       R
+    S------A------T-------T-------A-------H------TR                    
+           |      B       B       |       |      B                         
+           |   S                 BT       |                                 
+           |   |                  |  S----A-----S
+    S------H---A----S             |       | 
+           |   |      S------T----H---S   |
+           S   S             B    R       S  
+                                                               
+A basicaly different cabling scheme is used with Twisted Pair cabling. Each
+of the TP cards has two RJ (phone-cord style) connectors.  The cards are
+then daisy-chained together using a cable connecting every two neighboring
+cards.  The ends are terminated with RJ 93 Ohm terminators which plug into
+the empty connectors of cards on the ends of the chain.  An example:
+
+          ___________   ___________
+      _R_|_         _|_|_         _|_R_  
+     |     |       |     |       |     |      
+     |Card |       |Card |       |Card |     
+     |_____|       |_____|       |_____|          
+
+
+There are also hubs for the TP topology.  There is nothing difficult
+involved in using them; you just connect a TP chain to a hub on any end or
+even at both.  This way you can create almost any network configuration. 
+The maximum of 11 hubs between any two computers on the net applies here as
+well.  An example:
+
+    RP-------P--------P--------H-----P------P-----PR
+                               |
+      RP-----H--------P--------H-----P------PR
+             |                 |
+             PR                PR
+
+    R - RJ Terminator
+    P - TP Card
+    H - TP Hub
+
+Like any network, ARCnet has a limited cable length.  These are the maximum
+cable lengths between two active ends (an active end being an active hub or
+a STAR card).
+
+		RG-62       93 Ohm up to 650 m
+		RG-59/U     75 Ohm up to 457 m
+		RG-11/U     75 Ohm up to 533 m
+		IBM Type 1 150 Ohm up to 200 m
+		IBM Type 3 100 Ohm up to 100 m
+
+The maximum length of all cables connected to a passive hub is limited to 65
+meters for RG-62 cabling; less for others.  You can see that using passive
+hubs in a large network is a bad idea. The maximum length of a single "BUS
+Trunk" is about 300 meters for RG-62. The maximum distance between the two
+most distant points of the net is limited to 3000 meters. The maximum length
+of a TP cable between two cards/hubs is 650 meters.
 
 
 SETTING THE JUMPERS
@@ -208,10 +254,33 @@
 	- Avery's favourite: 0x300.
 
   - the IRQ: on  8-bit cards, it might be 2 (9), 3, 4, 5, or 7.
-             on 16-bit cards, it might be 2 (9), 3, 4, 5, 7, or 10-15.  Make
-    sure this is different from any other card on your system.  Note that
-    IRQ2 is the same as IRQ9, as far as Linux is concerned.
-	- Avery's favourite: IRQ2 (actually IRQ9).
+             on 16-bit cards, it might be 2 (9), 3, 4, 5, 7, or 10-15.
+             
+    Make sure this is different from any other card on your system.  Note
+    that IRQ2 is the same as IRQ9, as far as Linux is concerned.  You can
+    "cat /proc/interrupts" for a somewhat complete list of which ones are in
+    use at any given time.  Here is a list of common usages from Vojtech
+    Pavlik <vpav4328@diana.troja.mff.cuni.cz>:
+    	("Not on bus" means there is no way for a card to generate this
+	interrupt)
+	IRQ  0 - Timer 0 (Not on bus)
+	IRQ  1 - Keyboard (Not on bus)
+	IRQ  2 - IRQ Controller 2 (Not on bus, nor does interrupt the CPU)
+	IRQ  3 - COM2
+	IRQ  4 - COM1
+	IRQ  5 - FREE (LPT2 if you have it; sometimes COM3)
+	IRQ  6 - Floppy disk controller
+	IRQ  7 - FREE (LPT1 if you don't use the polling driver or PLIP) 
+	IRQ  8 - Realtime Clock Interrupt (Not on bus)
+	IRQ  9 - FREE (VGA vertical sync interrupt if enabled)
+	IRQ 10 - FREE
+	IRQ 11 - FREE
+	IRQ 12 - FREE
+	IRQ 13 - Numeric Coprocessor (Not on bus)
+	IRQ 14 - Fixed Disk Controller
+	IRQ 15 - FREE (Fixed Disk Controller 2 if you have it) 
+
+	- Avery's favourite: IRQ2 (actually IRQ9).  Watch that VGA, though.
 
   - the memory address:  Unlike most cards, ARCnets use "shared memory" for
     copying buffers around.  Make SURE it doesn't conflict with any other
@@ -230,28 +299,54 @@
     address from 0 to 255.  Unlike ethernet, you can set this address
     yourself with a jumper or switch (or on some cards, with special
     software).  Since it's only 8 bits, you can only have 254 ARCnet cards
-    on a network.  DON'T use 0 or 255, since these are reserved. (although
+    on a network.  DON'T use 0 or 255, since these are reserved (although
     neat stuff will probably happen if you DO use them).  By the way, if you
     haven't already guessed, don't set this the same as any other ARCnet on
     your network!
 	- Avery's favourite:  3 and 4.  Not that it matters.
-	
-  - There may be ETS1 and ETS2 settings.  These may or may not make a
-    difference on your card, but are used to change the delays used when
-    powering up a computer on the network.  This is only necessary when
-    wiring VERY long range ARCnet networks, on the order of 4km or so; in
-    any case, the only real requirement here is that all cards on the
-    network with ETS1 and ETS2 jumpers have them in the same position.
 
-
-Here's the all the jumper information I could obtain for individual card
-types.  The format of this list has changed somewhat; I finally got around
-to unduplicating some of the information and making a few other changes, but
-didn't get very far yet.  If you notice any problems with the info, it's now
-officially my fault :(
+  - There may be ETS1 and ETS2 settings.  These may or may not make a
+    difference on your card (many manuals call them "reserved"), but are
+    used to change the delays used when powering up a computer on the
+    network.  This is only necessary when wiring VERY long range ARCnet
+    networks, on the order of 4km or so; in any case, the only real
+    requirement here is that all cards on the network with ETS1 and ETS2
+    jumpers have them in the same position.  Chris Hindy <chrish@io.org>
+    sent in a chart with actual values for this:
+	ET1	ET2	Response Time	Reconfiguration Time
+	---	---	-------------	--------------------
+	open	open	74.7us		840us
+	open	closed	283.4us		1680us
+	closed	open	561.8us		1680us
+	closed	closed	1118.6us	1680us
+    
+    Make sure you set ETS1 and ETS2 to the SAME VALUE for all cards on your
+    network.
+    
+Also, on many cards (not mine, though) there are red and green LED's. 
+Vojtech Pavlik <vpav4328@diana.troja.mff.cuni.cz> tells me this is what they
+mean:
+	GREEN           RED             Status
+	-----		---		------
+	OFF             OFF             Power off
+	OFF             Short flashes   Cabling problems (broken cable or not
+					  terminated)
+	OFF (short)     ON              Card init
+	ON              ON              Normal state - everything OK, nothing
+					  happens
+	ON              Long flashes    Data transfer
+	ON              OFF             Never happens (maybe when wrong ID)
+
+
+The following is all the specific information people have sent me about
+their own particular ARCnet cards.  It is officially a mess, and contains
+huge amounts of duplicated information.  I have no time to fix it.  If you
+want to, PLEASE DO!  Just send me a 'diff -u' of all your changes.
 
 The model # is listed right above specifics for that card, so you should be
 able to use your text viewer's "search" function to find the entry you want. 
+If you don't KNOW what kind of card you have, try looking through the
+various diagrams to see if you can tell.
 
 If your model isn't listed and/or has different settings, PLEASE PLEASE
 tell me.  I had to figure mine out without the manual, and it WASN'T FUN!
@@ -261,24 +356,32 @@
 
 Cards Listed in this file (in this order, mostly):
 
-	Manufacturer	Model #		Bits
-	------------	-------		----
-	SMC		PC100		8
-	SMC		PC110		8
-	SMC		PC120		8
-	SMC		PC130		8
-	SMC		PC270E		8
-	SMC		PC500		16
-	SMC		PC500Longboard	16
-	SMC		PC550Longboard	16
-	SMC		PC600		16
-	SMC?		LCS-8830-T	16?
-	Puredata	PDI507		8
-	CNet Tech	CN120-Series	8
-	CNet Tech	CN160-Series	16
-	No Name		--		8/16
-	No Name		Taiwan R.O.C(?)	8
-	Tiara		Tiara Lancard(?)
+	Manufacturer	Model #			Bits
+	------------	-------			----
+	SMC		PC100			8
+	SMC		PC110			8
+	SMC		PC120			8
+	SMC		PC130			8
+	SMC		PC270E			8
+	SMC		PC500			16
+	SMC		PC500Longboard		16
+	SMC		PC550Longboard		16
+	SMC		PC600			16
+	SMC		PC710			8
+	SMC?		LCS-8830-T		16?
+	Puredata	PDI507			8
+	CNet Tech	CN120-Series		8
+	CNet Tech	CN160-Series		16
+	Lantech?	UM9065L chipset		8
+	Acer		5210-003		8
+	Datapoint?	LAN-ARC-8		8
+	Topware		TA-ARC/10		8
+	Thomas-Conrad	500-6242-0097 REV A	8
+	Waterloo?	(C)1985 Waterloo Micro. 8
+	No Name		--			8/16
+	No Name		Taiwan R.O.C?		8
+	No Name		Model 9058		8
+	Tiara		Tiara Lancard?		8
 	
 
 ** SMC = Standard Microsystems Corp.
@@ -289,7 +392,7 @@
 ------------------
   - Please send any other information you can find.
   
-  - And some unknowns (other info is welcome!):
+  - And some other stuff (more info is welcome!):
      From: root@ultraworld.xs4all.nl (Timo Hilbrink)
      To: apenwarr@foxnet.net (Avery Pennarun)
      Date: Wed, 26 Oct 1994 02:10:32 +0000 (GMT)
@@ -602,7 +705,7 @@
 *****************************************************************************
 
 ** Standard Microsystems Corp (SMC) **
-PC500/PC550 Long Board (16-bit cards)
+PC500/PC550 Longboard (16-bit cards)
 -------------------------------------
   - from Juergen Seifert <seifert@htwm.de>
 
@@ -613,10 +716,15 @@
 Note: There is another Version of the PC500 called Short Version, which 
       is different in hard- and software! The most important differences
       are:
-      - The long board has no Shared memory
+      - The long board has no Shared memory.
       - On the long board the selection of the interrupt is done by binary
         coded switch, on the short board directly by jumper.
-
+        
+[Avery's note: pay special attention to that: the long board HAS NO SHARED
+MEMORY.  This means the current Linux-ARCnet driver can't use these cards. 
+I have obtained a PC500Longboard and will be doing some experiments on it in
+the future, but don't hold your breath.  Thanks again to Juergen Seifert for
+his advice about this!]
 
 This description has been written by Juergen Seifert <seifert@htwm.de>
 using information from the following Original SMC Manual 
@@ -823,6 +931,48 @@
 
 *****************************************************************************
 
+** SMC **
+PC710 (8-bit card)
+------------------
+  - from J.S. van Oosten <jvoosten@compiler.tdcnet.nl>
+  
+Note: this data is gathered by experimenting and looking at info of other
+cards. However, I'm sure I got 99% of the settings right.
+
+The SMC710 card resembles the PC270 card, but is much more basic (i.e. no
+LEDs, RJ11 jacks, etc.) and 8 bit. Here's a little drawing:
+
+    _______________________________________   
+   | +---------+  +---------+              |____
+   | |   S2    |  |   S1    |              |
+   | +---------+  +---------+              |
+   |                                       |
+   |  +===+    __                          |
+   |  | R |   |  | X-tal                 ###___
+   |  | O |   |__|                      ####__'|
+   |  | M |    ||                        ###
+   |  +===+                                |
+   |                                       |
+   |   .. JP1   +----------+               |
+   |   ..       | big chip |               |   
+   |   ..       |  90C63   |               |
+   |   ..       |          |               |
+   |   ..       +----------+               |
+    -------                     -----------
+           |||||||||||||||||||||
+
+The row of jumpers at JP1 actually consists of 8 jumpers, (sometimes
+labelled) the same as on the PC270, from top to bottom: EXT2, EXT1, ROM,
+IRQ7, IRQ5, IRQ4, IRQ3, IRQ2 (gee, wonder what they would do? :-) )
+
+S1 and S2 perform the same function as on the PC270, only their numbers
+are swapped (S1 is the nodeaddress, S2 sets IO- and RAM-address).
+
+I know it works when connected to a PC110 type ARCnet board.
+
+	
+*****************************************************************************
+
 ** Possibly SMC **
 LCS-8830-T (16-bit card)
 ------------------------
@@ -936,27 +1086,17 @@
 PDI507 (8-bit card)
 --------------------
   - from Mark Rejhon <mdrejhon@magi.com> (slight modifications by Avery)
+  - Avery's note: I think PDI508 cards (but definitely NOT PDI508Plus cards)
+    are mostly the same as this.  PDI508Plus cards appear to be mainly
+    software-configured.
 
 Jumpers:
-
 	There is a jumper array at the bottom of the card, near the edge
         connector.  This array is labelled J1.  They control the IRQs and
         something else.  Put only one jumper on the IRQ pins.
 
-	IRQ2    - Use IRQ 2 (same as IRQ 9 as far as software is concerned)
-	IRQ3	- Use IRQ 3 (used by COM2 or COM4 serial port if either exists)
-	IRQ4	- Use IRQ 4 (used by COM1 or COM3 serial port if either exists)
-	IRQ5	- Use IRQ 5 (used by LPT2 parallel port if one exists)
-	IRQ6	- Use IRQ 6 (used by Floppy Disk Controller if one exists)
-	IRQ7	- Use IRQ 7 (used by LPT1 parallel port if one exists)
-
-[Avery's note:  This "unknown" set of two jumpers appears to be on all
-ARCnet cards by SMC as well.  Putting jumpers on them seems to affect the
-status register, but only for the two "reserved" bits, ETS1 and ETS2.  Any
-further information is welcome.]
-
-	ET1	- What is this?  (Not tested, no jumper put on it)
-	ET2	- What is this?  (Not tested, no jumper put on it)
+	ETS1, ETS2 are for timing on very long distance networks.  See the
+	more general information near the top of this file.
 
 	There is a J2 jumper on two pins.  A jumper should be put on them,
         since it was already there when I got the card.  I don't know what
@@ -973,9 +1113,32 @@
 	 o | o   o |             in this direction ------->
 	   `-------'
 
-	There is also a J4 jumper on two pins.  A jumper should be put on
-        them, since it was already there when I got the card.  I don't know
-        what this jumper is for though.
+Carl de Billy <CARL@carainfo.com> explains J3 and J4:
+
+	J3 Diagram:
+
+           .-------.
+         o | o   o |
+           :-------:    TWIST Technology
+         o | o   o |
+           `-------'
+           .-------.
+           | o   o | o
+           :-------:    COAX Technology
+           | o   o | o
+           `-------'
+
+  - If using coax cable in a bus topology the J4 jumper must be removed;
+    place it on one pin.
+
+  - If using bus topology with twisted pair wiring move the J3 
+    jumpers so they connect the middle pin and the pins closest to the RJ11
+    Connectors.  Also the J4 jumper must be removed; place it on one pin of
+    J4 jumper for storage.
+
+  - If using  star topology with twisted pair wiring move the J3 
+    jumpers so they connect the middle pin and the pins closest to the RJ11
+    connectors.
 
 
 DIP Switches:
@@ -1467,6 +1630,724 @@
 
 *****************************************************************************
 
+** Lantech **
+8-bit card, unknown model
+-------------------------
+  - from Vlad Lungu <vlungu@ugal.ro> - his e-mail address seemed broken at
+    the time I tried to reach him.  Sorry Vlad, if you didn't get my reply.
+
+   ________________________________________________________________
+   |   1         8                                                 |
+   |   ___________                                               __|
+   |   |   SW1    |                                         LED |__|
+   |   |__________|                                                |
+   |                                                            ___|
+   |                _____________________                       |S | 8
+   |                |                   |                       |W |
+   |                |                   |                       |2 |
+   |                |                   |                       |__| 1
+   |                |      UM9065L      |     |o|  JP4         ____|____
+   |                |                   |     |o|              |  CN    |
+   |                |                   |                      |________|
+   |                |                   |                          |
+   |                |___________________|                          |
+   |                                                               |
+   |                                                               |
+   |      _____________                                            |
+   |      |            |                                           |
+   |      |    PROM    |        |ooooo|  JP6                       |
+   |      |____________|        |ooooo|                            |
+   |_____________                                             _   _|
+                |____________________________________________| |__|
+
+
+UM9065L : Arcnet Controller
+
+SW 1    : Shared Memory Address and I/O Base
+
+        ON=0
+
+        12345|Memory Address
+        -----|--------------
+        00001|  D4000
+        00010|  CC000
+        00110|  D0000
+        01110|  D1000
+        01101|  D9000
+        10010|  CC800
+        10011|  DC800
+        11110|  D1800
+
+It seems that the bits are considered in reverse order.  Also, you must
+observe that some of those addresses are unusual and I didn't probe them; I
+used a memory dump in DOS to identify them.  For the 00000 configuration and
+some others that I didn't write here the card seems to conflict with the
+video card (an S3 GENDAC). I leave the full decoding of those addresses to
+you.
+
+        678| I/O Address
+        ---|------------
+        000|    260
+        001|    failed probe
+        010|    2E0
+        011|    380
+        100|    290
+        101|    350
+        110|    failed probe
+        111|    3E0
+
+SW 2  : Node ID (binary coded)
+
+JP 4  : Boot PROM enable   CLOSE - enabled
+                           OPEN  - disabled
+
+JP 6  : IRQ set (ONLY ONE jumper on 1-5 for IRQ 2-6)
+
+
+*****************************************************************************
+
+** Acer **
+8-bit card, Model 5210-003
+--------------------------
+  - from Vojtech Pavlik <vpav4328@diana.troja.mff.cuni.cz> using portions of
+    the existing arcnet-hardware file.
+
+This is a 90C26 based card.  Its configuration seems similar to 
+the SMC PC100, but has some additional jumpers I don't know.
+
+               __
+              |  |
+   ___________|__|_________________________
+  |         |      |                       |
+  |         | BNC  |                       |
+  |         |______|                    ___|
+  |  _____________________             |___  
+  | |                     |                |
+  | | Hybrid IC           |                |
+  | |                     |       o|o J1   |
+  | |_____________________|       8|8      |
+  |                               8|8 J5   |
+  |                               o|o      |
+  |                               8|8      |
+  |__                             8|8      |
+ (|__| LED                        o|o      |
+  |                               8|8      |
+  |                               8|8 J15  |
+  |                                        |
+  |                    _____               |
+  |                   |     |   _____      |
+  |                   |     |  |     |  ___|
+  |                   |     |  |     | |    
+  |  _____            | ROM |  | UFS | |    
+  | |     |           |     |  |     | |   
+  | |     |     ___   |     |  |     | |   
+  | |     |    |   |  |__.__|  |__.__| |   
+  | | NCR |    |XTL|   _____    _____  |   
+  | |     |    |___|  |     |  |     | |   
+  | |90C26|           |     |  |     | |   
+  | |     |           | RAM |  | UFS | |   
+  | |     | J17 o|o   |     |  |     | |   
+  | |     | J16 o|o   |     |  |     | |   
+  | |__.__|           |__.__|  |__.__| |   
+  |  ___                               |   
+  | |   |8                             |   
+  | |SW2|                              |   
+  | |   |                              |   
+  | |___|1                             |   
+  |  ___                               |   
+  | |   |10           J18 o|o          |   
+  | |   |                 o|o          |   
+  | |SW1|                 o|o          |   
+  | |   |             J21 o|o          |   
+  | |___|1                             |   
+  |                                    |   
+  |____________________________________|   
+
+
+Legend:
+
+90C26       ARCNET Chip
+XTL         20 MHz Crystal
+SW1 1-6     Base I/O Address Select
+    7-10    Memory Address Select
+SW2 1-8     Node ID Select (ID0-ID7)
+J1-J5       IRQ Select
+J6-J21      Unknown (Probably extra timeouts & ROM enable ...)
+LED1        Activity LED 
+BNC         Coax connector (STAR arcnet)
+RAM         2k of SRAM
+ROM         Boot ROM socket
+UFS         Unidentified Flying Sockets
+
+
+Setting the Node ID
+-------------------
+
+The eight switches in SW2 are used to set the node ID. Each node attached
+to the network must have an unique node ID which must not be 0.
+Switch 1 (ID0) serves as the least significant bit (LSB).
+
+Setting one of the switches to OFF means "1", ON means "0".
+
+The node ID is the sum of the values of all switches set to "1"
+These values are:
+
+   Switch | Value
+   -------|-------
+     1    |   1
+     2    |   2
+     3    |   4
+     4    |   8
+     5    |  16
+     6    |  32
+     7    |  64
+     8    | 128
+
+Don't set this to 0 or 255; these values are reserved.
+
+
+Setting the I/O Base Address
+----------------------------
+
+The switches 1 to 6 of switch block SW1 are used to select one
+of 32 possible I/O Base addresses using the followig tables
+   
+          | Hex
+   Switch | Value
+   -------|-------
+     1    | 200  
+     2    | 100  
+     3    |  80  
+     4    |  40  
+     5    |  20  
+     6    |  10 
+
+The I/O address is sum of all switches set to "1". Remember that
+the I/O address space bellow 0x200 is RESERVED for mainboard, so
+switch 1 should be ALWAYS SET TO OFF. 
+
+
+Setting the Base Memory (RAM) buffer Address
+--------------------------------------------
+
+The memory buffer (RAM) requires 2K. The base of this buffer can be
+located in any of sixteen positions. However, the addresses below
+A0000 are likely to cause system hang because there's main RAM.
+
+Jumpers 7-10 of switch block SW1 select the Memory Base address.
+
+   Switch          | Hex RAM
+    7   8   9  10  | Address
+   ----------------|---------
+   OFF OFF OFF OFF |  F0000 (conflicts with main BIOS)
+   OFF OFF OFF ON  |  E0000 
+   OFF OFF ON  OFF |  D0000
+   OFF OFF ON  ON  |  C0000 (conflicts with video BIOS)
+   OFF ON  OFF OFF |  B0000 (conflicts with mono video)
+   OFF ON  OFF ON  |  A0000 (conflicts with graphics)
+
+
+Setting the Interrupt Line
+--------------------------
+
+Jumpers 1-5 of the jumper block J1 controll the IRQ level. ON means 
+shorted, OFF means open.
+
+    Jumper              |  IRQ
+    1   2   3   4   5   |
+   ----------------------------
+    ON  OFF OFF OFF OFF |  7
+    OFF ON  OFF OFF OFF |  5
+    OFF OFF ON  OFF OFF |  4
+    OFF OFF OFF ON  OFF |  3
+    OFF OFF OFF OFF ON  |  2
+
+
+Unknown jumpers & sockets
+-------------------------
+
+I know nothing about these. I just guess that J16&J17 are timeout
+jumpers and maybe one of J18-J21 selects ROM. Also J6-J10 and
+J11-J15 are connecting IRQ2-7 to some pins on the UFSs. I can't
+guess the purpose.
+
+
+*****************************************************************************
+
+** Datapoint? **
+LAN-ARC-8, an 8-bit card
+------------------------
+  - from Vojtech Pavlik <vpav4328@diana.troja.mff.cuni.cz>
+
+This is another SMC 90C65 based arcnet card. I couldn't identify the
+manufacturer, but it might be DataPoint, becsuse the card has the
+original arcNet logo in its upper right corner.
+
+          _______________________________________________________
+         |                         _________                     |
+         |                        |   SW2   | ON      arcNet     |
+         |                        |_________| OFF             ___|
+         |  _____________         1 ______  8                |   | 8  
+         | |             | SW1     | XTAL | ____________     | S |    
+         | > RAM (2k)    |         |______||            |    | W |    
+         | |_____________|                 |      H     |    | 3 |    
+         |                        _________|_____ y     |    |___| 1  
+         |  _________            |         |     |b     |        |    
+         | |_________|           |         |     |r     |        |    
+         |                       |     SMC |     |i     |        |    
+         |                       |    90C65|     |d     |        |      
+         |  _________            |         |     |      |        |
+         | |   SW1   | ON        |         |     |I     |        |
+         | |_________| OFF       |_________|_____/C     |   _____|
+         |  1       8                      |            |  |     |___
+         |  ______________                 |            |  | BNC |___|
+         | |              |                |____________|  |_____|
+         | > EPROM SOCKET |              _____________           |
+         | |______________|             |_____________|          |
+         |                                         ______________|
+         |                                        | 
+         |________________________________________|
+
+Legend:
+
+90C65       ARCNET Chip 
+SW1 1-5:    Base Memory Address Select
+    6-8:    Base I/O Address Select
+SW2 1-8:    Node ID Select
+SW3 1-5:    IRQ Select   
+    6-7:    Extra Timeout
+    8  :    Rom Enable   
+BNC         Coax connector
+XTAL        20MHz Crystal
+
+
+Setting the Node ID
+-------------------
+
+The eight switches in SW3 are used to set the node ID. Each node attached
+to the network must have an unique node ID which must not be 0.
+Switch 1 serves as the least significant bit (LSB).
+
+Setting one of the switches to Off means "1", On means "0".
+
+The node ID is the sum of the values of all switches set to "1"  
+These values are:
+
+   Switch | Value
+   -------|-------
+     1    |   1
+     2    |   2
+     3    |   4
+     4    |   8
+     5    |  16
+     6    |  32
+     7    |  64
+     8    | 128
+
+
+Setting the I/O Base Address
+----------------------------
+
+The last three switches in switch block SW1 are used to select one
+of eight possible I/O Base addresses using the followig table
+
+
+   Switch      | Hex I/O
+    6   7   8  | Address
+   ------------|--------
+   ON  ON  ON  |  260
+   OFF ON  ON  |  290
+   ON  OFF ON  |  2E0  (Manufacturer's default)
+   OFF OFF ON  |  2F0
+   ON  ON  OFF |  300
+   OFF ON  OFF |  350
+   ON  OFF OFF |  380
+   OFF OFF OFF |  3E0
+
+
+Setting the Base Memory (RAM) buffer Address
+--------------------------------------------
+
+The memory buffer (RAM) requires 2K. The base of this buffer can be 
+located in any of eight positions. The address of the Boot Prom is
+memory base + 0x2000.
+Jumpers 3-5 of switch block SW1 select the Memory Base address.
+
+   Switch              | Hex RAM | Hex ROM
+    1   2   3   4   5  | Address | Address *)
+   --------------------|---------|-----------
+   ON  ON  ON  ON  ON  |  C0000  |  C2000
+   ON  ON  OFF ON  ON  |  C4000  |  C6000
+   ON  ON  ON  OFF ON  |  CC000  |  CE000
+   ON  ON  OFF OFF ON  |  D0000  |  D2000  (Manufacturerr's default)
+   ON  ON  ON  ON  OFF |  D4000  |  D6000
+   ON  ON  OFF ON  OFF |  D8000  |  DA000
+   ON  ON  ON  OFF OFF |  DC000  |  DE000
+   ON  ON  OFF OFF OFF |  E0000  |  E2000
+  
+*) To enable the Boot ROM set the switch 8 of switch block SW3 to position ON.
+
+The switches 1 and 2 probably add 0x0800 and 0x1000 to RAM base address.
+
+
+Setting the Interrupt Line
+--------------------------
+
+Switches 1-5 of the switch block SW3 control the IRQ level.
+
+    Jumper              |  IRQ
+    1   2   3   4   5   |
+   ----------------------------
+    ON  OFF OFF OFF OFF |  3
+    OFF ON  OFF OFF OFF |  4
+    OFF OFF ON  OFF OFF |  5
+    OFF OFF OFF ON  OFF |  7
+    OFF OFF OFF OFF ON  |  2
+
+
+Setting the Timeout Parameters
+------------------------------
+
+The switches 6-7 of the switch block SW3 are used to determine the timeout
+parameters.  These two switches are normally left in the OFF position.
+
+
+*****************************************************************************
+
+** Topware **
+8-bit card, TA-ARC/10
+-------------------------
+  - from Vojtech Pavlik <vpav4328@diana.troja.mff.cuni.cz>
+
+This is another very similar 90C65 card. Most of the switches and jumpers
+are the same as on other clones.
+
+ _____________________________________________________________________
+|  ___________   |                         |            ______        |
+| |SW2 NODE ID|  |                         |           | XTAL |       |
+| |___________|  |  Hybrid IC              |           |______|       |
+|  ___________   |                         |                        __|    
+| |SW1 MEM+I/O|  |_________________________|                   LED1|__|)   
+| |___________|           1 2                                         |     
+|                     J3 |o|o| TIMEOUT                          ______|    
+|     ______________     |o|o|                                 |      |    
+|    |              |  ___________________                     | RJ   |    
+|    > EPROM SOCKET | |                   \                    |------|     
+|J2  |______________| |                    |                   |      |    
+||o|                  |                    |                   |______|
+||o| ROM ENABLE       |        SMC         |    _________             |
+|     _____________   |       90C65        |   |_________|       _____|    
+|    |             |  |                    |                    |     |___ 
+|    > RAM (2k)    |  |                    |                    | BNC |___|
+|    |_____________|  |                    |                    |_____|    
+|                     |____________________|                          |    
+| ________ IRQ 2 3 4 5 7                  ___________                 |
+||________|   |o|o|o|o|o|                |___________|                |
+|________   J1|o|o|o|o|o|                               ______________|
+         |                                             |
+         |_____________________________________________|
+
+Legend:
+
+90C65       ARCNET Chip
+XTAL        20 MHz Crystal
+SW1 1-5     Base Memory Address Select
+    6-8     Base I/O Address Select
+SW2 1-8     Node ID Select (ID0-ID7)
+J1          IRQ Select
+J2          Rom Enable
+J3          Extra Timeout
+LED1        Activity LED 
+BNC         Coax connector (BUS arcnet)
+RJ          Twisted Pair Connector (daisychain)
+
+
+Setting the Node ID
+-------------------
+
+The eight switches in SW2 are used to set the node ID. Each node attached to
+the network must have an unique node ID which must not be 0.  Switch 1 (ID0)
+serves as the least significant bit (LSB).
+
+Setting one of the switches to Off means "1", On means "0".
+
+The node ID is the sum of the values of all switches set to "1"
+These values are:
+
+   Switch | Label | Value
+   -------|-------|-------
+     1    | ID0   |   1
+     2    | ID1   |   2
+     3    | ID2   |   4
+     4    | ID3   |   8
+     5    | ID4   |  16
+     6    | ID5   |  32
+     7    | ID6   |  64
+     8    | ID7   | 128
+
+Setting the I/O Base Address
+----------------------------
+
+The last three switches in switch block SW1 are used to select one
+of eight possible I/O Base addresses using the following table:
+
+
+   Switch      | Hex I/O
+    6   7   8  | Address
+   ------------|--------
+   ON  ON  ON  |  260  (Manufacturer's default)
+   OFF ON  ON  |  290
+   ON  OFF ON  |  2E0                         
+   OFF OFF ON  |  2F0
+   ON  ON  OFF |  300
+   OFF ON  OFF |  350
+   ON  OFF OFF |  380
+   OFF OFF OFF |  3E0
+
+
+Setting the Base Memory (RAM) buffer Address
+--------------------------------------------
+
+The memory buffer (RAM) requires 2K. The base of this buffer can be
+located in any of eight positions. The address of the Boot Prom is
+memory base + 0x2000.
+Jumpers 3-5 of switch block SW1 select the Memory Base address.
+
+   Switch              | Hex RAM | Hex ROM
+    1   2   3   4   5  | Address | Address *)
+   --------------------|---------|-----------
+   ON  ON  ON  ON  ON  |  C0000  |  C2000
+   ON  ON  OFF ON  ON  |  C4000  |  C6000  (Manufacturer's default) 
+   ON  ON  ON  OFF ON  |  CC000  |  CE000
+   ON  ON  OFF OFF ON  |  D0000  |  D2000  
+   ON  ON  ON  ON  OFF |  D4000  |  D6000
+   ON  ON  OFF ON  OFF |  D8000  |  DA000
+   ON  ON  ON  OFF OFF |  DC000  |  DE000
+   ON  ON  OFF OFF OFF |  E0000  |  E2000
+
+*) To enable the Boot ROM short the jumper J2.
+
+The jumpers 1 and 2 probably add 0x0800 and 0x1000 to RAM address.
+
+
+Setting the Interrupt Line
+--------------------------
+
+Jumpers 1-5 of the jumper block J1 control the IRQ level.  ON means
+shorted, OFF means open.
+
+    Jumper              |  IRQ
+    1   2   3   4   5   |
+   ----------------------------
+    ON  OFF OFF OFF OFF |  2
+    OFF ON  OFF OFF OFF |  3
+    OFF OFF ON  OFF OFF |  4
+    OFF OFF OFF ON  OFF |  5
+    OFF OFF OFF OFF ON  |  7
+
+
+Setting the Timeout Parameters
+------------------------------
+
+The jumpers J3 are used to set the timeout parameters. These two 
+jumpers are normally left open.
+
+  
+*****************************************************************************
+
+** Thomas-Conrad **
+Model #500-6242-0097 REV A (8-bit card)
+---------------------------------------
+  - from Lars Karlsson <100617.3473@compuserve.com>
+
+     ________________________________________________________
+   |          ________   ________                           |_____
+   |         |........| |........|                            |
+   |         |________| |________|                         ___|
+   |            SW 3       SW 1                           |   |
+   |         Base I/O   Base Addr.                Station |   |
+   |                                              address |   |
+   |    ______                                    switch  |   |
+   |   |      |                                           |   |
+   |   |      |                                           |___|    
+   |   |      |                                 ______        |___._
+   |   |______|                                |______|         ____| BNC
+   |                                            Jumper-        _____| Connector
+   |   Main chip                                block  _    __|   '  
+   |                                                  | |  |    RJ Connector
+   |                                                  |_|  |    with 110 Ohm
+   |                                                       |__  Terminator
+   |    ___________                                         __|
+   |   |...........|                                       |    RJ-jack
+   |   |...........|    _____                              |    (unused)
+   |   |___________|   |_____|                             |__
+   |  Boot PROM socket IRQ-jumpers                            |_  Diagnostic
+   |________                                       __          _| LED (red)
+            | | | | | | | | | | | | | | | | | | | |  |        |
+            | | | | | | | | | | | | | | | | | | | |  |________|
+                                                              |
+                                                              |
+
+And here are the settings for some of the switches and jumpers on the cards.
+
+
+          I/O
+
+         1 2 3 4 5 6 7 8
+
+2E0----- 0 0 0 1 0 0 0 1
+2F0----- 0 0 0 1 0 0 0 0
+300----- 0 0 0 0 1 1 1 1
+350----- 0 0 0 0 1 1 1 0
+
+"0" in the above example means switch is off "1" means that it is on.
+
+
+    ShMem address.
+
+      1 2 3 4 5 6 7 8
+
+CX00--0 0 1 1 | |   |
+DX00--0 0 1 0       |
+X000--------- 1 1   |
+X400--------- 1 0   |
+X800--------- 0 1   |
+XC00--------- 0 0   
+ENHANCED----------- 1
+COMPATIBLE--------- 0
+
+
+       IRQ
+
+
+   3 4 5 7 2
+   . . . . .
+   . . . . .
+
+
+There is a DIP-switch with 8 switches, used to set the shared memory address
+to be used. The first 6 switches set the address, the 7th doesn't have any
+function, and the 8th switch is used to select "compatible" or "enhanced".
+When I got my two cards, one of them had this switch set to "enhanced". That
+card didn't work at all, it wasn't even recognized by the driver. The other
+card had this switch set to "compatible" and it behaved absolutely normal. I
+guess that the switch on one of the cards, must have been changed accidently
+when the card was taken out of it's former host. The question remains
+unanswered, what is the purpose of the "enhanced" position?
+
+[Avery's note: "enhanced" probably either disables shared memory (use IO
+ports instead) or disables IO ports (use memory addresses instead).  This
+varies by the type of card involved.  I fail to see how either of these
+enhance anything.  Send me more detailed information about this mode, or
+just use "compatible" mode instead.]
+
+
+*****************************************************************************
+
+** Waterloo Microsystems Inc. ?? **
+8-bit card (C) 1985
+-------------------
+  - from Robert Michael Best <rmb117@cs.usask.ca>
+
+[Avery's note: these don't work with my driver for some reason.  These cards
+SEEM to have settings similar to the PDI508Plus, which is
+software-configured and doesn't work with my driver either.  The "Waterloo
+chip" is a boot PROM, probably designed specifically for the University of
+Waterloo.  If you have any further information about this card, please
+e-mail me.]
+
+The probe has not been able to detect the card on any of the J2 settings,
+and I tried them again with the "Waterloo" chip removed.
+ 
+ _____________________________________________________________________
+| \/  \/              ___  __ __                                      |
+| C4  C4     |^|     | M ||  ^  ||^|                                  |
+| --  --     |_|     | 5 ||     || | C3                               |
+| \/  \/      C10    |___||     ||_|                                  | 
+| C4  C4             _  _ |     |                 ??                  | 
+| --  --            | \/ ||     |                                     | 
+|                   |    ||     |                                     | 
+|                   |    ||  C1 |                                     | 
+|                   |    ||     |  \/                            _____|    
+|                   | C6 ||     |  C9                           |     |___ 
+|                   |    ||     |  --                           | BNC |___| 
+|                   |    ||     |          >C7|                 |_____|
+|                   |    ||     |                                     |
+| __ __             |____||_____|       1 2 3     6                   |
+||  ^  |     >C4|                      |o|o|o|o|o|o| J2    >C4|       |
+||     |                               |o|o|o|o|o|o|                  |
+|| C2  |     >C4|                                          >C4|       |
+||     |                                   >C8|                       |
+||     |       2 3 4 5 6 7  IRQ                            >C4|       |
+||_____|      |o|o|o|o|o|o| J3                                        |
+|_______      |o|o|o|o|o|o|                            _______________|
+        |                                             |
+        |_____________________________________________|
+
+C1 -- "COM9026
+       SMC 8638"
+      In a chip socket.
+
+C2 -- "@Copyright
+       Waterloo Microsystems Inc.
+       1985"
+      In a chip Socket with info printed on a label covering a round window
+      showing the circuit inside. (The window indicates it is an EPROM chip.)
+
+C3 -- "COM9032
+       SMC 8643"
+      In a chip socket.
+
+C4 -- "74LS"
+      9 total no sockets.
+
+M5 -- "50006-136
+       20.000000 MHZ
+       MTQ-T1-S3
+       0 M-TRON 86-40"
+      Metallic case with 4 pins, no socket.
+
+C6 -- "MOSTEK@TC8643
+       MK6116N-20
+       MALAYSIA"
+      No socket.
+
+C7 -- No stamp or label but in a 20 pin chip socket.
+
+C8 -- "PAL10L8CN
+       8623"
+      In a 20 pin socket.
+
+C9 -- "PAl16R4A-2CN
+       8641"
+      In a 20 pin socket.
+
+C10 -- "M8640
+          NMC
+        9306N"
+       In an 8 pin socket.
+
+?? -- Some components on a smaller board and attached with 20 pins all 
+      along the side closest to the BNC connector.  The are coated in a dark 
+      resin.
+
+On the board there are two jumper banks labeled J2 and J3. The 
+manufacturer didn't put a J1 on the board. The two boards I have both 
+came with a jumper box for each bank.
+
+J2 -- Numbered 1 2 3 4 5 6. 
+      4 and 5 are not stamped due to solder points.
+       
+J3 -- IRQ 2 3 4 5 6 7
+
+The board itself has a maple leaf stamped just above the irq jumpers 
+and "-2 46-86" beside C2. Between C1 and C6 "ASS 'Y 300163" and "@1986 
+CORMAN CUSTOM ELECTRONICS CORP." stamped just below the BNC connector.
+Below that "MADE IN CANADA"
+
+  
+*****************************************************************************
+
 ** No Name **
 8-bit cards, 16-bit cards
 -------------------------
@@ -1477,7 +2358,7 @@
 
 I have named this ARCnet card "NONAME", since there is no name of any
 manufactor on the Installation manual nor on the shipping box. The only
-hint to the existence of a manufactor at all is written into cupper,
+hint to the existence of a manufacturer at all is written in copper,
 it is "Made in Taiwan"
 
 This description has been written by Juergen Seifert <seifert@htwm.de>
@@ -1851,10 +2732,6 @@
 no manual at all and the only text identifying the manufacturer is 
 "MADE IN TAIWAN R.O.C" printed on the card.
 
-This description was written by Vojtech Pavlik 
-(vpav4328@diana.troja.mff.cuni.cz) using parts of the ARCNET-jumpers 
-README file from Linux kernel 1.2.2. 
-
           ____________________________________________________________
          |                 1 2 3 4 5 6 7 8                            |
          | |o|o| JP1       o|o|o|o|o|o|o|o| ON                        |
@@ -1902,7 +2779,7 @@
 -------------------
 
 The eight switches in SW2 are used to set the node ID. Each node attached
-to the network must have an unique node ID which must be diffrent from 0.
+to the network must have an unique node ID which must not be 0.
 Switch 1 (ID0) serves as the least significant bit (LSB).
 
 Setting one of the switches to Off means "1", On means "0".
@@ -2007,6 +2884,157 @@
 
 
 *****************************************************************************
+
+** No Name **
+(Generic Model 9058)
+--------------------
+  - from Andrew J. Kroll <ag784@freenet.buffalo.edu>
+  - Sorry this sat in my to-do box for so long, Andrew! (yikes - over a
+    year!)
+                                                                      _____
+                                                                     |    <
+                                                                     | .---'
+    ________________________________________________________________ | |
+   |                           |     SW2     |                      |  |
+   |   ___________             |_____________|                      |  |
+   |  |           |              1 2 3 4 5 6                     ___|  |
+   |  >  6116 RAM |         _________                         8 |   |  |
+   |  |___________|        |20MHzXtal|                        7 |   |  |
+   |                       |_________|       __________       6 | S |  |
+   |    74LS373                             |          |-     5 | W |  |
+   |   _________                            |      E   |-     4 |   |  |
+   |   >_______|              ______________|..... P   |-     3 | 3 |  |
+   |                         |              |    : O   |-     2 |   |  |
+   |                         |              |    : X   |-     1 |___|  |
+   |   ________________      |              |    : Y   |-           |  |
+   |  |      SW1       |     |      SL90C65 |    :     |-           |  |
+   |  |________________|     |              |    : B   |-           |  |
+   |    1 2 3 4 5 6 7 8      |              |    : O   |-           |  |
+   |                         |_________o____|..../ A   |-    _______|  |
+   |    ____________________                |      R   |-   |       |------,   
+   |   |                    |               |      D   |-   |  BNC  |   #  |
+   |   > 2764 PROM SOCKET   |               |__________|-   |_______|------'
+   |   |____________________|              _________                |  |
+   |                                       >________| <- 74LS245    |  |
+   |                                                                |  |
+   |___                                               ______________|  |
+       |H H H H H H H H H H H H H H H H H H H H H H H|               | |
+       |U_U_U_U_U_U_U_U_U_U_U_U_U_U_U_U_U_U_U_U_U_U_U|               | |
+                                                                      \|
+Legend:
+
+SL90C65 	ARCNET Controller / Transceiver /Logic
+SW1	1-5:	IRQ Select
+	  6:	ET1
+	  7:	ET2
+	  8:	ROM ENABLE 
+SW2	1-3:    Memory Buffer/PROM Address
+	3-6:	I/O Address Map
+SW3	1-8:	Node ID Select
+BNC		BNC RG62/U Connection 
+		*I* have had success using RG59B/U with *NO* terminators!
+		What gives?!
+
+SW1: Timeouts, Interrupt and ROM
+---------------------------------
+
+To select a hardware interrupt level set one (only one!) of the dip switches
+up (on) SW1...(switches 1-5)
+IRQ3, IRQ4, IRQ5, IRQ7, IRQ2. The Manufacturer's default is IRQ2.
+
+The switches on SW1 labeled EXT1 (switch 6) and EXT2 (switch 7)
+are used to determine the timeout parameters. These two dip switches
+are normally left off (down).
+
+   To enable the 8K Boot PROM position SW1 switch 8 on (UP) labeled ROM.
+   The default is jumper ROM not installed.
+
+
+Setting the I/O Base Address
+----------------------------
+
+The last three switches in switch group SW2 are used to select one
+of eight possible I/O Base addresses using the following table
+
+
+   Switch | Hex I/O
+   4 5 6  | Address
+   -------|--------
+   0 0 0  |  260
+   0 0 1  |  290
+   0 1 0  |  2E0  (Manufacturer's default)
+   0 1 1  |  2F0
+   1 0 0  |  300
+   1 0 1  |  350
+   1 1 0  |  380
+   1 1 1  |  3E0
+
+
+Setting the Base Memory Address (RAM & ROM)
+-------------------------------------------
+
+The memory buffer requires 2K of a 16K block of RAM. The base of this
+16K block can be located in any of eight positions.
+Switches 1-3 of switch group SW2 select the Base of the 16K block.
+(0 = DOWN, 1 = UP)
+I could, however, only verify two settings...
+
+   Switch| Hex RAM | Hex ROM
+   1 2 3 | Address | Address
+   ------|---------|-----------
+   0 0 0 |  E0000  |  E2000
+   0 0 1 |  D0000  |  D2000  (Manufacturer's default)
+   0 1 0 |  ?????  |  ?????
+   0 1 1 |  ?????  |  ?????  
+   1 0 0 |  ?????  |  ?????
+   1 0 1 |  ?????  |  ?????
+   1 1 0 |  ?????  |  ?????
+   1 1 1 |  ?????  |  ?????
+
+
+Setting the Node ID
+-------------------
+
+The eight switches in group SW3 are used to set the node ID.
+Each node attached to the network must have an unique node ID which
+must be diffrent from 0.
+Switch 1 serves as the least significant bit (LSB).
+switches in the DOWN position are OFF (0) and in the UP position are ON (1)
+
+The node ID is the sum of the values of all switches set to "1"  
+These values are:
+    Switch | Value
+    -------|-------
+      1    |   1
+      2    |   2
+      3    |   4
+      4    |   8
+      5    |  16
+      6    |  32
+      7    |  64
+      8    | 128
+
+Some Examples:
+
+    Switch#     |   Hex   | Decimal 
+8 7 6 5 4 3 2 1 | Node ID | Node ID
+----------------|---------|---------
+0 0 0 0 0 0 0 0 |    not allowed  <-.
+0 0 0 0 0 0 0 1 |    1    |    1    | 
+0 0 0 0 0 0 1 0 |    2    |    2    |
+0 0 0 0 0 0 1 1 |    3    |    3    |
+    . . .       |         |         |
+0 1 0 1 0 1 0 1 |   55    |   85    |
+    . . .       |         |         + Don't use 0 or 255!
+1 0 1 0 1 0 1 0 |   AA    |  170    |
+    . . .       |         |         |
+1 1 1 1 1 1 0 1 |   FD    |  253    |
+1 1 1 1 1 1 1 0 |   FE    |  254    |
+1 1 1 1 1 1 1 1 |   FF    |  255  <-'
+  
+
+*****************************************************************************
+
 ** Tiara **
 (model unknown)
 -------------------------
diff -u --recursive --new-file v1.3.56/linux/Documentation/networking/arcnet.txt linux/Documentation/networking/arcnet.txt
--- v1.3.56/linux/Documentation/networking/arcnet.txt	Mon Dec 11 15:41:59 1995
+++ linux/Documentation/networking/arcnet.txt	Thu Jan 11 12:00:43 1996
@@ -1,6 +1,6 @@
 
 ----------------------------------------------------------------------------
-NOTE:  See also README.arcnet-hardware in this directory for jumper-setting
+NOTE:  See also arcnet-hardware.txt in this directory for jumper-setting
 and cabling information if you're like many of us and didn't happen to get a
 manual with your ARCnet card.
 ----------------------------------------------------------------------------
@@ -144,6 +144,9 @@
 	insmod arcnet.o io=0x300 irqnum=2 shmem=0xd0000
 You can also add a num=1, num=2 etc for additional arcnet cards that will
 use arc1, arc2 etc for their device names (instead of the default, arc0).
+
+** NEWS FLASH!  Starting with 2.30 ALPHA, the ARCnet driver can autoprobe
+   even as a module.  So "insmod arcnet.o" by itself should work.
 	
 
 Using the Driver
diff -u --recursive --new-file v1.3.56/linux/Makefile linux/Makefile
--- v1.3.56/linux/Makefile	Mon Jan  8 14:10:27 1996
+++ linux/Makefile	Mon Jan  8 14:10:50 1996
@@ -1,6 +1,6 @@
 VERSION = 1
 PATCHLEVEL = 3
-SUBLEVEL = 56
+SUBLEVEL = 57
 
 ARCH = i386
 
diff -u --recursive --new-file v1.3.56/linux/Rules.make linux/Rules.make
--- v1.3.56/linux/Rules.make	Sat Nov 11 17:41:32 1995
+++ linux/Rules.make	Tue Jan  9 11:29:27 1996
@@ -166,7 +166,7 @@
 $(LX_OBJS) $(OX_OBJS): $(TOPDIR)/include/linux/modversions.h
 	$(CC) $(CFLAGS) -DMODVERSIONS -DEXPORT_SYMTAB -c $(@:.o=.c)
 
-dep fastdep: $(TOPDIR)/include/linux/modversions.h
+dep fastdep $(M_OBJS): $(TOPDIR)/include/linux/modversions.h
 
 endif
 endif
diff -u --recursive --new-file v1.3.56/linux/arch/alpha/config.in linux/arch/alpha/config.in
--- v1.3.56/linux/arch/alpha/config.in	Sat Dec 30 15:50:52 1995
+++ linux/arch/alpha/config.in	Tue Jan  9 11:29:26 1996
@@ -52,7 +52,7 @@
 bool 'Echo console messages on /dev/ttyS1' CONFIG_SERIAL_ECHO
 if [ "$CONFIG_PCI" = "y" ]; then
   bool 'TGA Console Support' CONFIG_TGA_CONSOLE
-  bool 'PCI bridge optimisation (experimental)' CONFIG_PCI_OPTIMIZE
+  bool 'PCI bridge optimization (experimental)' CONFIG_PCI_OPTIMIZE
 fi
 bool 'Networking support' CONFIG_NET
 bool 'System V IPC' CONFIG_SYSVIPC
diff -u --recursive --new-file v1.3.56/linux/arch/alpha/kernel/irq.c linux/arch/alpha/kernel/irq.c
--- v1.3.56/linux/arch/alpha/kernel/irq.c	Tue Dec 26 04:45:34 1995
+++ linux/arch/alpha/kernel/irq.c	Fri Jan 12 12:56:58 1996
@@ -207,7 +207,7 @@
 	} else {
 		cache_806 &= mask;
 		outb(cache_806, 0x806);
-#elif defined(CONFIG_ALPHA_EB66) || defined(CONFIG_ALPHA_EB66P)
+#elif NR_IRQS == 32
 	} else if (irq < 24) {
 		cache_26 &= mask;
 		outb(cache_26, 0x26);
diff -u --recursive --new-file v1.3.56/linux/arch/alpha/mm/init.c linux/arch/alpha/mm/init.c
--- v1.3.56/linux/arch/alpha/mm/init.c	Thu Jan  4 21:54:53 1996
+++ linux/arch/alpha/mm/init.c	Fri Jan 12 08:43:20 1996
@@ -15,6 +15,7 @@
 #include <linux/ptrace.h>
 #include <linux/mman.h>
 #include <linux/mm.h>
+#include <linux/swap.h>
 
 #include <asm/system.h>
 #include <asm/segment.h>
diff -u --recursive --new-file v1.3.56/linux/arch/i386/boot/compressed/misc.c linux/arch/i386/boot/compressed/misc.c
--- v1.3.56/linux/arch/i386/boot/compressed/misc.c	Sun Dec 17 11:43:09 1995
+++ linux/arch/i386/boot/compressed/misc.c	Tue Jan  9 11:29:26 1996
@@ -316,7 +316,7 @@
 	lines = SCREEN_INFO.orig_video_lines;
 	cols = SCREEN_INFO.orig_video_cols;
 
-	if (EXT_MEM_K < 1024) error("<2M of mem\n");
+	if (EXT_MEM_K < 1024) error("Less than 2MB of memory.\n");
 
 	output_data = (char *)0x100000;	/* Points to 1M */
 	makecrc();
diff -u --recursive --new-file v1.3.56/linux/arch/i386/config.in linux/arch/i386/config.in
--- v1.3.56/linux/arch/i386/config.in	Tue Jan  2 16:46:21 1996
+++ linux/arch/i386/config.in	Tue Jan  9 12:26:51 1996
@@ -10,6 +10,7 @@
 if [ "$CONFIG_MODULES" = "y" ]; then
   MODULES=y
   bool 'Set version information on all symbols for modules' CONFIG_MODVERSIONS
+  bool 'Kernel daemon support (e.g. autoload of modules)' CONFIG_KERNELD
 fi
 
 mainmenu_option next_comment
@@ -20,7 +21,7 @@
 bool 'Limit memory to low 16MB' CONFIG_MAX_16M
 bool 'PCI bios support' CONFIG_PCI
 if [ "$CONFIG_PCI" = "y" ]; then
-  bool '   PCI bridge optimisation (experimental)' CONFIG_PCI_OPTIMIZE
+  bool '   PCI bridge optimization (experimental)' CONFIG_PCI_OPTIMIZE
 fi
 bool 'System V IPC' CONFIG_SYSVIPC
 tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF
diff -u --recursive --new-file v1.3.56/linux/arch/i386/defconfig linux/arch/i386/defconfig
--- v1.3.56/linux/arch/i386/defconfig	Tue Jan  2 16:46:21 1996
+++ linux/arch/i386/defconfig	Tue Jan  9 12:26:52 1996
@@ -7,6 +7,7 @@
 #
 CONFIG_MODULES=y
 # CONFIG_MODVERSIONS is not set
+# CONFIG_KERNELD is not set
 
 #
 # General setup
diff -u --recursive --new-file v1.3.56/linux/arch/i386/kernel/entry.S linux/arch/i386/kernel/entry.S
--- v1.3.56/linux/arch/i386/kernel/entry.S	Sat Jan  6 19:10:38 1996
+++ linux/arch/i386/kernel/entry.S	Wed Jan 10 09:27:39 1996
@@ -658,7 +658,7 @@
 	.long SYMBOL_NAME(sys_writev)
 	.long SYMBOL_NAME(sys_getsid)
 	.long SYMBOL_NAME(sys_fdatasync)
-	.long 0
+	.long SYMBOL_NAME(sys_sysctl)
 	.long SYMBOL_NAME(sys_mlock)		/* 150 */
 	.long SYMBOL_NAME(sys_munlock)
 	.long SYMBOL_NAME(sys_mlockall)
diff -u --recursive --new-file v1.3.56/linux/arch/i386/mm/init.c linux/arch/i386/mm/init.c
--- v1.3.56/linux/arch/i386/mm/init.c	Thu Jan  4 21:54:53 1996
+++ linux/arch/i386/mm/init.c	Wed Jan 10 09:27:40 1996
@@ -15,6 +15,7 @@
 #include <linux/ptrace.h>
 #include <linux/mman.h>
 #include <linux/mm.h>
+#include <linux/swap.h>
 #include <linux/smp.h>
 
 #include <asm/system.h>
diff -u --recursive --new-file v1.3.56/linux/arch/mips/config.in linux/arch/mips/config.in
--- v1.3.56/linux/arch/mips/config.in	Sun Dec 17 11:43:10 1995
+++ linux/arch/mips/config.in	Tue Jan  9 11:29:27 1996
@@ -44,7 +44,7 @@
 bool 'Networking support' CONFIG_NET
 #bool 'PCI bios support' CONFIG_PCI
 #if [ "$CONFIG_PCI" = "y" ]; then
-#  bool '   PCI bridge optimisation (experimental)' CONFIG_PCI_OPTIMIZE
+#  bool '   PCI bridge optimization (experimental)' CONFIG_PCI_OPTIMIZE
 #fi
 bool 'System V IPC' CONFIG_SYSVIPC
 
diff -u --recursive --new-file v1.3.56/linux/arch/ppc/config.in linux/arch/ppc/config.in
--- v1.3.56/linux/arch/ppc/config.in	Mon Nov 27 12:48:26 1995
+++ linux/arch/ppc/config.in	Tue Jan  9 11:29:27 1996
@@ -29,7 +29,7 @@
 #bool 'Limit memory to low 16MB' CONFIG_MAX_16M n
 bool 'PCI bios support' CONFIG_PCI y
 if [ "$CONFIG_PCI" = "y" ]; then
-  bool '   PCI bridge optimisation (experimental)' CONFIG_PCI_OPTIMIZE n
+  bool '   PCI bridge optimization (experimental)' CONFIG_PCI_OPTIMIZE n
   if [ "$CONFIG_BLK_DEV_IDE" = "y" ]; then
     bool '   PCI Triton IDE Bus Master DMA support' CONFIG_BLK_DEV_TRITON y
   fi
diff -u --recursive --new-file v1.3.56/linux/drivers/char/Config.in linux/drivers/char/Config.in
--- v1.3.56/linux/drivers/char/Config.in	Wed Dec 27 09:12:13 1995
+++ linux/drivers/char/Config.in	Tue Jan  9 11:29:28 1996
@@ -38,10 +38,13 @@
 fi
 bool 'Watchdog Timer Support'	CONFIG_WATCHDOG
 if [ "$CONFIG_WATCHDOG" = "y" ]; then
-#  bool '   WDT501P Watchdog timer' CONFIG_WDT_501P
-#  if [ "$CONFIG_WDT_501P" = "y" ]; then
-#    bool '       Fan Tachomeeter' CONFIG_WDT_501P_TACHO
-#  fi
-#  bool '   WDT500P Watchdog timer' CONFIG_WDT_500P
-  bool '   Software Watchdog' CONFIG_SOFT_WATCHDOG
+  bool '   WDT Watchdog timer' CONFIG_WDT
+  if [ "$CONFIG_WDT" = "y" ]; then
+     bool '       WDT501 features' CONFIG_WDT_501
+     if [ "$CONFIG_WDT_501" = "y" ]; then
+         bool '       Fan Tachometer' CONFIG_WDT_501_FAN
+     fi
+  else
+     bool '   Software Watchdog' CONFIG_SOFT_WATCHDOG
+  fi
 fi
diff -u --recursive --new-file v1.3.56/linux/drivers/char/wd501p.h linux/drivers/char/wd501p.h
--- v1.3.56/linux/drivers/char/wd501p.h	Thu Jan  1 02:00:00 1970
+++ linux/drivers/char/wd501p.h	Tue Jan  9 11:29:28 1996
@@ -0,0 +1,67 @@
+/*
+ *	Industrial Computer Source WDT500/501 driver for Linux 1.3.x
+ *
+ *	(c) Copyright 1995	CymruNET Ltd
+ *				Innovation Centre
+ *				Singleton Park
+ *				Swansea
+ *				Wales
+ *				UK
+ *				SA2 8PP
+ *
+ *	http://www.cymru.net
+ *
+ *	This driver is provided under the GNU public license, incorporated
+ *	herein by reference. The driver is provided without warranty or 
+ *	support.
+ *
+ *	Release 0.04.
+ *
+ */
+ 
+#define WATCHDOG_MINOR		130	/* Watchdog timer     */
+#define TEMP_MINOR		131	/* Temperature Sensor */
+
+#define WDT_COUNT0		(io+0)
+#define WDT_COUNT1		(io+1)
+#define WDT_COUNT2		(io+2)
+#define WDT_CR			(io+3)
+#define WDT_SR			(io+4)
+#define WDT_RT			(io+5)
+#define WDT_UNUSED		(io+6)
+#define WDT_DC			(io+7)
+
+#define WDC_SR_WCCR		1	/* Active low */
+#define WDC_SR_TGOOD		2
+#define WDC_SR_ISOI0		4
+#define WDC_SR_ISII1		8
+#define WDC_SR_FANGOOD		16
+#define WDC_SR_PSUOVER		32	/* Active low */
+#define WDC_SR_PSUUNDR		64	/* Active low */
+#define WDC_SR_IRQ		128	/* Active low */
+
+/*
+ *	Feature Map 1 is the active high inputs not supported on your card.
+ *	Feature Map 2 is the active low inputs not supported on your card.
+ */
+ 
+#ifdef CONFIG_WDT_501		/* Full board */
+
+#ifdef CONFIG_WDT501_FAN	/* Full board, Fan has no tachometer */
+#define FEATUREMAP1		0
+#else
+#define FEATUREMAP1		WDC_SR_FANGOOD
+#endif
+
+#define FEATUREMAP2		0
+#endif
+
+
+#ifdef CONFIG_WDT500		/* Minimal board */
+#define FEATUREMAP1		(WDC_SR_TGOOD|WDC_SR_FANGOOD)
+#define FEATUREMAP2		(WDC_SR_PSUOVER|WDC_SR_PSUUNDR)
+#endif
+
+#ifndef FEATUREMAP1
+#error "Config option not set"
+#endif
diff -u --recursive --new-file v1.3.56/linux/drivers/char/wdt.c linux/drivers/char/wdt.c
--- v1.3.56/linux/drivers/char/wdt.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/char/wdt.c	Tue Jan  9 11:29:28 1996
@@ -0,0 +1,271 @@
+/*
+ *	Industrial Computer Source WDT500/501 driver for Linux 1.3.x
+ *
+ *	(c) Copyright 1995	CymruNET Ltd
+ *				Innovation Centre
+ *				Singleton Park
+ *				Swansea
+ *				Wales
+ *				UK
+ *				SA2 8PP
+ *
+ *	http://www.cymru.net
+ *
+ *	This driver is provided under the GNU public license, incorporated
+ *	herein by reference. The driver is provided without warranty or 
+ *	support.
+ *
+ *	Release 0.04.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mouse.h>
+#include "wd501p.h"
+#include <linux/malloc.h>
+#include <linux/ioport.h>
+#include <linux/fcntl.h>
+#include <asm/io.h>
+#include <asm/segment.h>
+#include <asm/system.h>
+
+static int wdt_is_open=0;
+
+/*
+ *	You must set these - there is no sane way to probe for this board.
+ */
+ 
+int io=0x240;
+int irq=14;
+
+#define WD_TIMO (100*60)		/* 1 minute */
+
+/*
+ *	Programming suppoort
+ */
+ 
+static void wdt_ctr_mode(int ctr, int mode)
+{
+	ctr<<=6;
+	ctr|=0x30;
+	ctr|=(mode<<1);
+	outb_p(ctr, WDT_CR);
+}
+
+static void wdt_ctr_load(int ctr, int val)
+{
+	outb_p(val&0xFF, WDT_COUNT0+ctr);
+	outb_p(val>>8, WDT_COUNT0+ctr);
+}
+
+/*
+ *	Kernel methods.
+ */
+ 
+static void wdt_interrupt(int irq, struct pt_regs *regs)
+{
+	/*
+	 *	Read the status register see what is up and
+	 *	then printk it.
+	 */
+	 
+	unsigned char status=inb_p(WDT_SR);
+	
+	status|=FEATUREMAP1;
+	status&=~FEATUREMAP2;	
+	
+	printk(KERN_CRIT "WDT status %d\n", status);
+	
+	if(!(status&WDC_SR_TGOOD))
+		printk(KERN_CRIT "Overheat alarm.(%d)\n",inb_p(WDT_RT));
+	if(!(status&WDC_SR_PSUOVER))
+		printk(KERN_CRIT "PSU over voltage.\n");
+	if(!(status&WDC_SR_PSUUNDR))
+		printk(KERN_CRIT "PSU under voltage.\n");
+	if(!(status&WDC_SR_FANGOOD))
+		printk(KERN_CRIT "Possible fan fault.\n");
+	if(!(status&WDC_SR_WCCR))
+#ifdef SOFTWARE_REBOOT
+#ifdef ONLY_TESTING
+		printk(KERN_CRIT "Would Reboot.\n");
+#else		
+		printk(KERN_CRIT "Initiating system reboot.\n");
+		hard_reset_now();
+#endif		
+#else
+		printk(KERN_CRIT "Reset in 5ms.\n");
+#endif		
+}
+
+
+static int wdt_lseek(struct inode *inode, struct file *file, off_t offset, 
+	int origin)
+{
+	return -ESPIPE;
+}
+
+static int wdt_write(struct inode *inode, struct file *file, char *buf, int count)
+{
+	/* Write a watchdog value */
+	inb_p(WDT_DC);
+	wdt_ctr_mode(1,2);
+	wdt_ctr_load(1,WD_TIMO);		/* Timeout */
+	outb_p(0, WDT_DC);
+	return count;
+}
+
+/*
+ *	Read reports the temperature in farenheit
+ */
+ 
+static int wdt_read(struct inode *inode, struct file *file, char *buf, int count)
+{
+	unsigned short c=inb_p(WDT_RT);
+	unsigned char cp;
+	int err;
+	
+	switch(MINOR(inode->i_rdev))
+	{
+		case TEMP_MINOR:
+			err=verify_area(VERIFY_WRITE, buf, 1);
+			if(err)
+				return err;
+			c*=11;
+			c/=15;
+			cp=c;
+			memcpy_tofs(buf,&cp,1);
+			return 1;
+		default:
+			return -EINVAL;
+	}
+}
+
+static int wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
+	unsigned long arg)
+{
+	return -EINVAL;
+}
+
+static int wdt_open(struct inode *inode, struct file *file)
+{
+	switch(MINOR(inode->i_rdev))
+	{
+		case WATCHDOG_MINOR:
+			if(wdt_is_open)
+				return -EBUSY;
+			MOD_INC_USE_COUNT;
+			/*
+			 *	Activate 
+			 */
+	 
+			wdt_is_open=1;
+			inb_p(WDT_DC);		/* Disable */
+			wdt_ctr_mode(0,3);
+			wdt_ctr_mode(1,2);
+			wdt_ctr_mode(2,0);
+			wdt_ctr_load(0, 8948);		/* count at 100Hz */
+			wdt_ctr_load(1,WD_TIMO);	/* Timeout 120 seconds */
+			wdt_ctr_load(2,65535);
+			outb_p(0, WDT_DC);	/* Enable */
+			return 0;
+		case TEMP_MINOR:
+			MOD_INC_USE_COUNT;
+			return 0;
+		default:
+			return -ENODEV;
+	}
+}
+
+static void wdt_release(struct inode *inode, struct file *file)
+{
+	if(MINOR(inode->i_rdev)==WATCHDOG_MINOR)
+	{
+		inb_p(WDT_DC);		/* Disable counters */
+		wdt_ctr_load(2,0);	/* 0 length reset pulses now */
+		wdt_is_open=0;
+	}
+	MOD_DEC_USE_COUNT;
+}
+
+/*
+ *	Kernel Interfaces
+ */
+ 
+ 
+static struct file_operations wdt_fops = {
+	wdt_lseek,
+	wdt_read,
+	wdt_write,
+	NULL,		/* No Readdir */
+	NULL,		/* No Select */
+	wdt_ioctl,
+	NULL,		/* No mmap */
+	wdt_open,
+	wdt_release
+};
+
+static struct mouse wdt_mouse=
+{
+	WATCHDOG_MINOR,
+	"wdt",
+	&wdt_fops
+};
+
+static struct mouse temp_mouse=
+{
+	TEMP_MINOR,
+	"temperature",
+	&wdt_fops
+};
+
+#ifdef MODULE
+
+int init_module(void)
+{
+	printk("WDT501-P module at %X(Interrupt %d)\n", io,irq);
+	if(request_irq(irq, wdt_interrupt, SA_INTERRUPT, "wdt501p"))
+	{
+		printk("IRQ %d is not free.\n", irq);
+		return -EIO;
+	}
+	mouse_register(&wdt_mouse);
+#ifdef CONFIG_WDT_501	
+	mouse_register(&temp_mouse);
+#endif	
+	request_region(io, 8, "wdt501");
+	return 0;
+}
+
+void cleanup_module(void)
+{
+	mouse_deregister(&wdt_mouse);
+#ifdef CONFIG_WDT_501	
+	mouse_deregister(&temp_mouse);
+#endif	
+	release_region(io,8);
+	free_irq(irq);
+}
+
+#else
+
+int wdt_init(void)
+{
+	printk("WDT500/501-P driver at %X(Interrupt %d)\n", io,irq);
+	if(request_irq(irq, wdt_interrupt, SA_INTERRUPT, "wdt501p"))
+	{
+		printk("IRQ %d is not free.\n", irq);
+		return -EIO;
+	}
+	mouse_register(&wdt_mouse);
+	mouse_register(&temp_mouse);
+	request_region(io, 8, "wdt501");
+	return 0;
+}
+
+#endif
diff -u --recursive --new-file v1.3.56/linux/drivers/net/arcnet.c linux/drivers/net/arcnet.c
--- v1.3.56/linux/drivers/net/arcnet.c	Wed Dec 13 09:02:46 1995
+++ linux/drivers/net/arcnet.c	Thu Jan 11 12:00:43 1996
@@ -1,6 +1,6 @@
 /* arcnet.c
-	Written 1994-95 by Avery Pennarun, derived from skeleton.c by Donald
-	Becker.
+	Written 1994-1996 by Avery Pennarun,
+	derived from skeleton.c by Donald Becker.
 
 	Contact Avery at: apenwarr@foxnet.net or
 	RR #5 Pole Line Road, Thunder Bay, ON, Canada P7C 5M9
@@ -16,104 +16,37 @@
         modified by SRC, incorporated herein by reference.
          
 	**********************
+	
+	This is ONLY A SUMMARY.  The complete ChangeLog is available in
+	the full Linux-ARCnet package.
 
+	v2.30 ALPHA (96/01/10)
+	  - Abandoned support for Linux 1.2 to simplify coding and allow me
+	    to take advantage of new features in 1.3.
+	  - Updated cabling/jumpers documentation with MUCH information that
+	    people have recently (and in some cases, not so recently) sent
+	    me.  I don't mind writing documentation, but the jumpers
+	    database is REALLY starting to get dull.
+	  - Autoprobing works as a module now - but ONLY with my fix to
+	    register_netdev and unregister_netdev.  It's also much faster,
+	    and uses the "new-style" IRQ autoprobe routines.  Autoprobe is
+	    still a horrible mess and will be cleaned up further as time
+	    passes.
+	    
 	v2.22 (95/12/08)
-	  - IRQ is always printed as a decimal, instead of a hex number.
-	  - Replaced most remaining printk's with BUGMSG macros.
-	  - Moved variables for VERIFY_ACK from the Outgoing struct into
-	    the lp struct; Outgoing is really for packet-splitting info
-	    only.
-	  - Simplified INTMASK handling using a memory variable to keep a
-	    "running total."
-	  - Tracked down more (maybe the last?) "missed IRQ" messages caused
-	    by TX-done IRQ's occurring WHILE arcnet??_send_packet was in
-	    progress.  These were pretty obscure and mostly harmless.
-	  - Made an actual non-ALPHA release of the thing.
-
-	v2.21 ALPHA (95/11/29)
-	  - "Unknown protocol ID" messages now also indicate the station
-	    which sent the unrecognized packet, to aid in debugging network
-	    confusion.  Also, if anyone knows why Novell servers send packets
-	    with protocol ID 0xEC, be sure to tell me.  For now they're
-	    ignored.
-	  - Rearranged ARC_P_* handling a bit, so it makes slightly more
-	    sense.
-	  - We were clearing irq2dev_map too soon, and causing spurious
-	    "irq %d for unknown device" messages.  Moved all the set/clear
-	    irq2dev_map operations to more intelligent places.
-	  - 1.2.x kernels really didn't work with 2.20 ALPHA.  Maybe this
-	    will fix it.
-	  - Fixed the setting of set_multicast_list.  Since we don't have
-	    multicast support, there's no point in using this at all.
-
-	v2.20 ALPHA (95/11/12)
-	  - Added a bit of protocol confusion to the arc0 code to allow
-	    trxnet-compatible IPX support - and the use of all that new
-	    Linux-IPX stuff (including lwared).  Just tell the ipx_interface
-	    command that arc0 uses 802.3 (other protocols will probably also
-	    work).
-	  - Fixed lp->stats to update tx_packets on all devices, not just
-	    arc0.  Also, changed a lot of the error counting to make more
-	    sense.
-	  - rx_packets on arc0 was being updated for each completely received
-	    packet.  It now updates for each segment received, which makes
-	    more sense. 
-	  - Removed unneeded extra check of dev->start in arcnet_inthandler.
-	  - Included someone else's fixes from kernel 1.3.39.  Does it still
-	    work with kernel 1.2?
-	  - Added a new define to disable PRINTING (not checking) of RECON
-	    messages.
-	  - Separated the duplicated error checking code from the various
-	    arcnet??_send_packet routines into a new function.
-	  - Cleaned up lock variables and ping-pong buffers a bit.  This
-	    should mean more stability, fewer missing packets, and less ugly
-	    debug messages.  Assuming it works.
-	  - Changed the format of debug messages to always include the actual
-	    device name instead of just "arcnet:".  Used more macros to
-	    shorten debugging code even more.
-	  - Finally squashed the "startup NULL pointer" bug.  irq2dev_map
-	    wasn't being cleared to NULL when the driver was closed.
-	  - Improved RECON checking; if a certain number of RECON messages
-	    are received within one minute, a warning message is printed
-	    to the effect of "cabling problem."  One-log-message-per-recon
-	    now defaults to OFF.
-
-	v2.12 ALPHA (95/10/27)
-	  - Tried to improve skb handling and init code to fix problems with
-	    the latest 1.3.x kernels. (We no longer use ether_setup except
-	    in arc0e since they keep coming up with new and improved
-	    incompatibilities for us.)
-
-	v2.11 ALPHA (95/10/25)
-	  - Removed superfluous sti() from arcnet_inthandler.
-	  - "Cleaned up" (?) handling of dev->interrupt for multiple
-	    devices.
-	  - Place includes BEFORE configuration settings (solves some
-	    problems with insmod and undefined symbols)
-	  - Removed race condition in arcnet_open that could cause
-	    packet reception to be disabled until after the first TX.
-	  
-	v2.10 ALPHA (95/09/10)
-	  - Integrated Tomasz Motylewski's new RFC1051 compliant "arc0s"
-	    ([S]imple [S]tandard?) device.  This should make Linux-ARCnet
-	    work with the NetBSD/AmiTCP implementation of this older RFC,
-	    via the arc0s device.
-	  - Decided the current implementation of Multiprotocol ARCnet
-	    involved way too much duplicated code, and tried to share things
-	    a _bit_ more, at least.  This means, pretty much, that any
-	    bugs in the arc0s module are now my fault :)
-	  - Added a new ARCNET_DEBUG_MAX define that sets the highest
-	    debug message level to be included in the driver.  This can
-	    reduce the size of the object file, and probably increases
-	    efficiency a bit.  I get a 0.1 ms speedup in my "ping" times if
-	    I disable all debug messages.  Oh, wow.
-	  - Fixed a long-standing annoyance with some of the power-up debug
-	    messages.  ("IRQ for unknown device" was showing up too often)
+	  - Major cleanups, speedups, and better code-sharing.
+	  - Eliminated/changed many useless/meaningless/scary debug messages
+	    (and, in most cases, the bugs that caused them).
+	  - Better IPX support.
+	  - lp->stats updated properly.
+	  - RECON checking now by default only bugs you if there are an
+	    excessive number (ie. your cable is probably broken).
+	  - New RFC1051-compliant "arc0s" virtual device by Tomasz
+	    Motylewski.
+	  - Excess debug messages can be compiled out for maximum
+	    efficiency.
 
 	v2.00 (95/09/06)
-	  - THIS IS ONLY A SUMMARY.  The complete changelog is available
-	    from me upon request.
-
 	  - ARCnet RECON messages are now detected and logged.  These occur
 	    when a new computer is powered up on the network, or in a
 	    constant stream when the network cable is broken.  Thanks to
@@ -147,29 +80,31 @@
 	  - Initial non-alpha release.
 	
          
-	TO DO:
+	TO DO: (it it just me, or does this list only get longer?)
 	
          - Test in systems with NON-ARCnet network cards, just to see if
            autoprobe kills anything.  Currently, we do cause some NE2000's
            to die.  Autoprobe is also way too slow and verbose, particularly
-           if there aren't any ARCnet cards in the system.  And why shouldn't
-           it work as a module again?
-         - Rewrite autoprobe.
+           if there aren't any ARCnet cards in the system.
+         - Rewrite autoprobe.  Try the Linux-generic-autoirq function instead
+           of the net-specific one.
          - Make sure RESET flag is cleared during an IRQ even if dev->start==0;
            mainly a portability fix.  This will confuse autoprobe a bit, so
            test that too.
          - What about cards with shared memory that can be "turned off?"
            (or that have none at all, like the SMC PC500longboard)
-         - Some newer ARCnets support promiscuous mode, supposedly.  If
-           someone sends me information, I'll try to implement it.
-         - Dump Linux 1.2 support and its ugly #ifdefs.
+         - Autoconfigure PDI5xxPlus cards. (I now have a PDI508Plus to play
+           with temporarily)
+         - Try to implement promiscuous (receive-all-packets) mode available
+           on some newer cards with COM20020 and similar chips.  I don't have
+           one, but SMC sent me the specs.
          - Add support for the new 1.3.x IP header cache features.
          - Support printk's priority levels.
          - Make arcnetE_send_packet use arcnet_prepare_tx for loading the
            packet into ARCnet memory.
          - Move the SKB and memory dump code into separate functions.
          - ATA protocol support?? 
-         - Banyan VINES TCP/IP support??
+         - VINES TCP/IP encapsulation?? (info needed)
 
            
 	Sources:
@@ -179,8 +114,10 @@
 	 - skeleton.c v0.05 dated 11/16/93 by Donald Becker
 	 	(from Linux Kernel 1.1.45)
 	 - RFC's 1201 and 1051 - re: TCP/IP over ARCnet
-	 - The official ARCnet data sheets (!) thanks to Ken Cornetet
-		<kcornete@nyx10.cs.du.edu>
+	 - The official ARCnet COM9026 data sheets (!) thanks to Ken
+		Cornetet <kcornete@nyx10.cs.du.edu>
+	 - Information on some more obscure ARCnet controller chips, thanks
+	   to the nice people at SMC.
 	 - net/inet/eth.c (from kernel 1.1.50) for header-building info.
 	 - Alternate Linux ARCnet source by V.Shergin <vsher@sao.stavropol.su>
 	 - Textual information and more alternate source from Joachim Koenig
@@ -188,7 +125,7 @@
 */
 
 static const char *version =
- "arcnet.c: v2.22 95/12/08 Avery Pennarun <apenwarr@foxnet.net>\n";
+ "arcnet.c: v2.30 ALPHA 96/01/10 Avery Pennarun <apenwarr@foxnet.net>\n";
 
  
 
@@ -196,23 +133,6 @@
 #include <linux/config.h>
 #include <linux/version.h>
 
-/* are we Linux 1.2.x? */
-#if LINUX_VERSION_CODE < 0x10300
-# define LINUX12
-#endif
-
-/* for older kernels with older module.h */
-#ifdef LINUX12 
-# ifdef MODULE
-   char kernel_version[] = UTS_RELEASE;
-# else
-#  undef  MOD_INC_USE_COUNT
-#  define MOD_INC_USE_COUNT
-#  undef  MOD_DEC_USE_COUNT
-#  define MOD_DEC_USE_COUNT
-# endif
-#endif
-
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/types.h>
@@ -231,18 +151,12 @@
 #include <linux/etherdevice.h>
 #include <linux/skbuff.h>
 
-/*#include <linux/config.h>*/		/* For CONFIG_INET */
-
 #include <asm/system.h>
 #include <asm/bitops.h>
 #include <asm/io.h>
 #include <asm/dma.h>
 
-#ifdef LINUX12
-#include "arp.h"
-#else
 #include <net/arp.h>
-#endif
 
 /**************************************************************************/
 
@@ -252,7 +166,7 @@
  * the cable is broken.
  *
  * Define DETECT_RECONFIGS if you want to detect network reconfigurations. 
- * They may be a real nuisance on a larger ARCnet network; if you are a
+ * Recons may be a real nuisance on a larger ARCnet network; if you are a
  * network administrator you probably would like to count them. 
  * Reconfigurations will be recorded in stats.tx_carrier_errors (the last
  * field of the /proc/net/dev file).
@@ -278,8 +192,9 @@
 /* Define this if you want to make sure transmitted packets are "acknowledged"
  * by the destination host, as long as they're not to the broadcast address.
  *
- * That way, if one segment of a split packet doesn't get through, it can
- * be resent immediately rather than confusing the other end.
+ * That way, if one segment of a split packet doesn't get through, it can be
+ * resent immediately rather than confusing the other end.  We don't
+ * actually do that yet, though.
  *
  * Disable this to return to 1.02-style behaviour, if you have problems.
  */
@@ -339,20 +254,6 @@
 #define BUGLVL(x) if ((ARCNET_DEBUG_MAX)&arcnet_debug&(x))
 #define BUGMSG(x,msg,args...) BUGLVL(x) printk("%6s: " msg, dev->name , ## args);
 
-#ifndef HAVE_AUTOIRQ
-/* From auto_irq.c, in ioport.h for later versions. */
-extern void autoirq_setup(int waittime);
-extern int autoirq_report(int waittime);
-/* The map from IRQ number (as passed to the interrupt handler) to
-   'struct device'. */
-extern struct device *irq2dev_map[16];
-#endif
-
-#ifndef HAVE_PORTRESERVE
-#define check_region(ioaddr, size) 		0
-#define	request_region(ioaddr, size)		do ; while (0)
-#endif
-
 /* Some useful multiprotocol macros */
 #define TBUSY lp->adev->tbusy \
 		=lp->edev->tbusy \
@@ -388,9 +289,7 @@
 
 	/* Time needed for various things (in clock ticks, 1/100 sec) */
 	/* We mostly don't bother with these - watch out. */
-#define RESETtime 40		/* reset */
-#define XMITtime 10		/* send (?) */
-#define ACKtime 10		/* acknowledge (?) */
+#define RESETtime 30		/* reset */
 
 	/* these are the max/min lengths of packet data. (including
 	 * ClientData header)
@@ -582,10 +481,9 @@
 
 /* Index to functions, as function prototypes. */
 extern int arcnet_probe(struct device *dev);
-#ifndef MODULE
 static int arcnet_memprobe(struct device *dev,u_char *addr);
 static int arcnet_ioprobe(struct device *dev, short ioaddr);
-#endif
+
 static void arcnet_setup(struct device *dev);
 static int arcnetE_init(struct device *dev);
 static int arcnetS_init(struct device *dev);
@@ -619,19 +517,10 @@
 static void set_multicast_list(struct device *dev);
 */
 	/* functions for header/arp/etc building */
-#ifdef LINUX12
-int arcnetA_header(unsigned char *buff,struct device *dev,
-		unsigned short type,void *daddr,void *saddr,unsigned len,
-		struct sk_buff *skb);
-int arcnetS_header(unsigned char *buff,struct device *dev,
-		unsigned short type,void *daddr,void *saddr,unsigned len,
-		struct sk_buff *skb);
-#else
 int arcnetA_header(struct sk_buff *skb,struct device *dev,
 		unsigned short type,void *daddr,void *saddr,unsigned len);
 int arcnetS_header(struct sk_buff *skb,struct device *dev,
 		unsigned short type,void *daddr,void *saddr,unsigned len);
-#endif
 int arcnetA_rebuild_header(void *eth,struct device *dev,unsigned long raddr,
 		struct sk_buff *skb);
 int arcnetS_rebuild_header(void *eth,struct device *dev,unsigned long raddr,
@@ -665,7 +554,6 @@
 int
 arcnet_probe(struct device *dev)
 {
-#ifndef MODULE
 	/* I refuse to probe anything less than 0x200, because anyone using
 	 * an address like that should probably be shot.
 	 */
@@ -691,13 +579,12 @@
 					/* terminator */
 					0};
 	int base_addr=dev->base_addr, status=0;
-#endif /* MODULE */
-	int delayval;
+	int delayval,ioaddr;
 	struct arcnet_local *lp;
 
 	printk(version);
 	
-#if 0
+#if 1
 	BUGLVL(D_NORMAL)
 	{
 		printk("arcnet: ***\n");
@@ -722,32 +609,34 @@
 	BUGMSG(D_INIT,"given: base %lXh, IRQ %d, shmem %lXh\n",
 			dev->base_addr,dev->irq,dev->mem_start);
 
-#ifndef MODULE
 	if (base_addr > 0x1ff)		/* Check a single specified location. */
 		status=arcnet_ioprobe(dev, base_addr);
 	else if (base_addr > 0)		/* Don't probe at all. */
-		return ENXIO;
+		return -ENXIO;
 	else for (port = &ports[0]; *port; port++)
 	{
-		int ioaddr = *port;
-		if (check_region(ioaddr, ARCNET_TOTAL_SIZE))
+		if (check_region(*port, ARCNET_TOTAL_SIZE))
 		{
 			BUGMSG(D_INIT,"Skipping %Xh because of check_region...\n",
-					ioaddr);
+					*port);
 			continue;
 		}
 
-		status=arcnet_ioprobe(dev, ioaddr);
+		status=arcnet_ioprobe(dev, *port);
 		if (!status) break;
 	}
 	
 	if (status) return status;
+	
+	/* arcnet_ioprobe set this */
+	ioaddr=dev->base_addr;
 
-	/* ioprobe turned out okay.  Now give it a couple seconds to finish
-	 * initializing...
+	/* ioprobe turned out okay.  Now reset the card, so we have
+	 * a test byte to look for in shared memory...
 	 */
 	BUGMSG(D_INIT,"ioprobe okay!  Waiting for reset...\n");
-	JIFFER(100);
+	inb(RESET);
+	JIFFER(RESETtime);
 
 	/* okay, now we have to find the shared memory area. */
 	BUGMSG(D_INIT,"starting memory probe, given %lXh\n",
@@ -766,24 +655,16 @@
 		
 		if (status) return status;
 	}
-#else /* MODULE */
-	if (!dev->base_addr || !dev->irq || !dev->mem_start 
-		|| !dev->rmem_start)
-	{
-		BUGMSG(D_NORMAL,"loadable modules can't autoprobe!\n");
-		BUGMSG(D_NORMAL,"try using io=, irqnum=, and shmem= on the insmod line.\n");
-		BUGMSG(D_NORMAL,"you may also need num= to change the device name. (ie. num=1 for arc1)\n");
-		return ENODEV;
-	}
-#endif
+
 	/* now reserve the irq... */
 	{	
 		int irqval = request_irq(dev->irq, &arcnet_interrupt, 0,
 			"arcnet");
-		if (irqval) {
+		if (irqval)
+		{
 			BUGMSG(D_NORMAL,"unable to get IRQ %d (irqval=%d).\n",
 				dev->irq, irqval);
-			return EAGAIN;
+			return -EIO;
 		}
 		
 		irq2dev_map[dev->irq]=dev;
@@ -827,7 +708,6 @@
 	
 	BUGMSG(D_INIT,"arcnet_probe: resetting card.\n");
 	arcnet_reset(dev);
-	JIFFER(50);
 	BUGMSG(D_NORMAL,"We appear to be station %d (%02Xh)\n",
 			lp->stationid,lp->stationid);
 	if (lp->stationid==0)
@@ -841,18 +721,14 @@
 	dev->hard_header=arcnetA_header;
 	dev->rebuild_header=arcnetA_rebuild_header;
 	
-	#ifdef LINUX12
-	dev->type_trans=arcnetA_type_trans;
-	#endif
-
 	return 0;
 }
 
-#ifndef MODULE
 
 int arcnet_ioprobe(struct device *dev, short ioaddr)
 {
-	int delayval,airq;
+	int airq;
+	unsigned long airqmask;
 
 	BUGMSG(D_INIT,"probing address %Xh\n",ioaddr);
 	BUGMSG(D_INIT," status1=%Xh\n",inb(STATUS));
@@ -861,17 +737,23 @@
 	/* very simple - all we have to do is reset the card, and if there's
 	 * no irq, it's not an ARCnet.  We can also kill two birds with
 	 * one stone because we detect the IRQ at the same time :)
+	 *
+	 * BUT: some newer cards don't seem to IRQ upon reset, or worse,
+	 * don't reset when BASE+0x08 is read.  This code is no longer
+	 * so simple, and in fact quite dangerous.  It should be redesigned.
 	 */
 	 
+#if 0
 	/* reset the card by reading the reset port */
 	inb(RESET);
 	JIFFER(RESETtime);
+#endif
 
 	/* if status port is FF, there's certainly no arcnet... give up. */
 	if (inb(STATUS)==0xFF)
 	{
 		BUGMSG(D_INIT," probe failed.  Status port empty.\n");
-		return ENODEV;
+		return -ENODEV;
 	}
 
 #if 0
@@ -892,7 +774,7 @@
 		{
 			BUGLVL(D_INIT," probe failed.  never-changing command port (%02Xh).\n",
 				initval);
-			return ENODEV;
+			return -ENODEV;
 		}
 	}
 #endif
@@ -905,27 +787,43 @@
 	{
 		BUGMSG(D_INIT," probe failed.  eternal reset flag1...(status=%Xh)\n",
 				inb(STATUS));
-		return ENODEV;
+		return -ENODEV;
 	}
 
+	outb(NORXcmd,COMMAND);
+	outb(0,INTMASK);
+	udelay(1);
+
 	/* set up automatic IRQ detection */
-	autoirq_setup(0);
-	
+	airqmask = probe_irq_on();
+	BUGMSG(D_INIT," airqmask=%lXh\n",airqmask);
 
-	/* if we do this, we're sure to get an IRQ since the card has
-	 * just reset and the NORXflag is on until we tell it to start
-	 * receiving.
+	/* if we do this, we're sure to get an IRQ since the card
+	 * has just reset and the NORXflag is on until we tell it to
+	 * start receiving.
 	 */
 	outb(NORXflag,INTMASK);
-	JIFFER(RESETtime);
+	udelay(1);
 	outb(0,INTMASK);
 
-	/* and turn the reset flag back off */
+	/* and turn the reset flag back off.  On some systems, we NEED to do
+	 * this before we re-enable the IRQ!
+	 */
 	outb(CFLAGScmd|RESETclear|CONFIGclear,COMMAND);
 
-	airq = autoirq_report(0);
-	if (airq)
+	/* get autoirq results */
+	airq = probe_irq_off(airqmask);
+
+	if (airq>0)
+	{
 		BUGMSG(D_INIT," autoirq is %d\n", airq);
+	}
+	else if (airq<0)
+	{
+		BUGMSG(D_INIT," autoirq MAY be %d (please send mail to Avery)\n",
+			-airq);
+		airq=0;
+	}
 
 	/* if there was no autoirq AND the user hasn't set any defaults,
 	 * give up.
@@ -933,30 +831,34 @@
 	if (!airq && !(dev->base_addr && dev->irq))
 	{
 		BUGMSG(D_INIT," probe failed.  no autoirq...\n");
-		return ENODEV;
+		return -ENODEV;
 	}
 
 	/* otherwise we probably have a card.  Let's make sure. */
 	
+#if 0
 	if (inb(STATUS) & RESETflag) /* reset flag on */
 	{
 		/* now we turn the reset bit off */
 		outb(CFLAGScmd|RESETclear|CONFIGclear,COMMAND);
 	}
+#endif
 	
 	if (inb(STATUS) & RESETflag) /* reset flag STILL on */
 	{
 		BUGMSG(D_INIT," probe failed.  eternal reset flag...(status=%Xh)\n",
 				inb(STATUS));
-		return ENODEV;
+		return -ENODEV;
 	}
 	
-	/* okay, we've got a real, live ARCnet on our hands. */
+	/* okay, we've got a real, live ARCnet on our hands.
+	 * I hope.
+	 */
 	if (!dev->base_addr) dev->base_addr=ioaddr;
 	
 	if (dev->irq < 2)		/* "Auto-IRQ" */
 	{
-		/* we already did the autoirq above, so store the values */
+		/* we already did the autoirq above, so store the value */
 		dev->irq=airq;
 	}
 	else if (dev->irq == 2)
@@ -986,7 +888,7 @@
 	{
 		BUGMSG(D_INIT," probe failed.  addr=%lXh, addr[0]=%Xh (not %Xh)\n",
 				(unsigned long)addr,addr[0],TESTvalue);
-		return ENODEV;
+		return -ENODEV;
 	}
 	
 	/* now verify the shared memory writability */
@@ -995,7 +897,7 @@
 	{
 		BUGMSG(D_INIT," probe failed.  addr=%lXh, addr[0]=%Xh (not 42h)\n",
 				(unsigned long)addr,addr[0]);
-	 	return ENODEV;
+	 	return -ENODEV;
 	}
 
 	/* got it!  fill in dev */
@@ -1007,8 +909,6 @@
 	return 0;
 }
 
-#endif /* MODULE */
-
 
 /* Do a hardware reset on the card.
  */
@@ -1133,9 +1033,6 @@
 	dev->hard_start_xmit=arcnetS_send_packet;
 	dev->hard_header=arcnetS_header;
 	dev->rebuild_header=arcnetS_rebuild_header;
-#ifdef LINUX12
-	dev->type_trans=arcnetS_type_trans;
-#endif
 	BUGMSG(D_EXTRA,"ARCnet RFC1051 (NetBSD, AmiTCP) protocol initialized.\n");
 
 	return 0;
@@ -1160,7 +1057,7 @@
 arcnet_open(struct device *dev)
 {
 	struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
-	int ioaddr=dev->base_addr,delayval;
+	int ioaddr=dev->base_addr;
 	
 	if (dev->metric>=1000)
 	{
@@ -1214,10 +1111,14 @@
 	
 	/* make sure we're ready to receive IRQ's.
 	 * arcnet_reset sets this for us, but if we receive one before
-	 * START is set to 1, it could be ignored.
+	 * START is set to 1, it could be ignored.  So, we turn IRQ's
+	 * off, then on again to clean out the IRQ controller.
 	 */
 	outb(0,INTMASK);
-	JIFFER(ACKtime);
+	udelay(1);	/* give it time to set the mask before
+			 * we reset it again. (may not even be
+			 * necessary)
+			 */
 	SETMASK;
 	
 	MOD_INC_USE_COUNT;
@@ -1241,7 +1142,7 @@
 	SETMASK;	/* no IRQ's (except RESET, of course) */
 	outb(NOTXcmd,COMMAND);  /* stop transmit */
 	outb(NORXcmd,COMMAND);	/* disable receive */
-	
+
 	/* reset more flags */
 	INTERRUPT=0;
 	
@@ -2394,9 +2295,7 @@
                		printk("\n");
             	}
 
-		#ifndef LINUX12
 		skb->protocol=arcnetA_type_trans(skb,dev);
-		#endif
 
          	netif_rx(skb);
          }
@@ -2567,9 +2466,7 @@
 	            		}
 
 			
-				#ifndef LINUX12
 				skb->protocol=arcnetA_type_trans(skb,dev);
-				#endif
 				
 	         		netif_rx(skb);
 	         	}
@@ -2616,9 +2513,7 @@
                 printk("\n");
         }
         
-	#ifndef LINUX12
 	skb->protocol=eth_type_trans(skb,dev);
-	#endif
         
         netif_rx(skb);
 }
@@ -2673,9 +2568,7 @@
                		printk("\n");
             	}
 
-		#ifndef LINUX12
 		skb->protocol=arcnetS_type_trans(skb,dev);
-		#endif
 
          	netif_rx(skb);
          }
@@ -2729,21 +2622,11 @@
  * saddr=NULL	means use device source address (always will anyway)
  * daddr=NULL	means leave destination address (eg unresolved arp)
  */
-#ifdef LINUX12
-int arcnetA_header(unsigned char *buff,struct device *dev,
-		unsigned short type,void *daddr,void *saddr,unsigned len,
-		struct sk_buff *skb)
-#else
 int arcnetA_header(struct sk_buff *skb,struct device *dev,
 		unsigned short type,void *daddr,void *saddr,unsigned len)
-#endif
 {
 	struct ClientData *head = (struct ClientData *)
-#ifdef LINUX12
-		buff;
-#else
 		skb_push(skb,dev->hard_header_len);
-#endif
 	struct arcnet_local *lp=(struct arcnet_local *)(dev->priv);
 
 	BUGMSG(D_DURING,"create header from %d to %d; protocol %d (%Xh); size %u.\n",
@@ -2813,21 +2696,11 @@
  * saddr=NULL	means use device source address (always will anyway)
  * daddr=NULL	means leave destination address (eg unresolved arp)
  */
-#ifdef LINUX12
-int arcnetS_header(unsigned char *buff,struct device *dev,
-		unsigned short type,void *daddr,void *saddr,unsigned len,
-		struct sk_buff *skb)
-#else
 int arcnetS_header(struct sk_buff *skb,struct device *dev,
 		unsigned short type,void *daddr,void *saddr,unsigned len)
-#endif
 {
 	struct S_ClientData *head = (struct S_ClientData *)
-#ifdef LINUX12
-		buff;
-#else
 		skb_push(skb,dev->hard_header_len);
-#endif
 	struct arcnet_local *lp=(struct arcnet_local *)(dev->priv);
 
 	/* set the protocol ID according to RFC1051 */
@@ -2959,14 +2832,10 @@
 	struct ClientData *head;
 	struct arcnet_local *lp=(struct arcnet_local *) (dev->priv);
 
-#ifdef LINUX12
-	head=(struct ClientData *)skb->data;
-#else
 	/* Pull off the arcnet header. */
 	skb->mac.raw=skb->data;
 	skb_pull(skb,dev->hard_header_len);
 	head=(struct ClientData *)skb->mac.raw;
-#endif
 	
 	if (head->daddr==0)
 		skb->pkt_type=PACKET_BROADCAST;
@@ -3004,14 +2873,10 @@
 	struct S_ClientData *head;
 	struct arcnet_local *lp=(struct arcnet_local *) (dev->priv);
 
-#ifdef LINUX12
-	head=(struct S_ClientData *)skb->data;
-#else
 	/* Pull off the arcnet header. */
 	skb->mac.raw=skb->data;
 	skb_pull(skb,dev->hard_header_len);
 	head=(struct S_ClientData *)skb->mac.raw;
-#endif
 	
 	if (head->daddr==0)
 		skb->pkt_type=PACKET_BROADCAST;
@@ -3089,8 +2954,18 @@
 void
 cleanup_module(void)
 {
+	int ioaddr=thiscard.base_addr;
+
 	if (thiscard.start) arcnet_close(&thiscard);
-	
+
+	/* Flush TX and disable RX */
+	if (ioaddr)
+	{
+		outb(0,INTMASK);	/* disable IRQ's */
+		outb(NOTXcmd,COMMAND);  /* stop transmit */
+		outb(NORXcmd,COMMAND);	/* disable receive */
+	}
+
 	if (thiscard.irq)
 	{
 		free_irq(thiscard.irq);
diff -u --recursive --new-file v1.3.56/linux/drivers/net/net_init.c linux/drivers/net/net_init.c
--- v1.3.56/linux/drivers/net/net_init.c	Tue Jan  2 16:46:25 1996
+++ linux/drivers/net/net_init.c	Thu Jan 11 12:00:42 1996
@@ -258,11 +258,13 @@
 				}
 		}
 
+		sti();	/* device probes assume interrupts enabled */
 		if (dev->init(dev) != 0) {
 		    if (i < MAX_ETH_CARDS) ethdev_index[i] = NULL;
 			restore_flags(flags);
 			return -EIO;
 		}
+		cli();
 
 		/* Add device to end of chain */
 		if (dev_base) {
@@ -345,6 +347,9 @@
 			break;
 		}
 	}
+
+	restore_flags(flags);
+
 	/* You can i.e use a interfaces in a route though it is not up.
 	   We call close_dev (which is changed: it will down a device even if
 	   dev->flags==0 (but it will not call dev->stop if IFF_UP
@@ -353,8 +358,6 @@
 	   dev_mc_discard(dev), ....
 	*/
 	dev_close(dev);
-
-	restore_flags(flags);
 }
 
 
diff -u --recursive --new-file v1.3.56/linux/drivers/net/sk_g16.c linux/drivers/net/sk_g16.c
--- v1.3.56/linux/drivers/net/sk_g16.c	Mon Nov 27 12:48:30 1995
+++ linux/drivers/net/sk_g16.c	Fri Jan 12 15:08:44 1996
@@ -1753,8 +1753,6 @@
     }
 } /* End of set_multicast_list() */
 
-#endif     
-
 
 
 /*-
diff -u --recursive --new-file v1.3.56/linux/drivers/scsi/aha152x.c linux/drivers/scsi/aha152x.c
--- v1.3.56/linux/drivers/scsi/aha152x.c	Thu Dec 21 08:53:33 1995
+++ linux/drivers/scsi/aha152x.c	Wed Jan 10 09:27:38 1996
@@ -20,9 +20,14 @@
  * General Public License for more details.
  *
  *
- * $Id: aha152x.c,v 1.12 1995/12/16 12:26:07 fischer Exp fischer $
+ * $Id: aha152x.c,v 1.13 1996/01/09 02:15:53 fischer Exp $
  *
  * $Log: aha152x.c,v $
+ * Revision 1.13  1996/01/09  02:15:53  fischer
+ * - some cleanups
+ * - moved request_irq behind controller initialization
+ *   (to avoid spurious interrupts)
+ *
  * Revision 1.12  1995/12/16  12:26:07  fischer
  * - barrier()'s added
  * - configurable RESET delay added
@@ -299,6 +304,8 @@
 
 extern long loops_per_sec;
 
+#define DELAY_DEFAULT 100
+
 /* some additional "phases" for getphase() */
 #define P_BUSFREE  1
 #define P_PARITY   2
@@ -322,7 +329,6 @@
 /* set by aha152x_setup according to the command line */
 static int  setup_count=0;
 static struct aha152x_setup {
-  char *conf;
   int io_port;
   int irq;
   int scsiid;
@@ -333,6 +339,7 @@
 #ifdef DEBUG_AHA152X
   int debug;
 #endif
+  char *conf;
 } setup[2];
 
 static struct Scsi_Host *aha152x_host[IRQS];
@@ -566,7 +573,7 @@
   setup[setup_count].reconnect   = ints[0] >= 4 ? ints[4] : 1;
   setup[setup_count].parity      = ints[0] >= 5 ? ints[5] : 1;
   setup[setup_count].synchronous = ints[0] >= 6 ? ints[6] : 0 /* FIXME: 1 */;
-  setup[setup_count].delay       = ints[0] >= 7 ? ints[7] : 100;
+  setup[setup_count].delay       = ints[0] >= 7 ? ints[7] : DELAY_DEFAULT;
 #ifdef DEBUG_AHA152X
   setup[setup_count].debug       = ints[0] >= 8 ? ints[8] : DEBUG_DEFAULT;
   if(ints[0]>8)
@@ -745,7 +752,10 @@
                   setup[setup_count].reconnect   = conf.cf_tardisc;
                   setup[setup_count].parity      = !conf.cf_parity;
                   setup[setup_count].synchronous = 0 /* FIXME: conf.cf_syncneg */;
+                  setup[setup_count].delay       = DELAY_DEFAULT;
+#ifdef DEBUG_AHA152X
                   setup[setup_count].debug       = DEBUG_DEFAULT;
+#endif
                   setup_count++;
                 }
 	}
@@ -775,7 +785,9 @@
       HOSTDATA(shpnt)->parity            = setup[i].parity;
       HOSTDATA(shpnt)->synchronous       = setup[i].synchronous;
       HOSTDATA(shpnt)->delay             = setup[i].delay;
+#ifdef DEBUG_AHA152X
       HOSTDATA(shpnt)->debug             = setup[i].debug;
+#endif
 
       HOSTDATA(shpnt)->aborting          = 0;
       HOSTDATA(shpnt)->abortion_complete = 0;
@@ -787,29 +799,6 @@
       for(j=0; j<8; j++)
         HOSTDATA(shpnt)->syncrate[j] = 0;
  
-      ok = request_irq(setup[i].irq, aha152x_intr, SA_INTERRUPT, "aha152x");
-  
-  if(ok<0)
-    {
-      if(ok == -EINVAL)
-	{
-              printk("aha152x%d: bad IRQ %d.\n", i, setup[i].irq);
-	   printk("         Contact author.\n");
-	}
-      else
-            if(ok == -EBUSY)
-              printk("aha152x%d: IRQ %d already in use. Configure another.\n",
-        	     i, setup[i].irq);
-	else
-	  {
-                printk("\naha152x%d: Unexpected error code on"
-        	       " requesting IRQ %d.\n", i, setup[i].irq);
-	    printk("         Contact author.\n");
-	  }
-          printk("aha152x: driver needs an IRQ.\n");
-          continue;
-    }
-
       SETPORT(SCSIID, setup[i].scsiid << 4);
       shpnt->this_id=setup[i].scsiid;
   
@@ -842,6 +831,29 @@
   SETPORT(SIMODE1, 0);
 
       SETBITS(DMACNTRL0, INTEN);
+
+      ok = request_irq(setup[i].irq, aha152x_intr, SA_INTERRUPT, "aha152x");
+      
+      if(ok<0)
+        {
+          if(ok == -EINVAL)
+            {
+              printk("aha152x%d: bad IRQ %d.\n", i, setup[i].irq);
+              printk("          Contact author.\n");
+            }
+          else
+            if(ok == -EBUSY)
+              printk("aha152x%d: IRQ %d already in use. Configure another.\n",
+        	     i, setup[i].irq);
+            else
+              {
+                printk("\naha152x%d: Unexpected error code on"
+        	       " requesting IRQ %d.\n", i, setup[i].irq);
+                printk("          Contact author.\n");
+              }
+          printk("aha152x: driver needs an IRQ.\n");
+          continue;
+        }
     }
   
   return (setup_count>0);
diff -u --recursive --new-file v1.3.56/linux/drivers/scsi/aha152x.h linux/drivers/scsi/aha152x.h
--- v1.3.56/linux/drivers/scsi/aha152x.h	Thu Dec 21 08:53:33 1995
+++ linux/drivers/scsi/aha152x.h	Wed Jan 10 09:27:38 1996
@@ -2,7 +2,7 @@
 #define _AHA152X_H
 
 /*
- * $Id: aha152x.h,v 1.12 1995/12/07 01:03:48 fischer Exp fischer $
+ * $Id: aha152x.h,v 1.13 1995/12/16 12:27:23 fischer Exp $
  */
 
 #if defined(__KERNEL__)
@@ -23,7 +23,7 @@
    (unless we support more than 1 cmd_per_lun this should do) */
 #define AHA152X_MAXQUEUE	7		
 
-#define AHA152X_REVID "Adaptec 152x SCSI driver; $Revision: 1.12 $"
+#define AHA152X_REVID "Adaptec 152x SCSI driver; $Revision: 1.13 $"
 
 extern struct proc_dir_entry proc_scsi_aha152x;
 
diff -u --recursive --new-file v1.3.56/linux/drivers/scsi/sg.c linux/drivers/scsi/sg.c
--- v1.3.56/linux/drivers/scsi/sg.c	Sat Dec 30 15:50:54 1995
+++ linux/drivers/scsi/sg.c	Tue Jan  9 11:33:30 1996
@@ -5,9 +5,6 @@
  *  Development Sponsored by Killy Corp. NY NY
  *   
  *  Borrows code from st driver.
- *
- *  Version from 1.3.51 modified by Rick Richardson to fix problem in
- *  detecting whether its a send or a recieve style command (see sg_write)
  */
 #include <linux/module.h>
 
@@ -295,9 +292,6 @@
     wake_up(&scsi_generics[dev].read_wait);
 }
 
-#define SG_SEND 0
-#define SG_REC  1
-
 static int sg_write(struct inode *inode,struct file *filp,const char *buf,int count)
 {
     int			  bsize,size,amt,i;
@@ -305,7 +299,7 @@
     kdev_t		  devt = inode->i_rdev;
     int			  dev = MINOR(devt);
     struct scsi_generic   * device=&scsi_generics[dev];
-    int			  direction;
+    int			  input_size;
     unsigned char	  opcode;
     Scsi_Cmnd		* SCpnt;
     
@@ -342,34 +336,44 @@
     device->complete=0;
     memcpy_fromfs(&device->header,buf,sizeof(struct sg_header));
 
-    /*
-     * fix input size, and see if we are sending data.
-     *
-     * Mod by Rick Richardson (rick@dgii.com):
-     * The original test to see if its a SEND/REC was:
-     *     if( device->header.pack_len > device->header.reply_len )
-     * I haven't a clue why the author thought this would work.  Instead,
-     * I've changed it to see if there is any additional data in this
-     * packet beyond the length of the SCSI command itself.
-     */
     device->header.pack_len=count;
     buf+=sizeof(struct sg_header);
-    size = COMMAND_SIZE(get_user(buf)) + sizeof(struct sg_header);
-    if( device->header.pack_len > size)
+
+    /*
+     * Now we need to grab the command itself from the user's buffer.
+     */
+    opcode = get_user(buf);
+    size=COMMAND_SIZE(opcode);
+    if (opcode >= 0xc0 && device->header.twelve_byte) size = 12;
+
+    /*
+     * Determine buffer size.
+     */
+    input_size = device->header.pack_len - size;
+    if( input_size > device->header.reply_len)
     {
-        bsize = device->header.pack_len;
-        direction = SG_SEND;
+        bsize = input_size;
     } else {
         bsize = device->header.reply_len;
-        direction = SG_REC;
     }
     
     /*
      * Don't include the command header itself in the size.
      */
     bsize-=sizeof(struct sg_header);
+    input_size-=sizeof(struct sg_header);
 
     /*
+     * Verify that the user has actually passed enough bytes for this command.
+     */
+    if( input_size < 0 )
+    {
+	device->pending=0;
+        wake_up( &device->write_wait );
+	return -EIO;
+    }
+    
+    /*
      * Allocate a buffer that is large enough to hold the data
      * that has been requested.  Round up to an even number of sectors,
      * since scsi_malloc allocates in chunks of 512 bytes.
@@ -409,38 +413,12 @@
     printk("device allocated\n");
 #endif    
 
-    /*
-     * Now we need to grab the command itself from the user's buffer.
-     */
     SCpnt->request.rq_dev = devt;
     SCpnt->request.rq_status = RQ_ACTIVE;
     SCpnt->sense_buffer[0]=0;
-    opcode = get_user(buf);
-    size=COMMAND_SIZE(opcode);
-    if (opcode >= 0xc0 && device->header.twelve_byte) size = 12;
     SCpnt->cmd_len = size;
 
     /*
-     * If we are writing data, subtract off the size
-     * of the command itself, to get the amount of actual data
-     * that we need to send to the device.
-     */
-    if( direction == SG_SEND )
-        amt -= size;
-    
-    /*
-     * Verify that the user has actually passed enough bytes for this command.
-     */
-    if( count < (sizeof(struct sg_header) + size) )
-    {
-	device->pending=0;
-        wake_up( &device->write_wait );
-        sg_free( device->buff, device->buff_len );
-	device->buff = NULL;
-	return -EIO;
-    }
-    
-    /*
      * Now copy the SCSI command from the user's address space.
      */
     memcpy_fromfs(cmnd,buf,size);
@@ -451,7 +429,7 @@
      * field also includes the length of the header and the command,
      * so we need to subtract these off.
      */
-    if( direction == SG_SEND )  memcpy_fromfs(device->buff,buf, amt);
+    if (input_size > 0) memcpy_fromfs(device->buff, buf, input_size);
     
     /*
      * Set the LUN field in the command structure.
diff -u --recursive --new-file v1.3.56/linux/fs/buffer.c linux/fs/buffer.c
--- v1.3.56/linux/fs/buffer.c	Mon Jan  8 14:10:27 1996
+++ linux/fs/buffer.c	Fri Jan 12 14:31:14 1996
@@ -26,6 +26,7 @@
 #include <linux/errno.h>
 #include <linux/malloc.h>
 #include <linux/pagemap.h>
+#include <linux/swap.h>
 #include <linux/swapctl.h>
 #include <linux/smp.h>
 #include <linux/smp_lock.h>
@@ -1216,10 +1217,10 @@
 /* =========== Reduce the buffer memory ============= */
 
 /*
- * try_to_free() checks if all the buffers on this particular page
+ * try_to_free_buffer() checks if all the buffers on this particular page
  * are unused, and free's the page if so.
  */
-static int try_to_free(struct buffer_head * bh, struct buffer_head ** bhp,
+int try_to_free_buffer(struct buffer_head * bh, struct buffer_head ** bhp,
 		       int priority)
 {
 	unsigned long page;
@@ -1286,9 +1287,9 @@
 	clear_bit(BH_Has_aged, &bh->b_state);
 
 	if (touched) 
-		touch_page((unsigned long) bh->b_data);
+		touch_page(mem_map + MAP_NR((unsigned long) bh->b_data));
 	else
-		age_page((unsigned long) bh->b_data);
+		age_page(mem_map + MAP_NR((unsigned long) bh->b_data));
 }
 
 /*
@@ -1352,19 +1353,6 @@
  * that are in the 0 - limit address range, for DMA re-allocations.
  * We ignore that right now.
  */
-int shrink_buffers(unsigned int priority, unsigned long limit)
-{
-	if (priority < 2) {
-		sync_buffers(0,0);
-	}
-
-	if(priority == 2) wakeup_bdflush(1);
-
-	if(maybe_shrink_lav_buffers(0)) return 1;
-
-	/* No good candidate size - take any size we can find */
-        return shrink_specific_buffers(priority, 0);
-}
 
 static int shrink_specific_buffers(unsigned int priority, int size)
 {
@@ -1388,7 +1376,7 @@
 			    !bh->b_this_page)
 				 continue;
 			if (!age_of((unsigned long) bh->b_data) &&
-			    try_to_free(bh, &bh, 6))
+			    try_to_free_buffer(bh, &bh, 6))
 				 return 1;
 			if(!bh) break;
 			/* Some interrupt must have used it after we
@@ -1436,7 +1424,7 @@
 			if ((age_of((unsigned long) bh->b_data) >>
 			     (6-priority)) > 0)
 				continue;				
-			if (try_to_free(bh, &bh, 0))
+			if (try_to_free_buffer(bh, &bh, 0))
 				 return 1;
 			if(!bh) break;
 		}
@@ -1855,7 +1843,7 @@
 
 	current->session = 1;
 	current->pgrp = 1;
-	sprintf(current->comm, "kernel bdflush");
+	sprintf(current->comm, "kflushd");
 
 	/*
 	 *	As a kernel thread we want to tamper with system buffers
diff -u --recursive --new-file v1.3.56/linux/fs/devices.c linux/fs/devices.c
--- v1.3.56/linux/fs/devices.c	Tue Dec 26 04:45:37 1995
+++ linux/fs/devices.c	Tue Jan  9 12:26:53 1996
@@ -4,8 +4,11 @@
  * (C) 1993 Matthias Urlichs -- collected common code and tables.
  * 
  *  Copyright (C) 1991, 1992  Linus Torvalds
+ *
+ *  Added kerneld support: Jacques Gelinas and Bjorn Ekwall
  */
 
+#include <linux/config.h>
 #include <linux/fs.h>
 #include <linux/major.h>
 #include <linux/string.h>
@@ -14,6 +17,9 @@
 #include <linux/stat.h>
 #include <linux/fcntl.h>
 #include <linux/errno.h>
+#ifdef CONFIG_KERNELD
+#include <linux/kerneld.h>
+#endif
 
 struct device_struct {
 	const char * name;
@@ -47,19 +53,53 @@
 	}
 	return len;
 }
+/*
+	Return the function table of a device.
+	Load the driver if needed.
+*/
+static struct file_operations * get_fops(
+	unsigned int major,
+	unsigned int maxdev,
+	const char *mangle,		/* String to use to build the module name */
+	struct device_struct tb[])
+{
+	struct file_operations *ret = NULL;
+	if (major < maxdev){
+#ifdef CONFIG_KERNELD
+		/*
+		 * I do get request for device 0. I have no idea why. It happen
+		 * at shutdown time for one. Without the following test, the
+		 * kernel will happily trigger a request_module() which will
+		 * trigger kerneld and modprobe for nothing (since there
+		 * is no device with major number == 0. And furthermore
+		 * it locks the reboot process :-(
+		 *
+		 * Jacques Gelinas (jacques@solucorp.qc.ca)
+		 */
+		if (!tb[major].fops && major != 0) {
+			char name[20];
+			sprintf(name, mangle, major);
+			request_module(name);
+		}
+#endif
+		ret = tb[major].fops;
+	}
+	return ret;
+}
 
+
+/*
+	Return the function table of a device.
+	Load the driver if needed.
+*/
 struct file_operations * get_blkfops(unsigned int major)
 {
-	if (major >= MAX_BLKDEV)
-		return NULL;
-	return blkdevs[major].fops;
+	return get_fops (major,MAX_BLKDEV,"block-major-%d",blkdevs);
 }
 
 struct file_operations * get_chrfops(unsigned int major)
 {
-	if (major >= MAX_CHRDEV)
-		return NULL;
-	return chrdevs[major].fops;
+	return get_fops (major,MAX_CHRDEV,"char-major-%d",chrdevs);
 }
 
 int register_chrdev(unsigned int major, const char * name, struct file_operations *fops)
@@ -170,17 +210,24 @@
  */
 int blkdev_open(struct inode * inode, struct file * filp)
 {
-	int i;
-
-	i = MAJOR(inode->i_rdev);
-	if (i >= MAX_BLKDEV || !blkdevs[i].fops)
-		return -ENODEV;
-	filp->f_op = blkdevs[i].fops;
-	if (filp->f_op->open)
-		return filp->f_op->open(inode,filp);
-	return 0;
+	int ret = -ENODEV;
+	filp->f_op = get_blkfops(MAJOR(inode->i_rdev));
+	if (filp->f_op != NULL){
+		ret = 0;
+		if (filp->f_op->open != NULL)
+			ret = filp->f_op->open(inode,filp);
+	}	
+	return ret;
 }	
 
+void blkdev_release(struct inode * inode)
+{
+	struct file_operations *fops = get_blkfops(MAJOR(inode->i_rdev));
+	if (fops && fops->release)
+		fops->release(inode,NULL);
+}
+
+
 /*
  * Dummy default file-operations: the only thing this does
  * is contain the open that then fills in the correct operations
@@ -223,15 +270,14 @@
  */
 int chrdev_open(struct inode * inode, struct file * filp)
 {
-	int i;
-
-	i = MAJOR(inode->i_rdev);
-	if (i >= MAX_CHRDEV || !chrdevs[i].fops)
-		return -ENODEV;
-	filp->f_op = chrdevs[i].fops;
-	if (filp->f_op->open)
-		return filp->f_op->open(inode,filp);
-	return 0;
+	int ret = -ENODEV;
+	filp->f_op = get_chrfops(MAJOR(inode->i_rdev));
+	if (filp->f_op != NULL){
+		ret = 0;
+		if (filp->f_op->open != NULL)
+			ret = filp->f_op->open(inode,filp);
+	}
+	return ret;
 }
 
 /*
diff -u --recursive --new-file v1.3.56/linux/fs/exec.c linux/fs/exec.c
--- v1.3.56/linux/fs/exec.c	Sat Dec 30 15:50:54 1995
+++ linux/fs/exec.c	Tue Jan  9 12:26:53 1996
@@ -44,6 +44,9 @@
 #include <asm/pgtable.h>
 
 #include <linux/config.h>
+#ifdef CONFIG_KERNELD
+#include <linux/kerneld.h>
+#endif
 
 asmlinkage int sys_exit(int exit_code);
 asmlinkage int sys_brk(unsigned long);
@@ -616,6 +619,7 @@
 	int i;
 	int retval;
 	int sh_bang = 0;
+	int try;
 #ifdef __alpha__
 	int loader = 0;
 #endif
@@ -781,18 +785,29 @@
 	}
 
 	bprm.sh_bang = sh_bang;
-	for (fmt = formats ; fmt ; fmt = fmt->next) {
-		int (*fn)(struct linux_binprm *, struct pt_regs *) = fmt->load_binary;
-		if (!fn)
-			break;
-		retval = fn(&bprm, regs);
-		if (retval >= 0) {
-			iput(bprm.inode);
-			current->did_exec = 1;
-			return retval;
+	for (try=0; try<2; try++) {
+		for (fmt = formats ; fmt ; fmt = fmt->next) {
+			int (*fn)(struct linux_binprm *, struct pt_regs *) = fmt->load_binary;
+			if (!fn)
+				break;
+			retval = fn(&bprm, regs);
+			if (retval >= 0) {
+				iput(bprm.inode);
+				current->did_exec = 1;
+				return retval;
+			}
+			if (retval != -ENOEXEC)
+				break;
 		}
-		if (retval != -ENOEXEC)
+		if (retval != -ENOEXEC) {
 			break;
+#ifdef CONFIG_KERNELD
+		}else{
+			char modname[20];
+			sprintf(modname, "binfmt-%hd", *(short*)(&bprm.buf));
+			request_module(modname);
+#endif
+		}
 	}
 exec_error2:
 	iput(bprm.inode);
diff -u --recursive --new-file v1.3.56/linux/fs/file_table.c linux/fs/file_table.c
--- v1.3.56/linux/fs/file_table.c	Wed Dec 27 09:12:13 1995
+++ linux/fs/file_table.c	Wed Jan 10 09:27:39 1996
@@ -16,6 +16,7 @@
  */
 struct file * first_file = NULL;
 int nr_files = 0;
+int max_files = NR_FILE;
 
 /*
  * Insert a new file structure at the head of the list of available ones.
@@ -116,7 +117,7 @@
 				f->f_version = ++event;
 				return f;
 			}
-	} while (nr_files < NR_FILE && grow_files());
+	} while (nr_files < max_files && grow_files());
 
 	return NULL;
 }
diff -u --recursive --new-file v1.3.56/linux/fs/inode.c linux/fs/inode.c
--- v1.3.56/linux/fs/inode.c	Thu Jan  4 21:54:56 1996
+++ linux/fs/inode.c	Wed Jan 10 09:27:39 1996
@@ -19,7 +19,9 @@
 
 static struct inode * first_inode;
 static struct wait_queue * inode_wait = NULL;
-static int nr_inodes = 0, nr_free_inodes = 0;
+/* Keep these next two contiguous in memory for sysctl.c */
+int nr_inodes = 0, nr_free_inodes = 0;
+int max_inodes = NR_INODE;
 
 static inline int const hashfn(kdev_t dev, unsigned int i)
 {
@@ -455,7 +457,7 @@
 	unsigned long badness = 1000;
 	int i;
 
-	if (nr_inodes < NR_INODE && nr_free_inodes < (nr_inodes >> 1))
+	if (nr_inodes < max_inodes && nr_free_inodes < (nr_inodes >> 1))
 		grow_inodes();
 repeat:
 	inode = first_inode;
@@ -471,7 +473,7 @@
 		}
 	}
 	if (badness)
-		if (nr_inodes < NR_INODE) {
+		if (nr_inodes < max_inodes) {
 			if (grow_inodes() == 0)
 				goto repeat;
 		}
diff -u --recursive --new-file v1.3.56/linux/fs/msdos/inode.c linux/fs/msdos/inode.c
--- v1.3.56/linux/fs/msdos/inode.c	Sun Dec 17 11:43:20 1995
+++ linux/fs/msdos/inode.c	Tue Jan  9 11:29:28 1996
@@ -81,9 +81,7 @@
 
 	*check = 'n';
 	*conversion = 'b';
-	/* Please leave dotsOK as 1, and contact Albert Cahalan if */
-	/* it causes any problems for you. <albert@ccs.neu.edu>    */
-	*dotsOK =1;  /* see note above, and report problems */
+	*dotsOK = 0;
 	*uid = current->uid;
 	*gid = current->gid;
 	*umask = current->fs->umask;
@@ -107,6 +105,12 @@
 			else if (!strcmp(value,"text")) *conversion = 't';
 			else if (!strcmp(value,"auto")) *conversion = 'a';
 			else return 0;
+		}
+		else if (!strcmp(this_char,"dots")) {
+		  *dotsOK = 1;
+		}
+		else if (!strcmp(this_char,"nodots")) {
+		  *dotsOK = 0;
 		}
 		else if (!strcmp(this_char,"dotsOK") && value) {
 			if (!strcmp(value,"yes")) *dotsOK = 1;
diff -u --recursive --new-file v1.3.56/linux/fs/nfs/nfsroot.c linux/fs/nfs/nfsroot.c
--- v1.3.56/linux/fs/nfs/nfsroot.c	Thu Dec 21 08:53:33 1995
+++ linux/fs/nfs/nfsroot.c	Tue Jan  9 11:29:28 1996
@@ -675,7 +675,7 @@
 static struct inode nfs_inode;		/* Inode containing socket */
 static int *rpc_packet = NULL;		/* RPC packet */
 
-extern asmlinkage int sys_socketcall(int call, unsigned long *args);
+extern asmlinkage int sys_socket(int family, int type, int protocol);
 
 
 /*
@@ -684,10 +684,9 @@
 static int root_nfs_open(void)
 {
 	struct file *filp;
-	unsigned long opt[] = { AF_INET, SOCK_DGRAM, IPPROTO_UDP };
 
 	/* Open the socket */
-	if ((nfs_data.fd = sys_socketcall(SYS_SOCKET, opt)) < 0) {
+	if ((nfs_data.fd = sys_socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
 		printk(KERN_ERR "NFS: Cannot open UDP socket\n");
 		return -1;
 	}
diff -u --recursive --new-file v1.3.56/linux/fs/nfs/sock.c linux/fs/nfs/sock.c
--- v1.3.56/linux/fs/nfs/sock.c	Wed Dec 27 09:12:13 1995
+++ linux/fs/nfs/sock.c	Thu Jan 11 11:57:28 1996
@@ -81,13 +81,13 @@
 		if (result == -ETIMEDOUT) {
 			if (server->flags & NFS_MOUNT_SOFT) {
 				printk("NFS server %s not responding, "
-					"still trying.\n", server->hostname);
+					"timed out.\n", server->hostname);
 				result = -EIO;
 				break;
 			}
 			if (!major_timeout_seen) {
 				printk("NFS server %s not responding, "
-					"timed out.\n", server->hostname);
+					"still trying.\n", server->hostname);
 				major_timeout_seen = 1;
 			}
 			if ((timeout.init_timeout <<= 1) >= maxtimeo)
diff -u --recursive --new-file v1.3.56/linux/fs/proc/array.c linux/fs/proc/array.c
--- v1.3.56/linux/fs/proc/array.c	Tue Jan  2 16:46:28 1996
+++ linux/fs/proc/array.c	Tue Jan  9 20:41:30 1996
@@ -45,6 +45,7 @@
 #ifdef CONFIG_APM
 #include <linux/apm_bios.h>
 #endif
+#include <linux/swap.h>
 
 #include <asm/segment.h>
 #include <asm/pgtable.h>
diff -u --recursive --new-file v1.3.56/linux/fs/proc/root.c linux/fs/proc/root.c
--- v1.3.56/linux/fs/proc/root.c	Sat Jan  6 19:10:40 1996
+++ linux/fs/proc/root.c	Wed Jan 10 09:27:39 1996
@@ -16,6 +16,7 @@
 #ifdef CONFIG_APM
 #include <linux/apm_bios.h>
 #endif
+#include <asm/bitops.h>
 
 /*
  * Offset of the first process in the /proc root directory..
@@ -25,6 +26,8 @@
 static int proc_root_readdir(struct inode *, struct file *, void *, filldir_t);
 static int proc_root_lookup(struct inode *,const char *,int,struct inode **);
 
+static unsigned char proc_alloc_map[PROC_NDYNAMIC / 8] = {0};
+
 /*
  * These are the generic /proc directory operations. They
  * use the in-memory "struct proc_dir_entry" tree to parse
@@ -49,7 +52,7 @@
 /*
  * proc directories can do almost nothing..
  */
-static struct inode_operations proc_dir_inode_operations = {
+struct inode_operations proc_dir_inode_operations = {
 	&proc_dir_operations,	/* default net directory file-ops */
 	NULL,			/* create */
 	proc_lookup,		/* lookup */
@@ -139,6 +142,15 @@
 	NULL, &proc_root, NULL
 };
 
+struct proc_dir_entry proc_sys_root = {
+	PROC_SYS, 3, "sys",			/* inode, name */
+	S_IFDIR | S_IRUGO | S_IXUGO, 2, 0, 0,	/* mode, nlink, uid, gid */
+	0, &proc_dir_inode_operations,		/* size, ops */
+	NULL, NULL,				/* get_info, fill_inode */
+	NULL,					/* next */
+	NULL, NULL				/* parent, subdir */
+};
+
 int proc_register(struct proc_dir_entry * dir, struct proc_dir_entry * dp)
 {
 	dp->next = dir->subdir;
@@ -159,6 +171,10 @@
 			dp->next = NULL;
 			if (S_ISDIR(dp->mode))
 				dir->nlink--;
+			if (ino >= PROC_DYNAMIC_FIRST &&
+			    ino < PROC_DYNAMIC_FIRST+PROC_NDYNAMIC)
+				clear_bit(ino-PROC_DYNAMIC_FIRST, 
+					  (void *) proc_alloc_map);
 			return 0;
 		}
 		p = &dp->next;
@@ -166,6 +182,30 @@
 	return -EINVAL;
 }	
 
+static int make_inode_number(void)
+{
+	int i = find_first_zero_bit((void *) proc_alloc_map, PROC_NDYNAMIC);
+	if (i<0 || i>=PROC_NDYNAMIC) 
+		return -1;
+	set_bit(i, (void *) proc_alloc_map);
+	return PROC_DYNAMIC_FIRST + i;
+}
+
+int proc_register_dynamic(struct proc_dir_entry * dir,
+			  struct proc_dir_entry * dp)
+{
+	int i = make_inode_number();
+	if (i < 0)
+		return -EAGAIN;
+	dp->low_ino = i;
+	dp->next = dir->subdir;
+	dp->parent = dir;
+	dir->subdir = dp;
+	if (S_ISDIR(dp->mode))
+		dir->nlink++;
+	return 0;
+}
+
 /*
  * /proc/self:
  */
@@ -261,6 +301,8 @@
 #ifdef CONFIG_SCSI
 	proc_register(&proc_root, &proc_scsi);
 #endif
+
+	proc_register(&proc_root, &proc_sys_root);
 
 #ifdef CONFIG_DEBUG_MALLOC
 	proc_register(&proc_root, &(struct proc_dir_entry) {
diff -u --recursive --new-file v1.3.56/linux/fs/super.c linux/fs/super.c
--- v1.3.56/linux/fs/super.c	Sat Dec 30 15:50:55 1995
+++ linux/fs/super.c	Tue Jan  9 12:26:53 1996
@@ -9,6 +9,8 @@
  *                                   - umount systemcall
  *
  * GK 2/5/95  -  Changed to support mounting the root fs via NFS
+ *
+ *  Added kerneld support: Jacques Gelinas and Bjorn Ekwall
  */
 
 #include <stdarg.h>
@@ -28,11 +30,14 @@
 #include <asm/system.h>
 #include <asm/segment.h>
 #include <asm/bitops.h>
- 
-extern struct file_operations * get_blkfops(unsigned int);
-extern struct file_operations * get_chrfops(unsigned int);
 
+#ifdef CONFIG_KERNELD
+#include <linux/kerneld.h>
+#endif
+ 
 extern void wait_for_keypress(void);
+extern struct file_operations * get_blkfops(unsigned int major);
+extern void blkdev_release (struct inode *);
 
 extern int root_mountflags;
 
@@ -278,11 +283,15 @@
 	
 	if (!name)
 		return fs;
-	while (fs) {
-		if (!strcmp(name,fs->name))
-			break;
-		fs = fs->next;
+	for (fs = file_systems; fs && strcmp(fs->name, name); fs = fs->next)
+		;
+#ifdef CONFIG_KERNELD
+	if (!fs && (request_module(name) == 0)) {
+		for (fs = file_systems; fs && strcmp(fs->name, name); fs = fs->next)
+			;
 	}
+#endif
+
 	return fs;
 }
 
@@ -525,7 +534,6 @@
 	kdev_t dev;
 	int retval;
 	struct inode dummy_inode;
-	struct file_operations * fops;
 
 	if (!suser())
 		return -EPERM;
@@ -557,9 +565,7 @@
 		return -ENXIO;
 	}
 	if (!(retval = do_umount(dev)) && dev != ROOT_DEV) {
-		fops = get_blkfops(MAJOR(dev));
-		if (fops && fops->release)
-			fops->release(inode,NULL);
+		blkdev_release (inode);
 		if (MAJOR(dev) == UNNAMED_MAJOR)
 			put_unnamed_dev(dev);
 	}
diff -u --recursive --new-file v1.3.56/linux/include/asm-alpha/byteorder.h linux/include/asm-alpha/byteorder.h
--- v1.3.56/linux/include/asm-alpha/byteorder.h	Tue Dec 26 04:45:40 1995
+++ linux/include/asm-alpha/byteorder.h	Fri Jan 12 08:20:10 1996
@@ -36,26 +36,28 @@
 {
 	unsigned long int res, t1, t2;
 
-	__asm__
-	    ("bis	%3,%3,%0	# %0 is result; %0=aabbccdd
-	      extlh	%0,5,%1		# %1 = dd000000
-	      zap	%0,0xfd,%2	# %2 = 0000cc00
-	      sll	%2,5,%2		# %2 = 00198000
-	      s8addl	%2,%1,%1	# %1 = ddcc0000
-	      zap	%0,0xfb,%2	# %2 = 00bb0000
-	      srl	%2,8,%2		# %2 = 0000bb00
-	      extbl	%0,3,%0		# %0 = 000000aa
-	      or	%1,%0,%0	# %0 = ddcc00aa
-	      or	%2,%0,%0	# %0 = ddccbbaa"
-	     : "r="(res), "r="(t1), "r="(t2) : "r"(x));
+	__asm__(
+	"# bswap input: %0 (aabbccdd)
+	# output: %0, used %1 %2
+	extlh	%0,5,%1		# %1 = dd000000
+	zap	%0,0xfd,%2	# %2 = 0000cc00
+	sll	%2,5,%2		# %2 = 00198000
+	s8addq	%2,%1,%1	# %1 = ddcc0000
+	zap	%0,0xfb,%2	# %2 = 00bb0000
+	srl	%2,8,%2		# %2 = 0000bb00
+	extbl	%0,3,%0		# %0 = 000000aa
+	or	%1,%0,%0	# %0 = ddcc00aa
+	or	%2,%0,%0	# %0 = ddccbbaa"
+	: "r="(res), "r="(t1), "r="(t2)
+	: "0" (x & 0xffffffffUL));
 	return res;
 }
 
 #define __constant_ntohl(x) \
-((unsigned int)((((unsigned int)(x) & 0x000000ffU) << 24) | \
-		(((unsigned int)(x) & 0x0000ff00U) <<  8) | \
-		(((unsigned int)(x) & 0x00ff0000U) >>  8) | \
-		(((unsigned int)(x) & 0xff000000U) >> 24)))
+   ((unsigned long int)((((x) & 0x000000ffUL) << 24) | \
+			(((x) & 0x0000ff00UL) <<  8) | \
+			(((x) & 0x00ff0000UL) >>  8) | \
+			(((x) & 0xff000000UL) >> 24)))
 
 extern __inline__ unsigned short int
 __ntohs(unsigned short int x)
diff -u --recursive --new-file v1.3.56/linux/include/asm-i386/smp_lock.h linux/include/asm-i386/smp_lock.h
--- v1.3.56/linux/include/asm-i386/smp_lock.h	Thu Jan  4 21:54:58 1996
+++ linux/include/asm-i386/smp_lock.h	Fri Jan 12 07:07:42 1996
@@ -1,5 +1,5 @@
-#ifndef __LINUX_SMPLOCK_H
-#define __LINUX_SMPLOCK_H
+#ifndef __I386_SMPLOCK_H
+#define __I386_SMPLOCK_H
 
 #ifdef __SMP__
 
diff -u --recursive --new-file v1.3.56/linux/include/asm-i386/unistd.h linux/include/asm-i386/unistd.h
--- v1.3.56/linux/include/asm-i386/unistd.h	Sat Jan  6 19:10:40 1996
+++ linux/include/asm-i386/unistd.h	Wed Jan 10 09:27:39 1996
@@ -154,6 +154,7 @@
 #define __NR_writev		146
 #define __NR_getsid		147
 #define __NR_fdatasync		148
+#define __NR__sysctl		149
 
 #define __NR_mlock		150
 #define __NR_munlock		151
diff -u --recursive --new-file v1.3.56/linux/include/linux/fs.h linux/include/linux/fs.h
--- v1.3.56/linux/include/linux/fs.h	Thu Jan  4 21:54:59 1996
+++ linux/include/linux/fs.h	Thu Jan 11 11:57:28 1996
@@ -23,16 +23,22 @@
  * Some programs (notably those using select()) may have to be 
  * recompiled to take full advantage of the new limits..
  */
+
+/* Fixed constants first: */
 #undef NR_OPEN
 #define NR_OPEN 256
 
-#define NR_INODE 2048	/* this should be bigger than NR_FILE */
-#define NR_FILE 1024	/* this can well be larger on a larger system */
-#define NR_SUPER 32
+#define NR_SUPER 64
 #define NR_IHASH 131
 #define BLOCK_SIZE 1024
 #define BLOCK_SIZE_BITS 10
 
+/* And dynamically-tunable limits and defaults: */
+extern int max_inodes, nr_inodes;
+extern int max_files, nr_files;
+#define NR_INODE 2048	/* this should be bigger than NR_FILE */
+#define NR_FILE 1024	/* this can well be larger on a larger system */
+
 #define MAY_EXEC 1
 #define MAY_WRITE 2
 #define MAY_READ 4
@@ -483,10 +489,10 @@
 extern int nr_files;
 extern struct super_block super_blocks[NR_SUPER];
 
-extern int shrink_buffers(unsigned int priority, unsigned long limit);
 extern void refile_buffer(struct buffer_head * buf);
 extern void set_writetime(struct buffer_head * buf, int flag);
 extern void refill_freelist(int size);
+extern int try_to_free_buffer(struct buffer_head*, struct buffer_head**, int);
 
 extern struct buffer_head ** buffer_pages;
 extern int nr_buffers;
diff -u --recursive --new-file v1.3.56/linux/include/linux/kerneld.h linux/include/linux/kerneld.h
--- v1.3.56/linux/include/linux/kerneld.h	Thu Jan  1 02:00:00 1970
+++ linux/include/linux/kerneld.h	Tue Jan  9 13:29:15 1996
@@ -0,0 +1,110 @@
+#ifndef _LINUX_KERNELD_H
+#define _LINUX_KERNELD_H
+
+#define KERNELD_SYSTEM 1
+#define KERNELD_REQUEST_MODULE 2 /* "insmod" */
+#define KERNELD_RELEASE_MODULE 3 /* "rmmod" */
+#define KERNELD_DELAYED_RELEASE_MODULE 4 /* "rmmod" */
+#define KERNELD_CANCEL_RELEASE_MODULE 5 /* "rmmod" */
+#define KERNELD_REQUEST_ROUTE 6 /* from net/ipv4/route.c */
+#define KERNELD_BLANKER 7 /* from drivers/char/console.c */
+
+#define IPC_KERNELD 00040000   /* use the kerneld message channel */
+#define KERNELD_MAXCMD 0x7ffeffff
+#define KERNELD_MINSEQ 0x7fff0000 /* "commands" legal up to 0x7ffeffff */
+#define KERNELD_WAIT 0x80000000
+#define KERNELD_NOWAIT 0
+
+struct kerneld_msg {
+	long mtype;
+	long id;
+#ifdef __KERNEL__
+	char *text;
+#else
+	char text[1];
+#endif /* __KERNEL__ */
+};
+
+#ifdef __KERNEL__
+extern int kerneld_send(int msgtype, int ret_size, int msgsz,
+		const char *text, const char *ret_val);
+
+/*
+ * Request that a module should be loaded.
+ * Wait for the exit status from insmod/modprobe.
+ * If it fails, it fails... at least we tried...
+ */
+static inline int request_module(const char *name)
+{
+	return kerneld_send(KERNELD_REQUEST_MODULE,
+			0 | KERNELD_WAIT,
+			strlen(name), name, NULL);
+}
+
+/*
+ * Request the removal of a module, maybe don't wait for it.
+ * It doesn't matter if the removal fails, now does it?
+ */
+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);
+}
+
+/*
+ * Request a delayed removal of a module, but don't wait for it.
+ * The delay is done by kerneld (default: 60 seconds)
+ */
+static inline int delayed_release_module(const char *name)
+{
+	return kerneld_send(KERNELD_DELAYED_RELEASE_MODULE,
+			0 | KERNELD_NOWAIT,
+			strlen(name), name, NULL);
+}
+
+/*
+ * Attempt to cancel a previous request for removal of a module,
+ * but don't wait for it.
+ * This call can be made if the kernel wants to prevent a delayed
+ * unloading of a module.
+ */
+static inline int cancel_release_module(const char *name)
+{
+	return kerneld_send(KERNELD_CANCEL_RELEASE_MODULE,
+			0 | KERNELD_NOWAIT,
+			strlen(name), name, NULL);
+}
+
+/*
+ * Perform an "inverted" system call, maybe return the exit status
+ */
+static inline int ksystem(const char *cmd, int waitflag)
+{
+	return kerneld_send(KERNELD_SYSTEM,
+			0 | (waitflag?KERNELD_WAIT:KERNELD_NOWAIT),
+			strlen(cmd), cmd, NULL);
+}
+
+/*
+ * Try to create a route, possibly by opening a ppp-connection
+ */
+static inline int kerneld_route(const char *ip_route)
+{
+	return kerneld_send(KERNELD_REQUEST_ROUTE,
+			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" */
+{
+	return kerneld_send(KERNELD_BLANKER,
+			0 | (on_off?KERNELD_NOWAIT:KERNELD_WAIT),
+			strlen(on_off?"on":"off"), on_off?"on":"off", NULL);
+}
+
+#endif /* __KERNEL__ */
+#endif
diff -u --recursive --new-file v1.3.56/linux/include/linux/lists.h linux/include/linux/lists.h
--- v1.3.56/linux/include/linux/lists.h	Thu Jan  1 02:00:00 1970
+++ linux/include/linux/lists.h	Wed Jan 10 09:27:39 1996
@@ -0,0 +1,40 @@
+/*
+ * lists.h:  Simple list macros for Linux
+ */
+
+#define DLNODE(ptype)			       	       	       	\
+	struct {			 			\
+		ptype * dl_prev;	 			\
+		ptype * dl_next;	 			\
+	}
+
+#define DNODE_SINGLE(node) {(node),(node)}
+#define DNODE_NULL {0,0}
+
+#define DLIST_INIT(listnam)	                                \
+	(listnam).dl_prev = &(listnam);				\
+	(listnam).dl_last = &(listnam);
+
+#define DLIST_NEXT(listnam)	listnam.dl_next
+#define DLIST_PREV(listnam)	listnam.dl_prev
+
+#define DLIST_INSERT_AFTER(node, new, listnam)	do {		\
+	(new)->listnam.dl_prev = (node);			\
+	(new)->listnam.dl_next = (node)->listnam.dl_next;	\
+	(node)->listnam.dl_next->listnam.dl_prev = (new);	\
+	(node)->listnam.dl_next = (new);			\
+	} while (0)
+
+#define DLIST_INSERT_BEFORE(node, new, listnam)	do {		\
+	(new)->listnam.dl_next = (node);			\
+	(new)->listnam.dl_prev = (node)->listnam.dl_prev;	\
+	(node)->listnam.dl_prev->listnam.dl_next = (new);	\
+	(node)->listnam.dl_prev = (new);			\
+	} while (0)
+
+#define DLIST_DELETE(node, listnam)	do {		\
+	node->listnam.dl_prev->listnam.dl_next =		\
+		node->listnam.dl_next;				\
+	node->listnam.dl_next->listnam.dl_prev =		\
+		node->listnam.dl_prev;				\
+	} while (0)
diff -u --recursive --new-file v1.3.56/linux/include/linux/mm.h linux/include/linux/mm.h
--- v1.3.56/linux/include/linux/mm.h	Mon Jan  8 14:10:28 1996
+++ linux/include/linux/mm.h	Thu Jan 11 19:17:09 1996
@@ -111,7 +111,8 @@
 		 age:8,
 		 uptodate:1,
 		 error:1,
-		 unused:5,
+		 referenced:1,
+		 unused:4,
 		 reserved:1;
 	unsigned long offset;
 	struct inode *inode;
@@ -127,10 +128,6 @@
  * Free area management
  */
 
-extern int nr_swap_pages;
-extern int nr_free_pages;
-extern int min_free_pages;
-
 #define NR_MEM_LISTS 6
 
 struct mem_list {
@@ -197,15 +194,6 @@
 extern void vfree(void * addr);
 extern int vread(char *buf, char *addr, int count);
 
-/* swap.c */
-
-extern void swap_free(unsigned long);
-extern void swap_duplicate(unsigned long);
-extern void swap_in(struct task_struct *, struct vm_area_struct *, pte_t *, unsigned long id, int write_access);
-
-extern void si_swapinfo(struct sysinfo * val);
-extern void rw_swap_page(int rw, unsigned long nr, char * buf);
-
 /* mmap.c */
 extern unsigned long do_mmap(struct file * file, unsigned long addr, unsigned long len,
 	unsigned long prot, unsigned long flags, unsigned long off);
@@ -221,11 +209,6 @@
 extern unsigned long page_unuse(unsigned long);
 extern int shrink_mmap(int, unsigned long);
 
-#define read_swap_page(nr,buf) \
-	rw_swap_page(READ,(nr),(buf))
-#define write_swap_page(nr,buf) \
-	rw_swap_page(WRITE,(nr),(buf))
-
 #define GFP_BUFFER	0x00
 #define GFP_ATOMIC	0x01
 #define GFP_USER	0x02
@@ -289,69 +272,6 @@
 	if (!vma || end_addr <= vma->vm_start)
 		return NULL;
 	return vma;
-}
-
-/*
- * vm_ops not present page codes for shared memory.
- *
- * Will go away eventually..
- */
-#define SHM_SWP_TYPE 0x40
-
-extern void shm_no_page (ulong *);
-
-/*
- * swap cache stuff (in swap.c)
- */
-#define SWAP_CACHE_INFO
-
-extern unsigned long * swap_cache;
-
-#ifdef SWAP_CACHE_INFO
-extern unsigned long swap_cache_add_total;
-extern unsigned long swap_cache_add_success;
-extern unsigned long swap_cache_del_total;
-extern unsigned long swap_cache_del_success;
-extern unsigned long swap_cache_find_total;
-extern unsigned long swap_cache_find_success;
-#endif
-
-extern inline unsigned long in_swap_cache(unsigned long addr)
-{
-	return swap_cache[MAP_NR(addr)]; 
-}
-
-extern inline long find_in_swap_cache (unsigned long addr)
-{
-	unsigned long entry;
-
-#ifdef SWAP_CACHE_INFO
-	swap_cache_find_total++;
-#endif
-	entry = xchg(swap_cache + MAP_NR(addr), 0);
-#ifdef SWAP_CACHE_INFO
-	if (entry)
-		swap_cache_find_success++;
-#endif	
-	return entry;
-}
-
-extern inline int delete_from_swap_cache(unsigned long addr)
-{
-	unsigned long entry;
-	
-#ifdef SWAP_CACHE_INFO
-	swap_cache_del_total++;
-#endif	
-	entry= xchg(swap_cache + MAP_NR(addr), 0);
-	if (entry)  {
-#ifdef SWAP_CACHE_INFO
-		swap_cache_del_success++;
-#endif
-		swap_free(entry);
-		return 1;
-	}
-	return 0;
 }
 
 #endif /* __KERNEL__ */
diff -u --recursive --new-file v1.3.56/linux/include/linux/module.h linux/include/linux/module.h
--- v1.3.56/linux/include/linux/module.h	Tue Dec 26 04:45:41 1995
+++ linux/include/linux/module.h	Tue Jan  9 12:26:53 1996
@@ -26,6 +26,9 @@
 /* maximum length of module name */
 #define MOD_MAX_NAME 64
 
+/* magic marker for modules inserted from kerneld, to be auto-reaped */
+#define MOD_AUTOCLEAN 0x40000000 /* big enough, but no sign problems... */
+
 /* maximum length of symbol name */
 #define SYM_MAX_NAME 60
 
@@ -90,7 +93,7 @@
 extern long mod_use_count_;
 #define MOD_INC_USE_COUNT      mod_use_count_++
 #define MOD_DEC_USE_COUNT      mod_use_count_--
-#define MOD_IN_USE	       (mod_use_count_ != 0)
+#define MOD_IN_USE	       ((mod_use_count_ & ~MOD_AUTOCLEAN) != 0)
 
 #ifndef __NO_VERSION__
 #include <linux/version.h>
diff -u --recursive --new-file v1.3.56/linux/include/linux/pagemap.h linux/include/linux/pagemap.h
--- v1.3.56/linux/include/linux/pagemap.h	Mon Jan  8 14:10:28 1996
+++ linux/include/linux/pagemap.h	Tue Jan  9 11:29:29 1996
@@ -39,16 +39,6 @@
 
 #define page_hash(inode,offset) page_hash_table[_page_hashfn(inode,offset)]
 
-static inline int page_age_update(struct page * page, int accessed)
-{
-	unsigned int age = page->age;
-	if (accessed)
-		age |= PAGE_AGE_VALUE << 1;
-	age >>= 1;
-	page->age = age;
-	return age > (PAGE_AGE_VALUE >> 1);
-}
-
 static inline struct page * find_page(struct inode * inode, unsigned long offset)
 {
 	struct page *page;
@@ -58,7 +48,7 @@
 			continue;
 		if (page->offset != offset)
 			continue;
-		page->age = PAGE_AGE_VALUE | (page->age >> 1);
+		page->referenced = 1;
 		page->count++;
 		break;
 	}
@@ -84,6 +74,7 @@
 	struct page **p = &page_hash(inode,page->offset);
 
 	page_cache_size++;
+	page->referenced = 1;
 	page->age = PAGE_AGE_VALUE;
 	page->prev_hash = NULL;
 	if ((page->next_hash = *p) != NULL)
diff -u --recursive --new-file v1.3.56/linux/include/linux/proc_fs.h linux/include/linux/proc_fs.h
--- v1.3.56/linux/include/linux/proc_fs.h	Tue Dec 26 04:45:41 1995
+++ linux/include/linux/proc_fs.h	Thu Jan 11 19:17:20 1996
@@ -36,7 +36,8 @@
 	PROC_IOPORTS,
 	PROC_APM,
 	PROC_PROFILE, /* whether enabled or not */
-	PROC_CMDLINE
+	PROC_CMDLINE,
+	PROC_SYS
 };
 
 enum pid_directory_inos {
@@ -123,6 +124,11 @@
 	PROC_SCSI_LAST = (PROC_SCSI_FILE + 16) /* won't ever see more than */
 };                                             /* 16 HBAs in one machine   */
 
+/* Finally, the dynamically allocatable proc entries are reserved: */
+
+#define PROC_DYNAMIC_FIRST 4096
+#define PROC_NDYNAMIC      4096
+
 #define PROC_SUPER_MAGIC 0x9fa0
 
 /*
@@ -153,11 +159,13 @@
 	int (*get_info)(char *, char **, off_t, int, int);
 	void (*fill_inode)(struct inode *);
 	struct proc_dir_entry *next, *parent, *subdir;
+	void *data;
 };
 
 extern struct proc_dir_entry proc_root;
 extern struct proc_dir_entry proc_net;
 extern struct proc_dir_entry proc_scsi;
+extern struct proc_dir_entry proc_sys;
 extern struct proc_dir_entry proc_pid;
 extern struct proc_dir_entry proc_pid_fd;
 
@@ -168,6 +176,8 @@
 extern void proc_net_init(void);
 
 extern int proc_register(struct proc_dir_entry *, struct proc_dir_entry *);
+extern int proc_register_dynamic(struct proc_dir_entry *, 
+				 struct proc_dir_entry *);
 extern int proc_unregister(struct proc_dir_entry *, int);
 
 static inline int proc_net_register(struct proc_dir_entry * x)
@@ -229,10 +239,12 @@
 extern int proc_readdir(struct inode *, struct file *, void *, filldir_t);
 extern int proc_lookup(struct inode *, const char *, int, struct inode **);
 
+extern struct inode_operations proc_dir_inode_operations;
 extern struct inode_operations proc_net_inode_operations;
 extern struct inode_operations proc_netdir_inode_operations;
 extern struct inode_operations proc_scsi_inode_operations;
 extern struct inode_operations proc_mem_inode_operations;
+extern struct inode_operations proc_sys_inode_operations;
 extern struct inode_operations proc_array_inode_operations;
 extern struct inode_operations proc_arraylong_inode_operations;
 extern struct inode_operations proc_kcore_inode_operations;
diff -u --recursive --new-file v1.3.56/linux/include/linux/swap.h linux/include/linux/swap.h
--- v1.3.56/linux/include/linux/swap.h	Fri Jun 16 22:02:55 1995
+++ linux/include/linux/swap.h	Wed Jan 10 09:27:40 1996
@@ -5,4 +5,134 @@
 #define SWAP_FLAG_PRIO_MASK	0x7fff
 #define SWAP_FLAG_PRIO_SHIFT	0
 
+#define MAX_SWAPFILES 8
+
+#ifdef __KERNEL__
+
+#define SWP_USED	1
+#define SWP_WRITEOK	3
+
+struct swap_info_struct {
+	unsigned int flags;
+	kdev_t swap_device;
+	struct inode * swap_file;
+	unsigned char * swap_map;
+	unsigned char * swap_lockmap;
+	int lowest_bit;
+	int highest_bit;
+	int prio;			/* swap priority */
+	int pages;
+	unsigned long max;
+	int next;			/* next entry on swap list */
+};
+
+extern int nr_swap_pages;
+extern int nr_free_pages;
+extern int min_free_pages;
+extern int free_pages_low;
+extern int free_pages_high;
+
+/* Incomplete types for prototype declarations: */
+struct task_struct;
+struct vm_area_struct;
+struct sysinfo;
+
+/* linux/ipc/shm.c */
+extern int shm_swap (int, unsigned long);
+
+/* linux/mm/swap_clock.c */
+extern int try_to_free_page(int, unsigned long);
+
+/* linux/mm/swap_io.c */
+extern void rw_swap_page(int, unsigned long, char *);
+#define read_swap_page(nr,buf) \
+	rw_swap_page(READ,(nr),(buf))
+#define write_swap_page(nr,buf) \
+	rw_swap_page(WRITE,(nr),(buf))
+
+/* linux/mm/swap_mman.c */
+extern void swap_in(struct task_struct *, struct vm_area_struct *,
+		    pte_t *, unsigned long, int);
+
+
+/* linux/mm/swap_state.c */
+extern void show_swap_cache_info(void);
+extern int add_to_swap_cache(unsigned long, unsigned long);
+extern unsigned long init_swap_cache(unsigned long, unsigned long);
+extern void swap_duplicate(unsigned long);
+
+/* linux/mm/swapfile.c */
+extern int nr_swapfiles;
+extern struct swap_info_struct swap_info[];
+void si_swapinfo(struct sysinfo *);
+unsigned long get_swap_page(void);
+extern void swap_free(unsigned long);
+
+/*
+ * vm_ops not present page codes for shared memory.
+ *
+ * Will go away eventually..
+ */
+#define SHM_SWP_TYPE 0x40
+
+extern void shm_no_page (ulong *);
+
+/*
+ * swap cache stuff (in linux/mm/swap_state.c)
+ */
+
+#define SWAP_CACHE_INFO
+
+extern unsigned long * swap_cache;
+
+#ifdef SWAP_CACHE_INFO
+extern unsigned long swap_cache_add_total;
+extern unsigned long swap_cache_add_success;
+extern unsigned long swap_cache_del_total;
+extern unsigned long swap_cache_del_success;
+extern unsigned long swap_cache_find_total;
+extern unsigned long swap_cache_find_success;
+#endif
+
+extern inline unsigned long in_swap_cache(unsigned long addr)
+{
+	return swap_cache[MAP_NR(addr)]; 
+}
+
+extern inline long find_in_swap_cache (unsigned long addr)
+{
+	unsigned long entry;
+
+#ifdef SWAP_CACHE_INFO
+	swap_cache_find_total++;
+#endif
+	entry = xchg(swap_cache + MAP_NR(addr), 0);
+#ifdef SWAP_CACHE_INFO
+	if (entry)
+		swap_cache_find_success++;
+#endif	
+	return entry;
+}
+
+extern inline int delete_from_swap_cache(unsigned long addr)
+{
+	unsigned long entry;
+	
+#ifdef SWAP_CACHE_INFO
+	swap_cache_del_total++;
+#endif	
+	entry= xchg(swap_cache + MAP_NR(addr), 0);
+	if (entry)  {
+#ifdef SWAP_CACHE_INFO
+		swap_cache_del_success++;
+#endif
+		swap_free(entry);
+		return 1;
+	}
+	return 0;
+}
+
+
+#endif /* __KERNEL__*/
+
 #endif /* _LINUX_SWAP_H */
diff -u --recursive --new-file v1.3.56/linux/include/linux/swapctl.h linux/include/linux/swapctl.h
--- v1.3.56/linux/include/linux/swapctl.h	Fri Oct 13 14:44:45 1995
+++ linux/include/linux/swapctl.h	Thu Jan 11 19:17:57 1996
@@ -28,11 +28,20 @@
 	int 	sc_nr_pages_to_free;
 	enum RCL_POLICY	sc_policy;
 } swap_control_v5;
-
 typedef struct swap_control_v5 swap_control_t;
-
 extern swap_control_t swap_control;
 
+typedef struct kswapd_control_v1
+{
+	int	maxpages;
+	int	pages_buff;
+	int	pages_shm;
+	int	pages_mmap;
+	int	pages_swap;
+} kswapd_control_v1;
+typedef kswapd_control_v1 kswapd_control_t;
+extern kswapd_control_t kswapd_ctl;
+
 #define SC_VERSION	1
 #define SC_MAX_VERSION	1
 
@@ -79,24 +88,20 @@
 		return n;
 }
 
-static inline void touch_page(unsigned long addr)
+static inline void touch_page(struct page *page)
 {
-	unsigned char age = mem_map[MAP_NR(addr)].age;
-	if (age < (MAX_PAGE_AGE - PAGE_ADVANCE))
-		age += PAGE_ADVANCE;
+	if (page->age < (MAX_PAGE_AGE - PAGE_ADVANCE))
+		page->age += PAGE_ADVANCE;
 	else
-		age = MAX_PAGE_AGE;
-	mem_map[MAP_NR(addr)].age = age;
+		page->age = MAX_PAGE_AGE;
 }
 
-static inline void age_page(unsigned long addr)
+static inline void age_page(struct page *page)
 {
-	unsigned char age = mem_map[MAP_NR(addr)].age;
-	if (age > PAGE_DECLINE)
-		age -= PAGE_DECLINE;
+	if (page->age > PAGE_DECLINE)
+		page->age -= PAGE_DECLINE;
 	else
-		age = 0;
-	mem_map[MAP_NR(addr)].age = age;
+		page->age = 0;
 }
 
 static inline int age_of(unsigned long addr)
diff -u --recursive --new-file v1.3.56/linux/include/linux/sysctl.h linux/include/linux/sysctl.h
--- v1.3.56/linux/include/linux/sysctl.h	Thu Jan  1 02:00:00 1970
+++ linux/include/linux/sysctl.h	Wed Jan 10 09:27:40 1996
@@ -0,0 +1,182 @@
+/*
+ * sysctl.h: General linux system control interface
+ *
+ * Begun 24 March 1995, Stephen Tweedie
+ */
+
+#include <linux/lists.h>
+
+#ifndef _LINUX_SYSCTL_H
+#define _LINUX_SYSCTL_H
+
+#define CTL_MAXNAME 10
+
+struct __sysctl_args {
+	int *name;
+	int nlen;
+	void *oldval;
+	size_t *oldlenp;
+	void *newval;
+	size_t newlen;
+	unsigned long __unused[4];
+};
+
+/* Define sysctl names first */
+
+/* Top-level names: */
+
+/* For internal pattern-matching use only: */
+#ifdef __KERNEL__
+#define CTL_ANY		-1	/* Matches any name */
+#define CTL_NONE		0
+#endif
+
+#define CTL_KERN	1	/* General kernel info and control */
+#define CTL_VM		2	/* VM management */
+#define CTL_NET		3	/* Networking */
+#define CTL_PROC	4	/* Process info */
+#define CTL_FS		5	/* Filesystems */
+#define CTL_DEBUG	6	/* Debugging */
+#define CTL_DEV		7	/* Devices */
+#define CTL_MAXID	8
+
+/* CTL_KERN names: */
+#define KERN_OSTYPE	1	/* string: system version */
+#define KERN_OSRELEASE	2	/* string: system release */
+#define KERN_OSREV	3	/* int: system revision */
+#define KERN_VERSION	4	/* string: compile time info */
+#define KERN_SECUREMASK 5	/* struct: maximum rights mask */
+#define KERN_PROF 	6	/* table: profiling information */
+#define KERN_NODENAME   7
+#define KERN_DOMAINNAME 8
+#define KERN_NRINODE	9
+#define KERN_MAXINODE	10
+#define KERN_NRFILE	11
+#define KERN_MAXFILE	12
+#define KERN_MAXID	13
+
+/* CTL_VM names: */
+#define VM_SWAPCTL	1	/* struct: Set vm swapping control */
+#define VM_KSWAPD	2	/* struct: control background pagout */
+#define VM_MAXID	3
+
+/* CTL_NET names: */
+
+/* CTL_PROC names: */
+
+/* CTL_FS names: */
+
+/* CTL_DEBUG names: */
+
+/* CTL_DEV names: */
+
+#ifdef __KERNEL__
+
+extern asmlinkage int sys_sysctl(struct __sysctl_args *);
+extern void init_sysctl(void);
+
+typedef struct ctl_table ctl_table;
+
+typedef int ctl_handler (ctl_table *table, int *name, int nlen,
+			 void *oldval, size_t *oldlenp,
+			 void *newval, size_t newlen, 
+			 void **context);
+
+typedef int proc_handler (ctl_table *ctl, int write, struct file * filp,
+			  void *buffer, size_t *lenp);
+
+extern int proc_dostring(ctl_table *, int, struct file *,
+			 void *, size_t *);
+extern int proc_dointvec(ctl_table *, int, struct file *,
+			 void *, size_t *);
+
+extern int do_sysctl (int *name, int nlen,
+		      void *oldval, size_t *oldlenp,
+		      void *newval, size_t newlen);
+
+extern int do_sysctl_strategy (ctl_table *table, 
+			       int *name, int nlen,
+			       void *oldval, size_t *oldlenp,
+			       void *newval, size_t newlen, void ** context);
+
+extern ctl_handler sysctl_string;
+
+extern int do_string (
+	void *oldval, size_t *oldlenp, void *newval, size_t newlen,
+	int rdwr, char *data, size_t max);
+extern int do_int (
+	void *oldval, size_t *oldlenp, void *newval, size_t newlen,
+	int rdwr, int *data);
+extern int do_struct (
+	void *oldval, size_t *oldlenp, void *newval, size_t newlen,
+	int rdwr, void *data, size_t len);
+
+
+/*
+ * Register a set of sysctl names by calling register_sysctl_table
+ * with an initialised array of ctl_table's.  An entry with zero
+ * ctl_name terminates the table.  table->de will be set up by the
+ * registration and need not be initialised in advance.
+ *
+ * sysctl names can be mirrored automatically under /proc/sys.  The
+ * procname supplied controls /proc naming.
+ *
+ * The table's mode will be honoured both for sys_sysctl(2) and
+ * proc-fs access.
+ *
+ * Leaf nodes in the sysctl tree will be represented by a single file
+ * under /proc; non-leaf nodes will be represented by directories.  A
+ * null procname disables /proc mirroring at this node.
+ * 
+ * sysctl(2) can automatically manage read and write requests through
+ * the sysctl table.  The data and maxlen fields of the ctl_table
+ * struct enable minimal validation of the values being written to be
+ * performed, and the mode field allows minimal authentication.
+ * 
+ * More sophisticated management can be enabled by the provision of a
+ * strategy routine with the table entry.  This will be called before
+ * any automatic read or write of the data is performed.
+ * 
+ * The strategy routine may return:
+ * <0: Error occurred (error is passed to user process)
+ * 0:  OK - proceed with automatic read or write.
+ * >0: OK - read or write has been done by the strategy routine, so 
+ *     return immediately.
+ * 
+ * There must be a proc_handler routine for any terminal nodes
+ * mirrored under /proc/sys (non-terminals are handled by a built-in
+ * directory handler).  Several default handlers are available to
+ * cover common cases.
+ */
+
+/* A sysctl table is an array of struct ctl_table: */
+struct ctl_table 
+{
+	int ctl_name;			/* Binary ID */
+	const char *procname;		/* Text ID for /proc/sys, or zero */
+	void *data;
+	int maxlen;
+	mode_t mode;
+	ctl_table *child;
+	proc_handler *proc_handler;	/* Callback for text formatting */
+	ctl_handler *strategy;		/* Callback function for all r/w */
+	struct proc_dir_entry *de;	/* /proc control block */
+};
+
+/* struct ctl_table_header is used to maintain dynamic lists of
+   ctl_table trees. */
+struct ctl_table_header
+{
+	ctl_table *ctl_table;
+	DLNODE(struct ctl_table_header) ctl_entry;	
+};
+
+struct ctl_table_header * register_sysctl_table(ctl_table * table, 
+						int insert_at_head);
+void unregister_sysctl_table(struct ctl_table_header * table);
+
+#else /* __KERNEL__ */
+
+#endif /* __KERNEL__ */
+
+#endif /* _LINUX_SYSCTL_H */
diff -u --recursive --new-file v1.3.56/linux/include/linux/timer.h linux/include/linux/timer.h
--- v1.3.56/linux/include/linux/timer.h	Tue Jul 18 16:28:59 1995
+++ linux/include/linux/timer.h	Wed Jan 10 09:27:40 1996
@@ -14,6 +14,8 @@
  * BEEP_TIMER		console beep timer
  *
  * RS_TIMER		timer for the RS-232 ports
+ *
+ * SWAP_TIMER		timer for the background pageout daemon
  * 
  * HD_TIMER		harddisk timer
  *
@@ -40,6 +42,7 @@
 #define BLANK_TIMER	0
 #define BEEP_TIMER	1
 #define RS_TIMER	2
+#define SWAP_TIMER	3
 
 #define HD_TIMER	16
 #define FLOPPY_TIMER	17
diff -u --recursive --new-file v1.3.56/linux/include/net/route.h linux/include/net/route.h
--- v1.3.56/linux/include/net/route.h	Fri Nov 17 08:42:29 1995
+++ linux/include/net/route.h	Tue Jan  9 12:26:53 1996
@@ -185,6 +185,9 @@
 ;
 #endif
 
+#ifdef CONFIG_KERNELD
+extern struct rtable * ip_rt_route(__u32 daddr, int local);
+#else
 extern __inline__ struct rtable * ip_rt_route(__u32 daddr, int local)
 #ifndef MODULE
 {
@@ -207,6 +210,7 @@
 }
 #else
 ;
+#endif
 #endif
 
 extern __inline__ struct rtable * ip_check_route(struct rtable ** rp,
diff -u --recursive --new-file v1.3.56/linux/init/main.c linux/init/main.c
--- v1.3.56/linux/init/main.c	Thu Jan  4 21:54:59 1996
+++ linux/init/main.c	Wed Jan 10 09:27:40 1996
@@ -44,6 +44,7 @@
 
 static int init(void *);
 extern int bdflush(void *);
+extern int kswapd(void *);
 
 extern void init_IRQ(void);
 extern void init_modules(void);
@@ -51,6 +52,7 @@
 extern long kmalloc_init(long,long);
 extern void sock_init(void);
 extern long pci_init(long, long);
+extern void sysctl_init(void);
 
 extern void swap_setup(char *str, int *ints);
 extern void buff_setup(char *str, int *ints);
@@ -108,7 +110,7 @@
 static void prompt_ramdisk(char *str, int *ints);
 #endif CONFIG_BLK_DEV_RAM
 
-#ifdef CONFIG_SYSVIPC
+#if defined(CONFIG_SYSVIPC) || defined(CONFIG_KERNELD)
 extern void ipc_init(void);
 #endif
 
@@ -626,7 +628,7 @@
 	mem_init(memory_start,memory_end);
 	buffer_init();
 	sock_init();
-#ifdef CONFIG_SYSVIPC
+#if defined(CONFIG_SYSVIPC) || defined(CONFIG_KERNELD)
 	ipc_init();
 #endif
 #ifdef CONFIG_APM
@@ -640,6 +642,7 @@
 #ifdef __SMP__
 	smp_init();
 #endif
+	sysctl_init();
 	/* 
 	 *	We count on the initial thread going ok 
 	 *	Like idlers init is an unlocked kernel thread, which will
@@ -693,6 +696,8 @@
 
 	/* Launch bdflush from here, instead of the old syscall way. */
 	kernel_thread(bdflush, NULL, 0);
+	/* Start the background pageout daemon. */
+	kernel_thread(kswapd, NULL, 0);
 
 	setup();
 
diff -u --recursive --new-file v1.3.56/linux/ipc/Makefile linux/ipc/Makefile
--- v1.3.56/linux/ipc/Makefile	Tue Aug 15 20:39:05 1995
+++ linux/ipc/Makefile	Tue Jan  9 12:26:52 1996
@@ -10,6 +10,10 @@
 O_TARGET := ipc.o
 O_OBJS   := util.o
 
+ifdef CONFIG_KERNELD
+CONFIG_SYSVIPC=1
+endif
+
 ifdef CONFIG_SYSVIPC
 O_OBJS += msg.o sem.o shm.o
 endif
diff -u --recursive --new-file v1.3.56/linux/ipc/msg.c linux/ipc/msg.c
--- v1.3.56/linux/ipc/msg.c	Tue Jul 11 10:03:00 1995
+++ linux/ipc/msg.c	Tue Jan  9 12:26:52 1996
@@ -1,13 +1,18 @@
 /*
  * linux/ipc/msg.c
  * Copyright (C) 1992 Krishna Balasubramanian 
+ *
+ * Kerneld extensions by Bjorn Ekwall <bj0rn@blox.se> in May 1995
+ *
  */
 
+#include <linux/autoconf.h>
 #include <linux/errno.h>
 #include <linux/sched.h>
 #include <linux/msg.h>
 #include <linux/stat.h>
 #include <linux/malloc.h>
+#include <linux/kerneld.h>
 
 #include <asm/segment.h>
 
@@ -24,6 +29,8 @@
 static int used_queues = 0;
 static int max_msqid = 0;
 static struct wait_queue *msg_lock = NULL;
+static int kerneld_msqid = -1;
+static int kerneld_pid;
 
 void msg_init (void)
 {
@@ -36,7 +43,7 @@
 	return;
 }
 
-asmlinkage int sys_msgsnd (int msqid, struct msgbuf *msgp, size_t msgsz, int msgflg)
+static int real_msgsnd (int msqid, struct msgbuf *msgp, size_t msgsz, int msgflg)
 {
 	int id, err;
 	struct msqid_ds *msq;
@@ -48,11 +55,19 @@
 		return -EINVAL;
 	if (!msgp) 
 		return -EFAULT;
-	err = verify_area (VERIFY_READ, msgp->mtext, msgsz);
-	if (err) 
-		return err;
-	if ((mtype = get_user (&msgp->mtype)) < 1)
-		return -EINVAL;
+	/*
+	 * Calls from kernel level (IPC_KERNELD set)
+	 * have the message somewhere in kernel space already!
+	 */
+	if ((msgflg & IPC_KERNELD))
+		mtype = msgp->mtype;
+	else {
+		err = verify_area (VERIFY_READ, msgp->mtext, msgsz);
+		if (err) 
+			return err;
+		if ((mtype = get_user (&msgp->mtype)) < 1)
+			return -EINVAL;
+	}
 	id = (unsigned int) msqid % MSGMNI;
 	msq = msgque [id];
 	if (msq == IPC_UNUSED || msq == IPC_NOID)
@@ -62,8 +77,14 @@
  slept:
 	if (msq->msg_perm.seq != (unsigned int) msqid / MSGMNI) 
 		return -EIDRM;
-	if (ipcperms(ipcp, S_IWUGO)) 
-		return -EACCES;
+	/*
+	 * Non-root processes may send to kerneld! 
+	 * i.e. no permission check if called from the kernel
+	 * otoh we don't want user level non-root snoopers...
+	 */
+	if ((msgflg & IPC_KERNELD) == 0)
+		if (ipcperms(ipcp, S_IWUGO)) 
+			return -EACCES;
 	
 	if (msgsz + msq->msg_cbytes > msq->msg_qbytes) { 
 		/* no space in queue */
@@ -71,6 +92,11 @@
 			return -EAGAIN;
 		if (current->signal & ~current->blocked)
 			return -EINTR;
+		if (intr_count) {
+			/* Very unlikely, but better safe than sorry... */
+			printk("Ouch, kerneld:msgsnd wants to sleep at interrupt!\n");
+			return -EINTR;
+		}
 		interruptible_sleep_on (&msq->wwait);
 		goto slept;
 	}
@@ -80,7 +106,24 @@
 	if (!msgh)
 		return -ENOMEM;
 	msgh->msg_spot = (char *) (msgh + 1);
-	memcpy_fromfs (msgh->msg_spot, msgp->mtext, msgsz); 
+
+	/*
+	 * Calls from kernel level (IPC_KERNELD set)
+	 * have the message somewhere in kernel space already!
+	 */
+	if (msgflg & IPC_KERNELD) {
+		struct kerneld_msg *kdmp = (struct kerneld_msg *)msgp;
+
+		/*
+		 * Note that the kernel supplies a pointer
+		 * but the user-level kerneld uses a char array...
+		 */
+		memcpy(msgh->msg_spot, (char *)(&(kdmp->id)), sizeof(long)); 
+		memcpy(msgh->msg_spot + sizeof(long), kdmp->text,
+			msgsz - sizeof(long)); 
+	}
+	else
+		memcpy_fromfs (msgh->msg_spot, msgp->mtext, msgsz); 
 	
 	if (msgque[id] == IPC_UNUSED || msgque[id] == IPC_NOID
 		|| msq->msg_perm.seq != (unsigned int) msqid / MSGMNI) {
@@ -108,8 +151,7 @@
 	return 0;
 }
 
-asmlinkage int sys_msgrcv (int msqid, struct msgbuf *msgp, size_t msgsz, long msgtyp, 
-		int msgflg)
+static int real_msgrcv (int msqid, struct msgbuf *msgp, size_t msgsz, long msgtyp, int msgflg)
 {
 	struct msqid_ds *msq;
 	struct ipc_perm *ipcp;
@@ -121,9 +163,15 @@
 		return -EINVAL;
 	if (!msgp || !msgp->mtext)
 	    return -EFAULT;
-	err = verify_area (VERIFY_WRITE, msgp->mtext, msgsz);
-	if (err)
-		return err;
+	/*
+	 * Calls from kernel level (IPC_KERNELD set)
+	 * wants the message put in kernel space!
+	 */
+	if ((msgflg & IPC_KERNELD) == 0) {
+		err = verify_area (VERIFY_WRITE, msgp->mtext, msgsz);
+		if (err)
+			return err;
+	}
 
 	id = (unsigned int) msqid % MSGMNI;
 	msq = msgque [id];
@@ -140,6 +188,12 @@
 	while (!nmsg) {
 		if (msq->msg_perm.seq != (unsigned int) msqid / MSGMNI)
 			return -EIDRM;
+		if ((msgflg & IPC_KERNELD) == 0)
+			/*
+			 * Non-root processes may recieve from kerneld! 
+			 * i.e. no permission check if called from the kernel
+			 * otoh we don't want user level non-root snoopers...
+			 */
 		if (ipcperms (ipcp, S_IRUGO))
 			return -EACCES;
 		if (msgtyp == 0) 
@@ -192,8 +246,29 @@
 			msq->msg_cbytes -= nmsg->msg_ts;
 			if (msq->wwait)
 				wake_up (&msq->wwait);
-			put_user (nmsg->msg_type, &msgp->mtype);
-			memcpy_tofs (msgp->mtext, nmsg->msg_spot, msgsz);
+			/*
+			 * Calls from kernel level (IPC_KERNELD set)
+			 * wants the message copied to kernel space!
+			 */
+			if (msgflg & IPC_KERNELD) {
+				struct kerneld_msg *kdmp = (struct kerneld_msg *) msgp;
+
+				memcpy((char *)(&(kdmp->id)),
+					nmsg->msg_spot,
+					sizeof(long)); 
+				/*
+				 * Note that kdmp->text is a pointer
+				 * when called from kernel space!
+				 */
+				if ((msgsz > sizeof(long)) && kdmp->text)
+					memcpy(kdmp->text,
+						nmsg->msg_spot + sizeof(long),
+						msgsz - sizeof(long)); 
+			}
+			else {
+				put_user (nmsg->msg_type, &msgp->mtype);
+				memcpy_tofs (msgp->mtext, nmsg->msg_spot, msgsz);
+			}
 			kfree(nmsg);
 			return msgsz;
 		} else {  /* did not find a message */
@@ -201,12 +276,29 @@
 				return -ENOMSG;
 			if (current->signal & ~current->blocked)
 				return -EINTR; 
+			if (intr_count) {
+				/* Won't happen... */
+				printk("Ouch, kerneld:msgrcv wants to sleep at interrupt!\n");
+				return -EINTR;
+			}
 			interruptible_sleep_on (&msq->rwait);
 		}
 	} /* end while */
 	return -1;
 }
 
+asmlinkage int sys_msgsnd (int msqid, struct msgbuf *msgp, size_t msgsz, int msgflg)
+{
+	/* IPC_KERNELD is used as a marker for kernel calls */
+	return real_msgsnd(msqid, msgp, msgsz, msgflg & ~IPC_KERNELD);
+}
+
+asmlinkage int sys_msgrcv (int msqid, struct msgbuf *msgp, size_t msgsz,
+	long msgtyp, int msgflg)
+{
+	/* IPC_KERNELD is used as a marker for kernel calls */
+	return real_msgrcv (msqid, msgp, msgsz, msgtyp, msgflg & ~IPC_KERNELD);
+}
 
 static int findkey (key_t key)
 {
@@ -272,6 +364,19 @@
 	int id;
 	struct msqid_ds *msq;
 	
+	/*
+	 * If the IPC_KERNELD flag is set, the key is forced to IPC_PRIVATE,
+	 * and a designated kerneld message queue is created/refered to
+	 */
+	if ((msgflg & IPC_KERNELD)) {
+		if (!suser())
+			return -EPERM;
+		if ((kerneld_msqid == -1) && (kerneld_msqid =
+				newque(IPC_PRIVATE, msgflg & S_IRWXU)) >= 0)
+			kerneld_pid = current->pid;
+		return kerneld_msqid;
+	}
+	/* else it is a "normal" request */
 	if (key == IPC_PRIVATE) 
 		return newque(key, msgflg);
 	if ((id = findkey (key)) == -1) { /* key not used */
@@ -433,9 +538,104 @@
 		if (!suser() && current->euid != ipcp->cuid && 
 		    current->euid != ipcp->uid)
 			return -EPERM;
+		/*
+		 * There is only one kerneld message queue,
+		 * mark it as non-existant
+		 */
+		if ((kerneld_msqid >= 0) && (msqid == kerneld_msqid))
+			kerneld_pid = kerneld_msqid = -1;
 		freeque (id); 
 		return 0;
 	default:
 		return -EINVAL;
 	}
+}
+
+/*
+ * We do perhaps need a "flush" for waiting processes,
+ * so that if they are terminated, a call from do_exit
+ * will minimize the possibility of orphaned recieved
+ * messages in the queue.  For now we just make sure
+ * that the queue is shut down whenever kerneld dies.
+ */
+void kerneld_exit(void)
+{
+        if ((current->pid == kerneld_pid) && (kerneld_msqid != -1))
+                sys_msgctl(kerneld_msqid, IPC_RMID, NULL);
+}
+
+/*
+ * Kerneld internal message format/syntax:
+ *
+ * The message type from the kernel to kerneld is used to specify _what_
+ * function we want kerneld to perform. 
+ *
+ * The "normal" message area is divided into a long, followed by a char array.
+ * The long is used to hold the sequence number of the request, which will
+ * be used as the return message type from kerneld back to the kernel.
+ * In the return message, the long will be used to store the exit status
+ * of the kerneld "job", or task.
+ * The character array is used to pass parameters to kerneld and (optional)
+ * return information from kerneld back to the kernel.
+ * It is the responsibility of kerneld and the kernel level caller
+ * to set usable sizes on the parameter/return value array, since
+ * that information is _not_ included in the message format
+ */
+
+/*
+ * The basic kernel level entry point to kerneld.
+ *	msgtype should correspond to a task type for (a) kerneld
+ *	ret_size is the size of the (optional) return _value,
+ *		OR-ed with KERNELD_WAIT if we want an answer
+ *	msgsize is the size (in bytes) of the message, not including
+ *		the long that is always sent first in a kerneld message
+ *	text is the parameter for the kerneld specific task
+ *	ret_val is NULL or the kernel address where an expected answer
+ *		from kerneld should be placed.
+ *
+ * See <linux/kerneld.h> for usage (inline convenience functions)
+ *
+ */
+int kerneld_send(int msgtype, int ret_size, int msgsz,
+		const char *text, const char *ret_val)
+{
+	int status = -ENOSYS;
+#ifdef CONFIG_KERNELD
+	static int id = KERNELD_MINSEQ;
+	struct kerneld_msg kmsp = { msgtype, 0, (char *)text };
+	int msgflg = S_IRUSR | S_IWUSR | IPC_KERNELD | MSG_NOERROR;
+
+	msgsz += sizeof(long);
+	if (ret_size & KERNELD_WAIT) {
+		if (++id <= 0)
+			id = KERNELD_MINSEQ;
+		kmsp.id = id;
+	}
+
+	if (kerneld_msqid == -1)
+		return -ENODEV;
+
+	status = real_msgsnd(kerneld_msqid, (struct msgbuf *)&kmsp, msgsz, msgflg);
+	if ((status >= 0) && (ret_size & KERNELD_WAIT)) {
+		ret_size &= ~KERNELD_WAIT;
+		if (intr_count) {
+			/*
+			 * Do not wait for an answer at interrupt-time!
+			 * OK, so fake it...
+			 * If the kerneld request failed in user-space
+			 * we will find out eventually, and retry again!
+			 */
+			return 0; /* i.e. say that it worked... */
+		}
+		/* else */
+		kmsp.text = (char *)ret_val;
+		status = real_msgrcv(kerneld_msqid, (struct msgbuf *)&kmsp,
+				sizeof(long) + ((ret_val)?ret_size:0),
+				kmsp.id, msgflg);
+		if (status > 0) /* a valid answer contains at least a long */
+			status = kmsp.id;
+	}
+
+#endif /* CONFIG_KERNELD */
+	return status;
 }
diff -u --recursive --new-file v1.3.56/linux/ipc/shm.c linux/ipc/shm.c
--- v1.3.56/linux/ipc/shm.c	Wed Dec 13 09:02:47 1995
+++ linux/ipc/shm.c	Tue Jan  9 20:41:30 1996
@@ -12,6 +12,7 @@
 #include <linux/shm.h>
 #include <linux/stat.h>
 #include <linux/malloc.h>
+#include <linux/swap.h>
 
 #include <asm/segment.h>
 #include <asm/pgtable.h>
diff -u --recursive --new-file v1.3.56/linux/ipc/util.c linux/ipc/util.c
--- v1.3.56/linux/ipc/util.c	Tue Jun 27 14:11:47 1995
+++ linux/ipc/util.c	Tue Jan  9 12:26:52 1996
@@ -13,7 +13,7 @@
 #include <linux/shm.h>
 #include <linux/stat.h>
 
-#ifdef CONFIG_SYSVIPC
+#if defined(CONFIG_SYSVIPC) || defined(CONFIG_KERNELD)
 
 extern void sem_init (void), msg_init (void), shm_init (void);
 
diff -u --recursive --new-file v1.3.56/linux/kernel/Makefile linux/kernel/Makefile
--- v1.3.56/linux/kernel/Makefile	Sat Dec 30 15:50:55 1995
+++ linux/kernel/Makefile	Wed Jan 10 09:27:39 1996
@@ -13,7 +13,7 @@
 O_TARGET := kernel.o
 O_OBJS    = sched.o dma.o fork.o exec_domain.o panic.o printk.o sys.o \
 	module.o exit.o signal.o itimer.o info.o time.o softirq.o \
-	resource.o
+	resource.o sysctl.o
 
 ifeq ($(CONFIG_MODULES),y)
 OX_OBJS = ksyms.o
diff -u --recursive --new-file v1.3.56/linux/kernel/exit.c linux/kernel/exit.c
--- v1.3.56/linux/kernel/exit.c	Thu Jan  4 21:54:59 1996
+++ linux/kernel/exit.c	Tue Jan  9 12:26:52 1996
@@ -18,6 +18,7 @@
 
 #include <asm/segment.h>
 extern void sem_exit (void);
+extern void kerneld_exit(void);
 
 int getrusage(struct task_struct *, int, struct rusage *);
 
@@ -511,6 +512,7 @@
 	current->flags |= PF_EXITING;
 	del_timer(&current->real_timer);
 	sem_exit();
+	kerneld_exit();
 	exit_mm();
 	__exit_files(current);
 	__exit_fs(current);
diff -u --recursive --new-file v1.3.56/linux/kernel/info.c linux/kernel/info.c
--- v1.3.56/linux/kernel/info.c	Mon Sep 18 14:54:10 1995
+++ linux/kernel/info.c	Tue Jan  9 20:41:30 1996
@@ -13,6 +13,7 @@
 #include <linux/unistd.h>
 #include <linux/types.h>
 #include <linux/mm.h>
+#include <linux/swap.h>
 
 asmlinkage int sys_sysinfo(struct sysinfo *info)
 {
diff -u --recursive --new-file v1.3.56/linux/kernel/ksyms.c linux/kernel/ksyms.c
--- v1.3.56/linux/kernel/ksyms.c	Sat Jan  6 19:10:42 1996
+++ linux/kernel/ksyms.c	Wed Jan 10 09:27:39 1996
@@ -40,6 +40,7 @@
 #include <linux/random.h>
 #include <linux/mount.h>
 #include <linux/pagemap.h>
+#include <linux/sysctl.h>
 
 extern unsigned char aux_device_present, kbd_read_mask;
 
@@ -102,7 +103,9 @@
 #if defined(CONFIG_PROC_FS)
 #include <linux/proc_fs.h>
 #endif
-
+#ifdef CONFIG_KERNELD
+#include <linux/kerneld.h>
+#endif
 #include <asm/irq.h>
 #ifdef __SMP__
 #include <linux/smp.h>
@@ -178,6 +181,9 @@
 	/* stackable module support */
 	X(rename_module_symbol),
 	X(register_symtab),
+#ifdef CONFIG_KERNELD
+	X(kerneld_send),
+#endif
 	X(get_options),
 
 	/* system info variables */
@@ -306,6 +312,10 @@
 	X(lookup_exec_domain),
 	X(register_exec_domain),
 	X(unregister_exec_domain),
+
+	/* sysctl table registration */
+	X(register_sysctl_table),
+	X(unregister_sysctl_table),
 
 	/* interrupt handling */
 	X(request_irq),
diff -u --recursive --new-file v1.3.56/linux/kernel/module.c linux/kernel/module.c
--- v1.3.56/linux/kernel/module.c	Tue Jan  2 16:46:31 1996
+++ linux/kernel/module.c	Tue Jan  9 19:42:35 1996
@@ -228,6 +228,14 @@
 	memcpy_fromfs(&rt, routines, sizeof rt);
 	if ((mp = find_module(name)) == NULL)
 		return -ENOENT;
+	if (codesize & MOD_AUTOCLEAN) {
+		/*
+		 * set autoclean marker from codesize...
+		 * set useage count to "zero"
+		 */
+		codesize &= ~MOD_AUTOCLEAN;
+		GET_USE_COUNT(mp) = MOD_AUTOCLEAN;
+	}
 	if ((codesize + sizeof (long) + PAGE_SIZE - 1) / PAGE_SIZE > mp->size)
 		return -EINVAL;
 	memcpy_fromfs((char *)mp->addr + sizeof (long), code, codesize);
@@ -336,13 +344,26 @@
 			return error;
 		if ((mp = find_module(name)) == NULL)
 			return -ENOENT;
-		if ((mp->ref != NULL) || (GET_USE_COUNT(mp) != 0))
+		if ((mp->ref != NULL) || ((GET_USE_COUNT(mp) & ~MOD_AUTOCLEAN) != 0))
 			return -EBUSY;
+		GET_USE_COUNT(mp) &= ~MOD_AUTOCLEAN;
 		if (mp->state == MOD_RUNNING)
 			(*mp->cleanup)();
 		mp->state = MOD_DELETED;
+		free_modules();
+	}
+	/* for automatic reaping */
+	else {
+		for (mp = module_list; mp != &kernel_module; mp = mp->next) {
+			if ((mp->ref == NULL) && (GET_USE_COUNT(mp) == MOD_AUTOCLEAN) &&
+			    (mp->state == MOD_RUNNING)) {
+				GET_USE_COUNT(mp) &= ~MOD_AUTOCLEAN;
+				(*mp->cleanup)();
+				mp->state = MOD_DELETED;
+				free_modules();
+			}
+		}
 	}
-	free_modules();
 	return 0;
 }
 
@@ -495,7 +516,7 @@
 		if (mp->state != MOD_DELETED) {
 			mpp = &mp->next;
 		} else {
-			if (GET_USE_COUNT(mp) != 0) {
+			if ((GET_USE_COUNT(mp) != 0) || (mp->ref != NULL)) {
 				freeing_modules = 1;
 				mpp = &mp->next;
 			} else {	/* delete it */
@@ -552,10 +573,8 @@
 			*p++ = *q++;
 		if (mp->state == MOD_UNINITIALIZED)
 			q = "  (uninitialized)";
-		else if (mp->state == MOD_RUNNING) {
-			sprintf(size,"\t%ld",GET_USE_COUNT(mp));
-			q=size;
-		}
+		else if (mp->state == MOD_RUNNING)
+			q = "";
 		else if (mp->state == MOD_DELETED)
 			q = "  (deleted)";
 		else
@@ -574,6 +593,15 @@
 					*p++ = ' ';
 			}
 			*p++ = ']';
+		}
+		if (mp->state == MOD_RUNNING) {
+			sprintf(size,"\t%ld%s",
+				GET_USE_COUNT(mp) & ~MOD_AUTOCLEAN,
+				((GET_USE_COUNT(mp) & MOD_AUTOCLEAN)?
+					" (autoclean)":""));
+			q = size;
+			while (*q)
+				*p++ = *q++;
 		}
 		*p++ = '\n';
 	}
diff -u --recursive --new-file v1.3.56/linux/kernel/sys.c linux/kernel/sys.c
--- v1.3.56/linux/kernel/sys.c	Tue Jan  2 16:46:31 1996
+++ linux/kernel/sys.c	Wed Jan 10 09:27:40 1996
@@ -18,6 +18,7 @@
 #include <linux/mman.h>
 #include <linux/mm.h>
 #include <linux/pagemap.h>
+#include <linux/swap.h>
 
 #include <asm/segment.h>
 #include <asm/io.h>
diff -u --recursive --new-file v1.3.56/linux/kernel/sysctl.c linux/kernel/sysctl.c
--- v1.3.56/linux/kernel/sysctl.c	Thu Jan  1 02:00:00 1970
+++ linux/kernel/sysctl.c	Wed Jan 10 09:27:40 1996
@@ -0,0 +1,669 @@
+/*
+ * sysctl.c: General linux system control interface
+ *
+ * Begun 24 March 1995, Stephen Tweedie
+ * Added /proc support, Dec 1995
+ */
+
+#include <linux/autoconf.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/sysctl.h>
+#include <linux/swapctl.h>
+#include <linux/proc_fs.h>
+#include <linux/malloc.h>
+#include <linux/stat.h>
+#include <linux/ctype.h>
+#include <asm/bitops.h>
+#include <asm/segment.h>
+
+#include <linux/utsname.h>
+#include <linux/swapctl.h>
+
+static ctl_table root_table[];
+static struct ctl_table_header root_table_header = 
+	{root_table, DNODE_SINGLE(&root_table_header)};
+
+static int parse_table(int *, int, void *, size_t *, void *, size_t,
+		       ctl_table *, void **);
+
+static ctl_table kern_table[];
+static ctl_table vm_table[];
+
+/* /proc declarations: */
+
+#ifdef CONFIG_PROC_FS
+
+static int proc_readsys(struct inode * inode, struct file * file,
+			char * buf, int count);
+static int proc_writesys(struct inode * inode, struct file * file,
+			 const char * buf, int count);
+static int proc_sys_permission(struct inode *, int);
+
+struct file_operations proc_sys_file_operations =
+{
+	NULL,		/* lseek   */
+	proc_readsys,	/* read	   */
+	proc_writesys,	/* write   */
+	NULL,		/* readdir */
+	NULL,		/* select  */
+	NULL,		/* ioctl   */
+	NULL,		/* mmap	   */
+	NULL,		/* no special open code	   */
+	NULL,		/* no special release code */
+	NULL		/* can't fsync */
+};
+
+struct inode_operations proc_sys_inode_operations =
+{
+	&proc_sys_file_operations,
+	NULL,		/* create */
+	NULL,		/* lookup */
+	NULL,		/* link */
+	NULL,		/* unlink */
+	NULL,		/* symlink */
+	NULL,		/* mkdir */
+	NULL,		/* rmdir */
+	NULL,		/* mknod */
+	NULL,		/* rename */
+	NULL,		/* readlink */
+	NULL,		/* follow_link */
+	NULL,		/* readpage */
+	NULL,		/* writepage */
+	NULL,		/* bmap */
+	NULL,		/* truncate */
+	proc_sys_permission
+};
+
+extern struct proc_dir_entry proc_sys_root;
+
+static void register_proc_table(ctl_table *, struct proc_dir_entry *);
+static void unregister_proc_table(ctl_table *, struct proc_dir_entry *);
+#endif
+
+/* The default sysctl tables: */
+
+static ctl_table root_table[] = {
+	{CTL_KERN, "kernel", NULL, 0, 0555, kern_table},
+	{CTL_VM, "vm", NULL, 0, 0555, vm_table},
+	{0}
+};
+
+static ctl_table kern_table[] = {
+	{KERN_OSTYPE, "ostype", system_utsname.sysname, 64,
+	 0444, NULL, &proc_dostring, &sysctl_string},
+	{KERN_OSRELEASE, "osrelease", system_utsname.release, 64,
+	 0444, NULL, &proc_dostring, &sysctl_string},
+	{KERN_VERSION, "version", system_utsname.version, 64,
+	 0444, NULL, &proc_dostring, &sysctl_string},
+	{KERN_NODENAME, "hostname", system_utsname.nodename, 64,
+	 0644, NULL, &proc_dostring, &sysctl_string},
+	{KERN_DOMAINNAME, "domainname", system_utsname.domainname, 64,
+	 0644, NULL, &proc_dostring, &sysctl_string},
+	{KERN_NRINODE, "inode-nr", &nr_inodes, 2*sizeof(int),
+	 0444, NULL, &proc_dointvec},
+	{KERN_MAXINODE, "inode-max", &max_inodes, sizeof(int),
+	 0644, NULL, &proc_dointvec},
+	{KERN_NRFILE, "file-nr", &nr_files, sizeof(int),
+	 0444, NULL, &proc_dointvec},
+	{KERN_MAXFILE, "file-max", &max_files, sizeof(int),
+	 0644, NULL, &proc_dointvec},
+	{0}
+};
+
+static ctl_table vm_table[] = {
+	{VM_SWAPCTL, "swapctl", 
+	 &swap_control, sizeof(swap_control_t), 0600, NULL, &proc_dointvec},
+	{VM_KSWAPD, "kswapd", 
+	 &kswapd_ctl, sizeof(kswapd_ctl), 0600, NULL, &proc_dointvec},
+	{0}
+};
+
+void sysctl_init(void)
+{
+#ifdef CONFIG_PROC_FS
+	register_proc_table(root_table, &proc_sys_root);
+#endif
+}
+
+
+int do_sysctl (int *name, int nlen,
+	       void *oldval, size_t *oldlenp,
+	       void *newval, size_t newlen)
+{
+	int error;
+	struct ctl_table_header *tmp;
+	void *context;
+	
+	if (nlen == 0 || nlen >= CTL_MAXNAME)
+		return -ENOTDIR;
+	
+	error = verify_area(VERIFY_READ,name,nlen*sizeof(int));
+	if (error) return error;
+	if (oldval) {
+		if (!oldlenp)
+			return -EFAULT;
+		error = verify_area(VERIFY_WRITE,oldlenp,sizeof(size_t));
+		if (error) return error;
+		error = verify_area(VERIFY_WRITE,oldval,get_user(oldlenp));
+		if (error) return error;
+	}
+	if (newval) {
+		error = verify_area(VERIFY_READ,newval,newlen);
+		if (error) return error;
+	}
+	tmp = &root_table_header;
+	do {
+		context = 0;
+		error = parse_table(name, nlen, oldval, oldlenp, 
+				    newval, newlen, root_table, &context);
+		if (context)
+			kfree(context);
+		if (error != ENOTDIR)
+			return error;
+		tmp = tmp->DLIST_NEXT(ctl_entry);
+	} while (tmp != &root_table_header);
+	return -ENOTDIR;
+}
+
+extern asmlinkage int sys_sysctl(struct __sysctl_args *args)
+{
+	struct __sysctl_args tmp;
+	int error;
+	error = verify_area(VERIFY_READ, args, sizeof(*args));
+	if (error)
+		return error;
+	memcpy_fromfs(&tmp, args, sizeof(tmp));
+	return do_sysctl(tmp.name, tmp.nlen, tmp.oldval, tmp.oldlenp, 
+			 tmp.newval, tmp.newlen);
+}
+
+/* Like in_group_p, but testing against egid, not fsgid */
+static int in_egroup_p(gid_t grp)
+{
+	int	i;
+
+	if (grp == current->euid)
+		return 1;
+
+	for (i = 0; i < NGROUPS; i++) {
+		if (current->groups[i] == NOGROUP)
+			break;
+		if (current->groups[i] == grp)
+			return 1;
+	}
+	return 0;
+}
+/* ctl_perm does NOT grant the superuser all rights automatically, because
+   some sysctl variables are readonly even to root. */
+static int test_perm(int mode, int op)
+{
+	if (!current->euid)
+		mode >>= 6;
+	else if (in_egroup_p(0))
+		mode >>= 3;
+	if ((mode & op & 0007) == op)
+		return 0;
+	return -EACCES;
+}
+static inline int ctl_perm(ctl_table *table, int op)
+{
+	return test_perm(table->mode, op);
+}
+
+static int parse_table(int *name, int nlen,
+		       void *oldval, size_t *oldlenp,
+		       void *newval, size_t newlen,
+		       ctl_table *table, void **context)
+{
+	int error;
+repeat:
+	if (!nlen)
+		return -ENOTDIR;
+
+	for ( ; table->ctl_name; table++) {
+		if (get_user(name) == table->ctl_name ||
+		    table->ctl_name == CTL_ANY) {
+			if (table->child) {
+				if (ctl_perm(table, 001))
+					return -EPERM;
+				if (table->strategy) {
+					error = table->strategy(
+						table, name, nlen,
+						oldval, oldlenp,
+						newval, newlen, context);
+				if (error)
+					return error;
+				}
+				name++;
+				nlen--;
+				table = table->child;
+				goto repeat;
+			}
+			error = do_sysctl_strategy(table, name, nlen,
+						   oldval, oldlenp,
+						   newval, newlen, context);
+			return error;
+		}
+	};
+	return -ENOTDIR;
+}
+
+/* Perform the actual read/write of a sysctl table entry. */
+int do_sysctl_strategy (ctl_table *table, 
+			int *name, int nlen,
+			void *oldval, size_t *oldlenp,
+			void *newval, size_t newlen, void **context)
+{
+	int op = 0, rc, len;
+
+	if (oldval)
+		op |= 004;
+	if (newval) 
+		op |= 002;
+	if (ctl_perm(table, op))
+		return -EPERM;
+
+	if (table->strategy) {
+		rc = table->strategy(table, name, nlen, oldval, oldlenp,
+				     newval, newlen, context);
+		if (rc < 0)
+			return rc;
+		if (rc > 0)
+			return 0;
+	}
+
+	/* If there is no strategy routine, or if the strategy returns
+	 * zero, proceed with automatic r/w */
+	if (table->data && table->maxlen) {
+		if (oldval && oldlenp && get_user(oldlenp)) {
+			len = get_user(oldlenp);
+			if (len > table->maxlen)
+				len = table->maxlen;
+			memcpy_tofs(oldval, table->data, len);
+			put_user(len, oldlenp);
+		}
+		if (newval && newlen) {
+			len = newlen;
+			if (len > table->maxlen)
+				len = table->maxlen;
+			memcpy_fromfs(table->data, newval, len);
+		}
+	}
+	return 0;
+}
+
+
+struct ctl_table_header *register_sysctl_table(ctl_table * table, 
+					       int insert_at_head)
+{
+	struct ctl_table_header *tmp;
+	tmp = kmalloc(sizeof(*tmp), GFP_KERNEL);
+	if (!tmp)
+		return 0;
+	*tmp = ((struct ctl_table_header) {table, DNODE_NULL});
+	if (insert_at_head)
+		DLIST_INSERT_AFTER(&root_table_header, tmp, ctl_entry);
+	else
+		DLIST_INSERT_BEFORE(&root_table_header, tmp, ctl_entry);
+#ifdef CONFIG_PROC_FS
+	register_proc_table(table, &proc_sys_root);
+#endif
+	return tmp;
+}
+
+void unregister_sysctl_table(struct ctl_table_header * table)
+{
+	DLIST_DELETE(table, ctl_entry);
+#ifdef CONFIG_PROC_FS
+	unregister_proc_table(table->ctl_table, &proc_sys_root);
+#endif
+}
+
+/*
+ * /proc/sys support
+ */
+
+#ifdef CONFIG_PROC_FS
+
+/* Scan the sysctl entries in table and add them all into /proc */
+static void register_proc_table(ctl_table * table, struct proc_dir_entry *root)
+{
+	struct proc_dir_entry *de;
+	
+	for (; table->ctl_name; table++) {
+		/* Can't do anything without a proc name. */
+		if (!table->procname)
+			continue;
+		/* Maybe we can't do anything with it... */
+		if (!table->proc_handler &&
+		    !table->child)
+			continue;
+		
+		de = kmalloc(sizeof(*de), GFP_KERNEL);
+		if (!de) continue;
+		de->namelen = strlen(table->procname);
+		de->name = table->procname;
+		de->mode = table->mode;
+		de->nlink = 1;
+		de->uid = 0;
+		de->gid = 0;
+		de->size = 0;
+		de->get_info = 0;	/* For internal use if we want it */
+		de->fill_inode = 0;	/* To override struct inode fields */
+		de->next = de->subdir = 0;
+		de->data = (void *) table;
+		/* Is it a file? */
+		if (table->proc_handler) {
+			de->ops = &proc_sys_inode_operations;
+			de->mode |= S_IFREG;
+		}
+		/* Otherwise it's a subdir */
+		else  {
+			de->ops = &proc_dir_inode_operations;
+			de->nlink++;
+			de->mode |= S_IFDIR;
+		}
+		table->de = de;
+		proc_register_dynamic(root, de);
+		if (de->mode & S_IFDIR )
+			register_proc_table(table->child, de);
+	}
+}
+
+static void unregister_proc_table(ctl_table * table, struct proc_dir_entry *root)
+{
+	struct proc_dir_entry *de;
+	for (; table->ctl_name; table++) {
+		if (!(de = table->de))
+			continue;
+		if (de->mode & S_IFDIR) {
+			if (!table->child) {
+				printk (KERN_ALERT "Help - malformed sysctl tree on free\n");
+				continue;
+			}
+			unregister_proc_table(table->child, de);
+		}
+		proc_unregister(root, de->low_ino);
+		kfree(de);			
+	}
+}
+
+
+static int do_rw_proc(int write, struct inode * inode, struct file * file,
+		      char * buf, int count)
+{
+	int error, op;
+	struct proc_dir_entry *de;
+	struct ctl_table *table;
+	size_t res;
+	
+	error = verify_area(write ? VERIFY_READ : VERIFY_WRITE, buf, count);
+	if (error)
+		return error;
+
+	de = (struct proc_dir_entry*) inode->u.generic_ip;
+	if (!de || !de->data)
+		return -ENOTDIR;
+	table = (struct ctl_table *) de->data;
+	if (!table || !table->proc_handler)
+		return -ENOTDIR;
+	op = (write ? 002 : 004);
+	if (ctl_perm(table, op))
+		return -EPERM;
+	
+	res = count;
+	error = (*table->proc_handler) (table, write, file, buf, &res);
+	if (error)
+		return error;
+	return res;
+}
+
+static int proc_readsys(struct inode * inode, struct file * file,
+			char * buf, int count)
+{
+	return do_rw_proc(0, inode, file, buf, count);
+}
+
+static int proc_writesys(struct inode * inode, struct file * file,
+			 const char * buf, int count)
+{
+	return do_rw_proc(1, inode, file, (char *) buf, count);
+}
+
+static int proc_sys_permission(struct inode *inode, int op)
+{
+	return test_perm(inode->i_mode, op);
+}
+
+int proc_dostring(ctl_table *table, int write, struct file *filp,
+		  void *buffer, size_t *lenp)
+{
+	int len;
+	char *p, c;
+	
+	if (!table->data || !table->maxlen || !*lenp ||
+	    (filp->f_pos && !write)) {
+		*lenp = 0;
+		return 0;
+	}
+	
+	if (write) {
+		len = 0;
+		p = buffer;
+		while (len < *lenp && 
+		       (c = get_user(p++)) != 0 && c != '\n')
+			len++;
+		if (len >= table->maxlen)
+			len = table->maxlen-1;
+		memcpy_fromfs(table->data, buffer, len);
+		((char *) table->data)[len] = 0;
+		filp->f_pos += *lenp;
+	} else {
+		len = strlen(table->data) + 1;
+		if (len > table->maxlen)
+			len = table->maxlen;
+		if (len > *lenp)
+			len = *lenp;
+		if (len) {			
+			memcpy_tofs(buffer, table->data, len-1);
+			put_user(0, ((char *) buffer) + len - 1);
+		}
+		if (len < *lenp) {
+			put_user('\n', ((char *) buffer) + len);
+			len++;
+		}
+		*lenp = len;
+		filp->f_pos += len;
+	}
+	return 0;
+}
+
+int proc_dointvec(ctl_table *table, int write, struct file *filp,
+		  void *buffer, size_t *lenp)
+{
+	int *i, vleft, first=1, len, left, neg, val;
+	#define TMPBUFLEN 20
+	char buf[TMPBUFLEN], *p;
+	
+	if (!table->data || !table->maxlen || !*lenp ||
+	    (filp->f_pos && !write)) {
+		*lenp = 0;
+		return 0;
+	}
+	
+	i = (int *) table->data;
+	vleft = table->maxlen / sizeof(int);
+	left = *lenp;
+	
+	for (; left && vleft--; i++, first=0) {
+		if (write) {
+			while (left && isspace(get_user((char *) buffer)))
+				left--, ((char *) buffer)++;
+			if (!left)
+				break;
+			neg = 0;
+			len = left;
+			if (len > TMPBUFLEN-1)
+				len = TMPBUFLEN-1;
+			memcpy_fromfs(buf, buffer, len);
+			buf[len] = 0;
+			p = buf;
+			if (*p == '-' && left > 1) {
+				neg = 1;
+				left--, p++;
+			}
+			if (*p < '0' || *p > '9')
+				break;
+			val = simple_strtoul(p, &p, 0);
+			len = p-buf;
+			if ((len < left) && *p && !isspace(*p))
+				break;
+			if (neg)
+				val = -val;
+			buffer += len;
+			left -= len;
+			*i = val;
+		} else {
+			p = buf;
+			if (!first)
+				*p++ = '\t';
+			sprintf(p, "%d", *i);
+			len = strlen(buf);
+			if (len > left)
+				len = left;
+			memcpy_tofs(buffer, buf, len);
+			left -= len;
+			buffer += len;
+		}
+	}
+
+	if (!write && !first && left) {
+		put_user('\n', (char *) buffer);
+		left--, buffer++;
+	}
+	if (write) {
+		p = (char *) buffer;
+		while (left && isspace(get_user(p++)))
+			left--;
+	}
+	if (write && first)
+		return -EINVAL;
+	*lenp -= left;
+	filp->f_pos += *lenp;
+	return 0;
+}
+
+#else /* CONFIG_PROC_FS */
+
+int proc_dostring(ctl_table *table, int write, struct file *filp,
+		  void *buffer, size_t *lenp)
+{
+	return -ENOSYS;
+}
+
+int proc_dointvec(ctl_table *table, int write, struct file *filp,
+		  void *buffer, size_t *lenp)
+{
+	return -ENOSYS;
+}
+
+#endif /* CONFIG_PROC_FS */
+
+
+/*
+ * General sysctl support routines 
+ */
+
+/* The generic string strategy routine: */
+int sysctl_string(ctl_table *table, int *name, int nlen,
+		  void *oldval, size_t *oldlenp,
+		  void *newval, size_t newlen, void **context)
+{
+	int l, len;
+	
+	if (!table->data || !table->maxlen) 
+		return -ENOTDIR;
+	
+	if (oldval && oldlenp && get_user(oldlenp)) {
+		len = get_user(oldlenp);
+		l = strlen(table->data);
+		if (len > l) len = l;
+		if (len >= table->maxlen)
+			len = table->maxlen;
+		memcpy_tofs(oldval, table->data, len);
+		put_user(0, ((char *) oldval) + len);
+		put_user(len, oldlenp);
+	}
+	if (newval && newlen) {
+		len = newlen;
+		if (len > table->maxlen)
+			len = table->maxlen;
+		memcpy_fromfs(table->data, newval, len);
+		if (len == table->maxlen)
+			len--;
+		((char *) table->data)[len] = 0;
+	}
+	return 0;
+}
+
+int do_string (
+	void *oldval, size_t *oldlenp, void *newval, size_t newlen,
+	int rdwr, char *data, size_t max)
+{
+	int l = strlen(data) + 1;
+	if (newval && !rdwr)
+		return -EPERM;
+	if (newval && newlen >= max)
+		return -EINVAL;
+	if (oldval) {
+		if (l > get_user(oldlenp))
+			return -ENOMEM;
+		put_user(l, oldlenp);
+		memcpy_tofs(oldval, data, l);
+	}
+	if (newval) {
+		memcpy_fromfs(data, newval, newlen);
+		data[newlen] = 0;
+	}
+	return 0;
+}
+
+int do_int (
+	void *oldval, size_t *oldlenp, void *newval, size_t newlen,
+	int rdwr, int *data)
+{
+	if (newval && !rdwr)
+		return -EPERM;
+	if (newval && newlen != sizeof(int))
+		return -EINVAL;
+	if (oldval) {
+		if (get_user(oldlenp) < sizeof(int))
+			return -ENOMEM;
+		put_user(sizeof(int), oldlenp);
+		memcpy_tofs(oldval, data, sizeof(int));
+	}
+	if (newval)
+		memcpy_fromfs(data, newval, sizeof(int));
+	return 0;
+}
+
+int do_struct (
+	void *oldval, size_t *oldlenp, void *newval, size_t newlen,
+	int rdwr, void *data, size_t len)
+{
+	if (newval && !rdwr)
+		return -EPERM;
+	if (newval && newlen != len)
+		return -EINVAL;
+	if (oldval) {
+		if (get_user(oldlenp) < len)
+			return -ENOMEM;
+		put_user(len, oldlenp);
+		memcpy_tofs(oldval, data, len);
+	}
+	if (newval)
+		memcpy_fromfs(data, newval, len);
+	return 0;
+}
+
diff -u --recursive --new-file v1.3.56/linux/mm/Makefile linux/mm/Makefile
--- v1.3.56/linux/mm/Makefile	Tue Nov 21 13:22:14 1995
+++ linux/mm/Makefile	Tue Jan  9 20:52:07 1996
@@ -8,7 +8,8 @@
 # Note 2! The CFLAGS definition is now in the main makefile...
 
 O_TARGET := mm.o
-O_OBJS	 := memory.o swap.o mmap.o filemap.o mprotect.o mlock.o \
-	    kmalloc.o vmalloc.o
+O_OBJS	 := memory.o mmap.o filemap.o mprotect.o mlock.o \
+	    kmalloc.o vmalloc.o \
+	    swap.o vmscan.o page_io.o page_alloc.o swap_state.o swapfile.o
 
 include $(TOPDIR)/Rules.make
diff -u --recursive --new-file v1.3.56/linux/mm/filemap.c linux/mm/filemap.c
--- v1.3.56/linux/mm/filemap.c	Mon Jan  8 14:10:28 1996
+++ linux/mm/filemap.c	Thu Jan 11 11:57:29 1996
@@ -21,6 +21,7 @@
 #include <linux/fs.h>
 #include <linux/locks.h>
 #include <linux/pagemap.h>
+#include <linux/swap.h>
 
 #include <asm/segment.h>
 #include <asm/system.h>
@@ -73,6 +74,7 @@
 {
 	static int clock = 0;
 	struct page * page;
+	struct buffer_head *tmp, *bh;
 
 	if (limit > high_memory)
 		limit = high_memory;
@@ -82,18 +84,40 @@
 	priority = (limit<<2) >> priority;
 	page = mem_map + clock;
 	while (priority-- > 0) {
-		if (page->inode) {
-			unsigned age = page->age;
-			/* if the page is shared, we juvenate it */
-			if (page->count != 1)
-				age |= PAGE_AGE_VALUE << 1;
-			page->age = age >> 1;
-			if (age < PAGE_AGE_VALUE) {
+		/* First of all, regenerate the page's referenced bit
+                   from any buffers in the page */
+		bh = buffer_pages[MAP_NR(page_address(page))];
+		if (bh) {
+			tmp = bh;
+			do {
+				if (buffer_touched(tmp)) {
+					clear_bit(BH_Touched, &tmp->b_state);
+					page->referenced = 1;
+				}
+				tmp = tmp->b_this_page;
+			} while (tmp != bh);
+		}
+
+		/* We can't throw away shared pages, but we do mark
+		   them as referenced.  This relies on the fact that
+		   no page is currently in both the page cache and the
+		   buffer cache; we'd have to modify the following
+		   test to allow for that case. */
+		if (page->count > 1)
+			page->referenced = 1;
+		else if (page->referenced)
+			page->referenced = 0;
+		else if (page->count) {
+			/* The page is an old, unshared page --- try
+                           to discard it. */
+			if (page->inode) {
 				remove_page_from_hash_queue(page);
 				remove_page_from_inode_queue(page);
 				free_page(page_address(page));
 				return 1;
 			}
+			if (bh && try_to_free_buffer(bh, &bh, 6))
+				return 1;
 		}
 		page++;
 		clock++;
diff -u --recursive --new-file v1.3.56/linux/mm/memory.c linux/mm/memory.c
--- v1.3.56/linux/mm/memory.c	Mon Jan  8 14:10:28 1996
+++ linux/mm/memory.c	Tue Jan  9 20:41:30 1996
@@ -43,6 +43,7 @@
 #include <linux/ptrace.h>
 #include <linux/mman.h>
 #include <linux/mm.h>
+#include <linux/swap.h>
 
 #include <asm/system.h>
 #include <asm/segment.h>
diff -u --recursive --new-file v1.3.56/linux/mm/page_alloc.c linux/mm/page_alloc.c
--- v1.3.56/linux/mm/page_alloc.c	Thu Jan  1 02:00:00 1970
+++ linux/mm/page_alloc.c	Wed Jan 10 09:28:05 1996
@@ -0,0 +1,304 @@
+/*
+ *  linux/mm/page_alloc.c
+ *
+ *  Copyright (C) 1991, 1992, 1993, 1994  Linus Torvalds
+ *  Swap reorganised 29.12.95, Stephen Tweedie
+ */
+
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/head.h>
+#include <linux/kernel.h>
+#include <linux/kernel_stat.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/stat.h>
+#include <linux/swap.h>
+#include <linux/fs.h>
+#include <linux/swapctl.h>
+
+#include <asm/dma.h>
+#include <asm/system.h> /* for cli()/sti() */
+#include <asm/segment.h> /* for memcpy_to/fromfs */
+#include <asm/bitops.h>
+#include <asm/pgtable.h>
+
+static inline void add_mem_queue(struct mem_list * head, struct mem_list * entry)
+{
+	entry->prev = head;
+	(entry->next = head->next)->prev = entry;
+	head->next = entry;
+}
+
+static inline void remove_mem_queue(struct mem_list * head, struct mem_list * entry)
+{
+	struct mem_list * next = entry->next;
+	(next->prev = entry->prev)->next = next;
+}
+
+/*
+ * Free_page() adds the page to the free lists. This is optimized for
+ * fast normal cases (no error jumps taken normally).
+ *
+ * The way to optimize jumps for gcc-2.2.2 is to:
+ *  - select the "normal" case and put it inside the if () { XXX }
+ *  - no else-statements if you can avoid them
+ *
+ * With the above two rules, you get a straight-line execution path
+ * for the normal case, giving better asm-code.
+ *
+ * free_page() may sleep since the page being freed may be a buffer
+ * page or present in the swap cache. It will not sleep, however,
+ * for a freshly allocated page (get_free_page()).
+ */
+
+/*
+ * Buddy system. Hairy. You really aren't expected to understand this
+ */
+static inline void free_pages_ok(unsigned long addr, unsigned long order)
+{
+	unsigned long index = MAP_NR(addr) >> (1 + order);
+	unsigned long mask = PAGE_MASK << order;
+
+	addr &= mask;
+	nr_free_pages += 1 << order;
+	while (order < NR_MEM_LISTS-1) {
+		if (!change_bit(index, free_area_map[order]))
+			break;
+		remove_mem_queue(free_area_list+order, (struct mem_list *) (addr ^ (1+~mask)));
+		order++;
+		index >>= 1;
+		mask <<= 1;
+		addr &= mask;
+	}
+	add_mem_queue(free_area_list+order, (struct mem_list *) addr);
+}
+
+static inline void check_free_buffers(unsigned long addr)
+{
+	struct buffer_head * bh;
+
+	bh = buffer_pages[MAP_NR(addr)];
+	if (bh) {
+		struct buffer_head *tmp = bh;
+		do {
+			if (tmp->b_list == BUF_SHARED
+			    && tmp->b_dev != B_FREE)
+				refile_buffer(tmp);
+			tmp = tmp->b_this_page;
+		} while (tmp != bh);
+	}
+}
+
+void free_pages(unsigned long addr, unsigned long order)
+{
+	if (MAP_NR(addr) < MAP_NR(high_memory)) {
+		unsigned long flag;
+		mem_map_t * map = mem_map + MAP_NR(addr);
+		if (map->reserved)
+			return;
+		if (map->count) {
+			save_flags(flag);
+			cli();
+			if (!--map->count) {
+				free_pages_ok(addr, order);
+				delete_from_swap_cache(addr);
+			}
+			restore_flags(flag);
+			if (map->count == 1)
+				check_free_buffers(addr);
+			return;
+		}
+		printk("Trying to free free memory (%08lx): memory probably corrupted\n",addr);
+		printk("PC = %p\n", __builtin_return_address(0));
+		return;
+	}
+}
+
+/*
+ * Some ugly macros to speed up __get_free_pages()..
+ */
+#define RMQUEUE(order, limit) \
+do { struct mem_list * queue = free_area_list+order; \
+     unsigned long new_order = order; \
+	do { struct mem_list *prev = queue, *ret; \
+		while (queue != (ret = prev->next)) { \
+			if ((unsigned long) ret < (limit)) { \
+				(prev->next = ret->next)->prev = prev; \
+				mark_used((unsigned long) ret, new_order); \
+				nr_free_pages -= 1 << order; \
+				restore_flags(flags); \
+				EXPAND(ret, order, new_order); \
+				return (unsigned long) ret; \
+			} \
+			prev = ret; \
+		} \
+		new_order++; queue++; \
+	} while (new_order < NR_MEM_LISTS); \
+} while (0)
+
+static inline int mark_used(unsigned long addr, unsigned long order)
+{
+	return change_bit(MAP_NR(addr) >> (1+order), free_area_map[order]);
+}
+
+#define EXPAND(addr,low,high) \
+do { unsigned long size = PAGE_SIZE << high; \
+	while (high > low) { \
+		high--; size >>= 1; cli(); \
+		add_mem_queue(free_area_list+high, addr); \
+		mark_used((unsigned long) addr, high); \
+		restore_flags(flags); \
+		addr = (struct mem_list *) (size + (unsigned long) addr); \
+	} mem_map[MAP_NR((unsigned long) addr)].count = 1; \
+	mem_map[MAP_NR((unsigned long) addr)].age = PAGE_INITIAL_AGE; \
+} while (0)
+
+unsigned long __get_free_pages(int priority, unsigned long order, unsigned long limit)
+{
+	unsigned long flags;
+	int reserved_pages;
+
+	if (order >= NR_MEM_LISTS)
+		return 0;
+	if (intr_count && priority != GFP_ATOMIC) {
+		static int count = 0;
+		if (++count < 5) {
+			printk("gfp called nonatomically from interrupt %p\n",
+				__builtin_return_address(0));
+			priority = GFP_ATOMIC;
+		}
+	}
+	reserved_pages = 5;
+	if (priority != GFP_NFS)
+		reserved_pages = min_free_pages;
+	save_flags(flags);
+repeat:
+	cli();
+	if ((priority==GFP_ATOMIC) || nr_free_pages > reserved_pages) {
+		RMQUEUE(order, limit);
+		restore_flags(flags);
+		return 0;
+	}
+	restore_flags(flags);
+	if (priority != GFP_BUFFER && try_to_free_page(priority, limit))
+		goto repeat;
+	return 0;
+}
+
+/*
+ * Show free area list (used inside shift_scroll-lock stuff)
+ * We also calculate the percentage fragmentation. We do this by counting the
+ * memory on each free list with the exception of the first item on the list.
+ */
+void show_free_areas(void)
+{
+ 	unsigned long order, flags;
+ 	unsigned long total = 0;
+
+	printk("Free pages:      %6dkB\n ( ",nr_free_pages<<(PAGE_SHIFT-10));
+	save_flags(flags);
+	cli();
+ 	for (order=0 ; order < NR_MEM_LISTS; order++) {
+		struct mem_list * tmp;
+		unsigned long nr = 0;
+		for (tmp = free_area_list[order].next ; tmp != free_area_list + order ; tmp = tmp->next) {
+			nr ++;
+		}
+		total += nr * ((PAGE_SIZE>>10) << order);
+		printk("%lu*%lukB ", nr, (PAGE_SIZE>>10) << order);
+	}
+	restore_flags(flags);
+	printk("= %lukB)\n", total);
+#ifdef SWAP_CACHE_INFO
+	show_swap_cache_info();
+#endif	
+}
+
+#define LONG_ALIGN(x) (((x)+(sizeof(long))-1)&~((sizeof(long))-1))
+
+/*
+ * set up the free-area data structures:
+ *   - mark all pages reserved
+ *   - mark all memory queues empty
+ *   - clear the memory bitmaps
+ */
+unsigned long free_area_init(unsigned long start_mem, unsigned long end_mem)
+{
+	mem_map_t * p;
+	unsigned long mask = PAGE_MASK;
+	int i;
+
+	/*
+	 * select nr of pages we try to keep free for important stuff
+	 * with a minimum of 16 pages. This is totally arbitrary
+	 */
+	i = (end_mem - PAGE_OFFSET) >> (PAGE_SHIFT+7);
+	if (i < 16)
+		i = 16;
+	min_free_pages = i;
+	free_pages_low = i + (i>>1);
+	free_pages_high = i + i;
+	start_mem = init_swap_cache(start_mem, end_mem);
+	mem_map = (mem_map_t *) start_mem;
+	p = mem_map + MAP_NR(end_mem);
+	start_mem = LONG_ALIGN((unsigned long) p);
+	memset(mem_map, 0, start_mem - (unsigned long) mem_map);
+	do {
+		--p;
+		p->reserved = 1;
+	} while (p > mem_map);
+
+	for (i = 0 ; i < NR_MEM_LISTS ; i++) {
+		unsigned long bitmap_size;
+		free_area_list[i].prev = free_area_list[i].next = &free_area_list[i];
+		mask += mask;
+		end_mem = (end_mem + ~mask) & mask;
+		bitmap_size = (end_mem - PAGE_OFFSET) >> (PAGE_SHIFT + i);
+		bitmap_size = (bitmap_size + 7) >> 3;
+		bitmap_size = LONG_ALIGN(bitmap_size);
+		free_area_map[i] = (unsigned int *) start_mem;
+		memset((void *) start_mem, 0, bitmap_size);
+		start_mem += bitmap_size;
+	}
+	return start_mem;
+}
+
+/*
+ * The tests may look silly, but it essentially makes sure that
+ * no other process did a swap-in on us just as we were waiting.
+ *
+ * Also, don't bother to add to the swap cache if this page-in
+ * was due to a write access.
+ */
+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);
+
+	if (pte_val(*page_table) != entry) {
+		free_page(page);
+		return;
+	}
+	if (!page) {
+		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;
+	}
+	vma->vm_mm->rss++;
+	tsk->maj_flt++;
+	if (!write_access && add_to_swap_cache(page, entry)) {
+		set_pte(page_table, mk_pte(page, vma->vm_page_prot));
+		return;
+	}
+	set_pte(page_table, pte_mkwrite(pte_mkdirty(mk_pte(page, vma->vm_page_prot))));
+  	swap_free(entry);
+  	return;
+}
+
diff -u --recursive --new-file v1.3.56/linux/mm/page_io.c linux/mm/page_io.c
--- v1.3.56/linux/mm/page_io.c	Thu Jan  1 02:00:00 1970
+++ linux/mm/page_io.c	Tue Jan  9 20:56:47 1996
@@ -0,0 +1,108 @@
+/*
+ *  linux/mm/page_io.c
+ *
+ *  Copyright (C) 1991, 1992, 1993, 1994  Linus Torvalds
+ *
+ *  Swap reorganised 29.12.95, 
+ *  Asynchronous swapping added 30.12.95. Stephen Tweedie
+ */
+
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/head.h>
+#include <linux/kernel.h>
+#include <linux/kernel_stat.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/stat.h>
+#include <linux/swap.h>
+#include <linux/fs.h>
+#include <linux/locks.h>
+#include <linux/swapctl.h>
+
+#include <asm/dma.h>
+#include <asm/system.h> /* for cli()/sti() */
+#include <asm/segment.h> /* for memcpy_to/fromfs */
+#include <asm/bitops.h>
+#include <asm/pgtable.h>
+
+static struct wait_queue * lock_queue = NULL;
+
+void rw_swap_page(int rw, unsigned long entry, char * buf)
+{
+	unsigned long type, offset;
+	struct swap_info_struct * p;
+
+	type = SWP_TYPE(entry);
+	if (type >= nr_swapfiles) {
+		printk("Internal error: bad swap-device\n");
+		return;
+	}
+	p = &swap_info[type];
+	offset = SWP_OFFSET(entry);
+	if (offset >= p->max) {
+		printk("rw_swap_page: weirdness\n");
+		return;
+	}
+	if (p->swap_map && !p->swap_map[offset]) {
+		printk("Hmm.. Trying to use unallocated swap (%08lx)\n", entry);
+		return;
+	}
+	if (!(p->flags & SWP_USED)) {
+		printk("Trying to swap to unused swap-device\n");
+		return;
+	}
+	while (set_bit(offset,p->swap_lockmap))
+		sleep_on(&lock_queue);
+	if (rw == READ)
+		kstat.pswpin++;
+	else
+		kstat.pswpout++;
+	if (p->swap_device) {
+		ll_rw_page(rw,p->swap_device,offset,buf);
+	} else if (p->swap_file) {
+		struct inode *swapf = p->swap_file;
+		unsigned int zones[PAGE_SIZE/512];
+		int i;
+		if (swapf->i_op->bmap == NULL
+			&& swapf->i_op->smap != NULL){
+			/*
+				With MsDOS, we use msdos_smap which return
+				a sector number (not a cluster or block number).
+				It is a patch to enable the UMSDOS project.
+				Other people are working on better solution.
+
+				It sounds like ll_rw_swap_file defined
+				it operation size (sector size) based on
+				PAGE_SIZE and the number of block to read.
+				So using bmap or smap should work even if
+				smap will require more blocks.
+			*/
+			int j;
+			unsigned int block = offset << 3;
+
+			for (i=0, j=0; j< PAGE_SIZE ; i++, j += 512){
+				if (!(zones[i] = swapf->i_op->smap(swapf,block++))) {
+					printk("rw_swap_page: bad swap file\n");
+					return;
+				}
+			}
+		}else{
+			int j;
+			unsigned int block = offset
+				<< (PAGE_SHIFT - swapf->i_sb->s_blocksize_bits);
+
+			for (i=0, j=0; j< PAGE_SIZE ; i++, j +=swapf->i_sb->s_blocksize)
+				if (!(zones[i] = bmap(swapf,block++))) {
+					printk("rw_swap_page: bad swap file\n");
+					return;
+				}
+		}
+		ll_rw_swap_file(rw,swapf->i_dev, zones, i,buf);
+	} else
+		printk("re_swap_page: no swap file or device\n");
+	if (offset && !clear_bit(offset,p->swap_lockmap))
+		printk("rw_swap_page: lock already cleared\n");
+	wake_up(&lock_queue);
+}
+
diff -u --recursive --new-file v1.3.56/linux/mm/swap.c linux/mm/swap.c
--- v1.3.56/linux/mm/swap.c	Mon Jan  8 14:10:28 1996
+++ linux/mm/swap.c	Thu Jan 11 11:57:29 1996
@@ -30,12 +30,17 @@
 #include <asm/bitops.h>
 #include <asm/pgtable.h>
 
-#define MAX_SWAPFILES 8
-
-#define SWP_USED	1
-#define SWP_WRITEOK	3
-
+/*
+ * We identify three levels of free memory.  We never let free mem
+ * fall below the min_free_pages except for atomic allocations.  We
+ * start background swapping if we fall below free_pages_high free
+ * pages, and we begin intensive swapping below free_pages_low.
+ *
+ * Keep these three variables contiguous for sysctl(2).  
+ */
 int min_free_pages = 20;
+int free_pages_low = 30;
+int free_pages_high = 40;
 
 /*
  * Constants for the page aging mechanism: the maximum age (actually,
@@ -53,87 +58,6 @@
 	RCL_ROUND_ROBIN		/* Balancing policy */
 };
 
-static int nr_swapfiles = 0;
-static struct wait_queue * lock_queue = NULL;
-static struct {
-	int head;	/* head of priority-ordered swapfile list */
-	int next;	/* swapfile to be used next */
-} swap_list = {-1, -1};
-
-static struct swap_info_struct {
-	unsigned int flags;
-	kdev_t swap_device;
-	struct inode * swap_file;
-	unsigned char * swap_map;
-	unsigned char * swap_lockmap;
-	int lowest_bit;
-	int highest_bit;
-	int prio;			/* swap priority */
-	int pages;
-	unsigned long max;
-	int next;			/* next entry on swap list */
-} swap_info[MAX_SWAPFILES];
-
-extern int shm_swap (int, unsigned long);
-
-/*
- * To save us from swapping out pages which have just been swapped in and
- * have not been modified since then, we keep in swap_cache[page>>PAGE_SHIFT]
- * the swap entry which was last used to fill the page, or zero if the
- * page does not currently correspond to a page in swap. PAGE_DIRTY makes
- * this info useless.
- */
-unsigned long *swap_cache;
-
-#ifdef SWAP_CACHE_INFO
-unsigned long swap_cache_add_total = 0;
-unsigned long swap_cache_add_success = 0;
-unsigned long swap_cache_del_total = 0;
-unsigned long swap_cache_del_success = 0;
-unsigned long swap_cache_find_total = 0;
-unsigned long swap_cache_find_success = 0;
-
-extern inline void show_swap_cache_info(void)
-{
-	printk("Swap cache: add %ld/%ld, delete %ld/%ld, find %ld/%ld\n",
-		swap_cache_add_total, swap_cache_add_success, 
-		swap_cache_del_total, swap_cache_del_success,
-		swap_cache_find_total, swap_cache_find_success);
-}
-#endif
-
-static int add_to_swap_cache(unsigned long addr, unsigned long entry)
-{
-	struct swap_info_struct * p = &swap_info[SWP_TYPE(entry)];
-
-#ifdef SWAP_CACHE_INFO
-	swap_cache_add_total++;
-#endif
-	if ((p->flags & SWP_WRITEOK) == SWP_WRITEOK) {
-		entry = xchg(swap_cache + MAP_NR(addr), entry);
-		if (entry)  {
-			printk("swap_cache: replacing non-NULL entry\n");
-		}
-#ifdef SWAP_CACHE_INFO
-		swap_cache_add_success++;
-#endif
-		return 1;
-	}
-	return 0;
-}
-
-static unsigned long init_swap_cache(unsigned long mem_start,
-	unsigned long mem_end)
-{
-	unsigned long swap_cache_size;
-
-	mem_start = (mem_start + 15) & ~15;
-	swap_cache = (unsigned long *) mem_start;
-	swap_cache_size = MAP_NR(mem_end);
-	memset(swap_cache, 0, swap_cache_size * sizeof (unsigned long));
-	return (unsigned long) (swap_cache + swap_cache_size);
-}
-
 /* General swap control */
 
 /* Parse the kernel command line "swap=" option at load time: */
@@ -174,1174 +98,3 @@
 	}
 }
 
-/* Page aging */
-
-void rw_swap_page(int rw, unsigned long entry, char * buf)
-{
-	unsigned long type, offset;
-	struct swap_info_struct * p;
-
-	type = SWP_TYPE(entry);
-	if (type >= nr_swapfiles) {
-		printk("Internal error: bad swap-device\n");
-		return;
-	}
-	p = &swap_info[type];
-	offset = SWP_OFFSET(entry);
-	if (offset >= p->max) {
-		printk("rw_swap_page: weirdness\n");
-		return;
-	}
-	if (p->swap_map && !p->swap_map[offset]) {
-		printk("Hmm.. Trying to use unallocated swap (%08lx)\n", entry);
-		return;
-	}
-	if (!(p->flags & SWP_USED)) {
-		printk("Trying to swap to unused swap-device\n");
-		return;
-	}
-	while (set_bit(offset,p->swap_lockmap))
-		sleep_on(&lock_queue);
-	if (rw == READ)
-		kstat.pswpin++;
-	else
-		kstat.pswpout++;
-	if (p->swap_device) {
-		ll_rw_page(rw,p->swap_device,offset,buf);
-	} else if (p->swap_file) {
-		struct inode *swapf = p->swap_file;
-		unsigned int zones[PAGE_SIZE/512];
-		int i;
-		if (swapf->i_op->bmap == NULL
-			&& swapf->i_op->smap != NULL){
-			/*
-				With MsDOS, we use msdos_smap which return
-				a sector number (not a cluster or block number).
-				It is a patch to enable the UMSDOS project.
-				Other people are working on better solution.
-
-				It sounds like ll_rw_swap_file defined
-				it operation size (sector size) based on
-				PAGE_SIZE and the number of block to read.
-				So using bmap or smap should work even if
-				smap will require more blocks.
-			*/
-			int j;
-			unsigned int block = offset << 3;
-
-			for (i=0, j=0; j< PAGE_SIZE ; i++, j += 512){
-				if (!(zones[i] = swapf->i_op->smap(swapf,block++))) {
-					printk("rw_swap_page: bad swap file\n");
-					return;
-				}
-			}
-		}else{
-			int j;
-			unsigned int block = offset
-				<< (PAGE_SHIFT - swapf->i_sb->s_blocksize_bits);
-
-			for (i=0, j=0; j< PAGE_SIZE ; i++, j +=swapf->i_sb->s_blocksize)
-				if (!(zones[i] = bmap(swapf,block++))) {
-					printk("rw_swap_page: bad swap file\n");
-					return;
-				}
-		}
-		ll_rw_swap_file(rw,swapf->i_dev, zones, i,buf);
-	} else
-		printk("re_swap_page: no swap file or device\n");
-	if (offset && !clear_bit(offset,p->swap_lockmap))
-		printk("rw_swap_page: lock already cleared\n");
-	wake_up(&lock_queue);
-}
-
-unsigned long get_swap_page(void)
-{
-	struct swap_info_struct * p;
-	unsigned long offset, entry;
-	int type, wrapped = 0;
-
-	type = swap_list.next;
-	if (type < 0)
-	  return 0;
-
-	while (1) {
-		p = &swap_info[type];
-		if ((p->flags & SWP_WRITEOK) == SWP_WRITEOK) {
-			for (offset = p->lowest_bit; offset <= p->highest_bit ; offset++) {
-				if (p->swap_map[offset])
-				  continue;
-				if (test_bit(offset, p->swap_lockmap))
-				  continue;
-				p->swap_map[offset] = 1;
-				nr_swap_pages--;
-				if (offset == p->highest_bit)
-				  p->highest_bit--;
-				p->lowest_bit = offset;
-				entry = SWP_ENTRY(type,offset);
-
-				type = swap_info[type].next;
-				if (type < 0 || p->prio != swap_info[type].prio) {
-				    swap_list.next = swap_list.head;
-				} else {
-				    swap_list.next = type;
-				}
-				return entry;
-			}
-		}
-		type = p->next;
-		if (!wrapped) {
-			if (type < 0 || p->prio != swap_info[type].prio) {
-				type = swap_list.head;
-				wrapped = 1;
-			}
-		} else if (type < 0) {
-			return 0;	/* out of swap space */
-		}
-	}
-}
-
-void swap_duplicate(unsigned long entry)
-{
-	struct swap_info_struct * p;
-	unsigned long offset, type;
-
-	if (!entry)
-		return;
-	offset = SWP_OFFSET(entry);
-	type = SWP_TYPE(entry);
-	if (type & SHM_SWP_TYPE)
-		return;
-	if (type >= nr_swapfiles) {
-		printk("Trying to duplicate nonexistent swap-page\n");
-		return;
-	}
-	p = type + swap_info;
-	if (offset >= p->max) {
-		printk("swap_duplicate: weirdness\n");
-		return;
-	}
-	if (!p->swap_map[offset]) {
-		printk("swap_duplicate: trying to duplicate unused page\n");
-		return;
-	}
-	p->swap_map[offset]++;
-	return;
-}
-
-void swap_free(unsigned long entry)
-{
-	struct swap_info_struct * p;
-	unsigned long offset, type;
-
-	if (!entry)
-		return;
-	type = SWP_TYPE(entry);
-	if (type & SHM_SWP_TYPE)
-		return;
-	if (type >= nr_swapfiles) {
-		printk("Trying to free nonexistent swap-page\n");
-		return;
-	}
-	p = & swap_info[type];
-	offset = SWP_OFFSET(entry);
-	if (offset >= p->max) {
-		printk("swap_free: weirdness\n");
-		return;
-	}
-	if (!(p->flags & SWP_USED)) {
-		printk("Trying to free swap from unused swap-device\n");
-		return;
-	}
-	if (offset < p->lowest_bit)
-		p->lowest_bit = offset;
-	if (offset > p->highest_bit)
-		p->highest_bit = offset;
-	if (!p->swap_map[offset])
-		printk("swap_free: swap-space map bad (entry %08lx)\n",entry);
-	else
-		if (!--p->swap_map[offset])
-			nr_swap_pages++;
-	if (p->prio > swap_info[swap_list.next].prio) {
-	    swap_list.next = swap_list.head;
-	}
-}
-
-/*
- * The tests may look silly, but it essentially makes sure that
- * no other process did a swap-in on us just as we were waiting.
- *
- * Also, don't bother to add to the swap cache if this page-in
- * was due to a write access.
- */
-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);
-
-	if (pte_val(*page_table) != entry) {
-		free_page(page);
-		return;
-	}
-	if (!page) {
-		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;
-	}
-	vma->vm_mm->rss++;
-	tsk->maj_flt++;
-	if (!write_access && add_to_swap_cache(page, entry)) {
-		set_pte(page_table, mk_pte(page, vma->vm_page_prot));
-		return;
-	}
-	set_pte(page_table, pte_mkwrite(pte_mkdirty(mk_pte(page, vma->vm_page_prot))));
-  	swap_free(entry);
-  	return;
-}
-
-/*
- * The swap-out functions return 1 if they successfully
- * threw something out, and we got a free page. It returns
- * zero if it couldn't do anything, and any other value
- * indicates it decreased rss, but the page was shared.
- *
- * NOTE! If it sleeps, it *must* return 1 to make sure we
- * don't continue with the swap-out. Otherwise we may be
- * using a process that no longer actually exists (it might
- * have died while we slept).
- */
-static inline int try_to_swap_out(struct task_struct * tsk, struct vm_area_struct* vma,
-	unsigned long address, pte_t * page_table, unsigned long limit)
-{
-	pte_t pte;
-	unsigned long entry;
-	unsigned long page;
-	struct page * page_map;
-
-	pte = *page_table;
-	if (!pte_present(pte))
-		return 0;
-	page = pte_page(pte);
-	if (MAP_NR(page) >= MAP_NR(high_memory))
-		return 0;
-	if (page >= limit)
-		return 0;
-
-	page_map = mem_map + MAP_NR(page);
-	if (page_map->reserved)
-		return 0;
-	/* Deal with page aging.  Pages age from being unused; they
-	 * rejuvinate on being accessed.  Only swap old pages (age==0
-	 * is oldest). */
-	if ((pte_dirty(pte) && delete_from_swap_cache(page)) 
-	    || pte_young(pte))  {
-		set_pte(page_table, pte_mkold(pte));
-		page_age_update(page_map, 1);
-		return 0;
-	}	
-	if (page_age_update(page_map, pte_young(pte)))
-		return 0;
-	if (pte_dirty(pte)) {
-		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 (page_map->count != 1)
-				return 0;
-			if (!(entry = get_swap_page()))
-				return 0;
-			vma->vm_mm->rss--;
-			set_pte(page_table, __pte(entry));
-			invalidate_page(vma, address);
-			tsk->nswap++;
-			write_swap_page(entry, (char *) page);
-		}
-		free_page(page);
-		return 1;	/* we slept: the process may not exist any more */
-	}
-        if ((entry = find_in_swap_cache(page)))  {
-		if (page_map->count != 1) {
-			set_pte(page_table, pte_mkdirty(pte));
-			printk("Aiee.. duplicated cached swap-cache entry\n");
-			return 0;
-		}
-		vma->vm_mm->rss--;
-		set_pte(page_table, __pte(entry));
-		invalidate_page(vma, address);
-		free_page(page);
-		return 1;
-	} 
-	vma->vm_mm->rss--;
-	pte_clear(page_table);
-	invalidate_page(vma, address);
-	entry = page_unuse(page);
-	free_page(page);
-	return entry;
-}
-
-/*
- * A new implementation of swap_out().  We do not swap complete processes,
- * but only a small number of blocks, before we continue with the next
- * process.  The number of blocks actually swapped is determined on the
- * number of page faults, that this process actually had in the last time,
- * so we won't swap heavily used processes all the time ...
- *
- * Note: the priority argument is a hint on much CPU to waste with the
- *       swap block search, not a hint, of how much blocks to swap with
- *       each process.
- *
- * (C) 1993 Kai Petzke, wpp@marie.physik.tu-berlin.de
- */
-
-static inline int swap_out_pmd(struct task_struct * tsk, struct vm_area_struct * vma,
-	pmd_t *dir, unsigned long address, unsigned long end, unsigned long limit)
-{
-	pte_t * pte;
-	unsigned long pmd_end;
-
-	if (pmd_none(*dir))
-		return 0;
-	if (pmd_bad(*dir)) {
-		printk("swap_out_pmd: bad pmd (%08lx)\n", pmd_val(*dir));
-		pmd_clear(dir);
-		return 0;
-	}
-	
-	pte = pte_offset(dir, address);
-	
-	pmd_end = (address + PMD_SIZE) & PMD_MASK;
-	if (end > pmd_end)
-		end = pmd_end;
-
-	do {
-		int result;
-		tsk->swap_address = address + PAGE_SIZE;
-		result = try_to_swap_out(tsk, vma, address, pte, limit);
-		if (result)
-			return result;
-		address += PAGE_SIZE;
-		pte++;
-	} while (address < end);
-	return 0;
-}
-
-static inline int swap_out_pgd(struct task_struct * tsk, struct vm_area_struct * vma,
-	pgd_t *dir, unsigned long address, unsigned long end, unsigned long limit)
-{
-	pmd_t * pmd;
-	unsigned long pgd_end;
-
-	if (pgd_none(*dir))
-		return 0;
-	if (pgd_bad(*dir)) {
-		printk("swap_out_pgd: bad pgd (%08lx)\n", pgd_val(*dir));
-		pgd_clear(dir);
-		return 0;
-	}
-
-	pmd = pmd_offset(dir, address);
-
-	pgd_end = (address + PGDIR_SIZE) & PGDIR_MASK;	
-	if (end > pgd_end)
-		end = pgd_end;
-	
-	do {
-		int result = swap_out_pmd(tsk, vma, pmd, address, end, limit);
-		if (result)
-			return result;
-		address = (address + PMD_SIZE) & PMD_MASK;
-		pmd++;
-	} while (address < end);
-	return 0;
-}
-
-static int swap_out_vma(struct task_struct * tsk, struct vm_area_struct * vma,
-	pgd_t *pgdir, unsigned long start, unsigned long limit)
-{
-	unsigned long end;
-
-	/* Don't swap out areas like shared memory which have their
-	    own separate swapping mechanism or areas which are locked down */
-	if (vma->vm_flags & (VM_SHM | VM_LOCKED))
-		return 0;
-
-	end = vma->vm_end;
-	while (start < end) {
-		int result = swap_out_pgd(tsk, vma, pgdir, start, end, limit);
-		if (result)
-			return result;
-		start = (start + PGDIR_SIZE) & PGDIR_MASK;
-		pgdir++;
-	}
-	return 0;
-}
-
-static int swap_out_process(struct task_struct * p, unsigned long limit)
-{
-	unsigned long address;
-	struct vm_area_struct* vma;
-
-	/*
-	 * Go through process' page directory.
-	 */
-	address = p->swap_address;
-	p->swap_address = 0;
-
-	/*
-	 * Find the proper vm-area
-	 */
-	vma = find_vma(p, address);
-	if (!vma)
-		return 0;
-	if (address < vma->vm_start)
-		address = vma->vm_start;
-
-	for (;;) {
-		int result = swap_out_vma(p, vma, pgd_offset(p->mm, address), address, limit);
-		if (result)
-			return result;
-		vma = vma->vm_next;
-		if (!vma)
-			break;
-		address = vma->vm_start;
-	}
-	p->swap_address = 0;
-	return 0;
-}
-
-static int swap_out(unsigned int priority, unsigned long limit)
-{
-	static int swap_task;
-	int loop, counter;
-	struct task_struct *p;
-
-	counter = ((PAGEOUT_WEIGHT * nr_tasks) >> 10) >> priority;
-	for(; counter >= 0; counter--) {
-		/*
-		 * Check that swap_task is suitable for swapping.  If not, look for
-		 * the next suitable process.
-		 */
-		loop = 0;
-		while(1) {
-			if (swap_task >= NR_TASKS) {
-				swap_task = 1;
-				if (loop)
-					/* all processes are unswappable or already swapped out */
-					return 0;
-				loop = 1;
-			}
-
-			p = task[swap_task];
-			if (p && p->swappable && p->mm->rss)
-				break;
-
-			swap_task++;
-		}
-
-		/*
-		 * Determine the number of pages to swap from this process.
-		 */
-		if (!p->swap_cnt) {
- 			/* Normalise the number of pages swapped by
-			   multiplying by (RSS / 1MB) */
-			p->swap_cnt = AGE_CLUSTER_SIZE(p->mm->rss);
-		}
-		if (!--p->swap_cnt)
-			swap_task++;
-		switch (swap_out_process(p, limit)) {
-			case 0:
-				if (p->swap_cnt)
-					swap_task++;
-				break;
-			case 1:
-				return 1;
-			default:
-				break;
-		}
-	}
-	return 0;
-}
-
-/*
- * We are much more aggressive about trying to swap out than we used
- * to be.  This works out OK, because we now do proper aging on page
- * contents. 
- */
-static int try_to_free_page(int priority, unsigned long limit)
-{
-	static int state = 0;
-	int i=6;
-
-	switch (state) {
-		do {
-		case 0:
-			if (priority != GFP_NOBUFFER && shrink_buffers(i, limit))
-				return 1;
-			state = 1;
-		case 1:
-			if (shm_swap(i, limit))
-				return 1;
-			state = 2;
-		case 2:
-			if (shrink_mmap(i, limit))
-				return 1;
-			state = 3;
-		default:
-			if (swap_out(i, limit))
-				return 1;
-			state = 0;
-		} while(i--);
-	}
-	return 0;
-}
-
-static inline void add_mem_queue(struct mem_list * head, struct mem_list * entry)
-{
-	entry->prev = head;
-	(entry->next = head->next)->prev = entry;
-	head->next = entry;
-}
-
-static inline void remove_mem_queue(struct mem_list * head, struct mem_list * entry)
-{
-	struct mem_list * next = entry->next;
-	(next->prev = entry->prev)->next = next;
-}
-
-/*
- * Free_page() adds the page to the free lists. This is optimized for
- * fast normal cases (no error jumps taken normally).
- *
- * The way to optimize jumps for gcc-2.2.2 is to:
- *  - select the "normal" case and put it inside the if () { XXX }
- *  - no else-statements if you can avoid them
- *
- * With the above two rules, you get a straight-line execution path
- * for the normal case, giving better asm-code.
- *
- * free_page() may sleep since the page being freed may be a buffer
- * page or present in the swap cache. It will not sleep, however,
- * for a freshly allocated page (get_free_page()).
- */
-
-/*
- * Buddy system. Hairy. You really aren't expected to understand this
- */
-static inline void free_pages_ok(unsigned long addr, unsigned long order)
-{
-	unsigned long index = MAP_NR(addr) >> (1 + order);
-	unsigned long mask = PAGE_MASK << order;
-
-	addr &= mask;
-	nr_free_pages += 1 << order;
-	while (order < NR_MEM_LISTS-1) {
-		if (!change_bit(index, free_area_map[order]))
-			break;
-		remove_mem_queue(free_area_list+order, (struct mem_list *) (addr ^ (1+~mask)));
-		order++;
-		index >>= 1;
-		mask <<= 1;
-		addr &= mask;
-	}
-	add_mem_queue(free_area_list+order, (struct mem_list *) addr);
-}
-
-static inline void check_free_buffers(unsigned long addr)
-{
-	struct buffer_head * bh;
-
-	bh = buffer_pages[MAP_NR(addr)];
-	if (bh) {
-		struct buffer_head *tmp = bh;
-		do {
-			if (tmp->b_list == BUF_SHARED
-			    && tmp->b_dev != B_FREE)
-				refile_buffer(tmp);
-			tmp = tmp->b_this_page;
-		} while (tmp != bh);
-	}
-}
-
-void free_pages(unsigned long addr, unsigned long order)
-{
-	if (MAP_NR(addr) < MAP_NR(high_memory)) {
-		unsigned long flag;
-		mem_map_t * map = mem_map + MAP_NR(addr);
-		if (map->reserved)
-			return;
-		if (map->count) {
-			save_flags(flag);
-			cli();
-			if (!--map->count) {
-				free_pages_ok(addr, order);
-				delete_from_swap_cache(addr);
-			}
-			restore_flags(flag);
-			if (map->count == 1)
-				check_free_buffers(addr);
-			return;
-		}
-		printk("Trying to free free memory (%08lx): memory probably corrupted\n",addr);
-		printk("PC = %p\n", __builtin_return_address(0));
-		return;
-	}
-}
-
-/*
- * Some ugly macros to speed up __get_free_pages()..
- */
-#define RMQUEUE(order, limit) \
-do { struct mem_list * queue = free_area_list+order; \
-     unsigned long new_order = order; \
-	do { struct mem_list *prev = queue, *ret; \
-		while (queue != (ret = prev->next)) { \
-			if ((unsigned long) ret < (limit)) { \
-				(prev->next = ret->next)->prev = prev; \
-				mark_used((unsigned long) ret, new_order); \
-				nr_free_pages -= 1 << order; \
-				restore_flags(flags); \
-				EXPAND(ret, order, new_order); \
-				return (unsigned long) ret; \
-			} \
-			prev = ret; \
-		} \
-		new_order++; queue++; \
-	} while (new_order < NR_MEM_LISTS); \
-} while (0)
-
-static inline int mark_used(unsigned long addr, unsigned long order)
-{
-	return change_bit(MAP_NR(addr) >> (1+order), free_area_map[order]);
-}
-
-#define EXPAND(addr,low,high) \
-do { unsigned long size = PAGE_SIZE << high; \
-	while (high > low) { \
-		high--; size >>= 1; cli(); \
-		add_mem_queue(free_area_list+high, addr); \
-		mark_used((unsigned long) addr, high); \
-		restore_flags(flags); \
-		addr = (struct mem_list *) (size + (unsigned long) addr); \
-	} mem_map[MAP_NR((unsigned long) addr)].count = 1; \
-	mem_map[MAP_NR((unsigned long) addr)].age = PAGE_INITIAL_AGE; \
-} while (0)
-
-unsigned long __get_free_pages(int priority, unsigned long order, unsigned long limit)
-{
-	unsigned long flags;
-	int reserved_pages;
-
-	if (order >= NR_MEM_LISTS)
-		return 0;
-	if (intr_count && priority != GFP_ATOMIC) {
-		static int count = 0;
-		if (++count < 5) {
-			printk("gfp called nonatomically from interrupt %p\n",
-				__builtin_return_address(0));
-			priority = GFP_ATOMIC;
-		}
-	}
-	reserved_pages = 5;
-	if (priority != GFP_NFS)
-		reserved_pages = min_free_pages;
-	save_flags(flags);
-repeat:
-	cli();
-	if ((priority==GFP_ATOMIC) || nr_free_pages > reserved_pages) {
-		RMQUEUE(order, limit);
-		restore_flags(flags);
-		return 0;
-	}
-	restore_flags(flags);
-	if (priority != GFP_BUFFER && try_to_free_page(priority, limit))
-		goto repeat;
-	return 0;
-}
-
-/*
- * Show free area list (used inside shift_scroll-lock stuff)
- * We also calculate the percentage fragmentation. We do this by counting the
- * memory on each free list with the exception of the first item on the list.
- */
-void show_free_areas(void)
-{
- 	unsigned long order, flags;
- 	unsigned long total = 0;
-
-	printk("Free pages:      %6dkB\n ( ",nr_free_pages<<(PAGE_SHIFT-10));
-	save_flags(flags);
-	cli();
- 	for (order=0 ; order < NR_MEM_LISTS; order++) {
-		struct mem_list * tmp;
-		unsigned long nr = 0;
-		for (tmp = free_area_list[order].next ; tmp != free_area_list + order ; tmp = tmp->next) {
-			nr ++;
-		}
-		total += nr * ((PAGE_SIZE>>10) << order);
-		printk("%lu*%lukB ", nr, (PAGE_SIZE>>10) << order);
-	}
-	restore_flags(flags);
-	printk("= %lukB)\n", total);
-#ifdef SWAP_CACHE_INFO
-	show_swap_cache_info();
-#endif	
-}
-
-/*
- * Trying to stop swapping from a file is fraught with races, so
- * we repeat quite a bit here when we have to pause. swapoff()
- * isn't exactly timing-critical, so who cares (but this is /really/
- * inefficient, ugh).
- *
- * We return 1 after having slept, which makes the process start over
- * from the beginning for this process..
- */
-static inline int unuse_pte(struct vm_area_struct * vma, unsigned long address,
-	pte_t *dir, unsigned int type, unsigned long page)
-{
-	pte_t pte = *dir;
-
-	if (pte_none(pte))
-		return 0;
-	if (pte_present(pte)) {
-		unsigned long page = pte_page(pte);
-		if (page >= high_memory)
-			return 0;
-		if (!in_swap_cache(page))
-			return 0;
-		if (SWP_TYPE(in_swap_cache(page)) != type)
-			return 0;
-		delete_from_swap_cache(page);
-		set_pte(dir, pte_mkdirty(pte));
-		return 0;
-	}
-	if (SWP_TYPE(pte_val(pte)) != type)
-		return 0;
-	read_swap_page(pte_val(pte), (char *) page);
-	if (pte_val(*dir) != pte_val(pte)) {
-		free_page(page);
-		return 1;
-	}
-	set_pte(dir, pte_mkwrite(pte_mkdirty(mk_pte(page, vma->vm_page_prot))));
-	++vma->vm_mm->rss;
-	swap_free(pte_val(pte));
-	return 1;
-}
-
-static inline int unuse_pmd(struct vm_area_struct * vma, pmd_t *dir,
-	unsigned long address, unsigned long size, unsigned long offset,
-	unsigned int type, unsigned long page)
-{
-	pte_t * pte;
-	unsigned long end;
-
-	if (pmd_none(*dir))
-		return 0;
-	if (pmd_bad(*dir)) {
-		printk("unuse_pmd: bad pmd (%08lx)\n", pmd_val(*dir));
-		pmd_clear(dir);
-		return 0;
-	}
-	pte = pte_offset(dir, address);
-	offset += address & PMD_MASK;
-	address &= ~PMD_MASK;
-	end = address + size;
-	if (end > PMD_SIZE)
-		end = PMD_SIZE;
-	do {
-		if (unuse_pte(vma, offset+address-vma->vm_start, pte, type, page))
-			return 1;
-		address += PAGE_SIZE;
-		pte++;
-	} while (address < end);
-	return 0;
-}
-
-static inline int unuse_pgd(struct vm_area_struct * vma, pgd_t *dir,
-	unsigned long address, unsigned long size,
-	unsigned int type, unsigned long page)
-{
-	pmd_t * pmd;
-	unsigned long offset, end;
-
-	if (pgd_none(*dir))
-		return 0;
-	if (pgd_bad(*dir)) {
-		printk("unuse_pgd: bad pgd (%08lx)\n", pgd_val(*dir));
-		pgd_clear(dir);
-		return 0;
-	}
-	pmd = pmd_offset(dir, address);
-	offset = address & PGDIR_MASK;
-	address &= ~PGDIR_MASK;
-	end = address + size;
-	if (end > PGDIR_SIZE)
-		end = PGDIR_SIZE;
-	do {
-		if (unuse_pmd(vma, pmd, address, end - address, offset, type, page))
-			return 1;
-		address = (address + PMD_SIZE) & PMD_MASK;
-		pmd++;
-	} while (address < end);
-	return 0;
-}
-
-static int unuse_vma(struct vm_area_struct * vma, pgd_t *pgdir,
-	unsigned long start, unsigned long end,
-	unsigned int type, unsigned long page)
-{
-	while (start < end) {
-		if (unuse_pgd(vma, pgdir, start, end - start, type, page))
-			return 1;
-		start = (start + PGDIR_SIZE) & PGDIR_MASK;
-		pgdir++;
-	}
-	return 0;
-}
-
-static int unuse_process(struct task_struct * p, unsigned int type, unsigned long page)
-{
-	struct vm_area_struct* vma;
-
-	/*
-	 * Go through process' page directory.
-	 */
-	if (!p->mm || pgd_inuse(p->mm->pgd))
-		return 0;
-	vma = p->mm->mmap;
-	while (vma) {
-		pgd_t * pgd = pgd_offset(p->mm, vma->vm_start);
-		if (unuse_vma(vma, pgd, vma->vm_start, vma->vm_end, type, page))
-			return 1;
-		vma = vma->vm_next;
-	}
-	return 0;
-}
-
-/*
- * To avoid races, we repeat for each process after having
- * swapped something in. That gets rid of a few pesky races,
- * and "swapoff" isn't exactly timing critical.
- */
-static int try_to_unuse(unsigned int type)
-{
-	int nr;
-	unsigned long page = get_free_page(GFP_KERNEL);
-
-	if (!page)
-		return -ENOMEM;
-	nr = 0;
-	while (nr < NR_TASKS) {
-		if (task[nr]) {
-			if (unuse_process(task[nr], type, page)) {
-				page = get_free_page(GFP_KERNEL);
-				if (!page)
-					return -ENOMEM;
-				continue;
-			}
-		}
-		nr++;
-	}
-	free_page(page);
-	return 0;
-}
-
-asmlinkage int sys_swapoff(const char * specialfile)
-{
-	struct swap_info_struct * p;
-	struct inode * inode;
-	struct file filp;
-	int i, type, prev;
-
-	if (!suser())
-		return -EPERM;
-	i = namei(specialfile,&inode);
-	if (i)
-		return i;
-	prev = -1;
-	for (type = swap_list.head; type >= 0; type = swap_info[type].next) {
-		p = swap_info + type;
-		if ((p->flags & SWP_WRITEOK) == SWP_WRITEOK) {
-			if (p->swap_file) {
-				if (p->swap_file == inode)
-				  break;
-			} else {
-				if (S_ISBLK(inode->i_mode)
-				    && (p->swap_device == inode->i_rdev))
-				  break;
-			}
-		}
-		prev = type;
-	}
-	if (type < 0){
-		iput(inode);
-		return -EINVAL;
-	}
-	if (prev < 0) {
-		swap_list.head = p->next;
-	} else {
-		swap_info[prev].next = p->next;
-	}
-	if (type == swap_list.next) {
-		/* just pick something that's safe... */
-		swap_list.next = swap_list.head;
-	}
-	p->flags = SWP_USED;
-	i = try_to_unuse(type);
-	if (i) {
-		iput(inode);
-		p->flags = SWP_WRITEOK;
-		return i;
-	}
-
-	if(p->swap_device){
-		memset(&filp, 0, sizeof(filp));		
-		filp.f_inode = inode;
-		filp.f_mode = 3; /* read write */
-		/* open it again to get fops */
-		if( !blkdev_open(inode, &filp) &&
-		   filp.f_op && filp.f_op->release){
-			filp.f_op->release(inode,&filp);
-			filp.f_op->release(inode,&filp);
-		}
-	}
-	iput(inode);
-
-	nr_swap_pages -= p->pages;
-	iput(p->swap_file);
-	p->swap_file = NULL;
-	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;
-	return 0;
-}
-
-/*
- * Written 01/25/92 by Simmule Turner, heavily changed by Linus.
- *
- * The swapon system call
- */
-asmlinkage int sys_swapon(const char * specialfile, int swap_flags)
-{
-	struct swap_info_struct * p;
-	struct inode * swap_inode;
-	unsigned int type;
-	int i, j, prev;
-	int error;
-	struct file filp;
-	static int least_priority = 0;
-
-	memset(&filp, 0, sizeof(filp));
-	if (!suser())
-		return -EPERM;
-	p = swap_info;
-	for (type = 0 ; type < nr_swapfiles ; type++,p++)
-		if (!(p->flags & SWP_USED))
-			break;
-	if (type >= MAX_SWAPFILES)
-		return -EPERM;
-	if (type >= nr_swapfiles)
-		nr_swapfiles = type+1;
-	p->flags = SWP_USED;
-	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->max = 1;
-	p->next = -1;
-	if (swap_flags & SWAP_FLAG_PREFER) {
-		p->prio =
-		  (swap_flags & SWAP_FLAG_PRIO_MASK)>>SWAP_FLAG_PRIO_SHIFT;
-	} else {
-		p->prio = --least_priority;
-	}
-	error = namei(specialfile,&swap_inode);
-	if (error)
-		goto bad_swap_2;
-	p->swap_file = swap_inode;
-	error = -EBUSY;
-	if (swap_inode->i_count != 1)
-		goto bad_swap_2;
-	error = -EINVAL;
-
-	if (S_ISBLK(swap_inode->i_mode)) {
-		p->swap_device = swap_inode->i_rdev;
-
-		filp.f_inode = swap_inode;
-		filp.f_mode = 3; /* read write */
-		error = blkdev_open(swap_inode, &filp);
-		p->swap_file = NULL;
-		iput(swap_inode);
-		if(error)
-			goto bad_swap_2;
-		error = -ENODEV;
-		if (!p->swap_device)
-			goto bad_swap;
-		error = -EBUSY;
-		for (i = 0 ; i < nr_swapfiles ; i++) {
-			if (i == type)
-				continue;
-			if (p->swap_device == swap_info[i].swap_device)
-				goto bad_swap;
-		}
-	} else if (!S_ISREG(swap_inode->i_mode))
-		goto bad_swap;
-	p->swap_lockmap = (unsigned char *) get_free_page(GFP_USER);
-	if (!p->swap_lockmap) {
-		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)) {
-		printk("Unable to find swap-space signature\n");
-		error = -EINVAL;
-		goto bad_swap;
-	}
-	memset(p->swap_lockmap+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 (!p->lowest_bit)
-				p->lowest_bit = i;
-			p->highest_bit = i;
-			p->max = i+1;
-			j++;
-		}
-	}
-	if (!j) {
-		printk("Empty swap-file\n");
-		error = -EINVAL;
-		goto bad_swap;
-	}
-	p->swap_map = (unsigned char *) vmalloc(p->max);
-	if (!p->swap_map) {
-		error = -ENOMEM;
-		goto bad_swap;
-	}
-	for (i = 1 ; i < p->max ; i++) {
-		if (test_bit(i,p->swap_lockmap))
-			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;
-	printk("Adding Swap: %dk swap-space\n",j<<(PAGE_SHIFT-10));
-
-	/* insert swap space into swap_list: */
-	prev = -1;
-	for (i = swap_list.head; i >= 0; i = swap_info[i].next) {
-		if (p->prio >= swap_info[i].prio) {
-			break;
-		}
-		prev = i;
-	}
-	p->next = i;
-	if (prev < 0) {
-		swap_list.head = swap_list.next = p - swap_info;
-	} else {
-		swap_info[prev].next = p - swap_info;
-	}
-	return 0;
-bad_swap:
-	if(filp.f_op && filp.f_op->release)
-		filp.f_op->release(filp.f_inode,&filp);
-bad_swap_2:
-	free_page((long) p->swap_lockmap);
-	vfree(p->swap_map);
-	iput(p->swap_file);
-	p->swap_device = 0;
-	p->swap_file = NULL;
-	p->swap_map = NULL;
-	p->swap_lockmap = NULL;
-	p->flags = 0;
-	return error;
-}
-
-void si_swapinfo(struct sysinfo *val)
-{
-	unsigned int i, j;
-
-	val->freeswap = val->totalswap = 0;
-	for (i = 0; i < nr_swapfiles; i++) {
-		if ((swap_info[i].flags & SWP_WRITEOK) != SWP_WRITEOK)
-			continue;
-		for (j = 0; j < swap_info[i].max; ++j)
-			switch (swap_info[i].swap_map[j]) {
-				case 128:
-					continue;
-				case 0:
-					++val->freeswap;
-				default:
-					++val->totalswap;
-			}
-	}
-	val->freeswap <<= PAGE_SHIFT;
-	val->totalswap <<= PAGE_SHIFT;
-	return;
-}
-
-#define LONG_ALIGN(x) (((x)+(sizeof(long))-1)&~((sizeof(long))-1))
-
-/*
- * set up the free-area data structures:
- *   - mark all pages reserved
- *   - mark all memory queues empty
- *   - clear the memory bitmaps
- */
-unsigned long free_area_init(unsigned long start_mem, unsigned long end_mem)
-{
-	mem_map_t * p;
-	unsigned long mask = PAGE_MASK;
-	int i;
-
-	/*
-	 * select nr of pages we try to keep free for important stuff
-	 * with a minimum of 16 pages. This is totally arbitrary
-	 */
-	i = (end_mem - PAGE_OFFSET) >> (PAGE_SHIFT+6);
-	if (i < 16)
-		i = 16;
-	min_free_pages = i;
-	start_mem = init_swap_cache(start_mem, end_mem);
-	mem_map = (mem_map_t *) start_mem;
-	p = mem_map + MAP_NR(end_mem);
-	start_mem = LONG_ALIGN((unsigned long) p);
-	memset(mem_map, 0, start_mem - (unsigned long) mem_map);
-	do {
-		--p;
-		p->reserved = 1;
-	} while (p > mem_map);
-
-	for (i = 0 ; i < NR_MEM_LISTS ; i++) {
-		unsigned long bitmap_size;
-		free_area_list[i].prev = free_area_list[i].next = &free_area_list[i];
-		mask += mask;
-		end_mem = (end_mem + ~mask) & mask;
-		bitmap_size = (end_mem - PAGE_OFFSET) >> (PAGE_SHIFT + i);
-		bitmap_size = (bitmap_size + 7) >> 3;
-		bitmap_size = LONG_ALIGN(bitmap_size);
-		free_area_map[i] = (unsigned int *) start_mem;
-		memset((void *) start_mem, 0, bitmap_size);
-		start_mem += bitmap_size;
-	}
-	return start_mem;
-}
diff -u --recursive --new-file v1.3.56/linux/mm/swap_state.c linux/mm/swap_state.c
--- v1.3.56/linux/mm/swap_state.c	Thu Jan  1 02:00:00 1970
+++ linux/mm/swap_state.c	Tue Jan  9 20:41:31 1996
@@ -0,0 +1,111 @@
+/*
+ *  linux/mm/swap_state.c
+ *
+ *  Copyright (C) 1991, 1992, 1993, 1994  Linus Torvalds
+ *  Swap reorganised 29.12.95, Stephen Tweedie
+ */
+
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/head.h>
+#include <linux/kernel.h>
+#include <linux/kernel_stat.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/stat.h>
+#include <linux/swap.h>
+#include <linux/fs.h>
+#include <linux/swapctl.h>
+
+#include <asm/dma.h>
+#include <asm/system.h> /* for cli()/sti() */
+#include <asm/segment.h> /* for memcpy_to/fromfs */
+#include <asm/bitops.h>
+#include <asm/pgtable.h>
+
+/*
+ * To save us from swapping out pages which have just been swapped in and
+ * have not been modified since then, we keep in swap_cache[page>>PAGE_SHIFT]
+ * the swap entry which was last used to fill the page, or zero if the
+ * page does not currently correspond to a page in swap. PAGE_DIRTY makes
+ * this info useless.
+ */
+unsigned long *swap_cache;
+
+#ifdef SWAP_CACHE_INFO
+unsigned long swap_cache_add_total = 0;
+unsigned long swap_cache_add_success = 0;
+unsigned long swap_cache_del_total = 0;
+unsigned long swap_cache_del_success = 0;
+unsigned long swap_cache_find_total = 0;
+unsigned long swap_cache_find_success = 0;
+
+void show_swap_cache_info(void)
+{
+	printk("Swap cache: add %ld/%ld, delete %ld/%ld, find %ld/%ld\n",
+		swap_cache_add_total, swap_cache_add_success, 
+		swap_cache_del_total, swap_cache_del_success,
+		swap_cache_find_total, swap_cache_find_success);
+}
+#endif
+
+int add_to_swap_cache(unsigned long addr, unsigned long entry)
+{
+	struct swap_info_struct * p = &swap_info[SWP_TYPE(entry)];
+
+#ifdef SWAP_CACHE_INFO
+	swap_cache_add_total++;
+#endif
+	if ((p->flags & SWP_WRITEOK) == SWP_WRITEOK) {
+		entry = xchg(swap_cache + MAP_NR(addr), entry);
+		if (entry)  {
+			printk("swap_cache: replacing non-NULL entry\n");
+		}
+#ifdef SWAP_CACHE_INFO
+		swap_cache_add_success++;
+#endif
+		return 1;
+	}
+	return 0;
+}
+
+unsigned long init_swap_cache(unsigned long mem_start,
+	unsigned long mem_end)
+{
+	unsigned long swap_cache_size;
+
+	mem_start = (mem_start + 15) & ~15;
+	swap_cache = (unsigned long *) mem_start;
+	swap_cache_size = MAP_NR(mem_end);
+	memset(swap_cache, 0, swap_cache_size * sizeof (unsigned long));
+	return (unsigned long) (swap_cache + swap_cache_size);
+}
+
+void swap_duplicate(unsigned long entry)
+{
+	struct swap_info_struct * p;
+	unsigned long offset, type;
+
+	if (!entry)
+		return;
+	offset = SWP_OFFSET(entry);
+	type = SWP_TYPE(entry);
+	if (type & SHM_SWP_TYPE)
+		return;
+	if (type >= nr_swapfiles) {
+		printk("Trying to duplicate nonexistent swap-page\n");
+		return;
+	}
+	p = type + swap_info;
+	if (offset >= p->max) {
+		printk("swap_duplicate: weirdness\n");
+		return;
+	}
+	if (!p->swap_map[offset]) {
+		printk("swap_duplicate: trying to duplicate unused page\n");
+		return;
+	}
+	p->swap_map[offset]++;
+	return;
+}
+
diff -u --recursive --new-file v1.3.56/linux/mm/swapfile.c linux/mm/swapfile.c
--- v1.3.56/linux/mm/swapfile.c	Thu Jan  1 02:00:00 1970
+++ linux/mm/swapfile.c	Tue Jan  9 20:41:31 1996
@@ -0,0 +1,522 @@
+/*
+ *  linux/mm/swapfile.c
+ *
+ *  Copyright (C) 1991, 1992, 1993, 1994  Linus Torvalds
+ *  Swap reorganised 29.12.95, Stephen Tweedie
+ */
+
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/head.h>
+#include <linux/kernel.h>
+#include <linux/kernel_stat.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/stat.h>
+#include <linux/swap.h>
+#include <linux/fs.h>
+#include <linux/swapctl.h>
+
+#include <asm/dma.h>
+#include <asm/system.h> /* for cli()/sti() */
+#include <asm/segment.h> /* for memcpy_to/fromfs */
+#include <asm/bitops.h>
+#include <asm/pgtable.h>
+
+int nr_swapfiles = 0;
+static struct {
+	int head;	/* head of priority-ordered swapfile list */
+	int next;	/* swapfile to be used next */
+} swap_list = {-1, -1};
+
+struct swap_info_struct swap_info[MAX_SWAPFILES];
+
+unsigned long get_swap_page(void)
+{
+	struct swap_info_struct * p;
+	unsigned long offset, entry;
+	int type, wrapped = 0;
+
+	type = swap_list.next;
+	if (type < 0)
+	  return 0;
+
+	while (1) {
+		p = &swap_info[type];
+		if ((p->flags & SWP_WRITEOK) == SWP_WRITEOK) {
+			for (offset = p->lowest_bit; offset <= p->highest_bit ; offset++) {
+				if (p->swap_map[offset])
+				  continue;
+				if (test_bit(offset, p->swap_lockmap))
+				  continue;
+				p->swap_map[offset] = 1;
+				nr_swap_pages--;
+				if (offset == p->highest_bit)
+				  p->highest_bit--;
+				p->lowest_bit = offset;
+				entry = SWP_ENTRY(type,offset);
+
+				type = swap_info[type].next;
+				if (type < 0 || p->prio != swap_info[type].prio) {
+				    swap_list.next = swap_list.head;
+				} else {
+				    swap_list.next = type;
+				}
+				return entry;
+			}
+		}
+		type = p->next;
+		if (!wrapped) {
+			if (type < 0 || p->prio != swap_info[type].prio) {
+				type = swap_list.head;
+				wrapped = 1;
+			}
+		} else if (type < 0) {
+			return 0;	/* out of swap space */
+		}
+	}
+}
+
+void swap_free(unsigned long entry)
+{
+	struct swap_info_struct * p;
+	unsigned long offset, type;
+
+	if (!entry)
+		return;
+	type = SWP_TYPE(entry);
+	if (type & SHM_SWP_TYPE)
+		return;
+	if (type >= nr_swapfiles) {
+		printk("Trying to free nonexistent swap-page\n");
+		return;
+	}
+	p = & swap_info[type];
+	offset = SWP_OFFSET(entry);
+	if (offset >= p->max) {
+		printk("swap_free: weirdness\n");
+		return;
+	}
+	if (!(p->flags & SWP_USED)) {
+		printk("Trying to free swap from unused swap-device\n");
+		return;
+	}
+	if (offset < p->lowest_bit)
+		p->lowest_bit = offset;
+	if (offset > p->highest_bit)
+		p->highest_bit = offset;
+	if (!p->swap_map[offset])
+		printk("swap_free: swap-space map bad (entry %08lx)\n",entry);
+	else
+		if (!--p->swap_map[offset])
+			nr_swap_pages++;
+	if (p->prio > swap_info[swap_list.next].prio) {
+	    swap_list.next = swap_list.head;
+	}
+}
+
+/*
+ * Trying to stop swapping from a file is fraught with races, so
+ * we repeat quite a bit here when we have to pause. swapoff()
+ * isn't exactly timing-critical, so who cares (but this is /really/
+ * inefficient, ugh).
+ *
+ * We return 1 after having slept, which makes the process start over
+ * from the beginning for this process..
+ */
+static inline int unuse_pte(struct vm_area_struct * vma, unsigned long address,
+	pte_t *dir, unsigned int type, unsigned long page)
+{
+	pte_t pte = *dir;
+
+	if (pte_none(pte))
+		return 0;
+	if (pte_present(pte)) {
+		unsigned long page = pte_page(pte);
+		if (page >= high_memory)
+			return 0;
+		if (!in_swap_cache(page))
+			return 0;
+		if (SWP_TYPE(in_swap_cache(page)) != type)
+			return 0;
+		delete_from_swap_cache(page);
+		set_pte(dir, pte_mkdirty(pte));
+		return 0;
+	}
+	if (SWP_TYPE(pte_val(pte)) != type)
+		return 0;
+	read_swap_page(pte_val(pte), (char *) page);
+	if (pte_val(*dir) != pte_val(pte)) {
+		free_page(page);
+		return 1;
+	}
+	set_pte(dir, pte_mkwrite(pte_mkdirty(mk_pte(page, vma->vm_page_prot))));
+	++vma->vm_mm->rss;
+	swap_free(pte_val(pte));
+	return 1;
+}
+
+static inline int unuse_pmd(struct vm_area_struct * vma, pmd_t *dir,
+	unsigned long address, unsigned long size, unsigned long offset,
+	unsigned int type, unsigned long page)
+{
+	pte_t * pte;
+	unsigned long end;
+
+	if (pmd_none(*dir))
+		return 0;
+	if (pmd_bad(*dir)) {
+		printk("unuse_pmd: bad pmd (%08lx)\n", pmd_val(*dir));
+		pmd_clear(dir);
+		return 0;
+	}
+	pte = pte_offset(dir, address);
+	offset += address & PMD_MASK;
+	address &= ~PMD_MASK;
+	end = address + size;
+	if (end > PMD_SIZE)
+		end = PMD_SIZE;
+	do {
+		if (unuse_pte(vma, offset+address-vma->vm_start, pte, type, page))
+			return 1;
+		address += PAGE_SIZE;
+		pte++;
+	} while (address < end);
+	return 0;
+}
+
+static inline int unuse_pgd(struct vm_area_struct * vma, pgd_t *dir,
+	unsigned long address, unsigned long size,
+	unsigned int type, unsigned long page)
+{
+	pmd_t * pmd;
+	unsigned long offset, end;
+
+	if (pgd_none(*dir))
+		return 0;
+	if (pgd_bad(*dir)) {
+		printk("unuse_pgd: bad pgd (%08lx)\n", pgd_val(*dir));
+		pgd_clear(dir);
+		return 0;
+	}
+	pmd = pmd_offset(dir, address);
+	offset = address & PGDIR_MASK;
+	address &= ~PGDIR_MASK;
+	end = address + size;
+	if (end > PGDIR_SIZE)
+		end = PGDIR_SIZE;
+	do {
+		if (unuse_pmd(vma, pmd, address, end - address, offset, type, page))
+			return 1;
+		address = (address + PMD_SIZE) & PMD_MASK;
+		pmd++;
+	} while (address < end);
+	return 0;
+}
+
+static int unuse_vma(struct vm_area_struct * vma, pgd_t *pgdir,
+	unsigned long start, unsigned long end,
+	unsigned int type, unsigned long page)
+{
+	while (start < end) {
+		if (unuse_pgd(vma, pgdir, start, end - start, type, page))
+			return 1;
+		start = (start + PGDIR_SIZE) & PGDIR_MASK;
+		pgdir++;
+	}
+	return 0;
+}
+
+static int unuse_process(struct task_struct * p, unsigned int type, unsigned long page)
+{
+	struct vm_area_struct* vma;
+
+	/*
+	 * Go through process' page directory.
+	 */
+	if (!p->mm || pgd_inuse(p->mm->pgd))
+		return 0;
+	vma = p->mm->mmap;
+	while (vma) {
+		pgd_t * pgd = pgd_offset(p->mm, vma->vm_start);
+		if (unuse_vma(vma, pgd, vma->vm_start, vma->vm_end, type, page))
+			return 1;
+		vma = vma->vm_next;
+	}
+	return 0;
+}
+
+/*
+ * To avoid races, we repeat for each process after having
+ * swapped something in. That gets rid of a few pesky races,
+ * and "swapoff" isn't exactly timing critical.
+ */
+static int try_to_unuse(unsigned int type)
+{
+	int nr;
+	unsigned long page = get_free_page(GFP_KERNEL);
+
+	if (!page)
+		return -ENOMEM;
+	nr = 0;
+	while (nr < NR_TASKS) {
+		if (task[nr]) {
+			if (unuse_process(task[nr], type, page)) {
+				page = get_free_page(GFP_KERNEL);
+				if (!page)
+					return -ENOMEM;
+				continue;
+			}
+		}
+		nr++;
+	}
+	free_page(page);
+	return 0;
+}
+
+asmlinkage int sys_swapoff(const char * specialfile)
+{
+	struct swap_info_struct * p;
+	struct inode * inode;
+	struct file filp;
+	int i, type, prev;
+
+	if (!suser())
+		return -EPERM;
+	i = namei(specialfile,&inode);
+	if (i)
+		return i;
+	prev = -1;
+	for (type = swap_list.head; type >= 0; type = swap_info[type].next) {
+		p = swap_info + type;
+		if ((p->flags & SWP_WRITEOK) == SWP_WRITEOK) {
+			if (p->swap_file) {
+				if (p->swap_file == inode)
+				  break;
+			} else {
+				if (S_ISBLK(inode->i_mode)
+				    && (p->swap_device == inode->i_rdev))
+				  break;
+			}
+		}
+		prev = type;
+	}
+	if (type < 0){
+		iput(inode);
+		return -EINVAL;
+	}
+	if (prev < 0) {
+		swap_list.head = p->next;
+	} else {
+		swap_info[prev].next = p->next;
+	}
+	if (type == swap_list.next) {
+		/* just pick something that's safe... */
+		swap_list.next = swap_list.head;
+	}
+	p->flags = SWP_USED;
+	i = try_to_unuse(type);
+	if (i) {
+		iput(inode);
+		p->flags = SWP_WRITEOK;
+		return i;
+	}
+
+	if(p->swap_device){
+		memset(&filp, 0, sizeof(filp));		
+		filp.f_inode = inode;
+		filp.f_mode = 3; /* read write */
+		/* open it again to get fops */
+		if( !blkdev_open(inode, &filp) &&
+		   filp.f_op && filp.f_op->release){
+			filp.f_op->release(inode,&filp);
+			filp.f_op->release(inode,&filp);
+		}
+	}
+	iput(inode);
+
+	nr_swap_pages -= p->pages;
+	iput(p->swap_file);
+	p->swap_file = NULL;
+	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;
+	return 0;
+}
+
+/*
+ * Written 01/25/92 by Simmule Turner, heavily changed by Linus.
+ *
+ * The swapon system call
+ */
+asmlinkage int sys_swapon(const char * specialfile, int swap_flags)
+{
+	struct swap_info_struct * p;
+	struct inode * swap_inode;
+	unsigned int type;
+	int i, j, prev;
+	int error;
+	struct file filp;
+	static int least_priority = 0;
+
+	memset(&filp, 0, sizeof(filp));
+	if (!suser())
+		return -EPERM;
+	p = swap_info;
+	for (type = 0 ; type < nr_swapfiles ; type++,p++)
+		if (!(p->flags & SWP_USED))
+			break;
+	if (type >= MAX_SWAPFILES)
+		return -EPERM;
+	if (type >= nr_swapfiles)
+		nr_swapfiles = type+1;
+	p->flags = SWP_USED;
+	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->max = 1;
+	p->next = -1;
+	if (swap_flags & SWAP_FLAG_PREFER) {
+		p->prio =
+		  (swap_flags & SWAP_FLAG_PRIO_MASK)>>SWAP_FLAG_PRIO_SHIFT;
+	} else {
+		p->prio = --least_priority;
+	}
+	error = namei(specialfile,&swap_inode);
+	if (error)
+		goto bad_swap_2;
+	p->swap_file = swap_inode;
+	error = -EBUSY;
+	if (swap_inode->i_count != 1)
+		goto bad_swap_2;
+	error = -EINVAL;
+
+	if (S_ISBLK(swap_inode->i_mode)) {
+		p->swap_device = swap_inode->i_rdev;
+
+		filp.f_inode = swap_inode;
+		filp.f_mode = 3; /* read write */
+		error = blkdev_open(swap_inode, &filp);
+		p->swap_file = NULL;
+		iput(swap_inode);
+		if(error)
+			goto bad_swap_2;
+		error = -ENODEV;
+		if (!p->swap_device)
+			goto bad_swap;
+		error = -EBUSY;
+		for (i = 0 ; i < nr_swapfiles ; i++) {
+			if (i == type)
+				continue;
+			if (p->swap_device == swap_info[i].swap_device)
+				goto bad_swap;
+		}
+	} else if (!S_ISREG(swap_inode->i_mode))
+		goto bad_swap;
+	p->swap_lockmap = (unsigned char *) get_free_page(GFP_USER);
+	if (!p->swap_lockmap) {
+		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)) {
+		printk("Unable to find swap-space signature\n");
+		error = -EINVAL;
+		goto bad_swap;
+	}
+	memset(p->swap_lockmap+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 (!p->lowest_bit)
+				p->lowest_bit = i;
+			p->highest_bit = i;
+			p->max = i+1;
+			j++;
+		}
+	}
+	if (!j) {
+		printk("Empty swap-file\n");
+		error = -EINVAL;
+		goto bad_swap;
+	}
+	p->swap_map = (unsigned char *) vmalloc(p->max);
+	if (!p->swap_map) {
+		error = -ENOMEM;
+		goto bad_swap;
+	}
+	for (i = 1 ; i < p->max ; i++) {
+		if (test_bit(i,p->swap_lockmap))
+			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;
+	printk("Adding Swap: %dk swap-space\n",j<<(PAGE_SHIFT-10));
+
+	/* insert swap space into swap_list: */
+	prev = -1;
+	for (i = swap_list.head; i >= 0; i = swap_info[i].next) {
+		if (p->prio >= swap_info[i].prio) {
+			break;
+		}
+		prev = i;
+	}
+	p->next = i;
+	if (prev < 0) {
+		swap_list.head = swap_list.next = p - swap_info;
+	} else {
+		swap_info[prev].next = p - swap_info;
+	}
+	return 0;
+bad_swap:
+	if(filp.f_op && filp.f_op->release)
+		filp.f_op->release(filp.f_inode,&filp);
+bad_swap_2:
+	free_page((long) p->swap_lockmap);
+	vfree(p->swap_map);
+	iput(p->swap_file);
+	p->swap_device = 0;
+	p->swap_file = NULL;
+	p->swap_map = NULL;
+	p->swap_lockmap = NULL;
+	p->flags = 0;
+	return error;
+}
+
+void si_swapinfo(struct sysinfo *val)
+{
+	unsigned int i, j;
+
+	val->freeswap = val->totalswap = 0;
+	for (i = 0; i < nr_swapfiles; i++) {
+		if ((swap_info[i].flags & SWP_WRITEOK) != SWP_WRITEOK)
+			continue;
+		for (j = 0; j < swap_info[i].max; ++j)
+			switch (swap_info[i].swap_map[j]) {
+				case 128:
+					continue;
+				case 0:
+					++val->freeswap;
+				default:
+					++val->totalswap;
+			}
+	}
+	val->freeswap <<= PAGE_SHIFT;
+	val->totalswap <<= PAGE_SHIFT;
+	return;
+}
+
diff -u --recursive --new-file v1.3.56/linux/mm/vmscan.c linux/mm/vmscan.c
--- v1.3.56/linux/mm/vmscan.c	Thu Jan  1 02:00:00 1970
+++ linux/mm/vmscan.c	Fri Jan 12 07:07:11 1996
@@ -0,0 +1,427 @@
+/*
+ *  linux/mm/vmscan.c
+ *
+ *  Copyright (C) 1991, 1992, 1993, 1994  Linus Torvalds
+ *
+ *  Swap reorganised 29.12.95, 
+ */
+
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/head.h>
+#include <linux/kernel.h>
+#include <linux/kernel_stat.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/stat.h>
+#include <linux/swap.h>
+#include <linux/fs.h>
+#include <linux/swapctl.h>
+#include <linux/smp_lock.h>
+
+#include <asm/dma.h>
+#include <asm/system.h> /* for cli()/sti() */
+#include <asm/segment.h> /* for memcpy_to/fromfs */
+#include <asm/bitops.h>
+#include <asm/pgtable.h>
+
+/* 
+ * When are we next due for a page scan? 
+ */
+static int next_swap_jiffies = 0;
+
+/* 
+ * How often do we do a pageout scan during normal conditions?
+ * Default is four times a second.
+ */
+int swapout_interval = HZ / 4;
+
+/* 
+ * The wait queue for waking up the pageout daemon:
+ */
+static struct wait_queue * kswapd_wait = NULL;
+
+/* 
+ * We avoid doing a reschedule if the pageout daemon is already awake;
+ */
+static int kswapd_awake = 0;
+
+/*
+ * sysctl-modifiable parameters to control the aggressiveness of the
+ * page-searching within the kswapd page recovery daemon.
+ */
+kswapd_control_t kswapd_ctl = {4, -1, -1, -1, -1};
+
+static void init_swap_timer(void);
+
+/*
+ * The swap-out functions return 1 if they successfully
+ * threw something out, and we got a free page. It returns
+ * zero if it couldn't do anything, and any other value
+ * indicates it decreased rss, but the page was shared.
+ *
+ * NOTE! If it sleeps, it *must* return 1 to make sure we
+ * don't continue with the swap-out. Otherwise we may be
+ * using a process that no longer actually exists (it might
+ * have died while we slept).
+ */
+static inline int try_to_swap_out(struct task_struct * tsk, struct vm_area_struct* vma,
+	unsigned long address, pte_t * page_table, unsigned long limit)
+{
+	pte_t pte;
+	unsigned long entry;
+	unsigned long page;
+	struct page * page_map;
+
+	pte = *page_table;
+	if (!pte_present(pte))
+		return 0;
+	page = pte_page(pte);
+	if (MAP_NR(page) >= MAP_NR(high_memory))
+		return 0;
+	if (page >= limit)
+		return 0;
+
+	page_map = mem_map + MAP_NR(page);
+	if (page_map->reserved)
+		return 0;
+	/* Deal with page aging.  Pages age from being unused; they
+	 * rejuvinate on being accessed.  Only swap old pages (age==0
+	 * is oldest). */
+	if ((pte_dirty(pte) && delete_from_swap_cache(page)) 
+	    || pte_young(pte))  {
+		set_pte(page_table, pte_mkold(pte));
+		touch_page(page_map);
+		return 0;
+	}
+	age_page(page_map);
+	if (page_map->age)
+		return 0;
+	if (pte_dirty(pte)) {
+		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 (page_map->count != 1)
+				return 0;
+			if (!(entry = get_swap_page()))
+				return 0;
+			vma->vm_mm->rss--;
+			set_pte(page_table, __pte(entry));
+			invalidate_page(vma, address);
+			tsk->nswap++;
+			write_swap_page(entry, (char *) page);
+		}
+		free_page(page);
+		return 1;	/* we slept: the process may not exist any more */
+	}
+        if ((entry = find_in_swap_cache(page)))  {
+		if (page_map->count != 1) {
+			set_pte(page_table, pte_mkdirty(pte));
+			printk("Aiee.. duplicated cached swap-cache entry\n");
+			return 0;
+		}
+		vma->vm_mm->rss--;
+		set_pte(page_table, __pte(entry));
+		invalidate_page(vma, address);
+		free_page(page);
+		return 1;
+	} 
+	vma->vm_mm->rss--;
+	pte_clear(page_table);
+	invalidate_page(vma, address);
+	entry = page_unuse(page);
+	free_page(page);
+	return entry;
+}
+
+/*
+ * A new implementation of swap_out().  We do not swap complete processes,
+ * but only a small number of blocks, before we continue with the next
+ * process.  The number of blocks actually swapped is determined on the
+ * number of page faults, that this process actually had in the last time,
+ * so we won't swap heavily used processes all the time ...
+ *
+ * Note: the priority argument is a hint on much CPU to waste with the
+ *       swap block search, not a hint, of how much blocks to swap with
+ *       each process.
+ *
+ * (C) 1993 Kai Petzke, wpp@marie.physik.tu-berlin.de
+ */
+
+static inline int swap_out_pmd(struct task_struct * tsk, struct vm_area_struct * vma,
+	pmd_t *dir, unsigned long address, unsigned long end, unsigned long limit)
+{
+	pte_t * pte;
+	unsigned long pmd_end;
+
+	if (pmd_none(*dir))
+		return 0;
+	if (pmd_bad(*dir)) {
+		printk("swap_out_pmd: bad pmd (%08lx)\n", pmd_val(*dir));
+		pmd_clear(dir);
+		return 0;
+	}
+	
+	pte = pte_offset(dir, address);
+	
+	pmd_end = (address + PMD_SIZE) & PMD_MASK;
+	if (end > pmd_end)
+		end = pmd_end;
+
+	do {
+		int result;
+		tsk->swap_address = address + PAGE_SIZE;
+		result = try_to_swap_out(tsk, vma, address, pte, limit);
+		if (result)
+			return result;
+		address += PAGE_SIZE;
+		pte++;
+	} while (address < end);
+	return 0;
+}
+
+static inline int swap_out_pgd(struct task_struct * tsk, struct vm_area_struct * vma,
+	pgd_t *dir, unsigned long address, unsigned long end, unsigned long limit)
+{
+	pmd_t * pmd;
+	unsigned long pgd_end;
+
+	if (pgd_none(*dir))
+		return 0;
+	if (pgd_bad(*dir)) {
+		printk("swap_out_pgd: bad pgd (%08lx)\n", pgd_val(*dir));
+		pgd_clear(dir);
+		return 0;
+	}
+
+	pmd = pmd_offset(dir, address);
+
+	pgd_end = (address + PGDIR_SIZE) & PGDIR_MASK;	
+	if (end > pgd_end)
+		end = pgd_end;
+	
+	do {
+		int result = swap_out_pmd(tsk, vma, pmd, address, end, limit);
+		if (result)
+			return result;
+		address = (address + PMD_SIZE) & PMD_MASK;
+		pmd++;
+	} while (address < end);
+	return 0;
+}
+
+static int swap_out_vma(struct task_struct * tsk, struct vm_area_struct * vma,
+	pgd_t *pgdir, unsigned long start, unsigned long limit)
+{
+	unsigned long end;
+
+	/* Don't swap out areas like shared memory which have their
+	    own separate swapping mechanism or areas which are locked down */
+	if (vma->vm_flags & (VM_SHM | VM_LOCKED))
+		return 0;
+
+	end = vma->vm_end;
+	while (start < end) {
+		int result = swap_out_pgd(tsk, vma, pgdir, start, end, limit);
+		if (result)
+			return result;
+		start = (start + PGDIR_SIZE) & PGDIR_MASK;
+		pgdir++;
+	}
+	return 0;
+}
+
+static int swap_out_process(struct task_struct * p, unsigned long limit)
+{
+	unsigned long address;
+	struct vm_area_struct* vma;
+
+	/*
+	 * Go through process' page directory.
+	 */
+	address = p->swap_address;
+	p->swap_address = 0;
+
+	/*
+	 * Find the proper vm-area
+	 */
+	vma = find_vma(p, address);
+	if (!vma)
+		return 0;
+	if (address < vma->vm_start)
+		address = vma->vm_start;
+
+	for (;;) {
+		int result = swap_out_vma(p, vma, pgd_offset(p->mm, address), address, limit);
+		if (result)
+			return result;
+		vma = vma->vm_next;
+		if (!vma)
+			break;
+		address = vma->vm_start;
+	}
+	p->swap_address = 0;
+	return 0;
+}
+
+static int swap_out(unsigned int priority, unsigned long limit)
+{
+	static int swap_task;
+	int loop, counter;
+	struct task_struct *p;
+
+	counter = ((PAGEOUT_WEIGHT * nr_tasks) >> 10) >> priority;
+	for(; counter >= 0; counter--) {
+		/*
+		 * Check that swap_task is suitable for swapping.  If not, look for
+		 * the next suitable process.
+		 */
+		loop = 0;
+		while(1) {
+			if (swap_task >= NR_TASKS) {
+				swap_task = 1;
+				if (loop)
+					/* all processes are unswappable or already swapped out */
+					return 0;
+				loop = 1;
+			}
+
+			p = task[swap_task];
+			if (p && p->swappable && p->mm->rss)
+				break;
+
+			swap_task++;
+		}
+
+		/*
+		 * Determine the number of pages to swap from this process.
+		 */
+		if (!p->swap_cnt) {
+ 			/* Normalise the number of pages swapped by
+			   multiplying by (RSS / 1MB) */
+			p->swap_cnt = AGE_CLUSTER_SIZE(p->mm->rss);
+		}
+		if (!--p->swap_cnt)
+			swap_task++;
+		switch (swap_out_process(p, limit)) {
+			case 0:
+				if (p->swap_cnt)
+					swap_task++;
+				break;
+			case 1:
+				return 1;
+			default:
+				break;
+		}
+	}
+	return 0;
+}
+
+/*
+ * We are much more aggressive about trying to swap out than we used
+ * to be.  This works out OK, because we now do proper aging on page
+ * contents. 
+ */
+int try_to_free_page(int priority, unsigned long limit)
+{
+	static int state = 0;
+	int i=6;
+
+	switch (state) {
+		do {
+		case 0:
+			if (shrink_mmap(i, limit))
+				return 1;
+			state = 1;
+		case 1:
+			if (shm_swap(i, limit))
+				return 1;
+			state = 2;
+		default:
+			if (swap_out(i, limit))
+				return 1;
+			state = 0;
+		} while(i--);
+	}
+	return 0;
+}
+
+
+/*
+ * The background pageout daemon.
+ * Started as a kernel thread from the init process.
+ */
+int kswapd(void *unused)
+{
+	int i;
+	
+	current->session = 1;
+	current->pgrp = 1;
+	sprintf(current->comm, "kswapd");
+	current->blocked = ~0UL;
+	
+	/*
+	 *	As a kernel thread we want to tamper with system buffers
+	 *	and other internals and thus be subject to the SMP locking
+	 *	rules. (On a uniprocessor box this does nothing).
+	 */
+	 
+#ifdef __SMP__
+	lock_kernel();
+	syscall_count++;
+#endif
+
+	/* Give kswapd a realtime priority. */
+	current->policy = SCHED_FIFO;
+	current->priority = 32;  /* Fixme --- we need to standardise our
+				    namings for POSIX.4 realtime scheduling
+				    priorities.  */
+
+	printk ("Started kswapd v$Revision: 1.1.2.3 $\n");
+	init_swap_timer();
+	
+	while (1) {
+		kswapd_awake = 0;
+		current->signal = 0;
+		interruptible_sleep_on(&kswapd_wait);
+		kswapd_awake = 1;
+		
+		/* Do the background pageout: */
+		for (i=0; i < kswapd_ctl.maxpages; i++)
+			try_to_free_page(GFP_KERNEL, ~0UL);
+	}
+}
+
+/* 
+ * The swap_tick function gets called on every clock tick.
+ */
+
+void swap_tick(void)
+{
+	if (nr_free_pages < free_pages_low ||
+	    (nr_free_pages < free_pages_high && 
+	     jiffies >= next_swap_jiffies)) {
+		if (!kswapd_awake && kswapd_ctl.maxpages > 0) {
+			wake_up(&kswapd_wait);
+			need_resched = 1;
+			kswapd_awake = 1;
+		}
+		next_swap_jiffies = jiffies + swapout_interval;
+	}
+	timer_active |= (1<<SWAP_TIMER);
+}
+
+
+/* 
+ * Initialise the swap timer
+ */
+
+void init_swap_timer(void)
+{
+	timer_table[SWAP_TIMER].expires = 0;
+	timer_table[SWAP_TIMER].fn = swap_tick;
+	timer_active |= (1<<SWAP_TIMER);
+}
diff -u --recursive --new-file v1.3.56/linux/net/core/dev.c linux/net/core/dev.c
--- v1.3.56/linux/net/core/dev.c	Wed Dec 27 09:12:14 1995
+++ linux/net/core/dev.c	Tue Jan  9 12:26:54 1996
@@ -41,6 +41,7 @@
  *		Rudi Cilibrasi	:	Pass the right thing to set_mac_address()
  *		Dave Miller	:	32bit quantity for the device lock to make it work out
  *					on a Sparc.
+ *		Bjorn Ekwall	:	Added KERNELD hack
  *
  */
 
@@ -73,6 +74,9 @@
 #ifdef CONFIG_NET_ALIAS
 #include <linux/net_alias.h>
 #endif
+#ifdef CONFIG_KERNELD
+#include <linux/kerneld.h>
+#endif
 
 /*
  *	The list of packet types we will receive (as opposed to discard)
@@ -203,6 +207,13 @@
 		if (strcmp(dev->name, name) == 0)
 			return(dev);
 	}
+#ifdef CONFIG_KERNELD
+	if (request_module(name) == 0)
+		for (dev = dev_base; dev != NULL; dev = dev->next) {
+			if (strcmp(dev->name, name) == 0)
+				return(dev);
+		}
+#endif
 	return(NULL);
 }
 
diff -u --recursive --new-file v1.3.56/linux/net/ipv4/ip_input.c linux/net/ipv4/ip_input.c
--- v1.3.56/linux/net/ipv4/ip_input.c	Wed Dec 27 09:12:14 1995
+++ linux/net/ipv4/ip_input.c	Tue Jan  9 11:29:27 1996
@@ -204,6 +204,8 @@
 	int brd=IS_MYADDR;
 	struct options * opt = NULL;
 	int is_frag=0;
+	__u32 daddr;
+
 #ifdef CONFIG_FIREWALL
 	int err;
 #endif	
@@ -326,10 +328,10 @@
 	 *	and don't go via ip_chk_addr. Note: brd is set to IS_MYADDR at
 	 *	function entry.
 	 */
-
+	daddr = iph->daddr;
 	if ( iph->daddr == skb->dev->pa_addr || (brd = ip_chk_addr(iph->daddr)) != 0)
 	{
-	        if (opt && opt->srr) 
+		if (opt && opt->srr) 
 	        {
 			int srrspace, srrptr;
 			__u32 nexthop;
@@ -368,6 +370,7 @@
 					kfree_skb(skb, FREE_WRITE);
 					return -EINVAL;
 				}
+				memcpy(&daddr, &optptr[srrptr-1], 4);
 			}
 			if (srrptr <= srrspace) 
 			{
@@ -485,7 +488,7 @@
 					else
 						break;	/* One pending raw socket left */
 					if(skb1)
-						raw_rcv(raw_sk, skb1, dev, iph->saddr,iph->daddr);
+						raw_rcv(raw_sk, skb1, dev, iph->saddr,daddr);
 					raw_sk=sknext;
 				}
 				while(raw_sk!=NULL);
@@ -540,7 +543,7 @@
 			*	check the protocol handler's return values here...
 			*/
 
-			ipprot->handler(skb2, dev, opt, iph->daddr,
+			ipprot->handler(skb2, dev, opt, daddr,
 				(ntohs(iph->tot_len) - (iph->ihl * 4)),
 				iph->saddr, 0, ipprot);
 		}
@@ -577,7 +580,7 @@
 #endif		
 
 		if(raw_sk!=NULL)	/* Shift to last raw user */
-			raw_rcv(raw_sk, skb, dev, iph->saddr, iph->daddr);
+			raw_rcv(raw_sk, skb, dev, iph->saddr, daddr);
 		else if (!flag)		/* Free and report errors */
 		{
 			if (brd != IS_BROADCAST && brd!=IS_MULTICAST)
diff -u --recursive --new-file v1.3.56/linux/net/ipv4/ip_sockglue.c linux/net/ipv4/ip_sockglue.c
--- v1.3.56/linux/net/ipv4/ip_sockglue.c	Fri Nov 17 08:42:34 1995
+++ linux/net/ipv4/ip_sockglue.c	Tue Jan  9 11:29:27 1996
@@ -462,7 +462,7 @@
 				if (opt->srr) 
 				{
 					unsigned  char * optptr = opt->__data+opt->srr-sizeof(struct  iphdr);
-					memmove(optptr+7, optptr+4, optptr[1]-7);
+					memmove(optptr+7, optptr+3, optptr[1]-7);
 					memcpy(optptr+3, &opt->faddr, 4);
 				}
 				if (opt->rr_needaddr) 
diff -u --recursive --new-file v1.3.56/linux/net/ipv4/route.c linux/net/ipv4/route.c
--- v1.3.56/linux/net/ipv4/route.c	Mon Dec 11 15:42:10 1995
+++ linux/net/ipv4/route.c	Tue Jan  9 12:26:54 1996
@@ -39,6 +39,7 @@
  *					routing caches and better behaviour.
  *		
  *		Olaf Erb	:	irtt wasnt being copied right.
+ *		Bjorn Ekwall	:	Added KERNELD hack for autodialling
  *
  *		This program is free software; you can redistribute it and/or
  *		modify it under the terms of the GNU General Public License
@@ -69,6 +70,9 @@
 #include <net/sock.h>
 #include <net/icmp.h>
 #include <net/netlink.h>
+#ifdef CONFIG_KERNELD
+#include <linux/kerneld.h>
+#endif
 
 /*
  * Forwarding Information Base definitions.
@@ -1561,7 +1565,12 @@
 		ATOMIC_DECR(&rt->rt_refcnt);
 }
 
-struct rtable * ip_rt_route(__u32 daddr, int local)
+#ifdef CONFIG_KERNELD
+static struct rtable * real_ip_rt_route
+#else
+struct rtable * ip_rt_route
+#endif
+	(__u32 daddr, int local)
 {
 	struct rtable * rth;
 
@@ -1580,6 +1589,27 @@
 	}
 	return ip_rt_slow_route (daddr, local);
 }
+
+#ifdef CONFIG_KERNELD
+struct rtable * ip_rt_route(__u32 daddr, int local)
+{
+	struct rtable *rt;
+	char wanted_route[20];
+	long ipaddr = ntohl(daddr);
+
+	if ((rt = real_ip_rt_route(daddr, local)) == NULL) {
+		sprintf(wanted_route, "%d.%d.%d.%d",
+			(int)(ipaddr >> 24) & 0xff, (int)(ipaddr >> 16) & 0xff,
+			(int)(ipaddr >> 8) & 0xff, (int)ipaddr & 0xff);
+		/* Not good while forwarding a packet... see ipc/msg.c */
+		kerneld_route(wanted_route); /* perhaps a ppp-connection? */
+		/* Try again */
+		rt = real_ip_rt_route(daddr, local);
+	}
+
+	return rt;
+}
+#endif
 
 
 /*
diff -u --recursive --new-file v1.3.56/linux/net/ipv4/udp.c linux/net/ipv4/udp.c
--- v1.3.56/linux/net/ipv4/udp.c	Sat Nov 25 19:04:58 1995
+++ linux/net/ipv4/udp.c	Thu Jan 11 19:05:44 1996
@@ -216,10 +216,10 @@
 struct udpfakehdr 
 {
 	struct udphdr uh;
-	int daddr;
-	int other;
+	__u32 daddr;
+	__u32 other;
 	const char *from;
-	int wcheck;
+	__u32 wcheck;
 };
 
 /*
