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.dovga...@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 --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; +}