Patchwork [2/3] alleviate time drift with HPET periodic timers

login
register
mail settings
Submitter Ulrich Obergfell
Date March 18, 2011, 3:55 p.m.
Message ID <1805510926.421756.1300463714585.JavaMail.root@zmail07.collab.prod.int.phx2.redhat.com>
Download mbox | patch
Permalink /patch/87544/
State New
Headers show

Comments

Ulrich Obergfell - March 18, 2011, 3:55 p.m.
Part 2 of the patch compensates loss of timer interrupts caused by ...

-  delayed timer callback.
-  interrupt coalescing (x86 only).

Lost timer interrupts are compensated by gradually injecting additional
interrupts during the subsequent timer intervals, starting at a rate of
one additional interrupt per interval. If further interrupts are lost
while compensation is still in progress, the rate is increased. A limit
is imposed on the rate and on the 'backlog' of lost interrupts that are
to be injected.

Signed-off-by: Ulrich Obergfell <uobergfe@redhat.com>

Patch

diff -up ./hw/apic.c.orig2 ./hw/apic.c
--- ./hw/apic.c.orig2	2011-02-18 22:48:06.000000000 +0100
+++ ./hw/apic.c	2011-03-13 12:40:56.110991702 +0100
@@ -24,6 +24,9 @@ 
 #include "sysbus.h"
 #include "trace.h"
 #include "kvm.h"
+#ifdef CONFIG_HPET_DRIFTFIX
+#include "sysemu.h"
+#endif
 
 /* APIC Local Vector Table */
 #define APIC_LVT_TIMER   0
