flexible refill_freelist 2.0.30 patch

Bill Hawes (whawes@star.net)
Wed, 09 Jul 1997 15:12:43 -0400


This is a multi-part message in MIME format.
--------------7C56437F91E80250C05549CB
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit

The attached patch against 2.0.30 is a refinement of the flexible
refill_freelist I previously posted. It adjusts the amount of memory to
be freed based on the current number of buffers, so that no more than
25% of the buffers will be released per invocation. In addition, the
"flexible refill" policy can be toggled using one of the bdflush
parameters, so it's easy to test the effects of the new algorithm.
Flexible refill can be turned off using the update -4 0 command and
turned back on with update -4 1 (it's on by default.)

In testing with simultaneous compiles under low memory the flexible
refill speeds up operations substantially, and should almost never need
to wake up bdflush.

The patch also includes one other minor fix: in getblk the b_list field
wasn't being initialized for new buffers, so that clean buffers
previously released from the locked list were being put back on the
locked list. This patch should be used in addition to the my previous
fixes for recover_reusable_buffer_heads and refile_buffer.

Regards,
Bill
--------------7C56437F91E80250C05549CB
Content-Type: text/plain; charset=us-ascii; name="buffer_ref30-patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline; filename="buffer_ref30-patch"

--- fs/buffer.c.old Sun Apr 27 15:54:43 1997
+++ fs/buffer.c Wed Jul 9 14:08:03 1997
@@ -86,7 +86,7 @@
each time we call refill */
int nref_dirt; /* Dirty buffer threshold for activating bdflush
when trying to refill buffers. */
- int dummy1; /* unused */
+ int flex_refill; /* Flexible refill policy switch */
int age_buffer; /* Time for normal buffer to age before
we flush it */
int age_super; /* Time for superblock to age before we
@@ -95,11 +95,11 @@
int dummy3; /* unused */
} b_un;
unsigned int data[N_PARAM];
-} bdf_prm = {{60, 500, 64, 256, 15, 30*HZ, 5*HZ, 1884, 2}};
+} bdf_prm = {{60, 500, 64, 256, 1, 30*HZ, 5*HZ, 1884, 2}};

/* 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, 100, 100, 1, 1};
-int bdflush_max[N_PARAM] = {100,5000, 2000, 2000,100, 60000, 60000, 2047, 5};
+int bdflush_min[N_PARAM] = { 0, 10, 5, 25, 0, 100, 100, 1, 1};
+int bdflush_max[N_PARAM] = {100,5000, 2000, 2000, 1, 60000, 60000, 2047, 5};

/*
* Rewrote the wait-routines to use the "new" wait-queue functionality,
@@ -596,81 +596,119 @@
unsigned int best_time, winner;
int buffers[BUF_DIRTY];
int i;
- int needed;
+ int needed, obtained=0;

refilled = 1;
- /* 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 to locate this much memory */
- needed =bdf_prm.b_un.nrefill * size;
+ /* We are going to try to locate this much memory. */
+ needed = bdf_prm.b_un.nrefill * size;

