Skip to contentSkip to conversation

Add RMPOPT support.

PATCH Superseded Ashish Kalra 5 participants 49 messages Jun 30 – Jul 23

Subsystems (6)
x86 KVM Crypto API KVM x86 +2 more subsystems

v10 of 16 Superseded Latest is v16 →

lore
Share & tools
lore

Ashish Kalra

author maintainer
#0 lore raw

v10 cover Add RMPOPT support.

From: Ashish Kalra <[email protected]>

In the SEV-SNP architecture, hypervisor and non-SNP guests are subject to RMP checks on writes to provide integrity of SEV-SNP guest memory.

The RMPOPT architecture enables optimizations whereby the RMP checks can be skipped if 1GB regions of memory are known to not contain any SNP guest memory.

RMPOPT is a new instruction designed to minimize the performance overhead of RMP checks for the hypervisor and non-SNP guests.

RMPOPT instruction currently supports two functions. In case of the verify and report status function the CPU will read the RMP contents, verify the entire 1GB region starting at the provided SPA is HV-owned. For the entire 1GB region it checks that all RMP entries in this region are HV-owned (i.e, not in assigned state) and then accordingly updates the RMPOPT table to indicate if optimization has been enabled and provide indication to software if the optimization was successful.

In case of report status function, the CPU returns the optimization status for the 1GB region.

The RMPOPT table is managed by a combination of software and hardware. Software uses the RMPOPT instruction to set bits in the table, indicating that regions of memory are entirely HV-owned. Hardware automatically clears bits in the RMPOPT table when RMP contents are changed during RMPUPDATE instruction.

For more information on the RMPOPT instruction, see the AMD64 RMPOPT technical documentation.

As SNP is enabled by default the hypervisor and non-SNP guests are subject to RMP write checks to provide integrity of SNP guest memory.

This patch-series adds support to enable RMP optimizations for up to 2TB of system RAM across the system and allow RMPUPDATE to disable those optimizations as SNP guests are launched.

Support for RAM larger than 2 TB will be added in follow-on series.

This series also adds support to disable CPU hotplug while SNP is active, as the SEV firmware enumerates CPUs at SNP initialization and is not aware of the OS bringing CPUs online or offline afterwards. This also keeps the set of CPUs stable for the asynchronous RMPOPT scan, so the per-core RMPOPT_BASE MSRs programmed during setup remain valid.

This series also introduces support to re-enable RMP optimizations during SNP guest termination, after guest pages have been converted back to shared.

RMP optimizations are performed asynchronously by queuing work on a dedicated workqueue after a 10 second delay.

Delaying work allows batching of multiple SNP guest terminations.

Once 1GB hugetlb guest_memfd support is merged, support for re-enabling RMPOPT optimizations during 1GB page cleanup will be added in follow-on series.

v10:
- Rework the CPU-hotplug patch (3/6): disable CPU hotplug in snp_prepare(), before SnpEn is set, instead of late in __sev_snp_init_locked(), so no CPU can come online without SnpEn during SNP initialization (per upstream review). Tie hotplug to SnpEn: it stays disabled while SnpEn is set -- including across a failed SNP_INIT and across the legacy SNP_SHUTDOWN_EX path -- and is re-enabled only once the firmware clears SnpEn on the x86_snp_shutdown path. Drop the separate idempotent flag: snp_prepare() re-enables hotplug on its own early failure, and a kexec target that boots with SnpEn already set disables hotplug once in snp_rmptable_init(). Reword the commit log and comments accordingly.
- Emit a pr_warn() in rmpopt_work_handler() (4/6) when the follower cpumask allocation fails, instead of silently skipping the optimization pass.

Sashiko AI upstream review identified several of the above issues.

v9:
- Rename rmpopt_configured to rmpopt_capable.
- Make rmpopt_cpumask a cpumask_var_t (allocated/freed at setup/cleanup) instead of a static cpumask_t.
- Drop the v8 WARN_ON_ONCE() on the RMPOPT_BASE writes; use a plain wrmsrq_on_cpu(), matching the SNP MSR-write convention in this file.
- Disable CPU hotplug with cpu_hotplug_disable()/cpu_hotplug_enable() (per tglx); re-enable only on the full x86_snp_shutdown path.
- Simplify rmpopt_work_handler() to a single leader-then-followers path:
with CPU hotplug disabled while SNP is active and snp_prepare() requiring all CPUs online when RMPOPT_BASE is programmed, every core is always programmed, so the explicit-leader fallback is now unreachable. Drop it along with the v8 work_on_cpu()/rmpopt_leader_fn() helper.
- Drop the debugfs interface (was patch 7/7) and its report-only plumbing; observability will be revisited after this series is merged.
- Restrict snp_rmpopt_all_physmem()'s export to the kvm-amd module.
- Use scoped_guard(cpus_read_lock) for the per-CPU MSR and follower loops.

Sashiko AI upstream review identified several of the above issues.

v8:
- Add a new patch to disable CPU hotplug while SNP is active, keeping the CPU set stable for the RMPOPT work handler.
- Drop the setup_clear_cpu_cap(X86_FEATURE_RMPOPT) calls; the rmpopt_configured bool is the runtime guard.
- WARN_ON_ONCE() on the RMPOPT_BASE MSR writes that previously ignored their return value.
- Simplify rmpopt_work_handler() by removing the explicit-leader
fallback: with CPU hotplug disabled while SNP is active and snp_prepare() requiring all CPUs online when RMPOPT_BASE is programmed, every core is always programmed, so the running CPU can always be the leader. This drops the smp_call_function_single() fallback (and with it the AB-BA deadlock and IRQ-latency concerns) and collapses the leader selection into a single leader-then-followers path.
- Use mod_delayed_work() in snp_rmpopt_all_physmem() so the batching delay tracks the last SNP guest termination.

Sashiko AI code review identified several of the above issues.

v7:
- Sync tools/arch/x86/include/asm/cpufeatures.h to mirror the kernel header for X86_FEATURE_RMPOPT.
- Fix commit title to use X86_FEATURE_RMPOPT to match the code (was X86_FEATURE_AMD_RMPOPT).
- Add static bool rmpopt_configured, set only when segmented RMP setup succeeds in setup_rmptable(). Check rmpopt_configured alongside cpu_feature_enabled(X86_FEATURE_RMPOPT) in snp_setup_rmpopt() and snp_rmpopt_all_physmem(), because setup_clear_cpu_cap() is unreliable after alternatives are patched. Add snp_clear_rmpopt_configured() called from amd_cc_platform_clear() when CC_ATTR_HOST_SEV_SNP is cleared. Do not use __ro_after_init on rmpopt_configured since the writer snp_clear_rmpopt_configured() is not __init.
- Add cond_resched() to all three leader loops in rmpopt_work_handler() to prevent soft lockups on systems with up to 2TB of RAM.
- Add comment above __rmpopt() documenting the RMPOPT instruction encoding (F2 0F 01 FC) and register interface (RAX = system physical address input, RCX = operation type input, RFLAGS.CF = output).
Note: RMPOPT does not modify RAX unlike PVALIDATE/RMPUPDATE, so the existing "a" (input-only) constraint is correct.

Sashiko AI code review identified several of the above issues.

v6:
- Drop wrmsrq_on_cpus() helper; use for_each_cpu() with wrmsrq_on_cpu() instead, as RMPOPT_BASE MSR programming is not performance-critical.
- Rewrite rmpopt_work_handler() leader selection to use a local follower_mask copy instead of modifying the global rmpopt_cpumask. This eliminates the current_cpu_cleared tracking and the restore at the end, and removes the need for synchronization comments about transient cpumask inconsistency.
- Add three-way leader selection in rmpopt_work_handler():
1. Current CPU is a primary thread in cpumask: run leader locally.
2. Current CPU is a sibling thread whose primary is in cpumask:
run leader locally (RMPOPT_BASE MSR is per-core), remove the primary from followers via cpumask_andnot(topology_sibling_cpumask).
3. Current CPU's core has no RMPOPT_BASE MSR programmed: pick an explicit leader via cpumask_first() + smp_call_function_single() to avoid #UD, with cpus_read_lock() around the IPI loop.
- Add WARN_ON_ONCE guard for empty cpumask in the explicit leader fallback path, with migrate_enable() before goto out.
- Add .llseek = seq_lseek to rmpopt_table_fops for consistency with other seq_file-based debugfs files and to support tools like "less".
- Change debugfs file permissions from 0444 to 0400 to restrict access to root only.
- Add comment in rmpopt_table_seq_show() explaining why cpu_online_mask is safe: RMPOPT_BASE MSR is per-core and snp_prepare() ensures all CPUs are online when the MSR is programmed.

Sashiko AI code review identified several of the above issues.

v5:
- Introduce rmpopt_cleanup() to tear down workqueue, debugfs, cpumask, and MSR state, called from snp_shutdown().
- Introduce rmpopt_wq_mutex to serialize snp_setup_rmpopt(), snp_rmpopt_all_physmem(), and rmpopt_cleanup().
- Introduce rmpopt_show_mutex to serialize debugfs reporting of rmpopt_report_cpumask.
- Move snp_rmpopt_all_physmem() call after SNP DECOMMISSION during guest shutdown.
- Use migrate_disable()/migrate_enable() for CPU pinning in the rmpopt_work_handler() leader loop to maintain CPU affinity without disabling preemption for the entire RMPOPT scan.
- Add cpus_read_lock()/cpus_read_unlock() around the follower on_each_cpu_mask() loop in rmpopt_work_handler().
- Guard snp_setup_rmpopt() against re-initialization when SNP_SHUTDOWN_EX with x86_snp_shutdown=0 skips rmpopt_cleanup() but clears snp_initialized, preventing workqueue and resource leaks on repeated init/shutdown cycles.
- Replace setup_clear_cpu_cap() with pr_err() on alloc_workqueue() failure in snp_setup_rmpopt(), as setup_clear_cpu_cap() cannot be used after alternatives are patched; callers check rmpopt_wq != NULL as the runtime guard instead.
- Add pr_info() when RMPOPT coverage is capped at 2TB.
- Add comments noting CPU hotplug is not supported with SNP enabled and only online primary threads are covered by rmpopt_cpumask.
- Add comment in setup_rmptable() noting Segmented RMP must be enabled to enable RMPOPT.
- Simplify cpumask setup loop to set if primary thread rather than skip if not primary.
- Improve grammar and clarity in snp_setup_rmpopt() comments.
- Added Reviewed-by's.

Sashiko AI code review identified several of the above issues.

v4:
- Add new wrmsrq_on_cpus() helper to write same u64 value to a per-CPU MSR across a cpumask without per-cpu struct allocation overhead.
- Rename configure_and_enable_rmpopt() to snp_setup_rmpopt().
- Use wrmsrq_on_cpus() instead of wrmsrq_on_cpu() loop for programming RMPOPT_BASE MSRs.
- Add setup_clear_cpu_cap(X86_FEATURE_RMPOPT) if segmented RMP setup fails or workqueue allocation fails.
- Add X86_FEATURE_RMPOPT feature clear logic in amd_cc_platform_clear() for CC_ATTR_HOST_SEV_SNP.
- All of the above allow checking for only X86_FEATURE_RMPOPT for both RMPOPT setup/enable and RMP re-optimizations.
- Rename snp_perform_rmp_optimization() to snp_rmpopt_all_physmem().
- Split rmpopt() into rmpopt() and rmpopt_smp() for SMP callback use.
- Introduce separate rmpopt_report_cpumask for debugfs reporting, distinct from rmpopt_cpumask used for primary thread tracking.
- Remove snp_perform_rmp_optimization() call from __sev_snp_init_locked() and instead setup and enable RMPOPT after SNP is enabled and initialized.

v3:
- Drop all RMPOPT kthread support and introduce adding custom and dedicated workqueue to schedule delayed and asynchronous RMPOPT work.
- Drop the guest_memfd inode cleanup interface and add support to re-enable RMP optimizations during guest shutdown using the asynchronous and delayed workqueue interface.
- Introduce new __rmpopt() helper and rmpopt() and rmpopt_report_status() wrappers on top which use rax and rcx parameters to closely match RMPOPT specs.
- Use new optimized RMPOPT loop to issue RMPOPT instructions on all system RAM upto 2TB and all CPUs, by optimizing each range on one CPU first, then let other CPUs execute RMPOPT in parallel so they can skip most work as the range has already been optimized.
- Also add support for running the optimized RMPOPT loop only on one thread per core.
- Replace all PUD_SIZE references with SZ_1G to conform to 1GB regions as specified by RMPOPT specifications and not be dependent on PUD_SIZE which makes the RMPOPT patch-set independent of x86 page table sizes.
- Use wrmsrq_on_cpu() to program the RMPOPT_BASE MSR registers on all CPUs that removes all ugly casting to use on_each_cpu_mask().
- Fix inline commits and patch commit messages

v2:
- Drop all NUMA and Socket configuration and enablement support and enable RMPOPT support for up to 2TB of system RAM.
- Drop get_cpumask_of_primary_threads() and enable per-core RMPOPT base MSRs and issue RMPOPT instruction on all CPUs.
- Drop the configfs interface to manually re-enable RMP optimizations.
- Add new guest_memfd cleanup interface to automatically re-enable RMP optimizations during guest shutdown.
- Include references to the public RMPOPT documentation.
- Move debugfs directory for RMPOPT under architecuture specific parent directory.

Ashish Kalra (6):
x86/cpufeatures: Add X86_FEATURE_RMPOPT feature flag x86/sev: Initialize RMPOPT configuration MSRs x86/sev: Disable CPU hotplug while SNP is active x86/sev: Add support to perform RMP optimizations asynchronously x86/sev: Add interface to re-enable RMP optimizations.
KVM: SEV: Perform RMP optimizations on SNP guest shutdown

 arch/x86/coco/core.c                     |   2 +
 arch/x86/include/asm/cpufeatures.h       |   2 +-
 arch/x86/include/asm/msr-index.h         |   3 +
 arch/x86/include/asm/sev.h               |   6 +
 arch/x86/kernel/cpu/scattered.c          |   1 +
 arch/x86/kvm/svm/sev.c                   |  10 +
 arch/x86/virt/svm/sev.c                  | 277 +++++++++++++++++++++++
 drivers/crypto/ccp/sev-dev.c             |   3 +
 tools/arch/x86/include/asm/cpufeatures.h |   2 +-
 9 files changed, 304 insertions(+), 2 deletions(-)
Signature · 1 line
2.43.0
author maintainer
#1 lore raw

PATCH v10 1/6 x86/cpufeatures: Add X86_FEATURE_RMPOPT feature flag

From: Ashish Kalra <[email protected]>

Add a flag indicating whether RMPOPT instruction is supported.

RMPOPT is a new instruction that reduces the performance overhead of RMP checks for the hypervisor and non-SNP guests by allowing those checks to be skipped when 1-GB memory regions are known to contain no SEV-SNP guest memory.

For more information on the RMPOPT instruction, see the AMD64 RMPOPT technical documentation.

---
 arch/x86/include/asm/cpufeatures.h       | 2 +-
 arch/x86/kernel/cpu/scattered.c          | 1 +
 tools/arch/x86/include/asm/cpufeatures.h | 2 +-
 3 files changed, 3 insertions(+), 2 deletions(-)
arch/x86/include/asm/cpufeatures.h +1 -1
index 1b4a48bff18f..14f23d19d864 100644
@@ -76,7 +76,7 @@
 #define X86_FEATURE_K8			( 3*32+ 4) /* Opteron, Athlon64 */
 #define X86_FEATURE_ZEN5		( 3*32+ 5) /* CPU based on Zen5 microarchitecture */
 #define X86_FEATURE_ZEN6		( 3*32+ 6) /* CPU based on Zen6 microarchitecture */
-/* Free                                 ( 3*32+ 7) */
+#define X86_FEATURE_RMPOPT		( 3*32+ 7) /* Support for AMD RMPOPT instruction */
 #define X86_FEATURE_CONSTANT_TSC	( 3*32+ 8) /* "constant_tsc" TSC ticks at a constant rate */
 #define X86_FEATURE_UP			( 3*32+ 9) /* "up" SMP kernel running on UP */
 #define X86_FEATURE_ART			( 3*32+10) /* "art" Always running timer (ART) */
arch/x86/kernel/cpu/scattered.c +1 -0
index 937129ce6a96..021c0bf22de2 100644
@@ -67,6 +67,7 @@ static const struct cpuid_bit cpuid_bits[] = {
 	{ X86_FEATURE_PERFMON_V2,		CPUID_EAX,  0, 0x80000022, 0 },
 	{ X86_FEATURE_AMD_LBR_V2,		CPUID_EAX,  1, 0x80000022, 0 },
 	{ X86_FEATURE_AMD_LBR_PMC_FREEZE,	CPUID_EAX,  2, 0x80000022, 0 },
+	{ X86_FEATURE_RMPOPT,			CPUID_EDX,  0, 0x80000025, 0 },
 	{ X86_FEATURE_AMD_HTR_CORES,		CPUID_EAX, 30, 0x80000026, 0 },
 	{ 0, 0, 0, 0, 0 }
 };
tools/arch/x86/include/asm/cpufeatures.h +1 -1
index 86d17b195e79..7ce681af1dd7 100644
@@ -76,7 +76,7 @@
 #define X86_FEATURE_K8			( 3*32+ 4) /* Opteron, Athlon64 */
 #define X86_FEATURE_ZEN5		( 3*32+ 5) /* CPU based on Zen5 microarchitecture */
 #define X86_FEATURE_ZEN6		( 3*32+ 6) /* CPU based on Zen6 microarchitecture */
-/* Free                                 ( 3*32+ 7) */
+#define X86_FEATURE_RMPOPT		( 3*32+ 7) /* Support for AMD RMPOPT instruction */
 #define X86_FEATURE_CONSTANT_TSC	( 3*32+ 8) /* "constant_tsc" TSC ticks at a constant rate */
 #define X86_FEATURE_UP			( 3*32+ 9) /* "up" SMP kernel running on UP */
 #define X86_FEATURE_ART			( 3*32+10) /* "art" Always running timer (ART) */
Signature · 1 line
2.43.0
maintainer Reviewed-by Dave Hansen Reviewed-by Ackerley Tng on v10 1/6
#2 lore raw

Re: [PATCH v10 1/6] x86/cpufeatures: Add X86_FEATURE_RMPOPT feature flag

Quoted 3 lines · Ashish Kalra
 arch/x86/include/asm/cpufeatures.h       | 2 +-
 arch/x86/kernel/cpu/scattered.c          | 1 +
 tools/arch/x86/include/asm/cpufeatures.h | 2 +-

Yeah, apparently we don't do the tools/ change anymore:

https://lore.kernel.org/r/[email protected]

Final version:

Author: Ashish Kalra <[email protected]>
Date:   Tue Jun 30 18:09:44 2026 +0000

x86/cpufeatures: Add X86_FEATURE_RMPOPT feature flag

Add a flag indicating whether RMPOPT instruction is supported.

