KVM: In kernel pit model
PATCH 1/6 KVM: In kernel pit model
From f389ff3bc388888b1684f15d8073c6d9abd2f2c9 Mon Sep 17 00:00:00 2001
From: Sheng Yang <[email protected]>
Date: Mon, 28 Jan 2008 05:10:22 +0800
Subject: [PATCH] KVM: In kernel pit model
The patch moved PIT from userspace to kernel, and increase the timer accuracy greatly.
--- arch/x86/kvm/Makefile | 3 +- arch/x86/kvm/i8254.c | 583 ++++++++++++++++++++++++++++++++++++++++++++ arch/x86/kvm/i8254.h | 59 +++++ arch/x86/kvm/irq.c | 3 + arch/x86/kvm/x86.c | 9 + include/asm-x86/kvm_host.h | 1 + include/linux/kvm.h | 2 + 7 files changed, 659 insertions(+), 1 deletions(-) create mode 100644 arch/x86/kvm/i8254.c create mode 100644 arch/x86/kvm/i8254.h
arch/x86/kvm/Makefile +0 -0
@@ -6,7 +6,8 @@ common-objs = $(addprefix ../../../virt/kvm/, kvm_main.o
ioapic.o)
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
obj-$(CONFIG_KVM) += kvm.o
kvm-intel-objs = vmx.o
obj-$(CONFIG_KVM_INTEL) += kvm-intel.o
arch/x86/kvm/i8254.c new file +9 -0
@@ -0,0 +1,583 @@ +/* +* 8253/8254 interval timer emulation +* +* Copyright (c) 2003-2004 Fabrice Bellard +* Copyright (c) 2006 Intel Corporation +* Copyright (c) 2007 Keir Fraser, XenSource Inc +* Copyright (c) 2008 Intel Corporation +* +* Permission is hereby granted, free of charge, to any person obtaining a
copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * Authors:
+ * Sheng Yang <[email protected]>
+ * Based on QEMU and Xen.
+ */
+
+#include <linux/kvm_host.h>
+
+#include "irq.h"
+#include "i8254.h"
+
+#if 0
+#define pit_debug(fmt, arg...) printk(KERN_WARNING fmt, ##arg) +#else
+#define pit_debug(fmt, arg...)
+#endif
+
+#ifndef CONFIG_X86_64
+#define mod_64(x, y) ((x) - (y) * div64_64(x, y))
+#else
+#define mod_64(x, y) ((x) % (y))
+#endif
+
+#define RW_STATE_LSB 1
+#define RW_STATE_MSB 2
+#define RW_STATE_WORD0 3
+#define RW_STATE_WORD1 4
+
+/* Compute with 96 bit intermediate result: (a*b)/c */
+static u64 muldiv64(u64 a, u32 b, u32 c)
+{
+ union {
+ u64 ll;
+ struct {
+ u32 low, high;
+ } l;
+ } u, res;
+ u64 rl, rh;
+
+ u.ll = a;
+ rl = (u64)u.l.low * (u64)b;
+ rh = (u64)u.l.high * (u64)b;
+ rh += (rl >> 32);
+ res.l.high = div64_64(rh, c);
+ res.l.low = div64_64(((mod_64(rh, c) << 32) + (rl & 0xffffffff)), c);
+ return res.ll;
+}
+
+static void pit_set_gate(struct kvm *kvm, int channel, u32 val) +{
+ struct kvm_kpit_channel_state *c =
+ &kvm->arch.vpit->pit_state.channels[channel];
+
+ ASSERT(mutex_is_locked(&kvm->arch.vpit->pit_state.lock));
+
+ switch (c->mode) {
+ default:
+ case 0:
+ case 4:
+ /* XXX: just disable/enable counting */
+ break;
+ case 1:
+ case 2:
+ case 3:
+ case 5:
+ /* Restart counting on rising edge. */
+ if (c->gate < val)
+ c->count_load_time = ktime_get();
+ break;
+ }
+
+ c->gate = val;
+}
+
+int pit_get_gate(struct kvm *kvm, int channel)
+{
+ ASSERT(mutex_is_locked(&kvm->arch.vpit->pit_state.lock));
+
+ return kvm->arch.vpit->pit_state.channels[channel].gate;
+}
+
+static int pit_get_count(struct kvm *kvm, int channel)
+{
+ struct kvm_kpit_channel_state *c =
+ &kvm->arch.vpit->pit_state.channels[channel];
+ s64 d, t;
+ int counter;
+
+ ASSERT(mutex_is_locked(&kvm->arch.vpit->pit_state.lock));
+
+ t = ktime_to_ns(ktime_sub(ktime_get(), c->count_load_time));
+ d = muldiv64(t, PIT_FREQ, NSEC_PER_SEC);
+
+ switch (c->mode) {
+ case 0:
+ case 1:
+ case 4:
+ case 5:
+ counter = (c->count - d) & 0xffff;
+ break;
+ case 3:
+ /* XXX: may be incorrect for odd counts */
+ counter = c->count - (mod_64((2 * d), c->count));
+ break;
+ default:
+ counter = c->count - mod_64(d, c->count);
+ break;
+ }
+ return counter;
+}
+
+static int pit_get_out(struct kvm *kvm, int channel)
+{
+ struct kvm_kpit_channel_state *c =
+ &kvm->arch.vpit->pit_state.channels[channel];
+ s64 d, t;
+ int out;
+
+ ASSERT(mutex_is_locked(&kvm->arch.vpit->pit_state.lock));
+
+ t = ktime_to_ns(ktime_sub(ktime_get(), c->count_load_time));
+ d = muldiv64(t, PIT_FREQ, 1e9);
+
+ switch (c->mode) {
+ default:
+ case 0:
+ out = (d >= c->count);
+ break;
+ case 1:
+ out = (d < c->count);
+ break;
+ case 2:
+ out = ((mod_64(d, c->count) == 0) && (d != 0));
+ break;
+ case 3:
+ out = (mod_64(d, c->count) < ((c->count + 1) >> 1));
+ break;
+ case 4:
+ case 5:
+ out = (d == c->count);
+ break;
+ }
+
+ return out;
+}
+
+static void pit_latch_count(struct kvm *kvm, int channel)
+{
+ struct kvm_kpit_channel_state *c =
+ &kvm->arch.vpit->pit_state.channels[channel];
+
+ ASSERT(mutex_is_locked(&kvm->arch.vpit->pit_state.lock));
+
+ if (!c->count_latched) {
+ c->latched_count = pit_get_count(kvm, channel);
+ c->count_latched = c->rw_mode;
+ }
+}
+
+static void pit_latch_status(struct kvm *kvm, int channel)
+{
+ struct kvm_kpit_channel_state *c =
+ &kvm->arch.vpit->pit_state.channels[channel];
+
+ ASSERT(mutex_is_locked(&kvm->arch.vpit->pit_state.lock));
+
+ if (!c->status_latched) {
+ /* TODO: Return NULL COUNT (bit 6). */
+ c->status = ((pit_get_out(kvm, channel) << 7) |
+ (c->rw_mode << 4) |
+ (c->mode << 1) |
+ c->bcd);
+ c->status_latched = 1;
+ }
+}
+
+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;
+
+ atomic_inc(&pt->pending);
+ if (vcpu0 && waitqueue_active(&vcpu0->wq)) {
+ vcpu0->arch.mp_state = VCPU_MP_STATE_RUNNABLE;
+ wake_up_interruptible(&vcpu0->wq);
+ }
+
+ pt->timer.expires = ktime_add_ns(pt->timer.expires, pt->period);
+ pt->scheduled = ktime_to_ns(pt->timer.expires);
+
+ return (pt->period == 0 ? 0 : 1);
+}
+
+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;
+}
+
+static void destroy_pit_timer(struct kvm_kpit_timer *pt)
+{
+ pit_debug("pit: execute del timer!\n");
+ hrtimer_cancel(&pt->timer);
+}
+
+static void create_pit_timer(struct kvm_kpit_timer *pt, u32 val, int is_period)
+{
+ s64 interval;
+
+ interval = muldiv64(val, 1e9, PIT_FREQ);
+
+ pit_debug("pit: create pit timer, interval is %llu nsec\n", interval);
+
+ /* TODO The new value only affected after the retriggered */
+ hrtimer_cancel(&pt->timer);
+ pt->period = (is_period == 0) ? 0 : interval;
+ pt->timer.function = pit_timer_fn;
+ atomic_set(&pt->pending, 0);
+
+ hrtimer_start(&pt->timer, ktime_add_ns(ktime_get(), interval),
+ HRTIMER_MODE_ABS);
+}
+
+static void pit_load_count(struct kvm *kvm, int channel, u32 val) +{
+ struct kvm_kpit_state *ps = &kvm->arch.vpit->pit_state;
+
+ ASSERT(mutex_is_locked(&ps->lock));
+
+ pit_debug("pit: load_count val is %d, channel is %d\n", val, channel);
+
+ if (val == 0)
+ val = 0x10000;
+
+ ps->channels[channel].count_load_time = ktime_get();
+ ps->channels[channel].count = val;
+
+ if (channel != 0)
+ return;
+
+ /* Two types of timer
+ * mode 1 is one shot, mode 2 is period, otherwise del timer */
+ switch (ps->channels[0].mode) {
+ case 1:
+ create_pit_timer(&ps->pit_timer, val, 0);
+ break;
+ case 2:
+ create_pit_timer(&ps->pit_timer, val, 1);
+ break;
+ default:
+ destroy_pit_timer(&ps->pit_timer);
+ }
+}
+
+static void pit_ioport_write(struct kvm_io_device *this,
+ gpa_t addr, int len, const void *data)
+{
+ struct kvm_pit *pit = (struct kvm_pit *)this->private;
+ struct kvm_kpit_state *pit_state = &pit->pit_state;
+ struct kvm *kvm = pit->kvm;
+ int channel, access;
+ struct kvm_kpit_channel_state *s;
+ u32 val = *(u32 *) data;
+
+ val &= 0xff;
+ addr &= 3;
+
+ mutex_lock(&pit_state->lock);
+
+ if (val != 0)
+ pit_debug("pit: write addr is 0x%x, len is %d, val is 0x%x\n",
+ (unsigned int)addr, len, val);
+
+ if (addr == 3) {
+ channel = val >> 6;
+ if (channel == 3) {
+ /* Read-Back Command. */
+ for (channel = 0; channel < 3; channel++) {
+ s = &pit_state->channels[channel];
+ if (val & (2 << channel)) {
+ if (!(val & 0x20))
+ pit_latch_count(kvm, channel);
+ if (!(val & 0x10))
+ pit_latch_status(kvm, channel);
+ }
+ }
+ } else {
+ /* Select Counter <channel>. */
+ s = &pit_state->channels[channel];
+ access = (val >> 4) & 3;
+ if (access == 0) {
+ pit_latch_count(kvm, channel);
+ } else {
+ s->rw_mode = access;
+ s->read_state = access;
+ s->write_state = access;
+ s->mode = (val >> 1) & 7;
+ if (s->mode > 5)
+ s->mode -= 4;
+ s->bcd = val & 1;
+ }
+ }
+ } else {
+ /* Write Count. */
+ s = &pit_state->channels[addr];
+ switch (s->write_state) {
+ default:
+ case RW_STATE_LSB:
+ pit_load_count(kvm, addr, val);
+ break;
+ case RW_STATE_MSB:
+ pit_load_count(kvm, addr, val << 8);
+ break;
+ case RW_STATE_WORD0:
+ s->write_latch = val;
+ s->write_state = RW_STATE_WORD1;
+ break;
+ case RW_STATE_WORD1:
+ pit_load_count(kvm, addr, s->write_latch | (val << 8));
+ s->write_state = RW_STATE_WORD0;
+ break;
+ }
+ }
+
+ mutex_unlock(&pit_state->lock);
+}
+
+static void pit_ioport_read(struct kvm_io_device *this,
+ gpa_t addr, int len, void *data)
+{
+ struct kvm_pit *pit = (struct kvm_pit *)this->private;
+ struct kvm_kpit_state *pit_state = &pit->pit_state;
+ struct kvm *kvm = pit->kvm;
+ int ret, count;
+ struct kvm_kpit_channel_state *s;
+
+ addr &= 3;
+ s = &pit_state->channels[addr];
+
+ mutex_lock(&pit_state->lock);
+
+ if (s->status_latched) {
+ s->status_latched = 0;
+ ret = s->status;
+ } else if (s->count_latched) {
+ switch (s->count_latched) {
+ default:
+ case RW_STATE_LSB:
+ ret = s->latched_count & 0xff;
+ s->count_latched = 0;
+ break;
+ case RW_STATE_MSB:
+ ret = s->latched_count >> 8;
+ s->count_latched = 0;
+ break;
+ case RW_STATE_WORD0:
+ ret = s->latched_count & 0xff;
+ s->count_latched = RW_STATE_MSB;
+ break;
+ }
+ } else {
+ switch (s->read_state) {
+ default:
+ case RW_STATE_LSB:
+ count = pit_get_count(kvm, addr);
+ ret = count & 0xff;
+ break;
+ case RW_STATE_MSB:
+ count = pit_get_count(kvm, addr);
+ ret = (count >> 8) & 0xff;
+ break;
+ case RW_STATE_WORD0:
+ count = pit_get_count(kvm, addr);
+ ret = count & 0xff;
+ s->read_state = RW_STATE_WORD1;
+ break;
+ case RW_STATE_WORD1:
+ count = pit_get_count(kvm, addr);
+ ret = (count >> 8) & 0xff;
+ s->read_state = RW_STATE_WORD0;
+ break;
+ }
+ }
+
+ if (len > sizeof(ret))
+ len = sizeof(ret);
+ memcpy(data, (char *)&ret, len);
+
+ mutex_unlock(&pit_state->lock);
+}
+
+static int pit_in_range(struct kvm_io_device *this, gpa_t addr) +{
+ return ((addr >= PIT_BASE_ADDRESS) &&
+ (addr < PIT_BASE_ADDRESS + PIT_MEM_LENGTH));
+}
+
+static void speaker_ioport_write(struct kvm_io_device *this,
+ gpa_t addr, int len, const void *data)
+{
+ struct kvm_pit *pit = (struct kvm_pit *)this->private;
+ struct kvm_kpit_state *pit_state = &pit->pit_state;
+ struct kvm *kvm = pit->kvm;
+ u32 val = *(u32 *) data;
+
+ mutex_lock(&pit_state->lock);
+ pit_state->speaker_data_on = (val >> 1) & 1;
+ pit_set_gate(kvm, 2, val & 1);
+ mutex_unlock(&pit_state->lock);
+}
+
+static void speaker_ioport_read(struct kvm_io_device *this,
+ gpa_t addr, int len, void *data)
+{
+ struct kvm_pit *pit = (struct kvm_pit *)this->private;
+ struct kvm_kpit_state *pit_state = &pit->pit_state;
+ struct kvm *kvm = pit->kvm;
+ unsigned int refresh_clock;
+ int ret;
+
+ /* Refresh clock toggles at about 15us. We approximate as 2^14ns. */
+ refresh_clock = ((unsigned int)ktime_to_ns(ktime_get()) >> 14) & 1;
+
+ mutex_lock(&pit_state->lock);
+ ret = ((pit_state->speaker_data_on << 1) | pit_get_gate(kvm, 2) |
+ (pit_get_out(kvm, 2) << 5) | (refresh_clock << 4));
+ if (len > sizeof(ret))
+ len = sizeof(ret);
+ memcpy(data, (char *)&ret, len);
+ mutex_unlock(&pit_state->lock);
+}
+
+static int speaker_in_range(struct kvm_io_device *this, gpa_t addr) +{
+ return (addr == SPEAKER_BASE_ADDRESS);
+}
+
+struct kvm_pit *kvm_create_pit(struct kvm *kvm)
+{
+ int i;
+ struct kvm_pit *pit;
+ struct kvm_kpit_state *pit_state;
+ struct kvm_kpit_channel_state *c;
+
+ pit = kzalloc(sizeof(struct kvm_pit), GFP_KERNEL);
+ if (!pit)
+ return NULL;
+
+ mutex_init(&pit->pit_state.lock);
+ mutex_lock(&pit->pit_state.lock);
+
+ /* Initialize PIO device */
+ pit->dev.read = pit_ioport_read;
+ pit->dev.write = pit_ioport_write;
+ pit->dev.in_range = pit_in_range;
+ pit->dev.private = pit;
+ kvm_io_bus_register_dev(&kvm->pio_bus, &pit->dev);
+
+ pit->speaker_dev.read = speaker_ioport_read;
+ pit->speaker_dev.write = speaker_ioport_write;
+ pit->speaker_dev.in_range = speaker_in_range;
+ pit->speaker_dev.private = pit;
+ kvm_io_bus_register_dev(&kvm->pio_bus, &pit->speaker_dev);
+
+ kvm->arch.vpit = pit;
+ pit->kvm = kvm;
+
+ pit_state = &pit->pit_state;
+ pit_state->pit = pit;
+ hrtimer_init(&pit_state->pit_timer.timer,
+ CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
+ atomic_set(&pit_state->pit_timer.pending, 0);
+ for (i = 0; i < 3; i++) {
+ c = &pit_state->channels[i];
+ c->mode = 0xff;
+ c->gate = (i != 2);
+ pit_load_count(kvm, i, 0);
+ }
+
+ mutex_unlock(&pit->pit_state.lock);
+
+ pit->pit_state.inject_pending = 1;
+
+ return pit;
+}
+
+void kvm_free_pit(struct kvm *kvm)
+{
+ struct hrtimer *timer;
+
+ if (kvm->arch.vpit) {
+ mutex_lock(&kvm->arch.vpit->pit_state.lock);
+ timer = &kvm->arch.vpit->pit_state.pit_timer.timer;
+ hrtimer_cancel(timer);
+ mutex_unlock(&kvm->arch.vpit->pit_state.lock);
+ kfree(kvm->arch.vpit);
+ }
+}
+
+void __inject_pit_timer_intr(struct kvm *kvm)
+{
+ mutex_lock(&kvm->lock);
+ kvm_ioapic_set_irq(kvm->arch.vioapic, 0, 1);
+ kvm_ioapic_set_irq(kvm->arch.vioapic, 0, 0);
+ kvm_pic_set_irq(pic_irqchip(kvm), 0, 1);
+ kvm_pic_set_irq(pic_irqchip(kvm), 0, 0);
+ mutex_unlock(&kvm->lock);
+}
+
+void kvm_inject_pit_timer_irqs(struct kvm_vcpu *vcpu)
+{
+ struct kvm_pit *pit = vcpu->kvm->arch.vpit;
+ struct kvm *kvm = vcpu->kvm;
+ struct kvm_kpit_state *ps;
+ static unsigned long last_injected_time;
+
+ if (vcpu && pit) {
+ ps = &pit->pit_state;
+ /* Try to inject pending interrupts when:
+ * 1. Pending exists
+ * 2. Last interrupt was accepted or waited for too long time*/
+ if (atomic_read(&ps->pit_timer.pending) &&
+ (ps->inject_pending ||
+ (jiffies - last_injected_time >= MAX_PIT_INTR_INTERVAL))) {
+ ps->inject_pending = 0;
+ __inject_pit_timer_intr(kvm);
+ last_injected_time = jiffies;
+ }
+ }
+}
+
+void kvm_pit_timer_intr_post(struct kvm_vcpu *vcpu, int vec)
+{
+ struct kvm_arch *arch = &vcpu->kvm->arch;
+ struct kvm_kpit_state *ps;
+
+ if (vcpu && arch->vpit) {
+ ps = &arch->vpit->pit_state;
+ if (atomic_read(&ps->pit_timer.pending) &&
+ (((arch->vpic->pics[0].imr & 1) == 0 &&
+ arch->vpic->pics[0].irq_base == vec) ||
+ (arch->vioapic->redirtbl[0].fields.vector == vec &&
+ arch->vioapic->redirtbl[0].fields.mask != 1))) {+ ps->inject_pending = 1;
+ atomic_dec(&ps->pit_timer.pending);
+ ps->channels[0].count_load_time = ktime_get();
+ }
+ }
+}
arch/x86/kvm/i8254.h new file +59 -0
59 lines not on this page · raw message
arch/x86/kvm/irq.c +3 -0
@@ -23,6 +23,7 @@#include <linux/kvm_host.h> #include "irq.h" +#include "i8254.h" /* * check if there is pending interrupt without
@@ -66,6 +67,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);/* TODO: PIT, RTC etc. */ }EXPORT_SYMBOL_GPL (kvm_inject_pending_timer_irqs);
@@ -73,6 +75,7 @@ EXPORT_SYMBOL_GPL(kvm_inject_pending_timer_irqs);void kvm_timer_intr_post (struct kvm_vcpu *vcpu,int vec) {kvm_apic_timer_intr_post (vcpu, vec); +kvm_pit_timer_intr_post (vcpu, vec);/* TODO: PIT, RTC etc. */ }EXPORT_SYMBOL_GPL (kvm_timer_intr_post);
arch/x86/kvm/x86.c +9 -0
@@ -17,6 +17,7 @@#include <linux/kvm_host.h> #include "irq.h" #include "mmu.h" +#include "i8254.h" #include <linux/clocksource.h> #include <linux/kvm.h>
@@ -805,6 +806,7 @@ int kvm_dev_ioctl_check_extension(long ext)case KVM_CAP_SET_TSS_ADDR :case KVM_CAP_EXT_CPUID :case KVM_CAP_CLOCKSOURCE : +case KVM_CAP_PIT : r =1 ;break ;case KVM_CAP_VAPIC :
@@ -1581,6 +1583,12 @@ long kvm_arch_vm_ioctl(struct file *filp, }else goto out;break ; +case KVM_CREATE_PIT : + r = -ENOMEM ; + kvm->arch.vpit =kvm_create_pit (kvm); +if (kvm->arch.vpit) + r =0 ; +break ;case KVM_IRQ_LINE : {struct kvm_irq_level irq_event;
@@ -3359,6 +3367,7 @@ static void kvm_free_vcpus(struct kvm *kvm)void kvm_arch_destroy_vm (struct kvm *kvm) { +kvm_free_pit (kvm);kfree (kvm->arch.vpic);kfree (kvm->arch.vioapic);kvm_free_vcpus (kvm);
include/asm-x86/kvm_host.h +1 -0
@@ -297,6 +297,7 @@ struct kvm_arch{struct list_head active_mmu_pages;struct kvm_pic *vpic;struct kvm_ioapic *vioapic; +struct kvm_pit *vpit;int round_robin_prev_vcpu;unsigned int tss_addr;
include/linux/kvm.h +2 -0
@@ -236,6 +236,7 @@ struct kvm_vapic_addr {#define KVM_CAP_CLOCKSOURCE 8#define KVM_CAP_NR_VCPUS 9/* returns max vcpus per vm */ #define KVM_CAP_NR_MEMSLOTS 10/* returns max memory slots per vm */ +#define KVM_CAP_PIT 11/* * ioctls for VM fds
@@ -258,6 +259,7 @@ struct kvm_vapic_addr {#define KVM_IRQ_LINE _IOW(KVMIO, 0x61, struct kvm_irq_level)#define KVM_GET_IRQCHIP _IOWR(KVMIO, 0x62, struct kvm_irqchip)#define KVM_SET_IRQCHIP _IOR(KVMIO, 0x63, struct kvm_irqchip) +#define KVM_CREATE_PIT _IO(KVMIO, 0x64)/* * ioctls for vcpu fds
--
debian.1.5.3.7.1-dirty
From f389ff3bc388888b1684f15d8073c6d9abd2f2c9 Mon Sep 17 00:00:00 2001
From: Sheng Yang <[email protected]>
Date: Mon, 28 Jan 2008 05:10:22 +0800
Subject: [PATCH] In kernel pit model
The patch moved PIT from userspace to kernel, and increase the timer accuracy greatly.
--- arch/x86/kvm/Makefile | 3 +- arch/x86/kvm/i8254.c | 583 ++++++++++++++++++++++++++++++++++++++++++++ arch/x86/kvm/i8254.h | 59 +++++ arch/x86/kvm/irq.c | 3 + arch/x86/kvm/x86.c | 9 + include/asm-x86/kvm_host.h | 1 + include/linux/kvm.h | 2 + 7 files changed, 659 insertions(+), 1 deletions(-) create mode 100644 arch/x86/kvm/i8254.c create mode 100644 arch/x86/kvm/i8254.h
arch/x86/kvm/Makefile +2 -1
@@ -6,7 +6,8 @@ common-objs = $(addprefix ../../../virt/kvm/, kvm_main.o ioapic.o) 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 obj-$(CONFIG_KVM) += kvm.o kvm-intel-objs = vmx.o obj-$(CONFIG_KVM_INTEL) += kvm-intel.o
arch/x86/kvm/i8254.c new file +583 -0
583 lines not on this page · raw message
arch/x86/kvm/i8254.h new file +59 -0
59 lines not on this page · raw message
arch/x86/kvm/irq.c +3 -0
@@ -23,6 +23,7 @@#include <linux/kvm_host.h> #include "irq.h" +#include "i8254.h" /* * check if there is pending interrupt without
@@ -66,6 +67,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);/* TODO: PIT, RTC etc. */ }EXPORT_SYMBOL_GPL (kvm_inject_pending_timer_irqs);
@@ -73,6 +75,7 @@ EXPORT_SYMBOL_GPL(kvm_inject_pending_timer_irqs);void kvm_timer_intr_post (struct kvm_vcpu *vcpu,int vec) {kvm_apic_timer_intr_post (vcpu, vec); +kvm_pit_timer_intr_post (vcpu, vec);/* TODO: PIT, RTC etc. */ }EXPORT_SYMBOL_GPL (kvm_timer_intr_post);
arch/x86/kvm/x86.c +9 -0
@@ -17,6 +17,7 @@#include <linux/kvm_host.h> #include "irq.h" #include "mmu.h" +#include "i8254.h" #include <linux/clocksource.h> #include <linux/kvm.h>
@@ -805,6 +806,7 @@ int kvm_dev_ioctl_check_extension(long ext)case KVM_CAP_SET_TSS_ADDR :case KVM_CAP_EXT_CPUID :case KVM_CAP_CLOCKSOURCE : +case KVM_CAP_PIT : r =1 ;break ;case KVM_CAP_VAPIC :
@@ -1581,6 +1583,12 @@ long kvm_arch_vm_ioctl(struct file *filp, }else goto out;break ; +case KVM_CREATE_PIT : + r = -ENOMEM ; + kvm->arch.vpit =kvm_create_pit (kvm); +if (kvm->arch.vpit) + r =0 ; +break ;case KVM_IRQ_LINE : {struct kvm_irq_level irq_event;
@@ -3359,6 +3367,7 @@ static void kvm_free_vcpus(struct kvm *kvm)void kvm_arch_destroy_vm (struct kvm *kvm) { +kvm_free_pit (kvm);kfree (kvm->arch.vpic);kfree (kvm->arch.vioapic);kvm_free_vcpus (kvm);
include/asm-x86/kvm_host.h +1 -0
@@ -297,6 +297,7 @@ struct kvm_arch{struct list_head active_mmu_pages;struct kvm_pic *vpic;struct kvm_ioapic *vioapic; +struct kvm_pit *vpit;int round_robin_prev_vcpu;unsigned int tss_addr;
include/linux/kvm.h +2 -0
@@ -236,6 +236,7 @@ struct kvm_vapic_addr {#define KVM_CAP_CLOCKSOURCE 8#define KVM_CAP_NR_VCPUS 9/* returns max vcpus per vm */ #define KVM_CAP_NR_MEMSLOTS 10/* returns max memory slots per vm */ +#define KVM_CAP_PIT 11/* * ioctls for VM fds
@@ -258,6 +259,7 @@ struct kvm_vapic_addr {#define KVM_IRQ_LINE _IOW(KVMIO, 0x61, struct kvm_irq_level)#define KVM_GET_IRQCHIP _IOWR(KVMIO, 0x62, struct kvm_irqchip)#define KVM_SET_IRQCHIP _IOR(KVMIO, 0x63, struct kvm_irqchip) +#define KVM_CREATE_PIT _IO(KVMIO, 0x64)/* * ioctls for vcpu fds
Signature · 1 line
debian.1.5.3.7.1-dirty
-------------------------------------------------------------------------
This SF.net email is sponsored by: Microsoft Defy all challenges. Microsoft(R) Visual Studio 2008.
http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/
_______________________________________________
kvm-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/kvm-devel
arch/x86/kvm/i8254.c line 147
Quoted 13 lines · Sheng
arch/x86/kvm/i8254.c new file +12
-0+ +static int pit_get_out (struct kvm *kvm,int channel) +{ +struct kvm_kpit_channel_state *c = + &kvm->arch.vpit->pit_state.channels[channel]; +s64 d, t; +int out; + +ASSERT (mutex_is_locked (&kvm->arch.vpit->pit_state.lock)); + + t =ktime_to_ns (ktime_sub (ktime_get (), c->count_load_time)); + d =muldiv64 (t,PIT_FREQ ,1e9 );
NSECS_PER_SEC to avoid people jumping on you saying you can't use floating point in the kernel (yes, the compiler converts it at compile-time, but they'll still say it).
Signature · 7 lines
Do not meddle in the internals of kernels, for they are subtle and quick to panic. ------------------------------------------------------------------------- This SF.net email is sponsored by: Microsoft Defy all challenges. Microsoft(R) Visual Studio 2008. http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/
Quoted 17 lines · On Wednesday 05 March 2008 14:54:06 Avi Kivity wrote:
Review
Quoted 12 lines · Sheng
(quoted diff) +12
-0+ +static int pit_get_out(struct kvm *kvm, int channel) +{ + struct kvm_kpit_channel_state *c = + &kvm->arch.vpit->pit_state.channels[channel]; + s64 d, t; + int out; + + ASSERT(mutex_is_locked(&kvm->arch.vpit->pit_state.lock)); + + t = ktime_to_ns(ktime_sub(ktime_get(), c->count_load_time)); + d = muldiv64(t, PIT_FREQ, 1e9);
NSECS_PER_SECto avoid people jumping on you saying you can't use floating point in the kernel (yes, the compiler converts it at compile-time, but they'll still say it).
Sorry... I remembered I've modified it, seems something got wrong...
Here is the updated patch:
--- arch/x86/kvm/Makefile | 3 +- arch/x86/kvm/i8254.c | 583 ++++++++++++++++++++++++++++++++++++++++++++ arch/x86/kvm/i8254.h | 59 +++++ arch/x86/kvm/irq.c | 3 + arch/x86/kvm/x86.c | 9 + include/asm-x86/kvm_host.h | 1 + include/linux/kvm.h | 2 + 7 files changed, 659 insertions(+), 1 deletions(-) create mode 100644 arch/x86/kvm/i8254.c create mode 100644 arch/x86/kvm/i8254.h
arch/x86/kvm/Makefile +0 -0
@@ -6,7 +6,8 @@ common-objs = $(addprefix ../../../virt/kvm/, kvm_main.o
ioapic.o)
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
obj-$(CONFIG_KVM) += kvm.o
kvm-intel-objs = vmx.o
obj-$(CONFIG_KVM_INTEL) += kvm-intel.o
arch/x86/kvm/i8254.c new file +9 -0
@@ -0,0 +1,583 @@ +/* +* 8253/8254 interval timer emulation +* +* Copyright (c) 2003-2004 Fabrice Bellard +* Copyright (c) 2006 Intel Corporation +* Copyright (c) 2007 Keir Fraser, XenSource Inc +* Copyright (c) 2008 Intel Corporation +* +* Permission is hereby granted, free of charge, to any person obtaining a
copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * Authors:
+ * Sheng Yang <[email protected]>
+ * Based on QEMU and Xen.
+ */
+
+#include <linux/kvm_host.h>
+
+#include "irq.h"
+#include "i8254.h"
+
+#if 1
+#define pit_debug(fmt, arg...) printk(KERN_WARNING fmt, ##arg) +#else
+#define pit_debug(fmt, arg...)
+#endif
+
+#ifndef CONFIG_X86_64
+#define mod_64(x, y) ((x) - (y) * div64_64(x, y))
+#else
+#define mod_64(x, y) ((x) % (y))
+#endif
+
+#define RW_STATE_LSB 1
+#define RW_STATE_MSB 2
+#define RW_STATE_WORD0 3
+#define RW_STATE_WORD1 4
+
+/* Compute with 96 bit intermediate result: (a*b)/c */
+static u64 muldiv64(u64 a, u32 b, u32 c)
+{
+ union {
+ u64 ll;
+ struct {
+ u32 low, high;
+ } l;
+ } u, res;
+ u64 rl, rh;
+
+ u.ll = a;
+ rl = (u64)u.l.low * (u64)b;
+ rh = (u64)u.l.high * (u64)b;
+ rh += (rl >> 32);
+ res.l.high = div64_64(rh, c);
+ res.l.low = div64_64(((mod_64(rh, c) << 32) + (rl & 0xffffffff)), c);
+ return res.ll;
+}
+
+static void pit_set_gate(struct kvm *kvm, int channel, u32 val) +{
+ struct kvm_kpit_channel_state *c =
+ &kvm->arch.vpit->pit_state.channels[channel];
+
+ ASSERT(mutex_is_locked(&kvm->arch.vpit->pit_state.lock));
+
+ switch (c->mode) {
+ default:
+ case 0:
+ case 4:
+ /* XXX: just disable/enable counting */
+ break;
+ case 1:
+ case 2:
+ case 3:
+ case 5:
+ /* Restart counting on rising edge. */
+ if (c->gate < val)
+ c->count_load_time = ktime_get();
+ break;
+ }
+
+ c->gate = val;
+}
+
+int pit_get_gate(struct kvm *kvm, int channel)
+{
+ ASSERT(mutex_is_locked(&kvm->arch.vpit->pit_state.lock));
+
+ return kvm->arch.vpit->pit_state.channels[channel].gate;
+}
+
+static int pit_get_count(struct kvm *kvm, int channel)
+{
+ struct kvm_kpit_channel_state *c =
+ &kvm->arch.vpit->pit_state.channels[channel];
+ s64 d, t;
+ int counter;
+
+ ASSERT(mutex_is_locked(&kvm->arch.vpit->pit_state.lock));
+
+ t = ktime_to_ns(ktime_sub(ktime_get(), c->count_load_time));
+ d = muldiv64(t, PIT_FREQ, NSEC_PER_SEC);
+
+ switch (c->mode) {
+ case 0:
+ case 1:
+ case 4:
+ case 5:
+ counter = (c->count - d) & 0xffff;
+ break;
+ case 3:
+ /* XXX: may be incorrect for odd counts */
+ counter = c->count - (mod_64((2 * d), c->count));
+ break;
+ default:
+ counter = c->count - mod_64(d, c->count);
+ break;
+ }
+ return counter;
+}
+
+static int pit_get_out(struct kvm *kvm, int channel)
+{
+ struct kvm_kpit_channel_state *c =
+ &kvm->arch.vpit->pit_state.channels[channel];
+ s64 d, t;
+ int out;
+
+ ASSERT(mutex_is_locked(&kvm->arch.vpit->pit_state.lock));
+
+ t = ktime_to_ns(ktime_sub(ktime_get(), c->count_load_time));
+ d = muldiv64(t, PIT_FREQ, NSEC_PER_SEC);
+
+ switch (c->mode) {
+ default:
+ case 0:
+ out = (d >= c->count);
+ break;
+ case 1:
+ out = (d < c->count);
+ break;
+ case 2:
+ out = ((mod_64(d, c->count) == 0) && (d != 0));
+ break;
+ case 3:
+ out = (mod_64(d, c->count) < ((c->count + 1) >> 1));
+ break;
+ case 4:
+ case 5:
+ out = (d == c->count);
+ break;
+ }
+
+ return out;
+}
+
+static void pit_latch_count(struct kvm *kvm, int channel)
+{
+ struct kvm_kpit_channel_state *c =
+ &kvm->arch.vpit->pit_state.channels[channel];
+
+ ASSERT(mutex_is_locked(&kvm->arch.vpit->pit_state.lock));
+
+ if (!c->count_latched) {
+ c->latched_count = pit_get_count(kvm, channel);
+ c->count_latched = c->rw_mode;
+ }
+}
+
+static void pit_latch_status(struct kvm *kvm, int channel)
+{
+ struct kvm_kpit_channel_state *c =
+ &kvm->arch.vpit->pit_state.channels[channel];
+
+ ASSERT(mutex_is_locked(&kvm->arch.vpit->pit_state.lock));
+
+ if (!c->status_latched) {
+ /* TODO: Return NULL COUNT (bit 6). */
+ c->status = ((pit_get_out(kvm, channel) << 7) |
+ (c->rw_mode << 4) |
+ (c->mode << 1) |
+ c->bcd);
+ c->status_latched = 1;
+ }
+}
+
+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;
+
+ atomic_inc(&pt->pending);
+ if (vcpu0 && waitqueue_active(&vcpu0->wq)) {
+ vcpu0->arch.mp_state = VCPU_MP_STATE_RUNNABLE;
+ wake_up_interruptible(&vcpu0->wq);
+ }
+
+ pt->timer.expires = ktime_add_ns(pt->timer.expires, pt->period);
+ pt->scheduled = ktime_to_ns(pt->timer.expires);
+
+ return (pt->period == 0 ? 0 : 1);
+}
+
+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;
+}
+
+static void destroy_pit_timer(struct kvm_kpit_timer *pt)
+{
+ pit_debug("pit: execute del timer!\n");
+ hrtimer_cancel(&pt->timer);
+}
+
+static void create_pit_timer(struct kvm_kpit_timer *pt, u32 val, int is_period)
+{
+ s64 interval;
+
+ interval = muldiv64(val, NSEC_PER_SEC, PIT_FREQ);
+
+ pit_debug("pit: create pit timer, interval is %llu nsec\n", interval);
+
+ /* TODO The new value only affected after the retriggered */
+ hrtimer_cancel(&pt->timer);
+ pt->period = (is_period == 0) ? 0 : interval;
+ pt->timer.function = pit_timer_fn;
+ atomic_set(&pt->pending, 0);
+
+ hrtimer_start(&pt->timer, ktime_add_ns(ktime_get(), interval),
+ HRTIMER_MODE_ABS);
+}
+
+static void pit_load_count(struct kvm *kvm, int channel, u32 val) +{
+ struct kvm_kpit_state *ps = &kvm->arch.vpit->pit_state;
+
+ ASSERT(mutex_is_locked(&ps->lock));
+
+ pit_debug("pit: load_count val is %d, channel is %d\n", val, channel);
+
+ if (val == 0)
+ val = 0x10000;
+
+ ps->channels[channel].count_load_time = ktime_get();
+ ps->channels[channel].count = val;
+
+ if (channel != 0)
+ return;
+
+ /* Two types of timer
+ * mode 1 is one shot, mode 2 is period, otherwise del timer */
+ switch (ps->channels[0].mode) {
+ case 1:
+ create_pit_timer(&ps->pit_timer, val, 0);
+ break;
+ case 2:
+ create_pit_timer(&ps->pit_timer, val, 1);
+ break;
+ default:
+ destroy_pit_timer(&ps->pit_timer);
+ }
+}
+
+static void pit_ioport_write(struct kvm_io_device *this,
+ gpa_t addr, int len, const void *data)
+{
+ struct kvm_pit *pit = (struct kvm_pit *)this->private;
+ struct kvm_kpit_state *pit_state = &pit->pit_state;
+ struct kvm *kvm = pit->kvm;
+ int channel, access;
+ struct kvm_kpit_channel_state *s;
+ u32 val = *(u32 *) data;
+
+ val &= 0xff;
+ addr &= 3;
+
+ mutex_lock(&pit_state->lock);
+
+ if (val != 0)
+ pit_debug("pit: write addr is 0x%x, len is %d, val is 0x%x\n",
+ (unsigned int)addr, len, val);
+
+ if (addr == 3) {
+ channel = val >> 6;
+ if (channel == 3) {
+ /* Read-Back Command. */
+ for (channel = 0; channel < 3; channel++) {
+ s = &pit_state->channels[channel];
+ if (val & (2 << channel)) {
+ if (!(val & 0x20))
+ pit_latch_count(kvm, channel);
+ if (!(val & 0x10))
+ pit_latch_status(kvm, channel);
+ }
+ }
+ } else {
+ /* Select Counter <channel>. */
+ s = &pit_state->channels[channel];
+ access = (val >> 4) & 3;
+ if (access == 0) {
+ pit_latch_count(kvm, channel);
+ } else {
+ s->rw_mode = access;
+ s->read_state = access;
+ s->write_state = access;
+ s->mode = (val >> 1) & 7;
+ if (s->mode > 5)
+ s->mode -= 4;
+ s->bcd = val & 1;
+ }
+ }
+ } else {
+ /* Write Count. */
+ s = &pit_state->channels[addr];
+ switch (s->write_state) {
+ default:
+ case RW_STATE_LSB:
+ pit_load_count(kvm, addr, val);
+ break;
+ case RW_STATE_MSB:
+ pit_load_count(kvm, addr, val << 8);
+ break;
+ case RW_STATE_WORD0:
+ s->write_latch = val;
+ s->write_state = RW_STATE_WORD1;
+ break;
+ case RW_STATE_WORD1:
+ pit_load_count(kvm, addr, s->write_latch | (val << 8));
+ s->write_state = RW_STATE_WORD0;
+ break;
+ }
+ }
+
+ mutex_unlock(&pit_state->lock);
+}
+
+static void pit_ioport_read(struct kvm_io_device *this,
+ gpa_t addr, int len, void *data)
+{
+ struct kvm_pit *pit = (struct kvm_pit *)this->private;
+ struct kvm_kpit_state *pit_state = &pit->pit_state;
+ struct kvm *kvm = pit->kvm;
+ int ret, count;
+ struct kvm_kpit_channel_state *s;
+
+ addr &= 3;
+ s = &pit_state->channels[addr];
+
+ mutex_lock(&pit_state->lock);
+
+ if (s->status_latched) {
+ s->status_latched = 0;
+ ret = s->status;
+ } else if (s->count_latched) {
+ switch (s->count_latched) {
+ default:
+ case RW_STATE_LSB:
+ ret = s->latched_count & 0xff;
+ s->count_latched = 0;
+ break;
+ case RW_STATE_MSB:
+ ret = s->latched_count >> 8;
+ s->count_latched = 0;
+ break;
+ case RW_STATE_WORD0:
+ ret = s->latched_count & 0xff;
+ s->count_latched = RW_STATE_MSB;
+ break;
+ }
+ } else {
+ switch (s->read_state) {
+ default:
+ case RW_STATE_LSB:
+ count = pit_get_count(kvm, addr);
+ ret = count & 0xff;
+ break;
+ case RW_STATE_MSB:
+ count = pit_get_count(kvm, addr);
+ ret = (count >> 8) & 0xff;
+ break;
+ case RW_STATE_WORD0:
+ count = pit_get_count(kvm, addr);
+ ret = count & 0xff;
+ s->read_state = RW_STATE_WORD1;
+ break;
+ case RW_STATE_WORD1:
+ count = pit_get_count(kvm, addr);
+ ret = (count >> 8) & 0xff;
+ s->read_state = RW_STATE_WORD0;
+ break;
+ }
+ }
+
+ if (len > sizeof(ret))
+ len = sizeof(ret);
+ memcpy(data, (char *)&ret, len);
+
+ mutex_unlock(&pit_state->lock);
+}
+
+static int pit_in_range(struct kvm_io_device *this, gpa_t addr) +{
+ return ((addr >= PIT_BASE_ADDRESS) &&
+ (addr < PIT_BASE_ADDRESS + PIT_MEM_LENGTH));
+}
+
+static void speaker_ioport_write(struct kvm_io_device *this,
+ gpa_t addr, int len, const void *data)
+{
+ struct kvm_pit *pit = (struct kvm_pit *)this->private;
+ struct kvm_kpit_state *pit_state = &pit->pit_state;
+ struct kvm *kvm = pit->kvm;
+ u32 val = *(u32 *) data;
+
+ mutex_lock(&pit_state->lock);
+ pit_state->speaker_data_on = (val >> 1) & 1;
+ pit_set_gate(kvm, 2, val & 1);
+ mutex_unlock(&pit_state->lock);
+}
+
+static void speaker_ioport_read(struct kvm_io_device *this,
+ gpa_t addr, int len, void *data)
+{
+ struct kvm_pit *pit = (struct kvm_pit *)this->private;
+ struct kvm_kpit_state *pit_state = &pit->pit_state;
+ struct kvm *kvm = pit->kvm;
+ unsigned int refresh_clock;
+ int ret;
+
+ /* Refresh clock toggles at about 15us. We approximate as 2^14ns. */
+ refresh_clock = ((unsigned int)ktime_to_ns(ktime_get()) >> 14) & 1;
+
+ mutex_lock(&pit_state->lock);
+ ret = ((pit_state->speaker_data_on << 1) | pit_get_gate(kvm, 2) |
+ (pit_get_out(kvm, 2) << 5) | (refresh_clock << 4));
+ if (len > sizeof(ret))
+ len = sizeof(ret);
+ memcpy(data, (char *)&ret, len);
+ mutex_unlock(&pit_state->lock);
+}
+
+static int speaker_in_range(struct kvm_io_device *this, gpa_t addr) +{
+ return (addr == SPEAKER_BASE_ADDRESS);
+}
+
+struct kvm_pit *kvm_create_pit(struct kvm *kvm)
+{
+ int i;
+ struct kvm_pit *pit;
+ struct kvm_kpit_state *pit_state;
+ struct kvm_kpit_channel_state *c;
+
+ pit = kzalloc(sizeof(struct kvm_pit), GFP_KERNEL);
+ if (!pit)
+ return NULL;
+
+ mutex_init(&pit->pit_state.lock);
+ mutex_lock(&pit->pit_state.lock);
+
+ /* Initialize PIO device */
+ pit->dev.read = pit_ioport_read;
+ pit->dev.write = pit_ioport_write;
+ pit->dev.in_range = pit_in_range;
+ pit->dev.private = pit;
+ kvm_io_bus_register_dev(&kvm->pio_bus, &pit->dev);
+
+ pit->speaker_dev.read = speaker_ioport_read;
+ pit->speaker_dev.write = speaker_ioport_write;
+ pit->speaker_dev.in_range = speaker_in_range;
+ pit->speaker_dev.private = pit;
+ kvm_io_bus_register_dev(&kvm->pio_bus, &pit->speaker_dev);
+
+ kvm->arch.vpit = pit;
+ pit->kvm = kvm;
+
+ pit_state = &pit->pit_state;
+ pit_state->pit = pit;
+ hrtimer_init(&pit_state->pit_timer.timer,
+ CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
+ atomic_set(&pit_state->pit_timer.pending, 0);
+ for (i = 0; i < 3; i++) {
+ c = &pit_state->channels[i];
+ c->mode = 0xff;
+ c->gate = (i != 2);
+ pit_load_count(kvm, i, 0);
+ }
+
+ mutex_unlock(&pit->pit_state.lock);
+
+ pit->pit_state.inject_pending = 1;
+
+ return pit;
+}
+
+void kvm_free_pit(struct kvm *kvm)
+{
+ struct hrtimer *timer;
+
+ if (kvm->arch.vpit) {
+ mutex_lock(&kvm->arch.vpit->pit_state.lock);
+ timer = &kvm->arch.vpit->pit_state.pit_timer.timer;
+ hrtimer_cancel(timer);
+ mutex_unlock(&kvm->arch.vpit->pit_state.lock);
+ kfree(kvm->arch.vpit);
+ }
+}
+
+void __inject_pit_timer_intr(struct kvm *kvm)
+{
+ mutex_lock(&kvm->lock);
+ kvm_ioapic_set_irq(kvm->arch.vioapic, 0, 1);
+ kvm_ioapic_set_irq(kvm->arch.vioapic, 0, 0);
+ kvm_pic_set_irq(pic_irqchip(kvm), 0, 1);
+ kvm_pic_set_irq(pic_irqchip(kvm), 0, 0);
+ mutex_unlock(&kvm->lock);
+}
+
+void kvm_inject_pit_timer_irqs(struct kvm_vcpu *vcpu)
+{
+ struct kvm_pit *pit = vcpu->kvm->arch.vpit;
+ struct kvm *kvm = vcpu->kvm;
+ struct kvm_kpit_state *ps;
+ static unsigned long last_injected_time;
+
+ if (vcpu && pit) {
+ ps = &pit->pit_state;
+ /* Try to inject pending interrupts when:
+ * 1. Pending exists
+ * 2. Last interrupt was accepted or waited for too long time*/
+ if (atomic_read(&ps->pit_timer.pending) &&
+ (ps->inject_pending ||
+ (jiffies - last_injected_time >= MAX_PIT_INTR_INTERVAL))) {
+ ps->inject_pending = 0;
+ __inject_pit_timer_intr(kvm);
+ last_injected_time = jiffies;
+ }
+ }
+}
+
+void kvm_pit_timer_intr_post(struct kvm_vcpu *vcpu, int vec)
+{
+ struct kvm_arch *arch = &vcpu->kvm->arch;
+ struct kvm_kpit_state *ps;
+
+ if (vcpu && arch->vpit) {
+ ps = &arch->vpit->pit_state;
+ if (atomic_read(&ps->pit_timer.pending) &&
+ (((arch->vpic->pics[0].imr & 1) == 0 &&
+ arch->vpic->pics[0].irq_base == vec) ||
+ (arch->vioapic->redirtbl[0].fields.vector == vec &&
+ arch->vioapic->redirtbl[0].fields.mask != 1))) {+ ps->inject_pending = 1;
+ atomic_dec(&ps->pit_timer.pending);
+ ps->channels[0].count_load_time = ktime_get();
+ }
+ }
+}
arch/x86/kvm/i8254.h new file +59 -0
59 lines not on this page · raw message
arch/x86/kvm/irq.c +3 -0
@@ -23,6 +23,7 @@#include <linux/kvm_host.h> #include "irq.h" +#include "i8254.h" /* * check if there is pending interrupt without
@@ -66,6 +67,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);/* TODO: PIT, RTC etc. */ }EXPORT_SYMBOL_GPL (kvm_inject_pending_timer_irqs);
@@ -73,6 +75,7 @@ EXPORT_SYMBOL_GPL(kvm_inject_pending_timer_irqs);void kvm_timer_intr_post (struct kvm_vcpu *vcpu,int vec) {kvm_apic_timer_intr_post (vcpu, vec); +kvm_pit_timer_intr_post (vcpu, vec);/* TODO: PIT, RTC etc. */ }EXPORT_SYMBOL_GPL (kvm_timer_intr_post);
arch/x86/kvm/x86.c +9 -0
@@ -17,6 +17,7 @@#include <linux/kvm_host.h> #include "irq.h" #include "mmu.h" +#include "i8254.h" #include <linux/clocksource.h> #include <linux/kvm.h>
@@ -805,6 +806,7 @@ int kvm_dev_ioctl_check_extension(long ext)case KVM_CAP_SET_TSS_ADDR :case KVM_CAP_EXT_CPUID :case KVM_CAP_CLOCKSOURCE : +case KVM_CAP_PIT : r =1 ;break ;case KVM_CAP_VAPIC :
@@ -1581,6 +1583,12 @@ long kvm_arch_vm_ioctl(struct file *filp, }else goto out;break ; +case KVM_CREATE_PIT : + r = -ENOMEM ; + kvm->arch.vpit =kvm_create_pit (kvm); +if (kvm->arch.vpit) + r =0 ; +break ;case KVM_IRQ_LINE : {struct kvm_irq_level irq_event;
@@ -3359,6 +3367,7 @@ static void kvm_free_vcpus(struct kvm *kvm)void kvm_arch_destroy_vm (struct kvm *kvm) { +kvm_free_pit (kvm);kfree (kvm->arch.vpic);kfree (kvm->arch.vioapic);kvm_free_vcpus (kvm);
include/asm-x86/kvm_host.h +1 -0
@@ -297,6 +297,7 @@ struct kvm_arch{struct list_head active_mmu_pages;struct kvm_pic *vpic;struct kvm_ioapic *vioapic; +struct kvm_pit *vpit;int round_robin_prev_vcpu;unsigned int tss_addr;
include/linux/kvm.h +2 -0
@@ -236,6 +236,7 @@ struct kvm_vapic_addr {#define KVM_CAP_CLOCKSOURCE 8#define KVM_CAP_NR_VCPUS 9/* returns max vcpus per vm */ #define KVM_CAP_NR_MEMSLOTS 10/* returns max memory slots per vm */ +#define KVM_CAP_PIT 11/* * ioctls for VM fds
@@ -258,6 +259,7 @@ struct kvm_vapic_addr {#define KVM_IRQ_LINE _IOW(KVMIO, 0x61, struct kvm_irq_level)#define KVM_GET_IRQCHIP _IOWR(KVMIO, 0x62, struct kvm_irqchip)#define KVM_SET_IRQCHIP _IOR(KVMIO, 0x63, struct kvm_irqchip) +#define KVM_CREATE_PIT _IO(KVMIO, 0x64)/* * ioctls for vcpu fds
--
debian.1.5.3.7.1-dirty
From 14e4b71fb765c8243d968c4ad8e3271c3188c469 Mon Sep 17 00:00:00 2001
From: Sheng Yang <[email protected]>
Date: Mon, 28 Jan 2008 05:10:22 +0800
Subject: [PATCH] In kernel pit model
The patch moved PIT from userspace to kernel, and increase the timer accuracy greatly.
--- arch/x86/kvm/Makefile | 3 +- arch/x86/kvm/i8254.c | 583 ++++++++++++++++++++++++++++++++++++++++++++ arch/x86/kvm/i8254.h | 59 +++++ arch/x86/kvm/irq.c | 3 + arch/x86/kvm/x86.c | 9 + include/asm-x86/kvm_host.h | 1 + include/linux/kvm.h | 2 + 7 files changed, 659 insertions(+), 1 deletions(-) create mode 100644 arch/x86/kvm/i8254.c create mode 100644 arch/x86/kvm/i8254.h
arch/x86/kvm/Makefile +2 -1
@@ -6,7 +6,8 @@ common-objs = $(addprefix ../../../virt/kvm/, kvm_main.o ioapic.o) 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 obj-$(CONFIG_KVM) += kvm.o kvm-intel-objs = vmx.o obj-$(CONFIG_KVM_INTEL) += kvm-intel.o
arch/x86/kvm/i8254.c new file +583 -0
583 lines not on this page · raw message
arch/x86/kvm/i8254.h new file +59 -0
59 lines not on this page · raw message
arch/x86/kvm/irq.c +3 -0
@@ -23,6 +23,7 @@#include <linux/kvm_host.h> #include "irq.h" +#include "i8254.h" /* * check if there is pending interrupt without
@@ -66,6 +67,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);/* TODO: PIT, RTC etc. */ }EXPORT_SYMBOL_GPL (kvm_inject_pending_timer_irqs);
@@ -73,6 +75,7 @@ EXPORT_SYMBOL_GPL(kvm_inject_pending_timer_irqs);void kvm_timer_intr_post (struct kvm_vcpu *vcpu,int vec) {kvm_apic_timer_intr_post (vcpu, vec); +kvm_pit_timer_intr_post (vcpu, vec);/* TODO: PIT, RTC etc. */ }EXPORT_SYMBOL_GPL (kvm_timer_intr_post);
arch/x86/kvm/x86.c +9 -0
@@ -17,6 +17,7 @@#include <linux/kvm_host.h> #include "irq.h" #include "mmu.h" +#include "i8254.h" #include <linux/clocksource.h> #include <linux/kvm.h>
@@ -805,6 +806,7 @@ int kvm_dev_ioctl_check_extension(long ext)case KVM_CAP_SET_TSS_ADDR :case KVM_CAP_EXT_CPUID :case KVM_CAP_CLOCKSOURCE : +case KVM_CAP_PIT : r =1 ;break ;case KVM_CAP_VAPIC :
@@ -1581,6 +1583,12 @@ long kvm_arch_vm_ioctl(struct file *filp, }else goto out;break ; +case KVM_CREATE_PIT : + r = -ENOMEM ; + kvm->arch.vpit =kvm_create_pit (kvm); +if (kvm->arch.vpit) + r =0 ; +break ;case KVM_IRQ_LINE : {struct kvm_irq_level irq_event;
@@ -3359,6 +3367,7 @@ static void kvm_free_vcpus(struct kvm *kvm)void kvm_arch_destroy_vm (struct kvm *kvm) { +kvm_free_pit (kvm);kfree (kvm->arch.vpic);kfree (kvm->arch.vioapic);kvm_free_vcpus (kvm);
include/asm-x86/kvm_host.h +1 -0
@@ -297,6 +297,7 @@ struct kvm_arch{struct list_head active_mmu_pages;struct kvm_pic *vpic;struct kvm_ioapic *vioapic; +struct kvm_pit *vpit;int round_robin_prev_vcpu;unsigned int tss_addr;
include/linux/kvm.h +2 -0
@@ -236,6 +236,7 @@ struct kvm_vapic_addr {#define KVM_CAP_CLOCKSOURCE 8#define KVM_CAP_NR_VCPUS 9/* returns max vcpus per vm */ #define KVM_CAP_NR_MEMSLOTS 10/* returns max memory slots per vm */ +#define KVM_CAP_PIT 11/* * ioctls for VM fds
@@ -258,6 +259,7 @@ struct kvm_vapic_addr {#define KVM_IRQ_LINE _IOW(KVMIO, 0x61, struct kvm_irq_level)#define KVM_GET_IRQCHIP _IOWR(KVMIO, 0x62, struct kvm_irqchip)#define KVM_SET_IRQCHIP _IOR(KVMIO, 0x63, struct kvm_irqchip) +#define KVM_CREATE_PIT _IO(KVMIO, 0x64)/* * ioctls for vcpu fds
Signature · 1 line
debian.1.5.3.7.1-dirty
-------------------------------------------------------------------------
This SF.net email is sponsored by: Microsoft Defy all challenges. Microsoft(R) Visual Studio 2008.
http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/
_______________________________________________
kvm-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/kvm-devel
arch/x86/kvm/i8254.c line 41
Quoted 5 lines · Yang, Sheng
arch/x86/kvm/i8254.c new file +5
-0+#if 1 +#define pit_debug(fmt, arg...) printk(KERN_WARNING fmt, ##arg) +#else +#define pit_debug(fmt, arg...) +#endif
this should use pr_debug() instead i guess.
Quoted 15 lines
arch/x86/kvm/i8254.c new file +14
-0+#ifndef CONFIG_X86_64 +#define mod_64 (x, y) ((x) - (y) * div64_64(x, y)) +#else +#define mod_64 (x, y) ((x) % (y)) +#endif +/* Compute with 96 bit intermediate result: (a*b)/c */ +static u64 muldiv64 (u64 a,u32 b,u32 c) +{ +union { +u64 ll; +struct { +u32 low, high; + } l; + } u, res;
arch/x86/kvm/i8254.c line 72
Quoted 10 lines
arch/x86/kvm/i8254.c new file +10
-0+u64 rl, rh; + + u.ll = a; + rl = (u64 )u.l.low * (u64 )b; + rh = (u64 )u.l.high * (u64 )b; + rh += (rl >>32 ); + res.l.high =div64_64 (rh, c); + res.l.low =div64_64 (((mod_64 (rh, c) <<32 ) + (rl &0xffffffff )), c); +return res.ll; +}
eventually these should move into a generic file, for example lib/div64.c.
Review
Quoted 1 line
(quoted diff) +1
-0+ ASSERT(mutex_is_locked(&kvm->arch.vpit->pit_state.lock));
could we please standardize on WARN_ON(!(x)) instead?
Quoted 4 lines
arch/x86/kvm/i8254.c new file +4
-0+static enum hrtimer_restart pit_timer_fn (struct hrtimer *data) +{ +struct kvm_kpit_state *ps; +int restart_timer =0 ;
arch/x86/kvm/i8254.c line 232
Quoted 10 lines
arch/x86/kvm/i8254.c new file +10
-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 ; +}
elegant use of hrtimers! :-)
arch/x86/kvm/i8254.c line 267
Quoted 2 lines
arch/x86/kvm/i8254.c new file +2
-0+if (val ==0 ) + val =0x10000 ;
magic constant.
arch/x86/kvm/i8254.c line 300
Quoted 2 lines
arch/x86/kvm/i8254.c new file +2
-0+ val &=0xff ; + addr &=3 ;
magic constants.
Ingo
-------------------------------------------------------------------------
This SF.net email is sponsored by: Microsoft Defy all challenges. Microsoft(R) Visual Studio 2008.
http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/
Thanks for comments!
Quoted 8 lines · On Wednesday 05 March 2008 17:15:29 Ingo Molnar wrote:
arch/x86/kvm/i8254.c line 41
Quoted 5 lines · Yang, Sheng
arch/x86/kvm/i8254.c new file +5
-0+#if 1 +#define pit_debug(fmt, arg...) printk(KERN_WARNING fmt, ##arg) +#else +#define pit_debug(fmt, arg...) +#endif this should use
pr_debug()instead i guess.
Um... I followed example on ./virt/kvm/ioapic.c here. Though I think it's good to substitute all self defined debug printk with pr_debug, why KVM have little pr_xxx(the only ones are in x86.c)? Maybe for KVM is acting more like a separate driver, and using printk is easier for separate debug? I really don't know...
Quoted 20 lines
Quoted 20 lines
arch/x86/kvm/i8254.c new file +19
-0+#ifndef CONFIG_X86_64 +#define mod_64 (x, y) ((x) - (y) * div64_64(x, y)) +#else +#define mod_64 (x, y) ((x) % (y)) +#endif +/* Compute with 96 bit intermediate result: (a*b)/c */ +static u64 muldiv64 (u64 a,u32 b,u32 c) +{ +union { +u64 ll; +struct { +u32 low, high; + } l; + } u, res; +u64 rl, rh; + + u.ll = a; + rl = (u64 )u.l.low * (u64 )b; + rh = (u64 )u.l.high * (u64 )b;
Quoted 8 lines
arch/x86/kvm/i8254.c line 72
Quoted 5 lines
arch/x86/kvm/i8254.c new file +5
-0+ rh += (rl >>32 ); + res.l.high =div64_64 (rh, c); + res.l.low =div64_64 (((mod_64 (rh, c) <<32 ) + (rl &0xffffffff )), c); +return res.ll; +}eventually these should move into a generic file, for example
lib/div64.c.
That's my hope (of course with big endian support). But is there any user outside KVM? I think it may not easy to get into the generic part.
Quoted 3 lines
Review
Quoted 1 line
(quoted diff) +1
-0+ ASSERT(mutex_is_locked(&kvm->arch.vpit->pit_state.lock));could we please standardize on WARN_ON(!(x)) instead?
Sure. :)
Quoted 22 lines
Quoted 4 lines
arch/x86/kvm/i8254.c new file +4
-0+static enum hrtimer_restart pit_timer_fn (struct hrtimer *data) +{ +struct kvm_kpit_state *ps; +int restart_timer =0 ;arch/x86/kvm/i8254.c line 232
Quoted 10 lines
arch/x86/kvm/i8254.c new file +10
-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 ; +}elegant use of hrtimers! :-)
arch/x86/kvm/i8254.c line 267
Quoted 2 lines
arch/x86/kvm/i8254.c new file +2
-0+if (val ==0 ) + val =0x10000 ;magic constant.
Quoted 4 lines
arch/x86/kvm/i8254.c line 300
Quoted 2 lines
arch/x86/kvm/i8254.c new file +2
-0+ val &=0xff ; + addr &=3 ;magic constants.
I will update these constants. :) In fact, I have thought of these before, but not insist...
Thanks!
Yang, Sheng
Quoted 2 lines
Ingo
-------------------------------------------------------------------------
This SF.net email is sponsored by: Microsoft Defy all challenges. Microsoft(R) Visual Studio 2008.
http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/
Quoted 15 lines · Sheng
Thanks for comments!
Quoted 10 lines · On Wednesday 05 March 2008 17:15:29 Ingo Molnar wrote:
arch/x86/kvm/i8254.c line 41
Quoted 6 lines · Yang, Sheng
arch/x86/kvm/i8254.c new file +5
-0+#if 1 +#define pit_debug(fmt, arg...) printk(KERN_WARNING fmt, ##arg) +#else +#define pit_debug(fmt, arg...) +#endif this should use
pr_debug()instead i guess.
Quoted 7 lines
Um... I followed example on
./virt/kvm/ioapic.chere. Though I think it's good to substitute all self defined debug printk withpr_debug, why KVM have littlepr_xxx(the only ones are in x86.c)? Maybe for KVM is acting more like a separate driver, and using printk is easier for separate debug? I really don't know...
It's mostly due to lack of knowledge about pr_debug(); it wasn't intentional.
Signature · 7 lines
Do not meddle in the internals of kernels, for they are subtle and quick to panic. ------------------------------------------------------------------------- This SF.net email is sponsored by: Microsoft Defy all challenges. Microsoft(R) Visual Studio 2008. http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/
Quoted 9 lines · Yang, Sheng
Quoted 8 lines · Yang, Sheng
arch/x86/kvm/i8254.c line 41
Quoted 5 lines
arch/x86/kvm/i8254.c new file +5
-0+#if 1 +#define pit_debug(fmt, arg...) printk(KERN_WARNING fmt, ##arg) +#else +#define pit_debug(fmt, arg...) +#endif this should use
pr_debug()instead i guess.
Quoted 5 lines
Um... I followed example on
./virt/kvm/ioapic.chere. Though I think it's good to substitute all self defined debug printk withpr_debug, why KVM have littlepr_xxx(the only ones are in x86.c)? Maybe for KVM is acting more like a separate driver, and using printk is easier for separate debug? I really don't know...
it's just a small detail - it's really not a big issue and my suggestion should be functionally equivalent. What i meant is that with pr_debug() you can remove the pit_debug() define altogether (and change all pit_debug's to pr_debug). In that case all you need to do is to get the printks is to stick this to the head of the source code file (to before the include files):
#define DEBUG
and the pr_debug() calls turn from a NOP into a printk(KERN_DEBUG,...).
Ingo
-------------------------------------------------------------------------
This SF.net email is sponsored by: Microsoft Defy all challenges. Microsoft(R) Visual Studio 2008.
http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/
Quoted 9 lines · Sheng
Quoted 5 lines · On Wednesday 05 March 2008 17:15:29 Ingo Molnar wrote:
Review
Quoted 2 lines · Yang, Sheng
(quoted diff) +2
-0+ val &= 0xff; + addr &= 3;magic constants.
I will update these constants. :) In fact, I have thought of these before, but not insist...
Here is the updated patch. I kept 0xff because I think it's OK for understand easily. :)
Thanks.
Yang, Sheng
--- arch/x86/kvm/Makefile | 3 +- arch/x86/kvm/i8254.c | 583 ++++++++++++++++++++++++++++++++++++++++++++ arch/x86/kvm/i8254.h | 60 +++++ arch/x86/kvm/irq.c | 3 + arch/x86/kvm/x86.c | 9 + include/asm-x86/kvm_host.h | 1 + include/linux/kvm.h | 2 + 7 files changed, 660 insertions(+), 1 deletions(-) create mode 100644 arch/x86/kvm/i8254.c create mode 100644 arch/x86/kvm/i8254.h
arch/x86/kvm/Makefile +0 -0
@@ -6,7 +6,8 @@ common-objs = $(addprefix ../../../virt/kvm/, kvm_main.o
ioapic.o)
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
obj-$(CONFIG_KVM) += kvm.o
kvm-intel-objs = vmx.o
obj-$(CONFIG_KVM_INTEL) += kvm-intel.o
arch/x86/kvm/i8254.c new file +9 -0
@@ -0,0 +1,583 @@ +/* +* 8253/8254 interval timer emulation +* +* Copyright (c) 2003-2004 Fabrice Bellard +* Copyright (c) 2006 Intel Corporation +* Copyright (c) 2007 Keir Fraser, XenSource Inc +* Copyright (c) 2008 Intel Corporation +* +* Permission is hereby granted, free of charge, to any person obtaining a
copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * Authors:
+ * Sheng Yang <[email protected]>
+ * Based on QEMU and Xen.
+ */
+
+#include <linux/kvm_host.h>
+
+#include "irq.h"
+#include "i8254.h"
+
+#ifndef CONFIG_X86_64
+#define mod_64(x, y) ((x) - (y) * div64_64(x, y))
+#else
+#define mod_64(x, y) ((x) % (y))
+#endif
+
+#define RW_STATE_LSB 1
+#define RW_STATE_MSB 2
+#define RW_STATE_WORD0 3
+#define RW_STATE_WORD1 4
+
+/* Compute with 96 bit intermediate result: (a*b)/c */
+static u64 muldiv64(u64 a, u32 b, u32 c)
+{
+ union {
+ u64 ll;
+ struct {
+ u32 low, high;
+ } l;
+ } u, res;
+ u64 rl, rh;
+
+ u.ll = a;
+ rl = (u64)u.l.low * (u64)b;
+ rh = (u64)u.l.high * (u64)b;
+ rh += (rl >> 32);
+ res.l.high = div64_64(rh, c);
+ res.l.low = div64_64(((mod_64(rh, c) << 32) + (rl & 0xffffffff)), c);
+ return res.ll;
+}
+
+static void pit_set_gate(struct kvm *kvm, int channel, u32 val) +{
+ struct kvm_kpit_channel_state *c =
+ &kvm->arch.vpit->pit_state.channels[channel];
+
+ WARN_ON(!mutex_is_locked(&kvm->arch.vpit->pit_state.lock));
+
+ switch (c->mode) {
+ default:
+ case 0:
+ case 4:
+ /* XXX: just disable/enable counting */
+ break;
+ case 1:
+ case 2:
+ case 3:
+ case 5:
+ /* Restart counting on rising edge. */
+ if (c->gate < val)
+ c->count_load_time = ktime_get();
+ break;
+ }
+
+ c->gate = val;
+}
+
+int pit_get_gate(struct kvm *kvm, int channel)
+{
+ WARN_ON(!mutex_is_locked(&kvm->arch.vpit->pit_state.lock));
+
+ return kvm->arch.vpit->pit_state.channels[channel].gate;
+}
+
+static int pit_get_count(struct kvm *kvm, int channel)
+{
+ struct kvm_kpit_channel_state *c =
+ &kvm->arch.vpit->pit_state.channels[channel];
+ s64 d, t;
+ int counter;
+
+ WARN_ON(!mutex_is_locked(&kvm->arch.vpit->pit_state.lock));
+
+ t = ktime_to_ns(ktime_sub(ktime_get(), c->count_load_time));
+ d = muldiv64(t, KVM_PIT_FREQ, NSEC_PER_SEC);
+
+ switch (c->mode) {
+ case 0:
+ case 1:
+ case 4:
+ case 5:
+ counter = (c->count - d) & 0xffff;
+ break;
+ case 3:
+ /* XXX: may be incorrect for odd counts */
+ counter = c->count - (mod_64((2 * d), c->count));
+ break;
+ default:
+ counter = c->count - mod_64(d, c->count);
+ break;
+ }
+ return counter;
+}
+
+static int pit_get_out(struct kvm *kvm, int channel)
+{
+ struct kvm_kpit_channel_state *c =
+ &kvm->arch.vpit->pit_state.channels[channel];
+ s64 d, t;
+ int out;
+
+ WARN_ON(!mutex_is_locked(&kvm->arch.vpit->pit_state.lock));
+
+ t = ktime_to_ns(ktime_sub(ktime_get(), c->count_load_time));
+ d = muldiv64(t, KVM_PIT_FREQ, NSEC_PER_SEC);
+
+ switch (c->mode) {
+ default:
+ case 0:
+ out = (d >= c->count);
+ break;
+ case 1:
+ out = (d < c->count);
+ break;
+ case 2:
+ out = ((mod_64(d, c->count) == 0) && (d != 0));
+ break;
+ case 3:
+ out = (mod_64(d, c->count) < ((c->count + 1) >> 1));
+ break;
+ case 4:
+ case 5:
+ out = (d == c->count);
+ break;
+ }
+
+ return out;
+}
+
+static void pit_latch_count(struct kvm *kvm, int channel)
+{
+ struct kvm_kpit_channel_state *c =
+ &kvm->arch.vpit->pit_state.channels[channel];
+
+ WARN_ON(!mutex_is_locked(&kvm->arch.vpit->pit_state.lock));
+
+ if (!c->count_latched) {
+ c->latched_count = pit_get_count(kvm, channel);
+ c->count_latched = c->rw_mode;
+ }
+}
+
+static void pit_latch_status(struct kvm *kvm, int channel)
+{
+ struct kvm_kpit_channel_state *c =
+ &kvm->arch.vpit->pit_state.channels[channel];
+
+ WARN_ON(!mutex_is_locked(&kvm->arch.vpit->pit_state.lock));
+
+ if (!c->status_latched) {
+ /* TODO: Return NULL COUNT (bit 6). */
+ c->status = ((pit_get_out(kvm, channel) << 7) |
+ (c->rw_mode << 4) |
+ (c->mode << 1) |
+ c->bcd);
+ c->status_latched = 1;
+ }
+}
+
+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;
+
+ atomic_inc(&pt->pending);
+ if (vcpu0 && waitqueue_active(&vcpu0->wq)) {
+ vcpu0->arch.mp_state = VCPU_MP_STATE_RUNNABLE;
+ wake_up_interruptible(&vcpu0->wq);
+ }
+
+ pt->timer.expires = ktime_add_ns(pt->timer.expires, pt->period);
+ pt->scheduled = ktime_to_ns(pt->timer.expires);
+
+ return (pt->period == 0 ? 0 : 1);
+}
+
+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;
+}
+
+static void destroy_pit_timer(struct kvm_kpit_timer *pt)
+{
+ pr_debug("pit: execute del timer!\n");
+ hrtimer_cancel(&pt->timer);
+}
+
+static void create_pit_timer(struct kvm_kpit_timer *pt, u32 val, int is_period)
+{
+ s64 interval;
+
+ interval = muldiv64(val, NSEC_PER_SEC, KVM_PIT_FREQ);
+
+ pr_debug("pit: create pit timer, interval is %llu nsec\n", interval);
+
+ /* TODO The new value only affected after the retriggered */
+ hrtimer_cancel(&pt->timer);
+ pt->period = (is_period == 0) ? 0 : interval;
+ pt->timer.function = pit_timer_fn;
+ atomic_set(&pt->pending, 0);
+
+ hrtimer_start(&pt->timer, ktime_add_ns(ktime_get(), interval),
+ HRTIMER_MODE_ABS);
+}
+
+static void pit_load_count(struct kvm *kvm, int channel, u32 val) +{
+ struct kvm_kpit_state *ps = &kvm->arch.vpit->pit_state;
+
+ WARN_ON(!mutex_is_locked(&ps->lock));
+
+ pr_debug("pit: load_count val is %d, channel is %d\n", val, channel);
+
+ /* Though spec said the state of 8254 is undefined after power-up,
+ * seems some tricky OS like Windows XP depends on IRQ0 interrupt
+ * when booting up.
+ * So here setting initialize rate for it, and not a specific number */
+ if (val == 0)
+ val = 0x10000;
+
+ ps->channels[channel].count_load_time = ktime_get();
+ ps->channels[channel].count = val;
+
+ if (channel != 0)
+ return;
+
+ /* Two types of timer
+ * mode 1 is one shot, mode 2 is period, otherwise del timer */
+ switch (ps->channels[0].mode) {
+ case 1:
+ create_pit_timer(&ps->pit_timer, val, 0);
+ break;
+ case 2:
+ create_pit_timer(&ps->pit_timer, val, 1);
+ break;
+ default:
+ destroy_pit_timer(&ps->pit_timer);
+ }
+}
+
+static void pit_ioport_write(struct kvm_io_device *this,
+ gpa_t addr, int len, const void *data)
+{
+ struct kvm_pit *pit = (struct kvm_pit *)this->private;
+ struct kvm_kpit_state *pit_state = &pit->pit_state;
+ struct kvm *kvm = pit->kvm;
+ int channel, access;
+ struct kvm_kpit_channel_state *s;
+ u32 val = *(u32 *) data;
+
+ val &= 0xff;
+ addr &= KVM_PIT_CHANNEL_MASK;
+
+ mutex_lock(&pit_state->lock);
+
+ if (val != 0)
+ pr_debug("pit: write addr is 0x%x, len is %d, val is 0x%x\n",
+ (unsigned int)addr, len, val);
+
+ if (addr == 3) {
+ channel = val >> 6;
+ if (channel == 3) {
+ /* Read-Back Command. */
+ for (channel = 0; channel < 3; channel++) {
+ s = &pit_state->channels[channel];
+ if (val & (2 << channel)) {
+ if (!(val & 0x20))
+ pit_latch_count(kvm, channel);
+ if (!(val & 0x10))
+ pit_latch_status(kvm, channel);
+ }
+ }
+ } else {
+ /* Select Counter <channel>. */
+ s = &pit_state->channels[channel];
+ access = (val >> 4) & KVM_PIT_CHANNEL_MASK;
+ if (access == 0) {
+ pit_latch_count(kvm, channel);
+ } else {
+ s->rw_mode = access;
+ s->read_state = access;
+ s->write_state = access;
+ s->mode = (val >> 1) & 7;
+ if (s->mode > 5)
+ s->mode -= 4;
+ s->bcd = val & 1;
+ }
+ }
+ } else {
+ /* Write Count. */
+ s = &pit_state->channels[addr];
+ switch (s->write_state) {
+ default:
+ case RW_STATE_LSB:
+ pit_load_count(kvm, addr, val);
+ break;
+ case RW_STATE_MSB:
+ pit_load_count(kvm, addr, val << 8);
+ break;
+ case RW_STATE_WORD0:
+ s->write_latch = val;
+ s->write_state = RW_STATE_WORD1;
+ break;
+ case RW_STATE_WORD1:
+ pit_load_count(kvm, addr, s->write_latch | (val << 8));
+ s->write_state = RW_STATE_WORD0;
+ break;
+ }
+ }
+
+ mutex_unlock(&pit_state->lock);
+}
+
+static void pit_ioport_read(struct kvm_io_device *this,
+ gpa_t addr, int len, void *data)
+{
+ struct kvm_pit *pit = (struct kvm_pit *)this->private;
+ struct kvm_kpit_state *pit_state = &pit->pit_state;
+ struct kvm *kvm = pit->kvm;
+ int ret, count;
+ struct kvm_kpit_channel_state *s;
+
+ addr &= KVM_PIT_CHANNEL_MASK;
+ s = &pit_state->channels[addr];
+
+ mutex_lock(&pit_state->lock);
+
+ if (s->status_latched) {
+ s->status_latched = 0;
+ ret = s->status;
+ } else if (s->count_latched) {
+ switch (s->count_latched) {
+ default:
+ case RW_STATE_LSB:
+ ret = s->latched_count & 0xff;
+ s->count_latched = 0;
+ break;
+ case RW_STATE_MSB:
+ ret = s->latched_count >> 8;
+ s->count_latched = 0;
+ break;
+ case RW_STATE_WORD0:
+ ret = s->latched_count & 0xff;
+ s->count_latched = RW_STATE_MSB;
+ break;
+ }
+ } else {
+ switch (s->read_state) {
+ default:
+ case RW_STATE_LSB:
+ count = pit_get_count(kvm, addr);
+ ret = count & 0xff;
+ break;
+ case RW_STATE_MSB:
+ count = pit_get_count(kvm, addr);
+ ret = (count >> 8) & 0xff;
+ break;
+ case RW_STATE_WORD0:
+ count = pit_get_count(kvm, addr);
+ ret = count & 0xff;
+ s->read_state = RW_STATE_WORD1;
+ break;
+ case RW_STATE_WORD1:
+ count = pit_get_count(kvm, addr);
+ ret = (count >> 8) & 0xff;
+ s->read_state = RW_STATE_WORD0;
+ break;
+ }
+ }
+
+ if (len > sizeof(ret))
+ len = sizeof(ret);
+ memcpy(data, (char *)&ret, len);
+
+ mutex_unlock(&pit_state->lock);
+}
+
+static int pit_in_range(struct kvm_io_device *this, gpa_t addr) +{
+ return ((addr >= KVM_PIT_BASE_ADDRESS) &&
+ (addr < KVM_PIT_BASE_ADDRESS + KVM_PIT_MEM_LENGTH));
+}
+
+static void speaker_ioport_write(struct kvm_io_device *this,
+ gpa_t addr, int len, const void *data)
+{
+ struct kvm_pit *pit = (struct kvm_pit *)this->private;
+ struct kvm_kpit_state *pit_state = &pit->pit_state;
+ struct kvm *kvm = pit->kvm;
+ u32 val = *(u32 *) data;
+
+ mutex_lock(&pit_state->lock);
+ pit_state->speaker_data_on = (val >> 1) & 1;
+ pit_set_gate(kvm, 2, val & 1);
+ mutex_unlock(&pit_state->lock);
+}
+
+static void speaker_ioport_read(struct kvm_io_device *this,
+ gpa_t addr, int len, void *data)
+{
+ struct kvm_pit *pit = (struct kvm_pit *)this->private;
+ struct kvm_kpit_state *pit_state = &pit->pit_state;
+ struct kvm *kvm = pit->kvm;
+ unsigned int refresh_clock;
+ int ret;
+
+ /* Refresh clock toggles at about 15us. We approximate as 2^14ns. */
+ refresh_clock = ((unsigned int)ktime_to_ns(ktime_get()) >> 14) & 1;
+
+ mutex_lock(&pit_state->lock);
+ ret = ((pit_state->speaker_data_on << 1) | pit_get_gate(kvm, 2) |
+ (pit_get_out(kvm, 2) << 5) | (refresh_clock << 4));
+ if (len > sizeof(ret))
+ len = sizeof(ret);
+ memcpy(data, (char *)&ret, len);
+ mutex_unlock(&pit_state->lock);
+}
+
+static int speaker_in_range(struct kvm_io_device *this, gpa_t addr) +{
+ return (addr == KVM_SPEAKER_BASE_ADDRESS);
+}
+
+struct kvm_pit *kvm_create_pit(struct kvm *kvm)
+{
+ int i;
+ struct kvm_pit *pit;
+ struct kvm_kpit_state *pit_state;
+ struct kvm_kpit_channel_state *c;
+
+ pit = kzalloc(sizeof(struct kvm_pit), GFP_KERNEL);
+ if (!pit)
+ return NULL;
+
+ mutex_init(&pit->pit_state.lock);
+ mutex_lock(&pit->pit_state.lock);
+
+ /* Initialize PIO device */
+ pit->dev.read = pit_ioport_read;
+ pit->dev.write = pit_ioport_write;
+ pit->dev.in_range = pit_in_range;
+ pit->dev.private = pit;
+ kvm_io_bus_register_dev(&kvm->pio_bus, &pit->dev);
+
+ pit->speaker_dev.read = speaker_ioport_read;
+ pit->speaker_dev.write = speaker_ioport_write;
+ pit->speaker_dev.in_range = speaker_in_range;
+ pit->speaker_dev.private = pit;
+ kvm_io_bus_register_dev(&kvm->pio_bus, &pit->speaker_dev);
+
+ kvm->arch.vpit = pit;
+ pit->kvm = kvm;
+
+ pit_state = &pit->pit_state;
+ pit_state->pit = pit;
+ hrtimer_init(&pit_state->pit_timer.timer,
+ CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
+ atomic_set(&pit_state->pit_timer.pending, 0);
+ for (i = 0; i < 3; i++) {
+ c = &pit_state->channels[i];
+ c->mode = 0xff;
+ c->gate = (i != 2);
+ pit_load_count(kvm, i, 0);
+ }
+
+ mutex_unlock(&pit->pit_state.lock);
+
+ pit->pit_state.inject_pending = 1;
+
+ return pit;
+}
+
+void kvm_free_pit(struct kvm *kvm)
+{
+ struct hrtimer *timer;
+
+ if (kvm->arch.vpit) {
+ mutex_lock(&kvm->arch.vpit->pit_state.lock);
+ timer = &kvm->arch.vpit->pit_state.pit_timer.timer;
+ hrtimer_cancel(timer);
+ mutex_unlock(&kvm->arch.vpit->pit_state.lock);
+ kfree(kvm->arch.vpit);
+ }
+}
+
+void __inject_pit_timer_intr(struct kvm *kvm)
+{
+ mutex_lock(&kvm->lock);
+ kvm_ioapic_set_irq(kvm->arch.vioapic, 0, 1);
+ kvm_ioapic_set_irq(kvm->arch.vioapic, 0, 0);
+ kvm_pic_set_irq(pic_irqchip(kvm), 0, 1);
+ kvm_pic_set_irq(pic_irqchip(kvm), 0, 0);
+ mutex_unlock(&kvm->lock);
+}
+
+void kvm_inject_pit_timer_irqs(struct kvm_vcpu *vcpu)
+{
+ struct kvm_pit *pit = vcpu->kvm->arch.vpit;
+ struct kvm *kvm = vcpu->kvm;
+ struct kvm_kpit_state *ps;
+ static unsigned long last_injected_time;
+
+ if (vcpu && pit) {
+ ps = &pit->pit_state;
+
+ /* Try to inject pending interrupts when:
+ * 1. Pending exists
+ * 2. Last interrupt was accepted or waited for too long time*/
+ if (atomic_read(&ps->pit_timer.pending) &&
+ (ps->inject_pending ||
+ (jiffies - last_injected_time
+ >= KVM_MAX_PIT_INTR_INTERVAL))) {
+ ps->inject_pending = 0;
+ __inject_pit_timer_intr(kvm);
+ last_injected_time = jiffies;
+ }
+ }
+}
+
+void kvm_pit_timer_intr_post(struct kvm_vcpu *vcpu, int vec)
+{
+ struct kvm_arch *arch = &vcpu->kvm->arch;
+ struct kvm_kpit_state *ps;
+
+ if (vcpu && arch->vpit) {
+ ps = &arch->vpit->pit_state;
+ if (atomic_read(&ps->pit_timer.pending) &&
+ (((arch->vpic->pics[0].imr & 1) == 0 &&
+ arch->vpic->pics[0].irq_base == vec) ||
+ (arch->vioapic->redirtbl[0].fields.vector == vec &&
+ arch->vioapic->redirtbl[0].fields.mask != 1))) {+ ps->inject_pending = 1;
+ atomic_dec(&ps->pit_timer.pending);
+ ps->channels[0].count_load_time = ktime_get();
+ }
+ }
+}
arch/x86/kvm/i8254.h new file +60 -0
60 lines not on this page · raw message
arch/x86/kvm/irq.c +3 -0
@@ -23,6 +23,7 @@#include <linux/kvm_host.h> #include "irq.h" +#include "i8254.h" /* * check if there is pending interrupt without
@@ -66,6 +67,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);/* TODO: PIT, RTC etc. */ }EXPORT_SYMBOL_GPL (kvm_inject_pending_timer_irqs);
@@ -73,6 +75,7 @@ EXPORT_SYMBOL_GPL(kvm_inject_pending_timer_irqs);void kvm_timer_intr_post (struct kvm_vcpu *vcpu,int vec) {kvm_apic_timer_intr_post (vcpu, vec); +kvm_pit_timer_intr_post (vcpu, vec);/* TODO: PIT, RTC etc. */ }EXPORT_SYMBOL_GPL (kvm_timer_intr_post);
arch/x86/kvm/x86.c +9 -0
@@ -17,6 +17,7 @@#include <linux/kvm_host.h> #include "irq.h" #include "mmu.h" +#include "i8254.h" #include <linux/clocksource.h> #include <linux/kvm.h>
@@ -805,6 +806,7 @@ int kvm_dev_ioctl_check_extension(long ext)case KVM_CAP_SET_TSS_ADDR :case KVM_CAP_EXT_CPUID :case KVM_CAP_CLOCKSOURCE : +case KVM_CAP_PIT : r =1 ;break ;case KVM_CAP_VAPIC :
@@ -1581,6 +1583,12 @@ long kvm_arch_vm_ioctl(struct file *filp, }else goto out;break ; +case KVM_CREATE_PIT : + r = -ENOMEM ; + kvm->arch.vpit =kvm_create_pit (kvm); +if (kvm->arch.vpit) + r =0 ; +break ;case KVM_IRQ_LINE : {struct kvm_irq_level irq_event;
@@ -3359,6 +3367,7 @@ static void kvm_free_vcpus(struct kvm *kvm)void kvm_arch_destroy_vm (struct kvm *kvm) { +kvm_free_pit (kvm);kfree (kvm->arch.vpic);kfree (kvm->arch.vioapic);kvm_free_vcpus (kvm);
include/asm-x86/kvm_host.h +1 -0
@@ -297,6 +297,7 @@ struct kvm_arch{struct list_head active_mmu_pages;struct kvm_pic *vpic;struct kvm_ioapic *vioapic; +struct kvm_pit *vpit;int round_robin_prev_vcpu;unsigned int tss_addr;
include/linux/kvm.h +2 -0
@@ -236,6 +236,7 @@ struct kvm_vapic_addr {#define KVM_CAP_CLOCKSOURCE 8#define KVM_CAP_NR_VCPUS 9/* returns max vcpus per vm */ #define KVM_CAP_NR_MEMSLOTS 10/* returns max memory slots per vm */ +#define KVM_CAP_PIT 11/* * ioctls for VM fds
@@ -258,6 +259,7 @@ struct kvm_vapic_addr {#define KVM_IRQ_LINE _IOW(KVMIO, 0x61, struct kvm_irq_level)#define KVM_GET_IRQCHIP _IOWR(KVMIO, 0x62, struct kvm_irqchip)#define KVM_SET_IRQCHIP _IOR(KVMIO, 0x63, struct kvm_irqchip) +#define KVM_CREATE_PIT _IO(KVMIO, 0x64)/* * ioctls for vcpu fds
--
debian.1.5.3.7.1-dirty
From 7064ec4c1afd2c04acdbcda82986befc70241ae9 Mon Sep 17 00:00:00 2001
From: Sheng Yang <[email protected]>
Date: Mon, 28 Jan 2008 05:10:22 +0800
Subject: [PATCH] In kernel pit model
The patch moved PIT from userspace to kernel, and increase the timer accuracy greatly.
--- arch/x86/kvm/Makefile | 3 +- arch/x86/kvm/i8254.c | 583 ++++++++++++++++++++++++++++++++++++++++++++ arch/x86/kvm/i8254.h | 60 +++++ arch/x86/kvm/irq.c | 3 + arch/x86/kvm/x86.c | 9 + include/asm-x86/kvm_host.h | 1 + include/linux/kvm.h | 2 + 7 files changed, 660 insertions(+), 1 deletions(-) create mode 100644 arch/x86/kvm/i8254.c create mode 100644 arch/x86/kvm/i8254.h
arch/x86/kvm/Makefile +2 -1
@@ -6,7 +6,8 @@ common-objs = $(addprefix ../../../virt/kvm/, kvm_main.o ioapic.o) 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 obj-$(CONFIG_KVM) += kvm.o kvm-intel-objs = vmx.o obj-$(CONFIG_KVM_INTEL) += kvm-intel.o
arch/x86/kvm/i8254.c new file +583 -0
583 lines not on this page · raw message
arch/x86/kvm/i8254.h new file +60 -0
60 lines not on this page · raw message
arch/x86/kvm/irq.c +3 -0
@@ -23,6 +23,7 @@#include <linux/kvm_host.h> #include "irq.h" +#include "i8254.h" /* * check if there is pending interrupt without
@@ -66,6 +67,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);/* TODO: PIT, RTC etc. */ }EXPORT_SYMBOL_GPL (kvm_inject_pending_timer_irqs);
@@ -73,6 +75,7 @@ EXPORT_SYMBOL_GPL(kvm_inject_pending_timer_irqs);void kvm_timer_intr_post (struct kvm_vcpu *vcpu,int vec) {kvm_apic_timer_intr_post (vcpu, vec); +kvm_pit_timer_intr_post (vcpu, vec);/* TODO: PIT, RTC etc. */ }EXPORT_SYMBOL_GPL (kvm_timer_intr_post);
arch/x86/kvm/x86.c +9 -0
@@ -17,6 +17,7 @@#include <linux/kvm_host.h> #include "irq.h" #include "mmu.h" +#include "i8254.h" #include <linux/clocksource.h> #include <linux/kvm.h>
@@ -805,6 +806,7 @@ int kvm_dev_ioctl_check_extension(long ext)case KVM_CAP_SET_TSS_ADDR :case KVM_CAP_EXT_CPUID :case KVM_CAP_CLOCKSOURCE : +case KVM_CAP_PIT : r =1 ;break ;case KVM_CAP_VAPIC :
@@ -1581,6 +1583,12 @@ long kvm_arch_vm_ioctl(struct file *filp, }else goto out;break ; +case KVM_CREATE_PIT : + r = -ENOMEM ; + kvm->arch.vpit =kvm_create_pit (kvm); +if (kvm->arch.vpit) + r =0 ; +break ;case KVM_IRQ_LINE : {struct kvm_irq_level irq_event;
@@ -3359,6 +3367,7 @@ static void kvm_free_vcpus(struct kvm *kvm)void kvm_arch_destroy_vm (struct kvm *kvm) { +kvm_free_pit (kvm);kfree (kvm->arch.vpic);kfree (kvm->arch.vioapic);kvm_free_vcpus (kvm);
include/asm-x86/kvm_host.h +1 -0
@@ -297,6 +297,7 @@ struct kvm_arch{struct list_head active_mmu_pages;struct kvm_pic *vpic;struct kvm_ioapic *vioapic; +struct kvm_pit *vpit;int round_robin_prev_vcpu;unsigned int tss_addr;
include/linux/kvm.h +2 -0
@@ -236,6 +236,7 @@ struct kvm_vapic_addr {#define KVM_CAP_CLOCKSOURCE 8#define KVM_CAP_NR_VCPUS 9/* returns max vcpus per vm */ #define KVM_CAP_NR_MEMSLOTS 10/* returns max memory slots per vm */ +#define KVM_CAP_PIT 11/* * ioctls for VM fds
@@ -258,6 +259,7 @@ struct kvm_vapic_addr {#define KVM_IRQ_LINE _IOW(KVMIO, 0x61, struct kvm_irq_level)#define KVM_GET_IRQCHIP _IOWR(KVMIO, 0x62, struct kvm_irqchip)#define KVM_SET_IRQCHIP _IOR(KVMIO, 0x63, struct kvm_irqchip) +#define KVM_CREATE_PIT _IO(KVMIO, 0x64)/* * ioctls for vcpu fds
Signature · 1 line
debian.1.5.3.7.1-dirty
-------------------------------------------------------------------------
This SF.net email is sponsored by: Microsoft Defy all challenges. Microsoft(R) Visual Studio 2008.
http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/
_______________________________________________
kvm-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/kvm-devel
Quoted 4 lines · Sheng
Here is the updated patch. I kept
0xffbecause I think it's OK for understand easily. :)
Any news on the regression with older Linux guests? That's the only thing keeping my from applying the patchset.
Signature · 7 lines
Do not meddle in the internals of kernels, for they are subtle and quick to panic. ------------------------------------------------------------------------- This SF.net email is sponsored by: Microsoft Defy all challenges. Microsoft(R) Visual Studio 2008. http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/
Quoted 6 lines · On Thursday 06 March 2008 16:06:51 Avi Kivity wrote:
Quoted 2 lines · Sheng
Here is the updated patch. I kept
0xffbecause I think it's OK for understand easily. :)Any news on the regression with older Linux guests? That's the only thing keeping my from applying the patchset.
Not much. PIT interrupts injection is all right, 1000 per second. Just found two clock source in guest got problem: PM timer and TSC. Seems both due to compensate for lost ticks. Get rid of something like "jiffies_64 += lost -1" get the time ok.
And I think it's a exist bug. As you see, in most condition, userspace pit + in kernel irqchip resulted in time flow slowly, due to the lost of interrupts. But RHEL4 runs even faster than host...
I will do more investigate.
Signature · 7 lines
Thanks Yang, Sheng ------------------------------------------------------------------------- This SF.net email is sponsored by: Microsoft Defy all challenges. Microsoft(R) Visual Studio 2008. http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/
Quoted 13 lines · Sheng
Quoted 6 lines · On Thursday 06 March 2008 16:06:51 Avi Kivity wrote:
Quoted 2 lines · Sheng
Here is the updated patch. I kept
0xffbecause I think it's OK for understand easily. :)Any news on the regression with older Linux guests? That's the only thing keeping my from applying the patchset.
Not much. PIT interrupts injection is all right, 1000 per second. Just found two clock source in guest got problem: PM timer and TSC. Seems both due to compensate for lost ticks. Get rid of something like "
jiffies_64+= lost -1" get the time ok.
Quoted 5 lines
And I think it's a exist bug. As you see, in most condition, userspace pit
+ in kernel irqchip resulted in time flow slowly, due to the lost of interrupts. But RHEL4 runs even faster than host...I will do more investigate.
Get some clues.
It seems like for the kernel which is active to inject lost interrupt, when some PIT interrupts were pending, the TSC/other clocksource got the wrong impression that some PIT interrupt lost, then using itself's counter to adjust the jiffies. So the problem occurs.
Xen adjust TSC to fit this mode. It pull TSC backward to get the correct value when injecting one PIT interrupt. But this would causing trouble on some Windows. Then, it got the "time mode" concept...
Signature · 7 lines
Thanks Yang, Sheng ------------------------------------------------------------------------- This SF.net email is sponsored by: Microsoft Defy all challenges. Microsoft(R) Visual Studio 2008. http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/
Quoted 22 lines · Sheng
Quoted 13 lines · Sheng
Quoted 6 lines · On Thursday 06 March 2008 16:06:51 Avi Kivity wrote:
Quoted 2 lines · Sheng
Here is the updated patch. I kept
0xffbecause I think it's OK for understand easily. :)Any news on the regression with older Linux guests? That's the only thing keeping my from applying the patchset.
Not much. PIT interrupts injection is all right, 1000 per second. Just found two clock source in guest got problem: PM timer and TSC. Seems both due to compensate for lost ticks. Get rid of something like "
jiffies_64+= lost -1" get the time ok.Quoted 5 lines
And I think it's a exist bug. As you see, in most condition, userspace pit + in kernel irqchip resulted in time flow slowly, due to the lost of interrupts. But RHEL4 runs even faster than host...
I will do more investigate.
Get some clues.
Quoted 8 lines
It seems like for the kernel which is active to inject lost interrupt, when some PIT interrupts were pending, the TSC/other clocksource got the wrong impression that some PIT interrupt lost, then using itself's counter to adjust the jiffies. So the problem occurs.
Xen adjust TSC to fit this mode. It pull TSC backward to get the correct value when injecting one PIT interrupt. But this would causing trouble on some Windows. Then, it got the "time mode" concept...
Found more complex for KVM. Xen pulled pm timer down to kernel part, and used the guest TSC as source. So only adjust TSC is OK for it. But we are still using pm timer in QEmu, which using host time as source. So even we pull back TSC, the problem still exists, for 2.6.9 prefer to pm timer by default.
Signature · 7 lines
Thanks Yang, Sheng ------------------------------------------------------------------------- This SF.net email is sponsored by: Microsoft Defy all challenges. Microsoft(R) Visual Studio 2008. http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/
Quoted 4 lines · Sheng
Found more complex for KVM. Xen pulled pm timer down to kernel part, and used the guest TSC as source. So only adjust TSC is OK for it. But we are still using pm timer in QEmu, which using host time as source. So even we pull back TSC, the problem still exists, for 2.6.9 prefer to pm timer by default
Interesting. I guess we should pull the pm timer into the kernel as well. Timing is too tricky for userspace.
Signature · 7 lines
Any sufficiently difficult bug is indistinguishable from a feature. ------------------------------------------------------------------------- This SF.net email is sponsored by: Microsoft Defy all challenges. Microsoft(R) Visual Studio 2008. http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/
Quoted 9 lines · On Friday 07 March 2008 16:53:40 Avi Kivity wrote:
Quoted 5 lines · Sheng
Found more complex for KVM. Xen pulled pm timer down to kernel part, and used the guest TSC as source. So only adjust TSC is OK for it. But we are still using pm timer in QEmu, which using host time as source. So even we pull back TSC, the problem still exists, for 2.6.9 prefer to pm timer by default
Interesting. I guess we should pull the pm timer into the kernel as well. Timing is too tricky for userspace.
... Should we suggest using "clock=pit" on pae 2.6.9 at first?
Signature · 7 lines
Thanks Yang, Sheng ------------------------------------------------------------------------- This SF.net email is sponsored by: Microsoft Defy all challenges. Microsoft(R) Visual Studio 2008. http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/
Quoted 10 lines · Sheng
Quoted 8 lines · On Friday 07 March 2008 16:53:40 Avi Kivity wrote:
Quoted 6 lines · Sheng
Found more complex for KVM. Xen pulled pm timer down to kernel part, and used the guest TSC as source. So only adjust TSC is OK for it. But we are still using pm timer in QEmu, which using host time as source. So even we pull back TSC, the problem still exists, for 2.6.9 prefer to pm timer by default
Quoted 7 lines
Quoted 3 lines
Interesting. I guess we should pull the pm timer into the kernel as well. Timing is too tricky for userspace.
... Should we suggest using "clock=pit" on pae 2.6.9 at first?
While it is hardly a lovely solution (things should work out of the box) it is reasonable as a temporary measure.
Can you repost your patchset? If you're quick I can apply it today, otherwise it will have to wait until next week.
Signature · 7 lines
Any sufficiently difficult bug is indistinguishable from a feature. ------------------------------------------------------------------------- This SF.net email is sponsored by: Microsoft Defy all challenges. Microsoft(R) Visual Studio 2008. http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/
arch/x86/kvm/i8254.c line 263
Quoted 4 lines · Yang, Sheng
arch/x86/kvm/i8254.c new file +4
-0+/* Though spec said the state of 8254 is undefined after power-up, +* seems some tricky OS like Windows XP depends on IRQ0 interrupt +* when booting up. +* So here setting initialize rate for it, and not a specific number */
another silly style nit, the canonical comment style is:
Review
Quoted 6 lines
(quoted diff) +6
-0+ /* + * Though spec said the state of 8254 is undefined after power-up, + * seems some tricky OS like Windows XP depends on IRQ0 interrupt + * when booting up. + * So here setting initialize rate for it, and not a specific number + */
we are standardizing on that in other areas of arch/x86 and KVM uses that too.
but your patch looks good otherwise :)
/me goes back into lurker mode
Ingo
-------------------------------------------------------------------------
This SF.net email is sponsored by: Microsoft Defy all challenges. Microsoft(R) Visual Studio 2008.
http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/
Quoted 7 lines · On Thursday 06 March 2008 17:14:34 Ingo Molnar wrote:
arch/x86/kvm/i8254.c line 263
Quoted 4 lines · Yang, Sheng
arch/x86/kvm/i8254.c new file +4
-0+/* Though spec said the state of 8254 is undefined after power-up, +* seems some tricky OS like Windows XP depends on IRQ0 interrupt +* when booting up. +* So here setting initialize rate for it, and not a specific number */ another silly style nit, the canonical comment style is:
Quoted 9 lines
Review
Quoted 6 lines
(quoted diff) +6
-0+ /* + * Though spec said the state of 8254 is undefined after power-up, + * seems some tricky OS like Windows XP depends on IRQ0 interrupt + * when booting up. + * So here setting initialize rate for it, and not a specific number + */we are standardizing on that in other areas of
arch/x86and KVM uses that too.
... Seems I still can't fully depend on script/checkpatch.pl to correct my coding style error...
Quoted 5 lines
but your patch looks good otherwise :)
/me goes back into lurker mode
Ingo
Thanks for review! :)
Yang, Sheng
-------------------------------------------------------------------------
This SF.net email is sponsored by: Microsoft Defy all challenges. Microsoft(R) Visual Studio 2008.
http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/
Quoted 11 lines · Yang, Sheng
Quoted 10 lines
another silly style nit, the canonical comment style is:
Review
Quoted 6 lines
(quoted diff) +6
-0+ /* + * Though spec said the state of 8254 is undefined after power-up, + * seems some tricky OS like Windows XP depends on IRQ0 interrupt + * when booting up. + * So here setting initialize rate for it, and not a specific number + */we are standardizing on that in other areas of
arch/x86and KVM uses that too.
Quoted 2 lines
... Seems I still can't fully depend on
script/checkpatch.plto correct my coding style error...
yeah - checkpatch.pl tries to err on the side of caution, so that it can be used in automated setups. (And your patch was pretty clean already so this was mostly a sidenote.)
Ingo
-------------------------------------------------------------------------
This SF.net email is sponsored by: Microsoft Defy all challenges. Microsoft(R) Visual Studio 2008.
http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/