diff -ru linux-2.4.17/Makefile linux-2.4.17-fstune/Makefile
--- linux-2.4.17/Makefile	Fri May  3 21:10:20 2002
+++ linux-2.4.17-fstune/Makefile	Wed Aug  7 19:36:46 2002
@@ -1,7 +1,7 @@
 VERSION = 2
 PATCHLEVEL = 4
 SUBLEVEL = 17
-EXTRAVERSION =
+EXTRAVERSION = -fstune
 
 KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
 
diff -ru linux-2.4.17/drivers/ide/via82cxxx.c linux-2.4.17-fstune/drivers/ide/via82cxxx.c
--- linux-2.4.17/drivers/ide/via82cxxx.c	Wed Sep 12 00:40:36 2001
+++ linux-2.4.17-fstune/drivers/ide/via82cxxx.c	Wed Aug  7 19:36:46 2002
@@ -1,5 +1,5 @@
 /*
- * $Id: via82cxxx.c,v 3.29 2001/09/10 10:06:00 vojtech Exp $
+ * $Id: via82cxxx.c,v 3.20 2001/01/27 10:13:60 vojtech Exp $
  *
  *  Copyright (c) 2000-2001 Vojtech Pavlik
  *
@@ -14,20 +14,17 @@
 /*
  * VIA IDE driver for Linux. Supports
  *
- *   vt82c576, vt82c586, vt82c586a, vt82c586b, vt82c596a, vt82c596b,
+ *   vt82c586, vt82c586a, vt82c586b, vt82c596a, vt82c596b,
  *   vt82c686, vt82c686a, vt82c686b, vt8231, vt8233
  *
  * southbridges, which can be found in
  *
- *  VIA Apollo Master, VP, VP2, VP2/97, VP3, VPX, VPX/97, MVP3, MVP4, P6, Pro,
- *    ProII, ProPlus, Pro133, Pro133+, Pro133A, Pro133A Dual, Pro133T, Pro133Z,
- *    PLE133, PLE133T, Pro266, Pro266T, ProP4X266, PM601, PM133, PN133, PL133T,
- *    PX266, PM266, KX133, KT133, KT133A, KLE133, KT266, KX266, KM133, KM133A,
- *    KL133, KN133, KM266
- *  PC-Chips VXPro, VXPro+, VXTwo, TXPro-III, TXPro-AGP, AGPPro, ViaGra, BXToo,
- *    BXTel, BXpert
- *  AMD 640, 640 AGP, 750 IronGate, 760, 760MP
- *  ETEQ 6618, 6628, 6629, 6638
+ *  VIA Apollo VP, VPX, VPX/97, VP2, VP2/97, VP3, MVP3, MVP4, P6, Pro,
+ *  Pro Plus, Pro 133, Pro 133A, ProMedia PM601, ProSavage PM133, PLE133,
+ *  Pro 266, KX133, KT133, ProSavage KM133, KT133A, KT266
+ *  PC-Chips VXPro, VXPro+, TXPro-III, TXPro-AGP, ViaGra, BXToo, BXTel
+ *  AMD 640, 640 AGP, 750 IronGate
+ *  ETEQ 6618, 6628, 6638
  *  Micron Samurai
  *
  * chipsets. Supports
@@ -35,9 +32,14 @@
  *   PIO 0-5, MWDMA 0-2, SWDMA 0-2 and UDMA 0-5
  *
  * (this includes UDMA33, 66 and 100) modes. UDMA66 and higher modes are
- * autoenabled only in case the BIOS has detected a 80 wire cable. To ignore
- * the BIOS data and assume the cable is present, use 'ide0=ata66' or
- * 'ide1=ata66' on the kernel command line.
+ * autodetected only in case the BIOS has detected a 80 wire cable. To ignore
+ * the BIOS data, use 'ide0=ata66' or 'ide1=ata66' on the kernel command line.
+ *
+ * For correct operation it's needed to tell the driver the speed of the PCI
+ * bus. The default, and most common value is 33 MHz. Most likely, your system
+ * is using this value and you don't need to use any command line options. If
+ * you run your PCI bus speed at 25, 30, 37, 40 or 42 MHz, use the 'idebus=xx'
+ * option of the IDE driver. Note that this has nothing to do with UDMA66.
  */
 
 /*
@@ -82,15 +84,15 @@
 #define VIA_ADDRESS_SETUP	0x4c
 #define VIA_UDMA_TIMING		0x50
 
-#define VIA_UDMA		0x007
-#define VIA_UDMA_NONE		0x000
-#define VIA_UDMA_33		0x001
-#define VIA_UDMA_66		0x002
-#define VIA_UDMA_100		0x003
-#define VIA_BAD_PREQ		0x010	/* Crashes if PREQ# till DDACK# set */
-#define VIA_BAD_CLK66		0x020	/* 66 MHz clock doesn't work correctly */
-#define VIA_SET_FIFO		0x040	/* Needs to have FIFO split set */
-#define VIA_NO_UNMASK		0x080	/* Doesn't work with IRQ unmasking on */
+#define VIA_UDMA		0x07
+#define VIA_UDMA_NONE		0x00
+#define VIA_UDMA_33		0x01
+#define VIA_UDMA_66		0x02
+#define VIA_UDMA_100		0x03
+#define VIA_BAD_PREQ		0x10
+#define VIA_BAD_CLK66		0x20
+#define VIA_SET_FIFO		0x40
+#define VIA_SET_THRESH		0x80
 
 /*
  * VIA SouthBridge chips.
@@ -101,26 +103,20 @@
 	unsigned short id;
 	unsigned char rev_min;
 	unsigned char rev_max;
-	unsigned short flags;
+	unsigned char flags;
 } via_isa_bridges[] = {
-#ifdef FUTURE_BRIDGES
-	{ "vt8237",	PCI_DEVICE_ID_VIA_8237,     0x00, 0x2f, VIA_UDMA_100 },
-	{ "vt8235",	PCI_DEVICE_ID_VIA_8235,     0x00, 0x2f, VIA_UDMA_100 },
-	{ "vt8233c",	PCI_DEVICE_ID_VIA_8233C,    0x00, 0x2f, VIA_UDMA_100 },
-#endif
-	{ "vt8233",	PCI_DEVICE_ID_VIA_8233_0,   0x00, 0x2f, VIA_UDMA_100 },
-	{ "vt8231",	PCI_DEVICE_ID_VIA_8231,     0x00, 0x2f, VIA_UDMA_100 },
-	{ "vt82c686b",	PCI_DEVICE_ID_VIA_82C686,   0x40, 0x4f, VIA_UDMA_100 },
-	{ "vt82c686a",	PCI_DEVICE_ID_VIA_82C686,   0x10, 0x2f, VIA_UDMA_66 },
-	{ "vt82c686",	PCI_DEVICE_ID_VIA_82C686,   0x00, 0x0f, VIA_UDMA_33 | VIA_BAD_CLK66 },
-	{ "vt82c596b",	PCI_DEVICE_ID_VIA_82C596,   0x10, 0x2f, VIA_UDMA_66 },
+	{ "vt8233", 	PCI_DEVICE_ID_VIA_8233_0,   0x00, 0x2f, VIA_UDMA_100 },
+/* Modified by VIA,{"vt8231", 	PCI_DEVICE_ID_VIA_8231,   0x00, 0x2f, VIA_UDMA_66 },*/  
+        { "vt8231", 	PCI_DEVICE_ID_VIA_8231,   0x00, 0x2f, VIA_UDMA_100 },  
+        { "vt82c686b",	PCI_DEVICE_ID_VIA_82C686,   0x40, 0x4f, VIA_UDMA_100 },  
+	{ "vt82c686a",	PCI_DEVICE_ID_VIA_82C686,   0x10, 0x2f, VIA_UDMA_66 },  
+        { "vt82c686",	PCI_DEVICE_ID_VIA_82C686,   0x00, 0x0f, VIA_UDMA_66 },
+	{ "vt82c596b",	PCI_DEVICE_ID_VIA_82C596,   0x10, 0x2f, VIA_UDMA_66 },  
 	{ "vt82c596a",	PCI_DEVICE_ID_VIA_82C596,   0x00, 0x0f, VIA_UDMA_33 | VIA_BAD_CLK66 },
