diff options
| -rw-r--r-- | include/linux/printk.h | 2 | ||||
| -rw-r--r-- | kernel/kexec_core.c | 1 | ||||
| -rw-r--r-- | kernel/panic.c | 6 | ||||
| -rw-r--r-- | kernel/printk/internal.h | 2 | ||||
| -rw-r--r-- | kernel/printk/nmi.c | 39 | ||||
| -rw-r--r-- | kernel/printk/printk.c | 2 |
6 files changed, 49 insertions, 3 deletions
diff --git a/include/linux/printk.h b/include/linux/printk.h index 51dd6b824fe2..f4da695fd615 100644 --- a/include/linux/printk.h +++ b/include/linux/printk.h | |||
| @@ -127,11 +127,13 @@ extern void printk_nmi_init(void); | |||
| 127 | extern void printk_nmi_enter(void); | 127 | extern void printk_nmi_enter(void); |
| 128 | extern void printk_nmi_exit(void); | 128 | extern void printk_nmi_exit(void); |
| 129 | extern void printk_nmi_flush(void); | 129 | extern void printk_nmi_flush(void); |
| 130 | extern void printk_nmi_flush_on_panic(void); | ||
| 130 | #else | 131 | #else |
| 131 | static inline void printk_nmi_init(void) { } | 132 | static inline void printk_nmi_init(void) { } |
| 132 | static inline void printk_nmi_enter(void) { } | 133 | static inline void printk_nmi_enter(void) { } |
| 133 | static inline void printk_nmi_exit(void) { } | 134 | static inline void printk_nmi_exit(void) { } |
| 134 | static inline void printk_nmi_flush(void) { } | 135 | static inline void printk_nmi_flush(void) { } |
| 136 | static inline void printk_nmi_flush_on_panic(void) { } | ||
| 135 | #endif /* PRINTK_NMI */ | 137 | #endif /* PRINTK_NMI */ |
| 136 | 138 | ||
| 137 | #ifdef CONFIG_PRINTK | 139 | #ifdef CONFIG_PRINTK |
diff --git a/kernel/kexec_core.c b/kernel/kexec_core.c index 1c03dfb4abfd..d5d408252992 100644 --- a/kernel/kexec_core.c +++ b/kernel/kexec_core.c | |||
| @@ -893,6 +893,7 @@ void crash_kexec(struct pt_regs *regs) | |||
| 893 | old_cpu = atomic_cmpxchg(&panic_cpu, PANIC_CPU_INVALID, this_cpu); | 893 | old_cpu = atomic_cmpxchg(&panic_cpu, PANIC_CPU_INVALID, this_cpu); |
| 894 | if (old_cpu == PANIC_CPU_INVALID) { | 894 | if (old_cpu == PANIC_CPU_INVALID) { |
| 895 | /* This is the 1st CPU which comes here, so go ahead. */ | 895 | /* This is the 1st CPU which comes here, so go ahead. */ |
| 896 | printk_nmi_flush_on_panic(); | ||
| 896 | __crash_kexec(regs); | 897 | __crash_kexec(regs); |
| 897 | 898 | ||
| 898 | /* | 899 | /* |
diff --git a/kernel/panic.c b/kernel/panic.c index 535c96510a44..8aa74497cc5a 100644 --- a/kernel/panic.c +++ b/kernel/panic.c | |||
| @@ -160,8 +160,10 @@ void panic(const char *fmt, ...) | |||
| 160 | * | 160 | * |
| 161 | * Bypass the panic_cpu check and call __crash_kexec directly. | 161 | * Bypass the panic_cpu check and call __crash_kexec directly. |
| 162 | */ | 162 | */ |
| 163 | if (!crash_kexec_post_notifiers) | 163 | if (!crash_kexec_post_notifiers) { |
| 164 | printk_nmi_flush_on_panic(); | ||
| 164 | __crash_kexec(NULL); | 165 | __crash_kexec(NULL); |
| 166 | } | ||
| 165 | 167 | ||
| 166 | /* | 168 | /* |
| 167 | * Note smp_send_stop is the usual smp shutdown function, which | 169 | * Note smp_send_stop is the usual smp shutdown function, which |
| @@ -176,6 +178,8 @@ void panic(const char *fmt, ...) | |||
| 176 | */ | 178 | */ |
| 177 | atomic_notifier_call_chain(&panic_notifier_list, 0, buf); | 179 | atomic_notifier_call_chain(&panic_notifier_list, 0, buf); |
| 178 | 180 | ||
| 181 | /* Call flush even twice. It tries harder with a single online CPU */ | ||
| 182 | printk_nmi_flush_on_panic(); | ||
| 179 | kmsg_dump(KMSG_DUMP_PANIC); | 183 | kmsg_dump(KMSG_DUMP_PANIC); |
| 180 | 184 | ||
| 181 | /* | 185 | /* |
diff --git a/kernel/printk/internal.h b/kernel/printk/internal.h index 341bedccc065..7fd2838fa417 100644 --- a/kernel/printk/internal.h +++ b/kernel/printk/internal.h | |||
| @@ -22,6 +22,8 @@ int __printf(1, 0) vprintk_default(const char *fmt, va_list args); | |||
| 22 | 22 | ||
| 23 | #ifdef CONFIG_PRINTK_NMI | 23 | #ifdef CONFIG_PRINTK_NMI |
| 24 | 24 | ||
| 25 | extern raw_spinlock_t logbuf_lock; | ||
| 26 | |||
| 25 | /* | 27 | /* |
| 26 | * printk() could not take logbuf_lock in NMI context. Instead, | 28 | * printk() could not take logbuf_lock in NMI context. Instead, |
| 27 | * it temporary stores the strings into a per-CPU buffer. | 29 | * it temporary stores the strings into a per-CPU buffer. |
diff --git a/kernel/printk/nmi.c b/kernel/printk/nmi.c index bf08557d7e3d..b69eb8a2876f 100644 --- a/kernel/printk/nmi.c +++ b/kernel/printk/nmi.c | |||
| @@ -17,6 +17,7 @@ | |||
| 17 | 17 | ||
| 18 | #include <linux/preempt.h> | 18 | #include <linux/preempt.h> |
| 19 | #include <linux/spinlock.h> | 19 | #include <linux/spinlock.h> |
| 20 | #include <linux/debug_locks.h> | ||
| 20 | #include <linux/smp.h> | 21 | #include <linux/smp.h> |
| 21 | #include <linux/cpumask.h> | 22 | #include <linux/cpumask.h> |
| 22 | #include <linux/irq_work.h> | 23 | #include <linux/irq_work.h> |
| @@ -106,7 +107,16 @@ static void print_nmi_seq_line(struct nmi_seq_buf *s, int start, int end) | |||
| 106 | { | 107 | { |
| 107 | const char *buf = s->buffer + start; | 108 | const char *buf = s->buffer + start; |
| 108 | 109 | ||
| 109 | printk("%.*s", (end - start) + 1, buf); | 110 | /* |
| 111 | * The buffers are flushed in NMI only on panic. The messages must | ||
| 112 | * go only into the ring buffer at this stage. Consoles will get | ||
| 113 | * explicitly called later when a crashdump is not generated. | ||
| 114 | */ | ||
| 115 | if (in_nmi()) | ||
| 116 | printk_deferred("%.*s", (end - start) + 1, buf); | ||
| 117 | else | ||
| 118 | printk("%.*s", (end - start) + 1, buf); | ||
| 119 | |||
| 110 | } | 120 | } |
| 111 | 121 | ||
| 112 | /* | 122 | /* |
| @@ -194,6 +204,33 @@ void printk_nmi_flush(void) | |||
| 194 | __printk_nmi_flush(&per_cpu(nmi_print_seq, cpu).work); | 204 | __printk_nmi_flush(&per_cpu(nmi_print_seq, cpu).work); |
| 195 | } | 205 | } |
| 196 | 206 | ||
| 207 | /** | ||
| 208 | * printk_nmi_flush_on_panic - flush all per-cpu nmi buffers when the system | ||
| 209 | * goes down. | ||
| 210 | * | ||
| 211 | * Similar to printk_nmi_flush() but it can be called even in NMI context when | ||
| 212 | * the system goes down. It does the best effort to get NMI messages into | ||
| 213 | * the main ring buffer. | ||
| 214 | * | ||
| 215 | * Note that it could try harder when there is only one CPU online. | ||
| 216 | */ | ||
| 217 | void printk_nmi_flush_on_panic(void) | ||
| 218 | { | ||
| 219 | /* | ||
| 220 | * Make sure that we could access the main ring buffer. | ||
| 221 | * Do not risk a double release when more CPUs are up. | ||
| 222 | */ | ||
| 223 | if (in_nmi() && raw_spin_is_locked(&logbuf_lock)) { | ||
| 224 | if (num_online_cpus() > 1) | ||
| 225 | return; | ||
| 226 | |||
| 227 | debug_locks_off(); | ||
| 228 | raw_spin_lock_init(&logbuf_lock); | ||
| 229 | } | ||
| 230 | |||
| 231 | printk_nmi_flush(); | ||
| 232 | } | ||
| 233 | |||
| 197 | void __init printk_nmi_init(void) | 234 | void __init printk_nmi_init(void) |
| 198 | { | 235 | { |
| 199 | int cpu; | 236 | int cpu; |
diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c index e38579d730f4..60cdf6386763 100644 --- a/kernel/printk/printk.c +++ b/kernel/printk/printk.c | |||
| @@ -245,7 +245,7 @@ __packed __aligned(4) | |||
| 245 | * within the scheduler's rq lock. It must be released before calling | 245 | * within the scheduler's rq lock. It must be released before calling |
| 246 | * console_unlock() or anything else that might wake up a process. | 246 | * console_unlock() or anything else that might wake up a process. |
| 247 | */ | 247 | */ |
| 248 | static DEFINE_RAW_SPINLOCK(logbuf_lock); | 248 | DEFINE_RAW_SPINLOCK(logbuf_lock); |
| 249 | 249 | ||
| 250 | #ifdef CONFIG_PRINTK | 250 | #ifdef CONFIG_PRINTK |
| 251 | DECLARE_WAIT_QUEUE_HEAD(log_wait); | 251 | DECLARE_WAIT_QUEUE_HEAD(log_wait); |
