qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH] Dynamic ticks


From: Dan Kenigsberg
Subject: [Qemu-devel] [PATCH] Dynamic ticks
Date: Sun, 12 Aug 2007 20:06:59 +0300
User-agent: Mutt/1.5.14 (2007-02-12)

"Dynamic ticks" in qemu: have a SIGALRM generated only when it is
needed, instead of every 1 millisecond. This patch requires that the
host supports high resolution timers, since it arms a POSIX timer to the
nearest Qemu timer's expiry time (which might be rather near).

Note that I raise a flag called dont_rearm_host_timer when I want to
inhibit redundant arming of the POSIX timer (see comment in sourcecode).

Index: vl.c
===================================================================
RCS file: /sources/qemu/qemu/vl.c,v
retrieving revision 1.323
diff -u -r1.323 vl.c
--- vl.c        29 Jul 2007 17:57:25 -0000      1.323
+++ vl.c        12 Aug 2007 16:24:32 -0000
@@ -793,6 +793,16 @@
 /* frequency of the times() clock tick */
 static int timer_freq;
 #endif
+#ifdef DYNAMIC_TICKS
+/* If DYNAMIC_TICKS is defined (and use_dynamic_ticks selected) 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. */
+static timer_t host_timer;
+static void rearm_host_timer(void);
+static int dont_rearm_host_timer = 0;
+static int use_dynamic_ticks = 1;
+#endif
 
 QEMUClock *qemu_new_clock(int type)
 {
@@ -838,6 +848,10 @@
         }
         pt = &t->next;
     }
+#ifdef DYNAMIC_TICKS
+    if (use_dynamic_ticks)
+        rearm_host_timer();
+#endif
 }
 
 /* modify the current timer so that it will be fired when current_time
@@ -846,6 +860,10 @@
 {
     QEMUTimer **pt, *t;
 
+#ifdef DYNAMIC_TICKS
+    if (use_dynamic_ticks)
+        dont_rearm_host_timer++;
+#endif
     qemu_del_timer(ts);
 
     /* add the timer in the sorted list */
@@ -863,6 +881,13 @@
     ts->expire_time = expire_time;
     ts->next = *pt;
     *pt = ts;
+
+#ifdef DYNAMIC_TICKS
+    if (use_dynamic_ticks) {
+        dont_rearm_host_timer--;
+        rearm_host_timer();
+    }
+#endif
 }
 
 int qemu_timer_pending(QEMUTimer *ts)
@@ -886,6 +911,14 @@
 {
     QEMUTimer *ts;
     
+#ifdef DYNAMIC_TICKS
+    /* callback functions that are run here may each try to rearm the timer. In
+     * oreder to avoid this, we raise the dont_rearm_host_timer flag.
+     * Alternatively, it is possible to go over all callback functions and make
+     * sure they do not call functions that rearm the system timer. */
+    if (use_dynamic_ticks)
+        dont_rearm_host_timer++;
+#endif
     for(;;) {
         ts = *ptimer_head;
         if (!ts || ts->expire_time > current_time)
@@ -897,6 +930,12 @@
         /* run the callback (the timer list can be modified) */
         ts->cb(ts->opaque);
     }
+#ifdef DYNAMIC_TICKS
+    if (use_dynamic_ticks) {
+        dont_rearm_host_timer--;
+        rearm_host_timer();
+    }
+#endif
 }
 
 int64_t qemu_get_clock(QEMUClock *clock)
@@ -1004,7 +1043,12 @@
         last_clock = ti;
     }
 #endif
-    if (qemu_timer_expired(active_timers[QEMU_TIMER_VIRTUAL],
+
+    if (
+#ifdef DYNAMIC_TICKS
+        use_dynamic_ticks ||
+#endif
+        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))) {
@@ -1102,28 +1146,44 @@
         
         /* timer signal */
         sigfillset(&act.sa_mask);
-       act.sa_flags = 0;
+        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);
+#ifdef DYNAMIC_TICKS
+        if (use_dynamic_ticks) {
+            struct sigevent 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");
+        } else
+#endif /* DYNAMIC_TICKS */
+        {
+            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);
+        }
 
 #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) {