-	{ "vt82c586b",	PCI_DEVICE_ID_VIA_82C586_0, 0x47, 0x4f, VIA_UDMA_33 | VIA_SET_FIFO },
-	{ "vt82c586b",	PCI_DEVICE_ID_VIA_82C586_0, 0x40, 0x46, VIA_UDMA_33 | VIA_SET_FIFO | VIA_BAD_PREQ },
+	{ "vt82c586b",	PCI_DEVICE_ID_VIA_82C586_0, 0x40, 0x4f, VIA_UDMA_33 | VIA_SET_FIFO | VIA_BAD_PREQ },
 	{ "vt82c586b",	PCI_DEVICE_ID_VIA_82C586_0, 0x30, 0x3f, VIA_UDMA_33 | VIA_SET_FIFO },
 	{ "vt82c586a",	PCI_DEVICE_ID_VIA_82C586_0, 0x20, 0x2f, VIA_UDMA_33 | VIA_SET_FIFO },
 	{ "vt82c586",	PCI_DEVICE_ID_VIA_82C586_0, 0x00, 0x0f, VIA_UDMA_NONE | VIA_SET_FIFO },
-	{ "vt82c576",	PCI_DEVICE_ID_VIA_82C576,   0x00, 0x2f, VIA_UDMA_NONE | VIA_SET_FIFO | VIA_NO_UNMASK },
 	{ NULL }
 };
 
@@ -128,7 +124,6 @@
 static unsigned char via_enabled;
 static unsigned int via_80w;
 static unsigned int via_clock;
-static char *via_dma[] = { "MWDMA16", "UDMA33", "UDMA66", "UDMA100" };
 
 /*
  * VIA /proc entry.
@@ -151,8 +146,8 @@
 
 static int via_get_info(char *buffer, char **addr, off_t offset, int count)
 {
-	short speed[4], cycle[4], setup[4], active[4], recover[4], den[4],
-		 uen[4], udma[4], umul[4], active8b[4], recover8b[4];
+	short speed[4], cycle[4], setup[4], active[4],
+		recover[4], uen[4], udma[4], active8b[4], recover8b[4];
 	struct pci_dev *dev = bmide_dev;
 	unsigned int v, u, i;
 	unsigned short c, w;
@@ -161,13 +156,12 @@
 
 	via_print("----------VIA BusMastering IDE Configuration----------------");
 
-	via_print("Driver Version:                     3.29");
+	via_print("Driver Version:                     3.20");
 	via_print("South Bridge:                       VIA %s", via_config->name);
 
 	pci_read_config_byte(isa_dev, PCI_REVISION_ID, &t);
 	pci_read_config_byte(dev, PCI_REVISION_ID, &x);
 	via_print("Revision:                           ISA %#x IDE %#x", t, x);
-	via_print("Highest DMA rate:                   %s", via_dma[via_config->flags & VIA_UDMA]);
 
 	via_print("BM-DMA base:                        %#x", via_base);
 	via_print("PCI clock:                          %dMHz", via_clock);
@@ -207,43 +201,28 @@
 	else u = 0;
 
 	for (i = 0; i < 4; i++) {
-
 		setup[i]     = ((t >> ((3 - i) << 1)) & 0x3) + 1;
 		recover8b[i] = ((w >> ((1 - (i >> 1)) << 3)) & 0xf) + 1;
 		active8b[i]  = ((w >> (((1 - (i >> 1)) << 3) + 4)) & 0xf) + 1;
 		active[i]    = ((v >> (((3 - i) << 3) + 4)) & 0xf) + 1;
 		recover[i]   = ((v >> ((3 - i) << 3)) & 0xf) + 1;
 		udma[i]      = ((u >> ((3 - i) << 3)) & 0x7) + 2;
-		umul[i]      = ((u >> (((3 - i) & 2) << 3)) & 0x8) ? 1 : 2;
-		uen[i]       = ((u >> ((3 - i) << 3)) & 0x20);
-		den[i]       = (c & ((i & 1) ? 0x40 : 0x20) << ((i & 2) << 2));
 
-		speed[i] = 20 * via_clock / (active[i] + recover[i]);
-		cycle[i] = 1000 / via_clock * (active[i] + recover[i]);
-
-		if (!uen[i] || !den[i])
+		if ((via_config->flags & VIA_UDMA) == VIA_UDMA_100) {
+			speed[i] = 2000 / udma[i];
+			cycle[i] = 10 * udma[i];
 			continue;
+		}
 
-		switch (via_config->flags & VIA_UDMA) {
-			
-			case VIA_UDMA_100:
-				speed[i] = 60 * via_clock / udma[i];
-				cycle[i] = 333 / via_clock * udma[i];
-				break;
-
-			case VIA_UDMA_66:
-				speed[i] = 40 * via_clock / (udma[i] * umul[i]);
-				cycle[i] = 500 / via_clock * (udma[i] * umul[i]);
-				break;
+		uen[i]   = (u >> ((3 - i) << 3)) & 0x20;
+		udma[i] *= ((u >> (((3 - i) & 2) << 3)) & 0x8) ? 1 : 2;
 
-			case VIA_UDMA_33:
-				speed[i] = 20 * via_clock / udma[i];
-				cycle[i] = 1000 / via_clock * udma[i];
-				break;
-		}
+		speed[i] = 40 * via_clock / (uen[i] ? udma[i] : (active[i] + recover[i]) * 2);
+		cycle[i] = 1000 / via_clock * (uen[i] ? udma[i] : (active[i] + recover[i]) * 2) / 2;
 	}
 
-	via_print_drive("Transfer Mode: ", "%10s", den[i] ? (uen[i] ? "UDMA" : "DMA") : "PIO");
+	via_print_drive("Transfer Mode: ", "%10s",
+			(c & ((i & 1) ? 0x40 : 0x20) << ((i & 2) << 2)) ? (uen[i] ? "UDMA" : "DMA") : "PIO");
 
 	via_print_drive("Address Setup: ", "%8dns", (1000 / via_clock) * setup[i]);
 	via_print_drive("Cmd Active:    ", "%8dns", (1000 / via_clock) * active8b[i]);
@@ -308,7 +287,7 @@
 	switch (via_config->flags & VIA_UDMA) {
 		case VIA_UDMA_33:   UT = T;   break;
 		case VIA_UDMA_66:   UT = T/2; break;
-		case VIA_UDMA_100:  UT = T/3; break;
+		case VIA_UDMA_100:  UT = 10;  break;
 		default:	    UT = T;   break;
 	}
 
@@ -364,13 +343,12 @@
 		short speed = ide_find_best_mode(drive,
 			XFER_PIO | XFER_EPIO | XFER_SWDMA | XFER_MWDMA |
 			(via_config->flags & VIA_UDMA ? XFER_UDMA : 0) |
-			(w80 && (via_config->flags & VIA_UDMA) >= VIA_UDMA_66 ? XFER_UDMA_66 : 0) |
-			(w80 && (via_config->flags & VIA_UDMA) >= VIA_UDMA_100 ? XFER_UDMA_100 : 0));
+			(w80 && (via_config->flags & VIA_UDMA) == VIA_UDMA_66 ? XFER_UDMA_66 : 0) |
+			(w80 && (via_config->flags & VIA_UDMA) == VIA_UDMA_100 ? XFER_UDMA_100 : 0));
 
-		via_set_drive(drive, speed);
+		func = ((speed & XFER_MODE) != XFER_PIO) ? ide_dma_on : ide_dma_off_quietly;
 
-		func = (HWIF(drive)->autodma && (speed & XFER_MODE) != XFER_PIO)
-			? ide_dma_on : ide_dma_off_quietly;
+		via_set_drive(drive, speed);
 	}
 
 	return ide_dmaproc(func, drive);
@@ -430,8 +408,12 @@
 			break;
 	}
 
+/* v3.20 bug, removed by VIA;
+ * via_80w = 0; 
+ */
+
 	if (via_config->flags & VIA_BAD_CLK66) {			/* Disable Clk66 */
-		pci_read_config_dword(dev, VIA_UDMA_TIMING, &u);	/* Would cause trouble on 596a and 686 */
+		pci_read_config_dword(dev, VIA_UDMA_TIMING, &u);	/* Would cause trouble on 596a */
 		pci_write_config_dword(dev, VIA_UDMA_TIMING, u & ~0x80008);
 	}
 
