From: David Woodhouse <[email protected]> Sashiko analysis and torture testing of the upcoming conversion of the gfn_to_pfn_cache from rwlock to atomic SRCU¹ highlighted a pre-existing problem: refreshing a cache can fail while a memslot is briefly marked KVM_MEMSLOT_INVALID during a memslot update. The Xen event channel and timer delivery paths silently dropped events when they hit that window.
Return -EAGAIN from __kvm_gpc_refresh() to distinguish the transient case from a genuinely unbacked GFN, and add a retry loop for the common case callers which can just wait for the memslot update to complete, as well as dealing with the corner cases which can't: - The Xen attribute ioctls wait, so userspace never sees the transient state. - Timer delivery leaves ->arch.xen.timer_pending set and retries from kvm_xen_inject_timer_irqs() on the next vCPU entry. - The guest's own EVTCHNOP_send hypercall runs under vcpu_run's kvm->srcu read section — the very reader the memslot update is waiting for — so it cannot wait. Leave the hypercall uncompleted without advancing RIP, so the guest re-executes it. This is how Xen itself restarts preempted hypercalls. Patch 3 steps the hypercall page MSR write out of the vCPU's kvm->srcu read section around xen_lock, so an ioctl waiting out a memslot update while holding that lock cannot deadlock it. Patches 7-10 are pulled forward from the SRCU series because they stand alone: - Three from Sean², extracting vcpu_info delivery into a separate helper and taking the shared_info and vcpu_info pages out of dirty tracking. Rebased: his series had first converted the GPC locks to read_trylock(), which this one doesn't carry. - Requesting the guest TLB flush from record_steal_time() itself, as a standalone cleanup in preparation for converting that to GPC. The final patch adds a selftest racing a guest EVTCHNOP_send loop against a tight memslot delete/recreate loop. Tested with that and xen_shinfo_test; an instrumented run showed 10 of its 4000 sends hitting the update window and re-executing. Based on kvm/next (d4b7fb647204). v2: https://git.infradead.org/?p=users/dwmw2/linux.git;a=shortlog;h=refs/heads/gpc-eagain-v2 - Take kvm->srcu around the kvm_gpc_check() loop and the wallclock write in kvm_xen_shared_info_init(), which had lost it. - New patch 3: don't block on xen_lock within the vCPU's kvm->srcu read section (three-way deadlock against a waiting ioctl and a memslot update). Document the rule at the lock's definition. - On -EINVAL (deactivated shared_info, or an invalid port), clear timer_pending but keep timer_expires: a halted vCPU would otherwise spin aborting its own block, and the event is still reported as pending through KVM_XEN_VCPU_GET_ATTR. - Drop the wait argument from kvm_xen_inject_timer_irqs(); neither caller may wait. - Extend the KVM_GET_DIRTY_LOG note in api.rst to the vcpu_info area. - Teach lockdep that the wait functions as a synchronize_srcu(), so it reports any lock held across it which a kvm->srcu read section can take. - Comment fixes; drop a leftover cosmetic hunk. Thanks to the Sashiko review bot for finding three of those on v1. v1: https://git.infradead.org/?p=users/dwmw2/linux.git;a=shortlog;h=refs/heads/gpc-eagain https://lore.kernel.org/kvm/[email protected]/ ¹ https://git.infradead.org/?p=users/dwmw2/linux.git;a=shortlog;h=refs/heads/gpc-srcu-v2 ² https://lore.kernel.org/kvm/[email protected]/ David Woodhouse (8): KVM: pfncache: Return -EAGAIN for a lookup which hits an invalid memslot KVM: pfncache: Add a 'wait' argument to the refresh and activate functions KVM: x86/xen: Don't block on xen_lock within the vCPU's kvm->srcu section KVM: x86/xen: Wait out memslot updates in the Xen attribute ioctls KVM: x86/xen: Don't lose the timer event when delivery fails transiently KVM: x86/xen: Retry event channel delivery which races a memslot update KVM: x86: Request the guest TLB flush from record_steal_time() KVM: selftests: Add Xen evtchn vs memslot-update race test Sean Christopherson (3): KVM: x86/xen: Extract delivery of event to vCPU into a separate helper KVM: x86/xen: Explicitly tag "shared info" page as never being dirty tracked KVM: x86/xen: Don't dirty track "vCPU info" page Documentation/virt/kvm/api.rst | 5 +- arch/x86/include/asm/kvm_host.h | 4 + arch/x86/kvm/msrs.c | 2 +- arch/x86/kvm/x86.c | 28 +- arch/x86/kvm/xen.c | 341 ++++++++++++------ include/linux/kvm_host.h | 27 +- include/linux/kvm_types.h | 1 + tools/testing/selftests/kvm/Makefile.kvm | 1 + .../kvm/x86/xen_evtchn_memslot_race_test.c | 178 +++++++++ .../selftests/kvm/x86/xen_shinfo_test.c | 11 +- virt/kvm/pfncache.c | 137 +++++-- 11 files changed, 564 insertions(+), 171 deletions(-) create mode 100644 tools/testing/selftests/kvm/x86/xen_evtchn_memslot_race_test.c base-commit: d4b7fb647204f0c81dfeae2d1a708e4d858e0c94 -- 2.43.0