RMPOPT is a new instruction that reduces the performance overhead of RMP checks for the hypervisor and non-SNP guests by allowing those checks to be skipped when 1-GB memory regions are known to contain no SEV-SNP guest memory.

For more information on the RMPOPT instruction, see the AMD64 RMPOPT technical documentation.

[ bp: Zap respective tools/ change. ]

arch/x86/include/asm/cpufeatures.h +1 -1
index 3d0940a3b9f3..dbccde9ee5cd 100644
@@ -76,7 +76,7 @@
 #define X86_FEATURE_K8			( 3*32+ 4) /* Opteron, Athlon64 */
 #define X86_FEATURE_ZEN5		( 3*32+ 5) /* CPU based on Zen5 microarchitecture */
 #define X86_FEATURE_ZEN6		( 3*32+ 6) /* CPU based on Zen6 microarchitecture */
-/* Free                                 ( 3*32+ 7) */
+#define X86_FEATURE_RMPOPT		( 3*32+ 7) /* Support for AMD RMPOPT instruction */
 #define X86_FEATURE_CONSTANT_TSC	( 3*32+ 8) /* "constant_tsc" TSC ticks at a constant rate */
 /* free: was #define X86_FEATURE_UP	( 3*32+ 9) * "up" SMP kernel running on UP */
 #define X86_FEATURE_ART			( 3*32+10) /* "art" Always running timer (ART) */
arch/x86/kernel/cpu/scattered.c +1 -0
index 937129ce6a96..021c0bf22de2 100644
@@ -67,6 +67,7 @@ static const struct cpuid_bit cpuid_bits[] = {
 	{ X86_FEATURE_PERFMON_V2,		CPUID_EAX,  0, 0x80000022, 0 },
 	{ X86_FEATURE_AMD_LBR_V2,		CPUID_EAX,  1, 0x80000022, 0 },
 	{ X86_FEATURE_AMD_LBR_PMC_FREEZE,	CPUID_EAX,  2, 0x80000022, 0 },
+	{ X86_FEATURE_RMPOPT,			CPUID_EDX,  0, 0x80000025, 0 },
 	{ X86_FEATURE_AMD_HTR_CORES,		CPUID_EAX, 30, 0x80000026, 0 },
 	{ 0, 0, 0, 0, 0 }
 };
Signature · 4 lines
Regards/Gruss,
    Boris.

https://people.kernel.org/tglx/notes-about-netiquette
author maintainer
#3 lore raw

PATCH v10 2/6 x86/sev: Initialize RMPOPT configuration MSRs

In reply to Ashish Kalra

From: Ashish Kalra <[email protected]>

The new RMPOPT instruction helps manage per-CPU RMP optimization structures inside the CPU. It takes a 1GB-aligned physical address and either returns the status of the optimizations or tries to enable the optimizations.

Per-CPU RMPOPT tables support at most 2 TB of addressable memory for RMP optimizations.

Initialize the per-CPU RMPOPT table base to the starting physical address. This enables RMP optimization for up to 2 TB of system RAM on all CPUs.

Additionally, add support to setup and enable RMPOPT once SNP is enabled and initialized.

---
 arch/x86/coco/core.c             |  2 +
 arch/x86/include/asm/msr-index.h |  3 ++
 arch/x86/include/asm/sev.h       |  4 ++
 arch/x86/virt/svm/sev.c          | 70 ++++++++++++++++++++++++++++++++
 drivers/crypto/ccp/sev-dev.c     |  3 ++
 5 files changed, 82 insertions(+)
arch/x86/coco/core.c +2 -0
index 989ca9f72ba3..f0ed6c62d86c 100644
@@ -16,6 +16,7 @@
 #include <asm/archrandom.h>
 #include <asm/coco.h>
 #include <asm/processor.h>
+#include <asm/sev.h>
 
 enum cc_vendor cc_vendor __ro_after_init = CC_VENDOR_NONE;
 SYM_PIC_ALIAS(cc_vendor);
@@ -172,6 +173,7 @@ static void amd_cc_platform_clear(enum cc_attr attr)
 	switch (attr) {
 	case CC_ATTR_HOST_SEV_SNP:
 		cc_flags.host_sev_snp = 0;
+		snp_clear_rmpopt_capable();
 		break;
 	default:
 		break;
arch/x86/include/asm/msr-index.h +3 -0
index 18c4be75e927..d2cb0a7cd0a2 100644
@@ -761,6 +761,9 @@
 #define MSR_AMD64_SEG_RMP_ENABLED_BIT	0
 #define MSR_AMD64_SEG_RMP_ENABLED	BIT_ULL(MSR_AMD64_SEG_RMP_ENABLED_BIT)
 #define MSR_AMD64_RMP_SEGMENT_SHIFT(x)	(((x) & GENMASK_ULL(13, 8)) >> 8)
+#define MSR_AMD64_RMPOPT_BASE		0xc0010139
+#define MSR_AMD64_RMPOPT_ENABLE_BIT	0
+#define MSR_AMD64_RMPOPT_ENABLE		BIT_ULL(MSR_AMD64_RMPOPT_ENABLE_BIT)
 
 #define MSR_SVSM_CAA			0xc001f000
 
arch/x86/include/asm/sev.h +4 -0
index 594cfa19cbd4..0243989f229b 100644
@@ -662,6 +662,8 @@ static inline void snp_leak_pages(u64 pfn, unsigned int pages)
 	__snp_leak_pages(pfn, pages, true);
 }
 int snp_prepare(void);
+void snp_setup_rmpopt(void);
+void snp_clear_rmpopt_capable(void);
 void snp_shutdown(void);
 #else
 static inline bool snp_probe_rmptable_info(void) { return false; }
@@ -680,6 +682,8 @@ static inline void snp_leak_pages(u64 pfn, unsigned int npages) {}
 static inline void kdump_sev_callback(void) { }
 static inline void snp_fixup_e820_tables(void) {}
 static inline int snp_prepare(void) { return -ENODEV; }
+static inline void snp_setup_rmpopt(void) {}
+static inline void snp_clear_rmpopt_capable(void) {}
 static inline void snp_shutdown(void) {}
 #endif
 
arch/x86/virt/svm/sev.c +70 -0

99 lines not on this page · raw message

drivers/crypto/ccp/sev-dev.c +3 -0
index ca473ca198b8..c002a7ca26a8 100644
@@ -1477,6 +1477,9 @@ static int __sev_snp_init_locked(int *error, unsigned int max_snp_asid)
 	}
 
 	snp_hv_fixed_pages_state_update(sev, HV_FIXED);
+
+	snp_setup_rmpopt();
+
 	sev->snp_initialized = true;
 	dev_dbg(sev->dev, "SEV-SNP firmware initialized, SEV-TIO is %s\n",
 		data.tio_en ? "enabled" : "disabled");
Signature · 1 line
2.43.0
maintainer
#4 lore raw

Re: [PATCH v10 2/6] x86/sev: Initialize RMPOPT configuration MSRs

arch/x86/virt/svm/sev.c static bool __init setup_rmptable(void) · line 501

