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:

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

Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
Ingo Molnar 2025-05-13 10:46:04 +02:00
commit d6680b0077
7 changed files with 121 additions and 107 deletions

View File

@ -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);

View File

@ -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

View File

@ -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;

View File

@ -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,12 +119,12 @@ 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",
action->handler, duration, decimal_msecs);
pr_info_ratelimited("INFO: NMI handler (%ps) took too long to run: %lld.%03d msecs\n",
action->handler, duration, decimal_msecs);
}
static int nmi_handle(unsigned int type, struct pt_regs *regs)
@ -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:
*/
reassert_nmi();
#endif
if (IS_ENABLED(CONFIG_X86_32))
reassert_nmi();
__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);

View File

@ -1,7 +1,5 @@
// 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 |");
}
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");
}

View File

@ -11,6 +11,7 @@
#include <linux/crash_dump.h>
#include <linux/dma-map-ops.h>
#include <linux/efi.h>
#include <linux/hugetlb.h>
#include <linux/ima.h>
#include <linux/init_ohci1394_dma.h>
#include <linux/initrd.h>
@ -18,21 +19,19 @@
#include <linux/memblock.h>
#include <linux/panic_notifier.h>
#include <linux/pci.h>
#include <linux/random.h>
#include <linux/root_dev.h>
#include <linux/hugetlb.h>
#include <linux/tboot.h>
#include <linux/usb/xhci-dbgp.h>
#include <linux/static_call.h>
#include <linux/swiotlb.h>
#include <linux/random.h>
#include <linux/tboot.h>
#include <linux/usb/xhci-dbgp.h>
#include <linux/vmalloc.h>
#include <uapi/linux/mount.h>
#include <xen/xen.h>
#include <asm/apic.h>
#include <asm/efi.h>
#include <asm/numa.h>
#include <asm/bios_ebda.h>
#include <asm/bugs.h>
#include <asm/cacheinfo.h>
@ -47,18 +46,16 @@
#include <asm/mce.h>
#include <asm/memtype.h>
#include <asm/mtrr.h>
#include <asm/realmode.h>
#include <asm/nmi.h>
#include <asm/numa.h>
#include <asm/olpc_ofw.h>
#include <asm/pci-direct.h>
#include <asm/prom.h>
#include <asm/proto.h>
#include <asm/realmode.h>
#include <asm/thermal.h>
#include <asm/unwind.h>
#include <asm/vsyscall.h>
#include <linux/vmalloc.h>
#if defined(CONFIG_X86_LOCAL_APIC)
#include <asm/nmi.h>
#endif
/*
* max_low_pfn_mapped: highest directly mapped pfn < 4 GB
@ -151,6 +148,13 @@ static size_t ima_kexec_buffer_size;
int bootloader_type, bootloader_version;
static const struct ctl_table x86_sysctl_table[] = {
{
.procname = "unknown_nmi_panic",
.data = &unknown_nmi_panic,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = proc_dointvec,
},
{
.procname = "panic_on_unrecovered_nmi",
.data = &panic_on_unrecovered_nmi,
@ -186,15 +190,6 @@ static const struct ctl_table x86_sysctl_table[] = {
.mode = 0644,
.proc_handler = proc_dointvec,
},
#if defined(CONFIG_X86_LOCAL_APIC)
{
.procname = "unknown_nmi_panic",
.data = &unknown_nmi_panic,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = proc_dointvec,
},
#endif
#if defined(CONFIG_ACPI_SLEEP)
{
.procname = "acpi_video_flags",

View File

@ -20,8 +20,6 @@ extern bool panic_triggering_all_cpu_backtrace;
extern int panic_timeout;
extern unsigned long panic_print;
extern int panic_on_oops;
extern int panic_on_unrecovered_nmi;
extern int panic_on_io_nmi;
extern int panic_on_warn;
extern unsigned long panic_on_taint;