Commit 871751e25d956ad24f129ca972b7851feaa61d53
Committed by
Linus Torvalds
1 parent
f52ac8fec8
[PATCH] slab: implement /proc/slab_allocators
Implement /proc/slab_allocators. It produces output like: idr_layer_cache: 80 idr_pre_get+0x33/0x4e buffer_head: 2555 alloc_buffer_head+0x20/0x75 mm_struct: 9 mm_alloc+0x1e/0x42 mm_struct: 20 dup_mm+0x36/0x370 vm_area_struct: 384 dup_mm+0x18f/0x370 vm_area_struct: 151 do_mmap_pgoff+0x2e0/0x7c3 vm_area_struct: 1 split_vma+0x5a/0x10e vm_area_struct: 11 do_brk+0x206/0x2e2 vm_area_struct: 2 copy_vma+0xda/0x142 vm_area_struct: 9 setup_arg_pages+0x99/0x214 fs_cache: 8 copy_fs_struct+0x21/0x133 fs_cache: 29 copy_process+0xf38/0x10e3 files_cache: 30 alloc_files+0x1b/0xcf signal_cache: 81 copy_process+0xbaa/0x10e3 sighand_cache: 77 copy_process+0xe65/0x10e3 sighand_cache: 1 de_thread+0x4d/0x5f8 anon_vma: 241 anon_vma_prepare+0xd9/0xf3 size-2048: 1 add_sect_attrs+0x5f/0x145 size-2048: 2 journal_init_revoke+0x99/0x302 size-2048: 2 journal_init_revoke+0x137/0x302 size-2048: 2 journal_init_inode+0xf9/0x1c4 Cc: Manfred Spraul <manfred@colorfullife.com> Cc: Alexander Nyberg <alexn@telia.com> Cc: Pekka Enberg <penberg@cs.helsinki.fi> Cc: Christoph Lameter <clameter@engr.sgi.com> Cc: Ravikiran Thirumalai <kiran@scalex86.org> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk> DESC slab-leaks3-locking-fix EDESC From: Andrew Morton <akpm@osdl.org> Update for slab-remove-cachep-spinlock.patch Cc: Al Viro <viro@ftp.linux.org.uk> Cc: Manfred Spraul <manfred@colorfullife.com> Cc: Alexander Nyberg <alexn@telia.com> Cc: Pekka Enberg <penberg@cs.helsinki.fi> Cc: Christoph Lameter <clameter@engr.sgi.com> Cc: Ravikiran Thirumalai <kiran@scalex86.org> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Showing 6 changed files with 222 additions and 11 deletions Side-by-side Diff
fs/proc/proc_misc.c
... | ... | @@ -485,7 +485,41 @@ |
485 | 485 | .llseek = seq_lseek, |
486 | 486 | .release = seq_release, |
487 | 487 | }; |
488 | + | |
489 | +#ifdef CONFIG_DEBUG_SLAB_LEAK | |
490 | +extern struct seq_operations slabstats_op; | |
491 | +static int slabstats_open(struct inode *inode, struct file *file) | |
492 | +{ | |
493 | + unsigned long *n = kzalloc(PAGE_SIZE, GFP_KERNEL); | |
494 | + int ret = -ENOMEM; | |
495 | + if (n) { | |
496 | + ret = seq_open(file, &slabstats_op); | |
497 | + if (!ret) { | |
498 | + struct seq_file *m = file->private_data; | |
499 | + *n = PAGE_SIZE / (2 * sizeof(unsigned long)); | |
500 | + m->private = n; | |
501 | + n = NULL; | |
502 | + } | |
503 | + kfree(n); | |
504 | + } | |
505 | + return ret; | |
506 | +} | |
507 | + | |
508 | +static int slabstats_release(struct inode *inode, struct file *file) | |
509 | +{ | |
510 | + struct seq_file *m = file->private_data; | |
511 | + kfree(m->private); | |
512 | + return seq_release(inode, file); | |
513 | +} | |
514 | + | |
515 | +static struct file_operations proc_slabstats_operations = { | |
516 | + .open = slabstats_open, | |
517 | + .read = seq_read, | |
518 | + .llseek = seq_lseek, | |
519 | + .release = slabstats_release, | |
520 | +}; | |
488 | 521 | #endif |
522 | +#endif | |
489 | 523 | |
490 | 524 | static int show_stat(struct seq_file *p, void *v) |
491 | 525 | { |
... | ... | @@ -744,6 +778,9 @@ |
744 | 778 | create_seq_entry("interrupts", 0, &proc_interrupts_operations); |
745 | 779 | #ifdef CONFIG_SLAB |
746 | 780 | create_seq_entry("slabinfo",S_IWUSR|S_IRUGO,&proc_slabinfo_operations); |
781 | +#ifdef CONFIG_DEBUG_SLAB_LEAK | |
782 | + create_seq_entry("slab_allocators", 0 ,&proc_slabstats_operations); | |
783 | +#endif | |
747 | 784 | #endif |
748 | 785 | create_seq_entry("buddyinfo",S_IRUGO, &fragmentation_file_operations); |
749 | 786 | create_seq_entry("vmstat",S_IRUGO, &proc_vmstat_file_operations); |
include/linux/slab.h
... | ... | @@ -77,11 +77,12 @@ |
77 | 77 | }; |
78 | 78 | extern struct cache_sizes malloc_sizes[]; |
79 | 79 | |
80 | -#ifndef CONFIG_DEBUG_SLAB | |
81 | 80 | extern void *__kmalloc(size_t, gfp_t); |
81 | +#ifndef CONFIG_DEBUG_SLAB | |
82 | +#define ____kmalloc(size, flags) __kmalloc(size, flags) | |
82 | 83 | #else |
83 | 84 | extern void *__kmalloc_track_caller(size_t, gfp_t, void*); |
84 | -#define __kmalloc(size, flags) \ | |
85 | +#define ____kmalloc(size, flags) \ | |
85 | 86 | __kmalloc_track_caller(size, flags, __builtin_return_address(0)) |
86 | 87 | #endif |
87 | 88 | |
... | ... | @@ -173,6 +174,7 @@ |
173 | 174 | #define kmem_ptr_validate(a, b) (0) |
174 | 175 | #define kmem_cache_alloc_node(c, f, n) kmem_cache_alloc(c, f) |
175 | 176 | #define kmalloc_node(s, f, n) kmalloc(s, f) |
177 | +#define ____kmalloc kmalloc | |
176 | 178 | |
177 | 179 | #endif /* CONFIG_SLOB */ |
178 | 180 |
lib/Kconfig.debug
... | ... | @@ -85,6 +85,10 @@ |
85 | 85 | allocation as well as poisoning memory on free to catch use of freed |
86 | 86 | memory. This can make kmalloc/kfree-intensive workloads much slower. |
87 | 87 | |
88 | +config DEBUG_SLAB_LEAK | |
89 | + bool "Memory leak debugging" | |
90 | + depends on DEBUG_SLAB | |
91 | + | |
88 | 92 | config DEBUG_PREEMPT |
89 | 93 | bool "Debug preemptible kernel" |
90 | 94 | depends on DEBUG_KERNEL && PREEMPT |
mm/slab.c
... | ... | @@ -204,7 +204,8 @@ |
204 | 204 | typedef unsigned int kmem_bufctl_t; |
205 | 205 | #define BUFCTL_END (((kmem_bufctl_t)(~0U))-0) |
206 | 206 | #define BUFCTL_FREE (((kmem_bufctl_t)(~0U))-1) |
207 | -#define SLAB_LIMIT (((kmem_bufctl_t)(~0U))-2) | |
207 | +#define BUFCTL_ACTIVE (((kmem_bufctl_t)(~0U))-2) | |
208 | +#define SLAB_LIMIT (((kmem_bufctl_t)(~0U))-3) | |
208 | 209 | |
209 | 210 | /* Max number of objs-per-slab for caches which use off-slab slabs. |
210 | 211 | * Needed to avoid a possible looping condition in cache_grow(). |
... | ... | @@ -2399,7 +2400,7 @@ |
2399 | 2400 | /* Verify that the slab belongs to the intended node */ |
2400 | 2401 | WARN_ON(slabp->nodeid != nodeid); |
2401 | 2402 | |
2402 | - if (slab_bufctl(slabp)[objnr] != BUFCTL_FREE) { | |
2403 | + if (slab_bufctl(slabp)[objnr] + 1 <= SLAB_LIMIT + 1) { | |
2403 | 2404 | printk(KERN_ERR "slab: double free detected in cache " |
2404 | 2405 | "'%s', objp %p\n", cachep->name, objp); |
2405 | 2406 | BUG(); |
... | ... | @@ -2605,6 +2606,9 @@ |
2605 | 2606 | */ |
2606 | 2607 | cachep->dtor(objp + obj_offset(cachep), cachep, 0); |
2607 | 2608 | } |
2609 | +#ifdef CONFIG_DEBUG_SLAB_LEAK | |
2610 | + slab_bufctl(slabp)[objnr] = BUFCTL_FREE; | |
2611 | +#endif | |
2608 | 2612 | if (cachep->flags & SLAB_POISON) { |
2609 | 2613 | #ifdef CONFIG_DEBUG_PAGEALLOC |
2610 | 2614 | if ((cachep->buffer_size % PAGE_SIZE)==0 && OFF_SLAB(cachep)) { |
... | ... | @@ -2788,6 +2792,16 @@ |
2788 | 2792 | *dbg_redzone1(cachep, objp) = RED_ACTIVE; |
2789 | 2793 | *dbg_redzone2(cachep, objp) = RED_ACTIVE; |
2790 | 2794 | } |
2795 | +#ifdef CONFIG_DEBUG_SLAB_LEAK | |
2796 | + { | |
2797 | + struct slab *slabp; | |
2798 | + unsigned objnr; | |
2799 | + | |
2800 | + slabp = page_get_slab(virt_to_page(objp)); | |
2801 | + objnr = (unsigned)(objp - slabp->s_mem) / cachep->buffer_size; | |
2802 | + slab_bufctl(slabp)[objnr] = BUFCTL_ACTIVE; | |
2803 | + } | |
2804 | +#endif | |
2791 | 2805 | objp += obj_offset(cachep); |
2792 | 2806 | if (cachep->ctor && cachep->flags & SLAB_POISON) { |
2793 | 2807 | unsigned long ctor_flags = SLAB_CTOR_CONSTRUCTOR; |
2794 | 2808 | |
2795 | 2809 | |
2796 | 2810 | |
2797 | 2811 | |
... | ... | @@ -3220,22 +3234,23 @@ |
3220 | 3234 | return __cache_alloc(cachep, flags, caller); |
3221 | 3235 | } |
3222 | 3236 | |
3223 | -#ifndef CONFIG_DEBUG_SLAB | |
3224 | 3237 | |
3225 | 3238 | void *__kmalloc(size_t size, gfp_t flags) |
3226 | 3239 | { |
3240 | +#ifndef CONFIG_DEBUG_SLAB | |
3227 | 3241 | return __do_kmalloc(size, flags, NULL); |
3242 | +#else | |
3243 | + return __do_kmalloc(size, flags, __builtin_return_address(0)); | |
3244 | +#endif | |
3228 | 3245 | } |
3229 | 3246 | EXPORT_SYMBOL(__kmalloc); |
3230 | 3247 | |
3231 | -#else | |
3232 | - | |
3248 | +#ifdef CONFIG_DEBUG_SLAB | |
3233 | 3249 | void *__kmalloc_track_caller(size_t size, gfp_t flags, void *caller) |
3234 | 3250 | { |
3235 | 3251 | return __do_kmalloc(size, flags, caller); |
3236 | 3252 | } |
3237 | 3253 | EXPORT_SYMBOL(__kmalloc_track_caller); |
3238 | - | |
3239 | 3254 | #endif |
3240 | 3255 | |
3241 | 3256 | #ifdef CONFIG_SMP |
... | ... | @@ -3899,6 +3914,159 @@ |
3899 | 3914 | res = count; |
3900 | 3915 | return res; |
3901 | 3916 | } |
3917 | + | |
3918 | +#ifdef CONFIG_DEBUG_SLAB_LEAK | |
3919 | + | |
3920 | +static void *leaks_start(struct seq_file *m, loff_t *pos) | |
3921 | +{ | |
3922 | + loff_t n = *pos; | |
3923 | + struct list_head *p; | |
3924 | + | |
3925 | + mutex_lock(&cache_chain_mutex); | |
3926 | + p = cache_chain.next; | |
3927 | + while (n--) { | |
3928 | + p = p->next; | |
3929 | + if (p == &cache_chain) | |
3930 | + return NULL; | |
3931 | + } | |
3932 | + return list_entry(p, struct kmem_cache, next); | |
3933 | +} | |
3934 | + | |
3935 | +static inline int add_caller(unsigned long *n, unsigned long v) | |
3936 | +{ | |
3937 | + unsigned long *p; | |
3938 | + int l; | |
3939 | + if (!v) | |
3940 | + return 1; | |
3941 | + l = n[1]; | |
3942 | + p = n + 2; | |
3943 | + while (l) { | |
3944 | + int i = l/2; | |
3945 | + unsigned long *q = p + 2 * i; | |
3946 | + if (*q == v) { | |
3947 | + q[1]++; | |
3948 | + return 1; | |
3949 | + } | |
3950 | + if (*q > v) { | |
3951 | + l = i; | |
3952 | + } else { | |
3953 | + p = q + 2; | |
3954 | + l -= i + 1; | |
3955 | + } | |
3956 | + } | |
3957 | + if (++n[1] == n[0]) | |
3958 | + return 0; | |
3959 | + memmove(p + 2, p, n[1] * 2 * sizeof(unsigned long) - ((void *)p - (void *)n)); | |
3960 | + p[0] = v; | |
3961 | + p[1] = 1; | |
3962 | + return 1; | |
3963 | +} | |
3964 | + | |
3965 | +static void handle_slab(unsigned long *n, struct kmem_cache *c, struct slab *s) | |
3966 | +{ | |
3967 | + void *p; | |
3968 | + int i; | |
3969 | + if (n[0] == n[1]) | |
3970 | + return; | |
3971 | + for (i = 0, p = s->s_mem; i < c->num; i++, p += c->buffer_size) { | |
3972 | + if (slab_bufctl(s)[i] != BUFCTL_ACTIVE) | |
3973 | + continue; | |
3974 | + if (!add_caller(n, (unsigned long)*dbg_userword(c, p))) | |
3975 | + return; | |
3976 | + } | |
3977 | +} | |
3978 | + | |
3979 | +static void show_symbol(struct seq_file *m, unsigned long address) | |
3980 | +{ | |
3981 | +#ifdef CONFIG_KALLSYMS | |
3982 | + char *modname; | |
3983 | + const char *name; | |
3984 | + unsigned long offset, size; | |
3985 | + char namebuf[KSYM_NAME_LEN+1]; | |
3986 | + | |
3987 | + name = kallsyms_lookup(address, &size, &offset, &modname, namebuf); | |
3988 | + | |
3989 | + if (name) { | |
3990 | + seq_printf(m, "%s+%#lx/%#lx", name, offset, size); | |
3991 | + if (modname) | |
3992 | + seq_printf(m, " [%s]", modname); | |
3993 | + return; | |
3994 | + } | |
3995 | +#endif | |
3996 | + seq_printf(m, "%p", (void *)address); | |
3997 | +} | |
3998 | + | |
3999 | +static int leaks_show(struct seq_file *m, void *p) | |
4000 | +{ | |
4001 | + struct kmem_cache *cachep = p; | |
4002 | + struct list_head *q; | |
4003 | + struct slab *slabp; | |
4004 | + struct kmem_list3 *l3; | |
4005 | + const char *name; | |
4006 | + unsigned long *n = m->private; | |
4007 | + int node; | |
4008 | + int i; | |
4009 | + | |
4010 | + if (!(cachep->flags & SLAB_STORE_USER)) | |
4011 | + return 0; | |
4012 | + if (!(cachep->flags & SLAB_RED_ZONE)) | |
4013 | + return 0; | |
4014 | + | |
4015 | + /* OK, we can do it */ | |
4016 | + | |
4017 | + n[1] = 0; | |
4018 | + | |
4019 | + for_each_online_node(node) { | |
4020 | + l3 = cachep->nodelists[node]; | |
4021 | + if (!l3) | |
4022 | + continue; | |
4023 | + | |
4024 | + check_irq_on(); | |
4025 | + spin_lock_irq(&l3->list_lock); | |
4026 | + | |
4027 | + list_for_each(q, &l3->slabs_full) { | |
4028 | + slabp = list_entry(q, struct slab, list); | |
4029 | + handle_slab(n, cachep, slabp); | |
4030 | + } | |
4031 | + list_for_each(q, &l3->slabs_partial) { | |
4032 | + slabp = list_entry(q, struct slab, list); | |
4033 | + handle_slab(n, cachep, slabp); | |
4034 | + } | |
4035 | + spin_unlock_irq(&l3->list_lock); | |
4036 | + } | |
4037 | + name = cachep->name; | |
4038 | + if (n[0] == n[1]) { | |
4039 | + /* Increase the buffer size */ | |
4040 | + mutex_unlock(&cache_chain_mutex); | |
4041 | + m->private = kzalloc(n[0] * 4 * sizeof(unsigned long), GFP_KERNEL); | |
4042 | + if (!m->private) { | |
4043 | + /* Too bad, we are really out */ | |
4044 | + m->private = n; | |
4045 | + mutex_lock(&cache_chain_mutex); | |
4046 | + return -ENOMEM; | |
4047 | + } | |
4048 | + *(unsigned long *)m->private = n[0] * 2; | |
4049 | + kfree(n); | |
4050 | + mutex_lock(&cache_chain_mutex); | |
4051 | + /* Now make sure this entry will be retried */ | |
4052 | + m->count = m->size; | |
4053 | + return 0; | |
4054 | + } | |
4055 | + for (i = 0; i < n[1]; i++) { | |
4056 | + seq_printf(m, "%s: %lu ", name, n[2*i+3]); | |
4057 | + show_symbol(m, n[2*i+2]); | |
4058 | + seq_putc(m, '\n'); | |
4059 | + } | |
4060 | + return 0; | |
4061 | +} | |
4062 | + | |
4063 | +struct seq_operations slabstats_op = { | |
4064 | + .start = leaks_start, | |
4065 | + .next = s_next, | |
4066 | + .stop = s_stop, | |
4067 | + .show = leaks_show, | |
4068 | +}; | |
4069 | +#endif | |
3902 | 4070 | #endif |
3903 | 4071 | |
3904 | 4072 | /** |
mm/util.c
... | ... | @@ -11,7 +11,7 @@ |
11 | 11 | */ |
12 | 12 | void *kzalloc(size_t size, gfp_t flags) |
13 | 13 | { |
14 | - void *ret = kmalloc(size, flags); | |
14 | + void *ret = ____kmalloc(size, flags); | |
15 | 15 | if (ret) |
16 | 16 | memset(ret, 0, size); |
17 | 17 | return ret; |
... | ... | @@ -33,7 +33,7 @@ |
33 | 33 | return NULL; |
34 | 34 | |
35 | 35 | len = strlen(s) + 1; |
36 | - buf = kmalloc(len, gfp); | |
36 | + buf = ____kmalloc(len, gfp); | |
37 | 37 | if (buf) |
38 | 38 | memcpy(buf, s, len); |
39 | 39 | return buf; |
net/core/skbuff.c
... | ... | @@ -149,7 +149,7 @@ |
149 | 149 | |
150 | 150 | /* Get the DATA. Size must match skb_add_mtu(). */ |
151 | 151 | size = SKB_DATA_ALIGN(size); |
152 | - data = kmalloc(size + sizeof(struct skb_shared_info), gfp_mask); | |
152 | + data = ____kmalloc(size + sizeof(struct skb_shared_info), gfp_mask); | |
153 | 153 | if (!data) |
154 | 154 | goto nodata; |
155 | 155 |