diff options
Diffstat (limited to 'kernel/panic.c')
| -rw-r--r-- | kernel/panic.c | 138 | 
1 files changed, 103 insertions, 35 deletions
diff --git a/kernel/panic.c b/kernel/panic.c index 4c13b1a88eb..62e16cef9cc 100644 --- a/kernel/panic.c +++ b/kernel/panic.c @@ -15,6 +15,7 @@  #include <linux/notifier.h>  #include <linux/module.h>  #include <linux/random.h> +#include <linux/ftrace.h>  #include <linux/reboot.h>  #include <linux/delay.h>  #include <linux/kexec.h> @@ -22,18 +23,19 @@  #include <linux/sysrq.h>  #include <linux/init.h>  #include <linux/nmi.h> -#include <linux/dmi.h>  #define PANIC_TIMER_STEP 100  #define PANIC_BLINK_SPD 18 -int panic_on_oops; +int panic_on_oops = CONFIG_PANIC_ON_OOPS_VALUE;  static unsigned long tainted_mask;  static int pause_on_oops;  static int pause_on_oops_flag;  static DEFINE_SPINLOCK(pause_on_oops_lock); +static bool crash_kexec_post_notifiers; -int panic_timeout; +int panic_timeout = CONFIG_PANIC_TIMEOUT; +EXPORT_SYMBOL_GPL(panic_timeout);  ATOMIC_NOTIFIER_HEAD(panic_notifier_list); @@ -48,6 +50,15 @@ static long no_blink(int state)  long (*panic_blink)(int state);  EXPORT_SYMBOL(panic_blink); +/* + * Stop ourself in panic -- architecture code may override this + */ +void __weak panic_smp_self_stop(void) +{ +	while (1) +		cpu_relax(); +} +  /**   *	panic - halt the system   *	@fmt: The text string to print @@ -56,38 +67,57 @@ EXPORT_SYMBOL(panic_blink);   *   *	This function never returns.   */ -NORET_TYPE void panic(const char * fmt, ...) +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;  	/* +	 * 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. +	 */ +	local_irq_disable(); + +	/*  	 * It's possible to come here directly from a panic-assertion and  	 * not have preempt disabled. Some functions called from here want  	 * preempt to be disabled. No point enabling it later though... +	 * +	 * Only one CPU is allowed to execute the panic code from here. For +	 * 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().  	 */ -	preempt_disable(); +	if (!spin_trylock(&panic_lock)) +		panic_smp_self_stop();  	console_verbose();  	bust_spinlocks(1);  	va_start(args, fmt);  	vsnprintf(buf, sizeof(buf), fmt, args);  	va_end(args); -	printk(KERN_EMERG "Kernel panic - not syncing: %s\n",buf); +	pr_emerg("Kernel panic - not syncing: %s\n", buf);  #ifdef CONFIG_DEBUG_BUGVERBOSE -	dump_stack(); +	/* +	 * Avoid nested stack-dumping if a panic occurs during oops processing +	 */ +	if (!test_taint(TAINT_DIE) && oops_in_progress <= 1) +		dump_stack();  #endif  	/*  	 * If we have crashed and we have a crash kernel loaded let it handle  	 * everything else. -	 * Do we want to call this before we try to display a message? +	 * If we want to run this after calling panic_notifiers, pass +	 * the "crash_kexec_post_notifiers" option to the kernel.  	 */ -	crash_kexec(NULL); - -	kmsg_dump(KMSG_DUMP_PANIC); +	if (!crash_kexec_post_notifiers) +		crash_kexec(NULL);  	/*  	 * Note smp_send_stop is the usual smp shutdown function, which @@ -96,8 +126,23 @@ NORET_TYPE void panic(const char * fmt, ...)  	 */  	smp_send_stop(); +	/* +	 * Run any panic handlers, including those that might need to +	 * add information to the kmsg dump output. +	 */  	atomic_notifier_call_chain(&panic_notifier_list, 0, buf); +	kmsg_dump(KMSG_DUMP_PANIC); + +	/* +	 * If you doubt kdump always works fine in any situation, +	 * "crash_kexec_post_notifiers" offers you a chance to run +	 * 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. +	 */ +	crash_kexec(NULL); +  	bust_spinlocks(0);  	if (!panic_blink) @@ -108,7 +153,7 @@ NORET_TYPE void panic(const char * fmt, ...)  		 * Delay timeout seconds before rebooting the machine.  		 * We can't use the "normal" timers since we just panicked.  		 */ -		printk(KERN_EMERG "Rebooting in %d seconds..", panic_timeout); +		pr_emerg("Rebooting in %d seconds..", panic_timeout);  		for (i = 0; i < panic_timeout * 1000; i += PANIC_TIMER_STEP) {  			touch_nmi_watchdog(); @@ -118,6 +163,8 @@ NORET_TYPE void panic(const char * fmt, ...)  			}  			mdelay(PANIC_TIMER_STEP);  		} +	} +	if (panic_timeout != 0) {  		/*  		 * This will not be a clean reboot, with everything  		 * shutting down.  But if there is a chance of @@ -130,7 +177,7 @@ NORET_TYPE void panic(const char * fmt, ...)  		extern int stop_a_enabled;  		/* Make sure the user can actually press Stop-A (L1-A) */  		stop_a_enabled = 1; -		printk(KERN_EMERG "Press Stop-A (L1-A) to return to the boot prom\n"); +		pr_emerg("Press Stop-A (L1-A) to return to the boot prom\n");  	}  #endif  #if defined(CONFIG_S390) @@ -141,6 +188,7 @@ NORET_TYPE void panic(const char * fmt, ...)  		disabled_wait(caller);  	}  #endif +	pr_emerg("---[ end Kernel panic - not syncing: %s\n", buf);  	local_irq_enable();  	for (i = 0; ; i += PANIC_TIMER_STEP) {  		touch_softlockup_watchdog(); @@ -164,7 +212,7 @@ struct tnt {  static const struct tnt tnts[] = {  	{ TAINT_PROPRIETARY_MODULE,	'P', 'G' },  	{ TAINT_FORCED_MODULE,		'F', ' ' }, -	{ TAINT_UNSAFE_SMP,		'S', ' ' }, +	{ TAINT_CPU_OUT_OF_SPEC,	'S', ' ' },  	{ TAINT_FORCED_RMMOD,		'R', ' ' },  	{ TAINT_MACHINE_CHECK,		'M', ' ' },  	{ TAINT_BAD_PAGE,		'B', ' ' }, @@ -174,6 +222,8 @@ static const struct tnt tnts[] = {  	{ TAINT_WARN,			'W', ' ' },  	{ TAINT_CRAP,			'C', ' ' },  	{ TAINT_FIRMWARE_WORKAROUND,	'I', ' ' }, +	{ TAINT_OOT_MODULE,		'O', ' ' }, +	{ TAINT_UNSIGNED_MODULE,	'E', ' ' },  };  /** @@ -191,12 +241,14 @@ static const struct tnt tnts[] = {   *  'W' - Taint on warning.   *  'C' - modules from drivers/staging are loaded.   *  'I' - Working around severe firmware bug. + *  'O' - Out-of-tree module has been loaded. + *  'E' - Unsigned module has been loaded.   *   *	The string is overwritten by the next call to print_tainted().   */  const char *print_tainted(void)  { -	static char buf[ARRAY_SIZE(tnts) + sizeof("Tainted: ") + 1]; +	static char buf[ARRAY_SIZE(tnts) + sizeof("Tainted: ")];  	if (tainted_mask) {  		char *s; @@ -226,17 +278,18 @@ unsigned long get_taint(void)  	return tainted_mask;  } -void add_taint(unsigned flag) +/** + * add_taint: add a taint flag if not already set. + * @flag: one of the TAINT_* constants. + * @lockdep_ok: whether lock debugging is still OK. + * + * If something bad has gone wrong, you'll want @lockdebug_ok = false, but for + * some notewortht-but-not-corrupting cases, it can be set to true. + */ +void add_taint(unsigned flag, enum lockdep_ok lockdep_ok)  { -	/* -	 * Can't trust the integrity of the kernel anymore. -	 * We don't call directly debug_locks_off() because the issue -	 * is not necessarily serious enough to set oops_in_progress to 1 -	 * Also we want to keep up lockdep for staging development and -	 * post-warning case. -	 */ -	if (flag != TAINT_CRAP && flag != TAINT_WARN && __debug_locks_off()) -		printk(KERN_WARNING "Disabling lock debugging due to kernel taint\n"); +	if (lockdep_ok == LOCKDEP_NOW_UNRELIABLE && __debug_locks_off()) +		pr_warn("Disabling lock debugging due to kernel taint\n");  	set_bit(flag, &tainted_mask);  } @@ -341,8 +394,7 @@ late_initcall(init_oops_id);  void print_oops_end_marker(void)  {  	init_oops_id(); -	printk(KERN_WARNING "---[ end trace %016llx ]---\n", -		(unsigned long long)oops_id); +	pr_warn("---[ end trace %016llx ]---\n", (unsigned long long)oops_id);  }  /* @@ -365,13 +417,11 @@ struct slowpath_args {  static void warn_slowpath_common(const char *file, int line, void *caller,  				 unsigned taint, struct slowpath_args *args)  { -	const char *board; +	disable_trace_on_warning(); -	printk(KERN_WARNING "------------[ cut here ]------------\n"); -	printk(KERN_WARNING "WARNING: at %s:%d %pS()\n", file, line, caller); -	board = dmi_get_system_info(DMI_PRODUCT_NAME); -	if (board) -		printk(KERN_WARNING "Hardware name: %s\n", board); +	pr_warn("------------[ cut here ]------------\n"); +	pr_warn("WARNING: CPU: %d PID: %d at %s:%d %pS()\n", +		raw_smp_processor_id(), current->pid, file, line, caller);  	if (args)  		vprintk(args->fmt, args->args); @@ -379,7 +429,8 @@ static void warn_slowpath_common(const char *file, int line, void *caller,  	print_modules();  	dump_stack();  	print_oops_end_marker(); -	add_taint(taint); +	/* Just a warning, don't kill lockdep. */ +	add_taint(taint, LOCKDEP_STILL_OK);  }  void warn_slowpath_fmt(const char *file, int line, const char *fmt, ...) @@ -421,7 +472,7 @@ EXPORT_SYMBOL(warn_slowpath_null);   * Called when gcc's -fstack-protector feature is used, and   * gcc detects corruption of the on-stack canary value   */ -void __stack_chk_fail(void) +__visible void __stack_chk_fail(void)  {  	panic("stack-protector: Kernel stack is corrupted in: %p\n",  		__builtin_return_address(0)); @@ -432,3 +483,20 @@ EXPORT_SYMBOL(__stack_chk_fail);  core_param(panic, panic_timeout, int, 0644);  core_param(pause_on_oops, pause_on_oops, int, 0644); + +static int __init setup_crash_kexec_post_notifiers(char *s) +{ +	crash_kexec_post_notifiers = true; +	return 0; +} +early_param("crash_kexec_post_notifiers", setup_crash_kexec_post_notifiers); + +static int __init oops_setup(char *s) +{ +	if (!s) +		return -EINVAL; +	if (!strcmp(s, "panic")) +		panic_on_oops = 1; +	return 0; +} +early_param("oops", oops_setup);  | 
