diff mbox

[RFC,v3,32/49] vmclock: add virtual clock based on replay icount

Message ID 20140731125628.1600.74115.stgit@PASHA-ISP.novsu.ac.ru
State New
Headers show

Commit Message

Pavel Dovgalyuk July 31, 2014, 12:56 p.m. UTC
This patch introduces virtual clock which values are calculated using
number of executed instructions. Instruction counter is taken from replay
module.

Signed-off-by: Pavel Dovgalyuk <pavel.dovgaluk@ispras.ru>
---
 cpus.c                 |   22 ++++++--
 qemu-timer.c           |    4 +
 replay/Makefile.objs   |    1 
 replay/replay-icount.c |  130 ++++++++++++++++++++++++++++++++++++++++++++++++
 replay/replay.h        |   10 ++++
 stubs/replay.c         |    6 ++
 6 files changed, 167 insertions(+), 6 deletions(-)
 create mode 100755 replay/replay-icount.c
diff mbox

Patch

diff --git a/cpus.c b/cpus.c
index fce5ebc..d5fa5d1 100644
--- a/cpus.c
+++ b/cpus.c
@@ -172,6 +172,9 @@  int64_t cpu_get_ticks(void)
     if (use_icount) {
         return cpu_get_icount();
     }
+    if (replay_icount) {
+        return replay_get_icount();
+    }
 
     ticks = timers_state.cpu_ticks_offset;
     if (timers_state.cpu_ticks_enabled) {
@@ -231,8 +234,10 @@  void cpu_enable_ticks(void)
     /* Here, the really thing protected by seqlock is cpu_clock_offset. */
     seqlock_write_lock(&timers_state.vm_clock_seqlock);
     if (!timers_state.cpu_ticks_enabled) {
-        timers_state.cpu_ticks_offset -= cpu_get_real_ticks();
-        timers_state.cpu_clock_offset -= get_clock();
+        if (!replay_icount) {
+            timers_state.cpu_ticks_offset -= cpu_get_real_ticks();
+            timers_state.cpu_clock_offset -= get_clock();
+        }
         timers_state.cpu_ticks_enabled = 1;
     }
     seqlock_write_unlock(&timers_state.vm_clock_seqlock);
@@ -247,8 +252,10 @@  void cpu_disable_ticks(void)
     /* Here, the really thing protected by seqlock is cpu_clock_offset. */
     seqlock_write_lock(&timers_state.vm_clock_seqlock);
     if (timers_state.cpu_ticks_enabled) {
-        timers_state.cpu_ticks_offset += cpu_get_real_ticks();
-        timers_state.cpu_clock_offset = cpu_get_clock_locked();
+        if (!replay_icount) {
+            timers_state.cpu_ticks_offset += cpu_get_real_ticks();
+            timers_state.cpu_clock_offset = cpu_get_clock_locked();
+        }
         timers_state.cpu_ticks_enabled = 0;
     }
     seqlock_write_unlock(&timers_state.vm_clock_seqlock);
@@ -379,7 +386,12 @@  void qemu_clock_warp(QEMUClockType type)
      * applicable to other clocks.  But a clock argument removes the
      * need for if statements all over the place.
      */
-    if (type != QEMU_CLOCK_VIRTUAL || !use_icount) {
+    if (type != QEMU_CLOCK_VIRTUAL || (!use_icount || !replay_icount)) {
+        return;
+    }
+
+    if (replay_icount) {
+        replay_clock_warp();
         return;
     }
 
diff --git a/qemu-timer.c b/qemu-timer.c
index b06aa4a..f8bf060 100644
--- a/qemu-timer.c
+++ b/qemu-timer.c
@@ -579,7 +579,9 @@  int64_t qemu_clock_get_ns(QEMUClockType type)
         return get_clock();
     default:
     case QEMU_CLOCK_VIRTUAL:
-        if (use_icount) {
+        if (replay_icount) {
+            return replay_get_icount();
+        } else if (use_icount) {
             return cpu_get_icount();
         } else {
             return cpu_get_clock();
diff --git a/replay/Makefile.objs b/replay/Makefile.objs
index 257c320..7dec93f 100755
--- a/replay/Makefile.objs
+++ b/replay/Makefile.objs
@@ -2,3 +2,4 @@  obj-y += replay.o
 obj-y += replay-internal.o
 obj-y += replay-events.o
 obj-y += replay-time.o
+obj-y += replay-icount.o
diff --git a/replay/replay-icount.c b/replay/replay-icount.c
new file mode 100755
index 0000000..3c9dad1
--- /dev/null
+++ b/replay/replay-icount.c
@@ -0,0 +1,130 @@ 
+/*
+ * replay-icount.c
+ *
+ * Copyright (c) 2010-2014 Institute for System Programming
+ *                         of the Russian Academy of Sciences.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "qemu-common.h"
+#include "sysemu/cpus.h"
+#include "sysemu/sysemu.h"
+#include "qemu/timer.h"
+#include "migration/vmstate.h"
+#include "replay.h"
+#include "replay-internal.h"
+
+int replay_icount;
+
+typedef struct {
+    /* Compensate for varying guest execution speed.  */
+    int64_t bias;
+    /* Timer for advancing VM clock, when all CPUs are sleeping */
+    QEMUTimer *icount_warp_timer;
+    int64_t vm_clock_warp_start;
+} ReplayIcount;
+static ReplayIcount icount_data;
+
+
+/* Return the virtual CPU time, based on the instruction counter.  */
+int64_t replay_get_icount(void)
+{
+    int64_t icount = replay_get_current_step();
+    return icount_data.bias + (icount << replay_icount);
+}
+
+static void replay_icount_warp_rt(void *opaque)
+{
+    if (icount_data.vm_clock_warp_start == -1) {
+        return;
+    }
+
+    if (runstate_is_running()) {
+        int64_t clock = qemu_clock_get_ns(QEMU_CLOCK_HOST);
+        int64_t warp_delta = clock - icount_data.vm_clock_warp_start;
+        icount_data.bias += warp_delta;
+        if (qemu_clock_expired(QEMU_CLOCK_VIRTUAL)) {
+            qemu_notify_event();
+        }
+    }
+    icount_data.vm_clock_warp_start = -1;
+}
+
+void replay_clock_warp(void)
+{
+    int64_t deadline;
+    if (!replay_checkpoint(9)) {
+        return;
+    }
+    /*
+     * If the CPUs have been sleeping, advance the vm_clock timer now.  This
+     * ensures that the deadline for the timer is computed correctly below.
+     * This also makes sure that the insn counter is synchronized before the
+     * CPU starts running, in case the CPU is woken by an event other than
+     * the earliest vm_clock timer.
+     */
+    if (icount_data.vm_clock_warp_start != -1) {
+        replay_icount_warp_rt(NULL);
+    }
+    if (!all_cpu_threads_idle() || !qemu_clock_has_timers(QEMU_CLOCK_VIRTUAL)) {
+        timer_del(icount_data.icount_warp_timer);
+        return;
+    }
+
+    icount_data.vm_clock_warp_start = qemu_clock_get_ns(QEMU_CLOCK_HOST);
+    deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL);
+    if (deadline > 0) {
+        /*
+         * Ensure the vm_clock proceeds even when the virtual CPU goes to
+         * sleep.  Otherwise, the CPU might be waiting for a future timer
+         * interrupt to wake it up, but the interrupt never comes because
+         * the vCPU isn't running any insns and thus doesn't advance the
+         * vm_clock.
+         *
+         * An extreme solution for this problem would be to never let VCPUs
+         * sleep in icount mode if there is a pending vm_clock timer; rather
+         * time could just advance to the next vm_clock event.  Instead, we
+         * do stop VCPUs and only advance vm_clock after some "real" time,
+         * (related to the time left until the next event) has passed.  This
+         * rt_clock timer will do this.  This avoids that the warps are too
+         * visible externally---for example, you will not be sending network
+         * packets continuously instead of every 100ms.
+         */
+        timer_mod_ns(icount_data.icount_warp_timer,
+                     icount_data.vm_clock_warp_start + deadline);
+    } else {
+        qemu_notify_event();
+    }
+}
+
+static const VMStateDescription vmstate_icount = {
+    .name = "icount",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_INT64(bias, ReplayIcount),
+        VMSTATE_TIMER(icount_warp_timer, ReplayIcount),
+        VMSTATE_INT64(vm_clock_warp_start, ReplayIcount),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+void replay_init_icount(void)
+{
+    if (!replay_icount) {
+        return;
+    }
+
+    vmstate_register(NULL, 0, &vmstate_icount, &icount_data);
+    icount_data.icount_warp_timer = timer_new_ns(QEMU_CLOCK_HOST,
+                                                 replay_icount_warp_rt, NULL);
+}
diff --git a/replay/replay.h b/replay/replay.h
index f659f54..ae76f23 100755
--- a/replay/replay.h
+++ b/replay/replay.h
@@ -29,6 +29,8 @@ 
 
 extern ReplayMode replay_mode;
 extern char *replay_image_suffix;
+/*! Shift value for icount based on replay or zero, if it is disabled. */
+extern int replay_icount;
 
 /*! Returns replay play submode */
 ReplaySubmode replay_get_play_submode(void);
@@ -89,4 +91,12 @@  int replay_checkpoint(unsigned int checkpoint);
 /*! Disables storing events in the queue */
 void replay_disable_events(void);
 
+/* icount-based virtual clock */
+
+/* Initializes icount-based virtual clock */
+void replay_init_icount(void);
+/* Returns the virtual CPU time, based on the instruction counter.  */
+int64_t replay_get_icount(void);
+void replay_clock_warp(void);
+
 #endif
diff --git a/stubs/replay.c b/stubs/replay.c
index 8f0b3b5..e0d8d12 100755
--- a/stubs/replay.c
+++ b/stubs/replay.c
@@ -2,6 +2,7 @@ 
 #include "sysemu/sysemu.h"
 
 ReplayMode replay_mode;
+int replay_icount;
 
 ReplaySubmode replay_get_play_submode(void)
 {
@@ -26,3 +27,8 @@  int runstate_is_running(void)
 {
     return 0;
 }
+
+int64_t replay_get_icount(void)
+{
+    return 0;
+}