* [PATCH v11 1/6] x86/cpufeatures: Add X86_FEATURE_RMPOPT feature flag
2026-07-27 19:00 [PATCH v11 0/6] Add RMPOPT support Ashish Kalra
@ 2026-07-27 19:03 ` Ashish Kalra
2026-07-27 19:04 ` [PATCH v11 2/6] x86/sev: Disable CPU hotplug while SNP is active Ashish Kalra
` (4 subsequent siblings)
5 siblings, 0 replies; 14+ messages in thread
From: Ashish Kalra @ 2026-07-27 19:01 UTC (permalink / raw)
To: tglx, mingo, bp, dave.hansen, x86, hpa, seanjc, peterz,
thomas.lendacky, herbert, davem, ardb
Cc: pbonzini, aik, Michael.Roth, KPrateek.Nayak, Tycho.Andersen,
Nathan.Fontenot, ackerleytng, jackyli, pgonda, rientjes, jacobhxu,
xin, pawan.kumar.gupta, babu.moger, dyoung, nikunj, john.allen,
darwi, linux-kernel, linux-crypto, kvm, linux-coco
From: Ashish Kalra <ashish.kalra@amd.com>
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. ]
Suggested-by: Borislav Petkov (AMD) <bp@alien8.de>
Signed-off-by: Ashish Kalra <ashish.kalra@amd.com>
Signed-off-by: Borislav Petkov (AMD) <bp@alien8.de>
Reviewed-by: Dave Hansen <dave.hansen@linux.intel.com>
Reviewed-by: Ackerley Tng <ackerleytng@google.com>
Link: https://patch.msgid.link/39e9ee269a572c516a3f4e937bfe12d00697d5e6.1782841284.git.ashish.kalra@amd.com
---
arch/x86/include/asm/cpufeatures.h | 2 +-
arch/x86/kernel/cpu/scattered.c | 1 +
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/arch/x86/include/asm/cpufeatures.h b/arch/x86/include/asm/cpufeatures.h
index 3d0940a3b9f3..dbccde9ee5cd 100644
--- a/arch/x86/include/asm/cpufeatures.h
+++ b/arch/x86/include/asm/cpufeatures.h
@@ -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) */
diff --git a/arch/x86/kernel/cpu/scattered.c b/arch/x86/kernel/cpu/scattered.c
index 937129ce6a96..021c0bf22de2 100644
--- a/arch/x86/kernel/cpu/scattered.c
+++ b/arch/x86/kernel/cpu/scattered.c
@@ -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 }
};
--
2.43.0
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH v11 1/6] x86/cpufeatures: Add X86_FEATURE_RMPOPT feature flag
@ 2026-07-27 19:03 ` Ashish Kalra
0 siblings, 0 replies; 14+ messages in thread
From: Ashish Kalra @ 2026-07-27 19:01 UTC (permalink / raw)
To: tglx, mingo, bp, dave.hansen, x86, hpa, seanjc, peterz,
thomas.lendacky, herbert, davem, ardb
Cc: pbonzini, aik, Michael.Roth, KPrateek.Nayak, Tycho.Andersen,
Nathan.Fontenot, ackerleytng, jackyli, pgonda, rientjes, jacobhxu,
xin, pawan.kumar.gupta, babu.moger, dyoung, nikunj, john.allen,
darwi, linux-kernel, linux-crypto, kvm, linux-coco
From: Ashish Kalra <ashish.kalra@amd.com>
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. ]
Suggested-by: Borislav Petkov (AMD) <bp@alien8.de>
Signed-off-by: Ashish Kalra <ashish.kalra@amd.com>
Signed-off-by: Borislav Petkov (AMD) <bp@alien8.de>
Reviewed-by: Dave Hansen <dave.hansen@linux.intel.com>
Reviewed-by: Ackerley Tng <ackerleytng@google.com>
Link: https://patch.msgid.link/39e9ee269a572c516a3f4e937bfe12d00697d5e6.1782841284.git.ashish.kalra@amd.com
---
arch/x86/include/asm/cpufeatures.h | 2 +-
arch/x86/kernel/cpu/scattered.c | 1 +
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/arch/x86/include/asm/cpufeatures.h b/arch/x86/include/asm/cpufeatures.h
index 3d0940a3b9f3..dbccde9ee5cd 100644
--- a/arch/x86/include/asm/cpufeatures.h
+++ b/arch/x86/include/asm/cpufeatures.h
@@ -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) */
diff --git a/arch/x86/kernel/cpu/scattered.c b/arch/x86/kernel/cpu/scattered.c
index 937129ce6a96..021c0bf22de2 100644
--- a/arch/x86/kernel/cpu/scattered.c
+++ b/arch/x86/kernel/cpu/scattered.c
@@ -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 }
};
--
2.43.0
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH v11 1/6] x86/cpufeatures: Add X86_FEATURE_RMPOPT feature flag
@ 2026-07-27 19:03 ` Ashish Kalra
0 siblings, 0 replies; 14+ messages in thread
From: Ashish Kalra @ 2026-07-27 19:03 UTC (permalink / raw)
To: tglx, mingo, bp, dave.hansen, x86, hpa, seanjc, peterz,
thomas.lendacky, herbert, davem, ardb
Cc: pbonzini, aik, Michael.Roth, KPrateek.Nayak, Tycho.Andersen,
Nathan.Fontenot, ackerleytng, jackyli, pgonda, rientjes, jacobhxu,
xin, pawan.kumar.gupta, babu.moger, dyoung, nikunj, john.allen,
darwi, linux-kernel, linux-crypto, kvm, linux-coco
From: Ashish Kalra <ashish.kalra@amd.com>
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. ]
Suggested-by: Borislav Petkov (AMD) <bp@alien8.de>
Signed-off-by: Ashish Kalra <ashish.kalra@amd.com>
Signed-off-by: Borislav Petkov (AMD) <bp@alien8.de>
Reviewed-by: Dave Hansen <dave.hansen@linux.intel.com>
Reviewed-by: Ackerley Tng <ackerleytng@google.com>
Link: https://patch.msgid.link/39e9ee269a572c516a3f4e937bfe12d00697d5e6.1782841284.git.ashish.kalra@amd.com
---
arch/x86/include/asm/cpufeatures.h | 2 +-
arch/x86/kernel/cpu/scattered.c | 1 +
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/arch/x86/include/asm/cpufeatures.h b/arch/x86/include/asm/cpufeatures.h
index 3d0940a3b9f3..dbccde9ee5cd 100644
--- a/arch/x86/include/asm/cpufeatures.h
+++ b/arch/x86/include/asm/cpufeatures.h
@@ -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) */
diff --git a/arch/x86/kernel/cpu/scattered.c b/arch/x86/kernel/cpu/scattered.c
index 937129ce6a96..021c0bf22de2 100644
--- a/arch/x86/kernel/cpu/scattered.c
+++ b/arch/x86/kernel/cpu/scattered.c
@@ -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 }
};
--
2.43.0
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH v11 2/6] x86/sev: Disable CPU hotplug while SNP is active
2026-07-27 19:00 [PATCH v11 0/6] Add RMPOPT support Ashish Kalra
2026-07-27 19:03 ` Ashish Kalra
@ 2026-07-27 19:04 ` Ashish Kalra
2026-07-27 19:37 ` sashiko-bot
2026-07-27 19:04 ` [PATCH v11 3/6] x86/sev: Initialize RMPOPT configuration MSRs Ashish Kalra
` (3 subsequent siblings)
5 siblings, 1 reply; 14+ messages in thread
From: Ashish Kalra @ 2026-07-27 19:04 UTC (permalink / raw)
To: tglx, mingo, bp, dave.hansen, x86, hpa, seanjc, peterz,
thomas.lendacky, herbert, davem, ardb
Cc: pbonzini, aik, Michael.Roth, KPrateek.Nayak, Tycho.Andersen,
Nathan.Fontenot, ackerleytng, jackyli, pgonda, rientjes, jacobhxu,
xin, pawan.kumar.gupta, babu.moger, dyoung, nikunj, john.allen,
darwi, linux-kernel, linux-crypto, kvm, linux-coco
From: Ashish Kalra <ashish.kalra@amd.com>
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.
With CPU hotplug now disabled while SNP is active, the online CPU mask is
stable, so the cpus_read_lock() previously taken in snp_prepare() to
iterate it is redundant. Drop cpus_read_lock()/cpus_read_unlock() here.
The RMPOPT setup and cleanup added later are introduced after this patch
and never take the lock for the same reason.
Suggested-by: Thomas Lendacky <thomas.lendacky@amd.com>
Suggested-by: Borislav Petkov (AMD) <bp@alien8.de>
Signed-off-by: Ashish Kalra <ashish.kalra@amd.com>
---
arch/x86/virt/svm/sev.c | 39 +++++++++++++++++++++++++++++----------
1 file changed, 29 insertions(+), 10 deletions(-)
diff --git a/arch/x86/virt/svm/sev.c b/arch/x86/virt/svm/sev.c
index cff285d8ad8e..e2f69fba0938 100644
--- a/arch/x86/virt/svm/sev.c
+++ b/arch/x86/virt/svm/sev.c
@@ -513,7 +513,6 @@ static void clear_hsave_pa(void *arg)
int snp_prepare(void)
{
- int ret;
u64 val;
/*
@@ -526,14 +525,21 @@ int snp_prepare(void)
clear_rmp();
- cpus_read_lock();
+ /*
+ * Disable CPU hotplug before enabling SNP: no CPU may come online
+ * without SnpEn while SNP is active, and none may go offline during
+ * enable. This keeps cpu_online_mask stable for the check and the
+ * on_each_cpu() calls below, so cpus_read_lock() is not needed. It is
+ * re-enabled in snp_shutdown() once the firmware disables SNP.
+ */
+ cpu_hotplug_disable();
if (!cpumask_equal(cpu_online_mask, cpu_present_mask)) {
- ret = -EOPNOTSUPP;
+ cpu_hotplug_enable();
pr_warn("SNP init failed: not all CPUs online. (%*pbl online <-> %*pbl present masks).\n",
cpumask_pr_args(cpu_online_mask),
cpumask_pr_args(cpu_present_mask));
- goto unlock;
+ return -EOPNOTSUPP;
}
wbinvd_on_all_cpus();
@@ -548,12 +554,7 @@ int snp_prepare(void)
/* SNP_INIT requires MSR_VM_HSAVE_PA to be cleared on all CPUs. */
on_each_cpu(clear_hsave_pa, NULL, 1);
- ret = 0;
-
-unlock:
- cpus_read_unlock();
-
- return ret;
+ return 0;
}
EXPORT_SYMBOL_FOR_MODULES(snp_prepare, "ccp");
@@ -565,6 +566,13 @@ void snp_shutdown(void)
if (syscfg & MSR_AMD64_SYSCFG_SNP_EN)
return;
+ /*
+ * The firmware has disabled SNP (SnpEn is clear), so re-enable CPU
+ * hotplug. A legacy SNP shutdown returns above with SnpEn still set and
+ * leaves hotplug disabled.
+ */
+ cpu_hotplug_enable();
+
clear_rmp();
on_each_cpu(mfd_reconfigure, NULL, 1);
}
@@ -577,6 +585,8 @@ EXPORT_SYMBOL_FOR_MODULES(snp_shutdown, "ccp");
*/
int __init snp_rmptable_init(void)
{
+ u64 val;
+
if (WARN_ON_ONCE(!cc_platform_has(CC_ATTR_HOST_SEV_SNP)))
return -ENOSYS;
@@ -586,6 +596,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.
--
2.43.0
^ permalink raw reply related [flat|nested] 14+ messages in thread* Re: [PATCH v11 2/6] x86/sev: Disable CPU hotplug while SNP is active
2026-07-27 19:04 ` [PATCH v11 2/6] x86/sev: Disable CPU hotplug while SNP is active Ashish Kalra
@ 2026-07-27 19:37 ` sashiko-bot
2026-07-27 20:44 ` Kalra, Ashish
0 siblings, 1 reply; 14+ messages in thread
From: sashiko-bot @ 2026-07-27 19:37 UTC (permalink / raw)
To: Ashish Kalra; +Cc: kvm
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] System suspend/resume bypasses `cpu_hotplug_disable()`, causing secondary CPUs to come online without the `SnpEn` bit set.
--
--- Patch [2]: [PATCH v11 2/6] x86/sev: Disable CPU hotplug while SNP is active ---
commit 2ca360619d15fed203ba00f43d20453130503df3
Author: Ashish Kalra <ashish.kalra@amd.com>
x86/sev: Disable CPU hotplug while SNP is active
This commit replaces cpus_read_lock() with cpu_hotplug_disable() in
snp_prepare() to prevent CPU hotplug while SEV-SNP is being enabled. It
additionally keeps hotplug disabled as long as SNP is active to ensure OS CPU
hotplug does not diverge from firmware expectations.
> diff --git a/arch/x86/virt/svm/sev.c b/arch/x86/virt/svm/sev.c
> index cff285d8ad8e3..e2f69fba09389 100644
> --- a/arch/x86/virt/svm/sev.c
> +++ b/arch/x86/virt/svm/sev.c
[ ... ]
> @@ -526,14 +525,21 @@ int snp_prepare(void)
>
> clear_rmp();
>
> - cpus_read_lock();
> + /*
> + * Disable CPU hotplug before enabling SNP: no CPU may come online
> + * without SnpEn while SNP is active, and none may go offline during
> + * enable. This keeps cpu_online_mask stable for the check and the
> + * on_each_cpu() calls below, so cpus_read_lock() is not needed. It is
> + * re-enabled in snp_shutdown() once the firmware disables SNP.
> + */
> + cpu_hotplug_disable();
[Severity: High]
This is a pre-existing issue, but does cpu_hotplug_disable() reliably
prevent secondary CPUs from coming online without the SnpEn bit set?
System suspend and resume transitions bypass the sysfs hotplug locks. During a
suspend and resume cycle, freeze_secondary_cpus() and thaw_secondary_cpus()
directly invoke internal routines that offline and online CPUs, respectively.
Because snp_enable() is not registered in a CPU hotplug callback, if a
secondary CPU is thawed, it appears it would wake up and come online without
the MSR_AMD64_SYSCFG_SNP_EN bit set. Would this leave the system vulnerable
by allowing arbitrary host access to SEV-SNP guest memory, since no RMP
checks would be performed by those CPUs?
--
Sashiko AI review · https://sashiko.dev/#/patchset/cover.1784844080.git.ashish.kalra@amd.com?part=2
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v11 2/6] x86/sev: Disable CPU hotplug while SNP is active
2026-07-27 19:37 ` sashiko-bot
@ 2026-07-27 20:44 ` Kalra, Ashish
0 siblings, 0 replies; 14+ messages in thread
From: Kalra, Ashish @ 2026-07-27 20:44 UTC (permalink / raw)
To: sashiko-reviews; +Cc: kvm
On 7/27/2026 2:37 PM, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
>
> Pre-existing issues:
> - [High] System suspend/resume bypasses `cpu_hotplug_disable()`, causing secondary CPUs to come online without the `SnpEn` bit set.
> --
>
> --- Patch [2]: [PATCH v11 2/6] x86/sev: Disable CPU hotplug while SNP is active ---
> commit 2ca360619d15fed203ba00f43d20453130503df3
> Author: Ashish Kalra <ashish.kalra@amd.com>
>
> x86/sev: Disable CPU hotplug while SNP is active
>
> This commit replaces cpus_read_lock() with cpu_hotplug_disable() in
> snp_prepare() to prevent CPU hotplug while SEV-SNP is being enabled. It
> additionally keeps hotplug disabled as long as SNP is active to ensure OS CPU
> hotplug does not diverge from firmware expectations.
>
>> diff --git a/arch/x86/virt/svm/sev.c b/arch/x86/virt/svm/sev.c
>> index cff285d8ad8e3..e2f69fba09389 100644
>> --- a/arch/x86/virt/svm/sev.c
>> +++ b/arch/x86/virt/svm/sev.c
>
> [ ... ]
>
>> @@ -526,14 +525,21 @@ int snp_prepare(void)
>>
>> clear_rmp();
>>
>> - cpus_read_lock();
>> + /*
>> + * Disable CPU hotplug before enabling SNP: no CPU may come online
>> + * without SnpEn while SNP is active, and none may go offline during
>> + * enable. This keeps cpu_online_mask stable for the check and the
>> + * on_each_cpu() calls below, so cpus_read_lock() is not needed. It is
>> + * re-enabled in snp_shutdown() once the firmware disables SNP.
>> + */
>> + cpu_hotplug_disable();
>
> [Severity: High]
> This is a pre-existing issue, but does cpu_hotplug_disable() reliably
> prevent secondary CPUs from coming online without the SnpEn bit set?
>
> System suspend and resume transitions bypass the sysfs hotplug locks. During a
> suspend and resume cycle, freeze_secondary_cpus() and thaw_secondary_cpus()
> directly invoke internal routines that offline and online CPUs, respectively.
>
> Because snp_enable() is not registered in a CPU hotplug callback, if a
> secondary CPU is thawed, it appears it would wake up and come online without
> the MSR_AMD64_SYSCFG_SNP_EN bit set. Would this leave the system vulnerable
> by allowing arbitrary host access to SEV-SNP guest memory, since no RMP
> checks would be performed by those CPUs?
>
Pre-existing base SNP-host behaviour, not introduced here (as tagged). An SNP host doesn't support suspend/resume:
SnpEn is set on all CPUs once at boot, immediately followed by SNP_INIT, and that state can't be rebuilt on resume without a full re-init.
So a CPU coming back online without SnpEn can't occur in a working config.
Thanks,
Ashish
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v11 3/6] x86/sev: Initialize RMPOPT configuration MSRs
2026-07-27 19:00 [PATCH v11 0/6] Add RMPOPT support Ashish Kalra
2026-07-27 19:03 ` Ashish Kalra
2026-07-27 19:04 ` [PATCH v11 2/6] x86/sev: Disable CPU hotplug while SNP is active Ashish Kalra
@ 2026-07-27 19:04 ` Ashish Kalra
2026-07-27 19:22 ` sashiko-bot
2026-07-27 19:05 ` [PATCH v11 4/6] x86/sev: Add support to perform RMP optimizations asynchronously Ashish Kalra
` (2 subsequent siblings)
5 siblings, 1 reply; 14+ messages in thread
From: Ashish Kalra @ 2026-07-27 19:04 UTC (permalink / raw)
To: tglx, mingo, bp, dave.hansen, x86, hpa, seanjc, peterz,
thomas.lendacky, herbert, davem, ardb
Cc: pbonzini, aik, Michael.Roth, KPrateek.Nayak, Tycho.Andersen,
Nathan.Fontenot, ackerleytng, jackyli, pgonda, rientjes, jacobhxu,
xin, pawan.kumar.gupta, babu.moger, dyoung, nikunj, john.allen,
darwi, linux-kernel, linux-crypto, kvm, linux-coco
From: Ashish Kalra <ashish.kalra@amd.com>
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.
Suggested-by: Thomas Lendacky <thomas.lendacky@amd.com>
Suggested-by: Dave Hansen <dave.hansen@linux.intel.com>
Suggested-by: Borislav Petkov (AMD) <bp@alien8.de>
Suggested-by: K Prateek Nayak <kprateek.nayak@amd.com>
Reviewed-by: Dave Hansen <dave.hansen@linux.intel.com>
Signed-off-by: Ashish Kalra <ashish.kalra@amd.com>
---
arch/x86/include/asm/msr-index.h | 3 ++
arch/x86/include/asm/sev.h | 2 +
arch/x86/virt/svm/sev.c | 67 +++++++++++++++++++++++++++++---
drivers/crypto/ccp/sev-dev.c | 3 ++
4 files changed, 70 insertions(+), 5 deletions(-)
diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h
index 18c4be75e927..d2cb0a7cd0a2 100644
--- a/arch/x86/include/asm/msr-index.h
+++ b/arch/x86/include/asm/msr-index.h
@@ -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
diff --git a/arch/x86/include/asm/sev.h b/arch/x86/include/asm/sev.h
index 594cfa19cbd4..6fd72a44a51e 100644
--- a/arch/x86/include/asm/sev.h
+++ b/arch/x86/include/asm/sev.h
@@ -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_setup_rmpopt(void);
void snp_shutdown(void);
#else
static inline bool snp_probe_rmptable_info(void) { return false; }
@@ -680,6 +681,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_setup_rmpopt(void) {}
static inline void snp_shutdown(void) {}
#endif
diff --git a/arch/x86/virt/svm/sev.c b/arch/x86/virt/svm/sev.c
index e2f69fba0938..8bfd80284836 100644
--- a/arch/x86/virt/svm/sev.c
+++ b/arch/x86/virt/svm/sev.c
@@ -124,6 +124,9 @@ 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 LIST_HEAD(snp_leaked_pages_list);
static DEFINE_SPINLOCK(snp_leaked_pages_list_lock);
@@ -558,6 +561,17 @@ int snp_prepare(void)
}
EXPORT_SYMBOL_FOR_MODULES(snp_prepare, "ccp");
+static void snp_cleanup_rmpopt(void)
+{
+ int cpu;
+
+ for_each_cpu(cpu, rmpopt_cpumask)
+ wrmsrq_on_cpu(cpu, MSR_AMD64_RMPOPT_BASE, 0);
+
+ free_cpumask_var(rmpopt_cpumask);
+ rmpopt_pa_start = 0;
+}
+
void snp_shutdown(void)
{
u64 syscfg;
@@ -567,10 +581,11 @@ void snp_shutdown(void)
return;
/*
- * The firmware has disabled SNP (SnpEn is clear), so re-enable CPU
- * hotplug. A legacy SNP shutdown returns above with SnpEn still set and
- * leaves hotplug disabled.
+ * Clear the RMPOPT_BASE MSRs while CPU hotplug is still disabled, then
+ * re-enable hotplug now that the firmware has disabled SNP. A legacy SNP
+ * shutdown returns above with SnpEn still set and leaves hotplug disabled.
*/
+ snp_cleanup_rmpopt();
cpu_hotplug_enable();
clear_rmp();
@@ -578,6 +593,46 @@ void snp_shutdown(void)
}
EXPORT_SYMBOL_FOR_MODULES(snp_shutdown, "ccp");
+static bool rmpopt_capable(void)
+{
+ return cpu_feature_enabled(X86_FEATURE_RMPOPT) &&
+ cc_platform_has(CC_ATTR_HOST_SEV_SNP);
+}
+
+void snp_setup_rmpopt(void)
+{
+ u64 rmpopt_base;
+ int cpu;
+
+ if (!rmpopt_capable())
+ return;
+
+ if (!zalloc_cpumask_var(&rmpopt_cpumask, GFP_KERNEL)) {
+ 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. All primary threads are online,
+ * otherwise SNP would not have been enabled.
+ */
+ for_each_online_cpu(cpu)
+ if (topology_is_primary_thread(cpu))
+ cpumask_set_cpu(cpu, rmpopt_cpumask);
+
+ 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 cover at most 2 TB. Program each core's
+ * RMPOPT_BASE with the start of RAM to optimize up to 2 TB.
+ */
+ for_each_cpu(cpu, rmpopt_cpumask)
+ wrmsrq_on_cpu(cpu, MSR_AMD64_RMPOPT_BASE, rmpopt_base);
+}
+EXPORT_SYMBOL_FOR_MODULES(snp_setup_rmpopt, "ccp");
+
/*
* Do the necessary preparations which are verified by the firmware as
* described in the SNP_INIT_EX firmware command description in the SNP
@@ -705,10 +760,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();
+ }
}
/*
diff --git a/drivers/crypto/ccp/sev-dev.c b/drivers/crypto/ccp/sev-dev.c
index ca473ca198b8..c002a7ca26a8 100644
--- a/drivers/crypto/ccp/sev-dev.c
+++ b/drivers/crypto/ccp/sev-dev.c
@@ -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");
--
2.43.0
^ permalink raw reply related [flat|nested] 14+ messages in thread* Re: [PATCH v11 3/6] x86/sev: Initialize RMPOPT configuration MSRs
2026-07-27 19:04 ` [PATCH v11 3/6] x86/sev: Initialize RMPOPT configuration MSRs Ashish Kalra
@ 2026-07-27 19:22 ` sashiko-bot
2026-07-27 21:02 ` Kalra, Ashish
0 siblings, 1 reply; 14+ messages in thread
From: sashiko-bot @ 2026-07-27 19:22 UTC (permalink / raw)
To: Ashish Kalra; +Cc: kvm
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] `snp_cleanup_rmpopt()` unconditionally dereferences `rmpopt_cpumask`, causing a deterministic kernel panic on shutdown if the mask was never allocated (e.g., on CPUs without RMPOPT support or if allocation fails).
- [High] If SEV-SNP firmware initialization fails, the error path returns directly without cleaning up the state set by `snp_prepare()`, leaving CPU hotplug permanently disabled.
- [Medium] Re-initializing SEV-SNP after a legacy shutdown leaks the `rmpopt_cpumask` allocation because the old pointer is overwritten without being freed.
--
--- Patch [3]: [PATCH v11 3/6] x86/sev: Initialize RMPOPT configuration MSRs ---
commit e4623d49ab6201adc5b609f5a6043ed6dfb7f55c
Author: Ashish Kalra <ashish.kalra@amd.com>
Subject: x86/sev: Initialize RMPOPT configuration MSRs
The new RMPOPT instruction helps manage per-CPU RMP optimization
structures inside the CPU. This commit initializes the per-CPU RMPOPT
table base to enable RMP optimization for up to 2 TB of system RAM.
> diff --git a/arch/x86/virt/svm/sev.c b/arch/x86/virt/svm/sev.c
> index e2f69fba09389..8bfd802848361 100644
> --- a/arch/x86/virt/svm/sev.c
> +++ b/arch/x86/virt/svm/sev.c
[ ... ]
> @@ -558,6 +561,17 @@ int snp_prepare(void)
> }
> EXPORT_SYMBOL_FOR_MODULES(snp_prepare, "ccp");
>
> +static void snp_cleanup_rmpopt(void)
> +{
> + int cpu;
> +
> + for_each_cpu(cpu, rmpopt_cpumask)
[Severity: High]
If rmpopt_cpumask was never allocated, does this macro dereference a NULL
pointer?
In snp_setup_rmpopt(), if rmpopt_capable() is false, the mask remains
unallocated. When snp_shutdown() subsequently calls snp_cleanup_rmpopt(),
it iterates over the mask without checking if it is available.
Can this cause a kernel panic on system shutdown or module unload for
systems without RMPOPT support?
> + wrmsrq_on_cpu(cpu, MSR_AMD64_RMPOPT_BASE, 0);
> +
> + free_cpumask_var(rmpopt_cpumask);
> + rmpopt_pa_start = 0;
> +}
[ ... ]
> @@ -578,6 +593,46 @@ void snp_shutdown(void)
> }
> EXPORT_SYMBOL_FOR_MODULES(snp_shutdown, "ccp");
>
> +static bool rmpopt_capable(void)
> +{
> + return cpu_feature_enabled(X86_FEATURE_RMPOPT) &&
> + cc_platform_has(CC_ATTR_HOST_SEV_SNP);
> +}
> +
> +void snp_setup_rmpopt(void)
> +{
> + u64 rmpopt_base;
> + int cpu;
> +
> + if (!rmpopt_capable())
> + return;
> +
> + if (!zalloc_cpumask_var(&rmpopt_cpumask, GFP_KERNEL)) {
[Severity: Medium]
Does this unconditional allocation leak the previously allocated cpumask
when SEV-SNP is re-initialized?
During a legacy shutdown, snp_shutdown() returns early when the SnpEn
firmware bit is still set, skipping snp_cleanup_rmpopt():
arch/x86/virt/svm/sev.c:snp_shutdown() {
...
rdmsrq(MSR_AMD64_SYSCFG, syscfg);
if (syscfg & MSR_AMD64_SYSCFG_SNP_EN)
return;
...
}
A subsequent call to __sev_snp_init_locked() will invoke snp_setup_rmpopt()
again and overwrite the old pointer without freeing it.
> + pr_err("Failed to allocate RMPOPT cpumask\n");
> + return;
> + }
[ ... ]
> diff --git a/drivers/crypto/ccp/sev-dev.c b/drivers/crypto/ccp/sev-dev.c
> index f833cb7e4da36..0b40d23a26b45 100644
> --- a/drivers/crypto/ccp/sev-dev.c
> +++ b/drivers/crypto/ccp/sev-dev.c
> @@ -1630,6 +1630,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();
> +
[Severity: High]
If the SEV-SNP firmware initialization fails, does this leave CPU hotplug
permanently disabled?
A global cpu_hotplug_disable() call is made inside snp_prepare() right before
we reach this code. If the subsequent firmware command fails in
__sev_snp_init_locked(), it returns the error directly without cleaning up:
drivers/crypto/ccp/sev-dev.c:__sev_snp_init_locked() {
...
rc = __sev_do_cmd_locked(cmd, arg, error);
if (rc) {
dev_err(sev->dev, "SEV-SNP: %s failed rc %d, error %#x\n",
cmd == SEV_CMD_SNP_INIT_EX ? "SNP_INIT_EX" : "SNP_INIT",
rc, *error);
return rc;
}
...
}
Since sev->snp_initialized remains false on this error path, snp_shutdown()
is never executed on teardown, meaning cpu_hotplug_enable() is never invoked
to release the lock.
> sev->snp_initialized = true;
> dev_dbg(sev->dev, "SEV-SNP firmware initialized, SEV-TIO is %s\n",
> data.tio_en ? "enabled" : "disabled");
--
Sashiko AI review · https://sashiko.dev/#/patchset/cover.1784844080.git.ashish.kalra@amd.com?part=3
^ permalink raw reply [flat|nested] 14+ messages in thread* Re: [PATCH v11 3/6] x86/sev: Initialize RMPOPT configuration MSRs
2026-07-27 19:22 ` sashiko-bot
@ 2026-07-27 21:02 ` Kalra, Ashish
0 siblings, 0 replies; 14+ messages in thread
From: Kalra, Ashish @ 2026-07-27 21:02 UTC (permalink / raw)
To: sashiko-reviews; +Cc: kvm
On 7/27/2026 2:22 PM, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
> - [High] `snp_cleanup_rmpopt()` unconditionally dereferences `rmpopt_cpumask`, causing a deterministic kernel panic on shutdown if the mask was never allocated (e.g., on CPUs without RMPOPT support or if allocation fails).
> - [High] If SEV-SNP firmware initialization fails, the error path returns directly without cleaning up the state set by `snp_prepare()`, leaving CPU hotplug permanently disabled.
> - [Medium] Re-initializing SEV-SNP after a legacy shutdown leaks the `rmpopt_cpumask` allocation because the old pointer is overwritten without being freed.
> --
>
> --- Patch [3]: [PATCH v11 3/6] x86/sev: Initialize RMPOPT configuration MSRs ---
> commit e4623d49ab6201adc5b609f5a6043ed6dfb7f55c
> Author: Ashish Kalra <ashish.kalra@amd.com>
> Subject: x86/sev: Initialize RMPOPT configuration MSRs
>
> The new RMPOPT instruction helps manage per-CPU RMP optimization
> structures inside the CPU. This commit initializes the per-CPU RMPOPT
> table base to enable RMP optimization for up to 2 TB of system RAM.
>
>> diff --git a/arch/x86/virt/svm/sev.c b/arch/x86/virt/svm/sev.c
>> index e2f69fba09389..8bfd802848361 100644
>> --- a/arch/x86/virt/svm/sev.c
>> +++ b/arch/x86/virt/svm/sev.c
> [ ... ]
>> @@ -558,6 +561,17 @@ int snp_prepare(void)
>> }
>> EXPORT_SYMBOL_FOR_MODULES(snp_prepare, "ccp");
>>
>> +static void snp_cleanup_rmpopt(void)
>> +{
>> + int cpu;
>> +
>> + for_each_cpu(cpu, rmpopt_cpumask)
>
> [Severity: High]
> If rmpopt_cpumask was never allocated, does this macro dereference a NULL
> pointer?
>
> In snp_setup_rmpopt(), if rmpopt_capable() is false, the mask remains
> unallocated. When snp_shutdown() subsequently calls snp_cleanup_rmpopt(),
> it iterates over the mask without checking if it is available.
>
> Can this cause a kernel panic on system shutdown or module unload for
> systems without RMPOPT support?
Can't happen in the full series: patch 4 adds if (!rmpopt_wq) return;, and rmpopt_wq is non-NULL only after snp_setup_rmpopt() succeeds.
On a host without RMPOPT it stays NULL, so rmpopt_cpumask is never dereferenced.
>
>> + wrmsrq_on_cpu(cpu, MSR_AMD64_RMPOPT_BASE, 0);
>> +
>> + free_cpumask_var(rmpopt_cpumask);
>> + rmpopt_pa_start = 0;
>> +}
> [ ... ]
>> @@ -578,6 +593,46 @@ void snp_shutdown(void)
>> }
>> EXPORT_SYMBOL_FOR_MODULES(snp_shutdown, "ccp");
>>
>> +static bool rmpopt_capable(void)
>> +{
>> + return cpu_feature_enabled(X86_FEATURE_RMPOPT) &&
>> + cc_platform_has(CC_ATTR_HOST_SEV_SNP);
>> +}
>> +
>> +void snp_setup_rmpopt(void)
>> +{
>> + u64 rmpopt_base;
>> + int cpu;
>> +
>> + if (!rmpopt_capable())
>> + return;
>> +
>> + if (!zalloc_cpumask_var(&rmpopt_cpumask, GFP_KERNEL)) {
>
> [Severity: Medium]
> Does this unconditional allocation leak the previously allocated cpumask
> when SEV-SNP is re-initialized?
>
> During a legacy shutdown, snp_shutdown() returns early when the SnpEn
> firmware bit is still set, skipping snp_cleanup_rmpopt():
>
> arch/x86/virt/svm/sev.c:snp_shutdown() {
> ...
> rdmsrq(MSR_AMD64_SYSCFG, syscfg);
> if (syscfg & MSR_AMD64_SYSCFG_SNP_EN)
> return;
> ...
> }
>
> A subsequent call to __sev_snp_init_locked() will invoke snp_setup_rmpopt()
> again and overwrite the old pointer without freeing it.
Handled in the full series: patch 4's re-init guard (if (rmpopt_wq) { queue_delayed_work(...); return; }) re-queues the pass instead of re-allocating,
so there's no leak.
>
>> + pr_err("Failed to allocate RMPOPT cpumask\n");
>> + return;
>> + }
> [ ... ]
>> diff --git a/drivers/crypto/ccp/sev-dev.c b/drivers/crypto/ccp/sev-dev.c
>> index f833cb7e4da36..0b40d23a26b45 100644
>> --- a/drivers/crypto/ccp/sev-dev.c
>> +++ b/drivers/crypto/ccp/sev-dev.c
>> @@ -1630,6 +1630,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();
>> +
>
> [Severity: High]
> If the SEV-SNP firmware initialization fails, does this leave CPU hotplug
> permanently disabled?
>
> A global cpu_hotplug_disable() call is made inside snp_prepare() right before
> we reach this code. If the subsequent firmware command fails in
> __sev_snp_init_locked(), it returns the error directly without cleaning up:
>
> drivers/crypto/ccp/sev-dev.c:__sev_snp_init_locked() {
> ...
> rc = __sev_do_cmd_locked(cmd, arg, error);
> if (rc) {
> dev_err(sev->dev, "SEV-SNP: %s failed rc %d, error %#x\n",
> cmd == SEV_CMD_SNP_INIT_EX ? "SNP_INIT_EX" : "SNP_INIT",
> rc, *error);
> return rc;
> }
> ...
> }
>
> Since sev->snp_initialized remains false on this error path, snp_shutdown()
> is never executed on teardown, meaning cpu_hotplug_enable() is never invoked
> to release the lock.
Intentional. snp_prepare() re-enables hotplug on its own failure (before SnpEn is set); once SnpEn is set, hotplug must stay disabled —
a CPU coming online without it would break RMP-check consistency and any SNP_INIT retry (which needs SnpEn on all present CPUs).
Documented in the commit message, and the triggering failure is already logged.
Thanks,
Ashish
>
>> sev->snp_initialized = true;
>> dev_dbg(sev->dev, "SEV-SNP firmware initialized, SEV-TIO is %s\n",
>> data.tio_en ? "enabled" : "disabled");
>
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v11 4/6] x86/sev: Add support to perform RMP optimizations asynchronously
2026-07-27 19:00 [PATCH v11 0/6] Add RMPOPT support Ashish Kalra
` (2 preceding siblings ...)
2026-07-27 19:04 ` [PATCH v11 3/6] x86/sev: Initialize RMPOPT configuration MSRs Ashish Kalra
@ 2026-07-27 19:05 ` Ashish Kalra
2026-07-27 19:22 ` sashiko-bot
2026-07-27 19:05 ` [PATCH v11 5/6] x86/sev: Add interface to re-enable RMP optimizations Ashish Kalra
2026-07-27 19:06 ` [PATCH v11 6/6] KVM: SEV: Perform RMP optimizations on SNP guest shutdown Ashish Kalra
5 siblings, 1 reply; 14+ messages in thread
From: Ashish Kalra @ 2026-07-27 19:05 UTC (permalink / raw)
To: tglx, mingo, bp, dave.hansen, x86, hpa, seanjc, peterz,
thomas.lendacky, herbert, davem, ardb
Cc: pbonzini, aik, Michael.Roth, KPrateek.Nayak, Tycho.Andersen,
Nathan.Fontenot, ackerleytng, jackyli, pgonda, rientjes, jacobhxu,
xin, pawan.kumar.gupta, babu.moger, dyoung, nikunj, john.allen,
darwi, linux-kernel, linux-crypto, kvm, linux-coco
From: Ashish Kalra <ashish.kalra@amd.com>
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.
Suggested-by: Thomas Lendacky <thomas.lendacky@amd.com>
Suggested-by: Dave Hansen <dave.hansen@linux.intel.com>
Suggested-by: K Prateek Nayak <kprateek.nayak@amd.com>
Reviewed-by: Ackerley Tng <ackerleytng@google.com>
Signed-off-by: Ashish Kalra <ashish.kalra@amd.com>
---
arch/x86/virt/svm/sev.c | 160 +++++++++++++++++++++++++++++++++++++++-
1 file changed, 158 insertions(+), 2 deletions(-)
diff --git a/arch/x86/virt/svm/sev.c b/arch/x86/virt/svm/sev.c
index 8bfd80284836..04b19e64f832 100644
--- a/arch/x86/virt/svm/sev.c
+++ b/arch/x86/virt/svm/sev.c
@@ -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,7 +126,18 @@ 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;
+
+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);
@@ -565,11 +577,20 @@ static void snp_cleanup_rmpopt(void)
{
int cpu;
+ guard(mutex)(&rmpopt_wq_mutex);
+
+ if (!rmpopt_wq)
+ return;
+
+ cancel_delayed_work_sync(&rmpopt_delayed_work);
+ destroy_workqueue(rmpopt_wq);
+
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)
@@ -599,6 +620,96 @@ static bool rmpopt_capable(void)
cc_platform_has(CC_ATTR_HOST_SEV_SNP);
}
+/*
+ * 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);
+
+ migrate_enable();
+
+ /*
+ * 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.
+ */
+ for (pa = rmpopt_pa_start; pa < rmpopt_pa_end; pa += SZ_1G)
+ on_each_cpu_mask(follower_mask, rmpopt_smp, (void *)pa, true);
+
+ free_cpumask_var(follower_mask);
+}
+
void snp_setup_rmpopt(void)
{
u64 rmpopt_base;
@@ -607,8 +718,38 @@ void snp_setup_rmpopt(void)
if (!rmpopt_capable())
return;
+ guard(mutex)(&rmpopt_wq_mutex);
+
+ /*
+ * On re-initialization after a legacy SNP shutdown (SNP_SHUTDOWN_EX
+ * with x86_snp_shutdown=0), snp_shutdown() and thus snp_cleanup_rmpopt()
+ * are skipped, so the workqueue, delayed work, cpumask and per-CPU
+ * RMPOPT_BASE MSRs are still set up and valid (SnpEn stayed set and
+ * CPU hotplug stayed disabled). Rather than re-doing the setup, which
+ * would leak the existing state, just re-queue the optimization pass
+ * to re-optimize any memory the previous SNP session de-optimized.
+ */
+ if (rmpopt_wq) {
+ queue_delayed_work(rmpopt_wq, &rmpopt_delayed_work, 0);
+ 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;
}
@@ -630,6 +771,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");
--
2.43.0
^ permalink raw reply related [flat|nested] 14+ messages in thread* Re: [PATCH v11 4/6] x86/sev: Add support to perform RMP optimizations asynchronously
2026-07-27 19:05 ` [PATCH v11 4/6] x86/sev: Add support to perform RMP optimizations asynchronously Ashish Kalra
@ 2026-07-27 19:22 ` sashiko-bot
2026-07-27 20:49 ` Kalra, Ashish
0 siblings, 1 reply; 14+ messages in thread
From: sashiko-bot @ 2026-07-27 19:22 UTC (permalink / raw)
To: Ashish Kalra; +Cc: kvm
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] Inline assembly for __rmpopt fails to specify EAX/RAX as a clobbered or output register, risking an infinite loop.
- [High] Missing CPU yield in long-running RMP optimization loops leads to soft lockups on non-preemptible kernels.
--
--- Patch [4]: [PATCH v11 4/6] x86/sev: Add support to perform RMP optimizations asynchronously ---
commit 9b1e0ac7d870b3ba02472132010185afc997e02d
Author: Ashish Kalra <ashish.kalra@amd.com>
x86/sev: Add support to perform RMP optimizations asynchronously
This commit adds support for performing RMP optimizations asynchronously using
a dedicated workqueue. It introduces a new RMPOPT instruction to skip RMP checks
for 1GB regions not containing SEV-SNP guest memory. Optimizations are enabled
for up to 2TB of system RAM starting at RMP initialization time.
> diff --git a/arch/x86/virt/svm/sev.c b/arch/x86/virt/svm/sev.c
> index 8bfd802848361..04b19e64f8329 100644
> --- a/arch/x86/virt/svm/sev.c
> +++ b/arch/x86/virt/svm/sev.c
[ ... ]
> +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");
[Severity: High]
Does this inline assembly need to specify EAX/RAX as a clobbered or output
register?
If the RMPOPT instruction modifies RAX (as AMD SEV-SNP instructions like
RMPUPDATE typically return an execution status code in EAX/RAX), the current
specification of RAX as input-only ("a" (pa_start)) could allow the compiler
to optimize the loop counter into RAX and silently overwrite it.
This could lead to an infinite loop during rmpopt_work_handler() execution.
> +
> + return optimized;
> +}
[ ... ]
> +static void rmpopt_work_handler(struct work_struct *work)
> +{
[ ... ]
> + 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);
[Severity: High]
Does this loop need a cond_resched() to yield the CPU?
This loops up to 2048 times doing an expensive hardware scan inside a
migrate_disable() block. On kernels with CONFIG_PREEMPT_NONE or
PREEMPT_VOLUNTARY, preemption is not implicit.
Without explicitly yielding the CPU, this could cause severe scheduling latency
spikes or trigger a soft lockup watchdog panic.
> +
> + migrate_enable();
> +
> + /*
> + * 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.
> + */
> + for (pa = rmpopt_pa_start; pa < rmpopt_pa_end; pa += SZ_1G)
> + on_each_cpu_mask(follower_mask, rmpopt_smp, (void *)pa, true);
[Severity: High]
Could this secondary follower loop also need a cond_resched() to prevent
CPU monopolization?
This loop iterates up to 2048 times broadcasting IPIs via on_each_cpu_mask().
Similar to the first loop, without a yield, this could lead to soft lockups on
non-preemptible kernels.
> +
> + free_cpumask_var(follower_mask);
> +}
--
Sashiko AI review · https://sashiko.dev/#/patchset/cover.1784844080.git.ashish.kalra@amd.com?part=4
^ permalink raw reply [flat|nested] 14+ messages in thread* Re: [PATCH v11 4/6] x86/sev: Add support to perform RMP optimizations asynchronously
2026-07-27 19:22 ` sashiko-bot
@ 2026-07-27 20:49 ` Kalra, Ashish
0 siblings, 0 replies; 14+ messages in thread
From: Kalra, Ashish @ 2026-07-27 20:49 UTC (permalink / raw)
To: sashiko-reviews; +Cc: kvm
On 7/27/2026 2:22 PM, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
> - [High] Inline assembly for __rmpopt fails to specify EAX/RAX as a clobbered or output register, risking an infinite loop.
> - [High] Missing CPU yield in long-running RMP optimization loops leads to soft lockups on non-preemptible kernels.
> --
>
> --- Patch [4]: [PATCH v11 4/6] x86/sev: Add support to perform RMP optimizations asynchronously ---
> commit 9b1e0ac7d870b3ba02472132010185afc997e02d
> Author: Ashish Kalra <ashish.kalra@amd.com>
>
> x86/sev: Add support to perform RMP optimizations asynchronously
>
> This commit adds support for performing RMP optimizations asynchronously using
> a dedicated workqueue. It introduces a new RMPOPT instruction to skip RMP checks
> for 1GB regions not containing SEV-SNP guest memory. Optimizations are enabled
> for up to 2TB of system RAM starting at RMP initialization time.
>
>> diff --git a/arch/x86/virt/svm/sev.c b/arch/x86/virt/svm/sev.c
>> index 8bfd802848361..04b19e64f8329 100644
>> --- a/arch/x86/virt/svm/sev.c
>> +++ b/arch/x86/virt/svm/sev.c
>
> [ ... ]
>
>> +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");
>
> [Severity: High]
> Does this inline assembly need to specify EAX/RAX as a clobbered or output
> register?
>
> If the RMPOPT instruction modifies RAX (as AMD SEV-SNP instructions like
> RMPUPDATE typically return an execution status code in EAX/RAX), the current
> specification of RAX as input-only ("a" (pa_start)) could allow the compiler
> to optimize the loop counter into RAX and silently overwrite it.
>
> This could lead to an infinite loop during rmpopt_work_handler() execution.
>
RMPOPT's only output is RFLAGS.CF; it does not modify RAX. RAX is not modified and cannot be clobbered and
so the loop counter can't be corrupted. (RMPUPDATE differs: it returns a status in RAX, hence "=a".)
>> +
>> + return optimized;
>> +}
>
> [ ... ]
>
>> +static void rmpopt_work_handler(struct work_struct *work)
>> +{
>
> [ ... ]
>
>> + 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);
>
> [Severity: High]
> Does this loop need a cond_resched() to yield the CPU?
>
> This loops up to 2048 times doing an expensive hardware scan inside a
> migrate_disable() block. On kernels with CONFIG_PREEMPT_NONE or
> PREEMPT_VOLUNTARY, preemption is not implicit.
>
> Without explicitly yielding the CPU, this could cause severe scheduling latency
> spikes or trigger a soft lockup watchdog panic.
>
Dropped intentionally: since commit 7dadeaa6e851 x86 has no non-preemptible model (PREEMPT_VOLUNTARY/PREEMPT_NONE aren't selectable),
so cond_resched() is a no-op and the loops are already preemptible — migrate_disable() pins the CPU but doesn't disable preemption.
No soft-lockup risk.
>> +
>> + migrate_enable();
>> +
>> + /*
>> + * 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.
>> + */
>> + for (pa = rmpopt_pa_start; pa < rmpopt_pa_end; pa += SZ_1G)
>> + on_each_cpu_mask(follower_mask, rmpopt_smp, (void *)pa, true);
>
> [Severity: High]
> Could this secondary follower loop also need a cond_resched() to prevent
> CPU monopolization?
>
> This loop iterates up to 2048 times broadcasting IPIs via on_each_cpu_mask().
> Similar to the first loop, without a yield, this could lead to soft lockups on
> non-preemptible kernels.
>
Dropped intentionally: since commit 7dadeaa6e851 x86 has no non-preemptible model (PREEMPT_VOLUNTARY/PREEMPT_NONE aren't selectable),
so cond_resched() is a no-op and the loops are already preemptible — No soft-lockup risk.
Thanks,
Ashish
>> +
>> + free_cpumask_var(follower_mask);
>> +}
>
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v11 5/6] x86/sev: Add interface to re-enable RMP optimizations.
2026-07-27 19:00 [PATCH v11 0/6] Add RMPOPT support Ashish Kalra
` (3 preceding siblings ...)
2026-07-27 19:05 ` [PATCH v11 4/6] x86/sev: Add support to perform RMP optimizations asynchronously Ashish Kalra
@ 2026-07-27 19:05 ` Ashish Kalra
2026-07-27 19:06 ` [PATCH v11 6/6] KVM: SEV: Perform RMP optimizations on SNP guest shutdown Ashish Kalra
5 siblings, 0 replies; 14+ messages in thread
From: Ashish Kalra @ 2026-07-27 19:05 UTC (permalink / raw)
To: tglx, mingo, bp, dave.hansen, x86, hpa, seanjc, peterz,
thomas.lendacky, herbert, davem, ardb
Cc: pbonzini, aik, Michael.Roth, KPrateek.Nayak, Tycho.Andersen,
Nathan.Fontenot, ackerleytng, jackyli, pgonda, rientjes, jacobhxu,
xin, pawan.kumar.gupta, babu.moger, dyoung, nikunj, john.allen,
darwi, linux-kernel, linux-crypto, kvm, linux-coco
From: Ashish Kalra <ashish.kalra@amd.com>
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.
Reviewed-by: Ackerley Tng <ackerleytng@google.com>
Signed-off-by: Ashish Kalra <ashish.kalra@amd.com>
---
arch/x86/include/asm/sev.h | 2 ++
arch/x86/virt/svm/sev.c | 15 +++++++++++++++
2 files changed, 17 insertions(+)
diff --git a/arch/x86/include/asm/sev.h b/arch/x86/include/asm/sev.h
index 6fd72a44a51e..09b1c5d33790 100644
--- a/arch/x86/include/asm/sev.h
+++ b/arch/x86/include/asm/sev.h
@@ -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_shutdown(void);
#else
@@ -681,6 +682,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_shutdown(void) {}
#endif
diff --git a/arch/x86/virt/svm/sev.c b/arch/x86/virt/svm/sev.c
index 04b19e64f832..291a360291eb 100644
--- a/arch/x86/virt/svm/sev.c
+++ b/arch/x86/virt/svm/sev.c
@@ -710,6 +710,21 @@ static void rmpopt_work_handler(struct work_struct *work)
free_cpumask_var(follower_mask);
}
+void snp_rmpopt_all_physmem(void)
+{
+ if (!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;
--
2.43.0
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH v11 6/6] KVM: SEV: Perform RMP optimizations on SNP guest shutdown
2026-07-27 19:00 [PATCH v11 0/6] Add RMPOPT support Ashish Kalra
` (4 preceding siblings ...)
2026-07-27 19:05 ` [PATCH v11 5/6] x86/sev: Add interface to re-enable RMP optimizations Ashish Kalra
@ 2026-07-27 19:06 ` Ashish Kalra
5 siblings, 0 replies; 14+ messages in thread
From: Ashish Kalra @ 2026-07-27 19:06 UTC (permalink / raw)
To: tglx, mingo, bp, dave.hansen, x86, hpa, seanjc, peterz,
thomas.lendacky, herbert, davem, ardb
Cc: pbonzini, aik, Michael.Roth, KPrateek.Nayak, Tycho.Andersen,
Nathan.Fontenot, ackerleytng, jackyli, pgonda, rientjes, jacobhxu,
xin, pawan.kumar.gupta, babu.moger, dyoung, nikunj, john.allen,
darwi, linux-kernel, linux-crypto, kvm, linux-coco
From: Ashish Kalra <ashish.kalra@amd.com>
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.
Acked-by: Dave Hansen <dave.hansen@linux.intel.com>
Reviewed-by: Ackerley Tng <ackerleytng@google.com>
Signed-off-by: Ashish Kalra <ashish.kalra@amd.com>
---
arch/x86/kvm/svm/sev.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c
index 427229347876..c574849b1587 100644
--- a/arch/x86/kvm/svm/sev.c
+++ b/arch/x86/kvm/svm/sev.c
@@ -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);
}
--
2.43.0
^ permalink raw reply related [flat|nested] 14+ messages in thread