/* * 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 */ #include #include #include #include #include #define SUCCESS 0 #define FAILURE 1 #define TIMEOUT 2 static int nmi_fail; /* check to see if NMI IPIs work on this machine */ static DECLARE_BITMAP(nmi_ipi_mask, NR_CPUS) __read_mostly; static int testcase_total; static int testcase_successes; static int expected_testcase_failures; static int unexpected_testcase_failures; static int unexpected_testcase_unknowns; static int nmi_unk_cb(unsigned int val, struct pt_regs *regs) { unexpected_testcase_unknowns++; return NMI_HANDLED; } static void init_nmi_testsuite(void) { /* trap all the unknown NMIs we may generate */ register_nmi_handler(NMI_UNKNOWN, nmi_unk_cb, 0, "nmi_selftest_unk"); } static void cleanup_nmi_testsuite(void) { unregister_nmi_handler(NMI_UNKNOWN, "nmi_selftest_unk"); } static int test_nmi_ipi_callback(unsigned int val, struct pt_regs *regs) { int cpu = raw_smp_processor_id(); if (cpumask_test_and_clear_cpu(cpu, to_cpumask(nmi_ipi_mask))) return NMI_HANDLED; return NMI_DONE; } static void test_nmi_ipi(struct cpumask *mask) { unsigned long timeout; if (register_nmi_handler(NMI_LOCAL, test_nmi_ipi_callback, NMI_FLAG_FIRST, "nmi_selftest")) { 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; } static void remote_ipi(void) { cpumask_copy(to_cpumask(nmi_ipi_mask), cpu_online_mask); cpumask_clear_cpu(smp_processor_id(), to_cpumask(nmi_ipi_mask)); if (!cpumask_empty(nmi_ipi_mask)) test_nmi_ipi(to_cpumask(nmi_ipi_mask)); } static void local_ipi(void) { 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)); } static void reset_nmi(void) { nmi_fail = 0; } static void dotest(void (*testcase_fn)(void), int expected) { testcase_fn(); /* * Filter out expected failures: */ if (nmi_fail != expected) { unexpected_testcase_failures++; if (nmi_fail == FAILURE) printk("FAILED |"); else if (nmi_fail == TIMEOUT) printk("TIMEOUT|"); else printk("ERROR |"); dump_stack(); } else { testcase_successes++; printk(" ok |"); } testcase_total++; reset_nmi(); } static inline void print_testname(const char *testname) { printk("%12s:", testname); } void nmi_selftest(void) { init_nmi_testsuite(); /* * Run the testsuite: */ printk("----------------\n"); printk("| NMI testsuite:\n"); printk("--------------------\n"); print_testname("remote IPI"); dotest(remote_ipi, SUCCESS); printk("\n"); print_testname("local IPI"); dotest(local_ipi, SUCCESS); printk("\n"); 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"); } }