diff options
author | Eddie Dong <eddie.dong@intel.com> | 2007-09-03 09:56:58 -0400 |
---|---|---|
committer | Avi Kivity <avi@qumranet.com> | 2007-10-13 04:18:26 -0400 |
commit | 1b9778dae71dc64d3678d766c0f1fbed79c80f9f (patch) | |
tree | 3196bbfe474119a849f35aefddb5e0922205850f /drivers/kvm | |
parent | 6e5d865c0b9679b00b5e5f0754c9fc2b6b9894d6 (diff) |
KVM: Keep track of missed timer irq injections
APIC timer IRQ is set every time when a certain period
expires at host time, but the guest may be descheduled
at that time and thus the irq be overwritten by later fire.
This patch keep track of firing irq numbers and decrease
only when the IRQ is injected to guest or buffered in
APIC.
Signed-off-by: Yaozu (Eddie) Dong <Eddie.Dong@intel.com>
Signed-off-by: Qing He <qing.he@intel.com>
Signed-off-by: Avi Kivity <avi@qumranet.com>
Diffstat (limited to 'drivers/kvm')
-rw-r--r-- | drivers/kvm/irq.c | 13 | ||||
-rw-r--r-- | drivers/kvm/irq.h | 4 | ||||
-rw-r--r-- | drivers/kvm/kvm_main.c | 2 | ||||
-rw-r--r-- | drivers/kvm/lapic.c | 58 | ||||
-rw-r--r-- | drivers/kvm/svm.c | 7 | ||||
-rw-r--r-- | drivers/kvm/vmx.c | 10 |
6 files changed, 69 insertions, 25 deletions
diff --git a/drivers/kvm/irq.c b/drivers/kvm/irq.c index e09cd65925d6..b88e50115588 100644 --- a/drivers/kvm/irq.c +++ b/drivers/kvm/irq.c | |||
@@ -78,3 +78,16 @@ void kvm_vcpu_kick(struct kvm_vcpu *vcpu) | |||
78 | smp_call_function_single(ipi_pcpu, vcpu_kick_intr, vcpu, 0, 0); | 78 | smp_call_function_single(ipi_pcpu, vcpu_kick_intr, vcpu, 0, 0); |
79 | } | 79 | } |
80 | 80 | ||
81 | void kvm_inject_pending_timer_irqs(struct kvm_vcpu *vcpu) | ||
82 | { | ||
83 | kvm_inject_apic_timer_irqs(vcpu); | ||
84 | /* TODO: PIT, RTC etc. */ | ||
85 | } | ||
86 | EXPORT_SYMBOL_GPL(kvm_inject_pending_timer_irqs); | ||
87 | |||
88 | void kvm_timer_intr_post(struct kvm_vcpu *vcpu, int vec) | ||
89 | { | ||
90 | kvm_apic_timer_intr_post(vcpu, vec); | ||
91 | /* TODO: PIT, RTC etc. */ | ||
92 | } | ||
93 | EXPORT_SYMBOL_GPL(kvm_timer_intr_post); | ||
diff --git a/drivers/kvm/irq.h b/drivers/kvm/irq.h index 07035e8279d4..87baf7e69ea2 100644 --- a/drivers/kvm/irq.h +++ b/drivers/kvm/irq.h | |||
@@ -154,5 +154,9 @@ int kvm_ioapic_init(struct kvm *kvm); | |||
154 | void kvm_ioapic_set_irq(struct kvm_ioapic *ioapic, int irq, int level); | 154 | void kvm_ioapic_set_irq(struct kvm_ioapic *ioapic, int irq, int level); |
155 | int kvm_lapic_enabled(struct kvm_vcpu *vcpu); | 155 | int kvm_lapic_enabled(struct kvm_vcpu *vcpu); |
156 | int kvm_lapic_find_highest_irr(struct kvm_vcpu *vcpu); | 156 | int kvm_lapic_find_highest_irr(struct kvm_vcpu *vcpu); |
157 | void kvm_apic_timer_intr_post(struct kvm_vcpu *vcpu, int vec); | ||
158 | void kvm_timer_intr_post(struct kvm_vcpu *vcpu, int vec); | ||
159 | void kvm_inject_pending_timer_irqs(struct kvm_vcpu *vcpu); | ||
160 | void kvm_inject_apic_timer_irqs(struct kvm_vcpu *vcpu); | ||
157 | 161 | ||
158 | #endif | 162 | #endif |
diff --git a/drivers/kvm/kvm_main.c b/drivers/kvm/kvm_main.c index d56964a6eb80..8f8bfc9160e3 100644 --- a/drivers/kvm/kvm_main.c +++ b/drivers/kvm/kvm_main.c | |||
@@ -283,6 +283,8 @@ EXPORT_SYMBOL_GPL(kvm_vcpu_init); | |||
283 | void kvm_vcpu_uninit(struct kvm_vcpu *vcpu) | 283 | void kvm_vcpu_uninit(struct kvm_vcpu *vcpu) |
284 | { | 284 | { |
285 | kvm_mmu_destroy(vcpu); | 285 | kvm_mmu_destroy(vcpu); |
286 | if (vcpu->apic) | ||
287 | hrtimer_cancel(&vcpu->apic->timer.dev); | ||
286 | kvm_free_apic(vcpu->apic); | 288 | kvm_free_apic(vcpu->apic); |
287 | free_page((unsigned long)vcpu->pio_data); | 289 | free_page((unsigned long)vcpu->pio_data); |
288 | free_page((unsigned long)vcpu->run); | 290 | free_page((unsigned long)vcpu->run); |
diff --git a/drivers/kvm/lapic.c b/drivers/kvm/lapic.c index 68bbbb38edad..490d4939dba7 100644 --- a/drivers/kvm/lapic.c +++ b/drivers/kvm/lapic.c | |||
@@ -313,6 +313,7 @@ static int __apic_accept_irq(struct kvm_lapic *apic, int delivery_mode, | |||
313 | int vector, int level, int trig_mode) | 313 | int vector, int level, int trig_mode) |
314 | { | 314 | { |
315 | int result = 0; | 315 | int result = 0; |
316 | int orig_irr; | ||
316 | 317 | ||
317 | switch (delivery_mode) { | 318 | switch (delivery_mode) { |
318 | case APIC_DM_FIXED: | 319 | case APIC_DM_FIXED: |
@@ -321,7 +322,8 @@ static int __apic_accept_irq(struct kvm_lapic *apic, int delivery_mode, | |||
321 | if (unlikely(!apic_enabled(apic))) | 322 | if (unlikely(!apic_enabled(apic))) |
322 | break; | 323 | break; |
323 | 324 | ||
324 | if (apic_test_and_set_irr(vector, apic) && trig_mode) { | 325 | orig_irr = apic_test_and_set_irr(vector, apic); |
326 | if (orig_irr && trig_mode) { | ||
325 | apic_debug("level trig mode repeatedly for vector %d", | 327 | apic_debug("level trig mode repeatedly for vector %d", |
326 | vector); | 328 | vector); |
327 | break; | 329 | break; |
@@ -335,7 +337,7 @@ static int __apic_accept_irq(struct kvm_lapic *apic, int delivery_mode, | |||
335 | 337 | ||
336 | kvm_vcpu_kick(apic->vcpu); | 338 | kvm_vcpu_kick(apic->vcpu); |
337 | 339 | ||
338 | result = 1; | 340 | result = (orig_irr == 0); |
339 | break; | 341 | break; |
340 | 342 | ||
341 | case APIC_DM_REMRD: | 343 | case APIC_DM_REMRD: |
@@ -831,38 +833,33 @@ EXPORT_SYMBOL_GPL(kvm_lapic_enabled); | |||
831 | * timer interface | 833 | * timer interface |
832 | *---------------------------------------------------------------------- | 834 | *---------------------------------------------------------------------- |
833 | */ | 835 | */ |
836 | |||
837 | /* TODO: make sure __apic_timer_fn runs in current pCPU */ | ||
834 | static int __apic_timer_fn(struct kvm_lapic *apic) | 838 | static int __apic_timer_fn(struct kvm_lapic *apic) |
835 | { | 839 | { |
836 | u32 vector; | ||
837 | int result = 0; | 840 | int result = 0; |
841 | wait_queue_head_t *q = &apic->vcpu->wq; | ||
838 | 842 | ||
839 | if (unlikely(!apic_enabled(apic) || | ||
840 | !apic_lvt_enabled(apic, APIC_LVTT))) { | ||
841 | apic_debug("%s: time interrupt although apic is down\n", | ||
842 | __FUNCTION__); | ||
843 | return 0; | ||
844 | } | ||
845 | |||
846 | vector = apic_lvt_vector(apic, APIC_LVTT); | ||
847 | apic->timer.last_update = apic->timer.dev.expires; | ||
848 | atomic_inc(&apic->timer.pending); | 843 | atomic_inc(&apic->timer.pending); |
849 | __apic_accept_irq(apic, APIC_DM_FIXED, vector, 1, 0); | 844 | if (waitqueue_active(q)) |
850 | 845 | wake_up_interruptible(q); | |
851 | if (apic_lvtt_period(apic)) { | 846 | if (apic_lvtt_period(apic)) { |
852 | u32 offset; | ||
853 | u32 tmict = apic_get_reg(apic, APIC_TMICT); | ||
854 | |||
855 | offset = APIC_BUS_CYCLE_NS * apic->timer.divide_count * tmict; | ||
856 | |||
857 | result = 1; | 847 | result = 1; |
858 | apic->timer.dev.expires = ktime_add_ns( | 848 | apic->timer.dev.expires = ktime_add_ns( |
859 | apic->timer.dev.expires, | 849 | apic->timer.dev.expires, |
860 | apic->timer.period); | 850 | apic->timer.period); |
861 | } | 851 | } |
862 | |||
863 | return result; | 852 | return result; |
864 | } | 853 | } |
865 | 854 | ||
855 | static int __inject_apic_timer_irq(struct kvm_lapic *apic) | ||
856 | { | ||
857 | int vector; | ||
858 | |||
859 | vector = apic_lvt_vector(apic, APIC_LVTT); | ||
860 | return __apic_accept_irq(apic, APIC_DM_FIXED, vector, 1, 0); | ||
861 | } | ||
862 | |||
866 | static enum hrtimer_restart apic_timer_fn(struct hrtimer *data) | 863 | static enum hrtimer_restart apic_timer_fn(struct hrtimer *data) |
867 | { | 864 | { |
868 | struct kvm_lapic *apic; | 865 | struct kvm_lapic *apic; |
@@ -935,6 +932,27 @@ int kvm_apic_has_interrupt(struct kvm_vcpu *vcpu) | |||
935 | return highest_irr; | 932 | return highest_irr; |
936 | } | 933 | } |
937 | 934 | ||
935 | void kvm_inject_apic_timer_irqs(struct kvm_vcpu *vcpu) | ||
936 | { | ||
937 | struct kvm_lapic *apic = vcpu->apic; | ||
938 | |||
939 | if (apic && apic_lvt_enabled(apic, APIC_LVTT) && | ||
940 | atomic_read(&apic->timer.pending) > 0) { | ||
941 | if (__inject_apic_timer_irq(apic)) | ||
942 | atomic_dec(&apic->timer.pending); | ||
943 | } | ||
944 | } | ||
945 | |||
946 | void kvm_apic_timer_intr_post(struct kvm_vcpu *vcpu, int vec) | ||
947 | { | ||
948 | struct kvm_lapic *apic = vcpu->apic; | ||
949 | |||
950 | if (apic && apic_lvt_vector(apic, APIC_LVTT) == vec) | ||
951 | apic->timer.last_update = ktime_add_ns( | ||
952 | apic->timer.last_update, | ||
953 | apic->timer.period); | ||
954 | } | ||
955 | |||
938 | int kvm_get_apic_interrupt(struct kvm_vcpu *vcpu) | 956 | int kvm_get_apic_interrupt(struct kvm_vcpu *vcpu) |
939 | { | 957 | { |
940 | int vector = kvm_apic_has_interrupt(vcpu); | 958 | int vector = kvm_apic_has_interrupt(vcpu); |
diff --git a/drivers/kvm/svm.c b/drivers/kvm/svm.c index c8cd242f36ff..00119ec41669 100644 --- a/drivers/kvm/svm.c +++ b/drivers/kvm/svm.c | |||
@@ -1331,7 +1331,9 @@ static void svm_intr_assist(struct vcpu_svm *svm) | |||
1331 | { | 1331 | { |
1332 | struct vmcb *vmcb = svm->vmcb; | 1332 | struct vmcb *vmcb = svm->vmcb; |
1333 | int intr_vector = -1; | 1333 | int intr_vector = -1; |
1334 | struct kvm_vcpu *vcpu = &svm->vcpu; | ||
1334 | 1335 | ||
1336 | kvm_inject_pending_timer_irqs(vcpu); | ||
1335 | if ((vmcb->control.exit_int_info & SVM_EVTINJ_VALID) && | 1337 | if ((vmcb->control.exit_int_info & SVM_EVTINJ_VALID) && |
1336 | ((vmcb->control.exit_int_info & SVM_EVTINJ_TYPE_MASK) == 0)) { | 1338 | ((vmcb->control.exit_int_info & SVM_EVTINJ_TYPE_MASK) == 0)) { |
1337 | intr_vector = vmcb->control.exit_int_info & | 1339 | intr_vector = vmcb->control.exit_int_info & |
@@ -1344,7 +1346,7 @@ static void svm_intr_assist(struct vcpu_svm *svm) | |||
1344 | if (vmcb->control.int_ctl & V_IRQ_MASK) | 1346 | if (vmcb->control.int_ctl & V_IRQ_MASK) |
1345 | return; | 1347 | return; |
1346 | 1348 | ||
1347 | if (!kvm_cpu_has_interrupt(&svm->vcpu)) | 1349 | if (!kvm_cpu_has_interrupt(vcpu)) |
1348 | return; | 1350 | return; |
1349 | 1351 | ||
1350 | if (!(vmcb->save.rflags & X86_EFLAGS_IF) || | 1352 | if (!(vmcb->save.rflags & X86_EFLAGS_IF) || |
@@ -1356,8 +1358,9 @@ static void svm_intr_assist(struct vcpu_svm *svm) | |||
1356 | return; | 1358 | return; |
1357 | } | 1359 | } |
1358 | /* Okay, we can deliver the interrupt: grab it and update PIC state. */ | 1360 | /* Okay, we can deliver the interrupt: grab it and update PIC state. */ |
1359 | intr_vector = kvm_cpu_get_interrupt(&svm->vcpu); | 1361 | intr_vector = kvm_cpu_get_interrupt(vcpu); |
1360 | svm_inject_irq(svm, intr_vector); | 1362 | svm_inject_irq(svm, intr_vector); |
1363 | kvm_timer_intr_post(vcpu, intr_vector); | ||
1361 | } | 1364 | } |
1362 | 1365 | ||
1363 | static void kvm_reput_irq(struct vcpu_svm *svm) | 1366 | static void kvm_reput_irq(struct vcpu_svm *svm) |
diff --git a/drivers/kvm/vmx.c b/drivers/kvm/vmx.c index 5c2c6e71abf2..eeecadf5da46 100644 --- a/drivers/kvm/vmx.c +++ b/drivers/kvm/vmx.c | |||
@@ -2151,7 +2151,9 @@ static void vmx_intr_assist(struct kvm_vcpu *vcpu) | |||
2151 | { | 2151 | { |
2152 | u32 idtv_info_field, intr_info_field; | 2152 | u32 idtv_info_field, intr_info_field; |
2153 | int has_ext_irq, interrupt_window_open; | 2153 | int has_ext_irq, interrupt_window_open; |
2154 | int vector; | ||
2154 | 2155 | ||
2156 | kvm_inject_pending_timer_irqs(vcpu); | ||
2155 | update_tpr_threshold(vcpu); | 2157 | update_tpr_threshold(vcpu); |
2156 | 2158 | ||
2157 | has_ext_irq = kvm_cpu_has_interrupt(vcpu); | 2159 | has_ext_irq = kvm_cpu_has_interrupt(vcpu); |
@@ -2183,9 +2185,11 @@ static void vmx_intr_assist(struct kvm_vcpu *vcpu) | |||
2183 | interrupt_window_open = | 2185 | interrupt_window_open = |
2184 | ((vmcs_readl(GUEST_RFLAGS) & X86_EFLAGS_IF) && | 2186 | ((vmcs_readl(GUEST_RFLAGS) & X86_EFLAGS_IF) && |
2185 | (vmcs_read32(GUEST_INTERRUPTIBILITY_INFO) & 3) == 0); | 2187 | (vmcs_read32(GUEST_INTERRUPTIBILITY_INFO) & 3) == 0); |
2186 | if (interrupt_window_open) | 2188 | if (interrupt_window_open) { |
2187 | vmx_inject_irq(vcpu, kvm_cpu_get_interrupt(vcpu)); | 2189 | vector = kvm_cpu_get_interrupt(vcpu); |
2188 | else | 2190 | vmx_inject_irq(vcpu, vector); |
2191 | kvm_timer_intr_post(vcpu, vector); | ||
2192 | } else | ||
2189 | enable_irq_window(vcpu); | 2193 | enable_irq_window(vcpu); |
2190 | } | 2194 | } |
2191 | 2195 | ||