diff options
Diffstat (limited to 'kernel/panic.c')
| -rw-r--r-- | kernel/panic.c | 102 |
1 files changed, 48 insertions, 54 deletions
diff --git a/kernel/panic.c b/kernel/panic.c index 213725b612aa..170744163fc2 100644 --- a/kernel/panic.c +++ b/kernel/panic.c @@ -307,7 +307,7 @@ atomic_t panic_cpu = ATOMIC_INIT(PANIC_CPU_INVALID); atomic_t panic_redirect_cpu = ATOMIC_INIT(PANIC_CPU_INVALID); #if defined(CONFIG_SMP) && defined(CONFIG_CRASH_DUMP) -static char *panic_force_buf; +static char panic_force_buf[PANIC_MSG_BUFSZ]; static int __init panic_force_cpu_setup(char *str) { @@ -326,17 +326,6 @@ static int __init panic_force_cpu_setup(char *str) } early_param("panic_force_cpu", panic_force_cpu_setup); -static int __init panic_force_cpu_late_init(void) -{ - if (panic_force_cpu < 0) - return 0; - - panic_force_buf = kmalloc(PANIC_MSG_BUFSZ, GFP_KERNEL); - - return 0; -} -late_initcall(panic_force_cpu_late_init); - static void do_panic_on_target_cpu(void *info) { panic("%s", (char *)info); @@ -371,15 +360,16 @@ int __weak panic_smp_redirect_cpu(int target_cpu, void *msg) * for the crash kernel to function correctly. This function redirects * panic handling to the CPU specified via the panic_force_cpu= boot parameter. * - * Returns false if panic should proceed on current CPU. - * Returns true if panic was redirected. + * Returns true when this CPU must stop: the panic was redirected or is + * already running on another CPU. + * Returns false when panic() should proceed on this CPU. */ __printf(1, 0) static bool panic_try_force_cpu(const char *fmt, va_list args) { int this_cpu = raw_smp_processor_id(); int old_cpu = PANIC_CPU_INVALID; - const char *msg; + va_list ap; /* Feature not enabled via boot parameter */ if (panic_force_cpu < 0) @@ -396,27 +386,27 @@ static bool panic_try_force_cpu(const char *fmt, va_list args) return false; } - /* Another panic already in progress */ + /* + * Don't redirect when a panic is already in progress. Stop this + * CPU when it's another one, proceed when it's this one. + */ if (panic_in_progress()) - return false; + return panic_on_other_cpu(); /* - * Only one CPU can do the redirect. Use atomic cmpxchg to ensure - * we don't race with another CPU also trying to redirect. + * Only one CPU can do the redirection. Others should go offline. + * Continue with panic() when we already tried the redirection + * from this CPU before, for example via nmi_panic(). */ if (!atomic_try_cmpxchg(&panic_redirect_cpu, &old_cpu, this_cpu)) - return false; + return old_cpu != this_cpu; /* - * Use dynamically allocated buffer if available, otherwise - * fall back to static message for early boot panics or allocation failure. + * Do not consume args, the caller reuses them if we fail. */ - if (panic_force_buf) { - vsnprintf(panic_force_buf, PANIC_MSG_BUFSZ, fmt, args); - msg = panic_force_buf; - } else { - msg = "Redirected panic (buffer unavailable)"; - } + va_copy(ap, args); + vsnprintf(panic_force_buf, PANIC_MSG_BUFSZ, fmt, ap); + va_end(ap); console_verbose(); bust_spinlocks(1); @@ -431,7 +421,7 @@ static bool panic_try_force_cpu(const char *fmt, va_list args) dump_stack(); } - if (panic_smp_redirect_cpu(panic_force_cpu, (void *)msg) != 0) { + if (panic_smp_redirect_cpu(panic_force_cpu, panic_force_buf) != 0) { atomic_set(&panic_redirect_cpu, PANIC_CPU_INVALID); pr_warn("panic: failed to redirect to CPU %d, continuing on CPU %d\n", panic_force_cpu, this_cpu); @@ -503,17 +493,35 @@ bool panic_on_other_cpu(void) EXPORT_SYMBOL(panic_on_other_cpu); /* - * A variant of panic() called from NMI context. We return if we've already - * panicked on this CPU. If another CPU already panicked, loop in - * nmi_panic_self_stop() which can provide architecture dependent code such - * as saving register state for crash dump. + * A variant of panic() called from NMI context. The panic is first + * redirected to the CPU requested via panic_force_cpu=, when configured. + * We return if we've already panicked on this CPU. If another CPU already + * panicked, loop in nmi_panic_self_stop() which can provide architecture + * dependent code for saving register state for crash dump. */ -void nmi_panic(struct pt_regs *regs, const char *msg) +void nmi_panic(struct pt_regs *regs, const char *fmt, ...) { + va_list args; + + va_start(args, fmt); + + /* Try to redirect to the requested CPU before claiming panic_cpu. */ + if (panic_try_force_cpu(fmt, args)) { + /* + * Mark ourselves offline so panic_other_cpus_shutdown() won't + * wait for us on architectures that check num_online_cpus(). + */ + set_cpu_online(raw_smp_processor_id(), false); + nmi_panic_self_stop(regs); + } + if (panic_try_start()) - panic("%s", msg); - else if (panic_on_other_cpu()) + vpanic(fmt, args); + + if (panic_on_other_cpu()) nmi_panic_self_stop(regs); + + va_end(args); } EXPORT_SYMBOL(nmi_panic); @@ -567,6 +575,8 @@ static void panic_other_cpus_shutdown(bool crash_kexec) crash_smp_send_stop(); } +void __weak arch_do_panic(void) {} + /** * vpanic - halt the system * @fmt: The text string to print @@ -742,20 +752,9 @@ void vpanic(const char *fmt, va_list args) reboot_mode = panic_reboot_mode; emergency_restart(); } -#ifdef __sparc__ - { - extern int stop_a_enabled; - /* Make sure the user can actually press Stop-A (L1-A) */ - stop_a_enabled = 1; - pr_emerg("Press Stop-A (L1-A) from sun keyboard or send break\n" - "twice on console to return to the boot prom\n"); - } -#endif -#if defined(CONFIG_S390) - disabled_wait(); -#endif pr_emerg("---[ end Kernel panic - not syncing: %s ]---\n", buf); + arch_do_panic(); /* Do not scroll important messages printed above */ suppress_printk = 1; @@ -1225,14 +1224,9 @@ static int panic_print_set(const char *val, const struct kernel_param *kp) return param_set_ulong(val, kp); } -static int panic_print_get(char *val, const struct kernel_param *kp) -{ - return param_get_ulong(val, kp); -} - static const struct kernel_param_ops panic_print_ops = { .set = panic_print_set, - .get = panic_print_get, + .get = param_get_ulong, }; __core_param_cb(panic_print, &panic_print_ops, &panic_print, 0644); |
