diff -u --recursive --new-file v1.1.2/linux/Configure linux/Configure
--- v1.1.2/linux/Configure	Mon Feb 21 20:38:30 1994
+++ linux/Configure	Wed Apr 13 11:15:06 1994
@@ -11,87 +11,6 @@
 #
 # Each line in the config file is a command.
 #
-#	# internal comment
-#
-#		Lines beginning with a `#' are ignored.
-#
-#	: message
-#
-#		`:' causes the line to be echoed to the screen.
-#
-#	* external comment
-#
-#		`*' causes the line to be placed in the output
-#		configuration file as a comment as well as being
-#		echoed to the screen.
-#
-#	if condition
-#		... commands ...
-#	else
-#		... commands ...
-#	fi
-#
-#		This does the obvious thing.  The `else' clause is
-#		optional.  Conditionals can be nested.
-#
-#		The `condition' can be any valid bash expression.
-#		They typically involve tests against environment
-#		variables set by configuration options.  For example,
-#
-#		if [ "$CONFIG_SCSI" = "y" ]
-#			...More stuff...
-#		fi
-#
-#		Note!  That there is no `then' keyword.
-#
-#	bool 'prompt' CONFIG_VARIABLE default
-#
-#		This prompts the user for a boolean value.
-#		The prompt may not contain an apostrophe.
-#		`default' should be either `y' or `n'.
-#		The user's response is recorded in four places.
-#
-#		In .config, if `y'
-#			CONFIG_VARIABLE = CONFIG_VARIABLE
-#		In .config, if `n'
-#			# CONFIG_VARIABLE is not set
-#			
-#		In autoconf.h, if `y'
-#			#define CONFIG_VARIABLE 1
-#		In autoconf.h, if `n'
-#			#undef CONFIG_VARIABLE
-#
-#		In config.in, if `y'
-#			bool 'prompt' CONFIG_VARIABLE y
-#		In config.in, if `n'
-#			bool 'prompt' CONFIG_VARIABLE n
-#
-#		In the environment of the Configure script, if `y'
-#			CONFIG_VARIABLE = y
-#		In the environment of the Configure script, if `n'
-#			CONFIG_VARIABLE = n
-#
-#		The value is placed into the environment of the Configure
-#		script so that later parts of config.in can use the `if'
-#		command to inspect the results of previous queries.
-#
-#	int 'prompt' CONFIG_VARIABLE default
-#
-#		This prompts the user for an integer value.
-#		The prompt may not contain an apostrophe.
-#		`default' should be an integer.
-#
-#		The response is recorded as follows.
-#
-#		In .config
-#			CONFIG_VARIABLE = response
-#		In autoconf.h
-#			#define CONFIG_VARIABLE (response)
-#		In config.in
-#			int 'prompt' CONFIG_VARIABLE response
-#		In the environment of the Configure script
-#			CONFIG_VARIABLE = response
-#
 # 050793 - use IFS='@' to get around a bug in a pre-version of bash-1.13
 # with an empty IFS.
 
@@ -114,58 +33,85 @@
 #	readln prompt default
 #
 function readln () {
-	echo -n "$1"
-	IFS='@' read ans </dev/tty || exit 1
-	[ -z "$ans" ] && ans=$2
+	if [ "$DEFAULT" = "-d" ]; then
+		echo "$1"
+		ans=$2
+	else
+		echo -n "$1"
+		IFS='@' read ans </dev/tty || exit 1
+		[ -z "$ans" ] && ans=$2
+	fi
+}
+
+#
+# change updates the "config.new" file according to the answer
+#
+#	change define old new
+#
+function change () {
+	if [ "$2" != "$3" ]; then
+		sed "s/$1 $2$/$1 $3/" < $CONFIG_NEW > .tmpc
+		mv .tmpc $CONFIG_NEW
+	fi
+}
+
+#
+# comment does some pretty-printing
+#
+#	comment 'xxx'
+# 
+function comment () {
+	echo "*"; echo "* $1" ; echo "*"
+	(echo "" ; echo "#"; echo "# $1" ; echo "#") >>$CONFIG
+	(echo "" ; echo "/*"; echo " * $1" ; echo " */") >>$CONFIG_H
 }
 
+#
 # bool processes a boolean argument
 #
-#	bool tail
+#	bool question define default
 #
 function bool () {
-	# Slimier hack to get bash to rescan a line.
-	eval "set -- $1"
 	ans=""
 	while [ "$ans" != "y" -a "$ans" != "n" ]; do
 		readln "$1 ($2) [$3] " "$3"
 	done
 	if [ "$ans" = "y" ]; then
-		echo "$2 = $2" >>$CONFIG
+		echo "  $2 = $2" >>$CONFIG
 		echo "#define $2 1" >>$CONFIG_H
 	else
 		echo "# $2 is not set" >>$CONFIG
-		echo "#undef $2" >>$CONFIG_H
+		echo "#undef  $2" >>$CONFIG_H
 	fi
-	raw_input_line="bool '$1' $2 $ans"
+	change $2 $3 $ans
 	eval "$2=$ans"
 }
 
+#
 # int processes an integer argument
 #
-#	int tail
+#	int question define default
 #
 function int () {
 	# Slimier hack to get bash to rescan a line.
-	eval "set -- $1"
 	ans="x"
 	while [ $[$ans+0] != "$ans" ]; do
 		readln "$1 ($2) [$3] " "$3"
 	done
-	echo "$2 = $ans" >>$CONFIG
+	echo "  $2 = $ans" >>$CONFIG
 	echo "#define $2 ($ans)" >>$CONFIG_H
-	raw_input_line="int '$1' $2 $ans"
 	eval "$2=$ans"
 }
 
 CONFIG=.tmpconfig
-CONFIG_H=include/linux/autoconf.h
-trap "rm -f $CONFIG $CONFIG_H config.new ; exit 1" 1 2
+CONFIG_H=.tmpconfig.h
+CONFIG_NEW=config.new
+trap "rm -f $CONFIG $CONFIG_H $CONFIG_NEW ; exit 1" 1 2
 
 #
 # Make sure we start out with a clean slate.
 #
-> config.new
+cp config.in $CONFIG_NEW
 echo "#" > $CONFIG
 echo "# Automatically generated make config: don't edit" >> $CONFIG
 echo "#" >> $CONFIG
@@ -174,62 +120,16 @@
 echo " * Automatically generated C config: don't edit" >> $CONFIG_H
 echo " */" >> $CONFIG_H
 
-stack=''
-branch='t'
+DEFAULT=$1
 
-while IFS='@' read raw_input_line
-do
-	# Slimy hack to get bash to rescan a line.
-	read cmd rest <<-END_OF_COMMAND
-		$raw_input_line
-	END_OF_COMMAND
-
-	if [ "$cmd" = "*" ]; then
-		if [ "$branch" = "t" ]; then
-			echo "$raw_input_line"
-			echo "# $rest" >>$CONFIG
-			if [ "$prevcmd" != "*" ]; then
-				echo >>$CONFIG_H
-				echo "/* $rest" >>$CONFIG_H
-			else
-				echo " * $rest" >>$CONFIG_H
-			fi
-			prevcmd="*"
-		fi
-	else
-		[ "$prevcmd" = "*" ] && echo " */" >>$CONFIG_H
-		prevcmd=""
-		case "$cmd" in
-		:)	[ "$branch" = "t" ] && echo "$raw_input_line" ;;
-		int)	[ "$branch" = "t" ] && int "$rest" ;;
-		bool)	[ "$branch" = "t" ] && bool "$rest" ;;
-		exec)	[ "$branch" = "t" ] && ( sh -c "$rest" ) ;;
-		if)	stack="$branch $stack"
-			if [ "$branch" = "t" ] && eval "$rest"; then
-				branch=t
-			else
-				branch=f
-			fi ;;
-		else)	if [ "$branch" = "t" ]; then
-				branch=f
-			else
-				read branch rest <<-END_OF_STACK
-					$stack
-				END_OF_STACK
-			fi ;;
-		fi)	[ -z "$stack" ] && echo "Error!  Extra fi." 1>&2
-			read branch stack <<-END_OF_STACK
-				$stack
-			END_OF_STACK
-			;;
-		esac
-	fi
-	echo "$raw_input_line" >>config.new
-done
-[ "$prevcmd" = "*" ] && echo " */" >>$CONFIG_H
+. ./config.in
 
-[ -z "$stack" ] || echo "Error!  Untermiated if." 1>&2
+if [ "$CONFIG_SOUND" = "y" ] ; then
+	$MAKE -C drivers/sound config || exit 1
+fi
 
+mv .tmpconfig .config
+mv .tmpconfig.h include/linux/autoconf.h
 mv config.in config.old
 mv config.new config.in
 
diff -u --recursive --new-file v1.1.2/linux/Makefile linux/Makefile
--- v1.1.2/linux/Makefile	Wed Apr 13 11:33:50 1994
+++ linux/Makefile	Wed Apr 13 11:33:02 1994
@@ -1,6 +1,6 @@
 VERSION = 1
 PATCHLEVEL = 1
-SUBLEVEL = 2
+SUBLEVEL = 3
 
 all:	Version zImage
 
@@ -116,12 +116,11 @@
 Version: dummy
 	rm -f tools/version.h
 
+oldconfig:
+	$(CONFIG_SHELL) Configure -d $(OPTS)
+
 config:
-	$(CONFIG_SHELL) Configure $(OPTS) < config.in
-	@if grep -s '^CONFIG_SOUND' .tmpconfig ; then \
-		$(MAKE) -C drivers/sound config; \
-		else : ; fi
-	mv .tmpconfig .config
+	$(CONFIG_SHELL) Configure $(OPTS)
 
 linuxsubdirs: dummy
 	set -e; for i in $(SUBDIRS); do $(MAKE) -C $$i; done
diff -u --recursive --new-file v1.1.2/linux/config.in linux/config.in
--- v1.1.2/linux/config.in	Wed Apr  6 08:57:38 1994
+++ linux/config.in	Wed Apr 13 11:28:12 1994
@@ -2,9 +2,9 @@
 # For a description of the syntax of this configuration file,
 # see the Configure script.
 #
-*
-* General setup
-*
+
+comment 'General setup'
+
 bool 'Kernel math emulation' CONFIG_MATH_EMULATION y
 bool 'Normal harddisk support' CONFIG_BLK_DEV_HD y
 bool 'XT harddisk support' CONFIG_BLK_DEV_XD n
@@ -12,59 +12,60 @@
 bool 'Limit memory to low 16MB' CONFIG_MAX_16M n
 bool 'System V IPC' CONFIG_SYSVIPC y
 bool 'Use -m486 flag for 486-specific optimizations' CONFIG_M486 y
-*
-* Program binary formats
-*
+
+comment 'Program binary formats'
+
 bool 'Elf executables' CONFIG_BINFMT_ELF y
 bool 'COFF executables' CONFIG_BINFMT_COFF y
-*
-* SCSI support
-*
+
+comment 'SCSI support'
+
 bool 'SCSI support?' CONFIG_SCSI n
-if [ "$CONFIG_SCSI" = "n" ]
-:
-: Skipping SCSI configuration options...
-:
+
+if [ "$CONFIG_SCSI" = "n" ]; then
+
+comment 'Skipping SCSI configuration options...'
+
 else
-	*
-	* SCSI support type (disk, tape, CDrom)
-	*
+
+comment 'SCSI support type (disk, tape, CDrom)'
+
 bool 'Scsi disk support' CONFIG_BLK_DEV_SD y
-bool 'Scsi tape support' CONFIG_CHR_DEV_ST y
-bool 'Scsi CDROM support' CONFIG_BLK_DEV_SR y
-bool 'Scsi generic support' CONFIG_CHR_DEV_SG y
-	*
-	* SCSI low-level drivers
-	*
-bool 'Adaptec AHA152X support' CONFIG_SCSI_AHA152X y
+bool 'Scsi tape support' CONFIG_CHR_DEV_ST n
+bool 'Scsi CDROM support' CONFIG_BLK_DEV_SR n
+bool 'Scsi generic support' CONFIG_CHR_DEV_SG n
+
+comment 'SCSI low-level drivers'
+
+bool 'Adaptec AHA152X support' CONFIG_SCSI_AHA152X n
 bool 'Adaptec AHA1542 support' CONFIG_SCSI_AHA1542 y
-bool 'Adaptec AHA1740 support' CONFIG_SCSI_AHA1740 y
-bool 'Future Domain 16xx SCSI support' CONFIG_SCSI_FUTURE_DOMAIN y
-bool 'Generic NCR5380 SCSI support' CONFIG_SCSI_GENERIC_NCR5380 y
-bool 'PAS16 SCSI support' CONFIG_SCSI_PAS16 y
-bool 'Seagate ST-02 and Future Domain TMC-8xx SCSI support' CONFIG_SCSI_SEAGATE y
-bool 'Trantor T128/T128F/T228 SCSI support' CONFIG_SCSI_T128 y
-bool 'UltraStor SCSI support' CONFIG_SCSI_ULTRASTOR y
-bool '7000FASST SCSI support' CONFIG_SCSI_7000FASST y
-fi
-*
-* Network device support
-*
+bool 'Adaptec AHA1740 support' CONFIG_SCSI_AHA1740 n
+bool 'Future Domain 16xx SCSI support' CONFIG_SCSI_FUTURE_DOMAIN n
+bool 'Generic NCR5380 SCSI support' CONFIG_SCSI_GENERIC_NCR5380 n
+bool 'PAS16 SCSI support' CONFIG_SCSI_PAS16 n
+bool 'Seagate ST-02 and Future Domain TMC-8xx SCSI support' CONFIG_SCSI_SEAGATE n
+bool 'Trantor T128/T128F/T228 SCSI support' CONFIG_SCSI_T128 n
+bool 'UltraStor SCSI support' CONFIG_SCSI_ULTRASTOR n
+bool '7000FASST SCSI support' CONFIG_SCSI_7000FASST n
+fi
+
+comment 'Network device support'
+
 bool 'Network device support?' CONFIG_ETHERCARDS y
-if [ "$CONFIG_ETHERCARDS" = "n" ]
-:
-: Skipping ethercard configuration options...
-:
+if [ "$CONFIG_ETHERCARDS" = "n" ]; then
+
+comment 'Skipping ethercard configuration options...'
+
 else
 bool 'SLIP (serial line) support' CONFIG_SLIP n
-if [ "$CONFIG_SLIP" = "y" ]
+if [ "$CONFIG_SLIP" = "y" ]; then
   bool ' CSLIP compressed headers' SL_COMPRESSED y
 #  bool ' SLIP debugging on' SL_DUMP y
 fi
 #bool 'PPP (point-to-point) support' CONFIG_PPP n
 bool 'PLIP (parallel port) support' CONFIG_PLIP n
 bool 'NE2000/NE1000 support' CONFIG_NE2000 n
-bool 'WD80*3 support' CONFIG_WD80x3 y
+bool 'WD80E3 support' CONFIG_WD80x3 y
 bool 'SMC Ultra support' CONFIG_ULTRA n
 bool '3c501 support' CONFIG_EL1 n
 bool '3c503 support' CONFIG_EL2 n
@@ -77,20 +78,22 @@
 #bool 'Zenith Z-Note support' CONFIG_ZNET n
 #bool 'EtherExpress support' CONFIG_EEXPRESS n
 bool 'DEPCA support' CONFIG_DEPCA n
-#bool 'NI52** support' CONFIG_NI52 n
-#bool 'NI65** support' CONFIG_NI65 n
+#bool 'NI52EE support' CONFIG_NI52 n
+#bool 'NI65EE support' CONFIG_NI65 n
 #bool 'Ansel Communications EISA 3200 support' CONFIG_AC3200 n
 #bool 'Cabletron E21xx support (not recommended)' CONFIG_E21 n
 bool 'D-Link DE600 pocket adaptor support' CONFIG_DE600 n
 bool 'AT-LAN-TEC/RealTek pocket adaptor support' CONFIG_ATP n
 fi
-*
+
+comment 'CD-ROM drivers'
+
 bool 'Sony CDU31A CDROM driver support' CONFIG_CDU31A n
 bool 'Mitsumi CDROM driver support' CONFIG_MCD n
 bool 'Matsushita/Panasonic CDROM driver support' CONFIG_SBPCD n
-*
-* Filesystems
-*
+
+comment 'Filesystems'
+
 bool 'Standard (minix) fs support' CONFIG_MINIX_FS y
 bool 'Extended fs support' CONFIG_EXT_FS n
 bool 'Second extended fs support' CONFIG_EXT2_FS y
@@ -101,13 +104,13 @@
 bool 'ISO9660 cdrom filesystem support' CONFIG_ISO9660_FS n
 bool 'OS/2 HPFS filesystem support (read only)' CONFIG_HPFS_FS n
 bool 'System V and Coherent filesystem support' CONFIG_SYSV_FS n
-*
-*  character devices
-*
+
+comment 'character devices'
+
 bool 'Parallel printer support' CONFIG_PRINTER n
 bool 'Logitech busmouse support' CONFIG_BUSMOUSE n
 bool 'PS/2 mouse (aka "auxiliary device") support' CONFIG_PSMOUSE y
-if [ "$CONFIG_PSMOUSE" = "y" ]
+if [ "$CONFIG_PSMOUSE" = "y" ]; then
 bool 'C&T 82C710 mouse port support (as on TI Travelmate)' CONFIG_82C710_MOUSE y
 fi
 bool 'Microsoft busmouse support' CONFIG_MS_BUSMOUSE n
@@ -115,23 +118,18 @@
 bool 'Selection (cut and paste for virtual consoles)' CONFIG_SELECTION n
 bool 'QIC-02 tape support' CONFIG_TAPE_QIC02 n
 bool 'QIC-117 tape support' CONFIG_FTAPE n
-if [ "$CONFIG_FTAPE" = "y" ]
+if [ "$CONFIG_FTAPE" = "y" ]; then
 int ' number of ftape buffers' NR_FTAPE_BUFFERS 3
 fi
-*
-* Sound
-*
+
+comment 'Sound'
+
 bool 'Sound card support' CONFIG_SOUND n
-*
-* Kernel hacking
-*
+
+comment 'Kernel hacking'
+
 #bool 'Debug kmalloc/kfree' CONFIG_DEBUG_MALLOC n
 bool 'Kernel profiling support' CONFIG_PROFILE n
-if [ "$CONFIG_SCSI" = "y" ]
+if [ "$CONFIG_SCSI" = "y" ]; then
 bool 'Verbose scsi error reporting (kernel size +=12K)' CONFIG_SCSI_CONSTANTS y
-fi
-if [ "$CONFIG_SOUND" = "y" ]
-  exec touch .makesound
-else
-  exec rm -f .makesound
 fi
diff -u --recursive --new-file v1.1.2/linux/drivers/block/ll_rw_blk.c linux/drivers/block/ll_rw_blk.c
--- v1.1.2/linux/drivers/block/ll_rw_blk.c	Tue Feb  1 09:03:51 1994
+++ linux/drivers/block/ll_rw_blk.c	Wed Apr 13 11:35:38 1994
@@ -156,7 +156,7 @@
 	req->next = NULL;
 	cli();
 	if (req->bh)
-		req->bh->b_dirt = 0;
+		mark_buffer_clean(req->bh);
 	if (!(tmp = dev->current_request)) {
 		dev->current_request = req;
 		(dev->request_fn)();
@@ -240,12 +240,12 @@
 			    !req->waiting &&
 			    req->cmd == rw &&
 			    req->sector + req->nr_sectors == sector &&
-			    req->nr_sectors < 254)
+			    req->nr_sectors < 244)
 			{
 				req->bhtail->b_reqnext = bh;
 				req->bhtail = bh;
 				req->nr_sectors += count;
-				bh->b_dirt = 0;
+				mark_buffer_clean(bh);
 				sti();
 				return;
 			}
@@ -254,14 +254,14 @@
 			    !req->waiting &&
 			    req->cmd == rw &&
 			    req->sector - count == sector &&
