@@ -33,6 +33,7 @@
#include "irq.h"
#include "i8254.h"
+#include "kvm_timer.h"
#ifndef CONFIG_X86_64
#define mod_64(x, y) ((x) - (y) * div64_u64(x, y))
@@ -227,12 +228,13 @@ static void pit_latch_status(struct kvm *kvm, int channel)
}
}
-int pit_has_pending_timer(struct kvm_vcpu *vcpu)
+int pit_has_pending_timer(struct kvm *kvm)
{
- struct kvm_pit *pit = vcpu->kvm->arch.vpit;
+ struct kvm_pit *pit = kvm->arch.vpit;
- if (pit && kvm_vcpu_is_bsp(vcpu) && pit->pit_state.irq_ack)
+ if (pit && pit->pit_state.irq_ack)
return atomic_read(&pit->pit_state.pit_timer.pending);
+
return 0;
}
@@ -252,6 +254,13 @@ void __kvm_migrate_pit_timer(struct kvm_vcpu *vcpu)
struct kvm_pit *pit = vcpu->kvm->arch.vpit;
struct hrtimer *timer;
+ /*
+ * technically, the PIT isn't hooked to a particular VCPU;
+ * the logical structure is PIT -> [IOA]PIC -> CPU[s]. However,
+ * hrtimers expire on a per-cpu basis, and since we initially
+ * created the hrtimer during BSP creation, we move it around
+ * with the BSP.
+ */
if (!kvm_vcpu_is_bsp(vcpu) || !pit)
return;
@@ -277,6 +286,33 @@ static struct kvm_timer_ops kpit_ops = {
.is_periodic = kpit_is_periodic,
};
+static enum hrtimer_restart pit_timer_fn(struct hrtimer *data)
+{
+ struct kvm_timer *ktimer = container_of(data, struct kvm_timer, timer);
+ int restart_timer = 0;
+
+ /*
+ * There is a race window between reading and incrementing, but we do
+ * not care about potentially losing timer events in the !reinject
+ * case anyway.
+ */
+ if (ktimer->reinject || !atomic_read(&ktimer->pending))
+ atomic_inc(&ktimer->pending);
+
+ if (waitqueue_active(&ktimer->kvm->bsp_vcpu->wq))
+ wake_up_interruptible(&ktimer->kvm->bsp_vcpu->wq);
+
+ if (ktimer->t_ops->is_periodic(ktimer)) {
+ hrtimer_add_expires_ns(&ktimer->timer, ktimer->period);
+ restart_timer = 1;
+ }
+
+ if (restart_timer)
+ return HRTIMER_RESTART;
+ else
+ return HRTIMER_NORESTART;
+}
+
static void create_pit_timer(struct kvm_kpit_state *ps, u32 val, int is_period)
{
struct kvm_timer *pt = &ps->pit_timer;
@@ -291,10 +327,9 @@ static void create_pit_timer(struct kvm_kpit_state *ps, u32 val, int is_period)
pt->period = interval;
ps->is_periodic = is_period;
- pt->timer.function = kvm_timer_fn;
+ pt->timer.function = pit_timer_fn;
pt->t_ops = &kpit_ops;
pt->kvm = ps->pit->kvm;
- pt->vcpu = pt->kvm->bsp_vcpu;
atomic_set(&pt->pending, 0);
ps->irq_ack = 1;
@@ -705,10 +740,9 @@ static void __inject_pit_timer_intr(struct kvm *kvm)
kvm_apic_nmi_wd_deliver(vcpu);
}
-void kvm_inject_pit_timer_irqs(struct kvm_vcpu *vcpu)
+void kvm_inject_pit_timer_irqs(struct kvm *kvm)
{
- struct kvm_pit *pit = vcpu->kvm->arch.vpit;
- struct kvm *kvm = vcpu->kvm;
+ struct kvm_pit *pit = kvm->arch.vpit;
struct kvm_kpit_state *ps;
if (pit) {
@@ -49,10 +49,12 @@ struct kvm_pit {
#define KVM_MAX_PIT_INTR_INTERVAL HZ / 100
#define KVM_PIT_CHANNEL_MASK 0x3
-void kvm_inject_pit_timer_irqs(struct kvm_vcpu *vcpu);
+int pit_has_pending_timer(struct kvm *kvm);
+void kvm_inject_pit_timer_irqs(struct kvm *kvm);
void kvm_pit_load_count(struct kvm *kvm, int channel, u32 val, int hpet_legacy_start);
struct kvm_pit *kvm_create_pit(struct kvm *kvm, u32 flags);
void kvm_free_pit(struct kvm *kvm);
void kvm_pit_reset(struct kvm_pit *pit);
+void __kvm_migrate_pit_timer(struct kvm_vcpu *vcpu);
#endif
@@ -34,7 +34,7 @@ int kvm_cpu_has_pending_timer(struct kvm_vcpu *vcpu)
{
int ret;
- ret = pit_has_pending_timer(vcpu);
+ ret = pit_has_pending_timer(vcpu->kvm);
ret |= apic_has_pending_timer(vcpu);
return ret;
@@ -89,7 +89,7 @@ EXPORT_SYMBOL_GPL(kvm_cpu_get_interrupt);
void kvm_inject_pending_timer_irqs(struct kvm_vcpu *vcpu)
{
kvm_inject_apic_timer_irqs(vcpu);
- kvm_inject_pit_timer_irqs(vcpu);
+ kvm_inject_pit_timer_irqs(vcpu->kvm);
/* TODO: PIT, RTC etc. */
}
EXPORT_SYMBOL_GPL(kvm_inject_pending_timer_irqs);
@@ -95,10 +95,8 @@ void kvm_inject_pending_timer_irqs(struct kvm_vcpu *vcpu);
void kvm_inject_apic_timer_irqs(struct kvm_vcpu *vcpu);
void kvm_apic_nmi_wd_deliver(struct kvm_vcpu *vcpu);
void __kvm_migrate_apic_timer(struct kvm_vcpu *vcpu);
-void __kvm_migrate_pit_timer(struct kvm_vcpu *vcpu);
void __kvm_migrate_timers(struct kvm_vcpu *vcpu);
-int pit_has_pending_timer(struct kvm_vcpu *vcpu);
int apic_has_pending_timer(struct kvm_vcpu *vcpu);
#endif
@@ -1,3 +1,5 @@
+#ifndef __KVM_TIMER_H
+#define __KVM_TIMER_H
struct kvm_timer {
struct hrtimer timer;
@@ -16,3 +18,4 @@ struct kvm_timer_ops {
enum hrtimer_restart kvm_timer_fn(struct hrtimer *data);
+#endif