Quoted 9 lines · Ashish Kalra
arch/x86/virt/svm/sev.c +5 -0
@@ -490,6 +494,11 @@ static bool __init setup_rmptable(void)
 	if (rmp_cfg & MSR_AMD64_SEG_RMP_ENABLED) {
 		if (!setup_segmented_rmptable())
 			return false;
+		/*
+		 * RMPOPT requires a segmented RMP, so indicate that the
+		 * system is capable of configuring and running RMPOPT.
+		 */
+		rmpopt_capable = true;

So we're capable of doing RMPOPT when setup_segmented_rmptable() has succeeded. Which means, when rmp_segment_table is not NULL, i.e., when we have a segmented table.

Which means that instead of testing rmpopt_capable, you need to test CC_ATTR_HOST_SEV_SNP and rmp_segment_table != NULL and you can put that in a helper local to arch/x86/virt/svm/sev.c

Which means, you don't need that bool.

Signature · 4 lines
Regards/Gruss,
    Boris.

https://people.kernel.org/tglx/notes-about-netiquette
author maintainer
#5 lore raw

Re: [PATCH v10 2/6] x86/sev: Initialize RMPOPT configuration MSRs

Hello Boris,

Quoted 15 lines · Borislav Petkov

arch/x86/virt/svm/sev.c static bool __init setup_rmptable(void) · line 501

Quoted 9 lines · Ashish Kalra
arch/x86/virt/svm/sev.c +5 -0
@@ -490,6 +494,11 @@ static bool __init setup_rmptable(void)
 	if (rmp_cfg & MSR_AMD64_SEG_RMP_ENABLED) {
 		if (!setup_segmented_rmptable())
 			return false;
+		/*
+		 * RMPOPT requires a segmented RMP, so indicate that the
+		 * system is capable of configuring and running RMPOPT.
+		 */
+		rmpopt_capable = true;

So we're capable of doing RMPOPT when setup_segmented_rmptable() has succeeded. Which means, when rmp_segment_table is not NULL, i.e., when we have a segmented table.

Quoted 6 lines

Which means that instead of testing rmpopt_capable, you need to test CC_ATTR_HOST_SEV_SNP and rmp_segment_table != NULL and you can put that in a helper local to arch/x86/virt/svm/sev.c

Which means, you don't need that bool.

Agreed on dropping the bool — I'll derive it in a local helper.

One issue though: rmp_segment_table != NULL isn't segmented-only. setup_contiguous_rmptable() also allocates rmp_segment_table (the contiguous RMP is stored as a single segment in the same table), so it's non-NULL for the contiguous case too.

To keep it segmented-only, I'll also need to gate on the segmented-RMP mode, something like:

  static bool rmpopt_capable(void)
  {
        return cpu_feature_enabled(X86_FEATURE_RMPOPT) &&
               cc_platform_has(CC_ATTR_HOST_SEV_SNP) &&
               (rmp_cfg & MSR_AMD64_SEG_RMP_ENABLED) &&
               rmp_segment_table;
  }

The CC_ATTR_HOST_SEV_SNP check also handles SNP being disabled at runtime, so snp_clear_rmpopt_capable() and its caller go away as well.

Thanks,
Ashish

maintainer
#6 lore raw

Re: [PATCH v10 2/6] x86/sev: Initialize RMPOPT configuration MSRs

Quoted 15 lines · Ashish

Agreed on dropping the bool — I'll derive it in a local helper.

One issue though: rmp_segment_table != NULL isn't segmented-only. setup_contiguous_rmptable() also allocates rmp_segment_table (the contiguous RMP is stored as a single segment in the same table), so it's non-NULL for the contiguous case too.

To keep it segmented-only, I'll also need to gate on the segmented-RMP mode, something like:

  static bool rmpopt_capable(void)
  {
        return cpu_feature_enabled(X86_FEATURE_RMPOPT) &&
               cc_platform_has(CC_ATTR_HOST_SEV_SNP) &&
               (rmp_cfg & MSR_AMD64_SEG_RMP_ENABLED) &&
               rmp_segment_table;
  }
Quoted 1 line

The CC_ATTR_HOST_SEV_SNP check also handles SNP being disabled at runtime, so snp_clear_rmpopt_capable() and its caller go away as well.

Ok, you can simply do:

setup_clear_cpu_cap(X86_FEATURE_RMPOPT)

and clear our internal representation of RMPOPT support and make it a lot simpler.

Signature · 4 lines
Regards/Gruss,
    Boris.

https://people.kernel.org/tglx/notes-about-netiquette
author maintainer
#7 lore raw

Re: [PATCH v10 2/6] x86/sev: Initialize RMPOPT configuration MSRs

Hello Boris,

Quoted 16 lines · Borislav Petkov
Quoted 15 lines · Ashish

Agreed on dropping the bool — I'll derive it in a local helper.

One issue though: rmp_segment_table != NULL isn't segmented-only. setup_contiguous_rmptable() also allocates rmp_segment_table (the contiguous RMP is stored as a single segment in the same table), so it's non-NULL for the contiguous case too.

To keep it segmented-only, I'll also need to gate on the segmented-RMP mode, something like:

  static bool rmpopt_capable(void)
  {
        return cpu_feature_enabled(X86_FEATURE_RMPOPT) &&
               cc_platform_has(CC_ATTR_HOST_SEV_SNP) &&
               (rmp_cfg & MSR_AMD64_SEG_RMP_ENABLED) &&
               rmp_segment_table;
  }
Quoted 2 lines
Quoted 1 line

The CC_ATTR_HOST_SEV_SNP check also handles SNP being disabled at runtime, so snp_clear_rmpopt_capable() and its caller go away as well.

Ok, you can simply do:

Quoted 6 lines

setup_clear_cpu_cap(X86_FEATURE_RMPOPT)

and clear our internal representation of RMPOPT support and make it a lot simpler.

We did exactly that in v4, but had to drop it in v7 (flagged during Sashiko review) because it's unreliable here — we only find out whether RMPOPT is usable after alternatives are patched — too late for setup_clear_cpu_cap() to take effect.

Segmented RMP (which RMPOPT requires) is only determined in setup_rmptable(), reached via:

rootfs_initcall(pci_iommu_init)

    -> amd_iommu_init() 
	-> state_next() 
		-> iommu_snp_enable()
      			-> snp_rmptable_init() 
				-> setup_rmptable()

That's a rootfs_initcall, which runs after alternative_instructions(). By then static_cpu_has(X86_FEATURE_RMPOPT) is already patched, so a setup_clear_cpu_cap() at that point doesn't take effect and cpu_feature_enabled(X86_FEATURE_RMPOPT) keeps returning true.

That's why v7 moved to a runtime check. In v11 (as i mentioned) this will be a small local helper, it will also handles two things the cap-clear can't:
- a contiguous (non-segmented) RMP, where RMPOPT isn't usable even with X86_FEATURE_RMPOPT set.
- SNP being disabled at runtime (CC_ATTR_HOST_SEV_SNP).

  static bool rmpopt_capable(void)
  {
        return cpu_feature_enabled(X86_FEATURE_RMPOPT) &&
               cc_platform_has(CC_ATTR_HOST_SEV_SNP) &&
               (rmp_cfg & MSR_AMD64_SEG_RMP_ENABLED) &&
               rmp_segment_table;
  }

Thanks,
Ashish

#8 lore raw

Re: [PATCH v10 2/6] x86/sev: Initialize RMPOPT configuration MSRs

Hello Ashish,

Quoted 9 lines · Ashish

That's why v7 moved to a runtime check. In v11 (as i mentioned) this will be a small local helper, it will also handles two things the cap-clear can't:
- a contiguous (non-segmented) RMP, where RMPOPT isn't usable even with X86_FEATURE_RMPOPT set.
- SNP being disabled at runtime (CC_ATTR_HOST_SEV_SNP).

  static bool rmpopt_capable(void)
  {
        return cpu_feature_enabled(X86_FEATURE_RMPOPT) &&
               cc_platform_has(CC_ATTR_HOST_SEV_SNP) &&

CC_ATTR_HOST_SEV_SNP is set from bsp_determine_snp() ...

Quoted 1 line
               (rmp_cfg & MSR_AMD64_SEG_RMP_ENABLED) &&

... and that already checks snp_probe_rmptable_info() ...

Quoted 1 line
               rmp_segment_table;

.. and if iommu_snp_enable() fails snp_rmptable_init(), we clear CC_ATTR_HOST_SEV_SNP.

So, the way I see it, CC_ATTR_HOST_SEV_SNP being set means we have already tried provisioning RMP table.

I'm assuming all platforms that have X86_FEATURE_RMPOPT will all have segmented RMP. If not, you can just clear X86_FEATURE_RMPOPT in snp_rmptable_init() no?

We can never reach functions that need rmpopt_capable() without first checking CC_ATTR_HOST_SEV_SNP.

Signature · 2 lines
Thanks and Regards,
Prateek
author maintainer
#9 lore raw

Re: [PATCH v10 2/6] x86/sev: Initialize RMPOPT configuration MSRs

Hello Prateek,

Quoted 19 lines · K Prateek Nayak

Hello Ashish,

Quoted 9 lines · Ashish

That's why v7 moved to a runtime check. In v11 (as i mentioned) this will be a small local helper, it will also handles two things the cap-clear can't:
- a contiguous (non-segmented) RMP, where RMPOPT isn't usable even with X86_FEATURE_RMPOPT set.
- SNP being disabled at runtime (CC_ATTR_HOST_SEV_SNP).

  static bool rmpopt_capable(void)
  {
        return cpu_feature_enabled(X86_FEATURE_RMPOPT) &&
               cc_platform_has(CC_ATTR_HOST_SEV_SNP) &&

CC_ATTR_HOST_SEV_SNP is set from bsp_determine_snp() ...

Quoted 1 line
               (rmp_cfg & MSR_AMD64_SEG_RMP_ENABLED) &&

... and that already checks snp_probe_rmptable_info() ...

Quoted 7 lines
Quoted 1 line
               rmp_segment_table;

.. and if iommu_snp_enable() fails snp_rmptable_init(), we clear CC_ATTR_HOST_SEV_SNP.

So, the way I see it, CC_ATTR_HOST_SEV_SNP being set means we have already tried provisioning RMP table.

Agreed, and that makes rmp_segment_table redundant — iommu_snp_enable() clears CC_ATTR_HOST_SEV_SNP when snp_rmptable_init() fails, so the attr being set already implies rmp_segment_table != NULL. I'll drop it.

The one I'd keep is (rmp_cfg & MSR_AMD64_SEG_RMP_ENABLED). It's a runtime config bit, not the CPUID feature:
setup_rmptable() allocates rmp_segment_table and keeps CC_ATTR_HOST_SEV_SNP set for both segmented and contiguous RMP, so the MSR bit is the only thing that distinguishes them — and RMPOPT needs a segmented RMP.

Clearing X86_FEATURE_RMPOPT in snp_rmptable_init() won't work on its own as i mentioned in this reply earlier: it runs at rootfs_initcall, after alternative_instructions(), so cpu_feature_enabled() keeps returning true off the already-patched static_cpu_has().

So the helper becomes:

  static bool rmpopt_capable(void)
  {
        return cpu_feature_enabled(X86_FEATURE_RMPOPT) &&
               cc_platform_has(CC_ATTR_HOST_SEV_SNP) &&
               (rmp_cfg & MSR_AMD64_SEG_RMP_ENABLED);
  }

Thanks,
Ashish

Quoted 8 lines

I'm assuming all platforms that have X86_FEATURE_RMPOPT will all have segmented RMP. If not, you can just clear X86_FEATURE_RMPOPT in snp_rmptable_init() no?

We can never reach functions that need rmpopt_capable() without first checking CC_ATTR_HOST_SEV_SNP.

#10 lore raw

Re: [PATCH v10 2/6] x86/sev: Initialize RMPOPT configuration MSRs

Hello Ashish,

Quoted 7 lines · Ashish

The one I'd keep is (rmp_cfg & MSR_AMD64_SEG_RMP_ENABLED). It's a runtime config bit, not the CPUID feature:
setup_rmptable() allocates rmp_segment_table and keeps CC_ATTR_HOST_SEV_SNP set for both segmented and contiguous RMP, so the MSR bit is the only thing that distinguishes them — and RMPOPT needs a segmented RMP.

Clearing X86_FEATURE_RMPOPT in snp_rmptable_init() won't work on its own as i mentioned in this reply earlier: it runs at rootfs_initcall, after alternative_instructions(), so cpu_feature_enabled() keeps returning true off the already-patched static_cpu_has().

My bad! I actually meant in snp_probe_rmptable_info() during bsp_determine_snp(). Something like:

arch/x86/virt/svm/sev.c +4 -2
index 8bcdce98f6dc..3ad53ef16c2f 100644
@@ -684,10 +684,12 @@ bool snp_probe_rmptable_info(void)
 	if (cpu_feature_enabled(X86_FEATURE_SEGMENTED_RMP))
 		rdmsrq(MSR_AMD64_RMP_CFG, rmp_cfg);
 
-	if (rmp_cfg & MSR_AMD64_SEG_RMP_ENABLED)
+	if (rmp_cfg & MSR_AMD64_SEG_RMP_ENABLED) {
 		return probe_segmented_rmptable_info();
-	else
+	} else {
+		setup_clear_cpu_cap(X86_FEATURE_RMPOPT);
 		return probe_contiguous_rmptable_info();
+	}
 }
 
 /*

---

Based on a quick look, it gets only called at bsp init and is early enough to allow patching alternatives and I don't see anything writing to MSR_AMD64_RMP_CFG.

Thoughts?

Signature · 2 lines
Thanks and Regards,
Prateek
author maintainer
#11 lore raw

Re: [PATCH v10 2/6] x86/sev: Initialize RMPOPT configuration MSRs

Quoted 35 lines · K Prateek Nayak

Hello Ashish,

Quoted 7 lines · Ashish

The one I'd keep is (rmp_cfg & MSR_AMD64_SEG_RMP_ENABLED). It's a runtime config bit, not the CPUID feature:
setup_rmptable() allocates rmp_segment_table and keeps CC_ATTR_HOST_SEV_SNP set for both segmented and contiguous RMP, so the MSR bit is the only thing that distinguishes them — and RMPOPT needs a segmented RMP.

Clearing X86_FEATURE_RMPOPT in snp_rmptable_init() won't work on its own as i mentioned in this reply earlier: it runs at rootfs_initcall, after alternative_instructions(), so cpu_feature_enabled() keeps returning true off the already-patched static_cpu_has().

My bad! I actually meant in snp_probe_rmptable_info() during bsp_determine_snp(). Something like:

arch/x86/virt/svm/sev.c +4 -3
index 8bcdce98f6dc..3ad53ef16c2f 100644
@@ -684,10 +684,12 @@ bool snp_probe_rmptable_info(void)
 	if (cpu_feature_enabled(X86_FEATURE_SEGMENTED_RMP))
 		rdmsrq(MSR_AMD64_RMP_CFG, rmp_cfg);
 
-	if (rmp_cfg & MSR_AMD64_SEG_RMP_ENABLED)
+	if (rmp_cfg & MSR_AMD64_SEG_RMP_ENABLED) {
 		return probe_segmented_rmptable_info();
-	else
+	} else {
+		setup_clear_cpu_cap(X86_FEATURE_RMPOPT);
 		return probe_contiguous_rmptable_info();
+	}
 }
 
 /*
---
Quoted 6 lines

Based on a quick look, it gets only called at bsp init and is early enough to allow patching alternatives and I don't see anything writing to MSR_AMD64_RMP_CFG.

Thoughts?

Ah, that works — thanks.

With the contiguous case cleared early, X86_FEATURE_RMPOPT set now implies a segmented RMP, so the MSR-bit test drops out and the helper becomes:

  static bool rmpopt_capable(void)
  {
        return cpu_feature_enabled(X86_FEATURE_RMPOPT) &&
               cc_platform_has(CC_ATTR_HOST_SEV_SNP);
  }

I'll keep cc_platform_has(CC_ATTR_HOST_SEV_SNP) for the late failures the early clear can't cover: setup_segmented_rmptable() can still fail in snp_rmptable_init() (rootfs_initcall, i.e. post-alternatives, where clearing the feature bit wouldn't stick) — and on that path iommu_snp_enable() clears CC_ATTR_HOST_SEV_SNP, so the helper returns false. Same term also covers SNP being disabled at runtime.

Thanks,
Ashish

maintainer
#12 lore raw

Re: [PATCH v10 2/6] x86/sev: Initialize RMPOPT configuration MSRs

In reply to Kalra, Ashish
Quoted 3 lines · Ashish

That's why v7 moved to a runtime check. In v11 (as i mentioned) this will be a small local helper, it will also handles two things the cap-clear can't:
- a contiguous (non-segmented) RMP, where RMPOPT isn't usable even with X86_FEATURE_RMPOPT set.

That's the point of clearing the flag - to mark it non-usable.

Quoted 1 line

- SNP being disabled at runtime (CC_ATTR_HOST_SEV_SNP).

Same as RMPOPT being not usable follows from disabled SNP.

Quoted 7 lines
  static bool rmpopt_capable(void)
  {
        return cpu_feature_enabled(X86_FEATURE_RMPOPT) &&
               cc_platform_has(CC_ATTR_HOST_SEV_SNP) &&
               (rmp_cfg & MSR_AMD64_SEG_RMP_ENABLED) &&
               rmp_segment_table;
  }

But yes, this is a local helper so that's good enough.

We can always change it later if needed.

Thx.

Signature · 4 lines
Regards/Gruss,
    Boris.

https://people.kernel.org/tglx/notes-about-netiquette
maintainer
#13 lore raw

Re: [PATCH v10 2/6] x86/sev: Initialize RMPOPT configuration MSRs

In reply to Ashish Kalra

arch/x86/virt/svm/sev.c int snp_prepare(void) · line 576

Quoted 10 lines · Ashish Kalra
arch/x86/virt/svm/sev.c +10 -0
+static void rmpopt_cleanup(void)
+{
+	int cpu;
+
+	scoped_guard(cpus_read_lock) {
+		for_each_cpu(cpu, rmpopt_cpumask)
+			wrmsrq_on_cpu(cpu, MSR_AMD64_RMPOPT_BASE, 0);
+	}
+
+	free_cpumask_var(rmpopt_cpumask);

The counterpart function which allocates this cpumask is called snp_setup_rmpopt(). I'm guessing then this one should be called snp_cleanup_rmpopt(). For balance...

...

Quoted 9 lines
arch/x86/virt/svm/sev.c +9 -0
+void snp_setup_rmpopt(void)
+{
+	u64 rmpopt_base;
+	int cpu;
+
+	if (!cpu_feature_enabled(X86_FEATURE_RMPOPT) || !rmpopt_capable)
+		return;
+
+	if (!zalloc_cpumask_var(&rmpopt_cpumask, GFP_KERNEL)) {

arch/x86/virt/svm/sev.c void snp_shutdown(void) · line 618

Quoted 10 lines
arch/x86/virt/svm/sev.c +10 -0
+		pr_err("Failed to allocate RMPOPT cpumask\n");
+		return;
+	}
+
+	/*
+	 * The RMPOPT_BASE MSR is per-core, so only one thread per core needs
+	 * to set up the RMPOPT_BASE MSR.
+	 *
+	 * Note: only online primary threads are included.  If a core's
+	 * primary thread is offline, that core is not covered.  CPU hotplug

So what happens if not all cores are online?

This should check that and fail if that is the case instead of blindly continuing here.

Quoted 6 lines
arch/x86/virt/svm/sev.c +6 -0
+	 * is not currently supported with SNP enabled.
+	 */
+	scoped_guard(cpus_read_lock) {
+		for_each_online_cpu(cpu)
+			if (topology_is_primary_thread(cpu))
+				cpumask_set_cpu(cpu, rmpopt_cpumask);

arch/x86/virt/svm/sev.c void snp_shutdown(void) · line 634

Quoted 10 lines
arch/x86/virt/svm/sev.c +10 -0
+
+		rmpopt_pa_start = ALIGN_DOWN(PFN_PHYS(min_low_pfn), SZ_1G);
+		rmpopt_base = rmpopt_pa_start | MSR_AMD64_RMPOPT_ENABLE;
+
+		/*
+		 * Per-CPU RMPOPT tables support at most 2 TB of addressable memory
+		 * for RMP optimizations. Initialize the per-CPU RMPOPT table base
+		 * to the starting physical address to enable RMP optimizations for
+		 * up to 2 TB of system RAM on all CPUs.
+		 */

Simplify this comment.

Quoted 3 lines
arch/x86/virt/svm/sev.c +3 -0
+		for_each_cpu(cpu, rmpopt_cpumask)
+			wrmsrq_on_cpu(cpu, MSR_AMD64_RMPOPT_BASE, rmpopt_base);
+	}
Signature · 4 lines
Regards/Gruss,
    Boris.

https://people.kernel.org/tglx/notes-about-netiquette
#14 lore raw

Re: [PATCH v10 2/6] x86/sev: Initialize RMPOPT configuration MSRs

Hello Boris,

Quoted 11 lines · Borislav Petkov

arch/x86/virt/svm/sev.c void snp_shutdown(void) · line 618

Quoted 6 lines
arch/x86/virt/svm/sev.c +6 -0
+	/*
+	 * The RMPOPT_BASE MSR is per-core, so only one thread per core needs
+	 * to set up the RMPOPT_BASE MSR.
+	 *
+	 * Note: only online primary threads are included.  If a core's
+	 * primary thread is offline, that core is not covered.  CPU hotplug

So what happens if not all cores are online?

This should check that and fail if that is the case instead of blindly continuing here.

The offline cores will remain offline since hotplug is disabled. RMPOPT is simply a performance optimization for RMP checks and leaving the offline cores (that will never exit idle) unoptimized should be acceptable.

When hotplug is enabled again, the platform would have already executed SNP_SHUTDOWN and SnpEn would go back to 0 and RMP checks are disabled entirely by then.

Quoted 3 lines
Quoted 3 lines
(quoted diff) +2 -0
+	 * is not currently supported with SNP enabled.
+	 */
Signature · 2 lines
Thanks and Regards,
Prateek
maintainer
#15 lore raw

Re: [PATCH v10 2/6] x86/sev: Initialize RMPOPT configuration MSRs

Quoted 4 lines · K Prateek Nayak

The offline cores will remain offline since hotplug is disabled. RMPOPT is simply a performance optimization for RMP checks and leaving the offline cores (that will never exit idle) unoptimized should be acceptable.

What happens if you boot with a subset of cores, the boot flow enables RMPOPT and then you online the rest?

Have we tried that?

Signature · 4 lines
Regards/Gruss,
    Boris.

https://people.kernel.org/tglx/notes-about-netiquette
#16 lore raw

Re: [PATCH v10 2/6] x86/sev: Initialize RMPOPT configuration MSRs

Hello Boris,

Quoted 8 lines · Borislav Petkov
Quoted 4 lines · K Prateek Nayak

The offline cores will remain offline since hotplug is disabled. RMPOPT is simply a performance optimization for RMP checks and leaving the offline cores (that will never exit idle) unoptimized should be acceptable.

What happens if you boot with a subset of cores, the boot flow enables RMPOPT and then you online the rest?

RMPOPT happens at __sev_snp_init_locked() for all online cores. Until then, SnpEn is still 0 and we don't need RMPOPT because RMP checks haven't been enabled yet.

Quoted 2 lines

Have we tried that?

That said, Ashish, should snp_rmptable_init() do a snp_rmpopt_all_physmem() (or something equivalent) when it finds SNP_EN set in MSR_AMD64_SYSCFG after a kexec?

Signature · 2 lines
Thanks and Regards,
Prateek
author maintainer
#17 lore raw

Re: [PATCH v10 2/6] x86/sev: Initialize RMPOPT configuration MSRs

Hello Prateek,

Quoted 17 lines · K Prateek Nayak

Hello Boris,

Quoted 8 lines · Borislav Petkov
Quoted 4 lines · K Prateek Nayak

The offline cores will remain offline since hotplug is disabled. RMPOPT is simply a performance optimization for RMP checks and leaving the offline cores (that will never exit idle) unoptimized should be acceptable.

What happens if you boot with a subset of cores, the boot flow enables RMPOPT and then you online the rest?

RMPOPT happens at __sev_snp_init_locked() for all online cores. Until then, SnpEn is still 0 and we don't need RMPOPT because RMP checks haven't been enabled yet.

Quoted 1 line
Quoted 6 lines
Quoted 1 line

Have we tried that?

That said, Ashish, should snp_rmptable_init() do a snp_rmpopt_all_physmem() (or something equivalent) when it finds SNP_EN set in MSR_AMD64_SYSCFG after a kexec?

It already happens, just later in the sequence. On kexec __sev_snp_init_locked() still runs: snp_prepare() returns early (SnpEn set), SNP_INIT re-initializes the firmware context, and then snp_setup_rmpopt() is called.

On the fresh kexec kernel, rmpopt_wq is NULL, so snp_setup_rmpopt() does the full setup — programs the per-CPU RMPOPT_BASE MSRs and queues the initial all-physmem optimization pass. So RMPOPT is re-applied on kexec through the normal path.

Doing it in snp_rmptable_init() would be too early: the per-CPU RMPOPT_BASE MSRs aren't programmed until snp_setup_rmpopt(), so a pass there would have no base configured.

Thanks,
Ashish

#18 lore raw

Re: [PATCH v10 2/6] x86/sev: Initialize RMPOPT configuration MSRs

Hello Ashish,

Quoted 22 lines · Ashish

Hello Prateek,

Quoted 19 lines · K Prateek Nayak

Hello Boris,

Quoted 8 lines · Borislav Petkov
Quoted 4 lines · K Prateek Nayak

The offline cores will remain offline since hotplug is disabled. RMPOPT is simply a performance optimization for RMP checks and leaving the offline cores (that will never exit idle) unoptimized should be acceptable.

What happens if you boot with a subset of cores, the boot flow enables RMPOPT and then you online the rest?

RMPOPT happens at __sev_snp_init_locked() for all online cores. Until then, SnpEn is still 0 and we don't need RMPOPT because RMP checks haven't been enabled yet.

Quoted 2 lines

Have we tried that?

Quoted 7 lines
Quoted 4 lines

That said, Ashish, should snp_rmptable_init() do a snp_rmpopt_all_physmem() (or something equivalent) when it finds SNP_EN set in MSR_AMD64_SYSCFG after a kexec?

It already happens, just later in the sequence. On kexec __sev_snp_init_locked() still runs: snp_prepare() returns early (SnpEn set), SNP_INIT re-initializes the firmware context, and then snp_setup_rmpopt() is called.

But that only happens when the first SNP guest is created right? Until then RMP checks are enforced but no confidential guest is running and the CPUs are paying price for those checks.

Quoted 5 lines

On the fresh kexec kernel, rmpopt_wq is NULL, so snp_setup_rmpopt() does the full setup — programs the per-CPU RMPOPT_BASE MSRs and queues the initial all-physmem optimization pass. So RMPOPT is re-applied on kexec through the normal path.

Doing it in snp_rmptable_init() would be too early: the per-CPU RMPOPT_BASE MSRs aren't programmed until snp_setup_rmpopt(), so a pass there would have no base configured.

Ack! Which is why I mentioned something equivalent ;-)

Signature · 2 lines
Thanks and Regards,
Prateek
author maintainer
#19 lore raw

Re: [PATCH v10 2/6] x86/sev: Initialize RMPOPT configuration MSRs

Quoted 30 lines · K Prateek Nayak

Hello Ashish,

Quoted 27 lines · Ashish

Hello Prateek,

Quoted 23 lines · K Prateek Nayak

Hello Boris,

Quoted 8 lines · Borislav Petkov
Quoted 4 lines · K Prateek Nayak

The offline cores will remain offline since hotplug is disabled. RMPOPT is simply a performance optimization for RMP checks and leaving the offline cores (that will never exit idle) unoptimized should be acceptable.

What happens if you boot with a subset of cores, the boot flow enables RMPOPT and then you online the rest?

RMPOPT happens at __sev_snp_init_locked() for all online cores. Until then, SnpEn is still 0 and we don't need RMPOPT because RMP checks haven't been enabled yet.

Quoted 2 lines

Have we tried that?

That said, Ashish, should snp_rmptable_init() do a snp_rmpopt_all_physmem() (or something equivalent) when it finds SNP_EN set in MSR_AMD64_SYSCFG after a kexec?

Quoted 6 lines
Quoted 2 lines

It already happens, just later in the sequence. On kexec __sev_snp_init_locked() still runs: snp_prepare() returns early (SnpEn set), SNP_INIT re-initializes the firmware context, and then snp_setup_rmpopt() is called.

But that only happens when the first SNP guest is created right? Until then RMP checks are enforced but no confidential guest is running and the CPUs are paying price for those checks.

Typically, on RMPOPT-capable platforms SnpEn isn't inherited across kexec.

As i mentioned earlier (on v9 thread), kexec with SNP active currently requires a full SNP shutdown before the kexec. SNP_SHUTDOWN_EX (and the IOMMU SNP shutdown it performs) fail if there are any active SNP guests or assigned ASIDs, so a working kexec has to terminate all SNP guests and run a full shutdown first (via systemctl kexec).

Now, on any platform that supports the x86 SNP shutdown, that issues SNP_SHUTDOWN_EX with x86_snp_shutdown=1, the firmware clears SnpEn before the kexec. The kexec kernel boots with SnpEn clear, exactly like a normal boot. (The only case SnpEn survives kexec is the legacy shutdown path, and those platforms don't support RMPOPT anyway.)

Thanks,
Ashish

Quoted 9 lines
Quoted 6 lines

On the fresh kexec kernel, rmpopt_wq is NULL, so snp_setup_rmpopt() does the full setup — programs the per-CPU RMPOPT_BASE MSRs and queues the initial all-physmem optimization pass. So RMPOPT is re-applied on kexec through the normal path.

Doing it in snp_rmptable_init() would be too early: the per-CPU RMPOPT_BASE MSRs aren't programmed until snp_setup_rmpopt(), so a pass there would have no base configured.

Ack! Which is why I mentioned something equivalent ;-)

author maintainer
#20 lore raw

Re: [PATCH v10 2/6] x86/sev: Initialize RMPOPT configuration MSRs

In reply to Borislav Petkov

Hello Boris,

Quoted 11 lines · Borislav Petkov
Quoted 4 lines · K Prateek Nayak

The offline cores will remain offline since hotplug is disabled. RMPOPT is simply a performance optimization for RMP checks and leaving the offline cores (that will never exit idle) unoptimized should be acceptable.

What happens if you boot with a subset of cores, the boot flow enables RMPOPT and then you online the rest?

Have we tried that?

It can't happen here. snp_prepare() does the following, in order:

1. cpu_hotplug_disable() — freeze the CPU set.
2. fail SNP init (-EOPNOTSUPP) unless every present CPU is online (cpumask_equal(cpu_online_mask, cpu_present_mask).
3. enable SNP.

snp_setup_rmpopt() runs only after snp_prepare() succeeds, so all present CPUs are online and stay online (hotplug is disabled). No core's primary thread can be offline at this point.

(The kexec path is the one exception — snp_prepare() returns early there — which I'm handling in the other thread.)

Thanks,
Ashish

author maintainer
#21 lore raw

Re: [PATCH v10 2/6] x86/sev: Initialize RMPOPT configuration MSRs

Quoted 21 lines · Ashish

Hello Boris,

Quoted 11 lines · Borislav Petkov
Quoted 4 lines · K Prateek Nayak

The offline cores will remain offline since hotplug is disabled. RMPOPT is simply a performance optimization for RMP checks and leaving the offline cores (that will never exit idle) unoptimized should be acceptable.

What happens if you boot with a subset of cores, the boot flow enables RMPOPT and then you online the rest?

Have we tried that?

It can't happen here. snp_prepare() does the following, in order:

1. cpu_hotplug_disable() — freeze the CPU set.
2. fail SNP init (-EOPNOTSUPP) unless every present CPU is online (cpumask_equal(cpu_online_mask, cpu_present_mask).
3. enable SNP.

Quoted 8 lines

snp_setup_rmpopt() runs only after snp_prepare() succeeds, so all present CPUs are online and stay online (hotplug is disabled). No core's primary thread can be offline at this point.

(The kexec path is the one exception — snp_prepare() returns early there — which I'm handling in the other thread.)

Thanks,
Ashish

Additionally for kexec considerations: on RMPOPT-capable platforms kexec clears SnpEn (full SNP_SHUTDOWN_EX, x86_snp_shutdown=1), so snp_prepare() re-runs its cpumask_equal() all-online check on the kexec boot exactly as on a normal boot.

So no kexec-specific case where RMPOPT gets set up with a partial CPU set.

Thanks,
Ashish

#22 lore raw

Re: [PATCH v10 2/6] x86/sev: Initialize RMPOPT configuration MSRs

Hello Ashish,

Quoted 28 lines · Ashish
Quoted 26 lines · Ashish

Hello Boris,

Quoted 11 lines · Borislav Petkov
Quoted 4 lines · K Prateek Nayak

The offline cores will remain offline since hotplug is disabled. RMPOPT is simply a performance optimization for RMP checks and leaving the offline cores (that will never exit idle) unoptimized should be acceptable.

What happens if you boot with a subset of cores, the boot flow enables RMPOPT and then you online the rest?

Have we tried that?

It can't happen here. snp_prepare() does the following, in order:

1. cpu_hotplug_disable() — freeze the CPU set.
2. fail SNP init (-EOPNOTSUPP) unless every present CPU is online (cpumask_equal(cpu_online_mask, cpu_present_mask).
3. enable SNP.

snp_setup_rmpopt() runs only after snp_prepare() succeeds, so all present CPUs are online and stay online (hotplug is disabled). No core's primary thread can be offline at this point.

(The kexec path is the one exception — snp_prepare() returns early there — which I'm handling in the other thread.)

Quoted 8 lines
Quoted 3 lines

Thanks,
Ashish

Additionally for kexec considerations: on RMPOPT-capable platforms kexec clears SnpEn (full SNP_SHUTDOWN_EX, x86_snp_shutdown=1), so snp_prepare() re-runs its cpumask_equal() all-online check on the kexec boot exactly as on a normal boot.

So no kexec-specific case where RMPOPT gets set up with a partial CPU set.

Thank you for clarifying! Much appreciated _/\_

Signature · 2 lines
Thanks and Regards,
Prateek
maintainer
#23 lore raw

Re: [PATCH v10 2/6] x86/sev: Initialize RMPOPT configuration MSRs

In reply to Kalra, Ashish
Quoted 6 lines · Ashish
Quoted 5 lines

What happens if you boot with a subset of cores, the boot flow enables RMPOPT and then you online the rest?

Have we tried that?

It can't happen here. snp_prepare() does the following, in order:

Then the comment in the code is greatly misleading:

/*
* The RMPOPT_BASE MSR is per-core, so only one thread per core needs
* to set up the RMPOPT_BASE MSR.
*
* Note: only online primary threads are included. If a core's
* primary thread is offline, that core is not covered. CPU hotplug
* is not currently supported with SNP enabled.
*/
scoped_guard(cpus_read_lock) {
for_each_online_cpu(cpu)
if (topology_is_primary_thread(cpu))

I'd expect to read there:

/*
* The RMPOPT_BASE MSR is per-core, so only one thread per core needs
* to set up the RMPOPT_BASE MSR. All primary threads are online,
* otherwise SNP would not have been enabled.
*/

Signature · 4 lines
Regards/Gruss,
    Boris.

https://people.kernel.org/tglx/notes-about-netiquette
author maintainer
#24 lore raw

PATCH v10 3/6 x86/sev: Disable CPU hotplug while SNP is active

In reply to Ashish Kalra

From: Ashish Kalra <[email protected]>

While SNP is active, every memory write is checked against the RMP to protect SEV-SNP guest memory. A core performs these RMP checks only once SNP has been initialized via SNP_INIT and the SNP-enable bit in SYSCFG is set on that core; the firmware requires the SNP-enable bit to be set on every present CPU before SNP initialization. A core that is not SNP-enabled and not SNP-initialized performs no RMP checks at all, so there is no valid configuration with SNP active and any CPU exempt from RMP checks.

The firmware determines which CPUs are present from the processor and the BIOS/UEFI configuration (e.g. SMT disabled in the BIOS) and enumerates them at SNP init; it is not aware of the OS bringing CPUs online or offline afterwards. SNP_INIT fails unless SnpEn is set on all CPUs, so a CPU that is offline at SNP init does not have SnpEn set, SNP_INIT fails, and there can be no SNP guest memory. OS CPU hotplug can thus diverge from the firmware's expectations and break SNP.

Tie CPU hotplug to the SNP-enable bit: disable it in snp_prepare() before SNP is enabled, and re-enable it in snp_shutdown() once the firmware has disabled SNP. If snp_prepare() fails before enabling SNP it re-enables hotplug itself; once SNP is enabled hotplug stays disabled, including across a failed SNP_INIT and across the legacy SNP_SHUTDOWN_EX path, both of which leave SNP enabled. A kexec target that boots with SNP already enabled disables hotplug once in snp_rmptable_init(), since snp_prepare() bails when SNP is already enabled.

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.

---
 arch/x86/virt/svm/sev.c | 31 +++++++++++++++++++++++++++++++
 1 file changed, 31 insertions(+)
arch/x86/virt/svm/sev.c +31 -0
index dab6e1c290bc..04a58ac4339c 100644
@@ -535,6 +535,15 @@ int snp_prepare(void)
 
 	clear_rmp();
 
+	/*
+	 * Disable CPU hotplug before enabling SNP, so no CPU can come online
+	 * without SnpEn while SNP is enabled; it is re-enabled in snp_shutdown()
+	 * once SNP is disabled.  Must be before cpus_read_lock():
+	 * cpu_hotplug_disable() takes cpu_add_remove_lock, which nests above
+	 * cpu_hotplug_lock.
+	 */
+	cpu_hotplug_disable();
+
 	cpus_read_lock();
 
 	if (!cpumask_equal(cpu_online_mask, cpu_present_mask)) {
@@ -560,6 +569,10 @@ int snp_prepare(void)
 unlock:
 	cpus_read_unlock();
 
+	/* Re-enable CPU hotplug; SnpEn was never set. */
+	if (ret)
+		cpu_hotplug_enable();
+
 	return ret;
 }
 EXPORT_SYMBOL_FOR_MODULES(snp_prepare, "ccp");
@@ -587,6 +600,13 @@ void snp_shutdown(void)
 
 	rmpopt_cleanup();
 
+	/*
+	 * Re-enable CPU hotplug now that the firmware has disabled SNP; CPU
+	 * hotplug is not re-enabled for a legacy SNP shutdown.  After
+	 * rmpopt_cleanup() so RMPOPT_BASE is cleared with hotplug still disabled.
+	 */
+	cpu_hotplug_enable();
+
 	clear_rmp();
 	on_each_cpu(mfd_reconfigure, NULL, 1);
 }
@@ -645,6 +665,8 @@ EXPORT_SYMBOL_FOR_MODULES(snp_setup_rmpopt, "ccp");
  */
 int __init snp_rmptable_init(void)
 {
+	u64 val;
+
 	if (WARN_ON_ONCE(!cc_platform_has(CC_ATTR_HOST_SEV_SNP)))
 		return -ENOSYS;
 
@@ -654,6 +676,15 @@ int __init snp_rmptable_init(void)
 	if (!setup_rmptable())
 		return -ENOSYS;
 
+	/*
+	 * On a kexec boot SNP may already be enabled (legacy firmware leaves
+	 * SnpEn set across shutdown), in which case snp_prepare() bails without
+	 * disabling CPU hotplug, so disable it here.
+	 */
+	rdmsrq(MSR_AMD64_SYSCFG, val);
+	if (val & MSR_AMD64_SYSCFG_SNP_EN)
+		cpu_hotplug_disable();
+
 	/*
 	 * Setting crash_kexec_post_notifiers to 'true' to ensure that SNP panic
 	 * notifier is invoked to do SNP IOMMU shutdown before kdump.
Signature · 1 line
2.43.0
#25 lore raw

Re: [PATCH v10 3/6] x86/sev: Disable CPU hotplug while SNP is active

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [Critical] System suspend (PM transitions) bypasses `cpu_hotplug_disable()`, stripping `SnpEn` from…

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Critical] System suspend (PM transitions) bypasses cpu_hotplug_disable(), stripping SnpEn from secondary CPUs on resume. --

commit 29b8362e9ad72efa8892f6bb3124dbbcc29ee822
Author: Ashish Kalra <[email protected]>

x86/sev: Disable CPU hotplug while SNP is active

This commit disables CPU hotplug in snp_prepare() before SNP is enabled and re-enables it in snp_shutdown(). This aims to prevent CPUs from coming online without the SnpEn bit set while SNP is active, as the firmware cannot track OS CPU hotplug events.

Quoted 6 lines
arch/x86/virt/svm/sev.c +0 -0
index dab6e1c290bca..04a58ac4339cb 100644
@@ -535,6 +535,15 @@ int snp_prepare(void)
 

arch/x86/virt/svm/sev.c int snp_prepare(void) · line 545

Quoted 10 lines
arch/x86/virt/svm/sev.c +8 -0
 	clear_rmp();
 
+	/*
+	 * Disable CPU hotplug before enabling SNP, so no CPU can come online
+	 * without SnpEn while SNP is enabled; it is re-enabled in snp_shutdown()
+	 * once SNP is disabled.  Must be before cpus_read_lock():
+	 * cpu_hotplug_disable() takes cpu_add_remove_lock, which nests above
+	 * cpu_hotplug_lock.
+	 */
+	cpu_hotplug_disable();

[Severity: Critical]
Does this actually prevent all CPUs from coming online without SnpEn?

Looking at system suspend (PM transitions), freeze_secondary_cpus() calls _cpu_down() directly, which bypasses the cpu_hotplug_disabled check. Upon resume, thaw_secondary_cpus() brings them back online via _cpu_up().

Since snp_enable() is called via a one-off on_each_cpu() in snp_prepare() rather than being registered as a CPU hotplug callback, wouldn't secondary CPUs resume with the SnpEn MSR bit cleared?

This could allow a suspend/resume cycle to create an asymmetrical state where hardware RMP checks are bypassed.

[ ... ]

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=3
author maintainer
#26 lore raw

Re: [PATCH v10 3/6] x86/sev: Disable CPU hotplug while SNP is active

Quoted 38 lines · On 6/30/2026 1:32 PM, [email protected] wrote:

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Critical] System suspend (PM transitions) bypasses cpu_hotplug_disable(), stripping SnpEn from secondary CPUs on resume. --

commit 29b8362e9ad72efa8892f6bb3124dbbcc29ee822
Author: Ashish Kalra <[email protected]>

x86/sev: Disable CPU hotplug while SNP is active

This commit disables CPU hotplug in snp_prepare() before SNP is enabled and re-enables it in snp_shutdown(). This aims to prevent CPUs from coming online without the SnpEn bit set while SNP is active, as the firmware cannot track OS CPU hotplug events.

Quoted 6 lines
arch/x86/virt/svm/sev.c +0 -0
index dab6e1c290bca..04a58ac4339cb 100644
@@ -535,6 +535,15 @@ int snp_prepare(void)
 

arch/x86/virt/svm/sev.c int snp_prepare(void) · line 545

Quoted 10 lines
arch/x86/virt/svm/sev.c +8 -0
 	clear_rmp();
 
+	/*
+	 * Disable CPU hotplug before enabling SNP, so no CPU can come online
+	 * without SnpEn while SNP is enabled; it is re-enabled in snp_shutdown()
+	 * once SNP is disabled.  Must be before cpus_read_lock():
+	 * cpu_hotplug_disable() takes cpu_add_remove_lock, which nests above
+	 * cpu_hotplug_lock.
+	 */
+	cpu_hotplug_disable();

[Severity: Critical]
Does this actually prevent all CPUs from coming online without SnpEn?

Looking at system suspend (PM transitions), freeze_secondary_cpus() calls _cpu_down() directly, which bypasses the cpu_hotplug_disabled check. Upon resume, thaw_secondary_cpus() brings them back online via _cpu_up().

Quoted 9 lines

Since snp_enable() is called via a one-off on_each_cpu() in snp_prepare() rather than being registered as a CPU hotplug callback, wouldn't secondary CPUs resume with the SnpEn MSR bit cleared?

This could allow a suspend/resume cycle to create an asymmetrical state where hardware RMP checks are bypassed.

[ ... ]

You're right about the mechanism — cpu_hotplug_disable() doesn't cover the suspend freeze/thaw path, and snp_enable() has no resume hook. But an SNP host doesn't support suspend/resume: SNP is set up once at boot (SnpEn on all CPUs followed by SNP_INIT), and that state can't be rebuilt on resume without a full re-init. So this can't arise in practice, and it's pre-existing base-SNP behavior, not something this patch introduces.

Thanks,
Ashish

#27 lore raw

Re: [PATCH v10 3/6] x86/sev: Disable CPU hotplug while SNP is active

In reply to Ashish Kalra

Hi Ashish,

I don't believe my concern has been addressed

https://lore.kernel.org/lkml/[email protected]/

--
Jethro Beekman | CTO | Fortanix

Quoted 108 lines · Ashish Kalra

From: Ashish Kalra <[email protected]>

While SNP is active, every memory write is checked against the RMP to protect SEV-SNP guest memory. A core performs these RMP checks only once SNP has been initialized via SNP_INIT and the SNP-enable bit in SYSCFG is set on that core; the firmware requires the SNP-enable bit to be set on every present CPU before SNP initialization. A core that is not SNP-enabled and not SNP-initialized performs no RMP checks at all, so there is no valid configuration with SNP active and any CPU exempt from RMP checks.

The firmware determines which CPUs are present from the processor and the BIOS/UEFI configuration (e.g. SMT disabled in the BIOS) and enumerates them at SNP init; it is not aware of the OS bringing CPUs online or offline afterwards. SNP_INIT fails unless SnpEn is set on all CPUs, so a CPU that is offline at SNP init does not have SnpEn set, SNP_INIT fails, and there can be no SNP guest memory. OS CPU hotplug can thus diverge from the firmware's expectations and break SNP.

Tie CPU hotplug to the SNP-enable bit: disable it in snp_prepare() before SNP is enabled, and re-enable it in snp_shutdown() once the firmware has disabled SNP. If snp_prepare() fails before enabling SNP it re-enables hotplug itself; once SNP is enabled hotplug stays disabled, including across a failed SNP_INIT and across the legacy SNP_SHUTDOWN_EX path, both of which leave SNP enabled. A kexec target that boots with SNP already enabled disables hotplug once in snp_rmptable_init(), since snp_prepare() bails when SNP is already enabled.

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.

---
 arch/x86/virt/svm/sev.c | 31 +++++++++++++++++++++++++++++++
 1 file changed, 31 insertions(+)
arch/x86/virt/svm/sev.c +31 -0
index dab6e1c290bc..04a58ac4339c 100644
@@ -535,6 +535,15 @@ int snp_prepare(void)
 
 	clear_rmp();
 
+	/*
+	 * Disable CPU hotplug before enabling SNP, so no CPU can come online
+	 * without SnpEn while SNP is enabled; it is re-enabled in snp_shutdown()
+	 * once SNP is disabled.  Must be before cpus_read_lock():
+	 * cpu_hotplug_disable() takes cpu_add_remove_lock, which nests above
+	 * cpu_hotplug_lock.
+	 */
+	cpu_hotplug_disable();
+
 	cpus_read_lock();
 
 	if (!cpumask_equal(cpu_online_mask, cpu_present_mask)) {
@@ -560,6 +569,10 @@ int snp_prepare(void)
 unlock:
 	cpus_read_unlock();
 
+	/* Re-enable CPU hotplug; SnpEn was never set. */
+	if (ret)
+		cpu_hotplug_enable();
+
 	return ret;
 }
 EXPORT_SYMBOL_FOR_MODULES(snp_prepare, "ccp");
@@ -587,6 +600,13 @@ void snp_shutdown(void)
 
 	rmpopt_cleanup();
 
+	/*
+	 * Re-enable CPU hotplug now that the firmware has disabled SNP; CPU
+	 * hotplug is not re-enabled for a legacy SNP shutdown.  After
+	 * rmpopt_cleanup() so RMPOPT_BASE is cleared with hotplug still disabled.
+	 */
+	cpu_hotplug_enable();
+
 	clear_rmp();
 	on_each_cpu(mfd_reconfigure, NULL, 1);
 }
@@ -645,6 +665,8 @@ EXPORT_SYMBOL_FOR_MODULES(snp_setup_rmpopt, "ccp");
  */
 int __init snp_rmptable_init(void)
 {
+	u64 val;
+
 	if (WARN_ON_ONCE(!cc_platform_has(CC_ATTR_HOST_SEV_SNP)))
 		return -ENOSYS;
 
@@ -654,6 +676,15 @@ int __init snp_rmptable_init(void)
 	if (!setup_rmptable())
 		return -ENOSYS;
 
+	/*
+	 * On a kexec boot SNP may already be enabled (legacy firmware leaves
+	 * SnpEn set across shutdown), in which case snp_prepare() bails without
+	 * disabling CPU hotplug, so disable it here.
+	 */
+	rdmsrq(MSR_AMD64_SYSCFG, val);
+	if (val & MSR_AMD64_SYSCFG_SNP_EN)
+		cpu_hotplug_disable();
+
 	/*
 	 * Setting crash_kexec_post_notifiers to 'true' to ensure that SNP panic
 	 * notifier is invoked to do SNP IOMMU shutdown before kdump.
#28 lore raw

Re: [PATCH v10 3/6] x86/sev: Disable CPU hotplug while SNP is active

Hello Jethro,

Quoted 3 lines · Jethro Beekman

I don't believe my concern has been addressed

https://lore.kernel.org/lkml/[email protected]/

Quoting your question:

Quoted 3 lines

I think this is too broad. If I have a hypervisor that supports SNP virtualization, a (non-confidential) L1 guest running Linux should still support CPU hotplug while also running confidential L2 guests.

Ashish, Tom, correct me if I'm wrong, but I don't think KVM exposes SNP support to L1, at least as per
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/x86/kvm/cpuid.c?h=v7.2-rc1#n1221
and only SNP initialization disables hotplug - not the other variants.

L1, running a confidential guest (SEV/SEV-ES) should still be able to support hotplug since it doesn't go through SNP init. Only the base hypervisor can setup the RMP tables and go through snp_prepare().

Also bsp_determine_snp() should clear CC_ATTR_HOST_SEV_SNP if it detects X86_FEATURE_HYPERVISOR so I don't see how this can be a problem for hotplug in L1.
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/x86/kernel/cpu/amd.c?h=v7.2-rc1#n368

Signature · 2 lines
Thanks and Regards,
Prateek
author maintainer
#29 lore raw

Re: [PATCH v10 3/6] x86/sev: Disable CPU hotplug while SNP is active

Hi Prateek,

Quoted 18 lines · K Prateek Nayak

Hello Jethro,

Quoted 3 lines · Jethro Beekman

I don't believe my concern has been addressed

https://lore.kernel.org/lkml/[email protected]/

Quoting your question:

Quoted 3 lines

I think this is too broad. If I have a hypervisor that supports SNP virtualization, a (non-confidential) L1 guest running Linux should still support CPU hotplug while also running confidential L2 guests.

Ashish, Tom, correct me if I'm wrong, but I don't think KVM exposes SNP support to L1, at least as per
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/x86/kvm/cpuid.c?h=v7.2-rc1#n1221
and only SNP initialization disables hotplug - not the other variants.

Quoted 9 lines

L1, running a confidential guest (SEV/SEV-ES) should still be able to support hotplug since it doesn't go through SNP init. Only the base hypervisor can setup the RMP tables and go through snp_prepare().

Also bsp_determine_snp() should clear CC_ATTR_HOST_SEV_SNP if it detects X86_FEATURE_HYPERVISOR so I don't see how this can be a problem for hotplug in L1.
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/x86/kernel/cpu/amd.c?h=v7.2-rc1#n368

bsp_determine_snp() only sets CC_ATTR_HOST_SEV_SNP when X86_FEATURE_HYPERVISOR is clear:

  if (!cpu_has(c, X86_FEATURE_HYPERVISOR) &&
      (ZEN3 || ZEN4 || RMPREAD) && snp_probe_rmptable_info())
          cc_platform_set(CC_ATTR_HOST_SEV_SNP);
  else {
          setup_clear_cpu_cap(X86_FEATURE_SEV_SNP);
          cc_platform_clear(CC_ATTR_HOST_SEV_SNP);  
  }

So Linux running as an L1 guest (HYPERVISOR set) never has CC_ATTR_HOST_SEV_SNP.

And both hotplug-disable sites sit behind that flag:
- snp_prepare() is only called from __sev_snp_init_locked(), which returns -ENODEV early if !cc_platform_has(CC_ATTR_HOST_SEV_SNP).
- snp_rmptable_init() bails (WARN_ON_ONCE(!cc_platform_has(CC_ATTR_HOST_SEV_SNP))) before its kexec one-shot disable.

So an L1 guest can't reach the disable at all; only the bare-metal host that programs the RMP does.

An L1 running SEV/SEV-ES guests never goes through SNP host init, so it's hotplug is unaffected and KVM doesn't expose SNP to L1.

So there's no impact on L1 hotplug currently.

Thanks,
Ashish

author maintainer
#30 lore raw

Re: [PATCH v10 3/6] x86/sev: Disable CPU hotplug while SNP is active

In reply to Jethro Beekman
Quoted 7 lines · Jethro Beekman

Hi Ashish,

I don't believe my concern has been addressed

https://lore.kernel.org/lkml/[email protected]/

--

The disable tracks SNP_INIT, not "SNP" in general: SNP_INIT requires SnpEn to be set on all present CPUs, and a CPU brought online afterward wouldn't have it, so the kernel that runs SNP_INIT must keep its CPU set stable. Today the only kernel that runs SNP_INIT is the bare-metal host, so a plain L1 guest keeps full CPU hotplug.

Concretely, the path is gated by CC_ATTR_HOST_SEV_SNP, which bsp_determine_snp() sets only when X86_FEATURE_HYPERVISOR is clear and clears otherwise (as Prateek pointed out). So a Linux L1 guest never has it set, never reaches snp_prepare()/snp_rmptable_init(), and keeps CPU hotplug — including while running SEV/SEV-ES confidential L2 guests. Only SNP initialization disables hotplug; the other SEV variants don't. And KVM doesn't expose SNP to L1, so an L1 can't be an SNP host today in any case.

On the nested scenario you raised: if SNP-guest-as-L2 support is added, an L1 acting as an SNP host would run a *virtualized* SNP_INIT. A faithful virtualization carries the same constraint as physical SNP_INIT — all present (v)CPUs must be SnpEn — so that L1 would have the same (v)CPU-hotplug-disable requirement, just over its virtual CPUs, and this same code would apply at that level. So the disable isn't too broad; it correctly tracks SNP_INIT. It simply doesn't apply to a plain L1 guest today, because such a guest isn't running SNP_INIT.

Thanks,
Ashish

Quoted 112 lines

Jethro Beekman | CTO | Fortanix

Quoted 108 lines · Ashish Kalra

From: Ashish Kalra <[email protected]>

While SNP is active, every memory write is checked against the RMP to protect SEV-SNP guest memory. A core performs these RMP checks only once SNP has been initialized via SNP_INIT and the SNP-enable bit in SYSCFG is set on that core; the firmware requires the SNP-enable bit to be set on every present CPU before SNP initialization. A core that is not SNP-enabled and not SNP-initialized performs no RMP checks at all, so there is no valid configuration with SNP active and any CPU exempt from RMP checks.

The firmware determines which CPUs are present from the processor and the BIOS/UEFI configuration (e.g. SMT disabled in the BIOS) and enumerates them at SNP init; it is not aware of the OS bringing CPUs online or offline afterwards. SNP_INIT fails unless SnpEn is set on all CPUs, so a CPU that is offline at SNP init does not have SnpEn set, SNP_INIT fails, and there can be no SNP guest memory. OS CPU hotplug can thus diverge from the firmware's expectations and break SNP.

Tie CPU hotplug to the SNP-enable bit: disable it in snp_prepare() before SNP is enabled, and re-enable it in snp_shutdown() once the firmware has disabled SNP. If snp_prepare() fails before enabling SNP it re-enables hotplug itself; once SNP is enabled hotplug stays disabled, including across a failed SNP_INIT and across the legacy SNP_SHUTDOWN_EX path, both of which leave SNP enabled. A kexec target that boots with SNP already enabled disables hotplug once in snp_rmptable_init(), since snp_prepare() bails when SNP is already enabled.

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.

---
 arch/x86/virt/svm/sev.c | 31 +++++++++++++++++++++++++++++++
 1 file changed, 31 insertions(+)
arch/x86/virt/svm/sev.c +31 -0
index dab6e1c290bc..04a58ac4339c 100644
@@ -535,6 +535,15 @@ int snp_prepare(void)
 
 	clear_rmp();
 
+	/*
+	 * Disable CPU hotplug before enabling SNP, so no CPU can come online
+	 * without SnpEn while SNP is enabled; it is re-enabled in snp_shutdown()
+	 * once SNP is disabled.  Must be before cpus_read_lock():
+	 * cpu_hotplug_disable() takes cpu_add_remove_lock, which nests above
+	 * cpu_hotplug_lock.
+	 */
+	cpu_hotplug_disable();
+
 	cpus_read_lock();
 
 	if (!cpumask_equal(cpu_online_mask, cpu_present_mask)) {
@@ -560,6 +569,10 @@ int snp_prepare(void)
 unlock:
 	cpus_read_unlock();
 
+	/* Re-enable CPU hotplug; SnpEn was never set. */
+	if (ret)
+		cpu_hotplug_enable();
+
 	return ret;
 }
 EXPORT_SYMBOL_FOR_MODULES(snp_prepare, "ccp");
@@ -587,6 +600,13 @@ void snp_shutdown(void)
 
 	rmpopt_cleanup();
 
+	/*
+	 * Re-enable CPU hotplug now that the firmware has disabled SNP; CPU
+	 * hotplug is not re-enabled for a legacy SNP shutdown.  After
+	 * rmpopt_cleanup() so RMPOPT_BASE is cleared with hotplug still disabled.
+	 */
+	cpu_hotplug_enable();
+
 	clear_rmp();
 	on_each_cpu(mfd_reconfigure, NULL, 1);
 }
@@ -645,6 +665,8 @@ EXPORT_SYMBOL_FOR_MODULES(snp_setup_rmpopt, "ccp");
  */
 int __init snp_rmptable_init(void)
 {
+	u64 val;
+
 	if (WARN_ON_ONCE(!cc_platform_has(CC_ATTR_HOST_SEV_SNP)))
 		return -ENOSYS;
 
@@ -654,6 +676,15 @@ int __init snp_rmptable_init(void)
 	if (!setup_rmptable())
 		return -ENOSYS;
 
+	/*
+	 * On a kexec boot SNP may already be enabled (legacy firmware leaves
+	 * SnpEn set across shutdown), in which case snp_prepare() bails without
+	 * disabling CPU hotplug, so disable it here.
+	 */
+	rdmsrq(MSR_AMD64_SYSCFG, val);
+	if (val & MSR_AMD64_SYSCFG_SNP_EN)
+		cpu_hotplug_disable();
+
 	/*
 	 * Setting crash_kexec_post_notifiers to 'true' to ensure that SNP panic
 	 * notifier is invoked to do SNP IOMMU shutdown before kdump.
#31 lore raw

Re: [PATCH v10 3/6] x86/sev: Disable CPU hotplug while SNP is active

Quoted 10 lines · Ashish
Quoted 7 lines · Jethro Beekman

Hi Ashish,

I don't believe my concern has been addressed

https://lore.kernel.org/lkml/[email protected]/

--

Quoted 8 lines

The disable tracks SNP_INIT, not "SNP" in general: SNP_INIT requires SnpEn to be set on all present CPUs, and a CPU brought online afterward wouldn't have it, so the kernel that runs SNP_INIT must keep its CPU set stable. Today the only kernel that runs SNP_INIT is the bare-metal host, so a plain L1 guest keeps full CPU hotplug.

Concretely, the path is gated by CC_ATTR_HOST_SEV_SNP, which bsp_determine_snp() sets only when X86_FEATURE_HYPERVISOR is clear and clears otherwise (as Prateek pointed out). So a Linux L1 guest never has it set, never reaches snp_prepare()/snp_rmptable_init(), and keeps CPU hotplug — including while running SEV/SEV-ES confidential L2 guests. Only SNP initialization disables hotplug; the other SEV variants don't. And KVM doesn't expose SNP to L1, so an L1 can't be an SNP host today in any case.

On the nested scenario you raised: if SNP-guest-as-L2 support is added, an L1 acting as an SNP host would run a *virtualized* SNP_INIT. A faithful virtualization carries the same constraint as physical SNP_INIT — all present (v)CPUs must be SnpEn — so that L1 would have the same (v)CPU-hotplug-disable requirement, just over its virtual CPUs, and this same code would apply at that level. So the disable isn't too broad; it correctly tracks SNP_INIT. It simply doesn't apply to a plain L1 guest today, because such a guest isn't running SNP_INIT.

Thanks, Ashish, Prateek, for the clarification.

--
Jethro Beekman | CTO | Fortanix

maintainer
#32 lore raw

Re: [PATCH v10 3/6] x86/sev: Disable CPU hotplug while SNP is active

In reply to Ashish Kalra
Quoted 5 lines · Ashish Kalra

From: Ashish Kalra <[email protected]>

While SNP is active, every memory write is checked against the RMP to protect SEV-SNP guest memory. A core performs these RMP checks only once
...

Use this commit message for your next revision. The idea is to split it into smaller paragraphs for easier parsing, do simple formulations and not talk about future patches because git history is not always linear:

"While SNP is active, every memory write is checked against the RMP to protect SEV-SNP guest memory. A core performs these RMP checks only once SNP has been initialized via SNP_INIT and the SNP-enable bit in SYSCFG is set on that core; the firmware requires the SNP-enable bit to be set on every present CPU before SNP initialization.

A core that is not SNP-enabled and not SNP-initialized performs no RMP checks at all, so there is no valid configuration with SNP active and any CPU exempt from RMP checks.

The firmware determines which CPUs are present from the processor and the BIOS/UEFI configuration (e.g. SMT disabled in the BIOS) and enumerates them at SNP init; it is not aware of the OS bringing CPUs online or offline afterwards.

SNP_INIT fails unless SnpEn is set on all CPUs, so a CPU that is offline when SNP_INIT is issued, does not have SnpEn set, SNP_INIT fails, and there can be no SNP guest memory. OS CPU hotplug can thus diverge from the firmware's expectations and break SNP.

Tie CPU hotplug to the SNP-enable bit: disable it in snp_prepare() before SNP is enabled, and re-enable it in snp_shutdown() once the firmware has disabled SNP.

If snp_prepare() fails before enabling SNP it re-enables hotplug itself; once SNP is enabled hotplug stays disabled, including across a failed SNP_INIT and across the legacy SNP_SHUTDOWN_EX path, both of which leave SNP enabled.

A kexec target that boots with SNP already enabled, disables hotplug once in snp_rmptable_init(), since snp_prepare() bails when SNP is already enabled."

Quoted 15 lines
---
 arch/x86/virt/svm/sev.c | 31 +++++++++++++++++++++++++++++++
 1 file changed, 31 insertions(+)
arch/x86/virt/svm/sev.c +0 -0
index dab6e1c290bc..04a58ac4339c 100644
@@ -535,6 +535,15 @@ int snp_prepare(void)
 
 	clear_rmp();
 

arch/x86/virt/svm/sev.c int snp_prepare(void) · line 547

Quoted 10 lines
arch/x86/virt/svm/sev.c +9 -0
+	/*
+	 * Disable CPU hotplug before enabling SNP, so no CPU can come online
+	 * without SnpEn while SNP is enabled; it is re-enabled in snp_shutdown()
+	 * once SNP is disabled.  Must be before cpus_read_lock():
+	 * cpu_hotplug_disable() takes cpu_add_remove_lock, which nests above
+	 * cpu_hotplug_lock.
+	 */
+	cpu_hotplug_disable();
+
 	cpus_read_lock();

What's the point of grabbing the hotplug lock if you just disabled hotplug?

arch/x86/virt/svm/sev.c int snp_prepare(void) · line 570

Quoted 4 lines
arch/x86/virt/svm/sev.c +0 -0
 	if (!cpumask_equal(cpu_online_mask, cpu_present_mask)) {
@@ -560,6 +569,10 @@ int snp_prepare(void)
 unlock:
 	cpus_read_unlock();

ditto.

arch/x86/virt/svm/sev.c int snp_prepare(void) · line 572

Quoted 2 lines
arch/x86/virt/svm/sev.c +1 -0
+	/* Re-enable CPU hotplug; SnpEn was never set. */

^ Superfluous comment.

Quoted 7 lines
arch/x86/virt/svm/sev.c +3 -0
+	if (ret)
+		cpu_hotplug_enable();
+
 	return ret;
 }
 EXPORT_SYMBOL_FOR_MODULES(snp_prepare, "ccp");

arch/x86/virt/svm/sev.c void snp_shutdown(void) · line 607

Quoted 8 lines
arch/x86/virt/svm/sev.c +5 -0
@@ -587,6 +600,13 @@ void snp_shutdown(void)
 
 	rmpopt_cleanup();
 
+	/*
+	 * Re-enable CPU hotplug now that the firmware has disabled SNP; CPU
+	 * hotplug is not re-enabled for a legacy SNP shutdown.  After
+	 * rmpopt_cleanup() so RMPOPT_BASE is cleared with hotplug still disabled.
+	 */

I can't parse that comment.

Quoted 5 lines
arch/x86/virt/svm/sev.c +2 -0
+	cpu_hotplug_enable();
+
 	clear_rmp();
 	on_each_cpu(mfd_reconfigure, NULL, 1);
 }
Signature · 4 lines
Regards/Gruss,
    Boris.

https://people.kernel.org/tglx/notes-about-netiquette
author maintainer
#33 lore raw

Re: [PATCH v10 3/6] x86/sev: Disable CPU hotplug while SNP is active

Quoted 35 lines · Borislav Petkov
Quoted 5 lines · Ashish Kalra

From: Ashish Kalra <[email protected]>

While SNP is active, every memory write is checked against the RMP to protect SEV-SNP guest memory. A core performs these RMP checks only once
...

Use this commit message for your next revision. The idea is to split it into smaller paragraphs for easier parsing, do simple formulations and not talk about future patches because git history is not always linear:

"While SNP is active, every memory write is checked against the RMP to protect SEV-SNP guest memory. A core performs these RMP checks only once SNP has been initialized via SNP_INIT and the SNP-enable bit in SYSCFG is set on that core; the firmware requires the SNP-enable bit to be set on every present CPU before SNP initialization.

A core that is not SNP-enabled and not SNP-initialized performs no RMP checks at all, so there is no valid configuration with SNP active and any CPU exempt from RMP checks.

The firmware determines which CPUs are present from the processor and the BIOS/UEFI configuration (e.g. SMT disabled in the BIOS) and enumerates them at SNP init; it is not aware of the OS bringing CPUs online or offline afterwards.

SNP_INIT fails unless SnpEn is set on all CPUs, so a CPU that is offline when SNP_INIT is issued, does not have SnpEn set, SNP_INIT fails, and there can be no SNP guest memory. OS CPU hotplug can thus diverge from the firmware's expectations and break SNP.

Tie CPU hotplug to the SNP-enable bit: disable it in snp_prepare() before SNP is enabled, and re-enable it in snp_shutdown() once the firmware has disabled SNP.

Quoted 7 lines

If snp_prepare() fails before enabling SNP it re-enables hotplug itself; once SNP is enabled hotplug stays disabled, including across a failed SNP_INIT and across the legacy SNP_SHUTDOWN_EX path, both of which leave SNP enabled.

A kexec target that boots with SNP already enabled, disables hotplug once in snp_rmptable_init(), since snp_prepare() bails when SNP is already enabled."

Sure.

Quoted 28 lines
Quoted 15 lines
---
 arch/x86/virt/svm/sev.c | 31 +++++++++++++++++++++++++++++++
 1 file changed, 31 insertions(+)
arch/x86/virt/svm/sev.c +0 -0
index dab6e1c290bc..04a58ac4339c 100644
@@ -535,6 +535,15 @@ int snp_prepare(void)
 
 	clear_rmp();
 

arch/x86/virt/svm/sev.c int snp_prepare(void) · line 547

Quoted 10 lines
arch/x86/virt/svm/sev.c +9 -0
+	/*
+	 * Disable CPU hotplug before enabling SNP, so no CPU can come online
+	 * without SnpEn while SNP is enabled; it is re-enabled in snp_shutdown()
+	 * once SNP is disabled.  Must be before cpus_read_lock():
+	 * cpu_hotplug_disable() takes cpu_add_remove_lock, which nests above
+	 * cpu_hotplug_lock.
+	 */
+	cpu_hotplug_disable();
+
 	cpus_read_lock();

What's the point of grabbing the hotplug lock if you just disabled hotplug?

Quoted 7 lines

arch/x86/virt/svm/sev.c int snp_prepare(void) · line 570

Quoted 4 lines
arch/x86/virt/svm/sev.c +0 -0
 	if (!cpumask_equal(cpu_online_mask, cpu_present_mask)) {
@@ -560,6 +569,10 @@ int snp_prepare(void)
 unlock:
 	cpus_read_unlock();

ditto.

This is part of the base code, but i will have to modify it now that hotplug is explicitly disabled.

Actually, i will have to remove cpus_read_lock()/unlock() across the whole patch series and also in the original code.

Thanks,
Ashish

maintainer
#34 lore raw

Re: [PATCH v10 3/6] x86/sev: Disable CPU hotplug while SNP is active

Quoted 2 lines · Ashish

Actually, i will have to remove cpus_read_lock()/unlock() across the whole patch series and also in the original code.

You need to do that in the same patch which adds the hotplug disable so that there's no hole in hotplug handling when bisecting.

Make sure to explain why you're doing that in the commit message.

Thx.

Signature · 4 lines
Regards/Gruss,
    Boris.

https://people.kernel.org/tglx/notes-about-netiquette
author maintainer
#35 lore raw

Re: [PATCH v10 3/6] x86/sev: Disable CPU hotplug while SNP is active

Quoted 11 lines · Borislav Petkov
Quoted 2 lines · Ashish

Actually, i will have to remove cpus_read_lock()/unlock() across the whole patch series and also in the original code.

You need to do that in the same patch which adds the hotplug disable so that there's no hole in hotplug handling when bisecting.

Make sure to explain why you're doing that in the commit message.

Thx.

This is the list of the consolidated changes:

Patch 2: "Initialize RMPOPT configuration MSRs":
- Renamed rmpopt_cleanup() to snp_cleanup_rmpopt() to match snp_setup_rmpopt().
- Simplified the 2 TB RMPOPT_BASE comment.
- Replaced the misleading primary-thread comment with "…only one thread per core needs to set up the RMPOPT_BASE MSR. All primary threads are online, otherwise SNP would not have been enabled."

Patch 3: "Disable CPU hotplug while SNP is active":
- Used your commit message.
- Since CPU hotplug is disabled while SNP is active, the online mask is stable, so I dropped the redundant cpus_read_lock()/cpus_read_unlock() in snp_prepare() and the cpus_read_lock() in snp_setup_rmpopt()/snp_cleanup_rmpopt() — all in this same patch so there's no hole in hotplug handling when bisecting, and explained why in the commit message.
- Dropped the superfluous "SnpEn was never set" comment and rewrote the snp_shutdown() re-enable comment.

The RMPOPT scan in rmpopt_work_handler() (added in 4/6) is likewise lock-free — it runs only while SNP is active, i.e. after the hotplug-disable patch, so it never needed cpus_read_lock() in the first place.

Thanks,
Ashish

author maintainer
#36 lore raw

Re: [PATCH v10 3/6] x86/sev: Disable CPU hotplug while SNP is active

Quoted 30 lines · Ashish
Quoted 11 lines · Borislav Petkov
Quoted 2 lines · Ashish

Actually, i will have to remove cpus_read_lock()/unlock() across the whole patch series and also in the original code.

You need to do that in the same patch which adds the hotplug disable so that there's no hole in hotplug handling when bisecting.

Make sure to explain why you're doing that in the commit message.

Thx.

This is the list of the consolidated changes:

Patch 2: "Initialize RMPOPT configuration MSRs":
- Renamed rmpopt_cleanup() to snp_cleanup_rmpopt() to match snp_setup_rmpopt().
- Simplified the 2 TB RMPOPT_BASE comment.
- Replaced the misleading primary-thread comment with "…only one thread per core needs to set up the RMPOPT_BASE MSR. All primary threads are online, otherwise SNP would not have been enabled."

Patch 3: "Disable CPU hotplug while SNP is active":
- Used your commit message.
- Since CPU hotplug is disabled while SNP is active, the online mask is stable, so I dropped the redundant cpus_read_lock()/cpus_read_unlock() in snp_prepare() and the cpus_read_lock() in snp_setup_rmpopt()/snp_cleanup_rmpopt() — all in this same patch so there's no hole in hotplug handling when bisecting, and explained why in the commit message.
- Dropped the superfluous "SnpEn was never set" comment and rewrote the snp_shutdown() re-enable comment.

Quoted 3 lines

The RMPOPT scan in rmpopt_work_handler() (added in 4/6) is likewise lock-free — it runs only while SNP is active, i.e. after the hotplug-disable patch, so it never needed cpus_read_lock() in the first place.

Actually for v11, I am reordering the patches so "Disable CPU hotplug" comes before "Initialize RMPOPT configuration MSRs."

snp_prepare()'s cpus_read_lock() is then dropped in the hotplug-disable patch itself (bisect stays clean, explained in the commit message), and snp_setup_rmpopt()/snp_cleanup_rmpopt() — introduced afterwards — never take it in the first place, same as rmpopt_work_handler(). So the removal is implied by ordering rather than an add-then-remove.

Also folded in: renamed rmpopt_cleanup() to snp_cleanup_rmpopt(), simplified the RMPOPT_BASE comment and the primary-thread comment and used your reworded hotplug commit message.

Thanks,
Ashish

author maintainer
#37 lore raw

PATCH v10 4/6 x86/sev: Add support to perform RMP optimizations asynchronously

In reply to Ashish Kalra

From: Ashish Kalra <[email protected]>

When SEV-SNP is enabled, all writes to memory are checked to ensure integrity of SNP guest memory. This imposes performance overhead on the whole system.

RMPOPT is a new instruction that minimizes the performance overhead of RMP checks on the hypervisor and on non-SNP guests by allowing RMP checks to be skipped for 1GB regions of memory that are known not to contain any SEV-SNP guest memory.

Add support for performing RMP optimizations asynchronously using a dedicated workqueue.

Enable RMPOPT optimizations for up to 2TB of system RAM starting from the lowest physical memory address aligned down to a 1GB boundary at RMP initialization time. RMP checks can initially be skipped for 1GB memory ranges that do not contain SEV-SNP guest memory (excluding preassigned pages such as the RMP table and firmware pages). As SNP guests are launched, RMPUPDATE will disable the corresponding RMPOPT optimizations.

---
 arch/x86/virt/svm/sev.c | 167 +++++++++++++++++++++++++++++++++++++++-
 1 file changed, 164 insertions(+), 3 deletions(-)
arch/x86/virt/svm/sev.c +164 -3
index 04a58ac4339c..40b06e959ee8 100644
@@ -19,6 +19,7 @@
 #include <linux/iommu.h>
 #include <linux/amd-iommu.h>
 #include <linux/nospec.h>
+#include <linux/workqueue.h>
 
 #include <asm/sev.h>
 #include <asm/processor.h>
@@ -125,9 +126,20 @@ static void *rmp_bookkeeping __ro_after_init;
 static u64 probed_rmp_base, probed_rmp_size;
 
 static cpumask_var_t rmpopt_cpumask;
-static phys_addr_t rmpopt_pa_start;
+static phys_addr_t rmpopt_pa_start, rmpopt_pa_end;
 static bool rmpopt_capable;
 
+enum rmpopt_function {
+	RMPOPT_FUNC_VERIFY_AND_REPORT_STATUS,
+	RMPOPT_FUNC_REPORT_STATUS
+};
+
+#define RMPOPT_WORK_TIMEOUT	10000
+
+static struct workqueue_struct *rmpopt_wq;
+static struct delayed_work rmpopt_delayed_work;
+static DEFINE_MUTEX(rmpopt_wq_mutex);
+
 static LIST_HEAD(snp_leaked_pages_list);
 static DEFINE_SPINLOCK(snp_leaked_pages_list_lock);
 
@@ -581,13 +593,22 @@ static void rmpopt_cleanup(void)
 {
 	int cpu;
 
+	guard(mutex)(&rmpopt_wq_mutex);
+
+	if (!rmpopt_wq)
+		return;
+
+	cancel_delayed_work_sync(&rmpopt_delayed_work);
+	destroy_workqueue(rmpopt_wq);
+
 	scoped_guard(cpus_read_lock) {
 		for_each_cpu(cpu, rmpopt_cpumask)
 			wrmsrq_on_cpu(cpu, MSR_AMD64_RMPOPT_BASE, 0);
 	}
 
 	free_cpumask_var(rmpopt_cpumask);
-	rmpopt_pa_start = 0;
+	rmpopt_pa_start = rmpopt_pa_end = 0;
+	rmpopt_wq = NULL;
 }
 
 void snp_shutdown(void)
@@ -617,6 +638,103 @@ void snp_clear_rmpopt_capable(void)
 	rmpopt_capable = false;
 }
 
+/*
+ * RMPOPT: F2 0F 01 FC
+ *   Input:  RAX = system physical address (1GB aligned)
+ *           RCX = operation type
+ *   Output: CF set if the range was optimized
+ */
+static inline bool __rmpopt(u64 pa_start, u64 op_type)
+{
+	bool optimized;
+
+	asm volatile(".byte 0xf2, 0x0f, 0x01, 0xfc"
+		     : "=@ccc" (optimized)
+		     : "a" (pa_start), "c" (op_type)
+		     : "memory", "cc");
+
+	return optimized;
+}
+
+static void rmpopt(u64 pa)
+{
+	u64 pa_start = ALIGN_DOWN(pa, SZ_1G);
+	u64 op_type = RMPOPT_FUNC_VERIFY_AND_REPORT_STATUS;
+
+	__rmpopt(pa_start, op_type);
+}
+
+/*
+ * 'val' is a system physical address.
+ */
+static void rmpopt_smp(void *val)
+{
+	rmpopt((u64)val);
+}
+
+/*
+ * RMPOPT optimizations skip RMP checks at 1GB granularity if this
+ * range of memory does not contain any SNP guest memory.
+ */
+static void rmpopt_work_handler(struct work_struct *work)
+{
+	cpumask_var_t follower_mask;
+	phys_addr_t pa;
+	int this_cpu;
+
+	pr_info("Attempt RMP optimizations on physical address range @1GB alignment [0x%016llx - 0x%016llx]\n",
+		rmpopt_pa_start, rmpopt_pa_end);
+
+	if (!alloc_cpumask_var(&follower_mask, GFP_KERNEL)) {
+		pr_warn("RMP optimization pass skipped: cpumask allocation failed\n");
+		return;
+	}
+
+	/*
+	 * RMPOPT scans the RMP table, stores the result of the scan in the
+	 * reserved processor memory. The RMP scan is the most expensive
+	 * part. If a second RMPOPT occurs, it can skip the expensive scan
+	 * if they can see a cached result in the reserved processor memory.
+	 *
+	 * Do RMPOPT on one CPU alone. Then, follow that up with RMPOPT
+	 * on every other primary thread. Followers are "designed to"
+	 * skip the scan if they see the "cached" scan results.
+	 *
+	 * Pin the worker to the current CPU for the leader loop so that
+	 * this_cpu remains valid and the RMPOPT instruction executes on
+	 * the correct CPU.  Use migrate_disable() rather than get_cpu() to
+	 * prevent migration while still allowing preemption.
+	 */
+	migrate_disable();
+	this_cpu = smp_processor_id();
+
+	cpumask_andnot(follower_mask, rmpopt_cpumask,
+		       topology_sibling_cpumask(this_cpu));
+
+	for (pa = rmpopt_pa_start; pa < rmpopt_pa_end; pa += SZ_1G) {
+		rmpopt(pa);
+		cond_resched();
+	}
+	migrate_enable();
+
+	/*
+	 * Followers: run RMPOPT on remaining cores.  CPUs cannot go offline
+	 * while SNP is active, so the follower set stays valid across the
+	 * scan and cpus_read_lock() is uncontended.
+	 */
+	scoped_guard(cpus_read_lock) {
+		for (pa = rmpopt_pa_start; pa < rmpopt_pa_end; pa += SZ_1G) {
+			on_each_cpu_mask(follower_mask, rmpopt_smp,
+					 (void *)pa, true);
+
+			/* Give a chance for other threads to run */
+			cond_resched();
+		}
+	}
+
+	free_cpumask_var(follower_mask);
+}
+
 void snp_setup_rmpopt(void)
 {
 	u64 rmpopt_base;
@@ -625,14 +743,42 @@ void snp_setup_rmpopt(void)
 	if (!cpu_feature_enabled(X86_FEATURE_RMPOPT) || !rmpopt_capable)
 		return;
 
+	guard(mutex)(&rmpopt_wq_mutex);
+
+	/*
+	 * Guard against re-initialization.  When SNP_SHUTDOWN_EX is issued
+	 * with x86_snp_shutdown=0, snp_shutdown() is not called and
+	 * rmpopt_cleanup() is skipped, but snp_initialized is still cleared.
+	 * A subsequent __sev_snp_init_locked() would call snp_setup_rmpopt()
+	 * again, leaking the existing workqueue, delayed work, and cpumask
+	 * state.
+	 */
+	if (rmpopt_wq)
+		return;
+
+	/*
+	 * Create an RMPOPT-specific workqueue to avoid scheduling
+	 * RMPOPT workitem on the global system workqueue.
+	 */
+	rmpopt_wq = alloc_workqueue("rmpopt_wq", WQ_UNBOUND, 1);
+	if (!rmpopt_wq) {
+		pr_err("Failed to allocate RMPOPT workqueue\n");
+		return;
+	}
+
+	INIT_DELAYED_WORK(&rmpopt_delayed_work, rmpopt_work_handler);
+
 	if (!zalloc_cpumask_var(&rmpopt_cpumask, GFP_KERNEL)) {
 		pr_err("Failed to allocate RMPOPT cpumask\n");
+		destroy_workqueue(rmpopt_wq);
+		rmpopt_wq = NULL;
 		return;
 	}
 
 	/*
 	 * The RMPOPT_BASE MSR is per-core, so only one thread per core needs
-	 * to set up the RMPOPT_BASE MSR.
+	 * to set up the RMPOPT_BASE MSR. Likewise, only one thread per core
+	 * needs to issue the RMPOPT instruction.
 	 *
 	 * Note: only online primary threads are included.  If a core's
 	 * primary thread is offline, that core is not covered.  CPU hotplug
@@ -655,6 +801,21 @@ void snp_setup_rmpopt(void)
 		for_each_cpu(cpu, rmpopt_cpumask)
 			wrmsrq_on_cpu(cpu, MSR_AMD64_RMPOPT_BASE, rmpopt_base);
 	}
+
+	rmpopt_pa_end = ALIGN(PFN_PHYS(max_pfn), SZ_1G);
+
+	/* Limit memory scanning to 2TB of RAM */
+	if ((rmpopt_pa_end - rmpopt_pa_start) > SZ_2T) {
+		pr_info("RMPOPT coverage limited to 2TB; memory above 0x%llx not optimized\n",
+			rmpopt_pa_start + SZ_2T);
+		rmpopt_pa_end = rmpopt_pa_start + SZ_2T;
+	}
+
+	/*
+	 * Once all per-CPU RMPOPT tables have been configured, enable RMPOPT
+	 * optimizations on all physical memory.
+	 */
+	queue_delayed_work(rmpopt_wq, &rmpopt_delayed_work, 0);
 }
 EXPORT_SYMBOL_FOR_MODULES(snp_setup_rmpopt, "ccp");
 
Signature · 1 line
2.43.0
#38 lore raw

Re: [PATCH v10 4/6] x86/sev: Add support to perform RMP optimizations asynchronously

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [Medium] Re-initializing SNP after a partial shutdown bypasses the RMPOPT optimization pass, leaving the…

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] Re-initializing SNP after a partial shutdown bypasses the RMPOPT optimization pass, leaving the new RMP table unoptimized. --

commit 2d85f8f029da9849b60d0154807adccc997f6502
Author: Ashish Kalra <[email protected]>

x86/sev: Add support to perform RMP optimizations asynchronously

This patch introduces support for the RMPOPT instruction to minimize performance overhead by skipping RMP checks on 1GB memory ranges known not to contain SEV-SNP guest memory. It adds a dedicated workqueue to perform these optimizations asynchronously.

Quoted 4 lines
arch/x86/virt/svm/sev.c +0 -0
index 04a58ac4339cb..40b06e959ee87 100644

[ ... ]

Quoted 6 lines
arch/x86/virt/svm/sev.c +2 -0
@@ -625,14 +743,42 @@ void snp_setup_rmpopt(void)
 	if (!cpu_feature_enabled(X86_FEATURE_RMPOPT) || !rmpopt_capable)
 		return;
 
+	guard(mutex)(&rmpopt_wq_mutex);
+

arch/x86/virt/svm/sev.c void snp_setup_rmpopt(void) · line 757

Quoted 10 lines
arch/x86/virt/svm/sev.c +10 -0
+	/*
+	 * Guard against re-initialization.  When SNP_SHUTDOWN_EX is issued
+	 * with x86_snp_shutdown=0, snp_shutdown() is not called and
+	 * rmpopt_cleanup() is skipped, but snp_initialized is still cleared.
+	 * A subsequent __sev_snp_init_locked() would call snp_setup_rmpopt()
+	 * again, leaking the existing workqueue, delayed work, and cpumask
+	 * state.
+	 */
+	if (rmpopt_wq)
+		return;

[Severity: Medium]
Will this early return prevent the initial RMPOPT optimization pass if SNP is re-initialized after a partial shutdown?

If a partial shutdown occurs where rmpopt_cleanup() is skipped, rmpopt_wq remains allocated. When snp_setup_rmpopt() is called again during re-initialization, returning early here skips the queue_delayed_work() call at the end of the function.

Does this mean the system will run without RMP optimizations on the newly initialized RMP table, incurring RMP check overhead until a later event triggers a rescan?

[ ... ]

Quoted 13 lines
arch/x86/virt/svm/sev.c +12 -0
+	/* Limit memory scanning to 2TB of RAM */
+	if ((rmpopt_pa_end - rmpopt_pa_start) > SZ_2T) {
+		pr_info("RMPOPT coverage limited to 2TB; memory above 0x%llx not optimized\n",
+			rmpopt_pa_start + SZ_2T);
+		rmpopt_pa_end = rmpopt_pa_start + SZ_2T;
+	}
+
+	/*
+	 * Once all per-CPU RMPOPT tables have been configured, enable RMPOPT
+	 * optimizations on all physical memory.
+	 */
+	queue_delayed_work(rmpopt_wq, &rmpopt_delayed_work, 0);
 }
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=4
author maintainer
#39 lore raw

Re: [PATCH v10 4/6] x86/sev: Add support to perform RMP optimizations asynchronously

Quoted 42 lines · On 6/30/2026 1:28 PM, [email protected] wrote:

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] Re-initializing SNP after a partial shutdown bypasses the RMPOPT optimization pass, leaving the new RMP table unoptimized. --

commit 2d85f8f029da9849b60d0154807adccc997f6502
Author: Ashish Kalra <[email protected]>

x86/sev: Add support to perform RMP optimizations asynchronously

This patch introduces support for the RMPOPT instruction to minimize performance overhead by skipping RMP checks on 1GB memory ranges known not to contain SEV-SNP guest memory. It adds a dedicated workqueue to perform these optimizations asynchronously.

Quoted 4 lines
arch/x86/virt/svm/sev.c +0 -0
index 04a58ac4339cb..40b06e959ee87 100644

[ ... ]

Quoted 6 lines
arch/x86/virt/svm/sev.c +2 -0
@@ -625,14 +743,42 @@ void snp_setup_rmpopt(void)
 	if (!cpu_feature_enabled(X86_FEATURE_RMPOPT) || !rmpopt_capable)
 		return;
 
+	guard(mutex)(&rmpopt_wq_mutex);
+

arch/x86/virt/svm/sev.c void snp_setup_rmpopt(void) · line 757

Quoted 10 lines
arch/x86/virt/svm/sev.c +10 -0
+	/*
+	 * Guard against re-initialization.  When SNP_SHUTDOWN_EX is issued
+	 * with x86_snp_shutdown=0, snp_shutdown() is not called and
+	 * rmpopt_cleanup() is skipped, but snp_initialized is still cleared.
+	 * A subsequent __sev_snp_init_locked() would call snp_setup_rmpopt()
+	 * again, leaking the existing workqueue, delayed work, and cpumask
+	 * state.
+	 */
+	if (rmpopt_wq)
+		return;

[Severity: Medium]
Will this early return prevent the initial RMPOPT optimization pass if SNP is re-initialized after a partial shutdown?

Quoted 9 lines

If a partial shutdown occurs where rmpopt_cleanup() is skipped, rmpopt_wq remains allocated. When snp_setup_rmpopt() is called again during re-initialization, returning early here skips the queue_delayed_work() call at the end of the function.

Does this mean the system will run without RMP optimizations on the newly initialized RMP table, incurring RMP check overhead until a later event triggers a rescan?

Good point — the early return skips the initial optimization pass on re-init. It's performance-only (RMP protection is unaffected) and self-heals on the next SNP guest teardown via snp_rmpopt_all_physmem(), which re-optimizes.

In practice this path doesn't arise on RMPOPT-capable platforms. RMPOPT is supported only on newer platforms, which use the x86_snp_shutdown shutdown path, that calls snp_shutdown() -> rmpopt_cleanup(), which destroys and NULLs rmpopt_wq, so a subsequent re-init re-allocates and re-queues the initial pass as usual. The partial-shutdown case this guard covers (x86_snp_shutdown=0, rmpopt_cleanup() skipped) is legacy-firmware-only and effectively doesn't occur where RMPOPT is available. So I'll leave this as-is, the guard just prevents a resource leak in that unlikely legacy re-init, and correctness (and, on RMPOPT platforms, the initial pass) is unaffected.

Thanks,
Ashish

Quoted 16 lines

[ ... ]

Quoted 13 lines
arch/x86/virt/svm/sev.c +12 -0
+	/* Limit memory scanning to 2TB of RAM */
+	if ((rmpopt_pa_end - rmpopt_pa_start) > SZ_2T) {
+		pr_info("RMPOPT coverage limited to 2TB; memory above 0x%llx not optimized\n",
+			rmpopt_pa_start + SZ_2T);
+		rmpopt_pa_end = rmpopt_pa_start + SZ_2T;
+	}
+
+	/*
+	 * Once all per-CPU RMPOPT tables have been configured, enable RMPOPT
+	 * optimizations on all physical memory.
+	 */
+	queue_delayed_work(rmpopt_wq, &rmpopt_delayed_work, 0);
 }
#40 lore raw

Re: [PATCH v10 4/6] x86/sev: Add support to perform RMP optimizations asynchronously

In reply to Ashish Kalra

Hello Ashish,

Quoted 23 lines · Ashish Kalra
arch/x86/virt/svm/sev.c +23 -0
+	/*
+	 * RMPOPT scans the RMP table, stores the result of the scan in the
+	 * reserved processor memory. The RMP scan is the most expensive
+	 * part. If a second RMPOPT occurs, it can skip the expensive scan
+	 * if they can see a cached result in the reserved processor memory.
+	 *
+	 * Do RMPOPT on one CPU alone. Then, follow that up with RMPOPT
+	 * on every other primary thread. Followers are "designed to"
+	 * skip the scan if they see the "cached" scan results.
+	 *
+	 * Pin the worker to the current CPU for the leader loop so that
+	 * this_cpu remains valid and the RMPOPT instruction executes on
+	 * the correct CPU.  Use migrate_disable() rather than get_cpu() to
+	 * prevent migration while still allowing preemption.
+	 */
+	migrate_disable();
+	this_cpu = smp_processor_id();
+
+	cpumask_andnot(follower_mask, rmpopt_cpumask,
+		       topology_sibling_cpumask(this_cpu));
+
+	for (pa = rmpopt_pa_start; pa < rmpopt_pa_end; pa += SZ_1G) {
+		rmpopt(pa);

arch/x86/virt/svm/sev.c void snp_clear_rmpopt_capable(void) · line 725

Quoted 10 lines
arch/x86/virt/svm/sev.c +10 -0
+		cond_resched();
+	}
+	migrate_enable();
+
+	/*
+	 * Followers: run RMPOPT on remaining cores.  CPUs cannot go offline
+	 * while SNP is active, so the follower set stays valid across the
+	 * scan and cpus_read_lock() is uncontended.
+	 */
+	scoped_guard(cpus_read_lock) {

We can only reach here after disabling hotplug. Do we still to hold the cpus_read_lock?

arch/x86/virt/svm/sev.c void snp_clear_rmpopt_capable(void) · line 731

Quoted 6 lines
arch/x86/virt/svm/sev.c +6 -0
+		for (pa = rmpopt_pa_start; pa < rmpopt_pa_end; pa += SZ_1G) {
+			on_each_cpu_mask(follower_mask, rmpopt_smp,
+					 (void *)pa, true);
+
+			/* Give a chance for other threads to run */
+			cond_resched();

Ever since commit 7dadeaa6e851 ("sched: Further restrict the preemption modes"), one cannot select PREEMPT_VOLUNTARY on x86 and cond_resched() is a nop with all the other preemption models (PREEMPT_NONE will not preempt a kernel thread; PREEMPT_LAZY / PREEMPT / PREEMPT_RT all make the kernel fully preemptible except for sections with preempt_count != 0)

You can drop these cond_resched() calls in your respin.

Quoted 6 lines
arch/x86/virt/svm/sev.c +6 -0
+		}
+	}
+
+	free_cpumask_var(follower_mask);
+}
+
Signature · 2 lines
Thanks and Regards,
Prateek
author maintainer
#41 lore raw

Re: [PATCH v10 4/6] x86/sev: Add support to perform RMP optimizations asynchronously

Hello Prateek,

Quoted 31 lines · K Prateek Nayak

Hello Ashish,

Quoted 28 lines · Ashish Kalra
arch/x86/virt/svm/sev.c +28 -0
+	/*
+	 * RMPOPT scans the RMP table, stores the result of the scan in the
+	 * reserved processor memory. The RMP scan is the most expensive
+	 * part. If a second RMPOPT occurs, it can skip the expensive scan
+	 * if they can see a cached result in the reserved processor memory.
+	 *
+	 * Do RMPOPT on one CPU alone. Then, follow that up with RMPOPT
+	 * on every other primary thread. Followers are "designed to"
+	 * skip the scan if they see the "cached" scan results.
+	 *
+	 * Pin the worker to the current CPU for the leader loop so that
+	 * this_cpu remains valid and the RMPOPT instruction executes on
+	 * the correct CPU.  Use migrate_disable() rather than get_cpu() to
+	 * prevent migration while still allowing preemption.
+	 */
+	migrate_disable();
+	this_cpu = smp_processor_id();
+
+	cpumask_andnot(follower_mask, rmpopt_cpumask,
+		       topology_sibling_cpumask(this_cpu));
+
+	for (pa = rmpopt_pa_start; pa < rmpopt_pa_end; pa += SZ_1G) {
+		rmpopt(pa);
+		cond_resched();
+	}
+	migrate_enable();
+
+	/*
Quoted 9 lines

arch/x86/virt/svm/sev.c void snp_clear_rmpopt_capable(void) · line 725

Quoted 5 lines
arch/x86/virt/svm/sev.c +5 -0
+	 * Followers: run RMPOPT on remaining cores.  CPUs cannot go offline
+	 * while SNP is active, so the follower set stays valid across the
+	 * scan and cpus_read_lock() is uncontended.
+	 */
+	scoped_guard(cpus_read_lock) {

We can only reach here after disabling hotplug. Do we still to hold the cpus_read_lock?

Right — hotplug is disabled for the whole SNP-active window and this work only runs while SNP is active, so the follower set is already stable. I'll drop the cpus_read_lock() and document it:

/*
* Followers: run RMPOPT on the remaining cores. cpus_read_lock() is
* intentionally not held here: CPU hotplug is disabled for the entire
* time SNP is active (see snp_prepare()), and this work only runs while
* SNP is active, so the follower set stays valid across the whole scan. */

I'll also trim the matching sentence in patch 3's commit log ("ensures cpus_read_lock() in the scan is uncontended").

Actually one of the reasons, why i am still holding the cpus_read_lock is that Sashiko is likely to flag the *bare* on_each_cpu_mask() as missing cpus_read_lock() — the comment above captures why it's safe, so the reasoning is in-tree if it comes up.

Quoted 14 lines

arch/x86/virt/svm/sev.c void snp_clear_rmpopt_capable(void) · line 731

Quoted 6 lines
arch/x86/virt/svm/sev.c +6 -0
+		for (pa = rmpopt_pa_start; pa < rmpopt_pa_end; pa += SZ_1G) {
+			on_each_cpu_mask(follower_mask, rmpopt_smp,
+					 (void *)pa, true);
+
+			/* Give a chance for other threads to run */
+			cond_resched();

Ever since commit 7dadeaa6e851 ("sched: Further restrict the preemption modes"), one cannot select PREEMPT_VOLUNTARY on x86 and cond_resched() is a nop with all the other preemption models (PREEMPT_NONE will not preempt a kernel thread; PREEMPT_LAZY / PREEMPT / PREEMPT_RT all make the kernel fully preemptible except for sections with preempt_count != 0)

Quoted 2 lines

You can drop these cond_resched() calls in your respin.

Will do — with PREEMPT_VOLUNTARY unavailable on x86 and the remaining models fully preemptible, so cond_resched() are nops. Dropping both.

Thanks,
Ashish

Quoted 7 lines
Quoted 6 lines
arch/x86/virt/svm/sev.c +6 -0
+		}
+	}
+
+	free_cpumask_var(follower_mask);
+}
+
author maintainer
#42 lore raw

PATCH v10 5/6 x86/sev: Add interface to re-enable RMP optimizations.

In reply to Ashish Kalra

From: Ashish Kalra <[email protected]>

RMPOPT table is a per-CPU table which indicates if 1GB regions of physical memory are entirely hypervisor-owned or not.

When performing host memory accesses in hypervisor mode as well as non-SNP guest mode, the processor may consult the RMPOPT table to potentially skip an RMP access and improve performance.

Normal guest events clear RMP optimizations: pages are converted from shared to private as SNP guests are launched, and large pages are split and collapsed during guest operation -- both clear the RMPOPT optimizations for the affected 1GB regions. Conversely, guest pages are converted back to shared during SNP guest termination, so those regions may become eligible for RMPOPT optimization again.

Without some intervention, all RMP optimizations would eventually be lost. Add an interface to re-optimize all of physical memory.

The interface uses mod_delayed_work() instead of queue_delayed_work() so that the delay timer is reset on each call. This provides proper batching semantics: re-optimization runs 10 seconds after the *last* VM termination rather than after the first. mod_delayed_work() also re-queues work that is already in-flight, so a re-scan request during an active scan is not silently dropped.

---
 arch/x86/include/asm/sev.h |  2 ++
 arch/x86/virt/svm/sev.c    | 15 +++++++++++++++
 2 files changed, 17 insertions(+)
arch/x86/include/asm/sev.h +2 -0
index 0243989f229b..54b4ae5c3735 100644
@@ -662,6 +662,7 @@ static inline void snp_leak_pages(u64 pfn, unsigned int pages)
 	__snp_leak_pages(pfn, pages, true);
 }
 int snp_prepare(void);
