drm/edid/firmware: Add built-in edid/1280x720.bin firmware
[platform/kernel/linux-starfive.git] / kernel / panic.c
index da32320..ca5452a 100644 (file)
@@ -32,6 +32,8 @@
 #include <linux/bug.h>
 #include <linux/ratelimit.h>
 #include <linux/debugfs.h>
+#include <linux/sysfs.h>
+#include <linux/context_tracking.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;
@@ -180,9 +211,6 @@ static void panic_print_sys_info(bool console_flush)
                return;
        }
 
-       if (panic_print & PANIC_PRINT_ALL_CPU_BT)
-               trigger_all_cpu_backtrace();
-
        if (panic_print & PANIC_PRINT_TASK_INFO)
                show_state();
 
@@ -199,6 +227,43 @@ static void panic_print_sys_info(bool console_flush)
                ftrace_dump(DUMP_ALL);
 }
 
+void check_panic_on_warn(const char *origin)
+{
+       unsigned int limit;
+
+       if (panic_on_warn)
+               panic("%s: panic_on_warn set ...\n", origin);
+
+       limit = READ_ONCE(warn_limit);
+       if (atomic_inc_return(&warn_count) >= limit && limit)
+               panic("%s: system warned too often (kernel.warn_limit is %d)",
+                     origin, limit);
+}
+
+/*
+ * Helper that triggers the NMI backtrace (if set in panic_print)
+ * and then performs the secondary CPUs shutdown - we cannot have
+ * the NMI backtrace after the CPUs are off!
+ */
+static void panic_other_cpus_shutdown(bool crash_kexec)
+{
+       if (panic_print & PANIC_PRINT_ALL_CPU_BT)
+               trigger_all_cpu_backtrace();
+
+       /*
+        * Note that smp_send_stop() is the usual SMP shutdown function,
+        * which unfortunately may not be hardened to work in a panic
+        * situation. If we want to do crash dump after notifier calls
+        * and kmsg_dump, we will need architecture dependent extra
+        * bits in addition to stopping other CPUs, hence we rely on
+        * crash_smp_send_stop() for that.
+        */
+       if (!crash_kexec)
+               smp_send_stop();
+       else
+               crash_smp_send_stop();
+}
+
 /**
  *     panic - halt the system
  *     @fmt: The text string to print
@@ -289,23 +354,10 @@ void panic(const char *fmt, ...)
         *
         * Bypass the panic_cpu check and call __crash_kexec directly.
         */
-       if (!_crash_kexec_post_notifiers) {
+       if (!_crash_kexec_post_notifiers)
                __crash_kexec(NULL);
 
-               /*
-                * Note smp_send_stop is the usual smp shutdown function, which
-                * unfortunately means it may not be hardened to work in a
-                * panic situation.
-                */
-               smp_send_stop();
-       } else {
-               /*
-                * If we want to do crash dump after notifier calls and
-                * kmsg_dump, we will need architecture dependent extra
-                * works in addition to stopping other CPUs.
-                */
-               crash_smp_send_stop();
-       }
+       panic_other_cpus_shutdown(_crash_kexec_post_notifiers);
 
        /*
         * Run any panic handlers, including those that might need to
@@ -617,8 +669,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();
@@ -636,6 +687,7 @@ void __warn(const char *file, int line, void *caller, unsigned taint,
 void warn_slowpath_fmt(const char *file, int line, unsigned taint,
                       const char *fmt, ...)
 {
+       bool rcu = warn_rcu_enter();
        struct warn_args args;
 
        pr_warn(CUT_HERE);
@@ -650,11 +702,13 @@ void warn_slowpath_fmt(const char *file, int line, unsigned taint,
        va_start(args.args, fmt);
        __warn(file, line, __builtin_return_address(0), taint, NULL, &args);
        va_end(args.args);
+       warn_rcu_exit(rcu);
 }
 EXPORT_SYMBOL(warn_slowpath_fmt);
 #else
 void __warn_printk(const char *fmt, ...)
 {
+       bool rcu = warn_rcu_enter();
        va_list args;
 
        pr_warn(CUT_HERE);
@@ -662,6 +716,7 @@ void __warn_printk(const char *fmt, ...)
        va_start(args, fmt);
        vprintk(fmt, args);
        va_end(args);
+       warn_rcu_exit(rcu);
 }
 EXPORT_SYMBOL(__warn_printk);
 #endif