AI Trajectory


Trajectory: (Expand/Collapse All)
Seq Timestamp Type Name Duration
0/0 2026/09/01 04:38 flow patch-triage
1m Args:
null

Results:
{
  "EnableConfigs": [
    "KVM",
    "KVM_INTEL",
    "KVM_AMD",
    "KVM_X86",
    "KVM_XEN"
  ],
  "FocusSymbols": [
    "kvm_xen_vcpu_set_attr",
    "kvm_xen_hvm_set_attr",
    "kvm_xen_hypercall",
    "kvm_xen_inject_pending_events",
    "__kvm_xen_has_interrupt",
    "kvm_xen_set_evtchn_fast",
    "kvm_xen_hvm_evtchn_send",
    "kvm_gpc_refresh",
    "kvm_gpc_activate"
  ],
  "KMSANReasoning": "The patch modifies KVM's Xen emulation and GFN-to-PFN cache invalidation logic. Specifically, it:\n1. Replaces the IDR data structure with XArray for event channel port tracking (`evtchn_ports`).\n2. Introduces a dedicated sequence counter (`gpc_invalidate_seq`) in `pfncache.c` and `kvm_main.c` to properly track GPC invalidation events.\n3. Fixes potential split-lock `#AC` exceptions by checking GPA/HVA 32-bit alignment in `kvm_xen_vcpu_set_attr` and using 32-bit atomic bitops for `vi_pending_sel`.\n4. Fixes SRCU locking in `__kvm_xen_has_interrupt` and atomic reads/writes for `poll_evtchn`.\n\nNone of these changes allocate uninitialized buffers, read uninitialized stack/heap memory, or expose kernel info-leaks to userspace. Memory safety and concurrency concerns related to these changes (such as locking, use-after-free, or out-of-bounds accesses) are covered by KASAN and LOCKDEP. Therefore, dedicated KMSAN fuzzing is not needed.",
  "NeedsKMSAN": false,
  "Reasoning": "The patch makes functional and concurrency-critical changes to KVM's Xen emulation and gfn_to_pfn_cache (GPC) subsystem. These include migrating Xen event channel storage from IDR to XArray, enforcing alignment checks for vCPU info GPA/HVA, using explicit 32-bit atomic bitops to prevent split-lock AC exceptions on unaligned structures, introducing SRCU locking in __kvm_xen_has_interrupt, and separating GPC invalidation sequencing from general MMU invalidation sequencing. These changes affect reachable virtualization ioctls, hypercalls, and event channel delivery mechanisms.",
  "WorthFuzzing": true
}

