## Automatically generated incremental diff
## From:   linux-2.4.4-pre5
## To:     linux-2.4.4-pre6
## Robot:  $Id: make-incremental-diff,v 1.8 2001/12/04 06:05:04 hpa Exp $

diff -urN linux-2.4.4-pre5/CREDITS linux/CREDITS
--- linux-2.4.4-pre5/CREDITS	Mon Dec  3 22:36:13 2001
+++ linux/CREDITS	Mon Dec  3 22:36:23 2001
@@ -1121,8 +1121,8 @@
 E: rth@twiddle.net
 E: rth@cygnus.com
 D: Alpha hacker, kernel and userland
-S: 50 E. Middlefield #10
-S: Mountain View, California 94043
+S: 1668 California St.
+S: Mountain View, California 94041
 S: USA
 
 N: Benjamin Herrenschmidt
@@ -1483,8 +1483,9 @@
 D: 6pack driver for AX.25
 
 N: Harald Koerfgen
-E: harald@unix-ag.org
-D: DECstation port
+E: hkoerfg@web.de
+D: Linux/MIPS kernel hacks and fixes,
+D: DECstation port, Sharp Mobilon port
 S: D-50931 Koeln
 S: Germany
 
diff -urN linux-2.4.4-pre5/Documentation/Configure.help linux/Documentation/Configure.help
--- linux-2.4.4-pre5/Documentation/Configure.help	Mon Dec  3 22:36:13 2001
+++ linux/Documentation/Configure.help	Mon Dec  3 22:36:24 2001
@@ -1617,6 +1617,27 @@
   This enables support for the VR5000-based NEC DDB Vrc-5074
   evaluation board.
 
+Support for NEC DDB Vrc-5476
+CONFIG_DDB5476
+  This enables support for the R5432-based NEC DDB Vrc-5476
+  evaluation board.
+
+  Features : kernel debugging, serial terminal, NFS root fs, on-board
+  ether port (with a patch to tulip driver), IDE controller, PS2 keyboard
+  PS2 mouse, etc.
+
+  TODO : USB, Compact-PCI interface.
+
+Support for MIPS Atlas board
+CONFIG_MIPS_ATLAS
+  This enables support for the QED R5231-based MIPS Atlas evaluation
+  board.
+
+Support for MIPS Malta board
+CONFIG_MIPS_MALTA
+  This enables support for the VR5000-based MIPS Malta evaluation
+  board.
+
 Support for Mips Magnum 4000
 CONFIG_MIPS_MAGNUM_4000
   This is a machine with a R4000 100 MHz CPU. To compile a Linux
@@ -1660,6 +1681,12 @@
   4000, Acer PICA, Olivetti M700-10 and a few other identical OEM
   systems.
 
+Kernel floating-point instruction emulation
+CONFIG_MIPS_FPU_EMULATOR
+  This option enables the MIPS software floatingpoint support.  Due to the
+  way floatingpoint works you should always enable this option unless
+  you exactly know what you're doing.
+
 PCMCIA SCSI adapter support
 CONFIG_SCSI_PCMCIA
   Say Y here if you intend to attach a PCMCIA or CardBus card to your
@@ -1756,6 +1783,10 @@
   certain types of data to get through the socket. Linux Socket
   Filtering works on all socket types except TCP for now. See the text
   file Documentation/networking/filter.txt for more information.
+
+  You need to say Y here if you want to use PPP packet filtering
+  (see the CONFIG_PPP_FILTER option below).
+
   If unsure, say N.
 
 Network packet filtering
@@ -2415,7 +2446,7 @@
   of those special I/O ports.
 
 SGI PROM Console Support
-CONFIG_SGI_PROM_CONSOLE
+CONFIG_ARC_CONSOLE
   Say Y here if you want to use the PROMs for console I/O.
 
 SGI Zilog85C30 serial support
@@ -7087,6 +7118,17 @@
 
   If unsure, say N.
 
+PPP filtering (EXPERIMENTAL)
+CONFIG_PPP_FILTER
+  Say Y here if you want to be able to filter the packets passing over
+  PPP interfaces.  This allows you to control which packets count as
+  activity (i.e. which packets will reset the idle timer or bring up
+  a demand-dialled link) and which packets are to be dropped entirely.
+  You need to say Y here if you wish to use the pass-filter and
+  active-filter options to pppd.
+
+  If unsure, say N.
+
 PPP support for async serial ports
 CONFIG_PPP_ASYNC
   Say Y (or M) here if you want to be able to use PPP over standard
@@ -12926,8 +12968,8 @@
   how to pass options to the kernel at boot time.) The syntax of the
   "lp" command line option can be found in drivers/char/lp.c.
 
-  If you have more than 3 printers, you need to increase the LP_NO
-  variable in lp.c.
+  If you have more than 8 printers, you need to increase the LP_NO
+  macro in lp.c and the PARPORT_MAX macro in parport.h.
 
 Support for console on line printer
 CONFIG_LP_CONSOLE
@@ -15515,10 +15557,10 @@
   Enable support for the AVM T1 T1B card.
   Note: This is a PRI card and handle 30 B-channels.
 
-AVM C4 support
+AVM C4/C2 support
 CONFIG_ISDN_DRV_AVMB1_C4
-  Enable support for the AVM C4 PCI card.
-  This card handle 4 BRI ISDN lines (8 channels).
+  Enable support for the AVM C4/C2 PCI cards.
+  These cards handle 4/2 BRI ISDN lines (8/4 channels).
 
 Verbose reason code reporting (kernel size +=7K)
 CONFIG_ISDN_DRV_AVMB1_VERBOSE_REASON
diff -urN linux-2.4.4-pre5/Documentation/filesystems/ext2.txt linux/Documentation/filesystems/ext2.txt
--- linux-2.4.4-pre5/Documentation/filesystems/ext2.txt	Sun Oct  1 20:21:20 2000
+++ linux/Documentation/filesystems/ext2.txt	Mon Dec  3 22:36:24 2001
@@ -4,7 +4,7 @@
 
 ext2 was originally released in January 1993.  Written by R\'emy Card,
 Theodore Ts'o and Stephen Tweedie, it was a major rewrite of the
-Extended Filesystem.  It is currently (February 1999) the predominant
+Extended Filesystem.  It is currently still (April 2001) the predominant
 filesystem in use by Linux.  There are also implementations available
 for NetBSD, FreeBSD, the GNU HURD, Windows 95/98/NT, OS/2 and RISC OS.
 
@@ -17,11 +17,11 @@
 bsddf			(*)	Makes `df' act like BSD.
 minixdf				Makes `df' act like Minix.
 
-check=none, nocheck		Perform no checks upon the filesystem.
-check=normal		(*)	Perform normal checks on the filesystem.
-check=strict			Perform extra checks on the filesystem.
+check=none, nocheck	(*)	Don't do extra checking of bitmaps on mount
+				(check=normal and check=strict options removed)
 
-debug				For developers only.
+debug				Extra debugging information is sent to the
+				kernel syslog.  Useful for developers.
 
 errors=continue		(*)	Keep going on a filesystem error.
 errors=remount-ro		Remount the filesystem read-only on an error.
@@ -30,8 +30,8 @@
 grpid, bsdgroups		Give objects the same group ID as their parent.
 nogrpid, sysvgroups	(*)	New objects have the group ID of their creator.
 
-resuid=n			The user which may use the reserved blocks.
-resgid=n			The group which may use the reserved blocks. 
+resuid=n			The user ID which may use the reserved blocks.
+resgid=n			The group ID which may use the reserved blocks. 
 
 sb=n				Use alternate superblock at this location.
 
@@ -53,46 +53,53 @@
 ------
 
 The space in the device or file is split up into blocks.  These are
-a fixed size, of 1024, 2048 or 4096 bytes, which is decided when the
-filesystem is created.  Smaller blocks mean less wasted space per file,
-but require slightly more accounting overhead.
+a fixed size, of 1024, 2048 or 4096 bytes (8192 bytes on Alpha systems),
+which is decided when the filesystem is created.  Smaller blocks mean
+less wasted space per file, but require slightly more accounting overhead,
+and also impose other limits on the size of files and the filesystem.
+
+Block Groups
+------------
 
 Blocks are clustered into block groups in order to reduce fragmentation
-and minimise the amount of head seeking when reading a large amount of
-consecutive data.  Each block group has a descriptor and the array of
-descriptors is stored immediately after the superblock.  Two blocks at
-the start of each group are reserved for the block usage bitmap and
-the inode usage bitmap which show which blocks and inodes are used.
-Since each bitmap fits in a block, this means that the maximum size of
-a block group is 8 times the size of a block.
-
-The first (non-reserved) blocks in the block group are designated as
-the inode table for the block and the remainder are the data blocks.
-The block allocation algorithm attempts to allocate data blocks in the
-same block group as the inode which contains them.
+and minimise the amount of head seeking when reading a large amount
+of consecutive data.  Information about each block group is kept in a
+descriptor table stored in the block(s) immediately after the superblock.
+Two blocks near the start of each group are reserved for the block usage
+bitmap and the inode usage bitmap which show which blocks and inodes
+are in use.  Since each bitmap is limited to a single block, this means
+that the maximum size of a block group is 8 times the size of a block.
+
+The block(s) following the bitmaps in each block group are designated
+as the inode table for that block group and the remainder are the data
+blocks.  The block allocation algorithm attempts to allocate data blocks
+in the same block group as the inode which contains them.
 
 The Superblock
 --------------
 
 The superblock contains all the information about the configuration of
-the filing system.  It is stored in block 1 of the filesystem (numbering
-from 0) and it is essential to mounting it.  Since it is so important,
-backup copies of the superblock are stored in block groups throughout
-the filesystem.  The first revision of ext2 stores a copy at the start
-of every block group.  Later revisions can store a copy in only some
-block groups to reduce the amount of redundancy on large filesystems.
-The groups chosen are 0, 1 and powers of 3, 5 and 7.
-
-The information in the superblock contains fields such as how many
-inodes and blocks are in the filesystem and how many are unused, how
-many inodes and blocks are in a block group, when the filesystem was
-mounted, when it was modified, what version of the filesystem it is
-(see the Revisions section below) and which OS created it.
-
-If the revision of the filesystem is recent enough then there are extra
-fields, such as a volume name, a unique identifier, the inode size,
-support for compression, block preallocation and creating fewer backup
-superblocks.
+the filing system.  The primary copy of the superblock is stored at an
+offset of 1024 bytes from the start of the device, and it is essential
+to mounting the filesystem.  Since it is so important, backup copies of
+the superblock are stored in block groups throughout the filesystem.
+The first version of ext2 (revision 0) stores a copy at the start of
+every block group, along with backups of the group descriptor block(s).
+Because this can consume a considerable amount of space for large
+filesystems, later revisions can optionally reduce the number of backup
+copies by only putting backups in specific groups (this is the sparse
+superblock feature).  The groups chosen are 0, 1 and powers of 3, 5 and 7.
+
+The information in the superblock contains fields such as the total
+number of inodes and blocks in the filesystem and how many are free,
+how many inodes and blocks are in each block group, when the filesystem
+was mounted (and if it was cleanly unmounted), when it was modified,
+what version of the filesystem it is (see the Revisions section below)
+and which OS created it.
+
+If the filesystem is revision 1 or higher, then there are extra fields,
+such as a volume name, a unique identification number, the inode size,
+and space for optional filesystem features to store configuration info.
 
 All fields in the superblock (as in all other ext2 structures) are stored
 on the disc in little endian format, so a filesystem is portable between
@@ -101,23 +108,25 @@
 Inodes
 ------
 
-The inode (index node) is the fundamental concept in the ext2 filesystem.
+The inode (index node) is a fundamental concept in the ext2 filesystem.
 Each object in the filesystem is represented by an inode.  The inode
 structure contains pointers to the filesystem blocks which contain the
 data held in the object and all of the metadata about an object except
 its name.  The metadata about an object includes the permissions, owner,
 group, flags, size, number of blocks used, access time, change time,
 modification time, deletion time, number of links, fragments, version
-(for NFS) and ACLs.
+(for NFS) and extended attributes (EAs) and/or Access Control Lists (ACLs).
 
-There are several reserved fields which are currently unused in the inode
-structure and several which are overloaded.  One field is used for the
-directory ACL if the inode is a directory and for the top 32 bits of
-the file size if the inode is a regular file.  The translator field is
-unused under Linux, but is used by the HURD to reference the inode of
-a program which will be used to interpret this object.  The HURD also
-has larger permissions, owner and group fields, so it uses some of the
-other unused by Linux fields to store the extra bits.
+There are some reserved fields which are currently unused in the inode
+structure and several which are overloaded.  One field is reserved for the
+directory ACL if the inode is a directory and alternately for the top 32
+bits of the file size if the inode is a regular file (allowing file sizes
+larger than 2GB).  The translator field is unused under Linux, but is used
+by the HURD to reference the inode of a program which will be used to
+interpret this object.  Most of the remaining reserved fields have been
+used up for both Linux and the HURD for larger owner and group fields,
+The HURD also has a larger mode field so it uses another of the remaining
+fields to store the extra more bits.
 
 There are pointers to the first 12 blocks which contain the file's data
 in the inode.  There is a pointer to an indirect block (which contains
@@ -126,11 +135,12 @@
 trebly-indirect block (which contains pointers to doubly-indirect blocks).
 
 The flags field contains some ext2-specific flags which aren't catered
-for by the standard chmod flags.  These flags can be listed with
-lsattr and changed with the chattr command.  There are flags for secure
-deletion, undeletable, compression, synchronous updates, immutability,
-append-only, dumpable, no-atime, and btree directories.  Not all of
-these are supported yet.
+for by the standard chmod flags.  These flags can be listed with lsattr
+and changed with the chattr command, and allow specific filesystem
+behaviour on a per-file basis.  There are flags for secure deletion,
+undeletable, compression, synchronous updates, immutability, append-only,
+dumpable, no-atime, indexed directories, and data-journaling.  Not all
+of these are supported yet.
 
 Directories
 -----------
@@ -139,10 +149,19 @@
 It is a specially formatted file containing records which associate
 each name with an inode number.  Later revisions of the filesystem also
 encode the type of the object (file, directory, symlink, device, fifo,
-socket) in the directory entry for speed.  The current implementation
-of ext2 uses a linked list in directories; a planned enhancement will
-use btrees instead.  The current implementation also never shrinks
-directories once they have grown to accommodate more files.
+socket) to avoid the need to check the inode itself for this information
+(support for taking advantage of this feature does not yet exist in
+Glibc 2.2).
+
+The inode allocation code tries to assign inodes which are in the same
+block group as the directory in which they are first created.
+
+The current implementation of ext2 uses a singly-linked list to store
+the filenames in the directory; a pending enhancement uses hashing of the
+filenames to allow lookup without the need to scan the entire directory.
+
+The current implementation never removes empty directory blocks once they
+have been allocated to hold more files.
 
 Special files
 -------------
@@ -150,31 +169,23 @@
 Symbolic links are also filesystem objects with inodes.  They deserve
 special mention because the data for them is stored within the inode
 itself if the symlink is less than 60 bytes long.  It uses the fields
-which would normally be used to store the pointers to blocks to store
-the data.  This is a worthwhile optimisation to make as it does not then
-take up a block, and most symlinks are less than 60 characters long.
+which would normally be used to store the pointers to data blocks.
+This is a worthwhile optimisation as it we avoid allocating a full
+block for the symlink, and most symlinks are less than 60 characters long.
 
 Character and block special devices never have data blocks assigned to
 them.  Instead, their device number is stored in the inode, again reusing
-the fields which would be used to point to the blocks.
-
-Revisions
----------
-
-The revisioning mechanism used in ext2 is sophisticated.  The revisioning
-mechanism is not supported by version 0 (EXT2_GOOD_OLD_REV) of ext2 but
-was introduced in version 1.  There are three 32-bit fields, one for
-compatible features, one for read-only compatible features and one for
-incompatible features.
+the fields which would be used to point to the data blocks.
 
 Reserved Space
 --------------
 
 In ext2, there is a mechanism for reserving a certain number of blocks
 for a particular user (normally the super-user).  This is intended to
-allow for the system to continue functioning even if a user fills up
-all the available space.  It also keeps the filesystem from filling up
-entirely which helps combat fragmentation.
+allow for the system to continue functioning even if non-priveleged users
+fill up all the space available to them (this is independent of filesystem
+quotas).  It also keeps the filesystem from filling up entirely which
+helps combat fragmentation.
 
 Filesystem check
 ----------------
@@ -183,9 +194,66 @@
 filesystems.  The superblock of the ext2 filesystem contains several
 fields which indicate whether fsck should actually run (since checking
 the filesystem at boot can take a long time if it is large).  fsck will
-run if the filesystem was not unmounted without errors, if the maximum
-mount count has been exceeded or if the maximum time between checks has
-been exceeded.
+run if the filesystem was not cleanly unmounted, if the maximum mount
+count has been exceeded or if the maximum time between checks has been
+exceeded.
+
+Feature Compatibility
+---------------------
+
+The compatibility feature mechanism used in ext2 is sophisticated.
+It safely allows features to be added to the filesystem, without
+unnecessarily sacrificing compatibility with older versions of the
+filesystem code.  The feature compatibility mechanism is not supported by
+the original revision 0 (EXT2_GOOD_OLD_REV) of ext2, but was introduced in
+revision 1.  There are three 32-bit fields, one for compatible features
+(COMPAT), one for read-only compatible (RO_COMPAT) features and one for
+incompatible (INCOMPAT) features.
+
+These feature flags have specific meanings for the kernel as follows:
+
+A COMPAT flag indicates that a feature is present in the filesystem,
+but the on-disk format is 100% compatible with older on-disk formats, so
+a kernel which didn't know anything about this feature could read/write
+the filesystem without any chance of corrupting the filesystem (or even
+making it inconsistent).  This is essentially just a flag which says
+"this filesystem has a (hidden) feature" that the kernel or e2fsck may
+want to be aware of (more on e2fsck and feature flags later).  The ext3
+HAS_JOURNAL feature is a COMPAT flag because the ext3 journal is simply
+a regular file with data blocks in it so the kernel does not need to
+take any special notice of it if it doesn't understand ext3 journaling.
+
+An RO_COMPAT flag indicates that the on-disk format is 100% compatible
+with older on-disk formats for reading (i.e. the feature does not change
+the visible on-disk format).  However, an old kernel writing to such a
+filesystem would/could corrupt the filesystem, so this is prevented. The
+most common such feature, SPARSE_SUPER, is an RO_COMPAT feature because
+sparse groups allow file data blocks where superblock/group descriptor
+backups used to live, and ext2_free_blocks() refuses to free these blocks,
+which would leading to inconsistent bitmaps.  An old kernel would also
+get an error if it tried to free a series of blocks which crossed a group
+boundary, but this is a legitimate layout in a SPARSE_SUPER filesystem.
+
+An INCOMPAT flag indicates the on-disk format has changed in some
+way that makes it unreadable by older kernels, or would otherwise
+cause a problem if an old kernel tried to mount it.  FILETYPE is an
+INCOMPAT flag because older kernels would think a filename was longer
+than 256 characters, which would lead to corrupt directory listings.
+The COMPRESSION flag is an obvious INCOMPAT flag - if the kernel
+doesn't understand compression, you would just get garbage back from
+read() instead of it automatically decompressing your data.  The ext3
+RECOVER flag is needed to prevent a kernel which does not understand the
+ext3 journal from mounting the filesystem without replaying the journal.
+
+For e2fsck, it needs to be more strict with the handling of these
+flags than the kernel.  If it doesn't understand ANY of the COMPAT,
+RO_COMPAT, or INCOMPAT flags it will refuse to check the filesystem,
+because it has no way of verifying whether a given feature is valid
+or not.  Allowing e2fsck to succeed on a filesystem with an unknown
+feature is a false sense of security for the user.  Refusing to check
+a filesystem with unknown features is a good incentive for the user to
+update to the latest e2fsck.  This also means that anyone adding feature
+flags to ext2 also needs to update e2fsck to verify these features.
 
 Metadata
 --------
@@ -196,29 +264,98 @@
 respective fsck programs.
 
 If you're exceptionally paranoid, there are 3 ways of making metadata
-writes synchronous:
+writes synchronous on ext2:
 
-per-file if you have the source: use the O_SYNC argument to open()
-per-file if you don't have the source: use chattr +S
-per-filesystem: mount -o sync
+per-file if you have the program source: use the O_SYNC flag to open()
+per-file if you don't have the source: use "chattr +S" on the file
+per-filesystem: add the "sync" option to mount (or in /etc/fstab)
 
 the first and last are not ext2 specific but do force the metadata to
-be written synchronously.
+be written synchronously.  See also Journaling below.
+
+Limitations
+-----------
+
+There are various limits imposed by the on-disk layout of ext2.  Other
+limits are imposed by the current implementation of the kernel code.
+Many of the limits are determined at the time the filesystem is first
+created, and depend upon the block size chosen.  The ratio of inodes to
+data blocks is fixed at filesystem creation time, so the only way to
+increase the number of inodes is to increase the size of the filesystem.
+No tools currently exist which can change the ratio of inodes to blocks.
+
+Most of these limits could be overcome with slight changes in the on-disk
+format and using a compatibility flag to signal the format change (at
+the expense of some compatibility).
+
+Filesystem block size:     1kB        2kB        4kB        8kB
+
+File size limit:          16GB      256GB     2048GB     2048GB
+Filesystem size limit:  2047GB     8192GB    16384GB    32768GB
+
+There is a 2.4 kernel limit of 2048GB for a single block device, so no
+filesystem larger than that can be created at this time.  There is also
+an upper limit on the block size imposed by the page size of the kernel,
+so 8kB blocks are only allowed on Alpha systems (and other architectures
+which support larger pages).
+
+There is a "soft" upper limit of about 10-15k files in a single directory
+with the current linear linked-list directory implementation.  This limit
+stems from performance problems when creating and deleting (and also
+finding) files in such large directories.  Using a hashed directory index
+(under development) allows 100k-1M+ files in a single directory without
+performance problems (although RAM size becomes an issue at this point).
+
+The (meaningless) absolute upper limit of files in a single directory
+(imposed by the file size, the realistic limit is obviously much less)
+is over 130 trillion files.  It would be higher except there are not
+enough 4-character names to make up unique directory entries, so they
+have to be 8 character filenames, even then we are fairly close to
+running out of unique filenames.
+
+Journaling
+----------
+
+A journaling extension to the ext2 code has been developed by Stephen
+Tweedie.  It avoids the risks of metadata corruption and the need to
+wait for e2fsck to complete after a crash, without requiring a change
+to the on-disk ext2 layout.  In a nutshell, the journal is a regular
+file which stores whole metadata (and optionally data) blocks that have
+been modified, prior to writing them into the filesystem.  This means
+it is possible to add a journal to an existing ext2 filesystem without
+the need for data conversion.
+
+When changes to the filesystem (e.g. a file is renamed) they are stored in
+a transaction in the journal and can either be complete or incomplete at
+the time of a crash.  If a transaction is complete at the time of a crash
+(or in the normal case where the system does not crash), then any blocks
+in that transaction are guaranteed to represent a valid filesystem state,
+and are copied into the filesystem.  If a transaction is incomplete at
+the time of the crash, then there is no guarantee of consistency for
+the blocks in that transaction so they are discarded (which means any
+filesystem changes they represent are also lost).
+
+The ext3 code is currently (Apr 2001) available for 2.2 kernels only,
+and not yet available for 2.4 kernels.
 
 References
 ==========
 
 The kernel source	file:/usr/src/linux/fs/ext2/
-Design & Implementation	http://khg.redhat.com/HyperNews/get/fs/ext2intro.html
-Compression		http://debs.fuller.edu/e2compr/
-ACL support		ftp://tsx-11.mit.edu/pub/linux/ALPHA/ext2fs
-updated ACL work	http://aerobee.informatik.uni-bremen.de/acl_eng.html
-e2fsprogs 		ftp://tsx-11.mit.edu/pub/linux/packages/ext2fs
+e2fsprogs (e2fsck)	http://e2fsprogs.sourceforge.net/
+Design & Implementation	http://e2fsprogs.sourceforge.net/ext2intro.html
+Journaling (ext3)	ftp://ftp.uk.linux.org/pub/linux/sct/fs/jfs/
+Hashed Directories	http://kernelnewbies.org/~phillips/htree/
+Filesystem Resizing	http://ext2resize.sourceforge.net/
+Extended Attributes &
+Access Control Lists	http://acl.bestbits.at/
+Compression (*)		http://www.netspace.net.au/~reiter/e2compr/
 
 Implementations for:
+Windows 95/98/NT/2000	http://uranus.it.swin.edu.au/~jn/linux/Explore2fs.htm
+Windows 95 (*)		http://www.yipton.demon.co.uk/content.html#FSDEXT2
+DOS client (*)		ftp://metalab.unc.edu/pub/Linux/system/filesystems/ext2/
 OS/2			http://perso.wanadoo.fr/matthieu.willm/ext2-os2/
-Windows 95		http://www.yipton.demon.co.uk/
-Windows NT		http://www.cyco.nl/~andreys/ext2fsnt/
-			http://uranus.it.swin.edu.au/~jn/linux/Explore2fs.htm
-DOS client		ftp://metalab.unc.edu/pub/Linux/system/filesystems/ext2/
 RISC OS client		ftp://ftp.barnet.ac.uk/pub/acorn/armlinux/iscafs/
+
+(*) no longer actively developed/supported (as of Apr 2001)
diff -urN linux-2.4.4-pre5/Documentation/mips/GT64120.README linux/Documentation/mips/GT64120.README
--- linux-2.4.4-pre5/Documentation/mips/GT64120.README	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/mips/GT64120.README	Mon Dec  3 22:36:24 2001
@@ -0,0 +1,65 @@
+README for arch/mips/gt64120 directory and subdirectories
+
+Jun Sun, jsun@mvista.com or jsun@junsun.net
+01/27, 2001
+
+MOTIVATION
+----------
+
+Many MIPS boards share the same system controller (or CPU companian chip),
+such as GT-64120.  It is highly desirable to let these boards share
+the same controller code instead of duplicating them.
+
+This directory is meant to hold all MIPS boards that use GT-64120 or GT-64120A.
+
+
+HOW TO ADD A BOARD
+------------------
+ 
+. Create a subdirectory include/asm/gt64120/<board>.  
+
+. Create a file called gt64120_dep.h under that directory.
+
+. Modify include/asm/gt64120/gt64120.h file to include the new gt64120_dep.h
+  based on config options.  The board-dep section is at the end of 
+  include/asm/gt64120/gt64120.h file. There you can find all required
+  definitions include/asm/gt64120/<board>/gt64120_dep.h file must supply.
+
+. Create a subdirectory arch/mips/gt64120/<board> directory to hold
+  board specific routines.
+
+. The GT-64120 common code is supplied under arch/mips/gt64120/common directory.
+  It includes:
+	1) arch/mips/gt64120/pci.c -
+		common PCI routine, include the top-level pcibios_init()
+	2) arch/mips/gt64120/irq.c -
+		common IRQ routine, include the top-level do_IRQ() 
+	   [This part really belongs to arch/mips/kernel. jsun]
+ 	3) arch/mips/gt64120/gt_irq.c -
+		common IRQ routines for GT-64120 chip.  Currently it only handles
+	 	the timer interrupt.
+
+. Board-specific routines are supplied under arch/mips/gt64120/<board> dir.
+	1) arch/mips/gt64120/<board>/pci.c - it provides bus fixup routine
+	2) arch/mips/gt64120/<board>/irq.c - it provides enable/disable irqs
+		and board irq setup routine (irq_setup)
+	3) arch/mips/gt64120/<board>/int-handler.S -
+		The first-level interrupt dispatching routine.
+	4) a bunch of other "normal" stuff (setup, prom, dbg_io, reset, etc)
+
+. Follow other "normal" procedure to modify configuration files, etc.
+
+
+TO-DO LIST
+----------
+
+. Expand arch/mips/gt64120/gt_irq.c to handle all GT-64120 interrupts.
+  We probably need to introduce GT_IRQ_BASE  in board-dep header file,
+  which is used the starting irq_nr for all GT irqs.
+
+  A function, gt64120_handle_irq(), will be added so that the first-level
+  irq dispatcher will call this function if it detects an interrupt
+  from GT-64120.
+
+. More support for GT-64120 PCI features (2nd PCI bus, perhaps)
+
diff -urN linux-2.4.4-pre5/Documentation/mips/time.README linux/Documentation/mips/time.README
--- linux-2.4.4-pre5/Documentation/mips/time.README	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/mips/time.README	Mon Dec  3 22:36:24 2001
@@ -0,0 +1,161 @@
+README for MIPS time services
+
+Jun Sun
+jsun@mvista.com or jsun@junsun.net
+
+
+ABOUT
+-----
+This file describes the new arch/mips/kernel/time.c, related files and the 
+services they provide. 
+
+If you are short in patience and just want to know how to use time.c for a 
+new board or convert an existing board, go to the last section.
+
+
+FILES, COMPATABILITY AND CONFIGS
+---------------------------------
+
+The old arch/mips/kernel/time.c is renamed to old-time.c.
+
+A new time.c is put there, together with include/asm-mips/time.h.
+
+Two configs variables are introduced, CONFIG_OLD_TIME_C and CONFIG_NEW_TIME_C.
+So we allow boards using 
+
+	1) old time.c (CONFIG_OLD_TIME_C)
+	2) new time.c (CONFIG_NEW_TIME_C)
+	3) neither (their own private time.c)
+
+However, it is expected every board will move to the new time.c in the near
+future.
+
+
+WHAT THE NEW CODE PROVIDES?
+--------------------------- 
+
+The new time code provide the following services:
+
+  a) Implements functions required by Linux common code:
+	time_init
+	do_gettimeofday
+	do_settimeofday
+
+  b) provides an abstraction of RTC and null RTC implementation as default.
+	extern unsigned long (*rtc_get_time)(void);
+	extern int (*rtc_set_time)(unsigned long);
+
+  c) a set of gettimeoffset functions for different CPUs and different
+     needs.
+
+  d) high-level and low-level timer interrupt routines where the timer 
+     interrupt source  may or may not be the CPU timer.  The high-level 
+     routine is dispatched through do_IRQ() while the low-level is 
+     dispatched in assemably code (usually int-handler.S)
+
+
+WHAT THE NEW CODE REQUIRES?
+---------------------------
+
+For the new code to work properly, each board implementation needs to supply
+the following functions or values:
+
+  a) board_time_init - a function pointer.  Invoked at the beginnig of
+     time_init().  It is optional.
+	1. (optional) set up RTC routines
+	2. (optional) calibrate and set the mips_counter_frequency
+
+  b) board_timer_setup - a function pointer.  Invoked at the end of time_init()
+	1. (optional) over-ride any decisions made in time_init()
+	2. set up the irqaction for timer interrupt.
+	3. enable the timer interrupt
+
+  c) (optional) board-specific RTC routines.
+
+  d) (optional) mips_counter_frequency - It must be definied if the board
+     is using CPU counter for timer interrupt or it is using fixed rate
+     gettimeoffset().
+
+
+PORTING GUIDE
+-------------
+
+Step 1: decide how you like to implement the time services.
+
+  a) does this board have a RTC?  If yes, implement the two RTC funcs.
+
+  b) does the CPU have counter/compare registers? 
+
+     If the answer is no, you need a timer to provide the timer interrupt
+     at 100 HZ speed.
+
+     You cannot use the fast gettimeoffset functions, i.e.,
+
+	unsigned long fixed_rate_gettimeoffset(void);
+	unsigned long calibrate_div32_gettimeoffset(void);
+	unsigned long calibrate_div64_gettimeoffset(void);
+
+    You can use null_gettimeoffset() will gives the same time resolution as
+    jiffy.  Or you can implement your own gettimeoffset (probably based on 
+    some ad hoc hardware on your machine.)
+
+  c) The following sub steps assume your CPU has counter register.
+     Do you plan to use the CPU counter register as the timer interrupt
+     or use an exnternal timer?
+
+     In order to CPU counter register as the timer interrupt source, you must
+     know the counter speed (mips_counter_frequency).  It is usually the
+     same as the CPU speed (Or it is ALWAYS the same?)
+
+  d) decide on whether you want to use high-level or low-level timer
+     interrupt routines.  The low-level one is presumably faster, but should
+     not make too mcuh difference.
+
+
+Step 2:  the machine setup() function
+
+  If you supply board_time_init(), set the function poointer.
+
+  Set the function pointer board_timer_setup() (mandatory)
+
+
+Step 3: implement rtc routines, board_time_init() and board_timer_setup()
+  if needed.
+
+  board_time_init() - 
+  	a) (optional) set up RTC routines, 
+        b) (optional) calibrate and set the mips_counter_frequency
+ 	    (only needed if you intended to use fixed_rate_gettimeoffset
+ 	     or use cpu counter as timer interrupt source)
+
+  board_timer_setup() - 
+ 	a) (optional) over-write any choices made above by time_init().
+ 	b) machine specific code should setup the timer irqaction.
+ 	c) enable the timer interrupt
+
+
+  If the RTC chip is a common chip, I suggest the routines are put under
+  arch/mips/libs.  For example, for DS1386 chip, one would create
+  rtc-ds1386.c under arch/mips/lib directory.  Add the following line to
+  the arch/mips/lib/Makefile:
+
+	obj-$(CONFIG_DDB5476) += rtc-ds1386.o
+
+Step 4: if you are using low-level timer interrupt, change your interrupt
+  dispathcing code to check for timer interrupt and jump to 
+  ll_timer_interrupt() directly  if one is detected.
+
+Step 5: Modify arch/mips/config.in and add CONFIG_NEW_TIME_C to your machine.
+  Modify the appropriate defconfig if applicable.
+
+Final notes: 
+
+For some tricky cases, you may need to add your own wrapper functions 
+for some of the functions in time.c.  
+
+For example, you may define your own timer interrupt routine, which does
+its own processing and in turn calls timer_interrupt().
+
+You can also over-ride any of the built-in functions (gettimeoffset,
+RTC routines and/or timer interrupt routine).
+
diff -urN linux-2.4.4-pre5/Makefile linux/Makefile
--- linux-2.4.4-pre5/Makefile	Mon Dec  3 22:36:13 2001
+++ linux/Makefile	Mon Dec  3 22:36:24 2001
@@ -1,7 +1,7 @@
 VERSION = 2
 PATCHLEVEL = 4
 SUBLEVEL = 4
-EXTRAVERSION =-pre5
+EXTRAVERSION =-pre6
 
 KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
 
diff -urN linux-2.4.4-pre5/arch/alpha/kernel/alpha_ksyms.c linux/arch/alpha/kernel/alpha_ksyms.c
--- linux-2.4.4-pre5/arch/alpha/kernel/alpha_ksyms.c	Mon Dec  3 22:36:13 2001
+++ linux/arch/alpha/kernel/alpha_ksyms.c	Mon Dec  3 22:36:24 2001
@@ -173,9 +173,6 @@
 EXPORT_SYMBOL(down_interruptible);
 EXPORT_SYMBOL(down_trylock);
 EXPORT_SYMBOL(up);
-EXPORT_SYMBOL(__down_read_failed);
-EXPORT_SYMBOL(__down_write_failed);
-EXPORT_SYMBOL(__rwsem_wake);
 EXPORT_SYMBOL(down_read);
 EXPORT_SYMBOL(down_write);
 EXPORT_SYMBOL(up_read);
diff -urN linux-2.4.4-pre5/arch/i386/config.in linux/arch/i386/config.in
--- linux-2.4.4-pre5/arch/i386/config.in	Mon Dec  3 22:36:13 2001
+++ linux/arch/i386/config.in	Mon Dec  3 22:36:24 2001
@@ -9,8 +9,6 @@
 define_bool CONFIG_SBUS n
 
 define_bool CONFIG_UID16 y
-define_bool CONFIG_RWSEM_GENERIC_SPINLOCK n
-define_bool CONFIG_RWSEM_XCHGADD_ALGORITHM y
 
 mainmenu_option next_comment
 comment 'Code maturity level options'
@@ -52,6 +50,8 @@
    define_bool CONFIG_X86_CMPXCHG n
    define_bool CONFIG_X86_XADD n
    define_int  CONFIG_X86_L1_CACHE_SHIFT 4
+   define_bool CONFIG_RWSEM_GENERIC_SPINLOCK y
+   define_bool CONFIG_RWSEM_XCHGADD_ALGORITHM n
 else
    define_bool CONFIG_X86_WP_WORKS_OK y
    define_bool CONFIG_X86_INVLPG y
@@ -59,6 +59,8 @@
    define_bool CONFIG_X86_XADD y
    define_bool CONFIG_X86_BSWAP y
    define_bool CONFIG_X86_POPAD_OK y
+   define_bool CONFIG_RWSEM_GENERIC_SPINLOCK n
+   define_bool CONFIG_RWSEM_XCHGADD_ALGORITHM y
 fi
 if [ "$CONFIG_M486" = "y" ]; then
    define_int  CONFIG_X86_L1_CACHE_SHIFT 4
diff -urN linux-2.4.4-pre5/arch/i386/kernel/head.S linux/arch/i386/kernel/head.S
--- linux-2.4.4-pre5/arch/i386/kernel/head.S	Sun Mar 25 17:38:31 2001
+++ linux/arch/i386/kernel/head.S	Mon Dec  3 22:36:24 2001
@@ -169,9 +169,7 @@
 	rep
 	movsl
 1:
-#ifdef CONFIG_SMP
 checkCPUtype:
-#endif
 
 	movl $-1,X86_CPUID		#  -1 for no CPUID initially
 
@@ -244,9 +242,7 @@
 	orl $2,%eax		# set MP
 2:	movl %eax,%cr0
 	call check_x87
-#ifdef CONFIG_SMP
 	incb ready
-#endif
 	lgdt gdt_descr
 	lidt idt_descr
 	ljmp $(__KERNEL_CS),$1f
@@ -278,9 +274,7 @@
 	jmp L6			# main should never return here, but
 				# just in case, we know what happens.
 
-#ifdef CONFIG_SMP
 ready:	.byte 0
-#endif
 
 /*
  * We depend on ET to be correct. This checks for 287/387.
diff -urN linux-2.4.4-pre5/arch/i386/kernel/i386_ksyms.c linux/arch/i386/kernel/i386_ksyms.c
--- linux-2.4.4-pre5/arch/i386/kernel/i386_ksyms.c	Mon Dec  3 22:36:13 2001
+++ linux/arch/i386/kernel/i386_ksyms.c	Mon Dec  3 22:36:24 2001
@@ -80,9 +80,11 @@
 EXPORT_SYMBOL_NOVERS(__down_failed_interruptible);
 EXPORT_SYMBOL_NOVERS(__down_failed_trylock);
 EXPORT_SYMBOL_NOVERS(__up_wakeup);
+#ifdef CONFIG_RWSEM_XCHGADD_ALGORITHM
 EXPORT_SYMBOL_NOVERS(__rwsem_down_write_failed);
 EXPORT_SYMBOL_NOVERS(__rwsem_down_read_failed);
 EXPORT_SYMBOL_NOVERS(__rwsem_wake);
+#endif
 /* Networking helper routines. */
 EXPORT_SYMBOL(csum_partial_copy_generic);
 /* Delay loops */
diff -urN linux-2.4.4-pre5/arch/i386/kernel/pci-pc.c linux/arch/i386/kernel/pci-pc.c
--- linux-2.4.4-pre5/arch/i386/kernel/pci-pc.c	Mon Dec  3 22:36:13 2001
+++ linux/arch/i386/kernel/pci-pc.c	Mon Dec  3 22:36:24 2001
@@ -960,34 +960,6 @@
 	d->irq = 9;
 }
 
-static void __init pci_fixup_vt8363(struct pci_dev *d)
-{
-	/*
-	 * The VIA bridge will corrupt disks without these settings.
-	 */
-	u8 tmp;
-	pci_read_config_byte(d, 0x54, &tmp);
-	if(tmp & (1<<2)) {
-		printk("PCI: Bus master Pipeline request disabled\n");
-		pci_write_config_byte(d, 0x54, tmp & ~(1<<2));
-	}
-	pci_read_config_byte(d, 0x70, &tmp);
-	if(tmp & (1<<3)) {
-		printk("PCI: Disabled enhanced CPU to PCI writes\n");
-		pci_write_config_byte(d, 0x70, tmp & ~(1<<3));
-	}
-	pci_read_config_byte(d, 0x71, &tmp);
-	if((tmp & (1<<3)) == 0) {
-		printk("PCI: Bursting cornercase bug worked around\n");
-		pci_write_config_byte(d, 0x71, tmp | (1<<3));
-	}
-	pci_read_config_byte(d, 0x76, &tmp);
-	if(tmp & (1<<7)) {
-		printk("PCI: Post Write Fail set to Retry\n");
-		pci_write_config_byte(d, 0x76, tmp & ~(1<<7));
-	}
-}
-
 static void __init pci_fixup_via691(struct pci_dev *d)
 {
 	/*
@@ -1036,7 +1008,6 @@
 	{ PCI_FIXUP_HEADER,	PCI_VENDOR_ID_SI,	PCI_DEVICE_ID_SI_5598,		pci_fixup_latency },
 	{ PCI_FIXUP_HEADER,	PCI_VENDOR_ID_VIA,	PCI_DEVICE_ID_VIA_82C586_3,	pci_fixup_via_acpi },
 	{ PCI_FIXUP_HEADER,	PCI_VENDOR_ID_VIA,	PCI_DEVICE_ID_VIA_82C686_4,	pci_fixup_via_acpi },
- 	{ PCI_FIXUP_HEADER,	PCI_VENDOR_ID_VIA,	PCI_DEVICE_ID_VIA_8363_0,	pci_fixup_vt8363 },
  	{ PCI_FIXUP_HEADER,	PCI_VENDOR_ID_VIA,	PCI_DEVICE_ID_VIA_82C691,	pci_fixup_via691 },
  	{ PCI_FIXUP_HEADER,	PCI_VENDOR_ID_VIA,	PCI_DEVICE_ID_VIA_82C598_1,	pci_fixup_via691_2 },
  	{ PCI_FIXUP_HEADER,	PCI_VENDOR_ID_INTEL,	PCI_DEVICE_ID_INTEL_82371AB_3,	pci_fixup_piix4_acpi },
diff -urN linux-2.4.4-pre5/arch/i386/mm/init.c linux/arch/i386/mm/init.c
--- linux-2.4.4-pre5/arch/i386/mm/init.c	Mon Mar 26 11:01:56 2001
+++ linux/arch/i386/mm/init.c	Mon Dec  3 22:36:24 2001
@@ -309,14 +309,11 @@
 	 * Zap initial low-memory mappings.
 	 *
 	 * Note that "pgd_clear()" doesn't do it for
-	 * us in this case, because pgd_clear() is a
-	 * no-op in the 2-level case (pmd_clear() is
-	 * the thing that clears the page-tables in
-	 * that case).
+	 * us, because pgd_clear() is a no-op on i386.
 	 */
 	for (i = 0; i < USER_PTRS_PER_PGD; i++)
 #if CONFIG_X86_PAE
-		pgd_clear(swapper_pg_dir+i);
+		set_pgd(swapper_pg_dir+i, __pgd(1 + __pa(empty_zero_page)));
 #else
 		set_pgd(swapper_pg_dir+i, __pgd(0));
 #endif
diff -urN linux-2.4.4-pre5/drivers/char/mem.c linux/drivers/char/mem.c
--- linux-2.4.4-pre5/drivers/char/mem.c	Mon Dec  3 22:36:14 2001
+++ linux/drivers/char/mem.c	Mon Dec  3 22:36:25 2001
@@ -646,9 +646,6 @@
 #if defined(CONFIG_S390_TAPE) && defined(CONFIG_S390_TAPE_CHAR)
 	tapechar_init();
 #endif
-#if defined(CONFIG_S390_TAPE) && defined(CONFIG_S390_TAPE_CHAR)
-	tapechar_init();
-#endif
 #if defined(CONFIG_ADB)
 	adbdev_init();
 #endif
diff -urN linux-2.4.4-pre5/drivers/ide/ide-pci.c linux/drivers/ide/ide-pci.c
--- linux-2.4.4-pre5/drivers/ide/ide-pci.c	Tue Jan  2 16:58:45 2001
+++ linux/drivers/ide/ide-pci.c	Mon Dec  3 22:36:25 2001
@@ -571,6 +571,26 @@
 	 * Can we trust the reported IRQ?
 	 */
 	pciirq = dev->irq;
+	
+	if (dev->class >> 8 == PCI_CLASS_STORAGE_RAID)
+	{
+		/* By rights we want to ignore these, but the Promise Fastrak
+		   people have some strange ideas about proprietary so we have
+		   to act otherwise on those. The supertrak however we need
+		   to skip */
+		if (IDE_PCI_DEVID_EQ(d->devid, DEVID_PDC20265))
+		{
+			printk(KERN_INFO "ide: Found promise 20265 in RAID mode.\n");
+			if(dev->bus->self->vendor == PCI_VENDOR_ID_INTEL &&
+				dev->bus->self->device == PCI_DEVICE_ID_INTEL_I960)
+			{
+				printk(KERN_INFO "ide: Skipping Promise PDC20265 attached to I2O RAID controller.\n");
+				return;
+			}
+		}
+		/* Its attached to something else, just a random bridge. 
+		   Suspect a fastrak and fall through */
+	}
 	if ((dev->class & ~(0xfa)) != ((PCI_CLASS_STORAGE_IDE << 8) | 5)) {
 		printk("%s: not 100%% native mode: will probe irqs later\n", d->name);
 		/*
diff -urN linux-2.4.4-pre5/drivers/ide/pdc202xx.c linux/drivers/ide/pdc202xx.c
--- linux-2.4.4-pre5/drivers/ide/pdc202xx.c	Fri Feb 16 16:02:36 2001
+++ linux/drivers/ide/pdc202xx.c	Mon Dec  3 22:36:25 2001
@@ -224,6 +224,7 @@
 	"QUANTUM FIREBALLlct08 08",
 	"QUANTUM FIREBALLP KA6.4",
 	"QUANTUM FIREBALLP LM20.4",
+	"QUANTUM FIREBALLP KX20.5",
 	"QUANTUM FIREBALLP LM20.5",
 	NULL
 };
diff -urN linux-2.4.4-pre5/drivers/isdn/Config.in linux/drivers/isdn/Config.in
--- linux-2.4.4-pre5/drivers/isdn/Config.in	Mon Dec  3 22:36:14 2001
+++ linux/drivers/isdn/Config.in	Mon Dec  3 22:36:25 2001
@@ -133,7 +133,7 @@
    dep_tristate '  AVM B1/M1/M2 PCMCIA support' CONFIG_ISDN_DRV_AVMB1_B1PCMCIA $CONFIG_ISDN_CAPI
    dep_tristate '  AVM B1/M1/M2 PCMCIA cs module' CONFIG_ISDN_DRV_AVMB1_AVM_CS $CONFIG_ISDN_DRV_AVMB1_B1PCMCIA $CONFIG_PCMCIA
    dep_tristate '  AVM T1/T1-B PCI support' CONFIG_ISDN_DRV_AVMB1_T1PCI $CONFIG_ISDN_CAPI $CONFIG_PCI
-   dep_tristate '  AVM C4 support' CONFIG_ISDN_DRV_AVMB1_C4 $CONFIG_ISDN_CAPI $CONFIG_PCI
+   dep_tristate '  AVM C4/C2 support' CONFIG_ISDN_DRV_AVMB1_C4 $CONFIG_ISDN_CAPI $CONFIG_PCI
 fi
 
 # HYSDN
diff -urN linux-2.4.4-pre5/drivers/isdn/avmb1/avmcard.h linux/drivers/isdn/avmb1/avmcard.h
--- linux-2.4.4-pre5/drivers/isdn/avmb1/avmcard.h	Sat Nov 11 18:58:02 2000
+++ linux/drivers/isdn/avmb1/avmcard.h	Mon Dec  3 22:36:25 2001
@@ -1,9 +1,12 @@
 /*
- * $Id: avmcard.h,v 1.8 2000/10/10 17:44:19 kai Exp $
+ * $Id: avmcard.h,v 1.8.6.1 2001/04/20 02:41:59 keil Exp $
  *
  * Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de)
  *
  * $Log: avmcard.h,v $
+ * Revision 1.8.6.1  2001/04/20 02:41:59  keil
+ * changes from mainstream
+ *
  * Revision 1.8  2000/10/10 17:44:19  kai
  * changes from/for 2.2.18
  *
@@ -83,7 +86,8 @@
 	avm_m2,
 	avm_t1isa,
 	avm_t1pci,
-	avm_c4
+	avm_c4,
+	avm_c2
 };
 
 typedef struct avmcard_dmainfo {
diff -urN linux-2.4.4-pre5/drivers/isdn/avmb1/b1.c linux/drivers/isdn/avmb1/b1.c
--- linux-2.4.4-pre5/drivers/isdn/avmb1/b1.c	Sun Mar 25 18:20:44 2001
+++ linux/drivers/isdn/avmb1/b1.c	Mon Dec  3 22:36:25 2001
@@ -1,11 +1,14 @@
 /*
- * $Id: b1.c,v 1.20.6.3 2001/03/21 08:52:20 kai Exp $
+ * $Id: b1.c,v 1.20.6.4 2001/04/20 02:41:59 keil Exp $
  * 
  * Common module for AVM B1 cards.
  * 
  * (c) Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de)
  * 
  * $Log: b1.c,v $
+ * Revision 1.20.6.4  2001/04/20 02:41:59  keil
+ * changes from mainstream
+ *
  * Revision 1.20.6.3  2001/03/21 08:52:20  kai
  * merge from main branch: fix buffer for revision string (calle)
  *
@@ -126,7 +129,7 @@
 #include "capicmd.h"
 #include "capiutil.h"
 
-static char *revision = "$Revision: 1.20.6.3 $";
+static char *revision = "$Revision: 1.20.6.4 $";
 
 /* ------------------------------------------------------------- */
 
@@ -686,6 +689,7 @@
 	case avm_t1isa: s = "T1 ISA (HEMA)"; break;
 	case avm_t1pci: s = "T1 PCI"; break;
 	case avm_c4: s = "C4"; break;
+	case avm_c2: s = "C2"; break;
 	default: s = "???"; break;
 	}
 	len += sprintf(page+len, "%-16s %s\n", "type", s);
diff -urN linux-2.4.4-pre5/drivers/isdn/avmb1/b1dma.c linux/drivers/isdn/avmb1/b1dma.c
--- linux-2.4.4-pre5/drivers/isdn/avmb1/b1dma.c	Sun Mar 25 18:20:44 2001
+++ linux/drivers/isdn/avmb1/b1dma.c	Mon Dec  3 22:36:25 2001
@@ -1,11 +1,14 @@
 /*
- * $Id: b1dma.c,v 1.11.6.3 2001/03/21 08:52:21 kai Exp $
+ * $Id: b1dma.c,v 1.11.6.4 2001/04/20 02:41:59 keil Exp $
  * 
  * Common module for AVM B1 cards that support dma with AMCC
  * 
  * (c) Copyright 2000 by Carsten Paeth (calle@calle.in-berlin.de)
  * 
  * $Log: b1dma.c,v $
+ * Revision 1.11.6.4  2001/04/20 02:41:59  keil
+ * changes from mainstream
+ *
  * Revision 1.11.6.3  2001/03/21 08:52:21  kai
  * merge from main branch: fix buffer for revision string (calle)
  *
@@ -71,7 +74,7 @@
 #include "capicmd.h"
 #include "capiutil.h"
 
-static char *revision = "$Revision: 1.11.6.3 $";
+static char *revision = "$Revision: 1.11.6.4 $";
 
 /* ------------------------------------------------------------- */
 
@@ -907,6 +910,7 @@
 	case avm_t1isa: s = "T1 ISA (HEMA)"; break;
 	case avm_t1pci: s = "T1 PCI"; break;
 	case avm_c4: s = "C4"; break;
+	case avm_c2: s = "C2"; break;
 	default: s = "???"; break;
 	}
 	len += sprintf(page+len, "%-16s %s\n", "type", s);
diff -urN linux-2.4.4-pre5/drivers/isdn/avmb1/b1pci.c linux/drivers/isdn/avmb1/b1pci.c
--- linux-2.4.4-pre5/drivers/isdn/avmb1/b1pci.c	Mon Dec  3 22:36:14 2001
+++ linux/drivers/isdn/avmb1/b1pci.c	Mon Dec  3 22:36:25 2001
@@ -1,11 +1,14 @@
 /*
- * $Id: b1pci.c,v 1.29.6.2 2001/03/21 08:52:21 kai Exp $
+ * $Id: b1pci.c,v 1.29.6.3 2001/04/20 02:41:59 keil Exp $
  * 
  * Module for AVM B1 PCI-card.
  * 
  * (c) Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de)
  * 
  * $Log: b1pci.c,v $
+ * Revision 1.29.6.3  2001/04/20 02:41:59  keil
+ * changes from mainstream
+ *
  * Revision 1.29.6.2  2001/03/21 08:52:21  kai
  * merge from main branch: fix buffer for revision string (calle)
  *
@@ -122,7 +125,7 @@
 #include "capilli.h"
 #include "avmcard.h"
 
-static char *revision = "$Revision: 1.29.6.2 $";
+static char *revision = "$Revision: 1.29.6.3 $";
 
 /* ------------------------------------------------------------- */
 
diff -urN linux-2.4.4-pre5/drivers/isdn/avmb1/c4.c linux/drivers/isdn/avmb1/c4.c
--- linux-2.4.4-pre5/drivers/isdn/avmb1/c4.c	Mon Dec  3 22:36:14 2001
+++ linux/drivers/isdn/avmb1/c4.c	Mon Dec  3 22:36:25 2001
@@ -1,11 +1,14 @@
 /*
- * $Id: c4.c,v 1.20.6.5 2001/03/21 08:52:21 kai Exp $
+ * $Id: c4.c,v 1.20.6.6 2001/04/20 02:41:59 keil Exp $
  * 
  * Module for AVM C4 card.
  * 
  * (c) Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de)
  * 
  * $Log: c4.c,v $
+ * Revision 1.20.6.6  2001/04/20 02:41:59  keil
+ * changes from mainstream
+ *
  * Revision 1.20.6.5  2001/03/21 08:52:21  kai
  * merge from main branch: fix buffer for revision string (calle)
  *
@@ -118,17 +121,22 @@
 #include "capilli.h"
 #include "avmcard.h"
 
-static char *revision = "$Revision: 1.20.6.5 $";
+static char *revision = "$Revision: 1.20.6.6 $";
 
 #undef CONFIG_C4_DEBUG
 #undef CONFIG_C4_POLLDEBUG
 
 /* ------------------------------------------------------------- */
+#ifndef PCI_DEVICE_ID_AVM_C2
+#define PCI_DEVICE_ID_AVM_C2	0x1100
+#endif
+/* ------------------------------------------------------------- */
 
 static int suppress_pollack;
 
 static struct pci_device_id c4_pci_tbl[] __initdata = {
 	{ PCI_VENDOR_ID_DEC,PCI_DEVICE_ID_DEC_21285, PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_C4 },
+	{ PCI_VENDOR_ID_DEC,PCI_DEVICE_ID_DEC_21285, PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_C2 },
 	{ }			/* Terminating entry */
 };
 
@@ -614,7 +622,7 @@
 		MsgLen = _get_slice(&p, card->msgbuf);
 		DataB3Len = _get_slice(&p, card->databuf);
 		cidx = CAPIMSG_CONTROLLER(card->msgbuf)-card->cardnr;
-		if (cidx > 3) cidx = 0;
+		if (cidx >= card->nlogcontr) cidx = 0;
 		ctrl = card->ctrlinfo[cidx].capi_ctrl;
 
 		if (MsgLen < 30) { /* not CAPI 64Bit */
@@ -637,7 +645,7 @@
 		ApplId = (unsigned) _get_word(&p);
 		MsgLen = _get_slice(&p, card->msgbuf);
 		cidx = CAPIMSG_CONTROLLER(card->msgbuf)-card->cardnr;
-		if (cidx > 3) cidx = 0;
+		if (cidx >= card->nlogcontr) cidx = 0;
 		ctrl = card->ctrlinfo[cidx].capi_ctrl;
 
 		if (!(skb = alloc_skb(MsgLen, GFP_ATOMIC))) {
@@ -655,7 +663,7 @@
 		NCCI = _get_word(&p);
 		WindowSize = _get_word(&p);
 		cidx = (NCCI&0x7f) - card->cardnr;
-		if (cidx > 3) cidx = 0;
+		if (cidx >= card->nlogcontr) cidx = 0;
 		ctrl = card->ctrlinfo[cidx].capi_ctrl;
 
 		ctrl->new_ncci(ctrl, ApplId, NCCI, WindowSize);
@@ -669,13 +677,15 @@
 
 		if (NCCI != 0xffffffff) {
 			cidx = (NCCI&0x7f) - card->cardnr;
-			if (cidx > 3) cidx = 0;
+			if (cidx >= card->nlogcontr) cidx = 0;
 			ctrl = card->ctrlinfo[cidx].capi_ctrl;
-			ctrl->free_ncci(ctrl, ApplId, NCCI);
+			if (ctrl)
+				ctrl->free_ncci(ctrl, ApplId, NCCI);
 		} else {
 			for (cidx=0; cidx < 4; cidx++) {
 				ctrl = card->ctrlinfo[cidx].capi_ctrl;
-				ctrl->appl_released(ctrl, ApplId);
+				if (ctrl)
+					ctrl->appl_released(ctrl, ApplId);
 			}
 		}
 		break;
@@ -688,20 +698,28 @@
 			queue_pollack(card);
 		for (cidx=0; cidx < 4; cidx++) {
 			ctrl = card->ctrlinfo[cidx].capi_ctrl;
-			ctrl->resume_output(ctrl);
+			if (ctrl)
+				ctrl->resume_output(ctrl);
 		}
 		break;
 
 	case RECEIVE_STOP:
 		for (cidx=0; cidx < 4; cidx++) {
 			ctrl = card->ctrlinfo[cidx].capi_ctrl;
-			ctrl->suspend_output(ctrl);
+			if (ctrl)
+				ctrl->suspend_output(ctrl);
 		}
 		break;
 
 	case RECEIVE_INIT:
 
-	        cidx = card->nlogcontr++;
+	        cidx = card->nlogcontr;
+		if (cidx >= 4 || !card->ctrlinfo[cidx].capi_ctrl) {
+			printk(KERN_ERR "%s: card with %d controllers ??\n",
+					card->name, cidx+1);
+			break;
+		}
+	        card->nlogcontr++;
 	        cinfo = &card->ctrlinfo[cidx];
 		ctrl = cinfo->capi_ctrl;
 		cinfo->versionlen = _get_slice(&p, cinfo->versionbuf);
@@ -755,6 +773,8 @@
 	if (status & DBELL_RESET_HOST) {
 		int i;
 		c4outmeml(card->mbase+PCI_OUT_INT_MASK, 0x0c);
+		if (card->nlogcontr == 0)
+			return;
 		printk(KERN_ERR "%s: unexpected reset\n", card->name);
                 for (i=0; i < 4; i++) {
 			avmctrl_info *cinfo = &card->ctrlinfo[i];
@@ -762,6 +782,7 @@
 			if (cinfo->capi_ctrl)
 				cinfo->capi_ctrl->reseted(cinfo->capi_ctrl);
 		}
+		card->nlogcontr = 0;
 		return;
 	}
 
@@ -993,6 +1014,7 @@
 		if (cinfo->capi_ctrl)
 			cinfo->capi_ctrl->reseted(cinfo->capi_ctrl);
 	}
+	card->nlogcontr = 0;
 }
 
 static void c4_remove_ctr(struct capi_ctr *ctrl)
@@ -1145,6 +1167,7 @@
 	case avm_t1isa: s = "T1 ISA (HEMA)"; break;
 	case avm_t1pci: s = "T1 PCI"; break;
 	case avm_c4: s = "C4"; break;
+	case avm_c2: s = "C2"; break;
 	default: s = "???"; break;
 	}
 	len += sprintf(page+len, "%-16s %s\n", "type", s);
@@ -1192,7 +1215,9 @@
 
 /* ------------------------------------------------------------- */
 
-static int c4_add_card(struct capi_driver *driver, struct capicardparams *p)
+static int c4_add_card(struct capi_driver *driver,
+		       struct capicardparams *p,
+		       int nr)
 {
 	avmctrl_info *cinfo;
 	avmcard *card;
@@ -1231,11 +1256,11 @@
 		cinfo = &card->ctrlinfo[i];
 		cinfo->card = card;
 	}
-	sprintf(card->name, "c4-%x", p->port);
+	sprintf(card->name, "%s-%x", driver->name, p->port);
 	card->port = p->port;
 	card->irq = p->irq;
 	card->membase = p->membase;
-	card->cardtype = avm_c4;
+	card->cardtype = nr == 4 ? avm_c4 : avm_c2;
 
 	if (check_region(card->port, AVMB1_PORTLEN)) {
 		printk(KERN_WARNING
@@ -1286,7 +1311,7 @@
 		return -EBUSY;
 	}
 
-	for (i=0; i < 4; i++) {
+	for (i=0; i < nr ; i++) {
 		cinfo = &card->ctrlinfo[i];
 		cinfo->card = card;
 		cinfo->capi_ctrl = di->attach_ctr(driver, card->name, cinfo);
@@ -1313,14 +1338,31 @@
 	skb_queue_head_init(&card->dma->send_queue);
 
 	printk(KERN_INFO
-		"%s: AVM C4 at i/o %#x, irq %d, mem %#lx\n",
-		driver->name, card->port, card->irq, card->membase);
+		"%s: AVM C%d at i/o %#x, irq %d, mem %#lx\n",
+		driver->name, nr, card->port, card->irq, card->membase);
 
 	return 0;
 }
 
 /* ------------------------------------------------------------- */
 
+static struct capi_driver c2_driver = {
+    name: "c2",
+    revision: "0.0",
+    load_firmware: c4_load_firmware,
+    reset_ctr: c4_reset_ctr,
+    remove_ctr: c4_remove_ctr,
+    register_appl: c4_register_appl,
+    release_appl: c4_release_appl,
+    send_message: c4_send_message,
+
+    procinfo: c4_procinfo,
+    ctr_read_proc: c4_read_proc,
+    driver_read_proc: 0,	/* use standard driver_read_proc */
+
+    add_card: 0, /* no add_card function */
+};
+
 static struct capi_driver c4_driver = {
     name: "c4",
     revision: "0.0",
@@ -1340,15 +1382,9 @@
 
 static int ncards = 0;
 
-static int __init c4_init(void)
+static int c4_attach_driver (struct capi_driver * driver)
 {
-	struct capi_driver *driver = &c4_driver;
-	struct pci_dev *dev = NULL;
 	char *p;
-	int retval;
-
-	MOD_INC_USE_COUNT;
-
 	if ((p = strchr(revision, ':')) != 0 && p[1]) {
 		strncpy(driver->revision, p + 2, sizeof(driver->revision));
 		driver->revision[sizeof(driver->revision)-1] = 0;
@@ -1365,15 +1401,23 @@
 		MOD_DEC_USE_COUNT;
 		return -ENODEV;
 	}
+	return 0;
+}
+
+static int __init search_cards(struct capi_driver * driver,
+				int pci_id, int nr)
+{
+	struct pci_dev *	dev	= NULL;
+	int			retval	= 0;
 
 	while ((dev = pci_find_subsys(
 			PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_21285,
-			PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_C4, dev))) {
+			PCI_VENDOR_ID_AVM, pci_id, dev))) {
 		struct capicardparams param;
 
 		if (pci_enable_device(dev) < 0) {
-		        printk(KERN_ERR "%s: failed to enable AVM-C4\n",
-			       driver->name);
+		        printk(KERN_ERR "%s: failed to enable AVM-C%d\n",
+			       driver->name, nr);
 			continue;
 		}
 		pci_set_master(dev);
@@ -1383,31 +1427,69 @@
 		param.membase = pci_resource_start(dev, 0);
   
 		printk(KERN_INFO
-			"%s: PCI BIOS reports AVM-C4 at i/o %#x, irq %d, mem %#x\n",
-			driver->name, param.port, param.irq, param.membase);
-		retval = c4_add_card(driver, &param);
+			"%s: PCI BIOS reports AVM-C%d at i/o %#x, irq %d, mem %#x\n",
+			driver->name, nr, param.port, param.irq, param.membase);
+		retval = c4_add_card(driver, &param, nr);
 		if (retval != 0) {
 		        printk(KERN_ERR
-			"%s: no AVM-C4 at i/o %#x, irq %d detected, mem %#x\n",
-			driver->name, param.port, param.irq, param.membase);
+			"%s: no AVM-C%d at i/o %#x, irq %d detected, mem %#x\n",
+			driver->name, nr, param.port, param.irq, param.membase);
 			continue;
 		}
 		ncards++;
 	}
+	return retval;
+}
+
+static int __init c4_init(void)
+{
+	int retval;
+
+	MOD_INC_USE_COUNT;
+
+	retval = c4_attach_driver (&c4_driver);
+	if (retval) {
+		MOD_DEC_USE_COUNT;
+		return retval;
+	}
+
+	retval = c4_attach_driver (&c2_driver);
+	if (retval) {
+		MOD_DEC_USE_COUNT;
+		return retval;
+	}
+
+	retval = search_cards(&c4_driver, PCI_DEVICE_ID_AVM_C4, 4);
+	if (retval && ncards == 0) {
+    		detach_capi_driver(&c2_driver);
+    		detach_capi_driver(&c4_driver);
+		MOD_DEC_USE_COUNT;
+		return retval;
+	}
+	retval = search_cards(&c2_driver, PCI_DEVICE_ID_AVM_C2, 2);
+	if (retval && ncards == 0) {
+    		detach_capi_driver(&c2_driver);
+    		detach_capi_driver(&c4_driver);
+		MOD_DEC_USE_COUNT;
+		return retval;
+	}
+
 	if (ncards) {
-		printk(KERN_INFO "%s: %d C4 card(s) detected\n",
-				driver->name, ncards);
+		printk(KERN_INFO "%s: %d C4/C2 card(s) detected\n",
+				c4_driver.name, ncards);
 		MOD_DEC_USE_COUNT;
 		return 0;
 	}
-	printk(KERN_ERR "%s: NO C4 card detected\n", driver->name);
-	detach_capi_driver(driver);
+	printk(KERN_ERR "%s: NO C4/C2 card detected\n", c4_driver.name);
+	detach_capi_driver(&c4_driver);
+	detach_capi_driver(&c2_driver);
 	MOD_DEC_USE_COUNT;
 	return -ENODEV;
 }
 
 static void __exit c4_exit(void)
 {
+    detach_capi_driver(&c2_driver);
     detach_capi_driver(&c4_driver);
 }
 
diff -urN linux-2.4.4-pre5/drivers/isdn/hisax/md5sums.asc linux/drivers/isdn/hisax/md5sums.asc
--- linux-2.4.4-pre5/drivers/isdn/hisax/md5sums.asc	Wed Jan 10 14:12:53 2001
+++ linux/drivers/isdn/hisax/md5sums.asc	Mon Dec  3 22:36:25 2001
@@ -7,27 +7,27 @@
 # cards in the moment.
 # Read ../../../Documentation/isdn/HiSax.cert for more informations.
 # 
-ca7bd9bac39203f3074f3f093948cc3c  isac.c
-a2ad619fd404b3149099a2984de9d23c  isdnl1.c
-d2a78e407f3d94876deac160c6f9aae6  isdnl2.c
-e7932ca7ae39c497c17f13a2e1434fcd  isdnl3.c
-afb5f2f4ac296d6de45c856993b161e1  tei.c
-00023e2a482cb86a26ea870577ade5d6  callc.c
-a1834e9b2ec068440cff2e899eff4710  cert.c
-1551f78b3cd01097ecd586b5c96d0765  l3dss1.c
-89aecf3a80489c723dc885fcaa4aba1b  l3_1tr6.c
-1685c1ddfecf3e1b88ae5f3d7202ce69  elsa.c
-6d7056d1558bf6cc57dd89b7b260dc27  diva.c
-4398918680d45c4618bb48108ea0c282  sedlbauer.c
+9663cc9f4374c361197d394f6d27c459  isac.c
+9666c672c0fa0e65d5cc5b322f10a18c  isdnl1.c
+9250f15b932dfc36855aa120b896ed0b  isdnl2.c
+0cc2ef892bdb4a2be473e00eb1398950  isdnl3.c
+cac9c32fff889c57ff50d59823053019  tei.c
+665044a72334336533ac79da1a831b17  callc.c
+e592db58630c1f1029cc064110108156  cert.c
+fadeb3b85bb23bc1ac48470c0848d6fa  l3dss1.c
+cf7dec9fac6283716904d26b99188476  l3_1tr6.c
+65d9e5471bc129624f858ebcf0743525  elsa.c
+b4cf8a4dceed9ea6dcba65a85b4eecc7  diva.c
+99e67bea8f6945fa0d4e0aded5bf0fa0  sedlbauer.c
 # end of md5sums
 
 -----BEGIN PGP SIGNATURE-----
 Version: 2.6.3i
 Charset: noconv
 
-iQCVAwUBOlxeLTpxHvX/mS9tAQH6RwP8DhyvqAnXFV6WIGi16iQ3vKikkPoqnDQs
-GEn5uCW0dPYKlwthD2Grj/JbMYZhOmCFuDxF7ufJnjTSDe/D8XNe2wngxzAiwcIe
-WjCrT8X95cuP3HZHscbFTEinVV0GAnoI0ZEgs5eBDhVHDqILLYMaTFBQaRH3jgXc
-i5VH88jPfUM=
-=qc+J
+iQCVAwUBOt+j/jpxHvX/mS9tAQGXwAP/U4voKzXAcTfo9CqJhHN92GRxunj6mlvn
+H+1pxSe0GdtC7BlrPhrokB5dNSwewk89Z5t7kTD76kx2FFuTcXBJxbgH7LZVF3ga
+JX92bOWQekHMH7Hk12Qc7zpeTmPzY02pvVc37Eo614BCvJMCk02cpQyo8a5wWRKH
+8vpQkQKiSyY=
+=FFLG
 -----END PGP SIGNATURE-----
diff -urN linux-2.4.4-pre5/drivers/isdn/icn/icn.c linux/drivers/isdn/icn/icn.c
--- linux-2.4.4-pre5/drivers/isdn/icn/icn.c	Fri Mar  2 11:12:10 2001
+++ linux/drivers/isdn/icn/icn.c	Mon Dec  3 22:36:25 2001
@@ -1,4 +1,4 @@
-/* $Id: icn.c,v 1.65.6.3 2001/02/16 16:43:31 kai Exp $
+/* $Id: icn.c,v 1.65.6.4 2001/04/20 02:42:01 keil Exp $
 
  * ISDN low-level module for the ICN active ISDN-Card.
  *
@@ -34,7 +34,7 @@
 #undef MAP_DEBUG
 
 static char
-*revision = "$Revision: 1.65.6.3 $";
+*revision = "$Revision: 1.65.6.4 $";
 
 static int icn_addcard(int, char *, char *);
 
@@ -838,23 +838,23 @@
 			card->other->rvalid = 1;
 	}
 	if (!dev.mvalid) {
-		if (check_shmem((ulong) dev.shmem, 0x4000)) {
+		if (check_mem_region(dev.memaddr, 0x4000)) {
 			printk(KERN_WARNING
-			       "icn: memory at 0x%08lx in use.\n",
-			       (ulong) dev.shmem);
+			       "icn: memory at 0x%08lx in use.\n", dev.memaddr);
 			restore_flags(flags);
 			return -EBUSY;
 		}
-		request_shmem((ulong) dev.shmem, 0x4000, "icn");
+		request_mem_region(dev.memaddr, 0x4000, "icn-isdn (all cards)");
+		dev.shmem = ioremap(dev.memaddr, 0x4000);
 		dev.mvalid = 1;
 	}
 	restore_flags(flags);
 	OUTB_P(0, ICN_RUN);     /* Reset Controller */
 	OUTB_P(0, ICN_MAPRAM);  /* Disable RAM      */
 	icn_shiftout(ICN_CFG, 0x0f, 3, 4);	/* Windowsize= 16k  */
-	icn_shiftout(ICN_CFG, (unsigned long) dev.shmem, 23, 10);	/* Set RAM-Addr.    */
+	icn_shiftout(ICN_CFG, dev.memaddr, 23, 10);	/* Set RAM-Addr.    */
 #ifdef BOOT_DEBUG
-	printk(KERN_DEBUG "shmem=%08lx\n", (ulong) dev.shmem);
+	printk(KERN_DEBUG "shmem=%08lx\n", dev.memaddr);
 #endif
 	SLEEP(1);
 #ifdef BOOT_DEBUG
@@ -1177,29 +1177,31 @@
 			memcpy(&a, c->parm.num, sizeof(ulong));
 			switch (c->arg) {
 				case ICN_IOCTL_SETMMIO:
-					if ((unsigned long) dev.shmem != (a & 0x0ffc000)) {
-						if (check_shmem((ulong) (a & 0x0ffc000), 0x4000)) {
+					if (dev.memaddr != (a & 0x0ffc000)) {
+						if (check_mem_region(a & 0x0ffc000, 0x4000)) {
 							printk(KERN_WARNING
 							       "icn: memory at 0x%08lx in use.\n",
-							       (ulong) (a & 0x0ffc000));
+							       a & 0x0ffc000);
 							return -EINVAL;
 						}
 						icn_stopallcards();
 						save_flags(flags);
 						cli();
-						if (dev.mvalid)
-							release_shmem((ulong) dev.shmem, 0x4000);
+						if (dev.mvalid) {
+							iounmap(dev.shmem);
+							release_mem_region(dev.memaddr, 0x4000);
+						}
 						dev.mvalid = 0;
-						dev.shmem = (icn_shmem *) (a & 0x0ffc000);
+						dev.memaddr = a & 0x0ffc000;
 						restore_flags(flags);
 						printk(KERN_INFO
 						       "icn: (%s) mmio set to 0x%08lx\n",
 						       CID,
-						       (unsigned long) dev.shmem);
+						       dev.memaddr);
 					}
 					break;
 				case ICN_IOCTL_GETMMIO:
-					return (long) dev.shmem;
+					return (long) dev.memaddr;
 				case ICN_IOCTL_SETPORT:
 					if (a == 0x300 || a == 0x310 || a == 0x320 || a == 0x330
 					    || a == 0x340 || a == 0x350 || a == 0x360 ||
@@ -1652,7 +1654,7 @@
 	if (ints[0])
 		portbase = ints[1];
 	if (ints[0] > 1)
-		membase = ints[2];
+		membase = (unsigned long)ints[2];
 	if (str && *str) {
 		strcpy(sid, str);
 		icn_id = sid;
@@ -1673,7 +1675,7 @@
 	char rev[10];
 
 	memset(&dev, 0, sizeof(icn_dev));
-	dev.shmem = (icn_shmem *) ((unsigned long) membase & 0x0ffc000);
+	dev.memaddr = (membase & 0x0ffc000);
 	dev.channel = -1;
 	dev.mcard = NULL;
 	dev.firstload = 1;
@@ -1685,7 +1687,7 @@
 	} else
 		strcpy(rev, " ??? ");
 	printk(KERN_NOTICE "ICN-ISDN-driver Rev%smem=0x%08lx\n", rev,
-	       (ulong) dev.shmem);
+	       dev.memaddr);
 	return (icn_addcard(portbase, icn_id, icn_id2));
 }
 
@@ -1719,8 +1721,10 @@
 		card = card->next;
 		kfree(last);
 	}
-	if (dev.mvalid)
-		release_shmem((ulong) dev.shmem, 0x4000);
+	if (dev.mvalid) {
+		iounmap(dev.shmem);
+		release_mem_region(dev.memaddr, 0x4000);
+	}
 	printk(KERN_NOTICE "ICN-ISDN-driver unloaded\n");
 }
 
diff -urN linux-2.4.4-pre5/drivers/isdn/icn/icn.h linux/drivers/isdn/icn/icn.h
--- linux-2.4.4-pre5/drivers/isdn/icn/icn.h	Fri Mar  2 11:12:10 2001
+++ linux/drivers/isdn/icn/icn.h	Mon Dec  3 22:36:25 2001
@@ -1,4 +1,4 @@
-/* $Id: icn.h,v 1.30.6.2 2001/02/16 16:43:31 kai Exp $
+/* $Id: icn.h,v 1.30.6.3 2001/04/20 02:42:01 keil Exp $
 
  * ISDN lowlevel-module for the ICN active ISDN-Card.
  *
@@ -187,6 +187,7 @@
  * Main driver data
  */
 typedef struct icn_dev {
+	unsigned long memaddr;	/* Address of memory mapped buffers */
 	icn_shmem *shmem;       /* Pointer to memory-mapped-buffers */
 	int mvalid;             /* IO-shmem has been requested      */
 	int channel;            /* Currently mapped channel         */
@@ -210,7 +211,7 @@
  * integers.
  */
 static int portbase = ICN_BASEADDR;
-static int membase = ICN_MEMADDR;
+static unsigned long membase = ICN_MEMADDR;
 static char *icn_id = "\0";
 static char *icn_id2 = "\0";
 
@@ -218,7 +219,7 @@
 MODULE_AUTHOR("Fritz Elfert");
 MODULE_PARM(portbase, "i");
 MODULE_PARM_DESC(portbase, "Port address of first card");
-MODULE_PARM(membase, "i");
+MODULE_PARM(membase, "l");
 MODULE_PARM_DESC(membase, "Shared memory address of all cards");
 MODULE_PARM(icn_id, "s");
 MODULE_PARM_DESC(icn_id, "ID-String of first card");
@@ -286,14 +287,6 @@
 
 #define MIN(a,b) ((a<b)?a:b)
 #define MAX(a,b) ((a>b)?a:b)
-
-/* Hopefully, a separate resource-registration-scheme for shared-memory
- * will be introduced into the kernel. Until then, we use the normal
- * routines, designed for port-registration.
- */
-#define check_shmem   check_region
-#define release_shmem release_region
-#define request_shmem request_region
 
 #endif                          /* defined(__KERNEL__) || defined(__DEBUGVAR__) */
 #endif                          /* icn_h */
diff -urN linux-2.4.4-pre5/drivers/md/lvm.c linux/drivers/md/lvm.c
--- linux-2.4.4-pre5/drivers/md/lvm.c	Mon Dec  3 22:36:14 2001
+++ linux/drivers/md/lvm.c	Mon Dec  3 22:36:25 2001
@@ -1674,10 +1674,11 @@
 			       int rw,
 			       struct buffer_head *bh)
 {
-	int ret = lvm_map(bh, rw);
-	if (ret < 0)
-		buffer_IO_error(bh);
-	return ret;
+	if (lvm_map(bh, rw) >= 0)
+		return 1;
+
+	buffer_IO_error(bh);
+	return 0;
 }
 
 
diff -urN linux-2.4.4-pre5/drivers/net/Config.in linux/drivers/net/Config.in
--- linux-2.4.4-pre5/drivers/net/Config.in	Mon Dec  3 22:36:14 2001
+++ linux/drivers/net/Config.in	Mon Dec  3 22:36:25 2001
@@ -227,6 +227,7 @@
 tristate 'PPP (point-to-point protocol) support' CONFIG_PPP
 if [ ! "$CONFIG_PPP" = "n" ]; then
    dep_bool '  PPP multilink support (EXPERIMENTAL)' CONFIG_PPP_MULTILINK $CONFIG_EXPERIMENTAL
+   bool '  PPP filtering' CONFIG_PPP_FILTER
    dep_tristate '  PPP support for async serial ports' CONFIG_PPP_ASYNC $CONFIG_PPP
    dep_tristate '  PPP support for sync tty ports' CONFIG_PPP_SYNC_TTY $CONFIG_PPP
    dep_tristate '  PPP Deflate compression' CONFIG_PPP_DEFLATE $CONFIG_PPP
diff -urN linux-2.4.4-pre5/drivers/net/atarilance.c linux/drivers/net/atarilance.c
--- linux-2.4.4-pre5/drivers/net/atarilance.c	Mon Dec  3 22:36:14 2001
+++ linux/drivers/net/atarilance.c	Mon Dec  3 22:36:25 2001
@@ -375,8 +375,8 @@
 
 int __init atarilance_probe( struct net_device *dev )
 {	
-    int i;
-	static int found = 0;
+	int i;
+	static int found;
 
 	SET_MODULE_OWNER(dev);
 
@@ -458,7 +458,7 @@
 	struct lance_private	*lp;
 	struct lance_ioreg		*IO;
 	int 					i;
-	static int 				did_version = 0;
+	static int 				did_version;
 	unsigned short			save1, save2;
 
 	PROBE_PRINT(( "Probing for Lance card at mem %#lx io %#lx\n",
diff -urN linux-2.4.4-pre5/drivers/net/defxx.c linux/drivers/net/defxx.c
--- linux-2.4.4-pre5/drivers/net/defxx.c	Sun Mar 25 18:24:31 2001
+++ linux/drivers/net/defxx.c	Mon Dec  3 22:36:25 2001
@@ -414,14 +414,17 @@
 {
 	struct net_device *dev;
 	DFX_board_t	  *bp;			/* board pointer */
-	static int version_disp;
 	int err;
 
-	if (!version_disp)					/* display version info if adapter is found */
+#ifndef MODULE
+	static int version_disp;
+
+	if (!version_disp)	/* display version info if adapter is found */
 	{
-		version_disp = 1;				/* set display flag to TRUE so that */
-		printk(version);				/* we only display this string ONCE */
+		version_disp = 1;	/* set display flag to TRUE so that */
+		printk(version);	/* we only display this string ONCE */
 	}
+#endif
 
 	/*
 	 * init_fddidev() allocates a device structure with private data, clears the device structure and private data,
@@ -3384,6 +3387,11 @@
 static int __init dfx_init(void)
 {
 	int rc_pci, rc_eisa;
+
+/* when a module, this is printed whether or not devices are found in probe */
+#ifdef MODULE
+	printk(version);
+#endif
 
 	rc_pci = pci_module_init(&dfx_driver);
 	if (rc_pci >= 0) dfx_have_pci = 1;
diff -urN linux-2.4.4-pre5/drivers/net/dmfe.c linux/drivers/net/dmfe.c
--- linux-2.4.4-pre5/drivers/net/dmfe.c	Tue Mar 20 12:05:01 2001
+++ linux/drivers/net/dmfe.c	Mon Dec  3 22:36:25 2001
@@ -57,6 +57,14 @@
    Resource usage cleanups.
    Report driver version to user.
 
+   Tobias Ringstr÷m <zajbot@goteborg.utfors.se> :
+   Added additional proper locking
+   Rewrote the transmit code to actually use the ring buffer,
+   and to generate a lot fewer interrupts.
+
+   Frank Davis <fdavis112@juno.com>
+   Added SMP-safe locking mechanisms in addition to Andrew Morton's work
+
    TODO
 
    Implement pci_driver::suspend() and pci_driver::resume()
@@ -68,7 +76,7 @@
 
  */
 
-#define DMFE_VERSION "1.30 (June 11, 2000)"
+#define DMFE_VERSION "1.30p3 (April 17, 2001)"
 
 #include <linux/module.h>
 
@@ -88,12 +96,16 @@
 #include <linux/etherdevice.h>
 #include <linux/skbuff.h>
 #include <linux/delay.h>
+#include <linux/spinlock.h>
 
 #include <asm/processor.h>
 #include <asm/bitops.h>
 #include <asm/io.h>
 #include <asm/dma.h>
 
+static char version[] __initdata =
+KERN_INFO "Davicom DM91xx net driver, version " DMFE_VERSION "\n";
+
 
 
 /* Board/System/Debug information/definition ---------------- */
@@ -108,6 +120,7 @@
 #define TX_MAX_SEND_CNT 0x1	/* Maximum tx packet per time */
 #define TX_DESC_CNT     0x10	/* Allocated Tx descriptors */
 #define RX_DESC_CNT     0x10	/* Allocated Rx descriptors */
+#define TX_IRQ_THR      12
 #define DESC_ALL_CNT    TX_DESC_CNT+RX_DESC_CNT
 #define TX_BUF_ALLOC    0x600
 #define RX_ALLOC_SIZE   0x620
@@ -187,7 +200,7 @@
 	u32 chip_id;		/* Chip vendor/Device ID */
 	u32 chip_revision;	/* Chip revision */
 	struct net_device *next_dev;	/* next device */
-
+	spinlock_t lock; /* Spinlock */
 	struct pci_dev *net_dev;	/* PCI device */
 
 	unsigned long ioaddr;		/* I/O base address */
@@ -207,8 +220,9 @@
 	struct rx_desc *first_rx_desc;
 	struct rx_desc *rx_insert_ptr;
 	struct rx_desc *rx_ready_ptr;	/* packet come pointer */
-	u32 tx_packet_cnt;	/* transmitted packet count */
-	u32 tx_queue_cnt;	/* wait to send packet count */
+	int tx_int_pkt_num;	/* number of packets to transmit until
+				 * a transmit interrupt is requested */
+	u32 tx_live_cnt;	/* number of used/live tx slots */
 	u32 rx_avail_cnt;	/* available rx descriptor count */
 	u32 interval_rx_cnt;	/* rx packet count a callback time */
 
@@ -220,7 +234,7 @@
 	u8 link_failed;		/* Ever link failed */
 	u8 wait_reset;		/* Hardware failed, need to reset */
 	u8 in_reset_state;	/* Now driver in reset routine */
-	u8 rx_error_cnt;	/* recievd abnormal case count */
+	u8 rx_error_cnt;	/* recieved abnormal case count */
 	u8 dm910x_chk_mode;	/* Operating mode check */
 	struct timer_list timer;
 	struct net_device_stats stats;	/* statistic counter */
@@ -240,13 +254,13 @@
 };
 
 /* Global variable declaration ----------------------------- */
-static int dmfe_debug = 0;
+static int dmfe_debug;
 static unsigned char dmfe_media_mode = 8;
-static u32 dmfe_cr6_user_set = 0;
+static u32 dmfe_cr6_user_set;
 
 /* For module input parameter */
-static int debug = 0;
-static u32 cr6set = 0;
+static int debug;
+static u32 cr6set;
 static unsigned char mode = 8;
 static u8 chkmode = 1;
 
@@ -364,6 +378,13 @@
 	u32 dev_rev;
 	u16 pci_command;
 
+/* when built into the kernel, we only print version if device is found */
+#ifndef MODULE
+	static int printed_version;
+	if (!printed_version++)
+		printk(version);
+#endif
+
 	DMFE_DBUG(0, "dmfe_probe()", 0);
 
 	/* Enable Master/IO access, Disable memory access */
@@ -380,14 +401,14 @@
 
 	/* Interrupt check */
 	if (pci_irqline == 0) {
-		printk(KERN_ERR "dmfe: Interrupt wrong : IRQ=%d\n",
-		       pci_irqline);
+		printk(KERN_ERR "dmfe(%s): Interrupt wrong : IRQ=%d\n",
+		       pdev->slot_name, pci_irqline);
 		goto err_out;
 	}
 
 	/* iobase check */
-	if (pci_iobase == 0) {
-		printk(KERN_ERR "dmfe: I/O base is zero\n");
+	if (pci_iobase == 0 || pci_resource_len(pdev, 0) == 0) {
+		printk(KERN_ERR "dmfe(%s): I/O base is zero\n", pdev->slot_name);
 		goto err_out;
 	}
 
@@ -404,20 +425,17 @@
 	pci_read_config_dword(pdev, PCI_REVISION_ID, &dev_rev);
 
 	/* Init network device */
-	dev = init_etherdev(NULL, sizeof(*db));
+	dev = alloc_etherdev(sizeof(*db));
 	if (dev == NULL)
 		goto err_out;
 	SET_MODULE_OWNER(dev);
 
-	/* IO range check */
-	if (!request_region(pci_iobase, CHK_IO_SIZE(pdev, dev_rev), dev->name)) {
-		printk(KERN_ERR "dmfe: I/O conflict : IO=%lx Range=%x\n",
-		       pci_iobase, CHK_IO_SIZE(pdev, dev_rev));
+	if (pci_request_regions(pdev, "dmfe"))
 		goto err_out_netdev;
-	}
 
 	db = dev->priv;
 	pci_set_drvdata(pdev, dev);
+	spin_lock_init(&db->lock);
 
 	db->chip_id = ent->driver_data;
 	db->ioaddr = pci_iobase;
@@ -438,14 +456,28 @@
 	for (i = 0; i < 64; i++)
 		((u16 *) db->srom)[i] = read_srom_word(pci_iobase, i);
 
+	printk(KERN_INFO "%s: Davicom DM%04lx at 0x%lx,",
+	       dev->name,
+	       ent->driver_data >> 16,
+	       pci_iobase);
+
 	/* Set Node address */
-	for (i = 0; i < 6; i++)
+	for (i = 0; i < 6; i++) {
 		dev->dev_addr[i] = db->srom[20 + i];
+		printk("%c%02x", i ? ':' : ' ', dev->dev_addr[i]);
+	}
+
+	printk(", IRQ %d\n", pci_irqline);
+
+	i = register_netdev(dev);
+	if (i)
+		goto err_out_res;
 
 	return 0;
 
+err_out_res:
+	pci_release_regions(pdev);
 err_out_netdev:
-	unregister_netdev(dev);
 	kfree(dev);
 err_out:
 	return -ENODEV;
@@ -462,7 +494,7 @@
 	db = dev->priv;
 
 	unregister_netdev(dev);
-	release_region(dev->base_addr, CHK_IO_SIZE(pdev, db->chip_revision));
+	pci_release_regions(pdev);
 	kfree(dev);	/* free board information */
 
 	pci_set_drvdata(pdev, NULL);
@@ -509,8 +541,8 @@
 
 	/* system variable init */
 	db->cr6_data = CR6_DEFAULT | dmfe_cr6_user_set;
-	db->tx_packet_cnt = 0;
-	db->tx_queue_cnt = 0;
+	db->tx_int_pkt_num = TX_IRQ_THR;
+	db->tx_live_cnt = 0;
 	db->rx_avail_cnt = 0;
 	db->link_failed = 0;
 	db->wait_reset = 0;
@@ -552,7 +584,7 @@
 {
 	struct dmfe_board_info *db = dev->priv;
 	u32 ioaddr = db->ioaddr;
-
+	
 	DMFE_DBUG(0, "dmfe_init_dm910x()", 0);
 
 	/* Reset DM910x board : need 32 PCI clock to complete */
@@ -611,46 +643,41 @@
 {
 	struct dmfe_board_info *db = dev->priv;
 	struct tx_desc *txptr;
+	unsigned long flags;
 
 	DMFE_DBUG(0, "dmfe_start_xmit", 0);
- 
-	netif_stop_queue(dev);
+	spin_lock_irqsave(&db->lock, flags);
 	
-	/* Too large packet check */
-	if (skb->len > MAX_PACKET_SIZE) {
-		printk(KERN_ERR "%s: oversized frame (%d bytes) for transmit.\n", dev->name, (u16) skb->len);
-		dev_kfree_skb(skb);
-		return 0;
-	}
-	/* No Tx resource check, it never happen nromally */
-	if (db->tx_packet_cnt >= TX_FREE_DESC_CNT) {
-		return 1;
-	}
-
 	/* transmit this packet */
 	txptr = db->tx_insert_ptr;
 	memcpy((char *) txptr->tx_buf_ptr, (char *) skb->data, skb->len);
-	txptr->tdes1 = 0xe1000000 | skb->len;
+	if (--db->tx_int_pkt_num < 0)
+	{
+		txptr->tdes1 = 0xe1000000 | skb->len;
+		db->tx_int_pkt_num = TX_IRQ_THR;
+	}
+	else
+		txptr->tdes1 = 0x61000000 | skb->len;
+
+	/* Transmit Packet Process */
+	txptr->tdes0 = 0x80000000;	/* set owner bit to DM910X */
+	outl(0x1, dev->base_addr + DCR1);	/* Issue Tx polling command */
+	dev->trans_start = jiffies;     /* saved the time stamp */
 
 	/* Point to next transmit free descriptor */
-	db->tx_insert_ptr = (struct tx_desc *) txptr->next_tx_desc;
+	txptr = (struct tx_desc *)txptr->next_tx_desc;
 
-	/* Transmit Packet Process */
-	if (db->tx_packet_cnt < TX_MAX_SEND_CNT) {
-		txptr->tdes0 = 0x80000000;	/* set owner bit to DM910X */
-		db->tx_packet_cnt++;	/* Ready to send count */
-		outl(0x1, dev->base_addr + DCR1);	/* Issue Tx polling command */
-	} else {
-		db->tx_queue_cnt++;	/* queue the tx packet */
-		outl(0x1, dev->base_addr + DCR1);	/* Issue Tx polling command */
-	}
+	if (txptr->tdes0 & 0x80000000)
+		netif_stop_queue(dev);
 
-	/* Tx resource check */
-	if (db->tx_packet_cnt < TX_FREE_DESC_CNT)
-		netif_wake_queue(dev);
+	db->tx_insert_ptr = txptr;
+	db->tx_live_cnt++;
+
+	spin_unlock_irqrestore(&db->lock, flags);
 
 	/* free this SKB */
 	dev_kfree_skb(skb);
+
 	return 0;
 }
 
@@ -674,7 +701,7 @@
 
 	/* deleted timer */
 	del_timer_sync(&db->timer);
-
+	
 	/* free interrupt */
 	free_irq(dev->irq, dev);
 
@@ -710,6 +737,8 @@
 
 	DMFE_DBUG(0, "dmfe_interrupt()", 0);
 
+	spin_lock_irq(&db->lock);
+
 	/* Disable all interrupt in CR7 to solve the interrupt edge problem */
 	outl(0, ioaddr + DCR7);
 
@@ -725,14 +754,15 @@
 		netif_stop_queue(dev);
 		db->wait_reset = 1;		/* Need to RESET */
 		outl(0, ioaddr + DCR7);		/* disable all interrupt */
+		spin_unlock_irq(&db->lock);
 		return;
 	}
+
 	/* Free the transmitted descriptor */
 	txptr = db->tx_remove_ptr;
-	while (db->tx_packet_cnt) {
+	while (db->tx_live_cnt > 0 && (txptr->tdes0 & 0x80000000) == 0)
+	{
 		/* printk("tdes0=%x\n", txptr->tdes0); */
-		if (txptr->tdes0 & 0x80000000)
-			break;
 		db->stats.tx_packets++;
 		
 		if ((txptr->tdes0 & TDES0_ERR_MASK) && (txptr->tdes0 != 0x7fffffff)) {
@@ -748,21 +778,12 @@
 				db->stats.tx_errors++;
 		}
 		txptr = (struct tx_desc *) txptr->next_tx_desc;
-		db->tx_packet_cnt--;
+		db->tx_live_cnt--;
 	}
 	/* Update TX remove pointer to next */
 	db->tx_remove_ptr = (struct tx_desc *) txptr;
 
-	/* Send the Tx packet in queue */
-	if ((db->tx_packet_cnt < TX_MAX_SEND_CNT) && db->tx_queue_cnt) {
-		txptr->tdes0 = 0x80000000;	/* set owner bit to DM910X */
-		db->tx_packet_cnt++;	/* Ready to send count */
-		outl(0x1, ioaddr + DCR1);	/* Issue Tx polling command */
-		dev->trans_start = jiffies;	/* saved the time stamp */
-		db->tx_queue_cnt--;
- 	}
-	/* Resource available check */
-	if (db->tx_packet_cnt < TX_FREE_DESC_CNT)
+	if ((db->tx_insert_ptr->tdes0 & 0x80000000) == 0)
 		netif_wake_queue(dev);
 
 	/* Received the coming packet */
@@ -786,6 +807,8 @@
 	else
 		db->cr7_data = 0x1a2cd;
 	outl(db->cr7_data, ioaddr + DCR7);
+
+	spin_unlock_irq(&db->lock);
 }
 
 /*
@@ -902,6 +925,11 @@
 /*
    Process the upper socket ioctl command
  */
+
+/* 
+ * The following function just returns 0. Shouldn't it do more? 
+ */
+
 static int dmfe_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
 {
 	DMFE_DBUG(0, "dmfe_do_ioctl()", 0);
@@ -944,12 +972,14 @@
 
 	db->interval_rx_cnt = 0;
 
-	if (db->wait_reset | (db->tx_packet_cnt &&
-			      ((jiffies - dev->trans_start) > DMFE_TX_TIMEOUT)) | (db->rx_error_cnt > 3)) {
+	if (db->wait_reset ||
+	    (db->tx_live_cnt > 0 &&
+	     ((jiffies - dev->trans_start) > DMFE_TX_TIMEOUT)) ||
+	    (db->rx_error_cnt > 3)) {
 		/*
 		   printk("wait_reset %x, tx cnt %x, rx err %x, time %x\n", db->wait_reset, db->tx_packet_cnt, db->rx_error_cnt, jiffies-dev->trans_start);
 		 */
-		DMFE_DBUG(0, "Warn!! Warn!! Tx/Rx moniotr step1", db->tx_packet_cnt);
+		DMFE_DBUG(0, "Warn!! Warn!! Tx/Rx monitor step1", 0);
 		dmfe_dynamic_reset(dev);
 		db->timer.expires = jiffies + DMFE_TIMER_WUT;
 		add_timer(&db->timer);
@@ -1036,8 +1066,8 @@
 	dmfe_free_rxbuffer(db);
 
 	/* system variable init */
-	db->tx_packet_cnt = 0;
-	db->tx_queue_cnt = 0;
+	db->tx_int_pkt_num = TX_IRQ_THR;
+	db->tx_live_cnt = 0;
 	db->rx_avail_cnt = 0;
 	db->link_failed = 0;
 	db->wait_reset = 0;
@@ -1203,15 +1233,24 @@
 	struct dmfe_board_info *db = dev->priv;
 	struct dev_mc_list *mcptr;
 	struct tx_desc *txptr;
+	unsigned long flags;
 	u16 *addrptr;
 	u32 *suptr;
 	int i;
 
-	DMFE_DBUG(0, "send_filetr_frame()", 0);
+	DMFE_DBUG(0, "send_filter_frame()", 0);
+	spin_lock_irqsave(&db->lock, flags);
 
 	txptr = db->tx_insert_ptr;
 	suptr = (u32 *) txptr->tx_buf_ptr;
 
+	if (txptr->tdes0 & 0x80000000) {
+		spin_unlock_irqrestore(&db->lock, flags);
+		printk(KERN_WARNING "%s: Too busy to send filter frame\n",
+		       dev->name);
+		return;
+	}
+
 	/* Node address */
 	addrptr = (u16 *) dev->dev_addr;
 	*suptr++ = addrptr[0];
@@ -1231,26 +1270,21 @@
 		*suptr++ = addrptr[2];
 	}
 
-	for (; i < 14; i++) {
+	for (i=0; i < 14; i++) 
 		*suptr++ = 0xffff;
 		*suptr++ = 0xffff;
 		*suptr++ = 0xffff;
-	}
+	
 	/* prepare the setup frame */
 	db->tx_insert_ptr = (struct tx_desc *) txptr->next_tx_desc;
+	db->tx_live_cnt++;
 	txptr->tdes1 = 0x890000c0;
-	/* Resource Check and Send the setup packet */
-	if (!db->tx_packet_cnt) {
-		/* Resource Empty */
-		db->tx_packet_cnt++;
-		txptr->tdes0 = 0x80000000;
-		update_cr6(db->cr6_data | 0x2000, dev->base_addr);
-		outl(0x1, dev->base_addr + DCR1);	/* Issue Tx polling command */
-		update_cr6(db->cr6_data, dev->base_addr);
-	} else {
-		/* Put into TX queue */
-		db->tx_queue_cnt++;
-	}
+	/* Send the setup packet */
+	txptr->tdes0 = 0x80000000;
+	update_cr6(db->cr6_data | 0x2000, dev->base_addr);
+	outl(0x1, dev->base_addr + DCR1);	/* Issue Tx polling command */
+	update_cr6(db->cr6_data, dev->base_addr);
+	spin_unlock_irqrestore(&db->lock, flags);
 }
 
 /*
@@ -1593,6 +1627,11 @@
 {
 	int rc;
 	
+/* when a module, this is printed whether or not devices are found in probe */
+#ifdef MODULE
+	printk(version);
+#endif
+
 	DMFE_DBUG(0, "init_module() ", debug);
 
 	if (debug)
@@ -1616,8 +1655,6 @@
 	if (rc < 0)
 		return rc;
 
-	printk (KERN_INFO "Davicom DM91xx net driver loaded, version "
-		DMFE_VERSION "\n");
 	return 0;
 }
 
diff -urN linux-2.4.4-pre5/drivers/net/epic100.c linux/drivers/net/epic100.c
--- linux-2.4.4-pre5/drivers/net/epic100.c	Sun Mar 25 18:24:31 2001
+++ linux/drivers/net/epic100.c	Mon Dec  3 22:36:25 2001
@@ -43,14 +43,6 @@
 
 */
 
-/* These identify the driver base version and may not be removed. */
-static const char version[] =
-"epic100.c:v1.11 1/7/2001 Written by Donald Becker <becker@scyld.com>\n";
-static const char version2[] =
-"  http://www.scyld.com/network/epic100.html\n";
-static const char version3[] =
-" (unofficial 2.4.x kernel port, version 1.1.6, January 11, 2001)\n";
-
 /* The user-configurable values.
    These may be modified when a driver module is loaded.*/
 
@@ -115,6 +107,14 @@
 #include <asm/bitops.h>
 #include <asm/io.h>
 
+/* These identify the driver base version and may not be removed. */
+static char version[] __devinitdata =
+"epic100.c:v1.11 1/7/2001 Written by Donald Becker <becker@scyld.com>\n";
+static char version2[] __devinitdata =
+"  http://www.scyld.com/network/epic100.html\n";
+static char version3[] __devinitdata =
+"  (unofficial 2.4.x kernel port, version 1.1.7, April 17, 2001)\n";
+
 MODULE_AUTHOR("Donald Becker <becker@scyld.com>");
 MODULE_DESCRIPTION("SMC 83c170 EPIC series Ethernet driver");
 MODULE_PARM(debug, "i");
@@ -337,7 +337,6 @@
 				    const struct pci_device_id *ent)
 {
 	static int card_idx = -1;
-	static int printed_version;
 	long ioaddr;
 	int chip_idx = (int) ent->driver_data;
 	int irq;
@@ -347,11 +346,15 @@
 	void *ring_space;
 	dma_addr_t ring_dma;
 
-	card_idx++;
-	
+/* when built into the kernel, we only print version if device is found */
+#ifndef MODULE
+	static int printed_version;
 	if (!printed_version++)
 		printk (KERN_INFO "%s" KERN_INFO "%s" KERN_INFO "%s",
 			version, version2, version3);
+#endif
+	
+	card_idx++;
 	
 	i = pci_enable_device(pdev);
 	if (i)
@@ -1440,6 +1443,12 @@
 
 static int __init epic_init (void)
 {
+/* when a module, this is printed whether or not devices are found in probe */
+#ifdef MODULE
+	printk (KERN_INFO "%s" KERN_INFO "%s" KERN_INFO "%s",
+		version, version2, version3);
+#endif
+
 	return pci_module_init (&epic_driver);
 }
 
diff -urN linux-2.4.4-pre5/drivers/net/hamachi.c linux/drivers/net/hamachi.c
--- linux-2.4.4-pre5/drivers/net/hamachi.c	Mon Dec  3 22:36:14 2001
+++ linux/drivers/net/hamachi.c	Mon Dec  3 22:36:25 2001
@@ -27,16 +27,6 @@
 
 */
 
-static const char *version =
-"hamachi.c:v1.01 5/16/2000  Written by Donald Becker\n"
-"   Some modifications by Eric kasten <kasten@nscl.msu.edu>\n"
-"   Further modifications by Keith Underwood <keithu@parl.clemson.edu>\n"
-"   Support by many others\n"
-"   http://www.scyld.com/network/hamachi.html\n"
-"   or\n"
-"   http://www.parl.clemson.edu/~keithu/drivers/hamachi.html\n";
-
-
 /* A few user-configurable values. */
 
 static int debug = 1;		/* 1 normal messages, 0 quiet .. 7 verbose.  */
@@ -175,6 +165,12 @@
 #include <linux/ip.h>
 #include <linux/delay.h>
 
+static char version[] __initdata =
+KERN_INFO "hamachi.c:v1.01 5/16/2000  Written by Donald Becker\n"
+KERN_INFO "   Some modifications by Eric kasten <kasten@nscl.msu.edu>\n"
+KERN_INFO "   Further modifications by Keith Underwood <keithu@parl.clemson.edu>\n";
+
+
 /* IP_MF appears to be only defined in <netinet/ip.h>, however,
    we need it for hardware checksumming support.  FYI... some of
    the definitions in <netinet/ip.h> conflict/duplicate those in
@@ -548,7 +544,6 @@
 static int __init hamachi_init_one (struct pci_dev *pdev,
 				    const struct pci_device_id *ent)
 {
-	static int did_version;			/* Already printed version info. */
 	struct hamachi_private *hmp;
 	int option, i, rx_int_var, tx_int_var, boguscnt;
 	int chip_id = ent->driver_data;
@@ -557,8 +552,12 @@
 	static int card_idx;
 	struct net_device *dev;
 
-	if (hamachi_debug > 0  &&  did_version++ == 0)
+/* when built into the kernel, we only print version if device is found */
+#ifndef MODULE
+	static int printed_version;
+	if (!printed_version++)
 		printk(version);
+#endif
 
 	if (pci_enable_device(pdev))
 		return -EIO;
@@ -1887,6 +1886,10 @@
 
 static int __init hamachi_init (void)
 {
+/* when a module, this is printed whether or not devices are found in probe */
+#ifdef MODULE
+	printk(version);
+#endif
 	if (pci_register_driver(&hamachi_driver) > 0)
 		return 0;
 	pci_unregister_driver(&hamachi_driver);
diff -urN linux-2.4.4-pre5/drivers/net/natsemi.c linux/drivers/net/natsemi.c
--- linux-2.4.4-pre5/drivers/net/natsemi.c	Mon Dec  3 22:36:14 2001
+++ linux/drivers/net/natsemi.c	Mon Dec  3 22:36:25 2001
@@ -37,13 +37,6 @@
 
 */
 
-/* These identify the driver base version and may not be removed. */
-static const char version1[] =
-"natsemi.c:v1.07 1/9/2001  Written by Donald Becker <becker@scyld.com>\n";
-static const char version2[] =
-"  http://www.scyld.com/network/natsemi.html\n";
-static const char version3[] =
-"  (unofficial 2.4.x kernel port, version 1.0.4, February 26, 2001 Jeff Garzik, Tjeerd Mulder)\n";
 /* Updated to recommendations in pci-skeleton v2.03. */
 
 /* Automatically extracted configuration info:
@@ -125,6 +118,12 @@
 #include <asm/bitops.h>
 #include <asm/io.h>
 
+/* These identify the driver base version and may not be removed. */
+static char version[] __devinitdata =
+KERN_INFO "natsemi.c:v1.07 1/9/2001  Written by Donald Becker <becker@scyld.com>\n"
+KERN_INFO "  http://www.scyld.com/network/natsemi.html\n"
+KERN_INFO "  (unofficial 2.4.x kernel port, version 1.0.5, April 17, 2001 Jeff Garzik, Tjeerd Mulder)\n";
+
 /* Condensed operations for readability. */
 #define virt_to_le32desc(addr)  cpu_to_le32(virt_to_bus(addr))
 #define le32desc_to_virt(addr)  bus_to_virt(le32_to_cpu(addr))
@@ -368,15 +367,17 @@
 	struct netdev_private *np;
 	int i, option, irq, chip_idx = ent->driver_data;
 	static int find_cnt = -1;
-	static int printed_version;
 	unsigned long ioaddr, iosize;
 	const int pcibar = 1; /* PCI base address register */
 	int prev_eedata;
 	u32 tmp;
 
-	if ((debug <= 1) && !printed_version++)
-		printk(KERN_INFO "%s" KERN_INFO "%s" KERN_INFO "%s",
-			version1, version2, version3);
+/* when built into the kernel, we only print version if device is found */
+#ifndef MODULE
+	static int printed_version;
+	if (!printed_version++)
+		printk(version);
+#endif
 
 	i = pci_enable_device(pdev);
 	if (i) return i;
@@ -1250,9 +1251,10 @@
 
 static int __init natsemi_init_mod (void)
 {
-	if (debug > 1)
-		printk(KERN_INFO "%s" KERN_INFO "%s" KERN_INFO "%s",
-			version1, version2, version3);
+/* when a module, this is printed whether or not devices are found in probe */
+#ifdef MODULE
+	printk(version);
+#endif
 
 	return pci_module_init (&natsemi_driver);
 }
diff -urN linux-2.4.4-pre5/drivers/net/ne2k-pci.c linux/drivers/net/ne2k-pci.c
--- linux-2.4.4-pre5/drivers/net/ne2k-pci.c	Sun Mar 25 18:24:31 2001
+++ linux/drivers/net/ne2k-pci.c	Mon Dec  3 22:36:25 2001
@@ -25,12 +25,6 @@
 	Limited full-duplex support.
 */
 
-/* These identify the driver base version and may not be removed. */
-static const char version1[] =
-"ne2k-pci.c:v1.02 10/19/2000 D. Becker/P. Gortmaker\n";
-static const char version2[] =
-"  http://www.scyld.com/network/ne2k-pci.html\n";
-
 /* The user-configurable values.
    These may be modified when a driver module is loaded.*/
 
@@ -60,6 +54,11 @@
 #include <linux/etherdevice.h>
 #include "8390.h"
 
+/* These identify the driver base version and may not be removed. */
+static char version[] __devinitdata =
+KERN_INFO "ne2k-pci.c:v1.02 10/19/2000 D. Becker/P. Gortmaker\n"
+KERN_INFO "  http://www.scyld.com/network/ne2k-pci.html\n";
+
 #if defined(__powerpc__)
 #define inl_le(addr)  le32_to_cpu(inl(addr))
 #define inw_le(addr)  le16_to_cpu(inw(addr))
@@ -199,8 +198,14 @@
 	long ioaddr;
 	int flags = pci_clone_list[chip_idx].flags;
 
-	if (fnd_cnt++ == 0)
-		printk(KERN_INFO "%s" KERN_INFO "%s", version1, version2);
+/* when built into the kernel, we only print version if device is found */
+#ifndef MODULE
+	static int printed_version;
+	if (!printed_version++)
+		printk(version);
+#endif
+
+	fnd_cnt++;
 
 	i = pci_enable_device (pdev);
 	if (i)
@@ -593,6 +598,10 @@
 
 static int __init ne2k_pci_init(void)
 {
+/* when a module, this is printed whether or not devices are found in probe */
+#ifdef MODULE
+	printk(version);
+#endif
 	return pci_module_init (&ne2k_driver);
 }
 
diff -urN linux-2.4.4-pre5/drivers/net/pci-skeleton.c linux/drivers/net/pci-skeleton.c
--- linux-2.4.4-pre5/drivers/net/pci-skeleton.c	Mon Dec  3 22:36:14 2001
+++ linux/drivers/net/pci-skeleton.c	Mon Dec  3 22:36:25 2001
@@ -83,13 +83,6 @@
 
 IVc. Errata
 
-1) The RTL-8139 has a serious problem with motherboards which do
-posted MMIO writes to PCI space.  This driver works around the
-problem by having an MMIO  register write be immediately followed by
-an MMIO register read.
-
-2) The RTL-8129 is only supported in Donald Becker's rtl8139 driver.
-
 */
 
 #include <linux/module.h>
@@ -102,12 +95,14 @@
 #include <linux/delay.h>
 #include <asm/io.h>
 
-
 #define NETDRV_VERSION		"1.0.0"
 #define MODNAME			"netdrv"
 #define NETDRV_DRIVER_LOAD_MSG	"MyVendor Fast Ethernet driver " NETDRV_VERSION " loaded"
 #define PFX			MODNAME ": "
 
+static char version[] __devinitdata =
+KERN_INFO NETDRV_DRIVER_LOAD_MSG "\n"
+KERN_INFO "  Support available from http://foo.com/bar/baz.html\n";
 
 /* define to 1 to enable PIO instead of MMIO */
 #undef USE_IO_OPS
@@ -740,9 +735,15 @@
 	int i, addr_len, option;
 	void *ioaddr = NULL;
 	static int board_idx = -1;
-	static int printed_version;
 	u8 tmp;
 
+/* when built into the kernel, we only print version if device is found */
+#ifndef MODULE
+	static int printed_version;
+	if (!printed_version++)
+		printk(version);
+#endif
+
 	DPRINTK ("ENTER\n");
 
 	assert (pdev != NULL);
@@ -750,11 +751,6 @@
 
 	board_idx++;
 
-	if (!printed_version) {
-		printk (KERN_INFO NETDRV_DRIVER_LOAD_MSG "\n");
-		printed_version = 1;
-	}
-
 	i = netdrv_init_board (pdev, &dev, &ioaddr);
 	if (i < 0) {
 		DPRINTK ("EXIT, returning %d\n", i);
@@ -1973,6 +1969,10 @@
 
 static int __init netdrv_init_module (void)
 {
+/* when a module, this is printed whether or not devices are found in probe */
+#ifdef MODULE
+	printk(version);
+#endif
 	return pci_module_init (&netdrv_pci_driver);
 }
 
diff -urN linux-2.4.4-pre5/drivers/net/pcmcia/pcnet_cs.c linux/drivers/net/pcmcia/pcnet_cs.c
--- linux-2.4.4-pre5/drivers/net/pcmcia/pcnet_cs.c	Fri Mar  2 11:02:15 2001
+++ linux/drivers/net/pcmcia/pcnet_cs.c	Mon Dec  3 22:36:25 2001
@@ -195,6 +195,7 @@
     { /* SCM Ethernet */ 0x0ff0, 0x00, 0x20, 0xcb, 0 },
     { /* Socket EA */ 0x4000, 0x00, 0xc0, 0x1b,
       DELAY_OUTPUT | HAS_MISC_REG | USE_BIG_BUF },
+    { /* Socket LP-E CF+ */ 0x01c0, 0x00, 0xc0, 0x1b, 0 },
     { /* SuperSocket RE450T */ 0x0110, 0x00, 0xe0, 0x98, 0 },
     { /* Volktek NPL-402CT */ 0x0060, 0x00, 0x40, 0x05, 0 },
     { /* NEC PC-9801N-J12 */ 0x0ff0, 0x00, 0x00, 0x4c, 0 },
diff -urN linux-2.4.4-pre5/drivers/net/pcnet32.c linux/drivers/net/pcnet32.c
--- linux-2.4.4-pre5/drivers/net/pcnet32.c	Sun Mar 25 18:24:31 2001
+++ linux/drivers/net/pcnet32.c	Mon Dec  3 22:36:25 2001
@@ -535,13 +535,12 @@
     pcnet32_dwio_reset(ioaddr);
     pcnet32_wio_reset(ioaddr);
 
-    /* Important to do the check for dwio mode first. */
-    if (pcnet32_dwio_read_csr(ioaddr, 0) == 4 && pcnet32_dwio_check(ioaddr)) {
-        a = &pcnet32_dwio;
+    /* NOTE: 16-bit check is first, otherwise some older PCnet chips fail */
+    if (pcnet32_wio_read_csr (ioaddr, 0) == 4 && pcnet32_wio_check (ioaddr)) {
+	a = &pcnet32_wio;
     } else {
-        if (pcnet32_wio_read_csr(ioaddr, 0) == 4 && 
-	    pcnet32_wio_check(ioaddr)) {
-	    a = &pcnet32_wio;
+	if (pcnet32_dwio_read_csr (ioaddr, 0) == 4 && pcnet32_dwio_check(ioaddr)) {
+	    a = &pcnet32_dwio;
 	} else
 	    return -ENODEV;
     }
@@ -653,7 +652,13 @@
 	    promaddr[i] = inb(ioaddr + i);
 	}
 	if( memcmp( promaddr, dev->dev_addr, 6) )
-	    printk(" warning: PROM address does not match CSR address");
+	{
+	    printk(" warning PROM address does not match CSR address");
+#if defined(__i386__)
+	    printk(KERN_WARNING "%s: Probably a Compaq, using the PROM address of", dev->name);
+	    memcpy(dev->dev_addr, promaddr, 6);
+#endif
+	}	    	    
     }
     /* if the ethernet address is not valid, force to 00:00:00:00:00:00 */
     if( !is_valid_ether_addr(dev->dev_addr) )
diff -urN linux-2.4.4-pre5/drivers/net/ppp_async.c linux/drivers/net/ppp_async.c
--- linux-2.4.4-pre5/drivers/net/ppp_async.c	Tue Feb 13 13:15:05 2001
+++ linux/drivers/net/ppp_async.c	Mon Dec  3 22:36:25 2001
@@ -244,11 +244,6 @@
 		err = 0;
 		break;
 
-	case PPPIOCATTACH:
-	case PPPIOCDETACH:
-		err = ppp_channel_ioctl(&ap->chan, cmd, arg);
-		break;
-
 	default:
 		err = -ENOIOCTLCMD;
 	}
diff -urN linux-2.4.4-pre5/drivers/net/ppp_generic.c linux/drivers/net/ppp_generic.c
--- linux-2.4.4-pre5/drivers/net/ppp_generic.c	Mon Dec  3 22:36:14 2001
+++ linux/drivers/net/ppp_generic.c	Mon Dec  3 22:36:25 2001
@@ -19,7 +19,7 @@
  * PPP driver, written by Michael Callahan and Al Longyear, and
  * subsequently hacked by Paul Mackerras.
  *
- * ==FILEVERSION 20000417==
+ * ==FILEVERSION 20000902==
  */
 
 #include <linux/config.h>
@@ -32,6 +32,7 @@
 #include <linux/netdevice.h>
 #include <linux/poll.h>
 #include <linux/ppp_defs.h>
+#include <linux/filter.h>
 #include <linux/if_ppp.h>
 #include <linux/ppp_channel.h>
 #include <linux/ppp-comp.h>
@@ -121,6 +122,10 @@
 	struct sk_buff_head mrq;	/* MP: receive reconstruction queue */
 #endif /* CONFIG_PPP_MULTILINK */
 	struct net_device_stats stats;	/* statistics */
+#ifdef CONFIG_PPP_FILTER
+	struct sock_fprog pass_filter;	/* filter for packets to pass */
+	struct sock_fprog active_filter;/* filter for pkts to reset idle */
+#endif /* CONFIG_PPP_FILTER */
 };
 
 /*
@@ -621,6 +626,43 @@
 		err = 0;
 		break;
 
+#ifdef CONFIG_PPP_FILTER
+	case PPPIOCSPASS:
+	case PPPIOCSACTIVE:
+	{
+		struct sock_fprog uprog, *filtp;
+		struct sock_filter *code = NULL;
+		int len;
+
+		if (copy_from_user(&uprog, (void *) arg, sizeof(uprog)))
+			break;
+		if (uprog.len > 0) {
+			err = -ENOMEM;
+			len = uprog.len * sizeof(struct sock_filter);
+			code = kmalloc(len, GFP_KERNEL);
+			if (code == 0)
+				break;
+			err = -EFAULT;
+			if (copy_from_user(code, uprog.filter, len))
+				break;
+			err = sk_chk_filter(code, uprog.len);
+			if (err) {
+				kfree(code);
+				break;
+			}
+		}
+		filtp = (cmd == PPPIOCSPASS)? &ppp->pass_filter: &ppp->active_filter;
+		ppp_lock(ppp);
+		if (filtp->filter)
+			kfree(filtp->filter);
+		filtp->filter = code;
+		filtp->len = uprog.len;
+		ppp_unlock(ppp);
+		err = 0;
+		break;
+	}
+#endif /* CONFIG_PPP_FILTER */
+
 #ifdef CONFIG_PPP_MULTILINK
 	case PPPIOCSMRRU:
 		if (get_user(val, (int *) arg))
@@ -890,6 +932,33 @@
 	int len;
 	unsigned char *cp;
 
+	if (proto < 0x8000) {
+#ifdef CONFIG_PPP_FILTER
+		/* check if we should pass this packet */
+		/* the filter instructions are constructed assuming
+		   a four-byte PPP header on each packet */
+		*skb_push(skb, 2) = 1;
+		if (ppp->pass_filter.filter
+		    && sk_run_filter(skb, ppp->pass_filter.filter,
+				     ppp->pass_filter.len) == 0) {
+			if (ppp->debug & 1) {
+				printk(KERN_DEBUG "PPP: outbound frame not passed\n");
+				kfree_skb(skb);
+				return;
+			}
+		}
+		/* if this packet passes the active filter, record the time */
+		if (!(ppp->active_filter.filter
+		      && sk_run_filter(skb, ppp->active_filter.filter,
+				       ppp->active_filter.len) == 0))
+			ppp->last_xmit = jiffies;
+		skb_pull(skb, 2);
+#else
+		/* for data packets, record the time */
+		ppp->last_xmit = jiffies;
+#endif /* CONFIG_PPP_FILTER */
+	}
+
 	++ppp->stats.tx_packets;
 	ppp->stats.tx_bytes += skb->len - 2;
 
@@ -962,10 +1031,6 @@
 		}
 	}
 
-	/* for data packets, record the time */
-	if (proto < 0x8000)
-		ppp->last_xmit = jiffies;
-
 	/*
 	 * If we are waiting for traffic (demand dialling),
 	 * queue it up for pppd to receive.
@@ -1400,7 +1465,29 @@
 
 	} else {
 		/* network protocol frame - give it to the kernel */
+
+#ifdef CONFIG_PPP_FILTER
+		/* check if the packet passes the pass and active filters */
+		/* the filter instructions are constructed assuming
+		   a four-byte PPP header on each packet */
+		*skb_push(skb, 2) = 0;
+		if (ppp->pass_filter.filter
+		    && sk_run_filter(skb, ppp->pass_filter.filter,
+				     ppp->pass_filter.len) == 0) {
+			if (ppp->debug & 1)
+				printk(KERN_DEBUG "PPP: inbound frame not passed\n");
+			kfree_skb(skb);
+			return;
+		}
+		if (!(ppp->active_filter.filter
+		      && sk_run_filter(skb, ppp->active_filter.filter,
+				       ppp->active_filter.len) == 0))
+			ppp->last_recv = jiffies;
+		skb_pull(skb, 2);
+#else
 		ppp->last_recv = jiffies;
+#endif /* CONFIG_PPP_FILTER */
+
 		if ((ppp->dev->flags & IFF_UP) == 0
 		    || ppp->npmode[npi] != NPMODE_PASS) {
 			kfree_skb(skb);
@@ -1806,70 +1893,6 @@
 }
 
 /*
- * This is basically temporary compatibility stuff.
- */
-ssize_t
-ppp_channel_read(struct ppp_channel *chan, struct file *file,
-		 char *buf, size_t count)
-{
-	struct channel *pch = chan->ppp;
-
-	if (pch == 0)
-		return -ENXIO;
-	return ppp_file_read(&pch->file, file, buf, count);
-}
-
-ssize_t
-ppp_channel_write(struct ppp_channel *chan, const char *buf, size_t count)
-{
-	struct channel *pch = chan->ppp;
-
-	if (pch == 0)
-		return -ENXIO;
-	return ppp_file_write(&pch->file, buf, count);
-}
-
-/* No kernel lock - fine */
-unsigned int
-ppp_channel_poll(struct ppp_channel *chan, struct file *file, poll_table *wait)
-{
-	unsigned int mask;
-	struct channel *pch = chan->ppp;
-
-	mask = POLLOUT | POLLWRNORM;
-	if (pch != 0) {
-		poll_wait(file, &pch->file.rwait, wait);
-		if (skb_peek(&pch->file.rq) != 0)
-			mask |= POLLIN | POLLRDNORM;
-	}
-	return mask;
-}
-
-int ppp_channel_ioctl(struct ppp_channel *chan, unsigned int cmd,
-		      unsigned long arg)
-{
-	struct channel *pch = chan->ppp;
-	int err = -ENOTTY;
-	int unit;
-
-	if (!capable(CAP_NET_ADMIN))
-		return -EPERM;
-	if (pch == 0)
-		return -EINVAL;
-	switch (cmd) {
-	case PPPIOCATTACH:
-		if (get_user(unit, (int *) arg))
-			break;
-		err = ppp_connect_channel(pch, unit);
-		break;
-	case PPPIOCDETACH:
-		err = ppp_disconnect_channel(pch);
-		break;
-	}
-	return err;
-}
-
-/*
  * Compression control.
  */
 
@@ -2192,6 +2215,7 @@
 	ppp->file.index = unit;
 	ppp->mru = PPP_MRU;
 	init_ppp_file(&ppp->file, INTERFACE);
+	ppp->file.hdrlen = PPP_HDRLEN - 2;	/* don't count proto bytes */
 	for (i = 0; i < NUM_NP; ++i)
 		ppp->npmode[i] = NPMODE_PASS;
 	INIT_LIST_HEAD(&ppp->channels);
@@ -2263,6 +2287,16 @@
 #ifdef CONFIG_PPP_MULTILINK
 	skb_queue_purge(&ppp->mrq);
 #endif /* CONFIG_PPP_MULTILINK */
+#ifdef CONFIG_PPP_FILTER
+	if (ppp->pass_filter.filter) {
+		kfree(ppp->pass_filter.filter);
+		ppp->pass_filter.filter = NULL;
+	}
+	if (ppp->active_filter.filter) {
+		kfree(ppp->active_filter.filter);
+		ppp->active_filter.filter = 0;
+	}
+#endif /* CONFIG_PPP_FILTER */
 	dev = ppp->dev;
 	ppp->dev = 0;
 	ppp_unlock(ppp);
@@ -2429,9 +2463,5 @@
 EXPORT_SYMBOL(ppp_output_wakeup);
 EXPORT_SYMBOL(ppp_register_compressor);
 EXPORT_SYMBOL(ppp_unregister_compressor);
-EXPORT_SYMBOL(ppp_channel_read);
-EXPORT_SYMBOL(ppp_channel_write);
-EXPORT_SYMBOL(ppp_channel_poll);
-EXPORT_SYMBOL(ppp_channel_ioctl);
 EXPORT_SYMBOL(all_ppp_units); /* for debugging */
 EXPORT_SYMBOL(all_channels); /* for debugging */
diff -urN linux-2.4.4-pre5/drivers/net/ppp_synctty.c linux/drivers/net/ppp_synctty.c
--- linux-2.4.4-pre5/drivers/net/ppp_synctty.c	Tue Feb 13 13:15:05 2001
+++ linux/drivers/net/ppp_synctty.c	Mon Dec  3 22:36:25 2001
@@ -233,31 +233,25 @@
 }
 
 /*
- * Read a PPP frame, for compatibility until pppd is updated.
+ * Read does nothing - no data is ever available this way.
+ * Pppd reads and writes packets via /dev/ppp instead.
  */
 static ssize_t
 ppp_sync_read(struct tty_struct *tty, struct file *file,
 	       unsigned char *buf, size_t count)
 {
-	struct syncppp *ap = tty->disc_data;
-
-	if (ap == 0)
-		return -ENXIO;
-	return ppp_channel_read(&ap->chan, file, buf, count);
+	return -EAGAIN;
 }
 
 /*
- * Write a ppp frame, for compatibility until pppd is updated.
+ * Write on the tty does nothing, the packets all come in
+ * from the ppp generic stuff.
  */
 static ssize_t
 ppp_sync_write(struct tty_struct *tty, struct file *file,
 		const unsigned char *buf, size_t count)
 {
-	struct syncppp *ap = tty->disc_data;
-
-	if (ap == 0)
-		return -ENXIO;
-	return ppp_channel_write(&ap->chan, buf, count);
+	return -EAGAIN;
 }
 
 static int
@@ -308,30 +302,6 @@
 		err = 0;
 		break;
 
-	/*
-	 * Compatibility calls until pppd is updated.
-	 */
-	case PPPIOCGFLAGS:
-	case PPPIOCSFLAGS:
-	case PPPIOCGASYNCMAP:
-	case PPPIOCSASYNCMAP:
-	case PPPIOCGRASYNCMAP:
-	case PPPIOCSRASYNCMAP:
-	case PPPIOCGXASYNCMAP:
-	case PPPIOCSXASYNCMAP:
-	case PPPIOCGMRU:
-	case PPPIOCSMRU:
-		err = -EPERM;
-		if (!capable(CAP_NET_ADMIN))
-			break;
-		err = ppp_sync_ioctl(&ap->chan, cmd, arg);
-		break;
-
-	case PPPIOCATTACH:
-	case PPPIOCDETACH:
-		err = ppp_channel_ioctl(&ap->chan, cmd, arg);
-		break;
-
 	default:
 		err = -ENOIOCTLCMD;
 	}
@@ -343,16 +313,7 @@
 static unsigned int
 ppp_sync_poll(struct tty_struct *tty, struct file *file, poll_table *wait)
 {
-	struct syncppp *ap = tty->disc_data;
-	unsigned int mask;
-
-	mask = POLLOUT | POLLWRNORM;
-	/* compatibility for old pppd */
-	if (ap != 0)
-		mask |= ppp_channel_poll(&ap->chan, file, wait);
-	if (test_bit(TTY_OTHER_CLOSED, &tty->flags) || tty_hung_up_p(file))
-		mask |= POLLHUP;
-	return mask;
+	return 0;
 }
 
 static int
diff -urN linux-2.4.4-pre5/drivers/net/sis900.c linux/drivers/net/sis900.c
--- linux-2.4.4-pre5/drivers/net/sis900.c	Mon Dec  3 22:36:14 2001
+++ linux/drivers/net/sis900.c	Mon Dec  3 22:36:25 2001
@@ -47,7 +47,7 @@
 #include <linux/timer.h>
 #include <linux/errno.h>
 #include <linux/ioport.h>
-#include <linux/malloc.h>
+#include <linux/slab.h>
 #include <linux/interrupt.h>
 #include <linux/pci.h>
 #include <linux/netdevice.h>
@@ -62,8 +62,8 @@
 
 #include "sis900.h"
 
-static const char *version =
-"sis900.c: v1.07.11  4/10/2001\n";
+static char version[] __devinitdata =
+KERN_INFO "sis900.c: v1.07.11  4/10/2001\n";
 
 static int max_interrupt_work = 40;
 static int multicast_filter_limit = 128;
@@ -308,6 +308,13 @@
 	u8 revision;
 	char *card_name = card_names[pci_id->driver_data];
 
+/* when built into the kernel, we only print version if device is found */
+#ifndef MODULE
+	static int printed_version;
+	if (!printed_version++)
+		printk(version);
+#endif
+
 	/* setup various bits in PCI command register */
 	ret = pci_enable_device(pci_dev);
 	if(ret) return ret;
@@ -1997,7 +2004,10 @@
 
 static int __init sis900_init_module(void)
 {
-	printk(KERN_INFO "%s", version);
+/* when a module, this is printed whether or not devices are found in probe */
+#ifdef MODULE
+	printk(version);
+#endif
 
 	return pci_module_init(&sis900_pci_driver);
 }
diff -urN linux-2.4.4-pre5/drivers/net/starfire.c linux/drivers/net/starfire.c
--- linux-2.4.4-pre5/drivers/net/starfire.c	Mon Dec  3 22:36:15 2001
+++ linux/drivers/net/starfire.c	Mon Dec  3 22:36:25 2001
@@ -36,15 +36,6 @@
 	- Merge Becker version 1.03
 */
 
-/* These identify the driver base version and may not be removed. */
-static const char version1[] =
-"starfire.c:v1.03 7/26/2000  Written by Donald Becker <becker@scyld.com>\n";
-static const char version2[] =
-" Updates and info at http://www.scyld.com/network/starfire.html\n";
-
-static const char version3[] =
-" (unofficial 2.4.x kernel port, version 1.1.4, August 10, 2000)\n";
-
 /* The user-configurable values.
    These may be modified when a driver module is loaded.*/
 
@@ -123,6 +114,12 @@
 #include <asm/bitops.h>
 #include <asm/io.h>
 
+/* These identify the driver base version and may not be removed. */
+static char version[] __devinitdata =
+KERN_INFO "starfire.c:v1.03 7/26/2000  Written by Donald Becker <becker@scyld.com>\n"
+KERN_INFO " Updates and info at http://www.scyld.com/network/starfire.html\n"
+KERN_INFO " (unofficial 2.4.x kernel port, version 1.1.4a, April 17, 2001)\n";
+
 MODULE_AUTHOR("Donald Becker <becker@scyld.com>");
 MODULE_DESCRIPTION("Adaptec Starfire Ethernet driver");
 MODULE_PARM(max_interrupt_work, "i");
@@ -395,17 +392,19 @@
 	int i, irq, option, chip_idx = ent->driver_data;
 	struct net_device *dev;
 	static int card_idx = -1;
-	static int printed_version;
 	long ioaddr;
 	int drv_flags, io_size = netdrv_tbl[chip_idx].io_size;
 
+/* when built into the kernel, we only print version if device is found */
+#ifndef MODULE
+	static int printed_version;
+	if (!printed_version++)
+		printk(version);
+#endif
+
 	card_idx++;
 	option = card_idx < MAX_UNITS ? options[card_idx] : 0;
 
-	if (!printed_version++)
-		printk(KERN_INFO "%s" KERN_INFO "%s" KERN_INFO "%s",
-		       version1, version2, version3);
-
 	if (pci_enable_device (pdev))
 		return -EIO;
 
@@ -1390,6 +1389,10 @@
 
 static int __init starfire_init (void)
 {
+/* when a module, this is printed whether or not devices are found in probe */
+#ifdef MODULE
+	printk(version);
+#endif
 	return pci_module_init (&starfire_driver);
 }
 
diff -urN linux-2.4.4-pre5/drivers/net/sunbmac.c linux/drivers/net/sunbmac.c
--- linux-2.4.4-pre5/drivers/net/sunbmac.c	Mon Dec  3 22:36:15 2001
+++ linux/drivers/net/sunbmac.c	Mon Dec  3 22:36:25 2001
@@ -910,7 +910,10 @@
 		return ret;
 	}
 	init_timer(&bp->bigmac_timer);
-	return bigmac_init(bp, 0);
+	ret = bigmac_init(bp, 0);
+	if (ret)
+		free_irq(dev->irq, bp);
+	return ret;
 }
 
 static int bigmac_close(struct net_device *dev)
@@ -923,7 +926,7 @@
 
 	bigmac_stop(bp);
 	bigmac_clean_rings(bp);
-	free_irq(dev->irq, (void *)bp);
+	free_irq(dev->irq, bp);
 	return 0;
 }
 
@@ -1249,7 +1252,7 @@
 	struct net_device *dev = NULL;
 	struct sbus_bus *sbus;
 	struct sbus_dev *sdev = 0;
-	static int called = 0;
+	static int called;
 	int cards = 0, v;
 
 	root_bigmac_dev = NULL;
diff -urN linux-2.4.4-pre5/drivers/net/sundance.c linux/drivers/net/sundance.c
--- linux-2.4.4-pre5/drivers/net/sundance.c	Mon Dec  3 22:36:15 2001
+++ linux/drivers/net/sundance.c	Mon Dec  3 22:36:25 2001
@@ -18,12 +18,6 @@
 	http://www.scyld.com/network/sundance.html
 */
 
-/* These identify the driver base version and may not be removed. */
-static const char version1[] =
-"sundance.c:v1.01 4/09/00  Written by Donald Becker\n";
-static const char version2[] =
-"  http://www.scyld.com/network/sundance.html\n";
-
 /* The user-configurable values.
    These may be modified when a driver module is loaded.*/
 
@@ -97,6 +91,10 @@
 
 #include <linux/spinlock.h>
 
+/* These identify the driver base version and may not be removed. */
+static char version[] __devinitdata =
+KERN_INFO "sundance.c:v1.01 4/09/00  Written by Donald Becker\n"
+KERN_INFO "  http://www.scyld.com/network/sundance.html\n";
 
 /* Condensed operations for readability. */
 #define virt_to_le32desc(addr)  cpu_to_le32(virt_to_bus(addr))
@@ -384,6 +382,13 @@
 	int i, option = card_idx < MAX_UNITS ? options[card_idx] : 0;
 	long ioaddr;
 
+/* when built into the kernel, we only print version if device is found */
+#ifndef MODULE
+	static int printed_version;
+	if (!printed_version++)
+		printk(version);
+#endif
+
 	if (pci_enable_device(pdev))
 		return -EIO;
 	pci_set_master(pdev);
@@ -404,7 +409,7 @@
 	ioaddr = pci_resource_start(pdev, 1);
 	ioaddr = (long) ioremap (ioaddr, pci_id_tbl[chip_idx].io_size);
 	if (!ioaddr)
-		goto err_out_iomem;
+		goto err_out_res;
 #endif
 
 	for (i = 0; i < 3; i++)
@@ -445,6 +450,7 @@
 	dev->do_ioctl = &mii_ioctl;
 	dev->tx_timeout = &tx_timeout;
 	dev->watchdog_timeo = TX_TIMEOUT;
+	pci_set_drvdata(pdev, dev);
 
 	if (mtu)
 		dev->mtu = mtu;
@@ -493,7 +499,7 @@
 	pci_set_drvdata(pdev, NULL);
 #ifndef USE_IO_OPS
 	iounmap((void *)ioaddr);
-err_out_iomem:
+err_out_res:
 #endif
 	pci_release_regions(pdev);
 err_out_netdev:
@@ -1260,6 +1266,10 @@
 
 static int __init sundance_init(void)
 {
+/* when a module, this is printed whether or not devices are found in probe */
+#ifdef MODULE
+	printk(version);
+#endif
 	return pci_module_init(&sundance_driver);
 }
 
diff -urN linux-2.4.4-pre5/drivers/net/tokenring/madgemc.c linux/drivers/net/tokenring/madgemc.c
--- linux-2.4.4-pre5/drivers/net/tokenring/madgemc.c	Mon Dec  3 22:36:15 2001
+++ linux/drivers/net/tokenring/madgemc.c	Mon Dec  3 22:36:25 2001
@@ -17,7 +17,7 @@
  *	16-Jan-00	AF	Created
  *
  */
-static const char *version = "madgemc.c: v0.91 23/01/2000 by Adam Fritzler\n";
+static const char version[] = "madgemc.c: v0.91 23/01/2000 by Adam Fritzler\n";
 
 #include <linux/module.h>
 #include <linux/mca.h>
diff -urN linux-2.4.4-pre5/drivers/net/tokenring/tmsisa.c linux/drivers/net/tokenring/tmsisa.c
--- linux-2.4.4-pre5/drivers/net/tokenring/tmsisa.c	Mon Dec  3 22:36:15 2001
+++ linux/drivers/net/tokenring/tmsisa.c	Mon Dec  3 22:36:25 2001
@@ -19,7 +19,7 @@
  *  TODO:
  *	1. Add support for Proteon TR ISA adapters (1392, 1392+)
  */
-static const char *version = "tmsisa.c: v1.00 14/01/2001 by Jochen Friedrich\n";
+static const char version[] = "tmsisa.c: v1.00 14/01/2001 by Jochen Friedrich\n";
 
 #include <linux/module.h>
 #include <linux/kernel.h>
@@ -60,7 +60,7 @@
 	0
 };
 
-static char *isa_cardname = "SK NET TR 4/16 ISA\0";
+static char isa_cardname[] = "SK NET TR 4/16 ISA\0";
 
 int tms_isa_probe(struct net_device *dev);
 static int tms_isa_open(struct net_device *dev);
@@ -271,6 +271,14 @@
 	{
 		/* Enlist in the card list */
 		card = kmalloc(sizeof(struct tms_isa_card), GFP_KERNEL);
+		if (!card) {
+			unregister_trdev(dev);
+			release_region(dev->base_addr, TMS_ISA_IO_EXTENT); 
+			free_irq(dev->irq, dev);
+			free_dma(dev->dma);
+			tmsdev_term(dev);
+			return -1;
+		}
 		card->next = tms_isa_card_list;
 		tms_isa_card_list = card;
 		card->dev = dev;
diff -urN linux-2.4.4-pre5/drivers/net/tulip/ChangeLog linux/drivers/net/tulip/ChangeLog
--- linux-2.4.4-pre5/drivers/net/tulip/ChangeLog	Mon Dec  3 22:36:15 2001
+++ linux/drivers/net/tulip/ChangeLog	Mon Dec  3 22:36:25 2001
@@ -1,5 +1,30 @@
 2001-04-03  Jeff Garzik  <jgarzik@mandrakesoft.com>
 
+	* tulip_core.c: Now that dev->name is only available late
+	  in the probe, insert a hack to replace a not-evaluated
+	  "eth%d" string with an evaluated "tulip%d" string.
+	  Also, remove obvious comment and an indentation cleanup.
+
+2001-04-03  Jeff Garzik  <jgarzik@mandrakesoft.com>
+
+	* tulip_core.c: If we are a module, always print out the
+	  version string.  If we are built into the kernel, only print
+	  the version string if at least one tulip is detected.
+
+2001-04-03  Jeff Garzik  <jgarzik@mandrakesoft.com>
+
+	Merged from Becker's tulip.c 0.92t:
+
+	* tulip_core.c: Add support for Conexant LANfinity.
+
+2001-04-03  Jeff Garzik  <jgarzik@mandrakesoft.com>
+
+	* tulip_core.c: Only suspend/resume if the interface
+	  is up and running.  Use alloc_etherdev and pci_request_regions.
+	  Spelling fix.
+
+2001-04-03  Jeff Garzik  <jgarzik@mandrakesoft.com>
+
 	* tulip_core.c: Remove code that existed when one or more of
 	  the following defines existed.  These defines were never used
 	  by normal users in practice: TULIP_FULL_DUPLEX,
diff -urN linux-2.4.4-pre5/drivers/net/tulip/interrupt.c linux/drivers/net/tulip/interrupt.c
--- linux-2.4.4-pre5/drivers/net/tulip/interrupt.c	Mon Dec  3 22:36:15 2001
+++ linux/drivers/net/tulip/interrupt.c	Mon Dec  3 22:36:25 2001
@@ -321,7 +321,19 @@
 					(tp->link_change)(dev, csr5);
 			}
 			if (csr5 & SytemError) {
-				printk(KERN_ERR "%s: (%lu) System Error occured\n", dev->name, tp->nir);
+				int error = (csr5 >> 23) & 7;
+				/* oops, we hit a PCI error.  The code produced corresponds
+				 * to the reason:
+				 *  0 - parity error
+				 *  1 - master abort
+				 *  2 - target abort
+				 * Note that on parity error, we should do a software reset
+				 * of the chip to get it back into a sane state (according
+				 * to the 21142/3 docs that is).
+				 *   -- rmk
+				 */
+				printk(KERN_ERR "%s: (%lu) System Error occured (%d)\n",
+					dev->name, tp->nir, error);
 			}
 			/* Clear all error sources, included undocumented ones! */
 			outl(0x0800f7ba, ioaddr + CSR5);
diff -urN linux-2.4.4-pre5/drivers/net/tulip/tulip.h linux/drivers/net/tulip/tulip.h
--- linux-2.4.4-pre5/drivers/net/tulip/tulip.h	Mon Dec  3 22:36:15 2001
+++ linux/drivers/net/tulip/tulip.h	Mon Dec  3 22:36:25 2001
@@ -83,6 +83,7 @@
 	COMPEX9881,
 	I21145,
 	DM910X,
+	CONEXANT,
 };
 
 
diff -urN linux-2.4.4-pre5/drivers/net/tulip/tulip_core.c linux/drivers/net/tulip/tulip_core.c
--- linux-2.4.4-pre5/drivers/net/tulip/tulip_core.c	Mon Dec  3 22:36:15 2001
+++ linux/drivers/net/tulip/tulip_core.c	Mon Dec  3 22:36:25 2001
@@ -23,7 +23,7 @@
 #include <asm/unaligned.h>
 
 static char version[] __devinitdata =
-	"Linux Tulip driver version 0.9.14d (April 3, 2001)\n";
+	"Linux Tulip driver version 0.9.14e (April 20, 2001)\n";
 
 
 /* A few user-configurable values. */
@@ -175,6 +175,11 @@
   { "Davicom DM9102/DM9102A", 128, 0x0001ebef,
 	HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM | HAS_ACPI,
 	tulip_timer },
+
+  /* CONEXANT */
+  { "Conexant LANfinity", 0x100, 0x0001ebef,
+	HAS_MII | HAS_ACPI,
+	tulip_timer },
 };
 
 
@@ -203,7 +208,8 @@
 	{ 0x1113, 0x1216, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET },
 	{ 0x1113, 0x1217, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MX98715 },
 	{ 0x1113, 0x9511, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET },
-	{0, }
+	{ 0x14f1, 0x1803, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CONEXANT },
+	{ } /* terminate list */
 };
 MODULE_DEVICE_TABLE(pci, tulip_pci_tbl);
 
@@ -1117,12 +1123,11 @@
 static int __devinit tulip_init_one (struct pci_dev *pdev,
 				     const struct pci_device_id *ent)
 {
-	static int did_version;			/* Already printed version info. */
 	struct tulip_private *tp;
 	/* See note below on the multiport cards. */
 	static unsigned char last_phys_addr[6] = {0x00, 'L', 'i', 'n', 'u', 'x'};
 	static int last_irq;
-	static int multiport_cnt;		/* For four-port boards w/one EEPROM */
+	static int multiport_cnt;	/* For four-port boards w/one EEPROM */
 	u8 chip_rev;
 	int i, irq;
 	unsigned short sum;
@@ -1131,11 +1136,16 @@
 	long ioaddr;
 	static int board_idx = -1;
 	int chip_idx = ent->driver_data;
+	int t2104x_mode = 0;
+	int eeprom_missing = 0;
 
-	board_idx++;
-
+#ifndef MODULE
+	static int did_version;		/* Already printed version info. */
 	if (tulip_debug > 0  &&  did_version++ == 0)
 		printk (KERN_INFO "%s", version);
+#endif
+
+	board_idx++;
 
 	/*
 	 *	Lan media wire a tulip chip to a wan interface. Needs a very
@@ -1195,7 +1205,7 @@
 	irq = pdev->irq;
 
 	/* init_etherdev ensures aligned and zeroed private structures */
-	dev = init_etherdev (NULL, sizeof (*tp));
+	dev = alloc_etherdev (sizeof (*tp));
 	if (!dev) {
 		printk (KERN_ERR PFX "ether device alloc failed, aborting\n");
 		return -ENOMEM;
@@ -1203,36 +1213,23 @@
 
 	if (pci_resource_len (pdev, 0) < tulip_tbl[chip_idx].io_size) {
 		printk (KERN_ERR PFX "%s: I/O region (0x%lx@0x%lx) too small, "
-			"aborting\n", dev->name, pci_resource_len (pdev, 0),
+			"aborting\n", pdev->slot_name,
+			pci_resource_len (pdev, 0),
 			pci_resource_start (pdev, 0));
 		goto err_out_free_netdev;
 	}
 
 	/* grab all resources from both PIO and MMIO regions, as we
 	 * don't want anyone else messing around with our hardware */
-	if (!request_region (pci_resource_start (pdev, 0),
-			     pci_resource_len (pdev, 0),
-			     dev->name)) {
-		printk (KERN_ERR PFX "%s: I/O region (0x%lx@0x%lx) unavailable, "
-			"aborting\n", dev->name, pci_resource_len (pdev, 0),
-			pci_resource_start (pdev, 0));
+	if (pci_request_regions (pdev, "tulip"))
 		goto err_out_free_netdev;
-	}
-	if (!request_mem_region (pci_resource_start (pdev, 1),
-				 pci_resource_len (pdev, 1),
-				 dev->name)) {
-		printk (KERN_ERR PFX "%s: MMIO region (0x%lx@0x%lx) unavailable, "
-			"aborting\n", dev->name, pci_resource_len (pdev, 1),
-			pci_resource_start (pdev, 1));
-		goto err_out_free_pio_res;
-	}
 
 	pci_set_master(pdev);
 
 	pci_read_config_byte (pdev, PCI_REVISION_ID, &chip_rev);
 
 	/*
-	 * initialize priviate data structure 'tp'
+	 * initialize private data structure 'tp'
 	 * it is zeroed and aligned in init_etherdev
 	 */
 	tp = dev->priv;
@@ -1242,7 +1239,7 @@
 					   sizeof(struct tulip_tx_desc) * TX_RING_SIZE,
 					   &tp->rx_ring_dma);
 	if (!tp->rx_ring)
-		goto err_out_free_mmio_res;
+		goto err_out_free_res;
 	tp->tx_ring = (struct tulip_tx_desc *)(tp->rx_ring + RX_RING_SIZE);
 	tp->tx_ring_dma = tp->rx_ring_dma + sizeof(struct tulip_rx_desc) * RX_RING_SIZE;
 
@@ -1257,10 +1254,6 @@
 
 	dev->base_addr = ioaddr;
 	dev->irq = irq;
-	pci_set_drvdata(pdev, dev);
-
-	printk(KERN_INFO "%s: %s rev %d at %#3lx,",
-		   dev->name, tulip_tbl[chip_idx].chip_name, chip_rev, ioaddr);
 
 	/* bugfix: the ASIX must have a burst limit or horrible things happen. */
 	if (chip_idx == AX88140) {
@@ -1278,10 +1271,10 @@
 
 	if (chip_idx == DC21041) {
 		if (inl(ioaddr + CSR9) & 0x8000) {
-			printk(" 21040 compatible mode,");
 			chip_idx = DC21040;
+			t2104x_mode = 1;
 		} else {
-			printk(" 21041 mode,");
+			t2104x_mode = 2;
 		}
 	}
 
@@ -1354,7 +1347,7 @@
 	   Many PCI BIOSes also incorrectly report the IRQ line, so we correct
 	   that here as well. */
 	if (sum == 0  || sum == 6*0xff) {
-		printk(" EEPROM not present,");
+		eeprom_missing = 1;
 		for (i = 0; i < 5; i++)
 			dev->dev_addr[i] = last_phys_addr[i];
 		dev->dev_addr[i] = last_phys_addr[i] + 1;
@@ -1365,8 +1358,7 @@
 	}
 
 	for (i = 0; i < 6; i++)
-		printk("%c%2.2X", i ? ':' : ' ', last_phys_addr[i] = dev->dev_addr[i]);
-	printk(", IRQ %d.\n", irq);
+		last_phys_addr[i] = dev->dev_addr[i];
 	last_irq = irq;
 
 	/* The lower four bits are the media type. */
@@ -1382,7 +1374,7 @@
 		tp->default_port = dev->mem_start & MEDIA_MASK;
 	if (tp->default_port) {
 		printk(KERN_INFO "%s: Transceiver selection forced to %s.\n",
-		       dev->name, medianame[tp->default_port & MEDIA_MASK]);
+		       pdev->slot_name, medianame[tp->default_port & MEDIA_MASK]);
 		tp->medialock = 1;
 		if (tulip_media_cap[tp->default_port] & MediaAlwaysFD)
 			tp->full_duplex = 1;
@@ -1398,10 +1390,12 @@
 	else
 		tp->to_advertise = 0x01e1;
 
-	/* This is logically part of _init_one(), but too complex to write inline. */
 	if (tp->flags & HAS_MEDIA_TABLE) {
 		memcpy(tp->eeprom, ee_data, sizeof(tp->eeprom));
+
+		sprintf(dev->name, "tulip%d", board_idx);	/* hack */
 		tulip_parse_eeprom(dev);
+		strcpy(dev->name, "eth%d");			/* un-hack */
 	}
 
 	if ((tp->flags & ALWAYS_CHECK_MII) ||
@@ -1433,15 +1427,15 @@
 				tp->advertising[phy_idx++] = reg4;
 				printk(KERN_INFO "%s:  MII transceiver #%d "
 					   "config %4.4x status %4.4x advertising %4.4x.\n",
-					   dev->name, phy, mii_reg0, mii_status, mii_advert);
+					   pdev->slot_name, phy, mii_reg0, mii_status, mii_advert);
 				/* Fixup for DLink with miswired PHY. */
 				if (mii_advert != reg4) {
 					printk(KERN_DEBUG "%s:  Advertising %4.4x on PHY %d,"
 						   " previously advertising %4.4x.\n",
-						   dev->name, reg4, phy, mii_advert);
+						   pdev->slot_name, reg4, phy, mii_advert);
 					printk(KERN_DEBUG "%s:  Advertising %4.4x (to advertise"
 						   " is %4.4x).\n",
-						   dev->name, reg4, tp->to_advertise);
+						   pdev->slot_name, reg4, tp->to_advertise);
 					tulip_mdio_write(dev, phy, 4, reg4);
 				}
 				/* Enable autonegotiation: some boards default to off. */
@@ -1453,7 +1447,7 @@
 		tp->mii_cnt = phy_idx;
 		if (tp->mtable  &&  tp->mtable->has_mii  &&  phy_idx == 0) {
 			printk(KERN_INFO "%s: ***WARNING***: No MII transceiver found!\n",
-				   dev->name);
+			       pdev->slot_name);
 			tp->phys[0] = 1;
 		}
 	}
@@ -1468,6 +1462,23 @@
 	dev->do_ioctl = private_ioctl;
 	dev->set_multicast_list = set_rx_mode;
 
+	if (register_netdev(dev))
+		goto err_out_mtable;
+
+	printk(KERN_INFO "%s: %s rev %d at %#3lx,",
+	       dev->name, tulip_tbl[chip_idx].chip_name, chip_rev, ioaddr);
+	pci_set_drvdata(pdev, dev);
+
+	if (t2104x_mode == 1)
+		printk(" 21040 compatible mode,");
+	else if (t2104x_mode == 2)
+		printk(" 21041 mode,");
+	if (eeprom_missing)
+		printk(" EEPROM not present,");
+	for (i = 0; i < 6; i++)
+		printk("%c%2.2X", i ? ':' : ' ', dev->dev_addr[i]);
+	printk(", IRQ %d.\n", irq);
+
 	if ((tp->flags & HAS_NWAY)  || tp->chip_id == DC21041)
 		tp->link_change = t21142_lnk_change;
 	else if (tp->flags & HAS_PNICNWAY)
@@ -1535,12 +1546,11 @@
 
 	return 0;
 
-err_out_free_mmio_res:
-	release_mem_region (pci_resource_start (pdev, 1),
-			    pci_resource_len (pdev, 1));
-err_out_free_pio_res:
-	release_region (pci_resource_start (pdev, 0),
-			pci_resource_len (pdev, 0));
+err_out_mtable:
+	if (tp->mtable)
+		kfree (tp->mtable);
+err_out_free_res:
+	pci_release_regions (pdev);
 err_out_free_netdev:
 	unregister_netdev (dev);
 	kfree (dev);
@@ -1552,7 +1562,7 @@
 {
 	struct net_device *dev = pci_get_drvdata(pdev);
 
-	if (dev && netif_device_present (dev)) {
+	if (dev && netif_running (dev) && netif_device_present (dev)) {
 		netif_device_detach (dev);
 		tulip_down (dev);
 		/* pci_power_off(pdev, -1); */
@@ -1564,10 +1574,10 @@
 {
 	struct net_device *dev = pci_get_drvdata(pdev);
 
+	if (dev && netif_running (dev) && !netif_device_present (dev)) {
 #if 1
-	pci_enable_device (pdev);
+		pci_enable_device (pdev);
 #endif
-	if (dev && !netif_device_present (dev)) {
 		/* pci_power_on(pdev); */
 		tulip_up (dev);
 		netif_device_attach (dev);
@@ -1589,14 +1599,10 @@
 			     sizeof (struct tulip_tx_desc) * TX_RING_SIZE,
 			     tp->rx_ring, tp->rx_ring_dma);
 	unregister_netdev (dev);
-	release_mem_region (pci_resource_start (pdev, 1),
-			    pci_resource_len (pdev, 1));
-	release_region (pci_resource_start (pdev, 0),
-			pci_resource_len (pdev, 0));
 	if (tp->mtable)
 		kfree (tp->mtable);
 	kfree (dev);
-
+	pci_release_regions (pdev);
 	pci_set_drvdata (pdev, NULL);
 
 	/* pci_power_off (pdev, -1); */
@@ -1615,6 +1621,10 @@
 
 static int __init tulip_init (void)
 {
+#ifdef MODULE
+	printk (KERN_INFO "%s", version);
+#endif
+
 	/* copy module parms into globals */
 	tulip_rx_copybreak = rx_copybreak;
 	tulip_max_interrupt_work = max_interrupt_work;
diff -urN linux-2.4.4-pre5/drivers/net/via-rhine.c linux/drivers/net/via-rhine.c
--- linux-2.4.4-pre5/drivers/net/via-rhine.c	Mon Dec  3 22:36:15 2001
+++ linux/drivers/net/via-rhine.c	Mon Dec  3 22:36:25 2001
@@ -131,10 +131,9 @@
 #include <asm/irq.h>
 
 /* These identify the driver base version and may not be removed. */
-static char version1[] __devinitdata =
-"via-rhine.c:v1.08b-LK1.1.7  8/9/2000  Written by Donald Becker\n";
-static char version2[] __devinitdata =
-"  http://www.scyld.com/network/via-rhine.html\n";
+static char version[] __devinitdata =
+KERN_INFO "via-rhine.c:v1.08b-LK1.1.8  4/17/2000  Written by Donald Becker\n"
+KERN_INFO "  http://www.scyld.com/network/via-rhine.html\n";
 
 
 
@@ -477,17 +476,17 @@
 	int i, option;
 	int chip_id = (int) ent->driver_data;
 	static int card_idx = -1;
-	static int did_version;
 	long ioaddr;
 	int io_size;
 	int pci_flags;
 	
-	/* print version once and once only */
-	if (! did_version++) {
-		printk (KERN_INFO "%s", version1);
-		printk (KERN_INFO "%s", version2);
-	}
-	
+/* when built into the kernel, we only print version if device is found */
+#ifndef MODULE
+	static int printed_version;
+	if (!printed_version++)
+		printk(version);
+#endif
+
 	card_idx++;
 	option = card_idx < MAX_UNITS ? options[card_idx] : 0;
 	io_size = via_rhine_chip_info[chip_id].io_size;
@@ -1499,6 +1498,10 @@
 
 static int __init via_rhine_init (void)
 {
+/* when a module, this is printed whether or not devices are found in probe */
+#ifdef MODULE
+	printk(version);
+#endif
 	return pci_module_init (&via_rhine_driver);
 }
 
diff -urN linux-2.4.4-pre5/drivers/net/wan/dlci.c linux/drivers/net/wan/dlci.c
--- linux-2.4.4-pre5/drivers/net/wan/dlci.c	Mon Dec  3 22:36:15 2001
+++ linux/drivers/net/wan/dlci.c	Mon Dec  3 22:36:25 2001
@@ -57,8 +57,8 @@
 
 #include <net/sock.h>
 
-static const char *devname = "dlci";
-static const char *version = "DLCI driver v0.35, 4 Jan 1997, mike.mclagan@linux.org";
+static const char devname[] = "dlci";
+static const char version[] = "DLCI driver v0.35, 4 Jan 1997, mike.mclagan@linux.org";
 
 static struct net_device *open_dev[CONFIG_DLCI_COUNT];
 
diff -urN linux-2.4.4-pre5/drivers/net/wan/dscc4.c linux/drivers/net/wan/dscc4.c
--- linux-2.4.4-pre5/drivers/net/wan/dscc4.c	Tue Mar 20 12:05:00 2001
+++ linux/drivers/net/wan/dscc4.c	Mon Dec  3 22:36:25 2001
@@ -107,7 +107,7 @@
 #include <linux/hdlc.h>
 
 /* Version */
-static const char * version = "$Id: dscc4.c,v 1.130 2001/02/25 15:27:34 romieu Exp $\n";
+static const char version[] = "$Id: dscc4.c,v 1.130 2001/02/25 15:27:34 romieu Exp $\n";
 static int debug;
 
 
diff -urN linux-2.4.4-pre5/drivers/net/wan/sdla_chdlc.c linux/drivers/net/wan/sdla_chdlc.c
--- linux-2.4.4-pre5/drivers/net/wan/sdla_chdlc.c	Mon Dec  3 22:36:15 2001
+++ linux/drivers/net/wan/sdla_chdlc.c	Mon Dec  3 22:36:25 2001
@@ -1024,9 +1024,7 @@
         dev->tx_queue_len = 100;
    
 	/* Initialize socket buffers */
-#if defined(LINUX_2_1) || defined(LINUX_2_4)
-	dev_init_buffers(dev);
-#else
+#if !defined(LINUX_2_1) && !defined(LINUX_2_4)
         for (i = 0; i < DEV_NUMBUFFS; ++i)
                 skb_queue_head_init(&dev->buffs[i]);
 #endif
diff -urN linux-2.4.4-pre5/drivers/net/wan/sdla_fr.c linux/drivers/net/wan/sdla_fr.c
--- linux-2.4.4-pre5/drivers/net/wan/sdla_fr.c	Mon Dec  3 22:36:15 2001
+++ linux/drivers/net/wan/sdla_fr.c	Mon Dec  3 22:36:25 2001
@@ -1278,9 +1278,7 @@
         	dev->tx_queue_len = 100;
 
 			/* Initialize socket buffers */
-#if defined(LINUX_2_1) || defined(LINUX_2_4)
-		dev_init_buffers(dev);
-#else
+#if !defined(LINUX_2_1) && !defined(LINUX_2_4)
 	        for (i = 0; i < DEV_NUMBUFFS; ++i)
 			skb_queue_head_init(&dev->buffs[i]);
 #endif
diff -urN linux-2.4.4-pre5/drivers/net/wan/sdla_ppp.c linux/drivers/net/wan/sdla_ppp.c
--- linux-2.4.4-pre5/drivers/net/wan/sdla_ppp.c	Mon Dec  3 22:36:15 2001
+++ linux/drivers/net/wan/sdla_ppp.c	Mon Dec  3 22:36:25 2001
@@ -793,9 +793,7 @@
         dev->tx_queue_len = 100;
    
 	/* Initialize socket buffers */
-#if defined(LINUX_2_1) || defined(LINUX_2_4)
-	dev_init_buffers(dev);
-#else
+      #if !defined(LINUX_2_1) && !defined(LINUX_2_4)
         for (i = 0; i < DEV_NUMBUFFS; ++i)
                 skb_queue_head_init(&dev->buffs[i]);
       #endif
diff -urN linux-2.4.4-pre5/drivers/net/wan/sdla_x25.c linux/drivers/net/wan/sdla_x25.c
--- linux-2.4.4-pre5/drivers/net/wan/sdla_x25.c	Mon Dec  3 22:36:15 2001
+++ linux/drivers/net/wan/sdla_x25.c	Mon Dec  3 22:36:25 2001
@@ -1174,9 +1174,7 @@
         dev->tx_queue_len = 100;
 
 	/* Initialize socket buffers */
-#if defined(LINUX_2_1) || defined(LINUX_2_4)
-	dev_init_buffers(dev);	
-#else
+#if !defined(LINUX_2_1) && !defined(LINUX_2_4)
         for (i = 0; i < DEV_NUMBUFFS; ++i)
                 skb_queue_head_init(&dev->buffs[i]);
 #endif
diff -urN linux-2.4.4-pre5/drivers/net/wan/wanpipe_multppp.c linux/drivers/net/wan/wanpipe_multppp.c
--- linux-2.4.4-pre5/drivers/net/wan/wanpipe_multppp.c	Mon Dec  3 22:36:15 2001
+++ linux/drivers/net/wan/wanpipe_multppp.c	Mon Dec  3 22:36:25 2001
@@ -719,8 +719,7 @@
         dev->tx_queue_len = 100;
    
 	/* Initialize socket buffers */
-      #if defined(LINUX_2_1) || defined(LINUX_2_4)
-      #else
+      #if !defined(LINUX_2_1) && !defined(LINUX_2_4)
         for (i = 0; i < DEV_NUMBUFFS; ++i)
                 skb_queue_head_init(&dev->buffs[i]);
       #endif
diff -urN linux-2.4.4-pre5/drivers/net/winbond-840.c linux/drivers/net/winbond-840.c
--- linux-2.4.4-pre5/drivers/net/winbond-840.c	Mon Dec  3 22:36:15 2001
+++ linux/drivers/net/winbond-840.c	Mon Dec  3 22:36:25 2001
@@ -38,12 +38,6 @@
 		descriptors. Remove cpu_to_le32, enable BE descriptors.
 */
 
-/* These identify the driver base version and may not be removed. */
-static const char version1[] =
-"winbond-840.c:v1.01 (2.4 port) 5/15/2000  Donald Becker <becker@scyld.com>\n";
-static const char version2[] =
-"  http://www.scyld.com/network/drivers.html\n";
-
 /* Automatically extracted configuration info:
 probe-func: winbond840_probe
 config-in: tristate 'Winbond W89c840 Ethernet support' CONFIG_WINBOND_840
@@ -132,6 +126,11 @@
 #include <asm/bitops.h>
 #include <asm/io.h>
 
+/* These identify the driver base version and may not be removed. */
+static char version[] __devinitdata =
+KERN_INFO "winbond-840.c:v1.01 (2.4 port) 5/15/2000  Donald Becker <becker@scyld.com>\n"
+KERN_INFO "  http://www.scyld.com/network/drivers.html\n";
+
 MODULE_AUTHOR("Donald Becker <becker@scyld.com>");
 MODULE_DESCRIPTION("Winbond W89c840 Ethernet driver");
 MODULE_PARM(max_interrupt_work, "i");
@@ -1412,6 +1411,10 @@
 
 static int __init w840_init(void)
 {
+/* when a module, this is printed whether or not devices are found in probe */
+#ifdef MODULE
+	printk(version);
+#endif
 	return pci_module_init(&w840_driver);
 }
 
diff -urN linux-2.4.4-pre5/drivers/net/yellowfin.c linux/drivers/net/yellowfin.c
--- linux-2.4.4-pre5/drivers/net/yellowfin.c	Mon Dec  3 22:36:15 2001
+++ linux/drivers/net/yellowfin.c	Mon Dec  3 22:36:25 2001
@@ -31,14 +31,6 @@
 
 */
 
-/* These identify the driver base version and may not be removed. */
-static const char version1[] =
-"yellowfin.c:v1.05  1/09/2001  Written by Donald Becker <becker@scyld.com>\n";
-static const char version2[] =
-"  http://www.scyld.com/network/yellowfin.html\n";
-static const char version3[] =
-"  (unofficial 2.4.x port, LK1.1.2, January 11, 2001)\n";
-
 /* The user-configurable values.
    These may be modified when a driver module is loaded.*/
 
@@ -115,6 +107,12 @@
 #include <asm/bitops.h>
 #include <asm/io.h>
 
+/* These identify the driver base version and may not be removed. */
+static char version[] __devinitdata =
+KERN_INFO "yellowfin.c:v1.05  1/09/2001  Written by Donald Becker <becker@scyld.com>\n"
+KERN_INFO "  http://www.scyld.com/network/yellowfin.html\n"
+KERN_INFO "  (unofficial 2.4.x port, LK1.1.2, January 11, 2001)\n";
+
 /* Condensed operations for readability. */
 #define virt_to_le32desc(addr)  cpu_to_le32(virt_to_bus(addr))
 #define le32desc_to_virt(addr)  bus_to_virt(le32_to_cpu(addr))
@@ -391,6 +389,13 @@
 	int i, option = find_cnt < MAX_UNITS ? options[find_cnt] : 0;
 	int drv_flags = pci_id_tbl[chip_idx].drv_flags;
 	
+/* when built into the kernel, we only print version if device is found */
+#ifndef MODULE
+	static int printed_version;
+	if (!printed_version++)
+		printk(version);
+#endif
+
 	i = pci_enable_device(pdev);
 	if (i) return i;
 
@@ -1391,10 +1396,10 @@
 
 static int __init yellowfin_init (void)
 {
-	if (debug)					/* Emit version even if no cards detected. */
-		printk(KERN_INFO "%s" KERN_INFO "%s" KERN_INFO "%s",
-		       version1, version2, version3);
-
+/* when a module, this is printed whether or not devices are found in probe */
+#ifdef MODULE
+	printk(version);
+#endif
 	return pci_module_init (&yellowfin_driver);
 }
 
diff -urN linux-2.4.4-pre5/drivers/parport/ChangeLog linux/drivers/parport/ChangeLog
--- linux-2.4.4-pre5/drivers/parport/ChangeLog	Mon Dec  3 22:36:15 2001
+++ linux/drivers/parport/ChangeLog	Mon Dec  3 22:36:25 2001
@@ -1,3 +1,18 @@
+2001-04-20  Paul Gortmaker  <p_gortmaker@yahoo.com>
+
+	* parport_pc.c: Cut down the size quite a bit (more than 4k off
+	the object, about 1k off the zImage) for the older non-PCI
+	machines which are typically resource starved anyway...
+
+2001-03-26  R Horn  <rjh@world.std.com>
+
+	* parport_pc.c: Some commentary changes.
+
+2001-04-19  Tim Waugh  <twaugh@redhat.com>
+
+	* parport_pc.c (parport_pc_probe_port): Remove __devinit
+	attribute.  Export unconditionally.
+
 2001-04-14  Jeff Garzik  <jgarzik@mandrakesoft.com>
 
 	Merged:  2001-03-30  Tim Waugh  <twaugh@redhat.com>
diff -urN linux-2.4.4-pre5/drivers/parport/parport_pc.c linux/drivers/parport/parport_pc.c
--- linux-2.4.4-pre5/drivers/parport/parport_pc.c	Mon Dec  3 22:36:15 2001
+++ linux/drivers/parport/parport_pc.c	Mon Dec  3 22:36:25 2001
@@ -11,6 +11,7 @@
  * Cleaned up include files - Russell King <linux@arm.uk.linux.org>
  * DMA support - Bert De Jonghe <bert@sophis.be>
  * Many ECP bugs fixed.  Fred Barnes & Jamie Lokier, 1999
+ * More PCI support now conditional on CONFIG_PCI, 03/2001, Paul G. 
  */
 
 /* This driver should work with any hardware that is broadly compatible
@@ -940,13 +941,11 @@
 			/* Can't yield the port. */
 			schedule ();
 
-		/* At this point, the FIFO may already be full.
-		 * Ideally, we'd be able to tell the port to hold on
-		 * for a second while we empty the FIFO, and we'd be
-		 * able to ensure that no data is lost.  I'm not sure
-		 * that's the case. :-(  It might be that you can play
-		 * games with STB, as in the forward case; someone should
-		 * look at a datasheet. */
+		/* At this point, the FIFO may already be full. In
+                 * that case ECP is already holding back the
+                 * peripheral (assuming proper design) with a delayed
+                 * handshake.  Work fast to avoid a peripheral
+                 * timeout.  */
 
 		if (ecrval & 0x01) {
 			/* FIFO is empty. Wait for interrupt. */
@@ -978,6 +977,10 @@
 				goto false_alarm;
 			}
 
+			/* Depending on how the FIFO threshold was
+                         * set, how long interrupt service took, and
+                         * how fast the peripheral is, we might be
+                         * lucky and have a just filled FIFO. */
 			continue;
 		}
 
@@ -989,6 +992,9 @@
 			continue;
 		}
 
+		/* FIFO not filled.  We will cycle this loop for a while
+                 * and either the peripheral will fill it faster,
+                 * tripping a fast empty with insb, or we empty it. */
 		*bufp++ = inb (fifo);
 		left--;
 	}
@@ -1983,10 +1989,10 @@
 
 /* --- Initialisation code -------------------------------- */
 
-struct parport *__devinit parport_pc_probe_port (unsigned long int base,
-						 unsigned long int base_hi,
-						 int irq, int dma,
-						 struct pci_dev *dev)
+struct parport *parport_pc_probe_port (unsigned long int base,
+				       unsigned long int base_hi,
+				       int irq, int dma,
+				       struct pci_dev *dev)
 {
 	struct parport_pc_private *priv;
 	struct parport_operations *ops;
@@ -2179,6 +2185,7 @@
 }
 
 
+#ifdef CONFIG_PCI
 /* Via support maintained by Jeff Garzik <jgarzik@mandrakesoft.com> */
 static int __devinit sio_via_686a_probe (struct pci_dev *pdev, int autoirq,
 					 int autodma)
@@ -2553,7 +2560,6 @@
 
 static int __init parport_pc_init_superio (int autoirq, int autodma)
 {
-#ifdef CONFIG_PCI
 	const struct pci_device_id *id;
 	struct pci_dev *pdev;
 
@@ -2565,10 +2571,13 @@
 		return parport_pc_superio_info[id->driver_data].probe
 			(pdev, autoirq, autodma);
 	}
-#endif /* CONFIG_PCI */
 
 	return 0; /* zero devices found */
 }
+#else
+static struct pci_driver parport_pc_pci_driver;
+static int __init parport_pc_init_superio(void) {return 0;}
+#endif /* CONFIG_PCI */
 
 /* This is called by parport_pc_find_nonpci_ports (in asm/parport.h) */
 static int __init __attribute__((unused))
@@ -2641,16 +2650,7 @@
 }
 
 /* Exported symbols. */
-#ifdef CONFIG_PARPORT_PC_PCMCIA
-
-/* parport_cs needs this in order to dyncamically get us to find ports. */
 EXPORT_SYMBOL (parport_pc_probe_port);
-
-#else
-
-EXPORT_NO_SYMBOLS;
-
-#endif
 
 #ifdef MODULE
 static int io[PARPORT_PC_MAX_PORTS+1] = { [0 ... PARPORT_PC_MAX_PORTS] = 0 };
diff -urN linux-2.4.4-pre5/drivers/pci/quirks.c linux/drivers/pci/quirks.c
--- linux-2.4.4-pre5/drivers/pci/quirks.c	Mon Dec  3 22:36:15 2001
+++ linux/drivers/pci/quirks.c	Mon Dec  3 22:36:25 2001
@@ -85,6 +85,29 @@
 }
 
 /*
+ *	VIA Apollo KT133 needs PCI latency patch
+ *	Made according to a windows driver based patch by George E. Breese
+ *	see PCI Latency Adjust on http://www.viahardware.com/download/viatweak.shtm
+ */
+static void __init quirk_vialatency(struct pci_dev *dev)
+{
+	u8 r70;
+
+	printk(KERN_INFO "Applying VIA PCI latency patch.\n");
+	/*
+	 *    In register 0x70, mask off bit 2 (PCI Master read caching)
+	 *    and 1 (Delay Transaction)
+	 */
+	pci_read_config_byte(dev, 0x70, &r70);
+	r70 &= 0xf9;
+	pci_write_config_byte(dev, 0x70, r70);
+	/*
+	 *    Turn off PCI Latency timeout (set to 0 clocks)
+	 */
+	pci_write_config_byte(dev, 0x75, 0x80);
+}
+
+/*
  *	VIA Apollo VP3 needs ETBF on BT848/878
  */
  
@@ -289,6 +312,7 @@
 	{ PCI_FIXUP_FINAL,	PCI_VENDOR_ID_INTEL, 	PCI_DEVICE_ID_INTEL_82443BX_2, 	quirk_natoma },
 	{ PCI_FIXUP_FINAL,	PCI_VENDOR_ID_SI,	PCI_DEVICE_ID_SI_5597,		quirk_nopcipci },
 	{ PCI_FIXUP_FINAL,	PCI_VENDOR_ID_SI,	PCI_DEVICE_ID_SI_496,		quirk_nopcipci },
+	{ PCI_FIXUP_FINAL,	PCI_VENDOR_ID_VIA,	PCI_DEVICE_ID_VIA_8363_0,	quirk_vialatency },
 	{ PCI_FIXUP_FINAL,	PCI_VENDOR_ID_VIA,	PCI_DEVICE_ID_VIA_82C597_0,	quirk_viaetbf },
 	{ PCI_FIXUP_HEADER,	PCI_VENDOR_ID_VIA,	PCI_DEVICE_ID_VIA_82C597_0,	quirk_vt82c598_id },
 	{ PCI_FIXUP_HEADER,	PCI_VENDOR_ID_VIA,	PCI_DEVICE_ID_VIA_82C586_3,	quirk_vt82c586_acpi },
diff -urN linux-2.4.4-pre5/drivers/sound/maestro3.c linux/drivers/sound/maestro3.c
--- linux-2.4.4-pre5/drivers/sound/maestro3.c	Mon Dec  3 22:36:15 2001
+++ linux/drivers/sound/maestro3.c	Mon Dec  3 22:36:26 2001
@@ -2307,6 +2307,8 @@
     codec->private_data = card;
     codec->codec_read = m3_ac97_read;
     codec->codec_write = m3_ac97_write;
+    /* someday we should support secondary codecs.. */
+    codec->id = 0;
 
     if (ac97_probe_codec(codec) == 0) {
         printk(KERN_ERR PFX "codec probe failed\n");
@@ -2934,6 +2936,7 @@
 
     if (!pci_register_driver(&m3_pci_driver)) {
         pci_unregister_driver(&m3_pci_driver);
+        unregister_reboot_notifier(&m3_reboot_nb);
         return -ENODEV;
     }
     return 0;
diff -urN linux-2.4.4-pre5/fs/affs/Changes linux/fs/affs/Changes
--- linux-2.4.4-pre5/fs/affs/Changes	Thu Apr 13 13:57:09 2000
+++ linux/fs/affs/Changes	Mon Dec  3 22:36:26 2001
@@ -26,7 +26,30 @@
   last allocated block is not cleared. Well,
   this fs never claimed to be Posix conformant.
 
-Please direct bug reports to: hjw@zvw.de
+Please direct bug reports to: zippel@linux-m68k.org
+
+Version 3.14
+------------
+
+- correctly cut off long file names for compares
+- correctly initialize s_last_bmap
+
+Version 3.13
+------------
+
+Major cleanup for 2.4 [Roman Zippel]
+- new extended block handling
+- new bitmap allocation functions
+- locking should be safe for the future
+- cleanup of some interfaces
+
+Version 3.12
+------------
+
+more 2.4 fixes: [Roman Zippel]
+- s_lock changes
+- increased getblock mess
+- clear meta blocks 
 
 Version 3.11
 ------------
diff -urN linux-2.4.4-pre5/fs/affs/Makefile linux/fs/affs/Makefile
--- linux-2.4.4-pre5/fs/affs/Makefile	Fri Dec 29 14:07:23 2000
+++ linux/fs/affs/Makefile	Mon Dec  3 22:36:26 2001
@@ -7,6 +7,8 @@
 #
 # Note 2! The CFLAGS definitions are now in the main makefile.
 
+#EXTRA_CFLAGS=-DDEBUG=1
+
 O_TARGET := affs.o
 
 obj-y   := super.o namei.o inode.o file.o dir.o amigaffs.o bitmap.o symlink.o
diff -urN linux-2.4.4-pre5/fs/affs/amigaffs.c linux/fs/affs/amigaffs.c
--- linux-2.4.4-pre5/fs/affs/amigaffs.c	Tue Sep  5 14:07:29 2000
+++ linux/fs/affs/amigaffs.c	Mon Dec  3 22:36:26 2001
@@ -8,7 +8,6 @@
  *  Please send bug reports to: hjw@zvw.de
  */
 
-#define DEBUG 0
 #include <stdarg.h>
 #include <linux/stat.h>
 #include <linux/sched.h>
@@ -26,170 +25,236 @@
  * Functions for accessing Amiga-FFS structures.
  */
 
-/* Set *NAME to point to the file name in a file header block in memory
-   pointed to by FH_DATA.  The length of the name is returned. */
 
-int
-affs_get_file_name(int bsize, void *fh_data, unsigned char **name)
-{
-	struct file_end *file_end;
-
-	file_end = GET_END_PTR(struct file_end, fh_data, bsize);
-	if (file_end->file_name[0] == 0
-	    || file_end->file_name[0] > 30) {
-		printk(KERN_WARNING "AFFS: bad filename (length=%d chars)\n",
-			file_end->file_name[0]);
-		*name = "***BAD_FILE***";
-		return 14;
-        }
-	*name = (unsigned char *)&file_end->file_name[1];
-        return file_end->file_name[0];
-}
-
-/* Insert a header block (file) into the directory (next).
- * This routine assumes that the caller has the superblock locked.
+/* Insert a header block bh into the directory dir
+ * caller must hold AFFS_DIR->i_hash_lock!
  */
 
 int
-affs_insert_hash(unsigned long next, struct buffer_head *file, struct inode *inode)
+affs_insert_hash(struct inode *dir, struct buffer_head *bh)
 {
-	struct buffer_head	*bh;
-	s32			 ino;
-	int			 offset;
+	struct super_block *sb = dir->i_sb;
+	struct buffer_head *dir_bh;
+	u32 ino, hash_ino;
+	int offset;
 
-	offset = affs_hash_name(FILE_END(file->b_data,inode)->file_name+1,
-				FILE_END(file->b_data,inode)->file_name[0],
-				AFFS_I2FSTYPE(inode),AFFS_I2HSIZE(inode)) + 6;
-	ino    = be32_to_cpu(((struct dir_front *)file->b_data)->own_key);
+	ino = bh->b_blocknr;
+	offset = affs_hash_name(sb, AFFS_TAIL(sb, bh)->name + 1, AFFS_TAIL(sb, bh)->name[0]);
 
-	pr_debug("AFFS: insert_hash(dir_ino=%lu,ino=%d)\n",next,ino);
+	pr_debug("AFFS: insert_hash(dir=%u, ino=%d)\n", (u32)dir->i_ino, ino);
 
-	FILE_END(file->b_data,inode)->parent = cpu_to_be32(next);
+	dir_bh = affs_bread(sb, dir->i_ino);
+	if (!dir_bh)
+		return -EIO;
 
-	while (1) {
-		if (!(bh = affs_bread(inode->i_dev,next,AFFS_I2BSIZE(inode))))
+	hash_ino = be32_to_cpu(AFFS_HEAD(dir_bh)->table[offset]);
+	while (hash_ino) {
+		affs_brelse(dir_bh);
+		dir_bh = affs_bread(sb, hash_ino);
+		if (!dir_bh)
 			return -EIO;
-		next = be32_to_cpu(((s32 *)bh->b_data)[offset]);
-		if (!next || next > ino)
-			break;
-		offset = AFFS_I2BSIZE(inode) / 4 - 4;
-		affs_brelse(bh);
+		hash_ino = be32_to_cpu(AFFS_TAIL(sb, dir_bh)->hash_chain);
 	}
+	AFFS_TAIL(sb, bh)->parent = cpu_to_be32(dir->i_ino);
+	AFFS_TAIL(sb, bh)->hash_chain = 0;
 
-	DIR_END(file->b_data,inode)->hash_chain = cpu_to_be32(next);
-	((s32 *)bh->b_data)[offset]             = cpu_to_be32(ino);
-	affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5);
-	mark_buffer_dirty(bh);
-	affs_brelse(bh);
+	if (dir->i_ino == dir_bh->b_blocknr)
+		AFFS_HEAD(dir_bh)->table[offset] = cpu_to_be32(ino);
+	else
+		AFFS_TAIL(sb, dir_bh)->hash_chain = cpu_to_be32(ino);
+
+	affs_adjust_checksum(dir_bh, ino);
+	mark_buffer_dirty(dir_bh);
+	affs_brelse(dir_bh);
+
+	dir->i_mtime = dir->i_ctime = CURRENT_TIME;
+	dir->i_version = ++event;
+	mark_inode_dirty(dir);
 
 	return 0;
 }
-/* Remove a header block from its hash table (directory).
- * 'inode' may be any inode on the partition, it's only
- * used for calculating the block size and superblock
- * reference.
+
+/* Remove a header block from its directory.
+ * caller must hold AFFS_DIR->i_hash_lock!
  */
 
 int
-affs_remove_hash(struct buffer_head *dbh, struct inode *inode)
+affs_remove_hash(struct inode *dir, struct buffer_head *rem_bh)
 {
-	s32			 ownkey;
-	s32			 key;
-	s32			 ptype;
-	s32			 stype;
-	int			 offset;
-	int			 retval;
-	struct buffer_head	*bh;
-
-	ownkey = be32_to_cpu(((struct dir_front *)dbh->b_data)->own_key);
-	key    = be32_to_cpu(FILE_END(dbh->b_data,inode)->parent);
-	offset = affs_hash_name(FILE_END(dbh->b_data,inode)->file_name+1,
-				FILE_END(dbh->b_data,inode)->file_name[0],
-				AFFS_I2FSTYPE(inode),AFFS_I2HSIZE(inode)) + 6;
-	pr_debug("AFFS: remove_hash(dir=%d, ino=%d, hashval=%d)\n",key,ownkey,offset-6);
-	retval = -ENOENT;
+	struct super_block *sb;
+	struct buffer_head *bh;
+	u32 rem_ino, hash_ino, ino;
+	int offset, retval;
+
+	sb = dir->i_sb;
+	rem_ino = rem_bh->b_blocknr;
+	offset = affs_hash_name(sb, AFFS_TAIL(sb, rem_bh)->name+1, AFFS_TAIL(sb, rem_bh)->name[0]);
+	pr_debug("AFFS: remove_hash(dir=%d, ino=%d, hashval=%d)\n", (u32)dir->i_ino, rem_ino, offset);
 
-	lock_super(inode->i_sb);
-	while (key) {
-		if (!(bh = affs_bread(inode->i_dev,key,AFFS_I2BSIZE(inode)))) {
-			retval = -EIO;
-			break;
-		}
-		if (affs_checksum_block(AFFS_I2BSIZE(inode),bh->b_data,&ptype,&stype)
-		    || ptype != T_SHORT || (stype != ST_FILE && stype != ST_USERDIR &&
-					    stype != ST_LINKFILE && stype != ST_LINKDIR &&
-					    stype != ST_ROOT && stype != ST_SOFTLINK)) {
-			affs_error(inode->i_sb,"affs_remove_hash",
-				"Bad block in hash chain (key=%d, ptype=%d, stype=%d, ownkey=%d)",
-				key,ptype,stype,ownkey);
-			affs_brelse(bh);
-			retval = -EINVAL;
-			break;
-		}
-		key = be32_to_cpu(((s32 *)bh->b_data)[offset]);
-		if (ownkey == key) {
-			((s32 *)bh->b_data)[offset] = FILE_END(dbh->b_data,inode)->hash_chain;
-			affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5);
+	bh = affs_bread(sb, dir->i_ino);
+	if (!bh)
+		return -EIO;
+
+	retval = -ENOENT;
+	hash_ino = be32_to_cpu(AFFS_HEAD(bh)->table[offset]);
+	while (hash_ino) {
+		if (hash_ino == rem_ino) {
+			ino = AFFS_TAIL(sb, rem_bh)->hash_chain;
+			if (dir->i_ino == bh->b_blocknr)
+				AFFS_HEAD(bh)->table[offset] = ino;
+			else
+				AFFS_TAIL(sb, bh)->hash_chain = ino;
+			affs_adjust_checksum(bh, be32_to_cpu(ino) - hash_ino);
 			mark_buffer_dirty(bh);
-			affs_brelse(bh);
 			retval = 0;
 			break;
 		}
 		affs_brelse(bh);
-		offset = AFFS_I2BSIZE(inode) / 4 - 4;
+		bh = affs_bread(sb, hash_ino);
+		if (!bh)
+			return -EIO;
+		hash_ino = be32_to_cpu(AFFS_TAIL(sb, bh)->hash_chain);
 	}
-	unlock_super(inode->i_sb);
+
+	affs_brelse(bh);
+
+	dir->i_mtime = dir->i_ctime = CURRENT_TIME;
+	dir->i_version = ++event;
+	mark_inode_dirty(dir);
 
 	return retval;
 }
 
+static void
+affs_fix_dcache(struct dentry *dentry, u32 entry_ino)
+{
+	struct inode *inode = dentry->d_inode;
+	void *data = dentry->d_fsdata;
+	struct list_head *head, *next;
+
+	spin_lock(&dcache_lock);
+	head = &inode->i_dentry;
+	next = head->next;
+	while (next != head) {
+		dentry = list_entry(next, struct dentry, d_alias);
+		if (entry_ino == (u32)dentry->d_fsdata) {
+			dentry->d_fsdata = data;
+			break;
+		}
+		next = next->next;
+	}
+	spin_unlock(&dcache_lock);
+}
+
+
 /* Remove header from link chain */
 
-int
-affs_remove_link(struct buffer_head *dbh, struct inode *inode)
+static int
+affs_remove_link(struct dentry *dentry)
 {
-	int			 retval;
-	s32			 key;
-	s32			 ownkey;
-	s32			 ptype;
-	s32			 stype;
-	struct buffer_head	*bh;
-
-	ownkey = be32_to_cpu((DIR_FRONT(dbh)->own_key));
-	key    = be32_to_cpu(FILE_END(dbh->b_data,inode)->original);
-	retval = -ENOENT;
+	struct inode *dir, *inode = dentry->d_inode;
+	struct super_block *sb = inode->i_sb;
+	struct buffer_head *bh = NULL, *link_bh = NULL;
+	u32 link_ino, ino;
+	int retval;
+
+	pr_debug("AFFS: remove_link(key=%ld)\n", inode->i_ino);
+	down(&AFFS_INODE->i_link_lock);
+	retval = -EIO;
+	bh = affs_bread(sb, inode->i_ino);
+	if (!bh)
+		goto done;
+
+	link_ino = (u32)dentry->d_fsdata;
+	if (inode->i_ino == link_ino) {
+		/* we can't remove the head of the link, as its blocknr is still used as ino,
+		 * so we remove the block of the first link instead.
+		 */ 
+		link_ino = be32_to_cpu(AFFS_TAIL(sb, bh)->link_chain);
+		link_bh = affs_bread(sb, link_ino);
+		if (!link_bh)
+			goto done;
+
+		dir = iget(sb, be32_to_cpu(AFFS_TAIL(sb, link_bh)->parent));
+		if (!dir)
+			goto done;
+
+		down(&AFFS_DIR->i_hash_lock);
+		affs_fix_dcache(dentry, link_ino);
+		retval = affs_remove_hash(dir, link_bh);
+		if (retval)
+			goto done;
+
+		memcpy(AFFS_TAIL(sb, bh)->name, AFFS_TAIL(sb, link_bh)->name, 32);
+		retval = affs_insert_hash(dir, bh);
+		if (retval)
+			goto done;
 
-	pr_debug("AFFS: remove_link(link=%d, original=%d)\n",ownkey,key);
+		up(&AFFS_DIR->i_hash_lock);
+		iput(dir);
+	} else {
+		link_bh = affs_bread(sb, link_ino);
+		if (!link_bh)
+			goto done;
+	}
 
-	lock_super(inode->i_sb);
-	while (key) {
-		if (!(bh = affs_bread(inode->i_dev,key,AFFS_I2BSIZE(inode)))) {
-			retval = -EIO;
-			break;
-		}
-		if (affs_checksum_block(AFFS_I2BSIZE(inode),bh->b_data,&ptype,&stype)) {
-			affs_error(inode->i_sb,"affs_remove_link","Checksum error (block %d)",key);
-			affs_brelse(bh);
-			retval = -EINVAL;
-			break;
-		}
-		key = be32_to_cpu(FILE_END(bh->b_data,inode)->link_chain);
-		if (ownkey == key) {
-			FILE_END(bh->b_data,inode)->link_chain =
-						FILE_END(dbh->b_data,inode)->link_chain;
-			affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5);
+	while ((ino = be32_to_cpu(AFFS_TAIL(sb, bh)->link_chain))) {
+		if (ino == link_ino) {
+			ino = AFFS_TAIL(sb, link_bh)->link_chain;
+			AFFS_TAIL(sb, bh)->link_chain = ino;
+			affs_adjust_checksum(bh, be32_to_cpu(ino) - link_ino);
 			mark_buffer_dirty(bh);
-			affs_brelse(bh);
 			retval = 0;
-			break;
+			/* Fix the link count, if bh is a normal header block without links */
+			switch (be32_to_cpu(AFFS_TAIL(sb, bh)->stype)) {
+			case ST_LINKDIR:
+			case ST_LINKFILE:
+				break;
+			default:
+				if (!AFFS_TAIL(sb, bh)->link_chain)
+					inode->i_nlink = 1;
+			}
+			affs_free_block(sb, link_ino);
+			goto done;
 		}
 		affs_brelse(bh);
+		bh = affs_bread(sb, ino);
+		if (!bh)
+			goto done;
 	}
-	unlock_super(inode->i_sb);
+	retval = -ENOENT;
+done:
+	affs_brelse(link_bh);
+	affs_brelse(bh);
+	up(&AFFS_INODE->i_link_lock);
+	return retval;
+}
+
 
+static int
+affs_empty_dir(struct inode *inode)
+{
+	struct super_block *sb = inode->i_sb;
+	struct buffer_head *bh;
+	int retval, size;
+
+	retval = -EIO;
+	bh = affs_bread(sb, inode->i_ino);
+	if (!bh)
+		goto done;
+
+	retval = -ENOTEMPTY;
+	for (size = AFFS_SB->s_hashsize - 1; size >= 0; size--)
+		if (AFFS_HEAD(bh)->table[size])
+			goto not_empty;
+	retval = 0;
+not_empty:
+	affs_brelse(bh);
+done:
 	return retval;
 }
 
+
 /* Remove a filesystem object. If the object to be removed has
  * links to it, one of the links must be changed to inherit
  * the file or directory. As above, any inode will do.
@@ -200,86 +265,55 @@
  */
 
 int
-affs_remove_header(struct buffer_head *bh, struct inode *inode)
+affs_remove_header(struct dentry *dentry)
 {
-	struct buffer_head	*link_bh;
-	struct inode		*dir;
-	unsigned long		 link_ino;
-	unsigned long		 orig_ino;
-	unsigned int		 dir_ino;
-	int			 error;
-
-	pr_debug("AFFS: remove_header(key=%ld)\n",be32_to_cpu(DIR_FRONT(bh)->own_key));
-
-	/* Mark directory as changed.  We do this before anything else,
-	 * as it must be done anyway and doesn't hurt even if an
-	 * error occurs later.
-	 */
-	dir = iget(inode->i_sb,be32_to_cpu(FILE_END(bh->b_data,inode)->parent));
-	if (!dir)
-		return -EIO;
-	dir->i_ctime = dir->i_mtime = CURRENT_TIME;
-	dir->i_version++;
-	mark_inode_dirty(dir);
-	iput(dir);
+	struct super_block *sb;
+	struct inode *inode, *dir;
+	struct buffer_head *bh = NULL;
+	int retval;
 
-	orig_ino = be32_to_cpu(FILE_END(bh->b_data,inode)->original);
-	if (orig_ino) {		/* This is just a link. Nothing much to do. */
-		pr_debug("AFFS: Removing link.\n");
-		if ((error = affs_remove_link(bh,inode)))
-			return error;
-		if ((error = affs_remove_hash(bh,inode)))
-			return error;
-		affs_free_block(inode->i_sb,be32_to_cpu(DIR_FRONT(bh)->own_key));
-		return 1;
-	}
-	
-	link_ino = be32_to_cpu(FILE_END(bh->b_data,inode)->link_chain);
-	if (link_ino) {		/* This is the complicated case. Yuck. */
-		pr_debug("AFFS: Removing original with links to it.\n");
-		/* Unlink the object and its first link from their directories. */
-		if ((error = affs_remove_hash(bh,inode)))
-			return error;
-		if (!(link_bh = affs_bread(inode->i_dev,link_ino,AFFS_I2BSIZE(inode))))
-			return -EIO;
-		if ((error = affs_remove_hash(link_bh,inode))) {
-			affs_brelse(link_bh);
-			return error;
-		}
-		/* Fix link chain. */
-		if ((error = affs_remove_link(link_bh,inode))) {
-			affs_brelse(link_bh);
-			return error;
-		}
-		/* Rename link to object. */
-		memcpy(FILE_END(bh->b_data,inode)->file_name,
-			FILE_END(link_bh->b_data,inode)->file_name,32);
-		/* Insert object into dir the link was in. */
-		dir_ino = be32_to_cpu(FILE_END(link_bh->b_data,inode)->parent);
-		if ((error = affs_insert_hash(dir_ino,bh,inode))) {
-			affs_brelse(link_bh);
-			return error;
-		}
-		affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5);
-		mark_buffer_dirty(bh);
-		affs_brelse(link_bh);
-		affs_free_block(inode->i_sb,link_ino);
-		/* Mark the link's parent dir as changed, too. */
-		if (!(dir = iget(inode->i_sb,dir_ino)))
-			return -EIO;
-		dir->i_ctime = dir->i_mtime = CURRENT_TIME;
-		dir->i_version++;
-		mark_inode_dirty(dir);
-		iput(dir);
-		return 1;
+	dir = dentry->d_parent->d_inode;
+	sb = dir->i_sb;
+
+	retval = -ENOENT;
+	inode = dentry->d_inode;
+	if (!inode)
+		goto done;
+
+	pr_debug("AFFS: remove_header(key=%ld)\n", inode->i_ino);
+	down(&AFFS_DIR->i_hash_lock);
+	if (S_ISDIR(inode->i_mode)) {
+		retval = affs_empty_dir(inode);
+		if (retval)
+			goto done_unlock;
 	}
-	/* Plain file/dir. This is the simplest case. */
-	pr_debug("AFFS: Removing plain file/dir.\n");
-	if ((error = affs_remove_hash(bh,inode)))
-		return error;
-	return 0;
-}
 
+	retval = -EIO;
+	bh = affs_bread(sb, inode->i_ino);
+	if (!bh)
+		goto done;
+
+	retval = affs_remove_hash(dir, bh);
+	if (retval)
+		goto done_unlock;
+
+	up(&AFFS_DIR->i_hash_lock);
+
+	if (inode->i_nlink > 1)
+		retval = affs_remove_link(dentry);
+	else
+		inode->i_nlink = 0;
+	inode->i_ctime = CURRENT_TIME;
+	mark_inode_dirty(inode);
+
+done:
+	affs_brelse(bh);
+	return retval;
+
+done_unlock:
+	up(&AFFS_DIR->i_hash_lock);
+	goto done;
+}
 
 /* Checksum a block, do various consistency checks and optionally return
    the blocks type number.  DATA points to the block.  If their pointers
@@ -289,21 +323,15 @@
    Returns non-zero if the block is not consistent. */
 
 u32
-affs_checksum_block(int bsize, void *data, s32 *ptype, s32 *stype)
+affs_checksum_block(struct super_block *sb, struct buffer_head *bh)
 {
-	u32	 sum;
-	u32	*p;
-
-	bsize /= 4;
-	if (ptype)
-		*ptype = be32_to_cpu(((s32 *)data)[0]);
-	if (stype)
-		*stype = be32_to_cpu(((s32 *)data)[bsize - 1]);
-
-	sum    = 0;
-	p      = data;
-	while (bsize--)
-		sum += be32_to_cpu(*p++);
+	u32 *ptr = (u32 *)bh->b_data;
+	u32 sum;
+	int bsize;
+
+	sum = 0;
+	for (bsize = sb->s_blocksize / sizeof(u32); bsize > 0; bsize--)
+		sum += be32_to_cpu(*ptr++);
 	return sum;
 }
 
@@ -313,19 +341,21 @@
  */
 
 void
-affs_fix_checksum(int bsize, void *data, int cspos)
+affs_fix_checksum(struct super_block *sb, struct buffer_head *bh)
 {
-	u32	 ocs;
-	u32	 cs;
-
-	cs   = affs_checksum_block(bsize,data,NULL,NULL);
-	ocs  = be32_to_cpu(((u32 *)data)[cspos]);
-	ocs -= cs;
-	((u32 *)data)[cspos] = be32_to_cpu(ocs);
+	int cnt = sb->s_blocksize / sizeof(u32);
+	u32 *ptr = (u32 *)bh->b_data;
+	u32 checksum, *checksumptr;
+
+	checksumptr = ptr + 5;
+	*checksumptr = 0;
+	for (checksum = 0; cnt > 0; ptr++, cnt--)
+		checksum += be32_to_cpu(*ptr);
+	*checksumptr = cpu_to_be32(-checksum);
 }
 
 void
-secs_to_datestamp(time_t secs, struct DateStamp *ds)
+secs_to_datestamp(time_t secs, struct affs_date *ds)
 {
 	u32	 days;
 	u32	 minute;
@@ -338,59 +368,64 @@
 	minute  = secs / 60;
 	secs   -= minute * 60;
 
-	ds->ds_Days   = be32_to_cpu(days);
-	ds->ds_Minute = be32_to_cpu(minute);
-	ds->ds_Tick   = be32_to_cpu(secs * 50);
+	ds->days = be32_to_cpu(days);
+	ds->mins = be32_to_cpu(minute);
+	ds->ticks = be32_to_cpu(secs * 50);
 }
 
-int
+mode_t
 prot_to_mode(u32 prot)
 {
-	int	 mode = 0;
+	int mode = 0;
 
-	if (AFFS_UMAYWRITE(prot))
+	if (!(prot & FIBF_NOWRITE))
 		mode |= S_IWUSR;
-	if (AFFS_UMAYREAD(prot))
+	if (!(prot & FIBF_NOREAD))
 		mode |= S_IRUSR;
-	if (AFFS_UMAYEXECUTE(prot))
+	if (!(prot & FIBF_NOEXECUTE))
 		mode |= S_IXUSR;
-	if (AFFS_GMAYWRITE(prot))
+	if (prot & FIBF_GRP_WRITE)
 		mode |= S_IWGRP;
-	if (AFFS_GMAYREAD(prot))
+	if (prot & FIBF_GRP_READ)
 		mode |= S_IRGRP;
-	if (AFFS_GMAYEXECUTE(prot))
+	if (prot & FIBF_GRP_EXECUTE)
 		mode |= S_IXGRP;
-	if (AFFS_OMAYWRITE(prot))
+	if (prot & FIBF_OTR_WRITE)
 		mode |= S_IWOTH;
-	if (AFFS_OMAYREAD(prot))
+	if (prot & FIBF_OTR_READ)
 		mode |= S_IROTH;
-	if (AFFS_OMAYEXECUTE(prot))
+	if (prot & FIBF_OTR_EXECUTE)
 		mode |= S_IXOTH;
-	
+
 	return mode;
 }
 
-u32
-mode_to_prot(int mode)
+void
+mode_to_prot(struct inode *inode)
 {
-	u32	 prot = 0;
+	u32 prot = AFFS_INODE->i_protect;
+	mode_t mode = inode->i_mode;
 
-	if (mode & S_IXUSR)
-		prot |= FIBF_SCRIPT;
-	if (mode & S_IRUSR)
-		prot |= FIBF_READ;
-	if (mode & S_IWUSR)
-		prot |= FIBF_WRITE | FIBF_DELETE;
+	if (!(mode & S_IXUSR))
+		prot |= FIBF_NOEXECUTE;
+	if (!(mode & S_IRUSR))
+		prot |= FIBF_NOREAD;
+	if (!(mode & S_IWUSR))
+		prot |= FIBF_NOWRITE;
+	if (mode & S_IXGRP)
+		prot |= FIBF_GRP_EXECUTE;
 	if (mode & S_IRGRP)
 		prot |= FIBF_GRP_READ;
 	if (mode & S_IWGRP)
 		prot |= FIBF_GRP_WRITE;
+	if (mode & S_IXOTH)
+		prot |= FIBF_OTR_EXECUTE;
 	if (mode & S_IROTH)
 		prot |= FIBF_OTR_READ;
 	if (mode & S_IWOTH)
 		prot |= FIBF_OTR_WRITE;
-	
-	return prot;
+
+	AFFS_INODE->i_protect = prot;
 }
 
 void
@@ -402,12 +437,12 @@
 	vsprintf(ErrorBuffer,fmt,args);
 	va_end(args);
 
-	printk(KERN_CRIT "AFFS error (device %s): %s(): %s\n",kdevname(sb->s_dev),
+	printk(KERN_CRIT "AFFS error (device %s): %s(): %s\n", bdevname(sb->s_dev),
 		function,ErrorBuffer);
 	if (!(sb->s_flags & MS_RDONLY))
 		printk(KERN_WARNING "AFFS: Remounting filesystem read-only\n");
 	sb->s_flags |= MS_RDONLY;
-	sb->u.affs_sb.s_flags |= SF_READONLY;	/* Don't allow to remount rw */
+	AFFS_SB->s_flags |= SF_READONLY;	/* Don't allow to remount rw */
 }
 
 void
@@ -419,7 +454,7 @@
 	vsprintf(ErrorBuffer,fmt,args);
 	va_end(args);
 
-	printk(KERN_WARNING "AFFS warning (device %s): %s(): %s\n",kdevname(sb->s_dev),
+	printk(KERN_WARNING "AFFS warning (device %s): %s(): %s\n", bdevname(sb->s_dev),
 		function,ErrorBuffer);
 }
 
@@ -454,15 +489,11 @@
  */
 
 int
-affs_copy_name(unsigned char *bstr, const unsigned char *name)
+affs_copy_name(unsigned char *bstr, struct dentry *dentry)
 {
-	int	 len;
+	int len = MIN(dentry->d_name.len, 30);
 
-	for (len = 0; len < 30; len++) {
-		bstr[len + 1] = name[len];
-		if (name[len] == '\0')
-			break;
-	}
-	bstr[0] = len;
+	*bstr++ = len;
+	memcpy(bstr, dentry->d_name.name, len);
 	return len;
 }
diff -urN linux-2.4.4-pre5/fs/affs/bitmap.c linux/fs/affs/bitmap.c
--- linux-2.4.4-pre5/fs/affs/bitmap.c	Tue Sep  5 14:07:29 2000
+++ linux/fs/affs/bitmap.c	Mon Dec  3 22:36:26 2001
@@ -7,11 +7,11 @@
  *  block allocation, deallocation, calculation of free space.
  */
 
-#define DEBUG 0
 #include <linux/sched.h>
 #include <linux/affs_fs.h>
 #include <linux/stat.h>
 #include <linux/kernel.h>
+#include <linux/slab.h>
 #include <linux/string.h>
 #include <linux/locks.h>
 #include <linux/amigaffs.h>
@@ -22,85 +22,115 @@
 
 static int nibblemap[] = { 0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4 };
 
-int
-affs_count_free_bits(int blocksize, const char *data)
+u32
+affs_count_free_bits(u32 blocksize, const void *data)
 {
-  int	 free;
-  int	 i;
+	const u32 *map;
+	u32 free;
+	u32 tmp;
 
-  free = 0;
-  for (i = 0; i < blocksize; i++) {
-    free  += nibblemap[data[i] & 0xF] + nibblemap[(data[i]>>4) & 0xF];
-  }
+	map = data;
+	free = 0;
+	for (blocksize /= 4; blocksize > 0; blocksize--) {
+		tmp = *map++;
+		while (tmp) {
+			free += nibblemap[tmp & 0xf];
+			tmp >>= 4;
+		}
+	}
 
-  return free;
+	return free;
 }
 
-int
-affs_count_free_blocks(struct super_block *s)
+u32
+affs_count_free_blocks(struct super_block *sb)
 {
-	int	 free;
-	int	 i;
+	struct affs_bm_info *bm;
+	u32 free;
+	int i;
 
 	pr_debug("AFFS: count_free_blocks()\n");
 
+	if (sb->s_flags & MS_RDONLY)
+		return 0;
+
+	down(&AFFS_SB->s_bmlock);
+
+	bm = AFFS_SB->s_bitmap;
 	free = 0;
-	if (s->u.affs_sb.s_flags & SF_BM_VALID) {
-		for (i = 0; i < s->u.affs_sb.s_num_az; i++) {
-			free += s->u.affs_sb.s_alloc[i].az_free;
-		}
-	}
+	for (i = AFFS_SB->s_bmap_count; i > 0; bm++, i--)
+		free += bm->bm_free;
+
+	up(&AFFS_SB->s_bmlock);
+
 	return free;
 }
 
 void
-affs_free_block(struct super_block *sb, s32 block)
+affs_free_block(struct super_block *sb, u32 block)
 {
-	int			 bmap;
-	int			 bit;
-	int			 blk;
-	int			 zone_no;
-	struct affs_bm_info	*bm;
-
-	pr_debug("AFFS: free_block(%d)\n",block);
-
-	blk     = block - sb->u.affs_sb.s_reserved;
-	bmap    = blk / (sb->s_blocksize * 8 - 32);
-	bit     = blk % (sb->s_blocksize * 8 - 32);
-	zone_no = (bmap << (sb->s_blocksize_bits - 7)) + bit / 1024;
-	bm      = &sb->u.affs_sb.s_bitmap[bmap];
-	if (bmap >= sb->u.affs_sb.s_bm_count) {
-		affs_error(sb,"affs_free_block","Block %d outside partition",block);
-		return;
-	}
-	blk = 0;
-	set_bit(bit & 31,&blk);
+	struct affs_bm_info *bm;
+	struct buffer_head *bh;
+	u32 blk, bmap, bit, mask, tmp;
+	u32 *data;
+
+	pr_debug("AFFS: free_block(%u)\n", block);
+
+	if (block > AFFS_SB->s_partition_size)
+		goto err_range;
+
+	blk     = block - AFFS_SB->s_reserved;
+	bmap    = blk / AFFS_SB->s_bmap_bits;
+	bit     = blk % AFFS_SB->s_bmap_bits;
+	bm      = &AFFS_SB->s_bitmap[bmap];
+
+	down(&AFFS_SB->s_bmlock);
+
+	bh = AFFS_SB->s_bmap_bh;
+	if (AFFS_SB->s_last_bmap != bmap) {
+		affs_brelse(bh);
+		bh = affs_bread(sb, bm->bm_key);
+		if (!bh)
+			goto err_bh_read;
+		AFFS_SB->s_bmap_bh = bh;
+		AFFS_SB->s_last_bmap = bmap;
+	}
+
+	mask = 1 << (bit & 31);
+	data = (u32 *)bh->b_data + bit / 32 + 1;
+
+	/* mark block free */
+	tmp = be32_to_cpu(*data);
+	if (tmp & mask)
+		goto err_free;
+	*data = cpu_to_be32(tmp | mask);
+
+	/* fix checksum */
+	tmp = be32_to_cpu(*(u32 *)bh->b_data);
+	*(u32 *)bh->b_data = cpu_to_be32(tmp - mask);
 
-	lock_super(sb);
-	bm->bm_count++;
-	if (!bm->bm_bh) {
-		bm->bm_bh = affs_bread(sb->s_dev,bm->bm_key,sb->s_blocksize);
-		if (!bm->bm_bh) {
-			bm->bm_count--;
-			unlock_super(sb);
-			affs_error(sb,"affs_free_block","Cannot read bitmap block %d",bm->bm_key);
-			return;
-		}
-	}
-	if (test_and_set_bit(bit ^ BO_EXBITS,bm->bm_bh->b_data + 4))
-		affs_warning(sb,"affs_free_block","Trying to free block %d which is already free",
-				block);
-	else {
-		sb->u.affs_sb.s_alloc[zone_no].az_free++;
-		((u32 *)bm->bm_bh->b_data)[0] = cpu_to_be32(be32_to_cpu(((u32 *)bm->bm_bh->b_data)[0]) - blk);
-		mark_buffer_dirty(bm->bm_bh);
-		sb->s_dirt = 1;
-	}
-	if (--bm->bm_count == 0) {
-		affs_brelse(bm->bm_bh);
-		bm->bm_bh = NULL;
-	}
-	unlock_super(sb);
+	mark_buffer_dirty(bh);
+	sb->s_dirt = 1;
+	bm->bm_free++;
+
+	up(&AFFS_SB->s_bmlock);
+	return;
+
+err_free:
+	affs_warning(sb,"affs_free_block","Trying to free block %u which is already free", block);
+	up(&AFFS_SB->s_bmlock);
+	return;
+
+err_bh_read:
+	affs_error(sb,"affs_free_block","Cannot read bitmap block %u", bm->bm_key);
+	AFFS_SB->s_bmap_bh = NULL;
+	AFFS_SB->s_last_bmap = ~0;
+	up(&AFFS_SB->s_bmlock);
+	return;
+
+err_range:
+	affs_error(sb, "affs_free_block","Block %u outside partition", block);
+	return;
 }
 
 /*
@@ -112,266 +142,240 @@
  * header zone, though.
  */
 
-static s32
-affs_balloc(struct inode *inode, int zone_no)
+u32
+affs_alloc_block(struct inode *inode, u32 goal)
 {
-	u32			 w;
-	u32			*bm;
-	int			 fb;
-	int			 i;
-	int			 fwb;
-	s32			 block;
-	struct affs_zone	*zone;
-	struct affs_alloc_zone	*az;
-	struct super_block	*sb;
-
-	sb   = inode->i_sb;
-	zone = &sb->u.affs_sb.s_zones[zone_no];
-
-	if (!zone->z_bm || !zone->z_bm->bm_bh)
-		return 0;	
-
-	pr_debug("AFFS: balloc(inode=%lu,zone=%d)\n",inode->i_ino,zone_no);
-
-	az = &sb->u.affs_sb.s_alloc[zone->z_az_no];
-	bm = (u32 *)zone->z_bm->bm_bh->b_data;
-repeat:
-	for (i = zone->z_start; i < zone->z_end; i++) {
-		if (bm[i])
-			goto found;
-	}
-	return 0;	
+	struct super_block *sb;
+	struct affs_bm_info *bm;
+	struct buffer_head *bh;
+	u32 *data, *enddata;
+	u32 blk, bmap, bit, mask, mask2, tmp;
+	int i;
 
-found:
-	fwb = zone->z_bm->bm_firstblk + (i - 1) * 32;
-	lock_super(sb);
-	zone->z_start = i;
-	w   = ~be32_to_cpu(bm[i]);
-	fb  = find_first_zero_bit(&w,32);
-	if (fb > 31 || !test_and_clear_bit(fb ^ BO_EXBITS,&bm[i])) {
-		unlock_super(sb);
-		affs_warning(sb,"balloc","Empty block disappeared somehow");
-		goto repeat;
-	}
-	block = fwb + fb;
-	az->az_free--;
+	sb = inode->i_sb;
 
-	/* prealloc as much as possible within this word, but not in header zone */
+	pr_debug("AFFS: balloc(inode=%lu,goal=%u): ", inode->i_ino, goal);
 
-	if (zone_no) {
-		while (inode->u.affs_i.i_pa_cnt < AFFS_MAX_PREALLOC && ++fb < 32) {
-			fb = find_next_zero_bit(&w,32,fb);
-			if (fb > 31)
-				break;
-			if (!test_and_clear_bit(fb ^ BO_EXBITS,&bm[i])) {
-				affs_warning(sb,"balloc","Empty block disappeared somehow");
-				break;
-			}
-			inode->u.affs_i.i_data[inode->u.affs_i.i_pa_last++] = fwb + fb;
-			inode->u.affs_i.i_pa_last &= AFFS_MAX_PREALLOC - 1;
-			inode->u.affs_i.i_pa_cnt++;
-			az->az_free--;
-		}
+	if (inode->u.affs_i.i_pa_cnt) {
+		pr_debug("%d\n", inode->u.affs_i.i_lastalloc+1);
+		inode->u.affs_i.i_pa_cnt--;
+		return ++inode->u.affs_i.i_lastalloc;
 	}
-	w     = ~w - be32_to_cpu(bm[i]);
-	bm[0] = cpu_to_be32(be32_to_cpu(bm[0]) + w);
-	unlock_super(sb);
-	mark_buffer_dirty(zone->z_bm->bm_bh);
-	sb->s_dirt = 1;
-	zone->z_lru_time = jiffies;
-
-	return block;
-}
 
-/* Find a new allocation zone, starting at zone_no. */
-
-static int
-affs_find_new_zone(struct super_block *sb, int zone_no)
-{
-	struct affs_bm_info	*bm;
-	struct affs_zone	*zone;
-	struct affs_alloc_zone	*az;
-	int			 bestfree;
-	int			 bestno;
-	int			 bestused;
-	int			 lusers;
-	int			 i;
-	int			 min;
-
-	pr_debug("AFFS: find_new_zone(zone_no=%d)\n",zone_no);
-
-	bestfree = 0;
-	bestused = -1;
-	bestno   = -1;
-	lusers   = MAX_ZONES;
-	min      = zone_no ? AFFS_DATA_MIN_FREE : AFFS_HDR_MIN_FREE;
-	lock_super(sb);
-	zone = &sb->u.affs_sb.s_zones[zone_no];
-	i    = zone->z_az_no;
-	az   = &sb->u.affs_sb.s_alloc[i];
-	if (zone->z_bm && zone->z_bm->bm_count) {
-		if (--zone->z_bm->bm_count == 0) {
-			affs_brelse(zone->z_bm->bm_bh);
-			zone->z_bm->bm_bh = NULL;
-		}
-		if (az->az_count)
-			az->az_count--;
-		else
-			affs_error(sb,"find_new_zone","az_count=0, but bm used");
-
-	}
-	while (1) {
-		az = &sb->u.affs_sb.s_alloc[i];
-		if (!az->az_count) {
-			if (az->az_free > min) {
-				break;
-			}
-			if (az->az_free > bestfree) {
-				bestfree = az->az_free;
-				bestno   = i;
-			}
-		} else if (az->az_free && az->az_count < lusers) {
-			lusers   = az->az_count;
-			bestused = i;
-		}
-		if (++i >= sb->u.affs_sb.s_num_az)
-			i = 0;
-		if (i == zone->z_az_no) {		/* Seen all */
-			if (bestno >= 0) {
-				i = bestno;
-			} else {
-				i = bestused;
-			}
+	if (!goal || goal > AFFS_SB->s_partition_size) {
+		if (goal)
+			affs_warning(sb, "affs_balloc", "invalid goal %d", goal);
+		//if (!inode->u.affs_i.i_last_block)
+		//	affs_warning(sb, "affs_balloc", "no last alloc block");
+		goal = AFFS_SB->s_reserved;
+	}
+
+	blk = goal - AFFS_SB->s_reserved;
+	bmap = blk / AFFS_SB->s_bmap_bits;
+	bm = &AFFS_SB->s_bitmap[bmap];
+
+	down(&AFFS_SB->s_bmlock);
+
+	if (bm->bm_free)
+		goto find_bmap_bit;
+
+find_bmap:
+	/* search for the next bmap buffer with free bits */
+	i = AFFS_SB->s_bmap_count;
+	do {
+		bmap++;
+		bm++;
+		if (bmap < AFFS_SB->s_bmap_count)
+			continue;
+		/* restart search at zero */
+		bmap = 0;
+		bm = AFFS_SB->s_bitmap;
+		if (--i <= 0)
+			goto err_full;
+	} while (!bm->bm_free);
+	blk = bmap * AFFS_SB->s_bmap_bits;
+
+find_bmap_bit:
+
+	bh = AFFS_SB->s_bmap_bh;
+	if (AFFS_SB->s_last_bmap != bmap) {
+		affs_brelse(bh);
+		bh = affs_bread(sb, bm->bm_key);
+		if (!bh)
+			goto err_bh_read;
+		AFFS_SB->s_bmap_bh = bh;
+		AFFS_SB->s_last_bmap = bmap;
+	}
+
+	/* find an unused block in this bitmap block */
+	bit = blk % AFFS_SB->s_bmap_bits;
+	data = (u32 *)bh->b_data + bit / 32 + 1;
+	enddata = (u32 *)((u8 *)bh->b_data + sb->s_blocksize);
+	mask = ~0UL << (bit & 31);
+	blk &= ~31UL;
+
+	tmp = be32_to_cpu(*data) & mask;
+	if (tmp)
+		goto find_bit;
+
+	/* scan the rest of the buffer */
+	do {
+		blk += 32;
+		if (++data >= enddata)
+			/* didn't find something, can only happen
+			 * if scan didn't start at 0, try next bmap
+			 */
+			goto find_bmap;
+	} while (!(tmp = *data));
+	tmp = be32_to_cpu(tmp);
+
+find_bit:
+	/* finally look for a free bit in the word */
+	bit = ffs(tmp) - 1;
+	blk += bit + AFFS_SB->s_reserved;
+	mask2 = mask = 1 << (bit & 31);
+	inode->u.affs_i.i_lastalloc = blk;
+
+	/* prealloc as much as possible within this word */
+	while ((mask2 <<= 1)) {
+		if (!(tmp & mask2))
 			break;
-		}
-	}
-	if (i < 0) {
-		/* Didn't find a single free block anywhere. */
-		unlock_super(sb);
-		return 0;
-	}
-	az = &sb->u.affs_sb.s_alloc[i];
-	az->az_count++;
-	bm = &sb->u.affs_sb.s_bitmap[i >> (sb->s_blocksize_bits - 7)];
-	bm->bm_count++;
-	if (!bm->bm_bh)
-		bm->bm_bh = affs_bread(sb->s_dev,bm->bm_key,sb->s_blocksize);
-	if (!bm->bm_bh) {
-		bm->bm_count--;
-		az->az_count--;
-		unlock_super(sb);
-		affs_error(sb,"find_new_zone","Cannot read bitmap");
-		return 0;
+		inode->u.affs_i.i_pa_cnt++;
+		mask |= mask2;
 	}
-	zone->z_bm    = bm;
-	zone->z_start = (i & ((sb->s_blocksize / 128) - 1)) * 32 + 1;
-	zone->z_end   = zone->z_start + az->az_size;
-	zone->z_az_no = i;
-	zone->z_lru_time = jiffies;
-	pr_debug("AFFS: found zone (%d) in bm %d at lw offset %d with %d free blocks\n",
-		 i,(i >> (sb->s_blocksize_bits - 7)),zone->z_start,az->az_free);
-	unlock_super(sb);
-	return az->az_free;
-}
-
-/* Allocate a new header block. */
+	bm->bm_free -= inode->u.affs_i.i_pa_cnt + 1;
 
-s32
-affs_new_header(struct inode *inode)
-{
-	s32			 block;
+	*data = cpu_to_be32(tmp & ~mask);
 
-	pr_debug("AFFS: new_header(ino=%lu)\n",inode->i_ino);
+	/* fix checksum */
+	tmp = be32_to_cpu(*(u32 *)bh->b_data);
+	*(u32 *)bh->b_data = cpu_to_be32(tmp + mask);
 
-	if (!(block = affs_balloc(inode,0))) {
-		while (affs_find_new_zone(inode->i_sb,0)) {
-			if ((block = affs_balloc(inode,0)))
-				return block;
-			schedule();
-		}
-		return 0;
-	}
-	return block;
-}
+	mark_buffer_dirty(bh);
+	sb->s_dirt = 1;
 
-/* Allocate a new data block. */
+	up(&AFFS_SB->s_bmlock);
 
-s32
-affs_new_data(struct inode *inode)
-{
-	int			 empty, old;
-	unsigned long		 oldest;
-	struct affs_zone	*zone;
-	struct super_block	*sb;
-	int			 i = 0;
-	s32			 block;
+	pr_debug("%d\n", blk);
+	return blk;
 
-	pr_debug("AFFS: new_data(ino=%lu)\n",inode->i_ino);
+err_bh_read:
+	affs_error(sb,"affs_read_block","Cannot read bitmap block %u", bm->bm_key);
+	AFFS_SB->s_bmap_bh = NULL;
+	AFFS_SB->s_last_bmap = ~0;
+err_full:
+	pr_debug("failed\n");
+	up(&AFFS_SB->s_bmlock);
+	return 0;
+}
 
-	sb = inode->i_sb;
-	lock_super(sb);
-	if (inode->u.affs_i.i_pa_cnt) {
-		inode->u.affs_i.i_pa_cnt--;
-		unlock_super(sb);
-		block = inode->u.affs_i.i_data[inode->u.affs_i.i_pa_next++];
-		inode->u.affs_i.i_pa_next &= AFFS_MAX_PREALLOC - 1;
-		return block;
-	}
-	unlock_super(sb);
-	oldest = jiffies;
-	old    = 0;
-	empty  = 0;
-	zone   = &sb->u.affs_sb.s_zones[inode->u.affs_i.i_zone];
-	if (zone->z_ino == inode->i_ino) {
-		i = inode->u.affs_i.i_zone;
-		goto found;
-	}
-	for (i = 1; i < MAX_ZONES; i++) {
-		zone = &sb->u.affs_sb.s_zones[i];
-		if (!empty && zone->z_bm && !zone->z_ino)
-			empty = i;
-		if (zone->z_bm && zone->z_lru_time < oldest) {
-			old    = i;
-			oldest = zone->z_lru_time;
-		}
-	}
-	if (empty)
-		i = empty;
-	else if (old)
-		i = old;
-	else {
-		inode->u.affs_i.i_zone = 0;
-		return affs_new_header(inode);
-	}
+int
+affs_init_bitmap(struct super_block *sb)
+{
+	struct affs_bm_info *bm;
+	struct buffer_head *bmap_bh = NULL, *bh = NULL;
+	u32 *bmap_blk;
+	u32 size, blk, end, offset, mask;
+	int i, res = 0;
 
-	inode->u.affs_i.i_zone = i;
-	zone->z_ino            = inode->i_ino;
+	if (sb->s_flags & MS_RDONLY)
+		return 0;
 
-found:
-	zone = &sb->u.affs_sb.s_zones[i];
-	if (!(block = affs_balloc(inode,i))) {		/* No data zones left */
-		while (affs_find_new_zone(sb,i)) {
-			if ((block = affs_balloc(inode,i)))
-				return block;
-			schedule();
-		}
-		inode->u.affs_i.i_zone = 0;
-		zone->z_ino            = -1;
+	if (!AFFS_ROOT_TAIL(sb, AFFS_SB->s_root_bh)->bm_flag) {
+		printk(KERN_NOTICE "AFFS: Bitmap invalid - mounting %s read only\n",
+			kdevname(sb->s_dev));
+		sb->s_flags |= MS_RDONLY;
 		return 0;
 	}
-	return block;
-}
-
-void
-affs_make_zones(struct super_block *sb)
-{
-	int	 i, mid;
 
-	pr_debug("AFFS: make_zones(): num_zones=%d\n",sb->u.affs_sb.s_num_az);
+	AFFS_SB->s_last_bmap = ~0;
+	AFFS_SB->s_bmap_bh = NULL;
+	AFFS_SB->s_bmap_bits = sb->s_blocksize * 8 - 32;
+	AFFS_SB->s_bmap_count = (AFFS_SB->s_partition_size - AFFS_SB->s_reserved +
+				 AFFS_SB->s_bmap_bits - 1) / AFFS_SB->s_bmap_bits;
+	size = AFFS_SB->s_bmap_count * sizeof(struct affs_bm_info);
+	bm = AFFS_SB->s_bitmap = kmalloc(size, GFP_KERNEL);
+	if (!AFFS_SB->s_bitmap) {
+		printk(KERN_ERR "AFFS: Bitmap allocation failed\n");
+		return 1;
+	}
+	memset(AFFS_SB->s_bitmap, 0, size);
+
+	bmap_blk = (u32 *)AFFS_SB->s_root_bh->b_data;
+	blk = sb->s_blocksize / 4 - 49;
+	end = blk + 25;
+
+	for (i = AFFS_SB->s_bmap_count; i > 0; bm++, i--) {
+		affs_brelse(bh);
+
+		bm->bm_key = be32_to_cpu(bmap_blk[blk]);
+		bh = affs_bread(sb, bm->bm_key);
+		if (!bh) {
+			printk(KERN_ERR "AFFS: Cannot read bitmap\n");
+			res = 1;
+			goto out;
+		}
+		if (affs_checksum_block(sb, bh)) {
+			printk(KERN_WARNING "AFFS: Bitmap %u invalid - mounting %s read only.\n",
+			       bm->bm_key, kdevname(sb->s_dev));
+			sb->s_flags |= MS_RDONLY;
+			goto out;
+		}
+		pr_debug("AFFS: read bitmap block %d: %d\n", blk, bm->bm_key);
+		bm->bm_free = affs_count_free_bits(sb->s_blocksize - 4, bh->b_data + 4);
 
-	mid = (sb->u.affs_sb.s_num_az + 1) / 2;
-	for (i = 0; i < MAX_ZONES; i++) {
-		sb->u.affs_sb.s_zones[i].z_az_no = mid;
-		affs_find_new_zone(sb,i);
-	}
+		/* Don't try read the extension if this is the last block,
+		 * but we also need the right bm pointer below
+		 */
+		if (++blk < end || i == 1)
+			continue;
+		if (bmap_bh)
+			affs_brelse(bmap_bh);
+		bmap_bh = affs_bread(sb, be32_to_cpu(bmap_blk[blk]));
+		if (!bmap_bh) {
+			printk(KERN_ERR "AFFS: Cannot read bitmap extension\n");
+			res = 1;
+			goto out;
+		}
+		bmap_blk = (u32 *)bmap_bh->b_data;
+		blk = 0;
+		end = sb->s_blocksize / 4 - 1;
+	}
+
+	offset = (AFFS_SB->s_partition_size - AFFS_SB->s_reserved) % AFFS_SB->s_bmap_bits;
+	mask = ~(0xFFFFFFFFU << (offset & 31));
+	pr_debug("last word: %d %d %d\n", offset, offset / 32 + 1, mask);
+	offset = offset / 32 + 1;
+
+	if (mask) {
+		u32 old, new;
+
+		/* Mark unused bits in the last word as allocated */
+		old = be32_to_cpu(((u32 *)bh->b_data)[offset]);
+		new = old & mask;
+		//if (old != new) {
+			((u32 *)bh->b_data)[offset] = cpu_to_be32(new);
+			/* fix checksum */
+			//new -= old;
+			//old = be32_to_cpu(*(u32 *)bh->b_data);
+			//*(u32 *)bh->b_data = cpu_to_be32(old - new);
+			//mark_buffer_dirty(bh);
+		//}
+		/* correct offset for the bitmap count below */
+		//offset++;
+	}
+	while (++offset < sb->s_blocksize / 4)
+		((u32 *)bh->b_data)[offset] = 0;
+	((u32 *)bh->b_data)[0] = 0;
+	((u32 *)bh->b_data)[0] = cpu_to_be32(-affs_checksum_block(sb, bh));
+	mark_buffer_dirty(bh);
+
+	/* recalculate bitmap count for last block */
+	bm--;
+	bm->bm_free = affs_count_free_bits(sb->s_blocksize - 4, bh->b_data + 4);
+
+out:
+	affs_brelse(bh);
+	affs_brelse(bmap_bh);
+	return res;
 }
diff -urN linux-2.4.4-pre5/fs/affs/dir.c linux/fs/affs/dir.c
--- linux-2.4.4-pre5/fs/affs/dir.c	Fri Aug 11 14:29:02 2000
+++ linux/fs/affs/dir.c	Mon Dec  3 22:36:26 2001
@@ -13,7 +13,6 @@
  *
  */
 
-#define DEBUG 0
 #include <asm/uaccess.h>
 #include <linux/errno.h>
 #include <linux/fs.h>
@@ -50,109 +49,115 @@
 static int
 affs_readdir(struct file *filp, void *dirent, filldir_t filldir)
 {
-	int			 j, namelen;
-	s32			 i;
+	struct inode		*inode = filp->f_dentry->d_inode;
+	struct super_block	*sb = inode->i_sb;
+	struct buffer_head	*dir_bh;
+	struct buffer_head	*fh_bh;
+	unsigned char		*name;
+	int			 namelen;
+	u32			 i;
 	int			 hash_pos;
 	int			 chain_pos;
-	unsigned long		 ino;
+	u32			 f_pos;
+	u32			 ino;
 	int			 stored;
-	unsigned char		*name;
-	struct buffer_head	*dir_bh;
-	struct buffer_head	*fh_bh;
-	struct inode		*dir;
-	struct inode		*inode = filp->f_dentry->d_inode;
+	int			 res;
 
-	pr_debug("AFFS: readdir(ino=%lu,f_pos=%lu)\n",inode->i_ino,(unsigned long)filp->f_pos);
+	pr_debug("AFFS: readdir(ino=%lu,f_pos=%lx)\n",inode->i_ino,(unsigned long)filp->f_pos);
 
 	stored = 0;
+	res    = -EIO;
 	dir_bh = NULL;
 	fh_bh  = NULL;
-	dir    = NULL;
-	ino    = inode->i_ino;
+	f_pos  = filp->f_pos;
 
-	if (filp->f_pos == 0) {
+	if (f_pos == 0) {
 		filp->private_data = (void *)0;
-		if (filldir(dirent,".",1,filp->f_pos,inode->i_ino,DT_DIR) < 0) {
+		if (filldir(dirent, ".", 1, f_pos, inode->i_ino, DT_DIR) < 0)
 			return 0;
-		}
-		++filp->f_pos;
+		filp->f_pos = f_pos = 1;
 		stored++;
 	}
-	if (filp->f_pos == 1) {
-		if (filldir(dirent,"..",2,filp->f_pos,affs_parent_ino(inode),DT_DIR) < 0) {
+	if (f_pos == 1) {
+		if (filldir(dirent, "..", 2, f_pos, filp->f_dentry->d_parent->d_inode->i_ino, DT_DIR) < 0)
 			return stored;
-		}
-		filp->f_pos = 2;
+		filp->f_pos = f_pos = 2;
 		stored++;
 	}
-	chain_pos = (filp->f_pos - 2) & 0xffff;
-	hash_pos  = (filp->f_pos - 2) >> 16;
+
+	down(&AFFS_INODE->i_hash_lock);
+	chain_pos = (f_pos - 2) & 0xffff;
+	hash_pos  = (f_pos - 2) >> 16;
 	if (chain_pos == 0xffff) {
-		affs_warning(inode->i_sb,"readdir","More than 65535 entries in chain");
+		affs_warning(sb, "readdir", "More than 65535 entries in chain");
 		chain_pos = 0;
 		hash_pos++;
 		filp->f_pos = ((hash_pos << 16) | chain_pos) + 2;
 	}
-	if (!(dir_bh = affs_bread(inode->i_dev,inode->i_ino,
-				  AFFS_I2BSIZE(inode))))
-		goto readdir_done;
-
-	while (1) {
-		while (hash_pos < AFFS_I2HSIZE(inode) &&
-		     !((struct dir_front *)dir_bh->b_data)->hashtable[hash_pos])
-			hash_pos++;
-		if (hash_pos >= AFFS_I2HSIZE(inode))
-			break;
-		
-		i = be32_to_cpu(((struct dir_front *)dir_bh->b_data)->hashtable[hash_pos]);
-		j = chain_pos;
-
-		/* If the directory hasn't changed since the last call to readdir(),
-		 * we can jump directly to where we left off.
-		 */
-		if (filp->private_data && filp->f_version == inode->i_version) {
-			i = (s32)(unsigned long)filp->private_data;
-			j = 0;
-			pr_debug("AFFS: readdir() left off=%d\n",i);
+	dir_bh = affs_bread(sb, inode->i_ino);
+	if (!dir_bh)
+		goto readdir_out;
+
+	/* If the directory hasn't changed since the last call to readdir(),
+	 * we can jump directly to where we left off.
+	 */
+	ino = (u32)filp->private_data;
+	if (ino && filp->f_version == inode->i_version) {
+		pr_debug("AFFS: readdir() left off=%d\n", ino);
+		goto inside;
+	}
+
+	ino = be32_to_cpu(AFFS_HEAD(dir_bh)->table[hash_pos]);
+	for (i = 0; ino && i < chain_pos; i++) {
+		fh_bh = affs_bread(sb, ino);
+		if (!fh_bh) {
+			affs_error(sb, "readdir","Cannot read block %d", i);
+			goto readdir_out;
 		}
-		filp->f_version = inode->i_version;
-		pr_debug("AFFS: hash_pos=%d chain_pos=%d\n",hash_pos,chain_pos);
-		while (i) {
-			if (!(fh_bh = affs_bread(inode->i_dev,i,AFFS_I2BSIZE(inode)))) {
-				affs_error(inode->i_sb,"readdir","Cannot read block %d",i);
+		ino = be32_to_cpu(AFFS_TAIL(sb, fh_bh)->hash_chain);
+		affs_brelse(fh_bh);
+		fh_bh = NULL;
+	}
+	if (ino)
+		goto inside;
+	hash_pos++;
+
+	for (; hash_pos < AFFS_SB->s_hashsize; hash_pos++) {
+		ino = be32_to_cpu(AFFS_HEAD(dir_bh)->table[hash_pos]);
+		if (!ino)
+			continue;
+		f_pos = (hash_pos << 16) + 2;
+inside:
+		do {
+			fh_bh = affs_bread(sb, ino);
+			if (!fh_bh) {
+				affs_error(sb, "readdir","Cannot read block %d", ino);
 				goto readdir_done;
 			}
-			ino = i;
-			i   = be32_to_cpu(FILE_END(fh_bh->b_data,inode)->hash_chain);
-			if (j == 0)
-				break;
-			affs_brelse(fh_bh);
-			fh_bh = NULL;
-			j--;
-		}
-		if (fh_bh) {
-			namelen = affs_get_file_name(AFFS_I2BSIZE(inode),fh_bh->b_data,&name);
-			pr_debug("AFFS: readdir(): filldir(\"%.*s\",ino=%lu), i=%d\n",
-				 namelen,name,ino,i);
-			filp->private_data = (void *)ino;
-			if (filldir(dirent,name,namelen,filp->f_pos,ino,DT_UNKNOWN) < 0)
+
+			namelen = MIN(AFFS_TAIL(sb, fh_bh)->name[0], 30);
+			name = AFFS_TAIL(sb, fh_bh)->name + 1;
+			pr_debug("AFFS: readdir(): filldir(\"%.*s\", ino=%u), hash=%d, f_pos=%x\n",
+				 namelen, name, ino, hash_pos, f_pos);
+			if (filldir(dirent, name, namelen, f_pos, ino, DT_UNKNOWN) < 0)
 				goto readdir_done;
-			filp->private_data = (void *)(unsigned long)i;
+			stored++;
+			f_pos++;
+			ino = be32_to_cpu(AFFS_TAIL(sb, fh_bh)->hash_chain);
 			affs_brelse(fh_bh);
 			fh_bh = NULL;
-			stored++;
-		}
-		if (i == 0) {
-			hash_pos++;
-			chain_pos = 0;
-		} else
-			chain_pos++;
-		filp->f_pos = ((hash_pos << 16) | chain_pos) + 2;
+		} while (ino);
 	}
-
 readdir_done:
+	filp->f_pos = f_pos;
+	filp->f_version = inode->i_version;
+	filp->private_data = (void *)ino;
+	res = stored;
+
+readdir_out:
 	affs_brelse(dir_bh);
 	affs_brelse(fh_bh);
-	pr_debug("AFFS: readdir()=%d\n",stored);
-	return stored;
+	up(&AFFS_INODE->i_hash_lock);
+	pr_debug("AFFS: readdir()=%d\n", stored);
+	return res;
 }
diff -urN linux-2.4.4-pre5/fs/affs/file.c linux/fs/affs/file.c
--- linux-2.4.4-pre5/fs/affs/file.c	Fri Feb  9 11:29:44 2001
+++ linux/fs/affs/file.c	Mon Dec  3 22:36:26 2001
@@ -12,7 +12,6 @@
  *  affs regular file handling primitives
  */
 
-#define DEBUG 0
 #include <asm/div64.h>
 #include <asm/uaccess.h>
 #include <asm/system.h>
@@ -31,23 +30,26 @@
 #include <linux/mm.h>
 #include <linux/pagemap.h>
 
-#define MIN(a,b) (((a)<(b))?(a):(b))
-#define MAX(a,b) (((a)>(b))?(a):(b))
-
 #if PAGE_SIZE < 4096
 #error PAGE_SIZE must be at least 4096
 #endif
 
-static struct buffer_head *affs_getblock(struct inode *inode, s32 block);
-static ssize_t affs_file_read_ofs(struct file *filp, char *buf, size_t count, loff_t *ppos);
+static int affs_grow_extcache(struct inode *inode, u32 lc_idx);
+static struct buffer_head *affs_alloc_extblock(struct inode *inode, struct buffer_head *bh, u32 ext);
+static inline struct buffer_head *affs_get_extblock(struct inode *inode, u32 ext);
+static struct buffer_head *affs_get_extblock_slow(struct inode *inode, u32 ext);
+static int affs_get_block(struct inode *inode, long block, struct buffer_head *bh_result, int create);
+
 static ssize_t affs_file_write(struct file *filp, const char *buf, size_t count, loff_t *ppos);
-static ssize_t affs_file_write_ofs(struct file *filp, const char *buf, size_t cnt, loff_t *ppos);
-static int alloc_ext_cache(struct inode *inode);
+static int affs_file_open(struct inode *inode, struct file *filp);
+static int affs_file_release(struct inode *inode, struct file *filp);
 
 struct file_operations affs_file_operations = {
 	read:		generic_file_read,
 	write:		affs_file_write,
 	mmap:		generic_file_mmap,
+	open:		affs_file_open,
+	release:	affs_file_release,
 	fsync:		file_fsync,
 };
 
@@ -56,299 +58,368 @@
 	setattr:	affs_notify_change,
 };
 
-struct file_operations affs_file_operations_ofs = {
-	read:		affs_file_read_ofs,
-	write:		affs_file_write_ofs,
-	fsync:		file_fsync,
-};
-
-#define AFFS_ISINDEX(x)	((x < 129) ||				\
-			 (x < 512 && (x & 1) == 0) ||		\
-			 (x < 1024 && (x & 3) == 0) ||		\
-			 (x < 2048 && (x & 15) == 0) ||		\
-			 (x < 4096 && (x & 63) == 0) ||		\
-			 (x < 20480 && (x & 255) == 0) ||	\
-			 (x < 36864 && (x & 511) == 0))
-
-/* The keys of the extension blocks are stored in a 512-entry
- * deep cache. In order to save memory, not every key of later
- * extension blocks is stored - the larger the file gets, the
- * bigger the holes in between.
- */
-
 static int
-seqnum_to_index(int seqnum)
+affs_file_open(struct inode *inode, struct file *filp)
 {
-	/* All of the first 127 keys are stored */
-	if (seqnum < 128)
-		return seqnum;
-	seqnum -= 128;
-
-	/* Of the next 384 keys, every 2nd is kept */
-	if (seqnum < (192 * 2))
-		return 128 + (seqnum >> 1);
-	seqnum -= 192 * 2;
-	
-	/* Every 4th of the next 512 */
-	if (seqnum < (128 * 4))
-		return 128 + 192 + (seqnum >> 2);
-	seqnum -= 128 * 4;
-
-	/* Every 16th of the next 1024 */
-	if (seqnum < (64 * 16))
-		return 128 + 192 + 128 + (seqnum >> 4);
-	seqnum -= 64 * 16;
-
-	/* Every 64th of the next 2048 */
-	if (seqnum < (32 * 64))
-		return 128 + 192 + 128 + 64 + (seqnum >> 6);
-	seqnum -= 32 * 64;
-
-	/* Every 256th of the next 16384 */
-	if (seqnum < (64 * 256))
-		return 128 + 192 + 128 + 64 + 32 + (seqnum >> 8);
-	seqnum -= 64 * 256;
-
-	/* Every 512th upto 36479 (1.3 GB with 512 byte blocks).
-	 * Seeking to positions behind this will get slower
-	 * than dead snails nailed to the ground. But if
-	 * someone uses files that large with 512-byte blocks,
-	 * he or she deserves no better.
-	 */
-	
-	if (seqnum > (31 * 512))
-		seqnum = 31 * 512;
-	return 128 + 192 + 128 + 64 + 32 + 64 + (seqnum >> 9);
-}
-
-/* Now the other way round: Calculate the sequence
- * number of an extension block of a key at the
- * given index in the cache.
- */
+	if (atomic_read(&filp->f_count) != 1)
+		return 0;
+	pr_debug("AFFS: open(%d)\n", AFFS_INODE->i_opencnt);
+	AFFS_INODE->i_opencnt++;
+	return 0;
+}
 
 static int
-index_to_seqnum(int index)
+affs_file_release(struct inode *inode, struct file *filp)
 {
-	if (index < 128)
-		return index;
-	index -= 128;
-	if (index < 192)
-		return 128 + (index << 1);
-	index -= 192;
-	if (index < 128)
-		return 128 + 192 * 2 + (index << 2);
-	index -= 128;
-	if (index < 64)
-		return 128 + 192 * 2 + 128 * 4 + (index << 4);
-	index -= 64;
-	if (index < 32)
-		return 128 + 192 * 2 + 128 * 4 + 64 * 16 + (index << 6);
-	index -= 32;
-	if (index < 64)
-		return 128 + 192 * 2 + 128 * 4 + 64 * 16 + 32 * 64 + (index << 8);
-	index -= 64;
-	return 128 + 192 * 2 + 128 * 4 + 64 * 16 + 32 * 64 + 64 * 256 + (index << 9);
-}
-
-static s32 __inline__
-calc_key(struct inode *inode, int *ext)
-{
-	int		  index;
-	struct key_cache *kc;
-
-	for (index = 0; index < 4; index++) {
-		kc = &inode->u.affs_i.i_ec->kc[index];
-		if (kc->kc_last == -1)
-			continue;	/* don't look in cache if invalid. */
-		if (*ext == kc->kc_this_seq) {
-			return kc->kc_this_key;
-		} else if (*ext == kc->kc_this_seq + 1) {
-			if (kc->kc_next_key)
-				return kc->kc_next_key;
-			else {
-				(*ext)--;
-				return kc->kc_this_key;
-			}
-		}
-	}
-	index = seqnum_to_index(*ext);
-	if (index > inode->u.affs_i.i_ec->max_ext)
-		index = inode->u.affs_i.i_ec->max_ext;
-	*ext = index_to_seqnum(index);
-	return inode->u.affs_i.i_ec->ec[index];
+	if (atomic_read(&filp->f_count) != 0)
+		return 0;
+	pr_debug("AFFS: release(%d)\n", AFFS_INODE->i_opencnt);
+	AFFS_INODE->i_opencnt--;
+	if (!AFFS_INODE->i_opencnt)
+		affs_free_prealloc(inode);
+
+	return 0;
 }
 
-int
-affs_bmap(struct inode *inode, int block)
+static int
+affs_grow_extcache(struct inode *inode, u32 lc_idx)
 {
+	struct super_block	*sb = inode->i_sb;
 	struct buffer_head	*bh;
-	s32			 key, nkey;
-	s32			 ptype, stype;
-	int			 ext;
-	int			 index;
-	int			 keycount;
-	struct key_cache	*kc;
-	struct key_cache	*tkc;
-	struct timeval		 tv;
-	s32			*keyp;
-	int			 i;
-
-	pr_debug("AFFS: bmap(%lu,%d)\n",inode->i_ino,block);
-
-	lock_kernel();
-	if (block < 0) {
-		affs_error(inode->i_sb,"bmap","Block < 0");
-		goto out_fail;
-	}
-	if (!inode->u.affs_i.i_ec) {
-		if (alloc_ext_cache(inode)) {
-			goto out_fail;
-		}
-	}
+	u32 lc_max;
+	int i, j, key;
 
-	/* Try to find the requested key in the cache.
-	 * In order to speed this up as much as possible,
-	 * the cache line lookup is done in a separate
-	 * step.
-	 */
-
-	for (i = 0; i < 4; i++) {
-		tkc = &inode->u.affs_i.i_ec->kc[i];
-		/* Look in any cache if the key is there */
-		if (block <= tkc->kc_last && block >= tkc->kc_first) {
-			unlock_kernel();
-			return tkc->kc_keys[block - tkc->kc_first];
-		}
-	}
-	kc = NULL;
-	tv = xtime;
-	for (i = 0; i < 4; i++) {
-		tkc = &inode->u.affs_i.i_ec->kc[i];
-		if (tkc->kc_lru_time.tv_sec > tv.tv_sec)
+	if (!AFFS_INODE->i_lc) {
+		char *ptr = (char *)get_zeroed_page(GFP_BUFFER);
+		if (!ptr)
+			return -ENOMEM;
+		AFFS_INODE->i_lc = (u32 *)ptr;
+		AFFS_INODE->i_ac = (struct affs_ext_key *)(ptr + AFFS_CACHE_SIZE / 2);
+	}
+
+	lc_max = AFFS_LC_SIZE << AFFS_INODE->i_lc_shift;
+
+	if (AFFS_INODE->i_extcnt > lc_max) {
+		u32 lc_shift, lc_mask, tmp, off;
+
+		/* need to recalculate linear cache, start from old size */
+		lc_shift = AFFS_INODE->i_lc_shift;
+		tmp = (AFFS_INODE->i_extcnt / AFFS_LC_SIZE) >> lc_shift;
+		for (; tmp; tmp >>= 1)
+			lc_shift++;
+		lc_mask = (1 << lc_shift) - 1;
+
+		/* fix idx and old size to new shift */
+		lc_idx >>= (lc_shift - AFFS_INODE->i_lc_shift);
+		AFFS_INODE->i_lc_size >>= (lc_shift - AFFS_INODE->i_lc_shift);
+
+		/* first shrink old cache to make more space */
+		off = 1 << (lc_shift - AFFS_INODE->i_lc_shift);
+		for (i = 1, j = off; j < AFFS_LC_SIZE; i++, j += off)
+			AFFS_INODE->i_ac[i] = AFFS_INODE->i_ac[j];
+
+		AFFS_INODE->i_lc_shift = lc_shift;
+		AFFS_INODE->i_lc_mask = lc_mask;
+	}
+
+	/* fill cache to the needed index */
+	i = AFFS_INODE->i_lc_size;
+	AFFS_INODE->i_lc_size = lc_idx + 1;
+	for (; i <= lc_idx; i++) {
+		if (!i) {
+			AFFS_INODE->i_lc[0] = inode->i_ino;
 			continue;
-		if (tkc->kc_lru_time.tv_sec < tv.tv_sec ||
-		    tkc->kc_lru_time.tv_usec < tv.tv_usec) {
-			kc = tkc;
-			tv = tkc->kc_lru_time;
 		}
+		key = AFFS_INODE->i_lc[i - 1];
+		j = AFFS_INODE->i_lc_mask + 1;
+		// unlock cache
+		for (; j > 0; j--) {
+			bh = affs_bread(sb, key);
+			if (!bh)
+				goto err;
+			key = be32_to_cpu(AFFS_TAIL(sb, bh)->extension);
+			affs_brelse(bh);
+		}
+		// lock cache
+		AFFS_INODE->i_lc[i] = key;
 	}
-	if (!kc)	/* Really shouldn't happen */
-		kc = tkc;
-	kc->kc_lru_time = xtime;
-	keyp            = kc->kc_keys;
-	kc->kc_first    = block;
-	kc->kc_last     = -1;
-	keycount        = AFFS_KCSIZE;
-
-	/* Calculate sequence number of the extension block where the
-	 * number of the requested block is stored. 0 means it's in
-	 * the file header.
-	 */
-
-	ext    = block / AFFS_I2HSIZE(inode);
-	key    = calc_key(inode,&ext);
-	block -= ext * AFFS_I2HSIZE(inode);
 
-	for (;;) {
-		bh = affs_bread(inode->i_dev,key,AFFS_I2BSIZE(inode));
-		if (!bh)
-			goto out_fail;
+	return 0;
 
-		index = seqnum_to_index(ext);
-		if (index > inode->u.affs_i.i_ec->max_ext &&
-		    (affs_checksum_block(AFFS_I2BSIZE(inode),bh->b_data,&ptype,&stype) ||
-		     (ptype != T_SHORT && ptype != T_LIST) || stype != ST_FILE)) {
-			affs_brelse(bh);
-			goto out_fail;
-		}
-		nkey = be32_to_cpu(FILE_END(bh->b_data,inode)->extension);
-		if (block < AFFS_I2HSIZE(inode)) {
-			/* Fill cache as much as possible */
-			if (keycount) {
-				kc->kc_first = ext * AFFS_I2HSIZE(inode) + block;
-				keycount     = keycount < AFFS_I2HSIZE(inode) - block ? keycount :
-						AFFS_I2HSIZE(inode) - block;
-				for (i = 0; i < keycount; i++)
-					kc->kc_keys[i] = be32_to_cpu(AFFS_BLOCK(bh->b_data,inode,block + i));
-				kc->kc_last = kc->kc_first + i - 1;
-			}
-			break;
-		}
-		block -= AFFS_I2HSIZE(inode);
-		affs_brelse(bh);
-		ext++;
-		if (index > inode->u.affs_i.i_ec->max_ext && AFFS_ISINDEX(ext)) {
-			inode->u.affs_i.i_ec->ec[index] = nkey;
-			inode->u.affs_i.i_ec->max_ext   = index;
-		}
-		key = nkey;
+err:
+	// lock cache
+	return -EIO;
+}
+
+static struct buffer_head *
+affs_alloc_extblock(struct inode *inode, struct buffer_head *bh, u32 ext)
+{
+	struct super_block *sb = inode->i_sb;
+	struct buffer_head *new_bh;
+	u32 blocknr, tmp;
+
+	blocknr = affs_alloc_block(inode, bh->b_blocknr);
+	if (!blocknr)
+		return ERR_PTR(-ENOSPC);
+
+	new_bh = affs_getzeroblk(sb, blocknr);
+	if (!new_bh) {
+		affs_free_block(sb, blocknr);
+		return ERR_PTR(-EIO);
 	}
-	kc->kc_this_key = key;
-	kc->kc_this_seq = ext;
-	kc->kc_next_key = nkey;
-	key = be32_to_cpu(AFFS_BLOCK(bh->b_data,inode,block));
-	affs_brelse(bh);
-out:
-	unlock_kernel();
-	return key;
 
-out_fail:
-	key=0;
-	goto out;
+	AFFS_HEAD(new_bh)->ptype = cpu_to_be32(T_LIST);
+	AFFS_HEAD(new_bh)->key = cpu_to_be32(blocknr);
+	AFFS_TAIL(sb, new_bh)->stype = cpu_to_be32(ST_FILE);
+	AFFS_TAIL(sb, new_bh)->parent = cpu_to_be32(inode->i_ino);
+	affs_fix_checksum(sb, new_bh);
+
+	mark_buffer_dirty(new_bh);
+
+	tmp = be32_to_cpu(AFFS_TAIL(sb, bh)->extension);
+	if (tmp)
+		affs_warning(sb, "alloc_ext", "previous extension set (%x)", tmp);
+	AFFS_TAIL(sb, bh)->extension = cpu_to_be32(blocknr);
+	affs_adjust_checksum(bh, blocknr - tmp);
+	mark_buffer_dirty(bh);
+
+	AFFS_INODE->i_extcnt++;
+	mark_inode_dirty(inode);
+
+	return new_bh;
 }
 
+static inline struct buffer_head *
+affs_get_extblock(struct inode *inode, u32 ext)
+{
+	/* inline the simplest case: same extended block as last time */
+	struct buffer_head *bh = AFFS_INODE->i_ext_bh;
+	if (ext == AFFS_INODE->i_ext_last)
+		atomic_inc(&bh->b_count);
+	else
+		/* we have to do more (not inlined) */
+		bh = affs_get_extblock_slow(inode, ext);
 
-static int affs_get_block(struct inode *inode, long block, struct buffer_head *bh_result, int create)
+	return bh;
+}
+
+static struct buffer_head *
+affs_get_extblock_slow(struct inode *inode, u32 ext)
 {
-	int err, phys=0, new=0;
+	struct super_block *sb = inode->i_sb;
+	struct buffer_head *bh;
+	u32 ext_key;
+	u32 lc_idx, lc_off, ac_idx;
+	u32 tmp, idx;
+
+	if (ext == AFFS_INODE->i_ext_last + 1) {
+		/* read the next extended block from the current one */
+		bh = AFFS_INODE->i_ext_bh;
+		ext_key = be32_to_cpu(AFFS_TAIL(sb, bh)->extension);
+		if (ext < AFFS_INODE->i_extcnt)
+			goto read_ext;
+		if (ext > AFFS_INODE->i_extcnt)
+			BUG();
+		bh = affs_alloc_extblock(inode, bh, ext);
+		if (IS_ERR(bh))
+			return bh;
+		goto store_ext;
+	}
+
+	if (ext == 0) {
+		/* we seek back to the file header block */
+		ext_key = inode->i_ino;
+		goto read_ext;
+	}
+
+	if (ext >= AFFS_INODE->i_extcnt) {
+		struct buffer_head *prev_bh;
+
+		/* allocate a new extended block */
+		if (ext > AFFS_INODE->i_extcnt)
+			BUG();
+
+		/* get previous extended block */
+		prev_bh = affs_get_extblock(inode, ext - 1);
+		if (IS_ERR(prev_bh))
+			return prev_bh;
+		bh = affs_alloc_extblock(inode, prev_bh, ext);
+		affs_brelse(prev_bh);
+		if (IS_ERR(bh))
+			return bh;
+		goto store_ext;
+	}
+
+again:
+	/* check if there is an extended cache and whether it's large enough */
+	lc_idx = ext >> AFFS_INODE->i_lc_shift;
+	lc_off = ext & AFFS_INODE->i_lc_mask;
+
+	if (lc_idx >= AFFS_INODE->i_lc_size) {
+		int err;
+
+		err = affs_grow_extcache(inode, lc_idx);
+		if (err)
+			return ERR_PTR(err);
+		goto again;
+	}
+
+	/* every n'th key we find in the linear cache */
+	if (!lc_off) {
+		ext_key = AFFS_INODE->i_lc[lc_idx];
+		goto read_ext;
+	}
+
+	/* maybe it's still in the associative cache */
+	ac_idx = (ext - lc_idx - 1) & AFFS_AC_MASK;
+	if (AFFS_INODE->i_ac[ac_idx].ext == ext) {
+		ext_key = AFFS_INODE->i_ac[ac_idx].key;
+		goto read_ext;
+	}
+
+	/* try to find one of the previous extended blocks */
+	tmp = ext;
+	idx = ac_idx;
+	while (--tmp, --lc_off > 0) {
+		idx = (idx - 1) & AFFS_AC_MASK;
+		if (AFFS_INODE->i_ac[idx].ext == tmp) {
+			ext_key = AFFS_INODE->i_ac[idx].key;
+			goto find_ext;
+		}
+	}
+
+	/* fall back to the linear cache */
+	ext_key = AFFS_INODE->i_lc[lc_idx];
+find_ext:
+	/* read all extended blocks until we find the one we need */
+	//unlock cache
+	do {
+		bh = affs_bread(sb, ext_key);
+		if (!bh)
+			goto err_bread;
+		ext_key = be32_to_cpu(AFFS_TAIL(sb, bh)->extension);
+		affs_brelse(bh);
+		tmp++;
+	} while (tmp < ext);
+	//lock cache
+
+	/* store it in the associative cache */
+	// recalculate ac_idx?
+	AFFS_INODE->i_ac[ac_idx].ext = ext;
+	AFFS_INODE->i_ac[ac_idx].key = ext_key;
+
+read_ext:
+	/* finally read the right extended block */
+	//unlock cache
+	bh = affs_bread(sb, ext_key);
+	if (!bh)
+		goto err_bread;
+	//lock cache
+
+store_ext:
+	/* release old cached extended block and store the new one */
+	affs_brelse(AFFS_INODE->i_ext_bh);
+	AFFS_INODE->i_ext_last = ext;
+	AFFS_INODE->i_ext_bh = bh;
+	atomic_inc(&bh->b_count);
 
-	if (!create) {
-		phys = affs_bmap(inode, block);
-		if (phys) {
-			bh_result->b_dev = inode->i_dev;
-			bh_result->b_blocknr = phys;
-			bh_result->b_state |= (1UL << BH_Mapped);
-		}
-		return 0;
-	}
+	return bh;
 
-	err = -EIO;
-	lock_kernel();
-	if (block < 0)
-		goto abort_negative;
+err_bread:
+	affs_brelse(bh);
+	return ERR_PTR(-EIO);
+}
 
-	if (affs_getblock(inode, block)==NULL) {
-		err = -EIO;
-		goto abort;
-	}
+static int
+affs_get_block(struct inode *inode, long block, struct buffer_head *bh_result, int create)
+{
+	struct super_block	*sb = inode->i_sb;
+	struct buffer_head	*ext_bh;
+	u32			 ext;
+
+	pr_debug("AFFS: get_block(%u, %ld)\n", (u32)inode->i_ino, block);
 
+	if (block < 0)
+		goto err_small;
+
+	if (block >= AFFS_INODE->i_blkcnt) {
+		if (block > AFFS_INODE->i_blkcnt || !create)
+			goto err_big;
+	} else
+		create = 0;
+
+	//lock cache
+	down(&AFFS_INODE->i_ext_lock);
+
+	ext = block / AFFS_SB->s_hashsize;
+	block -= ext * AFFS_SB->s_hashsize;
+	ext_bh = affs_get_extblock(inode, ext);
+	if (IS_ERR(ext_bh))
+		goto err_ext;
+	bh_result->b_blocknr = be32_to_cpu(AFFS_BLOCK(sb, ext_bh, block));
 	bh_result->b_dev = inode->i_dev;
-	bh_result->b_blocknr = phys;
 	bh_result->b_state |= (1UL << BH_Mapped);
-	if (new)
+
+	if (create) {
+		u32 blocknr = affs_alloc_block(inode, ext_bh->b_blocknr);
+		if (!blocknr)
+			goto err_alloc;
 		bh_result->b_state |= (1UL << BH_New);
-	
-abort:
-	unlock_kernel();
-	return err;
+		AFFS_INODE->mmu_private += AFFS_SB->s_data_blksize;
+		AFFS_INODE->i_blkcnt++;
 
-abort_negative:
-	affs_error(inode->i_sb,"affs_get_block","Block < 0");
-	goto abort;
+		/* store new block */
+		if (bh_result->b_blocknr)
+			affs_warning(sb, "get_block", "block already set (%x)", bh_result->b_blocknr);
+		AFFS_BLOCK(sb, ext_bh, block) = cpu_to_be32(blocknr);
+		AFFS_HEAD(ext_bh)->block_count = cpu_to_be32(block + 1);
+		affs_adjust_checksum(ext_bh, blocknr - bh_result->b_blocknr + 1);
+		bh_result->b_blocknr = blocknr;
+
+		if (!block) {
+			/* insert first block into header block */
+			u32 tmp = be32_to_cpu(AFFS_HEAD(ext_bh)->first_data);
+			if (tmp)
+				affs_warning(sb, "get_block", "first block already set (%d)", tmp);
+			AFFS_HEAD(ext_bh)->first_data = cpu_to_be32(blocknr);
+			affs_adjust_checksum(ext_bh, blocknr - tmp);
+		}
+	}
 
+	affs_brelse(ext_bh);
+	//unlock cache
+	up(&AFFS_INODE->i_ext_lock);
+	return 0;
+
+err_small:
+	affs_error(inode->i_sb,"get_block","Block < 0");
+	return -EIO;
+err_big:
+	affs_error(inode->i_sb,"get_block","strange block request %d", block);
+	return -EIO;
+err_ext:
+	// unlock cache
+	up(&AFFS_INODE->i_ext_lock);
+	return PTR_ERR(ext_bh);
+err_alloc:
+	brelse(ext_bh);
+	bh_result->b_state &= ~(1UL << BH_Mapped);
+	// unlock cache
+	up(&AFFS_INODE->i_ext_lock);
+	return -ENOSPC;
 }
-		
+
 static int affs_writepage(struct page *page)
 {
-	return block_write_full_page(page,affs_get_block);
+	return block_write_full_page(page, affs_get_block);
 }
 static int affs_readpage(struct file *file, struct page *page)
 {
-	return block_read_full_page(page,affs_get_block);
+	return block_read_full_page(page, affs_get_block);
 }
 static int affs_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to)
 {
-	return cont_prepare_write(page,from,to,affs_get_block,
+	return cont_prepare_write(page, from, to, affs_get_block,
 		&page->mapping->host->u.affs_i.mmu_private);
 }
 static int _affs_bmap(struct address_space *mapping, long block)
@@ -364,227 +435,61 @@
 	bmap: _affs_bmap
 };
 
-/* With the affs, getting a random block from a file is not
- * a simple business. Since this fs does not allow holes,
- * it may be necessary to allocate all the missing blocks
- * in between, as well as some new extension blocks. The OFS
- * is even worse: All data blocks contain pointers to the
- * next ones, so you have to fix [n-1] after allocating [n].
- * What a mess.
- */
-
-static struct buffer_head * affs_getblock(struct inode *inode, s32 block)
+static inline struct buffer_head *
+affs_bread_ino(struct inode *inode, int block, int create)
 {
-	struct super_block	*sb = inode->i_sb;
-	int			 ofs = sb->u.affs_sb.s_flags & SF_OFS;
-	int			 ext = block / AFFS_I2HSIZE(inode);
-	struct buffer_head	*bh, *ebh, *pbh = NULL;
-	struct key_cache	*kc;
-	s32			 key, nkey;
-	int			 cf, j, pt;
-	int			 index;
-	int			 err;
-
-	pr_debug("AFFS: getblock(%lu,%d)\n",inode->i_ino,block);
-
-	key    = calc_key(inode,&ext);
-	block -= ext * AFFS_I2HSIZE(inode);
-	pt     = ext ? T_LIST : T_SHORT;
-
-	/* Key refers now to the last known extension block,
-	 * ext is its sequence number (if 0, key refers to the
-	 * header block), and block is the block number relative
-	 * to the first block stored in that extension block.
-	 */
-	for (;;) {	/* Loop over header block and extension blocks */
-		struct file_front *fdp;
+	struct buffer_head *bh, tmp_bh;
+	int err;
 
-		bh = affs_bread(inode->i_dev,key,AFFS_I2BSIZE(inode));
-		if (!bh)
-			goto out_fail;
-		fdp = (struct file_front *) bh->b_data;
-		err = affs_checksum_block(AFFS_I2BSIZE(inode),bh->b_data,&cf,&j);
-		if (err || cf != pt || j != ST_FILE) {
-		    	affs_error(sb, "getblock",
-				"Block %d is not a valid %s", key,
-				pt == T_SHORT ? "file header" : "ext block");
-			goto out_free_bh;
-		}
-		j  = be32_to_cpu(((struct file_front *)bh->b_data)->block_count);
-		for (cf = 0; j < AFFS_I2HSIZE(inode) && j <= block; j++) {
-			if (ofs && !pbh && inode->u.affs_i.i_lastblock >= 0) {
-				if (j > 0) {
-					s32 k = AFFS_BLOCK(bh->b_data, inode, j - 1);
-					pbh = affs_bread(inode->i_dev,
-							be32_to_cpu(k),
-							AFFS_I2BSIZE(inode));
-				} else
-					pbh = affs_getblock(inode,inode->u.affs_i.i_lastblock);
-				if (!pbh) {
-					affs_error(sb,"getblock", "Cannot get last block in file");
-					break;
-				}
-			}
-			nkey = affs_new_data(inode);
-			if (!nkey)
-				break;
-			inode->u.affs_i.i_lastblock++;
-			if (AFFS_BLOCK(bh->b_data,inode,j)) {
-				affs_warning(sb,"getblock","Block already allocated");
-				affs_free_block(sb,nkey);
-				continue;
-			}
-			AFFS_BLOCK(bh->b_data,inode,j) = cpu_to_be32(nkey);
-			if (ofs) {
-				ebh = affs_bread(inode->i_dev,nkey,AFFS_I2BSIZE(inode));
-				if (!ebh) {
-					affs_error(sb,"getblock", "Cannot get block %d",nkey);
-					affs_free_block(sb,nkey);
-					AFFS_BLOCK(bh->b_data,inode,j) = 0;
-					break;
-				}
-				DATA_FRONT(ebh)->primary_type    = cpu_to_be32(T_DATA);
-				DATA_FRONT(ebh)->header_key      = cpu_to_be32(inode->i_ino);
-				DATA_FRONT(ebh)->sequence_number = cpu_to_be32(inode->u.affs_i.i_lastblock + 1);
-				affs_fix_checksum(AFFS_I2BSIZE(inode), ebh->b_data, 5);
-				mark_buffer_dirty(ebh);
-				if (pbh) {
-					DATA_FRONT(pbh)->data_size = cpu_to_be32(AFFS_I2BSIZE(inode) - 24);
-					DATA_FRONT(pbh)->next_data = cpu_to_be32(nkey);
-					affs_fix_checksum(AFFS_I2BSIZE(inode),pbh->b_data,5);
-					mark_buffer_dirty(pbh);
-					affs_brelse(pbh);
-				}
-				pbh = ebh;
-			}
-			cf = 1;
-		}
-		/* N.B. May need to release pbh after here */
-
-		if (cf) {
-			if (pt == T_SHORT)
-				fdp->first_data = AFFS_BLOCK(bh->b_data,inode,0);
-			fdp->block_count = cpu_to_be32(j);
-			affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5);
-			mark_buffer_dirty(bh);
+	tmp_bh.b_state = 0;
+	err = affs_get_block(inode, block, &tmp_bh, create);
+	if (!err) {
+		bh = affs_bread(inode->i_sb, tmp_bh.b_blocknr);
+		if (bh) {
+			bh->b_state |= tmp_bh.b_state;
+			return bh;
 		}
+		err = -EIO;
+	}
+	return ERR_PTR(err);
+}
 
-		if (block < j) {
-			if (pbh)
-				affs_brelse(pbh);
-			break;
-		}
-		if (j < AFFS_I2HSIZE(inode)) {
-			/* N.B. What about pbh here? */
-			goto out_free_bh;
-		}
+static inline struct buffer_head *
+affs_getzeroblk_ino(struct inode *inode, int block)
+{
+	struct buffer_head *bh, tmp_bh;
+	int err;
 
-		block -= AFFS_I2HSIZE(inode);
-		key    = be32_to_cpu(FILE_END(bh->b_data,inode)->extension);
-		if (!key) {
-			key = affs_new_header(inode);
-			if (!key)
-				goto out_free_bh;
-			ebh = affs_bread(inode->i_dev,key,AFFS_I2BSIZE(inode));
-			if (!ebh) {
-				/* N.B. must free bh here */
-				goto out_free_block;
-			}
-			((struct file_front *)ebh->b_data)->primary_type = cpu_to_be32(T_LIST);
-			((struct file_front *)ebh->b_data)->own_key      = cpu_to_be32(key);
-			FILE_END(ebh->b_data,inode)->secondary_type      = cpu_to_be32(ST_FILE);
-			FILE_END(ebh->b_data,inode)->parent              = cpu_to_be32(inode->i_ino);
-			affs_fix_checksum(AFFS_I2BSIZE(inode),ebh->b_data,5);
-			mark_buffer_dirty(ebh);
-			FILE_END(bh->b_data,inode)->extension = cpu_to_be32(key);
-			affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5);
-			mark_buffer_dirty(bh);
-			affs_brelse(bh);
-			bh = ebh;
-		}
-		pt = T_LIST;
-		ext++;
-		index = seqnum_to_index(ext);
-		if (index > inode->u.affs_i.i_ec->max_ext &&
-		    AFFS_ISINDEX(ext)) {
-			inode->u.affs_i.i_ec->ec[index] = key;
-			inode->u.affs_i.i_ec->max_ext   = index;
+	tmp_bh.b_state = 0;
+	err = affs_get_block(inode, block, &tmp_bh, 1);
+	if (!err) {
+		bh = affs_getzeroblk(inode->i_sb, tmp_bh.b_blocknr);
+		if (bh) {
+			bh->b_state |= tmp_bh.b_state;
+			return bh;
 		}
-		affs_brelse(bh);
-	}
-
-	/* Invalidate key cache */
-	for (j = 0; j < 4; j++) {
-		kc = &inode->u.affs_i.i_ec->kc[j];
-		kc->kc_last = -1;
+		err = -EIO;
 	}
-	key = be32_to_cpu(AFFS_BLOCK(bh->b_data,inode,block));
-	affs_brelse(bh);
-	if (!key)
-		goto out_fail;
-
-	bh = affs_bread(inode->i_dev, key, AFFS_I2BSIZE(inode));
-	return bh;
-
-out_free_block:
-	affs_free_block(sb, key);
-out_free_bh:
-	affs_brelse(bh);
-out_fail:
-	return NULL;
+	return ERR_PTR(err);
 }
 
-static ssize_t
-affs_file_read_ofs(struct file *filp, char *buf, size_t count, loff_t *ppos)
+static inline struct buffer_head *
+affs_getemptyblk_ino(struct inode *inode, int block)
 {
-	struct inode		*inode = filp->f_dentry->d_inode;
-	char			*start;
-	ssize_t			 left, offset, size, sector;
-	ssize_t			 blocksize;
-	struct buffer_head	*bh;
-	void			*data;
-	loff_t		tmp;
-
-	pr_debug("AFFS: file_read_ofs(ino=%lu,pos=%lu,%d)\n",inode->i_ino,
-		 (unsigned long)*ppos,count);
-
-	if (!inode) {
-		affs_error(inode->i_sb,"file_read_ofs","Inode = NULL");
-		return -EINVAL;
-	}
-	blocksize = AFFS_I2BSIZE(inode) - 24;
-	if (!(S_ISREG(inode->i_mode))) {
-		pr_debug("AFFS: file_read: mode = %07o",inode->i_mode);
-		return -EINVAL;
-	}
-	if (*ppos >= inode->i_size || count <= 0)
-		return 0;
+	struct buffer_head *bh, tmp_bh;
+	int err;
 
-	start = buf;
-	for (;;) {
-		left = MIN (inode->i_size - *ppos,count - (buf - start));
-		if (!left)
-			break;
-		tmp = *ppos;
-		do_div(tmp, blocksize);
-		sector = affs_bmap(inode, tmp);
-		if (!sector)
-			break;
-		tmp = *ppos;
-		offset = do_div(tmp, blocksize);
-		bh = affs_bread(inode->i_dev,sector,AFFS_I2BSIZE(inode));
-		if (!bh)
-			break;
-		data = bh->b_data + 24;
-		size = MIN(blocksize - offset,left);
-		*ppos += size;
-		copy_to_user(buf,data + offset,size);
-		buf += size;
-		affs_brelse(bh);
+	tmp_bh.b_state = 0;
+	err = affs_get_block(inode, block, &tmp_bh, 1);
+	if (!err) {
+		bh = affs_getemptyblk(inode->i_sb, tmp_bh.b_blocknr);
+		if (bh) {
+			bh->b_state |= tmp_bh.b_state;
+			return bh;
+		}
+		err = -EIO;
 	}
-	if (start == buf)
-		return -EIO;
-	return buf - start;
+	return ERR_PTR(err);
 }
 
 static ssize_t
@@ -601,232 +506,388 @@
 	return retval;
 }
 
-static ssize_t
-affs_file_write_ofs(struct file *file, const char *buf, size_t count, loff_t *ppos)
+static int
+affs_do_readpage_ofs(struct file *file, struct page *page, unsigned from, unsigned to)
 {
-	ssize_t retval;
-
-	retval = generic_file_write (file, buf, count, ppos);
-	if (retval >0) {
-		struct inode *inode = file->f_dentry->d_inode;
-		inode->i_ctime = inode->i_mtime = CURRENT_TIME;
-		mark_inode_dirty(inode);
+	struct inode *inode = file->f_dentry->d_inode;
+	struct super_block *sb = inode->i_sb;
+	struct buffer_head *bh;
+	char *data;
+	u32 bidx, boff, bsize;
+	u32 tmp;
+
+	pr_debug("AFFS: read_page(%u, %ld, %d, %d)\n", (u32)inode->i_ino, page->index, from, to);
+	data = page_address(page);
+	bsize = AFFS_SB->s_data_blksize;
+	tmp = (page->index << PAGE_CACHE_SHIFT) + from;
+	bidx = tmp / bsize;
+	boff = tmp % bsize;
+
+	while (from < to) {
+		bh = affs_bread_ino(inode, bidx, 0);
+		if (IS_ERR(bh))
+			return PTR_ERR(bh);
+		tmp = MIN(bsize - boff, from - to);
+		memcpy(data + from, AFFS_DATA(bh) + boff, tmp);
+		affs_brelse(bh);
+		bidx++;
+		from += tmp;
+		boff = 0;
 	}
-	return retval;
+	return 0;
 }
 
-/* Free any preallocated blocks. */
-
-void
-affs_free_prealloc(struct inode *inode)
+static int
+affs_extent_file_ofs(struct file *file, u32 newsize)
 {
-	struct super_block	*sb = inode->i_sb;
-	struct affs_zone	*zone;
-	int block;
-
-	pr_debug("AFFS: free_prealloc(ino=%lu)\n", inode->i_ino);
-
-	while (inode->u.affs_i.i_pa_cnt) {	
-		block = inode->u.affs_i.i_data[inode->u.affs_i.i_pa_next++];
-		inode->u.affs_i.i_pa_next &= AFFS_MAX_PREALLOC - 1;
-		inode->u.affs_i.i_pa_cnt--;
-		affs_free_block(sb, block);
-	}
-	if (inode->u.affs_i.i_zone) {
-		zone = &sb->u.affs_sb.s_zones[inode->u.affs_i.i_zone];
-		if (zone->z_ino == inode->i_ino)
-			zone->z_ino = 0;
+	struct inode *inode = file->f_dentry->d_inode;
+	struct super_block *sb = inode->i_sb;
+	struct buffer_head *bh, *prev_bh;
+	u32 bidx, boff;
+	u32 size, bsize;
+	u32 tmp;
+
+	pr_debug("AFFS: extent_file(%u, %d)\n", (u32)inode->i_ino, newsize);
+	bsize = AFFS_SB->s_data_blksize;
+	bh = NULL;
+	size = inode->i_size;
+	bidx = size / bsize;
+	boff = size % bsize;
+	if (boff) {
+		bh = affs_bread_ino(inode, bidx, 0);
+		if (IS_ERR(bh))
+			return PTR_ERR(bh);
+		tmp = MIN(bsize - boff, newsize - size);
+		memset(AFFS_DATA(bh) + boff, 0, tmp);
+		AFFS_DATA_HEAD(bh)->size = cpu_to_be32(be32_to_cpu(AFFS_DATA_HEAD(bh)->size) + tmp);
+		affs_fix_checksum(sb, bh);
+		mark_buffer_dirty(bh);
+		size += tmp;
+		bidx++;
+	} else if (bidx) {
+		bh = affs_bread_ino(inode, bidx - 1, 0);
+		if (IS_ERR(bh))
+			return PTR_ERR(bh);
+	}
+
+	while (size < newsize) {
+		prev_bh = bh;
+		bh = affs_getzeroblk_ino(inode, bidx);
+		if (IS_ERR(bh))
+			goto out;
+		tmp = MIN(bsize, newsize - size);
+		AFFS_DATA_HEAD(bh)->ptype = cpu_to_be32(T_DATA);
+		AFFS_DATA_HEAD(bh)->key = cpu_to_be32(inode->i_ino);
+		AFFS_DATA_HEAD(bh)->sequence = cpu_to_be32(bidx);
+		AFFS_DATA_HEAD(bh)->size = cpu_to_be32(tmp);
+		affs_fix_checksum(sb, bh);
+		mark_buffer_dirty(bh);
+		if (prev_bh) {
+			u32 tmp = be32_to_cpu(AFFS_DATA_HEAD(prev_bh)->next);
+			if (tmp)
+				affs_warning(sb, "prepare_write_ofs", "next block already set for %d (%d)", bidx, tmp);
+			AFFS_DATA_HEAD(prev_bh)->next = cpu_to_be32(bh->b_blocknr);
+			affs_adjust_checksum(prev_bh, bidx - tmp);
+			mark_buffer_dirty(prev_bh);
+			affs_brelse(prev_bh);
+		}
+		size += bsize;
+		bidx++;
 	}
+	affs_brelse(bh);
+	inode->i_size = AFFS_INODE->mmu_private = size;
+	return 0;
+
+out:
+	inode->i_size = AFFS_INODE->mmu_private = size;
+	return PTR_ERR(bh);
 }
 
-/* Truncate (or enlarge) a file to the requested size. */
+static int
+affs_readpage_ofs(struct file *file, struct page *page)
+{
+	struct inode *inode = file->f_dentry->d_inode;
+	u32 to;
+	int err;
+
+	pr_debug("AFFS: read_page(%u, %ld)\n", (u32)inode->i_ino, page->index);
+	to = PAGE_CACHE_SIZE;
+	if (((page->index + 1) << PAGE_CACHE_SHIFT) > inode->i_size) {
+		to = inode->i_size & ~PAGE_CACHE_MASK;
+		memset(page_address(page) + to, 0, PAGE_CACHE_SIZE - to);
+	}
+
+	err = affs_do_readpage_ofs(file, page, 0, to);
+	if (!err)
+		SetPageUptodate(page);
+	UnlockPage(page);
+	return err;
+}
 
-void
-affs_truncate(struct inode *inode)
+static int affs_prepare_write_ofs(struct file *file, struct page *page, unsigned from, unsigned to)
 {
-	struct buffer_head	*bh = NULL;
-	int	 first;			/* First block to be thrown away	*/
-	int	 block;
-	s32	 key;
-	s32	*keyp;
-	s32	 ekey;
-	s32	 ptype, stype;
-	int	 freethis;
-	int	 net_blocksize;
-	int	 blocksize = AFFS_I2BSIZE(inode);
-	int	 rem;
-	int	 ext;
-	loff_t tmp;
-
-	pr_debug("AFFS: truncate(inode=%ld,size=%lu)\n",inode->i_ino,inode->i_size);
-
-	net_blocksize = blocksize - ((inode->i_sb->u.affs_sb.s_flags & SF_OFS) ? 24 : 0);
-	first = inode->i_size + net_blocksize -1;
-	do_div (first, net_blocksize);
-	if (inode->u.affs_i.i_lastblock < first - 1) {
-		/* There has to be at least one new block to be allocated */
-		if (!inode->u.affs_i.i_ec && alloc_ext_cache(inode)) {
-			/* XXX Fine! No way to indicate an error. */
-			return /* -ENOSPC */;
-		}
-		bh = affs_getblock(inode,first - 1);
-		if (!bh) {
-			affs_warning(inode->i_sb,"truncate","Cannot extend file");
-			inode->i_size = net_blocksize * (inode->u.affs_i.i_lastblock + 1);
-		} else if (inode->i_sb->u.affs_sb.s_flags & SF_OFS) {
-			tmp = inode->i_size;
-			rem = do_div(tmp, net_blocksize);
-			DATA_FRONT(bh)->data_size = cpu_to_be32(rem ? rem : net_blocksize);
-			affs_fix_checksum(blocksize,bh->b_data,5);
-			mark_buffer_dirty(bh);
+	struct inode *inode = file->f_dentry->d_inode;
+	u32 size, offset;
+	u32 tmp;
+	int err = 0;
+
+	pr_debug("AFFS: prepare_write(%u, %ld, %d, %d)\n", (u32)inode->i_ino, page->index, from, to);
+	if (Page_Uptodate(page))
+		return 0;
+
+	size = inode->i_size;
+	offset = page->index << PAGE_CACHE_SHIFT;
+	if (offset + from > size) {
+		err = affs_extent_file_ofs(file, offset + from);
+		if (err)
+			return err;
+	}
+
+	if (from) {
+		err = affs_do_readpage_ofs(file, page, 0, from);
+		if (err)
+			return err;
+	}
+	if (to < PAGE_CACHE_SIZE) {
+		memset(page_address(page) + to, 0, PAGE_CACHE_SIZE - to);
+		if (size > offset + to) {
+			if (size < offset + PAGE_CACHE_SIZE)
+				tmp = size & PAGE_CACHE_MASK;
+			else
+				tmp = PAGE_CACHE_SIZE;
+			err = affs_do_readpage_ofs(file, page, to, tmp);
 		}
-		goto out_truncate;
 	}
-	ekey = inode->i_ino;
-	ext  = 0;
+	return err;
+}
 
-	/* Free all blocks starting at 'first' and all then-empty
-	 * extension blocks. Do not free the header block, though.
-	 */
-	while (ekey) {
-		if (!(bh = affs_bread(inode->i_dev,ekey,blocksize))) {
-			affs_error(inode->i_sb,"truncate","Cannot read block %d",ekey);
-			goto out_truncate;
-		}
-		if (affs_checksum_block(blocksize,bh->b_data,&ptype,&stype)) {
-			affs_error(inode->i_sb,"truncate","Checksum error in header/ext block %d",
-				   ekey);
-			goto out_truncate;
-		}
-		if (stype != ST_FILE || (ptype != T_SHORT && ptype != T_LIST)) {
-			affs_error(inode->i_sb,"truncate",
-				   "Bad block (key=%d, ptype=%d, stype=%d)",ekey,ptype,stype);
-			goto out_truncate;
-		}
-		/* Do we have to free this extension block after
-		 * freeing the data blocks pointed to?
-		 */
-		freethis = first == 0 && ekey != inode->i_ino;
-
-		/* Free the data blocks. 'first' is relative to this
-		 * extension block and may well lie behind this block.
-		 */
-		for (block = first; block < AFFS_I2HSIZE(inode); block++) {
-			keyp = &AFFS_BLOCK(bh->b_data,inode,block);
-			key  = be32_to_cpu(*keyp);
-			if (key) {
-				*keyp = 0;
-				affs_free_block(inode->i_sb,key);
-			} else
-				break;
+static int affs_commit_write_ofs(struct file *file, struct page *page, unsigned from, unsigned to)
+{
+	struct inode *inode = file->f_dentry->d_inode;
+	struct super_block *sb = inode->i_sb;
+	struct buffer_head *bh, *prev_bh;
+	char *data;
+	u32 bidx, boff, bsize;
+	u32 tmp;
+	int written;
+
+	pr_debug("AFFS: commit_write(%u, %ld, %d, %d)\n", (u32)inode->i_ino, page->index, from, to);
+	bsize = AFFS_SB->s_data_blksize;
+	data = page_address(page);
+
+	bh = NULL;
+	written = 0;
+	tmp = (page->index << PAGE_CACHE_SHIFT) + from;
+	bidx = tmp / bsize;
+	boff = tmp % bsize;
+	if (boff) {
+		bh = affs_bread_ino(inode, bidx, 0);
+		if (IS_ERR(bh))
+			return PTR_ERR(bh);
+		tmp = MIN(bsize - boff, to - from);
+		memcpy(AFFS_DATA(bh) + boff, data + from, tmp);
+		AFFS_DATA_HEAD(bh)->size = cpu_to_be32(be32_to_cpu(AFFS_DATA_HEAD(bh)->size) + tmp);
+		affs_fix_checksum(sb, bh);
+		mark_buffer_dirty(bh);
+		written += tmp;
+		from += tmp;
+		bidx++;
+	} else if (bidx) {
+		bh = affs_bread_ino(inode, bidx - 1, 0);
+		if (IS_ERR(bh))
+			return PTR_ERR(bh);
+	}
+	while (from + bsize <= to) {
+		prev_bh = bh;
+		bh = affs_getemptyblk_ino(inode, bidx);
+		if (IS_ERR(bh))
+			goto out;
+		memcpy(AFFS_DATA(bh), data + from, bsize);
+		if (bh->b_state & (1UL << BH_New)) {
+			AFFS_DATA_HEAD(bh)->ptype = cpu_to_be32(T_DATA);
+			AFFS_DATA_HEAD(bh)->key = cpu_to_be32(inode->i_ino);
+			AFFS_DATA_HEAD(bh)->sequence = cpu_to_be32(bidx);
+			AFFS_DATA_HEAD(bh)->size = cpu_to_be32(bsize);
+			AFFS_DATA_HEAD(bh)->next = 0;
+			if (prev_bh) {
+				u32 tmp = be32_to_cpu(AFFS_DATA_HEAD(prev_bh)->next);
+				if (tmp)
+					affs_warning(sb, "prepare_write_ofs", "next block already set for %d (%d)", bidx, tmp);
+				AFFS_DATA_HEAD(prev_bh)->next = cpu_to_be32(bh->b_blocknr);
+				affs_adjust_checksum(prev_bh, bidx - tmp);
+				mark_buffer_dirty(prev_bh);
+			}
 		}
-		keyp = &GET_END_PTR(struct file_end,bh->b_data,blocksize)->extension;
-		key  = be32_to_cpu(*keyp);
-
-		/* If 'first' is in this block or is the first
-		 * in the next one, this will be the last in
-		 * the list, thus we have to adjust the count
-		 * and zero the pointer to the next ext block.
-		 */
-		if (first <= AFFS_I2HSIZE(inode)) {
-			((struct file_front *)bh->b_data)->block_count = cpu_to_be32(first);
-			first = 0;
-			*keyp = 0;
-			affs_fix_checksum(blocksize,bh->b_data,5);
-			mark_buffer_dirty(bh);
-		} else
-			first -= AFFS_I2HSIZE(inode);
-		affs_brelse(bh);
-		bh = NULL;
-		if (freethis)			/* Don't bother fixing checksum */
-			affs_free_block(inode->i_sb,ekey);
-		ekey = key;
-	}
-	block = inode->i_size + net_blocksize - 1;
-	do_div (block, net_blocksize);
-	block--;
-	inode->u.affs_i.i_lastblock = block;
-
-	/* If the file is not truncated to a block boundary,
-	 * the partial block after the EOF must be zeroed
-	 * so it cannot become accessible again.
-	 */
-
-	tmp = inode->i_size;
-	rem = do_div(tmp, net_blocksize);
-	if (rem) {
-		if ((inode->i_sb->u.affs_sb.s_flags & SF_OFS)) 
-			rem += 24;
-		pr_debug("AFFS: Zeroing from offset %d in block %d\n",rem,block);
-		bh = affs_getblock(inode,block);
-		if (bh) {
-			memset(bh->b_data + rem,0,blocksize - rem);
-			if ((inode->i_sb->u.affs_sb.s_flags & SF_OFS)) {
-				((struct data_front *)bh->b_data)->data_size = cpu_to_be32(rem);
-				((struct data_front *)bh->b_data)->next_data = 0;
-				affs_fix_checksum(blocksize,bh->b_data,5);
+		affs_brelse(prev_bh);
+		affs_fix_checksum(sb, bh);
+		mark_buffer_dirty(bh);
+		written += bsize;
+		from += bsize;
+		bidx++;
+	}
+	if (from < to) {
+		prev_bh = bh;
+		bh = affs_bread_ino(inode, bidx, 1);
+		if (IS_ERR(bh))
+			goto out;
+		tmp = MIN(bsize, to - from);
+		memcpy(AFFS_DATA(bh), data + from, tmp);
+		if (bh->b_state & (1UL << BH_New)) {
+			AFFS_DATA_HEAD(bh)->ptype = cpu_to_be32(T_DATA);
+			AFFS_DATA_HEAD(bh)->key = cpu_to_be32(inode->i_ino);
+			AFFS_DATA_HEAD(bh)->sequence = cpu_to_be32(bidx);
+			AFFS_DATA_HEAD(bh)->size = cpu_to_be32(tmp);
+			AFFS_DATA_HEAD(bh)->next = 0;
+			if (prev_bh) {
+				u32 tmp = be32_to_cpu(AFFS_DATA_HEAD(prev_bh)->next);
+				if (tmp)
+					affs_warning(sb, "prepare_write_ofs", "next block already set for %d (%d)", bidx, tmp);
+				AFFS_DATA_HEAD(prev_bh)->next = cpu_to_be32(bh->b_blocknr);
+				affs_adjust_checksum(prev_bh, bidx - tmp);
+				mark_buffer_dirty(prev_bh);
 			}
-			mark_buffer_dirty(bh);
-		} else 
-			affs_error(inode->i_sb,"truncate","Cannot read block %d",block);
+		} else if (be32_to_cpu(AFFS_DATA_HEAD(bh)->size) < tmp)
+			AFFS_DATA_HEAD(bh)->size = cpu_to_be32(tmp);
+		affs_brelse(prev_bh);
+		affs_fix_checksum(sb, bh);
+		mark_buffer_dirty(bh);
+		written += tmp;
+		from += tmp;
+		bidx++;
 	}
+	SetPageUptodate(page);
 
-out_truncate:
+done:
 	affs_brelse(bh);
-	/* Invalidate cache */
-	if (inode->u.affs_i.i_ec) {
-		inode->u.affs_i.i_ec->max_ext = 0;
-		for (key = 0; key < 4; key++) {
-			inode->u.affs_i.i_ec->kc[key].kc_next_key = 0;
-			inode->u.affs_i.i_ec->kc[key].kc_last     = -1;
-		}
-	}
-	mark_inode_dirty(inode);
+	tmp = (page->index << PAGE_CACHE_SHIFT) + from;
+	if (tmp > inode->i_size)
+		inode->i_size = AFFS_INODE->mmu_private = tmp;
+
+	return written;
+
+out:
+	bh = prev_bh;
+	if (!written)
+		written = PTR_ERR(bh);
+	goto done;
 }
 
-/*
- * Called only when we need to allocate the extension cache.
- */
+struct address_space_operations affs_aops_ofs = {
+	readpage: affs_readpage_ofs,
+	//writepage: affs_writepage_ofs,
+	//sync_page: affs_sync_page_ofs,
+	prepare_write: affs_prepare_write_ofs,
+	commit_write: affs_commit_write_ofs
+};
 
-static int
-alloc_ext_cache(struct inode *inode)
+/* Free any preallocated blocks. */
+
+void
+affs_free_prealloc(struct inode *inode)
 {
-	s32	 key;
-	int	 i;
-	unsigned long cache_page;
-	int      error = 0;
-
-	pr_debug("AFFS: alloc_ext_cache(ino=%lu)\n",inode->i_ino);
-
-	cache_page = get_free_page(GFP_KERNEL);
-	/*
-	 * Check whether somebody else allocated it for us ...
-	 */
-	if (inode->u.affs_i.i_ec)
-		goto out_free;
-	if (!cache_page)
-		goto out_error;
-
-	inode->u.affs_i.i_ec = (struct ext_cache *) cache_page;
-	/* We only have to initialize non-zero values.
-	 * get_free_page() zeroed the page already.
-	 */
-	key = inode->i_ino;
-	inode->u.affs_i.i_ec->ec[0] = key;
-	for (i = 0; i < 4; i++) {
-		inode->u.affs_i.i_ec->kc[i].kc_this_key = key;
-		inode->u.affs_i.i_ec->kc[i].kc_last     = -1;
+	struct super_block *sb = inode->i_sb;
+
+	pr_debug("AFFS: free_prealloc(ino=%lu)\n", inode->i_ino);
+
+	while (inode->u.affs_i.i_pa_cnt) {
+		inode->u.affs_i.i_pa_cnt--;
+		affs_free_block(sb, ++inode->u.affs_i.i_lastalloc);
 	}
-out:
-	return error;
+}
+
+/* Truncate (or enlarge) a file to the requested size. */
 
-out_free:
-	if (cache_page)
-		free_page(cache_page);
-	goto out;
-
-out_error:
-	affs_error(inode->i_sb,"alloc_ext_cache","Cache allocation failed");
-	error = -ENOMEM;
-	goto out;
+void
+affs_truncate(struct inode *inode)
+{
+	struct super_block *sb = inode->i_sb;
+	u32 ext, ext_key;
+	u32 last_blk, blkcnt, blk;
+	u32 size;
+	struct buffer_head *ext_bh;
+	int i;
+
+	pr_debug("AFFS: truncate(inode=%d, size=%u)\n", (u32)inode->i_ino, (u32)inode->i_size);
+
+	last_blk = 0;
+	ext = 0;
+	if (inode->i_size) {
+		last_blk = ((u32)inode->i_size - 1) / AFFS_SB->s_data_blksize;
+		ext = last_blk / AFFS_SB->s_hashsize;
+	}
+
+	if (inode->i_size > AFFS_INODE->mmu_private) {
+		struct address_space *mapping = inode->i_mapping;
+		struct page *page;
+		u32 size = inode->i_size - 1;
+		int res;
+
+		page = grab_cache_page(mapping, size >> PAGE_CACHE_SHIFT);
+		if (!page)
+			return;
+		size = (size & (PAGE_CACHE_SIZE - 1)) + 1;
+		res = mapping->a_ops->prepare_write(NULL, page, size, size);
+		if (!res)
+			res = mapping->a_ops->commit_write(NULL, page, size, size);
+		UnlockPage(page);
+		page_cache_release(page);
+		mark_inode_dirty(inode);
+		return;
+	} else if (inode->i_size == AFFS_INODE->mmu_private)
+		return;
+
+	// lock cache
+	ext_bh = affs_get_extblock(inode, ext);
+	if (AFFS_INODE->i_lc) {
+		/* clear linear cache */
+		for (i = (ext + 1) >> AFFS_INODE->i_lc_shift; i < AFFS_LC_SIZE; i++)
+			AFFS_INODE->i_lc[i] = 0;
+		/* clear associative cache */
+		for (i = 0; i < AFFS_AC_SIZE; i++)
+			if (AFFS_INODE->i_ac[i].ext >= ext)
+				AFFS_INODE->i_ac[i].ext = 0;
+	}
+	ext_key = be32_to_cpu(AFFS_TAIL(sb, ext_bh)->extension);
+
+	blkcnt = AFFS_INODE->i_blkcnt;
+	i = 0;
+	blk = last_blk;
+	if (inode->i_size) {
+		i = last_blk % AFFS_SB->s_hashsize + 1;
+		blk++;
+	} else
+		AFFS_HEAD(ext_bh)->first_data = 0;
+	size = AFFS_SB->s_hashsize;
+	if (size > blkcnt - blk + i)
+		size = blkcnt - blk + i;
+	for (; i < size; i++, blk++) {
+		affs_free_block(sb, be32_to_cpu(AFFS_BLOCK(sb, ext_bh, i)));
+		AFFS_BLOCK(sb, ext_bh, i) = 0;
+	}
+	AFFS_TAIL(sb, ext_bh)->extension = 0;
+	affs_fix_checksum(sb, ext_bh);
+	mark_buffer_dirty(ext_bh);
+	affs_brelse(ext_bh);
+
+	if (inode->i_size) {
+		AFFS_INODE->i_blkcnt = last_blk + 1;
+		AFFS_INODE->i_extcnt = ext + 1;
+	} else {
+		AFFS_INODE->i_blkcnt = 0;
+		AFFS_INODE->i_extcnt = 1;
+	}
+	AFFS_INODE->mmu_private = inode->i_size;
+	// unlock cache
+
+	while (ext_key) {
+		ext_bh = affs_bread(sb, ext_key);
+		size = AFFS_SB->s_hashsize;
+		if (size > blkcnt - blk)
+			size = blkcnt - blk;
+		for (i = 0; i < size; i++, blk++)
+			affs_free_block(sb, be32_to_cpu(AFFS_BLOCK(sb, ext_bh, i)));
+		affs_free_block(sb, ext_key);
+		ext_key = be32_to_cpu(AFFS_TAIL(sb, ext_bh)->extension);
+		affs_brelse(ext_bh);
+	}
 }
diff -urN linux-2.4.4-pre5/fs/affs/inode.c linux/fs/affs/inode.c
--- linux-2.4.4-pre5/fs/affs/inode.c	Fri Feb  9 11:29:44 2001
+++ linux/fs/affs/inode.c	Mon Dec  3 22:36:26 2001
@@ -10,7 +10,6 @@
  *  (C) 1991  Linus Torvalds - minix filesystem
  */
 
-#define DEBUG 0
 #include <asm/div64.h>
 #include <linux/errno.h>
 #include <linux/fs.h>
@@ -31,209 +30,185 @@
 #include <asm/system.h>
 #include <asm/uaccess.h>
 
-extern int *blk_size[];
-extern struct timezone sys_tz;
 extern struct inode_operations affs_symlink_inode_operations;
-
-#define MIN(a,b) (((a)<(b))?(a):(b))
-
-unsigned long
-affs_parent_ino(struct inode *dir)
-{
-	int root_ino = (dir->i_sb->u.affs_sb.s_root_block);
-
-	if (!S_ISDIR(dir->i_mode)) {
-		affs_error(dir->i_sb,"parent_ino","Trying to get parent of non-directory");
-		return root_ino;
-	}
-	if (dir->i_ino == root_ino)
-		return root_ino;
-	return dir->u.affs_i.i_parent;
-}
+extern struct timezone sys_tz;
 
 void
 affs_read_inode(struct inode *inode)
 {
+	struct super_block	*sb = inode->i_sb;
 	struct buffer_head	*bh;
-	struct file_front	*file_front;
-	struct file_end		*file_end;
-	s32			 block;
-	unsigned long		 prot;
-	s32			 ptype, stype;
-	unsigned short		 id;
-	loff_t		tmp;
+	struct affs_head	*head;
+	struct affs_tail	*tail;
+	u32			 block;
+	u32			 size;
+	u32			 prot;
+	u16			 id;
 
 	pr_debug("AFFS: read_inode(%lu)\n",inode->i_ino);
 
 	block = inode->i_ino;
-	if (!(bh = affs_bread(inode->i_dev,block,AFFS_I2BSIZE(inode)))) {
-		affs_error(inode->i_sb,"read_inode","Cannot read block %d",block);
-		return;
-	}
-	if (affs_checksum_block(AFFS_I2BSIZE(inode),bh->b_data,&ptype,&stype) || ptype != T_SHORT) {
-		affs_error(inode->i_sb,"read_inode",
-			   "Checksum or type (ptype=%d) error on inode %d",ptype,block);
-		affs_brelse(bh);
-		return;
+	bh = affs_bread(sb, block);
+	if (!bh) {
+		affs_warning(sb, "read_inode", "Cannot read block %d", block);
+		goto bad_inode;
+	}
+	if (affs_checksum_block(sb, bh) || be32_to_cpu(AFFS_HEAD(bh)->ptype) != T_SHORT) {
+		affs_warning(sb,"read_inode",
+			   "Checksum or type (ptype=%d) error on inode %d",
+			   AFFS_HEAD(bh)->ptype, block);
+		goto bad_inode;
 	}
 
-	file_front = (struct file_front *)bh->b_data;
-	file_end   = GET_END_PTR(struct file_end, bh->b_data,AFFS_I2BSIZE(inode));
-	prot       = (be32_to_cpu(file_end->protect) & ~0x10) ^ FIBF_OWNER;
-
-	inode->u.affs_i.i_protect      = prot;
-	inode->u.affs_i.i_parent       = be32_to_cpu(file_end->parent);
-	inode->u.affs_i.i_original     = 0;
-	inode->u.affs_i.i_zone         = 0;
-	inode->u.affs_i.i_hlink        = 0;
-	inode->u.affs_i.i_pa_cnt       = 0;
-	inode->u.affs_i.i_pa_next      = 0;
-	inode->u.affs_i.i_pa_last      = 0;
-	inode->u.affs_i.i_ec           = NULL;
-	inode->u.affs_i.i_lastblock    = -1;
-	inode->i_nlink                 = 1;
-	inode->i_mode                  = 0;
+	head = AFFS_HEAD(bh);
+	tail = AFFS_TAIL(sb, bh);
+	prot = be32_to_cpu(tail->protect);
 
-	if (inode->i_sb->u.affs_sb.s_flags & SF_SETMODE)
-		inode->i_mode = inode->i_sb->u.affs_sb.s_mode;
+	inode->i_size = 0;
+	inode->i_nlink = 1;
+	inode->i_mode = 0;
+	memset(AFFS_INODE, 0, sizeof(struct affs_inode_info));
+	init_MUTEX(&AFFS_INODE->i_link_lock);
+	init_MUTEX(&AFFS_INODE->i_ext_lock);
+	AFFS_INODE->i_extcnt = 1;
+	AFFS_INODE->i_ext_last = ~1;
+	AFFS_INODE->i_protect = prot;
+
+	if (AFFS_SB->s_flags & SF_SETMODE)
+		inode->i_mode = AFFS_SB->s_mode;
 	else
 		inode->i_mode = prot_to_mode(prot);
 
-	if (inode->i_sb->u.affs_sb.s_flags & SF_SETUID)
-		inode->i_uid = inode->i_sb->u.affs_sb.s_uid;
-	id = be16_to_cpu(file_end->owner_uid);
-	if (id == 0 || inode->i_sb->u.affs_sb.s_flags & SF_SETUID)
-		inode->i_uid = inode->i_sb->u.affs_sb.s_uid;
-	else if (id == 0xFFFF && inode->i_sb->u.affs_sb.s_flags & SF_MUFS)
+	id = be16_to_cpu(tail->uid);
+	if (id == 0 || AFFS_SB->s_flags & SF_SETUID)
+		inode->i_uid = AFFS_SB->s_uid;
+	else if (id == 0xFFFF && AFFS_SB->s_flags & SF_MUFS)
 		inode->i_uid = 0;
-	else 
+	else
 		inode->i_uid = id;
 
-	id = be16_to_cpu(file_end->owner_gid);
-	if (id == 0 || inode->i_sb->u.affs_sb.s_flags & SF_SETGID)
-		inode->i_gid = inode->i_sb->u.affs_sb.s_gid;
-	else if (id == 0xFFFF && inode->i_sb->u.affs_sb.s_flags & SF_MUFS)
+	id = be16_to_cpu(tail->gid);
+	if (id == 0 || AFFS_SB->s_flags & SF_SETGID)
+		inode->i_gid = AFFS_SB->s_gid;
+	else if (id == 0xFFFF && AFFS_SB->s_flags & SF_MUFS)
 		inode->i_gid = 0;
 	else
 		inode->i_gid = id;
 
-	switch (be32_to_cpu(file_end->secondary_type)) {
-		case ST_ROOT:
-			inode->i_uid   = inode->i_sb->u.affs_sb.s_uid;
-			inode->i_gid   = inode->i_sb->u.affs_sb.s_gid;
-		case ST_USERDIR:
-			if (be32_to_cpu(file_end->secondary_type) == ST_USERDIR ||
-			    inode->i_sb->u.affs_sb.s_flags & SF_SETMODE) {
-				if (inode->i_mode & S_IRUSR)
-					inode->i_mode |= S_IXUSR;
-				if (inode->i_mode & S_IRGRP)
-					inode->i_mode |= S_IXGRP;
-				if (inode->i_mode & S_IROTH)
-					inode->i_mode |= S_IXOTH;
-				inode->i_mode |= S_IFDIR;
-			} else
-				inode->i_mode = S_IRUGO | S_IXUGO | S_IWUSR | S_IFDIR;
-			inode->i_size  = 0;
-			break;
-		case ST_LINKDIR:
-			affs_error(inode->i_sb,"read_inode","inode is LINKDIR");
-			affs_brelse(bh);
-			return;
-		case ST_LINKFILE:
-			affs_error(inode->i_sb,"read_inode","inode is LINKFILE");
-			affs_brelse(bh);
-			return;
-		case ST_FILE:
-			inode->i_mode |= S_IFREG;
-			inode->i_size  = be32_to_cpu(file_end->byte_size);
-			if (inode->i_sb->u.affs_sb.s_flags & SF_OFS)
-				block = AFFS_I2BSIZE(inode) - 24;
-			else
-				block = AFFS_I2BSIZE(inode);
-			tmp = inode->i_size + block -1;
-			do_div (tmp, block);
-			tmp--;
-			inode->u.affs_i.i_lastblock = tmp;
-			break;
-		case ST_SOFTLINK:
-			inode->i_mode |= S_IFLNK;
-			inode->i_size  = 0;
-			break;
+	switch (be32_to_cpu(tail->stype)) {
+	case ST_ROOT:
+		inode->i_uid = AFFS_SB->s_uid;
+		inode->i_gid = AFFS_SB->s_gid;
+		/* fall through */
+	case ST_USERDIR:
+		if (be32_to_cpu(tail->stype) == ST_USERDIR ||
+		    AFFS_SB->s_flags & SF_SETMODE) {
+			if (inode->i_mode & S_IRUSR)
+				inode->i_mode |= S_IXUSR;
+			if (inode->i_mode & S_IRGRP)
+				inode->i_mode |= S_IXGRP;
+			if (inode->i_mode & S_IROTH)
+				inode->i_mode |= S_IXOTH;
+			inode->i_mode |= S_IFDIR;
+		} else
+			inode->i_mode = S_IRUGO | S_IXUGO | S_IWUSR | S_IFDIR;
+		if (tail->link_chain)
+			inode->i_nlink = 2;
+		/* Maybe it should be controlled by mount parameter? */
+		//inode->i_mode |= S_ISVTX;
+		inode->i_op = &affs_dir_inode_operations;
+		inode->i_fop = &affs_dir_operations;
+		break;
+	case ST_LINKDIR:
+		affs_warning(sb, "read_inode", "inode is LINKDIR");
+		goto bad_inode;
+	case ST_LINKFILE:
+		affs_warning(sb, "read_inode", "inode is LINKFILE");
+		goto bad_inode;
+	case ST_FILE:
+		size = be32_to_cpu(tail->size);
+		inode->i_mode |= S_IFREG;
+		AFFS_INODE->mmu_private = inode->i_size = size;
+		if (inode->i_size) {
+			AFFS_INODE->i_blkcnt = (size - 1) /
+					       AFFS_SB->s_data_blksize + 1;
+			AFFS_INODE->i_extcnt = (AFFS_INODE->i_blkcnt - 1) /
+					       AFFS_SB->s_hashsize + 1;
+		}
+		if (tail->link_chain)
+			inode->i_nlink = 2;
+		inode->i_mapping->a_ops = (AFFS_SB->s_flags & SF_OFS) ? &affs_aops_ofs : &affs_aops;
+		inode->i_op = &affs_file_inode_operations;
+		inode->i_fop = &affs_file_operations;
+		break;
+	case ST_SOFTLINK:
+		inode->i_mode |= S_IFLNK;
+		inode->i_op = &affs_symlink_inode_operations;
+		inode->i_data.a_ops = &affs_symlink_aops;
+		break;
 	}
 
 	inode->i_mtime = inode->i_atime = inode->i_ctime
-		       = (be32_to_cpu(file_end->created.ds_Days) * (24 * 60 * 60) +
-		         be32_to_cpu(file_end->created.ds_Minute) * 60 +
-			 be32_to_cpu(file_end->created.ds_Tick) / 50 +
+		       = (be32_to_cpu(tail->change.days) * (24 * 60 * 60) +
+		         be32_to_cpu(tail->change.mins) * 60 +
+			 be32_to_cpu(tail->change.ticks) / 50 +
 			 ((8 * 365 + 2) * 24 * 60 * 60)) +
 			 sys_tz.tz_minuteswest * 60;
 	affs_brelse(bh);
+	return;
 
-	if (S_ISREG(inode->i_mode)) {
-		if (inode->i_sb->u.affs_sb.s_flags & SF_OFS) {
-			inode->i_op = &affs_file_inode_operations;
-			inode->i_fop = &affs_file_operations_ofs;
-			return;
-		}
-		inode->i_op = &affs_file_inode_operations;
-		inode->i_fop = &affs_file_operations;
-		inode->i_mapping->a_ops = &affs_aops;
-		inode->u.affs_i.mmu_private = inode->i_size;
-	} else if (S_ISDIR(inode->i_mode)) {
-		/* Maybe it should be controlled by mount parameter? */
-		inode->i_mode |= S_ISVTX;
-		inode->i_op = &affs_dir_inode_operations;
-		inode->i_fop = &affs_dir_operations;
-	}
-	else if (S_ISLNK(inode->i_mode)) {
-		inode->i_op = &affs_symlink_inode_operations;
-		inode->i_data.a_ops = &affs_symlink_aops;
-	}
+bad_inode:
+	make_bad_inode(inode);
+	affs_brelse(bh);
+	return;
 }
 
 void
 affs_write_inode(struct inode *inode, int unused)
 {
+	struct super_block	*sb = inode->i_sb;
 	struct buffer_head	*bh;
-	struct file_end		*file_end;
+	struct affs_tail	*tail;
 	uid_t			 uid;
 	gid_t			 gid;
 
 	pr_debug("AFFS: write_inode(%lu)\n",inode->i_ino);
 
 	if (!inode->i_nlink)
+		// possibly free block
 		return;
 	lock_kernel();
-	if (!(bh = bread(inode->i_dev,inode->i_ino,AFFS_I2BSIZE(inode)))) {
-		affs_error(inode->i_sb,"write_inode","Cannot read block %lu",inode->i_ino);
+	bh = affs_bread(sb, inode->i_ino);
+	if (!bh) {
+		affs_error(sb,"write_inode","Cannot read block %lu",inode->i_ino);
 		unlock_kernel();
 		return;
 	}
-	file_end = GET_END_PTR(struct file_end, bh->b_data,AFFS_I2BSIZE(inode));
-	if (file_end->secondary_type == be32_to_cpu(ST_ROOT)) {
-		secs_to_datestamp(inode->i_mtime,&ROOT_END(bh->b_data,inode)->disk_altered);
+	tail = AFFS_TAIL(sb, bh);
+	if (tail->stype == be32_to_cpu(ST_ROOT)) {
+		secs_to_datestamp(inode->i_mtime,&AFFS_ROOT_TAIL(sb, bh)->root_change);
 	} else {
-		file_end->protect   = cpu_to_be32(inode->u.affs_i.i_protect ^ FIBF_OWNER);
-		file_end->byte_size = cpu_to_be32(inode->i_size);
-		secs_to_datestamp(inode->i_mtime,&file_end->created);
-		if (!(inode->i_ino == inode->i_sb->u.affs_sb.s_root_block)) {
+		tail->protect = cpu_to_be32(AFFS_INODE->i_protect);
+		tail->size = cpu_to_be32(inode->i_size);
+		secs_to_datestamp(inode->i_mtime,&tail->change);
+		if (!(inode->i_ino == AFFS_SB->s_root_block)) {
 			uid = inode->i_uid;
 			gid = inode->i_gid;
-			if (inode->i_sb->u.affs_sb.s_flags & SF_MUFS) {
+			if (sb->u.affs_sb.s_flags & SF_MUFS) {
 				if (inode->i_uid == 0 || inode->i_uid == 0xFFFF)
 					uid = inode->i_uid ^ ~0;
 				if (inode->i_gid == 0 || inode->i_gid == 0xFFFF)
 					gid = inode->i_gid ^ ~0;
 			}
-			if (!(inode->i_sb->u.affs_sb.s_flags & SF_SETUID))
-				file_end->owner_uid = cpu_to_be16(uid);
-			if (!(inode->i_sb->u.affs_sb.s_flags & SF_SETGID))
-				file_end->owner_gid = cpu_to_be16(gid);
+			if (!(sb->u.affs_sb.s_flags & SF_SETUID))
+				tail->uid = cpu_to_be16(uid);
+			if (!(sb->u.affs_sb.s_flags & SF_SETGID))
+				tail->gid = cpu_to_be16(gid);
 		}
 	}
-	affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5);
+	affs_fix_checksum(sb, bh);
 	mark_buffer_dirty(bh);
-	brelse(bh);
+	affs_brelse(bh);
 	unlock_kernel();
 }
 
@@ -258,11 +233,9 @@
 		goto out;
 	}
 
-	if (attr->ia_valid & ATTR_MODE)
-		inode->u.affs_i.i_protect = mode_to_prot(attr->ia_mode);
-
-	error = 0;
-	inode_setattr(inode, attr);
+	error = inode_setattr(inode, attr);
+	if (!error && (attr->ia_valid & ATTR_MODE))
+		mode_to_prot(inode);
 out:
 	return error;
 }
@@ -270,18 +243,14 @@
 void
 affs_put_inode(struct inode *inode)
 {
-	pr_debug("AFFS: put_inode(ino=%lu, nlink=%u)\n",
-		inode->i_ino,inode->i_nlink);
-
+	pr_debug("AFFS: put_inode(ino=%lu, nlink=%u)\n", inode->i_ino, inode->i_nlink);
 	lock_kernel();
 	affs_free_prealloc(inode);
 	if (atomic_read(&inode->i_count) == 1) {
-		unsigned long cache_page = (unsigned long) inode->u.affs_i.i_ec;
-		if (cache_page) {
-			pr_debug("AFFS: freeing ext cache\n");
-			inode->u.affs_i.i_ec = NULL;
-			free_page(cache_page);
-		}
+		if (inode->i_size != AFFS_INODE->mmu_private)
+			affs_truncate(inode);
+		//if (inode->i_nlink)
+		//	affs_clear_inode(inode);
 	}
 	unlock_kernel();
 }
@@ -289,55 +258,76 @@
 void
 affs_delete_inode(struct inode *inode)
 {
-	pr_debug("AFFS: delete_inode(ino=%lu, nlink=%u)\n",inode->i_ino,inode->i_nlink);
+	pr_debug("AFFS: delete_inode(ino=%lu, nlink=%u)\n", inode->i_ino, inode->i_nlink);
 	lock_kernel();
 	inode->i_size = 0;
-	if (S_ISREG(inode->i_mode) && !inode->u.affs_i.i_hlink)
+	if (S_ISREG(inode->i_mode))
 		affs_truncate(inode);
-	affs_free_block(inode->i_sb,inode->i_ino);
-	unlock_kernel();
 	clear_inode(inode);
+	affs_free_block(inode->i_sb, inode->i_ino);
+	unlock_kernel();
+}
+
+void
+affs_clear_inode(struct inode *inode)
+{
+	unsigned long cache_page = (unsigned long) inode->u.affs_i.i_lc;
+
+	pr_debug("AFFS: clear_inode(ino=%lu, nlink=%u)\n", inode->i_ino, inode->i_nlink);
+	if (cache_page) {
+		pr_debug("AFFS: freeing ext cache\n");
+		inode->u.affs_i.i_lc = NULL;
+		inode->u.affs_i.i_ac = NULL;
+		free_page(cache_page);
+	}
+	affs_brelse(AFFS_INODE->i_ext_bh);
+	AFFS_INODE->i_ext_last = ~1;
+	AFFS_INODE->i_ext_bh = NULL;
 }
 
 struct inode *
-affs_new_inode(const struct inode *dir)
+affs_new_inode(struct inode *dir)
 {
+	struct super_block	*sb = dir->i_sb;
 	struct inode		*inode;
-	struct super_block	*sb;
-	s32			 block;
+	u32			 block;
+	struct buffer_head	*bh;
 
-	if (!dir)
-		return NULL;
+	if (!dir || !(inode = get_empty_inode()))
+		goto err_inode;
 
-	sb = dir->i_sb;
-	inode = new_inode(sb);
-	if (!inode)
-		return NULL;
-
-	if (!(block = affs_new_header((struct inode *)dir))) {
-		iput(inode);
-		return NULL;
-	}
+	if (!(block = affs_alloc_block(dir, dir->i_ino)))
+		goto err_block;
+
+	bh = affs_getzeroblk(sb, block);
+	if (!bh)
+		goto err_bh;
+	mark_buffer_dirty(bh);
+	affs_brelse(bh);
 
+	inode->i_sb      = sb;
+	inode->i_dev     = sb->s_dev;
 	inode->i_uid     = current->fsuid;
 	inode->i_gid     = current->fsgid;
 	inode->i_ino     = block;
+	inode->i_nlink   = 1;
 	inode->i_mtime   = inode->i_atime = inode->i_ctime = CURRENT_TIME;
-
-	inode->u.affs_i.i_original  = 0;
-	inode->u.affs_i.i_parent    = dir->i_ino;
-	inode->u.affs_i.i_zone      = 0;
-	inode->u.affs_i.i_hlink     = 0;
-	inode->u.affs_i.i_pa_cnt    = 0;
-	inode->u.affs_i.i_pa_next   = 0;
-	inode->u.affs_i.i_pa_last   = 0;
-	inode->u.affs_i.i_ec        = NULL;
-	inode->u.affs_i.i_lastblock = -1;
+	memset(AFFS_INODE, 0, sizeof(struct affs_inode_info));
+	AFFS_INODE->i_extcnt = 1;
+	AFFS_INODE->i_ext_last = ~1;
+	init_MUTEX(&AFFS_INODE->i_link_lock);
+	init_MUTEX(&AFFS_INODE->i_ext_lock);
 
 	insert_inode_hash(inode);
-	mark_inode_dirty(inode);
 
 	return inode;
+
+err_bh:
+	affs_free_block(sb, block);
+err_block:
+	iput(inode);
+err_inode:
+	return NULL;
 }
 
 /*
@@ -346,70 +336,73 @@
  */
 
 int
-affs_add_entry(struct inode *dir, struct inode *link, struct inode *inode,
-	       struct dentry *dentry, int type)
+affs_add_entry(struct inode *dir, struct inode *inode, struct dentry *dentry, s32 type)
 {
-	struct buffer_head	*dir_bh;
-	struct buffer_head	*inode_bh;
-	struct buffer_head	*link_bh;
-	int			 retval;
-	const unsigned char	*name = dentry->d_name.name;
-	int			 len  = dentry->d_name.len;
-
-	pr_debug("AFFS: add_entry(dir=%lu,inode=%lu,\"%*s\",type=%d)\n",dir->i_ino,inode->i_ino,
-		 len,name,type);
-
-	if ((retval = affs_check_name(name,len)))
-		return retval;
-	if (len > 30)
-		len = 30;
-
-	dir_bh   = affs_bread(dir->i_dev,dir->i_ino,AFFS_I2BSIZE(dir));
-	inode_bh = affs_bread(inode->i_dev,inode->i_ino,AFFS_I2BSIZE(inode));
-	link_bh  = NULL;
-	retval   = -EIO;
-	if (!dir_bh || !inode_bh)
-		goto addentry_done;
-	if (link) {
-		link_bh = affs_bread(link->i_dev,link->i_ino,AFFS_I2BSIZE(link));
-		if (!link_bh)
-			goto addentry_done;
-	}
-	((struct dir_front *)inode_bh->b_data)->primary_type = cpu_to_be32(T_SHORT);
-	((struct dir_front *)inode_bh->b_data)->own_key      = cpu_to_be32(inode->i_ino);
-	DIR_END(inode_bh->b_data,inode)->dir_name[0]         = len;
-	strncpy(DIR_END(inode_bh->b_data,inode)->dir_name + 1,name,len);
-	DIR_END(inode_bh->b_data,inode)->secondary_type = cpu_to_be32(type);
-	DIR_END(inode_bh->b_data,inode)->parent         = cpu_to_be32(dir->i_ino);
-
-	lock_super(inode->i_sb);
-	retval = affs_insert_hash(dir->i_ino,inode_bh,dir);
-
-	if (link_bh) {
-		LINK_END(inode_bh->b_data,inode)->original   = cpu_to_be32(link->i_ino);
-		LINK_END(inode_bh->b_data,inode)->link_chain =
-						FILE_END(link_bh->b_data,link)->link_chain;
-		FILE_END(link_bh->b_data,link)->link_chain   = cpu_to_be32(inode->i_ino);
-		affs_fix_checksum(AFFS_I2BSIZE(link),link_bh->b_data,5);
-		link->i_version = ++event;
-		mark_inode_dirty(link);
-		mark_buffer_dirty(link_bh);
+	struct super_block *sb = dir->i_sb;
+	struct buffer_head *inode_bh = NULL;
+	struct buffer_head *bh = NULL;
+	u32 block = 0;
+	int retval;
+
+	pr_debug("AFFS: add_entry(dir=%u, inode=%u, \"%*s\", type=%d)\n", (u32)dir->i_ino,
+	         (u32)inode->i_ino, (int)dentry->d_name.len, dentry->d_name.name, type);
+
+	retval = -EIO;
+	bh = affs_bread(sb, inode->i_ino);
+	if (!bh)
+		goto done;
+
+	switch (type) {
+	case ST_LINKFILE:
+	case ST_LINKDIR:
+		inode_bh = bh;
+		retval = -ENOSPC;
+		block = affs_alloc_block(dir, dir->i_ino);
+		if (!block)
+			goto err;
+		retval = -EIO;
+		bh = affs_getzeroblk(sb, block);
+		if (!bh)
+			goto err;
+		break;
+	default:
+		break;
+	}
+
+	AFFS_HEAD(bh)->ptype = cpu_to_be32(T_SHORT);
+	AFFS_HEAD(bh)->key = cpu_to_be32(bh->b_blocknr);
+	affs_copy_name(AFFS_TAIL(sb, bh)->name, dentry);
+	AFFS_TAIL(sb, bh)->stype = cpu_to_be32(type);
+	AFFS_TAIL(sb, bh)->parent = cpu_to_be32(dir->i_ino);
+
+	if (inode_bh) {
+		u32 chain;
+		down(&AFFS_INODE->i_link_lock);
+	       	chain = AFFS_TAIL(sb, inode_bh)->link_chain;
+		AFFS_TAIL(sb, bh)->original = cpu_to_be32(inode->i_ino);
+		AFFS_TAIL(sb, bh)->link_chain = chain;
+		AFFS_TAIL(sb, inode_bh)->link_chain = cpu_to_be32(block);
+		affs_adjust_checksum(inode_bh, block - be32_to_cpu(chain));
+		mark_buffer_dirty(inode_bh);
+		inode->i_nlink = 2;
+		atomic_inc(&inode->i_count);
+		up(&AFFS_INODE->i_link_lock);
 	}
-	affs_fix_checksum(AFFS_I2BSIZE(inode),inode_bh->b_data,5);
-	affs_fix_checksum(AFFS_I2BSIZE(dir),dir_bh->b_data,5);
-	dir->i_version = ++event;
-	dir->i_mtime   = dir->i_atime = dir->i_ctime = CURRENT_TIME;
-	unlock_super(inode->i_sb);
-
-	mark_inode_dirty(dir);
-	mark_inode_dirty(inode);
-	mark_buffer_dirty(dir_bh);
-	mark_buffer_dirty(inode_bh);
+	affs_fix_checksum(sb, bh);
+	mark_buffer_dirty(bh);
 
-addentry_done:
-	affs_brelse(dir_bh);
+	down(&AFFS_DIR->i_hash_lock);
+	retval = affs_insert_hash(dir, bh);
+	up(&AFFS_DIR->i_hash_lock);
+
+	dentry->d_fsdata = (void *)bh->b_blocknr;
+	d_instantiate(dentry, inode);
+done:
 	affs_brelse(inode_bh);
-	affs_brelse(link_bh);
-
+	affs_brelse(bh);
 	return retval;
+err:
+	if (block)
+		affs_free_block(sb, block);
+	goto done;
 }
diff -urN linux-2.4.4-pre5/fs/affs/namei.c linux/fs/affs/namei.c
--- linux-2.4.4-pre5/fs/affs/namei.c	Tue Sep  5 14:07:29 2000
+++ linux/fs/affs/namei.c	Mon Dec  3 22:36:26 2001
@@ -8,7 +8,6 @@
  *  (C) 1991  Linus Torvalds - minix filesystem
  */
 
-#define DEBUG 0
 #include <linux/sched.h>
 #include <linux/affs_fs.h>
 #include <linux/kernel.h>
@@ -21,283 +20,268 @@
 
 #include <linux/errno.h>
 
+typedef int (*toupper_t)(int);
+
 extern struct inode_operations affs_symlink_inode_operations;
 
+static int	 affs_toupper(int ch);
+static int	 affs_hash_dentry(struct dentry *, struct qstr *);
+static int       affs_compare_dentry(struct dentry *, struct qstr *, struct qstr *);
+static int	 affs_intl_toupper(int ch);
+static int	 affs_intl_hash_dentry(struct dentry *, struct qstr *);
+static int       affs_intl_compare_dentry(struct dentry *, struct qstr *, struct qstr *);
+
+struct dentry_operations affs_dentry_operations = {
+	d_hash:		affs_hash_dentry,
+	d_compare:	affs_compare_dentry,
+};
+
+struct dentry_operations affs_intl_dentry_operations = {
+	d_hash:		affs_intl_hash_dentry,
+	d_compare:	affs_intl_compare_dentry,
+};
+
+
 /* Simple toupper() for DOS\1 */
 
-static unsigned int
-affs_toupper(unsigned int ch)
+static int
+affs_toupper(int ch)
 {
 	return ch >= 'a' && ch <= 'z' ? ch -= ('a' - 'A') : ch;
 }
 
 /* International toupper() for DOS\3 ("international") */
 
-static unsigned int
-affs_intl_toupper(unsigned int ch)
+static int
+affs_intl_toupper(int ch)
 {
 	return (ch >= 'a' && ch <= 'z') || (ch >= 0xE0
 		&& ch <= 0xFE && ch != 0xF7) ?
 		ch - ('a' - 'A') : ch;
 }
 
-static int	 affs_hash_dentry(struct dentry *, struct qstr *);
-static int       affs_compare_dentry(struct dentry *, struct qstr *, struct qstr *);
-struct dentry_operations affs_dentry_operations = {
-	d_hash:		affs_hash_dentry,
-	d_compare:	affs_compare_dentry,
-};
+static inline toupper_t
+affs_get_toupper(struct super_block *sb)
+{
+	return AFFS_SB->s_flags & SF_INTL ? affs_intl_toupper : affs_toupper;
+}
 
 /*
  * Note: the dentry argument is the parent dentry.
  */
-static int
-affs_hash_dentry(struct dentry *dentry, struct qstr *qstr)
+static inline int
+__affs_hash_dentry(struct dentry *dentry, struct qstr *qstr, toupper_t toupper)
 {
-	unsigned int (*toupper)(unsigned int) = affs_toupper;
-	unsigned long	 hash;
-	int		 i;
+	const char *name = qstr->name;
+	unsigned long hash;
+	int i;
 
-	if ((i = affs_check_name(qstr->name,qstr->len)))
+	i = affs_check_name(qstr->name,qstr->len);
+	if (i)
 		return i;
 
-	/* Check whether to use the international 'toupper' routine */
-	if (AFFS_I2FSTYPE(dentry->d_inode))
-		toupper = affs_intl_toupper;
 	hash = init_name_hash();
-	for (i = 0; i < qstr->len && i < 30; i++)
-		hash = partial_name_hash(toupper(qstr->name[i]), hash);
+	i = MIN(qstr->len, 30);
+	for (; i > 0; name++, i--)
+		hash = partial_name_hash(toupper(*name), hash);
 	qstr->hash = end_name_hash(hash);
 
 	return 0;
 }
 
 static int
-affs_compare_dentry(struct dentry *dentry, struct qstr *a, struct qstr *b)
+affs_hash_dentry(struct dentry *dentry, struct qstr *qstr)
+{
+	return __affs_hash_dentry(dentry, qstr, affs_toupper);
+}
+static int
+affs_intl_hash_dentry(struct dentry *dentry, struct qstr *qstr)
+{
+	return __affs_hash_dentry(dentry, qstr, affs_intl_toupper);
+}
+
+static inline int
+__affs_compare_dentry(struct dentry *dentry, struct qstr *a, struct qstr *b, toupper_t toupper)
 {
-	unsigned int (*toupper)(unsigned int) = affs_toupper;
-	int	 alen = a->len;
-	int      blen = b->len;
-	int	 i;
+	const u8 *aname = a->name;
+	const u8 *bname = b->name;
+	int len;
 
 	/* 'a' is the qstr of an already existing dentry, so the name
 	 * must be valid. 'b' must be validated first.
 	 */
-	
+
 	if (affs_check_name(b->name,b->len))
 		return 1;
 
 	/* If the names are longer than the allowed 30 chars,
 	 * the excess is ignored, so their length may differ.
 	 */
-	if (alen > 30)
-		alen = 30;
-	if (blen > 30)
-		blen = 30;
-	if (alen != blen)
+	len = a->len;
+	if (len >= 30) {
+		if (b->len < 30)
+			return 1;
+		len = 30;
+	} else if (len != b->len)
 		return 1;
 
-	/* Check whether to use the international 'toupper' routine */
-	if (AFFS_I2FSTYPE(dentry->d_inode))
-		toupper = affs_intl_toupper;
-
-	for (i = 0; i < alen; i++)
-		if (toupper(a->name[i]) != toupper(b->name[i]))
+	for (; len > 0; len--)
+		if (toupper(*aname++) != toupper(*bname++))
 			return 1;
-	
+
 	return 0;
 }
 
+static int
+affs_compare_dentry(struct dentry *dentry, struct qstr *a, struct qstr *b)
+{
+	return __affs_compare_dentry(dentry, a, b, affs_toupper);
+}
+static int
+affs_intl_compare_dentry(struct dentry *dentry, struct qstr *a, struct qstr *b)
+{
+	return __affs_compare_dentry(dentry, a, b, affs_intl_toupper);
+}
+
 /*
  * NOTE! unlike strncmp, affs_match returns 1 for success, 0 for failure.
  */
 
-static int
-affs_match(const unsigned char *name, int len, const unsigned char *compare, int dlen, int intl)
+static inline int
+affs_match(struct dentry *dentry, const u8 *name2, toupper_t toupper)
 {
-	unsigned int	(*toupper)(unsigned int) = intl ? affs_intl_toupper : affs_toupper;
-	int		  i;
-
-	if (!compare)
-		return 0;
+	const u8 *name = dentry->d_name.name;
+	int len = dentry->d_name.len;
 
-	if (len > 30)
+	if (len >= 30) {
+		if (*name2 < 30)
+			return 0;
 		len = 30;
-	if (dlen > 30)
-		dlen = 30;
-
-	/* "" means "." ---> so paths like "/usr/lib//libc.a" work */
-	if (!len && dlen == 1 && compare[0] == '.')
-		return 1;
-	if (dlen != len)
+	} else if (len != *name2)
 		return 0;
-	for (i = 0; i < len; i++)
-		if (toupper(name[i]) != toupper(compare[i]))
+
+	for (name2++; len > 0; len--)
+		if (toupper(*name++) != toupper(*name2++))
 			return 0;
 	return 1;
 }
 
 int
-affs_hash_name(const unsigned char *name, int len, int intl, int hashsize)
+affs_hash_name(struct super_block *sb, const u8 *name, unsigned int len)
 {
-	unsigned int i, x;
-
-	if (len > 30)
-		len = 30;
+	toupper_t toupper = affs_get_toupper(sb);
+	int hash;
 
-	x = len;
-	for (i = 0; i < len; i++)
-		if (intl)
-			x = (x * 13 + affs_intl_toupper(name[i] & 0xFF)) & 0x7ff;
-		else
-			x = (x * 13 + affs_toupper(name[i] & 0xFF)) & 0x7ff;
+	hash = len = MIN(len, 30);
+	for (; len > 0; len--)
+		hash = (hash * 13 + toupper(*name++)) & 0x7ff;
 
-	return x % hashsize;
+	return hash % AFFS_SB->s_hashsize;
 }
 
 static struct buffer_head *
-affs_find_entry(struct inode *dir, struct dentry *dentry, unsigned long *ino)
+affs_find_entry(struct inode *dir, struct dentry *dentry)
 {
-	struct buffer_head	*bh;
-	int			 intl = AFFS_I2FSTYPE(dir);
-	s32			 key;
-	const char		*name = dentry->d_name.name;
-	int			 namelen = dentry->d_name.len;
+	struct super_block *sb = dir->i_sb;
+	struct buffer_head *bh;
+	toupper_t toupper = affs_get_toupper(sb);
+	u32 key;
 
-	pr_debug("AFFS: find_entry(\"%.*s\")\n",namelen,name);
+	pr_debug("AFFS: find_entry(\"%.*s\")\n", (int)dentry->d_name.len, dentry->d_name.name);
 
-	bh = affs_bread(dir->i_dev,dir->i_ino,AFFS_I2BSIZE(dir));
+	bh = affs_bread(sb, dir->i_ino);
 	if (!bh)
-		return NULL;
+		return ERR_PTR(-EIO);
 
-	if (namelen == 1 && name[0] == '.') {
-		*ino = dir->i_ino;
-		return bh;
-	}
-	if (namelen == 2 && name[0] == '.' && name[1] == '.') {
-		*ino = affs_parent_ino(dir);
-		return bh;
-	}
-
-	key = AFFS_GET_HASHENTRY(bh->b_data,affs_hash_name(name,namelen,intl,AFFS_I2HSIZE(dir)));
+	key = be32_to_cpu(AFFS_HEAD(bh)->table[affs_hash_name(sb, dentry->d_name.name, dentry->d_name.len)]);
 
 	for (;;) {
-		unsigned char *cname;
-		int cnamelen;
-
 		affs_brelse(bh);
-		bh = NULL;
 		if (key == 0)
-			break;
-		bh = affs_bread(dir->i_dev,key,AFFS_I2BSIZE(dir));
+			return NULL;
+		bh = affs_bread(sb, key);
 		if (!bh)
-			break;
-		cnamelen = affs_get_file_name(AFFS_I2BSIZE(dir),bh->b_data,&cname);
-		if (affs_match(name,namelen,cname,cnamelen,intl))
-			break;
-		key = be32_to_cpu(FILE_END(bh->b_data,dir)->hash_chain);
+			return ERR_PTR(-EIO);
+		if (affs_match(dentry, AFFS_TAIL(sb, bh)->name, toupper))
+			return bh;
+		key = be32_to_cpu(AFFS_TAIL(sb, bh)->hash_chain);
 	}
-	*ino = key;
-	return bh;
 }
 
 struct dentry *
 affs_lookup(struct inode *dir, struct dentry *dentry)
 {
-	unsigned long		 ino;
-	struct buffer_head	*bh;
-	struct inode		*inode;
+	struct super_block *sb = dir->i_sb;
+	struct buffer_head *bh;
+	struct inode *inode = NULL;
 
 	pr_debug("AFFS: lookup(\"%.*s\")\n",(int)dentry->d_name.len,dentry->d_name.name);
 
-	inode = NULL;
-	bh = affs_find_entry(dir,dentry,&ino);
+	down(&AFFS_DIR->i_hash_lock);
+	bh = affs_find_entry(dir, dentry);
+	up(&AFFS_DIR->i_hash_lock);
+	if (IS_ERR(bh))
+		return ERR_PTR(PTR_ERR(bh));
 	if (bh) {
-		if (FILE_END(bh->b_data,dir)->original)
-			ino = be32_to_cpu(FILE_END(bh->b_data,dir)->original);
+		u32 ino = bh->b_blocknr;
+
+		/* store the real header ino in d_fsdata for faster lookups */
+		dentry->d_fsdata = (void *)ino;
+		switch (be32_to_cpu(AFFS_TAIL(sb, bh)->stype)) {
+		case ST_LINKDIR:
+		case ST_LINKFILE:
+			ino = be32_to_cpu(AFFS_TAIL(sb, bh)->original);
+		}
 		affs_brelse(bh);
-		inode = iget(dir->i_sb,ino);
+		inode = iget(sb, ino);
 		if (!inode)
 			return ERR_PTR(-EACCES);
 	}
-	dentry->d_op = &affs_dentry_operations;
-	d_add(dentry,inode);
+	dentry->d_op = AFFS_SB->s_flags & SF_INTL ? &affs_intl_dentry_operations : &affs_dentry_operations;
+	d_add(dentry, inode);
 	return NULL;
 }
 
 int
 affs_unlink(struct inode *dir, struct dentry *dentry)
 {
-	int			 retval;
-	struct buffer_head	*bh;
-	unsigned long		 ino;
-	struct inode		*inode;
-
-	pr_debug("AFFS: unlink(dir=%ld,\"%.*s\")\n",dir->i_ino,
-		 (int)dentry->d_name.len,dentry->d_name.name);
+	pr_debug("AFFS: unlink(dir=%d, \"%.*s\")\n", (u32)dir->i_ino,
+		 (int)dentry->d_name.len, dentry->d_name.name);
 
-	retval  = -ENOENT;
-	if (!(bh = affs_find_entry(dir,dentry,&ino)))
-		goto unlink_done;
-
-	inode  = dentry->d_inode;
-
-	if ((retval = affs_remove_header(bh,inode)) < 0)
-		goto unlink_done;
-	
-	inode->i_nlink = retval;
-	inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
-	dir->i_version = ++event;
-	mark_inode_dirty(inode);
-	mark_inode_dirty(dir);
-	retval = 0;
+	if (!dentry->d_inode)
+		return -ENOENT;
 
-unlink_done:
-	affs_brelse(bh);
-	return retval;
+	return affs_remove_header(dentry);
 }
 
 int
 affs_create(struct inode *dir, struct dentry *dentry, int mode)
 {
+	struct super_block *sb = dir->i_sb;
 	struct inode	*inode;
 	int		 error;
-	
+
 	pr_debug("AFFS: create(%lu,\"%.*s\",0%o)\n",dir->i_ino,(int)dentry->d_name.len,
 		 dentry->d_name.name,mode);
 
-	error = -ENOSPC;
 	inode = affs_new_inode(dir);
 	if (!inode)
-		goto out;
+		return -ENOSPC;
 
-	pr_debug("AFFS: ino=%lu\n",inode->i_ino);
-	if (dir->i_sb->u.affs_sb.s_flags & SF_OFS) {
-		inode->i_op = &affs_file_inode_operations;
-		inode->i_fop = &affs_file_operations_ofs;
-	} else {
-		inode->i_op = &affs_file_inode_operations;
-		inode->i_fop = &affs_file_operations;
-		inode->i_mapping->a_ops = &affs_aops;
-		inode->u.affs_i.mmu_private = inode->i_size;
-	}
-	error = affs_add_entry(dir,NULL,inode,dentry,ST_FILE);
-	if (error)
-		goto out_iput;
 	inode->i_mode = mode;
-	inode->u.affs_i.i_protect = mode_to_prot(inode->i_mode);
-	d_instantiate(dentry,inode);
+	mode_to_prot(inode);
 	mark_inode_dirty(inode);
-	dir->i_version = ++event;
-	mark_inode_dirty(dir);
-out:
-	return error;
 
-out_iput:
-	inode->i_nlink = 0;
-	mark_inode_dirty(inode);
-	iput(inode);
-	goto out;
+	inode->i_op = &affs_file_inode_operations;
+	inode->i_fop = &affs_file_operations;
+	inode->i_mapping->a_ops = (AFFS_SB->s_flags & SF_OFS) ? &affs_aops_ofs : &affs_aops;
+	error = affs_add_entry(dir, inode, dentry, ST_FILE);
+	if (error) {
+		inode->i_nlink = 0;
+		iput(inode);
+		return error;
+	}
+	return 0;
 }
 
 int
@@ -305,121 +289,78 @@
 {
 	struct inode		*inode;
 	int			 error;
-	
+
 	pr_debug("AFFS: mkdir(%lu,\"%.*s\",0%o)\n",dir->i_ino,
 		 (int)dentry->d_name.len,dentry->d_name.name,mode);
 
-	error = -ENOSPC;
 	inode = affs_new_inode(dir);
 	if (!inode)
-		goto out;
+		return -ENOSPC;
+
+	inode->i_mode = S_IFDIR | mode;
+	mode_to_prot(inode);
 
 	inode->i_op = &affs_dir_inode_operations;
 	inode->i_fop = &affs_dir_operations;
-	error       = affs_add_entry(dir,NULL,inode,dentry,ST_USERDIR);
-	if (error)
-		goto out_iput;
-	inode->i_mode = S_IFDIR | S_ISVTX | mode;
-	inode->u.affs_i.i_protect = mode_to_prot(inode->i_mode);
-	d_instantiate(dentry,inode);
-	mark_inode_dirty(inode);
-	dir->i_version = ++event;
-	mark_inode_dirty(dir);
-out:
-	return error;
 
-out_iput:
-	inode->i_nlink = 0;
-	mark_inode_dirty(inode);
-	iput(inode);
-	goto out;
-}
-
-static int
-empty_dir(struct buffer_head *bh, int hashsize)
-{
-	while (--hashsize >= 0) {
-		if (((struct dir_front *)bh->b_data)->hashtable[hashsize])
-			return 0;
+	error = affs_add_entry(dir, inode, dentry, ST_USERDIR);
+	if (error) {
+		inode->i_nlink = 0;
+		mark_inode_dirty(inode);
+		iput(inode);
+		return error;
 	}
-	return 1;
+	return 0;
 }
 
 int
 affs_rmdir(struct inode *dir, struct dentry *dentry)
 {
-	struct inode		*inode = dentry->d_inode;
-	int			 retval;
-	unsigned long		 ino;
-	struct buffer_head	*bh;
-
-	pr_debug("AFFS: rmdir(dir=%lu,\"%.*s\")\n",dir->i_ino,
-		 (int)dentry->d_name.len,dentry->d_name.name);
-
-	retval = -ENOENT;
-	if (!(bh = affs_find_entry(dir,dentry,&ino)))
-		goto rmdir_done;
+	pr_debug("AFFS: rmdir(dir=%u, \"%.*s\")\n", (u32)dir->i_ino,
+		 (int)dentry->d_name.len, dentry->d_name.name);
 
-	/*
-	 * Make sure the directory is empty and the dentry isn't busy.
-	 */
-	retval = -ENOTEMPTY;
-	if (!empty_dir(bh,AFFS_I2HSIZE(inode)))
-		goto rmdir_done;
-	retval = -EBUSY;
-	if (!d_unhashed(dentry))
-		goto rmdir_done;
-
-	if ((retval = affs_remove_header(bh,inode)) < 0)
-		goto rmdir_done;
-	
-	inode->i_nlink = retval;
-	inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
-	retval         = 0;
-	dir->i_version = ++event;
-	mark_inode_dirty(dir);
-	mark_inode_dirty(inode);
+	if (!dentry->d_inode)
+		return -ENOENT;
 
-rmdir_done:
-	affs_brelse(bh);
-	return retval;
+	return affs_remove_header(dentry);
 }
 
 int
 affs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
 {
+	struct super_block	*sb = dir->i_sb;
 	struct buffer_head	*bh;
 	struct inode		*inode;
 	char			*p;
-	unsigned long		 tmp;
 	int			 i, maxlen, error;
 	char			 c, lc;
 
 	pr_debug("AFFS: symlink(%lu,\"%.*s\" -> \"%s\")\n",dir->i_ino,
 		 (int)dentry->d_name.len,dentry->d_name.name,symname);
-	
-	maxlen = 4 * AFFS_I2HSIZE(dir) - 1;
+
+	maxlen = AFFS_SB->s_hashsize * sizeof(u32) - 1;
 	error = -ENOSPC;
 	inode  = affs_new_inode(dir);
 	if (!inode)
-		goto out;
+		return -ENOSPC;
 
 	inode->i_op = &affs_symlink_inode_operations;
 	inode->i_data.a_ops = &affs_symlink_aops;
 	inode->i_mode = S_IFLNK | 0777;
-	inode->u.affs_i.i_protect = mode_to_prot(inode->i_mode);
+	mode_to_prot(inode);
+
 	error = -EIO;
-	bh = affs_bread(inode->i_dev,inode->i_ino,AFFS_I2BSIZE(inode));
+	bh = affs_bread(sb, inode->i_ino);
 	if (!bh)
-		goto out_iput;
+		goto err;
 	i  = 0;
-	p  = ((struct slink_front *)bh->b_data)->symname;
+	p  = (char *)AFFS_HEAD(bh)->table;
 	lc = '/';
 	if (*symname == '/') {
 		while (*symname == '/')
 			symname++;
-		while (inode->i_sb->u.affs_sb.s_volume[i])	/* Cannot overflow */
-			*p++ = inode->i_sb->u.affs_sb.s_volume[i++];
+		while (AFFS_SB->s_volume[i])	/* Cannot overflow */
+			*p++ = AFFS_SB->s_volume[i++];
 	}
 	while (i < maxlen && (c = *symname++)) {
 		if (c == '.' && lc == '/' && *symname == '.' && symname[1] == '/') {
@@ -444,159 +385,90 @@
 	affs_brelse(bh);
 	mark_inode_dirty(inode);
 
-	/* N.B. This test shouldn't be necessary ... dentry must be negative */
-	error = -EEXIST;
-	bh = affs_find_entry(dir,dentry,&tmp);
-	if (bh)
-		goto out_release;
-	/* N.B. Shouldn't we add the entry before dirtying the buffer? */
-	error = affs_add_entry(dir,NULL,inode,dentry,ST_SOFTLINK);
+	error = affs_add_entry(dir, inode, dentry, ST_SOFTLINK);
 	if (error)
-		goto out_release;
-	d_instantiate(dentry,inode);
-	dir->i_version = ++event;
-	mark_inode_dirty(dir);
+		goto err;
 
-out:
-	return error;
+	return 0;
 
-out_release:
-	affs_brelse(bh);
-out_iput:
+err:
 	inode->i_nlink = 0;
 	mark_inode_dirty(inode);
 	iput(inode);
-	goto out;
+	return error;
 }
 
 int
 affs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry)
 {
-	struct inode		*oldinode = old_dentry->d_inode;
-	struct inode		*inode;
-	struct buffer_head	*bh;
-	unsigned long		 i;
-	int			 error;
-	
-	pr_debug("AFFS: link(%lu,%lu,\"%.*s\")\n",oldinode->i_ino,dir->i_ino,
-		 (int)dentry->d_name.len,dentry->d_name.name);
+	struct inode *inode = old_dentry->d_inode;
+	int error;
 
-	/* N.B. Do we need this test? The dentry must be negative ... */
-	bh = affs_find_entry(dir,dentry,&i);
-	if (bh) {
-		affs_brelse(bh);
-		return -EEXIST;
-	}
-	if (oldinode->u.affs_i.i_hlink)	{	/* Cannot happen */
-		affs_warning(dir->i_sb,"link","Impossible link to link");
-		return -EINVAL;
-	}
-	error = -ENOSPC;
-	if (!(inode = affs_new_inode(dir)))
-		goto out;
+	pr_debug("AFFS: link(%u, %u, \"%.*s\")\n", (u32)inode->i_ino, (u32)dir->i_ino,
+		 (int)dentry->d_name.len,dentry->d_name.name);
 
-	inode->i_op                = oldinode->i_op;
-	inode->i_fop               = oldinode->i_fop;
-	inode->u.affs_i.i_protect  = mode_to_prot(oldinode->i_mode);
-	inode->u.affs_i.i_original = oldinode->i_ino;
-	inode->u.affs_i.i_hlink    = 1;
-	inode->i_mtime             = oldinode->i_mtime;
-
-	if (S_ISDIR(oldinode->i_mode))
-		error = affs_add_entry(dir,oldinode,inode,dentry,ST_LINKDIR);
-	else
-		error = affs_add_entry(dir,oldinode,inode,dentry,ST_LINKFILE);
-	if (error)
+	error = affs_add_entry(dir, inode, dentry, S_ISDIR(inode->i_mode) ? ST_LINKDIR : ST_LINKFILE);
+	if (error) {
 		inode->i_nlink = 0;
-	else {
-		dir->i_version = ++event;
-		mark_inode_dirty(dir);
-		mark_inode_dirty(oldinode);
-		atomic_inc(&oldinode->i_count);
-		d_instantiate(dentry,oldinode);
+		mark_inode_dirty(inode);
+		iput(inode);
+		return error;
 	}
-	mark_inode_dirty(inode);
-	iput(inode);
-
-out:
-	return error;
+	return 0;
 }
 
 int
 affs_rename(struct inode *old_dir, struct dentry *old_dentry,
 	    struct inode *new_dir, struct dentry *new_dentry)
 {
-	struct inode		*old_inode = old_dentry->d_inode;
-	struct inode		*new_inode = new_dentry->d_inode;
-	struct buffer_head	*old_bh;
-	struct buffer_head	*new_bh;
-	unsigned long		 old_ino;
-	unsigned long		 new_ino;
-	int			 retval;
-
-	pr_debug("AFFS: rename(old=%lu,\"%*s\" (inode=%p) to new=%lu,\"%*s\" (inode=%p))\n",
-		 old_dir->i_ino,old_dentry->d_name.len,old_dentry->d_name.name,old_inode,
-		 new_dir->i_ino,new_dentry->d_name.len,new_dentry->d_name.name,new_inode);
-	
-	if ((retval = affs_check_name(new_dentry->d_name.name,new_dentry->d_name.len)))
-		goto out;
+	struct super_block *sb = old_dir->i_sb;
+	struct inode *dir;
+	struct buffer_head *bh = NULL;
+	int retval;
+
+	pr_debug("AFFS: rename(old=%u,\"%*s\" to new=%u,\"%*s\")\n",
+		 (u32)old_dir->i_ino, (int)old_dentry->d_name.len, old_dentry->d_name.name,
+		 (u32)new_dir->i_ino, (int)new_dentry->d_name.len, new_dentry->d_name.name);
 
-	new_bh = NULL;
-	retval = -ENOENT;
-	old_bh = affs_find_entry(old_dir,old_dentry,&old_ino);
-	if (!old_bh)
-		goto end_rename;
-
-	new_bh = affs_find_entry(new_dir,new_dentry,&new_ino);
-	if (new_bh && !new_inode) {
-		affs_error(old_inode->i_sb,"affs_rename",
-			   "No inode for entry found (key=%lu)\n",new_ino);
-		goto end_rename;
-	}
-	if (S_ISDIR(old_inode->i_mode)) {
-		if (new_inode) {
-			retval = -EBUSY;
-			if (!d_unhashed(new_dentry))
-				goto end_rename;
-			retval = -ENOTEMPTY;
-			if (!empty_dir(new_bh,AFFS_I2HSIZE(new_inode)))
-				goto end_rename;
-		}
+	if ((retval = affs_check_name(new_dentry->d_name.name,new_dentry->d_name.len)))
+		goto done;
 
-		retval = -ENOENT;
-		if (affs_parent_ino(old_inode) != old_dir->i_ino)
-			goto end_rename;
-	}
 	/* Unlink destination if it already exists */
-	if (new_inode) {
-		if ((retval = affs_remove_header(new_bh,new_dir)) < 0)
-			goto end_rename;
-		new_inode->i_nlink = retval;
-		mark_inode_dirty(new_inode);
-		if (new_inode->i_ino == new_ino)
-			new_inode->i_nlink = 0;
+	if (new_dentry->d_inode) {
+		retval = affs_remove_header(new_dentry);
+		if (retval)
+			return retval;
 	}
+
+	retval = -EIO;
+	bh = affs_bread(sb, old_dentry->d_inode->i_ino);
+	if (!bh)
+		goto done;
+
 	/* Remove header from its parent directory. */
-	if ((retval = affs_remove_hash(old_bh,old_dir)))
-		goto end_rename;
+	dir = old_dir;
+	down(&AFFS_DIR->i_hash_lock);
+	retval = affs_remove_hash(dir, bh);
+	if (retval)
+		goto done_unlock;
+
+	up(&AFFS_DIR->i_hash_lock);
+
 	/* And insert it into the new directory with the new name. */
-	affs_copy_name(FILE_END(old_bh->b_data,old_inode)->file_name,new_dentry->d_name.name);
-	if ((retval = affs_insert_hash(new_dir->i_ino,old_bh,new_dir)))
-		goto end_rename;
-	affs_fix_checksum(AFFS_I2BSIZE(new_dir),old_bh->b_data,5);
-
-	new_dir->i_ctime   = new_dir->i_mtime = old_dir->i_ctime
-			   = old_dir->i_mtime = CURRENT_TIME;
-	new_dir->i_version = ++event;
-	old_dir->i_version = ++event;
-	retval             = 0;
-	mark_inode_dirty(new_dir);
-	mark_inode_dirty(old_dir);
-	mark_buffer_dirty(old_bh);
-	
-end_rename:
-	affs_brelse(old_bh);
-	affs_brelse(new_bh);
-out:
+	dir = new_dir;
+	affs_copy_name(AFFS_TAIL(sb, bh)->name, new_dentry);
+	affs_fix_checksum(sb, bh);
+	down(&AFFS_DIR->i_hash_lock);
+	retval = affs_insert_hash(new_dir, bh);
+	if (retval)
+		goto done_unlock;
+	up(&AFFS_DIR->i_hash_lock);
+
+done:
+	affs_brelse(bh);
 	return retval;
+
+done_unlock:
+	up(&AFFS_DIR->i_hash_lock);
+	goto done;
 }
diff -urN linux-2.4.4-pre5/fs/affs/super.c linux/fs/affs/super.c
--- linux-2.4.4-pre5/fs/affs/super.c	Mon Dec  3 22:36:15 2001
+++ linux/fs/affs/super.c	Mon Dec  3 22:36:26 2001
@@ -10,7 +10,6 @@
  *  (C) 1991  Linus Torvalds - minix filesystem
  */
 
-#define DEBUG 0
 #include <linux/module.h>
 #include <linux/errno.h>
 #include <linux/fs.h>
@@ -33,37 +32,27 @@
 extern int *blk_size[];
 extern struct timezone sys_tz;
 
-#define MIN(a,b) (((a)<(b))?(a):(b))
-
 static int affs_statfs(struct super_block *sb, struct statfs *buf);
 static int affs_remount (struct super_block *sb, int *flags, char *data);
 
 static void
 affs_put_super(struct super_block *sb)
 {
-	int	 i;
-
 	pr_debug("AFFS: put_super()\n");
 
-	for (i = 0; i < sb->u.affs_sb.s_bm_count; i++)
-		affs_brelse(sb->u.affs_sb.s_bitmap[i].bm_bh);
 	if (!(sb->s_flags & MS_RDONLY)) {
-		ROOT_END_S(sb->u.affs_sb.s_root_bh->b_data,sb)->bm_flag = be32_to_cpu(1);
+		AFFS_ROOT_TAIL(sb, AFFS_SB->s_root_bh)->bm_flag = be32_to_cpu(1);
 		secs_to_datestamp(CURRENT_TIME,
-				  &ROOT_END_S(sb->u.affs_sb.s_root_bh->b_data,sb)->disk_altered);
-		affs_fix_checksum(sb->s_blocksize,sb->u.affs_sb.s_root_bh->b_data,5);
-		mark_buffer_dirty(sb->u.affs_sb.s_root_bh);
+				  &AFFS_ROOT_TAIL(sb, AFFS_SB->s_root_bh)->disk_change);
+		affs_fix_checksum(sb, AFFS_SB->s_root_bh);
+		mark_buffer_dirty(AFFS_SB->s_root_bh);
 	}
 
-	if (sb->u.affs_sb.s_prefix)
-		kfree(sb->u.affs_sb.s_prefix);
-	kfree(sb->u.affs_sb.s_bitmap);
-	affs_brelse(sb->u.affs_sb.s_root_bh);
-
-	/*
-	 * Restore the previous value of this device's blksize_size[][]
-	 */
-	set_blocksize(sb->s_dev, sb->u.affs_sb.s_blksize);
+	affs_brelse(AFFS_SB->s_bmap_bh);
+	if (AFFS_SB->s_prefix)
+		kfree(AFFS_SB->s_prefix);
+	kfree(AFFS_SB->s_bitmap);
+	affs_brelse(AFFS_SB->s_root_bh);
 
 	return;
 }
@@ -71,24 +60,17 @@
 static void
 affs_write_super(struct super_block *sb)
 {
-	int	 i, clean = 2;
+	int clean = 2;
 
 	if (!(sb->s_flags & MS_RDONLY)) {
-		lock_super(sb);
-		for (i = 0, clean = 1; i < sb->u.affs_sb.s_bm_count; i++) {
-			if (sb->u.affs_sb.s_bitmap[i].bm_bh) {
-				if (buffer_dirty(sb->u.affs_sb.s_bitmap[i].bm_bh)) {
-					clean = 0;
-					break;
-				}
-			}
-		}
-		unlock_super(sb);
-		ROOT_END_S(sb->u.affs_sb.s_root_bh->b_data,sb)->bm_flag = be32_to_cpu(clean);
+		//	if (AFFS_SB->s_bitmap[i].bm_bh) {
+		//		if (buffer_dirty(AFFS_SB->s_bitmap[i].bm_bh)) {
+		//			clean = 0;
+		AFFS_ROOT_TAIL(sb, AFFS_SB->s_root_bh)->bm_flag = be32_to_cpu(clean);
 		secs_to_datestamp(CURRENT_TIME,
-				  &ROOT_END_S(sb->u.affs_sb.s_root_bh->b_data,sb)->disk_altered);
-		affs_fix_checksum(sb->s_blocksize,sb->u.affs_sb.s_root_bh->b_data,5);
-		mark_buffer_dirty(sb->u.affs_sb.s_root_bh);
+				  &AFFS_ROOT_TAIL(sb, AFFS_SB->s_root_bh)->disk_change);
+		affs_fix_checksum(sb, AFFS_SB->s_root_bh);
+		mark_buffer_dirty(AFFS_SB->s_root_bh);
 		sb->s_dirt = !clean;	/* redo until bitmap synced */
 	} else
 		sb->s_dirt = 0;
@@ -101,6 +83,7 @@
 	write_inode:	affs_write_inode,
 	put_inode:	affs_put_inode,
 	delete_inode:	affs_delete_inode,
+	clear_inode:	affs_clear_inode,
 	put_super:	affs_put_super,
 	write_super:	affs_write_super,
 	statfs:		affs_statfs,
@@ -236,65 +219,59 @@
  */
 
 static struct super_block *
-affs_read_super(struct super_block *s, void *data, int silent)
+affs_read_super(struct super_block *sb, void *data, int silent)
 {
-	struct buffer_head	*bh = NULL;
-	struct buffer_head	*bb;
-	struct inode		*root_inode;
-	kdev_t			 dev = s->s_dev;
+	struct buffer_head	*root_bh = NULL;
+	struct buffer_head	*boot_bh;
+	struct inode		*root_inode = NULL;
+	kdev_t			 dev = sb->s_dev;
 	s32			 root_block;
 	int			 blocks, size, blocksize;
 	u32			 chksum;
-	u32			*bm;
-	s32			 ptype, stype;
-	int			 mapidx;
 	int			 num_bm;
 	int			 i, j;
 	s32			 key;
 	uid_t			 uid;
 	gid_t			 gid;
 	int			 reserved;
-	int			 az_no;
-	int			 bmalt = 0;
 	unsigned long		 mount_flags;
-	unsigned long		 offset;
 
 	pr_debug("AFFS: read_super(%s)\n",data ? (const char *)data : "no options");
 
-	s->s_magic             = AFFS_SUPER_MAGIC;
-	s->s_op                = &affs_sops;
-	s->u.affs_sb.s_bitmap  = NULL;
-	s->u.affs_sb.s_root_bh = NULL;
-	s->u.affs_sb.s_prefix  = NULL;
-	s->u.affs_sb.s_hashsize= 0;
+	sb->s_magic             = AFFS_SUPER_MAGIC;
+	sb->s_op                = &affs_sops;
+	memset(AFFS_SB, 0, sizeof(struct affs_sb_info));
+	init_MUTEX(&AFFS_SB->s_bmlock);
 
 	if (!parse_options(data,&uid,&gid,&i,&reserved,&root_block,
-				&blocksize,&s->u.affs_sb.s_prefix,
-				s->u.affs_sb.s_volume, &mount_flags))
-		goto out_bad_opts;
+				&blocksize,&AFFS_SB->s_prefix,
+				AFFS_SB->s_volume, &mount_flags)) {
+		printk(KERN_ERR "AFFS: Error parsing options\n");
+		return NULL;
+	}
 	/* N.B. after this point s_prefix must be released */
 
-	s->u.affs_sb.s_flags   = mount_flags;
-	s->u.affs_sb.s_mode    = i;
-	s->u.affs_sb.s_uid     = uid;
-	s->u.affs_sb.s_gid     = gid;
-	s->u.affs_sb.s_reserved= reserved;
+	AFFS_SB->s_flags   = mount_flags;
+	AFFS_SB->s_mode    = i;
+	AFFS_SB->s_uid     = uid;
+	AFFS_SB->s_gid     = gid;
+	AFFS_SB->s_reserved= reserved;
 
 	/* Get the size of the device in 512-byte blocks.
 	 * If we later see that the partition uses bigger
 	 * blocks, we will have to change it.
 	 */
 
-	blocks = blk_size[MAJOR(dev)][MINOR(dev)];
-	if (blocks == 0)
-		goto out_bad_size;
-	s->u.affs_sb.s_blksize = blksize_size[MAJOR(dev)][MINOR(dev)];
-	if (!s->u.affs_sb.s_blksize)
-		s->u.affs_sb.s_blksize = BLOCK_SIZE;
-	size = (s->u.affs_sb.s_blksize / 512) * blocks;
-	pr_debug("AFFS: initial blksize=%d, blocks=%d\n",
-		s->u.affs_sb.s_blksize, blocks);
+	blocks = blk_size[MAJOR(dev)] ? blk_size[MAJOR(dev)][MINOR(dev)] : 0;
+	if (!blocks) {
+		printk(KERN_ERR "AFFS: Could not determine device size\n");
+		goto out_error;
+	}
+	size = (BLOCK_SIZE / 512) * blocks;
+	pr_debug("AFFS: initial blksize=%d, blocks=%d\n", 512, blocks);
 
+#warning
+	affs_set_blocksize(sb, PAGE_SIZE);
 	/* Try to find root block. Its location depends on the block size. */
 
 	i = 512;
@@ -304,11 +281,12 @@
 		size = size / (blocksize / 512);
 	}
 	for (blocksize = i, key = 0; blocksize <= j; blocksize <<= 1, size >>= 1) {
-		s->u.affs_sb.s_root_block = root_block;
+		AFFS_SB->s_root_block = root_block;
 		if (root_block < 0)
-			s->u.affs_sb.s_root_block = (reserved + size - 1) / 2;
+			AFFS_SB->s_root_block = (reserved + size - 1) / 2;
 		pr_debug("AFFS: setting blocksize to %d\n", blocksize);
-		set_blocksize(dev, blocksize);
+		affs_set_blocksize(sb, blocksize);
+		AFFS_SB->s_partition_size = size;
 
 		/* The root block location that was calculated above is not
 		 * correct if the partition size is an odd number of 512-
@@ -324,293 +302,137 @@
 			pr_debug("AFFS: Dev %s, trying root=%u, bs=%d, "
 				"size=%d, reserved=%d\n",
 				kdevname(dev),
-				s->u.affs_sb.s_root_block + num_bm,
+				AFFS_SB->s_root_block + num_bm,
 				blocksize, size, reserved);
-			bh = affs_bread(dev, s->u.affs_sb.s_root_block + num_bm,
-					blocksize);
-			if (!bh)
+			root_bh = affs_bread(sb, AFFS_SB->s_root_block + num_bm);
+			if (!root_bh)
 				continue;
-			if (!affs_checksum_block(blocksize,bh->b_data,&ptype,&stype) &&
-			    ptype == T_SHORT && stype == ST_ROOT) {
-				s->s_blocksize             = blocksize;
-				s->u.affs_sb.s_hashsize    = blocksize / 4 - 56;
-				s->u.affs_sb.s_root_block += num_bm;
+			if (!affs_checksum_block(sb, root_bh) &&
+			    be32_to_cpu(AFFS_ROOT_HEAD(root_bh)->ptype) == T_SHORT &&
+			    be32_to_cpu(AFFS_ROOT_TAIL(sb, root_bh)->stype) == ST_ROOT) {
+				AFFS_SB->s_hashsize    = blocksize / 4 - 56;
+				AFFS_SB->s_root_block += num_bm;
 				key                        = 1;
 				goto got_root;
 			}
-			affs_brelse(bh);
-			bh = NULL;
+			affs_brelse(root_bh);
+			root_bh = NULL;
 		}
 	}
-	goto out_no_valid_block;
+	if (!silent)
+		printk(KERN_ERR "AFFS: No valid root block on device %s\n",
+			kdevname(dev));
+	goto out_error;
 
 	/* N.B. after this point bh must be released */
 got_root:
-	root_block = s->u.affs_sb.s_root_block;
+	root_block = AFFS_SB->s_root_block;
 
-	s->u.affs_sb.s_partition_size   = size;
-	s->s_blocksize_bits             = blocksize == 512 ? 9 :
-					  blocksize == 1024 ? 10 :
-					  blocksize == 2048 ? 11 : 12;
+	sb->s_blocksize_bits = blocksize == 512 ? 9 :
+			       blocksize == 1024 ? 10 :
+			       blocksize == 2048 ? 11 : 12;
 
 	/* Find out which kind of FS we have */
-	bb = affs_bread(dev,0,s->s_blocksize);
-	if (!bb)
-		goto out_no_root_block;
-	chksum = be32_to_cpu(*(u32 *)bb->b_data);
-	affs_brelse(bb);
+	boot_bh = bread(sb->s_dev, 0, sb->s_blocksize);
+	if (!boot_bh) {
+		printk(KERN_ERR "AFFS: Cannot read boot block\n");
+		goto out_error;
+	}
+	chksum = be32_to_cpu(*(u32 *)boot_bh->b_data);
+	brelse(boot_bh);
 
 	/* Dircache filesystems are compatible with non-dircache ones
 	 * when reading. As long as they aren't supported, writing is
 	 * not recommended.
 	 */
 	if ((chksum == FS_DCFFS || chksum == MUFS_DCFFS || chksum == FS_DCOFS
-	     || chksum == MUFS_DCOFS) && !(s->s_flags & MS_RDONLY)) {
+	     || chksum == MUFS_DCOFS) && !(sb->s_flags & MS_RDONLY)) {
 		printk(KERN_NOTICE "AFFS: Dircache FS - mounting %s read only\n",
 			kdevname(dev));
-		s->s_flags |= MS_RDONLY;
-		s->u.affs_sb.s_flags |= SF_READONLY;
+		sb->s_flags |= MS_RDONLY;
+		AFFS_SB->s_flags |= SF_READONLY;
 	}
 	switch (chksum) {
 		case MUFS_FS:
 		case MUFS_INTLFFS:
-			s->u.affs_sb.s_flags |= SF_MUFS;
+			AFFS_SB->s_flags |= SF_MUFS;
 			/* fall thru */
 		case FS_INTLFFS:
-			s->u.affs_sb.s_flags |= SF_INTL;
+			AFFS_SB->s_flags |= SF_INTL;
 			break;
 		case MUFS_DCFFS:
 		case MUFS_FFS:
-			s->u.affs_sb.s_flags |= SF_MUFS;
+			AFFS_SB->s_flags |= SF_MUFS;
 			break;
 		case FS_DCFFS:
 		case FS_FFS:
 			break;
 		case MUFS_OFS:
-			s->u.affs_sb.s_flags |= SF_MUFS;
+			AFFS_SB->s_flags |= SF_MUFS;
 			/* fall thru */
 		case FS_OFS:
-			s->u.affs_sb.s_flags |= SF_OFS;
-			s->s_flags |= MS_NOEXEC;
+			AFFS_SB->s_flags |= SF_OFS;
+			sb->s_flags |= MS_NOEXEC;
 			break;
 		case MUFS_DCOFS:
 		case MUFS_INTLOFS:
-			s->u.affs_sb.s_flags |= SF_MUFS;
+			AFFS_SB->s_flags |= SF_MUFS;
 		case FS_DCOFS:
 		case FS_INTLOFS:
-			s->u.affs_sb.s_flags |= SF_INTL | SF_OFS;
-			s->s_flags |= MS_NOEXEC;
+			AFFS_SB->s_flags |= SF_INTL | SF_OFS;
+			sb->s_flags |= MS_NOEXEC;
 			break;
 		default:
-			goto out_unknown_fs;
+			printk(KERN_ERR "AFFS: Unknown filesystem on device %s: %08X\n",
+				kdevname(dev), chksum);
+			goto out_error;
 	}
 
 	if (mount_flags & SF_VERBOSE) {
 		chksum = cpu_to_be32(chksum);
 		printk(KERN_NOTICE "AFFS: Mounting volume \"%*s\": Type=%.3s\\%c, Blocksize=%d\n",
-			GET_END_PTR(struct root_end,bh->b_data,blocksize)->disk_name[0],
-			&GET_END_PTR(struct root_end,bh->b_data,blocksize)->disk_name[1],
+			AFFS_ROOT_TAIL(sb, root_bh)->disk_name[0],
+			AFFS_ROOT_TAIL(sb, root_bh)->disk_name + 1,
 			(char *)&chksum,((char *)&chksum)[3] + '0',blocksize);
 	}
 
-	s->s_flags |= MS_NODEV | MS_NOSUID;
+	sb->s_flags |= MS_NODEV | MS_NOSUID;
+
+	AFFS_SB->s_data_blksize = sb->s_blocksize;
+	if (AFFS_SB->s_flags & SF_OFS)
+		AFFS_SB->s_data_blksize -= 24;
 
 	/* Keep super block in cache */
-	bb = affs_bread(dev,root_block,s->s_blocksize);
-	if (!bb)
-		goto out_no_root_block;
-	s->u.affs_sb.s_root_bh = bb;
+	AFFS_SB->s_root_bh = root_bh;
 	/* N.B. after this point s_root_bh must be released */
 
-	/* Allocate space for bitmaps, zones and others */
-
-	size   = s->u.affs_sb.s_partition_size - reserved;
-	num_bm = (size + s->s_blocksize * 8 - 32 - 1) / (s->s_blocksize * 8 - 32);
-	az_no  = (size + AFFS_ZONE_SIZE - 1) / (AFFS_ZONE_SIZE - 32);
-	ptype  = num_bm * sizeof(struct affs_bm_info) +
-		 az_no * sizeof(struct affs_alloc_zone) +
-		 MAX_ZONES * sizeof(struct affs_zone);
-	pr_debug("AFFS: num_bm=%d, az_no=%d, sum=%d\n",num_bm,az_no,ptype);
-	if (!(s->u.affs_sb.s_bitmap = kmalloc(ptype, GFP_KERNEL)))
-		goto out_no_bitmap;
-	memset(s->u.affs_sb.s_bitmap,0,ptype);
-	/* N.B. after the point s_bitmap must be released */
-
-	s->u.affs_sb.s_zones   = (struct affs_zone *)&s->u.affs_sb.s_bitmap[num_bm];
-	s->u.affs_sb.s_alloc   = (struct affs_alloc_zone *)&s->u.affs_sb.s_zones[MAX_ZONES];
-	s->u.affs_sb.s_num_az  = az_no;
-
-	mapidx = 0;
-
-	if (ROOT_END_S(bh->b_data,s)->bm_flag == 0) {
-		if (!(s->s_flags & MS_RDONLY)) {
-			printk(KERN_NOTICE "AFFS: Bitmap invalid - mounting %s read only\n",
-				kdevname(dev));
-			s->s_flags |= MS_RDONLY;
-		}
-		affs_brelse(bh);
-		bh = NULL;
-		goto nobitmap;
-	}
-
-	/* The following section is ugly, I know. Especially because of the
-	 * reuse of some variables that are not named properly.
-	 */
-
-	key    = root_block;
-	ptype  = s->s_blocksize / 4 - 49;
-	stype  = ptype + 25;
-	offset = s->u.affs_sb.s_reserved;
-	az_no  = 0;
-	while (bh) {
-		bm = (u32 *)bh->b_data;
-		for (i = ptype; i < stype && bm[i]; i++, mapidx++) {
-			if (mapidx >= num_bm) {
-				printk(KERN_ERR "AFFS: Extraneous bitmap pointer - "
-					       "mounting %s read only.\n",kdevname(dev));
-				s->s_flags |= MS_RDONLY;
-				s->u.affs_sb.s_flags |= SF_READONLY;
-				continue;
-			}
-			bb = affs_bread(dev,be32_to_cpu(bm[i]),s->s_blocksize);
-			if (!bb)
-				goto out_no_read_bm;
-			if (affs_checksum_block(s->s_blocksize,bb->b_data,NULL,NULL) &&
-			    !(s->s_flags & MS_RDONLY)) {
-				printk(KERN_WARNING "AFFS: Bitmap (%d,key=%u) invalid - "
-				       "mounting %s read only.\n",mapidx,be32_to_cpu(bm[i]),
-					kdevname(dev));
-				s->s_flags |= MS_RDONLY;
-				s->u.affs_sb.s_flags |= SF_READONLY;
-			}
-			/* Mark unused bits in the last word as allocated */
-			if (size <= s->s_blocksize * 8 - 32) {	/* last bitmap */
-				ptype = size / 32 + 1;		/* word number */
-				key   = size & 0x1F;		/* used bits */
-				if (key && !(s->s_flags & MS_RDONLY)) {
-					chksum = cpu_to_be32(0x7FFFFFFF >> (31 - key));
-					((u32 *)bb->b_data)[ptype] &= chksum;
-					affs_fix_checksum(s->s_blocksize,bb->b_data,0);
-					mark_buffer_dirty(bb);
-					bmalt = 1;
-				}
-				ptype = (size + 31) & ~0x1F;
-				size  = 0;
-				s->u.affs_sb.s_flags |= SF_BM_VALID;
-			} else {
-				ptype = s->s_blocksize * 8 - 32;
-				size -= ptype;
-			}
-			s->u.affs_sb.s_bitmap[mapidx].bm_firstblk = offset;
-			s->u.affs_sb.s_bitmap[mapidx].bm_bh       = NULL;
-			s->u.affs_sb.s_bitmap[mapidx].bm_key      = be32_to_cpu(bm[i]);
-			s->u.affs_sb.s_bitmap[mapidx].bm_count    = 0;
-			offset += ptype;
-
-			for (j = 0; ptype > 0; j++, az_no++, ptype -= key) {
-				key = MIN(ptype,AFFS_ZONE_SIZE);	/* size in bits */
-				s->u.affs_sb.s_alloc[az_no].az_size = key / 32;
-				s->u.affs_sb.s_alloc[az_no].az_free =
-					affs_count_free_bits(key / 8,bb->b_data +
-					     j * (AFFS_ZONE_SIZE / 8) + 4);
-			}
-			affs_brelse(bb);
-		}
-		key   = be32_to_cpu(bm[stype]);		/* Next block of bitmap pointers	*/
-		ptype = 0;
-		stype = s->s_blocksize / 4 - 1;
-		affs_brelse(bh);
-		bh = NULL;
-		if (key) {
-			bh = affs_bread(dev,key,s->s_blocksize);
-			if (!bh)
-				goto out_no_bm_ext;
-		}
-	}
-	if (mapidx < num_bm)
-		goto out_bad_num;
-
-nobitmap:
-	s->u.affs_sb.s_bm_count = num_bm;
+	if (affs_init_bitmap(sb))
+		goto out_error;
 
 	/* set up enough so that it can read an inode */
 
-	s->s_dirt  = 1;
-	root_inode = iget(s,root_block);
-	if (!root_inode)
-		goto out_no_root;
-	s->s_root  = d_alloc_root(root_inode);
-	if (!s->s_root)
-		goto out_no_root;
-	s->s_root->d_op = &affs_dentry_operations;
-
-	/* Record date of last change if the bitmap was truncated and
-	 * create data zones if the volume is writable.
-	 */
-
-	if (!(s->s_flags & MS_RDONLY)) {
-		if (bmalt) {
-			secs_to_datestamp(CURRENT_TIME,&ROOT_END(
-				s->u.affs_sb.s_root_bh->b_data,root_inode)->disk_altered);
-			affs_fix_checksum(s->s_blocksize,s->u.affs_sb.s_root_bh->b_data,5);
-			mark_buffer_dirty(s->u.affs_sb.s_root_bh);
-		}
-		affs_make_zones(s);
+	root_inode = iget(sb, root_block);
+	sb->s_root = d_alloc_root(root_inode);
+	if (!sb->s_root) {
+		printk(KERN_ERR "AFFS: Get root inode failed\n");
+		goto out_error;
 	}
+	sb->s_root->d_op = &affs_dentry_operations;
 
-	pr_debug("AFFS: s_flags=%lX\n",s->s_flags);
-	return s;
-
-out_bad_opts:
-	printk(KERN_ERR "AFFS: Error parsing options\n");
-	goto out_fail;
-out_bad_size:
-	printk(KERN_ERR "AFFS: Could not determine device size\n");
-	goto out_free_prefix;
-out_no_valid_block:
-	if (!silent)
-		printk(KERN_ERR "AFFS: No valid root block on device %s\n",
-			kdevname(dev));
-	goto out_restore;
-out_unknown_fs:
-	printk(KERN_ERR "AFFS: Unknown filesystem on device %s: %08X\n",
-		kdevname(dev), chksum);
-	goto out_free_bh;
-out_no_root_block:
-	printk(KERN_ERR "AFFS: Cannot read root block\n");
-	goto out_free_bh;
-out_no_bitmap:
-	printk(KERN_ERR "AFFS: Bitmap allocation failed\n");
-	goto out_free_root_block;
-out_no_read_bm:
-	printk(KERN_ERR "AFFS: Cannot read bitmap\n");
-	goto out_free_bitmap;
-out_no_bm_ext:
-	printk(KERN_ERR "AFFS: Cannot read bitmap extension\n");
-	goto out_free_bitmap;
-out_bad_num:
-	printk(KERN_ERR "AFFS: Got only %d bitmap blocks, expected %d\n",
-		mapidx, num_bm);
-	goto out_free_bitmap;
-out_no_root:
-	printk(KERN_ERR "AFFS: Get root inode failed\n");
+	pr_debug("AFFS: s_flags=%lX\n",sb->s_flags);
+	return sb;
 
 	/*
 	 * Begin the cascaded cleanup ...
 	 */
-	iput(root_inode);
-out_free_bitmap:
-	kfree(s->u.affs_sb.s_bitmap);
-out_free_root_block:
-	affs_brelse(s->u.affs_sb.s_root_bh);
-out_free_bh:
-	affs_brelse(bh);
-out_restore:
-	set_blocksize(dev, s->u.affs_sb.s_blksize);
-out_free_prefix:
-	if (s->u.affs_sb.s_prefix)
-		kfree(s->u.affs_sb.s_prefix);
-out_fail:
+out_error:
+	if (root_inode)
+		iput(root_inode);
+	if (AFFS_SB->s_bitmap)
+		kfree(AFFS_SB->s_bitmap);
+	affs_brelse(root_bh);
+	if (AFFS_SB->s_prefix)
+		kfree(AFFS_SB->s_prefix);
 	return NULL;
 }
 
@@ -624,17 +446,17 @@
 	int			 reserved;
 	int			 root_block;
 	unsigned long		 mount_flags;
-	unsigned long		 read_only = sb->u.affs_sb.s_flags & SF_READONLY;
+	unsigned long		 read_only = AFFS_SB->s_flags & SF_READONLY;
 
 	pr_debug("AFFS: remount(flags=0x%x,opts=\"%s\")\n",*flags,data);
 
 	if (!parse_options(data,&uid,&gid,&mode,&reserved,&root_block,
-	    &blocksize,&sb->u.affs_sb.s_prefix,sb->u.affs_sb.s_volume,&mount_flags))
+	    &blocksize,&AFFS_SB->s_prefix,AFFS_SB->s_volume,&mount_flags))
 		return -EINVAL;
-	sb->u.affs_sb.s_flags = mount_flags | read_only;
-	sb->u.affs_sb.s_mode  = mode;
-	sb->u.affs_sb.s_uid   = uid;
-	sb->u.affs_sb.s_gid   = gid;
+	AFFS_SB->s_flags = mount_flags | read_only;
+	AFFS_SB->s_mode  = mode;
+	AFFS_SB->s_uid   = uid;
+	AFFS_SB->s_gid   = gid;
 
 	if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY))
 		return 0;
@@ -643,9 +465,8 @@
 		while (sb->s_dirt)
 			affs_write_super(sb);
 		sb->s_flags |= MS_RDONLY;
-	} else if (!(sb->u.affs_sb.s_flags & SF_READONLY)) {
+	} else if (!(AFFS_SB->s_flags & SF_READONLY)) {
 		sb->s_flags &= ~MS_RDONLY;
-		affs_make_zones(sb);
 	} else {
 		affs_warning(sb,"remount","Cannot remount fs read/write because of errors");
 		return -EINVAL;
@@ -658,13 +479,13 @@
 {
 	int		 free;
 
-	pr_debug("AFFS: statfs() partsize=%d, reserved=%d\n",sb->u.affs_sb.s_partition_size,
-	     sb->u.affs_sb.s_reserved);
+	pr_debug("AFFS: statfs() partsize=%d, reserved=%d\n",AFFS_SB->s_partition_size,
+	     AFFS_SB->s_reserved);
 
 	free          = affs_count_free_blocks(sb);
 	buf->f_type    = AFFS_SUPER_MAGIC;
 	buf->f_bsize   = sb->s_blocksize;
-	buf->f_blocks  = sb->u.affs_sb.s_partition_size - sb->u.affs_sb.s_reserved;
+	buf->f_blocks  = AFFS_SB->s_partition_size - AFFS_SB->s_reserved;
 	buf->f_bfree   = free;
 	buf->f_bavail  = free;
 	return 0;
diff -urN linux-2.4.4-pre5/fs/affs/symlink.c linux/fs/affs/symlink.c
--- linux-2.4.4-pre5/fs/affs/symlink.c	Fri Dec 29 14:07:57 2000
+++ linux/fs/affs/symlink.c	Mon Dec  3 22:36:26 2001
@@ -32,7 +32,7 @@
 
 	err = -EIO;
 	lock_kernel();
-	bh = affs_bread(inode->i_dev,inode->i_ino,AFFS_I2BSIZE(inode));
+	bh = affs_bread(inode->i_sb, inode->i_ino);
 	unlock_kernel();
 	if (!bh)
 		goto fail;
diff -urN linux-2.4.4-pre5/fs/autofs4/expire.c linux/fs/autofs4/expire.c
--- linux-2.4.4-pre5/fs/autofs4/expire.c	Mon Oct 23 21:57:38 2000
+++ linux/fs/autofs4/expire.c	Mon Dec  3 22:36:26 2001
@@ -98,8 +98,6 @@
 		 top, count));
 	this_parent = top;
 
-	count--;	/* top is passed in after being dgot */
-
 	if (is_autofs4_dentry(top)) {
 		count--;
 		DPRINTK(("is_tree_busy: autofs; count=%d\n", count));
@@ -168,8 +166,6 @@
 	unsigned long timeout;
 	struct dentry *root = sb->s_root;
 	struct list_head *tmp;
-	struct dentry *d;
-	struct vfsmount *p;
 
 	if (!sbi->exp_timeout || !root)
 		return NULL;
@@ -208,23 +204,17 @@
 			     attempts if expire fails the first time */
 			ino->last_used = now;
 		}
-		p = mntget(mnt);
-		d = dget_locked(dentry);
-
-		if (!is_tree_busy(p, d)) {
+		if (!is_tree_busy(mnt, dentry)) {
 			DPRINTK(("autofs_expire: returning %p %.*s\n",
 				 dentry, (int)dentry->d_name.len, dentry->d_name.name));
 			/* Start from here next time */
 			list_del(&root->d_subdirs);
 			list_add(&root->d_subdirs, &dentry->d_child);
+			dget(dentry);
 			spin_unlock(&dcache_lock);
 
-			dput(d);
-			mntput(p);
 			return dentry;
 		}
-		dput(d);
-		mntput(p);
 	}
 	spin_unlock(&dcache_lock);
 
@@ -251,6 +241,7 @@
 	pkt.len = dentry->d_name.len;
 	memcpy(pkt.name, dentry->d_name.name, pkt.len);
 	pkt.name[pkt.len] = '\0';
+	dput(dentry);
 
 	if ( copy_to_user(pkt_p, &pkt, sizeof(struct autofs_packet_expire)) )
 		return -EFAULT;
@@ -278,6 +269,7 @@
 		de_info->flags |= AUTOFS_INF_EXPIRING;
 		ret = autofs4_wait(sbi, &dentry->d_name, NFY_EXPIRE);
 		de_info->flags &= ~AUTOFS_INF_EXPIRING;
+		dput(dentry);
 	}
 		
 	return ret;
diff -urN linux-2.4.4-pre5/fs/namei.c linux/fs/namei.c
--- linux-2.4.4-pre5/fs/namei.c	Thu Mar 22 11:07:58 2001
+++ linux/fs/namei.c	Mon Dec  3 22:36:26 2001
@@ -113,7 +113,7 @@
 	if ((unsigned long) filename >= TASK_SIZE) {
 		if (!segment_eq(get_fs(), KERNEL_DS))
 			return -EFAULT;
-	} else if (TASK_SIZE - (unsigned long) filename < PAGE_SIZE)
+	} else if (TASK_SIZE - (unsigned long) filename < PATH_MAX + 1)
 		len = TASK_SIZE - (unsigned long) filename;
 
 	retval = strncpy_from_user((char *)page, filename, len);
diff -urN linux-2.4.4-pre5/fs/reiserfs/journal.c linux/fs/reiserfs/journal.c
--- linux-2.4.4-pre5/fs/reiserfs/journal.c	Mon Dec  3 22:36:15 2001
+++ linux/fs/reiserfs/journal.c	Mon Dec  3 22:36:26 2001
@@ -1814,16 +1814,14 @@
 ** then run the per filesystem commit task queue when we wakeup.
 */
 static int reiserfs_journal_commit_thread(void *nullp) {
-  exit_files(current);
-  exit_mm(current);
+
+  daemonize() ;
 
   spin_lock_irq(&current->sigmask_lock);
   sigfillset(&current->blocked);
   recalc_sigpending(current);
   spin_unlock_irq(&current->sigmask_lock);
 
-  current->session = 1;
-  current->pgrp = 1;
   sprintf(current->comm, "kreiserfsd") ;
   lock_kernel() ;
   while(1) {
diff -urN linux-2.4.4-pre5/include/asm-alpha/semaphore.h linux/include/asm-alpha/semaphore.h
--- linux-2.4.4-pre5/include/asm-alpha/semaphore.h	Mon Dec  3 22:36:15 2001
+++ linux/include/asm-alpha/semaphore.h	Mon Dec  3 22:36:26 2001
@@ -225,5 +225,3 @@
 #endif
 
 #endif
-
-#endif
diff -urN linux-2.4.4-pre5/include/asm-i386/rwsem-spin.h linux/include/asm-i386/rwsem-spin.h
--- linux-2.4.4-pre5/include/asm-i386/rwsem-spin.h	Mon Dec  3 22:36:16 2001
+++ linux/include/asm-i386/rwsem-spin.h	Wed Dec 31 16:00:00 1969
@@ -1,318 +0,0 @@
-/* rwsem.h: R/W semaphores based on spinlocks
- *
- * Written by David Howells (dhowells@redhat.com).
- *
- * Derived from asm-i386/semaphore.h and asm-i386/spinlock.h
- */
-
-#ifndef _I386_RWSEM_SPIN_H
-#define _I386_RWSEM_SPIN_H
-
-#include <linux/config.h>
-
-#ifndef _LINUX_RWSEM_H
-#error please dont include asm/rwsem-spin.h directly, use linux/rwsem.h instead
-#endif
-
-#include <linux/spinlock.h>
-
-#ifdef __KERNEL__
-
-/*
- * the semaphore definition
- */
-struct rw_semaphore {
-	signed long		count;
-#define RWSEM_UNLOCKED_VALUE		0x00000000
-#define RWSEM_ACTIVE_BIAS		0x00000001
-#define RWSEM_ACTIVE_MASK		0x0000ffff
-#define RWSEM_WAITING_BIAS		(-0x00010000)
-#define RWSEM_ACTIVE_READ_BIAS		RWSEM_ACTIVE_BIAS
-#define RWSEM_ACTIVE_WRITE_BIAS		(RWSEM_WAITING_BIAS + RWSEM_ACTIVE_BIAS)
-	spinlock_t		lock;
-#define RWSEM_SPINLOCK_OFFSET_STR	"4" /* byte offset of spinlock */
-	wait_queue_head_t	wait;
-#define RWSEM_WAITING_FOR_READ	WQ_FLAG_CONTEXT_0	/* bits to use in wait_queue_t.flags */
-#define RWSEM_WAITING_FOR_WRITE	WQ_FLAG_CONTEXT_1
-#if RWSEM_DEBUG
-	int			debug;
-#endif
-#if RWSEM_DEBUG_MAGIC
-	long			__magic;
-	atomic_t		readers;
-	atomic_t		writers;
-#endif
-};
-
-/*
- * initialisation
- */
-#if RWSEM_DEBUG
-#define __RWSEM_DEBUG_INIT      , 0
-#else
-#define __RWSEM_DEBUG_INIT	/* */
-#endif
-#if RWSEM_DEBUG_MAGIC
-#define __RWSEM_DEBUG_MINIT(name)	, (int)&(name).__magic, ATOMIC_INIT(0), ATOMIC_INIT(0)
-#else
-#define __RWSEM_DEBUG_MINIT(name)	/* */
-#endif
-
-#define __RWSEM_INITIALIZER(name) \
-{ RWSEM_UNLOCKED_VALUE, SPIN_LOCK_UNLOCKED, \
-	__WAIT_QUEUE_HEAD_INITIALIZER((name).wait) \
-	__RWSEM_DEBUG_INIT __RWSEM_DEBUG_MINIT(name) }
-
-#define DECLARE_RWSEM(name) \
-	struct rw_semaphore name = __RWSEM_INITIALIZER(name)
-
-static inline void init_rwsem(struct rw_semaphore *sem)
-{
-	sem->count = RWSEM_UNLOCKED_VALUE;
-	spin_lock_init(&sem->lock);
-	init_waitqueue_head(&sem->wait);
-#if RWSEM_DEBUG
-	sem->debug = 0;
-#endif
-#if RWSEM_DEBUG_MAGIC
-	sem->__magic = (long)&sem->__magic;
-	atomic_set(&sem->readers, 0);
-	atomic_set(&sem->writers, 0);
-#endif
-}
-
-/*
- * lock for reading
- */
-static inline void __down_read(struct rw_semaphore *sem)
-{
-	__asm__ __volatile__(
-		"# beginning down_read\n\t"
-#ifdef CONFIG_SMP
-LOCK_PREFIX	"  decb      "RWSEM_SPINLOCK_OFFSET_STR"(%%eax)\n\t" /* try to grab the spinlock */
-		"  js        3f\n" /* jump if failed */
-		"1:\n\t"
-#endif
-		"  incl      (%%eax)\n\t" /* adds 0x00000001, returns the old value */
-#ifdef CONFIG_SMP
-		"  movb      $1,"RWSEM_SPINLOCK_OFFSET_STR"(%%eax)\n\t" /* release the spinlock */
-#endif
-		"  js        4f\n\t" /* jump if we weren't granted the lock */
-		"2:\n"
-		".section .text.lock,\"ax\"\n"
-#ifdef CONFIG_SMP
-		"3:\n\t" /* spin on the spinlock till we get it */
-		"  cmpb      $0,"RWSEM_SPINLOCK_OFFSET_STR"(%%eax)\n\t"
-		"  rep;nop   \n\t"
-		"  jle       3b\n\t"
-		"  jmp       1b\n"
-#endif
-		"4:\n\t"
-		"  call      __rwsem_down_read_failed\n\t"
-		"  jmp       2b\n"
-		".previous"
-		"# ending __down_read\n\t"
-		: "=m"(sem->count), "=m"(sem->lock)
-		: "a"(sem), "m"(sem->count), "m"(sem->lock)
-		: "memory");
-}
-
-/*
- * lock for writing
- */
-static inline void __down_write(struct rw_semaphore *sem)
-{
-	int tmp;
-
-	tmp = RWSEM_ACTIVE_WRITE_BIAS;
-	__asm__ __volatile__(
-		"# beginning down_write\n\t"
-#ifdef CONFIG_SMP
-LOCK_PREFIX	"  decb      "RWSEM_SPINLOCK_OFFSET_STR"(%%eax)\n\t" /* try to grab the spinlock */
-		"  js        3f\n" /* jump if failed */
-		"1:\n\t"
-#endif
-		"  xchg      %0,(%%eax)\n\t" /* retrieve the old value */
-		"  add       %0,(%%eax)\n\t" /* add 0xffff0001, result in memory */
-#ifdef CONFIG_SMP
-		"  movb      $1,"RWSEM_SPINLOCK_OFFSET_STR"(%%eax)\n\t" /* release the spinlock */
-#endif
-		"  testl     %0,%0\n\t" /* was the count 0 before? */
-		"  jnz       4f\n\t" /* jump if we weren't granted the lock */
-		"2:\n\t"
-		".section .text.lock,\"ax\"\n"
-#ifdef CONFIG_SMP
-		"3:\n\t" /* spin on the spinlock till we get it */
-		"  cmpb      $0,"RWSEM_SPINLOCK_OFFSET_STR"(%%eax)\n\t"
-		"  rep;nop   \n\t"
-		"  jle       3b\n\t"
-		"  jmp       1b\n"
-#endif
-		"4:\n\t"
-		"  call     __rwsem_down_write_failed\n\t"
-		"  jmp      2b\n"
-		".previous\n"
-		"# ending down_write"
-		: "+r"(tmp), "=m"(sem->count), "=m"(sem->lock)
-		: "a"(sem), "m"(sem->count), "m"(sem->lock)
-		: "memory");
-}
-
-/*
- * unlock after reading
- */
-static inline void __up_read(struct rw_semaphore *sem)
-{
-	int tmp;
-
-	tmp = -RWSEM_ACTIVE_READ_BIAS;
-	__asm__ __volatile__(
-		"# beginning __up_read\n\t"
-#ifdef CONFIG_SMP
-LOCK_PREFIX	"  decb      "RWSEM_SPINLOCK_OFFSET_STR"(%%eax)\n\t" /* try to grab the spinlock */
-		"  js        3f\n" /* jump if failed */
-		"1:\n\t"
-#endif
-		"  xchg      %0,(%%eax)\n\t" /* retrieve the old value */
-		"  addl      %0,(%%eax)\n\t" /* subtract 1, result in memory */
-#ifdef CONFIG_SMP
-		"  movb      $1,"RWSEM_SPINLOCK_OFFSET_STR"(%%eax)\n\t" /* release the spinlock */
-#endif
-		"  js        4f\n\t" /* jump if the lock is being waited upon */
-		"2:\n\t"
-		".section .text.lock,\"ax\"\n"
-#ifdef CONFIG_SMP
-		"3:\n\t" /* spin on the spinlock till we get it */
-		"  cmpb      $0,"RWSEM_SPINLOCK_OFFSET_STR"(%%eax)\n\t"
-		"  rep;nop   \n\t"
-		"  jle       3b\n\t"
-		"  jmp       1b\n"
-#endif
-		"4:\n\t"
-		"  decl      %0\n\t" /* xchg gave us the old count */
-		"  testl     %4,%0\n\t" /* do nothing if still outstanding active readers */
-		"  jnz       2b\n\t"
-		"  call      __rwsem_wake\n\t"
-		"  jmp       2b\n"
-		".previous\n"
-		"# ending __up_read\n"
-		: "+r"(tmp), "=m"(sem->count), "=m"(sem->lock)
-		: "a"(sem), "i"(RWSEM_ACTIVE_MASK), "m"(sem->count), "m"(sem->lock)
-		: "memory");
-}
-
-/*
- * unlock after writing
- */
-static inline void __up_write(struct rw_semaphore *sem)
-{
-	__asm__ __volatile__(
-		"# beginning __up_write\n\t"
-#ifdef CONFIG_SMP
-LOCK_PREFIX	"  decb      "RWSEM_SPINLOCK_OFFSET_STR"(%%eax)\n\t" /* try to grab the spinlock */
-		"  js        3f\n" /* jump if failed */
-		"1:\n\t"
-#endif
-		"  addl      %3,(%%eax)\n\t" /* adds 0x00010001 */
-#ifdef CONFIG_SMP
-		"  movb      $1,"RWSEM_SPINLOCK_OFFSET_STR"(%%eax)\n\t" /* release the spinlock */
-#endif
-		"  js        4f\n\t" /* jump if the lock is being waited upon */
-		"2:\n\t"
-		".section .text.lock,\"ax\"\n"
-#ifdef CONFIG_SMP
-		"3:\n\t" /* spin on the spinlock till we get it */
-		"  cmpb      $0,"RWSEM_SPINLOCK_OFFSET_STR"(%%eax)\n\t"
-		"  rep;nop   \n\t"
-		"  jle       3b\n\t"
-		"  jmp       1b\n"
-#endif
-		"4:\n\t"
-		"  call     __rwsem_wake\n\t"
-		"  jmp      2b\n"
-		".previous\n"
-		"# ending __up_write\n"
-		: "=m"(sem->count), "=m"(sem->lock)
-		: "a"(sem), "i"(-RWSEM_ACTIVE_WRITE_BIAS), "m"(sem->count), "m"(sem->lock)
-		: "memory");
-}
-
-/*
- * implement exchange and add functionality
- */
-static inline int rwsem_atomic_update(int delta, struct rw_semaphore *sem)
-{
-	int tmp = delta;
-
-	__asm__ __volatile__(
-		"# beginning rwsem_atomic_update\n\t"
-#ifdef CONFIG_SMP
-LOCK_PREFIX	"  decb      "RWSEM_SPINLOCK_OFFSET_STR"(%1)\n\t" /* try to grab the spinlock */
-		"  js        3f\n" /* jump if failed */
-		"1:\n\t"
-#endif
-		"  xchgl     %0,(%1)\n\t" /* retrieve the old value */
-		"  addl      %0,(%1)\n\t" /* add 0xffff0001, result in memory */
-#ifdef CONFIG_SMP
-		"  movb      $1,"RWSEM_SPINLOCK_OFFSET_STR"(%1)\n\t" /* release the spinlock */
-#endif
-		".section .text.lock,\"ax\"\n"
-#ifdef CONFIG_SMP
-		"3:\n\t" /* spin on the spinlock till we get it */
-		"  cmpb      $0,"RWSEM_SPINLOCK_OFFSET_STR"(%1)\n\t"
-		"  rep;nop   \n\t"
-		"  jle       3b\n\t"
-		"  jmp       1b\n"
-#endif
-		".previous\n"
-		"# ending rwsem_atomic_update\n\t"
-		: "+r"(tmp)
-		: "r"(sem)
-		: "memory");
-
-	return tmp+delta;
-}
-
-/*
- * implement compare and exchange functionality on the rw-semaphore count LSW
- */
-static inline __u16 rwsem_cmpxchgw(struct rw_semaphore *sem, __u16 old, __u16 new)
-{
-	__u16 prev;
-
-	__asm__ __volatile__(
-		"# beginning rwsem_cmpxchgw\n\t"
-#ifdef CONFIG_SMP
-LOCK_PREFIX	"  decb      "RWSEM_SPINLOCK_OFFSET_STR"(%3)\n\t" /* try to grab the spinlock */
-		"  js        3f\n" /* jump if failed */
-		"1:\n\t"
-#endif
-		"  cmpw      %w1,(%3)\n\t"
-		"  jne       4f\n\t" /* jump if old doesn't match sem->count LSW */
-		"  movw      %w2,(%3)\n\t" /* replace sem->count LSW with the new value */
-		"2:\n\t"
-#ifdef CONFIG_SMP
-		"  movb      $1,"RWSEM_SPINLOCK_OFFSET_STR"(%3)\n\t" /* release the spinlock */
-#endif
-		".section .text.lock,\"ax\"\n"
-#ifdef CONFIG_SMP
-		"3:\n\t" /* spin on the spinlock till we get it */
-		"  cmpb      $0,"RWSEM_SPINLOCK_OFFSET_STR"(%3)\n\t"
-		"  rep;nop   \n\t"
-		"  jle       3b\n\t"
-		"  jmp       1b\n"
-#endif
-		"4:\n\t"
-		"  movw      (%3),%w0\n" /* we'll want to return the current value */
-		"  jmp       2b\n"
-		".previous\n"
-		"# ending rwsem_cmpxchgw\n\t"
-		: "=r"(prev)
-		: "r0"(old), "r"(new), "r"(sem)
-		: "memory");
-
-	return prev;
-}
-
-#endif /* __KERNEL__ */
-#endif /* _I386_RWSEM_SPIN_H */
diff -urN linux-2.4.4-pre5/include/asm-i386/rwsem-xadd.h linux/include/asm-i386/rwsem-xadd.h
--- linux-2.4.4-pre5/include/asm-i386/rwsem-xadd.h	Mon Dec  3 22:36:16 2001
+++ linux/include/asm-i386/rwsem-xadd.h	Wed Dec 31 16:00:00 1969
@@ -1,194 +0,0 @@
-/* rwsem-xadd.h: R/W semaphores implemented using XADD/CMPXCHG
- *
- * Written by David Howells (dhowells@redhat.com), 2001.
- * Derived from asm-i386/semaphore.h
- */
-
-#ifndef _I386_RWSEM_XADD_H
-#define _I386_RWSEM_XADD_H
-
-#ifndef _LINUX_RWSEM_H
-#error please dont include asm/rwsem-xadd.h directly, use linux/rwsem.h instead
-#endif
-
-#ifdef __KERNEL__
-
-/*
- * the semaphore definition
- */
-struct rw_semaphore {
-	signed long		count;
-#define RWSEM_UNLOCKED_VALUE		0x00000000
-#define RWSEM_ACTIVE_BIAS		0x00000001
-#define RWSEM_ACTIVE_MASK		0x0000ffff
-#define RWSEM_WAITING_BIAS		(-0x00010000)
-#define RWSEM_ACTIVE_READ_BIAS		RWSEM_ACTIVE_BIAS
-#define RWSEM_ACTIVE_WRITE_BIAS		(RWSEM_WAITING_BIAS + RWSEM_ACTIVE_BIAS)
-	wait_queue_head_t	wait;
-#define RWSEM_WAITING_FOR_READ	WQ_FLAG_CONTEXT_0	/* bits to use in wait_queue_t.flags */
-#define RWSEM_WAITING_FOR_WRITE	WQ_FLAG_CONTEXT_1
-#if RWSEM_DEBUG
-	int			debug;
-#endif
-#if RWSEM_DEBUG_MAGIC
-	long			__magic;
-	atomic_t		readers;
-	atomic_t		writers;
-#endif
-};
-
-/*
- * initialisation
- */
-#if RWSEM_DEBUG
-#define __RWSEM_DEBUG_INIT      , 0
-#else
-#define __RWSEM_DEBUG_INIT	/* */
-#endif
-#if RWSEM_DEBUG_MAGIC
-#define __RWSEM_DEBUG_MINIT(name)	, (int)&(name).__magic, ATOMIC_INIT(0), ATOMIC_INIT(0)
-#else
-#define __RWSEM_DEBUG_MINIT(name)	/* */
-#endif
-
-#define __RWSEM_INITIALIZER(name) \
-{ RWSEM_UNLOCKED_VALUE, __WAIT_QUEUE_HEAD_INITIALIZER((name).wait) \
-	__RWSEM_DEBUG_INIT __RWSEM_DEBUG_MINIT(name) }
-
-#define DECLARE_RWSEM(name) \
-	struct rw_semaphore name = __RWSEM_INITIALIZER(name)
-
-static inline void init_rwsem(struct rw_semaphore *sem)
-{
-	sem->count = RWSEM_UNLOCKED_VALUE;
-	init_waitqueue_head(&sem->wait);
-#if RWSEM_DEBUG
-	sem->debug = 0;
-#endif
-#if RWSEM_DEBUG_MAGIC
-	sem->__magic = (long)&sem->__magic;
-	atomic_set(&sem->readers, 0);
-	atomic_set(&sem->writers, 0);
-#endif
-}
-
-/*
- * lock for reading
- */
-static inline void __down_read(struct rw_semaphore *sem)
-{
-	__asm__ __volatile__(
-		"# beginning down_read\n\t"
-LOCK_PREFIX	"  incl      (%%eax)\n\t" /* adds 0x00000001, returns the old value */
-		"  js        2f\n\t" /* jump if we weren't granted the lock */
-		"1:\n\t"
-		".section .text.lock,\"ax\"\n"
-		"2:\n\t"
-		"  call      __rwsem_down_read_failed\n\t"
-		"  jmp       1b\n"
-		".previous"
-		"# ending down_read\n\t"
-		: "=m"(sem->count)
-		: "a"(sem), "m"(sem->count)
-		: "memory");
-}
-
-/*
- * lock for writing
- */
-static inline void __down_write(struct rw_semaphore *sem)
-{
-	int tmp;
-
-	tmp = RWSEM_ACTIVE_WRITE_BIAS;
-	__asm__ __volatile__(
-		"# beginning down_write\n\t"
-LOCK_PREFIX	"  xadd      %0,(%%eax)\n\t" /* subtract 0x00010001, returns the old value */
-		"  testl     %0,%0\n\t" /* was the count 0 before? */
-		"  jnz       2f\n\t" /* jump if we weren't granted the lock */
-		"1:\n\t"
-		".section .text.lock,\"ax\"\n"
-		"2:\n\t"
-		"  call      __rwsem_down_write_failed\n\t"
-		"  jmp       1b\n"
-		".previous\n"
-		"# ending down_write"
-		: "+r"(tmp), "=m"(sem->count)
-		: "a"(sem), "m"(sem->count)
-		: "memory");
-}
-
-/*
- * unlock after reading
- */
-static inline void __up_read(struct rw_semaphore *sem)
-{
-	int tmp;
-
-	tmp = -RWSEM_ACTIVE_READ_BIAS;
-	__asm__ __volatile__(
-		"# beginning __up_read\n\t"
-LOCK_PREFIX	"  xadd      %0,(%%eax)\n\t" /* subtracts 1, returns the old value */
-		"  js        2f\n\t" /* jump if the lock is being waited upon */
-		"1:\n\t"
-		".section .text.lock,\"ax\"\n"
-		"2:\n\t"
-		"  decl      %0\n\t" /* xadd gave us the old count */
-		"  testl     %3,%0\n\t" /* do nothing if still outstanding active readers */
-		"  jnz       1b\n\t"
-		"  call      __rwsem_wake\n\t"
-		"  jmp       1b\n"
-		".previous\n"
-		"# ending __up_read\n"
-		: "+r"(tmp), "=m"(sem->count)
-		: "a"(sem), "i"(RWSEM_ACTIVE_MASK), "m"(sem->count)
-		: "memory");
-}
-
-/*
- * unlock after writing
- */
-static inline void __up_write(struct rw_semaphore *sem)
-{
-	__asm__ __volatile__(
-		"# beginning __up_write\n\t"
-LOCK_PREFIX	"  addl      %2,(%%eax)\n\t" /* adds 0x0000ffff */
-		"  js        2f\n\t" /* jump if the lock is being waited upon */
-		"1:\n\t"
-		".section .text.lock,\"ax\"\n"
-		"2:\n\t"
-		"  call      __rwsem_wake\n\t"
-		"  jmp       1b\n"
-		".previous\n"
-		"# ending __up_write\n"
-		: "=m"(sem->count)
-		: "a"(sem), "i"(-RWSEM_ACTIVE_WRITE_BIAS), "m"(sem->count)
-		: "memory");
-}
-
-/*
- * implement exchange and add functionality
- */
-static inline int rwsem_atomic_update(int delta, struct rw_semaphore *sem)
-{
-	int tmp = delta;
-
-	__asm__ __volatile__(
-		LOCK_PREFIX "xadd %0,(%1)"
-		: "+r"(tmp)
-		: "r"(sem)
-		: "memory");
-
-	return tmp+delta;
-}
-
-/*
- * implement compare and exchange functionality on the rw-semaphore count LSW
- */
-static inline __u16 rwsem_cmpxchgw(struct rw_semaphore *sem, __u16 old, __u16 new)
-{
-	return cmpxchg((__u16*)&sem->count,0,RWSEM_ACTIVE_BIAS);
-}
-
-#endif /* __KERNEL__ */
-#endif /* _I386_RWSEM_XADD_H */
diff -urN linux-2.4.4-pre5/include/asm-i386/rwsem.h linux/include/asm-i386/rwsem.h
--- linux-2.4.4-pre5/include/asm-i386/rwsem.h	Mon Dec  3 22:36:16 2001
+++ linux/include/asm-i386/rwsem.h	Mon Dec  3 22:36:26 2001
@@ -1,4 +1,4 @@
-/* rwsem.h: R/W semaphores optimised using i386 assembly
+/* rwsem.h: R/W semaphores implemented using XADD/CMPXCHG for i486+
  *
  * Written by David Howells (dhowells@redhat.com).
  *
@@ -14,16 +14,194 @@
 
 #ifdef __KERNEL__
 
-#ifdef CONFIG_X86_XADD
-#include <asm/rwsem-xadd.h> /* use XADD based semaphores if possible */
-#else
-#include <asm/rwsem-spin.h> /* use optimised spinlock based semaphores otherwise */
-#endif
+#include <linux/list.h>
+#include <linux/spinlock.h>
 
 /* we use FASTCALL convention for the helpers */
 extern struct rw_semaphore *FASTCALL(__rwsem_down_read_failed(struct rw_semaphore *sem));
 extern struct rw_semaphore *FASTCALL(__rwsem_down_write_failed(struct rw_semaphore *sem));
 extern struct rw_semaphore *FASTCALL(__rwsem_wake(struct rw_semaphore *sem));
+
+struct rwsem_waiter;
+
+/*
+ * the semaphore definition
+ */
+struct rw_semaphore {
+	signed long		count;
+#define RWSEM_UNLOCKED_VALUE		0x00000000
+#define RWSEM_ACTIVE_BIAS		0x00000001
+#define RWSEM_ACTIVE_MASK		0x0000ffff
+#define RWSEM_WAITING_BIAS		(-0x00010000)
+#define RWSEM_ACTIVE_READ_BIAS		RWSEM_ACTIVE_BIAS
+#define RWSEM_ACTIVE_WRITE_BIAS		(RWSEM_WAITING_BIAS + RWSEM_ACTIVE_BIAS)
+	spinlock_t		wait_lock;
+	struct rwsem_waiter	*wait_front;
+	struct rwsem_waiter	**wait_back;
+#if RWSEM_DEBUG
+	int			debug;
+#endif
+#if RWSEM_DEBUG_MAGIC
+	long			__magic;
+	atomic_t		readers;
+	atomic_t		writers;
+#endif
+};
+
+/*
+ * initialisation
+ */
+#if RWSEM_DEBUG
+#define __RWSEM_DEBUG_INIT      , 0
+#else
+#define __RWSEM_DEBUG_INIT	/* */
+#endif
+#if RWSEM_DEBUG_MAGIC
+#define __RWSEM_DEBUG_MINIT(name)	, (int)&(name).__magic, ATOMIC_INIT(0), ATOMIC_INIT(0)
+#else
+#define __RWSEM_DEBUG_MINIT(name)	/* */
+#endif
+
+#define __RWSEM_INITIALIZER(name) \
+{ RWSEM_UNLOCKED_VALUE, SPIN_LOCK_UNLOCKED, NULL, &(name).wait_front \
+	__RWSEM_DEBUG_INIT __RWSEM_DEBUG_MINIT(name) }
+
+#define DECLARE_RWSEM(name) \
+	struct rw_semaphore name = __RWSEM_INITIALIZER(name)
+
+static inline void init_rwsem(struct rw_semaphore *sem)
+{
+	sem->count = RWSEM_UNLOCKED_VALUE;
+	spin_lock_init(&sem->wait_lock);
+	sem->wait_front = NULL;
+	sem->wait_back = &sem->wait_front;
+#if RWSEM_DEBUG
+	sem->debug = 0;
+#endif
+#if RWSEM_DEBUG_MAGIC
+	sem->__magic = (long)&sem->__magic;
+	atomic_set(&sem->readers, 0);
+	atomic_set(&sem->writers, 0);
+#endif
+}
+
+/*
+ * lock for reading
+ */
+static inline void __down_read(struct rw_semaphore *sem)
+{
+	__asm__ __volatile__(
+		"# beginning down_read\n\t"
+LOCK_PREFIX	"  incl      (%%eax)\n\t" /* adds 0x00000001, returns the old value */
+		"  js        2f\n\t" /* jump if we weren't granted the lock */
+		"1:\n\t"
+		".section .text.lock,\"ax\"\n"
+		"2:\n\t"
+		"  call      __rwsem_down_read_failed\n\t"
+		"  jmp       1b\n"
+		".previous"
+		"# ending down_read\n\t"
+		: "=m"(sem->count)
+		: "a"(sem), "m"(sem->count)
+		: "memory");
+}
+
+/*
+ * lock for writing
+ */
+static inline void __down_write(struct rw_semaphore *sem)
+{
+	int tmp;
+
+	tmp = RWSEM_ACTIVE_WRITE_BIAS;
+	__asm__ __volatile__(
+		"# beginning down_write\n\t"
+LOCK_PREFIX	"  xadd      %0,(%%eax)\n\t" /* subtract 0x00010001, returns the old value */
+		"  testl     %0,%0\n\t" /* was the count 0 before? */
+		"  jnz       2f\n\t" /* jump if we weren't granted the lock */
+		"1:\n\t"
+		".section .text.lock,\"ax\"\n"
+		"2:\n\t"
+		"  call      __rwsem_down_write_failed\n\t"
+		"  jmp       1b\n"
+		".previous\n"
+		"# ending down_write"
+		: "+r"(tmp), "=m"(sem->count)
+		: "a"(sem), "m"(sem->count)
+		: "memory");
+}
+
+/*
+ * unlock after reading
+ */
+static inline void __up_read(struct rw_semaphore *sem)
+{
+	int tmp;
+
+	tmp = -RWSEM_ACTIVE_READ_BIAS;
+	__asm__ __volatile__(
+		"# beginning __up_read\n\t"
+LOCK_PREFIX	"  xadd      %0,(%%eax)\n\t" /* subtracts 1, returns the old value */
+		"  js        2f\n\t" /* jump if the lock is being waited upon */
+		"1:\n\t"
+		".section .text.lock,\"ax\"\n"
+		"2:\n\t"
+		"  decl      %0\n\t" /* xadd gave us the old count */
+		"  testl     %3,%0\n\t" /* do nothing if still outstanding active readers */
+		"  jnz       1b\n\t"
+		"  call      __rwsem_wake\n\t"
+		"  jmp       1b\n"
+		".previous\n"
+		"# ending __up_read\n"
+		: "+r"(tmp), "=m"(sem->count)
+		: "a"(sem), "i"(RWSEM_ACTIVE_MASK), "m"(sem->count)
+		: "memory");
+}
+
+/*
+ * unlock after writing
+ */
+static inline void __up_write(struct rw_semaphore *sem)
+{
+	__asm__ __volatile__(
+		"# beginning __up_write\n\t"
+LOCK_PREFIX	"  addl      %2,(%%eax)\n\t" /* adds 0x0000ffff */
+		"  js        2f\n\t" /* jump if the lock is being waited upon */
+		"1:\n\t"
+		".section .text.lock,\"ax\"\n"
+		"2:\n\t"
+		"  call      __rwsem_wake\n\t"
+		"  jmp       1b\n"
+		".previous\n"
+		"# ending __up_write\n"
+		: "=m"(sem->count)
+		: "a"(sem), "i"(-RWSEM_ACTIVE_WRITE_BIAS), "m"(sem->count)
+		: "memory");
+}
+
+/*
+ * implement exchange and add functionality
+ */
+static inline int rwsem_atomic_update(int delta, struct rw_semaphore *sem)
+{
+	int tmp = delta;
+
+	__asm__ __volatile__(
+		LOCK_PREFIX "xadd %0,(%1)"
+		: "+r"(tmp)
+		: "r"(sem)
+		: "memory");
+
+	return tmp+delta;
+}
+
+/*
+ * implement compare and exchange functionality on the rw-semaphore count LSW
+ */
+static inline __u16 rwsem_cmpxchgw(struct rw_semaphore *sem, __u16 old, __u16 new)
+{
+	return cmpxchg((__u16*)&sem->count,0,RWSEM_ACTIVE_BIAS);
+}
 
 #endif /* __KERNEL__ */
 #endif /* _I386_RWSEM_H */
diff -urN linux-2.4.4-pre5/include/asm-sparc64/rwsem.h linux/include/asm-sparc64/rwsem.h
--- linux-2.4.4-pre5/include/asm-sparc64/rwsem.h	Mon Dec  3 22:36:16 2001
+++ linux/include/asm-sparc64/rwsem.h	Mon Dec  3 22:36:26 2001
@@ -13,6 +13,8 @@
 
 #ifdef __KERNEL__
 
+struct rwsem_waiter;
+
 struct rw_semaphore {
 	signed int count;
 #define RWSEM_UNLOCKED_VALUE		0x00000000
@@ -21,13 +23,13 @@
 #define RWSEM_WAITING_BIAS		0xffff0000
 #define RWSEM_ACTIVE_READ_BIAS		RWSEM_ACTIVE_BIAS
 #define RWSEM_ACTIVE_WRITE_BIAS		(RWSEM_WAITING_BIAS + RWSEM_ACTIVE_BIAS)
-	wait_queue_head_t wait;
-#define RWSEM_WAITING_FOR_READ	WQ_FLAG_CONTEXT_0	/* bits to use in wait_queue_t.flags */
-#define RWSEM_WAITING_FOR_WRITE	WQ_FLAG_CONTEXT_1
+	spinlock_t		wait_lock;
+	struct rwsem_waiter	*wait_front;
+	struct rwsem_waiter	**wait_back;
 };
 
 #define __RWSEM_INITIALIZER(name) \
-{ RWSEM_UNLOCKED_VALUE, __WAIT_QUEUE_HEAD_INITIALIZER((name).wait) }
+{ RWSEM_UNLOCKED_VALUE, SPIN_LOCK_UNLOCKED, NULL, &(name).wait_front }
 
 #define DECLARE_RWSEM(name) \
 	struct rw_semaphore name = __RWSEM_INITIALIZER(name)
diff -urN linux-2.4.4-pre5/include/linux/affs_fs.h linux/include/linux/affs_fs.h
--- linux-2.4.4-pre5/include/linux/affs_fs.h	Mon Dec 11 13:26:57 2000
+++ linux/include/linux/affs_fs.h	Mon Dec  3 22:36:26 2001
@@ -8,58 +8,36 @@
 
 #define AFFS_SUPER_MAGIC 0xadff
 
-/* Get the filesystem block size given an inode. */
-#define AFFS_I2BSIZE(inode) ((inode)->i_sb->s_blocksize)
-
-/* Get the filesystem hash table size given an inode. */
-#define AFFS_I2HSIZE(inode) ((inode)->i_sb->u.affs_sb.s_hashsize)
-
-/* Get the block number bits given an inode */
-#define AFFS_I2BITS(inode) ((inode)->i_sb->s_blocksize_bits)
-
-/* Get the fs type given an inode */
-#define AFFS_I2FSTYPE(inode) ((inode)->i_sb->u.affs_sb.s_flags & SF_INTL)
-
-struct DateStamp
-{
-  u32 ds_Days;
-  u32 ds_Minute;
-  u32 ds_Tick;
-};
+struct affs_date;
 
 /* --- Prototypes -----------------------------------------------------------------------------	*/
 
 /* amigaffs.c */
 
-extern int	affs_get_key_entry(int bsize, void *data, int entry_pos);
-extern int	affs_get_file_name(int bsize, void *fh_data, unsigned char **name);
-extern u32	affs_checksum_block(int bsize, void *data, s32 *ptype, s32 *stype);
-extern void	affs_fix_checksum(int bsize, void *data, int cspos);
-extern void	secs_to_datestamp(time_t secs, struct DateStamp *ds);
-extern int	prot_to_mode(unsigned int prot);
-extern u32	mode_to_prot(int mode);
-extern int	affs_insert_hash(unsigned long dir_ino, struct buffer_head *header,
-			struct inode *inode);
-extern int	affs_remove_hash(struct buffer_head *bh, struct inode *inode);
-extern int	affs_remove_link(struct buffer_head *bh, struct inode *inode);
-extern int	affs_remove_header(struct buffer_head *bh, struct inode *inode);
+extern int	affs_insert_hash(struct inode *inode, struct buffer_head *bh);
+extern int	affs_remove_hash(struct inode *dir, struct buffer_head *rem_bh);
+extern int	affs_remove_header(struct dentry *dentry);
+extern u32	affs_checksum_block(struct super_block *sb, struct buffer_head *bh);
+extern void	affs_fix_checksum(struct super_block *sb, struct buffer_head *bh);
+extern void	secs_to_datestamp(time_t secs, struct affs_date *ds);
+extern mode_t	prot_to_mode(u32 prot);
+extern void	mode_to_prot(struct inode *inode);
 extern void	affs_error(struct super_block *sb, const char *function, const char *fmt, ...);
 extern void	affs_warning(struct super_block *sb, const char *function, const char *fmt, ...);
 extern int	affs_check_name(const unsigned char *name, int len);
-extern int	affs_copy_name(unsigned char *bstr, const unsigned char *name);
+extern int	affs_copy_name(unsigned char *bstr, struct dentry *dentry);
 
 /* bitmap. c */
 
-extern int	affs_count_free_blocks(struct super_block *s);
-extern int	affs_count_free_bits(int blocksize, const char *data);
-extern void	affs_free_block(struct super_block *sb, s32 block);
-extern s32	affs_new_header(struct inode *inode);
-extern s32	affs_new_data(struct inode *inode);
-extern void	affs_make_zones(struct super_block *sb);
+extern u32	affs_count_free_bits(u32 blocksize, const void *data);
+extern u32	affs_count_free_blocks(struct super_block *s);
+extern void	affs_free_block(struct super_block *sb, u32 block);
+extern u32	affs_alloc_block(struct inode *inode, u32 goal);
+extern int	affs_init_bitmap(struct super_block *sb);
 
 /* namei.c */
 
-extern int	affs_hash_name(const unsigned char *name, int len, int intl, int hashsize);
+extern int	affs_hash_name(struct super_block *sb, const u8 *name, unsigned int len);
 extern struct dentry *affs_lookup(struct inode *dir, struct dentry *dentry);
 extern int	affs_unlink(struct inode *dir, struct dentry *dentry);
 extern int	affs_create(struct inode *dir, struct dentry *dentry, int mode);
@@ -74,17 +52,15 @@
 
 /* inode.c */
 
-extern struct buffer_head	*affs_bread(kdev_t dev, int block, int size);
-extern void			 affs_brelse(struct buffer_head *buf);
 extern unsigned long		 affs_parent_ino(struct inode *dir);
-extern struct inode		*affs_new_inode(const struct inode *dir);
+extern struct inode		*affs_new_inode(struct inode *dir);
 extern int			 affs_notify_change(struct dentry *dentry, struct iattr *attr);
-extern int			 affs_add_entry(struct inode *dir, struct inode *link,
-					  struct inode *inode, struct dentry *dentry, s32 type);
 extern void			 affs_put_inode(struct inode *inode);
 extern void			 affs_delete_inode(struct inode *inode);
+extern void			 affs_clear_inode(struct inode *inode);
 extern void			 affs_read_inode(struct inode *inode);
 extern void			 affs_write_inode(struct inode *inode, int);
+extern int			 affs_add_entry(struct inode *dir, struct inode *inode, struct dentry *dentry, s32 type);
 
 /* super.c */
 
@@ -109,9 +85,9 @@
 extern struct file_operations	 affs_dir_operations;
 extern struct address_space_operations	 affs_symlink_aops;
 extern struct address_space_operations	 affs_aops;
+extern struct address_space_operations	 affs_aops_ofs;
 
 extern struct dentry_operations	 affs_dentry_operations;
 extern struct dentry_operations	 affs_dentry_operations_intl;
 
-extern int affs_bmap(struct inode *, int);
 #endif
diff -urN linux-2.4.4-pre5/include/linux/affs_fs_i.h linux/include/linux/affs_fs_i.h
--- linux-2.4.4-pre5/include/linux/affs_fs_i.h	Mon Mar 26 15:48:11 2001
+++ linux/include/linux/affs_fs_i.h	Mon Dec  3 22:36:26 2001
@@ -2,47 +2,56 @@
 #define _AFFS_FS_I
 
 #include <linux/a.out.h>
-#include <linux/time.h>
 
-#define AFFS_MAX_PREALLOC	16	/* MUST be a power of 2 */
-#define AFFS_KCSIZE		73	/* Allows for 1 extension block at 512 byte-blocks */
+// move this to linux/coda.h!!!
+#include <linux/time.h>
 
-struct key_cache {
-	struct timeval	 kc_lru_time;	/* Last time this cache was used */
-	s32	 kc_first;		/* First cached key */
-	s32	 kc_last;		/* Last cached key */
-	s32	 kc_this_key;		/* Key of extension block this data block keys are from */
-	int	 kc_this_seq;		/* Sequence number of this extension block */
-	s32	 kc_next_key;		/* Key of next extension block */
-	s32	 kc_keys[AFFS_KCSIZE];	/* Key cache */
-};
+#define AFFS_CACHE_SIZE		PAGE_SIZE
+//#define AFFS_CACHE_SIZE		(4*4)
 
-#define EC_SIZE	(PAGE_SIZE - 4 * sizeof(struct key_cache) - 4) / 4
+#define AFFS_MAX_PREALLOC	32
+#define AFFS_LC_SIZE		(AFFS_CACHE_SIZE/sizeof(u32)/2)
+#define AFFS_AC_SIZE		(AFFS_CACHE_SIZE/sizeof(struct affs_ext_key)/2)
+#define AFFS_AC_MASK		(AFFS_AC_SIZE-1)
 
-struct ext_cache {
-	struct key_cache  kc[4];	/* The 4 key caches */
-	s32	 ec[EC_SIZE];		/* Keys of assorted extension blocks */
-	int	 max_ext;		/* Index of last known extension block */
+struct affs_ext_key {
+	u32	ext;				/* idx of the extended block */
+	u32	key;				/* block number */
 };
 
 /*
  * affs fs inode data in memory
  */
 struct affs_inode_info {
+	u32	 i_opencnt;
+	struct semaphore i_link_lock;		/* Protects internal inode access. */
+	struct semaphore i_ext_lock;		/* Protects internal inode access. */
+#define i_hash_lock i_ext_lock
+	u32	 i_blkcnt;			/* block count */
+	u32	 i_extcnt;			/* extended block count */
+	u32	*i_lc;				/* linear cache of extended blocks */
+	u32	 i_lc_size;
+	u32	 i_lc_shift;
+	u32	 i_lc_mask;
+	struct affs_ext_key *i_ac;		/* associative cache of extended blocks */
+	u32	 i_ext_last;			/* last accessed extended block */
+	struct buffer_head *i_ext_bh;		/* bh of last extended block */
 	unsigned long mmu_private;
 	u32	 i_protect;			/* unused attribute bits */
-	s32	 i_parent;			/* parent ino */
+	u32	 i_lastalloc;			/* last allocated block */
+	int	 i_pa_cnt;			/* number of preallocated blocks */
+#if 0
 	s32	 i_original;			/* if != 0, this is the key of the original */
-	s32	 i_data[AFFS_MAX_PREALLOC];	/* preallocated blocks */
-	struct ext_cache *i_ec;			/* Cache gets allocated dynamically */
+	u32	 i_data[AFFS_MAX_PREALLOC];	/* preallocated blocks */
 	int	 i_cache_users;			/* Cache cannot be freed while > 0 */
-	int	 i_lastblock;			/* last allocated block */
-	short	 i_pa_cnt;			/* number of preallocated blocks */
-	short	 i_pa_next;			/* Index of next block in i_data[] */
-	short	 i_pa_last;			/* Index of next free slot in i_data[] */
-	short	 i_zone;			/* write zone */
 	unsigned char i_hlink;			/* This is a fake */
 	unsigned char i_pad;
+	s32	 i_parent;			/* parent ino */
+#endif
 };
+
+/* short cut to get to the affs specific inode data */
+#define AFFS_INODE	(&inode->u.affs_i)
+#define AFFS_DIR	(&dir->u.affs_i)
 
 #endif
diff -urN linux-2.4.4-pre5/include/linux/affs_fs_sb.h linux/include/linux/affs_fs_sb.h
--- linux-2.4.4-pre5/include/linux/affs_fs_sb.h	Mon Feb 23 22:01:26 1998
+++ linux/include/linux/affs_fs_sb.h	Mon Dec  3 22:36:26 2001
@@ -8,51 +8,29 @@
  *
  */
 
-#define MAX_ZONES		8
-#define AFFS_DATA_MIN_FREE	512	/* Number of free blocks in zone for data blocks */
-#define AFFS_HDR_MIN_FREE	128	/* Same for header blocks */
-#define AFFS_ZONE_SIZE		1024	/* Blocks per alloc zone, must be multiple of 32 */
-
 struct affs_bm_info {
-	struct buffer_head *bm_bh;	/* Buffer head if loaded (bm_count > 0) */
-	s32 bm_firstblk;		/* Block number of first bit in this map */
-	s32 bm_key;			/* Disk block number */
-	int bm_count;			/* Usage counter */
-};
-
-struct affs_alloc_zone {
-	short az_size;			/* Size of this allocation zone in double words */
-	short az_count;			/* Number of users */
-	int az_free;			/* Free blocks in here (no. of bits) */
-};
-
-struct affs_zone {
-	unsigned long z_ino;		/* Associated inode number */
-	struct affs_bm_info *z_bm;	/* Zone lies in this bitmap */
-	int z_start;			/* Index of first word in bitmap */
-	int z_end;			/* Index of last word in zone + 1 */
-	int z_az_no;			/* Zone number */
-	unsigned long z_lru_time;	/* Time of last usage */
+	u32 bm_key;			/* Disk block number */
+	u32 bm_free;			/* Free blocks in here */
 };
 
 struct affs_sb_info {
 	int s_partition_size;		/* Partition size in blocks. */
-	int s_blksize;			/* Initial device blksize */
-	s32 s_root_block;		/* FFS root block number. */
+	int s_reserved;			/* Number of reserved blocks. */
+	//u32 s_blksize;			/* Initial device blksize */
+	u32 s_data_blksize;		/* size of the data block w/o header */
+	u32 s_root_block;		/* FFS root block number. */
 	int s_hashsize;			/* Size of hash table. */
 	unsigned long s_flags;		/* See below. */
-	s16 s_uid;			/* uid to override */
-	s16 s_gid;			/* gid to override */
+	uid_t s_uid;			/* uid to override */
+	gid_t s_gid;			/* gid to override */
 	umode_t s_mode;			/* mode to override */
-	int s_reserved;			/* Number of reserved blocks. */
 	struct buffer_head *s_root_bh;	/* Cached root block. */
+	struct semaphore s_bmlock;	/* Protects bitmap access. */
 	struct affs_bm_info *s_bitmap;	/* Bitmap infos. */
-	int s_bm_count;			/* Number of bitmap blocks. */
-	int s_nextzone;			/* Next zone to look for free blocks. */
-	int s_num_az;			/* Total number of alloc zones. */
-	struct affs_zone *s_zones;	/* The zones themselves. */
-	struct affs_alloc_zone *s_alloc;/* The allocation zones. */
-	char *s_zonemap;		/* Bitmap for allocation zones. */
+	u32 s_bmap_count;		/* # of bitmap blocks. */
+	u32 s_bmap_bits;		/* # of bits in one bitmap blocks */
+	u32 s_last_bmap;
+	struct buffer_head *s_bmap_bh;
 	char *s_prefix;			/* Prefix for volumes and assigns. */
 	int s_prefix_len;		/* Length of prefix. */
 	char s_volume[32];		/* Volume prefix for absolute symlinks. */
@@ -70,5 +48,8 @@
 #define SF_PREFIX	0x0400		/* Buffer for prefix is allocated */
 #define SF_VERBOSE	0x0800		/* Talk about fs when mounting */
 #define SF_READONLY	0x1000		/* Don't allow to remount rw */
+
+/* short cut to get to the affs specific sb data */
+#define AFFS_SB		(&sb->u.affs_sb)
 
 #endif
diff -urN linux-2.4.4-pre5/include/linux/amigaffs.h linux/include/linux/amigaffs.h
--- linux-2.4.4-pre5/include/linux/amigaffs.h	Mon Dec 11 13:26:57 2000
+++ linux/include/linux/amigaffs.h	Mon Dec  3 22:36:26 2001
@@ -1,8 +1,10 @@
 #ifndef AMIGAFFS_H
 #define AMIGAFFS_H
 
-#include <asm/byteorder.h>
 #include <linux/types.h>
+#include <linux/locks.h>
+
+#include <asm/byteorder.h>
 
 /* AmigaOS allows file names with up to 30 characters length.
  * Names longer than that will be silently truncated. If you
@@ -16,19 +18,83 @@
 
 #define GET_END_PTR(st,p,sz)		 ((st *)((char *)(p)+((sz)-sizeof(st))))
 #define AFFS_GET_HASHENTRY(data,hashkey) be32_to_cpu(((struct dir_front *)data)->hashtable[hashkey])
-#define AFFS_BLOCK(data,ino,blk)	 ((struct file_front *)data)->blocks[AFFS_I2HSIZE(ino)-1-(blk)]
+#define AFFS_BLOCK(sb, bh, blk)		(AFFS_HEAD(bh)->table[(sb)->u.affs_sb.s_hashsize-1-(blk)])
 
-#define FILE_END(p,i)	GET_END_PTR(struct file_end,p,AFFS_I2BSIZE(i))
-#define ROOT_END(p,i)	GET_END_PTR(struct root_end,p,AFFS_I2BSIZE(i))
-#define DIR_END(p,i)	GET_END_PTR(struct dir_end,p,AFFS_I2BSIZE(i))
-#define LINK_END(p,i)	GET_END_PTR(struct hlink_end,p,AFFS_I2BSIZE(i))
-#define ROOT_END_S(p,s)	GET_END_PTR(struct root_end,p,(s)->s_blocksize)
-#define DATA_FRONT(bh)	((struct data_front *)(bh)->b_data)
-#define DIR_FRONT(bh)	((struct dir_front *)(bh)->b_data)
-
-/* Only for easier debugging if need be */
-#define affs_bread	bread
-#define affs_brelse	brelse
+static inline void
+affs_set_blocksize(struct super_block *sb, int size)
+{
+	set_blocksize(sb->s_dev, size);
+	sb->s_blocksize = size;
+}
+static inline struct buffer_head *
+affs_bread(struct super_block *sb, int block)
+{
+	pr_debug(KERN_DEBUG "affs_bread: %d\n", block);
+	if (block >= AFFS_SB->s_reserved && block < AFFS_SB->s_partition_size)
+		return bread(sb->s_dev, block, sb->s_blocksize);
+	return NULL;
+}
+static inline struct buffer_head *
+affs_getblk(struct super_block *sb, int block)
+{
+	pr_debug(KERN_DEBUG "affs_getblk: %d\n", block);
+	if (block >= AFFS_SB->s_reserved && block < AFFS_SB->s_partition_size)
+		return getblk(sb->s_dev, block, sb->s_blocksize);
+	return NULL;
+}
+static inline struct buffer_head *
+affs_getzeroblk(struct super_block *sb, int block)
+{
+	struct buffer_head *bh;
+	pr_debug(KERN_DEBUG "affs_getzeroblk: %d\n", block);
+	if (block >= AFFS_SB->s_reserved && block < AFFS_SB->s_partition_size) {
+		bh = getblk(sb->s_dev, block, sb->s_blocksize);
+		wait_on_buffer(bh);
+		memset(bh->b_data, 0 , sb->s_blocksize);
+		mark_buffer_uptodate(bh, 1);
+		return bh;
+	}
+	return NULL;
+}
+static inline struct buffer_head *
+affs_getemptyblk(struct super_block *sb, int block)
+{
+	struct buffer_head *bh;
+	pr_debug(KERN_DEBUG "affs_getemptyblk: %d\n", block);
+	if (block >= AFFS_SB->s_reserved && block < AFFS_SB->s_partition_size) {
+		bh = getblk(sb->s_dev, block, sb->s_blocksize);
+		wait_on_buffer(bh);
+		mark_buffer_uptodate(bh, 1);
+		return bh;
+	}
+	return NULL;
+}
+static inline void
+affs_brelse(struct buffer_head *bh)
+{
+	if (bh)
+		pr_debug(KERN_DEBUG "affs_brelse: %ld\n", bh->b_blocknr);
+	brelse(bh);
+}
+
+static inline void
+affs_adjust_checksum(struct buffer_head *bh, u32 val)
+{
+	u32 tmp = be32_to_cpu(((u32 *)bh->b_data)[5]);
+	((u32 *)bh->b_data)[5] = cpu_to_be32(tmp - val);
+}
+static inline void
+affs_adjust_bitmapchecksum(struct buffer_head *bh, u32 val)
+{
+	u32 tmp = be32_to_cpu(((u32 *)bh->b_data)[0]);
+	((u32 *)bh->b_data)[0] = cpu_to_be32(tmp - val);
+}
+
+#define MIN(a, b) ({		\
+	typeof(a) _a = (a);	\
+	typeof(b) _b = (b);	\
+	_a < _b ? _a : _b;	\
+})	
 
 #ifdef __LITTLE_ENDIAN
 #define BO_EXBITS	0x18UL
@@ -63,133 +129,100 @@
 #define ST_SOFTLINK	3
 #define ST_LINKDIR	4
 
-struct root_front
-{
-  s32 primary_type;
-  s32 spare1[2];
-  s32 hash_size;
-  s32 spare2;
-  u32 checksum;
-  s32 hashtable[0];
-};
-
-struct root_end
-{
-  s32 bm_flag;
-  s32 bm_keys[25];
-  s32 bm_extend;
-  struct DateStamp dir_altered;
-  u8 disk_name[40];
-  struct DateStamp disk_altered;
-  struct DateStamp disk_made;
-  s32 spare1[3];
-  s32 secondary_type;
-};
-
-struct dir_front
-{
-  s32 primary_type;
-  s32 own_key;
-  s32 spare1[3];
-  u32 checksum;
-  s32 hashtable[0];
-};
-
-struct dir_end
-{
-  s32 spare1;
-  s16 owner_uid;
-  s16 owner_gid;
-  u32 protect;
-  s32 spare2;
-  u8 comment[92];
-  struct DateStamp created;
-  u8 dir_name[32];
-  s32 spare3[2];
-  s32 link_chain;
-  s32 spare4[5];
-  s32 hash_chain;
-  s32 parent;
-  s32 spare5;
-  s32 secondary_type;
-};
-
-struct file_front
-{
-  s32 primary_type;
-  s32 own_key;
-  s32 block_count;
-  s32 unknown1;
-  s32 first_data;
-  u32 checksum;
-  s32 blocks[0];
-};
-
-struct file_end
-{
-  s32 spare1;
-  s16 owner_uid;
-  s16 owner_gid;
-  u32 protect;
-  s32 byte_size;
-  u8 comment[92];
-  struct DateStamp created;
-  u8 file_name[32];
-  s32 spare2;
-  s32 original;	/* not really in file_end */
-  s32 link_chain;
-  s32 spare3[5];
-  s32 hash_chain;
-  s32 parent;
-  s32 extension;
-  s32 secondary_type;
-};
-
-struct hlink_front
-{
-  s32 primary_type;
-  s32 own_key;
-  s32 spare1[3];
-  u32 checksum;
-};
-
-struct hlink_end
-{
-  s32 spare1;
-  s16 owner_uid;
-  s16 owner_gid;
-  u32 protect;
-  u8 comment[92];
-  struct DateStamp created;
-  u8 link_name[32];
-  s32 spare2;
-  s32 original;
-  s32 link_chain;
-  s32 spare3[5];
-  s32 hash_chain;
-  s32 parent;
-  s32 spare4;
-  s32 secondary_type;
+#define AFFS_ROOT_BMAPS		25
+
+#define AFFS_HEAD(bh)		((struct affs_head *)(bh)->b_data)
+#define AFFS_TAIL(sb, bh)	((struct affs_tail *)((bh)->b_data+(sb)->s_blocksize-sizeof(struct affs_tail)))
+#define AFFS_ROOT_HEAD(bh)	((struct affs_root_head *)(bh)->b_data)
+#define AFFS_ROOT_TAIL(sb, bh)	((struct affs_root_tail *)((bh)->b_data+(sb)->s_blocksize-sizeof(struct affs_root_tail)))
+#define AFFS_DATA_HEAD(bh)	((struct affs_data_head *)(bh)->b_data)
+#define AFFS_DATA(bh)		(((struct affs_data_head *)(bh)->b_data)->data)
+
+struct affs_date {
+	u32 days;
+	u32 mins;
+	u32 ticks;
+};
+
+struct affs_short_date {
+	u16 days;
+	u16 mins;
+	u16 ticks;
+};
+
+struct affs_root_head {
+	u32 ptype;
+	u32 spare1;
+	u32 spare2;
+	u32 hash_size;
+	u32 spare3;
+	u32 checksum;
+	u32 hashtable[1];
+};
+
+struct affs_root_tail {
+	u32 bm_flag;
+	u32 bm_blk[AFFS_ROOT_BMAPS];
+	u32 bm_ext;
+	struct affs_date root_change;
+	u8 disk_name[32];
+	u32 spare1;
+	u32 spare2;
+	struct affs_date disk_change;
+	struct affs_date disk_create;
+	u32 spare3;
+	u32 spare4;
+	u32 dcache;
+	u32 stype;
+};
+
+struct affs_head {
+	u32 ptype;
+	u32 key;
+	u32 block_count;
+	u32 spare1;
+	u32 first_data;
+	u32 checksum;
+	u32 table[1];
+};
+
+struct affs_tail {
+	u32 spare1;
+	u16 uid;
+	u16 gid;
+	u32 protect;
+	u32 size;
+	u8 comment[92];
+	struct affs_date change;
+	u8 name[32];
+	u32 spare2;
+	u32 original;
+	u32 link_chain;
+	u32 spare[5];
+	u32 hash_chain;
+	u32 parent;
+	u32 extension;
+	u32 stype;
 };
 
 struct slink_front
 {
-  s32 primary_type;
-  s32 own_key;
-  s32 spare1[3];
-  s32 checksum;
-  u8	symname[288];	/* depends on block size */
-};
-
-struct data_front
-{
-  s32 primary_type;
-  s32 header_key;
-  s32 sequence_number;
-  s32 data_size;
-  s32 next_data;
-  s32 checksum;
-  u8 data[488];	/* depends on block size */
+	u32 ptype;
+	u32 key;
+	u32 spare1[3];
+	u32 checksum;
+	u8 symname[1];	/* depends on block size */
+};
+
+struct affs_data_head
+{
+	u32 ptype;
+	u32 key;
+	u32 sequence;
+	u32 size;
+	u32 next;
+	u32 checksum;
+	u8 data[1];	/* depends on block size */
 };
 
 /* Permission bits */
@@ -203,26 +236,16 @@
 #define FIBF_GRP_EXECUTE	0x0200
 #define FIBF_GRP_DELETE		0x0100
 
+#define FIBF_HIDDEN		0x0080
 #define FIBF_SCRIPT		0x0040
 #define FIBF_PURE		0x0020		/* no use under linux */
-#define FIBF_ARCHIVE		0x0010		/* never set, always cleared on write */
-#define FIBF_READ		0x0008		/* 0 means allowed */
-#define FIBF_WRITE		0x0004		/* 0 means allowed */
-#define FIBF_EXECUTE		0x0002		/* 0 means allowed, ignored under linux */
-#define FIBF_DELETE		0x0001		/* 0 means allowed */
+#define FIBF_ARCHIVED		0x0010		/* never set, always cleared on write */
+#define FIBF_NOREAD		0x0008		/* 0 means allowed */
+#define FIBF_NOWRITE		0x0004		/* 0 means allowed */
+#define FIBF_NOEXECUTE		0x0002		/* 0 means allowed, ignored under linux */
+#define FIBF_NODELETE		0x0001		/* 0 means allowed */
 
 #define FIBF_OWNER		0x000F		/* Bits pertaining to owner */
-
-#define AFFS_UMAYWRITE(prot)	(((prot) & (FIBF_WRITE|FIBF_DELETE)) == (FIBF_WRITE|FIBF_DELETE))
-#define AFFS_UMAYREAD(prot)	((prot) & FIBF_READ)
-#define AFFS_UMAYEXECUTE(prot)	((prot) & FIBF_EXECUTE)
-#define AFFS_GMAYWRITE(prot)	(((prot)&(FIBF_GRP_WRITE|FIBF_GRP_DELETE))==\
-							(FIBF_GRP_WRITE|FIBF_GRP_DELETE))
-#define AFFS_GMAYREAD(prot)	((prot) & FIBF_GRP_READ)
-#define AFFS_GMAYEXECUTE(prot)	((prot) & FIBF_EXECUTE)
-#define AFFS_OMAYWRITE(prot)	(((prot)&(FIBF_OTR_WRITE|FIBF_OTR_DELETE))==\
-							(FIBF_OTR_WRITE|FIBF_OTR_DELETE))
-#define AFFS_OMAYREAD(prot)	((prot) & FIBF_OTR_READ)
-#define AFFS_OMAYEXECUTE(prot)	((prot) & FIBF_EXECUTE)
+#define FIBF_MASK		0xEE0E		/* Bits modified by Linux */
 
 #endif
diff -urN linux-2.4.4-pre5/include/linux/filter.h linux/include/linux/filter.h
--- linux-2.4.4-pre5/include/linux/filter.h	Thu Apr 27 15:55:09 2000
+++ linux/include/linux/filter.h	Mon Dec  3 22:36:26 2001
@@ -135,6 +135,7 @@
 #ifdef __KERNEL__
 extern int sk_run_filter(struct sk_buff *skb, struct sock_filter *filter, int flen);
 extern int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk);
+extern int sk_chk_filter(struct sock_filter *filter, int flen);
 #endif /* __KERNEL__ */
 
 #endif /* __LINUX_FILTER_H__ */
diff -urN linux-2.4.4-pre5/include/linux/if_ppp.h linux/include/linux/if_ppp.h
--- linux-2.4.4-pre5/include/linux/if_ppp.h	Mon Mar 27 10:28:55 2000
+++ linux/include/linux/if_ppp.h	Mon Dec  3 22:36:26 2001
@@ -1,4 +1,4 @@
-/*	$Id: if_ppp.h,v 1.19 1999/03/31 06:07:57 paulus Exp $	*/
+/*	$Id: if_ppp.h,v 1.21 2000/03/27 06:03:36 paulus Exp $	*/
 
 /*
  * if_ppp.h - Point-to-Point Protocol definitions.
@@ -21,7 +21,7 @@
  */
 
 /*
- *  ==FILEVERSION 20000324==
+ *  ==FILEVERSION 20000724==
  *
  *  NOTE TO MAINTAINERS:
  *     If you modify this file at all, please set the above date.
@@ -130,6 +130,8 @@
 #define PPPIOCSCOMPRESS	_IOW('t', 77, struct ppp_option_data)
 #define PPPIOCGNPMODE	_IOWR('t', 76, struct npioctl) /* get NP mode */
 #define PPPIOCSNPMODE	_IOW('t', 75, struct npioctl)  /* set NP mode */
+#define PPPIOCSPASS	_IOW('t', 71, struct sock_fprog) /* set pass filter */
+#define PPPIOCSACTIVE	_IOW('t', 70, struct sock_fprog) /* set active filt */
 #define PPPIOCGDEBUG	_IOR('t', 65, int)	/* Read debug level */
 #define PPPIOCSDEBUG	_IOW('t', 64, int)	/* Set debug level */
 #define PPPIOCGIDLE	_IOR('t', 63, struct ppp_idle) /* get idle time */
diff -urN linux-2.4.4-pre5/include/linux/isdn.h linux/include/linux/isdn.h
--- linux-2.4.4-pre5/include/linux/isdn.h	Mon Dec  3 22:36:16 2001
+++ linux/include/linux/isdn.h	Mon Dec  3 22:36:26 2001
@@ -1,4 +1,4 @@
-/* $Id: isdn.h,v 1.111.6.1 2001/02/07 11:31:31 kai Exp $
+/* $Id: isdn.h,v 1.111.6.5 2001/04/20 02:40:48 keil Exp $
 
  * Main header for the Linux ISDN subsystem (linklevel).
  *
@@ -25,7 +25,9 @@
 #ifndef isdn_h
 #define isdn_h
 
+#ifdef __KERNEL__
 #include <linux/config.h>
+#endif
 #include <linux/ioctl.h>
 
 #define ISDN_TTY_MAJOR    43
@@ -37,8 +39,14 @@
  * the correspondent code in isdn.c
  */
 
+#ifdef USE_MINIMUM_MEM
+/* Save memory */
+#define ISDN_MAX_DRIVERS    2
+#define ISDN_MAX_CHANNELS   8
+#else
 #define ISDN_MAX_DRIVERS    32
 #define ISDN_MAX_CHANNELS   64
+#endif
 #define ISDN_MINOR_B        0
 #define ISDN_MINOR_BMAX     (ISDN_MAX_CHANNELS-1)
 #define ISDN_MINOR_CTRL     64
@@ -98,6 +106,12 @@
 
 #define IIOCDRVCTL  _IO('I',128)
 
+/* cisco hdlck device private ioctls */
+#define SIOCGKEEPPERIOD	(SIOCDEVPRIVATE + 0)
+#define SIOCSKEEPPERIOD	(SIOCDEVPRIVATE + 1)
+#define SIOCGDEBSERINT	(SIOCDEVPRIVATE + 2)
+#define SIOCSDEBSERINT	(SIOCDEVPRIVATE + 3)
+
 /* Packet encapsulations for net-interfaces */
 #define ISDN_NET_ENCAP_ETHER      0
 #define ISDN_NET_ENCAP_RAWIP      1
@@ -258,9 +272,9 @@
                              ((x & ISDN_USAGE_MASK)==ISDN_USAGE_VOICE)     )
 
 /* Timer-delays and scheduling-flags */
-#define ISDN_TIMER_RES         3                         /* Main Timer-Resolution   */
-#define ISDN_TIMER_02SEC       (HZ/(ISDN_TIMER_RES+1)/5) /* Slow-Timer1 .2 sec      */
-#define ISDN_TIMER_1SEC        (HZ/(ISDN_TIMER_RES+1))   /* Slow-Timer2 1 sec       */
+#define ISDN_TIMER_RES         4                         /* Main Timer-Resolution   */
+#define ISDN_TIMER_02SEC       (HZ/ISDN_TIMER_RES/5)     /* Slow-Timer1 .2 sec      */
+#define ISDN_TIMER_1SEC        (HZ/ISDN_TIMER_RES)       /* Slow-Timer2 1 sec       */
 #define ISDN_TIMER_RINGING     5 /* tty RINGs = ISDN_TIMER_1SEC * this factor       */
 #define ISDN_TIMER_KEEPINT    10 /* Cisco-Keepalive = ISDN_TIMER_1SEC * this factor */
 #define ISDN_TIMER_MODEMREAD   1
@@ -269,13 +283,11 @@
 #define ISDN_TIMER_MODEMXMIT   8
 #define ISDN_TIMER_NETDIAL    16 
 #define ISDN_TIMER_NETHANGUP  32
-#define ISDN_TIMER_KEEPALIVE 128 /* Cisco-Keepalive */
 #define ISDN_TIMER_CARRIER   256 /* Wait for Carrier */
 #define ISDN_TIMER_FAST      (ISDN_TIMER_MODEMREAD | ISDN_TIMER_MODEMPLUS | \
                               ISDN_TIMER_MODEMXMIT)
 #define ISDN_TIMER_SLOW      (ISDN_TIMER_MODEMRING | ISDN_TIMER_NETHANGUP | \
-                              ISDN_TIMER_NETDIAL | ISDN_TIMER_KEEPALIVE | \
-                              ISDN_TIMER_CARRIER)
+                              ISDN_TIMER_NETDIAL | ISDN_TIMER_CARRIER)
 
 /* Timeout-Values for isdn_net_dial() */
 #define ISDN_TIMER_DTIMEOUT10 (10*HZ/(ISDN_TIMER_02SEC*(ISDN_TIMER_RES+1)))
@@ -397,9 +409,15 @@
 #ifdef CONFIG_ISDN_X25
   struct concap_device_ops *dops;      /* callbacks used by encapsulator   */
 #endif
-  int  cisco_loop;                     /* Loop counter for Cisco-SLARP     */
+  /* use an own struct for that in later versions */
   ulong cisco_myseq;                   /* Local keepalive seq. for Cisco   */
+  ulong cisco_mineseen;                /* returned keepalive seq. from remote */
   ulong cisco_yourseq;                 /* Remote keepalive seq. for Cisco  */
+  int cisco_keepalive_period;		/* keepalive period */
+  ulong cisco_last_slarp_in;		/* jiffie of last keepalive packet we received */
+  char cisco_line_state;		/* state of line according to keepalive packets */
+  char cisco_debserint;			/* debugging flag of cisco hdlc with slarp */
+  struct timer_list cisco_timer;
   struct tq_struct tqueue;
 } isdn_net_local;
 
diff -urN linux-2.4.4-pre5/include/linux/isdn_ppp.h linux/include/linux/isdn_ppp.h
--- linux-2.4.4-pre5/include/linux/isdn_ppp.h	Mon Mar 26 15:38:19 2001
+++ linux/include/linux/isdn_ppp.h	Mon Dec  3 22:36:26 2001
@@ -3,7 +3,9 @@
 #ifndef _LINUX_ISDN_PPP_H
 #define _LINUX_ISDN_PPP_H
 
+#ifdef __KERNEL__
 #include <linux/config.h>
+#endif
 
 #define CALLTYPE_INCOMING 0x1
 #define CALLTYPE_OUTGOING 0x2
diff -urN linux-2.4.4-pre5/include/linux/isdnif.h linux/include/linux/isdnif.h
--- linux-2.4.4-pre5/include/linux/isdnif.h	Mon Dec 11 13:20:27 2000
+++ linux/include/linux/isdnif.h	Mon Dec  3 22:36:26 2001
@@ -26,7 +26,9 @@
 #ifndef isdnif_h
 #define isdnif_h
 
+#ifdef __KERNEL__
 #include <linux/config.h>
+#endif
 
 /*
  * Values for general protocol-selection
diff -urN linux-2.4.4-pre5/include/linux/ppp_channel.h linux/include/linux/ppp_channel.h
--- linux-2.4.4-pre5/include/linux/ppp_channel.h	Mon Mar 26 15:48:19 2001
+++ linux/include/linux/ppp_channel.h	Mon Dec  3 22:36:26 2001
@@ -22,7 +22,6 @@
 #include <linux/list.h>
 #include <linux/skbuff.h>
 #include <linux/poll.h>
-#include <asm/atomic.h>
 
 struct ppp_channel;
 
@@ -32,7 +31,6 @@
 	int	(*start_xmit)(struct ppp_channel *, struct sk_buff *);
 	/* Handle an ioctl call that has come in via /dev/ppp. */
 	int	(*ioctl)(struct ppp_channel *, unsigned int, unsigned long);
-	
 };
 
 struct ppp_channel {
@@ -78,16 +76,6 @@
  * in the start_xmit and ioctl routines for the channel by the time
  * that ppp_unregister_channel returns.
  */
-
-/* The following are temporary compatibility stuff */
-ssize_t ppp_channel_read(struct ppp_channel *chan, struct file *file,
-			 char *buf, size_t count);
-ssize_t ppp_channel_write(struct ppp_channel *chan, const char *buf,
-			  size_t count);
-unsigned int ppp_channel_poll(struct ppp_channel *chan, struct file *file,
-			      poll_table *wait);
-int ppp_channel_ioctl(struct ppp_channel *chan, unsigned int cmd,
-		      unsigned long arg);
 
 #endif /* __KERNEL__ */
 #endif
diff -urN linux-2.4.4-pre5/include/linux/proc_fs.h linux/include/linux/proc_fs.h
--- linux-2.4.4-pre5/include/linux/proc_fs.h	Mon Dec  3 22:36:16 2001
+++ linux/include/linux/proc_fs.h	Mon Dec  3 22:36:26 2001
@@ -117,6 +117,7 @@
 /*
  * proc_tty.c
  */
+struct tty_driver;
 extern void proc_tty_init(void);
 extern void proc_tty_register_driver(struct tty_driver *driver);
 extern void proc_tty_unregister_driver(struct tty_driver *driver);
diff -urN linux-2.4.4-pre5/include/linux/rwsem-spinlock.h linux/include/linux/rwsem-spinlock.h
--- linux-2.4.4-pre5/include/linux/rwsem-spinlock.h	Mon Dec  3 22:36:16 2001
+++ linux/include/linux/rwsem-spinlock.h	Mon Dec  3 22:36:26 2001
@@ -14,22 +14,24 @@
 
 #ifdef __KERNEL__
 
+#include <linux/types.h>
+
+struct rwsem_waiter;
+
 /*
  * the semaphore definition
  */
 struct rw_semaphore {
-	signed long			count;
+	signed long		count;
 #define RWSEM_UNLOCKED_VALUE		0x00000000
 #define RWSEM_ACTIVE_BIAS		0x00000001
 #define RWSEM_ACTIVE_MASK		0x0000ffff
 #define RWSEM_WAITING_BIAS		(-0x00010000)
 #define RWSEM_ACTIVE_READ_BIAS		RWSEM_ACTIVE_BIAS
 #define RWSEM_ACTIVE_WRITE_BIAS		(RWSEM_WAITING_BIAS + RWSEM_ACTIVE_BIAS)
-	spinlock_t		lock;
-#define RWSEM_SPINLOCK_OFFSET_STR	"4" /* byte offset of spinlock */
-	wait_queue_head_t	wait;
-#define RWSEM_WAITING_FOR_READ	WQ_FLAG_CONTEXT_0	/* bits to use in wait_queue_t.flags */
-#define RWSEM_WAITING_FOR_WRITE	WQ_FLAG_CONTEXT_1
+	spinlock_t		wait_lock;
+	struct rwsem_waiter	*wait_front;
+	struct rwsem_waiter	**wait_back;
 #if RWSEM_DEBUG
 	int			debug;
 #endif
@@ -55,8 +57,7 @@
 #endif
 
 #define __RWSEM_INITIALIZER(name) \
-{ RWSEM_UNLOCKED_VALUE, SPIN_LOCK_UNLOCKED, \
-	__WAIT_QUEUE_HEAD_INITIALIZER((name).wait) \
+{ RWSEM_UNLOCKED_VALUE, SPIN_LOCK_UNLOCKED, NULL, &(name).wait_front \
 	__RWSEM_DEBUG_INIT __RWSEM_DEBUG_MINIT(name) }
 
 #define DECLARE_RWSEM(name) \
@@ -65,8 +66,9 @@
 static inline void init_rwsem(struct rw_semaphore *sem)
 {
 	sem->count = RWSEM_UNLOCKED_VALUE;
-	spin_lock_init(&sem->lock);
-	init_waitqueue_head(&sem->wait);
+	spin_lock_init(&sem->wait_lock);
+	sem->wait_front = NULL;
+	sem->wait_back = &sem->wait_front;
 #if RWSEM_DEBUG
 	sem->debug = 0;
 #endif
@@ -83,10 +85,10 @@
 static inline void __down_read(struct rw_semaphore *sem)
 {
 	int count;
-	spin_lock(&sem->lock);
+	spin_lock(&sem->wait_lock);
 	sem->count += RWSEM_ACTIVE_READ_BIAS;
 	count = sem->count;
-	spin_unlock(&sem->lock);
+	spin_unlock(&sem->wait_lock);
 	if (count<0)
 		rwsem_down_read_failed(sem);
 }
@@ -97,10 +99,10 @@
 static inline void __down_write(struct rw_semaphore *sem)
 {
 	int count;
-	spin_lock(&sem->lock);
+	spin_lock(&sem->wait_lock);
 	count = sem->count;
 	sem->count += RWSEM_ACTIVE_WRITE_BIAS;
-	spin_unlock(&sem->lock);
+	spin_unlock(&sem->wait_lock);
 	if (count)
 		rwsem_down_write_failed(sem);
 }
@@ -111,10 +113,10 @@
 static inline void __up_read(struct rw_semaphore *sem)
 {
 	int count;
-	spin_lock(&sem->lock);
+	spin_lock(&sem->wait_lock);
 	count = sem->count;
 	sem->count -= RWSEM_ACTIVE_READ_BIAS;
-	spin_unlock(&sem->lock);
+	spin_unlock(&sem->wait_lock);
 	if (count<0 && !((count-RWSEM_ACTIVE_READ_BIAS)&RWSEM_ACTIVE_MASK))
 		rwsem_wake(sem);
 }
@@ -125,41 +127,39 @@
 static inline void __up_write(struct rw_semaphore *sem)
 {
 	int count;
-	spin_lock(&sem->lock);
+	spin_lock(&sem->wait_lock);
 	sem->count -= RWSEM_ACTIVE_WRITE_BIAS;
 	count = sem->count;
-	spin_unlock(&sem->lock);
+	spin_unlock(&sem->wait_lock);
 	if (count<0)
 		rwsem_wake(sem);
 }
 
 /*
  * implement exchange and add functionality
+ * - only called when spinlock is already held
  */
 static inline int rwsem_atomic_update(int delta, struct rw_semaphore *sem)
 {
 	int count;
 
-	spin_lock(&sem->lock);
 	sem->count += delta;
 	count = sem->count;
-	spin_unlock(&sem->lock);
 
 	return count;
 }
 
 /*
  * implement compare and exchange functionality on the rw-semaphore count LSW
+ * - only called by __rwsem_do_wake(), so spinlock is already held when called
  */
 static inline __u16 rwsem_cmpxchgw(struct rw_semaphore *sem, __u16 old, __u16 new)
 {
 	__u16 prev;
 
-	spin_lock(&sem->lock);
 	prev = sem->count & RWSEM_ACTIVE_MASK;
 	if (prev==old)
 		sem->count = (sem->count & ~RWSEM_ACTIVE_MASK) | new;
-	spin_unlock(&sem->lock);
 
 	return prev;
 }
diff -urN linux-2.4.4-pre5/include/linux/rwsem.h linux/include/linux/rwsem.h
--- linux-2.4.4-pre5/include/linux/rwsem.h	Mon Dec  3 22:36:16 2001
+++ linux/include/linux/rwsem.h	Mon Dec  3 22:36:26 2001
@@ -38,9 +38,11 @@
 
 #ifdef __KERNEL__
 
+#include <linux/types.h>
 #include <asm/system.h>
 #include <asm/atomic.h>
-#include <linux/wait.h>
+
+struct rw_semaphore;
 
 /* defined contention handler functions for the generic case
  * - these are also used for the exchange-and-add based algorithm
@@ -60,8 +62,7 @@
 
 #ifndef rwsemtrace
 #if RWSEM_DEBUG
-#include <asm/current.h>
-#define rwsemtrace(SEM,FMT) do { if ((SEM)->debug) printk("[%d] "FMT"(count=%08lx)\n",current->pid,(SEM)->count); } while(0)
+extern void FASTCALL(rwsemtrace(struct rw_semaphore *sem, const char *str));
 #else
 #define rwsemtrace(SEM,FMT)
 #endif
diff -urN linux-2.4.4-pre5/include/linux/sched.h linux/include/linux/sched.h
--- linux-2.4.4-pre5/include/linux/sched.h	Mon Dec  3 22:36:16 2001
+++ linux/include/linux/sched.h	Mon Dec  3 22:36:26 2001
@@ -548,8 +548,6 @@
 
 extern void FASTCALL(__wake_up(wait_queue_head_t *q, unsigned int mode, int nr));
 extern void FASTCALL(__wake_up_sync(wait_queue_head_t *q, unsigned int mode, int nr));
-extern int FASTCALL(__wake_up_ctx(wait_queue_head_t *q, unsigned int mode, int count, int bit));
-extern int FASTCALL(__wake_up_sync_ctx(wait_queue_head_t *q, unsigned int mode, int count, int bit));
 extern void FASTCALL(sleep_on(wait_queue_head_t *q));
 extern long FASTCALL(sleep_on_timeout(wait_queue_head_t *q,
 				      signed long timeout));
@@ -568,8 +566,6 @@
 #define wake_up_interruptible_all(x)	__wake_up((x),TASK_INTERRUPTIBLE, 0)
 #define wake_up_interruptible_sync(x)	__wake_up_sync((x),TASK_INTERRUPTIBLE, 1)
 #define wake_up_interruptible_sync_nr(x) __wake_up_sync((x),TASK_INTERRUPTIBLE,  nr)
-#define wake_up_ctx(x,count,bit)	__wake_up_ctx((x),TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE,count,bit)
-#define wake_up_sync_ctx(x,count,bit)	__wake_up_ctx((x),TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE,count,bit)
 asmlinkage long sys_wait4(pid_t pid,unsigned int * stat_addr, int options, struct rusage * ru);
 
 extern int in_group_p(gid_t);
diff -urN linux-2.4.4-pre5/include/linux/wait.h linux/include/linux/wait.h
--- linux-2.4.4-pre5/include/linux/wait.h	Mon Dec  3 22:36:16 2001
+++ linux/include/linux/wait.h	Mon Mar 26 15:48:11 2001
@@ -26,14 +26,6 @@
 struct __wait_queue {
 	unsigned int flags;
 #define WQ_FLAG_EXCLUSIVE	0x01
-#define WQ_FLAG_CONTEXT_0	8	/* context specific flag bit numbers */
-#define WQ_FLAG_CONTEXT_1	9
-#define WQ_FLAG_CONTEXT_2	10
-#define WQ_FLAG_CONTEXT_3	11
-#define WQ_FLAG_CONTEXT_4	12
-#define WQ_FLAG_CONTEXT_5	13
-#define WQ_FLAG_CONTEXT_6	14
-#define WQ_FLAG_CONTEXT_7	15
 	struct task_struct * task;
 	struct list_head task_list;
 #if WAITQUEUE_DEBUG
diff -urN linux-2.4.4-pre5/kernel/info.c linux/kernel/info.c
--- linux-2.4.4-pre5/kernel/info.c	Tue Sep  5 13:57:57 2000
+++ linux/kernel/info.c	Mon Dec  3 22:36:26 2001
@@ -33,41 +33,46 @@
 	si_swapinfo(&val);
 
 	{
-		/* If the sum of all the available memory (i.e. ram + swap +
-		 * highmem) is less then can be stored in a 32 bit unsigned long
-		 * then we can be binary compatible with 2.2.x kernels.  If not,
-		 * well, who cares since in that case 2.2.x was broken anyways...
+		unsigned long mem_total, sav_total;
+		unsigned int mem_unit, bitcount;
+
+		/* If the sum of all the available memory (i.e. ram + swap)
+		 * is less than can be stored in a 32 bit unsigned long then
+		 * we can be binary compatible with 2.2.x kernels.  If not,
+		 * well, in that case 2.2.x was broken anyways...
 		 *
 		 *  -Erik Andersen <andersee@debian.org> */
 
-		unsigned long mem_total = val.totalram + val.totalswap;
-		if ( !(mem_total < val.totalram || mem_total < val.totalswap)) {
-			unsigned long mem_total2 = mem_total + val.totalhigh; 
-			if (!(mem_total2 < mem_total || mem_total2 < val.totalhigh))
-			{
-				/* If mem_total did not overflow.  Divide all memory values by
-				 * mem_unit and set mem_unit=1.  This leaves things compatible with
-				 * 2.2.x, and also retains compatibility with earlier 2.4.x
-				 * kernels...  */
-
-				int bitcount = 0;
-				while (val.mem_unit > 1) 
-				{
-					bitcount++;
-					val.mem_unit >>= 1;
-				}
-				val.totalram <<= bitcount;
-				val.freeram <<= bitcount;
-				val.sharedram <<= bitcount;
-				val.bufferram <<= bitcount;
-				val.totalswap <<= bitcount;
-				val.freeswap <<= bitcount;
-				val.totalhigh <<= bitcount;
-				val.freehigh <<= bitcount;
-			}
+		mem_total = val.totalram + val.totalswap;
+		if (mem_total < val.totalram || mem_total < val.totalswap)
+			goto out;
+		bitcount = 0;
+		mem_unit = val.mem_unit;
+		while (mem_unit > 1) {
+			bitcount++;
+			mem_unit >>= 1;
+			sav_total = mem_total;
+			mem_total <<= 1;
+			if (mem_total < sav_total)
+				goto out;
 		}
-	}
 
+		/* If mem_total did not overflow, multiply all memory values by
+		 * val.mem_unit and set it to 1.  This leaves things compatible
+		 * with 2.2.x, and also retains compatibility with earlier 2.4.x
+		 * kernels...  */
+
+		val.mem_unit = 1;
+		val.totalram <<= bitcount;
+		val.freeram <<= bitcount;
+		val.sharedram <<= bitcount;
+		val.bufferram <<= bitcount;
+		val.totalswap <<= bitcount;
+		val.freeswap <<= bitcount;
+		val.totalhigh <<= bitcount;
+		val.freehigh <<= bitcount;
+	}
+out:
 	if (copy_to_user(info, &val, sizeof(struct sysinfo)))
 		return -EFAULT;
 	return 0;
diff -urN linux-2.4.4-pre5/kernel/sched.c linux/kernel/sched.c
--- linux-2.4.4-pre5/kernel/sched.c	Mon Dec  3 22:36:16 2001
+++ linux/kernel/sched.c	Mon Dec  3 22:36:26 2001
@@ -765,75 +765,6 @@
 	}
 }
 
-/*
- * wake up processes in the wait queue depending on the state of a context bit in the flags
- * - wakes up a process if the specified bit is set in the flags member
- * - the context bit is cleared if the process is woken up
- * - if the bit number is negative, then the loop stops at the first unset context bit encountered
- * - returns the number of processes woken
- */
-static inline int __wake_up_ctx_common (wait_queue_head_t *q,
-					int count, int bit, const int sync)
-{
-	struct list_head *tmp, *head;
-	struct task_struct *p;
-	int stop, woken;
-
-	woken = 0;
-	stop = bit<0;
-	if (bit<0) bit = -bit;
-
-	CHECK_MAGIC_WQHEAD(q);
-	head = &q->task_list;
-	WQ_CHECK_LIST_HEAD(head);
-	tmp = head->next;
-	while (tmp != head) {
-                wait_queue_t *curr = list_entry(tmp, wait_queue_t, task_list);
-
-		tmp = tmp->next;
-		CHECK_MAGIC(curr->__magic);
-		p = curr->task;
-		if (!test_and_clear_bit(bit,&curr->flags)) {
-			if (stop)
-				break;
-			continue;
-		}
-
-		WQ_NOTE_WAKER(curr);
-		try_to_wake_up(p,sync);
-
-		woken++;
-		if (woken>=count)
-			break;
-	}
-
-	return woken;
-}
-
-int __wake_up_ctx(wait_queue_head_t *q, unsigned int mode, int count, int bit)
-{
-	int woken = 0;
-	if (q && count) {
-		unsigned long flags;
-		wq_read_lock_irqsave(&q->lock, flags);
-		woken = __wake_up_ctx_common(q, count, bit, 0);
-		wq_read_unlock_irqrestore(&q->lock, flags);
-	}
-	return woken;
-}
-
-int __wake_up_ctx_sync(wait_queue_head_t *q, unsigned int mode, int count, int bit)
-{
-	int woken = 0;
-	if (q && count) {
-		unsigned long flags;
-		wq_read_lock_irqsave(&q->lock, flags);
-		woken = __wake_up_ctx_common(q, count, bit, 1);
-		wq_read_unlock_irqrestore(&q->lock, flags);
-	}
-	return woken;
-}
-
 #define	SLEEP_ON_VAR				\
 	unsigned long flags;			\
 	wait_queue_t wait;			\
diff -urN linux-2.4.4-pre5/lib/rwsem.c linux/lib/rwsem.c
--- linux-2.4.4-pre5/lib/rwsem.c	Mon Dec  3 22:36:16 2001
+++ linux/lib/rwsem.c	Mon Dec  3 22:36:26 2001
@@ -7,6 +7,110 @@
 #include <linux/sched.h>
 #include <linux/module.h>
 
+struct rwsem_waiter {
+	struct rwsem_waiter	*next;
+	struct task_struct	*task;
+	unsigned int		flags;
+#define RWSEM_WAITING_FOR_READ	0x00000001
+#define RWSEM_WAITING_FOR_WRITE	0x00000002
+};
+#define RWSEM_WAITER_MAGIC 0x52575345
+
+static struct rw_semaphore *FASTCALL(__rwsem_do_wake(struct rw_semaphore *sem));
+
+#if RWSEM_DEBUG
+void rwsemtrace(struct rw_semaphore *sem, const char *str)
+{
+	if (sem->debug)
+		printk("[%d] %s(count=%08lx)\n",current->pid,str,sem->count);
+}
+#endif
+
+/*
+ * handle the lock being released whilst there are processes blocked on it that can now run
+ * - if we come here, then:
+ *   - the 'active part' of the count (&0x0000ffff) reached zero (but may no longer be zero)
+ *   - the 'waiting part' of the count (&0xffff0000) is negative (and will still be so)
+ *   - the spinlock must be held before entry
+ *   - woken process blocks are discarded from the list after having flags zeroised
+ */
+static struct rw_semaphore *__rwsem_do_wake(struct rw_semaphore *sem)
+{
+	struct rwsem_waiter *waiter, *next;
+	int woken, loop;
+
+	rwsemtrace(sem,"Entering __rwsem_do_wake");
+
+	/* try to grab an 'activity' marker
+	 * - need to make sure two copies of rwsem_wake() don't do this for two separate processes
+	 *   simultaneously
+	 * - be horribly naughty, and only deal with the LSW of the atomic counter
+	 */
+	if (rwsem_cmpxchgw(sem,0,RWSEM_ACTIVE_BIAS)!=0) {
+		rwsemtrace(sem,"__rwsem_do_wake: abort wakeup due to renewed activity");
+		goto out;
+	}
+
+	/* check the wait queue is populated */
+	waiter = sem->wait_front;
+
+	if (__builtin_expect(!waiter,0)) {
+		printk("__rwsem_do_wake(): wait_list unexpectedly empty\n");
+		BUG();
+		goto out;
+	}
+
+	if (__builtin_expect(!waiter->flags,0)) {
+		printk("__rwsem_do_wake(): wait_list front apparently not waiting\n");
+		BUG();
+		goto out;
+	}
+
+	next = NULL;
+
+	/* try to grant a single write lock if there's a writer at the front of the queue
+	 * - note we leave the 'active part' of the count incremented by 1 and the waiting part
+	 *   incremented by 0x00010000
+	 */
+	if (waiter->flags & RWSEM_WAITING_FOR_WRITE) {
+		next = waiter->next;
+		waiter->flags = 0;
+		wake_up_process(waiter->task);
+		goto discard_woken_processes;
+	}
+
+	/* grant an infinite number of read locks to the readers at the front of the queue
+	 * - note we increment the 'active part' of the count by the number of readers (less one
+	 *   for the activity decrement we've already done) before waking any processes up
+	 */
+	woken = 0;
+	do {
+		woken++;
+		waiter = waiter->next;
+	} while (waiter && waiter->flags&RWSEM_WAITING_FOR_READ);
+
+	loop = woken;
+	woken *= RWSEM_ACTIVE_BIAS-RWSEM_WAITING_BIAS;
+	woken -= RWSEM_ACTIVE_BIAS;
+	rwsem_atomic_update(woken,sem);
+
+	waiter = sem->wait_front;
+	for (; loop>0; loop--) {
+		next = waiter->next;
+		waiter->flags = 0;
+		wake_up_process(waiter->task);
+		waiter = next;
+	}
+
+ discard_woken_processes:
+	sem->wait_front = next;
+	if (!next) sem->wait_back = &sem->wait_front;
+
+ out:
+	rwsemtrace(sem,"Leaving __rwsem_do_wake");
+	return sem;
+}
+
 /*
  * wait for the read lock to be granted
  * - need to repeal the increment made inline by the caller
@@ -14,17 +118,23 @@
  */
 struct rw_semaphore *rwsem_down_read_failed(struct rw_semaphore *sem)
 {
+	struct rwsem_waiter waiter;
 	struct task_struct *tsk = current;
-	DECLARE_WAITQUEUE(wait,tsk);
 	signed long count;
 
 	rwsemtrace(sem,"Entering rwsem_down_read_failed");
+	
+	set_task_state(tsk,TASK_UNINTERRUPTIBLE);
 
-	/* this waitqueue context flag will be cleared when we are granted the lock */
-	__set_bit(RWSEM_WAITING_FOR_READ,&wait.flags);
-	set_task_state(tsk, TASK_UNINTERRUPTIBLE);
+	/* set up my own style of waitqueue */
+	waiter.next = NULL;
+	waiter.task = tsk;
+	waiter.flags = RWSEM_WAITING_FOR_READ;
 
-	add_wait_queue_exclusive(&sem->wait, &wait); /* FIFO */
+	spin_lock(&sem->wait_lock);
+
+	*sem->wait_back = &waiter; /* add to back of queue */
+	sem->wait_back = &waiter.next;
 
 	/* note that we're now waiting on the lock, but no longer actively read-locking */
 	count = rwsem_atomic_update(RWSEM_WAITING_BIAS-RWSEM_ACTIVE_BIAS,sem);
@@ -33,17 +143,18 @@
 	 * - it might even be this process, since the waker takes a more active part
 	 */
 	if (!(count & RWSEM_ACTIVE_MASK))
-		rwsem_wake(sem);
+		__rwsem_do_wake(sem);
+
+	spin_unlock(&sem->wait_lock);
 
 	/* wait to be given the lock */
 	for (;;) {
-		if (!test_bit(RWSEM_WAITING_FOR_READ,&wait.flags))
+		if (!waiter.flags)
 			break;
 		schedule();
 		set_task_state(tsk, TASK_UNINTERRUPTIBLE);
 	}
 
-	remove_wait_queue(&sem->wait,&wait);
 	tsk->state = TASK_RUNNING;
 
 	rwsemtrace(sem,"Leaving rwsem_down_read_failed");
@@ -55,17 +166,23 @@
  */
 struct rw_semaphore *rwsem_down_write_failed(struct rw_semaphore *sem)
 {
+	struct rwsem_waiter waiter;
 	struct task_struct *tsk = current;
-	DECLARE_WAITQUEUE(wait,tsk);
 	signed long count;
 
 	rwsemtrace(sem,"Entering rwsem_down_write_failed");
 
-	/* this waitqueue context flag will be cleared when we are granted the lock */
-	__set_bit(RWSEM_WAITING_FOR_WRITE,&wait.flags);
 	set_task_state(tsk, TASK_UNINTERRUPTIBLE);
 
-	add_wait_queue_exclusive(&sem->wait, &wait); /* FIFO */
+	/* set up my own style of waitqueue */
+	waiter.next = NULL;
+	waiter.task = tsk;
+	waiter.flags = RWSEM_WAITING_FOR_WRITE;
+
+	spin_lock(&sem->wait_lock);
+
+	*sem->wait_back = &waiter; /* add to back of queue */
+	sem->wait_back = &waiter.next;
 
 	/* note that we're waiting on the lock, but no longer actively locking */
 	count = rwsem_atomic_update(-RWSEM_ACTIVE_BIAS,sem);
@@ -74,17 +191,18 @@
 	 * - it might even be this process, since the waker takes a more active part
 	 */
 	if (!(count & RWSEM_ACTIVE_MASK))
-		rwsem_wake(sem);
+		__rwsem_do_wake(sem);
+
+	spin_unlock(&sem->wait_lock);
 
 	/* wait to be given the lock */
 	for (;;) {
-		if (!test_bit(RWSEM_WAITING_FOR_WRITE,&wait.flags))
+		if (!waiter.flags)
 			break;
 		schedule();
 		set_task_state(tsk, TASK_UNINTERRUPTIBLE);
 	}
 
-	remove_wait_queue(&sem->wait,&wait);
 	tsk->state = TASK_RUNNING;
 
 	rwsemtrace(sem,"Leaving rwsem_down_write_failed");
@@ -92,61 +210,25 @@
 }
 
 /*
- * handle the lock being released whilst there are processes blocked on it that can now run
- * - if we come here, then:
- *   - the 'active part' of the count (&0x0000ffff) reached zero (but may no longer be zero)
- *   - the 'waiting part' of the count (&0xffff0000) is negative (and will still be so)
+ * spinlock grabbing wrapper for __rwsem_do_wake()
  */
 struct rw_semaphore *rwsem_wake(struct rw_semaphore *sem)
 {
-	signed long count;
-	int woken;
-
 	rwsemtrace(sem,"Entering rwsem_wake");
 
- try_again:
-	/* try to grab an 'activity' marker
-	 * - need to make sure two copies of rwsem_wake() don't do this for two separate processes
-	 *   simultaneously
-	 * - be horribly naughty, and only deal with the LSW of the atomic counter
-	 */
-	if (rwsem_cmpxchgw(sem,0,RWSEM_ACTIVE_BIAS)!=0) {
-		rwsemtrace(sem,"rwsem_wake: abort wakeup due to renewed activity");
-		goto out;
-	}
+	spin_lock(&sem->wait_lock);
 
-	/* try to grant a single write lock if there's a writer at the front of the queue
-	 * - note we leave the 'active part' of the count incremented by 1 and the waiting part
-	 *   incremented by 0x00010000
-	 */
-	if (wake_up_ctx(&sem->wait,1,-RWSEM_WAITING_FOR_WRITE)==1)
-		goto out;
+	sem = __rwsem_do_wake(sem);
 
-	/* grant an infinite number of read locks to the readers at the front of the queue
-	 * - note we increment the 'active part' of the count by the number of readers just woken,
-	 *   less one for the activity decrement we've already done
-	 */
-	woken = wake_up_ctx(&sem->wait,65535,-RWSEM_WAITING_FOR_READ);
-	if (woken<=0)
-		goto counter_correction;
-
-	woken *= RWSEM_ACTIVE_BIAS-RWSEM_WAITING_BIAS;
-	woken -= RWSEM_ACTIVE_BIAS;
-	rwsem_atomic_update(woken,sem);
+	spin_unlock(&sem->wait_lock);
 
- out:
 	rwsemtrace(sem,"Leaving rwsem_wake");
 	return sem;
-
-	/* come here if we need to correct the counter for odd SMP-isms */
- counter_correction:
-	count = rwsem_atomic_update(-RWSEM_ACTIVE_BIAS,sem);
-	rwsemtrace(sem,"corrected count");
-	if (!(count & RWSEM_ACTIVE_MASK))
-		goto try_again;
-	goto out;
 }
 
 EXPORT_SYMBOL(rwsem_down_read_failed);
 EXPORT_SYMBOL(rwsem_down_write_failed);
 EXPORT_SYMBOL(rwsem_wake);
+#if RWSEM_DEBUG
+EXPORT_SYMBOL(rwsemtrace);
+#endif
diff -urN linux-2.4.4-pre5/net/irda/irnet/irnet_ppp.c linux/net/irda/irnet/irnet_ppp.c
--- linux-2.4.4-pre5/net/irda/irnet/irnet_ppp.c	Sat Nov 11 18:11:23 2000
+++ linux/net/irda/irnet/irnet_ppp.c	Mon Dec  3 22:36:26 2001
@@ -476,7 +476,7 @@
 
   /* If we are connected to ppp_generic, let it handle the job */
   if(ap->ppp_open)
-    return ppp_channel_write(&ap->chan, buf, count);
+    return -EAGAIN;
   else
     return irnet_ctrl_write(ap, buf, count);
 }
@@ -500,7 +500,7 @@
 
   /* If we are connected to ppp_generic, let it handle the job */
   if(ap->ppp_open)
-    return ppp_channel_read(&ap->chan, file, buf, count);
+    return -EAGAIN;
   else
     return irnet_ctrl_read(ap, file, buf, count);
 }
@@ -523,9 +523,7 @@
   DABORT(ap == NULL, mask, FS_ERROR, "ap is NULL !!!\n");
 
   /* If we are connected to ppp_generic, let it handle the job */
-  if(ap->ppp_open)
-    mask |= ppp_channel_poll(&ap->chan, file, wait);
-  else
+  if(!ap->ppp_open)
     mask |= irnet_ctrl_poll(ap, file, wait);
 
   DEXIT(FS_TRACE, " - mask=0x%X\n", mask);
@@ -597,15 +595,6 @@
 	  ap->ppp_open = 0;
 	  err = 0;
 	}
-      break;
-
-      /* Attach this PPP instance to the PPP driver (set it active) */
-    case PPPIOCATTACH:
-    case PPPIOCDETACH:
-      if(ap->ppp_open)
-	err = ppp_channel_ioctl(&ap->chan, cmd, arg);
-      else
-	DERROR(FS_ERROR, "Channel not registered !\n");
       break;
 
       /* Query PPP channel and unit number */
diff -urN linux-2.4.4-pre5/scripts/kernel-doc linux/scripts/kernel-doc
--- linux-2.4.4-pre5/scripts/kernel-doc	Fri Dec 29 14:35:47 2000
+++ linux/scripts/kernel-doc	Mon Dec  3 22:36:26 2001
@@ -1,16 +1,27 @@
-#!/usr/bin/perl
+#!/usr/bin/perl -w
+
+use strict;
 
 ## Copyright (c) 1998 Michael Zucchi, All Rights Reserved        ##
-## Copyright (C) 2000  Tim Waugh <twaugh@redhat.com>             ##
+## Copyright (C) 2000, 1  Tim Waugh <twaugh@redhat.com>          ##
+## Copyright (C) 2001  Simon Huggins                             ##
+## 								 ##
+## #define enhancements by Armin Kuster <akuster@mvista.com>	 ##
+## Copyright (c) 2000 MontaVista Software, Inc.			 ##
 ## 								 ##
-## #define enhancements by Armin Kuster <akuster@mvista.com>	 ## 
-## Copyright (c) 2000 MontaVista Software, Inc.			 ##   
-##                                                               ##
 ## This software falls under the GNU General Public License.     ##
 ## Please read the COPYING file for more information             ##
 
 # w.o. 03-11-2000: added the '-filelist' option.
 
+# 18/01/2001 - 	Cleanups
+# 		Functions prototyped as foo(void) same as foo()
+# 		Stop eval'ing where we don't need to.
+# -- huggie@earth.li
+# Still to do:
+# 	- add perldoc documentation
+# 	- Look more closely at some of the scarier bits :)
+
 #
 # This will read a 'c' file and scan for embedded comments in the
 # style of gnome comments (+minor extensions - see below).
@@ -87,53 +98,53 @@
 # '%CONST' - name of a constant.
 
 # match expressions used to find embedded type information
-$type_constant = "\\\%([-_\\w]+)";
-$type_func = "(\\w+)\\(\\)";
-$type_param = "\\\@(\\w+)";
-$type_struct = "\\\&((struct\\s*)?[_\\w]+)";
-$type_env = "(\\\$\\w+)";
+my $type_constant = '\%([-_\w]+)';
+my $type_func = '(\w+)\(\)';
+my $type_param = '\@(\w+)';
+my $type_struct = '\&((struct\s*)?[_\w]+)';
+my $type_env = '(\$\w+)';
 
 
 # Output conversion substitutions.
 #  One for each output format
 
 # these work fairly well
-%highlights_html = ( $type_constant, "<i>\$1</i>",
-		     $type_func, "<b>\$1</b>",
-		     $type_struct, "<i>\$1</i>",
-		     $type_param, "<tt><b>\$1</b></tt>" );
-$blankline_html = "<p>";
+my %highlights_html = ( $type_constant, "<i>\$1</i>",
+			$type_func, "<b>\$1</b>",
+			$type_struct, "<i>\$1</i>",
+			$type_param, "<tt><b>\$1</b></tt>" );
+my $blankline_html = "<p>";
 
 # sgml, docbook format
-%highlights_sgml = ( "([^=])\\\"([^\\\"<]+)\\\"", "\$1<quote>\$2</quote>",
-		     $type_constant, "<constant>\$1</constant>",
-		     $type_func, "<function>\$1</function>",
-		     $type_struct, "<structname>\$1</structname>",
-		     $type_env, "<envar>\$1</envar>",
-		     $type_param, "<parameter>\$1</parameter>" );
-$blankline_sgml = "</para><para>\n";
+my %highlights_sgml = ( "([^=])\\\"([^\\\"<]+)\\\"", "\$1<quote>\$2</quote>",
+			$type_constant, "<constant>\$1</constant>",
+			$type_func, "<function>\$1</function>",
+			$type_struct, "<structname>\$1</structname>",
+			$type_env, "<envar>\$1</envar>",
+			$type_param, "<parameter>\$1</parameter>" );
+my $blankline_sgml = "</para><para>\n";
 
 # gnome, docbook format
-%highlights_gnome = ( $type_constant, "<replaceable class=\"option\">\$1</replaceable>",
-		     $type_func, "<function>\$1</function>",
-		     $type_struct, "<structname>\$1</structname>",
-		     $type_env, "<envar>\$1</envar>",
-		     $type_param, "<parameter>\$1</parameter>" );
-$blankline_gnome = "</para><para>\n";
+my %highlights_gnome = ( $type_constant, "<replaceable class=\"option\">\$1</replaceable>",
+			 $type_func, "<function>\$1</function>",
+			 $type_struct, "<structname>\$1</structname>",
+			 $type_env, "<envar>\$1</envar>",
+			 $type_param, "<parameter>\$1</parameter>" );
+my $blankline_gnome = "</para><para>\n";
 
 # these are pretty rough
-%highlights_man = ( $type_constant, "\$1",
-		    $type_func, "\\\\fB\$1\\\\fP",
-		    $type_struct, "\\\\fI\$1\\\\fP",
-		    $type_param, "\\\\fI\$1\\\\fP" );
-$blankline_man = "";
+my %highlights_man = ( $type_constant, "\$1",
+		       $type_func, "\\\\fB\$1\\\\fP",
+		       $type_struct, "\\\\fI\$1\\\\fP",
+		       $type_param, "\\\\fI\$1\\\\fP" );
+my $blankline_man = "";
 
 # text-mode
-%highlights_text = ( $type_constant, "\$1",
-		     $type_func, "\$1",
-		     $type_struct, "\$1",
-		     $type_param, "\$1" );
-$blankline_text = "";
+my %highlights_text = ( $type_constant, "\$1",
+			$type_func, "\$1",
+			$type_struct, "\$1",
+			$type_param, "\$1" );
+my $blankline_text = "";
 
 
 sub usage {
@@ -149,16 +160,52 @@
     usage();
 }
 
-$verbose = 0;
-$output_mode = "man";
-%highlights = %highlights_man;
-$blankline = $blankline_man;
-$modulename = "API Documentation";
-$function_only = 0;
-$filelist = '';
+my $verbose = 0;
+my $output_mode = "man";
+my %highlights = %highlights_man;
+my $blankline = $blankline_man;
+my $modulename = "API Documentation";
+my $function_only = 0;
+
+# Essentially these are globals
+# They probably want to be tidied up made more localised or summat.
+# CAVEAT EMPTOR!  Some of the others I localised may not want to be which
+# could cause "use of undefined value" or other bugs.
+my ($function, %function_table,%parametertypes,$function_purpose);
+my ($type,$file,$function_name,$return_type);
+my ($newsection,$newcontents,$prototype,$filelist);
+
+my $lineprefix="";
+
+# states
+# 0 - normal code
+# 1 - looking for function name
+# 2 - scanning field start.
+# 3 - scanning prototype.
+my $state = 0;
+my $doc_special = "\@\%\$\&";
+
+my $doc_start = "^/\\*\\*\$";
+my $doc_end = "\\*/";
+my $doc_com = "\\s*\\*\\s*";
+my $doc_func = $doc_com."(\\w+):?";
+my $doc_sect = $doc_com."([".$doc_special."]?[\\w ]+):(.*)";
+my $doc_content = $doc_com."(.*)";
+my $doc_block = $doc_com."DOC:\\s*(.*)?";
+
+my %constants = ();
+my %parameters = ();
+my @parameterlist = ();
+my %sections = ();
+my @sectionlist = ();
+
+my $contents = "";
+my $section_default = "Description";	# default section
+my $section_intro = "Introduction";
+my $section = $section_default;
 
 while ($ARGV[0] =~ m/^-(.*)/) {
-    $cmd = shift @ARGV;
+    my $cmd = shift @ARGV;
     if ($cmd eq "-html") {
 	$output_mode = "html";
 	%highlights = %highlights_html;
@@ -201,8 +248,8 @@
 
 # generate a sequence of code that will splice in highlighting information
 # using the s// operator.
-$dohighlight = "";
-foreach $pattern (keys %highlights) {
+my $dohighlight = "";
+foreach my $pattern (keys %highlights) {
 #    print "scanning pattern $pattern ($highlights{$pattern})\n";
     $dohighlight .=  "\$contents =~ s:$pattern:$highlights{$pattern}:gs;\n";
 }
@@ -211,7 +258,7 @@
 # dumps section contents to arrays/hashes intended for that purpose.
 #
 sub dump_section {
-    my $name = shift @_;
+    my $name = shift;
     my $contents = join "\n", @_;
 
     if ($name =~ m/$type_constant/) {
@@ -240,11 +287,18 @@
 #  sections => %descriont descriptions
 #  
 
-sub output_highlight {
-    my $contents = join "\n", @_;
+sub output_highlight(@) {
+    my $contents = join "\n",@_;
     my $line;
 
+#	DEBUG
+#	if (!defined $contents) {
+#	    use Carp;
+#	    confess "output_highlight got called with no args?\n";
+#    }
+
     eval $dohighlight;
+    die $@ if $@;
     foreach $line (split "\n", $contents) {
 	if ($line eq ""){
 	    print $lineprefix, $blankline;
@@ -258,7 +312,7 @@
 
 
 # output in html
-sub output_html {
+sub output_html(%) {
     my %args = %{$_[0]};
     my ($parameter, $section);
     my $count;
@@ -302,7 +356,7 @@
 
 
 # output in html
-sub output_intro_html {
+sub output_intro_html(%) {
     my %args = %{$_[0]};
     my ($parameter, $section);
     my $count;
@@ -316,10 +370,8 @@
     print "<hr>\n";
 }
 
-
-
 # output in sgml DocBook
-sub output_sgml {
+sub output_sgml(%) {
     my %args = %{$_[0]};
     my ($parameter, $section);
     my $count;
@@ -347,13 +399,6 @@
     print "<function>".$args{'function'}." ";
     print "</function></funcdef>\n";
 
-#    print "<refsect1>\n";
-#    print " <title>Synopsis</title>\n";
-#    print "  <funcsynopsis>\n";
-#    print "   <funcdef>".$args{'functiontype'}." ";
-#    print "<function>".$args{'function'}." ";
-#    print "</function></funcdef>\n";
-
     $count = 0;
     if ($#{$args{'parameterlist'}} >= 0) {
 	foreach $parameter (@{$args{'parameterlist'}}) {
@@ -372,11 +417,9 @@
     }
     print "  </funcsynopsis>\n";
     print "</refsynopsisdiv>\n";
-#    print "</refsect1>\n";
 
     # print parameters
     print "<refsect1>\n <title>Arguments</title>\n";
-#    print "<para>\nArguments\n";
     if ($#{$args{'parameterlist'}} >= 0) {
 	print " <variablelist>\n";
 	foreach $parameter (@{$args{'parameterlist'}}) {
@@ -396,12 +439,10 @@
     $lineprefix="   ";
     foreach $section (@{$args{'sectionlist'}}) {
 	print "<refsect1>\n <title>$section</title>\n <para>\n";
-#	print "<para>\n$section\n";
 	if ($section =~ m/EXAMPLE/i) {
 	    print "<example><para>\n";
 	}
 	output_highlight($args{'sections'}{$section});
-#	print "</para>";
 	if ($section =~ m/EXAMPLE/i) {
 	    print "</para></example>\n";
 	}
@@ -412,25 +453,22 @@
 }
 
 # output in sgml DocBook
-sub output_intro_sgml {
+sub output_intro_sgml(%) {
     my %args = %{$_[0]};
     my ($parameter, $section);
     my $count;
-    my $id;
 
-    $id = $args{'module'};
+    my $id = $args{'module'};
     $id =~ s/[^A-Za-z0-9]/-/g;
 
     # print out each section
     $lineprefix="   ";
     foreach $section (@{$args{'sectionlist'}}) {
 	print "<refsect1>\n <title>$section</title>\n <para>\n";
-#	print "<para>\n$section\n";
 	if ($section =~ m/EXAMPLE/i) {
 	    print "<example><para>\n";
 	}
 	output_highlight($args{'sections'}{$section});
-#	print "</para>";
 	if ($section =~ m/EXAMPLE/i) {
 	    print "</para></example>\n";
 	}
@@ -453,8 +491,6 @@
     print "<sect2>\n";
     print " <title id=\"$id\">".$args{'function'}."</title>\n";
 
-#    print "<simplesect>\n";
-#    print " <title>Synopsis</title>\n";
     print "  <funcsynopsis>\n";
     print "   <funcdef>".$args{'functiontype'}." ";
     print "<function>".$args{'function'}." ";
@@ -477,27 +513,6 @@
 	print "  <void>\n";
     }
     print "  </funcsynopsis>\n";
-#    print "</simplesect>\n";
-#    print "</refsect1>\n";
-
-    # print parameters
-#    print "<simplesect>\n <title>Arguments</title>\n";
-#    if ($#{$args{'parameterlist'}} >= 0) {
-#	print " <variablelist>\n";
-#	foreach $parameter (@{$args{'parameterlist'}}) {
-#	    print "  <varlistentry>\n   <term><parameter>$parameter</parameter></term>\n";
-#	    print "   <listitem>\n    <para>\n";
-#	    $lineprefix="     ";
-#	    output_highlight($args{'parameters'}{$parameter});
-#	    print "    </para>\n   </listitem>\n  </varlistentry>\n";
-#	}
-#	print " </variablelist>\n";
-#    } else {
-#	print " <para>\n  None\n </para>\n";
-#    }
-#    print "</simplesect>\n";
-
-#    print "<simplesect>\n <title>Arguments</title>\n";
     if ($#{$args{'parameterlist'}} >= 0) {
 	print " <informaltable pgwide=\"1\" frame=\"none\" role=\"params\">\n";
 	print "<tgroup cols=\"2\">\n";
@@ -515,20 +530,17 @@
     } else {
 	print " <para>\n  None\n </para>\n";
     }
-#    print "</simplesect>\n";
 
     # print out each section
     $lineprefix="   ";
     foreach $section (@{$args{'sectionlist'}}) {
 	print "<simplesect>\n <title>$section</title>\n";
-#	print "<para>\n$section\n";
 	if ($section =~ m/EXAMPLE/i) {
 	    print "<example><programlisting>\n";
 	} else {
 	}
 	print "<para>\n";
 	output_highlight($args{'sections'}{$section});
-#	print "</para>";
 	print "</para>\n";
 	if ($section =~ m/EXAMPLE/i) {
 	    print "</programlisting></example>\n";
@@ -542,7 +554,7 @@
 
 ##
 # output in man
-sub output_man {
+sub output_man(%) {
     my %args = %{$_[0]};
     my ($parameter, $section);
     my $count;
@@ -555,9 +567,9 @@
     print ".SH SYNOPSIS\n";
     print ".B \"".$args{'functiontype'}."\" ".$args{'function'}."\n";
     $count = 0;
-    $parenth = "(";
-    $post = ",";
-    foreach $parameter (@{$args{'parameterlist'}}) {
+    my $parenth = "(";
+    my $post = ",";
+    foreach my $parameter (@{$args{'parameterlist'}}) {
 	if ($count == $#{$args{'parameterlist'}}) {
 	    $post = ");";
 	}
@@ -566,7 +578,7 @@
 	    # pointer-to-function
 	    print ".BI \"".$parenth.$1."\" ".$parameter." \") (".$2.")".$post."\"\n";
 	} else {
-	    $type =~ s/([^\*])$/\1 /;
+	    $type =~ s/([^\*])$/$1 /;
 	    print ".BI \"".$parenth.$type."\" ".$parameter." \"".$post."\"\n";
 	}
 	$count++;
@@ -584,7 +596,7 @@
     }
 }
 
-sub output_intro_man {
+sub output_intro_man(%) {
     my %args = %{$_[0]};
     my ($parameter, $section);
     my $count;
@@ -599,15 +611,15 @@
 
 ##
 # output in text
-sub output_text {
+sub output_text(%) {
     my %args = %{$_[0]};
     my ($parameter, $section);
 
     print "Function:\n\n";
-    $start=$args{'functiontype'}." ".$args{'function'}." (";
+    my $start=$args{'functiontype'}." ".$args{'function'}." (";
     print $start;
-    $count = 0;
-    foreach $parameter (@{$args{'parameterlist'}}) {
+    my $count = 0;
+    foreach my $parameter (@{$args{'parameterlist'}}) {
 	if ($type =~ m/([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)/) {
 	    # pointer-to-function
 	    print $1.$parameter.") (".$2;
@@ -634,7 +646,7 @@
     print "\n\n";
 }
 
-sub output_intro_text {
+sub output_intro_text(%) {
     my %args = %{$_[0]};
     my ($parameter, $section);
 
@@ -649,24 +661,28 @@
 # generic output function - calls the right one based
 # on current output mode.
 sub output_function {
-#    output_html(@_);
-    eval "output_".$output_mode."(\@_);";
+    no strict 'refs';
+    my $func = "output_".$output_mode;
+    &$func(@_);
 }
 
 ##
 # generic output function - calls the right one based
 # on current output mode.
 sub output_intro {
-#    output_html(@_);
-    eval "output_intro_".$output_mode."(\@_);";
+    no strict 'refs';
+    my $func = "output_intro_".$output_mode;
+    &$func(@_);
 }
 
 
 ##
-# takes a function prototype and spits out all the details
-# stored in the global arrays/hashes.
-sub dump_function {
-    my $prototype = shift @_;
+# takes a function prototype and the name of the current file being
+# processed and spits out all the details stored in the global
+# arrays/hashes.
+sub dump_function($$) {
+    my $prototype = shift;
+    my $file = shift;
 
     $prototype =~ s/^static +//;
     $prototype =~ s/^extern +//;
@@ -706,15 +722,18 @@
 	$prototype =~ m/^(\w+\s+\w+\s+\w+\s*\*)\s*([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/)  {
 	$return_type = $1;
 	$function_name = $2;
-	$args = $3;
+	my $args = $3;
+	my ($param);
 
-	# allow for up to fours args to function pointers
-	$args =~ s/(\([^\),]+),/\1#/g;
-	$args =~ s/(\([^\),]+),/\1#/g;
-	$args =~ s/(\([^\),]+),/\1#/g;
+	# allow for up to six args to function pointers
+	$args =~ s/(\([^\),]+),/$1#/g;
+	$args =~ s/(\([^\),]+),/$1#/g;
+	$args =~ s/(\([^\),]+),/$1#/g;
+	$args =~ s/(\([^\),]+),/$1#/g;
+	$args =~ s/(\([^\),]+),/$1#/g;
 #	print STDERR "ARGS = '$args'\n";
 
-	foreach $arg (split ',', $args) {
+	foreach my $arg (split ',', $args) {
 	    # strip leading/trailing spaces
 	    $arg =~ s/^\s*//;
 	    $arg =~ s/\s*$//;
@@ -725,12 +744,12 @@
 		$arg =~ m/[^\(]+\(\*([^\)]+)\)/;
 		$param = $1;
 		$type = $arg;
-		$type =~ s/([^\(]+\(\*)$param/\1/;
+		$type =~ s/([^\(]+\(\*)$param/$1/;
 	    } else {
 		# evil magic to get fixed array parameters to work
-		$arg =~ s/(.+\s+)(.+)\[.*/\1* \2/;
+		$arg =~ s/(.+\s+)(.+)\[.*/$1* $2/;
 #		print STDERR "SCAN ARG: '$arg'\n";
-		@args = split('\s', $arg);
+		my @args = split('\s', $arg);
 
 #		print STDERR " -> @args\n";
 		$param = pop @args;
@@ -748,15 +767,15 @@
 		$param="...";
 		$parameters{"..."} = "variable arguments";
 	    }
-	    elsif ($type eq "" && $param eq "")
+	    elsif ($type eq "" && ($param eq "" or $param eq "void"))
 	    {
 		$type="";
 		$param="void";
 		$parameters{void} = "no arguments";
 	    }
-            if ($type ne "" && $parameters{$param} eq "") {
+            if (defined $type && $type && !defined $parameters{$param}) {
 	        $parameters{$param} = "-- undescribed --";
-	        print STDERR "Warning($file:$lineno): Function parameter '$param' not described in '$function_name'\n";
+	        print STDERR "Warning($file:$.): Function parameter '$param' not described in '$function_name'\n";
 	    }
 
 	    push @parameterlist, $param;
@@ -764,7 +783,7 @@
 #	    print STDERR "param = '$param', type = '$type'\n";
 	}
     } else {
-	print STDERR "Error($lineno): cannot understand prototype: '$prototype'\n";
+	print STDERR "Error($.): cannot understand prototype: '$prototype'\n";
 	return;
     }
 
@@ -816,7 +835,9 @@
 $section_intro = "Introduction";
 $section = $section_default;
 
-if( $filelist ne '' ) {
+sub process_file($);
+
+if ($filelist) {
 	open(FLIST,"<$filelist") or die "Can't open file list $filelist";
 	while(<FLIST>) {
 		chop;
@@ -824,9 +845,9 @@
 	}
 }
 
-foreach $file (@ARGV) {
-    chomp($file);
-    process_file($file);
+foreach (@ARGV) {
+    chomp;
+    process_file($_);
 }
 
 sub process_file($) {
@@ -837,10 +858,7 @@
 	return;
     }
 
-    $lineno = 0;
     while (<IN>) {
-	$lineno++;
-
 	if ($state == 0) {
 	    if (/$doc_start/o) {
 		$state = 1;		# next line is always the function name
@@ -864,10 +882,10 @@
 		    $function_purpose = "";
 		}
 		if ($verbose) {
-		    print STDERR "Info($lineno): Scanning doc for $function\n";
+		    print STDERR "Info($.): Scanning doc for $function\n";
 		}
 	    } else {
-		print STDERR "WARN($lineno): Cannot understand $_ on line $lineno",
+		print STDERR "WARN($.): Cannot understand $_ on line $.",
 		" - I thought it was a doc line\n";
 		$state = 0;
 	    }
@@ -918,7 +936,7 @@
 		}
 	    } else {
 		# i dont know - bad line?  ignore.
-		print STDERR "WARNING($lineno): bad line: $_"; 
+		print STDERR "WARNING($.): bad line: $_"; 
 	    }
 	} elsif ($state == 3) {	# scanning for function { (end of prototype)
 	    if (m#\s*/\*\s+MACDOC\s*#io) {
@@ -931,7 +949,7 @@
 		$prototype =~ s@/\*.*?\*/@@gos;	# strip comments.
 		$prototype =~ s@[\r\n]+@ @gos; # strip newlines/cr's.
 		$prototype =~ s@^ +@@gos; # strip leading spaces
-		dump_function($prototype);
+		dump_function($prototype,$file);
 
 		$function = "";
 		%constants = ();