+void snp_rmpopt_all_physmem(void);
 void snp_setup_rmpopt(void);
 void snp_clear_rmpopt_capable(void);
 void snp_shutdown(void);
@@ -682,6 +683,7 @@ static inline void snp_leak_pages(u64 pfn, unsigned int npages) {}
 static inline void kdump_sev_callback(void) { }
 static inline void snp_fixup_e820_tables(void) {}
 static inline int snp_prepare(void) { return -ENODEV; }
+static inline void snp_rmpopt_all_physmem(void) {}
 static inline void snp_setup_rmpopt(void) {}
 static inline void snp_clear_rmpopt_capable(void) {}
 static inline void snp_shutdown(void) {}
arch/x86/virt/svm/sev.c +15 -0
index 40b06e959ee8..6672c7f17825 100644
@@ -735,6 +735,21 @@ static void rmpopt_work_handler(struct work_struct *work)
 	free_cpumask_var(follower_mask);
 }
 
+void snp_rmpopt_all_physmem(void)
+{
+	if (!cpu_feature_enabled(X86_FEATURE_RMPOPT) || !rmpopt_capable)
+		return;
+
+	guard(mutex)(&rmpopt_wq_mutex);
+
+	if (!rmpopt_wq)
+		return;
+
+	mod_delayed_work(rmpopt_wq, &rmpopt_delayed_work,
+			 msecs_to_jiffies(RMPOPT_WORK_TIMEOUT));
+}
+EXPORT_SYMBOL_FOR_MODULES(snp_rmpopt_all_physmem, "kvm-amd");
+
 void snp_setup_rmpopt(void)
 {
 	u64 rmpopt_base;
Signature · 1 line
2.43.0
author maintainer
#43 lore raw

PATCH v10 6/6 KVM: SEV: Perform RMP optimizations on SNP guest shutdown

In reply to Ashish Kalra

From: Ashish Kalra <[email protected]>

Pages are converted from shared to private as SNP guests are launched. This destroys exisiting RMPOPT optimizations in the regions where pages are converted.

Conversely, guest pages are converted back to shared during SNP guest termination and their region may become eligible for RMPOPT optimization.

To take advantage of this, perform RMPOPT after guest termination. Do it after a delay so that a single RMPOPT pass can be done if multiple guests terminate in a short period of time.

---
 arch/x86/kvm/svm/sev.c | 10 ++++++++++
 1 file changed, 10 insertions(+)
arch/x86/kvm/svm/sev.c +10 -0
index 427229347876..c574849b1587 100644
@@ -2992,6 +2992,16 @@ void sev_vm_destroy(struct kvm *kvm)
 		 */
 		if (snp_decommission_context(kvm))
 			return;
+
+		/*
+		 * Perform RMP optimizations on memory freed by terminating
+		 * guests.  The scan is deferred, so it normally runs after
+		 * sev_gmem_invalidate() has converted this guest's pages back to
+		 * shared, and picks them up then.  A very large guest whose
+		 * conversion has not finished by then is picked up by a later
+		 * teardown's scan.
+		 */
+		snp_rmpopt_all_physmem();
 	} else {
 		sev_unbind_asid(kvm, sev->handle);
 	}