-			    req->nr_sectors < 254)
+			    req->nr_sectors < 244)
 			{
 			    	req->nr_sectors += count;
 			    	bh->b_reqnext = req->bh;
 			    	req->buffer = bh->b_data;
 			    	req->current_nr_sectors = count;
 			    	req->sector = sector;
-			    	bh->b_dirt = 0;
+				mark_buffer_clean(bh);
 			    	req->bh = bh;
 			    	sti();
 			    	return;
diff -u --recursive --new-file v1.1.2/linux/drivers/scsi/fdomain.c linux/drivers/scsi/fdomain.c
--- v1.1.2/linux/drivers/scsi/fdomain.c	Thu Apr  7 11:57:31 1994
+++ linux/drivers/scsi/fdomain.c	Tue Apr 12 06:33:14 1994
@@ -1,10 +1,10 @@
 /* fdomain.c -- Future Domain TMC-16x0 SCSI driver
  * Created: Sun May  3 18:53:19 1992 by faith@cs.unc.edu
- * Revised: Fri Apr  1 23:47:55 1994 by faith@cs.unc.edu
+ * Revised: Thu Apr  7 20:30:09 1994 by faith@cs.unc.edu
  * Author: Rickard E. Faith, faith@cs.unc.edu
  * Copyright 1992, 1993, 1994 Rickard E. Faith
  *
- * $Id: fdomain.c,v 5.15 1994/04/02 04:48:04 root Exp $
+ * $Id: fdomain.c,v 5.16 1994/04/08 00:30:15 root Exp $
 
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
@@ -168,7 +168,7 @@
 #include <linux/string.h>
 #include <linux/ioport.h>
 
-#define VERSION          "$Revision: 5.15 $"
+#define VERSION          "$Revision: 5.16 $"
 
 /* START OF USER DEFINABLE OPTIONS */
 
@@ -282,6 +282,9 @@
 static int               Write_FIFO_port;
 static int               Write_SCSI_Data_port;
 
+static int               FIFO_Size = 0x2000; /* 8k FIFO for
+						pre-tmc18c30 chips */
+
 extern void              fdomain_16x0_intr( int unused );
 
 static void *addresses[] = {
@@ -421,7 +424,10 @@
       outb( 0x80, port + IO_Control );
       if (inb( port + Configuration2 ) & 0x80 == 0x80) {
 	 outb( 0x00, port + IO_Control );
-	 if (inb( port + Configuration2 ) & 0x80 == 0x00) chip = tmc18c30;
+	 if (inb( port + Configuration2 ) & 0x80 == 0x00) {
+	    chip = tmc18c30;
+	    FIFO_Size = 0x800;	/* 2k FIFO */
+	 }
       }
 #else
 
@@ -429,7 +435,10 @@
                                    have problems.  Lets assume it is an
                                    18c30 if the RAM is disabled. */
 
-      if (inb( port + Configuration2 ) & 0x02) chip = tmc18c30;
+      if (inb( port + Configuration2 ) & 0x02) {
+	 chip      = tmc18c30;
+	 FIFO_Size = 0x800;	/* 2k FIFO */
+      }
 #endif
 				/* If that failed, we are an 18c50. */
    }
@@ -1127,7 +1136,7 @@
    }
 
    if (current_SC->SCp.have_data_in == -1) { /* DATA OUT */
-      while ( (data_count = 0x2000 - inw( FIFO_Data_Count_port )) > 512 ) {
+      while ( (data_count = FIFO_Size - inw( FIFO_Data_Count_port )) > 512 ) {
 #if EVERY_ACCESS
 	 printk( "DC=%d, ", data_count ) ;
 #endif
diff -u --recursive --new-file v1.1.2/linux/fs/block_dev.c linux/fs/block_dev.c
--- v1.1.2/linux/fs/block_dev.c	Wed Apr 13 11:33:53 1994
+++ linux/fs/block_dev.c	Wed Apr 13 11:26:14 1994
@@ -14,13 +14,18 @@
 extern int *blk_size[];
 extern int *blksize_size[];
 
+#define NBUF 64
+
 int block_write(struct inode * inode, struct file * filp, char * buf, int count)
 {
-	int blocksize, blocksize_bits, i;
-	int block;
+	int blocksize, blocksize_bits, i, j;
+	int block, blocks;
 	int offset;
 	int chars;
 	int written = 0;
+	int cluster_list[4];
+	struct buffer_head * bhlist[NBUF];
+	int blocks_per_cluster;
 	unsigned int size;
 	unsigned int dev;
 	struct buffer_head * bh;
@@ -38,6 +43,8 @@
 		i >>= 1;
 	}
 
+	blocks_per_cluster = PAGE_SIZE / blocksize;
+
 	block = filp->f_pos >> blocksize_bits;
 	offset = filp->f_pos & (blocksize-1);
 
@@ -51,11 +58,50 @@
 		chars = blocksize - offset;
 		if (chars > count)
 			chars=count;
+
+#if 0
 		if (chars == blocksize)
 			bh = getblk(dev, block, blocksize);
 		else
-			bh = breada(dev, block, blocksize, offset, 
-				    size << blocksize_bits);
+			bh = breada(dev,block,block+1,block+2,-1);
+
+#else
+		for(i=0; i<blocks_per_cluster; i++) cluster_list[i] = block+i;
+		if((block % blocks_per_cluster) == 0)
+		  generate_cluster(dev, cluster_list, blocksize);
+		bh = getblk(dev, block, blocksize);
+
+		if (chars != blocksize && !bh->b_uptodate) {
+		  if(!filp->f_reada ||
+		     !read_ahead[MAJOR(dev)]) {
+		    /* We do this to force the read of a single buffer */
+		    brelse(bh);
+		    bh = bread(dev,block,blocksize);
+		  } else {
+		    /* Read-ahead before write */
+		    blocks = read_ahead[MAJOR(dev)] / (blocksize >> 9) / 2;
+		    if (block + blocks > size) blocks = size - block;
+		    if (blocks > NBUF) blocks=NBUF;
+		    blocks -= (block % blocks_per_cluster);
+		    if(!blocks) blocks = 1;
+		    bhlist[0] = bh;
+		    for(i=1; i<blocks; i++){
+		      if(((i+block) % blocks_per_cluster) == 0) {
+			for(j=0; j<blocks_per_cluster; j++) cluster_list[j] = block+i+j;
+			generate_cluster(dev, cluster_list, blocksize);
+		      };
+		      bhlist[i] = getblk (dev, block+i, blocksize);
+		      if(!bhlist[i]){
+			while(i >= 0) brelse(bhlist[i--]);
+			return written? written: -EIO;
+		      };
+		    };
+		    ll_rw_block(READ, blocks, bhlist);
+		    for(i=1; i<blocks; i++) brelse(bhlist[i]);
+		      
+		  };
+		};
+#endif
 		block++;
 		if (!bh)
 			return written?written:-EIO;
@@ -68,23 +114,23 @@
 		p += chars;
 		buf += chars;
 		bh->b_uptodate = 1;
-		dirtify_buffer(bh, 0);
+		mark_buffer_dirty(bh, 0);
 		brelse(bh);
 	}
+	filp->f_reada = 1;
 	return written;
 }
 
-#define NBUF 32
-
 int block_read(struct inode * inode, struct file * filp, char * buf, int count)
 {
 	unsigned int block;
 	unsigned int offset;
 	int blocksize;
 	int blocksize_bits, i;
-	unsigned int left;
-	unsigned int blocks;
+	unsigned int blocks, rblocks, left;
 	int bhrequest, uptodate;
+	int cluster_list[4];
+	int blocks_per_cluster;
 	struct buffer_head ** bhb, ** bhe;
 	struct buffer_head * buflist[NBUF];
 	struct buffer_head * bhreq[NBUF];
@@ -110,6 +156,8 @@
 	else
 		size = INT_MAX;
 
+	blocks_per_cluster = PAGE_SIZE / blocksize;
+
 	if (offset > size)
 		left = 0;
 	else
@@ -122,12 +170,16 @@
 	block = offset >> blocksize_bits;
 	offset &= blocksize-1;
 	size >>= blocksize_bits;
-	blocks = (left + offset + blocksize - 1) >> blocksize_bits;
+	rblocks = blocks = (left + offset + blocksize - 1) >> blocksize_bits;
 	bhb = bhe = buflist;
 	if (filp->f_reada) {
-		blocks += read_ahead[MAJOR(dev)] / (blocksize >> 9);
+	        if(blocks < read_ahead[MAJOR(dev)] / (blocksize >> 9))
+		  blocks = read_ahead[MAJOR(dev)] / (blocksize >> 9);
 		if (block + blocks > size)
 			blocks = size - block;
+		blocks -= (block % blocks_per_cluster);
+		if(rblocks > blocks) blocks = rblocks;
+		
 	}
 
 	/* We do this in a two stage process.  We first try and request
@@ -145,6 +197,12 @@
 		uptodate = 1;
 		while (blocks) {
 			--blocks;
+#if 1
+			if((block % blocks_per_cluster) == 0) {
+			  for(i=0; i<blocks_per_cluster; i++) cluster_list[i] = block+i;
+			  generate_cluster(dev, cluster_list, blocksize);
+			}
+#endif
 			*bhb = getblk(dev, block++, blocksize);
 			if (*bhb && !(*bhb)->b_uptodate) {
 				uptodate = 0;
@@ -163,8 +221,10 @@
 		}
 
 		/* Now request them all */
-		if (bhrequest)
+		if (bhrequest) {
 			ll_rw_block(READ, bhrequest, bhreq);
+			refill_freelist(blocksize);
+		}
 
 		do { /* Finish off all I/O that has actually completed */
 			if (*bhe) {
diff -u --recursive --new-file v1.1.2/linux/fs/buffer.c linux/fs/buffer.c
--- v1.1.2/linux/fs/buffer.c	Wed Apr 13 11:33:53 1994
+++ linux/fs/buffer.c	Wed Apr 13 11:24:06 1994
@@ -26,8 +26,10 @@
 #include <linux/string.h>
 #include <linux/locks.h>
 #include <linux/errno.h>
+#include <linux/malloc.h>
 
 #include <asm/system.h>
+#include <asm/segment.h>
 #include <asm/io.h>
 
 #ifdef CONFIG_SCSI
@@ -46,23 +48,76 @@
 extern int check_mcd_media_change(int, int);
 #endif
 
+#define NR_SIZES 4
 static char buffersize_index[9] = {-1,  0,  1, -1,  2, -1, -1, -1, 3};
+static short int bufferindex_size[NR_SIZES] = {512, 1024, 2048, 4096};
 
 #define BUFSIZE_INDEX(X) (buffersize_index[(X)>>9])
 
 static int grow_buffers(int pri, int size);
+static int shrink_specific_buffers(unsigned int priority, int size);
+static int maybe_shrink_lav_buffers(int);
 
-static struct buffer_head * hash_table[NR_HASH];
-static struct buffer_head * free_list = NULL;
+static int nr_hash = 0;  /* Size of hash table */
+static struct buffer_head ** hash_table;
+struct buffer_head ** buffer_pages;
+static struct buffer_head * lru_list[NR_LIST] = {NULL, };
+static struct buffer_head * free_list[NR_SIZES] = {NULL, };
 static struct buffer_head * unused_list = NULL;
 static struct wait_queue * buffer_wait = NULL;
 
 int nr_buffers = 0;
+int nr_buffers_type[NR_LIST] = {0,};
+int nr_buffers_size[NR_SIZES] = {0,};
+int nr_buffers_st[NR_SIZES][NR_LIST] = {{0,},};
+int buffer_usage[NR_SIZES] = {0,};  /* Usage counts used to determine load average */
+int buffers_lav[NR_SIZES] = {0,};  /* Load average of buffer usage */
+int nr_free[NR_SIZES] = {0,};
 int buffermem = 0;
 int nr_buffer_heads = 0;
 static int min_free_pages = 20;	/* nr free pages needed before buffer grows */
 extern int *blksize_size[];
 
+/* Here is the parameter block for the bdflush process. */
+static void wakeup_bdflush(int);
+
+#define N_PARAM 9
+#define LAV
+
+static union bdflush_param{
+	struct {
+		int nfract;  /* Percentage of buffer cache dirty to 
+				activate bdflush */
+		int ndirty;  /* Maximum number of dirty blocks to write out per
+				wake-cycle */
+		int nrefill; /* Number of clean buffers to try and obtain
+				each time we call refill */
+		int nref_dirt; /* Dirty buffer threshold for activating bdflush
+				  when trying to refill buffers. */
+		int clu_nfract;  /* Percentage of buffer cache to scan to 
+				    search for free clusters */
+		int age_buffer;  /* Time for normal buffer to age before 
+				    we flush it */
+		int age_super;  /* Time for superblock to age before we 
+				   flush it */
+		int lav_const;  /* Constant used for load average (time
+				   constant */
+		int lav_ratio;  /* Used to determine how low a lav for a
+				   particular size can go before we start to
+				   trim back the buffers */
+	} b_un;
+	unsigned int data[N_PARAM];
+} bdf_prm = {{25, 500, 64, 256, 15, 3000, 500, 1884, 2}};
+
+/* The lav constant is set for 1 minute, as long as the update process runs
+   every 5 seconds.  If you change the frequency of update, the time
+   constant will also change. */
+
+
+/* These are the min and max parameter values that we will allow to be assigned */
+static int bdflush_min[N_PARAM] = {  0,  10,    5,   25,  0,   100,   100, 1, 1};
+static int bdflush_max[N_PARAM] = {100,5000, 2000, 2000,100, 60000, 60000, 2047, 5};
+
 /*
  * Rewrote the wait-routines to use the "new" wait-queue functionality,
  * and getting rid of the cli-sti pairs. The wait-queue routines still
@@ -93,61 +148,81 @@
    return until all buffer writes have completed.  Sync() may return
    before the writes have finished; fsync() may not. */
 
+
+/* Godamity-damn.  Some buffers (bitmaps for filesystems)
+   spontaneously dirty themselves without ever brelse being called.
+   We will ultimately want to put these in a separate list, but for
+   now we search all of the lists for dirty buffers */
+
 static int sync_buffers(dev_t dev, int wait)
 {
 	int i, retry, pass = 0, err = 0;
-	struct buffer_head * bh;
+	int nlist, ncount;
+	struct buffer_head * bh, *next;
 
 	/* One pass for no-wait, three for wait:
 	   0) write out all dirty, unlocked buffers;
 	   1) write out all dirty buffers, waiting if locked;
-	   2) wait for completion by waiting for all buffers to unlock.
-	 */
-repeat:
+	   2) wait for completion by waiting for all buffers to unlock. */
+ repeat:
 	retry = 0;
-	bh = free_list;
-	for (i = nr_buffers*2 ; i-- > 0 ; bh = bh->b_next_free) {
-		if (dev && bh->b_dev != dev)
-			continue;
-#ifdef 0 /* Disable bad-block debugging code */
-		if (bh->b_req && !bh->b_lock &&
-		    !bh->b_dirt && !bh->b_uptodate)
-			printk ("Warning (IO error) - orphaned block %08x on %04x\n",
-				bh->b_blocknr, bh->b_dev);
-#endif
-		if (bh->b_lock)
-		{
-			/* Buffer is locked; skip it unless wait is
-			   requested AND pass > 0. */
-			if (!wait || !pass) {
-				retry = 1;
-				continue;
-			}
-			wait_on_buffer (bh);
-		}
-		/* If an unlocked buffer is not uptodate, there has been 
-		   an IO error. Skip it. */
-		if (wait && bh->b_req && !bh->b_lock &&
-		    !bh->b_dirt && !bh->b_uptodate)
-		{
-			err = 1;
-			continue;
-		}
-		/* Don't write clean buffers.  Don't write ANY buffers
-		   on the third pass. */
-		if (!bh->b_dirt || pass>=2)
-			continue;
-		bh->b_count++;
-		ll_rw_block(WRITE, 1, &bh);
-		bh->b_count--;
-		retry = 1;
-	}
+	ncount = 0;
+	/* We search all lists as a failsafe mechanism, not because we expect
+	   there to be dirty buffers on any of the other lists. */
+	for(nlist = 0; nlist < NR_LIST; nlist++)
+	 {
+	 repeat1:
+		 bh = lru_list[nlist];
+		 if(!bh) continue;
+		 for (i = nr_buffers_type[nlist]*2 ; i-- > 0 ; bh = next) {
+			 if(bh->b_list != nlist) goto repeat1;
+			 next = bh->b_next_free;
+			 if(!lru_list[nlist]) break;
+			 if (dev && bh->b_dev != dev)
+				  continue;
+			 if (bh->b_lock)
+			  {
+				  /* Buffer is locked; skip it unless wait is
+				     requested AND pass > 0. */
+				  if (!wait || !pass) {
+					  retry = 1;
+					  continue;
+				  }
+				  wait_on_buffer (bh);
+			  }
+			 /* If an unlocked buffer is not uptodate, there has
+			     been an IO error. Skip it. */
+			 if (wait && bh->b_req && !bh->b_lock &&
+			     !bh->b_dirt && !bh->b_uptodate)
+			  {
+				  err = 1;
+				  printk("Weird - unlocked, clean and not uptodate buffer on %d list %d\n", nlist);
+				  continue;
+			  }
+			 /* Don't write clean buffers.  Don't write ANY buffers
+			    on the third pass. */
+			 if (!bh->b_dirt || pass>=2)
+				  continue;
+			 bh->b_count++;
+			 bh->b_flushtime = 0;
+			 ll_rw_block(WRITE, 1, &bh);
+
+			 if(nlist != BUF_DIRTY) { 
+				 printk("[%d %x %d] ", nlist, bh->b_dev, bh->b_blocknr);
+				 ncount++;
+			 };
+			 bh->b_count--;
+			 retry = 1;
+		 }
+	 }
+	if (ncount) printk("sys_sync: %d dirty buffers not on dirty list\n", ncount);
+	
 	/* If we are waiting for the sync to succeed, and if any dirty
 	   blocks were written, then repeat; on the second pass, only
 	   wait for buffers being written (do not pass to write any
 	   more buffers on the second pass). */
 	if (wait && retry && ++pass<=2)
-		goto repeat;
+		 goto repeat;
 	return err;
 }
 
@@ -195,15 +270,20 @@
 void invalidate_buffers(dev_t dev)
 {
 	int i;
+	int nlist;
 	struct buffer_head * bh;
 
-	bh = free_list;
-	for (i = nr_buffers*2 ; --i > 0 ; bh = bh->b_next_free) {
-		if (bh->b_dev != dev)
-			continue;
-		wait_on_buffer(bh);
-		if (bh->b_dev == dev)
-			bh->b_uptodate = bh->b_dirt = bh->b_req = 0;
+	for(nlist = 0; nlist < NR_LIST; nlist++) {
+		bh = lru_list[nlist];
+		for (i = nr_buffers_type[nlist]*2 ; --i > 0 ; 
+		     bh = bh->b_next_free) {
+			if (bh->b_dev != dev)
+				 continue;
+			wait_on_buffer(bh);
+			if (bh->b_dev == dev)
+				 bh->b_flushtime = bh->b_uptodate = 
+					  bh->b_dirt = bh->b_req = 0;
+		}
 	}
 }
 
@@ -280,7 +360,7 @@
 #endif
 }
 
-#define _hashfn(dev,block) (((unsigned)(dev^block))%NR_HASH)
+#define _hashfn(dev,block) (((unsigned)(dev^block))%nr_hash)
 #define hash(dev,block) hash_table[_hashfn(dev,block)]
 
 static inline void remove_from_hash_queue(struct buffer_head * bh)
@@ -294,59 +374,118 @@
 	bh->b_next = bh->b_prev = NULL;
 }
 
-static inline void remove_from_free_list(struct buffer_head * bh)
+static inline void remove_from_lru_list(struct buffer_head * bh)
 {
 	if (!(bh->b_prev_free) || !(bh->b_next_free))
-		panic("VFS: Free block list corrupted");
+		panic("VFS: LRU block list corrupted");
+	if (bh->b_dev == 0xffff) panic("LRU list corrupted");
 	bh->b_prev_free->b_next_free = bh->b_next_free;
 	bh->b_next_free->b_prev_free = bh->b_prev_free;
-	if (free_list == bh)
-		free_list = bh->b_next_free;
+
+	if (lru_list[bh->b_list] == bh)
+		 lru_list[bh->b_list] = bh->b_next_free;
+	if(lru_list[bh->b_list] == bh)
+		 lru_list[bh->b_list] = NULL;
+	bh->b_next_free = bh->b_prev_free = NULL;
+}
+
+static inline void remove_from_free_list(struct buffer_head * bh)
+{
+        int isize = BUFSIZE_INDEX(bh->b_size);
+	if (!(bh->b_prev_free) || !(bh->b_next_free))
+		panic("VFS: Free block list corrupted");
+	if(bh->b_dev != 0xffff) panic("Free list corrupted");
+	if(!free_list[isize])
+		 panic("Free list empty");
+	nr_free[isize]--;
+	if(bh->b_next_free == bh)
+		 free_list[isize] = NULL;
+	else {
+		bh->b_prev_free->b_next_free = bh->b_next_free;
+		bh->b_next_free->b_prev_free = bh->b_prev_free;
+		if (free_list[isize] == bh)
+			 free_list[isize] = bh->b_next_free;
+	};
 	bh->b_next_free = bh->b_prev_free = NULL;
 }
 
 static inline void remove_from_queues(struct buffer_head * bh)
 {
+        if(bh->b_dev == 0xffff) {
+		remove_from_free_list(bh); /* Free list entries should not be
+					      in the hash queue */
+		return;
+	};
+	nr_buffers_type[bh->b_list]--;
+	nr_buffers_st[BUFSIZE_INDEX(bh->b_size)][bh->b_list]--;
 	remove_from_hash_queue(bh);
-	remove_from_free_list(bh);
+	remove_from_lru_list(bh);
 }
 
-static inline void put_first_free(struct buffer_head * bh)
+static inline void put_last_lru(struct buffer_head * bh)
 {
-	if (!bh || (bh == free_list))
+	if (!bh)
 		return;
-	remove_from_free_list(bh);
-/* add to front of free list */
-	bh->b_next_free = free_list;
-	bh->b_prev_free = free_list->b_prev_free;
-	free_list->b_prev_free->b_next_free = bh;
-	free_list->b_prev_free = bh;
-	free_list = bh;
+	if (bh == lru_list[bh->b_list]) {
+		lru_list[bh->b_list] = bh->b_next_free;
+		return;
+	}
+	if(bh->b_dev == 0xffff) panic("Wrong block for lru list");
+	remove_from_lru_list(bh);
+/* add to back of free list */
+
+	if(!lru_list[bh->b_list]) {
+		lru_list[bh->b_list] = bh;
+		lru_list[bh->b_list]->b_prev_free = bh;
+	};
+
+	bh->b_next_free = lru_list[bh->b_list];
+	bh->b_prev_free = lru_list[bh->b_list]->b_prev_free;
+	lru_list[bh->b_list]->b_prev_free->b_next_free = bh;
+	lru_list[bh->b_list]->b_prev_free = bh;
 }
 
 static inline void put_last_free(struct buffer_head * bh)
 {
+        int isize;
 	if (!bh)
 		return;
-	if (bh == free_list) {
-		free_list = bh->b_next_free;
-		return;
-	}
-	remove_from_free_list(bh);
+
+	isize = BUFSIZE_INDEX(bh->b_size);	
+	bh->b_dev = 0xffff;  /* So it is obvious we are on the free list */
 /* add to back of free list */
-	bh->b_next_free = free_list;
-	bh->b_prev_free = free_list->b_prev_free;
-	free_list->b_prev_free->b_next_free = bh;
-	free_list->b_prev_free = bh;
+
+	if(!free_list[isize]) {
+		free_list[isize] = bh;
+		bh->b_prev_free = bh;
+	};
+
+	nr_free[isize]++;
+	bh->b_next_free = free_list[isize];
+	bh->b_prev_free = free_list[isize]->b_prev_free;
+	free_list[isize]->b_prev_free->b_next_free = bh;
+	free_list[isize]->b_prev_free = bh;
 }
 
 static inline void insert_into_queues(struct buffer_head * bh)
 {
 /* put at end of free list */
-	bh->b_next_free = free_list;
-	bh->b_prev_free = free_list->b_prev_free;
-	free_list->b_prev_free->b_next_free = bh;
-	free_list->b_prev_free = bh;
+
+        if(bh->b_dev == 0xffff) {
+		put_last_free(bh);
+		return;
+	};
+	if(!lru_list[bh->b_list]) {
+		lru_list[bh->b_list] = bh;
+		bh->b_prev_free = bh;
+	};
+	if (bh->b_next_free) panic("VFS: buffer LRU pointers corrupted");
+	bh->b_next_free = lru_list[bh->b_list];
+	bh->b_prev_free = lru_list[bh->b_list]->b_prev_free;
+	lru_list[bh->b_list]->b_prev_free->b_next_free = bh;
+	lru_list[bh->b_list]->b_prev_free = bh;
+	nr_buffers_type[bh->b_list]++;
+	nr_buffers_st[BUFSIZE_INDEX(bh->b_size)][bh->b_list]++;
 /* put the buffer in new hash-queue if it has a device */
 	bh->b_prev = NULL;
 	bh->b_next = NULL;
@@ -398,7 +537,7 @@
 
 void set_blocksize(dev_t dev, int size)
 {
-	int i;
+	int i, nlist;
 	struct buffer_head * bh, *bhnext;
 
 	if (!blksize_size[MAJOR(dev)])
@@ -421,20 +560,206 @@
   /* We need to be quite careful how we do this - we are moving entries
      around on the free list, and we can get in a loop if we are not careful.*/
 
-	bh = free_list;
-	for (i = nr_buffers*2 ; --i > 0 ; bh = bhnext) {
-		bhnext = bh->b_next_free; 
-		if (bh->b_dev != dev)
-			continue;
-		if (bh->b_size == size)
-			continue;
+	for(nlist = 0; nlist < NR_LIST; nlist++) {
+		bh = lru_list[nlist];
+		for (i = nr_buffers_type[nlist]*2 ; --i > 0 ; bh = bhnext) {
+			if(!bh) break;
+			bhnext = bh->b_next_free; 
+			if (bh->b_dev != dev)
+				 continue;
+			if (bh->b_size == size)
+				 continue;
+			
+			wait_on_buffer(bh);
+			if (bh->b_dev == dev && bh->b_size != size) {
+				bh->b_uptodate = bh->b_dirt = 
+					 bh->b_flushtime = 0;
+			};
+			remove_from_hash_queue(bh);
+		}
+	}
+}
 
-		wait_on_buffer(bh);
-		if (bh->b_dev == dev && bh->b_size != size)
-			bh->b_uptodate = bh->b_dirt = 0;
-		remove_from_hash_queue(bh);
-/*    put_first_free(bh); */
+#define BADNESS(bh) (((bh)->b_dirt<<1)+(bh)->b_lock)
+
+void refill_freelist(int size)
+{
+	struct buffer_head * bh, * tmp;
+	struct buffer_head * candidate[NR_LIST];
+	unsigned int best_time, winner;
+        int isize = BUFSIZE_INDEX(size);
+	int buffers[NR_LIST];
+	int i;
+	int needed;
+
+	/* First see if we even need this.  Sometimes it is advantageous
+	 to request some blocks in a filesystem that we know that we will
+	 be needing ahead of time. */
+
+	if(nr_free[isize] > 100) return 0;
+
+	/* If there are too many dirty buffers, we wake up the update process
+	   now so as to ensure that there are still clean buffers available
+	   for user processes to use (and dirty) */
+	
+	/* We are going to try and locate this much memory */
+	needed =bdf_prm.b_un.nrefill * size;  
+
+	while (nr_free_pages > min_free_pages && needed > 0 &&
+	       grow_buffers(GFP_BUFFER, size)) {
+		needed -= PAGE_SIZE;
+	}
+
+	if(needed <= 0) return;
+
+	/* See if there are too many buffers of a different size.
+	   If so, victimize them */
+
+	while(maybe_shrink_lav_buffers(size))
+	 {
+		 if(!grow_buffers(GFP_BUFFER, size)) break;
+		 needed -= PAGE_SIZE;
+		 if(needed <= 0) return;
+	 };
+
+	/* OK, we cannot grow the buffer cache, now try and get some
+	   from the lru list */
+
+	/* First set the candidate pointers to usable buffers.  This
+	   should be quick nearly all of the time. */
+
+repeat0:
+	for(i=0; i<NR_LIST; i++){
+		if(i == BUF_DIRTY || i == BUF_SHARED || 
+		   nr_buffers_type[i] == 0) {
+			candidate[i] = NULL;
+			buffers[i] = 0;
+			continue;
+		}
+		buffers[i] = nr_buffers_type[i];
+		for (bh = lru_list[i]; buffers[i] > 0; bh = tmp, buffers[i]--)
+		 {
+			 if(buffers[i] < 0) panic("Here is the problem");
+			 tmp = bh->b_next_free;
+			 if (!bh) break;
+			 
+			 if (mem_map[MAP_NR((unsigned long) bh->b_data)] != 1 ||
+			     bh->b_dirt) {
+				 refile_buffer(bh);
+				 continue;
+			 };
+			 
+			 if (bh->b_count || bh->b_size != size)
+				  continue;
+			 
+			 /* Buffers are written in the order they are placed 
+			    on the locked list. If we encounter a locked
+			    buffer here, this means that the rest of them
+			    are also locked */
+			 if(bh->b_lock && (i == BUF_LOCKED || i == BUF_LOCKED1)) {
+				 buffers[i] = 0;
+				 break;
+			 }
+			 
+			 if (BADNESS(bh)) continue;
+			 break;
+		 };
+		if(!buffers[i]) candidate[i] = NULL; /* Nothing on this list */
+		else candidate[i] = bh;
+		if(candidate[i] && candidate[i]->b_count) panic("Here is the problem");
+	}
+	
+ repeat:
+	if(needed <= 0) return;
+	
+	/* Now see which candidate wins the election */
+	
+	winner = best_time = UINT_MAX;	
+	for(i=0; i<NR_LIST; i++){
+		if(!candidate[i]) continue;
+		if(candidate[i]->b_lru_time < best_time){
+			best_time = candidate[i]->b_lru_time;
+			winner = i;
+		}
+	}
+	
+	/* If we have a winner, use it, and then get a new candidate from that list */
+	if(winner != UINT_MAX) {
+		i = winner;
+		bh = candidate[i];
+		candidate[i] = bh->b_next_free;
+		if(candidate[i] == bh) candidate[i] = NULL;  /* Got last one */
+		if (bh->b_count || bh->b_size != size)
+			 panic("Busy buffer in candidate list\n");
+		if (mem_map[MAP_NR((unsigned long) bh->b_data)] != 1)
+			 panic("Shared buffer in candidate list\n");
+		if (BADNESS(bh)) panic("Buffer in candidate list with BADNESS != 0\n");
+		
+		if(bh->b_dev == 0xffff) panic("Wrong list");
+		remove_from_queues(bh);
+		bh->b_dev = 0xffff;
+		put_last_free(bh);
+		needed -= bh->b_size;
+		buffers[i]--;
+		if(buffers[i] < 0) panic("Here is the problem");
+		
+		if(buffers[i] == 0) candidate[i] = NULL;
+		
+		/* Now all we need to do is advance the candidate pointer
+		   from the winner list to the next usable buffer */
+		if(candidate[i] && buffers[i] > 0){
+			if(buffers[i] <= 0) panic("Here is another problem");
+			for (bh = candidate[i]; buffers[i] > 0; bh = tmp, buffers[i]--) {
+				if(buffers[i] < 0) panic("Here is the problem");
+				tmp = bh->b_next_free;
+				if (!bh) break;
+				
+				if (mem_map[MAP_NR((unsigned long) bh->b_data)] != 1 ||
+				    bh->b_dirt) {
+					refile_buffer(bh);
+					continue;
+				};
+				
+				if (bh->b_count || bh->b_size != size)
+					 continue;
+				
+				/* Buffers are written in the order they are
+				   placed on the locked list.  If we encounter
+				   a locked buffer here, this means that the
+				   rest of them are also locked */
+				if(bh->b_lock && (i == BUF_LOCKED || i == BUF_LOCKED1)) {
+					buffers[i] = 0;
+					break;
+				}
+	      
+				if (BADNESS(bh)) continue;
+				break;
+			};
+			if(!buffers[i]) candidate[i] = NULL; /* Nothing here */
+			else candidate[i] = bh;
+			if(candidate[i] && candidate[i]->b_count) 
+				 panic("Here is the problem");
+		}
+		
+		goto repeat;
+	}
+	
+	if(needed <= 0) return;
+	
+	/* Too bad, that was not enough. Try a little harder to grow some. */
+	
+	if (nr_free_pages > 5) {
+		if (grow_buffers(GFP_BUFFER, size)) {
+	                needed -= PAGE_SIZE;
+			goto repeat0;
+		};
 	}
+	
+	/* and repeat until we find something good */
+	if (!grow_buffers(GFP_ATOMIC, size))
+		wakeup_bdflush(1);
+	needed -= PAGE_SIZE;
+	goto repeat0;
 }
 
 /*
@@ -447,85 +772,105 @@
  * 14.02.92: changed it to sync dirty buffers a bit: better performance
  * when the filesystem starts to get full of dirty blocks (I hope).
  */
-#define BADNESS(bh) (((bh)->b_dirt<<1)+(bh)->b_lock)
 struct buffer_head * getblk(dev_t dev, int block, int size)
 {
-	struct buffer_head * bh, * tmp;
-	int buffers;
-	static int grow_size = 0;
+	struct buffer_head * bh;
+        int isize = BUFSIZE_INDEX(size);
+
+	/* Update this for the buffer size lav. */
+	buffer_usage[isize]++;
 
+	/* If there are too many dirty buffers, we wake up the update process
+	   now so as to ensure that there are still clean buffers available
+	   for user processes to use (and dirty) */
 repeat:
 	bh = get_hash_table(dev, block, size);
 	if (bh) {
 		if (bh->b_uptodate && !bh->b_dirt)
-			put_last_free(bh);
+			 put_last_lru(bh);
+		if(!bh->b_dirt) bh->b_flushtime = 0;
 		return bh;
 	}
-	grow_size -= size;
-	if (nr_free_pages > min_free_pages && grow_size <= 0) {
-		if (grow_buffers(GFP_BUFFER, size))
-			grow_size = PAGE_SIZE;
-	}
-	buffers = nr_buffers;
-	bh = NULL;
 
-	for (tmp = free_list; buffers-- > 0 ; tmp = tmp->b_next_free) {
-		if (tmp->b_count || tmp->b_size != size)
-			continue;
-		if (mem_map[MAP_NR((unsigned long) tmp->b_data)] != 1)
-			continue;
-		if (!bh || BADNESS(tmp)<BADNESS(bh)) {
-			bh = tmp;
-			if (!BADNESS(tmp))
-				break;
-		}
-#if 0
-		if (tmp->b_dirt) {
-			tmp->b_count++;
-			ll_rw_block(WRITEA, 1, &tmp);
-			tmp->b_count--;
-		}
-#endif
-	}
+	while(!free_list[isize]) refill_freelist(size);
+	
+	if (find_buffer(dev,block,size))
+		 goto repeat;
 
-	if (!bh) {
-		if (nr_free_pages > 5)
-			if (grow_buffers(GFP_BUFFER, size))
-				goto repeat;
-		if (!grow_buffers(GFP_ATOMIC, size))
-			sleep_on(&buffer_wait);
-		goto repeat;
-	}
+	bh = free_list[isize];
+	remove_from_free_list(bh);
 
-	wait_on_buffer(bh);
-	if (bh->b_count || bh->b_size != size)
-		goto repeat;
-	if (bh->b_dirt) {
-		sync_buffers(0,0);
-		goto repeat;
-	}
-/* NOTE!! While we slept waiting for this block, somebody else might */
-/* already have added "this" block to the cache. check it */
-	if (find_buffer(dev,block,size))
-		goto repeat;
-/* OK, FINALLY we know that this buffer is the only one of its kind, */
+/* OK, FINALLY we know that this buffer is the only one of it's kind, */
 /* and that it's unused (b_count=0), unlocked (b_lock=0), and clean */
 	bh->b_count=1;
 	bh->b_dirt=0;
+	bh->b_lock=0;
 	bh->b_uptodate=0;
+	bh->b_flushtime = 0;
 	bh->b_req=0;
-	remove_from_queues(bh);
 	bh->b_dev=dev;
 	bh->b_blocknr=block;
 	insert_into_queues(bh);
 	return bh;
 }
 
+void set_writetime(struct buffer_head * buf, int flag)
+{
+        int newtime;
+
+	if (buf->b_dirt){
+		/* Move buffer to dirty list if jiffies is clear */
+		newtime = jiffies + (flag ? bdf_prm.b_un.age_super : 
+				     bdf_prm.b_un.age_buffer);
+		if(!buf->b_flushtime || buf->b_flushtime > newtime)
+			 buf->b_flushtime = newtime;
+	} else {
+		buf->b_flushtime = 0;
+	}
+}
+
+
+static char buffer_disposition[] = {BUF_CLEAN, BUF_SHARED, BUF_LOCKED, BUF_SHARED, 
+				      BUF_DIRTY, BUF_DIRTY, BUF_DIRTY, BUF_DIRTY};
+
+void refile_buffer(struct buffer_head * buf){
+	int i, dispose;
+	i = 0;
+	if(buf->b_dev == 0xffff) panic("Attempt to refile free buffer\n");
+	if(mem_map[MAP_NR((unsigned long) buf->b_data)] != 1) i = 1;
+	if(buf->b_lock) i |= 2;
+	if(buf->b_dirt) i |= 4;
+	dispose = buffer_disposition[i];
+	if(buf->b_list == BUF_SHARED && dispose == BUF_CLEAN)
+		 dispose = BUF_UNSHARED;
+	if(dispose == -1) panic("Bad buffer settings (%d)\n", i);
+	if(dispose == BUF_CLEAN) buf->b_lru_time = jiffies;
+	if(dispose != buf->b_list)  {
+		if(dispose == BUF_DIRTY || dispose == BUF_UNSHARED)
+			 buf->b_lru_time = jiffies;
+		if(dispose == BUF_LOCKED && 
+		   (buf->b_flushtime - buf->b_lru_time) <= bdf_prm.b_un.age_super)
+			 dispose = BUF_LOCKED1;
+		remove_from_queues(buf);
+		buf->b_list = dispose;
+		insert_into_queues(buf);
+		if(dispose == BUF_DIRTY && nr_buffers_type[BUF_DIRTY] > 
+		   (nr_buffers - nr_buffers_type[BUF_SHARED]) *
+		   bdf_prm.b_un.nfract/100)
+			 wakeup_bdflush(0);
+	}
+}
+
 void brelse(struct buffer_head * buf)
 {
 	if (!buf)
 		return;
 	wait_on_buffer(buf);
+
+	/* If dirty, mark the time this buffer should be written back */
+	set_writetime(buf, 0);
+	refile_buffer(buf);
+
 	if (buf->b_count) {
 		if (--buf->b_count)
 			return;
@@ -560,7 +905,8 @@
 
 /*
  * Ok, breada can be used as bread, but additionally to mark other
- * blocks for reading as well.
+ * blocks for reading as well. End the argument list with a negative
+ * number.
  */
 
 #define NBUF 16
@@ -686,6 +1032,7 @@
 		head = bh;
 		bh->b_data = (char *) (page+offset);
 		bh->b_size = size;
+		bh->b_dev = 0xffff;  /* Flag as unused */
 	}
 	return head;
 /*
@@ -766,6 +1113,7 @@
 {
 	struct buffer_head * bh, * tmp, * arr[8];
 	unsigned long offset;
+        int isize = BUFSIZE_INDEX(size);
 	int * p;
 	int block;
 
@@ -788,10 +1136,14 @@
 		arr[block++] = bh;
 		bh->b_count = 1;
 		bh->b_dirt = 0;
+		bh->b_flushtime = 0;
 		bh->b_uptodate = 0;
+		bh->b_req = 0;
 		bh->b_dev = dev;
 		bh->b_blocknr = *(p++);
+		bh->b_list = BUF_CLEAN;
 		nr_buffers++;
+		nr_buffers_size[isize]++;
 		insert_into_queues(bh);
 		if (bh->b_this_page)
 			bh = bh->b_this_page;
@@ -801,6 +1153,7 @@
 	buffermem += PAGE_SIZE;
 	bh->b_this_page = tmp;
 	mem_map[MAP_NR(address)]++;
+	buffer_pages[address >> PAGE_SHIFT] = bh;
 	read_buffers(arr,block);
 	while (block-- > 0)
 		brelse(arr[block]);
@@ -889,11 +1242,16 @@
 {
 	unsigned long page;
 	struct buffer_head *bh, *tmp;
+	struct buffer_head * insert_point;
+	int isize;
 
 	if ((size & 511) || (size > PAGE_SIZE)) {
 		printk("VFS: grow_buffers: size = %d\n",size);
 		return 0;
 	}
+
+	isize = BUFSIZE_INDEX(size);
+
 	if (!(page = __get_free_page(pri)))
 		return 0;
 	bh = create_buffers(page, size);
@@ -901,25 +1259,32 @@
 		free_page(page);
 		return 0;
 	}
+
+	insert_point = free_list[isize];
+
 	tmp = bh;
 	while (1) {
-		if (free_list) {
-			tmp->b_next_free = free_list;
-			tmp->b_prev_free = free_list->b_prev_free;
-			free_list->b_prev_free->b_next_free = tmp;
-			free_list->b_prev_free = tmp;
+	        nr_free[isize]++;
+		if (insert_point) {
+			tmp->b_next_free = insert_point->b_next_free;
+			tmp->b_prev_free = insert_point;
+			insert_point->b_next_free->b_prev_free = tmp;
+			insert_point->b_next_free = tmp;
 		} else {
 			tmp->b_prev_free = tmp;
 			tmp->b_next_free = tmp;
 		}
-		free_list = tmp;
+		insert_point = tmp;
 		++nr_buffers;
 		if (tmp->b_this_page)
 			tmp = tmp->b_this_page;
 		else
 			break;
 	}
+	free_list[isize] = bh;
+	buffer_pages[page >> PAGE_SHIFT] = bh;
 	tmp->b_this_page = bh;
+	wake_up(&buffer_wait);
 	buffermem += PAGE_SIZE;
 	return 1;
 }
@@ -932,6 +1297,7 @@
 {
 	unsigned long page;
 	struct buffer_head * tmp, * p;
+        int isize = BUFSIZE_INDEX(bh->b_size);
 
 	*bhp = bh;
 	page = (unsigned long) bh->b_data;
@@ -949,16 +1315,72 @@
 		p = tmp;
 		tmp = tmp->b_this_page;
 		nr_buffers--;
+		nr_buffers_size[isize]--;
 		if (p == *bhp)
-			*bhp = p->b_prev_free;
+		  {
+		    *bhp = p->b_prev_free;
+		    if (p == *bhp) /* Was this the last in the list? */
+		      *bhp = NULL;
+		  }
 		remove_from_queues(p);
 		put_unused_buffer_head(p);
 	} while (tmp != bh);
 	buffermem -= PAGE_SIZE;
+	buffer_pages[page >> PAGE_SHIFT] = NULL;
 	free_page(page);
 	return !mem_map[MAP_NR(page)];
 }
 
+
+/*
+ * Consult the load average for buffers and decide whether or not
+ * we should shrink the buffers of one size or not.  If we decide yes,
+ * do it and return 1.  Else return 0.  Do not attempt to shrink size
+ * that is specified.
+ *
+ * I would prefer not to use a load average, but the way things are now it
+ * seems unavoidable.  The way to get rid of it would be to force clustering
+ * universally, so that when we reclaim buffers we always reclaim an entire
+ * page.  Doing this would mean that we all need to move towards QMAGIC.
+ */
+
+static int maybe_shrink_lav_buffers(int size)
+{	   
+	int nlist;
+	int isize;
+	int total_lav, total_n_buffers, n_sizes;
+	
+	/* Do not consider the shared buffers since they would not tend
+	   to have getblk called very often, and this would throw off
+	   the lav.  They are not easily reclaimable anyway (let the swapper
+	   make the first move). */
+  
+	total_lav = total_n_buffers = n_sizes = 0;
+	for(nlist = 0; nlist < NR_SIZES; nlist++)
+	 {
+		 total_lav += buffers_lav[nlist];
+		 if(nr_buffers_size[nlist]) n_sizes++;
+		 total_n_buffers += nr_buffers_size[nlist];
+		 total_n_buffers -= nr_buffers_st[nlist][BUF_SHARED]; 
+	 }
+	
+	/* See if we have an excessive number of buffers of a particular
+	   size - if so, victimize that bunch. */
+  
+	isize = (size ? BUFSIZE_INDEX(size) : -1);
+	
+	if (n_sizes > 1)
+		 for(nlist = 0; nlist < NR_SIZES; nlist++)
+		  {
+			  if(nlist == isize) continue;
+			  if(nr_buffers_size[nlist] &&
+			     bdf_prm.b_un.lav_const * buffers_lav[nlist]*total_n_buffers < 
+			     total_lav * (nr_buffers_size[nlist] - nr_buffers_st[nlist][BUF_SHARED]))
+				   if(shrink_specific_buffers(6, bufferindex_size[nlist])) 
+					    return 1;
+		  }
+	return 0;
+}
 /*
  * Try to free up some pages by shrinking the buffer-cache
  *
@@ -968,64 +1390,288 @@
  */
 int shrink_buffers(unsigned int priority)
 {
+	if (priority < 2) {
+		sync_buffers(0,0);
+	}
+
+	if(priority == 2) wakeup_bdflush(1);
+
+	if(maybe_shrink_lav_buffers(0)) return 1;
+
+	/* No good candidate size - take any size we can find */
+        return shrink_specific_buffers(priority, 0);
+}
+
+static int shrink_specific_buffers(unsigned int priority, int size)
+{
 	struct buffer_head *bh;
-	int i;
+	int nlist;
+	int i, isize, isize1;
 
-	if (priority < 2)
-		sync_buffers(0,0);
-	bh = free_list;
-	i = nr_buffers >> priority;
-	for ( ; i-- > 0 ; bh = bh->b_next_free) {
-		if (bh->b_count ||
-		    (priority >= 5 &&
-		     mem_map[MAP_NR((unsigned long) bh->b_data)] > 1)) {
-			put_last_free(bh);
-			continue;
+#ifdef DEBUG
+	if(size) printk("Shrinking buffers of size %d\n", size);
+#endif
+	/* First try the free lists, and see if we can get a complete page
+	   from here */
+	isize1 = (size ? BUFSIZE_INDEX(size) : -1);
+
+	for(isize = 0; isize<NR_SIZES; isize++){
+		if(isize1 != -1 && isize1 != isize) continue;
+		bh = free_list[isize];
+		if(!bh) continue;
+		for (i=0 ; !i || bh != free_list[isize]; bh = bh->b_next_free, i++) {
+			if (bh->b_count || !bh->b_this_page)
+				 continue;
+			if (try_to_free(bh, &bh))
+				 return 1;
+			if(!bh) break; /* Some interrupt must have used it after we
+					  freed the page.  No big deal - keep looking */
 		}
-		if (!bh->b_this_page)
-			continue;
-		if (bh->b_lock)
-			if (priority)
+	}
+	
+	/* Not enough in the free lists, now try the lru list */
+	
+	for(nlist = 0; nlist < NR_LIST; nlist++) {
+	repeat1:
+		if(priority > 3 && nlist == BUF_SHARED) continue;
+		bh = lru_list[nlist];
+		if(!bh) continue;
+		i = nr_buffers_type[nlist] >> priority;
+		for ( ; i-- > 0 ; bh = bh->b_next_free) {
+			/* We may have stalled while waiting for I/O to complete. */
+			if(bh->b_list != nlist) goto repeat1;
+			if (bh->b_count || !bh->b_this_page)
+				 continue;
+			if(size && bh->b_size != size) continue;
+			if (bh->b_lock)
+				 if (priority)
+					  continue;
+				 else
+					  wait_on_buffer(bh);
+			if (bh->b_dirt) {
+				bh->b_count++;
+				bh->b_flushtime = 0;
+				ll_rw_block(WRITEA, 1, &bh);
+				bh->b_count--;
 				continue;
-			else
-				wait_on_buffer(bh);
-		if (bh->b_dirt) {
-			bh->b_count++;
-			ll_rw_block(WRITEA, 1, &bh);
-			bh->b_count--;
-			continue;
+			}
+			if (try_to_free(bh, &bh))
+				 return 1;
+			if(!bh) break;
 		}
-		if (try_to_free(bh, &bh))
-			return 1;
 	}
 	return 0;
 }
 
+
 void show_buffers(void)
 {
 	struct buffer_head * bh;
 	int found = 0, locked = 0, dirty = 0, used = 0, lastused = 0;
+	int shared;
+	int nlist, isize;
 
 	printk("Buffer memory:   %6dkB\n",buffermem>>10);
 	printk("Buffer heads:    %6d\n",nr_buffer_heads);
 	printk("Buffer blocks:   %6d\n",nr_buffers);
-	bh = free_list;
-	do {
+
+	for(nlist = 0; nlist < NR_LIST; nlist++) {
+	  shared = found = locked = dirty = used = lastused = 0;
+	  bh = lru_list[nlist];
+	  if(!bh) continue;
+	  do {
 		found++;
 		if (bh->b_lock)
 			locked++;
 		if (bh->b_dirt)
 			dirty++;
+		if(mem_map[MAP_NR(((unsigned long) bh->b_data))] !=1) shared++;
 		if (bh->b_count)
 			used++, lastused = found;
 		bh = bh->b_next_free;
-	} while (bh != free_list);
-	printk("Buffer mem: %d buffers, %d used (last=%d), %d locked, %d dirty\n",
-		found, used, lastused, locked, dirty);
+	      } while (bh != lru_list[nlist]);
+	printk("Buffer[%d] mem: %d buffers, %d used (last=%d), %d locked, %d dirty %d shrd\n",
+		nlist, found, used, lastused, locked, dirty, shared);
+	};
+	printk("Size    [LAV]     Free  Clean  Unshar     Lck    Lck1   Dirty  Shared\n");
+	for(isize = 0; isize<NR_SIZES; isize++){
+		printk("%5d [%5d]: %7d ", bufferindex_size[isize],
+		       buffers_lav[isize], nr_free[isize]);
+		for(nlist = 0; nlist < NR_LIST; nlist++)
+			 printk("%7d ", nr_buffers_st[isize][nlist]);
+		printk("\n");
+	}
+}
+
+/*
+ * try_to_reassign() checks if all the buffers on this particular page
+ * are unused, and reassign to a new cluster them if this is true.
+ */
+static inline int try_to_reassign(struct buffer_head * bh, struct buffer_head ** bhp,
+			   dev_t dev, unsigned int starting_block)
+{
+	unsigned long page;
+	struct buffer_head * tmp, * p;
+
+	*bhp = bh;
+	page = (unsigned long) bh->b_data;
+	page &= PAGE_MASK;
+	if(mem_map[MAP_NR(page)] != 1) return 0;
+	tmp = bh;
+	do {
+		if (!tmp)
+			 return 0;
+		
+		if (tmp->b_count || tmp->b_dirt || tmp->b_lock)
+			 return 0;
+		tmp = tmp->b_this_page;
+	} while (tmp != bh);
+	tmp = bh;
+	
+	while((unsigned int) tmp->b_data & (PAGE_SIZE - 1)) 
+		 tmp = tmp->b_this_page;
+	
+	/* This is the buffer at the head of the page */
+	bh = tmp;
+	do {
+		p = tmp;
+		tmp = tmp->b_this_page;
+		remove_from_queues(p);
+		p->b_dev=dev;
+		p->b_uptodate = 0;
+		p->b_req = 0;
+		p->b_blocknr=starting_block++;
+		insert_into_queues(p);
+	} while (tmp != bh);
+	return 1;
+}
+
+/*
+ * Try to find a free cluster by locating a page where
+ * all of the buffers are unused.  We would like this function
+ * to be atomic, so we do not call anything that might cause
+ * the process to sleep.  The priority is somewhat similar to
+ * the priority used in shrink_buffers.
+ * 
+ * My thinking is that the kernel should end up using whole
+ * pages for the buffer cache as much of the time as possible.
+ * This way the other buffers on a particular page are likely
+ * to be very near each other on the free list, and we will not
+ * be expiring data prematurely.  For now we only canibalize buffers
+ * of the same size to keep the code simpler.
+ */
+static int reassign_cluster(dev_t dev, 
+		     unsigned int starting_block, int size)
+{
+	struct buffer_head *bh;
+        int isize = BUFSIZE_INDEX(size);
+	int i;
+
+	/* We want to give ourselves a really good shot at generating
+	   a cluster, and since we only take buffers from the free
+	   list, we "overfill" it a little. */
+
+	while(nr_free[isize] < 32) refill_freelist(size);
+
+	bh = free_list[isize];
+	if(bh)
+		 for (i=0 ; !i || bh != free_list[isize] ; bh = bh->b_next_free, i++) {
+			 if (!bh->b_this_page)	continue;
+			 if (try_to_reassign(bh, &bh, dev, starting_block))
+				 return 4;
+		 }
+	return 0;
+}
+
+/* This function tries to generate a new cluster of buffers
+ * from a new page in memory.  We should only do this if we have
+ * not expanded the buffer cache to the maximum size that we allow.
+ */
+static unsigned long try_to_generate_cluster(dev_t dev, int block, int size)
+{
+	struct buffer_head * bh, * tmp, * arr[8];
+        int isize = BUFSIZE_INDEX(size);
+	unsigned long offset;
+	unsigned long page;
+	int nblock;
+
+	page = get_free_page(GFP_NOBUFFER);
+	if(!page) return 0;
+
+	bh = create_buffers(page, size);
+	if (!bh) {
+		free_page(page);
+		return 0;
+	};
+	nblock = block;
+	for (offset = 0 ; offset < PAGE_SIZE ; offset += size) {
+		if (find_buffer(dev, nblock++, size))
+			 goto not_aligned;
+	}
+	tmp = bh;
+	nblock = 0;
+	while (1) {
+		arr[nblock++] = bh;
+		bh->b_count = 1;
+		bh->b_dirt = 0;
+		bh->b_flushtime = 0;
+		bh->b_lock = 0;
+		bh->b_uptodate = 0;
+		bh->b_req = 0;
+		bh->b_dev = dev;
+		bh->b_list = BUF_CLEAN;
+		bh->b_blocknr = block++;
+		nr_buffers++;
+		nr_buffers_size[isize]++;
+		insert_into_queues(bh);
+		if (bh->b_this_page)
+			bh = bh->b_this_page;
+		else
+			break;
+	}
+	buffermem += PAGE_SIZE;
+	buffer_pages[page >> PAGE_SHIFT] = bh;
+	bh->b_this_page = tmp;
+	while (nblock-- > 0)
+		brelse(arr[nblock]);
+	return 4;
+not_aligned:
+	while ((tmp = bh) != NULL) {
+		bh = bh->b_this_page;
+		put_unused_buffer_head(tmp);
+	}
+	free_page(page);
+	return 0;
+}
+
+unsigned long generate_cluster(dev_t dev, int b[], int size)
+{
+	int i, offset;
+	
+	for (i = 0, offset = 0 ; offset < PAGE_SIZE ; i++, offset += size) {
+		if(i && b[i]-1 != b[i-1]) return 0;  /* No need to cluster */
+		if(find_buffer(dev, b[i], size)) return 0;
+	};
+
+	/* OK, we have a candidate for a new cluster */
+	
+	/* See if one size of buffer is over-represented in the buffer cache,
+	   if so reduce the numbers of buffers */
+	if(maybe_shrink_lav_buffers(size))
+	 {
+		 int retval;
+		 retval = try_to_generate_cluster(dev, b[0], size);
+		 if(retval) return retval;
+	 };
+	
+	if (nr_free_pages > min_free_pages) 
+		 return try_to_generate_cluster(dev, b[0], size);
+	else
+		 return reassign_cluster(dev, b[0], size);
 }
 
 /*
- * This initializes the initial buffer free list.  nr_buffers is set
+ * This initializes the initial buffer free list.  nr_buffers_type is set
  * to one less the actual number of buffers, as a sop to backwards
  * compatibility --- the old code did this (I think unintentionally,
  * but I'm not sure), and programs in the ps package expect it.
@@ -1034,16 +1680,254 @@
 void buffer_init(void)
 {
 	int i;
+        int isize = BUFSIZE_INDEX(BLOCK_SIZE);
 
-	if (high_memory >= 4*1024*1024)
+	if (high_memory >= 4*1024*1024) {
 		min_free_pages = 200;
-	else
+		if(high_memory >= 16*1024*1024)
+			 nr_hash = 16381;
+		else
+			 nr_hash = 4093;
+	} else {
 		min_free_pages = 20;
-	for (i = 0 ; i < NR_HASH ; i++)
+		nr_hash = 997;
+	};
+	
+	hash_table = (struct buffer_head **) vmalloc(nr_hash * 
+						     sizeof(struct buffer_head *));
+
+
+	buffer_pages = (struct buffer_head **) vmalloc((high_memory >>PAGE_SHIFT) * 
+						     sizeof(struct buffer_head *));
+	for (i = 0 ; i < high_memory >> PAGE_SHIFT ; i++)
+		buffer_pages[i] = NULL;
+
+	for (i = 0 ; i < nr_hash ; i++)
 		hash_table[i] = NULL;
-	free_list = 0;
+	lru_list[BUF_CLEAN] = 0;
 	grow_buffers(GFP_KERNEL, BLOCK_SIZE);
-	if (!free_list)
+	if (!free_list[isize])
 		panic("VFS: Unable to initialize buffer free list!");
 	return;
 }
+
+/* This is a simple kernel daemon, whose job it is to provide a dynamicly
+ * response to dirty buffers.  Once this process is activated, we write back
+ * a limited number of buffers to the disks and then go back to sleep again.
+ * In effect this is a process which never leaves kernel mode, and does not have
+ * any user memory associated with it except for the stack.  There is also
+ * a kernel stack page, which obviously must be separate from the user stack.
+ */
+struct wait_queue * bdflush_wait = NULL;
+struct wait_queue * bdflush_done = NULL;
+
+static int bdflush_running = 0;
+
+static void wakeup_bdflush(int wait)
+{
+	if(!bdflush_running){
+		printk("Warning - bdflush not running\n");
+		sync_buffers(0,0);
+		return;
+	};
+	wake_up(&bdflush_wait);
+	if(wait) sleep_on(&bdflush_done);
+}
+
+
+
+/* 
+ * Here we attempt to write back old buffers.  We also try and flush indoes 
+ * and supers as well, since this function is essentially "update", and 
+ * otherwise there would be no way of ensuring that these quantities ever 
+ * get written back.  Ideally, we would have a timestamp on the inodes
+ * and superblocks so that we could write back only the old ones as well
+ */
+
+asmlinkage int sync_old_buffers(void)
+{
+	int i, isize;
+	int ndirty, nwritten;
+	int nlist;
+	int ncount;
+	struct buffer_head * bh, *next;
+
+	sync_supers(0);
+	sync_inodes(0);
+
+	ncount = 0;
+#ifdef DEBUG
+	for(nlist = 0; nlist < NR_LIST; nlist++)
+#else
+	for(nlist = BUF_DIRTY; nlist <= BUF_DIRTY; nlist++)
+#endif
+	{
+		ndirty = 0;
+		nwritten = 0;
+	repeat:
+		bh = lru_list[nlist];
+		if(bh) 
+			 for (i = nr_buffers_type[nlist]; --i > 0; bh = next) {
+				 /* We may have stalled while waiting for I/O to complete. */
+				 if(bh->b_list != nlist) goto repeat;
+				 next = bh->b_next_free;
+				 if(!lru_list[nlist]) {
+					 printk("Dirty list empty %d\n", i);
+					 break;
+				 }
+				 
+				 /* Clean buffer on dirty list?  Refile it */
+				 if (nlist == BUF_DIRTY && !bh->b_dirt && !bh->b_lock)
+				  {
+					  refile_buffer(bh);
+					  continue;
+				  }
+				 
+				 if (bh->b_lock || !bh->b_dirt)
+					  continue;
+				 ndirty++;
+				 if(bh->b_flushtime > jiffies) continue;
+				 nwritten++;
+				 bh->b_count++;
+				 bh->b_flushtime = 0;
+#ifdef DEBUG
+				 if(nlist != BUF_DIRTY) ncount++;
+#endif
+				 ll_rw_block(WRITE, 1, &bh);
+				 bh->b_count--;
+			 }
+	}
+#ifdef DEBUG
+	if (ncount) printk("sync_old_buffers: %d dirty buffers not on dirty list\n", ncount);
+	printk("Wrote %d/%d buffers\n", nwritten, ndirty);
+#endif
+	
+	/* We assume that we only come through here on a regular
+	   schedule, like every 5 seconds.  Now update load averages.  
+	   Shift usage counts to prevent overflow. */
+	for(isize = 0; isize<NR_SIZES; isize++){
+		CALC_LOAD(buffers_lav[isize], bdf_prm.b_un.lav_const, buffer_usage[isize]);
+		buffer_usage[isize] = 0;
+	};
+	return 0;
+}
+
+
+/* This is the interface to bdflush.  As we get more sophisticated, we can
+ * pass tuning parameters to this "process", to adjust how it behaves.  If you
+ * invoke this again after you have done this once, you would simply modify 
+ * the tuning parameters.  We would want to verify each parameter, however,
+ * to make sure that it is reasonable. */
+
+asmlinkage int sys_bdflush(int func, int data)
+{
+	int i, error;
+	int ndirty;
+	int nlist;
+	int ncount;
+	struct buffer_head * bh, *next;
+
+	if(!suser()) return -EPERM;
+
+	if(func == 1)
+		 return sync_old_buffers();
+
+	/* Basically func 0 means start, 1 means read param 1, 2 means write param 1, etc */
+	if(func >= 2){
+		i = (func-2) >> 1;
+		if (i < 0 || i >= N_PARAM) return -EINVAL;
+		if((func & 1) == 0) {
+			error = verify_area(VERIFY_WRITE, (void *) data, sizeof(int));
+			if(error) return error;
+			put_fs_long(bdf_prm.data[i], data);
+			return 0;
+		};
+		if(data < bdflush_min[i] || data > bdflush_max[i]) return -EINVAL;
+		bdf_prm.data[i] = data;
+		return 0;
+	};
+	
+	if(bdflush_running++) return -EBUSY; /* Only one copy of this running at one time */
+	
+	/* OK, from here on is the daemon */
+	
+	while(1==1){
+#ifdef DEBUG
+		printk("bdflush() activated...");
+#endif
+		
+		ncount = 0;
+#ifdef DEBUG
+		for(nlist = 0; nlist < NR_LIST; nlist++)
+#else
+		for(nlist = BUF_DIRTY; nlist <= BUF_DIRTY; nlist++)
+#endif
+		 {
+			 ndirty = 0;
+		 repeat:
+			 bh = lru_list[nlist];
+			 if(bh) 
+				  for (i = nr_buffers_type[nlist]; --i > 0 && ndirty < bdf_prm.b_un.ndirty; 
+				       bh = next) {
+					  /* We may have stalled while waiting for I/O to complete. */
+					  if(bh->b_list != nlist) goto repeat;
+					  next = bh->b_next_free;
+					  if(!lru_list[nlist]) {
+						  printk("Dirty list empty %d\n", i);
+						  break;
+					  }
+					  
+					  /* Clean buffer on dirty list?  Refile it */
+					  if (nlist == BUF_DIRTY && !bh->b_dirt && !bh->b_lock)
+					   {
+						   refile_buffer(bh);
+						   continue;
+					   }
+					  
+					  if (bh->b_lock || !bh->b_dirt)
+						   continue;
+					  /* Should we write back buffers that are shared or not??
+					     currently dirty buffers are not shared, so it does not matter */
+					  bh->b_count++;
+					  ndirty++;
+					  bh->b_flushtime = 0;
+					  ll_rw_block(WRITE, 1, &bh);
+#ifdef DEBUG
+					  if(nlist != BUF_DIRTY) ncount++;
+#endif
+					  bh->b_count--;
+				  }
+		 }
+#ifdef DEBUG
+		if (ncount) printk("sys_bdflush: %d dirty buffers not on dirty list\n", ncount);
+		printk("sleeping again.\n");
+#endif
+		wake_up(&bdflush_done);
+		
+		/* If there are still a lot of dirty buffers around, skip the sleep
+		   and flush some more */
+		
+		if(nr_buffers_type[BUF_DIRTY] < (nr_buffers - nr_buffers_type[BUF_SHARED]) * 
+		   bdf_prm.b_un.nfract/100) {
+			interruptible_sleep_on(&bdflush_wait);
+		}
+	}
+}
+
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only.  This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
diff -u --recursive --new-file v1.1.2/linux/fs/devices.c linux/fs/devices.c
--- v1.1.2/linux/fs/devices.c	Mon Dec 27 07:35:48 1993
+++ linux/fs/devices.c	Wed Apr 13 09:14:07 1994
@@ -28,6 +28,26 @@
 	{ NULL, NULL },
 };
 
+int get_device_list(char * page)
+{
+	int i;
+	int len;
+
+	len = sprintf(page, "Character devices:\n");
+	for (i = 0; i < MAX_CHRDEV ; i++) {
+		if (chrdevs[i].fops) {
+			len += sprintf(page+len, "%2d %s\n", i, chrdevs[i].name);
+		}
+	}
+	len += sprintf(page+len, "\nBlock devices:\n");
+	for (i = 0; i < MAX_BLKDEV ; i++) {
+		if (blkdevs[i].fops) {
+			len += sprintf(page+len, "%2d %s\n", i, blkdevs[i].name);
+		}
+	}
+	return len;
+}
+
 struct file_operations * get_blkfops(unsigned int major)
 {
 	if (major >= MAX_BLKDEV)
diff -u --recursive --new-file v1.1.2/linux/fs/ext/file.c linux/fs/ext/file.c
--- v1.1.2/linux/fs/ext/file.c	Wed Apr 13 11:33:53 1994
+++ linux/fs/ext/file.c	Wed Apr 13 11:16:27 1994
@@ -248,7 +248,7 @@
 		memcpy_fromfs(p,buf,c);
 		buf += c;
 		bh->b_uptodate = 1;
-		dirtify_buffer(bh, 0);
+		mark_buffer_dirty(bh, 0);
 		brelse(bh);
 	}
 	inode->i_mtime = inode->i_ctime = CURRENT_TIME;
diff -u --recursive --new-file v1.1.2/linux/fs/ext/freelists.c linux/fs/ext/freelists.c
--- v1.1.2/linux/fs/ext/freelists.c	Wed Apr 13 11:33:53 1994
+++ linux/fs/ext/freelists.c	Wed Apr 13 11:16:28 1994
@@ -83,7 +83,7 @@
 	}
 	sb->u.ext_sb.s_freeblockscount ++;
 	sb->s_dirt = 1;
-	dirtify_buffer(sb->u.ext_sb.s_firstfreeblock, 1);
+	mark_buffer_dirty(sb->u.ext_sb.s_firstfreeblock, 1);
 	unlock_super (sb);
 	return;
 }
@@ -104,7 +104,7 @@
 	efb = (struct ext_free_block *) sb->u.ext_sb.s_firstfreeblock->b_data;
 	if (efb->count) {
 		j = efb->free[--efb->count];
-		dirtify_buffer(sb->u.ext_sb.s_firstfreeblock, 1);
+		mark_buffer_dirty(sb->u.ext_sb.s_firstfreeblock, 1);
 	} else {
 #ifdef EXTFS_DEBUG
 printk("ext_new_block: block empty, skipping to %d\n", efb->next);
@@ -135,7 +135,7 @@
 	}
 	clear_block(bh->b_data);
 	bh->b_uptodate = 1;
-	dirtify_buffer(bh, 1);
+	mark_buffer_dirty(bh, 1);
 	brelse(bh);
 #ifdef EXTFS_DEBUG
 printk("ext_new_block: allocating block %d\n", j);
@@ -239,7 +239,7 @@
 	}
 	sb->u.ext_sb.s_freeinodescount ++;
 	sb->s_dirt = 1;
-	dirtify_buffer(sb->u.ext_sb.s_firstfreeinodeblock, 1);
+	mark_buffer_dirty(sb->u.ext_sb.s_firstfreeinodeblock, 1);
 	unlock_super (sb);
 }
 
@@ -263,7 +263,7 @@
 		(sb->u.ext_sb.s_firstfreeinodenumber-1)%EXT_INODES_PER_BLOCK;
 	if (efi->count) {
 		j = efi->free[--efi->count];
-		dirtify_buffer(sb->u.ext_sb.s_firstfreeinodeblock, 1);
+		mark_buffer_dirty(sb->u.ext_sb.s_firstfreeinodeblock, 1);
 	} else {
 #ifdef EXTFS_DEBUG
 printk("ext_free_inode: inode empty, skipping to %d\n", efi->next);
diff -u --recursive --new-file v1.1.2/linux/fs/ext/inode.c linux/fs/ext/inode.c
--- v1.1.2/linux/fs/ext/inode.c	Wed Apr 13 11:33:53 1994
+++ linux/fs/ext/inode.c	Wed Apr 13 11:16:28 1994
@@ -139,7 +139,7 @@
 	es->s_freeblockscount = sb->u.ext_sb.s_freeblockscount;
 	es->s_firstfreeinode = sb->u.ext_sb.s_firstfreeinodenumber;
 	es->s_freeinodescount = sb->u.ext_sb.s_freeinodescount;
-	dirtify_buffer(bh, 1);
+	mark_buffer_dirty(bh, 1);
 	brelse (bh);
 	sb->s_dirt = 0;
 }