@@ -460,6 +442,9 @@
 		}
 	}
 
+	if (via_config->flags & VIA_SET_THRESH)				/* 1/2 FIFO full to trigger xmit */
+		t = (t & 0xf0) | 0x0a;
+
 	pci_write_config_byte(dev, VIA_FIFO_CONFIG, t);
 
 /*
@@ -479,7 +464,11 @@
 
 	pci_read_config_byte(isa, PCI_REVISION_ID, &t);
 	printk(KERN_INFO "VP_IDE: VIA %s (rev %02x) IDE %s controller on pci%s\n",
-		via_config->name, t, via_dma[via_config->flags & VIA_UDMA], dev->slot_name);
+			via_config->name, t,
+			(via_config->flags & VIA_UDMA) == VIA_UDMA_100 ? "UDMA100" :
+			(via_config->flags & VIA_UDMA) == VIA_UDMA_66 ? "UDMA66" :
+			(via_config->flags & VIA_UDMA) == VIA_UDMA_33 ? "UDMA33" : "MWDMA16",
+			dev->slot_name);
 
 /*
  * Setup /proc/ide/via entry.
@@ -487,11 +476,11 @@
 
 #ifdef CONFIG_PROC_FS
 	if (!via_proc) {
+		via_proc = 1;
 		via_base = pci_resource_start(dev, 4);
 		bmide_dev = dev;
 		isa_dev = isa;
 		via_display_info = &via_get_info;
-		via_proc = 1;
 	}
 #endif
 
@@ -513,7 +502,7 @@
 
 	for (i = 0; i < 2; i++) {
 		hwif->drives[i].io_32bit = 1;
-		hwif->drives[i].unmask = (via_config->flags & VIA_NO_UNMASK) ? 0 : 1;
+		hwif->drives[i].unmask = 1;
 		hwif->drives[i].autotune = 1;
 		hwif->drives[i].dn = hwif->channel * 2 + i;
 	}
diff -ru linux-2.4.17/fs/Config.in linux-2.4.17-fstune/fs/Config.in
--- linux-2.4.17/fs/Config.in	Tue Nov 13 02:34:16 2001
+++ linux-2.4.17-fstune/fs/Config.in	Wed Aug  7 19:36:46 2002
@@ -86,6 +86,11 @@
 tristate 'UFS file system support (read only)' CONFIG_UFS_FS
 dep_mbool '  UFS file system write support (DANGEROUS)' CONFIG_UFS_FS_WRITE $CONFIG_UFS_FS $CONFIG_EXPERIMENTAL
 
+bool ' File System Tuning ' CONFIG_FSTUNE
+if [ "$CONFIG_FSTUNE" = "y" ]; then
+	bool ' use bread_zone ' CONFIG_FSTUNE_BREAD_ZONE
+fi
+
 if [ "$CONFIG_NET" = "y" ]; then
 
    mainmenu_option next_comment
diff -ru linux-2.4.17/fs/buffer.c linux-2.4.17-fstune/fs/buffer.c
--- linux-2.4.17/fs/buffer.c	Fri May  3 21:10:23 2002
+++ linux-2.4.17-fstune/fs/buffer.c	Wed Aug  7 20:50:56 2002
@@ -47,6 +47,7 @@
 #include <linux/highmem.h>
 #include <linux/module.h>
 #include <linux/completion.h>
+#include <linux/delay.h>
 
 #include <asm/uaccess.h>
 #include <asm/io.h>
@@ -76,6 +77,11 @@
 static spinlock_t lru_list_lock __cacheline_aligned_in_smp = SPIN_LOCK_UNLOCKED;
 static int nr_buffers_type[NR_LIST];
 static unsigned long size_buffers_type[NR_LIST];
+#ifdef CONFIG_FSTUNE
+static struct buffer_head **lru_hash_table; /* use with lru_list_lock */
+static struct buffer_head * __get_lru_hash_table(kdev_t dev, int block, int size);
+static kmem_cache_t *gw_array;
+#endif
 
 static struct buffer_head * unused_list;
 static int nr_unused_buffer_heads;
@@ -87,6 +93,11 @@
 
 /* This is used by some architectures to estimate available memory. */
 atomic_t buffermem_pages = ATOMIC_INIT(0);
