The Linux Kernel Mailing List
 help / color / mirror / Atom feed
From: Sean Christopherson <seanjc@google.com>
To: David Woodhouse <dwmw2@infradead.org>
Cc: Paul Durrant <paul@xen.org>, Hyunwoo Kim <imv4bel@gmail.com>,
	Paolo Bonzini <pbonzini@redhat.com>,
	 Thomas Gleixner <tglx@kernel.org>,
	Ingo Molnar <mingo@redhat.com>, Borislav Petkov <bp@alien8.de>,
	 Dave Hansen <dave.hansen@linux.intel.com>,
	x86@kernel.org,  "H. Peter Anvin" <hpa@zytor.com>,
	kvm@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: Re: [PATCH 8/8] KVM: x86/xen: Use 32-bit locked ops in kvm_xen_inject_pending_events()
Date: Wed, 8 Jul 2026 08:23:36 -0700	[thread overview]
Message-ID: <ak5reLzVpGuVJtEH@google.com> (raw)
In-Reply-To: <20260605143034.3603-9-dwmw2@infradead.org>

On Fri, Jun 05, 2026, David Woodhouse wrote:
> From: David Woodhouse <dwmw@amazon.co.uk>
> 
> The 64-bit path in kvm_xen_inject_pending_events() uses 'lock orq' and
> 'lock andq' on vcpu_info->evtchn_pending_sel. If the vcpu_info was
> registered with only 4-byte alignment (valid for a 32-bit guest that
> later switches to 64-bit mode), this 8-byte locked operation can cause
> split-lock #AC exceptions on hosts with split_lock_detect=fatal.
> 
> Use the original 64-bit atomics when the vcpu_info is 8-byte aligned
> (the common case). Fall back to a 32-bit loop for the rare case where
> vcpu_info was registered at only 4-byte alignment. For compat guests
> (32-bit evtchn_pending_sel) the loop executes once. For native guests
> it executes a second iteration only if the high half has bits to
> deliver.
> 
> Fixes: 14243b387137 ("KVM: x86/xen: Add KVM_IRQ_ROUTING_XEN_EVTCHN and event channel delivery")
> Reported-by: sashiko-bot@kernel.org
> Assisted-by: Kiro:claude-opus-4.6-1m
> Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>
> ---
>  arch/x86/kvm/xen.c | 63 ++++++++++++++++++++++++++++++++--------------
>  1 file changed, 44 insertions(+), 19 deletions(-)
> 
> diff --git a/arch/x86/kvm/xen.c b/arch/x86/kvm/xen.c
> index 24e939ef5d64..e7b0263d5143 100644
> --- a/arch/x86/kvm/xen.c
> +++ b/arch/x86/kvm/xen.c
> @@ -638,11 +638,17 @@ void kvm_xen_inject_vcpu_vector(struct kvm_vcpu *v)
>   */
>  void kvm_xen_inject_pending_events(struct kvm_vcpu *v)
>  {
> -	unsigned long evtchn_pending_sel = READ_ONCE(v->arch.xen.evtchn_pending_sel);
>  	struct gfn_to_pfn_cache *gpc = &v->arch.xen.vcpu_info_cache;
> +	bool has_64bit_shinfo = kvm_xen_has_64bit_shinfo(v->kvm);
> +	union evtchn_pending_sel {
> +		u64 sel64;
> +		u32 sel32[2];
> +	} pending, *sel_addr;
> +	struct vcpu_info *vi;
>  	unsigned long flags;
>  
> -	if (!evtchn_pending_sel)
> +	pending.sel64 = READ_ONCE(v->arch.xen.evtchn_pending_sel);
> +	if (!pending.sel64)
>  		return;
>  
>  	/*
> @@ -661,31 +667,50 @@ void kvm_xen_inject_pending_events(struct kvm_vcpu *v)
>  	}
>  
>  	/* Now gpc->khva is a valid kernel address for the vcpu_info */
> -	if (kvm_xen_has_64bit_shinfo(v->kvm)) {
> -		struct vcpu_info *vi = gpc->khva;
> +	vi = gpc->khva;
> +	sel_addr = gpc->khva + (has_64bit_shinfo ?
> +				offsetof(struct vcpu_info, evtchn_pending_sel) :
> +				offsetof(struct compat_vcpu_info, evtchn_pending_sel));
>  
> +	if (has_64bit_shinfo && IS_ALIGNED((unsigned long)sel_addr, sizeof(u64))) {
> +		/*
> +		 * 64-bit shinfo with 8-byte aligned vcpu_info (the common
> +		 * case): use a single 64-bit atomic.

Nit, the "single 64-bit atomic" is confusing because there are obviously two
atomic operations in the assembly below.

> +		 */
>  		asm volatile(LOCK_PREFIX "orq %0, %1\n"
>  			     "notq %0\n"
>  			     LOCK_PREFIX "andq %0, %2\n"
> -			     : "=r" (evtchn_pending_sel),
> -			       "+m" (vi->evtchn_pending_sel),
> +			     : "=r" (pending.sel64),
> +			       "+m" (sel_addr->sel64),
>  			       "+m" (v->arch.xen.evtchn_pending_sel)
> -			     : "0" (evtchn_pending_sel));
> -		WRITE_ONCE(vi->evtchn_upcall_pending, 1);
> +			     : "0" (pending.sel64));
>  	} else {
> -		u32 evtchn_pending_sel32 = evtchn_pending_sel;
> -		struct compat_vcpu_info *vi = gpc->khva;
> -
> -		asm volatile(LOCK_PREFIX "orl %0, %1\n"
> -			     "notl %0\n"
> -			     LOCK_PREFIX "andl %0, %2\n"
> -			     : "=r" (evtchn_pending_sel32),
> -			       "+m" (vi->evtchn_pending_sel),
> -			       "+m" (v->arch.xen.evtchn_pending_sel)
> -			     : "0" (evtchn_pending_sel32));
> -		WRITE_ONCE(vi->evtchn_upcall_pending, 1);
> +		/*
> +		 * Use 32-bit operations to avoid splitlock on a vcpu_info
> +		 * that is only 4-byte aligned (registered in 32-bit mode).
> +		 * The loop copes with the extremely rare case that the
> +		 * vcpu_info was registered in 32-bit mode and only enforced
> +		 * 4-byte alignment, and then the VM was latched to 64-bit
> +		 * mode afterwards. Which Xen tolerates, so so should KVM.
> +		 */
> +		int i = 0;
> +		do {
> +			asm volatile(LOCK_PREFIX "orl %0, %1\n"
> +				     "notl %0\n"
> +				     LOCK_PREFIX "andl %0, %2\n"
> +				     : "=r" (pending.sel32[i]),
> +				       "+m" (sel_addr->sel32[i]),
> +				       "+m" (((u32 *)&v->arch.xen.evtchn_pending_sel)[i])
> +				     : "0" (pending.sel32[i]));
> +			i++;
> +		} while (has_64bit_shinfo && i < 2 && pending.sel32[i]);

This is... impressive?  Related to the above comment about there being two separate
atomic operation, only the access to vi->evtchn_pending_sel needs to deal with
potential split-lock issues.  And there's zero to handle the NOT in the asm blob.

Rather than munge the 32-bit and 64-bit cases together, just manually handle the
case where the bitwise-OR needs to be chunked in two.

--
From: Sean Christopherson <seanjc@google.com>
Date: Wed, 8 Jul 2026 08:09:19 -0700
Subject: [PATCH] KVM: x86/xen: Use 32-bit atomics if vCPU's evtchn_pending_sel
 isn't aligned

When propagating pending Xen events from KVM's "cache" to the guest-visible
structure, use two 32-bit atomic operations to do the bitwise-OR into the
guest-controlled structure if the structure isn't 64-bit aligned, i.e. if
the guest registered its vcpu_info in 32-bit mode and then switched to
64-bit mode, in which case using a 64-bit atomic OR will generate a
split-lock #AC (if enabled).

Opportunistically isolate the clearing of the bits from KVM's cache, as
that structure is KVM-controlled, i.e. is guaranteed to be 64-bit aligned.
This will allow dropping the open-coded inline asm blobs in the future.

Fixes: 14243b387137 ("KVM: x86/xen: Add KVM_IRQ_ROUTING_XEN_EVTCHN and event channel delivery")
Signed-off-by: Sean Christopherson <seanjc@google.com>
---
 arch/x86/kvm/xen.c | 22 +++++++++++++++-------
 1 file changed, 15 insertions(+), 7 deletions(-)

diff --git a/arch/x86/kvm/xen.c b/arch/x86/kvm/xen.c
index 24e939ef5d64..7a7f90710847 100644
--- a/arch/x86/kvm/xen.c
+++ b/arch/x86/kvm/xen.c
@@ -664,13 +664,21 @@ void kvm_xen_inject_pending_events(struct kvm_vcpu *v)
 	if (kvm_xen_has_64bit_shinfo(v->kvm)) {
 		struct vcpu_info *vi = gpc->khva;
 
-		asm volatile(LOCK_PREFIX "orq %0, %1\n"
-			     "notq %0\n"
-			     LOCK_PREFIX "andq %0, %2\n"
-			     : "=r" (evtchn_pending_sel),
-			       "+m" (vi->evtchn_pending_sel),
-			       "+m" (v->arch.xen.evtchn_pending_sel)
-			     : "0" (evtchn_pending_sel));
+		if (IS_ALIGNED((unsigned long)&vi->evtchn_pending_sel, sizeof(u64)))
+			asm volatile(LOCK_PREFIX "orq %[src], %[dst]\n"
+				     : [dst] "+m" (vi->evtchn_pending_sel)
+				     : [src] "r" (evtchn_pending_sel));
+		else
+			asm volatile(LOCK_PREFIX "orl %[src_lo], %[dst_lo]\n"
+				     LOCK_PREFIX "orl %[src_hi], %[dst_hi]\n"
+				     : [dst_lo] "+m" (vi->evtchn_pending_sel),
+				       [dst_hi] "+m" (*(((u32 *)&vi->evtchn_pending_sel) + 1))
+				     : [src_lo] "r" ((u32)evtchn_pending_sel),
+				       [src_hi] "r" ((u32)(evtchn_pending_sel >> 32)));
+
+		asm volatile(LOCK_PREFIX "andq %1, %0\n"
+			     : "+m" (v->arch.xen.evtchn_pending_sel)
+			     : "r" (~evtchn_pending_sel));
 		WRITE_ONCE(vi->evtchn_upcall_pending, 1);
 	} else {
 		u32 evtchn_pending_sel32 = evtchn_pending_sel;

base-commit: 0c393754b28263323bed3ac091744ff8456c97d0
-- 

And then as a follow-up, drop the inline asm:

--
From: Sean Christopherson <seanjc@google.com>
Date: Wed, 8 Jul 2026 08:16:22 -0700
Subject: [PATCH] KVM: x86/xen: Use atomic*() APIs instead of open coded
 equivalents

Replace the open coded atomic asm blobs in the Xen event injection code
with equivalent atomic{,64}_xxx() operations.  Casting the event channel
to atomic types is ugly, but not as ugly as asm blobs.

No functional change intended.

Signed-off-by: Sean Christopherson <seanjc@google.com>
---
 arch/x86/kvm/xen.c | 32 ++++++++++----------------------
 1 file changed, 10 insertions(+), 22 deletions(-)

diff --git a/arch/x86/kvm/xen.c b/arch/x86/kvm/xen.c
index 7a7f90710847..94d1644ca6d1 100644
--- a/arch/x86/kvm/xen.c
+++ b/arch/x86/kvm/xen.c
@@ -663,34 +663,22 @@ void kvm_xen_inject_pending_events(struct kvm_vcpu *v)
 	/* Now gpc->khva is a valid kernel address for the vcpu_info */
 	if (kvm_xen_has_64bit_shinfo(v->kvm)) {
 		struct vcpu_info *vi = gpc->khva;
+		void *vi_pending_sel = &vi->evtchn_pending_sel;
 
-		if (IS_ALIGNED((unsigned long)&vi->evtchn_pending_sel, sizeof(u64)))
-			asm volatile(LOCK_PREFIX "orq %[src], %[dst]\n"
-				     : [dst] "+m" (vi->evtchn_pending_sel)
-				     : [src] "r" (evtchn_pending_sel));
-		else
-			asm volatile(LOCK_PREFIX "orl %[src_lo], %[dst_lo]\n"
-				     LOCK_PREFIX "orl %[src_hi], %[dst_hi]\n"
-				     : [dst_lo] "+m" (vi->evtchn_pending_sel),
-				       [dst_hi] "+m" (*(((u32 *)&vi->evtchn_pending_sel) + 1))
-				     : [src_lo] "r" ((u32)evtchn_pending_sel),
-				       [src_hi] "r" ((u32)(evtchn_pending_sel >> 32)));
+		if (IS_ALIGNED((unsigned long)vi_pending_sel, sizeof(u64))) {
+			atomic64_or(evtchn_pending_sel, vi_pending_sel);
+		} else {
+			atomic_or(evtchn_pending_sel, vi_pending_sel);
+			atomic_or(evtchn_pending_sel >> 32, vi_pending_sel + 4);
+		}
 
-		asm volatile(LOCK_PREFIX "andq %1, %0\n"
-			     : "+m" (v->arch.xen.evtchn_pending_sel)
-			     : "r" (~evtchn_pending_sel));
+		atomic64_andnot(evtchn_pending_sel, (void *)&v->arch.xen.evtchn_pending_sel);
 		WRITE_ONCE(vi->evtchn_upcall_pending, 1);
 	} else {
-		u32 evtchn_pending_sel32 = evtchn_pending_sel;
 		struct compat_vcpu_info *vi = gpc->khva;
 
-		asm volatile(LOCK_PREFIX "orl %0, %1\n"
-			     "notl %0\n"
-			     LOCK_PREFIX "andl %0, %2\n"
-			     : "=r" (evtchn_pending_sel32),
-			       "+m" (vi->evtchn_pending_sel),
-			       "+m" (v->arch.xen.evtchn_pending_sel)
-			     : "0" (evtchn_pending_sel32));
+		atomic_or(evtchn_pending_sel, (void *)&vi->evtchn_pending_sel);
+		atomic_andnot(evtchn_pending_sel, (void *)&v->arch.xen.evtchn_pending_sel);
 		WRITE_ONCE(vi->evtchn_upcall_pending, 1);
 	}
 

base-commit: f7a8319462668aa415333f2853b70eb82ea17f34
--

  reply	other threads:[~2026-07-08 15:23 UTC|newest]

Thread overview: 16+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-06-05 14:17 [PATCH 0/8] KVM: x86/xen: Clean up 32-bit vs. 64-bit shared info mode handling David Woodhouse
2026-06-05 14:17 ` [PATCH 1/8] KVM: x86/xen: Rename 'longmode' to 'is_64bit' in hypercall handling David Woodhouse
2026-06-05 14:17 ` [PATCH 2/8] KVM: x86/xen: Introduce kvm_xen_has_64bit_shinfo() macro David Woodhouse
2026-06-06  9:30   ` David Laight
2026-06-06  9:35     ` David Woodhouse
2026-06-06 11:11       ` David Laight
2026-06-06 11:22         ` David Woodhouse
2026-06-05 14:17 ` [PATCH 3/8] KVM: x86/xen: Rename max_evtchn_port() to kvm_max_evtchn_port() David Woodhouse
2026-06-05 14:17 ` [PATCH 4/8] KVM: x86/xen: Latch shinfo mode in kvm_xen_set_evtchn_fast() David Woodhouse
2026-06-05 14:17 ` [PATCH 5/8] KVM: x86/xen: Latch shinfo mode in kvm_xen_schedop_poll() David Woodhouse
2026-06-05 14:17 ` [PATCH 6/8] KVM: x86/xen: Enforce alignment of vcpu_info registration David Woodhouse
2026-06-05 14:17 ` [PATCH 7/8] KVM: x86/xen: Use 32-bit locked bts for vcpu_info evtchn_pending_sel David Woodhouse
2026-07-08 15:48   ` Sean Christopherson
2026-06-05 14:17 ` [PATCH 8/8] KVM: x86/xen: Use 32-bit locked ops in kvm_xen_inject_pending_events() David Woodhouse
2026-07-08 15:23   ` Sean Christopherson [this message]
2026-07-08 15:41     ` David Woodhouse

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=ak5reLzVpGuVJtEH@google.com \
    --to=seanjc@google.com \
    --cc=bp@alien8.de \
    --cc=dave.hansen@linux.intel.com \
    --cc=dwmw2@infradead.org \
    --cc=hpa@zytor.com \
    --cc=imv4bel@gmail.com \
    --cc=kvm@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mingo@redhat.com \
    --cc=paul@xen.org \
    --cc=pbonzini@redhat.com \
    --cc=tglx@kernel.org \
    --cc=x86@kernel.org \
    /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