@@ -296,7 +296,7 @@
 		goto repeat;
 	}
 	*p = tmp;
-	dirtify_buffer(bh, 1);
+	mark_buffer_dirty(bh, 1);
 	brelse(bh);
 	return result;
 }
@@ -407,7 +407,7 @@
 		raw_inode->i_zone[0] = inode->i_rdev;
 	else for (block = 0; block < 12; block++)
 		raw_inode->i_zone[block] = inode->u.ext_i.i_data[block];
-	dirtify_buffer(bh, 1);
+	mark_buffer_dirty(bh, 1);
 	inode->i_dirt=0;
 	return bh;
 }
diff -u --recursive --new-file v1.1.2/linux/fs/ext/namei.c linux/fs/ext/namei.c
--- v1.1.2/linux/fs/ext/namei.c	Wed Apr 13 11:33:54 1994
+++ linux/fs/ext/namei.c	Wed Apr 13 11:16:28 1994
@@ -249,7 +249,7 @@
 #if 0
 					dir->i_ctime = CURRENT_TIME;
 #endif
-					dirtify_buffer(bh, 1);
+					mark_buffer_dirty(bh, 1);
 				}
 				brelse (bh);
 				bh = NULL;
@@ -296,7 +296,7 @@
 			de->name_len = namelen;
 			for (i=0; i < namelen ; i++)
 				de->name[i] = name[i];
