From ab33fcda9f96b9195dfb3fcf5bd9bb5383caeaea Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 13 Apr 2011 10:03:44 +0200 Subject: enable vm_clock to "warp" in the iothread+icount case The previous patch however is not enough, because if the virtual CPU goes to sleep waiting for a future timer interrupt to wake it up, qemu deadlocks. The timer interrupt never comes because time is driven by icount, but the vCPU doesn't run any insns. You could say that VCPUs should never go to sleep in icount mode if there is a pending vm_clock timer; rather time should just warp to the next vm_clock event with no sleep ever taking place. Even better, you can sleep for some time related to the time left until the next event, to avoid that the warps are too visible externally; for example, you could be sending network packets continously instead of every 100ms. This is what this patch implements. qemu_clock_warp is called: 1) whenever a vm_clock timer is adjusted, to ensure the warp_timer is synchronized; 2) at strategic points in the CPU thread, to make sure the insn counter is synchronized before the CPU starts running. In any case, the warp_timer is disabled while the CPU is running, because the insn counter will then be making progress on its own. Signed-off-by: Paolo Bonzini Tested-by: Edgar E. Iglesias Signed-off-by: Edgar E. Iglesias --- qemu-timer.c | 94 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 93 insertions(+), 1 deletion(-) (limited to 'qemu-timer.c') diff --git a/qemu-timer.c b/qemu-timer.c index 50f1943afd..4959688895 100644 --- a/qemu-timer.c +++ b/qemu-timer.c @@ -153,6 +153,8 @@ void cpu_disable_ticks(void) struct QEMUClock { int type; int enabled; + + QEMUTimer *warp_timer; }; struct QEMUTimer { @@ -386,6 +388,90 @@ void qemu_clock_enable(QEMUClock *clock, int enabled) clock->enabled = enabled; } +static int64_t vm_clock_warp_start; + +static void icount_warp_rt(void *opaque) +{ + if (vm_clock_warp_start == -1) { + return; + } + + if (vm_running) { + int64_t clock = qemu_get_clock_ns(rt_clock); + int64_t warp_delta = clock - vm_clock_warp_start; + if (use_icount == 1) { + qemu_icount_bias += warp_delta; + } else { + /* + * In adaptive mode, do not let the vm_clock run too + * far ahead of real time. + */ + int64_t cur_time = cpu_get_clock(); + int64_t cur_icount = qemu_get_clock_ns(vm_clock); + int64_t delta = cur_time - cur_icount; + qemu_icount_bias += MIN(warp_delta, delta); + } + if (qemu_timer_expired(active_timers[QEMU_CLOCK_VIRTUAL], + qemu_get_clock_ns(vm_clock))) { + qemu_notify_event(); + } + } + vm_clock_warp_start = -1; +} + +void qemu_clock_warp(QEMUClock *clock) +{ + int64_t deadline; + + if (!clock->warp_timer) { + return; + } + + /* + * 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. + */ + assert(clock == vm_clock); + + /* + * If the CPUs have been sleeping, advance the vm_clock 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 vm_clock timer. + */ + icount_warp_rt(NULL); + if (!all_cpu_threads_idle() || !active_timers[clock->type]) { + qemu_del_timer(clock->warp_timer); + return; + } + + vm_clock_warp_start = qemu_get_clock_ns(rt_clock); + deadline = qemu_next_deadline(); + if (deadline > 0) { + /* + * Ensure the vm_clock proceeds even when the virtual CPU goes to + * sleep. Otherwise, the CPU might be waiting for a future timer + * interrupt to wake it up, but the interrupt never comes because + * the vCPU isn't running any insns and thus doesn't advance the + * vm_clock. + * + * An extreme solution for this problem would be to never let VCPUs + * sleep in icount mode if there is a pending vm_clock timer; rather + * time could just advance to the next vm_clock event. Instead, we + * do stop VCPUs and only advance vm_clock after some "real" time, + * (related to the time left until the next event) has passed. This + * rt_clock timer will do this. This avoids that the warps are too + * visible externally---for example, you will not be sending network + * packets continously instead of every 100ms. + */ + qemu_mod_timer(clock->warp_timer, vm_clock_warp_start + deadline); + } else { + qemu_notify_event(); + } +} + QEMUTimer *qemu_new_timer(QEMUClock *clock, int scale, QEMUTimerCB *cb, void *opaque) { @@ -454,8 +540,10 @@ static void qemu_mod_timer_ns(QEMUTimer *ts, int64_t expire_time) qemu_rearm_alarm_timer(alarm_timer); } /* Interrupt execution to force deadline recalculation. */ - if (use_icount) + qemu_clock_warp(ts->clock); + if (use_icount) { qemu_notify_event(); + } } } @@ -576,6 +664,10 @@ void configure_icount(const char *option) if (!option) return; +#ifdef CONFIG_IOTHREAD + vm_clock->warp_timer = qemu_new_timer_ns(rt_clock, icount_warp_rt, NULL); +#endif + if (strcmp(option, "auto") != 0) { icount_time_shift = strtol(option, NULL, 0); use_icount = 1; -- cgit v1.2.3 From 1ece93a91b8435b815ce7214cf41bbbbe7929e8b Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 13 Apr 2011 10:03:45 +0200 Subject: Revert wrong fixes for -icount in the iothread case This reverts commits 225d02cd and c9f7383c. While some parts of the latter could be saved, I preferred a smooth, complete revert. Signed-off-by: Paolo Bonzini Tested-by: Edgar E. Iglesias Signed-off-by: Edgar E. Iglesias --- qemu-timer.c | 66 +++++++++++++++++++++++++++++++++--------------------------- 1 file changed, 36 insertions(+), 30 deletions(-) (limited to 'qemu-timer.c') diff --git a/qemu-timer.c b/qemu-timer.c index 4959688895..7998f37a51 100644 --- a/qemu-timer.c +++ b/qemu-timer.c @@ -110,9 +110,12 @@ static int64_t cpu_get_clock(void) } } +#ifndef CONFIG_IOTHREAD static int64_t qemu_icount_delta(void) { - if (use_icount == 1) { + if (!use_icount) { + return 5000 * (int64_t) 1000000; + } else if (use_icount == 1) { /* When not using an adaptive execution frequency we tend to get badly out of sync with real time, so just delay for a reasonable amount of time. */ @@ -121,6 +124,7 @@ static int64_t qemu_icount_delta(void) return cpu_get_icount() - cpu_get_clock(); } } +#endif /* enable cpu_get_ticks() */ void cpu_enable_ticks(void) @@ -1147,39 +1151,41 @@ void quit_timers(void) int qemu_calculate_timeout(void) { +#ifndef CONFIG_IOTHREAD int timeout; - int64_t add; - int64_t delta; - /* When using icount, making forward progress with qemu_icount when the - guest CPU is idle is critical. We only use the static io-thread timeout - for non icount runs. */ - if (!use_icount || !vm_running) { - return 5000; - } - - /* Advance virtual time to the next event. */ - delta = qemu_icount_delta(); - if (delta > 0) { - /* If virtual time is ahead of real time then just - wait for IO. */ - timeout = (delta + 999999) / 1000000; - } else { - /* Wait for either IO to occur or the next - timer event. */ - add = qemu_next_deadline(); - /* We advance the timer before checking for IO. - Limit the amount we advance so that early IO - activity won't get the guest too far ahead. */ - if (add > 10000000) - add = 10000000; - delta += add; - qemu_icount += qemu_icount_round (add); - timeout = delta / 1000000; - if (timeout < 0) - timeout = 0; + if (!vm_running) + timeout = 5000; + else { + /* XXX: use timeout computed from timers */ + int64_t add; + int64_t delta; + /* Advance virtual time to the next event. */ + delta = qemu_icount_delta(); + if (delta > 0) { + /* If virtual time is ahead of real time then just + wait for IO. */ + timeout = (delta + 999999) / 1000000; + } else { + /* Wait for either IO to occur or the next + timer event. */ + add = qemu_next_deadline(); + /* We advance the timer before checking for IO. + Limit the amount we advance so that early IO + activity won't get the guest too far ahead. */ + if (add > 10000000) + add = 10000000; + delta += add; + qemu_icount += qemu_icount_round (add); + timeout = delta / 1000000; + if (timeout < 0) + timeout = 0; + } } return timeout; +#else /* CONFIG_IOTHREAD */ + return 1000; +#endif } -- cgit v1.2.3 From cb842c90a485d9dbf05fa51e1500b3c1a1931256 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 13 Apr 2011 10:03:46 +0200 Subject: qemu_next_deadline should not consider host-time timers It is purely for icount-based virtual timers. And now that we got the code right, rename the function to clarify the intended scope. Signed-off-by: Paolo Bonzini Tested-by: Edgar E. Iglesias Signed-off-by: Edgar E. Iglesias --- qemu-timer.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) (limited to 'qemu-timer.c') diff --git a/qemu-timer.c b/qemu-timer.c index 7998f37a51..b8c0c8870d 100644 --- a/qemu-timer.c +++ b/qemu-timer.c @@ -452,7 +452,7 @@ void qemu_clock_warp(QEMUClock *clock) } vm_clock_warp_start = qemu_get_clock_ns(rt_clock); - deadline = qemu_next_deadline(); + deadline = qemu_next_icount_deadline(); if (deadline > 0) { /* * Ensure the vm_clock proceeds even when the virtual CPU goes to @@ -765,21 +765,16 @@ static void host_alarm_handler(int host_signum) } } -int64_t qemu_next_deadline(void) +int64_t qemu_next_icount_deadline(void) { /* To avoid problems with overflow limit this to 2^32. */ int64_t delta = INT32_MAX; + assert(use_icount); if (active_timers[QEMU_CLOCK_VIRTUAL]) { delta = active_timers[QEMU_CLOCK_VIRTUAL]->expire_time - qemu_get_clock_ns(vm_clock); } - if (active_timers[QEMU_CLOCK_HOST]) { - int64_t hdelta = active_timers[QEMU_CLOCK_HOST]->expire_time - - qemu_get_clock_ns(host_clock); - if (hdelta < delta) - delta = hdelta; - } if (delta < 0) delta = 0; @@ -1169,7 +1164,7 @@ int qemu_calculate_timeout(void) } else { /* Wait for either IO to occur or the next timer event. */ - add = qemu_next_deadline(); + add = qemu_next_icount_deadline(); /* We advance the timer before checking for IO. Limit the amount we advance so that early IO activity won't get the guest too far ahead. */ -- cgit v1.2.3 From 45c7b37fb9c452bcc6fce0ac429ea1d1aa1f9e51 Mon Sep 17 00:00:00 2001 From: Stefan Weil Date: Thu, 24 Mar 2011 21:31:24 +0100 Subject: qemu-timer: Add and use new function qemu_timer_expired_ns This simply moves code which is used three times into a new function thus improving readability. Signed-off-by: Stefan Weil --- qemu-timer.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) (limited to 'qemu-timer.c') diff --git a/qemu-timer.c b/qemu-timer.c index b8c0c8870d..f771697574 100644 --- a/qemu-timer.c +++ b/qemu-timer.c @@ -183,6 +183,11 @@ struct qemu_alarm_timer { static struct qemu_alarm_timer *alarm_timer; +static bool qemu_timer_expired_ns(QEMUTimer *timer_head, int64_t current_time) +{ + return timer_head && (timer_head->expire_time <= current_time); +} + int qemu_alarm_pending(void) { return alarm_timer->pending; @@ -528,10 +533,9 @@ static void qemu_mod_timer_ns(QEMUTimer *ts, int64_t expire_time) pt = &active_timers[ts->clock->type]; for(;;) { t = *pt; - if (!t) - break; - if (t->expire_time > expire_time) + if (!qemu_timer_expired_ns(t, expire_time)) { break; + } pt = &t->next; } ts->expire_time = expire_time; @@ -570,9 +574,7 @@ int qemu_timer_pending(QEMUTimer *ts) int qemu_timer_expired(QEMUTimer *timer_head, int64_t current_time) { - if (!timer_head) - return 0; - return (timer_head->expire_time <= current_time * timer_head->scale); + return qemu_timer_expired_ns(timer_head, current_time * timer_head->scale); } static void qemu_run_timers(QEMUClock *clock) @@ -587,8 +589,9 @@ static void qemu_run_timers(QEMUClock *clock) ptimer_head = &active_timers[clock->type]; for(;;) { ts = *ptimer_head; - if (!ts || ts->expire_time > current_time) + if (!qemu_timer_expired_ns(ts, current_time)) { break; + } /* remove timer from the list before calling the callback */ *ptimer_head = ts->next; ts->next = NULL; -- cgit v1.2.3 From cd0544ee550cb125d752f765e9d9e7c3fdf464b2 Mon Sep 17 00:00:00 2001 From: Stefan Weil Date: Sun, 10 Apr 2011 20:15:09 +0200 Subject: qemu-timer: Avoid type casts The type casts are no longer needed after some small changes in struct qemu_alarm_timer. This also improves readability of the code. Signed-off-by: Stefan Weil --- qemu-timer.c | 42 ++++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 20 deletions(-) (limited to 'qemu-timer.c') diff --git a/qemu-timer.c b/qemu-timer.c index f771697574..e8e5e15c8e 100644 --- a/qemu-timer.c +++ b/qemu-timer.c @@ -175,8 +175,12 @@ struct qemu_alarm_timer { int (*start)(struct qemu_alarm_timer *t); void (*stop)(struct qemu_alarm_timer *t); void (*rearm)(struct qemu_alarm_timer *t); - void *priv; - +#if defined(__linux__) + int fd; + timer_t timer; +#elif defined(_WIN32) + HANDLE timer; +#endif char expired; char pending; }; @@ -295,18 +299,16 @@ static struct qemu_alarm_timer alarm_timers[] = { #ifndef _WIN32 #ifdef __linux__ {"dynticks", dynticks_start_timer, - dynticks_stop_timer, dynticks_rearm_timer, NULL}, + dynticks_stop_timer, dynticks_rearm_timer}, /* HPET - if available - is preferred */ - {"hpet", hpet_start_timer, hpet_stop_timer, NULL, NULL}, + {"hpet", hpet_start_timer, hpet_stop_timer, NULL}, /* ...otherwise try RTC */ - {"rtc", rtc_start_timer, rtc_stop_timer, NULL, NULL}, + {"rtc", rtc_start_timer, rtc_stop_timer, NULL}, #endif - {"unix", unix_start_timer, unix_stop_timer, NULL, NULL}, + {"unix", unix_start_timer, unix_stop_timer, NULL}, #else - {"dynticks", win32_start_timer, - win32_stop_timer, win32_rearm_timer, NULL}, - {"win32", win32_start_timer, - win32_stop_timer, NULL, NULL}, + {"dynticks", win32_start_timer, win32_stop_timer, win32_rearm_timer}, + {"win32", win32_start_timer, win32_stop_timer, NULL}, #endif {NULL, } }; @@ -864,7 +866,7 @@ static int hpet_start_timer(struct qemu_alarm_timer *t) goto fail; enable_sigio_timer(fd); - t->priv = (void *)(long)fd; + t->fd = fd; return 0; fail: @@ -874,7 +876,7 @@ fail: static void hpet_stop_timer(struct qemu_alarm_timer *t) { - int fd = (long)t->priv; + int fd = t->fd; close(fd); } @@ -903,14 +905,14 @@ static int rtc_start_timer(struct qemu_alarm_timer *t) enable_sigio_timer(rtc_fd); - t->priv = (void *)(long)rtc_fd; + t->fd = rtc_fd; return 0; } static void rtc_stop_timer(struct qemu_alarm_timer *t) { - int rtc_fd = (long)t->priv; + int rtc_fd = t->fd; close(rtc_fd); } @@ -945,21 +947,21 @@ static int dynticks_start_timer(struct qemu_alarm_timer *t) return -1; } - t->priv = (void *)(long)host_timer; + t->timer = host_timer; return 0; } static void dynticks_stop_timer(struct qemu_alarm_timer *t) { - timer_t host_timer = (timer_t)(long)t->priv; + timer_t host_timer = t->timer; timer_delete(host_timer); } static void dynticks_rearm_timer(struct qemu_alarm_timer *t) { - timer_t host_timer = (timer_t)(long)t->priv; + timer_t host_timer = t->timer; struct itimerspec timeout; int64_t nearest_delta_ns = INT64_MAX; int64_t current_ns; @@ -1061,13 +1063,13 @@ static int win32_start_timer(struct qemu_alarm_timer *t) return -1; } - t->priv = (PVOID) hTimer; + t->timer = hTimer; return 0; } static void win32_stop_timer(struct qemu_alarm_timer *t) { - HANDLE hTimer = t->priv; + HANDLE hTimer = t->timer; if (hTimer) { DeleteTimerQueueTimer(NULL, hTimer, NULL); @@ -1076,7 +1078,7 @@ static void win32_stop_timer(struct qemu_alarm_timer *t) static void win32_rearm_timer(struct qemu_alarm_timer *t) { - HANDLE hTimer = t->priv; + HANDLE hTimer = t->timer; int nearest_delta_ms; BOOLEAN success; -- cgit v1.2.3 From 2f9cba0c148af32fad6813480f5c92efe17c2d49 Mon Sep 17 00:00:00 2001 From: Stefan Weil Date: Tue, 5 Apr 2011 18:34:21 +0200 Subject: qemu-timer: Fix timers for w32 Commit 68c23e5520e8286d79d96ab47c0ea722ceb75041 removed the multimedia timer, but this timer is needed for certain Linux kernels. Otherwise Linux boot stops with this error: MP-BIOS bug: 8254 timer not connected to IO-APIC So the multimedia timer is added again here. Cc: Paolo Bonzini Signed-off-by: Stefan Weil --- qemu-timer.c | 96 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) (limited to 'qemu-timer.c') diff --git a/qemu-timer.c b/qemu-timer.c index e8e5e15c8e..4141b6edbe 100644 --- a/qemu-timer.c +++ b/qemu-timer.c @@ -215,6 +215,10 @@ static void qemu_rearm_alarm_timer(struct qemu_alarm_timer *t) #ifdef _WIN32 +static int mm_start_timer(struct qemu_alarm_timer *t); +static void mm_stop_timer(struct qemu_alarm_timer *t); +static void mm_rearm_timer(struct qemu_alarm_timer *t); + static int win32_start_timer(struct qemu_alarm_timer *t); static void win32_stop_timer(struct qemu_alarm_timer *t); static void win32_rearm_timer(struct qemu_alarm_timer *t); @@ -307,6 +311,8 @@ static struct qemu_alarm_timer alarm_timers[] = { #endif {"unix", unix_start_timer, unix_stop_timer, NULL}, #else + {"mmtimer", mm_start_timer, mm_stop_timer, NULL}, + {"mmtimer2", mm_start_timer, mm_stop_timer, mm_rearm_timer}, {"dynticks", win32_start_timer, win32_stop_timer, win32_rearm_timer}, {"win32", win32_start_timer, win32_stop_timer, NULL}, #endif @@ -1040,6 +1046,96 @@ static void unix_stop_timer(struct qemu_alarm_timer *t) #ifdef _WIN32 +static MMRESULT mm_timer; +static unsigned mm_period; + +static void CALLBACK mm_alarm_handler(UINT uTimerID, UINT uMsg, + DWORD_PTR dwUser, DWORD_PTR dw1, + DWORD_PTR dw2) +{ + struct qemu_alarm_timer *t = alarm_timer; + if (!t) { + return; + } + if (alarm_has_dynticks(t) || qemu_next_alarm_deadline() <= 0) { + t->expired = alarm_has_dynticks(t); + t->pending = 1; + qemu_notify_event(); + } +} + +static int mm_start_timer(struct qemu_alarm_timer *t) +{ + TIMECAPS tc; + UINT flags; + + memset(&tc, 0, sizeof(tc)); + timeGetDevCaps(&tc, sizeof(tc)); + + mm_period = tc.wPeriodMin; + timeBeginPeriod(mm_period); + + flags = TIME_CALLBACK_FUNCTION; + if (alarm_has_dynticks(t)) { + flags |= TIME_ONESHOT; + } else { + flags |= TIME_PERIODIC; + } + + mm_timer = timeSetEvent(1, /* interval (ms) */ + mm_period, /* resolution */ + mm_alarm_handler, /* function */ + (DWORD_PTR)t, /* parameter */ + flags); + + if (!mm_timer) { + fprintf(stderr, "Failed to initialize win32 alarm timer: %ld\n", + GetLastError()); + timeEndPeriod(mm_period); + return -1; + } + + return 0; +} + +static void mm_stop_timer(struct qemu_alarm_timer *t) +{ + timeKillEvent(mm_timer); + timeEndPeriod(mm_period); +} + +static void mm_rearm_timer(struct qemu_alarm_timer *t) +{ + int nearest_delta_ms; + + assert(alarm_has_dynticks(t)); + if (!active_timers[QEMU_CLOCK_REALTIME] && + !active_timers[QEMU_CLOCK_VIRTUAL] && + !active_timers[QEMU_CLOCK_HOST]) { + return; + } + + timeKillEvent(mm_timer); + + nearest_delta_ms = (qemu_next_alarm_deadline() + 999999) / 1000000; + if (nearest_delta_ms < 1) { + nearest_delta_ms = 1; + } + mm_timer = timeSetEvent(nearest_delta_ms, + mm_period, + mm_alarm_handler, + (DWORD_PTR)t, + TIME_ONESHOT | TIME_CALLBACK_FUNCTION); + + if (!mm_timer) { + fprintf(stderr, "Failed to re-arm win32 alarm timer %ld\n", + GetLastError()); + + timeEndPeriod(mm_period); + exit(1); + } +} + static int win32_start_timer(struct qemu_alarm_timer *t) { HANDLE hTimer; -- cgit v1.2.3