x86: nmi - unify die_nmi() interface
PATCH 1/11 x86: nmi - unify die_nmi() interface
In reply to a message not in the archiveBy slightly changing 32bit mode die_nmi() we may unify the interface and make it common for both (32/64bit) modes
---
arch/x86/kernel/nmi_32.c +3 -4
@@ -320,8 +320,6 @@ void touch_nmi_watchdog(void) }EXPORT_SYMBOL (touch_nmi_watchdog); -extern void die_nmi (struct pt_regs *,const char *msg); - notrace __kprobes intnmi_watchdog_tick (struct pt_regs *regs,unsigned reason) {
@@ -375,7 +373,8 @@ nmi_watchdog_tick(struct pt_regs *regs,/* * die_nmi will return ONLY if NOTIFY_STOP happens.. */ -die_nmi (regs,"BUG: NMI Watchdog detected LOCKUP" ); +die_nmi ("BUG: NMI Watchdog detected LOCKUP" , + regs,0 ); }else {__get_cpu_var (last_irq_sum) = sum;local_set (&__get_cpu_var (alert_counter),0 );
@@ -406,7 +405,7 @@ static int unknown_nmi_panic_callback(stchar buf[64 ];sprintf (buf,"NMI received for unknown reason %02x\n" , reason); -die_nmi (regs, buf); +die_nmi (buf, regs,0 );return 0 ; }
arch/x86/kernel/traps_32.c +5 -3
@@ -755,9 +755,9 @@ unknown_nmi_error(unsigned char reason,static DEFINE_SPINLOCK (nmi_print_lock); -void notrace __kprobes die_nmi(struct pt_regs *regs, const char *msg) +void notrace __kprobes die_nmi(char *str, struct pt_regs *regs, int do_panic) { -if (notify_die (DIE_NMIWATCHDOG , msg, regs,0 ,2 ,SIGINT ) ==NOTIFY_STOP ) +if (notify_die (DIE_NMIWATCHDOG , str, regs,0 ,2 ,SIGINT ) ==NOTIFY_STOP )return ;spin_lock (&nmi_print_lock);
@@ -766,10 +766,12 @@ void notrace __kprobes die_nmi(struct pt* to get a message out: */ bust_spinlocks (1 ); -printk (KERN_EMERG "%s" , msg); +printk (KERN_EMERG "%s" , str);printk (" on CPU%d, ip %08lx, registers:\n" ,smp_processor_id (), regs->ip);show_registers (regs); +if (do_panic) +panic ("Non maskable interrupt" );console_silent ();spin_unlock (&nmi_print_lock);bust_spinlocks (0 );
include/asm-x86/nmi.h +1 -1
@@ -38,12 +38,12 @@ static inline void unset_nmi_pm_callback#ifdef CONFIG_X86_64 extern void default_do_nmi (struct pt_regs *); -extern void die_nmi (char *str,struct pt_regs *regs,int do_panic);extern void nmi_watchdog_default (void );#else #define nmi_watchdog_default () do {} while (0)#endif +extern void die_nmi (char *str,struct pt_regs *regs,int do_panic);extern int check_nmi_watchdog (void );extern int nmi_watchdog_enabled;extern int unknown_nmi_panic;
PATCH 2/11 x86: nmi - die_nmi() output message unification
In reply to a message not in the archiveMake 64bit die_nmi() to produce the same message as 32bit mode has
---
arch/x86/kernel/nmi_64.c +2 -2
@@ -358,8 +358,8 @@ nmi_watchdog_tick(struct pt_regs *regs,*/ local_inc (&__get_cpu_var (alert_counter));if (local_read (&__get_cpu_var (alert_counter)) ==5 *nmi_hz) -die_nmi ("NMI Watchdog detected LOCKUP on CPU %d\n" , regs, - panic_on_timeout); +die_nmi ("NMI Watchdog detected LOCKUP" , + regs, panic_on_timeout); }else {__get_cpu_var (last_irq_sum) = sum;local_set (&__get_cpu_var (alert_counter),0 );
arch/x86/kernel/traps_64.c +3 -1
@@ -614,7 +614,9 @@ die_nmi(char *str, struct pt_regs *regs,* We are in trouble anyway, lets at least try * to get a message out. */ -printk (str,smp_processor_id ()); +printk (KERN_EMERG "%s" , str); +printk (" on CPU%d, ip %08lx, registers:\n" , +smp_processor_id (), regs->ip);show_registers (regs);if (kexec_should_crash (current))crash_kexec (regs);
PATCH 3/11 x86: nmi_64.c - move do_nmi(), stop_nmi() and restart_nmi() implementation to traps_64.c
In reply to a message not in the archivetraps_32.c already holds these functions so do the same for traps_64.c
---
arch/x86/kernel/nmi_64.c +0 -25
@@ -30,7 +30,6 @@int unknown_nmi_panic;int nmi_watchdog_enabled; -int panic_on_unrecovered_nmi;static cpumask_t backtrace_mask =CPU_MASK_NONE ;
@@ -383,30 +382,6 @@ nmi_watchdog_tick(struct pt_regs *regs,return rc; } -static unsigned ignore_nmis; - -asmlinkage notrace __kprobes void -do_nmi (struct pt_regs *regs,long error_code) -{ -nmi_enter (); -add_pda (__nmi_count,1 ); -if (!ignore_nmis) -default_do_nmi (regs); -nmi_exit (); -} - -void stop_nmi (void ) -{ -acpi_nmi_disable (); - ignore_nmis++; -} - -void restart_nmi (void ) -{ - ignore_nmis--; -acpi_nmi_enable (); -} -#ifdef CONFIG_SYSCTL static int unknown_nmi_panic_callback (struct pt_regs *regs,int cpu)
arch/x86/kernel/traps_64.c +24 -0
@@ -76,7 +76,9 @@ asmlinkage void alignment_check(void); asmlinkage void machine_check(void); asmlinkage void spurious_interrupt_bug(void); +int panic_on_unrecovered_nmi;static unsigned int code_bytes =64 ; +static unsigned ignore_nmis;static inline void conditional_sti (struct pt_regs *regs) {
@@ -867,6 +869,28 @@ asmlinkage notrace __kprobes void defauio_check_error (reason, regs); } +asmlinkage notrace __kprobes void +do_nmi (struct pt_regs *regs,long error_code) +{ +nmi_enter (); +add_pda (__nmi_count,1 ); +if (!ignore_nmis) +default_do_nmi (regs); +nmi_exit (); +} + +void stop_nmi (void ) +{ +acpi_nmi_disable (); + ignore_nmis++; +} + +void restart_nmi (void ) +{ + ignore_nmis--; +acpi_nmi_enable (); +} +/* runs on IST stack. */ asmlinkage void __kprobes do_int3(struct pt_regs * regs, long error_code) {
PATCH 5/11 x86: nmi_32.c - add nmi_watchdog_default helper
In reply to a message not in the archive---
arch/x86/kernel/nmi_32.c +13 -6
@@ -51,6 +51,17 @@ static DEFINE_PER_CPU(short, wd_enabled)static int endflag__initdata =0 ; +/* Run after command line and cpu_init init, but before all other checks */ +void nmi_watchdog_default (void ) +{ +if (nmi_watchdog !=NMI_DEFAULT ) +return ; +if (lapic_watchdog_ok ()) + nmi_watchdog =NMI_LOCAL_APIC ; +else + nmi_watchdog =NMI_IO_APIC ; +} +#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
@@ -437,12 +448,8 @@ int proc_nmi_enabled(struct ctl_table *treturn -EIO ; } -if (nmi_watchdog ==NMI_DEFAULT ) { -if (lapic_watchdog_ok ()) - nmi_watchdog =NMI_LOCAL_APIC ; -else - nmi_watchdog =NMI_IO_APIC ; - } +/* if nmi_watchdog is not set yet, then set it */ +nmi_watchdog_default ();if (nmi_watchdog ==NMI_LOCAL_APIC ) {if (nmi_watchdog_enabled)
include/asm-x86/nmi.h +1 -3
@@ -38,11 +38,9 @@ static inline void unset_nmi_pm_callback#ifdef CONFIG_X86_64 extern void default_do_nmi (struct pt_regs *); -extern void nmi_watchdog_default (void ); -#else -#define nmi_watchdog_default () do {} while (0)#endif +extern void nmi_watchdog_default (void );extern void die_nmi (char *str,struct pt_regs *regs,int do_panic);extern int check_nmi_watchdog (void );extern int nmi_watchdog_enabled;
PATCH 4/11 x86: nmi_32.c - add "panic" option
In reply to a message not in the archiveAllow to pass "panic" option in 32bit mode
---
arch/x86/kernel/nmi_32.c +10 -1
@@ -42,6 +42,7 @@ static cpumask_t backtrace_mask = CPU_MA* 0: the lapic NMI watchdog is disabled, but can be enabled */ atomic_t nmi_active =ATOMIC_INIT (0 );/* oprofile uses this */ +static int panic_on_timeout;unsigned int nmi_watchdog =NMI_DEFAULT ;static unsigned int nmi_hz =HZ ;
@@ -140,6 +141,14 @@ static int __init setup_nmi_watchdog(cha {int nmi; +if (!strncmp (str,"panic" ,5 )) { + panic_on_timeout =1 ; + str =strchr (str,',' ); +if (!str) +return 1 ; + ++str; + } +get_option (&str, &nmi);if ((nmi >=NMI_INVALID ) || (nmi <NMI_NONE ))
@@ -374,7 +383,7 @@ nmi_watchdog_tick(struct pt_regs *regs,* die_nmi will return ONLY if NOTIFY_STOP happens.. */ die_nmi ("BUG: NMI Watchdog detected LOCKUP" , - regs,0 ); + regs, panic_on_timeout); }else {__get_cpu_var (last_irq_sum) = sum;local_set (&__get_cpu_var (alert_counter),0 );
PATCH 6/11 x86: nmi_32/64.c - use apic_write_around instead of apic_write
In reply to a message not in the archiveapic_write_around will be expanded to apic_write in 64bit mode anyway. Only a few CPUs (well, old CPUs to be precise) requires such an action. In general it should not hurt and could be cleaned up for apic_write (just in case)
---
arch/x86/kernel/nmi_32.c +1 -1
@@ -248,7 +248,7 @@ void acpi_nmi_enable(void)static void __acpi_nmi_disable (void *__unused) { -apic_write (APIC_LVT0 ,APIC_DM_NMI |APIC_LVT_MASKED ); +apic_write_around (APIC_LVT0 ,APIC_DM_NMI |APIC_LVT_MASKED ); }/*
arch/x86/kernel/nmi_64.c +2 -2
@@ -215,7 +215,7 @@ late_initcall(init_lapic_nmi_sysfs);static void __acpi_nmi_enable (void *__unused) { -apic_write (APIC_LVT0 ,APIC_DM_NMI ); +apic_write_around (APIC_LVT0 ,APIC_DM_NMI ); }/*
@@ -229,7 +229,7 @@ void acpi_nmi_enable(void)static void __acpi_nmi_disable (void *__unused) { -apic_write (APIC_LVT0 ,APIC_DM_NMI |APIC_LVT_MASKED ); +apic_write_around (APIC_LVT0 ,APIC_DM_NMI |APIC_LVT_MASKED ); }/*
PATCH 8/11 x86: nmi_64.c - use for_each_possible_cpu helper instead of for statement
In reply to a message not in the archive---
arch/x86/kernel/nmi_64.c +1 -1
@@ -98,7 +98,7 @@ int __init check_nmi_watchdog(void)smp_call_function (nmi_cpu_busy, (void *)&endflag,0 ,0 );#endif -for (cpu =0 ; cpu <NR_CPUS ; cpu++) +for_each_possible_cpu (cpu) prev_nmi_count[cpu] =cpu_pda (cpu)->__nmi_count;local_irq_enable ();mdelay ((20 *1000 )/nmi_hz);// wait 20 ticks
PATCH 9/11 x86: nmi_32.c cleanup - use for_each_online_cpu helper
In reply to a message not in the archiveSince cpu_online_map is touched (by for_each_online_cpu) at moment when cpu_callin_map is already filled up we can get rid of its checking at all
---
arch/x86/kernel/nmi_32.c +1 -7
@@ -108,13 +108,7 @@ int __init check_nmi_watchdog(void)local_irq_enable ();mdelay ((20 *1000 )/nmi_hz);// wait 20 ticks -for_each_possible_cpu (cpu) { -#ifdef CONFIG_SMP -/* Check cpu_callin_map here because that is set -after the timer is started. */ -if (!cpu_isset (cpu, cpu_callin_map)) -continue ; -#endif +for_each_online_cpu (cpu) {if (!per_cpu (wd_enabled, cpu))continue ;if (nmi_count (cpu) - prev_nmi_count[cpu] <=5 ) {
PATCH 7/11 x86: nmi_32.c - unknown_nmi_panic_callback should always panic on being called
In reply to a message not in the archive---
arch/x86/kernel/nmi_32.c +1 -1
@@ -425,7 +425,7 @@ static int unknown_nmi_panic_callback(stchar buf[64 ];sprintf (buf,"NMI received for unknown reason %02x\n" , reason); -die_nmi (buf, regs,0 ); +die_nmi (buf, regs,1 );/* Always panic here */ return 0 ; }
PATCH 10/11 x86: nmi_32/64.c - add helper functions to hide mode-specific data
In reply to a message not in the archive---
arch/x86/kernel/nmi_32.c +32 -12
@@ -51,6 +51,26 @@ static DEFINE_PER_CPU(short, wd_enabled)static int endflag__initdata =0 ; +static inline unsigned int get_nmi_count (int cpu) +{ +return nmi_count (cpu); +} + +static inline int mce_in_progress (void ) +{ +return 0 ; +} + +/* +* Take the local apic timer and PIT/HPET into account. We don't +* know which one is active, when we have highres/dyntick on +*/ +static inline unsigned int get_timer_irqs (int cpu) +{ +return per_cpu (irq_stat, cpu).apic_timer_irqs + +per_cpu (irq_stat, cpu).irq0_irqs; +} +/* Run after command line and cpu_init init, but before all other checks */ void nmi_watchdog_default (void ) {
@@ -104,19 +124,19 @@ int __init check_nmi_watchdog(void)#endif for_each_possible_cpu (cpu) - prev_nmi_count[cpu] =nmi_count (cpu); + prev_nmi_count[cpu] =get_nmi_count (cpu);local_irq_enable ();mdelay ((20 *1000 )/nmi_hz);// wait 20 ticks for_each_online_cpu (cpu) {if (!per_cpu (wd_enabled, cpu))continue ; -if (nmi_count (cpu) - prev_nmi_count[cpu] <=5 ) { +if (get_nmi_count (cpu) - prev_nmi_count[cpu] <=5 ) {printk (KERN_WARNING "WARNING: CPU#%d: NMI " "appears to be stuck (%d->%d)!\n" , cpu, prev_nmi_count[cpu], -nmi_count (cpu)); +get_nmi_count (cpu));per_cpu (wd_enabled, cpu) =0 ;atomic_dec (&nmi_active); }
@@ -355,6 +375,13 @@ nmi_watchdog_tick(struct pt_regs *regs, touched =1 ; } + sum =get_timer_irqs (cpu); + +if (__get_cpu_var (nmi_touch)) { +__get_cpu_var (nmi_touch) =0 ; + touched =1 ; + } +if (cpu_isset (cpu, backtrace_mask)) {static DEFINE_SPINLOCK (lock);/* Serialise the printks */
@@ -365,16 +392,9 @@ nmi_watchdog_tick(struct pt_regs *regs,cpu_clear (cpu, backtrace_mask); } -/* -* Take the local apic timer and PIT/HPET into account. We don't -* know which one is active, when we have highres/dyntick on -*/ - sum =per_cpu (irq_stat, cpu).apic_timer_irqs + -per_cpu (irq_stat, cpu).irq0_irqs; -if (__get_cpu_var (nmi_touch)) { -__get_cpu_var (nmi_touch) =0 ; +/* Could check oops_in_progress here too, but it's safer not to */ +if (mce_in_progress ()) touched =1 ; - }/* if the none of the timers isn't firing, this cpu isn't doing much */ if (!touched &&__get_cpu_var (last_irq_sum) == sum) {
arch/x86/kernel/nmi_64.c +31 -11
83 lines not on this page · raw message
PATCH 11/11 x86: nmi_32/64.c - merge down nmi_32.c and nmi_64.c to nmi.c
In reply to a message not in the archive---
arch/x86/kernel/nmi.c new file +532 -0
@@ -0,0 +1,532 @@ +/* +* NMI watchdog support on APIC systems +* +* Started by Ingo Molnar <[email protected]> +* +* Fixes: +* Mikael Pettersson : AMD K7 support for local APIC NMI watchdog. +* Mikael Pettersson : Power Management for local APIC NMI watchdog. +* Mikael Pettersson : Pentium 4 support for local APIC NMI watchdog. +* Pavel Machek and +* 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/sysctl.h> +#include <linux/percpu.h> +#include <linux/kprobes.h> +#include <linux/cpumask.h> +#include <linux/kernel_stat.h> +#include <linux/kdebug.h> + +#include <asm/smp.h> +#include <asm/nmi.h> +#include <asm/proto.h> +#include <asm/timer.h> + +#include <asm/mce.h> + +#include <mach_traps.h> + +int unknown_nmi_panic; +int nmi_watchdog_enabled; + +static cpumask_t backtrace_mask =CPU_MASK_NONE ; + +/* nmi_active: +* >0: the lapic NMI watchdog is active, but can be disabled +* <0: the lapic NMI watchdog has not been set up, and cannot +* be enabled +* 0: the lapic NMI watchdog is disabled, but can be enabled +*/ +atomic_t nmi_active =ATOMIC_INIT (0 );/* oprofile uses this */ +static int panic_on_timeout; + +unsigned int nmi_watchdog =NMI_DEFAULT ; + +static unsigned int nmi_hz =HZ ; +static DEFINE_PER_CPU(short, wd_enabled); +static int endflag__initdata =0 ; + +static inline unsigned int get_nmi_count (int cpu) +{ +#ifdef CONFIG_X86_64 +return cpu_pda (cpu)->__nmi_count; +#else +return nmi_count (cpu); +#endif +} + +static inline int mce_in_progress (void ) +{ +#if defined(CONFIX_X86_64 ) && defined(CONFIG_X86_MCE ) +return atomic_read (&mce_entry) >0 ; +#endif +return 0 ; +} + +/* +* Take the local apic timer and PIT/HPET into account. We don't +* know which one is active, when we have highres/dyntick on +*/ +static inline unsigned int get_timer_irqs (int cpu) +{ +#ifdef CONFIG_X86_64 +return read_pda (apic_timer_irqs) +read_pda (irq0_irqs); +#else +return per_cpu (irq_stat, cpu).apic_timer_irqs + +per_cpu (irq_stat, cpu).irq0_irqs; +#endif +} + +/* Run after command line and cpu_init init, but before all other checks */ +void nmi_watchdog_default (void ) +{ +if (nmi_watchdog !=NMI_DEFAULT ) +return ; +#ifdef CONFIG_X86_64 + nmi_watchdog =NMI_NONE ; +#else +if (lapic_watchdog_ok ()) + nmi_watchdog =NMI_LOCAL_APIC ; +else + nmi_watchdog =NMI_IO_APIC ; +#endif +} + +#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 +* CPUs during the test make them busy. +*/ +static __init void nmi_cpu_busy (void *data) +{ +local_irq_enable_in_hardirq (); +/* +* Intentionally don't use cpu_relax here. This is +* to make sure that the performance counter really ticks, +* even if there is a simulator or similar that catches the +* 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 ) +mb (); +} +#endif + +int __init check_nmi_watchdog (void ) +{ +unsigned int *prev_nmi_count; +int cpu; + +if (nmi_watchdog ==NMI_NONE || nmi_watchdog ==NMI_DISABLED ) +return 0 ; + +if (!atomic_read (&nmi_active)) +return 0 ; + + prev_nmi_count =kmalloc (NR_CPUS *sizeof (int ),GFP_KERNEL ); +if (!prev_nmi_count) + goto error; + +printk (KERN_INFO "Testing NMI watchdog ... " ); + +#ifdef CONFIG_SMP +if (nmi_watchdog ==NMI_LOCAL_APIC ) +smp_call_function (nmi_cpu_busy, (void *)&endflag,0 ,0 ); +#endif + +for_each_possible_cpu (cpu) + prev_nmi_count[cpu] =get_nmi_count (cpu); +local_irq_enable (); +mdelay ((20 *1000 ) / nmi_hz);/* wait 20 ticks */ + +for_each_online_cpu (cpu) { +if (!per_cpu (wd_enabled, cpu)) +continue ; +if (get_nmi_count (cpu) - prev_nmi_count[cpu] <=5 ) { +printk (KERN_WARNING "WARNING: CPU#%d: NMI " +"appears to be stuck (%d->%d)!\n" , + cpu, + prev_nmi_count[cpu], +get_nmi_count (cpu)); +per_cpu (wd_enabled, cpu) =0 ; +atomic_dec (&nmi_active); + } + } + endflag =1 ; +if (!atomic_read (&nmi_active)) { +kfree (prev_nmi_count); +atomic_set (&nmi_active,-1 ); + goto error; + } +printk ("OK.\n" ); + +/* +* now that we know it works we can reduce NMI frequency to +* something more reasonable; makes a difference in some configs +*/ +if (nmi_watchdog ==NMI_LOCAL_APIC ) + nmi_hz =lapic_adjust_nmi_hz (1 ); + +kfree (prev_nmi_count); +return 0 ; + +error: +#ifdef CONFIG_X86_32 + timer_ack = !cpu_has_tsc; +#endif +return -1 ; +} + +static int __init setup_nmi_watchdog (char *str) +{ +int nmi; + +if (!strncmp (str,"panic" ,5 )) { + panic_on_timeout =1 ; + str =strchr (str,',' ); +if (!str) +return 1 ; + ++str; + } + +get_option (&str, &nmi); + +if (nmi >=NMI_INVALID || nmi <NMI_NONE ) +return 0 ; + + nmi_watchdog = nmi; +return 1 ; +} +__setup ("nmi_watchdog=" , setup_nmi_watchdog); + +/* +* Suspend/resume support +*/ +#ifdef CONFIG_PM + +static int nmi_pm_active;/* nmi_active before suspend */ + +static int lapic_nmi_suspend (struct sys_device *dev,pm_message_t state) +{ +/* only CPU0 goes here, other CPUs should be offline */ + nmi_pm_active =atomic_read (&nmi_active); +stop_apic_nmi_watchdog (NULL ); +BUG_ON (atomic_read (&nmi_active) !=0 ); +return 0 ; +} + +static int lapic_nmi_resume (struct sys_device *dev) +{ +/* only CPU0 goes here, other CPUs should be offline */ +if (nmi_pm_active >0 ) { +setup_apic_nmi_watchdog (NULL ); +touch_nmi_watchdog (); + } +return 0 ; +} + +static struct sysdev_class nmi_sysclass = { + .name ="lapic_nmi" , + .resume = lapic_nmi_resume, + .suspend = lapic_nmi_suspend, +}; + +static struct sys_device device_lapic_nmi = { + .id =0 , + .cls = &nmi_sysclass, +}; + +static int __init init_lapic_nmi_sysfs (void ) +{ +int error; + +/* +* should really be a BUG_ON but b/c this is an +* init call, it just doesn't work. -dcz +*/ +if (nmi_watchdog !=NMI_LOCAL_APIC ) +return 0 ; + +if (atomic_read (&nmi_active) <0 ) +return 0 ; + + error =sysdev_class_register (&nmi_sysclass); +if (!error) + error =sysdev_register (&device_lapic_nmi); +return error; +} + +/* must come after the local APIC's device_initcall() */ +late_initcall (init_lapic_nmi_sysfs); + +#endif /* CONFIG_PM */ + +static void __acpi_nmi_enable (void *__unused) +{ +apic_write_around (APIC_LVT0 ,APIC_DM_NMI ); +} + +/* +* Enable timer based NMIs on all CPUs: +*/ +void acpi_nmi_enable (void ) +{ +if (atomic_read (&nmi_active) && nmi_watchdog ==NMI_IO_APIC ) +on_each_cpu (__acpi_nmi_enable,NULL ,0 ,1 ); +} + +static void __acpi_nmi_disable (void *__unused) +{ +apic_write_around (APIC_LVT0 ,APIC_DM_NMI |APIC_LVT_MASKED ); +} + +/* +* Disable timer based NMIs on all CPUs: +*/ +void acpi_nmi_disable (void ) +{ +if (atomic_read (&nmi_active) && nmi_watchdog ==NMI_IO_APIC ) +on_each_cpu (__acpi_nmi_disable,NULL ,0 ,1 ); +} + +void setup_apic_nmi_watchdog (void *unused) +{ +if (__get_cpu_var (wd_enabled)) +return ; + +/* cheap hack to support suspend/resume */ +/* if cpu0 is not active neither should the other cpus */ +if (smp_processor_id () !=0 &&atomic_read (&nmi_active) <=0 ) +return ; + +switch (nmi_watchdog) { +case NMI_LOCAL_APIC : +__get_cpu_var (wd_enabled) =1 ;/* enable it before to avoid race with handler */ +if (lapic_watchdog_init (nmi_hz) <0 ) { +__get_cpu_var (wd_enabled) =0 ; +return ; + } +/* FALL THROUGH */ +case NMI_IO_APIC : +__get_cpu_var (wd_enabled) =1 ; +atomic_inc (&nmi_active); + } +} + +void stop_apic_nmi_watchdog (void *unused) +{ +/* only support LOCAL and IO APICs for now */ +if (nmi_watchdog !=NMI_LOCAL_APIC && + nmi_watchdog !=NMI_IO_APIC ) +return ; +if (__get_cpu_var (wd_enabled) ==0 ) +return ; +if (nmi_watchdog ==NMI_LOCAL_APIC ) +lapic_watchdog_stop (); +__get_cpu_var (wd_enabled) =0 ; +atomic_dec (&nmi_active); +} + +/* +* the best way to detect whether a CPU has a 'hard lockup' problem +* is to check it's local APIC timer IRQ counts. If they are not +* changing then that CPU has some problem. +* +* as these watchdog NMI IRQs are generated on every CPU, we only +* have to check the current processor. +* +* since NMIs don't listen to _any_ locks, we have to be extremely +* careful not to rely on unsafe variables. The printk might lock +* up though, so we have to break up any console locks first ... +* [when there will be more tty-related locks, break them up here too!] +*/ + +static DEFINE_PER_CPU(unsigned, last_irq_sum); +static DEFINE_PER_CPU(local_t, alert_counter); +static DEFINE_PER_CPU(int, nmi_touch); + +void touch_nmi_watchdog (void ) +{ +if (nmi_watchdog >0 ) { +unsigned cpu; + +/* +* Tell other CPUs to reset their alert counters. We cannot +* do it ourselves because the alert count increase is not +* atomic. +*/ +for_each_present_cpu (cpu) { +if (per_cpu (nmi_touch, cpu) !=1 ) +per_cpu (nmi_touch, cpu) =1 ; + } + } + +/* +* Tickle the softlockup detector too: +*/ +touch_softlockup_watchdog (); +} +EXPORT_SYMBOL (touch_nmi_watchdog); + +notrace __kprobes int +nmi_watchdog_tick (struct pt_regs *regs,unsigned reason) +{ +/* +* Since current_thread_info()-> is always on the stack, and we +* always switch the stack NMI-atomically, it's safe to use +* smp_processor_id(). +*/ +unsigned int sum; +int touched =0 ; +int cpu =smp_processor_id (); +int rc =0 ; + +/* check for other users first */ +if (notify_die (DIE_NMI ,"nmi" , regs, reason,2 ,SIGINT ) + ==NOTIFY_STOP ) { + rc =1 ; + touched =1 ; + } + + sum =get_timer_irqs (cpu); + +if (__get_cpu_var (nmi_touch)) { +__get_cpu_var (nmi_touch) =0 ; + 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); + } + +/* Could check oops_in_progress here too, but it's safer not to */ +if (mce_in_progress ()) + touched =1 ; + +/* if the none of the timers isn't firing, this cpu isn't doing much */ +if (!touched &&__get_cpu_var (last_irq_sum) == sum) { +/* +* Ayiee, looks like this CPU is stuck ... +* wait a few IRQs (5 seconds) before doing the oops ... +*/ +local_inc (&__get_cpu_var (alert_counter)); +if (local_read (&__get_cpu_var (alert_counter)) ==5 * nmi_hz) +/* +* die_nmi will return ONLY if NOTIFY_STOP happens.. +*/ +die_nmi ("BUG: NMI Watchdog detected LOCKUP" , + regs, panic_on_timeout); + }else { +__get_cpu_var (last_irq_sum) = sum; +local_set (&__get_cpu_var (alert_counter),0 ); + } + +/* see if the nmi watchdog went off */ +if (!__get_cpu_var (wd_enabled)) +return rc; +switch (nmi_watchdog) { +case NMI_LOCAL_APIC : + rc |=lapic_wd_event (nmi_hz); +break ; +case NMI_IO_APIC : +/* +* don't know how to accurately check for this. +* just assume it was a watchdog timer interrupt +* This matches the old behaviour. +*/ + rc =1 ; +break ; + } +return rc; +} + +#ifdef CONFIG_SYSCTL + +static int unknown_nmi_panic_callback (struct pt_regs *regs,int cpu) +{ +unsigned char reason =get_nmi_reason (); +char buf[64 ]; + +sprintf (buf,"NMI received for unknown reason %02x\n" , reason); +die_nmi (buf, regs,1 );/* Always panic here */ +return 0 ; +} + +/* +* proc handler for /proc/sys/kernel/nmi +*/ +int proc_nmi_enabled (struct ctl_table *table,int write,struct file *file, +void __user *buffer,size_t *length,loff_t *ppos) +{ +int old_state; + + nmi_watchdog_enabled = (atomic_read (&nmi_active) >0 ) ?1 :0 ; + old_state = nmi_watchdog_enabled; +proc_dointvec (table, write, file, buffer, length, ppos); +if (!!old_state == !!nmi_watchdog_enabled) +return 0 ; + +if (atomic_read (&nmi_active) <0 || nmi_watchdog ==NMI_DISABLED ) { +printk (KERN_WARNING +"NMI watchdog is permanently disabled\n" ); +return -EIO ; + } + +/* if nmi_watchdog is not set yet, then set it */ +nmi_watchdog_default (); + +if (nmi_watchdog ==NMI_LOCAL_APIC ) { +if (nmi_watchdog_enabled) +enable_lapic_nmi_watchdog (); +else +disable_lapic_nmi_watchdog (); + }else { +printk (KERN_WARNING +"NMI watchdog doesn't know what hardware to touch\n" ); +return -EIO ; + } +return 0 ; +} + +#endif /* CONFIG_SYSCTL */ + +int do_nmi_callback (struct pt_regs *regs,int cpu) +{ +#ifdef CONFIG_SYSCTL +if (unknown_nmi_panic) +return unknown_nmi_panic_callback (regs, cpu); +#endif +return 0 ; +} + +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); +
arch/x86/kernel/nmi_32.c deleted +0 -506
506 lines not on this page · raw message
arch/x86/kernel/nmi_64.c deleted +0 -477
477 lines not on this page · raw message
arch/x86/kernel/Makefile +1 -1
@@ -53,7 +53,7 @@ obj-$(CONFIG_X86_32_SMP) += smpcommon.o obj-$(CONFIG_X86_64_SMP) += tsc_sync.o smpcommon.o obj-$(CONFIG_X86_TRAMPOLINE) += trampoline_$(BITS).o obj-$(CONFIG_X86_MPPARSE) += mpparse.o -obj-$(CONFIG_X86_LOCAL_APIC) += apic_$(BITS).o nmi_$(BITS).o +obj-$(CONFIG_X86_LOCAL_APIC) += apic_$(BITS).o nmi.o obj-$(CONFIG_X86_IO_APIC) += io_apic_$(BITS).o obj-$(CONFIG_X86_REBOOTFIXUPS) += reboot_fixups_32.o obj-$(CONFIG_KEXEC) += machine_kexec_$(BITS).o
Re: [patch 06/11] x86: nmi_32/64.c - use apic_write_around instead of apic_write
In reply to Cyrill GorcunovQuoted 7 lines · Cyrill Gorcunov
apic_write_aroundwill be expanded toapic_writein 64bit mode anyway. Only a few CPUs (well, old CPUs to be precise) requires such an action. In general it should not hurt and could be cleaned up forapic_write(just in case)Signed-off-by Cyrill Gorcunov <[email protected]>---
Almost all local APIC write accesses must use apic_write_around(). The
notable exceptions are appropriately guarded accesses to the ESR (there is no need ever to write to this register on Pentium processors and a read of the register has side-effects) and pieces of code known never to run on original Pentium processors.
Maciej
Re: [patch 06/11] x86: nmi_32/64.c - use apic_write_around instead of apic_write
[Maciej W. Rozycki - Wed, May 28, 2008 at 04:35:14PM +0100]
| On Sat, 24 May 2008, Cyrill Gorcunov wrote: | | > apic_write_around will be expanded to apic_write in 64bit mode | > anyway. Only a few CPUs (well, old CPUs to be precise) requires | > such an action. In general it should not hurt and could be cleaned | > up for apic_write (just in case) | > | > Signed-off-by: Cyrill Gorcunov <[email protected]> | > --- | | Acked-by: Maciej W. Rozycki <[email protected]> | | Almost all local APIC write accesses must use apic_write_around(). The | notable exceptions are appropriately guarded accesses to the ESR (there is | no need ever to write to this register on Pentium processors and a read of | the register has side-effects) and pieces of code known never to run on | original Pentium processors. | | Maciej |
Thanks Maciej!
Could you take a look please on
http://lkml.org/lkml/2008/5/26/146
i'm investigateting what is happening (Adrian pointed on main reason I think) but can't understand why is that.
- Cyrill -
Re: [patch 06/11] x86: nmi_32/64.c - use apic_write_around instead of apic_write
Quoted 6 lines · Cyrill Gorcunov
Could you take a look please on
http://lkml.org/lkml/2008/5/26/146
i'm investigateting what is happening (Adrian pointed on main reason I think) but can't understand why is that.
Do you have a link with the patch included?
Maciej
Re: [patch 06/11] x86: nmi_32/64.c - use apic_write_around instead of apic_write
[Maciej W. Rozycki - Wed, May 28, 2008 at 05:13:08PM +0100]
| On Wed, 28 May 2008, Cyrill Gorcunov wrote: | | > Could you take a look please on | > | > http://lkml.org/lkml/2008/5/26/146 | > | > i'm investigateting what is happening (Adrian pointed on | > main reason I think) but can't understand why is that. | | Do you have a link with the patch included? | | Maciej |
Here is the patch itself:
---
x86: nmi_32.c - add nmi_watchdog_default helper
---
arch/x86/kernel/nmi_32.c +13 -6
@@ -51,6 +51,17 @@ static DEFINE_PER_CPU(short, wd_enabled)static int endflag__initdata =0 ; +/* Run after command line and cpu_init init, but before all other checks */ +void nmi_watchdog_default (void ) +{ +if (nmi_watchdog !=NMI_DEFAULT ) +return ; +if (lapic_watchdog_ok ()) + nmi_watchdog =NMI_LOCAL_APIC ; +else + nmi_watchdog =NMI_IO_APIC ; +} +#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
@@ -437,12 +448,8 @@ int proc_nmi_enabled(struct ctl_table *treturn -EIO ; } -if (nmi_watchdog ==NMI_DEFAULT ) { -if (lapic_watchdog_ok ()) - nmi_watchdog =NMI_LOCAL_APIC ; -else - nmi_watchdog =NMI_IO_APIC ; - } +/* if nmi_watchdog is not set yet, then set it */ +nmi_watchdog_default ();if (nmi_watchdog ==NMI_LOCAL_APIC ) {if (nmi_watchdog_enabled)
include/asm-x86/nmi.h +1 -3
@@ -38,11 +38,9 @@ static inline void unset_nmi_pm_callback#ifdef CONFIG_X86_64 extern void default_do_nmi (struct pt_regs *); -extern void nmi_watchdog_default (void ); -#else -#define nmi_watchdog_default () do {} while (0)#endif +extern void nmi_watchdog_default (void );extern void die_nmi (char *str,struct pt_regs *regs,int do_panic);extern int check_nmi_watchdog (void );extern int nmi_watchdog_enabled;
---
So I've moved a part of code (32bit) from proc_nmi_enabled() to nmi_watchdog_default() BUT this nmi_watchdog_default() also called from smpboot.c:native_smp_prepare_cpus() and before this patch this call was just an empty call (and eliminated by gcc I think) now it's not empy. But how it leads to hang I can't understand. The only thing is done - nmi_watchdog is set to NMI_LOCAL_APIC or NMI_IO_APIC and my only suspicious is that something happens asynchronously and leads to machine hang. Let me know if I wrote in obscure manner.
- Cyrill -
Re: [patch 06/11] x86: nmi_32/64.c - use apic_write_around instead of apic_write
Quoted 9 lines · Cyrill Gorcunov
So I've moved a part of code (32bit) from
proc_nmi_enabled()tonmi_watchdog_default()BUT thisnmi_watchdog_default()also called from smpboot.c:native_smp_prepare_cpus() and before this patch this call was just an empty call (and eliminated by gcc I think) now it's not empy. But how it leads to hang I can't understand. The only thing is done -nmi_watchdogis set toNMI_LOCAL_APICorNMI_IO_APICand my only suspicious is that something happens asynchronously and leads to machine hang. Let me know if I wrote in obscure manner.
The problem is the I/O APIC NMI watchdog which does not work correctly on
the reporter's machine. Your patch makes the NMI watchdog be enabled by default on 32-bit platforms (OTOH, proc_nmi_enabled() is only called by an explicit request via sysctl). Unfortunately many hardware vendors have not fully recognised the existence of Linux yet and the NMI watchdog frequently triggers breakage in SMM firmware even on modern equipment; actually it was less likely to break in the old days.
Maciej
Re: [patch 06/11] x86: nmi_32/64.c - use apic_write_around instead of apic_write
[Maciej W. Rozycki - Wed, May 28, 2008 at 06:09:38PM +0100]
| On Wed, 28 May 2008, Cyrill Gorcunov wrote: | | > So I've moved a part of code (32bit) from proc_nmi_enabled() to | > nmi_watchdog_default() BUT this nmi_watchdog_default() also called | > from smpboot.c:native_smp_prepare_cpus() and before this patch | > this call was just an empty call (and eliminated by gcc I think) | > now it's not empy. But how it leads to hang I can't understand. | > The only thing is done - nmi_watchdog is set to NMI_LOCAL_APIC | > or NMI_IO_APIC and my only suspicious is that something happens | > asynchronously and leads to machine hang. Let me know if I wrote | > in obscure manner. | | The problem is the I/O APIC NMI watchdog which does not work correctly on | the reporter's machine. Your patch makes the NMI watchdog be enabled by | default on 32-bit platforms (OTOH, proc_nmi_enabled() is only called by an | explicit request via sysctl). Unfortunately many hardware vendors have | not fully recognised the existence of Linux yet and the NMI watchdog | frequently triggers breakage in SMM firmware even on modern equipment; | actually it was less likely to break in the old days. | | Maciej |
Thanks a lot, Maciej!!! Could you please explain me how did you find that? 'cause reporter said that with nmi_watchdog=2 it works and with nmi_watchdog=1 it stalls? Maybe I should better make this function the same as 64bit version has? I.e. set nmi_watchdog = NMI_NONE by default?
- Cyrill -
Re: [patch 06/11] x86: nmi_32/64.c - use apic_write_around instead of apic_write
Quoted 4 lines · Cyrill Gorcunov
Thanks a lot, Maciej!!! Could you please explain me how did you find that? 'cause reporter said that with nmi_watchdog=2 it works and with nmi_watchdog=1 it stalls? Maybe I should better make this function the same as 64bit version has? I.e. set
nmi_watchdog=NMI_NONEby default?
Well, nmi_watchdog=1 is the I/O APIC watchdog and if no watchdog has been
specified at the command line, the piece of code you have moved selects between the local and the I/O APIC watchdog based on availability of the former. So in this case the local watchdog must have been unavailable as it works if requested explicitly.
No piece of code in nmi_watchdog_default() touches peripheral hardware
and native_smp_prepare_cpus() is called early enough the system is still running UP and no APIC setup has happened yet, so any interference with running hardware can be excluded.
Random lock-ups are a typical symptom of the NMI watchdog interfering
with SMM firmware -- of course in the context of the watchdog being suspected in the first place -- there may be plenty of other reasons of random lock-ups. Obviously this is the SMM firmware asking for trouble explicitly, because NMIs are disabled by the processor upon entering the SMM and it is the SMI handler that unmasks the NMI explicitly (with an IRET, which shouldn't be used in the SMM mode at all) -- otherwise it wouldn't even notice the watchdog running, but there you go.
As a rule of thumb any piece of firmware that has a possibility to run
from an OS context should not use interrupts of any kind, because it is quite likely it cannot handle them in the way the OS expects them to be handled. It is as simple as that, but perhaps too simple for some to comprehend. :(
Maciej
Re: [patch 06/11] x86: nmi_32/64.c - use apic_write_around instead of apic_write
[Maciej W. Rozycki - Wed, May 28, 2008 at 06:47:56PM +0100]
| On Wed, 28 May 2008, Cyrill Gorcunov wrote: | | > Thanks a lot, Maciej!!! Could you please explain me how did you find | > that? 'cause reporter said that with nmi_watchdog=2 it works and with | > nmi_watchdog=1 it stalls? Maybe I should better make this function | > the same as 64bit version has? I.e. set nmi_watchdog = NMI_NONE by default? | | Well, nmi_watchdog=1 is the I/O APIC watchdog and if no watchdog has been | specified at the command line, the piece of code you have moved selects | between the local and the I/O APIC watchdog based on availability of the | former. So in this case the local watchdog must have been unavailable as | it works if requested explicitly. | | No piece of code in nmi_watchdog_default() touches peripheral hardware | and native_smp_prepare_cpus() is called early enough the system is still | running UP and no APIC setup has happened yet, so any interference with | running hardware can be excluded. | | Random lock-ups are a typical symptom of the NMI watchdog interfering | with SMM firmware -- of course in the context of the watchdog being | suspected in the first place -- there may be plenty of other reasons of | random lock-ups. Obviously this is the SMM firmware asking for trouble | explicitly, because NMIs are disabled by the processor upon entering the | SMM and it is the SMI handler that unmasks the NMI explicitly (with an | IRET, which shouldn't be used in the SMM mode at all) -- otherwise it | wouldn't even notice the watchdog running, but there you go. | | As a rule of thumb any piece of firmware that has a possibility to run | from an OS context should not use interrupts of any kind, because it is | quite likely it cannot handle them in the way the OS expects them to be | handled. It is as simple as that, but perhaps too simple for some to | comprehend. :( | | Maciej |
oh... :( i think i just restore old behaviour for now. Thanks for explanation!
- Cyrill -