patch-1.3.34 linux/fs/buffer.c

Next file: linux/fs/proc/link.c
Previous file: linux/drivers/sound/uart6850.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v1.3.33/linux/fs/buffer.c linux/fs/buffer.c
@@ -23,6 +23,7 @@
 #include <linux/locks.h>
 #include <linux/errno.h>
 #include <linux/malloc.h>
+#include <linux/swapctl.h>
 
 #include <asm/system.h>
 #include <asm/segment.h>
@@ -43,6 +44,10 @@
 static struct buffer_head ** hash_table;
 struct buffer_head ** buffer_pages;
 static struct buffer_head * lru_list[NR_LIST] = {NULL, };
+/* next_to_age is an array of pointers into the lru lists, used to
+   cycle through the buffers aging their contents when deciding which
+   buffers to discard when more memory is needed */
+static struct buffer_head * next_to_age[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;
@@ -177,9 +182,6 @@
 			 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 list %d %s %lu\n",
-				    nlist, kdevname(bh->b_dev), bh->b_blocknr);
 				  continue;
 			  }
 			 /* Don't write clean buffers.  Don't write ANY buffers
@@ -302,8 +304,13 @@
 
 	if (lru_list[bh->b_list] == bh)
 		 lru_list[bh->b_list] = bh->b_next_free;
-	if(lru_list[bh->b_list] == bh)
+	if (lru_list[bh->b_list] == bh)
 		 lru_list[bh->b_list] = NULL;
+	if (next_to_age[bh->b_list] == bh)
+		next_to_age[bh->b_list] = bh->b_next_free;
+	if (next_to_age[bh->b_list] == bh)
+		next_to_age[bh->b_list] = NULL;
+
 	bh->b_next_free = bh->b_prev_free = NULL;
 }
 
@@ -347,6 +354,8 @@
 		return;
 	if (bh == lru_list[bh->b_list]) {
 		lru_list[bh->b_list] = bh->b_next_free;
+		if (next_to_age[bh->b_list] == bh)
+			next_to_age[bh->b_list] = bh->b_next_free;
 		return;
 	}
 	if(bh->b_dev == B_FREE)
@@ -358,6 +367,8 @@
 		lru_list[bh->b_list] = bh;
 		lru_list[bh->b_list]->b_prev_free = bh;
 	};
+	if (!next_to_age[bh->b_list])
+		next_to_age[bh->b_list] = bh;
 
 	bh->b_next_free = lru_list[bh->b_list];
 	bh->b_prev_free = lru_list[bh->b_list]->b_prev_free;
@@ -373,8 +384,7 @@
 
 	isize = BUFSIZE_INDEX(bh->b_size);	
 	bh->b_dev = B_FREE;  /* So it is obvious we are on the free list */
-/* add to back of free list */
-
+	/* add to back of free list */
 	if(!free_list[isize]) {
 		free_list[isize] = bh;
 		bh->b_prev_free = bh;
@@ -389,16 +399,17 @@
 
 static inline void insert_into_queues(struct buffer_head * bh)
 {
-/* put at end of free list */
-
+	/* put at end of free list */
         if(bh->b_dev == B_FREE) {
 		put_last_free(bh);
 		return;
-	};
+	}
 	if(!lru_list[bh->b_list]) {
 		lru_list[bh->b_list] = bh;
 		bh->b_prev_free = bh;
-	};
+	}
+	if (!next_to_age[bh->b_list])
+		next_to_age[bh->b_list] = 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;
@@ -672,7 +683,7 @@
 	
 	/* Too bad, that was not enough. Try a little harder to grow some. */
 	
