Commit 35adacd6 authored by Nicholas Piggin's avatar Nicholas Piggin Committed by Michael Ellerman

powerpc/pseries, ps3: panic flush kernel messages before halting system

Platforms with a panic handler that halts the system can have problems
getting kernel messages out, because the panic notifiers are called
before kernel/panic.c does its flushing of printk buffers an console
etc.

This was attempted to be solved with commit a3b2cb30 ("powerpc: Do
not call ppc_md.panic in fadump panic notifier"), but that wasn't the
right approach and caused other problems, and was reverted by commit
ab9dbf77.

Instead, the powernv shutdown paths have already had a similar
problem, fixed by taking the message flushing sequence from
kernel/panic.c. That's a little bit ugly, but while we have the code
duplicated, it will work for this case as well. So have ppc panic
handlers do the same flushing before they terminate.

Without this patch, a qemu pseries_le_defconfig guest stops silently
when issued the nmi command when xmon is off and no crash dumpers
enabled. Afterwards, an oops is printed by each CPU as expected.

Fixes: ab9dbf77 ("Revert "powerpc: Do not call ppc_md.panic in fadump panic notifier"")
Signed-off-by: default avatarNicholas Piggin <npiggin@gmail.com>
Reviewed-by: default avatarDavid Gibson <david@gibson.dropbear.id.au>
Signed-off-by: default avatarMichael Ellerman <mpe@ellerman.id.au>
parent a08082f8
...@@ -136,7 +136,8 @@ extern void _exception(int, struct pt_regs *, int, unsigned long); ...@@ -136,7 +136,8 @@ extern void _exception(int, struct pt_regs *, int, unsigned long);
extern void _exception_pkey(int, struct pt_regs *, int, unsigned long, int); extern void _exception_pkey(int, struct pt_regs *, int, unsigned long, int);
extern void die(const char *, struct pt_regs *, long); extern void die(const char *, struct pt_regs *, long);
extern bool die_will_crash(void); extern bool die_will_crash(void);
extern void panic_flush_kmsg_start(void);
extern void panic_flush_kmsg_end(void);
#endif /* !__ASSEMBLY__ */ #endif /* !__ASSEMBLY__ */
#endif /* __KERNEL__ */ #endif /* __KERNEL__ */
......
...@@ -39,6 +39,8 @@ ...@@ -39,6 +39,8 @@
#include <linux/ratelimit.h> #include <linux/ratelimit.h>
#include <linux/context_tracking.h> #include <linux/context_tracking.h>
#include <linux/smp.h> #include <linux/smp.h>
#include <linux/console.h>
#include <linux/kmsg_dump.h>
#include <asm/emulated_ops.h> #include <asm/emulated_ops.h>
#include <asm/pgtable.h> #include <asm/pgtable.h>
...@@ -143,6 +145,28 @@ static int die_owner = -1; ...@@ -143,6 +145,28 @@ static int die_owner = -1;
static unsigned int die_nest_count; static unsigned int die_nest_count;
static int die_counter; static int die_counter;
extern void panic_flush_kmsg_start(void)
{
/*
* These are mostly taken from kernel/panic.c, but tries to do
* relatively minimal work. Don't use delay functions (TB may
* be broken), don't crash dump (need to set a firmware log),
* don't run notifiers. We do want to get some information to
* Linux console.
*/
console_verbose();
bust_spinlocks(1);
}
extern void panic_flush_kmsg_end(void)
{
printk_safe_flush_on_panic();
kmsg_dump(KMSG_DUMP_PANIC);
bust_spinlocks(0);
debug_locks_off();
console_flush_on_panic();
}
static unsigned long oops_begin(struct pt_regs *regs) static unsigned long oops_begin(struct pt_regs *regs)
{ {
int cpu; int cpu;
......
...@@ -461,24 +461,14 @@ static int opal_recover_mce(struct pt_regs *regs, ...@@ -461,24 +461,14 @@ static int opal_recover_mce(struct pt_regs *regs,
void pnv_platform_error_reboot(struct pt_regs *regs, const char *msg) void pnv_platform_error_reboot(struct pt_regs *regs, const char *msg)
{ {
/* panic_flush_kmsg_start();
* This is mostly taken from kernel/panic.c, but tries to do
* relatively minimal work. Don't use delay functions (TB may
* be broken), don't crash dump (need to set a firmware log),
* don't run notifiers. We do want to get some information to
* Linux console.
*/
console_verbose();
bust_spinlocks(1);
pr_emerg("Hardware platform error: %s\n", msg); pr_emerg("Hardware platform error: %s\n", msg);
if (regs) if (regs)
show_regs(regs); show_regs(regs);
smp_send_stop(); smp_send_stop();
printk_safe_flush_on_panic();
kmsg_dump(KMSG_DUMP_PANIC); panic_flush_kmsg_end();
bust_spinlocks(0);
debug_locks_off();
console_flush_on_panic();
/* /*
* Don't bother to shut things down because this will * Don't bother to shut things down because this will
......
...@@ -113,6 +113,7 @@ static void ps3_panic(char *str) ...@@ -113,6 +113,7 @@ static void ps3_panic(char *str)
printk(" System does not reboot automatically.\n"); printk(" System does not reboot automatically.\n");
printk(" Please press POWER button.\n"); printk(" Please press POWER button.\n");
printk("\n"); printk("\n");
panic_flush_kmsg_end();
while(1) while(1)
lv1_pause(1); lv1_pause(1);
......
...@@ -533,6 +533,12 @@ static void __init pSeries_setup_arch(void) ...@@ -533,6 +533,12 @@ static void __init pSeries_setup_arch(void)
ppc_md.pcibios_root_bridge_prepare = pseries_root_bridge_prepare; ppc_md.pcibios_root_bridge_prepare = pseries_root_bridge_prepare;
} }
static void pseries_panic(char *str)
{
panic_flush_kmsg_end();
rtas_os_term(str);
}
static int __init pSeries_init_panel(void) static int __init pSeries_init_panel(void)
{ {
/* Manually leave the kernel version on the panel. */ /* Manually leave the kernel version on the panel. */
...@@ -761,7 +767,7 @@ define_machine(pseries) { ...@@ -761,7 +767,7 @@ define_machine(pseries) {
.pcibios_fixup = pSeries_final_fixup, .pcibios_fixup = pSeries_final_fixup,
.restart = rtas_restart, .restart = rtas_restart,
.halt = rtas_halt, .halt = rtas_halt,
.panic = rtas_os_term, .panic = pseries_panic,
.get_boot_time = rtas_get_boot_time, .get_boot_time = rtas_get_boot_time,
.get_rtc_time = rtas_get_rtc_time, .get_rtc_time = rtas_get_rtc_time,
.set_rtc_time = rtas_set_rtc_time, .set_rtc_time = rtas_set_rtc_time,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment