This patch introduces checkpoints that synchronize cpu thread and iothread. When checkpoint is met in the code all asynchronous events from the queue are executed.
Signed-off-by: Pavel Dovgalyuk <pavel.dovga...@ispras.ru> --- cpus.c | 5 +++++ qemu-timer.c | 40 ++++++++++++++++++++++++++++++++++++---- replay/replay-internal.h | 4 ++++ replay/replay.c | 34 ++++++++++++++++++++++++++++++++++ replay/replay.h | 20 ++++++++++++++++++++ stubs/replay.c | 11 +++++++++++ vl.c | 23 +++++++++++++++++++---- 7 files changed, 129 insertions(+), 8 deletions(-) diff --git a/cpus.c b/cpus.c index 46d6717..ad33fc1 100644 --- a/cpus.c +++ b/cpus.c @@ -402,6 +402,11 @@ void qemu_clock_warp(QEMUClockType type) return; } + /* warp clock deterministically in record/replay mode */ + if (!replay_checkpoint(CHECKPOINT_CLOCK_WARP)) { + return; + } + if (icount_sleep) { /* * If the CPUs have been sleeping, advance QEMU_CLOCK_VIRTUAL timer now. diff --git a/qemu-timer.c b/qemu-timer.c index c7bd643..e7a5c96 100644 --- a/qemu-timer.c +++ b/qemu-timer.c @@ -25,6 +25,7 @@ #include "qemu/main-loop.h" #include "qemu/timer.h" #include "replay/replay.h" +#include "sysemu/sysemu.h" #ifdef CONFIG_POSIX #include <pthread.h> @@ -478,10 +479,34 @@ bool timerlist_run_timers(QEMUTimerList *timer_list) void *opaque; qemu_event_reset(&timer_list->timers_done_ev); - if (!timer_list->clock->enabled) { + if (!timer_list->clock->enabled || !timer_list->active_timers) { goto out; } + switch (timer_list->clock->type) { + case QEMU_CLOCK_REALTIME: + break; + default: + case QEMU_CLOCK_VIRTUAL: + if ((replay_mode != REPLAY_MODE_NONE && !runstate_is_running()) + || !replay_checkpoint(CHECKPOINT_CLOCK_VIRTUAL)) { + goto out; + } + break; + case QEMU_CLOCK_HOST: + if ((replay_mode != REPLAY_MODE_NONE && !runstate_is_running()) + || !replay_checkpoint(CHECKPOINT_CLOCK_HOST)) { + goto out; + } + break; + case QEMU_CLOCK_VIRTUAL_RT: + if ((replay_mode != REPLAY_MODE_NONE && !runstate_is_running()) + || !replay_checkpoint(CHECKPOINT_CLOCK_VIRTUAL_RT)) { + goto out; + } + break; + } + current_time = qemu_clock_get_ns(timer_list->clock->type); for(;;) { qemu_mutex_lock(&timer_list->active_timers_lock); @@ -545,11 +570,18 @@ int64_t timerlistgroup_deadline_ns(QEMUTimerListGroup *tlg) { int64_t deadline = -1; QEMUClockType type; + bool play = replay_mode == REPLAY_MODE_PLAY; for (type = 0; type < QEMU_CLOCK_MAX; type++) { if (qemu_clock_use_for_deadline(tlg->tl[type]->clock->type)) { - deadline = qemu_soonest_timeout(deadline, - timerlist_deadline_ns( - tlg->tl[type])); + if (!play || tlg->tl[type]->clock->type == QEMU_CLOCK_REALTIME) { + deadline = qemu_soonest_timeout(deadline, + timerlist_deadline_ns( + tlg->tl[type])); + } else { + /* Read clock from the replay file and + do not calculate the deadline, based on virtual clock. */ + qemu_clock_get_ns(tlg->tl[type]->clock->type); + } } } return deadline; diff --git a/replay/replay-internal.h b/replay/replay-internal.h index 4414695..bf64be5 100755 --- a/replay/replay-internal.h +++ b/replay/replay-internal.h @@ -29,6 +29,10 @@ enum ReplayEvents { /* some of greater codes are reserved for clocks */ EVENT_CLOCK, EVENT_CLOCK_LAST = EVENT_CLOCK + REPLAY_CLOCK_COUNT - 1, + /* for checkpoint event */ + /* some of greater codes are reserved for checkpoints */ + EVENT_CHECKPOINT, + EVENT_CHECKPOINT_LAST = EVENT_CHECKPOINT + CHECKPOINT_COUNT - 1, EVENT_COUNT }; diff --git a/replay/replay.c b/replay/replay.c index 83b0f8c..ca9996e 100755 --- a/replay/replay.c +++ b/replay/replay.c @@ -160,3 +160,37 @@ void replay_shutdown_request(void) replay_mutex_unlock(); } } + +bool replay_checkpoint(ReplayCheckpoint checkpoint) +{ + bool res = false; + assert(EVENT_CHECKPOINT + checkpoint <= EVENT_CHECKPOINT_LAST); + replay_save_instructions(); + + if (!replay_file) { + return true; + } + + replay_mutex_lock(); + + if (replay_mode == REPLAY_MODE_PLAY) { + if (replay_next_event_is(EVENT_CHECKPOINT + checkpoint)) { + replay_finish_event(); + } else if (replay_data_kind != EVENT_ASYNC) { + res = false; + goto out; + } + replay_read_events(checkpoint); + /* replay_read_events may leave some unread events. + Return false if not all of the events associated with + checkpoint were processed */ + res = replay_data_kind != EVENT_ASYNC; + } else if (replay_mode == REPLAY_MODE_RECORD) { + replay_put_event(EVENT_CHECKPOINT + checkpoint); + replay_save_events(checkpoint); + res = true; + } +out: + replay_mutex_unlock(); + return res; +} diff --git a/replay/replay.h b/replay/replay.h index fcc93d1..e2696fe 100755 --- a/replay/replay.h +++ b/replay/replay.h @@ -26,6 +26,20 @@ enum ReplayClockKind { }; typedef enum ReplayClockKind ReplayClockKind; +/* IDs of the checkpoints */ +enum ReplayCheckpoint { + CHECKPOINT_CLOCK_WARP, + CHECKPOINT_RESET_REQUESTED, + CHECKPOINT_SUSPEND_REQUESTED, + CHECKPOINT_CLOCK_VIRTUAL, + CHECKPOINT_CLOCK_HOST, + CHECKPOINT_CLOCK_VIRTUAL_RT, + CHECKPOINT_INIT, + CHECKPOINT_RESET, + CHECKPOINT_COUNT +}; +typedef enum ReplayCheckpoint ReplayCheckpoint; + extern ReplayMode replay_mode; /* Processing the instructions */ @@ -70,6 +84,12 @@ int64_t replay_read_clock(ReplayClockKind kind); /*! Called when qemu shutdown is requested. */ void replay_shutdown_request(void); +/*! Should be called at check points in the execution. + These check points are skipped, if they were not met. + Saves checkpoint in the SAVE mode and validates in the PLAY mode. + Returns 0 in PLAY mode if checkpoint was not found. + Returns 1 in all other cases. */ +bool replay_checkpoint(ReplayCheckpoint checkpoint); /* Asynchronous events queue */ diff --git a/stubs/replay.c b/stubs/replay.c index 3e7c3fe..e737dad 100755 --- a/stubs/replay.c +++ b/stubs/replay.c @@ -1,5 +1,6 @@ #include "replay/replay.h" #include <stdlib.h> +#include "sysemu/sysemu.h" ReplayMode replay_mode; @@ -14,3 +15,13 @@ int64_t replay_read_clock(unsigned int kind) abort(); return 0; } + +bool replay_checkpoint(ReplayCheckpoint checkpoint) +{ + return 0; +} + +int runstate_is_running(void) +{ + return 0; +} diff --git a/vl.c b/vl.c index a563c9e..72ab97c 100644 --- a/vl.c +++ b/vl.c @@ -122,6 +122,7 @@ int main(int argc, char **argv) #include "qapi-event.h" #include "exec/semihost.h" #include "crypto/init.h" +#include "replay/replay.h" #define MAX_VIRTIO_CONSOLES 1 #define MAX_SCLP_CONSOLES 1 @@ -1666,15 +1667,21 @@ static void qemu_kill_report(void) static int qemu_reset_requested(void) { int r = reset_requested; - reset_requested = 0; - return r; + if (r && replay_checkpoint(CHECKPOINT_RESET_REQUESTED)) { + reset_requested = 0; + return r; + } + return false; } static int qemu_suspend_requested(void) { int r = suspend_requested; - suspend_requested = 0; - return r; + if (r && replay_checkpoint(CHECKPOINT_SUSPEND_REQUESTED)) { + suspend_requested = 0; + return r; + } + return false; } static WakeupReason qemu_wakeup_requested(void) @@ -4511,6 +4518,10 @@ int main(int argc, char **argv, char **envp) } qemu_add_globals(); + /* This checkpoint is required by replay to separate prior clock + reading from the other reads, because timer polling functions query + clock values from the log. */ + replay_checkpoint(CHECKPOINT_INIT); qdev_machine_init(); current_machine->ram_size = ram_size; @@ -4629,6 +4640,10 @@ int main(int argc, char **argv, char **envp) exit(1); } + /* This checkpoint is required by replay to separate prior clock + reading from the other reads, because timer polling functions query + clock values from the log. */ + replay_checkpoint(CHECKPOINT_RESET); qemu_system_reset(VMRESET_SILENT); register_global_state(); if (loadvm) {