-			dirtify_buffer(bh, 1);
+			mark_buffer_dirty(bh, 1);
 			*res_dir = de;
 			return bh;
 		}
@@ -334,7 +334,7 @@
 		return -ENOSPC;
 	}
 	de->inode = inode->i_ino;
-	dirtify_buffer(bh, 1);
+	mark_buffer_dirty(bh, 1);
 	brelse(bh);
 	iput(dir);
 	*result = inode;
@@ -393,7 +393,7 @@
 		return -ENOSPC;
 	}
 	de->inode = inode->i_ino;
-	dirtify_buffer(bh, 1);
+	mark_buffer_dirty(bh, 1);
 	brelse(bh);
 	iput(dir);
 	iput(inode);
@@ -445,7 +445,7 @@
 	de->name_len=2;
 	strcpy(de->name,"..");
 	inode->i_nlink = 2;
-	dirtify_buffer(dir_block, 1);
+	mark_buffer_dirty(dir_block, 1);
 	brelse(dir_block);
 	inode->i_mode = S_IFDIR | (mode & 0777 & ~current->umask);
 	if (dir->i_mode & S_ISGID)
@@ -459,7 +459,7 @@
 		return -ENOSPC;
 	}
 	de->inode = inode->i_ino;
-	dirtify_buffer(bh, 1);
+	mark_buffer_dirty(bh, 1);
 	dir->i_nlink++;
 	dir->i_dirt = 1;
 	iput(dir);
@@ -567,7 +567,7 @@
 	de->inode = 0;
 	de->name_len = 0;
 	ext_merge_entries (de, pde, nde);
-	dirtify_buffer(bh, 1);
+	mark_buffer_dirty(bh, 1);
 	inode->i_nlink=0;
 	inode->i_dirt=1;
 	dir->i_nlink--;
@@ -610,7 +610,7 @@
 	de->inode = 0;
 	de->name_len = 0;
 	ext_merge_entries (de, pde, nde);
-	dirtify_buffer(bh, 1);
+	mark_buffer_dirty(bh, 1);
 	inode->i_nlink--;
 	inode->i_dirt = 1;
 	inode->i_ctime = CURRENT_TIME;
@@ -650,7 +650,7 @@
 	while (i < 1023 && (c = *(symname++)))
 		name_block->b_data[i++] = c;
 	name_block->b_data[i] = 0;
-	dirtify_buffer(name_block, 1);
+	mark_buffer_dirty(name_block, 1);
 	brelse(name_block);
 	inode->i_size = i;
 	inode->i_dirt = 1;
@@ -672,7 +672,7 @@
 		return -ENOSPC;
 	}
 	de->inode = inode->i_ino;
-	dirtify_buffer(bh, 1);
+	mark_buffer_dirty(bh, 1);
 	brelse(bh);
 	iput(dir);
 	iput(inode);
@@ -708,7 +708,7 @@
 		return -ENOSPC;
 	}
 	de->inode = oldinode->i_ino;
-	dirtify_buffer(bh, 1);
+	mark_buffer_dirty(bh, 1);
 	brelse(bh);
 	iput(dir);
 	oldinode->i_nlink++;
@@ -851,11 +851,11 @@
 		new_inode->i_nlink--;
 		new_inode->i_dirt = 1;
 	}
-	dirtify_buffer(old_bh, 1);
-	dirtify_buffer(new_bh, 1);
+	mark_buffer_dirty(old_bh, 1);
+	mark_buffer_dirty(new_bh, 1);
 	if (dir_bh) {
 		PARENT_INO(dir_bh->b_data) = new_dir->i_ino;
-		dirtify_buffer(dir_bh, 1);
+		mark_buffer_dirty(dir_bh, 1);
 		old_dir->i_nlink--;
 		new_dir->i_nlink++;
 		old_dir->i_dirt = 1;
diff -u --recursive --new-file v1.1.2/linux/fs/ext/truncate.c linux/fs/ext/truncate.c
--- v1.1.2/linux/fs/ext/truncate.c	Wed Apr 13 11:33:54 1994
+++ linux/fs/ext/truncate.c	Wed Apr 13 11:16:28 1994
@@ -102,7 +102,7 @@
 			continue;
 		}
 		*ind = 0;
-		dirtify_buffer(ind_bh, 1);
+		mark_buffer_dirty(ind_bh, 1);
 		brelse(bh);
 		ext_free_block(inode->i_sb,tmp);
 	}
@@ -154,7 +154,7 @@
 		if (!tmp)
 			continue;
 		retry |= trunc_indirect(inode,offset+(i<<8),dind);
-		dirtify_buffer(dind_bh, 1);
+		mark_buffer_dirty(dind_bh, 1);
 	}
 	dind = (unsigned long *) dind_bh->b_data;
 	for (i = 0; i < 256; i++)
@@ -201,7 +201,7 @@
 			goto repeat;
 		tind = i+(unsigned long *) tind_bh->b_data;
 		retry |= trunc_dindirect(inode,9+256+256*256+(i<<16),tind);
-		dirtify_buffer(tind_bh, 1);
+		mark_buffer_dirty(tind_bh, 1);
 	}
 	tind = (unsigned long *) tind_bh->b_data;
 	for (i = 0; i < 256; i++)
diff -u --recursive --new-file v1.1.2/linux/fs/ext2/balloc.c linux/fs/ext2/balloc.c
--- v1.1.2/linux/fs/ext2/balloc.c	Wed Apr 13 11:33:54 1994
+++ linux/fs/ext2/balloc.c	Wed Apr 13 11:16:28 1994
@@ -310,10 +310,10 @@
 		}
 	}
 	
-	dirtify_buffer(bh2, 1);
-	dirtify_buffer(sb->u.ext2_sb.s_sbh, 1);
+	mark_buffer_dirty(bh2, 1);
+	mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1);
 
-	dirtify_buffer(bh, 1);
+	mark_buffer_dirty(bh, 1);
 	if (sb->s_flags & MS_SYNC) {
 		ll_rw_block (WRITE, 1, &bh);
 		wait_on_buffer (bh);
@@ -524,7 +524,7 @@
 
 	j = tmp;
 
-	dirtify_buffer(bh, 1);
+	mark_buffer_dirty(bh, 1);
 	if (sb->s_flags & MS_SYNC) {
 		ll_rw_block (WRITE, 1, &bh);
 		wait_on_buffer (bh);
@@ -544,16 +544,16 @@
 	}
 	clear_block (bh->b_data, sb->s_blocksize);
 	bh->b_uptodate = 1;
-	dirtify_buffer(bh, 1);
+	mark_buffer_dirty(bh, 1);
 	brelse (bh);
 
 	ext2_debug ("allocating block %d. "
 		    "Goal hits %d of %d.\n", j, goal_hits, goal_attempts);
 
 	gdp->bg_free_blocks_count--;
-	dirtify_buffer(bh2, 1);
+	mark_buffer_dirty(bh2, 1);
 	es->s_free_blocks_count--;
-	dirtify_buffer(sb->u.ext2_sb.s_sbh, 1);
+	mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1);
 	sb->s_dirt = 1;
 	unlock_super (sb);
 	return j;
diff -u --recursive --new-file v1.1.2/linux/fs/ext2/file.c linux/fs/ext2/file.c
--- v1.1.2/linux/fs/ext2/file.c	Wed Apr 13 11:33:54 1994
+++ linux/fs/ext2/file.c	Wed Apr 13 11:17:48 1994
@@ -78,6 +78,7 @@
 	int read, left, chars;
 	int block, blocks, offset;
 	int bhrequest, uptodate;
+	int clusterblocks;
 	struct buffer_head ** bhb, ** bhe;
 	struct buffer_head * bhreq[NBUF];
 	struct buffer_head * buflist[NBUF];
@@ -130,11 +131,18 @@
 	 * buffers and caches.
 	 */
 
+	clusterblocks = 0;
+
 	do {
 		bhrequest = 0;
 		uptodate = 1;
 		while (blocks) {
 			--blocks;
+#if 1
+			if(!clusterblocks) clusterblocks = ext2_getcluster(inode, block);
+			if(clusterblocks) clusterblocks--;
+#endif
+
 			*bhb = ext2_getblk (inode, block++, 0, &err);
 			if (*bhb && !(*bhb)->b_uptodate) {
 				uptodate = 0;
@@ -277,7 +285,7 @@
 		memcpy_fromfs (p, buf, c);
 		buf += c;
 		bh->b_uptodate = 1;
-		dirtify_buffer(bh, 0);
+		mark_buffer_dirty(bh, 0);
 		brelse (bh);
 	}
 	up(&inode->i_sem);
diff -u --recursive --new-file v1.1.2/linux/fs/ext2/ialloc.c linux/fs/ext2/ialloc.c
--- v1.1.2/linux/fs/ext2/ialloc.c	Wed Apr 13 11:33:54 1994
+++ linux/fs/ext2/ialloc.c	Wed Apr 13 11:16:29 1994
@@ -202,7 +202,7 @@
 			EXT2_INODES_PER_BLOCK(inode->i_sb));
 	raw_inode->i_links_count = 0;
 	raw_inode->i_dtime = CURRENT_TIME;
-	dirtify_buffer(bh, 1);
+	mark_buffer_dirty(bh, 1);
 	if (IS_SYNC(inode)) {
 		ll_rw_block (WRITE, 1, &bh);
 		wait_on_buffer (bh);
@@ -266,12 +266,12 @@
 		gdp->bg_free_inodes_count++;
 		if (S_ISDIR(inode->i_mode))
 			gdp->bg_used_dirs_count--;
-		dirtify_buffer(bh2, 1);
+		mark_buffer_dirty(bh2, 1);
 		es->s_free_inodes_count++;
-		dirtify_buffer(sb->u.ext2_sb.s_sbh, 1);
+		mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1);
 		set_inode_dtime (inode, gdp);
 	}
-	dirtify_buffer(bh, 1);
+	mark_buffer_dirty(bh, 1);
 	if (sb->s_flags & MS_SYNC) {
 		ll_rw_block (WRITE, 1, &bh);
 		wait_on_buffer (bh);
@@ -313,7 +313,7 @@
 			EXT2_INODES_PER_BLOCK(inode->i_sb));
 	raw_inode->i_version++;
 	inode->u.ext2_i.i_version = raw_inode->i_version;
-	dirtify_buffer(bh, 1);
+	mark_buffer_dirty(bh, 1);
 	brelse (bh);
 }
 
@@ -438,7 +438,7 @@
 				      "bit already set for inode %d", j);
 			goto repeat;
 		}
-		dirtify_buffer(bh, 1);
+		mark_buffer_dirty(bh, 1);
 		if (sb->s_flags & MS_SYNC) {
 			ll_rw_block (WRITE, 1, &bh);
 			wait_on_buffer (bh);
@@ -466,9 +466,9 @@
 	gdp->bg_free_inodes_count--;
 	if (S_ISDIR(mode))
 		gdp->bg_used_dirs_count++;
-	dirtify_buffer(bh2, 1);
+	mark_buffer_dirty(bh2, 1);
 	es->s_free_inodes_count--;
-	dirtify_buffer(sb->u.ext2_sb.s_sbh, 1);
+	mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1);
 	sb->s_dirt = 1;
 	inode->i_mode = mode;
 	inode->i_sb = sb;
diff -u --recursive --new-file v1.1.2/linux/fs/ext2/inode.c linux/fs/ext2/inode.c
--- v1.1.2/linux/fs/ext2/inode.c	Wed Apr 13 11:33:54 1994
+++ linux/fs/ext2/inode.c	Wed Apr 13 11:17:49 1994
@@ -109,7 +109,7 @@
 		}
 		clear_block (bh->b_data, inode->i_sb->s_blocksize);
 		bh->b_uptodate = 1;
-		dirtify_buffer(bh, 1);
+		mark_buffer_dirty(bh, 1);
 		brelse (bh);
 	} else {
 		ext2_discard_prealloc (inode);
@@ -314,7 +314,7 @@
 		goto repeat;
 	}
 	*p = tmp;
-	dirtify_buffer(bh, 1);
+	mark_buffer_dirty(bh, 1);
 	if (IS_SYNC(inode)) {
 		ll_rw_block (WRITE, 1, &bh);
 		wait_on_buffer (bh);
@@ -328,6 +328,41 @@
 	return result;
 }
 
