On 10/03/2016 12:56, Pavel Dovgalyuk wrote: > qemu_clock_warp function is called to update virtual clock when CPU > is sleeping. This function includes replay checkpoint to make execution > deterministic in icount mode. > Record/replay module flushes async event queue at checkpoints. > Some of the events (e.g., block devices operations) include interaction > with hardware. E.g., APIC polled by block devices sets one of IRQ flags. > Flag to be set depends on currently executed thread (CPU or iothread). > Therefore in replay mode we have to process the checkpoints in the same thread > as they were recorded. > qemu_clock_warp function (and its checkpoint) may be called from different > thread. This patch decouples two different execution cases of this function: > call when CPU is sleeping from iothread and call from cpu thread to update > virtual clock. > First task is performed by qemu_start_warp_timer function. It sets warp > timer event to the moment of nearest pending virtual timer. > Second function (qemu_account_warp_timer) is called from cpu thread > before execution of the code. It advances virtual clock by adding the length > of period while CPU was sleeping. > > Signed-off-by: Pavel Dovgalyuk <pavel.dovga...@ispras.ru>
Lovely. :) One question, why doesn't icount_dummy_timer need a checkpoint? Only needs a change to the documentation: diff --git a/docs/replay.txt b/docs/replay.txt index 149727e..26dfb6e 100644 --- a/docs/replay.txt +++ b/docs/replay.txt @@ -134,11 +134,18 @@ of time. That's why we do not process a group of timers until the checkpoint event will be read from the log. Such an event allows synchronizing CPU execution and timer events. -Another checkpoints application in record/replay is instruction counting -while the virtual machine is idle. This function (qemu_clock_warp) is called -from the wait loop. It changes virtual machine state and must be deterministic -then. That is why we added checkpoint to this function to prevent its -operation in replay mode when it does not correspond to record mode. +Two other checkpoints govern the "warping" of the virtual clock. While +the virtual machine is idle, the virtual clock increments at 1 ns per +*real time* nanosecond. This is done by setting up a timer (called the +warp timer) and then incrementing the virtual clock (called "warping" +the virtual clock) as soon as the CPUs need to go out of the idle state. +These actions change virtual machine state and must be deterministic. +Two functions are used for this purpose, and each of them creates a +checkpoint. qemu_start_warp_timer checks if the CPUs are idle and if so +starts accounting real time to virtual clock. qemu_account_warp_timer +is called when the CPUs get an interrupt or when a virtual clock timer +fires, and it warps the virtual clock by the amount of real time that +has passed since qemu_start_warp_timer. Bottom halves ------------- Paolo > --- > cpus.c | 53 > +++++++++++++++++++++++++++-------------------- > include/qemu/timer.h | 14 +++++++++--- > include/sysemu/replay.h | 3 ++- > main-loop.c | 2 +- > qemu-timer.c | 4 +++- > stubs/clock-warp.c | 2 +- > 6 files changed, 48 insertions(+), 30 deletions(-) > > diff --git a/cpus.c b/cpus.c > index 85d0f87..3ab9e04 100644 > --- a/cpus.c > +++ b/cpus.c > @@ -373,6 +373,7 @@ static void icount_warp_rt(void) > static void icount_dummy_timer(void *opaque) > { > (void)opaque; > + icount_warp_rt(); > } > > void qtest_clock_warp(int64_t dest) > @@ -396,17 +397,12 @@ void qtest_clock_warp(int64_t dest) > qemu_clock_notify(QEMU_CLOCK_VIRTUAL); > } > > -void qemu_clock_warp(QEMUClockType type) > +void qemu_start_warp_timer(void) > { > int64_t clock; > int64_t deadline; > > - /* > - * There are too many global variables to make the "warp" behavior > - * 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 (!use_icount) { > return; > } > > @@ -418,29 +414,17 @@ void qemu_clock_warp(QEMUClockType type) > } > > /* warp clock deterministically in record/replay mode */ > - if (!replay_checkpoint(CHECKPOINT_CLOCK_WARP)) { > + if (!replay_checkpoint(CHECKPOINT_CLOCK_WARP_START)) { > return; > } > > - if (icount_sleep) { > - /* > - * If the CPUs have been sleeping, advance QEMU_CLOCK_VIRTUAL 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 QEMU_CLOCK_VIRTUAL timer. > - */ > - icount_warp_rt(); > - timer_del(icount_warp_timer); > - } > if (!all_cpu_threads_idle()) { > return; > } > > if (qtest_enabled()) { > /* When testing, qtest commands advance icount. */ > - return; > + return; > } > > /* We want to use the earliest deadline from ALL vm_clocks */ > @@ -496,6 +480,31 @@ void qemu_clock_warp(QEMUClockType type) > } > } > > +void qemu_account_warp_timer(void) > +{ > + int64_t clock; > + int64_t warp_delta; > + > + if (!use_icount || !icount_sleep) { > + return; > + } > + > + /* Nothing to do if the VM is stopped: QEMU_CLOCK_VIRTUAL timers > + * do not fire, so computing the deadline does not make sense. > + */ > + if (!runstate_is_running()) { > + return; > + } > + > + /* warp clock deterministically in record/replay mode */ > + if (!replay_checkpoint(CHECKPOINT_CLOCK_WARP_ACCOUNT)) { > + return; > + } > + > + timer_del(icount_warp_timer); > + icount_warp_rt(); > +} > + > static bool icount_state_needed(void *opaque) > { > return use_icount; > @@ -1496,7 +1505,7 @@ static void tcg_exec_all(void) > int r; > > /* Account partial waits to QEMU_CLOCK_VIRTUAL. */ > - qemu_clock_warp(QEMU_CLOCK_VIRTUAL); > + qemu_account_warp_timer(); > > if (next_cpu == NULL) { > next_cpu = first_cpu; > diff --git a/include/qemu/timer.h b/include/qemu/timer.h > index d0946cb..21ffec6 100644 > --- a/include/qemu/timer.h > +++ b/include/qemu/timer.h > @@ -210,12 +210,18 @@ void qemu_clock_notify(QEMUClockType type); > void qemu_clock_enable(QEMUClockType type, bool enabled); > > /** > - * qemu_clock_warp: > - * @type: the clock type > + * qemu_start_warp_timer: > + * > + * Starts a timer for virtual clock update > + */ > +void qemu_start_warp_timer(void); > + > +/** > + * qemu_account_warp_timer: > * > - * Warp a clock to a new value > + * Updates virtual clock for the time CPU was sleeping > */ > -void qemu_clock_warp(QEMUClockType type); > +void qemu_account_warp_timer(void); > > /** > * qemu_clock_register_reset_notifier: > diff --git a/include/sysemu/replay.h b/include/sysemu/replay.h > index 4763e56..6c332e5 100644 > --- a/include/sysemu/replay.h > +++ b/include/sysemu/replay.h > @@ -27,7 +27,8 @@ typedef enum ReplayClockKind ReplayClockKind; > > /* IDs of the checkpoints */ > enum ReplayCheckpoint { > - CHECKPOINT_CLOCK_WARP, > + CHECKPOINT_CLOCK_WARP_START, > + CHECKPOINT_CLOCK_WARP_ACCOUNT, > CHECKPOINT_RESET_REQUESTED, > CHECKPOINT_SUSPEND_REQUESTED, > CHECKPOINT_CLOCK_VIRTUAL, > diff --git a/main-loop.c b/main-loop.c > index 19beae7..3a7f4cd 100644 > --- a/main-loop.c > +++ b/main-loop.c > @@ -509,7 +509,7 @@ int main_loop_wait(int nonblocking) > > /* CPU thread can infinitely wait for event after > missing the warp */ > - qemu_clock_warp(QEMU_CLOCK_VIRTUAL); > + qemu_start_warp_timer(); > qemu_clock_run_all_timers(); > > return ret; > diff --git a/qemu-timer.c b/qemu-timer.c > index e98ecc9..4441fe6 100644 > --- a/qemu-timer.c > +++ b/qemu-timer.c > @@ -394,7 +394,9 @@ static bool timer_mod_ns_locked(QEMUTimerList *timer_list, > static void timerlist_rearm(QEMUTimerList *timer_list) > { > /* Interrupt execution to force deadline recalculation. */ > - qemu_clock_warp(timer_list->clock->type); > + if (timer_list->clock->type == QEMU_CLOCK_VIRTUAL) { > + qemu_start_warp_timer(); > + } > timerlist_notify(timer_list); > } > > diff --git a/stubs/clock-warp.c b/stubs/clock-warp.c > index 5ae32b9..8acb58a 100644 > --- a/stubs/clock-warp.c > +++ b/stubs/clock-warp.c > @@ -2,7 +2,7 @@ > #include "qemu-common.h" > #include "qemu/timer.h" > > -void qemu_clock_warp(QEMUClockType type) > +void qemu_start_warp_timer(void) > { > } > >