From 45756c15ff600d9b2baac76071eaec8baef387ee Mon Sep 17 00:00:00 2001
From: Qing He <qing.he@intel.com>
Date: Mon, 17 Sep 2007 14:47:13 +0800
Subject: [PATCH] KVM: fix PIC interrupt delivery on different APIC conditions

This patch changes the PIC interrupts delivery. Now it is only deliverd
to vcpu0 when either condition is met (on vcpu0):
  1. local APIC is hardware disabled
  2. LVT0 is unmasked and configured to delivery mode ExtInt

It fixes the 2x faster wall clock on x86_64 and SMP i386 Linux guests

Signed-off-by: Qing He <qing.he@intel.com>

---
 drivers/kvm/irq.c   |    4 ++--
 drivers/kvm/irq.h   |    1 +
 drivers/kvm/lapic.c |   17 +++++++++++++++++
 3 files changed, 20 insertions(+), 2 deletions(-)

diff --git a/drivers/kvm/irq.c b/drivers/kvm/irq.c
index 608c530..0f663fe 100644
--- a/drivers/kvm/irq.c
+++ b/drivers/kvm/irq.c
@@ -33,7 +33,7 @@ int kvm_cpu_has_interrupt(struct kvm_vcpu *v)
 	struct kvm_pic *s;
 
 	if (kvm_apic_has_interrupt(v) == -1) {	/* LAPIC */
-		if (v->vcpu_id == 0) {
+		if (kvm_apic_accept_pic_intr(v)) {
 			s = pic_irqchip(v->kvm);	/* PIC */
 			return s->output;
 		} else
@@ -53,7 +53,7 @@ int kvm_cpu_get_interrupt(struct kvm_vcpu *v)
 
 	vector = kvm_get_apic_interrupt(v);	/* APIC */
 	if (vector == -1) {
-		if (v->vcpu_id == 0) {
+		if (kvm_apic_accept_pic_intr(v)) {
 			s = pic_irqchip(v->kvm);
 			s->output = 0;		/* PIC */
 			vector = kvm_pic_read_irq(s);
diff --git a/drivers/kvm/irq.h b/drivers/kvm/irq.h
index 5f97e25..11fc014 100644
--- a/drivers/kvm/irq.h
+++ b/drivers/kvm/irq.h
@@ -135,6 +135,7 @@ do {									\
 
 void kvm_vcpu_kick(struct kvm_vcpu *vcpu);
 int kvm_apic_has_interrupt(struct kvm_vcpu *vcpu);
+int kvm_apic_accept_pic_intr(struct kvm_vcpu *vcpu);
 int kvm_get_apic_interrupt(struct kvm_vcpu *vcpu);
 int kvm_create_lapic(struct kvm_vcpu *vcpu);
 void kvm_lapic_reset(struct kvm_vcpu *vcpu);
diff --git a/drivers/kvm/lapic.c b/drivers/kvm/lapic.c
index 472a1c7..ddf9f20 100644
--- a/drivers/kvm/lapic.c
+++ b/drivers/kvm/lapic.c
@@ -836,6 +836,8 @@ void kvm_lapic_reset(struct kvm_vcpu *vcpu)
 
 	for (i = 0; i < APIC_LVT_NUM; i++)
 		apic_set_reg(apic, APIC_LVTT + 0x10 * i, APIC_LVT_MASKED);
+	apic_set_reg(apic, APIC_LVT0,
+		     SET_APIC_DELIVERY_MODE(0, APIC_MODE_EXTINT));
 
 	apic_set_reg(apic, APIC_DFR, 0xffffffffU);
 	apic_set_reg(apic, APIC_SPIV, 0xff);
@@ -984,6 +986,21 @@ int kvm_apic_has_interrupt(struct kvm_vcpu *vcpu)
 	return highest_irr;
 }
 
+int kvm_apic_accept_pic_intr(struct kvm_vcpu *vcpu)
+{
+	u32 lvt0 = apic_get_reg(vcpu->apic, APIC_LVT0);
+	int r = 0;
+
+	if (vcpu->vcpu_id == 0) {
+		if (!apic_hw_enabled(vcpu->apic))
+			r = 1;
+		if ((lvt0 & APIC_LVT_MASKED) == 0 &&
+		    GET_APIC_DELIVERY_MODE(lvt0) == APIC_MODE_EXTINT)
+			r = 1;
+	}
+	return r;
+}
+
 void kvm_inject_apic_timer_irqs(struct kvm_vcpu *vcpu)
 {
 	struct kvm_lapic *apic = vcpu->apic;
-- 
1.5.1.2

