qemu-timer: avoid checkpoints for virtual clock timers in external subsystems
Adds EXTERNAL attribute definition to qemu timers subsystem and assigns it to virtual clock timers, used in slirp (ICMP IPv6) and ui (key queue). Virtual clock processing in rr mode can use this attribute instead of a separate clock type. Fixes:87f4fe7653
Fixes:775a412bf8
Fixes:9888091404
Signed-off-by: Artem Pisarenko <artem.k.pisarenko@gmail.com> Message-Id: <e771f96ab94e86b54b9a783c974f2af3009fe5d1.1539764043.git.artem.k.pisarenko@gmail.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
parent
89a603a0c8
commit
e81f86790f
@ -60,9 +60,17 @@ typedef enum {
|
||||
* Each attribute corresponds to one bit. Attributes modify the processing
|
||||
* of timers when they fire.
|
||||
*
|
||||
* No attributes defined currently.
|
||||
* The following attributes are available:
|
||||
*
|
||||
* QEMU_TIMER_ATTR_EXTERNAL: drives external subsystem
|
||||
*
|
||||
* Timers with this attribute do not recorded in rr mode, therefore it could be
|
||||
* used for the subsystems that operate outside the guest core. Applicable only
|
||||
* with virtual clock type.
|
||||
*/
|
||||
|
||||
#define QEMU_TIMER_ATTR_EXTERNAL BIT(0)
|
||||
|
||||
typedef struct QEMUTimerList QEMUTimerList;
|
||||
|
||||
struct QEMUTimerListGroup {
|
||||
|
@ -27,7 +27,9 @@ void icmp6_init(Slirp *slirp)
|
||||
return;
|
||||
}
|
||||
|
||||
slirp->ra_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, ra_timer_handler, slirp);
|
||||
slirp->ra_timer = timer_new_full(NULL, QEMU_CLOCK_VIRTUAL,
|
||||
SCALE_MS, QEMU_TIMER_ATTR_EXTERNAL,
|
||||
ra_timer_handler, slirp);
|
||||
timer_mod(slirp->ra_timer,
|
||||
qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + NDP_Interval);
|
||||
}
|
||||
|
@ -448,8 +448,9 @@ void qemu_input_event_send_key_delay(uint32_t delay_ms)
|
||||
}
|
||||
|
||||
if (!kbd_timer) {
|
||||
kbd_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, qemu_input_queue_process,
|
||||
&kbd_queue);
|
||||
kbd_timer = timer_new_full(NULL, QEMU_CLOCK_VIRTUAL,
|
||||
SCALE_MS, QEMU_TIMER_ATTR_EXTERNAL,
|
||||
qemu_input_queue_process, &kbd_queue);
|
||||
}
|
||||
if (queue_count < queue_limit) {
|
||||
qemu_input_queue_delay(&kbd_queue, kbd_timer,
|
||||
|
@ -489,6 +489,7 @@ bool timerlist_run_timers(QEMUTimerList *timer_list)
|
||||
bool progress = false;
|
||||
QEMUTimerCB *cb;
|
||||
void *opaque;
|
||||
bool need_replay_checkpoint = false;
|
||||
|
||||
if (!atomic_read(&timer_list->active_timers)) {
|
||||
return false;
|
||||
@ -504,8 +505,15 @@ bool timerlist_run_timers(QEMUTimerList *timer_list)
|
||||
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:
|
||||
@ -520,14 +528,39 @@ bool timerlist_run_timers(QEMUTimerList *timer_list)
|
||||
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;
|
||||
@ -535,12 +568,15 @@ bool timerlist_run_timers(QEMUTimerList *timer_list)
|
||||
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);
|
||||
|
Loading…
Reference in New Issue
Block a user