+#ifdef CONFIG_FSTUNE
+static atomic_t nr_writing = ATOMIC_INIT(0);
+static atomic_t size_writing = ATOMIC_INIT(0);
+static atomic_t nr_waiting = ATOMIC_INIT(0);
+#endif
 
 /* Here is the parameter block for the bdflush process. If you add or
  * remove any of the parameters, make sure to update kernel/sysctl.c
@@ -113,12 +124,26 @@
 		int dummy5;	/* unused */
 	} b_un;
 	unsigned int data[N_PARAM];
+#ifdef CONFIG_FSTUNE
+} bdf_prm = {{30, 0, 0, 0, 5*HZ, 30*HZ, 70, 0, 0}};
+#else
 } bdf_prm = {{40, 0, 0, 0, 5*HZ, 30*HZ, 60, 0, 0}};
+#endif
 
 /* These are the min and max parameter values that we will allow to be assigned */
 int bdflush_min[N_PARAM] = {  0,  10,    5,   25,  0,   1*HZ,   0, 0, 0};
 int bdflush_max[N_PARAM] = {100,50000, 20000, 20000,10000*HZ, 6000*HZ, 100, 0, 0};
 
+#ifdef CONFIG_FSTUNE
+struct fstune_param fstune_param = {
+	dcache_limit: 35000,
+	unused_icache_limit: 15000,
+	icache_check_interval: 2 * HZ,
+	shrink_clean_pages: 128,
+};
+unsigned int fstune_stat[20];
+#endif
+
 void unlock_buffer(struct buffer_head *bh)
 {
 	clear_bit(BH_Wait_IO, &bh->b_state);
@@ -168,6 +193,17 @@
 	put_bh(bh);
 }
 
+#ifdef CONFIG_FSTUNE
+static void __end_buffer_io_sync(struct buffer_head *bh, int uptodate)
+{
+	atomic_dec(&nr_writing);
+	atomic_sub(bh->b_size,&size_writing);
+	mark_buffer_uptodate(bh, uptodate);
+	unlock_buffer(bh);
+	put_bh(bh);
+}
+#endif
+
 /*
  * The buffers have been marked clean and locked.  Just submit the dang
  * things.. 
@@ -176,7 +212,13 @@
 {
 	do {
 		struct buffer_head * bh = *array++;
+#ifdef CONFIG_FSTUNE
+		atomic_inc(&nr_writing);
+		atomic_add(bh->b_size,&size_writing);
+		bh->b_end_io = __end_buffer_io_sync;
+#else
 		bh->b_end_io = end_buffer_io_sync;
+#endif
 		submit_bh(WRITE, bh);
 	} while (--count);
 }
@@ -187,6 +229,135 @@
  * This must be called with the LRU lock held, and will
  * return without it!
  */
+#ifdef CONFIG_FSTUNE
+#define NRSYNC (32)
+#define NRSYNCG (512)
+
+static int write_some_buffers(kdev_t dev)
+{
+	struct buffer_head *next;
+	struct buffer_head *array[NRSYNC];
+	unsigned int count;
+	int nr;
+
+	next = lru_list[BUF_DIRTY];
+	nr = nr_buffers_type[BUF_DIRTY];
+	count = 0;
+
+	while (next && --nr >= 0) {
+		struct buffer_head * bh = next;
+		next = bh->b_next_free;
+
+		if (dev && bh->b_dev != dev)
+			continue;
+		if (test_and_set_bit(BH_Lock, &bh->b_state))
+			continue;
+		if (atomic_set_buffer_clean(bh)) {
+		   if (!count && (bh->b_size == PAGE_SIZE)) {
+			int zone_num;
+			int zone_min;
+			int zone_max;
+			int zone_self;
+			int i;
+			unsigned long zone;
+			unsigned long blk;
+			struct buffer_head * bhf;
+			struct buffer_head **array2 = NULL;
+
+			zone_num = (2048*1024)/bh->b_size;
+			zone = bh->b_blocknr / zone_num * zone_num;
+			if (zone_num > NRSYNCG) {
+				zone_num = NRSYNCG;
+				zone = bh->b_blocknr / zone_num * zone_num;
+			}
+			bhf = __get_lru_hash_table(bh->b_dev,bh->b_blocknr,bh->b_size);
+			if (!bhf) {
+				goto normal;
+			}
+			if (gw_array) {
+				array2 = (struct buffer_head **)kmem_cache_alloc(
+					gw_array, SLAB_ATOMIC);
+			}
+			if (!array2) {
+				goto normal;
+			}
+			zone_self = bh->b_blocknr - zone;
+			zone_min = zone_max = zone_self;
+			__refile_buffer(bh);
+			get_bh(bh);
+			array2[zone_self] = bh;
+			for (i= zone_self - 1; i >= 0; --i) {
+				blk = zone + i;
+				bhf = __get_lru_hash_table(bh->b_dev,blk,bh->b_size);
+				if (!bhf) {
+					break;
+				}
+				if (test_and_set_bit(BH_Lock, &bhf->b_state)) {
+					break;
+				}
+				if (!atomic_set_buffer_clean(bhf)) {
+					clear_bit(BH_Lock, &bhf->b_state);
+					break;
+				}
+				__refile_buffer(bhf);
+				get_bh(bhf);
+				array2[i] = bhf;
+				zone_min = i;
+			}
+			for (i= zone_self + 1; i < zone_num; i++) {
+				blk = zone + i;
+				bhf = __get_lru_hash_table(bh->b_dev,blk,bh->b_size);
+				if (!bhf) {
+					break;
+				}
+				if (test_and_set_bit(BH_Lock, &bhf->b_state)) {
+					break;
+				}
+				if (!atomic_set_buffer_clean(bhf)) {
+					clear_bit(BH_Lock, &bhf->b_state);
+					break;
+				}
+				__refile_buffer(bhf);
+				get_bh(bhf);
+				array2[i] = bhf;
+				zone_max = i;
+			}
+			spin_unlock(&lru_list_lock);
+			count = zone_max - zone_min + 1;
+			fstune_stat[2]++;
+			fstune_stat[3] += count;
+			write_locked_buffers(array2+zone_min, count);
+			kmem_cache_free(gw_array, array2);
+			count = 0;
+			return -EAGAIN;
+		   } else {
+normal:
+			__refile_buffer(bh);
+			get_bh(bh);
+			array[count++] = bh;
+			if (count < NRSYNC)
+				continue;
+
+			spin_unlock(&lru_list_lock);
+			fstune_stat[0]++;
+			fstune_stat[1] += count;
+			write_locked_buffers(array, count);
+			return -EAGAIN;
+		   }
+		}
+		unlock_buffer(bh);
+		__refile_buffer(bh);
+	}
+	spin_unlock(&lru_list_lock);
+
+	if (count) {
+		fstune_stat[0]++;
+		fstune_stat[1] += count;
+		write_locked_buffers(array, count);
+	}
+	return 0;
+}
+#else
 #define NRSYNC (32)
 static int write_some_buffers(kdev_t dev)
 {
@@ -226,6 +397,7 @@
 		write_locked_buffers(array, count);
 	return 0;
 }
