File Index
Files and directories
Sign in
FILE

nmi_selftest.c

File preview with syntax highlighting.

linux-6.19.14/arch/x86/kernel/nmi_selftest.c C
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * Testsuite for NMI: IPIs
  4 *
  5 * Started by Don Zickus:
  6 * (using lib/locking-selftest.c as a guide)
  7 *
  8 *   Copyright (C) 2011 Red Hat, Inc., Don Zickus <dzickus@redhat.com>
  9 */
 10
 11#include <linux/smp.h>
 12#include <linux/cpumask.h>
 13#include <linux/delay.h>
 14#include <linux/init.h>
 15#include <linux/percpu.h>
 16
 17#include <asm/apic.h>
 18#include <asm/nmi.h>
 19
 20#define SUCCESS		0
 21#define FAILURE		1
 22#define TIMEOUT		2
 23
 24static int __initdata nmi_fail;
 25
 26/* check to see if NMI IPIs work on this machine */
 27static DECLARE_BITMAP(nmi_ipi_mask, NR_CPUS) __initdata;
 28
 29static int __initdata testcase_total;
 30static int __initdata testcase_successes;
 31static int __initdata unexpected_testcase_failures;
 32static int __initdata unexpected_testcase_unknowns;
 33
 34static int __init nmi_unk_cb(unsigned int val, struct pt_regs *regs)
 35{
 36	unexpected_testcase_unknowns++;
 37	return NMI_HANDLED;
 38}
 39
 40static void __init init_nmi_testsuite(void)
 41{
 42	/* trap all the unknown NMIs we may generate */
 43	register_nmi_handler(NMI_UNKNOWN, nmi_unk_cb, 0, "nmi_selftest_unk",
 44			__initdata);
 45}
 46
 47static void __init cleanup_nmi_testsuite(void)
 48{
 49	unregister_nmi_handler(NMI_UNKNOWN, "nmi_selftest_unk");
 50}
 51
 52static int __init test_nmi_ipi_callback(unsigned int val, struct pt_regs *regs)
 53{
 54        int cpu = raw_smp_processor_id();
 55
 56        if (cpumask_test_and_clear_cpu(cpu, to_cpumask(nmi_ipi_mask)))
 57                return NMI_HANDLED;
 58
 59        return NMI_DONE;
 60}
 61
 62static void __init test_nmi_ipi(struct cpumask *mask)
 63{
 64	unsigned long timeout;
 65
 66	if (register_nmi_handler(NMI_LOCAL, test_nmi_ipi_callback,
 67				 NMI_FLAG_FIRST, "nmi_selftest", __initdata)) {
 68		nmi_fail = FAILURE;
 69		return;
 70	}
 71
 72	/* sync above data before sending NMI */
 73	wmb();
 74
 75	__apic_send_IPI_mask(mask, NMI_VECTOR);
 76
 77	/* Don't wait longer than a second */
 78	timeout = USEC_PER_SEC;
 79	while (!cpumask_empty(mask) && --timeout)
 80	        udelay(1);
 81
 82	/* What happens if we timeout, do we still unregister?? */
 83	unregister_nmi_handler(NMI_LOCAL, "nmi_selftest");
 84
 85	if (!timeout)
 86		nmi_fail = TIMEOUT;
 87	return;
 88}
 89
 90static void __init remote_ipi(void)
 91{
 92	cpumask_copy(to_cpumask(nmi_ipi_mask), cpu_online_mask);
 93	cpumask_clear_cpu(smp_processor_id(), to_cpumask(nmi_ipi_mask));
 94	if (!cpumask_empty(to_cpumask(nmi_ipi_mask)))
 95		test_nmi_ipi(to_cpumask(nmi_ipi_mask));
 96}
 97
 98static void __init local_ipi(void)
 99{
100	cpumask_clear(to_cpumask(nmi_ipi_mask));
101	cpumask_set_cpu(smp_processor_id(), to_cpumask(nmi_ipi_mask));
102	test_nmi_ipi(to_cpumask(nmi_ipi_mask));
103}
104
105static void __init reset_nmi(void)
106{
107	nmi_fail = 0;
108}
109
110static void __init dotest(void (*testcase_fn)(void), int expected)
111{
112	testcase_fn();
113	/*
114	 * Filter out expected failures:
115	 */
116	if (nmi_fail != expected) {
117		unexpected_testcase_failures++;
118
119		if (nmi_fail == FAILURE)
120			pr_cont("FAILED |");
121		else if (nmi_fail == TIMEOUT)
122			pr_cont("TIMEOUT|");
123		else
124			pr_cont("ERROR  |");
125		dump_stack();
126	} else {
127		testcase_successes++;
128		pr_cont("  ok  |");
129	}
130	pr_cont("\n");
131
132	testcase_total++;
133	reset_nmi();
134}
135
136void __init nmi_selftest(void)
137{
138	init_nmi_testsuite();
139
140        /*
141	 * Run the testsuite:
142	 */
143	pr_info("----------------\n");
144	pr_info("| NMI testsuite:\n");
145	pr_info("--------------------\n");
146
147	pr_info("%12s:", "remote IPI");
148	dotest(remote_ipi, SUCCESS);
149
150	pr_info("%12s:", "local IPI");
151	dotest(local_ipi, SUCCESS);
152
153	cleanup_nmi_testsuite();
154
155	pr_info("--------------------\n");
156	if (unexpected_testcase_failures) {
157		pr_info("BUG: %3d unexpected failures (out of %3d) - debugging disabled! |\n",
158			unexpected_testcase_failures, testcase_total);
159	} else {
160		pr_info("Good, all %3d testcases passed! |\n",
161			testcase_successes);
162	}
163	pr_info("-----------------------------------------------------------------\n");
164}
~/ Read-only file preview.