@@ -1143,6 +1146,10 @@  static SysBusDeviceInfo apic_info = {
 
 static void apic_register_devices(void)
 {
+#ifdef CONFIG_HPET_DRIFTFIX
+    qemu_get_irq_delivered = apic_get_irq_delivered;
+    qemu_reset_irq_delivered = apic_reset_irq_delivered;
+#endif
     sysbus_register_withprop(&apic_info);
 }
 
diff -up ./hw/hpet.c.orig2 ./hw/hpet.c
--- ./hw/hpet.c.orig2	2011-02-18 22:48:06.000000000 +0100
+++ ./hw/hpet.c	2011-03-13 12:40:56.111991655 +0100
@@ -31,6 +31,9 @@ 
 #include "hpet_emul.h"
 #include "sysbus.h"
 #include "mc146818rtc.h"
+#ifdef CONFIG_HPET_DRIFTFIX
+#include "sysemu.h"
+#endif
 
 //#define HPET_DEBUG
 #ifdef HPET_DEBUG
@@ -41,6 +44,13 @@ 
 
 #define HPET_MSI_SUPPORT        0
 
+#ifdef CONFIG_HPET_DRIFTFIX
+#define MAX_IRQS_TO_INJECT (uint32_t)5000
+#define MAX_IRQ_RATE       (uint32_t)10
+
+extern int hpet_driftfix;
+#endif
+
 struct HPETState;
 typedef struct HPETTimer {  /* timers */
     uint8_t tn;             /*timer number*/
@@ -55,6 +65,12 @@  typedef struct HPETTimer {  /* timers */
     uint8_t wrap_flag;      /* timer pop will indicate wrap for one-shot 32-bit
                              * mode. Next pop will be actual timer expiration.
                              */
+#ifdef CONFIG_HPET_DRIFTFIX
+    uint64_t saved_period;
+    uint32_t irqs_to_inject;
+    uint32_t irq_rate;
+    uint32_t divisor;
+#endif
 } HPETTimer;
 
 typedef struct HPETState {
@@ -169,11 +185,12 @@  static inline uint64_t hpet_calculate_di
     }
 }
 
-static void update_irq(struct HPETTimer *timer, int set)
+static int update_irq(struct HPETTimer *timer, int set)
 {
     uint64_t mask;
     HPETState *s;
     int route;
+    int irq_delivered = 1;
 
     if (timer->tn <= 1 && hpet_in_legacy_mode(timer->state)) {
         /* if LegacyReplacementRoute bit is set, HPET specification requires
@@ -198,8 +215,19 @@  static void update_irq(struct HPETTimer 
         qemu_irq_raise(s->irqs[route]);
     } else {
         s->isr &= ~mask;
+#ifdef CONFIG_HPET_DRIFTFIX
+        if (hpet_driftfix && qemu_get_irq_delivered
+                          && qemu_reset_irq_delivered) {
+            qemu_reset_irq_delivered();
+            qemu_irq_raise(s->irqs[route]);
+            irq_delivered = qemu_get_irq_delivered();
+            qemu_irq_lower(s->irqs[route]);
+        }
+        else
+#endif
         qemu_irq_pulse(s->irqs[route]);
     }
+    return irq_delivered;
 }
 
 static void hpet_pre_save(void *opaque)
@@ -246,7 +274,11 @@  static int hpet_post_load(void *opaque, 
 
 static const VMStateDescription vmstate_hpet_timer = {
     .name = "hpet_timer",
+#ifdef CONFIG_HPET_DRIFTFIX
+    .version_id = 3,
+#else
     .version_id = 1,
+#endif
     .minimum_version_id = 1,
     .minimum_version_id_old = 1,
     .fields      = (VMStateField []) {
@@ -256,6 +288,12 @@  static const VMStateDescription vmstate_
         VMSTATE_UINT64(fsb, HPETTimer),
         VMSTATE_UINT64(period, HPETTimer),
         VMSTATE_UINT8(wrap_flag, HPETTimer),
+#ifdef CONFIG_HPET_DRIFTFIX
+        VMSTATE_UINT64_V(saved_period, HPETTimer, 3),
+        VMSTATE_UINT32_V(irqs_to_inject, HPETTimer, 3),
+        VMSTATE_UINT32_V(irq_rate, HPETTimer, 3),
+        VMSTATE_UINT32_V(divisor, HPETTimer, 3),
+#endif
         VMSTATE_TIMER(qemu_timer, HPETTimer),
         VMSTATE_END_OF_LIST()
     }
@@ -263,7 +301,11 @@  static const VMStateDescription vmstate_
 
 static const VMStateDescription vmstate_hpet = {
     .name = "hpet",
+#ifdef CONFIG_HPET_DRIFTFIX
+    .version_id = 3,
+#else
     .version_id = 2,
+#endif
     .minimum_version_id = 1,
     .minimum_version_id_old = 1,
     .pre_save = hpet_pre_save,
@@ -287,7 +329,10 @@  static void hpet_timer(void *opaque)
 {
     HPETTimer *t = opaque;
     uint64_t diff;
-
+#ifdef CONFIG_HPET_DRIFTFIX
+    uint32_t irq_count = 0;
+    int irq_delivered = 0;
+#endif
     uint64_t period = t->period;
     uint64_t cur_tick = hpet_get_ticks(t->state);
 
@@ -295,13 +340,39 @@  static void hpet_timer(void *opaque)
         if (t->config & HPET_TN_32BIT) {
             while (hpet_time_after(cur_tick, t->cmp)) {
                 t->cmp = (uint32_t)(t->cmp + t->period);
+#ifdef CONFIG_HPET_DRIFTFIX
+                irq_count++;
+#endif
             }
         } else {
             while (hpet_time_after64(cur_tick, t->cmp)) {
                 t->cmp += period;
+#ifdef CONFIG_HPET_DRIFTFIX
+                irq_count++;
+#endif
             }
         }
         diff = hpet_calculate_diff(t, cur_tick);
+#ifdef CONFIG_HPET_DRIFTFIX
+        if (hpet_driftfix) {
+            if (t->saved_period != t->period) {
+                t->irqs_to_inject = (t->irqs_to_inject * t->saved_period)
+                                                       / t->period;
+                t->saved_period = t->period;
+            }
+            t->irqs_to_inject += irq_count;
+            t->irqs_to_inject = MIN(t->irqs_to_inject, MAX_IRQS_TO_INJECT);
+            if (t->irqs_to_inject > 1) {
+                if (irq_count > 1) {
+                    t->irq_rate++;
+                    t->irq_rate = MIN(t->irq_rate, MAX_IRQ_RATE);
+                }
+                if (irq_count || t->divisor == 0)
+                    t->divisor = t->irq_rate;
+                diff /= t->divisor--;
+            }
+        }
+#endif
         qemu_mod_timer(t->qemu_timer,
                        qemu_get_clock(vm_clock) + (int64_t)ticks_to_ns(diff));
     } else if (t->config & HPET_TN_32BIT && !timer_is_periodic(t)) {
@@ -312,6 +383,23 @@  static void hpet_timer(void *opaque)
             t->wrap_flag = 0;
         }
     }
+#ifdef CONFIG_HPET_DRIFTFIX
+    if (hpet_driftfix && timer_is_periodic(t) && period != 0) {
+        if (t->irqs_to_inject) {
+            irq_delivered = update_irq(t, 1);
+            if (irq_delivered) {
+                t->irq_rate = MIN(t->irq_rate, t->irqs_to_inject);
+                t->irqs_to_inject--;
+            } else {
+                if (irq_count) {
+                    t->irq_rate++;
+                    t->irq_rate = MIN(t->irq_rate, MAX_IRQ_RATE);
+                }
+            }
+        }
+    }
+    else
+#endif
     update_irq(t, 1);
 }
 
@@ -651,6 +739,12 @@  static void hpet_reset(DeviceState *d)
         timer->config |=  0x00000004ULL << 32;
         timer->period = 0ULL;
         timer->wrap_flag = 0;
+#ifdef CONFIG_HPET_DRIFTFIX
+        timer->saved_period = 0;
+        timer->irqs_to_inject = 0;
+        timer->irq_rate = 1;
+        timer->divisor = 1;
+#endif
     }
 
     s->hpet_counter = 0ULL;
@@ -711,6 +805,12 @@  static int hpet_init(SysBusDevice *dev)
         timer->qemu_timer = qemu_new_timer(vm_clock, hpet_timer, timer);
         timer->tn = i;
         timer->state = s;
+#ifdef CONFIG_HPET_DRIFTFIX
+        timer->saved_period = 0;
+        timer->irqs_to_inject = 0;
+        timer->irq_rate = 1;
+        timer->divisor = 1;
+#endif
     }
 
     /* 64-bit main counter; LegacyReplacementRoute. */
diff -up ./sysemu.h.orig2 ./sysemu.h
--- ./sysemu.h.orig2	2011-02-18 22:48:06.000000000 +0100
+++ ./sysemu.h	2011-03-13 12:40:56.112991611 +0100
@@ -96,6 +96,11 @@  int qemu_savevm_state_complete(Monitor *
 void qemu_savevm_state_cancel(Monitor *mon, QEMUFile *f);
 int qemu_loadvm_state(QEMUFile *f);
 
+#ifdef CONFIG_HPET_DRIFTFIX
+extern int (*qemu_get_irq_delivered)(void);
+extern void (*qemu_reset_irq_delivered)(void);
+#endif
+
 /* SLIRP */
 void do_info_slirp(Monitor *mon);
 
diff -up ./vl.c.orig2 ./vl.c
--- ./vl.c.orig2	2011-03-13 12:38:35.167984285 +0100
+++ ./vl.c	2011-03-13 12:40:56.112991611 +0100
@@ -205,6 +205,8 @@  int win2k_install_hack = 0;
 int rtc_td_hack = 0;
 #ifdef CONFIG_HPET_DRIFTFIX
 int hpet_driftfix = 0;
+int (*qemu_get_irq_delivered)(void) = 0;
+void (*qemu_reset_irq_delivered)(void) = 0;
 #endif
 int usb_enabled = 0;
 int singlestep = 0;