@@ -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;
}
@@ -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();
@@ -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
new file mode 100755
@@ -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);
+}
@@ -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
@@ -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;
+}
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