From: David Woodhouse <[email protected]> A suspended plane's events must preempt the lower plane running on the same vCPU, but nothing told the running twin:
- An interrupt, local-APIC timer or Hyper-V stimer landing on a stopped twin only kicked the twin itself — a no-op while it is parked. Kick the lower twins too, with KVM_REQ_UNBLOCK so a sibling already past its event checks aborts the in-flight vmentry. - A parked plane's lapic timer rode the VMX preemption timer, which only counts in guest mode, so it never fired while the plane was suspended. Hand it to the hrtimer when a non-zero plane's KVM_RUN ends. - KVM_REQ_HV_STIMER was a shared request, so the running plane consumed the twin's request without processing the twin's stimers. Make it per-plane. - kvm_higher_plane_with_event() now also reports a pending-but-not-injected apic timer. - Suppress KVM_EXIT_HYPERV_SYNIC for non-zero planes: userspace's SynIC device models plane 0 only, and treated a twin's SIMP move as a plane-0 overlay relocation, carrying stale overlay contents to the new GPA. Signed-off-by: David Woodhouse <[email protected]> Assisted-by: LLM --- arch/x86/include/asm/kvm_host.h | 2 +- arch/x86/kvm/hyperv.c | 27 +++++++++++++++++++ arch/x86/kvm/lapic.c | 46 ++++++++++++++++++++++++++++++++- arch/x86/kvm/x86.c | 19 ++++++++++++-- 4 files changed, 90 insertions(+), 4 deletions(-) diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index 74810e0e9ed7..c54f47c52a59 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -112,7 +112,7 @@ #define KVM_REQ_IOAPIC_EOI_EXIT KVM_ARCH_PLANE_REQ(19) #define KVM_REQ_HV_RESET KVM_ARCH_REQ(20) #define KVM_REQ_HV_EXIT KVM_ARCH_REQ(21) -#define KVM_REQ_HV_STIMER KVM_ARCH_REQ(22) +#define KVM_REQ_HV_STIMER KVM_ARCH_PLANE_REQ(22) #define KVM_REQ_LOAD_EOI_EXITMAP KVM_ARCH_REQ(23) #define KVM_REQ_GET_NESTED_STATE_PAGES KVM_ARCH_REQ(24) #define KVM_REQ_APICV_UPDATE \ diff --git a/arch/x86/kvm/hyperv.c b/arch/x86/kvm/hyperv.c index ca31054ed065..4551e011bf56 100644 --- a/arch/x86/kvm/hyperv.c +++ b/arch/x86/kvm/hyperv.c @@ -247,6 +247,15 @@ static void synic_exit(struct kvm_vcpu_hv_synic *synic, u32 msr) struct kvm_vcpu *vcpu = hv_synic_to_vcpu(synic); struct kvm_vcpu_hv *hv_vcpu = to_hv_vcpu(vcpu); + /* + * Userspace's SynIC device models plane 0 only; a higher plane's + * exit would be mistaken for a plane-0 page move and relocate the + * overlay the VMM keeps for SIMP/SIEFP. Higher-plane SynICs are + * handled fully in-kernel. + */ + if (vcpu->plane_level) + return; + hv_vcpu->exit.type = KVM_EXIT_HYPERV_SYNIC; hv_vcpu->exit.u.synic.msr = msr; hv_vcpu->exit.u.synic.control = synic->control; @@ -598,6 +607,24 @@ static void stimer_mark_pending(struct kvm_vcpu_hv_stimer *stimer, kvm_make_request(KVM_REQ_HV_STIMER, vcpu); if (vcpu_kick) kvm_vcpu_kick(vcpu); + + /* + * A pending stimer on a suspended higher plane is only processed + * once that plane runs; kick the lower twins so the running one + * re-evaluates pending higher-plane events and forces entry. + */ + if (vcpu_kick && vcpu->plane_level) { + struct kvm_vcpu *twin; + int p; + + for (p = 0; p < vcpu->plane_level; p++) { + twin = vcpu->common->vcpus[p]; + if (twin) { + kvm_make_request(KVM_REQ_UNBLOCK, twin); + kvm_vcpu_kick(twin); + } + } + } } static void stimer_cleanup(struct kvm_vcpu_hv_stimer *stimer) diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c index dc7a08831a54..f89156c26972 100644 --- a/arch/x86/kvm/lapic.c +++ b/arch/x86/kvm/lapic.c @@ -1436,6 +1436,26 @@ static int __apic_accept_irq(struct kvm_lapic *apic, int delivery_mode, kvm_x86_call(deliver_interrupt)(apic, delivery_mode, trig_mode, vector); + + /* + * An interrupt for a higher plane must preempt a lower + * plane running on the same vCPU: kicking only the (stopped) + * target twin wakes a blocked sibling via the shared wait, + * but a sibling executing in guest mode needs an explicit + * kick to re-evaluate pending higher-plane events. + */ + if (vcpu->plane_level) { + struct kvm_vcpu *twin; + int p; + + for (p = 0; p < vcpu->plane_level; p++) { + twin = vcpu->common->vcpus[p]; + if (twin) { + kvm_make_request(KVM_REQ_UNBLOCK, twin); + kvm_vcpu_kick(twin); + } + } + } break; case APIC_DM_REMRD: @@ -2060,6 +2080,24 @@ static void apic_timer_expired(struct kvm_lapic *apic, bool from_timer_fn) kvm_make_request(KVM_REQ_UNBLOCK, vcpu); if (from_timer_fn) kvm_vcpu_kick(vcpu); + + /* + * A pending timer on a suspended higher plane only gets injected + * once that plane runs again; kick the lower twins so the running + * one re-evaluates pending higher-plane events and forces entry. + */ + if (vcpu->plane_level) { + struct kvm_vcpu *twin; + int p; + + for (p = 0; p < vcpu->plane_level; p++) { + twin = vcpu->common->vcpus[p]; + if (twin) { + kvm_make_request(KVM_REQ_UNBLOCK, twin); + kvm_vcpu_kick(twin); + } + } + } } static void start_sw_tscdeadline(struct kvm_lapic *apic) @@ -3107,7 +3145,13 @@ int kvm_create_lapic(struct kvm_vcpu *vcpu) * that vCPU creation is guaranteed to run with a deterministic value, * the request will ensure the vCPU gets the correct state before VM-Entry. */ - if (enable_apicv) { + /* + * Higher-plane twins are software-switched and mostly suspended; + * hardware interrupt virtualization on them only leaves pending + * vectors in the PIR where a suspended plane's state cannot be + * reliably observed. Keep them on the legacy delivery path. + */ + if (enable_apicv && !vcpu->plane_level) { apic->apicv_active = true; kvm_make_request(KVM_REQ_APICV_UPDATE, vcpu); } diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 374330f61eb2..4b12ec7ee100 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -11226,7 +11226,9 @@ static struct kvm_vcpu *kvm_higher_plane_with_event(struct kvm_vcpu *vcpu) */ if (kvm_cpu_has_extint(higher) || kvm_apic_has_interrupt(higher) != -1 || - kvm_test_request(KVM_REQ_HV_STIMER, higher)) + kvm_test_request(KVM_REQ_HV_STIMER, higher) || + (higher->arch.apic && + atomic_read(&higher->arch.apic->lapic_timer.pending))) return higher; } return NULL; @@ -11336,8 +11338,12 @@ void __kvm_vcpu_update_apicv(struct kvm_vcpu *vcpu) down_read(&vcpu->kvm->arch.apicv_update_lock); preempt_disable(); - /* Do not activate APICV when APIC is disabled */ + /* + * Do not activate APICV when APIC is disabled, nor on higher-plane + * twins (see kvm_create_lapic). + */ activate = kvm_vcpu_apicv_activated(vcpu) && + !vcpu->plane_level && (kvm_get_apic_mode(vcpu) != LAPIC_MODE_DISABLED); if (apic->apicv_active == activate) @@ -12566,6 +12572,15 @@ static int __kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu) post_kvm_run_save(vcpu); kvm_vcpu_srcu_read_unlock(vcpu); + /* + * A non-zero plane may stay suspended indefinitely after this run + * ends, and its hv timer only counts in VMX non-root mode. Hand + * the lapic timer to the hrtimer so it keeps firing while the + * plane is parked; firing is what forces re-entry into the plane. + */ + if (vcpu->plane_level && kvm_lapic_hv_timer_in_use(vcpu)) + kvm_lapic_switch_to_sw_timer(vcpu); + kvm_sigset_deactivate(vcpu); vcpu_put(vcpu); return r; -- 2.50.1