Signature · 1 line
2.43.0
#44 lore raw

Re: [PATCH v10 6/6] KVM: SEV: Perform RMP optimizations on SNP guest shutdown

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [High] Indefinite deferral of RMP optimizations via mod_delayed_work timer reset. --

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Indefinite deferral of RMP optimizations via mod_delayed_work timer reset. --

commit 5a78324bed688cb2c4b58d363233a323513bd9b4
Author: Ashish Kalra <[email protected]>

KVM: SEV: Perform RMP optimizations on SNP guest shutdown

This commit performs RMP optimizations on memory freed by terminating SNP guests. The scan is deferred so that a single optimization pass can batch memory from multiple guests terminating in a short period of time.

Quoted 4 lines
arch/x86/kvm/svm/sev.c +0 -0
index 4272293478766..c574849b15878 100644

[ ... ]

Quoted 4 lines
arch/x86/kvm/svm/sev.c +0 -0
@@ -2992,6 +2992,16 @@ void sev_vm_destroy(struct kvm *kvm)
 		 */
 		if (snp_decommission_context(kvm))
 			return;

arch/x86/kvm/svm/sev.c void sev_vm_destroy(struct kvm *kvm) · line 3004

Quoted 10 lines
arch/x86/kvm/svm/sev.c +10 -0
+
+		/*
+		 * Perform RMP optimizations on memory freed by terminating
+		 * guests.  The scan is deferred, so it normally runs after
+		 * sev_gmem_invalidate() has converted this guest's pages back to
+		 * shared, and picks them up then.  A very large guest whose
+		 * conversion has not finished by then is picked up by a later
+		 * teardown's scan.
+		 */
+		snp_rmpopt_all_physmem();

