Patchwork [v2,5/5] pl031: switch clock base to rtc_clock

login
register
mail settings
Submitter Paolo Bonzini
Date March 5, 2012, 9:08 a.m.
Message ID <1330938495-30573-6-git-send-email-pbonzini@redhat.com>
Download mbox | patch
Permalink /patch/144631/
State New
Delegated to: Peter Maydell
Headers show

Comments

Paolo Bonzini - March 5, 2012, 9:08 a.m.
This lets the user specify the desired semantics.  By default, the RTC
will follow adjustments from the host's NTP client, and will remain in
sync when the virtual machine is stopped.  The previous behavior, which
provides determinism with both icount and qtest, remains available with
"-rtc clock=vm".

pl031 supports migration, so we need to convert the time base from
rtc_clock to vm_clock and back for backwards compatibility.  (The
rtc_clock may not be synchronized on the two machines, especially with
savevm/loadvm, so the conversion is needed anyway.  And since any time
base will do, why not pick the one base that is backwards compatible).

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 hw/pl031.c |   38 ++++++++++++++++++++++++++------------
 1 files changed, 26 insertions(+), 12 deletions(-)

Patch

diff --git a/hw/pl031.c b/hw/pl031.c
index 713f85d..9602664 100644
--- a/hw/pl031.c
+++ b/hw/pl031.c
@@ -13,6 +13,7 @@ 
 
 #include "sysbus.h"
 #include "qemu-timer.h"
+#include "sysemu.h"
 
 //#define DEBUG_PL031
 
@@ -38,6 +39,11 @@  typedef struct {
     QEMUTimer *timer;
     qemu_irq irq;
 
+    /* Needed to preserve the tick_count across migration, even if the
+     * absolute value of the rtc_clock is different on the source and
+     * destination.
+     */
+    uint32_t tick_offset_vmstate;
     uint32_t tick_offset;
 
     uint32_t mr;
@@ -68,27 +74,23 @@  static void pl031_interrupt(void * opaque)
 
 static uint32_t pl031_get_count(pl031_state *s)
 {
-    /* This assumes qemu_get_clock_ns returns the time since the machine was
-       created.  */
-    return s->tick_offset + qemu_get_clock_ns(vm_clock) / get_ticks_per_sec();
+    int64_t now = qemu_get_clock_ns(rtc_clock);
+    return s->tick_offset + now / get_ticks_per_sec();
 }
 
 static void pl031_set_alarm(pl031_state *s)
 {
-    int64_t now;
     uint32_t ticks;
 
-    now = qemu_get_clock_ns(vm_clock);
-    ticks = s->tick_offset + now / get_ticks_per_sec();
-
     /* The timer wraps around.  This subtraction also wraps in the same way,
        and gives correct results when alarm < now_ticks.  */
-    ticks = s->mr - ticks;
+    ticks = s->mr - pl031_get_count(s);
     DPRINTF("Alarm set in %ud ticks\n", ticks);
     if (ticks == 0) {
         qemu_del_timer(s->timer);
         pl031_interrupt(s);
     } else {
+        int64_t now = qemu_get_clock_ns(rtc_clock);
         qemu_mod_timer(s->timer, now + (int64_t)ticks * get_ticks_per_sec());
     }
 }
@@ -190,18 +192,29 @@  static int pl031_init(SysBusDevice *dev)
     sysbus_init_mmio(dev, &s->iomem);
 
     sysbus_init_irq(dev, &s->irq);
-    /* ??? We assume vm_clock is zero at this point.  */
     qemu_get_timedate(&tm, 0);
-    s->tick_offset = mktimegm(&tm);
+    s->tick_offset = mktimegm(&tm) - qemu_get_clock_ns(rtc_clock) / get_ticks_per_sec();
 
-    s->timer = qemu_new_timer_ns(vm_clock, pl031_interrupt, s);
+    s->timer = qemu_new_timer_ns(rtc_clock, pl031_interrupt, s);
     return 0;
 }
 
+static void pl031_pre_save(void *opaque)
+{
+    pl031_state *s = opaque;
+
+    /* tick_offset is base_time - rtc_clock base time.  Instead, we want to
+     * store the base time relative to the vm_clock for backwards-compatibility.  */
+    int64_t delta = qemu_get_clock_ns(rtc_clock) - qemu_get_clock_ns(vm_clock);
+    s->tick_offset_vmstate = s->tick_offset + delta / get_ticks_per_sec();
+}
+
 static int pl031_post_load(void *opaque, int version_id)
 {
     pl031_state *s = opaque;
 
+    int64_t delta = qemu_get_clock_ns(rtc_clock) - qemu_get_clock_ns(vm_clock);
+    s->tick_offset = s->tick_offset_vmstate - delta / get_ticks_per_sec();
     pl031_set_alarm(s);
     return 0;
 }
@@ -210,9 +223,10 @@  static const VMStateDescription vmstate_pl031 = {
     .name = "pl031",
     .version_id = 1,
     .minimum_version_id = 1,
+    .pre_save = pl031_pre_save,
     .post_load = pl031_post_load,
     .fields = (VMStateField[]) {
-        VMSTATE_UINT32(tick_offset, pl031_state),
+        VMSTATE_UINT32(tick_offset_vmstate, pl031_state),
         VMSTATE_UINT32(mr, pl031_state),
         VMSTATE_UINT32(lr, pl031_state),
         VMSTATE_UINT32(cr, pl031_state),