diff options
Diffstat (limited to 'arch/x86/kernel/apic/hw_nmi.c')
| -rw-r--r-- | arch/x86/kernel/apic/hw_nmi.c | 93 | 
1 files changed, 44 insertions, 49 deletions
diff --git a/arch/x86/kernel/apic/hw_nmi.c b/arch/x86/kernel/apic/hw_nmi.c index cefd6942f0e..6a1e71bde32 100644 --- a/arch/x86/kernel/apic/hw_nmi.c +++ b/arch/x86/kernel/apic/hw_nmi.c @@ -9,6 +9,7 @@   *   */  #include <asm/apic.h> +#include <asm/nmi.h>  #include <linux/cpumask.h>  #include <linux/kdebug.h> @@ -16,51 +17,65 @@  #include <linux/kprobes.h>  #include <linux/nmi.h>  #include <linux/module.h> +#include <linux/delay.h> +#ifdef CONFIG_HARDLOCKUP_DETECTOR +u64 hw_nmi_get_sample_period(int watchdog_thresh) +{ +	return (u64)(cpu_khz) * 1000 * watchdog_thresh; +} +#endif + +#ifdef arch_trigger_all_cpu_backtrace  /* For reliability, we're prepared to waste bits here. */  static DECLARE_BITMAP(backtrace_mask, NR_CPUS) __read_mostly; -u64 hw_nmi_get_sample_period(void) -{ -	return (u64)(cpu_khz) * 1000 * 60; -} +/* "in progress" flag of arch_trigger_all_cpu_backtrace */ +static unsigned long backtrace_flag; -#ifdef ARCH_HAS_NMI_WATCHDOG -void arch_trigger_all_cpu_backtrace(void) +void arch_trigger_all_cpu_backtrace(bool include_self)  {  	int i; +	int cpu = get_cpu(); + +	if (test_and_set_bit(0, &backtrace_flag)) { +		/* +		 * If there is already a trigger_all_cpu_backtrace() in progress +		 * (backtrace_flag == 1), don't output double cpu dump infos. +		 */ +		put_cpu(); +		return; +	}  	cpumask_copy(to_cpumask(backtrace_mask), cpu_online_mask); +	if (!include_self) +		cpumask_clear_cpu(cpu, to_cpumask(backtrace_mask)); -	printk(KERN_INFO "sending NMI to all CPUs:\n"); -	apic->send_IPI_all(NMI_VECTOR); +	if (!cpumask_empty(to_cpumask(backtrace_mask))) { +		pr_info("sending NMI to %s CPUs:\n", +			(include_self ? "all" : "other")); +		apic->send_IPI_mask(to_cpumask(backtrace_mask), NMI_VECTOR); +	}  	/* Wait for up to 10 seconds for all CPUs to do the backtrace */  	for (i = 0; i < 10 * 1000; i++) {  		if (cpumask_empty(to_cpumask(backtrace_mask)))  			break;  		mdelay(1); +		touch_softlockup_watchdog();  	} + +	clear_bit(0, &backtrace_flag); +	smp_mb__after_atomic(); +	put_cpu();  } -static int __kprobes -arch_trigger_all_cpu_backtrace_handler(struct notifier_block *self, -			 unsigned long cmd, void *__args) +static int +arch_trigger_all_cpu_backtrace_handler(unsigned int cmd, struct pt_regs *regs)  { -	struct die_args *args = __args; -	struct pt_regs *regs; -	int cpu = smp_processor_id(); - -	switch (cmd) { -	case DIE_NMI: -	case DIE_NMI_IPI: -		break; - -	default: -		return NOTIFY_DONE; -	} +	int cpu; -	regs = args->regs; +	cpu = smp_processor_id();  	if (cpumask_test_cpu(cpu, to_cpumask(backtrace_mask))) {  		static arch_spinlock_t lock = __ARCH_SPIN_LOCK_UNLOCKED; @@ -68,40 +83,20 @@ arch_trigger_all_cpu_backtrace_handler(struct notifier_block *self,  		arch_spin_lock(&lock);  		printk(KERN_WARNING "NMI backtrace for cpu %d\n", cpu);  		show_regs(regs); -		dump_stack();  		arch_spin_unlock(&lock);  		cpumask_clear_cpu(cpu, to_cpumask(backtrace_mask)); -		return NOTIFY_STOP; +		return NMI_HANDLED;  	} -	return NOTIFY_DONE; +	return NMI_DONE;  } - -static __read_mostly struct notifier_block backtrace_notifier = { -	.notifier_call          = arch_trigger_all_cpu_backtrace_handler, -	.next                   = NULL, -	.priority               = 1 -}; +NOKPROBE_SYMBOL(arch_trigger_all_cpu_backtrace_handler);  static int __init register_trigger_all_cpu_backtrace(void)  { -	register_die_notifier(&backtrace_notifier); +	register_nmi_handler(NMI_LOCAL, arch_trigger_all_cpu_backtrace_handler, +				0, "arch_bt");  	return 0;  }  early_initcall(register_trigger_all_cpu_backtrace);  #endif - -/* STUB calls to mimic old nmi_watchdog behaviour */ -#if defined(CONFIG_X86_LOCAL_APIC) -unsigned int nmi_watchdog = NMI_NONE; -EXPORT_SYMBOL(nmi_watchdog); -void acpi_nmi_enable(void) { return; } -void acpi_nmi_disable(void) { return; } -#endif -atomic_t nmi_active = ATOMIC_INIT(0);           /* oprofile uses this */ -EXPORT_SYMBOL(nmi_active); -int unknown_nmi_panic; -void cpu_nmi_set_wd_enabled(void) { return; } -void stop_apic_nmi_watchdog(void *unused) { return; } -void setup_apic_nmi_watchdog(void *unused) { return; } -int __init check_nmi_watchdog(void) { return 0; }  | 
