This patch obtains some statistics about dcache and exports it as part of /proc/meminfo. The following data is collected: 1. A count of pages with 1,2,3,... dentries. 2. Number of dentries requested for freeing and the actual number of dentries freed during the last invocation of prune_dcache. 3. Information about dcache lru list: number of inuse, free, referenced and total dentries. Original Author: Dave Hansen Signed-off-by: Bharata B Rao --- arch/i386/mm/init.c | 8 +++++++ fs/dcache.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++++ fs/proc/proc_misc.c | 27 +++++++++++++++++++++++ include/linux/dcache.h | 11 +++++++++ include/linux/mm.h | 3 ++ mm/bootmem.c | 4 +++ 6 files changed, 109 insertions(+) diff -puN include/linux/mm.h~dcache_stats include/linux/mm.h --- linux-2.6.13-rc7/include/linux/mm.h~dcache_stats 2005-09-12 10:57:52.000000000 +0530 +++ linux-2.6.13-rc7-bharata/include/linux/mm.h 2005-09-13 11:21:52.601920944 +0530 @@ -225,6 +225,9 @@ struct page { * to show when page is mapped * & limit reverse map searches. */ + int nr_dentry; /* Number of dentries in this page */ + spinlock_t nr_dentry_lock; + unsigned long private; /* Mapping-private opaque data: * usually used for buffer_heads * if PagePrivate set; used for diff -puN arch/i386/mm/init.c~dcache_stats arch/i386/mm/init.c --- linux-2.6.13-rc7/arch/i386/mm/init.c~dcache_stats 2005-09-12 10:57:52.000000000 +0530 +++ linux-2.6.13-rc7-bharata/arch/i386/mm/init.c 2005-09-13 11:22:29.357333272 +0530 @@ -272,6 +272,7 @@ void __init one_highpage_init(struct pag set_page_count(page, 1); __free_page(page); totalhigh_pages++; + spin_lock_init(&page->nr_dentry_lock); } else SetPageReserved(page); } @@ -669,6 +670,7 @@ static int noinline do_test_wp_bit(void) void free_initmem(void) { unsigned long addr; + struct page *page; addr = (unsigned long)(&__init_begin); for (; addr < (unsigned long)(&__init_end); addr += PAGE_SIZE) { @@ -676,6 +678,8 @@ void free_initmem(void) set_page_count(virt_to_page(addr), 1); memset((void *)addr, 0xcc, PAGE_SIZE); free_page(addr); + page = virt_to_page(addr); + spin_lock_init(&page->nr_dentry_lock); totalram_pages++; } printk (KERN_INFO "Freeing unused kernel memory: %dk freed\n", (__init_end - __init_begin) >> 10); @@ -684,12 +688,16 @@ void free_initmem(void) #ifdef CONFIG_BLK_DEV_INITRD void free_initrd_mem(unsigned long start, unsigned long end) { + struct page *page; + if (start < end) printk (KERN_INFO "Freeing initrd memory: %ldk freed\n", (end - start) >> 10); for (; start < end; start += PAGE_SIZE) { ClearPageReserved(virt_to_page(start)); set_page_count(virt_to_page(start), 1); free_page(start); + page = virt_to_page(start); + spin_lock_init(&page->nr_dentry_lock); totalram_pages++; } } diff -puN fs/dcache.c~dcache_stats fs/dcache.c --- linux-2.6.13-rc7/fs/dcache.c~dcache_stats 2005-09-12 10:57:52.000000000 +0530 +++ linux-2.6.13-rc7-bharata/fs/dcache.c 2005-09-13 12:27:07.079829848 +0530 @@ -33,6 +33,7 @@ #include #include #include +#include /* #define DCACHE_DEBUG 1 */ @@ -69,12 +70,48 @@ struct dentry_stat_t dentry_stat = { .age_limit = 45, }; +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); + +void get_dstat_info(void) +{ + struct dentry *dentry; + + lru_dentry_stat.nr_total = lru_dentry_stat.nr_inuse = 0; + lru_dentry_stat.nr_ref = lru_dentry_stat.nr_free = 0; + + spin_lock(&dcache_lock); + list_for_each_entry(dentry, &dentry_unused, d_lru) { + if (atomic_read(&dentry->d_count)) + lru_dentry_stat.nr_inuse++; + if (dentry->d_flags & DCACHE_REFERENCED) + lru_dentry_stat.nr_ref++; + } + lru_dentry_stat.nr_total = dentry_stat.nr_unused; + lru_dentry_stat.nr_free = lru_dentry_stat.nr_total - + lru_dentry_stat.nr_inuse; + spin_unlock(&dcache_lock); +} + static void d_callback(struct rcu_head *head) { struct dentry * dentry = container_of(head, struct dentry, d_rcu); + unsigned long flags; + struct page *page; if (dname_external(dentry)) kfree(dentry->d_name.name); + + page = virt_to_page(dentry); + spin_lock_irqsave(&page->nr_dentry_lock, flags); + atomic_dec(&nr_dentry[page->nr_dentry]); + if (--page->nr_dentry != 0) + atomic_inc(&nr_dentry[page->nr_dentry]); + BUG_ON(atomic_read(&nr_dentry[page->nr_dentry]) < 0); + BUG_ON(page->nr_dentry > 29); + spin_unlock_irqrestore(&page->nr_dentry_lock, flags); + kmem_cache_free(dentry_cache, dentry); } @@ -393,6 +430,9 @@ static inline void prune_one_dentry(stru static void prune_dcache(int count) { + int nr_requested = count; + int nr_freed = 0; + spin_lock(&dcache_lock); for (; count ; count--) { struct dentry *dentry; @@ -427,8 +467,13 @@ static void prune_dcache(int count) continue; } prune_one_dentry(dentry); + nr_freed++; } spin_unlock(&dcache_lock); + spin_lock(&prune_dcache_lock); + lru_dentry_stat.dprune_req = nr_requested; + lru_dentry_stat.dprune_freed = nr_freed; + spin_unlock(&prune_dcache_lock); } /* @@ -720,6 +765,8 @@ struct dentry *d_alloc(struct dentry * p { struct dentry *dentry; char *dname; + unsigned long flags; + struct page *page; dentry = kmem_cache_alloc(dentry_cache, GFP_KERNEL); if (!dentry) @@ -769,6 +816,15 @@ struct dentry *d_alloc(struct dentry * p dentry_stat.nr_dentry++; spin_unlock(&dcache_lock); + page = virt_to_page(dentry); + spin_lock_irqsave(&page->nr_dentry_lock, flags); + if (page->nr_dentry != 0) + atomic_dec(&nr_dentry[page->nr_dentry]); + atomic_inc(&nr_dentry[++page->nr_dentry]); + BUG_ON(atomic_read(&nr_dentry[page->nr_dentry]) < 0); + BUG_ON(page->nr_dentry > 29); + spin_unlock_irqrestore(&page->nr_dentry_lock, flags); + return dentry; } diff -puN fs/proc/proc_misc.c~dcache_stats fs/proc/proc_misc.c --- linux-2.6.13-rc7/fs/proc/proc_misc.c~dcache_stats 2005-09-12 10:57:52.000000000 +0530 +++ linux-2.6.13-rc7-bharata/fs/proc/proc_misc.c 2005-09-13 11:49:43.460911768 +0530 @@ -45,6 +45,7 @@ #include #include #include +#include #include #include #include @@ -115,6 +116,9 @@ static int uptime_read_proc(char *page, return proc_calc_metrics(page, start, off, count, eof, len); } +extern atomic_t nr_dentry[]; +extern spinlock_t prune_dcache_lock; + static int meminfo_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data) { @@ -128,6 +132,7 @@ static int meminfo_read_proc(char *page, unsigned long allowed; struct vmalloc_info vmi; long cached; + int j, total_dcache_pages = 0; get_page_state(&ps); get_zone_counts(&active, &inactive, &free); @@ -200,6 +205,28 @@ static int meminfo_read_proc(char *page, vmi.largest_chunk >> 10 ); + for (j =0; j < 30; j++) { + len += sprintf(page + len, "pages_with_[%2d]_dentries: %d\n", + j, atomic_read(&nr_dentry[j])); + total_dcache_pages += atomic_read(&nr_dentry[j]); + } + len += sprintf(page + len, "dcache_pages total: %d\n", + total_dcache_pages); + + spin_lock(&prune_dcache_lock); + len += sprintf(page + len, "prune_dcache: requested %d freed %d\n", + lru_dentry_stat.dprune_req, lru_dentry_stat.dprune_freed); + spin_unlock(&prune_dcache_lock); + + get_dstat_info(); + len += sprintf(page + len, "dcache lru list data:\n" + "dentries total: %d\n" + "dentries in_use: %d\n" + "dentries free: %d\n" + "dentries referenced: %d\n", + lru_dentry_stat.nr_total, lru_dentry_stat.nr_inuse, + lru_dentry_stat.nr_free, lru_dentry_stat.nr_ref); + len += hugetlb_report_meminfo(page + len); return proc_calc_metrics(page, start, off, count, eof, len); diff -puN mm/bootmem.c~dcache_stats mm/bootmem.c --- linux-2.6.13-rc7/mm/bootmem.c~dcache_stats 2005-09-12 10:57:52.000000000 +0530 +++ linux-2.6.13-rc7-bharata/mm/bootmem.c 2005-09-13 11:26:31.358543496 +0530 @@ -291,12 +291,14 @@ static unsigned long __init free_all_boo page = pfn_to_page(pfn); count += BITS_PER_LONG; __ClearPageReserved(page); + spin_lock_init(&page->nr_dentry_lock); order = ffs(BITS_PER_LONG) - 1; set_page_refs(page, order); for (j = 1; j < BITS_PER_LONG; j++) { if (j + 16 < BITS_PER_LONG) prefetchw(page + j + 16); __ClearPageReserved(page + j); + spin_lock_init(&((page + j)->nr_dentry_lock)); } __free_pages(page, order); i += BITS_PER_LONG; @@ -311,6 +313,7 @@ static unsigned long __init free_all_boo __ClearPageReserved(page); set_page_refs(page, 0); __free_page(page); + spin_lock_init(&page->nr_dentry_lock); } } } else { @@ -331,6 +334,7 @@ static unsigned long __init free_all_boo __ClearPageReserved(page); set_page_count(page, 1); __free_page(page); + spin_lock_init(&page->nr_dentry_lock); } total += count; bdata->node_bootmem_map = NULL; diff -puN include/linux/dcache.h~dcache_stats include/linux/dcache.h --- linux-2.6.13-rc7/include/linux/dcache.h~dcache_stats 2005-09-12 17:30:01.000000000 +0530 +++ linux-2.6.13-rc7-bharata/include/linux/dcache.h 2005-09-13 12:27:07.080829696 +0530 @@ -46,6 +46,17 @@ struct dentry_stat_t { }; extern struct dentry_stat_t dentry_stat; +struct lru_dentry_stat { + int nr_total; + int nr_inuse; + int nr_ref; + int nr_free; + int dprune_req; + int dprune_freed; +}; +extern struct lru_dentry_stat lru_dentry_stat; +extern void get_dstat_info(void); + /* Name hashing routines. Initial hash value */ /* Hash courtesy of the R5 hash in reiserfs modulo sign bits */ #define init_name_hash() 0 _