Skip to content

Commit 719f6a7

Browse files
committed
printk: Use the main logbuf in NMI when logbuf_lock is available
The commit 42a0bb3 ("printk/nmi: generic solution for safe printk in NMI") caused that printk stores messages into a temporary buffer in NMI context. The buffer is per-CPU and therefore the size is rather limited. It works quite well for NMI backtraces. But there are longer logs that might get printed in NMI context, for example, lockdep warnings, ftrace_dump_on_oops. The temporary buffer is used to avoid deadlocks caused by logbuf_lock. Also it is needed to avoid races with the other temporary buffer that is used when PRINTK_SAFE_CONTEXT is entered. But the main buffer can be used in NMI if the lock is available and we did not interrupt PRINTK_SAFE_CONTEXT. The lock is checked using raw_spin_is_locked(). It might cause false negatives when the lock is taken on another CPU and this CPU is in the safe context from other reasons. Note that the safe context is used also to get console semaphore or when calling console drivers. For this reason, we do the check in printk_nmi_enter(). It makes the handling consistent for the entire NMI handler and avoids reshuffling of the messages. The patch also defines special printk context that allows to use printk_deferred() in NMI. Note that we could not flush the messages to the consoles because console drivers might use many other internal locks. The newly created vprintk_deferred() disables the preemption only around the irq work handling. It is needed there to keep the consistency between the two per-CPU variables. But there is no reason to disable preemption around vprintk_emit(). Finally, the patch puts back explicit serialization of the NMI backtraces from different CPUs. It was removed by the commit a9edc88 ("x86/nmi: Perform a safe NMI stack trace on all CPUs"). It was not needed because the flushing of the temporary per-CPU buffers was serialized. Link: http://lkml.kernel.org/r/[email protected] Cc: Steven Rostedt <[email protected]> Cc: Andrew Morton <[email protected]> Cc: Peter Zijlstra <[email protected]> Cc: Russell King <[email protected]> Cc: Daniel Thompson <[email protected]> Cc: Ingo Molnar <[email protected]> Cc: Thomas Gleixner <[email protected]> Cc: Chris Metcalf <[email protected]> Cc: [email protected] Cc: [email protected] Cc: [email protected] Suggested-by: Sergey Senozhatsky <[email protected]> Acked-by: Sergey Senozhatsky <[email protected]> Signed-off-by: Petr Mladek <[email protected]>
1 parent 9c35baf commit 719f6a7

File tree

4 files changed

+45
-9
lines changed

4 files changed

+45
-9
lines changed

kernel/printk/internal.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,14 @@
1818

1919
#ifdef CONFIG_PRINTK
2020

21-
#define PRINTK_SAFE_CONTEXT_MASK 0x7fffffff
22-
#define PRINTK_NMI_CONTEXT_MASK 0x80000000
21+
#define PRINTK_SAFE_CONTEXT_MASK 0x3fffffff
22+
#define PRINTK_NMI_DEFERRED_CONTEXT_MASK 0x40000000
23+
#define PRINTK_NMI_CONTEXT_MASK 0x80000000
2324

2425
extern raw_spinlock_t logbuf_lock;
2526

2627
__printf(1, 0) int vprintk_default(const char *fmt, va_list args);
28+
__printf(1, 0) int vprintk_deferred(const char *fmt, va_list args);
2729
__printf(1, 0) int vprintk_func(const char *fmt, va_list args);
2830
void __printk_safe_enter(void);
2931
void __printk_safe_exit(void);

kernel/printk/printk.c

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2722,23 +2722,32 @@ void wake_up_klogd(void)
27222722
preempt_enable();
27232723
}
27242724

2725-
int printk_deferred(const char *fmt, ...)
2725+
int vprintk_deferred(const char *fmt, va_list args)
27262726
{
2727-
va_list args;
27282727
int r;
27292728

2730-
preempt_disable();
2731-
va_start(args, fmt);
27322729
r = vprintk_emit(0, LOGLEVEL_SCHED, NULL, 0, fmt, args);
2733-
va_end(args);
27342730

2731+
preempt_disable();
27352732
__this_cpu_or(printk_pending, PRINTK_PENDING_OUTPUT);
27362733
irq_work_queue(this_cpu_ptr(&wake_up_klogd_work));
27372734
preempt_enable();
27382735

27392736
return r;
27402737
}
27412738

