diff options
author | Paolo Bonzini <pbonzini@redhat.com> | 2010-03-10 11:38:55 +0100 |
---|---|---|
committer | Anthony Liguori <aliguori@us.ibm.com> | 2010-03-17 11:16:15 -0500 |
commit | db1a49726c3c6cbe9cbca5b118e80c0fec65e8dd (patch) | |
tree | dc4f42c9d7476e43f8a0c5547e31c193b71fbe96 /qemu-timer.c | |
parent | d6f4ade214a9f74dca9495b83a24ff9c113e4f9a (diff) | |
download | qemu-db1a49726c3c6cbe9cbca5b118e80c0fec65e8dd.tar.gz qemu-db1a49726c3c6cbe9cbca5b118e80c0fec65e8dd.tar.bz2 qemu-db1a49726c3c6cbe9cbca5b118e80c0fec65e8dd.zip |
split out qemu-timer.c
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
Diffstat (limited to 'qemu-timer.c')
-rw-r--r-- | qemu-timer.c | 1203 |
1 files changed, 1203 insertions, 0 deletions
diff --git a/qemu-timer.c b/qemu-timer.c new file mode 100644 index 0000000000..329d3a4e60 --- /dev/null +++ b/qemu-timer.c @@ -0,0 +1,1203 @@ +/* + * QEMU System Emulator + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "sysemu.h" +#include "net.h" +#include "monitor.h" +#include "console.h" + +#include "hw/hw.h" + +#include <unistd.h> +#include <fcntl.h> +#include <time.h> +#include <errno.h> +#include <sys/time.h> +#include <signal.h> + +#ifdef __linux__ +#include <sys/ioctl.h> +#include <linux/rtc.h> +/* For the benefit of older linux systems which don't supply it, + we use a local copy of hpet.h. */ +/* #include <linux/hpet.h> */ +#include "hpet.h" +#endif + +#ifdef _WIN32 +#include <windows.h> +#include <mmsystem.h> +#endif + +#include "cpu-defs.h" +#include "qemu-timer.h" +#include "exec-all.h" + +/* Conversion factor from emulated instructions to virtual clock ticks. */ +static int icount_time_shift; +/* Arbitrarily pick 1MIPS as the minimum allowable speed. */ +#define MAX_ICOUNT_SHIFT 10 +/* Compensate for varying guest execution speed. */ +static int64_t qemu_icount_bias; +static QEMUTimer *icount_rt_timer; +static QEMUTimer *icount_vm_timer; + + +/***********************************************************/ +/* real time host monotonic timer */ + + +static int64_t get_clock_realtime(void) +{ + struct timeval tv; + + gettimeofday(&tv, NULL); + return tv.tv_sec * 1000000000LL + (tv.tv_usec * 1000); +} + +#ifdef WIN32 + +static int64_t clock_freq; + +static void init_get_clock(void) +{ + LARGE_INTEGER freq; + int ret; + ret = QueryPerformanceFrequency(&freq); + if (ret == 0) { + fprintf(stderr, "Could not calibrate ticks\n"); + exit(1); + } + clock_freq = freq.QuadPart; +} + +static int64_t get_clock(void) +{ + LARGE_INTEGER ti; + QueryPerformanceCounter(&ti); + return muldiv64(ti.QuadPart, get_ticks_per_sec(), clock_freq); +} + +#else + +static int use_rt_clock; + +static void init_get_clock(void) +{ + use_rt_clock = 0; +#if defined(__linux__) || (defined(__FreeBSD__) && __FreeBSD_version >= 500000) \ + || defined(__DragonFly__) || defined(__FreeBSD_kernel__) + { + struct timespec ts; + if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) { + use_rt_clock = 1; + } + } +#endif +} + +static int64_t get_clock(void) +{ +#if defined(__linux__) || (defined(__FreeBSD__) && __FreeBSD_version >= 500000) \ + || defined(__DragonFly__) || defined(__FreeBSD_kernel__) + if (use_rt_clock) { + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return ts.tv_sec * 1000000000LL + ts.tv_nsec; + } else +#endif + { + /* XXX: using gettimeofday leads to problems if the date + changes, so it should be avoided. */ + return get_clock_realtime(); + } +} +#endif + +/* Return the virtual CPU time, based on the instruction counter. */ +static int64_t cpu_get_icount(void) +{ + int64_t icount; + CPUState *env = cpu_single_env;; + icount = qemu_icount; + if (env) { + if (!can_do_io(env)) + fprintf(stderr, "Bad clock read\n"); + icount -= (env->icount_decr.u16.low + env->icount_extra); + } + return qemu_icount_bias + (icount << icount_time_shift); +} + +/***********************************************************/ +/* 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; + } +} + +#ifndef CONFIG_IOTHREAD +static int64_t qemu_icount_delta(void) +{ + 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. */ + return 0; + } else { + return cpu_get_icount() - cpu_get_clock(); + } +} +#endif + +/* 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 */ + +#define QEMU_CLOCK_REALTIME 0 +#define QEMU_CLOCK_VIRTUAL 1 +#define QEMU_CLOCK_HOST 2 + +struct QEMUClock { + int type; + int enabled; + /* XXX: add frequency */ +}; + +struct QEMUTimer { + QEMUClock *clock; + int64_t expire_time; + QEMUTimerCB *cb; + void *opaque; + struct QEMUTimer *next; +}; + +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; + + char expired; + char pending; +}; + +static struct qemu_alarm_timer *alarm_timer; + +int qemu_alarm_pending(void) +{ + return alarm_timer->pending; +} + +static inline int alarm_has_dynticks(struct qemu_alarm_timer *t) +{ + return !!t->rearm; +} + +static void qemu_rearm_alarm_timer(struct qemu_alarm_timer *t) +{ + if (!alarm_has_dynticks(t)) + return; + + t->rearm(t); +} + +/* TODO: MIN_TIMER_REARM_US should be optimized */ +#define MIN_TIMER_REARM_US 250 + +#ifdef _WIN32 + +struct qemu_alarm_win32 { + MMRESULT timerId; + unsigned int period; +} alarm_win32_data = {0, 0}; + +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); + +#else + +static int unix_start_timer(struct qemu_alarm_timer *t); +static void unix_stop_timer(struct qemu_alarm_timer *t); + +#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); + +#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(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(rt_clock) + 1000); + icount_adjust(); +} + +static void icount_adjust_vm(void * opaque) +{ + qemu_mod_timer(icount_vm_timer, + qemu_get_clock(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}, +#endif + {"unix", unix_start_timer, unix_stop_timer, NULL, NULL}, +#else + {"dynticks", win32_start_timer, + win32_stop_timer, win32_rearm_timer, &alarm_win32_data}, + {"win32", win32_start_timer, + win32_stop_timer, NULL, &alarm_win32_data}, +#endif + {NULL, } +}; + +static void show_available_alarms(void) +{ + int i; + + printf("Available alarm timers, in order of precedence:\n"); + for (i = 0; alarm_timers[i].name; i++) + printf("%s\n", alarm_timers[i].name); +} + +void configure_alarms(char const *opt) +{ + int i; + int cur = 0; + int count = ARRAY_SIZE(alarm_timers) - 1; + char *arg; + char *name; + struct qemu_alarm_timer tmp; + + if (!strcmp(opt, "?")) { + show_available_alarms(); + exit(0); + } + + arg = qemu_strdup(opt); + + /* Reorder the array */ + name = strtok(arg, ","); + while (name) { + for (i = 0; i < count && alarm_timers[i].name; i++) { + if (!strcmp(alarm_timers[i].name, name)) + break; + } + + if (i == count) { + fprintf(stderr, "Unknown clock %s\n", name); + goto next; + } + + if (i < cur) + /* Ignore */ + goto next; + + /* Swap */ + tmp = alarm_timers[i]; + alarm_timers[i] = alarm_timers[cur]; + alarm_timers[cur] = tmp; + + cur++; +next: + name = strtok(NULL, ","); + } + + qemu_free(arg); + + if (cur) { + /* Disable remaining timers */ + for (i = cur; i < count; i++) + alarm_timers[i].name = NULL; + } else { + show_available_alarms(); + exit(1); + } +} + +#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->type = type; + clock->enabled = 1; + return clock; +} + +void qemu_clock_enable(QEMUClock *clock, int enabled) +{ + clock->enabled = enabled; +} + +QEMUTimer *qemu_new_timer(QEMUClock *clock, QEMUTimerCB *cb, void *opaque) +{ + QEMUTimer *ts; + + ts = qemu_mallocz(sizeof(QEMUTimer)); + ts->clock = clock; + ts->cb = cb; + ts->opaque = opaque; + return ts; +} + +void qemu_free_timer(QEMUTimer *ts) +{ + qemu_free(ts); +} + +/* stop a timer, but do not dealloc it */ +void qemu_del_timer(QEMUTimer *ts) +{ + QEMUTimer **pt, *t; + + /* NOTE: this code must be signal safe because + qemu_timer_expired() can be called from a signal. */ + pt = &active_timers[ts->clock->type]; + for(;;) { + t = *pt; + if (!t) + break; + if (t == ts) { + *pt = t->next; + break; + } + pt = &t->next; + } +} + +/* 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) +{ + QEMUTimer **pt, *t; + + qemu_del_timer(ts); + + /* 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]; + for(;;) { + t = *pt; + if (!t) + break; + if (t->expire_time > expire_time) + break; + pt = &t->next; + } + ts->expire_time = expire_time; + ts->next = *pt; + *pt = ts; + + /* Rearm if necessary */ + if (pt == &active_timers[ts->clock->type]) { + if (!alarm_timer->pending) { + qemu_rearm_alarm_timer(alarm_timer); + } + /* Interrupt execution to force deadline recalculation. */ + if (use_icount) + qemu_notify_event(); + } +} + +int qemu_timer_pending(QEMUTimer *ts) +{ + QEMUTimer *t; + for(t = active_timers[ts->clock->type]; t != NULL; t = t->next) { + if (t == ts) + return 1; + } + return 0; +} + +int qemu_timer_expired(QEMUTimer *timer_head, int64_t current_time) +{ + if (!timer_head) + return 0; + return (timer_head->expire_time <= current_time); +} + +static void qemu_run_timers(QEMUClock *clock) +{ + QEMUTimer **ptimer_head, *ts; + int64_t current_time; + + if (!clock->enabled) + return; + + current_time = qemu_get_clock (clock); + ptimer_head = &active_timers[clock->type]; + for(;;) { + ts = *ptimer_head; + if (!ts || ts->expire_time > current_time) + break; + /* remove timer from the list before calling the callback */ + *ptimer_head = ts->next; + ts->next = NULL; + + /* run the callback (the timer list can be modified) */ + ts->cb(ts->opaque); + } +} + +int64_t qemu_get_clock(QEMUClock *clock) +{ + switch(clock->type) { + case QEMU_CLOCK_REALTIME: + return get_clock() / 1000000; + default: + case QEMU_CLOCK_VIRTUAL: + if (use_icount) { + return cpu_get_icount(); + } else { + return cpu_get_clock(); + } + case QEMU_CLOCK_HOST: + return get_clock_realtime(); + } +} + +int64_t qemu_get_clock_ns(QEMUClock *clock) +{ + switch(clock->type) { + case QEMU_CLOCK_REALTIME: + return get_clock(); + default: + case QEMU_CLOCK_VIRTUAL: + if (use_icount) { + return cpu_get_icount(); + } else { + return cpu_get_clock(); + } + case QEMU_CLOCK_HOST: + return get_clock_realtime(); + } +} + +void init_clocks(void) +{ + init_get_clock(); + 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; +} + +/* save a timer */ +void qemu_put_timer(QEMUFile *f, QEMUTimer *ts) +{ + uint64_t expire_time; + + if (qemu_timer_pending(ts)) { + expire_time = ts->expire_time; + } else { + expire_time = -1; + } + qemu_put_be64(f, expire_time); +} + +void qemu_get_timer(QEMUFile *f, QEMUTimer *ts) +{ + uint64_t expire_time; + + expire_time = qemu_get_be64(f); + if (expire_time != -1) { + qemu_mod_timer(ts, expire_time); + } else { + qemu_del_timer(ts); + } +} + +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) +{ + vmstate_register(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(rt_clock, icount_adjust_rt, NULL); + qemu_mod_timer(icount_rt_timer, + qemu_get_clock(rt_clock) + 1000); + icount_vm_timer = qemu_new_timer(vm_clock, icount_adjust_vm, NULL); + qemu_mod_timer(icount_vm_timer, + qemu_get_clock(vm_clock) + get_ticks_per_sec() / 10); +} + +void qemu_run_all_timers(void) +{ + /* rearm timer, if not periodic */ + if (alarm_timer->expired) { + alarm_timer->expired = 0; + qemu_rearm_alarm_timer(alarm_timer); + } + + alarm_timer->pending = 0; + + /* vm time timers */ + if (vm_running) { + qemu_run_timers(vm_clock); + } + + qemu_run_timers(rt_clock); + qemu_run_timers(host_clock); +} + +#ifdef _WIN32 +static void CALLBACK host_alarm_handler(UINT uTimerID, UINT uMsg, + DWORD_PTR dwUser, DWORD_PTR dw1, + DWORD_PTR dw2) +#else +static void host_alarm_handler(int host_signum) +#endif +{ + struct qemu_alarm_timer *t = alarm_timer; + if (!t) + return; + +#if 0 +#define DISP_FREQ 1000 + { + static int64_t delta_min = INT64_MAX; + static int64_t delta_max, delta_cum, last_clock, delta, ti; + static int count; + ti = qemu_get_clock(vm_clock); + if (last_clock != 0) { + delta = ti - last_clock; + if (delta < delta_min) + delta_min = delta; + if (delta > delta_max) + delta_max = delta; + delta_cum += delta; + if (++count == DISP_FREQ) { + printf("timer: min=%" PRId64 " us max=%" PRId64 " us avg=%" PRId64 " us avg_freq=%0.3f Hz\n", + muldiv64(delta_min, 1000000, get_ticks_per_sec()), + muldiv64(delta_max, 1000000, get_ticks_per_sec()), + muldiv64(delta_cum, 1000000 / DISP_FREQ, get_ticks_per_sec()), + (double)get_ticks_per_sec() / ((double)delta_cum / DISP_FREQ)); + count = 0; + delta_min = INT64_MAX; + delta_max = 0; + delta_cum = 0; + } + } + last_clock = ti; + } +#endif + if (alarm_has_dynticks(t) || + (!use_icount && + qemu_timer_expired(active_timers[QEMU_CLOCK_VIRTUAL], + qemu_get_clock(vm_clock))) || + qemu_timer_expired(active_timers[QEMU_CLOCK_REALTIME], + qemu_get_clock(rt_clock)) || + qemu_timer_expired(active_timers[QEMU_CLOCK_HOST], + qemu_get_clock(host_clock))) { + + t->expired = alarm_has_dynticks(t); + t->pending = 1; + qemu_notify_event(); + } +} + +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(vm_clock); + } + if (active_timers[QEMU_CLOCK_HOST]) { + int64_t hdelta = active_timers[QEMU_CLOCK_HOST]->expire_time - + qemu_get_clock(host_clock); + if (hdelta < delta) + delta = hdelta; + } + + if (delta < 0) + delta = 0; + + return delta; +} + +#ifndef _WIN32 + +#if defined(__linux__) + +#define RTC_FREQ 1024 + +static uint64_t qemu_next_deadline_dyntick(void) +{ + int64_t delta; + int64_t rtdelta; + + if (use_icount) + delta = INT32_MAX; + else + delta = (qemu_next_deadline() + 999) / 1000; + + if (active_timers[QEMU_CLOCK_REALTIME]) { + rtdelta = (active_timers[QEMU_CLOCK_REALTIME]->expire_time - + qemu_get_clock(rt_clock))*1000; + if (rtdelta < delta) + delta = rtdelta; + } + + if (delta < MIN_TIMER_REARM_US) + delta = MIN_TIMER_REARM_US; + + return delta; +} + +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); +} + +static int dynticks_start_timer(struct qemu_alarm_timer *t) +{ + struct sigevent ev; + timer_t host_timer; + struct sigaction act; + + sigfillset(&act.sa_mask); + act.sa_flags = 0; + act.sa_handler = host_alarm_handler; + + sigaction(SIGALRM, &act, NULL); + + /* + * Initialize ev struct to 0 to avoid valgrind complaining + * about uninitialized data in timer_create call + */ + memset(&ev, 0, sizeof(ev)); + ev.sigev_value.sival_int = 0; + ev.sigev_notify = SIGEV_SIGNAL; + ev.sigev_signo = SIGALRM; + + if (timer_create(CLOCK_REALTIME, &ev, &host_timer)) { + perror("timer_create"); + + /* disable dynticks */ + fprintf(stderr, "Dynamic Ticks disabled\n"); + + return -1; + } + + t->priv = (void *)(long)host_timer; + + return 0; +} + +static void dynticks_stop_timer(struct qemu_alarm_timer *t) +{ + timer_t host_timer = (timer_t)(long)t->priv; + + timer_delete(host_timer); +} + +static void dynticks_rearm_timer(struct qemu_alarm_timer *t) +{ + timer_t host_timer = (timer_t)(long)t->priv; + struct itimerspec timeout; + int64_t nearest_delta_us = INT64_MAX; + int64_t current_us; + + assert(alarm_has_dynticks(t)); + if (!active_timers[QEMU_CLOCK_REALTIME] && + !active_timers[QEMU_CLOCK_VIRTUAL] && + !active_timers[QEMU_CLOCK_HOST]) + return; + + nearest_delta_us = qemu_next_deadline_dyntick(); + + /* check whether a timer is already running */ + if (timer_gettime(host_timer, &timeout)) { + perror("gettime"); + fprintf(stderr, "Internal timer error: aborting\n"); + exit(1); + } + current_us = timeout.it_value.tv_sec * 1000000 + timeout.it_value.tv_nsec/1000; + if (current_us && current_us <= nearest_delta_us) + return; + + timeout.it_interval.tv_sec = 0; + timeout.it_interval.tv_nsec = 0; /* 0 for one-shot timer */ + timeout.it_value.tv_sec = nearest_delta_us / 1000000; + timeout.it_value.tv_nsec = (nearest_delta_us % 1000000) * 1000; + if (timer_settime(host_timer, 0 /* RELATIVE */, &timeout, NULL)) { + perror("settime"); + fprintf(stderr, "Internal timer error: aborting\n"); + exit(1); + } +} + +#endif /* defined(__linux__) */ + +static int unix_start_timer(struct qemu_alarm_timer *t) +{ + struct sigaction act; + struct itimerval itv; + int err; + + /* timer signal */ + sigfillset(&act.sa_mask); + act.sa_flags = 0; + act.sa_handler = host_alarm_handler; + + sigaction(SIGALRM, &act, NULL); + + 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; + + err = setitimer(ITIMER_REAL, &itv, NULL); + if (err) + return -1; + + return 0; +} + +static void unix_stop_timer(struct qemu_alarm_timer *t) +{ + struct itimerval itv; + + memset(&itv, 0, sizeof(itv)); + setitimer(ITIMER_REAL, &itv, NULL); +} + +#endif /* !defined(_WIN32) */ + + +#ifdef _WIN32 + +static int win32_start_timer(struct qemu_alarm_timer *t) +{ + TIMECAPS tc; + struct qemu_alarm_win32 *data = t->priv; + UINT flags; + + memset(&tc, 0, sizeof(tc)); + timeGetDevCaps(&tc, sizeof(tc)); + + data->period = tc.wPeriodMin; + timeBeginPeriod(data->period); + + flags = TIME_CALLBACK_FUNCTION; + if (alarm_has_dynticks(t)) + flags |= TIME_ONESHOT; + else + flags |= TIME_PERIODIC; + + data->timerId = timeSetEvent(1, // interval (ms) + data->period, // resolution + host_alarm_handler, // function + (DWORD)t, // parameter + flags); + + if (!data->timerId) { + fprintf(stderr, "Failed to initialize win32 alarm timer: %ld\n", + GetLastError()); + timeEndPeriod(data->period); + return -1; + } + + return 0; +} + +static void win32_stop_timer(struct qemu_alarm_timer *t) +{ + struct qemu_alarm_win32 *data = t->priv; + + timeKillEvent(data->timerId); + timeEndPeriod(data->period); +} + +static void win32_rearm_timer(struct qemu_alarm_timer *t) +{ + struct qemu_alarm_win32 *data = t->priv; + + assert(alarm_has_dynticks(t)); + if (!active_timers[QEMU_CLOCK_REALTIME] && + !active_timers[QEMU_CLOCK_VIRTUAL] && + !active_timers[QEMU_CLOCK_HOST]) + return; + + timeKillEvent(data->timerId); + + data->timerId = timeSetEvent(1, + data->period, + host_alarm_handler, + (DWORD)t, + TIME_ONESHOT | TIME_CALLBACK_FUNCTION); + + if (!data->timerId) { + fprintf(stderr, "Failed to re-arm win32 alarm timer %ld\n", + GetLastError()); + + timeEndPeriod(data->period); + exit(1); + } +} + +#endif /* _WIN32 */ + +static void alarm_timer_on_change_state_rearm(void *opaque, int running, int reason) +{ + if (running) + qemu_rearm_alarm_timer((struct qemu_alarm_timer *) opaque); +} + +int init_timer_alarm(void) +{ + struct qemu_alarm_timer *t = NULL; + int i, err = -1; + + for (i = 0; alarm_timers[i].name; i++) { + t = &alarm_timers[i]; + + err = t->start(t); + if (!err) + break; + } + + if (err) { + err = -ENOENT; + goto fail; + } + + /* first event is at time 0 */ + t->pending = 1; + alarm_timer = t; + qemu_add_vm_change_state_handler(alarm_timer_on_change_state_rearm, t); + + return 0; + +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) +{ +#ifndef CONFIG_IOTHREAD + int timeout; + + 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 +} + |