]> git.ipfire.org Git - thirdparty/dhcpcd.git/commitdiff
Support epoll(7)
authorRoy Marples <roy@marples.name>
Wed, 4 Mar 2015 11:51:55 +0000 (11:51 +0000)
committerRoy Marples <roy@marples.name>
Wed, 4 Mar 2015 11:51:55 +0000 (11:51 +0000)
configure
eloop.c
eloop.h

index cf91a1711d67fbb38eeba234f183a9edcc712777..697e368cd3481aae668b4cd24041347c4de5bac0 100755 (executable)
--- a/configure
+++ b/configure
@@ -24,6 +24,7 @@ STATIC=
 INCLUDEDIR=
 DEVS=
 EMBEDDED=
+POLL=
 
 for x do
        opt=${x%%=*}
@@ -66,13 +67,12 @@ for x do
        --without-getline) GETLINE=no;;
        --without-strlcpy) STRLCPY=no;;
         --without-posix_spawn) POSIX_SPAWN=no;;
-       --without-pollts) POLLTS=no;;
-       --with-pollts) POLLTS=$var;;
        --without-md5) MD5=no;;
        --without-sha2) SHA2=no;;
        --without-sha256) SHA2=no;;
        --without-dev) DEV=no;;
        --without-udev) UDEV=no;;
+       --with-poll) POLL="$var";;
        --serviceexists) SERVICEEXISTS=$var;;
        --servicecmd) SERVICECMD=$var;;
        --servicestatus) SERVICESTATUS=$var;;
@@ -789,27 +789,34 @@ else
        echo "#define HAVE_SPAWN_H" >>$CONFIG_H
 fi
 
-if [ -z "$POLLTS" ]; then
-       printf "Testing for pollts ... "
-       cat <<EOF >_pollts.c
-#include <poll.h>
-#include <signal.h>
-#include <stdlib.h>
-#include <time.h>
+if [ -z "$POLL" ]; then
+       printf "Testing for epoll ... "
+       cat <<EOF >_epoll.c
+#ifdef __linux__
+#include <linux/version.h>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 37)
+#error kernel has buggy epoll_wait timeout
+#endif
+#endif
+
+#include <sys/epoll.h>
+#include <unistd.h>
 int main(void) {
-       pollts(NULL, 0, NULL, NULL);
+       epoll_create1(EPOLL_CLOEXEC);
+       epoll_pwait(-1, NULL, 0, 0, NULL);
        return 0;
 }
 EOF
-       if $XCC _pollts.c -o _pollts 2>&3; then
-               POLLTS=yes
+       if $XCC _epoll.c -o _epoll 2>&3; then
+               POLL=epoll
+               echo "#define HAVE_EPOLL" >>$CONFIG_MK
+               echo "yes"
        else
-               POLLTS=no
+               echo "no"
        fi
-       echo "$POLLTS"
-       rm -f _pollts.c _pollts
+       rm -f _epoll.c _epoll
 fi
-if [ "$POLLTS" = no ]; then
+if [ -z "$POLL" ]; then
        printf "Testing for ppoll ... "
        cat <<EOF >_ppoll.c
 #include <poll.h>
@@ -820,15 +827,14 @@ int main(void) {
 }
 EOF
        if $XCC _ppoll.c -o _ppoll 2>&3; then
-               POLLTS=ppoll
-               echo "yes"
-       else
-               POLLTS=no
-               echo "no"
-       fi
+               POLL=ppoll
+               echo "yes"
+       else
+               echo "no"
+       fi
        rm -f _ppoll.c _ppoll
 fi
-if [ "$POLLTS" = no ]; then
+if [ -z "$POLL" ]; then
        printf "Testing for pselect ... "
        cat <<EOF >_pselect.c
 #include <sys/select.h>
@@ -839,16 +845,16 @@ int main(void) {
 }
 EOF
        if $XCC _pselect.c -o _pselect 2>&3; then
-               POLLTS=pselect
+               POLL=pselect
                echo "yes"
        else
-               POLLTS=no
                echo "no"
        fi
        rm -f _pselect.c _pselect
 fi
-case "$POLLTS" in
-yes)
+case "$POLL" in
+epoll)
+       echo "#define HAVE_EPOLL" >>$CONFIG_H
        ;;
 ppoll)
        echo "#define pollts            ppoll" >>$CONFIG_H
diff --git a/eloop.c b/eloop.c
index da4483601866c4d84283924a698da957fcd4868c..a209f089902a5851a0703771729ccdcae7c9c3b6 100644 (file)
--- a/eloop.c
+++ b/eloop.c
@@ -32,9 +32,9 @@
 
 #include <errno.h>
 #include <limits.h>
-#include <poll.h>
 #include <signal.h>
 #include <stdlib.h>
+#include <string.h>
 #include <syslog.h>
 
 #include "config.h"
 #include "dhcpcd.h"
 #include "eloop.h"
 
