Commit d6680b00 authored by Ingo Molnar's avatar Ingo Molnar
Browse files

Merge branch 'x86/nmi' into x86/core, to merge dependent commits

Prepare to resolve conflicts with an upstream series of fixes that conflict
with pending x86 changes:

  6f5bf947 Merge tag 'its-for-linus-20250509' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip



Signed-off-by: default avatarIngo Molnar <mingo@kernel.org>
parents 1f82e8e1 f2e01dcf
Loading
Loading
Loading
Loading
+46 −3
Original line number Diff line number Diff line
@@ -14,12 +14,26 @@ extern void release_perfctr_nmi(unsigned int);
extern int reserve_evntsel_nmi(unsigned int);
extern void release_evntsel_nmi(unsigned int);

extern int unknown_nmi_panic;

#endif /* CONFIG_X86_LOCAL_APIC */

extern int unknown_nmi_panic;
extern int panic_on_unrecovered_nmi;
extern int panic_on_io_nmi;

/* NMI handler flags */
#define NMI_FLAG_FIRST	1

/**
 * enum - NMI types.
 * @NMI_LOCAL:    Local NMI, CPU-specific NMI generated by the Local APIC.
 * @NMI_UNKNOWN:  Unknown NMI, the source of the NMI may not be identified.
 * @NMI_SERR:     System Error NMI, typically triggered by PCI errors.
 * @NMI_IO_CHECK: I/O Check NMI, related to I/O errors.
 * @NMI_MAX:      Maximum value for NMI types.
 *
 * NMI types are used to categorize NMIs and to dispatch them to the
 * appropriate handler.
 */
