diff options
author | Ingo Molnar <mingo@elte.hu> | 2009-07-01 03:43:53 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-07-01 13:26:23 -0400 |
commit | 57d81f6f393b245894ca0cd828f80ce7e3294f39 (patch) | |
tree | e825b033d86bf42e768c645cb66201a94cd7a354 /mm | |
parent | e83c2b0ff325f52dda1aff3572d0e1516216c54b (diff) |
kmemleak: Fix scheduling-while-atomic bug
One of the kmemleak changes caused the following
scheduling-while-holding-the-tasklist-lock regression on x86:
BUG: sleeping function called from invalid context at mm/kmemleak.c:795
in_atomic(): 1, irqs_disabled(): 0, pid: 1737, name: kmemleak
2 locks held by kmemleak/1737:
#0: (scan_mutex){......}, at: [<c10c4376>] kmemleak_scan_thread+0x45/0x86
#1: (tasklist_lock){......}, at: [<c10c3bb4>] kmemleak_scan+0x1a9/0x39c
Pid: 1737, comm: kmemleak Not tainted 2.6.31-rc1-tip #59266
Call Trace:
[<c105ac0f>] ? __debug_show_held_locks+0x1e/0x20
[<c102e490>] __might_sleep+0x10a/0x111
[<c10c38d5>] scan_yield+0x17/0x3b
[<c10c3970>] scan_block+0x39/0xd4
[<c10c3bc6>] kmemleak_scan+0x1bb/0x39c
[<c10c4331>] ? kmemleak_scan_thread+0x0/0x86
[<c10c437b>] kmemleak_scan_thread+0x4a/0x86
[<c104d73e>] kthread+0x6e/0x73
[<c104d6d0>] ? kthread+0x0/0x73
[<c100959f>] kernel_thread_helper+0x7/0x10
kmemleak: 834 new suspected memory leaks (see /sys/kernel/debug/kmemleak)
The bit causing it is highly dubious:
static void scan_yield(void)
{
might_sleep();
if (time_is_before_eq_jiffies(next_scan_yield)) {
schedule();
next_scan_yield = jiffies + jiffies_scan_yield;
}
}
It called deep inside the codepath and in a conditional way,
and that is what crapped up when one of the new scan_block()
uses grew a tasklist_lock dependency.
This minimal patch removes that yielding stuff and adds the
proper cond_resched().
The background scanning thread could probably also be reniced
to +10.
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Acked-by: Pekka Enberg <penberg@cs.helsinki.fi>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'mm')
-rw-r--r-- | mm/kmemleak.c | 31 |
1 files changed, 1 insertions, 30 deletions
diff --git a/mm/kmemleak.c b/mm/kmemleak.c index eeece2deace2..e766e1da09d2 100644 --- a/mm/kmemleak.c +++ b/mm/kmemleak.c | |||
@@ -105,7 +105,6 @@ | |||
105 | #define MAX_TRACE 16 /* stack trace length */ | 105 | #define MAX_TRACE 16 /* stack trace length */ |
106 | #define REPORTS_NR 50 /* maximum number of reported leaks */ | 106 | #define REPORTS_NR 50 /* maximum number of reported leaks */ |
107 | #define MSECS_MIN_AGE 5000 /* minimum object age for reporting */ | 107 | #define MSECS_MIN_AGE 5000 /* minimum object age for reporting */ |
108 | #define MSECS_SCAN_YIELD 10 /* CPU yielding period */ | ||
109 | #define SECS_FIRST_SCAN 60 /* delay before the first scan */ | 108 | #define SECS_FIRST_SCAN 60 /* delay before the first scan */ |
110 | #define SECS_SCAN_WAIT 600 /* subsequent auto scanning delay */ | 109 | #define SECS_SCAN_WAIT 600 /* subsequent auto scanning delay */ |
111 | 110 | ||
@@ -186,10 +185,7 @@ static atomic_t kmemleak_error = ATOMIC_INIT(0); | |||
186 | static unsigned long min_addr = ULONG_MAX; | 185 | static unsigned long min_addr = ULONG_MAX; |
187 | static unsigned long max_addr; | 186 | static unsigned long max_addr; |
188 | 187 | ||
189 | /* used for yielding the CPU to other tasks during scanning */ | ||
190 | static unsigned long next_scan_yield; | ||
191 | static struct task_struct *scan_thread; | 188 | static struct task_struct *scan_thread; |
192 | static unsigned long jiffies_scan_yield; | ||
193 | /* used to avoid reporting of recently allocated objects */ | 189 | /* used to avoid reporting of recently allocated objects */ |
194 | static unsigned long jiffies_min_age; | 190 | static unsigned long jiffies_min_age; |
195 | static unsigned long jiffies_last_scan; | 191 | static unsigned long jiffies_last_scan; |
@@ -786,21 +782,6 @@ void kmemleak_no_scan(const void *ptr) | |||
786 | EXPORT_SYMBOL(kmemleak_no_scan); | 782 | EXPORT_SYMBOL(kmemleak_no_scan); |
787 | 783 | ||
788 | /* | 784 | /* |
789 | * Yield the CPU so that other tasks get a chance to run. The yielding is | ||
790 | * rate-limited to avoid excessive number of calls to the schedule() function | ||
791 | * during memory scanning. | ||
792 | */ | ||
793 | static void scan_yield(void) | ||
794 | { | ||
795 | might_sleep(); | ||
796 | |||
797 | if (time_is_before_eq_jiffies(next_scan_yield)) { | ||
798 | schedule(); | ||
799 | next_scan_yield = jiffies + jiffies_scan_yield; | ||
800 | } | ||
801 | } | ||
802 | |||
803 | /* | ||
804 | * Memory scanning is a long process and it needs to be interruptable. This | 785 | * Memory scanning is a long process and it needs to be interruptable. This |
805 | * function checks whether such interrupt condition occured. | 786 | * function checks whether such interrupt condition occured. |
806 | */ | 787 | */ |
@@ -840,15 +821,6 @@ static void scan_block(void *_start, void *_end, | |||
840 | if (scan_should_stop()) | 821 | if (scan_should_stop()) |
841 | break; | 822 | break; |
842 | 823 | ||
843 | /* | ||
844 | * When scanning a memory block with a corresponding | ||
845 | * kmemleak_object, the CPU yielding is handled in the calling | ||
846 | * code since it holds the object->lock to avoid the block | ||
847 | * freeing. | ||
848 | */ | ||
849 | if (!scanned) | ||
850 | scan_yield(); | ||
851 | |||
852 | object = find_and_get_object(pointer, 1); | 824 | object = find_and_get_object(pointer, 1); |
853 | if (!object) | 825 | if (!object) |
854 | continue; | 826 | continue; |
@@ -1014,7 +986,7 @@ static void kmemleak_scan(void) | |||
1014 | */ | 986 | */ |
1015 | object = list_entry(gray_list.next, typeof(*object), gray_list); | 987 | object = list_entry(gray_list.next, typeof(*object), gray_list); |
1016 | while (&object->gray_list != &gray_list) { | 988 | while (&object->gray_list != &gray_list) { |
1017 | scan_yield(); | 989 | cond_resched(); |
1018 | 990 | ||
1019 | /* may add new objects to the list */ | 991 | /* may add new objects to the list */ |
1020 | if (!scan_should_stop()) | 992 | if (!scan_should_stop()) |
@@ -1385,7 +1357,6 @@ void __init kmemleak_init(void) | |||
1385 | int i; | 1357 | int i; |
1386 | unsigned long flags; | 1358 | unsigned long flags; |
1387 | 1359 | ||
1388 | jiffies_scan_yield = msecs_to_jiffies(MSECS_SCAN_YIELD); | ||
1389 | jiffies_min_age = msecs_to_jiffies(MSECS_MIN_AGE); | 1360 | jiffies_min_age = msecs_to_jiffies(MSECS_MIN_AGE); |
1390 | jiffies_scan_wait = msecs_to_jiffies(SECS_SCAN_WAIT * 1000); | 1361 | jiffies_scan_wait = msecs_to_jiffies(SECS_SCAN_WAIT * 1000); |
1391 | 1362 | ||