- while (nr_free_pages > min_free_pages*2 && needed > 0 &&
- grow_buffers(GFP_BUFFER, size)) {
- needed -= PAGE_SIZE;
+ while ((nr_free_pages > min_free_pages*2) &&
+ grow_buffers(GFP_BUFFER, size)) {
+ obtained += PAGE_SIZE;
+ if (obtained >= needed)
+ return;
}

-repeat:
- /* OK, we cannot grow the buffer cache, now try to get some
- from the lru list */
+ /*
+ * Check whether the flexible refill policy is in effect, and if
+ * so update the needed amount. We don't want to free more than
+ * one quarter of the (potentially) available buffers.
+ */
+ if (bdf_prm.b_un.flex_refill) {
+ i = nr_buffers_type[BUF_CLEAN] + nr_buffers_type[BUF_LOCKED];
+ i = i >> 2;
+ if (i < bdf_prm.b_un.nrefill) {
+ needed = i * size;
+ if (needed < PAGE_SIZE)
+ needed = PAGE_SIZE;
+ }
+ }

- /* First set the candidate pointers to usable buffers. This
- should be quick nearly all of the time. */
+repeat:
+ if (obtained >= needed)
+ return;

- if(needed <= 0) return;
+ /* OK, we cannot grow the buffer cache, now try to get some
+ * from the lru list.
+ *
+ * First set the candidate pointers to usable buffers. This
+ * should be quick nearly all of the time.
+ */

- for(i=0; i<BUF_DIRTY; i++){
+ for(i=0; i<BUF_DIRTY; i++) {
buffers[i] = nr_buffers_type[i];
candidate[i] = find_candidate(lru_list[i], &buffers[i], size);
}

- /* Now see which candidate wins the election */
+ /* Now see which candidate wins the election. */

winner = best_time = UINT_MAX;
- for(i=0; i<BUF_DIRTY; i++){
- if(!candidate[i]) continue;
- if(candidate[i]->b_lru_time < best_time){
+ for(i=0; i<BUF_DIRTY; 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 we have a winner, use it, and then get a new candidate from that list. */
if(winner != UINT_MAX) {
i = winner;
- while (needed>0 && (bh=candidate[i])) {
+ while (obtained < needed && (bh=candidate[i])) {
candidate[i] = bh->b_next_free;
- if(candidate[i] == bh) candidate[i] = NULL; /* Got last one */
+
remove_from_queues(bh);
bh->b_dev = B_FREE;
put_last_free(bh);
- needed -= bh->b_size;
- buffers[i]--;
- if(buffers[i] == 0) candidate[i] = NULL;
+ obtained += bh->b_size;

- if (candidate[i] && !can_reclaim(candidate[i],size))
- candidate[i] = find_candidate(candidate[i],&buffers[i], size);
+ /*
+ * Validate the next node as a candidate
+ */
+ if(--buffers[i] == 0 || candidate[i] == bh)
+ candidate[i] = NULL; /* Got last one */
+ else
+ candidate[i] = find_candidate(candidate[i],
+ &buffers[i], size);
}
- if (needed >= 0)
- goto repeat;
+ goto repeat;
}
-
- if(needed <= 0) return;
+
+ /*
+ * If we achieved at least half of our goal, return now.
+ */
+ if (bdf_prm.b_un.flex_refill && obtained >= (needed >> 1))
+ return;

/* Too bad, that was not enough. Try a little harder to grow some. */
-
if (nr_free_pages > min_free_pages + 5) {
if (grow_buffers(GFP_BUFFER, size)) {
- needed -= PAGE_SIZE;
+ obtained += PAGE_SIZE;
goto repeat;
- };
+ }
+ }
+
+ /*
+ * Make one last attempt to allocate some buffers ...
+ */
+ if (grow_buffers(GFP_ATOMIC, size))
+ obtained += PAGE_SIZE;
+
+ /*
+ * If we got any buffers, return now to let this task proceed.
+ */
+ if (bdf_prm.b_un.flex_refill && obtained) {
+printk("refill_freelist: returning some obtained=%d\n", obtained);
+ return;
}

- /* and repeat until we find something good */
- if (!grow_buffers(GFP_ATOMIC, size))
- wakeup_bdflush(1);
- needed -= PAGE_SIZE;
+ /*
+ * System is _very_ low on memory ... wake up bdflush and wait.
+ */
+printk("refill_freelist: waking bdflush\n");
+ wakeup_bdflush(1);
goto repeat;
}

@@ -717,6 +755,7 @@
/* OK, FINALLY we know that this buffer is the only one of its kind, */
/* and that it's unused (b_count=0), unlocked (buffer_locked=0), and clean */
bh->b_count=1;
+ bh->b_list = BUF_CLEAN;
bh->b_flushtime=0;
bh->b_state=(1<<BH_Touched);
bh->b_dev=dev;

--------------7C56437F91E80250C05549CB--