+static int block_getcluster (struct inode * inode, struct buffer_head * bh,
+					  int nr,
+					  int blocksize)
+{
+	unsigned long * p;
+	int firstblock = 0;
+	int result = 0;
+	int i;
+
+	/* Check to see if clustering possible here. */
+
+	if(!bh) return 0;
+
+	if(nr % (PAGE_SIZE / inode->i_sb->s_blocksize) != 0) goto out;
+	if(nr + 3 > EXT2_ADDR_PER_BLOCK(inode->i_sb)) goto out;
+
+	for(i=0; i< (PAGE_SIZE / inode->i_sb->s_blocksize); i++) {
+	  p = (unsigned long *) bh->b_data + nr + i;
+	  
+	  /* All blocks in cluster must already be allocated */
+	  if(*p == 0) goto out;
+	  
+	  /* See if aligned correctly */
+	  if(i==0) firstblock = *p;
+	  else if(*p != firstblock + i) goto out;
+	};
+	
+	p = (unsigned long *) bh->b_data + nr;
+	result = generate_cluster(bh->b_dev, (int *) p, blocksize);
+
+      out:
+	brelse(bh);
+	return result;
+}
+
 struct buffer_head * ext2_getblk (struct inode * inode, long block,
 				  int create, int * err)
 {
@@ -389,6 +424,55 @@
 			     inode->i_sb->s_blocksize, b, err);
 }
 
+int ext2_getcluster (struct inode * inode, long block)
+{
+	struct buffer_head * bh;
+	int err, create;
+	unsigned long b;
+	unsigned long addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb);
+
+	create = 0;
+	err = -EIO;
+	if (block < 0) {
+		ext2_warning (inode->i_sb, "ext2_getblk", "block < 0");
+		return 0;
+	}
+	if (block > EXT2_NDIR_BLOCKS + addr_per_block  +
+		    addr_per_block * addr_per_block +
+		    addr_per_block * addr_per_block * addr_per_block) {
+		ext2_warning (inode->i_sb, "ext2_getblk", "block > big");
+		return 0;
+	}
+
+	err = -ENOSPC;
+	b = block;
+	if (block < EXT2_NDIR_BLOCKS) return 0;
+
+	block -= EXT2_NDIR_BLOCKS;
+
+	if (block < addr_per_block) {
+		bh = inode_getblk (inode, EXT2_IND_BLOCK, create, b, &err);
+		return block_getcluster (inode, bh, block, 
+					 inode->i_sb->s_blocksize);
+	}
+	block -= addr_per_block;
+	if (block < addr_per_block * addr_per_block) {
+		bh = inode_getblk (inode, EXT2_DIND_BLOCK, create, b, &err);
+		bh = block_getblk (inode, bh, block / addr_per_block, create,
+				   inode->i_sb->s_blocksize, b, &err);
+		return block_getcluster (inode, bh, block & (addr_per_block - 1),
+				     inode->i_sb->s_blocksize);
+	}
+	block -= addr_per_block * addr_per_block;
+	bh = inode_getblk (inode, EXT2_TIND_BLOCK, create, b, &err);
+	bh = block_getblk (inode, bh, block/(addr_per_block * addr_per_block),
+			   create, inode->i_sb->s_blocksize, b, &err);
+	bh = block_getblk (inode, bh, (block/addr_per_block) & (addr_per_block - 1),
+			   create, inode->i_sb->s_blocksize, b, &err);
+	return block_getcluster (inode, bh, block & (addr_per_block - 1),
+				 inode->i_sb->s_blocksize);
+}
+
 struct buffer_head * ext2_bread (struct inode * inode, int block, 
 				 int create, int *err)
 {
@@ -549,7 +633,7 @@
 		raw_inode->i_block[0] = inode->i_rdev;
 	else for (block = 0; block < EXT2_N_BLOCKS; block++)
 		raw_inode->i_block[block] = inode->u.ext2_i.i_data[block];
-	dirtify_buffer(bh, 1);
+	mark_buffer_dirty(bh, 1);
 	inode->i_dirt = 0;
 	return bh;
 }
diff -u --recursive --new-file v1.1.2/linux/fs/ext2/namei.c linux/fs/ext2/namei.c
--- v1.1.2/linux/fs/ext2/namei.c	Wed Apr 13 11:33:54 1994
+++ linux/fs/ext2/namei.c	Wed Apr 13 11:16:29 1994
@@ -320,7 +320,7 @@
 			 */
 			dir->i_mtime = dir->i_ctime = CURRENT_TIME;
 			dir->i_dirt = 1;
-			dirtify_buffer(bh, 1);
+			mark_buffer_dirty(bh, 1);
 			*res_dir = de;
 			*err = 0;
 			return bh;
@@ -394,7 +394,7 @@
 	ext2_dcache_add (dir->i_dev, dir->i_ino, de->name, de->name_len,
 			 de->inode);
 #endif
-	dirtify_buffer(bh, 1);
+	mark_buffer_dirty(bh, 1);
 	if (IS_SYNC(dir)) {
 		ll_rw_block (WRITE, 1, &bh);
 		wait_on_buffer (bh);
@@ -460,7 +460,7 @@
 	ext2_dcache_add (dir->i_dev, dir->i_ino, de->name, de->name_len,
 			 de->inode);
 #endif
-	dirtify_buffer(bh, 1);
+	mark_buffer_dirty(bh, 1);
 	if (IS_SYNC(dir)) {
 		ll_rw_block (WRITE, 1, &bh);
 		wait_on_buffer (bh);
@@ -517,7 +517,7 @@
 	de->name_len = 2;
 	strcpy (de->name, "..");
 	inode->i_nlink = 2;
-	dirtify_buffer(dir_block, 1);
+	mark_buffer_dirty(dir_block, 1);
 	brelse (dir_block);
 	inode->i_mode = S_IFDIR | (mode & S_IRWXUGO & ~current->umask);
 	if (dir->i_mode & S_ISGID)
@@ -536,7 +536,7 @@
 	ext2_dcache_add (dir->i_dev, dir->i_ino, de->name, de->name_len,
 			 de->inode);
 #endif
-	dirtify_buffer(bh, 1);
+	mark_buffer_dirty(bh, 1);
 	if (IS_SYNC(dir)) {
 		ll_rw_block (WRITE, 1, &bh);
 		wait_on_buffer (bh);
@@ -660,7 +660,7 @@
 	up(&inode->i_sem);
 	if (retval)
 		goto end_rmdir;
-	dirtify_buffer(bh, 1);
+	mark_buffer_dirty(bh, 1);
 	if (IS_SYNC(dir)) {
 		ll_rw_block (WRITE, 1, &bh);
 		wait_on_buffer (bh);
@@ -728,7 +728,7 @@
 	retval = ext2_delete_entry (de, bh);
 	if (retval)
 		goto end_unlink;
-	dirtify_buffer(bh, 1);
+	mark_buffer_dirty(bh, 1);
 	if (IS_SYNC(dir)) {
 		ll_rw_block (WRITE, 1, &bh);
 		wait_on_buffer (bh);
@@ -793,7 +793,7 @@
 		link[i++] = c;
 	link[i] = 0;
 	if (name_block) {
-		dirtify_buffer(name_block, 1);
+		mark_buffer_dirty(name_block, 1);
 		brelse (name_block);
 	}
 	inode->i_size = i;
@@ -820,7 +820,7 @@
 	ext2_dcache_add (dir->i_dev, dir->i_ino, de->name, de->name_len,
 			 de->inode);
 #endif
-	dirtify_buffer(bh, 1);
+	mark_buffer_dirty(bh, 1);
 	if (IS_SYNC(dir)) {
 		ll_rw_block (WRITE, 1, &bh);
 		wait_on_buffer (bh);
@@ -866,7 +866,7 @@
 	ext2_dcache_add (dir->i_dev, dir->i_ino, de->name, de->name_len,
 			 de->inode);
 #endif
-	dirtify_buffer(bh, 1);
+	mark_buffer_dirty(bh, 1);
 	if (IS_SYNC(dir)) {
 		ll_rw_block (WRITE, 1, &bh);
 		wait_on_buffer (bh);
@@ -1042,19 +1042,19 @@
 	}
 	old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME;
 	old_dir->i_dirt = 1;
-	dirtify_buffer(old_bh,  1);
+	mark_buffer_dirty(old_bh,  1);
 	if (IS_SYNC(old_dir)) {
 		ll_rw_block (WRITE, 1, &old_bh);
 		wait_on_buffer (old_bh);
 	}
-	dirtify_buffer(new_bh, 1);
+	mark_buffer_dirty(new_bh, 1);
 	if (IS_SYNC(new_dir)) {
 		ll_rw_block (WRITE, 1, &new_bh);
 		wait_on_buffer (new_bh);
 	}
 	if (dir_bh) {
 		PARENT_INO(dir_bh->b_data) = new_dir->i_ino;
-		dirtify_buffer(dir_bh, 1);
+		mark_buffer_dirty(dir_bh, 1);
 		old_dir->i_nlink--;
 		old_dir->i_dirt = 1;
 		if (new_inode) {
diff -u --recursive --new-file v1.1.2/linux/fs/ext2/super.c linux/fs/ext2/super.c
--- v1.1.2/linux/fs/ext2/super.c	Wed Apr 13 11:33:55 1994
+++ linux/fs/ext2/super.c	Wed Apr 13 11:16:29 1994
@@ -36,7 +36,7 @@
 	if (!(sb->s_flags & MS_RDONLY)) {
 		sb->u.ext2_sb.s_mount_state |= EXT2_ERROR_FS;
 		sb->u.ext2_sb.s_es->s_state |= EXT2_ERROR_FS;
-		dirtify_buffer(sb->u.ext2_sb.s_sbh, 1);
+		mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1);
 		sb->s_dirt = 1;
 	}
 	va_start (args, fmt);
@@ -66,7 +66,7 @@
 	if (!(sb->s_flags & MS_RDONLY)) {
 		sb->u.ext2_sb.s_mount_state |= EXT2_ERROR_FS;
 		sb->u.ext2_sb.s_es->s_state |= EXT2_ERROR_FS;
-		dirtify_buffer(sb->u.ext2_sb.s_sbh, 1);
+		mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1);
 		sb->s_dirt = 1;
 	}
 	va_start (args, fmt);
@@ -96,7 +96,7 @@
 	lock_super (sb);
 	if (!(sb->s_flags & MS_RDONLY)) {
 		sb->u.ext2_sb.s_es->s_state = sb->u.ext2_sb.s_mount_state;
-		dirtify_buffer(sb->u.ext2_sb.s_sbh, 1);
+		mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1);
 	}
 #ifndef DONT_USE_DCACHE
 	ext2_dcache_invalidate (sb->s_dev);
@@ -159,10 +159,10 @@
 		gdp[i].bg_free_blocks_count = old_group_desc[i].bg_free_blocks_count;
 		gdp[i].bg_free_inodes_count = old_group_desc[i].bg_free_inodes_count;
 	}
-	dirtify_buffer(bh2, 1);
+	mark_buffer_dirty(bh2, 1);
 	brelse (bh2);
 	es->s_magic = EXT2_SUPER_MAGIC;
-	dirtify_buffer(bh, 1);
+	mark_buffer_dirty(bh, 1);
 	sb->s_magic = EXT2_SUPER_MAGIC;
 	return 1;
 }
@@ -287,7 +287,7 @@
 			es->s_max_mnt_count = EXT2_DFL_MAX_MNT_COUNT;
 		es->s_mnt_count++;
 		es->s_mtime = CURRENT_TIME;
-		dirtify_buffer(sb->u.ext2_sb.s_sbh, 1);
+		mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1);
 		sb->s_dirt = 1;
 		if (test_opt (sb, DEBUG))
 			printk ("[EXT II FS %s, %s, bs=%lu, fs=%lu, gc=%lu, "
@@ -570,7 +570,7 @@
 #ifdef EXT2FS_PRE_02B_COMPAT
 	if (fs_converted) {
 		for (i = 0; i < bh_count; i++)
-			dirtify_buffer(sb->u.ext2_sb.s_group_desc[i], 1);
+			mark_buffer_dirty(sb->u.ext2_sb.s_group_desc[i], 1);
 		sb->s_dirt = 1;
 	}
 #endif
@@ -582,7 +582,7 @@
 			       struct ext2_super_block * es)
 {
 	es->s_wtime = CURRENT_TIME;
-	dirtify_buffer(sb->u.ext2_sb.s_sbh, 1);
+	mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1);
 	sb->s_dirt = 0;
 }
 
@@ -639,7 +639,7 @@
 		 */
 		es->s_state = sb->u.ext2_sb.s_mount_state;
 		es->s_mtime = CURRENT_TIME;
-		dirtify_buffer(sb->u.ext2_sb.s_sbh, 1);
+		mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1);
 		sb->s_dirt = 1;
 		ext2_commit_super (sb, es);
 	}
diff -u --recursive --new-file v1.1.2/linux/fs/ext2/truncate.c linux/fs/ext2/truncate.c
--- v1.1.2/linux/fs/ext2/truncate.c	Wed Apr 13 11:33:55 1994
+++ linux/fs/ext2/truncate.c	Wed Apr 13 11:16:29 1994
@@ -90,7 +90,7 @@
 		if (inode->u.ext2_i.i_flags & EXT2_SECRM_FL) {
 			clear_block (bh->b_data, inode->i_sb->s_blocksize,
 				     RANDOM_INT);
-			dirtify_buffer(bh, 1);
+			mark_buffer_dirty(bh, 1);
 		}
 		brelse (bh);
 		if (free_count == 0) {
@@ -162,11 +162,11 @@
 			continue;
 		}
 		*ind = 0;
-		dirtify_buffer(ind_bh, 1);
+		mark_buffer_dirty(ind_bh, 1);
 		if (inode->u.ext2_i.i_flags & EXT2_SECRM_FL) {
 			clear_block (bh->b_data, inode->i_sb->s_blocksize,
 				     RANDOM_INT);
-			dirtify_buffer(bh, 1);
+			mark_buffer_dirty(bh, 1);
 		}
 		brelse (bh);
 		if (free_count == 0) {
@@ -243,7 +243,7 @@
 			continue;
 		retry |= trunc_indirect (inode, offset + (i * addr_per_block),
 					  dind);
-		dirtify_buffer(dind_bh, 1);
+		mark_buffer_dirty(dind_bh, 1);
 	}
 	dind = (unsigned long *) dind_bh->b_data;
 	for (i = 0; i < addr_per_block; i++)
@@ -302,7 +302,7 @@
 		retry |= trunc_dindirect(inode, EXT2_NDIR_BLOCKS +
 			addr_per_block + (i + 1) * addr_per_block * addr_per_block,
 			tind);
-		dirtify_buffer(tind_bh, 1);
+		mark_buffer_dirty(tind_bh, 1);
 	}
 	tind = (unsigned long *) tind_bh->b_data;
 	for (i = 0; i < addr_per_block; i++)
diff -u --recursive --new-file v1.1.2/linux/fs/minix/bitmap.c linux/fs/minix/bitmap.c
--- v1.1.2/linux/fs/minix/bitmap.c	Wed Apr 13 11:33:56 1994
+++ linux/fs/minix/bitmap.c	Wed Apr 13 11:16:30 1994
@@ -93,7 +93,7 @@
 	}
 	if (!clear_bit(bit,bh->b_data))
 		printk("free_block (%04x:%d): bit already cleared\n",sb->s_dev,block);
-	dirtify_buffer(bh, 1);
+	mark_buffer_dirty(bh, 1);
 	return;
 }
 
@@ -118,7 +118,7 @@
 		printk("new_block: bit already set");
 		goto repeat;
 	}
-	dirtify_buffer(bh, 1);
+	mark_buffer_dirty(bh, 1);
 	j += i*8192 + sb->u.minix_sb.s_firstdatazone-1;
 	if (j < sb->u.minix_sb.s_firstdatazone ||
 	    j >= sb->u.minix_sb.s_nzones)
@@ -129,7 +129,7 @@
 	}
 	clear_block(bh->b_data);
 	bh->b_uptodate = 1;
-	dirtify_buffer(bh, 1);
+	mark_buffer_dirty(bh, 1);
 	brelse(bh);
 	return j;
 }
@@ -175,7 +175,7 @@
 	clear_inode(inode);
 	if (!clear_bit(ino & 8191, bh->b_data))
 		printk("free_inode: bit %lu already cleared.\n",ino);
-	dirtify_buffer(bh, 1);
+	mark_buffer_dirty(bh, 1);
 }
 
 struct inode * minix_new_inode(const struct inode * dir)
@@ -204,7 +204,7 @@
 		iput(inode);
 		return NULL;
 	}
-	dirtify_buffer(bh, 1);
+	mark_buffer_dirty(bh, 1);
 	j += i*8192;
 	if (!j || j >= inode->i_sb->u.minix_sb.s_ninodes) {
 		iput(inode);
diff -u --recursive --new-file v1.1.2/linux/fs/minix/file.c linux/fs/minix/file.c
--- v1.1.2/linux/fs/minix/file.c	Wed Apr 13 11:33:56 1994
+++ linux/fs/minix/file.c	Wed Apr 13 11:16:30 1994
@@ -240,7 +240,7 @@
 		memcpy_fromfs(p,buf,c);
 		buf += c;
 		bh->b_uptodate = 1;
-		dirtify_buffer(bh, 0);
+		mark_buffer_dirty(bh, 0);
 		brelse(bh);
 	}
 	inode->i_mtime = inode->i_ctime = CURRENT_TIME;
diff -u --recursive --new-file v1.1.2/linux/fs/minix/inode.c linux/fs/minix/inode.c
--- v1.1.2/linux/fs/minix/inode.c	Wed Apr 13 11:33:56 1994
+++ linux/fs/minix/inode.c	Wed Apr 13 11:16:30 1994
@@ -28,7 +28,7 @@
 static void minix_commit_super (struct super_block * sb,
 			       struct minix_super_block * ms)
 {
-	dirtify_buffer(sb->u.minix_sb.s_sbh, 1);
+	mark_buffer_dirty(sb->u.minix_sb.s_sbh, 1);
 	sb->s_dirt = 0;
 }
 
@@ -54,7 +54,7 @@
 	lock_super(sb);
 	if (!(sb->s_flags & MS_RDONLY)) {
 		sb->u.minix_sb.s_ms->s_state = sb->u.minix_sb.s_mount_state;
-		dirtify_buffer(sb->u.minix_sb.s_sbh, 1);
+		mark_buffer_dirty(sb->u.minix_sb.s_sbh, 1);
 	}
 	sb->s_dev = 0;
 	for(i = 0 ; i < MINIX_I_MAP_SLOTS ; i++)
@@ -90,7 +90,7 @@
 			return 0;
 		/* Mounting a rw partition read-only. */
 		ms->s_state = sb->u.minix_sb.s_mount_state;
-		dirtify_buffer(sb->u.minix_sb.s_sbh, 1);
+		mark_buffer_dirty(sb->u.minix_sb.s_sbh, 1);
 		sb->s_dirt = 1;
 		minix_commit_super (sb, ms);
 	}
@@ -98,7 +98,7 @@
 	  	/* Mount a partition which is read-only, read-write. */
 		sb->u.minix_sb.s_mount_state = ms->s_state;
 		ms->s_state &= ~MINIX_VALID_FS;
-		dirtify_buffer(sb->u.minix_sb.s_sbh, 1);
+		mark_buffer_dirty(sb->u.minix_sb.s_sbh, 1);
 		sb->s_dirt = 1;
 
 		if (!(sb->u.minix_sb.s_mount_state & MINIX_VALID_FS))
@@ -197,7 +197,7 @@
 	}
 	if (!(s->s_flags & MS_RDONLY)) {
 		ms->s_state &= ~MINIX_VALID_FS;
-		dirtify_buffer(bh, 1);
+		mark_buffer_dirty(bh, 1);
 		s->s_dirt = 1;
 	}
 	if (!(s->u.minix_sb.s_mount_state & MINIX_VALID_FS))
@@ -349,7 +349,7 @@
 		goto repeat;
 	}
 	*p = tmp;
-	dirtify_buffer(bh, 1);
+	mark_buffer_dirty(bh, 1);
 	brelse(bh);
 	return result;
 }
@@ -477,7 +477,7 @@
 	else for (block = 0; block < 9; block++)
 		raw_inode->i_zone[block] = inode->u.minix_i.i_data[block];
 	inode->i_dirt=0;
-	dirtify_buffer(bh, 1);
+	mark_buffer_dirty(bh, 1);
 	return bh;
 }
 