[Severity: High]
Can an unprivileged user indefinitely defer the RMP optimizations by repeatedly creating and destroying minimal SNP VMs?

Looking at snp_rmpopt_all_physmem(), it uses mod_delayed_work(), which cancels any pending timer and pushes the execution timeout back on every call.

If VMs are destroyed at a frequency higher than the timeout, the background optimization task might be starved and never execute, leading to degraded memory access performance.

Would using queue_delayed_work() instead prevent this starvation by ensuring the timer is not pushed back if the work is already queued?

Quoted 3 lines
arch/x86/kvm/svm/sev.c +0 -0
 	} else {
 		sev_unbind_asid(kvm, sev->handle);
 	}
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=6
author maintainer
#45 lore raw

Re: [PATCH v10 6/6] KVM: SEV: Perform RMP optimizations on SNP guest shutdown

Quoted 41 lines · On 6/30/2026 1:24 PM, [email protected] wrote:

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Indefinite deferral of RMP optimizations via mod_delayed_work timer reset. --

commit 5a78324bed688cb2c4b58d363233a323513bd9b4
Author: Ashish Kalra <[email protected]>

KVM: SEV: Perform RMP optimizations on SNP guest shutdown

This commit performs RMP optimizations on memory freed by terminating SNP guests. The scan is deferred so that a single optimization pass can batch memory from multiple guests terminating in a short period of time.

