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

From: Derbyshev Dmitry <dderbyshev@virtuozzo.com> Provides about 20% boost on local machine with 35 vms. virEventPollDispatchHandles can also be split to pass cb via epoll data field. Should start sending as PATCH instead? Changes since v1: * ifdef supstituded by 2 .c files with vireventpollinternal.h implementations * PROBE purged Derbyshev Dmitry (2): vireventpoll: isolate common code vireventpoll implimentation using epoll configure.ac | 28 + src/Makefile.am | 12 +- src/util/vireventepoll.c | 201 +++++++ src/util/vireventpoll.c | 700 ++-------------------- src/util/{vireventpoll.c => vireventpollcommon.c} | 231 ++----- src/util/vireventpollinternal.h | 91 +++ tests/commanddata/{test14.log => test3epoll.log} | 2 + tests/commandtest.c | 4 + 8 files changed, 451 insertions(+), 818 deletions(-) create mode 100644 src/util/vireventepoll.c copy src/util/{vireventpoll.c => vireventpollcommon.c} (78%) create mode 100644 src/util/vireventpollinternal.h copy tests/commanddata/{test14.log => test3epoll.log} (94%) -- 1.9.5.msysgit.0

From: Derbyshev Dmitry <dderbyshev@virtuozzo.com> To introduce new implementation (ex. with epoll) some code should be substituted. This patch divides vireventpoll into common and implementation-specific parts. --- src/Makefile.am | 4 +- src/util/vireventpoll.c | 700 ++-------------------- src/util/{vireventpoll.c => vireventpollcommon.c} | 231 ++----- src/util/vireventpollinternal.h | 91 +++ 4 files changed, 209 insertions(+), 817 deletions(-) copy src/util/{vireventpoll.c => vireventpollcommon.c} (78%) create mode 100644 src/util/vireventpollinternal.h diff --git a/src/Makefile.am b/src/Makefile.am index 2f32d41..f2643ea 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -112,7 +112,8 @@ UTIL_SOURCES = \ util/virendian.h \ util/virerror.c util/virerror.h \ util/virevent.c util/virevent.h \ - util/vireventpoll.c util/vireventpoll.h \ + util/vireventpollcommon.c util/vireventpoll.h \ + util/vireventpoll.c util/vireventpollinternal.h \ util/virfile.c util/virfile.h \ util/virfirewall.c util/virfirewall.h \ util/virfirewallpriv.h \ @@ -2376,6 +2377,7 @@ libvirt_setuid_rpc_client_la_SOURCES = \ util/virerror.c \ util/virevent.c \ util/vireventpoll.c \ + util/vireventpollcommon.c \ util/virfile.c \ util/virgettext.c \ util/virhash.c \ diff --git a/src/util/vireventpoll.c b/src/util/vireventpoll.c index 81ecab4..e429e98 100644 --- a/src/util/vireventpoll.c +++ b/src/util/vireventpoll.c @@ -23,23 +23,12 @@ #include <config.h> -#include <stdlib.h> -#include <string.h> #include <poll.h> -#include <sys/time.h> -#include <errno.h> -#include <unistd.h> -#include <fcntl.h> -#include "virthread.h" +#include "viralloc.h" #include "virlog.h" #include "vireventpoll.h" -#include "viralloc.h" -#include "virutil.h" -#include "virfile.h" -#include "virerror.h" -#include "virprobe.h" -#include "virtime.h" +#include "vireventpollinternal.h" #define EVENT_DEBUG(fmt, ...) VIR_DEBUG(fmt, __VA_ARGS__) @@ -47,329 +36,26 @@ VIR_LOG_INIT("util.eventpoll"); -static int virEventPollInterruptLocked(void); - -/* State for a single file handle being monitored */ -struct virEventPollHandle { - int watch; - int fd; - int events; - virEventHandleCallback cb; - virFreeCallback ff; - void *opaque; - int deleted; -}; - -/* State for a single timer being generated */ -struct virEventPollTimeout { - int timer; - int frequency; - unsigned long long expiresAt; - virEventTimeoutCallback cb; - virFreeCallback ff; - void *opaque; - int deleted; -}; - -/* Allocate extra slots for virEventPollHandle/virEventPollTimeout - records in this multiple */ -#define EVENT_ALLOC_EXTENT 10 - -/* State for the main event loop */ -struct virEventPollLoop { - virMutex lock; - int running; - virThread leader; - int wakeupfd[2]; - size_t handlesCount; - size_t handlesAlloc; - struct virEventPollHandle *handles; - size_t timeoutsCount; - size_t timeoutsAlloc; - struct virEventPollTimeout *timeouts; -}; - -/* Only have one event loop */ -static struct virEventPollLoop eventLoop; - -/* Unique ID for the next FD watch to be registered */ -static int nextWatch = 1; - -/* Unique ID for the next timer to be registered */ -static int nextTimer = 1; - -/* - * Register a callback for monitoring file handle events. - * NB, it *must* be safe to call this from within a callback - * For this reason we only ever append to existing list. - */ -int virEventPollAddHandle(int fd, int events, - virEventHandleCallback cb, - void *opaque, - virFreeCallback ff) +int virEventPollAddHandleInternal(int watch ATTRIBUTE_UNUSED, + int fd ATTRIBUTE_UNUSED, + int nativeevents ATTRIBUTE_UNUSED) { - int watch; - virMutexLock(&eventLoop.lock); - if (eventLoop.handlesCount == eventLoop.handlesAlloc) { - EVENT_DEBUG("Used %zu handle slots, adding at least %d more", - eventLoop.handlesAlloc, EVENT_ALLOC_EXTENT); - if (VIR_RESIZE_N(eventLoop.handles, eventLoop.handlesAlloc, - eventLoop.handlesCount, EVENT_ALLOC_EXTENT) < 0) { - virMutexUnlock(&eventLoop.lock); - return -1; - } - } - - watch = nextWatch++; - - eventLoop.handles[eventLoop.handlesCount].watch = watch; - eventLoop.handles[eventLoop.handlesCount].fd = fd; - eventLoop.handles[eventLoop.handlesCount].events = - virEventPollToNativeEvents(events); - eventLoop.handles[eventLoop.handlesCount].cb = cb; - eventLoop.handles[eventLoop.handlesCount].ff = ff; - eventLoop.handles[eventLoop.handlesCount].opaque = opaque; - eventLoop.handles[eventLoop.handlesCount].deleted = 0; - - eventLoop.handlesCount++; - virEventPollInterruptLocked(); - - PROBE(EVENT_POLL_ADD_HANDLE, - "watch=%d fd=%d events=%d cb=%p opaque=%p ff=%p", - watch, fd, events, cb, opaque, ff); - virMutexUnlock(&eventLoop.lock); - - return watch; -} - -void virEventPollUpdateHandle(int watch, int events) -{ - size_t i; - bool found = false; - PROBE(EVENT_POLL_UPDATE_HANDLE, - "watch=%d events=%d", - watch, events); - - if (watch <= 0) { - VIR_WARN("Ignoring invalid update watch %d", watch); - return; - } - - virMutexLock(&eventLoop.lock); - for (i = 0; i < eventLoop.handlesCount; i++) { - if (eventLoop.handles[i].watch == watch) { - eventLoop.handles[i].events = - virEventPollToNativeEvents(events); - virEventPollInterruptLocked(); - found = true; - break; - } - } - virMutexUnlock(&eventLoop.lock); - - if (!found) - VIR_WARN("Got update for non-existent handle watch %d", watch); -} - -/* - * Unregister a callback from a file handle - * NB, it *must* be safe to call this from within a callback - * For this reason we only ever set a flag in the existing list. - * Actual deletion will be done out-of-band - */ -int virEventPollRemoveHandle(int watch) -{ - size_t i; - PROBE(EVENT_POLL_REMOVE_HANDLE, - "watch=%d", - watch); - - if (watch <= 0) { - VIR_WARN("Ignoring invalid remove watch %d", watch); - return -1; - } - - virMutexLock(&eventLoop.lock); - for (i = 0; i < eventLoop.handlesCount; i++) { - if (eventLoop.handles[i].deleted) - continue; - - if (eventLoop.handles[i].watch == watch) { - EVENT_DEBUG("mark delete %zu %d", i, eventLoop.handles[i].fd); - eventLoop.handles[i].deleted = 1; - virEventPollInterruptLocked(); - virMutexUnlock(&eventLoop.lock); - return 0; - } - } - virMutexUnlock(&eventLoop.lock); - return -1; + return 0; } - -/* - * Register a callback for a timer event - * NB, it *must* be safe to call this from within a callback - * For this reason we only ever append to existing list. - */ -int virEventPollAddTimeout(int frequency, - virEventTimeoutCallback cb, - void *opaque, - virFreeCallback ff) +int virEventPollUpdateHandleInternal(int watch ATTRIBUTE_UNUSED, + int fd ATTRIBUTE_UNUSED, + int nativeevents ATTRIBUTE_UNUSED) { - unsigned long long now; - int ret; - - if (virTimeMillisNow(&now) < 0) - return -1; - - virMutexLock(&eventLoop.lock); - if (eventLoop.timeoutsCount == eventLoop.timeoutsAlloc) { - EVENT_DEBUG("Used %zu timeout slots, adding at least %d more", - eventLoop.timeoutsAlloc, EVENT_ALLOC_EXTENT); - if (VIR_RESIZE_N(eventLoop.timeouts, eventLoop.timeoutsAlloc, - eventLoop.timeoutsCount, EVENT_ALLOC_EXTENT) < 0) { - virMutexUnlock(&eventLoop.lock); - return -1; - } - } - - eventLoop.timeouts[eventLoop.timeoutsCount].timer = nextTimer++; - eventLoop.timeouts[eventLoop.timeoutsCount].frequency = frequency; - eventLoop.timeouts[eventLoop.timeoutsCount].cb = cb; - eventLoop.timeouts[eventLoop.timeoutsCount].ff = ff; - eventLoop.timeouts[eventLoop.timeoutsCount].opaque = opaque; - eventLoop.timeouts[eventLoop.timeoutsCount].deleted = 0; - eventLoop.timeouts[eventLoop.timeoutsCount].expiresAt = - frequency >= 0 ? frequency + now : 0; - - eventLoop.timeoutsCount++; - ret = nextTimer-1; virEventPollInterruptLocked(); - - PROBE(EVENT_POLL_ADD_TIMEOUT, - "timer=%d frequency=%d cb=%p opaque=%p ff=%p", - ret, frequency, cb, opaque, ff); - virMutexUnlock(&eventLoop.lock); - return ret; -} - -void virEventPollUpdateTimeout(int timer, int frequency) -{ - unsigned long long now; - size_t i; - bool found = false; - PROBE(EVENT_POLL_UPDATE_TIMEOUT, - "timer=%d frequency=%d", - timer, frequency); - - if (timer <= 0) { - VIR_WARN("Ignoring invalid update timer %d", timer); - return; - } - - if (virTimeMillisNow(&now) < 0) - return; - - virMutexLock(&eventLoop.lock); - for (i = 0; i < eventLoop.timeoutsCount; i++) { - if (eventLoop.timeouts[i].timer == timer) { - eventLoop.timeouts[i].frequency = frequency; - eventLoop.timeouts[i].expiresAt = - frequency >= 0 ? frequency + now : 0; - VIR_DEBUG("Set timer freq=%d expires=%llu", frequency, - eventLoop.timeouts[i].expiresAt); - virEventPollInterruptLocked(); - found = true; - break; - } - } - virMutexUnlock(&eventLoop.lock); - - if (!found) - VIR_WARN("Got update for non-existent timer %d", timer); -} - -/* - * Unregister a callback for a timer - * NB, it *must* be safe to call this from within a callback - * For this reason we only ever set a flag in the existing list. - * Actual deletion will be done out-of-band - */ -int virEventPollRemoveTimeout(int timer) -{ - size_t i; - PROBE(EVENT_POLL_REMOVE_TIMEOUT, - "timer=%d", - timer); - - if (timer <= 0) { - VIR_WARN("Ignoring invalid remove timer %d", timer); - return -1; - } - - virMutexLock(&eventLoop.lock); - for (i = 0; i < eventLoop.timeoutsCount; i++) { - if (eventLoop.timeouts[i].deleted) - continue; - - if (eventLoop.timeouts[i].timer == timer) { - eventLoop.timeouts[i].deleted = 1; - virEventPollInterruptLocked(); - virMutexUnlock(&eventLoop.lock); - return 0; - } - } - virMutexUnlock(&eventLoop.lock); - return -1; + return 0; } -/* Iterates over all registered timeouts and determine which - * will be the first to expire. - * @timeout: filled with expiry time of soonest timer, or -1 if - * no timeout is pending - * returns: 0 on success, -1 on error - */ -static int virEventPollCalculateTimeout(int *timeout) +int virEventPollRemoveHandleInternal(int watch ATTRIBUTE_UNUSED, + int fd ATTRIBUTE_UNUSED) { - unsigned long long then = 0; - size_t i; - EVENT_DEBUG("Calculate expiry of %zu timers", eventLoop.timeoutsCount); - /* Figure out if we need a timeout */ - for (i = 0; i < eventLoop.timeoutsCount; i++) { - if (eventLoop.timeouts[i].deleted) - continue; - if (eventLoop.timeouts[i].frequency < 0) - continue; - - EVENT_DEBUG("Got a timeout scheduled for %llu", eventLoop.timeouts[i].expiresAt); - if (then == 0 || - eventLoop.timeouts[i].expiresAt < then) - then = eventLoop.timeouts[i].expiresAt; - } - - /* Calculate how long we should wait for a timeout if needed */ - if (then > 0) { - unsigned long long now; - - if (virTimeMillisNow(&now) < 0) - return -1; - - EVENT_DEBUG("Schedule timeout then=%llu now=%llu", then, now); - if (then <= now) - *timeout = 0; - else - *timeout = ((then - now) > INT_MAX) ? INT_MAX : (then - now); - } else { - *timeout = -1; - } - - if (*timeout > -1) - EVENT_DEBUG("Timeout at %llu due in %d ms", then, *timeout); - else - EVENT_DEBUG("%s", "No timeout is pending"); - + // virEventPollInterruptLocked() is called in common code. return 0; } @@ -410,332 +96,14 @@ static struct pollfd *virEventPollMakePollFDs(int *nfds) { return fds; } - -/* - * Iterate over all timers and determine if any have expired. - * Invoke the user supplied callback for each timer whose - * expiry time is met, and schedule the next timeout. Does - * not try to 'catch up' on time if the actual expiry time - * was later than the requested time. - * - * This method must cope with new timers being registered - * by a callback, and must skip any timers marked as deleted. - * - * Returns 0 upon success, -1 if an error occurred - */ -static int virEventPollDispatchTimeouts(void) -{ - unsigned long long now; - size_t i; - /* Save this now - it may be changed during dispatch */ - int ntimeouts = eventLoop.timeoutsCount; - VIR_DEBUG("Dispatch %d", ntimeouts); - - if (virTimeMillisNow(&now) < 0) - return -1; - - for (i = 0; i < ntimeouts; i++) { - if (eventLoop.timeouts[i].deleted || eventLoop.timeouts[i].frequency < 0) - continue; - - /* Add 20ms fuzz so we don't pointlessly spin doing - * <10ms sleeps, particularly on kernels with low HZ - * it is fine that a timer expires 20ms earlier than - * requested - */ - if (eventLoop.timeouts[i].expiresAt <= (now+20)) { - virEventTimeoutCallback cb = eventLoop.timeouts[i].cb; - int timer = eventLoop.timeouts[i].timer; - void *opaque = eventLoop.timeouts[i].opaque; - eventLoop.timeouts[i].expiresAt = - now + eventLoop.timeouts[i].frequency; - - PROBE(EVENT_POLL_DISPATCH_TIMEOUT, - "timer=%d", - timer); - virMutexUnlock(&eventLoop.lock); - (cb)(timer, opaque); - virMutexLock(&eventLoop.lock); - } - } - return 0; -} - - -/* Iterate over all file handles and dispatch any which - * have pending events listed in the poll() data. Invoke - * the user supplied callback for each handle which has - * pending events - * - * This method must cope with new handles being registered - * by a callback, and must skip any handles marked as deleted. - * - * Returns 0 upon success, -1 if an error occurred - */ -static int virEventPollDispatchHandles(int nfds, struct pollfd *fds) -{ - size_t i, n; - VIR_DEBUG("Dispatch %d", nfds); - - /* NB, use nfds not eventLoop.handlesCount, because new - * fds might be added on end of list, and they're not - * in the fds array we've got */ - for (i = 0, n = 0; n < nfds && i < eventLoop.handlesCount; n++) { - while (i < eventLoop.handlesCount && - (eventLoop.handles[i].fd != fds[n].fd || - eventLoop.handles[i].events == 0)) { - i++; - } - if (i == eventLoop.handlesCount) - break; - - VIR_DEBUG("i=%zu w=%d", i, eventLoop.handles[i].watch); - if (eventLoop.handles[i].deleted) { - EVENT_DEBUG("Skip deleted n=%zu w=%d f=%d", i, - eventLoop.handles[i].watch, eventLoop.handles[i].fd); - continue; - } - - if (fds[n].revents) { - virEventHandleCallback cb = eventLoop.handles[i].cb; - int watch = eventLoop.handles[i].watch; - void *opaque = eventLoop.handles[i].opaque; - int hEvents = virEventPollFromNativeEvents(fds[n].revents); - PROBE(EVENT_POLL_DISPATCH_HANDLE, - "watch=%d events=%d", - watch, hEvents); - virMutexUnlock(&eventLoop.lock); - (cb)(watch, fds[n].fd, hEvents, opaque); - virMutexLock(&eventLoop.lock); - } - } - - return 0; -} - - -/* Used post dispatch to actually remove any timers that - * were previously marked as deleted. This asynchronous - * cleanup is needed to make dispatch re-entrant safe. - */ -static void virEventPollCleanupTimeouts(void) -{ - size_t i; - size_t gap; - VIR_DEBUG("Cleanup %zu", eventLoop.timeoutsCount); - - /* Remove deleted entries, shuffling down remaining - * entries as needed to form contiguous series - */ - for (i = 0; i < eventLoop.timeoutsCount;) { - if (!eventLoop.timeouts[i].deleted) { - i++; - continue; - } - - PROBE(EVENT_POLL_PURGE_TIMEOUT, - "timer=%d", - eventLoop.timeouts[i].timer); - if (eventLoop.timeouts[i].ff) { - virFreeCallback ff = eventLoop.timeouts[i].ff; - void *opaque = eventLoop.timeouts[i].opaque; - virMutexUnlock(&eventLoop.lock); - ff(opaque); - virMutexLock(&eventLoop.lock); - } - - if ((i+1) < eventLoop.timeoutsCount) { - memmove(eventLoop.timeouts+i, - eventLoop.timeouts+i+1, - sizeof(struct virEventPollTimeout)*(eventLoop.timeoutsCount - -(i+1))); - } - eventLoop.timeoutsCount--; - } - - /* Release some memory if we've got a big chunk free */ - gap = eventLoop.timeoutsAlloc - eventLoop.timeoutsCount; - if (eventLoop.timeoutsCount == 0 || - (gap > eventLoop.timeoutsCount && gap > EVENT_ALLOC_EXTENT)) { - EVENT_DEBUG("Found %zu out of %zu timeout slots used, releasing %zu", - eventLoop.timeoutsCount, eventLoop.timeoutsAlloc, gap); - VIR_SHRINK_N(eventLoop.timeouts, eventLoop.timeoutsAlloc, gap); - } -} - -/* Used post dispatch to actually remove any handles that - * were previously marked as deleted. This asynchronous - * cleanup is needed to make dispatch re-entrant safe. - */ -static void virEventPollCleanupHandles(void) -{ - size_t i; - size_t gap; - VIR_DEBUG("Cleanup %zu", eventLoop.handlesCount); - - /* Remove deleted entries, shuffling down remaining - * entries as needed to form contiguous series - */ - for (i = 0; i < eventLoop.handlesCount;) { - if (!eventLoop.handles[i].deleted) { - i++; - continue; - } - - PROBE(EVENT_POLL_PURGE_HANDLE, - "watch=%d", - eventLoop.handles[i].watch); - if (eventLoop.handles[i].ff) { - virFreeCallback ff = eventLoop.handles[i].ff; - void *opaque = eventLoop.handles[i].opaque; - virMutexUnlock(&eventLoop.lock); - ff(opaque); - virMutexLock(&eventLoop.lock); - } - - if ((i+1) < eventLoop.handlesCount) { - memmove(eventLoop.handles+i, - eventLoop.handles+i+1, - sizeof(struct virEventPollHandle)*(eventLoop.handlesCount - -(i+1))); - } - eventLoop.handlesCount--; - } - - /* Release some memory if we've got a big chunk free */ - gap = eventLoop.handlesAlloc - eventLoop.handlesCount; - if (eventLoop.handlesCount == 0 || - (gap > eventLoop.handlesCount && gap > EVENT_ALLOC_EXTENT)) { - EVENT_DEBUG("Found %zu out of %zu handles slots used, releasing %zu", - eventLoop.handlesCount, eventLoop.handlesAlloc, gap); - VIR_SHRINK_N(eventLoop.handles, eventLoop.handlesAlloc, gap); - } -} - -/* - * Run a single iteration of the event loop, blocking until - * at least one file handle has an event, or a timer expires - */ -int virEventPollRunOnce(void) +int virEventPollInitInternal(void) { - struct pollfd *fds = NULL; - int ret, timeout, nfds; - - virMutexLock(&eventLoop.lock); - eventLoop.running = 1; - virThreadSelf(&eventLoop.leader); - - virEventPollCleanupTimeouts(); - virEventPollCleanupHandles(); - - if (!(fds = virEventPollMakePollFDs(&nfds)) || - virEventPollCalculateTimeout(&timeout) < 0) - goto error; - - virMutexUnlock(&eventLoop.lock); - - retry: - PROBE(EVENT_POLL_RUN, - "nhandles=%d timeout=%d", - nfds, timeout); - ret = poll(fds, nfds, 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")); - goto error_unlocked; - } - EVENT_DEBUG("Poll got %d event(s)", ret); - - virMutexLock(&eventLoop.lock); - if (virEventPollDispatchTimeouts() < 0) - goto error; - - if (ret > 0 && - virEventPollDispatchHandles(nfds, fds) < 0) - goto error; - - virEventPollCleanupTimeouts(); - virEventPollCleanupHandles(); - - eventLoop.running = 0; - virMutexUnlock(&eventLoop.lock); - VIR_FREE(fds); - return 0; - - error: - virMutexUnlock(&eventLoop.lock); - error_unlocked: - VIR_FREE(fds); - return -1; -} - - -static void virEventPollHandleWakeup(int watch ATTRIBUTE_UNUSED, - int fd, - int events ATTRIBUTE_UNUSED, - void *opaque ATTRIBUTE_UNUSED) -{ - char c; - virMutexLock(&eventLoop.lock); - ignore_value(saferead(fd, &c, sizeof(c))); - virMutexUnlock(&eventLoop.lock); -} - -int virEventPollInit(void) -{ - if (virMutexInit(&eventLoop.lock) < 0) { - virReportSystemError(errno, "%s", - _("Unable to initialize mutex")); - return -1; - } - - if (pipe2(eventLoop.wakeupfd, O_CLOEXEC | O_NONBLOCK) < 0) { - virReportSystemError(errno, "%s", - _("Unable to setup wakeup pipe")); - return -1; - } - - if (virEventPollAddHandle(eventLoop.wakeupfd[0], - VIR_EVENT_HANDLE_READABLE, - virEventPollHandleWakeup, NULL, NULL) < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("Unable to add handle %d to event loop"), - eventLoop.wakeupfd[0]); - VIR_FORCE_CLOSE(eventLoop.wakeupfd[0]); - VIR_FORCE_CLOSE(eventLoop.wakeupfd[1]); - return -1; - } - return 0; } -static int virEventPollInterruptLocked(void) +void virEventPollDeinitInternal(void) { - char c = '\0'; - - if (!eventLoop.running || - virThreadIsSelf(&eventLoop.leader)) { - VIR_DEBUG("Skip interrupt, %d %llu", eventLoop.running, - virThreadID(&eventLoop.leader)); - return 0; - } - - VIR_DEBUG("Interrupting"); - if (safewrite(eventLoop.wakeupfd[1], &c, sizeof(c)) != sizeof(c)) - return -1; - return 0; -} - -int virEventPollInterrupt(void) -{ - int ret; - virMutexLock(&eventLoop.lock); - ret = virEventPollInterruptLocked(); - virMutexUnlock(&eventLoop.lock); - return ret; + return; } int @@ -769,3 +137,39 @@ virEventPollFromNativeEvents(int events) ret |= VIR_EVENT_HANDLE_HANGUP; return ret; } + +int virEventPollWait(int timeout, void **opaque) +{ + int ret, nfds; + struct pollfd *fds = NULL; + + if (!(fds = virEventPollMakePollFDs(&nfds))) + return -1; + *opaque = fds; + + retry: + ret = poll(fds, nfds, 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 nfds; +} + +void virEventPollOpaqueFree(void *opaque) +{ + VIR_FREE(opaque); +} + +int VirWokenFD(void *opaque, size_t n) +{ + return ((struct pollfd *)opaque)[n].fd; +} + +int VirWokenEvents(void *opaque, size_t n) +{ + return ((struct pollfd *)opaque)[n].revents; +} diff --git a/src/util/vireventpoll.c b/src/util/vireventpollcommon.c similarity index 78% copy from src/util/vireventpoll.c copy to src/util/vireventpollcommon.c index 81ecab4..6a3a372 100644 --- a/src/util/vireventpoll.c +++ b/src/util/vireventpollcommon.c @@ -25,7 +25,6 @@ #include <stdlib.h> #include <string.h> -#include <poll.h> #include <sys/time.h> #include <errno.h> #include <unistd.h> @@ -34,6 +33,7 @@ #include "virthread.h" #include "virlog.h" #include "vireventpoll.h" +#include "vireventpollinternal.h" #include "viralloc.h" #include "virutil.h" #include "virfile.h" @@ -47,56 +47,9 @@ VIR_LOG_INIT("util.eventpoll"); -static int virEventPollInterruptLocked(void); - -/* State for a single file handle being monitored */ -struct virEventPollHandle { - int watch; - int fd; - int events; - virEventHandleCallback cb; - virFreeCallback ff; - void *opaque; - int deleted; -}; - -/* State for a single timer being generated */ -struct virEventPollTimeout { - int timer; - int frequency; - unsigned long long expiresAt; - virEventTimeoutCallback cb; - virFreeCallback ff; - void *opaque; - int deleted; -}; - -/* Allocate extra slots for virEventPollHandle/virEventPollTimeout - records in this multiple */ -#define EVENT_ALLOC_EXTENT 10 - -/* State for the main event loop */ -struct virEventPollLoop { - virMutex lock; - int running; - virThread leader; - int wakeupfd[2]; - size_t handlesCount; - size_t handlesAlloc; - struct virEventPollHandle *handles; - size_t timeoutsCount; - size_t timeoutsAlloc; - struct virEventPollTimeout *timeouts; -}; - -/* Only have one event loop */ -static struct virEventPollLoop eventLoop; - -/* Unique ID for the next FD watch to be registered */ -static int nextWatch = 1; - -/* Unique ID for the next timer to be registered */ -static int nextTimer = 1; +struct virEventPollLoop eventLoop; +int nextWatch = 1; +int nextTimer = 1; /* * Register a callback for monitoring file handle events. @@ -109,6 +62,7 @@ int virEventPollAddHandle(int fd, int events, virFreeCallback ff) { int watch; + int native = virEventPollToNativeEvents(events); virMutexLock(&eventLoop.lock); if (eventLoop.handlesCount == eventLoop.handlesAlloc) { EVENT_DEBUG("Used %zu handle slots, adding at least %d more", @@ -124,8 +78,7 @@ int virEventPollAddHandle(int fd, int events, eventLoop.handles[eventLoop.handlesCount].watch = watch; eventLoop.handles[eventLoop.handlesCount].fd = fd; - eventLoop.handles[eventLoop.handlesCount].events = - virEventPollToNativeEvents(events); + eventLoop.handles[eventLoop.handlesCount].events = native; eventLoop.handles[eventLoop.handlesCount].cb = cb; eventLoop.handles[eventLoop.handlesCount].ff = ff; eventLoop.handles[eventLoop.handlesCount].opaque = opaque; @@ -133,7 +86,10 @@ int virEventPollAddHandle(int fd, int events, eventLoop.handlesCount++; - virEventPollInterruptLocked(); + if (virEventPollAddHandleInternal(watch, fd, native) < 0) { + virMutexUnlock(&eventLoop.lock); + return -1; + } PROBE(EVENT_POLL_ADD_HANDLE, "watch=%d fd=%d events=%d cb=%p opaque=%p ff=%p", @@ -147,6 +103,7 @@ void virEventPollUpdateHandle(int watch, int events) { size_t i; bool found = false; + int native = virEventPollToNativeEvents(events); PROBE(EVENT_POLL_UPDATE_HANDLE, "watch=%d events=%d", watch, events); @@ -159,17 +116,21 @@ void virEventPollUpdateHandle(int watch, int events) virMutexLock(&eventLoop.lock); for (i = 0; i < eventLoop.handlesCount; i++) { if (eventLoop.handles[i].watch == watch) { - eventLoop.handles[i].events = - virEventPollToNativeEvents(events); - virEventPollInterruptLocked(); + eventLoop.handles[i].events = native; found = true; break; } } - virMutexUnlock(&eventLoop.lock); - if (!found) + if (!found) { VIR_WARN("Got update for non-existent handle watch %d", watch); + } + else if (virEventPollUpdateHandleInternal(watch, + eventLoop.handles[i].fd, native) < 0) { + VIR_WARN("Update for existing handle watch %d failed", watch); + } + + virMutexUnlock(&eventLoop.lock); } /* @@ -196,15 +157,25 @@ int virEventPollRemoveHandle(int watch) continue; if (eventLoop.handles[i].watch == watch) { - EVENT_DEBUG("mark delete %zu %d", i, eventLoop.handles[i].fd); - eventLoop.handles[i].deleted = 1; - virEventPollInterruptLocked(); - virMutexUnlock(&eventLoop.lock); - return 0; + break; } } + + if (i == eventLoop.handlesCount) { + virMutexUnlock(&eventLoop.lock); + return -1; + } + + if (virEventPollRemoveHandleInternal(watch, eventLoop.handles[i].fd) < 0) { + virMutexUnlock(&eventLoop.lock); + return -1; + } + + EVENT_DEBUG("mark delete %zu %d", i, eventLoop.handles[i].fd); + eventLoop.handles[i].deleted = 1; + virEventPollInterruptLocked(); virMutexUnlock(&eventLoop.lock); - return -1; + return 0; } @@ -374,44 +345,6 @@ static int virEventPollCalculateTimeout(int *timeout) } /* - * Allocate a pollfd array containing data for all registered - * file handles. The caller must free the returned data struct - * returns: the pollfd array, or NULL on error - */ -static struct pollfd *virEventPollMakePollFDs(int *nfds) { - struct pollfd *fds; - size_t i; - - *nfds = 0; - for (i = 0; i < eventLoop.handlesCount; i++) { - if (eventLoop.handles[i].events && !eventLoop.handles[i].deleted) - (*nfds)++; - } - - /* Setup the poll file handle data structs */ - if (VIR_ALLOC_N(fds, *nfds) < 0) - return NULL; - - *nfds = 0; - for (i = 0; i < eventLoop.handlesCount; i++) { - EVENT_DEBUG("Prepare n=%zu w=%d, f=%d e=%d d=%d", i, - eventLoop.handles[i].watch, - eventLoop.handles[i].fd, - eventLoop.handles[i].events, - eventLoop.handles[i].deleted); - if (!eventLoop.handles[i].events || eventLoop.handles[i].deleted) - continue; - fds[*nfds].fd = eventLoop.handles[i].fd; - fds[*nfds].events = eventLoop.handles[i].events; - fds[*nfds].revents = 0; - (*nfds)++; - } - - return fds; -} - - -/* * Iterate over all timers and determine if any have expired. * Invoke the user supplied callback for each timer whose * expiry time is met, and schedule the next timeout. Does @@ -472,7 +405,7 @@ static int virEventPollDispatchTimeouts(void) * * Returns 0 upon success, -1 if an error occurred */ -static int virEventPollDispatchHandles(int nfds, struct pollfd *fds) +static int virEventPollDispatchHandles(int nfds, void *opaque) { size_t i, n; VIR_DEBUG("Dispatch %d", nfds); @@ -480,14 +413,14 @@ static int virEventPollDispatchHandles(int nfds, struct pollfd *fds) /* NB, use nfds not eventLoop.handlesCount, because new * fds might be added on end of list, and they're not * in the fds array we've got */ - for (i = 0, n = 0; n < nfds && i < eventLoop.handlesCount; n++) { - while (i < eventLoop.handlesCount && - (eventLoop.handles[i].fd != fds[n].fd || - eventLoop.handles[i].events == 0)) { - i++; + for (n = 0; n < nfds; n++) { + for (i = 0; i < eventLoop.handlesCount && + (eventLoop.handles[i].fd != VirWokenFD(opaque, n) || + eventLoop.handles[i].events == 0) ; i++) { + ; } if (i == eventLoop.handlesCount) - break; + continue; VIR_DEBUG("i=%zu w=%d", i, eventLoop.handles[i].watch); if (eventLoop.handles[i].deleted) { @@ -496,16 +429,16 @@ static int virEventPollDispatchHandles(int nfds, struct pollfd *fds) continue; } - if (fds[n].revents) { + if (VirWokenEvents(opaque, n)) { + int hEvents = virEventPollFromNativeEvents(VirWokenEvents(opaque, n)); virEventHandleCallback cb = eventLoop.handles[i].cb; int watch = eventLoop.handles[i].watch; - void *opaque = eventLoop.handles[i].opaque; - int hEvents = virEventPollFromNativeEvents(fds[n].revents); + void *cbopaque = eventLoop.handles[i].opaque; PROBE(EVENT_POLL_DISPATCH_HANDLE, "watch=%d events=%d", watch, hEvents); virMutexUnlock(&eventLoop.lock); - (cb)(watch, fds[n].fd, hEvents, opaque); + (cb)(watch, VirWokenFD(opaque, n), hEvents, cbopaque); virMutexLock(&eventLoop.lock); } } @@ -618,8 +551,8 @@ static void virEventPollCleanupHandles(void) */ int virEventPollRunOnce(void) { - struct pollfd *fds = NULL; - int ret, timeout, nfds; + int nfds, timeout; + void *opaque = NULL; virMutexLock(&eventLoop.lock); eventLoop.running = 1; @@ -628,33 +561,20 @@ int virEventPollRunOnce(void) virEventPollCleanupTimeouts(); virEventPollCleanupHandles(); - if (!(fds = virEventPollMakePollFDs(&nfds)) || - virEventPollCalculateTimeout(&timeout) < 0) + if (virEventPollCalculateTimeout(&timeout) < 0) goto error; virMutexUnlock(&eventLoop.lock); - retry: - PROBE(EVENT_POLL_RUN, - "nhandles=%d timeout=%d", - nfds, timeout); - ret = poll(fds, nfds, 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")); + if ((nfds = virEventPollWait(timeout, &opaque)) < 0) goto error_unlocked; - } - EVENT_DEBUG("Poll got %d event(s)", ret); + EVENT_DEBUG("Poll got %d event(s)", nfds); virMutexLock(&eventLoop.lock); if (virEventPollDispatchTimeouts() < 0) goto error; - if (ret > 0 && - virEventPollDispatchHandles(nfds, fds) < 0) + if (nfds > 0 && virEventPollDispatchHandles(nfds, opaque) < 0) goto error; virEventPollCleanupTimeouts(); @@ -662,13 +582,13 @@ int virEventPollRunOnce(void) eventLoop.running = 0; virMutexUnlock(&eventLoop.lock); - VIR_FREE(fds); + virEventPollOpaqueFree(opaque); return 0; error: virMutexUnlock(&eventLoop.lock); error_unlocked: - VIR_FREE(fds); + virEventPollOpaqueFree(opaque); return -1; } @@ -698,6 +618,12 @@ int virEventPollInit(void) return -1; } + if (virEventPollInitInternal() < 0) { + VIR_FORCE_CLOSE(eventLoop.wakeupfd[0]); + VIR_FORCE_CLOSE(eventLoop.wakeupfd[1]); + return -1; + } + if (virEventPollAddHandle(eventLoop.wakeupfd[0], VIR_EVENT_HANDLE_READABLE, virEventPollHandleWakeup, NULL, NULL) < 0) { @@ -706,13 +632,14 @@ int virEventPollInit(void) eventLoop.wakeupfd[0]); VIR_FORCE_CLOSE(eventLoop.wakeupfd[0]); VIR_FORCE_CLOSE(eventLoop.wakeupfd[1]); + virEventPollDeinitInternal(); return -1; } return 0; } -static int virEventPollInterruptLocked(void) +int virEventPollInterruptLocked(void) { char c = '\0'; @@ -737,35 +664,3 @@ int virEventPollInterrupt(void) virMutexUnlock(&eventLoop.lock); return ret; } - -int -virEventPollToNativeEvents(int events) -{ - int ret = 0; - if (events & VIR_EVENT_HANDLE_READABLE) - ret |= POLLIN; - if (events & VIR_EVENT_HANDLE_WRITABLE) - ret |= POLLOUT; - if (events & VIR_EVENT_HANDLE_ERROR) - ret |= POLLERR; - if (events & VIR_EVENT_HANDLE_HANGUP) - ret |= POLLHUP; - return ret; -} - -int -virEventPollFromNativeEvents(int events) -{ - int ret = 0; - if (events & POLLIN) - ret |= VIR_EVENT_HANDLE_READABLE; - if (events & POLLOUT) - ret |= VIR_EVENT_HANDLE_WRITABLE; - if (events & POLLERR) - ret |= VIR_EVENT_HANDLE_ERROR; - if (events & POLLNVAL) /* Treat NVAL as error, since libvirt doesn't distinguish */ - ret |= VIR_EVENT_HANDLE_ERROR; - if (events & POLLHUP) - ret |= VIR_EVENT_HANDLE_HANGUP; - return ret; -} diff --git a/src/util/vireventpollinternal.h b/src/util/vireventpollinternal.h new file mode 100644 index 0000000..19d2fd9 --- /dev/null +++ b/src/util/vireventpollinternal.h @@ -0,0 +1,91 @@ +/* + * vireventpoll.h: Poll based event loop for monitoring file handles + * + * Copyright (C) 2007 Daniel P. Berrange + * Copyright (C) 2007 Red Hat, Inc. + * + * 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@redhat.com> + */ + +#ifndef __VIR_EVENT_POLL_INTERNAL_H__ +# define __VIR_EVENT_POLL_INTERNAL_H__ + +# include "internal.h" +#include "virthread.h" + +int virEventPollInterruptLocked(void); + +/* State for a single file handle being monitored */ +struct virEventPollHandle { + int watch; + int fd; + int events; + virEventHandleCallback cb; + virFreeCallback ff; + void *opaque; + int deleted; +}; + +/* State for a single timer being generated */ +struct virEventPollTimeout { + int timer; + int frequency; + unsigned long long expiresAt; + virEventTimeoutCallback cb; + virFreeCallback ff; + void *opaque; + int deleted; +}; + +/* Allocate extra slots for virEventPollHandle/virEventPollTimeout + records in this multiple */ +#define EVENT_ALLOC_EXTENT 10 + +/* State for the main event loop */ +struct virEventPollLoop { + virMutex lock; + int running; + virThread leader; + int wakeupfd[2]; + size_t handlesCount; + size_t handlesAlloc; + struct virEventPollHandle *handles; + size_t timeoutsCount; + size_t timeoutsAlloc; + struct virEventPollTimeout *timeouts; +}; + +/* Only have one event loop */ +extern struct virEventPollLoop eventLoop; + +/* Unique ID for the next FD watch to be registered */ +extern int nextWatch; + +/* Unique ID for the next timer to be registered */ +extern int nextTimer; + +int virEventPollAddHandleInternal(int watch, int fd, int nativeevents); +int virEventPollUpdateHandleInternal(int watch, int fd, int nativeevents); +int virEventPollRemoveHandleInternal(int watch, int fd); +int virEventPollInitInternal(void); +void virEventPollDeinitInternal(void); +int virEventPollWait(int timeout, void **opaque); +void virEventPollOpaqueFree(void *opaque); +int VirWokenFD(void *opaque, size_t n); +int VirWokenEvents(void *opaque, size_t n); + +#endif /* __VIR_EVENT_POLL_INTERNAL_H__ */ -- 1.9.5.msysgit.0

