Kernel KVM virtualization development
 help / color / mirror / Atom feed
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
>>
> 

  reply	other threads:[~2026-09-08 20:21 UTC|newest]

Thread overview: 21+ 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:51   ` sashiko-bot
2026-09-02 22:12     ` Kalra, Ashish
2026-09-02 21:28 ` [PATCH v13 3/5] x86/sev: Initialize RMPOPT configuration MSRs Ashish Kalra
2026-09-02 21:44   ` sashiko-bot
2026-09-02 21:56     ` Kalra, Ashish
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-02 21:57   ` sashiko-bot
2026-09-02 22:36     ` Kalra, Ashish
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-02 22:01   ` sashiko-bot
2026-09-02 22:45     ` Kalra, Ashish
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