Re: New dcache not using slab allocator?

Kevin Buhr (buhr@stat.wisc.edu)
04 Aug 1997 21:09:57 -0500


[ Hope you don't mind me ccing this reply to "linux-kernel". ]

Dean Gaudet <dgaudet@arctic.org> writes:
>
> filenames probably display a regularity that you can exploit ... such as a
> mode around 7 or 8 chars. I'm assuming the dcache has just the basename
> for each entry rather than the full name ... but I've never looked at the
> code. You could create a slab for the 1 .. 8 char names, and then another
> for the 9 .. 16, and then just kmalloc the rest. Those numbers would have
> to be verified by experimentation.

Yup, it's just the basename, and your scheme is right on the money.

In fact, right now the smallest size "kmalloc" objects are 32 bytes,
so even by introducing a "dname-16" cache (for filenames 1..15
characters in length), I think we make a big win.

I've just slapped together a patch along these lines (with a dname-16,
dname-32, and dname-64 cache), and it seems to be working fine.
However, it needs some testing with regards to filenames longer than
64 characters (and the renaming to and from all combinations of short
and long filenames). I also haven't checked if some naughty bit of
filesystem code outside of "dcache.c" tries allocating or freeing
dnames the "old" way.

Anyway, I've enclosed the patch against 2.1.47; enjoy, but use at your
own risk.

Kevin <buhr@stat.wisc.edu>

* * *

Index: linux/mm/page_alloc.c
diff -u linux/mm/page_alloc.c:1.1.1.1 linux/mm/page_alloc.c:1.2
--- linux/mm/page_alloc.c:1.1.1.1 Thu Jul 17 12:45:06 1997
+++ linux/mm/page_alloc.c Sun Aug 3 12:49:49 1997
@@ -219,11 +219,19 @@
if ((priority==GFP_ATOMIC) || nr_free_pages > reserved_pages) {
RMQUEUE(order, dma);
spin_unlock_irqrestore(&page_alloc_lock, flags);
+#if 1
+ printk("__get_free_pages: failed(A) pri=%d ord=%lu dma=%d\n",
+ priority, order, dma);
+#endif
return 0;
}
spin_unlock_irqrestore(&page_alloc_lock, flags);
if (priority != GFP_BUFFER && try_to_free_page(priority, dma, 1))
goto repeat;
+#if 1
+ printk("__get_free_pages: failed(B) pri=%d ord=%lu dma=%d\n",
+ priority, order, dma);
+#endif
return 0;
}

Index: linux/mm/slab.c
diff -u linux/mm/slab.c:1.1.1.1 linux/mm/slab.c:1.3
--- linux/mm/slab.c:1.1.1.1 Thu Jul 17 12:45:07 1997
+++ linux/mm/slab.c Mon Aug 4 21:03:38 1997
@@ -1816,7 +1816,12 @@
up(&cache_chain_sem);

if (!best_cachep) {
- /* couldn't find anthying to reap */
+ /* couldn't find anything to reap */
+#if 1
+ if (pri<4)
+ printk("kmem_cache_reap: nothing to reap pri=%d dma=%d wait=%d\n",
+ pri, dma, wait);
+#endif
return 0;
}

Index: linux/mm/vmscan.c
diff -u linux/mm/vmscan.c:1.1.1.2 linux/mm/vmscan.c:1.5
--- linux/mm/vmscan.c:1.1.1.2 Tue Jul 29 18:27:22 1997
+++ linux/mm/vmscan.c Mon Aug 4 21:03:51 1997
@@ -365,13 +365,10 @@
shrink_dcache();
state = 2;
case 2:
- /*
- * We shouldn't have a priority here:
- * If we're low on memory we should
- * unconditionally throw away _all_
- * kmalloc caches!
+ /* kmem_cache_reap relies on getting
+ * different priority values
*/
- if (kmem_cache_reap(0, dma, wait))
+ if (kmem_cache_reap(i, dma, wait))
return 1;
state = 3;
case 3:
@@ -385,6 +382,10 @@
i--;
} while ((i - stop) >= 0);
}
+#if 1
+ printk("do_try_to_free_page: failed pri=%d dma=%d wait=%d\n",
+ priority, dma, wait);
+#endif
return 0;
}

@@ -401,6 +402,11 @@

lock_kernel();
retval = do_try_to_free_page(priority,dma,wait);
+#if 1
+ if (!retval)
+ printk("try_to_free_page: failed pri=%d dma=%d wait=%d\n",
+ priority, dma, wait);
+#endif
unlock_kernel();
return retval;
}
Index: linux/fs/dcache.c
diff -u linux/fs/dcache.c:1.1.1.2 linux/fs/dcache.c:1.3
--- linux/fs/dcache.c:1.1.1.2 Tue Jul 29 18:21:47 1997
+++ linux/fs/dcache.c Mon Aug 4 21:03:01 1997
@@ -17,6 +17,7 @@
#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/malloc.h>
+#include <linux/slab.h>
#include <linux/init.h>