+        if (
+#ifdef DYNAMIC_TICKS
+            !use_dynamic_ticks &&
+#endif
+            (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;
@@ -6287,6 +6347,10 @@
         cpu_enable_ticks();
         vm_running = 1;
         vm_state_notify(1);
+#ifdef DYNAMIC_TICKS
+        if (use_dynamic_ticks)
+            rearm_host_timer();
+#endif
     }
 }
 
@@ -6708,6 +6772,9 @@
 #ifdef TARGET_SPARC
            "-prom-env variable=value  set OpenBIOS nvram variables\n"
 #endif
+#if defined(DYNAMIC_TICKS)
+           "-no-dyntick     don't use dynamic ticks\n"
+#endif
            "\n"
            "During emulation, the following keys are useful:\n"
            "ctrl-alt-f      toggle full screen\n"
@@ -6805,6 +6872,9 @@
     QEMU_OPTION_name,
     QEMU_OPTION_prom_env,
     QEMU_OPTION_old_param,
+#ifdef DYNAMIC_TICKS
+    QEMU_OPTION_no_dyntick,
+#endif
 };
 
 typedef struct QEMUOption {
@@ -6909,6 +6979,9 @@
 #if defined(TARGET_ARM)
     { "old-param", 0, QEMU_OPTION_old_param },
 #endif
+#if defined(DYNAMIC_TICKS)
+    { "no-dyntick", 0, QEMU_OPTION_no_dyntick },
+#endif
     { NULL },
 };
 
@@ -7137,6 +7210,55 @@
 
 #define MAX_NET_CLIENTS 32
 
+
+#ifdef DYNAMIC_TICKS
+/* call host_alarm_handler just when the nearest QEMUTimer expires */
+/* expire_time is measured in nanosec for vm_clock
+ *                     but in millisec for rt_clock */
+static void rearm_host_timer(void)
+{
+    struct itimerspec timeout;
+    int64_t nearest_delta_us = INT64_MAX;
+
+    if (dont_rearm_host_timer) return;
+
+    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]) {
+            vmdelta_us = (active_timers[QEMU_TIMER_VIRTUAL]->expire_time - 
qemu_get_clock(vm_clock)+999)/1000; /* round up */
+            if (vmdelta_us < nearest_delta_us) {
+                nearest_delta_us = vmdelta_us;
+            }
+        }
+        /* Avoid arming the timer to negative, zero, or too low values */
+        /* 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");
+        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");
+    }
+}
+#endif /* DYNAMIC_TICKS */
+
 int main(int argc, char **argv)
 {
 #ifdef CONFIG_GDBSTUB
@@ -7687,6 +7809,12 @@
 #ifdef TARGET_ARM
             case QEMU_OPTION_old_param:
                 old_param = 1;
+                break;
+#endif
+#if defined(DYNAMIC_TICKS)
+            case QEMU_OPTION_no_dyntick:
+                use_dynamic_ticks = 0;
+                break;
 #endif
             }
         }
Index: configure
===================================================================
RCS file: /sources/qemu/qemu/configure,v
retrieving revision 1.152
diff -u -r1.152 configure
--- configure   1 Aug 2007 00:09:31 -0000       1.152
+++ configure   12 Aug 2007 16:24:33 -0000
@@ -294,6 +294,8 @@
         *)     echo "undefined SPARC architecture. Exiting";exit 1;;
       esac
   ;;
+  --disable-dynamic-ticks) dynamic_ticks="no"
+  ;;
   esac
 done
 
@@ -859,6 +861,10 @@
 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
+  echo "DYNAMIC_TICKS=yes" >> $config_mak
+fi
 
 # XXX: suppress that
 if [ "$bsd" = "yes" ] ; then




reply via email to

[Prev in Thread] Current Thread [Next in Thread]