irq->level, irq->trig_mode);
 }
 
+static int pv_eoi_put_user(struct kvm_vcpu *vcpu, u8 val)
+{
+
+       return kvm_write_guest_cached(vcpu->kvm, &vcpu->arch.pv_eoi.data, &val,
+                                     sizeof(val));
+}
+
+static int pv_eoi_get_user(struct kvm_vcpu *vcpu, u8 *val)
+{
+
+       return kvm_read_guest_cached(vcpu->kvm, &vcpu->arch.pv_eoi.data, val,
+                                     sizeof(*val));
+}
+
+static inline bool pv_eoi_enabled(struct kvm_vcpu *vcpu)
+{
+       return vcpu->arch.pv_eoi.msr_val & KVM_MSR_ENABLED;
+}
+
+static bool pv_eoi_get_pending(struct kvm_vcpu *vcpu)
+{
+       u8 val;
+       if (pv_eoi_get_user(vcpu, &val) < 0)
+               apic_debug("Can't read EOI MSR value: 0x%llx\n",
+                          (unsigned long long)vcpi->arch.pv_eoi.msr_val);
+       return val & 0x1;
+}
+
+static void pv_eoi_set_pending(struct kvm_vcpu *vcpu)
+{
+       if (pv_eoi_put_user(vcpu, KVM_PV_EOI_ENABLED) < 0) {
+               apic_debug("Can't set EOI MSR value: 0x%llx\n",
+                          (unsigned long long)vcpi->arch.pv_eoi.msr_val);
+               return;
+       }
+       __set_bit(KVM_APIC_PV_EOI_PENDING, &vcpu->arch.apic_attention);
+}
+
+static void pv_eoi_clr_pending(struct kvm_vcpu *vcpu)
+{
+       if (pv_eoi_put_user(vcpu, KVM_PV_EOI_DISABLED) < 0) {
+               apic_debug("Can't clear EOI MSR value: 0x%llx\n",
+                          (unsigned long long)vcpi->arch.pv_eoi.msr_val);
+               return;
+       }
+       __clear_bit(KVM_APIC_PV_EOI_PENDING, &vcpu->arch.apic_attention);
+}
+
 static inline int apic_find_highest_isr(struct kvm_lapic *apic)
 {
        int result;
        return vcpu1->arch.apic_arb_prio - vcpu2->arch.apic_arb_prio;
 }
 
-static void apic_set_eoi(struct kvm_lapic *apic)
+static int apic_set_eoi(struct kvm_lapic *apic)
 {
        int vector = apic_find_highest_isr(apic);
+
+       trace_kvm_eoi(apic, vector);
+
        /*
         * Not every write EOI will has corresponding ISR,
         * one example is when Kernel check timer on setup_IO_APIC
         */
        if (vector == -1)
-               return;
+               return vector;
 
        apic_clear_isr(vector, apic);
        apic_update_ppr(apic);
                kvm_ioapic_update_eoi(apic->vcpu->kvm, vector, trigger_mode);
        }
        kvm_make_request(KVM_REQ_EVENT, apic->vcpu);
+       return vector;
 }
 
 static void apic_send_ipi(struct kvm_lapic *apic)
        atomic_set(&apic->lapic_timer.pending, 0);
        if (kvm_vcpu_is_bsp(vcpu))
                vcpu->arch.apic_base |= MSR_IA32_APICBASE_BSP;
+       vcpu->arch.pv_eoi.msr_val = 0;
        apic_update_ppr(apic);
 
        vcpu->arch.apic_arb_prio = 0;
                hrtimer_start_expires(timer, HRTIMER_MODE_ABS);
 }
 