1/1 2026/09/01 04:38 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit c3e03ff3b8c379d8b32820c99ebc02d802e303b2\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Tue Sep 1 04:38:22 2026 +0000\n\n    syz-cluster: applied patch under review\n\ndiff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h\nindex 683bb8bf43a94..3af7395c2430a 100644\n--- a/arch/x86/include/asm/kvm_host.h\n+++ b/arch/x86/include/asm/kvm_host.h\n@@ -16,6 +16,7 @@\n #include \u003clinux/irq_work.h\u003e\n #include \u003clinux/irq.h\u003e\n #include \u003clinux/workqueue.h\u003e\n+#include \u003clinux/xarray.h\u003e\n \n #include \u003clinux/kvm.h\u003e\n #include \u003clinux/kvm_para.h\u003e\n@@ -1113,7 +1114,7 @@ struct kvm_xen {\n \tbool runstate_update_flag;\n \tu8 upcall_vector;\n \tstruct gfn_to_pfn_cache shinfo_cache;\n-\tstruct idr evtchn_ports;\n+\tstruct xarray evtchn_ports;\n \tunsigned long poll_mask[BITS_TO_LONGS(KVM_MAX_VCPUS)];\n \n \tstruct kvm_xen_hvm_config hvm_config;\ndiff --git a/arch/x86/kvm/xen.c b/arch/x86/kvm/xen.c\nindex 998cdcb6aa303..718396340d3c1 100644\n--- a/arch/x86/kvm/xen.c\n+++ b/arch/x86/kvm/xen.c\n@@ -73,7 +73,7 @@ static int kvm_xen_shared_info_init(struct kvm *kvm)\n \tBUILD_BUG_ON(offsetof(struct shared_info, wc) != 0xc00);\n \tBUILD_BUG_ON(offsetof(struct shared_info, wc_sec_hi) != 0xc0c);\n \n-\tif (IS_ENABLED(CONFIG_64BIT) \u0026\u0026 kvm-\u003earch.xen.long_mode) {\n+\tif (kvm_xen_has_64bit_shinfo(kvm)) {\n \t\tstruct shared_info *shinfo = gpc-\u003ekhva;\n \n \t\twc_sec_hi = \u0026shinfo-\u003ewc_sec_hi;\n@@ -389,7 +389,7 @@ static void kvm_xen_update_runstate_guest(struct kvm_vcpu *v, bool atomic)\n \tBUILD_BUG_ON(sizeof_field(struct vcpu_runstate_info, time) !=\n \t\t     sizeof(vx-\u003erunstate_times));\n \n-\tif (IS_ENABLED(CONFIG_64BIT) \u0026\u0026 v-\u003ekvm-\u003earch.xen.long_mode) {\n+\tif (kvm_xen_has_64bit_shinfo(v-\u003ekvm)) {\n \t\tuser_len = sizeof(struct vcpu_runstate_info);\n \t\ttimes_ofs = offsetof(struct vcpu_runstate_info,\n \t\t\t\t     state_entry_time);\n@@ -676,28 +676,32 @@ void kvm_xen_inject_pending_events(struct kvm_vcpu *v)\n \t}\n \n \t/* Now gpc-\u003ekhva is a valid kernel address for the vcpu_info */\n-\tif (IS_ENABLED(CONFIG_64BIT) \u0026\u0026 v-\u003ekvm-\u003earch.xen.long_mode) {\n+\tif (kvm_xen_has_64bit_shinfo(v-\u003ekvm)) {\n \t\tstruct vcpu_info *vi = gpc-\u003ekhva;\n+\t\tvoid *vi_pending_sel = \u0026vi-\u003eevtchn_pending_sel;\n \n-\t\tasm volatile(LOCK_PREFIX \"orq %0, %1\\n\"\n-\t\t\t     \"notq %0\\n\"\n-\t\t\t     LOCK_PREFIX \"andq %0, %2\\n\"\n-\t\t\t     : \"=r\" (evtchn_pending_sel),\n-\t\t\t       \"+m\" (vi-\u003eevtchn_pending_sel),\n-\t\t\t       \"+m\" (v-\u003earch.xen.evtchn_pending_sel)\n-\t\t\t     : \"0\" (evtchn_pending_sel));\n+\t\tif (IS_ALIGNED((unsigned long)vi_pending_sel, sizeof(u64))) {\n+\t\t\tatomic64_or(evtchn_pending_sel, vi_pending_sel);\n+\t\t} else {\n+\t\t\tatomic_or(evtchn_pending_sel, vi_pending_sel);\n+\t\t\t/*\n+\t\t\t * The cast keeps the shift well-defined on 32-bit,\n+\t\t\t * where evtchn_pending_sel is 32 bits wide and this\n+\t\t\t * branch is unreachable anyway (this is inside\n+\t\t\t * kvm_xen_has_64bit_shinfo(), which is gated on\n+\t\t\t * IS_ENABLED(CONFIG_64BIT)).\n+\t\t\t */\n+\t\t\tatomic_or((u64)evtchn_pending_sel \u003e\u003e 32,\n+\t\t\t\t  vi_pending_sel + 4);\n+\t\t}\n+\n+\t\tatomic64_andnot(evtchn_pending_sel, (void *)\u0026v-\u003earch.xen.evtchn_pending_sel);\n \t\tWRITE_ONCE(vi-\u003eevtchn_upcall_pending, 1);\n \t} else {\n-\t\tu32 evtchn_pending_sel32 = evtchn_pending_sel;\n \t\tstruct compat_vcpu_info *vi = gpc-\u003ekhva;\n \n-\t\tasm volatile(LOCK_PREFIX \"orl %0, %1\\n\"\n-\t\t\t     \"notl %0\\n\"\n-\t\t\t     LOCK_PREFIX \"andl %0, %2\\n\"\n-\t\t\t     : \"=r\" (evtchn_pending_sel32),\n-\t\t\t       \"+m\" (vi-\u003eevtchn_pending_sel),\n-\t\t\t       \"+m\" (v-\u003earch.xen.evtchn_pending_sel)\n-\t\t\t     : \"0\" (evtchn_pending_sel32));\n+\t\tatomic_or(evtchn_pending_sel, (void *)\u0026vi-\u003eevtchn_pending_sel);\n+\t\tatomic_andnot(evtchn_pending_sel, (void *)\u0026v-\u003earch.xen.evtchn_pending_sel);\n \t\tWRITE_ONCE(vi-\u003eevtchn_upcall_pending, 1);\n \t}\n \n@@ -728,6 +732,16 @@ int __kvm_xen_has_interrupt(struct kvm_vcpu *v)\n \tBUILD_BUG_ON(sizeof(rc) !=\n \t\t     sizeof_field(struct compat_vcpu_info, evtchn_upcall_pending));\n \n+\t/*\n+\t * kvm_gpc_check() checks the memslot generation, so kvm-\u003esrcu must be\n+\t * held. Most callers hold it already, but this is also reached from\n+\t * kvm_emulate_halt() on the VM-Exit path and from kvm_vcpu_block(),\n+\t * where vcpu_enter_guest() has already dropped the vCPU's SRCU lock.\n+\t * Taking SRCU does not sleep, so it is safe even in the atomic case\n+\t * which is handled below.\n+\t */\n+\tguard(srcu)(\u0026v-\u003ekvm-\u003esrcu);\n+\n \tread_lock_irqsave(\u0026gpc-\u003elock, flags);\n \twhile (!kvm_gpc_check(gpc, sizeof(struct vcpu_info))) {\n \t\tread_unlock_irqrestore(\u0026gpc-\u003elock, flags);\n@@ -941,6 +955,10 @@ int kvm_xen_vcpu_set_attr(struct kvm_vcpu *vcpu, struct kvm_xen_vcpu_attr *data)\n \t\t\t\tbreak;\n \t\t\t}\n \n+\t\t\tr = -ENXIO;\n+\t\t\tif (!IS_ALIGNED(data-\u003eu.gpa, sizeof(u32)))\n+\t\t\t\tbreak;\n+\n \t\t\tr = kvm_gpc_activate(\u0026vcpu-\u003earch.xen.vcpu_info_cache,\n \t\t\t\t\t     data-\u003eu.gpa, sizeof(struct vcpu_info));\n \t\t} else {\n@@ -950,6 +968,10 @@ int kvm_xen_vcpu_set_attr(struct kvm_vcpu *vcpu, struct kvm_xen_vcpu_attr *data)\n \t\t\t\tbreak;\n \t\t\t}\n \n+\t\t\tr = -ENXIO;\n+\t\t\tif (!IS_ALIGNED(data-\u003eu.hva, sizeof(u32)))\n+\t\t\t\tbreak;\n+\n \t\t\tr = kvm_gpc_activate_hva(\u0026vcpu-\u003earch.xen.vcpu_info_cache,\n \t\t\t\t\t\t data-\u003eu.hva, sizeof(struct vcpu_info));\n \t\t}\n@@ -993,7 +1015,7 @@ int kvm_xen_vcpu_set_attr(struct kvm_vcpu *vcpu, struct kvm_xen_vcpu_attr *data)\n \t\t * address, that's actually OK. kvm_xen_update_runstate_guest()\n \t\t * will cope.\n \t\t */\n-\t\tif (IS_ENABLED(CONFIG_64BIT) \u0026\u0026 vcpu-\u003ekvm-\u003earch.xen.long_mode)\n+\t\tif (kvm_xen_has_64bit_shinfo(vcpu-\u003ekvm))\n \t\t\tsz = sizeof(struct vcpu_runstate_info);\n \t\telse\n \t\t\tsz = sizeof(struct compat_vcpu_runstate_info);\n@@ -1439,16 +1461,21 @@ static int kvm_xen_hypercall_complete_userspace(struct kvm_vcpu *vcpu)\n \treturn kvm_xen_hypercall_set_result(vcpu, run-\u003exen.u.hcall.result);\n }\n \n-static inline int max_evtchn_port(struct kvm *kvm)\n+static inline int max_evtchn_port(bool has_64bit_shinfo)\n {\n-\tif (IS_ENABLED(CONFIG_64BIT) \u0026\u0026 kvm-\u003earch.xen.long_mode)\n+\tif (has_64bit_shinfo)\n \t\treturn EVTCHN_2L_NR_CHANNELS;\n \telse\n \t\treturn COMPAT_EVTCHN_2L_NR_CHANNELS;\n }\n \n-static bool wait_pending_event(struct kvm_vcpu *vcpu, int nr_ports,\n-\t\t\t       evtchn_port_t *ports)\n+static inline int kvm_max_evtchn_port(struct kvm *kvm)\n+{\n+\treturn max_evtchn_port(kvm_xen_has_64bit_shinfo(kvm));\n+}\n+\n+static bool wait_pending_event(struct kvm_vcpu *vcpu, bool has_64bit_shinfo,\n+\t\t\t       int nr_ports, evtchn_port_t *ports)\n {\n \tstruct kvm *kvm = vcpu-\u003ekvm;\n \tstruct gfn_to_pfn_cache *gpc = \u0026kvm-\u003earch.xen.shinfo_cache;\n@@ -1463,7 +1490,7 @@ static bool wait_pending_event(struct kvm_vcpu *vcpu, int nr_ports,\n \t\tgoto out_rcu;\n \n \tret = false;\n-\tif (IS_ENABLED(CONFIG_64BIT) \u0026\u0026 kvm-\u003earch.xen.long_mode) {\n+\tif (has_64bit_shinfo) {\n \t\tstruct shared_info *shinfo = gpc-\u003ekhva;\n \t\tpending_bits = (unsigned long *)\u0026shinfo-\u003eevtchn_pending;\n \t} else {\n@@ -1485,9 +1512,10 @@ static bool wait_pending_event(struct kvm_vcpu *vcpu, int nr_ports,\n \treturn ret;\n }\n \n-static bool kvm_xen_schedop_poll(struct kvm_vcpu *vcpu, bool longmode,\n+static bool kvm_xen_schedop_poll(struct kvm_vcpu *vcpu, bool is_64bit,\n \t\t\t\t u64 param, u64 *r)\n {\n+\tbool has_64bit_shinfo = kvm_xen_has_64bit_shinfo(vcpu-\u003ekvm);\n \tstruct sched_poll sched_poll;\n \tevtchn_port_t port, *ports;\n \tstruct x86_exception e;\n@@ -1497,7 +1525,7 @@ static bool kvm_xen_schedop_poll(struct kvm_vcpu *vcpu, bool longmode,\n \t    !(vcpu-\u003ekvm-\u003earch.xen.hvm_config.flags \u0026 KVM_XEN_HVM_CONFIG_EVTCHN_SEND))\n \t\treturn false;\n \n-\tif (IS_ENABLED(CONFIG_64BIT) \u0026\u0026 !longmode) {\n+\tif (IS_ENABLED(CONFIG_64BIT) \u0026\u0026 !is_64bit) {\n \t\tstruct compat_sched_poll sp32;\n \n \t\t/* Sanity check that the compat struct definition is correct */\n@@ -1546,20 +1574,20 @@ static bool kvm_xen_schedop_poll(struct kvm_vcpu *vcpu, bool longmode,\n \t}\n \n \tfor (i = 0; i \u003c sched_poll.nr_ports; i++) {\n-\t\tif (ports[i] \u003e= max_evtchn_port(vcpu-\u003ekvm)) {\n+\t\tif (ports[i] \u003e= max_evtchn_port(has_64bit_shinfo)) {\n \t\t\t*r = -EINVAL;\n \t\t\tgoto out;\n \t\t}\n \t}\n \n \tif (sched_poll.nr_ports == 1)\n-\t\tvcpu-\u003earch.xen.poll_evtchn = port;\n+\t\tWRITE_ONCE(vcpu-\u003earch.xen.poll_evtchn, port);\n \telse\n-\t\tvcpu-\u003earch.xen.poll_evtchn = -1;\n+\t\tWRITE_ONCE(vcpu-\u003earch.xen.poll_evtchn, -1);\n \n \tset_bit(vcpu-\u003evcpu_idx, vcpu-\u003ekvm-\u003earch.xen.poll_mask);\n \n-\tif (!wait_pending_event(vcpu, sched_poll.nr_ports, ports)) {\n+\tif (!wait_pending_event(vcpu, has_64bit_shinfo, sched_poll.nr_ports, ports)) {\n \t\tkvm_set_mp_state(vcpu, KVM_MP_STATE_HALTED);\n \n \t\tif (sched_poll.timeout)\n@@ -1574,7 +1602,7 @@ static bool kvm_xen_schedop_poll(struct kvm_vcpu *vcpu, bool longmode,\n \t\tkvm_set_mp_state(vcpu, KVM_MP_STATE_RUNNABLE);\n \t}\n \n-\tvcpu-\u003earch.xen.poll_evtchn = 0;\n+\tWRITE_ONCE(vcpu-\u003earch.xen.poll_evtchn, 0);\n \t*r = 0;\n out:\n \t/* Really, this is only needed in case of timeout */\n@@ -1594,12 +1622,12 @@ static void cancel_evtchn_poll(struct timer_list *t)\n \tkvm_vcpu_kick(vcpu);\n }\n \n-static bool kvm_xen_hcall_sched_op(struct kvm_vcpu *vcpu, bool longmode,\n+static bool kvm_xen_hcall_sched_op(struct kvm_vcpu *vcpu, bool is_64bit,\n \t\t\t\t   int cmd, u64 param, u64 *r)\n {\n \tswitch (cmd) {\n \tcase SCHEDOP_poll:\n-\t\tif (kvm_xen_schedop_poll(vcpu, longmode, param, r))\n+\t\tif (kvm_xen_schedop_poll(vcpu, is_64bit, param, r))\n \t\t\treturn true;\n \t\tfallthrough;\n \tcase SCHEDOP_yield:\n@@ -1618,7 +1646,7 @@ struct compat_vcpu_set_singleshot_timer {\n     uint32_t flags;\n } __attribute__((packed));\n \n-static bool kvm_xen_hcall_vcpu_op(struct kvm_vcpu *vcpu, bool longmode, int cmd,\n+static bool kvm_xen_hcall_vcpu_op(struct kvm_vcpu *vcpu, bool is_64bit, int cmd,\n \t\t\t\t  int vcpu_id, u64 param, u64 *r)\n {\n \tstruct vcpu_set_singleshot_timer oneshot;\n@@ -1662,7 +1690,7 @@ static bool kvm_xen_hcall_vcpu_op(struct kvm_vcpu *vcpu, bool longmode, int cmd,\n \t\tBUILD_BUG_ON(sizeof_field(struct compat_vcpu_set_singleshot_timer, flags) !=\n \t\t\t     sizeof_field(struct vcpu_set_singleshot_timer, flags));\n \n-\t\tif (kvm_read_guest_virt(vcpu, param, \u0026oneshot, longmode ? sizeof(oneshot) :\n+\t\tif (kvm_read_guest_virt(vcpu, param, \u0026oneshot, is_64bit ? sizeof(oneshot) :\n \t\t\t\t\tsizeof(struct compat_vcpu_set_singleshot_timer), \u0026e)) {\n \t\t\t*r = -EFAULT;\n \t\t\treturn true;\n@@ -1694,7 +1722,7 @@ static bool kvm_xen_hcall_set_timer_op(struct kvm_vcpu *vcpu, uint64_t timeout,\n \n int kvm_xen_hypercall(struct kvm_vcpu *vcpu)\n {\n-\tbool longmode;\n+\tbool is_64bit;\n \tu64 input, params[6], r = -ENOSYS;\n \tbool handled = false;\n \tu8 cpl;\n@@ -1704,8 +1732,8 @@ int kvm_xen_hypercall(struct kvm_vcpu *vcpu)\n \t    kvm_hv_hypercall_enabled(vcpu))\n \t\treturn kvm_hv_hypercall(vcpu);\n \n-\tlongmode = is_64_bit_hypercall(vcpu);\n-\tif (!longmode) {\n+\tis_64bit = is_64_bit_hypercall(vcpu);\n+\tif (!is_64bit) {\n \t\tinput = kvm_eax_read(vcpu);\n \t\tparams[0] = kvm_ebx_read(vcpu);\n \t\tparams[1] = kvm_ecx_read(vcpu);\n@@ -1751,17 +1779,17 @@ int kvm_xen_hypercall(struct kvm_vcpu *vcpu)\n \t\t\thandled = kvm_xen_hcall_evtchn_send(vcpu, params[1], \u0026r);\n \t\tbreak;\n \tcase __HYPERVISOR_sched_op:\n-\t\thandled = kvm_xen_hcall_sched_op(vcpu, longmode, params[0],\n+\t\thandled = kvm_xen_hcall_sched_op(vcpu, is_64bit, params[0],\n \t\t\t\t\t\t params[1], \u0026r);\n \t\tbreak;\n \tcase __HYPERVISOR_vcpu_op:\n-\t\thandled = kvm_xen_hcall_vcpu_op(vcpu, longmode, params[0], params[1],\n+\t\thandled = kvm_xen_hcall_vcpu_op(vcpu, is_64bit, params[0], params[1],\n \t\t\t\t\t\tparams[2], \u0026r);\n \t\tbreak;\n \tcase __HYPERVISOR_set_timer_op: {\n \t\tu64 timeout = params[0];\n \t\t/* In 32-bit mode, the 64-bit timeout is in two 32-bit params. */\n-\t\tif (!longmode)\n+\t\tif (!is_64bit)\n \t\t\ttimeout |= params[1] \u003c\u003c 32;\n \t\thandled = kvm_xen_hcall_set_timer_op(vcpu, timeout, \u0026r);\n \t\tbreak;\n@@ -1776,7 +1804,7 @@ int kvm_xen_hypercall(struct kvm_vcpu *vcpu)\n handle_in_userspace:\n \tvcpu-\u003erun-\u003eexit_reason = KVM_EXIT_XEN;\n \tvcpu-\u003erun-\u003exen.type = KVM_EXIT_XEN_HCALL;\n-\tvcpu-\u003erun-\u003exen.u.hcall.longmode = longmode;\n+\tvcpu-\u003erun-\u003exen.u.hcall.longmode = is_64bit;\n \tvcpu-\u003erun-\u003exen.u.hcall.cpl = cpl;\n \tvcpu-\u003erun-\u003exen.u.hcall.input = input;\n \tvcpu-\u003erun-\u003exen.u.hcall.params[0] = params[0];\n@@ -1794,7 +1822,7 @@ int kvm_xen_hypercall(struct kvm_vcpu *vcpu)\n \n static void kvm_xen_check_poller(struct kvm_vcpu *vcpu, int port)\n {\n-\tint poll_evtchn = vcpu-\u003earch.xen.poll_evtchn;\n+\tint poll_evtchn = READ_ONCE(vcpu-\u003earch.xen.poll_evtchn);\n \n \tif ((poll_evtchn == port || poll_evtchn == -1) \u0026\u0026\n \t    test_and_clear_bit(vcpu-\u003evcpu_idx, vcpu-\u003ekvm-\u003earch.xen.poll_mask)) {\n@@ -1816,8 +1844,9 @@ static void kvm_xen_check_poller(struct kvm_vcpu *vcpu, int port)\n int kvm_xen_set_evtchn_fast(struct kvm_xen_evtchn *xe, struct kvm *kvm)\n {\n \tstruct gfn_to_pfn_cache *gpc = \u0026kvm-\u003earch.xen.shinfo_cache;\n+\tbool has_64bit_shinfo = kvm_xen_has_64bit_shinfo(kvm);\n+\tunsigned long *pending_bits, *mask_bits, vi_pending_sel_ofs;\n \tstruct kvm_vcpu *vcpu;\n-\tunsigned long *pending_bits, *mask_bits;\n \tunsigned long flags;\n \tint port_word_bit;\n \tbool kick_vcpu = false;\n@@ -1833,7 +1862,7 @@ int kvm_xen_set_evtchn_fast(struct kvm_xen_evtchn *xe, struct kvm *kvm)\n \t\tWRITE_ONCE(xe-\u003evcpu_idx, vcpu-\u003evcpu_idx);\n \t}\n \n-\tif (xe-\u003eport \u003e= max_evtchn_port(kvm))\n+\tif (xe-\u003eport \u003e= max_evtchn_port(has_64bit_shinfo))\n \t\treturn -EINVAL;\n \n \trc = -EWOULDBLOCK;\n@@ -1844,16 +1873,23 @@ int kvm_xen_set_evtchn_fast(struct kvm_xen_evtchn *xe, struct kvm *kvm)\n \tif (!kvm_gpc_check(gpc, PAGE_SIZE))\n \t\tgoto out_rcu;\n \n-\tif (IS_ENABLED(CONFIG_64BIT) \u0026\u0026 kvm-\u003earch.xen.long_mode) {\n+\tif (has_64bit_shinfo) {\n \t\tstruct shared_info *shinfo = gpc-\u003ekhva;\n \t\tpending_bits = (unsigned long *)\u0026shinfo-\u003eevtchn_pending;\n \t\tmask_bits = (unsigned long *)\u0026shinfo-\u003eevtchn_mask;\n \t\tport_word_bit = xe-\u003eport / 64;\n+\n+\t\tvi_pending_sel_ofs = offsetof(struct vcpu_info, evtchn_pending_sel);\n \t} else {\n \t\tstruct compat_shared_info *shinfo = gpc-\u003ekhva;\n \t\tpending_bits = (unsigned long *)\u0026shinfo-\u003eevtchn_pending;\n \t\tmask_bits = (unsigned long *)\u0026shinfo-\u003eevtchn_mask;\n \t\tport_word_bit = xe-\u003eport / 32;\n+\n+\t\tvi_pending_sel_ofs = offsetof(struct compat_vcpu_info, evtchn_pending_sel);\n+\n+\t\t/* test_and_set_bit() needs 64-bit alignment, but that's OK */\n+\t\tBUILD_BUG_ON(offsetof(struct compat_shared_info, evtchn_pending) \u0026 7);\n \t}\n \n \t/*\n@@ -1869,6 +1905,8 @@ int kvm_xen_set_evtchn_fast(struct kvm_xen_evtchn *xe, struct kvm *kvm)\n \t\trc = -ENOTCONN; /* Masked */\n \t\tkvm_xen_check_poller(vcpu, xe-\u003eport);\n \t} else {\n+\t\tbool old;\n+\n \t\trc = 1; /* Delivered to the bitmap in shared_info. */\n \t\t/* Now switch to the vCPU's vcpu_info to set the index and pending_sel */\n \t\tread_unlock_irqrestore(\u0026gpc-\u003elock, flags);\n@@ -1885,19 +1923,29 @@ int kvm_xen_set_evtchn_fast(struct kvm_xen_evtchn *xe, struct kvm *kvm)\n \t\t\tgoto out_rcu;\n \t\t}\n \n-\t\tif (IS_ENABLED(CONFIG_64BIT) \u0026\u0026 kvm-\u003earch.xen.long_mode) {\n-\t\t\tstruct vcpu_info *vcpu_info = gpc-\u003ekhva;\n-\t\t\tif (!test_and_set_bit(port_word_bit, \u0026vcpu_info-\u003eevtchn_pending_sel)) {\n-\t\t\t\tWRITE_ONCE(vcpu_info-\u003eevtchn_upcall_pending, 1);\n-\t\t\t\tkick_vcpu = true;\n-\t\t\t}\n-\t\t} else {\n-\t\t\tstruct compat_vcpu_info *vcpu_info = gpc-\u003ekhva;\n-\t\t\tif (!test_and_set_bit(port_word_bit,\n-\t\t\t\t\t      (unsigned long *)\u0026vcpu_info-\u003eevtchn_pending_sel)) {\n-\t\t\t\tWRITE_ONCE(vcpu_info-\u003eevtchn_upcall_pending, 1);\n-\t\t\t\tkick_vcpu = true;\n-\t\t\t}\n+\t\t/*\n+\t\t * Explicitly use a 32-bit btsl instead of test_and_set_bit(),\n+\t\t * which would use btsq on x86-64. The vcpu_info is guest-\n+\t\t * controlled and only required to be 32-bit aligned, so a\n+\t\t * 64-bit access could generate a split-lock #AC.\n+\t\t *\n+\t\t * Note, this does not apply to the test_and_set_bit() on\n+\t\t * pending_bits above: that is in the per-VM shared_info, which\n+\t\t * is page aligned, so the access is guaranteed to be 64-bit\n+\t\t * aligned.\n+\t\t */\n+\t\told = GEN_BINARY_RMWcc(LOCK_PREFIX \"btsl\",\n+\t\t\t\t       *(u32 *)(gpc-\u003ekhva + vi_pending_sel_ofs),\n+\t\t\t\t       c, \"Ir\", port_word_bit);\n+\t\tif (!old) {\n+\t\t\tstruct vcpu_info *vi = gpc-\u003ekhva;\n+\n+\t\t\t/* No need for compat handling */\n+\t\t\tBUILD_BUG_ON(offsetof(struct vcpu_info, evtchn_upcall_pending) !=\n+\t\t\t\t     offsetof(struct compat_vcpu_info, evtchn_upcall_pending));\n+\n+\t\t\tWRITE_ONCE(vi-\u003eevtchn_upcall_pending, 1);\n+\t\t\tkick_vcpu = true;\n \t\t}\n \n \t\t/* For the per-vCPU lapic vector, deliver it as MSI. */\n@@ -1995,7 +2043,7 @@ int kvm_xen_setup_evtchn(struct kvm *kvm,\n \tstruct kvm_vcpu *vcpu;\n \n \t/*\n-\t * Don't check for the port being within range of max_evtchn_port().\n+\t * Don't check for the port being within range of kvm_max_evtchn_port().\n \t * Userspace can configure what ever targets it likes; events just won't\n \t * be delivered if/while the target is invalid, just like userspace can\n \t * configure MSIs which target non-existent APICs.\n@@ -2004,8 +2052,8 @@ int kvm_xen_setup_evtchn(struct kvm *kvm,\n \t * can be restored *independently* of other things like creating vCPUs,\n \t * without imposing an ordering dependency on userspace.  In this\n \t * particular case, the problematic ordering would be with setting the\n-\t * Xen 'long mode' flag, which changes max_evtchn_port() to allow 4096\n-\t * instead of 1024 event channels.\n+\t * Xen 'long mode' flag, which changes kvm_max_evtchn_port() to allow\n+\t * 4096 instead of 1024 event channels.\n \t */\n \n \t/* We only support 2 level event channels for now */\n@@ -2042,7 +2090,7 @@ int kvm_xen_hvm_evtchn_send(struct kvm *kvm, struct kvm_irq_routing_xen_evtchn *\n \tstruct kvm_xen_evtchn e;\n \tint ret;\n \n-\tif (!uxe-\u003eport || uxe-\u003eport \u003e= max_evtchn_port(kvm))\n+\tif (!uxe-\u003eport || uxe-\u003eport \u003e= kvm_max_evtchn_port(kvm))\n \t\treturn -EINVAL;\n \n \t/* We only support 2 level event channels for now */\n@@ -2093,7 +2141,7 @@ static int kvm_xen_eventfd_update(struct kvm *kvm,\n \n \t/* Protect writes to evtchnfd as well as the idr lookup.  */\n \tmutex_lock(\u0026kvm-\u003earch.xen.xen_lock);\n-\tevtchnfd = idr_find(\u0026kvm-\u003earch.xen.evtchn_ports, port);\n+\tevtchnfd = xa_load(\u0026kvm-\u003earch.xen.evtchn_ports, port);\n \n \tret = -ENOENT;\n \tif (!evtchnfd)\n@@ -2152,7 +2200,7 @@ static int kvm_xen_eventfd_assign(struct kvm *kvm,\n \n \tcase EVTCHNSTAT_interdomain:\n \t\tif (data-\u003eu.evtchn.deliver.port.port) {\n-\t\t\tif (data-\u003eu.evtchn.deliver.port.port \u003e= max_evtchn_port(kvm))\n+\t\t\tif (data-\u003eu.evtchn.deliver.port.port \u003e= kvm_max_evtchn_port(kvm))\n \t\t\t\tgoto out_noeventfd; /* -EINVAL */\n \t\t} else {\n \t\t\teventfd = eventfd_ctx_fdget(data-\u003eu.evtchn.deliver.eventfd.fd);\n@@ -2187,13 +2235,13 @@ static int kvm_xen_eventfd_assign(struct kvm *kvm,\n \t}\n \n \tmutex_lock(\u0026kvm-\u003earch.xen.xen_lock);\n-\tret = idr_alloc(\u0026kvm-\u003earch.xen.evtchn_ports, evtchnfd, port, port + 1,\n+\tret = xa_insert(\u0026kvm-\u003earch.xen.evtchn_ports, port, evtchnfd,\n \t\t\tGFP_KERNEL);\n \tmutex_unlock(\u0026kvm-\u003earch.xen.xen_lock);\n-\tif (ret \u003e= 0)\n+\tif (!ret)\n \t\treturn 0;\n \n-\tif (ret == -ENOSPC)\n+\tif (ret == -EBUSY)\n \t\tret = -EEXIST;\n out:\n \tif (eventfd)\n@@ -2208,7 +2256,7 @@ static int kvm_xen_eventfd_deassign(struct kvm *kvm, u32 port)\n \tstruct evtchnfd *evtchnfd;\n \n \tmutex_lock(\u0026kvm-\u003earch.xen.xen_lock);\n-\tevtchnfd = idr_remove(\u0026kvm-\u003earch.xen.evtchn_ports, port);\n+\tevtchnfd = xa_erase(\u0026kvm-\u003earch.xen.evtchn_ports, port);\n \tmutex_unlock(\u0026kvm-\u003earch.xen.xen_lock);\n \n \tif (!evtchnfd)\n@@ -2224,7 +2272,7 @@ static int kvm_xen_eventfd_deassign(struct kvm *kvm, u32 port)\n static int kvm_xen_eventfd_reset(struct kvm *kvm)\n {\n \tstruct evtchnfd *evtchnfd, **all_evtchnfds;\n-\tint i;\n+\tunsigned long i;\n \tint n = 0;\n \n \tmutex_lock(\u0026kvm-\u003earch.xen.xen_lock);\n@@ -2234,7 +2282,7 @@ static int kvm_xen_eventfd_reset(struct kvm *kvm)\n \t * critical section, first collect all the evtchnfd objects\n \t * in an array as they are removed from evtchn_ports.\n \t */\n-\tidr_for_each_entry(\u0026kvm-\u003earch.xen.evtchn_ports, evtchnfd, i)\n+\txa_for_each(\u0026kvm-\u003earch.xen.evtchn_ports, i, evtchnfd)\n \t\tn++;\n \n \tall_evtchnfds = kmalloc_objs(struct evtchnfd *, n);\n@@ -2244,9 +2292,9 @@ static int kvm_xen_eventfd_reset(struct kvm *kvm)\n \t}\n \n \tn = 0;\n-\tidr_for_each_entry(\u0026kvm-\u003earch.xen.evtchn_ports, evtchnfd, i) {\n+\txa_for_each(\u0026kvm-\u003earch.xen.evtchn_ports, i, evtchnfd) {\n \t\tall_evtchnfds[n++] = evtchnfd;\n-\t\tidr_remove(\u0026kvm-\u003earch.xen.evtchn_ports, evtchnfd-\u003esend_port);\n+\t\txa_erase(\u0026kvm-\u003earch.xen.evtchn_ports, evtchnfd-\u003esend_port);\n \t}\n \tmutex_unlock(\u0026kvm-\u003earch.xen.xen_lock);\n \n@@ -2270,7 +2318,7 @@ static int kvm_xen_setattr_evtchn(struct kvm *kvm, struct kvm_xen_hvm_attr *data\n \tif (data-\u003eu.evtchn.flags == KVM_XEN_EVTCHN_RESET)\n \t\treturn kvm_xen_eventfd_reset(kvm);\n \n-\tif (!port || port \u003e= max_evtchn_port(kvm))\n+\tif (!port || port \u003e= kvm_max_evtchn_port(kvm))\n \t\treturn -EINVAL;\n \n \tif (data-\u003eu.evtchn.flags == KVM_XEN_EVTCHN_DEASSIGN)\n@@ -2297,12 +2345,10 @@ static bool kvm_xen_hcall_evtchn_send(struct kvm_vcpu *vcpu, u64 param, u64 *r)\n \t}\n \n \t/*\n-\t * evtchnfd is protected by kvm-\u003esrcu; the idr lookup instead\n-\t * is protected by RCU.\n+\t * evtchnfd is protected by kvm-\u003esrcu; the xa_load is RCU-safe\n+\t * internally, no explicit rcu_read_lock() needed.\n \t */\n-\trcu_read_lock();\n-\tevtchnfd = idr_find(\u0026vcpu-\u003ekvm-\u003earch.xen.evtchn_ports, send.port);\n-\trcu_read_unlock();\n+\tevtchnfd = xa_load(\u0026vcpu-\u003ekvm-\u003earch.xen.evtchn_ports, send.port);\n \tif (!evtchnfd)\n \t\treturn false;\n \n@@ -2349,23 +2395,23 @@ void kvm_xen_destroy_vcpu(struct kvm_vcpu *vcpu)\n void kvm_xen_init_vm(struct kvm *kvm)\n {\n \tmutex_init(\u0026kvm-\u003earch.xen.xen_lock);\n-\tidr_init(\u0026kvm-\u003earch.xen.evtchn_ports);\n+\txa_init(\u0026kvm-\u003earch.xen.evtchn_ports);\n \tkvm_gpc_init(\u0026kvm-\u003earch.xen.shinfo_cache, kvm);\n }\n \n void kvm_xen_destroy_vm(struct kvm *kvm)\n {\n \tstruct evtchnfd *evtchnfd;\n-\tint i;\n+\tunsigned long i;\n \n \tkvm_gpc_deactivate(\u0026kvm-\u003earch.xen.shinfo_cache);\n \n-\tidr_for_each_entry(\u0026kvm-\u003earch.xen.evtchn_ports, evtchnfd, i) {\n+\txa_for_each(\u0026kvm-\u003earch.xen.evtchn_ports, i, evtchnfd) {\n \t\tif (!evtchnfd-\u003edeliver.port.port)\n \t\t\teventfd_ctx_put(evtchnfd-\u003edeliver.eventfd.ctx);\n \t\tkfree(evtchnfd);\n \t}\n-\tidr_destroy(\u0026kvm-\u003earch.xen.evtchn_ports);\n+\txa_destroy(\u0026kvm-\u003earch.xen.evtchn_ports);\n \n \tif (kvm-\u003earch.xen.hvm_config.msr)\n \t\tstatic_branch_slow_dec_deferred(\u0026kvm_xen_enabled);\ndiff --git a/arch/x86/kvm/xen.h b/arch/x86/kvm/xen.h\nindex f372855857a88..9d04e350bdb1f 100644\n--- a/arch/x86/kvm/xen.h\n+++ b/arch/x86/kvm/xen.h\n@@ -235,6 +235,11 @@ struct compat_shared_info {\n #define COMPAT_EVTCHN_2L_NR_CHANNELS (8 *\t\t\t\t\\\n \t\t\t\t      sizeof_field(struct compat_shared_info, \\\n \t\t\t\t\t\t   evtchn_pending))\n+\n+/* Latched VM-wide mode; the KVM equivalent of Xen's !has_32bit_shinfo(). */\n+#define kvm_xen_has_64bit_shinfo(kvm) \\\n+\t(IS_ENABLED(CONFIG_64BIT) \u0026\u0026 READ_ONCE((kvm)-\u003earch.xen.long_mode))\n+\n struct compat_vcpu_runstate_info {\n     int state;\n     uint64_t state_entry_time;\ndiff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h\nindex 03bfc92864b6e..3dd04605f2e55 100644\n--- a/include/linux/kvm_host.h\n+++ b/include/linux/kvm_host.h\n@@ -855,6 +855,8 @@ struct kvm {\n \tgfn_t mmu_invalidate_range_start;\n \tgfn_t mmu_invalidate_range_end;\n \n+\tunsigned long gpc_invalidate_seq;\n+\n \tstruct list_head devices;\n \tu64 manual_dirty_log_protect;\n \tstruct dentry *debugfs_dentry;\ndiff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c\nindex 65eb26a0520d8..108d42c5c1d6a 100644\n--- a/virt/kvm/kvm_main.c\n+++ b/virt/kvm/kvm_main.c\n@@ -813,6 +813,16 @@ static void kvm_mmu_notifier_invalidate_range_end(struct mmu_notifier *mn,\n \n \t/* Pairs with the increment in range_start(). */\n \tspin_lock(\u0026kvm-\u003emn_invalidate_lock);\n+\tkvm-\u003egpc_invalidate_seq++;\n+\n+\t/*\n+\t * As with the MMU sequence counter and mmu_invalidate_in_progress, the\n+\t * GPC sequence increase must be visible before the invalidate count\n+\t * goes to zero.  Pairs with the smp_rmb() in\n+\t * mmu_notifier_retry_cache().\n+\t */\n+\tsmp_wmb();\n+\n \tif (!WARN_ON_ONCE(!kvm-\u003emn_active_invalidate_count))\n \t\t--kvm-\u003emn_active_invalidate_count;\n \twake = !kvm-\u003emn_active_invalidate_count;\ndiff --git a/virt/kvm/pfncache.c b/virt/kvm/pfncache.c\nindex 728d2c1b488a8..3659686b97c24 100644\n--- a/virt/kvm/pfncache.c\n+++ b/virt/kvm/pfncache.c\n@@ -124,7 +124,7 @@ static void gpc_unmap(kvm_pfn_t pfn, void *khva)\n #endif\n }\n \n-static inline bool mmu_notifier_retry_cache(struct kvm *kvm, unsigned long mmu_seq)\n+static inline bool mmu_notifier_retry_cache(struct kvm *kvm, unsigned long gpc_seq)\n {\n \t/*\n \t * mn_active_invalidate_count acts for all intents and purposes\n@@ -136,20 +136,20 @@ static inline bool mmu_notifier_retry_cache(struct kvm *kvm, unsigned long mmu_s\n \t * Note, it does not matter that mn_active_invalidate_count\n \t * is not protected by gpc-\u003elock.  It is guaranteed to\n \t * be elevated before the mmu_notifier acquires gpc-\u003elock, and\n-\t * isn't dropped until after mmu_invalidate_seq is updated.\n+\t * isn't dropped until after gpc_invalidate_seq is updated.\n \t */\n \tif (kvm-\u003emn_active_invalidate_count)\n \t\treturn true;\n \n \t/*\n \t * Ensure mn_active_invalidate_count is read before\n-\t * mmu_invalidate_seq.  This pairs with the smp_wmb() in\n-\t * mmu_notifier_invalidate_range_end() to guarantee either the\n+\t * gpc_invalidate_seq.  This pairs with the smp_wmb() in\n+\t * kvm_mmu_notifier_invalidate_range_end() to guarantee either the\n \t * old (non-zero) value of mn_active_invalidate_count or the\n-\t * new (incremented) value of mmu_invalidate_seq is observed.\n+\t * new (incremented) value of gpc_invalidate_seq is observed.\n \t */\n \tsmp_rmb();\n-\treturn kvm-\u003emmu_invalidate_seq != mmu_seq;\n+\treturn kvm-\u003egpc_invalidate_seq != gpc_seq;\n }\n \n static kvm_pfn_t hva_to_pfn_retry(struct gfn_to_pfn_cache *gpc)\n@@ -158,7 +158,7 @@ static kvm_pfn_t hva_to_pfn_retry(struct gfn_to_pfn_cache *gpc)\n \tvoid *old_khva = (void *)PAGE_ALIGN_DOWN((uintptr_t)gpc-\u003ekhva);\n \tkvm_pfn_t new_pfn = KVM_PFN_ERR_FAULT;\n \tvoid *new_khva = NULL;\n-\tunsigned long mmu_seq;\n+\tunsigned long gpc_seq;\n \tstruct page *page;\n \n \tstruct kvm_follow_pfn kfp = {\n@@ -181,7 +181,7 @@ static kvm_pfn_t hva_to_pfn_retry(struct gfn_to_pfn_cache *gpc)\n \tgpc-\u003evalid = false;\n \n \tdo {\n-\t\tmmu_seq = gpc-\u003ekvm-\u003emmu_invalidate_seq;\n+\t\tgpc_seq = gpc-\u003ekvm-\u003egpc_invalidate_seq;\n \t\tsmp_rmb();\n \n \t\twrite_unlock_irq(\u0026gpc-\u003elock);\n@@ -232,7 +232,7 @@ static kvm_pfn_t hva_to_pfn_retry(struct gfn_to_pfn_cache *gpc)\n \t\t * attempting to refresh.\n \t\t */\n \t\tWARN_ON_ONCE(gpc-\u003evalid);\n-\t} while (mmu_notifier_retry_cache(gpc-\u003ekvm, mmu_seq));\n+\t} while (mmu_notifier_retry_cache(gpc-\u003ekvm, gpc_seq));\n \n \tgpc-\u003evalid = true;\n \tgpc-\u003epfn = new_pfn;\n"
}

