2005-04-17 02:20:36 +04: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 .
* 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/delay.h>
# include <linux/interrupt.h>
# include <linux/module.h>
# include <linux/nmi.h>
# include <linux/sysdev.h>
# include <linux/sysctl.h>
2006-06-26 15:57:01 +04:00
# include <linux/percpu.h>
2006-09-26 12:52:36 +04:00
# include <linux/kprobes.h>
2006-12-07 04:14:01 +03:00
# include <linux/cpumask.h>
2007-02-16 12:28:09 +03:00
# include <linux/kernel_stat.h>
2007-05-08 11:27:03 +04:00
# include <linux/kdebug.h>
2005-04-17 02:20:36 +04:00
# include <asm/smp.h>
# include <asm/nmi.h>
# include "mach_traps.h"
2006-09-30 03:47:55 +04:00
int unknown_nmi_panic ;
int nmi_watchdog_enabled ;
2007-04-16 12:30:27 +04:00
static cpumask_t backtrace_mask = CPU_MASK_NONE ;
2007-05-02 21:27:20 +04:00
2005-04-17 02:20:36 +04:00
/* nmi_active:
2006-09-26 12:52:26 +04: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-17 02:20:36 +04:00
* be enabled
2006-09-26 12:52:26 +04:00
* 0 : the lapic NMI watchdog is disabled , but can be enabled
2005-04-17 02:20:36 +04:00
*/
2006-09-26 12:52:26 +04:00
atomic_t nmi_active = ATOMIC_INIT ( 0 ) ; /* oprofile uses this */
2005-04-17 02:20:36 +04:00
2006-09-26 12:52:26 +04:00
unsigned int nmi_watchdog = NMI_DEFAULT ;
static unsigned int nmi_hz = HZ ;
2005-04-17 02:20:36 +04:00
2007-05-02 21:27:20 +04:00
static DEFINE_PER_CPU ( short , wd_enabled ) ;
2005-04-17 02:20:36 +04:00
2006-09-26 12:52:26 +04:00
/* local prototypes */
static int unknown_nmi_panic_callback ( struct pt_regs * regs , int cpu ) ;
2006-12-09 23:33:35 +03:00
static int endflag __initdata = 0 ;
2008-01-30 15:30:33 +03:00
# ifdef CONFIG_SMP
2005-10-31 01:59:40 +03:00
/* 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 )
{
2006-07-03 11:25:25 +04:00
local_irq_enable_in_hardirq ( ) ;
2005-10-31 01:59:40 +03: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 23:33:35 +03:00
while ( endflag = = 0 )
mb ( ) ;
2007-11-10 00:39:38 +03:00
}
2008-01-30 15:30:33 +03:00
# endif
2005-10-31 01:59:40 +03:00
[PATCH] check nmi watchdog is broken
A bug against an xSeries system showed up recently noting that the
check_nmi_watchdog() test was failing.
I have been investigating it and discovered in both i386 and x86_64 the
recent change to the routine to use the cpu_callin_map has uncovered a
problem. Prior to that change, on an SMP box, the test was trivally
passing because all cpu's were found to not yet be online, but now with the
callin_map they are discovered, it goes on to test the counter and they
have not yet begun to increment, so it announces a CPU is stuck and bails
out.
On all the systems I have access to test, the announcement of failure is
also bougs... by the time you can login and check /proc/interrupts, the
NMI count is happily incrementing on all CPUs. Its just that the test is
being done too early.
I have tried moving the call to the test around a bit, and it was always
too early. I finally hit on this proposed solution, it delays the routine
via a late_initcall(), seems like the right solution to me.
Signed-off-by: Adrian Bunk <bunk@stusta.de>
Cc: Andi Kleen <ak@muc.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-05-01 19:58:48 +04:00
static int __init check_nmi_watchdog ( void )
2005-04-17 02:20:36 +04:00
{
2005-10-31 01:59:40 +03:00
unsigned int * prev_nmi_count ;
2005-04-17 02:20:36 +04:00
int cpu ;
2007-08-15 04:40:35 +04:00
if ( ( nmi_watchdog = = NMI_NONE ) | | ( nmi_watchdog = = NMI_DISABLED ) )
2006-09-26 12:52:26 +04:00
return 0 ;
if ( ! atomic_read ( & nmi_active ) )
[PATCH] check nmi watchdog is broken
A bug against an xSeries system showed up recently noting that the
check_nmi_watchdog() test was failing.
I have been investigating it and discovered in both i386 and x86_64 the
recent change to the routine to use the cpu_callin_map has uncovered a
problem. Prior to that change, on an SMP box, the test was trivally
passing because all cpu's were found to not yet be online, but now with the
callin_map they are discovered, it goes on to test the counter and they
have not yet begun to increment, so it announces a CPU is stuck and bails
out.
On all the systems I have access to test, the announcement of failure is
also bougs... by the time you can login and check /proc/interrupts, the
NMI count is happily incrementing on all CPUs. Its just that the test is
being done too early.
I have tried moving the call to the test around a bit, and it was always
too early. I finally hit on this proposed solution, it delays the routine
via a late_initcall(), seems like the right solution to me.
Signed-off-by: Adrian Bunk <bunk@stusta.de>
Cc: Andi Kleen <ak@muc.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-05-01 19:58:48 +04:00
return 0 ;
2005-10-31 01:59:40 +03:00
prev_nmi_count = kmalloc ( NR_CPUS * sizeof ( int ) , GFP_KERNEL ) ;
if ( ! prev_nmi_count )
2008-01-22 12:23:01 +03:00
return - 1 ;
2005-10-31 01:59:40 +03:00
[PATCH] check nmi watchdog is broken
A bug against an xSeries system showed up recently noting that the
check_nmi_watchdog() test was failing.
I have been investigating it and discovered in both i386 and x86_64 the
recent change to the routine to use the cpu_callin_map has uncovered a
problem. Prior to that change, on an SMP box, the test was trivally
passing because all cpu's were found to not yet be online, but now with the
callin_map they are discovered, it goes on to test the counter and they
have not yet begun to increment, so it announces a CPU is stuck and bails
out.
On all the systems I have access to test, the announcement of failure is
also bougs... by the time you can login and check /proc/interrupts, the
NMI count is happily incrementing on all CPUs. Its just that the test is
being done too early.
I have tried moving the call to the test around a bit, and it was always
too early. I finally hit on this proposed solution, it delays the routine
via a late_initcall(), seems like the right solution to me.
Signed-off-by: Adrian Bunk <bunk@stusta.de>
Cc: Andi Kleen <ak@muc.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-05-01 19:58:48 +04:00
printk ( KERN_INFO " Testing NMI watchdog ... " ) ;
2005-04-17 02:20:36 +04:00
2008-01-30 15:30:33 +03:00
# ifdef CONFIG_SMP
2005-10-31 01:59:40 +03:00
if ( nmi_watchdog = = NMI_LOCAL_APIC )
smp_call_function ( nmi_cpu_busy , ( void * ) & endflag , 0 , 0 ) ;
2008-01-30 15:30:33 +03:00
# endif
2005-10-31 01:59:40 +03:00
2006-03-28 13:56:39 +04:00
for_each_possible_cpu ( cpu )
2008-01-30 15:30:33 +03:00
prev_nmi_count [ cpu ] = nmi_count ( cpu ) ;
2005-04-17 02:20:36 +04:00
local_irq_enable ( ) ;
2007-04-02 14:14:12 +04:00
mdelay ( ( 20 * 1000 ) / nmi_hz ) ; // wait 20 ticks
2005-04-17 02:20:36 +04:00
2006-03-28 13:56:39 +04:00
for_each_possible_cpu ( cpu ) {
2005-04-17 02:20:36 +04:00
# 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
2007-05-02 21:27:20 +04:00
if ( ! per_cpu ( wd_enabled , cpu ) )
2006-09-26 12:52:26 +04:00
continue ;
2005-04-17 02:20:36 +04:00
if ( nmi_count ( cpu ) - prev_nmi_count [ cpu ] < = 5 ) {
2007-12-04 19:19:07 +03:00
printk ( KERN_WARNING " WARNING: CPU#%d: NMI "
" appears to be stuck (%d->%d)! \n " ,
2005-10-31 01:59:40 +03:00
cpu ,
prev_nmi_count [ cpu ] ,
nmi_count ( cpu ) ) ;
2007-05-02 21:27:20 +04:00
per_cpu ( wd_enabled , cpu ) = 0 ;
2006-09-26 12:52:26 +04:00
atomic_dec ( & nmi_active ) ;
2005-04-17 02:20:36 +04:00
}
}
2007-09-06 18:59:54 +04:00
endflag = 1 ;
2006-09-26 12:52:26 +04:00
if ( ! atomic_read ( & nmi_active ) ) {
kfree ( prev_nmi_count ) ;
atomic_set ( & nmi_active , - 1 ) ;
2008-01-22 12:23:01 +03:00
return - 1 ;
2006-09-26 12:52:26 +04:00
}
2005-04-17 02:20:36 +04: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 21:27:20 +04:00
if ( nmi_watchdog = = NMI_LOCAL_APIC )
nmi_hz = lapic_adjust_nmi_hz ( 1 ) ;
2005-04-17 02:20:36 +04:00
2005-10-31 01:59:40 +03:00
kfree ( prev_nmi_count ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
[PATCH] check nmi watchdog is broken
A bug against an xSeries system showed up recently noting that the
check_nmi_watchdog() test was failing.
I have been investigating it and discovered in both i386 and x86_64 the
recent change to the routine to use the cpu_callin_map has uncovered a
problem. Prior to that change, on an SMP box, the test was trivally
passing because all cpu's were found to not yet be online, but now with the
callin_map they are discovered, it goes on to test the counter and they
have not yet begun to increment, so it announces a CPU is stuck and bails
out.
On all the systems I have access to test, the announcement of failure is
also bougs... by the time you can login and check /proc/interrupts, the
NMI count is happily incrementing on all CPUs. Its just that the test is
being done too early.
I have tried moving the call to the test around a bit, and it was always
too early. I finally hit on this proposed solution, it delays the routine
via a late_initcall(), seems like the right solution to me.
Signed-off-by: Adrian Bunk <bunk@stusta.de>
Cc: Andi Kleen <ak@muc.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-05-01 19:58:48 +04:00
/* This needs to happen later in boot so counters are working */
late_initcall ( check_nmi_watchdog ) ;
2005-04-17 02:20:36 +04:00
static int __init setup_nmi_watchdog ( char * str )
{
int nmi ;
get_option ( & str , & nmi ) ;
2006-09-26 12:52:26 +04:00
if ( ( nmi > = NMI_INVALID ) | | ( nmi < NMI_NONE ) )
2005-04-17 02:20:36 +04:00
return 0 ;
2007-01-23 07:40:34 +03:00
2006-09-26 12:52:26 +04:00
nmi_watchdog = nmi ;
2005-04-17 02:20:36 +04:00
return 1 ;
}
__setup ( " nmi_watchdog= " , setup_nmi_watchdog ) ;
2007-02-13 15:26:24 +03:00
2007-05-02 21:27:20 +04:00
/* Suspend/resume support */
2007-02-13 15:26:24 +03:00
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_PM
static int nmi_pm_active ; /* nmi_active before suspend */
2005-04-17 02:25:24 +04:00
static int lapic_nmi_suspend ( struct sys_device * dev , pm_message_t state )
2005-04-17 02:20:36 +04:00
{
2006-09-26 12:52:27 +04:00
/* only CPU0 goes here, other CPUs should be offline */
2006-09-26 12:52:26 +04:00
nmi_pm_active = atomic_read ( & nmi_active ) ;
2006-09-26 12:52:27 +04:00
stop_apic_nmi_watchdog ( NULL ) ;
BUG_ON ( atomic_read ( & nmi_active ) ! = 0 ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
static int lapic_nmi_resume ( struct sys_device * dev )
{
2006-09-26 12:52:27 +04: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-17 02:20:36 +04:00
return 0 ;
}
static struct sysdev_class nmi_sysclass = {
2007-12-20 04:09:39 +03:00
. name = " lapic_nmi " ,
2005-04-17 02:20:36 +04:00
. 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 ;
2006-09-26 12:52:26 +04: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 ;
2007-05-02 21:27:20 +04:00
if ( atomic_read ( & nmi_active ) < 0 )
2005-04-17 02:20:36 +04: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 */
2007-05-02 21:27:20 +04:00
static void __acpi_nmi_enable ( void * __unused )
2005-04-17 02:20:36 +04:00
{
2007-05-02 21:27:20 +04:00
apic_write_around ( APIC_LVT0 , APIC_DM_NMI ) ;
2005-04-17 02:20:36 +04:00
}
2007-05-02 21:27:20 +04:00
/*
* Enable timer based NMIs on all CPUs :
*/
void acpi_nmi_enable ( void )
2005-04-17 02:20:36 +04:00
{
2007-05-02 21:27:20 +04:00
if ( atomic_read ( & nmi_active ) & & nmi_watchdog = = NMI_IO_APIC )
on_each_cpu ( __acpi_nmi_enable , NULL , 0 , 1 ) ;
2006-09-26 12:52:26 +04:00
}
2007-05-02 21:27:20 +04:00
static void __acpi_nmi_disable ( void * __unused )
2006-09-26 12:52:27 +04:00
{
2007-05-02 21:27:20 +04:00
apic_write ( APIC_LVT0 , APIC_DM_NMI | APIC_LVT_MASKED ) ;
2006-09-26 12:52:27 +04:00
}
2007-05-02 21:27:20 +04:00
/*
* Disable timer based NMIs on all CPUs :
*/
void acpi_nmi_disable ( void )
2006-09-26 12:52:27 +04:00
{
2007-05-02 21:27:20 +04:00
if ( atomic_read ( & nmi_active ) & & nmi_watchdog = = NMI_IO_APIC )
on_each_cpu ( __acpi_nmi_disable , NULL , 0 , 1 ) ;
2006-09-26 12:52:27 +04:00
}
2008-01-30 15:30:33 +03:00
void setup_apic_nmi_watchdog ( void * unused )
2006-09-26 12:52:26 +04:00
{
2007-05-02 21:27:20 +04:00
if ( __get_cpu_var ( wd_enabled ) )
2008-01-30 15:30:33 +03:00
return ;
2006-09-26 12:52:27 +04:00
/* 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 21:27:20 +04:00
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 ;
2006-09-26 12:52:26 +04:00
return ;
}
2007-05-02 21:27:20 +04:00
/* FALL THROUGH */
case NMI_IO_APIC :
__get_cpu_var ( wd_enabled ) = 1 ;
atomic_inc ( & nmi_active ) ;
2006-09-26 12:52:26 +04:00
}
}
2006-09-26 12:52:27 +04:00
void stop_apic_nmi_watchdog ( void * unused )
2006-09-26 12:52:26 +04:00
{
/* only support LOCAL and IO APICs for now */
if ( ( nmi_watchdog ! = NMI_LOCAL_APIC ) & &
( nmi_watchdog ! = NMI_IO_APIC ) )
return ;
2007-05-02 21:27:20 +04:00
if ( __get_cpu_var ( wd_enabled ) = = 0 )
2006-09-26 12:52:27 +04:00
return ;
2007-05-02 21:27:20 +04:00
if ( nmi_watchdog = = NMI_LOCAL_APIC )
lapic_watchdog_stop ( ) ;
__get_cpu_var ( wd_enabled ) = 0 ;
2006-09-26 12:52:26 +04:00
atomic_dec ( & nmi_active ) ;
2005-04-17 02:20:36 +04: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 .
*
* 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 unsigned int
last_irq_sums [ NR_CPUS ] ,
alert_counter [ NR_CPUS ] ;
2007-07-17 15:03:57 +04:00
void touch_nmi_watchdog ( void )
2005-04-17 02:20:36 +04:00
{
2006-12-07 04:14:09 +03:00
if ( nmi_watchdog > 0 ) {
unsigned cpu ;
2005-04-17 02:20:36 +04:00
2006-12-07 04:14:09 +03:00
/*
* Just reset the alert counters , ( other CPUs might be
* spinning on locks we hold ) :
*/
2007-07-17 15:03:57 +04:00
for_each_present_cpu ( cpu ) {
if ( alert_counter [ cpu ] )
alert_counter [ cpu ] = 0 ;
}
2006-12-07 04:14:09 +03:00
}
2005-09-07 02:16:27 +04:00
/*
* Tickle the softlockup detector too :
*/
touch_softlockup_watchdog ( ) ;
2005-04-17 02:20:36 +04:00
}
2006-07-30 14:03:29 +04:00
EXPORT_SYMBOL ( touch_nmi_watchdog ) ;
2005-04-17 02:20:36 +04:00
extern void die_nmi ( struct pt_regs * , const char * msg ) ;
2006-09-26 12:52:36 +04:00
__kprobes int nmi_watchdog_tick ( struct pt_regs * regs , unsigned reason )
2005-04-17 02:20:36 +04:00
{
/*
* 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 ( ) .
*/
2006-03-28 13:56:52 +04:00
unsigned int sum ;
2006-09-26 12:52:26 +04:00
int touched = 0 ;
2006-03-28 13:56:52 +04:00
int cpu = smp_processor_id ( ) ;
2008-01-30 15:30:33 +03:00
int rc = 0 ;
2006-09-26 12:52:26 +04:00
/* check for other users first */
if ( notify_die ( DIE_NMI , " nmi " , regs , reason , 2 , SIGINT )
= = NOTIFY_STOP ) {
2006-09-26 12:52:26 +04:00
rc = 1 ;
2006-09-26 12:52:26 +04:00
touched = 1 ;
}
2005-04-17 02:20:36 +04:00
2006-12-07 04:14:01 +03: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 ) ;
}
2007-02-16 12:28:09 +03:00
/*
* Take the local apic timer and PIT / HPET into account . We don ' t
* know which one is active , when we have highres / dyntick on
*/
2007-10-13 01:04:06 +04:00
sum = per_cpu ( irq_stat , cpu ) . apic_timer_irqs +
per_cpu ( irq_stat , cpu ) . irq0_irqs ;
2005-04-17 02:20:36 +04:00
2007-02-16 12:28:09 +03:00
/* if the none of the timers isn't firing, this cpu isn't doing much */
2006-09-26 12:52:26 +04:00
if ( ! touched & & last_irq_sums [ cpu ] = = sum ) {
2005-04-17 02:20:36 +04:00
/*
* Ayiee , looks like this CPU is stuck . . .
* wait a few IRQs ( 5 seconds ) before doing the oops . . .
*/
alert_counter [ cpu ] + + ;
if ( alert_counter [ cpu ] = = 5 * nmi_hz )
2005-09-04 02:56:48 +04:00
/*
* die_nmi will return ONLY if NOTIFY_STOP happens . .
*/
2006-03-23 14:00:54 +03:00
die_nmi ( regs , " BUG: NMI Watchdog detected LOCKUP " ) ;
2006-03-08 08:55:29 +03:00
} else {
2005-04-17 02:20:36 +04:00
last_irq_sums [ cpu ] = sum ;
alert_counter [ cpu ] = 0 ;
}
2006-09-26 12:52:26 +04:00
/* see if the nmi watchdog went off */
2007-05-02 21:27:20 +04: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-04-17 02:20:36 +04:00
}
2006-09-26 12:52:26 +04:00
return rc ;
2005-04-17 02:20:36 +04:00
}
2006-09-26 12:52:27 +04: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 ;
}
2005-04-17 02:20:36 +04: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 12:52:27 +04:00
sprintf ( buf , " NMI received for unknown reason %02x \n " , reason ) ;
die_nmi ( regs , buf ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2006-09-26 12:52:27 +04:00
/*
2006-09-26 12:52:27 +04:00
* proc handler for / proc / sys / kernel / nmi
2006-09-26 12:52:27 +04:00
*/
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 04:40:35 +04:00
if ( atomic_read ( & nmi_active ) < 0 | | nmi_watchdog = = NMI_DISABLED ) {
2006-09-26 12:52:27 +04:00
printk ( KERN_WARNING " NMI watchdog is permanently disabled \n " ) ;
return - EIO ;
2006-09-26 12:52:27 +04:00
}
if ( nmi_watchdog = = NMI_DEFAULT ) {
2007-05-02 21:27:20 +04:00
if ( lapic_watchdog_ok ( ) )
2006-09-26 12:52:27 +04:00
nmi_watchdog = NMI_LOCAL_APIC ;
else
nmi_watchdog = NMI_IO_APIC ;
}
2006-09-26 12:52:27 +04:00
if ( nmi_watchdog = = NMI_LOCAL_APIC ) {
2006-09-26 12:52:27 +04:00
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 ;
}
2005-04-17 02:20:36 +04:00
# endif
2006-12-07 04:14:01 +03: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-17 02:20:36 +04:00
EXPORT_SYMBOL ( nmi_active ) ;
EXPORT_SYMBOL ( nmi_watchdog ) ;