Merge tag 'kvm-x86-apic-6.7' of https://github.com/kvm-x86/linux into HEAD
authorPaolo Bonzini <pbonzini@redhat.com>
Tue, 31 Oct 2023 14:11:19 +0000 (10:11 -0400)
committerPaolo Bonzini <pbonzini@redhat.com>
Tue, 31 Oct 2023 14:11:19 +0000 (10:11 -0400)
KVM x86 APIC changes for 6.7:

 - Purge VMX's posted interrupt descriptor *before* loading APIC state when
   handling KVM_SET_LAPIC.  Purging the PID after loading APIC state results in
   lost APIC timer IRQs as the APIC timer can be armed as part of loading APIC
   state, i.e. can immediately pend an IRQ if the expiry is in the past.

 - Clear the ICR.BUSY bit when handling trap-like x2APIC writes.  This avoids a
   WARN, due to KVM expecting the BUSY bit to be cleared when sending IPIs.

1  2 
arch/x86/include/asm/kvm_host.h
arch/x86/kvm/lapic.c

index 70d139406bc80db9fe65ab3257f2336dfff08309,f77568c6a3266ff6a152cdb5041cf2adeed98b1a..fb9f5fa96cc964d39041e2757773a21754dffc2c
@@@ -528,6 -528,7 +528,6 @@@ struct kvm_pmu 
        u64 raw_event_mask;
        struct kvm_pmc gp_counters[KVM_INTEL_PMC_MAX_GENERIC];
        struct kvm_pmc fixed_counters[KVM_PMC_MAX_FIXED];
 -      struct irq_work irq_work;
  
        /*
         * Overlay the bitmap with a 64-bit atomic so that all bits can be
@@@ -1708,6 -1709,7 +1708,7 @@@ struct kvm_x86_ops 
        int (*pi_update_irte)(struct kvm *kvm, unsigned int host_irq,
                              uint32_t guest_irq, bool set);
        void (*pi_start_assignment)(struct kvm *kvm);
+       void (*apicv_pre_state_restore)(struct kvm_vcpu *vcpu);
        void (*apicv_post_state_restore)(struct kvm_vcpu *vcpu);
        bool (*dy_apicv_has_pending_interrupt)(struct kvm_vcpu *vcpu);
  
diff --combined arch/x86/kvm/lapic.c
index 3e977dbbf9933d833fadd1a9e643e8db64ffcd49,953700e68cf6e552d5050341408bb1399b6a5fe7..245b20973caee481055da37cd1ae01a78bbb335b
@@@ -2444,22 -2444,22 +2444,22 @@@ EXPORT_SYMBOL_GPL(kvm_lapic_set_eoi)
  void kvm_apic_write_nodecode(struct kvm_vcpu *vcpu, u32 offset)
  {
        struct kvm_lapic *apic = vcpu->arch.apic;
-       u64 val;
  
        /*
-        * ICR is a single 64-bit register when x2APIC is enabled.  For legacy
-        * xAPIC, ICR writes need to go down the common (slightly slower) path
-        * to get the upper half from ICR2.
+        * ICR is a single 64-bit register when x2APIC is enabled, all others
+        * registers hold 32-bit values.  For legacy xAPIC, ICR writes need to
+        * go down the common path to get the upper half from ICR2.
+        *
+        * Note, using the write helpers may incur an unnecessary write to the
+        * virtual APIC state, but KVM needs to conditionally modify the value
+        * in certain cases, e.g. to clear the ICR busy bit.  The cost of extra
+        * conditional branches is likely a wash relative to the cost of the
+        * maybe-unecessary write, and both are in the noise anyways.
         */
-       if (apic_x2apic_mode(apic) && offset == APIC_ICR) {
-               val = kvm_lapic_get_reg64(apic, APIC_ICR);
-               kvm_apic_send_ipi(apic, (u32)val, (u32)(val >> 32));
-               trace_kvm_apic_write(APIC_ICR, val);
-       } else {
-               /* TODO: optimize to just emulate side effect w/o one more write */
-               val = kvm_lapic_get_reg(apic, offset);
-               kvm_lapic_reg_write(apic, offset, (u32)val);
-       }
+       if (apic_x2apic_mode(apic) && offset == APIC_ICR)
+               kvm_x2apic_icr_write(apic, kvm_lapic_get_reg64(apic, APIC_ICR));
+       else
+               kvm_lapic_reg_write(apic, offset, kvm_lapic_get_reg(apic, offset));
  }
  EXPORT_SYMBOL_GPL(kvm_apic_write_nodecode);
  
@@@ -2670,6 -2670,8 +2670,8 @@@ void kvm_lapic_reset(struct kvm_vcpu *v
        u64 msr_val;
        int i;
  
+       static_call_cond(kvm_x86_apicv_pre_state_restore)(vcpu);
        if (!init_event) {
                msr_val = APIC_DEFAULT_PHYS_BASE | MSR_IA32_APICBASE_ENABLE;
                if (kvm_vcpu_is_reset_bsp(vcpu))
@@@ -2759,17 -2761,13 +2761,17 @@@ int kvm_apic_local_deliver(struct kvm_l
  {
        u32 reg = kvm_lapic_get_reg(apic, lvt_type);
        int vector, mode, trig_mode;
 +      int r;
  
        if (kvm_apic_hw_enabled(apic) && !(reg & APIC_LVT_MASKED)) {
                vector = reg & APIC_VECTOR_MASK;
                mode = reg & APIC_MODE_MASK;
                trig_mode = reg & APIC_LVT_LEVEL_TRIGGER;
 -              return __apic_accept_irq(apic, mode, vector, 1, trig_mode,
 -                                      NULL);
 +
 +              r = __apic_accept_irq(apic, mode, vector, 1, trig_mode, NULL);
 +              if (r && lvt_type == APIC_LVTPC)
 +                      kvm_lapic_set_reg(apic, APIC_LVTPC, reg | APIC_LVT_MASKED);
 +              return r;
        }
        return 0;
  }
@@@ -2981,6 -2979,8 +2983,8 @@@ int kvm_apic_set_state(struct kvm_vcpu 
        struct kvm_lapic *apic = vcpu->arch.apic;
        int r;
  
+       static_call_cond(kvm_x86_apicv_pre_state_restore)(vcpu);
        kvm_lapic_set_base(vcpu, vcpu->arch.apic_base);
        /* set SPIV separately to get count of SW disabled APICs right */
        apic_set_spiv(apic, *((u32 *)(s->regs + APIC_SPIV)));