SLUB: Add MIN_PARTIAL We leave a mininum of partial slabs on nodes when we search for partial slabs on other node. Define a constant for that value. Then modify slub to keep MIN_PARTIAL slabs around on the freelist. This avoids bad situations where a function frees the last object in a slab (which results in the page being returned to the page allocator) only to then allocate one again (which requires geting a page back from the page allocator). Keeping the page on the partial list reduces overhead. Signed-off-by: Christoph Lameter Index: linux-2.6.21-rc6/mm/slub.c =================================================================== --- linux-2.6.21-rc6.orig/mm/slub.c 2007-04-16 21:34:32.000000000 -0700 +++ linux-2.6.21-rc6/mm/slub.c 2007-04-16 21:35:32.000000000 -0700 @@ -112,6 +112,9 @@ /* Enable to test recovery from slab corruption on boot */ #define SLUB_RESILIENCY_TEST +/* Mininum number of partial slabs */ +#define MIN_PARTIAL 2 + /* * Flags from the regular SLAB that SLUB does not support: */ @@ -635,16 +638,10 @@ static int on_freelist(struct kmem_cache /* * Tracking of fully allocated slabs for debugging */ -static void add_full(struct kmem_cache *s, struct page *page) +static void add_full(struct kmem_cache_node *n, struct page *page) { - struct kmem_cache_node *n; - VM_BUG_ON(!irqs_disabled()); - if (!(s->flags & SLAB_STORE_USER)) - return; - - n = get_node(s, page_to_nid(page)); spin_lock(&n->list_lock); list_add(&page->lru, &n->full); spin_unlock(&n->list_lock); @@ -939,10 +936,16 @@ static __always_inline int slab_trylock( /* * Management of partially allocated slabs */ -static void add_partial(struct kmem_cache *s, struct page *page) +static void add_partial_tail(struct kmem_cache_node *n, struct page *page) { - struct kmem_cache_node *n = get_node(s, page_to_nid(page)); + spin_lock(&n->list_lock); + n->nr_partial++; + list_add_tail(&page->lru, &n->partial); + spin_unlock(&n->list_lock); +} +static void add_partial(struct kmem_cache_node *n, struct page *page) +{ spin_lock(&n->list_lock); n->nr_partial++; list_add(&page->lru, &n->partial); @@ -1042,7 +1045,7 @@ static struct page *get_any_partial(stru n = get_node(s, zone_to_nid(*z)); if (n && cpuset_zone_allowed_hardwall(*z, flags) && - n->nr_partial > 2) { + n->nr_partial > MIN_PARTIAL) { page = get_partial_node(n); if (page) return page; @@ -1076,15 +1079,32 @@ static struct page *get_partial(struct k */ static void putback_slab(struct kmem_cache *s, struct page *page) { - if (page->inuse) { + struct kmem_cache_node *n = get_node(s, page_to_nid(page)); + + if (page->inuse || n->nr_partial < MIN_PARTIAL) { + if (page->freelist) - add_partial(s, page); - else if (PageError(page)) - add_full(s, page); + add_partial(n, page); + else + if (PageError(page) && (s->flags & SLAB_STORE_USER)) + add_full(n, page); slab_unlock(page); + } else { - slab_unlock(page); - discard_slab(s, page); + if (n->nr_partial < MIN_PARTIAL) { + /* + * Adding an empty page to the partial slabs in order + * to avoid page allocator overhead. This page needs to + * come after all the others that are not fully empty + * in order to make sure that we do maximum + * defragmentation. + */ + add_partial_tail(n, page); + slab_unlock(page); + } else { + slab_unlock(page); + discard_slab(s, page); + } } } @@ -1341,7 +1361,7 @@ checks_ok: * then add it. */ if (unlikely(!prior)) - add_partial(s, page); + add_partial(get_node(s, page_to_nid(page)), page); out_unlock: slab_unlock(page); @@ -1547,7 +1567,7 @@ static struct kmem_cache_node * __init e kmalloc_caches->node[node] = n; init_kmem_cache_node(n); atomic_long_inc(&n->nr_slabs); - add_partial(kmalloc_caches, page); + add_partial(n, page); return n; }