bootmem: print request details before BUG_ON(them)
[linux-2.6.git] / mm / slab.c
index ff31261..ddc41f3 100644 (file)
--- a/mm/slab.c
+++ b/mm/slab.c
@@ -95,6 +95,7 @@
 #include       <linux/init.h>
 #include       <linux/compiler.h>
 #include       <linux/cpuset.h>
+#include       <linux/proc_fs.h>
 #include       <linux/seq_file.h>
 #include       <linux/notifier.h>
 #include       <linux/kallsyms.h>
 #include       <linux/fault-inject.h>
 #include       <linux/rtmutex.h>
 #include       <linux/reciprocal_div.h>
+#include       <linux/debugobjects.h>
 
 #include       <asm/cacheflush.h>
 #include       <asm/tlbflush.h>
 #define        BYTES_PER_WORD          sizeof(void *)
 #define        REDZONE_ALIGN           max(BYTES_PER_WORD, __alignof__(unsigned long long))
 
-#ifndef cache_line_size
-#define cache_line_size()      L1_CACHE_BYTES
-#endif
-
 #ifndef ARCH_KMALLOC_MINALIGN
 /*
  * Enforce a minimum alignment for the kmalloc caches.
                         SLAB_CACHE_DMA | \
                         SLAB_STORE_USER | \
                         SLAB_RECLAIM_ACCOUNT | SLAB_PANIC | \
-                        SLAB_DESTROY_BY_RCU | SLAB_MEM_SPREAD)
+                        SLAB_DESTROY_BY_RCU | SLAB_MEM_SPREAD | \
+                        SLAB_DEBUG_OBJECTS)
 #else
 # define CREATE_MASK   (SLAB_HWCACHE_ALIGN | \
                         SLAB_CACHE_DMA | \
                         SLAB_RECLAIM_ACCOUNT | SLAB_PANIC | \
-                        SLAB_DESTROY_BY_RCU | SLAB_MEM_SPREAD)
+                        SLAB_DESTROY_BY_RCU | SLAB_MEM_SPREAD | \
+                        SLAB_DEBUG_OBJECTS)
 #endif
 
 /*
@@ -333,7 +333,7 @@ static __always_inline int index_of(const size_t size)
                return i; \
        else \
                i++;
-#include "linux/kmalloc_sizes.h"
+#include <linux/kmalloc_sizes.h>
 #undef CACHE
                __bad_size();
        } else
@@ -407,7 +407,7 @@ struct kmem_cache {
        unsigned int dflags;            /* dynamic flags */
 
        /* constructor func */
-       void (*ctor)(struct kmem_cache *, void *);
+       void (*ctor)(void *obj);
 
 /* 5) cache creation/removal */
        const char *name;
@@ -730,8 +730,7 @@ static inline void init_lock_keys(void)
 #endif
 
 /*
- * 1. Guard access to the cache-chain.
- * 2. Protect sanity of cpu_online_map against cpu hotplug events
+ * Guard access to the cache-chain.
  */
 static DEFINE_MUTEX(cache_chain_mutex);
 static struct list_head cache_chain;
@@ -863,7 +862,7 @@ static void cache_estimate(unsigned long gfporder, size_t buffer_size,
        *left_over = slab_size - nr_objs*buffer_size - mgmt_size;
 }
 
-#define slab_error(cachep, msg) __slab_error(__FUNCTION__, cachep, msg)
+#define slab_error(cachep, msg) __slab_error(__func__, cachep, msg)
 
 static void __slab_error(const char *function, struct kmem_cache *cachep,
                        char *msg)
@@ -1161,14 +1160,13 @@ static void __cpuinit cpuup_canceled(long cpu)
        struct kmem_cache *cachep;
        struct kmem_list3 *l3 = NULL;
        int node = cpu_to_node(cpu);