From: Derbyshev Dmitry <dderbyshev@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@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

Ping. Also, noticed that I renamed patch series, old name was "Substitute poll by epoll in virEventPollRunOnce". My bad. /14/2017 9:04 PM, Derbyshev Dmitriy пишет:
From: Derbyshev Dmitry <dderbyshev@virtuozzo.com>
Provides about 20% boost on local machine with 35 vms. virEventPollDispatchHandles can also be split to pass cb via epoll data field.
Should start sending as PATCH instead?
Changes since v1: * ifdef supstituded by 2 .c files with vireventpollinternal.h implementations * PROBE purged
Derbyshev Dmitry (2): vireventpoll: isolate common code vireventpoll implimentation using epoll
configure.ac | 28 + src/Makefile.am | 12 +- src/util/vireventepoll.c | 201 +++++++ src/util/vireventpoll.c | 700 ++-------------------- src/util/{vireventpoll.c => vireventpollcommon.c} | 231 ++----- src/util/vireventpollinternal.h | 91 +++ tests/commanddata/{test14.log => test3epoll.log} | 2 + tests/commandtest.c | 4 + 8 files changed, 451 insertions(+), 818 deletions(-) create mode 100644 src/util/vireventepoll.c copy src/util/{vireventpoll.c => vireventpollcommon.c} (78%) create mode 100644 src/util/vireventpollinternal.h copy tests/commanddata/{test14.log => test3epoll.log} (94%)

