diff mbox

[Bug?] Windows 7's time drift obviously while RTC rate switching frequently between high and low timer rate

Message ID 56FD2358.8050801@huawei.com (mailing list archive)
State New, archived
Headers show

Commit Message

Zhanghailiang March 31, 2016, 1:17 p.m. UTC
ping...

It seems that we can eliminate the drift by the following patch.
(I tested it for two hours, and there is no drift, before, the timer
in Windows 7 drifts about 2 seconds per minute.) I'm not sure if it is
the right way to solve the problem.
Any comments are welcomed. Thanks.

 From bd6acd577cbbc9d92d6376c770219470f184f7de Mon Sep 17 00:00:00 2001
From: zhanghailiang <zhang.zhanghailiang@huawei.com>
Date: Thu, 31 Mar 2016 16:36:15 -0400
Subject: [PATCH] timer/mc146818rtc: fix timer drift in Windows OS while RTC
  rate converting frequently

Signed-off-by: zhanghailiang <zhang.zhanghailiang@huawei.com>
---
  hw/timer/mc146818rtc.c | 25 ++++++++++++++++++++++---
  1 file changed, 22 insertions(+), 3 deletions(-)
diff mbox

Patch

diff --git a/hw/timer/mc146818rtc.c b/hw/timer/mc146818rtc.c
index 2ac0fd3..e39d2da 100644
--- a/hw/timer/mc146818rtc.c
+++ b/hw/timer/mc146818rtc.c
@@ -79,6 +79,7 @@  typedef struct RTCState {
      /* periodic timer */
      QEMUTimer *periodic_timer;
      int64_t next_periodic_time;
+    uint64_t last_periodic_time;
      /* update-ended timer */
      QEMUTimer *update_timer;
      uint64_t next_alarm_time;
@@ -152,7 +153,8 @@  static void rtc_coalesced_timer(void *opaque)
  static void periodic_timer_update(RTCState *s, int64_t current_time)
  {
      int period_code, period;
-    int64_t cur_clock, next_irq_clock;
+    int64_t cur_clock, next_irq_clock, pre_irq_clock;
+    bool change = false;

      period_code = s->cmos_data[RTC_REG_A] & 0x0f;
      if (period_code != 0
@@ -165,14 +167,28 @@  static void periodic_timer_update(RTCState *s, int64_t current_time)
          if (period != s->period) {
              s->irq_coalesced = (s->irq_coalesced * s->period) / period;
              DPRINTF_C("cmos: coalesced irqs scaled to %d\n", s->irq_coalesced);
+            if (s->period && period) {
+                change = true;
+            }
          }
          s->period = period;
  #endif
          /* compute 32 khz clock */
          cur_clock =
              muldiv64(current_time, RTC_CLOCK_RATE, NANOSECONDS_PER_SECOND);
+        if (change) {
+            int offset = 0;

-        next_irq_clock = (cur_clock & ~(period - 1)) + period;
+            pre_irq_clock = muldiv64(s->last_periodic_time, RTC_CLOCK_RATE,
+                                    NANOSECONDS_PER_SECOND);
+            if ((cur_clock - pre_irq_clock) >  period) {
+                offset =  (cur_clock - pre_irq_clock) / period;
+            }
+            s->irq_coalesced += offset;
+            next_irq_clock = pre_irq_clock + (offset + 1) * period;
+        } else {
+            next_irq_clock = (cur_clock & ~(period - 1)) + period;
+        }
          s->next_periodic_time = muldiv64(next_irq_clock, NANOSECONDS_PER_SECOND,
                                           RTC_CLOCK_RATE) + 1;
          timer_mod(s->periodic_timer, s->next_periodic_time);
@@ -187,7 +203,9 @@  static void periodic_timer_update(RTCState *s, int64_t current_time)
  static void rtc_periodic_timer(void *opaque)
  {
      RTCState *s = opaque;
-
+    int64_t next_periodic_time;
+
+    next_periodic_time = s->next_periodic_time;
      periodic_timer_update(s, s->next_periodic_time);
      s->cmos_data[RTC_REG_C] |= REG_C_PF;
      if (s->cmos_data[RTC_REG_B] & REG_B_PIE) {
@@ -204,6 +222,7 @@  static void rtc_periodic_timer(void *opaque)
                  DPRINTF_C("cmos: coalesced irqs increased to %d\n",
                            s->irq_coalesced);
              }
+            s->last_periodic_time = next_periodic_time;
          } else
  #endif
          qemu_irq_raise(s->irq);