mirror of
https://github.com/torvalds/linux.git
synced 2024-11-10 06:01:57 +00:00
printk: Avoid false positive lockdep report for legacy printing
Legacy console printing from printk() caller context may invoke the console driver from atomic context. This leads to a lockdep splat because the console driver will acquire a sleeping lock and the caller may already hold a spinning lock. This is noticed by lockdep on !PREEMPT_RT configurations because it will lead to a problem on PREEMPT_RT. However, on PREEMPT_RT the printing path from atomic context is always avoided and the console driver is always invoked from a dedicated thread. Thus the lockdep splat on !PREEMPT_RT is a false positive. For !PREEMPT_RT override the lock-context before invoking the console driver to avoid the false positive. Do not override the lock-context for PREEMPT_RT in order to allow lockdep to catch any real locking context issues related to the write callback usage. Signed-off-by: John Ogness <john.ogness@linutronix.de> Reviewed-by: Petr Mladek <pmladek@suse.com> Link: https://lore.kernel.org/r/20240904120536.115780-18-john.ogness@linutronix.de Signed-off-by: Petr Mladek <pmladek@suse.com>
This commit is contained in:
parent
1529bbb6e2
commit
daeed1595b
@ -2981,6 +2981,34 @@ out:
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Legacy console printing from printk() caller context does not respect
|
||||
* raw_spinlock/spinlock nesting. For !PREEMPT_RT the lockdep warning is a
|
||||
* false positive. For PREEMPT_RT the false positive condition does not
|
||||
* occur.
|
||||
*
|
||||
* This map is used to temporarily establish LD_WAIT_SLEEP context for the
|
||||
* console write() callback when legacy printing to avoid false positive
|
||||
* lockdep complaints, thus allowing lockdep to continue to function for
|
||||
* real issues.
|
||||
*/
|
||||
#ifdef CONFIG_PREEMPT_RT
|
||||
static inline void printk_legacy_allow_spinlock_enter(void) { }
|
||||
static inline void printk_legacy_allow_spinlock_exit(void) { }
|
||||
#else
|
||||
static DEFINE_WAIT_OVERRIDE_MAP(printk_legacy_map, LD_WAIT_SLEEP);
|
||||
|
||||
static inline void printk_legacy_allow_spinlock_enter(void)
|
||||
{
|
||||
lock_map_acquire_try(&printk_legacy_map);
|
||||
}
|
||||
|
||||
static inline void printk_legacy_allow_spinlock_exit(void)
|
||||
{
|
||||
lock_map_release(&printk_legacy_map);
|
||||
}
|
||||
#endif /* CONFIG_PREEMPT_RT */
|
||||
|
||||
/*
|
||||
* Used as the printk buffers for non-panic, serialized console printing.
|
||||
* This is for legacy (!CON_NBCON) as well as all boot (CON_BOOT) consoles.
|
||||
@ -3030,31 +3058,46 @@ static bool console_emit_next_record(struct console *con, bool *handover, int co
|
||||
con->dropped = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* While actively printing out messages, if another printk()
|
||||
* were to occur on another CPU, it may wait for this one to
|
||||
* finish. This task can not be preempted if there is a
|
||||
* waiter waiting to take over.
|
||||
*
|
||||
* Interrupts are disabled because the hand over to a waiter
|
||||
* must not be interrupted until the hand over is completed
|
||||
* (@console_waiter is cleared).
|
||||
*/
|
||||
printk_safe_enter_irqsave(flags);
|
||||
console_lock_spinning_enable();
|
||||
|
||||
/* Do not trace print latency. */
|
||||
stop_critical_timings();
|
||||
|
||||
/* Write everything out to the hardware. */
|
||||
con->write(con, outbuf, pmsg.outbuf_len);
|
||||
|
||||
start_critical_timings();
|
||||
if (force_legacy_kthread() && !panic_in_progress()) {
|
||||
/*
|
||||
* With forced threading this function is in a task context
|
||||
* (either legacy kthread or get_init_console_seq()). There
|
||||
* is no need for concern about printk reentrance, handovers,
|
||||
* or lockdep complaints.
|
||||
*/
|
||||
|
||||
con->seq = pmsg.seq + 1;
|
||||
con->write(con, outbuf, pmsg.outbuf_len);
|
||||
con->seq = pmsg.seq + 1;
|
||||
} else {
|
||||
/*
|
||||
* While actively printing out messages, if another printk()
|
||||
* were to occur on another CPU, it may wait for this one to
|
||||
* finish. This task can not be preempted if there is a
|
||||
* waiter waiting to take over.
|
||||
*
|
||||
* Interrupts are disabled because the hand over to a waiter
|
||||
* must not be interrupted until the hand over is completed
|
||||
* (@console_waiter is cleared).
|
||||
*/
|
||||
printk_safe_enter_irqsave(flags);
|
||||
console_lock_spinning_enable();
|
||||
|
||||
*handover = console_lock_spinning_disable_and_check(cookie);
|
||||
printk_safe_exit_irqrestore(flags);
|
||||
/* Do not trace print latency. */
|
||||
stop_critical_timings();
|
||||
|
||||
printk_legacy_allow_spinlock_enter();
|
||||
con->write(con, outbuf, pmsg.outbuf_len);
|
||||
printk_legacy_allow_spinlock_exit();
|
||||
|
||||
start_critical_timings();
|
||||
|
||||
con->seq = pmsg.seq + 1;
|
||||
|
||||
*handover = console_lock_spinning_disable_and_check(cookie);
|
||||
printk_safe_exit_irqrestore(flags);
|
||||
}
|
||||
skip:
|
||||
return true;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user