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


Reply via email to