mirror of
https://github.com/qemu/qemu.git
synced 2024-11-24 19:33:39 +08:00
Add support for dynamic ticks, by Luca Tettamanti and Dan Kenigsberg.
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@3130 c046a42c-6fe2-441c-8c8c-71466251a162
This commit is contained in:
parent
f3dcfadac7
commit
efe75411ec
185
vl.c
185
vl.c
@ -784,12 +784,32 @@ 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
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/* TODO: MIN_TIMER_REARM_US should be optimized */
|
||||
#define MIN_TIMER_REARM_US 250
|
||||
|
||||
static struct qemu_alarm_timer *alarm_timer;
|
||||
|
||||
#ifdef _WIN32
|
||||
@ -802,12 +822,17 @@ struct qemu_alarm_win32 {
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
#ifdef __linux__
|
||||
|
||||
static int hpet_start_timer(struct qemu_alarm_timer *t);
|
||||
@ -816,21 +841,26 @@ 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
|
||||
#endif /* __linux__ */
|
||||
|
||||
#endif /* _WIN32 */
|
||||
|
||||
static struct qemu_alarm_timer alarm_timers[] = {
|
||||
#ifndef _WIN32
|
||||
{"dynticks", ALARM_FLAG_DYNTICKS, dynticks_start_timer,
|
||||
dynticks_stop_timer, dynticks_rearm_timer, NULL},
|
||||
#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},
|
||||
{"dynticks", ALARM_FLAG_DYNTICKS, win32_start_timer,
|
||||
win32_stop_timer, win32_rearm_timer, &alarm_win32_data},
|
||||
{"win32", 0, win32_start_timer,
|
||||
win32_stop_timer, NULL, &alarm_win32_data},
|
||||
#endif
|
||||
{NULL, }
|
||||
};
|
||||
@ -949,6 +979,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
|
||||
@ -1008,6 +1040,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)
|
||||
@ -1115,7 +1148,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))) {
|
||||
@ -1136,6 +1170,30 @@ static void host_alarm_handler(int host_signum)
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t qemu_next_deadline(void)
|
||||
{
|
||||
int64_t nearest_delta_us = ULLONG_MAX;
|
||||
int64_t vmdelta_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 */
|
||||
if (nearest_delta_us <= MIN_TIMER_REARM_US)
|
||||
nearest_delta_us = MIN_TIMER_REARM_US;
|
||||
|
||||
return nearest_delta_us;
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
|
||||
#if defined(__linux__)
|
||||
@ -1243,6 +1301,80 @@ static void rtc_stop_timer(struct qemu_alarm_timer *t)
|
||||
|
||||
#endif /* !defined(__linux__) */
|
||||
|
||||
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;
|
||||
int64_t current_us;
|
||||
|
||||
if (!active_timers[QEMU_TIMER_REALTIME] &&
|
||||
!active_timers[QEMU_TIMER_VIRTUAL])
|
||||
return;
|
||||
|
||||
nearest_delta_us = qemu_next_deadline();
|
||||
|
||||
/* 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);
|
||||
}
|
||||
}
|
||||
|
||||
static int unix_start_timer(struct qemu_alarm_timer *t)
|
||||
{
|
||||
struct sigaction act;
|
||||
@ -1288,6 +1420,7 @@ static int win32_start_timer(struct qemu_alarm_timer *t)
|
||||
{
|
||||
TIMECAPS tc;
|
||||
struct qemu_alarm_win32 *data = t->priv;
|
||||
UINT flags;
|
||||
|
||||
data->host_alarm = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
if (!data->host_alarm) {
|
||||
@ -1303,11 +1436,17 @@ static int win32_start_timer(struct qemu_alarm_timer *t)
|
||||
|
||||
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
|
||||
TIME_PERIODIC | TIME_CALLBACK_FUNCTION);
|
||||
flags);
|
||||
|
||||
if (!data->timerId) {
|
||||
perror("Failed to initialize win32 alarm timer");
|
||||
@ -1332,6 +1471,35 @@ static void win32_stop_timer(struct qemu_alarm_timer *t)
|
||||
CloseHandle(data->host_alarm);
|
||||
}
|
||||
|
||||
static void win32_rearm_timer(struct qemu_alarm_timer *t)
|
||||
{
|
||||
struct qemu_alarm_win32 *data = t->priv;
|
||||
uint64_t nearest_delta_us;
|
||||
|
||||
if (!active_timers[QEMU_TIMER_REALTIME] &&
|
||||
!active_timers[QEMU_TIMER_VIRTUAL])
|
||||
return;
|
||||
|
||||
nearest_delta_us = qemu_next_deadline();
|
||||
nearest_delta_us /= 1000;
|
||||
|
||||
timeKillEvent(data->timerId);
|
||||
|
||||
data->timerId = timeSetEvent(1,
|
||||
data->period,
|
||||
host_alarm_handler,
|
||||
(DWORD)t,
|
||||
TIME_ONESHOT | TIME_PERIODIC);
|
||||
|
||||
if (!data->timerId) {
|
||||
perror("Failed to re-arm win32 alarm timer");
|
||||
|
||||
timeEndPeriod(data->period);
|
||||
CloseHandle(data->host_alarm);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* _WIN32 */
|
||||
|
||||
static void init_timer_alarm(void)
|
||||
@ -6490,6 +6658,7 @@ void vm_start(void)
|
||||
cpu_enable_ticks();
|
||||
vm_running = 1;
|
||||
vm_state_notify(1);
|
||||
qemu_rearm_alarm_timer(alarm_timer);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user