Commit d3c7b77d authored by Marcelo Tosatti's avatar Marcelo Tosatti Committed by Avi Kivity

KVM: unify part of generic timer handling

Hide the internals of vcpu awakening / injection from the in-kernel
emulated timers. This makes future changes in this logic easier and
decreases the distance to more generic timer handling.
Signed-off-by: default avatarMarcelo Tosatti <mtosatti@redhat.com>
Signed-off-by: default avatarAvi Kivity <avi@redhat.com>
parent fd668423
...@@ -14,7 +14,7 @@ endif ...@@ -14,7 +14,7 @@ endif
EXTRA_CFLAGS += -Ivirt/kvm -Iarch/x86/kvm EXTRA_CFLAGS += -Ivirt/kvm -Iarch/x86/kvm
kvm-objs := $(common-objs) x86.o mmu.o x86_emulate.o i8259.o irq.o lapic.o \ kvm-objs := $(common-objs) x86.o mmu.o x86_emulate.o i8259.o irq.o lapic.o \
i8254.o i8254.o timer.o
obj-$(CONFIG_KVM) += kvm.o obj-$(CONFIG_KVM) += kvm.o
kvm-intel-objs = vmx.o kvm-intel-objs = vmx.o
obj-$(CONFIG_KVM_INTEL) += kvm-intel.o obj-$(CONFIG_KVM_INTEL) += kvm-intel.o
......
...@@ -219,25 +219,6 @@ static void pit_latch_status(struct kvm *kvm, int channel) ...@@ -219,25 +219,6 @@ static void pit_latch_status(struct kvm *kvm, int channel)
} }
} }
static int __pit_timer_fn(struct kvm_kpit_state *ps)
{
struct kvm_vcpu *vcpu0 = ps->pit->kvm->vcpus[0];
struct kvm_kpit_timer *pt = &ps->pit_timer;
if (!atomic_inc_and_test(&pt->pending))
set_bit(KVM_REQ_PENDING_TIMER, &vcpu0->requests);
if (!pt->reinject)
atomic_set(&pt->pending, 1);
if (vcpu0 && waitqueue_active(&vcpu0->wq))
wake_up_interruptible(&vcpu0->wq);
hrtimer_add_expires_ns(&pt->timer, pt->period);
return (pt->period == 0 ? 0 : 1);
}
int pit_has_pending_timer(struct kvm_vcpu *vcpu) int pit_has_pending_timer(struct kvm_vcpu *vcpu)
{ {
struct kvm_pit *pit = vcpu->kvm->arch.vpit; struct kvm_pit *pit = vcpu->kvm->arch.vpit;
...@@ -258,21 +239,6 @@ static void kvm_pit_ack_irq(struct kvm_irq_ack_notifier *kian) ...@@ -258,21 +239,6 @@ static void kvm_pit_ack_irq(struct kvm_irq_ack_notifier *kian)
spin_unlock(&ps->inject_lock); spin_unlock(&ps->inject_lock);
} }
static enum hrtimer_restart pit_timer_fn(struct hrtimer *data)
{
struct kvm_kpit_state *ps;
int restart_timer = 0;
ps = container_of(data, struct kvm_kpit_state, pit_timer.timer);
restart_timer = __pit_timer_fn(ps);
if (restart_timer)
return HRTIMER_RESTART;
else
return HRTIMER_NORESTART;
}
void __kvm_migrate_pit_timer(struct kvm_vcpu *vcpu) void __kvm_migrate_pit_timer(struct kvm_vcpu *vcpu)
{ {
struct kvm_pit *pit = vcpu->kvm->arch.vpit; struct kvm_pit *pit = vcpu->kvm->arch.vpit;
...@@ -286,15 +252,26 @@ void __kvm_migrate_pit_timer(struct kvm_vcpu *vcpu) ...@@ -286,15 +252,26 @@ void __kvm_migrate_pit_timer(struct kvm_vcpu *vcpu)
hrtimer_start_expires(timer, HRTIMER_MODE_ABS); hrtimer_start_expires(timer, HRTIMER_MODE_ABS);
} }
static void destroy_pit_timer(struct kvm_kpit_timer *pt) static void destroy_pit_timer(struct kvm_timer *pt)
{ {
pr_debug("pit: execute del timer!\n"); pr_debug("pit: execute del timer!\n");
hrtimer_cancel(&pt->timer); hrtimer_cancel(&pt->timer);
} }
static bool kpit_is_periodic(struct kvm_timer *ktimer)
{
struct kvm_kpit_state *ps = container_of(ktimer, struct kvm_kpit_state,
pit_timer);
return ps->is_periodic;
}
struct kvm_timer_ops kpit_ops = {
.is_periodic = kpit_is_periodic,
};
static void create_pit_timer(struct kvm_kpit_state *ps, u32 val, int is_period) static void create_pit_timer(struct kvm_kpit_state *ps, u32 val, int is_period)
{ {
struct kvm_kpit_timer *pt = &ps->pit_timer; struct kvm_timer *pt = &ps->pit_timer;
s64 interval; s64 interval;
interval = muldiv64(val, NSEC_PER_SEC, KVM_PIT_FREQ); interval = muldiv64(val, NSEC_PER_SEC, KVM_PIT_FREQ);
...@@ -304,7 +281,13 @@ static void create_pit_timer(struct kvm_kpit_state *ps, u32 val, int is_period) ...@@ -304,7 +281,13 @@ static void create_pit_timer(struct kvm_kpit_state *ps, u32 val, int is_period)
/* TODO The new value only affected after the retriggered */ /* TODO The new value only affected after the retriggered */
hrtimer_cancel(&pt->timer); hrtimer_cancel(&pt->timer);
pt->period = (is_period == 0) ? 0 : interval; pt->period = (is_period == 0) ? 0 : interval;
pt->timer.function = pit_timer_fn; ps->is_periodic = is_period;
pt->timer.function = kvm_timer_fn;
pt->t_ops = &kpit_ops;
pt->kvm = ps->pit->kvm;
pt->vcpu_id = 0;
atomic_set(&pt->pending, 0); atomic_set(&pt->pending, 0);
ps->irq_ack = 1; ps->irq_ack = 1;
......
...@@ -3,14 +3,6 @@ ...@@ -3,14 +3,6 @@
#include "iodev.h" #include "iodev.h"
struct kvm_kpit_timer {
struct hrtimer timer;
int irq;
s64 period; /* unit: ns */
atomic_t pending;
bool reinject;
};
struct kvm_kpit_channel_state { struct kvm_kpit_channel_state {
u32 count; /* can be 65536 */ u32 count; /* can be 65536 */
u16 latched_count; u16 latched_count;
...@@ -29,7 +21,8 @@ struct kvm_kpit_channel_state { ...@@ -29,7 +21,8 @@ struct kvm_kpit_channel_state {
struct kvm_kpit_state { struct kvm_kpit_state {
struct kvm_kpit_channel_state channels[3]; struct kvm_kpit_channel_state channels[3];
struct kvm_kpit_timer pit_timer; struct kvm_timer pit_timer;
bool is_periodic;
u32 speaker_data_on; u32 speaker_data_on;
struct mutex lock; struct mutex lock;
struct kvm_pit *pit; struct kvm_pit *pit;
......
struct kvm_timer {
struct hrtimer timer;
s64 period; /* unit: ns */
atomic_t pending; /* accumulated triggered timers */
bool reinject;
struct kvm_timer_ops *t_ops;
struct kvm *kvm;
int vcpu_id;
};
struct kvm_timer_ops {
bool (*is_periodic)(struct kvm_timer *);
};
enum hrtimer_restart kvm_timer_fn(struct hrtimer *data);
...@@ -528,12 +528,13 @@ static u32 apic_get_tmcct(struct kvm_lapic *apic) ...@@ -528,12 +528,13 @@ static u32 apic_get_tmcct(struct kvm_lapic *apic)
if (apic_get_reg(apic, APIC_TMICT) == 0) if (apic_get_reg(apic, APIC_TMICT) == 0)
return 0; return 0;
remaining = hrtimer_expires_remaining(&apic->timer.dev); remaining = hrtimer_expires_remaining(&apic->lapic_timer.timer);
if (ktime_to_ns(remaining) < 0) if (ktime_to_ns(remaining) < 0)
remaining = ktime_set(0, 0); remaining = ktime_set(0, 0);
ns = mod_64(ktime_to_ns(remaining), apic->timer.period); ns = mod_64(ktime_to_ns(remaining), apic->lapic_timer.period);
tmcct = div64_u64(ns, (APIC_BUS_CYCLE_NS * apic->timer.divide_count)); tmcct = div64_u64(ns,
(APIC_BUS_CYCLE_NS * apic->divide_count));
return tmcct; return tmcct;
} }
...@@ -620,25 +621,25 @@ static void update_divide_count(struct kvm_lapic *apic) ...@@ -620,25 +621,25 @@ static void update_divide_count(struct kvm_lapic *apic)
tdcr = apic_get_reg(apic, APIC_TDCR); tdcr = apic_get_reg(apic, APIC_TDCR);
tmp1 = tdcr & 0xf; tmp1 = tdcr & 0xf;
tmp2 = ((tmp1 & 0x3) | ((tmp1 & 0x8) >> 1)) + 1; tmp2 = ((tmp1 & 0x3) | ((tmp1 & 0x8) >> 1)) + 1;
apic->timer.divide_count = 0x1 << (tmp2 & 0x7); apic->divide_count = 0x1 << (tmp2 & 0x7);
apic_debug("timer divide count is 0x%x\n", apic_debug("timer divide count is 0x%x\n",
apic->timer.divide_count); apic->lapic_timer.divide_count);
} }
static void start_apic_timer(struct kvm_lapic *apic) static void start_apic_timer(struct kvm_lapic *apic)
{ {
ktime_t now = apic->timer.dev.base->get_time(); ktime_t now = apic->lapic_timer.timer.base->get_time();
apic->timer.period = apic_get_reg(apic, APIC_TMICT) * apic->lapic_timer.period = apic_get_reg(apic, APIC_TMICT) *
APIC_BUS_CYCLE_NS * apic->timer.divide_count; APIC_BUS_CYCLE_NS * apic->divide_count;
atomic_set(&apic->timer.pending, 0); atomic_set(&apic->lapic_timer.pending, 0);
if (!apic->timer.period) if (!apic->lapic_timer.period)
return; return;
hrtimer_start(&apic->timer.dev, hrtimer_start(&apic->lapic_timer.timer,
ktime_add_ns(now, apic->timer.period), ktime_add_ns(now, apic->lapic_timer.period),
HRTIMER_MODE_ABS); HRTIMER_MODE_ABS);
apic_debug("%s: bus cycle is %" PRId64 "ns, now 0x%016" apic_debug("%s: bus cycle is %" PRId64 "ns, now 0x%016"
...@@ -647,9 +648,9 @@ static void start_apic_timer(struct kvm_lapic *apic) ...@@ -647,9 +648,9 @@ static void start_apic_timer(struct kvm_lapic *apic)
"expire @ 0x%016" PRIx64 ".\n", __func__, "expire @ 0x%016" PRIx64 ".\n", __func__,
APIC_BUS_CYCLE_NS, ktime_to_ns(now), APIC_BUS_CYCLE_NS, ktime_to_ns(now),
apic_get_reg(apic, APIC_TMICT), apic_get_reg(apic, APIC_TMICT),
apic->timer.period, apic->lapic_timer.period,
ktime_to_ns(ktime_add_ns(now, ktime_to_ns(ktime_add_ns(now,
apic->timer.period))); apic->lapic_timer.period)));
} }
static void apic_manage_nmi_watchdog(struct kvm_lapic *apic, u32 lvt0_val) static void apic_manage_nmi_watchdog(struct kvm_lapic *apic, u32 lvt0_val)
...@@ -731,7 +732,7 @@ static void apic_mmio_write(struct kvm_io_device *this, ...@@ -731,7 +732,7 @@ static void apic_mmio_write(struct kvm_io_device *this,
apic_set_reg(apic, APIC_LVTT + 0x10 * i, apic_set_reg(apic, APIC_LVTT + 0x10 * i,
lvt_val | APIC_LVT_MASKED); lvt_val | APIC_LVT_MASKED);
} }
atomic_set(&apic->timer.pending, 0); atomic_set(&apic->lapic_timer.pending, 0);
} }
break; break;
...@@ -763,7 +764,7 @@ static void apic_mmio_write(struct kvm_io_device *this, ...@@ -763,7 +764,7 @@ static void apic_mmio_write(struct kvm_io_device *this,
break; break;
case APIC_TMICT: case APIC_TMICT:
hrtimer_cancel(&apic->timer.dev); hrtimer_cancel(&apic->lapic_timer.timer);
apic_set_reg(apic, APIC_TMICT, val); apic_set_reg(apic, APIC_TMICT, val);
start_apic_timer(apic); start_apic_timer(apic);
return; return;
...@@ -803,7 +804,7 @@ void kvm_free_lapic(struct kvm_vcpu *vcpu) ...@@ -803,7 +804,7 @@ void kvm_free_lapic(struct kvm_vcpu *vcpu)
if (!vcpu->arch.apic) if (!vcpu->arch.apic)
return; return;
hrtimer_cancel(&vcpu->arch.apic->timer.dev); hrtimer_cancel(&vcpu->arch.apic->lapic_timer.timer);
if (vcpu->arch.apic->regs_page) if (vcpu->arch.apic->regs_page)
__free_page(vcpu->arch.apic->regs_page); __free_page(vcpu->arch.apic->regs_page);
...@@ -881,7 +882,7 @@ void kvm_lapic_reset(struct kvm_vcpu *vcpu) ...@@ -881,7 +882,7 @@ void kvm_lapic_reset(struct kvm_vcpu *vcpu)
ASSERT(apic != NULL); ASSERT(apic != NULL);
/* Stop the timer in case it's a reset to an active apic */ /* Stop the timer in case it's a reset to an active apic */
hrtimer_cancel(&apic->timer.dev); hrtimer_cancel(&apic->lapic_timer.timer);
apic_set_reg(apic, APIC_ID, vcpu->vcpu_id << 24); apic_set_reg(apic, APIC_ID, vcpu->vcpu_id << 24);
apic_set_reg(apic, APIC_LVR, APIC_VERSION); apic_set_reg(apic, APIC_LVR, APIC_VERSION);
...@@ -906,7 +907,7 @@ void kvm_lapic_reset(struct kvm_vcpu *vcpu) ...@@ -906,7 +907,7 @@ void kvm_lapic_reset(struct kvm_vcpu *vcpu)
apic_set_reg(apic, APIC_TMR + 0x10 * i, 0); apic_set_reg(apic, APIC_TMR + 0x10 * i, 0);
} }
update_divide_count(apic); update_divide_count(apic);
atomic_set(&apic->timer.pending, 0); atomic_set(&apic->lapic_timer.pending, 0);
if (vcpu->vcpu_id == 0) if (vcpu->vcpu_id == 0)
vcpu->arch.apic_base |= MSR_IA32_APICBASE_BSP; vcpu->arch.apic_base |= MSR_IA32_APICBASE_BSP;
apic_update_ppr(apic); apic_update_ppr(apic);
...@@ -937,22 +938,11 @@ EXPORT_SYMBOL_GPL(kvm_lapic_enabled); ...@@ -937,22 +938,11 @@ EXPORT_SYMBOL_GPL(kvm_lapic_enabled);
*---------------------------------------------------------------------- *----------------------------------------------------------------------
*/ */
/* TODO: make sure __apic_timer_fn runs in current pCPU */ static bool lapic_is_periodic(struct kvm_timer *ktimer)
static int __apic_timer_fn(struct kvm_lapic *apic)
{ {
int result = 0; struct kvm_lapic *apic = container_of(ktimer, struct kvm_lapic,
wait_queue_head_t *q = &apic->vcpu->wq; lapic_timer);
return apic_lvtt_period(apic);
if(!atomic_inc_and_test(&apic->timer.pending))
set_bit(KVM_REQ_PENDING_TIMER, &apic->vcpu->requests);
if (waitqueue_active(q))
wake_up_interruptible(q);
if (apic_lvtt_period(apic)) {
result = 1;
hrtimer_add_expires_ns(&apic->timer.dev, apic->timer.period);
}
return result;
} }
int apic_has_pending_timer(struct kvm_vcpu *vcpu) int apic_has_pending_timer(struct kvm_vcpu *vcpu)
...@@ -960,7 +950,7 @@ int apic_has_pending_timer(struct kvm_vcpu *vcpu) ...@@ -960,7 +950,7 @@ int apic_has_pending_timer(struct kvm_vcpu *vcpu)
struct kvm_lapic *lapic = vcpu->arch.apic; struct kvm_lapic *lapic = vcpu->arch.apic;
if (lapic && apic_enabled(lapic) && apic_lvt_enabled(lapic, APIC_LVTT)) if (lapic && apic_enabled(lapic) && apic_lvt_enabled(lapic, APIC_LVTT))
return atomic_read(&lapic->timer.pending); return atomic_read(&lapic->lapic_timer.pending);
return 0; return 0;
} }
...@@ -987,20 +977,9 @@ void kvm_apic_nmi_wd_deliver(struct kvm_vcpu *vcpu) ...@@ -987,20 +977,9 @@ void kvm_apic_nmi_wd_deliver(struct kvm_vcpu *vcpu)
kvm_apic_local_deliver(apic, APIC_LVT0); kvm_apic_local_deliver(apic, APIC_LVT0);
} }
static enum hrtimer_restart apic_timer_fn(struct hrtimer *data) struct kvm_timer_ops lapic_timer_ops = {
{ .is_periodic = lapic_is_periodic,
struct kvm_lapic *apic; };
int restart_timer = 0;
apic = container_of(data, struct kvm_lapic, timer.dev);
restart_timer = __apic_timer_fn(apic);
if (restart_timer)
return HRTIMER_RESTART;
else
return HRTIMER_NORESTART;
}
int kvm_create_lapic(struct kvm_vcpu *vcpu) int kvm_create_lapic(struct kvm_vcpu *vcpu)
{ {
...@@ -1025,8 +1004,13 @@ int kvm_create_lapic(struct kvm_vcpu *vcpu) ...@@ -1025,8 +1004,13 @@ int kvm_create_lapic(struct kvm_vcpu *vcpu)
memset(apic->regs, 0, PAGE_SIZE); memset(apic->regs, 0, PAGE_SIZE);
apic->vcpu = vcpu; apic->vcpu = vcpu;
hrtimer_init(&apic->timer.dev, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); hrtimer_init(&apic->lapic_timer.timer, CLOCK_MONOTONIC,
apic->timer.dev.function = apic_timer_fn; HRTIMER_MODE_ABS);
apic->lapic_timer.timer.function = kvm_timer_fn;
apic->lapic_timer.t_ops = &lapic_timer_ops;
apic->lapic_timer.kvm = vcpu->kvm;
apic->lapic_timer.vcpu_id = vcpu->vcpu_id;
apic->base_address = APIC_DEFAULT_PHYS_BASE; apic->base_address = APIC_DEFAULT_PHYS_BASE;
vcpu->arch.apic_base = APIC_DEFAULT_PHYS_BASE; vcpu->arch.apic_base = APIC_DEFAULT_PHYS_BASE;
...@@ -1079,9 +1063,9 @@ void kvm_inject_apic_timer_irqs(struct kvm_vcpu *vcpu) ...@@ -1079,9 +1063,9 @@ void kvm_inject_apic_timer_irqs(struct kvm_vcpu *vcpu)
{ {
struct kvm_lapic *apic = vcpu->arch.apic; struct kvm_lapic *apic = vcpu->arch.apic;
if (apic && atomic_read(&apic->timer.pending) > 0) { if (apic && atomic_read(&apic->lapic_timer.pending) > 0) {
if (kvm_apic_local_deliver(apic, APIC_LVTT)) if (kvm_apic_local_deliver(apic, APIC_LVTT))
atomic_dec(&apic->timer.pending); atomic_dec(&apic->lapic_timer.pending);
} }
} }
...@@ -1107,7 +1091,7 @@ void kvm_apic_post_state_restore(struct kvm_vcpu *vcpu) ...@@ -1107,7 +1091,7 @@ void kvm_apic_post_state_restore(struct kvm_vcpu *vcpu)
MSR_IA32_APICBASE_BASE; MSR_IA32_APICBASE_BASE;
apic_set_reg(apic, APIC_LVR, APIC_VERSION); apic_set_reg(apic, APIC_LVR, APIC_VERSION);
apic_update_ppr(apic); apic_update_ppr(apic);
hrtimer_cancel(&apic->timer.dev); hrtimer_cancel(&apic->lapic_timer.timer);
update_divide_count(apic); update_divide_count(apic);
start_apic_timer(apic); start_apic_timer(apic);
} }
...@@ -1120,7 +1104,7 @@ void __kvm_migrate_apic_timer(struct kvm_vcpu *vcpu) ...@@ -1120,7 +1104,7 @@ void __kvm_migrate_apic_timer(struct kvm_vcpu *vcpu)
if (!apic) if (!apic)
return; return;
timer = &apic->timer.dev; timer = &apic->lapic_timer.timer;
if (hrtimer_cancel(timer)) if (hrtimer_cancel(timer))
hrtimer_start_expires(timer, HRTIMER_MODE_ABS); hrtimer_start_expires(timer, HRTIMER_MODE_ABS);
} }
......
...@@ -2,18 +2,15 @@ ...@@ -2,18 +2,15 @@
#define __KVM_X86_LAPIC_H #define __KVM_X86_LAPIC_H
#include "iodev.h" #include "iodev.h"
#include "kvm_timer.h"
#include <linux/kvm_host.h> #include <linux/kvm_host.h>
struct kvm_lapic { struct kvm_lapic {
unsigned long base_address; unsigned long base_address;
struct kvm_io_device dev; struct kvm_io_device dev;
struct { struct kvm_timer lapic_timer;
atomic_t pending; u32 divide_count;
s64 period; /* unit: ns */
u32 divide_count;
struct hrtimer dev;
} timer;
struct kvm_vcpu *vcpu; struct kvm_vcpu *vcpu;
struct page *regs_page; struct page *regs_page;
void *regs; void *regs;
......
#include <linux/kvm_host.h>
#include <linux/kvm.h>
#include <linux/hrtimer.h>
#include <asm/atomic.h>
#include "kvm_timer.h"
static int __kvm_timer_fn(struct kvm_vcpu *vcpu, struct kvm_timer *ktimer)
{
int restart_timer = 0;
wait_queue_head_t *q = &vcpu->wq;
/* FIXME: this code should not know anything about vcpus */
if (!atomic_inc_and_test(&ktimer->pending))
set_bit(KVM_REQ_PENDING_TIMER, &vcpu->requests);
if (!ktimer->reinject)
atomic_set(&ktimer->pending, 1);
if (waitqueue_active(q))
wake_up_interruptible(q);
if (ktimer->t_ops->is_periodic(ktimer)) {
hrtimer_add_expires_ns(&ktimer->timer, ktimer->period);
restart_timer = 1;
}
return restart_timer;
}
enum hrtimer_restart kvm_timer_fn(struct hrtimer *data)
{
int restart_timer;
struct kvm_vcpu *vcpu;
struct kvm_timer *ktimer = container_of(data, struct kvm_timer, timer);
vcpu = ktimer->kvm->vcpus[ktimer->vcpu_id];
if (!vcpu)
return HRTIMER_NORESTART;
restart_timer = __kvm_timer_fn(vcpu, ktimer);
if (restart_timer)
return HRTIMER_RESTART;
else
return HRTIMER_NORESTART;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment