2011-10-13 23:14:26 +04:00
/*
* arch / x86 / kernel / nmi - selftest . c
*
* Testsuite for NMI : IPIs
*
* Started by Don Zickus :
* ( using lib / locking - selftest . c as a guide )
*
* Copyright ( C ) 2011 Red Hat , Inc . , Don Zickus < dzickus @ redhat . com >
*/
# include <linux/smp.h>
# include <linux/cpumask.h>
# include <linux/delay.h>
2012-02-24 16:09:21 +04:00
# include <linux/init.h>
2012-05-11 11:35:27 +04:00
# include <linux/percpu.h>
2011-10-13 23:14:26 +04:00
# include <asm/apic.h>
# include <asm/nmi.h>
# define SUCCESS 0
# define FAILURE 1
# define TIMEOUT 2
2012-02-24 16:09:21 +04:00
static int __initdata nmi_fail ;
2011-10-13 23:14:26 +04:00
/* check to see if NMI IPIs work on this machine */
2012-02-24 16:09:21 +04:00
static DECLARE_BITMAP ( nmi_ipi_mask , NR_CPUS ) __initdata ;
2011-10-13 23:14:26 +04:00
2012-02-24 16:09:21 +04:00
static int __initdata testcase_total ;
static int __initdata testcase_successes ;
static int __initdata expected_testcase_failures ;
static int __initdata unexpected_testcase_failures ;
static int __initdata unexpected_testcase_unknowns ;
2011-10-13 23:14:26 +04:00
2012-02-24 16:09:21 +04:00
static int __init nmi_unk_cb ( unsigned int val , struct pt_regs * regs )
2011-10-13 23:14:26 +04:00
{
unexpected_testcase_unknowns + + ;
return NMI_HANDLED ;
}
2012-02-24 16:09:21 +04:00
static void __init init_nmi_testsuite ( void )
2011-10-13 23:14:26 +04:00
{
/* trap all the unknown NMIs we may generate */
2012-06-18 23:56:33 +04:00
register_nmi_handler ( NMI_UNKNOWN , nmi_unk_cb , 0 , " nmi_selftest_unk " ,
__initdata ) ;
2011-10-13 23:14:26 +04:00
}
2012-02-24 16:09:21 +04:00
static void __init cleanup_nmi_testsuite ( void )
2011-10-13 23:14:26 +04:00
{
unregister_nmi_handler ( NMI_UNKNOWN , " nmi_selftest_unk " ) ;
}
2012-02-24 16:09:21 +04:00
static int __init test_nmi_ipi_callback ( unsigned int val , struct pt_regs * regs )
2011-10-13 23:14:26 +04:00
{
int cpu = raw_smp_processor_id ( ) ;
if ( cpumask_test_and_clear_cpu ( cpu , to_cpumask ( nmi_ipi_mask ) ) )
return NMI_HANDLED ;
return NMI_DONE ;
}
2012-02-24 16:09:21 +04:00
static void __init test_nmi_ipi ( struct cpumask * mask )
2011-10-13 23:14:26 +04:00
{
unsigned long timeout ;
2012-06-18 23:56:33 +04:00
if ( register_nmi_handler ( NMI_LOCAL , test_nmi_ipi_callback ,
NMI_FLAG_FIRST , " nmi_selftest " , __initdata ) ) {
2011-10-13 23:14:26 +04:00
nmi_fail = FAILURE ;
return ;
}
/* sync above data before sending NMI */
wmb ( ) ;
apic - > send_IPI_mask ( mask , NMI_VECTOR ) ;
/* Don't wait longer than a second */
timeout = USEC_PER_SEC ;
while ( ! cpumask_empty ( mask ) & & timeout - - )
udelay ( 1 ) ;
/* What happens if we timeout, do we still unregister?? */
unregister_nmi_handler ( NMI_LOCAL , " nmi_selftest " ) ;
if ( ! timeout )
nmi_fail = TIMEOUT ;
return ;
}
2012-02-24 16:09:21 +04:00
static void __init remote_ipi ( void )
2011-10-13 23:14:26 +04:00
{
cpumask_copy ( to_cpumask ( nmi_ipi_mask ) , cpu_online_mask ) ;
cpumask_clear_cpu ( smp_processor_id ( ) , to_cpumask ( nmi_ipi_mask ) ) ;
2011-12-07 15:06:12 +04:00
if ( ! cpumask_empty ( to_cpumask ( nmi_ipi_mask ) ) )
2011-12-06 22:08:59 +04:00
test_nmi_ipi ( to_cpumask ( nmi_ipi_mask ) ) ;
2011-10-13 23:14:26 +04:00
}
2012-02-24 16:09:21 +04:00
static void __init local_ipi ( void )
2011-10-13 23:14:26 +04:00
{
cpumask_clear ( to_cpumask ( nmi_ipi_mask ) ) ;
cpumask_set_cpu ( smp_processor_id ( ) , to_cpumask ( nmi_ipi_mask ) ) ;
test_nmi_ipi ( to_cpumask ( nmi_ipi_mask ) ) ;
}
2012-02-24 16:09:21 +04:00
static void __init reset_nmi ( void )
2011-10-13 23:14:26 +04:00
{
nmi_fail = 0 ;
}
2012-02-24 16:09:21 +04:00
static void __init dotest ( void ( * testcase_fn ) ( void ) , int expected )
2011-10-13 23:14:26 +04:00
{
testcase_fn ( ) ;
/*
* Filter out expected failures :
*/
if ( nmi_fail ! = expected ) {
unexpected_testcase_failures + + ;
if ( nmi_fail = = FAILURE )
2012-05-08 19:56:12 +04:00
printk ( KERN_CONT " FAILED | " ) ;
2011-10-13 23:14:26 +04:00
else if ( nmi_fail = = TIMEOUT )
2012-05-08 19:56:12 +04:00
printk ( KERN_CONT " TIMEOUT| " ) ;
2011-10-13 23:14:26 +04:00
else
2012-05-08 19:56:12 +04:00
printk ( KERN_CONT " ERROR | " ) ;
2011-10-13 23:14:26 +04:00
dump_stack ( ) ;
} else {
testcase_successes + + ;
2012-05-08 19:56:12 +04:00
printk ( KERN_CONT " ok | " ) ;
2011-10-13 23:14:26 +04:00
}
testcase_total + + ;
reset_nmi ( ) ;
}
2012-02-24 16:09:21 +04:00
static inline void __init print_testname ( const char * testname )
2011-10-13 23:14:26 +04:00
{
printk ( " %12s: " , testname ) ;
}
2012-02-24 16:09:21 +04:00
void __init nmi_selftest ( void )
2011-10-13 23:14:26 +04:00
{
init_nmi_testsuite ( ) ;
/*
* Run the testsuite :
*/
printk ( " ---------------- \n " ) ;
printk ( " | NMI testsuite: \n " ) ;
printk ( " -------------------- \n " ) ;
print_testname ( " remote IPI " ) ;
dotest ( remote_ipi , SUCCESS ) ;
2012-05-08 19:56:12 +04:00
printk ( KERN_CONT " \n " ) ;
2011-10-13 23:14:26 +04:00
print_testname ( " local IPI " ) ;
dotest ( local_ipi , SUCCESS ) ;
2012-05-08 19:56:12 +04:00
printk ( KERN_CONT " \n " ) ;
2011-10-13 23:14:26 +04:00
cleanup_nmi_testsuite ( ) ;
if ( unexpected_testcase_failures ) {
printk ( " -------------------- \n " ) ;
printk ( " BUG: %3d unexpected failures (out of %3d) - debugging disabled! | \n " ,
unexpected_testcase_failures , testcase_total ) ;
printk ( " ----------------------------------------------------------------- \n " ) ;
} else if ( expected_testcase_failures & & testcase_successes ) {
printk ( " -------------------- \n " ) ;
printk ( " %3d out of %3d testcases failed, as expected. | \n " ,
expected_testcase_failures , testcase_total ) ;
printk ( " ---------------------------------------------------- \n " ) ;
} else if ( expected_testcase_failures & & ! testcase_successes ) {
printk ( " -------------------- \n " ) ;
printk ( " All %3d testcases failed, as expected. | \n " ,
expected_testcase_failures ) ;
printk ( " ---------------------------------------- \n " ) ;
} else {
printk ( " -------------------- \n " ) ;
printk ( " Good, all %3d testcases passed! | \n " ,
testcase_successes ) ;
printk ( " --------------------------------- \n " ) ;
}
}