| 0/0 |
2026/08/12 11:19 |
flow |
patching |
1h33m
Results: map[AckedBy:[] Fixes:map[AuthorEmail:dwmw@amazon.co.uk AuthorName:David Woodhouse Hash:14243b387137a4afbe1df5d9dc15182d6657bb79 Title:KVM: x86/xen: Add KVM_IRQ_ROUTING_XEN_EVTCHN and event channel delivery] KernelBranch:master KernelCommit:db2ddb87143519e20a95aa36c60b36107b736a58 KernelRepo:git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git PatchDescription:KVM: x86/xen: Avoid sleeping lock in kvm_xen_set_evtchn_fast()
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:
<IRQ>
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
</IRQ>
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. PatchDiff: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);
Recipients:[map[Email:bp@alien8.de Name:Borislav Petkov To:true] map[Email:dave.hansen@linux.intel.com Name:Dave Hansen To:true] map[Email:dwmw2@infradead.org Name:David Woodhouse To:true] map[Email:hpa@zytor.com Name:H. Peter Anvin To:false] map[Email:kvm@vger.kernel.org Name: To:true] map[Email:linux-kernel@vger.kernel.org Name: To:false] map[Email:mingo@redhat.com Name:Ingo Molnar To:true] map[Email:paul@xen.org Name:Paul Durrant To:true] map[Email:pbonzini@redhat.com Name:Paolo Bonzini To:true] map[Email:seanjc@google.com Name:Sean Christopherson To:true] map[Email:tglx@kernel.org Name:Thomas Gleixner To:true] map[Email:x86@kernel.org Name: To:true] map[Email:dwmw@amazon.co.uk Name:David Woodhouse To:true]] ReportedBy:[] ReviewedBy:[] SuggestedBy:[] TestedBy:[]]
|
| 1/1 |
2026/08/12 11:19 |
action |
base-commit-picker |
0m
Results: map[KernelBranch:master KernelCommit:db2ddb87143519e20a95aa36c60b36107b736a58 KernelRepo:git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git]
|
| 2/1 |
2026/08/12 11:19 |
action |
syz-repro-to-c-repro |
0m
Results: map[SimplifiedCRepro:// autogenerated by syzkaller (https://github.com/google/syzkaller)
#define _GNU_SOURCE
#include <endian.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mount.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>
uint64_t r[3] = {0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff};
int main(void)
{
syscall(__NR_mmap, /*addr=*/0x1ffffffff000ul, /*len=*/0x1000, /*prot=*/0ul, /*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/0x32ul, /*fd=*/(intptr_t)-1, /*offset=*/0ul);
syscall(__NR_mmap, /*addr=*/0x200000000000ul, /*len=*/0x1000000, /*prot=PROT_WRITE|PROT_READ|PROT_EXEC*/7ul, /*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/0x32ul, /*fd=*/(intptr_t)-1, /*offset=*/0ul);
syscall(__NR_mmap, /*addr=*/0x200001000000ul, /*len=*/0x1000, /*prot=*/0ul, /*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/0x32ul, /*fd=*/(intptr_t)-1, /*offset=*/0ul);
const char* reason;
(void)reason;
intptr_t res = 0;
if (write(1, "executing program\n", sizeof("executing program\n") - 1)) {}
// sendmsg$NFT_BATCH arguments: [
// fd: sock_nl_netfilter (resource)
// msg: ptr[in, msghdr_netlink[nft_batch_msg]] {
// msghdr_netlink[nft_batch_msg] {
// addr: nil
// addrlen: len = 0x0 (4 bytes)
// pad = 0x0 (4 bytes)
// vec: ptr[in, iovec[in, nft_batch_msg]] {
// iovec[in, nft_batch_msg] {
// addr: ptr[inout, array[ANYUNION]] {
// array[ANYUNION] {
// union ANYUNION {
// ANYBLOB: buffer: {14 00 00 00 10 00 01 00 00 00 00 00 00 00 00 00 00 00 00 0a} (length 0x14)
// }
// }
// }
// len: len = 0xd4 (8 bytes)
// }
// }
// vlen: const = 0x1 (8 bytes)
// ctrl: const = 0x0 (8 bytes)
// ctrllen: const = 0x0 (8 bytes)
// f: send_flags = 0x0 (4 bytes)
// pad = 0x0 (4 bytes)
// }
// }
// f: send_flags = 0x4001 (8 bytes)
// ]
*(uint64_t*)0x2000000000c0 = 0;
*(uint32_t*)0x2000000000c8 = 0;
*(uint64_t*)0x2000000000d0 = 0x200000000000;
*(uint64_t*)0x200000000000 = 0x200000000100;
memcpy((void*)0x200000000100, "\x14\x00\x00\x00\x10\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0a", 20);
*(uint64_t*)0x200000000008 = 0xd4;
*(uint64_t*)0x2000000000d8 = 1;
*(uint64_t*)0x2000000000e0 = 0;
*(uint64_t*)0x2000000000e8 = 0;
*(uint32_t*)0x2000000000f0 = 0;
syscall(__NR_sendmsg, /*fd=*/(intptr_t)-1, /*msg=*/0x2000000000c0ul, /*f=MSG_OOB|MSG_NOSIGNAL*/0x4001ul);
// openat$kvm arguments: [
// fd: const = 0xffffffffffffff9c (8 bytes)
// file: ptr[in, buffer] {
// buffer: {2f 64 65 76 2f 6b 76 6d 00} (length 0x9)
// }
// flags: open_flags = 0x0 (4 bytes)
// mode: const = 0x0 (2 bytes)
// ]
// returns fd_kvm
memcpy((void*)0x200000000000, "/dev/kvm\000", 9);
res = syscall(__NR_openat, /*fd=*/0xffffffffffffff9cul, /*file=*/0x200000000000ul, /*flags=*/0, /*mode=*/0);
if (res != -1)
r[0] = res;
// ioctl$KVM_CREATE_VM arguments: [
// fd: fd_kvm (resource)
// cmd: const = 0xae01 (4 bytes)
// type: intptr = 0x0 (8 bytes)
// ]
// returns fd_kvmvm
res = syscall(__NR_ioctl, /*fd=*/r[0], /*cmd=*/0xae01, /*type=*/0ul);
if (res != -1)
r[1] = res;
// ioctl$KVM_CREATE_VCPU arguments: [
// fd: fd_kvmvm (resource)
// cmd: const = 0xae41 (4 bytes)
// id: intptr = 0x0 (8 bytes)
// ]
// returns fd_kvmcpu
res = syscall(__NR_ioctl, /*fd=*/r[1], /*cmd=*/0xae41, /*id=*/0ul);
if (res != -1)
r[2] = res;
// ioctl arguments: [
// fd: fd (resource)
// cmd: int32 = 0x4048aecb (4 bytes)
// arg: ptr[in, buffer] {
// buffer: {07 00 00 00 00 00 00 00 01 00 00 00 ff ff ff ff} (length 0x10)
// }
// ]
memcpy((void*)0x200000000100, "\x07\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\xff\xff\xff\xff", 16);
syscall(__NR_ioctl, /*fd=*/r[2], /*cmd=*/0x4048aecb, /*arg=*/0x200000000100ul);
return 0;
}
]
|
| 3/1 |
2026/08/12 11:19 |
action |
kernel-checkouter |
0m
Results: map[KernelSrc:/app/workdir/cache/src/a1b57dc758c6c34333284131703ffa376c4cdbb7]
|
| 4/1 |
2026/08/12 11:19 |
action |
kernel-builder |
15m
Results: map[KernelObj:/app/workdir/cache/build/8bc68370983c4ec23fc9751c02b10669f0c2433f]
|
| 5/1 |
2026/08/12 11:34 |
action |
crash-reproducer |
9m
Results: map[OtherCrashReports:<nil> ReproducedBugTitle:BUG: sleeping function called from invalid context in kvm_xen_set_evtchn_fast ReproducedCrashReport: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
preempt_count: 10001, expected: 0
RCU nest depth: 0, expected: 0
1 lock held by kworker/0:5/5685:
#0: ffff8881964ddb48 (&kvm->srcu){.?.+}-{0:0}, at: srcu_lock_acquire include/linux/srcu.h:187 [inline]
#0: ffff8881964ddb48 (&kvm->srcu){.?.+}-{0:0}, at: srcu_read_lock include/linux/srcu.h:294 [inline]
#0: ffff8881964ddb48 (&kvm->srcu){.?.+}-{0:0}, at: kvm_xen_set_evtchn_fast+0x1be/0x990 arch/x86/kvm/xen.c:1820
irq event stamp: 78678
hardirqs last enabled at (78677): [<ffffffff8194da12>] raw_spin_rq_unlock_irq kernel/sched/sched.h:1673 [inline]
hardirqs last enabled at (78677): [<ffffffff8194da12>] finish_lock_switch kernel/sched/core.c:5241 [inline]
hardirqs last enabled at (78677): [<ffffffff8194da12>] finish_task_switch+0x412/0xc60 kernel/sched/core.c:5359
hardirqs last disabled at (78678): [<ffffffff8b4edc4e>] sysvec_apic_timer_interrupt+0xe/0xc0 arch/x86/kernel/apic/apic.c:1062
softirqs last enabled at (78668): [<ffffffff8187f8b0>] __local_bh_enable_ip+0x1a0/0x2b0 kernel/softirq.c:305
softirqs last disabled at (78666): [<ffffffff861be59f>] local_bh_disable include/linux/bottom_half.h:20 [inline]
softirqs last disabled at (78666): [<ffffffff861be59f>] rcu_read_lock_bh include/linux/rcupdate.h:893 [inline]
softirqs last disabled at (78666): [<ffffffff861be59f>] keep_key_fresh drivers/net/wireguard/send.c:129 [inline]
softirqs last disabled at (78666): [<ffffffff861be59f>] wg_packet_create_data_done drivers/net/wireguard/send.c:259 [inline]
softirqs last disabled at (78666): [<ffffffff861be59f>] wg_packet_tx_worker+0x25f/0x7e0 drivers/net/wireguard/send.c:276
Preemption disabled at:
[<ffffffff8b50149d>] __schedule_loop kernel/sched/core.c:7310 [inline]
[<ffffffff8b50149d>] schedule+0x15d/0x2b0 kernel/sched/core.c:7326
CPU: 0 UID: 0 PID: 5685 Comm: kworker/0:5 Not tainted syzkaller #1 PREEMPT_{RT,(full)}
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
Workqueue: 0x0 (wg-crypt-wg1)
Call Trace:
<IRQ>
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
instr_sysvec_apic_timer_interrupt arch/x86/kernel/apic/apic.c:1062 [inline]
sysvec_apic_timer_interrupt+0xa1/0xc0 arch/x86/kernel/apic/apic.c:1062
</IRQ>
<TASK>
asm_sysvec_apic_timer_interrupt+0x1a/0x20 arch/x86/include/asm/idtentry.h:674
RIP: 0010:finish_task_switch+0x417/0xc60 kernel/sched/core.c:5361
Code: 04 00 00 41 c7 84 24 20 0e 00 00 00 00 00 00 0f 1f 44 00 00 49 83 c4 48 4c 89 e7 e8 b3 99 bd 09 e8 de c7 38 00 fb 4c 8b 65 c8 <49> 8d bc 24 e0 16 00 00 48 89 f8 48 c1 e8 03 42 0f b6 04 30 84 c0
RSP: 0018:ffffc9000442fa60 EFLAGS: 00000206
RAX: 0000000000013355 RBX: ffff88813883c6e0 RCX: 0000000080000001
RDX: 0000000000000000 RSI: ffffffff8daa4fe1 RDI: ffffffff8bd1a580
RBP: ffffc9000442fab0 R08: ffffffff8fd9f0f7 R09: 1ffffffff1fb3e1e
R10: dffffc0000000000 R11: fffffbfff1fb3e1f R12: ffff888114048000
R13: ffff88813883c6a8 R14: dffffc0000000000 R15: 1ffff110271078dc
context_switch kernel/sched/core.c:5513 [inline]
__schedule+0x1711/0x5530 kernel/sched/core.c:7234
__schedule_loop kernel/sched/core.c:7311 [inline]
schedule+0x164/0x2b0 kernel/sched/core.c:7326
worker_thread+0xc12/0xfb0 kernel/workqueue.c:3501
kthread+0x388/0x470 kernel/kthread.c:436
ret_from_fork+0x514/0xb70 arch/x86/kernel/process.c:158
ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245
</TASK>
=============================
[ BUG: Invalid wait context ]
syzkaller #1 Tainted: G W
-----------------------------
kworker/0:5/5685 is trying to lock:
ffff8881964dd530 (&gpc->lock){....}-{3:3}, at: kvm_xen_set_evtchn_fast+0x1f4/0x990 arch/x86/kvm/xen.c:1822
other info that might help us debug this:
context-{2:2}
1 lock held by kworker/0:5/5685:
#0: ffff8881964ddb48 (&kvm->srcu){.?.+}-{0:0}, at: srcu_lock_acquire include/linux/srcu.h:187 [inline]
#0: ffff8881964ddb48 (&kvm->srcu){.?.+}-{0:0}, at: srcu_read_lock include/linux/srcu.h:294 [inline]
#0: ffff8881964ddb48 (&kvm->srcu){.?.+}-{0:0}, at: kvm_xen_set_evtchn_fast+0x1be/0x990 arch/x86/kvm/xen.c:1820
stack backtrace:
CPU: 0 UID: 0 PID: 5685 Comm: kworker/0:5 Tainted: G W syzkaller #1 PREEMPT_{RT,(full)}
Tainted: [W]=WARN
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
Workqueue: 0x0 (wg-crypt-wg1)
Call Trace:
<IRQ>
dump_stack_lvl+0xe8/0x150 lib/dump_stack.c:120
print_lock_invalid_wait_context kernel/locking/lockdep.c:4830 [inline]
check_wait_context kernel/locking/lockdep.c:4902 [inline]
__lock_acquire+0xe39/0x2ce0 kernel/locking/lockdep.c:5187
lock_acquire+0x106/0x350 kernel/locking/lockdep.c:5868
rt_read_lock+0xcc/0x4b0 kernel/locking/spinlock_rt.c:249
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
instr_sysvec_apic_timer_interrupt arch/x86/kernel/apic/apic.c:1062 [inline]
sysvec_apic_timer_interrupt+0xa1/0xc0 arch/x86/kernel/apic/apic.c:1062
</IRQ>
<TASK>
asm_sysvec_apic_timer_interrupt+0x1a/0x20 arch/x86/include/asm/idtentry.h:674
RIP: 0010:finish_task_switch+0x417/0xc60 kernel/sched/core.c:5361
Code: 04 00 00 41 c7 84 24 20 0e 00 00 00 00 00 00 0f 1f 44 00 00 49 83 c4 48 4c 89 e7 e8 b3 99 bd 09 e8 de c7 38 00 fb 4c 8b 65 c8 <49> 8d bc 24 e0 16 00 00 48 89 f8 48 c1 e8 03 42 0f b6 04 30 84 c0
RSP: 0018:ffffc9000442fa60 EFLAGS: 00000206
RAX: 0000000000013355 RBX: ffff88813883c6e0 RCX: 0000000080000001
RDX: 0000000000000000 RSI: ffffffff8daa4fe1 RDI: ffffffff8bd1a580
RBP: ffffc9000442fab0 R08: ffffffff8fd9f0f7 R09: 1ffffffff1fb3e1e
R10: dffffc0000000000 R11: fffffbfff1fb3e1f R12: ffff888114048000
R13: ffff88813883c6a8 R14: dffffc0000000000 R15: 1ffff110271078dc
context_switch kernel/sched/core.c:5513 [inline]
__schedule+0x1711/0x5530 kernel/sched/core.c:7234
__schedule_loop kernel/sched/core.c:7311 [inline]
schedule+0x164/0x2b0 kernel/sched/core.c:7326
worker_thread+0xc12/0xfb0 kernel/workqueue.c:3501
kthread+0x388/0x470 kernel/kthread.c:436
ret_from_fork+0x514/0xb70 arch/x86/kernel/process.c:158
ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245
</TASK>
----------------
Code disassembly (best guess), 1 bytes skipped:
0: 00 00 add %al,(%rax)
2: 41 c7 84 24 20 0e 00 movl $0x0,0xe20(%r12)
9: 00 00 00 00 00
e: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)
13: 49 83 c4 48 add $0x48,%r12
17: 4c 89 e7 mov %r12,%rdi
1a: e8 b3 99 bd 09 call 0x9bd99d2
1f: e8 de c7 38 00 call 0x38c802
24: fb sti
25: 4c 8b 65 c8 mov -0x38(%rbp),%r12
* 29: 49 8d bc 24 e0 16 00 lea 0x16e0(%r12),%rdi <-- trapping instruction
30: 00
31: 48 89 f8 mov %rdi,%rax
34: 48 c1 e8 03 shr $0x3,%rax
38: 42 0f b6 04 30 movzbl (%rax,%r14,1),%eax
3d: 84 c0 test %al,%al
ReproducedFaultInjection:]
|
| 6/1 |
2026/08/12 11:44 |
action |
codesearch-prepare |
9m
Results: map[Index:codesearch-index]
|
| 7/1 |
2026/08/12 11:53 |
agent |
debugger |
5m
Model:
Instruction:
You are an experienced Linux kernel developer tasked with debugging a kernel crash root cause.
You need to provide a detailed explanation of the root cause for another developer to be
able to write a fix for the bug based on your explanation. Include all relevant details
into the response: function/struct/field/etc names, code snippets, line numbers,
macro/enum values, etc.
Don't make assumptions about the kernel source code (it may be different from what you assume it is).
Extensively use the provided code access tools (codesearch-*, git-*, grepper, etc)
to examine the actual source code, and confirm any assumptions.
Prefer calling several tools at the same time to save round-trips.
Prompt:
The crash is:
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
preempt_count: 10001, expected: 0
RCU nest depth: 0, expected: 0
1 lock held by kworker/0:5/5685:
#0: ffff8881964ddb48 (&kvm->srcu){.?.+}-{0:0}, at: srcu_lock_acquire include/linux/srcu.h:187 [inline]
#0: ffff8881964ddb48 (&kvm->srcu){.?.+}-{0:0}, at: srcu_read_lock include/linux/srcu.h:294 [inline]
#0: ffff8881964ddb48 (&kvm->srcu){.?.+}-{0:0}, at: kvm_xen_set_evtchn_fast+0x1be/0x990 arch/x86/kvm/xen.c:1820
irq event stamp: 78678
hardirqs last enabled at (78677): [<ffffffff8194da12>] raw_spin_rq_unlock_irq kernel/sched/sched.h:1673 [inline]
hardirqs last enabled at (78677): [<ffffffff8194da12>] finish_lock_switch kernel/sched/core.c:5241 [inline]
hardirqs last enabled at (78677): [<ffffffff8194da12>] finish_task_switch+0x412/0xc60 kernel/sched/core.c:5359
hardirqs last disabled at (78678): [<ffffffff8b4edc4e>] sysvec_apic_timer_interrupt+0xe/0xc0 arch/x86/kernel/apic/apic.c:1062
softirqs last enabled at (78668): [<ffffffff8187f8b0>] __local_bh_enable_ip+0x1a0/0x2b0 kernel/softirq.c:305
softirqs last disabled at (78666): [<ffffffff861be59f>] local_bh_disable include/linux/bottom_half.h:20 [inline]
softirqs last disabled at (78666): [<ffffffff861be59f>] rcu_read_lock_bh include/linux/rcupdate.h:893 [inline]
softirqs last disabled at (78666): [<ffffffff861be59f>] keep_key_fresh drivers/net/wireguard/send.c:129 [inline]
softirqs last disabled at (78666): [<ffffffff861be59f>] wg_packet_create_data_done drivers/net/wireguard/send.c:259 [inline]
softirqs last disabled at (78666): [<ffffffff861be59f>] wg_packet_tx_worker+0x25f/0x7e0 drivers/net/wireguard/send.c:276
Preemption disabled at:
[<ffffffff8b50149d>] __schedule_loop kernel/sched/core.c:7310 [inline]
[<ffffffff8b50149d>] schedule+0x15d/0x2b0 kernel/sched/core.c:7326
CPU: 0 UID: 0 PID: 5685 Comm: kworker/0:5 Not tainted syzkaller #1 PREEMPT_{RT,(full)}
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
Workqueue: 0x0 (wg-crypt-wg1)
Call Trace:
<IRQ>
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
instr_sysvec_apic_timer_interrupt arch/x86/kernel/apic/apic.c:1062 [inline]
sysvec_apic_timer_interrupt+0xa1/0xc0 arch/x86/kernel/apic/apic.c:1062
</IRQ>
<TASK>
asm_sysvec_apic_timer_interrupt+0x1a/0x20 arch/x86/include/asm/idtentry.h:674
RIP: 0010:finish_task_switch+0x417/0xc60 kernel/sched/core.c:5361
Code: 04 00 00 41 c7 84 24 20 0e 00 00 00 00 00 00 0f 1f 44 00 00 49 83 c4 48 4c 89 e7 e8 b3 99 bd 09 e8 de c7 38 00 fb 4c 8b 65 c8 <49> 8d bc 24 e0 16 00 00 48 89 f8 48 c1 e8 03 42 0f b6 04 30 84 c0
RSP: 0018:ffffc9000442fa60 EFLAGS: 00000206
RAX: 0000000000013355 RBX: ffff88813883c6e0 RCX: 0000000080000001
RDX: 0000000000000000 RSI: ffffffff8daa4fe1 RDI: ffffffff8bd1a580
RBP: ffffc9000442fab0 R08: ffffffff8fd9f0f7 R09: 1ffffffff1fb3e1e
R10: dffffc0000000000 R11: fffffbfff1fb3e1f R12: ffff888114048000
R13: ffff88813883c6a8 R14: dffffc0000000000 R15: 1ffff110271078dc
context_switch kernel/sched/core.c:5513 [inline]
__schedule+0x1711/0x5530 kernel/sched/core.c:7234
__schedule_loop kernel/sched/core.c:7311 [inline]
schedule+0x164/0x2b0 kernel/sched/core.c:7326
worker_thread+0xc12/0xfb0 kernel/workqueue.c:3501
kthread+0x388/0x470 kernel/kthread.c:436
ret_from_fork+0x514/0xb70 arch/x86/kernel/process.c:158
ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245
</TASK>
=============================
[ BUG: Invalid wait context ]
syzkaller #1 Tainted: G W
-----------------------------
kworker/0:5/5685 is trying to lock:
ffff8881964dd530 (&gpc->lock){....}-{3:3}, at: kvm_xen_set_evtchn_fast+0x1f4/0x990 arch/x86/kvm/xen.c:1822
other info that might help us debug this:
context-{2:2}
1 lock held by kworker/0:5/5685:
#0: ffff8881964ddb48 (&kvm->srcu){.?.+}-{0:0}, at: srcu_lock_acquire include/linux/srcu.h:187 [inline]
#0: ffff8881964ddb48 (&kvm->srcu){.?.+}-{0:0}, at: srcu_read_lock include/linux/srcu.h:294 [inline]
#0: ffff8881964ddb48 (&kvm->srcu){.?.+}-{0:0}, at: kvm_xen_set_evtchn_fast+0x1be/0x990 arch/x86/kvm/xen.c:1820
stack backtrace:
CPU: 0 UID: 0 PID: 5685 Comm: kworker/0:5 Tainted: G W syzkaller #1 PREEMPT_{RT,(full)}
Tainted: [W]=WARN
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
Workqueue: 0x0 (wg-crypt-wg1)
Call Trace:
<IRQ>
dump_stack_lvl+0xe8/0x150 lib/dump_stack.c:120
print_lock_invalid_wait_context kernel/locking/lockdep.c:4830 [inline]
check_wait_context kernel/locking/lockdep.c:4902 [inline]
__lock_acquire+0xe39/0x2ce0 kernel/locking/lockdep.c:5187
lock_acquire+0x106/0x350 kernel/locking/lockdep.c:5868
rt_read_lock+0xcc/0x4b0 kernel/locking/spinlock_rt.c:249
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
instr_sysvec_apic_timer_interrupt arch/x86/kernel/apic/apic.c:1062 [inline]
sysvec_apic_timer_interrupt+0xa1/0xc0 arch/x86/kernel/apic/apic.c:1062
</IRQ>
<TASK>
asm_sysvec_apic_timer_interrupt+0x1a/0x20 arch/x86/include/asm/idtentry.h:674
RIP: 0010:finish_task_switch+0x417/0xc60 kernel/sched/core.c:5361
Code: 04 00 00 41 c7 84 24 20 0e 00 00 00 00 00 00 0f 1f 44 00 00 49 83 c4 48 4c 89 e7 e8 b3 99 bd 09 e8 de c7 38 00 fb 4c 8b 65 c8 <49> 8d bc 24 e0 16 00 00 48 89 f8 48 c1 e8 03 42 0f b6 04 30 84 c0
RSP: 0018:ffffc9000442fa60 EFLAGS: 00000206
RAX: 0000000000013355 RBX: ffff88813883c6e0 RCX: 0000000080000001
RDX: 0000000000000000 RSI: ffffffff8daa4fe1 RDI: ffffffff8bd1a580
RBP: ffffc9000442fab0 R08: ffffffff8fd9f0f7 R09: 1ffffffff1fb3e1e
R10: dffffc0000000000 R11: fffffbfff1fb3e1f R12: ffff888114048000
R13: ffff88813883c6a8 R14: dffffc0000000000 R15: 1ffff110271078dc
context_switch kernel/sched/core.c:5513 [inline]
__schedule+0x1711/0x5530 kernel/sched/core.c:7234
__schedule_loop kernel/sched/core.c:7311 [inline]
schedule+0x164/0x2b0 kernel/sched/core.c:7326
worker_thread+0xc12/0xfb0 kernel/workqueue.c:3501
kthread+0x388/0x470 kernel/kthread.c:436
ret_from_fork+0x514/0xb70 arch/x86/kernel/process.c:158
ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245
</TASK>
----------------
Code disassembly (best guess), 1 bytes skipped:
0: 00 00 add %al,(%rax)
2: 41 c7 84 24 20 0e 00 movl $0x0,0xe20(%r12)
9: 00 00 00 00 00
e: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)
13: 49 83 c4 48 add $0x48,%r12
17: 4c 89 e7 mov %r12,%rdi
1a: e8 b3 99 bd 09 call 0x9bd99d2
1f: e8 de c7 38 00 call 0x38c802
24: fb sti
25: 4c 8b 65 c8 mov -0x38(%rbp),%r12
* 29: 49 8d bc 24 e0 16 00 lea 0x16e0(%r12),%rdi <-- trapping instruction
30: 00
31: 48 89 f8 mov %rdi,%rax
34: 48 c1 e8 03 shr $0x3,%rax
38: 42 0f b6 04 30 movzbl (%rax,%r14,1),%eax
3d: 84 c0 test %al,%al
The following C code is a draft of the vulnerable syscall sequence. Keep in mind that
it may lack the precise threading, sandboxing, and some arguments of a working reproducer:
// autogenerated by syzkaller (https://github.com/google/syzkaller)
#define _GNU_SOURCE
#include <endian.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mount.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>
uint64_t r[3] = {0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff};
int main(void)
{
syscall(__NR_mmap, /*addr=*/0x1ffffffff000ul, /*len=*/0x1000, /*prot=*/0ul, /*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/0x32ul, /*fd=*/(intptr_t)-1, /*offset=*/0ul);
syscall(__NR_mmap, /*addr=*/0x200000000000ul, /*len=*/0x1000000, /*prot=PROT_WRITE|PROT_READ|PROT_EXEC*/7ul, /*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/0x32ul, /*fd=*/(intptr_t)-1, /*offset=*/0ul);
syscall(__NR_mmap, /*addr=*/0x200001000000ul, /*len=*/0x1000, /*prot=*/0ul, /*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/0x32ul, /*fd=*/(intptr_t)-1, /*offset=*/0ul);
const char* reason;
(void)reason;
intptr_t res = 0;
if (write(1, "executing program\n", sizeof("executing program\n") - 1)) {}
// sendmsg$NFT_BATCH arguments: [
// fd: sock_nl_netfilter (resource)
// msg: ptr[in, msghdr_netlink[nft_batch_msg]] {
// msghdr_netlink[nft_batch_msg] {
// addr: nil
// addrlen: len = 0x0 (4 bytes)
// pad = 0x0 (4 bytes)
// vec: ptr[in, iovec[in, nft_batch_msg]] {
// iovec[in, nft_batch_msg] {
// addr: ptr[inout, array[ANYUNION]] {
// array[ANYUNION] {
// union ANYUNION {
// ANYBLOB: buffer: {14 00 00 00 10 00 01 00 00 00 00 00 00 00 00 00 00 00 00 0a} (length 0x14)
// }
// }
// }
// len: len = 0xd4 (8 bytes)
// }
// }
// vlen: const = 0x1 (8 bytes)
// ctrl: const = 0x0 (8 bytes)
// ctrllen: const = 0x0 (8 bytes)
// f: send_flags = 0x0 (4 bytes)
// pad = 0x0 (4 bytes)
// }
// }
// f: send_flags = 0x4001 (8 bytes)
// ]
*(uint64_t*)0x2000000000c0 = 0;
*(uint32_t*)0x2000000000c8 = 0;
*(uint64_t*)0x2000000000d0 = 0x200000000000;
*(uint64_t*)0x200000000000 = 0x200000000100;
memcpy((void*)0x200000000100, "\x14\x00\x00\x00\x10\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0a", 20);
*(uint64_t*)0x200000000008 = 0xd4;
*(uint64_t*)0x2000000000d8 = 1;
*(uint64_t*)0x2000000000e0 = 0;
*(uint64_t*)0x2000000000e8 = 0;
*(uint32_t*)0x2000000000f0 = 0;
syscall(__NR_sendmsg, /*fd=*/(intptr_t)-1, /*msg=*/0x2000000000c0ul, /*f=MSG_OOB|MSG_NOSIGNAL*/0x4001ul);
// openat$kvm arguments: [
// fd: const = 0xffffffffffffff9c (8 bytes)
// file: ptr[in, buffer] {
// buffer: {2f 64 65 76 2f 6b 76 6d 00} (length 0x9)
// }
// flags: open_flags = 0x0 (4 bytes)
// mode: const = 0x0 (2 bytes)
// ]
// returns fd_kvm
memcpy((void*)0x200000000000, "/dev/kvm\000", 9);
res = syscall(__NR_openat, /*fd=*/0xffffffffffffff9cul, /*file=*/0x200000000000ul, /*flags=*/0, /*mode=*/0);
if (res != -1)
r[0] = res;
// ioctl$KVM_CREATE_VM arguments: [
// fd: fd_kvm (resource)
// cmd: const = 0xae01 (4 bytes)
// type: intptr = 0x0 (8 bytes)
// ]
// returns fd_kvmvm
res = syscall(__NR_ioctl, /*fd=*/r[0], /*cmd=*/0xae01, /*type=*/0ul);
if (res != -1)
r[1] = res;
// ioctl$KVM_CREATE_VCPU arguments: [
// fd: fd_kvmvm (resource)
// cmd: const = 0xae41 (4 bytes)
// id: intptr = 0x0 (8 bytes)
// ]
// returns fd_kvmcpu
res = syscall(__NR_ioctl, /*fd=*/r[1], /*cmd=*/0xae41, /*id=*/0ul);
if (res != -1)
r[2] = res;
// ioctl arguments: [
// fd: fd (resource)
// cmd: int32 = 0x4048aecb (4 bytes)
// arg: ptr[in, buffer] {
// buffer: {07 00 00 00 00 00 00 00 01 00 00 00 ff ff ff ff} (length 0x10)
// }
// ]
memcpy((void*)0x200000000100, "\x07\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\xff\xff\xff\xff", 16);
syscall(__NR_ioctl, /*fd=*/r[2], /*cmd=*/0x4048aecb, /*arg=*/0x200000000100ul);
return 0;
}
Reply: The root cause of this crash is an attempt to acquire a sleeping lock from a hardirq context on a `PREEMPT_RT` kernel.
### Detailed Analysis
1. **The Lock:**
The crash happens when `kvm_xen_set_evtchn_fast()` attempts to acquire `gpc->lock` via `read_lock_irqsave(&gpc->lock, flags)`. The `gpc->lock` is an `rwlock_t` used to protect the `gfn_to_pfn_cache`.
In a `PREEMPT_RT` kernel, `rwlock_t` is mapped to `rt_rw_lock`, which is a sleeping lock (rtmutex-based). It cannot be acquired in hardirq context because it may sleep while waiting for the lock or while waking up waiters during unlock.
2. **The Context:**
`kvm_xen_set_evtchn_fast()` is called from `xen_timer_callback()`, which is an hrtimer callback initialized with `HRTIMER_MODE_ABS_HARD`. This means the timer callback executes in hardirq context, even on `PREEMPT_RT`.
Additionally, `kvm_xen_set_evtchn_fast()` is also called from `kvm_arch_set_irq_inatomic()`, which is invoked during `irqfd` waitqueue wakeups (also a hardirq/atomic context).
3. **The Lockdep Splat:**
The lockdep splat mentions `srcu_read_lock` because `kvm->srcu` was acquired just before the `read_lock_irqsave`. While `srcu_read_lock` itself is perfectly safe in hardirq context (it just increments a per-CPU counter), lockdep prints it as a "held lock" when the actual sleeping function (`rt_read_lock`) is called.
### How to Fix
To fix this, `kvm_xen_set_evtchn_fast()` must avoid using `read_lock_irqsave()` when called from an atomic context. Instead, it should use `read_trylock()`. If the trylock fails, it can safely fall back to the slow path (returning `-EWOULDBLOCK` or kicking the vCPU), which is exactly what the callers expect and handle.
This pattern of using `read_trylock()` for `gpc->lock` in atomic contexts is already established in KVM Xen (e.g., in `kvm_xen_update_runstate_guest()`).
You should modify `kvm_xen_set_evtchn_fast()` to accept a `bool in_atomic` parameter:
```c
// arch/x86/kvm/xen.h
int kvm_xen_set_evtchn_fast(struct kvm_xen_evtchn *xe, struct kvm *kvm, bool in_atomic);
```
Then update `kvm_xen_set_evtchn_fast()` in `arch/x86/kvm/xen.c` to use `read_trylock()` when `in_atomic` is true:
```c
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);
}
```
You will need to do this for **both** `gpc->lock` acquisitions in that function. For the second acquisition (the `vcpu_info_cache`), if the trylock fails, you should fall back to setting the bit in-kernel and kicking the vCPU, just like it does when `kvm_gpc_check()` fails:
```c
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);
}
```
Make sure to add the `out_rcu_no_lock` label to avoid unlocking a lock you don't hold:
```c
out_rcu:
read_unlock_irqrestore(&gpc->lock, flags);
out_rcu_no_lock:
srcu_read_unlock(&kvm->srcu, idx);
```
Finally, update the callers to pass the correct context:
- `xen_timer_callback()` in `arch/x86/kvm/xen.c`: pass `true`.
- `kvm_arch_set_irq_inatomic()` in `arch/x86/kvm/irq.c`: pass `true`.
- `kvm_xen_set_evtchn()` in `arch/x86/kvm/xen.c` (both calls): pass `false` (since it runs in process context, e.g., from a hypercall or the irqfd workqueue).
|
| 8/2 |
2026/08/12 11:53 |
llm |
debugger |
0m
Model:
Tokens:
input: 10064
output: 41
thoughts: 540
Thoughts: **Analyzing a PREEMPT_RT Lock Violation in KVM/Xen Integration**
Okay, so I've got this bug report, "BUG: sleeping function called from invalid context". The stack trace immediately screams a PREEMPT_RT violation, and I can see precisely where. The culprit is `rt_read_lock` at `kernel/locking/spinlock_rt.c:248`. This is a classic – attempting to sleep while holding a real-time spinlock. That's a huge no-no, obviously.
Now, the context is the kicker. It’s triggered within `kvm_xen_set_evtchn_fast`, which is being called from `xen_timer_callback` located in `arch/x86/kvm/xen.c`. The stack trace clearly indicates this interrupt is originating from a hard interrupt context (indicated by `<IRQ>` and the hrtimer). `xen_timer_callback` is running on an hrtimer, and that's the hard interrupt. So, `kvm_xen_set_evtchn_fast` is acquiring a lock that *can* sleep when it should not be. This is a very serious problem.
My next step is to examine the source code for `xen_timer_callback` and `kvm_xen_set_evtchn_fast` to understand *why* this lock is acquired there and why it might be sleeping. From what I see in the trace, my immediate thought is that it's likely something to do with event channel handling in the KVM/Xen integration. I need to figure out what's the purpose of the lock in `kvm_xen_set_evtchn_fast` and if there's an alternative, lockless, or interrupt-safe approach for this specific scenario. I need to see what can be done to address the problem in that function, or maybe upstream to `xen_timer_callback` by pushing the work to a softirq context. This will likely involve careful analysis of the Xen hypercall interactions and any shared data structures involved.
|
| 9/2 |
2026/08/12 11:53 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:arch/x86/kvm/xen.c Name:xen_timer_callback]
Results: map[SourceCode: 125: static enum hrtimer_restart xen_timer_callback(struct hrtimer *timer)
126: {
127: struct kvm_vcpu *vcpu = container_of(timer, struct kvm_vcpu,
128: arch.xen.timer);
129: struct kvm_xen_evtchn e;
130: int rc;
131:
132: if (atomic_read(&vcpu->arch.xen.timer_pending))
133: return HRTIMER_NORESTART;
134:
135: e.vcpu_id = vcpu->vcpu_id;
136: e.vcpu_idx = vcpu->vcpu_idx;
137: e.port = vcpu->arch.xen.timer_virq;
138: e.priority = KVM_IRQ_ROUTING_XEN_EVTCHN_PRIO_2LEVEL;
139:
140: rc = kvm_xen_set_evtchn_fast(&e, vcpu->kvm);
141: if (rc != -EWOULDBLOCK) {
142: vcpu->arch.xen.timer_expires = 0;
143: return HRTIMER_NORESTART;
144: }
145:
146: atomic_inc(&vcpu->arch.xen.timer_pending);
147: kvm_make_request(KVM_REQ_UNBLOCK, vcpu);
148: kvm_vcpu_kick(vcpu);
149:
150: return HRTIMER_NORESTART;
151: }
SourceFile:arch/x86/kvm/xen.c]
|
| 10/2 |
2026/08/12 11:53 |
llm |
debugger |
0m
Model:
Tokens:
input: 11578
output: 47
thoughts: 40
Thoughts: Here's a breakdown of my thought process:
**Diving into the KVM-Xen Event Channel Interaction**
Okay, so I'm tracing through this code, and I've hit `xen_timer_callback`. Right off the bat, I see it's calling `kvm_xen_set_evtchn_fast`. My internal radar just pinged - that's interesting. I know that function is responsible for setting up the fast event channel mechanism between KVM and Xen, a pretty crucial piece for performance. Now, I need to understand what this `xen_timer_callback` is doing to trigger that function. But first, let me refresh my memory of the mechanics of `kvm_xen_set_evtchn_fast`. Let's take a closer look at that function's implementation and how it fits into the broader picture.
|
| 11/2 |
2026/08/12 11:53 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:arch/x86/kvm/xen.c Name:kvm_xen_set_evtchn_fast]
Results: map[SourceCode:1785: /*
1786: * The return value from this function is propagated to kvm_set_irq() API,
1787: * so it returns:
1788: * < 0 Interrupt was ignored (masked or not delivered for other reasons)
1789: * = 0 Interrupt was coalesced (previous irq is still pending)
1790: * > 0 Number of CPUs interrupt was delivered to
1791: *
1792: * It is also called directly from kvm_arch_set_irq_inatomic(), where the
1793: * only check on its return value is a comparison with -EWOULDBLOCK'.
1794: */
1795: int kvm_xen_set_evtchn_fast(struct kvm_xen_evtchn *xe, struct kvm *kvm)
1796: {
1797: struct gfn_to_pfn_cache *gpc = &kvm->arch.xen.shinfo_cache;
1798: struct kvm_vcpu *vcpu;
1799: unsigned long *pending_bits, *mask_bits;
1800: unsigned long flags;
1801: int port_word_bit;
1802: bool kick_vcpu = false;
1803: int vcpu_idx, idx, rc;
1804:
1805: vcpu_idx = READ_ONCE(xe->vcpu_idx);
1806: if (vcpu_idx >= 0)
1807: vcpu = kvm_get_vcpu(kvm, vcpu_idx);
1808: else {
1809: vcpu = kvm_get_vcpu_by_id(kvm, xe->vcpu_id);
1810: if (!vcpu)
1811: return -EINVAL;
1812: WRITE_ONCE(xe->vcpu_idx, vcpu->vcpu_idx);
1813: }
1814:
1815: if (xe->port >= max_evtchn_port(kvm))
1816: return -EINVAL;
1817:
1818: rc = -EWOULDBLOCK;
1819:
1820: idx = srcu_read_lock(&kvm->srcu);
1821:
1822: read_lock_irqsave(&gpc->lock, flags);
1823: if (!kvm_gpc_check(gpc, PAGE_SIZE))
1824: goto out_rcu;
1825:
1826: if (IS_ENABLED(CONFIG_64BIT) && kvm->arch.xen.long_mode) {
1827: struct shared_info *shinfo = gpc->khva;
1828: pending_bits = (unsigned long *)&shinfo->evtchn_pending;
1829: mask_bits = (unsigned long *)&shinfo->evtchn_mask;
1830: port_word_bit = xe->port / 64;
1831: } else {
1832: struct compat_shared_info *shinfo = gpc->khva;
1833: pending_bits = (unsigned long *)&shinfo->evtchn_pending;
1834: mask_bits = (unsigned long *)&shinfo->evtchn_mask;
1835: port_word_bit = xe->port / 32;
1836: }
1837:
1838: /*
1839: * If this port wasn't already set, and if it isn't masked, then
1840: * we try to set the corresponding bit in the in-kernel shadow of
1841: * evtchn_pending_sel for the target vCPU. And if *that* wasn't
1842: * already set, then we kick the vCPU in question to write to the
1843: * *real* evtchn_pending_sel in its own guest vcpu_info struct.
1844: */
1845: if (test_and_set_bit(xe->port, pending_bits)) {
1846: rc = 0; /* It was already raised */
1847: } else if (test_bit(xe->port, mask_bits)) {
1848: rc = -ENOTCONN; /* Masked */
1849: kvm_xen_check_poller(vcpu, xe->port);
1850: } else {
1851: rc = 1; /* Delivered to the bitmap in shared_info. */
1852: /* Now switch to the vCPU's vcpu_info to set the index and pending_sel */
1853: read_unlock_irqrestore(&gpc->lock, flags);
1854: gpc = &vcpu->arch.xen.vcpu_info_cache;
1855:
1856: read_lock_irqsave(&gpc->lock, flags);
1857: if (!kvm_gpc_check(gpc, sizeof(struct vcpu_info))) {
1858: /*
1859: * Could not access the vcpu_info. Set the bit in-kernel
1860: * and prod the vCPU to deliver it for itself.
1861: */
1862: if (!test_and_set_bit(port_word_bit, &vcpu->arch.xen.evtchn_pending_sel))
1863: kick_vcpu = true;
1864: goto out_rcu;
1865: }
1866:
1867: if (IS_ENABLED(CONFIG_64BIT) && kvm->arch.xen.long_mode) {
1868: struct vcpu_info *vcpu_info = gpc->khva;
1869: if (!test_and_set_bit(port_word_bit, &vcpu_info->evtchn_pending_sel)) {
1870: WRITE_ONCE(vcpu_info->evtchn_upcall_pending, 1);
1871: kick_vcpu = true;
1872: }
1873: } else {
1874: struct compat_vcpu_info *vcpu_info = gpc->khva;
1875: if (!test_and_set_bit(port_word_bit,
1876: (unsigned long *)&vcpu_info->evtchn_pending_sel)) {
1877: WRITE_ONCE(vcpu_info->evtchn_upcall_pending, 1);
1878: kick_vcpu = true;
1879: }
1880: }
1881:
1882: /* For the per-vCPU lapic vector, deliver it as MSI. */
1883: if (kick_vcpu && vcpu->arch.xen.upcall_vector) {
1884: kvm_xen_inject_vcpu_vector(vcpu);
1885: kick_vcpu = false;
1886: }
1887: }
1888:
1889: out_rcu:
1890: read_unlock_irqrestore(&gpc->lock, flags);
1891: srcu_read_unlock(&kvm->srcu, idx);
1892:
1893: if (kick_vcpu) {
1894: kvm_make_request(KVM_REQ_UNBLOCK, vcpu);
1895: kvm_vcpu_kick(vcpu);
1896: }
1897:
1898: return rc;
1899: }
SourceFile:arch/x86/kvm/xen.c]
|
| 12/2 |
2026/08/12 11:53 |
llm |
debugger |
0m
Model:
Tokens:
input: 13931
output: 54
thoughts: 129
Thoughts: **Debugging a Kernel Lock Issue**
Okay, so I've got a crash, and the backtrace points directly to line 1822, specifically at `read_lock_irqsave(&gpc->lock, flags);`. Right off the bat, I know that `gpc->lock` is an `rwlock_t`, which, under PREEMPT_RT, is a *sleeping* lock. That's immediately problematic because sleeping locks aren't allowed in hardirq context. The critical clue is that `xen_timer_callback`, which seems to be the function in question, is a high-resolution timer callback, and hrtimer callbacks *always* run in hardirq context. This is a classic violation! Now I need to confirm where `xen_timer_callback` is initialized. I need to understand how this function got registered as a high-resolution timer in the first place, or perhaps it shouldn't have been. Time to trace back the initialization path and see what's going on!
|
| 13/2 |
2026/08/12 11:53 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:arch/x86/kvm/xen.c IncludeSnippetLines:2 Name:xen_timer_callback SourceTreePrefix:]
Results: map[References:[map[ReferenceKind:takes-address-of ReferencingEntityKind:function ReferencingEntityName:kvm_xen_init_vcpu SourceFile:arch/x86/kvm/xen.c SourceLine:2306 SourceSnippet:2304:
2305: timer_setup(&vcpu->arch.xen.poll_timer, cancel_evtchn_poll, 0);
2306: hrtimer_setup(&vcpu->arch.xen.timer, xen_timer_callback, CLOCK_MONOTONIC,
2307: HRTIMER_MODE_ABS_HARD);
2308:
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:kvm_xen_start_timer SourceFile:arch/x86/kvm/xen.c SourceLine:307 SourceSnippet: 305:
306: if (delta <= 0)
307: xen_timer_callback(&vcpu->arch.xen.timer);
308: else
309: hrtimer_start(&vcpu->arch.xen.timer,
]] TruncatedOutput:false]
|
| 14/2 |
2026/08/12 11:53 |
llm |
debugger |
0m
Model:
Tokens:
input: 14653
output: 45
thoughts: 565
Thoughts: **Investigating a PREEMPT_RT Concurrency Issue in KVM/Xen Interaction**
Okay, so I'm looking at this timer setup: `hrtimer_setup` is configured with `HRTIMER_MODE_ABS_HARD`. That means this timer callback, `xen_timer_callback`, is running in a hard interrupt context. Fine, standard stuff. But I'm also thinking about PREEMPT_RT, and how things work there. I know that in that environment, a `rwlock_t` is actually a *sleeping* lock. Now, I see that `kvm_xen_set_evtchn_fast` is taking a `read_lock_irqsave` on `gpc->lock` and also a `srcu_read_lock` on `kvm->srcu`.
Hang on a second… isn't `srcu_read_lock` also problematic in a hardirq context under PREEMPT_RT? I need to confirm this. Let me quickly check the implementation of `srcu_read_lock` in a PREEMPT_RT environment.
Ah, but the crash report is actually giving me more specific information. Let's look:
```
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
preempt_count: 10001, expected: 0
RCU nest depth: 0, expected: 0
1 lock held by kworker/0:5/5685:
#0: ffff8881964ddb48 (&kvm->srcu){.?.+}-{0:0}, at: srcu_lock_acquire include/linux/srcu.h:187 [inline]
#0: ffff8881964ddb48 (&kvm->srcu){.?.+}-{0:0}, at: srcu_read_lock include/linux/srcu.h:294 [inline]
#0: ffff8881964ddb48 (&kvm->srcu){.?.+}-{0:0}, at: kvm_xen_set_evtchn_fast+0x1be/0x990 arch/x86/kvm/xen.c:1820
```
Right, the crash report is showing that the lock held is actually `&kvm->srcu`, and the crash is indeed happening in `srcu_read_lock`, as I suspected. And the crash is being caused by `rt_read_lock` being a sleeping lock. But this suggests the immediate cause isn't the `srcu_read_lock` call directly, it's something called from within `read_lock_irqsave(&gpc->lock, flags);`. Ah-HAH! So, in PREEMPT_RT, that `read_lock_irqsave` on `rwlock_t` *is* a sleeping lock.
Okay, so the real question becomes: Is `gpc->lock` an `rwlock_t`? If it is, then the problem is clear, the timer callback in hardirq context is trying to take a sleeping lock, and that's a big no-no under PREEMPT_RT. I need to trace the definition of `gpc->lock` to confirm this.
|
| 15/2 |
2026/08/12 11:54 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:arch/x86/kvm/xen.c Name:gfn_to_pfn_cache]
Results: map[SourceCode: 84: struct gfn_to_pfn_cache {
85: u64 generation;
86: gpa_t gpa;
87: unsigned long uhva;
88: struct kvm_memory_slot *memslot;
89: struct kvm *kvm;
90: struct list_head list;
91: rwlock_t lock;
92: struct mutex refresh_lock;
93: void *khva;
94: kvm_pfn_t pfn;
95: bool active;
96: bool valid;
97: };
SourceFile:include/linux/kvm_types.h]
|
| 16/2 |
2026/08/12 11:54 |
llm |
debugger |
0m
Model:
Tokens:
input: 16241
output: 45
thoughts: 143
Thoughts: **Analyzing the Potential for Sleep in an Interrupt Context**
Okay, so I'm seeing `rwlock_t lock;` used here. In a PREEMPT_RT environment, that means this is going to translate to `rt_rw_lock`, which is a sleeping lock, right? So if I'm understanding this correctly, that call to `read_lock_irqsave(&gpc->lock, flags);` *will* potentially sleep. But now the question is: can it actually *do* that here? Because `xen_timer_callback` runs within the hardirq context, thanks to that `HRTIMER_MODE_ABS_HARD` setting.
Wait a second… why is it configured as `HRTIMER_MODE_ABS_HARD` in the first place? I need to go back and check. Let's dig into `kvm_xen_init_vcpu` in `arch/x86/kvm/xen.c`. I need to figure out the context surrounding that timer setup to see if this lock is actually going to be a problem.
|
| 17/2 |
2026/08/12 11:54 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:arch/x86/kvm/xen.c Name:kvm_xen_init_vcpu]
Results: map[SourceCode:2300: void kvm_xen_init_vcpu(struct kvm_vcpu *vcpu)
2301: {
2302: vcpu->arch.xen.vcpu_id = vcpu->vcpu_idx;
2303: vcpu->arch.xen.poll_evtchn = 0;
2304:
2305: timer_setup(&vcpu->arch.xen.poll_timer, cancel_evtchn_poll, 0);
2306: hrtimer_setup(&vcpu->arch.xen.timer, xen_timer_callback, CLOCK_MONOTONIC,
2307: HRTIMER_MODE_ABS_HARD);
2308:
2309: kvm_gpc_init(&vcpu->arch.xen.runstate_cache, vcpu->kvm);
2310: kvm_gpc_init(&vcpu->arch.xen.runstate2_cache, vcpu->kvm);
2311: kvm_gpc_init(&vcpu->arch.xen.vcpu_info_cache, vcpu->kvm);
2312: kvm_gpc_init(&vcpu->arch.xen.vcpu_time_info_cache, vcpu->kvm);
2313: }
SourceFile:arch/x86/kvm/xen.c]
|
| 18/2 |
2026/08/12 11:54 |
llm |
debugger |
0m
Model:
Tokens:
input: 17025
output: 60
thoughts: 27
Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience:
**Analyzing the `HRTIMER_MODE_ABS_HARD` Implementation**
Hmm, `HRTIMER_MODE_ABS_HARD`. I'm immediately curious about that specific mode being chosen. Why *that* one? It's not the most common or straightforward. I need to understand the design rationale behind its selection. Is there some specific hardware requirement, timing constraint, or optimization it facilitates that other modes wouldn't?
My next instinct is to trace its introduction. Time to dig into the version control logs. Knowing *when* this mode entered the picture will likely provide significant clues. It might coincide with a new hardware feature, a bug fix, or a performance improvement. The commit message or related patch description will likely be very informative. The timeline, the context surrounding its introduction—that's key to understanding the intent. I need that history. Let's see what the change log tells us.
|
| 19/2 |
2026/08/12 11:54 |
tool |
git-log |
0m
Args: map[CodeRegexp:HRTIMER_MODE_ABS_HARD Count:10 PathPrefix:arch/x86/kvm/xen.c SourcePath: SymbolName:]
Results: map[Output:7764b9dd174c KVM: x86: Switch to use hrtimer_setup()
f6e12766c52d KVM: x86/xen: Initialize hrtimer in kvm_xen_init_vcpu()
77c9b9dea4fb KVM: x86/xen: Use fast path for Xen timer delivery
536395260582 KVM: x86/xen: handle PV timers oneshot mode
]
|
| 20/2 |
2026/08/12 11:54 |
llm |
debugger |
0m
Model:
Tokens:
input: 17450
output: 24
thoughts: 16
Thoughts: Okay, let's dive into this. Here's my take on examining commit `77c9b9dea4fb`:
**Analyzing Commit `77c9b9dea4fb`**
Right, let's take a look at this specific commit, `77c9b9dea4fb`. I need to understand the context and the potential impact of the changes introduced here. My first thought is to review the diff itself. What files were modified? What were the specific line-by-line alterations? I'll probably start with the most critical files, based on my understanding of the codebase's architecture and the areas that are most prone to regressions or performance bottlenecks. I need to quickly identify the *why* behind these changes. Was it a bug fix? A performance optimization? A new feature implementation? What was the original issue, or the problem being solved? I will need to consider whether those changes introduce any regressions that I would want to test. I should also be mindful of dependencies – did this commit touch any shared libraries, configuration files, or other components that could be affected? And, it is important to remember that I need to be aware of the original PR description, as there may be additional context or related information I can reference. I'll need to go through the commits leading up to this one, so I can see the history and any prior decisions, as well as the conversation attached to the commit. This will give me a better understanding of how the code evolved to this point. Now, let's open up the diff and get to work.
|
| 21/2 |
2026/08/12 11:54 |
tool |
git-show |
0m
Args:
Results: map[Output:commit 77c9b9dea4fb3e51e0d850db7f21cb1156d987bd
Author: David Woodhouse <dwmw@amazon.co.uk>
Date: Sat Sep 30 14:58:35 2023 +0100
KVM: x86/xen: Use fast path for Xen timer delivery
Most of the time there's no need to kick the vCPU and deliver the timer
event through kvm_xen_inject_timer_irqs(). Use kvm_xen_set_evtchn_fast()
directly from the timer callback, and only fall back to the slow path if
delivering the timer would block, i.e. if kvm_xen_set_evtchn_fast()
returns -EWOULDBLOCK. If delivery fails for any other reason, do nothing
and just let it fail silently, as that is what the slow path would end up
doing anyways.
This gives a significant improvement in timer latency testing (using
nanosleep() for various periods and then measuring the actual time
elapsed).
However, there was a reason[1] the fast path was dropped when this support
was first added. The current code holds vcpu->mutex for all operations on
the kvm->arch.timer_expires field, and the fast path introduces a
potential race condition. Avoid that race by ensuring the hrtimer is
(temporarily) cancelled before making changes in kvm_xen_start_timer(),
and also when reading the values out for KVM_XEN_VCPU_ATTR_TYPE_TIMER.
[1] https://lore.kernel.org/kvm/846caa99-2e42-4443-1070-84e49d2f11d2@redhat.com
Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>
Reviewed-by: Paul Durrant <paul@xen.org>
Link: https://lore.kernel.org/r/f21ee3bd852761e7808240d4ecaec3013c649dc7.camel@infradead.org
[sean: massage changelog]
Signed-off-by: Sean Christopherson <seanjc@google.com>
diff --git a/arch/x86/kvm/xen.c b/arch/x86/kvm/xen.c
index 40edf4d1974c..75586da134b3 100644
--- a/arch/x86/kvm/xen.c
+++ b/arch/x86/kvm/xen.c
@@ -134,9 +134,23 @@ static enum hrtimer_restart xen_timer_callback(struct hrtimer *timer)
{
struct kvm_vcpu *vcpu = container_of(timer, struct kvm_vcpu,
arch.xen.timer);
+ struct kvm_xen_evtchn e;
+ int rc;
+
if (atomic_read(&vcpu->arch.xen.timer_pending))
return HRTIMER_NORESTART;
+ e.vcpu_id = vcpu->vcpu_id;
+ e.vcpu_idx = vcpu->vcpu_idx;
+ 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);
+ if (rc != -EWOULDBLOCK) {
+ vcpu->arch.xen.timer_expires = 0;
+ return HRTIMER_NORESTART;
+ }
+
atomic_inc(&vcpu->arch.xen.timer_pending);
kvm_make_request(KVM_REQ_UNBLOCK, vcpu);
kvm_vcpu_kick(vcpu);
@@ -146,6 +160,14 @@ static enum hrtimer_restart xen_timer_callback(struct hrtimer *timer)
static void kvm_xen_start_timer(struct kvm_vcpu *vcpu, u64 guest_abs, s64 delta_ns)
{
+ /*
+ * Avoid races with the old timer firing. Checking timer_expires
+ * to avoid calling hrtimer_cancel() will only have false positives
+ * so is fine.
+ */
+ if (vcpu->arch.xen.timer_expires)
+ hrtimer_cancel(&vcpu->arch.xen.timer);
+
atomic_set(&vcpu->arch.xen.timer_pending, 0);
vcpu->arch.xen.timer_expires = guest_abs;
@@ -1019,9 +1041,36 @@ int kvm_xen_vcpu_get_attr(struct kvm_vcpu *vcpu, struct kvm_xen_vcpu_attr *data)
break;
case KVM_XEN_VCPU_ATTR_TYPE_TIMER:
+ /*
+ * Ensure a consistent snapshot of state is captured, with a
+ * timer either being pending, or the event channel delivered
+ * to the corresponding bit in the shared_info. Not still
+ * lurking in the timer_pending flag for deferred delivery.
+ * Purely as an optimisation, if the timer_expires field is
+ * zero, that means the timer isn't active (or even in the
+ * timer_pending flag) and there is no need to cancel it.
+ */
+ if (vcpu->arch.xen.timer_expires) {
+ hrtimer_cancel(&vcpu->arch.xen.timer);
+ kvm_xen_inject_timer_irqs(vcpu);
+ }
+
data->u.timer.port = vcpu->arch.xen.timer_virq;
data->u.timer.priority = KVM_IRQ_ROUTING_XEN_EVTCHN_PRIO_2LEVEL;
data->u.timer.expires_ns = vcpu->arch.xen.timer_expires;
+
+ /*
+ * The hrtimer may trigger and raise the IRQ immediately,
+ * while the returned state causes it to be set up and
+ * raised again on the destination system after migration.
+ * That's fine, as the guest won't even have had a chance
+ * to run and handle the interrupt. Asserting an already
+ * pending event channel is idempotent.
+ */
+ if (vcpu->arch.xen.timer_expires)
+ hrtimer_start_expires(&vcpu->arch.xen.timer,
+ HRTIMER_MODE_ABS_HARD);
+
r = 0;
break;
]
|
| 22/2 |
2026/08/12 11:54 |
llm |
debugger |
0m
Model:
Tokens:
input: 19547
output: 27
thoughts: 206
Thoughts: **Investigating a Potential Locking Issue in KVM-Xen Integration**
Okay, so I've stumbled upon something potentially problematic. I'm seeing that `kvm_xen_set_evtchn_fast` is being called from `xen_timer_callback`. The critical part is that `xen_timer_callback` is an hrtimer callback. That raises a red flag immediately. If `kvm_xen_set_evtchn_fast` is taking a lock that might sleep within a PREEMPT_RT kernel, we have a serious bug on our hands.
Let's drill down. I know that `gpc->lock` is an `rwlock_t`, which is a pretty standard read-write lock. The question then becomes, is there a way to avoid taking that lock while in the hardirq context of the hrtimer? Or, could we safely modify the timer itself? Perhaps changing it from `HRTIMER_MODE_ABS_HARD` to something else would alleviate the issue.
I need to investigate the necessity of `HRTIMER_MODE_ABS_HARD`. Is it really crucial? My gut tells me we should check. Let's see... and according to the code, this was introduced in `kvm_xen_init_vcpu`, it looks like it was changed in `f6e12766c52d` or even earlier. Time to look into that commit to understand the rationale behind it and see if there are any hints about this locking scenario. Let's start with that commit.
|
| 23/2 |
2026/08/12 11:54 |
tool |
git-show |
0m
Args:
Results: map[Output:commit f6e12766c52dc8e7032fe51d4ef33320b475775e
Author: Nam Cao <namcao@linutronix.de>
Date: Thu Oct 31 16:14:17 2024 +0100
KVM: x86/xen: Initialize hrtimer in kvm_xen_init_vcpu()
The hrtimer is initialized in the KVM_XEN_VCPU_SET_ATTR ioctl. That caused
problem in the past, because the hrtimer can be initialized multiple times,
which was fixed by commit af735db31285 ("KVM: x86/xen: Initialize Xen timer
only once"). This commit avoids initializing the timer multiple times by
checking the field 'function' of struct hrtimer to determine if it has
already been initialized.
This is not required and in the way to make the function field private.
Move the hrtimer initialization into kvm_xen_init_vcpu() so that it will
only be initialized once.
Signed-off-by: Nam Cao <namcao@linutronix.de>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Acked-by: Sean Christopherson <seanjc@google.com>
Link: https://lore.kernel.org/all/9c33c7224d97d08f4fa30d3cc8687981c1d3e953.1730386209.git.namcao@linutronix.de
diff --git a/arch/x86/kvm/xen.c b/arch/x86/kvm/xen.c
index 622fe24da910..a909b817b9c0 100644
--- a/arch/x86/kvm/xen.c
+++ b/arch/x86/kvm/xen.c
@@ -263,13 +263,6 @@ static void kvm_xen_stop_timer(struct kvm_vcpu *vcpu)
atomic_set(&vcpu->arch.xen.timer_pending, 0);
}
-static void kvm_xen_init_timer(struct kvm_vcpu *vcpu)
-{
- hrtimer_init(&vcpu->arch.xen.timer, CLOCK_MONOTONIC,
- HRTIMER_MODE_ABS_HARD);
- vcpu->arch.xen.timer.function = xen_timer_callback;
-}
-
static void kvm_xen_update_runstate_guest(struct kvm_vcpu *v, bool atomic)
{
struct kvm_vcpu_xen *vx = &v->arch.xen;
@@ -1070,9 +1063,6 @@ int kvm_xen_vcpu_set_attr(struct kvm_vcpu *vcpu, struct kvm_xen_vcpu_attr *data)
break;
}
- if (!vcpu->arch.xen.timer.function)
- kvm_xen_init_timer(vcpu);
-
/* Stop the timer (if it's running) before changing the vector */
kvm_xen_stop_timer(vcpu);
vcpu->arch.xen.timer_virq = data->u.timer.port;
@@ -2235,6 +2225,8 @@ void kvm_xen_init_vcpu(struct kvm_vcpu *vcpu)
vcpu->arch.xen.poll_evtchn = 0;
timer_setup(&vcpu->arch.xen.poll_timer, cancel_evtchn_poll, 0);
+ hrtimer_init(&vcpu->arch.xen.timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS_HARD);
+ vcpu->arch.xen.timer.function = xen_timer_callback;
kvm_gpc_init(&vcpu->arch.xen.runstate_cache, vcpu->kvm);
kvm_gpc_init(&vcpu->arch.xen.runstate2_cache, vcpu->kvm);
]
|
| 24/2 |
2026/08/12 11:54 |
llm |
debugger |
0m
Model:
Tokens:
input: 21189
output: 60
thoughts: 61
Thoughts: **Investigating the HRTIMER Initialization**
Okay, so I'm looking at this code and something feels… off. I swear that `hrtimer_init(&vcpu->arch.xen.timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS_HARD);` was in a different spot previously. My initial reaction is that it's moved, which isn't inherently a problem, but it raises a question. I need to understand why it might have been shifted, or if it's part of a larger change.
My next thought is to delve into `HRTIMER_MODE_ABS_HARD`. I need to confirm when this specific mode was introduced into the code base. Knowing the timeline will help me grasp the context around its usage here. Was it a recent addition, perhaps tied to a particular feature or fix? Or has it been around for a while, meaning the change is less likely to be something critical related to the mode itself, and more related to the surrounding architecture. Knowing this will help me determine the significance of the shift and if there are any potential implications based on its timing. I need to see the commit logs to figure this out.
|
| 25/2 |
2026/08/12 11:54 |
tool |
git-log |
0m
Args: map[CodeRegexp:HRTIMER_MODE_ABS_HARD Count:10 PathPrefix:arch/x86/kvm/xen.c SourcePath: SymbolName:]
Results: map[Output:7764b9dd174c KVM: x86: Switch to use hrtimer_setup()
f6e12766c52d KVM: x86/xen: Initialize hrtimer in kvm_xen_init_vcpu()
77c9b9dea4fb KVM: x86/xen: Use fast path for Xen timer delivery
536395260582 KVM: x86/xen: handle PV timers oneshot mode
]
|
| 26/2 |
2026/08/12 11:54 |
llm |
debugger |
0m
Model:
Tokens:
input: 21687
output: 27
thoughts: 18
Thoughts: Okay, here's my attempt at summarizing the thought process, as if it were my own, assuming I'm an expert in the field:
**Initial Assessment: Number Verification**
Alright, I'm presented with a number: `536395260582`. My first instinct is to immediately assess it, likely in the context of whatever dataset or system I'm currently working with. Is this a code, an ID, a timestamp, a financial figure? The format alone gives me little information, but it triggers several immediate mental checks.
* **Format Recognition:** Does it adhere to any known industry formats? Could this be a SKU, a serial number, a credit card number (unlikely given the length but still a consideration at this stage), a unique identifier from a database, or perhaps something encoded in a specific system? My brain automatically starts filtering against known patterns and rules.
* **Data Type Estimation:** Given the length, it's likely an integer. Though it's possible it could be something like a long double or floating-point number.
* **Validity Check:** Even before knowing its purpose, I'd instinctively consider its possible validity. Is this a number that could exist in my known systems, given any associated size restrictions? Is it a reasonable range for a given ID schema?
Essentially, I'm immediately running a quick mental diagnostic, trying to categorize it and begin forming hypotheses about its meaning and potential significance. My expert knowledge then comes into play; the better I know the data and systems, the more quickly I'd be able to assess this number's place in whatever I'm currently dealing with.
|
| 27/2 |
2026/08/12 11:54 |
tool |
git-show |
0m
Args:
Results: map[Output:commit 536395260582be7443b0b35b0bbb89ffe3947f62
Author: Joao Martins <joao.m.martins@oracle.com>
Date: Thu Mar 3 15:41:22 2022 +0000
KVM: x86/xen: handle PV timers oneshot mode
If the guest has offloaded the timer virq, handle the following
hypercalls for programming the timer:
VCPUOP_set_singleshot_timer
VCPUOP_stop_singleshot_timer
set_timer_op(timestamp_ns)
The event channel corresponding to the timer virq is then used to inject
events once timer deadlines are met. For now we back the PV timer with
hrtimer.
[ dwmw2: Add save/restore, 32-bit compat mode, immediate delivery,
don't check timer in kvm_vcpu_has_event() ]
Signed-off-by: Joao Martins <joao.m.martins@oracle.com>
Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Message-Id: <20220303154127.202856-13-dwmw2@infradead.org>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index 9e3408542b41..8193d5285544 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -614,6 +614,10 @@ struct kvm_vcpu_xen {
u64 runstate_times[4];
unsigned long evtchn_pending_sel;
u32 vcpu_id; /* The Xen / ACPI vCPU ID */
+ u32 timer_virq;
+ u64 timer_expires; /* In guest epoch */
+ atomic_t timer_pending;
+ struct hrtimer timer;
};
struct kvm_vcpu_arch {
diff --git a/arch/x86/kvm/irq.c b/arch/x86/kvm/irq.c
index 172b05343cfd..f371f1292ca3 100644
--- a/arch/x86/kvm/irq.c
+++ b/arch/x86/kvm/irq.c
@@ -22,10 +22,14 @@
*/
int kvm_cpu_has_pending_timer(struct kvm_vcpu *vcpu)
{
+ int r = 0;
+
if (lapic_in_kernel(vcpu))
- return apic_has_pending_timer(vcpu);
+ r = apic_has_pending_timer(vcpu);
+ if (kvm_xen_timer_enabled(vcpu))
+ r += kvm_xen_has_pending_timer(vcpu);
- return 0;
+ return r;
}
EXPORT_SYMBOL(kvm_cpu_has_pending_timer);
@@ -143,6 +147,8 @@ void kvm_inject_pending_timer_irqs(struct kvm_vcpu *vcpu)
{
if (lapic_in_kernel(vcpu))
kvm_inject_apic_timer_irqs(vcpu);
+ if (kvm_xen_timer_enabled(vcpu))
+ kvm_xen_inject_timer_irqs(vcpu);
}
EXPORT_SYMBOL_GPL(kvm_inject_pending_timer_irqs);
diff --git a/arch/x86/kvm/xen.c b/arch/x86/kvm/xen.c
index 244cf3cd858a..0e1595dc8c5f 100644
--- a/arch/x86/kvm/xen.c
+++ b/arch/x86/kvm/xen.c
@@ -23,6 +23,7 @@
#include "trace.h"
+static int kvm_xen_set_evtchn(struct kvm_xen_evtchn *xe, struct kvm *kvm);
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);
@@ -108,6 +109,66 @@ static int kvm_xen_shared_info_init(struct kvm *kvm, gfn_t gfn)
return ret;
}
+void kvm_xen_inject_timer_irqs(struct kvm_vcpu *vcpu)
+{
+ if (atomic_read(&vcpu->arch.xen.timer_pending) > 0) {
+ struct kvm_xen_evtchn e;
+
+ e.vcpu_id = vcpu->vcpu_id;
+ e.vcpu_idx = vcpu->vcpu_idx;
+ e.port = vcpu->arch.xen.timer_virq;
+ e.priority = KVM_IRQ_ROUTING_XEN_EVTCHN_PRIO_2LEVEL;
+
+ kvm_xen_set_evtchn(&e, vcpu->kvm);
+
+ vcpu->arch.xen.timer_expires = 0;
+ atomic_set(&vcpu->arch.xen.timer_pending, 0);
+ }
+}
+
+static enum hrtimer_restart xen_timer_callback(struct hrtimer *timer)
+{
+ struct kvm_vcpu *vcpu = container_of(timer, struct kvm_vcpu,
+ arch.xen.timer);
+ if (atomic_read(&vcpu->arch.xen.timer_pending))
+ return HRTIMER_NORESTART;
+
+ atomic_inc(&vcpu->arch.xen.timer_pending);
+ kvm_make_request(KVM_REQ_UNBLOCK, vcpu);
+ kvm_vcpu_kick(vcpu);
+
+ return HRTIMER_NORESTART;
+}
+
+static void kvm_xen_start_timer(struct kvm_vcpu *vcpu, u64 guest_abs, s64 delta_ns)
+{
+ atomic_set(&vcpu->arch.xen.timer_pending, 0);
+ vcpu->arch.xen.timer_expires = guest_abs;
+
+ if (delta_ns <= 0) {
+ xen_timer_callback(&vcpu->arch.xen.timer);
+ } else {
+ ktime_t ktime_now = ktime_get();
+ hrtimer_start(&vcpu->arch.xen.timer,
+ ktime_add_ns(ktime_now, delta_ns),
+ HRTIMER_MODE_ABS_HARD);
+ }
+}
+
+static void kvm_xen_stop_timer(struct kvm_vcpu *vcpu)
+{
+ hrtimer_cancel(&vcpu->arch.xen.timer);
+ vcpu->arch.xen.timer_expires = 0;
+ atomic_set(&vcpu->arch.xen.timer_pending, 0);
+}
+
+static void kvm_xen_init_timer(struct kvm_vcpu *vcpu)
+{
+ hrtimer_init(&vcpu->arch.xen.timer, CLOCK_MONOTONIC,
+ HRTIMER_MODE_ABS_HARD);
+ vcpu->arch.xen.timer.function = xen_timer_callback;
+}
+
static void kvm_xen_update_runstate(struct kvm_vcpu *v, int state)
{
struct kvm_vcpu_xen *vx = &v->arch.xen;
@@ -612,6 +673,28 @@ int kvm_xen_vcpu_set_attr(struct kvm_vcpu *vcpu, struct kvm_xen_vcpu_attr *data)
}
break;
+ case KVM_XEN_VCPU_ATTR_TYPE_TIMER:
+ if (data->u.timer.port) {
+ if (data->u.timer.priority != KVM_IRQ_ROUTING_XEN_EVTCHN_PRIO_2LEVEL) {
+ r = -EINVAL;
+ break;
+ }
+ vcpu->arch.xen.timer_virq = data->u.timer.port;
+ kvm_xen_init_timer(vcpu);
+
+ /* Restart the timer if it's set */
+ if (data->u.timer.expires_ns)
+ kvm_xen_start_timer(vcpu, data->u.timer.expires_ns,
+ data->u.timer.expires_ns -
+ get_kvmclock_ns(vcpu->kvm));
+ } else if (kvm_xen_timer_enabled(vcpu)) {
+ kvm_xen_stop_timer(vcpu);
+ vcpu->arch.xen.timer_virq = 0;
+ }
+
+ r = 0;
+ break;
+
default:
break;
}
@@ -692,6 +775,13 @@ int kvm_xen_vcpu_get_attr(struct kvm_vcpu *vcpu, struct kvm_xen_vcpu_attr *data)
r = 0;
break;
+ case KVM_XEN_VCPU_ATTR_TYPE_TIMER:
+ data->u.timer.port = vcpu->arch.xen.timer_virq;
+ data->u.timer.priority = KVM_IRQ_ROUTING_XEN_EVTCHN_PRIO_2LEVEL;
+ data->u.timer.expires_ns = vcpu->arch.xen.timer_expires;
+ r = 0;
+ break;
+
default:
break;
}
@@ -827,6 +917,112 @@ static bool kvm_xen_hcall_sched_op(struct kvm_vcpu *vcpu, int cmd, u64 param, u6
return false;
}
+struct compat_vcpu_set_singleshot_timer {
+ uint64_t timeout_abs_ns;
+ uint32_t flags;
+} __attribute__((packed));
+
+static bool kvm_xen_hcall_vcpu_op(struct kvm_vcpu *vcpu, bool longmode, int cmd,
+ int vcpu_id, u64 param, u64 *r)
+{
+ struct vcpu_set_singleshot_timer oneshot;
+ s64 delta;
+ gpa_t gpa;
+ int idx;
+
+ if (!kvm_xen_timer_enabled(vcpu))
+ return false;
+
+ switch (cmd) {
+ case VCPUOP_set_singleshot_timer:
+ if (vcpu->arch.xen.vcpu_id != vcpu_id) {
+ *r = -EINVAL;
+ return true;
+ }
+ idx = srcu_read_lock(&vcpu->kvm->srcu);
+ gpa = kvm_mmu_gva_to_gpa_system(vcpu, param, NULL);
+ srcu_read_unlock(&vcpu->kvm->srcu, idx);
+
+ /*
+ * The only difference for 32-bit compat is the 4 bytes of
+ * padding after the interesting part of the structure. So
+ * for a faithful emulation of Xen we have to *try* to copy
+ * the padding and return -EFAULT if we can't. Otherwise we
+ * might as well just have copied the 12-byte 32-bit struct.
+ */
+ BUILD_BUG_ON(offsetof(struct compat_vcpu_set_singleshot_timer, timeout_abs_ns) !=
+ offsetof(struct vcpu_set_singleshot_timer, timeout_abs_ns));
+ BUILD_BUG_ON(sizeof_field(struct compat_vcpu_set_singleshot_timer, timeout_abs_ns) !=
+ sizeof_field(struct vcpu_set_singleshot_timer, timeout_abs_ns));
+ BUILD_BUG_ON(offsetof(struct compat_vcpu_set_singleshot_timer, flags) !=
+ offsetof(struct vcpu_set_singleshot_timer, flags));
+ BUILD_BUG_ON(sizeof_field(struct compat_vcpu_set_singleshot_timer, flags) !=
+ sizeof_field(struct vcpu_set_singleshot_timer, flags));
+
+ if (!gpa ||
+ kvm_vcpu_read_guest(vcpu, gpa, &oneshot, longmode ? sizeof(oneshot) :
+ sizeof(struct compat_vcpu_set_singleshot_timer))) {
+ *r = -EFAULT;
+ return true;
+ }
+
+ delta = oneshot.timeout_abs_ns - get_kvmclock_ns(vcpu->kvm);
+ if ((oneshot.flags & VCPU_SSHOTTMR_future) && delta < 0) {
+ *r = -ETIME;
+ return true;
+ }
+
+ kvm_xen_start_timer(vcpu, oneshot.timeout_abs_ns, delta);
+ *r = 0;
+ return true;
+
+ case VCPUOP_stop_singleshot_timer:
+ if (vcpu->arch.xen.vcpu_id != vcpu_id) {
+ *r = -EINVAL;
+ return true;
+ }
+ kvm_xen_stop_timer(vcpu);
+ *r = 0;
+ return true;
+ }
+
+ return false;
+}
+
+static bool kvm_xen_hcall_set_timer_op(struct kvm_vcpu *vcpu, uint64_t timeout,
+ u64 *r)
+{
+ if (!kvm_xen_timer_enabled(vcpu))
+ return false;
+
+ if (timeout) {
+ uint64_t guest_now = get_kvmclock_ns(vcpu->kvm);
+ int64_t delta = timeout - guest_now;
+
+ /* Xen has a 'Linux workaround' in do_set_timer_op() which
+ * checks for negative absolute timeout values (caused by
+ * integer overflow), and for values about 13 days in the
+ * future (2^50ns) which would be caused by jiffies
+ * overflow. For those cases, it sets the timeout 100ms in
+ * the future (not *too* soon, since if a guest really did
+ * set a long timeout on purpose we don't want to keep
+ * churning CPU time by waking it up).
+ */
+ if (unlikely((int64_t)timeout < 0 ||
+ (delta > 0 && (uint32_t) (delta >> 50) != 0))) {
+ delta = 100 * NSEC_PER_MSEC;
+ timeout = guest_now + delta;
+ }
+
+ kvm_xen_start_timer(vcpu, timeout, delta);
+ } else {
+ kvm_xen_stop_timer(vcpu);
+ }
+
+ *r = 0;
+ return true;
+}
+
int kvm_xen_hypercall(struct kvm_vcpu *vcpu)
{
bool longmode;
@@ -870,6 +1066,18 @@ int kvm_xen_hypercall(struct kvm_vcpu *vcpu)
case __HYPERVISOR_sched_op:
handled = kvm_xen_hcall_sched_op(vcpu, params[0], params[1], &r);
break;
+ case __HYPERVISOR_vcpu_op:
+ handled = kvm_xen_hcall_vcpu_op(vcpu, longmode, params[0], params[1],
+ params[2], &r);
+ break;
+ case __HYPERVISOR_set_timer_op: {
+ u64 timeout = params[0];
+ /* In 32-bit mode, the 64-bit timeout is in two 32-bit params. */
+ if (!longmode)
+ timeout |= params[1] << 32;
+ handled = kvm_xen_hcall_set_timer_op(vcpu, timeout, &r);
+ break;
+ }
default:
break;
}
@@ -1398,6 +1606,9 @@ void kvm_xen_init_vcpu(struct kvm_vcpu *vcpu)
void kvm_xen_destroy_vcpu(struct kvm_vcpu *vcpu)
{
+ if (kvm_xen_timer_enabled(vcpu))
+ kvm_xen_stop_timer(vcpu);
+
kvm_gfn_to_pfn_cache_destroy(vcpu->kvm,
&vcpu->arch.xen.runstate_cache);
kvm_gfn_to_pfn_cache_destroy(vcpu->kvm,
diff --git a/arch/x86/kvm/xen.h b/arch/x86/kvm/xen.h
index 54d587aae85b..ee5c4ae0755c 100644
--- a/arch/x86/kvm/xen.h
+++ b/arch/x86/kvm/xen.h
@@ -62,6 +62,20 @@ static inline bool kvm_xen_has_pending_events(struct kvm_vcpu *vcpu)
vcpu->arch.xen.evtchn_pending_sel;
}
+static inline bool kvm_xen_timer_enabled(struct kvm_vcpu *vcpu)
+{
+ return !!vcpu->arch.xen.timer_virq;
+}
+
+static inline int kvm_xen_has_pending_timer(struct kvm_vcpu *vcpu)
+{
+ if (kvm_xen_hypercall_enabled(vcpu->kvm) && kvm_xen_timer_enabled(vcpu))
+ return atomic_read(&vcpu->arch.xen.timer_pending);
+
+ return 0;
+}
+
+void kvm_xen_inject_timer_irqs(struct kvm_vcpu *vcpu);
#else
static inline int kvm_xen_write_hypercall_page(struct kvm_vcpu *vcpu, u64 data)
{
@@ -107,6 +121,20 @@ static inline bool kvm_xen_has_pending_events(struct kvm_vcpu *vcpu)
{
return false;
}
+
+static inline int kvm_xen_has_pending_timer(struct kvm_vcpu *vcpu)
+{
+ return 0;
+}
+
+static inline void kvm_xen_inject_timer_irqs(struct kvm_vcpu *vcpu)
+{
+}
+
+static inline bool kvm_xen_timer_enabled(struct kvm_vcpu *vcpu)
+{
+ return false;
+}
#endif
int kvm_xen_hypercall(struct kvm_vcpu *vcpu);
diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
index 4b65e9f0a4d9..cb223e425223 100644
--- a/include/uapi/linux/kvm.h
+++ b/include/uapi/linux/kvm.h
@@ -1748,6 +1748,11 @@ struct kvm_xen_vcpu_attr {
__u64 time_offline;
} runstate;
__u32 vcpu_id;
+ struct {
+ __u32 port;
+ __u32 priority;
+ __u64 expires_ns;
+ } timer;
} u;
};
@@ -1760,6 +1765,7 @@ struct kvm_xen_vcpu_attr {
#define KVM_XEN_VCPU_ATTR_TYPE_RUNSTATE_ADJUST 0x5
/* Available with KVM_CAP_XEN_HVM / KVM_XEN_HVM_CONFIG_EVTCHN_SEND */
#define KVM_XEN_VCPU_ATTR_TYPE_VCPU_ID 0x6
+#define KVM_XEN_VCPU_ATTR_TYPE_TIMER 0x7
/* Secure Encrypted Virtualization command */
enum sev_cmd_id {
]
|
| 28/2 |
2026/08/12 11:54 |
llm |
debugger |
0m
Model:
Tokens:
input: 27545
output: 38
thoughts: 112
Thoughts: **Analyzing Potential Lock Contention in KVM/Xen Event Channel Handling**
Okay, so I'm looking at this code and it appears `HRTIMER_MODE_ABS_HARD` was used right from the start. That's a key observation. Now, I'm thinking about the implications of how `kvm_xen_set_evtchn_fast` is implemented. It takes the `gpc->lock`, which is an `rwlock_t`, and also uses `srcu_read_lock(&kvm->srcu)`. The question is, can *both* of those potentially sleep within a PREEMPT_RT environment? I know `rwlocks` can, but `srcu_read_lock` is where I'm a little hazy. I need to be absolutely sure because sleeping in hardirq context is a big no-no, and that's precisely what PREEMPT_RT strives to avoid. Therefore, to clarify my thinking and the correctness of the code, I need to check the actual implementation of `srcu_read_lock`. Let's dig into the source and see what's really happening under the hood.
|
| 29/2 |
2026/08/12 11:54 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:include/linux/srcu.h Name:srcu_read_lock]
Results: map[SourceCode: 266: /**
267: * srcu_read_lock - register a new reader for an SRCU-protected structure.
268: * @ssp: srcu_struct in which to register the new reader.
269: *
270: * Enter an SRCU read-side critical section. Note that SRCU read-side
271: * critical sections may be nested. However, it is illegal to
272: * call anything that waits on an SRCU grace period for the same
273: * srcu_struct, whether directly or indirectly. Please note that
274: * one way to indirectly wait on an SRCU grace period is to acquire
275: * a mutex that is held elsewhere while calling synchronize_srcu() or
276: * synchronize_srcu_expedited().
277: *
278: * The return value from srcu_read_lock() is guaranteed to be
279: * non-negative. This value must be passed unaltered to the matching
280: * srcu_read_unlock(). Note that srcu_read_lock() and the matching
281: * srcu_read_unlock() must occur in the same context, for example, it is
282: * illegal to invoke srcu_read_unlock() in an irq handler if the matching
283: * srcu_read_lock() was invoked in process context. Or, for that matter to
284: * invoke srcu_read_unlock() from one task and the matching srcu_read_lock()
285: * from another.
286: */
287: static inline int srcu_read_lock(struct srcu_struct *ssp)
288: __acquires_shared(ssp)
289: {
290: int retval;
291:
292: srcu_check_read_flavor(ssp, SRCU_READ_FLAVOR_NORMAL);
293: retval = __srcu_read_lock(ssp);
294: srcu_lock_acquire(&ssp->dep_map);
295: return retval;
296: }
SourceFile:include/linux/srcu.h]
|
| 30/2 |
2026/08/12 11:54 |
llm |
debugger |
0m
Model:
Tokens:
input: 28554
output: 39
thoughts: 10
Thoughts: Okay, here's my interpretation of that thought process, assuming I'm well-versed in kernel concurrency:
**Diving into SRCU Read Locking**
Alright, so I'm thinking about `__srcu_read_lock`. That's a fundamental function, isn't it? It's the entry point, the keystone, for a reader in an SRCU (read-copy-update) protected section of the kernel. This is where the magic starts. I'm immediately picturing the critical section. It is likely the place to be, to quickly obtain access to the resource.
This isn't a simple mutex or spinlock though, it's about much more subtle concurrency. It's about letting the writers do their thing without blocking readers, and the readers are almost never blocked. It is about a complex dance between readers and writers.
What's the goal? To let me safely read some data structure that *might* be modified by a writer without forcing the writer to pause or creating a deadlock situation.
I'm probably going to be reading some shared data that's frequently updated, and I need to avoid getting stale or corrupted data. This could be a reference count, configuration information, or some other data structure that can change underneath me.
I need to quickly obtain a read lock, and the function will ensure that any writer that starts updating after my read lock will complete their update before I can proceed. No blocking. That's the beauty of SRCU, right? It’s all about making sure that the old version of the data remains valid for me while the new version is being prepared and eventually published, and that I don’t try to access a data structure that is in transition.
I'm focusing on the low overhead, the minimal impact on writers. SRCU is, ideally, incredibly efficient in this regard. This `__srcu_read_lock` is where it all starts.
|
| 31/2 |
2026/08/12 11:54 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:include/linux/srcu.h Name:__srcu_read_lock]
Results: map[SourceCode: 784: /*
785: * Counts the new reader in the appropriate per-CPU element of the
786: * srcu_struct.
787: * Returns a guaranteed non-negative index that must be passed to the
788: * matching __srcu_read_unlock().
789: */
790: int __srcu_read_lock(struct srcu_struct *ssp)
791: {
792: struct srcu_ctr __percpu *scp = READ_ONCE(ssp->srcu_ctrp);
793:
794: this_cpu_inc(scp->srcu_locks.counter);
795: smp_mb(); /* B */ /* Avoid leaking the critical section. */
796: return __srcu_ptr_to_ctr(ssp, scp);
797: }
SourceFile:kernel/rcu/srcutree.c]
|
| 32/2 |
2026/08/12 11:54 |
llm |
debugger |
0m
Model:
Tokens:
input: 29281
output: 36
thoughts: 760
Thoughts: **Debugging a PREEMPT_RT Kernel Panic**
Okay, so I'm looking at this crash dump, and immediately something jumps out at me. The error says a sleeping function was called from an invalid context, specifically `kernel/locking/spinlock_rt.c:248`. The stack trace shows `srcu_read_lock` being held, and then `rt_read_lock` being called, which is where the sleeping function originates. That `rt_read_lock` is actually `read_lock_irqsave(&gpc->lock, flags);`. Right, `srcu_read_lock` shouldn't sleep, so the problem lies *after* that call.
Now, I recognize that `kvm_xen_set_evtchn_fast` is the function causing the trouble, and it's called from `xen_timer_callback`. That's an hrtimer callback. Ah, and that callback is likely running in hardirq context! In PREEMPT_RT, that's a no-go for taking `gpc->lock`. The code obviously assumes it will *not* be interrupted.
So the obvious question is, how do we get around this? Can we defer timer delivery to a softirq or a worker thread? Or perhaps change the timer mode? I need to check if `HRTIMER_MODE_ABS` is even an option here. Let's see... in the commit `536395260582`, they mention the PV timer is backed by an hrtimer, and currently, `HRTIMER_MODE_ABS_HARD` is used. If we switched to `HRTIMER_MODE_ABS`, that *should* run in softirq context, right?
But wait… PREEMPT_RT. Are softirqs even safe to sleep in? In PREEMPT_RT, softirqs can sleep, because they run in `ksoftirqd` threads or after the end of a hardirq. They can also run within `local_bh_disable()`, which is a sleeping lock, in PREEMPT_RT. So, that means a `rwlock_t` can be acquired in the softirq context? Let me confirm. In PREEMPT_RT, `rwlock_t` is actually an `rt_rw_lock`, which *is* a sleeping lock. It's safe to acquire in task context *and* softirq context because softirqs run in task context in PREEMPT_RT.
Okay, so the hrtimer callback will run in softirq context if it's not `_HARD`. Now, I have to remember how `hrtimer_init` handles different modes.
|
| 33/2 |
2026/08/12 11:55 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:include/linux/hrtimer.h Name:hrtimer_mode]
Results: map[SourceCode: 24: /*
25: * Mode arguments of xxx_hrtimer functions:
26: *
27: * HRTIMER_MODE_ABS - Time value is absolute
28: * HRTIMER_MODE_REL - Time value is relative to now
29: * HRTIMER_MODE_PINNED - Timer is bound to CPU (is only considered
30: * when starting the timer)
31: * HRTIMER_MODE_SOFT - Timer callback function will be executed in
32: * soft irq context
33: * HRTIMER_MODE_HARD - Timer callback function will be executed in
34: * hard irq context even on PREEMPT_RT.
35: * HRTIMER_MODE_LAZY_REARM - Avoid reprogramming if the timer was the
36: * first expiring timer and is moved into the
37: * future. Special mode for the HRTICK timer to
38: * avoid extensive reprogramming of the hardware,
39: * which is expensive in virtual machines. Risks
40: * a pointless expiry, but that's better than
41: * reprogramming on every context switch,
42: */
43: enum hrtimer_mode {
44: HRTIMER_MODE_ABS = 0x00,
45: HRTIMER_MODE_REL = 0x01,
46: HRTIMER_MODE_PINNED = 0x02,
47: HRTIMER_MODE_SOFT = 0x04,
48: HRTIMER_MODE_HARD = 0x08,
49: HRTIMER_MODE_LAZY_REARM = 0x10,
50:
51: HRTIMER_MODE_ABS_PINNED = HRTIMER_MODE_ABS | HRTIMER_MODE_PINNED,
52: HRTIMER_MODE_REL_PINNED = HRTIMER_MODE_REL | HRTIMER_MODE_PINNED,
53:
54: HRTIMER_MODE_ABS_SOFT = HRTIMER_MODE_ABS | HRTIMER_MODE_SOFT,
55: HRTIMER_MODE_REL_SOFT = HRTIMER_MODE_REL | HRTIMER_MODE_SOFT,
56:
57: HRTIMER_MODE_ABS_PINNED_SOFT = HRTIMER_MODE_ABS_PINNED | HRTIMER_MODE_SOFT,
58: HRTIMER_MODE_REL_PINNED_SOFT = HRTIMER_MODE_REL_PINNED | HRTIMER_MODE_SOFT,
59:
60: HRTIMER_MODE_ABS_HARD = HRTIMER_MODE_ABS | HRTIMER_MODE_HARD,
61: HRTIMER_MODE_REL_HARD = HRTIMER_MODE_REL | HRTIMER_MODE_HARD,
62:
63: HRTIMER_MODE_ABS_PINNED_HARD = HRTIMER_MODE_ABS_PINNED | HRTIMER_MODE_HARD,
64: HRTIMER_MODE_REL_PINNED_HARD = HRTIMER_MODE_REL_PINNED | HRTIMER_MODE_HARD,
65: };
SourceFile:include/linux/hrtimer.h]
|
| 34/2 |
2026/08/12 11:55 |
llm |
debugger |
0m
Model:
Tokens:
input: 31507
output: 62
thoughts: 201
Thoughts: **Understanding HRTIMER Modes**
Okay, so `HRTIMER_MODE_HARD` is pretty straightforward; it guarantees execution within a hard interrupt context, even under PREEMPT_RT. But what happens if I just use `HRTIMER_MODE_ABS` without any `_HARD` or `_SOFT` modifiers? In the PREEMPT_RT world, these timers, without the explicit `_HARD` setting, run in the softirq context, specifically the hrtimer softirq. Hmm, so if I switch to `HRTIMER_MODE_ABS`, it might run in a softirq context on PREEMPT_RT, but what about non-RT systems? Will it default to a hardirq context there?
Wait, `HRTIMER_MODE_SOFT` is the explicit request for softirq context. So, if I use `HRTIMER_MODE_ABS`, my understanding is that on a non-RT system it'll be in hardirq, but PREEMPT_RT will push it into the softirq context? Okay, to be absolutely sure, let's take a quick peek at how the `apic_timer` is initialized. I need to be certain about its behavior in both scenarios.
|
| 35/2 |
2026/08/12 11:55 |
tool |
codesearch-find-references |
0m
Error:
Args: map[ContextFile:arch/x86/kvm/lapic.c IncludeSnippetLines:2 Name:hrtimer_init SourceTreePrefix:arch/x86/kvm]
Results: map[References:<nil> TruncatedOutput:false]
|
| 36/2 |
2026/08/12 11:55 |
llm |
debugger |
0m
Model:
Tokens:
input: 32050
output: 35
thoughts: 27
Thoughts: Okay, here's my attempt at summarizing the thought process, as if it were my own, assuming I'm an expert in the field:
**Digging into KVM LAPIC Timing**
Alright, I need to get to the bottom of how the High-Resolution Timer (HRTimer) is interacting with the Local APIC (LAPIC) within the KVM virtualization context, specifically on x86 architectures. My hunch is that I'll find the setup code I'm looking for in `arch/x86/kvm/lapic.c`. Let's start with a focused search within that file using `hrtimer_setup`. That function likely plays a key role in configuring the HRTimer to interact correctly with the LAPIC's timer capabilities, especially in a virtualized environment. This function should be the first place to check if I am dealing with any kind of issues on the x86 architecture. From there, I can begin my investigation.
|
| 37/2 |
2026/08/12 11:55 |
tool |
grepper |
0m
Args: map[Expression:hrtimer_setup.*lapic PathPrefix:arch/x86/kvm]
Results: map[Output:arch/x86/kvm/lapic.c=3102=int kvm_create_lapic(struct kvm_vcpu *vcpu)
--
arch/x86/kvm/lapic.c-3129-
arch/x86/kvm/lapic.c:3130: hrtimer_setup(&apic->lapic_timer.timer, apic_timer_fn, CLOCK_MONOTONIC,
arch/x86/kvm/lapic.c-3131- HRTIMER_MODE_ABS_HARD);
]
|
| 38/2 |
2026/08/12 11:55 |
llm |
debugger |
0m
Model:
Tokens:
input: 32470
output: 43
thoughts: 66
Thoughts: Okay, here's my interpretation of these thoughts, formatted as you requested:
**Investigating `apic_timer` and PREEMPT_RT**
Right, so `apic_timer` is using `HRTIMER_MODE_ABS_HARD`. That immediately triggers a question for me, given my understanding of the system. If it's employing that mode, which is designed for absolute, hard deadlines, how the heck does it manage to avoid the common pitfall of taking sleeping locks under PREEMPT_RT? That's a critical point to understand for real-time performance. This likely hinges on the specifics of its implementation.
My instinct is to dive into the core function associated with this timer. I need to see the details. Let's dig into `apic_timer_fn`. That's where I expect to find the crucial details that either confirm or allay my concerns about potential lock contention issues within the PREEMPT_RT environment. This could be a fascinating piece of code.
|
| 39/2 |
2026/08/12 11:55 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:arch/x86/kvm/lapic.c Name:apic_timer_fn]
Results: map[SourceCode:3087: static enum hrtimer_restart apic_timer_fn(struct hrtimer *data)
3088: {
3089: struct kvm_timer *ktimer = container_of(data, struct kvm_timer, timer);
3090: struct kvm_lapic *apic = container_of(ktimer, struct kvm_lapic, lapic_timer);
3091:
3092: apic_timer_expired(apic, true);
3093:
3094: if (lapic_is_periodic(apic) && !WARN_ON_ONCE(!apic->lapic_timer.period)) {
3095: advance_periodic_target_expiration(apic);
3096: hrtimer_set_expires(&ktimer->timer, ktimer->target_expiration);
3097: return HRTIMER_RESTART;
3098: } else
3099: return HRTIMER_NORESTART;
3100: }
SourceFile:arch/x86/kvm/lapic.c]
|
| 40/2 |
2026/08/12 11:55 |
llm |
debugger |
0m
Model:
Tokens:
input: 33087
output: 43
thoughts: 9
Thoughts: Okay, here's my interpretation of that.
**My Analysis of `apic_timer_expired`**
Right, so I'm looking at this `apic_timer_expired` function, and my initial thought is, "Okay, this is clearly tied to the Advanced Programmable Interrupt Controller (APIC) and timer interrupts." Given my experience, I'm immediately thinking about how the APIC is the cornerstone of interrupt management on modern systems, particularly in multi-processor environments. So, this function is central to handling timer events signaled by the hardware.
My brain is immediately racing through the possible implications: is this a periodic timer, a one-shot, or something more complex? What's the context? Is this the system timer, a per-CPU timer, or something application-specific? I need to understand the larger architecture to fully grasp its significance.
I'm mentally mapping the control flow. What happens *before* this function is called? What interrupts lead to its execution? What tasks are likely blocked or resumed within this function? I'm already anticipating some of the key areas to probe for performance bottlenecks or potential race conditions. Is this function running in interrupt context? If so, what are the implications for locking and preemption?
I'm expecting this to involve reading or writing to APIC registers, and probably also interacting with data structures related to task scheduling, context switching, or possibly even power management. I'm already formulating questions in my head: What is the timer's frequency, and how is it programmed? What happens if this function is interrupted? What happens if this function takes too long?
The real questions are: what exactly is it meant to do, and is it doing it well? I am excited to read the code and dig into the detail.
|
| 41/2 |
2026/08/12 11:55 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:arch/x86/kvm/lapic.c Name:apic_timer_expired]
Results: map[SourceCode:2044: static void apic_timer_expired(struct kvm_lapic *apic, bool from_timer_fn)
2045: {
2046: struct kvm_vcpu *vcpu = apic->vcpu;
2047: struct kvm_timer *ktimer = &apic->lapic_timer;
2048:
2049: if (atomic_read(&apic->lapic_timer.pending))
2050: return;
2051:
2052: if (apic_lvtt_tscdeadline(apic) || ktimer->hv_timer_in_use)
2053: ktimer->expired_tscdeadline = ktimer->tscdeadline;
2054:
2055: if (!from_timer_fn && apic->apicv_active && vcpu->wants_to_run) {
2056: WARN_ON(kvm_get_running_vcpu() != vcpu);
2057: kvm_apic_inject_pending_timer_irqs(apic);
2058: return;
2059: }
2060:
2061: if (kvm_use_posted_timer_interrupt(apic->vcpu)) {
2062: /*
2063: * Ensure the guest's timer has truly expired before posting an
2064: * interrupt. Open code the relevant checks to avoid querying
2065: * lapic_timer_int_injected(), which will be false since the
2066: * interrupt isn't yet injected. Waiting until after injecting
2067: * is not an option since that won't help a posted interrupt.
2068: */
2069: if (vcpu->arch.apic->lapic_timer.expired_tscdeadline &&
2070: vcpu->arch.apic->lapic_timer.timer_advance_ns)
2071: __kvm_wait_lapic_expire(vcpu);
2072: kvm_apic_inject_pending_timer_irqs(apic);
2073: return;
2074: }
2075:
2076: atomic_inc(&apic->lapic_timer.pending);
2077: kvm_make_request(KVM_REQ_UNBLOCK, vcpu);
2078: if (from_timer_fn)
2079: kvm_vcpu_kick(vcpu);
2080: }
SourceFile:arch/x86/kvm/lapic.c]
|
| 42/2 |
2026/08/12 11:55 |
llm |
debugger |
0m
Model:
Tokens:
input: 34213
output: 24
thoughts: 107
Thoughts: **Analyzing the APIC Timer Expiration Mechanism**
Okay, so `apic_timer_expired` is, surprisingly, pretty straightforward. It just flags the timer as pending with `apic->lapic_timer.pending`, then issues a `KVM_REQ_UNBLOCK` request. Finally, it kicks the vCPU with `kvm_vcpu_kick(vcpu)`. Seems like it doesn't try to directly inject the interrupt from *within* the timer callback itself, which is interesting. It offloads the interrupt handling.
Wait a second… My memory's kicking in here. I could have sworn that `xen_timer_callback`, back in the day, used to handle this differently. It feels like there was a period when it *did* try to do the interrupt injection directly. I should go back and verify. Let me find that commit where the behavior likely changed, before `77c9b9dea4fb`. Going to need to dig into the older `xen_timer_callback` code.
|
| 43/2 |
2026/08/12 11:55 |
tool |
git-show |
0m
Args:
Results: map[Output:commit 77c9b9dea4fb3e51e0d850db7f21cb1156d987bd
Author: David Woodhouse <dwmw@amazon.co.uk>
Date: Sat Sep 30 14:58:35 2023 +0100
KVM: x86/xen: Use fast path for Xen timer delivery
Most of the time there's no need to kick the vCPU and deliver the timer
event through kvm_xen_inject_timer_irqs(). Use kvm_xen_set_evtchn_fast()
directly from the timer callback, and only fall back to the slow path if
delivering the timer would block, i.e. if kvm_xen_set_evtchn_fast()
returns -EWOULDBLOCK. If delivery fails for any other reason, do nothing
and just let it fail silently, as that is what the slow path would end up
doing anyways.
This gives a significant improvement in timer latency testing (using
nanosleep() for various periods and then measuring the actual time
elapsed).
However, there was a reason[1] the fast path was dropped when this support
was first added. The current code holds vcpu->mutex for all operations on
the kvm->arch.timer_expires field, and the fast path introduces a
potential race condition. Avoid that race by ensuring the hrtimer is
(temporarily) cancelled before making changes in kvm_xen_start_timer(),
and also when reading the values out for KVM_XEN_VCPU_ATTR_TYPE_TIMER.
[1] https://lore.kernel.org/kvm/846caa99-2e42-4443-1070-84e49d2f11d2@redhat.com
Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>
Reviewed-by: Paul Durrant <paul@xen.org>
Link: https://lore.kernel.org/r/f21ee3bd852761e7808240d4ecaec3013c649dc7.camel@infradead.org
[sean: massage changelog]
Signed-off-by: Sean Christopherson <seanjc@google.com>
diff --git a/arch/x86/kvm/xen.c b/arch/x86/kvm/xen.c
index 40edf4d1974c..75586da134b3 100644
--- a/arch/x86/kvm/xen.c
+++ b/arch/x86/kvm/xen.c
@@ -134,9 +134,23 @@ static enum hrtimer_restart xen_timer_callback(struct hrtimer *timer)
{
struct kvm_vcpu *vcpu = container_of(timer, struct kvm_vcpu,
arch.xen.timer);
+ struct kvm_xen_evtchn e;
+ int rc;
+
if (atomic_read(&vcpu->arch.xen.timer_pending))
return HRTIMER_NORESTART;
+ e.vcpu_id = vcpu->vcpu_id;
+ e.vcpu_idx = vcpu->vcpu_idx;
+ 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);
+ if (rc != -EWOULDBLOCK) {
+ vcpu->arch.xen.timer_expires = 0;
+ return HRTIMER_NORESTART;
+ }
+
atomic_inc(&vcpu->arch.xen.timer_pending);
kvm_make_request(KVM_REQ_UNBLOCK, vcpu);
kvm_vcpu_kick(vcpu);
@@ -146,6 +160,14 @@ static enum hrtimer_restart xen_timer_callback(struct hrtimer *timer)
static void kvm_xen_start_timer(struct kvm_vcpu *vcpu, u64 guest_abs, s64 delta_ns)
{
+ /*
+ * Avoid races with the old timer firing. Checking timer_expires
+ * to avoid calling hrtimer_cancel() will only have false positives
+ * so is fine.
+ */
+ if (vcpu->arch.xen.timer_expires)
+ hrtimer_cancel(&vcpu->arch.xen.timer);
+
atomic_set(&vcpu->arch.xen.timer_pending, 0);
vcpu->arch.xen.timer_expires = guest_abs;
@@ -1019,9 +1041,36 @@ int kvm_xen_vcpu_get_attr(struct kvm_vcpu *vcpu, struct kvm_xen_vcpu_attr *data)
break;
case KVM_XEN_VCPU_ATTR_TYPE_TIMER:
+ /*
+ * Ensure a consistent snapshot of state is captured, with a
+ * timer either being pending, or the event channel delivered
+ * to the corresponding bit in the shared_info. Not still
+ * lurking in the timer_pending flag for deferred delivery.
+ * Purely as an optimisation, if the timer_expires field is
+ * zero, that means the timer isn't active (or even in the
+ * timer_pending flag) and there is no need to cancel it.
+ */
+ if (vcpu->arch.xen.timer_expires) {
+ hrtimer_cancel(&vcpu->arch.xen.timer);
+ kvm_xen_inject_timer_irqs(vcpu);
+ }
+
data->u.timer.port = vcpu->arch.xen.timer_virq;
data->u.timer.priority = KVM_IRQ_ROUTING_XEN_EVTCHN_PRIO_2LEVEL;
data->u.timer.expires_ns = vcpu->arch.xen.timer_expires;
+
+ /*
+ * The hrtimer may trigger and raise the IRQ immediately,
+ * while the returned state causes it to be set up and
+ * raised again on the destination system after migration.
+ * That's fine, as the guest won't even have had a chance
+ * to run and handle the interrupt. Asserting an already
+ * pending event channel is idempotent.
+ */
+ if (vcpu->arch.xen.timer_expires)
+ hrtimer_start_expires(&vcpu->arch.xen.timer,
+ HRTIMER_MODE_ABS_HARD);
+
r = 0;
break;
]
|
| 44/2 |
2026/08/12 11:55 |
llm |
debugger |
0m
Model:
Tokens:
input: 36291
output: 43
thoughts: 1089
Thoughts: **Analyzing a KVM/Xen Interaction in PREEMPT_RT**
Okay, so this commit `77c9b9dea4fb` introduced `kvm_xen_set_evtchn_fast` and it's being called from `xen_timer_callback`. The problem is, `kvm_xen_set_evtchn_fast` takes `gpc->lock`, which is a `rwlock_t`, and also takes a `srcu_read_lock`. Now, in PREEMPT_RT, a sleeping lock. Therefore, `kvm_xen_set_evtchn_fast` can't be called from this timer callback because it's a `HRTIMER_MODE_ABS_HARD` timer.
But wait, `kvm_xen_set_evtchn_fast` also uses `srcu_read_lock(&kvm->srcu)`. I need to confirm if `srcu_read_lock` is permissible in a hardirq context under PREEMPT_RT. Actually, `srcu_read_lock` internally uses `srcu_lock_acquire`, which is lockdep annotated. The lockdep splat indicates that a sleeping function is being called from an invalid context, specifically `rt_read_lock`, which is derived from `read_lock_irqsave(&gpc->lock, flags)`. So while `srcu_read_lock` itself doesn't sleep, the `read_lock_irqsave` on the `rwlock_t` does sleep in PREEMPT_RT.
So, how do we fix this? My immediate thoughts are:
1. We can check for `PREEMPT_RT` and skip the fast path in `xen_timer_callback`.
2. Alternatively, maybe we change the timer to `HRTIMER_MODE_ABS` (softirq) instead.
However, if we change the timer to `HRTIMER_MODE_ABS`, it would now be running in a softirq context. Is there any inherent reason it must be a hardirq context? I should check `apic_timer` for reference. It uses `HRTIMER_MODE_ABS_HARD`. Changing it might increase latency... But wait, `kvm_xen_set_evtchn_fast` is called from `xen_timer_callback`. Disabling the fast path in PREEMPT_RT would cause it to fall back to the slow path, which sets `timer_pending` and kicks the vCPU. That slow path is, specifically:
```c
atomic_inc(&vcpu->arch.xen.timer_pending);
kvm_make_request(KVM_REQ_UNBLOCK, vcpu);
kvm_vcpu_kick(vcpu);
```
This is *exactly* what `apic_timer_expired` does! And `apic_timer_expired` doesn't directly inject the interrupt from the timer callback. It only injects directly if `!from_timer_fn` or if `kvm_use_posted_timer_interrupt`. But from the timer callback (`from_timer_fn == true`), it just sets pending and kicks the vCPU. So the fast path is just an optimization.
Can we just disable it on PREEMPT_RT, then? I should do something like:
```c
#ifdef CONFIG_PREEMPT_RT
rc = -EWOULDBLOCK;
#else
rc = kvm_xen_set_evtchn_fast(&e, vcpu->kvm);
#endif
```
Or maybe use `IS_ENABLED(CONFIG_PREEMPT_RT)`.
Wait, is `read_lock_irqsave` the only problem? What about `srcu_read_lock`? In PREEMPT_RT, it doesn't sleep; it just disables preemption? No, `__srcu_read_lock` just increments a per-cpu counter. But that `read_lock_irqsave` on `rwlock_t` definitely sleeps in PREEMPT_RT.
Is there any other lock being taken? Yes, the `gpc->lock` that is an `rwlock_t` is taken by `kvm_xen_set_evtchn_fast`. Wait, it's initialized in `kvm_gpc_init`. Could we make `gpc->lock` a raw spinlock instead? Let's check `gfn_to_pfn_cache`.
|
| 45/2 |
2026/08/12 11:55 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:include/linux/kvm_types.h Name:gfn_to_pfn_cache]
Results: map[SourceCode: 84: struct gfn_to_pfn_cache {
85: u64 generation;
86: gpa_t gpa;
87: unsigned long uhva;
88: struct kvm_memory_slot *memslot;
89: struct kvm *kvm;
90: struct list_head list;
91: rwlock_t lock;
92: struct mutex refresh_lock;
93: void *khva;
94: kvm_pfn_t pfn;
95: bool active;
96: bool valid;
97: };
SourceFile:include/linux/kvm_types.h]
|
| 46/2 |
2026/08/12 11:55 |
llm |
debugger |
0m
Model:
Tokens:
input: 38593
output: 68
thoughts: 71
Thoughts: **Analyzing Cache Protection in PREEMPT_RT**
Okay, so I'm looking at this `gpc->lock`. It's an `rwlock_t`, which suggests it's being used to protect the cache. Now, the question that springs to mind, given my expertise in these real-time systems, is whether we can simply disable the "fast path" within the PREEMPT_RT environment. This could potentially simplify things and reduce complexity.
Before I go too far down that rabbit hole, I need to be absolutely certain of the scope of this. I'll need to meticulously check to see if `kvm_xen_set_evtchn_fast` is called from *any* hard interrupt context anywhere else. Knowing if this is limited in scope is critical to properly asses what can be done here.
|
| 47/2 |
2026/08/12 11:55 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:arch/x86/kvm/xen.c IncludeSnippetLines:2 Name:kvm_xen_set_evtchn_fast SourceTreePrefix:arch/x86/kvm]
Results: map[References:[map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:kvm_arch_set_irq_inatomic SourceFile:arch/x86/kvm/irq.c SourceLine:270 SourceSnippet: 268: return -1;
269:
270: return kvm_xen_set_evtchn_fast(&e->xen_evtchn, kvm);
271: #endif
272: default:
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:kvm_xen_set_evtchn SourceFile:arch/x86/kvm/xen.c SourceLine:1906 SourceSnippet:1904: int rc;
1905:
1906: rc = kvm_xen_set_evtchn_fast(xe, kvm);
1907: if (rc != -EWOULDBLOCK)
1908: return rc;
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:kvm_xen_set_evtchn SourceFile:arch/x86/kvm/xen.c SourceLine:1940 SourceSnippet:1938: int idx;
1939:
1940: rc = kvm_xen_set_evtchn_fast(xe, kvm);
1941: if (rc != -EWOULDBLOCK)
1942: break;
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:xen_timer_callback SourceFile:arch/x86/kvm/xen.c SourceLine:140 SourceSnippet: 138: e.priority = KVM_IRQ_ROUTING_XEN_EVTCHN_PRIO_2LEVEL;
139:
140: rc = kvm_xen_set_evtchn_fast(&e, vcpu->kvm);
141: if (rc != -EWOULDBLOCK) {
142: vcpu->arch.xen.timer_expires = 0;
]] TruncatedOutput:false]
|
| 48/2 |
2026/08/12 11:55 |
llm |
debugger |
0m
Model:
Tokens:
input: 39498
output: 49
thoughts: 51
Thoughts: Okay, let's see...
**Diving into the KVM Interrupt Handling**
Hold on a second...it just clicked. This `kvm_arch_set_irq_inatomic` function, it's not just doing what I initially assumed. I need to refresh my memory, this is important for the Xen hypervisor, so I remember its context. It also calls `kvm_xen_set_evtchn_fast`! I need to trace this quickly.
Right, time to dig into the implementation of `kvm_arch_set_irq_inatomic`. This is a critical point of interoperation, I need to understand this to make sure the Xen hypervisor is being correctly integrated here. Let's see what that function is actually doing. There's probably more going on here than meets the eye initially. I have to think about the atomicity and how it affects the event channel setting with Xen.
|
| 49/2 |
2026/08/12 11:55 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:arch/x86/kvm/irq.c Name:kvm_arch_set_irq_inatomic]
Results: map[SourceCode: 241: int kvm_arch_set_irq_inatomic(struct kvm_kernel_irq_routing_entry *e,
242: struct kvm *kvm, int irq_source_id, int level,
243: bool line_status)
244: {
245: struct kvm_lapic_irq irq;
246: int r;
247:
248: switch (e->type) {
249: #ifdef CONFIG_KVM_HYPERV
250: case KVM_IRQ_ROUTING_HV_SINT:
251: return kvm_hv_synic_set_irq(e, kvm, irq_source_id, level,
252: line_status);
253: #endif
254:
255: case KVM_IRQ_ROUTING_MSI:
256: if (kvm_msi_route_invalid(kvm, e))
257: return -EINVAL;
258:
259: kvm_msi_to_lapic_irq(kvm, e, &irq);
260:
261: if (kvm_irq_delivery_to_apic_fast(kvm, NULL, &irq, &r))
262: return r;
263: break;
264:
265: #ifdef CONFIG_KVM_XEN
266: case KVM_IRQ_ROUTING_XEN_EVTCHN:
267: if (!level)
268: return -1;
269:
270: return kvm_xen_set_evtchn_fast(&e->xen_evtchn, kvm);
271: #endif
272: default:
273: break;
274: }
275:
276: return -EWOULDBLOCK;
277: }
SourceFile:arch/x86/kvm/irq.c]
|
| 50/2 |
2026/08/12 11:55 |
llm |
debugger |
0m
Model:
Tokens:
input: 40393
output: 62
thoughts: 175
Thoughts: **Analyzing a Potential Preemptibility Issue in KVM/Xen Interaction**
Okay, so I'm looking at this `kvm_arch_set_irq_inatomic` function. It seems like it's designed to be called from a hard interrupt context – something like an `irqfd` wakeup, which makes sense. The issue, though, is that this function calls `kvm_xen_set_evtchn_fast`. And, *that* function takes a lock, `gpc->lock`, which is an `rwlock_t`. Under PREEMPT_RT, that's a recipe for disaster! If `kvm_xen_set_evtchn_fast` tries to sleep while holding that lock in an atomic context, we're going to have a serious problem. That means `kvm_xen_set_evtchn_fast` *cannot* be called from an atomic context when PREEMPT_RT is enabled.
Wait a second… isn't `kvm_arch_set_irq_inatomic` *specifically* meant to be called from atomic context? That was the whole point of it being "inatomic", so to speak. This requires a double-check. I need to trace *exactly* where `kvm_arch_set_irq_inatomic` is called to confirm its intended calling context. I need to be absolutely sure about this before I start suggesting any fixes; this could be a race condition waiting to happen.
|
| 51/2 |
2026/08/12 11:55 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:arch/x86/kvm/irq.c IncludeSnippetLines:2 Name:kvm_arch_set_irq_inatomic SourceTreePrefix:]
Results: map[References:[map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:irqfd_wakeup SourceFile:virt/kvm/eventfd.c SourceLine:236 SourceSnippet: 234: */
235: if (unlikely(!irqfd_is_active(irqfd)) ||
236: kvm_arch_set_irq_inatomic(&irq, kvm,
237: KVM_USERSPACE_IRQ_SOURCE_ID, 1,
238: false) == -EWOULDBLOCK)
]] TruncatedOutput:false]
|
| 52/2 |
2026/08/12 11:55 |
llm |
debugger |
0m
Model:
Tokens:
input: 41123
output: 41
thoughts: 1183
Thoughts: **Analyzing Lock Usage in PREEMPT_RT for KVM/Xen Event Channel Fast Path**
Okay, so I'm looking at this `irqfd_wakeup` function. It's called from a waitqueue wakeup, and that can happen within a hard interrupt context, which is the first thing that sets off alarm bells. The situation is that `kvm_arch_set_irq_inatomic` can return `-EWOULDBLOCK`, and when it does, `irqfd_wakeup` schedules a workqueue. This means `kvm_arch_set_irq_inatomic` *must* be allowed to return that error if it can't handle the interrupt atomically.
Now, specifically regarding PREEMPT_RT, the `rwlock_t` type is a sleeping lock, which complicates things. `kvm_xen_set_evtchn_fast` probably needs to return `-EWOULDBLOCK` if called from an atomic context. Otherwise, perhaps we should disable the fast path entirely under PREEMPT_RT. But wait... `kvm_irq_delivery_to_apic_fast` uses `rcu_read_lock`, which is okay in PREEMPT_RT, it doesn't sleep.
However, `kvm_xen_set_evtchn_fast` uses `read_lock_irqsave(&gpc->lock, flags);` and `srcu_read_lock(&kvm->srcu);`. Now, both those locks, especially in a PREEMPT_RT kernel, potentially sleep. Although, wait... `srcu_read_lock` doesn't sleep in PREEMPT_RT, it just increments a counter. The lockdep splat is just because it's held while we are acquiring a sleeping lock (`rt_read_lock`), so it is printed in the "locks held" section. The real problem is `read_lock_irqsave(&gpc->lock, flags);`.
Could we use `read_trylock` instead? If it fails, we simply return `-EWOULDBLOCK`. Okay, let's confirm `read_trylock` is even available for `rwlock_t`. Yes, it exists. Excellent!
But, here's the rub: in PREEMPT_RT, `read_trylock` on `rwlock_t` maps to `rt_read_trylock`. Does `rt_read_trylock` sleep? No, it's a try lock, so it returns 0 (false) if it would sleep. However, even if it succeeds, it *still* acquires a sleeping lock. That's a huge flag.
Can you acquire a sleeping lock in a hardirq context, even via trylock, in PREEMPT_RT? You cannot, because the unlock operation might need to wake up waiters, which means the program might have to take the `wait_lock` (a raw spinlock) which is fine, but the lock itself is a sleeping lock.
In fact, the use of `rt_read_trylock` in hardirq context is illegal if the lock is a sleeping lock. Let's see if `rt_read_trylock` is allowed in hardirq. In PREEMPT_RT, `rt_read_trylock` calls `__rt_rwlock_tryread`, and `in_hardirq()` will be true in that case. Lockdep complains because you might be acquiring a sleeping lock in hardirq context, even via trylock! But no, it's about `rt_read_lock` because it calls `__might_resched`, not `rt_read_trylock`.
Does `rt_read_trylock` call `__might_resched`? No. But even if the program holds a sleeping lock, it might be in a preemptible section, or at least that's what I think. No, `rt_read_trylock` disables preemption. Well, not exactly, it does not. If you are in hardirq context, preemption is already disabled. Lockdep will complain if the program holds a sleeping lock and preemption is disabled when it tries to acquire or unlock it.
It's better not to do it in hardirq context. `kvm_xen_set_evtchn_fast` is called from `xen_timer_callback` (hardirq) and `kvm_arch_set_irq_inatomic` (hardirq). How about just returning `-EWOULDBLOCK` in PREEMPT_RT within the hardirq context? Or perhaps use `IS_ENABLED(CONFIG_PREEMPT_RT) && in_hardirq()`?
One more thing...why is `gpc->lock`
|
| 53/2 |
2026/08/12 11:56 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:virt/kvm/pfncache.c Name:kvm_gpc_init]
Results: map[SourceCode: 385: void kvm_gpc_init(struct gfn_to_pfn_cache *gpc, struct kvm *kvm)
386: {
387: rwlock_init(&gpc->lock);
388: mutex_init(&gpc->refresh_lock);
389:
390: gpc->kvm = kvm;
391: gpc->pfn = KVM_PFN_ERR_FAULT;
392: gpc->gpa = INVALID_GPA;
393: gpc->uhva = KVM_HVA_ERR_BAD;
394: gpc->active = gpc->valid = false;
395: }
SourceFile:virt/kvm/pfncache.c]
|
| 54/2 |
2026/08/12 11:56 |
llm |
debugger |
0m
Model:
Tokens:
input: 43598
output: 52
thoughts: 70
Thoughts: **Examining the Locking Mechanism**
Okay, so I'm looking at this `gpc->lock` and immediately I'm thinking, "Hang on a second, if this thing is an `rwlock_t`, which in a PREEMPT_RT environment means it's a *sleeping* read-write lock, is that really the best choice here?" My experience tells me that sleeping locks, while offering their benefits, can sometimes introduce unnecessary overhead.
I'm wondering if a simpler, faster lock would be sufficient. Could we get away with a `raw_rwlock_t` or even a `raw_spinlock_t`? It’s worth exploring. It might be possible to get better performance by reducing context switching. Now, I need to see *where* this `gpc->lock` is used. That's the next step; I need to understand the access patterns and any potential contention to determine if a more basic lock would maintain integrity. Let's dig into the code...
|
| 55/2 |
2026/08/12 11:56 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:virt/kvm/pfncache.c IncludeSnippetLines:2 Name:lock SourceTreePrefix:virt/kvm]
Results: map[References:<nil> TruncatedOutput:false]
|
| 56/2 |
2026/08/12 11:56 |
llm |
debugger |
0m
Model:
Tokens:
input: 43948
output: 34
thoughts: 12
Thoughts: Okay, here's my interpretation of that thought process, tailored for an expert audience:
**Debugging Reflex**
Hmm, something's not quite right. My initial gut feeling is that I need to delve into a specific area. Instead of broad strokes, I need to pinpoint the likely source. Let me narrow my focus and immediately consider the potential for contention. The phrase `gpc->lock` just popped into my head, which is likely part of the debugging ritual. Let me jump in there and see if this is the cause.
|
| 57/2 |
2026/08/12 11:56 |
tool |
grepper |
0m
Args: map[Expression:gpc->lock PathPrefix:virt/kvm/pfncache.c]
Results: map[Output:virt/kvm/pfncache.c=25=void gfn_to_pfn_cache_invalidate_start(struct kvm *kvm, unsigned long start,
--
virt/kvm/pfncache.c-31- list_for_each_entry(gpc, &kvm->gpc_list, list) {
virt/kvm/pfncache.c:32: read_lock_irq(&gpc->lock);
virt/kvm/pfncache.c-33-
--
virt/kvm/pfncache.c-36- gpc->uhva >= start && gpc->uhva < end) {
virt/kvm/pfncache.c:37: read_unlock_irq(&gpc->lock);
virt/kvm/pfncache.c-38-
--
virt/kvm/pfncache.c-46-
virt/kvm/pfncache.c:47: write_lock_irq(&gpc->lock);
virt/kvm/pfncache.c-48- if (gpc->valid && !is_error_noslot_pfn(gpc->pfn) &&
--
virt/kvm/pfncache.c-50- gpc->valid = false;
virt/kvm/pfncache.c:51: write_unlock_irq(&gpc->lock);
virt/kvm/pfncache.c-52- continue;
--
virt/kvm/pfncache.c-54-
virt/kvm/pfncache.c:55: read_unlock_irq(&gpc->lock);
virt/kvm/pfncache.c-56- }
--
virt/kvm/pfncache.c=127=static inline bool mmu_notifier_retry_cache(struct kvm *kvm, unsigned long mmu_seq)
--
virt/kvm/pfncache.c-136- * Note, it does not matter that mn_active_invalidate_count
virt/kvm/pfncache.c:137: * is not protected by gpc->lock. It is guaranteed to
virt/kvm/pfncache.c:138: * be elevated before the mmu_notifier acquires gpc->lock, and
virt/kvm/pfncache.c-139- * isn't dropped until after mmu_invalidate_seq is updated.
--
virt/kvm/pfncache.c=155=static kvm_pfn_t hva_to_pfn_retry(struct gfn_to_pfn_cache *gpc)
--
virt/kvm/pfncache.c-173-
virt/kvm/pfncache.c:174: lockdep_assert_held_write(&gpc->lock);
virt/kvm/pfncache.c-175-
virt/kvm/pfncache.c-176- /*
virt/kvm/pfncache.c:177: * Invalidate the cache prior to dropping gpc->lock, the gpa=>uhva
virt/kvm/pfncache.c-178- * assets have already been updated and so a concurrent check() from a
--
virt/kvm/pfncache.c-186-
virt/kvm/pfncache.c:187: write_unlock_irq(&gpc->lock);
virt/kvm/pfncache.c-188-
--
virt/kvm/pfncache.c-215- * pfn. Note, kmap() and memremap() can both sleep, so this
virt/kvm/pfncache.c:216: * too must be done outside of gpc->lock!
virt/kvm/pfncache.c-217- */
--
virt/kvm/pfncache.c-227-
virt/kvm/pfncache.c:228: write_lock_irq(&gpc->lock);
virt/kvm/pfncache.c-229-
--
virt/kvm/pfncache.c-250-out_error:
virt/kvm/pfncache.c:251: write_lock_irq(&gpc->lock);
virt/kvm/pfncache.c-252-
--
virt/kvm/pfncache.c=256=static int __kvm_gpc_refresh(struct gfn_to_pfn_cache *gpc, gpa_t gpa, unsigned long uhva)
--
virt/kvm/pfncache.c-271-
virt/kvm/pfncache.c:272: write_lock_irq(&gpc->lock);
virt/kvm/pfncache.c-273-
--
virt/kvm/pfncache.c-357-out_unlock:
virt/kvm/pfncache.c:358: write_unlock_irq(&gpc->lock);
virt/kvm/pfncache.c-359-
--
virt/kvm/pfncache.c=385=void kvm_gpc_init(struct gfn_to_pfn_cache *gpc, struct kvm *kvm)
virt/kvm/pfncache.c-386-{
virt/kvm/pfncache.c:387: rwlock_init(&gpc->lock);
virt/kvm/pfncache.c-388- mutex_init(&gpc->refresh_lock);
--
virt/kvm/pfncache.c=397=static int __kvm_gpc_activate(struct gfn_to_pfn_cache *gpc, gpa_t gpa, unsigned long uhva,
--
virt/kvm/pfncache.c-419- */
virt/kvm/pfncache.c:420: write_lock_irq(&gpc->lock);
virt/kvm/pfncache.c-421- gpc->active = true;
virt/kvm/pfncache.c:422: write_unlock_irq(&gpc->lock);
virt/kvm/pfncache.c-423- }
--
virt/kvm/pfncache.c=447=void kvm_gpc_deactivate(struct gfn_to_pfn_cache *gpc)
--
virt/kvm/pfncache.c-458- * must stall mmu_notifier events until all users go away, i.e.
virt/kvm/pfncache.c:459: * until gpc->lock is dropped and refresh is guaranteed to fail.
virt/kvm/pfncache.c-460- */
virt/kvm/pfncache.c:461: write_lock_irq(&gpc->lock);
virt/kvm/pfncache.c-462- gpc->active = false;
--
virt/kvm/pfncache.c-475- gpc->pfn = KVM_PFN_ERR_FAULT;
virt/kvm/pfncache.c:476: write_unlock_irq(&gpc->lock);
virt/kvm/pfncache.c-477-
]
|
| 58/2 |
2026/08/12 11:56 |
llm |
debugger |
0m
Model:
Tokens:
input: 45936
output: 41
thoughts: 216
Thoughts: **Analyzing a Potential PREEMPT_RT Incompatibility**
Okay, so I'm looking at this `gpc->lock`, and it's defined as an `rwlock_t`. Right away, that raises a flag. We know that `rwlock_t` in a PREEMPT_RT kernel is implemented as a sleeping lock. That means acquiring it requires task context. I'm seeing it being acquired with either `read_lock_irq` or `write_lock_irq`. That's the first problem. Those functions are meant for use in interrupt context. If `gpc->lock` *is* a sleeping lock under PREEMPT_RT, that's immediately a no-go.
Now, the code shows `kvm_xen_set_evtchn_fast` acquiring this lock using `read_lock_irqsave(&gpc->lock, flags)`. And here's where it gets interesting: I see that `kvm_xen_set_evtchn_fast` is called from two hardirq contexts: `xen_timer_callback` and `kvm_arch_set_irq_inatomic`. That confirms my initial suspicion - it's a conflict! A sleeping lock acquired within a hardirq context is fundamentally incompatible with the principles of PREEMPT_RT. We're violating the real-time constraints.
But wait a minute... *thinking hard*... I also see `kvm_gpc_check` is called while holding the `gpc->lock`. That's important. It may be part of the solution, or the problem. I need to understand that.
|
| 59/2 |
2026/08/12 11:56 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:include/linux/kvm_types.h Name:kvm_gpc_check]
Results: map[SourceCode: 73: bool kvm_gpc_check(struct gfn_to_pfn_cache *gpc, unsigned long len)
74: {
75: struct kvm_memslots *slots = kvm_memslots(gpc->kvm);
76:
77: if (!gpc->active)
78: return false;
79:
80: /*
81: * If the page was cached from a memslot, make sure the memslots have
82: * not been re-configured.
83: */
84: if (!kvm_is_error_gpa(gpc->gpa) && gpc->generation != slots->generation)
85: return false;
86:
87: if (kvm_is_error_hva(gpc->uhva))
88: return false;
89:
90: if (!kvm_gpc_is_valid_len(gpc->gpa, gpc->uhva, len))
91: return false;
92:
93: if (!gpc->valid)
94: return false;
95:
96: return true;
97: }
SourceFile:virt/kvm/pfncache.c]
|
| 60/2 |
2026/08/12 11:56 |
llm |
debugger |
0m
Model:
Tokens:
input: 46911
output: 35
thoughts: 499
Thoughts: **Analyzing Lock Acquisition in PREEMPT_RT for KVM/Xen Event Channel Handling**
Okay, so the challenge is this: `kvm_xen_set_evtchn_fast` needs to behave correctly under PREEMPT_RT, and it's called from both hardirq and process contexts. If it *can't* grab `gpc->lock` immediately in PREEMPT_RT when called from a hardirq, it *must* return `-EWOULDBLOCK`. The critical thing is determining *when* to return this error.
Initially, I was thinking it's simple: if it's PREEMPT_RT and called from a hardirq, return `-EWOULDBLOCK`. But then I remembered that `kvm_xen_set_evtchn_fast` is also called from `kvm_xen_set_evtchn`, and that's called by `kvm_xen_hcall_evtchn_send`, which is *process* context. It's totally fine to take the lock in PREEMPT_RT when in process context. So the error needs to be conditional on *atomic* context *and* PREEMPT_RT.
Now, the real issue: it's also called from `xen_timer_callback` (hardirq) and `kvm_arch_set_irq_inatomic` (also hardirq). So how do I know if I *can* take the lock? I can't just blindly grab it.
The obvious solution is to use a trylock. Specifically, `read_trylock(&gpc->lock)`. I thought if PREEMPT_RT means it will sleep, it will return zero, but I need to know for sure if `rt_read_trylock` returns zero if it would sleep in hardirq context.
I then realized that using a trylock in hardirq might be problematic because if it succeeds, you *hold* a sleeping lock in hardirq context. That's usually a big no-no; lockdep will surely complain. And if you try to unlock it, `rt_read_unlock` might wake a writer task.
Wait. Does lockdep complain if you use trylock? *No, it doesn't!* I should probably double-check `lock_acquire` in `kernel/locking/lockdep.c` to be sure. But the trylock approach seems promising now.
|
| 61/2 |
2026/08/12 11:56 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:kernel/locking/lockdep.c Name:lock_acquire]
Results: map[SourceCode:5821: /*
5822: * We are not always called with irqs disabled - do that here,
5823: * and also avoid lockdep recursion:
5824: */
5825: void lock_acquire(struct lockdep_map *lock, unsigned int subclass,
5826: int trylock, int read, int check,
5827: struct lockdep_map *nest_lock, unsigned long ip)
5828: {
5829: unsigned long flags;
5830:
5831: trace_lock_acquire(lock, subclass, trylock, read, check, nest_lock, ip);
5832:
5833: if (!debug_locks)
5834: return;
5835:
5836: /*
5837: * As KASAN instrumentation is disabled and lock_acquire() is usually
5838: * the first lockdep call when a task tries to acquire a lock, add
5839: * kasan_check_byte() here to check for use-after-free and other
5840: * memory errors.
5841: */
5842: kasan_check_byte(lock);
5843:
5844: if (unlikely(!lockdep_enabled())) {
5845: /* XXX allow trylock from NMI ?!? */
5846: if (lockdep_nmi() && !trylock) {
5847: struct held_lock hlock;
5848:
5849: hlock.acquire_ip = ip;
5850: hlock.instance = lock;
5851: hlock.nest_lock = nest_lock;
5852: hlock.irq_context = 2; // XXX
5853: hlock.trylock = trylock;
5854: hlock.read = read;
5855: hlock.check = check;
5856: hlock.hardirqs_off = true;
5857: hlock.references = 0;
5858:
5859: verify_lock_unused(lock, &hlock, subclass);
5860: }
5861: return;
5862: }
5863:
5864: raw_local_irq_save(flags);
5865: check_flags(flags);
5866:
5867: lockdep_recursion_inc();
5868: __lock_acquire(lock, subclass, trylock, read, check,
5869: irqs_disabled_flags(flags), nest_lock, ip, 0, 0, 0);
5870: lockdep_recursion_finish();
5871: raw_local_irq_restore(flags);
5872: }
SourceFile:kernel/locking/lockdep.c]
|
| 62/2 |
2026/08/12 11:56 |
llm |
debugger |
0m
Model:
Tokens:
input: 48765
output: 37
thoughts: 18
Thoughts: Here's a summary of my understanding:
**Deep Dive into `check_wait_context`**
Okay, so I'm tracing through this code, and I've hit a key point. It looks like the `check_wait_context` function is directly invoked from within `__lock_acquire`. This is significant because `__lock_acquire` is, as the name implies, at the very heart of the locking mechanism. This suggests that `check_wait_context` is absolutely critical to the core locking workflow. It's not some peripheral utility; it's right in the thick of things. I need to understand its role within this very specific context. Given its placement, I'm now expecting it's probably deeply involved in evaluating the conditions under which a thread will wait for a lock and determining whether that wait is permitted or might be problematic, perhaps enforcing specific wait behavior. This also means any performance implications within `check_wait_context` will likely have a ripple effect on overall lock acquisition times, therefore performance is something to be aware of.
|
| 63/2 |
2026/08/12 11:56 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:kernel/locking/lockdep.c Name:check_wait_context]
Results: map[SourceCode:4837: /*
4838: * Verify the wait_type context.
4839: *
4840: * This check validates we take locks in the right wait-type order; that is it
4841: * ensures that we do not take mutexes inside spinlocks and do not attempt to
4842: * acquire spinlocks inside raw_spinlocks and the sort.
4843: *
4844: * The entire thing is slightly more complex because of RCU, RCU is a lock that
4845: * can be taken from (pretty much) any context but also has constraints.
4846: * However when taken in a stricter environment the RCU lock does not loosen
4847: * the constraints.
4848: *
4849: * Therefore we must look for the strictest environment in the lock stack and
4850: * compare that to the lock we're trying to acquire.
4851: */
4852: static int check_wait_context(struct task_struct *curr, struct held_lock *next)
4853: {
4854: u8 next_inner = hlock_class(next)->wait_type_inner;
4855: u8 next_outer = hlock_class(next)->wait_type_outer;
4856: u8 curr_inner;
4857: int depth;
4858:
4859: if (!next_inner || next->trylock)
4860: return 0;
4861:
4862: if (!next_outer)
4863: next_outer = next_inner;
4864:
4865: /*
4866: * Find start of current irq_context..
4867: */
4868: for (depth = curr->lockdep_depth - 1; depth >= 0; depth--) {
4869: struct held_lock *prev = curr->held_locks + depth;
4870: if (prev->irq_context != next->irq_context)
4871: break;
4872: }
4873: depth++;
4874:
4875: curr_inner = task_wait_context(curr);
4876:
4877: for (; depth < curr->lockdep_depth; depth++) {
4878: struct held_lock *prev = curr->held_locks + depth;
4879: struct lock_class *class = hlock_class(prev);
4880: u8 prev_inner = class->wait_type_inner;
4881:
4882: if (prev_inner) {
4883: /*
4884: * We can have a bigger inner than a previous one
4885: * when outer is smaller than inner, as with RCU.
4886: *
4887: * Also due to trylocks.
4888: */
4889: curr_inner = min(curr_inner, prev_inner);
4890:
4891: /*
4892: * Allow override for annotations -- this is typically
4893: * only valid/needed for code that only exists when
4894: * CONFIG_PREEMPT_RT=n.
4895: */
4896: if (unlikely(class->lock_type == LD_LOCK_WAIT_OVERRIDE))
4897: curr_inner = prev_inner;
4898: }
4899: }
4900:
4901: if (next_outer > curr_inner)
4902: return print_lock_invalid_wait_context(curr, next);
4903:
4904: return 0;
4905: }
SourceFile:kernel/locking/lockdep.c]
|
| 64/2 |
2026/08/12 11:56 |
llm |
debugger |
0m
Model:
Tokens:
input: 50086
output: 40
thoughts: 279
Thoughts: **My Analysis of a PREEMPT_RT Lock Optimization**
Okay, here's the situation. I'm looking at this bit of code, specifically this `if (!next_inner || next->trylock) return 0;` line. It just hit me - lockdep doesn't seem to be validating the wait context for trylocks, which is an interesting detail.
Now, hold on. `rt_read_trylock` doesn't include a `__might_resched` call, which is key. This suggests that if I use `read_trylock(&gpc->lock)`, it *won't* trigger a lockdep warning, and crucially, it *won't* cause the task to sleep. But that opens up another question… If the trylock is successful, it means I'll be holding a "sleeping" lock while in a hardirq context. Is that inherently safe?
Wait, in the PREEMPT_RT kernel, does `rt_read_trylock` disable preemption? No, it doesn't. And if I'm already within a hardirq context, preemption is, by definition, already disabled. Good.
Let's think about releasing the lock now. If I successfully take the read lock using `trylock` and later call `rt_read_unlock`, that release *might* need to wake up a writer. And the process of waking up a writer involves grabbing the `wait_lock` (a raw spinlock) and calling `wake_up_process`. But, crucially, `wake_up_process` is known to be safe to call from a hardirq context. So it appears that using `read_trylock` on a `rwlock_t` within a hardirq context is, surprisingly, safe in the PREEMPT_RT kernel!
Hold on, I need to double-check this. Let's dig into the implementation of `rt_read_trylock` and make absolutely certain. It's best practice to be certain of all these assumptions!
|
| 65/2 |
2026/08/12 11:56 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:kernel/locking/rwlock-rt.c Name:rt_read_trylock]
Results: map[SourceCode: 215: /*
216: * The common functions which get wrapped into the rwlock API.
217: */
218: int __sched rt_read_trylock(rwlock_t *rwlock)
219: {
220: int ret;
221:
222: ret = rwbase_read_trylock(&rwlock->rwbase);
223: if (ret) {
224: rwlock_acquire_read(&rwlock->dep_map, 0, 1, _RET_IP_);
225: rcu_read_lock();
226: migrate_disable();
227: }
228: return ret;
229: }
SourceFile:kernel/locking/spinlock_rt.c]
|
| 66/2 |
2026/08/12 11:56 |
llm |
debugger |
0m
Model:
Tokens:
input: 51079
output: 47
thoughts: 378
Thoughts: **Analyzing Lock Contention in the KVM/Xen Event Channel Fast Path**
Okay, so I'm digging into this `rt_read_trylock` function and immediately I see it's calling `rcu_read_lock()` and `migrate_disable()`. `migrate_disable()` is fine in a hard interrupt context, no issues there. But, hold on a second... the problem is still bubbling up because `read_lock_irqsave` is being used in `kvm_xen_set_evtchn_fast`, which is where we're trying to improve things.
My initial thought was to replace that `read_lock_irqsave` with `read_trylock`, but then I remembered that `kvm_xen_set_evtchn_fast` gets called from both process context *and* interrupt context. In process context, we *do* want to wait, so a simple replacement won't work. I briefly considered an `in_atomic()` check with `read_trylock` if we're in a hardirq context, but that felt clunky:
```c
if (in_atomic() || irqs_disabled()) {
if (!read_trylock(&gpc->lock))
goto out_rcu;
} else {
read_lock_irqsave(&gpc->lock, flags);
}
```
Then I remembered that `kvm_xen_set_evtchn_fast` *already* returns `-EWOULDBLOCK` if it can't complete the operation quickly. So a much simpler solution is to just disable the fast path in the PREEMPT_RT kernel for hard interrupt contexts, by returning -EWOULDBLOCK in `kvm_xen_set_evtchn_fast`.
Actually, wait, `kvm_arch_set_irq_inatomic` is also potentially called from hardirq context. So it seems more general if we return `-EWOULDBLOCK` when `in_atomic() || irqs_disabled()` in PREEMPT_RT.
Hold on, using `in_atomic()` isn't recommended in general. Is there a better, more robust way to handle this? I'd better revisit `kvm_xen_set_evtchn_fast` and its surrounding code to ensure I haven't missed anything. I've got a feeling a cleaner solution is lurking somewhere.
|
| 67/2 |
2026/08/12 11:56 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:arch/x86/kvm/xen.c Name:kvm_xen_set_evtchn_fast]
Results: map[SourceCode:1785: /*
1786: * The return value from this function is propagated to kvm_set_irq() API,
1787: * so it returns:
1788: * < 0 Interrupt was ignored (masked or not delivered for other reasons)
1789: * = 0 Interrupt was coalesced (previous irq is still pending)
1790: * > 0 Number of CPUs interrupt was delivered to
1791: *
1792: * It is also called directly from kvm_arch_set_irq_inatomic(), where the
1793: * only check on its return value is a comparison with -EWOULDBLOCK'.
1794: */
1795: int kvm_xen_set_evtchn_fast(struct kvm_xen_evtchn *xe, struct kvm *kvm)
1796: {
1797: struct gfn_to_pfn_cache *gpc = &kvm->arch.xen.shinfo_cache;
1798: struct kvm_vcpu *vcpu;
1799: unsigned long *pending_bits, *mask_bits;
1800: unsigned long flags;
1801: int port_word_bit;
1802: bool kick_vcpu = false;
1803: int vcpu_idx, idx, rc;
1804:
1805: vcpu_idx = READ_ONCE(xe->vcpu_idx);
1806: if (vcpu_idx >= 0)
1807: vcpu = kvm_get_vcpu(kvm, vcpu_idx);
1808: else {
1809: vcpu = kvm_get_vcpu_by_id(kvm, xe->vcpu_id);
1810: if (!vcpu)
1811: return -EINVAL;
1812: WRITE_ONCE(xe->vcpu_idx, vcpu->vcpu_idx);
1813: }
1814:
1815: if (xe->port >= max_evtchn_port(kvm))
1816: return -EINVAL;
1817:
1818: rc = -EWOULDBLOCK;
1819:
1820: idx = srcu_read_lock(&kvm->srcu);
1821:
1822: read_lock_irqsave(&gpc->lock, flags);
1823: if (!kvm_gpc_check(gpc, PAGE_SIZE))
1824: goto out_rcu;
1825:
1826: if (IS_ENABLED(CONFIG_64BIT) && kvm->arch.xen.long_mode) {
1827: struct shared_info *shinfo = gpc->khva;
1828: pending_bits = (unsigned long *)&shinfo->evtchn_pending;
1829: mask_bits = (unsigned long *)&shinfo->evtchn_mask;
1830: port_word_bit = xe->port / 64;
1831: } else {
1832: struct compat_shared_info *shinfo = gpc->khva;
1833: pending_bits = (unsigned long *)&shinfo->evtchn_pending;
1834: mask_bits = (unsigned long *)&shinfo->evtchn_mask;
1835: port_word_bit = xe->port / 32;
1836: }
1837:
1838: /*
1839: * If this port wasn't already set, and if it isn't masked, then
1840: * we try to set the corresponding bit in the in-kernel shadow of
1841: * evtchn_pending_sel for the target vCPU. And if *that* wasn't
1842: * already set, then we kick the vCPU in question to write to the
1843: * *real* evtchn_pending_sel in its own guest vcpu_info struct.
1844: */
1845: if (test_and_set_bit(xe->port, pending_bits)) {
1846: rc = 0; /* It was already raised */
1847: } else if (test_bit(xe->port, mask_bits)) {
1848: rc = -ENOTCONN; /* Masked */
1849: kvm_xen_check_poller(vcpu, xe->port);
1850: } else {
1851: rc = 1; /* Delivered to the bitmap in shared_info. */
1852: /* Now switch to the vCPU's vcpu_info to set the index and pending_sel */
1853: read_unlock_irqrestore(&gpc->lock, flags);
1854: gpc = &vcpu->arch.xen.vcpu_info_cache;
1855:
1856: read_lock_irqsave(&gpc->lock, flags);
1857: if (!kvm_gpc_check(gpc, sizeof(struct vcpu_info))) {
1858: /*
1859: * Could not access the vcpu_info. Set the bit in-kernel
1860: * and prod the vCPU to deliver it for itself.
1861: */
1862: if (!test_and_set_bit(port_word_bit, &vcpu->arch.xen.evtchn_pending_sel))
1863: kick_vcpu = true;
1864: goto out_rcu;
1865: }
1866:
1867: if (IS_ENABLED(CONFIG_64BIT) && kvm->arch.xen.long_mode) {
1868: struct vcpu_info *vcpu_info = gpc->khva;
1869: if (!test_and_set_bit(port_word_bit, &vcpu_info->evtchn_pending_sel)) {
1870: WRITE_ONCE(vcpu_info->evtchn_upcall_pending, 1);
1871: kick_vcpu = true;
1872: }
1873: } else {
1874: struct compat_vcpu_info *vcpu_info = gpc->khva;
1875: if (!test_and_set_bit(port_word_bit,
1876: (unsigned long *)&vcpu_info->evtchn_pending_sel)) {
1877: WRITE_ONCE(vcpu_info->evtchn_upcall_pending, 1);
1878: kick_vcpu = true;
1879: }
1880: }
1881:
1882: /* For the per-vCPU lapic vector, deliver it as MSI. */
1883: if (kick_vcpu && vcpu->arch.xen.upcall_vector) {
1884: kvm_xen_inject_vcpu_vector(vcpu);
1885: kick_vcpu = false;
1886: }
1887: }
1888:
1889: out_rcu:
1890: read_unlock_irqrestore(&gpc->lock, flags);
1891: srcu_read_unlock(&kvm->srcu, idx);
1892:
1893: if (kick_vcpu) {
1894: kvm_make_request(KVM_REQ_UNBLOCK, vcpu);
1895: kvm_vcpu_kick(vcpu);
1896: }
1897:
1898: return rc;
1899: }
SourceFile:arch/x86/kvm/xen.c]
|
| 68/2 |
2026/08/12 11:56 |
llm |
debugger |
0m
Model:
Tokens:
input: 54115
output: 64
thoughts: 1120
Thoughts: **Analyzing Lock Usage in `kvm_xen_set_evtchn_fast` and `gfn_to_pfn_cache`**
Okay, so I'm looking at how we're using `read_trylock` within `kvm_xen_set_evtchn_fast`, particularly when `CONFIG_PREEMPT_RT` is enabled and we're in an atomic context. The initial concern was that `in_atomic()` might be unreliable without preemption. However, with `PREEMPT_RT`, preemption is enabled, so that's not the primary issue.
I've realized `kvm_xen_set_evtchn_fast` gets called from `kvm_arch_set_irq_inatomic` and `xen_timer_callback` – both of which run in hard interrupt context. My first thought was to pass an `in_atomic` boolean to `kvm_xen_set_evtchn_fast` to differentiate. However, `kvm_xen_set_evtchn_fast` is also called from `kvm_xen_set_evtchn` which operates in process context.
If I were to pass an `in_atomic` flag, I could use a conditional lock approach: use `read_trylock` if `in_atomic` is true, otherwise use `read_lock_irqsave`. The immediate problem with this is that `read_trylock` does *not* disable interrupts. Then I remembered, if we are in hardirq context, interrupts are *already* disabled, so that's okay. But wait, `kvm_arch_set_irq_inatomic` is called from `irqfd_wakeup`, which holds a waitqueue lock, which is a raw spinlock. So interrupts are disabled there too. `xen_timer_callback` is called from the hrtimer interrupt, so interrupts are disabled.
So, if `in_atomic` is true, we should be able to just use `read_trylock`.
But hold on, under PREEMPT_RT, `read_trylock` on an `rwlock_t` won't disable interrupts. Given we are in hardirq context, interrupts are *already* disabled. The real question is whether the current `rwlock_t` is the right choice. Maybe we should use `raw_rwlock_t` instead?
Let's check if the existing uses of `gpc->lock` can handle a `raw_rwlock_t`. It's held during `kvm_gpc_check`, which is simple. It's also held in `hva_to_pfn_retry` when calling `kvm_is_error_hva`, `kvm_gpc_is_valid_len`, and also in `kvm_gpc_activate` and `kvm_gpc_deactivate`. None of these operations sleep, so a raw lock *should* work.
Then I saw it: `gpc->lock` is held inside `gfn_to_pfn_cache_invalidate_start` while iterating over `kvm->gpc_list`.
```c
list_for_each_entry(gpc, &kvm->gpc_list, list) {
read_lock_irq(&gpc->lock);
if (gpc->valid && !is_error_noslot_pfn(gpc->pfn) &&
gpc->uhva >= start && gpc->uhva < end) {
read_unlock_irq(&gpc->lock);
...
```
This is called from MMU notifiers. Can MMU notifiers be called from atomic context? Not typically, but `mmu_notifier_invalidate_range_start` *can* be called from atomic context in some rare cases. But the key here is the current `gpc->lock` is an `rwlock_t`. Changing it to `raw_rwlock_t` would disable preemption *and* interrupts under PREEMPT_RT.
How long is `gpc->lock` actually held? Inside `hva_to_pfn_retry`, it's held for a very short duration: write locking, setting `gpc->valid`, `gpc->pfn`, `gpc->khva`, and unlocking. Very short.
So, why was it made an `rwlock_t` in the first place, instead of a `raw_rwlock_t`? Generally, we use `spinlock_t` or `rwlock_t` unless we *need* a raw lock. Since we're taking `gpc->lock` in hardirq context (via `
|
| 69/2 |
2026/08/12 11:57 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:virt/kvm/pfncache.c IncludeSnippetLines:2 Name:gfn_to_pfn_cache SourceTreePrefix:arch/x86/kvm]
Results: map[References:[map[ReferenceKind:uses ReferencingEntityKind:function ReferencingEntityName:__kvm_xen_has_interrupt SourceFile:arch/x86/kvm/xen.c SourceLine:699 SourceSnippet: 697: int __kvm_xen_has_interrupt(struct kvm_vcpu *v)
698: {
699: struct gfn_to_pfn_cache *gpc = &v->arch.xen.vcpu_info_cache;
700: unsigned long flags;
701: u8 rc = 0;
] map[ReferenceKind:uses ReferencingEntityKind:function ReferencingEntityName:kvm_setup_guest_pvclock SourceFile:arch/x86/kvm/x86.c SourceLine:3244 SourceSnippet:3242: static void kvm_setup_guest_pvclock(struct pvclock_vcpu_time_info *ref_hv_clock,
3243: struct kvm_vcpu *vcpu,
3244: struct gfn_to_pfn_cache *gpc,
3245: unsigned int offset)
3246: {
] map[ReferenceKind:uses ReferencingEntityKind:function ReferencingEntityName:kvm_xen_inject_pending_events SourceFile:arch/x86/kvm/xen.c SourceLine:642 SourceSnippet: 640: {
641: unsigned long evtchn_pending_sel = READ_ONCE(v->arch.xen.evtchn_pending_sel);
642: struct gfn_to_pfn_cache *gpc = &v->arch.xen.vcpu_info_cache;
643: unsigned long flags;
644:
] map[ReferenceKind:uses ReferencingEntityKind:function ReferencingEntityName:kvm_xen_set_evtchn SourceFile:arch/x86/kvm/xen.c SourceLine:1937 SourceSnippet:1935: */
1936: do {
1937: struct gfn_to_pfn_cache *gpc = &kvm->arch.xen.shinfo_cache;
1938: int idx;
1939:
] map[ReferenceKind:uses ReferencingEntityKind:function ReferencingEntityName:kvm_xen_set_evtchn_fast SourceFile:arch/x86/kvm/xen.c SourceLine:1797 SourceSnippet:1795: int kvm_xen_set_evtchn_fast(struct kvm_xen_evtchn *xe, struct kvm *kvm)
1796: {
1797: struct gfn_to_pfn_cache *gpc = &kvm->arch.xen.shinfo_cache;
1798: struct kvm_vcpu *vcpu;
1799: unsigned long *pending_bits, *mask_bits;
] map[ReferenceKind:uses ReferencingEntityKind:function ReferencingEntityName:kvm_xen_shared_info_init SourceFile:arch/x86/kvm/xen.c SourceLine:40 SourceSnippet: 38: static int kvm_xen_shared_info_init(struct kvm *kvm)
39: {
40: struct gfn_to_pfn_cache *gpc = &kvm->arch.xen.shinfo_cache;
41: struct pvclock_wall_clock *wc;
42: u32 *wc_sec_hi;
] map[ReferenceKind:uses ReferencingEntityKind:function ReferencingEntityName:kvm_xen_update_runstate_guest SourceFile:arch/x86/kvm/xen.c SourceLine:324 SourceSnippet: 322: {
323: struct kvm_vcpu_xen *vx = &v->arch.xen;
324: struct gfn_to_pfn_cache *gpc1 = &vx->runstate_cache;
325: struct gfn_to_pfn_cache *gpc2 = &vx->runstate2_cache;
326: size_t user_len, user_len1, user_len2;
] map[ReferenceKind:uses ReferencingEntityKind:function ReferencingEntityName:kvm_xen_update_runstate_guest SourceFile:arch/x86/kvm/xen.c SourceLine:325 SourceSnippet: 323: struct kvm_vcpu_xen *vx = &v->arch.xen;
324: struct gfn_to_pfn_cache *gpc1 = &vx->runstate_cache;
325: struct gfn_to_pfn_cache *gpc2 = &vx->runstate2_cache;
326: size_t user_len, user_len1, user_len2;
327: struct vcpu_runstate_info rs;
] map[ReferenceKind:uses ReferencingEntityKind:function ReferencingEntityName:wait_pending_event SourceFile:arch/x86/kvm/xen.c SourceLine:1437 SourceSnippet:1435: {
1436: struct kvm *kvm = vcpu->kvm;
1437: struct gfn_to_pfn_cache *gpc = &kvm->arch.xen.shinfo_cache;
1438: unsigned long *pending_bits;
1439: unsigned long flags;
] map[ReferenceKind:uses ReferencingEntityKind:function ReferencingEntityName:xen_get_guest_pvclock SourceFile:arch/x86/kvm/xen.c SourceLine:155 SourceSnippet: 153: static int xen_get_guest_pvclock(struct kvm_vcpu *vcpu,
154: struct pvclock_vcpu_time_info *hv_clock,
155: struct gfn_to_pfn_cache *gpc,
156: unsigned int offset)
157: {
]] TruncatedOutput:false]
|
| 70/2 |
2026/08/12 11:57 |
llm |
debugger |
0m
Model:
Tokens:
input: 57982
output: 29
thoughts: 926
Thoughts: **Analyzing the Lock Contention in `kvm_xen_set_evtchn_fast`**
Okay, so I'm looking at `kvm_xen_set_evtchn_fast` and I need to figure out the implications of the locking scheme. It seems this function is the *only* one directly called from a hard interrupt context. I quickly ruled out `kvm_xen_update_runstate_guest` and `kvm_setup_guest_pvclock` because they operate in process context.
My initial thought was to replace `gpc->lock`, which is a `rwlock_t`, with `raw_rwlock_t`. This would allow for optimization in hardirq context. However, this change would affect *all* users of `gfn_to_pfn_cache`, which is a generic KVM feature used by Xen and potentially other things. The concern is that `read_lock_irq` with a `raw_rwlock_t` would disable interrupts and preemption even in PREEMPT_RT environments, which is generally acceptable given the critical sections are very short.
But, hang on, `gfn_to_pfn_cache_invalidate_start` iterates through `kvm->gpc_list` and also grabs the `gpc->lock` for each entry. Fortunately, those sections are short too.
Then I remembered that `kvm_xen_set_evtchn_fast` *also* takes `srcu_read_lock(&kvm->srcu)`. I needed to make sure that was allowed in the hardirq context. So I checked for any lockdep splats. The error pointed to `kvm->srcu` being held when a sleeping function was called. But, it doesn't mean `srcu_read_lock` *itself* is a sleeping function! Let's verify. `srcu_read_lock` calls `__srcu_read_lock`, which just does an atomic increment and a memory barrier, so it's perfectly safe in hardirq context!
So, the *only* remaining issue is `read_lock_irqsave(&gpc->lock, flags)`.
Then a bigger thought hits: I was going to change `gpc->lock` to `raw_rwlock_t`, which means `rwlock_t` in `struct gfn_to_pfn_cache` also needs to change to `raw_rwlock_t`. But wait...I'm an idiot! There is NO `raw_rwlock_t` in Linux! It's `rwlock_t` which maps to `rt_rw_lock` in PREEMPT_RT. Linux only has `raw_spinlock_t`.
I need to double-check that `raw_rwlock_t` isn't a typo and doesn't exist.
|
| 71/2 |
2026/08/12 11:57 |
tool |
grepper |
0m
Error:
Args: map[Expression:raw_rwlock_t PathPrefix:include/linux]
Results:
|
| 72/2 |
2026/08/12 11:57 |
llm |
debugger |
0m
Model:
Tokens:
input: 59570
output: 35
thoughts: 1311
Thoughts: **Analyzing Lock Contention in PREEMPT_RT KVM Code**
Okay, so I'm looking at this `gpc->lock` and realizing there's no way to just change it to a `raw_rwlock_t`. We can't make it raw because it *is* an rwlock, which is there to allow concurrent readers. Changing it to a raw spinlock could cause contention, which defeats the purpose.
But, if we *can't* change it to a raw lock, that means `kvm_xen_set_evtchn_fast` *can't* take `gpc->lock` in a hard interrupt context under PREEMPT_RT. That's a problem.
My first thought was, okay, we need to return `-EWOULDBLOCK` in PREEMPT_RT when called from hard interrupt context. I quickly considered an `if (IS_ENABLED(CONFIG_PREEMPT_RT) && in_hardirq()) return -EWOULDBLOCK;` check.
However, then I realized that `kvm_xen_set_evtchn_fast` is called from `xen_timer_callback`, which is a high-resolution timer callback, and therefore *is* in hard interrupt context. It's also called from `kvm_arch_set_irq_inatomic`, which itself is called from `irqfd_wakeup`, and that could be in either hard, soft interrupt context, or even with interrupts disabled.
`in_atomic()` covers hardirq, softirq, and preemption disabled, but it is not recommended for driver use, so let's find a more elegant solution.
Wait, the `kvm_arch_set_irq_inatomic` path is explicitly for atomic contexts, so maybe we could just use:
```c
#ifdef CONFIG_PREEMPT_RT
if (in_atomic() || irqs_disabled())
return -EWOULDBLOCK;
#endif
```
Then I remembered, this function is called from 1) `xen_timer_callback` (hrtimer), 2) `kvm_arch_set_irq_inatomic` (atomic), and 3) the regular `kvm_xen_set_evtchn` path (process context).
So then I thought, what if we just use `read_trylock` in `kvm_xen_set_evtchn_fast`?
Ah, does `read_trylock` sleep in PREEMPT_RT? I checked, and `rt_read_trylock` does *not* sleep. It returns 0 if it would block. And, importantly, if it succeeds, it *does* acquire the lock.
But can we acquire a potentially sleeping lock (even via a trylock) in hard interrupt context? Let's check lockdep. Lockdep's `check_wait_context` *explicitly* skips trylocks. So lockdep won't complain! And, `rt_read_trylock` doesn't call `__might_resched`, so it won't trigger the "BUG: sleeping function called from invalid context" splat!
If `rt_read_trylock` succeeds, we *hold* a sleeping lock in hard interrupt context. But then we call `read_unlock_irqrestore`. On `rwlock_t` in PREEMPT_RT, that calls `rt_read_unlock`. Does *that* sleep?
No! `rt_read_unlock` *might* wake up a writer, but doing so involves taking the `wait_lock` (a raw spinlock) and calling `wake_up_process`, which is safe in hard interrupt context. So, `read_trylock` is perfectly safe in PREEMPT_RT.
So, let's verify that. If we change `read_lock_irqsave(&gpc->lock, flags);` to:
```c
if (!read_trylock(&gpc->lock))
goto out_rcu;
/* need to save flags? read_trylock doesn't save flags. */
```
Wait, `read_lock_irqsave` *disables interrupts*. `read_trylock` does *not* disable interrupts. So we need to disable interrupts *before* the trylock.
```c
local_irq_save(flags);
if (!read_trylock(&gpc->lock)) {
local_irq_restore(flags);
goto out_rcu;
}
```
Wait, but if we do this, we're *always* using `trylock`, even in process context! We *might* get
|
| 73/2 |
2026/08/12 11:57 |
tool |
grepper |
0m
Args: map[Expression:read_trylock.*gpc PathPrefix:arch/x86/kvm]
Results: map[Output:arch/x86/kvm/xen.c=321=static void kvm_xen_update_runstate_guest(struct kvm_vcpu *v, bool atomic)
--
arch/x86/kvm/xen.c-424- local_irq_save(flags);
arch/x86/kvm/xen.c:425: if (!read_trylock(&gpc1->lock)) {
arch/x86/kvm/xen.c-426- local_irq_restore(flags);
--
arch/x86/kvm/xen.c-468- if (atomic) {
arch/x86/kvm/xen.c:469: if (!read_trylock(&gpc2->lock)) {
arch/x86/kvm/xen.c-470- read_unlock_irqrestore(&gpc1->lock, flags);
]
|
| 74/2 |
2026/08/12 11:57 |
llm |
debugger |
0m
Model:
Tokens:
input: 62182
output: 47
thoughts: 39
Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience:
**Eureka! A Familiar Pattern**
Wait a second... *kvm_xen_update_runstate_guest*... that function already handles this exact scenario! I was heading down a rabbit hole for nothing. Let me pull up the source for `kvm_xen_update_runstate_guest` and refresh my memory. This might save me a ton of time. I’m pretty sure the logic there directly addresses the condition I'm trying to resolve. Right, time to dig in and confirm, and maybe reuse or adapt what's already there rather than reinventing the wheel.
|
| 75/2 |
2026/08/12 11:57 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:arch/x86/kvm/xen.c Name:kvm_xen_update_runstate_guest]
Results: map[SourceCode: 321: static void kvm_xen_update_runstate_guest(struct kvm_vcpu *v, bool atomic)
322: {
323: struct kvm_vcpu_xen *vx = &v->arch.xen;
324: struct gfn_to_pfn_cache *gpc1 = &vx->runstate_cache;
325: struct gfn_to_pfn_cache *gpc2 = &vx->runstate2_cache;
326: size_t user_len, user_len1, user_len2;
327: struct vcpu_runstate_info rs;
328: unsigned long flags;
329: size_t times_ofs;
330: uint8_t *update_bit = NULL;
331: uint64_t entry_time;
332: uint64_t *rs_times;
333: int *rs_state;
334:
335: /*
336: * The only difference between 32-bit and 64-bit versions of the
337: * runstate struct is the alignment of uint64_t in 32-bit, which
338: * means that the 64-bit version has an additional 4 bytes of
339: * padding after the first field 'state'. Let's be really really
340: * paranoid about that, and matching it with our internal data
341: * structures that we memcpy into it...
342: */
343: BUILD_BUG_ON(offsetof(struct vcpu_runstate_info, state) != 0);
344: BUILD_BUG_ON(offsetof(struct compat_vcpu_runstate_info, state) != 0);
345: BUILD_BUG_ON(sizeof(struct compat_vcpu_runstate_info) != 0x2c);
346: #ifdef CONFIG_X86_64
347: /*
348: * The 64-bit structure has 4 bytes of padding before 'state_entry_time'
349: * so each subsequent field is shifted by 4, and it's 4 bytes longer.
350: */
351: BUILD_BUG_ON(offsetof(struct vcpu_runstate_info, state_entry_time) !=
352: offsetof(struct compat_vcpu_runstate_info, state_entry_time) + 4);
353: BUILD_BUG_ON(offsetof(struct vcpu_runstate_info, time) !=
354: offsetof(struct compat_vcpu_runstate_info, time) + 4);
355: BUILD_BUG_ON(sizeof(struct vcpu_runstate_info) != 0x2c + 4);
356: #endif
357: /*
358: * The state field is in the same place at the start of both structs,
359: * and is the same size (int) as vx->current_runstate.
360: */
361: BUILD_BUG_ON(offsetof(struct vcpu_runstate_info, state) !=
362: offsetof(struct compat_vcpu_runstate_info, state));
363: BUILD_BUG_ON(sizeof_field(struct vcpu_runstate_info, state) !=
364: sizeof(vx->current_runstate));
365: BUILD_BUG_ON(sizeof_field(struct compat_vcpu_runstate_info, state) !=
366: sizeof(vx->current_runstate));
367:
368: /*
369: * The state_entry_time field is 64 bits in both versions, and the
370: * XEN_RUNSTATE_UPDATE flag is in the top bit, which given that x86
371: * is little-endian means that it's in the last *byte* of the word.
372: * That detail is important later.
373: */
374: BUILD_BUG_ON(sizeof_field(struct vcpu_runstate_info, state_entry_time) !=
375: sizeof(uint64_t));
376: BUILD_BUG_ON(sizeof_field(struct compat_vcpu_runstate_info, state_entry_time) !=
377: sizeof(uint64_t));
378: BUILD_BUG_ON((XEN_RUNSTATE_UPDATE >> 56) != 0x80);
379:
380: /*
381: * The time array is four 64-bit quantities in both versions, matching
382: * the vx->runstate_times and immediately following state_entry_time.
383: */
384: BUILD_BUG_ON(offsetof(struct vcpu_runstate_info, state_entry_time) !=
385: offsetof(struct vcpu_runstate_info, time) - sizeof(uint64_t));
386: BUILD_BUG_ON(offsetof(struct compat_vcpu_runstate_info, state_entry_time) !=
387: offsetof(struct compat_vcpu_runstate_info, time) - sizeof(uint64_t));
388: BUILD_BUG_ON(sizeof_field(struct vcpu_runstate_info, time) !=
389: sizeof_field(struct compat_vcpu_runstate_info, time));
390: BUILD_BUG_ON(sizeof_field(struct vcpu_runstate_info, time) !=
391: sizeof(vx->runstate_times));
392:
393: if (IS_ENABLED(CONFIG_64BIT) && v->kvm->arch.xen.long_mode) {
394: user_len = sizeof(struct vcpu_runstate_info);
395: times_ofs = offsetof(struct vcpu_runstate_info,
396: state_entry_time);
397: } else {
398: user_len = sizeof(struct compat_vcpu_runstate_info);
399: times_ofs = offsetof(struct compat_vcpu_runstate_info,
400: state_entry_time);
401: }
402:
403: /*
404: * There are basically no alignment constraints. The guest can set it
405: * up so it crosses from one page to the next, and at arbitrary byte
406: * alignment (and the 32-bit ABI doesn't align the 64-bit integers
407: * anyway, even if the overall struct had been 64-bit aligned).
408: */
409: if ((gpc1->gpa & ~PAGE_MASK) + user_len >= PAGE_SIZE) {
410: user_len1 = PAGE_SIZE - (gpc1->gpa & ~PAGE_MASK);
411: user_len2 = user_len - user_len1;
412: } else {
413: user_len1 = user_len;
414: user_len2 = 0;
415: }
416: BUG_ON(user_len1 + user_len2 != user_len);
417:
418: retry:
419: /*
420: * Attempt to obtain the GPC lock on *both* (if there are two)
421: * gfn_to_pfn caches that cover the region.
422: */
423: if (atomic) {
424: local_irq_save(flags);
425: if (!read_trylock(&gpc1->lock)) {
426: local_irq_restore(flags);
427: return;
428: }
429: } else {
430: read_lock_irqsave(&gpc1->lock, flags);
431: }
432: while (!kvm_gpc_check(gpc1, user_len1)) {
433: read_unlock_irqrestore(&gpc1->lock, flags);
434:
435: /* When invoked from kvm_sched_out() we cannot sleep */
436: if (atomic)
437: return;
438:
439: if (kvm_gpc_refresh(gpc1, user_len1))
440: return;
441:
442: read_lock_irqsave(&gpc1->lock, flags);
443: }
444:
445: if (likely(!user_len2)) {
446: /*
447: * Set up three pointers directly to the runstate_info
448: * struct in the guest (via the GPC).
449: *
450: * • @rs_state → state field
451: * • @rs_times → state_entry_time field.
452: * • @update_bit → last byte of state_entry_time, which
453: * contains the XEN_RUNSTATE_UPDATE bit.
454: */
455: rs_state = gpc1->khva;
456: rs_times = gpc1->khva + times_ofs;
457: if (v->kvm->arch.xen.runstate_update_flag)
458: update_bit = ((void *)(&rs_times[1])) - 1;
459: } else {
460: /*
461: * The guest's runstate_info is split across two pages and we
462: * need to hold and validate both GPCs simultaneously. We can
463: * declare a lock ordering GPC1 > GPC2 because nothing else
464: * takes them more than one at a time. Set a subclass on the
465: * gpc1 lock to make lockdep shut up about it.
466: */
467: lock_set_subclass(&gpc1->lock.dep_map, 1, _THIS_IP_);
468: if (atomic) {
469: if (!read_trylock(&gpc2->lock)) {
470: read_unlock_irqrestore(&gpc1->lock, flags);
471: return;
472: }
473: } else {
474: read_lock(&gpc2->lock);
475: }
476:
477: if (!kvm_gpc_check(gpc2, user_len2)) {
478: read_unlock(&gpc2->lock);
479: read_unlock_irqrestore(&gpc1->lock, flags);
480:
481: /* When invoked from kvm_sched_out() we cannot sleep */
482: if (atomic)
483: return;
484:
485: /*
486: * Use kvm_gpc_activate() here because if the runstate
487: * area was configured in 32-bit mode and only extends
488: * to the second page now because the guest changed to
489: * 64-bit mode, the second GPC won't have been set up.
490: */
491: if (kvm_gpc_activate(gpc2, gpc1->gpa + user_len1,
492: user_len2))
493: return;
494:
495: /*
496: * We dropped the lock on GPC1 so we have to go all the
497: * way back and revalidate that too.
498: */
499: goto retry;
500: }
501:
502: /*
503: * In this case, the runstate_info struct will be assembled on
504: * the kernel stack (compat or not as appropriate) and will
505: * be copied to GPC1/GPC2 with a dual memcpy. Set up the three
506: * rs pointers accordingly.
507: */
508: rs_times = &rs.state_entry_time;
509:
510: /*
511: * The rs_state pointer points to the start of what we'll
512: * copy to the guest, which in the case of a compat guest
513: * is the 32-bit field that the compiler thinks is padding.
514: */
515: rs_state = ((void *)rs_times) - times_ofs;
516:
517: /*
518: * The update_bit is still directly in the guest memory,
519: * via one GPC or the other.
520: */
521: if (v->kvm->arch.xen.runstate_update_flag) {
522: if (user_len1 >= times_ofs + sizeof(uint64_t))
523: update_bit = gpc1->khva + times_ofs +
524: sizeof(uint64_t) - 1;
525: else
526: update_bit = gpc2->khva + times_ofs +
527: sizeof(uint64_t) - 1 - user_len1;
528: }
529:
530: #ifdef CONFIG_X86_64
531: /*
532: * Don't leak kernel memory through the padding in the 64-bit
533: * version of the struct.
534: */
535: memset(&rs, 0, offsetof(struct vcpu_runstate_info, state_entry_time));
536: #endif
537: }
538:
539: /*
540: * First, set the XEN_RUNSTATE_UPDATE bit in the top bit of the
541: * state_entry_time field, directly in the guest. We need to set
542: * that (and write-barrier) before writing to the rest of the
543: * structure, and clear it last. Just as Xen does, we address the
544: * single *byte* in which it resides because it might be in a
545: * different cache line to the rest of the 64-bit word, due to
546: * the (lack of) alignment constraints.
547: */
548: entry_time = vx->runstate_entry_time;
549: if (update_bit) {
550: entry_time |= XEN_RUNSTATE_UPDATE;
551: *update_bit = (vx->runstate_entry_time | XEN_RUNSTATE_UPDATE) >> 56;
552: smp_wmb();
553: }
554:
555: /*
556: * Now assemble the actual structure, either on our kernel stack
557: * or directly in the guest according to how the rs_state and
558: * rs_times pointers were set up above.
559: */
560: *rs_state = vx->current_runstate;
561: rs_times[0] = entry_time;
562: memcpy(rs_times + 1, vx->runstate_times, sizeof(vx->runstate_times));
563:
564: /* For the split case, we have to then copy it to the guest. */
565: if (user_len2) {
566: memcpy(gpc1->khva, rs_state, user_len1);
567: memcpy(gpc2->khva, ((void *)rs_state) + user_len1, user_len2);
568: }
569: smp_wmb();
570:
571: /* Finally, clear the XEN_RUNSTATE_UPDATE bit. */
572: if (update_bit) {
573: entry_time &= ~XEN_RUNSTATE_UPDATE;
574: *update_bit = entry_time >> 56;
575: smp_wmb();
576: }
577:
578: if (user_len2) {
579: kvm_gpc_mark_dirty_in_slot(gpc2);
580: read_unlock(&gpc2->lock);
581: }
582:
583: kvm_gpc_mark_dirty_in_slot(gpc1);
584: read_unlock_irqrestore(&gpc1->lock, flags);
585: }
SourceFile:arch/x86/kvm/xen.c]
|
| 76/2 |
2026/08/12 11:57 |
llm |
debugger |
0m
Model:
Tokens:
input: 67076
output: 47
thoughts: 3019
Thoughts: **Refactoring `kvm_xen_set_evtchn_fast` for Atomic Context Safety**
Okay, so I've been digging into the `kvm_xen_set_evtchn_fast` function and its interactions with the reader-writer lock (`gpc->lock`) in the KVM-Xen code. The core issue is ensuring safe lock usage when called from atomic contexts, particularly with PREEMPT_RT. My initial thought was to use `read_trylock` always for "fast" execution but the nuances are more subtle.
* **Identifying the Problem:** I noticed `kvm_xen_update_runstate_guest` uses `read_trylock` when an `atomic` boolean is true, which is good precedent. `kvm_xen_set_evtchn_fast` is called from `xen_timer_callback` (hardirq), `kvm_arch_set_irq_inatomic` (atomic) and `kvm_xen_set_evtchn` (process context). The challenge is that `read_lock_irqsave` *spins* in non-RT atomic contexts and *sleeps* in RT atomic contexts which can lead to deadlocks.
* **Atomic Context Check:** I considered using `!preemptible()` as a proxy for atomic context, but that's not reliable. But, since `kvm_xen_set_evtchn_fast` is called from known atomic contexts (`xen_timer_callback` and `kvm_arch_set_irq_inatomic`), a boolean `in_atomic` argument to the function would be the cleanest solution.
* **The `read_trylock` Option:** The alternative is always using `read_trylock` inside `kvm_xen_set_evtchn_fast`, which would cause `-EWOULDBLOCK` if the lock is held. The caller `kvm_xen_set_evtchn` handles this error by falling back to the slow path (`kvm_gpc_refresh`), which also takes a mutex, so this won't break anything. But I do need to ensure that the lock is still acquired and released in the right way:
* I need `local_irq_save` before the `read_trylock` to avoid potential deadlocks.
* I also need to ensure that *both* lock acquisitions within `kvm_xen_set_evtchn_fast` become `read_trylock`.
* **Potential False Positives:** Using `read_trylock` everywhere could cause spurious failures in process context, leading to unnecessary slow-path execution. The slow path is not an ideal result since it involves calling `kvm_gpc_refresh`, so I need to avoid this if at all possible. This means relying on the `in_atomic` flag instead of blindly using `read_trylock`.
* **PREEMPT_RT Consideration:** The main motivation here is PREEMPT_RT, where `read_lock_irqsave` in an atomic context is completely forbidden because it sleeps, but the `in_atomic` flag helps.
* **The Clean Solution:** The best path is to change `kvm_xen_set_evtchn_fast`'s signature to `int kvm_xen_set_evtchn_fast(struct kvm_xen_evtchn *xe, struct kvm *kvm, bool in_atomic)`. Then, inside the function, use `read_trylock` when `in_atomic` is true, and the existing `read_lock_irqsave` otherwise. The `in_atomic` value will be passed from `xen_timer_callback` (true), `kvm_arch_set_irq_inatomic` (true), and `kvm_xen_set_evtchn` (false). I'll need to update the calling function, `kvm_arch_set_irq_inatomic`, accordingly. This makes the code safe for both non-RT and RT environments. It mirrors the approach already taken in `kvm_xen_update_runstate_guest`. This avoids unnecessary calls to the slower path.
* **Exported Function Check**: I should also check to see if the function is exported, but it appears in the `xen.h` header file.
This seems the most robust solution. I'll implement the signature change and conditional lock acquisition based on the `in_atomic` flag, maintaining the existing behavior in the non-atomic process context and making it safe for atomic contexts.
|
| 77/2 |
2026/08/12 11:58 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:arch/x86/kvm/xen.h Name:kvm_xen_set_evtchn_fast]
Results: map[SourceCode:1785: /*
1786: * The return value from this function is propagated to kvm_set_irq() API,
1787: * so it returns:
1788: * < 0 Interrupt was ignored (masked or not delivered for other reasons)
1789: * = 0 Interrupt was coalesced (previous irq is still pending)
1790: * > 0 Number of CPUs interrupt was delivered to
1791: *
1792: * It is also called directly from kvm_arch_set_irq_inatomic(), where the
1793: * only check on its return value is a comparison with -EWOULDBLOCK'.
1794: */
1795: int kvm_xen_set_evtchn_fast(struct kvm_xen_evtchn *xe, struct kvm *kvm)
1796: {
1797: struct gfn_to_pfn_cache *gpc = &kvm->arch.xen.shinfo_cache;
1798: struct kvm_vcpu *vcpu;
1799: unsigned long *pending_bits, *mask_bits;
1800: unsigned long flags;
1801: int port_word_bit;
1802: bool kick_vcpu = false;
1803: int vcpu_idx, idx, rc;
1804:
1805: vcpu_idx = READ_ONCE(xe->vcpu_idx);
1806: if (vcpu_idx >= 0)
1807: vcpu = kvm_get_vcpu(kvm, vcpu_idx);
1808: else {
1809: vcpu = kvm_get_vcpu_by_id(kvm, xe->vcpu_id);
1810: if (!vcpu)
1811: return -EINVAL;
1812: WRITE_ONCE(xe->vcpu_idx, vcpu->vcpu_idx);
1813: }
1814:
1815: if (xe->port >= max_evtchn_port(kvm))
1816: return -EINVAL;
1817:
1818: rc = -EWOULDBLOCK;
1819:
1820: idx = srcu_read_lock(&kvm->srcu);
1821:
1822: read_lock_irqsave(&gpc->lock, flags);
1823: if (!kvm_gpc_check(gpc, PAGE_SIZE))
1824: goto out_rcu;
1825:
1826: if (IS_ENABLED(CONFIG_64BIT) && kvm->arch.xen.long_mode) {
1827: struct shared_info *shinfo = gpc->khva;
1828: pending_bits = (unsigned long *)&shinfo->evtchn_pending;
1829: mask_bits = (unsigned long *)&shinfo->evtchn_mask;
1830: port_word_bit = xe->port / 64;
1831: } else {
1832: struct compat_shared_info *shinfo = gpc->khva;
1833: pending_bits = (unsigned long *)&shinfo->evtchn_pending;
1834: mask_bits = (unsigned long *)&shinfo->evtchn_mask;
1835: port_word_bit = xe->port / 32;
1836: }
1837:
1838: /*
1839: * If this port wasn't already set, and if it isn't masked, then
1840: * we try to set the corresponding bit in the in-kernel shadow of
1841: * evtchn_pending_sel for the target vCPU. And if *that* wasn't
1842: * already set, then we kick the vCPU in question to write to the
1843: * *real* evtchn_pending_sel in its own guest vcpu_info struct.
1844: */
1845: if (test_and_set_bit(xe->port, pending_bits)) {
1846: rc = 0; /* It was already raised */
1847: } else if (test_bit(xe->port, mask_bits)) {
1848: rc = -ENOTCONN; /* Masked */
1849: kvm_xen_check_poller(vcpu, xe->port);
1850: } else {
1851: rc = 1; /* Delivered to the bitmap in shared_info. */
1852: /* Now switch to the vCPU's vcpu_info to set the index and pending_sel */
1853: read_unlock_irqrestore(&gpc->lock, flags);
1854: gpc = &vcpu->arch.xen.vcpu_info_cache;
1855:
1856: read_lock_irqsave(&gpc->lock, flags);
1857: if (!kvm_gpc_check(gpc, sizeof(struct vcpu_info))) {
1858: /*
1859: * Could not access the vcpu_info. Set the bit in-kernel
1860: * and prod the vCPU to deliver it for itself.
1861: */
1862: if (!test_and_set_bit(port_word_bit, &vcpu->arch.xen.evtchn_pending_sel))
1863: kick_vcpu = true;
1864: goto out_rcu;
1865: }
1866:
1867: if (IS_ENABLED(CONFIG_64BIT) && kvm->arch.xen.long_mode) {
1868: struct vcpu_info *vcpu_info = gpc->khva;
1869: if (!test_and_set_bit(port_word_bit, &vcpu_info->evtchn_pending_sel)) {
1870: WRITE_ONCE(vcpu_info->evtchn_upcall_pending, 1);
1871: kick_vcpu = true;
1872: }
1873: } else {
1874: struct compat_vcpu_info *vcpu_info = gpc->khva;
1875: if (!test_and_set_bit(port_word_bit,
1876: (unsigned long *)&vcpu_info->evtchn_pending_sel)) {
1877: WRITE_ONCE(vcpu_info->evtchn_upcall_pending, 1);
1878: kick_vcpu = true;
1879: }
1880: }
1881:
1882: /* For the per-vCPU lapic vector, deliver it as MSI. */
1883: if (kick_vcpu && vcpu->arch.xen.upcall_vector) {
1884: kvm_xen_inject_vcpu_vector(vcpu);
1885: kick_vcpu = false;
1886: }
1887: }
1888:
1889: out_rcu:
1890: read_unlock_irqrestore(&gpc->lock, flags);
1891: srcu_read_unlock(&kvm->srcu, idx);
1892:
1893: if (kick_vcpu) {
1894: kvm_make_request(KVM_REQ_UNBLOCK, vcpu);
1895: kvm_vcpu_kick(vcpu);
1896: }
1897:
1898: return rc;
1899: }
SourceFile:arch/x86/kvm/xen.c]
|
| 78/2 |
2026/08/12 11:58 |
llm |
debugger |
0m
Model:
Tokens:
input: 73220
output: 47
thoughts: 80
Thoughts: **Investigating the KVM/Xen Event Channel Function**
Okay, hold on a second. I was under the impression that `kvm_xen_set_evtchn_fast` was a publicly available function, maybe defined in `xen.h`. But a closer look reveals that's not the case. It's actually *defined* within `xen.c`, which makes it a private implementation detail, and it seems to only be used within `xen.c` itself, and in `irq.c`. That's unexpected. Wait a minute... `irq.c` calls it? That's definitely worth digging into. I need to understand why `irq.c` relies on this internally-defined function, given its presumed purpose related to event channels. Time to check `arch/x86/kvm/xen.h` specifically to see if anything sheds light on this apparent discrepancy. Maybe there's a good reason for this internal usage that isn't immediately obvious.
|
| 79/2 |
2026/08/12 11:58 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:arch/x86/kvm/xen.h Name:kvm_xen_set_evtchn_fast]
Results: map[SourceCode:1785: /*
1786: * The return value from this function is propagated to kvm_set_irq() API,
1787: * so it returns:
1788: * < 0 Interrupt was ignored (masked or not delivered for other reasons)
1789: * = 0 Interrupt was coalesced (previous irq is still pending)
1790: * > 0 Number of CPUs interrupt was delivered to
1791: *
1792: * It is also called directly from kvm_arch_set_irq_inatomic(), where the
1793: * only check on its return value is a comparison with -EWOULDBLOCK'.
1794: */
1795: int kvm_xen_set_evtchn_fast(struct kvm_xen_evtchn *xe, struct kvm *kvm)
1796: {
1797: struct gfn_to_pfn_cache *gpc = &kvm->arch.xen.shinfo_cache;
1798: struct kvm_vcpu *vcpu;
1799: unsigned long *pending_bits, *mask_bits;
1800: unsigned long flags;
1801: int port_word_bit;
1802: bool kick_vcpu = false;
1803: int vcpu_idx, idx, rc;
1804:
1805: vcpu_idx = READ_ONCE(xe->vcpu_idx);
1806: if (vcpu_idx >= 0)
1807: vcpu = kvm_get_vcpu(kvm, vcpu_idx);
1808: else {
1809: vcpu = kvm_get_vcpu_by_id(kvm, xe->vcpu_id);
1810: if (!vcpu)
1811: return -EINVAL;
1812: WRITE_ONCE(xe->vcpu_idx, vcpu->vcpu_idx);
1813: }
1814:
1815: if (xe->port >= max_evtchn_port(kvm))
1816: return -EINVAL;
1817:
1818: rc = -EWOULDBLOCK;
1819:
1820: idx = srcu_read_lock(&kvm->srcu);
1821:
1822: read_lock_irqsave(&gpc->lock, flags);
1823: if (!kvm_gpc_check(gpc, PAGE_SIZE))
1824: goto out_rcu;
1825:
1826: if (IS_ENABLED(CONFIG_64BIT) && kvm->arch.xen.long_mode) {
1827: struct shared_info *shinfo = gpc->khva;
1828: pending_bits = (unsigned long *)&shinfo->evtchn_pending;
1829: mask_bits = (unsigned long *)&shinfo->evtchn_mask;
1830: port_word_bit = xe->port / 64;
1831: } else {
1832: struct compat_shared_info *shinfo = gpc->khva;
1833: pending_bits = (unsigned long *)&shinfo->evtchn_pending;
1834: mask_bits = (unsigned long *)&shinfo->evtchn_mask;
1835: port_word_bit = xe->port / 32;
1836: }
1837:
1838: /*
1839: * If this port wasn't already set, and if it isn't masked, then
1840: * we try to set the corresponding bit in the in-kernel shadow of
1841: * evtchn_pending_sel for the target vCPU. And if *that* wasn't
1842: * already set, then we kick the vCPU in question to write to the
1843: * *real* evtchn_pending_sel in its own guest vcpu_info struct.
1844: */
1845: if (test_and_set_bit(xe->port, pending_bits)) {
1846: rc = 0; /* It was already raised */
1847: } else if (test_bit(xe->port, mask_bits)) {
1848: rc = -ENOTCONN; /* Masked */
1849: kvm_xen_check_poller(vcpu, xe->port);
1850: } else {
1851: rc = 1; /* Delivered to the bitmap in shared_info. */
1852: /* Now switch to the vCPU's vcpu_info to set the index and pending_sel */
1853: read_unlock_irqrestore(&gpc->lock, flags);
1854: gpc = &vcpu->arch.xen.vcpu_info_cache;
1855:
1856: read_lock_irqsave(&gpc->lock, flags);
1857: if (!kvm_gpc_check(gpc, sizeof(struct vcpu_info))) {
1858: /*
1859: * Could not access the vcpu_info. Set the bit in-kernel
1860: * and prod the vCPU to deliver it for itself.
1861: */
1862: if (!test_and_set_bit(port_word_bit, &vcpu->arch.xen.evtchn_pending_sel))
1863: kick_vcpu = true;
1864: goto out_rcu;
1865: }
1866:
1867: if (IS_ENABLED(CONFIG_64BIT) && kvm->arch.xen.long_mode) {
1868: struct vcpu_info *vcpu_info = gpc->khva;
1869: if (!test_and_set_bit(port_word_bit, &vcpu_info->evtchn_pending_sel)) {
1870: WRITE_ONCE(vcpu_info->evtchn_upcall_pending, 1);
1871: kick_vcpu = true;
1872: }
1873: } else {
1874: struct compat_vcpu_info *vcpu_info = gpc->khva;
1875: if (!test_and_set_bit(port_word_bit,
1876: (unsigned long *)&vcpu_info->evtchn_pending_sel)) {
1877: WRITE_ONCE(vcpu_info->evtchn_upcall_pending, 1);
1878: kick_vcpu = true;
1879: }
1880: }
1881:
1882: /* For the per-vCPU lapic vector, deliver it as MSI. */
1883: if (kick_vcpu && vcpu->arch.xen.upcall_vector) {
1884: kvm_xen_inject_vcpu_vector(vcpu);
1885: kick_vcpu = false;
1886: }
1887: }
1888:
1889: out_rcu:
1890: read_unlock_irqrestore(&gpc->lock, flags);
1891: srcu_read_unlock(&kvm->srcu, idx);
1892:
1893: if (kick_vcpu) {
1894: kvm_make_request(KVM_REQ_UNBLOCK, vcpu);
1895: kvm_vcpu_kick(vcpu);
1896: }
1897:
1898: return rc;
1899: }
SourceFile:arch/x86/kvm/xen.c]
|
| 80/2 |
2026/08/12 11:58 |
llm |
debugger |
0m
Model:
Tokens:
input: 75643
output: 43
thoughts: 155
Thoughts: **Investigating the KVM/Xen Interaction**
Okay, hold on a second. I was under the impression that `kvm_xen_set_evtchn_fast` was defined in `arch/x86/kvm/xen.c` but *not* exported in `xen.h`. That's strange. But I know `kvm_arch_set_irq_inatomic`, which is in `arch/x86/kvm/irq.c`, actually calls it! How is that even possible? It doesn't make sense that a function in `irq.c` could call something that wasn't exported in a header. Let me see, aha, the function *is* exported in `arch/x86/kvm/xen.h`! Now I have to go double check `arch/x86/kvm/xen.h` to see how it's defined.
|
| 81/2 |
2026/08/12 11:58 |
tool |
grepper |
0m
Args: map[Expression:kvm_xen_set_evtchn_fast PathPrefix:arch/x86/kvm/xen.h]
Results: map[Output:arch/x86/kvm/xen.h=33=void kvm_xen_destroy_vcpu(struct kvm_vcpu *vcpu);
arch/x86/kvm/xen.h:34:int kvm_xen_set_evtchn_fast(struct kvm_xen_evtchn *xe,
arch/x86/kvm/xen.h-35- struct kvm *kvm);
]
|
| 82/2 |
2026/08/12 11:58 |
llm |
debugger |
0m
Model:
Tokens:
input: 76160
output: 45
thoughts: 5838
Thoughts: **Refining Interrupt Handling in KVM/Xen: A Deep Dive**
Okay, here's the breakdown of my thought process. I need to make some changes to how event channels are handled in KVM/Xen, particularly to avoid potential sleeping in atomic context issues, especially when PREEMPT_RT is enabled. The goal is to use non-blocking locking where appropriate and prevent any hard lock contention.
First, I realized that the `kvm_xen_set_evtchn_fast` function, which is the core of this interrupt delivery mechanism, is called from multiple places, some in process context, some in interrupt context. The first step, logically, is to make the lock acquisition non-blocking if we are in an atomic context. This requires us to change the function signature to accept a boolean argument `in_atomic`. This flag will be set by the caller and will tell the function whether it is safe to use a non-blocking lock.
Initial thoughts were around using `in_atomic() || irqs_disabled()` macro, but I concluded that it wasn't the cleanest approach. By introducing the `in_atomic` argument, we create a clear and controlled way to determine if we are in an atomic context.
After this change, `kvm_xen_set_evtchn_fast` will use `read_trylock` if `in_atomic` is true, or `read_lock_irqsave` if it is false.
I then went through the existing code and realized that the function has *two* lock acquisitions, so I applied the same logic for both. I also realized that if the second `read_trylock` fails, we need to correctly handle the error, since the function calls `read_unlock_irqrestore` before entering the second lock acquisition. The logic must then be modified to jump to `out_rcu_no_lock` if either try lock fails.
Furthermore, I have discovered that `kvm_xen_update_runstate_guest` already uses the exact same `read_trylock` in atomic context, so it validates the correctness of the new approach. It becomes clear that in the cases where the try lock fails, we fall back to the slower interrupt delivery mechanism.
All in all, the final implementation looks safe and robust, avoiding the spinlock contention.
|
| 83/2 |
2026/08/12 11:59 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:arch/x86/kvm/xen.c Name:kvm_xen_set_evtchn]
Results: map[SourceCode:1901: static int kvm_xen_set_evtchn(struct kvm_xen_evtchn *xe, struct kvm *kvm)
1902: {
1903: bool mm_borrowed = false;
1904: int rc;
1905:
1906: rc = kvm_xen_set_evtchn_fast(xe, kvm);
1907: if (rc != -EWOULDBLOCK)
1908: return rc;
1909:
1910: if (current->mm != kvm->mm) {
1911: /*
1912: * If not on a thread which already belongs to this KVM,
1913: * we'd better be in the irqfd workqueue.
1914: */
1915: if (WARN_ON_ONCE(current->mm))
1916: return -EINVAL;
1917:
1918: kthread_use_mm(kvm->mm);
1919: mm_borrowed = true;
1920: }
1921:
1922: /*
1923: * It is theoretically possible for the page to be unmapped
1924: * and the MMU notifier to invalidate the shared_info before
1925: * we even get to use it. In that case, this looks like an
1926: * infinite loop. It was tempting to do it via the userspace
1927: * HVA instead... but that just *hides* the fact that it's
1928: * an infinite loop, because if a fault occurs and it waits
1929: * for the page to come back, it can *still* immediately
1930: * fault and have to wait again, repeatedly.
1931: *
1932: * Conversely, the page could also have been reinstated by
1933: * another thread before we even obtain the mutex above, so
1934: * check again *first* before remapping it.
1935: */
1936: do {
1937: struct gfn_to_pfn_cache *gpc = &kvm->arch.xen.shinfo_cache;
1938: int idx;
1939:
1940: rc = kvm_xen_set_evtchn_fast(xe, kvm);
1941: if (rc != -EWOULDBLOCK)
1942: break;
1943:
1944: idx = srcu_read_lock(&kvm->srcu);
1945: rc = kvm_gpc_refresh(gpc, PAGE_SIZE);
1946: srcu_read_unlock(&kvm->srcu, idx);
1947: } while(!rc);
1948:
1949: if (mm_borrowed)
1950: kthread_unuse_mm(kvm->mm);
1951:
1952: return rc;
1953: }
SourceFile:arch/x86/kvm/xen.c]
|
| 84/2 |
2026/08/12 11:59 |
llm |
debugger |
0m
Model:
Tokens:
input: 83386
output: 1209
thoughts: 754
Thoughts: **Analyzing the Event Channel and Lock Contention in KVM/Xen Interaction**
Okay, let's break this down. Initially, I was looking at how `kvm_xen_set_evtchn` is called. It's called twice, and the expectation is that both times, `in_atomic` is `false`. That seemed straightforward until I started tracing the call chain. It's called from `kvm_xen_hcall_evtchn_send` and `kvm_xen_irqfd_assign`, both within a process context. But then, I found that `kvm_xen_set_evtchn` is also invoked from `irqfd_wakeup` if `kvm_arch_set_irq_inatomic` returns `-EWOULDBLOCK`.
Ah, that's where it gets interesting. `irqfd_wakeup` schedules a workqueue, and that workqueue calls `irqfd_inject`, which in turn calls `kvm_set_irq`, ultimately leading to `kvm_xen_set_evtchn`. And `irqfd_inject` is, crucially, a workqueue item, meaning it operates in process context (specifically, a `kworker`). This confirms that `in_atomic` is indeed `false` for this path.
But wait, what about the potential for `-EWOULDBLOCK` from `kvm_xen_set_evtchn_fast` when `in_atomic` is false? I went through the code path and realized that when it returns `-EWOULDBLOCK`, it loops, calls `kvm_gpc_refresh`, and then calls `kvm_xen_set_evtchn_fast` again. Since `in_atomic` is false, it uses `read_lock_irqsave`, which waits for the writer to finish. Given the sleeping nature of these locks and the fact that we were not in atomic context, this doesn't lead to any sort of an infinite loop. This checks out.
So, the root cause here is pretty clear in PREEMPT_RT. The `rwlock_t` type is mapped to `rt_rw_lock`, which is a sleeping lock. `kvm_xen_set_evtchn_fast` acquires a read lock using `read_lock_irqsave(&gpc->lock, flags)`, where `gpc->lock` is an `rwlock_t`. The issue is that `kvm_xen_set_evtchn_fast` is called from `xen_timer_callback` (which runs in hardirq context as a hrtimer callback) and `kvm_arch_set_irq_inatomic` (called from a waitqueue wakeup, also in hardirq context). Attempting to acquire a sleeping lock within a hardirq context results in a "BUG: sleeping function called from invalid context" splat. This is invalid in PREEMPT_RT.
The fix is to modify `kvm_xen_set_evtchn_fast` to take a `bool in_atomic` parameter. When `in_atomic` is true, we use `read_trylock(&gpc->lock)` instead of `read_lock_irqsave`. If `read_trylock` fails, we either return `-EWOULDBLOCK` (for the initial lock) or fall back to setting a pending bit in-kernel and then kicking the vCPU (for the second lock). Caller's are updated to pass `true` when calling from atomic context (`xen_timer_callback` and `kvm_arch_set_irq_inatomic`) and `false` when calling from process context (`kvm_xen_set_evtchn`). This mirrors how we handle `gfn_to_pfn_cache` locks in atomic context via `kvm_xen_update_runstate_guest`. This feels like a solid solution. I'll document this accordingly.
|
| 85/1 |
2026/08/12 11:59 |
agent |
history-explorer |
3m
Model:
Instruction:
You are an experienced Linux kernel developer researching prior art for fixing a kernel bug.
You are given a bug explanation. This explanation details the root cause of the bug resulting
from debugging, but does not provide the final fix strategy. Your goal is to explore how
similar bugs were fixed in the past in the same subsystem or files.
CRITICAL: Do NOT attempt to debug the issue further or write a patch for it yourself.
Your ONLY objective is to research and provide the necessary historical context.
Use the git-log tool with the Since parameter set to "3 years" to focus on recent history.
Search for commits that address issues with similar root causes (e.g. similar missing locks,
incorrect refcounting, or similar error path bugs) in the affected files.
Your final reply must summarize your findings: what idioms, locking rules, or common patterns
should be followed when writing a fix for this bug based on how previous similar bugs were addressed.
If you find no relevant past fixes, clearly state that.
Don't make assumptions about the kernel source code (it may be different from what you assume it is).
Extensively use the provided code access tools (codesearch-*, git-*, grepper, etc)
to examine the actual source code, and confirm any assumptions.
Prefer calling several tools at the same time to save round-trips.
Prompt:
The crash is:
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
preempt_count: 10001, expected: 0
RCU nest depth: 0, expected: 0
1 lock held by kworker/0:5/5685:
#0: ffff8881964ddb48 (&kvm->srcu){.?.+}-{0:0}, at: srcu_lock_acquire include/linux/srcu.h:187 [inline]
#0: ffff8881964ddb48 (&kvm->srcu){.?.+}-{0:0}, at: srcu_read_lock include/linux/srcu.h:294 [inline]
#0: ffff8881964ddb48 (&kvm->srcu){.?.+}-{0:0}, at: kvm_xen_set_evtchn_fast+0x1be/0x990 arch/x86/kvm/xen.c:1820
irq event stamp: 78678
hardirqs last enabled at (78677): [<ffffffff8194da12>] raw_spin_rq_unlock_irq kernel/sched/sched.h:1673 [inline]
hardirqs last enabled at (78677): [<ffffffff8194da12>] finish_lock_switch kernel/sched/core.c:5241 [inline]
hardirqs last enabled at (78677): [<ffffffff8194da12>] finish_task_switch+0x412/0xc60 kernel/sched/core.c:5359
hardirqs last disabled at (78678): [<ffffffff8b4edc4e>] sysvec_apic_timer_interrupt+0xe/0xc0 arch/x86/kernel/apic/apic.c:1062
softirqs last enabled at (78668): [<ffffffff8187f8b0>] __local_bh_enable_ip+0x1a0/0x2b0 kernel/softirq.c:305
softirqs last disabled at (78666): [<ffffffff861be59f>] local_bh_disable include/linux/bottom_half.h:20 [inline]
softirqs last disabled at (78666): [<ffffffff861be59f>] rcu_read_lock_bh include/linux/rcupdate.h:893 [inline]
softirqs last disabled at (78666): [<ffffffff861be59f>] keep_key_fresh drivers/net/wireguard/send.c:129 [inline]
softirqs last disabled at (78666): [<ffffffff861be59f>] wg_packet_create_data_done drivers/net/wireguard/send.c:259 [inline]
softirqs last disabled at (78666): [<ffffffff861be59f>] wg_packet_tx_worker+0x25f/0x7e0 drivers/net/wireguard/send.c:276
Preemption disabled at:
[<ffffffff8b50149d>] __schedule_loop kernel/sched/core.c:7310 [inline]
[<ffffffff8b50149d>] schedule+0x15d/0x2b0 kernel/sched/core.c:7326
CPU: 0 UID: 0 PID: 5685 Comm: kworker/0:5 Not tainted syzkaller #1 PREEMPT_{RT,(full)}
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
Workqueue: 0x0 (wg-crypt-wg1)
Call Trace:
<IRQ>
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
instr_sysvec_apic_timer_interrupt arch/x86/kernel/apic/apic.c:1062 [inline]
sysvec_apic_timer_interrupt+0xa1/0xc0 arch/x86/kernel/apic/apic.c:1062
</IRQ>
<TASK>
asm_sysvec_apic_timer_interrupt+0x1a/0x20 arch/x86/include/asm/idtentry.h:674
RIP: 0010:finish_task_switch+0x417/0xc60 kernel/sched/core.c:5361
Code: 04 00 00 41 c7 84 24 20 0e 00 00 00 00 00 00 0f 1f 44 00 00 49 83 c4 48 4c 89 e7 e8 b3 99 bd 09 e8 de c7 38 00 fb 4c 8b 65 c8 <49> 8d bc 24 e0 16 00 00 48 89 f8 48 c1 e8 03 42 0f b6 04 30 84 c0
RSP: 0018:ffffc9000442fa60 EFLAGS: 00000206
RAX: 0000000000013355 RBX: ffff88813883c6e0 RCX: 0000000080000001
RDX: 0000000000000000 RSI: ffffffff8daa4fe1 RDI: ffffffff8bd1a580
RBP: ffffc9000442fab0 R08: ffffffff8fd9f0f7 R09: 1ffffffff1fb3e1e
R10: dffffc0000000000 R11: fffffbfff1fb3e1f R12: ffff888114048000
R13: ffff88813883c6a8 R14: dffffc0000000000 R15: 1ffff110271078dc
context_switch kernel/sched/core.c:5513 [inline]
__schedule+0x1711/0x5530 kernel/sched/core.c:7234
__schedule_loop kernel/sched/core.c:7311 [inline]
schedule+0x164/0x2b0 kernel/sched/core.c:7326
worker_thread+0xc12/0xfb0 kernel/workqueue.c:3501
kthread+0x388/0x470 kernel/kthread.c:436
ret_from_fork+0x514/0xb70 arch/x86/kernel/process.c:158
ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245
</TASK>
=============================
[ BUG: Invalid wait context ]
syzkaller #1 Tainted: G W
-----------------------------
kworker/0:5/5685 is trying to lock:
ffff8881964dd530 (&gpc->lock){....}-{3:3}, at: kvm_xen_set_evtchn_fast+0x1f4/0x990 arch/x86/kvm/xen.c:1822
other info that might help us debug this:
context-{2:2}
1 lock held by kworker/0:5/5685:
#0: ffff8881964ddb48 (&kvm->srcu){.?.+}-{0:0}, at: srcu_lock_acquire include/linux/srcu.h:187 [inline]
#0: ffff8881964ddb48 (&kvm->srcu){.?.+}-{0:0}, at: srcu_read_lock include/linux/srcu.h:294 [inline]
#0: ffff8881964ddb48 (&kvm->srcu){.?.+}-{0:0}, at: kvm_xen_set_evtchn_fast+0x1be/0x990 arch/x86/kvm/xen.c:1820
stack backtrace:
CPU: 0 UID: 0 PID: 5685 Comm: kworker/0:5 Tainted: G W syzkaller #1 PREEMPT_{RT,(full)}
Tainted: [W]=WARN
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
Workqueue: 0x0 (wg-crypt-wg1)
Call Trace:
<IRQ>
dump_stack_lvl+0xe8/0x150 lib/dump_stack.c:120
print_lock_invalid_wait_context kernel/locking/lockdep.c:4830 [inline]
check_wait_context kernel/locking/lockdep.c:4902 [inline]
__lock_acquire+0xe39/0x2ce0 kernel/locking/lockdep.c:5187
lock_acquire+0x106/0x350 kernel/locking/lockdep.c:5868
rt_read_lock+0xcc/0x4b0 kernel/locking/spinlock_rt.c:249
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
instr_sysvec_apic_timer_interrupt arch/x86/kernel/apic/apic.c:1062 [inline]
sysvec_apic_timer_interrupt+0xa1/0xc0 arch/x86/kernel/apic/apic.c:1062
</IRQ>
<TASK>
asm_sysvec_apic_timer_interrupt+0x1a/0x20 arch/x86/include/asm/idtentry.h:674
RIP: 0010:finish_task_switch+0x417/0xc60 kernel/sched/core.c:5361
Code: 04 00 00 41 c7 84 24 20 0e 00 00 00 00 00 00 0f 1f 44 00 00 49 83 c4 48 4c 89 e7 e8 b3 99 bd 09 e8 de c7 38 00 fb 4c 8b 65 c8 <49> 8d bc 24 e0 16 00 00 48 89 f8 48 c1 e8 03 42 0f b6 04 30 84 c0
RSP: 0018:ffffc9000442fa60 EFLAGS: 00000206
RAX: 0000000000013355 RBX: ffff88813883c6e0 RCX: 0000000080000001
RDX: 0000000000000000 RSI: ffffffff8daa4fe1 RDI: ffffffff8bd1a580
RBP: ffffc9000442fab0 R08: ffffffff8fd9f0f7 R09: 1ffffffff1fb3e1e
R10: dffffc0000000000 R11: fffffbfff1fb3e1f R12: ffff888114048000
R13: ffff88813883c6a8 R14: dffffc0000000000 R15: 1ffff110271078dc
context_switch kernel/sched/core.c:5513 [inline]
__schedule+0x1711/0x5530 kernel/sched/core.c:7234
__schedule_loop kernel/sched/core.c:7311 [inline]
schedule+0x164/0x2b0 kernel/sched/core.c:7326
worker_thread+0xc12/0xfb0 kernel/workqueue.c:3501
kthread+0x388/0x470 kernel/kthread.c:436
ret_from_fork+0x514/0xb70 arch/x86/kernel/process.c:158
ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245
</TASK>
----------------
Code disassembly (best guess), 1 bytes skipped:
0: 00 00 add %al,(%rax)
2: 41 c7 84 24 20 0e 00 movl $0x0,0xe20(%r12)
9: 00 00 00 00 00
e: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)
13: 49 83 c4 48 add $0x48,%r12
17: 4c 89 e7 mov %r12,%rdi
1a: e8 b3 99 bd 09 call 0x9bd99d2
1f: e8 de c7 38 00 call 0x38c802
24: fb sti
25: 4c 8b 65 c8 mov -0x38(%rbp),%r12
* 29: 49 8d bc 24 e0 16 00 lea 0x16e0(%r12),%rdi <-- trapping instruction
30: 00
31: 48 89 f8 mov %rdi,%rax
34: 48 c1 e8 03 shr $0x3,%rax
38: 42 0f b6 04 30 movzbl (%rax,%r14,1),%eax
3d: 84 c0 test %al,%al
The explanation of the root cause of the bug is:
The root cause of this crash is an attempt to acquire a sleeping lock from a hardirq context on a `PREEMPT_RT` kernel.
### Detailed Analysis
1. **The Lock:**
The crash happens when `kvm_xen_set_evtchn_fast()` attempts to acquire `gpc->lock` via `read_lock_irqsave(&gpc->lock, flags)`. The `gpc->lock` is an `rwlock_t` used to protect the `gfn_to_pfn_cache`.
In a `PREEMPT_RT` kernel, `rwlock_t` is mapped to `rt_rw_lock`, which is a sleeping lock (rtmutex-based). It cannot be acquired in hardirq context because it may sleep while waiting for the lock or while waking up waiters during unlock.
2. **The Context:**
`kvm_xen_set_evtchn_fast()` is called from `xen_timer_callback()`, which is an hrtimer callback initialized with `HRTIMER_MODE_ABS_HARD`. This means the timer callback executes in hardirq context, even on `PREEMPT_RT`.
Additionally, `kvm_xen_set_evtchn_fast()` is also called from `kvm_arch_set_irq_inatomic()`, which is invoked during `irqfd` waitqueue wakeups (also a hardirq/atomic context).
3. **The Lockdep Splat:**
The lockdep splat mentions `srcu_read_lock` because `kvm->srcu` was acquired just before the `read_lock_irqsave`. While `srcu_read_lock` itself is perfectly safe in hardirq context (it just increments a per-CPU counter), lockdep prints it as a "held lock" when the actual sleeping function (`rt_read_lock`) is called.
### How to Fix
To fix this, `kvm_xen_set_evtchn_fast()` must avoid using `read_lock_irqsave()` when called from an atomic context. Instead, it should use `read_trylock()`. If the trylock fails, it can safely fall back to the slow path (returning `-EWOULDBLOCK` or kicking the vCPU), which is exactly what the callers expect and handle.
This pattern of using `read_trylock()` for `gpc->lock` in atomic contexts is already established in KVM Xen (e.g., in `kvm_xen_update_runstate_guest()`).
You should modify `kvm_xen_set_evtchn_fast()` to accept a `bool in_atomic` parameter:
```c
// arch/x86/kvm/xen.h
int kvm_xen_set_evtchn_fast(struct kvm_xen_evtchn *xe, struct kvm *kvm, bool in_atomic);
```
Then update `kvm_xen_set_evtchn_fast()` in `arch/x86/kvm/xen.c` to use `read_trylock()` when `in_atomic` is true:
```c
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);
}
```
You will need to do this for **both** `gpc->lock` acquisitions in that function. For the second acquisition (the `vcpu_info_cache`), if the trylock fails, you should fall back to setting the bit in-kernel and kicking the vCPU, just like it does when `kvm_gpc_check()` fails:
```c
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);
}
```
Make sure to add the `out_rcu_no_lock` label to avoid unlocking a lock you don't hold:
```c
out_rcu:
read_unlock_irqrestore(&gpc->lock, flags);
out_rcu_no_lock:
srcu_read_unlock(&kvm->srcu, idx);
```
Finally, update the callers to pass the correct context:
- `xen_timer_callback()` in `arch/x86/kvm/xen.c`: pass `true`.
- `kvm_arch_set_irq_inatomic()` in `arch/x86/kvm/irq.c`: pass `true`.
- `kvm_xen_set_evtchn()` in `arch/x86/kvm/xen.c` (both calls): pass `false` (since it runs in process context, e.g., from a hypercall or the irqfd workqueue).
Reply: Based on the historical context and prior fixes in the KVM subsystem, the proposed fix perfectly aligns with established idioms for handling locking in atomic contexts, specifically regarding the `gfn_to_pfn_cache` (`gpc`).
Here is a summary of the findings, locking rules, and common patterns you should follow when writing the fix:
### 1. Established Idiom: `read_trylock()` with Slow-Path Fallback
The primary pattern for safely accessing `gpc->lock` (an `rwlock_t` which becomes a sleepable `rt_rw_lock` on PREEMPT_RT) from an atomic or hardirq context is to conditionally use `read_trylock()` and fall back to a slow path if the lock is contended.
**Relevant Prior Art:**
This exact pattern was established in `arch/x86/kvm/xen.c` by **commit `bbe17c625d684` ("KVM: x86/xen: Fix potential deadlock in kvm_xen_update_runstate_guest()")**.
* In that commit, `kvm_xen_update_runstate_guest()` was modified to accept a `bool atomic` parameter because it could be called from a preempt notifier (an atomic context).
* When `atomic` is true, the code uses `read_trylock(&gpc->lock)` instead of `read_lock_irqsave()`.
* If the trylock fails, it simply aborts the opportunistic update and returns.
This perfectly mirrors your proposed fix for `kvm_xen_set_evtchn_fast()`: plumbing a `bool in_atomic` parameter, using `read_trylock()`, and falling back to the slow path (returning `-EWOULDBLOCK` or kicking the vCPU) if the lock cannot be immediately acquired.
### 2. Why Not Convert to a Raw Lock?
Another common pattern in the kernel for fixing PREEMPT_RT "sleeping in atomic context" bugs is to convert the offending lock to a raw lock.
**Relevant Prior Art:**
**Commit `fd92bd3b4445` ("KVM: SVM: switch to raw spinlock for svm->ir_list_lock")** fixed a very similar "Invalid wait context" lockdep splat by converting a `spinlock_t` to a `raw_spinlock_t` because it was acquired during `schedule()`.
However, following this pattern is **not appropriate** for this bug. The `gpc->lock` is an `rwlock_t` that belongs to the generic `gfn_to_pfn_cache` infrastructure used extensively across KVM. Converting it to a `raw_rwlock_t` would globally affect KVM and is unfeasible because the lock protects cache invalidations that may legitimately need to sleep or be held for longer durations. Thus, the localized `read_trylock()` approach is the correct idiom.
### Summary of Rules to Follow for the Fix:
When writing your patch, you should strictly follow the pattern set by `kvm_xen_update_runstate_guest()`:
1. **Context Parameter:** Plumb a `bool in_atomic` (or `bool atomic`) parameter through the call stack to KVM Xen event channel delivery functions.
2. **Conditional Locking:** Use `if (in_atomic) { ... read_trylock(&gpc->lock) ... } else { read_lock_irqsave(&gpc->lock, flags); }`.
3. **Graceful Fallback:** If the trylock fails in atomic context, do not attempt to spin or wait. Immediately fall back to the slow path (e.g., returning `-EWOULDBLOCK` for the first lock, or setting the bit in-kernel and kicking the vCPU for the second lock), exactly as the code currently does when `kvm_gpc_check()` fails.
|
| 86/2 |
2026/08/12 11:59 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 9338
output: 48
thoughts: 302
Thoughts: **Investigating the KVM Lock Issue**
Okay, so we've got a bug related to acquiring a sleeping lock (`rwlock_t`, specifically `rt_rw_lock` with PREEMPT_RT) from a hardirq context in KVM's x86 implementation, specifically in `arch/x86/kvm/xen.c`. The suggested fix is to use `read_trylock()` instead of `read_lock_irqsave()` within an atomic context, with a fallback to a slower path if the lock isn't immediately available. Sounds like a classic context issue.
My first thought is to leverage past experiences with similar problems. This smells like something that's probably been encountered and fixed before in this subsystem. Let's see what we can find. I'll reach for `git-log` to dig through the history. I'll target the KVM x86 code, so I'll focus searches in `arch/x86/kvm/` and, specifically, `arch/x86/kvm/xen.c`. I'll try a few search strategies.
First, I'll search for commits that contain "PREEMPT_RT" and "trylock". That should give us a baseline for similar issues and the suggested solution.
Then, I'll try a broader search using "sleeping function called from invalid context". That might uncover some less direct, but potentially relevant, fixes.
Finally, I'll restrict the search to the specific file, `arch/x86/kvm/xen.c`, and look for any mentions of locking or PREEMPT_RT. That will hopefully give us something very specific to that file. We'll examine the patches and see what we can learn!
|
| 87/2 |
2026/08/12 11:59 |
tool |
git-log |
0m
Args: map[MessageRegexps:[PREEMPT_RT trylock] PathPrefix:arch/x86/kvm/ Since:3 years]
Results:
|
| 88/2 |
2026/08/12 11:59 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 10076
output: 44
thoughts: 36
Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience:
**Investigating trylock() Implementation in `arch/x86/kvm/xen.c`**
Alright, no luck with "PREEMPT_RT and trylock," huh? Expected, but always worth the quick double-check. Okay, let's cut to the chase and narrow the focus. Let's directly search for just "trylock" within `arch/x86/kvm/xen.c`. This suggests we're likely dealing with the fundamental implementation or usage of the `trylock()` function within the Xen-specific KVM code for x86 architectures. This is the right level of precision to start. I'm hoping to understand how they are implementing the locking mechanism or how they are interacting with the trylock specifically in this context. I'll be looking for any clues about spinlocks, mutexes, or other synchronization primitives in relation to the `trylock()` call and what specific critical sections are being protected, and how they are working in this particular file.
|
| 89/2 |
2026/08/12 11:59 |
tool |
git-log |
0m
Args: map[MessageRegexps:[trylock] PathPrefix:arch/x86/kvm/xen.c Since:3 years]
Results:
|
| 90/2 |
2026/08/12 11:59 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 10400
output: 47
thoughts: 29
Thoughts: Okay, here's my summary, tailored for an expert audience, as if I'm thinking these things myself:
**Digging into the KVM/Xen Interaction on x86**
Alright, no immediate hits on the initial search. I need to be more precise, more targeted. Given the context, and the nature of the problem I'm likely dealing with, I think I'll try a more focused search in the `arch/x86/kvm/xen.c` file. This area is where the KVM and Xen interactions on the x86 architecture are most likely to be addressed, which is critical. Let me refine the query using the terms "atomic" and "lock". That should narrow down areas of the code that deal with concurrency control, which is the most likely source of the problem, given the lack of initial search returns. Focusing on those keywords may lead to more successful results.
|
| 91/2 |
2026/08/12 11:59 |
tool |
git-log |
0m
Args: map[MessageRegexps:[atomic lock] PathPrefix:arch/x86/kvm/xen.c Since:3 years]
Results:
|
| 92/2 |
2026/08/12 11:59 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 10682
output: 40
thoughts: 33
Thoughts: Okay, here's my interpretation of that thought process, assuming I'm deep in the weeds of kernel development and specifically working with KVM and Xen on x86:
**Diving into the History of `arch/x86/kvm/xen.c`**
Alright, I need to get a handle on the evolution of this particular file, `arch/x86/kvm/xen.c`. I'm thinking, let's just go straight to the source and review the commit history. Seeing the kinds of fixes and changes that have been applied over time will probably give me a lot of immediate context. It should highlight the areas that have been problematic, the specific issues that have cropped up in the past related to KVM and Xen interactions on x86 architectures. This will be much more informative than just guessing or reading a wiki. I'm hoping to quickly identify recurring issues, understand the types of bugs we've been seeing, and possibly anticipate what might be causing problems *now* based on patterns. A deep dive into the commit history is often the best way to get a good overall perspective. Let's see what we can find.
|
| 93/2 |
2026/08/12 11:59 |
tool |
git-log |
0m
Args: map[Count:20 PathPrefix:arch/x86/kvm/xen.c Since:3 years]
Results: map[Output:fc40b1254c32 KVM: x86: Add mode-aware versions of kvm_<reg>_{read,write}() helpers
9377016c2f9e KVM: x86/xen: Don't truncate RAX when handling hypercall from protected guest
09912b8ad22f KVM: x86/xen: Bug the VM if 32-bit KVM observes a 64-bit mode hypercall
bf4afc53b77a Convert 'alloc_obj' family to use the new default GFP_KERNEL argument
69050f8d6d07 treewide: Replace kmalloc with kmalloc_obj for non-scalar types
1a5d7f9540af KVM: x86: Add a wrapper to handle common case of IRQ delivery without dest_map
5a53249d149f KVM: x86/xen: Fix cleanup logic in emulation of Xen schedop poll hypercalls
a7f4dff21fd7 KVM: x86/xen: Allow 'out of range' event channel ports in IRQ routing table.
41cb08555c41 treewide, timers: Rename from_timer() to timer_container_of()
8fa7292fee5c treewide: Switch/rename to timer_delete[_sync]()
a2b00f85d783 KVM: x86: Update Xen TSC leaves during CPUID emulation
26e228ec1695 KVM: x86/xen: Move kvm_xen_hvm_config field into kvm_xen
5c17848134ab KVM: x86/xen: Restrict hypercall MSR to unofficial synthetic range
7764b9dd174c KVM: x86: Switch to use hrtimer_setup()
39d61b46adfd KVM: x86: Remove per-vCPU "cache" of its reference pvclock
ca28aa63918b KVM: x86/xen: Use guest's copy of pvclock when starting timer
c9e5f3fa9039 KVM: x86: Introduce kvm_set_mp_state()
f6e12766c52d KVM: x86/xen: Initialize hrtimer in kvm_xen_init_vcpu()
896046474f8d KVM: x86: Introduce kvm_x86_call() to simplify static calls of kvm_x86_ops
ebbdf37ce9ab KVM: Validate hva in kvm_gpc_activate_hva() to fix __kvm_gpc_refresh() WARN
]
|
| 94/2 |
2026/08/12 11:59 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 11672
output: 43
thoughts: 44
Thoughts: Okay, here's my summary, taking on the persona you requested:
**KVM/RT Research Path**
Alright, time to get to work. I need to dig into the KVM code, specifically regarding real-time (RT) considerations. My initial instinct tells me the most relevant areas are likely within the `arch/x86/kvm/` and `virt/kvm/` directories, given this is an x86 platform and a virtualization context. But I'll need to remember that related definitions and structures could also be in `include/linux/kvm_host.h`. So, I'll start by searching for the string "PREEMPT_RT" within those paths. That should be a decent jumping-off point to find relevant code dealing with real-time preemption within the KVM environment. Once I find those mentions, I'll be able to work through the code context and determine the relationship between RT and KVM.
|
| 95/2 |
2026/08/12 12:00 |
tool |
git-log |
0m
Args: map[MessageRegexps:[PREEMPT_RT] PathPrefix:arch/x86/kvm/ Since:3 years]
Results: map[Output:fd92bd3b4445 KVM: SVM: switch to raw spinlock for svm->ir_list_lock
]
|
| 96/2 |
2026/08/12 12:00 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 12004
output: 25
thoughts: 17
Thoughts: Okay, here's my interpretation of that thought process, tailored for an expert audience, and written in the first person:
**Analyzing Commit fd92bd3b4445**
Right, let's dive into commit `fd92bd3b4445`. My first instinct is to pull it up directly, of course. I need to understand the context and the nature of the changes made in this particular commit. Is it a hotfix? A refactor? A feature addition? The commit message should give me some initial clues, but I'm going to look beyond that to understand the scope and potential implications. I'll be looking for clues about the affected modules, dependencies, and any potential breaking changes. I'll need to rapidly assess if the changes introduce any new vulnerabilities, performance bottlenecks, or unintended side-effects within the larger system. My mental checklist will include looking for obvious code smells, adherence to established coding standards, and whether the implementation aligns with best practices for the language and framework being used. I'll be particularly interested in anything that touches the core data models or critical business logic. Deep dive time!
|
| 97/2 |
2026/08/12 12:00 |
tool |
git-show |
0m
Args:
Results: map[Output:commit fd92bd3b4445342e55f2c541c577796e0c3f1b8a
Author: Maxim Levitsky <mlevitsk@redhat.com>
Date: Thu Oct 30 15:41:30 2025 -0400
KVM: SVM: switch to raw spinlock for svm->ir_list_lock
Use a raw spinlock for vcpu_svm.ir_list_lock as the lock can be taken
during schedule() via kvm_sched_out() => __avic_vcpu_put(), and "normal"
spinlocks are sleepable locks when PREEMPT_RT=y.
This fixes the following lockdep warning:
=============================
[ BUG: Invalid wait context ]
6.12.0-146.1640_2124176644.el10.x86_64+debug #1 Not tainted
-----------------------------
qemu-kvm/38299 is trying to lock:
ff11000239725600 (&svm->ir_list_lock){....}-{3:3}, at: __avic_vcpu_put+0xfd/0x300 [kvm_amd]
other info that might help us debug this:
context-{5:5}
2 locks held by qemu-kvm/38299:
#0: ff11000239723ba8 (&vcpu->mutex){+.+.}-{4:4}, at: kvm_vcpu_ioctl+0x240/0xe00 [kvm]
#1: ff11000b906056d8 (&rq->__lock){-.-.}-{2:2}, at: raw_spin_rq_lock_nested+0x2e/0x130
stack backtrace:
CPU: 1 UID: 0 PID: 38299 Comm: qemu-kvm Kdump: loaded Not tainted 6.12.0-146.1640_2124176644.el10.x86_64+debug #1 PREEMPT(voluntary)
Hardware name: AMD Corporation QUARTZ/QUARTZ, BIOS RQZ100AB 09/14/2023
Call Trace:
<TASK>
dump_stack_lvl+0x6f/0xb0
__lock_acquire+0x921/0xb80
lock_acquire.part.0+0xbe/0x270
_raw_spin_lock_irqsave+0x46/0x90
__avic_vcpu_put+0xfd/0x300 [kvm_amd]
svm_vcpu_put+0xfa/0x130 [kvm_amd]
kvm_arch_vcpu_put+0x48c/0x790 [kvm]
kvm_sched_out+0x161/0x1c0 [kvm]
prepare_task_switch+0x36b/0xf60
__schedule+0x4f7/0x1890
schedule+0xd4/0x260
xfer_to_guest_mode_handle_work+0x54/0xc0
vcpu_run+0x69a/0xa70 [kvm]
kvm_arch_vcpu_ioctl_run+0xdc0/0x17e0 [kvm]
kvm_vcpu_ioctl+0x39f/0xe00 [kvm]
Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
Link: https://patch.msgid.link/20251030194130.307900-1-mlevitsk@redhat.com
[sean: massage changelog]
Signed-off-by: Sean Christopherson <seanjc@google.com>
diff --git a/arch/x86/kvm/svm/avic.c b/arch/x86/kvm/svm/avic.c
index 89864fee6e83..fef00546c885 100644
--- a/arch/x86/kvm/svm/avic.c
+++ b/arch/x86/kvm/svm/avic.c
@@ -788,7 +788,7 @@ int avic_init_vcpu(struct vcpu_svm *svm)
struct kvm_vcpu *vcpu = &svm->vcpu;
INIT_LIST_HEAD(&svm->ir_list);
- spin_lock_init(&svm->ir_list_lock);
+ raw_spin_lock_init(&svm->ir_list_lock);
if (!enable_apicv || !irqchip_in_kernel(vcpu->kvm))
return 0;
@@ -816,9 +816,9 @@ static void svm_ir_list_del(struct kvm_kernel_irqfd *irqfd)
if (!vcpu)
return;
- spin_lock_irqsave(&to_svm(vcpu)->ir_list_lock, flags);
+ raw_spin_lock_irqsave(&to_svm(vcpu)->ir_list_lock, flags);
list_del(&irqfd->vcpu_list);
- spin_unlock_irqrestore(&to_svm(vcpu)->ir_list_lock, flags);
+ raw_spin_unlock_irqrestore(&to_svm(vcpu)->ir_list_lock, flags);
}
int avic_pi_update_irte(struct kvm_kernel_irqfd *irqfd, struct kvm *kvm,
@@ -855,7 +855,7 @@ int avic_pi_update_irte(struct kvm_kernel_irqfd *irqfd, struct kvm *kvm,
* list of IRQs being posted to the vCPU, to ensure the IRTE
* isn't programmed with stale pCPU/IsRunning information.
*/
- guard(spinlock_irqsave)(&svm->ir_list_lock);
+ guard(raw_spinlock_irqsave)(&svm->ir_list_lock);
/*
* Update the target pCPU for IOMMU doorbells if the vCPU is
@@ -972,7 +972,7 @@ static void __avic_vcpu_load(struct kvm_vcpu *vcpu, int cpu,
* up-to-date entry information, or that this task will wait until
* svm_ir_list_add() completes to set the new target pCPU.
*/
- spin_lock_irqsave(&svm->ir_list_lock, flags);
+ raw_spin_lock_irqsave(&svm->ir_list_lock, flags);
entry = svm->avic_physical_id_entry;
WARN_ON_ONCE(entry & AVIC_PHYSICAL_ID_ENTRY_IS_RUNNING_MASK);
@@ -997,7 +997,7 @@ static void __avic_vcpu_load(struct kvm_vcpu *vcpu, int cpu,
avic_update_iommu_vcpu_affinity(vcpu, h_physical_id, action);
- spin_unlock_irqrestore(&svm->ir_list_lock, flags);
+ raw_spin_unlock_irqrestore(&svm->ir_list_lock, flags);
}
void avic_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
@@ -1035,7 +1035,7 @@ static void __avic_vcpu_put(struct kvm_vcpu *vcpu, enum avic_vcpu_action action)
* or that this task will wait until svm_ir_list_add() completes to
* mark the vCPU as not running.
*/
- spin_lock_irqsave(&svm->ir_list_lock, flags);
+ raw_spin_lock_irqsave(&svm->ir_list_lock, flags);
avic_update_iommu_vcpu_affinity(vcpu, -1, action);
@@ -1059,7 +1059,7 @@ static void __avic_vcpu_put(struct kvm_vcpu *vcpu, enum avic_vcpu_action action)
svm->avic_physical_id_entry = entry;
- spin_unlock_irqrestore(&svm->ir_list_lock, flags);
+ raw_spin_unlock_irqrestore(&svm->ir_list_lock, flags);
}
void avic_vcpu_put(struct kvm_vcpu *vcpu)
diff --git a/arch/x86/kvm/svm/svm.h b/arch/x86/kvm/svm/svm.h
index 8c36ee0d67ef..c856d8e0f95e 100644
--- a/arch/x86/kvm/svm/svm.h
+++ b/arch/x86/kvm/svm/svm.h
@@ -329,7 +329,7 @@ struct vcpu_svm {
* back into remapped mode).
*/
struct list_head ir_list;
- spinlock_t ir_list_lock;
+ raw_spinlock_t ir_list_lock;
struct vcpu_sev_es_state sev_es;
]
|
| 98/2 |
2026/08/12 12:00 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 14723
output: 43
thoughts: 193
Thoughts: **Analyzing the Locking Mechanism Change**
Okay, so I see the change in that commit. They swapped `spinlock_t` for `raw_spinlock_t` because the lock was being acquired during `schedule()`, specifically via `kvm_sched_out()`, and that's an atomic context where `spinlock_t`'s sleepability on PREEMPT_RT becomes a problem. Makes perfect sense.
But hold on, our situation is a bit different. We're dealing with `gpc->lock`, an `rwlock_t`. We can't just slap a `raw_rwlock_t` in there. The `rwlock_t` protects `gfn_to_pfn_cache`, and that cache might be accessed for extended periods or in contexts where sleeping is perfectly acceptable, even desirable.
The bug explanation is pointing towards using `read_trylock()` and handling a slow path as a fallback. That's a reasonable approach, providing a non-blocking attempt to acquire the read lock and gracefully handling contention.
Now, let's see if this pattern has been used anywhere else in the `arch/x86/kvm/` area. I'll search for `read_trylock` to get a better handle on how this method has been implemented previously. That might give me some clues as to the best approach here.
|
| 99/2 |
2026/08/12 12:00 |
tool |
git-log |
0m
Args: map[MessageRegexps:[read_trylock] PathPrefix:arch/x86/kvm/ Since:3 years]
Results:
|
| 100/2 |
2026/08/12 12:00 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 15261
output: 33
thoughts: 41
Thoughts: Okay, here's my summarized thought process, presented as if I'm the one thinking it:
**Investigating the Absence of `read_trylock` in Recent Commits**
Right, so I'm trying to track down something related to `read_trylock`, and a quick check of the commit logs didn't turn up anything useful with that specific term in the message. That's not a huge surprise, but it means I need to dig deeper. My next step is clearly to get a more granular search going. Since I'm focusing on the x86 KVM implementation, I'm going to leverage `grepper`. This should help me pinpoint where `read_trylock` is actually being *used* within that specific architecture and virtual machine environment, even if it's not explicitly mentioned in commit messages. This search should provide the context I need to proceed. Let's get to it.
|
| 101/2 |
2026/08/12 12:00 |
tool |
grepper |
0m
Args: map[Expression:read_trylock PathPrefix:arch/x86/kvm/]
Results: map[Output:arch/x86/kvm/xen.c=321=static void kvm_xen_update_runstate_guest(struct kvm_vcpu *v, bool atomic)
--
arch/x86/kvm/xen.c-424- local_irq_save(flags);
arch/x86/kvm/xen.c:425: if (!read_trylock(&gpc1->lock)) {
arch/x86/kvm/xen.c-426- local_irq_restore(flags);
--
arch/x86/kvm/xen.c-468- if (atomic) {
arch/x86/kvm/xen.c:469: if (!read_trylock(&gpc2->lock)) {
arch/x86/kvm/xen.c-470- read_unlock_irqrestore(&gpc1->lock, flags);
]
|
| 102/2 |
2026/08/12 12:00 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 15770
output: 40
thoughts: 83
Thoughts: Okay, here's my take on those thoughts, framed as my own:
**Pattern Recognition and Leveraging Existing Code**
Ah, this looks familiar! It strikes me that the `kvm_xen_update_runstate_guest` function already implements precisely the pattern I'm considering. That's excellent; no need to reinvent the wheel here. Let's see...if I remember correctly, the key to understanding this pattern is going back to its origin. Time to dig into the commit history. I'll focus on `arch/x86/kvm/xen.c`, specifically around line 425. A quick `git-blame` should do the trick, or perhaps a more focused `git-log` search to pinpoint the commit that introduced this particular approach. Understanding that original implementation will give me a solid foundation for evaluating its applicability here and potentially adapting it for this new scenario.
|
| 103/2 |
2026/08/12 12:00 |
tool |
git-blame |
0m
Args: map[End:430 File:arch/x86/kvm/xen.c Start:420]
Results: map[Output:5ec3289b31ab9 420) * Attempt to obtain the GPC lock on *both* (if there are two)
5ec3289b31ab9 421) * gfn_to_pfn caches that cover the region.
5ec3289b31ab9 422) */
bbe17c625d684 423) if (atomic) {
bbe17c625d684 424) local_irq_save(flags);
bbe17c625d684 425) if (!read_trylock(&gpc1->lock)) {
bbe17c625d684 426) local_irq_restore(flags);
bbe17c625d684 427) return;
bbe17c625d684 428) }
bbe17c625d684 429) } else {
bbe17c625d684 430) read_lock_irqsave(&gpc1->lock, flags);
]
|
| 104/2 |
2026/08/12 12:00 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 16399
output: 26
thoughts: 19
Thoughts: Okay, let's dive into commit `bbe17c625d684`.
**Diving into the Commit: Initial Assessment**
My initial thought is to quickly assess what this commit is attempting to achieve. A brief scan of the changes would be my first instinct: What files are modified? How significant are the changes – are we talking minor tweaks, or major refactoring? I'd be looking for immediate indicators of the scope: are there many lines added/deleted, or is it concentrated in a few key areas? The commit message should hopefully give me some guidance but I'm expecting to make my own educated assessment, and not just swallow the commit message. My gut reaction is that the author's intention could be anything from a bug fix, to an important performance improvement.
Next, I'll be looking for any immediately obvious coding style violations, obvious bugs, or architectural red flags. I'll likely mentally categorize the change: is this functional, a refactor, a performance optimization, or perhaps a documentation update? This initial categorization will affect how thoroughly I subsequently assess the changes. It's important to understand the overall architecture so that I can easily recognize implications of new design patterns.
|
| 105/2 |
2026/08/12 12:00 |
tool |
git-show |
0m
Args: map[Commit:bbe17c625d684]
Results: map[Output:commit bbe17c625d6843e9cdf14d81fbece1b0f0c3fb2f
Author: David Woodhouse <dwmw@amazon.co.uk>
Date: Wed Jan 11 18:06:49 2023 +0000
KVM: x86/xen: Fix potential deadlock in kvm_xen_update_runstate_guest()
The kvm_xen_update_runstate_guest() function can be called when the vCPU
is being scheduled out, from a preempt notifier. It *opportunistically*
updates the runstate area in the guest memory, if the gfn_to_pfn_cache
which caches the appropriate address is still valid.
If there is *contention* when it attempts to obtain gpc->lock, then
locking inside the priority inheritance checks may cause a deadlock.
Lockdep reports:
[13890.148997] Chain exists of:
&gpc->lock --> &p->pi_lock --> &rq->__lock
[13890.149002] Possible unsafe locking scenario:
[13890.149003] CPU0 CPU1
[13890.149004] ---- ----
[13890.149005] lock(&rq->__lock);
[13890.149007] lock(&p->pi_lock);
[13890.149009] lock(&rq->__lock);
[13890.149011] lock(&gpc->lock);
[13890.149013]
*** DEADLOCK ***
In the general case, if there's contention for a read lock on gpc->lock,
that's going to be because something else is either invalidating or
revalidating the cache. Either way, we've raced with seeing it in an
invalid state, in which case we would have aborted the opportunistic
update anyway.
So in the 'atomic' case when called from the preempt notifier, just
switch to using read_trylock() and avoid the PI handling altogether.
Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>
Message-Id: <20230111180651.14394-2-dwmw2@infradead.org>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
diff --git a/arch/x86/kvm/xen.c b/arch/x86/kvm/xen.c
index bfa9809721b5..651f9c5b873d 100644
--- a/arch/x86/kvm/xen.c
+++ b/arch/x86/kvm/xen.c
@@ -271,7 +271,15 @@ static void kvm_xen_update_runstate_guest(struct kvm_vcpu *v, bool atomic)
* Attempt to obtain the GPC lock on *both* (if there are two)
* gfn_to_pfn caches that cover the region.
*/
- read_lock_irqsave(&gpc1->lock, flags);
+ if (atomic) {
+ local_irq_save(flags);
+ if (!read_trylock(&gpc1->lock)) {
+ local_irq_restore(flags);
+ return;
+ }
+ } else {
+ read_lock_irqsave(&gpc1->lock, flags);
+ }
while (!kvm_gpc_check(gpc1, user_len1)) {
read_unlock_irqrestore(&gpc1->lock, flags);
@@ -308,7 +316,14 @@ static void kvm_xen_update_runstate_guest(struct kvm_vcpu *v, bool atomic)
* gpc1 lock to make lockdep shut up about it.
*/
lock_set_subclass(&gpc1->lock.dep_map, 1, _THIS_IP_);
- read_lock(&gpc2->lock);
+ if (atomic) {
+ if (!read_trylock(&gpc2->lock)) {
+ read_unlock_irqrestore(&gpc1->lock, flags);
+ return;
+ }
+ } else {
+ read_lock(&gpc2->lock);
+ }
if (!kvm_gpc_check(gpc2, user_len2)) {
read_unlock(&gpc2->lock);
]
|
| 106/2 |
2026/08/12 12:00 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 17909
output: 44
thoughts: 171
Thoughts: **Analyzing a Potential Deadlock in KVM/Xen Interaction**
Okay, this is hitting me as a very familiar pattern. It's almost *identical* to the deadlock we saw fixed in commit `bbe17c625d684`. That fix, "KVM: x86/xen: Fix potential deadlock in kvm_xen_update_runstate_guest()", cleverly used `read_trylock()` when called from an atomic context within a preempt notifier, and that's precisely where my alarm bells are ringing.
The bug I'm currently investigating centers around `kvm_xen_set_evtchn_fast()`. Now, where is *that* called from? Oh yes, `xen_timer_callback()`, which is a high-resolution timer callback, which means hardirq context. And, also, `kvm_arch_set_irq_inatomic()`, which is an irqfd waitqueue wakeup, and we know that's a hardirq/atomic context.
Right. Both potentially atomic contexts, both calling into a function, and both possibly leading to a lock conflict. The situation is pretty similar.
Let's see if there are any other commits in `arch/x86/kvm/xen.c` that handled the same issue. It's worth a look to see if this is a recurring class of error. Maybe they can provide some insight.
|
| 107/2 |
2026/08/12 12:00 |
tool |
git-log |
0m
Args: map[MessageRegexps:[trylock] PathPrefix:arch/x86/kvm/xen.c Since:5 years]
Results: map[Output:bbe17c625d68 KVM: x86/xen: Fix potential deadlock in kvm_xen_update_runstate_guest()
]
|
| 108/2 |
2026/08/12 12:00 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 18479
output: 41
thoughts: 37
Thoughts: Okay, here's my summarized thought process, geared towards a user who's deeply familiar with kernel internals:
**Investigating Potential Synchronization Issues**
Alright, so there's that one `trylock` issue that's been flagged, but I need to make sure this isn't a symptom of something bigger. I should really dig into the `arch/x86/kvm/` directory. That seems like the right place to start looking for related commits. I need to specifically look for other uses of `trylock` that interact with something similar to `gpc->lock` (or whatever the relevant locking construct is in that section). The goal is to see if this `trylock` usage is an isolated incident, or if it's indicative of a broader pattern of potential concurrency problems I need to be aware of.
|
| 109/2 |
2026/08/12 12:00 |
tool |
git-log |
0m
Args: map[MessageRegexps:[trylock] PathPrefix:arch/x86/kvm/ Since:5 years]
Results: map[Output:bbe17c625d68 KVM: x86/xen: Fix potential deadlock in kvm_xen_update_runstate_guest()
]
|
| 110/2 |
2026/08/12 12:00 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 18781
output: 43
thoughts: 32
Thoughts: Here's my thought process, summarized as you requested:
**Digging Deeper: KVM and PREEMPT_RT**
Okay, I've got that initial finding in `arch/x86/kvm/` regarding `PREEMPT_RT` logged, but let's not stop there. My gut tells me this could be a more widespread thing within KVM, especially given its architectural complexities and real-time considerations. I'll need to run another search within that `arch/x86/kvm/` directory specifically, but this time with a broader scope. Perhaps a deeper dive into the interactions between `PREEMPT_RT` and KVM could uncover other related vulnerabilities or design limitations. I'm thinking, are there other instances where real-time features might be unintentionally affected or where RT's stricter constraints introduce subtle issues? Let's see what else we can uncover!
|
| 111/2 |
2026/08/12 12:00 |
tool |
git-log |
0m
Args: map[MessageRegexps:[PREEMPT_RT] PathPrefix:arch/x86/kvm/ Since:5 years]
Results: map[Output:fd92bd3b4445 KVM: SVM: switch to raw spinlock for svm->ir_list_lock
]
|
| 112/2 |
2026/08/12 12:00 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 19088
output: 39
thoughts: 33
Thoughts: Okay, here's my interpretation of that thought process, assuming I'm already deep in kernel internals and specifically dealing with KVM:
**Digging into Read-Trylock in KVM**
Alright, so I'm trying to figure out what's going on with a specific synchronization primitive, `read_trylock`. I need to understand how it's being used within the KVM subsystem, and specifically where it's relevant in terms of its read-side locking implementation. Given that it can be relevant to the architecture, I should also check the x86-specific parts, too, just in case there's something platform-dependent going on. Therefore, I need to thoroughly search the KVM codebase for instances of `read_trylock`, specifically within the `virt/kvm/` directory, which houses the core virtualization logic, and also expand the search to `arch/x86/kvm/` to ensure I have full coverage of all relevant implementations. This should give me a good handle on its use cases and implementation details within this area of the kernel. This focused search is essential to understand how KVM is using `read_trylock` and any associated interactions, avoiding sifting through unrelated code.
|
| 113/2 |
2026/08/12 12:00 |
tool |
git-log |
0m
Args: map[MessageRegexps:[read_trylock] PathPrefix:virt/kvm/ Since:5 years]
Results:
|
| 114/2 |
2026/08/12 12:00 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 19431
output: 37
thoughts: 17
Thoughts: Okay, here's my summary, presented as a stream of thought:
**Diving into the KVM Codebase: trylock Search**
Alright, time to dig into this KVM stuff. I need to find where they're using `trylock`. My initial instinct is to start within the `virt/kvm/` directory – that seems like the logical place to start looking for interactions between the hypervisor and the virtual machines. It's the core of the KVM implementation, so if there's any clever synchronization going on with `trylock`, that's likely where it will be. I'll initiate a search for `trylock` within that directory, and see what the results bring. Let's get to it!
|
| 115/2 |
2026/08/12 12:00 |
tool |
git-log |
0m
Args: map[MessageRegexps:[trylock] PathPrefix:virt/kvm/ Since:5 years]
Results: map[Output:e4a454ced74c KVM: add kvm_lock_all_vcpus and kvm_trylock_all_vcpus
]
|
| 116/2 |
2026/08/12 12:01 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 19693
output: 43
thoughts: 35
Thoughts: Okay, here's my interpretation of that thought process, as if it were my own:
**Investigating Potential Synchronization Issues in KVM/Xen Interaction**
Right, I need to investigate something related to synchronization within `arch/x86/kvm/xen.c`. Given my expertise, I suspect this has to do with some subtle interaction between the KVM and Xen hypervisors, and I'm homing in on potential concurrency issues. The mention of "atomic" or "hardirq" is highly suggestive of this. Those keywords immediately flag potential problem areas: atomics often deal with concurrent access to shared resources, and hard interrupts, well, those can wreak havoc on any unsynchronized access.
Therefore, my immediate next step is crystal clear: I need to examine the commit history of that specific file, specifically looking for any commits that touch on the concepts of `atomic` operations, or code which is sensitive to `hardirq` context, within the context of the KVM/Xen interaction. This will likely give me some important clues as to the cause of the issue.
|
| 117/2 |
2026/08/12 12:01 |
tool |
git-log |
0m
Args: map[MessageRegexps:[atomic] PathPrefix:arch/x86/kvm/xen.c Since:5 years]
Results: map[Output:003d914220c9 KVM: x86/xen: allow vcpu_info content to be 'safely' copied
b9220d32799a KVM: x86/xen: allow shared_info to be mapped by fixed HVA
bbe17c625d68 KVM: x86/xen: Fix potential deadlock in kvm_xen_update_runstate_guest()
5ec3289b31ab KVM: x86/xen: Compatibility fixes for shared runstate area
8733068b9bdb KVM: x86/xen: Make kvm_xen_set_evtchn() reusable from other places
7caf9571563e KVM: x86/xen: Use gfn_to_pfn_cache for vcpu_info
fcb732d8f8cf KVM: x86/xen: Fix runstate updates to be atomic when preempting vCPU
1cfc9c4b9d46 KVM: x86/xen: Maintain valid mapping of Xen shared_info page
0985dba842ea KVM: x86/xen: Fix kvm_xen_has_interrupt() sleeping in kvm_vcpu_block()
]
|
| 118/2 |
2026/08/12 12:01 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 20317
output: 25
thoughts: 17
Thoughts: Okay, let's dive into commit `fcb732d8f8cf`. Here's what I'm thinking:
**Analyzing Commit fcb732d8f8cf**
Alright, my first step is always to understand the scope. I need to quickly assess the impact of this particular commit. What files were touched? Are we talking about a simple bug fix, a feature addition, a refactor, or something more complex like a performance optimization or architectural change? The diff will tell the story, but I need a quick high-level overview before I get lost in the details. What were the commit messages, and can I quickly assess any context for the work?
Next, I'll be looking for potential side effects. Even seemingly small changes can have ripple effects, especially in a codebase of this size. I'm immediately thinking about cross-dependencies – are there any areas that rely on the code being modified? How well is this tested? Are there existing integration or end-to-end tests that might be impacted? If the changes involve external libraries or APIs, I’m mentally checking for version compatibility and potential breaking changes.
Finally, I'm going to critically evaluate the implementation itself. Does the code adhere to established coding standards and best practices for the project? Is it clean, readable, and maintainable? Is it efficient, or are there obvious performance bottlenecks? I'll be looking for opportunities to improve the code, either through suggestions for improvement. Depending on the size of the commit, I may need to dig a little deeper, looking for potential issues I can address or questions I need to raise in order to further the quality of the project.
|
| 119/2 |
2026/08/12 12:01 |
tool |
git-show |
0m
Args:
Results: map[Output:commit fcb732d8f8cf6084f8480015ad41d25fb023a4dd
Author: David Woodhouse <dwmw@amazon.co.uk>
Date: Mon Oct 25 14:29:01 2021 +0100
KVM: x86/xen: Fix runstate updates to be atomic when preempting vCPU
There are circumstances whem kvm_xen_update_runstate_guest() should not
sleep because it ends up being called from __schedule() when the vCPU
is preempted:
[ 222.830825] kvm_xen_update_runstate_guest+0x24/0x100
[ 222.830878] kvm_arch_vcpu_put+0x14c/0x200
[ 222.830920] kvm_sched_out+0x30/0x40
[ 222.830960] __schedule+0x55c/0x9f0
To handle this, make it use the same trick as __kvm_xen_has_interrupt(),
of using the hva from the gfn_to_hva_cache directly. Then it can use
pagefault_disable() around the accesses and just bail out if the page
is absent (which is unlikely).
I almost switched to using a gfn_to_pfn_cache here and bailing out if
kvm_map_gfn() fails, like kvm_steal_time_set_preempted() does — but on
closer inspection it looks like kvm_map_gfn() will *always* fail in
atomic context for a page in IOMEM, which means it will silently fail
to make the update every single time for such guests, AFAICT. So I
didn't do it that way after all. And will probably fix that one too.
Cc: stable@vger.kernel.org
Fixes: 30b5c851af79 ("KVM: x86/xen: Add support for vCPU runstate information")
Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>
Message-Id: <b17a93e5ff4561e57b1238e3e7ccd0b613eb827e.camel@infradead.org>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
diff --git a/arch/x86/kvm/xen.c b/arch/x86/kvm/xen.c
index bad57535fad0..74be1fda58e3 100644
--- a/arch/x86/kvm/xen.c
+++ b/arch/x86/kvm/xen.c
@@ -133,32 +133,57 @@ static void kvm_xen_update_runstate(struct kvm_vcpu *v, int state)
void kvm_xen_update_runstate_guest(struct kvm_vcpu *v, int state)
{
struct kvm_vcpu_xen *vx = &v->arch.xen;
+ struct gfn_to_hva_cache *ghc = &vx->runstate_cache;
+ struct kvm_memslots *slots = kvm_memslots(v->kvm);
+ bool atomic = (state == RUNSTATE_runnable);
uint64_t state_entry_time;
- unsigned int offset;
+ int __user *user_state;
+ uint64_t __user *user_times;
kvm_xen_update_runstate(v, state);
if (!vx->runstate_set)
return;
- BUILD_BUG_ON(sizeof(struct compat_vcpu_runstate_info) != 0x2c);
+ if (unlikely(slots->generation != ghc->generation || kvm_is_error_hva(ghc->hva)) &&
+ kvm_gfn_to_hva_cache_init(v->kvm, ghc, ghc->gpa, ghc->len))
+ return;
+
+ /* We made sure it fits in a single page */
+ BUG_ON(!ghc->memslot);
+
+ if (atomic)
+ pagefault_disable();
- offset = offsetof(struct compat_vcpu_runstate_info, state_entry_time);
-#ifdef CONFIG_X86_64
/*
- * The only difference is alignment of uint64_t in 32-bit.
- * So the first field 'state' is accessed directly using
- * offsetof() (where its offset happens to be zero), while the
- * remaining fields which are all uint64_t, start at 'offset'
- * which we tweak here by adding 4.
+ * The only difference between 32-bit and 64-bit versions of the
+ * runstate struct us the alignment of uint64_t in 32-bit, which
+ * means that the 64-bit version has an additional 4 bytes of
+ * padding after the first field 'state'.
+ *
+ * So we use 'int __user *user_state' to point to the state field,
+ * and 'uint64_t __user *user_times' for runstate_entry_time. So
+ * the actual array of time[] in each state starts at user_times[1].
*/
+ BUILD_BUG_ON(offsetof(struct vcpu_runstate_info, state) != 0);
+ BUILD_BUG_ON(offsetof(struct compat_vcpu_runstate_info, state) != 0);
+ user_state = (int __user *)ghc->hva;
+
+ BUILD_BUG_ON(sizeof(struct compat_vcpu_runstate_info) != 0x2c);
+
+ user_times = (uint64_t __user *)(ghc->hva +
+ offsetof(struct compat_vcpu_runstate_info,
+ state_entry_time));
+#ifdef CONFIG_X86_64
BUILD_BUG_ON(offsetof(struct vcpu_runstate_info, state_entry_time) !=
offsetof(struct compat_vcpu_runstate_info, state_entry_time) + 4);
BUILD_BUG_ON(offsetof(struct vcpu_runstate_info, time) !=
offsetof(struct compat_vcpu_runstate_info, time) + 4);
if (v->kvm->arch.xen.long_mode)
- offset = offsetof(struct vcpu_runstate_info, state_entry_time);
+ user_times = (uint64_t __user *)(ghc->hva +
+ offsetof(struct vcpu_runstate_info,
+ state_entry_time));
#endif
/*
* First write the updated state_entry_time at the appropriate
@@ -172,10 +197,8 @@ void kvm_xen_update_runstate_guest(struct kvm_vcpu *v, int state)
BUILD_BUG_ON(sizeof_field(struct compat_vcpu_runstate_info, state_entry_time) !=
sizeof(state_entry_time));
- if (kvm_write_guest_offset_cached(v->kvm, &v->arch.xen.runstate_cache,
- &state_entry_time, offset,
- sizeof(state_entry_time)))
- return;
+ if (__put_user(state_entry_time, user_times))
+ goto out;
smp_wmb();
/*
@@ -189,11 +212,8 @@ void kvm_xen_update_runstate_guest(struct kvm_vcpu *v, int state)
BUILD_BUG_ON(sizeof_field(struct compat_vcpu_runstate_info, state) !=
sizeof(vx->current_runstate));
- if (kvm_write_guest_offset_cached(v->kvm, &v->arch.xen.runstate_cache,
- &vx->current_runstate,
- offsetof(struct vcpu_runstate_info, state),
- sizeof(vx->current_runstate)))
- return;
+ if (__put_user(vx->current_runstate, user_state))
+ goto out;
/*
* Write the actual runstate times immediately after the
@@ -208,24 +228,23 @@ void kvm_xen_update_runstate_guest(struct kvm_vcpu *v, int state)
BUILD_BUG_ON(sizeof_field(struct vcpu_runstate_info, time) !=
sizeof(vx->runstate_times));
- if (kvm_write_guest_offset_cached(v->kvm, &v->arch.xen.runstate_cache,
- &vx->runstate_times[0],
- offset + sizeof(u64),
- sizeof(vx->runstate_times)))
- return;
-
+ if (__copy_to_user(user_times + 1, vx->runstate_times, sizeof(vx->runstate_times)))
+ goto out;
smp_wmb();
/*
* Finally, clear the XEN_RUNSTATE_UPDATE bit in the guest's
* runstate_entry_time field.
*/
-
state_entry_time &= ~XEN_RUNSTATE_UPDATE;
- if (kvm_write_guest_offset_cached(v->kvm, &v->arch.xen.runstate_cache,
- &state_entry_time, offset,
- sizeof(state_entry_time)))
- return;
+ __put_user(state_entry_time, user_times);
+ smp_wmb();
+
+ out:
+ mark_page_dirty_in_slot(v->kvm, ghc->memslot, ghc->gpa >> PAGE_SHIFT);
+
+ if (atomic)
+ pagefault_enable();
}
int __kvm_xen_has_interrupt(struct kvm_vcpu *v)
@@ -443,6 +462,12 @@ int kvm_xen_vcpu_set_attr(struct kvm_vcpu *vcpu, struct kvm_xen_vcpu_attr *data)
break;
}
+ /* It must fit within a single page */
+ if ((data->u.gpa & ~PAGE_MASK) + sizeof(struct vcpu_info) > PAGE_SIZE) {
+ r = -EINVAL;
+ break;
+ }
+
r = kvm_gfn_to_hva_cache_init(vcpu->kvm,
&vcpu->arch.xen.vcpu_info_cache,
data->u.gpa,
@@ -460,6 +485,12 @@ int kvm_xen_vcpu_set_attr(struct kvm_vcpu *vcpu, struct kvm_xen_vcpu_attr *data)
break;
}
+ /* It must fit within a single page */
+ if ((data->u.gpa & ~PAGE_MASK) + sizeof(struct pvclock_vcpu_time_info) > PAGE_SIZE) {
+ r = -EINVAL;
+ break;
+ }
+
r = kvm_gfn_to_hva_cache_init(vcpu->kvm,
&vcpu->arch.xen.vcpu_time_info_cache,
data->u.gpa,
@@ -481,6 +512,12 @@ int kvm_xen_vcpu_set_attr(struct kvm_vcpu *vcpu, struct kvm_xen_vcpu_attr *data)
break;
}
+ /* It must fit within a single page */
+ if ((data->u.gpa & ~PAGE_MASK) + sizeof(struct vcpu_runstate_info) > PAGE_SIZE) {
+ r = -EINVAL;
+ break;
+ }
+
r = kvm_gfn_to_hva_cache_init(vcpu->kvm,
&vcpu->arch.xen.runstate_cache,
data->u.gpa,
]
|
| 120/2 |
2026/08/12 12:01 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 23915
output: 47
thoughts: 338
Thoughts: **Analyzing Atomic Runstate Updates in KVM/Xen Preemption**
Okay, so this commit, `fcb732d8f8cf`, is addressing how we handle runstate updates in an atomic way when a vCPU is preempted. It's moving away from `kvm_write_guest_offset_cached()` – which, annoyingly, can sleep – and towards using `pagefault_disable()` and `__put_user()` instead. The presence of the term "atomic context" immediately flags a potential concurrency issue.
Let me refresh my memory on the underlying problem. Right, the bug report specifies that `kvm_xen_set_evtchn_fast()` needs to avoid `read_lock_irqsave()` when called from an atomic context. Instead, it should be using `read_trylock()`. If that fails, it should gracefully fall back to the slower path – either returning `-EWOULDBLOCK` or kicking the vCPU – which, importantly, the callers are already designed to handle.
Aha! The bug description explicitly references `kvm_xen_update_runstate_guest()` as an existing example of this pattern! And look at that, commit `bbe17c625d684` ("KVM: x86/xen: Fix potential deadlock in kvm_xen_update_runstate_guest()") looks like *precisely* the prior art we're looking for here. Perfect, a very relevant reference.
Now I need to take a look at `kvm_xen_set_evtchn_fast()` itself, in `arch/x86/kvm/xen.c`. Let's see how it's currently implemented and how we can apply the `read_trylock()` pattern here to fix the issue.
|
| 121/2 |
2026/08/12 12:01 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:arch/x86/kvm/xen.c Name:kvm_xen_set_evtchn_fast]
Results: map[SourceCode:1785: /*
1786: * The return value from this function is propagated to kvm_set_irq() API,
1787: * so it returns:
1788: * < 0 Interrupt was ignored (masked or not delivered for other reasons)
1789: * = 0 Interrupt was coalesced (previous irq is still pending)
1790: * > 0 Number of CPUs interrupt was delivered to
1791: *
1792: * It is also called directly from kvm_arch_set_irq_inatomic(), where the
1793: * only check on its return value is a comparison with -EWOULDBLOCK'.
1794: */
1795: int kvm_xen_set_evtchn_fast(struct kvm_xen_evtchn *xe, struct kvm *kvm)
1796: {
1797: struct gfn_to_pfn_cache *gpc = &kvm->arch.xen.shinfo_cache;
1798: struct kvm_vcpu *vcpu;
1799: unsigned long *pending_bits, *mask_bits;
1800: unsigned long flags;
1801: int port_word_bit;
1802: bool kick_vcpu = false;
1803: int vcpu_idx, idx, rc;
1804:
1805: vcpu_idx = READ_ONCE(xe->vcpu_idx);
1806: if (vcpu_idx >= 0)
1807: vcpu = kvm_get_vcpu(kvm, vcpu_idx);
1808: else {
1809: vcpu = kvm_get_vcpu_by_id(kvm, xe->vcpu_id);
1810: if (!vcpu)
1811: return -EINVAL;
1812: WRITE_ONCE(xe->vcpu_idx, vcpu->vcpu_idx);
1813: }
1814:
1815: if (xe->port >= max_evtchn_port(kvm))
1816: return -EINVAL;
1817:
1818: rc = -EWOULDBLOCK;
1819:
1820: idx = srcu_read_lock(&kvm->srcu);
1821:
1822: read_lock_irqsave(&gpc->lock, flags);
1823: if (!kvm_gpc_check(gpc, PAGE_SIZE))
1824: goto out_rcu;
1825:
1826: if (IS_ENABLED(CONFIG_64BIT) && kvm->arch.xen.long_mode) {
1827: struct shared_info *shinfo = gpc->khva;
1828: pending_bits = (unsigned long *)&shinfo->evtchn_pending;
1829: mask_bits = (unsigned long *)&shinfo->evtchn_mask;
1830: port_word_bit = xe->port / 64;
1831: } else {
1832: struct compat_shared_info *shinfo = gpc->khva;
1833: pending_bits = (unsigned long *)&shinfo->evtchn_pending;
1834: mask_bits = (unsigned long *)&shinfo->evtchn_mask;
1835: port_word_bit = xe->port / 32;
1836: }
1837:
1838: /*
1839: * If this port wasn't already set, and if it isn't masked, then
1840: * we try to set the corresponding bit in the in-kernel shadow of
1841: * evtchn_pending_sel for the target vCPU. And if *that* wasn't
1842: * already set, then we kick the vCPU in question to write to the
1843: * *real* evtchn_pending_sel in its own guest vcpu_info struct.
1844: */
1845: if (test_and_set_bit(xe->port, pending_bits)) {
1846: rc = 0; /* It was already raised */
1847: } else if (test_bit(xe->port, mask_bits)) {
1848: rc = -ENOTCONN; /* Masked */
1849: kvm_xen_check_poller(vcpu, xe->port);
1850: } else {
1851: rc = 1; /* Delivered to the bitmap in shared_info. */
1852: /* Now switch to the vCPU's vcpu_info to set the index and pending_sel */
1853: read_unlock_irqrestore(&gpc->lock, flags);
1854: gpc = &vcpu->arch.xen.vcpu_info_cache;
1855:
1856: read_lock_irqsave(&gpc->lock, flags);
1857: if (!kvm_gpc_check(gpc, sizeof(struct vcpu_info))) {
1858: /*
1859: * Could not access the vcpu_info. Set the bit in-kernel
1860: * and prod the vCPU to deliver it for itself.
1861: */
1862: if (!test_and_set_bit(port_word_bit, &vcpu->arch.xen.evtchn_pending_sel))
1863: kick_vcpu = true;
1864: goto out_rcu;
1865: }
1866:
1867: if (IS_ENABLED(CONFIG_64BIT) && kvm->arch.xen.long_mode) {
1868: struct vcpu_info *vcpu_info = gpc->khva;
1869: if (!test_and_set_bit(port_word_bit, &vcpu_info->evtchn_pending_sel)) {
1870: WRITE_ONCE(vcpu_info->evtchn_upcall_pending, 1);
1871: kick_vcpu = true;
1872: }
1873: } else {
1874: struct compat_vcpu_info *vcpu_info = gpc->khva;
1875: if (!test_and_set_bit(port_word_bit,
1876: (unsigned long *)&vcpu_info->evtchn_pending_sel)) {
1877: WRITE_ONCE(vcpu_info->evtchn_upcall_pending, 1);
1878: kick_vcpu = true;
1879: }
1880: }
1881:
1882: /* For the per-vCPU lapic vector, deliver it as MSI. */
1883: if (kick_vcpu && vcpu->arch.xen.upcall_vector) {
1884: kvm_xen_inject_vcpu_vector(vcpu);
1885: kick_vcpu = false;
1886: }
1887: }
1888:
1889: out_rcu:
1890: read_unlock_irqrestore(&gpc->lock, flags);
1891: srcu_read_unlock(&kvm->srcu, idx);
1892:
1893: if (kick_vcpu) {
1894: kvm_make_request(KVM_REQ_UNBLOCK, vcpu);
1895: kvm_vcpu_kick(vcpu);
1896: }
1897:
1898: return rc;
1899: }
SourceFile:arch/x86/kvm/xen.c]
|
| 122/2 |
2026/08/12 12:01 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 26770
output: 47
thoughts: 76
Thoughts: Okay, let's break this down.
**Analyzing a Potential Bug Fix**
So, the proposed fix for this bug involves modifying `kvm_xen_set_evtchn_fast`. The idea is to add a `bool in_atomic` parameter and, crucially, to use `read_trylock` if `in_atomic` is set to true. My first instinct is to immediately dive into the context surrounding this change. I need to understand *why* this parameter is being introduced and what the implications are. This definitely sounds like it's addressing some sort of potential race condition or lock contention issue in a critical section. To get a better grasp, I should revisit `kvm_xen_update_runstate_guest`. I need to refresh my memory on how that function utilizes `read_trylock`, which presumably involves an attempt to lock a resource without blocking. Understanding its application there will help illuminate the rationale behind applying it conditionally in `kvm_xen_set_evtchn_fast` using the `in_atomic` flag. Knowing the surrounding architecture is critical here. It seems like the issue is tied to either an atomic context or a non-blocking context, and `read_trylock` seems to be the crucial piece of the puzzle here.
|
| 123/2 |
2026/08/12 12:01 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:arch/x86/kvm/xen.c Name:kvm_xen_update_runstate_guest]
Results: map[SourceCode: 321: static void kvm_xen_update_runstate_guest(struct kvm_vcpu *v, bool atomic)
322: {
323: struct kvm_vcpu_xen *vx = &v->arch.xen;
324: struct gfn_to_pfn_cache *gpc1 = &vx->runstate_cache;
325: struct gfn_to_pfn_cache *gpc2 = &vx->runstate2_cache;
326: size_t user_len, user_len1, user_len2;
327: struct vcpu_runstate_info rs;
328: unsigned long flags;
329: size_t times_ofs;
330: uint8_t *update_bit = NULL;
331: uint64_t entry_time;
332: uint64_t *rs_times;
333: int *rs_state;
334:
335: /*
336: * The only difference between 32-bit and 64-bit versions of the
337: * runstate struct is the alignment of uint64_t in 32-bit, which
338: * means that the 64-bit version has an additional 4 bytes of
339: * padding after the first field 'state'. Let's be really really
340: * paranoid about that, and matching it with our internal data
341: * structures that we memcpy into it...
342: */
343: BUILD_BUG_ON(offsetof(struct vcpu_runstate_info, state) != 0);
344: BUILD_BUG_ON(offsetof(struct compat_vcpu_runstate_info, state) != 0);
345: BUILD_BUG_ON(sizeof(struct compat_vcpu_runstate_info) != 0x2c);
346: #ifdef CONFIG_X86_64
347: /*
348: * The 64-bit structure has 4 bytes of padding before 'state_entry_time'
349: * so each subsequent field is shifted by 4, and it's 4 bytes longer.
350: */
351: BUILD_BUG_ON(offsetof(struct vcpu_runstate_info, state_entry_time) !=
352: offsetof(struct compat_vcpu_runstate_info, state_entry_time) + 4);
353: BUILD_BUG_ON(offsetof(struct vcpu_runstate_info, time) !=
354: offsetof(struct compat_vcpu_runstate_info, time) + 4);
355: BUILD_BUG_ON(sizeof(struct vcpu_runstate_info) != 0x2c + 4);
356: #endif
357: /*
358: * The state field is in the same place at the start of both structs,
359: * and is the same size (int) as vx->current_runstate.
360: */
361: BUILD_BUG_ON(offsetof(struct vcpu_runstate_info, state) !=
362: offsetof(struct compat_vcpu_runstate_info, state));
363: BUILD_BUG_ON(sizeof_field(struct vcpu_runstate_info, state) !=
364: sizeof(vx->current_runstate));
365: BUILD_BUG_ON(sizeof_field(struct compat_vcpu_runstate_info, state) !=
366: sizeof(vx->current_runstate));
367:
368: /*
369: * The state_entry_time field is 64 bits in both versions, and the
370: * XEN_RUNSTATE_UPDATE flag is in the top bit, which given that x86
371: * is little-endian means that it's in the last *byte* of the word.
372: * That detail is important later.
373: */
374: BUILD_BUG_ON(sizeof_field(struct vcpu_runstate_info, state_entry_time) !=
375: sizeof(uint64_t));
376: BUILD_BUG_ON(sizeof_field(struct compat_vcpu_runstate_info, state_entry_time) !=
377: sizeof(uint64_t));
378: BUILD_BUG_ON((XEN_RUNSTATE_UPDATE >> 56) != 0x80);
379:
380: /*
381: * The time array is four 64-bit quantities in both versions, matching
382: * the vx->runstate_times and immediately following state_entry_time.
383: */
384: BUILD_BUG_ON(offsetof(struct vcpu_runstate_info, state_entry_time) !=
385: offsetof(struct vcpu_runstate_info, time) - sizeof(uint64_t));
386: BUILD_BUG_ON(offsetof(struct compat_vcpu_runstate_info, state_entry_time) !=
387: offsetof(struct compat_vcpu_runstate_info, time) - sizeof(uint64_t));
388: BUILD_BUG_ON(sizeof_field(struct vcpu_runstate_info, time) !=
389: sizeof_field(struct compat_vcpu_runstate_info, time));
390: BUILD_BUG_ON(sizeof_field(struct vcpu_runstate_info, time) !=
391: sizeof(vx->runstate_times));
392:
393: if (IS_ENABLED(CONFIG_64BIT) && v->kvm->arch.xen.long_mode) {
394: user_len = sizeof(struct vcpu_runstate_info);
395: times_ofs = offsetof(struct vcpu_runstate_info,
396: state_entry_time);
397: } else {
398: user_len = sizeof(struct compat_vcpu_runstate_info);
399: times_ofs = offsetof(struct compat_vcpu_runstate_info,
400: state_entry_time);
401: }
402:
403: /*
404: * There are basically no alignment constraints. The guest can set it
405: * up so it crosses from one page to the next, and at arbitrary byte
406: * alignment (and the 32-bit ABI doesn't align the 64-bit integers
407: * anyway, even if the overall struct had been 64-bit aligned).
408: */
409: if ((gpc1->gpa & ~PAGE_MASK) + user_len >= PAGE_SIZE) {
410: user_len1 = PAGE_SIZE - (gpc1->gpa & ~PAGE_MASK);
411: user_len2 = user_len - user_len1;
412: } else {
413: user_len1 = user_len;
414: user_len2 = 0;
415: }
416: BUG_ON(user_len1 + user_len2 != user_len);
417:
418: retry:
419: /*
420: * Attempt to obtain the GPC lock on *both* (if there are two)
421: * gfn_to_pfn caches that cover the region.
422: */
423: if (atomic) {
424: local_irq_save(flags);
425: if (!read_trylock(&gpc1->lock)) {
426: local_irq_restore(flags);
427: return;
428: }
429: } else {
430: read_lock_irqsave(&gpc1->lock, flags);
431: }
432: while (!kvm_gpc_check(gpc1, user_len1)) {
433: read_unlock_irqrestore(&gpc1->lock, flags);
434:
435: /* When invoked from kvm_sched_out() we cannot sleep */
436: if (atomic)
437: return;
438:
439: if (kvm_gpc_refresh(gpc1, user_len1))
440: return;
441:
442: read_lock_irqsave(&gpc1->lock, flags);
443: }
444:
445: if (likely(!user_len2)) {
446: /*
447: * Set up three pointers directly to the runstate_info
448: * struct in the guest (via the GPC).
449: *
450: * • @rs_state → state field
451: * • @rs_times → state_entry_time field.
452: * • @update_bit → last byte of state_entry_time, which
453: * contains the XEN_RUNSTATE_UPDATE bit.
454: */
455: rs_state = gpc1->khva;
456: rs_times = gpc1->khva + times_ofs;
457: if (v->kvm->arch.xen.runstate_update_flag)
458: update_bit = ((void *)(&rs_times[1])) - 1;
459: } else {
460: /*
461: * The guest's runstate_info is split across two pages and we
462: * need to hold and validate both GPCs simultaneously. We can
463: * declare a lock ordering GPC1 > GPC2 because nothing else
464: * takes them more than one at a time. Set a subclass on the
465: * gpc1 lock to make lockdep shut up about it.
466: */
467: lock_set_subclass(&gpc1->lock.dep_map, 1, _THIS_IP_);
468: if (atomic) {
469: if (!read_trylock(&gpc2->lock)) {
470: read_unlock_irqrestore(&gpc1->lock, flags);
471: return;
472: }
473: } else {
474: read_lock(&gpc2->lock);
475: }
476:
477: if (!kvm_gpc_check(gpc2, user_len2)) {
478: read_unlock(&gpc2->lock);
479: read_unlock_irqrestore(&gpc1->lock, flags);
480:
481: /* When invoked from kvm_sched_out() we cannot sleep */
482: if (atomic)
483: return;
484:
485: /*
486: * Use kvm_gpc_activate() here because if the runstate
487: * area was configured in 32-bit mode and only extends
488: * to the second page now because the guest changed to
489: * 64-bit mode, the second GPC won't have been set up.
490: */
491: if (kvm_gpc_activate(gpc2, gpc1->gpa + user_len1,
492: user_len2))
493: return;
494:
495: /*
496: * We dropped the lock on GPC1 so we have to go all the
497: * way back and revalidate that too.
498: */
499: goto retry;
500: }
501:
502: /*
503: * In this case, the runstate_info struct will be assembled on
504: * the kernel stack (compat or not as appropriate) and will
505: * be copied to GPC1/GPC2 with a dual memcpy. Set up the three
506: * rs pointers accordingly.
507: */
508: rs_times = &rs.state_entry_time;
509:
510: /*
511: * The rs_state pointer points to the start of what we'll
512: * copy to the guest, which in the case of a compat guest
513: * is the 32-bit field that the compiler thinks is padding.
514: */
515: rs_state = ((void *)rs_times) - times_ofs;
516:
517: /*
518: * The update_bit is still directly in the guest memory,
519: * via one GPC or the other.
520: */
521: if (v->kvm->arch.xen.runstate_update_flag) {
522: if (user_len1 >= times_ofs + sizeof(uint64_t))
523: update_bit = gpc1->khva + times_ofs +
524: sizeof(uint64_t) - 1;
525: else
526: update_bit = gpc2->khva + times_ofs +
527: sizeof(uint64_t) - 1 - user_len1;
528: }
529:
530: #ifdef CONFIG_X86_64
531: /*
532: * Don't leak kernel memory through the padding in the 64-bit
533: * version of the struct.
534: */
535: memset(&rs, 0, offsetof(struct vcpu_runstate_info, state_entry_time));
536: #endif
537: }
538:
539: /*
540: * First, set the XEN_RUNSTATE_UPDATE bit in the top bit of the
541: * state_entry_time field, directly in the guest. We need to set
542: * that (and write-barrier) before writing to the rest of the
543: * structure, and clear it last. Just as Xen does, we address the
544: * single *byte* in which it resides because it might be in a
545: * different cache line to the rest of the 64-bit word, due to
546: * the (lack of) alignment constraints.
547: */
548: entry_time = vx->runstate_entry_time;
549: if (update_bit) {
550: entry_time |= XEN_RUNSTATE_UPDATE;
551: *update_bit = (vx->runstate_entry_time | XEN_RUNSTATE_UPDATE) >> 56;
552: smp_wmb();
553: }
554:
555: /*
556: * Now assemble the actual structure, either on our kernel stack
557: * or directly in the guest according to how the rs_state and
558: * rs_times pointers were set up above.
559: */
560: *rs_state = vx->current_runstate;
561: rs_times[0] = entry_time;
562: memcpy(rs_times + 1, vx->runstate_times, sizeof(vx->runstate_times));
563:
564: /* For the split case, we have to then copy it to the guest. */
565: if (user_len2) {
566: memcpy(gpc1->khva, rs_state, user_len1);
567: memcpy(gpc2->khva, ((void *)rs_state) + user_len1, user_len2);
568: }
569: smp_wmb();
570:
571: /* Finally, clear the XEN_RUNSTATE_UPDATE bit. */
572: if (update_bit) {
573: entry_time &= ~XEN_RUNSTATE_UPDATE;
574: *update_bit = entry_time >> 56;
575: smp_wmb();
576: }
577:
578: if (user_len2) {
579: kvm_gpc_mark_dirty_in_slot(gpc2);
580: read_unlock(&gpc2->lock);
581: }
582:
583: kvm_gpc_mark_dirty_in_slot(gpc1);
584: read_unlock_irqrestore(&gpc1->lock, flags);
585: }
SourceFile:arch/x86/kvm/xen.c]
|
| 124/2 |
2026/08/12 12:01 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 31824
output: 41
thoughts: 631
Thoughts: **Analyzing the Atomic Lock Usage in KVM/Xen Code**
Okay, so I'm looking at this `kvm_xen_update_runstate_guest` function, and it's pretty clear. It takes an `atomic` boolean argument, and depending on its value, it uses either `read_trylock()` or `read_lock_irqsave()` on `gpc1->lock` and `gpc2->lock`. That conditional locking strategy is precisely the pattern the bug report outlined for `kvm_xen_set_evtchn_fast()`.
The bug stems from `kvm_xen_set_evtchn_fast()` using a sleeping lock (`read_lock_irqsave()`) when called from atomic context – either within `xen_timer_callback()` (a hardirq context triggered by the hrtimer) or `kvm_arch_set_irq_inatomic()` (triggered by an irqfd waitqueue wakeup). On PREEMPT_RT, that's a recipe for disaster. The fix, as the report indicates, is to use `read_trylock()` when in atomic context and gracefully handle the potential failure, either by returning an error code (-EWOULDBLOCK) or by kicking the vCPU instead of sleeping.
The critical piece of prior art is commit `bbe17c625d684`. That commit essentially did the same thing with `kvm_xen_update_runstate_guest()`: switched to `read_trylock()` in an atomic context (specifically, a preempt notifier) to avoid a potential deadlock. The commit message explicitly states that it's to avoid problems with priority inheritance (PI) handling, which can cause deadlocks if there's contention for the lock. The commit essentially avoids the PI handling altogether by simply using `read_trylock()` and bailing out if it fails in the atomic context.
So, while our context is PREEMPT_RT and sleeping in atomic context is the risk, and `bbe17c625d684` mentions PI related deadlocks, the core concept is identical: *when operating in an atomic context, avoid sleeping (or blocking) on a lock*. If the lock is unavailable immediately, fall back to a non-blocking alternative. That’s the pattern. Now, I need to check the broader KVM codebase for similar fixes in other files. There might be other relevant examples.
|
| 125/2 |
2026/08/12 12:01 |
tool |
git-log |
0m
Args: map[MessageRegexps:[trylock] PathPrefix:arch/x86/kvm/ Since:5 years]
Results: map[Output:bbe17c625d68 KVM: x86/xen: Fix potential deadlock in kvm_xen_update_runstate_guest()
]
|
| 126/2 |
2026/08/12 12:01 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 33063
output: 37
thoughts: 10
Thoughts: Okay, here's my interpretation of that thought process, tailored for an expert audience, and presented as if it were my own internal monologue:
**Investigating the Hypervisor Landscape**
Right, so we're talking about virtualization, and immediately my mind jumps to the core of the action: `virt/kvm/`. That directory – or the concept behind it – represents the very heart of Kernel-based Virtual Machine. When I see that, my brain immediately starts mapping out the implications. It's not just a file path; it's a doorway. It's *the* place to start digging deeper if we're trying to understand the nuances of the Linux virtualization ecosystem.
My mind begins to tick through the questions I would need to ask to start building a comprehensive analysis. Are we talking about performance optimizations within KVM? Potential security vulnerabilities? Specific hardware support? Is there something new I'm not aware of?
|
| 127/2 |
2026/08/12 12:01 |
tool |
git-log |
0m
Args: map[MessageRegexps:[trylock] PathPrefix:virt/kvm/ Since:5 years]
Results: map[Output:e4a454ced74c KVM: add kvm_lock_all_vcpus and kvm_trylock_all_vcpus
]
|
| 128/2 |
2026/08/12 12:01 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 33355
output: 43
thoughts: 27
Thoughts: Okay, here's my take on those thoughts, framed as an expert's internal monologue:
**Investigating KVM, `PREEMPT_RT`, and Hard Interrupts**
Right, so I'm thinking about KVM, specifically within the `arch/x86/kvm/` directory. I'm wondering about the interaction between KVM and `PREEMPT_RT`. My initial question is how `PREEMPT_RT` impacts KVM's behavior, particularly with regard to hard interrupt handling (`hardirq`). We're dealing with virtual machines here, so preemptibility is always a concern for latency-sensitive workloads. I need to understand if the real-time patches have been adequately integrated, and if any specific modifications were necessary within the KVM code to properly manage the timing of interrupt delivery and virtual CPU scheduling. My main concern is around the scheduling of the vCPUs within the context of real-time priorities and the impact of the virtualization layer on interrupt latency. Does KVM's interrupt delivery path introduce any significant jitter, even with `PREEMPT_RT` enabled? I need to dig deeper into the code to trace the interrupt path and see how it interacts with the real-time scheduler. I need to know the specific techniques that have been employed to mitigate any potential latency issues.
|
| 129/2 |
2026/08/12 12:02 |
tool |
git-log |
0m
Args: map[MessageRegexps:[PREEMPT_RT] PathPrefix:arch/x86/kvm/ Since:5 years]
Results: map[Output:fd92bd3b4445 KVM: SVM: switch to raw spinlock for svm->ir_list_lock
]
|
| 130/2 |
2026/08/12 12:02 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 33740
output: 40
thoughts: 713
Thoughts: **Analyzing and Addressing Potential Concurrency Issues in KVM on PREEMPT_RT**
Okay, so I've been digging into this, and here's what I've got so far. First, I revisited `fd92bd3b4445`. Remember, that commit addressed a similar issue by swapping a `spinlock_t` for a `raw_spinlock_t` because the lock was acquired within `schedule()`, which is atomic. This is a common strategy to avoid "sleeping in atomic context" problems, particularly with PREEMPT_RT. But, that won't work universally.
Now, for the specific problem at hand, it looks like the `gpc->lock`, which is an `rwlock_t` protecting the `gfn_to_pfn_cache`, is the culprit. This is where things get interesting. I can't just swap that lock for a raw equivalent. It's used widely in KVM, and might be held for a while, or in contexts where sleeping is permissible. Furthermore, there might not even be a direct `raw_rwlock_t` equivalent. It's defined as a simple `rwlock_t` in `include/linux/kvm_types.h`. If I tried to swap it, it would have implications across the entire KVM codebase.
So, the optimal fix here seems to be using `read_trylock()`, specifically when operating in atomic contexts. If that fails, it should then fall back to a slower path.
Let me summarize the findings I have.
1. The root cause is attempting to acquire a sleeping lock, in this case, `rwlock_t`, which maps to `rt_rw_lock` on PREEMPT_RT, from a hardirq context, like `xen_timer_callback` or `kvm_arch_set_irq_inatomic`.
2. The proposed solution is to use `read_trylock()` when operating in atomic context and handle the failure gracefully with a fallback mechanism.
3. This isn't a new pattern. I actually saw a similar method previously in `arch/x86/kvm/xen.c` with commit `bbe17c625d684` ("KVM: x86/xen: Fix potential deadlock in kvm_xen_update_runstate_guest()"). This commit introduced `read_trylock()` for `gpc->lock` when called from an atomic context, specifically a preempt notifier, to avoid a deadlock linked to priority inheritance checks.
4. Another example is the `fd92bd3b4445` commit ("KVM: SVM: switch to raw spinlock for svm->ir_list_lock") which solved a similar "Invalid wait context" bug by switching a `spinlock_t` to a `raw_spinlock_t` because it was taken in an atomic context (`kvm_sched_out`). But that's not workable here. I can't touch that `gpc->lock` rwlock. That makes using the `read_trylock()` fallback the established correct approach for this case.
I better double check that pattern in `kvm_xen_update_runstate_guest` to be sure it's consistent. Yup, there it is in the source code.
Now, I'll check if there are any other instances in `arch/x86/kvm/xen.c` where `gpc->lock` might be taken in an atomic context.
|
| 131/2 |
2026/08/12 12:02 |
tool |
grepper |
0m
Args: map[Expression:read_lock.*gpc->lock PathPrefix:arch/x86/kvm/xen.c]
Results: map[Output:arch/x86/kvm/xen.c=38=static int kvm_xen_shared_info_init(struct kvm *kvm)
--
arch/x86/kvm/xen.c-47-
arch/x86/kvm/xen.c:48: read_lock_irq(&gpc->lock);
arch/x86/kvm/xen.c-49- while (!kvm_gpc_check(gpc, PAGE_SIZE)) {
--
arch/x86/kvm/xen.c-55-
arch/x86/kvm/xen.c:56: read_lock_irq(&gpc->lock);
arch/x86/kvm/xen.c-57- }
--
arch/x86/kvm/xen.c=153=static int xen_get_guest_pvclock(struct kvm_vcpu *vcpu,
--
arch/x86/kvm/xen.c-160-
arch/x86/kvm/xen.c:161: read_lock_irqsave(&gpc->lock, flags);
arch/x86/kvm/xen.c-162- while (!kvm_gpc_check(gpc, offset + sizeof(*hv_clock))) {
--
arch/x86/kvm/xen.c-168-
arch/x86/kvm/xen.c:169: read_lock_irqsave(&gpc->lock, flags);
arch/x86/kvm/xen.c-170- }
--
arch/x86/kvm/xen.c=639=void kvm_xen_inject_pending_events(struct kvm_vcpu *v)
--
arch/x86/kvm/xen.c-652- */
arch/x86/kvm/xen.c:653: read_lock_irqsave(&gpc->lock, flags);
arch/x86/kvm/xen.c-654- while (!kvm_gpc_check(gpc, sizeof(struct vcpu_info))) {
--
arch/x86/kvm/xen.c-659-
arch/x86/kvm/xen.c:660: read_lock_irqsave(&gpc->lock, flags);
arch/x86/kvm/xen.c-661- }
--
arch/x86/kvm/xen.c=697=int __kvm_xen_has_interrupt(struct kvm_vcpu *v)
--
arch/x86/kvm/xen.c-715-
arch/x86/kvm/xen.c:716: read_lock_irqsave(&gpc->lock, flags);
arch/x86/kvm/xen.c-717- while (!kvm_gpc_check(gpc, sizeof(struct vcpu_info))) {
--
arch/x86/kvm/xen.c-737- }
arch/x86/kvm/xen.c:738: read_lock_irqsave(&gpc->lock, flags);
arch/x86/kvm/xen.c-739- }
--
arch/x86/kvm/xen.c=1433=static bool wait_pending_event(struct kvm_vcpu *vcpu, int nr_ports,
--
arch/x86/kvm/xen.c-1443- idx = srcu_read_lock(&kvm->srcu);
arch/x86/kvm/xen.c:1444: read_lock_irqsave(&gpc->lock, flags);
arch/x86/kvm/xen.c-1445- if (!kvm_gpc_check(gpc, PAGE_SIZE))
--
arch/x86/kvm/xen.c=1795=int kvm_xen_set_evtchn_fast(struct kvm_xen_evtchn *xe, struct kvm *kvm)
--
arch/x86/kvm/xen.c-1821-
arch/x86/kvm/xen.c:1822: read_lock_irqsave(&gpc->lock, flags);
arch/x86/kvm/xen.c-1823- if (!kvm_gpc_check(gpc, PAGE_SIZE))
--
arch/x86/kvm/xen.c-1855-
arch/x86/kvm/xen.c:1856: read_lock_irqsave(&gpc->lock, flags);
arch/x86/kvm/xen.c-1857- if (!kvm_gpc_check(gpc, sizeof(struct vcpu_info))) {
]
|
| 132/2 |
2026/08/12 12:02 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 36478
output: 45
thoughts: 258
Thoughts: **Investigating Potential Lock Issues in Xen KVM Interrupt Handling**
Okay, so we're focusing on that `kvm_xen_set_evtchn_fast` function, that's the one we're fixing. But hold on, what about `__kvm_xen_has_interrupt`? I'm seeing it uses `read_lock_irqsave(&gpc->lock, flags)`. Now, the big question is, is this function ever called from an atomic context? We can't have locks in atomic context; that's a recipe for disaster.
Right, so `__kvm_xen_has_interrupt` is called from `kvm_xen_has_interrupt` and `kvm_xen_has_pending_events`, I'll make a mental note. Let's dig into `__kvm_xen_has_interrupt` and see if it has the same lock-related problems as the one we are fixing.
Hmm, wait a second, there's a comment right here in the code for `__kvm_xen_has_interrupt`. Let me see... aha! It says:
```c
/*
* This is called from the kvm_vcpu_block() path, and also from
* kvm_arch_vcpu_runnable() which is called from the preempt
* notifier. So we can't sleep.
*/
```
Okay, so it *can't* sleep, and it's called from `kvm_vcpu_block` and the preempt notifier. That seems pretty important. But, if it can't sleep, why in the world is it using `read_lock_irqsave`? That doesn't make any sense at all. It's time to check the source. Let's dive into the `arch/x86/kvm/xen.c` file and examine `__kvm_xen_has_interrupt` to understand what's really going on there.
|
| 133/2 |
2026/08/12 12:02 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:arch/x86/kvm/xen.c Name:__kvm_xen_has_interrupt]
Results: map[SourceCode: 697: int __kvm_xen_has_interrupt(struct kvm_vcpu *v)
698: {
699: struct gfn_to_pfn_cache *gpc = &v->arch.xen.vcpu_info_cache;
700: unsigned long flags;
701: u8 rc = 0;
702:
703: /*
704: * If the global upcall vector (HVMIRQ_callback_vector) is set and
705: * the vCPU's evtchn_upcall_pending flag is set, the IRQ is pending.
706: */
707:
708: /* No need for compat handling here */
709: BUILD_BUG_ON(offsetof(struct vcpu_info, evtchn_upcall_pending) !=
710: offsetof(struct compat_vcpu_info, evtchn_upcall_pending));
711: BUILD_BUG_ON(sizeof(rc) !=
712: sizeof_field(struct vcpu_info, evtchn_upcall_pending));
713: BUILD_BUG_ON(sizeof(rc) !=
714: sizeof_field(struct compat_vcpu_info, evtchn_upcall_pending));
715:
716: read_lock_irqsave(&gpc->lock, flags);
717: while (!kvm_gpc_check(gpc, sizeof(struct vcpu_info))) {
718: read_unlock_irqrestore(&gpc->lock, flags);
719:
720: /*
721: * This function gets called from kvm_vcpu_block() after setting the
722: * task to TASK_INTERRUPTIBLE, to see if it needs to wake immediately
723: * from a HLT. So we really mustn't sleep. If the page ended up absent
724: * at that point, just return 1 in order to trigger an immediate wake,
725: * and we'll end up getting called again from a context where we *can*
726: * fault in the page and wait for it.
727: */
728: if (in_atomic() || !task_is_running(current))
729: return 1;
730:
731: if (kvm_gpc_refresh(gpc, sizeof(struct vcpu_info))) {
732: /*
733: * If this failed, userspace has screwed up the
734: * vcpu_info mapping. No interrupts for you.
735: */
736: return 0;
737: }
738: read_lock_irqsave(&gpc->lock, flags);
739: }
740:
741: rc = ((struct vcpu_info *)gpc->khva)->evtchn_upcall_pending;
742: read_unlock_irqrestore(&gpc->lock, flags);
743: return rc;
744: }
SourceFile:arch/x86/kvm/xen.c]
|
| 134/2 |
2026/08/12 12:02 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 38088
output: 48
thoughts: 398
Thoughts: **Analyzing Potential Concurrency Issues in `__kvm_xen_has_interrupt`**
Okay, so I'm looking at `__kvm_xen_has_interrupt` and immediately see it uses `read_lock_irqsave(&gpc->lock, flags)`. My initial thought is to scrutinize its usage in conjunction with potential sleeping. If this is called from `kvm_vcpu_block()` after `TASK_INTERRUPTIBLE` is set, we're *not* in hardirq context, but we are in a state that means we shouldn't be sleeping. Wait, I think I'm off-track...
Now, I'm trying to recall the exact call chain. If `kvm_arch_vcpu_runnable()` calls this from the preempt notifier... no, hang on, that seems like a misdirection, `fcb732d8f8cf` was actually related to `kvm_xen_update_runstate_guest`.
Here's the problem I see; If `__kvm_xen_has_interrupt` *is* called from atomic context, that `read_lock_irqsave` *will* sleep on PREEMPT_RT! So, should we use `read_trylock` instead?
Okay, let's zoom in. It looks like it checks `in_atomic() || !task_is_running(current)` *after* it drops the lock if `kvm_gpc_check` fails. But that initial `read_lock_irqsave` can still sleep if the lock is contended.
Hmm, the `in_atomic()` check is there specifically to avoid `kvm_gpc_refresh`, which does sleep.
But `read_lock_irqsave` *itself* can sleep on PREEMPT_RT. Is this a known PREEMPT_RT bug? I need to check the history and see if there are any commits out there that fix `__kvm_xen_has_interrupt` for PREEMPT_RT. I need to make sure I'm not reinventing the wheel.
|
| 135/2 |
2026/08/12 12:02 |
tool |
git-log |
0m
Args: map[MessageRegexps:[__kvm_xen_has_interrupt] PathPrefix:arch/x86/kvm/ Since:5 years]
Results: map[Output:7caf9571563e KVM: x86/xen: Use gfn_to_pfn_cache for vcpu_info
fcb732d8f8cf KVM: x86/xen: Fix runstate updates to be atomic when preempting vCPU
14243b387137 KVM: x86/xen: Add KVM_IRQ_ROUTING_XEN_EVTCHN and event channel delivery
f3d1436d4bf8 KVM: x86: Take srcu lock in post_kvm_run_save()
0985dba842ea KVM: x86/xen: Fix kvm_xen_has_interrupt() sleeping in kvm_vcpu_block()
]
|
| 136/2 |
2026/08/12 12:02 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 39184
output: 24
thoughts: 51
Thoughts: Here's my analysis of commit `0985dba842ea`, "KVM: x86/xen: Fix kvm_xen_has_interrupt() sleeping in kvm_vcpu_block()":
**Analyzing the KVM/Xen Interrupt Handling Fix**
Okay, so this commit, `0985dba842ea`, catches my eye. It's dealing with KVM and Xen integration, specifically focusing on a potential sleep within `kvm_xen_has_interrupt()`, which is called from `kvm_vcpu_block()`. Immediately, the phrase "sleeping in `kvm_vcpu_block()`" raises a red flag; that function's often a critical path for CPU management. You *never* want to see a potentially blocking operation there.
My mental model is now spinning up: I'm picturing the interrupt delivery path, probably involving some kind of Xen hypercall or shared memory interaction. The problem is likely that `kvm_xen_has_interrupt()` is somehow attempting to acquire a lock, wait for a condition variable, or perform some other operation that might block. This is bad because `kvm_vcpu_block()` is responsible for putting the vCPU to sleep or entering a wait loop, so any blocking operation in that context means it is holding up that operation. And that could lead to deadlocks, performance bottlenecks, or even more subtle timing issues. I'd need to look at the exact code to be certain.
The fact that it is a *fix* implies someone already identified this issue, so I'm interested in the impact. Were there any observed hangs, performance degradation, or errors? Did this manifest in a specific workload or configuration? What was the proposed fix? Is it a careful use of spinlocks, atomics, or maybe even a complete restructuring of the interrupt delivery path? Or maybe a change in the locking order? The description also mentions it is related to Xen, which I'd need to consider from the perspective of how the Xen PV driver handles interrupts on the host.
I'll dive into the commit itself now. I'm looking for the specific code changes and the rationale behind them. The code diff is where the true understanding lies. Let's see what the fix entails and if the approach is sound. My gut feeling is this is likely something subtle related to synchronization, but without seeing the changes, I'm just guessing.
|
| 137/2 |
2026/08/12 12:02 |
tool |
git-show |
0m
Args:
Results: map[Output:commit 0985dba842eaa391858972cfe2724c3c174a2827
Author: David Woodhouse <dwmw@amazon.co.uk>
Date: Sat Oct 23 20:47:19 2021 +0100
KVM: x86/xen: Fix kvm_xen_has_interrupt() sleeping in kvm_vcpu_block()
In kvm_vcpu_block, the current task is set to TASK_INTERRUPTIBLE before
making a final check whether the vCPU should be woken from HLT by any
incoming interrupt.
This is a problem for the get_user() in __kvm_xen_has_interrupt(), which
really shouldn't be sleeping when the task state has already been set.
I think it's actually harmless as it would just manifest itself as a
spurious wakeup, but it's causing a debug warning:
[ 230.963649] do not call blocking ops when !TASK_RUNNING; state=1 set at [<00000000b6bcdbc9>] prepare_to_swait_exclusive+0x30/0x80
Fix the warning by turning it into an *explicit* spurious wakeup. When
invoked with !task_is_running(current) (and we might as well add
in_atomic() there while we're at it), just return 1 to indicate that
an IRQ is pending, which will cause a wakeup and then something will
call it again in a context that *can* sleep so it can fault the page
back in.
Cc: stable@vger.kernel.org
Fixes: 40da8ccd724f ("KVM: x86/xen: Add event channel interrupt vector upcall")
Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>
Message-Id: <168bf8c689561da904e48e2ff5ae4713eaef9e2d.camel@infradead.org>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
diff --git a/arch/x86/kvm/xen.c b/arch/x86/kvm/xen.c
index 9ea9c3dabe37..8f62baebd028 100644
--- a/arch/x86/kvm/xen.c
+++ b/arch/x86/kvm/xen.c
@@ -190,6 +190,7 @@ void kvm_xen_update_runstate_guest(struct kvm_vcpu *v, int state)
int __kvm_xen_has_interrupt(struct kvm_vcpu *v)
{
+ int err;
u8 rc = 0;
/*
@@ -216,13 +217,29 @@ int __kvm_xen_has_interrupt(struct kvm_vcpu *v)
if (likely(slots->generation == ghc->generation &&
!kvm_is_error_hva(ghc->hva) && ghc->memslot)) {
/* Fast path */
- __get_user(rc, (u8 __user *)ghc->hva + offset);
- } else {
- /* Slow path */
- kvm_read_guest_offset_cached(v->kvm, ghc, &rc, offset,
- sizeof(rc));
+ pagefault_disable();
+ err = __get_user(rc, (u8 __user *)ghc->hva + offset);
+ pagefault_enable();
+ if (!err)
+ return rc;
}
+ /* Slow path */
+
+ /*
+ * This function gets called from kvm_vcpu_block() after setting the
+ * task to TASK_INTERRUPTIBLE, to see if it needs to wake immediately
+ * from a HLT. So we really mustn't sleep. If the page ended up absent
+ * at that point, just return 1 in order to trigger an immediate wake,
+ * and we'll end up getting called again from a context where we *can*
+ * fault in the page and wait for it.
+ */
+ if (in_atomic() || !task_is_running(current))
+ return 1;
+
+ kvm_read_guest_offset_cached(v->kvm, ghc, &rc, offset,
+ sizeof(rc));
+
return rc;
}
]
|
| 138/2 |
2026/08/12 12:02 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 40949
output: 847
thoughts: 2224
Thoughts: **Summary of Findings for Fixing the KVM Xen PREEMPT_RT Bug**
Okay, so looking into this, it seems this bug arises because we're potentially taking a sleeping lock (`rwlock_t`, which translates to `rt_rw_lock` on PREEMPT_RT) from an atomic context, specifically within the `kvm_xen_set_evtchn_fast()` path. This could happen, for instance, from an hrtimer callback or an irqfd waitqueue wakeup.
The key is that the KVM Xen code *already* has a well-defined and established idiom for dealing with this precise scenario, and it hinges around the `gpc->lock`, which protects the `gfn_to_pfn_cache`. The correct pattern is to use `read_trylock()` within these atomic contexts, and if the trylock fails, to gracefully fall back to a slower, non-blocking path.
This idiom was, in fact, implemented previously. We can see it in commit `bbe17c625d684` ("KVM: x86/xen: Fix potential deadlock in kvm_xen_update_runstate_guest()"). This commit modified the `kvm_xen_update_runstate_guest()` function to accept a `bool atomic` parameter. When called from an atomic context (like a preempt notifier), it correctly uses `read_trylock(&gpc->lock)` instead of the potentially blocking `read_lock_irqsave()`. If the `read_trylock` fails, the code just aborts the opportunistic update. The fix does this in order to ensure the code does not sleep within an atomic context on PREEMPT_RT.
So, the pattern is:
* **The Preferred Idiom**: Use `read_trylock()` with `gpc->lock` and then fall back to an alternate path if we cannot acquire it.
* **Example**: `bbe17c625d684` which fixed `kvm_xen_update_runstate_guest()`
* **Reasoning**: Prevents blocking in atomic context on PREEMPT_RT.
Now, there have been *other* PREEMPT_RT fixes in KVM, and they often involve converting a lock to a raw spinlock. For example, `fd92bd3b4445` ("KVM: SVM: switch to raw spinlock for svm->ir_list_lock") which addressed a lockdep issue. However, we *cannot* apply that approach here, because `gpc->lock` is an `rwlock_t` which is an important part of the generic `gfn_to_pfn_cache` infrastructure. Changing it to a raw lock would have wide-ranging consequences and is not appropriate for this targeted bug.
So, the recommended approach is the `read_trylock()` method, which aligns perfectly with what's proposed for `kvm_xen_set_evtchn_fast()`. Specifically:
* Add a `bool in_atomic` (or similar name) parameter to `kvm_xen_set_evtchn_fast()` and pass it through as necessary.
* Conditionally use `read_trylock(&gpc->lock)` when this parameter is true.
* If `read_trylock()` fails, gracefully fall back to returning `-EWOULDBLOCK` (or the equivalent) and kick the vCPU, just as happens with `kvm_gpc_check()` failures.
* Ensure that no blocking operations are called when the atomic flag is true.
This will avoid sleeping in an atomic context and mirrors the correct and idiomatic way of handling `gpc->lock` in similar situations within the KVM Xen code. It adheres to the existing KVM Xen pattern.
|
| 139/1 |
2026/08/12 12:03 |
action |
kernel-scratch-checkouter |
0m
Results: map[KernelScratchSrc:/app/workdir/cache/tmp/tmp3075591599]
|
| 140/1 |
2026/08/12 12:03 |
loop |
|
32m
|
| 141/2 |
2026/08/12 12:03 |
iteration |
0 |
32m
|
| 142/3 |
2026/08/12 12:03 |
agent |
patch-generator |
3m
Model:
Instruction:
You are an experienced Linux kernel developer tasked with creating a fix for a kernel bug.
You will be given a crash report, an initial explanation of the root cause done by another
kernel expert, and a summary of how similar bugs were fixed in the past.
Use the codeeditor tool to do code edits.
Note: you will not see your changes when looking at the code using codesearch tools.
Use the patch-diff tool to review the modifications you applied.
Your final reply should contain explanation of what you did in the patch and why
(details not present in the initial explanation of the bug).
Your fix must not just prevent the given crash, but also be the best fix for the underlying
root cause from the software engineering point of view. There can be several ways to fix the
same bug. Consider alternatives, and pick the best one. For example, additional checks may be
added at different locations/functions, it's usually better to place them earlier in the
execution to avoid multiple checks at various locations later.
Frequently the same coding mistake is done in several locations in the source code.
Check if your fix should be extended/applied to similar cases around to fix other similar bugs.
But don't go too wide, don't try to fix problems kernel-wide, fix similar issues
in the same file only.
If you are changing post-conditions of a function, consider all callers of the functions,
and if they need to be updated to handle new post-conditions. For example, if you make
a function that previously never returned a NULL, return NULL, consider if callers
need to be updated to handle NULL return value.
Prefer calling several tools at the same time to save round-trips.
Prompt:
The crash that corresponds to the bug is:
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
preempt_count: 10001, expected: 0
RCU nest depth: 0, expected: 0
1 lock held by kworker/0:5/5685:
#0: ffff8881964ddb48 (&kvm->srcu){.?.+}-{0:0}, at: srcu_lock_acquire include/linux/srcu.h:187 [inline]
#0: ffff8881964ddb48 (&kvm->srcu){.?.+}-{0:0}, at: srcu_read_lock include/linux/srcu.h:294 [inline]
#0: ffff8881964ddb48 (&kvm->srcu){.?.+}-{0:0}, at: kvm_xen_set_evtchn_fast+0x1be/0x990 arch/x86/kvm/xen.c:1820
irq event stamp: 78678
hardirqs last enabled at (78677): [<ffffffff8194da12>] raw_spin_rq_unlock_irq kernel/sched/sched.h:1673 [inline]
hardirqs last enabled at (78677): [<ffffffff8194da12>] finish_lock_switch kernel/sched/core.c:5241 [inline]
hardirqs last enabled at (78677): [<ffffffff8194da12>] finish_task_switch+0x412/0xc60 kernel/sched/core.c:5359
hardirqs last disabled at (78678): [<ffffffff8b4edc4e>] sysvec_apic_timer_interrupt+0xe/0xc0 arch/x86/kernel/apic/apic.c:1062
softirqs last enabled at (78668): [<ffffffff8187f8b0>] __local_bh_enable_ip+0x1a0/0x2b0 kernel/softirq.c:305
softirqs last disabled at (78666): [<ffffffff861be59f>] local_bh_disable include/linux/bottom_half.h:20 [inline]
softirqs last disabled at (78666): [<ffffffff861be59f>] rcu_read_lock_bh include/linux/rcupdate.h:893 [inline]
softirqs last disabled at (78666): [<ffffffff861be59f>] keep_key_fresh drivers/net/wireguard/send.c:129 [inline]
softirqs last disabled at (78666): [<ffffffff861be59f>] wg_packet_create_data_done drivers/net/wireguard/send.c:259 [inline]
softirqs last disabled at (78666): [<ffffffff861be59f>] wg_packet_tx_worker+0x25f/0x7e0 drivers/net/wireguard/send.c:276
Preemption disabled at:
[<ffffffff8b50149d>] __schedule_loop kernel/sched/core.c:7310 [inline]
[<ffffffff8b50149d>] schedule+0x15d/0x2b0 kernel/sched/core.c:7326
CPU: 0 UID: 0 PID: 5685 Comm: kworker/0:5 Not tainted syzkaller #1 PREEMPT_{RT,(full)}
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
Workqueue: 0x0 (wg-crypt-wg1)
Call Trace:
<IRQ>
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
instr_sysvec_apic_timer_interrupt arch/x86/kernel/apic/apic.c:1062 [inline]
sysvec_apic_timer_interrupt+0xa1/0xc0 arch/x86/kernel/apic/apic.c:1062
</IRQ>
<TASK>
asm_sysvec_apic_timer_interrupt+0x1a/0x20 arch/x86/include/asm/idtentry.h:674
RIP: 0010:finish_task_switch+0x417/0xc60 kernel/sched/core.c:5361
Code: 04 00 00 41 c7 84 24 20 0e 00 00 00 00 00 00 0f 1f 44 00 00 49 83 c4 48 4c 89 e7 e8 b3 99 bd 09 e8 de c7 38 00 fb 4c 8b 65 c8 <49> 8d bc 24 e0 16 00 00 48 89 f8 48 c1 e8 03 42 0f b6 04 30 84 c0
RSP: 0018:ffffc9000442fa60 EFLAGS: 00000206
RAX: 0000000000013355 RBX: ffff88813883c6e0 RCX: 0000000080000001
RDX: 0000000000000000 RSI: ffffffff8daa4fe1 RDI: ffffffff8bd1a580
RBP: ffffc9000442fab0 R08: ffffffff8fd9f0f7 R09: 1ffffffff1fb3e1e
R10: dffffc0000000000 R11: fffffbfff1fb3e1f R12: ffff888114048000
R13: ffff88813883c6a8 R14: dffffc0000000000 R15: 1ffff110271078dc
context_switch kernel/sched/core.c:5513 [inline]
__schedule+0x1711/0x5530 kernel/sched/core.c:7234
__schedule_loop kernel/sched/core.c:7311 [inline]
schedule+0x164/0x2b0 kernel/sched/core.c:7326
worker_thread+0xc12/0xfb0 kernel/workqueue.c:3501
kthread+0x388/0x470 kernel/kthread.c:436
ret_from_fork+0x514/0xb70 arch/x86/kernel/process.c:158
ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245
</TASK>
=============================
[ BUG: Invalid wait context ]
syzkaller #1 Tainted: G W
-----------------------------
kworker/0:5/5685 is trying to lock:
ffff8881964dd530 (&gpc->lock){....}-{3:3}, at: kvm_xen_set_evtchn_fast+0x1f4/0x990 arch/x86/kvm/xen.c:1822
other info that might help us debug this:
context-{2:2}
1 lock held by kworker/0:5/5685:
#0: ffff8881964ddb48 (&kvm->srcu){.?.+}-{0:0}, at: srcu_lock_acquire include/linux/srcu.h:187 [inline]
#0: ffff8881964ddb48 (&kvm->srcu){.?.+}-{0:0}, at: srcu_read_lock include/linux/srcu.h:294 [inline]
#0: ffff8881964ddb48 (&kvm->srcu){.?.+}-{0:0}, at: kvm_xen_set_evtchn_fast+0x1be/0x990 arch/x86/kvm/xen.c:1820
stack backtrace:
CPU: 0 UID: 0 PID: 5685 Comm: kworker/0:5 Tainted: G W syzkaller #1 PREEMPT_{RT,(full)}
Tainted: [W]=WARN
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
Workqueue: 0x0 (wg-crypt-wg1)
Call Trace:
<IRQ>
dump_stack_lvl+0xe8/0x150 lib/dump_stack.c:120
print_lock_invalid_wait_context kernel/locking/lockdep.c:4830 [inline]
check_wait_context kernel/locking/lockdep.c:4902 [inline]
__lock_acquire+0xe39/0x2ce0 kernel/locking/lockdep.c:5187
lock_acquire+0x106/0x350 kernel/locking/lockdep.c:5868
rt_read_lock+0xcc/0x4b0 kernel/locking/spinlock_rt.c:249
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
instr_sysvec_apic_timer_interrupt arch/x86/kernel/apic/apic.c:1062 [inline]
sysvec_apic_timer_interrupt+0xa1/0xc0 arch/x86/kernel/apic/apic.c:1062
</IRQ>
<TASK>
asm_sysvec_apic_timer_interrupt+0x1a/0x20 arch/x86/include/asm/idtentry.h:674
RIP: 0010:finish_task_switch+0x417/0xc60 kernel/sched/core.c:5361
Code: 04 00 00 41 c7 84 24 20 0e 00 00 00 00 00 00 0f 1f 44 00 00 49 83 c4 48 4c 89 e7 e8 b3 99 bd 09 e8 de c7 38 00 fb 4c 8b 65 c8 <49> 8d bc 24 e0 16 00 00 48 89 f8 48 c1 e8 03 42 0f b6 04 30 84 c0
RSP: 0018:ffffc9000442fa60 EFLAGS: 00000206
RAX: 0000000000013355 RBX: ffff88813883c6e0 RCX: 0000000080000001
RDX: 0000000000000000 RSI: ffffffff8daa4fe1 RDI: ffffffff8bd1a580
RBP: ffffc9000442fab0 R08: ffffffff8fd9f0f7 R09: 1ffffffff1fb3e1e
R10: dffffc0000000000 R11: fffffbfff1fb3e1f R12: ffff888114048000
R13: ffff88813883c6a8 R14: dffffc0000000000 R15: 1ffff110271078dc
context_switch kernel/sched/core.c:5513 [inline]
__schedule+0x1711/0x5530 kernel/sched/core.c:7234
__schedule_loop kernel/sched/core.c:7311 [inline]
schedule+0x164/0x2b0 kernel/sched/core.c:7326
worker_thread+0xc12/0xfb0 kernel/workqueue.c:3501
kthread+0x388/0x470 kernel/kthread.c:436
ret_from_fork+0x514/0xb70 arch/x86/kernel/process.c:158
ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245
</TASK>
----------------
Code disassembly (best guess), 1 bytes skipped:
0: 00 00 add %al,(%rax)
2: 41 c7 84 24 20 0e 00 movl $0x0,0xe20(%r12)
9: 00 00 00 00 00
e: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)
13: 49 83 c4 48 add $0x48,%r12
17: 4c 89 e7 mov %r12,%rdi
1a: e8 b3 99 bd 09 call 0x9bd99d2
1f: e8 de c7 38 00 call 0x38c802
24: fb sti
25: 4c 8b 65 c8 mov -0x38(%rbp),%r12
* 29: 49 8d bc 24 e0 16 00 lea 0x16e0(%r12),%rdi <-- trapping instruction
30: 00
31: 48 89 f8 mov %rdi,%rax
34: 48 c1 e8 03 shr $0x3,%rax
38: 42 0f b6 04 30 movzbl (%rax,%r14,1),%eax
3d: 84 c0 test %al,%al
The explanation of the root cause of the bug is:
The root cause of this crash is an attempt to acquire a sleeping lock from a hardirq context on a `PREEMPT_RT` kernel.
### Detailed Analysis
1. **The Lock:**
The crash happens when `kvm_xen_set_evtchn_fast()` attempts to acquire `gpc->lock` via `read_lock_irqsave(&gpc->lock, flags)`. The `gpc->lock` is an `rwlock_t` used to protect the `gfn_to_pfn_cache`.
In a `PREEMPT_RT` kernel, `rwlock_t` is mapped to `rt_rw_lock`, which is a sleeping lock (rtmutex-based). It cannot be acquired in hardirq context because it may sleep while waiting for the lock or while waking up waiters during unlock.
2. **The Context:**
`kvm_xen_set_evtchn_fast()` is called from `xen_timer_callback()`, which is an hrtimer callback initialized with `HRTIMER_MODE_ABS_HARD`. This means the timer callback executes in hardirq context, even on `PREEMPT_RT`.
Additionally, `kvm_xen_set_evtchn_fast()` is also called from `kvm_arch_set_irq_inatomic()`, which is invoked during `irqfd` waitqueue wakeups (also a hardirq/atomic context).
3. **The Lockdep Splat:**
The lockdep splat mentions `srcu_read_lock` because `kvm->srcu` was acquired just before the `read_lock_irqsave`. While `srcu_read_lock` itself is perfectly safe in hardirq context (it just increments a per-CPU counter), lockdep prints it as a "held lock" when the actual sleeping function (`rt_read_lock`) is called.
### How to Fix
To fix this, `kvm_xen_set_evtchn_fast()` must avoid using `read_lock_irqsave()` when called from an atomic context. Instead, it should use `read_trylock()`. If the trylock fails, it can safely fall back to the slow path (returning `-EWOULDBLOCK` or kicking the vCPU), which is exactly what the callers expect and handle.
This pattern of using `read_trylock()` for `gpc->lock` in atomic contexts is already established in KVM Xen (e.g., in `kvm_xen_update_runstate_guest()`).
You should modify `kvm_xen_set_evtchn_fast()` to accept a `bool in_atomic` parameter:
```c
// arch/x86/kvm/xen.h
int kvm_xen_set_evtchn_fast(struct kvm_xen_evtchn *xe, struct kvm *kvm, bool in_atomic);
```
Then update `kvm_xen_set_evtchn_fast()` in `arch/x86/kvm/xen.c` to use `read_trylock()` when `in_atomic` is true:
```c
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);
}
```
You will need to do this for **both** `gpc->lock` acquisitions in that function. For the second acquisition (the `vcpu_info_cache`), if the trylock fails, you should fall back to setting the bit in-kernel and kicking the vCPU, just like it does when `kvm_gpc_check()` fails:
```c
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);
}
```
Make sure to add the `out_rcu_no_lock` label to avoid unlocking a lock you don't hold:
```c
out_rcu:
read_unlock_irqrestore(&gpc->lock, flags);
out_rcu_no_lock:
srcu_read_unlock(&kvm->srcu, idx);
```
Finally, update the callers to pass the correct context:
- `xen_timer_callback()` in `arch/x86/kvm/xen.c`: pass `true`.
- `kvm_arch_set_irq_inatomic()` in `arch/x86/kvm/irq.c`: pass `true`.
- `kvm_xen_set_evtchn()` in `arch/x86/kvm/xen.c` (both calls): pass `false` (since it runs in process context, e.g., from a hypercall or the irqfd workqueue).
Historical context on how similar bugs were fixed in the past:
Based on the historical context and prior fixes in the KVM subsystem, the proposed fix perfectly aligns with established idioms for handling locking in atomic contexts, specifically regarding the `gfn_to_pfn_cache` (`gpc`).
Here is a summary of the findings, locking rules, and common patterns you should follow when writing the fix:
### 1. Established Idiom: `read_trylock()` with Slow-Path Fallback
The primary pattern for safely accessing `gpc->lock` (an `rwlock_t` which becomes a sleepable `rt_rw_lock` on PREEMPT_RT) from an atomic or hardirq context is to conditionally use `read_trylock()` and fall back to a slow path if the lock is contended.
**Relevant Prior Art:**
This exact pattern was established in `arch/x86/kvm/xen.c` by **commit `bbe17c625d684` ("KVM: x86/xen: Fix potential deadlock in kvm_xen_update_runstate_guest()")**.
* In that commit, `kvm_xen_update_runstate_guest()` was modified to accept a `bool atomic` parameter because it could be called from a preempt notifier (an atomic context).
* When `atomic` is true, the code uses `read_trylock(&gpc->lock)` instead of `read_lock_irqsave()`.
* If the trylock fails, it simply aborts the opportunistic update and returns.
This perfectly mirrors your proposed fix for `kvm_xen_set_evtchn_fast()`: plumbing a `bool in_atomic` parameter, using `read_trylock()`, and falling back to the slow path (returning `-EWOULDBLOCK` or kicking the vCPU) if the lock cannot be immediately acquired.
### 2. Why Not Convert to a Raw Lock?
Another common pattern in the kernel for fixing PREEMPT_RT "sleeping in atomic context" bugs is to convert the offending lock to a raw lock.
**Relevant Prior Art:**
**Commit `fd92bd3b4445` ("KVM: SVM: switch to raw spinlock for svm->ir_list_lock")** fixed a very similar "Invalid wait context" lockdep splat by converting a `spinlock_t` to a `raw_spinlock_t` because it was acquired during `schedule()`.
However, following this pattern is **not appropriate** for this bug. The `gpc->lock` is an `rwlock_t` that belongs to the generic `gfn_to_pfn_cache` infrastructure used extensively across KVM. Converting it to a `raw_rwlock_t` would globally affect KVM and is unfeasible because the lock protects cache invalidations that may legitimately need to sleep or be held for longer durations. Thus, the localized `read_trylock()` approach is the correct idiom.
### Summary of Rules to Follow for the Fix:
When writing your patch, you should strictly follow the pattern set by `kvm_xen_update_runstate_guest()`:
1. **Context Parameter:** Plumb a `bool in_atomic` (or `bool atomic`) parameter through the call stack to KVM Xen event channel delivery functions.
2. **Conditional Locking:** Use `if (in_atomic) { ... read_trylock(&gpc->lock) ... } else { read_lock_irqsave(&gpc->lock, flags); }`.
3. **Graceful Fallback:** If the trylock fails in atomic context, do not attempt to spin or wait. Immediately fall back to the slow path (e.g., returning `-EWOULDBLOCK` for the first lock, or setting the bit in-kernel and kicking the vCPU for the second lock), exactly as the code currently does when `kvm_gpc_check()` fails.
Reply: I have implemented the fix for the "sleeping function called from invalid context" bug in `kvm_xen_set_evtchn_fast()`.
### Explanation of the Fix
The crash occurs because `kvm_xen_set_evtchn_fast()` attempts to acquire `gpc->lock` using `read_lock_irqsave()` while executing in a hardirq context (e.g., from `xen_timer_callback()` or `kvm_arch_set_irq_inatomic()`). On a `PREEMPT_RT` kernel, `rwlock_t` is mapped to `rt_rw_lock`, which is a sleeping lock and cannot be acquired in atomic or hardirq contexts.
To resolve this, I modified `kvm_xen_set_evtchn_fast()` to accept a `bool in_atomic` parameter. This allows the function to adapt its locking strategy based on the context from which it is called:
1. **Atomic Context (`in_atomic = true`)**: The function uses `read_trylock(&gpc->lock)`. If the lock is contended and the trylock fails, it immediately falls back to the slow path. For the first lock (shared info), it returns `-EWOULDBLOCK`. For the second lock (vcpu info), it sets the pending bit in-kernel, kicks the vCPU, and returns `1`. This avoids any sleeping operations in atomic context.
2. **Process Context (`in_atomic = false`)**: The function continues to use `read_lock_irqsave(&gpc->lock, flags)`, which is safe and allows it to wait for the lock if necessary.
This approach perfectly aligns with the established idiom in the KVM Xen subsystem (e.g., `kvm_xen_update_runstate_guest()`), which also uses `read_trylock()` with a slow-path fallback to safely access `gpc->lock` from atomic contexts.
I also updated all callers of `kvm_xen_set_evtchn_fast()` to pass the correct context flag:
- `kvm_arch_set_irq_inatomic()`: Passes `true` (atomic context).
- `xen_timer_callback()`: Passes `true` (hrtimer callback, hardirq context).
- `kvm_xen_set_evtchn()`: Passes `false` (process context, e.g., hypercall or irqfd workqueue).
|
| 143/4 |
2026/08/12 12:03 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 10645
output: 47
thoughts: 157
Thoughts: **Fixing a KVM/Xen Event Channel Bug**
Alright, let's tackle this. I've got a bug report here that's immediately concerning – we're potentially taking a sleeping lock, `gpc->lock`, which is an `rt_rw_lock` under PREEMPT_RT, in hardirq context within `kvm_xen_set_evtchn_fast()`. That's a classic recipe for a lockup, especially on a real-time system. We need to address this immediately.
The proposed solution seems sensible. The idea is to introduce a `bool in_atomic` parameter to `kvm_xen_set_evtchn_fast()`. When this flag is set, indicating we're within an atomic context, we'll try to acquire the read lock using `read_trylock()`. This avoids the potential sleep. If the `trylock` fails – meaning the lock is already held – we'll gracefully fall back to the slower, safe path. That's the right approach: prioritize correctness and avoid deadlocks even if it means a slight performance penalty in rare cases.
Now, let's dive into the code. Time to fire up the editor and take a look at `arch/x86/kvm/xen.c` to see how this function is implemented and where we need to make these changes. I'll need to carefully analyze the surrounding code to ensure this modification doesn't introduce any regressions or unexpected behavior. This looks like a solid starting point though.
|
| 144/4 |
2026/08/12 12:03 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:arch/x86/kvm/xen.c Name:kvm_xen_set_evtchn_fast]
Results: map[SourceCode:1785: /*
1786: * The return value from this function is propagated to kvm_set_irq() API,
1787: * so it returns:
1788: * < 0 Interrupt was ignored (masked or not delivered for other reasons)
1789: * = 0 Interrupt was coalesced (previous irq is still pending)
1790: * > 0 Number of CPUs interrupt was delivered to
1791: *
1792: * It is also called directly from kvm_arch_set_irq_inatomic(), where the
1793: * only check on its return value is a comparison with -EWOULDBLOCK'.
1794: */
1795: int kvm_xen_set_evtchn_fast(struct kvm_xen_evtchn *xe, struct kvm *kvm)
1796: {
1797: struct gfn_to_pfn_cache *gpc = &kvm->arch.xen.shinfo_cache;
1798: struct kvm_vcpu *vcpu;
1799: unsigned long *pending_bits, *mask_bits;
1800: unsigned long flags;
1801: int port_word_bit;
1802: bool kick_vcpu = false;
1803: int vcpu_idx, idx, rc;
1804:
1805: vcpu_idx = READ_ONCE(xe->vcpu_idx);
1806: if (vcpu_idx >= 0)
1807: vcpu = kvm_get_vcpu(kvm, vcpu_idx);
1808: else {
1809: vcpu = kvm_get_vcpu_by_id(kvm, xe->vcpu_id);
1810: if (!vcpu)
1811: return -EINVAL;
1812: WRITE_ONCE(xe->vcpu_idx, vcpu->vcpu_idx);
1813: }
1814:
1815: if (xe->port >= max_evtchn_port(kvm))
1816: return -EINVAL;
1817:
1818: rc = -EWOULDBLOCK;
1819:
1820: idx = srcu_read_lock(&kvm->srcu);
1821:
1822: read_lock_irqsave(&gpc->lock, flags);
1823: if (!kvm_gpc_check(gpc, PAGE_SIZE))
1824: goto out_rcu;
1825:
1826: if (IS_ENABLED(CONFIG_64BIT) && kvm->arch.xen.long_mode) {
1827: struct shared_info *shinfo = gpc->khva;
1828: pending_bits = (unsigned long *)&shinfo->evtchn_pending;
1829: mask_bits = (unsigned long *)&shinfo->evtchn_mask;
1830: port_word_bit = xe->port / 64;
1831: } else {
1832: struct compat_shared_info *shinfo = gpc->khva;
1833: pending_bits = (unsigned long *)&shinfo->evtchn_pending;
1834: mask_bits = (unsigned long *)&shinfo->evtchn_mask;
1835: port_word_bit = xe->port / 32;
1836: }
1837:
1838: /*
1839: * If this port wasn't already set, and if it isn't masked, then
1840: * we try to set the corresponding bit in the in-kernel shadow of
1841: * evtchn_pending_sel for the target vCPU. And if *that* wasn't
1842: * already set, then we kick the vCPU in question to write to the
1843: * *real* evtchn_pending_sel in its own guest vcpu_info struct.
1844: */
1845: if (test_and_set_bit(xe->port, pending_bits)) {
1846: rc = 0; /* It was already raised */
1847: } else if (test_bit(xe->port, mask_bits)) {
1848: rc = -ENOTCONN; /* Masked */
1849: kvm_xen_check_poller(vcpu, xe->port);
1850: } else {
1851: rc = 1; /* Delivered to the bitmap in shared_info. */
1852: /* Now switch to the vCPU's vcpu_info to set the index and pending_sel */
1853: read_unlock_irqrestore(&gpc->lock, flags);
1854: gpc = &vcpu->arch.xen.vcpu_info_cache;
1855:
1856: read_lock_irqsave(&gpc->lock, flags);
1857: if (!kvm_gpc_check(gpc, sizeof(struct vcpu_info))) {
1858: /*
1859: * Could not access the vcpu_info. Set the bit in-kernel
1860: * and prod the vCPU to deliver it for itself.
1861: */
1862: if (!test_and_set_bit(port_word_bit, &vcpu->arch.xen.evtchn_pending_sel))
1863: kick_vcpu = true;
1864: goto out_rcu;
1865: }
1866:
1867: if (IS_ENABLED(CONFIG_64BIT) && kvm->arch.xen.long_mode) {
1868: struct vcpu_info *vcpu_info = gpc->khva;
1869: if (!test_and_set_bit(port_word_bit, &vcpu_info->evtchn_pending_sel)) {
1870: WRITE_ONCE(vcpu_info->evtchn_upcall_pending, 1);
1871: kick_vcpu = true;
1872: }
1873: } else {
1874: struct compat_vcpu_info *vcpu_info = gpc->khva;
1875: if (!test_and_set_bit(port_word_bit,
1876: (unsigned long *)&vcpu_info->evtchn_pending_sel)) {
1877: WRITE_ONCE(vcpu_info->evtchn_upcall_pending, 1);
1878: kick_vcpu = true;
1879: }
1880: }
1881:
1882: /* For the per-vCPU lapic vector, deliver it as MSI. */
1883: if (kick_vcpu && vcpu->arch.xen.upcall_vector) {
1884: kvm_xen_inject_vcpu_vector(vcpu);
1885: kick_vcpu = false;
1886: }
1887: }
1888:
1889: out_rcu:
1890: read_unlock_irqrestore(&gpc->lock, flags);
1891: srcu_read_unlock(&kvm->srcu, idx);
1892:
1893: if (kick_vcpu) {
1894: kvm_make_request(KVM_REQ_UNBLOCK, vcpu);
1895: kvm_vcpu_kick(vcpu);
1896: }
1897:
1898: return rc;
1899: }
SourceFile:arch/x86/kvm/xen.c]
|
| 145/4 |
2026/08/12 12:03 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 13258
output: 60
thoughts: 58
Thoughts: Okay, here's the summary, rendered as your internal thought process:
**Refactoring `kvm_xen_set_evtchn_fast` for Atomicity**
Alright, I've got a task here. I need to modify the function `kvm_xen_set_evtchn_fast`. Specifically, I need to add a `bool in_atomic` parameter to it. This suggests some internal synchronization changes are necessary, and this new parameter is likely related to handling atomic context. The next logical step is clear: I have to update all the places where `kvm_xen_set_evtchn_fast` is actually *used*. I need to find all the callers of this function. Let's get to it! I'll fire up my tools and locate every single instance of it being called throughout the codebase, so I can start planning the necessary parameter updates and ensure everything still works correctly after my modifications. I'm especially interested in how the callers manage their own atomicity requirements.
|
| 146/4 |
2026/08/12 12:03 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:arch/x86/kvm/xen.c IncludeSnippetLines:0 Name:kvm_xen_set_evtchn_fast SourceTreePrefix:]
Results: map[References:[map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:kvm_arch_set_irq_inatomic SourceFile:arch/x86/kvm/irq.c SourceLine:270] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:kvm_xen_set_evtchn SourceFile:arch/x86/kvm/xen.c SourceLine:1906] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:kvm_xen_set_evtchn SourceFile:arch/x86/kvm/xen.c SourceLine:1940] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:xen_timer_callback SourceFile:arch/x86/kvm/xen.c SourceLine:140]] TruncatedOutput:false]
|
| 147/4 |
2026/08/12 12:03 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 13841
output: 47
thoughts: 35
Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience:
**Refactoring the KVM/Xen Event Channel Fast Path**
Alright, let's get down to brass tacks. The objective is to revise the signature of `kvm_xen_set_evtchn_fast` within the `arch/x86/kvm/xen.h` header. This indicates a potential change to the interface used for fast event channel injection between KVM and Xen on x86 architectures. I need to carefully consider the implications. What's the driving force behind this change? Is this about addressing a specific performance bottleneck in the fast path? Are we looking at security hardening, potentially related to guest-to-host event signaling? Or perhaps a refactoring to accommodate some new Xen hypercall or feature?
I have to carefully analyze how this function is currently used throughout the codebase. The goal will be to make sure it doesn't break any existing KVM-Xen interactions. I'll need to check the calling conventions, the arguments passed, and any potential side effects. Is this a simple parameter modification, or does it involve a more significant internal restructuring of the fast path? I'll need to think about backward compatibility, too. Are we introducing a new version that requires a feature flag or a new hypercall? Will the signature change impact ABI compatibility with older Xen versions? Are there any related structures or macros that also need to be updated in lockstep? This will require going through the change with a fine-toothed comb to ensure that no regressions are introduced. I need to be methodical and account for all dependencies and corner cases. This is a critical piece of the KVM/Xen integration, so sloppiness is not an option.
|
| 148/4 |
2026/08/12 12:04 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:arch/x86/kvm/xen.h Name:kvm_xen_set_evtchn_fast]
Results: map[SourceCode:1785: /*
1786: * The return value from this function is propagated to kvm_set_irq() API,
1787: * so it returns:
1788: * < 0 Interrupt was ignored (masked or not delivered for other reasons)
1789: * = 0 Interrupt was coalesced (previous irq is still pending)
1790: * > 0 Number of CPUs interrupt was delivered to
1791: *
1792: * It is also called directly from kvm_arch_set_irq_inatomic(), where the
1793: * only check on its return value is a comparison with -EWOULDBLOCK'.
1794: */
1795: int kvm_xen_set_evtchn_fast(struct kvm_xen_evtchn *xe, struct kvm *kvm)
1796: {
1797: struct gfn_to_pfn_cache *gpc = &kvm->arch.xen.shinfo_cache;
1798: struct kvm_vcpu *vcpu;
1799: unsigned long *pending_bits, *mask_bits;
1800: unsigned long flags;
1801: int port_word_bit;
1802: bool kick_vcpu = false;
1803: int vcpu_idx, idx, rc;
1804:
1805: vcpu_idx = READ_ONCE(xe->vcpu_idx);
1806: if (vcpu_idx >= 0)
1807: vcpu = kvm_get_vcpu(kvm, vcpu_idx);
1808: else {
1809: vcpu = kvm_get_vcpu_by_id(kvm, xe->vcpu_id);
1810: if (!vcpu)
1811: return -EINVAL;
1812: WRITE_ONCE(xe->vcpu_idx, vcpu->vcpu_idx);
1813: }
1814:
1815: if (xe->port >= max_evtchn_port(kvm))
1816: return -EINVAL;
1817:
1818: rc = -EWOULDBLOCK;
1819:
1820: idx = srcu_read_lock(&kvm->srcu);
1821:
1822: read_lock_irqsave(&gpc->lock, flags);
1823: if (!kvm_gpc_check(gpc, PAGE_SIZE))
1824: goto out_rcu;
1825:
1826: if (IS_ENABLED(CONFIG_64BIT) && kvm->arch.xen.long_mode) {
1827: struct shared_info *shinfo = gpc->khva;
1828: pending_bits = (unsigned long *)&shinfo->evtchn_pending;
1829: mask_bits = (unsigned long *)&shinfo->evtchn_mask;
1830: port_word_bit = xe->port / 64;
1831: } else {
1832: struct compat_shared_info *shinfo = gpc->khva;
1833: pending_bits = (unsigned long *)&shinfo->evtchn_pending;
1834: mask_bits = (unsigned long *)&shinfo->evtchn_mask;
1835: port_word_bit = xe->port / 32;
1836: }
1837:
1838: /*
1839: * If this port wasn't already set, and if it isn't masked, then
1840: * we try to set the corresponding bit in the in-kernel shadow of
1841: * evtchn_pending_sel for the target vCPU. And if *that* wasn't
1842: * already set, then we kick the vCPU in question to write to the
1843: * *real* evtchn_pending_sel in its own guest vcpu_info struct.
1844: */
1845: if (test_and_set_bit(xe->port, pending_bits)) {
1846: rc = 0; /* It was already raised */
1847: } else if (test_bit(xe->port, mask_bits)) {
1848: rc = -ENOTCONN; /* Masked */
1849: kvm_xen_check_poller(vcpu, xe->port);
1850: } else {
1851: rc = 1; /* Delivered to the bitmap in shared_info. */
1852: /* Now switch to the vCPU's vcpu_info to set the index and pending_sel */
1853: read_unlock_irqrestore(&gpc->lock, flags);
1854: gpc = &vcpu->arch.xen.vcpu_info_cache;
1855:
1856: read_lock_irqsave(&gpc->lock, flags);
1857: if (!kvm_gpc_check(gpc, sizeof(struct vcpu_info))) {
1858: /*
1859: * Could not access the vcpu_info. Set the bit in-kernel
1860: * and prod the vCPU to deliver it for itself.
1861: */
1862: if (!test_and_set_bit(port_word_bit, &vcpu->arch.xen.evtchn_pending_sel))
1863: kick_vcpu = true;
1864: goto out_rcu;
1865: }
1866:
1867: if (IS_ENABLED(CONFIG_64BIT) && kvm->arch.xen.long_mode) {
1868: struct vcpu_info *vcpu_info = gpc->khva;
1869: if (!test_and_set_bit(port_word_bit, &vcpu_info->evtchn_pending_sel)) {
1870: WRITE_ONCE(vcpu_info->evtchn_upcall_pending, 1);
1871: kick_vcpu = true;
1872: }
1873: } else {
1874: struct compat_vcpu_info *vcpu_info = gpc->khva;
1875: if (!test_and_set_bit(port_word_bit,
1876: (unsigned long *)&vcpu_info->evtchn_pending_sel)) {
1877: WRITE_ONCE(vcpu_info->evtchn_upcall_pending, 1);
1878: kick_vcpu = true;
1879: }
1880: }
1881:
1882: /* For the per-vCPU lapic vector, deliver it as MSI. */
1883: if (kick_vcpu && vcpu->arch.xen.upcall_vector) {
1884: kvm_xen_inject_vcpu_vector(vcpu);
1885: kick_vcpu = false;
1886: }
1887: }
1888:
1889: out_rcu:
1890: read_unlock_irqrestore(&gpc->lock, flags);
1891: srcu_read_unlock(&kvm->srcu, idx);
1892:
1893: if (kick_vcpu) {
1894: kvm_make_request(KVM_REQ_UNBLOCK, vcpu);
1895: kvm_vcpu_kick(vcpu);
1896: }
1897:
1898: return rc;
1899: }
SourceFile:arch/x86/kvm/xen.c]
|
| 149/4 |
2026/08/12 12:04 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 16376
output: 170
thoughts: 32
Thoughts: Here's my analysis of the task, thinking through it as if I were about to execute it:
**Digging into `kvm_xen_set_evtchn_fast`**
Okay, so the instruction is to edit `kvm_xen_set_evtchn_fast` and, more specifically, to find it within `arch/x86/kvm/xen.c`. Right away, I'm thinking this is a hypervisor-specific optimization, likely dealing with event channel management between KVM and Xen. The "fast" in the name suggests it's a critical performance path, probably involved in interrupt delivery or signaling. This likely has a direct impact on guest OS responsiveness and latency. I need to be careful.
I'll start by making sure I have the correct version of the code, maybe checking the relevant git branch and commit history. I want to understand what's been changed recently around this function to avoid stepping on anyone's toes or introducing regressions. Before I even look at the function's internals, I'll review its surrounding code, what calls it, and where it's called from. That'll give me context. Understanding the purpose of `evtchn` and its relationship to the KVM/Xen communication will be key. Is it about efficiently delivering interrupts from Xen to a KVM guest, or something else entirely? A quick `git grep` within the project, perhaps restricted to that file or the KVM subsystem, should help me build that context quickly.
|
| 150/4 |
2026/08/12 12:04 |
tool |
codeeditor |
0m
Args: map[CurrentCode:int kvm_xen_set_evtchn_fast(struct kvm_xen_evtchn *xe, struct kvm *kvm)
{
struct gfn_to_pfn_cache *gpc = &kvm->arch.xen.shinfo_cache;
NewCode: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;
SourceFile:arch/x86/kvm/xen.c]
Results:
|
| 151/4 |
2026/08/12 12:04 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 16903
output: 230
thoughts: 23
Thoughts: Here's a summary of my thought process, as requested:
**Diving into `kvm_xen_set_evtchn_fast` - Lock Acquisition**
Okay, time to analyze this function: `kvm_xen_set_evtchn_fast`. My focus is zeroing in on the *first* lock acquisition within it. Understanding locking primitives is fundamental to concurrency, and therefore to hypervisor performance and stability. I need to understand how this lock fits into the overall scheme of things. Is it a spinlock, a mutex? What's it protecting? Is this where a potential bottleneck might exist? Are there any obvious areas for optimization, perhaps lock order reversal or lock-free approaches depending on the architecture? Understanding the scope of what is being protected here is vital.
|
| 152/4 |
2026/08/12 12:04 |
tool |
codeeditor |
0m
Args: map[CurrentCode: idx = srcu_read_lock(&kvm->srcu);
read_lock_irqsave(&gpc->lock, flags);
if (!kvm_gpc_check(gpc, PAGE_SIZE))
goto out_rcu;
NewCode: idx = srcu_read_lock(&kvm->srcu);
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;
SourceFile:arch/x86/kvm/xen.c]
Results:
|
| 153/4 |
2026/08/12 12:04 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 17327
output: 572
thoughts: 23
Thoughts: Here's my take on those thoughts, tailored for an expert audience:
**Refining Event Channel Fast Path in KVM/Xen Integration**
Okay, time to zero in on the second lock acquisition within `kvm_xen_set_evtchn_fast`. We're talking about optimization here, and that second lock is a potential bottleneck. I need to re-evaluate the critical section it's protecting. Is this lock truly necessary, or is there a way to achieve the same safety guarantees with a more granular locking strategy, perhaps leveraging atomic operations where applicable?
My focus is on minimizing contention, especially in this performance-critical path. I need to consider the implications of any changes on both KVM and Xen sides, especially any potential for race conditions or unintended side effects on event delivery. I'll need to meticulously analyze the surrounding code, considering the hardware dependencies, especially CPU features, memory ordering, and any potential cache line bouncing problems.
Essentially, I need to pinpoint whether the lock is *actually* needed, and if so, can it be relaxed in any form? What kind of optimizations could we achieve? Are there any clever tricks we can use to avoid the lock completely, or reduce its scope? I have to go through the code carefully. This is where the magic happens, and what's at stake is the efficiency of the virtualized environment.
|
| 154/4 |
2026/08/12 12:04 |
tool |
codeeditor |
0m
Args: map[CurrentCode: rc = 1; /* Delivered to the bitmap in shared_info. */
/* Now switch to the vCPU's vcpu_info to set the index and pending_sel */
read_unlock_irqrestore(&gpc->lock, flags);
gpc = &vcpu->arch.xen.vcpu_info_cache;
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
* 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;
}
NewCode: rc = 1; /* Delivered to the bitmap in shared_info. */
/* Now switch to the vCPU's vcpu_info to set the index and pending_sel */
read_unlock_irqrestore(&gpc->lock, flags);
gpc = &vcpu->arch.xen.vcpu_info_cache;
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
* 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;
}
SourceFile:arch/x86/kvm/xen.c]
Results:
|
| 155/4 |
2026/08/12 12:04 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 18213
output: 149
thoughts: 25
Thoughts: Here's my take on those thoughts:
**Refining the `kvm_xen_set_evtchn_fast` Label**
Okay, time to focus. I need to make some targeted edits to the `out_rcu` label within the `kvm_xen_set_evtchn_fast` function. No broad strokes, just surgical precision here. This implies I've identified something specific within that code segment that requires attention, likely related to how it handles the Release-Copy-Update (RCU) mechanism in the context of Xen event channel management. Let's see what needs tweaking in there.
|
| 156/4 |
2026/08/12 12:04 |
tool |
codeeditor |
0m
Args: map[CurrentCode: out_rcu:
read_unlock_irqrestore(&gpc->lock, flags);
srcu_read_unlock(&kvm->srcu, idx);
if (kick_vcpu) {
NewCode: out_rcu:
read_unlock_irqrestore(&gpc->lock, flags);
out_rcu_no_lock:
srcu_read_unlock(&kvm->srcu, idx);
if (kick_vcpu) {
SourceFile:arch/x86/kvm/xen.c]
Results:
|
| 157/4 |
2026/08/12 12:04 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 18525
output: 47
thoughts: 19
Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience, and written as if they were my own:
**Updating `arch/x86/kvm/xen.h`**
Right, let's get down to brass tacks and update `arch/x86/kvm/xen.h`. This file is critical, obviously. It’s where we deal with the KVM-Xen interactions, those nitty-gritty details of bridging the gap between the KVM hypervisor and Xen-specific functionality. We're likely either patching up some known issues, adding support for a new Xen feature that requires KVM integration, or perhaps cleaning up existing cruft and making the code more maintainable. I need to be especially mindful of any architectural nuances of the Xen interface – making sure we aren’t introducing regressions or breaking compatibility with earlier Xen versions. The potential for subtle interactions with things like paravirtualization and memory management are what I'll be keeping an eye on. I'll need to double check any changes that touch upon CPUID handling, interrupt delivery, or guest memory layout. This file touches on sensitive areas. I'm going to have to test any changes thoroughly on various Xen setups. Time to dive in.
|
| 158/4 |
2026/08/12 12:04 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:arch/x86/kvm/xen.h Name:kvm_xen_set_evtchn_fast]
Results: map[SourceCode:1785: /*
1786: * The return value from this function is propagated to kvm_set_irq() API,
1787: * so it returns:
1788: * < 0 Interrupt was ignored (masked or not delivered for other reasons)
1789: * = 0 Interrupt was coalesced (previous irq is still pending)
1790: * > 0 Number of CPUs interrupt was delivered to
1791: *
1792: * It is also called directly from kvm_arch_set_irq_inatomic(), where the
1793: * only check on its return value is a comparison with -EWOULDBLOCK'.
1794: */
1795: int kvm_xen_set_evtchn_fast(struct kvm_xen_evtchn *xe, struct kvm *kvm)
1796: {
1797: struct gfn_to_pfn_cache *gpc = &kvm->arch.xen.shinfo_cache;
1798: struct kvm_vcpu *vcpu;
1799: unsigned long *pending_bits, *mask_bits;
1800: unsigned long flags;
1801: int port_word_bit;
1802: bool kick_vcpu = false;
1803: int vcpu_idx, idx, rc;
1804:
1805: vcpu_idx = READ_ONCE(xe->vcpu_idx);
1806: if (vcpu_idx >= 0)
1807: vcpu = kvm_get_vcpu(kvm, vcpu_idx);
1808: else {
1809: vcpu = kvm_get_vcpu_by_id(kvm, xe->vcpu_id);
1810: if (!vcpu)
1811: return -EINVAL;
1812: WRITE_ONCE(xe->vcpu_idx, vcpu->vcpu_idx);
1813: }
1814:
1815: if (xe->port >= max_evtchn_port(kvm))
1816: return -EINVAL;
1817:
1818: rc = -EWOULDBLOCK;
1819:
1820: idx = srcu_read_lock(&kvm->srcu);
1821:
1822: read_lock_irqsave(&gpc->lock, flags);
1823: if (!kvm_gpc_check(gpc, PAGE_SIZE))
1824: goto out_rcu;
1825:
1826: if (IS_ENABLED(CONFIG_64BIT) && kvm->arch.xen.long_mode) {
1827: struct shared_info *shinfo = gpc->khva;
1828: pending_bits = (unsigned long *)&shinfo->evtchn_pending;
1829: mask_bits = (unsigned long *)&shinfo->evtchn_mask;
1830: port_word_bit = xe->port / 64;
1831: } else {
1832: struct compat_shared_info *shinfo = gpc->khva;
1833: pending_bits = (unsigned long *)&shinfo->evtchn_pending;
1834: mask_bits = (unsigned long *)&shinfo->evtchn_mask;
1835: port_word_bit = xe->port / 32;
1836: }
1837:
1838: /*
1839: * If this port wasn't already set, and if it isn't masked, then
1840: * we try to set the corresponding bit in the in-kernel shadow of
1841: * evtchn_pending_sel for the target vCPU. And if *that* wasn't
1842: * already set, then we kick the vCPU in question to write to the
1843: * *real* evtchn_pending_sel in its own guest vcpu_info struct.
1844: */
1845: if (test_and_set_bit(xe->port, pending_bits)) {
1846: rc = 0; /* It was already raised */
1847: } else if (test_bit(xe->port, mask_bits)) {
1848: rc = -ENOTCONN; /* Masked */
1849: kvm_xen_check_poller(vcpu, xe->port);
1850: } else {
1851: rc = 1; /* Delivered to the bitmap in shared_info. */
1852: /* Now switch to the vCPU's vcpu_info to set the index and pending_sel */
1853: read_unlock_irqrestore(&gpc->lock, flags);
1854: gpc = &vcpu->arch.xen.vcpu_info_cache;
1855:
1856: read_lock_irqsave(&gpc->lock, flags);
1857: if (!kvm_gpc_check(gpc, sizeof(struct vcpu_info))) {
1858: /*
1859: * Could not access the vcpu_info. Set the bit in-kernel
1860: * and prod the vCPU to deliver it for itself.
1861: */
1862: if (!test_and_set_bit(port_word_bit, &vcpu->arch.xen.evtchn_pending_sel))
1863: kick_vcpu = true;
1864: goto out_rcu;
1865: }
1866:
1867: if (IS_ENABLED(CONFIG_64BIT) && kvm->arch.xen.long_mode) {
1868: struct vcpu_info *vcpu_info = gpc->khva;
1869: if (!test_and_set_bit(port_word_bit, &vcpu_info->evtchn_pending_sel)) {
1870: WRITE_ONCE(vcpu_info->evtchn_upcall_pending, 1);
1871: kick_vcpu = true;
1872: }
1873: } else {
1874: struct compat_vcpu_info *vcpu_info = gpc->khva;
1875: if (!test_and_set_bit(port_word_bit,
1876: (unsigned long *)&vcpu_info->evtchn_pending_sel)) {
1877: WRITE_ONCE(vcpu_info->evtchn_upcall_pending, 1);
1878: kick_vcpu = true;
1879: }
1880: }
1881:
1882: /* For the per-vCPU lapic vector, deliver it as MSI. */
1883: if (kick_vcpu && vcpu->arch.xen.upcall_vector) {
1884: kvm_xen_inject_vcpu_vector(vcpu);
1885: kick_vcpu = false;
1886: }
1887: }
1888:
1889: out_rcu:
1890: read_unlock_irqrestore(&gpc->lock, flags);
1891: srcu_read_unlock(&kvm->srcu, idx);
1892:
1893: if (kick_vcpu) {
1894: kvm_make_request(KVM_REQ_UNBLOCK, vcpu);
1895: kvm_vcpu_kick(vcpu);
1896: }
1897:
1898: return rc;
1899: }
SourceFile:arch/x86/kvm/xen.c]
|
| 159/4 |
2026/08/12 12:04 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 20945
output: 47
thoughts: 54
Thoughts: Okay, here's the summary, tailored for an expert audience, in the first person:
**My Immediate Task: Ensuring Header Consistency**
Right, so I've just been working on `kvm_xen_set_evtchn_fast` within the `kvm/xen.c` file – made some changes there, implemented something specific, probably optimized something. Now, I need to circle back and confirm that the *declaration* of that function, the interface if you will, is correctly reflected in `kvm/xen.h`. It's critical that the header file stays in sync with the source file. Otherwise, I'll introduce a build error or, worse, some subtle, hard-to-debug runtime issues with the Xen integration. This is a basic, but vital, aspect of software maintenance. Got to keep my ducks in a row. It's time to check the header.
|
| 160/4 |
2026/08/12 12:04 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:arch/x86/kvm/xen.h Name:kvm_xen_set_evtchn_fast]
Results: map[SourceCode:1785: /*
1786: * The return value from this function is propagated to kvm_set_irq() API,
1787: * so it returns:
1788: * < 0 Interrupt was ignored (masked or not delivered for other reasons)
1789: * = 0 Interrupt was coalesced (previous irq is still pending)
1790: * > 0 Number of CPUs interrupt was delivered to
1791: *
1792: * It is also called directly from kvm_arch_set_irq_inatomic(), where the
1793: * only check on its return value is a comparison with -EWOULDBLOCK'.
1794: */
1795: int kvm_xen_set_evtchn_fast(struct kvm_xen_evtchn *xe, struct kvm *kvm)
1796: {
1797: struct gfn_to_pfn_cache *gpc = &kvm->arch.xen.shinfo_cache;
1798: struct kvm_vcpu *vcpu;
1799: unsigned long *pending_bits, *mask_bits;
1800: unsigned long flags;
1801: int port_word_bit;
1802: bool kick_vcpu = false;
1803: int vcpu_idx, idx, rc;
1804:
1805: vcpu_idx = READ_ONCE(xe->vcpu_idx);
1806: if (vcpu_idx >= 0)
1807: vcpu = kvm_get_vcpu(kvm, vcpu_idx);
1808: else {
1809: vcpu = kvm_get_vcpu_by_id(kvm, xe->vcpu_id);
1810: if (!vcpu)
1811: return -EINVAL;
1812: WRITE_ONCE(xe->vcpu_idx, vcpu->vcpu_idx);
1813: }
1814:
1815: if (xe->port >= max_evtchn_port(kvm))
1816: return -EINVAL;
1817:
1818: rc = -EWOULDBLOCK;
1819:
1820: idx = srcu_read_lock(&kvm->srcu);
1821:
1822: read_lock_irqsave(&gpc->lock, flags);
1823: if (!kvm_gpc_check(gpc, PAGE_SIZE))
1824: goto out_rcu;
1825:
1826: if (IS_ENABLED(CONFIG_64BIT) && kvm->arch.xen.long_mode) {
1827: struct shared_info *shinfo = gpc->khva;
1828: pending_bits = (unsigned long *)&shinfo->evtchn_pending;
1829: mask_bits = (unsigned long *)&shinfo->evtchn_mask;
1830: port_word_bit = xe->port / 64;
1831: } else {
1832: struct compat_shared_info *shinfo = gpc->khva;
1833: pending_bits = (unsigned long *)&shinfo->evtchn_pending;
1834: mask_bits = (unsigned long *)&shinfo->evtchn_mask;
1835: port_word_bit = xe->port / 32;
1836: }
1837:
1838: /*
1839: * If this port wasn't already set, and if it isn't masked, then
1840: * we try to set the corresponding bit in the in-kernel shadow of
1841: * evtchn_pending_sel for the target vCPU. And if *that* wasn't
1842: * already set, then we kick the vCPU in question to write to the
1843: * *real* evtchn_pending_sel in its own guest vcpu_info struct.
1844: */
1845: if (test_and_set_bit(xe->port, pending_bits)) {
1846: rc = 0; /* It was already raised */
1847: } else if (test_bit(xe->port, mask_bits)) {
1848: rc = -ENOTCONN; /* Masked */
1849: kvm_xen_check_poller(vcpu, xe->port);
1850: } else {
1851: rc = 1; /* Delivered to the bitmap in shared_info. */
1852: /* Now switch to the vCPU's vcpu_info to set the index and pending_sel */
1853: read_unlock_irqrestore(&gpc->lock, flags);
1854: gpc = &vcpu->arch.xen.vcpu_info_cache;
1855:
1856: read_lock_irqsave(&gpc->lock, flags);
1857: if (!kvm_gpc_check(gpc, sizeof(struct vcpu_info))) {
1858: /*
1859: * Could not access the vcpu_info. Set the bit in-kernel
1860: * and prod the vCPU to deliver it for itself.
1861: */
1862: if (!test_and_set_bit(port_word_bit, &vcpu->arch.xen.evtchn_pending_sel))
1863: kick_vcpu = true;
1864: goto out_rcu;
1865: }
1866:
1867: if (IS_ENABLED(CONFIG_64BIT) && kvm->arch.xen.long_mode) {
1868: struct vcpu_info *vcpu_info = gpc->khva;
1869: if (!test_and_set_bit(port_word_bit, &vcpu_info->evtchn_pending_sel)) {
1870: WRITE_ONCE(vcpu_info->evtchn_upcall_pending, 1);
1871: kick_vcpu = true;
1872: }
1873: } else {
1874: struct compat_vcpu_info *vcpu_info = gpc->khva;
1875: if (!test_and_set_bit(port_word_bit,
1876: (unsigned long *)&vcpu_info->evtchn_pending_sel)) {
1877: WRITE_ONCE(vcpu_info->evtchn_upcall_pending, 1);
1878: kick_vcpu = true;
1879: }
1880: }
1881:
1882: /* For the per-vCPU lapic vector, deliver it as MSI. */
1883: if (kick_vcpu && vcpu->arch.xen.upcall_vector) {
1884: kvm_xen_inject_vcpu_vector(vcpu);
1885: kick_vcpu = false;
1886: }
1887: }
1888:
1889: out_rcu:
1890: read_unlock_irqrestore(&gpc->lock, flags);
1891: srcu_read_unlock(&kvm->srcu, idx);
1892:
1893: if (kick_vcpu) {
1894: kvm_make_request(KVM_REQ_UNBLOCK, vcpu);
1895: kvm_vcpu_kick(vcpu);
1896: }
1897:
1898: return rc;
1899: }
SourceFile:arch/x86/kvm/xen.c]
|
| 161/4 |
2026/08/12 12:04 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 23321
output: 47
thoughts: 38
Thoughts: Here's my take on those thoughts:
**Xen Event Channel Mystery**
Okay, so `kvm_xen_set_evtchn_fast`... where *is* that declared? Right, the clue is in the name: `arch/x86/kvm/xen.h`. That's the likely spot. Time to dive into the codebase and locate it. I need to understand what this function does and how it's being used. Let's get searching.
|
| 162/4 |
2026/08/12 12:04 |
tool |
codesearch-definition-source |
0m
Error:
Args: map[ContextFile:arch/x86/kvm/xen.h Name:kvm_xen_set_evtchn_fast]
|
| 163/4 |
2026/08/12 12:04 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 23648
output: 44
thoughts: 57
Thoughts: Okay, let me delve into this.
**Tracing the Event Channel Configuration Function**
Right, so `kvm_xen_set_evtchn_fast`, interesting name. The code says it's defined in `arch/x86/kvm/xen.c`. That seems logical, given the naming convention. Now, if I'm understanding this correctly, my next step is to locate its *declaration*. Where is it *actually* defined and what are its inputs and outputs? Time to look in `arch/x86/kvm/xen.h`. That's where I'd expect to find the function signature, any related structure definitions, and perhaps some inline documentation. It's all about understanding the API, you know? Let's go digging and see what we find.
|
| 164/4 |
2026/08/12 12:04 |
tool |
grepper |
0m
Args: map[Expression:kvm_xen_set_evtchn_fast PathPrefix:arch/x86/kvm/xen.h]
Results: map[Output:arch/x86/kvm/xen.h=33=void kvm_xen_destroy_vcpu(struct kvm_vcpu *vcpu);
arch/x86/kvm/xen.h:34:int kvm_xen_set_evtchn_fast(struct kvm_xen_evtchn *xe,
arch/x86/kvm/xen.h-35- struct kvm *kvm);
]
|
| 165/4 |
2026/08/12 12:04 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 24036
output: 114
thoughts: 18
Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience:
**Diving into `arch/x86/kvm/xen.h`**
Right, let's get down to brass tacks and crack open `arch/x86/kvm/xen.h`. This file... it's a critical piece of the puzzle, isn't it? It holds the crucial definitions and structures that allow KVM on x86 to interact with Xen, or rather, to *emulate* the Xen hypercall interface. We're talking about the low-level stuff here – the ABI, the shared memory regions, the hypervisor call stubs, and all those intricate details that smooth out the interaction between the guest running under KVM and the potentially underlying Xen hypervisor, or, in many cases, gracefully handling the lack of it.
My first thought is always to quickly understand the current state of the code. Is it clean? Are there any known issues lingering? What's the change history lately? Any recent patches impacting it? Given that we're looking at a header file, my instinct is to immediately grasp the data structures: what are the key structs, enums, and macros that define how KVM mimics Xen? We're likely to see things like the `xen_hypercall_stubs`, the `xen_shared_info`, and probably a few bits and pieces related to PV (paravirtualization) and HVM (hardware virtualization) interactions.
Next, I'll be looking for potential areas that could benefit from optimization or clarification. I'm thinking about performance bottlenecks, memory alignment issues, or areas where the code might become less maintainable in the future. Are there any legacy bits lingering around that we could safely remove or streamline? Are there any data structures that are overly complex and could be simplified? I'm already anticipating potential problems around compatibility with newer kernel versions or with different Xen versions.
Finally, I'm thinking about the bigger picture. How does this file fit into the overall KVM-on-Xen architecture? What's the overall design? How robust is the emulation? What are the implications if we were to change some of these core definitions? This is a file that requires careful consideration. I need to make sure any modifications are well-tested and won't introduce regressions or security vulnerabilities. It's time to dig in.
|
| 166/4 |
2026/08/12 12:04 |
tool |
codeeditor |
0m
Args: map[CurrentCode:int kvm_xen_set_evtchn_fast(struct kvm_xen_evtchn *xe,
struct kvm *kvm);
NewCode:int kvm_xen_set_evtchn_fast(struct kvm_xen_evtchn *xe,
struct kvm *kvm, bool in_atomic);
SourceFile:arch/x86/kvm/xen.h]
Results:
|
| 167/4 |
2026/08/12 12:04 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 24685
output: 49
thoughts: 95
Thoughts: Okay, let's get down to brass tacks. I need to get these callers updated. Here's what's on my mental checklist:
**Updating Caller Functions**
First, I need to look at `kvm_arch_set_irq_inatomic` in `arch/x86/kvm/irq.c`. I have to figure out how this function is going to be impacted. Is it simply a matter of a straightforward adaptation, or is there a more significant change I need to consider?
Next up, I'll take a look at `kvm_xen_set_evtchn` in `arch/x86/kvm/xen.c`. This is another one that needs careful scrutiny. It deals with Xen-specific event channel setup, so I'll need to think about any implications this has, particularly in the context of interactions with Xen's hypercall interface. I'll need to consider performance impacts and potential race conditions here.
Finally, I'll review `xen_timer_callback`, also in `arch/x86/kvm/xen.c`. This is likely to be time-sensitive, so I need to make sure I understand how changes impact the callback's timing and the overall responsiveness of the system. I anticipate this one might have subtleties.
These are the immediate items I will look at in order to start addressing the changes.
|
| 168/4 |
2026/08/12 12:05 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:arch/x86/kvm/irq.c Name:kvm_arch_set_irq_inatomic]
Results: map[SourceCode: 241: int kvm_arch_set_irq_inatomic(struct kvm_kernel_irq_routing_entry *e,
242: struct kvm *kvm, int irq_source_id, int level,
243: bool line_status)
244: {
245: struct kvm_lapic_irq irq;
246: int r;
247:
248: switch (e->type) {
249: #ifdef CONFIG_KVM_HYPERV
250: case KVM_IRQ_ROUTING_HV_SINT:
251: return kvm_hv_synic_set_irq(e, kvm, irq_source_id, level,
252: line_status);
253: #endif
254:
255: case KVM_IRQ_ROUTING_MSI:
256: if (kvm_msi_route_invalid(kvm, e))
257: return -EINVAL;
258:
259: kvm_msi_to_lapic_irq(kvm, e, &irq);
260:
261: if (kvm_irq_delivery_to_apic_fast(kvm, NULL, &irq, &r))
262: return r;
263: break;
264:
265: #ifdef CONFIG_KVM_XEN
266: case KVM_IRQ_ROUTING_XEN_EVTCHN:
267: if (!level)
268: return -1;
269:
270: return kvm_xen_set_evtchn_fast(&e->xen_evtchn, kvm);
271: #endif
272: default:
273: break;
274: }
275:
276: return -EWOULDBLOCK;
277: }
SourceFile:arch/x86/kvm/irq.c]
|
| 169/4 |
2026/08/12 12:05 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 25724
output: 160
thoughts: 34
Thoughts: Here's my take on the proposed task:
**Focusing on `kvm_arch_set_irq_inatomic`**
Okay, so the task at hand is to dive into `kvm_arch_set_irq_inatomic`. Given my expertise, I'm already mentally running through the implications. I know this function, residing in `arch/x86/kvm/irq.c`, is critical for managing interrupts within the KVM virtualization layer on x86 architectures. The "inatomic" part immediately flags this as something handled with specific care to avoid race conditions and ensure interrupt delivery integrity. My first thought is to assess the potential impact of any changes on performance and stability within KVM. What are the calling contexts? How does it interact with other interrupt handling routines? I need to analyze its role in delivering virtual interrupts to guest VMs. Any modification to this area could have significant ripple effects. So, let's get down to brass tacks: time to dig into the code and the surrounding infrastructure!
|
| 170/4 |
2026/08/12 12:05 |
tool |
codeeditor |
0m
Args: map[CurrentCode: case KVM_IRQ_ROUTING_XEN_EVTCHN:
if (!level)
return -1;
return kvm_xen_set_evtchn_fast(&e->xen_evtchn, kvm);
#endif
NewCode: case KVM_IRQ_ROUTING_XEN_EVTCHN:
if (!level)
return -1;
return kvm_xen_set_evtchn_fast(&e->xen_evtchn, kvm, true);
#endif
SourceFile:arch/x86/kvm/irq.c]
Results:
|
| 171/4 |
2026/08/12 12:05 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 26145
output: 45
thoughts: 32
Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience:
**Diving into `kvm_xen_set_evtchn`**
Alright, let's get into the nitty-gritty of `kvm_xen_set_evtchn` – specifically, I'm checking it out in `arch/x86/kvm/xen.c`. This function is where the magic happens, right? I'm immediately thinking about the interaction between KVM and Xen. My primary focus is likely understanding how event channels are established and managed in this hybrid environment. I'm probably looking for the nuances of how KVM handles Xen's specific event mechanism, especially given the differing underlying interrupt models. I want to see if this function is responsible for mapping event channels or signaling mechanisms and if so, how. Are there any performance implications? Are there any potential race conditions I should be mindful of, particularly related to the hypercall interface or the shared memory regions? I bet there are some interesting synchronization primitives used here. Let's see how they handled the Xen-KVM integration.
|
| 172/4 |
2026/08/12 12:05 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:arch/x86/kvm/xen.c Name:kvm_xen_set_evtchn]
Results: map[SourceCode:1901: static int kvm_xen_set_evtchn(struct kvm_xen_evtchn *xe, struct kvm *kvm)
1902: {
1903: bool mm_borrowed = false;
1904: int rc;
1905:
1906: rc = kvm_xen_set_evtchn_fast(xe, kvm);
1907: if (rc != -EWOULDBLOCK)
1908: return rc;
1909:
1910: if (current->mm != kvm->mm) {
1911: /*
1912: * If not on a thread which already belongs to this KVM,
1913: * we'd better be in the irqfd workqueue.
1914: */
1915: if (WARN_ON_ONCE(current->mm))
1916: return -EINVAL;
1917:
1918: kthread_use_mm(kvm->mm);
1919: mm_borrowed = true;
1920: }
1921:
1922: /*
1923: * It is theoretically possible for the page to be unmapped
1924: * and the MMU notifier to invalidate the shared_info before
1925: * we even get to use it. In that case, this looks like an
1926: * infinite loop. It was tempting to do it via the userspace
1927: * HVA instead... but that just *hides* the fact that it's
1928: * an infinite loop, because if a fault occurs and it waits
1929: * for the page to come back, it can *still* immediately
1930: * fault and have to wait again, repeatedly.
1931: *
1932: * Conversely, the page could also have been reinstated by
1933: * another thread before we even obtain the mutex above, so
1934: * check again *first* before remapping it.
1935: */
1936: do {
1937: struct gfn_to_pfn_cache *gpc = &kvm->arch.xen.shinfo_cache;
1938: int idx;
1939:
1940: rc = kvm_xen_set_evtchn_fast(xe, kvm);
1941: if (rc != -EWOULDBLOCK)
1942: break;
1943:
1944: idx = srcu_read_lock(&kvm->srcu);
1945: rc = kvm_gpc_refresh(gpc, PAGE_SIZE);
1946: srcu_read_unlock(&kvm->srcu, idx);
1947: } while(!rc);
1948:
1949: if (mm_borrowed)
1950: kthread_unuse_mm(kvm->mm);
1951:
1952: return rc;
1953: }
SourceFile:arch/x86/kvm/xen.c]
|
| 173/4 |
2026/08/12 12:05 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 27303
output: 149
thoughts: 30
Thoughts: Here's my summary, as though I'm thinking through this:
**Digging into `kvm_xen_set_evtchn`**
Okay, time to dive into `kvm_xen_set_evtchn` within `arch/x86/kvm/xen.c`. That function's core to how we handle event channels within the KVM/Xen interaction. I'll need to understand how it's setting up the communication, the interrupt routing, and any potential issues with synchronization or race conditions in this area. We need this to behave *perfectly* if we're going to allow these platforms to cooperate. I need to refresh my memory on the Xen hypercall interface and how it interacts with the KVM infrastructure. Specifically, I'll need to see how it maps the virtual event channels to physical interrupts and the relevant interrupt vectors. I need to be careful about potential side effects on other components of the virtualization stack. Let's see what the original author's comments and any existing documentation have to say. I'll start by tracing the function's call path to understand its dependencies and any context it relies on. I'll be looking for opportunities for optimization or potential bugs that might affect performance or stability, especially around context switches. Let's get to it.
|
| 174/4 |
2026/08/12 12:05 |
tool |
codeeditor |
0m
Args: map[CurrentCode: bool mm_borrowed = false;
int rc;
rc = kvm_xen_set_evtchn_fast(xe, kvm);
if (rc != -EWOULDBLOCK)
return rc;
NewCode: bool mm_borrowed = false;
int rc;
rc = kvm_xen_set_evtchn_fast(xe, kvm, false);
if (rc != -EWOULDBLOCK)
return rc;
SourceFile:arch/x86/kvm/xen.c]
Results:
|
| 175/4 |
2026/08/12 12:05 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 27764
output: 185
thoughts: 16
Thoughts: Okay, here's my interpretation of that thought process, tailored for an expert audience, and presented as if it were my own internal monologue:
**Diving into Event Channel Setup**
Alright, so we're talking about event channel setup, specifically the `kvm_xen_set_evtchn` function. That's a pretty critical piece when bridging KVM and Xen. The first thing that jumps out at me is that we're dealing with the *second* call to this function in a given context. This likely implies that there's an earlier call, maybe related to initial setup or configuration. We'll need to remember that context. The second call must be doing something specific to our needs, rather than setting it up. What is that second action doing?
We should start with the overall KVM/Xen architecture and event channels. We need to be keenly aware of the context we're in because it is the "second" call. I'm immediately thinking about:
* **Understanding the context:** What's the state of the system when this call is made? Are we already within a VM, or during the initial setup? What led to the need for a second call?
* **The first call's purpose:** What initialization or preliminary setup was performed in the first call? What expectations were set at that time, and what's changing that necessitates another call? Was the first call about the *creation* of the event channel or just part of a larger setup?
* **The function's signature and implementation:** What arguments does `kvm_xen_set_evtchn` take? What's its internal logic? And what part of the underlying Xen code is this function calling?
* **Possible scenarios:** Why would an event channel need to be modified or re-configured *after* an initial setup? Is this related to device assignment, migration, CPU hotplug, or a specific guest operation?
My immediate goal is to map this particular second call to a higher-level workflow within the KVM-Xen integration. I need to understand what triggered this, and where the event channel setup fits into that picture. I'm going to start digging into the surrounding code to determine those answers.
|
| 176/4 |
2026/08/12 12:05 |
tool |
codeeditor |
0m
Args: map[CurrentCode: 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;
NewCode: struct gfn_to_pfn_cache *gpc = &kvm->arch.xen.shinfo_cache;
int idx;
rc = kvm_xen_set_evtchn_fast(xe, kvm, false);
if (rc != -EWOULDBLOCK)
break;
SourceFile:arch/x86/kvm/xen.c]
Results:
|
| 177/4 |
2026/08/12 12:05 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 28442
output: 41
thoughts: 28
Thoughts: Okay, here's my summary, taking on the persona you requested:
**Xen Timer Callback Deep Dive**
Alright, let's dive into this `xen_timer_callback` function, specifically in `arch/x86/kvm/xen.c`. I need to understand how Xen's timers are interacting with the KVM infrastructure on this x86 architecture. This function is crucial, as it's the point where the Xen hypervisor’s timer events are handled within the KVM domain. I'm thinking, this is where the guest's virtual timers are likely being synchronized with the host's physical timers, or perhaps where virtual interrupts are being injected into the guest. My primary focus here has to be on how efficiently and accurately this callback is handling timer expirations, and ensuring there's minimal overhead in transitioning between the Xen domain and the KVM environment. I'm expecting to see some sort of mechanism for scheduling KVM work from within the Xen context – perhaps using some sort of work queue or interrupt injection process. I also want to see how it handles edge cases like timer latencies and any potential race conditions between the Xen and KVM timer systems. This is the heart of time management in a Xen-KVM setup, so understanding the flow is critical.
|
| 178/4 |
2026/08/12 12:05 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:arch/x86/kvm/xen.c Name:xen_timer_callback]
Results: map[SourceCode: 125: static enum hrtimer_restart xen_timer_callback(struct hrtimer *timer)
126: {
127: struct kvm_vcpu *vcpu = container_of(timer, struct kvm_vcpu,
128: arch.xen.timer);
129: struct kvm_xen_evtchn e;
130: int rc;
131:
132: if (atomic_read(&vcpu->arch.xen.timer_pending))
133: return HRTIMER_NORESTART;
134:
135: e.vcpu_id = vcpu->vcpu_id;
136: e.vcpu_idx = vcpu->vcpu_idx;
137: e.port = vcpu->arch.xen.timer_virq;
138: e.priority = KVM_IRQ_ROUTING_XEN_EVTCHN_PRIO_2LEVEL;
139:
140: rc = kvm_xen_set_evtchn_fast(&e, vcpu->kvm);
141: if (rc != -EWOULDBLOCK) {
142: vcpu->arch.xen.timer_expires = 0;
143: return HRTIMER_NORESTART;
144: }
145:
146: atomic_inc(&vcpu->arch.xen.timer_pending);
147: kvm_make_request(KVM_REQ_UNBLOCK, vcpu);
148: kvm_vcpu_kick(vcpu);
149:
150: return HRTIMER_NORESTART;
151: }
SourceFile:arch/x86/kvm/xen.c]
|
| 179/4 |
2026/08/12 12:05 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 29273
output: 241
thoughts: 26
Thoughts: Okay, here's my take on those thoughts, framed for an expert audience, as if they were my own:
**Diving into `xen_timer_callback` in `arch/x86/kvm/xen.c`**
Alright, time to dig into the guts of the Xen-KVM integration. Specifically, I'm focusing on the `xen_timer_callback` function within the x86 architecture code, in `arch/x86/kvm/xen.c`. Given my understanding of the hypervisor's interaction with the guest, I'm already anticipating the nuances of how Xen's timing mechanisms are plumbed through to KVM. I'm probably looking at potential synchronization issues, interrupt handling, or maybe some corner cases around timer migration. I'll need to refresh myself on how the hypercall interface is used for time management, and exactly what the Xen clocksource looks like from within KVM's perspective. It's usually worth investigating for performance bottlenecks, especially under heavy load. I want to see how efficiently it handles the timer events and whether it could benefit from some optimization – perhaps some locking improvements or a more optimized approach to interrupt injection. I should also check how the code interacts with the other timing-related functions and if there's any potential for race conditions or unintended side effects. My ultimate goal is to see if there's an opportunity to fine-tune it or identify and address any potential vulnerabilities in timing control.
|
| 180/4 |
2026/08/12 12:05 |
tool |
codeeditor |
0m
Args: map[CurrentCode: 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);
if (rc != -EWOULDBLOCK) {
vcpu->arch.xen.timer_expires = 0;
NewCode: 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, true);
if (rc != -EWOULDBLOCK) {
vcpu->arch.xen.timer_expires = 0;
SourceFile:arch/x86/kvm/xen.c]
Results:
|
| 181/4 |
2026/08/12 12:05 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 29856
output: 15
thoughts: 7
Thoughts: Okay, here's my attempt at summarizing the thought process, tailored for an expert audience, and written in the first person:
**Reviewing the Revisions: A Critical Eye**
Alright, time to dig into these changes. First, I need to methodically walk through each modification, no matter how seemingly minor. I'm looking beyond the surface level; I need to assess the impact of each adjustment on the overall architecture, performance, and long-term maintainability. Are these improvements truly additive, or are they introducing unforeseen complexities or dependencies?
I'll be paying close attention to the edge cases. Does this change adequately handle all the scenarios we anticipated, and perhaps more importantly, the ones we *didn't*? Thinking specifically about [*insert relevant specific technical area*], is the implementation still robust under [*insert specific technical conditions relevant to the user's field*]?
Next, it's about evaluating the trade-offs. Did they prioritize [Specific aspect 1] at the expense of [Specific aspect 2]? Is that decision justifiable in the context of the project's long-term goals and constraints? I'll need to dissect the reasoning behind the modifications to ascertain if they're conceptually sound, and I'll need to ask myself if there were alternative approaches that might have yielded a more optimal balance. I'm looking for elegance and efficiency here.
Finally, I need to confirm compliance. I need to make sure that these modifications are in alignment with our established coding standards, design principles, and any relevant regulatory or security requirements. I need to make sure that the change is well-documented, making sure that there is clear traceability and making sure that the new code is adequately tested. After all, if the code is not well tested, what is the value?
In short, this is less about a cursory glance and more about a deep, critical analysis. We are looking for perfection.
|
| 182/4 |
2026/08/12 12:05 |
tool |
patch-diff |
0m
Args:
Results: map[Output:diff --git a/arch/x86/kvm/irq.c b/arch/x86/kvm/irq.c
index cb8ac4b9b..cfefa25be 100644
--- a/arch/x86/kvm/irq.c
+++ b/arch/x86/kvm/irq.c
@@ -241,40 +241,40 @@ int kvm_set_msi(struct kvm_kernel_irq_routing_entry *e,
int kvm_arch_set_irq_inatomic(struct kvm_kernel_irq_routing_entry *e,
struct kvm *kvm, int irq_source_id, int level,
bool line_status)
{
struct kvm_lapic_irq irq;
int r;
switch (e->type) {
#ifdef CONFIG_KVM_HYPERV
case KVM_IRQ_ROUTING_HV_SINT:
return kvm_hv_synic_set_irq(e, kvm, irq_source_id, level,
line_status);
#endif
case KVM_IRQ_ROUTING_MSI:
if (kvm_msi_route_invalid(kvm, e))
return -EINVAL;
kvm_msi_to_lapic_irq(kvm, e, &irq);
if (kvm_irq_delivery_to_apic_fast(kvm, NULL, &irq, &r))
return r;
break;
#ifdef CONFIG_KVM_XEN
- case KVM_IRQ_ROUTING_XEN_EVTCHN:
- if (!level)
- return -1;
+ case KVM_IRQ_ROUTING_XEN_EVTCHN:
+ 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;
}
return -EWOULDBLOCK;
}
int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *irq_event,
bool line_status)
diff --git a/arch/x86/kvm/xen.c b/arch/x86/kvm/xen.c
index 694b31c1f..7ebbefe43 100644
--- a/arch/x86/kvm/xen.c
+++ b/arch/x86/kvm/xen.c
@@ -125,27 +125,27 @@ void kvm_xen_inject_timer_irqs(struct kvm_vcpu *vcpu)
static enum hrtimer_restart xen_timer_callback(struct hrtimer *timer)
{
struct kvm_vcpu *vcpu = container_of(timer, struct kvm_vcpu,
arch.xen.timer);
struct kvm_xen_evtchn e;
int rc;
if (atomic_read(&vcpu->arch.xen.timer_pending))
return HRTIMER_NORESTART;
e.vcpu_id = vcpu->vcpu_id;
e.vcpu_idx = vcpu->vcpu_idx;
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;
}
atomic_inc(&vcpu->arch.xen.timer_pending);
kvm_make_request(KVM_REQ_UNBLOCK, vcpu);
kvm_vcpu_kick(vcpu);
return HRTIMER_NORESTART;
}
@@ -1785,171 +1785,190 @@ static void kvm_xen_check_poller(struct kvm_vcpu *vcpu, int port)
/*
* The return value from this function is propagated to kvm_set_irq() API,
* so it returns:
* < 0 Interrupt was ignored (masked or not delivered for other reasons)
* = 0 Interrupt was coalesced (previous irq is still pending)
* > 0 Number of CPUs interrupt was delivered to
*
* 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;
unsigned long *pending_bits, *mask_bits;
unsigned long flags;
int port_word_bit;
bool kick_vcpu = false;
int vcpu_idx, idx, rc;
vcpu_idx = READ_ONCE(xe->vcpu_idx);
if (vcpu_idx >= 0)
vcpu = kvm_get_vcpu(kvm, vcpu_idx);
else {
vcpu = kvm_get_vcpu_by_id(kvm, xe->vcpu_id);
if (!vcpu)
return -EINVAL;
WRITE_ONCE(xe->vcpu_idx, vcpu->vcpu_idx);
}
if (xe->port >= max_evtchn_port(kvm))
return -EINVAL;
rc = -EWOULDBLOCK;
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;
if (IS_ENABLED(CONFIG_64BIT) && kvm->arch.xen.long_mode) {
struct shared_info *shinfo = gpc->khva;
pending_bits = (unsigned long *)&shinfo->evtchn_pending;
mask_bits = (unsigned long *)&shinfo->evtchn_mask;
port_word_bit = xe->port / 64;
} else {
struct compat_shared_info *shinfo = gpc->khva;
pending_bits = (unsigned long *)&shinfo->evtchn_pending;
mask_bits = (unsigned long *)&shinfo->evtchn_mask;
port_word_bit = xe->port / 32;
}
/*
* If this port wasn't already set, and if it isn't masked, then
* we try to set the corresponding bit in the in-kernel shadow of
* evtchn_pending_sel for the target vCPU. And if *that* wasn't
* already set, then we kick the vCPU in question to write to the
* *real* evtchn_pending_sel in its own guest vcpu_info struct.
*/
if (test_and_set_bit(xe->port, pending_bits)) {
rc = 0; /* It was already raised */
} else if (test_bit(xe->port, mask_bits)) {
rc = -ENOTCONN; /* Masked */
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 */
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
* 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:
read_unlock_irqrestore(&gpc->lock, flags);
+ out_rcu_no_lock:
srcu_read_unlock(&kvm->srcu, idx);
if (kick_vcpu) {
kvm_make_request(KVM_REQ_UNBLOCK, vcpu);
kvm_vcpu_kick(vcpu);
}
return rc;
}
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;
if (current->mm != kvm->mm) {
/*
* If not on a thread which already belongs to this KVM,
* we'd better be in the irqfd workqueue.
*/
if (WARN_ON_ONCE(current->mm))
return -EINVAL;
kthread_use_mm(kvm->mm);
mm_borrowed = true;
}
/*
* It is theoretically possible for the page to be unmapped
* and the MMU notifier to invalidate the shared_info before
* we even get to use it. In that case, this looks like an
* infinite loop. It was tempting to do it via the userspace
* HVA instead... but that just *hides* the fact that it's
* an infinite loop, because if a fault occurs and it waits
* for the page to come back, it can *still* immediately
* fault and have to wait again, repeatedly.
*
* Conversely, the page could also have been reinstated by
* another thread before we even obtain the mutex above, so
* check again *first* before remapping it.
*/
do {
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;
idx = srcu_read_lock(&kvm->srcu);
rc = kvm_gpc_refresh(gpc, PAGE_SIZE);
srcu_read_unlock(&kvm->srcu, idx);
} while(!rc);
if (mm_borrowed)
kthread_unuse_mm(kvm->mm);
return rc;
}
/* This is the version called from kvm_set_irq() as the .set function */
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
@@ -25,21 +25,21 @@ int kvm_xen_vcpu_get_attr(struct kvm_vcpu *vcpu, struct kvm_xen_vcpu_attr *data)
int kvm_xen_hvm_set_attr(struct kvm *kvm, struct kvm_xen_hvm_attr *data);
int kvm_xen_hvm_get_attr(struct kvm *kvm, struct kvm_xen_hvm_attr *data);
int kvm_xen_hvm_evtchn_send(struct kvm *kvm, struct kvm_irq_routing_xen_evtchn *evt);
int kvm_xen_write_hypercall_page(struct kvm_vcpu *vcpu, u64 data);
int kvm_xen_hvm_config(struct kvm *kvm, struct kvm_xen_hvm_config *xhc);
void kvm_xen_init_vm(struct kvm *kvm);
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);
static inline void kvm_xen_sw_enable_lapic(struct kvm_vcpu *vcpu)
{
/*
* The local APIC is being enabled. If the per-vCPU upcall vector is
* set and the vCPU's evtchn_upcall_pending flag is set, inject the
* interrupt.
]
|
| 183/4 |
2026/08/12 12:05 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 34544
output: 60
thoughts: 114
Thoughts: **Reviewing the Changes**
Okay, so the diff appears to be spot-on. I've integrated the `in_atomic` parameter into `kvm_xen_set_evtchn_fast`, and I've also updated all the functions that call it. The implementation is what I expected; when `in_atomic` is set to true, we're using `read_trylock` as the preferred method, falling back to the slower path if we can't get the lock immediately. This pattern is consistent with how we handle similar scenarios in `kvm_xen_update_runstate_guest`, which is good. Now, just to be thorough and ensure I haven't missed anything, I'll quickly double-check that there aren't any other, unexpected callers of `kvm_xen_set_evtchn_fast` lurking in the code. Gotta be meticulous about these things.
|
| 184/4 |
2026/08/12 12:05 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:arch/x86/kvm/xen.c IncludeSnippetLines:0 Name:kvm_xen_set_evtchn_fast SourceTreePrefix:]
Results: map[References:[map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:kvm_arch_set_irq_inatomic SourceFile:arch/x86/kvm/irq.c SourceLine:270] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:kvm_xen_set_evtchn SourceFile:arch/x86/kvm/xen.c SourceLine:1906] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:kvm_xen_set_evtchn SourceFile:arch/x86/kvm/xen.c SourceLine:1940] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:xen_timer_callback SourceFile:arch/x86/kvm/xen.c SourceLine:140]] TruncatedOutput:false]
|
| 185/4 |
2026/08/12 12:05 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 35165
output: 44
thoughts: 462
Thoughts: **Analyzing Xen Event Channel Handling in KVM**
Okay, so I've just been reviewing the code related to event channel handling in KVM, specifically the Xen integration. I started by confirming that the caller updates were all correctly flagged. Specifically, `kvm_arch_set_irq_inatomic` is correctly passing `true` for atomic context, and `kvm_xen_set_evtchn` is correctly passing `false`, reflecting a non-atomic context. The `xen_timer_callback` is correctly passing `true`, as it executes within the hardirq context of an hrtimer callback. I double-checked these calls because the context is *crucial* to lock correctness.
My next focus was the logic within `kvm_xen_set_evtchn_fast`. I wanted to zero in on that second lock acquisition, and confirm that the handling of a failed trylock was correct. I walked through the code, and yes, it *does* align with my expectations. When `in_atomic` is set and the `read_trylock` fails, it does *exactly* what I'd expect. The code sets a bit to indicate a pending event and potentially kicks the vCPU, just like when the `kvm_gpc_check` fails. More importantly, it then branches to `out_rcu_no_lock`, which bypasses the `read_unlock_irqrestore(&gpc->lock, flags)` call, and proceeds with an `srcu_read_unlock`. This is precisely what the previous prompt implied, avoiding the attempted unlock.
Now, I'm going to take a look for any other instances where `gpc->lock` is acquired in atomic context within `arch/x86/kvm/xen.c`. Just a general sanity check at this point.
|
| 186/4 |
2026/08/12 12:06 |
tool |
grepper |
0m
Args: map[Expression:read_lock_irqsave.*gpc->lock PathPrefix:arch/x86/kvm/xen.c]
Results: map[Output:arch/x86/kvm/xen.c=153=static int xen_get_guest_pvclock(struct kvm_vcpu *vcpu,
--
arch/x86/kvm/xen.c-160-
arch/x86/kvm/xen.c:161: read_lock_irqsave(&gpc->lock, flags);
arch/x86/kvm/xen.c-162- while (!kvm_gpc_check(gpc, offset + sizeof(*hv_clock))) {
--
arch/x86/kvm/xen.c-168-
arch/x86/kvm/xen.c:169: read_lock_irqsave(&gpc->lock, flags);
arch/x86/kvm/xen.c-170- }
--
arch/x86/kvm/xen.c=639=void kvm_xen_inject_pending_events(struct kvm_vcpu *v)
--
arch/x86/kvm/xen.c-652- */
arch/x86/kvm/xen.c:653: read_lock_irqsave(&gpc->lock, flags);
arch/x86/kvm/xen.c-654- while (!kvm_gpc_check(gpc, sizeof(struct vcpu_info))) {
--
arch/x86/kvm/xen.c-659-
arch/x86/kvm/xen.c:660: read_lock_irqsave(&gpc->lock, flags);
arch/x86/kvm/xen.c-661- }
--
arch/x86/kvm/xen.c=697=int __kvm_xen_has_interrupt(struct kvm_vcpu *v)
--
arch/x86/kvm/xen.c-715-
arch/x86/kvm/xen.c:716: read_lock_irqsave(&gpc->lock, flags);
arch/x86/kvm/xen.c-717- while (!kvm_gpc_check(gpc, sizeof(struct vcpu_info))) {
--
arch/x86/kvm/xen.c-737- }
arch/x86/kvm/xen.c:738: read_lock_irqsave(&gpc->lock, flags);
arch/x86/kvm/xen.c-739- }
--
arch/x86/kvm/xen.c=1433=static bool wait_pending_event(struct kvm_vcpu *vcpu, int nr_ports,
--
arch/x86/kvm/xen.c-1443- idx = srcu_read_lock(&kvm->srcu);
arch/x86/kvm/xen.c:1444: read_lock_irqsave(&gpc->lock, flags);
arch/x86/kvm/xen.c-1445- if (!kvm_gpc_check(gpc, PAGE_SIZE))
--
arch/x86/kvm/xen.c=1795=int kvm_xen_set_evtchn_fast(struct kvm_xen_evtchn *xe, struct kvm *kvm)
--
arch/x86/kvm/xen.c-1821-
arch/x86/kvm/xen.c:1822: read_lock_irqsave(&gpc->lock, flags);
arch/x86/kvm/xen.c-1823- if (!kvm_gpc_check(gpc, PAGE_SIZE))
--
arch/x86/kvm/xen.c-1855-
arch/x86/kvm/xen.c:1856: read_lock_irqsave(&gpc->lock, flags);
arch/x86/kvm/xen.c-1857- if (!kvm_gpc_check(gpc, sizeof(struct vcpu_info))) {
]
|
| 187/4 |
2026/08/12 12:06 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 37100
output: 57
thoughts: 169
Thoughts: **My Current Investigation into Atomic Context Issues**
Okay, hold on a second. Didn't I *already* address that `kvm_xen_set_evtchn_fast` call? I'm pretty sure I switched it to use `read_trylock` for atomic context safety. The grepper output is still showing the old `read_lock_irqsave`, which is probably because it's running on the original source code, not the updated version. That explains the confusion.
Alright, let's refocus. I need to verify what *else* might be problematic. Let's make sure `xen_get_guest_pvclock`, `kvm_xen_inject_pending_events`, `__kvm_xen_has_interrupt`, and `wait_pending_event` are all safe in atomic context.
Hmmm, `xen_get_guest_pvclock`… it's called from `kvm_guest_time_update`, right? No, wait a minute, I need to double-check the call chain for that one. Let's go through it carefully.
|
| 188/4 |
2026/08/12 12:06 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:arch/x86/kvm/xen.c IncludeSnippetLines:0 Name:xen_get_guest_pvclock SourceTreePrefix:]
Results: map[References:[map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:kvm_xen_start_timer SourceFile:arch/x86/kvm/xen.c SourceLine:227] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:kvm_xen_start_timer SourceFile:arch/x86/kvm/xen.c SourceLine:230]] TruncatedOutput:false]
|
| 189/4 |
2026/08/12 12:06 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 37696
output: 46
thoughts: 153
Thoughts: **My KVM-Xen Conundrum**
Okay, so I'm digging into this KVM-Xen interaction, and I've got a couple of key functions flagged. First, `kvm_xen_start_timer`. It seems to be triggered by `kvm_xen_hvm_set_attr` and `kvm_xen_vcpu_set_attr`, both of which are ioctls, so we're talking process context here, that's straightforward.
Now, `kvm_xen_inject_pending_events` is the next one that has my attention, called from `vcpu_enter_guest` inside `arch/x86/kvm/x86.c`. *But*, `vcpu_enter_guest` disables preemption - right, a typical pattern to avoid unwanted interrupts - but is it truly in atomic context? That's what I'm trying to nail down. Since preemption is disabled, but hardirqs might be active, that doesn't definitively make this atomic context. And, wait, `vcpu_enter_guest` disables local IRQs too. So, yes, it's atomic context. I need to confirm that. Let me drill down into `kvm_xen_inject_pending_events` directly, to be sure.
|
| 190/4 |
2026/08/12 12:06 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:arch/x86/kvm/xen.c Name:kvm_xen_inject_pending_events]
Results: map[SourceCode: 632: /*
633: * On event channel delivery, the vcpu_info may not have been accessible.
634: * In that case, there are bits in vcpu->arch.xen.evtchn_pending_sel which
635: * need to be marked into the vcpu_info (and evtchn_upcall_pending set).
636: * Do so now that we can sleep in the context of the vCPU to bring the
637: * page in, and refresh the pfn cache for it.
638: */
639: void kvm_xen_inject_pending_events(struct kvm_vcpu *v)
640: {
641: unsigned long evtchn_pending_sel = READ_ONCE(v->arch.xen.evtchn_pending_sel);
642: struct gfn_to_pfn_cache *gpc = &v->arch.xen.vcpu_info_cache;
643: unsigned long flags;
644:
645: if (!evtchn_pending_sel)
646: return;
647:
648: /*
649: * Yes, this is an open-coded loop. But that's just what put_user()
650: * does anyway. Page it in and retry the instruction. We're just a
651: * little more honest about it.
652: */
653: read_lock_irqsave(&gpc->lock, flags);
654: while (!kvm_gpc_check(gpc, sizeof(struct vcpu_info))) {
655: read_unlock_irqrestore(&gpc->lock, flags);
656:
657: if (kvm_gpc_refresh(gpc, sizeof(struct vcpu_info)))
658: return;
659:
660: read_lock_irqsave(&gpc->lock, flags);
661: }
662:
663: /* Now gpc->khva is a valid kernel address for the vcpu_info */
664: if (IS_ENABLED(CONFIG_64BIT) && v->kvm->arch.xen.long_mode) {
665: struct vcpu_info *vi = gpc->khva;
666:
667: asm volatile(LOCK_PREFIX "orq %0, %1\n"
668: "notq %0\n"
669: LOCK_PREFIX "andq %0, %2\n"
670: : "=r" (evtchn_pending_sel),
671: "+m" (vi->evtchn_pending_sel),
672: "+m" (v->arch.xen.evtchn_pending_sel)
673: : "0" (evtchn_pending_sel));
674: WRITE_ONCE(vi->evtchn_upcall_pending, 1);
675: } else {
676: u32 evtchn_pending_sel32 = evtchn_pending_sel;
677: struct compat_vcpu_info *vi = gpc->khva;
678:
679: asm volatile(LOCK_PREFIX "orl %0, %1\n"
680: "notl %0\n"
681: LOCK_PREFIX "andl %0, %2\n"
682: : "=r" (evtchn_pending_sel32),
683: "+m" (vi->evtchn_pending_sel),
684: "+m" (v->arch.xen.evtchn_pending_sel)
685: : "0" (evtchn_pending_sel32));
686: WRITE_ONCE(vi->evtchn_upcall_pending, 1);
687: }
688:
689: kvm_gpc_mark_dirty_in_slot(gpc);
690: read_unlock_irqrestore(&gpc->lock, flags);
691:
692: /* For the per-vCPU lapic vector, deliver it as MSI. */
693: if (v->arch.xen.upcall_vector)
694: kvm_xen_inject_vcpu_vector(v);
695: }
SourceFile:arch/x86/kvm/xen.c]
|
| 191/4 |
2026/08/12 12:06 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 39402
output: 45
thoughts: 111
Thoughts: **Analyzing the Sleeping Context and Interrupt Handling**
Okay, so the comment indicates we're able to sleep within the `vCPU` context to bring in a page and refresh the `pfn` cache. That immediately got me thinking. The key point is that `kvm_xen_inject_pending_events` is called in a sleep-able context. Good, so we're not dealing with a hard interrupt here. Then I saw `read_lock_irqsave` being used, and remembered that `kvm_gpc_refresh` *can* sleep. That confirms it, the context isn't hardirq.
Now, the natural extension is to consider *how* we are going to use it... What about `__kvm_xen_has_interrupt`? It's the next logical step in the thought process - where does it come into the picture? What's its calling context? I need to understand if that function will also respect this new information. That has to be looked at to ensure consistency.
|
| 192/4 |
2026/08/12 12:06 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:arch/x86/kvm/xen.c Name:__kvm_xen_has_interrupt]
Results: map[SourceCode: 697: int __kvm_xen_has_interrupt(struct kvm_vcpu *v)
698: {
699: struct gfn_to_pfn_cache *gpc = &v->arch.xen.vcpu_info_cache;
700: unsigned long flags;
701: u8 rc = 0;
702:
703: /*
704: * If the global upcall vector (HVMIRQ_callback_vector) is set and
705: * the vCPU's evtchn_upcall_pending flag is set, the IRQ is pending.
706: */
707:
708: /* No need for compat handling here */
709: BUILD_BUG_ON(offsetof(struct vcpu_info, evtchn_upcall_pending) !=
710: offsetof(struct compat_vcpu_info, evtchn_upcall_pending));
711: BUILD_BUG_ON(sizeof(rc) !=
712: sizeof_field(struct vcpu_info, evtchn_upcall_pending));
713: BUILD_BUG_ON(sizeof(rc) !=
714: sizeof_field(struct compat_vcpu_info, evtchn_upcall_pending));
715:
716: read_lock_irqsave(&gpc->lock, flags);
717: while (!kvm_gpc_check(gpc, sizeof(struct vcpu_info))) {
718: read_unlock_irqrestore(&gpc->lock, flags);
719:
720: /*
721: * This function gets called from kvm_vcpu_block() after setting the
722: * task to TASK_INTERRUPTIBLE, to see if it needs to wake immediately
723: * from a HLT. So we really mustn't sleep. If the page ended up absent
724: * at that point, just return 1 in order to trigger an immediate wake,
725: * and we'll end up getting called again from a context where we *can*
726: * fault in the page and wait for it.
727: */
728: if (in_atomic() || !task_is_running(current))
729: return 1;
730:
731: if (kvm_gpc_refresh(gpc, sizeof(struct vcpu_info))) {
732: /*
733: * If this failed, userspace has screwed up the
734: * vcpu_info mapping. No interrupts for you.
735: */
736: return 0;
737: }
738: read_lock_irqsave(&gpc->lock, flags);
739: }
740:
741: rc = ((struct vcpu_info *)gpc->khva)->evtchn_upcall_pending;
742: read_unlock_irqrestore(&gpc->lock, flags);
743: return rc;
744: }
SourceFile:arch/x86/kvm/xen.c]
|
| 193/4 |
2026/08/12 12:06 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 40648
output: 58
thoughts: 134
Thoughts: **Investigating a Potential Lock Deprecation Issue in KVM-Xen Interrupt Handling**
Right, so I'm looking at `__kvm_xen_has_interrupt` and I'm seeing something that immediately sets off a red flag. It's using `read_lock_irqsave(&gpc->lock, flags);` and *then* checks `in_atomic()`. Now, hang on a second... if this function *is* actually called from an atomic context, that `read_lock_irqsave` on `gpc->lock` is going to trip the lockdep splat, especially on PREEMPT_RT! The issue is that `gpc->lock` is an `rwlock_t`, which, on PREEMPT_RT, translates to a sleeping lock. That means taking a sleeping lock in an atomic context is a big no-no.
So the million-dollar question is: is `__kvm_xen_has_interrupt` ever actually called in atomic context? That's what I need to figure out. I need to trace its callers. Let's dig into the call graph and see where this function is being used.
|
| 194/4 |
2026/08/12 12:06 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:arch/x86/kvm/xen.c IncludeSnippetLines:0 Name:__kvm_xen_has_interrupt SourceTreePrefix:]
Results: map[References:[map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:kvm_xen_has_interrupt SourceFile:arch/x86/kvm/xen.h SourceLine:87] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:kvm_xen_sw_enable_lapic SourceFile:arch/x86/kvm/xen.h SourceLine:49]] TruncatedOutput:false]
|
| 195/4 |
2026/08/12 12:06 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 41225
output: 57
thoughts: 16
Thoughts: Here's my thought process, summarized as you requested:
**Investigating `kvm_xen_has_interrupt` Callers**
Alright, let's dive in. My immediate thought is to trace the usage of `kvm_xen_has_interrupt`. I need to understand where this function is being called from. Knowing the callers will provide crucial context – it'll illuminate the specific parts of the KVM and Xen integration where this interrupt-handling logic is relevant. Understanding the "how" and "why" behind those calls is the first step in any deep analysis. Time to dig in and see what's using it.
|
| 196/4 |
2026/08/12 12:06 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:arch/x86/kvm/xen.h IncludeSnippetLines:0 Name:kvm_xen_has_interrupt SourceTreePrefix:]
Results: map[References:[map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:kvm_cpu_get_extint SourceFile:arch/x86/kvm/irq.c SourceLine:139] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:kvm_cpu_has_extint SourceFile:arch/x86/kvm/irq.c SourceLine:75] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:vcpu_enter_guest SourceFile:arch/x86/kvm/x86.c SourceLine:11284]] TruncatedOutput:false]
|
| 197/4 |
2026/08/12 12:06 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 41628
output: 44
thoughts: 202
Thoughts: **Analyzing Interrupt Handling in KVM/Xen Interaction**
Okay, so I'm digging into how interrupts are handled when KVM interacts with Xen, specifically looking at potential locking issues. I see that `kvm_cpu_has_extint` is called by both `kvm_cpu_has_interrupt` and `kvm_cpu_has_injectable_intr`. That's standard enough. But then things get interesting. I'm noting that `vcpu_enter_guest` calls `kvm_xen_has_interrupt`, and this call happens with preemption disabled, putting us in an atomic context!
Wait a minute, `vcpu_enter_guest` *also* disables local interrupts. Which means it is, in fact, an atomic context. This is important. If `__kvm_xen_has_interrupt` is called from *within* `vcpu_enter_guest`, it's going to try and acquire `gpc->lock` using `read_lock_irqsave`. And that, my friend, is a sleeping lock on PREEMPT_RT! A big red flag.
Okay, let's step back. I need to make sure I understand the context of the call. I see that `vcpu_enter_guest` is called from `vcpu_run`. `vcpu_run` generally runs in process context, but does it disable interrupts before calling `vcpu_enter_guest`? That's the crucial question. I need to clarify that execution path. Let's trace into `vcpu_enter_guest` to be certain.
|
| 198/4 |
2026/08/12 12:06 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:arch/x86/kvm/x86.c Name:vcpu_enter_guest]
Results: map[SourceCode:11106: /*
11107: * Called within kvm->srcu read side.
11108: * Returns 1 to let vcpu_run() continue the guest execution loop without
11109: * exiting to the userspace. Otherwise, the value will be returned to the
11110: * userspace.
11111: */
11112: static int vcpu_enter_guest(struct kvm_vcpu *vcpu)
11113: {
11114: int r;
11115: bool req_int_win =
11116: dm_request_for_irq_injection(vcpu) &&
11117: kvm_cpu_accept_dm_intr(vcpu);
11118: fastpath_t exit_fastpath;
11119: u64 run_flags, debug_ctl;
11120:
11121: bool req_immediate_exit = false;
11122:
11123: if (kvm_request_pending(vcpu)) {
11124: if (kvm_check_request(KVM_REQ_VM_DEAD, vcpu)) {
11125: r = -EIO;
11126: goto out;
11127: }
11128:
11129: if (kvm_dirty_ring_check_request(vcpu)) {
11130: r = 0;
11131: goto out;
11132: }
11133:
11134: if (kvm_check_request(KVM_REQ_GET_NESTED_STATE_PAGES, vcpu)) {
11135: if (unlikely(!kvm_x86_ops.nested_ops->get_nested_state_pages(vcpu))) {
11136: r = 0;
11137: goto out;
11138: }
11139: }
11140: if (kvm_check_request(KVM_REQ_MMU_FREE_OBSOLETE_ROOTS, vcpu))
11141: kvm_mmu_free_obsolete_roots(vcpu);
11142: if (kvm_check_request(KVM_REQ_MIGRATE_TIMER, vcpu))
11143: __kvm_migrate_timers(vcpu);
11144: if (kvm_check_request(KVM_REQ_MASTERCLOCK_UPDATE, vcpu))
11145: kvm_update_masterclock(vcpu->kvm);
11146: if (kvm_check_request(KVM_REQ_GLOBAL_CLOCK_UPDATE, vcpu))
11147: kvm_gen_kvmclock_update(vcpu);
11148: if (kvm_check_request(KVM_REQ_CLOCK_UPDATE, vcpu)) {
11149: r = kvm_guest_time_update(vcpu);
11150: if (unlikely(r))
11151: goto out;
11152: }
11153: if (kvm_check_request(KVM_REQ_MMU_SYNC, vcpu))
11154: kvm_mmu_sync_roots(vcpu);
11155: if (kvm_check_request(KVM_REQ_LOAD_MMU_PGD, vcpu))
11156: kvm_mmu_load_pgd(vcpu);
11157:
11158: /*
11159: * Note, the order matters here, as flushing "all" TLB entries
11160: * also flushes the "current" TLB entries, i.e. servicing the
11161: * flush "all" will clear any request to flush "current".
11162: */
11163: if (kvm_check_request(KVM_REQ_TLB_FLUSH, vcpu))
11164: kvm_vcpu_flush_tlb_all(vcpu);
11165:
11166: kvm_service_local_tlb_flush_requests(vcpu);
11167:
11168: /*
11169: * Fall back to a "full" guest flush if Hyper-V's precise
11170: * flushing fails. Note, Hyper-V's flushing is per-vCPU, but
11171: * the flushes are considered "remote" and not "local" because
11172: * the requests can be initiated from other vCPUs.
11173: */
11174: #ifdef CONFIG_KVM_HYPERV
11175: if (kvm_check_request(KVM_REQ_HV_TLB_FLUSH, vcpu) &&
11176: kvm_hv_vcpu_flush_tlb(vcpu))
11177: kvm_vcpu_flush_tlb_guest(vcpu);
11178: #endif
11179:
11180: if (kvm_check_request(KVM_REQ_REPORT_TPR_ACCESS, vcpu)) {
11181: vcpu->run->exit_reason = KVM_EXIT_TPR_ACCESS;
11182: r = 0;
11183: goto out;
11184: }
11185: if (kvm_test_request(KVM_REQ_TRIPLE_FAULT, vcpu)) {
11186: if (is_guest_mode(vcpu))
11187: kvm_x86_ops.nested_ops->triple_fault(vcpu);
11188:
11189: if (kvm_check_request(KVM_REQ_TRIPLE_FAULT, vcpu)) {
11190: vcpu->run->exit_reason = KVM_EXIT_SHUTDOWN;
11191: vcpu->mmio_needed = 0;
11192: r = 0;
11193: goto out;
11194: }
11195: }
11196: if (kvm_check_request(KVM_REQ_APF_HALT, vcpu)) {
11197: /* Page is swapped out. Do synthetic halt */
11198: vcpu->arch.apf.halted = true;
11199: r = 1;
11200: goto out;
11201: }
11202: if (kvm_check_request(KVM_REQ_STEAL_UPDATE, vcpu))
11203: record_steal_time(vcpu);
11204: if (kvm_check_request(KVM_REQ_PMU, vcpu))
11205: kvm_pmu_handle_event(vcpu);
11206: if (kvm_check_request(KVM_REQ_PMI, vcpu))
11207: kvm_pmu_deliver_pmi(vcpu);
11208: #ifdef CONFIG_KVM_SMM
11209: if (kvm_check_request(KVM_REQ_SMI, vcpu))
11210: process_smi(vcpu);
11211: #endif
11212: if (kvm_check_request(KVM_REQ_NMI, vcpu))
11213: process_nmi(vcpu);
11214: if (kvm_check_request(KVM_REQ_IOAPIC_EOI_EXIT, vcpu)) {
11215: BUG_ON(vcpu->arch.pending_ioapic_eoi > 255);
11216: if (test_bit(vcpu->arch.pending_ioapic_eoi,
11217: vcpu->arch.ioapic_handled_vectors)) {
11218: vcpu->run->exit_reason = KVM_EXIT_IOAPIC_EOI;
11219: vcpu->run->eoi.vector =
11220: vcpu->arch.pending_ioapic_eoi;
11221: r = 0;
11222: goto out;
11223: }
11224: }
11225: if (kvm_check_request(KVM_REQ_SCAN_IOAPIC, vcpu))
11226: vcpu_scan_ioapic(vcpu);
11227: if (kvm_check_request(KVM_REQ_LOAD_EOI_EXITMAP, vcpu))
11228: vcpu_load_eoi_exitmap(vcpu);
11229: if (kvm_check_request(KVM_REQ_APIC_PAGE_RELOAD, vcpu))
11230: kvm_vcpu_reload_apic_access_page(vcpu);
11231: #ifdef CONFIG_KVM_HYPERV
11232: if (kvm_check_request(KVM_REQ_HV_CRASH, vcpu)) {
11233: vcpu->run->exit_reason = KVM_EXIT_SYSTEM_EVENT;
11234: vcpu->run->system_event.type = KVM_SYSTEM_EVENT_CRASH;
11235: vcpu->run->system_event.ndata = 0;
11236: r = 0;
11237: goto out;
11238: }
11239: if (kvm_check_request(KVM_REQ_HV_RESET, vcpu)) {
11240: vcpu->run->exit_reason = KVM_EXIT_SYSTEM_EVENT;
11241: vcpu->run->system_event.type = KVM_SYSTEM_EVENT_RESET;
11242: vcpu->run->system_event.ndata = 0;
11243: r = 0;
11244: goto out;
11245: }
11246: if (kvm_check_request(KVM_REQ_HV_EXIT, vcpu)) {
11247: struct kvm_vcpu_hv *hv_vcpu = to_hv_vcpu(vcpu);
11248:
11249: vcpu->run->exit_reason = KVM_EXIT_HYPERV;
11250: vcpu->run->hyperv = hv_vcpu->exit;
11251: r = 0;
11252: goto out;
11253: }
11254:
11255: /*
11256: * KVM_REQ_HV_STIMER has to be processed after
11257: * KVM_REQ_CLOCK_UPDATE, because Hyper-V SynIC timers
11258: * depend on the guest clock being up-to-date
11259: */
11260: if (kvm_check_request(KVM_REQ_HV_STIMER, vcpu))
11261: kvm_hv_process_stimers(vcpu);
11262: #endif
11263: if (kvm_check_request(KVM_REQ_APICV_UPDATE, vcpu))
11264: kvm_vcpu_update_apicv(vcpu);
11265: if (kvm_check_request(KVM_REQ_APF_READY, vcpu))
11266: kvm_check_async_pf_completion(vcpu);
11267:
11268: if (kvm_check_request(KVM_REQ_RECALC_INTERCEPTS, vcpu))
11269: kvm_x86_call(recalc_intercepts)(vcpu);
11270:
11271: if (kvm_check_request(KVM_REQ_UPDATE_CPU_DIRTY_LOGGING, vcpu))
11272: kvm_x86_call(update_cpu_dirty_logging)(vcpu);
11273:
11274: if (kvm_check_request(KVM_REQ_UPDATE_PROTECTED_GUEST_STATE, vcpu)) {
11275: kvm_vcpu_reset(vcpu, true);
11276: if (vcpu->arch.mp_state != KVM_MP_STATE_RUNNABLE) {
11277: r = 1;
11278: goto out;
11279: }
11280: }
11281: }
11282:
11283: if (kvm_check_request(KVM_REQ_EVENT, vcpu) || req_int_win ||
11284: kvm_xen_has_interrupt(vcpu)) {
11285: ++vcpu->stat.req_event;
11286: r = kvm_apic_accept_events(vcpu);
11287: if (r < 0) {
11288: r = 0;
11289: goto out;
11290: }
11291: if (vcpu->arch.mp_state == KVM_MP_STATE_INIT_RECEIVED) {
11292: r = 1;
11293: goto out;
11294: }
11295:
11296: r = kvm_check_and_inject_events(vcpu, &req_immediate_exit);
11297: if (r < 0) {
11298: r = 0;
11299: goto out;
11300: }
11301: if (req_int_win)
11302: kvm_x86_call(enable_irq_window)(vcpu);
11303:
11304: if (kvm_lapic_enabled(vcpu)) {
11305: kvm_lapic_update_cr8_intercept(vcpu);
11306: kvm_lapic_sync_to_vapic(vcpu);
11307: }
11308: }
11309:
11310: r = kvm_mmu_reload(vcpu);
11311: if (unlikely(r)) {
11312: goto cancel_injection;
11313: }
11314:
11315: preempt_disable();
11316:
11317: kvm_x86_call(prepare_switch_to_guest)(vcpu);
11318:
11319: /*
11320: * Disable IRQs before setting IN_GUEST_MODE. Posted interrupt
11321: * IPI are then delayed after guest entry, which ensures that they
11322: * result in virtual interrupt delivery.
11323: */
11324: local_irq_disable();
11325:
11326: /* Store vcpu->apicv_active before vcpu->mode. */
11327: smp_store_release(&vcpu->mode, IN_GUEST_MODE);
11328:
11329: kvm_vcpu_srcu_read_unlock(vcpu);
11330:
11331: /*
11332: * 1) We should set ->mode before checking ->requests. Please see
11333: * the comment in kvm_vcpu_exiting_guest_mode().
11334: *
11335: * 2) For APICv, we should set ->mode before checking PID.ON. This
11336: * pairs with the memory barrier implicit in pi_test_and_set_on
11337: * (see vmx_deliver_posted_interrupt).
11338: *
11339: * 3) This also orders the write to mode from any reads to the page
11340: * tables done while the VCPU is running. Please see the comment
11341: * in kvm_flush_remote_tlbs.
11342: */
11343: smp_mb__after_srcu_read_unlock();
11344:
11345: /*
11346: * Process pending posted interrupts to handle the case where the
11347: * notification IRQ arrived in the host, or was never sent (because the
11348: * target vCPU wasn't running). Do this regardless of the vCPU's APICv
11349: * status, KVM doesn't update assigned devices when APICv is inhibited,
11350: * i.e. they can post interrupts even if APICv is temporarily disabled.
11351: */
11352: if (kvm_lapic_enabled(vcpu))
11353: kvm_x86_call(sync_pir_to_irr)(vcpu);
11354:
11355: if (kvm_vcpu_exit_request(vcpu)) {
11356: vcpu->mode = OUTSIDE_GUEST_MODE;
11357: smp_wmb();
11358: local_irq_enable();
11359: preempt_enable();
11360: kvm_vcpu_srcu_read_lock(vcpu);
11361: r = 1;
11362: goto cancel_injection;
11363: }
11364:
11365: run_flags = 0;
11366: if (req_immediate_exit) {
11367: run_flags |= KVM_RUN_FORCE_IMMEDIATE_EXIT;
11368: kvm_make_request(KVM_REQ_EVENT, vcpu);
11369: }
11370:
11371: fpregs_assert_state_consistent();
11372: if (test_thread_flag(TIF_NEED_FPU_LOAD))
11373: switch_fpu_return();
11374:
11375: if (vcpu->arch.guest_fpu.xfd_err)
11376: wrmsrq(MSR_IA32_XFD_ERR, vcpu->arch.guest_fpu.xfd_err);
11377:
11378: kvm_load_xfeatures(vcpu, true);
11379:
11380: if (unlikely(vcpu->arch.switch_db_regs &&
11381: !(vcpu->arch.switch_db_regs & KVM_DEBUGREG_AUTO_SWITCH))) {
11382: set_debugreg(DR7_FIXED_1, 7);
11383: set_debugreg(vcpu->arch.eff_db[0], 0);
11384: set_debugreg(vcpu->arch.eff_db[1], 1);
11385: set_debugreg(vcpu->arch.eff_db[2], 2);
11386: set_debugreg(vcpu->arch.eff_db[3], 3);
11387: /* When KVM_DEBUGREG_WONT_EXIT, dr6 is accessible in guest. */
11388: if (unlikely(vcpu->arch.switch_db_regs & KVM_DEBUGREG_WONT_EXIT))
11389: run_flags |= KVM_RUN_LOAD_GUEST_DR6;
11390: } else if (unlikely(hw_breakpoint_active())) {
11391: set_debugreg(DR7_FIXED_1, 7);
11392: }
11393:
11394: /*
11395: * Refresh the host DEBUGCTL snapshot after disabling IRQs, as DEBUGCTL
11396: * can be modified in IRQ context, e.g. via SMP function calls. Inform
11397: * vendor code if any host-owned bits were changed, e.g. so that the
11398: * value loaded into hardware while running the guest can be updated.
11399: */
11400: debug_ctl = get_debugctlmsr();
11401: if ((debug_ctl ^ vcpu->arch.host_debugctl) & kvm_x86_ops.HOST_OWNED_DEBUGCTL &&
11402: !vcpu->arch.guest_state_protected)
11403: run_flags |= KVM_RUN_LOAD_DEBUGCTL;
11404: vcpu->arch.host_debugctl = debug_ctl;
11405:
11406: kvm_mediated_pmu_load(vcpu);
11407:
11408: guest_timing_enter_irqoff();
11409:
11410: /*
11411: * Swap PKRU with hardware breakpoints disabled to minimize the number
11412: * of flows where non-KVM code can run with guest state loaded.
11413: */
11414: kvm_load_guest_pkru(vcpu);
11415:
11416: for (;;) {
11417: /*
11418: * Assert that vCPU vs. VM APICv state is consistent. An APICv
11419: * update must kick and wait for all vCPUs before toggling the
11420: * per-VM state, and responding vCPUs must wait for the update
11421: * to complete before servicing KVM_REQ_APICV_UPDATE.
11422: */
11423: WARN_ON_ONCE((kvm_vcpu_apicv_activated(vcpu) != kvm_vcpu_apicv_active(vcpu)) &&
11424: (kvm_get_apic_mode(vcpu) != LAPIC_MODE_DISABLED));
11425:
11426: exit_fastpath = kvm_x86_call(vcpu_run)(vcpu, run_flags);
11427: if (likely(exit_fastpath != EXIT_FASTPATH_REENTER_GUEST))
11428: break;
11429:
11430: if (kvm_lapic_enabled(vcpu))
11431: kvm_x86_call(sync_pir_to_irr)(vcpu);
11432:
11433: if (unlikely(kvm_vcpu_exit_request(vcpu))) {
11434: exit_fastpath = EXIT_FASTPATH_EXIT_HANDLED;
11435: break;
11436: }
11437:
11438: run_flags = 0;
11439:
11440: /* Note, VM-Exits that go down the "slow" path are accounted below. */
11441: ++vcpu->stat.exits;
11442: }
11443:
11444: kvm_load_host_pkru(vcpu);
11445:
11446: kvm_mediated_pmu_put(vcpu);
11447:
11448: /*
11449: * Do this here before restoring debug registers on the host. And
11450: * since we do this before handling the vmexit, a DR access vmexit
11451: * can (a) read the correct value of the debug registers, (b) set
11452: * KVM_DEBUGREG_WONT_EXIT again.
11453: */
11454: if (unlikely(vcpu->arch.switch_db_regs & KVM_DEBUGREG_WONT_EXIT)) {
11455: WARN_ON(vcpu->guest_debug & KVM_GUESTDBG_USE_HW_BP);
11456: WARN_ON(vcpu->arch.switch_db_regs & KVM_DEBUGREG_AUTO_SWITCH);
11457: kvm_x86_call(sync_dirty_debug_regs)(vcpu);
11458: kvm_update_dr0123(vcpu);
11459: kvm_update_dr7(vcpu);
11460: }
11461:
11462: /*
11463: * If the guest has used debug registers, at least dr7
11464: * will be disabled while returning to the host.
11465: * If we don't have active breakpoints in the host, we don't
11466: * care about the messed up debug address registers. But if
11467: * we have some of them active, restore the old state.
11468: */
11469: if (hw_breakpoint_active())
11470: hw_breakpoint_restore();
11471:
11472: vcpu->arch.last_vmentry_cpu = vcpu->cpu;
11473: vcpu->arch.last_guest_tsc = kvm_read_l1_tsc(vcpu, rdtsc());
11474:
11475: vcpu->mode = OUTSIDE_GUEST_MODE;
11476: smp_wmb();
11477:
11478: kvm_load_xfeatures(vcpu, false);
11479:
11480: /*
11481: * Sync xfd before calling handle_exit_irqoff() which may
11482: * rely on the fact that guest_fpu::xfd is up-to-date (e.g.
11483: * in #NM irqoff handler).
11484: */
11485: if (vcpu->arch.xfd_no_write_intercept)
11486: fpu_sync_guest_vmexit_xfd_state();
11487:
11488: kvm_x86_call(handle_exit_irqoff)(vcpu);
11489:
11490: if (vcpu->arch.guest_fpu.xfd_err)
11491: wrmsrq(MSR_IA32_XFD_ERR, 0);
11492:
11493: /*
11494: * Mark this CPU as needing a branch predictor flush before running
11495: * userspace. Must be done before enabling preemption to ensure it gets
11496: * set for the CPU that actually ran the guest, and not the CPU that it
11497: * may migrate to.
11498: */
11499: if (cpu_feature_enabled(X86_FEATURE_IBPB_EXIT_TO_USER))
11500: this_cpu_write(x86_ibpb_exit_to_user, true);
11501:
11502: /*
11503: * Consume any pending interrupts, including the possible source of
11504: * VM-Exit on SVM and any ticks that occur between VM-Exit and now.
11505: * An instruction is required after local_irq_enable() to fully unblock
11506: * interrupts on processors that implement an interrupt shadow, the
11507: * stat.exits increment will do nicely.
11508: */
11509: kvm_before_interrupt(vcpu, KVM_HANDLING_IRQ);
11510: local_irq_enable();
11511: ++vcpu->stat.exits;
11512: local_irq_disable();
11513: kvm_after_interrupt(vcpu);
11514:
11515: /*
11516: * Wait until after servicing IRQs to account guest time so that any
11517: * ticks that occurred while running the guest are properly accounted
11518: * to the guest. Waiting until IRQs are enabled degrades the accuracy
11519: * of accounting via context tracking, but the loss of accuracy is
11520: * acceptable for all known use cases.
11521: */
11522: guest_timing_exit_irqoff();
11523:
11524: local_irq_enable();
11525: preempt_enable();
11526:
11527: kvm_vcpu_srcu_read_lock(vcpu);
11528:
11529: /*
11530: * Call this to ensure WC buffers in guest are evicted after each VM
11531: * Exit, so that the evicted WC writes can be snooped across all cpus
11532: */
11533: smp_mb__after_srcu_read_lock();
11534:
11535: /*
11536: * Profile KVM exit RIPs:
11537: */
11538: if (unlikely(prof_on == KVM_PROFILING &&
11539: !vcpu->arch.guest_state_protected)) {
11540: unsigned long rip = kvm_rip_read(vcpu);
11541: profile_hit(KVM_PROFILING, (void *)rip);
11542: }
11543:
11544: if (unlikely(vcpu->arch.tsc_always_catchup))
11545: kvm_make_request(KVM_REQ_CLOCK_UPDATE, vcpu);
11546:
11547: if (vcpu->arch.apic_attention)
11548: kvm_lapic_sync_from_vapic(vcpu);
11549:
11550: r = kvm_x86_call(handle_exit)(vcpu, exit_fastpath);
11551: return r;
11552:
11553: cancel_injection:
11554: if (req_immediate_exit)
11555: kvm_make_request(KVM_REQ_EVENT, vcpu);
11556: kvm_x86_call(cancel_injection)(vcpu);
11557: if (unlikely(vcpu->arch.apic_attention))
11558: kvm_lapic_sync_from_vapic(vcpu);
11559: out:
11560: return r;
11561: }
SourceFile:arch/x86/kvm/x86.c]
|
| 199/4 |
2026/08/12 12:06 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 50656
output: 41
thoughts: 301
Thoughts: **Analyzing the Safety of `kvm_xen_has_interrupt`**
Okay, let's break down this call to `kvm_xen_has_interrupt` within `vcpu_enter_guest`. Looking at line 11284, the code checks for pending events, and critically, `kvm_xen_has_interrupt` is called *before* `preempt_disable()` at line 11315. That immediately tells me we're *not* in atomic context at this point. That's good to know.
But wait, I need to confirm that `kvm_xen_has_interrupt` itself is safe. Examining `__kvm_xen_has_interrupt` directly, I see it explicitly checks for `in_atomic() || !task_is_running(current)`. Hmm, now the pieces are coming together. This function is also called from within `kvm_vcpu_block`, through the call chain `kvm_arch_vcpu_runnable` -> `kvm_vcpu_has_events` -> `kvm_cpu_has_extint` -> `kvm_xen_has_interrupt`.
Ah, right! In `kvm_vcpu_block`, the task state is set to `TASK_INTERRUPTIBLE`. That means `!task_is_running(current)` *will* be true. And because of the check in `__kvm_xen_has_interrupt`, it returns 1 without taking a lock in that case. Excellent. So, it's designed to avoid locking when we can't sleep, or when we are in an atomic context, which makes it safe to use at that point. That confirms my understanding: the code cleverly sidesteps locking when the context dictates it.
The only question now is: What about `wait_pending_event`?
|
| 200/4 |
2026/08/12 12:06 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:arch/x86/kvm/xen.c Name:wait_pending_event]
Results: map[SourceCode:1433: static bool wait_pending_event(struct kvm_vcpu *vcpu, int nr_ports,
1434: evtchn_port_t *ports)
1435: {
1436: struct kvm *kvm = vcpu->kvm;
1437: struct gfn_to_pfn_cache *gpc = &kvm->arch.xen.shinfo_cache;
1438: unsigned long *pending_bits;
1439: unsigned long flags;
1440: bool ret = true;
1441: int idx, i;
1442:
1443: idx = srcu_read_lock(&kvm->srcu);
1444: read_lock_irqsave(&gpc->lock, flags);
1445: if (!kvm_gpc_check(gpc, PAGE_SIZE))
1446: goto out_rcu;
1447:
1448: ret = false;
1449: if (IS_ENABLED(CONFIG_64BIT) && kvm->arch.xen.long_mode) {
1450: struct shared_info *shinfo = gpc->khva;
1451: pending_bits = (unsigned long *)&shinfo->evtchn_pending;
1452: } else {
1453: struct compat_shared_info *shinfo = gpc->khva;
1454: pending_bits = (unsigned long *)&shinfo->evtchn_pending;
1455: }
1456:
1457: for (i = 0; i < nr_ports; i++) {
1458: if (test_bit(ports[i], pending_bits)) {
1459: ret = true;
1460: break;
1461: }
1462: }
1463:
1464: out_rcu:
1465: read_unlock_irqrestore(&gpc->lock, flags);
1466: srcu_read_unlock(&kvm->srcu, idx);
1467:
1468: return ret;
1469: }
SourceFile:arch/x86/kvm/xen.c]
|
| 201/4 |
2026/08/12 12:06 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 52016
output: 47
thoughts: 89
Thoughts: Okay, let me think this through.
**Analyzing the KVM-Xen Scheduling Context**
Alright, so `wait_pending_event` gets called by `kvm_xen_schedop_poll`. No surprises there. And `kvm_xen_schedop_poll`, as I recall, is invoked from `kvm_xen_hypercall`, which operates within the process context. So, in that particular call chain, everything seems to be perfectly safe as far as context goes. We're good.
Now, that brings up the other function, `kvm_xen_update_runstate_guest`. The prompt specifically flagged this one because of the use of `read_trylock` within an "atomic context," which is certainly something to pay close attention to.
|
| 202/4 |
2026/08/12 12:07 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:arch/x86/kvm/xen.c Name:kvm_xen_update_runstate_guest]
Results: map[SourceCode: 321: static void kvm_xen_update_runstate_guest(struct kvm_vcpu *v, bool atomic)
322: {
323: struct kvm_vcpu_xen *vx = &v->arch.xen;
324: struct gfn_to_pfn_cache *gpc1 = &vx->runstate_cache;
325: struct gfn_to_pfn_cache *gpc2 = &vx->runstate2_cache;
326: size_t user_len, user_len1, user_len2;
327: struct vcpu_runstate_info rs;
328: unsigned long flags;
329: size_t times_ofs;
330: uint8_t *update_bit = NULL;
331: uint64_t entry_time;
332: uint64_t *rs_times;
333: int *rs_state;
334:
335: /*
336: * The only difference between 32-bit and 64-bit versions of the
337: * runstate struct is the alignment of uint64_t in 32-bit, which
338: * means that the 64-bit version has an additional 4 bytes of
339: * padding after the first field 'state'. Let's be really really
340: * paranoid about that, and matching it with our internal data
341: * structures that we memcpy into it...
342: */
343: BUILD_BUG_ON(offsetof(struct vcpu_runstate_info, state) != 0);
344: BUILD_BUG_ON(offsetof(struct compat_vcpu_runstate_info, state) != 0);
345: BUILD_BUG_ON(sizeof(struct compat_vcpu_runstate_info) != 0x2c);
346: #ifdef CONFIG_X86_64
347: /*
348: * The 64-bit structure has 4 bytes of padding before 'state_entry_time'
349: * so each subsequent field is shifted by 4, and it's 4 bytes longer.
350: */
351: BUILD_BUG_ON(offsetof(struct vcpu_runstate_info, state_entry_time) !=
352: offsetof(struct compat_vcpu_runstate_info, state_entry_time) + 4);
353: BUILD_BUG_ON(offsetof(struct vcpu_runstate_info, time) !=
354: offsetof(struct compat_vcpu_runstate_info, time) + 4);
355: BUILD_BUG_ON(sizeof(struct vcpu_runstate_info) != 0x2c + 4);
356: #endif
357: /*
358: * The state field is in the same place at the start of both structs,
359: * and is the same size (int) as vx->current_runstate.
360: */
361: BUILD_BUG_ON(offsetof(struct vcpu_runstate_info, state) !=
362: offsetof(struct compat_vcpu_runstate_info, state));
363: BUILD_BUG_ON(sizeof_field(struct vcpu_runstate_info, state) !=
364: sizeof(vx->current_runstate));
365: BUILD_BUG_ON(sizeof_field(struct compat_vcpu_runstate_info, state) !=
366: sizeof(vx->current_runstate));
367:
368: /*
369: * The state_entry_time field is 64 bits in both versions, and the
370: * XEN_RUNSTATE_UPDATE flag is in the top bit, which given that x86
371: * is little-endian means that it's in the last *byte* of the word.
372: * That detail is important later.
373: */
374: BUILD_BUG_ON(sizeof_field(struct vcpu_runstate_info, state_entry_time) !=
375: sizeof(uint64_t));
376: BUILD_BUG_ON(sizeof_field(struct compat_vcpu_runstate_info, state_entry_time) !=
377: sizeof(uint64_t));
378: BUILD_BUG_ON((XEN_RUNSTATE_UPDATE >> 56) != 0x80);
379:
380: /*
381: * The time array is four 64-bit quantities in both versions, matching
382: * the vx->runstate_times and immediately following state_entry_time.
383: */
384: BUILD_BUG_ON(offsetof(struct vcpu_runstate_info, state_entry_time) !=
385: offsetof(struct vcpu_runstate_info, time) - sizeof(uint64_t));
386: BUILD_BUG_ON(offsetof(struct compat_vcpu_runstate_info, state_entry_time) !=
387: offsetof(struct compat_vcpu_runstate_info, time) - sizeof(uint64_t));
388: BUILD_BUG_ON(sizeof_field(struct vcpu_runstate_info, time) !=
389: sizeof_field(struct compat_vcpu_runstate_info, time));
390: BUILD_BUG_ON(sizeof_field(struct vcpu_runstate_info, time) !=
391: sizeof(vx->runstate_times));
392:
393: if (IS_ENABLED(CONFIG_64BIT) && v->kvm->arch.xen.long_mode) {
394: user_len = sizeof(struct vcpu_runstate_info);
395: times_ofs = offsetof(struct vcpu_runstate_info,
396: state_entry_time);
397: } else {
398: user_len = sizeof(struct compat_vcpu_runstate_info);
399: times_ofs = offsetof(struct compat_vcpu_runstate_info,
400: state_entry_time);
401: }
402:
403: /*
404: * There are basically no alignment constraints. The guest can set it
405: * up so it crosses from one page to the next, and at arbitrary byte
406: * alignment (and the 32-bit ABI doesn't align the 64-bit integers
407: * anyway, even if the overall struct had been 64-bit aligned).
408: */
409: if ((gpc1->gpa & ~PAGE_MASK) + user_len >= PAGE_SIZE) {
410: user_len1 = PAGE_SIZE - (gpc1->gpa & ~PAGE_MASK);
411: user_len2 = user_len - user_len1;
412: } else {
413: user_len1 = user_len;
414: user_len2 = 0;
415: }
416: BUG_ON(user_len1 + user_len2 != user_len);
417:
418: retry:
419: /*
420: * Attempt to obtain the GPC lock on *both* (if there are two)
421: * gfn_to_pfn caches that cover the region.
422: */
423: if (atomic) {
424: local_irq_save(flags);
425: if (!read_trylock(&gpc1->lock)) {
426: local_irq_restore(flags);
427: return;
428: }
429: } else {
430: read_lock_irqsave(&gpc1->lock, flags);
431: }
432: while (!kvm_gpc_check(gpc1, user_len1)) {
433: read_unlock_irqrestore(&gpc1->lock, flags);
434:
435: /* When invoked from kvm_sched_out() we cannot sleep */
436: if (atomic)
437: return;
438:
439: if (kvm_gpc_refresh(gpc1, user_len1))
440: return;
441:
442: read_lock_irqsave(&gpc1->lock, flags);
443: }
444:
445: if (likely(!user_len2)) {
446: /*
447: * Set up three pointers directly to the runstate_info
448: * struct in the guest (via the GPC).
449: *
450: * • @rs_state → state field
451: * • @rs_times → state_entry_time field.
452: * • @update_bit → last byte of state_entry_time, which
453: * contains the XEN_RUNSTATE_UPDATE bit.
454: */
455: rs_state = gpc1->khva;
456: rs_times = gpc1->khva + times_ofs;
457: if (v->kvm->arch.xen.runstate_update_flag)
458: update_bit = ((void *)(&rs_times[1])) - 1;
459: } else {
460: /*
461: * The guest's runstate_info is split across two pages and we
462: * need to hold and validate both GPCs simultaneously. We can
463: * declare a lock ordering GPC1 > GPC2 because nothing else
464: * takes them more than one at a time. Set a subclass on the
465: * gpc1 lock to make lockdep shut up about it.
466: */
467: lock_set_subclass(&gpc1->lock.dep_map, 1, _THIS_IP_);
468: if (atomic) {
469: if (!read_trylock(&gpc2->lock)) {
470: read_unlock_irqrestore(&gpc1->lock, flags);
471: return;
472: }
473: } else {
474: read_lock(&gpc2->lock);
475: }
476:
477: if (!kvm_gpc_check(gpc2, user_len2)) {
478: read_unlock(&gpc2->lock);
479: read_unlock_irqrestore(&gpc1->lock, flags);
480:
481: /* When invoked from kvm_sched_out() we cannot sleep */
482: if (atomic)
483: return;
484:
485: /*
486: * Use kvm_gpc_activate() here because if the runstate
487: * area was configured in 32-bit mode and only extends
488: * to the second page now because the guest changed to
489: * 64-bit mode, the second GPC won't have been set up.
490: */
491: if (kvm_gpc_activate(gpc2, gpc1->gpa + user_len1,
492: user_len2))
493: return;
494:
495: /*
496: * We dropped the lock on GPC1 so we have to go all the
497: * way back and revalidate that too.
498: */
499: goto retry;
500: }
501:
502: /*
503: * In this case, the runstate_info struct will be assembled on
504: * the kernel stack (compat or not as appropriate) and will
505: * be copied to GPC1/GPC2 with a dual memcpy. Set up the three
506: * rs pointers accordingly.
507: */
508: rs_times = &rs.state_entry_time;
509:
510: /*
511: * The rs_state pointer points to the start of what we'll
512: * copy to the guest, which in the case of a compat guest
513: * is the 32-bit field that the compiler thinks is padding.
514: */
515: rs_state = ((void *)rs_times) - times_ofs;
516:
517: /*
518: * The update_bit is still directly in the guest memory,
519: * via one GPC or the other.
520: */
521: if (v->kvm->arch.xen.runstate_update_flag) {
522: if (user_len1 >= times_ofs + sizeof(uint64_t))
523: update_bit = gpc1->khva + times_ofs +
524: sizeof(uint64_t) - 1;
525: else
526: update_bit = gpc2->khva + times_ofs +
527: sizeof(uint64_t) - 1 - user_len1;
528: }
529:
530: #ifdef CONFIG_X86_64
531: /*
532: * Don't leak kernel memory through the padding in the 64-bit
533: * version of the struct.
534: */
535: memset(&rs, 0, offsetof(struct vcpu_runstate_info, state_entry_time));
536: #endif
537: }
538:
539: /*
540: * First, set the XEN_RUNSTATE_UPDATE bit in the top bit of the
541: * state_entry_time field, directly in the guest. We need to set
542: * that (and write-barrier) before writing to the rest of the
543: * structure, and clear it last. Just as Xen does, we address the
544: * single *byte* in which it resides because it might be in a
545: * different cache line to the rest of the 64-bit word, due to
546: * the (lack of) alignment constraints.
547: */
548: entry_time = vx->runstate_entry_time;
549: if (update_bit) {
550: entry_time |= XEN_RUNSTATE_UPDATE;
551: *update_bit = (vx->runstate_entry_time | XEN_RUNSTATE_UPDATE) >> 56;
552: smp_wmb();
553: }
554:
555: /*
556: * Now assemble the actual structure, either on our kernel stack
557: * or directly in the guest according to how the rs_state and
558: * rs_times pointers were set up above.
559: */
560: *rs_state = vx->current_runstate;
561: rs_times[0] = entry_time;
562: memcpy(rs_times + 1, vx->runstate_times, sizeof(vx->runstate_times));
563:
564: /* For the split case, we have to then copy it to the guest. */
565: if (user_len2) {
566: memcpy(gpc1->khva, rs_state, user_len1);
567: memcpy(gpc2->khva, ((void *)rs_state) + user_len1, user_len2);
568: }
569: smp_wmb();
570:
571: /* Finally, clear the XEN_RUNSTATE_UPDATE bit. */
572: if (update_bit) {
573: entry_time &= ~XEN_RUNSTATE_UPDATE;
574: *update_bit = entry_time >> 56;
575: smp_wmb();
576: }
577:
578: if (user_len2) {
579: kvm_gpc_mark_dirty_in_slot(gpc2);
580: read_unlock(&gpc2->lock);
581: }
582:
583: kvm_gpc_mark_dirty_in_slot(gpc1);
584: read_unlock_irqrestore(&gpc1->lock, flags);
585: }
SourceFile:arch/x86/kvm/xen.c]
|
| 203/4 |
2026/08/12 12:07 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 56984
output: 15
thoughts: 1311
Thoughts: **Reviewing the `kvm_xen_set_evtchn_fast` Locking Strategy**
Okay, so I see that `kvm_xen_update_runstate_guest` employs the `atomic` boolean parameter and utilizes `read_trylock` when it's true, which is precisely the approach I've taken with `kvm_xen_set_evtchn_fast`. This warrants a thorough re-evaluation of my changes.
Let me scrutinize the locking logic in `kvm_xen_set_evtchn_fast`. The code is structured as:
```c
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);
}
```
The `read_lock_irqsave` call effectively disables interrupts and saves flags, as expected. When `in_atomic` is set, `local_irq_save(flags)` is executed followed by `read_trylock`. If this succeeds, the current interrupt state is preserved, aligning with the behavior of `read_lock_irqsave`. If it fails, interrupts are restored, and we jump to `out_rcu_no_lock`. This mirrors the control flow in `kvm_xen_update_runstate_guest`. So far, so good.
Now, let's examine the second lock acquisition:
```c
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);
}
```
This second attempt follows the same pattern, maintaining consistency. The atomic path still correctly saves the interrupt state before the `read_trylock`, and we correctly manage the recovery if the trylock fails.
Moving on to the `out_rcu_no_lock` label:
```c
out_rcu:
read_unlock_irqrestore(&gpc->lock, flags);
out_rcu_no_lock:
srcu_read_unlock(&kvm->srcu, idx);
if (kick_vcpu) {
kvm_make_request(KVM_REQ_UNBLOCK, vcpu);
kvm_vcpu_kick(vcpu);
}
return rc;
```
This section looks correct, the code jumps here when the trylock fails, performs the correct `srcu_read_unlock`, and the code appropriately handles kicking the vCPU if necessary, which indicates a pending event.
Now, I'll verify the return value paths.
When the first `read_trylock` fails, `rc` is set to `-EWOULDBLOCK`:
```c
rc = -EWOULDBLOCK;
idx = srcu_read_lock(&kvm->srcu);
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 it fails, we jump to `out_rcu_no_lock`, then do `srcu_read_unlock`, and return `rc`. Perfect, as `-EWOULDBLOCK` makes sense here as it's the right error code.
Now, let's look at the failure scenario for the second trylock, in this case, `rc` is set to `1`:
```c
} 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 */
read_unlock_irqrestore(&gpc->lock, flags);
gpc = &vcpu->arch.xen.vcpu_info_cache;
|
| 204/4 |
2026/08/12 12:07 |
tool |
patch-diff |
0m
Args:
Results: map[Output:diff --git a/arch/x86/kvm/irq.c b/arch/x86/kvm/irq.c
index cb8ac4b9b..cfefa25be 100644
--- a/arch/x86/kvm/irq.c
+++ b/arch/x86/kvm/irq.c
@@ -241,40 +241,40 @@ int kvm_set_msi(struct kvm_kernel_irq_routing_entry *e,
int kvm_arch_set_irq_inatomic(struct kvm_kernel_irq_routing_entry *e,
struct kvm *kvm, int irq_source_id, int level,
bool line_status)
{
struct kvm_lapic_irq irq;
int r;
switch (e->type) {
#ifdef CONFIG_KVM_HYPERV
case KVM_IRQ_ROUTING_HV_SINT:
return kvm_hv_synic_set_irq(e, kvm, irq_source_id, level,
line_status);
#endif
case KVM_IRQ_ROUTING_MSI:
if (kvm_msi_route_invalid(kvm, e))
return -EINVAL;
kvm_msi_to_lapic_irq(kvm, e, &irq);
if (kvm_irq_delivery_to_apic_fast(kvm, NULL, &irq, &r))
return r;
break;
#ifdef CONFIG_KVM_XEN
- case KVM_IRQ_ROUTING_XEN_EVTCHN:
- if (!level)
- return -1;
+ case KVM_IRQ_ROUTING_XEN_EVTCHN:
+ 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;
}
return -EWOULDBLOCK;
}
int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *irq_event,
bool line_status)
diff --git a/arch/x86/kvm/xen.c b/arch/x86/kvm/xen.c
index 694b31c1f..7ebbefe43 100644
--- a/arch/x86/kvm/xen.c
+++ b/arch/x86/kvm/xen.c
@@ -125,27 +125,27 @@ void kvm_xen_inject_timer_irqs(struct kvm_vcpu *vcpu)
static enum hrtimer_restart xen_timer_callback(struct hrtimer *timer)
{
struct kvm_vcpu *vcpu = container_of(timer, struct kvm_vcpu,
arch.xen.timer);
struct kvm_xen_evtchn e;
int rc;
if (atomic_read(&vcpu->arch.xen.timer_pending))
return HRTIMER_NORESTART;
e.vcpu_id = vcpu->vcpu_id;
e.vcpu_idx = vcpu->vcpu_idx;
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;
}
atomic_inc(&vcpu->arch.xen.timer_pending);
kvm_make_request(KVM_REQ_UNBLOCK, vcpu);
kvm_vcpu_kick(vcpu);
return HRTIMER_NORESTART;
}
@@ -1785,171 +1785,190 @@ static void kvm_xen_check_poller(struct kvm_vcpu *vcpu, int port)
/*
* The return value from this function is propagated to kvm_set_irq() API,
* so it returns:
* < 0 Interrupt was ignored (masked or not delivered for other reasons)
* = 0 Interrupt was coalesced (previous irq is still pending)
* > 0 Number of CPUs interrupt was delivered to
*
* 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;
unsigned long *pending_bits, *mask_bits;
unsigned long flags;
int port_word_bit;
bool kick_vcpu = false;
int vcpu_idx, idx, rc;
vcpu_idx = READ_ONCE(xe->vcpu_idx);
if (vcpu_idx >= 0)
vcpu = kvm_get_vcpu(kvm, vcpu_idx);
else {
vcpu = kvm_get_vcpu_by_id(kvm, xe->vcpu_id);
if (!vcpu)
return -EINVAL;
WRITE_ONCE(xe->vcpu_idx, vcpu->vcpu_idx);
}
if (xe->port >= max_evtchn_port(kvm))
return -EINVAL;
rc = -EWOULDBLOCK;
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;
if (IS_ENABLED(CONFIG_64BIT) && kvm->arch.xen.long_mode) {
struct shared_info *shinfo = gpc->khva;
pending_bits = (unsigned long *)&shinfo->evtchn_pending;
mask_bits = (unsigned long *)&shinfo->evtchn_mask;
port_word_bit = xe->port / 64;
} else {
struct compat_shared_info *shinfo = gpc->khva;
pending_bits = (unsigned long *)&shinfo->evtchn_pending;
mask_bits = (unsigned long *)&shinfo->evtchn_mask;
port_word_bit = xe->port / 32;
}
/*
* If this port wasn't already set, and if it isn't masked, then
* we try to set the corresponding bit in the in-kernel shadow of
* evtchn_pending_sel for the target vCPU. And if *that* wasn't
* already set, then we kick the vCPU in question to write to the
* *real* evtchn_pending_sel in its own guest vcpu_info struct.
*/
if (test_and_set_bit(xe->port, pending_bits)) {
rc = 0; /* It was already raised */
} else if (test_bit(xe->port, mask_bits)) {
rc = -ENOTCONN; /* Masked */
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 */
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
* 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:
read_unlock_irqrestore(&gpc->lock, flags);
+ out_rcu_no_lock:
srcu_read_unlock(&kvm->srcu, idx);
if (kick_vcpu) {
kvm_make_request(KVM_REQ_UNBLOCK, vcpu);
kvm_vcpu_kick(vcpu);
}
return rc;
}
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;
if (current->mm != kvm->mm) {
/*
* If not on a thread which already belongs to this KVM,
* we'd better be in the irqfd workqueue.
*/
if (WARN_ON_ONCE(current->mm))
return -EINVAL;
kthread_use_mm(kvm->mm);
mm_borrowed = true;
}
/*
* It is theoretically possible for the page to be unmapped
* and the MMU notifier to invalidate the shared_info before
* we even get to use it. In that case, this looks like an
* infinite loop. It was tempting to do it via the userspace
* HVA instead... but that just *hides* the fact that it's
* an infinite loop, because if a fault occurs and it waits
* for the page to come back, it can *still* immediately
* fault and have to wait again, repeatedly.
*
* Conversely, the page could also have been reinstated by
* another thread before we even obtain the mutex above, so
* check again *first* before remapping it.
*/
do {
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;
idx = srcu_read_lock(&kvm->srcu);
rc = kvm_gpc_refresh(gpc, PAGE_SIZE);
srcu_read_unlock(&kvm->srcu, idx);
} while(!rc);
if (mm_borrowed)
kthread_unuse_mm(kvm->mm);
return rc;
}
/* This is the version called from kvm_set_irq() as the .set function */
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
@@ -25,21 +25,21 @@ int kvm_xen_vcpu_get_attr(struct kvm_vcpu *vcpu, struct kvm_xen_vcpu_attr *data)
int kvm_xen_hvm_set_attr(struct kvm *kvm, struct kvm_xen_hvm_attr *data);
int kvm_xen_hvm_get_attr(struct kvm *kvm, struct kvm_xen_hvm_attr *data);
int kvm_xen_hvm_evtchn_send(struct kvm *kvm, struct kvm_irq_routing_xen_evtchn *evt);
int kvm_xen_write_hypercall_page(struct kvm_vcpu *vcpu, u64 data);
int kvm_xen_hvm_config(struct kvm *kvm, struct kvm_xen_hvm_config *xhc);
void kvm_xen_init_vm(struct kvm *kvm);
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);
static inline void kvm_xen_sw_enable_lapic(struct kvm_vcpu *vcpu)
{
/*
* The local APIC is being enabled. If the per-vCPU upcall vector is
* set and the vCPU's evtchn_upcall_pending flag is set, inject the
* interrupt.
]
|
| 205/4 |
2026/08/12 12:07 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 63605
output: 530
thoughts: 185
Thoughts: **Reviewing the Patch**
Okay, this diff looks spot-on. I've successfully updated the `kvm_xen_set_evtchn_fast` signature in both `arch/x86/kvm/xen.h` and `arch/x86/kvm/xen.c`. Crucially, I've made sure all the calling code now correctly passes the `in_atomic` flag, which is essential for this to work as intended.
I've also implemented the fallback logic using `read_trylock` within `kvm_xen_set_evtchn_fast`, addressing the potential locking issues on both `gpc->lock` acquisitions. The important part is that the fallback code skips that `read_unlock_irqrestore` and returns the right error code (-EWOULDBLOCK or 1). This is exactly as expected, and the callers can handle these error codes properly.
This implementation perfectly aligns with the existing pattern found in `kvm_xen_update_runstate_guest`. Everything seems to fit together seamlessly, so, I'm ready to write up the final response and move on.
|
| 206/3 |
2026/08/12 12:07 |
action |
test-patch |
28m
Results: map[PatchDiff:diff --git a/arch/x86/kvm/irq.c b/arch/x86/kvm/irq.c
index cb8ac4b9b..cfefa25be 100644
--- a/arch/x86/kvm/irq.c
+++ b/arch/x86/kvm/irq.c
@@ -263,11 +263,11 @@ int kvm_arch_set_irq_inatomic(struct kvm_kernel_irq_routing_entry *e,
break;
#ifdef CONFIG_KVM_XEN
- case KVM_IRQ_ROUTING_XEN_EVTCHN:
- if (!level)
- return -1;
+ case KVM_IRQ_ROUTING_XEN_EVTCHN:
+ 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..7ebbefe43 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,17 @@ 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 +1906,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 +1922,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 +1956,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);
TestError:]
|
| 207/1 |
2026/08/12 12:36 |
action |
apply-patch |
0m
Results:
|
| 208/1 |
2026/08/12 12:36 |
action |
init-style-items |
0m
Results:
|
| 209/1 |
2026/08/12 12:36 |
loop |
|
13m
|
| 210/2 |
2026/08/12 12:36 |
iteration |
0 |
13m
|
| 211/3 |
2026/08/12 12:36 |
agent |
patch-formatter |
0m
Model:
Instruction:
You are an expert Linux kernel developer tasked with formatting a kernel patch.
Your objective is purely formatting: you must ensure the patch complies with the kernel's coding style,
conforms to the surrounding code rules, and passes checkpatch.pl, while preserving the code logic exactly as it is.
You should stop once the requested formatting changes are done and checkpatch.pl is happy.
Do not question the requested changes unless they are obviously wrong.
If the code already conforms to the requested changes, surrounding code rules,
and checkpatch.pl is happy, you should just finish your task.
WARNING: The clang-format tool may break the formatting of the surrounding code (like manual alignment).
Use it with caution. We want to make the change fit into the existing formatting as much as possible.
Prefer calling several tools at the same time to save round-trips.
Prompt:
The current patch diff is:
diff --git a/arch/x86/kvm/irq.c b/arch/x86/kvm/irq.c
index cb8ac4b9b..cfefa25be 100644
--- a/arch/x86/kvm/irq.c
+++ b/arch/x86/kvm/irq.c
@@ -263,11 +263,11 @@ int kvm_arch_set_irq_inatomic(struct kvm_kernel_irq_routing_entry *e,
break;
#ifdef CONFIG_KVM_XEN
- case KVM_IRQ_ROUTING_XEN_EVTCHN:
- if (!level)
- return -1;
+ case KVM_IRQ_ROUTING_XEN_EVTCHN:
+ 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..7ebbefe43 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,17 @@ 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 +1906,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 +1922,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 +1956,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);
Use the provided tools to format the patch.
Reply: I have successfully formatted the patch to comply with the Linux kernel's coding style and the surrounding code rules. All indentation issues have been corrected, and the line length warning has been resolved. The patch now passes `checkpatch.pl` with zero errors and zero warnings.
|
| 212/4 |
2026/08/12 12:36 |
llm |
patch-formatter |
0m
Model:
Tokens:
input: 6258
output: 9
thoughts: 55
Thoughts: **Kernel Patch Formatting - My Approach**
Okay, so the user wants me to format a kernel patch. My immediate thought is to get a baseline understanding of any potential formatting issues. The most efficient way to do this is definitely to run `checkpatch` on the current patch. I need to make sure I identify any problems before I start modifying anything. Ah, wait a minute, I can use the tool specifically called `checkpatch`! Great. I should use that. Let's get that going and see what it tells me.
|
| 213/4 |
2026/08/12 12:36 |
tool |
checkpatch |
0m
Results: map[Output:WARNING: line length of 105 exceeds 100 columns
#69: FILE: arch/x86/kvm/xen.c:1868:
+ if (!test_and_set_bit(port_word_bit, &vcpu->arch.xen.evtchn_pending_sel))
total: 0 errors, 1 warnings, 96 lines checked
NOTE: For some of the reported defects, checkpatch may be able to
mechanically convert to the typical style using --fix or --fix-inplace.
Your patch has style problems, please review.
NOTE: If any of the errors are false positives, please report
them to the maintainer, see CHECKPATCH in MAINTAINERS.
]
|
| 214/4 |
2026/08/12 12:36 |
llm |
patch-formatter |
0m
Model:
Tokens:
input: 6610
output: 41
thoughts: 353
Thoughts: **Investigating Code Style and Potential Issues in KVM/Xen Integration**
Okay, so I've got a warning here about line length, specifically line 1868 in `arch/x86/kvm/xen.c`. The message is pretty clear; it exceeds the 100-column limit, so I need to check that out. The offending line is: `if (!test_and_set_bit(port_word_bit, &vcpu->arch.xen.evtchn_pending_sel))`. I'll keep that in mind.
Now, I'm also looking at some indentation changes in `arch/x86/kvm/irq.c` in this diff:
```
#ifdef CONFIG_KVM_XEN
- case KVM_IRQ_ROUTING_XEN_EVTCHN:
- if (!level)
- return -1;
+ case KVM_IRQ_ROUTING_XEN_EVTCHN:
+ 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
```
I'm a little puzzled by this change. It seems the `case` statement, `KVM_IRQ_ROUTING_XEN_EVTCHN:`, has been doubly indented now. That's unusual, I always align `case` with the `switch` statement or single-indent it. I'm going to look at the surrounding code in `arch/x86/kvm/irq.c` to see if there's any context I'm missing around line 263. Gotta ensure this isn't just a minor formatting inconsistency but a consequence of a larger refactor, or perhaps a stylistic deviation in this section.
|
| 215/4 |
2026/08/12 12:36 |
tool |
read-file |
0m
Args: map[File:arch/x86/kvm/irq.c FirstLine:240 LineCount:40]
Results: map[Contents: 240:
241: int kvm_arch_set_irq_inatomic(struct kvm_kernel_irq_routing_entry *e,
242: struct kvm *kvm, int irq_source_id, int level,
243: bool line_status)
244: {
245: struct kvm_lapic_irq irq;
246: int r;
247:
248: switch (e->type) {
249: #ifdef CONFIG_KVM_HYPERV
250: case KVM_IRQ_ROUTING_HV_SINT:
251: return kvm_hv_synic_set_irq(e, kvm, irq_source_id, level,
252: line_status);
253: #endif
254:
255: case KVM_IRQ_ROUTING_MSI:
256: if (kvm_msi_route_invalid(kvm, e))
257: return -EINVAL;
258:
259: kvm_msi_to_lapic_irq(kvm, e, &irq);
260:
261: if (kvm_irq_delivery_to_apic_fast(kvm, NULL, &irq, &r))
262: return r;
263: break;
264:
265: #ifdef CONFIG_KVM_XEN
266: case KVM_IRQ_ROUTING_XEN_EVTCHN:
267: if (!level)
268: return -1;
269:
270: return kvm_xen_set_evtchn_fast(&e->xen_evtchn, kvm);
271: #endif
272: default:
273: break;
274: }
275:
276: return -EWOULDBLOCK;
277: }
278:
279: int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *irq_event,
]
|
| 216/4 |
2026/08/12 12:36 |
llm |
patch-formatter |
0m
Model:
Tokens:
input: 8071
output: 41
thoughts: 356
Thoughts: **Analyzing a KVM Patch Diff**
Okay, so I'm looking at this patch and immediately something jumps out at me. The indentation of `case KVM_IRQ_ROUTING_XEN_EVTCHN:` is different here. It looks like it was originally aligned with the other `case` statements in the code, but the patch has it... well, not. My first thought is that this is likely an accidental formatting issue, a simple mistake made during the patch creation. It's the kind of thing that's easy to miss. I'm going to correct the indentation to match the surrounding code; that's just good practice and keeps things clean.
But before I submit the fix, let's address that other warning. I see a line length warning, exceeding the usual 100-column limit. I should take a look at the context of that line to see how best to address it. Let me go to `arch/x86/kvm/xen.c` around line 1853 and see what's going on.
|
| 217/4 |
2026/08/12 12:36 |
tool |
read-file |
0m
Args: map[File:arch/x86/kvm/xen.c FirstLine:1840 LineCount:70]
Results: map[Contents:1840: * we try to set the corresponding bit in the in-kernel shadow of
1841: * evtchn_pending_sel for the target vCPU. And if *that* wasn't
1842: * already set, then we kick the vCPU in question to write to the
1843: * *real* evtchn_pending_sel in its own guest vcpu_info struct.
1844: */
1845: if (test_and_set_bit(xe->port, pending_bits)) {
1846: rc = 0; /* It was already raised */
1847: } else if (test_bit(xe->port, mask_bits)) {
1848: rc = -ENOTCONN; /* Masked */
1849: kvm_xen_check_poller(vcpu, xe->port);
1850: } else {
1851: rc = 1; /* Delivered to the bitmap in shared_info. */
1852: /* Now switch to the vCPU's vcpu_info to set the index and pending_sel */
1853: read_unlock_irqrestore(&gpc->lock, flags);
1854: gpc = &vcpu->arch.xen.vcpu_info_cache;
1855:
1856: read_lock_irqsave(&gpc->lock, flags);
1857: if (!kvm_gpc_check(gpc, sizeof(struct vcpu_info))) {
1858: /*
1859: * Could not access the vcpu_info. Set the bit in-kernel
1860: * and prod the vCPU to deliver it for itself.
1861: */
1862: if (!test_and_set_bit(port_word_bit, &vcpu->arch.xen.evtchn_pending_sel))
1863: kick_vcpu = true;
1864: goto out_rcu;
1865: }
1866:
1867: if (IS_ENABLED(CONFIG_64BIT) && kvm->arch.xen.long_mode) {
1868: struct vcpu_info *vcpu_info = gpc->khva;
1869: if (!test_and_set_bit(port_word_bit, &vcpu_info->evtchn_pending_sel)) {
1870: WRITE_ONCE(vcpu_info->evtchn_upcall_pending, 1);
1871: kick_vcpu = true;
1872: }
1873: } else {
1874: struct compat_vcpu_info *vcpu_info = gpc->khva;
1875: if (!test_and_set_bit(port_word_bit,
1876: (unsigned long *)&vcpu_info->evtchn_pending_sel)) {
1877: WRITE_ONCE(vcpu_info->evtchn_upcall_pending, 1);
1878: kick_vcpu = true;
1879: }
1880: }
1881:
1882: /* For the per-vCPU lapic vector, deliver it as MSI. */
1883: if (kick_vcpu && vcpu->arch.xen.upcall_vector) {
1884: kvm_xen_inject_vcpu_vector(vcpu);
1885: kick_vcpu = false;
1886: }
1887: }
1888:
1889: out_rcu:
1890: read_unlock_irqrestore(&gpc->lock, flags);
1891: srcu_read_unlock(&kvm->srcu, idx);
1892:
1893: if (kick_vcpu) {
1894: kvm_make_request(KVM_REQ_UNBLOCK, vcpu);
1895: kvm_vcpu_kick(vcpu);
1896: }
1897:
1898: return rc;
1899: }
1900:
1901: static int kvm_xen_set_evtchn(struct kvm_xen_evtchn *xe, struct kvm *kvm)
1902: {
1903: bool mm_borrowed = false;
1904: int rc;
1905:
1906: rc = kvm_xen_set_evtchn_fast(xe, kvm);
1907: if (rc != -EWOULDBLOCK)
1908: return rc;
1909:
]
|
| 218/4 |
2026/08/12 12:36 |
llm |
patch-formatter |
0m
Model:
Tokens:
input: 9933
output: 180
thoughts: 932
Thoughts: **Analyzing and Refining a KVM/Xen Patch**
Okay, let's dive back into this patch diff. First, I'm focusing on that `kvm_xen_set_evtchn_fast` function. I see a conditional block in there, and I want to confirm the potential line length issues. Specifically, I'm interested in:
`if (!test_and_set_bit(port_word_bit, &vcpu->arch.xen.evtchn_pending_sel))`
Ah, yes, that's definitely over 100 characters as it is. Let's see... a simple wrap should fix it. Breaking it before the `&` seems logical:
```c
if (!test_and_set_bit(port_word_bit,
&vcpu->arch.xen.evtchn_pending_sel))
```
Now, let's verify the new line length, including the indentation. Okay, with the indentation, it's easily under 100 columns:
` if (!test_and_set_bit(port_word_bit,` is 40 characters.
` &vcpu->arch.xen.evtchn_pending_sel))` is 32 + 22 + 36 = 90 characters.
Perfect! That wrap is clean and compliant.
Next, I'm looking at the changes in `arch/x86/kvm/irq.c`. I notice the indentation of the `case KVM_IRQ_ROUTING_XEN_EVTCHN:` block has changed. It's a subtle but important detail, and I'm guessing an auto-formatter or a simple oversight caused this. I'll make sure to revert the indentation to its original form. I want to keep the code consistent and readable. So, let me make that adjustment:
```c
#ifdef CONFIG_KVM_XEN
case KVM_IRQ_ROUTING_XEN_EVTCHN:
if (!level)
return -1;
return kvm_xen_set_evtchn_fast(&e->xen_evtchn, kvm, true);
#endif
```
Now, I'll go ahead and edit `arch/x86/kvm/irq.c` to fix the indentation.
|
| 219/4 |
2026/08/12 12:36 |
tool |
codeeditor |
0m
Args: map[CurrentCode:#ifdef CONFIG_KVM_XEN
case KVM_IRQ_ROUTING_XEN_EVTCHN:
if (!level)
return -1;
return kvm_xen_set_evtchn_fast(&e->xen_evtchn, kvm, true);
#endif NewCode:#ifdef CONFIG_KVM_XEN
case KVM_IRQ_ROUTING_XEN_EVTCHN:
if (!level)
return -1;
return kvm_xen_set_evtchn_fast(&e->xen_evtchn, kvm, true);
#endif SourceFile:arch/x86/kvm/irq.c]
Results:
|
| 220/4 |
2026/08/12 12:36 |
llm |
patch-formatter |
0m
Model:
Tokens:
input: 11565
output: 28
thoughts: 28
Thoughts: Okay, here's my take on those thoughts, framed as an expert's internal monologue:
**Diving into the KVM IRQ Handling**
Alright, time to get my hands dirty. I need to understand what's changed in `arch/x86/kvm/irq.c`. Let's fire up `patch-diff` and get a clear look at the modifications. I know this file is critical for how KVM handles interrupts on x86, so understanding the diff is crucial for whatever I'm currently working on – likely something related to interrupt injection, virtualization, or maybe even performance. Need to see if there are any subtle changes to the interrupt routing, virtualization of the interrupt controller, or potentially performance optimizations in how interrupts are handled at the hypervisor level. The devil is often in the details when dealing with interrupt handling. Let's see what we're looking at.
|
| 221/4 |
2026/08/12 12:36 |
tool |
patch-diff |
0m
Args: map[File:arch/x86/kvm/irq.c]
Results: map[Output: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
@@ -241,40 +241,40 @@ int kvm_set_msi(struct kvm_kernel_irq_routing_entry *e,
int kvm_arch_set_irq_inatomic(struct kvm_kernel_irq_routing_entry *e,
struct kvm *kvm, int irq_source_id, int level,
bool line_status)
{
struct kvm_lapic_irq irq;
int r;
switch (e->type) {
#ifdef CONFIG_KVM_HYPERV
case KVM_IRQ_ROUTING_HV_SINT:
return kvm_hv_synic_set_irq(e, kvm, irq_source_id, level,
line_status);
#endif
case KVM_IRQ_ROUTING_MSI:
if (kvm_msi_route_invalid(kvm, e))
return -EINVAL;
kvm_msi_to_lapic_irq(kvm, e, &irq);
if (kvm_irq_delivery_to_apic_fast(kvm, NULL, &irq, &r))
return r;
break;
#ifdef CONFIG_KVM_XEN
case KVM_IRQ_ROUTING_XEN_EVTCHN:
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;
}
return -EWOULDBLOCK;
}
int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *irq_event,
bool line_status)
]
|
| 222/4 |
2026/08/12 12:36 |
llm |
patch-formatter |
0m
Model:
Tokens:
input: 12424
output: 41
thoughts: 96
Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience:
**Code Review - KVM/x86 Architecture**
Ah, excellent! The indentation in `arch/x86/kvm/irq.c` is spot-on now. A minor thing, but good to see it's been addressed and cleaned up according to the style guide. It's a testament to the attention to detail.
Now, let's switch gears and delve into `arch/x86/kvm/xen.c`. I need to zero in on a specific section. Let's start by taking a close look at the code block around lines 1861-1875. I'll read those lines carefully first to get a precise understanding of the context before I consider making any comments. I don't want to miss any nuances. The details are important.
|
| 223/4 |
2026/08/12 12:36 |
tool |
read-file |
0m
Args: map[File:arch/x86/kvm/xen.c FirstLine:1855 LineCount:35]
Results: map[Contents:1855:
1856: read_lock_irqsave(&gpc->lock, flags);
1857: if (!kvm_gpc_check(gpc, sizeof(struct vcpu_info))) {
1858: /*
1859: * Could not access the vcpu_info. Set the bit in-kernel
1860: * and prod the vCPU to deliver it for itself.
1861: */
1862: if (!test_and_set_bit(port_word_bit, &vcpu->arch.xen.evtchn_pending_sel))
1863: kick_vcpu = true;
1864: goto out_rcu;
1865: }
1866:
1867: if (IS_ENABLED(CONFIG_64BIT) && kvm->arch.xen.long_mode) {
1868: struct vcpu_info *vcpu_info = gpc->khva;
1869: if (!test_and_set_bit(port_word_bit, &vcpu_info->evtchn_pending_sel)) {
1870: WRITE_ONCE(vcpu_info->evtchn_upcall_pending, 1);
1871: kick_vcpu = true;
1872: }
1873: } else {
1874: struct compat_vcpu_info *vcpu_info = gpc->khva;
1875: if (!test_and_set_bit(port_word_bit,
1876: (unsigned long *)&vcpu_info->evtchn_pending_sel)) {
1877: WRITE_ONCE(vcpu_info->evtchn_upcall_pending, 1);
1878: kick_vcpu = true;
1879: }
1880: }
1881:
1882: /* For the per-vCPU lapic vector, deliver it as MSI. */
1883: if (kick_vcpu && vcpu->arch.xen.upcall_vector) {
1884: kvm_xen_inject_vcpu_vector(vcpu);
1885: kick_vcpu = false;
1886: }
1887: }
1888:
1889: out_rcu:
]
|
| 224/4 |
2026/08/12 12:36 |
llm |
patch-formatter |
0m
Model:
Tokens:
input: 13376
output: 256
thoughts: 658
Thoughts: **Analyzing a Xen KVM Patch Diff**
Okay, let's dive into this patch for `arch/x86/kvm/xen.c`. First, I need to see the context surrounding the changes, specifically around those lines. Looking at the diff, I see the conditional block using `read_trylock`. I'm immediately drawn to that rather long line within the `if (in_atomic)` section:
```c
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;
}
```
Yes, it is indeed 105 characters long. That needs to be addressed for readability. My instinct is to wrap that long line. Let's wrap it in two lines to improve visual flow.
```c
if (!test_and_set_bit(port_word_bit,
&vcpu->arch.xen.evtchn_pending_sel))
kick_vcpu = true;
```
Now, let's double-check the indentation. I need to make sure the tab/space mix is consistent. The first line of the conditional starts with four tabs:
` if (!test_and_set_bit(port_word_bit,`
The second line of the conditional includes six tabs and six spaces.
` &vcpu->arch.xen.evtchn_pending_sel))`
Now, let's make the edit to the file.
|
| 225/4 |
2026/08/12 12:36 |
tool |
codeeditor |
0m
Args: map[CurrentCode: 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 { NewCode: 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 { SourceFile:arch/x86/kvm/xen.c]
Results:
|
| 226/4 |
2026/08/12 12:36 |
llm |
patch-formatter |
0m
Model:
Tokens:
input: 14670
output: 9
thoughts: 20
Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience:
**Checking for Code Style Issues**
Alright, time to bring out the heavy artillery. I'm going to run `checkpatch` now. This is a critical step, obviously. I'm not expecting any *major* issues at this point, but even the smallest nitpicks can become a source of pain down the line. I want to make sure the code adheres to the project's coding standards. I'm specifically looking for any remaining warnings or outright errors that might have slipped through the cracks during the review process. This isn't just about cosmetic stuff; some warnings can indicate potential logic flaws, memory leaks, or portability problems, so I'll be meticulously checking the output. It's a fundamental part of maintaining code quality and ensuring a clean and consistent codebase. Ultimately it's about making sure that the patch is ready for integration.
|
| 227/4 |
2026/08/12 12:36 |
tool |
checkpatch |
0m
Results: map[Output:total: 0 errors, 0 warnings, 90 lines checked
Your patch has no obvious style problems and is ready for submission.
]
|
| 228/4 |
2026/08/12 12:36 |
llm |
patch-formatter |
0m
Model:
Tokens:
input: 14935
output: 10
thoughts: 31
Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience:
**Final Patch Review: A Deep Dive**
Alright, `checkpatch` is finally giving me the thumbs up! That's always a good sign. But we're not quite done yet. This is where I go for the *really* close look. I need to run a diff of the entire patch one last time. We're talking meticulously going through the entire changeset with a fine-tooth comb. Even with `checkpatch` passing, there's always the possibility of subtle stylistic inconsistencies or, heaven forbid, a misplaced space or tab that slipped through the cracks. We need to be absolutely certain that this patch adheres to every single coding standard and project guideline. No room for ambiguity here. This isn't just about correctness; it’s about maintainability, readability, and consistency across the codebase. One last careful review, and then we can sign off on this.
|
| 229/4 |
2026/08/12 12:36 |
tool |
patch-diff |
0m
Error:
|
| 230/4 |
2026/08/12 12:36 |
llm |
patch-formatter |
0m
Model:
Tokens:
input: 15194
output: 15
thoughts: 80
Thoughts: Okay, here's my interpretation of that thought process, assuming I'm an expert in this area and thinking this through:
**Examining the `patch-diff` Command's Argument Structure**
Right, so `patch-diff` needs a `File` argument. The description *seems* to suggest it's optional – that if I leave it blank, I get the whole diff. However, the schema clearly states `File` is *required*. That's a classic documentation mismatch and a potential source of errors. My gut says the documentation might be inaccurate, or at least misleading.
I have two options at this point. I could iterate through each individual file and run `patch-diff` for each. But that's inefficient. Instead, I should try passing an empty string for the `File` argument. That's usually the approach to communicate "all files". Let's see if `File: ""` works as a placeholder to get that global diff and resolve this discrepancy. This will quickly show me if the schema is overly restrictive or if the documentation is simply wrong.
|
| 231/4 |
2026/08/12 12:37 |
tool |
patch-diff |
0m
Args:
Results: map[Output: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
@@ -241,40 +241,40 @@ int kvm_set_msi(struct kvm_kernel_irq_routing_entry *e,
int kvm_arch_set_irq_inatomic(struct kvm_kernel_irq_routing_entry *e,
struct kvm *kvm, int irq_source_id, int level,
bool line_status)
{
struct kvm_lapic_irq irq;
int r;
switch (e->type) {
#ifdef CONFIG_KVM_HYPERV
case KVM_IRQ_ROUTING_HV_SINT:
return kvm_hv_synic_set_irq(e, kvm, irq_source_id, level,
line_status);
#endif
case KVM_IRQ_ROUTING_MSI:
if (kvm_msi_route_invalid(kvm, e))
return -EINVAL;
kvm_msi_to_lapic_irq(kvm, e, &irq);
if (kvm_irq_delivery_to_apic_fast(kvm, NULL, &irq, &r))
return r;
break;
#ifdef CONFIG_KVM_XEN
case KVM_IRQ_ROUTING_XEN_EVTCHN:
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;
}
return -EWOULDBLOCK;
}
int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *irq_event,
bool line_status)
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
@@ -125,27 +125,27 @@ void kvm_xen_inject_timer_irqs(struct kvm_vcpu *vcpu)
static enum hrtimer_restart xen_timer_callback(struct hrtimer *timer)
{
struct kvm_vcpu *vcpu = container_of(timer, struct kvm_vcpu,
arch.xen.timer);
struct kvm_xen_evtchn e;
int rc;
if (atomic_read(&vcpu->arch.xen.timer_pending))
return HRTIMER_NORESTART;
e.vcpu_id = vcpu->vcpu_id;
e.vcpu_idx = vcpu->vcpu_idx;
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;
}
atomic_inc(&vcpu->arch.xen.timer_pending);
kvm_make_request(KVM_REQ_UNBLOCK, vcpu);
kvm_vcpu_kick(vcpu);
return HRTIMER_NORESTART;
}
@@ -1785,171 +1785,191 @@ static void kvm_xen_check_poller(struct kvm_vcpu *vcpu, int port)
/*
* The return value from this function is propagated to kvm_set_irq() API,
* so it returns:
* < 0 Interrupt was ignored (masked or not delivered for other reasons)
* = 0 Interrupt was coalesced (previous irq is still pending)
* > 0 Number of CPUs interrupt was delivered to
*
* 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;
unsigned long *pending_bits, *mask_bits;
unsigned long flags;
int port_word_bit;
bool kick_vcpu = false;
int vcpu_idx, idx, rc;
vcpu_idx = READ_ONCE(xe->vcpu_idx);
if (vcpu_idx >= 0)
vcpu = kvm_get_vcpu(kvm, vcpu_idx);
else {
vcpu = kvm_get_vcpu_by_id(kvm, xe->vcpu_id);
if (!vcpu)
return -EINVAL;
WRITE_ONCE(xe->vcpu_idx, vcpu->vcpu_idx);
}
if (xe->port >= max_evtchn_port(kvm))
return -EINVAL;
rc = -EWOULDBLOCK;
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;
if (IS_ENABLED(CONFIG_64BIT) && kvm->arch.xen.long_mode) {
struct shared_info *shinfo = gpc->khva;
pending_bits = (unsigned long *)&shinfo->evtchn_pending;
mask_bits = (unsigned long *)&shinfo->evtchn_mask;
port_word_bit = xe->port / 64;
} else {
struct compat_shared_info *shinfo = gpc->khva;
pending_bits = (unsigned long *)&shinfo->evtchn_pending;
mask_bits = (unsigned long *)&shinfo->evtchn_mask;
port_word_bit = xe->port / 32;
}
/*
* If this port wasn't already set, and if it isn't masked, then
* we try to set the corresponding bit in the in-kernel shadow of
* evtchn_pending_sel for the target vCPU. And if *that* wasn't
* already set, then we kick the vCPU in question to write to the
* *real* evtchn_pending_sel in its own guest vcpu_info struct.
*/
if (test_and_set_bit(xe->port, pending_bits)) {
rc = 0; /* It was already raised */
} else if (test_bit(xe->port, mask_bits)) {
rc = -ENOTCONN; /* Masked */
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 */
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
* 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:
read_unlock_irqrestore(&gpc->lock, flags);
+ out_rcu_no_lock:
srcu_read_unlock(&kvm->srcu, idx);
if (kick_vcpu) {
kvm_make_request(KVM_REQ_UNBLOCK, vcpu);
kvm_vcpu_kick(vcpu);
}
return rc;
}
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;
if (current->mm != kvm->mm) {
/*
* If not on a thread which already belongs to this KVM,
* we'd better be in the irqfd workqueue.
*/
if (WARN_ON_ONCE(current->mm))
return -EINVAL;
kthread_use_mm(kvm->mm);
mm_borrowed = true;
}
/*
* It is theoretically possible for the page to be unmapped
* and the MMU notifier to invalidate the shared_info before
* we even get to use it. In that case, this looks like an
* infinite loop. It was tempting to do it via the userspace
* HVA instead... but that just *hides* the fact that it's
* an infinite loop, because if a fault occurs and it waits
* for the page to come back, it can *still* immediately
* fault and have to wait again, repeatedly.
*
* Conversely, the page could also have been reinstated by
* another thread before we even obtain the mutex above, so
* check again *first* before remapping it.
*/
do {
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;
idx = srcu_read_lock(&kvm->srcu);
rc = kvm_gpc_refresh(gpc, PAGE_SIZE);
srcu_read_unlock(&kvm->srcu, idx);
} while(!rc);
if (mm_borrowed)
kthread_unuse_mm(kvm->mm);
return rc;
}
/* This is the version called from kvm_set_irq() as the .set function */
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
@@ -25,21 +25,21 @@ int kvm_xen_vcpu_get_attr(struct kvm_vcpu *vcpu, struct kvm_xen_vcpu_attr *data)
int kvm_xen_hvm_set_attr(struct kvm *kvm, struct kvm_xen_hvm_attr *data);
int kvm_xen_hvm_get_attr(struct kvm *kvm, struct kvm_xen_hvm_attr *data);
int kvm_xen_hvm_evtchn_send(struct kvm *kvm, struct kvm_irq_routing_xen_evtchn *evt);
int kvm_xen_write_hypercall_page(struct kvm_vcpu *vcpu, u64 data);
int kvm_xen_hvm_config(struct kvm *kvm, struct kvm_xen_hvm_config *xhc);
void kvm_xen_init_vm(struct kvm *kvm);
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);
static inline void kvm_xen_sw_enable_lapic(struct kvm_vcpu *vcpu)
{
/*
* The local APIC is being enabled. If the per-vCPU upcall vector is
* set and the vCPU's evtchn_upcall_pending flag is set, inject the
* interrupt.
]
|
| 232/4 |
2026/08/12 12:37 |
llm |
patch-formatter |
0m
Model:
Tokens:
input: 19762
output: 55
thoughts: 58
Thoughts: **Ready for Submission!**
Okay, this is excellent. The diff itself is incredibly clean – perfectly formatted, just as it should be. The `checkpatch` output confirms it: zero errors, zero warnings, and a thorough 90 lines checked. That's exactly what I'm looking for. "Your patch has no obvious style problems and is ready for submission." Fantastic! This patch is pristine, and it's passing all the quality checks. I'm satisfied. I can confidently complete the task at hand and move forward with the next steps. This is a very satisfying result.
|
| 233/3 |
2026/08/12 12:37 |
action |
test-patch-inplace |
12m
Results: map[PatchDiff: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);
TestError:]
|
| 234/3 |
2026/08/12 12:49 |
action |
run-checkpatch |
0m
Results: map[CheckpatchOutput:total: 0 errors, 0 warnings, 90 lines checked
Your patch has no obvious style problems and is ready for submission.
NeedRefinement:false]
|
| 235/1 |
2026/08/12 12:49 |
agent |
fixes-finder |
2m
Model:
Results: map[FixesHash:14243b387137a4afbe1df5d9dc15182d6657bb79]
Instruction:
You are an experienced Linux kernel developer tasked with identifying the commit
that introduced the bug being fixed. Identifying the correct buggy commit is crucial
for proper kernel maintenance (backporting to stable trees, etc.).
Your investigation strategy:
1. Examine the patch that fixes the bug. Use git tools (like git-log or git-blame)
to trace the history of the lines or functions modified by the patch.
2. Analyze the stack trace in the crash report. Identify the key files and functions
involved in the crash and investigate their history to see when the problematic
logic was introduced.
3. Compare the bug explanation with the commit history to find the point where
the described logic error first appeared.
A bug is typically introduced when a piece of code is first written, or when
a refactoring changed its logic in a way that introduced the bug.
Trace the history of relevant symbols or find when specific code patterns were introduced/removed.
You must provide exactly one bug-introducing commit hash.
If you are unable to confidently determine the bug-introducing commit after investigation,
return an empty string rather than guessing.
Prefer calling several tools at the same time to save round-trips.
Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.
Prompt:
The crash is:
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
preempt_count: 10001, expected: 0
RCU nest depth: 0, expected: 0
1 lock held by kworker/0:5/5685:
#0: ffff8881964ddb48 (&kvm->srcu){.?.+}-{0:0}, at: srcu_lock_acquire include/linux/srcu.h:187 [inline]
#0: ffff8881964ddb48 (&kvm->srcu){.?.+}-{0:0}, at: srcu_read_lock include/linux/srcu.h:294 [inline]
#0: ffff8881964ddb48 (&kvm->srcu){.?.+}-{0:0}, at: kvm_xen_set_evtchn_fast+0x1be/0x990 arch/x86/kvm/xen.c:1820
irq event stamp: 78678
hardirqs last enabled at (78677): [<ffffffff8194da12>] raw_spin_rq_unlock_irq kernel/sched/sched.h:1673 [inline]
hardirqs last enabled at (78677): [<ffffffff8194da12>] finish_lock_switch kernel/sched/core.c:5241 [inline]
hardirqs last enabled at (78677): [<ffffffff8194da12>] finish_task_switch+0x412/0xc60 kernel/sched/core.c:5359
hardirqs last disabled at (78678): [<ffffffff8b4edc4e>] sysvec_apic_timer_interrupt+0xe/0xc0 arch/x86/kernel/apic/apic.c:1062
softirqs last enabled at (78668): [<ffffffff8187f8b0>] __local_bh_enable_ip+0x1a0/0x2b0 kernel/softirq.c:305
softirqs last disabled at (78666): [<ffffffff861be59f>] local_bh_disable include/linux/bottom_half.h:20 [inline]
softirqs last disabled at (78666): [<ffffffff861be59f>] rcu_read_lock_bh include/linux/rcupdate.h:893 [inline]
softirqs last disabled at (78666): [<ffffffff861be59f>] keep_key_fresh drivers/net/wireguard/send.c:129 [inline]
softirqs last disabled at (78666): [<ffffffff861be59f>] wg_packet_create_data_done drivers/net/wireguard/send.c:259 [inline]
softirqs last disabled at (78666): [<ffffffff861be59f>] wg_packet_tx_worker+0x25f/0x7e0 drivers/net/wireguard/send.c:276
Preemption disabled at:
[<ffffffff8b50149d>] __schedule_loop kernel/sched/core.c:7310 [inline]
[<ffffffff8b50149d>] schedule+0x15d/0x2b0 kernel/sched/core.c:7326
CPU: 0 UID: 0 PID: 5685 Comm: kworker/0:5 Not tainted syzkaller #1 PREEMPT_{RT,(full)}
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
Workqueue: 0x0 (wg-crypt-wg1)
Call Trace:
<IRQ>
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
instr_sysvec_apic_timer_interrupt arch/x86/kernel/apic/apic.c:1062 [inline]
sysvec_apic_timer_interrupt+0xa1/0xc0 arch/x86/kernel/apic/apic.c:1062
</IRQ>
<TASK>
asm_sysvec_apic_timer_interrupt+0x1a/0x20 arch/x86/include/asm/idtentry.h:674
RIP: 0010:finish_task_switch+0x417/0xc60 kernel/sched/core.c:5361
Code: 04 00 00 41 c7 84 24 20 0e 00 00 00 00 00 00 0f 1f 44 00 00 49 83 c4 48 4c 89 e7 e8 b3 99 bd 09 e8 de c7 38 00 fb 4c 8b 65 c8 <49> 8d bc 24 e0 16 00 00 48 89 f8 48 c1 e8 03 42 0f b6 04 30 84 c0
RSP: 0018:ffffc9000442fa60 EFLAGS: 00000206
RAX: 0000000000013355 RBX: ffff88813883c6e0 RCX: 0000000080000001
RDX: 0000000000000000 RSI: ffffffff8daa4fe1 RDI: ffffffff8bd1a580
RBP: ffffc9000442fab0 R08: ffffffff8fd9f0f7 R09: 1ffffffff1fb3e1e
R10: dffffc0000000000 R11: fffffbfff1fb3e1f R12: ffff888114048000
R13: ffff88813883c6a8 R14: dffffc0000000000 R15: 1ffff110271078dc
context_switch kernel/sched/core.c:5513 [inline]
__schedule+0x1711/0x5530 kernel/sched/core.c:7234
__schedule_loop kernel/sched/core.c:7311 [inline]
schedule+0x164/0x2b0 kernel/sched/core.c:7326
worker_thread+0xc12/0xfb0 kernel/workqueue.c:3501
kthread+0x388/0x470 kernel/kthread.c:436
ret_from_fork+0x514/0xb70 arch/x86/kernel/process.c:158
ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245
</TASK>
=============================
[ BUG: Invalid wait context ]
syzkaller #1 Tainted: G W
-----------------------------
kworker/0:5/5685 is trying to lock:
ffff8881964dd530 (&gpc->lock){....}-{3:3}, at: kvm_xen_set_evtchn_fast+0x1f4/0x990 arch/x86/kvm/xen.c:1822
other info that might help us debug this:
context-{2:2}
1 lock held by kworker/0:5/5685:
#0: ffff8881964ddb48 (&kvm->srcu){.?.+}-{0:0}, at: srcu_lock_acquire include/linux/srcu.h:187 [inline]
#0: ffff8881964ddb48 (&kvm->srcu){.?.+}-{0:0}, at: srcu_read_lock include/linux/srcu.h:294 [inline]
#0: ffff8881964ddb48 (&kvm->srcu){.?.+}-{0:0}, at: kvm_xen_set_evtchn_fast+0x1be/0x990 arch/x86/kvm/xen.c:1820
stack backtrace:
CPU: 0 UID: 0 PID: 5685 Comm: kworker/0:5 Tainted: G W syzkaller #1 PREEMPT_{RT,(full)}
Tainted: [W]=WARN
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
Workqueue: 0x0 (wg-crypt-wg1)
Call Trace:
<IRQ>
dump_stack_lvl+0xe8/0x150 lib/dump_stack.c:120
print_lock_invalid_wait_context kernel/locking/lockdep.c:4830 [inline]
check_wait_context kernel/locking/lockdep.c:4902 [inline]
__lock_acquire+0xe39/0x2ce0 kernel/locking/lockdep.c:5187
lock_acquire+0x106/0x350 kernel/locking/lockdep.c:5868
rt_read_lock+0xcc/0x4b0 kernel/locking/spinlock_rt.c:249
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
instr_sysvec_apic_timer_interrupt arch/x86/kernel/apic/apic.c:1062 [inline]
sysvec_apic_timer_interrupt+0xa1/0xc0 arch/x86/kernel/apic/apic.c:1062
</IRQ>
<TASK>
asm_sysvec_apic_timer_interrupt+0x1a/0x20 arch/x86/include/asm/idtentry.h:674
RIP: 0010:finish_task_switch+0x417/0xc60 kernel/sched/core.c:5361
Code: 04 00 00 41 c7 84 24 20 0e 00 00 00 00 00 00 0f 1f 44 00 00 49 83 c4 48 4c 89 e7 e8 b3 99 bd 09 e8 de c7 38 00 fb 4c 8b 65 c8 <49> 8d bc 24 e0 16 00 00 48 89 f8 48 c1 e8 03 42 0f b6 04 30 84 c0
RSP: 0018:ffffc9000442fa60 EFLAGS: 00000206
RAX: 0000000000013355 RBX: ffff88813883c6e0 RCX: 0000000080000001
RDX: 0000000000000000 RSI: ffffffff8daa4fe1 RDI: ffffffff8bd1a580
RBP: ffffc9000442fab0 R08: ffffffff8fd9f0f7 R09: 1ffffffff1fb3e1e
R10: dffffc0000000000 R11: fffffbfff1fb3e1f R12: ffff888114048000
R13: ffff88813883c6a8 R14: dffffc0000000000 R15: 1ffff110271078dc
context_switch kernel/sched/core.c:5513 [inline]
__schedule+0x1711/0x5530 kernel/sched/core.c:7234
__schedule_loop kernel/sched/core.c:7311 [inline]
schedule+0x164/0x2b0 kernel/sched/core.c:7326
worker_thread+0xc12/0xfb0 kernel/workqueue.c:3501
kthread+0x388/0x470 kernel/kthread.c:436
ret_from_fork+0x514/0xb70 arch/x86/kernel/process.c:158
ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245
</TASK>
----------------
Code disassembly (best guess), 1 bytes skipped:
0: 00 00 add %al,(%rax)
2: 41 c7 84 24 20 0e 00 movl $0x0,0xe20(%r12)
9: 00 00 00 00 00
e: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)
13: 49 83 c4 48 add $0x48,%r12
17: 4c 89 e7 mov %r12,%rdi
1a: e8 b3 99 bd 09 call 0x9bd99d2
1f: e8 de c7 38 00 call 0x38c802
24: fb sti
25: 4c 8b 65 c8 mov -0x38(%rbp),%r12
* 29: 49 8d bc 24 e0 16 00 lea 0x16e0(%r12),%rdi <-- trapping instruction
30: 00
31: 48 89 f8 mov %rdi,%rax
34: 48 c1 e8 03 shr $0x3,%rax
38: 42 0f b6 04 30 movzbl (%rax,%r14,1),%eax
3d: 84 c0 test %al,%al
The explanation of the root cause is:
The root cause of this crash is an attempt to acquire a sleeping lock from a hardirq context on a `PREEMPT_RT` kernel.
### Detailed Analysis
1. **The Lock:**
The crash happens when `kvm_xen_set_evtchn_fast()` attempts to acquire `gpc->lock` via `read_lock_irqsave(&gpc->lock, flags)`. The `gpc->lock` is an `rwlock_t` used to protect the `gfn_to_pfn_cache`.
In a `PREEMPT_RT` kernel, `rwlock_t` is mapped to `rt_rw_lock`, which is a sleeping lock (rtmutex-based). It cannot be acquired in hardirq context because it may sleep while waiting for the lock or while waking up waiters during unlock.
2. **The Context:**
`kvm_xen_set_evtchn_fast()` is called from `xen_timer_callback()`, which is an hrtimer callback initialized with `HRTIMER_MODE_ABS_HARD`. This means the timer callback executes in hardirq context, even on `PREEMPT_RT`.
Additionally, `kvm_xen_set_evtchn_fast()` is also called from `kvm_arch_set_irq_inatomic()`, which is invoked during `irqfd` waitqueue wakeups (also a hardirq/atomic context).
3. **The Lockdep Splat:**
The lockdep splat mentions `srcu_read_lock` because `kvm->srcu` was acquired just before the `read_lock_irqsave`. While `srcu_read_lock` itself is perfectly safe in hardirq context (it just increments a per-CPU counter), lockdep prints it as a "held lock" when the actual sleeping function (`rt_read_lock`) is called.
### How to Fix
To fix this, `kvm_xen_set_evtchn_fast()` must avoid using `read_lock_irqsave()` when called from an atomic context. Instead, it should use `read_trylock()`. If the trylock fails, it can safely fall back to the slow path (returning `-EWOULDBLOCK` or kicking the vCPU), which is exactly what the callers expect and handle.
This pattern of using `read_trylock()` for `gpc->lock` in atomic contexts is already established in KVM Xen (e.g., in `kvm_xen_update_runstate_guest()`).
You should modify `kvm_xen_set_evtchn_fast()` to accept a `bool in_atomic` parameter:
```c
// arch/x86/kvm/xen.h
int kvm_xen_set_evtchn_fast(struct kvm_xen_evtchn *xe, struct kvm *kvm, bool in_atomic);
```
Then update `kvm_xen_set_evtchn_fast()` in `arch/x86/kvm/xen.c` to use `read_trylock()` when `in_atomic` is true:
```c
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);
}
```
You will need to do this for **both** `gpc->lock` acquisitions in that function. For the second acquisition (the `vcpu_info_cache`), if the trylock fails, you should fall back to setting the bit in-kernel and kicking the vCPU, just like it does when `kvm_gpc_check()` fails:
```c
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);
}
```
Make sure to add the `out_rcu_no_lock` label to avoid unlocking a lock you don't hold:
```c
out_rcu:
read_unlock_irqrestore(&gpc->lock, flags);
out_rcu_no_lock:
srcu_read_unlock(&kvm->srcu, idx);
```
Finally, update the callers to pass the correct context:
- `xen_timer_callback()` in `arch/x86/kvm/xen.c`: pass `true`.
- `kvm_arch_set_irq_inatomic()` in `arch/x86/kvm/irq.c`: pass `true`.
- `kvm_xen_set_evtchn()` in `arch/x86/kvm/xen.c` (both calls): pass `false` (since it runs in process context, e.g., from a hypercall or the irqfd workqueue).
The patch that fixes the bug is:
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);
Search for the commit(s) that introduced this bug.
|
| 236/2 |
2026/08/12 12:49 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 11349
output: 45
thoughts: 318
Thoughts: **Pinpointing the Root Cause of the KVM/Xen Sleeping Issue**
Alright, let's get down to brass tacks. We've got a patch that fixes a nasty bug: a sleeping function, specifically `kvm_xen_set_evtchn_fast()`, being called from an invalid context. This is likely on `PREEMPT_RT` due to `read_lock_irqsave()`. The fix involves introducing an `in_atomic` flag and using `read_trylock()` when appropriate.
My immediate focus is zeroing in on the commit that *introduced* this issue. My strategy? Let's trace the evolution of `kvm_xen_set_evtchn_fast()`. I need to figure out when this function was first introduced or when the problematic `read_lock_irqsave(&gpc->lock, flags)` was added. Or, quite possibly, when `xen_timer_callback` (or `kvm_arch_set_irq_inatomic()`) started calling `kvm_xen_set_evtchn_fast()`. That's where I believe the problem is.
My first move is to use `git log` to walk the history. I'll zero in on commits that modify `kvm_xen_set_evtchn_fast` and its related callers. I'll search in `arch/x86/kvm/xen.c`, that's the obvious place to start. Let's see what we can find.
|
| 237/2 |
2026/08/12 12:49 |
tool |
git-log |
0m
Args: map[SourcePath:arch/x86/kvm/xen.c SymbolName:kvm_xen_set_evtchn_fast]
Results: map[Output:003d914220c9 KVM: x86/xen: allow vcpu_info content to be 'safely' copied
58f5ee5fedd9 KVM: Drop @gpa from exported gfn=>pfn cache check() and refresh() helpers
e308c24a358d KVM: Use gfn_to_pfn_cache's immutable "kvm" in kvm_gpc_check()
aba3caef5862 KVM: Shorten gfn_to_pfn_cache function names
79f772b9e800 KVM: x86: Query vcpu->vcpu_idx directly and drop its accessor, again
1a65105a5aba KVM: x86/xen: handle PV spinlocks slowpath
fde0451be8fb KVM: x86/xen: Support per-vCPU event channel upcall via local APIC
8733068b9bdb KVM: x86/xen: Make kvm_xen_set_evtchn() reusable from other places
7caf9571563e KVM: x86/xen: Use gfn_to_pfn_cache for vcpu_info
14243b387137 KVM: x86/xen: Add KVM_IRQ_ROUTING_XEN_EVTCHN and event channel delivery
]
|
| 238/2 |
2026/08/12 12:49 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 12387
output: 25
thoughts: 95
Thoughts: Okay, let's dive into these commits.
**Analyzing Commit Interdependencies**
First, I need to understand the relationship between `14243b387137` and `8733068b9bdb`. That second commit, `8733068b9bdb`, specifically targets the reusability of `kvm_xen_set_evtchn()`. My immediate thought is, "Okay, what's this about? Is this a refactor? Feature addition?" I need to determine the *why* behind this proposed change.
Now, I'm going to carefully compare the two commits. My focus will be on the code changes and the context of those changes to understand *how* `8733068b9bdb` proposes to achieve its goal and how `14243b387137` might be relevant in this context. I need to establish whether one depends on the other, or if they are potentially orthogonal. I'll be looking for shared code, modifications to related data structures, or any obvious interaction points. The ultimate goal is to understand if there is a potential for integration problems or merge conflicts here.
|
| 239/2 |
2026/08/12 12:49 |
tool |
git-show |
0m
Args:
Results: map[Output:commit 8733068b9bdbc7a54f02dcc59eb0e4789cd60942
Author: David Woodhouse <dwmw@amazon.co.uk>
Date: Thu Mar 3 15:41:17 2022 +0000
KVM: x86/xen: Make kvm_xen_set_evtchn() reusable from other places
Clean it up to return -errno on error consistently, while still being
compatible with the return conventions for kvm_arch_set_irq_inatomic()
and the kvm_set_irq() callback.
We use -ENOTCONN to indicate when the port is masked. No existing users
care, except that it's negative.
Also allow it to optimise the vCPU lookup. Unless we abuse the lapic
map, there is no quick lookup from APIC ID to a vCPU; the logic in
kvm_get_vcpu_by_id() will just iterate over all vCPUs till it finds
the one it wants. So do that just once and stash the result in the
struct kvm_xen_evtchn for next time.
Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Message-Id: <20220303154127.202856-8-dwmw2@infradead.org>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
diff --git a/arch/x86/kvm/irq_comm.c b/arch/x86/kvm/irq_comm.c
index 6e0dab04320e..0687162c4f22 100644
--- a/arch/x86/kvm/irq_comm.c
+++ b/arch/x86/kvm/irq_comm.c
@@ -181,7 +181,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, kvm);
+ return kvm_xen_set_evtchn_fast(&e->xen_evtchn, kvm);
#endif
default:
break;
diff --git a/arch/x86/kvm/xen.c b/arch/x86/kvm/xen.c
index 65ffba89441a..9c87263a5be2 100644
--- a/arch/x86/kvm/xen.c
+++ b/arch/x86/kvm/xen.c
@@ -859,13 +859,16 @@ static inline int max_evtchn_port(struct kvm *kvm)
}
/*
- * This follows the kvm_set_irq() API, so it returns:
+ * The return value from this function is propagated to kvm_set_irq() API,
+ * so it returns:
* < 0 Interrupt was ignored (masked or not delivered for other reasons)
* = 0 Interrupt was coalesced (previous irq is still pending)
* > 0 Number of CPUs interrupt was delivered to
+ *
+ * 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_kernel_irq_routing_entry *e,
- struct kvm *kvm)
+int kvm_xen_set_evtchn_fast(struct kvm_xen_evtchn *xe, struct kvm *kvm)
{
struct gfn_to_pfn_cache *gpc = &kvm->arch.xen.shinfo_cache;
struct kvm_vcpu *vcpu;
@@ -873,18 +876,23 @@ int kvm_xen_set_evtchn_fast(struct kvm_kernel_irq_routing_entry *e,
unsigned long flags;
int port_word_bit;
bool kick_vcpu = false;
- int idx;
- int rc;
+ int vcpu_idx, idx, rc;
- vcpu = kvm_get_vcpu_by_id(kvm, e->xen_evtchn.vcpu);
- if (!vcpu)
- return -1;
+ vcpu_idx = READ_ONCE(xe->vcpu_idx);
+ if (vcpu_idx >= 0)
+ vcpu = kvm_get_vcpu(kvm, vcpu_idx);
+ else {
+ vcpu = kvm_get_vcpu_by_id(kvm, xe->vcpu_id);
+ if (!vcpu)
+ return -EINVAL;
+ WRITE_ONCE(xe->vcpu_idx, kvm_vcpu_get_idx(vcpu));
+ }
if (!vcpu->arch.xen.vcpu_info_cache.active)
- return -1;
+ return -EINVAL;
- if (e->xen_evtchn.port >= max_evtchn_port(kvm))
- return -1;
+ if (xe->port >= max_evtchn_port(kvm))
+ return -EINVAL;
rc = -EWOULDBLOCK;
@@ -898,12 +906,12 @@ int kvm_xen_set_evtchn_fast(struct kvm_kernel_irq_routing_entry *e,
struct shared_info *shinfo = gpc->khva;
pending_bits = (unsigned long *)&shinfo->evtchn_pending;
mask_bits = (unsigned long *)&shinfo->evtchn_mask;
- port_word_bit = e->xen_evtchn.port / 64;
+ port_word_bit = xe->port / 64;
} else {
struct compat_shared_info *shinfo = gpc->khva;
pending_bits = (unsigned long *)&shinfo->evtchn_pending;
mask_bits = (unsigned long *)&shinfo->evtchn_mask;
- port_word_bit = e->xen_evtchn.port / 32;
+ port_word_bit = xe->port / 32;
}
/*
@@ -913,10 +921,10 @@ int kvm_xen_set_evtchn_fast(struct kvm_kernel_irq_routing_entry *e,
* already set, then we kick the vCPU in question to write to the
* *real* evtchn_pending_sel in its own guest vcpu_info struct.
*/
- if (test_and_set_bit(e->xen_evtchn.port, pending_bits)) {
+ if (test_and_set_bit(xe->port, pending_bits)) {
rc = 0; /* It was already raised */
- } else if (test_bit(e->xen_evtchn.port, mask_bits)) {
- rc = -1; /* Masked */
+ } else if (test_bit(xe->port, mask_bits)) {
+ rc = -ENOTCONN; /* Masked */
} 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 */
@@ -962,17 +970,12 @@ int kvm_xen_set_evtchn_fast(struct kvm_kernel_irq_routing_entry *e,
return rc;
}
-/* This is the version called from kvm_set_irq() as the .set function */
-static int evtchn_set_fn(struct kvm_kernel_irq_routing_entry *e, struct kvm *kvm,
- int irq_source_id, int level, bool line_status)
+static int kvm_xen_set_evtchn(struct kvm_xen_evtchn *xe, struct kvm *kvm)
{
bool mm_borrowed = false;
int rc;
- if (!level)
- return -1;
-
- rc = kvm_xen_set_evtchn_fast(e, kvm);
+ rc = kvm_xen_set_evtchn_fast(xe, kvm);
if (rc != -EWOULDBLOCK)
return rc;
@@ -1016,7 +1019,7 @@ static int evtchn_set_fn(struct kvm_kernel_irq_routing_entry *e, struct kvm *kvm
struct gfn_to_pfn_cache *gpc = &kvm->arch.xen.shinfo_cache;
int idx;
- rc = kvm_xen_set_evtchn_fast(e, kvm);
+ rc = kvm_xen_set_evtchn_fast(xe, kvm);
if (rc != -EWOULDBLOCK)
break;
@@ -1033,11 +1036,27 @@ static int evtchn_set_fn(struct kvm_kernel_irq_routing_entry *e, struct kvm *kvm
return rc;
}
+/* This is the version called from kvm_set_irq() as the .set function */
+static int evtchn_set_fn(struct kvm_kernel_irq_routing_entry *e, struct kvm *kvm,
+ int irq_source_id, int level, bool line_status)
+{
+ if (!level)
+ return -EINVAL;
+
+ return kvm_xen_set_evtchn(&e->xen_evtchn, kvm);
+}
+
+/*
+ * Set up an event channel interrupt from the KVM IRQ routing table.
+ * Used for e.g. PIRQ from passed through physical devices.
+ */
int kvm_xen_setup_evtchn(struct kvm *kvm,
struct kvm_kernel_irq_routing_entry *e,
const struct kvm_irq_routing_entry *ue)
{
+ struct kvm_vcpu *vcpu;
+
if (ue->u.xen_evtchn.port >= max_evtchn_port(kvm))
return -EINVAL;
@@ -1045,8 +1064,22 @@ int kvm_xen_setup_evtchn(struct kvm *kvm,
if (ue->u.xen_evtchn.priority != KVM_IRQ_ROUTING_XEN_EVTCHN_PRIO_2LEVEL)
return -EINVAL;
+ /*
+ * Xen gives us interesting mappings from vCPU index to APIC ID,
+ * which means kvm_get_vcpu_by_id() has to iterate over all vCPUs
+ * to find it. Do that once at setup time, instead of every time.
+ * But beware that on live update / live migration, the routing
+ * table might be reinstated before the vCPU threads have finished
+ * recreating their vCPUs.
+ */
+ vcpu = kvm_get_vcpu_by_id(kvm, ue->u.xen_evtchn.vcpu);
+ if (vcpu)
+ e->xen_evtchn.vcpu_idx = kvm_vcpu_get_idx(vcpu);
+ else
+ e->xen_evtchn.vcpu_idx = -1;
+
e->xen_evtchn.port = ue->u.xen_evtchn.port;
- e->xen_evtchn.vcpu = ue->u.xen_evtchn.vcpu;
+ e->xen_evtchn.vcpu_id = ue->u.xen_evtchn.vcpu;
e->xen_evtchn.priority = ue->u.xen_evtchn.priority;
e->set = evtchn_set_fn;
diff --git a/arch/x86/kvm/xen.h b/arch/x86/kvm/xen.h
index 7dd0590f93e1..e28feb32add6 100644
--- a/arch/x86/kvm/xen.h
+++ b/arch/x86/kvm/xen.h
@@ -25,7 +25,7 @@ int kvm_xen_hvm_config(struct kvm *kvm, struct kvm_xen_hvm_config *xhc);
void kvm_xen_init_vm(struct kvm *kvm);
void kvm_xen_destroy_vm(struct kvm *kvm);
void kvm_xen_destroy_vcpu(struct kvm_vcpu *vcpu);
-int kvm_xen_set_evtchn_fast(struct kvm_kernel_irq_routing_entry *e,
+int kvm_xen_set_evtchn_fast(struct kvm_xen_evtchn *xe,
struct kvm *kvm);
int kvm_xen_setup_evtchn(struct kvm *kvm,
struct kvm_kernel_irq_routing_entry *e,
diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
index 3f9b22c4983a..252ee4a61b58 100644
--- a/include/linux/kvm_host.h
+++ b/include/linux/kvm_host.h
@@ -611,7 +611,8 @@ struct kvm_hv_sint {
struct kvm_xen_evtchn {
u32 port;
- u32 vcpu;
+ u32 vcpu_id;
+ int vcpu_idx;
u32 priority;
};
]
|
| 240/2 |
2026/08/12 12:49 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 16364
output: 27
thoughts: 18
Thoughts: Okay, let's dive into this. Here's my thought process summarized, assuming I'm already pretty familiar with this kind of work:
**Checking `14243b387137`**
Alright, that's the starting point. The first thing I need to do is examine this identifier, `14243b387137`. I'm immediately running through the possibilities. Is this a commit hash? A version number? A unique ID for a data record? A transaction identifier? The format suggests a hexadecimal representation, likely a cryptographic hash or a short identifier, but without further context, I can only make an educated guess. My mind's running through my mental library: git commits, database keys, API keys, the usual suspects. I'm already looking for the systems I have access to that might contain this specific sequence. Time to pull up the relevant tooling and start digging. I'll need to know where this identifier originated to determine how to proceed. Let's see if I can cross-reference this against any logs or databases I have access to. It's a hunting game now!
|
| 241/2 |
2026/08/12 12:49 |
tool |
git-show |
0m
Args:
Results: map[Output:commit 14243b387137a4afbe1df5d9dc15182d6657bb79
Author: David Woodhouse <dwmw@amazon.co.uk>
Date: Fri Dec 10 16:36:23 2021 +0000
KVM: x86/xen: Add KVM_IRQ_ROUTING_XEN_EVTCHN and event channel delivery
This adds basic support for delivering 2 level event channels to a guest.
Initially, it only supports delivery via the IRQ routing table, triggered
by an eventfd. In order to do so, it has a kvm_xen_set_evtchn_fast()
function which will use the pre-mapped shared_info page if it already
exists and is still valid, while the slow path through the irqfd_inject
workqueue will remap the shared_info page if necessary.
It sets the bits in the shared_info page but not the vcpu_info; that is
deferred to __kvm_xen_has_interrupt() which raises the vector to the
appropriate vCPU.
Add a 'verbose' mode to xen_shinfo_test while adding test cases for this.
Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>
Message-Id: <20211210163625.2886-5-dwmw2@infradead.org>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst
index c168be764707..6b683dfea8f2 100644
--- a/Documentation/virt/kvm/api.rst
+++ b/Documentation/virt/kvm/api.rst
@@ -1799,6 +1799,7 @@ No flags are specified so far, the corresponding field must be set to zero.
struct kvm_irq_routing_msi msi;
struct kvm_irq_routing_s390_adapter adapter;
struct kvm_irq_routing_hv_sint hv_sint;
+ struct kvm_irq_routing_xen_evtchn xen_evtchn;
__u32 pad[8];
} u;
};
@@ -1808,6 +1809,7 @@ No flags are specified so far, the corresponding field must be set to zero.
#define KVM_IRQ_ROUTING_MSI 2
#define KVM_IRQ_ROUTING_S390_ADAPTER 3
#define KVM_IRQ_ROUTING_HV_SINT 4
+ #define KVM_IRQ_ROUTING_XEN_EVTCHN 5
flags:
@@ -1859,6 +1861,20 @@ address_hi must be zero.
__u32 sint;
};
+ struct kvm_irq_routing_xen_evtchn {
+ __u32 port;
+ __u32 vcpu;
+ __u32 priority;
+ };
+
+
+When KVM_CAP_XEN_HVM includes the KVM_XEN_HVM_CONFIG_EVTCHN_2LEVEL bit
+in its indication of supported features, routing to Xen event channels
+is supported. Although the priority field is present, only the value
+KVM_XEN_HVM_CONFIG_EVTCHN_2LEVEL is supported, which means delivery by
+2 level event channels. FIFO event channel support may be added in
+the future.
+
4.55 KVM_SET_TSC_KHZ
--------------------
@@ -7413,6 +7429,7 @@ PVHVM guests. Valid flags are::
#define KVM_XEN_HVM_CONFIG_INTERCEPT_HCALL (1 << 1)
#define KVM_XEN_HVM_CONFIG_SHARED_INFO (1 << 2)
#define KVM_XEN_HVM_CONFIG_RUNSTATE (1 << 2)
+ #define KVM_XEN_HVM_CONFIG_EVTCHN_2LEVEL (1 << 3)
The KVM_XEN_HVM_CONFIG_HYPERCALL_MSR flag indicates that the KVM_XEN_HVM_CONFIG
ioctl is available, for the guest to set its hypercall page.
@@ -7432,6 +7449,10 @@ The KVM_XEN_HVM_CONFIG_RUNSTATE flag indicates that the runstate-related
features KVM_XEN_VCPU_ATTR_TYPE_RUNSTATE_ADDR/_CURRENT/_DATA/_ADJUST are
supported by the KVM_XEN_VCPU_SET_ATTR/KVM_XEN_VCPU_GET_ATTR ioctls.
+The KVM_XEN_HVM_CONFIG_EVTCHN_2LEVEL flag indicates that IRQ routing entries
+of the type KVM_IRQ_ROUTING_XEN_EVTCHN are supported, with the priority
+field set to indicate 2 level event channel delivery.
+
8.31 KVM_CAP_PPC_MULTITCE
-------------------------
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index 6e61e11e750f..623fb7c4992c 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -610,6 +610,7 @@ struct kvm_vcpu_xen {
u64 last_steal;
u64 runstate_entry_time;
u64 runstate_times[4];
+ unsigned long evtchn_pending_sel;
};
struct kvm_vcpu_arch {
diff --git a/arch/x86/kvm/irq_comm.c b/arch/x86/kvm/irq_comm.c
index 39ad02d6dc63..6e0dab04320e 100644
--- a/arch/x86/kvm/irq_comm.c
+++ b/arch/x86/kvm/irq_comm.c
@@ -24,6 +24,7 @@
#include "hyperv.h"
#include "x86.h"
+#include "xen.h"
static int kvm_set_pic_irq(struct kvm_kernel_irq_routing_entry *e,
struct kvm *kvm, int irq_source_id, int level,
@@ -175,6 +176,13 @@ int kvm_arch_set_irq_inatomic(struct kvm_kernel_irq_routing_entry *e,
return r;
break;
+#ifdef CONFIG_KVM_XEN
+ case KVM_IRQ_ROUTING_XEN_EVTCHN:
+ if (!level)
+ return -1;
+
+ return kvm_xen_set_evtchn_fast(e, kvm);
+#endif
default:
break;
}
@@ -310,6 +318,10 @@ int kvm_set_routing_entry(struct kvm *kvm,
e->hv_sint.vcpu = ue->u.hv_sint.vcpu;
e->hv_sint.sint = ue->u.hv_sint.sint;
break;
+#ifdef CONFIG_KVM_XEN
+ case KVM_IRQ_ROUTING_XEN_EVTCHN:
+ return kvm_xen_setup_evtchn(kvm, e, ue);
+#endif
default:
return -EINVAL;
}
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 42bde45a1bc2..3050601d5d73 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -4188,7 +4188,8 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
case KVM_CAP_XEN_HVM:
r = KVM_XEN_HVM_CONFIG_HYPERCALL_MSR |
KVM_XEN_HVM_CONFIG_INTERCEPT_HCALL |
- KVM_XEN_HVM_CONFIG_SHARED_INFO;
+ KVM_XEN_HVM_CONFIG_SHARED_INFO |
+ KVM_XEN_HVM_CONFIG_EVTCHN_2LEVEL;
if (sched_info_on())
r |= KVM_XEN_HVM_CONFIG_RUNSTATE;
break;
diff --git a/arch/x86/kvm/xen.c b/arch/x86/kvm/xen.c
index da4bf2c6407f..ceddabd1f5c6 100644
--- a/arch/x86/kvm/xen.c
+++ b/arch/x86/kvm/xen.c
@@ -16,6 +16,7 @@
#include <trace/events/kvm.h>
#include <xen/interface/xen.h>
#include <xen/interface/vcpu.h>
+#include <xen/interface/event_channel.h>
#include "trace.h"
@@ -195,6 +196,8 @@ void kvm_xen_update_runstate_guest(struct kvm_vcpu *v, int state)
int __kvm_xen_has_interrupt(struct kvm_vcpu *v)
{
+ unsigned long evtchn_pending_sel = READ_ONCE(v->arch.xen.evtchn_pending_sel);
+ bool atomic = in_atomic() || !task_is_running(current);
int err;
u8 rc = 0;
@@ -204,6 +207,9 @@ int __kvm_xen_has_interrupt(struct kvm_vcpu *v)
*/
struct gfn_to_hva_cache *ghc = &v->arch.xen.vcpu_info_cache;
struct kvm_memslots *slots = kvm_memslots(v->kvm);
+ bool ghc_valid = slots->generation == ghc->generation &&
+ !kvm_is_error_hva(ghc->hva) && ghc->memslot;
+
unsigned int offset = offsetof(struct vcpu_info, evtchn_upcall_pending);
/* No need for compat handling here */
@@ -219,8 +225,7 @@ int __kvm_xen_has_interrupt(struct kvm_vcpu *v)
* cache in kvm_read_guest_offset_cached(), but just uses
* __get_user() instead. And falls back to the slow path.
*/
- if (likely(slots->generation == ghc->generation &&
- !kvm_is_error_hva(ghc->hva) && ghc->memslot)) {
+ if (!evtchn_pending_sel && ghc_valid) {
/* Fast path */
pagefault_disable();
err = __get_user(rc, (u8 __user *)ghc->hva + offset);
@@ -239,11 +244,82 @@ int __kvm_xen_has_interrupt(struct kvm_vcpu *v)
* and we'll end up getting called again from a context where we *can*
* fault in the page and wait for it.
*/
- if (in_atomic() || !task_is_running(current))
+ if (atomic)
return 1;
- kvm_read_guest_offset_cached(v->kvm, ghc, &rc, offset,
- sizeof(rc));
+ if (!ghc_valid) {
+ err = kvm_gfn_to_hva_cache_init(v->kvm, ghc, ghc->gpa, ghc->len);
+ if (err || !ghc->memslot) {
+ /*
+ * If this failed, userspace has screwed up the
+ * vcpu_info mapping. No interrupts for you.
+ */
+ return 0;
+ }
+ }
+
+ /*
+ * Now we have a valid (protected by srcu) userspace HVA in
+ * ghc->hva which points to the struct vcpu_info. If there
+ * are any bits in the in-kernel evtchn_pending_sel then
+ * we need to write those to the guest vcpu_info and set
+ * its evtchn_upcall_pending flag. If there aren't any bits
+ * to add, we only want to *check* evtchn_upcall_pending.
+ */
+ if (evtchn_pending_sel) {
+ bool long_mode = v->kvm->arch.xen.long_mode;
+
+ if (!user_access_begin((void __user *)ghc->hva, sizeof(struct vcpu_info)))
+ return 0;
+
+ if (IS_ENABLED(CONFIG_64BIT) && long_mode) {
+ struct vcpu_info __user *vi = (void __user *)ghc->hva;
+
+ /* Attempt to set the evtchn_pending_sel bits in the
+ * guest, and if that succeeds then clear the same
+ * bits in the in-kernel version. */
+ asm volatile("1:\t" LOCK_PREFIX "orq %0, %1\n"
+ "\tnotq %0\n"
+ "\t" LOCK_PREFIX "andq %0, %2\n"
+ "2:\n"
+ "\t.section .fixup,\"ax\"\n"
+ "3:\tjmp\t2b\n"
+ "\t.previous\n"
+ _ASM_EXTABLE_UA(1b, 3b)
+ : "=r" (evtchn_pending_sel),
+ "+m" (vi->evtchn_pending_sel),
+ "+m" (v->arch.xen.evtchn_pending_sel)
+ : "0" (evtchn_pending_sel));
+ } else {
+ struct compat_vcpu_info __user *vi = (void __user *)ghc->hva;
+ u32 evtchn_pending_sel32 = evtchn_pending_sel;
+
+ /* Attempt to set the evtchn_pending_sel bits in the
+ * guest, and if that succeeds then clear the same
+ * bits in the in-kernel version. */
+ asm volatile("1:\t" LOCK_PREFIX "orl %0, %1\n"
+ "\tnotl %0\n"
+ "\t" LOCK_PREFIX "andl %0, %2\n"
+ "2:\n"
+ "\t.section .fixup,\"ax\"\n"
+ "3:\tjmp\t2b\n"
+ "\t.previous\n"
+ _ASM_EXTABLE_UA(1b, 3b)
+ : "=r" (evtchn_pending_sel32),
+ "+m" (vi->evtchn_pending_sel),
+ "+m" (v->arch.xen.evtchn_pending_sel)
+ : "0" (evtchn_pending_sel32));
+ }
+ rc = 1;
+ unsafe_put_user(rc, (u8 __user *)ghc->hva + offset, err);
+
+ err:
+ user_access_end();
+
+ mark_page_dirty_in_slot(v->kvm, ghc->memslot, ghc->gpa >> PAGE_SHIFT);
+ } else {
+ __get_user(rc, (u8 __user *)ghc->hva + offset);
+ }
return rc;
}
@@ -740,3 +816,179 @@ int kvm_xen_hypercall(struct kvm_vcpu *vcpu)
return 0;
}
+
+static inline int max_evtchn_port(struct kvm *kvm)
+{
+ if (IS_ENABLED(CONFIG_64BIT) && kvm->arch.xen.long_mode)
+ return EVTCHN_2L_NR_CHANNELS;
+ else
+ return COMPAT_EVTCHN_2L_NR_CHANNELS;
+}
+
+/*
+ * This follows the kvm_set_irq() API, so it returns:
+ * < 0 Interrupt was ignored (masked or not delivered for other reasons)
+ * = 0 Interrupt was coalesced (previous irq is still pending)
+ * > 0 Number of CPUs interrupt was delivered to
+ */
+int kvm_xen_set_evtchn_fast(struct kvm_kernel_irq_routing_entry *e,
+ struct kvm *kvm)
+{
+ struct gfn_to_pfn_cache *gpc = &kvm->arch.xen.shinfo_cache;
+ struct kvm_vcpu *vcpu;
+ unsigned long *pending_bits, *mask_bits;
+ unsigned long flags;
+ int port_word_bit;
+ bool kick_vcpu = false;
+ int idx;
+ int rc;
+
+ vcpu = kvm_get_vcpu_by_id(kvm, e->xen_evtchn.vcpu);
+ if (!vcpu)
+ return -1;
+
+ if (!vcpu->arch.xen.vcpu_info_set)
+ return -1;
+
+ if (e->xen_evtchn.port >= max_evtchn_port(kvm))
+ return -1;
+
+ rc = -EWOULDBLOCK;
+ read_lock_irqsave(&gpc->lock, flags);
+
+ idx = srcu_read_lock(&kvm->srcu);
+ if (!kvm_gfn_to_pfn_cache_check(kvm, gpc, gpc->gpa, PAGE_SIZE))
+ goto out_rcu;
+
+ if (IS_ENABLED(CONFIG_64BIT) && kvm->arch.xen.long_mode) {
+ struct shared_info *shinfo = gpc->khva;
+ pending_bits = (unsigned long *)&shinfo->evtchn_pending;
+ mask_bits = (unsigned long *)&shinfo->evtchn_mask;
+ port_word_bit = e->xen_evtchn.port / 64;
+ } else {
+ struct compat_shared_info *shinfo = gpc->khva;
+ pending_bits = (unsigned long *)&shinfo->evtchn_pending;
+ mask_bits = (unsigned long *)&shinfo->evtchn_mask;
+ port_word_bit = e->xen_evtchn.port / 32;
+ }
+
+ /*
+ * If this port wasn't already set, and if it isn't masked, then
+ * we try to set the corresponding bit in the in-kernel shadow of
+ * evtchn_pending_sel for the target vCPU. And if *that* wasn't
+ * already set, then we kick the vCPU in question to write to the
+ * *real* evtchn_pending_sel in its own guest vcpu_info struct.
+ */
+ if (test_and_set_bit(e->xen_evtchn.port, pending_bits)) {
+ rc = 0; /* It was already raised */
+ } else if (test_bit(e->xen_evtchn.port, mask_bits)) {
+ rc = -1; /* Masked */
+ } else {
+ rc = 1; /* Delivered. But was the vCPU waking already? */
+ if (!test_and_set_bit(port_word_bit, &vcpu->arch.xen.evtchn_pending_sel))
+ kick_vcpu = true;
+ }
+
+ out_rcu:
+ srcu_read_unlock(&kvm->srcu, idx);
+ read_unlock_irqrestore(&gpc->lock, flags);
+
+ if (kick_vcpu) {
+ kvm_make_request(KVM_REQ_EVENT, vcpu);
+ kvm_vcpu_kick(vcpu);
+ }
+
+ return rc;
+}
+
+/* This is the version called from kvm_set_irq() as the .set function */
+static int evtchn_set_fn(struct kvm_kernel_irq_routing_entry *e, struct kvm *kvm,
+ int irq_source_id, int level, bool line_status)
+{
+ bool mm_borrowed = false;
+ int rc;
+
+ if (!level)
+ return -1;
+
+ rc = kvm_xen_set_evtchn_fast(e, kvm);
+ if (rc != -EWOULDBLOCK)
+ return rc;
+
+ if (current->mm != kvm->mm) {
+ /*
+ * If not on a thread which already belongs to this KVM,
+ * we'd better be in the irqfd workqueue.
+ */
+ if (WARN_ON_ONCE(current->mm))
+ return -EINVAL;
+
+ kthread_use_mm(kvm->mm);
+ mm_borrowed = true;
+ }
+
+ /*
+ * For the irqfd workqueue, using the main kvm->lock mutex is
+ * fine since this function is invoked from kvm_set_irq() with
+ * no other lock held, no srcu. In future if it will be called
+ * directly from a vCPU thread (e.g. on hypercall for an IPI)
+ * then it may need to switch to using a leaf-node mutex for
+ * serializing the shared_info mapping.
+ */
+ mutex_lock(&kvm->lock);
+
+ /*
+ * It is theoretically possible for the page to be unmapped
+ * and the MMU notifier to invalidate the shared_info before
+ * we even get to use it. In that case, this looks like an
+ * infinite loop. It was tempting to do it via the userspace
+ * HVA instead... but that just *hides* the fact that it's
+ * an infinite loop, because if a fault occurs and it waits
+ * for the page to come back, it can *still* immediately
+ * fault and have to wait again, repeatedly.
+ *
+ * Conversely, the page could also have been reinstated by
+ * another thread before we even obtain the mutex above, so
+ * check again *first* before remapping it.
+ */
+ do {
+ struct gfn_to_pfn_cache *gpc = &kvm->arch.xen.shinfo_cache;
+ int idx;
+
+ rc = kvm_xen_set_evtchn_fast(e, kvm);
+ if (rc != -EWOULDBLOCK)
+ break;
+
+ idx = srcu_read_lock(&kvm->srcu);
+ rc = kvm_gfn_to_pfn_cache_refresh(kvm, gpc, gpc->gpa,
+ PAGE_SIZE, false);
+ srcu_read_unlock(&kvm->srcu, idx);
+ } while(!rc);
+
+ mutex_unlock(&kvm->lock);
+
+ if (mm_borrowed)
+ kthread_unuse_mm(kvm->mm);
+
+ return rc;
+}
+
+int kvm_xen_setup_evtchn(struct kvm *kvm,
+ struct kvm_kernel_irq_routing_entry *e,
+ const struct kvm_irq_routing_entry *ue)
+
+{
+ if (ue->u.xen_evtchn.port >= max_evtchn_port(kvm))
+ return -EINVAL;
+
+ /* We only support 2 level event channels for now */
+ if (ue->u.xen_evtchn.priority != KVM_IRQ_ROUTING_XEN_EVTCHN_PRIO_2LEVEL)
+ return -EINVAL;
+
+ e->xen_evtchn.port = ue->u.xen_evtchn.port;
+ e->xen_evtchn.vcpu = ue->u.xen_evtchn.vcpu;
+ e->xen_evtchn.priority = ue->u.xen_evtchn.priority;
+ e->set = evtchn_set_fn;
+
+ return 0;
+}
diff --git a/arch/x86/kvm/xen.h b/arch/x86/kvm/xen.h
index cc0cf5f37450..adbcc9ed59db 100644
--- a/arch/x86/kvm/xen.h
+++ b/arch/x86/kvm/xen.h
@@ -24,6 +24,12 @@ int kvm_xen_hvm_config(struct kvm *kvm, struct kvm_xen_hvm_config *xhc);
void kvm_xen_init_vm(struct kvm *kvm);
void kvm_xen_destroy_vm(struct kvm *kvm);
+int kvm_xen_set_evtchn_fast(struct kvm_kernel_irq_routing_entry *e,
+ struct kvm *kvm);
+int kvm_xen_setup_evtchn(struct kvm *kvm,
+ struct kvm_kernel_irq_routing_entry *e,
+ const struct kvm_irq_routing_entry *ue);
+
static inline bool kvm_xen_msr_enabled(struct kvm *kvm)
{
return static_branch_unlikely(&kvm_xen_enabled.key) &&
@@ -134,6 +140,9 @@ struct compat_shared_info {
struct compat_arch_shared_info arch;
};
+#define COMPAT_EVTCHN_2L_NR_CHANNELS (8 * \
+ sizeof_field(struct compat_shared_info, \
+ evtchn_pending))
struct compat_vcpu_runstate_info {
int state;
uint64_t state_entry_time;
diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
index 9bbb1f1d9e48..3c47b146851a 100644
--- a/include/linux/kvm_host.h
+++ b/include/linux/kvm_host.h
@@ -497,6 +497,12 @@ struct kvm_hv_sint {
u32 sint;
};
+struct kvm_xen_evtchn {
+ u32 port;
+ u32 vcpu;
+ u32 priority;
+};
+
struct kvm_kernel_irq_routing_entry {
u32 gsi;
u32 type;
@@ -517,6 +523,7 @@ struct kvm_kernel_irq_routing_entry {
} msi;
struct kvm_s390_adapter_int adapter;
struct kvm_hv_sint hv_sint;
+ struct kvm_xen_evtchn xen_evtchn;
};
struct hlist_node link;
};
diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
index 469f05d69c8d..fbfd70d965c6 100644
--- a/include/uapi/linux/kvm.h
+++ b/include/uapi/linux/kvm.h
@@ -1163,11 +1163,20 @@ struct kvm_irq_routing_hv_sint {
__u32 sint;
};
+struct kvm_irq_routing_xen_evtchn {
+ __u32 port;
+ __u32 vcpu;
+ __u32 priority;
+};
+
+#define KVM_IRQ_ROUTING_XEN_EVTCHN_PRIO_2LEVEL ((__u32)(-1))
+
/* gsi routing entry types */
#define KVM_IRQ_ROUTING_IRQCHIP 1
#define KVM_IRQ_ROUTING_MSI 2
#define KVM_IRQ_ROUTING_S390_ADAPTER 3
#define KVM_IRQ_ROUTING_HV_SINT 4
+#define KVM_IRQ_ROUTING_XEN_EVTCHN 5
struct kvm_irq_routing_entry {
__u32 gsi;
@@ -1179,6 +1188,7 @@ struct kvm_irq_routing_entry {
struct kvm_irq_routing_msi msi;
struct kvm_irq_routing_s390_adapter adapter;
struct kvm_irq_routing_hv_sint hv_sint;
+ struct kvm_irq_routing_xen_evtchn xen_evtchn;
__u32 pad[8];
} u;
};
@@ -1209,6 +1219,7 @@ struct kvm_x86_mce {
#define KVM_XEN_HVM_CONFIG_INTERCEPT_HCALL (1 << 1)
#define KVM_XEN_HVM_CONFIG_SHARED_INFO (1 << 2)
#define KVM_XEN_HVM_CONFIG_RUNSTATE (1 << 3)
+#define KVM_XEN_HVM_CONFIG_EVTCHN_2LEVEL (1 << 4)
struct kvm_xen_hvm_config {
__u32 flags;
diff --git a/tools/testing/selftests/kvm/x86_64/xen_shinfo_test.c b/tools/testing/selftests/kvm/x86_64/xen_shinfo_test.c
index a0699f00b3d6..478e0ae8b93e 100644
--- a/tools/testing/selftests/kvm/x86_64/xen_shinfo_test.c
+++ b/tools/testing/selftests/kvm/x86_64/xen_shinfo_test.c
@@ -14,6 +14,9 @@
#include <stdint.h>
#include <time.h>
#include <sched.h>
+#include <signal.h>
+
+#include <sys/eventfd.h>
#define VCPU_ID 5
@@ -22,10 +25,15 @@
#define SHINFO_REGION_SLOT 10
#define PAGE_SIZE 4096
+#define DUMMY_REGION_GPA (SHINFO_REGION_GPA + (2 * PAGE_SIZE))
+#define DUMMY_REGION_SLOT 11
+
+#define SHINFO_ADDR (SHINFO_REGION_GPA)
#define PVTIME_ADDR (SHINFO_REGION_GPA + PAGE_SIZE)
#define RUNSTATE_ADDR (SHINFO_REGION_GPA + PAGE_SIZE + 0x20)
#define VCPU_INFO_ADDR (SHINFO_REGION_GPA + 0x40)
+#define SHINFO_VADDR (SHINFO_REGION_GVA)
#define RUNSTATE_VADDR (SHINFO_REGION_GVA + PAGE_SIZE + 0x20)
#define VCPU_INFO_VADDR (SHINFO_REGION_GVA + 0x40)
@@ -73,15 +81,37 @@ struct vcpu_info {
struct pvclock_vcpu_time_info time;
}; /* 64 bytes (x86) */
+struct shared_info {
+ struct vcpu_info vcpu_info[32];
+ unsigned long evtchn_pending[64];
+ unsigned long evtchn_mask[64];
+ struct pvclock_wall_clock wc;
+ uint32_t wc_sec_hi;
+ /* arch_shared_info here */
+};
+
#define RUNSTATE_running 0
#define RUNSTATE_runnable 1
#define RUNSTATE_blocked 2
#define RUNSTATE_offline 3
+static const char *runstate_names[] = {
+ "running",
+ "runnable",
+ "blocked",
+ "offline"
+};
+
+struct {
+ struct kvm_irq_routing info;
+ struct kvm_irq_routing_entry entries[2];
+} irq_routes;
+
static void evtchn_handler(struct ex_regs *regs)
{
struct vcpu_info *vi = (void *)VCPU_INFO_VADDR;
vi->evtchn_upcall_pending = 0;
+ vi->evtchn_pending_sel = 0;
GUEST_SYNC(0x20);
}
@@ -127,7 +157,25 @@ static void guest_code(void)
GUEST_SYNC(6);
GUEST_ASSERT(rs->time[RUNSTATE_runnable] >= MIN_STEAL_TIME);
- GUEST_DONE();
+ /* Attempt to deliver a *masked* interrupt */
+ GUEST_SYNC(7);
+
+ /* Wait until we see the bit set */
+ struct shared_info *si = (void *)SHINFO_VADDR;
+ while (!si->evtchn_pending[0])
+ __asm__ __volatile__ ("rep nop" : : : "memory");
+
+ /* Now deliver an *unmasked* interrupt */
+ GUEST_SYNC(8);
+
+ while (!si->evtchn_pending[1])
+ __asm__ __volatile__ ("rep nop" : : : "memory");
+
+ /* Change memslots and deliver an interrupt */
+ GUEST_SYNC(9);
+
+ for (;;)
+ __asm__ __volatile__ ("rep nop" : : : "memory");
}
static int cmp_timespec(struct timespec *a, struct timespec *b)
@@ -144,9 +192,18 @@ static int cmp_timespec(struct timespec *a, struct timespec *b)
return 0;
}
+static void handle_alrm(int sig)
+{
+ TEST_FAIL("IRQ delivery timed out");
+}
+
int main(int argc, char *argv[])
{
struct timespec min_ts, max_ts, vm_ts;
+ bool verbose;
+
+ verbose = argc > 1 && (!strncmp(argv[1], "-v", 3) ||
+ !strncmp(argv[1], "--verbose", 10));
int xen_caps = kvm_check_cap(KVM_CAP_XEN_HVM);
if (!(xen_caps & KVM_XEN_HVM_CONFIG_SHARED_INFO) ) {
@@ -155,6 +212,7 @@ int main(int argc, char *argv[])
}
bool do_runstate_tests = !!(xen_caps & KVM_XEN_HVM_CONFIG_RUNSTATE);
+ bool do_eventfd_tests = !!(xen_caps & KVM_XEN_HVM_CONFIG_EVTCHN_2LEVEL);
clock_gettime(CLOCK_REALTIME, &min_ts);
@@ -166,6 +224,11 @@ int main(int argc, char *argv[])
SHINFO_REGION_GPA, SHINFO_REGION_SLOT, 2, 0);
virt_map(vm, SHINFO_REGION_GVA, SHINFO_REGION_GPA, 2);
+ struct shared_info *shinfo = addr_gpa2hva(vm, SHINFO_VADDR);
+
+ int zero_fd = open("/dev/zero", O_RDONLY);
+ TEST_ASSERT(zero_fd != -1, "Failed to open /dev/zero");
+
struct kvm_xen_hvm_config hvmc = {
.flags = KVM_XEN_HVM_CONFIG_INTERCEPT_HCALL,
.msr = XEN_HYPERCALL_MSR,
@@ -184,6 +247,16 @@ int main(int argc, char *argv[])
};
vm_ioctl(vm, KVM_XEN_HVM_SET_ATTR, &ha);
+ /*
+ * Test what happens when the HVA of the shinfo page is remapped after
+ * the kernel has a reference to it. But make sure we copy the clock
+ * info over since that's only set at setup time, and we test it later.
+ */
+ struct pvclock_wall_clock wc_copy = shinfo->wc;
+ void *m = mmap(shinfo, PAGE_SIZE, PROT_READ|PROT_WRITE, MAP_FIXED|MAP_PRIVATE, zero_fd, 0);
+ TEST_ASSERT(m == shinfo, "Failed to map /dev/zero over shared info");
+ shinfo->wc = wc_copy;
+
struct kvm_xen_vcpu_attr vi = {
.type = KVM_XEN_VCPU_ATTR_TYPE_VCPU_INFO,
.u.gpa = VCPU_INFO_ADDR,
@@ -214,6 +287,49 @@ int main(int argc, char *argv[])
vcpu_ioctl(vm, VCPU_ID, KVM_XEN_VCPU_SET_ATTR, &st);
}
+ int irq_fd[2] = { -1, -1 };
+
+ if (do_eventfd_tests) {
+ irq_fd[0] = eventfd(0, 0);
+ irq_fd[1] = eventfd(0, 0);
+
+ /* Unexpected, but not a KVM failure */
+ if (irq_fd[0] == -1 || irq_fd[1] == -1)
+ do_eventfd_tests = false;
+ }
+
+ if (do_eventfd_tests) {
+ irq_routes.info.nr = 2;
+
+ irq_routes.entries[0].gsi = 32;
+ irq_routes.entries[0].type = KVM_IRQ_ROUTING_XEN_EVTCHN;
+ irq_routes.entries[0].u.xen_evtchn.port = 15;
+ irq_routes.entries[0].u.xen_evtchn.vcpu = VCPU_ID;
+ irq_routes.entries[0].u.xen_evtchn.priority = KVM_IRQ_ROUTING_XEN_EVTCHN_PRIO_2LEVEL;
+
+ irq_routes.entries[1].gsi = 33;
+ irq_routes.entries[1].type = KVM_IRQ_ROUTING_XEN_EVTCHN;
+ irq_routes.entries[1].u.xen_evtchn.port = 66;
+ irq_routes.entries[1].u.xen_evtchn.vcpu = VCPU_ID;
+ irq_routes.entries[1].u.xen_evtchn.priority = KVM_IRQ_ROUTING_XEN_EVTCHN_PRIO_2LEVEL;
+
+ vm_ioctl(vm, KVM_SET_GSI_ROUTING, &irq_routes);
+
+ struct kvm_irqfd ifd = { };
+
+ ifd.fd = irq_fd[0];
+ ifd.gsi = 32;
+ vm_ioctl(vm, KVM_IRQFD, &ifd);
+
+ ifd.fd = irq_fd[1];
+ ifd.gsi = 33;
+ vm_ioctl(vm, KVM_IRQFD, &ifd);
+
+ struct sigaction sa = { };
+ sa.sa_handler = handle_alrm;
+ sigaction(SIGALRM, &sa, NULL);
+ }
+
struct vcpu_info *vinfo = addr_gpa2hva(vm, VCPU_INFO_VADDR);
vinfo->evtchn_upcall_pending = 0;
@@ -248,6 +364,8 @@ int main(int argc, char *argv[])
switch (uc.args[1]) {
case 0:
+ if (verbose)
+ printf("Delivering evtchn upcall\n");
evtchn_irq_expected = true;
vinfo->evtchn_upcall_pending = 1;
break;
@@ -256,11 +374,16 @@ int main(int argc, char *argv[])
TEST_ASSERT(!evtchn_irq_expected, "Event channel IRQ not seen");
if (!do_runstate_tests)
goto done;
+ if (verbose)
+ printf("Testing runstate %s\n", runstate_names[uc.args[1]]);
rst.type = KVM_XEN_VCPU_ATTR_TYPE_RUNSTATE_CURRENT;
rst.u.runstate.state = uc.args[1];
vcpu_ioctl(vm, VCPU_ID, KVM_XEN_VCPU_SET_ATTR, &rst);
break;
+
case 4:
+ if (verbose)
+ printf("Testing RUNSTATE_ADJUST\n");
rst.type = KVM_XEN_VCPU_ATTR_TYPE_RUNSTATE_ADJUST;
memset(&rst.u, 0, sizeof(rst.u));
rst.u.runstate.state = (uint64_t)-1;
@@ -274,6 +397,8 @@ int main(int argc, char *argv[])
break;
case 5:
+ if (verbose)
+ printf("Testing RUNSTATE_DATA\n");
rst.type = KVM_XEN_VCPU_ATTR_TYPE_RUNSTATE_DATA;
memset(&rst.u, 0, sizeof(rst.u));
rst.u.runstate.state = RUNSTATE_running;
@@ -282,16 +407,54 @@ int main(int argc, char *argv[])
rst.u.runstate.time_offline = 0x5a;
vcpu_ioctl(vm, VCPU_ID, KVM_XEN_VCPU_SET_ATTR, &rst);
break;
+
case 6:
+ if (verbose)
+ printf("Testing steal time\n");
/* Yield until scheduler delay exceeds target */
rundelay = get_run_delay() + MIN_STEAL_TIME;
do {
sched_yield();
} while (get_run_delay() < rundelay);
break;
+
+ case 7:
+ if (!do_eventfd_tests)
+ goto done;
+ if (verbose)
+ printf("Testing masked event channel\n");
+ shinfo->evtchn_mask[0] = 0x8000;
+ eventfd_write(irq_fd[0], 1UL);
+ alarm(1);
+ break;
+
+ case 8:
+ if (verbose)
+ printf("Testing unmasked event channel\n");
+ /* Unmask that, but deliver the other one */
+ shinfo->evtchn_pending[0] = 0;
+ shinfo->evtchn_mask[0] = 0;
+ eventfd_write(irq_fd[1], 1UL);
+ evtchn_irq_expected = true;
+ alarm(1);
+ break;
+
+ case 9:
+ if (verbose)
+ printf("Testing event channel after memslot change\n");
+ vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS,
+ DUMMY_REGION_GPA, DUMMY_REGION_SLOT, 1, 0);
+ eventfd_write(irq_fd[0], 1UL);
+ evtchn_irq_expected = true;
+ alarm(1);
+ break;
+
case 0x20:
TEST_ASSERT(evtchn_irq_expected, "Unexpected event channel IRQ");
evtchn_irq_expected = false;
+ if (shinfo->evtchn_pending[1] &&
+ shinfo->evtchn_pending[0])
+ goto done;
break;
}
break;
@@ -318,6 +481,16 @@ int main(int argc, char *argv[])
ti = addr_gpa2hva(vm, SHINFO_REGION_GPA + 0x40 + 0x20);
ti2 = addr_gpa2hva(vm, PVTIME_ADDR);
+ if (verbose) {
+ printf("Wall clock (v %d) %d.%09d\n", wc->version, wc->sec, wc->nsec);
+ printf("Time info 1: v %u tsc %" PRIu64 " time %" PRIu64 " mul %u shift %u flags %x\n",
+ ti->version, ti->tsc_timestamp, ti->system_time, ti->tsc_to_system_mul,
+ ti->tsc_shift, ti->flags);
+ printf("Time info 2: v %u tsc %" PRIu64 " time %" PRIu64 " mul %u shift %u flags %x\n",
+ ti2->version, ti2->tsc_timestamp, ti2->system_time, ti2->tsc_to_system_mul,
+ ti2->tsc_shift, ti2->flags);
+ }
+
vm_ts.tv_sec = wc->sec;
vm_ts.tv_nsec = wc->nsec;
TEST_ASSERT(wc->version && !(wc->version & 1),
@@ -341,6 +514,15 @@ int main(int argc, char *argv[])
};
vcpu_ioctl(vm, VCPU_ID, KVM_XEN_VCPU_GET_ATTR, &rst);
+ if (verbose) {
+ printf("Runstate: %s(%d), entry %" PRIu64 " ns\n",
+ rs->state <= RUNSTATE_offline ? runstate_names[rs->state] : "unknown",
+ rs->state, rs->state_entry_time);
+ for (int i = RUNSTATE_running; i <= RUNSTATE_offline; i++) {
+ printf("State %s: %" PRIu64 " ns\n",
+ runstate_names[i], rs->time[i]);
+ }
+ }
TEST_ASSERT(rs->state == rst.u.runstate.state, "Runstate mismatch");
TEST_ASSERT(rs->state_entry_time == rst.u.runstate.state_entry_time,
"State entry time mismatch");
]
|
| 242/2 |
2026/08/12 12:49 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 28861
output: 39
thoughts: 774
Thoughts: **Analyzing a Potential Race Condition in KVM/Xen Interaction**
Okay, so I'm looking at commit `14243b387137a4afbe1df5d9dc15182d6657bb79` and the introduction of `kvm_xen_set_evtchn_fast`. My initial thought is to quickly verify the use of `read_lock_irqsave(&gpc->lock, flags)` within that function. Yes, there it is, right at the beginning.
Now, the bug report says there's a crash when acquiring `gpc->lock`. Specifically, the bug claims `kvm_xen_set_evtchn_fast()` is called from `xen_timer_callback()`, and that's the key. Because, if `xen_timer_callback` is running, it means we are in `HRTIMER_MODE_ABS_HARD` or `irqfd` context (atomically), which is where things get interesting. `read_lock_irqsave()` *shouldn't* be used in a hardirq or atomic context, so, if that's true, we have a serious issue.
My next step is to *confirm* the claim that `xen_timer_callback` is calling `kvm_xen_set_evtchn_fast`. That information is critical. Did this happen in `14243b387137a4afbe1df5d9dc15182d6657bb79` or was it introduced later? I'll need to trace back the code history to see exactly when `xen_timer_callback` started calling this function. That will give me a timeline, and help pinpoint the actual origin of the race condition.
|
| 243/2 |
2026/08/12 12:50 |
tool |
git-log |
0m
Args: map[SourcePath:arch/x86/kvm/xen.c SymbolName:xen_timer_callback]
Results: map[Output:77c9b9dea4fb KVM: x86/xen: Use fast path for Xen timer delivery
536395260582 KVM: x86/xen: handle PV timers oneshot mode
]
|
| 244/2 |
2026/08/12 12:50 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 30139
output: 24
thoughts: 15
Thoughts: Okay, here's my interpretation of that:
**Investigating a Hash Value**
Right, let's take a look at this hash: `77c9b9dea4fb`. It's just a sequence of characters, but I know it represents a digital fingerprint, a cryptographic checksum. The context is crucial; without it, this is just a string. I need to figure out what it's hashing, and what level of analysis is warranted. Is this a file hash, a password hash, or something else entirely? What tools am I going to use? What is my goal with this information? I'm going to have to make some assumptions and start with the most likely scenarios before doing anything else.
|
| 245/2 |
2026/08/12 12:50 |
tool |
git-show |
0m
Args:
Results: map[Output:commit 77c9b9dea4fb3e51e0d850db7f21cb1156d987bd
Author: David Woodhouse <dwmw@amazon.co.uk>
Date: Sat Sep 30 14:58:35 2023 +0100
KVM: x86/xen: Use fast path for Xen timer delivery
Most of the time there's no need to kick the vCPU and deliver the timer
event through kvm_xen_inject_timer_irqs(). Use kvm_xen_set_evtchn_fast()
directly from the timer callback, and only fall back to the slow path if
delivering the timer would block, i.e. if kvm_xen_set_evtchn_fast()
returns -EWOULDBLOCK. If delivery fails for any other reason, do nothing
and just let it fail silently, as that is what the slow path would end up
doing anyways.
This gives a significant improvement in timer latency testing (using
nanosleep() for various periods and then measuring the actual time
elapsed).
However, there was a reason[1] the fast path was dropped when this support
was first added. The current code holds vcpu->mutex for all operations on
the kvm->arch.timer_expires field, and the fast path introduces a
potential race condition. Avoid that race by ensuring the hrtimer is
(temporarily) cancelled before making changes in kvm_xen_start_timer(),
and also when reading the values out for KVM_XEN_VCPU_ATTR_TYPE_TIMER.
[1] https://lore.kernel.org/kvm/846caa99-2e42-4443-1070-84e49d2f11d2@redhat.com
Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>
Reviewed-by: Paul Durrant <paul@xen.org>
Link: https://lore.kernel.org/r/f21ee3bd852761e7808240d4ecaec3013c649dc7.camel@infradead.org
[sean: massage changelog]
Signed-off-by: Sean Christopherson <seanjc@google.com>
diff --git a/arch/x86/kvm/xen.c b/arch/x86/kvm/xen.c
index 40edf4d1974c..75586da134b3 100644
--- a/arch/x86/kvm/xen.c
+++ b/arch/x86/kvm/xen.c
@@ -134,9 +134,23 @@ static enum hrtimer_restart xen_timer_callback(struct hrtimer *timer)
{
struct kvm_vcpu *vcpu = container_of(timer, struct kvm_vcpu,
arch.xen.timer);
+ struct kvm_xen_evtchn e;
+ int rc;
+
if (atomic_read(&vcpu->arch.xen.timer_pending))
return HRTIMER_NORESTART;
+ e.vcpu_id = vcpu->vcpu_id;
+ e.vcpu_idx = vcpu->vcpu_idx;
+ 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);
+ if (rc != -EWOULDBLOCK) {
+ vcpu->arch.xen.timer_expires = 0;
+ return HRTIMER_NORESTART;
+ }
+
atomic_inc(&vcpu->arch.xen.timer_pending);
kvm_make_request(KVM_REQ_UNBLOCK, vcpu);
kvm_vcpu_kick(vcpu);
@@ -146,6 +160,14 @@ static enum hrtimer_restart xen_timer_callback(struct hrtimer *timer)
static void kvm_xen_start_timer(struct kvm_vcpu *vcpu, u64 guest_abs, s64 delta_ns)
{
+ /*
+ * Avoid races with the old timer firing. Checking timer_expires
+ * to avoid calling hrtimer_cancel() will only have false positives
+ * so is fine.
+ */
+ if (vcpu->arch.xen.timer_expires)
+ hrtimer_cancel(&vcpu->arch.xen.timer);
+
atomic_set(&vcpu->arch.xen.timer_pending, 0);
vcpu->arch.xen.timer_expires = guest_abs;
@@ -1019,9 +1041,36 @@ int kvm_xen_vcpu_get_attr(struct kvm_vcpu *vcpu, struct kvm_xen_vcpu_attr *data)
break;
case KVM_XEN_VCPU_ATTR_TYPE_TIMER:
+ /*
+ * Ensure a consistent snapshot of state is captured, with a
+ * timer either being pending, or the event channel delivered
+ * to the corresponding bit in the shared_info. Not still
+ * lurking in the timer_pending flag for deferred delivery.
+ * Purely as an optimisation, if the timer_expires field is
+ * zero, that means the timer isn't active (or even in the
+ * timer_pending flag) and there is no need to cancel it.
+ */
+ if (vcpu->arch.xen.timer_expires) {
+ hrtimer_cancel(&vcpu->arch.xen.timer);
+ kvm_xen_inject_timer_irqs(vcpu);
+ }
+
data->u.timer.port = vcpu->arch.xen.timer_virq;
data->u.timer.priority = KVM_IRQ_ROUTING_XEN_EVTCHN_PRIO_2LEVEL;
data->u.timer.expires_ns = vcpu->arch.xen.timer_expires;
+
+ /*
+ * The hrtimer may trigger and raise the IRQ immediately,
+ * while the returned state causes it to be set up and
+ * raised again on the destination system after migration.
+ * That's fine, as the guest won't even have had a chance
+ * to run and handle the interrupt. Asserting an already
+ * pending event channel is idempotent.
+ */
+ if (vcpu->arch.xen.timer_expires)
+ hrtimer_start_expires(&vcpu->arch.xen.timer,
+ HRTIMER_MODE_ABS_HARD);
+
r = 0;
break;
]
|
| 246/2 |
2026/08/12 12:50 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 32057
output: 50
thoughts: 321
Thoughts: **Analyzing a KVM/Xen Concurrency Bug**
Okay, here's what I'm seeing. This commit, `77c9b9dea4fb`, introduced the `kvm_xen_set_evtchn_fast` call from `xen_timer_callback`. Right, got it. But the bug report... aha, it says the crash happens when `kvm_xen_set_evtchn_fast()` tries to grab `gpc->lock` using `read_lock_irqsave(&gpc->lock, flags)`. Now, the key is the context.
`xen_timer_callback()` is an `hrtimer` callback, initialized with `HRTIMER_MODE_ABS_HARD`. That means it's running in hardirq context, even under `PREEMPT_RT`. So that's one source of contention. But `kvm_xen_set_evtchn_fast()` also gets called from `kvm_arch_set_irq_inatomic()`. Interesting. And that one is triggered by `irqfd` waitqueue wakeups, which are also happening in hardirq/atomic context!
So we have two paths, both potentially in hardirq context, calling the same function and trying to lock the same resource. This is classic concurrency nightmare stuff. The question now becomes when did `kvm_arch_set_irq_inatomic()` *start* calling `kvm_xen_set_evtchn_fast()`? That's the missing piece. Let's dig into commit `14243b387137a4afbe1df5d9dc15182d6657bb79`. That's where I need to start.
|
| 247/2 |
2026/08/12 12:50 |
tool |
git-show |
0m
Args: map[Commit:14243b387137a4afbe1df5d9dc15182d6657bb79]
Results: map[Output:commit 14243b387137a4afbe1df5d9dc15182d6657bb79
Author: David Woodhouse <dwmw@amazon.co.uk>
Date: Fri Dec 10 16:36:23 2021 +0000
KVM: x86/xen: Add KVM_IRQ_ROUTING_XEN_EVTCHN and event channel delivery
This adds basic support for delivering 2 level event channels to a guest.
Initially, it only supports delivery via the IRQ routing table, triggered
by an eventfd. In order to do so, it has a kvm_xen_set_evtchn_fast()
function which will use the pre-mapped shared_info page if it already
exists and is still valid, while the slow path through the irqfd_inject
workqueue will remap the shared_info page if necessary.
It sets the bits in the shared_info page but not the vcpu_info; that is
deferred to __kvm_xen_has_interrupt() which raises the vector to the
appropriate vCPU.
Add a 'verbose' mode to xen_shinfo_test while adding test cases for this.
Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>
Message-Id: <20211210163625.2886-5-dwmw2@infradead.org>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst
index c168be764707..6b683dfea8f2 100644
--- a/Documentation/virt/kvm/api.rst
+++ b/Documentation/virt/kvm/api.rst
@@ -1799,6 +1799,7 @@ No flags are specified so far, the corresponding field must be set to zero.
struct kvm_irq_routing_msi msi;
struct kvm_irq_routing_s390_adapter adapter;
struct kvm_irq_routing_hv_sint hv_sint;
+ struct kvm_irq_routing_xen_evtchn xen_evtchn;
__u32 pad[8];
} u;
};
@@ -1808,6 +1809,7 @@ No flags are specified so far, the corresponding field must be set to zero.
#define KVM_IRQ_ROUTING_MSI 2
#define KVM_IRQ_ROUTING_S390_ADAPTER 3
#define KVM_IRQ_ROUTING_HV_SINT 4
+ #define KVM_IRQ_ROUTING_XEN_EVTCHN 5
flags:
@@ -1859,6 +1861,20 @@ address_hi must be zero.
__u32 sint;
};
+ struct kvm_irq_routing_xen_evtchn {
+ __u32 port;
+ __u32 vcpu;
+ __u32 priority;
+ };
+
+
+When KVM_CAP_XEN_HVM includes the KVM_XEN_HVM_CONFIG_EVTCHN_2LEVEL bit
+in its indication of supported features, routing to Xen event channels
+is supported. Although the priority field is present, only the value
+KVM_XEN_HVM_CONFIG_EVTCHN_2LEVEL is supported, which means delivery by
+2 level event channels. FIFO event channel support may be added in
+the future.
+
4.55 KVM_SET_TSC_KHZ
--------------------
@@ -7413,6 +7429,7 @@ PVHVM guests. Valid flags are::
#define KVM_XEN_HVM_CONFIG_INTERCEPT_HCALL (1 << 1)
#define KVM_XEN_HVM_CONFIG_SHARED_INFO (1 << 2)
#define KVM_XEN_HVM_CONFIG_RUNSTATE (1 << 2)
+ #define KVM_XEN_HVM_CONFIG_EVTCHN_2LEVEL (1 << 3)
The KVM_XEN_HVM_CONFIG_HYPERCALL_MSR flag indicates that the KVM_XEN_HVM_CONFIG
ioctl is available, for the guest to set its hypercall page.
@@ -7432,6 +7449,10 @@ The KVM_XEN_HVM_CONFIG_RUNSTATE flag indicates that the runstate-related
features KVM_XEN_VCPU_ATTR_TYPE_RUNSTATE_ADDR/_CURRENT/_DATA/_ADJUST are
supported by the KVM_XEN_VCPU_SET_ATTR/KVM_XEN_VCPU_GET_ATTR ioctls.
+The KVM_XEN_HVM_CONFIG_EVTCHN_2LEVEL flag indicates that IRQ routing entries
+of the type KVM_IRQ_ROUTING_XEN_EVTCHN are supported, with the priority
+field set to indicate 2 level event channel delivery.
+
8.31 KVM_CAP_PPC_MULTITCE
-------------------------
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index 6e61e11e750f..623fb7c4992c 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -610,6 +610,7 @@ struct kvm_vcpu_xen {
u64 last_steal;
u64 runstate_entry_time;
u64 runstate_times[4];
+ unsigned long evtchn_pending_sel;
};
struct kvm_vcpu_arch {
diff --git a/arch/x86/kvm/irq_comm.c b/arch/x86/kvm/irq_comm.c
index 39ad02d6dc63..6e0dab04320e 100644
--- a/arch/x86/kvm/irq_comm.c
+++ b/arch/x86/kvm/irq_comm.c
@@ -24,6 +24,7 @@
#include "hyperv.h"
#include "x86.h"
+#include "xen.h"
static int kvm_set_pic_irq(struct kvm_kernel_irq_routing_entry *e,
struct kvm *kvm, int irq_source_id, int level,
@@ -175,6 +176,13 @@ int kvm_arch_set_irq_inatomic(struct kvm_kernel_irq_routing_entry *e,
return r;
break;
+#ifdef CONFIG_KVM_XEN
+ case KVM_IRQ_ROUTING_XEN_EVTCHN:
+ if (!level)
+ return -1;
+
+ return kvm_xen_set_evtchn_fast(e, kvm);
+#endif
default:
break;
}
@@ -310,6 +318,10 @@ int kvm_set_routing_entry(struct kvm *kvm,
e->hv_sint.vcpu = ue->u.hv_sint.vcpu;
e->hv_sint.sint = ue->u.hv_sint.sint;
break;
+#ifdef CONFIG_KVM_XEN
+ case KVM_IRQ_ROUTING_XEN_EVTCHN:
+ return kvm_xen_setup_evtchn(kvm, e, ue);
+#endif
default:
return -EINVAL;
}
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 42bde45a1bc2..3050601d5d73 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -4188,7 +4188,8 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
case KVM_CAP_XEN_HVM:
r = KVM_XEN_HVM_CONFIG_HYPERCALL_MSR |
KVM_XEN_HVM_CONFIG_INTERCEPT_HCALL |
- KVM_XEN_HVM_CONFIG_SHARED_INFO;
+ KVM_XEN_HVM_CONFIG_SHARED_INFO |
+ KVM_XEN_HVM_CONFIG_EVTCHN_2LEVEL;
if (sched_info_on())
r |= KVM_XEN_HVM_CONFIG_RUNSTATE;
break;
diff --git a/arch/x86/kvm/xen.c b/arch/x86/kvm/xen.c
index da4bf2c6407f..ceddabd1f5c6 100644
--- a/arch/x86/kvm/xen.c
+++ b/arch/x86/kvm/xen.c
@@ -16,6 +16,7 @@
#include <trace/events/kvm.h>
#include <xen/interface/xen.h>
#include <xen/interface/vcpu.h>
+#include <xen/interface/event_channel.h>
#include "trace.h"
@@ -195,6 +196,8 @@ void kvm_xen_update_runstate_guest(struct kvm_vcpu *v, int state)
int __kvm_xen_has_interrupt(struct kvm_vcpu *v)
{
+ unsigned long evtchn_pending_sel = READ_ONCE(v->arch.xen.evtchn_pending_sel);
+ bool atomic = in_atomic() || !task_is_running(current);
int err;
u8 rc = 0;
@@ -204,6 +207,9 @@ int __kvm_xen_has_interrupt(struct kvm_vcpu *v)
*/
struct gfn_to_hva_cache *ghc = &v->arch.xen.vcpu_info_cache;
struct kvm_memslots *slots = kvm_memslots(v->kvm);
+ bool ghc_valid = slots->generation == ghc->generation &&
+ !kvm_is_error_hva(ghc->hva) && ghc->memslot;
+
unsigned int offset = offsetof(struct vcpu_info, evtchn_upcall_pending);
/* No need for compat handling here */
@@ -219,8 +225,7 @@ int __kvm_xen_has_interrupt(struct kvm_vcpu *v)
* cache in kvm_read_guest_offset_cached(), but just uses
* __get_user() instead. And falls back to the slow path.
*/
- if (likely(slots->generation == ghc->generation &&
- !kvm_is_error_hva(ghc->hva) && ghc->memslot)) {
+ if (!evtchn_pending_sel && ghc_valid) {
/* Fast path */
pagefault_disable();
err = __get_user(rc, (u8 __user *)ghc->hva + offset);
@@ -239,11 +244,82 @@ int __kvm_xen_has_interrupt(struct kvm_vcpu *v)
* and we'll end up getting called again from a context where we *can*
* fault in the page and wait for it.
*/
- if (in_atomic() || !task_is_running(current))
+ if (atomic)
return 1;
- kvm_read_guest_offset_cached(v->kvm, ghc, &rc, offset,
- sizeof(rc));
+ if (!ghc_valid) {
+ err = kvm_gfn_to_hva_cache_init(v->kvm, ghc, ghc->gpa, ghc->len);
+ if (err || !ghc->memslot) {
+ /*
+ * If this failed, userspace has screwed up the
+ * vcpu_info mapping. No interrupts for you.
+ */
+ return 0;
+ }
+ }
+
+ /*
+ * Now we have a valid (protected by srcu) userspace HVA in
+ * ghc->hva which points to the struct vcpu_info. If there
+ * are any bits in the in-kernel evtchn_pending_sel then
+ * we need to write those to the guest vcpu_info and set
+ * its evtchn_upcall_pending flag. If there aren't any bits
+ * to add, we only want to *check* evtchn_upcall_pending.
+ */
+ if (evtchn_pending_sel) {
+ bool long_mode = v->kvm->arch.xen.long_mode;
+
+ if (!user_access_begin((void __user *)ghc->hva, sizeof(struct vcpu_info)))
+ return 0;
+
+ if (IS_ENABLED(CONFIG_64BIT) && long_mode) {
+ struct vcpu_info __user *vi = (void __user *)ghc->hva;
+
+ /* Attempt to set the evtchn_pending_sel bits in the
+ * guest, and if that succeeds then clear the same
+ * bits in the in-kernel version. */
+ asm volatile("1:\t" LOCK_PREFIX "orq %0, %1\n"
+ "\tnotq %0\n"
+ "\t" LOCK_PREFIX "andq %0, %2\n"
+ "2:\n"
+ "\t.section .fixup,\"ax\"\n"
+ "3:\tjmp\t2b\n"
+ "\t.previous\n"
+ _ASM_EXTABLE_UA(1b, 3b)
+ : "=r" (evtchn_pending_sel),
+ "+m" (vi->evtchn_pending_sel),
+ "+m" (v->arch.xen.evtchn_pending_sel)
+ : "0" (evtchn_pending_sel));
+ } else {
+ struct compat_vcpu_info __user *vi = (void __user *)ghc->hva;
+ u32 evtchn_pending_sel32 = evtchn_pending_sel;
+
+ /* Attempt to set the evtchn_pending_sel bits in the
+ * guest, and if that succeeds then clear the same
+ * bits in the in-kernel version. */
+ asm volatile("1:\t" LOCK_PREFIX "orl %0, %1\n"
+ "\tnotl %0\n"
+ "\t" LOCK_PREFIX "andl %0, %2\n"
+ "2:\n"
+ "\t.section .fixup,\"ax\"\n"
+ "3:\tjmp\t2b\n"
+ "\t.previous\n"
+ _ASM_EXTABLE_UA(1b, 3b)
+ : "=r" (evtchn_pending_sel32),
+ "+m" (vi->evtchn_pending_sel),
+ "+m" (v->arch.xen.evtchn_pending_sel)
+ : "0" (evtchn_pending_sel32));
+ }
+ rc = 1;
+ unsafe_put_user(rc, (u8 __user *)ghc->hva + offset, err);
+
+ err:
+ user_access_end();
+
+ mark_page_dirty_in_slot(v->kvm, ghc->memslot, ghc->gpa >> PAGE_SHIFT);
+ } else {
+ __get_user(rc, (u8 __user *)ghc->hva + offset);
+ }
return rc;
}
@@ -740,3 +816,179 @@ int kvm_xen_hypercall(struct kvm_vcpu *vcpu)
return 0;
}
+
+static inline int max_evtchn_port(struct kvm *kvm)
+{
+ if (IS_ENABLED(CONFIG_64BIT) && kvm->arch.xen.long_mode)
+ return EVTCHN_2L_NR_CHANNELS;
+ else
+ return COMPAT_EVTCHN_2L_NR_CHANNELS;
+}
+
+/*
+ * This follows the kvm_set_irq() API, so it returns:
+ * < 0 Interrupt was ignored (masked or not delivered for other reasons)
+ * = 0 Interrupt was coalesced (previous irq is still pending)
+ * > 0 Number of CPUs interrupt was delivered to
+ */
+int kvm_xen_set_evtchn_fast(struct kvm_kernel_irq_routing_entry *e,
+ struct kvm *kvm)
+{
+ struct gfn_to_pfn_cache *gpc = &kvm->arch.xen.shinfo_cache;
+ struct kvm_vcpu *vcpu;
+ unsigned long *pending_bits, *mask_bits;
+ unsigned long flags;
+ int port_word_bit;
+ bool kick_vcpu = false;
+ int idx;
+ int rc;
+
+ vcpu = kvm_get_vcpu_by_id(kvm, e->xen_evtchn.vcpu);
+ if (!vcpu)
+ return -1;
+
+ if (!vcpu->arch.xen.vcpu_info_set)
+ return -1;
+
+ if (e->xen_evtchn.port >= max_evtchn_port(kvm))
+ return -1;
+
+ rc = -EWOULDBLOCK;
+ read_lock_irqsave(&gpc->lock, flags);
+
+ idx = srcu_read_lock(&kvm->srcu);
+ if (!kvm_gfn_to_pfn_cache_check(kvm, gpc, gpc->gpa, PAGE_SIZE))
+ goto out_rcu;
+
+ if (IS_ENABLED(CONFIG_64BIT) && kvm->arch.xen.long_mode) {
+ struct shared_info *shinfo = gpc->khva;
+ pending_bits = (unsigned long *)&shinfo->evtchn_pending;
+ mask_bits = (unsigned long *)&shinfo->evtchn_mask;
+ port_word_bit = e->xen_evtchn.port / 64;
+ } else {
+ struct compat_shared_info *shinfo = gpc->khva;
+ pending_bits = (unsigned long *)&shinfo->evtchn_pending;
+ mask_bits = (unsigned long *)&shinfo->evtchn_mask;
+ port_word_bit = e->xen_evtchn.port / 32;
+ }
+
+ /*
+ * If this port wasn't already set, and if it isn't masked, then
+ * we try to set the corresponding bit in the in-kernel shadow of
+ * evtchn_pending_sel for the target vCPU. And if *that* wasn't
+ * already set, then we kick the vCPU in question to write to the
+ * *real* evtchn_pending_sel in its own guest vcpu_info struct.
+ */
+ if (test_and_set_bit(e->xen_evtchn.port, pending_bits)) {
+ rc = 0; /* It was already raised */
+ } else if (test_bit(e->xen_evtchn.port, mask_bits)) {
+ rc = -1; /* Masked */
+ } else {
+ rc = 1; /* Delivered. But was the vCPU waking already? */
+ if (!test_and_set_bit(port_word_bit, &vcpu->arch.xen.evtchn_pending_sel))
+ kick_vcpu = true;
+ }
+
+ out_rcu:
+ srcu_read_unlock(&kvm->srcu, idx);
+ read_unlock_irqrestore(&gpc->lock, flags);
+
+ if (kick_vcpu) {
+ kvm_make_request(KVM_REQ_EVENT, vcpu);
+ kvm_vcpu_kick(vcpu);
+ }
+
+ return rc;
+}
+
+/* This is the version called from kvm_set_irq() as the .set function */
+static int evtchn_set_fn(struct kvm_kernel_irq_routing_entry *e, struct kvm *kvm,
+ int irq_source_id, int level, bool line_status)
+{
+ bool mm_borrowed = false;
+ int rc;
+
+ if (!level)
+ return -1;
+
+ rc = kvm_xen_set_evtchn_fast(e, kvm);
+ if (rc != -EWOULDBLOCK)
+ return rc;
+
+ if (current->mm != kvm->mm) {
+ /*
+ * If not on a thread which already belongs to this KVM,
+ * we'd better be in the irqfd workqueue.
+ */
+ if (WARN_ON_ONCE(current->mm))
+ return -EINVAL;
+
+ kthread_use_mm(kvm->mm);
+ mm_borrowed = true;
+ }
+
+ /*
+ * For the irqfd workqueue, using the main kvm->lock mutex is
+ * fine since this function is invoked from kvm_set_irq() with
+ * no other lock held, no srcu. In future if it will be called
+ * directly from a vCPU thread (e.g. on hypercall for an IPI)
+ * then it may need to switch to using a leaf-node mutex for
+ * serializing the shared_info mapping.
+ */
+ mutex_lock(&kvm->lock);
+
+ /*
+ * It is theoretically possible for the page to be unmapped
+ * and the MMU notifier to invalidate the shared_info before
+ * we even get to use it. In that case, this looks like an
+ * infinite loop. It was tempting to do it via the userspace
+ * HVA instead... but that just *hides* the fact that it's
+ * an infinite loop, because if a fault occurs and it waits
+ * for the page to come back, it can *still* immediately
+ * fault and have to wait again, repeatedly.
+ *
+ * Conversely, the page could also have been reinstated by
+ * another thread before we even obtain the mutex above, so
+ * check again *first* before remapping it.
+ */
+ do {
+ struct gfn_to_pfn_cache *gpc = &kvm->arch.xen.shinfo_cache;
+ int idx;
+
+ rc = kvm_xen_set_evtchn_fast(e, kvm);
+ if (rc != -EWOULDBLOCK)
+ break;
+
+ idx = srcu_read_lock(&kvm->srcu);
+ rc = kvm_gfn_to_pfn_cache_refresh(kvm, gpc, gpc->gpa,
+ PAGE_SIZE, false);
+ srcu_read_unlock(&kvm->srcu, idx);
+ } while(!rc);
+
+ mutex_unlock(&kvm->lock);
+
+ if (mm_borrowed)
+ kthread_unuse_mm(kvm->mm);
+
+ return rc;
+}
+
+int kvm_xen_setup_evtchn(struct kvm *kvm,
+ struct kvm_kernel_irq_routing_entry *e,
+ const struct kvm_irq_routing_entry *ue)
+
+{
+ if (ue->u.xen_evtchn.port >= max_evtchn_port(kvm))
+ return -EINVAL;
+
+ /* We only support 2 level event channels for now */
+ if (ue->u.xen_evtchn.priority != KVM_IRQ_ROUTING_XEN_EVTCHN_PRIO_2LEVEL)
+ return -EINVAL;
+
+ e->xen_evtchn.port = ue->u.xen_evtchn.port;
+ e->xen_evtchn.vcpu = ue->u.xen_evtchn.vcpu;
+ e->xen_evtchn.priority = ue->u.xen_evtchn.priority;
+ e->set = evtchn_set_fn;
+
+ return 0;
+}
diff --git a/arch/x86/kvm/xen.h b/arch/x86/kvm/xen.h
index cc0cf5f37450..adbcc9ed59db 100644
--- a/arch/x86/kvm/xen.h
+++ b/arch/x86/kvm/xen.h
@@ -24,6 +24,12 @@ int kvm_xen_hvm_config(struct kvm *kvm, struct kvm_xen_hvm_config *xhc);
void kvm_xen_init_vm(struct kvm *kvm);
void kvm_xen_destroy_vm(struct kvm *kvm);
+int kvm_xen_set_evtchn_fast(struct kvm_kernel_irq_routing_entry *e,
+ struct kvm *kvm);
+int kvm_xen_setup_evtchn(struct kvm *kvm,
+ struct kvm_kernel_irq_routing_entry *e,
+ const struct kvm_irq_routing_entry *ue);
+
static inline bool kvm_xen_msr_enabled(struct kvm *kvm)
{
return static_branch_unlikely(&kvm_xen_enabled.key) &&
@@ -134,6 +140,9 @@ struct compat_shared_info {
struct compat_arch_shared_info arch;
};
+#define COMPAT_EVTCHN_2L_NR_CHANNELS (8 * \
+ sizeof_field(struct compat_shared_info, \
+ evtchn_pending))
struct compat_vcpu_runstate_info {
int state;
uint64_t state_entry_time;
diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
index 9bbb1f1d9e48..3c47b146851a 100644
--- a/include/linux/kvm_host.h
+++ b/include/linux/kvm_host.h
@@ -497,6 +497,12 @@ struct kvm_hv_sint {
u32 sint;
};
+struct kvm_xen_evtchn {
+ u32 port;
+ u32 vcpu;
+ u32 priority;
+};
+
struct kvm_kernel_irq_routing_entry {
u32 gsi;
u32 type;
@@ -517,6 +523,7 @@ struct kvm_kernel_irq_routing_entry {
} msi;
struct kvm_s390_adapter_int adapter;
struct kvm_hv_sint hv_sint;
+ struct kvm_xen_evtchn xen_evtchn;
};
struct hlist_node link;
};
diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
index 469f05d69c8d..fbfd70d965c6 100644
--- a/include/uapi/linux/kvm.h
+++ b/include/uapi/linux/kvm.h
@@ -1163,11 +1163,20 @@ struct kvm_irq_routing_hv_sint {
__u32 sint;
};
+struct kvm_irq_routing_xen_evtchn {
+ __u32 port;
+ __u32 vcpu;
+ __u32 priority;
+};
+
+#define KVM_IRQ_ROUTING_XEN_EVTCHN_PRIO_2LEVEL ((__u32)(-1))
+
/* gsi routing entry types */
#define KVM_IRQ_ROUTING_IRQCHIP 1
#define KVM_IRQ_ROUTING_MSI 2
#define KVM_IRQ_ROUTING_S390_ADAPTER 3
#define KVM_IRQ_ROUTING_HV_SINT 4
+#define KVM_IRQ_ROUTING_XEN_EVTCHN 5
struct kvm_irq_routing_entry {
__u32 gsi;
@@ -1179,6 +1188,7 @@ struct kvm_irq_routing_entry {
struct kvm_irq_routing_msi msi;
struct kvm_irq_routing_s390_adapter adapter;
struct kvm_irq_routing_hv_sint hv_sint;
+ struct kvm_irq_routing_xen_evtchn xen_evtchn;
__u32 pad[8];
} u;
};
@@ -1209,6 +1219,7 @@ struct kvm_x86_mce {
#define KVM_XEN_HVM_CONFIG_INTERCEPT_HCALL (1 << 1)
#define KVM_XEN_HVM_CONFIG_SHARED_INFO (1 << 2)
#define KVM_XEN_HVM_CONFIG_RUNSTATE (1 << 3)
+#define KVM_XEN_HVM_CONFIG_EVTCHN_2LEVEL (1 << 4)
struct kvm_xen_hvm_config {
__u32 flags;
diff --git a/tools/testing/selftests/kvm/x86_64/xen_shinfo_test.c b/tools/testing/selftests/kvm/x86_64/xen_shinfo_test.c
index a0699f00b3d6..478e0ae8b93e 100644
--- a/tools/testing/selftests/kvm/x86_64/xen_shinfo_test.c
+++ b/tools/testing/selftests/kvm/x86_64/xen_shinfo_test.c
@@ -14,6 +14,9 @@
#include <stdint.h>
#include <time.h>
#include <sched.h>
+#include <signal.h>
+
+#include <sys/eventfd.h>
#define VCPU_ID 5
@@ -22,10 +25,15 @@
#define SHINFO_REGION_SLOT 10
#define PAGE_SIZE 4096
+#define DUMMY_REGION_GPA (SHINFO_REGION_GPA + (2 * PAGE_SIZE))
+#define DUMMY_REGION_SLOT 11
+
+#define SHINFO_ADDR (SHINFO_REGION_GPA)
#define PVTIME_ADDR (SHINFO_REGION_GPA + PAGE_SIZE)
#define RUNSTATE_ADDR (SHINFO_REGION_GPA + PAGE_SIZE + 0x20)
#define VCPU_INFO_ADDR (SHINFO_REGION_GPA + 0x40)
+#define SHINFO_VADDR (SHINFO_REGION_GVA)
#define RUNSTATE_VADDR (SHINFO_REGION_GVA + PAGE_SIZE + 0x20)
#define VCPU_INFO_VADDR (SHINFO_REGION_GVA + 0x40)
@@ -73,15 +81,37 @@ struct vcpu_info {
struct pvclock_vcpu_time_info time;
}; /* 64 bytes (x86) */
+struct shared_info {
+ struct vcpu_info vcpu_info[32];
+ unsigned long evtchn_pending[64];
+ unsigned long evtchn_mask[64];
+ struct pvclock_wall_clock wc;
+ uint32_t wc_sec_hi;
+ /* arch_shared_info here */
+};
+
#define RUNSTATE_running 0
#define RUNSTATE_runnable 1
#define RUNSTATE_blocked 2
#define RUNSTATE_offline 3
+static const char *runstate_names[] = {
+ "running",
+ "runnable",
+ "blocked",
+ "offline"
+};
+
+struct {
+ struct kvm_irq_routing info;
+ struct kvm_irq_routing_entry entries[2];
+} irq_routes;
+
static void evtchn_handler(struct ex_regs *regs)
{
struct vcpu_info *vi = (void *)VCPU_INFO_VADDR;
vi->evtchn_upcall_pending = 0;
+ vi->evtchn_pending_sel = 0;
GUEST_SYNC(0x20);
}
@@ -127,7 +157,25 @@ static void guest_code(void)
GUEST_SYNC(6);
GUEST_ASSERT(rs->time[RUNSTATE_runnable] >= MIN_STEAL_TIME);
- GUEST_DONE();
+ /* Attempt to deliver a *masked* interrupt */
+ GUEST_SYNC(7);
+
+ /* Wait until we see the bit set */
+ struct shared_info *si = (void *)SHINFO_VADDR;
+ while (!si->evtchn_pending[0])
+ __asm__ __volatile__ ("rep nop" : : : "memory");
+
+ /* Now deliver an *unmasked* interrupt */
+ GUEST_SYNC(8);
+
+ while (!si->evtchn_pending[1])
+ __asm__ __volatile__ ("rep nop" : : : "memory");
+
+ /* Change memslots and deliver an interrupt */
+ GUEST_SYNC(9);
+
+ for (;;)
+ __asm__ __volatile__ ("rep nop" : : : "memory");
}
static int cmp_timespec(struct timespec *a, struct timespec *b)
@@ -144,9 +192,18 @@ static int cmp_timespec(struct timespec *a, struct timespec *b)
return 0;
}
+static void handle_alrm(int sig)
+{
+ TEST_FAIL("IRQ delivery timed out");
+}
+
int main(int argc, char *argv[])
{
struct timespec min_ts, max_ts, vm_ts;
+ bool verbose;
+
+ verbose = argc > 1 && (!strncmp(argv[1], "-v", 3) ||
+ !strncmp(argv[1], "--verbose", 10));
int xen_caps = kvm_check_cap(KVM_CAP_XEN_HVM);
if (!(xen_caps & KVM_XEN_HVM_CONFIG_SHARED_INFO) ) {
@@ -155,6 +212,7 @@ int main(int argc, char *argv[])
}
bool do_runstate_tests = !!(xen_caps & KVM_XEN_HVM_CONFIG_RUNSTATE);
+ bool do_eventfd_tests = !!(xen_caps & KVM_XEN_HVM_CONFIG_EVTCHN_2LEVEL);
clock_gettime(CLOCK_REALTIME, &min_ts);
@@ -166,6 +224,11 @@ int main(int argc, char *argv[])
SHINFO_REGION_GPA, SHINFO_REGION_SLOT, 2, 0);
virt_map(vm, SHINFO_REGION_GVA, SHINFO_REGION_GPA, 2);
+ struct shared_info *shinfo = addr_gpa2hva(vm, SHINFO_VADDR);
+
+ int zero_fd = open("/dev/zero", O_RDONLY);
+ TEST_ASSERT(zero_fd != -1, "Failed to open /dev/zero");
+
struct kvm_xen_hvm_config hvmc = {
.flags = KVM_XEN_HVM_CONFIG_INTERCEPT_HCALL,
.msr = XEN_HYPERCALL_MSR,
@@ -184,6 +247,16 @@ int main(int argc, char *argv[])
};
vm_ioctl(vm, KVM_XEN_HVM_SET_ATTR, &ha);
+ /*
+ * Test what happens when the HVA of the shinfo page is remapped after
+ * the kernel has a reference to it. But make sure we copy the clock
+ * info over since that's only set at setup time, and we test it later.
+ */
+ struct pvclock_wall_clock wc_copy = shinfo->wc;
+ void *m = mmap(shinfo, PAGE_SIZE, PROT_READ|PROT_WRITE, MAP_FIXED|MAP_PRIVATE, zero_fd, 0);
+ TEST_ASSERT(m == shinfo, "Failed to map /dev/zero over shared info");
+ shinfo->wc = wc_copy;
+
struct kvm_xen_vcpu_attr vi = {
.type = KVM_XEN_VCPU_ATTR_TYPE_VCPU_INFO,
.u.gpa = VCPU_INFO_ADDR,
@@ -214,6 +287,49 @@ int main(int argc, char *argv[])
vcpu_ioctl(vm, VCPU_ID, KVM_XEN_VCPU_SET_ATTR, &st);
}
+ int irq_fd[2] = { -1, -1 };
+
+ if (do_eventfd_tests) {
+ irq_fd[0] = eventfd(0, 0);
+ irq_fd[1] = eventfd(0, 0);
+
+ /* Unexpected, but not a KVM failure */
+ if (irq_fd[0] == -1 || irq_fd[1] == -1)
+ do_eventfd_tests = false;
+ }
+
+ if (do_eventfd_tests) {
+ irq_routes.info.nr = 2;
+
+ irq_routes.entries[0].gsi = 32;
+ irq_routes.entries[0].type = KVM_IRQ_ROUTING_XEN_EVTCHN;
+ irq_routes.entries[0].u.xen_evtchn.port = 15;
+ irq_routes.entries[0].u.xen_evtchn.vcpu = VCPU_ID;
+ irq_routes.entries[0].u.xen_evtchn.priority = KVM_IRQ_ROUTING_XEN_EVTCHN_PRIO_2LEVEL;
+
+ irq_routes.entries[1].gsi = 33;
+ irq_routes.entries[1].type = KVM_IRQ_ROUTING_XEN_EVTCHN;
+ irq_routes.entries[1].u.xen_evtchn.port = 66;
+ irq_routes.entries[1].u.xen_evtchn.vcpu = VCPU_ID;
+ irq_routes.entries[1].u.xen_evtchn.priority = KVM_IRQ_ROUTING_XEN_EVTCHN_PRIO_2LEVEL;
+
+ vm_ioctl(vm, KVM_SET_GSI_ROUTING, &irq_routes);
+
+ struct kvm_irqfd ifd = { };
+
+ ifd.fd = irq_fd[0];
+ ifd.gsi = 32;
+ vm_ioctl(vm, KVM_IRQFD, &ifd);
+
+ ifd.fd = irq_fd[1];
+ ifd.gsi = 33;
+ vm_ioctl(vm, KVM_IRQFD, &ifd);
+
+ struct sigaction sa = { };
+ sa.sa_handler = handle_alrm;
+ sigaction(SIGALRM, &sa, NULL);
+ }
+
struct vcpu_info *vinfo = addr_gpa2hva(vm, VCPU_INFO_VADDR);
vinfo->evtchn_upcall_pending = 0;
@@ -248,6 +364,8 @@ int main(int argc, char *argv[])
switch (uc.args[1]) {
case 0:
+ if (verbose)
+ printf("Delivering evtchn upcall\n");
evtchn_irq_expected = true;
vinfo->evtchn_upcall_pending = 1;
break;
@@ -256,11 +374,16 @@ int main(int argc, char *argv[])
TEST_ASSERT(!evtchn_irq_expected, "Event channel IRQ not seen");
if (!do_runstate_tests)
goto done;
+ if (verbose)
+ printf("Testing runstate %s\n", runstate_names[uc.args[1]]);
rst.type = KVM_XEN_VCPU_ATTR_TYPE_RUNSTATE_CURRENT;
rst.u.runstate.state = uc.args[1];
vcpu_ioctl(vm, VCPU_ID, KVM_XEN_VCPU_SET_ATTR, &rst);
break;
+
case 4:
+ if (verbose)
+ printf("Testing RUNSTATE_ADJUST\n");
rst.type = KVM_XEN_VCPU_ATTR_TYPE_RUNSTATE_ADJUST;
memset(&rst.u, 0, sizeof(rst.u));
rst.u.runstate.state = (uint64_t)-1;
@@ -274,6 +397,8 @@ int main(int argc, char *argv[])
break;
case 5:
+ if (verbose)
+ printf("Testing RUNSTATE_DATA\n");
rst.type = KVM_XEN_VCPU_ATTR_TYPE_RUNSTATE_DATA;
memset(&rst.u, 0, sizeof(rst.u));
rst.u.runstate.state = RUNSTATE_running;
@@ -282,16 +407,54 @@ int main(int argc, char *argv[])
rst.u.runstate.time_offline = 0x5a;
vcpu_ioctl(vm, VCPU_ID, KVM_XEN_VCPU_SET_ATTR, &rst);
break;
+
case 6:
+ if (verbose)
+ printf("Testing steal time\n");
/* Yield until scheduler delay exceeds target */
rundelay = get_run_delay() + MIN_STEAL_TIME;
do {
sched_yield();
} while (get_run_delay() < rundelay);
break;
+
+ case 7:
+ if (!do_eventfd_tests)
+ goto done;
+ if (verbose)
+ printf("Testing masked event channel\n");
+ shinfo->evtchn_mask[0] = 0x8000;
+ eventfd_write(irq_fd[0], 1UL);
+ alarm(1);
+ break;
+
+ case 8:
+ if (verbose)
+ printf("Testing unmasked event channel\n");
+ /* Unmask that, but deliver the other one */
+ shinfo->evtchn_pending[0] = 0;
+ shinfo->evtchn_mask[0] = 0;
+ eventfd_write(irq_fd[1], 1UL);
+ evtchn_irq_expected = true;
+ alarm(1);
+ break;
+
+ case 9:
+ if (verbose)
+ printf("Testing event channel after memslot change\n");
+ vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS,
+ DUMMY_REGION_GPA, DUMMY_REGION_SLOT, 1, 0);
+ eventfd_write(irq_fd[0], 1UL);
+ evtchn_irq_expected = true;
+ alarm(1);
+ break;
+
case 0x20:
TEST_ASSERT(evtchn_irq_expected, "Unexpected event channel IRQ");
evtchn_irq_expected = false;
+ if (shinfo->evtchn_pending[1] &&
+ shinfo->evtchn_pending[0])
+ goto done;
break;
}
break;
@@ -318,6 +481,16 @@ int main(int argc, char *argv[])
ti = addr_gpa2hva(vm, SHINFO_REGION_GPA + 0x40 + 0x20);
ti2 = addr_gpa2hva(vm, PVTIME_ADDR);
+ if (verbose) {
+ printf("Wall clock (v %d) %d.%09d\n", wc->version, wc->sec, wc->nsec);
+ printf("Time info 1: v %u tsc %" PRIu64 " time %" PRIu64 " mul %u shift %u flags %x\n",
+ ti->version, ti->tsc_timestamp, ti->system_time, ti->tsc_to_system_mul,
+ ti->tsc_shift, ti->flags);
+ printf("Time info 2: v %u tsc %" PRIu64 " time %" PRIu64 " mul %u shift %u flags %x\n",
+ ti2->version, ti2->tsc_timestamp, ti2->system_time, ti2->tsc_to_system_mul,
+ ti2->tsc_shift, ti2->flags);
+ }
+
vm_ts.tv_sec = wc->sec;
vm_ts.tv_nsec = wc->nsec;
TEST_ASSERT(wc->version && !(wc->version & 1),
@@ -341,6 +514,15 @@ int main(int argc, char *argv[])
};
vcpu_ioctl(vm, VCPU_ID, KVM_XEN_VCPU_GET_ATTR, &rst);
+ if (verbose) {
+ printf("Runstate: %s(%d), entry %" PRIu64 " ns\n",
+ rs->state <= RUNSTATE_offline ? runstate_names[rs->state] : "unknown",
+ rs->state, rs->state_entry_time);
+ for (int i = RUNSTATE_running; i <= RUNSTATE_offline; i++) {
+ printf("State %s: %" PRIu64 " ns\n",
+ runstate_names[i], rs->time[i]);
+ }
+ }
TEST_ASSERT(rs->state == rst.u.runstate.state, "Runstate mismatch");
TEST_ASSERT(rs->state_entry_time == rst.u.runstate.state_entry_time,
"State entry time mismatch");
]
|
| 248/2 |
2026/08/12 12:50 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 45017
output: 45
thoughts: 557
Thoughts: **Analyzing the Xen KVM Event Channel Bug**
Okay, so I'm looking at this crash report and tracing the root cause. It looks like the issue stems from an attempt to grab a sleeping lock from a hard IRQ context, specifically on a `PREEMPT_RT` kernel. My initial thought is to figure out the path. This code snippet from commit `14243b387137a4afbe1df5d9dc15182d6657bb79` shows `kvm_arch_set_irq_inatomic()` now calling `kvm_xen_set_evtchn_fast()`. That's the entry point.
The bug report is saying that `kvm_xen_set_evtchn_fast()` is called in two different, potentially problematic contexts. The first is `xen_timer_callback()`, which is an HRTimer callback initialized with `HRTIMER_MODE_ABS_HARD`. That means it's running in hard IRQ context. Even on a real-time kernel! The second one is through `kvm_arch_set_irq_inatomic()`, triggered by `irqfd` waitqueue wakeups, which are also happening within an atomic context.
So, `kvm_xen_set_evtchn_fast()` was introduced by this commit, and it's being used in both an atomic context (through the `irqfd` path) and a hard IRQ context (through the timer callback). That's a red flag. What's it doing? Examining `kvm_xen_set_evtchn_fast()` code reveals the culprit, the `read_lock_irqsave(&gpc->lock, flags)`. Now, I'm thinking about the locking primitives.
Is that lock, `gpc->lock`, the problematic one? Okay, let's see. Is `gpc->lock` a `rwlock_t` in the commit? I'll need to double check that. Let me look at the definition in the `gfn_to_pfn_cache` code.
|
| 249/2 |
2026/08/12 12:50 |
tool |
git-log |
0m
Args: map[SourcePath:arch/x86/kvm/xen.c SymbolName:kvm_xen_set_evtchn_fast]
Results: map[Output:003d914220c9 KVM: x86/xen: allow vcpu_info content to be 'safely' copied
58f5ee5fedd9 KVM: Drop @gpa from exported gfn=>pfn cache check() and refresh() helpers
e308c24a358d KVM: Use gfn_to_pfn_cache's immutable "kvm" in kvm_gpc_check()
aba3caef5862 KVM: Shorten gfn_to_pfn_cache function names
79f772b9e800 KVM: x86: Query vcpu->vcpu_idx directly and drop its accessor, again
1a65105a5aba KVM: x86/xen: handle PV spinlocks slowpath
fde0451be8fb KVM: x86/xen: Support per-vCPU event channel upcall via local APIC
8733068b9bdb KVM: x86/xen: Make kvm_xen_set_evtchn() reusable from other places
7caf9571563e KVM: x86/xen: Use gfn_to_pfn_cache for vcpu_info
14243b387137 KVM: x86/xen: Add KVM_IRQ_ROUTING_XEN_EVTCHN and event channel delivery
]
|
| 250/2 |
2026/08/12 12:50 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 46427
output: 36
thoughts: 115
Thoughts: **Investigating a Potential Race Condition in KVM-Xen Integration**
Okay, so I'm looking at this code, and the commit `14243b387137a4afbe1df5d9dc15182d6657bb79` is the one where `kvm_xen_set_evtchn_fast` and its call from `kvm_arch_set_irq_inatomic` were introduced. Now, I'm starting to wonder about the potential for a race condition. Specifically, I'm suspicious about `gpc->lock`. The code looks like `gpc->lock` might have been a spinlock at some point and was later changed to an rwlock. It's a hunch, but the timing of that change could be critical to understanding what's going on. I need to nail down when that `gpc->lock` transition happened. Time to dig through the commit history and find when it was modified to an rwlock. This information is key to understanding whether the locking primitives are sufficient to protect the shared resource under those functions.
|
| 251/2 |
2026/08/12 12:50 |
tool |
grepper |
0m
Error:
Args: map[Expression:rwlock_t lock; PathPrefix:include/linux/kvm_host.h]
Results:
|
| 252/2 |
2026/08/12 12:50 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 46833
output: 40
thoughts: 42
Thoughts: Okay, here's my thought process on that:
**Investigating the KVM GFN/PFN Cache**
Right, so I need to track down the definition or related code for `gfn_to_pfn_cache`. Given that it's dealing with GFNs (Guest Frame Numbers) and PFNs (Physical Frame Numbers), my first intuition is to look within the KVM-specific header files. It's likely associated with how KVM manages guest memory translation. I'll start by searching in the obvious places: `include/linux/kvm_types.h`, which is the typical location for KVM data structures, and `include/linux/kvm_host.h`, which likely contains more host-specific KVM definitions and functions. Let's see what turns up in those two files first.
|
| 253/2 |
2026/08/12 12:50 |
tool |
grepper |
0m
Args: map[Expression:struct gfn_to_pfn_cache PathPrefix:include/linux/kvm_types.h]
Results: map[Output:include/linux/kvm_types.h=76=struct gfn_to_hva_cache {
--
include/linux/kvm_types.h-83-
include/linux/kvm_types.h:84:struct gfn_to_pfn_cache {
include/linux/kvm_types.h-85- u64 generation;
]
|
| 254/2 |
2026/08/12 12:50 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 47195
output: 41
thoughts: 19
Thoughts: Here's a summary of my thought process, as requested:
**Deconstructing `struct gfn_to_pfn_cache`**
Okay, time to dissect this `struct gfn_to_pfn_cache`. I need to refresh my memory on exactly what this is, and how it's being used. I'll start by pulling up the definition. Context is everything when dealing with these structures, especially in kernel code. Is it involved with memory management, device assignment, or something else entirely? A quick scan of the structure's members will tell me a lot, but understanding its *purpose* is key to understanding its implementation. I'll keep in mind the potential performance implications of this cache as well. This information can indicate the design choices, and how the developers prioritized memory use vs execution speed.
|
| 255/2 |
2026/08/12 12:50 |
tool |
git-log |
0m
Args: map[SourcePath:include/linux/kvm_types.h SymbolName:gfn_to_pfn_cache]
Results: map[Output:a4bff3df5147 KVM: pfncache: remove KVM_GUEST_USES_PFN usage
8c82a0b3ba1a KVM: Store immutable gfn_to_pfn_cache properties
837f66c71207 KVM: Allow for different capacities in kvm_mmu_memory_cache structs
93984f19e7bc KVM: Fully serialize gfn=>pfn cache refresh via mutex
cf1d88b36ba7 KVM: Remove dirty handling from gfn_to_pfn_cache completely
d0d96121d03d KVM: Use enum to track if cached PFN will be used in guest and/or host
982ed0de4753 KVM: Reinstate gfn_to_pfn_cache with invalidation support
2aa9c199cf81 KVM: Move x86's version of struct kvm_mmu_memory_cache to common code
]
|
| 256/2 |
2026/08/12 12:51 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 47678
output: 25
thoughts: 16
Thoughts: Okay, let's dive into this. Here's my thought process on this task:
**Analyzing the Hash `982ed0de4753`**
Alright, I've got this hash, `982ed0de4753`. My immediate reaction is to treat this as potentially anything. Could be a Git commit SHA, a file hash, a password hash, or something completely proprietary. Without context, it's just a string of characters. I need more information to even begin to guess. I'm going to start with the standard checklist I use for these things.
First, is it a common hashing algorithm? I'll quickly check its length, which is 12 hex characters or 48 bits. This eliminates some possibilities, but leaves plenty. SHA-1, MD5, and even shorter hashes are out. I know that `982ed0de4753` is not SHA-256 (64 hex characters), or SHA-512 (128 hex characters), so those are out as well.
I need more information. Is there any associated data? Where did this hash come from? How was it generated? Is it from a codebase? Is it from a log file? Without that context, I'm essentially stuck. I'm going to note this hash, make sure I have it correct, and look for more clues. This is where I would start looking for the surrounding data or metadata that might offer context. Depending on what I'm looking at, this could mean checking version control history, log file timestamps, or looking for clues in the wider system.
|
| 257/2 |
2026/08/12 12:51 |
tool |
git-show |
0m
Args:
Results: map[Output:commit 982ed0de4753ed6e71dbd40f82a5a066baf133ed
Author: David Woodhouse <dwmw@amazon.co.uk>
Date: Fri Dec 10 16:36:21 2021 +0000
KVM: Reinstate gfn_to_pfn_cache with invalidation support
This can be used in two modes. There is an atomic mode where the cached
mapping is accessed while holding the rwlock, and a mode where the
physical address is used by a vCPU in guest mode.
For the latter case, an invalidation will wake the vCPU with the new
KVM_REQ_GPC_INVALIDATE, and the architecture will need to refresh any
caches it still needs to access before entering guest mode again.
Only one vCPU can be targeted by the wake requests; it's simple enough
to make it wake all vCPUs or even a mask but I don't see a use case for
that additional complexity right now.
Invalidation happens from the invalidate_range_start MMU notifier, which
needs to be able to sleep in order to wake the vCPU and wait for it.
This means that revalidation potentially needs to "wait" for the MMU
operation to complete and the invalidate_range_end notifier to be
invoked. Like the vCPU when it takes a page fault in that period, we
just spin — fixing that in a future patch by implementing an actual
*wait* may be another part of shaving this particularly hirsute yak.
As noted in the comments in the function itself, the only case where
the invalidate_range_start notifier is expected to be called *without*
being able to sleep is when the OOM reaper is killing the process. In
that case, we expect the vCPU threads already to have exited, and thus
there will be nothing to wake, and no reason to wait. So we clear the
KVM_REQUEST_WAIT bit and send the request anyway, then complain loudly
if there actually *was* anything to wake up.
Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>
Message-Id: <20211210163625.2886-3-dwmw2@infradead.org>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
diff --git a/arch/x86/kvm/Kconfig b/arch/x86/kvm/Kconfig
index 03b2ce34e7f4..ebc8ce9ec917 100644
--- a/arch/x86/kvm/Kconfig
+++ b/arch/x86/kvm/Kconfig
@@ -26,6 +26,7 @@ config KVM
select PREEMPT_NOTIFIERS
select MMU_NOTIFIER
select HAVE_KVM_IRQCHIP
+ select HAVE_KVM_PFNCACHE
select HAVE_KVM_IRQFD
select HAVE_KVM_DIRTY_RING
select IRQ_BYPASS_MANAGER
diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
index f9bbcf519280..9bbb1f1d9e48 100644
--- a/include/linux/kvm_host.h
+++ b/include/linux/kvm_host.h
@@ -155,6 +155,7 @@ static inline bool is_error_page(struct page *page)
#define KVM_REQ_UNBLOCK 2
#define KVM_REQ_UNHALT 3
#define KVM_REQ_VM_DEAD (4 | KVM_REQUEST_WAIT | KVM_REQUEST_NO_WAKEUP)
+#define KVM_REQ_GPC_INVALIDATE (5 | KVM_REQUEST_WAIT | KVM_REQUEST_NO_WAKEUP)
#define KVM_REQUEST_ARCH_BASE 8
#define KVM_ARCH_REQ_FLAGS(nr, flags) ({ \
@@ -593,6 +594,10 @@ struct kvm {
unsigned long mn_active_invalidate_count;
struct rcuwait mn_memslots_update_rcuwait;
+ /* For management / invalidation of gfn_to_pfn_caches */
+ spinlock_t gpc_lock;
+ struct list_head gpc_list;
+
/*
* created_vcpus is protected by kvm->lock, and is incremented
* at the beginning of KVM_CREATE_VCPU. online_vcpus is only
@@ -1099,6 +1104,104 @@ int kvm_vcpu_write_guest(struct kvm_vcpu *vcpu, gpa_t gpa, const void *data,
unsigned long len);
void kvm_vcpu_mark_page_dirty(struct kvm_vcpu *vcpu, gfn_t gfn);
+/**
+ * kvm_gfn_to_pfn_cache_init - prepare a cached kernel mapping and HPA for a
+ * given guest physical address.
+ *
+ * @kvm: pointer to kvm instance.
+ * @gpc: struct gfn_to_pfn_cache object.
+ * @vcpu: vCPU to be used for marking pages dirty and to be woken on
+ * invalidation.
+ * @guest_uses_pa: indicates that the resulting host physical PFN is used while
+ * @vcpu is IN_GUEST_MODE so invalidations should wake it.
+ * @kernel_map: requests a kernel virtual mapping (kmap / memremap).
+ * @gpa: guest physical address to map.
+ * @len: sanity check; the range being access must fit a single page.
+ * @dirty: mark the cache dirty immediately.
+ *
+ * @return: 0 for success.
+ * -EINVAL for a mapping which would cross a page boundary.
+ * -EFAULT for an untranslatable guest physical address.
+ *
+ * This primes a gfn_to_pfn_cache and links it into the @kvm's list for
+ * invalidations to be processed. Invalidation callbacks to @vcpu using
+ * %KVM_REQ_GPC_INVALIDATE will occur only for MMU notifiers, not for KVM
+ * memslot changes. Callers are required to use kvm_gfn_to_pfn_cache_check()
+ * to ensure that the cache is valid before accessing the target page.
+ */
+int kvm_gfn_to_pfn_cache_init(struct kvm *kvm, struct gfn_to_pfn_cache *gpc,
+ struct kvm_vcpu *vcpu, bool guest_uses_pa,
+ bool kernel_map, gpa_t gpa, unsigned long len,
+ bool dirty);
+
+/**
+ * kvm_gfn_to_pfn_cache_check - check validity of a gfn_to_pfn_cache.
+ *
+ * @kvm: pointer to kvm instance.
+ * @gpc: struct gfn_to_pfn_cache object.
+ * @gpa: current guest physical address to map.
+ * @len: sanity check; the range being access must fit a single page.
+ * @dirty: mark the cache dirty immediately.
+ *
+ * @return: %true if the cache is still valid and the address matches.
+ * %false if the cache is not valid.
+ *
+ * Callers outside IN_GUEST_MODE context should hold a read lock on @gpc->lock
+ * while calling this function, and then continue to hold the lock until the
+ * access is complete.
+ *
+ * Callers in IN_GUEST_MODE may do so without locking, although they should
+ * still hold a read lock on kvm->scru for the memslot checks.
+ */
+bool kvm_gfn_to_pfn_cache_check(struct kvm *kvm, struct gfn_to_pfn_cache *gpc,
+ gpa_t gpa, unsigned long len);
+
+/**
+ * kvm_gfn_to_pfn_cache_refresh - update a previously initialized cache.
+ *
+ * @kvm: pointer to kvm instance.
+ * @gpc: struct gfn_to_pfn_cache object.
+ * @gpa: updated guest physical address to map.
+ * @len: sanity check; the range being access must fit a single page.
+ * @dirty: mark the cache dirty immediately.
+ *
+ * @return: 0 for success.
+ * -EINVAL for a mapping which would cross a page boundary.
+ * -EFAULT for an untranslatable guest physical address.
+ *
+ * This will attempt to refresh a gfn_to_pfn_cache. Note that a successful
+ * returm from this function does not mean the page can be immediately
+ * accessed because it may have raced with an invalidation. Callers must
+ * still lock and check the cache status, as this function does not return
+ * with the lock still held to permit access.
+ */
+int kvm_gfn_to_pfn_cache_refresh(struct kvm *kvm, struct gfn_to_pfn_cache *gpc,
+ gpa_t gpa, unsigned long len, bool dirty);
+
+/**
+ * kvm_gfn_to_pfn_cache_unmap - temporarily unmap a gfn_to_pfn_cache.
+ *
+ * @kvm: pointer to kvm instance.
+ * @gpc: struct gfn_to_pfn_cache object.
+ *
+ * This unmaps the referenced page and marks it dirty, if appropriate. The
+ * cache is left in the invalid state but at least the mapping from GPA to
+ * userspace HVA will remain cached and can be reused on a subsequent
+ * refresh.
+ */
+void kvm_gfn_to_pfn_cache_unmap(struct kvm *kvm, struct gfn_to_pfn_cache *gpc);
+
+/**
+ * kvm_gfn_to_pfn_cache_destroy - destroy and unlink a gfn_to_pfn_cache.
+ *
+ * @kvm: pointer to kvm instance.
+ * @gpc: struct gfn_to_pfn_cache object.
+ *
+ * This removes a cache from the @kvm's list to be processed on MMU notifier
+ * invocation.
+ */
+void kvm_gfn_to_pfn_cache_destroy(struct kvm *kvm, struct gfn_to_pfn_cache *gpc);
+
void kvm_sigset_activate(struct kvm_vcpu *vcpu);
void kvm_sigset_deactivate(struct kvm_vcpu *vcpu);
diff --git a/include/linux/kvm_types.h b/include/linux/kvm_types.h
index 888ef12862c9..dceac12c1ce5 100644
--- a/include/linux/kvm_types.h
+++ b/include/linux/kvm_types.h
@@ -19,6 +19,7 @@ struct kvm_memslots;
enum kvm_mr_change;
#include <linux/types.h>
+#include <linux/spinlock_types.h>
#include <asm/kvm_types.h>
@@ -53,6 +54,23 @@ struct gfn_to_hva_cache {
struct kvm_memory_slot *memslot;
};
+struct gfn_to_pfn_cache {
+ u64 generation;
+ gpa_t gpa;
+ unsigned long uhva;
+ struct kvm_memory_slot *memslot;
+ struct kvm_vcpu *vcpu;
+ struct list_head list;
+ rwlock_t lock;
+ void *khva;
+ kvm_pfn_t pfn;
+ bool active;
+ bool valid;
+ bool dirty;
+ bool kernel_map;
+ bool guest_uses_pa;
+};
+
#ifdef KVM_ARCH_NR_OBJS_PER_MEMORY_CACHE
/*
* Memory caches are used to preallocate memory ahead of various MMU flows,
diff --git a/virt/kvm/Kconfig b/virt/kvm/Kconfig
index 97cf5413ac25..f4834c20e4a6 100644
--- a/virt/kvm/Kconfig
+++ b/virt/kvm/Kconfig
@@ -4,6 +4,9 @@
config HAVE_KVM
bool
+config HAVE_KVM_PFNCACHE
+ bool
+
config HAVE_KVM_IRQCHIP
bool
diff --git a/virt/kvm/Makefile.kvm b/virt/kvm/Makefile.kvm
index ffdcad3cc97a..2c27d5d0c367 100644
--- a/virt/kvm/Makefile.kvm
+++ b/virt/kvm/Makefile.kvm
@@ -11,3 +11,4 @@ kvm-$(CONFIG_KVM_MMIO) += $(KVM)/coalesced_mmio.o
kvm-$(CONFIG_KVM_ASYNC_PF) += $(KVM)/async_pf.o
kvm-$(CONFIG_HAVE_KVM_IRQ_ROUTING) += $(KVM)/irqchip.o
kvm-$(CONFIG_HAVE_KVM_DIRTY_RING) += $(KVM)/dirty_ring.o
+kvm-$(CONFIG_HAVE_KVM_PFNCACHE) += $(KVM)/pfncache.o
diff --git a/virt/kvm/dirty_ring.c b/virt/kvm/dirty_ring.c
index 8e9874760fb3..222ecc81d7df 100644
--- a/virt/kvm/dirty_ring.c
+++ b/virt/kvm/dirty_ring.c
@@ -9,7 +9,7 @@
#include <linux/vmalloc.h>
#include <linux/kvm_dirty_ring.h>
#include <trace/events/kvm.h>
-#include "mmu_lock.h"
+#include "kvm_mm.h"
int __weak kvm_cpu_dirty_log_size(void)
{
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
index af5b4427b139..6e8e9d36f382 100644
--- a/virt/kvm/kvm_main.c
+++ b/virt/kvm/kvm_main.c
@@ -59,7 +59,7 @@
#include "coalesced_mmio.h"
#include "async_pf.h"
-#include "mmu_lock.h"
+#include "kvm_mm.h"
#include "vfio.h"
#define CREATE_TRACE_POINTS
@@ -711,6 +711,9 @@ static int kvm_mmu_notifier_invalidate_range_start(struct mmu_notifier *mn,
kvm->mn_active_invalidate_count++;
spin_unlock(&kvm->mn_invalidate_lock);
+ gfn_to_pfn_cache_invalidate_start(kvm, range->start, range->end,
+ hva_range.may_block);
+
__kvm_handle_hva_range(kvm, &hva_range);
return 0;
@@ -1071,6 +1074,9 @@ static struct kvm *kvm_create_vm(unsigned long type)
rcuwait_init(&kvm->mn_memslots_update_rcuwait);
xa_init(&kvm->vcpu_array);
+ INIT_LIST_HEAD(&kvm->gpc_list);
+ spin_lock_init(&kvm->gpc_lock);
+
INIT_LIST_HEAD(&kvm->devices);
BUILD_BUG_ON(KVM_MEM_SLOTS_NUM > SHRT_MAX);
@@ -2539,8 +2545,8 @@ static int hva_to_pfn_remapped(struct vm_area_struct *vma,
* 2): @write_fault = false && @writable, @writable will tell the caller
* whether the mapping is writable.
*/
-static kvm_pfn_t hva_to_pfn(unsigned long addr, bool atomic, bool *async,
- bool write_fault, bool *writable)
+kvm_pfn_t hva_to_pfn(unsigned long addr, bool atomic, bool *async,
+ bool write_fault, bool *writable)
{
struct vm_area_struct *vma;
kvm_pfn_t pfn = 0;
diff --git a/virt/kvm/kvm_mm.h b/virt/kvm/kvm_mm.h
new file mode 100644
index 000000000000..34ca40823260
--- /dev/null
+++ b/virt/kvm/kvm_mm.h
@@ -0,0 +1,44 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#ifndef __KVM_MM_H__
+#define __KVM_MM_H__ 1
+
+/*
+ * Architectures can choose whether to use an rwlock or spinlock
+ * for the mmu_lock. These macros, for use in common code
+ * only, avoids using #ifdefs in places that must deal with
+ * multiple architectures.
+ */
+
+#ifdef KVM_HAVE_MMU_RWLOCK
+#define KVM_MMU_LOCK_INIT(kvm) rwlock_init(&(kvm)->mmu_lock)
+#define KVM_MMU_LOCK(kvm) write_lock(&(kvm)->mmu_lock)
+#define KVM_MMU_UNLOCK(kvm) write_unlock(&(kvm)->mmu_lock)
+#define KVM_MMU_READ_LOCK(kvm) read_lock(&(kvm)->mmu_lock)
+#define KVM_MMU_READ_UNLOCK(kvm) read_unlock(&(kvm)->mmu_lock)
+#else
+#define KVM_MMU_LOCK_INIT(kvm) spin_lock_init(&(kvm)->mmu_lock)
+#define KVM_MMU_LOCK(kvm) spin_lock(&(kvm)->mmu_lock)
+#define KVM_MMU_UNLOCK(kvm) spin_unlock(&(kvm)->mmu_lock)
+#define KVM_MMU_READ_LOCK(kvm) spin_lock(&(kvm)->mmu_lock)
+#define KVM_MMU_READ_UNLOCK(kvm) spin_unlock(&(kvm)->mmu_lock)
+#endif /* KVM_HAVE_MMU_RWLOCK */
+
+kvm_pfn_t hva_to_pfn(unsigned long addr, bool atomic, bool *async,
+ bool write_fault, bool *writable);
+
+#ifdef CONFIG_HAVE_KVM_PFNCACHE
+void gfn_to_pfn_cache_invalidate_start(struct kvm *kvm,
+ unsigned long start,
+ unsigned long end,
+ bool may_block);
+#else
+static inline void gfn_to_pfn_cache_invalidate_start(struct kvm *kvm,
+ unsigned long start,
+ unsigned long end,
+ bool may_block)
+{
+}
+#endif /* HAVE_KVM_PFNCACHE */
+
+#endif /* __KVM_MM_H__ */
diff --git a/virt/kvm/mmu_lock.h b/virt/kvm/mmu_lock.h
deleted file mode 100644
index 9e1308f9734c..000000000000
--- a/virt/kvm/mmu_lock.h
+++ /dev/null
@@ -1,23 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-
-#ifndef KVM_MMU_LOCK_H
-#define KVM_MMU_LOCK_H 1
-
-/*
- * Architectures can choose whether to use an rwlock or spinlock
- * for the mmu_lock. These macros, for use in common code
- * only, avoids using #ifdefs in places that must deal with
- * multiple architectures.
- */
-
-#ifdef KVM_HAVE_MMU_RWLOCK
-#define KVM_MMU_LOCK_INIT(kvm) rwlock_init(&(kvm)->mmu_lock)
-#define KVM_MMU_LOCK(kvm) write_lock(&(kvm)->mmu_lock)
-#define KVM_MMU_UNLOCK(kvm) write_unlock(&(kvm)->mmu_lock)
-#else
-#define KVM_MMU_LOCK_INIT(kvm) spin_lock_init(&(kvm)->mmu_lock)
-#define KVM_MMU_LOCK(kvm) spin_lock(&(kvm)->mmu_lock)
-#define KVM_MMU_UNLOCK(kvm) spin_unlock(&(kvm)->mmu_lock)
-#endif /* KVM_HAVE_MMU_RWLOCK */
-
-#endif
diff --git a/virt/kvm/pfncache.c b/virt/kvm/pfncache.c
new file mode 100644
index 000000000000..ce878f4be4da
--- /dev/null
+++ b/virt/kvm/pfncache.c
@@ -0,0 +1,337 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Kernel-based Virtual Machine driver for Linux
+ *
+ * This module enables kernel and guest-mode vCPU access to guest physical
+ * memory with suitable invalidation mechanisms.
+ *
+ * Copyright © 2021 Amazon.com, Inc. or its affiliates.
+ *
+ * Authors:
+ * David Woodhouse <dwmw2@infradead.org>
+ */
+
+#include <linux/kvm_host.h>
+#include <linux/kvm.h>
+#include <linux/highmem.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+
+#include "kvm_mm.h"
+
+/*
+ * MMU notifier 'invalidate_range_start' hook.
+ */
+void gfn_to_pfn_cache_invalidate_start(struct kvm *kvm, unsigned long start,
+ unsigned long end, bool may_block)
+{
+ DECLARE_BITMAP(vcpu_bitmap, KVM_MAX_VCPUS);
+ struct gfn_to_pfn_cache *gpc;
+ bool wake_vcpus = false;
+
+ spin_lock(&kvm->gpc_lock);
+ list_for_each_entry(gpc, &kvm->gpc_list, list) {
+ write_lock_irq(&gpc->lock);
+
+ /* Only a single page so no need to care about length */
+ if (gpc->valid && !is_error_noslot_pfn(gpc->pfn) &&
+ gpc->uhva >= start && gpc->uhva < end) {
+ gpc->valid = false;
+
+ /*
+ * If a guest vCPU could be using the physical address,
+ * it needs to be woken.
+ */
+ if (gpc->guest_uses_pa) {
+ if (!wake_vcpus) {
+ wake_vcpus = true;
+ bitmap_zero(vcpu_bitmap, KVM_MAX_VCPUS);
+ }
+ __set_bit(gpc->vcpu->vcpu_idx, vcpu_bitmap);
+ }
+
+ /*
+ * We cannot call mark_page_dirty() from here because
+ * this physical CPU might not have an active vCPU
+ * with which to do the KVM dirty tracking.
+ *
+ * Neither is there any point in telling the kernel MM
+ * that the underlying page is dirty. A vCPU in guest
+ * mode might still be writing to it up to the point
+ * where we wake them a few lines further down anyway.
+ *
+ * So all the dirty marking happens on the unmap.
+ */
+ }
+ write_unlock_irq(&gpc->lock);
+ }
+ spin_unlock(&kvm->gpc_lock);
+
+ if (wake_vcpus) {
+ unsigned int req = KVM_REQ_GPC_INVALIDATE;
+ bool called;
+
+ /*
+ * If the OOM reaper is active, then all vCPUs should have
+ * been stopped already, so perform the request without
+ * KVM_REQUEST_WAIT and be sad if any needed to be woken.
+ */
+ if (!may_block)
+ req &= ~KVM_REQUEST_WAIT;
+
+ called = kvm_make_vcpus_request_mask(kvm, req, vcpu_bitmap);
+
+ WARN_ON_ONCE(called && !may_block);
+ }
+}
+
+bool kvm_gfn_to_pfn_cache_check(struct kvm *kvm, struct gfn_to_pfn_cache *gpc,
+ gpa_t gpa, unsigned long len)
+{
+ struct kvm_memslots *slots = kvm_memslots(kvm);
+
+ if ((gpa & ~PAGE_MASK) + len > PAGE_SIZE)
+ return false;
+
+ if (gpc->gpa != gpa || gpc->generation != slots->generation ||
+ kvm_is_error_hva(gpc->uhva))
+ return false;
+
+ if (!gpc->valid)
+ return false;
+
+ return true;
+}
+EXPORT_SYMBOL_GPL(kvm_gfn_to_pfn_cache_check);
+
+static void __release_gpc(struct kvm *kvm, kvm_pfn_t pfn, void *khva,
+ gpa_t gpa, bool dirty)
+{
+ /* Unmap the old page if it was mapped before, and release it */
+ if (!is_error_noslot_pfn(pfn)) {
+ if (khva) {
+ if (pfn_valid(pfn))
+ kunmap(pfn_to_page(pfn));
+#ifdef CONFIG_HAS_IOMEM
+ else
+ memunmap(khva);
+#endif
+ }
+
+ kvm_release_pfn(pfn, dirty);
+ if (dirty)
+ mark_page_dirty(kvm, gpa);
+ }
+}
+
+static kvm_pfn_t hva_to_pfn_retry(struct kvm *kvm, unsigned long uhva)
+{
+ unsigned long mmu_seq;
+ kvm_pfn_t new_pfn;
+ int retry;
+
+ do {
+ mmu_seq = kvm->mmu_notifier_seq;
+ smp_rmb();
+
+ /* We always request a writeable mapping */
+ new_pfn = hva_to_pfn(uhva, false, NULL, true, NULL);
+ if (is_error_noslot_pfn(new_pfn))
+ break;
+
+ KVM_MMU_READ_LOCK(kvm);
+ retry = mmu_notifier_retry_hva(kvm, mmu_seq, uhva);
+ KVM_MMU_READ_UNLOCK(kvm);
+ if (!retry)
+ break;
+
+ cond_resched();
+ } while (1);
+
+ return new_pfn;
+}
+
+int kvm_gfn_to_pfn_cache_refresh(struct kvm *kvm, struct gfn_to_pfn_cache *gpc,
+ gpa_t gpa, unsigned long len, bool dirty)
+{
+ struct kvm_memslots *slots = kvm_memslots(kvm);
+ unsigned long page_offset = gpa & ~PAGE_MASK;
+ kvm_pfn_t old_pfn, new_pfn;
+ unsigned long old_uhva;
+ gpa_t old_gpa;
+ void *old_khva;
+ bool old_valid, old_dirty;
+ int ret = 0;
+
+ /*
+ * If must fit within a single page. The 'len' argument is
+ * only to enforce that.
+ */
+ if (page_offset + len > PAGE_SIZE)
+ return -EINVAL;
+
+ write_lock_irq(&gpc->lock);
+
+ old_gpa = gpc->gpa;
+ old_pfn = gpc->pfn;
+ old_khva = gpc->khva - offset_in_page(gpc->khva);
+ old_uhva = gpc->uhva;
+ old_valid = gpc->valid;
+ old_dirty = gpc->dirty;
+
+ /* If the userspace HVA is invalid, refresh that first */
+ if (gpc->gpa != gpa || gpc->generation != slots->generation ||
+ kvm_is_error_hva(gpc->uhva)) {
+ gfn_t gfn = gpa_to_gfn(gpa);
+
+ gpc->dirty = false;
+ gpc->gpa = gpa;
+ gpc->generation = slots->generation;
+ gpc->memslot = __gfn_to_memslot(slots, gfn);
+ gpc->uhva = gfn_to_hva_memslot(gpc->memslot, gfn);
+
+ if (kvm_is_error_hva(gpc->uhva)) {
+ ret = -EFAULT;
+ goto out;
+ }
+
+ gpc->uhva += page_offset;
+ }
+
+ /*
+ * If the userspace HVA changed or the PFN was already invalid,
+ * drop the lock and do the HVA to PFN lookup again.
+ */
+ if (!old_valid || old_uhva != gpc->uhva) {
+ unsigned long uhva = gpc->uhva;
+ void *new_khva = NULL;
+
+ /* Placeholders for "hva is valid but not yet mapped" */
+ gpc->pfn = KVM_PFN_ERR_FAULT;
+ gpc->khva = NULL;
+ gpc->valid = true;
+
+ write_unlock_irq(&gpc->lock);
+
+ new_pfn = hva_to_pfn_retry(kvm, uhva);
+ if (is_error_noslot_pfn(new_pfn)) {
+ ret = -EFAULT;
+ goto map_done;
+ }
+
+ if (gpc->kernel_map) {
+ if (new_pfn == old_pfn) {
+ new_khva = old_khva;
+ old_pfn = KVM_PFN_ERR_FAULT;
+ old_khva = NULL;
+ } else if (pfn_valid(new_pfn)) {
+ new_khva = kmap(pfn_to_page(new_pfn));
+#ifdef CONFIG_HAS_IOMEM
+ } else {
+ new_khva = memremap(pfn_to_hpa(new_pfn), PAGE_SIZE, MEMREMAP_WB);
+#endif
+ }
+ if (new_khva)
+ new_khva += page_offset;
+ else
+ ret = -EFAULT;
+ }
+
+ map_done:
+ write_lock_irq(&gpc->lock);
+ if (ret) {
+ gpc->valid = false;
+ gpc->pfn = KVM_PFN_ERR_FAULT;
+ gpc->khva = NULL;
+ } else {
+ /* At this point, gpc->valid may already have been cleared */
+ gpc->pfn = new_pfn;
+ gpc->khva = new_khva;
+ }
+ } else {
+ /* If the HVA→PFN mapping was already valid, don't unmap it. */
+ old_pfn = KVM_PFN_ERR_FAULT;
+ old_khva = NULL;
+ }
+
+ out:
+ if (ret)
+ gpc->dirty = false;
+ else
+ gpc->dirty = dirty;
+
+ write_unlock_irq(&gpc->lock);
+
+ __release_gpc(kvm, old_pfn, old_khva, old_gpa, old_dirty);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(kvm_gfn_to_pfn_cache_refresh);
+
+void kvm_gfn_to_pfn_cache_unmap(struct kvm *kvm, struct gfn_to_pfn_cache *gpc)
+{
+ void *old_khva;
+ kvm_pfn_t old_pfn;
+ bool old_dirty;
+ gpa_t old_gpa;
+
+ write_lock_irq(&gpc->lock);
+
+ gpc->valid = false;
+
+ old_khva = gpc->khva - offset_in_page(gpc->khva);
+ old_dirty = gpc->dirty;
+ old_gpa = gpc->gpa;
+ old_pfn = gpc->pfn;
+
+ /*
+ * We can leave the GPA → uHVA map cache intact but the PFN
+ * lookup will need to be redone even for the same page.
+ */
+ gpc->khva = NULL;
+ gpc->pfn = KVM_PFN_ERR_FAULT;
+
+ write_unlock_irq(&gpc->lock);
+
+ __release_gpc(kvm, old_pfn, old_khva, old_gpa, old_dirty);
+}
+EXPORT_SYMBOL_GPL(kvm_gfn_to_pfn_cache_unmap);
+
+
+int kvm_gfn_to_pfn_cache_init(struct kvm *kvm, struct gfn_to_pfn_cache *gpc,
+ struct kvm_vcpu *vcpu, bool guest_uses_pa,
+ bool kernel_map, gpa_t gpa, unsigned long len,
+ bool dirty)
+{
+ if (!gpc->active) {
+ rwlock_init(&gpc->lock);
+
+ gpc->khva = NULL;
+ gpc->pfn = KVM_PFN_ERR_FAULT;
+ gpc->uhva = KVM_HVA_ERR_BAD;
+ gpc->vcpu = vcpu;
+ gpc->kernel_map = kernel_map;
+ gpc->guest_uses_pa = guest_uses_pa;
+ gpc->valid = false;
+ gpc->active = true;
+
+ spin_lock(&kvm->gpc_lock);
+ list_add(&gpc->list, &kvm->gpc_list);
+ spin_unlock(&kvm->gpc_lock);
+ }
+ return kvm_gfn_to_pfn_cache_refresh(kvm, gpc, gpa, len, dirty);
+}
+EXPORT_SYMBOL_GPL(kvm_gfn_to_pfn_cache_init);
+
+void kvm_gfn_to_pfn_cache_destroy(struct kvm *kvm, struct gfn_to_pfn_cache *gpc)
+{
+ if (gpc->active) {
+ spin_lock(&kvm->gpc_lock);
+ list_del(&gpc->list);
+ spin_unlock(&kvm->gpc_lock);
+
+ kvm_gfn_to_pfn_cache_unmap(kvm, gpc);
+ gpc->active = false;
+ }
+}
+EXPORT_SYMBOL_GPL(kvm_gfn_to_pfn_cache_destroy);
]
|
| 258/2 |
2026/08/12 12:51 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 57421
output: 45
thoughts: 2235
Thoughts: **Analyzing the KVM Xen Event Channel Bug**
Okay, so let me break this down step-by-step. It looks like the `gpc->lock` was an `rwlock_t` since way back in the `gfn_to_pfn_cache` introduction. Then, `kvm_xen_set_evtchn_fast` and its call from `kvm_arch_set_irq_inatomic` was introduced. Specifically, `14243b387137a4afbe1df5d9dc15182d6657bb79` is the key commit. The bug report is very clear – it's crashing when trying to acquire `gpc->lock` with `read_lock_irqsave` inside `kvm_xen_set_evtchn_fast`. This is happening because `kvm_xen_set_evtchn_fast` is called from `xen_timer_callback` (hardirq) and `kvm_arch_set_irq_inatomic` which is invoked during `irqfd` waitqueue wakeups, meaning atomic context.
My initial thought is that the bug likely came in with `14243b387137a4afbe1df5d9dc15182d6657bb79`. Let me double-check. The code shows that `kvm_arch_set_irq_inatomic` is called from `irqfd_wakeup`, which is a waitqueue wakeup. Waitqueue wakeups usually involve spinlocks and disabled interrupts. `irqfd_wakeup` is called from `eventfd_signal`, and that can be in hardirq context. So yes, that commit seems like the source, which introduced the bug by calling `kvm_xen_set_evtchn_fast` from atomic context.
Now, let's look for other possible commits. What about `77c9b9dea4fb3e51e0d850db7f21cb1156d987bd`? That commit added the call from `xen_timer_callback` to `kvm_xen_set_evtchn_fast`. However, `kvm_arch_set_irq_inatomic` was already calling it since `14243b387137a4afbe1df5d9dc15182d6657bb79`. So the hardirq context bug existed before then.
Here's the rub - `rwlock_t` is a sleeping lock on `PREEMPT_RT`, and `read_lock_irqsave` on `PREEMPT_RT` can sleep. So that's the problem; calling this from atomic context is a bug on `PREEMPT_RT`. Since `kvm_arch_set_irq_inatomic` *is* called from atomic context and that calls the faulty function, the issue lies in the fact that `kvm_arch_set_irq_inatomic` started calling the problem function. That was `14243b387137a4afbe1df5d9dc15182d6657bb79`.
Let me look at the fix again. The fix introduces a `bool in_atomic` parameter to `kvm_xen_set_evtchn_fast`. This indicates, quite explicitly, that we have an atomic context issue.
So `kvm_arch_set_irq_inatomic` and `xen_timer_callback` are atomic contexts. `14243b387137a4afbe1df5d9dc15182d6657bb79` introduced the `kvm_arch_set_irq_inatomic` call, and `77c9b9dea4fb3e51e0d850db7f21cb1156d987bd` brought in `xen_timer_callback`.
The bug is usually introduced in the earliest commit that triggered the pattern. Thus, `14243b387137a4afbe1df5d9dc15182d6657bb79` seems like the one to focus on. To be absolutely certain, `kvm_arch_set_irq_inatomic` is called from `irqfd_wakeup`, and we know that's waitqueue wakeup with spinlocks and disabled interrupts. `irqfd_wakeup` is called from `eventfd_signal` which can be in
|
| 259/2 |
2026/08/12 12:51 |
tool |
git-log |
0m
Args: map[SourcePath:arch/x86/kvm/xen.c SymbolName:kvm_xen_set_evtchn_fast]
Results: map[Output:003d914220c9 KVM: x86/xen: allow vcpu_info content to be 'safely' copied
58f5ee5fedd9 KVM: Drop @gpa from exported gfn=>pfn cache check() and refresh() helpers
e308c24a358d KVM: Use gfn_to_pfn_cache's immutable "kvm" in kvm_gpc_check()
aba3caef5862 KVM: Shorten gfn_to_pfn_cache function names
79f772b9e800 KVM: x86: Query vcpu->vcpu_idx directly and drop its accessor, again
1a65105a5aba KVM: x86/xen: handle PV spinlocks slowpath
fde0451be8fb KVM: x86/xen: Support per-vCPU event channel upcall via local APIC
8733068b9bdb KVM: x86/xen: Make kvm_xen_set_evtchn() reusable from other places
7caf9571563e KVM: x86/xen: Use gfn_to_pfn_cache for vcpu_info
14243b387137 KVM: x86/xen: Add KVM_IRQ_ROUTING_XEN_EVTCHN and event channel delivery
]
|
| 260/2 |
2026/08/12 12:51 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 61077
output: 25
thoughts: 16
Thoughts: Okay, let's dive into this. Here's my thought process on encountering that code snippet:
**Analyzing the Code Snippet `7caf9571563e`**
Alright, a hash. Immediately, I'm thinking: this is likely a unique identifier. Given the hex characters, it could be a commit hash from Git, a database key, or possibly a cryptographic hash. The length is in the typical range for SHA-1, although SHA-256 would be more expected these days for new projects. I'd need more context to determine its exact origin or purpose. I'd immediately want to know where this string is coming from, is it a log entry, a database field, part of an API response? That would give me the first clue. If it's a Git commit, I'd probably go straight to the command line and use `git show` or `git log --oneline` to see the associated commit message and changes. If it's a database ID, I'd check the schema to see what it's linked to. If it's cryptographic, I'd try to figure out what data it's hashing. Without that, it's just a string, and I need to do some detective work to uncover its meaning.
|
| 261/2 |
2026/08/12 12:51 |
tool |
git-show |
0m
Args:
Results: map[Output:commit 7caf9571563eade546976d600dd023674b1c3185
Author: David Woodhouse <dwmw@amazon.co.uk>
Date: Thu Mar 3 15:41:15 2022 +0000
KVM: x86/xen: Use gfn_to_pfn_cache for vcpu_info
Currently, the fast path of kvm_xen_set_evtchn_fast() doesn't set the
index bits in the target vCPU's evtchn_pending_sel, because it only has
a userspace virtual address with which to do so. It just sets them in
the kernel, and kvm_xen_has_interrupt() then completes the delivery to
the actual vcpu_info structure when the vCPU runs.
Using a gfn_to_pfn_cache allows kvm_xen_set_evtchn_fast() to do the full
delivery in the common case.
Clean up the fallback case too, by moving the deferred delivery out into
a separate kvm_xen_inject_pending_events() function which isn't ever
called in atomic contexts as __kvm_xen_has_interrupt() is.
Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Message-Id: <20220303154127.202856-6-dwmw2@infradead.org>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index 5a0ff6a07431..fce4dca16e5b 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -606,9 +606,8 @@ struct kvm_vcpu_hv {
struct kvm_vcpu_xen {
u64 hypercall_rip;
u32 current_runstate;
- bool vcpu_info_set;
bool vcpu_time_info_set;
- struct gfn_to_hva_cache vcpu_info_cache;
+ struct gfn_to_pfn_cache vcpu_info_cache;
struct gfn_to_hva_cache vcpu_time_info_cache;
struct gfn_to_pfn_cache runstate_cache;
u64 last_steal;
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index e5d43b441c03..d67487efdba2 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -3158,9 +3158,9 @@ static int kvm_guest_time_update(struct kvm_vcpu *v)
if (vcpu->pv_time.active)
kvm_setup_guest_pvclock(v, &vcpu->pv_time, 0);
- if (vcpu->xen.vcpu_info_set)
- kvm_setup_pvclock_page(v, &vcpu->xen.vcpu_info_cache,
- offsetof(struct compat_vcpu_info, time));
+ if (vcpu->xen.vcpu_info_cache.active)
+ kvm_setup_guest_pvclock(v, &vcpu->xen.vcpu_info_cache,
+ offsetof(struct compat_vcpu_info, time));
if (vcpu->xen.vcpu_time_info_set)
kvm_setup_pvclock_page(v, &vcpu->xen.vcpu_time_info_cache, 0);
if (!v->vcpu_idx)
@@ -10424,6 +10424,9 @@ static int vcpu_run(struct kvm_vcpu *vcpu)
break;
kvm_clear_request(KVM_REQ_UNBLOCK, vcpu);
+ if (kvm_xen_has_pending_events(vcpu))
+ kvm_xen_inject_pending_events(vcpu);
+
if (kvm_cpu_has_pending_timer(vcpu))
kvm_inject_pending_timer_irqs(vcpu);
@@ -12236,6 +12239,9 @@ static inline bool kvm_vcpu_has_events(struct kvm_vcpu *vcpu)
kvm_x86_ops.nested_ops->hv_timer_pending(vcpu))
return true;
+ if (kvm_xen_has_pending_events(vcpu))
+ return true;
+
return false;
}
diff --git a/arch/x86/kvm/xen.c b/arch/x86/kvm/xen.c
index de1b3be7239e..56e8a6980b65 100644
--- a/arch/x86/kvm/xen.c
+++ b/arch/x86/kvm/xen.c
@@ -8,6 +8,7 @@
#include "x86.h"
#include "xen.h"
+#include "lapic.h"
#include "hyperv.h"
#include <linux/kvm_host.h>
@@ -246,23 +247,79 @@ void kvm_xen_update_runstate_guest(struct kvm_vcpu *v, int state)
mark_page_dirty_in_slot(v->kvm, gpc->memslot, gpc->gpa >> PAGE_SHIFT);
}
-int __kvm_xen_has_interrupt(struct kvm_vcpu *v)
+/*
+ * On event channel delivery, the vcpu_info may not have been accessible.
+ * In that case, there are bits in vcpu->arch.xen.evtchn_pending_sel which
+ * need to be marked into the vcpu_info (and evtchn_upcall_pending set).
+ * Do so now that we can sleep in the context of the vCPU to bring the
+ * page in, and refresh the pfn cache for it.
+ */
+void kvm_xen_inject_pending_events(struct kvm_vcpu *v)
{
unsigned long evtchn_pending_sel = READ_ONCE(v->arch.xen.evtchn_pending_sel);
- bool atomic = in_atomic() || !task_is_running(current);
- int err;
+ struct gfn_to_pfn_cache *gpc = &v->arch.xen.vcpu_info_cache;
+ unsigned long flags;
+
+ if (!evtchn_pending_sel)
+ return;
+
+ /*
+ * Yes, this is an open-coded loop. But that's just what put_user()
+ * does anyway. Page it in and retry the instruction. We're just a
+ * little more honest about it.
+ */
+ read_lock_irqsave(&gpc->lock, flags);
+ while (!kvm_gfn_to_pfn_cache_check(v->kvm, gpc, gpc->gpa,
+ sizeof(struct vcpu_info))) {
+ read_unlock_irqrestore(&gpc->lock, flags);
+
+ if (kvm_gfn_to_pfn_cache_refresh(v->kvm, gpc, gpc->gpa,
+ sizeof(struct vcpu_info)))
+ return;
+
+ read_lock_irqsave(&gpc->lock, flags);
+ }
+
+ /* Now gpc->khva is a valid kernel address for the vcpu_info */
+ if (IS_ENABLED(CONFIG_64BIT) && v->kvm->arch.xen.long_mode) {
+ 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));
+ 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));
+ WRITE_ONCE(vi->evtchn_upcall_pending, 1);
+ }
+ read_unlock_irqrestore(&gpc->lock, flags);
+
+ mark_page_dirty_in_slot(v->kvm, gpc->memslot, gpc->gpa >> PAGE_SHIFT);
+}
+
+int __kvm_xen_has_interrupt(struct kvm_vcpu *v)
+{
+ struct gfn_to_pfn_cache *gpc = &v->arch.xen.vcpu_info_cache;
+ unsigned long flags;
u8 rc = 0;
/*
* If the global upcall vector (HVMIRQ_callback_vector) is set and
* the vCPU's evtchn_upcall_pending flag is set, the IRQ is pending.
*/
- struct gfn_to_hva_cache *ghc = &v->arch.xen.vcpu_info_cache;
- struct kvm_memslots *slots = kvm_memslots(v->kvm);
- bool ghc_valid = slots->generation == ghc->generation &&
- !kvm_is_error_hva(ghc->hva) && ghc->memslot;
-
- unsigned int offset = offsetof(struct vcpu_info, evtchn_upcall_pending);
/* No need for compat handling here */
BUILD_BUG_ON(offsetof(struct vcpu_info, evtchn_upcall_pending) !=
@@ -272,101 +329,35 @@ int __kvm_xen_has_interrupt(struct kvm_vcpu *v)
BUILD_BUG_ON(sizeof(rc) !=
sizeof_field(struct compat_vcpu_info, evtchn_upcall_pending));
- /*
- * For efficiency, this mirrors the checks for using the valid
- * cache in kvm_read_guest_offset_cached(), but just uses
- * __get_user() instead. And falls back to the slow path.
- */
- if (!evtchn_pending_sel && ghc_valid) {
- /* Fast path */
- pagefault_disable();
- err = __get_user(rc, (u8 __user *)ghc->hva + offset);
- pagefault_enable();
- if (!err)
- return rc;
- }
-
- /* Slow path */
+ read_lock_irqsave(&gpc->lock, flags);
+ while (!kvm_gfn_to_pfn_cache_check(v->kvm, gpc, gpc->gpa,
+ sizeof(struct vcpu_info))) {
+ read_unlock_irqrestore(&gpc->lock, flags);
- /*
- * This function gets called from kvm_vcpu_block() after setting the
- * task to TASK_INTERRUPTIBLE, to see if it needs to wake immediately
- * from a HLT. So we really mustn't sleep. If the page ended up absent
- * at that point, just return 1 in order to trigger an immediate wake,
- * and we'll end up getting called again from a context where we *can*
- * fault in the page and wait for it.
- */
- if (atomic)
- return 1;
+ /*
+ * This function gets called from kvm_vcpu_block() after setting the
+ * task to TASK_INTERRUPTIBLE, to see if it needs to wake immediately
+ * from a HLT. So we really mustn't sleep. If the page ended up absent
+ * at that point, just return 1 in order to trigger an immediate wake,
+ * and we'll end up getting called again from a context where we *can*
+ * fault in the page and wait for it.
+ */
+ if (in_atomic() || !task_is_running(current))
+ return 1;
- if (!ghc_valid) {
- err = kvm_gfn_to_hva_cache_init(v->kvm, ghc, ghc->gpa, ghc->len);
- if (err || !ghc->memslot) {
+ if (kvm_gfn_to_pfn_cache_refresh(v->kvm, gpc, gpc->gpa,
+ sizeof(struct vcpu_info))) {
/*
* If this failed, userspace has screwed up the
* vcpu_info mapping. No interrupts for you.
*/
return 0;
}
+ read_lock_irqsave(&gpc->lock, flags);
}
- /*
- * Now we have a valid (protected by srcu) userspace HVA in
- * ghc->hva which points to the struct vcpu_info. If there
- * are any bits in the in-kernel evtchn_pending_sel then
- * we need to write those to the guest vcpu_info and set
- * its evtchn_upcall_pending flag. If there aren't any bits
- * to add, we only want to *check* evtchn_upcall_pending.
- */
- if (evtchn_pending_sel) {
- bool long_mode = v->kvm->arch.xen.long_mode;
-
- if (!user_access_begin((void __user *)ghc->hva, sizeof(struct vcpu_info)))
- return 0;
-
- if (IS_ENABLED(CONFIG_64BIT) && long_mode) {
- struct vcpu_info __user *vi = (void __user *)ghc->hva;
-
- /* Attempt to set the evtchn_pending_sel bits in the
- * guest, and if that succeeds then clear the same
- * bits in the in-kernel version. */
- asm volatile("1:\t" LOCK_PREFIX "orq %0, %1\n"
- "\tnotq %0\n"
- "\t" LOCK_PREFIX "andq %0, %2\n"
- "2:\n"
- _ASM_EXTABLE_UA(1b, 2b)
- : "=r" (evtchn_pending_sel),
- "+m" (vi->evtchn_pending_sel),
- "+m" (v->arch.xen.evtchn_pending_sel)
- : "0" (evtchn_pending_sel));
- } else {
- struct compat_vcpu_info __user *vi = (void __user *)ghc->hva;
- u32 evtchn_pending_sel32 = evtchn_pending_sel;
-
- /* Attempt to set the evtchn_pending_sel bits in the
- * guest, and if that succeeds then clear the same
- * bits in the in-kernel version. */
- asm volatile("1:\t" LOCK_PREFIX "orl %0, %1\n"
- "\tnotl %0\n"
- "\t" LOCK_PREFIX "andl %0, %2\n"
- "2:\n"
- _ASM_EXTABLE_UA(1b, 2b)
- : "=r" (evtchn_pending_sel32),
- "+m" (vi->evtchn_pending_sel),
- "+m" (v->arch.xen.evtchn_pending_sel)
- : "0" (evtchn_pending_sel32));
- }
- rc = 1;
- unsafe_put_user(rc, (u8 __user *)ghc->hva + offset, err);
-
- err:
- user_access_end();
-
- mark_page_dirty_in_slot(v->kvm, ghc->memslot, ghc->gpa >> PAGE_SHIFT);
- } else {
- __get_user(rc, (u8 __user *)ghc->hva + offset);
- }
-
+ rc = ((struct vcpu_info *)gpc->khva)->evtchn_upcall_pending;
+ read_unlock_irqrestore(&gpc->lock, flags);
return rc;
}
@@ -456,25 +447,18 @@ int kvm_xen_vcpu_set_attr(struct kvm_vcpu *vcpu, struct kvm_xen_vcpu_attr *data)
offsetof(struct compat_vcpu_info, time));
if (data->u.gpa == GPA_INVALID) {
- vcpu->arch.xen.vcpu_info_set = false;
+ kvm_gfn_to_pfn_cache_destroy(vcpu->kvm, &vcpu->arch.xen.vcpu_info_cache);
r = 0;
break;
}
- /* It must fit within a single page */
- if ((data->u.gpa & ~PAGE_MASK) + sizeof(struct vcpu_info) > PAGE_SIZE) {
- r = -EINVAL;
- break;
- }
-
- r = kvm_gfn_to_hva_cache_init(vcpu->kvm,
+ r = kvm_gfn_to_pfn_cache_init(vcpu->kvm,
&vcpu->arch.xen.vcpu_info_cache,
- data->u.gpa,
+ NULL, KVM_HOST_USES_PFN, data->u.gpa,
sizeof(struct vcpu_info));
- if (!r) {
- vcpu->arch.xen.vcpu_info_set = true;
+ if (!r)
kvm_make_request(KVM_REQ_CLOCK_UPDATE, vcpu);
- }
+
break;
case KVM_XEN_VCPU_ATTR_TYPE_VCPU_TIME_INFO:
@@ -630,7 +614,7 @@ int kvm_xen_vcpu_get_attr(struct kvm_vcpu *vcpu, struct kvm_xen_vcpu_attr *data)
switch (data->type) {
case KVM_XEN_VCPU_ATTR_TYPE_VCPU_INFO:
- if (vcpu->arch.xen.vcpu_info_set)
+ if (vcpu->arch.xen.vcpu_info_cache.active)
data->u.gpa = vcpu->arch.xen.vcpu_info_cache.gpa;
else
data->u.gpa = GPA_INVALID;
@@ -903,16 +887,17 @@ int kvm_xen_set_evtchn_fast(struct kvm_kernel_irq_routing_entry *e,
if (!vcpu)
return -1;
- if (!vcpu->arch.xen.vcpu_info_set)
+ if (!vcpu->arch.xen.vcpu_info_cache.active)
return -1;
if (e->xen_evtchn.port >= max_evtchn_port(kvm))
return -1;
rc = -EWOULDBLOCK;
- read_lock_irqsave(&gpc->lock, flags);
idx = srcu_read_lock(&kvm->srcu);
+
+ read_lock_irqsave(&gpc->lock, flags);
if (!kvm_gfn_to_pfn_cache_check(kvm, gpc, gpc->gpa, PAGE_SIZE))
goto out_rcu;
@@ -940,17 +925,44 @@ int kvm_xen_set_evtchn_fast(struct kvm_kernel_irq_routing_entry *e,
} else if (test_bit(e->xen_evtchn.port, mask_bits)) {
rc = -1; /* Masked */
} else {
- rc = 1; /* Delivered. But was the vCPU waking already? */
- if (!test_and_set_bit(port_word_bit, &vcpu->arch.xen.evtchn_pending_sel))
- kick_vcpu = true;
+ rc = 1; /* Delivered to the bitmap in shared_info. */
+ /* Now switch to the vCPU's vcpu_info to set the index and pending_sel */
+ read_unlock_irqrestore(&gpc->lock, flags);
+ gpc = &vcpu->arch.xen.vcpu_info_cache;
+
+ read_lock_irqsave(&gpc->lock, flags);
+ if (!kvm_gfn_to_pfn_cache_check(kvm, gpc, gpc->gpa, 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;
+ }
+ }
}
out_rcu:
- srcu_read_unlock(&kvm->srcu, idx);
read_unlock_irqrestore(&gpc->lock, flags);
+ srcu_read_unlock(&kvm->srcu, idx);
if (kick_vcpu) {
- kvm_make_request(KVM_REQ_EVENT, vcpu);
+ kvm_make_request(KVM_REQ_UNBLOCK, vcpu);
kvm_vcpu_kick(vcpu);
}
@@ -1052,4 +1064,6 @@ void kvm_xen_destroy_vcpu(struct kvm_vcpu *vcpu)
{
kvm_gfn_to_pfn_cache_destroy(vcpu->kvm,
&vcpu->arch.xen.runstate_cache);
+ kvm_gfn_to_pfn_cache_destroy(vcpu->kvm,
+ &vcpu->arch.xen.vcpu_info_cache);
}
diff --git a/arch/x86/kvm/xen.h b/arch/x86/kvm/xen.h
index 54b2bf4c3001..7dd0590f93e1 100644
--- a/arch/x86/kvm/xen.h
+++ b/arch/x86/kvm/xen.h
@@ -15,6 +15,7 @@
extern struct static_key_false_deferred kvm_xen_enabled;
int __kvm_xen_has_interrupt(struct kvm_vcpu *vcpu);
+void kvm_xen_inject_pending_events(struct kvm_vcpu *vcpu);
int kvm_xen_vcpu_set_attr(struct kvm_vcpu *vcpu, struct kvm_xen_vcpu_attr *data);
int kvm_xen_vcpu_get_attr(struct kvm_vcpu *vcpu, struct kvm_xen_vcpu_attr *data);
int kvm_xen_hvm_set_attr(struct kvm *kvm, struct kvm_xen_hvm_attr *data);
@@ -46,11 +47,19 @@ static inline bool kvm_xen_hypercall_enabled(struct kvm *kvm)
static inline int kvm_xen_has_interrupt(struct kvm_vcpu *vcpu)
{
if (static_branch_unlikely(&kvm_xen_enabled.key) &&
- vcpu->arch.xen.vcpu_info_set && vcpu->kvm->arch.xen.upcall_vector)
+ vcpu->arch.xen.vcpu_info_cache.active &&
+ vcpu->kvm->arch.xen.upcall_vector)
return __kvm_xen_has_interrupt(vcpu);
return 0;
}
+
+static inline bool kvm_xen_has_pending_events(struct kvm_vcpu *vcpu)
+{
+ return static_branch_unlikely(&kvm_xen_enabled.key) &&
+ vcpu->arch.xen.evtchn_pending_sel;
+}
+
#else
static inline int kvm_xen_write_hypercall_page(struct kvm_vcpu *vcpu, u64 data)
{
@@ -83,6 +92,15 @@ static inline int kvm_xen_has_interrupt(struct kvm_vcpu *vcpu)
{
return 0;
}
+
+static inline void kvm_xen_inject_pending_events(struct kvm_vcpu *vcpu)
+{
+}
+
+static inline bool kvm_xen_has_pending_events(struct kvm_vcpu *vcpu)
+{
+ return false;
+}
#endif
int kvm_xen_hypercall(struct kvm_vcpu *vcpu);
]
|
| 262/2 |
2026/08/12 12:51 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 68394
output: 52
thoughts: 2038
Thoughts: **Root Cause Analysis: Lock Depletion in KVM-Xen Integration**
Okay, let me walk through this. My initial thought was that `7caf9571563eade546976d600dd023674b1c3185` added the second `read_lock_irqsave` in `kvm_xen_set_evtchn_fast`, but the first one was already there from `14243b387137a4afbe1df5d9dc15182d6657bb79`. Further, `kvm_arch_set_irq_inatomic`, which calls `kvm_xen_set_evtchn_fast`, was also introduced in `14243b387137a4afbe1df5d9dc15182d6657bb79`. So, this `14243b387137a4afbe1df5d9dc15182d6657bb79` commit looks like the culprit.
But wait, the fix modifies both `read_lock_irqsave` calls, and the atomic context call. So I need to go deeper to understand where the root issue originates. The problem seems to be that `kvm_xen_set_evtchn_fast` uses a sleeping lock while being called from an atomic context. Both the atomic call (`kvm_arch_set_irq_inatomic`) and the initial `read_lock_irqsave` were introduced in `14243b387137a4afbe1df5d9dc15182d6657bb79`. That's likely the problem commit, which I need to confirm.
Let's quickly verify that the initial commit, `14243b387137a4afbe1df5d9dc15182d6657bb79`, is the source. Based on the commit message, the code supports eventfd-triggered IRQ routing and, critically, introduces `kvm_xen_set_evtchn_fast()` to use a pre-mapped shared info page. I see that the message says `kvm_arch_set_irq_inatomic` calls `kvm_xen_set_evtchn_fast`, and that in turn, `irqfd_wakeup` is called from waitqueue wakeup, and thus atomic context, while holding a spinlock. It calls `read_lock_irqsave(&gpc->lock, flags)`, where `gpc->lock` is an `rwlock_t`, which in an RT kernel is a sleeping lock. That means this is the bug.
I quickly scan through the code, and confirm the introduction of the problematic lock in `14243b387137a4afbe1df5d9dc15182d6657bb79`. On non-RT kernels, this works because `read_lock_irqsave` is a spinlock and doesn't sleep; however, on RT kernels, the lock sleeps. This confirms my suspicion that `14243b387137a4afbe1df5d9dc15182d6657bb79` is the introduction.
Let's confirm with the fix:
```c
-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)
```
```c
- 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);
+ }
```
The fix confirms that the issue is using `read_lock_irqsave` in `kvm_xen_set_evtchn_fast` and confirms that `kvm_xen_set_evtchn_fast` was introduced in `14243b38713
|
| 263/2 |
2026/08/12 12:52 |
tool |
set-results |
0m
Args: map[FixesHash:14243b387137a4afbe1df5d9dc15182d6657bb79]
Results: map[FixesHash:14243b387137a4afbe1df5d9dc15182d6657bb79]
|
| 264/1 |
2026/08/12 12:52 |
action |
format-fixes |
0m
Results: map[Fixes:map[AuthorEmail:dwmw@amazon.co.uk AuthorName:David Woodhouse Hash:14243b387137a4afbe1df5d9dc15182d6657bb79 Title:KVM: x86/xen: Add KVM_IRQ_ROUTING_XEN_EVTCHN and event channel delivery]]
|
| 265/1 |
2026/08/12 12:52 |
action |
get-maintainers |
0m
Results: map[Recipients:[map[Email:bp@alien8.de Name:Borislav Petkov To:true] map[Email:dave.hansen@linux.intel.com Name:Dave Hansen To:true] map[Email:dwmw2@infradead.org Name:David Woodhouse To:true] map[Email:hpa@zytor.com Name:H. Peter Anvin To:false] map[Email:kvm@vger.kernel.org Name: To:true] map[Email:linux-kernel@vger.kernel.org Name: To:false] map[Email:mingo@redhat.com Name:Ingo Molnar To:true] map[Email:paul@xen.org Name:Paul Durrant To:true] map[Email:pbonzini@redhat.com Name:Paolo Bonzini To:true] map[Email:seanjc@google.com Name:Sean Christopherson To:true] map[Email:tglx@kernel.org Name:Thomas Gleixner To:true] map[Email:x86@kernel.org Name: To:true] map[Email:dwmw@amazon.co.uk Name:David Woodhouse To:true]]]
|
| 266/1 |
2026/08/12 12:52 |
action |
get-recent-commits |
0m
Results: map[RecentCommits:KVM: x86: Nullify irqfd->producer if updating IRTE for bypass fails
KVM: x86: Add mode-aware versions of kvm_<reg>_{read,write}() helpers
KVM: x86/xen: Don't truncate RAX when handling hypercall from protected guest
KVM: x86/xen: Bug the VM if 32-bit KVM observes a 64-bit mode hypercall
KVM: x86: Take PIC lock on KVM_GET_IRQCHIP path
Convert 'alloc_obj' family to use the new default GFP_KERNEL argument
treewide: Replace kmalloc with kmalloc_obj for non-scalar types
KVM: x86: Assert that non-MSI doesn't have bypass vCPU when deleting producer
KVM: x86: Add a wrapper to handle common case of IRQ delivery without dest_map
KVM: x86: Export KVM-internal symbols for sub-modules only
KVM: x86: Move kvm_intr_is_single_vcpu() to lapic.c
KVM: x86: Move kvm_irq_delivery_to_apic() from irq.c to lapic.c
KVM: x86/xen: Fix cleanup logic in emulation of Xen schedop poll hypercalls
Revert "kvm: detect assigned device via irqbypass manager"
KVM: x86/xen: Allow 'out of range' event channel ports in IRQ routing table.
KVM: x86: Rename kvm_set_msi_irq() => kvm_msi_to_lapic_irq()
KVM: x86: Decouple device assignment from IRQ bypass
KVM: x86: WARN if IRQ bypass routing is updated without in-kernel local APIC
KVM: x86: WARN if IRQ bypass isn't supported in kvm_pi_update_irte()
KVM: x86: Drop superfluous "has assigned device" check in kvm_pi_update_irte()
]
|
| 267/1 |
2026/08/12 12:52 |
agent |
description-generator |
0m
Model:
Instruction:
You are an experienced Linux kernel developer tasked with writing a commit description for
a kernel bug fixing commit. The description should start with a one-line summary,
and then include description of the bug being fixed, and how it's fixed by the provided patch.
The one-line summary should describe the change being made, rather than mention the tool that
detected the bug.
The description must not contain lines starting with '#' because they will dropped by git as comments.
The description must not contain lines starting with '--' or '---' (including inline code diffs)
because they may confuse git/patch utilities.
Your final reply should contain only the text of the commit description.
The one-line summary must be not longer than 72 characters.
IMPORTANT: Do not wrap lines manually (e.g., at 80 characters); we will reformat the text
automatically, so keep paragraphs as single lines without newlines.
Generally try to phrase the description without mentioning syzkaller
(avoid phrases like "the bug was triggered by syzkaller" or "the bug was triggered by fuzzer", etc).
How the bug was triggered is generally an irrelevant detail.
Any bug triggered by a fuzzer can also be triggered by a malicious user, or a buggy program.
If the crash is reported by a sanitizer (e.g., KASAN, KMSAN, lockdep), include the relevant
parts of the sanitizer output to illustrate the problem. Exclude less relevant sections,
as the stack trace can be very long. Describe the execution path that leads to the manifestation
of the kernel bug.
Prefer calling several tools at the same time to save round-trips.
Prompt:
The crash that corresponds to the bug is:
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
preempt_count: 10001, expected: 0
RCU nest depth: 0, expected: 0
1 lock held by kworker/0:5/5685:
#0: ffff8881964ddb48 (&kvm->srcu){.?.+}-{0:0}, at: srcu_lock_acquire include/linux/srcu.h:187 [inline]
#0: ffff8881964ddb48 (&kvm->srcu){.?.+}-{0:0}, at: srcu_read_lock include/linux/srcu.h:294 [inline]
#0: ffff8881964ddb48 (&kvm->srcu){.?.+}-{0:0}, at: kvm_xen_set_evtchn_fast+0x1be/0x990 arch/x86/kvm/xen.c:1820
irq event stamp: 78678
hardirqs last enabled at (78677): [<ffffffff8194da12>] raw_spin_rq_unlock_irq kernel/sched/sched.h:1673 [inline]
hardirqs last enabled at (78677): [<ffffffff8194da12>] finish_lock_switch kernel/sched/core.c:5241 [inline]
hardirqs last enabled at (78677): [<ffffffff8194da12>] finish_task_switch+0x412/0xc60 kernel/sched/core.c:5359
hardirqs last disabled at (78678): [<ffffffff8b4edc4e>] sysvec_apic_timer_interrupt+0xe/0xc0 arch/x86/kernel/apic/apic.c:1062
softirqs last enabled at (78668): [<ffffffff8187f8b0>] __local_bh_enable_ip+0x1a0/0x2b0 kernel/softirq.c:305
softirqs last disabled at (78666): [<ffffffff861be59f>] local_bh_disable include/linux/bottom_half.h:20 [inline]
softirqs last disabled at (78666): [<ffffffff861be59f>] rcu_read_lock_bh include/linux/rcupdate.h:893 [inline]
softirqs last disabled at (78666): [<ffffffff861be59f>] keep_key_fresh drivers/net/wireguard/send.c:129 [inline]
softirqs last disabled at (78666): [<ffffffff861be59f>] wg_packet_create_data_done drivers/net/wireguard/send.c:259 [inline]
softirqs last disabled at (78666): [<ffffffff861be59f>] wg_packet_tx_worker+0x25f/0x7e0 drivers/net/wireguard/send.c:276
Preemption disabled at:
[<ffffffff8b50149d>] __schedule_loop kernel/sched/core.c:7310 [inline]
[<ffffffff8b50149d>] schedule+0x15d/0x2b0 kernel/sched/core.c:7326
CPU: 0 UID: 0 PID: 5685 Comm: kworker/0:5 Not tainted syzkaller #1 PREEMPT_{RT,(full)}
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
Workqueue: 0x0 (wg-crypt-wg1)
Call Trace:
<IRQ>
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
instr_sysvec_apic_timer_interrupt arch/x86/kernel/apic/apic.c:1062 [inline]
sysvec_apic_timer_interrupt+0xa1/0xc0 arch/x86/kernel/apic/apic.c:1062
</IRQ>
<TASK>
asm_sysvec_apic_timer_interrupt+0x1a/0x20 arch/x86/include/asm/idtentry.h:674
RIP: 0010:finish_task_switch+0x417/0xc60 kernel/sched/core.c:5361
Code: 04 00 00 41 c7 84 24 20 0e 00 00 00 00 00 00 0f 1f 44 00 00 49 83 c4 48 4c 89 e7 e8 b3 99 bd 09 e8 de c7 38 00 fb 4c 8b 65 c8 <49> 8d bc 24 e0 16 00 00 48 89 f8 48 c1 e8 03 42 0f b6 04 30 84 c0
RSP: 0018:ffffc9000442fa60 EFLAGS: 00000206
RAX: 0000000000013355 RBX: ffff88813883c6e0 RCX: 0000000080000001
RDX: 0000000000000000 RSI: ffffffff8daa4fe1 RDI: ffffffff8bd1a580
RBP: ffffc9000442fab0 R08: ffffffff8fd9f0f7 R09: 1ffffffff1fb3e1e
R10: dffffc0000000000 R11: fffffbfff1fb3e1f R12: ffff888114048000
R13: ffff88813883c6a8 R14: dffffc0000000000 R15: 1ffff110271078dc
context_switch kernel/sched/core.c:5513 [inline]
__schedule+0x1711/0x5530 kernel/sched/core.c:7234
__schedule_loop kernel/sched/core.c:7311 [inline]
schedule+0x164/0x2b0 kernel/sched/core.c:7326
worker_thread+0xc12/0xfb0 kernel/workqueue.c:3501
kthread+0x388/0x470 kernel/kthread.c:436
ret_from_fork+0x514/0xb70 arch/x86/kernel/process.c:158
ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245
</TASK>
=============================
[ BUG: Invalid wait context ]
syzkaller #1 Tainted: G W
-----------------------------
kworker/0:5/5685 is trying to lock:
ffff8881964dd530 (&gpc->lock){....}-{3:3}, at: kvm_xen_set_evtchn_fast+0x1f4/0x990 arch/x86/kvm/xen.c:1822
other info that might help us debug this:
context-{2:2}
1 lock held by kworker/0:5/5685:
#0: ffff8881964ddb48 (&kvm->srcu){.?.+}-{0:0}, at: srcu_lock_acquire include/linux/srcu.h:187 [inline]
#0: ffff8881964ddb48 (&kvm->srcu){.?.+}-{0:0}, at: srcu_read_lock include/linux/srcu.h:294 [inline]
#0: ffff8881964ddb48 (&kvm->srcu){.?.+}-{0:0}, at: kvm_xen_set_evtchn_fast+0x1be/0x990 arch/x86/kvm/xen.c:1820
stack backtrace:
CPU: 0 UID: 0 PID: 5685 Comm: kworker/0:5 Tainted: G W syzkaller #1 PREEMPT_{RT,(full)}
Tainted: [W]=WARN
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
Workqueue: 0x0 (wg-crypt-wg1)
Call Trace:
<IRQ>
dump_stack_lvl+0xe8/0x150 lib/dump_stack.c:120
print_lock_invalid_wait_context kernel/locking/lockdep.c:4830 [inline]
check_wait_context kernel/locking/lockdep.c:4902 [inline]
__lock_acquire+0xe39/0x2ce0 kernel/locking/lockdep.c:5187
lock_acquire+0x106/0x350 kernel/locking/lockdep.c:5868
rt_read_lock+0xcc/0x4b0 kernel/locking/spinlock_rt.c:249
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
instr_sysvec_apic_timer_interrupt arch/x86/kernel/apic/apic.c:1062 [inline]
sysvec_apic_timer_interrupt+0xa1/0xc0 arch/x86/kernel/apic/apic.c:1062
</IRQ>
<TASK>
asm_sysvec_apic_timer_interrupt+0x1a/0x20 arch/x86/include/asm/idtentry.h:674
RIP: 0010:finish_task_switch+0x417/0xc60 kernel/sched/core.c:5361
Code: 04 00 00 41 c7 84 24 20 0e 00 00 00 00 00 00 0f 1f 44 00 00 49 83 c4 48 4c 89 e7 e8 b3 99 bd 09 e8 de c7 38 00 fb 4c 8b 65 c8 <49> 8d bc 24 e0 16 00 00 48 89 f8 48 c1 e8 03 42 0f b6 04 30 84 c0
RSP: 0018:ffffc9000442fa60 EFLAGS: 00000206
RAX: 0000000000013355 RBX: ffff88813883c6e0 RCX: 0000000080000001
RDX: 0000000000000000 RSI: ffffffff8daa4fe1 RDI: ffffffff8bd1a580
RBP: ffffc9000442fab0 R08: ffffffff8fd9f0f7 R09: 1ffffffff1fb3e1e
R10: dffffc0000000000 R11: fffffbfff1fb3e1f R12: ffff888114048000
R13: ffff88813883c6a8 R14: dffffc0000000000 R15: 1ffff110271078dc
context_switch kernel/sched/core.c:5513 [inline]
__schedule+0x1711/0x5530 kernel/sched/core.c:7234
__schedule_loop kernel/sched/core.c:7311 [inline]
schedule+0x164/0x2b0 kernel/sched/core.c:7326
worker_thread+0xc12/0xfb0 kernel/workqueue.c:3501
kthread+0x388/0x470 kernel/kthread.c:436
ret_from_fork+0x514/0xb70 arch/x86/kernel/process.c:158
ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245
</TASK>
----------------
Code disassembly (best guess), 1 bytes skipped:
0: 00 00 add %al,(%rax)
2: 41 c7 84 24 20 0e 00 movl $0x0,0xe20(%r12)
9: 00 00 00 00 00
e: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)
13: 49 83 c4 48 add $0x48,%r12
17: 4c 89 e7 mov %r12,%rdi
1a: e8 b3 99 bd 09 call 0x9bd99d2
1f: e8 de c7 38 00 call 0x38c802
24: fb sti
25: 4c 8b 65 c8 mov -0x38(%rbp),%r12
* 29: 49 8d bc 24 e0 16 00 lea 0x16e0(%r12),%rdi <-- trapping instruction
30: 00
31: 48 89 f8 mov %rdi,%rax
34: 48 c1 e8 03 shr $0x3,%rax
38: 42 0f b6 04 30 movzbl (%rax,%r14,1),%eax
3d: 84 c0 test %al,%al
The explanation of the root cause of the bug is:
The root cause of this crash is an attempt to acquire a sleeping lock from a hardirq context on a `PREEMPT_RT` kernel.
### Detailed Analysis
1. **The Lock:**
The crash happens when `kvm_xen_set_evtchn_fast()` attempts to acquire `gpc->lock` via `read_lock_irqsave(&gpc->lock, flags)`. The `gpc->lock` is an `rwlock_t` used to protect the `gfn_to_pfn_cache`.
In a `PREEMPT_RT` kernel, `rwlock_t` is mapped to `rt_rw_lock`, which is a sleeping lock (rtmutex-based). It cannot be acquired in hardirq context because it may sleep while waiting for the lock or while waking up waiters during unlock.
2. **The Context:**
`kvm_xen_set_evtchn_fast()` is called from `xen_timer_callback()`, which is an hrtimer callback initialized with `HRTIMER_MODE_ABS_HARD`. This means the timer callback executes in hardirq context, even on `PREEMPT_RT`.
Additionally, `kvm_xen_set_evtchn_fast()` is also called from `kvm_arch_set_irq_inatomic()`, which is invoked during `irqfd` waitqueue wakeups (also a hardirq/atomic context).
3. **The Lockdep Splat:**
The lockdep splat mentions `srcu_read_lock` because `kvm->srcu` was acquired just before the `read_lock_irqsave`. While `srcu_read_lock` itself is perfectly safe in hardirq context (it just increments a per-CPU counter), lockdep prints it as a "held lock" when the actual sleeping function (`rt_read_lock`) is called.
### How to Fix
To fix this, `kvm_xen_set_evtchn_fast()` must avoid using `read_lock_irqsave()` when called from an atomic context. Instead, it should use `read_trylock()`. If the trylock fails, it can safely fall back to the slow path (returning `-EWOULDBLOCK` or kicking the vCPU), which is exactly what the callers expect and handle.
This pattern of using `read_trylock()` for `gpc->lock` in atomic contexts is already established in KVM Xen (e.g., in `kvm_xen_update_runstate_guest()`).
You should modify `kvm_xen_set_evtchn_fast()` to accept a `bool in_atomic` parameter:
```c
// arch/x86/kvm/xen.h
int kvm_xen_set_evtchn_fast(struct kvm_xen_evtchn *xe, struct kvm *kvm, bool in_atomic);
```
Then update `kvm_xen_set_evtchn_fast()` in `arch/x86/kvm/xen.c` to use `read_trylock()` when `in_atomic` is true:
```c
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);
}
```
You will need to do this for **both** `gpc->lock` acquisitions in that function. For the second acquisition (the `vcpu_info_cache`), if the trylock fails, you should fall back to setting the bit in-kernel and kicking the vCPU, just like it does when `kvm_gpc_check()` fails:
```c
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);
}
```
Make sure to add the `out_rcu_no_lock` label to avoid unlocking a lock you don't hold:
```c
out_rcu:
read_unlock_irqrestore(&gpc->lock, flags);
out_rcu_no_lock:
srcu_read_unlock(&kvm->srcu, idx);
```
Finally, update the callers to pass the correct context:
- `xen_timer_callback()` in `arch/x86/kvm/xen.c`: pass `true`.
- `kvm_arch_set_irq_inatomic()` in `arch/x86/kvm/irq.c`: pass `true`.
- `kvm_xen_set_evtchn()` in `arch/x86/kvm/xen.c` (both calls): pass `false` (since it runs in process context, e.g., from a hypercall or the irqfd workqueue).
The diff of the bug fix is:
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);
Additional description of the patch:
I have implemented the fix for the "sleeping function called from invalid context" bug in `kvm_xen_set_evtchn_fast()`.
### Explanation of the Fix
The crash occurs because `kvm_xen_set_evtchn_fast()` attempts to acquire `gpc->lock` using `read_lock_irqsave()` while executing in a hardirq context (e.g., from `xen_timer_callback()` or `kvm_arch_set_irq_inatomic()`). On a `PREEMPT_RT` kernel, `rwlock_t` is mapped to `rt_rw_lock`, which is a sleeping lock and cannot be acquired in atomic or hardirq contexts.
To resolve this, I modified `kvm_xen_set_evtchn_fast()` to accept a `bool in_atomic` parameter. This allows the function to adapt its locking strategy based on the context from which it is called:
1. **Atomic Context (`in_atomic = true`)**: The function uses `read_trylock(&gpc->lock)`. If the lock is contended and the trylock fails, it immediately falls back to the slow path. For the first lock (shared info), it returns `-EWOULDBLOCK`. For the second lock (vcpu info), it sets the pending bit in-kernel, kicks the vCPU, and returns `1`. This avoids any sleeping operations in atomic context.
2. **Process Context (`in_atomic = false`)**: The function continues to use `read_lock_irqsave(&gpc->lock, flags)`, which is safe and allows it to wait for the lock if necessary.
This approach perfectly aligns with the established idiom in the KVM Xen subsystem (e.g., `kvm_xen_update_runstate_guest()`), which also uses `read_trylock()` with a slow-path fallback to safely access `gpc->lock` from atomic contexts.
I also updated all callers of `kvm_xen_set_evtchn_fast()` to pass the correct context flag:
- `kvm_arch_set_irq_inatomic()`: Passes `true` (atomic context).
- `xen_timer_callback()`: Passes `true` (hrtimer callback, hardirq context).
- `kvm_xen_set_evtchn()`: Passes `false` (process context, e.g., hypercall or irqfd workqueue).
Here are summaries of recent commits that touched the same files.
Format the summary line consistently with these, look how prefixes
are specified, letter capitalization, style, etc.
KVM: x86: Nullify irqfd->producer if updating IRTE for bypass fails
KVM: x86: Add mode-aware versions of kvm_<reg>_{read,write}() helpers
KVM: x86/xen: Don't truncate RAX when handling hypercall from protected guest
KVM: x86/xen: Bug the VM if 32-bit KVM observes a 64-bit mode hypercall
KVM: x86: Take PIC lock on KVM_GET_IRQCHIP path
Convert 'alloc_obj' family to use the new default GFP_KERNEL argument
treewide: Replace kmalloc with kmalloc_obj for non-scalar types
KVM: x86: Assert that non-MSI doesn't have bypass vCPU when deleting producer
KVM: x86: Add a wrapper to handle common case of IRQ delivery without dest_map
KVM: x86: Export KVM-internal symbols for sub-modules only
KVM: x86: Move kvm_intr_is_single_vcpu() to lapic.c
KVM: x86: Move kvm_irq_delivery_to_apic() from irq.c to lapic.c
KVM: x86/xen: Fix cleanup logic in emulation of Xen schedop poll hypercalls
Revert "kvm: detect assigned device via irqbypass manager"
KVM: x86/xen: Allow 'out of range' event channel ports in IRQ routing table.
KVM: x86: Rename kvm_set_msi_irq() => kvm_msi_to_lapic_irq()
KVM: x86: Decouple device assignment from IRQ bypass
KVM: x86: WARN if IRQ bypass routing is updated without in-kernel local APIC
KVM: x86: WARN if IRQ bypass isn't supported in kvm_pi_update_irte()
KVM: x86: Drop superfluous "has assigned device" check in kvm_pi_update_irte()
Reply: KVM: x86/xen: Avoid sleeping lock in kvm_xen_set_evtchn_fast()
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:
<IRQ>
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
</IRQ>
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.
|
| 268/2 |
2026/08/12 12:52 |
llm |
description-generator |
0m
Model:
Tokens:
input: 12357
output: 727
thoughts: 4336
Thoughts: **KVM: x86/xen: Avoid sleeping lock in kvm_xen_set_evtchn_fast()**
I'm addressing a critical issue in the KVM/Xen code, specifically the `kvm_xen_set_evtchn_fast()` function. On PREEMPT_RT kernels, attempting to acquire the `gpc->lock` (an `rwlock_t`) using `read_lock_irqsave()` within this function can trigger a "sleeping function called from invalid context" BUG. This is because `rwlock_t` becomes a sleeping lock (`rt_rw_lock`) under PREEMPT_RT, and you can't sleep in hardirq or atomic contexts. The problem arises because `kvm_xen_set_evtchn_fast()` is called from contexts that can be atomic, such as `xen_timer_callback()` (an hrtimer callback) or `kvm_arch_set_irq_inatomic()` (during irqfd waitqueue wakeups).
Here's the relevant lockdep/BUG splat:
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:
<IRQ>
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
</IRQ>
To resolve this, I've modified `kvm_xen_set_evtchn_fast()` to accept a `bool in_atomic` parameter, indicating whether the call is from an atomic context. When `in_atomic` is true, I'm now using `read_trylock()` instead of `read_lock_irqsave()`. If `read_trylock()` fails (meaning the lock is contended), the code safely falls back to the slow path, which involves returning `-EWOULDBLOCK` or kicking the vCPU – behaviors that are already handled by the callers. I've updated all the calling functions to correctly pass the appropriate context flag.
|