diff options
Diffstat (limited to 'kernel/panic.c')
| -rw-r--r-- | kernel/panic.c | 38 | 
1 files changed, 32 insertions, 6 deletions
| diff --git a/kernel/panic.c b/kernel/panic.c index 4579dbb7ed87..b333380c6bb2 100644 --- a/kernel/panic.c +++ b/kernel/panic.c @@ -61,6 +61,17 @@ void __weak panic_smp_self_stop(void)  		cpu_relax();  } +/* + * Stop ourselves in NMI context if another CPU has already panicked. Arch code + * may override this to prepare for crash dumping, e.g. save regs info. + */ +void __weak nmi_panic_self_stop(struct pt_regs *regs) +{ +	panic_smp_self_stop(); +} + +atomic_t panic_cpu = ATOMIC_INIT(PANIC_CPU_INVALID); +  /**   *	panic - halt the system   *	@fmt: The text string to print @@ -71,17 +82,17 @@ void __weak panic_smp_self_stop(void)   */  void panic(const char *fmt, ...)  { -	static DEFINE_SPINLOCK(panic_lock);  	static char buf[1024];  	va_list args;  	long i, i_next = 0;  	int state = 0; +	int old_cpu, this_cpu;  	/*  	 * Disable local interrupts. This will prevent panic_smp_self_stop  	 * from deadlocking the first cpu that invokes the panic, since  	 * there is nothing to prevent an interrupt handler (that runs -	 * after the panic_lock is acquired) from invoking panic again. +	 * after setting panic_cpu) from invoking panic() again.  	 */  	local_irq_disable(); @@ -94,8 +105,16 @@ void panic(const char *fmt, ...)  	 * multiple parallel invocations of panic, all other CPUs either  	 * stop themself or will wait until they are stopped by the 1st CPU  	 * with smp_send_stop(). +	 * +	 * `old_cpu == PANIC_CPU_INVALID' means this is the 1st CPU which +	 * comes here, so go ahead. +	 * `old_cpu == this_cpu' means we came from nmi_panic() which sets +	 * panic_cpu to this CPU.  In this case, this is also the 1st CPU.  	 */ -	if (!spin_trylock(&panic_lock)) +	this_cpu = raw_smp_processor_id(); +	old_cpu  = atomic_cmpxchg(&panic_cpu, PANIC_CPU_INVALID, this_cpu); + +	if (old_cpu != PANIC_CPU_INVALID && old_cpu != this_cpu)  		panic_smp_self_stop();  	console_verbose(); @@ -117,9 +136,11 @@ void panic(const char *fmt, ...)  	 * everything else.  	 * If we want to run this after calling panic_notifiers, pass  	 * the "crash_kexec_post_notifiers" option to the kernel. +	 * +	 * Bypass the panic_cpu check and call __crash_kexec directly.  	 */  	if (!crash_kexec_post_notifiers) -		crash_kexec(NULL); +		__crash_kexec(NULL);  	/*  	 * Note smp_send_stop is the usual smp shutdown function, which @@ -142,9 +163,11 @@ void panic(const char *fmt, ...)  	 * panic_notifiers and dumping kmsg before kdump.  	 * Note: since some panic_notifiers can make crashed kernel  	 * more unstable, it can increase risks of the kdump failure too. +	 * +	 * Bypass the panic_cpu check and call __crash_kexec directly.  	 */  	if (crash_kexec_post_notifiers) -		crash_kexec(NULL); +		__crash_kexec(NULL);  	bust_spinlocks(0); @@ -152,8 +175,11 @@ void panic(const char *fmt, ...)  	 * We may have ended up stopping the CPU holding the lock (in  	 * smp_send_stop()) while still having some valuable data in the console  	 * buffer.  Try to acquire the lock then release it regardless of the -	 * result.  The release will also print the buffers out. +	 * result.  The release will also print the buffers out.  Locks debug +	 * should be disabled to avoid reporting bad unlock balance when +	 * panic() is not being callled from OOPS.  	 */ +	debug_locks_off();  	console_trylock();  	console_unlock(); | 
