cache_reap(): avoid free_block for remote notes Currently when we free objects to a remote node we need to run free_block on the remote node which costs a lot of off node accesses. This patch modified that behavior. Instead of running free_block() we simply transfer the object pointers to the shared array cache on the remote node. The remote node will either use these entries or drain them locally. Signed-off-by: Christoph Lameter Index: linux-2.6.16-rc5-mm1/mm/slab.c =================================================================== --- linux-2.6.16-rc5-mm1.orig/mm/slab.c 2006-02-28 12:17:08.000000000 -0800 +++ linux-2.6.16-rc5-mm1/mm/slab.c 2006-02-28 15:49:52.000000000 -0800 @@ -900,6 +900,34 @@ static struct array_cache *alloc_arrayca return nc; } +/* + * Try to transfer contents of the specified array cache + * to another array cache. Locking must be handled by the caller. + * + * Return the number of entries transferred. + */ +static int transfer_arraycache(struct array_cache *to, + struct array_cache *from, unsigned int max) +{ + unsigned int nr = min(from->avail, max); + + if (to->avail >= to->limit) + /* No space in target array cache */ + return 0; + + /* Figure out how many entries we could transfer */ + nr = min(nr, to->limit - to->avail); + + memcpy(to->entry + to->avail, + from->entry + from->avail - nr, + sizeof(void *) * nr); + + from->avail -= nr; + to->avail += nr; + to->touched = 1; + return nr; +} + #ifdef CONFIG_NUMA static void *__cache_alloc_node(struct kmem_cache *, gfp_t, int); static void *alternate_node_alloc(struct kmem_cache *, gfp_t); @@ -964,9 +992,18 @@ static void reap_alien(struct kmem_cache if (l3->alien) { struct array_cache *ac = l3->alien[node]; - if (ac && ac->avail) { - spin_lock_irq(&ac->lock); - __drain_alien_cache(cachep, ac, node); + + if (ac && ac->avail && spin_trylock_irq(&ac->lock)) { + struct kmem_list3 *rl3 = cachep->nodelists[node]; + + if (spin_trylock(&rl3->list_lock)) { + + transfer_arraycache( + cachep->nodelists[node]->shared, + ac, ac->limit); + + spin_unlock(&rl3->list_lock); + } spin_unlock_irq(&ac->lock); } } @@ -2678,20 +2715,9 @@ retry: BUG_ON(ac->avail > 0 || !l3); spin_lock(&l3->list_lock); - if (l3->shared) { - struct array_cache *shared_array = l3->shared; - if (shared_array->avail) { - if (batchcount > shared_array->avail) - batchcount = shared_array->avail; - shared_array->avail -= batchcount; - ac->avail = batchcount; - memcpy(ac->entry, - &(shared_array->entry[shared_array->avail]), - sizeof(void *) * batchcount); - shared_array->touched = 1; + if (l3->shared && transfer_arraycache(ac, l3->shared, batchcount)) goto alloc_done; - } - } + while (batchcount > 0) { struct list_head *entry; struct slab *slabp;