[PATCH v2 2/3] x86/mce/inject: Avoid racy updates to MSR_K7_HWCR in toggle_hw_mce_inject()
From: Jim Mattson
Date: Fri Jun 12 2026 - 17:58:12 EST
toggle_hw_mce_inject() performs a read-modify-write of MSR_K7_HWCR as two
independent crosscalls: one to read the MSR and one to write it. Another
HWCR update on the target CPU can be lost if it occurs CPU between the two
crosscalls.
Replace the two crosscalls with a single crosscall that performs the
read-modify-write using the amd_update_hwcr() helper.
Opportunistically, replace the open-coded BIT(18) with a new
MSR_K7_HWCR_MCSTATUSWREN macro.
Fixes: 21690934d934 ("EDAC, mce_amd_inj: Enable direct writes to MCE MSRs")
Signed-off-by: Jim Mattson <jmattson@xxxxxxxxxx>
---
arch/x86/include/asm/msr-index.h | 2 ++
arch/x86/kernel/cpu/mce/inject.c | 34 +++++++++++++++++++++++---------
2 files changed, 27 insertions(+), 9 deletions(-)
diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h
index 18c4be75e927..e24a0a6b5d17 100644
--- a/arch/x86/include/asm/msr-index.h
+++ b/arch/x86/include/asm/msr-index.h
@@ -900,6 +900,8 @@
#define MSR_K7_HWCR_IRPERF_EN BIT_ULL(MSR_K7_HWCR_IRPERF_EN_BIT)
#define MSR_K7_HWCR_CPUID_USER_DIS_BIT 35
#define MSR_K7_HWCR_CPUID_USER_DIS BIT_ULL(MSR_K7_HWCR_CPUID_USER_DIS_BIT)
+#define MSR_K7_HWCR_MCSTATUSWREN_BIT 18
+#define MSR_K7_HWCR_MCSTATUSWREN BIT_ULL(MSR_K7_HWCR_MCSTATUSWREN_BIT)
#define MSR_K7_FID_VID_CTL 0xc0010041
#define MSR_K7_FID_VID_STATUS 0xc0010042
#define MSR_K7_HWCR_CPB_DIS_BIT 25
diff --git a/arch/x86/kernel/cpu/mce/inject.c b/arch/x86/kernel/cpu/mce/inject.c
index d02c4f556cd0..9d6e330fec61 100644
--- a/arch/x86/kernel/cpu/mce/inject.c
+++ b/arch/x86/kernel/cpu/mce/inject.c
@@ -30,6 +30,7 @@
#include <asm/mce.h>
#include <asm/msr.h>
#include <asm/nmi.h>
+#include <asm/processor.h>
#include <asm/smp.h>
#include "internal.h"
@@ -310,28 +311,43 @@ static struct notifier_block inject_nb = {
.notifier_call = mce_inject_raise,
};
+struct hwcr_update_info {
+ u64 clear;
+ u64 set;
+ int err;
+};
+
+static void ipi_update_hwcr(void *info)
+{
+ struct hwcr_update_info *ui = info;
+
+ ui->err = amd_update_hwcr(ui->clear, ui->set);
+}
+
/*
* Caller needs to be make sure this cpu doesn't disappear
* from under us, i.e.: get_cpu/put_cpu.
*/
static int toggle_hw_mce_inject(unsigned int cpu, bool enable)
{
- u32 l, h;
+ struct hwcr_update_info ui = {
+ .clear = enable ? 0 : MSR_K7_HWCR_MCSTATUSWREN,
+ .set = enable ? MSR_K7_HWCR_MCSTATUSWREN : 0,
+ };
int err;
- err = rdmsr_on_cpu(cpu, MSR_K7_HWCR, &l, &h);
+ err = smp_call_function_single(cpu, ipi_update_hwcr, &ui, 1);
if (err) {
- pr_err("%s: error reading HWCR\n", __func__);
+ pr_err("%s: error calling ipi_update_hwcr on CPU %d\n", __func__, cpu);
return err;
}
- enable ? (l |= BIT(18)) : (l &= ~BIT(18));
-
- err = wrmsr_on_cpu(cpu, MSR_K7_HWCR, l, h);
- if (err)
- pr_err("%s: error writing HWCR\n", __func__);
+ if (ui.err) {
+ pr_err("%s: error updating HWCR on CPU %d\n", __func__, cpu);
+ return ui.err;
+ }
- return err;
+ return 0;
}
static int __set_inj(const char *buf)
--
2.54.0.1136.gdb2ca164c4-goog