qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH uq/master] kvm: i8254: Fix conversion of in-kernel to userspace state
@ 2012-06-06 14:28 Jan Kiszka
  2012-06-11 10:07 ` Avi Kivity
  0 siblings, 1 reply; 4+ messages in thread
From: Jan Kiszka @ 2012-06-06 14:28 UTC (permalink / raw)
  To: Avi Kivity, Marcelo Tosatti; +Cc: qemu-devel, kvm, qemu-stable

Due to a offset between the clock used to generate the in-kernel
count_load_time (CLOCK_MONOTONIC) and the clock used for processing this
in userspace (vm_clock), reading back the output of PIT channel 2 via
port 0x61 was broken. One use cases that suffered from it was the CPU
frequency calibration of SeaBIOS, which also affected IDE/AHCI timeouts.

This fixes it by calibrating the offset between both clocks on
kvm_pit_get and adjusting the kernel value before saving it in the
userspace state. As the calibration only works while the vm_clock is
running, we cache the in-kernel state across stopped phases.

Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
---

Should go into 1.1 as well if it's fine for master.

 hw/kvm/i8254.c |   57 +++++++++++++++++++++++++++++++++++++++++++++++++++----
 1 files changed, 52 insertions(+), 5 deletions(-)

diff --git a/hw/kvm/i8254.c b/hw/kvm/i8254.c
index bb5fe07..04333d8 100644
--- a/hw/kvm/i8254.c
+++ b/hw/kvm/i8254.c
@@ -23,31 +23,63 @@
  * THE SOFTWARE.
  */
 #include "qemu-timer.h"
+#include "sysemu.h"
 #include "hw/i8254.h"
 #include "hw/i8254_internal.h"
 #include "kvm.h"
 
 #define KVM_PIT_REINJECT_BIT 0
 
+#define CALIBRATION_ROUNDS   3
+
 typedef struct KVMPITState {
     PITCommonState pit;
     LostTickPolicy lost_tick_policy;
+    bool state_valid;
 } KVMPITState;
 
-static void kvm_pit_get(PITCommonState *s)
+static int64_t abs64(int64_t v)
 {
+    return v < 0 ? -v : v;
+}
+
+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;
+    int64_t offset, clock_offset;
+    struct timespec ts;
     int i, ret;
 
+    if (s->state_valid) {
+        return;
+    }
+
+    /*
+     * Measure the delta between CLOCK_MONOTONIC, the base used for
+     * kvm_pit_channel_state::count_load_time, and vm_clock. Take the
+     * minimum of several samples to filter out scheduling noise.
+     */
+    clock_offset = LLONG_MAX;
+    for (i = 0; i < CALIBRATION_ROUNDS; i++) {
+        offset = qemu_get_clock_ns(vm_clock);
+        clock_gettime(CLOCK_MONOTONIC, &ts);
+        offset -= ts.tv_nsec;
+        offset -= (int64_t)ts.tv_sec * 1000000000;
+        if (abs64(offset) < abs64(clock_offset)) {
+            clock_offset = offset;
+        }
+    }
+
     if (kvm_has_pit_state2()) {
         ret = kvm_vm_ioctl(kvm_state, KVM_GET_PIT2, &kpit);
         if (ret < 0) {
             fprintf(stderr, "KVM_GET_PIT2 failed: %s\n", strerror(ret));
             abort();
         }
-        s->channels[0].irq_disabled = kpit.flags & KVM_PIT_FLAGS_HPET_LEGACY;
+        pit->channels[0].irq_disabled = kpit.flags & KVM_PIT_FLAGS_HPET_LEGACY;
     } else {
         /*
          * kvm_pit_state2 is superset of kvm_pit_state struct,
@@ -61,7 +93,7 @@ static void kvm_pit_get(PITCommonState *s)
     }
     for (i = 0; i < 3; i++) {
         kchan = &kpit.channels[i];
-        sc = &s->channels[i];
+        sc = &pit->channels[i];
         sc->count = kchan->count;
         sc->latched_count = kchan->latched_count;
         sc->count_latched = kchan->count_latched;
@@ -74,10 +106,10 @@ static void kvm_pit_get(PITCommonState *s)
         sc->mode = kchan->mode;
         sc->bcd = kchan->bcd;
         sc->gate = kchan->gate;
-        sc->count_load_time = kchan->count_load_time;
+        sc->count_load_time = kchan->count_load_time + clock_offset;
     }
 
-    sc = &s->channels[0];
+    sc = &pit->channels[0];
     sc->next_transition_time =
         pit_get_next_transition_time(sc, sc->count_load_time);
 }
@@ -173,6 +205,19 @@ static void kvm_pit_irq_control(void *opaque, int n, int enable)
     kvm_pit_put(pit);
 }
 
+static void kvm_pit_vm_state_change(void *opaque, int running,
+                                    RunState state)
+{
+    KVMPITState *s = opaque;
+
+    if (running) {
+        s->state_valid = false;
+    } else {
+        kvm_pit_get(&s->pit);
+        s->state_valid = true;
+    }
+}
+
 static int kvm_pit_initfn(PITCommonState *pit)
 {
     KVMPITState *s = DO_UPCAST(KVMPITState, pit, pit);
@@ -215,6 +260,8 @@ static int kvm_pit_initfn(PITCommonState *pit)
 
     qdev_init_gpio_in(&pit->dev.qdev, kvm_pit_irq_control, 1);
 
+    qemu_add_vm_change_state_handler(kvm_pit_vm_state_change, s);
+
     return 0;
 }
 
-- 
1.7.3.4

^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [Qemu-devel] [PATCH uq/master] kvm: i8254: Fix conversion of in-kernel to userspace state
  2012-06-06 14:28 [Qemu-devel] [PATCH uq/master] kvm: i8254: Fix conversion of in-kernel to userspace state Jan Kiszka
@ 2012-06-11 10:07 ` Avi Kivity
  2012-06-11 10:29   ` [Qemu-devel] [PATCH] KVM: i8254: Clean up limit constant Jan Kiszka
  0 siblings, 1 reply; 4+ messages in thread
From: Avi Kivity @ 2012-06-11 10:07 UTC (permalink / raw)
  To: Jan Kiszka; +Cc: Marcelo Tosatti, qemu-devel, kvm, qemu-stable

On 06/06/2012 05:28 PM, Jan Kiszka wrote:
> Due to a offset between the clock used to generate the in-kernel
> count_load_time (CLOCK_MONOTONIC) and the clock used for processing this
> in userspace (vm_clock), reading back the output of PIT channel 2 via
> port 0x61 was broken. One use cases that suffered from it was the CPU
> frequency calibration of SeaBIOS, which also affected IDE/AHCI timeouts.
> 
> This fixes it by calibrating the offset between both clocks on
> kvm_pit_get and adjusting the kernel value before saving it in the
> userspace state. As the calibration only works while the vm_clock is
> running, we cache the in-kernel state across stopped phases.

Applied, thanks.

> +    clock_offset = LLONG_MAX;

INT64_MAX would me more strictly correct, but in practice it makes no
difference.

-- 
error compiling committee.c: too many arguments to function

^ permalink raw reply	[flat|nested] 4+ messages in thread

* [Qemu-devel] [PATCH] KVM: i8254: Clean up limit constant
  2012-06-11 10:07 ` Avi Kivity
