updated dcache management patch for 2.1.60

Bill Hawes (whawes@star.net)
Tue, 28 Oct 1997 14:09:31 -0500


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

I've made a few changes to the dcache management to improve performance,
especially for file find operations. The main change is to use an
explicit dentry timestamp rather than relying on the inode i_atime
field. This allows the code to detect when it reaches very recently
used dentries, to change the search behavior.

In limited testing against 2.0.31 it seems to be comparable, and in some
cases much better. I'm sure further improvements are possible, so let
me know if you find cases where it doesn't seem to be working very well.

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

--- linux-2.1.60/include/linux/dcache.h.old Sat Oct 25 07:43:21 1997
+++ linux-2.1.60/include/linux/dcache.h Sat Oct 25 20:55:28 1997
@@ -51,6 +51,7 @@
unsigned long d_time; /* used by d_revalidate */
struct dentry_operations *d_op;
struct super_block * d_sb; /* The root of the dentry tree */
+ unsigned long d_reftime; /* last time referenced */
};

struct dentry_operations {
--- linux-2.1.60/fs/dcache.c.old Sat Oct 25 07:43:17 1997
+++ linux-2.1.60/fs/dcache.c Sun Oct 26 11:39:19 1997
@@ -97,9 +97,10 @@
goto out;
}