+       node_to_cpumask_ptr(mask, node);
 
        list_for_each_entry(cachep, &cache_chain, next) {
                struct array_cache *nc;
                struct array_cache *shared;
                struct array_cache **alien;
-               cpumask_t mask;
 
-               mask = node_to_cpumask(node);
                /* cpu is dead; no one can alloc from it. */
                nc = cachep->array[cpu];
                cachep->array[cpu] = NULL;
@@ -1184,7 +1182,7 @@ static void __cpuinit cpuup_canceled(long cpu)
                if (nc)
                        free_block(cachep, nc->entry, nc->avail, node);
 
-               if (!cpus_empty(mask)) {
+               if (!cpus_empty(*mask)) {
                        spin_unlock_irq(&l3->list_lock);
                        goto free_array_cache;
                }
@@ -1331,12 +1329,11 @@ static int __cpuinit cpuup_callback(struct notifier_block *nfb,
        int err = 0;
 
        switch (action) {
-       case CPU_LOCK_ACQUIRE:
-               mutex_lock(&cache_chain_mutex);
-               break;
        case CPU_UP_PREPARE:
        case CPU_UP_PREPARE_FROZEN:
+               mutex_lock(&cache_chain_mutex);
                err = cpuup_prepare(cpu);
+               mutex_unlock(&cache_chain_mutex);
                break;
        case CPU_ONLINE:
        case CPU_ONLINE_FROZEN:
@@ -1373,9 +1370,8 @@ static int __cpuinit cpuup_callback(struct notifier_block *nfb,
 #endif
        case CPU_UP_CANCELED:
        case CPU_UP_CANCELED_FROZEN:
+               mutex_lock(&cache_chain_mutex);
                cpuup_canceled(cpu);
-               break;
-       case CPU_LOCK_RELEASE:
                mutex_unlock(&cache_chain_mutex);
                break;
        }
@@ -1484,7 +1480,7 @@ void __init kmem_cache_init(void)
        list_add(&cache_cache.next, &cache_chain);
        cache_cache.colour_off = cache_line_size();
        cache_cache.array[smp_processor_id()] = &initarray_cache.cache;
-       cache_cache.nodelists[node] = &initkmem_list3[CACHE_CACHE];
+       cache_cache.nodelists[node] = &initkmem_list3[CACHE_CACHE + node];
 
        /*
         * struct kmem_cache size depends on nr_node_ids, which
@@ -1605,7 +1601,7 @@ void __init kmem_cache_init(void)
                int nid;
 
                for_each_online_node(nid) {
-                       init_list(&cache_cache, &initkmem_list3[CACHE_CACHE], nid);
+                       init_list(&cache_cache, &initkmem_list3[CACHE_CACHE + nid], nid);
 
                        init_list(malloc_sizes[INDEX_AC].cs_cachep,
                                  &initkmem_list3[SIZE_AC + nid], nid);
@@ -1906,15 +1902,7 @@ static void check_poison_obj(struct kmem_cache *cachep, void *objp)
 #endif
 
 #if DEBUG
-/**
- * slab_destroy_objs - destroy a slab and its objects
- * @cachep: cache pointer being destroyed
- * @slabp: slab pointer being destroyed
- *
- * Call the registered destructor for each object in a slab that is being
- * destroyed.
- */
-static void slab_destroy_objs(struct kmem_cache *cachep, struct slab *slabp)
+static void slab_destroy_debugcheck(struct kmem_cache *cachep, struct slab *slabp)
 {
        int i;
        for (i = 0; i < cachep->num; i++) {
@@ -1943,7 +1931,7 @@ static void slab_destroy_objs(struct kmem_cache *cachep, struct slab *slabp)
        }
 }
 #else
-static void slab_destroy_objs(struct kmem_cache *cachep, struct slab *slabp)
+static void slab_destroy_debugcheck(struct kmem_cache *cachep, struct slab *slabp)
 {
 }
 #endif
@@ -1961,7 +1949,7 @@ static void slab_destroy(struct kmem_cache *cachep, struct slab *slabp)
 {
        void *addr = slabp->s_mem - slabp->colouroff;
 
-       slab_destroy_objs(cachep, slabp);
+       slab_destroy_debugcheck(cachep, slabp);
        if (unlikely(cachep->flags & SLAB_DESTROY_BY_RCU)) {
                struct slab_rcu *slab_rcu;
 
@@ -2135,6 +2123,8 @@ static int __init_refok setup_cpu_cache(struct kmem_cache *cachep)
  *
  * @name must be valid until the cache is destroyed. This implies that
  * the module calling this has to destroy the cache before getting unloaded.
+ * Note that kmem_cache_name() is not guaranteed to return the same pointer,
+ * therefore applications must manage it themselves.
  *
  * The flags are
  *
@@ -2150,8 +2140,7 @@ static int __init_refok setup_cpu_cache(struct kmem_cache *cachep)
  */
 struct kmem_cache *
 kmem_cache_create (const char *name, size_t size, size_t align,
-       unsigned long flags,
-       void (*ctor)(struct kmem_cache *, void *))
+       unsigned long flags, void (*ctor)(void *))
 {
        size_t left_over, slab_size, ralign;
        struct kmem_cache *cachep = NULL, *pc;
@@ -2161,15 +2150,16 @@ kmem_cache_create (const char *name, size_t size, size_t align,
         */
        if (!name || in_interrupt() || (size < BYTES_PER_WORD) ||
            size > KMALLOC_MAX_SIZE) {
-               printk(KERN_ERR "%s: Early error in slab %s\n", __FUNCTION__,
+               printk(KERN_ERR "%s: Early error in slab %s\n", __func__,
                                name);
                BUG();
        }
 
        /*
         * We use cache_chain_mutex to ensure a consistent view of
-        * cpu_online_map as well.  Please see cpuup_callback
+        * cpu_online_mask as well.  Please see cpuup_callback
         */
+       get_online_cpus();
        mutex_lock(&cache_chain_mutex);
 
        list_for_each_entry(pc, &cache_chain, next) {
@@ -2396,6 +2386,7 @@ oops:
                panic("kmem_cache_create(): failed to create slab `%s'\n",
                      name);
        mutex_unlock(&cache_chain_mutex);
+       put_online_cpus();
        return cachep;
 }
 EXPORT_SYMBOL(kmem_cache_create);
@@ -2457,7 +2448,7 @@ static void drain_cpu_caches(struct kmem_cache *cachep)
        struct kmem_list3 *l3;
        int node;
 
-       on_each_cpu(do_drain, cachep, 1, 1);
+       on_each_cpu(do_drain, cachep, 1);
        check_irq_on();
        for_each_online_node(node) {
                l3 = cachep->nodelists[node];
@@ -2547,9 +2538,11 @@ int kmem_cache_shrink(struct kmem_cache *cachep)
        int ret;
        BUG_ON(!cachep || in_interrupt());
 
+       get_online_cpus();
        mutex_lock(&cache_chain_mutex);
        ret = __cache_shrink(cachep);
        mutex_unlock(&cache_chain_mutex);
+       put_online_cpus();
        return ret;
 }
 EXPORT_SYMBOL(kmem_cache_shrink);
@@ -2575,6 +2568,7 @@ void kmem_cache_destroy(struct kmem_cache *cachep)
        BUG_ON(!cachep || in_interrupt());
 
        /* Find the cache in the chain of caches. */
+       get_online_cpus();
        mutex_lock(&cache_chain_mutex);
        /*
         * the chain is never empty, cache_cache is never destroyed
@@ -2584,6 +2578,7 @@ void kmem_cache_destroy(struct kmem_cache *cachep)
                slab_error(cachep, "Can't free all objects");
                list_add(&cachep->next, &cache_chain);
                mutex_unlock(&cache_chain_mutex);
+               put_online_cpus();
                return;
        }
 
@@ -2592,6 +2587,7 @@ void kmem_cache_destroy(struct kmem_cache *cachep)
 
        __kmem_cache_destroy(cachep);
        mutex_unlock(&cache_chain_mutex);
+       put_online_cpus();
 }
 EXPORT_SYMBOL(kmem_cache_destroy);
 
@@ -2615,7 +2611,7 @@ static struct slab *alloc_slabmgmt(struct kmem_cache *cachep, void *objp,
        if (OFF_SLAB(cachep)) {
                /* Slab management obj is off-slab. */
                slabp = kmem_cache_alloc_node(cachep->slabp_cache,
-                                             local_flags & ~GFP_THISNODE, nodeid);
+                                             local_flags, nodeid);
                if (!slabp)
                        return NULL;
        } else {
@@ -2626,6 +2622,7 @@ static struct slab *alloc_slabmgmt(struct kmem_cache *cachep, void *objp,
        slabp->colouroff = colour_off;
        slabp->s_mem = objp + colour_off;
        slabp->nodeid = nodeid;
+       slabp->free = 0;
        return slabp;
 }
 
@@ -2658,7 +2655,7 @@ static void cache_init_objs(struct kmem_cache *cachep,
                 * They must also be threaded.
                 */
                if (cachep->ctor && !(cachep->flags & SLAB_POISON))
-                       cachep->ctor(cachep, objp + obj_offset(cachep));
+                       cachep->ctor(objp + obj_offset(cachep));
 
                if (cachep->flags & SLAB_RED_ZONE) {
                        if (*dbg_redzone2(cachep, objp) != RED_INACTIVE)
@@ -2674,12 +2671,11 @@ static void cache_init_objs(struct kmem_cache *cachep,
                                         cachep->buffer_size / PAGE_SIZE, 0);
 #else
                if (cachep->ctor)
-                       cachep->ctor(cachep, objp);
+                       cachep->ctor(objp);
 #endif
                slab_bufctl(slabp)[i] = i + 1;
        }
        slab_bufctl(slabp)[i - 1] = BUFCTL_END;
-       slabp->free = 0;
 }
 
 static void kmem_flagcheck(struct kmem_cache *cachep, gfp_t flags)
@@ -2812,7 +2808,6 @@ static int cache_grow(struct kmem_cache *cachep,
        if (!slabp)
                goto opps1;
 
-       slabp->nodeid = nodeid;
        slab_map_pages(cachep, slabp, objp);
 
        cache_init_objs(cachep, slabp);
@@ -2961,11 +2956,10 @@ static void *cache_alloc_refill(struct kmem_cache *cachep, gfp_t flags)
        struct array_cache *ac;
        int node;
 
-       node = numa_node_id();
-
+retry:
        check_irq_off();
+       node = numa_node_id();
        ac = cpu_cache_get(cachep);
-retry:
        batchcount = ac->batchcount;
        if (!ac->touched && batchcount > BATCHREFILL_LIMIT) {
                /*
@@ -3005,7 +2999,7 @@ retry:
                 * there must be at least one object available for
                 * allocation.
                 */
-               BUG_ON(slabp->inuse < 0 || slabp->inuse >= cachep->num);
+               BUG_ON(slabp->inuse >= cachep->num);
 
                while (slabp->inuse < cachep->num && batchcount--) {
                        STATS_INC_ALLOCED(cachep);
@@ -3101,7 +3095,7 @@ static void *cache_alloc_debugcheck_after(struct kmem_cache *cachep,
 #endif
        objp += obj_offset(cachep);
        if (cachep->ctor && cachep->flags & SLAB_POISON)
-               cachep->ctor(cachep, objp);
+               cachep->ctor(objp);
 #if ARCH_SLAB_MINALIGN
        if ((u32)objp & (ARCH_SLAB_MINALIGN-1)) {
                printk(KERN_ERR "0x%p: not aligned to ARCH_SLAB_MINALIGN=%d\n",
@@ -3114,79 +3108,14 @@ static void *cache_alloc_debugcheck_after(struct kmem_cache *cachep,
 #define cache_alloc_debugcheck_after(a,b,objp,d) (objp)
 #endif
 
-#ifdef CONFIG_FAILSLAB
-
-static struct failslab_attr {
-
-       struct fault_attr attr;
-
-       u32 ignore_gfp_wait;
-#ifdef CONFIG_FAULT_INJECTION_DEBUG_FS
-       struct dentry *ignore_gfp_wait_file;
-#endif
-
-} failslab = {
-       .attr = FAULT_ATTR_INITIALIZER,
-       .ignore_gfp_wait = 1,
-};
-
-static int __init setup_failslab(char *str)
-{
-       return setup_fault_attr(&failslab.attr, str);
-}
-__setup("failslab=", setup_failslab);
-
-static int should_failslab(struct kmem_cache *cachep, gfp_t flags)
+static bool slab_should_failslab(struct kmem_cache *cachep, gfp_t flags)
 {
        if (cachep == &cache_cache)
-               return 0;
-       if (flags & __GFP_NOFAIL)
-               return 0;
-       if (failslab.ignore_gfp_wait && (flags & __GFP_WAIT))
-               return 0;
-
-       return should_fail(&failslab.attr, obj_size(cachep));
-}
-
-#ifdef CONFIG_FAULT_INJECTION_DEBUG_FS
-
-static int __init failslab_debugfs(void)
-{
-       mode_t mode = S_IFREG | S_IRUSR | S_IWUSR;
-       struct dentry *dir;
-       int err;
-
-       err = init_fault_attr_dentries(&failslab.attr, "failslab");
-       if (err)
-               return err;
-       dir = failslab.attr.dentries.dir;
-
-       failslab.ignore_gfp_wait_file =
-               debugfs_create_bool("ignore-gfp-wait", mode, dir,
-                                     &failslab.ignore_gfp_wait);
-
-       if (!failslab.ignore_gfp_wait_file) {
-               err = -ENOMEM;
-               debugfs_remove(failslab.ignore_gfp_wait_file);
-               cleanup_fault_attr_dentries(&failslab.attr);
-       }
-
-       return err;
-}
+               return false;
 
-late_initcall(failslab_debugfs);
-
-#endif /* CONFIG_FAULT_INJECTION_DEBUG_FS */
-
-#else /* CONFIG_FAILSLAB */
-
-static inline int should_failslab(struct kmem_cache *cachep, gfp_t flags)
-{
-       return 0;
+       return should_failslab(obj_size(cachep), flags);
 }
 
-#endif /* CONFIG_FAILSLAB */
-
 static inline void *____cache_alloc(struct kmem_cache *cachep, gfp_t flags)
 {
        void *objp;
@@ -3241,15 +3170,16 @@ static void *fallback_alloc(struct kmem_cache *cache, gfp_t flags)
 {
        struct zonelist *zonelist;
        gfp_t local_flags;
-       struct zone **z;
+       struct zoneref *z;
+       struct zone *zone;
+       enum zone_type high_zoneidx = gfp_zone(flags);
        void *obj = NULL;
        int nid;
 
        if (flags & __GFP_THISNODE)
                return NULL;
 
-       zonelist = &NODE_DATA(slab_node(current->mempolicy))
-                       ->node_zonelists[gfp_zone(flags)];
+       zonelist = node_zonelist(slab_node(current->mempolicy), flags);
        local_flags = flags & (GFP_CONSTRAINT_MASK|GFP_RECLAIM_MASK);
 
 retry:
@@ -3257,14 +3187,17 @@ retry:
         * Look through allowed nodes for objects available
         * from existing per node queues.
         */
-       for (z = zonelist->zones; *z && !obj; z++) {
-               nid = zone_to_nid(*z);
+       for_each_zone_zonelist(zone, z, zonelist, high_zoneidx) {
+               nid = zone_to_nid(zone);
 
-               if (cpuset_zone_allowed_hardwall(*z, flags) &&
+               if (cpuset_zone_allowed_hardwall(zone, flags) &&
                        cache->nodelists[nid] &&
-                       cache->nodelists[nid]->free_objects)
+                       cache->nodelists[nid]->free_objects) {
                                obj = ____cache_alloc_node(cache,
                                        flags | GFP_THISNODE, nid);
+                               if (obj)
+                                       break;
+               }
        }
 
        if (!obj) {
@@ -3277,7 +3210,7 @@ retry:
                if (local_flags & __GFP_WAIT)
                        local_irq_enable();
                kmem_flagcheck(cache, flags);
-               obj = kmem_getpages(cache, flags, -1);
+               obj = kmem_getpages(cache, local_flags, -1);
                if (local_flags & __GFP_WAIT)
                        local_irq_disable();
                if (obj) {
@@ -3385,7 +3318,7 @@ __cache_alloc_node(struct kmem_cache *cachep, gfp_t flags, int nodeid,
        unsigned long save_flags;
        void *ptr;
 
-       if (should_failslab(cachep, flags))
+       if (slab_should_failslab(cachep, flags))
                return NULL;
 
        cache_alloc_debugcheck_before(cachep, flags);
@@ -3461,7 +3394,7 @@ __cache_alloc(struct kmem_cache *cachep, gfp_t flags, void *caller)
        unsigned long save_flags;
        void *objp;
 
-       if (should_failslab(cachep, flags))
+       if (slab_should_failslab(cachep, flags))
                return NULL;
 
        cache_alloc_debugcheck_before(cachep, flags);
@@ -3622,12 +3555,11 @@ void *kmem_cache_alloc(struct kmem_cache *cachep, gfp_t flags)
 EXPORT_SYMBOL(kmem_cache_alloc);
 
 /**
- * kmem_ptr_validate - check if an untrusted pointer might
- *     be a slab entry.
+ * kmem_ptr_validate - check if an untrusted pointer might be a slab entry.
  * @cachep: the cache we're checking against
  * @ptr: pointer to validate
  *
- * This verifies that the untrusted pointer looks sane:
+ * This verifies that the untrusted pointer looks sane;
  * it is _not_ a guarantee that the pointer is actually
  * part of the slab cache in question, but it at least
  * validates that the pointer can be dereferenced and
@@ -3691,9 +3623,9 @@ void *__kmalloc_node(size_t size, gfp_t flags, int node)
 EXPORT_SYMBOL(__kmalloc_node);
 
 void *__kmalloc_node_track_caller(size_t size, gfp_t flags,
-               int node, void *caller)
+               int node, unsigned long caller)
 {
-       return __do_kmalloc_node(size, flags, node, caller);
+       return __do_kmalloc_node(size, flags, node, (void *)caller);
 }
 EXPORT_SYMBOL(__kmalloc_node_track_caller);
 #else
@@ -3735,9 +3667,9 @@ void *__kmalloc(size_t size, gfp_t flags)
 }
 EXPORT_SYMBOL(__kmalloc);
 
-void *__kmalloc_track_caller(size_t size, gfp_t flags, void *caller)
+void *__kmalloc_track_caller(size_t size, gfp_t flags, unsigned long caller)
 {
-       return __do_kmalloc(size, flags, caller);
+       return __do_kmalloc(size, flags, (void *)caller);
 }
 EXPORT_SYMBOL(__kmalloc_track_caller);
 
@@ -3763,6 +3695,8 @@ void kmem_cache_free(struct kmem_cache *cachep, void *objp)
 
        local_irq_save(flags);
        debug_check_no_locks_freed(objp, obj_size(cachep));
+       if (!(cachep->flags & SLAB_DEBUG_OBJECTS))
+               debug_check_no_obj_freed(objp, obj_size(cachep));
        __cache_free(cachep, objp);
        local_irq_restore(flags);
 }
@@ -3788,6 +3722,7 @@ void kfree(const void *objp)
        kfree_debugcheck(objp);
        c = virt_to_cache(objp);
        debug_check_no_locks_freed(objp, obj_size(c));
+       debug_check_no_obj_freed(objp, obj_size(c));
        __cache_free(c, (void *)objp);
        local_irq_restore(flags);
 }
@@ -3933,7 +3868,7 @@ static int do_tune_cpucache(struct kmem_cache *cachep, int limit,
        }
        new->cachep = cachep;
 
-       on_each_cpu(do_ccupdate_local, (void *)new, 1, 1);
+       on_each_cpu(do_ccupdate_local, (void *)new, 1);
 
        check_irq_on();
        cachep->batchcount = batchcount;
@@ -4261,7 +4196,7 @@ static int s_show(struct seq_file *m, void *p)
  * + further values on SMP and with statistics enabled
  */
 
-const struct seq_operations slabinfo_op = {
+static const struct seq_operations slabinfo_op = {
        .start = s_start,
        .next = s_next,
        .stop = s_stop,
@@ -4318,6 +4253,19 @@ ssize_t slabinfo_write(struct file *file, const char __user * buffer,
        return res;
 }
 
+static int slabinfo_open(struct inode *inode, struct file *file)
+{
+       return seq_open(file, &slabinfo_op);
+}
+
+static const struct file_operations proc_slabinfo_operations = {
+       .open           = slabinfo_open,
+       .read           = seq_read,
+       .write          = slabinfo_write,
+       .llseek         = seq_lseek,
+       .release        = seq_release,
+};
+
 #ifdef CONFIG_DEBUG_SLAB_LEAK
 
 static void *leaks_start(struct seq_file *m, loff_t *pos)
@@ -4446,13 +4394,47 @@ static int leaks_show(struct seq_file *m, void *p)
        return 0;
 }
 
-const struct seq_operations slabstats_op = {
+static const struct seq_operations slabstats_op = {
        .start = leaks_start,
        .next = s_next,
        .stop = s_stop,
        .show = leaks_show,
 };
+
+static int slabstats_open(struct inode *inode, struct file *file)
+{
+       unsigned long *n = kzalloc(PAGE_SIZE, GFP_KERNEL);
+       int ret = -ENOMEM;
+       if (n) {
+               ret = seq_open(file, &slabstats_op);
+               if (!ret) {
+                       struct seq_file *m = file->private_data;
+                       *n = PAGE_SIZE / (2 * sizeof(unsigned long));
+                       m->private = n;
+                       n = NULL;
+               }
+               kfree(n);
+       }
+       return ret;
+}
+
+static const struct file_operations proc_slabstats_operations = {
+       .open           = slabstats_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = seq_release_private,
+};
 #endif
+
+static int __init slab_proc_init(void)
+{
+       proc_create("slabinfo",S_IWUSR|S_IRUGO,NULL,&proc_slabinfo_operations);
+#ifdef CONFIG_DEBUG_SLAB_LEAK
+       proc_create("slab_allocators", 0, NULL, &proc_slabstats_operations);
+#endif
+       return 0;
+}
+module_init(slab_proc_init);
 #endif
 
 /**
@@ -4475,4 +4457,3 @@ size_t ksize(const void *objp)
 
        return obj_size(virt_to_cache(objp));
 }
-EXPORT_SYMBOL(ksize);