[PATCH v8 3/7] crypto/ccp: Disable CPU hotplug while SNP is active

From: Ashish Kalra

Date: Mon Jun 15 2026 - 15:49:46 EST


From: Ashish Kalra <ashish.kalra@xxxxxxx>

The SEV firmware enumerates the CPUs at SNP initialization and is not
aware of the OS bringing CPUs online or offline afterwards, so OS CPU
hotplug can diverge from the firmware's expectations and break SNP.
Disable CPU hotplug while SNP is active.

SNP is fully torn down only on the SNP_SHUTDOWN_EX x86_snp_shutdown
path; the legacy path leaves SNP enabled in hardware while clearing
snp_initialized, so __sev_snp_init_locked() can run again. Track the
disable with a flag so it is balanced by a matching enable rather than
stacked, and re-enable hotplug only on the x86_snp_shutdown path, after
snp_shutdown() has cleared the per-core RMPOPT_BASE MSRs with hotplug
still disabled.

This also keeps the CPU set stable for the asynchronous RMPOPT scan
added later in this series, and ensures cpus_read_lock() in the scan
is uncontended.

Suggested-by: Thomas Lendacky <thomas.lendacky@xxxxxxx>
Signed-off-by: Ashish Kalra <ashish.kalra@xxxxxxx>
---
drivers/crypto/ccp/sev-dev.c | 29 ++++++++++++++++++++++++++++-
1 file changed, 28 insertions(+), 1 deletion(-)

diff --git a/drivers/crypto/ccp/sev-dev.c b/drivers/crypto/ccp/sev-dev.c
index 217b6b19802e..c8c3c577463c 100644
--- a/drivers/crypto/ccp/sev-dev.c
+++ b/drivers/crypto/ccp/sev-dev.c
@@ -106,6 +106,9 @@ struct snp_hv_fixed_pages_entry {

static LIST_HEAD(snp_hv_fixed_pages);

+/* Set while SNP has CPU hotplug disabled. */
+static bool snp_cpu_hotplug_disabled;
+
/* Trusted Memory Region (TMR):
* The TMR is a 1MB area that must be 1MB aligned. Use the page allocator
* to allocate the memory, which will return aligned memory for the specified
@@ -1479,6 +1482,17 @@ static int __sev_snp_init_locked(int *error, unsigned int max_snp_asid)

snp_hv_fixed_pages_state_update(sev, HV_FIXED);

+ /*
+ * Disable CPU hotplug while SNP is active. Guard against stacking
+ * the disable count: the legacy SNP_SHUTDOWN_EX path clears
+ * snp_initialized without re-enabling hotplug, so this can run
+ * again while hotplug is already disabled.
+ */
+ if (!snp_cpu_hotplug_disabled) {
+ cpu_hotplug_disable();
+ snp_cpu_hotplug_disabled = true;
+ }
+
snp_setup_rmpopt();

sev->snp_initialized = true;
@@ -2083,8 +2097,21 @@ static int __sev_snp_shutdown_locked(int *error, bool panic)
}

if (data.x86_snp_shutdown) {
- if (!panic)
+ if (!panic) {
snp_shutdown();
+ /*
+ * snp_shutdown() fully tears SNP down (clear_rmp()) and
+ * has already cleared the per-core RMPOPT_BASE MSRs via
+ * rmpopt_cleanup() with hotplug still disabled. Re-enable
+ * CPU hotplug now. On the legacy path SNP stays
+ * enabled in hardware, so hotplug is correctly left
+ * disabled.
+ */
+ if (snp_cpu_hotplug_disabled) {
+ cpu_hotplug_enable();
+ snp_cpu_hotplug_disabled = false;
+ }
+ }
snp_hv_fixed_pages_state_update(sev, ALLOCATED);
} else {
/*
--
2.43.0