aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPetr Mladek <pmladek@suse.com>2017-04-20 04:52:31 -0400
committerPetr Mladek <pmladek@suse.com>2017-05-19 08:42:19 -0400
commit719f6a7040f1bdaf96fcc709d272548facb88e90 (patch)
tree050a91d990c5e870a5b19a9d77d0965e599544ef
parent9c35baf6cee9a5745d55de6f9995916dde642517 (diff)
printk: Use the main logbuf in NMI when logbuf_lock is available
The commit 42a0bb3f71383b457a7d ("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 a9edc88093287183ac934b ("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/1493912763-24873-1-git-send-email-pmladek@suse.com Cc: Steven Rostedt <rostedt@goodmis.org> Cc: Andrew Morton <akpm@linux-foundation.org> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Russell King <rack+kernel@arm.linux.org.uk> Cc: Daniel Thompson <daniel.thompson@linaro.org> Cc: Ingo Molnar <mingo@redhat.com> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Chris Metcalf <cmetcalf@ezchip.com> Cc: x86@kernel.org Cc: linux-arm-kernel@lists.infradead.org Cc: linux-kernel@vger.kernel.org Suggested-by: Sergey Senozhatsky <sergey.senozhatsky@gmail.com> Acked-by: Sergey Senozhatsky <sergey.senozhatsky@gmail.com> Signed-off-by: Petr Mladek <pmladek@suse.com>
-rw-r--r--kernel/printk/internal.h6
-rw-r--r--kernel/printk/printk.c19
-rw-r--r--kernel/printk/printk_safe.c26
-rw-r--r--lib/nmi_backtrace.c3
4 files changed, 45 insertions, 9 deletions
diff --git a/kernel/printk/internal.h b/kernel/printk/internal.h
index 1db044f808b7..2a7d04049af4 100644
--- a/kernel/printk/internal.h
+++ b/kernel/printk/internal.h
@@ -18,12 +18,14 @@
18 18
19#ifdef CONFIG_PRINTK 19#ifdef CONFIG_PRINTK
20 20
21#define PRINTK_SAFE_CONTEXT_MASK 0x7fffffff 21#define PRINTK_SAFE_CONTEXT_MASK 0x3fffffff
22#define PRINTK_NMI_CONTEXT_MASK 0x80000000 22#define PRINTK_NMI_DEFERRED_CONTEXT_MASK 0x40000000
23#define PRINTK_NMI_CONTEXT_MASK 0x80000000
23 24
24extern raw_spinlock_t logbuf_lock; 25extern raw_spinlock_t logbuf_lock;
25 26
26__printf(1, 0) int vprintk_default(const char *fmt, va_list args); 27__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);
27__printf(1, 0) int vprintk_func(const char *fmt, va_list args); 29__printf(1, 0) int vprintk_func(const char *fmt, va_list args);
28void __printk_safe_enter(void); 30void __printk_safe_enter(void);
29void __printk_safe_exit(void); 31void __printk_safe_exit(void);
diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
index 779479ac9f57..e5278e7d1922 100644
--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -2722,16 +2722,13 @@ void wake_up_klogd(void)
2722 preempt_enable(); 2722 preempt_enable();
2723} 2723}
2724 2724
2725int printk_deferred(const char *fmt, ...) 2725int vprintk_deferred(const char *fmt, va_list args)
2726{ 2726{
2727 va_list args;
2728 int r; 2727 int r;
2729 2728
2730 preempt_disable();
2731 va_start(args, fmt);
2732 r = vprintk_emit(0, LOGLEVEL_SCHED, NULL, 0, fmt, args); 2729 r = vprintk_emit(0, LOGLEVEL_SCHED, NULL, 0, fmt, args);
2733 va_end(args);
2734 2730
2731 preempt_disable();
2735 __this_cpu_or(printk_pending, PRINTK_PENDING_OUTPUT); 2732 __this_cpu_or(printk_pending, PRINTK_PENDING_OUTPUT);
2736 irq_work_queue(this_cpu_ptr(&wake_up_klogd_work)); 2733 irq_work_queue(this_cpu_ptr(&wake_up_klogd_work));
2737 preempt_enable(); 2734 preempt_enable();
@@ -2739,6 +2736,18 @@ int printk_deferred(const char *fmt, ...)
2739 return r; 2736 return r;
2740} 2737}
2741 2738
2739int 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
2742/* 2751/*
2743 * printk rate limiting, lifted from the networking subsystem. 2752 * printk rate limiting, lifted from the networking subsystem.
2744 * 2753 *
diff --git a/kernel/printk/printk_safe.c b/kernel/printk/printk_safe.c
index 033e50a7d706..03a42a539b20 100644
--- a/kernel/printk/printk_safe.c
+++ b/kernel/printk/printk_safe.c
@@ -308,12 +308,24 @@ static int vprintk_nmi(const char *fmt, va_list args)
308 308
309void printk_nmi_enter(void) 309void printk_nmi_enter(void)
310{ 310{
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 }
312} 322}
313 323
314void printk_nmi_exit(void) 324void printk_nmi_exit(void)
315{ 325{
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));
317} 329}
318 330
319#else 331#else
@@ -351,12 +363,22 @@ void __printk_safe_exit(void)
351 363
352__printf(1, 0) int vprintk_func(const char *fmt, va_list args) 364__printf(1, 0) int vprintk_func(const char *fmt, va_list args)
353{ 365{
366 /* Use extra buffer in NMI when logbuf_lock is taken or in safe mode. */
354 if (this_cpu_read(printk_context) & PRINTK_NMI_CONTEXT_MASK) 367 if (this_cpu_read(printk_context) & PRINTK_NMI_CONTEXT_MASK)
355 return vprintk_nmi(fmt, args); 368 return vprintk_nmi(fmt, args);
356 369
370 /* Use extra buffer to prevent a recursion deadlock in safe mode. */
357 if (this_cpu_read(printk_context) & PRINTK_SAFE_CONTEXT_MASK) 371 if (this_cpu_read(printk_context) & PRINTK_SAFE_CONTEXT_MASK)
358 return vprintk_safe(fmt, args); 372 return vprintk_safe(fmt, args);
359 373
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. */
360 return vprintk_default(fmt, args); 382 return vprintk_default(fmt, args);
361} 383}
362 384
diff --git a/lib/nmi_backtrace.c b/lib/nmi_backtrace.c
index 4e8a30d1c22f..0bc0a3535a8a 100644
--- a/lib/nmi_backtrace.c
+++ b/lib/nmi_backtrace.c
@@ -86,9 +86,11 @@ void nmi_trigger_cpumask_backtrace(const cpumask_t *mask,
86 86
87bool nmi_cpu_backtrace(struct pt_regs *regs) 87bool nmi_cpu_backtrace(struct pt_regs *regs)
88{ 88{
89 static arch_spinlock_t lock = __ARCH_SPIN_LOCK_UNLOCKED;
89 int cpu = smp_processor_id(); 90 int cpu = smp_processor_id();
90 91
91 if (cpumask_test_cpu(cpu, to_cpumask(backtrace_mask))) { 92 if (cpumask_test_cpu(cpu, to_cpumask(backtrace_mask))) {
93 arch_spin_lock(&lock);
92 if (regs && cpu_in_idle(instruction_pointer(regs))) { 94 if (regs && cpu_in_idle(instruction_pointer(regs))) {
93 pr_warn("NMI backtrace for cpu %d skipped: idling at pc %#lx\n", 95 pr_warn("NMI backtrace for cpu %d skipped: idling at pc %#lx\n",
94 cpu, instruction_pointer(regs)); 96 cpu, instruction_pointer(regs));
@@ -99,6 +101,7 @@ bool nmi_cpu_backtrace(struct pt_regs *regs)
99 else 101 else
100 dump_stack(); 102 dump_stack();
101 } 103 }
104 arch_spin_unlock(&lock);
102 cpumask_clear_cpu(cpu, to_cpumask(backtrace_mask)); 105 cpumask_clear_cpu(cpu, to_cpumask(backtrace_mask));
103 return true; 106 return true;
104 } 107 }