]> git.proxmox.com Git - mirror_qemu.git/commitdiff
Rework alarm timer infrastrucure, by Luca Tettamanti.
authorths <ths@c046a42c-6fe2-441c-8c8c-71466251a162>
Sun, 19 Aug 2007 21:56:03 +0000 (21:56 +0000)
committerths <ths@c046a42c-6fe2-441c-8c8c-71466251a162>
Sun, 19 Aug 2007 21:56:03 +0000 (21:56 +0000)
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@3125 c046a42c-6fe2-441c-8c8c-71466251a162

vl.c
vl.h

diff --git a/vl.c b/vl.c
index 4ff23413261c12a1fd1007e3a0ed626a273c599a..ae05b8f6ad39612a842140820d6a1f1a45fede7b 100644 (file)
--- 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 b05ea119a139d3db69742a7f6d648c072070dd82..e12b6f3e73902dd3885fc5cbbf7b0e754b49dbeb 100644 (file)
--- 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);