aio / timers: Use all timerlists in icount warp calculations

Notify all timerlists derived from vm_clock in icount warp
calculations.

When calculating timer delay based on vm_clock deadline, use
all timerlists.

For compatibility, maintain an apparent bug where when using
icount, if no vm_clock timer was set, qemu_clock_deadline
would return INT32_MAX and always set an icount clock expiry
about 2 seconds ahead.

NB: thread safety - when different timerlists sit on different
threads, this will need some locking.

Signed-off-by: Alex Bligh <alex@alex.org.uk>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
This commit is contained in:
Alex Bligh 2013-08-21 16:02:57 +01:00 committed by Stefan Hajnoczi
parent a3a726ae09
commit ac70aafc28
4 changed files with 67 additions and 10 deletions

46
cpus.c
View File

@ -267,7 +267,7 @@ static void icount_warp_rt(void *opaque)
qemu_icount_bias += MIN(warp_delta, delta); qemu_icount_bias += MIN(warp_delta, delta);
} }
if (qemu_clock_expired(vm_clock)) { if (qemu_clock_expired(vm_clock)) {
qemu_notify_event(); qemu_clock_notify(vm_clock);
} }
} }
vm_clock_warp_start = -1; vm_clock_warp_start = -1;
@ -278,13 +278,13 @@ void qtest_clock_warp(int64_t dest)
int64_t clock = qemu_get_clock_ns(vm_clock); int64_t clock = qemu_get_clock_ns(vm_clock);
assert(qtest_enabled()); assert(qtest_enabled());
while (clock < dest) { while (clock < dest) {
int64_t deadline = qemu_clock_deadline(vm_clock); int64_t deadline = qemu_clock_deadline_ns_all(vm_clock);
int64_t warp = MIN(dest - clock, deadline); int64_t warp = MIN(dest - clock, deadline);
qemu_icount_bias += warp; qemu_icount_bias += warp;
qemu_run_timers(vm_clock); qemu_run_timers(vm_clock);
clock = qemu_get_clock_ns(vm_clock); clock = qemu_get_clock_ns(vm_clock);
} }
qemu_notify_event(); qemu_clock_notify(vm_clock);
} }
void qemu_clock_warp(QEMUClock *clock) void qemu_clock_warp(QEMUClock *clock)
@ -319,7 +319,18 @@ void qemu_clock_warp(QEMUClock *clock)
} }
vm_clock_warp_start = qemu_get_clock_ns(rt_clock); vm_clock_warp_start = qemu_get_clock_ns(rt_clock);
deadline = qemu_clock_deadline(vm_clock); /* We want to use the earliest deadline from ALL vm_clocks */
deadline = qemu_clock_deadline_ns_all(vm_clock);
/* Maintain prior (possibly buggy) behaviour where if no deadline
* was set (as there is no vm_clock timer) or it is more than
* INT32_MAX nanoseconds ahead, we still use INT32_MAX
* nanoseconds.
*/
if ((deadline < 0) || (deadline > INT32_MAX)) {
deadline = INT32_MAX;
}
if (deadline > 0) { if (deadline > 0) {
/* /*
* Ensure the vm_clock proceeds even when the virtual CPU goes to * Ensure the vm_clock proceeds even when the virtual CPU goes to
@ -338,8 +349,8 @@ void qemu_clock_warp(QEMUClock *clock)
* packets continuously instead of every 100ms. * packets continuously instead of every 100ms.
*/ */
qemu_mod_timer(icount_warp_timer, vm_clock_warp_start + deadline); qemu_mod_timer(icount_warp_timer, vm_clock_warp_start + deadline);
} else { } else if (deadline == 0) {
qemu_notify_event(); qemu_clock_notify(vm_clock);
} }
} }
@ -866,8 +877,13 @@ static void *qemu_tcg_cpu_thread_fn(void *arg)
while (1) { while (1) {
tcg_exec_all(); tcg_exec_all();
if (use_icount && qemu_clock_deadline(vm_clock) <= 0) {
qemu_notify_event(); if (use_icount) {
int64_t deadline = qemu_clock_deadline_ns_all(vm_clock);
if (deadline == 0) {
qemu_clock_notify(vm_clock);
}
} }
qemu_tcg_wait_io_event(); qemu_tcg_wait_io_event();
} }
@ -1145,11 +1161,23 @@ static int tcg_cpu_exec(CPUArchState *env)
#endif #endif
if (use_icount) { if (use_icount) {
int64_t count; int64_t count;
int64_t deadline;
int decr; int decr;
qemu_icount -= (env->icount_decr.u16.low + env->icount_extra); qemu_icount -= (env->icount_decr.u16.low + env->icount_extra);
env->icount_decr.u16.low = 0; env->icount_decr.u16.low = 0;
env->icount_extra = 0; env->icount_extra = 0;
count = qemu_icount_round(qemu_clock_deadline(vm_clock)); deadline = qemu_clock_deadline_ns_all(vm_clock);
/* Maintain prior (possibly buggy) behaviour where if no deadline
* was set (as there is no vm_clock timer) or it is more than
* INT32_MAX nanoseconds ahead, we still use INT32_MAX
* nanoseconds.
*/
if ((deadline < 0) || (deadline > INT32_MAX)) {
deadline = INT32_MAX;
}
count = qemu_icount_round(deadline);
qemu_icount += count; qemu_icount += count;
decr = (count > 0xffff) ? 0xffff : count; decr = (count > 0xffff) ? 0xffff : count;
count -= decr; count -= decr;

View File

@ -103,6 +103,7 @@ int64_t qemu_clock_deadline(QEMUClock *clock);
* @clock: the clock to operate on * @clock: the clock to operate on
* *
* Calculate the timeout of the earliest expiring timer * Calculate the timeout of the earliest expiring timer
* on the default timer list associated with the clock
* in nanoseconds, or -1 if no timer is set to expire. * in nanoseconds, or -1 if no timer is set to expire.
* *
* Returns: time until expiry in nanoseconds or -1 * Returns: time until expiry in nanoseconds or -1
@ -125,6 +126,18 @@ int64_t qemu_clock_deadline_ns(QEMUClock *clock);
*/ */
bool qemu_clock_use_for_deadline(QEMUClock *clock); bool qemu_clock_use_for_deadline(QEMUClock *clock);
/**
* qemu_clock_use_for_deadline:
* @clock: the clock to operate on
*
* Calculate the deadline across all timer lists associated
* with a clock (as opposed to just the default one)
* in nanoseconds, or -1 if no timer is set to expire.
*
* Returns: time until expiry in nanoseconds or -1
*/
int64_t qemu_clock_deadline_ns_all(QEMUClock *clock);
/** /**
* qemu_clock_get_main_loop_timerlist: * qemu_clock_get_main_loop_timerlist:
* @clock: the clock to operate on * @clock: the clock to operate on

View File

@ -392,6 +392,22 @@ int64_t qemu_clock_deadline_ns(QEMUClock *clock)
return timerlist_deadline_ns(clock->main_loop_timerlist); return timerlist_deadline_ns(clock->main_loop_timerlist);
} }
/* Calculate the soonest deadline across all timerlists attached
* to the clock. This is used for the icount timeout so we
* ignore whether or not the clock should be used in deadline
* calculations.
*/
int64_t qemu_clock_deadline_ns_all(QEMUClock *clock)
{
int64_t deadline = -1;
QEMUTimerList *timer_list;
QLIST_FOREACH(timer_list, &clock->timerlists, list) {
deadline = qemu_soonest_timeout(deadline,
timerlist_deadline_ns(timer_list));
}
return deadline;
}
QEMUClock *timerlist_get_clock(QEMUTimerList *timer_list) QEMUClock *timerlist_get_clock(QEMUTimerList *timer_list)
{ {
return timer_list->clock; return timer_list->clock;

View File

@ -412,7 +412,7 @@ static void qtest_process_command(CharDriverState *chr, gchar **words)
if (words[1]) { if (words[1]) {
ns = strtoll(words[1], NULL, 0); ns = strtoll(words[1], NULL, 0);
} else { } else {
ns = qemu_clock_deadline(vm_clock); ns = qemu_clock_deadline_ns_all(vm_clock);
} }
qtest_clock_warp(qemu_get_clock_ns(vm_clock) + ns); qtest_clock_warp(qemu_get_clock_ns(vm_clock) + ns);
qtest_send_prefix(chr); qtest_send_prefix(chr);