diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2011-07-22 15:44:30 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-07-22 15:44:30 -0400 |
commit | f99b7880cb9863e11441bd8b2f31d4f556ef1a44 (patch) | |
tree | 6f3dc6e33e847b431dd899bd968d799f0d4a8fff /mm/slub.c | |
parent | 02f8c6aee8df3cdc935e9bdd4f2d020306035dbe (diff) | |
parent | 7ea466f2256b02a7047dfd47d76a2f6c1e427e3e (diff) |
Merge branch 'slab-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/penberg/slab-2.6
* 'slab-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/penberg/slab-2.6:
slab: fix DEBUG_SLAB warning
slab: shrink sizeof(struct kmem_cache)
slab: fix DEBUG_SLAB build
SLUB: Fix missing <linux/stacktrace.h> include
slub: reduce overhead of slub_debug
slub: Add method to verify memory is not freed
slub: Enable backtrace for create/delete points
slab allocators: Provide generic description of alignment defines
slab, slub, slob: Unify alignment definition
slob/lockdep: Fix gfp flags passed to lockdep
Diffstat (limited to 'mm/slub.c')
-rw-r--r-- | mm/slub.c | 105 |
1 files changed, 103 insertions, 2 deletions
@@ -27,6 +27,7 @@ | |||
27 | #include <linux/memory.h> | 27 | #include <linux/memory.h> |
28 | #include <linux/math64.h> | 28 | #include <linux/math64.h> |
29 | #include <linux/fault-inject.h> | 29 | #include <linux/fault-inject.h> |
30 | #include <linux/stacktrace.h> | ||
30 | 31 | ||
31 | #include <trace/events/kmem.h> | 32 | #include <trace/events/kmem.h> |
32 | 33 | ||
@@ -191,8 +192,12 @@ static LIST_HEAD(slab_caches); | |||
191 | /* | 192 | /* |
192 | * Tracking user of a slab. | 193 | * Tracking user of a slab. |
193 | */ | 194 | */ |
195 | #define TRACK_ADDRS_COUNT 16 | ||
194 | struct track { | 196 | struct track { |
195 | unsigned long addr; /* Called from address */ | 197 | unsigned long addr; /* Called from address */ |
198 | #ifdef CONFIG_STACKTRACE | ||
199 | unsigned long addrs[TRACK_ADDRS_COUNT]; /* Called from address */ | ||
200 | #endif | ||
196 | int cpu; /* Was running on cpu */ | 201 | int cpu; /* Was running on cpu */ |
197 | int pid; /* Pid context */ | 202 | int pid; /* Pid context */ |
198 | unsigned long when; /* When did the operation occur */ | 203 | unsigned long when; /* When did the operation occur */ |
@@ -420,6 +425,24 @@ static void set_track(struct kmem_cache *s, void *object, | |||
420 | struct track *p = get_track(s, object, alloc); | 425 | struct track *p = get_track(s, object, alloc); |
421 | 426 | ||
422 | if (addr) { | 427 | if (addr) { |
428 | #ifdef CONFIG_STACKTRACE | ||
429 | struct stack_trace trace; | ||
430 | int i; | ||
431 | |||
432 | trace.nr_entries = 0; | ||
433 | trace.max_entries = TRACK_ADDRS_COUNT; | ||
434 | trace.entries = p->addrs; | ||
435 | trace.skip = 3; | ||
436 | save_stack_trace(&trace); | ||
437 | |||
438 | /* See rant in lockdep.c */ | ||
439 | if (trace.nr_entries != 0 && | ||
440 | trace.entries[trace.nr_entries - 1] == ULONG_MAX) | ||
441 | trace.nr_entries--; | ||
442 | |||
443 | for (i = trace.nr_entries; i < TRACK_ADDRS_COUNT; i++) | ||
444 | p->addrs[i] = 0; | ||
445 | #endif | ||
423 | p->addr = addr; | 446 | p->addr = addr; |
424 | p->cpu = smp_processor_id(); | 447 | p->cpu = smp_processor_id(); |
425 | p->pid = current->pid; | 448 | p->pid = current->pid; |
@@ -444,6 +467,16 @@ static void print_track(const char *s, struct track *t) | |||
444 | 467 | ||
445 | printk(KERN_ERR "INFO: %s in %pS age=%lu cpu=%u pid=%d\n", | 468 | printk(KERN_ERR "INFO: %s in %pS age=%lu cpu=%u pid=%d\n", |
446 | s, (void *)t->addr, jiffies - t->when, t->cpu, t->pid); | 469 | s, (void *)t->addr, jiffies - t->when, t->cpu, t->pid); |
470 | #ifdef CONFIG_STACKTRACE | ||
471 | { | ||
472 | int i; | ||
473 | for (i = 0; i < TRACK_ADDRS_COUNT; i++) | ||
474 | if (t->addrs[i]) | ||
475 | printk(KERN_ERR "\t%pS\n", (void *)t->addrs[i]); | ||
476 | else | ||
477 | break; | ||
478 | } | ||
479 | #endif | ||
447 | } | 480 | } |
448 | 481 | ||
449 | static void print_tracking(struct kmem_cache *s, void *object) | 482 | static void print_tracking(struct kmem_cache *s, void *object) |
@@ -557,10 +590,10 @@ static void init_object(struct kmem_cache *s, void *object, u8 val) | |||
557 | memset(p + s->objsize, val, s->inuse - s->objsize); | 590 | memset(p + s->objsize, val, s->inuse - s->objsize); |
558 | } | 591 | } |
559 | 592 | ||
560 | static u8 *check_bytes(u8 *start, unsigned int value, unsigned int bytes) | 593 | static u8 *check_bytes8(u8 *start, u8 value, unsigned int bytes) |
561 | { | 594 | { |
562 | while (bytes) { | 595 | while (bytes) { |
563 | if (*start != (u8)value) | 596 | if (*start != value) |
564 | return start; | 597 | return start; |
565 | start++; | 598 | start++; |
566 | bytes--; | 599 | bytes--; |
@@ -568,6 +601,38 @@ static u8 *check_bytes(u8 *start, unsigned int value, unsigned int bytes) | |||
568 | return NULL; | 601 | return NULL; |
569 | } | 602 | } |
570 | 603 | ||
604 | static u8 *check_bytes(u8 *start, u8 value, unsigned int bytes) | ||
605 | { | ||
606 | u64 value64; | ||
607 | unsigned int words, prefix; | ||
608 | |||
609 | if (bytes <= 16) | ||
610 | return check_bytes8(start, value, bytes); | ||
611 | |||
612 | value64 = value | value << 8 | value << 16 | value << 24; | ||
613 | value64 = value64 | value64 << 32; | ||
614 | prefix = 8 - ((unsigned long)start) % 8; | ||
615 | |||
616 | if (prefix) { | ||
617 | u8 *r = check_bytes8(start, value, prefix); | ||
618 | if (r) | ||
619 | return r; | ||
620 | start += prefix; | ||
621 | bytes -= prefix; | ||
622 | } | ||
623 | |||
624 | words = bytes / 8; | ||
625 | |||
626 | while (words) { | ||
627 | if (*(u64 *)start != value64) | ||
628 | return check_bytes8(start, value, 8); | ||
629 | start += 8; | ||
630 | words--; | ||
631 | } | ||
632 | |||
633 | return check_bytes8(start, value, bytes % 8); | ||
634 | } | ||
635 | |||
571 | static void restore_bytes(struct kmem_cache *s, char *message, u8 data, | 636 | static void restore_bytes(struct kmem_cache *s, char *message, u8 data, |
572 | void *from, void *to) | 637 | void *from, void *to) |
573 | { | 638 | { |
@@ -2928,6 +2993,42 @@ size_t ksize(const void *object) | |||
2928 | } | 2993 | } |
2929 | EXPORT_SYMBOL(ksize); | 2994 | EXPORT_SYMBOL(ksize); |
2930 | 2995 | ||
2996 | #ifdef CONFIG_SLUB_DEBUG | ||
2997 | bool verify_mem_not_deleted(const void *x) | ||
2998 | { | ||
2999 | struct page *page; | ||
3000 | void *object = (void *)x; | ||
3001 | unsigned long flags; | ||
3002 | bool rv; | ||
3003 | |||
3004 | if (unlikely(ZERO_OR_NULL_PTR(x))) | ||
3005 | return false; | ||
3006 | |||
3007 | local_irq_save(flags); | ||
3008 | |||
3009 | page = virt_to_head_page(x); | ||
3010 | if (unlikely(!PageSlab(page))) { | ||
3011 | /* maybe it was from stack? */ | ||
3012 | rv = true; | ||
3013 | goto out_unlock; | ||
3014 | } | ||
3015 | |||
3016 | slab_lock(page); | ||
3017 | if (on_freelist(page->slab, page, object)) { | ||
3018 | object_err(page->slab, page, object, "Object is on free-list"); | ||
3019 | rv = false; | ||
3020 | } else { | ||
3021 | rv = true; | ||
3022 | } | ||
3023 | slab_unlock(page); | ||
3024 | |||
3025 | out_unlock: | ||
3026 | local_irq_restore(flags); | ||
3027 | return rv; | ||
3028 | } | ||
3029 | EXPORT_SYMBOL(verify_mem_not_deleted); | ||
3030 | #endif | ||
3031 | |||
2931 | void kfree(const void *x) | 3032 | void kfree(const void *x) |
2932 | { | 3033 | { |
2933 | struct page *page; | 3034 | struct page *page; |