2014-11-18 20:51+0100, Paolo Bonzini:
> On 16/11/2014 22:49, Nadav Amit wrote:
> > @@ -374,13 +378,15 @@ static inline void apic_clear_irr(int vec, struct 
> > kvm_lapic *apic)
> > +           apic->irr_pending = false;
> > +           apic_clear_vector(vec, apic->regs + APIC_IRR);
> > +           if (apic_search_irr(apic) != -1)
> > +                   apic->irr_pending = true;
> >     }
> >  }
> 
> This is even more tricky than it looks like. :)
> 
> No one can concurrently look at apic->irr_pending while it is false, in
> particular apic_sync_pv_eoi_to_guest cannot enable PV EOI by mistake
> just because it sees a false irr_pending.  So it's okay if it is first
> set to false and then to true.

Yeah, using 'atomic_t irr_count' instead seems less error-prone to me;
and it would usually end in temporary unpublished-patches tree, but you
can take a look at the idea:

---8<---
KVM: x86: convert irr_pending to atomic irr_count

We've already had a buggy race with it ... atomic_t is simple to grasp
and harder to misuse, so we can switch without losing much performance.
(Read is normal and clear_irr does not parse APIC_IRR in exchange.
 We inflate lapic_struct by 3 bytes.)

Only two races remain now, which is the minimum without a proper lock,
atomic_t also makes their existence obvious on every use.

/__apic_test_and.*()/ aren't atomic, so we have to introduce new ones.
---
 arch/x86/kvm/lapic.c | 48 ++++++++++++++++++++++++++++--------------------
 arch/x86/kvm/lapic.h |  4 ++--
 2 files changed, 30 insertions(+), 22 deletions(-)

diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c
index e0e5642..e3169c5 100644
--- a/arch/x86/kvm/lapic.c
+++ b/arch/x86/kvm/lapic.c
@@ -102,6 +102,16 @@ static inline void apic_clear_vector(int vec, void *bitmap)
        clear_bit(VEC_POS(vec), (bitmap) + REG_POS(vec));
 }
 
+static inline int apic_test_and_set_vector(int vec, void *bitmap)
+{
+       return test_and_set_bit(VEC_POS(vec), (bitmap) + REG_POS(vec));
+}
+
+static inline int apic_test_and_clear_vector(int vec, void *bitmap)
+{
+       return test_and_clear_bit(VEC_POS(vec), (bitmap) + REG_POS(vec));
+}
+
 static inline int __apic_test_and_set_vector(int vec, void *bitmap)
 {
        return __test_and_set_bit(VEC_POS(vec), (bitmap) + REG_POS(vec));
@@ -341,12 +351,11 @@ EXPORT_SYMBOL_GPL(kvm_apic_update_irr);
 
 static inline void apic_set_irr(int vec, struct kvm_lapic *apic)
 {
-       apic_set_vector(vec, apic->regs + APIC_IRR);
-       /*
-        * irr_pending must be true if any interrupt is pending; set it after
-        * APIC_IRR to avoid race with apic_clear_irr
-        */
-       apic->irr_pending = true;
+       if (apic_test_and_set_vector(vec, apic->regs + APIC_IRR))
+               return;
+
+       if (likely(!kvm_apic_vid_enabled(vcpu->kvm)))
+               atomic_inc(&apic->irr_count);
 }
 
 static inline int apic_search_irr(struct kvm_lapic *apic)
@@ -359,10 +368,10 @@ static inline int apic_find_highest_irr(struct kvm_lapic 
*apic)
        int result;
 
        /*
-        * Note that irr_pending is just a hint. It will be always
-        * true with virtual interrupt delivery enabled.
+        * Note that irr_count is just a hint. It will be always
+        * nonzero with virtual interrupt delivery enabled.
         */
-       if (!apic->irr_pending)
+       if (!atomic_read(&apic->irr_count))
                return -1;
 
        kvm_x86_ops->sync_pir_to_irr(apic->vcpu);
@@ -376,18 +385,16 @@ static inline void apic_clear_irr(int vec, struct 
kvm_lapic *apic)
 {
        struct kvm_vcpu *vcpu;
 
+       if(!apic_test_and_clear_vector(vec, apic->regs + APIC_IRR))
+               return;
+
        vcpu = apic->vcpu;
 
-       if (unlikely(kvm_apic_vid_enabled(vcpu->kvm))) {
+       if (unlikely(kvm_apic_vid_enabled(vcpu->kvm)))
                /* try to update RVI */
-               apic_clear_vector(vec, apic->regs + APIC_IRR);
                kvm_make_request(KVM_REQ_EVENT, vcpu);
-       } else {
-               apic->irr_pending = false;
-               apic_clear_vector(vec, apic->regs + APIC_IRR);
-               if (apic_search_irr(apic) != -1)
-                       apic->irr_pending = true;
-       }
+       else
+               atomic_dec(&apic->irr_count);
 }
 
 static inline void apic_set_isr(int vec, struct kvm_lapic *apic)
@@ -1522,7 +1529,7 @@ void kvm_lapic_reset(struct kvm_vcpu *vcpu)
                apic_set_reg(apic, APIC_ISR + 0x10 * i, 0);
                apic_set_reg(apic, APIC_TMR + 0x10 * i, 0);
        }
-       apic->irr_pending = kvm_apic_vid_enabled(vcpu->kvm);
+       atomic_set(&apic->irr_count, kvm_apic_vid_enabled(vcpu->kvm));
        apic->isr_count = kvm_apic_vid_enabled(vcpu->kvm);
        apic->highest_isr_cache = -1;
        update_divide_count(apic);
@@ -1732,7 +1739,8 @@ void kvm_apic_post_state_restore(struct kvm_vcpu *vcpu,
        hrtimer_cancel(&apic->lapic_timer.timer);
        update_divide_count(apic);
        start_apic_timer(apic);
-       apic->irr_pending = true;
+       atomic_set(&apic->irr_count, kvm_apic_vid_enabled(vcpu->kvm) ?
+                               1 : count_vectors(apic->regs + APIC_IRR));
        apic->isr_count = kvm_apic_vid_enabled(vcpu->kvm) ?
                                1 : count_vectors(apic->regs + APIC_ISR);
        apic->highest_isr_cache = -1;
@@ -1820,7 +1828,7 @@ static void apic_sync_pv_eoi_to_guest(struct kvm_vcpu 
*vcpu,
 {
        if (!pv_eoi_enabled(vcpu) ||
            /* IRR set or many bits in ISR: could be nested. */
-           apic->irr_pending ||
+           atomic_read(&apic->irr_count) ||
            /* Cache not set: could be safe but we don't bother. */
            apic->highest_isr_cache == -1 ||
            /* Need EOI to update ioapic. */
diff --git a/arch/x86/kvm/lapic.h b/arch/x86/kvm/lapic.h
index d4365f2..a3f533b 100644
--- a/arch/x86/kvm/lapic.h
+++ b/arch/x86/kvm/lapic.h
@@ -24,8 +24,8 @@ struct kvm_lapic {
        u32 divide_count;
        struct kvm_vcpu *vcpu;
        bool sw_enabled;
-       bool irr_pending;
-       /* Number of bits set in ISR. */
+       /* Number of bits set in IRR and ISR. */
+       atomic_t irr_count;
        s16 isr_count;
        /* The highest vector set in ISR; if -1 - invalid, must scan ISR. */
        int highest_isr_cache;
-- 
2.1.0

--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to