Index: linux-2.6.21-rc6/mm/slub.c =================================================================== --- linux-2.6.21-rc6.orig/mm/slub.c 2007-04-16 21:47:16.000000000 -0700 +++ linux-2.6.21-rc6/mm/slub.c 2007-04-16 21:47:20.000000000 -0700 @@ -2179,6 +2179,92 @@ void kfree(const void *x) EXPORT_SYMBOL(kfree); /* + * Attempt to empty a slab by using the callback. + * Must hold the list_lock on entry. + * May temporarily drop the list_lock. + */ +static int empty_slab(struct kmem_cache *s, + struct kmem_cache_node *n, + struct page *page) +{ + void *p; + void *addr = page_address(page); + unsigned long map[BITS_TO_LONGS(s->objects)]; + + if (!(s->flags & SLAB_FREE_CALLBACK)) + return 0; + + /* Extract from list */ + if (!slab_trylock(page)) + /* busy */ + return 0; + + /* + * Disable list management for this slab including the + * ability of kfree to free the slab back to the slab + * allocator. + */ + SetPageActive(page); + /* + * Take it off the partial lists. Means that no allocations + * can occur anymore. + */ + list_del(&page->lru); + spin_lock_irqrestore(n->list_lock, flags); + + /* + * Now we have the slab locked so that alloc/free operations + * can occur on it. list_lock is not held so other + * slab operations can continue. + */ + bitmap_zero(map, s->objects); + for(p = page->freelist; p; p = get_freepointer(s, p)) + set_bit((p - addr) / s->size, map); + + /* Lock the objects to be freed. */ + for(p = addr; p < addr + s->objects * s->size; p += s->size) { + int n = (p - addr) / s->size; + + if (!test_bit(n, map)) + /* + * Callback must establish reference on object + * to avoid concurrent frees. If reference + * cannot be established then we cannot free + * the object. This callback cannot sleep + * or do any allocation. + */ + if (!s->callback(p, s, SLAB_FREE_REFERENCE)) + set_bit(n, mmap); + } + + /* + * Drop slab lock so that allocations / free work during object + * destruction. + */ + slab_unlock(page); + + /* Attempt to free locked objects */ + for(p = addr; p < addr + s->objects * s->size; p += s->size) + if (!test_bit((p - addr) / s->size, map)) + /* + * Truly free the locked object. + * No other locks are held so the callback can + * anything it wants including failing to + * free the object. + */ + s->callback(p, s, SLAB_FREE_EXEC); + + /* Regain control */ + slab_lock(page); + spin_lock_irqsave(n->list_lock, flags); + ClearPageActive(page); + ClearPageReferenced(page); + /* This will free the slab if all objects were removed */ + putback_slab(s, page); + return page->inuse == 0; +} + +/* * kmem_cache_shrink removes empty slabs from the partial lists * and then sorts the partially allocated slabs by the number * of items in use. The slabs with the most items in use @@ -2186,6 +2272,10 @@ EXPORT_SYMBOL(kfree); * partial list because they are full. The slabs with the * least items are placed last. If it happens that the objects * are freed then the page can be returned to the page allocator. + * + * If the slab provides a callback for freeing objects then + * kmem_cache_shrink will perform callbacks for objects in slabs + * with few elements in order to free them up. */ int kmem_cache_shrink(struct kmem_cache *s) { @@ -2244,6 +2334,22 @@ int kmem_cache_shrink(struct kmem_cache spin_unlock_irqrestore(&n->list_lock, flags); } + /* + * Walk back through the partial attempting to free objects + * until we encounter a page that has more than 1/3rd + * objects. + */ + page = n->partial.prev; + while (n->nr_partial > MAX_PARTIAL && + (s->flags & SLAB_FREE_CALLBACK) && + page->prev != &n->partial && + page->inuse < s->objects / 3) { + + empty_slab(s, n, page); + + page = page->lru.prev; + } + kfree(slabs_by_inuse); return 0; } @@ -2400,7 +2506,7 @@ static struct kmem_cache *find_mergeable struct kmem_cache *kmem_cache_create(const char *name, size_t size, size_t align, unsigned long flags, - void (*ctor)(void *, struct kmem_cache *, unsigned long), + int (*ctor)(void *, struct kmem_cache *, unsigned long), void (*dtor)(void *, struct kmem_cache *, unsigned long)) { struct kmem_cache *s; Index: linux-2.6.21-rc6/include/linux/slab.h =================================================================== --- linux-2.6.21-rc6.orig/include/linux/slab.h 2007-04-16 21:34:30.000000000 -0700 +++ linux-2.6.21-rc6/include/linux/slab.h 2007-04-16 21:47:20.000000000 -0700 @@ -33,12 +33,21 @@ typedef struct kmem_cache kmem_cache_t _ #define SLAB_DESTROY_BY_RCU 0x00080000UL /* Defer freeing slabs to RCU */ #define SLAB_MEM_SPREAD 0x00100000UL /* Spread some memory over cpuset */ #define SLAB_TRACE 0x00200000UL /* Trace allocations and frees */ +#define SLAB_HAS_CTOR_CALLBACK 0x00400000UL +#define SLAB_HAS_DTOR_CALLBACK 0x00800000UL +#define SLAB_HAS_FREE_CALLBACK 0x01000000UL /* Flags passed to a constructor functions */ #define SLAB_CTOR_CONSTRUCTOR 0x001UL /* If not set, then deconstructor */ #define SLAB_CTOR_ATOMIC 0x002UL /* Tell constructor it can't sleep */ #define SLAB_CTOR_VERIFY 0x004UL /* Tell constructor it's a verify call */ +/* New style with on function as a callback */ +#define SLAB_CALLBACK_CTOR 0x001UL /* Constructor callback */ +#define SLAB_CALLBACK_DTOR 0x010UL /* Destructor callback */ +#define SLAB_CALLBACK_FREE 0x020UL /* Attempt to remove object */ +#define SLAB_CALLBACK_ATOMIC 0x002UL /* Callback is atomic */ + /* * struct kmem_cache related prototypes */ @@ -47,7 +56,7 @@ int slab_is_available(void); struct kmem_cache *kmem_cache_create(const char *, size_t, size_t, unsigned long, - void (*)(void *, struct kmem_cache *, unsigned long), + int (*)(void *, struct kmem_cache *, unsigned long), void (*)(void *, struct kmem_cache *, unsigned long)); void kmem_cache_destroy(struct kmem_cache *); int kmem_cache_shrink(struct kmem_cache *); Index: linux-2.6.21-rc6/include/linux/slub_def.h =================================================================== --- linux-2.6.21-rc6.orig/include/linux/slub_def.h 2007-04-16 21:34:30.000000000 -0700 +++ linux-2.6.21-rc6/include/linux/slub_def.h 2007-04-16 21:47:20.000000000 -0700 @@ -41,8 +41,7 @@ struct kmem_cache { /* Allocation and freeing of slabs */ int objects; /* Number of objects in slab */ int refcount; /* Refcount for slab cache destroy */ - void (*ctor)(void *, struct kmem_cache *, unsigned long); - void (*dtor)(void *, struct kmem_cache *, unsigned long); + int (*callback)(void *, struct kmem_cache *, unsigned long); int inuse; /* Offset to metadata */ int align; /* Alignment */ const char *name; /* Name (only for display!) */