]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
x86/mce: Save and use APEI corrected threshold limit
authorYazen Ghannam <yazen.ghannam@amd.com>
Tue, 4 Nov 2025 14:55:45 +0000 (14:55 +0000)
committerBorislav Petkov (AMD) <bp@alien8.de>
Fri, 21 Nov 2025 09:32:28 +0000 (10:32 +0100)
The MCA threshold limit generally is not something that needs to change during
runtime. It is common for a system administrator to decide on a policy for
their managed systems.

If MCA thresholding is OS-managed, then the threshold limit must be set at
every boot. However, many systems allow the user to set a value in their BIOS.
And this is reported through an APEI HEST entry even if thresholding is not in
FW-First mode.

Use this value, if available, to set the OS-managed threshold limit.  Users
can still override it through sysfs if desired for testing or debug.

APEI is parsed after MCE is initialized. So reset the thresholding blocks
later to pick up the threshold limit.

Signed-off-by: Yazen Ghannam <yazen.ghannam@amd.com>
Signed-off-by: Borislav Petkov (AMD) <bp@alien8.de>
Link: https://lore.kernel.org/20251104-wip-mca-updates-v8-0-66c8eacf67b9@amd.com
arch/x86/include/asm/mce.h
arch/x86/kernel/acpi/apei.c
arch/x86/kernel/cpu/mce/amd.c
arch/x86/kernel/cpu/mce/internal.h
arch/x86/kernel/cpu/mce/threshold.c

index 7d6588195d563f6714bc6dba64a7ab801f85fc72..1cfbfff0be3fb218350637165a98488d36c5b3f4 100644 (file)
@@ -308,6 +308,12 @@ DECLARE_PER_CPU(struct mce, injectm);
 /* Disable CMCI/polling for MCA bank claimed by firmware */
 extern void mce_disable_bank(int bank);
 
+#ifdef CONFIG_X86_MCE_THRESHOLD
+void mce_save_apei_thr_limit(u32 thr_limit);
+#else
+static inline void mce_save_apei_thr_limit(u32 thr_limit) { }
+#endif /* CONFIG_X86_MCE_THRESHOLD */
+
 /*
  * Exception handler
  */
index 0916f00a992e1b3384840c99ebe0ddf78383b20b..e21419e686eb3459d1a97f317901080fb73cbb1b 100644 (file)
@@ -19,6 +19,8 @@ int arch_apei_enable_cmcff(struct acpi_hest_header *hest_hdr, void *data)
        if (!cmc->enabled)
                return 0;
 
+       mce_save_apei_thr_limit(cmc->notify.error_threshold_value);
+
        /*
         * We expect HEST to provide a list of MC banks that report errors
         * in firmware first mode. Otherwise, return non-zero value to
index 940d1a03356913f95cb5411af7c7ff3519bf70a2..af6e9b5ef2881a997559f6ebc9858b344bd4d0d5 100644 (file)
@@ -489,6 +489,18 @@ static void threshold_restart_bank(unsigned int bank, bool intr_en)
        }
 }
 
+/* Try to use the threshold limit reported through APEI. */
+static u16 get_thr_limit(void)
+{
+       u32 thr_limit = mce_get_apei_thr_limit();
+
+       /* Fallback to old default if APEI limit is not available. */
+       if (!thr_limit)
+               return THRESHOLD_MAX;
+
+       return min(thr_limit, THRESHOLD_MAX);
+}
+
 static void mce_threshold_block_init(struct threshold_block *b, int offset)
 {
        struct thresh_restart tr = {
@@ -497,7 +509,7 @@ static void mce_threshold_block_init(struct threshold_block *b, int offset)
                .lvt_off                = offset,
        };
 
-       b->threshold_limit              = THRESHOLD_MAX;
+       b->threshold_limit              = get_thr_limit();
        threshold_restart_block(&tr);
 };
 
@@ -1071,7 +1083,7 @@ static int allocate_threshold_blocks(unsigned int cpu, struct threshold_bank *tb
        b->address              = address;
        b->interrupt_enable     = 0;
        b->interrupt_capable    = lvt_interrupt_supported(bank, high);
-       b->threshold_limit      = THRESHOLD_MAX;
+       b->threshold_limit      = get_thr_limit();
 
        if (b->interrupt_capable) {
                default_attrs[2] = &interrupt_enable.attr;
@@ -1082,6 +1094,8 @@ static int allocate_threshold_blocks(unsigned int cpu, struct threshold_bank *tb
 
        list_add(&b->miscj, &tb->miscj);
 
+       mce_threshold_block_init(b, (high & MASK_LVTOFF_HI) >> 20);
+
        err = kobject_init_and_add(&b->kobj, &threshold_ktype, tb->kobj, get_name(cpu, bank, b));
        if (err)
                goto out_free;
index b0e00ec5cc8c9f5c8a3452490182879de5e89774..4cf16fa7c2607aee8770873658365e63b1e3cbdb 100644 (file)
@@ -67,6 +67,7 @@ void mce_track_storm(struct mce *mce);
 void mce_inherit_storm(unsigned int bank);
 bool mce_get_storm_mode(void);
 void mce_set_storm_mode(bool storm);
+u32  mce_get_apei_thr_limit(void);
 #else
 static inline void cmci_storm_begin(unsigned int bank) {}
 static inline void cmci_storm_end(unsigned int bank) {}
@@ -74,6 +75,7 @@ static inline void mce_track_storm(struct mce *mce) {}
 static inline void mce_inherit_storm(unsigned int bank) {}
 static inline bool mce_get_storm_mode(void) { return false; }
 static inline void mce_set_storm_mode(bool storm) {}
+static inline u32  mce_get_apei_thr_limit(void) { return 0; }
 #endif
 
 /*
index f4a007616468ecf83d693df815bc62b5458e8b01..eebaa633df807dfd62ebe9c26e6c9a353b6133f1 100644 (file)
 
 #include "internal.h"
 
+static u32 mce_apei_thr_limit;
+
+void mce_save_apei_thr_limit(u32 thr_limit)
+{
+       mce_apei_thr_limit = thr_limit;
+       pr_info("HEST corrected error threshold limit: %u\n", thr_limit);
+}
+
+u32 mce_get_apei_thr_limit(void)
+{
+       return mce_apei_thr_limit;
+}
+
 static void default_threshold_interrupt(void)
 {
        pr_err("Unexpected threshold interrupt at vector %x\n",