From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from eggs.gnu.org ([140.186.70.92]:54792) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Qva46-0002b0-5D for qemu-devel@nongnu.org; Mon, 22 Aug 2011 15:21:57 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1Qva41-0005EB-CH for qemu-devel@nongnu.org; Mon, 22 Aug 2011 15:21:39 -0400 Received: from e6.ny.us.ibm.com ([32.97.182.146]:46833) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Qva41-0005Dv-34 for qemu-devel@nongnu.org; Mon, 22 Aug 2011 15:21:37 -0400 Received: from d01relay04.pok.ibm.com (d01relay04.pok.ibm.com [9.56.227.236]) by e6.ny.us.ibm.com (8.14.4/8.13.1) with ESMTP id p7MIvC3e021850 for ; Mon, 22 Aug 2011 14:57:12 -0400 Received: from d03av02.boulder.ibm.com (d03av02.boulder.ibm.com [9.17.195.168]) by d01relay04.pok.ibm.com (8.13.8/8.13.8/NCO v10.0) with ESMTP id p7MJLKoO217964 for ; Mon, 22 Aug 2011 15:21:21 -0400 Received: from d03av02.boulder.ibm.com (loopback [127.0.0.1]) by d03av02.boulder.ibm.com (8.14.4/8.13.1/NCO v10.0 AVout) with ESMTP id p7MDKpfA031241 for ; Mon, 22 Aug 2011 07:20:53 -0600 From: Anthony Liguori Date: Mon, 22 Aug 2011 14:21:13 -0500 Message-Id: <1314040874-9259-1-git-send-email-aliguori@us.ibm.com> Subject: [Qemu-devel] [PATCH 1/2] main: add high resolution GSource based timer List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: qemu-devel@nongnu.org Cc: Paolo Bonzini , Anthony Liguori , Jan Kiszka This was originally written by Paolo Bonzini. Signed-off-by: Anthony Liguori --- Makefile.objs | 1 + configure | 4 +- ghrtimer.c | 334 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ghrtimer.h | 49 +++++++++ 4 files changed, 386 insertions(+), 2 deletions(-) create mode 100644 ghrtimer.c create mode 100644 ghrtimer.h diff --git a/Makefile.objs b/Makefile.objs index d1f3e5d..e4267ed 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -9,6 +9,7 @@ qobject-obj-y += qerror.o error.o oslib-obj-y = osdep.o oslib-obj-$(CONFIG_WIN32) += oslib-win32.o qemu-thread-win32.o oslib-obj-$(CONFIG_POSIX) += oslib-posix.o qemu-thread-posix.o +oslib-obj-y += ghrtimer.o ####################################################################### # coroutines diff --git a/configure b/configure index 8bbd694..6339c45 100755 --- a/configure +++ b/configure @@ -1843,8 +1843,8 @@ fi ########################################## # glib support probe if $pkg_config --modversion gthread-2.0 > /dev/null 2>&1 ; then - glib_cflags=`$pkg_config --cflags gthread-2.0 2>/dev/null` - glib_libs=`$pkg_config --libs gthread-2.0 2>/dev/null` + glib_cflags=`$pkg_config --cflags gthread-2.0 gobject-2.0 2>/dev/null` + glib_libs=`$pkg_config --libs gthread-2.0 gobject-2.0 2>/dev/null` LIBS="$glib_libs $LIBS" libs_qga="$glib_libs $libs_qga" else diff --git a/ghrtimer.c b/ghrtimer.c new file mode 100644 index 0000000..1e7e1c2 --- /dev/null +++ b/ghrtimer.c @@ -0,0 +1,334 @@ +/* + * timerfd GSource wrapper + * + * Copyright IBM, Corp. 2011 + * Copyright Red Hat, Inc. 2011 + * + * Authors: + * Anthony Liguori + * Paolo Bonzini + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#include "config-host.h" +#include +#include "ghrtimer.h" +#include +#include +#include + +#ifdef CONFIG_TIMERFD +#include +#endif + +struct _GHRTimer { + GSource source; + gint64 deadline; + GPollFD poll; + char pending; +}; + +#define MIN_TIMER_REARM_NS 250000 + +#if GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION <= 26 +static inline guint64 muldiv64(guint64 a, guint32 b, guint32 c) +{ + guint64 rl = (a & 0xffffffff) * (guint64)b; + guint64 rh = (a >> 32) * (guint64)b + (rl >> 32); + rl &= 0xffffffff; + return ((rh / c) << 32) | ((((rh % c) << 32) + rl) / c); +} + +gint64 +g_get_monotonic_time_ns (void) +{ +#if defined(__linux__) || (defined(__FreeBSD__) && __FreeBSD_version >= 500000) \ + || defined(__DragonFly__) || defined(__FreeBSD_kernel__) \ + || defined(__OpenBSD__) + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return ts.tv_sec * 1000000000LL + ts.tv_nsec; + +#elif defined _WIN32 + LARGE_INTEGER ti; + static LARGE_INTEGER freq; + if (freq.QuadPart == 0) { + QueryPerformanceFrequency(&freq); + } + QueryPerformanceCounter(&ti); + return muldiv64(ti.QuadPart, 1000000000, freq.QuadPart); + +#else +#ifdef CONFIG_TIMERFD +#error configuration problem, timerfd uses CLOCK_MONOTONIC +#endif + GTimeVal tv; + g_get_current_time (&tv); + return ((guint64) tv.tv_sec) * 1000000000 + tv.tv_usec * 1000; +#endif +} + +gint64 +g_source_get_time_ns (GSource *source) +{ + return g_get_monotonic_time_ns (); +} + +#else +gint64 +g_get_monotonic_time_ns (void) +{ + return g_get_monotonic_time () * 1000; +} + +gint64 +g_source_get_time_ns (GSource *source) +{ + return g_source_get_time (source) * 1000; +} +#endif + +gboolean +g_hrtimer_pending (GHRTimer *timer) +{ + return timer->pending; +} + +void +g_hrtimer_rearm (GHRTimer *timer, + gint64 us) +{ + return g_hrtimer_rearm_ns (timer, us * 1000); +} + +void +g_hrtimer_rearm_ns (GHRTimer *timer, + gint64 ns) +{ + gint64 time = g_get_monotonic_time_ns (); + timer->deadline = ns; + if (ns < time) { + timer->pending = TRUE; + return; + } + + timer->pending = FALSE; + if (ns == G_HRTIMER_QUIESCE) { + return; + } + + if (ns - time < MIN_TIMER_REARM_NS) { + timer->deadline = ns = time + MIN_TIMER_REARM_NS; + } +#ifdef CONFIG_TIMERFD + if (timer->poll.fd != -1) { + struct itimerspec new = { + .it_interval = { 0, 0 }, + .it_value = { ns / 1000000000, ns % 1000000000 } + }; + timerfd_settime(timer->poll.fd, TFD_TIMER_ABSTIME, &new, NULL); + } +#endif +} + +static gboolean +g_hrtimer_prepare (GSource *source, + gint *timeout) +{ + GHRTimer *timer = (GHRTimer *) source; + + if (timer->deadline == G_HRTIMER_QUIESCE) { + g_assert (!timer->pending); + *timeout = -1; + return FALSE; + } + + if (timer->poll.fd == -1) { + gint64 timeout_ns = timer->deadline - g_get_monotonic_time_ns (); + if (timeout_ns < 0) { + *timeout = 0; + timer->pending = TRUE; + } else { + *timeout = timeout_ns / 1000000; + } + } else { + *timeout = -1; +#ifndef CONFIG_TIMERFD + abort (); +#endif + } + return timer->pending; +} + +static gboolean +g_hrtimer_check (GSource *source) +{ + GHRTimer *timer = (GHRTimer *) source; + + if (timer->deadline == G_HRTIMER_QUIESCE) { + g_assert (!timer->pending); + return FALSE; + } + + if (timer->poll.fd == -1) { + timer->pending |= (timer->deadline <= g_source_get_time_ns (source)); + } else { + long long overrun; + timer->pending |= (timer->poll.revents & G_IO_IN) != 0; + if (timer->pending) { + if (read (timer->poll.fd, (char *) &overrun, sizeof (overrun))) { + /* do nothing */ + } + } +#ifndef CONFIG_TIMERFD + abort (); +#endif + } + + return timer->pending; +} + +static gboolean +g_hrtimer_dispatch (GSource *source, + GSourceFunc callback, + gpointer user_data) +{ + GHRTimer *timer = (GHRTimer *) source; + + if (!callback) { + g_warning ("Timer source dispatched without callback\n" + "You must call g_source_set_callback()."); + return TRUE; + } + + timer->pending = FALSE; + timer->deadline = G_HRTIMER_QUIESCE; + if (user_data == NULL) + user_data = timer; + callback (user_data); + return TRUE; +} + +static void +g_hrtimer_finalize (GSource *source) +{ + GHRTimer *timer = (GHRTimer *) source; + + if (timer->poll.fd != -1) { + close (timer->poll.fd); +#ifndef CONFIG_TIMERFD + abort (); +#endif + } +} + +static void +g_hrtimer_closure_callback (gpointer data) +{ + GClosure *closure = data; + g_closure_invoke (closure, NULL, 0, NULL, NULL); +} + +static GSourceFuncs hrtimer_source_funcs = { + g_hrtimer_prepare, + g_hrtimer_check, + g_hrtimer_dispatch, + g_hrtimer_finalize, + (GSourceFunc) g_hrtimer_closure_callback, + (gpointer) g_cclosure_marshal_VOID__VOID +}; + +GHRTimer * +g_hrtimer_new (void) +{ + GHRTimer *timer; + + timer = (GHRTimer *) g_source_new (&hrtimer_source_funcs, + sizeof (GHRTimer)); + +#ifdef CONFIG_TIMERFD + timer->poll.fd = timerfd_create (CLOCK_MONOTONIC, 0); + if (timer->poll.fd != -1) { + fcntl(timer->poll.fd, F_SETFD, fcntl (timer->poll.fd, F_GETFD) | FD_CLOEXEC); + fcntl(timer->poll.fd, F_SETFL, fcntl (timer->poll.fd, F_GETFL) | O_NONBLOCK); + timer->poll.events = G_IO_IN; + g_source_add_poll (&timer->source, &timer->poll); + } +#else + timer->poll.fd = -1; +#endif + timer->deadline = G_HRTIMER_QUIESCE; + return timer; +} +guint +g_hrtimer_add (GHRTimer **timer, + GSourceFunc func, + gpointer user_data) +{ + return g_hrtimer_add_full (G_PRIORITY_DEFAULT, timer, func, user_data, NULL); +} + +guint +g_hrtimer_add_full (gint priority, + GHRTimer **timer, + GSourceFunc func, + gpointer user_data, + GDestroyNotify notify) +{ + GHRTimer *hrtimer; + guint id; + + hrtimer = g_hrtimer_new (); + if (priority != G_PRIORITY_DEFAULT) + g_source_set_priority (&hrtimer->source, priority); + + g_source_set_callback (&hrtimer->source, (GSourceFunc) func, + user_data, notify); + id = g_source_attach (&hrtimer->source, NULL); + + *timer = hrtimer; + return id; +} + +#ifdef MAIN +#include + +static int i = 3; +static GMainLoop *loop; + +void +rearm_timer (GHRTimer *timer) +{ + printf ("."); + fflush (stdout); + g_hrtimer_rearm_ns (timer, g_get_monotonic_time_ns () + 1000000000); +} + +void +hrtimer_callback (gpointer user_data) +{ + GHRTimer *timer = user_data; + + if (--i == 0) { + printf ("\n"); + fflush (stdout); + g_main_loop_quit (loop); + } else { + rearm_timer (timer); + } +} + +int main() +{ + GHRTimer *timer; + loop = g_main_loop_new (NULL, FALSE); + g_hrtimer_add (&timer, (GSourceFunc) hrtimer_callback, NULL); + rearm_timer (timer); + g_main_loop_run (loop); + g_source_unref ((GSource *) timer); +} +#endif + diff --git a/ghrtimer.h b/ghrtimer.h new file mode 100644 index 0000000..2cf6961 --- /dev/null +++ b/ghrtimer.h @@ -0,0 +1,49 @@ +/* + * timerfd GSource wrapper + * + * Copyright IBM, Corp. 2011 + * Copyright Red Hat, Inc. 2011 + * + * Authors: + * Anthony Liguori + * Paolo Bonzini + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later + * See the COPYING.LIB file in the top-level directory. + * + */ + +#ifndef G_HRTIMER_H +#define G_HRTIMER_H 1 + +#include + +#define G_HRTIMER_QUIESCE ((gint64) 0x7FFFFFFFFFFFFFFF) + +typedef struct _GHRTimer GHRTimer; + +gint64 g_get_monotonic_time_ns (void); + +gint64 g_source_get_time_ns (GSource *source); + +GHRTimer *g_hrtimer_new (void); + +gboolean g_hrtimer_pending (GHRTimer *timer); + +void g_hrtimer_rearm (GHRTimer *timer, + gint64 usec); + +void g_hrtimer_rearm_ns (GHRTimer *timer, + gint64 nsec); + +guint g_hrtimer_add (GHRTimer **timer, + GSourceFunc callback, + gpointer user_data); + +guint g_hrtimer_add_full (int priority, + GHRTimer **timer, + GSourceFunc callback, + gpointer user_data, + GDestroyNotify notify); + +#endif -- 1.7.4.1