/*
@@ -34,10 +35,99 @@
static struct list_head dentry_hashtable[D_HASHSIZE];
static LIST_HEAD(dentry_unused);

+static kmem_cache_t *de_cachep;
+
+typedef struct cache_sizes {
+ size_t cs_size;
+ kmem_cache_t *cs_cachep;
+} cache_sizes_t;
+
+static cache_sizes_t dname_cache_sizes[] = {
+ { 16, NULL},
+ { 32, NULL},
+ { 64, NULL},
+ {0, NULL}
+};
+
+static char * dname_cache_names[] = {
+ "dname-16",
+ "dname-32",
+ "dname-64"
+};
+
+static inline kmem_cache_t * dn_cachep(size_t size)
+{
+ int index = 0;
+
+ while (size >= dname_cache_sizes[index].cs_size) {
+ index++;
+ if (!dname_cache_sizes[index].cs_size)
+ return NULL;
+ }
+
+ return dname_cache_sizes[index].cs_cachep;
+}
+
+static char * d_namealloc(size_t size)
+{
+ kmem_cache_t *cachep = dn_cachep(size);
+
+ if (cachep) {
+ return kmem_cache_alloc(cachep, SLAB_KERNEL);
+ }
+ return kmalloc(size, GFP_KERNEL);
+}
+
+static void d_namefree(struct qstr *d_name)
+{
+ kmem_cache_t *cachep = dn_cachep(d_name->len);
+ char *name = (char *) d_name->name;
+
+ if (cachep) {
+ kmem_cache_free(cachep, name);
+ } else
+ kfree(name);
+}
+
+struct dentry * d_alloc(struct dentry * parent, const struct qstr *name)
+{
+ char * str;
+ struct dentry *dentry;
+
+ dentry = kmem_cache_alloc(de_cachep, SLAB_KERNEL);
+ if (!dentry)
+ return NULL;
+
+ str = d_namealloc(name->len);
+ if (!str) {
+ kmem_cache_free(de_cachep, dentry);
+ return NULL;
+ }
+
+ memcpy(str, name->name, name->len);
+ str[name->len] = 0;
+
+ dentry->d_count = 0;
+ dentry->d_flags = 0;
+ dentry->d_inode = NULL;
+ dentry->d_parent = parent;
+ dentry->d_mounts = dentry;
+ dentry->d_covers = dentry;
+ INIT_LIST_HEAD(&dentry->d_hash);
+ INIT_LIST_HEAD(&dentry->d_alias);
+ INIT_LIST_HEAD(&dentry->d_lru);
+
+ dentry->d_name.name = str;
+ dentry->d_name.len = name->len;
+ dentry->d_name.hash = name->hash;
+ dentry->d_revalidate = NULL;
+ return dentry;
+}
+
void d_free(struct dentry *dentry)
{
- kfree(dentry->d_name.name);
- kfree(dentry);
+ d_namefree(&dentry->d_name);
+ kmem_cache_free(de_cachep, dentry);
}

/*
@@ -126,43 +216,6 @@
}
}

-#define NAME_ALLOC_LEN(len) ((len+16) & ~15)
-
-struct dentry * d_alloc(struct dentry * parent, const struct qstr *name)
-{
- char * str;
- struct dentry *dentry;
-
- dentry = kmalloc(sizeof(struct dentry), GFP_KERNEL);
- if (!dentry)
- return NULL;
-
- str = kmalloc(NAME_ALLOC_LEN(name->len), GFP_KERNEL);
- if (!str) {
- kfree(dentry);
- return NULL;
- }
-
- memcpy(str, name->name, name->len);
- str[name->len] = 0;
-
- dentry->d_count = 0;
- dentry->d_flags = 0;
- dentry->d_inode = NULL;
- dentry->d_parent = parent;
- dentry->d_mounts = dentry;
- dentry->d_covers = dentry;
- INIT_LIST_HEAD(&dentry->d_hash);
- INIT_LIST_HEAD(&dentry->d_alias);
- INIT_LIST_HEAD(&dentry->d_lru);
-
- dentry->d_name.name = str;
- dentry->d_name.len = name->len;
- dentry->d_name.hash = name->hash;
- dentry->d_revalidate = NULL;
- return dentry;
-}
-
/*
* Fill in inode information in the entry.
*
@@ -326,12 +379,15 @@
int len = newname->len;
int hash = newname->hash;
char *name = (char *) entry->d_name.name;
+ kmem_cache_t *cachep;

- if (NAME_ALLOC_LEN(len) != NAME_ALLOC_LEN(entry->d_name.len)) {
- name = kmalloc(NAME_ALLOC_LEN(len), GFP_KERNEL);
+ cachep = dn_cachep(len);
+ /* reallocate if in different dn_caches *or* in kmalloc cache */
+ if (!cachep || cachep != dn_cachep(entry->d_name.len)) {
+ name = d_namealloc(len);
if (!name)
printk("out of memory for dcache\n");
- kfree(entry->d_name.name);
+ d_namefree(&entry->d_name); /* note: entry->d_name.len is old length */
entry->d_name.name = name;
}
memcpy(name, newname->name, len);
@@ -380,6 +436,8 @@
{
int i;
struct list_head *d = dentry_hashtable;
+ cache_sizes_t *cs;
+ char **cname;

i = D_HASHSIZE;
do {
@@ -387,4 +445,22 @@
d++;
i--;
} while (i);
+
+ de_cachep = kmem_cache_create("dentry",
+ sizeof(struct dentry),
+ 0,
+ SLAB_HWCACHE_ALIGN, NULL, NULL);
+ if (!de_cachep)
+ panic("Cannot create dentry SLAB cache\n");
+
+ for (cs = dname_cache_sizes, cname = dname_cache_names;
+ cs->cs_size;
+ cs++, cname++) {
+ cs->cs_cachep = kmem_cache_create(*cname,
+ cs->cs_size,
+ 0,
+ SLAB_HWCACHE_ALIGN, NULL, NULL);
+ if (!cs->cs_cachep)
+ panic("Cannot create dentry name SLAB caches\n");
+ }
}