X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=qemu-timer.c;h=cd026c6495fd8b0d0f91e1468c0177701ca69572;hb=refs%2Fheads%2Fstable-1.0;hp=50f1943afddafeb29c39259ab631c2dd5260ab71;hpb=6d5ad9bf9318b1555d5567cd7e1760a51a55ebf8;p=qemu.git diff --git a/qemu-timer.c b/qemu-timer.c index 50f1943af..cd026c649 100644 --- a/qemu-timer.c +++ b/qemu-timer.c @@ -39,15 +39,6 @@ #include #endif -#ifdef __linux__ -#include -#include -/* For the benefit of older linux systems which don't supply it, - we use a local copy of hpet.h. */ -/* #include */ -#include "hpet.h" -#endif - #ifdef _WIN32 #include #include @@ -55,94 +46,6 @@ #include "qemu-timer.h" -/* Conversion factor from emulated instructions to virtual clock ticks. */ -int icount_time_shift; -/* Arbitrarily pick 1MIPS as the minimum allowable speed. */ -#define MAX_ICOUNT_SHIFT 10 -/* Compensate for varying guest execution speed. */ -int64_t qemu_icount_bias; -static QEMUTimer *icount_rt_timer; -static QEMUTimer *icount_vm_timer; - -/***********************************************************/ -/* guest cycle counter */ - -typedef struct TimersState { - int64_t cpu_ticks_prev; - int64_t cpu_ticks_offset; - int64_t cpu_clock_offset; - int32_t cpu_ticks_enabled; - int64_t dummy; -} TimersState; - -TimersState timers_state; - -/* return the host CPU cycle counter and handle stop/restart */ -int64_t cpu_get_ticks(void) -{ - if (use_icount) { - return cpu_get_icount(); - } - if (!timers_state.cpu_ticks_enabled) { - return timers_state.cpu_ticks_offset; - } else { - int64_t ticks; - ticks = cpu_get_real_ticks(); - if (timers_state.cpu_ticks_prev > ticks) { - /* Note: non increasing ticks may happen if the host uses - software suspend */ - timers_state.cpu_ticks_offset += timers_state.cpu_ticks_prev - ticks; - } - timers_state.cpu_ticks_prev = ticks; - return ticks + timers_state.cpu_ticks_offset; - } -} - -/* return the host CPU monotonic timer and handle stop/restart */ -static int64_t cpu_get_clock(void) -{ - int64_t ti; - if (!timers_state.cpu_ticks_enabled) { - return timers_state.cpu_clock_offset; - } else { - ti = get_clock(); - return ti + timers_state.cpu_clock_offset; - } -} - -static int64_t qemu_icount_delta(void) -{ - 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. */ - return 0; - } else { - return cpu_get_icount() - cpu_get_clock(); - } -} - -/* enable cpu_get_ticks() */ -void cpu_enable_ticks(void) -{ - if (!timers_state.cpu_ticks_enabled) { - timers_state.cpu_ticks_offset -= cpu_get_real_ticks(); - timers_state.cpu_clock_offset -= get_clock(); - timers_state.cpu_ticks_enabled = 1; - } -} - -/* disable cpu_get_ticks() : the clock is stopped. You must not call - cpu_get_ticks() after that. */ -void cpu_disable_ticks(void) -{ - if (timers_state.cpu_ticks_enabled) { - timers_state.cpu_ticks_offset = cpu_get_ticks(); - timers_state.cpu_clock_offset = cpu_get_clock(); - timers_state.cpu_ticks_enabled = 0; - } -} - /***********************************************************/ /* timers */ @@ -153,6 +56,11 @@ void cpu_disable_ticks(void) struct QEMUClock { int type; int enabled; + + QEMUTimer *active_timers; + + NotifierList reset_notifiers; + int64_t last; }; struct QEMUTimer { @@ -168,15 +76,24 @@ struct qemu_alarm_timer { char const *name; int (*start)(struct qemu_alarm_timer *t); void (*stop)(struct qemu_alarm_timer *t); - void (*rearm)(struct qemu_alarm_timer *t); - void *priv; - + void (*rearm)(struct qemu_alarm_timer *t, int64_t nearest_delta_ns); +#if defined(__linux__) + int fd; + timer_t timer; +#elif defined(_WIN32) + HANDLE timer; +#endif char expired; char pending; }; 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; @@ -187,12 +104,46 @@ static inline int alarm_has_dynticks(struct qemu_alarm_timer *t) return !!t->rearm; } +static int64_t qemu_next_alarm_deadline(void) +{ + int64_t delta; + int64_t rtdelta; + + if (!use_icount && vm_clock->active_timers) { + delta = vm_clock->active_timers->expire_time - + qemu_get_clock_ns(vm_clock); + } else { + delta = INT32_MAX; + } + if (host_clock->active_timers) { + int64_t hdelta = host_clock->active_timers->expire_time - + qemu_get_clock_ns(host_clock); + if (hdelta < delta) { + delta = hdelta; + } + } + if (rt_clock->active_timers) { + rtdelta = (rt_clock->active_timers->expire_time - + qemu_get_clock_ns(rt_clock)); + if (rtdelta < delta) { + delta = rtdelta; + } + } + + return delta; +} + static void qemu_rearm_alarm_timer(struct qemu_alarm_timer *t) { - if (!alarm_has_dynticks(t)) + int64_t nearest_delta_ns; + assert(alarm_has_dynticks(t)); + if (!rt_clock->active_timers && + !vm_clock->active_timers && + !host_clock->active_timers) { return; - - t->rearm(t); + } + nearest_delta_ns = qemu_next_alarm_deadline(); + t->rearm(t, nearest_delta_ns); } /* TODO: MIN_TIMER_REARM_NS should be optimized */ @@ -200,102 +151,40 @@ 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, int64_t delta); + 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); +static void win32_rearm_timer(struct qemu_alarm_timer *t, int64_t delta); #else static int unix_start_timer(struct qemu_alarm_timer *t); static void unix_stop_timer(struct qemu_alarm_timer *t); +static void unix_rearm_timer(struct qemu_alarm_timer *t, int64_t delta); #ifdef __linux__ static int dynticks_start_timer(struct qemu_alarm_timer *t); static void dynticks_stop_timer(struct qemu_alarm_timer *t); -static void dynticks_rearm_timer(struct qemu_alarm_timer *t); - -static int hpet_start_timer(struct qemu_alarm_timer *t); -static void hpet_stop_timer(struct qemu_alarm_timer *t); - -static int rtc_start_timer(struct qemu_alarm_timer *t); -static void rtc_stop_timer(struct qemu_alarm_timer *t); +static void dynticks_rearm_timer(struct qemu_alarm_timer *t, int64_t delta); #endif /* __linux__ */ #endif /* _WIN32 */ -/* Correlation between real and virtual time is always going to be - fairly approximate, so ignore small variation. - When the guest is idle real and virtual time will be aligned in - the IO wait loop. */ -#define ICOUNT_WOBBLE (get_ticks_per_sec() / 10) - -static void icount_adjust(void) -{ - int64_t cur_time; - int64_t cur_icount; - int64_t delta; - static int64_t last_delta; - /* If the VM is not running, then do nothing. */ - if (!vm_running) - return; - - cur_time = cpu_get_clock(); - cur_icount = qemu_get_clock_ns(vm_clock); - delta = cur_icount - cur_time; - /* FIXME: This is a very crude algorithm, somewhat prone to oscillation. */ - if (delta > 0 - && last_delta + ICOUNT_WOBBLE < delta * 2 - && icount_time_shift > 0) { - /* The guest is getting too far ahead. Slow time down. */ - icount_time_shift--; - } - if (delta < 0 - && last_delta - ICOUNT_WOBBLE > delta * 2 - && icount_time_shift < MAX_ICOUNT_SHIFT) { - /* The guest is getting too far behind. Speed time up. */ - icount_time_shift++; - } - last_delta = delta; - qemu_icount_bias = cur_icount - (qemu_icount << icount_time_shift); -} - -static void icount_adjust_rt(void * opaque) -{ - qemu_mod_timer(icount_rt_timer, - qemu_get_clock_ms(rt_clock) + 1000); - icount_adjust(); -} - -static void icount_adjust_vm(void * opaque) -{ - qemu_mod_timer(icount_vm_timer, - qemu_get_clock_ns(vm_clock) + get_ticks_per_sec() / 10); - icount_adjust(); -} - -int64_t qemu_icount_round(int64_t count) -{ - return (count + (1 << icount_time_shift) - 1) >> icount_time_shift; -} - static struct qemu_alarm_timer alarm_timers[] = { #ifndef _WIN32 #ifdef __linux__ {"dynticks", dynticks_start_timer, - dynticks_stop_timer, dynticks_rearm_timer, NULL}, - /* HPET - if available - is preferred */ - {"hpet", hpet_start_timer, hpet_stop_timer, NULL, NULL}, - /* ...otherwise try RTC */ - {"rtc", rtc_start_timer, rtc_stop_timer, NULL, NULL}, + dynticks_stop_timer, dynticks_rearm_timer}, #endif - {"unix", unix_start_timer, unix_stop_timer, NULL, NULL}, + {"unix", unix_start_timer, unix_stop_timer, unix_rearm_timer}, #else - {"dynticks", win32_start_timer, - win32_stop_timer, win32_rearm_timer, NULL}, - {"win32", win32_start_timer, - win32_stop_timer, NULL, NULL}, + {"mmtimer", mm_start_timer, mm_stop_timer, mm_rearm_timer}, + {"dynticks", win32_start_timer, win32_stop_timer, win32_rearm_timer}, #endif {NULL, } }; @@ -323,7 +212,7 @@ void configure_alarms(char const *opt) exit(0); } - arg = qemu_strdup(opt); + arg = g_strdup(opt); /* Reorder the array */ name = strtok(arg, ","); @@ -352,7 +241,7 @@ next: name = strtok(NULL, ","); } - qemu_free(arg); + g_free(arg); if (cur) { /* Disable remaining timers */ @@ -364,26 +253,54 @@ next: } } -#define QEMU_NUM_CLOCKS 3 - QEMUClock *rt_clock; QEMUClock *vm_clock; QEMUClock *host_clock; -static QEMUTimer *active_timers[QEMU_NUM_CLOCKS]; - static QEMUClock *qemu_new_clock(int type) { QEMUClock *clock; - clock = qemu_mallocz(sizeof(QEMUClock)); + + clock = g_malloc0(sizeof(QEMUClock)); clock->type = type; clock->enabled = 1; + clock->last = INT64_MIN; + notifier_list_init(&clock->reset_notifiers); return clock; } void qemu_clock_enable(QEMUClock *clock, int enabled) { + bool old = clock->enabled; clock->enabled = enabled; + if (enabled && !old) { + qemu_rearm_alarm_timer(alarm_timer); + } +} + +int64_t qemu_clock_has_timers(QEMUClock *clock) +{ + return !!clock->active_timers; +} + +int64_t qemu_clock_expired(QEMUClock *clock) +{ + return (clock->active_timers && + clock->active_timers->expire_time < qemu_get_clock_ns(clock)); +} + +int64_t qemu_clock_deadline(QEMUClock *clock) +{ + /* To avoid problems with overflow limit this to 2^32. */ + int64_t delta = INT32_MAX; + + if (clock->active_timers) { + delta = clock->active_timers->expire_time - qemu_get_clock_ns(clock); + } + if (delta < 0) { + delta = 0; + } + return delta; } QEMUTimer *qemu_new_timer(QEMUClock *clock, int scale, @@ -391,7 +308,7 @@ QEMUTimer *qemu_new_timer(QEMUClock *clock, int scale, { QEMUTimer *ts; - ts = qemu_mallocz(sizeof(QEMUTimer)); + ts = g_malloc0(sizeof(QEMUTimer)); ts->clock = clock; ts->cb = cb; ts->opaque = opaque; @@ -401,7 +318,7 @@ QEMUTimer *qemu_new_timer(QEMUClock *clock, int scale, void qemu_free_timer(QEMUTimer *ts) { - qemu_free(ts); + g_free(ts); } /* stop a timer, but do not dealloc it */ @@ -411,7 +328,7 @@ void qemu_del_timer(QEMUTimer *ts) /* NOTE: this code must be signal safe because qemu_timer_expired() can be called from a signal. */ - pt = &active_timers[ts->clock->type]; + pt = &ts->clock->active_timers; for(;;) { t = *pt; if (!t) @@ -426,7 +343,7 @@ void qemu_del_timer(QEMUTimer *ts) /* modify the current timer so that it will be fired when current_time >= expire_time. The corresponding callback will be called. */ -static void qemu_mod_timer_ns(QEMUTimer *ts, int64_t expire_time) +void qemu_mod_timer_ns(QEMUTimer *ts, int64_t expire_time) { QEMUTimer **pt, *t; @@ -435,13 +352,12 @@ static void qemu_mod_timer_ns(QEMUTimer *ts, int64_t expire_time) /* add the timer in the sorted list */ /* NOTE: this code must be signal safe because qemu_timer_expired() can be called from a signal. */ - pt = &active_timers[ts->clock->type]; + pt = &ts->clock->active_timers; 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; @@ -449,18 +365,18 @@ static void qemu_mod_timer_ns(QEMUTimer *ts, int64_t expire_time) *pt = ts; /* Rearm if necessary */ - if (pt == &active_timers[ts->clock->type]) { + if (pt == &ts->clock->active_timers) { if (!alarm_timer->pending) { 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(); + } } } -/* modify the current timer so that it will be fired when current_time - >= expire_time. The corresponding callback will be called. */ void qemu_mod_timer(QEMUTimer *ts, int64_t expire_time) { qemu_mod_timer_ns(ts, expire_time * ts->scale); @@ -469,7 +385,7 @@ void qemu_mod_timer(QEMUTimer *ts, int64_t expire_time) int qemu_timer_pending(QEMUTimer *ts) { QEMUTimer *t; - for(t = active_timers[ts->clock->type]; t != NULL; t = t->next) { + for (t = ts->clock->active_timers; t != NULL; t = t->next) { if (t == ts) return 1; } @@ -478,9 +394,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) @@ -492,11 +406,12 @@ static void qemu_run_timers(QEMUClock *clock) return; current_time = qemu_get_clock_ns(clock); - ptimer_head = &active_timers[clock->type]; + ptimer_head = &clock->active_timers; 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; @@ -508,6 +423,8 @@ static void qemu_run_timers(QEMUClock *clock) int64_t qemu_get_clock_ns(QEMUClock *clock) { + int64_t now, last; + switch(clock->type) { case QEMU_CLOCK_REALTIME: return get_clock(); @@ -519,86 +436,36 @@ int64_t qemu_get_clock_ns(QEMUClock *clock) return cpu_get_clock(); } case QEMU_CLOCK_HOST: - return get_clock_realtime(); + now = get_clock_realtime(); + last = clock->last; + clock->last = now; + if (now < last) { + notifier_list_notify(&clock->reset_notifiers, &now); + } + return now; } } -void init_clocks(void) +void qemu_register_clock_reset_notifier(QEMUClock *clock, Notifier *notifier) { - rt_clock = qemu_new_clock(QEMU_CLOCK_REALTIME); - vm_clock = qemu_new_clock(QEMU_CLOCK_VIRTUAL); - host_clock = qemu_new_clock(QEMU_CLOCK_HOST); - - rtc_clock = host_clock; + notifier_list_add(&clock->reset_notifiers, notifier); } -/* save a timer */ -void qemu_put_timer(QEMUFile *f, QEMUTimer *ts) +void qemu_unregister_clock_reset_notifier(QEMUClock *clock, Notifier *notifier) { - uint64_t expire_time; - - if (qemu_timer_pending(ts)) { - expire_time = ts->expire_time; - } else { - expire_time = -1; - } - qemu_put_be64(f, expire_time); + notifier_list_remove(&clock->reset_notifiers, notifier); } -void qemu_get_timer(QEMUFile *f, QEMUTimer *ts) +void init_clocks(void) { - uint64_t expire_time; - - expire_time = qemu_get_be64(f); - if (expire_time != -1) { - qemu_mod_timer_ns(ts, expire_time); - } else { - qemu_del_timer(ts); - } + rt_clock = qemu_new_clock(QEMU_CLOCK_REALTIME); + vm_clock = qemu_new_clock(QEMU_CLOCK_VIRTUAL); + host_clock = qemu_new_clock(QEMU_CLOCK_HOST); } -static const VMStateDescription vmstate_timers = { - .name = "timer", - .version_id = 2, - .minimum_version_id = 1, - .minimum_version_id_old = 1, - .fields = (VMStateField []) { - VMSTATE_INT64(cpu_ticks_offset, TimersState), - VMSTATE_INT64(dummy, TimersState), - VMSTATE_INT64_V(cpu_clock_offset, TimersState, 2), - VMSTATE_END_OF_LIST() - } -}; - -void configure_icount(const char *option) +uint64_t qemu_timer_expire_time_ns(QEMUTimer *ts) { - vmstate_register(NULL, 0, &vmstate_timers, &timers_state); - if (!option) - return; - - if (strcmp(option, "auto") != 0) { - icount_time_shift = strtol(option, NULL, 0); - use_icount = 1; - return; - } - - use_icount = 2; - - /* 125MIPS seems a reasonable initial guess at the guest speed. - It will be corrected fairly quickly anyway. */ - icount_time_shift = 3; - - /* Have both realtime and virtual time triggers for speed adjustment. - The realtime trigger catches emulated time passing too slowly, - the virtual time trigger catches emulated time passing too fast. - Realtime triggers occur even when idle, so use them less frequently - than VM triggers. */ - icount_rt_timer = qemu_new_timer_ms(rt_clock, icount_adjust_rt, NULL); - qemu_mod_timer(icount_rt_timer, - qemu_get_clock_ms(rt_clock) + 1000); - icount_vm_timer = qemu_new_timer_ns(vm_clock, icount_adjust_vm, NULL); - qemu_mod_timer(icount_vm_timer, - qemu_get_clock_ns(vm_clock) + get_ticks_per_sec() / 10); + return qemu_timer_pending(ts) ? ts->expire_time : -1; } void qemu_run_all_timers(void) @@ -612,16 +479,11 @@ void qemu_run_all_timers(void) } /* vm time timers */ - if (vm_running) { - qemu_run_timers(vm_clock); - } - + qemu_run_timers(vm_clock); qemu_run_timers(rt_clock); qemu_run_timers(host_clock); } -static int64_t qemu_next_alarm_deadline(void); - #ifdef _WIN32 static void CALLBACK host_alarm_handler(PVOID lpParam, BOOLEAN unused) #else @@ -669,157 +531,9 @@ static void host_alarm_handler(int host_signum) } } -int64_t qemu_next_deadline(void) -{ - /* To avoid problems with overflow limit this to 2^32. */ - int64_t delta = INT32_MAX; - - 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; - - return delta; -} - -static int64_t qemu_next_alarm_deadline(void) -{ - int64_t delta; - int64_t rtdelta; - - if (!use_icount && active_timers[QEMU_CLOCK_VIRTUAL]) { - delta = active_timers[QEMU_CLOCK_VIRTUAL]->expire_time - - qemu_get_clock_ns(vm_clock); - } else { - delta = INT32_MAX; - } - 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 (active_timers[QEMU_CLOCK_REALTIME]) { - rtdelta = (active_timers[QEMU_CLOCK_REALTIME]->expire_time - - qemu_get_clock_ns(rt_clock)); - if (rtdelta < delta) - delta = rtdelta; - } - - return delta; -} - #if defined(__linux__) -#define RTC_FREQ 1024 - -static void enable_sigio_timer(int fd) -{ - struct sigaction act; - - /* timer signal */ - sigfillset(&act.sa_mask); - act.sa_flags = 0; - act.sa_handler = host_alarm_handler; - - sigaction(SIGIO, &act, NULL); - fcntl_setfl(fd, O_ASYNC); - fcntl(fd, F_SETOWN, getpid()); -} - -static int hpet_start_timer(struct qemu_alarm_timer *t) -{ - struct hpet_info info; - int r, fd; - - fd = qemu_open("/dev/hpet", O_RDONLY); - if (fd < 0) - return -1; - - /* Set frequency */ - r = ioctl(fd, HPET_IRQFREQ, RTC_FREQ); - if (r < 0) { - fprintf(stderr, "Could not configure '/dev/hpet' to have a 1024Hz timer. This is not a fatal\n" - "error, but for better emulation accuracy type:\n" - "'echo 1024 > /proc/sys/dev/hpet/max-user-freq' as root.\n"); - goto fail; - } - - /* Check capabilities */ - r = ioctl(fd, HPET_INFO, &info); - if (r < 0) - goto fail; - - /* Enable periodic mode */ - r = ioctl(fd, HPET_EPI, 0); - if (info.hi_flags && (r < 0)) - goto fail; - - /* Enable interrupt */ - r = ioctl(fd, HPET_IE_ON, 0); - if (r < 0) - goto fail; - - enable_sigio_timer(fd); - t->priv = (void *)(long)fd; - - return 0; -fail: - close(fd); - return -1; -} - -static void hpet_stop_timer(struct qemu_alarm_timer *t) -{ - int fd = (long)t->priv; - - close(fd); -} - -static int rtc_start_timer(struct qemu_alarm_timer *t) -{ - int rtc_fd; - unsigned long current_rtc_freq = 0; - - TFR(rtc_fd = qemu_open("/dev/rtc", O_RDONLY)); - if (rtc_fd < 0) - return -1; - ioctl(rtc_fd, RTC_IRQP_READ, ¤t_rtc_freq); - if (current_rtc_freq != RTC_FREQ && - ioctl(rtc_fd, RTC_IRQP_SET, RTC_FREQ) < 0) { - fprintf(stderr, "Could not configure '/dev/rtc' to have a 1024 Hz timer. This is not a fatal\n" - "error, but for better emulation accuracy either use a 2.6 host Linux kernel or\n" - "type 'echo 1024 > /proc/sys/dev/rtc/max-user-freq' as root.\n"); - goto fail; - } - if (ioctl(rtc_fd, RTC_PIE_ON, 0) < 0) { - fail: - close(rtc_fd); - return -1; - } - - enable_sigio_timer(rtc_fd); - - t->priv = (void *)(long)rtc_fd; - - return 0; -} - -static void rtc_stop_timer(struct qemu_alarm_timer *t) -{ - int rtc_fd = (long)t->priv; - - close(rtc_fd); -} +#include "compatfd.h" static int dynticks_start_timer(struct qemu_alarm_timer *t) { @@ -840,6 +554,12 @@ static int dynticks_start_timer(struct qemu_alarm_timer *t) memset(&ev, 0, sizeof(ev)); ev.sigev_value.sival_int = 0; ev.sigev_notify = SIGEV_SIGNAL; +#ifdef SIGEV_THREAD_ID + if (qemu_signalfd_available()) { + ev.sigev_notify = SIGEV_THREAD_ID; + ev._sigev_un._tid = qemu_get_thread_id(); + } +#endif /* SIGEV_THREAD_ID */ ev.sigev_signo = SIGALRM; if (timer_create(CLOCK_REALTIME, &ev, &host_timer)) { @@ -851,32 +571,25 @@ 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) +static void dynticks_rearm_timer(struct qemu_alarm_timer *t, + int64_t nearest_delta_ns) { - 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; - assert(alarm_has_dynticks(t)); - if (!active_timers[QEMU_CLOCK_REALTIME] && - !active_timers[QEMU_CLOCK_VIRTUAL] && - !active_timers[QEMU_CLOCK_HOST]) - return; - - nearest_delta_ns = qemu_next_alarm_deadline(); if (nearest_delta_ns < MIN_TIMER_REARM_NS) nearest_delta_ns = MIN_TIMER_REARM_NS; @@ -908,8 +621,6 @@ static void dynticks_rearm_timer(struct qemu_alarm_timer *t) static int unix_start_timer(struct qemu_alarm_timer *t) { struct sigaction act; - struct itimerval itv; - int err; /* timer signal */ sigfillset(&act.sa_mask); @@ -917,18 +628,28 @@ static int unix_start_timer(struct qemu_alarm_timer *t) act.sa_handler = host_alarm_handler; sigaction(SIGALRM, &act, NULL); + return 0; +} - itv.it_interval.tv_sec = 0; - /* for i386 kernel 2.6 to get 1 ms */ - itv.it_interval.tv_usec = 999; - itv.it_value.tv_sec = 0; - itv.it_value.tv_usec = 10 * 1000; +static void unix_rearm_timer(struct qemu_alarm_timer *t, + int64_t nearest_delta_ns) +{ + struct itimerval itv; + int err; - err = setitimer(ITIMER_REAL, &itv, NULL); - if (err) - return -1; + if (nearest_delta_ns < MIN_TIMER_REARM_NS) + nearest_delta_ns = MIN_TIMER_REARM_NS; - return 0; + itv.it_interval.tv_sec = 0; + itv.it_interval.tv_usec = 0; /* 0 for one-shot timer */ + itv.it_value.tv_sec = nearest_delta_ns / 1000000000; + itv.it_value.tv_usec = (nearest_delta_ns % 1000000000) / 1000; + err = setitimer(ITIMER_REAL, &itv, NULL); + if (err) { + perror("setitimer"); + fprintf(stderr, "Internal timer error: aborting\n"); + exit(1); + } } static void unix_stop_timer(struct qemu_alarm_timer *t) @@ -944,6 +665,87 @@ 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, int64_t delta) +{ + int nearest_delta_ms = (delta + 999999) / 1000000; + if (nearest_delta_ms < 1) { + nearest_delta_ms = 1; + } + + timeKillEvent(mm_timer); + 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; @@ -967,32 +769,27 @@ 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); } } -static void win32_rearm_timer(struct qemu_alarm_timer *t) +static void win32_rearm_timer(struct qemu_alarm_timer *t, + int64_t nearest_delta_ns) { - HANDLE hTimer = t->priv; + HANDLE hTimer = t->timer; int nearest_delta_ms; BOOLEAN success; - assert(alarm_has_dynticks(t)); - if (!active_timers[QEMU_CLOCK_REALTIME] && - !active_timers[QEMU_CLOCK_VIRTUAL] && - !active_timers[QEMU_CLOCK_HOST]) - return; - - nearest_delta_ms = (qemu_next_alarm_deadline() + 999999) / 1000000; + nearest_delta_ms = (nearest_delta_ns + 999999) / 1000000; if (nearest_delta_ms < 1) { nearest_delta_ms = 1; } @@ -1011,10 +808,11 @@ static void win32_rearm_timer(struct qemu_alarm_timer *t) #endif /* _WIN32 */ -static void alarm_timer_on_change_state_rearm(void *opaque, int running, int reason) +static void quit_timers(void) { - if (running) - qemu_rearm_alarm_timer((struct qemu_alarm_timer *) opaque); + struct qemu_alarm_timer *t = alarm_timer; + alarm_timer = NULL; + t->stop(t); } int init_timer_alarm(void) @@ -1036,9 +834,9 @@ int init_timer_alarm(void) } /* first event is at time 0 */ + atexit(quit_timers); t->pending = 1; alarm_timer = t; - qemu_add_vm_change_state_handler(alarm_timer_on_change_state_rearm, t); return 0; @@ -1046,48 +844,8 @@ fail: return err; } -void quit_timers(void) -{ - struct qemu_alarm_timer *t = alarm_timer; - alarm_timer = NULL; - t->stop(t); -} - int qemu_calculate_timeout(void) { - 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; - } - - return timeout; + return 1000; }