Skip to content

Commit

Permalink
SLUB: add CONFIG_SLUB_DEBUG
Browse files Browse the repository at this point in the history
CONFIG_SLUB_DEBUG can be used to switch off the debugging and sysfs components
of SLUB.  Thus SLUB will be able to replace SLOB.  SLUB can arrange objects in
a denser way than SLOB and the code size should be minimal without debugging
and sysfs support.

Note that CONFIG_SLUB_DEBUG is materially different from CONFIG_SLAB_DEBUG.
CONFIG_SLAB_DEBUG is used to enable slab debugging in SLAB.  SLUB enables
debugging via a boot parameter.  SLUB debug code should always be present.

CONFIG_SLUB_DEBUG can be modified in the embedded config section.

Signed-off-by: Christoph Lameter <clameter@sgi.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
  • Loading branch information
Christoph Lameter authored and Linus Torvalds committed May 9, 2007
1 parent 02cbc87 commit 41ecc55
Show file tree
Hide file tree
Showing 2 changed files with 123 additions and 75 deletions.
9 changes: 9 additions & 0 deletions init/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -504,6 +504,15 @@ config VM_EVENT_COUNTERS
on EMBEDDED systems. /proc/vmstat will only show page counts
if VM event counters are disabled.

config SLUB_DEBUG
default y
bool "Enable SLUB debugging support" if EMBEDDED
help
SLUB has extensive debug support features. Disabling these can
result in significant savings in code size. This also disables
SLUB sysfs support. /sys/slab will not exist and there will be
no support for cache validation etc.

choice
prompt "Choose SLAB allocator"
default SLAB
Expand Down
189 changes: 114 additions & 75 deletions mm/slub.c
Original file line number Diff line number Diff line change
Expand Up @@ -89,17 +89,25 @@

static inline int SlabDebug(struct page *page)
{
#ifdef CONFIG_SLUB_DEBUG
return PageError(page);
#else
return 0;
#endif
}

static inline void SetSlabDebug(struct page *page)
{
#ifdef CONFIG_SLUB_DEBUG
SetPageError(page);
#endif
}

static inline void ClearSlabDebug(struct page *page)
{
#ifdef CONFIG_SLUB_DEBUG
ClearPageError(page);
#endif
}

/*
Expand Down Expand Up @@ -207,7 +215,7 @@ struct track {

enum track_item { TRACK_ALLOC, TRACK_FREE };

#ifdef CONFIG_SYSFS
#if defined(CONFIG_SYSFS) && defined(CONFIG_SLUB_DEBUG)
static int sysfs_slab_add(struct kmem_cache *);
static int sysfs_slab_alias(struct kmem_cache *, const char *);
static void sysfs_slab_remove(struct kmem_cache *);
Expand Down Expand Up @@ -284,6 +292,14 @@ static inline int slab_index(void *p, struct kmem_cache *s, void *addr)
return (p - addr) / s->size;
}

#ifdef CONFIG_SLUB_DEBUG
/*
* Debug settings:
*/
static int slub_debug;

static char *slub_debug_slabs;

/*
* Object debugging
*/
Expand Down Expand Up @@ -821,6 +837,97 @@ static void trace(struct kmem_cache *s, struct page *page, void *object, int all
}
}

static int __init setup_slub_debug(char *str)
{
if (!str || *str != '=')
slub_debug = DEBUG_DEFAULT_FLAGS;
else {
str++;
if (*str == 0 || *str == ',')
slub_debug = DEBUG_DEFAULT_FLAGS;
else
for( ;*str && *str != ','; str++)
switch (*str) {
case 'f' : case 'F' :
slub_debug |= SLAB_DEBUG_FREE;
break;
case 'z' : case 'Z' :
slub_debug |= SLAB_RED_ZONE;
break;
case 'p' : case 'P' :
slub_debug |= SLAB_POISON;
break;
case 'u' : case 'U' :
slub_debug |= SLAB_STORE_USER;
break;
case 't' : case 'T' :
slub_debug |= SLAB_TRACE;
break;
default:
printk(KERN_ERR "slub_debug option '%c' "
"unknown. skipped\n",*str);
}
}

if (*str == ',')
slub_debug_slabs = str + 1;
return 1;
}

__setup("slub_debug", setup_slub_debug);

static void kmem_cache_open_debug_check(struct kmem_cache *s)
{
/*
* The page->offset field is only 16 bit wide. This is an offset
* in units of words from the beginning of an object. If the slab
* size is bigger then we cannot move the free pointer behind the
* object anymore.
*
* On 32 bit platforms the limit is 256k. On 64bit platforms
* the limit is 512k.
*
* Debugging or ctor/dtors may create a need to move the free
* pointer. Fail if this happens.
*/
if (s->size >= 65535 * sizeof(void *)) {
BUG_ON(s->flags & (SLAB_RED_ZONE | SLAB_POISON |
SLAB_STORE_USER | SLAB_DESTROY_BY_RCU));
BUG_ON(s->ctor || s->dtor);
}
else
/*
* Enable debugging if selected on the kernel commandline.
*/
if (slub_debug && (!slub_debug_slabs ||
strncmp(slub_debug_slabs, s->name,
strlen(slub_debug_slabs)) == 0))
s->flags |= slub_debug;
}
#else

static inline int alloc_object_checks(struct kmem_cache *s,
struct page *page, void *object) { return 0; }