22-Feb-17 17:35, Dmitry Derbyshev пишет:
Ping. Also, noticed that I renamed patch series, old name was "Substitute poll by epoll in virEventPollRunOnce". My bad.
A few issues required to be fixed to make 'make syntax-check' pass. Otherwise looks good to me. Maxim
/14/2017 9:04 PM, Derbyshev Dmitriy пишет:
From: Derbyshev Dmitry <dderbyshev@virtuozzo.com>
Provides about 20% boost on local machine with 35 vms. virEventPollDispatchHandles can also be split to pass cb via epoll data field.
Should start sending as PATCH instead?
Changes since v1: * ifdef supstituded by 2 .c files with vireventpollinternal.h implementations * PROBE purged
Derbyshev Dmitry (2): vireventpoll: isolate common code vireventpoll implimentation using epoll
configure.ac | 28 + src/Makefile.am | 12 +- src/util/vireventepoll.c | 201 +++++++ src/util/vireventpoll.c | 700 ++-------------------- src/util/{vireventpoll.c => vireventpollcommon.c} | 231 ++----- src/util/vireventpollinternal.h | 91 +++ tests/commanddata/{test14.log => test3epoll.log} | 2 + tests/commandtest.c | 4 + 8 files changed, 451 insertions(+), 818 deletions(-) create mode 100644 src/util/vireventepoll.c copy src/util/{vireventpoll.c => vireventpollcommon.c} (78%) create mode 100644 src/util/vireventpollinternal.h copy tests/commanddata/{test14.log => test3epoll.log} (94%)
-- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
participants (3)
-
Derbyshev Dmitriy
-
Dmitry Derbyshev
-
Maxim Nestratov