[libvirt] [RFC v2 2/2] vireventpoll implimentation using epoll

Derbyshev Dmitriy dderbyshev at virtuozzo.com
Tue Feb 14 18:04:21 UTC 2017


From: Derbyshev Dmitry <dderbyshev at virtuozzo.com>

This makes it possible to avoid allocations in
virEventPollMakePollFDs, as well as generally reduces time spent in
kernel if handles remain the same, which should generally be true.
---
 configure.ac                                     |  28 ++++
 src/Makefile.am                                  |  10 +-
 src/util/vireventepoll.c                         | 201 +++++++++++++++++++++++
 tests/commanddata/{test14.log => test3epoll.log} |   2 +
 tests/commandtest.c                              |   4 +
 5 files changed, 243 insertions(+), 2 deletions(-)
 create mode 100644 src/util/vireventepoll.c
 copy tests/commanddata/{test14.log => test3epoll.log} (94%)

diff --git a/configure.ac b/configure.ac
index a995a05..9a6a810 100644
--- a/configure.ac
+++ b/configure.ac
@@ -877,6 +877,34 @@ AC_DEFINE_UNQUOTED([isbase64],[libvirt_gl_isbase64],[Hack to avoid symbol clash]
 AC_DEFINE_UNQUOTED([base64_encode],[libvirt_gl_base64_encode],[Hack to avoid symbol clash])
 AC_DEFINE_UNQUOTED([base64_encode_alloc],[libvirt_gl_base64_encode_alloc],[Hack to avoid symbol clash])
 
+AC_ARG_ENABLE([epoll],
+              [AS_HELP_STRING([--enable-epoll],[use epoll(4) on Linux])],
+              [enable_epoll=$enableval], [enable_epoll=auto])
+if test x$enable_epoll = xno; then
+    have_linux_epoll=no
+else
+    AC_MSG_CHECKING([for Linux epoll(4)])
+    AC_LINK_IFELSE([AC_LANG_PROGRAM(
+        [
+        #ifndef __linux__
+        #error This is not Linux
+        #endif
+        #include <sys/epoll.h>
+        ],
+        [epoll_create1 (EPOLL_CLOEXEC);])],
+        [have_linux_epoll=yes],
+        [have_linux_epoll=no])
+    AC_MSG_RESULT([$have_linux_epoll])
+fi
+if test x$enable_epoll,$have_linux_epoll = xyes,no; then
+    AC_MSG_ERROR([epoll support explicitly enabled but not available])
+fi
+if test "x$have_linux_epoll" = xyes; then
+  AC_DEFINE_UNQUOTED([HAVE_LINUX_EPOLL], 1, [whether epoll is supported])
+fi
+
+AM_CONDITIONAL([HAVE_LINUX_EPOLL], [test "x$have_linux_epoll" = xyes])
+
 AC_CONFIG_FILES([run],
                 [chmod +x,-w run])
 AC_CONFIG_FILES([\
diff --git a/src/Makefile.am b/src/Makefile.am
index f2643ea..9ad39cb 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -89,6 +89,12 @@ augeas_DATA =
 augeastestdir = $(datadir)/augeas/lenses/tests
 augeastest_DATA =
 
+if HAVE_LINUX_EPOLL
+poll_src=util/vireventepoll.c
+else !HAVE_LINUX_EPOLL
+poll_src=util/vireventpoll.c
+endif !HAVE_LINUX_EPOLL
+
 # These files are not related to driver APIs. Simply generic
 # helper APIs for various purposes
 UTIL_SOURCES =							\
@@ -113,7 +119,7 @@ UTIL_SOURCES =							\
 		util/virerror.c util/virerror.h			\
 		util/virevent.c util/virevent.h			\
 		util/vireventpollcommon.c util/vireventpoll.h		\
-		util/vireventpoll.c util/vireventpollinternal.h		\
+		$(poll_src) util/vireventpollinternal.h		\
 		util/virfile.c util/virfile.h			\
 		util/virfirewall.c util/virfirewall.h		\
 		util/virfirewallpriv.h				\
@@ -2376,7 +2382,7 @@ libvirt_setuid_rpc_client_la_SOURCES = 		\
 		util/virdbus.c			\
 		util/virerror.c			\
 		util/virevent.c			\
-		util/vireventpoll.c		\
+		$(poll_src)				\
 		util/vireventpollcommon.c			\
 		util/virfile.c			\
 		util/virgettext.c		\
diff --git a/src/util/vireventepoll.c b/src/util/vireventepoll.c
new file mode 100644
index 0000000..5e2a0f5
--- /dev/null
+++ b/src/util/vireventepoll.c
@@ -0,0 +1,201 @@
+/*
+ * vireventpoll.c: Poll based event loop for monitoring file handles
+ *
+ * Copyright (C) 2007, 2010-2014 Red Hat, Inc.
+ * Copyright (C) 2007 Daniel P. Berrange
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Author: Daniel P. Berrange <berrange at redhat.com>
+ */
+
+#include <config.h>
+
+#include <sys/epoll.h>
+
+#include "virfile.h"
+#include "virlog.h"
+#include "vireventpoll.h"
+#include "vireventpollinternal.h"
+
+#define EVENT_DEBUG(fmt, ...) VIR_DEBUG(fmt, __VA_ARGS__)
+
+#define VIR_FROM_THIS VIR_FROM_EVENT
+
+VIR_LOG_INIT("util.eventpoll");
+
+/* Maximum number of events that are returned by epoll in virEventPollRunOnce */
+#define MAX_POLL_EVENTS_AT_ONCE 10
+
+int epollfd;
+
+int virEventPollAddHandleInternal(int watch ATTRIBUTE_UNUSED,
+                                  int fd,
+                                  int nativeevents)
+{
+    size_t i;
+    struct epoll_event ev;
+    ev.events = nativeevents;
+    ev.data.fd = fd;
+    if (epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev) < 0) {
+        if (errno == EEXIST) {
+            for (i = 0; i < eventLoop.handlesCount; i++) {
+                if (eventLoop.handles[i].fd == fd &&
+                    !eventLoop.handles[i].deleted) {
+                    ev.events |= eventLoop.handles[i].events;
+                }
+            }
+            if (epoll_ctl(epollfd, EPOLL_CTL_MOD, fd, &ev) < 0) {
+                return -1;
+            }
+        }
+        else {
+            return -1;
+        }
+    }
+    return 0;
+}
+
+int virEventPollUpdateHandleInternal(int watch, int fd, int nativeevents)
+{
+    struct epoll_event ev;
+    size_t i;
+
+    ev.events = nativeevents;
+    ev.data.fd = fd;
+    for (i = 0; i < eventLoop.handlesCount; i++) {
+        if (eventLoop.handles[i].fd == fd &&
+            !eventLoop.handles[i].deleted &&
+            eventLoop.handles[i].watch != watch) {
+            ev.events |= eventLoop.handles[i].events;
+        }
+    }
+
+    if (epoll_ctl(epollfd, EPOLL_CTL_MOD, fd, &ev) < 0) {
+        return -1;
+    }
+    return 0;
+}
+
+int virEventPollRemoveHandleInternal(int watch, int fd)
+{
+
+    struct epoll_event ev;
+    size_t i;
+
+    ev.events = 0;
+    ev.data.fd = fd;
+    for (i = 0; i < eventLoop.handlesCount; i++) {
+        if (eventLoop.handles[i].fd == fd &&
+            !eventLoop.handles[i].deleted &&
+            eventLoop.handles[i].watch != watch) {
+            ev.events |= eventLoop.handles[i].events;
+        }
+    }
+
+    if (ev.events) {
+        if (epoll_ctl(epollfd, EPOLL_CTL_MOD, fd, &ev) < 0) {
+            return -1;
+        }
+    }
+    else {
+        if (epoll_ctl(epollfd, EPOLL_CTL_DEL, fd, &ev) < 0) {
+            return -1;
+        }
+    }
+
+    return 0;
+}
+
+int virEventPollInitInternal(void)
+{
+    epollfd = epoll_create1(0);
+    if (epollfd < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("Unable to initialize epoll"));
+        return -1;
+    }
+    return 0;
+}
+
+void virEventPollDeinitInternal(void)
+{
+    VIR_FORCE_CLOSE(epollfd);
+}
+
+int
+virEventPollToNativeEvents(int events)
+{
+    int ret = 0;
+    if (events & VIR_EVENT_HANDLE_READABLE)
+        ret |= EPOLLIN;
+    if (events & VIR_EVENT_HANDLE_WRITABLE)
+        ret |= EPOLLOUT;
+    if (events & VIR_EVENT_HANDLE_ERROR)
+        ret |= EPOLLERR;
+    if (events & VIR_EVENT_HANDLE_HANGUP)
+        ret |= EPOLLHUP;
+    return ret;
+}
+
+int
+virEventPollFromNativeEvents(int events)
+{
+    int ret = 0;
+    if (events & EPOLLIN)
+        ret |= VIR_EVENT_HANDLE_READABLE;
+    if (events & EPOLLOUT)
+        ret |= VIR_EVENT_HANDLE_WRITABLE;
+    if (events & EPOLLERR)
+        ret |= VIR_EVENT_HANDLE_ERROR;
+    if (events & EPOLLHUP)
+        ret |= VIR_EVENT_HANDLE_HANGUP;
+    return ret;
+}
+
+struct epoll_event events[MAX_POLL_EVENTS_AT_ONCE];
+
+int virEventPollWait(int timeout, void **opaque)
+{
+    int ret;
+    *opaque = events;
+
+ retry:
+    ret = epoll_wait(epollfd, events,
+                     MAX_POLL_EVENTS_AT_ONCE, timeout);
+    if (ret < 0) {
+        EVENT_DEBUG("Poll got error event %d", errno);
+        if (errno == EINTR || errno == EAGAIN)
+            goto retry;
+        virReportSystemError(errno, "%s",
+                             _("Unable to poll on file handles"));
+    }
+    return ret;
+}
+
+void virEventPollOpaqueFree(void *opaque ATTRIBUTE_UNUSED)
+{
+    return;
+}
+
+int VirWokenFD(void *opaque, size_t n)
+{
+    return ((struct epoll_event *)opaque)[n].data.fd;
+}
+
+int VirWokenEvents(void *opaque, size_t n)
+{
+    return ((struct epoll_event *)opaque)[n].events;
+}
diff --git a/tests/commanddata/test14.log b/tests/commanddata/test3epoll.log
similarity index 94%
copy from tests/commanddata/test14.log
copy to tests/commanddata/test3epoll.log
index 703e6da..e4619b3 100644
--- a/tests/commanddata/test14.log
+++ b/tests/commanddata/test3epoll.log
@@ -8,6 +8,8 @@ ENV:USER=test
 FD:0
 FD:1
 FD:2
+FD:6
+FD:8
 DAEMON:no
 CWD:/tmp
 UMASK:0022
diff --git a/tests/commandtest.c b/tests/commandtest.c
index 7bf5447..6b23659 100644
--- a/tests/commandtest.c
+++ b/tests/commandtest.c
@@ -227,7 +227,11 @@ static int test3(const void *unused ATTRIBUTE_UNUSED)
         goto cleanup;
     }
 
+#ifdef HAVE_LINUX_EPOLL
+    ret = checkoutput("test3epoll", NULL);
+#else
     ret = checkoutput("test3", NULL);
+#endif
 
  cleanup:
     virCommandFree(cmd);
-- 
1.9.5.msysgit.0




More information about the libvir-list mailing list