diff options
author | Steven Rostedt <srostedt@redhat.com> | 2009-01-22 19:01:40 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2009-01-23 05:10:57 -0500 |
commit | 7e49fcce1bdadd723ae6a0b3b324c4daced61563 (patch) | |
tree | a2bf1a143ed33ca01612dfab1fb7c993c467cdb0 /kernel/sched.c | |
parent | b06a830183b610c0a88c29a92feb7991a867ab46 (diff) |
trace, lockdep: manual preempt count adding for local_bh_disable
Impact: fix to preempt trace triggering lockdep check_flag failure
In local_bh_disable, the use of add_preempt_count causes the
preempt tracer to start recording the time preemption is off.
But because it already modified the preempt_count to show
softirqs disabled, and before it called the lockdep code to
handle this, it causes a state that lockdep can not handle.
The preempt tracer will reset the ring buffer on start of a trace,
and the ring buffer reset code does a spin_lock_irqsave. This
calls into lockdep and lockdep will fail when it detects the
invalid state of having softirqs disabled but the internal
current->softirqs_enabled is still set.
The fix is to manually add the SOFTIRQ_OFFSET to preempt count
and call the preempt tracer code outside the lockdep critical
area.
Thanks to Peter Zijlstra for suggesting this solution.
Signed-off-by: Steven Rostedt <srostedt@redhat.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'kernel/sched.c')
-rw-r--r-- | kernel/sched.c | 8 |
1 files changed, 4 insertions, 4 deletions
diff --git a/kernel/sched.c b/kernel/sched.c index 52bbf1c842a..c154825ae75 100644 --- a/kernel/sched.c +++ b/kernel/sched.c | |||
@@ -4399,10 +4399,7 @@ void scheduler_tick(void) | |||
4399 | #endif | 4399 | #endif |
4400 | } | 4400 | } |
4401 | 4401 | ||
4402 | #if defined(CONFIG_PREEMPT) && (defined(CONFIG_DEBUG_PREEMPT) || \ | 4402 | unsigned long get_parent_ip(unsigned long addr) |
4403 | defined(CONFIG_PREEMPT_TRACER)) | ||
4404 | |||
4405 | static inline unsigned long get_parent_ip(unsigned long addr) | ||
4406 | { | 4403 | { |
4407 | if (in_lock_functions(addr)) { | 4404 | if (in_lock_functions(addr)) { |
4408 | addr = CALLER_ADDR2; | 4405 | addr = CALLER_ADDR2; |
@@ -4412,6 +4409,9 @@ static inline unsigned long get_parent_ip(unsigned long addr) | |||
4412 | return addr; | 4409 | return addr; |
4413 | } | 4410 | } |
4414 | 4411 | ||
4412 | #if defined(CONFIG_PREEMPT) && (defined(CONFIG_DEBUG_PREEMPT) || \ | ||
4413 | defined(CONFIG_PREEMPT_TRACER)) | ||
4414 | |||
4415 | void __kprobes add_preempt_count(int val) | 4415 | void __kprobes add_preempt_count(int val) |
4416 | { | 4416 | { |
4417 | #ifdef CONFIG_DEBUG_PREEMPT | 4417 | #ifdef CONFIG_DEBUG_PREEMPT |