Quoted 4 lines
arch/x86/kvm/svm/sev.c +0 -0
index 4272293478766..c574849b15878 100644

[ ... ]

Quoted 4 lines
arch/x86/kvm/svm/sev.c +0 -0
@@ -2992,6 +2992,16 @@ void sev_vm_destroy(struct kvm *kvm)
 		 */
 		if (snp_decommission_context(kvm))
 			return;

arch/x86/kvm/svm/sev.c void sev_vm_destroy(struct kvm *kvm) · line 3004

Quoted 10 lines
arch/x86/kvm/svm/sev.c +10 -0
+
+		/*
+		 * Perform RMP optimizations on memory freed by terminating
+		 * guests.  The scan is deferred, so it normally runs after
+		 * sev_gmem_invalidate() has converted this guest's pages back to
+		 * shared, and picks them up then.  A very large guest whose
+		 * conversion has not finished by then is picked up by a later
+		 * teardown's scan.
+		 */
+		snp_rmpopt_all_physmem();

[Severity: High]
Can an unprivileged user indefinitely defer the RMP optimizations by repeatedly creating and destroying minimal SNP VMs?

Looking at snp_rmpopt_all_physmem(), it uses mod_delayed_work(), which cancels any pending timer and pushes the execution timeout back on every call.

Quoted 11 lines

