If DYNAMIC_TICKS is defined qemu does not attepmt to generate SIGALRM at a constant rate. Rather, the system timer is set to generate SIGALRM only when it is needed. DYNAMIC_TICKS reduces the number of SIGALRMs sent to idle dynamic-ticked guests. Original patch from Dan Kenigsberg <[EMAIL PROTECTED]>
Signed-off-by: Luca Tettamanti <[EMAIL PROTECTED]> --- qemu/configure | 5 ++ qemu/vl.c | 149 ++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 148 insertions(+), 6 deletions(-) diff --git a/qemu/configure b/qemu/configure index 365b7fb..38373db 100755 --- a/qemu/configure +++ b/qemu/configure @@ -262,6 +262,8 @@ for opt do ;; --enable-uname-release=*) uname_release="$optarg" ;; + --disable-dynamic-ticks) dynamic_ticks="no" + ;; esac done @@ -788,6 +790,9 @@ echo "TARGET_DIRS=$target_list" >> $config_mak if [ "$build_docs" = "yes" ] ; then echo "BUILD_DOCS=yes" >> $config_mak fi +if test "$dynamic_ticks" != "no" ; then + echo "#define DYNAMIC_TICKS 1" >> $config_h +fi # XXX: suppress that if [ "$bsd" = "yes" ] ; then diff --git a/qemu/vl.c b/qemu/vl.c index 0373beb..096729d 100644 --- a/qemu/vl.c +++ b/qemu/vl.c @@ -748,12 +748,42 @@ struct QEMUTimer { struct qemu_alarm_timer { char const *name; + unsigned int flags; int (*start)(struct qemu_alarm_timer *t); void (*stop)(struct qemu_alarm_timer *t); + void (*rearm)(struct qemu_alarm_timer *t); void *priv; }; +#define ALARM_FLAG_DYNTICKS 0x1 + +#ifdef DYNAMIC_TICKS + +static inline int alarm_has_dynticks(struct qemu_alarm_timer *t) +{ + return t->flags & ALARM_FLAG_DYNTICKS; +} + +static void qemu_rearm_alarm_timer(struct qemu_alarm_timer *t) { + if (!alarm_has_dynticks(t)) + return; + + t->rearm(t); +} + +#else /* DYNAMIC_TICKS */ + +static inline int alarm_has_dynticks(struct qemu_alarm_timer *t) +{ + return 0; +} + +static void qemu_rearm_alarm_timer(void) { +} + +#endif /* DYNAMIC_TICKS */ + static struct qemu_alarm_timer *alarm_timer; #ifdef _WIN32 @@ -772,6 +802,14 @@ static void win32_stop_timer(struct qemu_alarm_timer *t); static int unix_start_timer(struct qemu_alarm_timer *t); static void unix_stop_timer(struct qemu_alarm_timer *t); +#ifdef DYNAMIC_TICKS + +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); + +#endif + #ifdef __linux__ static int hpet_start_timer(struct qemu_alarm_timer *t); @@ -785,16 +823,19 @@ static void rtc_stop_timer(struct qemu_alarm_timer *t); #endif /* _WIN32 */ static struct qemu_alarm_timer alarm_timers[] = { +#ifndef _WIN32 +#ifdef DYNAMIC_TICKS + {"dynticks", ALARM_FLAG_DYNTICKS, dynticks_start_timer, dynticks_stop_timer, dynticks_rearm_timer, NULL}, +#endif #ifdef __linux__ /* HPET - if available - is preferred */ - {"hpet", hpet_start_timer, hpet_stop_timer, NULL}, + {"hpet", 0, hpet_start_timer, hpet_stop_timer, NULL, NULL}, /* ...otherwise try RTC */ - {"rtc", rtc_start_timer, rtc_stop_timer, NULL}, + {"rtc", 0, rtc_start_timer, rtc_stop_timer, NULL, NULL}, #endif -#ifndef _WIN32 - {"unix", unix_start_timer, unix_stop_timer, NULL}, + {"unix", 0, unix_start_timer, unix_stop_timer, NULL, NULL}, #else - {"win32", win32_start_timer, win32_stop_timer, &alarm_win32_data}, + {"win32", 0, win32_start_timer, win32_stop_timer, NULL, &alarm_win32_data}, #endif {NULL, } }; @@ -913,6 +954,8 @@ void qemu_del_timer(QEMUTimer *ts) } pt = &t->next; } + + qemu_rearm_alarm_timer(alarm_timer); } /* modify the current timer so that it will be fired when current_time @@ -972,6 +1015,7 @@ static void qemu_run_timers(QEMUTimer **ptimer_head, int64_t current_time) /* run the callback (the timer list can be modified) */ ts->cb(ts->opaque); } + qemu_rearm_alarm_timer(alarm_timer); } int64_t qemu_get_clock(QEMUClock *clock) @@ -1079,7 +1123,8 @@ static void host_alarm_handler(int host_signum) last_clock = ti; } #endif - if (qemu_timer_expired(active_timers[QEMU_TIMER_VIRTUAL], + if (alarm_has_dynticks(alarm_timer) || + qemu_timer_expired(active_timers[QEMU_TIMER_VIRTUAL], qemu_get_clock(vm_clock)) || qemu_timer_expired(active_timers[QEMU_TIMER_REALTIME], qemu_get_clock(rt_clock))) { @@ -1207,6 +1252,97 @@ static void rtc_stop_timer(struct qemu_alarm_timer *t) #endif /* !defined(__linux__) */ +#ifdef DYNAMIC_TICKS +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; +#if defined(TARGET_I386) && defined(USE_CODE_COPY) + act.sa_flags |= SA_ONSTACK; +#endif + act.sa_handler = host_alarm_handler; + + sigaction(SIGALRM, &act, NULL); + + 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 *)host_timer; + + return 0; +} + +static void dynticks_stop_timer(struct qemu_alarm_timer *t) +{ + timer_t host_timer = (timer_t)t->priv; + + timer_delete(host_timer); +} + +static void dynticks_rearm_timer(struct qemu_alarm_timer *t) +{ + timer_t host_timer = (timer_t)t->priv; + struct itimerspec timeout; + int64_t nearest_delta_us = INT64_MAX; + + if (active_timers[QEMU_TIMER_REALTIME] || + active_timers[QEMU_TIMER_VIRTUAL]) { + int64_t vmdelta_us, current_us; + + if (active_timers[QEMU_TIMER_REALTIME]) + nearest_delta_us = (active_timers[QEMU_TIMER_REALTIME]->expire_time - qemu_get_clock(rt_clock))*1000; + + if (active_timers[QEMU_TIMER_VIRTUAL]) { + /* round up */ + vmdelta_us = (active_timers[QEMU_TIMER_VIRTUAL]->expire_time - qemu_get_clock(vm_clock)+999)/1000; + if (vmdelta_us < nearest_delta_us) + nearest_delta_us = vmdelta_us; + } + + /* Avoid arming the timer to negative, zero, or too low values */ + /* TODO: MIN_TIMER_REARM_US should be optimized */ + #define MIN_TIMER_REARM_US 250 + if (nearest_delta_us <= MIN_TIMER_REARM_US) + nearest_delta_us = MIN_TIMER_REARM_US; + + /* 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 /* DYNAMIC_TICKS */ + static int unix_start_timer(struct qemu_alarm_timer *t) { struct sigaction act; @@ -6259,6 +6395,7 @@ void vm_start(void) cpu_enable_ticks(); vm_running = 1; vm_state_notify(1); + qemu_rearm_alarm_timer(alarm_timer); } } -- 1.5.2.4