2005-04-16 15:20:36 -07:00
/*
* NMI watchdog support on APIC systems
*
* Started by Ingo Molnar < mingo @ redhat . com >
*
* Fixes :
* Mikael Pettersson : AMD K7 support for local APIC NMI watchdog .
* Mikael Pettersson : Power Management for local APIC NMI watchdog .
* Pavel Machek and
* Mikael Pettersson : PM converted to driver model . Disable / enable API .
*/
2006-12-07 02:14:01 +01:00
# include <linux/nmi.h>
2005-04-16 15:20:36 -07:00
# include <linux/mm.h>
# include <linux/delay.h>
# include <linux/interrupt.h>
# include <linux/module.h>
# include <linux/sysdev.h>
# include <linux/sysctl.h>
2006-02-03 21:50:41 +01:00
# include <linux/kprobes.h>
2006-12-07 02:14:01 +01:00
# include <linux/cpumask.h>
2007-05-08 00:27:03 -07:00
# include <linux/kdebug.h>
2005-04-16 15:20:36 -07:00
# include <asm/smp.h>
# include <asm/nmi.h>
# include <asm/proto.h>
2006-04-07 19:49:57 +02:00
# include <asm/mce.h>
2005-04-16 15:20:36 -07:00
2008-03-19 14:25:35 -03:00
# include <mach_traps.h>
2006-09-30 01:47:55 +02:00
int unknown_nmi_panic ;
int nmi_watchdog_enabled ;
int panic_on_unrecovered_nmi ;
2007-04-16 10:30:27 +02:00
static cpumask_t backtrace_mask = CPU_MASK_NONE ;
2006-09-26 10:52:26 +02:00
2005-04-16 15:20:36 -07:00
/* nmi_active:
2006-09-26 10:52:26 +02:00
* > 0 : the lapic NMI watchdog is active , but can be disabled
* < 0 : the lapic NMI watchdog has not been set up , and cannot
2005-04-16 15:20:36 -07:00
* be enabled
2006-09-26 10:52:26 +02:00
* 0 : the lapic NMI watchdog is disabled , but can be enabled
2005-04-16 15:20:36 -07:00
*/
2006-09-26 10:52:26 +02:00
atomic_t nmi_active = ATOMIC_INIT ( 0 ) ; /* oprofile uses this */
2008-01-30 13:30:31 +01:00
static int panic_on_timeout ;
2005-04-16 15:20:36 -07:00
unsigned int nmi_watchdog = NMI_DEFAULT ;
static unsigned int nmi_hz = HZ ;
2007-05-02 19:27:20 +02:00
static DEFINE_PER_CPU ( short , wd_enabled ) ;
2005-04-16 15:20:36 -07:00
/* Run after command line and cpu_init init, but before all other checks */
2006-09-26 10:52:27 +02:00
void nmi_watchdog_default ( void )
2005-04-16 15:20:36 -07:00
{
if ( nmi_watchdog ! = NMI_DEFAULT )
return ;
2007-03-14 17:50:48 -07:00
nmi_watchdog = NMI_NONE ;
2005-04-16 15:20:36 -07:00
}
2006-12-09 21:33:35 +01:00
static int endflag __initdata = 0 ;
2005-05-16 21:53:34 -07:00
# 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 )
2005-04-16 15:20:36 -07:00
{
2006-07-03 00:25:25 -07:00
local_irq_enable_in_hardirq ( ) ;
2005-05-16 21:53:34 -07:00
/* 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 . */
2006-12-09 21:33:35 +01:00
while ( endflag = = 0 )
mb ( ) ;
2005-04-16 15:20:36 -07:00
}
2005-05-16 21:53:34 -07:00
# endif
2005-04-16 15:20:36 -07:00
2008-01-30 13:30:33 +01:00
int __init check_nmi_watchdog ( void )
2005-04-16 15:20:36 -07:00
{
2008-01-30 13:30:33 +01:00
int * prev_nmi_count ;
2005-04-16 15:20:36 -07:00
int cpu ;
2008-01-30 13:30:33 +01:00
if ( ( nmi_watchdog = = NMI_NONE ) | | ( nmi_watchdog = = NMI_DISABLED ) )
2006-09-26 10:52:26 +02:00
return 0 ;
if ( ! atomic_read ( & nmi_active ) )
return 0 ;
2008-01-30 13:30:33 +01:00
prev_nmi_count = kmalloc ( NR_CPUS * sizeof ( int ) , GFP_KERNEL ) ;
if ( ! prev_nmi_count )
2005-05-16 21:53:34 -07:00
return - 1 ;
2005-04-16 15:20:36 -07:00
2008-01-30 13:30:33 +01:00
printk ( KERN_INFO " Testing NMI watchdog ... " ) ;
2005-05-16 21:53:19 -07:00
2006-01-11 22:45:45 +01:00
# ifdef CONFIG_SMP
2005-05-16 21:53:34 -07:00
if ( nmi_watchdog = = NMI_LOCAL_APIC )
smp_call_function ( nmi_cpu_busy , ( void * ) & endflag , 0 , 0 ) ;
2006-01-11 22:45:45 +01:00
# endif
2005-04-16 15:20:36 -07:00
for ( cpu = 0 ; cpu < NR_CPUS ; cpu + + )
2008-01-30 13:30:33 +01:00
prev_nmi_count [ cpu ] = cpu_pda ( cpu ) - > __nmi_count ;
2005-04-16 15:20:36 -07:00
local_irq_enable ( ) ;
2007-04-02 12:14:12 +02:00
mdelay ( ( 20 * 1000 ) / nmi_hz ) ; // wait 20 ticks
2005-04-16 15:20:36 -07:00
2006-03-23 03:01:05 -08:00
for_each_online_cpu ( cpu ) {
2007-05-02 19:27:20 +02:00
if ( ! per_cpu ( wd_enabled , cpu ) )
2006-09-26 10:52:26 +02:00
continue ;
2008-01-30 13:30:33 +01:00
if ( cpu_pda ( cpu ) - > __nmi_count - prev_nmi_count [ cpu ] < = 5 ) {
2007-12-04 17:19:07 +01:00
printk ( KERN_WARNING " WARNING: CPU#%d: NMI "
" appears to be stuck (%d->%d)! \n " ,
2008-01-30 13:30:33 +01:00
cpu ,
prev_nmi_count [ cpu ] ,
cpu_pda ( cpu ) - > __nmi_count ) ;
2007-05-02 19:27:20 +02:00
per_cpu ( wd_enabled , cpu ) = 0 ;
2006-09-26 10:52:26 +02:00
atomic_dec ( & nmi_active ) ;
2005-04-16 15:20:36 -07:00
}
}
2008-01-30 13:30:33 +01:00
endflag = 1 ;
2006-09-26 10:52:26 +02:00
if ( ! atomic_read ( & nmi_active ) ) {
2008-01-30 13:30:33 +01:00
kfree ( prev_nmi_count ) ;
2006-09-26 10:52:26 +02:00
atomic_set ( & nmi_active , - 1 ) ;
return - 1 ;
}
2005-04-16 15:20:36 -07:00
printk ( " OK. \n " ) ;
/* now that we know it works we can reduce NMI frequency to
something more reasonable ; makes a difference in some configs */
2007-05-02 19:27:20 +02:00
if ( nmi_watchdog = = NMI_LOCAL_APIC )
nmi_hz = lapic_adjust_nmi_hz ( 1 ) ;
2005-04-16 15:20:36 -07:00
2008-01-30 13:30:33 +01:00
kfree ( prev_nmi_count ) ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
2008-01-30 13:30:31 +01:00
static int __init setup_nmi_watchdog ( char * str )
2005-04-16 15:20:36 -07:00
{
int nmi ;
if ( ! strncmp ( str , " panic " , 5 ) ) {
panic_on_timeout = 1 ;
str = strchr ( str , ' , ' ) ;
if ( ! str )
return 1 ;
+ + str ;
}
get_option ( & str , & nmi ) ;
2006-09-26 10:52:26 +02:00
if ( ( nmi > = NMI_INVALID ) | | ( nmi < NMI_NONE ) )
2005-04-16 15:20:36 -07:00
return 0 ;
2006-09-26 10:52:26 +02:00
2005-05-16 21:53:34 -07:00
nmi_watchdog = nmi ;
2005-04-16 15:20:36 -07:00
return 1 ;
}
__setup ( " nmi_watchdog= " , setup_nmi_watchdog ) ;
# ifdef CONFIG_PM
static int nmi_pm_active ; /* nmi_active before suspend */
2005-09-03 15:56:56 -07:00
static int lapic_nmi_suspend ( struct sys_device * dev , pm_message_t state )
2005-04-16 15:20:36 -07:00
{
2006-09-26 10:52:27 +02:00
/* only CPU0 goes here, other CPUs should be offline */
2006-09-26 10:52:26 +02:00
nmi_pm_active = atomic_read ( & nmi_active ) ;
2006-09-26 10:52:27 +02:00
stop_apic_nmi_watchdog ( NULL ) ;
BUG_ON ( atomic_read ( & nmi_active ) ! = 0 ) ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
static int lapic_nmi_resume ( struct sys_device * dev )
{
2006-09-26 10:52:27 +02:00
/* only CPU0 goes here, other CPUs should be offline */
if ( nmi_pm_active > 0 ) {
setup_apic_nmi_watchdog ( NULL ) ;
touch_nmi_watchdog ( ) ;
}
2005-04-16 15:20:36 -07:00
return 0 ;
}
static struct sysdev_class nmi_sysclass = {
2007-12-20 02:09:39 +01:00
. name = " lapic_nmi " ,
2005-04-16 15:20:36 -07:00
. resume = lapic_nmi_resume ,
. suspend = lapic_nmi_suspend ,
} ;
static struct sys_device device_lapic_nmi = {
2008-01-30 13:30:33 +01:00
. id = 0 ,
2005-04-16 15:20:36 -07:00
. cls = & nmi_sysclass ,
} ;
static int __init init_lapic_nmi_sysfs ( void )
{
int error ;
2006-09-26 10:52:26 +02:00
/* 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 ;
2008-01-30 13:30:33 +01:00
if ( atomic_read ( & nmi_active ) < 0 )
2005-04-16 15:20:36 -07:00
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 */
2008-01-30 13:30:33 +01:00
static void __acpi_nmi_enable ( void * __unused )
{
apic_write ( 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 ( 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 ) ;
}
2006-09-26 10:52:26 +02:00
void setup_apic_nmi_watchdog ( void * unused )
{
2008-01-30 13:30:33 +01:00
if ( __get_cpu_var ( wd_enabled ) )
2006-09-26 10:52:27 +02:00
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 ;
2007-05-02 19:27:20 +02:00
switch ( nmi_watchdog ) {
case NMI_LOCAL_APIC :
__get_cpu_var ( wd_enabled ) = 1 ;
if ( lapic_watchdog_init ( nmi_hz ) < 0 ) {
__get_cpu_var ( wd_enabled ) = 0 ;
2005-05-16 21:53:34 -07:00
return ;
2006-09-26 10:52:26 +02:00
}
2007-05-02 19:27:20 +02:00
/* FALL THROUGH */
case NMI_IO_APIC :
__get_cpu_var ( wd_enabled ) = 1 ;
atomic_inc ( & nmi_active ) ;
2006-09-26 10:52:26 +02:00
}
}
2005-05-16 21:53:34 -07:00
2006-09-26 10:52:27 +02:00
void stop_apic_nmi_watchdog ( void * unused )
2006-09-26 10:52:26 +02:00
{
/* only support LOCAL and IO APICs for now */
if ( ( nmi_watchdog ! = NMI_LOCAL_APIC ) & &
( nmi_watchdog ! = NMI_IO_APIC ) )
return ;
2007-05-02 19:27:20 +02:00
if ( __get_cpu_var ( wd_enabled ) = = 0 )
2006-09-26 10:52:27 +02:00
return ;
2007-05-02 19:27:20 +02:00
if ( nmi_watchdog = = NMI_LOCAL_APIC )
lapic_watchdog_stop ( ) ;
__get_cpu_var ( wd_enabled ) = 0 ;
2006-09-26 10:52:26 +02:00
atomic_dec ( & nmi_active ) ;
2005-04-16 15:20:36 -07:00
}
/*
* 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 .
*/
2005-05-16 21:53:34 -07:00
static DEFINE_PER_CPU ( unsigned , last_irq_sum ) ;
static DEFINE_PER_CPU ( local_t , alert_counter ) ;
static DEFINE_PER_CPU ( int , nmi_touch ) ;
2005-04-16 15:20:36 -07:00
2007-07-17 04:03:58 -07:00
void touch_nmi_watchdog ( void )
2005-04-16 15:20:36 -07:00
{
2006-02-16 23:41:55 +01:00
if ( nmi_watchdog > 0 ) {
unsigned cpu ;
2005-04-16 15:20:36 -07:00
2006-02-16 23:41:55 +01:00
/*
* Tell other CPUs to reset their alert counters . We cannot
* do it ourselves because the alert count increase is not
* atomic .
*/
2007-07-17 04:03:58 -07:00
for_each_present_cpu ( cpu ) {
if ( per_cpu ( nmi_touch , cpu ) ! = 1 )
per_cpu ( nmi_touch , cpu ) = 1 ;
}
2006-02-16 23:41:55 +01:00
}
2005-09-06 15:16:27 -07:00
2008-01-30 13:30:33 +01:00
touch_softlockup_watchdog ( ) ;
2005-04-16 15:20:36 -07:00
}
2008-01-30 13:30:33 +01:00
EXPORT_SYMBOL ( touch_nmi_watchdog ) ;
2005-04-16 15:20:36 -07:00
2008-04-19 19:19:55 +02:00
notrace __kprobes int
nmi_watchdog_tick ( struct pt_regs * regs , unsigned reason )
2005-04-16 15:20:36 -07:00
{
2005-05-16 21:53:34 -07:00
int sum ;
int touched = 0 ;
2006-12-07 02:14:01 +01:00
int cpu = smp_processor_id ( ) ;
2007-05-02 19:27:20 +02:00
int rc = 0 ;
2006-09-26 10:52:26 +02:00
/* check for other users first */
if ( notify_die ( DIE_NMI , " nmi " , regs , reason , 2 , SIGINT )
= = NOTIFY_STOP ) {
2006-09-26 10:52:26 +02:00
rc = 1 ;
2006-09-26 10:52:26 +02:00
touched = 1 ;
}
2005-04-16 15:20:36 -07:00
2007-10-12 23:04:07 +02:00
sum = read_pda ( apic_timer_irqs ) + read_pda ( irq0_irqs ) ;
2005-05-16 21:53:34 -07:00
if ( __get_cpu_var ( nmi_touch ) ) {
__get_cpu_var ( nmi_touch ) = 0 ;
touched = 1 ;
}
2006-09-26 10:52:26 +02:00
2006-12-07 02:14:01 +01:00
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 ) ;
}
2006-04-07 19:49:57 +02:00
# ifdef CONFIG_X86_MCE
/* Could check oops_in_progress here too, but it's safer
not too */
if ( atomic_read ( & mce_entry ) > 0 )
touched = 1 ;
# endif
2006-09-26 10:52:26 +02:00
/* if the apic timer isn't firing, this cpu isn't doing much */
2005-05-16 21:53:34 -07:00
if ( ! touched & & __get_cpu_var ( last_irq_sum ) = = sum ) {
2005-04-16 15:20:36 -07:00
/*
* Ayiee , looks like this CPU is stuck . . .
* wait a few IRQs ( 5 seconds ) before doing the oops . . .
*/
2005-05-16 21:53:34 -07:00
local_inc ( & __get_cpu_var ( alert_counter ) ) ;
2006-09-26 10:52:26 +02:00
if ( local_read ( & __get_cpu_var ( alert_counter ) ) = = 5 * nmi_hz )
2006-09-26 10:52:27 +02:00
die_nmi ( " NMI Watchdog detected LOCKUP on CPU %d \n " , regs ,
panic_on_timeout ) ;
2005-04-16 15:20:36 -07:00
} else {
2005-05-16 21:53:34 -07:00
__get_cpu_var ( last_irq_sum ) = sum ;
local_set ( & __get_cpu_var ( alert_counter ) , 0 ) ;
2005-04-16 15:20:36 -07:00
}
2006-09-26 10:52:26 +02:00
/* see if the nmi watchdog went off */
2007-05-02 19:27:20 +02:00
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 ;
2005-05-16 21:53:34 -07:00
}
2006-09-26 10:52:26 +02:00
return rc ;
2005-04-16 15:20:36 -07:00
}
2007-07-22 11:12:32 +02:00
static unsigned ignore_nmis ;
2008-04-19 19:19:55 +02:00
asmlinkage notrace __kprobes void
do_nmi ( struct pt_regs * regs , long error_code )
2005-04-16 15:20:36 -07:00
{
nmi_enter ( ) ;
add_pda ( __nmi_count , 1 ) ;
2007-07-22 11:12:32 +02:00
if ( ! ignore_nmis )
default_do_nmi ( regs ) ;
2005-04-16 15:20:36 -07:00
nmi_exit ( ) ;
}
2007-07-22 11:12:32 +02:00
void stop_nmi ( void )
{
acpi_nmi_disable ( ) ;
ignore_nmis + + ;
}
void restart_nmi ( void )
{
ignore_nmis - - ;
acpi_nmi_enable ( ) ;
}
2005-04-16 15:20:36 -07:00
# ifdef CONFIG_SYSCTL
static int unknown_nmi_panic_callback ( struct pt_regs * regs , int cpu )
{
unsigned char reason = get_nmi_reason ( ) ;
char buf [ 64 ] ;
2006-09-26 10:52:27 +02:00
sprintf ( buf , " NMI received for unknown reason %02x \n " , reason ) ;
2006-09-26 10:52:27 +02:00
die_nmi ( buf , regs , 1 ) ; /* Always panic here */
2005-04-16 15:20:36 -07:00
return 0 ;
}
2006-09-26 10:52:27 +02:00
/*
* 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 ;
2007-08-15 02:40:35 +02:00
if ( atomic_read ( & nmi_active ) < 0 | | nmi_watchdog = = NMI_DISABLED ) {
2006-09-26 10:52:27 +02:00
printk ( KERN_WARNING " NMI watchdog is permanently disabled \n " ) ;
2006-09-26 10:52:27 +02:00
return - EIO ;
2006-09-26 10:52:27 +02:00
}
/* if nmi_watchdog is not set yet, then set it */
nmi_watchdog_default ( ) ;
2006-09-26 10:52:27 +02:00
if ( nmi_watchdog = = NMI_LOCAL_APIC ) {
2006-09-26 10:52:27 +02:00
if ( nmi_watchdog_enabled )
enable_lapic_nmi_watchdog ( ) ;
else
disable_lapic_nmi_watchdog ( ) ;
} else {
2006-09-26 10:52:27 +02:00
printk ( KERN_WARNING
2006-09-26 10:52:27 +02:00
" NMI watchdog doesn't know what hardware to touch \n " ) ;
return - EIO ;
}
return 0 ;
}
2005-04-16 15:20:36 -07:00
# endif
2008-02-03 15:40:30 +08:00
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 ;
}
2006-12-07 02:14:01 +01:00
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 ) ;
}
}
2005-04-16 15:20:36 -07:00
EXPORT_SYMBOL ( nmi_active ) ;
EXPORT_SYMBOL ( nmi_watchdog ) ;