diff options
Diffstat (limited to 'kernel/panic.c')
| -rw-r--r-- | kernel/panic.c | 50 | 
1 files changed, 45 insertions, 5 deletions
| diff --git a/kernel/panic.c b/kernel/panic.c index da323209f583..326d91505f04 100644 --- a/kernel/panic.c +++ b/kernel/panic.c @@ -25,6 +25,7 @@  #include <linux/kexec.h>  #include <linux/panic_notifier.h>  #include <linux/sched.h> +#include <linux/string_helpers.h>  #include <linux/sysrq.h>  #include <linux/init.h>  #include <linux/nmi.h> @@ -32,6 +33,7 @@  #include <linux/bug.h>  #include <linux/ratelimit.h>  #include <linux/debugfs.h> +#include <linux/sysfs.h>  #include <trace/events/error_report.h>  #include <asm/sections.h> @@ -58,6 +60,7 @@ bool crash_kexec_post_notifiers;  int panic_on_warn __read_mostly;  unsigned long panic_on_taint;  bool panic_on_taint_nousertaint = false; +static unsigned int warn_limit __read_mostly;  int panic_timeout = CONFIG_PANIC_TIMEOUT;  EXPORT_SYMBOL_GPL(panic_timeout); @@ -75,8 +78,9 @@ ATOMIC_NOTIFIER_HEAD(panic_notifier_list);  EXPORT_SYMBOL(panic_notifier_list); -#if defined(CONFIG_SMP) && defined(CONFIG_SYSCTL) +#ifdef CONFIG_SYSCTL  static struct ctl_table kern_panic_table[] = { +#ifdef CONFIG_SMP  	{  		.procname       = "oops_all_cpu_backtrace",  		.data           = &sysctl_oops_all_cpu_backtrace, @@ -86,6 +90,14 @@ static struct ctl_table kern_panic_table[] = {  		.extra1         = SYSCTL_ZERO,  		.extra2         = SYSCTL_ONE,  	}, +#endif +	{ +		.procname       = "warn_limit", +		.data           = &warn_limit, +		.maxlen         = sizeof(warn_limit), +		.mode           = 0644, +		.proc_handler   = proc_douintvec, +	},  	{ }  }; @@ -97,6 +109,25 @@ static __init int kernel_panic_sysctls_init(void)  late_initcall(kernel_panic_sysctls_init);  #endif +static atomic_t warn_count = ATOMIC_INIT(0); + +#ifdef CONFIG_SYSFS +static ssize_t warn_count_show(struct kobject *kobj, struct kobj_attribute *attr, +			       char *page) +{ +	return sysfs_emit(page, "%d\n", atomic_read(&warn_count)); +} + +static struct kobj_attribute warn_count_attr = __ATTR_RO(warn_count); + +static __init int kernel_panic_sysfs_init(void) +{ +	sysfs_add_file_to_group(kernel_kobj, &warn_count_attr.attr, NULL); +	return 0; +} +late_initcall(kernel_panic_sysfs_init); +#endif +  static long no_blink(int state)  {  	return 0; @@ -199,6 +230,16 @@ static void panic_print_sys_info(bool console_flush)  		ftrace_dump(DUMP_ALL);  } +void check_panic_on_warn(const char *origin) +{ +	if (panic_on_warn) +		panic("%s: panic_on_warn set ...\n", origin); + +	if (atomic_inc_return(&warn_count) >= READ_ONCE(warn_limit) && warn_limit) +		panic("%s: system warned too often (kernel.warn_limit is %d)", +		      origin, warn_limit); +} +  /**   *	panic - halt the system   *	@fmt: The text string to print @@ -617,8 +658,7 @@ void __warn(const char *file, int line, void *caller, unsigned taint,  	if (regs)  		show_regs(regs); -	if (panic_on_warn) -		panic("panic_on_warn set ...\n"); +	check_panic_on_warn("kernel");  	if (!regs)  		dump_stack(); @@ -744,8 +784,8 @@ static int __init panic_on_taint_setup(char *s)  	if (s && !strcmp(s, "nousertaint"))  		panic_on_taint_nousertaint = true; -	pr_info("panic_on_taint: bitmask=0x%lx nousertaint_mode=%sabled\n", -		panic_on_taint, panic_on_taint_nousertaint ? "en" : "dis"); +	pr_info("panic_on_taint: bitmask=0x%lx nousertaint_mode=%s\n", +		panic_on_taint, str_enabled_disabled(panic_on_taint_nousertaint));  	return 0;  } |