-	if (nr_free_pages > 5) {
+	if (nr_free_pages > min_free_pages + 5) {
 		if (grow_buffers(GFP_BUFFER, size)) {
 	                needed -= PAGE_SIZE;
 			goto repeat0;
@@ -713,6 +724,7 @@
 		if (bh->b_uptodate && !bh->b_dirt)
 			 put_last_lru(bh);
 		if(!bh->b_dirt) bh->b_flushtime = 0;
+		bh->b_touched = 1;
 		return bh;
 	}
 
@@ -733,6 +745,8 @@
 	bh->b_flushtime=0;
 	bh->b_req=0;
 	bh->b_reuse=0;
+	bh->b_touched = 1;
+	bh->b_has_aged = 0;
 	bh->b_dev=dev;
 	bh->b_blocknr=block;
 	insert_into_queues(bh);
@@ -800,10 +814,10 @@
 		if (--buf->b_count)
 			return;
 		wake_up(&buffer_wait);
-#if 0
 		if (buf->b_reuse) {
 			buf->b_reuse = 0;
-			if (!buf->b_lock && !buf->b_dirt && !buf->b_wait) {
+			if (!buf->b_lock && !buf->b_dirt && 
+			    !buf->b_wait && buf->b_uptodate) {
 				if(buf->b_dev == B_FREE)
 					panic("brelse: Wrong list");
 				remove_from_queues(buf);
@@ -811,7 +825,6 @@
 				put_last_free(buf);
 			}
 		}
-#endif
 		return;
 	}
 	printk("VFS: brelse: Trying to free free buffer\n");
@@ -1273,7 +1286,8 @@
  * try_to_free() checks if all the buffers on this particular page
  * are unused, and free's the page if so.
  */
-static int try_to_free(struct buffer_head * bh, struct buffer_head ** bhp)
+static int try_to_free(struct buffer_head * bh, struct buffer_head ** bhp,
+		       int priority)
 {
 	unsigned long page;
 	struct buffer_head * tmp, * p;
@@ -1288,6 +1302,8 @@
 			return 0;
 		if (tmp->b_count || tmp->b_dirt || tmp->b_lock || tmp->b_wait)
 			return 0;
+		if (priority && tmp->b_touched)
+			return 0;
 		tmp = tmp->b_this_page;
 	} while (tmp != bh);
 	tmp = bh;
@@ -1311,6 +1327,38 @@
 	return !mem_map[MAP_NR(page)].count;
 }
 
+/* Age buffers on a given page, according to whether they have been
+   visited recently or not. */
+static inline void age_buffer(struct buffer_head *bh)
+{
+	struct buffer_head *tmp = bh;
+	int touched = 0;
+
+	/*
+	 * When we age a page, we mark all other buffers in the page
+	 * with the "has_aged" flag.  Then, when these aliased buffers
+	 * come up for aging, we skip them until next pass.  This
+	 * ensures that a page full of multiple buffers only gets aged
+	 * once per pass through the lru lists. 
+	 */
+	if (bh->b_has_aged) {
+		bh->b_has_aged = 0;
+		return;
+	}
+	
+	do {
+		touched |= tmp->b_touched;
+		tmp->b_touched = 0;
+		tmp = tmp->b_this_page;
+		tmp->b_has_aged = 1;
+	} while (tmp != bh);
+	bh->b_has_aged = 0;
+
+	if (touched) 
+		touch_page((unsigned long) bh->b_data);
+	else
+		age_page((unsigned long) bh->b_data);
+}
 
 /*
  * Consult the load average for buffers and decide whether or not
@@ -1361,11 +1409,12 @@
 		  }
 	return 0;
 }
+
 /*
  * Try to free up some pages by shrinking the buffer-cache
  *
  * Priority tells the routine how hard to try to shrink the
- * buffers: 3 means "don't bother too much", while a value
+ * buffers: 6 means "don't bother too much", while a value
  * of 0 means "we'd better get some free pages now".
  *
  * "limit" is meant to limit the shrink-action only to pages
@@ -1406,10 +1455,12 @@
 		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))
+			if (!age_of((unsigned long) bh->b_data) &&
+			    try_to_free(bh, &bh, 6))
 				 return 1;
-			if(!bh) break; /* Some interrupt must have used it after we
-					  freed the page.  No big deal - keep looking */
+			if(!bh) break;
+			/* Some interrupt must have used it after we
+			   freed the page.  No big deal - keep looking */
 		}
 	}
 	
@@ -1417,12 +1468,19 @@
 	
 	for(nlist = 0; nlist < NR_LIST; nlist++) {
 	repeat1:
-		if(priority > 3 && nlist == BUF_SHARED) continue;
-		bh = lru_list[nlist];
-		if(!bh) continue;
-		i = 2*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(priority > 2 && nlist == BUF_SHARED) continue;
+		i = nr_buffers_type[nlist];
+		i = ((BUFFEROUT_WEIGHT * i) >> 10) >> priority;
+		for ( ; i > 0; i-- ) {
+			bh = next_to_age[nlist];
+			if (!bh)
+				break;
+			next_to_age[nlist] = bh->b_next_free;
+
+			/* First, age the buffer. */
+			age_buffer(bh);
+			/* 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;
@@ -1439,7 +1497,13 @@
 				bh->b_count--;
 				continue;
 			}
-			if (try_to_free(bh, &bh))
+			/* At priority 6, only consider really old
+                           (age==0) buffers for reclaiming.  At
+                           priority 0, consider any buffers. */
+			if ((age_of((unsigned long) bh->b_data) >>
+			     (6-priority)) > 0)
+				continue;				
+			if (try_to_free(bh, &bh, 0))
 				 return 1;
 			if(!bh) break;
 		}

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov with Sam's (original) version
of this