[PATCH 4/5] vfs: per superblock dentry stats

From: jblunck
Date: Fri Jun 16 2006 - 14:43:37 EST


This patch adds per superblock dentry statistics about unused and absolute
number of dentries.

Signed-off-by: Jan Blunck <jblunck@xxxxxxx>
---
fs/dcache.c | 47 ++++++++++++++++++++++++++---------------------
fs/super.c | 1 +
include/linux/dcache.h | 21 +++++++++++++++++----
include/linux/fs.h | 1 +
kernel/sysctl.c | 2 +-
5 files changed, 46 insertions(+), 26 deletions(-)

Index: work-2.6/fs/dcache.c
===================================================================
--- work-2.6.orig/fs/dcache.c
+++ work-2.6/fs/dcache.c
@@ -64,7 +64,7 @@ static struct hlist_head *dentry_hashtab
static LIST_HEAD(dentry_unused);

/* Statistics gathering. */
-struct dentry_stat_t dentry_stat = {
+struct dentry_stat global_dentry_stat = {
.age_limit = 45,
};

@@ -173,7 +173,7 @@ repeat:
if (list_empty(&dentry->d_lru)) {
dentry->d_flags |= DCACHE_REFERENCED;
list_add(&dentry->d_lru, &dentry_unused);
- dentry_stat.nr_unused++;
+ dentry_stat_inc(dentry->d_sb, nr_unused);
}
spin_unlock(&dentry->d_lock);
spin_unlock(&dcache_lock);
@@ -190,11 +190,13 @@ kill_it: {
*/
if (!list_empty(&dentry->d_lru)) {
list_del(&dentry->d_lru);
- dentry_stat.nr_unused--;
+ dentry_stat_dec(dentry->d_sb, nr_unused);
}
list_del(&dentry->d_u.d_child);
- dentry_stat.nr_dentry--; /* For d_free, below */
- /*drops the locks, at that point nobody can reach this dentry */
+ /* For d_free, below */
+ dentry_stat_dec(dentry->d_sb, nr_dentry);
+ /* drops the locks, at that point nobody can reach this
+ * dentry anymore */
dentry_iput(dentry);
parent = dentry->d_parent;
d_free(dentry);
@@ -268,7 +270,7 @@ static inline struct dentry * __dget_loc
{
atomic_inc(&dentry->d_count);
if (!list_empty(&dentry->d_lru)) {
- dentry_stat.nr_unused--;
+ dentry_stat_dec(dentry->d_sb, nr_unused);
list_del_init(&dentry->d_lru);
}
return dentry;
@@ -370,7 +372,7 @@ static inline void prune_one_dentry(stru

__d_drop(dentry);
list_del(&dentry->d_u.d_child);
- dentry_stat.nr_dentry--; /* For d_free, below */
+ dentry_stat_dec(dentry->d_sb, nr_dentry); /* For d_free, below */
dentry_iput(dentry);
parent = dentry->d_parent;
d_free(dentry);
@@ -403,10 +405,10 @@ static void prune_dcache(int count)
tmp = dentry_unused.prev;
if (tmp == &dentry_unused)
break;
- list_del_init(tmp);
prefetch(dentry_unused.prev);
- dentry_stat.nr_unused--;
dentry = list_entry(tmp, struct dentry, d_lru);
+ dentry_stat_dec(dentry->d_sb, nr_unused);
+ list_del_init(&dentry->d_lru);

spin_lock(&dentry->d_lock);
/*
@@ -422,7 +424,7 @@ static void prune_dcache(int count)
if (dentry->d_flags & DCACHE_REFERENCED) {
dentry->d_flags &= ~DCACHE_REFERENCED;
list_add(&dentry->d_lru, &dentry_unused);
- dentry_stat.nr_unused++;
+ dentry_stat_inc(dentry->d_sb, nr_unused);
spin_unlock(&dentry->d_lock);
continue;
}
@@ -444,7 +446,7 @@ static void select_anon(struct super_blo
spin_lock(&dcache_lock);
hlist_for_each_entry(dentry, lp, &sb->s_anon, d_hash) {
if (!list_empty(&dentry->d_lru)) {
- dentry_stat.nr_unused--;
+ dentry_stat_dec(sb, nr_unused);
list_del_init(&dentry->d_lru);
}

@@ -455,7 +457,7 @@ static void select_anon(struct super_blo
spin_lock(&dentry->d_lock);
if (!atomic_read(&dentry->d_count)) {
list_add(&dentry->d_lru, &dentry_unused);
- dentry_stat.nr_unused++;
+ dentry_stat_inc(sb, nr_unused);
}
spin_unlock(&dentry->d_lock);
}
@@ -519,7 +521,7 @@ repeat:
dentry = list_entry(tmp, struct dentry, d_lru);
if (dentry->d_sb != sb)
continue;
- dentry_stat.nr_unused--;
+ dentry_stat_dec(sb, nr_unused);
list_del_init(tmp);
spin_lock(&dentry->d_lock);
if (atomic_read(&dentry->d_count)) {
@@ -615,7 +617,7 @@ resume:
next = tmp->next;

if (!list_empty(&dentry->d_lru)) {
- dentry_stat.nr_unused--;
+ dentry_stat_dec(dentry->d_sb, nr_unused);
list_del_init(&dentry->d_lru);
}
/*
@@ -624,7 +626,7 @@ resume:
*/
if (!atomic_read(&dentry->d_count)) {
list_add(&dentry->d_lru, dentry_unused.prev);
- dentry_stat.nr_unused++;
+ dentry_stat_inc(dentry->d_sb, nr_unused);
found++;
}

@@ -691,7 +693,7 @@ static int shrink_dcache_memory(int nr,
return -1;
prune_dcache(nr);
}
- return (dentry_stat.nr_unused / 100) * sysctl_vfs_cache_pressure;
+ return (global_dentry_stat.nr_unused / 100) * sysctl_vfs_cache_pressure;
}

/**
@@ -756,7 +758,7 @@ struct dentry *d_alloc(struct dentry * p
spin_lock(&dcache_lock);
if (parent)
list_add(&dentry->d_u.d_child, &parent->d_subdirs);
- dentry_stat.nr_dentry++;
+ dentry_stat_inc(dentry->d_sb, nr_dentry);
spin_unlock(&dcache_lock);

return dentry;
@@ -932,6 +934,9 @@ struct dentry * d_alloc_anon(struct inod
tmp = NULL;
spin_lock(&res->d_lock);
res->d_sb = inode->i_sb;
+ /* Add to the sb dentry_stat here only,
+ * the global data is updated in d_alloc */
+ res->d_sb->s_dentry_stat.nr_dentry++;
res->d_parent = res;
res->d_inode = inode;
res->d_flags |= DCACHE_DISCONNECTED;
@@ -1613,23 +1618,23 @@ resume:
goto repeat;
}
if (!list_empty(&dentry->d_lru)) {
- dentry_stat.nr_unused--;
+ dentry_stat_dec(dentry->d_sb, nr_unused);
list_del_init(&dentry->d_lru);
}
if (atomic_dec_and_test(&dentry->d_count)) {
list_add(&dentry->d_lru, dentry_unused.prev);
- dentry_stat.nr_unused++;
+ dentry_stat_inc(dentry->d_sb, nr_unused);
}
}
if (this_parent != root) {
next = this_parent->d_u.d_child.next;
if (!list_empty(&this_parent->d_lru)) {
- dentry_stat.nr_unused--;
+ dentry_stat_dec(this_parent->d_sb, nr_unused);
list_del_init(&this_parent->d_lru);
}
if (atomic_dec_and_test(&this_parent->d_count)) {
list_add(&this_parent->d_lru, dentry_unused.prev);
- dentry_stat.nr_unused++;
+ dentry_stat_inc(this_parent->d_sb, nr_unused);
}
this_parent = this_parent->d_parent;
goto resume;
Index: work-2.6/fs/super.c
===================================================================
--- work-2.6.orig/fs/super.c
+++ work-2.6/fs/super.c
@@ -71,6 +71,7 @@ static struct super_block *alloc_super(v
INIT_LIST_HEAD(&s->s_instances);
INIT_HLIST_HEAD(&s->s_anon);
INIT_LIST_HEAD(&s->s_inodes);
+ s->s_dentry_stat.age_limit = 45;
init_rwsem(&s->s_umount);
mutex_init(&s->s_lock);
down_write(&s->s_umount);
Index: work-2.6/include/linux/dcache.h
===================================================================
--- work-2.6.orig/include/linux/dcache.h
+++ work-2.6/include/linux/dcache.h
@@ -36,14 +36,27 @@ struct qstr {
const unsigned char *name;
};

-struct dentry_stat_t {
+struct dentry_stat {
int nr_dentry;
int nr_unused;
- int age_limit; /* age in seconds */
- int want_pages; /* pages requested by system */
+ int age_limit; /* age in seconds */
int dummy[2];
};
-extern struct dentry_stat_t dentry_stat;
+extern struct dentry_stat global_dentry_stat;
+
+#define dentry_stat_inc(sb, x) \
+do { \
+ global_dentry_stat.x++; \
+ if (likely(sb)) \
+ (sb)->s_dentry_stat.x++;\
+} while(0)
+
+#define dentry_stat_dec(sb, x) \
+do { \
+ global_dentry_stat.x--; \
+ if (likely(sb)) \
+ (sb)->s_dentry_stat.x--;\
+} while(0)

/* Name hashing routines. Initial hash value */
/* Hash courtesy of the R5 hash in reiserfs modulo sign bits */
Index: work-2.6/include/linux/fs.h
===================================================================
--- work-2.6.orig/include/linux/fs.h
+++ work-2.6/include/linux/fs.h
@@ -847,6 +847,7 @@ struct super_block {
struct list_head s_io; /* parked for writeback */
struct hlist_head s_anon; /* anonymous dentries for (nfs) exporting */
struct list_head s_files;
+ struct dentry_stat s_dentry_stat;

struct block_device *s_bdev;
struct list_head s_instances;
Index: work-2.6/kernel/sysctl.c
===================================================================
--- work-2.6.orig/kernel/sysctl.c
+++ work-2.6/kernel/sysctl.c
@@ -958,7 +958,7 @@ static ctl_table fs_table[] = {
{
.ctl_name = FS_DENTRY,
.procname = "dentry-state",
- .data = &dentry_stat,
+ .data = &global_dentry_stat,
.maxlen = 6*sizeof(int),
.mode = 0444,
.proc_handler = &proc_dointvec,

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/