When running a PREEMPT_RT kernel, an attempt to acquire `gpc->lock` via
`read_lock_irqsave()` in `kvm_xen_set_evtchn_fast()` can lead to a
"sleeping function called from invalid context" BUG.
This happens because `gpc->lock` is an `rwlock_t`, which is mapped to a
sleeping lock (`rt_rw_lock`) on PREEMPT_RT. However,
`kvm_xen_set_evtchn_fast()` can be called from hardirq or atomic contexts,
such as `xen_timer_callback()` (an hrtimer callback) or
`kvm_arch_set_irq_inatomic()` (during irqfd waitqueue wakeups).
BUG: sleeping function called from invalid context at
kernel/locking/spinlock_rt.c:248
in_atomic(): 1, irqs_disabled(): 1, non_block: 0, pid: 5685, name:
kworker/0:5
...
Call Trace:
dump_stack_lvl+0xe8/0x150 lib/dump_stack.c:120
__might_resched+0x329/0x480 kernel/sched/core.c:9197
rt_read_lock+0xa9/0x4b0 kernel/locking/spinlock_rt.c:248
kvm_xen_set_evtchn_fast+0x1f4/0x990 arch/x86/kvm/xen.c:1822
xen_timer_callback+0x109/0x220 arch/x86/kvm/xen.c:140
__run_hrtimer kernel/time/hrtimer.c:2032 [inline]
__hrtimer_run_queues+0x3a0/0xaf0 kernel/time/hrtimer.c:2096
hrtimer_interrupt+0x44a/0x900 kernel/time/hrtimer.c:2215
local_apic_timer_interrupt arch/x86/kernel/apic/apic.c:1051 [inline]
__sysvec_apic_timer_interrupt+0x102/0x430 arch/x86/kernel/apic/apic.c:1068
sysvec_apic_timer_interrupt+0xa1/0xc0 arch/x86/kernel/apic/apic.c:1062
To fix this, modify `kvm_xen_set_evtchn_fast()` to accept a `bool
in_atomic` parameter. When called from an atomic context, use
`read_trylock()` instead of `read_lock_irqsave()`. If the trylock fails,
safely fall back to the slow path (returning `-EWOULDBLOCK` or kicking the
vCPU), which the callers already expect and handle. Update all callers to
pass the appropriate context flag.
Fixes: 14243b387137 ("KVM: x86/xen: Add KVM_IRQ_ROUTING_XEN_EVTCHN and event channel delivery")
Assisted-by: Gemini:gemini-3.5-flash Gemini:gemini-3.1-pro-preview syzbot
Reported-by: syzbot+e42793f1299e53beb2ee@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=e42793f1299e53beb2ee
Link: https://syzkaller.appspot.com/ai_job?id=f4bc5243-4ceb-4399-a79c-4c7ff19fbe0f
To: "Borislav Petkov"
To: "Dave Hansen"
To: "David Woodhouse"
To:
To: "Ingo Molnar"
To: "Paul Durrant"
To: "Paolo Bonzini"
To: "Sean Christopherson"
To: "Thomas Gleixner"
To:
To: "David Woodhouse"
Cc: "H. Peter Anvin"
Cc:
---
diff --git a/arch/x86/kvm/irq.c b/arch/x86/kvm/irq.c
index cb8ac4b9b..9aef11529 100644
--- a/arch/x86/kvm/irq.c
+++ b/arch/x86/kvm/irq.c
@@ -267,7 +267,7 @@ int kvm_arch_set_irq_inatomic(struct kvm_kernel_irq_routing_entry *e,
if (!level)
return -1;
- return kvm_xen_set_evtchn_fast(&e->xen_evtchn, kvm);
+ return kvm_xen_set_evtchn_fast(&e->xen_evtchn, kvm, true);
#endif
default:
break;
diff --git a/arch/x86/kvm/xen.c b/arch/x86/kvm/xen.c
index 694b31c1f..14988ae71 100644
--- a/arch/x86/kvm/xen.c
+++ b/arch/x86/kvm/xen.c
@@ -137,7 +137,7 @@ static enum hrtimer_restart xen_timer_callback(struct hrtimer *timer)
e.port = vcpu->arch.xen.timer_virq;
e.priority = KVM_IRQ_ROUTING_XEN_EVTCHN_PRIO_2LEVEL;
- rc = kvm_xen_set_evtchn_fast(&e, vcpu->kvm);
+ rc = kvm_xen_set_evtchn_fast(&e, vcpu->kvm, true);
if (rc != -EWOULDBLOCK) {
vcpu->arch.xen.timer_expires = 0;
return HRTIMER_NORESTART;
@@ -1792,7 +1792,7 @@ static void kvm_xen_check_poller(struct kvm_vcpu *vcpu, int port)
* It is also called directly from kvm_arch_set_irq_inatomic(), where the
* only check on its return value is a comparison with -EWOULDBLOCK'.
*/
-int kvm_xen_set_evtchn_fast(struct kvm_xen_evtchn *xe, struct kvm *kvm)
+int kvm_xen_set_evtchn_fast(struct kvm_xen_evtchn *xe, struct kvm *kvm, bool in_atomic)
{
struct gfn_to_pfn_cache *gpc = &kvm->arch.xen.shinfo_cache;
struct kvm_vcpu *vcpu;
@@ -1819,7 +1819,15 @@ int kvm_xen_set_evtchn_fast(struct kvm_xen_evtchn *xe, struct kvm *kvm)
idx = srcu_read_lock(&kvm->srcu);
- read_lock_irqsave(&gpc->lock, flags);
+ if (in_atomic) {
+ local_irq_save(flags);
+ if (!read_trylock(&gpc->lock)) {
+ local_irq_restore(flags);
+ goto out_rcu_no_lock;
+ }
+ } else {
+ read_lock_irqsave(&gpc->lock, flags);
+ }
if (!kvm_gpc_check(gpc, PAGE_SIZE))
goto out_rcu;
@@ -1853,7 +1861,18 @@ int kvm_xen_set_evtchn_fast(struct kvm_xen_evtchn *xe, struct kvm *kvm)
read_unlock_irqrestore(&gpc->lock, flags);
gpc = &vcpu->arch.xen.vcpu_info_cache;
- read_lock_irqsave(&gpc->lock, flags);
+ if (in_atomic) {
+ local_irq_save(flags);
+ if (!read_trylock(&gpc->lock)) {
+ local_irq_restore(flags);
+ if (!test_and_set_bit(port_word_bit,
+ &vcpu->arch.xen.evtchn_pending_sel))
+ kick_vcpu = true;
+ goto out_rcu_no_lock;
+ }
+ } else {
+ read_lock_irqsave(&gpc->lock, flags);
+ }
if (!kvm_gpc_check(gpc, sizeof(struct vcpu_info))) {
/*
* Could not access the vcpu_info. Set the bit in-kernel
@@ -1888,6 +1907,7 @@ int kvm_xen_set_evtchn_fast(struct kvm_xen_evtchn *xe, struct kvm *kvm)
out_rcu:
read_unlock_irqrestore(&gpc->lock, flags);
+ out_rcu_no_lock:
srcu_read_unlock(&kvm->srcu, idx);
if (kick_vcpu) {
@@ -1903,7 +1923,7 @@ static int kvm_xen_set_evtchn(struct kvm_xen_evtchn *xe, struct kvm *kvm)
bool mm_borrowed = false;
int rc;
- rc = kvm_xen_set_evtchn_fast(xe, kvm);
+ rc = kvm_xen_set_evtchn_fast(xe, kvm, false);
if (rc != -EWOULDBLOCK)
return rc;
@@ -1937,7 +1957,7 @@ static int kvm_xen_set_evtchn(struct kvm_xen_evtchn *xe, struct kvm *kvm)
struct gfn_to_pfn_cache *gpc = &kvm->arch.xen.shinfo_cache;
int idx;
- rc = kvm_xen_set_evtchn_fast(xe, kvm);
+ rc = kvm_xen_set_evtchn_fast(xe, kvm, false);
if (rc != -EWOULDBLOCK)
break;
diff --git a/arch/x86/kvm/xen.h b/arch/x86/kvm/xen.h
index 59e6128a7..13d34a06f 100644
--- a/arch/x86/kvm/xen.h
+++ b/arch/x86/kvm/xen.h
@@ -32,7 +32,7 @@ void kvm_xen_destroy_vm(struct kvm *kvm);
void kvm_xen_init_vcpu(struct kvm_vcpu *vcpu);
void kvm_xen_destroy_vcpu(struct kvm_vcpu *vcpu);
int kvm_xen_set_evtchn_fast(struct kvm_xen_evtchn *xe,
- struct kvm *kvm);
+ struct kvm *kvm, bool in_atomic);
int kvm_xen_setup_evtchn(struct kvm *kvm,
struct kvm_kernel_irq_routing_entry *e,
const struct kvm_irq_routing_entry *ue);
base-commit: db2ddb87143519e20a95aa36c60b36107b736a58
--
This is an AI-generated patch subject to moderation.
Reply with '#syz upstream' to Sign-off the patch as a human author
and send it to the upstream kernel mailing lists.
Reply with '#syz reject' to reject it ('#syz unreject' to undo).
See https://goo.gle/syzbot-ai-patches for information about AI-generated patches.
You can comment on the patch as usual, syzbot will try to address
the comments and send a new version of the patch if necessary.
syzbot engineers can be reached at syzkaller@googlegroups.com.