[PATCH v3 4/7] KVM: x86/xen: Extract delivery of event to vCPU into a separate helper

From: Woodhouse, David

Date: Wed Aug 05 2026 - 15:57:50 EST


From: Sean Christopherson <seanjc@xxxxxxxxxx>

Move the marking of an event as pending in the target vCPU's vcpu_info
into a separate helper, __kvm_xen_set_evtchn_fast(), invoked after the
shared_info processing is complete and its RCU read-side critical
section has been exited. This makes both halves easier to read.

No functional change intended.

Signed-off-by: Sean Christopherson <seanjc@xxxxxxxxxx>
[dwmw2: rebased onto the RCU conversion of the GPC locking; the
read_trylock() failure path in the original no longer exists. The
caller's kvm->srcu section now extends across the helper call, since
kvm_gpc_check() on the vcpu_info cache consults the memslot
generation and the irqfd path enters holding only irq_srcu; in Sean's
series that was covered by a guard(srcu) spanning the whole function,
which this series does not carry.]
Signed-off-by: David Woodhouse <dwmw@xxxxxxxxxxxx>
Assisted-by: Claude:claude-mythos-5
---
arch/x86/kvm/xen.c | 104 ++++++++++++++++++++++++++++-----------------
1 file changed, 65 insertions(+), 39 deletions(-)

diff --git a/arch/x86/kvm/xen.c b/arch/x86/kvm/xen.c
index 6bcece2439f5..75fbe88c2102 100644
--- a/arch/x86/kvm/xen.c
+++ b/arch/x86/kvm/xen.c
@@ -1759,6 +1759,67 @@ static void kvm_xen_check_poller(struct kvm_vcpu *vcpu, int port)
}
}

+/* Called with kvm->srcu held, as kvm_gpc_check() consults the memslots. */
+static void __kvm_xen_set_evtchn_fast(struct kvm_vcpu *vcpu, int port_word_bit)
+{
+ struct gfn_to_pfn_cache *gpc = &vcpu->arch.xen.vcpu_info_cache;
+ bool kick_vcpu = false;
+
+ /* Now switch to the vCPU's vcpu_info to set the index and pending_sel */
+ rcu_read_lock();
+ if (!kvm_gpc_check(gpc, sizeof(struct vcpu_info))) {
+ /*
+ * Could not access the vcpu_info. Set the bit in-kernel and
+ * prod the vCPU to deliver it for itself.
+ */
+ if (!test_and_set_bit(port_word_bit, &vcpu->arch.xen.evtchn_pending_sel))
+ kick_vcpu = true;
+ goto out_unlock;
+ }
+
+ if (IS_ENABLED(CONFIG_64BIT) && vcpu->kvm->arch.xen.long_mode) {
+ struct vcpu_info *vcpu_info = gpc->khva;
+
+ if (!test_and_set_bit(port_word_bit, &vcpu_info->evtchn_pending_sel)) {
+ WRITE_ONCE(vcpu_info->evtchn_upcall_pending, 1);
+ kick_vcpu = true;
+ }
+ } else {
+ struct compat_vcpu_info *vcpu_info = gpc->khva;
+
+ if (!test_and_set_bit(port_word_bit,
+ (unsigned long *)&vcpu_info->evtchn_pending_sel)) {
+ WRITE_ONCE(vcpu_info->evtchn_upcall_pending, 1);
+ kick_vcpu = true;
+ }
+ }
+
+out_unlock:
+ rcu_read_unlock();
+
+ /*
+ * Deliver the upcall or kick the vCPU only after dropping the RCU
+ * read lock. Both paths end up in kvm_vcpu_kick(), and the MSI
+ * delivery also walks the APIC map and takes APIC locks; none of
+ * that wants to be nested inside the GPC read-side critical
+ * section, which must be no longer than the accesses to gpc->khva
+ * above. Invalidation waits for a grace period, so holding the
+ * read lock across the kick would extend how long a memory
+ * invalidation is blocked.
+ */
+ if (!kick_vcpu)
+ return;
+
+ /* For the per-vCPU lapic vector, deliver it as MSI. */
+ if (vcpu->arch.xen.upcall_vector) {
+ kvm_xen_inject_vcpu_vector(vcpu);
+ return;
+ }
+
+ kvm_make_request(KVM_REQ_UNBLOCK, vcpu);
+ kvm_vcpu_kick(vcpu);
+}
+
/*
* The return value from this function is propagated to kvm_set_irq() API,
* so it returns:
@@ -1775,7 +1836,6 @@ int kvm_xen_set_evtchn_fast(struct kvm_xen_evtchn *xe, struct kvm *kvm)
struct kvm_vcpu *vcpu;
unsigned long *pending_bits, *mask_bits;
int port_word_bit;
- bool kick_vcpu = false;
int vcpu_idx, idx, rc;

vcpu_idx = READ_ONCE(xe->vcpu_idx);
@@ -1825,49 +1885,15 @@ int kvm_xen_set_evtchn_fast(struct kvm_xen_evtchn *xe, struct kvm *kvm)
kvm_xen_check_poller(vcpu, xe->port);
} else {
rc = 1; /* Delivered to the bitmap in shared_info. */
- /* Now switch to the vCPU's vcpu_info to set the index and pending_sel */
- gpc = &vcpu->arch.xen.vcpu_info_cache;
-
- if (!kvm_gpc_check(gpc, sizeof(struct vcpu_info))) {
- /*
- * Could not access the vcpu_info. Set the bit in-kernel
- * and prod the vCPU to deliver it for itself.
- */
- if (!test_and_set_bit(port_word_bit, &vcpu->arch.xen.evtchn_pending_sel))
- kick_vcpu = true;
- goto out_rcu;
- }
-
- if (IS_ENABLED(CONFIG_64BIT) && kvm->arch.xen.long_mode) {
- struct vcpu_info *vcpu_info = gpc->khva;
- if (!test_and_set_bit(port_word_bit, &vcpu_info->evtchn_pending_sel)) {
- WRITE_ONCE(vcpu_info->evtchn_upcall_pending, 1);
- kick_vcpu = true;
- }
- } else {
- struct compat_vcpu_info *vcpu_info = gpc->khva;
- if (!test_and_set_bit(port_word_bit,
- (unsigned long *)&vcpu_info->evtchn_pending_sel)) {
- WRITE_ONCE(vcpu_info->evtchn_upcall_pending, 1);
- kick_vcpu = true;
- }
- }
-
- /* For the per-vCPU lapic vector, deliver it as MSI. */
- if (kick_vcpu && vcpu->arch.xen.upcall_vector) {
- kvm_xen_inject_vcpu_vector(vcpu);
- kick_vcpu = false;
- }
}

out_rcu:
rcu_read_unlock();
- srcu_read_unlock(&kvm->srcu, idx);

- if (kick_vcpu) {
- kvm_make_request(KVM_REQ_UNBLOCK, vcpu);
- kvm_vcpu_kick(vcpu);
- }
+ if (rc == 1)
+ __kvm_xen_set_evtchn_fast(vcpu, port_word_bit);
+
+ srcu_read_unlock(&kvm->srcu, idx);

return rc;
}
--
2.43.0

Attachment: smime.p7s
Description: S/MIME cryptographic signature




Amazon Development Centre (London) Ltd.Registered in England and Wales with registration number 04543232 with its registered office at 1 Principal Place, Worship Street, London EC2A 2FA, United Kingdom.