diff -u --recursive --new-file v1.1.2/linux/fs/minix/namei.c linux/fs/minix/namei.c
--- v1.1.2/linux/fs/minix/namei.c	Wed Apr 13 11:33:56 1994
+++ linux/fs/minix/namei.c	Wed Apr 13 11:16:30 1994
@@ -195,7 +195,7 @@
 			dir->i_mtime = dir->i_ctime = CURRENT_TIME;
 			for (i = 0; i < info->s_namelen ; i++)
 				de->name[i] = (i < namelen) ? name[i] : 0;
-			dirtify_buffer(bh, 1);
+			mark_buffer_dirty(bh, 1);
 			*res_dir = de;
 			break;
 		}
@@ -238,7 +238,7 @@
 		return error;
 	}
 	de->inode = inode->i_ino;
-	dirtify_buffer(bh, 1);
+	mark_buffer_dirty(bh, 1);
 	brelse(bh);
 	iput(dir);
 	*result = inode;
@@ -295,7 +295,7 @@
 		return error;
 	}
 	de->inode = inode->i_ino;
-	dirtify_buffer(bh, 1);
+	mark_buffer_dirty(bh, 1);
 	brelse(bh);
 	iput(dir);
 	iput(inode);
@@ -347,7 +347,7 @@
 	de->inode = dir->i_ino;
 	strcpy(de->name,"..");
 	inode->i_nlink = 2;
-	dirtify_buffer(dir_block, 1);
+	mark_buffer_dirty(dir_block, 1);
 	brelse(dir_block);
 	inode->i_mode = S_IFDIR | (mode & 0777 & ~current->umask);
 	if (dir->i_mode & S_ISGID)
@@ -361,7 +361,7 @@
 		return error;
 	}
 	de->inode = inode->i_ino;
-	dirtify_buffer(bh, 1);
+	mark_buffer_dirty(bh, 1);
 	dir->i_nlink++;
 	dir->i_dirt = 1;
 	iput(dir);
@@ -469,7 +469,7 @@
 	if (inode->i_nlink != 2)
 		printk("empty directory has nlink!=2 (%d)\n",inode->i_nlink);
 	de->inode = 0;
-	dirtify_buffer(bh, 1);
+	mark_buffer_dirty(bh, 1);
 	inode->i_nlink=0;
 	inode->i_dirt=1;
 	dir->i_nlink--;
@@ -522,7 +522,7 @@
 		inode->i_nlink=1;
 	}
 	de->inode = 0;
-	dirtify_buffer(bh, 1);
+	mark_buffer_dirty(bh, 1);
 	dir->i_ctime = dir->i_mtime = CURRENT_TIME;
 	dir->i_dirt = 1;
 	inode->i_nlink--;
@@ -562,7 +562,7 @@
 	while (i < 1023 && (c=*(symname++)))
 		name_block->b_data[i++] = c;
 	name_block->b_data[i] = 0;
-	dirtify_buffer(name_block, 1);
+	mark_buffer_dirty(name_block, 1);
 	brelse(name_block);
 	inode->i_size = i;
 	inode->i_dirt = 1;
@@ -584,7 +584,7 @@
 		return i;
 	}
 	de->inode = inode->i_ino;
-	dirtify_buffer(bh, 1);
+	mark_buffer_dirty(bh, 1);
 	brelse(bh);
 	iput(dir);
 	iput(inode);
@@ -621,7 +621,7 @@
 		return error;
 	}
 	de->inode = oldinode->i_ino;
-	dirtify_buffer(bh, 1);
+	mark_buffer_dirty(bh, 1);
 	brelse(bh);
 	iput(dir);
 	oldinode->i_nlink++;
@@ -774,11 +774,11 @@
 		new_inode->i_ctime = CURRENT_TIME;
 		new_inode->i_dirt = 1;
 	}
-	dirtify_buffer(old_bh, 1);
-	dirtify_buffer(new_bh, 1);
+	mark_buffer_dirty(old_bh, 1);
+	mark_buffer_dirty(new_bh, 1);
 	if (dir_bh) {
 		PARENT_INO(dir_bh->b_data) = new_dir->i_ino;
-		dirtify_buffer(dir_bh, 1);
+		mark_buffer_dirty(dir_bh, 1);
 		old_dir->i_nlink--;
 		old_dir->i_dirt = 1;
 		if (new_inode) {
diff -u --recursive --new-file v1.1.2/linux/fs/minix/truncate.c linux/fs/minix/truncate.c
--- v1.1.2/linux/fs/minix/truncate.c	Wed Apr 13 11:33:56 1994
+++ linux/fs/minix/truncate.c	Wed Apr 13 11:16:30 1994
@@ -96,7 +96,7 @@
 			continue;
 		}
 		*ind = 0;
-		dirtify_buffer(ind_bh, 1);
+		mark_buffer_dirty(ind_bh, 1);
 		brelse(bh);
 		minix_free_block(inode->i_sb,tmp);
 	}
@@ -144,7 +144,7 @@
 			goto repeat;
 		dind = i+(unsigned short *) dind_bh->b_data;
 		retry |= trunc_indirect(inode,7+512+(i<<9),dind);
-		dirtify_buffer(dind_bh, 1);
+		mark_buffer_dirty(dind_bh, 1);
 	}
 	dind = (unsigned short *) dind_bh->b_data;
 	for (i = 0; i < 512; i++)
diff -u --recursive --new-file v1.1.2/linux/fs/msdos/fat.c linux/fs/msdos/fat.c
--- v1.1.2/linux/fs/msdos/fat.c	Wed Apr 13 11:33:56 1994
+++ linux/fs/msdos/fat.c	Wed Apr 13 11:16:30 1994
@@ -72,15 +72,15 @@
 				*p_first = new_value & 0xff;
 				*p_last = (*p_last & 0xf0) | (new_value >> 8);
 			}
-			dirtify_buffer(bh2, 1);
+			mark_buffer_dirty(bh2, 1);
 		}
-		dirtify_buffer(bh, 1);
+		mark_buffer_dirty(bh, 1);
 		for (copy = 1; copy < MSDOS_SB(sb)->fats; copy++) {
 			if (!(c_bh = msdos_sread(sb->s_dev,MSDOS_SB(sb)->
 			    fat_start+(first >> SECTOR_BITS)+MSDOS_SB(sb)->
 			    fat_length*copy,&c_data))) break;
 			memcpy(c_data,data,SECTOR_SIZE);
-			dirtify_buffer(c_bh, 1);
+			mark_buffer_dirty(c_bh, 1);
 			if (data != data2 || bh != bh2) {
 				if (!(c_bh2 = msdos_sread(sb->s_dev,
 				    MSDOS_SB(sb)->fat_start+(first >>
diff -u --recursive --new-file v1.1.2/linux/fs/msdos/file.c linux/fs/msdos/file.c
--- v1.1.2/linux/fs/msdos/file.c	Wed Apr 13 11:33:56 1994
+++ linux/fs/msdos/file.c	Wed Apr 13 11:16:30 1994
@@ -200,7 +200,7 @@
 			inode->i_size = filp->f_pos;
 			inode->i_dirt = 1;
 		}
-		dirtify_buffer(bh, 0);
+		mark_buffer_dirty(bh, 0);
 		brelse(bh);
 	}
 	if (start == buf)
diff -u --recursive --new-file v1.1.2/linux/fs/msdos/inode.c linux/fs/msdos/inode.c
--- v1.1.2/linux/fs/msdos/inode.c	Wed Apr 13 11:33:56 1994
+++ linux/fs/msdos/inode.c	Wed Apr 13 11:16:31 1994
@@ -415,7 +415,7 @@
 	date_unix2dos(inode->i_mtime,&raw_entry->time,&raw_entry->date);
 	raw_entry->time = CT_LE_W(raw_entry->time);
 	raw_entry->date = CT_LE_W(raw_entry->date);
-	dirtify_buffer(bh, 1);
+	mark_buffer_dirty(bh, 1);
 	brelse(bh);
 }
 
diff -u --recursive --new-file v1.1.2/linux/fs/msdos/misc.c linux/fs/msdos/misc.c
--- v1.1.2/linux/fs/msdos/misc.c	Wed Apr 13 11:33:56 1994
+++ linux/fs/msdos/misc.c	Wed Apr 13 11:16:31 1994
@@ -185,7 +185,7 @@
 			else memset(data,0,SECTOR_SIZE);
 		}
 		if (bh) {
-			dirtify_buffer(bh, 1);
+			mark_buffer_dirty(bh, 1);
 			brelse(bh);
 		}
 	}
diff -u --recursive --new-file v1.1.2/linux/fs/msdos/namei.c linux/fs/msdos/namei.c
--- v1.1.2/linux/fs/msdos/namei.c	Wed Apr 13 11:33:57 1994
+++ linux/fs/msdos/namei.c	Wed Apr 13 11:16:31 1994
@@ -181,7 +181,7 @@
 	de->start = 0;
 	date_unix2dos(dir->i_mtime,&de->time,&de->date);
 	de->size = 0;
-	dirtify_buffer(bh, 1);
+	mark_buffer_dirty(bh, 1);
 	if ((*result = iget(dir->i_sb,ino)) != NULL)
 		msdos_read_inode(*result);
 	brelse(bh);
@@ -350,7 +350,7 @@
 	dir->i_nlink--;
 	inode->i_dirt = dir->i_dirt = 1;
 	de->name[0] = DELETED_FLAG;
-	dirtify_buffer(bh, 1);
+	mark_buffer_dirty(bh, 1);
 	res = 0;
 rmdir_done:
 	brelse(bh);
@@ -384,7 +384,7 @@
 	MSDOS_I(inode)->i_busy = 1;
 	inode->i_dirt = dir->i_dirt = 1;
 	de->name[0] = DELETED_FLAG;
-	dirtify_buffer(bh, 1);
+	mark_buffer_dirty(bh, 1);
 unlink_done:
 	brelse(bh);
 	iput(inode);
@@ -429,12 +429,12 @@
 		MSDOS_I(new_inode)->i_busy = 1;
 		new_inode->i_dirt = 1;
 		new_de->name[0] = DELETED_FLAG;
-		dirtify_buffer(new_bh, 1);
+		mark_buffer_dirty(new_bh, 1);
 		iput(new_inode);
 		brelse(new_bh);
 	}
 	memcpy(old_de->name,new_name,MSDOS_NAME);
-	dirtify_buffer(old_bh, 1);
+	mark_buffer_dirty(old_bh, 1);
 	if (MSDOS_SB(old_dir->i_sb)->conversion == 'a') /* update binary info */
 		if ((old_inode = iget(old_dir->i_sb,old_ino)) != NULL) {
 			msdos_read_inode(old_inode);
@@ -503,7 +503,7 @@
 		MSDOS_I(new_inode)->i_busy = 1;
 		new_inode->i_dirt = 1;
 		new_de->name[0] = DELETED_FLAG;
-		dirtify_buffer(new_bh, 1);
+		mark_buffer_dirty(new_bh, 1);
 	}
 	memcpy(free_de,old_de,sizeof(struct msdos_dir_entry));
 	memcpy(free_de->name,new_name,MSDOS_NAME);
@@ -526,8 +526,8 @@
 	cache_inval_inode(old_inode);
 	old_inode->i_dirt = 1;
 	old_de->name[0] = DELETED_FLAG;
-	dirtify_buffer(old_bh, 1);
-	dirtify_buffer(free_bh, 1);
+	mark_buffer_dirty(old_bh, 1);
+	mark_buffer_dirty(free_bh, 1);
 	if (!exists) iput(free_inode);
 	else {
 		MSDOS_I(new_inode)->i_depend = free_inode;
@@ -547,7 +547,7 @@
 		dotdot_de->start = MSDOS_I(dotdot_inode)->i_start =
 		    MSDOS_I(new_dir)->i_start;
 		dotdot_inode->i_dirt = 1;
-		dirtify_buffer(dotdot_bh, 1);
+		mark_buffer_dirty(dotdot_bh, 1);
 		old_dir->i_nlink--;
 		new_dir->i_nlink++;
 		/* no need to mark them dirty */
diff -u --recursive --new-file v1.1.2/linux/fs/proc/array.c linux/fs/proc/array.c
--- v1.1.2/linux/fs/proc/array.c	Thu Mar 31 12:56:13 1994
+++ linux/fs/proc/array.c	Wed Apr 13 08:48:46 1994
@@ -465,6 +465,7 @@
 }
 
 extern int get_module_list(char *);
+extern int get_device_list(char *);
 
 static int array_read(struct inode * inode, struct file * file,char * buf, int count)
 {
@@ -521,6 +522,9 @@
 			break;
 		case 17:
 			length = get_kstat(page);
+			break;
+		case 18:
+			length = get_device_list(page);
 			break;
 		default:
 			free_page((unsigned long) page);
diff -u --recursive --new-file v1.1.2/linux/fs/proc/root.c linux/fs/proc/root.c
--- v1.1.2/linux/fs/proc/root.c	Tue Feb  1 09:03:51 1994
+++ linux/fs/proc/root.c	Wed Apr 13 09:13:30 1994
@@ -67,6 +67,7 @@
 	{14,5,"kcore" },
    	{16,7,"modules" },
    	{17,4,"stat" },
+   	{18,7,"devices" },
 };
 
 #define NR_ROOT_DIRENTRY ((sizeof (root_dir))/(sizeof (root_dir[0])))
diff -u --recursive --new-file v1.1.2/linux/fs/sysv/balloc.c linux/fs/sysv/balloc.c
--- v1.1.2/linux/fs/sysv/balloc.c	Wed Apr 13 11:33:57 1994
+++ linux/fs/sysv/balloc.c	Wed Apr 13 11:16:31 1994
@@ -96,7 +96,7 @@
 		}
 		*flc_count = *sb->sv_sb_flc_count; /* = sb->sv_flc_size */
 		memcpy(flc_blocks, sb->sv_sb_flc_blocks, *flc_count * sizeof(sysv_zone_t));
-		dirtify_buffer(bh, 1);
+		mark_buffer_dirty(bh, 1);
 		bh->b_uptodate = 1;
 		brelse(bh);
 		*sb->sv_sb_flc_count = 0;
@@ -117,7 +117,7 @@
 		bh_data = bh->b_data + ((block & sb->sv_block_size_ratio_1) << sb->sv_block_size_bits);
 		memzero(bh_data, sb->sv_block_size);
 		/* this implies ((struct ..._freelist_chunk *) bh_data)->flc_count = 0; */
-		dirtify_buffer(bh, 1);
+		mark_buffer_dirty(bh, 1);
 		bh->b_uptodate = 1;
 		brelse(bh);
 		/* still *sb->sv_sb_flc_count = 0 */
@@ -138,7 +138,7 @@
 		  to_coh_ulong(from_coh_ulong(*sb->sv_sb_total_free_blocks) + 1);
 	else
 		*sb->sv_sb_total_free_blocks = *sb->sv_sb_total_free_blocks + 1;
-	dirtify_buffer(sb->sv_bh, 1); /* super-block has been modified */
+	mark_buffer_dirty(sb->sv_bh, 1); /* super-block has been modified */
 	sb->s_dirt = 1; /* and needs time stamp */
 	unlock_super(sb);
 }
@@ -229,7 +229,7 @@
 			return 0;
 		}
 	memzero(bh_data,sb->sv_block_size);
-	dirtify_buffer(bh, 1);
+	mark_buffer_dirty(bh, 1);
 	bh->b_uptodate = 1;
 	brelse(bh);
 	if (sb->sv_convert)
@@ -237,7 +237,7 @@
 		  to_coh_ulong(from_coh_ulong(*sb->sv_sb_total_free_blocks) - 1);
 	else
 		*sb->sv_sb_total_free_blocks = *sb->sv_sb_total_free_blocks - 1;
-	dirtify_buffer(sb->sv_bh, 1); /* super-block has been modified */
+	mark_buffer_dirty(sb->sv_bh, 1); /* super-block has been modified */
 	sb->s_dirt = 1; /* and needs time stamp */
 	unlock_super(sb);
 	return block;
@@ -331,7 +331,7 @@
 		printk("sysv_count_free_blocks: free block count was %d, correcting to %d\n",old_count,count);
 		if (!(sb->s_flags & MS_RDONLY)) {
 			*sb->sv_sb_total_free_blocks = (sb->sv_convert ? to_coh_ulong(count) : count);
-			dirtify_buffer(sb->sv_bh, 1); /* super-block has been modified */
+			mark_buffer_dirty(sb->sv_bh, 1); /* super-block has been modified */
 			sb->s_dirt = 1; /* and needs time stamp */
 		}
 	}
diff -u --recursive --new-file v1.1.2/linux/fs/sysv/file.c linux/fs/sysv/file.c
--- v1.1.2/linux/fs/sysv/file.c	Wed Apr 13 11:33:57 1994
+++ linux/fs/sysv/file.c	Wed Apr 13 11:16:31 1994
@@ -301,7 +301,7 @@
 		memcpy_fromfs(p,buf,c);
 		buf += c;
 		bh->b_uptodate = 1;
-		dirtify_buffer(bh, 0);
+		mark_buffer_dirty(bh, 0);
 		brelse(bh);
 	}
 	inode->i_mtime = inode->i_ctime = CURRENT_TIME;
diff -u --recursive --new-file v1.1.2/linux/fs/sysv/ialloc.c linux/fs/sysv/ialloc.c
--- v1.1.2/linux/fs/sysv/ialloc.c	Wed Apr 13 11:33:57 1994
+++ linux/fs/sysv/ialloc.c	Wed Apr 13 11:16:31 1994
@@ -74,10 +74,10 @@
 	if (*sb->sv_sb_fic_count < sb->sv_fic_size)
 		sb->sv_sb_fic_inodes[(*sb->sv_sb_fic_count)++] = ino;
 	(*sb->sv_sb_total_free_inodes)++;
-	dirtify_buffer(sb->sv_bh, 1); /* super-block has been modified */
+	mark_buffer_dirty(sb->sv_bh, 1); /* super-block has been modified */
 	sb->s_dirt = 1; /* and needs time stamp */
 	memset(raw_inode, 0, sizeof(struct sysv_inode));
-	dirtify_buffer(bh, 1);
+	mark_buffer_dirty(bh, 1);
 	unlock_super(sb);
 	brelse(bh);
 	clear_inode(inode);
@@ -130,7 +130,7 @@
 	}
 	/* Now *sb->sv_sb_fic_count > 0. */
 	ino = sb->sv_sb_fic_inodes[--(*sb->sv_sb_fic_count)];
-	dirtify_buffer(sb->sv_bh, 1); /* super-block has been modified */
+	mark_buffer_dirty(sb->sv_bh, 1); /* super-block has been modified */
 	sb->s_dirt = 1; /* and needs time stamp */
 	inode->i_count = 1;
 	inode->i_nlink = 1;
@@ -152,7 +152,7 @@
 	inode->i_dirt = 1;		/* cleared by sysv_write_inode() */
 	/* That's it. */
 	(*sb->sv_sb_total_free_inodes)--;
-	dirtify_buffer(sb->sv_bh, 1); /* super-block has been modified again */
+	mark_buffer_dirty(sb->sv_bh, 1); /* super-block has been modified again */
 	sb->s_dirt = 1; /* and needs time stamp again */
 	unlock_super(sb);
 	return inode;
@@ -191,7 +191,7 @@
 		printk("sysv_count_free_inodes: free inode count was %d, correcting to %d\n",(short)(*sb->sv_sb_total_free_inodes),count);
 		if (!(sb->s_flags & MS_RDONLY)) {
 			*sb->sv_sb_total_free_inodes = count;
-			dirtify_buffer(sb->sv_bh, 1); /* super-block has been modified */
+			mark_buffer_dirty(sb->sv_bh, 1); /* super-block has been modified */
 			sb->s_dirt = 1; /* and needs time stamp */
 		}
 	}
diff -u --recursive --new-file v1.1.2/linux/fs/sysv/inode.c linux/fs/sysv/inode.c
--- v1.1.2/linux/fs/sysv/inode.c	Wed Apr 13 11:33:57 1994
+++ linux/fs/sysv/inode.c	Wed Apr 13 11:16:31 1994
@@ -557,7 +557,7 @@
 		goto repeat;
 	}
 	*p = (sb->sv_convert ? to_coh_ulong(block) : block);
-	dirtify_buffer(bh, 1);
+	mark_buffer_dirty(bh, 1);
 	brelse(bh);
 	*start = result->b_data + ((block & sb->sv_block_size_ratio_1) << sb->sv_block_size_bits);
 	return result;
@@ -773,7 +773,7 @@
 		for (block = 0; block < 10+1+1+1; block++)
 			write3byte(&raw_inode->i_a.i_addb[3*block],inode->u.sysv_i.i_data[block]);
 	inode->i_dirt=0;
-	dirtify_buffer(bh, 1);
+	mark_buffer_dirty(bh, 1);
 	return bh;
 }
 
