arm: tegra: register save and restore ops
[linux-3.10.git] / kernel / panic.c
index 512ab73..126b2ef 100644 (file)
@@ -10,6 +10,7 @@
  */
 #include <linux/debug_locks.h>
 #include <linux/interrupt.h>
+#include <linux/kmsg_dump.h>
 #include <linux/kallsyms.h>
 #include <linux/notifier.h>
 #include <linux/module.h>
 #include <linux/sysrq.h>
 #include <linux/init.h>
 #include <linux/nmi.h>
-#include <linux/dmi.h>
 
-int panic_on_oops;
+#define PANIC_TIMER_STEP 100
+#define PANIC_BLINK_SPD 18
+
+/* Machine specific panic information string */
+char *mach_panic_string;
+
+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);
 
-int panic_timeout;
+#ifndef CONFIG_PANIC_TIMEOUT
+#define CONFIG_PANIC_TIMEOUT 0
+#endif
+int panic_timeout = CONFIG_PANIC_TIMEOUT;
+EXPORT_SYMBOL_GPL(panic_timeout);
 
 ATOMIC_NOTIFIER_HEAD(panic_notifier_list);
 
 EXPORT_SYMBOL(panic_notifier_list);
 
-static long no_blink(long time)
+static long no_blink(int state)
 {
        return 0;
 }
 
 /* Returns how long it waited in ms */
-long (*panic_blink)(long time);
+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
@@ -52,26 +71,47 @@ 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;
+       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);
 #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
 
        /*
@@ -88,8 +128,12 @@ NORET_TYPE void panic(const char * fmt, ...)
         */
        smp_send_stop();
 
+       kmsg_dump(KMSG_DUMP_PANIC);
+
        atomic_notifier_call_chain(&panic_notifier_list, 0, buf);
 
+       bust_spinlocks(0);
+
        if (!panic_blink)
                panic_blink = no_blink;
 
@@ -100,12 +144,16 @@ NORET_TYPE void panic(const char * fmt, ...)
                 */
                printk(KERN_EMERG "Rebooting in %d seconds..", panic_timeout);
 
-               for (i = 0; i < panic_timeout*1000; ) {
+               for (i = 0; i < panic_timeout * 1000; i += PANIC_TIMER_STEP) {
                        touch_nmi_watchdog();
-                       i += panic_blink(i);
-                       mdelay(1);
-                       i++;
+                       if (i >= i_next) {
+                               i += panic_blink(state ^= 1);
+                               i_next = i + 3600 / PANIC_BLINK_SPD;
+                       }
+                       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,13 +178,14 @@ NORET_TYPE void panic(const char * fmt, ...)
        }
 #endif
        local_irq_enable();
-       for (i = 0; ; ) {
+       for (i = 0; ; i += PANIC_TIMER_STEP) {
                touch_softlockup_watchdog();
-               i += panic_blink(i);
-               mdelay(1);
-               i++;
+               if (i >= i_next) {
+                       i += panic_blink(state ^= 1);
+                       i_next = i + 3600 / PANIC_BLINK_SPD;
+               }
+               mdelay(PANIC_TIMER_STEP);
        }
-       bust_spinlocks(0);
 }
 
 EXPORT_SYMBOL(panic);
@@ -160,6 +209,8 @@ static const struct tnt tnts[] = {
        { TAINT_OVERRIDDEN_ACPI_TABLE,  'A', ' ' },
        { TAINT_WARN,                   'W', ' ' },
        { TAINT_CRAP,                   'C', ' ' },
+       { TAINT_FIRMWARE_WORKAROUND,    'I', ' ' },
+       { TAINT_OOT_MODULE,             'O', ' ' },
 };
 
 /**
@@ -176,8 +227,10 @@ static const struct tnt tnts[] = {
  *  'A' - ACPI table overridden.
  *  '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.
  *
- *     The string is overwritten by the next call to print_taint().
+ *     The string is overwritten by the next call to print_tainted().
  */
 const char *print_tainted(void)
 {
@@ -211,17 +264,19 @@ 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())
+               printk(KERN_WARNING
+                      "Disabling lock debugging due to kernel taint\n");
 
        set_bit(flag, &tainted_mask);
 }
@@ -323,9 +378,14 @@ static int init_oops_id(void)
 }
 late_initcall(init_oops_id);
 
-static void print_oops_end_marker(void)
+void print_oops_end_marker(void)
 {
        init_oops_id();
+
+       if (mach_panic_string)
+               printk(KERN_WARNING "Board Information: %s\n",
+                      mach_panic_string);
+
        printk(KERN_WARNING "---[ end trace %016llx ]---\n",
                (unsigned long long)oops_id);
 }
@@ -338,6 +398,7 @@ void oops_exit(void)
 {
        do_oops_enter_exit();
        print_oops_end_marker();
+       kmsg_dump(KMSG_DUMP_OOPS);
 }
 
 #ifdef WANT_WARN_ON_SLOWPATH
@@ -346,15 +407,11 @@ struct slowpath_args {
        va_list args;
 };
 
-static void warn_slowpath_common(const char *file, int line, void *caller, struct slowpath_args *args)
+static void warn_slowpath_common(const char *file, int line, void *caller,
+                                unsigned taint, struct slowpath_args *args)
 {
-       const char *board;
-
        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);
 
        if (args)
                vprintk(args->fmt, args->args);
@@ -362,7 +419,8 @@ static void warn_slowpath_common(const char *file, int line, void *caller, struc
        print_modules();
        dump_stack();
        print_oops_end_marker();
-       add_taint(TAINT_WARN);
+       /* 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, ...)
@@ -371,14 +429,29 @@ void warn_slowpath_fmt(const char *file, int line, const char *fmt, ...)
 
        args.fmt = fmt;
        va_start(args.args, fmt);
-       warn_slowpath_common(file, line, __builtin_return_address(0), &args);
+       warn_slowpath_common(file, line, __builtin_return_address(0),
+                            TAINT_WARN, &args);
        va_end(args.args);
 }
 EXPORT_SYMBOL(warn_slowpath_fmt);
 
+void warn_slowpath_fmt_taint(const char *file, int line,
+                            unsigned taint, const char *fmt, ...)
+{
+       struct slowpath_args args;
+
+       args.fmt = fmt;
+       va_start(args.args, fmt);
+       warn_slowpath_common(file, line, __builtin_return_address(0),
+                            taint, &args);
+       va_end(args.args);
+}
+EXPORT_SYMBOL(warn_slowpath_fmt_taint);
+
 void warn_slowpath_null(const char *file, int line)
 {
-       warn_slowpath_common(file, line, __builtin_return_address(0), NULL);
+       warn_slowpath_common(file, line, __builtin_return_address(0),
+                            TAINT_WARN, NULL);
 }
 EXPORT_SYMBOL(warn_slowpath_null);
 #endif
@@ -400,3 +473,13 @@ 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 oops_setup(char *s)
+{
+       if (!s)
+               return -EINVAL;
+       if (!strcmp(s, "panic"))
+               panic_on_oops = 1;
+       return 0;
+}
+early_param("oops", oops_setup);