summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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 }