enum {
	NMI_LOCAL=0,
	NMI_UNKNOWN,
@@ -28,6 +42,7 @@ enum {
	NMI_MAX
};

/* NMI handler return values */
#define NMI_DONE	0
#define NMI_HANDLED	1

@@ -41,6 +56,25 @@ struct nmiaction {
	const char		*name;
};

/**
 * register_nmi_handler - Register a handler for a specific NMI type
 * @t:    NMI type (e.g. NMI_LOCAL)
 * @fn:   The NMI handler
 * @fg:   Flags associated with the NMI handler
 * @n:    Name of the NMI handler
 * @init: Optional __init* attributes for struct nmiaction
 *
 * Adds the provided handler to the list of handlers for the specified
 * NMI type. Handlers flagged with NMI_FLAG_FIRST would be executed first.
 *
 * Sometimes the source of an NMI can't be reliably determined which
 * results in an NMI being tagged as "unknown". Register an additional
 * handler using the NMI type - NMI_UNKNOWN to handle such cases. The
 * caller would get one last chance to assume responsibility for the
 * NMI.
 *
 * Return: 0 on success, or an error code on failure.
 */
#define register_nmi_handler(t, fn, fg, n, init...)	\
({							\
	static struct nmiaction init fn##_na = {	\
@@ -54,7 +88,16 @@ struct nmiaction {

int __register_nmi_handler(unsigned int, struct nmiaction *);

void unregister_nmi_handler(unsigned int, const char *);
/**
 * unregister_nmi_handler - Unregister a handler for a specific NMI type
 * @type: NMI type (e.g. NMI_LOCAL)
 * @name: Name of the NMI handler used during registration
 *
 * Removes the handler associated with the specified NMI type from the
 * NMI handler list. The "name" is used as a lookup key to identify the
 * handler.
 */
void unregister_nmi_handler(unsigned int type, const char *name);

void set_emergency_nmi_handler(unsigned int type, nmi_handler_t handler);

+1 −0
Original line number Diff line number Diff line
@@ -292,6 +292,7 @@ struct x86_hyper_runtime {
 * @set_wallclock:		set time back to HW clock
 * @is_untracked_pat_range	exclude from PAT logic
 * @nmi_init			enable NMI on cpus
 * @get_nmi_reason		get the reason an NMI was received
 * @save_sched_clock_state:	save state for sched_clock() on suspend
 * @restore_sched_clock_state:	restore state for sched_clock() on resume
 * @apic_post_init:		adjust apic if needed
+0 −2
Original line number Diff line number Diff line
@@ -23,8 +23,6 @@
#include <asm/stacktrace.h>
#include <asm/unwind.h>

int panic_on_unrecovered_nmi;
int panic_on_io_nmi;
static int die_counter;

static struct pt_regs exec_summary_regs;
+43 −44
Original line number Diff line number Diff line
@@ -49,27 +49,20 @@ struct nmi_desc {
	struct list_head head;
};

static struct nmi_desc nmi_desc[NMI_MAX] = 
{
	{
		.lock = __RAW_SPIN_LOCK_UNLOCKED(&nmi_desc[0].lock),
		.head = LIST_HEAD_INIT(nmi_desc[0].head),
	},
	{
		.lock = __RAW_SPIN_LOCK_UNLOCKED(&nmi_desc[1].lock),
		.head = LIST_HEAD_INIT(nmi_desc[1].head),
	},
	{
		.lock = __RAW_SPIN_LOCK_UNLOCKED(&nmi_desc[2].lock),
		.head = LIST_HEAD_INIT(nmi_desc[2].head),
	},
	{
		.lock = __RAW_SPIN_LOCK_UNLOCKED(&nmi_desc[3].lock),
		.head = LIST_HEAD_INIT(nmi_desc[3].head),
	},
#define NMI_DESC_INIT(type) { \
	.lock = __RAW_SPIN_LOCK_UNLOCKED(&nmi_desc[type].lock), \
	.head = LIST_HEAD_INIT(nmi_desc[type].head), \
}

static struct nmi_desc nmi_desc[NMI_MAX] = {
	NMI_DESC_INIT(NMI_LOCAL),
	NMI_DESC_INIT(NMI_UNKNOWN),
	NMI_DESC_INIT(NMI_SERR),
	NMI_DESC_INIT(NMI_IO_CHECK),
};

#define nmi_to_desc(type) (&nmi_desc[type])

struct nmi_stats {
	unsigned int normal;
	unsigned int unknown;
@@ -91,6 +84,9 @@ static DEFINE_PER_CPU(struct nmi_stats, nmi_stats);
static int ignore_nmis __read_mostly;

int unknown_nmi_panic;
int panic_on_unrecovered_nmi;
int panic_on_io_nmi;

/*
 * Prevent NMI reason port (0x61) being accessed simultaneously, can
 * only be used in NMI handler.
@@ -104,8 +100,6 @@ static int __init setup_unknown_nmi_panic(char *str)
}
__setup("unknown_nmi_panic", setup_unknown_nmi_panic);

#define nmi_to_desc(type) (&nmi_desc[type])

static u64 nmi_longest_ns = 1 * NSEC_PER_MSEC;

static int __init nmi_warning_debugfs(void)
@@ -125,11 +119,11 @@ static void nmi_check_duration(struct nmiaction *action, u64 duration)

	action->max_duration = duration;

	remainder_ns = do_div(duration, (1000 * 1000));
	decimal_msecs = remainder_ns / 1000;
	/* Convert duration from nsec to msec */
	remainder_ns = do_div(duration, NSEC_PER_MSEC);
	decimal_msecs = remainder_ns / NSEC_PER_USEC;

	printk_ratelimited(KERN_INFO
		"INFO: NMI handler (%ps) took too long to run: %lld.%03d msecs\n",
	pr_info_ratelimited("INFO: NMI handler (%ps) took too long to run: %lld.%03d msecs\n",
			    action->handler, duration, decimal_msecs);
}

@@ -333,10 +327,9 @@ unknown_nmi_error(unsigned char reason, struct pt_regs *regs)
	int handled;

	/*
	 * Use 'false' as back-to-back NMIs are dealt with one level up.
	 * Of course this makes having multiple 'unknown' handlers useless
	 * as only the first one is ever run (unless it can actually determine
	 * if it caused the NMI)
	 * As a last resort, let the "unknown" handlers make a
	 * best-effort attempt to figure out if they can claim
	 * responsibility for this Unknown NMI.
	 */
	handled = nmi_handle(NMI_UNKNOWN, regs);
	if (handled) {
@@ -366,17 +359,18 @@ static noinstr void default_do_nmi(struct pt_regs *regs)
	bool b2b = false;

	/*
	 * CPU-specific NMI must be processed before non-CPU-specific
	 * NMI, otherwise we may lose it, because the CPU-specific
	 * NMI can not be detected/processed on other CPUs.
	 */

	/*
	 * Back-to-back NMIs are interesting because they can either
	 * be two NMI or more than two NMIs (any thing over two is dropped
	 * due to NMI being edge-triggered).  If this is the second half
	 * of the back-to-back NMI, assume we dropped things and process
	 * more handlers.  Otherwise reset the 'swallow' NMI behaviour
	 * Back-to-back NMIs are detected by comparing the RIP of the
	 * current NMI with that of the previous NMI. If it is the same,
	 * it is assumed that the CPU did not have a chance to jump back
	 * into a non-NMI context and execute code in between the two
	 * NMIs.
	 *
	 * They are interesting because even if there are more than two,
	 * only a maximum of two can be detected (anything over two is
	 * dropped due to NMI being edge-triggered). If this is the
	 * second half of the back-to-back NMI, assume we dropped things
	 * and process more handlers. Otherwise, reset the 'swallow' NMI
	 * behavior.
	 */
	if (regs->ip == __this_cpu_read(last_nmi_rip))
		b2b = true;
@@ -390,6 +384,11 @@ static noinstr void default_do_nmi(struct pt_regs *regs)
	if (microcode_nmi_handler_enabled() && microcode_nmi_handler())
		goto out;

	/*
	 * CPU-specific NMI must be processed before non-CPU-specific
	 * NMI, otherwise we may lose it, because the CPU-specific
	 * NMI can not be detected/processed on other CPUs.
	 */
	handled = nmi_handle(NMI_LOCAL, regs);
	__this_cpu_add(nmi_stats.normal, handled);
	if (handled) {
@@ -426,13 +425,14 @@ static noinstr void default_do_nmi(struct pt_regs *regs)
			pci_serr_error(reason, regs);
		else if (reason & NMI_REASON_IOCHK)
			io_check_error(reason, regs);
#ifdef CONFIG_X86_32

		/*
		 * Reassert NMI in case it became active
		 * meanwhile as it's edge-triggered:
		 */
		if (IS_ENABLED(CONFIG_X86_32))
			reassert_nmi();
#endif

		__this_cpu_add(nmi_stats.external, 1);
		raw_spin_unlock(&nmi_reason_lock);
		goto out;
@@ -751,4 +751,3 @@ void local_touch_nmi(void)
{
	__this_cpu_write(last_nmi_rip, 0);
}
EXPORT_SYMBOL_GPL(local_touch_nmi);
+16 −36
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0
/*
 * arch/x86/kernel/nmi-selftest.c
 *
 * Testsuite for NMI: IPIs
 *
 * Started by Don Zickus:
@@ -30,7 +28,6 @@ static DECLARE_BITMAP(nmi_ipi_mask, NR_CPUS) __initdata;

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;

@@ -120,26 +117,22 @@ static void __init dotest(void (*testcase_fn)(void), int expected)
		unexpected_testcase_failures++;

		if (nmi_fail == FAILURE)
			printk(KERN_CONT "FAILED |");
			pr_cont("FAILED |");
		else if (nmi_fail == TIMEOUT)
			printk(KERN_CONT "TIMEOUT|");
			pr_cont("TIMEOUT|");
		else
			printk(KERN_CONT "ERROR  |");
			pr_cont("ERROR  |");
		dump_stack();
	} else {
		testcase_successes++;
		printk(KERN_CONT "  ok  |");
		pr_cont("  ok  |");
	}
	testcase_total++;
	pr_cont("\n");

	testcase_total++;
	reset_nmi();
}

static inline void __init print_testname(const char *testname)
{
	printk("%12s:", testname);
}

void __init nmi_selftest(void)
{
	init_nmi_testsuite();
@@ -147,38 +140,25 @@ void __init nmi_selftest(void)
        /*
	 * Run the testsuite:
	 */
	printk("----------------\n");
	printk("| NMI testsuite:\n");
	printk("--------------------\n");
	pr_info("----------------\n");
	pr_info("| NMI testsuite:\n");
	pr_info("--------------------\n");

	print_testname("remote IPI");
	pr_info("%12s:", "remote IPI");
	dotest(remote_ipi, SUCCESS);
	printk(KERN_CONT "\n");
	print_testname("local IPI");

	pr_info("%12s:", "local IPI");
	dotest(local_ipi, SUCCESS);
	printk(KERN_CONT "\n");

	cleanup_nmi_testsuite();

	pr_info("--------------------\n");
	if (unexpected_testcase_failures) {
		printk("--------------------\n");
		printk("BUG: %3d unexpected failures (out of %3d) - debugging disabled! |\n",
		pr_info("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",
		pr_info("Good, all %3d testcases passed! |\n",
			testcase_successes);
		printk("---------------------------------\n");
	}
	pr_info("-----------------------------------------------------------------\n");
}
Loading