From: David Woodhouse <dwmw2@infradead.org>
To: kvm@vger.kernel.org
Cc: linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org,
Paolo Bonzini <pbonzini@redhat.com>,
Sean Christopherson <seanjc@google.com>,
Paul Durrant <paul@xen.org>,
Vitaly Kuznetsov <vkuznets@redhat.com>,
Fred Griffoul <fgriffo@amazon.co.uk>,
David Woodhouse <dwmw@amazon.co.uk>
Subject: [PATCH v4 06/12] KVM: x86/xen: Retry event channel delivery which races a memslot update
Date: Mon, 19 Oct 2026 17:24:37 +0100 [thread overview]
Message-ID: <20261019162445.37219-7-dwmw2@infradead.org> (raw)
In-Reply-To: <20261019162445.37219-1-dwmw2@infradead.org>
From: David Woodhouse <dwmw@amazon.co.uk>
Event channel delivery now returns -EAGAIN when it races a memslot
update. It always failed; now the failure is distinguishable, and
instead of dropping the event each caller reacts according to its
context:
- kvm_xen_hvm_evtchn_send(): the KVM_XEN_HVM_EVTCHN_SEND ioctl.
May sleep: passes wait == true, and kvm_gpc_refresh() rides the
update out.
- evtchn_set_fn(): irqfd routing, from the irqfd workqueue after
the atomic fast path failed. May also wait.
- kvm_xen_hcall_evtchn_send(): the guest's own EVTCHNOP_send, in
vcpu_run's exit handling under the vCPU's kvm->srcu read section
— the very reader the memslot update is waiting out, so waiting
there deadlocks, and any guest can trigger it. Instead, leave the
hypercall uncompleted, with RIP not advanced, and the guest
re-executes it; each attempt cycles the srcu read section on the
way back in, letting the update complete. This is how Xen itself
restarts preempted hypercalls, and the pending delivery is pure
guest state, so there is nothing extra to migrate.
- kvm_xen_inject_timer_irqs(): the timer retry added by the
previous commit — timer_pending *is* its pending state, so it
keeps that and retries on the next entry pass. The entry pass
holds the vCPU's kvm->srcu, so it may not wait. The
KVM_XEN_VCPU_GET_ATTR timer flush doesn't need to wait: an
undelivered event is reported as still owed in the snapshot.
Reported-by: sashiko-bot@kernel.org
Closes: https://lore.kernel.org/all/20260920215341.5ECE91F000FF@smtp.kernel.org
Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>
Assisted-by: LLM
---
arch/x86/kvm/xen.c | 58 +++++++++++++++++++++++++++++++---------------
1 file changed, 39 insertions(+), 19 deletions(-)
diff --git a/arch/x86/kvm/xen.c b/arch/x86/kvm/xen.c
index 8a9d6963fc05..c0b9ac674255 100644
--- a/arch/x86/kvm/xen.c
+++ b/arch/x86/kvm/xen.c
@@ -30,9 +30,10 @@
#include "cpuid.h"
#include "trace.h"
-static int kvm_xen_set_evtchn(struct kvm_xen_evtchn *xe, struct kvm *kvm);
+static int kvm_xen_set_evtchn(struct kvm_xen_evtchn *xe, struct kvm *kvm,
+ bool wait);
static int kvm_xen_setattr_evtchn(struct kvm *kvm, struct kvm_xen_hvm_attr *data);
-static bool kvm_xen_hcall_evtchn_send(struct kvm_vcpu *vcpu, u64 param, u64 *r);
+static int kvm_xen_hcall_evtchn_send(struct kvm_vcpu *vcpu, u64 param, u64 *r);
DEFINE_STATIC_KEY_DEFERRED_FALSE(kvm_xen_enabled, HZ);
@@ -146,7 +147,7 @@ void kvm_xen_inject_timer_irqs(struct kvm_vcpu *vcpu)
e.port = vcpu->arch.xen.timer_virq;
e.priority = KVM_IRQ_ROUTING_XEN_EVTCHN_PRIO_2LEVEL;
- rc = kvm_xen_set_evtchn(&e, vcpu->kvm);
+ rc = kvm_xen_set_evtchn(&e, vcpu->kvm, false);
/*
* Leave timer_pending set if the memslot is being updated
@@ -1900,8 +1901,21 @@ int kvm_xen_hypercall(struct kvm_vcpu *vcpu)
}
break;
case __HYPERVISOR_event_channel_op:
- if (params[0] == EVTCHNOP_send)
- handled = kvm_xen_hcall_evtchn_send(vcpu, params[1], &r);
+ if (params[0] == EVTCHNOP_send) {
+ int ret = kvm_xen_hcall_evtchn_send(vcpu, params[1], &r);
+
+ /*
+ * Delivery raced a memslot update; waiting for it
+ * here would deadlock under vcpu_run's kvm->srcu
+ * read section. Leave the hypercall uncompleted —
+ * RIP not advanced — so the guest re-executes it.
+ * Each attempt cycles the srcu read section on the
+ * way back in, letting the update complete.
+ */
+ if (ret == -EAGAIN)
+ return 1;
+ handled = !ret;
+ }
break;
case __HYPERVISOR_sched_op:
handled = kvm_xen_hcall_sched_op(vcpu, is_64bit, params[0],
@@ -2092,7 +2106,8 @@ int kvm_xen_set_evtchn_fast(struct kvm_xen_evtchn *xe, struct kvm *kvm)
return rc;
}
-static int kvm_xen_set_evtchn(struct kvm_xen_evtchn *xe, struct kvm *kvm)
+static int kvm_xen_set_evtchn(struct kvm_xen_evtchn *xe, struct kvm *kvm,
+ bool wait)
{
bool mm_borrowed = false;
int rc;
@@ -2129,16 +2144,13 @@ static int kvm_xen_set_evtchn(struct kvm_xen_evtchn *xe, struct kvm *kvm)
*/
do {
struct gfn_to_pfn_cache *gpc = &kvm->arch.xen.shinfo_cache;
- int idx;
rc = kvm_xen_set_evtchn_fast(xe, kvm);
if (rc != -EWOULDBLOCK)
break;
- idx = srcu_read_lock(&kvm->srcu);
- rc = kvm_gpc_refresh(gpc, PAGE_SIZE, false);
- srcu_read_unlock(&kvm->srcu, idx);
- } while(!rc);
+ rc = kvm_gpc_refresh(gpc, PAGE_SIZE, wait);
+ } while (!rc);
if (mm_borrowed)
kthread_unuse_mm(kvm->mm);
@@ -2153,7 +2165,7 @@ static int evtchn_set_fn(struct kvm_kernel_irq_routing_entry *e, struct kvm *kvm
if (!level)
return -EINVAL;
- return kvm_xen_set_evtchn(&e->xen_evtchn, kvm);
+ return kvm_xen_set_evtchn(&e->xen_evtchn, kvm, true);
}
/*
@@ -2227,7 +2239,7 @@ int kvm_xen_hvm_evtchn_send(struct kvm *kvm, struct kvm_irq_routing_xen_evtchn *
e.vcpu_idx = -1;
e.priority = uxe->priority;
- ret = kvm_xen_set_evtchn(&e, kvm);
+ ret = kvm_xen_set_evtchn(&e, kvm, true);
/*
* None of that 'return 1 if it actually got delivered' nonsense.
@@ -2456,7 +2468,11 @@ static int kvm_xen_setattr_evtchn(struct kvm *kvm, struct kvm_xen_hvm_attr *data
return kvm_xen_eventfd_assign(kvm, data);
}
-static bool kvm_xen_hcall_evtchn_send(struct kvm_vcpu *vcpu, u64 param, u64 *r)
+/*
+ * Returns 0 with *r set if handled, -EAGAIN if the hypercall should be
+ * re-executed by the guest, or -ENOENT to hand it to userspace.
+ */
+static int kvm_xen_hcall_evtchn_send(struct kvm_vcpu *vcpu, u64 param, u64 *r)
{
struct evtchnfd *evtchnfd;
struct evtchn_send send;
@@ -2466,7 +2482,7 @@ static bool kvm_xen_hcall_evtchn_send(struct kvm_vcpu *vcpu, u64 param, u64 *r)
BUILD_BUG_ON(sizeof(send) != 4);
if (kvm_read_guest_virt(vcpu, param, &send, sizeof(send), &e)) {
*r = -EFAULT;
- return true;
+ return 0;
}
/*
@@ -2475,18 +2491,22 @@ static bool kvm_xen_hcall_evtchn_send(struct kvm_vcpu *vcpu, u64 param, u64 *r)
*/
evtchnfd = xa_load(&vcpu->kvm->arch.xen.evtchn_ports, send.port);
if (!evtchnfd)
- return false;
+ return -ENOENT;
if (evtchnfd->deliver.port.port) {
- int ret = kvm_xen_set_evtchn(&evtchnfd->deliver.port, vcpu->kvm);
+ int ret = kvm_xen_set_evtchn(&evtchnfd->deliver.port,
+ vcpu->kvm, false);
+
+ if (ret == -EAGAIN)
+ return ret;
if (ret < 0 && ret != -ENOTCONN)
- return false;
+ return -ENOENT;
} else {
eventfd_signal(evtchnfd->deliver.eventfd.ctx);
}
*r = 0;
- return true;
+ return 0;
}
void kvm_xen_init_vcpu(struct kvm_vcpu *vcpu)
--
2.43.0
next prev parent reply other threads:[~2026-10-19 16:24 UTC|newest]
Thread overview: 16+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-19 16:24 [PATCH v4 00/12] KVM: x86/xen: Don't drop event deliveries which race memslot updates David Woodhouse
2026-10-19 16:24 ` [PATCH v4 01/12] KVM: x86/xen: Don't block memslot updates while halted in SCHEDOP_poll David Woodhouse
2026-10-19 16:24 ` [PATCH v4 02/12] KVM: pfncache: Return -EAGAIN for a lookup which hits an invalid memslot David Woodhouse
2026-10-19 16:24 ` [PATCH v4 03/12] KVM: pfncache: Add a 'wait' argument to the refresh and activate functions David Woodhouse
2026-10-19 16:24 ` [PATCH v4 04/12] KVM: x86/xen: Wait out memslot updates in the Xen attribute ioctls David Woodhouse
2026-10-19 16:24 ` [PATCH v4 05/12] KVM: x86/xen: Don't lose the timer event when delivery fails transiently David Woodhouse
2026-10-19 16:24 ` David Woodhouse [this message]
2026-10-19 16:24 ` [PATCH v4 07/12] KVM: x86/xen: Extract delivery of event to vCPU into a separate helper David Woodhouse
2026-10-19 16:24 ` [PATCH v4 08/12] KVM: x86/xen: Explicitly tag "shared info" page as never being dirty tracked David Woodhouse
2026-10-19 16:24 ` [PATCH v4 09/12] KVM: x86/xen: Don't dirty track "vCPU info" page David Woodhouse
2026-10-19 16:24 ` [PATCH v4 10/12] KVM: x86: Request the guest TLB flush from record_steal_time() David Woodhouse
2026-10-19 16:24 ` [PATCH v4 11/12] KVM: selftests: Add Xen evtchn vs memslot-update race test David Woodhouse
2026-10-19 16:24 ` [PATCH v4 12/12] KVM: selftests: Fix unreachable timer recovery in xen_shinfo_test David Woodhouse
2026-11-03 22:46 ` [PATCH v4 00/12] KVM: x86/xen: Don't drop event deliveries which race memslot updates David Woodhouse
2026-11-04 19:55 ` Sean Christopherson
2026-11-04 19:53 ` Sean Christopherson
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=20261019162445.37219-7-dwmw2@infradead.org \
--to=dwmw2@infradead.org \
--cc=dwmw@amazon.co.uk \
--cc=fgriffo@amazon.co.uk \
--cc=kvm@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-kselftest@vger.kernel.org \
--cc=paul@xen.org \
--cc=pbonzini@redhat.com \
--cc=seanjc@google.com \
--cc=vkuznets@redhat.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
all inboxes | Powered by JetHome®