#include "qemu/main-loop.h"
#include "qemu/timer.h"
#include "sysemu/replay.h"
-#include "sysemu/sysemu.h"
#include "sysemu/cpus.h"
#ifdef CONFIG_POSIX
/* We rely on BQL to protect the timerlists */
QLIST_HEAD(, QEMUTimerList) timerlists;
- NotifierList reset_notifiers;
int64_t last;
QEMUClockType type;
clock->enabled = (type == QEMU_CLOCK_VIRTUAL ? false : true);
clock->last = INT64_MIN;
QLIST_INIT(&clock->timerlists);
- notifier_list_init(&clock->reset_notifiers);
main_loop_tlg.tl[type] = timerlist_new(type, notify_cb, NULL);
}
bool progress = false;
QEMUTimerCB *cb;
void *opaque;
+ bool need_replay_checkpoint = false;
if (!atomic_read(&timer_list->active_timers)) {
return false;
break;
default:
case QEMU_CLOCK_VIRTUAL:
- if (!replay_checkpoint(CHECKPOINT_CLOCK_VIRTUAL)) {
- goto out;
+ if (replay_mode != REPLAY_MODE_NONE) {
+ /* Checkpoint for virtual clock is redundant in cases where
+ * it's being triggered with only non-EXTERNAL timers, because
+ * these timers don't change guest state directly.
+ * Since it has conditional dependence on specific timers, it is
+ * subject to race conditions and requires special handling.
+ * See below.
+ */
+ need_replay_checkpoint = true;
}
break;
case QEMU_CLOCK_HOST:
break;
}
+ /*
+ * Extract expired timers from active timers list and and process them.
+ *
+ * In rr mode we need "filtered" checkpointing for virtual clock. The
+ * checkpoint must be recorded/replayed before processing any non-EXTERNAL timer,
+ * and that must only be done once since the clock value stays the same. Because
+ * non-EXTERNAL timers may appear in the timers list while it being processed,
+ * the checkpoint can be issued at a time until no timers are left and we are
+ * done".
+ */
current_time = qemu_clock_get_ns(timer_list->clock->type);
- for(;;) {
- qemu_mutex_lock(&timer_list->active_timers_lock);
- ts = timer_list->active_timers;
+ qemu_mutex_lock(&timer_list->active_timers_lock);
+ while ((ts = timer_list->active_timers)) {
if (!timer_expired_ns(ts, current_time)) {
- qemu_mutex_unlock(&timer_list->active_timers_lock);
+ /* No expired timers left. The checkpoint can be skipped
+ * if no timers fired or they were all external.
+ */
break;
}
+ if (need_replay_checkpoint
+ && !(ts->attributes & QEMU_TIMER_ATTR_EXTERNAL)) {
+ /* once we got here, checkpoint clock only once */
+ need_replay_checkpoint = false;
+ qemu_mutex_unlock(&timer_list->active_timers_lock);
+ if (!replay_checkpoint(CHECKPOINT_CLOCK_VIRTUAL)) {
+ goto out;
+ }
+ qemu_mutex_lock(&timer_list->active_timers_lock);
+ /* The lock was released; start over again in case the list was
+ * modified.
+ */
+ continue;
+ }
/* remove timer from the list before calling the callback */
timer_list->active_timers = ts->next;
ts->expire_time = -1;
cb = ts->cb;
opaque = ts->opaque;
- qemu_mutex_unlock(&timer_list->active_timers_lock);
/* run the callback (the timer list can be modified) */
+ qemu_mutex_unlock(&timer_list->active_timers_lock);
cb(opaque);
+ qemu_mutex_lock(&timer_list->active_timers_lock);
+
progress = true;
}
+ qemu_mutex_unlock(&timer_list->active_timers_lock);
out:
qemu_event_set(&timer_list->timers_done_ev);
int64_t qemu_clock_get_ns(QEMUClockType type)
{
- int64_t now, last;
+ int64_t now;
QEMUClock *clock = qemu_clock_ptr(type);
switch (type) {
}
case QEMU_CLOCK_HOST:
now = REPLAY_CLOCK(REPLAY_CLOCK_HOST, get_clock_realtime());
- last = clock->last;
clock->last = now;
- if (now < last || now > (last + get_max_clock_jump())) {
- notifier_list_notify(&clock->reset_notifiers, &now);
- }
return now;
case QEMU_CLOCK_VIRTUAL_RT:
return REPLAY_CLOCK(REPLAY_CLOCK_VIRTUAL_RT, cpu_get_clock());
clock->last = last;
}
-void qemu_clock_register_reset_notifier(QEMUClockType type,
- Notifier *notifier)
-{
- QEMUClock *clock = qemu_clock_ptr(type);
- notifier_list_add(&clock->reset_notifiers, notifier);
-}
-
-void qemu_clock_unregister_reset_notifier(QEMUClockType type,
- Notifier *notifier)
-{
- notifier_remove(notifier);
-}
-
void init_clocks(QEMUTimerListNotifyCB *notify_cb)
{
QEMUClockType type;