+#if defined(HAVE_EPOLL)
+#include <sys/epoll.h>
+#define eloop_event_setup_fds(ctx)
+#else
+#include <poll.h>
 static void
 eloop_event_setup_fds(struct eloop_ctx *ctx)
 {
@@ -61,6 +66,7 @@ eloop_event_setup_fds(struct eloop_ctx *ctx)
                i++;
        }
 }
+#endif
 
 int
 eloop_event_add(struct eloop_ctx *ctx, int fd,
@@ -68,7 +74,19 @@ eloop_event_add(struct eloop_ctx *ctx, int fd,
     void (*write_cb)(void *), void *write_cb_arg)
 {
        struct eloop_event *e;
+#ifdef HAVE_EPOLL
+       struct epoll_event epe, *nfds;
+#else
        struct pollfd *nfds;
+#endif
+
+#ifdef HAVE_EPOLL
+       memset(&epe, 0, sizeof(epe));
+       epe.data.fd = fd;
+       epe.events = EPOLLIN;
+       if (write_cb)
+               epe.events |= EPOLLOUT;
+#endif
 
        /* We should only have one callback monitoring the fd */
        TAILQ_FOREACH(e, &ctx->events, next) {
@@ -81,8 +99,14 @@ eloop_event_add(struct eloop_ctx *ctx, int fd,
                                e->write_cb = write_cb;
                                e->write_cb_arg = write_cb_arg;
                        }
+#ifdef HAVE_EPOLL
+                       epe.data.ptr = e;
+                       return epoll_ctl(ctx->epoll_fd, EPOLL_CTL_MOD,
+                           fd, &epe);
+#else
                        eloop_event_setup_fds(ctx);
                        return 0;
+#endif
                }
        }
 
@@ -91,34 +115,34 @@ eloop_event_add(struct eloop_ctx *ctx, int fd,
                TAILQ_REMOVE(&ctx->free_events, e, next);
        } else {
                e = malloc(sizeof(*e));
-               if (e == NULL) {
-                       syslog(LOG_ERR, "%s: %m", __func__);
-                       return -1;
-               }
+               if (e == NULL)
+                       goto err;
        }
 
        /* Ensure we can actually listen to it */
        ctx->events_len++;
        if (ctx->events_len > ctx->fds_len) {
+               nfds = realloc(ctx->fds, sizeof(*ctx->fds) * (ctx->fds_len+5));
+               if (nfds == NULL)
+                       goto err;
                ctx->fds_len += 5;
-               nfds = malloc(sizeof(*ctx->fds) * (ctx->fds_len + 5));
-               if (nfds == NULL) {
-                       syslog(LOG_ERR, "%s: %m", __func__);
-                       ctx->events_len--;
-                       TAILQ_INSERT_TAIL(&ctx->free_events, e, next);
-                       return -1;
-               }
-               ctx->fds_len += 5;
-               free(ctx->fds);
                ctx->fds = nfds;
        }
 
+
        /* Now populate the structure and add it to the list */
        e->fd = fd;
        e->read_cb = read_cb;
        e->read_cb_arg = read_cb_arg;
        e->write_cb = write_cb;
        e->write_cb_arg = write_cb_arg;
+
+#ifdef HAVE_EPOLL
+       epe.data.ptr = e;
+       if (epoll_ctl(ctx->epoll_fd, EPOLL_CTL_ADD, fd, &epe) == -1)
+               goto err;
+#endif
+
        /* The order of events should not matter.
         * However, some PPP servers love to close the link right after
         * sending their final message. So to ensure dhcpcd processes this
@@ -128,6 +152,14 @@ eloop_event_add(struct eloop_ctx *ctx, int fd,
        TAILQ_INSERT_HEAD(&ctx->events, e, next);
        eloop_event_setup_fds(ctx);
        return 0;
+
+err:
+       syslog(LOG_ERR, "%s: %m", __func__);
+       if (e) {
+               ctx->events_len--;
+               TAILQ_INSERT_TAIL(&ctx->free_events, e, next);
+       }
+       return -1;
 }
 
 void
@@ -142,6 +174,13 @@ eloop_event_delete(struct eloop_ctx *ctx, int fd, int write_only)
                                e->write_cb_arg = NULL;
                        } else {
                                TAILQ_REMOVE(&ctx->events, e, next);
+#ifdef HAVE_EPOLL
+                               /* NULL event is safe because we
+                                * rely on epoll_pwait which as added
+                                * after the delete without event was fixed. */
+                               epoll_ctl(ctx->epoll_fd, EPOLL_CTL_DEL,
+                                   fd, NULL);
+#endif
                                TAILQ_INSERT_TAIL(&ctx->free_events, e, next);
                                ctx->events_len--;
                        }
@@ -272,7 +311,15 @@ eloop_init(void)
                TAILQ_INIT(&ctx->timeouts);
                TAILQ_INIT(&ctx->free_timeouts);
                ctx->exitcode = EXIT_FAILURE;
+#ifdef HAVE_EPOLL
+               if ((ctx->epoll_fd = epoll_create1(EPOLL_CLOEXEC)) == -1) {
+                       free(ctx);
+                       return NULL;
+               }
+#endif
+
        }