diff -u --recursive --new-file v1.1.2/linux/fs/sysv/namei.c linux/fs/sysv/namei.c
--- v1.1.2/linux/fs/sysv/namei.c	Wed Apr 13 11:33:57 1994
+++ linux/fs/sysv/namei.c	Wed Apr 13 11:16:32 1994
@@ -193,7 +193,7 @@
 			dir->i_mtime = dir->i_ctime = CURRENT_TIME;
 			for (i = 0; i < SYSV_NAMELEN ; i++)
 				de->name[i] = (i < namelen) ? name[i] : 0;
-			dirtify_buffer(bh, 1);
+			mark_buffer_dirty(bh, 1);
 			*res_dir = de;
 			break;
 		}
@@ -238,7 +238,7 @@
 		return error;
 	}
 	de->inode = inode->i_ino;
-	dirtify_buffer(bh, 1);
+	mark_buffer_dirty(bh, 1);
 	brelse(bh);
 	iput(dir);
 	*result = inode;
@@ -298,7 +298,7 @@
 		return error;
 	}
 	de->inode = inode->i_ino;
-	dirtify_buffer(bh, 1);
+	mark_buffer_dirty(bh, 1);
 	brelse(bh);
 	iput(dir);
 	iput(inode);
@@ -349,7 +349,7 @@
 	de->inode = dir->i_ino;
 	strcpy(de->name,".."); /* rest of de->name is zero, see sysv_new_block */
 	inode->i_nlink = 2;
-	dirtify_buffer(dir_block, 1);
+	mark_buffer_dirty(dir_block, 1);
 	brelse(dir_block);
 	inode->i_mode = S_IFDIR | (mode & 0777 & ~current->umask);
 	if (dir->i_mode & S_ISGID)
@@ -363,7 +363,7 @@
 		return error;
 	}
 	de->inode = inode->i_ino;
-	dirtify_buffer(bh, 1);
+	mark_buffer_dirty(bh, 1);
 	dir->i_nlink++;
 	dir->i_dirt = 1;
 	iput(dir);
@@ -473,7 +473,7 @@
 	if (inode->i_nlink != 2)
 		printk("empty directory has nlink!=2 (%d)\n",inode->i_nlink);
 	de->inode = 0;
-	dirtify_buffer(bh, 1);
+	mark_buffer_dirty(bh, 1);
 	inode->i_nlink=0;
 	inode->i_dirt=1;
 	dir->i_nlink--;
@@ -526,7 +526,7 @@
 		inode->i_nlink=1;
 	}
 	de->inode = 0;
-	dirtify_buffer(bh, 1);
+	mark_buffer_dirty(bh, 1);
 	dir->i_ctime = dir->i_mtime = CURRENT_TIME;
 	dir->i_dirt = 1;
 	inode->i_nlink--;
@@ -570,7 +570,7 @@
 	while (i < sb->sv_block_size_1 && (c = *(symname++)))
 		name_block_data[i++] = c;
 	name_block_data[i] = 0;
-	dirtify_buffer(name_block, 1);
+	mark_buffer_dirty(name_block, 1);
 	brelse(name_block);
 	inode->i_size = i;
 	inode->i_dirt = 1;
@@ -592,7 +592,7 @@
 		return i;
 	}
 	de->inode = inode->i_ino;
-	dirtify_buffer(bh, 1);
+	mark_buffer_dirty(bh, 1);
 	brelse(bh);
 	iput(dir);
 	iput(inode);
@@ -629,7 +629,7 @@
 		return error;
 	}
 	de->inode = oldinode->i_ino;
-	dirtify_buffer(bh, 1);
+	mark_buffer_dirty(bh, 1);
 	brelse(bh);
 	iput(dir);
 	oldinode->i_nlink++;
@@ -782,11 +782,11 @@
 		new_inode->i_ctime = CURRENT_TIME;
 		new_inode->i_dirt = 1;
 	}
-	dirtify_buffer(old_bh, 1);
-	dirtify_buffer(new_bh, 1);
+	mark_buffer_dirty(old_bh, 1);
+	mark_buffer_dirty(new_bh, 1);
 	if (dir_bh) {
 		PARENT_INO(dir_bh_data) = new_dir->i_ino;
-		dirtify_buffer(dir_bh, 1);
+		mark_buffer_dirty(dir_bh, 1);
 		old_dir->i_nlink--;
 		old_dir->i_dirt = 1;
 		if (new_inode) {
diff -u --recursive --new-file v1.1.2/linux/fs/sysv/truncate.c linux/fs/sysv/truncate.c
--- v1.1.2/linux/fs/sysv/truncate.c	Wed Apr 13 11:33:57 1994
+++ linux/fs/sysv/truncate.c	Wed Apr 13 11:16:32 1994
@@ -84,7 +84,7 @@
 		if (!indblock)
 			continue;
 		*ind = 0;
-		dirtify_buffer(bh, 1);
+		mark_buffer_dirty(bh, 1);
 		sysv_free_block(sb,indblock);
 	}
 	for (i = 0; i < sb->sv_ind_per_block; i++)
@@ -338,7 +338,7 @@
 			continue;
 		}
 		*ind = 0;
-		dirtify_buffer(indbh, 1);
+		mark_buffer_dirty(indbh, 1);
 		brelse(bh);
 		sysv_free_block(sb,block);
 	}
diff -u --recursive --new-file v1.1.2/linux/fs/xiafs/bitmap.c linux/fs/xiafs/bitmap.c
--- v1.1.2/linux/fs/xiafs/bitmap.c	Wed Apr 13 11:33:57 1994
+++ linux/fs/xiafs/bitmap.c	Wed Apr 13 11:16:32 1994
@@ -65,7 +65,7 @@
         start_bit=j + (i << 5) + 1;
 	goto repeat;
     }
-    dirtify_buffer(bh, 1);
+    mark_buffer_dirty(bh, 1);
     return j + (i << 5);
 }
 
@@ -245,7 +245,7 @@
         printk("XIA-FS: dev %04x"
 	       " block bit %u (0x%x) already cleared (%s %d)\n",
 	       sb->s_dev, bit, bit, WHERE_ERR);
-    dirtify_buffer(bh, 1);
+    mark_buffer_dirty(bh, 1);
     xiafs_unlock_super(sb, sb->u.xiafs_sb.s_zmap_cached);
 }
 
@@ -277,7 +277,7 @@
     }
     clear_buf(bh);
     bh->b_uptodate = 1;
-    dirtify_buffer(bh, 1);
+    mark_buffer_dirty(bh, 1);
     brelse(bh);
     return tmp;
 }
@@ -305,7 +305,7 @@
         printk("XIA-FS: dev %04x"
 	       "inode bit %ld (0x%lx) already cleared (%s %d)\n",
 	       inode->i_dev, ino, ino, WHERE_ERR);
-    dirtify_buffer(bh, 1);
+    mark_buffer_dirty(bh, 1);
     xiafs_unlock_super(sb, sb->u.xiafs_sb.s_imap_cached);
 }
 
diff -u --recursive --new-file v1.1.2/linux/fs/xiafs/file.c linux/fs/xiafs/file.c
--- v1.1.2/linux/fs/xiafs/file.c	Wed Apr 13 11:33:58 1994
+++ linux/fs/xiafs/file.c	Wed Apr 13 11:16:32 1994
@@ -241,7 +241,7 @@
 	memcpy_fromfs(cp,buf,c);
 	buf += c;
 	bh->b_uptodate = 1;
-	dirtify_buffer(bh, 0);
+	mark_buffer_dirty(bh, 0);
 	brelse(bh);
     }
     inode->i_mtime = inode->i_ctime = CURRENT_TIME;
diff -u --recursive --new-file v1.1.2/linux/fs/xiafs/inode.c linux/fs/xiafs/inode.c
--- v1.1.2/linux/fs/xiafs/inode.c	Wed Apr 13 11:33:58 1994
+++ linux/fs/xiafs/inode.c	Wed Apr 13 11:16:32 1994
@@ -306,7 +306,7 @@
     }
     *lp = tmp;
     inode->i_blocks+=2 << XIAFS_ZSHIFT(inode->i_sb);
-    dirtify_buffer(bh, 1);
+    mark_buffer_dirty(bh, 1);
     brelse(bh);
     return result;
 }
@@ -466,7 +466,7 @@
 	                             | (inode->u.xiafs_i.i_dind_zone  & 0xffffff);
     }
     inode->i_dirt=0;
-    dirtify_buffer(bh, 1);
+    mark_buffer_dirty(bh, 1);
     return bh;
 }
 
diff -u --recursive --new-file v1.1.2/linux/fs/xiafs/namei.c linux/fs/xiafs/namei.c
--- v1.1.2/linux/fs/xiafs/namei.c	Wed Apr 13 11:33:58 1994
+++ linux/fs/xiafs/namei.c	Wed Apr 13 11:16:33 1994
@@ -221,7 +221,7 @@
 		memcpy(de->d_name, name, namelen);
 		de->d_name[namelen]=0;
 		de->d_name_len=namelen;
-		dirtify_buffer(bh, 1);
+		mark_buffer_dirty(bh, 1);
 		*res_dir = de;
 		if (res_pre)
 		    *res_pre = de_pre;
@@ -268,7 +268,7 @@
 	return -ENOSPC;
     }
     de->d_ino = inode->i_ino;
-    dirtify_buffer(bh, 1);
+    mark_buffer_dirty(bh, 1);
     brelse(bh);
     iput(dir);
     *result = inode;
@@ -325,7 +325,7 @@
 	return -ENOSPC;
     }
     de->d_ino = inode->i_ino;
-    dirtify_buffer(bh, 1);
+    mark_buffer_dirty(bh, 1);
     brelse(bh);
     iput(dir);
     iput(inode);
@@ -375,7 +375,7 @@
     de->d_name_len=2;
     de->d_rec_len=XIAFS_ZSIZE(dir->i_sb)-12;
     inode->i_nlink = 2;
-    dirtify_buffer(dir_block, 1);
+    mark_buffer_dirty(dir_block, 1);
     brelse(dir_block);
     inode->i_mode = S_IFDIR | (mode & S_IRWXUGO & ~current->umask);
     if (dir->i_mode & S_ISGID)
@@ -389,7 +389,7 @@
 	return -ENOSPC;
     }
     de->d_ino = inode->i_ino;
-    dirtify_buffer(bh, 1);
+    mark_buffer_dirty(bh, 1);
     dir->i_nlink++;
     dir->i_dirt = 1;
     iput(dir);
@@ -518,7 +518,7 @@
     if (inode->i_nlink != 2)
         printk("XIA-FS: empty directory has nlink!=2 (%s %d)\n", WHERE_ERR);
     xiafs_rm_entry(de, de_pre);
-    dirtify_buffer(bh, 1);
+    mark_buffer_dirty(bh, 1);
     inode->i_nlink=0;
     inode->i_dirt=1;
     dir->i_nlink--;
@@ -566,7 +566,7 @@
 	inode->i_nlink=1;
     }
     xiafs_rm_entry(de, de_pre);
-    dirtify_buffer(bh, 1);
+    mark_buffer_dirty(bh, 1);
     inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
     dir->i_dirt = 1;
     inode->i_nlink--;
@@ -611,7 +611,7 @@
     for (i = 0; i < BLOCK_SIZE-1 && (c=*symname++); i++)
         name_block->b_data[i] = c;
     name_block->b_data[i] = 0;
-    dirtify_buffer(name_block, 1);
+    mark_buffer_dirty(name_block, 1);
     brelse(name_block);
     inode->i_size = i;
     inode->i_dirt = 1;
@@ -624,7 +624,7 @@
 	return -ENOSPC;
     }
     de->d_ino = inode->i_ino;
-    dirtify_buffer(bh, 1);
+    mark_buffer_dirty(bh, 1);
     brelse(bh);
     iput(dir);
     iput(inode);
@@ -661,7 +661,7 @@
 	return -ENOSPC;
     }
     de->d_ino = oldinode->i_ino;
-    dirtify_buffer(bh, 1);
+    mark_buffer_dirty(bh, 1);
     brelse(bh);
     iput(dir);
     oldinode->i_nlink++;
@@ -798,11 +798,11 @@
         new_inode->i_nlink--;
 	new_inode->i_dirt = 1;
     }
-    dirtify_buffer(old_bh, 1);
-    dirtify_buffer(new_bh, 1);
+    mark_buffer_dirty(old_bh, 1);
+    mark_buffer_dirty(new_bh, 1);
     if (dir_bh) {
         PARENT_INO(dir_bh->b_data) = new_dir->i_ino;
-	dirtify_buffer(dir_bh, 1);
+	mark_buffer_dirty(dir_bh, 1);
 	old_dir->i_nlink--;
 	new_dir->i_nlink++;
 	old_dir->i_dirt = 1;
diff -u --recursive --new-file v1.1.2/linux/fs/xiafs/truncate.c linux/fs/xiafs/truncate.c
--- v1.1.2/linux/fs/xiafs/truncate.c	Wed Apr 13 11:33:58 1994
+++ linux/fs/xiafs/truncate.c	Wed Apr 13 11:16:33 1994
@@ -104,7 +104,7 @@
 	    retry = 1;
 	else {
 	    *indp = 0;
-	    dirtify_buffer(ind_bh, 1);
+	    mark_buffer_dirty(ind_bh, 1);
 	    inode->i_blocks-= 2 << XIAFS_ZSHIFT(inode->i_sb);
 	    xiafs_free_zone(inode->i_sb, tmp);
 	}
@@ -157,7 +157,7 @@
 	retry |= trunc_indirect(inode, 
 				8+((1+i)<<XIAFS_ADDRS_PER_Z_BITS(inode->i_sb)), 
 				dindp);
-	dirtify_buffer(dind_bh, 1);
+	mark_buffer_dirty(dind_bh, 1);
     }
     dindp = (u_long *) dind_bh->b_data;
     for (i = 0; i < XIAFS_ADDRS_PER_Z(inode->i_sb) && !(*dindp++); i++);
diff -u --recursive --new-file v1.1.2/linux/include/linux/ext2_fs.h linux/include/linux/ext2_fs.h
--- v1.1.2/linux/include/linux/ext2_fs.h	Thu Mar 10 18:48:36 1994
+++ linux/include/linux/ext2_fs.h	Wed Apr 13 11:17:49 1994
@@ -390,6 +390,7 @@
 extern struct buffer_head * ext2_getblk (struct inode *, long, int, int *);
 extern struct buffer_head * ext2_bread (struct inode *, int, int, int *);
 
+extern int ext2_getcluster (struct inode * inode, long block);
 extern void ext2_read_inode (struct inode *);
 extern void ext2_write_inode (struct inode *);
 extern void ext2_put_inode (struct inode *);
diff -u --recursive --new-file v1.1.2/linux/include/linux/fs.h linux/include/linux/fs.h
--- v1.1.2/linux/include/linux/fs.h	Wed Apr 13 11:33:58 1994
+++ linux/include/linux/fs.h	Wed Apr 13 11:17:49 1994
@@ -28,7 +28,6 @@
 #define NR_INODE 2048	/* this should be bigger than NR_FILE */
 #define NR_FILE 1024	/* this can well be larger on a larger system */
 #define NR_SUPER 32
-#define NR_HASH 997
 #define NR_IHASH 131
 #define NR_FILE_LOCKS 64
 #define BLOCK_SIZE 1024
@@ -138,6 +137,11 @@
 	unsigned char b_dirt;		/* 0-clean,1-dirty */
 	unsigned char b_lock;		/* 0 - ok, 1 -locked */
 	unsigned char b_req;		/* 0 if the buffer has been invalidated */
+	unsigned char b_list;		/* List that this buffer appears */
+	unsigned char b_retain;         /* Expected number of times this will
+					   be used.  Put on freelist when 0 */
+	unsigned long b_flushtime;      /* Time when this (dirty) buffer should be written */
+	unsigned long b_lru_time;       /* Time when this buffer was last used. */
 	struct wait_queue * b_wait;
 	struct buffer_head * b_prev;		/* doubly linked list of hash-queue */
 	struct buffer_head * b_next;
@@ -356,16 +360,41 @@
 
 extern int shrink_buffers(unsigned int priority);
 
+extern void refile_buffer(struct buffer_head * buf);
+void set_writetime(struct buffer_head * buf, int flag);
+
+struct buffer_head ** buffer_pages;
+
 extern int nr_buffers;
 extern int buffermem;
 extern int nr_buffer_heads;
 
-/* Once the full cluster diffs are in place, this will be filled out a bit. */
-extern inline void dirtify_buffer(struct buffer_head * bh, int flag)
+#define BUF_CLEAN 0
+#define BUF_UNSHARED 1 /* Buffers that were shared but are not any more */
+#define BUF_LOCKED 2   /* Buffers scheduled for write */
+#define BUF_LOCKED1 3  /* Supers, inodes */
+#define BUF_DIRTY 4    /* Dirty buffers, not yet scheduled for write */
+#define BUF_SHARED 5   /* Buffers shared */
+#define NR_LIST 6
+
+extern inline void mark_buffer_clean(struct buffer_head * bh)
+{
+  if(bh->b_dirt) {
+    bh->b_dirt = 0;
+    if(bh->b_list == BUF_DIRTY) refile_buffer(bh);
+  }
+}
+
+extern inline void mark_buffer_dirty(struct buffer_head * bh, int flag)
 {
-  bh->b_dirt = 1;
+  if(!bh->b_dirt) {
+    bh->b_dirt = 1;
+    set_writetime(bh, flag);
+    if(bh->b_list != BUF_DIRTY) refile_buffer(bh);
+  }
 }
 
+
 extern void check_disk_change(dev_t dev);
 extern void invalidate_inodes(dev_t dev);
 extern void invalidate_buffers(dev_t dev);
@@ -402,6 +431,7 @@
 extern struct buffer_head * breada(dev_t dev,int block, int size, 
 				   unsigned int pos, unsigned int filesize);
 extern void put_super(dev_t dev);
+unsigned long generate_cluster(dev_t dev, int b[], int size);
 extern dev_t ROOT_DEV;
 
 extern void show_buffers(void);
diff -u --recursive --new-file v1.1.2/linux/include/linux/mm.h linux/include/linux/mm.h
--- v1.1.2/linux/include/linux/mm.h	Wed Apr  6 11:39:19 1994
+++ linux/include/linux/mm.h	Wed Apr 13 11:17:49 1994
@@ -217,6 +217,7 @@
 #define GFP_ATOMIC	0x01
 #define GFP_USER	0x02
 #define GFP_KERNEL	0x03
+#define GFP_NOBUFFER	0x04
 
 
 /* vm_ops not present page codes */
diff -u --recursive --new-file v1.1.2/linux/include/linux/sys.h linux/include/linux/sys.h
--- v1.1.2/linux/include/linux/sys.h	Tue Apr  5 09:00:20 1994
+++ linux/include/linux/sys.h	Wed Apr 13 11:17:49 1994
@@ -14,6 +14,7 @@
 /*
  * These are system calls that will be removed at some time
  * due to newer versions existing..
+ * (please be careful - ibcs2 may need some of these).
  */
 #ifdef notdef
 #define _sys_waitpid	_sys_old_syscall	/* _sys_wait4 */
@@ -32,6 +33,5 @@
  * but have an entry in the table for future expansion..
  */
 #define _sys_quotactl	_sys_ni_syscall
-#define _sys_bdflush	_sys_ni_syscall
 
 #endif
diff -u --recursive --new-file v1.1.2/linux/mm/swap.c linux/mm/swap.c
--- v1.1.2/linux/mm/swap.c	Wed Apr  6 11:48:16 1994
+++ linux/mm/swap.c	Wed Apr 13 11:17:49 1994
@@ -470,12 +470,12 @@
 
 #endif
 
-static int try_to_free_page(void)
+static int try_to_free_page(int priority)
 {
 	int i=6;
 
 	while (i--) {
-		if (shrink_buffers(i))
+	        if (priority != GFP_NOBUFFER && shrink_buffers(i))
 			return 1;
 		if (shm_swap(i))
 			return 1;
@@ -545,6 +545,17 @@
 				if (!--*map)
 					free_pages_ok(addr, order);
 				restore_flags(flag);
+				if(*map == 1) {
+				  int j;
+				  struct buffer_head * bh, *tmp;
+
+				  bh = buffer_pages[MAP_NR(addr)];
+				  if(bh)
+				    for(j = 0, tmp = bh; tmp && (!j || tmp != bh); 
+					tmp = tmp->b_this_page, j++)
+				      if(tmp->b_list == BUF_SHARED && tmp->b_dev != 0xffff)
+					refile_buffer(tmp);
+				}
 			}
 			return;
 		}
@@ -610,7 +621,7 @@
 		return 0;
 	}
 	restore_flags(flags);
-        if (priority != GFP_BUFFER && try_to_free_page())
+        if (priority != GFP_BUFFER && try_to_free_page(priority))
 		goto repeat;
 	return 0;
 }