- if (!list_empty(&dentry->d_lru))
+ if (!list_empty(&dentry->d_lru)) {
dentry_stat.nr_unused--;
- list_del(&dentry->d_lru);
+ list_del(&dentry->d_lru);
+ }
if (list_empty(&dentry->d_hash)) {
struct inode *inode = dentry->d_inode;
struct dentry * parent;
@@ -116,6 +117,11 @@
}
list_add(&dentry->d_lru, &dentry_unused);
dentry_stat.nr_unused++;
+ /*
+ * Update the timestamp
+ */
+ dentry->d_reftime = jiffies;
+
out:
if (count >= 0) {
dentry->d_count = count;
@@ -154,18 +160,28 @@
* we need inodes or memory. The selected dentries
* are moved to the old end of the list where
* prune_dcache() can find them.
+ *
+ * Negative dentries are included in the selection so
+ * that they don't accumulate at the end of the list.
+ * The returned value is the total number of dentries
+ * selected, which may be much larger than the number
+ * of inodes.
*/
-int select_dcache(int count, int page_count)
+int select_dcache(int inode_count, int page_count)
{
- struct list_head *tail = &dentry_unused;
- struct list_head *next = dentry_unused.prev;
- int forward = 0, young = 0, depth = dentry_stat.nr_unused >> 1;
- int found = 0, pages = 0;
+ struct list_head *next, *tail = &dentry_unused;
+ int found = 0, forward = 0, young = 8;
+ int depth = dentry_stat.nr_unused >> 1;
+ unsigned long min_value = 0, max_value = 4;

#ifdef DCACHE_DEBUG
-printk("select_dcache: %d unused, count=%d, pages=%d\n",
-dentry_stat.nr_unused, count, page_count);
+printk("select_dcache: unused=%d, inodes=%d, pages=%d\n",
+dentry_stat.nr_unused, inode_count, page_count);
#endif
+ if (page_count)
+ max_value = -1;
+
+ next = tail->prev;
while (next != &dentry_unused && depth--) {
struct list_head *tmp = next;
struct dentry *dentry = list_entry(tmp, struct dentry, d_lru);
@@ -182,56 +198,59 @@
continue;
}
/*
+ * Check the dentry's age to see whether to change direction.
+ */
+ if (!forward) {
+ int age = (jiffies - dentry->d_reftime) / HZ;
+ if (age < dentry_stat.age_limit) {
+ if (!--young) {
+ forward = 1;
+ /*
+ * If we're scanning forward, don't take
+ * files with too few or too many pages.
+ */
+ if (page_count) {
+ min_value = 3;
+ max_value = 15;
+ }
+ next = dentry_unused.next;
+#ifdef DCACHE_DEBUG
+printk("select_dcache: %s/%s age=%d, scanning forward\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, age);
+#endif
+ }
+ continue;
+ }
+ }
+
+ /*
* Select dentries based on the page cache count ...
* should factor in number of uses as well.
*/
if (inode) {
- if (inode->i_state)
+ if (inode->i_state || inode->i_count > 1)
continue;
value = inode->i_nrpages;
- }
- /*
- * Consider various exemptions ...
- */
- if (!page_count) {
- if (!inode)
- continue;
- if (value >= 3)
- continue;
- } else if (!forward) {
- if (inode) {
- int age = CURRENT_TIME - inode->i_atime;
- if (age < dentry_stat.age_limit) {
- if (++young > 8) {
- forward = 1;
- next = dentry_unused.next;
-#ifdef DCACHE_DEBUG
-printk("select_dcache: age=%d, pages=%d, scanning forward\n", age, pages);
-#endif
- }
- continue;
- }
- }
} else {
- /*
- * If we're scanning from the front, don't take
- * files with only a trivial amount of memory.
- */
- if (value < 3 || value > 15)
+ /* Don't take negative dentries from the front */
+ if (forward)
continue;
}
+
+ if (value >= max_value || value < min_value)
+ continue;
/*
- * Move the dentry behind the tail
+ * Move the selected dentries behind the tail.
*/
if (tmp != tail->prev) {
list_del(tmp);
list_add(tmp, tail->prev);
}
tail = tmp;
- pages += value;
- if (++found >= count)
+ found++;
+ if (inode && --inode_count <= 0)
break;
- if (page_count && pages >= page_count)
+ if (page_count && (page_count -= value) <= 0)
break;
}
return found;
@@ -364,7 +383,7 @@
if (goal) {
if (goal > 50)
goal = 50;
- count = select_dcache(128, goal);
+ count = select_dcache(32, goal);
#ifdef DCACHE_DEBUG
printk("check_dcache_memory: goal=%d, count=%d\n", goal, count);
#endif
--- linux-2.1.60/fs/inode.c.old Sat Oct 25 07:43:17 1997
+++ linux-2.1.60/fs/inode.c Sun Oct 26 09:16:27 1997
@@ -358,33 +384,30 @@
*/
static void try_to_free_inodes(int goal)
{
- int retried = 0, found;
+ int retry = 1, found;

/*
* Check whether to preshrink the dcache ...
*/
- if (inodes_stat.preshrink) {
- spin_unlock(&inode_lock);
- select_dcache(goal, 0);
- prune_dcache(goal);
- spin_lock(&inode_lock);
- }
+ if (inodes_stat.preshrink)
+ goto preshrink;

-repeat:
- found = free_inodes(goal);
-
- /*
- * If we didn't free any inodes, do a limited
- * pruning of the dcache to help the next time.
- */
- if (!found) {
+ retry = 0;
+ do {
+ if (free_inodes(goal))
+ break;
+ /*
+ * If we didn't free any inodes, do a limited
+ * pruning of the dcache to help the next time.
+ */
+ preshrink:
spin_unlock(&inode_lock);
- select_dcache(goal, 0);
- prune_dcache(goal);
+ found = select_dcache(goal, 0);
+ if (found < goal)
+ found = goal;
+ prune_dcache(found);
spin_lock(&inode_lock);
- if (inodes_stat.preshrink && !retried++)
- goto repeat;
- }
+ } while (retry--);
}

/*
@@ -440,11 +463,11 @@
* If the allocation failed, do an extensive pruning of
* the dcache and then try again to free some inodes.
*/
- prune_dcache(128);
+ prune_dcache(inodes_stat.nr_inodes >> 2);
inodes_stat.preshrink = 1;

spin_lock(&inode_lock);
- free_inodes(128);
+ free_inodes(inodes_stat.nr_inodes >> 2);
{
struct list_head *tmp = inode_unused.next;
if (tmp != &inode_unused) {

--------------0E7512FA049994C1F9994D6B--