changeset 2980:c66ed22ed0d6 draft

Support epoll(7)
author Roy Marples <roy@marples.name>
date Wed, 04 Mar 2015 11:51:55 +0000
parents d7169e1212f1
children 17ce5dad11dc
files configure eloop.c eloop.h
diffstat 3 files changed, 153 insertions(+), 50 deletions(-) [+]
line wrap: on
line diff
--- a/configure	Wed Mar 04 11:41:53 2015 +0000
+++ b/configure	Wed Mar 04 11:51:55 2015 +0000
@@ -24,6 +24,7 @@
 INCLUDEDIR=
 DEVS=
 EMBEDDED=
+POLL=
 
 for x do
 	opt=${x%%=*}
@@ -66,13 +67,12 @@
 	--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 @@
 	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 @@
 }
 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 @@
 }
 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
--- a/eloop.c	Wed Mar 04 11:41:53 2015 +0000
+++ b/eloop.c	Wed Mar 04 11:51:55 2015 +0000
@@ -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"
@@ -42,6 +42,11 @@
 #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 @@
 		i++;
 	}
 }
+#endif
 
 int
 eloop_event_add(struct eloop_ctx *ctx, int fd,
@@ -68,7 +74,19 @@
     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 @@
 				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 @@
 		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 @@
 	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 @@
 				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 @@
 		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 @@
 	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 @@
 			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 @@
 		    (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 @@
 		}
 
 		/* 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 @@
 				}
 			}
 		}
+#endif
 	}
 
 	return ctx->exitcode;
--- a/eloop.h	Wed Mar 04 11:41:53 2015 +0000
+++ b/eloop.h	Wed Mar 04 11:51:55 2015 +0000
@@ -30,6 +30,8 @@
 
 #include <time.h>
 
+#include "config.h"
+
 #ifndef ELOOP_QUEUE
   #define ELOOP_QUEUE 1
 #endif
@@ -45,7 +47,9 @@
 	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 @@
 	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;