diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2011-07-22 16:43:49 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-07-22 16:43:49 -0700 |
commit | 391d6276db9fbdedfbc30e1b56390414f0e55988 (patch) | |
tree | d22cd2482f5452d616ee981e954074ffc6ae094d | |
parent | 75b56ec294b074d70f8a676ab02611a3fea76cab (diff) | |
parent | dd4e5d3ac4a76b868daf30e35bd572def96c30ed (diff) |
Merge branch 'core-printk-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip
* 'core-printk-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip:
lockdep: Fix trace_[soft,hard]irqs_[on,off]() recursion
printk: Fix console_sem vs logbuf_lock unlock race
printk: Release console_sem after logbuf_lock
-rw-r--r-- | kernel/lockdep.c | 30 | ||||
-rw-r--r-- | kernel/printk.c | 24 |
2 files changed, 41 insertions, 13 deletions
diff --git a/kernel/lockdep.c b/kernel/lockdep.c index 628276d05915..3956f5149e25 100644 --- a/kernel/lockdep.c +++ b/kernel/lockdep.c @@ -2481,15 +2481,10 @@ mark_held_locks(struct task_struct *curr, enum mark_type mark) /* * Hardirqs will be enabled: */ -void trace_hardirqs_on_caller(unsigned long ip) +static void __trace_hardirqs_on_caller(unsigned long ip) { struct task_struct *curr = current; - time_hardirqs_on(CALLER_ADDR0, ip); - - if (unlikely(!debug_locks || current->lockdep_recursion)) - return; - if (DEBUG_LOCKS_WARN_ON(unlikely(early_boot_irqs_disabled))) return; @@ -2505,8 +2500,6 @@ void trace_hardirqs_on_caller(unsigned long ip) /* we'll do an OFF -> ON transition: */ curr->hardirqs_enabled = 1; - if (DEBUG_LOCKS_WARN_ON(!irqs_disabled())) - return; if (DEBUG_LOCKS_WARN_ON(current->hardirq_context)) return; /* @@ -2528,6 +2521,21 @@ void trace_hardirqs_on_caller(unsigned long ip) curr->hardirq_enable_event = ++curr->irq_events; debug_atomic_inc(hardirqs_on_events); } + +void trace_hardirqs_on_caller(unsigned long ip) +{ + time_hardirqs_on(CALLER_ADDR0, ip); + + if (unlikely(!debug_locks || current->lockdep_recursion)) + return; + + if (DEBUG_LOCKS_WARN_ON(!irqs_disabled())) + return; + + current->lockdep_recursion = 1; + __trace_hardirqs_on_caller(ip); + current->lockdep_recursion = 0; +} EXPORT_SYMBOL(trace_hardirqs_on_caller); void trace_hardirqs_on(void) @@ -2577,7 +2585,7 @@ void trace_softirqs_on(unsigned long ip) { struct task_struct *curr = current; - if (unlikely(!debug_locks)) + if (unlikely(!debug_locks || current->lockdep_recursion)) return; if (DEBUG_LOCKS_WARN_ON(!irqs_disabled())) @@ -2588,6 +2596,7 @@ void trace_softirqs_on(unsigned long ip) return; } + current->lockdep_recursion = 1; /* * We'll do an OFF -> ON transition: */ @@ -2602,6 +2611,7 @@ void trace_softirqs_on(unsigned long ip) */ if (curr->hardirqs_enabled) mark_held_locks(curr, SOFTIRQ); + current->lockdep_recursion = 0; } /* @@ -2611,7 +2621,7 @@ void trace_softirqs_off(unsigned long ip) { struct task_struct *curr = current; - if (unlikely(!debug_locks)) + if (unlikely(!debug_locks || current->lockdep_recursion)) return; if (DEBUG_LOCKS_WARN_ON(!irqs_disabled())) diff --git a/kernel/printk.c b/kernel/printk.c index 35185392173f..37dff3429adb 100644 --- a/kernel/printk.c +++ b/kernel/printk.c @@ -782,7 +782,7 @@ static inline int can_use_console(unsigned int cpu) static int console_trylock_for_printk(unsigned int cpu) __releases(&logbuf_lock) { - int retval = 0; + int retval = 0, wake = 0; if (console_trylock()) { retval = 1; @@ -795,12 +795,14 @@ static int console_trylock_for_printk(unsigned int cpu) */ if (!can_use_console(cpu)) { console_locked = 0; - up(&console_sem); + wake = 1; retval = 0; } } printk_cpu = UINT_MAX; spin_unlock(&logbuf_lock); + if (wake) + up(&console_sem); return retval; } static const char recursion_bug_msg [] = @@ -1242,7 +1244,7 @@ void console_unlock(void) { unsigned long flags; unsigned _con_start, _log_end; - unsigned wake_klogd = 0; + unsigned wake_klogd = 0, retry = 0; if (console_suspended) { up(&console_sem); @@ -1251,6 +1253,7 @@ void console_unlock(void) console_may_schedule = 0; +again: for ( ; ; ) { spin_lock_irqsave(&logbuf_lock, flags); wake_klogd |= log_start - log_end; @@ -1271,8 +1274,23 @@ void console_unlock(void) if (unlikely(exclusive_console)) exclusive_console = NULL; + spin_unlock(&logbuf_lock); + up(&console_sem); + + /* + * Someone could have filled up the buffer again, so re-check if there's + * something to flush. In case we cannot trylock the console_sem again, + * there's a new owner and the console_unlock() from them will do the + * flush, no worries. + */ + spin_lock(&logbuf_lock); + if (con_start != log_end) + retry = 1; spin_unlock_irqrestore(&logbuf_lock, flags); + if (retry && console_trylock()) + goto again; + if (wake_klogd) wake_up_klogd(); } |