@ 2012-06-11 10:29   ` Jan Kiszka
  2012-06-11 11:18     ` Avi Kivity
  0 siblings, 1 reply; 4+ messages in thread
From: Jan Kiszka @ 2012-06-11 10:29 UTC (permalink / raw)
  To: Avi Kivity; +Cc: Marcelo Tosatti, qemu-devel, kvm, qemu-stable

On 2012-06-11 12:07, Avi Kivity wrote:
> On 06/06/2012 05:28 PM, Jan Kiszka wrote:
>> Due to a offset between the clock used to generate the in-kernel
>> count_load_time (CLOCK_MONOTONIC) and the clock used for processing this
>> in userspace (vm_clock), reading back the output of PIT channel 2 via
>> port 0x61 was broken. One use cases that suffered from it was the CPU
>> frequency calibration of SeaBIOS, which also affected IDE/AHCI timeouts.
>>
>> This fixes it by calibrating the offset between both clocks on
>> kvm_pit_get and adjusting the kernel value before saving it in the
>> userspace state. As the calibration only works while the vm_clock is
>> running, we cache the in-kernel state across stopped phases.
> 
> Applied, thanks.
> 
>> +    clock_offset = LLONG_MAX;
> 
> INT64_MAX would me more strictly correct, but in practice it makes no
> difference.