static inline int free_object_checks(struct kmem_cache *s,
struct page *page, void *object) { return 0; }

static inline void add_full(struct kmem_cache_node *n, struct page *page) {}
static inline void remove_full(struct kmem_cache *s, struct page *page) {}
static inline void trace(struct kmem_cache *s, struct page *page,
void *object, int alloc) {}
static inline void init_object(struct kmem_cache *s,
void *object, int active) {}
static inline void init_tracking(struct kmem_cache *s, void *object) {}
static inline int slab_pad_check(struct kmem_cache *s, struct page *page)
{ return 1; }
static inline int check_object(struct kmem_cache *s, struct page *page,
void *object, int active) { return 1; }
static inline void set_track(struct kmem_cache *s, void *object,
enum track_item alloc, void *addr) {}
static inline void kmem_cache_open_debug_check(struct kmem_cache *s) {}
#define slub_debug 0
#endif
/*
* Slab allocation and freeing
*/
Expand Down Expand Up @@ -1445,13 +1552,6 @@ static int slub_min_objects = DEFAULT_MIN_OBJECTS;
*/
static int slub_nomerge;

/*
* Debug settings:
*/
static int slub_debug;

static char *slub_debug_slabs;

/*
* Calculate the order of allocation given an slab object size.
*
Expand Down Expand Up @@ -1660,20 +1760,23 @@ static int calculate_sizes(struct kmem_cache *s)
*/
size = ALIGN(size, sizeof(void *));

#ifdef CONFIG_SLUB_DEBUG
/*
* If we are Redzoning then check if there is some space between the
* end of the object and the free pointer. If not then add an
* additional word to have some bytes to store Redzone information.
*/
if ((flags & SLAB_RED_ZONE) && size == s->objsize)
size += sizeof(void *);
#endif

/*
* With that we have determined the number of bytes in actual use
* by the object. This is the potential offset to the free pointer.
*/
s->inuse = size;

#ifdef CONFIG_SLUB_DEBUG
if (((flags & (SLAB_DESTROY_BY_RCU | SLAB_POISON)) ||
s->ctor || s->dtor)) {
/*
Expand Down Expand Up @@ -1704,6 +1807,7 @@ static int calculate_sizes(struct kmem_cache *s)
* of the object.
*/
size += sizeof(void *);
#endif

/*
* Determine the alignment based on various parameters that the
Expand Down Expand Up @@ -1753,32 +1857,7 @@ static int kmem_cache_open(struct kmem_cache *s, gfp_t gfpflags,
s->objsize = size;
s->flags = flags;
s->align = align;

/*
* The page->offset field is only 16 bit wide. This is an offset
* in units of words from the beginning of an object. If the slab
* size is bigger then we cannot move the free pointer behind the
* object anymore.
*
* On 32 bit platforms the limit is 256k. On 64bit platforms
* the limit is 512k.
*
* Debugging or ctor/dtors may create a need to move the free
* pointer. Fail if this happens.
*/
if (s->size >= 65535 * sizeof(void *)) {
BUG_ON(flags & (SLAB_RED_ZONE | SLAB_POISON |
SLAB_STORE_USER | SLAB_DESTROY_BY_RCU));
BUG_ON(ctor || dtor);
}
else
/*
* Enable debugging if selected on the kernel commandline.
*/
if (slub_debug && (!slub_debug_slabs ||
strncmp(slub_debug_slabs, name,
strlen(slub_debug_slabs)) == 0))
s->flags |= slub_debug;
kmem_cache_open_debug_check(s);

if (!calculate_sizes(s))
goto error;
Expand Down Expand Up @@ -1949,45 +2028,6 @@ static int __init setup_slub_nomerge(char *str)

__setup("slub_nomerge", setup_slub_nomerge);

static int __init setup_slub_debug(char *str)
{
if (!str || *str != '=')
slub_debug = DEBUG_DEFAULT_FLAGS;
else {
str++;
if (*str == 0 || *str == ',')
slub_debug = DEBUG_DEFAULT_FLAGS;
else
for( ;*str && *str != ','; str++)
switch (*str) {
case 'f' : case 'F' :
slub_debug |= SLAB_DEBUG_FREE;
break;
case 'z' : case 'Z' :
slub_debug |= SLAB_RED_ZONE;
break;
case 'p' : case 'P' :
slub_debug |= SLAB_POISON;
break;
case 'u' : case 'U' :
slub_debug |= SLAB_STORE_USER;
break;
case 't' : case 'T' :
slub_debug |= SLAB_TRACE;
break;
default:
printk(KERN_ERR "slub_debug option '%c' "
"unknown. skipped\n",*str);
}
}

if (*str == ',')
slub_debug_slabs = str + 1;
return 1;
}

__setup("slub_debug", setup_slub_debug);

static struct kmem_cache *create_kmalloc_cache(struct kmem_cache *s,
const char *name, int size, gfp_t gfp_flags)
{
Expand Down Expand Up @@ -2554,8 +2594,7 @@ void *__kmalloc_node_track_caller(size_t size, gfp_t gfpflags,
return slab_alloc(s, gfpflags, node, caller);
}

#ifdef CONFIG_SYSFS

#if defined(CONFIG_SYSFS) && defined(CONFIG_SLUB_DEBUG)
static int validate_slab(struct kmem_cache *s, struct page *page)
{
void *p;
Expand Down

0 comments on commit 41ecc55

Please sign in to comment.