2/1 2026/09/01 04:38 agent patch-evaluator
1m Model:
core

Args:
null

Results:
{
  "EnableConfigs": [
    "KVM",
    "KVM_INTEL",
    "KVM_AMD",
    "KVM_X86",
    "KVM_XEN"
  ],
  "FocusSymbols": [
    "kvm_xen_vcpu_set_attr",
    "kvm_xen_hvm_set_attr",
    "kvm_xen_hypercall",
    "kvm_xen_inject_pending_events",
    "__kvm_xen_has_interrupt",
    "kvm_xen_set_evtchn_fast",
    "kvm_xen_hvm_evtchn_send",
    "kvm_gpc_refresh",
    "kvm_gpc_activate"
  ],
  "Reasoning": "The patch makes functional and concurrency-critical changes to KVM's Xen emulation and gfn_to_pfn_cache (GPC) subsystem. These include migrating Xen event channel storage from IDR to XArray, enforcing alignment checks for vCPU info GPA/HVA, using explicit 32-bit atomic bitops to prevent split-lock AC exceptions on unaligned structures, introducing SRCU locking in __kvm_xen_has_interrupt, and separating GPC invalidation sequencing from general MMU invalidation sequencing. These changes affect reachable virtualization ioctls, hypercalls, and event channel delivery mechanisms.",
  "WorthFuzzing": true
}

Instruction:
You are an expert Linux kernel maintainer.
Your job is to review a provided patch series and determine
if it makes functional changes to the kernel that should be fuzzed.

IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in
your workspace. Do NOT rely on your internal knowledge of the kernel. You must actively
use your code access tools to examine the actual source code and confirm any assumptions.

Return WorthFuzzing=false if the patch only contains:
- Modifications to Documentation/, Kconfig files, or code comments.
- Purely decorative changes, such as logging (e.g., pr_err, printk) or tracepoints.
- Changes to numeric constants or macros that do not functionally alter execution flow.
- Code paths that are impossible to reach in virtualized environments like GCE or QEMU,
  even when utilizing software-emulated hardware (e.g., usb gadget, mac80211_hwsim).
- Code in vendor-specific PCIe switch, SmartNIC, or GPU drivers (e.g., mlxsw, pds_core, qed,
  ionic, amdgpu) that require physical PCIe hardware cards not emulated in standard QEMU.
- Driver .remove, .shutdown, or pci_unregister_driver teardown callbacks (e.g., igb_remove)
  that are executed only during PCI hot-unplug or sysfs driver unbind operations.

If it modifies reachable core kernel logic, drivers, or architectures, use your code search
tools to verify the code can be executed, then return WorthFuzzing=true.

When returning WorthFuzzing=true, you MUST ALSO:
1. Extract any specific kernel functions that should be heavily fuzzed into FocusSymbols.
   Avoid listing generic hot-path functions to prevent skewed test distributions.
   Prefer non-static, non-inlined API entrypoint functions over internal static helper functions
   (which are inlined by the compiler and do not have distinct symbol addresses).
2. Identify any specific CONFIG_ options required to properly test this new/modified feature.
   Go and look into the Kconfig files and check for ifdefs around the code, do not make assumptions.
   Also check "depends on" lines in Kconfig to include any non-standard parent subsystem configs
   needed for Kbuild to compile the code statically into vmlinux. List them in the EnableConfigs
   output array, and DO NOT add a 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4").
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:
For your convenience, here is the diff of the changes:
commit c3e03ff3b8c379d8b32820c99ebc02d802e303b2
Author: syz-cluster <triage@syzkaller.com>
Date:   Tue Sep 1 04:38:22 2026 +0000

    syz-cluster: applied patch under review

diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index 683bb8bf43a94..3af7395c2430a 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -16,6 +16,7 @@
 #include <linux/irq_work.h>
 #include <linux/irq.h>
 #include <linux/workqueue.h>
+#include <linux/xarray.h>
 
 #include <linux/kvm.h>
 #include <linux/kvm_para.h>
@@ -1113,7 +1114,7 @@ struct kvm_xen {
 	bool runstate_update_flag;
 	u8 upcall_vector;
 	struct gfn_to_pfn_cache shinfo_cache;
-	struct idr evtchn_ports;
+	struct xarray evtchn_ports;
 	unsigned long poll_mask[BITS_TO_LONGS(KVM_MAX_VCPUS)];
 
 	struct kvm_xen_hvm_config hvm_config;
diff --git a/arch/x86/kvm/xen.c b/arch/x86/kvm/xen.c
index 998cdcb6aa303..718396340d3c1 100644
--- a/arch/x86/kvm/xen.c
+++ b/arch/x86/kvm/xen.c
@@ -73,7 +73,7 @@ static int kvm_xen_shared_info_init(struct kvm *kvm)
 	BUILD_BUG_ON(offsetof(struct shared_info, wc) != 0xc00);
 	BUILD_BUG_ON(offsetof(struct shared_info, wc_sec_hi) != 0xc0c);
 
-	if (IS_ENABLED(CONFIG_64BIT) && kvm->arch.xen.long_mode) {
+	if (kvm_xen_has_64bit_shinfo(kvm)) {
 		struct shared_info *shinfo = gpc->khva;
 
 		wc_sec_hi = &shinfo->wc_sec_hi;
@@ -389,7 +389,7 @@ static void kvm_xen_update_runstate_guest(struct kvm_vcpu *v, bool atomic)
 	BUILD_BUG_ON(sizeof_field(struct vcpu_runstate_info, time) !=
 		     sizeof(vx->runstate_times));
 
-	if (IS_ENABLED(CONFIG_64BIT) && v->kvm->arch.xen.long_mode) {
+	if (kvm_xen_has_64bit_shinfo(v->kvm)) {
 		user_len = sizeof(struct vcpu_runstate_info);
 		times_ofs = offsetof(struct vcpu_runstate_info,
 				     state_entry_time);
@@ -676,28 +676,32 @@ void kvm_xen_inject_pending_events(struct kvm_vcpu *v)
 	}
 
 	/* Now gpc->khva is a valid kernel address for the vcpu_info */
-	if (IS_ENABLED(CONFIG_64BIT) && v->kvm->arch.xen.long_mode) {
+	if (kvm_xen_has_64bit_shinfo(v->kvm)) {
 		struct vcpu_info *vi = gpc->khva;
+		void *vi_pending_sel = &vi->evtchn_pending_sel;
 
-		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));
+		if (IS_ALIGNED((unsigned long)vi_pending_sel, sizeof(u64))) {
+			atomic64_or(evtchn_pending_sel, vi_pending_sel);
+		} else {
+			atomic_or(evtchn_pending_sel, vi_pending_sel);
+			/*
+			 * The cast keeps the shift well-defined on 32-bit,
+			 * where evtchn_pending_sel is 32 bits wide and this
+			 * branch is unreachable anyway (this is inside
+			 * kvm_xen_has_64bit_shinfo(), which is gated on
+			 * IS_ENABLED(CONFIG_64BIT)).
+			 */
+			atomic_or((u64)evtchn_pending_sel >> 32,
+				  vi_pending_sel + 4);
+		}
+
+		atomic64_andnot(evtchn_pending_sel, (void *)&v->arch.xen.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));
+		atomic_or(evtchn_pending_sel, (void *)&vi->evtchn_pending_sel);
+		atomic_andnot(evtchn_pending_sel, (void *)&v->arch.xen.evtchn_pending_sel);
 		WRITE_ONCE(vi->evtchn_upcall_pending, 1);
 	}
 
@@ -728,6 +732,16 @@ int __kvm_xen_has_interrupt(struct kvm_vcpu *v)
 	BUILD_BUG_ON(sizeof(rc) !=
 		     sizeof_field(struct compat_vcpu_info, evtchn_upcall_pending));
 
+	/*
+	 * kvm_gpc_check() checks the memslot generation, so kvm->srcu must be
+	 * held. Most callers hold it already, but this is also reached from
+	 * kvm_emulate_halt() on the VM-Exit path and from kvm_vcpu_block(),
+	 * where vcpu_enter_guest() has already dropped the vCPU's SRCU lock.
+	 * Taking SRCU does not sleep, so it is safe even in the atomic case
+	 * which is handled below.
+	 */
+	guard(srcu)(&v->kvm->srcu);
+
 	read_lock_irqsave(&gpc->lock, flags);
 	while (!kvm_gpc_check(gpc, sizeof(struct vcpu_info))) {
 		read_unlock_irqrestore(&gpc->lock, flags);
@@ -941,6 +955,10 @@ int kvm_xen_vcpu_set_attr(struct kvm_vcpu *vcpu, struct kvm_xen_vcpu_attr *data)
 				break;
 			}
 
+			r = -ENXIO;
+			if (!IS_ALIGNED(data->u.gpa, sizeof(u32)))
+				break;
+
 			r = kvm_gpc_activate(&vcpu->arch.xen.vcpu_info_cache,
 					     data->u.gpa, sizeof(struct vcpu_info));
 		} else {
@@ -950,6 +968,10 @@ int kvm_xen_vcpu_set_attr(struct kvm_vcpu *vcpu, struct kvm_xen_vcpu_attr *data)
 				break;
 			}
 
+			r = -ENXIO;
+			if (!IS_ALIGNED(data->u.hva, sizeof(u32)))
+				break;
+
 			r = kvm_gpc_activate_hva(&vcpu->arch.xen.vcpu_info_cache,
 						 data->u.hva, sizeof(struct vcpu_info));
 		}
@@ -993,7 +1015,7 @@ int kvm_xen_vcpu_set_attr(struct kvm_vcpu *vcpu, struct kvm_xen_vcpu_attr *data)
 		 * address, that's actually OK. kvm_xen_update_runstate_guest()
 		 * will cope.
 		 */
-		if (IS_ENABLED(CONFIG_64BIT) && vcpu->kvm->arch.xen.long_mode)
+		if (kvm_xen_has_64bit_shinfo(vcpu->kvm))
 			sz = sizeof(struct vcpu_runstate_info);
 		else
 			sz = sizeof(struct compat_vcpu_runstate_info);
@@ -1439,16 +1461,21 @@ static int kvm_xen_hypercall_complete_userspace(struct kvm_vcpu *vcpu)
 	return kvm_xen_hypercall_set_result(vcpu, run->xen.u.hcall.result);
 }
 
-static inline int max_evtchn_port(struct kvm *kvm)
+static inline int max_evtchn_port(bool has_64bit_shinfo)
 {
-	if (IS_ENABLED(CONFIG_64BIT) && kvm->arch.xen.long_mode)
+	if (has_64bit_shinfo)
 		return EVTCHN_2L_NR_CHANNELS;
 	else
 		return COMPAT_EVTCHN_2L_NR_CHANNELS;
 }
 
