Skip to content

Commit

Permalink
printk: Avoid false positive lockdep report for legacy printing
Browse files Browse the repository at this point in the history
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 <[email protected]>
Reviewed-by: Petr Mladek <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
Signed-off-by: Petr Mladek <[email protected]>
  • Loading branch information
jogness authored and pmladek committed Sep 4, 2024
1 parent 1529bbb commit daeed15
Showing 1 changed file with 63 additions and 20 deletions.
83 changes: 63 additions & 20 deletions kernel/printk/printk.c
Original file line number Diff line number Diff line change
Expand Up @@ -2981,6 +2981,34 @@ bool printk_get_next_message(struct printk_message *pmsg, u64 seq,
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.
Expand Down Expand Up @@ -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();
/* Write everything out to the hardware. */

/* Do not trace print latency. */
stop_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.
*/

/* Write everything out to the hardware. */
con->write(con, outbuf, pmsg.outbuf_len);
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();

start_critical_timings();
/* Do not trace print latency. */
stop_critical_timings();

con->seq = pmsg.seq + 1;
printk_legacy_allow_spinlock_enter();
con->write(con, outbuf, pmsg.outbuf_len);
printk_legacy_allow_spinlock_exit();

*handover = console_lock_spinning_disable_and_check(cookie);
printk_safe_exit_irqrestore(flags);
start_critical_timings();

con->seq = pmsg.seq + 1;

*handover = console_lock_spinning_disable_and_check(cookie);
printk_safe_exit_irqrestore(flags);
}
skip:
return true;
}
Expand Down

0 comments on commit daeed15

Please sign in to comment.