Index: linux-2.6.21-rc4-mm1/fs/sysfs/mount.c =================================================================== --- linux-2.6.21-rc4-mm1.orig/fs/sysfs/mount.c 2007-03-15 17:20:01.000000000 -0700 +++ linux-2.6.21-rc4-mm1/fs/sysfs/mount.c 2007-03-21 11:31:50.000000000 -0700 @@ -109,6 +109,7 @@ int __init sysfs_init(void) } } else goto out_err; + slab_sysfs_init(); out: return err; out_err: Index: linux-2.6.21-rc4-mm1/include/linux/slab.h =================================================================== --- linux-2.6.21-rc4-mm1.orig/include/linux/slab.h 2007-03-20 16:00:28.000000000 -0700 +++ linux-2.6.21-rc4-mm1/include/linux/slab.h 2007-03-21 11:31:50.000000000 -0700 @@ -43,7 +43,8 @@ typedef struct kmem_cache kmem_cache_t _ * struct kmem_cache related prototypes */ void __init kmem_cache_init(void); -extern int slab_is_available(void); +int slab_is_available(void); +void __init slab_sysfs_init(void); struct kmem_cache *kmem_cache_create(const char *, size_t, size_t, unsigned long, @@ -190,7 +191,7 @@ static inline void *__kmalloc_node(size_ * allocator where we care about the real place the memory allocation * request comes from. */ -#ifdef CONFIG_DEBUG_SLAB +#if defined(CONFIG_DEBUG_SLAB) || defined(CONFIG_SLUB) extern void *__kmalloc_track_caller(size_t, gfp_t, void*); #define kmalloc_track_caller(size, flags) \ __kmalloc_track_caller(size, flags, __builtin_return_address(0)) @@ -208,7 +209,7 @@ extern void *__kmalloc_track_caller(size * standard allocator where we care about the real place the memory * allocation request comes from. */ -#ifdef CONFIG_DEBUG_SLAB +#if defined(CONFIG_DEBUG_SLAB) || defined(CONFIG_SLUB) extern void *__kmalloc_node_track_caller(size_t, gfp_t, int, void *); #define kmalloc_node_track_caller(size, flags, node) \ __kmalloc_node_track_caller(size, flags, node, \ Index: linux-2.6.21-rc4-mm1/include/linux/slub_def.h =================================================================== --- linux-2.6.21-rc4-mm1.orig/include/linux/slub_def.h 2007-03-20 16:00:28.000000000 -0700 +++ linux-2.6.21-rc4-mm1/include/linux/slub_def.h 2007-03-21 11:31:50.000000000 -0700 @@ -9,6 +9,7 @@ #include #include #include +#include struct kmem_cache_node { spinlock_t list_lock; /* Protect partial list and nr_partial */ @@ -26,6 +27,7 @@ struct kmem_cache { unsigned long flags; int size; /* Total size of an object */ int objects; /* Number of objects in slab */ + int align; /* Alignment */ struct kmem_cache_node local_node; int refcount; /* Refcount for destroy */ void (*ctor)(void *, struct kmem_cache *, unsigned long); @@ -34,8 +36,8 @@ struct kmem_cache { 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 */ + struct kobject kobj; /* For sysfs */ #ifdef CONFIG_SMP struct mutex flushing; atomic_t cpu_slabs; /* Index: linux-2.6.21-rc4-mm1/mm/slub.c =================================================================== --- linux-2.6.21-rc4-mm1.orig/mm/slub.c 2007-03-21 11:30:36.000000000 -0700 +++ linux-2.6.21-rc4-mm1/mm/slub.c 2007-03-21 11:35:43.000000000 -0700 @@ -93,17 +93,22 @@ static struct notifier_block slab_notifi static enum { DOWN, /* No slab functionality available */ PARTIAL, /* kmem_cache_open() works but kmalloc does not */ - UP /* Everything works */ + UP, /* Everything works */ + SYSFS /* Sysfs up */ } slab_state = DOWN; int slab_is_available(void) { - return slab_state == UP; + return slab_state >= UP; } /* A list of all slab caches on the system */ static DECLARE_RWSEM(slub_lock); LIST_HEAD(slab_caches); +static void sysfs_slab_add(struct kmem_cache *); +static void sysfs_slab_alias(struct kmem_cache *, const char *); +static void sysfs_slab_remove(struct kmem_cache *); + /******************************************************************** * Core slab cache functions *******************************************************************/ @@ -1366,7 +1371,7 @@ static int init_kmem_cache_nodes(struct int node; int local_node; - if (slab_state == UP) + if (slab_state >= UP) local_node = page_to_nid(virt_to_page(s)); else local_node = 0; @@ -1417,28 +1422,18 @@ static int init_kmem_cache_nodes(struct return 1; } -static int kmem_cache_open(struct kmem_cache *s, gfp_t gfpflags, - const char *name, size_t size, - size_t align, unsigned long flags, - void (*ctor)(void *, struct kmem_cache *, unsigned long), - void (*dtor)(void *, struct kmem_cache *, unsigned long)) +int calculate_sizes(struct kmem_cache *s) { int tentative_size; - - memset(s, 0, kmem_size); - BUG_ON(flags & SLUB_UNIMPLEMENTED); - - /* - * Enable debugging if selected on the kernel commandline. - */ - if (slub_debug && - (!slub_debug_slabs || - strncmp(slub_debug_slabs, name, strlen(slub_debug_slabs)) == 0)) - flags |= slub_debug; + unsigned long flags = s->flags; + unsigned long size = s->objsize; + unsigned long align = s->align; if ((flags & SLAB_POISON) && !(flags & SLAB_DESTROY_BY_RCU) && - !ctor && !dtor) + !s->ctor && !s->dtor) flags |= __OBJECT_POISON; + else + flags &= ~__OBJECT_POISON; tentative_size = ALIGN(size, calculate_alignment(align, flags)); @@ -1451,12 +1446,6 @@ static int kmem_cache_open(struct kmem_c flags &= ~(SLAB_RED_ZONE| SLAB_DEBUG_FREE | \ SLAB_STORE_USER | SLAB_POISON | __OBJECT_POISON); - s->name = name; - s->ctor = ctor; - s->dtor = dtor; - s->objsize = size; - s->flags = flags; - size = ALIGN(size, sizeof(void *)); /* @@ -1464,14 +1453,14 @@ static int kmem_cache_open(struct kmem_c * alignment. If not then add an additional word, so * that we have a guard value to check for overwrites. */ - if ((s->flags & SLAB_RED_ZONE) && size == s->objsize) + if ((flags & SLAB_RED_ZONE) && size == s->objsize) size += sizeof(void *); s->inuse = size; if (size * 2 < (PAGE_SIZE << calculate_order(size)) && ((flags & (SLAB_DESTROY_BY_RCU | SLAB_POISON)) || - ctor || dtor)) { + s->ctor || s->dtor)) { /* * Relocate free pointer after the object if it is not * permitted to overwrite the first word of the object on @@ -1494,10 +1483,40 @@ static int kmem_cache_open(struct kmem_c s->order = calculate_order(size); if (s->order < 0) - goto error; + return 0; s->objects = (PAGE_SIZE << s->order) / size; if (!s->objects || s->objects > 65535) + return 0; + return 1; + +} + +static int kmem_cache_open(struct kmem_cache *s, gfp_t gfpflags, + const char *name, size_t size, + size_t align, unsigned long flags, + void (*ctor)(void *, struct kmem_cache *, unsigned long), + void (*dtor)(void *, struct kmem_cache *, unsigned long)) +{ + memset(s, 0, kmem_size); + s->name = name; + s->ctor = ctor; + s->dtor = dtor; + s->objsize = size; + s->flags = flags; + s->align = align; + + BUG_ON(flags & SLUB_UNIMPLEMENTED); + + /* + * 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; + + if (!calculate_sizes(s)) goto error; s->refcount = 1; @@ -1621,6 +1640,7 @@ void kmem_cache_destroy(struct kmem_cach else { list_del(&s->list); BUG_ON(kmem_cache_close(s)); + sysfs_slab_remove(s); kfree(s); } up_write(&slub_lock); @@ -1647,7 +1667,8 @@ static unsigned long slab_objects(struct nr_slabs += atomic_read(&n->nr_slabs); nr_partial_slabs += n->nr_partial; - nodes[node] = atomic_read(&n->nr_slabs) + + if (nodes) + nodes[node] = atomic_read(&n->nr_slabs) + n->nr_partial; spin_lock_irqsave(&n->list_lock, flags); @@ -1661,7 +1682,8 @@ static unsigned long slab_objects(struct if (page) { nr_cpu_slabs++; in_cpu_slabs += page->inuse; - nodes[page_to_nid(page)]++; + if (nodes) + nodes[page_to_nid(page)]++; } } @@ -1767,6 +1789,7 @@ static struct kmem_cache *create_kmalloc name, size); list_add(&s->list, &slab_caches); up_write(&slub_lock); + sysfs_slab_add(s); return s; } @@ -2063,26 +2086,19 @@ struct kmem_cache *kmem_cache_create(con s = find_mergeable(size, align, flags, dtor, ctor); if (s) { s->refcount++; - if (!s->aliases) - s->aliases = kstrdup(name, flags); - else { - char *x = s->aliases; - - s->aliases = kasprintf(flags, "%s/%s", x, name); - kfree(x); - } - /* * Adjust the object sizes so that we clear * the complete object on kzalloc. */ s->objsize = max(s->objsize, (int)size); s->inuse = max(s->inuse, (int)ALIGN(size, sizeof(void *))); + sysfs_slab_alias(s, name); } else { s = kmalloc(kmem_size, GFP_KERNEL); if (s && kmem_cache_open(s, GFP_KERNEL, name, size, align, flags, ctor, dtor)) { list_add(&s->list, &slab_caches); + sysfs_slab_add(s); } else kfree(s); } @@ -2217,10 +2233,10 @@ static int s_show(struct seq_file *m, vo kfree(x); display_nodes(m, nodes); - if (s->aliases) { - seq_putc(m, ' '); - seq_puts(m, s->aliases); - } +// if (s->aliases) { +// seq_putc(m, ' '); +// seq_puts(m, s->aliases); +// } seq_putc(m, '\n'); return 0; } @@ -2397,3 +2413,421 @@ void *__kmalloc_node_track_caller(size_t return object; } +#ifdef CONFIG_SYSFS + +#define to_slab_attr(n) container_of(n, struct slab_attribute, attr) +#define to_slab(n) container_of(n, struct kmem_cache, kobj); + +struct slab_attribute { + struct attribute attr; + ssize_t (*show)(struct kmem_cache *s, char *buf); + ssize_t (*store)(struct kmem_cache *s, const char *x, size_t count); +}; + +#define SLAB_ATTR_RO(_name) \ + static struct slab_attribute _name##_attr = __ATTR_RO(_name) + +#define SLAB_ATTR(_name) \ + static struct slab_attribute _name##_attr = \ + __ATTR(_name, 0644, _name##_show, _name##_store) + + +static ssize_t size_show(struct kmem_cache *s, char *buf) +{ + return sprintf(buf, "%d\n", s->size); +} +SLAB_ATTR_RO(size); + +static ssize_t align_show(struct kmem_cache *s, char *buf) +{ + return sprintf(buf, "%d\n", s->align); +} +SLAB_ATTR_RO(align); + +static ssize_t object_size_show(struct kmem_cache *s, char *buf) +{ + return sprintf(buf, "%d\n", s->objsize); +} +SLAB_ATTR_RO(object_size); + +static ssize_t objs_per_slab_show(struct kmem_cache *s, char *buf) +{ + return sprintf(buf, "%d\n", s->objects); +} +SLAB_ATTR_RO(objs_per_slab); + +static ssize_t order_show(struct kmem_cache *s, char *buf) +{ + return sprintf(buf, "%d\n", s->order); +} +SLAB_ATTR_RO(order); + +static ssize_t ctor_show(struct kmem_cache *s, char *buf) +{ + if (s->ctor) + return sprint_symbol(buf, s->ctor); + return 0; +} +SLAB_ATTR_RO(ctor); + +static ssize_t dtor_show(struct kmem_cache *s, char *buf) +{ + if (s->dtor) + return sprint_symbol(buf, s->dtor); + return 0; +} +SLAB_ATTR_RO(dtor); + +static ssize_t aliases_show(struct kmem_cache *s, char *buf) +{ + return sprintf(buf, "%d\n", s->refcount - 1); +} +SLAB_ATTR_RO(aliases); + +static ssize_t slabs_show(struct kmem_cache *s, char *buf) +{ + unsigned long x; + + slab_objects(s, &x, NULL, NULL, NULL); + return sprintf(buf, "%lu\n", x); +} +SLAB_ATTR_RO(slabs); + +static ssize_t partial_show(struct kmem_cache *s, char *buf) +{ + unsigned long x; + + slab_objects(s, NULL, &x, NULL, NULL); + return sprintf(buf, "%lu\n", x); +} +SLAB_ATTR_RO(partial); + +static ssize_t cpu_slabs_show(struct kmem_cache *s, char *buf) +{ + unsigned long x; + + slab_objects(s, NULL, &x, NULL, NULL); + return sprintf(buf, "%lu\n", x); +} +SLAB_ATTR_RO(cpu_slabs); + +static ssize_t objects_show(struct kmem_cache *s, char *buf) +{ + unsigned long x; + + x = slab_objects(s, NULL, NULL, NULL, NULL); + return sprintf(buf, "%lu\n", x); +} +SLAB_ATTR_RO(objects); + +static ssize_t _sanity_checks_show(struct kmem_cache *s, char *buf) +{ + return sprintf(buf, "%d\n", !!(s->flags & SLAB_DEBUG_FREE)); +} + +static ssize_t _sanity_checks_store(struct kmem_cache *s, const char *buf, size_t length) +{ + s->flags &= ~SLAB_DEBUG_FREE; + if (buf[0] == '1') + s->flags |= SLAB_DEBUG_FREE; + return length; +} +SLAB_ATTR(_sanity_checks); + +static ssize_t _trace_show(struct kmem_cache *s, char *buf) +{ + return sprintf(buf, "%d\n", !!(s->flags & SLAB_TRACE)); +} + +static ssize_t _trace_store(struct kmem_cache *s, const char *buf, size_t length) +{ + s->flags &= ~SLAB_TRACE; + printk("_trace_store = %s\n", buf); + if (buf[0] == '1') + s->flags |= SLAB_TRACE; + return length; +} +SLAB_ATTR(_trace); + +#ifdef CONFIG_CPUSET +static ssize_t _mem_spread_show(struct kmem_cache *s, char *buf) +{ + return sprintf(buf, "%d\n", !!(s->flags & SLAB_MEM_SPREAD)); +} + +static ssize_t _mem_spread_store(struct kmem_cache *s, const char *buf, size_t length) +{ + s->flags &= ~SLAB_MEM_SPREAD; + if (buf[0] == '1') + s->flags |= SLAB_MEM_SPREAD; + return 1; +} +SLAB_ATTR(_mem_spread); +#endif + +static ssize_t _reclaim_account_show(struct kmem_cache *s, char *buf) +{ + return sprintf(buf, "%d\n", !!(s->flags & SLAB_RECLAIM_ACCOUNT)); +} + +static ssize_t _reclaim_account_store(struct kmem_cache *s, const char *buf, size_t length) +{ + s->flags &= ~SLAB_RECLAIM_ACCOUNT; + if (buf[0] == '1') + s->flags |= SLAB_RECLAIM_ACCOUNT; + return length; +} +SLAB_ATTR(_reclaim_account); + +static ssize_t _hwcache_align_show(struct kmem_cache *s, char *buf) +{ + return sprintf(buf, "%d\n", !!(s->flags & + (SLAB_HWCACHE_ALIGN|SLAB_MUST_HWCACHE_ALIGN))); +} +SLAB_ATTR_RO(_hwcache_align); + +#ifdef CONFIG_ZONE_DMA +static ssize_t _cache_dma_show(struct kmem_cache *s, char *buf) +{ + return sprintf(buf, "%d\n", !!(s->flags & SLAB_CACHE_DMA)); +} +SLAB_ATTR_RO(_cache_dma); +#endif + +static ssize_t _destroy_by_rcu_show(struct kmem_cache *s, char *buf) +{ + return sprintf(buf, "%d\n", !!(s->flags & SLAB_DESTROY_BY_RCU)); +} +SLAB_ATTR_RO(_destroy_by_rcu); + +static ssize_t _red_zone_show(struct kmem_cache *s, char *buf) +{ + return sprintf(buf, "%d\n", !!(s->flags & SLAB_RED_ZONE)); +} + +static ssize_t _red_zone_store(struct kmem_cache *s, const char *buf, size_t length) +{ + if (slab_objects(s, NULL, NULL, NULL, NULL)) + return -EBUSY; + + s->flags &= ~SLAB_RED_ZONE; + if (buf[0] == '1') + s->flags |= SLAB_RED_ZONE; + calculate_sizes(s); + return length; +} +SLAB_ATTR(_red_zone); + +static ssize_t _poison_show(struct kmem_cache *s, char *buf) +{ + return sprintf(buf, "%d\n", !!(s->flags & SLAB_POISON)); +} + +static ssize_t _poison_store(struct kmem_cache *s, const char *buf, size_t length) +{ + if (slab_objects(s, NULL, NULL, NULL, NULL)) + return -EBUSY; + + s->flags &= ~SLAB_POISON; + if (buf[0] == '1') + s->flags |= SLAB_POISON; + calculate_sizes(s); + return length; +} +SLAB_ATTR(_poison); + +static ssize_t _store_user_show(struct kmem_cache *s, char *buf) +{ + return sprintf(buf, "%d\n", !!(s->flags & SLAB_STORE_USER)); +} + +static ssize_t _store_user_store(struct kmem_cache *s, const char *buf, size_t length) +{ + if (slab_objects(s, NULL, NULL, NULL, NULL)) + return -EBUSY; + + s->flags &= ~SLAB_STORE_USER; + if (buf[0] == '1') + s->flags |= SLAB_STORE_USER; + calculate_sizes(s); + return length; +} +SLAB_ATTR(_store_user); + +static struct attribute * slab_attrs[] = { + &size_attr.attr, + &object_size_attr.attr, + &objs_per_slab_attr.attr, + &order_attr.attr, + &objects_attr.attr, + &slabs_attr.attr, + &partial_attr.attr, + &cpu_slabs_attr.attr, + &ctor_attr.attr, + &dtor_attr.attr, + &aliases_attr.attr, + &align_attr.attr, + &_sanity_checks_attr.attr, + &_trace_attr.attr, + &_hwcache_align_attr.attr, + &_reclaim_account_attr.attr, + &_destroy_by_rcu_attr.attr, + &_red_zone_attr.attr, + &_poison_attr.attr, + &_store_user_attr.attr, +#ifdef CONFIG_CPUSET + &_mem_spread_attr.attr, +#endif +#ifdef CONFIG_ZONE_DMA + &_cache_dma_attr.attr, +#endif + NULL +}; + +static struct attribute_group slab_attr_group = { + .attrs = slab_attrs, +}; + +static ssize_t slab_attr_show(struct kobject *kobj, + struct attribute *attr, + char *buf) +{ + struct slab_attribute *attribute; + struct kmem_cache *s; + int err; + + attribute = to_slab_attr(attr); + s = to_slab(kobj); + + if (!attribute->show) + return -EIO; + + err = attribute->show(s, buf); + + return err; +} + +static ssize_t slab_attr_store(struct kobject *kobj, + struct attribute *attr, + const char *buf, size_t len) +{ + struct slab_attribute *attribute; + struct kmem_cache *s; + int err; + + attribute = to_slab_attr(attr); + s = to_slab(kobj); + + if (!attribute->store) + return -EIO; + + err = attribute->store(s, buf, len); + + return err; +} + +static struct sysfs_ops slab_sysfs_ops = { + .show = slab_attr_show, + .store = slab_attr_store, +}; + +static struct kobj_type slab_ktype = { + .sysfs_ops = &slab_sysfs_ops, +}; + +static int uevent_filter(struct kset *kset, struct kobject *kobj) +{ + struct kobj_type *ktype = get_ktype(kobj); + + if (ktype == &slab_ktype) + return 1; + return 0; +} + +static struct kset_uevent_ops slab_uevent_ops = { + .filter = uevent_filter, +}; + +decl_subsys(slab, &slab_ktype, &slab_uevent_ops); + +void sysfs_slab_add(struct kmem_cache *s) +{ + int err; + + if (slab_state < SYSFS) + return; + + kobj_set_kset_s(s, slab_subsys); + kobject_set_name(&s->kobj, s->name); + kobject_init(&s->kobj); + err = kobject_add(&s->kobj); + BUG_ON(err < 0); + err = sysfs_create_group(&s->kobj, &slab_attr_group); + kobject_uevent(&s->kobj, KOBJ_ADD); +} + +void sysfs_slab_remove(struct kmem_cache *s) +{ + kobject_uevent(&s->kobj, KOBJ_REMOVE); + + /* Individual field destruct here */ + + kobject_del(&s->kobj); +} + +struct saved_alias { + struct kmem_cache *s; + const char *name; + struct saved_alias *next; +}; + +struct saved_alias *alias_list; + +void sysfs_slab_alias(struct kmem_cache *s, const char *name) +{ + int err; + + if (slab_state < SYSFS) { + struct saved_alias *al = + kmalloc(sizeof(struct saved_alias), GFP_KERNEL); + BUG_ON(!al); + al->s = s; + al->name = name; + al->next = alias_list; + alias_list = al; + return; + } + + err = sysfs_create_link(&slab_subsys.kset.kobj, &s->kobj, name); + BUG_ON(err < 0); +} + +void __init slab_sysfs_init(void) +{ + int err; + struct list_head *h; + + err = subsystem_register(&slab_subsys); + if (err) { + printk(KERN_ERR "Cannot register slab subsystem.\n"); + return; + } + + slab_state = SYSFS; + + list_for_each(h, &slab_caches) { + struct kmem_cache *s = + container_of(h, struct kmem_cache, list); + + sysfs_slab_add(s); + } + + while (alias_list) { + struct saved_alias *al = alias_list; + + alias_list = alias_list->next; + sysfs_slab_alias(al->s, al->name); + kfree(al); + } +} +#endif Index: linux-2.6.21-rc4-mm1/lib/Kconfig.debug =================================================================== --- linux-2.6.21-rc4-mm1.orig/lib/Kconfig.debug 2007-03-20 16:00:28.000000000 -0700 +++ linux-2.6.21-rc4-mm1/lib/Kconfig.debug 2007-03-21 11:31:50.000000000 -0700 @@ -157,8 +157,7 @@ config TIMER_STATS config DEBUG_SLAB bool "Debug slab memory allocations" - default y if SLUB - depends on (DEBUG_KERNEL && SLAB) || SLUB + depends on DEBUG_KERNEL && SLAB help Say Y here to have the kernel do limited verification on memory allocation as well as poisoning memory on free to catch use of freed