-static bool wait_pending_event(struct kvm_vcpu *vcpu, int nr_ports,
-			       evtchn_port_t *ports)
+static inline int kvm_max_evtchn_port(struct kvm *kvm)
+{
+	return max_evtchn_port(kvm_xen_has_64bit_shinfo(kvm));
+}
+
+static bool wait_pending_event(struct kvm_vcpu *vcpu, bool has_64bit_shinfo,
+			       int nr_ports, evtchn_port_t *ports)
 {
 	struct kvm *kvm = vcpu->kvm;
 	struct gfn_to_pfn_cache *gpc = &kvm->arch.xen.shinfo_cache;
@@ -1463,7 +1490,7 @@ static bool wait_pending_event(struct kvm_vcpu *vcpu, int nr_ports,
 		goto out_rcu;
 
 	ret = false;
-	if (IS_ENABLED(CONFIG_64BIT) && kvm->arch.xen.long_mode) {
+	if (has_64bit_shinfo) {
 		struct shared_info *shinfo = gpc->khva;
 		pending_bits = (unsigned long *)&shinfo->evtchn_pending;
 	} else {
@@ -1485,9 +1512,10 @@ static bool wait_pending_event(struct kvm_vcpu *vcpu, int nr_ports,
 	return ret;
 }
 
-static bool kvm_xen_schedop_poll(struct kvm_vcpu *vcpu, bool longmode,
+static bool kvm_xen_schedop_poll(struct kvm_vcpu *vcpu, bool is_64bit,
 				 u64 param, u64 *r)
 {
+	bool has_64bit_shinfo = kvm_xen_has_64bit_shinfo(vcpu->kvm);
 	struct sched_poll sched_poll;
 	evtchn_port_t port, *ports;
 	struct x86_exception e;
@@ -1497,7 +1525,7 @@ static bool kvm_xen_schedop_poll(struct kvm_vcpu *vcpu, bool longmode,
 	    !(vcpu->kvm->arch.xen.hvm_config.flags & KVM_XEN_HVM_CONFIG_EVTCHN_SEND))
 		return false;
 
-	if (IS_ENABLED(CONFIG_64BIT) && !longmode) {
+	if (IS_ENABLED(CONFIG_64BIT) && !is_64bit) {
 		struct compat_sched_poll sp32;
 
 		/* Sanity check that the compat struct definition is correct */
@@ -1546,20 +1574,20 @@ static bool kvm_xen_schedop_poll(struct kvm_vcpu *vcpu, bool longmode,
 	}
 
 	for (i = 0; i < sched_poll.nr_ports; i++) {
-		if (ports[i] >= max_evtchn_port(vcpu->kvm)) {
+		if (ports[i] >= max_evtchn_port(has_64bit_shinfo)) {
 			*r = -EINVAL;
 			goto out;
 		}
 	}
 
 	if (sched_poll.nr_ports == 1)
-		vcpu->arch.xen.poll_evtchn = port;
+		WRITE_ONCE(vcpu->arch.xen.poll_evtchn, port);
 	else
-		vcpu->arch.xen.poll_evtchn = -1;
+		WRITE_ONCE(vcpu->arch.xen.poll_evtchn, -1);
 
 	set_bit(vcpu->vcpu_idx, vcpu->kvm->arch.xen.poll_mask);
 
-	if (!wait_pending_event(vcpu, sched_poll.nr_ports, ports)) {
+	if (!wait_pending_event(vcpu, has_64bit_shinfo, sched_poll.nr_ports, ports)) {
 		kvm_set_mp_state(vcpu, KVM_MP_STATE_HALTED);
 
 		if (sched_poll.timeout)
@@ -1574,7 +1602,7 @@ static bool kvm_xen_schedop_poll(struct kvm_vcpu *vcpu, bool longmode,
 		kvm_set_mp_state(vcpu, KVM_MP_STATE_RUNNABLE);
 	}
 
-	vcpu->arch.xen.poll_evtchn = 0;
+	WRITE_ONCE(vcpu->arch.xen.poll_evtchn, 0);
 	*r = 0;
 out:
 	/* Really, this is only needed in case of timeout */
@@ -1594,12 +1622,12 @@ static void cancel_evtchn_poll(struct timer_list *t)
 	kvm_vcpu_kick(vcpu);
 }
 
-static bool kvm_xen_hcall_sched_op(struct kvm_vcpu *vcpu, bool longmode,
+static bool kvm_xen_hcall_sched_op(struct kvm_vcpu *vcpu, bool is_64bit,
 				   int cmd, u64 param, u64 *r)
 {
 	switch (cmd) {
 	case SCHEDOP_poll:
-		if (kvm_xen_schedop_poll(vcpu, longmode, param, r))
+		if (kvm_xen_schedop_poll(vcpu, is_64bit, param, r))
 			return true;
 		fallthrough;
 	case SCHEDOP_yield:
@@ -1618,7 +1646,7 @@ struct compat_vcpu_set_singleshot_timer {
     uint32_t flags;
 } __attribute__((packed));
 
-static bool kvm_xen_hcall_vcpu_op(struct kvm_vcpu *vcpu, bool longmode, int cmd,
+static bool kvm_xen_hcall_vcpu_op(struct kvm_vcpu *vcpu, bool is_64bit, int cmd,
 				  int vcpu_id, u64 param, u64 *r)
 {
 	struct vcpu_set_singleshot_timer oneshot;
@@ -1662,7 +1690,7 @@ static bool kvm_xen_hcall_vcpu_op(struct kvm_vcpu *vcpu, bool longmode, int cmd,
 		BUILD_BUG_ON(sizeof_field(struct compat_vcpu_set_singleshot_timer, flags) !=
 			     sizeof_field(struct vcpu_set_singleshot_timer, flags));
 
-		if (kvm_read_guest_virt(vcpu, param, &oneshot, longmode ? sizeof(oneshot) :
+		if (kvm_read_guest_virt(vcpu, param, &oneshot, is_64bit ? sizeof(oneshot) :
 					sizeof(struct compat_vcpu_set_singleshot_timer), &e)) {
 			*r = -EFAULT;
 			return true;
@@ -1694,7 +1722,7 @@ static bool kvm_xen_hcall_set_timer_op(struct kvm_vcpu *vcpu, uint64_t timeout,
 
 int kvm_xen_hypercall(struct kvm_vcpu *vcpu)
 {
-	bool longmode;
+	bool is_64bit;
 	u64 input, params[6], r = -ENOSYS;
 	bool handled = false;
 	u8 cpl;
@@ -1704,8 +1732,8 @@ int kvm_xen_hypercall(struct kvm_vcpu *vcpu)
 	    kvm_hv_hypercall_enabled(vcpu))
 		return kvm_hv_hypercall(vcpu);
 
-	longmode = is_64_bit_hypercall(vcpu);
-	if (!longmode) {
+	is_64bit = is_64_bit_hypercall(vcpu);
+	if (!is_64bit) {
 		input = kvm_eax_read(vcpu);
 		params[0] = kvm_ebx_read(vcpu);
 		params[1] = kvm_ecx_read(vcpu);
@@ -1751,17 +1779,17 @@ int kvm_xen_hypercall(struct kvm_vcpu *vcpu)
 			handled = kvm_xen_hcall_evtchn_send(vcpu, params[1], &r);
 		break;
 	case __HYPERVISOR_sched_op:
-		handled = kvm_xen_hcall_sched_op(vcpu, longmode, params[0],
+		handled = kvm_xen_hcall_sched_op(vcpu, is_64bit, params[0],
 						 params[1], &r);
 		break;
 	case __HYPERVISOR_vcpu_op:
-		handled = kvm_xen_hcall_vcpu_op(vcpu, longmode, params[0], params[1],
+		handled = kvm_xen_hcall_vcpu_op(vcpu, is_64bit, 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)
+		if (!is_64bit)
 			timeout |= params[1] << 32;
 		handled = kvm_xen_hcall_set_timer_op(vcpu, timeout, &r);
 		break;
@@ -1776,7 +1804,7 @@ int kvm_xen_hypercall(struct kvm_vcpu *vcpu)
 handle_in_userspace:
 	vcpu->run->exit_reason = KVM_EXIT_XEN;
 	vcpu->run->xen.type = KVM_EXIT_XEN_HCALL;
-	vcpu->run->xen.u.hcall.longmode = longmode;
+	vcpu->run->xen.u.hcall.longmode = is_64bit;
 	vcpu->run->xen.u.hcall.cpl = cpl;
 	vcpu->run->xen.u.hcall.input = input;
 	vcpu->run->xen.u.hcall.params[0] = params[0];
@@ -1794,7 +1822,7 @@ int kvm_xen_hypercall(struct kvm_vcpu *vcpu)
 
 static void kvm_xen_check_poller(struct kvm_vcpu *vcpu, int port)
 {
-	int poll_evtchn = vcpu->arch.xen.poll_evtchn;
+	int poll_evtchn = READ_ONCE(vcpu->arch.xen.poll_evtchn);
 
 	if ((poll_evtchn == port || poll_evtchn == -1) &&
 	    test_and_clear_bit(vcpu->vcpu_idx, vcpu->kvm->arch.xen.poll_mask)) {
@@ -1816,8 +1844,9 @@ static void kvm_xen_check_poller(struct kvm_vcpu *vcpu, int port)
 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;
+	bool has_64bit_shinfo = kvm_xen_has_64bit_shinfo(kvm);
+	unsigned long *pending_bits, *mask_bits, vi_pending_sel_ofs;
 	struct kvm_vcpu *vcpu;
-	unsigned long *pending_bits, *mask_bits;
 	unsigned long flags;
 	int port_word_bit;
 	bool kick_vcpu = false;
@@ -1833,7 +1862,7 @@ int kvm_xen_set_evtchn_fast(struct kvm_xen_evtchn *xe, struct kvm *kvm)
 		WRITE_ONCE(xe->vcpu_idx, vcpu->vcpu_idx);
 	}
 
-	if (xe->port >= max_evtchn_port(kvm))
+	if (xe->port >= max_evtchn_port(has_64bit_shinfo))
 		return -EINVAL;
 
 	rc = -EWOULDBLOCK;
@@ -1844,16 +1873,23 @@ int kvm_xen_set_evtchn_fast(struct kvm_xen_evtchn *xe, struct kvm *kvm)
 	if (!kvm_gpc_check(gpc, PAGE_SIZE))
 		goto out_rcu;
 
-	if (IS_ENABLED(CONFIG_64BIT) && kvm->arch.xen.long_mode) {
+	if (has_64bit_shinfo) {
 		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;
+
+		vi_pending_sel_ofs = offsetof(struct vcpu_info, evtchn_pending_sel);
 	} 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;
+
+		vi_pending_sel_ofs = offsetof(struct compat_vcpu_info, evtchn_pending_sel);
+
+		/* test_and_set_bit() needs 64-bit alignment, but that's OK */
+		BUILD_BUG_ON(offsetof(struct compat_shared_info, evtchn_pending) & 7);
 	}
 
 	/*
@@ -1869,6 +1905,8 @@ int kvm_xen_set_evtchn_fast(struct kvm_xen_evtchn *xe, struct kvm *kvm)
 		rc = -ENOTCONN; /* Masked */
 		kvm_xen_check_poller(vcpu, xe->port);
 	} else {
+		bool old;
+
 		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);
@@ -1885,19 +1923,29 @@ int kvm_xen_set_evtchn_fast(struct kvm_xen_evtchn *xe, struct kvm *kvm)
 			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;
-			}
+		/*
+		 * Explicitly use a 32-bit btsl instead of test_and_set_bit(),
+		 * which would use btsq on x86-64. The vcpu_info is guest-
+		 * controlled and only required to be 32-bit aligned, so a
+		 * 64-bit access could generate a split-lock #AC.
+		 *
+		 * Note, this does not apply to the test_and_set_bit() on
+		 * pending_bits above: that is in the per-VM shared_info, which
+		 * is page aligned, so the access is guaranteed to be 64-bit
+		 * aligned.
+		 */
+		old = GEN_BINARY_RMWcc(LOCK_PREFIX "btsl",
+				       *(u32 *)(gpc->khva + vi_pending_sel_ofs),
+				       c, "Ir", port_word_bit);
+		if (!old) {
+			struct vcpu_info *vi = gpc->khva;
+
+			/* No need for compat handling */
+			BUILD_BUG_ON(offsetof(struct vcpu_info, evtchn_upcall_pending) !=
+				     offsetof(struct compat_vcpu_info, evtchn_upcall_pending));
+
+			WRITE_ONCE(vi->evtchn_upcall_pending, 1);
+			kick_vcpu = true;
 		}
 
 		/* For the per-vCPU lapic vector, deliver it as MSI. */
@@ -1995,7 +2043,7 @@ int kvm_xen_setup_evtchn(struct kvm *kvm,
 	struct kvm_vcpu *vcpu;
 
 	/*
-	 * Don't check for the port being within range of max_evtchn_port().
+	 * Don't check for the port being within range of kvm_max_evtchn_port().
 	 * Userspace can configure what ever targets it likes; events just won't
 	 * be delivered if/while the target is invalid, just like userspace can
 	 * configure MSIs which target non-existent APICs.
@@ -2004,8 +2052,8 @@ int kvm_xen_setup_evtchn(struct kvm *kvm,
 	 * can be restored *independently* of other things like creating vCPUs,
 	 * without imposing an ordering dependency on userspace.  In this
 	 * particular case, the problematic ordering would be with setting the
-	 * Xen 'long mode' flag, which changes max_evtchn_port() to allow 4096
-	 * instead of 1024 event channels.
+	 * Xen 'long mode' flag, which changes kvm_max_evtchn_port() to allow
+	 * 4096 instead of 1024 event channels.
 	 */
 
 	/* We only support 2 level event channels for now */
@@ -2042,7 +2090,7 @@ int kvm_xen_hvm_evtchn_send(struct kvm *kvm, struct kvm_irq_routing_xen_evtchn *
 	struct kvm_xen_evtchn e;
 	int ret;
 
-	if (!uxe->port || uxe->port >= max_evtchn_port(kvm))
+	if (!uxe->port || uxe->port >= kvm_max_evtchn_port(kvm))
 		return -EINVAL;
 
 	/* We only support 2 level event channels for now */
@@ -2093,7 +2141,7 @@ static int kvm_xen_eventfd_update(struct kvm *kvm,
 
 	/* Protect writes to evtchnfd as well as the idr lookup.  */
 	mutex_lock(&kvm->arch.xen.xen_lock);
-	evtchnfd = idr_find(&kvm->arch.xen.evtchn_ports, port);
+	evtchnfd = xa_load(&kvm->arch.xen.evtchn_ports, port);
 
 	ret = -ENOENT;
 	if (!evtchnfd)
@@ -2152,7 +2200,7 @@ static int kvm_xen_eventfd_assign(struct kvm *kvm,
 
 	case EVTCHNSTAT_interdomain:
 		if (data->u.evtchn.deliver.port.port) {
-			if (data->u.evtchn.deliver.port.port >= max_evtchn_port(kvm))
+			if (data->u.evtchn.deliver.port.port >= kvm_max_evtchn_port(kvm))
 				goto out_noeventfd; /* -EINVAL */
 		} else {
 			eventfd = eventfd_ctx_fdget(data->u.evtchn.deliver.eventfd.fd);
@@ -2187,13 +2235,13 @@ static int kvm_xen_eventfd_assign(struct kvm *kvm,
 	}
 
 	mutex_lock(&kvm->arch.xen.xen_lock);
-	ret = idr_alloc(&kvm->arch.xen.evtchn_ports, evtchnfd, port, port + 1,
+	ret = xa_insert(&kvm->arch.xen.evtchn_ports, port, evtchnfd,
 			GFP_KERNEL);
 	mutex_unlock(&kvm->arch.xen.xen_lock);
-	if (ret >= 0)
+	if (!ret)
 		return 0;
 
-	if (ret == -ENOSPC)
+	if (ret == -EBUSY)
 		ret = -EEXIST;
 out:
 	if (eventfd)
@@ -2208,7 +2256,7 @@ static int kvm_xen_eventfd_deassign(struct kvm *kvm, u32 port)
 	struct evtchnfd *evtchnfd;
 
 	mutex_lock(&kvm->arch.xen.xen_lock);
-	evtchnfd = idr_remove(&kvm->arch.xen.evtchn_ports, port);
+	evtchnfd = xa_erase(&kvm->arch.xen.evtchn_ports, port);
 	mutex_unlock(&kvm->arch.xen.xen_lock);
 
 	if (!evtchnfd)
@@ -2224,7 +2272,7 @@ static int kvm_xen_eventfd_deassign(struct kvm *kvm, u32 port)
 static int kvm_xen_eventfd_reset(struct kvm *kvm)
 {
 	struct evtchnfd *evtchnfd, **all_evtchnfds;
-	int i;
+	unsigned long i;
 	int n = 0;
 
 	mutex_lock(&kvm->arch.xen.xen_lock);
@@ -2234,7 +2282,7 @@ static int kvm_xen_eventfd_reset(struct kvm *kvm)
 	 * critical section, first collect all the evtchnfd objects
 	 * in an array as they are removed from evtchn_ports.
 	 */
-	idr_for_each_entry(&kvm->arch.xen.evtchn_ports, evtchnfd, i)
+	xa_for_each(&kvm->arch.xen.evtchn_ports, i, evtchnfd)
 		n++;
 
 	all_evtchnfds = kmalloc_objs(struct evtchnfd *, n);
@@ -2244,9 +2292,9 @@ static int kvm_xen_eventfd_reset(struct kvm *kvm)
 	}
 
 	n = 0;
-	idr_for_each_entry(&kvm->arch.xen.evtchn_ports, evtchnfd, i) {
+	xa_for_each(&kvm->arch.xen.evtchn_ports, i, evtchnfd) {
 		all_evtchnfds[n++] = evtchnfd;
-		idr_remove(&kvm->arch.xen.evtchn_ports, evtchnfd->send_port);
+		xa_erase(&kvm->arch.xen.evtchn_ports, evtchnfd->send_port);
 	}
 	mutex_unlock(&kvm->arch.xen.xen_lock);
 
@@ -2270,7 +2318,7 @@ static int kvm_xen_setattr_evtchn(struct kvm *kvm, struct kvm_xen_hvm_attr *data
 	if (data->u.evtchn.flags == KVM_XEN_EVTCHN_RESET)
 		return kvm_xen_eventfd_reset(kvm);
 
-	if (!port || port >= max_evtchn_port(kvm))
+	if (!port || port >= kvm_max_evtchn_port(kvm))
 		return -EINVAL;
 
 	if (data->u.evtchn.flags == KVM_XEN_EVTCHN_DEASSIGN)
@@ -2297,12 +2345,10 @@ static bool kvm_xen_hcall_evtchn_send(struct kvm_vcpu *vcpu, u64 param, u64 *r)
 	}
 
 	/*
-	 * evtchnfd is protected by kvm->srcu; the idr lookup instead
-	 * is protected by RCU.
+	 * evtchnfd is protected by kvm->srcu; the xa_load is RCU-safe
+	 * internally, no explicit rcu_read_lock() needed.
 	 */
-	rcu_read_lock();
-	evtchnfd = idr_find(&vcpu->kvm->arch.xen.evtchn_ports, send.port);
-	rcu_read_unlock();
+	evtchnfd = xa_load(&vcpu->kvm->arch.xen.evtchn_ports, send.port);
 	if (!evtchnfd)
 		return false;
 
@@ -2349,23 +2395,23 @@ void kvm_xen_destroy_vcpu(struct kvm_vcpu *vcpu)
 void kvm_xen_init_vm(struct kvm *kvm)
 {
 	mutex_init(&kvm->arch.xen.xen_lock);
-	idr_init(&kvm->arch.xen.evtchn_ports);
+	xa_init(&kvm->arch.xen.evtchn_ports);
 	kvm_gpc_init(&kvm->arch.xen.shinfo_cache, kvm);
 }
 
 void kvm_xen_destroy_vm(struct kvm *kvm)
 {
 	struct evtchnfd *evtchnfd;
-	int i;
+	unsigned long i;
 
 	kvm_gpc_deactivate(&kvm->arch.xen.shinfo_cache);
 
-	idr_for_each_entry(&kvm->arch.xen.evtchn_ports, evtchnfd, i) {
+	xa_for_each(&kvm->arch.xen.evtchn_ports, i, evtchnfd) {
 		if (!evtchnfd->deliver.port.port)
 			eventfd_ctx_put(evtchnfd->deliver.eventfd.ctx);
 		kfree(evtchnfd);
 	}
-	idr_destroy(&kvm->arch.xen.evtchn_ports);
+	xa_destroy(&kvm->arch.xen.evtchn_ports);
 
 	if (kvm->arch.xen.hvm_config.msr)
 		static_branch_slow_dec_deferred(&kvm_xen_enabled);
diff --git a/arch/x86/kvm/xen.h b/arch/x86/kvm/xen.h
index f372855857a88..9d04e350bdb1f 100644
--- a/arch/x86/kvm/xen.h
+++ b/arch/x86/kvm/xen.h
@@ -235,6 +235,11 @@ struct compat_shared_info {
 #define COMPAT_EVTCHN_2L_NR_CHANNELS (8 *				\
 				      sizeof_field(struct compat_shared_info, \
 						   evtchn_pending))
+
+/* Latched VM-wide mode; the KVM equivalent of Xen's !has_32bit_shinfo(). */
+#define kvm_xen_has_64bit_shinfo(kvm) \
+	(IS_ENABLED(CONFIG_64BIT) && READ_ONCE((kvm)->arch.xen.long_mode))
+
 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 03bfc92864b6e..3dd04605f2e55 100644
--- a/include/linux/kvm_host.h
+++ b/include/linux/kvm_host.h
@@ -855,6 +855,8 @@ struct kvm {
 	gfn_t mmu_invalidate_range_start;
 	gfn_t mmu_invalidate_range_end;
 
+	unsigned long gpc_invalidate_seq;
+
 	struct list_head devices;
 	u64 manual_dirty_log_protect;
 	struct dentry *debugfs_dentry;
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
index 65eb26a0520d8..108d42c5c1d6a 100644
--- a/virt/kvm/kvm_main.c
+++ b/virt/kvm/kvm_main.c
@@ -813,6 +813,16 @@ static void kvm_mmu_notifier_invalidate_range_end(struct mmu_notifier *mn,
 
 	/* Pairs with the increment in range_start(). */
 	spin_lock(&kvm->mn_invalidate_lock);
+	kvm->gpc_invalidate_seq++;
+
+	/*
+	 * As with the MMU sequence counter and mmu_invalidate_in_progress, the
+	 * GPC sequence increase must be visible before the invalidate count
+	 * goes to zero.  Pairs with the smp_rmb() in
+	 * mmu_notifier_retry_cache().
+	 */
+	smp_wmb();
+
 	if (!WARN_ON_ONCE(!kvm->mn_active_invalidate_count))
 		--kvm->mn_active_invalidate_count;
 	wake = !kvm->mn_active_invalidate_count;
diff --git a/virt/kvm/pfncache.c b/virt/kvm/pfncache.c
index 728d2c1b488a8..3659686b97c24 100644
--- a/virt/kvm/pfncache.c
+++ b/virt/kvm/pfncache.c
@@ -124,7 +124,7 @@ static void gpc_unmap(kvm_pfn_t pfn, void *khva)
 #endif
 }
 
-static inline bool mmu_notifier_retry_cache(struct kvm *kvm, unsigned long mmu_seq)
+static inline bool mmu_notifier_retry_cache(struct kvm *kvm, unsigned long gpc_seq)
 {
 	/*
 	 * mn_active_invalidate_count acts for all intents and purposes
@@ -136,20 +136,20 @@ static inline bool mmu_notifier_retry_cache(struct kvm *kvm, unsigned long mmu_s
 	 * Note, it does not matter that mn_active_invalidate_count
 	 * is not protected by gpc->lock.  It is guaranteed to
 	 * be elevated before the mmu_notifier acquires gpc->lock, and
-	 * isn't dropped until after mmu_invalidate_seq is updated.
+	 * isn't dropped until after gpc_invalidate_seq is updated.
 	 */
 	if (kvm->mn_active_invalidate_count)
 		return true;
 
 	/*
 	 * Ensure mn_active_invalidate_count is read before
-	 * mmu_invalidate_seq.  This pairs with the smp_wmb() in
-	 * mmu_notifier_invalidate_range_end() to guarantee either the
+	 * gpc_invalidate_seq.  This pairs with the smp_wmb() in
+	 * kvm_mmu_notifier_invalidate_range_end() to guarantee either the
 	 * old (non-zero) value of mn_active_invalidate_count or the
-	 * new (incremented) value of mmu_invalidate_seq is observed.
+	 * new (incremented) value of gpc_invalidate_seq is observed.
 	 */
 	smp_rmb();
-	return kvm->mmu_invalidate_seq != mmu_seq;
+	return kvm->gpc_invalidate_seq != gpc_seq;
 }
 
 static kvm_pfn_t hva_to_pfn_retry(struct gfn_to_pfn_cache *gpc)
@@ -158,7 +158,7 @@ static kvm_pfn_t hva_to_pfn_retry(struct gfn_to_pfn_cache *gpc)
 	void *old_khva = (void *)PAGE_ALIGN_DOWN((uintptr_t)gpc->khva);
 	kvm_pfn_t new_pfn = KVM_PFN_ERR_FAULT;
 	void *new_khva = NULL;
-	unsigned long mmu_seq;
+	unsigned long gpc_seq;
 	struct page *page;
 
 	struct kvm_follow_pfn kfp = {
@@ -181,7 +181,7 @@ static kvm_pfn_t hva_to_pfn_retry(struct gfn_to_pfn_cache *gpc)
 	gpc->valid = false;
 
 	do {
-		mmu_seq = gpc->kvm->mmu_invalidate_seq;
+		gpc_seq = gpc->kvm->gpc_invalidate_seq;
 		smp_rmb();
 
 		write_unlock_irq(&gpc->lock);
@@ -232,7 +232,7 @@ static kvm_pfn_t hva_to_pfn_retry(struct gfn_to_pfn_cache *gpc)
 		 * attempting to refresh.
 		 */
 		WARN_ON_ONCE(gpc->valid);
-	} while (mmu_notifier_retry_cache(gpc->kvm, mmu_seq));
+	} while (mmu_notifier_retry_cache(gpc->kvm, gpc_seq));
 
 	gpc->valid = true;
 	gpc->pfn = new_pfn;

3/2 2026/09/01 04:38 llm patch-evaluator
0m Model:
gemini-3.7-flash

Args:
null

Results:
null

Tokens:
							input: 13271
							output: 31
							thoughts: 491
						

Thoughts:
**Analyzing the KVM Patch Series for Fuzzing Potential**

Okay, let's break this down. My initial scan of the patch series reveals some interesting files that have been modified: `arch/x86/include/asm/kvm_host.h`, `arch/x86/kvm/xen.c`, `arch/x86/kvm/xen.h`, `include/linux/kvm_host.h`, `virt/kvm/kvm_main.c`, and `virt/kvm/pfncache.c`. Immediately, I see KVM involvement, specifically related to Xen emulation within the x86 KVM context, as well as the generic KVM pfncache and MMU notifier logic. This piques my interest – KVM is a core component of virtualization, making it a potentially fruitful target for fuzzing. It's accessible within QEMU, VMs with KVM, and potentially even through nested virtualization or user-space interaction with `/dev/kvm`.

Now, digging into the functional changes: We have an XArray conversion replacing an IDR for Xen event channels in KVM Xen emulation (`evtchn_ports`), some alignment checks for GPA/HVA when activating the `vcpu_info` cache (`kvm_xen_vcpu_set_attr`), and a split of `mmu_invalidate_seq` into `gpc_invalidate_seq` for the GPC. There's also mention of `btsl` for atomic bitmap operations, aiming to avoid those pesky split-lock #AC faults on unaligned `vcpu_info` `evtchn_pending_sel` accesses. There's a `guard(srcu)` usage, and some fixes/refactorings related to those 64/32-bit Xen shared info mode checks (`kvm_xen_has_64bit_shinfo`).

From a functional perspective, all of these changes touch core kernel logic within KVM, specifically Xen emulation. Moreover, there's concurrency implications here, which is always a red flag that draws me towards fuzzing, so I'll set `WorthFuzzing = true`.

Next, I need to figure out the build configurations. I'll need to know which Kconfig options are required to enable all this functionality, so I'll check `KVM`, `KVM_XEN`, and any other relevant options. I'll use a `grepper` or `read-file` to check the Kconfig files, specifically `arch/x86/kvm/Kconfig` and `virt/kvm/Kconfig`.



4/2 2026/09/01 04:38 tool grepper
0m Args:
{
  "Expression": "config KVM",
  "PathPrefix": "arch/x86/kvm"
}

Results:
{
  "Output": "arch/x86/kvm/Kconfig=18=if VIRTUALIZATION\narch/x86/kvm/Kconfig-19-\narch/x86/kvm/Kconfig:20:config KVM_X86\narch/x86/kvm/Kconfig-21-\tdef_tristate KVM if (KVM_INTEL != n || KVM_AMD != n)\n--\narch/x86/kvm/Kconfig-51-\narch/x86/kvm/Kconfig:52:config KVM\narch/x86/kvm/Kconfig-53-\ttristate \"Kernel-based Virtual Machine (KVM) support\"\n--\narch/x86/kvm/Kconfig-68-\narch/x86/kvm/Kconfig:69:config KVM_WERROR\narch/x86/kvm/Kconfig-70-\tbool \"Compile KVM with -Werror\"\n--\narch/x86/kvm/Kconfig-82-\narch/x86/kvm/Kconfig:83:config KVM_SW_PROTECTED_VM\narch/x86/kvm/Kconfig-84-\tbool \"Enable support for KVM software-protected VMs\"\n--\narch/x86/kvm/Kconfig-95-\narch/x86/kvm/Kconfig:96:config KVM_INTEL\narch/x86/kvm/Kconfig-97-\ttristate \"KVM for Intel (and compatible) processors support\"\n--\narch/x86/kvm/Kconfig-106-\narch/x86/kvm/Kconfig:107:config KVM_INTEL_PROVE_VE\narch/x86/kvm/Kconfig-108-        bool \"Check that guests do not receive #VE exceptions\"\n--\narch/x86/kvm/Kconfig=122=config X86_SGX_KVM\n--\narch/x86/kvm/Kconfig-133-\narch/x86/kvm/Kconfig:134:config KVM_INTEL_TDX\narch/x86/kvm/Kconfig-135-\tbool \"Intel Trust Domain Extensions (TDX) support\"\n--\narch/x86/kvm/Kconfig-145-\narch/x86/kvm/Kconfig:146:config KVM_AMD\narch/x86/kvm/Kconfig-147-\ttristate \"KVM for AMD processors support\"\n--\narch/x86/kvm/Kconfig-155-\narch/x86/kvm/Kconfig:156:config KVM_AMD_SEV\narch/x86/kvm/Kconfig-157-\tbool \"AMD Secure Encrypted Virtualization (SEV) support\"\n--\narch/x86/kvm/Kconfig-172-\narch/x86/kvm/Kconfig:173:config KVM_IOAPIC\narch/x86/kvm/Kconfig-174-\tbool \"I/O APIC, PIC, and PIT emulation\"\n--\narch/x86/kvm/Kconfig-182-\narch/x86/kvm/Kconfig:183:config KVM_SMM\narch/x86/kvm/Kconfig-184-\tbool \"System Management Mode emulation\"\n--\narch/x86/kvm/Kconfig-193-\narch/x86/kvm/Kconfig:194:config KVM_HYPERV\narch/x86/kvm/Kconfig-195-\tbool \"Support for Microsoft Hyper-V emulation\"\n--\narch/x86/kvm/Kconfig-207-\narch/x86/kvm/Kconfig:208:config KVM_XEN\narch/x86/kvm/Kconfig-209-\tbool \"Support for Xen hypercall interface\"\n--\narch/x86/kvm/Kconfig-216-\narch/x86/kvm/Kconfig:217:config KVM_PROVE_MMU\narch/x86/kvm/Kconfig-218-\tbool \"Prove KVM MMU correctness\"\n--\narch/x86/kvm/Kconfig-229-\narch/x86/kvm/Kconfig:230:config KVM_EXTERNAL_WRITE_TRACKING\narch/x86/kvm/Kconfig-231-\tbool\narch/x86/kvm/Kconfig-232-\narch/x86/kvm/Kconfig:233:config KVM_MAX_NR_VCPUS\narch/x86/kvm/Kconfig-234-\tint \"Maximum number of vCPUs per KVM guest\"\n"
}

5/2 2026/09/01 04:38 llm patch-evaluator
0m Model:
gemini-3.7-flash

Args:
null

Results:
null

Tokens:
							input: 15569
							output: 37
							thoughts: 47
						

Thoughts:
**Examining KVM Configuration Options**

Okay, let's dive in. I need to get a clear picture of how KVM is configured in this x86 environment, specifically in relation to Xen and the Intel/AMD implementations. I'm going to take a look at `arch/x86/kvm/Kconfig` and focus on the interplay between `config KVM_XEN`, `config KVM`, and then the processor-specific options, `KVM_INTEL` and `KVM_AMD`. I need to understand the dependencies and how these options influence each other. Where does the Xen hypervisor fit in with a KVM setup here? And how do we choose between Intel and AMD virtualization extensions? Let's trace through these definitions and figure out the conditional logic. This should give me a good understanding of the overall KVM build strategy for this platform.



6/2 2026/09/01 04:38 tool read-file
0m Args:
{
  "File": "arch/x86/kvm/Kconfig",
  "FirstLine": 1,
  "LineCount": 80
}

Results:
{
  "Contents": "   1:\t# SPDX-License-Identifier: GPL-2.0\n   2:\t#\n   3:\t# KVM configuration\n   4:\t#\n   5:\t\n   6:\tsource \"virt/kvm/Kconfig\"\n   7:\t\n   8:\tmenuconfig VIRTUALIZATION\n   9:\t\tbool \"Virtualization\"\n  10:\t\tdefault y\n  11:\t\thelp\n  12:\t\t  Say Y here to get to see options for using your Linux host to run other\n  13:\t\t  operating systems inside virtual machines (guests).\n  14:\t\t  This option alone does not add any kernel code.\n  15:\t\n  16:\t\t  If you say N, all options in this submenu will be skipped and disabled.\n  17:\t\n  18:\tif VIRTUALIZATION\n  19:\t\n  20:\tconfig KVM_X86\n  21:\t\tdef_tristate KVM if (KVM_INTEL != n || KVM_AMD != n)\n  22:\t\tselect KVM_COMMON\n  23:\t\tselect KVM_ELIDE_TLB_FLUSH_IF_YOUNG\n  24:\t\tselect KVM_MMU_LOCKLESS_AGING\n  25:\t\tselect HAVE_KVM_IRQCHIP\n  26:\t\tselect HAVE_KVM_PFNCACHE\n  27:\t\tselect HAVE_KVM_DIRTY_RING_TSO\n  28:\t\tselect HAVE_KVM_DIRTY_RING_ACQ_REL\n  29:\t\tselect HAVE_KVM_IRQ_BYPASS\n  30:\t\tselect HAVE_KVM_IRQ_ROUTING\n  31:\t\tselect HAVE_KVM_READONLY_MEM\n  32:\t\tselect VHOST_TASK\n  33:\t\tselect KVM_ASYNC_PF\n  34:\t\tselect USER_RETURN_NOTIFIER\n  35:\t\tselect KVM_MMIO\n  36:\t\tselect SCHED_INFO\n  37:\t\tselect PERF_EVENTS\n  38:\t\tselect GUEST_PERF_EVENTS\n  39:\t\tselect PERF_GUEST_MEDIATED_PMU\n  40:\t\tselect HAVE_KVM_MSI\n  41:\t\tselect HAVE_KVM_CPU_RELAX_INTERCEPT\n  42:\t\tselect HAVE_KVM_NO_POLL\n  43:\t\tselect VIRT_XFER_TO_GUEST_WORK\n  44:\t\tselect KVM_GENERIC_DIRTYLOG_READ_PROTECT\n  45:\t\tselect KVM_VFIO\n  46:\t\tselect HAVE_KVM_PM_NOTIFIER if PM\n  47:\t\tselect KVM_GENERIC_HARDWARE_ENABLING\n  48:\t\tselect KVM_GENERIC_PRE_FAULT_MEMORY\n  49:\t\tselect KVM_WERROR if WERROR\n  50:\t\tselect KVM_GUEST_MEMFD if X86_64\n  51:\t\n  52:\tconfig KVM\n  53:\t\ttristate \"Kernel-based Virtual Machine (KVM) support\"\n  54:\t\tdepends on X86_LOCAL_APIC\n  55:\t\thelp\n  56:\t\t  Support hosting fully virtualized guest machines using hardware\n  57:\t\t  virtualization extensions.  You will need a fairly recent\n  58:\t\t  processor equipped with virtualization extensions. You will also\n  59:\t\t  need to select one or more of the processor modules below.\n  60:\t\n  61:\t\t  This module provides access to the hardware capabilities through\n  62:\t\t  a character device node named /dev/kvm.\n  63:\t\n  64:\t\t  To compile this as a module, choose M here: the module\n  65:\t\t  will be called kvm.\n  66:\t\n  67:\t\t  If unsure, say N.\n  68:\t\n  69:\tconfig KVM_WERROR\n  70:\t\tbool \"Compile KVM with -Werror\"\n  71:\t\t# Disallow KVM's -Werror if KASAN is enabled, e.g. to guard against\n  72:\t\t# randomized configs from selecting KVM_WERROR=y, which doesn't play\n  73:\t\t# nice with KASAN.  KASAN builds generates warnings for the default\n  74:\t\t# FRAME_WARN, i.e. KVM_WERROR=y with KASAN=y requires special tuning.\n  75:\t\t# Building KVM with -Werror and KASAN is still doable via enabling\n  76:\t\t# the kernel-wide WERROR=y.\n  77:\t\tdepends on KVM_X86 \u0026\u0026 ((EXPERT \u0026\u0026 !KASAN) || WERROR)\n  78:\t\thelp\n  79:\t\t  Add -Werror to the build flags for KVM.\n  80:\t\n"
}

7/2 2026/09/01 04:38 llm patch-evaluator
0m Model:
gemini-3.7-flash

Args:
null

Results:
null

Tokens:
							input: 17037
							output: 39
							thoughts: 35
						

Thoughts:
Here's my take on those lines in `arch/x86/kvm/Kconfig` concerning `KVM_XEN`:

**Examining KVM_XEN in arch/x86/kvm/Kconfig**

Alright, let's dive into lines 190-225 of `arch/x86/kvm/Kconfig`. I'm specifically looking for the configuration options related to `KVM_XEN`. I'm expecting to find the relevant dependencies, maybe some default values, and potentially a description of what enabling this option actually *does* in the context of KVM on x86. I'm assuming this section defines the interface or integration point between KVM and Xen, presumably allowing KVM to run as a guest inside a Xen hypervisor, which is a pretty common use-case. I'll be looking for any clues about whether it's enabled by default, what other features it might depend on (e.g., specific CPU features, virtualization extensions), and any caveats or warnings that might be included in the comments. I'll also try to understand if it's considered experimental or stable. Understanding this configuration is important, especially when dealing with nested virtualization or integrating different hypervisors within a single system. I'm keen to see if there is any mention of whether this is dependent on the `XEN` configuration option.



8/2 2026/09/01 04:38 tool read-file
0m Args:
{
  "File": "arch/x86/kvm/Kconfig",
  "FirstLine": 190,
  "LineCount": 40
}

Results:
{
  "Contents": " 190:\t\t  firmware to implement UEFI secure boot.\n 191:\t\n 192:\t\t  If unsure, say Y.\n 193:\t\n 194:\tconfig KVM_HYPERV\n 195:\t\tbool \"Support for Microsoft Hyper-V emulation\"\n 196:\t\tdepends on KVM_X86\n 197:\t\tdefault y\n 198:\t\thelp\n 199:\t\t  Provides KVM support for emulating Microsoft Hyper-V.  This allows KVM\n 200:\t\t  to expose a subset of the paravirtualized interfaces defined in the\n 201:\t\t  Hyper-V Hypervisor Top-Level Functional Specification (TLFS):\n 202:\t\t  https://docs.microsoft.com/en-us/virtualization/hyper-v-on-windows/reference/tlfs\n 203:\t\t  These interfaces are required for the correct and performant functioning\n 204:\t\t  of Windows and Hyper-V guests on KVM.\n 205:\t\n 206:\t\t  If unsure, say \"Y\".\n 207:\t\n 208:\tconfig KVM_XEN\n 209:\t\tbool \"Support for Xen hypercall interface\"\n 210:\t\tdepends on KVM_X86\n 211:\t\thelp\n 212:\t\t  Provides KVM support for the hosting Xen HVM guests and\n 213:\t\t  passing Xen hypercalls to userspace.\n 214:\t\n 215:\t\t  If in doubt, say \"N\".\n 216:\t\n 217:\tconfig KVM_PROVE_MMU\n 218:\t\tbool \"Prove KVM MMU correctness\"\n 219:\t\tdepends on DEBUG_KERNEL\n 220:\t\tdepends on KVM_X86\n 221:\t\tdepends on EXPERT\n 222:\t\thelp\n 223:\t\t  Enables runtime assertions in KVM's MMU that are too costly to enable\n 224:\t\t  in anything remotely resembling a production environment, e.g. this\n 225:\t\t  gates code that verifies a to-be-freed page table doesn't have any\n 226:\t\t  present SPTEs.\n 227:\t\n 228:\t\t  If in doubt, say \"N\".\n 229:\t\n"
}

9/2 2026/09/01 04:38 llm patch-evaluator
0m Model:
gemini-3.7-flash

Args:
null

Results:
null

Tokens:
							input: 18009
							output: 37
							thoughts: 36
						

Thoughts:
Okay, let's dive into this. Here's my thought process on this specific code snippet:

**Investigating KVM Compilation on x86**

Right, so we're looking at KVM and its interaction with Xen.  We already understand some of the basics, but it's time to get into the nitty-gritty. I need to understand how `xen.o` and `pfncache.o` are actually compiled within the x86 architecture's KVM context.  Knowing the exact build process will give me insight into their dependencies, any special compiler flags, and how they ultimately integrate.  Therefore, my next step is clear: I need to open and carefully analyze the `arch/x86/kvm/Makefile`.  That's where the compilation rules for these objects will be defined, and it's the key to truly understanding their build process. This should provide the low level information I need to proceed.



10/2 2026/09/01 04:39 tool grepper
0m Args:
{
  "Expression": "xen.o|pfncache.o",
  "PathPrefix": "arch/x86/kvm"
}

Results:
{
  "Output": "arch/x86/kvm/Makefile=13=kvm-$(CONFIG_KVM_HYPERV) += hyperv.o\narch/x86/kvm/Makefile:14:kvm-$(CONFIG_KVM_XEN)\t+= xen.o\narch/x86/kvm/Makefile-15-kvm-$(CONFIG_KVM_SMM)\t+= smm.o\n--\narch/x86/kvm/xen.c-24-#include \u003cxen/interface/sched.h\u003e\narch/x86/kvm/xen.c:25:#include \u003cxen/xen-ops.h\u003e\narch/x86/kvm/xen.c-26-\n"
}

11/2 2026/09/01 04:39 llm patch-evaluator
0m Model:
gemini-3.7-flash

Args:
null

Results:
null

Tokens:
							input: 18467
							output: 29
							thoughts: 44
						

Thoughts:
Okay, let's see... I need to figure out the build process for `pfncache.c` and `kvm_main.c` within the KVM virtualization module. My initial thought is to check the relevant Makefiles and Kconfig files. Specifically, I should look at `virt/kvm/Makefile` and `virt/kvm/Kconfig`. Those are typically where the build rules and configuration options reside for KVM-specific source files. Hopefully, this will shed some light on dependencies, build flags, and any conditional compilation involved in getting those two files compiled and linked. That should be a good starting point to trace the build process, so let me dive in there.



12/2 2026/09/01 04:39 tool grepper
0m Args:
{
  "Expression": "pfncache.o",
  "PathPrefix": "virt/kvm"
}

Results:
{
  "Output": "virt/kvm/Makefile.kvm=13=kvm-$(CONFIG_HAVE_KVM_DIRTY_RING) += $(KVM)/dirty_ring.o\nvirt/kvm/Makefile.kvm:14:kvm-$(CONFIG_HAVE_KVM_PFNCACHE) += $(KVM)/pfncache.o\nvirt/kvm/Makefile.kvm-15-kvm-$(CONFIG_KVM_GUEST_MEMFD) += $(KVM)/guest_memfd.o\n"
}

13/2 2026/09/01 04:39 llm patch-evaluator
0m Model:
gemini-3.7-flash

Args:
null

Results:
null

Tokens:
							input: 18818
							output: 44
							thoughts: 704
						

Thoughts:
**Analyzing KVM-Xen Integration Configuration and Code**

Okay, let's break this down. First, I need to understand the required configurations. It looks like we're dealing with `KVM`, and potentially `KVM_INTEL` or `KVM_AMD`, along with `KVM_X86`.  Crucially, there's `KVM_XEN`, which depends on `KVM_X86`.  I see that `KVM_X86` is automatically selected if any of `KVM_INTEL`, `KVM_AMD`, or just `KVM` are defined. Specifically, `config KVM_XEN` is a bool option, and it's under `if VIRTUALIZATION`. So I need both `KVM` and `KVM_XEN` (as well as `KVM_INTEL` or `KVM_AMD`).

Now, onto the code changes. I'll need to see how these configurations are used. I see a patch modifying symbols across a few files. Let's look at `arch/x86/kvm/xen.c`. There are quite a few changes here. I'll need to examine the function names to understand the impact. I see a mix of static and non-static functions being altered.

It looks like this affects a range of Xen-specific functionalities: shared information initialization, updating guest runstate, injecting events, checking for interrupts, setting VCPU attributes, completing hypercalls, scheduling operations, handling event channels, and VM initialization/destruction. The non-static functions are particularly interesting because they are the entry points for the various functionalities being changed. The static functions will be useful to understand how and where the functions are used.

Then there's a smaller change in `virt/kvm/kvm_main.c` where a static function related to MMU invalidation is being modified. Finally, there are also some changes in `virt/kvm/pfncache.c`. Specifically, `hva_to_pfn_retry` is static, which is called in `kvm_gpc_refresh` and `kvm_gpc_activate`. Now I need to check the non-static entry points in `pfncache.c`. This could be important for performance. I need to figure out what's causing the retry condition and why it's necessary.



14/2 2026/09/01 04:39 tool grepper
0m Args:
{
  "Expression": "^int kvm_gpc|^void kvm_gpc",
  "PathPrefix": "virt/kvm/pfncache.c"
}

Results:
{
  "Output": "virt/kvm/pfncache.c=256=static int __kvm_gpc_refresh(struct gfn_to_pfn_cache *gpc, gpa_t gpa, unsigned long uhva)\n--\nvirt/kvm/pfncache.c-365-\nvirt/kvm/pfncache.c:366:int kvm_gpc_refresh(struct gfn_to_pfn_cache *gpc, unsigned long len)\nvirt/kvm/pfncache.c-367-{\n--\nvirt/kvm/pfncache.c-384-\nvirt/kvm/pfncache.c:385:void kvm_gpc_init(struct gfn_to_pfn_cache *gpc, struct kvm *kvm)\nvirt/kvm/pfncache.c-386-{\n--\nvirt/kvm/pfncache.c=397=static int __kvm_gpc_activate(struct gfn_to_pfn_cache *gpc, gpa_t gpa, unsigned long uhva,\n--\nvirt/kvm/pfncache.c-426-\nvirt/kvm/pfncache.c:427:int kvm_gpc_activate(struct gfn_to_pfn_cache *gpc, gpa_t gpa, unsigned long len)\nvirt/kvm/pfncache.c-428-{\n--\nvirt/kvm/pfncache.c-438-\nvirt/kvm/pfncache.c:439:int kvm_gpc_activate_hva(struct gfn_to_pfn_cache *gpc, unsigned long uhva, unsigned long len)\nvirt/kvm/pfncache.c-440-{\n--\nvirt/kvm/pfncache.c-446-\nvirt/kvm/pfncache.c:447:void kvm_gpc_deactivate(struct gfn_to_pfn_cache *gpc)\nvirt/kvm/pfncache.c-448-{\n"
}

15/2 2026/09/01 04:39 llm patch-evaluator
0m Model:
gemini-3.7-flash

Args:
null

Results:
null

Tokens:
							input: 20598
							output: 42
							thoughts: 185
						

Thoughts:
**Analyzing Potential Interactions Between KVM and Xen in the x86 Architecture**

Okay, let's dive in. I need to get a handle on how KVM interacts with Xen, specifically in the x86 architecture. I'll start by checking the non-static functions in `arch/x86/kvm/xen.c` and `virt/kvm/pfncache.c`. Seems like the first file is the more likely candidate given the naming.

In `arch/x86/kvm/xen.c`, the following functions stand out as potentially relevant to the integration:

*   `kvm_xen_inject_pending_events`: This is probably related to event injection from the Xen hypervisor into the KVM guest.
*   `__kvm_xen_has_interrupt`: This suggests a mechanism for querying if there are pending interrupts related to Xen.
*   `kvm_xen_vcpu_set_attr`: Sounds like setting vCPU attributes through Xen.
*   `kvm_xen_hypercall`: This likely deals with Xen hypercalls, a fundamental interface for communication.
*   `kvm_xen_set_evtchn_fast`: This looks like an optimization for setting event channels.
*   `kvm_xen_setup_evtchn`: This is probably the setup routine for these channels.
*   `kvm_xen_hvm_evtchn_send`: Definitely sounds related to sending events through the HVM event channel.
*   `kvm_xen_hvm_set_attr`: This is probably setting HVM attributes through Xen. It also calls `kvm_xen_setattr_evtchn`, so I need to check how the event channel attributes are being set.

I'll keep a close eye on these functions. I need to understand how they work together, specifically looking for any potential synchronization issues or inefficiencies in the interface between KVM and Xen. I will need to look into `pfncache.c` as well to see how it might be used to optimize the process.



16/2 2026/09/01 04:39 tool grepper
0m Args:
{
  "Expression": "kvm_xen_setattr_evtchn",
  "PathPrefix": "arch/x86/kvm/xen.c"
}

Results:
{
  "Output": "arch/x86/kvm/xen.c=33=static int kvm_xen_set_evtchn(struct kvm_xen_evtchn *xe, struct kvm *kvm);\narch/x86/kvm/xen.c:34:static int kvm_xen_setattr_evtchn(struct kvm *kvm, struct kvm_xen_hvm_attr *data);\narch/x86/kvm/xen.c-35-static bool kvm_xen_hcall_evtchn_send(struct kvm_vcpu *vcpu, u64 param, u64 *r);\n--\narch/x86/kvm/xen.c=775=int kvm_xen_hvm_set_attr(struct kvm *kvm, struct kvm_xen_hvm_attr *data)\n--\narch/x86/kvm/xen.c-851-\tcase KVM_XEN_ATTR_TYPE_EVTCHN:\narch/x86/kvm/xen.c:852:\t\tr = kvm_xen_setattr_evtchn(kvm, data);\narch/x86/kvm/xen.c-853-\t\tbreak;\n--\narch/x86/kvm/xen.c=2272=static int kvm_xen_eventfd_reset(struct kvm *kvm)\n--\narch/x86/kvm/xen.c-2313-\narch/x86/kvm/xen.c:2314:static int kvm_xen_setattr_evtchn(struct kvm *kvm, struct kvm_xen_hvm_attr *data)\narch/x86/kvm/xen.c-2315-{\n"
}

17/2 2026/09/01 04:39 llm patch-evaluator
0m Model:
gemini-3.7-flash

Args:
null

Results:
null

Tokens:
							input: 21709
							output: 306
							thoughts: 1540
						

Thoughts:
**Analysis of KVM-Xen Changes for Fuzzing**

Okay, let's break this down. I see that `kvm_xen_hvm_set_attr` is an exported function called via the `KVM_XEN_HVM_SET_ATTR` ioctl. Similarly, `kvm_xen_vcpu_set_attr` handles the `KVM_XEN_VCPU_SET_ATTR` ioctl, and `kvm_xen_hypercall` deals with guest Xen hypercalls. We also have `kvm_gpc_refresh`, `kvm_gpc_activate`, and `kvm_gpc_activate_hva` in `pfncache.c` that are important.

I'll start by taking a closer look at key functions like `kvm_xen_hvm_evtchn_send`, `kvm_xen_set_evtchn_fast`, `kvm_xen_setup_evtchn`, `kvm_xen_inject_pending_events`, `__kvm_xen_has_interrupt`, `kvm_xen_vcpu_set_attr`, and `kvm_xen_hypercall`. I'll also glance over the other functions that may be modified. It's crucial to understand the scope of the modifications.

Now, let's dive into this diff. First, in `arch/x86/kvm/xen.c`, there's a lot of work related to the shared info structure and its 64-bit status.  Crucially, there are changes to how interrupts are injected, including atomic operations. This is a red flag. I see the use of atomic operations (`atomic64_or`, `atomic_or`, `atomic64_andnot`, `atomic_andnot`) instead of inline assembly. This suggests potential concurrency issues related to interrupt injection that *could* be exploited.  There's also alignment checks in `kvm_xen_vcpu_set_attr`. I'll need to pay close attention to GPA and HVA alignment, and the implications for fuzzing. The renaming of `longmode` to `is_64bit` is less concerning.  The `kvm_xen_set_evtchn_fast` change, replacing the bit operation with `btsl`, to avoid the split-lock #AC is noteworthy; potential for unaligned accesses to cause problems. The shift from `idr` to `xarray` for `evtchn_ports` also warrants a closer look as it touches critical event channel management.

In `virt/kvm/kvm_main.c`, I notice changes related to `kvm_mmu_notifier_invalidate_range_end`, specifically the addition of `gpc_invalidate_seq` and `smp_wmb()`. This indicates a potential synchronization point. In `virt/kvm/pfncache.c`, the use of `gpc_seq` and `kvm->gpc_invalidate_seq` in `mmu_notifier_retry_cache` and `hva_to_pfn_retry` indicates there has been a global page cache invalidation sequence added. I'll need to check how these sequences are maintained across CPUs.

The core of these changes is in KVM's Xen emulation and the pfncache, which are *prime* targets for fuzzing. This touches concurrency, race conditions, memory corruption, locking, ioctl handling, and hypercalls. Definitely, worthwhile.

The configuration options (`KVM`, `KVM_INTEL`/`KVM_AMD`, `KVM_X86`, `KVM_XEN`) are all relevant and need to be enabled during fuzzing.

My list of focus symbols will be: `kvm_xen_vcpu_set_attr`, `kvm_xen_hvm_set_attr`, `kvm_xen_hypercall`, `kvm_xen_inject_pending_events`, `__kvm_xen_has_interrupt`, `kvm_xen_set_evtchn_fast`, `kvm_xen_hvm_evtchn_send`, `kvm_gpc_refresh`, `kvm_gpc_activate`, and `kvm_gpc_activate_hva`.

I'm confident we have a solid set of targets for fuzzing, and I'm ready to proceed with setting up the fuzzing environment.



18/2 2026/09/01 04:39 tool set-results
0m Args:
{
  "EnableConfigs": [
    "KVM",
    "KVM_INTEL",
    "KVM_AMD",
    "KVM_X86",
    "KVM_XEN"
  ],
  "FocusSymbols": [
    "kvm_xen_vcpu_set_attr",
    "kvm_xen_hvm_set_attr",
    "kvm_xen_hypercall",
    "kvm_xen_inject_pending_events",
    "__kvm_xen_has_interrupt",
    "kvm_xen_set_evtchn_fast",
    "kvm_xen_hvm_evtchn_send",
    "kvm_gpc_refresh",
    "kvm_gpc_activate"
  ],
  "Reasoning": "The patch makes functional and concurrency-critical changes to KVM's Xen emulation and gfn_to_pfn_cache (GPC) subsystem. These include migrating Xen event channel storage from IDR to XArray, enforcing alignment checks for vCPU info GPA/HVA, using explicit 32-bit atomic bitops to prevent split-lock AC exceptions on unaligned structures, introducing SRCU locking in __kvm_xen_has_interrupt, and separating GPC invalidation sequencing from general MMU invalidation sequencing. These changes affect reachable virtualization ioctls, hypercalls, and event channel delivery mechanisms.",
  "WorthFuzzing": true
}

Results:
{
  "EnableConfigs": [
    "KVM",
    "KVM_INTEL",
    "KVM_AMD",
    "KVM_X86",
    "KVM_XEN"
  ],
  "FocusSymbols": [
    "kvm_xen_vcpu_set_attr",
    "kvm_xen_hvm_set_attr",
    "kvm_xen_hypercall",
    "kvm_xen_inject_pending_events",
    "__kvm_xen_has_interrupt",
    "kvm_xen_set_evtchn_fast",
    "kvm_xen_hvm_evtchn_send",
    "kvm_gpc_refresh",
    "kvm_gpc_activate"
  ],
  "Reasoning": "The patch makes functional and concurrency-critical changes to KVM's Xen emulation and gfn_to_pfn_cache (GPC) subsystem. These include migrating Xen event channel storage from IDR to XArray, enforcing alignment checks for vCPU info GPA/HVA, using explicit 32-bit atomic bitops to prevent split-lock AC exceptions on unaligned structures, introducing SRCU locking in __kvm_xen_has_interrupt, and separating GPC invalidation sequencing from general MMU invalidation sequencing. These changes affect reachable virtualization ioctls, hypercalls, and event channel delivery mechanisms.",
  "WorthFuzzing": true
}

19/1 2026/09/01 04:39 action If
0m Args:
{
  "WorthFuzzing": true
}

Results:
null

20/2 2026/09/01 04:39 agent kmsan-evaluator
0m Model:
core

Args:
null

Results:
{
  "KMSANReasoning": "The patch modifies KVM's Xen emulation and GFN-to-PFN cache invalidation logic. Specifically, it:\n1. Replaces the IDR data structure with XArray for event channel port tracking (`evtchn_ports`).\n2. Introduces a dedicated sequence counter (`gpc_invalidate_seq`) in `pfncache.c` and `kvm_main.c` to properly track GPC invalidation events.\n3. Fixes potential split-lock `#AC` exceptions by checking GPA/HVA 32-bit alignment in `kvm_xen_vcpu_set_attr` and using 32-bit atomic bitops for `vi_pending_sel`.\n4. Fixes SRCU locking in `__kvm_xen_has_interrupt` and atomic reads/writes for `poll_evtchn`.\n\nNone of these changes allocate uninitialized buffers, read uninitialized stack/heap memory, or expose kernel info-leaks to userspace. Memory safety and concurrency concerns related to these changes (such as locking, use-after-free, or out-of-bounds accesses) are covered by KASAN and LOCKDEP. Therefore, dedicated KMSAN fuzzing is not needed.",
  "NeedsKMSAN": false
}

Instruction:
You are an expert Linux kernel security engineer specializing in kernel memory
error detectors (KASAN and KMSAN). Your job is to review the provided patch series and
determine if the code changes justify spawning a dedicated KMSAN (KernelMemorySanitizer)
fuzzing session in addition to standard KASAN fuzzing.

CRITICAL DISTINCTION BETWEEN KASAN AND KMSAN:
- Standard KASAN kernel builds (upstream-apparmor-kasan.config) already enable
  a comprehensive suite of debugging tools and sanitizers, including KASAN
  (out-of-bounds accesses, use-after-free, double free, invalid free), LOCKDEP
  (locking bugs and deadlocks), UB-sanitizers, and memory corruption checks.
- KMSAN (KernelMemorySanitizer) detects reads of UNINITIALIZED memory (stack, heap,
  or page allocations) and kernel-to-user memory info-leaks.

Rule: THERE IS NO SENSE IN RUNNING A KMSAN SESSION IF A BUG CAN BE CAUGHT BY KASAN,
LOCKDEP, OR OTHER STANDARD BUG DETECTORS.
A dedicated KMSAN fuzzing session incurs significant resource costs. You must ONLY
set NeedsKMSAN=true if the code changes introduce or expose UNINITIALIZED MEMORY risks
that are detected ONLY by KMSAN.

Look holistically at the patch series and surrounding code. Even if no direct
uninitialized field accesses or new buffer allocations are added in the diff itself,
a patch may alter control flow, bounds checking, or data length calculations in ways
that change how the rest of the code operates on existing buffers (e.g. allowing
uninitialized stack/heap memory to be read, copied to user space, or used in control
flow). Do not hesitate to use your code access tools to inspect the surrounding code,
called functions, and callers.

Set NeedsKMSAN=true ONLY IF the patch introduces or modifies:
1. Kernel structures sent to user space (via copy_to_user, put_user, netlink skb
   attributes, ioctl output arguments, socket options, or BPF buffers) where fields
   or structure padding might not be fully initialized/zeroed.
2. Conditional logic or branching that depends on potentially uninitialized variables
   or struct fields.
3. Allocation or initialization of complex data structures where uninitialized fields
   could be read later in reachable code paths.
4. Bounds checks, lengths, or logic in a way that allows surrounding code to access
   uninitialized bytes of existing buffers.

Set NeedsKMSAN=false IF:
- The code changes primarily risk out-of-bounds access, array overflows, NULL pointer
  dereferences, locking deadlocks, or use-after-free bugs (these are already caught
  by KASAN, LOCKDEP, or standard bug detectors).
- All stack/heap structures touched or introduced by the patch are fully zeroed
  or initialized (e.g. using = {0}, memset, kzalloc) before being read or copied.
- The patch does not introduce any risk of uninitialized memory usage or info-leaks.

Use your code access tools to inspect the surrounding code if necessary, then provide
detailed KMSANReasoning contrasting KASAN vs KMSAN applicability for this patch.
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:
For your convenience, here is the diff of the changes:
commit c3e03ff3b8c379d8b32820c99ebc02d802e303b2
Author: syz-cluster <triage@syzkaller.com>
Date:   Tue Sep 1 04:38:22 2026 +0000

    syz-cluster: applied patch under review

diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index 683bb8bf43a94..3af7395c2430a 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -16,6 +16,7 @@
 #include <linux/irq_work.h>
 #include <linux/irq.h>
 #include <linux/workqueue.h>
+#include <linux/xarray.h>
 
 #include <linux/kvm.h>
 #include <linux/kvm_para.h>
@@ -1113,7 +1114,7 @@ struct kvm_xen {
 	bool runstate_update_flag;
 	u8 upcall_vector;
 	struct gfn_to_pfn_cache shinfo_cache;
-	struct idr evtchn_ports;
+	struct xarray evtchn_ports;
 	unsigned long poll_mask[BITS_TO_LONGS(KVM_MAX_VCPUS)];
 
 	struct kvm_xen_hvm_config hvm_config;
diff --git a/arch/x86/kvm/xen.c b/arch/x86/kvm/xen.c
index 998cdcb6aa303..718396340d3c1 100644
--- a/arch/x86/kvm/xen.c
+++ b/arch/x86/kvm/xen.c
@@ -73,7 +73,7 @@ static int kvm_xen_shared_info_init(struct kvm *kvm)
 	BUILD_BUG_ON(offsetof(struct shared_info, wc) != 0xc00);
 	BUILD_BUG_ON(offsetof(struct shared_info, wc_sec_hi) != 0xc0c);
 
-	if (IS_ENABLED(CONFIG_64BIT) && kvm->arch.xen.long_mode) {
+	if (kvm_xen_has_64bit_shinfo(kvm)) {
 		struct shared_info *shinfo = gpc->khva;
 
 		wc_sec_hi = &shinfo->wc_sec_hi;
@@ -389,7 +389,7 @@ static void kvm_xen_update_runstate_guest(struct kvm_vcpu *v, bool atomic)
 	BUILD_BUG_ON(sizeof_field(struct vcpu_runstate_info, time) !=
 		     sizeof(vx->runstate_times));
 
-	if (IS_ENABLED(CONFIG_64BIT) && v->kvm->arch.xen.long_mode) {
+	if (kvm_xen_has_64bit_shinfo(v->kvm)) {
 		user_len = sizeof(struct vcpu_runstate_info);
 		times_ofs = offsetof(struct vcpu_runstate_info,
 				     state_entry_time);
@@ -676,28 +676,32 @@ void kvm_xen_inject_pending_events(struct kvm_vcpu *v)
 	}
 
 	/* Now gpc->khva is a valid kernel address for the vcpu_info */
-	if (IS_ENABLED(CONFIG_64BIT) && v->kvm->arch.xen.long_mode) {
+	if (kvm_xen_has_64bit_shinfo(v->kvm)) {
 		struct vcpu_info *vi = gpc->khva;
+		void *vi_pending_sel = &vi->evtchn_pending_sel;
 
-		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));
+		if (IS_ALIGNED((unsigned long)vi_pending_sel, sizeof(u64))) {
+			atomic64_or(evtchn_pending_sel, vi_pending_sel);
+		} else {
+			atomic_or(evtchn_pending_sel, vi_pending_sel);
+			/*
+			 * The cast keeps the shift well-defined on 32-bit,
+			 * where evtchn_pending_sel is 32 bits wide and this
+			 * branch is unreachable anyway (this is inside
+			 * kvm_xen_has_64bit_shinfo(), which is gated on
+			 * IS_ENABLED(CONFIG_64BIT)).
+			 */
+			atomic_or((u64)evtchn_pending_sel >> 32,
+				  vi_pending_sel + 4);
+		}
+
+		atomic64_andnot(evtchn_pending_sel, (void *)&v->arch.xen.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));
+		atomic_or(evtchn_pending_sel, (void *)&vi->evtchn_pending_sel);
+		atomic_andnot(evtchn_pending_sel, (void *)&v->arch.xen.evtchn_pending_sel);
 		WRITE_ONCE(vi->evtchn_upcall_pending, 1);
 	}
 
@@ -728,6 +732,16 @@ int __kvm_xen_has_interrupt(struct kvm_vcpu *v)
 	BUILD_BUG_ON(sizeof(rc) !=
 		     sizeof_field(struct compat_vcpu_info, evtchn_upcall_pending));
 
+	/*
+	 * kvm_gpc_check() checks the memslot generation, so kvm->srcu must be
+	 * held. Most callers hold it already, but this is also reached from
+	 * kvm_emulate_halt() on the VM-Exit path and from kvm_vcpu_block(),
+	 * where vcpu_enter_guest() has already dropped the vCPU's SRCU lock.
+	 * Taking SRCU does not sleep, so it is safe even in the atomic case
+	 * which is handled below.
+	 */
+	guard(srcu)(&v->kvm->srcu);
+
 	read_lock_irqsave(&gpc->lock, flags);
 	while (!kvm_gpc_check(gpc, sizeof(struct vcpu_info))) {
 		read_unlock_irqrestore(&gpc->lock, flags);
@@ -941,6 +955,10 @@ int kvm_xen_vcpu_set_attr(struct kvm_vcpu *vcpu, struct kvm_xen_vcpu_attr *data)
 				break;
 			}
 
+			r = -ENXIO;
+			if (!IS_ALIGNED(data->u.gpa, sizeof(u32)))
+				break;
+
 			r = kvm_gpc_activate(&vcpu->arch.xen.vcpu_info_cache,
 					     data->u.gpa, sizeof(struct vcpu_info));
 		} else {
@@ -950,6 +968,10 @@ int kvm_xen_vcpu_set_attr(struct kvm_vcpu *vcpu, struct kvm_xen_vcpu_attr *data)
 				break;
 			}
 
+			r = -ENXIO;
+			if (!IS_ALIGNED(data->u.hva, sizeof(u32)))
+				break;
+
 			r = kvm_gpc_activate_hva(&vcpu->arch.xen.vcpu_info_cache,
 						 data->u.hva, sizeof(struct vcpu_info));
 		}
@@ -993,7 +1015,7 @@ int kvm_xen_vcpu_set_attr(struct kvm_vcpu *vcpu, struct kvm_xen_vcpu_attr *data)
 		 * address, that's actually OK. kvm_xen_update_runstate_guest()
 		 * will cope.
 		 */
-		if (IS_ENABLED(CONFIG_64BIT) && vcpu->kvm->arch.xen.long_mode)
+		if (kvm_xen_has_64bit_shinfo(vcpu->kvm))
 			sz = sizeof(struct vcpu_runstate_info);
 		else
 			sz = sizeof(struct compat_vcpu_runstate_info);
@@ -1439,16 +1461,21 @@ static int kvm_xen_hypercall_complete_userspace(struct kvm_vcpu *vcpu)
 	return kvm_xen_hypercall_set_result(vcpu, run->xen.u.hcall.result);
 }
 
-static inline int max_evtchn_port(struct kvm *kvm)
+static inline int max_evtchn_port(bool has_64bit_shinfo)
 {
-	if (IS_ENABLED(CONFIG_64BIT) && kvm->arch.xen.long_mode)
+	if (has_64bit_shinfo)
 		return EVTCHN_2L_NR_CHANNELS;
 	else
 		return COMPAT_EVTCHN_2L_NR_CHANNELS;
 }
 
-static bool wait_pending_event(struct kvm_vcpu *vcpu, int nr_ports,
-			       evtchn_port_t *ports)
+static inline int kvm_max_evtchn_port(struct kvm *kvm)
+{
+	return max_evtchn_port(kvm_xen_has_64bit_shinfo(kvm));
+}
+
+static bool wait_pending_event(struct kvm_vcpu *vcpu, bool has_64bit_shinfo,
+			       int nr_ports, evtchn_port_t *ports)
 {
 	struct kvm *kvm = vcpu->kvm;
 	struct gfn_to_pfn_cache *gpc = &kvm->arch.xen.shinfo_cache;
@@ -1463,7 +1490,7 @@ static bool wait_pending_event(struct kvm_vcpu *vcpu, int nr_ports,
 		goto out_rcu;
 
 	ret = false;
-	if (IS_ENABLED(CONFIG_64BIT) && kvm->arch.xen.long_mode) {
+	if (has_64bit_shinfo) {
 		struct shared_info *shinfo = gpc->khva;
 		pending_bits = (unsigned long *)&shinfo->evtchn_pending;
 	} else {
@@ -1485,9 +1512,10 @@ static bool wait_pending_event(struct kvm_vcpu *vcpu, int nr_ports,
 	return ret;
 }
 
-static bool kvm_xen_schedop_poll(struct kvm_vcpu *vcpu, bool longmode,
+static bool kvm_xen_schedop_poll(struct kvm_vcpu *vcpu, bool is_64bit,
 				 u64 param, u64 *r)
 {
+	bool has_64bit_shinfo = kvm_xen_has_64bit_shinfo(vcpu->kvm);
 	struct sched_poll sched_poll;
 	evtchn_port_t port, *ports;
 	struct x86_exception e;
@@ -1497,7 +1525,7 @@ static bool kvm_xen_schedop_poll(struct kvm_vcpu *vcpu, bool longmode,
 	    !(vcpu->kvm->arch.xen.hvm_config.flags & KVM_XEN_HVM_CONFIG_EVTCHN_SEND))
 		return false;
 
-	if (IS_ENABLED(CONFIG_64BIT) && !longmode) {
+	if (IS_ENABLED(CONFIG_64BIT) && !is_64bit) {
 		struct compat_sched_poll sp32;
 
 		/* Sanity check that the compat struct definition is correct */
@@ -1546,20 +1574,20 @@ static bool kvm_xen_schedop_poll(struct kvm_vcpu *vcpu, bool longmode,
 	}
 
 	for (i = 0; i < sched_poll.nr_ports; i++) {
-		if (ports[i] >= max_evtchn_port(vcpu->kvm)) {
+		if (ports[i] >= max_evtchn_port(has_64bit_shinfo)) {
 			*r = -EINVAL;
 			goto out;
 		}
 	}
 
 	if (sched_poll.nr_ports == 1)
-		vcpu->arch.xen.poll_evtchn = port;
+		WRITE_ONCE(vcpu->arch.xen.poll_evtchn, port);
 	else
-		vcpu->arch.xen.poll_evtchn = -1;
+		WRITE_ONCE(vcpu->arch.xen.poll_evtchn, -1);
 
 	set_bit(vcpu->vcpu_idx, vcpu->kvm->arch.xen.poll_mask);
 
-	if (!wait_pending_event(vcpu, sched_poll.nr_ports, ports)) {
+	if (!wait_pending_event(vcpu, has_64bit_shinfo, sched_poll.nr_ports, ports)) {
 		kvm_set_mp_state(vcpu, KVM_MP_STATE_HALTED);
 
 		if (sched_poll.timeout)
@@ -1574,7 +1602,7 @@ static bool kvm_xen_schedop_poll(struct kvm_vcpu *vcpu, bool longmode,
 		kvm_set_mp_state(vcpu, KVM_MP_STATE_RUNNABLE);
 	}
 
-	vcpu->arch.xen.poll_evtchn = 0;
+	WRITE_ONCE(vcpu->arch.xen.poll_evtchn, 0);
 	*r = 0;
 out:
 	/* Really, this is only needed in case of timeout */
@@ -1594,12 +1622,12 @@ static void cancel_evtchn_poll(struct timer_list *t)
 	kvm_vcpu_kick(vcpu);
 }
 
-static bool kvm_xen_hcall_sched_op(struct kvm_vcpu *vcpu, bool longmode,
+static bool kvm_xen_hcall_sched_op(struct kvm_vcpu *vcpu, bool is_64bit,
 				   int cmd, u64 param, u64 *r)
 {
 	switch (cmd) {
 	case SCHEDOP_poll:
-		if (kvm_xen_schedop_poll(vcpu, longmode, param, r))
+		if (kvm_xen_schedop_poll(vcpu, is_64bit, param, r))
 			return true;
 		fallthrough;
 	case SCHEDOP_yield:
@@ -1618,7 +1646,7 @@ struct compat_vcpu_set_singleshot_timer {
     uint32_t flags;
 } __attribute__((packed));
 
-static bool kvm_xen_hcall_vcpu_op(struct kvm_vcpu *vcpu, bool longmode, int cmd,
+static bool kvm_xen_hcall_vcpu_op(struct kvm_vcpu *vcpu, bool is_64bit, int cmd,
 				  int vcpu_id, u64 param, u64 *r)
 {
 	struct vcpu_set_singleshot_timer oneshot;
@@ -1662,7 +1690,7 @@ static bool kvm_xen_hcall_vcpu_op(struct kvm_vcpu *vcpu, bool longmode, int cmd,
 		BUILD_BUG_ON(sizeof_field(struct compat_vcpu_set_singleshot_timer, flags) !=
 			     sizeof_field(struct vcpu_set_singleshot_timer, flags));
 
-		if (kvm_read_guest_virt(vcpu, param, &oneshot, longmode ? sizeof(oneshot) :
+		if (kvm_read_guest_virt(vcpu, param, &oneshot, is_64bit ? sizeof(oneshot) :
 					sizeof(struct compat_vcpu_set_singleshot_timer), &e)) {
 			*r = -EFAULT;
 			return true;
@@ -1694,7 +1722,7 @@ static bool kvm_xen_hcall_set_timer_op(struct kvm_vcpu *vcpu, uint64_t timeout,
 
 int kvm_xen_hypercall(struct kvm_vcpu *vcpu)
 {
-	bool longmode;
+	bool is_64bit;
 	u64 input, params[6], r = -ENOSYS;
 	bool handled = false;
 	u8 cpl;
@@ -1704,8 +1732,8 @@ int kvm_xen_hypercall(struct kvm_vcpu *vcpu)
 	    kvm_hv_hypercall_enabled(vcpu))
 		return kvm_hv_hypercall(vcpu);
 
-	longmode = is_64_bit_hypercall(vcpu);
-	if (!longmode) {
+	is_64bit = is_64_bit_hypercall(vcpu);
+	if (!is_64bit) {
 		input = kvm_eax_read(vcpu);
 		params[0] = kvm_ebx_read(vcpu);
 		params[1] = kvm_ecx_read(vcpu);
@@ -1751,17 +1779,17 @@ int kvm_xen_hypercall(struct kvm_vcpu *vcpu)
 			handled = kvm_xen_hcall_evtchn_send(vcpu, params[1], &r);
 		break;
 	case __HYPERVISOR_sched_op:
-		handled = kvm_xen_hcall_sched_op(vcpu, longmode, params[0],
+		handled = kvm_xen_hcall_sched_op(vcpu, is_64bit, params[0],
 						 params[1], &r);
 		break;
 	case __HYPERVISOR_vcpu_op:
-		handled = kvm_xen_hcall_vcpu_op(vcpu, longmode, params[0], params[1],
+		handled = kvm_xen_hcall_vcpu_op(vcpu, is_64bit, 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)
+		if (!is_64bit)
 			timeout |= params[1] << 32;
 		handled = kvm_xen_hcall_set_timer_op(vcpu, timeout, &r);
 		break;
@@ -1776,7 +1804,7 @@ int kvm_xen_hypercall(struct kvm_vcpu *vcpu)
 handle_in_userspace:
 	vcpu->run->exit_reason = KVM_EXIT_XEN;
 	vcpu->run->xen.type = KVM_EXIT_XEN_HCALL;
-	vcpu->run->xen.u.hcall.longmode = longmode;
+	vcpu->run->xen.u.hcall.longmode = is_64bit;
 	vcpu->run->xen.u.hcall.cpl = cpl;
 	vcpu->run->xen.u.hcall.input = input;
 	vcpu->run->xen.u.hcall.params[0] = params[0];
@@ -1794,7 +1822,7 @@ int kvm_xen_hypercall(struct kvm_vcpu *vcpu)
 
 static void kvm_xen_check_poller(struct kvm_vcpu *vcpu, int port)
 {
-	int poll_evtchn = vcpu->arch.xen.poll_evtchn;
+	int poll_evtchn = READ_ONCE(vcpu->arch.xen.poll_evtchn);
 
 	if ((poll_evtchn == port || poll_evtchn == -1) &&
 	    test_and_clear_bit(vcpu->vcpu_idx, vcpu->kvm->arch.xen.poll_mask)) {
@@ -1816,8 +1844,9 @@ static void kvm_xen_check_poller(struct kvm_vcpu *vcpu, int port)
 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;
+	bool has_64bit_shinfo = kvm_xen_has_64bit_shinfo(kvm);
+	unsigned long *pending_bits, *mask_bits, vi_pending_sel_ofs;
 	struct kvm_vcpu *vcpu;
-	unsigned long *pending_bits, *mask_bits;
 	unsigned long flags;
 	int port_word_bit;
 	bool kick_vcpu = false;
@@ -1833,7 +1862,7 @@ int kvm_xen_set_evtchn_fast(struct kvm_xen_evtchn *xe, struct kvm *kvm)
 		WRITE_ONCE(xe->vcpu_idx, vcpu->vcpu_idx);
 	}
 
-	if (xe->port >= max_evtchn_port(kvm))
+	if (xe->port >= max_evtchn_port(has_64bit_shinfo))
 		return -EINVAL;
 
 	rc = -EWOULDBLOCK;
@@ -1844,16 +1873,23 @@ int kvm_xen_set_evtchn_fast(struct kvm_xen_evtchn *xe, struct kvm *kvm)
 	if (!kvm_gpc_check(gpc, PAGE_SIZE))
 		goto out_rcu;
 
-	if (IS_ENABLED(CONFIG_64BIT) && kvm->arch.xen.long_mode) {
+	if (has_64bit_shinfo) {
 		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;
+
+		vi_pending_sel_ofs = offsetof(struct vcpu_info, evtchn_pending_sel);
 	} 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;
+
+		vi_pending_sel_ofs = offsetof(struct compat_vcpu_info, evtchn_pending_sel);
+
+		/* test_and_set_bit() needs 64-bit alignment, but that's OK */
+		BUILD_BUG_ON(offsetof(struct compat_shared_info, evtchn_pending) & 7);
 	}
 
 	/*
@@ -1869,6 +1905,8 @@ int kvm_xen_set_evtchn_fast(struct kvm_xen_evtchn *xe, struct kvm *kvm)
 		rc = -ENOTCONN; /* Masked */
 		kvm_xen_check_poller(vcpu, xe->port);
 	} else {
+		bool old;
+
 		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);
@@ -1885,19 +1923,29 @@ int kvm_xen_set_evtchn_fast(struct kvm_xen_evtchn *xe, struct kvm *kvm)
 			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;
-			}
+		/*
+		 * Explicitly use a 32-bit btsl instead of test_and_set_bit(),
+		 * which would use btsq on x86-64. The vcpu_info is guest-
+		 * controlled and only required to be 32-bit aligned, so a
+		 * 64-bit access could generate a split-lock #AC.
+		 *
+		 * Note, this does not apply to the test_and_set_bit() on
+		 * pending_bits above: that is in the per-VM shared_info, which
+		 * is page aligned, so the access is guaranteed to be 64-bit
+		 * aligned.
+		 */
+		old = GEN_BINARY_RMWcc(LOCK_PREFIX "btsl",
+				       *(u32 *)(gpc->khva + vi_pending_sel_ofs),
+				       c, "Ir", port_word_bit);
+		if (!old) {
+			struct vcpu_info *vi = gpc->khva;
+
+			/* No need for compat handling */
+			BUILD_BUG_ON(offsetof(struct vcpu_info, evtchn_upcall_pending) !=
+				     offsetof(struct compat_vcpu_info, evtchn_upcall_pending));
+
+			WRITE_ONCE(vi->evtchn_upcall_pending, 1);
+			kick_vcpu = true;
 		}
 
 		/* For the per-vCPU lapic vector, deliver it as MSI. */
@@ -1995,7 +2043,7 @@ int kvm_xen_setup_evtchn(struct kvm *kvm,
 	struct kvm_vcpu *vcpu;
 
 	/*
-	 * Don't check for the port being within range of max_evtchn_port().
+	 * Don't check for the port being within range of kvm_max_evtchn_port().
 	 * Userspace can configure what ever targets it likes; events just won't
 	 * be delivered if/while the target is invalid, just like userspace can
 	 * configure MSIs which target non-existent APICs.
@@ -2004,8 +2052,8 @@ int kvm_xen_setup_evtchn(struct kvm *kvm,
 	 * can be restored *independently* of other things like creating vCPUs,
 	 * without imposing an ordering dependency on userspace.  In this
 	 * particular case, the problematic ordering would be with setting the
-	 * Xen 'long mode' flag, which changes max_evtchn_port() to allow 4096
-	 * instead of 1024 event channels.
+	 * Xen 'long mode' flag, which changes kvm_max_evtchn_port() to allow
+	 * 4096 instead of 1024 event channels.
 	 */
 
 	/* We only support 2 level event channels for now */
@@ -2042,7 +2090,7 @@ int kvm_xen_hvm_evtchn_send(struct kvm *kvm, struct kvm_irq_routing_xen_evtchn *
 	struct kvm_xen_evtchn e;
 	int ret;
 
-	if (!uxe->port || uxe->port >= max_evtchn_port(kvm))
+	if (!uxe->port || uxe->port >= kvm_max_evtchn_port(kvm))
 		return -EINVAL;
 
 	/* We only support 2 level event channels for now */
@@ -2093,7 +2141,7 @@ static int kvm_xen_eventfd_update(struct kvm *kvm,
 
 	/* Protect writes to evtchnfd as well as the idr lookup.  */
 	mutex_lock(&kvm->arch.xen.xen_lock);
-	evtchnfd = idr_find(&kvm->arch.xen.evtchn_ports, port);
+	evtchnfd = xa_load(&kvm->arch.xen.evtchn_ports, port);
 
 	ret = -ENOENT;
 	if (!evtchnfd)
@@ -2152,7 +2200,7 @@ static int kvm_xen_eventfd_assign(struct kvm *kvm,
 
 	case EVTCHNSTAT_interdomain:
 		if (data->u.evtchn.deliver.port.port) {
-			if (data->u.evtchn.deliver.port.port >= max_evtchn_port(kvm))
+			if (data->u.evtchn.deliver.port.port >= kvm_max_evtchn_port(kvm))
 				goto out_noeventfd; /* -EINVAL */
 		} else {
 			eventfd = eventfd_ctx_fdget(data->u.evtchn.deliver.eventfd.fd);
@@ -2187,13 +2235,13 @@ static int kvm_xen_eventfd_assign(struct kvm *kvm,
 	}
 
 	mutex_lock(&kvm->arch.xen.xen_lock);
-	ret = idr_alloc(&kvm->arch.xen.evtchn_ports, evtchnfd, port, port + 1,
+	ret = xa_insert(&kvm->arch.xen.evtchn_ports, port, evtchnfd,
 			GFP_KERNEL);
 	mutex_unlock(&kvm->arch.xen.xen_lock);
-	if (ret >= 0)
+	if (!ret)
 		return 0;
 
-	if (ret == -ENOSPC)
+	if (ret == -EBUSY)
 		ret = -EEXIST;
 out:
 	if (eventfd)
@@ -2208,7 +2256,7 @@ static int kvm_xen_eventfd_deassign(struct kvm *kvm, u32 port)
 	struct evtchnfd *evtchnfd;
 
 	mutex_lock(&kvm->arch.xen.xen_lock);
-	evtchnfd = idr_remove(&kvm->arch.xen.evtchn_ports, port);
+	evtchnfd = xa_erase(&kvm->arch.xen.evtchn_ports, port);
 	mutex_unlock(&kvm->arch.xen.xen_lock);
 
 	if (!evtchnfd)
@@ -2224,7 +2272,7 @@ static int kvm_xen_eventfd_deassign(struct kvm *kvm, u32 port)
 static int kvm_xen_eventfd_reset(struct kvm *kvm)
 {
 	struct evtchnfd *evtchnfd, **all_evtchnfds;
-	int i;
+	unsigned long i;
 	int n = 0;
 
 	mutex_lock(&kvm->arch.xen.xen_lock);
@@ -2234,7 +2282,7 @@ static int kvm_xen_eventfd_reset(struct kvm *kvm)
 	 * critical section, first collect all the evtchnfd objects
 	 * in an array as they are removed from evtchn_ports.
 	 */
-	idr_for_each_entry(&kvm->arch.xen.evtchn_ports, evtchnfd, i)
+	xa_for_each(&kvm->arch.xen.evtchn_ports, i, evtchnfd)
 		n++;
 
 	all_evtchnfds = kmalloc_objs(struct evtchnfd *, n);
@@ -2244,9 +2292,9 @@ static int kvm_xen_eventfd_reset(struct kvm *kvm)
 	}
 
 	n = 0;
-	idr_for_each_entry(&kvm->arch.xen.evtchn_ports, evtchnfd, i) {
+	xa_for_each(&kvm->arch.xen.evtchn_ports, i, evtchnfd) {
 		all_evtchnfds[n++] = evtchnfd;
-		idr_remove(&kvm->arch.xen.evtchn_ports, evtchnfd->send_port);
+		xa_erase(&kvm->arch.xen.evtchn_ports, evtchnfd->send_port);
 	}
 	mutex_unlock(&kvm->arch.xen.xen_lock);
 
@@ -2270,7 +2318,7 @@ static int kvm_xen_setattr_evtchn(struct kvm *kvm, struct kvm_xen_hvm_attr *data
 	if (data->u.evtchn.flags == KVM_XEN_EVTCHN_RESET)
 		return kvm_xen_eventfd_reset(kvm);
 
-	if (!port || port >= max_evtchn_port(kvm))
+	if (!port || port >= kvm_max_evtchn_port(kvm))
 		return -EINVAL;
 
 	if (data->u.evtchn.flags == KVM_XEN_EVTCHN_DEASSIGN)
@@ -2297,12 +2345,10 @@ static bool kvm_xen_hcall_evtchn_send(struct kvm_vcpu *vcpu, u64 param, u64 *r)
 	}
 
 	/*
-	 * evtchnfd is protected by kvm->srcu; the idr lookup instead
-	 * is protected by RCU.
+	 * evtchnfd is protected by kvm->srcu; the xa_load is RCU-safe
+	 * internally, no explicit rcu_read_lock() needed.
 	 */
-	rcu_read_lock();
-	evtchnfd = idr_find(&vcpu->kvm->arch.xen.evtchn_ports, send.port);
-	rcu_read_unlock();
+	evtchnfd = xa_load(&vcpu->kvm->arch.xen.evtchn_ports, send.port);
 	if (!evtchnfd)
 		return false;
 
@@ -2349,23 +2395,23 @@ void kvm_xen_destroy_vcpu(struct kvm_vcpu *vcpu)
 void kvm_xen_init_vm(struct kvm *kvm)
 {
 	mutex_init(&kvm->arch.xen.xen_lock);
-	idr_init(&kvm->arch.xen.evtchn_ports);
+	xa_init(&kvm->arch.xen.evtchn_ports);
 	kvm_gpc_init(&kvm->arch.xen.shinfo_cache, kvm);
 }
 
 void kvm_xen_destroy_vm(struct kvm *kvm)
 {
 	struct evtchnfd *evtchnfd;
-	int i;
+	unsigned long i;
 
 	kvm_gpc_deactivate(&kvm->arch.xen.shinfo_cache);
 
-	idr_for_each_entry(&kvm->arch.xen.evtchn_ports, evtchnfd, i) {
+	xa_for_each(&kvm->arch.xen.evtchn_ports, i, evtchnfd) {
 		if (!evtchnfd->deliver.port.port)
 			eventfd_ctx_put(evtchnfd->deliver.eventfd.ctx);
 		kfree(evtchnfd);
 	}
-	idr_destroy(&kvm->arch.xen.evtchn_ports);
+	xa_destroy(&kvm->arch.xen.evtchn_ports);
 
 	if (kvm->arch.xen.hvm_config.msr)
 		static_branch_slow_dec_deferred(&kvm_xen_enabled);
diff --git a/arch/x86/kvm/xen.h b/arch/x86/kvm/xen.h
index f372855857a88..9d04e350bdb1f 100644
--- a/arch/x86/kvm/xen.h
+++ b/arch/x86/kvm/xen.h
@@ -235,6 +235,11 @@ struct compat_shared_info {
 #define COMPAT_EVTCHN_2L_NR_CHANNELS (8 *				\
 				      sizeof_field(struct compat_shared_info, \
 						   evtchn_pending))
+
+/* Latched VM-wide mode; the KVM equivalent of Xen's !has_32bit_shinfo(). */
+#define kvm_xen_has_64bit_shinfo(kvm) \
+	(IS_ENABLED(CONFIG_64BIT) && READ_ONCE((kvm)->arch.xen.long_mode))
+
 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 03bfc92864b6e..3dd04605f2e55 100644
--- a/include/linux/kvm_host.h
+++ b/include/linux/kvm_host.h
@@ -855,6 +855,8 @@ struct kvm {
 	gfn_t mmu_invalidate_range_start;
 	gfn_t mmu_invalidate_range_end;
 
+	unsigned long gpc_invalidate_seq;
+
 	struct list_head devices;
 	u64 manual_dirty_log_protect;
 	struct dentry *debugfs_dentry;
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
index 65eb26a0520d8..108d42c5c1d6a 100644
--- a/virt/kvm/kvm_main.c
+++ b/virt/kvm/kvm_main.c
@@ -813,6 +813,16 @@ static void kvm_mmu_notifier_invalidate_range_end(struct mmu_notifier *mn,
 
 	/* Pairs with the increment in range_start(). */
 	spin_lock(&kvm->mn_invalidate_lock);
+	kvm->gpc_invalidate_seq++;
+
+	/*
+	 * As with the MMU sequence counter and mmu_invalidate_in_progress, the
+	 * GPC sequence increase must be visible before the invalidate count
+	 * goes to zero.  Pairs with the smp_rmb() in
+	 * mmu_notifier_retry_cache().
+	 */
+	smp_wmb();
+
 	if (!WARN_ON_ONCE(!kvm->mn_active_invalidate_count))
 		--kvm->mn_active_invalidate_count;
 	wake = !kvm->mn_active_invalidate_count;
diff --git a/virt/kvm/pfncache.c b/virt/kvm/pfncache.c
index 728d2c1b488a8..3659686b97c24 100644
--- a/virt/kvm/pfncache.c
+++ b/virt/kvm/pfncache.c
@@ -124,7 +124,7 @@ static void gpc_unmap(kvm_pfn_t pfn, void *khva)
 #endif
 }
 
-static inline bool mmu_notifier_retry_cache(struct kvm *kvm, unsigned long mmu_seq)
+static inline bool mmu_notifier_retry_cache(struct kvm *kvm, unsigned long gpc_seq)
 {
 	/*
 	 * mn_active_invalidate_count acts for all intents and purposes
@@ -136,20 +136,20 @@ static inline bool mmu_notifier_retry_cache(struct kvm *kvm, unsigned long mmu_s
 	 * Note, it does not matter that mn_active_invalidate_count
 	 * is not protected by gpc->lock.  It is guaranteed to
 	 * be elevated before the mmu_notifier acquires gpc->lock, and
-	 * isn't dropped until after mmu_invalidate_seq is updated.
+	 * isn't dropped until after gpc_invalidate_seq is updated.
 	 */
 	if (kvm->mn_active_invalidate_count)
 		return true;
 
 	/*
 	 * Ensure mn_active_invalidate_count is read before
-	 * mmu_invalidate_seq.  This pairs with the smp_wmb() in
-	 * mmu_notifier_invalidate_range_end() to guarantee either the
+	 * gpc_invalidate_seq.  This pairs with the smp_wmb() in
+	 * kvm_mmu_notifier_invalidate_range_end() to guarantee either the
 	 * old (non-zero) value of mn_active_invalidate_count or the
-	 * new (incremented) value of mmu_invalidate_seq is observed.
+	 * new (incremented) value of gpc_invalidate_seq is observed.
 	 */
 	smp_rmb();
-	return kvm->mmu_invalidate_seq != mmu_seq;
+	return kvm->gpc_invalidate_seq != gpc_seq;
 }
 
 static kvm_pfn_t hva_to_pfn_retry(struct gfn_to_pfn_cache *gpc)
@@ -158,7 +158,7 @@ static kvm_pfn_t hva_to_pfn_retry(struct gfn_to_pfn_cache *gpc)
 	void *old_khva = (void *)PAGE_ALIGN_DOWN((uintptr_t)gpc->khva);
 	kvm_pfn_t new_pfn = KVM_PFN_ERR_FAULT;
 	void *new_khva = NULL;
-	unsigned long mmu_seq;
+	unsigned long gpc_seq;
 	struct page *page;
 
 	struct kvm_follow_pfn kfp = {
@@ -181,7 +181,7 @@ static kvm_pfn_t hva_to_pfn_retry(struct gfn_to_pfn_cache *gpc)
 	gpc->valid = false;
 
 	do {
-		mmu_seq = gpc->kvm->mmu_invalidate_seq;
+		gpc_seq = gpc->kvm->gpc_invalidate_seq;
 		smp_rmb();
 
 		write_unlock_irq(&gpc->lock);
@@ -232,7 +232,7 @@ static kvm_pfn_t hva_to_pfn_retry(struct gfn_to_pfn_cache *gpc)
 		 * attempting to refresh.
 		 */
 		WARN_ON_ONCE(gpc->valid);
-	} while (mmu_notifier_retry_cache(gpc->kvm, mmu_seq));
+	} while (mmu_notifier_retry_cache(gpc->kvm, gpc_seq));
 
 	gpc->valid = true;
 	gpc->pfn = new_pfn;

21/3 2026/09/01 04:39 llm kmsan-evaluator
0m Model:
gemini-3.7-flash

Args:
null

Results:
null

Tokens:
							input: 13268
							output: 286
							thoughts: 1565
						

Thoughts:
**Patch Analysis: A Deep Dive into Memory Safety and Concurrency**

Okay, let's break down this patch. My initial thought is to immediately assess potential risks, particularly concerning uninitialized memory, which is a common source of security vulnerabilities. We're dealing with KVM, so we need to be extra cautious.

First, the patch touches a few key areas: the X86 KVM code, Xen integration, and the KVM core. There's a significant shift from `idr evtchn_ports` to `xarray evtchn_ports`. I need to make sure this transition is sound and doesn't introduce any new memory-related issues with memory allocation or initialization. The alignment checks in `kvm_xen_vcpu_set_attr` for `gpa` and `hva` are good, and the bitwise operations with atomic functions are replacing some inline assembly which is sensible for avoiding split locks. The new sequence counter `gpc_invalidate_seq` is also good.

I'm now going to go through these changes in detail.

*   In `arch/x86/kvm/xen.c`, the new `kvm_xen_shared_info_init` using `kvm_xen_has_64bit_shinfo` is straightforward. The atomic bit operations and the SRCU guard in `__kvm_xen_has_interrupt` looks good. The alignment checks in `kvm_xen_vcpu_set_attr` are appropriate. Helper updates seem okay. The locking with `READ_ONCE` and `WRITE_ONCE` for `poll_evtchn` and the renaming of `longmode` is OK as well. The change to atomic `btsl` makes sense. The XArray conversions are the most important change to verify here.

*   In `virt/kvm/pfncache.c` and `kvm_main.c`, the separate `gpc_invalidate_seq` counter is fine, since KVM uses `kzalloc` on creation and there are no initialization risks.

Are any structures allocated without initialization? No, the `kmalloc_objs` and `xa_init` usage looks correct. It's properly initialized. No usercopy from kernel structs, which is great. No conditionals based on uninitialized variables, either. This is all about concurrency, data structure transformations (the IDR to XArray conversion, which is the big one), and alignment checks.

Let me now perform a deeper inspection. None of these changes seem to introduce any direct usercopy, leaking info, or reading uninitialized memory. KMSAN looks like it wouldn't detect issues because there are no uninitialized reads (stack/heap).

Let's double-check the individual changes: The increment of `gpc_invalidate_seq` looks fine, as does the initialization of `gpc_seq` from it. The atomic operations seem correct and the RAII guard from `srcu` should be correct, and the xarray APIs look standard and safe.

**Conclusion:** After this thorough review, I'm confident that this patch is safe from uninitialized memory reads, leaks, and potential KASAN/KMSAN issues. The KASAN/LOCKDEP checks will catch any concurrency-related problems (like UAF, data races, and OOB access). KMSAN will also confirm this, as these changes appear to be safe. Therefore, the result of a KMSAN check would be negative.

KASAN deals with concurrency synchronization, replacement of data structures, and alignment. KMSAN checks for uninitialized variables, info leaks, or branching on such data.



22/3 2026/09/01 04:39 tool set-results
0m Args:
{
  "KMSANReasoning": "The patch modifies KVM's Xen emulation and GFN-to-PFN cache invalidation logic. Specifically, it:\n1. Replaces the IDR data structure with XArray for event channel port tracking (`evtchn_ports`).\n2. Introduces a dedicated sequence counter (`gpc_invalidate_seq`) in `pfncache.c` and `kvm_main.c` to properly track GPC invalidation events.\n3. Fixes potential split-lock `#AC` exceptions by checking GPA/HVA 32-bit alignment in `kvm_xen_vcpu_set_attr` and using 32-bit atomic bitops for `vi_pending_sel`.\n4. Fixes SRCU locking in `__kvm_xen_has_interrupt` and atomic reads/writes for `poll_evtchn`.\n\nNone of these changes allocate uninitialized buffers, read uninitialized stack/heap memory, or expose kernel info-leaks to userspace. Memory safety and concurrency concerns related to these changes (such as locking, use-after-free, or out-of-bounds accesses) are covered by KASAN and LOCKDEP. Therefore, dedicated KMSAN fuzzing is not needed.",
  "NeedsKMSAN": false
}

Results:
{
  "KMSANReasoning": "The patch modifies KVM's Xen emulation and GFN-to-PFN cache invalidation logic. Specifically, it:\n1. Replaces the IDR data structure with XArray for event channel port tracking (`evtchn_ports`).\n2. Introduces a dedicated sequence counter (`gpc_invalidate_seq`) in `pfncache.c` and `kvm_main.c` to properly track GPC invalidation events.\n3. Fixes potential split-lock `#AC` exceptions by checking GPA/HVA 32-bit alignment in `kvm_xen_vcpu_set_attr` and using 32-bit atomic bitops for `vi_pending_sel`.\n4. Fixes SRCU locking in `__kvm_xen_has_interrupt` and atomic reads/writes for `poll_evtchn`.\n\nNone of these changes allocate uninitialized buffers, read uninitialized stack/heap memory, or expose kernel info-leaks to userspace. Memory safety and concurrency concerns related to these changes (such as locking, use-after-free, or out-of-bounds accesses) are covered by KASAN and LOCKDEP. Therefore, dedicated KMSAN fuzzing is not needed.",
  "NeedsKMSAN": false
}

LLM Calls Summary:
Total Calls Total Tokens Avg Tokens Total Duration (Seconds) Avg Duration (Seconds)
Tool Calls Summary:
Total Calls Total Duration (Seconds) Avg Duration (Seconds)