aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorths <ths@c046a42c-6fe2-441c-8c8c-71466251a162>2007-08-19 21:56:03 +0000
committerths <ths@c046a42c-6fe2-441c-8c8c-71466251a162>2007-08-19 21:56:03 +0000
commitc89940133c02810197c405814439b0d529e5d551 (patch)
tree1d3c9d3276dd9515c6397562fc35bcdee0a29216
parent7603d1568e121f74b03dabc8f86c7c9b88723075 (diff)
Rework alarm timer infrastrucure, by Luca Tettamanti.
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@3125 c046a42c-6fe2-441c-8c8c-71466251a162
-rw-r--r--vl.c289
-rw-r--r--vl.h1
2 files changed, 186 insertions, 104 deletions
diff --git a/vl.c b/vl.c
index 4ff2341326..ae05b8f6ad 100644
--- a/vl.c
+++ b/vl.c
@@ -781,19 +781,59 @@ struct QEMUTimer {
struct QEMUTimer *next;
};
-QEMUClock *rt_clock;
-QEMUClock *vm_clock;
+struct qemu_alarm_timer {
+ char const *name;
+
+ int (*start)(struct qemu_alarm_timer *t);
+ void (*stop)(struct qemu_alarm_timer *t);
+ void *priv;
+};
+
+static struct qemu_alarm_timer *alarm_timer;
-static QEMUTimer *active_timers[2];
#ifdef _WIN32
-static MMRESULT timerID;
-static HANDLE host_alarm = NULL;
-static unsigned int period = 1;
+
+struct qemu_alarm_win32 {
+ MMRESULT timerId;
+ HANDLE host_alarm;
+ unsigned int period;
+} alarm_win32_data = {0, NULL, -1};
+
+static int win32_start_timer(struct qemu_alarm_timer *t);
+static void win32_stop_timer(struct qemu_alarm_timer *t);
+
#else
-/* frequency of the times() clock tick */
-static int timer_freq;
+
+static int unix_start_timer(struct qemu_alarm_timer *t);
+static void unix_stop_timer(struct qemu_alarm_timer *t);
+
+#ifdef __linux__
+
+static int rtc_start_timer(struct qemu_alarm_timer *t);
+static void rtc_stop_timer(struct qemu_alarm_timer *t);
+
#endif
+#endif /* _WIN32 */
+
+static struct qemu_alarm_timer alarm_timers[] = {
+#ifdef __linux__
+ /* RTC - if available - is preferred */
+ {"rtc", rtc_start_timer, rtc_stop_timer, NULL},
+#endif
+#ifndef _WIN32
+ {"unix", unix_start_timer, unix_stop_timer, NULL},
+#else
+ {"win32", win32_start_timer, win32_stop_timer, &alarm_win32_data},
+#endif
+ {NULL, }
+};
+
+QEMUClock *rt_clock;
+QEMUClock *vm_clock;
+
+static QEMUTimer *active_timers[2];
+
QEMUClock *qemu_new_clock(int type)
{
QEMUClock *clock;
@@ -1009,7 +1049,8 @@ static void host_alarm_handler(int host_signum)
qemu_timer_expired(active_timers[QEMU_TIMER_REALTIME],
qemu_get_clock(rt_clock))) {
#ifdef _WIN32
- SetEvent(host_alarm);
+ struct qemu_alarm_win32 *data = ((struct qemu_alarm_timer*)dwUser)->priv;
+ SetEvent(data->host_alarm);
#endif
CPUState *env = cpu_single_env;
if (env) {
@@ -1030,10 +1071,27 @@ static void host_alarm_handler(int host_signum)
#define RTC_FREQ 1024
-static int rtc_fd;
+static void enable_sigio_timer(int fd)
+{
+ struct sigaction act;
-static int start_rtc_timer(void)
+ /* timer signal */
+ 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(SIGIO, &act, NULL);
+ fcntl(fd, F_SETFL, O_ASYNC);
+ fcntl(fd, F_SETOWN, getpid());
+}
+
+static int rtc_start_timer(struct qemu_alarm_timer *t)
{
+ int rtc_fd;
+
TFR(rtc_fd = open("/dev/rtc", O_RDONLY));
if (rtc_fd < 0)
return -1;
@@ -1048,117 +1106,142 @@ static int start_rtc_timer(void)
close(rtc_fd);
return -1;
}
- pit_min_timer_count = PIT_FREQ / RTC_FREQ;
+
+ enable_sigio_timer(rtc_fd);
+
+ t->priv = (void *)rtc_fd;
+
return 0;
}
-#else
-
-static int start_rtc_timer(void)
+static void rtc_stop_timer(struct qemu_alarm_timer *t)
{
- return -1;
+ int rtc_fd = (int)t->priv;
+
+ close(rtc_fd);
}
#endif /* !defined(__linux__) */
-#endif /* !defined(_WIN32) */
+static int unix_start_timer(struct qemu_alarm_timer *t)
+{
+ struct sigaction act;
+ struct itimerval itv;
+ int err;
-static void init_timer_alarm(void)
+ /* timer signal */
+ 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);
+
+ 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
- {
- int count=0;
- TIMECAPS tc;
-
- ZeroMemory(&tc, sizeof(TIMECAPS));
- timeGetDevCaps(&tc, sizeof(TIMECAPS));
- if (period < tc.wPeriodMin)
- period = tc.wPeriodMin;
- timeBeginPeriod(period);
- timerID = timeSetEvent(1, // interval (ms)
- period, // resolution
- host_alarm_handler, // function
- (DWORD)&count, // user parameter
- TIME_PERIODIC | TIME_CALLBACK_FUNCTION);
- if( !timerID ) {
- perror("failed timer alarm");
- exit(1);
- }
- host_alarm = CreateEvent(NULL, FALSE, FALSE, NULL);
- if (!host_alarm) {
- perror("failed CreateEvent");
- exit(1);
- }
- qemu_add_wait_object(host_alarm, NULL, NULL);
+
+static int win32_start_timer(struct qemu_alarm_timer *t)
+{
+ TIMECAPS tc;
+ struct qemu_alarm_win32 *data = t->priv;
+
+ data->host_alarm = CreateEvent(NULL, FALSE, FALSE, NULL);
+ if (!data->host_alarm) {
+ perror("Failed CreateEvent");
+ return -1
}
- pit_min_timer_count = ((uint64_t)10000 * PIT_FREQ) / 1000000;
-#else
- {
- struct sigaction act;
- struct itimerval itv;
-
- /* get times() syscall frequency */
- timer_freq = sysconf(_SC_CLK_TCK);
-
- /* timer signal */
- 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);
- itv.it_interval.tv_sec = 0;
- itv.it_interval.tv_usec = 999; /* for i386 kernel 2.6 to get 1 ms */
- itv.it_value.tv_sec = 0;
- itv.it_value.tv_usec = 10 * 1000;
- setitimer(ITIMER_REAL, &itv, NULL);
- /* we probe the tick duration of the kernel to inform the user if
- the emulated kernel requested a too high timer frequency */
- getitimer(ITIMER_REAL, &itv);
+ memset(&tc, 0, sizeof(tc));
+ timeGetDevCaps(&tc, sizeof(tc));
-#if defined(__linux__)
- /* XXX: force /dev/rtc usage because even 2.6 kernels may not
- have timers with 1 ms resolution. The correct solution will
- be to use the POSIX real time timers available in recent
- 2.6 kernels */
- if (itv.it_interval.tv_usec > 1000 || 1) {
- /* try to use /dev/rtc to have a faster timer */
- if (start_rtc_timer() < 0)
- goto use_itimer;
- /* disable itimer */
- itv.it_interval.tv_sec = 0;
- itv.it_interval.tv_usec = 0;
- itv.it_value.tv_sec = 0;
- itv.it_value.tv_usec = 0;
- setitimer(ITIMER_REAL, &itv, NULL);
-
- /* use the RTC */
- sigaction(SIGIO, &act, NULL);
- fcntl(rtc_fd, F_SETFL, O_ASYNC);
- fcntl(rtc_fd, F_SETOWN, getpid());
- } else
-#endif /* defined(__linux__) */
- {
- use_itimer:
- pit_min_timer_count = ((uint64_t)itv.it_interval.tv_usec *
- PIT_FREQ) / 1000000;
- }
+ if (data->period < tc.wPeriodMin)
+ data->period = tc.wPeriodMin;
+
+ timeBeginPeriod(data->period);
+
+ data->timerId = timeSetEvent(1, // interval (ms)
+ data->period, // resolution
+ host_alarm_handler, // function
+ (DWORD)t, // parameter
+ TIME_PERIODIC | TIME_CALLBACK_FUNCTION);
+
+ if (!data->timerId) {
+ perror("Failed to initialize win32 alarm timer");
+
+ timeEndPeriod(data->period);
+ CloseHandle(data->host_alarm);
+ return -1;
}
-#endif
+
+ qemu_add_wait_object(data->host_alarm, NULL, NULL);
+
+ return 0;
}
-void quit_timers(void)
+static void win32_stop_timer(struct qemu_alarm_timer *t)
{
-#ifdef _WIN32
- timeKillEvent(timerID);
- timeEndPeriod(period);
- if (host_alarm) {
- CloseHandle(host_alarm);
- host_alarm = NULL;
+ struct qemu_alarm_win32 *data = t->priv;
+
+ timeKillEvent(data->timerId);
+ timeEndPeriod(data->period);
+
+ CloseHandle(data->host_alarm);
+}
+
+#endif /* _WIN32 */
+
+static void init_timer_alarm(void)
+{
+ struct qemu_alarm_timer *t;
+ int i, err = -1;
+
+ for (i = 0; alarm_timers[i].name; i++) {
+ t = &alarm_timers[i];
+
+ printf("trying %s...\n", t->name);
+
+ err = t->start(t);
+ if (!err)
+ break;
}
-#endif
+
+ if (err) {
+ fprintf(stderr, "Unable to find any suitable alarm timer.\n");
+ fprintf(stderr, "Terminating\n");
+ exit(1);
+ }
+
+ alarm_timer = t;
+}
+
+void quit_timers(void)
+{
+ alarm_timer->stop(alarm_timer);
+ alarm_timer = NULL;
}
/***********************************************************/
diff --git a/vl.h b/vl.h
index b05ea119a1..e12b6f3e73 100644
--- a/vl.h
+++ b/vl.h
@@ -447,7 +447,6 @@ void qemu_mod_timer(QEMUTimer *ts, int64_t expire_time);
int qemu_timer_pending(QEMUTimer *ts);
extern int64_t ticks_per_sec;
-extern int pit_min_timer_count;
int64_t cpu_get_ticks(void);
void cpu_enable_ticks(void);