Index: linux-2.6.21-rc1/mm/slub.c =================================================================== --- linux-2.6.21-rc1.orig/mm/slub.c 2007-02-24 13:40:48.000000000 -0800 +++ linux-2.6.21-rc1/mm/slub.c 2007-02-24 16:04:12.000000000 -0800 @@ -70,10 +70,15 @@ /* * Set of flags that will prohibit slab merging */ -#define SLUB_NO_MERGE (SLAB_RECLAIM_ACCOUNT | SLAB_DESTROY_BY_RCU | \ - SLAB_CACHE_DMA | SLAB_DEBUG_FREE | SLAB_DEBUG_INITIAL | \ +#define SLUB_NEVER_MERGE (SLAB_DEBUG_FREE | SLAB_DEBUG_INITIAL | \ SLAB_RED_ZONE | SLAB_POISON | SLAB_STORE_USER) +/* + * Set of flags that must be the same for merging + */ +#define SLUB_SAME_MERGE (SLAB_RECLAIM_ACCOUNT | SLAB_DESTROY_BY_RCU | \ + SLAB_CACHE_DMA) + #ifndef ARCH_KMALLOC_MINALIGN #define ARCH_KMALLOC_MINALIGN sizeof(void *) #endif @@ -98,6 +103,10 @@ UP /* Everything works */ } slab_state = DOWN; +static DECLARE_RWSEM(slabstat_sem); + +LIST_HEAD(slab_caches); + /******************************************************************** * Core slab cache functions *******************************************************************/ @@ -365,7 +374,7 @@ void *base = page_address(page); if (object < base || object >= base + s->objects * s->size) { - printk(KERN_CRIT "slab %s size %d: pointer %p->%p\nnot in" + printk(KERN_CRIT "slab %s size %d: pointer %p->%p not in" " range (%p-%p) in page %p\n", s->name, s->size, origin, object, base, base + s->objects * s->size, page); @@ -1118,66 +1127,55 @@ EXPORT_SYMBOL(kmem_cache_destroy); -static unsigned long count_objects(struct kmem_cache *s, - unsigned long *nodes) -{ - int count = 0; - struct page *page; - unsigned long flags; - int node; - - for_each_online_node(node) { - struct kmem_cache_node *n = s->node[node]; - - spin_lock_irqsave(&n->list_lock, flags); - list_for_each_entry(page, &n->partial, lru) { - count += page->inuse; - nodes[node]++; - } - spin_unlock_irqrestore(&n->list_lock, flags); - } - return count; -} - static unsigned long slab_objects(struct kmem_cache *s, unsigned long *p_total, unsigned long *p_cpu_slabs, unsigned long *p_partial, unsigned long *nodes) { - int partial = 0; - int in_partial_slabs = count_objects(s, nodes); int nr_slabs = 0; - int cpu_slabs = 0; - int nr_in_cpu_slabs = 0; + int nr_partial_slabs = 0; + int nr_cpu_slabs = 0; + int in_cpu_slabs = 0; + int in_partial_slabs = 0; int cpu; int node; + unsigned long flags; + struct page *page; for_each_online_node(node) { struct kmem_cache_node *n = s->node[node]; - nr_slabs += nodes[node] = atomic_read(&n->nr_slabs); - partial += n->nr_partial; + nr_slabs += atomic_read(&n->nr_slabs); + nr_partial_slabs += n->nr_partial; + + nodes[node] = atomic_read(&n->nr_slabs) + + n->nr_partial; + + spin_lock_irqsave(&n->list_lock, flags); + list_for_each_entry(page, &n->partial, lru) + in_partial_slabs += page->inuse; + spin_unlock_irqrestore(&n->list_lock, flags); } - for_each_possible_cpu(cpu) { - struct page *page = s->cpu_slab[cpu]; + for_each_possible_cpu(cpu) { + page = s->cpu_slab[cpu]; if (page) { - cpu_slabs++; - nr_in_cpu_slabs += page->inuse; + nr_cpu_slabs++; + in_cpu_slabs += page->inuse; nodes[page_to_nid(page)]++; } } if (p_partial) - *p_partial = partial; + *p_partial = nr_partial_slabs; if (p_cpu_slabs) - *p_cpu_slabs = cpu_slabs; + *p_cpu_slabs = nr_cpu_slabs; if (p_total) *p_total = nr_slabs; - return in_partial_slabs + nr_in_cpu_slabs + - (nr_slabs - partial - cpu_slabs) * s->objects; + return in_partial_slabs + in_cpu_slabs + + (nr_slabs - nr_partial_slabs - nr_cpu_slabs) * s->objects; } /******************************************************************** @@ -1370,20 +1368,53 @@ #endif } -static struct kmem_cache *kmem_cache_dup(struct kmem_cache *s) +static struct kmem_cache *kmem_cache_dup(struct kmem_cache *s, + gfp_t flags, const char *name) { + char *x; + atomic_inc(&s->refcount); + + if (!s->aliases) + s->aliases = kstrdup(name, flags); + else { + x = kmalloc(strlen(s->aliases) + strlen(name) + 1, + flags); + strcpy(x, s->aliases); + strcat(x, " "); + strcat(x, name); + kfree(s->aliases); + s->aliases = x; + } return s; } -static struct kmem_cache *__kmalloc_slab(size_t size) +/* + * Find a mergeable slab cache + */ +static struct kmem_cache *find_mergeable(unsigned long size, unsigned long flags) { - int index = kmalloc_index(size) - KMALLOC_SHIFT_LOW; + struct list_head *h; - if (index < 0) + if (slub_nomerge || (flags & SLUB_NEVER_MERGE)) return NULL; - return &kmalloc_caches[index]; + + down_read(&slabstat_sem); + list_for_each(h, &slab_caches) { + struct kmem_cache *s = + container_of(h, struct kmem_cache, list); + + if (s->size >= size && + s->size - size <= sizeof(void *) && + ((flags & SLUB_SAME_MERGE) == + (s->flags & SLUB_SAME_MERGE))) { + up_read(&slabstat_sem); + return s; + } + } + up_read(&slabstat_sem); + return NULL; } struct kmem_cache *kmem_cache_create(const char *name, size_t size, @@ -1391,29 +1422,18 @@ void (*ctor)(void *, struct kmem_cache *, unsigned long), void (*dtor)(void *, struct kmem_cache *, unsigned long)) { - struct kmem_cache *s; - - if (!slub_nomerge && !ctor && !dtor && !(flags & SLUB_NO_MERGE) && - align <= ARCH_SLAB_MINALIGN) { - int sz = ALIGN(size, calculate_alignment(flags, align)); - - /* Find the kmalloc slab that would be used for this size */ - s = __kmalloc_slab(sz); - if (!s) - return NULL; + struct kmem_cache *s = NULL; - /* - * Check if there would be less than a word difference - * between the size of the slab and the kmalloc slab. - * If so then just use the kmalloc array and avoid creating - * a new slab. - */ - if (s->size - sz <= sizeof(void *)) { - printk(KERN_INFO "SLUB: Merging slab_cache %s size %d" - " into kmalloc array size %d\n", - name, (int)size, (int)s->size); - return kmem_cache_dup(s); - } + if (!ctor && !dtor) + s = find_mergeable( + ALIGN(size, calculate_alignment(flags, align)), + flags); + + if (s) { + printk(KERN_INFO "SLUB: Merging slab_cache %s size %d" + " with slab_cache %s\n", + name, (int)size, s->name); + return kmem_cache_dup(s, GFP_KERNEL, name); } s = kmalloc(sizeof(struct kmem_cache), GFP_KERNEL); @@ -1444,10 +1464,6 @@ * Slab proc interface *******************************************************************/ -static DECLARE_RWSEM(slabstat_sem); - -LIST_HEAD(slab_caches); - void for_all_slabs(void (*func)(struct kmem_cache *, int), int cpu) { struct list_head *h; @@ -1457,7 +1473,7 @@ struct kmem_cache *s = container_of(h, struct kmem_cache, list); - func(s, cpu); + func(s, cpu); } up_read(&slabstat_sem); } @@ -1582,6 +1598,10 @@ kfree(x); display_nodes(m, nodes); + if (s->aliases) { + seq_putc(m, ' '); + seq_puts(m, s->aliases); + } seq_putc(m, '\n'); return 0; } Index: linux-2.6.21-rc1/include/linux/slub_def.h =================================================================== --- linux-2.6.21-rc1.orig/include/linux/slub_def.h 2007-02-24 11:07:12.000000000 -0800 +++ linux-2.6.21-rc1/include/linux/slub_def.h 2007-02-24 15:25:38.000000000 -0800 @@ -12,9 +12,9 @@ struct kmem_cache_node { spinlock_t list_lock; /* Protect partial list and nr_partial */ - unsigned long nr_partial; - struct list_head partial; + unsigned int nr_partial; atomic_long_t nr_slabs; + struct list_head partial; }; /* @@ -33,6 +33,7 @@ int objsize; /* The size of an object that is in a chunk */ int inuse; /* Used portion of the chunk */ const char *name; /* Name (only for display!) */ + char *aliases; /* Slabs merged into this one */ struct list_head list; /* List of slabs */ #ifdef CONFIG_SMP struct mutex flushing;