Re: [PATCH v2 02/20] KVM: x86/xen: Use read_trylock() for GPC locks in hardirq/atomic paths
From: Hillf Danton
Date: Fri May 29 2026 - 19:28:39 EST
On Fri, 29 May 2026 09:50:56 -0700 Sean Christopherson wrote:
> From: David Woodhouse <dwmw@xxxxxxxxxxxx>
>
> kvm_xen_set_evtchn_fast() is called from hardirq context (timer
> callback, kvm_arch_set_irq_inatomic()). On PREEMPT_RT, rwlock_t is a
> sleeping lock, so read_lock_irqsave() cannot be used in this context.
>
> Switch to read_trylock() and return -EWOULDBLOCK on contention, which is
> the designed fallback — there is always a slow path for the case where
> the GPC is invalid and needs to be refreshed.
>
> Reported-by: syzbot+208f7f3e5f59c11aeb90@xxxxxxxxxxxxxxxxxxxxxxxxx
> Closes: https://syzkaller.appspot.com/bug?extid=208f7f3e5f59c11aeb90
> Fixes: 14243b387137 ("KVM: x86/xen: Add KVM_IRQ_ROUTING_XEN_EVTCHN and event channel delivery")
> Signed-off-by: David Woodhouse <dwmw@xxxxxxxxxxxx>
> Signed-off-by: Sean Christopherson <seanjc@xxxxxxxxxx>
> ---
> arch/x86/kvm/xen.c | 32 +++++++++++++++++++++++---------
> 1 file changed, 23 insertions(+), 9 deletions(-)
>
> diff --git a/arch/x86/kvm/xen.c b/arch/x86/kvm/xen.c
> index 91fd3673c09a..9bdb8e3cad58 100644
> --- a/arch/x86/kvm/xen.c
> +++ b/arch/x86/kvm/xen.c
> @@ -697,6 +697,7 @@ void kvm_xen_inject_pending_events(struct kvm_vcpu *v)
> int __kvm_xen_has_interrupt(struct kvm_vcpu *v)
> {
> struct gfn_to_pfn_cache *gpc = &v->arch.xen.vcpu_info_cache;
> + bool atomic = in_atomic() || !task_is_running(current);
> unsigned long flags;
> u8 rc = 0;
>
> @@ -713,7 +714,15 @@ int __kvm_xen_has_interrupt(struct kvm_vcpu *v)
> BUILD_BUG_ON(sizeof(rc) !=
> sizeof_field(struct compat_vcpu_info, evtchn_upcall_pending));
>
> - read_lock_irqsave(&gpc->lock, flags);
> + if (atomic) {
> + local_irq_save(flags);
> + if (!read_trylock(&gpc->lock)) {
> + local_irq_restore(flags);
> + return 1;
> + }
> + } else {
> + read_lock_irqsave(&gpc->lock, flags);
> + }
> while (!kvm_gpc_check(gpc, sizeof(struct vcpu_info))) {
> read_unlock_irqrestore(&gpc->lock, flags);
>
I suspect this works given
static __always_inline void read_unlock_irqrestore(rwlock_t *rwlock,
unsigned long flags)
__releases_shared(rwlock)
{
rt_read_unlock(rwlock);
}