Merge branch 'master' into upstream-fixes
[linux-drm-fsl-dcu.git] / arch / x86_64 / kernel / nmi.c
index 7af9cb3e2d99a8e71e54d89c170245580b2d470e..9cb42ecb7f8966bb90bf3426f8b82e0cad9b55cf 100644 (file)
  *  Mikael Pettersson  : PM converted to driver model. Disable/enable API.
  */
 
+#include <linux/nmi.h>
 #include <linux/mm.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
 #include <linux/module.h>
 #include <linux/sysdev.h>
-#include <linux/nmi.h>
 #include <linux/sysctl.h>
 #include <linux/kprobes.h>
+#include <linux/cpumask.h>
 
 #include <asm/smp.h>
 #include <asm/nmi.h>
@@ -41,6 +42,8 @@ int panic_on_unrecovered_nmi;
 static DEFINE_PER_CPU(unsigned, perfctr_nmi_owner);
 static DEFINE_PER_CPU(unsigned, evntsel_nmi_owner[2]);
 
+static cpumask_t backtrace_mask = CPU_MASK_NONE;
+
 /* this number is calculated from Intel's MSR_P4_CRU_ESCR5 register and it's
  * offset from MSR_P4_BSU_ESCR0.  It will be the max for all platforms (for now)
  */
@@ -190,6 +193,8 @@ void nmi_watchdog_default(void)
                nmi_watchdog = NMI_IO_APIC;
 }
 
+static int endflag __initdata = 0;
+
 #ifdef CONFIG_SMP
 /* The performance counters used by NMI_LOCAL_APIC don't trigger when
  * the CPU is idle. To make sure the NMI watchdog really ticks on all
@@ -197,7 +202,6 @@ void nmi_watchdog_default(void)
  */
 static __init void nmi_cpu_busy(void *data)
 {
-       volatile int *endflag = data;
        local_irq_enable_in_hardirq();
        /* Intentionally don't use cpu_relax here. This is
           to make sure that the performance counter really ticks,
@@ -205,14 +209,13 @@ static __init void nmi_cpu_busy(void *data)
           pause instruction. On a real HT machine this is fine because
           all other CPUs are busy with "useless" delay loops and don't
           care if they get somewhat less cycles. */
-       while (*endflag == 0)
-               barrier();
+       while (endflag == 0)
+               mb();
 }
 #endif
 
 int __init check_nmi_watchdog (void)
 {
-       volatile int endflag = 0;
        int *counts;
        int cpu;
 
@@ -253,6 +256,7 @@ int __init check_nmi_watchdog (void)
        if (!atomic_read(&nmi_active)) {
                kfree(counts);
                atomic_set(&nmi_active, -1);
+               endflag = 1;
                return -1;
        }
        endflag = 1;
@@ -298,8 +302,6 @@ int __init setup_nmi_watchdog(char *str)
        if ((nmi >= NMI_INVALID) || (nmi < NMI_NONE))
                return 0;
 
-       if ((nmi == NMI_LOCAL_APIC) && (nmi_known_cpu() == 0))
-               return 0;  /* no lapic support */
        nmi_watchdog = nmi;
        return 1;
 }
@@ -782,6 +784,7 @@ int __kprobes nmi_watchdog_tick(struct pt_regs * regs, unsigned reason)
 {
        int sum;
        int touched = 0;
+       int cpu = smp_processor_id();
        struct nmi_watchdog_ctlblk *wd = &__get_cpu_var(nmi_watchdog_ctlblk);
        u64 dummy;
        int rc=0;
@@ -799,6 +802,16 @@ int __kprobes nmi_watchdog_tick(struct pt_regs * regs, unsigned reason)
                touched = 1;
        }
 
+       if (cpu_isset(cpu, backtrace_mask)) {
+               static DEFINE_SPINLOCK(lock);   /* Serialise the printks */
+
+               spin_lock(&lock);
+               printk("NMI backtrace for cpu %d\n", cpu);
+               dump_stack();
+               spin_unlock(&lock);
+               cpu_clear(cpu, backtrace_mask);
+       }
+
 #ifdef CONFIG_X86_MCE
        /* Could check oops_in_progress here too, but it's safer
           not too */
@@ -931,6 +944,19 @@ int proc_nmi_enabled(struct ctl_table *table, int write, struct file *file,
 
 #endif
 
+void __trigger_all_cpu_backtrace(void)
+{
+       int i;
+
+       backtrace_mask = cpu_online_map;
+       /* Wait for up to 10 seconds for all CPUs to do the backtrace */
+       for (i = 0; i < 10 * 1000; i++) {
+               if (cpus_empty(backtrace_mask))
+                       break;
+               mdelay(1);
+       }
+}
+
 EXPORT_SYMBOL(nmi_active);
 EXPORT_SYMBOL(nmi_watchdog);
 EXPORT_SYMBOL(avail_to_resrv_perfctr_nmi);