2739+
int printk_deferred(const char *fmt, ...)
2740+
{
2741+
va_list args;
2742+
int r;
2743+
2744+
va_start(args, fmt);
2745+
r = vprintk_deferred(fmt, args);
2746+
va_end(args);
2747+
2748+
return r;
2749+
}
2750+
27422751
/*
27432752
* printk rate limiting, lifted from the networking subsystem.
27442753
*

kernel/printk/printk_safe.c

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -308,12 +308,24 @@ static int vprintk_nmi(const char *fmt, va_list args)
308308

309309
void printk_nmi_enter(void)
310310
{
311-
this_cpu_or(printk_context, PRINTK_NMI_CONTEXT_MASK);
311+
/*
312+
* The size of the extra per-CPU buffer is limited. Use it only when
313+
* the main one is locked. If this CPU is not in the safe context,
314+
* the lock must be taken on another CPU and we could wait for it.
315+
*/
316+
if ((this_cpu_read(printk_context) & PRINTK_SAFE_CONTEXT_MASK) &&
317+
raw_spin_is_locked(&logbuf_lock)) {
318+
this_cpu_or(printk_context, PRINTK_NMI_CONTEXT_MASK);
319+
} else {
320+
this_cpu_or(printk_context, PRINTK_NMI_DEFERRED_CONTEXT_MASK);
321+
}
312322
}
313323

314324
void printk_nmi_exit(void)
315325
{
316-
this_cpu_and(printk_context, ~PRINTK_NMI_CONTEXT_MASK);
326+
this_cpu_and(printk_context,
327+
~(PRINTK_NMI_CONTEXT_MASK |
328+
PRINTK_NMI_DEFERRED_CONTEXT_MASK));
317329
}
318330

319331
#else
@@ -351,12 +363,22 @@ void __printk_safe_exit(void)
351363

352364
__printf(1, 0) int vprintk_func(const char *fmt, va_list args)
353365
{
366+
/* Use extra buffer in NMI when logbuf_lock is taken or in safe mode. */
354367
if (this_cpu_read(printk_context) & PRINTK_NMI_CONTEXT_MASK)
355368
return vprintk_nmi(fmt, args);
356369

370+
/* Use extra buffer to prevent a recursion deadlock in safe mode. */
357371
if (this_cpu_read(printk_context) & PRINTK_SAFE_CONTEXT_MASK)
358372
return vprintk_safe(fmt, args);
359373

374+
/*
375+
* Use the main logbuf when logbuf_lock is available in NMI.
376+
* But avoid calling console drivers that might have their own locks.
377+
*/
378+
if (this_cpu_read(printk_context) & PRINTK_NMI_DEFERRED_CONTEXT_MASK)
379+
return vprintk_deferred(fmt, args);
380+
381+
/* No obstacles. */
360382
return vprintk_default(fmt, args);
361383
}
362384

lib/nmi_backtrace.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,9 +86,11 @@ void nmi_trigger_cpumask_backtrace(const cpumask_t *mask,
8686

8787
bool nmi_cpu_backtrace(struct pt_regs *regs)
8888
{
89+
static arch_spinlock_t lock = __ARCH_SPIN_LOCK_UNLOCKED;
8990
int cpu = smp_processor_id();
9091

9192
if (cpumask_test_cpu(cpu, to_cpumask(backtrace_mask))) {
93+
arch_spin_lock(&lock);
9294
if (regs && cpu_in_idle(instruction_pointer(regs))) {
9395
pr_warn("NMI backtrace for cpu %d skipped: idling at pc %#lx\n",
9496
cpu, instruction_pointer(regs));
@@ -99,6 +101,7 @@ bool nmi_cpu_backtrace(struct pt_regs *regs)
99101
else
100102
dump_stack();
101103
}
104+
arch_spin_unlock(&lock);
102105
cpumask_clear_cpu(cpu, to_cpumask(backtrace_mask));
103106
return true;
104107
}

0 commit comments

Comments
 (0)