If VMs are destroyed at a frequency higher than the timeout, the background optimization task might be starved and never execute, leading to degraded memory access performance.

Would using queue_delayed_work() instead prevent this starvation by ensuring the timer is not pushed back if the work is already queued?

Quoted 3 lines
arch/x86/kvm/svm/sev.c +0 -0
 	} else {
 		sev_unbind_asid(kvm, sev->handle);
 	}

mod_delayed_work() is deliberate here. It runs the scan RMPOPT_WORK_TIMEOUT after the last teardown, which coalesces a burst into one scan and gives each guest's page conversion (sev_gmem_invalidate() -> shared) time to finish before the scan — so its freed pages actually get optimized.

The only difference from queue_delayed_work() is which state the work is in when a teardown happens:
- Executing (a scan is running): the work isn't pending, so both calls arm a fresh timer and schedule a follow-up scan — no difference.
- Pending (a scan is already scheduled but hasn't run yet): mod_delayed_work() restarts the timer on every teardown, so the scan always runs a full RMPOPT_WORK_TIMEOUT after the most recent teardown — each teardown gets enough time for its pages to be freed before the scan looks at them. queue_delayed_work() keeps the timer set by the first teardown and ignores later ones, so the scan can fire while a recent teardown's pages are still being freed, those pages are missed, and since nothing reschedules, they stay unoptimized until some later teardown starts a new timer.

- Example (10s timeout): VM-A ends at t=0 -> scan scheduled for t=10. VM-B ends at t=9. With mod, the scan moves to t=19, so VM-B's pages are freed in time and get optimized. With queue, the scan still fires at t=10 — before VM-B's pages are freed — so VM-B is missed until the next teardown.

That's a regression in the common case.

So queue_delayed_work() might help a rare edge case but hurts the common one. So, keeping mod_delayed_work().

Thanks,
Ashish

author maintainer
#46 lore raw
In reply to Ashish Kalra

Hi Boris, Dave,

Just a gentle ping on this series. I believe v10 addresses the feedback from the earlier rounds. Please let me know if it looks good to merge, or if there's anything else you'd like me to change.

Thanks,
Ashish

Quoted 278 lines · Ashish Kalra

From: Ashish Kalra <[email protected]>

In the SEV-SNP architecture, hypervisor and non-SNP guests are subject to RMP checks on writes to provide integrity of SEV-SNP guest memory.

The RMPOPT architecture enables optimizations whereby the RMP checks can be skipped if 1GB regions of memory are known to not contain any SNP guest memory.

RMPOPT is a new instruction designed to minimize the performance overhead of RMP checks for the hypervisor and non-SNP guests.

RMPOPT instruction currently supports two functions. In case of the verify and report status function the CPU will read the RMP contents, verify the entire 1GB region starting at the provided SPA is HV-owned. For the entire 1GB region it checks that all RMP entries in this region are HV-owned (i.e, not in assigned state) and then accordingly updates the RMPOPT table to indicate if optimization has been enabled and provide indication to software if the optimization was successful.

In case of report status function, the CPU returns the optimization status for the 1GB region.

The RMPOPT table is managed by a combination of software and hardware. Software uses the RMPOPT instruction to set bits in the table, indicating that regions of memory are entirely HV-owned. Hardware automatically clears bits in the RMPOPT table when RMP contents are changed during RMPUPDATE instruction.

For more information on the RMPOPT instruction, see the AMD64 RMPOPT technical documentation.

As SNP is enabled by default the hypervisor and non-SNP guests are subject to RMP write checks to provide integrity of SNP guest memory.

This patch-series adds support to enable RMP optimizations for up to 2TB of system RAM across the system and allow RMPUPDATE to disable those optimizations as SNP guests are launched.

Support for RAM larger than 2 TB will be added in follow-on series.

This series also adds support to disable CPU hotplug while SNP is active, as the SEV firmware enumerates CPUs at SNP initialization and is not aware of the OS bringing CPUs online or offline afterwards. This also keeps the set of CPUs stable for the asynchronous RMPOPT scan, so the per-core RMPOPT_BASE MSRs programmed during setup remain valid.

This series also introduces support to re-enable RMP optimizations during SNP guest termination, after guest pages have been converted back to shared.

RMP optimizations are performed asynchronously by queuing work on a dedicated workqueue after a 10 second delay.

Delaying work allows batching of multiple SNP guest terminations.

Once 1GB hugetlb guest_memfd support is merged, support for re-enabling RMPOPT optimizations during 1GB page cleanup will be added in follow-on series.

v10:
- Rework the CPU-hotplug patch (3/6): disable CPU hotplug in snp_prepare(), before SnpEn is set, instead of late in __sev_snp_init_locked(), so no CPU can come online without SnpEn during SNP initialization (per upstream review). Tie hotplug to SnpEn: it stays disabled while SnpEn is set -- including across a failed SNP_INIT and across the legacy SNP_SHUTDOWN_EX path -- and is re-enabled only once the firmware clears SnpEn on the x86_snp_shutdown path. Drop the separate idempotent flag: snp_prepare() re-enables hotplug on its own early failure, and a kexec target that boots with SnpEn already set disables hotplug once in snp_rmptable_init(). Reword the commit log and comments accordingly.
- Emit a pr_warn() in rmpopt_work_handler() (4/6) when the follower cpumask allocation fails, instead of silently skipping the optimization pass.

Sashiko AI upstream review identified several of the above issues.

v9:
- Rename rmpopt_configured to rmpopt_capable.
- Make rmpopt_cpumask a cpumask_var_t (allocated/freed at setup/cleanup) instead of a static cpumask_t.
- Drop the v8 WARN_ON_ONCE() on the RMPOPT_BASE writes; use a plain wrmsrq_on_cpu(), matching the SNP MSR-write convention in this file.
- Disable CPU hotplug with cpu_hotplug_disable()/cpu_hotplug_enable() (per tglx); re-enable only on the full x86_snp_shutdown path.
- Simplify rmpopt_work_handler() to a single leader-then-followers path:
with CPU hotplug disabled while SNP is active and snp_prepare() requiring all CPUs online when RMPOPT_BASE is programmed, every core is always programmed, so the explicit-leader fallback is now unreachable. Drop it along with the v8 work_on_cpu()/rmpopt_leader_fn() helper.
- Drop the debugfs interface (was patch 7/7) and its report-only plumbing; observability will be revisited after this series is merged.
- Restrict snp_rmpopt_all_physmem()'s export to the kvm-amd module.
- Use scoped_guard(cpus_read_lock) for the per-CPU MSR and follower loops.

Sashiko AI upstream review identified several of the above issues.

v8:
- Add a new patch to disable CPU hotplug while SNP is active, keeping the CPU set stable for the RMPOPT work handler.
- Drop the setup_clear_cpu_cap(X86_FEATURE_RMPOPT) calls; the rmpopt_configured bool is the runtime guard.
- WARN_ON_ONCE() on the RMPOPT_BASE MSR writes that previously ignored their return value.
- Simplify rmpopt_work_handler() by removing the explicit-leader
fallback: with CPU hotplug disabled while SNP is active and snp_prepare() requiring all CPUs online when RMPOPT_BASE is programmed, every core is always programmed, so the running CPU can always be the leader. This drops the smp_call_function_single() fallback (and with it the AB-BA deadlock and IRQ-latency concerns) and collapses the leader selection into a single leader-then-followers path.
- Use mod_delayed_work() in snp_rmpopt_all_physmem() so the batching delay tracks the last SNP guest termination.

Sashiko AI code review identified several of the above issues.

v7:
- Sync tools/arch/x86/include/asm/cpufeatures.h to mirror the kernel header for X86_FEATURE_RMPOPT.
- Fix commit title to use X86_FEATURE_RMPOPT to match the code (was X86_FEATURE_AMD_RMPOPT).
- Add static bool rmpopt_configured, set only when segmented RMP setup succeeds in setup_rmptable(). Check rmpopt_configured alongside cpu_feature_enabled(X86_FEATURE_RMPOPT) in snp_setup_rmpopt() and snp_rmpopt_all_physmem(), because setup_clear_cpu_cap() is unreliable after alternatives are patched. Add snp_clear_rmpopt_configured() called from amd_cc_platform_clear() when CC_ATTR_HOST_SEV_SNP is cleared. Do not use __ro_after_init on rmpopt_configured since the writer snp_clear_rmpopt_configured() is not __init.
- Add cond_resched() to all three leader loops in rmpopt_work_handler() to prevent soft lockups on systems with up to 2TB of RAM.
- Add comment above __rmpopt() documenting the RMPOPT instruction encoding (F2 0F 01 FC) and register interface (RAX = system physical address input, RCX = operation type input, RFLAGS.CF = output).
Note: RMPOPT does not modify RAX unlike PVALIDATE/RMPUPDATE, so the existing "a" (input-only) constraint is correct.

Sashiko AI code review identified several of the above issues.

v6:
- Drop wrmsrq_on_cpus() helper; use for_each_cpu() with wrmsrq_on_cpu() instead, as RMPOPT_BASE MSR programming is not performance-critical.
- Rewrite rmpopt_work_handler() leader selection to use a local follower_mask copy instead of modifying the global rmpopt_cpumask. This eliminates the current_cpu_cleared tracking and the restore at the end, and removes the need for synchronization comments about transient cpumask inconsistency.
- Add three-way leader selection in rmpopt_work_handler():
1. Current CPU is a primary thread in cpumask: run leader locally.
2. Current CPU is a sibling thread whose primary is in cpumask:
run leader locally (RMPOPT_BASE MSR is per-core), remove the primary from followers via cpumask_andnot(topology_sibling_cpumask).
3. Current CPU's core has no RMPOPT_BASE MSR programmed: pick an explicit leader via cpumask_first() + smp_call_function_single() to avoid #UD, with cpus_read_lock() around the IPI loop.
- Add WARN_ON_ONCE guard for empty cpumask in the explicit leader fallback path, with migrate_enable() before goto out.
- Add .llseek = seq_lseek to rmpopt_table_fops for consistency with other seq_file-based debugfs files and to support tools like "less".
- Change debugfs file permissions from 0444 to 0400 to restrict access to root only.
- Add comment in rmpopt_table_seq_show() explaining why cpu_online_mask is safe: RMPOPT_BASE MSR is per-core and snp_prepare() ensures all CPUs are online when the MSR is programmed.

Sashiko AI code review identified several of the above issues.

v5:
- Introduce rmpopt_cleanup() to tear down workqueue, debugfs, cpumask, and MSR state, called from snp_shutdown().
- Introduce rmpopt_wq_mutex to serialize snp_setup_rmpopt(), snp_rmpopt_all_physmem(), and rmpopt_cleanup().
- Introduce rmpopt_show_mutex to serialize debugfs reporting of rmpopt_report_cpumask.
- Move snp_rmpopt_all_physmem() call after SNP DECOMMISSION during guest shutdown.
- Use migrate_disable()/migrate_enable() for CPU pinning in the rmpopt_work_handler() leader loop to maintain CPU affinity without disabling preemption for the entire RMPOPT scan.
- Add cpus_read_lock()/cpus_read_unlock() around the follower on_each_cpu_mask() loop in rmpopt_work_handler().
- Guard snp_setup_rmpopt() against re-initialization when SNP_SHUTDOWN_EX with x86_snp_shutdown=0 skips rmpopt_cleanup() but clears snp_initialized, preventing workqueue and resource leaks on repeated init/shutdown cycles.
- Replace setup_clear_cpu_cap() with pr_err() on alloc_workqueue() failure in snp_setup_rmpopt(), as setup_clear_cpu_cap() cannot be used after alternatives are patched; callers check rmpopt_wq != NULL as the runtime guard instead.
- Add pr_info() when RMPOPT coverage is capped at 2TB.
- Add comments noting CPU hotplug is not supported with SNP enabled and only online primary threads are covered by rmpopt_cpumask.
- Add comment in setup_rmptable() noting Segmented RMP must be enabled to enable RMPOPT.
- Simplify cpumask setup loop to set if primary thread rather than skip if not primary.
- Improve grammar and clarity in snp_setup_rmpopt() comments.
- Added Reviewed-by's.

Sashiko AI code review identified several of the above issues.

v4:
- Add new wrmsrq_on_cpus() helper to write same u64 value to a per-CPU MSR across a cpumask without per-cpu struct allocation overhead.
- Rename configure_and_enable_rmpopt() to snp_setup_rmpopt().
- Use wrmsrq_on_cpus() instead of wrmsrq_on_cpu() loop for programming RMPOPT_BASE MSRs.
- Add setup_clear_cpu_cap(X86_FEATURE_RMPOPT) if segmented RMP setup fails or workqueue allocation fails.
- Add X86_FEATURE_RMPOPT feature clear logic in amd_cc_platform_clear() for CC_ATTR_HOST_SEV_SNP.
- All of the above allow checking for only X86_FEATURE_RMPOPT for both RMPOPT setup/enable and RMP re-optimizations.
- Rename snp_perform_rmp_optimization() to snp_rmpopt_all_physmem().
- Split rmpopt() into rmpopt() and rmpopt_smp() for SMP callback use.
- Introduce separate rmpopt_report_cpumask for debugfs reporting, distinct from rmpopt_cpumask used for primary thread tracking.
- Remove snp_perform_rmp_optimization() call from __sev_snp_init_locked() and instead setup and enable RMPOPT after SNP is enabled and initialized.

v3:
- Drop all RMPOPT kthread support and introduce adding custom and dedicated workqueue to schedule delayed and asynchronous RMPOPT work.
- Drop the guest_memfd inode cleanup interface and add support to re-enable RMP optimizations during guest shutdown using the asynchronous and delayed workqueue interface.
- Introduce new __rmpopt() helper and rmpopt() and rmpopt_report_status() wrappers on top which use rax and rcx parameters to closely match RMPOPT specs.
- Use new optimized RMPOPT loop to issue RMPOPT instructions on all system RAM upto 2TB and all CPUs, by optimizing each range on one CPU first, then let other CPUs execute RMPOPT in parallel so they can skip most work as the range has already been optimized.
- Also add support for running the optimized RMPOPT loop only on one thread per core.
- Replace all PUD_SIZE references with SZ_1G to conform to 1GB regions as specified by RMPOPT specifications and not be dependent on PUD_SIZE which makes the RMPOPT patch-set independent of x86 page table sizes.
- Use wrmsrq_on_cpu() to program the RMPOPT_BASE MSR registers on all CPUs that removes all ugly casting to use on_each_cpu_mask().
- Fix inline commits and patch commit messages

v2:
- Drop all NUMA and Socket configuration and enablement support and enable RMPOPT support for up to 2TB of system RAM.
- Drop get_cpumask_of_primary_threads() and enable per-core RMPOPT base MSRs and issue RMPOPT instruction on all CPUs.
- Drop the configfs interface to manually re-enable RMP optimizations.
- Add new guest_memfd cleanup interface to automatically re-enable RMP optimizations during guest shutdown.
- Include references to the public RMPOPT documentation.
- Move debugfs directory for RMPOPT under architecuture specific parent directory.

Ashish Kalra (6):
x86/cpufeatures: Add X86_FEATURE_RMPOPT feature flag x86/sev: Initialize RMPOPT configuration MSRs x86/sev: Disable CPU hotplug while SNP is active x86/sev: Add support to perform RMP optimizations asynchronously x86/sev: Add interface to re-enable RMP optimizations.
KVM: SEV: Perform RMP optimizations on SNP guest shutdown

 arch/x86/coco/core.c                     |   2 +
 arch/x86/include/asm/cpufeatures.h       |   2 +-
 arch/x86/include/asm/msr-index.h         |   3 +
 arch/x86/include/asm/sev.h               |   6 +
 arch/x86/kernel/cpu/scattered.c          |   1 +
 arch/x86/kvm/svm/sev.c                   |  10 +
 arch/x86/virt/svm/sev.c                  | 277 +++++++++++++++++++++++
 drivers/crypto/ccp/sev-dev.c             |   3 +
 tools/arch/x86/include/asm/cpufeatures.h |   2 +-
 9 files changed, 304 insertions(+), 2 deletions(-)
author maintainer
#48 lore raw

Hello Boris,

Quoted 10 lines · Borislav Petkov
Quoted 2 lines · Ashish

Just a gentle ping on this series. I believe v10 addresses the feedback from the earlier rounds. Please let me know if it looks good to merge, or if there's anything else you'd like me to change.

What about those Sashiko comments to it:

https://sashiko.dev/#/patchset/cover.1782841284.git.ashish.kalra%40amd.com

?

The three Sashiko comments on v10 all resolved to no code change, with rationale posted inline:

1. Suspend/resume vs. hotplug disable (patch 3): pre-existing base-SNP behavior — an SNP host doesn't support suspend/resume anyway (SnpEn is set once before SNP_INIT and can't be rebuilt on resume), so it can't arise.
2. snp_setup_rmpopt() re-init pass (patch 4): performance-only and self-correcting, doesn't occur on RMPOPT-capable platforms (the x86_snp_shutdown path frees the workqueue, so re-init re-runs the pass) — the guarded case is legacy-firmware-only.
3. mod_delayed_work() and VM churn (patch 6): deliberate — queue_delayed_work() would regress the common case (scanning a guest before its page conversion completes), the deferral is best-effort and self-limiting.

Thanks,
Ashish