+/*
+ * apic_sync_pv_eoi_from_guest - called on vmexit or cancel interrupt
+ *
+ * Detect whether guest triggered PV EOI since the
+ * last entry. If yes, set EOI on guests's behalf.
+ * Clear PV EOI in guest memory in any case.
+ */
+static void apic_sync_pv_eoi_from_guest(struct kvm_vcpu *vcpu,
+                                       struct kvm_lapic *apic)
+{
+       bool pending;
+       int vector;
+       /*
+        * PV EOI state is derived from KVM_APIC_PV_EOI_PENDING in host
+        * and KVM_PV_EOI_ENABLED in guest memory as follows:
+        *
+        * KVM_APIC_PV_EOI_PENDING is unset:
+        *      -> host disabled PV EOI.
+        * KVM_APIC_PV_EOI_PENDING is set, KVM_PV_EOI_ENABLED is set:
+        *      -> host enabled PV EOI, guest did not execute EOI yet.
+        * KVM_APIC_PV_EOI_PENDING is set, KVM_PV_EOI_ENABLED is unset:
+        *      -> host enabled PV EOI, guest executed EOI.
+        */
+       BUG_ON(!pv_eoi_enabled(vcpu));
+       pending = pv_eoi_get_pending(vcpu);
+       /*
+        * Clear pending bit in any case: it will be set again on vmentry.
+        * While this might not be ideal from performance point of view,
+        * this makes sure pv eoi is only enabled when we know it's safe.
+        */
+       pv_eoi_clr_pending(vcpu);
+       if (pending)
+               return;
+       vector = apic_set_eoi(apic);
+       trace_kvm_pv_eoi(apic, vector);
+}
+
 void kvm_lapic_sync_from_vapic(struct kvm_vcpu *vcpu)
 {
        u32 data;
        void *vapic;
 
+       if (test_bit(KVM_APIC_PV_EOI_PENDING, &vcpu->arch.apic_attention))
+               apic_sync_pv_eoi_from_guest(vcpu, vcpu->arch.apic);
+
        if (!test_bit(KVM_APIC_CHECK_VAPIC, &vcpu->arch.apic_attention))
                return;
 
        apic_set_tpr(vcpu->arch.apic, data & 0xff);
 }
 
+/*
+ * apic_sync_pv_eoi_to_guest - called before vmentry
+ *
+ * Detect whether it's safe to enable PV EOI and
+ * if yes do so.
+ */
+static void apic_sync_pv_eoi_to_guest(struct kvm_vcpu *vcpu,
+                                       struct kvm_lapic *apic)
+{
+       if (!pv_eoi_enabled(vcpu) ||
+           /* IRR set or many bits in ISR: could be nested. */
+           apic->irr_pending ||
+           /* Cache not set: could be safe but we don't bother. */
+           apic->highest_isr_cache == -1 ||
+           /* Need EOI to update ioapic. */
+           kvm_ioapic_handles_vector(vcpu->kvm, apic->highest_isr_cache)) {
+               /*
+                * PV EOI was disabled by apic_sync_pv_eoi_from_guest
+                * so we need not do anything here.
+                */
+               return;
+       }
+
+       pv_eoi_set_pending(apic->vcpu);
+}
+
 void kvm_lapic_sync_to_vapic(struct kvm_vcpu *vcpu)
 {
        u32 data, tpr;
        int max_irr, max_isr;
-       struct kvm_lapic *apic;
+       struct kvm_lapic *apic = vcpu->arch.apic;
        void *vapic;
 
+       apic_sync_pv_eoi_to_guest(vcpu, apic);
+
        if (!test_bit(KVM_APIC_CHECK_VAPIC, &vcpu->arch.apic_attention))
                return;
 
-       apic = vcpu->arch.apic;
        tpr = apic_get_reg(apic, APIC_TASKPRI) & 0xff;
        max_irr = apic_find_highest_irr(apic);
        if (max_irr < 0)
 
        return 0;
 }
+
+int kvm_lapic_enable_pv_eoi(struct kvm_vcpu *vcpu, u64 data)
+{
+       u64 addr = data & ~KVM_MSR_ENABLED;
+       if (!IS_ALIGNED(addr, 4))
+               return 1;
+
+       vcpu->arch.pv_eoi.msr_val = data;
+       if (!pv_eoi_enabled(vcpu))
+               return 0;
+       return kvm_gfn_to_hva_cache_init(vcpu->kvm, &vcpu->arch.pv_eoi.data,
+                                        addr);
+}