From: "Kalra, Ashish" <ashish.kalra@amd.com>
To: Borislav Petkov <bp@alien8.de>
Cc: tglx@kernel.org, mingo@redhat.com, dave.hansen@linux.intel.com,
x86@kernel.org, hpa@zytor.com, seanjc@google.com,
peterz@infradead.org, thomas.lendacky@amd.com,
herbert@gondor.apana.org.au, davem@davemloft.net,
ardb@kernel.org, pbonzini@redhat.com, aik@amd.com,
Michael.Roth@amd.com, KPrateek.Nayak@amd.com,
Tycho.Andersen@amd.com, Nathan.Fontenot@amd.com,
ackerleytng@google.com, jackyli@google.com, pgonda@google.com,
rientjes@google.com, jacobhxu@google.com, xin@zytor.com,
pawan.kumar.gupta@linux.intel.com, babu.moger@amd.com,
dyoung@redhat.com, nikunj@amd.com, darwi@linutronix.de,
linux-kernel@vger.kernel.org, linux-crypto@vger.kernel.org,
kvm@vger.kernel.org, linux-coco@lists.linux.dev
Subject: Re: [PATCH v13 4/5] x86/sev: Add support to perform RMP optimizations asynchronously
Date: Tue, 8 Sep 2026 15:21:31 -0500 [thread overview]
Message-ID: <4b940989-1c27-49a5-8112-d9f991ad1d48@amd.com> (raw)
In-Reply-To: <20260905012945.GPaptwicVjJ-SwXYzl@fat_crate.local>
Hello Boris,
On 9/4/2026 8:29 PM, Borislav Petkov wrote:
> On Wed, Sep 02, 2026 at 09:28:57PM +0000, Ashish Kalra wrote:
>> Subject: Re: [PATCH v13 4/5] x86/sev: Add support to perform RMP optimizations asynchronously
>
> s/Add support to perform/Perform/
>
>> From: Ashish Kalra <ashish.kalra@amd.com>
>>
>> When SNP is enabled, all writes to memory are checked to ensure memory
>> integrity. 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 SNP guest memory.
>>
>> Add support for performing RMP optimizations asynchronously using a
>> dedicated workqueue.
>>
>> At RMP initialization time, run an optimization pass over all physical
>> memory (up to 2TB of system RAM, starting from the lowest physical
>> memory address aligned down to a 1GB boundary), skipping RMP checks for
>> 1GB regions that do not contain SNP guest memory (excluding preassigned
>> pages such as the RMP table and firmware pages).
>>
>> As SNP guests are launched, RMPUPDATE assigns their private pages to
>> guest-owned state; when such a page falls within an optimized 1GB
>> region, the hardware clears that region's RMPOPT optimization and RMP
>> checks resume there to protect the guest memory.
>>
>> Since launching SNP guests clears these optimizations, perform them
>> again asynchronously using the dedicated workqueue.
>>
>> 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>
>> Suggested-by: Borislav Petkov (AMD) <bp@alien8.de>
>> Reviewed-by: Ackerley Tng <ackerleytng@google.com>
>> Reviewed-by: Tom Lendacky <thomas.lendacky@amd.com>
>
> R-by's need to get dropped when a patch changes in more or less significant
> way.
Tom and Dave gave their R-by's on v12/v13 series, so probably i will keep their
R-b's.
>
>> @@ -561,10 +571,26 @@ static void rmpopt_disable(void)
>> {
>> int cpu;
>>
>> + guard(mutex)(&rmpopt_wq_mutex);
>> +
>> + /*
>> + * rmpopt_wq is non-NULL only after RMPOPT has been fully set up: the
>> + * workqueue is allocated and the RMPOPT_BASE MSRs are programmed.
>> + * snp_setup_rmpopt() resets it to NULL if any of those steps fail, so a
>> + * NULL rmpopt_wq means nothing was set up and there is nothing to tear
>> + * down.
>> + */
>> + if (!rmpopt_wq)
>
> Now take your patch and rip all that gunk which destroys the setup work done
> by snp_setup_rmpopt(). Instead, you init things once and do not touch them
> even if RMP optimizations are disabled.
>
> In case they get enabled again later, you simply reactivate them instead of
> doing useless setup work all over again.
>
So, you want me to set up once, never tear down, on re-enable just reactivate.
So rmpopt_disable() should stop destroying the workqueue, clearing the RMPOPT_BASE MSRs, and resetting pa_start/pa_end.
One thing i need to handle here: The current rmpopt_disable() clears the MSRs for a reason: it runs only on a full SNP
shutdown (SnpEn cleared), right before cpu_hotplug_enable(). Once hotplug is back on, a primary thread can offline -> online
and lose its RMPOPT_BASE (reset to 0, no cpuhp callback - which is only safe because hotplug is disabled while SNP is active).
If we simply stop clearing MSRs but keep the current re-init fast-path that skips MSR programming, a re-init after such a cycle
could run rmpopt() on a core with RMPOPT_BASE=0.
So the fix is: keep the setup allocated once, but make setup always (re)program RMPOPT_BASE — then we never need to clear it on
disable, and the cycle case is covered on re-init. Proposed fix:
/* full shutdown: just stop a pending pass; leave the setup intact */
static void rmpopt_disable(void)
{
guard(mutex)(&rmpopt_wq_mutex);
cancel_delayed_work_sync(&rmpopt_delayed_work);
}
void snp_setup_rmpopt(void)
{
...
if (!rmpopt_capable())
return;
guard(mutex)(&rmpopt_wq_mutex);
/* allocate the workqueue exactly once, keep it for the kernel's life */
if (!rmpopt_wq) {
rmpopt_wq = alloc_workqueue(...); /* or use system_unbound_wq, see below */
if (!rmpopt_wq) { pr_err(...); return; }
INIT_DELAYED_WORK(&rmpopt_delayed_work, do_rmpopt_work);
}
/* always (re)program MSRs + range, so a hotplug cycle across a full
shutdown can't leave a core with a stale RMPOPT_BASE */
rmpopt_pa_start = ALIGN_DOWN(PFN_PHYS(min_low_pfn), SZ_1G);
rmpopt_base = rmpopt_pa_start | MSR_AMD64_RMPOPT_ENABLE;
for_each_cpu(cpu, cpu_primary_thread_mask)
wrmsrq_on_cpu(cpu, MSR_AMD64_RMPOPT_BASE, rmpopt_base);
rmpopt_pa_end = ALIGN(PFN_PHYS(max_pfn), SZ_1G);
if ((rmpopt_pa_end - rmpopt_pa_start) > SZ_2T)
rmpopt_pa_end = rmpopt_pa_start + SZ_2T;
queue_delayed_work(rmpopt_wq, &rmpopt_delayed_work, 0);
pr_info("RMPOPT optimizations enabled\n");
}
>> + return;
>> +
>> + cancel_delayed_work_sync(&rmpopt_delayed_work);
>> + destroy_workqueue(rmpopt_wq);
>> +
>> for_each_cpu(cpu, cpu_primary_thread_mask)
>> wrmsrq_on_cpu(cpu, MSR_AMD64_RMPOPT_BASE, 0);
>>
>> - rmpopt_pa_start = 0;
>> + rmpopt_pa_start = rmpopt_pa_end = 0;
>> + rmpopt_wq = NULL;
>> }
>>
>> void snp_shutdown(void)
>> @@ -595,6 +621,44 @@ static bool rmpopt_capable(void)
>> cc_platform_has(CC_ATTR_HOST_SEV_SNP);
>> }
>>
>> +/*
>> + * RMPOPT optimizations skip RMP checks at 1GB granularity if this range of
>> + * memory does not contain any SNP guest memory.
>> + *
>> + * @pa is a system physical address; RMPOPT operates on the containing 1GB.
>> + */
>> +static void rmpopt(u64 pa)
>> +{
>> + enum rmpopt_op_type op = RMPOPT_OP_VERIFY_AND_REPORT_STATUS;
>> + u64 pa_start = ALIGN_DOWN(pa, SZ_1G);
>> +
>
> /* Supported by binutils 2.48+ */
>
>> + asm volatile(".byte 0xf2, 0x0f, 0x01, 0xfc"
>> + :: "a" (pa_start), "c" (op)
>> + : "memory", "cc");
>> +}
>
>
>
>> +
>> +/* on_each_cpu() callback: optimize the whole RMPOPT range on this CPU. */
>> +static void rmpopt_scan_range(void *arg)
>> +{
>> + u64 pa;
>> +
>> + for (pa = rmpopt_pa_start; pa < rmpopt_pa_end; pa += SZ_1G)
>> + rmpopt(pa);
>> +}
>> +
>> +static void do_rmpopt_work(struct work_struct *work)
>> +{
>> + /*
>> + * RMPOPT caches the results of a RMP table scan in reserved processor
>> + * memory, allowing future invocations to skip such costly operations.
>> + */
>
> We know already. Drop this comment.
>
>> + migrate_disable();
>> + rmpopt_scan_range(NULL);
>> + migrate_enable();
>> +
>> + on_each_cpu_mask(cpu_primary_thread_mask, rmpopt_scan_range, NULL, true);
>> +}
>> +
>> void snp_setup_rmpopt(void)
>> {
>> u64 rmpopt_base;
>> @@ -603,6 +667,34 @@ 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 rmpopt_disable() are
>> + * skipped, so the workqueue, delayed work 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.
>> + */
>
> Why?
>
A full‑physmem RMPOPT pass is a warm‑up scan plus an IPI fan‑out over up to 2TB. The default system_wq is per‑CPU and concurrency‑managed,
a long‑running item there runs on the queueing CPU's worker pool and can stall (or be stalled by) other work on that pool. So we wanted it
off system_wq and use a dedicated RMPOPT specific workqueue.
Another thing i looked at is for long-running unbound work, probably the standard shared queue is system_unbound_wq, which is probably built
for this use case and won't clog the per-CPU system_wq.
If we switch to it, there's no workqueue to allocate or free — the setup/teardown "gunk" disappears entirely (static
DECLARE_DELAYED_WORK, queue/mod_delayed_work(system_unbound_wq, …)), which satisfies your "init once, don't touch."
Additionally, with system_unbound_wq and a static delayed_work there's no pointer to guard, and queue/mod/cancel are already
synchronized by the workqueue core, so rmpopt_wq_mutex can also be dropped.
Thanks,
Ashish
>> + 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, do_rmpopt_work);
>> +
>> rmpopt_pa_start = ALIGN_DOWN(PFN_PHYS(min_low_pfn), SZ_1G);
>> rmpopt_base = rmpopt_pa_start | MSR_AMD64_RMPOPT_ENABLE;
>>
>> @@ -612,6 +704,23 @@ void snp_setup_rmpopt(void)
>> */
>> for_each_cpu(cpu, cpu_primary_thread_mask)
>> 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 */
>
> No need for that comment - we know.
>
>> + if ((rmpopt_pa_end - rmpopt_pa_start) > SZ_2T) {
>> + pr_info("RMPOPT coverage limited to 2TB; memory above 0x%llx not optimized\n",
>
> No need for that print - nothing we can do about it anyway.
>
>> + 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.
>> + */
>
> Drop this comment too.
>
>> + queue_delayed_work(rmpopt_wq, &rmpopt_delayed_work, 0);
>> +
>> + pr_info("RMPOPT optimizations enabled\n");
>> }
>> EXPORT_SYMBOL_FOR_MODULES(snp_setup_rmpopt, "ccp");
>>
>> --
>> 2.43.0
>>
>
next prev parent reply other threads:[~2026-09-08 20:21 UTC|newest]
Thread overview: 13+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-02 21:27 [PATCH v13 0/5] Add RMPOPT support Ashish Kalra
2026-09-02 21:27 ` [PATCH v13 1/5] x86/cpufeatures: Add X86_FEATURE_RMPOPT feature flag Ashish Kalra
2026-09-02 21:28 ` [PATCH v13 2/5] x86/sev: Disable CPU hotplug while SNP is active Ashish Kalra
2026-09-02 21:28 ` [PATCH v13 3/5] x86/sev: Initialize RMPOPT configuration MSRs Ashish Kalra
2026-09-02 21:28 ` [PATCH v13 4/5] x86/sev: Add support to perform RMP optimizations asynchronously Ashish Kalra
2026-09-02 21:36 ` Dave Hansen
2026-09-05 1:29 ` Borislav Petkov
2026-09-08 20:21 ` Kalra, Ashish [this message]
2026-09-09 1:52 ` Borislav Petkov
2026-09-09 13:55 ` Kalra, Ashish
2026-09-02 21:29 ` [PATCH v13 5/5] x86/sev: Re-enable RMP optimizations on SNP guest shutdown Ashish Kalra
2026-09-02 21:37 ` Dave Hansen
2026-09-06 17:24 ` Borislav Petkov
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=4b940989-1c27-49a5-8112-d9f991ad1d48@amd.com \
--to=ashish.kalra@amd.com \
--cc=KPrateek.Nayak@amd.com \
--cc=Michael.Roth@amd.com \
--cc=Nathan.Fontenot@amd.com \
--cc=Tycho.Andersen@amd.com \
--cc=ackerleytng@google.com \
--cc=aik@amd.com \
--cc=ardb@kernel.org \
--cc=babu.moger@amd.com \
--cc=bp@alien8.de \
--cc=darwi@linutronix.de \
--cc=dave.hansen@linux.intel.com \
--cc=davem@davemloft.net \
--cc=dyoung@redhat.com \
--cc=herbert@gondor.apana.org.au \
--cc=hpa@zytor.com \
--cc=jackyli@google.com \
--cc=jacobhxu@google.com \
--cc=kvm@vger.kernel.org \
--cc=linux-coco@lists.linux.dev \
--cc=linux-crypto@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=mingo@redhat.com \
--cc=nikunj@amd.com \
--cc=pawan.kumar.gupta@linux.intel.com \
--cc=pbonzini@redhat.com \
--cc=peterz@infradead.org \
--cc=pgonda@google.com \
--cc=rientjes@google.com \
--cc=seanjc@google.com \
--cc=tglx@kernel.org \
--cc=thomas.lendacky@amd.com \
--cc=x86@kernel.org \
--cc=xin@zytor.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox