This patch maintains the dentries in a red black tree. RB tree is scanned in-order and dentries are put into the end of LRU list to increase the chances of freeing a dentries of a given page. Original Author: Santhosh Rao Signed-off-by: Bharata B Rao --- fs/dcache.c | 143 +++++++++++++++++++++++++++++++++++++++++++++++-- include/linux/dcache.h | 2 2 files changed, 141 insertions(+), 4 deletions(-) diff -puN fs/dcache.c~rbtree_dcache_reclaim fs/dcache.c --- linux-2.6.13-rc7/fs/dcache.c~rbtree_dcache_reclaim 2005-09-13 12:11:11.279133640 +0530 +++ linux-2.6.13-rc7-bharata/fs/dcache.c 2005-09-13 12:15:02.732947312 +0530 @@ -34,6 +34,7 @@ #include #include #include +#include /* #define DCACHE_DEBUG 1 */ @@ -70,6 +71,50 @@ struct dentry_stat_t dentry_stat = { .age_limit = 45, }; +static struct rb_root dentry_tree = RB_ROOT; + +#define RB_NONE (2) +#define ON_RB(node) ((node)->rb_color != RB_NONE) +#define RB_CLEAR(node) ((node)->rb_color = RB_NONE ) + + +/* take a dentry safely off the rbtree */ +static void drb_delete(struct dentry* dentry) +{ + if (ON_RB(&dentry->d_rb)) { + rb_erase(&dentry->d_rb, &dentry_tree); + RB_CLEAR(&dentry->d_rb); + } else { + /* All allocated dentry objs should be in the tree */ + BUG_ON(1); + } +} + +static struct dentry * drb_insert(struct dentry * dentry) +{ + struct rb_node ** p = &dentry_tree.rb_node; + struct rb_node * parent = NULL; + struct rb_node * node = &dentry->d_rb; + struct dentry * cur = NULL; + + while (*p) { + parent = *p; + cur = rb_entry(parent, struct dentry, d_rb); + + if (dentry < cur) + p = &(*p)->rb_left; + else if (dentry > cur) + p = &(*p)->rb_right; + else { + return cur; + } + } + + rb_link_node(node, parent, p); + rb_insert_color(node,&dentry_tree); + return NULL; +} + atomic_t nr_dentry[30]; /* I have seen a max of 27 dentries in a page */ struct lru_dentry_stat lru_dentry_stat; DEFINE_SPINLOCK(prune_dcache_lock); @@ -232,6 +277,7 @@ kill_it: { list_del(&dentry->d_child); dentry_stat.nr_dentry--; /* For d_free, below */ /*drops the locks, at that point nobody can reach this dentry */ + drb_delete(dentry); dentry_iput(dentry); parent = dentry->d_parent; d_free(dentry); @@ -407,6 +453,7 @@ static inline void prune_one_dentry(stru __d_drop(dentry); list_del(&dentry->d_child); dentry_stat.nr_dentry--; /* For d_free, below */ + drb_delete(dentry); dentry_iput(dentry); parent = dentry->d_parent; d_free(dentry); @@ -416,7 +463,7 @@ static inline void prune_one_dentry(stru } /** - * prune_dcache - shrink the dcache + * prune_lru - shrink the lru list * @count: number of entries to try and free * * Shrink the dcache. This is done when we need @@ -428,7 +475,7 @@ static inline void prune_one_dentry(stru * all the dentries are in use. */ -static void prune_dcache(int count) +static void prune_lru(int count) { int nr_requested = count; int nr_freed = 0; @@ -476,6 +523,93 @@ static void prune_dcache(int count) spin_unlock(&prune_dcache_lock); } +/** + * prune_dcache - try and "intelligently" shrink the dcache + * @requested - num of dentrys to try and free + * + * The basic strategy here is to scan through our tree of dentrys + * in-order and put them at the end of the lru - free list + * Why in-order? Because, we want the chances of actually freeing + * all 15-27 (depending on arch) dentrys on a given page, instead + * of just in random lru order, which tends to lower dcache utilization + * and not free many pages. + */ +static void prune_dcache(unsigned requested) +{ + /* ------ debug --------- */ + //static int mod = 0; + //int flag = 0, removed = 0; + /* ------ debug --------- */ + + unsigned found = 0; + unsigned count; + struct rb_node * next; + struct dentry *dentry; +#define NUM_LRU_PTRS 8 + struct rb_node *lru_ptrs[NUM_LRU_PTRS]; + struct list_head *cur; + int i; + + spin_lock(&dcache_lock); + + cur = dentry_unused.prev; + + /* grab NUM_LRU_PTRS entrys off the end of lru list */ + /* we'll use these as pseudo-random starting points in the tree */ + for (i = 0 ; i < NUM_LRU_PTRS ; i++ ){ + if ( cur == &dentry_unused ) { + /* if there aren't NUM_LRU_PTRS entrys, we probably + can't even free a page now, give up */ + spin_unlock(&dcache_lock); + return; + } + lru_ptrs[i] = &(list_entry(cur,struct dentry, d_lru)->d_rb); + cur = cur->prev; + } + + i = 0; + + do { + count = 4 * PAGE_SIZE / sizeof(struct dentry) ; /* abitrary heuristic */ + next = lru_ptrs[i]; + for (; count ; count--) { + if( ! next ) { + //flag = 1; /* ------ debug --------- */ + break; + } + dentry = list_entry(next, struct dentry, d_rb); + next = rb_next(next); + prefetch(next); + if( ! list_empty( &dentry->d_lru) ) { + list_del_init(&dentry->d_lru); + dentry_stat.nr_unused--; + } + if (atomic_read(&dentry->d_count)) { + //removed++; /* ------ debug --------- */ + continue; + } else { + list_add_tail(&dentry->d_lru, &dentry_unused); + dentry_stat.nr_unused++; + found++; + } + } + i++; + } while ( (found < requested / 2) && (i < NUM_LRU_PTRS ) ); +#undef NUM_LRU_PTRS + + spin_unlock(&dcache_lock); + + /* ------ debug --------- */ + //mod++; + //if ( ! (mod & 64) ) { + // mod = 0; + // printk("prune_dcache: i %d flag %d, found %d removed %d\n",i,flag,found,removed); + //} + /* ------ debug --------- */ + + prune_lru(found); +} + /* * Shrink the dcache for the specified super block. * This allows us to unmount a device without disturbing @@ -687,7 +821,7 @@ void shrink_dcache_parent(struct dentry int found; while ((found = select_parent(parent)) != 0) - prune_dcache(found); + prune_lru(found); } /** @@ -725,7 +859,7 @@ void shrink_dcache_anon(struct hlist_hea } } spin_unlock(&dcache_lock); - prune_dcache(found); + prune_lru(found); } while(found); } @@ -814,6 +948,7 @@ struct dentry *d_alloc(struct dentry * p if (parent) list_add(&dentry->d_child, &parent->d_subdirs); dentry_stat.nr_dentry++; + drb_insert(dentry); spin_unlock(&dcache_lock); page = virt_to_page(dentry); diff -puN include/linux/dcache.h~rbtree_dcache_reclaim include/linux/dcache.h --- linux-2.6.13-rc7/include/linux/dcache.h~rbtree_dcache_reclaim 2005-09-13 12:11:11.284132880 +0530 +++ linux-2.6.13-rc7-bharata/include/linux/dcache.h 2005-09-13 12:11:11.306129536 +0530 @@ -9,6 +9,7 @@ #include #include #include +#include struct nameidata; struct vfsmount; @@ -104,6 +105,7 @@ struct dentry { struct dentry *d_parent; /* parent directory */ struct qstr d_name; + struct rb_node d_rb; struct list_head d_lru; /* LRU list */ struct list_head d_child; /* child of parent list */ struct list_head d_subdirs; /* our children */ _