Commit b279d67d authored by Hidehiro Kawai's avatar Hidehiro Kawai Committed by Thomas Gleixner

x86/nmi: Save regs in crash dump on external NMI

Now, multiple CPUs can receive an external NMI simultaneously by
specifying the "apic_extnmi=all" command line parameter. When we take
a crash dump by using external NMI with this option, we fail to save
registers into the crash dump. This happens as follows:

  CPU 0                              CPU 1
  ================================   =============================
  receive an external NMI
  default_do_nmi()                   receive an external NMI
    spin_lock(&nmi_reason_lock)      default_do_nmi()
    io_check_error()                   spin_lock(&nmi_reason_lock)
      panic()                            busy loop
      ...
        kdump_nmi_shootdown_cpus()
          issue NMI IPI -----------> blocked until IRET
                                         busy loop...

Here, since CPU 1 is in NMI context, an additional NMI from CPU 0
remains unhandled until CPU 1 IRETs. However, CPU 1 will never execute
IRET so the NMI is not handled and the callback function to save
registers is never called.

To solve this issue, we check if the IPI for crash dumping was issued
while waiting for nmi_reason_lock to be released, and if so, call its
callback function directly. If the IPI is not issued (e.g. kdump is
disabled), the actual behavior doesn't change.
Signed-off-by: default avatarHidehiro Kawai <hidehiro.kawai.ez@hitachi.com>
Acked-by: default avatarMichal Hocko <mhocko@suse.com>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Andy Lutomirski <luto@kernel.org>
Cc: Baoquan He <bhe@redhat.com>
Cc: Dave Young <dyoung@redhat.com>
Cc: "Eric W. Biederman" <ebiederm@xmission.com>
Cc: "H. Peter Anvin" <hpa@zytor.com>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Jiang Liu <jiang.liu@linux.intel.com>
Cc: Jonathan Corbet <corbet@lwn.net>
Cc: kexec@lists.infradead.org
Cc: linux-doc@vger.kernel.org
Cc: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Stefan Lippers-Hollmann <s.l-h@gmx.de>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Vivek Goyal <vgoyal@redhat.com>
Cc: x86-ml <x86@kernel.org>
Link: http://lkml.kernel.org/r/20151210065245.4587.39316.stgit@softrsSigned-off-by: default avatarBorislav Petkov <bp@suse.de>
Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
parent b7c4948e
...@@ -25,5 +25,6 @@ void __noreturn machine_real_restart(unsigned int type); ...@@ -25,5 +25,6 @@ void __noreturn machine_real_restart(unsigned int type);
typedef void (*nmi_shootdown_cb)(int, struct pt_regs*); typedef void (*nmi_shootdown_cb)(int, struct pt_regs*);
void nmi_shootdown_cpus(nmi_shootdown_cb callback); void nmi_shootdown_cpus(nmi_shootdown_cb callback);
void run_crash_ipi_callback(struct pt_regs *regs);
#endif /* _ASM_X86_REBOOT_H */ #endif /* _ASM_X86_REBOOT_H */
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
#include <asm/mach_traps.h> #include <asm/mach_traps.h>
#include <asm/nmi.h> #include <asm/nmi.h>
#include <asm/x86_init.h> #include <asm/x86_init.h>
#include <asm/reboot.h>
#define CREATE_TRACE_POINTS #define CREATE_TRACE_POINTS
#include <trace/events/nmi.h> #include <trace/events/nmi.h>
...@@ -356,8 +357,19 @@ static void default_do_nmi(struct pt_regs *regs) ...@@ -356,8 +357,19 @@ static void default_do_nmi(struct pt_regs *regs)
return; return;
} }
/* Non-CPU-specific NMI: NMI sources can be processed on any CPU */ /*
raw_spin_lock(&nmi_reason_lock); * Non-CPU-specific NMI: NMI sources can be processed on any CPU.
*
* Another CPU may be processing panic routines while holding
* nmi_reason_lock. Check if the CPU issued the IPI for crash dumping,
* and if so, call its callback directly. If there is no CPU preparing
* crash dump, we simply loop here.
*/
while (!raw_spin_trylock(&nmi_reason_lock)) {
run_crash_ipi_callback(regs);
cpu_relax();
}
reason = x86_platform.get_nmi_reason(); reason = x86_platform.get_nmi_reason();
if (reason & NMI_REASON_MASK) { if (reason & NMI_REASON_MASK) {
......
...@@ -793,17 +793,23 @@ void nmi_shootdown_cpus(nmi_shootdown_cb callback) ...@@ -793,17 +793,23 @@ void nmi_shootdown_cpus(nmi_shootdown_cb callback)
/* Leave the nmi callback set */ /* Leave the nmi callback set */
} }
/*
* Check if the crash dumping IPI got issued and if so, call its callback
* directly. This function is used when we have already been in NMI handler.
* It doesn't return.
*/
void run_crash_ipi_callback(struct pt_regs *regs)
{
if (crash_ipi_issued)
crash_nmi_callback(0, regs);
}
/* Override the weak function in kernel/panic.c */ /* Override the weak function in kernel/panic.c */
void nmi_panic_self_stop(struct pt_regs *regs) void nmi_panic_self_stop(struct pt_regs *regs)
{ {
while (1) { while (1) {
/* /* If no CPU is preparing crash dump, we simply loop here. */
* Wait for the crash dumping IPI to be issued, and then run_crash_ipi_callback(regs);
* call its callback directly.
*/
if (READ_ONCE(crash_ipi_issued))
crash_nmi_callback(0, regs); /* Don't return */
cpu_relax(); cpu_relax();
} }
} }
...@@ -813,4 +819,8 @@ void nmi_shootdown_cpus(nmi_shootdown_cb callback) ...@@ -813,4 +819,8 @@ void nmi_shootdown_cpus(nmi_shootdown_cb callback)
{ {
/* No other CPUs to shoot down */ /* No other CPUs to shoot down */
} }
void run_crash_ipi_callback(struct pt_regs *regs)
{
}
#endif #endif
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