Was looking for this, just not long enough. Need to print some cheat
sheet. However, let's clean this up immediately:

---8<---

clock_offset is int64_t.

Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
---
 hw/kvm/i8254.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/hw/kvm/i8254.c b/hw/kvm/i8254.c
index 04333d8..c5d3711 100644
--- a/hw/kvm/i8254.c
+++ b/hw/kvm/i8254.c
@@ -62,7 +62,7 @@ static void kvm_pit_get(PITCommonState *pit)
      * kvm_pit_channel_state::count_load_time, and vm_clock. Take the
      * minimum of several samples to filter out scheduling noise.
      */
-    clock_offset = LLONG_MAX;
+    clock_offset = INT64_MAX;
     for (i = 0; i < CALIBRATION_ROUNDS; i++) {
         offset = qemu_get_clock_ns(vm_clock);
         clock_gettime(CLOCK_MONOTONIC, &ts);
-- 
1.7.3.4

^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [Qemu-devel] [PATCH] KVM: i8254: Clean up limit constant
  2012-06-11 10:29   ` [Qemu-devel] [PATCH] KVM: i8254: Clean up limit constant Jan Kiszka
@ 2012-06-11 11:18     ` Avi Kivity
  0 siblings, 0 replies; 4+ messages in thread
From: Avi Kivity @ 2012-06-11 11:18 UTC (permalink / raw)
  To: Jan Kiszka; +Cc: Marcelo Tosatti, qemu-devel, kvm, qemu-stable

On 06/11/2012 01:29 PM, Jan Kiszka wrote:
> On 2012-06-11 12:07, Avi Kivity wrote:
>> On 06/06/2012 05:28 PM, Jan Kiszka wrote:
>>> Due to a offset between the clock used to generate the in-kernel
>>> count_load_time (CLOCK_MONOTONIC) and the clock used for processing this
>>> in userspace (vm_clock), reading back the output of PIT channel 2 via
>>> port 0x61 was broken. One use cases that suffered from it was the CPU
>>> frequency calibration of SeaBIOS, which also affected IDE/AHCI timeouts.
>>>
>>> This fixes it by calibrating the offset between both clocks on
>>> kvm_pit_get and adjusting the kernel value before saving it in the
>>> userspace state. As the calibration only works while the vm_clock is
>>> running, we cache the in-kernel state across stopped phases.
>> 
>> Applied, thanks.
>> 
>>> +    clock_offset = LLONG_MAX;
>> 
>> INT64_MAX would me more strictly correct, but in practice it makes no
>> difference.
> 
> Was looking for this, just not long enough. Need to print some cheat
> sheet. However, let's clean this up immediately:
> 
> ---8<---
> 
> clock_offset is int64_t.
> 

Thanks, folded.


-- 
error compiling committee.c: too many arguments to function

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2012-06-11 11:18 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2012-06-06 14:28 [Qemu-devel] [PATCH uq/master] kvm: i8254: Fix conversion of in-kernel to userspace state Jan Kiszka
2012-06-11 10:07 ` Avi Kivity
2012-06-11 10:29   ` [Qemu-devel] [PATCH] KVM: i8254: Clean up limit constant Jan Kiszka
2012-06-11 11:18     ` Avi Kivity

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).