From 205df4d1a87cbb14a50655fb2c0a987467fb29d6 Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Tue, 14 Aug 2012 10:24:03 +0200 Subject: [PATCH 1/4] kvm: i8254: Cache kernel clock offset in KVMPITState To prepare the final fix for clock calibration issues with the in-kernel PIT, we want to cache the offset between vmclock and the clock used by the in-kernel PIT. So far, we only need to update it when the VM state changes between running and stopped because we only read the in-kernel PIT state while the VM is running. Signed-off-by: Jan Kiszka Signed-off-by: Marcelo Tosatti --- hw/kvm/i8254.c | 38 ++++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/hw/kvm/i8254.c b/hw/kvm/i8254.c index c5d3711a04..c235d80a5a 100644 --- a/hw/kvm/i8254.c +++ b/hw/kvm/i8254.c @@ -35,7 +35,8 @@ typedef struct KVMPITState { PITCommonState pit; LostTickPolicy lost_tick_policy; - bool state_valid; + bool vm_stopped; + int64_t kernel_clock_offset; } KVMPITState; static int64_t abs64(int64_t v) @@ -43,19 +44,11 @@ static int64_t abs64(int64_t v) return v < 0 ? -v : v; } -static void kvm_pit_get(PITCommonState *pit) +static void kvm_pit_update_clock_offset(KVMPITState *s) { - KVMPITState *s = DO_UPCAST(KVMPITState, pit, pit); - struct kvm_pit_state2 kpit; - struct kvm_pit_channel_state *kchan; - struct PITChannelState *sc; int64_t offset, clock_offset; struct timespec ts; - int i, ret; - - if (s->state_valid) { - return; - } + int i; /* * Measure the delta between CLOCK_MONOTONIC, the base used for @@ -72,6 +65,21 @@ static void kvm_pit_get(PITCommonState *pit) clock_offset = offset; } } + s->kernel_clock_offset = clock_offset; +} + +static void kvm_pit_get(PITCommonState *pit) +{ + KVMPITState *s = DO_UPCAST(KVMPITState, pit, pit); + struct kvm_pit_state2 kpit; + struct kvm_pit_channel_state *kchan; + struct PITChannelState *sc; + int i, ret; + + /* No need to re-read the state if VM is stopped. */ + if (s->vm_stopped) { + return; + } if (kvm_has_pit_state2()) { ret = kvm_vm_ioctl(kvm_state, KVM_GET_PIT2, &kpit); @@ -106,7 +114,7 @@ static void kvm_pit_get(PITCommonState *pit) sc->mode = kchan->mode; sc->bcd = kchan->bcd; sc->gate = kchan->gate; - sc->count_load_time = kchan->count_load_time + clock_offset; + sc->count_load_time = kchan->count_load_time + s->kernel_clock_offset; } sc = &pit->channels[0]; @@ -211,10 +219,12 @@ static void kvm_pit_vm_state_change(void *opaque, int running, KVMPITState *s = opaque; if (running) { - s->state_valid = false; + kvm_pit_update_clock_offset(s); + s->vm_stopped = false; } else { + kvm_pit_update_clock_offset(s); kvm_pit_get(&s->pit); - s->state_valid = true; + s->vm_stopped = true; } } From 050a46065de8e3d4ee5a04f5598d666f63d34800 Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Tue, 14 Aug 2012 10:24:47 +0200 Subject: [PATCH 2/4] kvm: i8254: Finish time conversion fix 0cdd3d1444 fixed reading back the counter load time from the kernel while assuming the kernel would always update its load time on writing the state. That is only true for channel 1, and so pit_get_channel_info returned wrong output pin states for high counter values. Fix this by applying the offset also on kvm_pit_put. Now we also need to update the offset when we write the state while the VM is stopped as it keeps on changing in that state. Signed-off-by: Jan Kiszka Signed-off-by: Marcelo Tosatti --- hw/kvm/i8254.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/hw/kvm/i8254.c b/hw/kvm/i8254.c index c235d80a5a..53d13e3123 100644 --- a/hw/kvm/i8254.c +++ b/hw/kvm/i8254.c @@ -122,17 +122,23 @@ static void kvm_pit_get(PITCommonState *pit) pit_get_next_transition_time(sc, sc->count_load_time); } -static void kvm_pit_put(PITCommonState *s) +static void kvm_pit_put(PITCommonState *pit) { + KVMPITState *s = DO_UPCAST(KVMPITState, pit, pit); struct kvm_pit_state2 kpit; struct kvm_pit_channel_state *kchan; struct PITChannelState *sc; int i, ret; - kpit.flags = s->channels[0].irq_disabled ? KVM_PIT_FLAGS_HPET_LEGACY : 0; + /* The offset keeps changing as long as the VM is stopped. */ + if (s->vm_stopped) { + kvm_pit_update_clock_offset(s); + } + + kpit.flags = pit->channels[0].irq_disabled ? KVM_PIT_FLAGS_HPET_LEGACY : 0; for (i = 0; i < 3; i++) { kchan = &kpit.channels[i]; - sc = &s->channels[i]; + sc = &pit->channels[i]; kchan->count = sc->count; kchan->latched_count = sc->latched_count; kchan->count_latched = sc->count_latched; @@ -145,7 +151,7 @@ static void kvm_pit_put(PITCommonState *s) kchan->mode = sc->mode; kchan->bcd = sc->bcd; kchan->gate = sc->gate; - kchan->count_load_time = sc->count_load_time; + kchan->count_load_time = sc->count_load_time - s->kernel_clock_offset; } ret = kvm_vm_ioctl(kvm_state, From a9605e0317c7a6d5e68f3a3b6708c8ef1096f4bc Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Tue, 14 Aug 2012 13:43:12 +0200 Subject: [PATCH 3/4] kvmvapic: Disable if there is insufficient memory We need at least 1M of RAM to map the option ROM. Otherwise, we will corrupt host memory or even crash: $ qemu-system-x86_64 -nodefaults --enable-kvm -vnc :0 -m 640k Segmentation fault (core dumped) Reported-and-tested-by: Markus Armbruster Signed-off-by: Jan Kiszka Signed-off-by: Marcelo Tosatti --- hw/apic_common.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hw/apic_common.c b/hw/apic_common.c index 58e63b00da..371f95d909 100644 --- a/hw/apic_common.c +++ b/hw/apic_common.c @@ -299,7 +299,9 @@ static int apic_init_common(SysBusDevice *dev) sysbus_init_mmio(dev, &s->io_memory); - if (!vapic && s->vapic_control & VAPIC_ENABLE_MASK) { + /* Note: We need at least 1M to map the VAPIC option ROM */ + if (!vapic && s->vapic_control & VAPIC_ENABLE_MASK && + ram_size >= 1024 * 1024) { vapic = sysbus_create_simple("kvmvapic", -1, NULL); } s->vapic = vapic; From 256d046ca70788e4cb3aad56cda64ad81f19b7cd Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 25 Jul 2012 16:29:07 +0100 Subject: [PATCH 4/4] update-linux-headers.sh: Pull in asm-generic/kvm_para.h Add asm-generic/kvm_para.h to the set of non-architecture specific KVM kernel headers we copy into QEMU. This header may be included by an architecture's kvm_para.h header. Reviewed-by: Jan Kiszka Signed-off-by: Peter Maydell Signed-off-by: Marcelo Tosatti --- scripts/update-linux-headers.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/scripts/update-linux-headers.sh b/scripts/update-linux-headers.sh index 9d2a4bc4b4..a639c5bff0 100755 --- a/scripts/update-linux-headers.sh +++ b/scripts/update-linux-headers.sh @@ -46,6 +46,11 @@ mkdir -p "$output/linux-headers/linux" for header in kvm.h kvm_para.h vhost.h virtio_config.h virtio_ring.h; do cp "$tmpdir/include/linux/$header" "$output/linux-headers/linux" done +rm -rf "$output/linux-headers/asm-generic" +mkdir -p "$output/linux-headers/asm-generic" +for header in kvm_para.h; do + cp "$tmpdir/include/asm-generic/$header" "$output/linux-headers/asm-generic" +done if [ -L "$linux/source" ]; then cp "$linux/source/COPYING" "$output/linux-headers" else