+#endif
 
 /*
  * Write out all buffers on the dirty list.
@@ -467,6 +639,9 @@
 	 (((block)<<(bh_hash_shift - 6)) ^ ((block) >> 13) ^ \
 	  ((block) << (bh_hash_shift - 12))))
 #define hash(dev,block) hash_table[(_hashfn(HASHDEV(dev),block) & bh_hash_mask)]
+#ifdef CONFIG_FSTUNE
+#define lru_hash(dev,block) lru_hash_table[(_hashfn(HASHDEV(dev),block) & bh_hash_mask)]
+#endif
 
 static inline void __insert_into_hash_list(struct buffer_head *bh)
 {
@@ -492,6 +667,26 @@
 	}
 }
 
+#ifdef CONFIG_FSTUNE
+static __inline__ void __lru_hash_link(struct buffer_head *bh, struct buffer_head **head)
+{
+	if ((bh->b_next_lru = *head) != NULL)
+		bh->b_next_lru->b_pprev_lru = &bh->b_next_lru;
+	*head = bh;
+	bh->b_pprev_lru = head;
+}
+
+static __inline__ void __lru_hash_unlink(struct buffer_head *bh)
+{
+	if (bh->b_pprev_lru) {
+		if (bh->b_next_lru)
+			bh->b_next_lru->b_pprev_lru = bh->b_pprev_lru;
+		*(bh->b_pprev_lru) = bh->b_next_lru;
+		bh->b_pprev_lru = NULL;
+	}
+}
+#endif
+
 static void __insert_into_lru_list(struct buffer_head * bh, int blist)
 {
 	struct buffer_head **bhp = &lru_list[blist];
@@ -508,6 +703,13 @@
 	(*bhp)->b_prev_free = bh;
 	nr_buffers_type[blist]++;
 	size_buffers_type[blist] += bh->b_size;
+
+#ifdef CONFIG_FSTUNE
+	if (blist == BUF_DIRTY) {
+		struct buffer_head **head = &lru_hash(bh->b_dev, bh->b_blocknr);
+		__lru_hash_link(bh, head);
+	}
+#endif
 }
 
 static void __remove_from_lru_list(struct buffer_head * bh)
@@ -517,6 +719,11 @@
 		struct buffer_head *prev = bh->b_prev_free;
 		int blist = bh->b_list;
 
+#ifdef CONFIG_FSTUNE
+		if (blist == BUF_DIRTY) {
+			__lru_hash_unlink(bh);
+		}
+#endif
 		prev->b_next_free = next;
 		next->b_prev_free = prev;
 		if (lru_list[blist] == bh) {
@@ -573,6 +780,21 @@
 	return bh;
 }
 
+
+#ifdef CONFIG_FSTUNE
+static struct buffer_head * __get_lru_hash_table(kdev_t dev, int block, int size)
+{
+	struct buffer_head *bh = lru_hash(dev, block);
+
+	for (; bh; bh = bh->b_next_lru)
+		if (bh->b_blocknr == block	&&
+		    bh->b_size    == size	&&
+		    bh->b_dev     == dev)
+			break;
+	return bh;
+}
+#endif
+
 void buffer_insert_inode_queue(struct buffer_head *bh, struct inode *inode)
 {
 	spin_lock(&lru_list_lock);
@@ -1036,13 +1258,22 @@
 	unsigned long dirty, tot, hard_dirty_limit, soft_dirty_limit;
 
 	dirty = size_buffers_type[BUF_DIRTY] >> PAGE_SHIFT;
+#ifdef CONFIG_FSTUNE
+	dirty += size_buffers_type[BUF_LOCKED] >> (PAGE_SHIFT+1);
+#else
 	dirty += size_buffers_type[BUF_LOCKED] >> PAGE_SHIFT;
+#endif
 	tot = nr_free_buffer_pages();
 
 	dirty *= 100;
 	soft_dirty_limit = tot * bdf_prm.b_un.nfract;
 	hard_dirty_limit = tot * bdf_prm.b_un.nfract_sync;
 
+#ifdef CONFIG_FSTUNE
+	fstune_stat[4] = dirty * 1000 / tot;
+	fstune_stat[5] = tot;
+#endif
+
 	/* First, check for the "real" dirty limit. */
 	if (dirty > soft_dirty_limit) {
 		if (dirty > hard_dirty_limit && !(current->flags & PF_NOIO))
@@ -1063,10 +1294,18 @@
 void balance_dirty(void)
 {
 	int state = balance_dirty_state();
-
 	if (state < 0)
 		return;
 
+#ifdef CONFIG_FSTUNE
+	if (state == 0) {
+		int request_size = atomic_read(&size_writing);
+		if (request_size > 512 * 4096) {
+			return;
+		}
+	}
+#endif
+
 	/* If we're getting into imbalance, start write-out */
 	spin_lock(&lru_list_lock);
 	write_some_buffers(NODEV);
@@ -1184,6 +1423,59 @@
 	return NULL;
 }
 
+#ifdef CONFIG_FSTUNE_BREAD_ZONE
+struct buffer_head * bread_zone(kdev_t dev, int block, int size)
+{
+	struct buffer_head * bh;
+	struct buffer_head * bhs[32];
+	int from,i,b,m,n;
+
+	bh = getblk(dev, block, size);
+	touch_buffer(bh);
+	if (buffer_uptodate(bh))
+		return bh;
+
+	from = (block >> 5) << 5;
+	b = block - from;
+	for (i=0; i< 32; i++) bhs[i] = NULL;
+	bhs[b] = bh;
+	for (i = b+1; i< 32; i++) {
+		bhs[i] = getblk(dev, from + i, size);
+		touch_buffer(bhs[i]);
+		if (buffer_uptodate(bh)) {
+			put_bh(bh);
+			break;
+		}
+	}
+	n = i;
+	for (i = b-1; i >= 0; --i) {
+		bhs[i] = getblk(dev, from + i, size);
+		touch_buffer(bhs[i]);
+		if (buffer_uptodate(bh)) {
+			put_bh(bh);
+			break;
+		}
+	}
+	m = i+1;
+
+	ll_rw_block(READ, n - m, bhs + m);
+	for (i=m; i< n; i++) {
+		wait_on_buffer(bhs[i]);
+	}
+	if (!buffer_uptodate(bh)) {
+		brelse(bh);
+		bh = NULL;
+	}
+	for (i=m; i< n; i++) {
+		if (i != b) {
+			put_bh(bhs[i]);
+		}
+	}
+	return bh;
+}
+#endif
+
+
 /*
  * Note: the caller should wake up the buffer_wait list if needed.
  */
@@ -1304,6 +1596,7 @@
 		bh->b_state = 0;
 		bh->b_next_free = NULL;
 		bh->b_pprev = NULL;
+		bh->b_pprev_lru = NULL;
 		atomic_set(&bh->b_count, 0);
 		bh->b_size = size;
 
@@ -1373,7 +1666,9 @@
  *
  */
 
-int try_to_release_page(struct page * page, int gfp_mask)
+static int try_to_free_buffers_sub(struct page *, unsigned int, int);
+
+int try_to_release_page(struct page * page, int gfp_mask,int clean_only)
 {
 	if (!PageLocked(page))
 		BUG();
@@ -1390,7 +1685,7 @@
 	 */
 	return 0;
 try_to_free:	
-	return try_to_free_buffers(page, gfp_mask);
+	return try_to_free_buffers_sub(page, gfp_mask, clean_only);
 }
 
 /*
@@ -1436,7 +1731,7 @@
 	 * instead.
 	 */
 	if (!offset) {
-		if (!try_to_release_page(page, 0))
+		if (!try_to_release_page(page, 0, 0))
 			return 0;
 	}
 
@@ -2263,6 +2558,10 @@
 	do {
 		struct buffer_head *next = bh->b_this_page;
 		submit_bh(rw, bh);
+#ifdef CONFIG_FSTUNE
+		if (rw == WRITE)
+			fstune_stat[8] ++;
+#endif
 		bh = next;
 	} while (bh != head);
 	return 0;
@@ -2487,7 +2786,7 @@
  *       obtain a reference to a buffer head within a page.  So we must
  *	 lock out all of these paths to cleanly toss the page.
  */
-int try_to_free_buffers(struct page * page, unsigned int gfp_mask)
+static int try_to_free_buffers_sub(struct page * page, unsigned int gfp_mask, int clean_only)
 {
 	struct buffer_head * tmp, * bh = page->buffers;
 
@@ -2534,6 +2833,11 @@
 	write_unlock(&hash_table_lock);
 	spin_unlock(&lru_list_lock);
 	gfp_mask = pf_gfp_mask(gfp_mask);
+#ifdef CONFIG_FSTUNE
+	if (clean_only) {
+		return 0;
+	}
+#endif
 	if (gfp_mask & __GFP_IO) {
 		if ((gfp_mask & __GFP_HIGHIO) || !PageHighMem(page)) {
 			if (sync_page_buffers(bh)) {
@@ -2547,6 +2851,10 @@
 		wakeup_bdflush();
 	return 0;
 }
+
+int try_to_free_buffers(struct page * page, unsigned int gfp_mask) {
+	return try_to_free_buffers_sub(page,gfp_mask, 0);
+}
 EXPORT_SYMBOL(try_to_free_buffers);
 
 /* ================== Debugging =================== */
@@ -2634,7 +2942,27 @@
 
 		hash_table = (struct buffer_head **)
 		    __get_free_pages(GFP_ATOMIC, order);
+#ifdef CONFIG_FSTUNE
+		lru_hash_table = (struct buffer_head **)
+		    __get_free_pages(GFP_ATOMIC, order);
+#endif
 	} while (hash_table == NULL && --order > 0);
+#ifdef CONFIG_FSTUNE
+	if (hash_table && !lru_hash_table) {
+		unsigned long tmp;
+
+		--order;
+		nr_hash = (PAGE_SIZE << order) / sizeof(struct buffer_head *);
+		bh_hash_mask = (nr_hash - 1);
+
+		tmp = nr_hash;
+		bh_hash_shift = 0;
+		while((tmp >>= 1UL) != 0UL)
+			bh_hash_shift++;
+		lru_hash_table = hash_table + nr_hash;
+	}
+#endif
+
 	printk("Buffer-cache hash table entries: %d (order: %d, %ld bytes)\n",
 	       nr_hash, order, (PAGE_SIZE << order));
 
@@ -2644,11 +2972,24 @@
 	/* Setup hash chains. */
 	for(i = 0; i < nr_hash; i++)
 		hash_table[i] = NULL;
+#ifdef CONFIG_FSTUNE
+	for(i = 0; i < nr_hash; i++)
+		lru_hash_table[i] = NULL;
+#endif
 
 	/* Setup lru lists. */
 	for(i = 0; i < NR_LIST; i++)
 		lru_list[i] = NULL;
 
+#ifdef CONFIG_FSTUNE
+	gw_array = kmem_cache_create(
+			"gw_array",
+			sizeof(void *) * NRSYNCG,
+			0,
+			SLAB_HWCACHE_ALIGN,
+			NULL, NULL);
+#endif
+
 }
 
 
diff -ru linux-2.4.17/fs/dcache.c linux-2.4.17-fstune/fs/dcache.c
--- linux-2.4.17/fs/dcache.c	Fri May  3 21:10:23 2002
+++ linux-2.4.17-fstune/fs/dcache.c	Wed Aug  7 19:36:46 2002
@@ -565,7 +565,19 @@
 	if (!(gfp_mask & __GFP_FS))
 		return 0;
 
+#ifdef CONFIG_FSTUNE
+	if (priority == 0) {	/* special case */
+		if (dentry_stat.nr_dentry > fstune_param.dcache_limit) { /* XXX suz */
+			count = dentry_stat.nr_unused / 6;
+		} else {
+			return 0;
+		}
+	} else {
+		count = dentry_stat.nr_unused / priority;
+	}
+#else
 	count = dentry_stat.nr_unused / priority;
+#endif
 
 	prune_dcache(count);
 	kmem_cache_shrink(dentry_cache);
@@ -588,7 +600,15 @@
 {
 	char * str;
 	struct dentry *dentry;
+#ifdef CONFIG_FSTUNE
+	int nr_dentry = dentry_stat.nr_dentry;
+	int nr_unused = dentry_stat.nr_unused;
 
+	if (nr_dentry > fstune_param.dcache_limit) { /* XXX suz */
+		shrink_dcache_memory(6,GFP_KERNEL);
+		shrink_icache_memory(3,GFP_KERNEL);
+	}
+#endif
 	dentry = kmem_cache_alloc(dentry_cache, GFP_KERNEL); 
 	if (!dentry)
 		return NULL;
diff -ru linux-2.4.17/fs/ext3/balloc.c linux-2.4.17-fstune/fs/ext3/balloc.c
--- linux-2.4.17/fs/ext3/balloc.c	Sat Nov 10 07:25:04 2001
+++ linux-2.4.17-fstune/fs/ext3/balloc.c	Wed Aug  7 19:36:46 2002
@@ -86,12 +86,17 @@
 	struct ext3_group_desc * gdp;
 	struct buffer_head * bh = NULL;
 	int retval = -EIO;
-	
+#ifdef CONFIG_FSTUNE_BREAD_ZONE
+	extern struct buffer_head * bread_zone(kdev_t dev, int block, int size);
+#else
+	#define bread_zone      bread
+#endif
+
 	gdp = ext3_get_group_desc (sb, block_group, NULL);
 	if (!gdp)
 		goto error_out;
 	retval = 0;
-	bh = bread (sb->s_dev,
+	bh = bread_zone (sb->s_dev,
 			le32_to_cpu(gdp->bg_block_bitmap), sb->s_blocksize);
 	if (!bh) {
 		ext3_error (sb, "read_block_bitmap",
diff -ru linux-2.4.17/fs/ext3/inode.c linux-2.4.17-fstune/fs/ext3/inode.c
--- linux-2.4.17/fs/ext3/inode.c	Fri May  3 21:10:23 2002
+++ linux-2.4.17-fstune/fs/ext3/inode.c	Wed Aug  7 19:36:46 2002
@@ -1995,6 +1995,11 @@
 	unsigned long desc;
 	unsigned long offset;
 	struct ext3_group_desc * gdp;
+#ifdef CONFIG_FSTUNE_BREAD_ZONE
+	extern struct buffer_head * bread_zone(kdev_t dev, int block, int size);
+#else
+	#define bread_zone	bread
+#endif
 		
 	if ((inode->i_ino != EXT3_ROOT_INO &&
 		inode->i_ino != EXT3_ACL_IDX_INO &&
@@ -2030,7 +2035,7 @@
 		EXT3_INODE_SIZE(inode->i_sb);
 	block = le32_to_cpu(gdp[desc].bg_inode_table) +
 		(offset >> EXT3_BLOCK_SIZE_BITS(inode->i_sb));
-	if (!(bh = bread (inode->i_dev, block, inode->i_sb->s_blocksize))) {
+	if (!(bh = bread_zone(inode->i_dev, block, inode->i_sb->s_blocksize))) {
 		ext3_error (inode->i_sb, "ext3_get_inode_loc",
 			    "unable to read inode block - "
 			    "inode=%lu, block=%lu", inode->i_ino, block);
diff -ru linux-2.4.17/fs/inode.c linux-2.4.17-fstune/fs/inode.c
--- linux-2.4.17/fs/inode.c	Fri May  3 21:10:23 2002
+++ linux-2.4.17-fstune/fs/inode.c	Wed Aug  7 19:36:46 2002
@@ -60,6 +60,10 @@
 static struct list_head *inode_hashtable;
 static LIST_HEAD(anon_hash_chain); /* for inodes with NULL i_sb */
 
+#ifdef CONFIG_FSTUNE
+static void check_unused_inode(void);
+#endif
+
 /*
  * A simple spinlock to protect the list manipulations.
  *
@@ -711,7 +715,6 @@
 int shrink_icache_memory(int priority, int gfp_mask)
 {
 	int count = 0;
-
 	/*
 	 * Nasty deadlock avoidance..
 	 *
@@ -722,13 +725,41 @@
 	if (!(gfp_mask & __GFP_FS))
 		return 0;
 
+#ifdef CONFIG_FSTUNE
+	if (priority == 0) {
+		if (inodes_stat.nr_unused > fstune_param.unused_icache_limit) { /* XXX suz */
+			count = inodes_stat.nr_unused / 6;
+		} else {
+			return 0;
+		}
+	} else {
+		count = inodes_stat.nr_unused / priority;
+	}
+#else
 	count = inodes_stat.nr_unused / priority;
+#endif
 
 	prune_icache(count);
 	kmem_cache_shrink(inode_cachep);
 	return 0;
 }
 
+#ifdef CONFIG_FSTUNE
+static unsigned long last_check_time;
+
+static void check_unused_inode() {
+	extern unsigned fstune_stat[];
+	fstune_stat[6] = inodes_stat.nr_inodes;
+	fstune_stat[7] = inodes_stat.nr_unused;
+	if (jiffies - last_check_time  > fstune_param.icache_check_interval) {
+		if (inodes_stat.nr_unused > fstune_param.unused_icache_limit) { /* XXX suz */
+			shrink_icache_memory(6, SLAB_KERNEL);
+		}
+		last_check_time = jiffies;
+	}
+}
+#endif
+
 /*
  * Called with the inode lock held.
  * NOTE: we are not increasing the inode-refcount, you must call __iget()
@@ -808,7 +839,10 @@
 	struct inode * inode;
 
 	spin_lock_prefetch(&inode_lock);
-	
+
+#ifdef CONFIG_FSTUNE
+	check_unused_inode();
+#endif	
 	inode = alloc_inode();
 	if (inode)
 	{
@@ -838,6 +872,9 @@
 {
 	struct inode * inode;
 
+#ifdef CONFIG_FSTUNE
+	check_unused_inode();
+#endif
 	inode = alloc_inode();
 	if (inode) {
 		struct inode * old;
diff -ru linux-2.4.17/fs/jbd/commit.c linux-2.4.17-fstune/fs/jbd/commit.c
--- linux-2.4.17/fs/jbd/commit.c	Fri May  3 21:10:23 2002
+++ linux-2.4.17-fstune/fs/jbd/commit.c	Wed Aug  7 21:55:44 2002
@@ -23,6 +23,9 @@
 
 extern spinlock_t journal_datalist_lock;
 
+#ifdef CONFIG_FSTUNE
+unsigned int jbd_stat[20];
+#endif
 /*
  * Default IO end handler for temporary BJ_IO buffer_heads.
  */
@@ -176,7 +179,13 @@
 	 * onto ->t_datalist, so we have to keep looping back to write_out_data
 	 * until we *know* that the list is empty.
 	 */
+#ifdef CONFIG_FSTUNE
+	jbd_stat[0]++;
+#endif
 write_out_data:
+#ifdef CONFIG_FSTUNE
+	jbd_stat[1]++;
+#endif
 
 	/*
 	 * Cleanup any flushed data buffers from the data list.  Even in
@@ -228,8 +237,12 @@
 		jbd_debug(2, "submit %d writes\n", bufs);
 		spin_unlock(&journal_datalist_lock);
 		unlock_journal(journal);
-		if (bufs)
+		if (bufs) {
 			ll_rw_block(WRITE, bufs, wbuf);
+#ifdef CONFIG_FSTUNE
+			jbd_stat[2]++;
+#endif
+		}
 		if (current->need_resched)
 			schedule();
 		journal_brelse_array(wbuf, bufs);
diff -ru linux-2.4.17/fs/proc/proc_misc.c linux-2.4.17-fstune/fs/proc/proc_misc.c
--- linux-2.4.17/fs/proc/proc_misc.c	Wed Nov 21 14:29:09 2001
+++ linux-2.4.17-fstune/fs/proc/proc_misc.c	Wed Aug  7 20:27:30 2002
@@ -65,6 +65,11 @@
 extern int get_ds1286_status(char *);
 #endif
 
+#ifdef CONFIG_FSTUNE
+extern unsigned int fstune_stat[];
+extern unsigned int jbd_stat[];
+#endif
+
 static int proc_calc_metrics(char *page, char **start, off_t off,
 				 int count, int *eof, int len)
 {
@@ -310,6 +315,21 @@
 		xtime.tv_sec - jif / HZ,
 		total_forks);
 
+#ifdef CONFIG_FSTUNE
+	len += sprintf(page + len,
+		"fstune %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u\n",
+		fstune_stat[0],fstune_stat[1],fstune_stat[2],fstune_stat[3],
+		fstune_stat[4], fstune_stat[5],fstune_stat[6],fstune_stat[7],
+		fstune_stat[8], fstune_stat[9],fstune_stat[10],fstune_stat[11],
+		fstune_stat[12], fstune_stat[13],fstune_stat[14],fstune_stat[15]);
+	len += sprintf(page + len,
+		"jbd %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u\n",
+		jbd_stat[0],jbd_stat[1],jbd_stat[2],jbd_stat[3],
+		jbd_stat[4], jbd_stat[5],jbd_stat[6],jbd_stat[7],
+		jbd_stat[8], jbd_stat[9],jbd_stat[10],jbd_stat[11],
+		jbd_stat[12], jbd_stat[13],jbd_stat[14],jbd_stat[15]);
+#endif
+
 	return proc_calc_metrics(page, start, off, count, eof, len);
 }
 
diff -ru linux-2.4.17/include/linux/fs.h linux-2.4.17-fstune/include/linux/fs.h
--- linux-2.4.17/include/linux/fs.h	Fri May  3 21:10:24 2002
+++ linux-2.4.17-fstune/include/linux/fs.h	Wed Aug  7 19:39:35 2002
@@ -254,6 +254,8 @@
 	struct buffer_head *b_reqnext;	/* request queue */
 
 	struct buffer_head **b_pprev;	/* doubly linked list of hash-queue */
+	struct buffer_head *b_next_lru;	/* LRU Hash queue list */
+	struct buffer_head **b_pprev_lru; /* doubly linked list of LRU hash-queue */
 	char * b_data;			/* pointer to data block */
 	struct page *b_page;		/* the page this bh is mapped to */
 	void (*b_end_io)(struct buffer_head *bh, int uptodate); /* I/O completion */
@@ -1374,7 +1376,7 @@
 typedef int (get_block_t)(struct inode*,long,struct buffer_head*,int);
 
 /* Generic buffer handling for block filesystems.. */
-extern int try_to_release_page(struct page * page, int gfp_mask);
+extern int try_to_release_page(struct page * page, int gfp_mask,int clean_only);
 extern int discard_bh_page(struct page *, unsigned long, int);
 #define block_flushpage(page, offset) discard_bh_page(page, offset, 1)
 #define block_invalidate_page(page) discard_bh_page(page, 0, 0)
@@ -1582,6 +1584,15 @@
 	dput(d2);
 }
 
+
+struct fstune_param {
+	int	dcache_limit;
+	int	unused_icache_limit;
+	int	icache_check_interval;
+	int	shrink_clean_pages;
+};
+extern struct fstune_param fstune_param;
+
 #endif /* __KERNEL__ */
 
 #endif /* _LINUX_FS_H */
diff -ru linux-2.4.17/mm/vmscan.c linux-2.4.17-fstune/mm/vmscan.c
--- linux-2.4.17/mm/vmscan.c	Fri May  3 21:10:25 2002
+++ linux-2.4.17-fstune/mm/vmscan.c	Wed Aug  7 19:36:47 2002
@@ -333,8 +333,8 @@
 	return 0;
 }
 
-static int FASTCALL(shrink_cache(int nr_pages, zone_t * classzone, unsigned int gfp_mask, int priority));
-static int shrink_cache(int nr_pages, zone_t * classzone, unsigned int gfp_mask, int priority)
+static int FASTCALL(shrink_cache(int nr_pages, zone_t * classzone, unsigned int gfp_mask, int priority,int clean_only));
+static int shrink_cache(int nr_pages, zone_t * classzone, unsigned int gfp_mask, int priority, int clean_only)
 {
 	struct list_head * entry;
 	int max_scan = nr_inactive_pages / priority;
@@ -369,8 +369,13 @@
 		if (unlikely(!page_count(page)))
 			continue;
 
+#ifdef CONFIG_FSTUNE
+		if (!clean_only && !memclass(page->zone, classzone))
+			continue;
+#else
 		if (!memclass(page->zone, classzone))
 			continue;
+#endif
 
 		/* Racy check to avoid trylocking when not worthwhile */
 		if (!page->buffers && (page_count(page) != 1 || !page->mapping))
@@ -381,6 +386,11 @@
 		 * Move it to the back of the list.
 		 */
 		if (unlikely(TryLockPage(page))) {
+#ifndef CONFIG_FSTUNE
+			if (clean_only) {
+				continue;
+			}
+#endif
 			if (PageLaunder(page) && (gfp_mask & __GFP_FS)) {
 				page_cache_get(page);
 				spin_unlock(&pagemap_lru_lock);
@@ -402,6 +412,12 @@
 			 */
 			int (*writepage)(struct page *);
 
+#ifdef CONFIG_FSTUNE
+			if (clean_only) {
+				UnlockPage(page);
+				continue;
+			}
+#endif
 			writepage = page->mapping->a_ops->writepage;
 			if ((gfp_mask & __GFP_FS) && writepage) {
 				ClearPageDirty(page);
@@ -428,7 +444,7 @@
 			/* avoid to free a locked page */
 			page_cache_get(page);
 
-			if (try_to_release_page(page, gfp_mask)) {
+			if (try_to_release_page(page, gfp_mask,clean_only)) {
 				if (!page->mapping) {
 					/*
 					 * We must not allow an anon page
@@ -475,6 +491,10 @@
 			spin_unlock(&pagecache_lock);
 			UnlockPage(page);
 page_mapped:
+#ifdef CONFIG_FSTUNE
+			if (clean_only)
+				continue;
+#endif
 			if (--max_mapped >= 0)
 				continue;
 
@@ -524,6 +544,7 @@
 	return nr_pages;
 }
 
+
 /*
  * This moves pages from the active list to
  * the inactive list.
@@ -570,7 +591,21 @@
 	ratio = (unsigned long) nr_pages * nr_active_pages / ((nr_inactive_pages + 1) * 2);
 	refill_inactive(ratio);
 
-	nr_pages = shrink_cache(nr_pages, classzone, gfp_mask, priority);
+#ifdef CONFIG_FSTUNE
+	if (classzone->free_pages < classzone->pages_high * 3) {
+		int tmp;
+		shrink_icache_memory(0, gfp_mask);
+		tmp = shrink_cache(fstune_param.shrink_clean_pages, classzone, gfp_mask, priority, 1); /* XXX suz */
+		nr_pages -= (128 - tmp);
+		if (nr_pages <= 0)
+			return 0;
+	}
+	if (classzone->free_pages > classzone->pages_high) {
+		return 0;
+	}
+#endif
+	
+	nr_pages = shrink_cache(nr_pages, classzone, gfp_mask, priority, 0);
 	if (nr_pages <= 0)
 		return 0;
 
