From: Derbyshev Dmitry <dderbyshev(a)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(a)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