diff options
-rw-r--r-- | kernel/printk/internal.h | 6 | ||||
-rw-r--r-- | kernel/printk/printk.c | 19 | ||||
-rw-r--r-- | kernel/printk/printk_safe.c | 26 | ||||
-rw-r--r-- | lib/nmi_backtrace.c | 3 |
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 | ||
24 | extern raw_spinlock_t logbuf_lock; | 25 | extern 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); |
28 | void __printk_safe_enter(void); | 30 | void __printk_safe_enter(void); |
29 | void __printk_safe_exit(void); | 31 | void __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 | ||
2725 | int printk_deferred(const char *fmt, ...) | 2725 | int 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 | ||
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 | |||
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 | ||
309 | void printk_nmi_enter(void) | 309 | void 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 | ||
314 | void printk_nmi_exit(void) | 324 | void 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 | ||
87 | bool nmi_cpu_backtrace(struct pt_regs *regs) | 87 | bool 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 | } |