+
        return ctx;
 }
 
@@ -314,9 +361,12 @@ eloop_start(struct dhcpcd_ctx *dctx)
        struct eloop_timeout *t;
        struct timespec now, ts, *tsp;
        void (*t0)(void *);
-#ifndef USE_SIGNALS
+#if defined(HAVE_EPOLL) || !defined(USE_SIGNALS)
        int timeout;
 #endif
+#ifdef HAVE_EPOLL
+       int i;
+#endif
 
        ctx = dctx->eloop;
        for (;;) {
@@ -349,10 +399,7 @@ eloop_start(struct dhcpcd_ctx *dctx)
                        break;
                }
 
-#ifdef USE_SIGNALS
-               n = pollts(ctx->fds, (nfds_t)ctx->events_len,
-                   tsp, &dctx->sigset);
-#else
+#if defined(HAVE_EPOLL) || !defined(USE_SIGNALS)
                if (tsp == NULL)
                        timeout = -1;
                else if (tsp->tv_sec > INT_MAX / 1000 ||
@@ -360,9 +407,25 @@ eloop_start(struct dhcpcd_ctx *dctx)
                    (tsp->tv_nsec + 999999) / 1000000 > INT_MAX % 1000000))
                        timeout = INT_MAX;
                else
-                       timeout = tsp->tv_sec * 1000 +
-                           (tsp->tv_nsec + 999999) / 1000000;
-               n = poll(ctx->fds, ctx->events_len, timeout);
+                       timeout = (int)(tsp->tv_sec * 1000 +
+                           (tsp->tv_nsec + 999999) / 1000000);
+#endif
+
+#ifdef HAVE_EPOLL
+#ifdef USE_SIGNALS
+               n = epoll_pwait(ctx->epoll_fd, ctx->fds, (int)ctx->events_len,
+                   timeout, &dctx->sigset);
+#else
+               n = epoll_wait(ctx->epoll_fd, ctx->fds, (int)ctx->events_len,
+                   timeout);
+#endif
+#else
+#ifdef USE_SIGNALS
+               n = pollts(ctx->fds, (nfds_t)ctx->events_len,
+                   tsp, &dctx->sigset);
+#else
+               n = poll(ctx->fds, (nfds_t)ctx->events_len, timeout);
+#endif
 #endif
                if (n == -1) {
                        if (errno == EINTR)
@@ -372,10 +435,34 @@ eloop_start(struct dhcpcd_ctx *dctx)
                }
 
                /* Process any triggered events. */
+#ifdef HAVE_EPOLL
+               for (i = 0; i < n; i++) {
+                       e = (struct eloop_event *)ctx->fds[i].data.ptr;
+                       if (ctx->fds[i].events & EPOLLOUT &&
+                           e->write_cb)
+                       {
+                               e->write_cb(e->write_cb_arg);
+                               /* We need to break here as the
+                                * callback could destroy the next
+                                * fd to process. */
+                               break;
+                       }
+                       if (ctx->fds[i].events &&
+                           ctx->fds[i].events &
+                           (EPOLLIN | EPOLLERR | EPOLLHUP))
+                       {
+                               e->read_cb(e->read_cb_arg);
+                               /* We need to break here as the
+                                * callback could destroy the next
+                                * fd to process. */
+                               break;
+                       }
+               }
+#else
                if (n > 0) {
                        TAILQ_FOREACH(e, &ctx->events, next) {
                                if (e->pollfd->revents & POLLOUT &&
-                                       e->write_cb)
+                                   e->write_cb)
                                {
                                        e->write_cb(e->write_cb_arg);
                                        /* We need to break here as the
@@ -392,6 +479,7 @@ eloop_start(struct dhcpcd_ctx *dctx)
                                }
                        }
                }
+#endif
        }
 
        return ctx->exitcode;
diff --git a/eloop.h b/eloop.h
index 29bf1fe17acb4323d3e25b55d942f372ffab19da..e906dc84cbb20756fba4cda259c35ae55fcb1945 100644 (file)
--- a/eloop.h
+++ b/eloop.h
@@ -30,6 +30,8 @@
 
 #include <time.h>
 
+#include "config.h"
+
 #ifndef ELOOP_QUEUE
   #define ELOOP_QUEUE 1
 #endif
@@ -45,7 +47,9 @@ struct eloop_event {
        void *read_cb_arg;
        void (*write_cb)(void *);
        void *write_cb_arg;
+#if !defined(HAVE_EPOLL)
        struct pollfd *pollfd;
+#endif
 };
 
 struct eloop_timeout {
@@ -67,7 +71,12 @@ struct eloop_ctx {
        void (*timeout0)(void *);
        void *timeout0_arg;
 
+#ifdef HAVE_EPOLL
+       int epoll_fd;
+       struct epoll_event *fds;
+#else
        struct pollfd *fds;
+#endif
        size_t fds_len;
 
        int exitnow;