]> git.ipfire.org Git - thirdparty/hostap.git/blobdiff - src/utils/eloop.c
tests: HE AP parameters
[thirdparty/hostap.git] / src / utils / eloop.c
index 2667c8c9abfc30e098dac88dd858157f9b609907..bb375be1095e6a95d9e9c808342fd4e34bf2806f 100644 (file)
 #include "list.h"
 #include "eloop.h"
 
+#if defined(CONFIG_ELOOP_POLL) && defined(CONFIG_ELOOP_EPOLL)
+#error Do not define both of poll and epoll
+#endif
+
+#if defined(CONFIG_ELOOP_POLL) && defined(CONFIG_ELOOP_KQUEUE)
+#error Do not define both of poll and kqueue
+#endif
+
+#if !defined(CONFIG_ELOOP_POLL) && !defined(CONFIG_ELOOP_EPOLL) && \
+    !defined(CONFIG_ELOOP_KQUEUE)
+#define CONFIG_ELOOP_SELECT
+#endif
+
 #ifdef CONFIG_ELOOP_POLL
 #include <poll.h>
 #endif /* CONFIG_ELOOP_POLL */
 
+#ifdef CONFIG_ELOOP_EPOLL
+#include <sys/epoll.h>
+#endif /* CONFIG_ELOOP_EPOLL */
+
+#ifdef CONFIG_ELOOP_KQUEUE
+#include <sys/event.h>
+#endif /* CONFIG_ELOOP_KQUEUE */
 
 struct eloop_sock {
        int sock;
@@ -50,6 +70,7 @@ struct eloop_signal {
 struct eloop_sock_table {
        int count;
        struct eloop_sock *table;
+       eloop_event_type type;
        int changed;
 };
 
@@ -63,6 +84,20 @@ struct eloop_data {
        struct pollfd *pollfds;
        struct pollfd **pollfds_map;
 #endif /* CONFIG_ELOOP_POLL */
+#if defined(CONFIG_ELOOP_EPOLL) || defined(CONFIG_ELOOP_KQUEUE)
+       int max_fd;
+       struct eloop_sock *fd_table;
+#endif /* CONFIG_ELOOP_EPOLL || CONFIG_ELOOP_KQUEUE */
+#ifdef CONFIG_ELOOP_EPOLL
+       int epollfd;
+       int epoll_max_event_num;
+       struct epoll_event *epoll_events;
+#endif /* CONFIG_ELOOP_EPOLL */
+#ifdef CONFIG_ELOOP_KQUEUE
+       int kqueuefd;
+       int kqueue_nevents;
+       struct kevent *kqueue_events;
+#endif /* CONFIG_ELOOP_KQUEUE */
        struct eloop_sock_table readers;
        struct eloop_sock_table writers;
        struct eloop_sock_table exceptions;
@@ -75,7 +110,6 @@ struct eloop_data {
        int pending_terminate;
 
        int terminate;
-       int reader_table_changed;
 };
 
 static struct eloop_data eloop;
@@ -128,6 +162,27 @@ int eloop_init(void)
 {
        os_memset(&eloop, 0, sizeof(eloop));
        dl_list_init(&eloop.timeout);
+#ifdef CONFIG_ELOOP_EPOLL
+       eloop.epollfd = epoll_create1(0);
+       if (eloop.epollfd < 0) {
+               wpa_printf(MSG_ERROR, "%s: epoll_create1 failed. %s",
+                          __func__, strerror(errno));
+               return -1;
+       }
+#endif /* CONFIG_ELOOP_EPOLL */
+#ifdef CONFIG_ELOOP_KQUEUE
+       eloop.kqueuefd = kqueue();
+       if (eloop.kqueuefd < 0) {
+               wpa_printf(MSG_ERROR, "%s: kqueue failed: %s",
+                          __func__, strerror(errno));
+               return -1;
+       }
+#endif /* CONFIG_ELOOP_KQUEUE */
+#if defined(CONFIG_ELOOP_EPOLL) || defined(CONFIG_ELOOP_KQUEUE)
+       eloop.readers.type = EVENT_TYPE_READ;
+       eloop.writers.type = EVENT_TYPE_WRITE;
+       eloop.exceptions.type = EVENT_TYPE_EXCEPTION;
+#endif /* CONFIG_ELOOP_EPOLL || CONFIG_ELOOP_KQUEUE */
 #ifdef WPA_TRACE
        signal(SIGSEGV, eloop_sigsegv_handler);
 #endif /* WPA_TRACE */
@@ -135,10 +190,84 @@ int eloop_init(void)
 }
 
 
+#ifdef CONFIG_ELOOP_EPOLL
+static int eloop_sock_queue(int sock, eloop_event_type type)
+{
+       struct epoll_event ev;
+
+       os_memset(&ev, 0, sizeof(ev));
+       switch (type) {
+       case EVENT_TYPE_READ:
+               ev.events = EPOLLIN;
+               break;
+       case EVENT_TYPE_WRITE:
+               ev.events = EPOLLOUT;
+               break;
+       /*
+        * Exceptions are always checked when using epoll, but I suppose it's
+        * possible that someone registered a socket *only* for exception
+        * handling.
+        */
+       case EVENT_TYPE_EXCEPTION:
+               ev.events = EPOLLERR | EPOLLHUP;
+               break;
+       }
+       ev.data.fd = sock;
+       if (epoll_ctl(eloop.epollfd, EPOLL_CTL_ADD, sock, &ev) < 0) {
+               wpa_printf(MSG_ERROR, "%s: epoll_ctl(ADD) for fd=%d failed: %s",
+                          __func__, sock, strerror(errno));
+               return -1;
+       }
+       return 0;
+}
+#endif /* CONFIG_ELOOP_EPOLL */
+
+
+#ifdef CONFIG_ELOOP_KQUEUE
+
+static short event_type_kevent_filter(eloop_event_type type)
+{
+       switch (type) {
+       case EVENT_TYPE_READ:
+               return EVFILT_READ;
+       case EVENT_TYPE_WRITE:
+               return EVFILT_WRITE;
+       default:
+               return 0;
+       }
+}
+
+
+static int eloop_sock_queue(int sock, eloop_event_type type)
+{
+       struct kevent ke;
+
+       EV_SET(&ke, sock, event_type_kevent_filter(type), EV_ADD, 0, 0, 0);
+       if (kevent(eloop.kqueuefd, &ke, 1, NULL, 0, NULL) == -1) {
+               wpa_printf(MSG_ERROR, "%s: kevent(ADD) for fd=%d failed: %s",
+                          __func__, sock, strerror(errno));
+               return -1;
+       }
+       return 0;
+}
+
+#endif /* CONFIG_ELOOP_KQUEUE */
+
+
 static int eloop_sock_table_add_sock(struct eloop_sock_table *table,
                                      int sock, eloop_sock_handler handler,
                                      void *eloop_data, void *user_data)
 {
+#ifdef CONFIG_ELOOP_EPOLL
+       struct epoll_event *temp_events;
+#endif /* CONFIG_ELOOP_EPOLL */
+#ifdef CONFIG_ELOOP_KQUEUE
+       struct kevent *temp_events;
+#endif /* CONFIG_ELOOP_EPOLL */
+#if defined(CONFIG_ELOOP_EPOLL) || defined(CONFIG_ELOOP_KQUEUE)
+       struct eloop_sock *temp_table;
+       int next;
+#endif /* CONFIG_ELOOP_EPOLL || CONFIG_ELOOP_KQUEUE */
        struct eloop_sock *tmp;
        int new_max_sock;
 
@@ -174,12 +303,59 @@ static int eloop_sock_table_add_sock(struct eloop_sock_table *table,
                eloop.pollfds = n;
        }
 #endif /* CONFIG_ELOOP_POLL */
+#if defined(CONFIG_ELOOP_EPOLL) || defined(CONFIG_ELOOP_KQUEUE)
+       if (new_max_sock >= eloop.max_fd) {
+               next = new_max_sock + 16;
+               temp_table = os_realloc_array(eloop.fd_table, next,
+                                             sizeof(struct eloop_sock));
+               if (temp_table == NULL)
+                       return -1;
+
+               eloop.max_fd = next;
+               eloop.fd_table = temp_table;
+       }
+#endif /* CONFIG_ELOOP_EPOLL || CONFIG_ELOOP_KQUEUE */
+
+#ifdef CONFIG_ELOOP_EPOLL
+       if (eloop.count + 1 > eloop.epoll_max_event_num) {
+               next = eloop.epoll_max_event_num == 0 ? 8 :
+                       eloop.epoll_max_event_num * 2;
+               temp_events = os_realloc_array(eloop.epoll_events, next,
+                                              sizeof(struct epoll_event));
+               if (temp_events == NULL) {
+                       wpa_printf(MSG_ERROR, "%s: malloc for epoll failed: %s",
+                                  __func__, strerror(errno));
+                       return -1;
+               }
+
+               eloop.epoll_max_event_num = next;
+               eloop.epoll_events = temp_events;
+       }
+#endif /* CONFIG_ELOOP_EPOLL */
+#ifdef CONFIG_ELOOP_KQUEUE
+       if (eloop.count + 1 > eloop.kqueue_nevents) {
+               next = eloop.kqueue_nevents == 0 ? 8 : eloop.kqueue_nevents * 2;
+               temp_events = os_malloc(next * sizeof(*temp_events));
+               if (!temp_events) {
+                       wpa_printf(MSG_ERROR,
+                                  "%s: malloc for kqueue failed: %s",
+                                  __func__, strerror(errno));
+                       return -1;
+               }
+
+               os_free(eloop.kqueue_events);
+               eloop.kqueue_events = temp_events;
+               eloop.kqueue_nevents = next;
+       }
+#endif /* CONFIG_ELOOP_KQUEUE */
 
        eloop_trace_sock_remove_ref(table);
        tmp = os_realloc_array(table->table, table->count + 1,
                               sizeof(struct eloop_sock));
-       if (tmp == NULL)
+       if (tmp == NULL) {
+               eloop_trace_sock_add_ref(table);
                return -1;
+       }
 
        tmp[table->count].sock = sock;
        tmp[table->count].eloop_data = eloop_data;
@@ -193,6 +369,12 @@ static int eloop_sock_table_add_sock(struct eloop_sock_table *table,
        table->changed = 1;
        eloop_trace_sock_add_ref(table);
 
+#if defined(CONFIG_ELOOP_EPOLL) || defined(CONFIG_ELOOP_KQUEUE)
+       if (eloop_sock_queue(sock, table->type) < 0)
+               return -1;
+       os_memcpy(&eloop.fd_table[sock], &table->table[table->count - 1],
+                 sizeof(struct eloop_sock));
+#endif /* CONFIG_ELOOP_EPOLL || CONFIG_ELOOP_KQUEUE */
        return 0;
 }
 
@@ -200,6 +382,9 @@ static int eloop_sock_table_add_sock(struct eloop_sock_table *table,
 static void eloop_sock_table_remove_sock(struct eloop_sock_table *table,
                                          int sock)
 {
+#ifdef CONFIG_ELOOP_KQUEUE
+       struct kevent ke;
+#endif /* CONFIG_ELOOP_KQUEUE */
        int i;
 
        if (table == NULL || table->table == NULL || table->count == 0)
@@ -221,6 +406,24 @@ static void eloop_sock_table_remove_sock(struct eloop_sock_table *table,
        eloop.count--;
        table->changed = 1;
        eloop_trace_sock_add_ref(table);
+#ifdef CONFIG_ELOOP_EPOLL
+       if (epoll_ctl(eloop.epollfd, EPOLL_CTL_DEL, sock, NULL) < 0) {
+               wpa_printf(MSG_ERROR, "%s: epoll_ctl(DEL) for fd=%d failed: %s",
+                          __func__, sock, strerror(errno));
+               return;
+       }
+       os_memset(&eloop.fd_table[sock], 0, sizeof(struct eloop_sock));
+#endif /* CONFIG_ELOOP_EPOLL */
+#ifdef CONFIG_ELOOP_KQUEUE
+       EV_SET(&ke, sock, event_type_kevent_filter(table->type), EV_DELETE, 0,
+              0, 0);
+       if (kevent(eloop.kqueuefd, &ke, 1, NULL, 0, NULL) < 0) {
+               wpa_printf(MSG_ERROR, "%s: kevent(DEL) for fd=%d failed: %s",
+                          __func__, sock, strerror(errno));
+               return;
+       }
+       os_memset(&eloop.fd_table[sock], 0, sizeof(struct eloop_sock));
+#endif /* CONFIG_ELOOP_KQUEUE */
 }
 
 
@@ -362,7 +565,9 @@ static void eloop_sock_table_dispatch(struct eloop_sock_table *readers,
                                        max_pollfd_map, POLLERR | POLLHUP);
 }
 
-#else /* CONFIG_ELOOP_POLL */
+#endif /* CONFIG_ELOOP_POLL */
+
+#ifdef CONFIG_ELOOP_SELECT
 
 static void eloop_sock_table_set_fds(struct eloop_sock_table *table,
                                     fd_set *fds)
@@ -401,7 +606,89 @@ static void eloop_sock_table_dispatch(struct eloop_sock_table *table,
        }
 }
 
-#endif /* CONFIG_ELOOP_POLL */
+#endif /* CONFIG_ELOOP_SELECT */
+
+
+#ifdef CONFIG_ELOOP_EPOLL
+static void eloop_sock_table_dispatch(struct epoll_event *events, int nfds)
+{
+       struct eloop_sock *table;
+       int i;
+
+       for (i = 0; i < nfds; i++) {
+               table = &eloop.fd_table[events[i].data.fd];
+               if (table->handler == NULL)
+                       continue;
+               table->handler(table->sock, table->eloop_data,
+                              table->user_data);
+               if (eloop.readers.changed ||
+                   eloop.writers.changed ||
+                   eloop.exceptions.changed)
+                       break;
+       }
+}
+#endif /* CONFIG_ELOOP_EPOLL */
+
+
+#ifdef CONFIG_ELOOP_KQUEUE
+
+static void eloop_sock_table_dispatch(struct kevent *events, int nfds)
+{
+       struct eloop_sock *table;
+       int i;
+
+       for (i = 0; i < nfds; i++) {
+               table = &eloop.fd_table[events[i].ident];
+               if (table->handler == NULL)
+                       continue;
+               table->handler(table->sock, table->eloop_data,
+                              table->user_data);
+               if (eloop.readers.changed ||
+                   eloop.writers.changed ||
+                   eloop.exceptions.changed)
+                       break;
+       }
+}
+
+
+static int eloop_sock_table_requeue(struct eloop_sock_table *table)
+{
+       int i, r;
+
+       r = 0;
+       for (i = 0; i < table->count && table->table; i++) {
+               if (eloop_sock_queue(table->table[i].sock, table->type) == -1)
+                       r = -1;
+       }
+       return r;
+}
+
+#endif /* CONFIG_ELOOP_KQUEUE */
+
+
+int eloop_sock_requeue(void)
+{
+       int r = 0;
+
+#ifdef CONFIG_ELOOP_KQUEUE
+       close(eloop.kqueuefd);
+       eloop.kqueuefd = kqueue();
+       if (eloop.kqueuefd < 0) {
+               wpa_printf(MSG_ERROR, "%s: kqueue failed: %s",
+                          __func__, strerror(errno));
+               return -1;
+       }
+
+       if (eloop_sock_table_requeue(&eloop.readers) < 0)
+               r = -1;
+       if (eloop_sock_table_requeue(&eloop.writers) < 0)
+               r = -1;
+       if (eloop_sock_table_requeue(&eloop.exceptions) < 0)
+               r = -1;
+#endif /* CONFIG_ELOOP_KQUEUE */
+
+       return r;
+}
 
 
 static void eloop_sock_table_destroy(struct eloop_sock_table *table)
@@ -776,25 +1063,46 @@ void eloop_run(void)
 #ifdef CONFIG_ELOOP_POLL
        int num_poll_fds;
        int timeout_ms = 0;
-#else /* CONFIG_ELOOP_POLL */
+#endif /* CONFIG_ELOOP_POLL */
+#ifdef CONFIG_ELOOP_SELECT
        fd_set *rfds, *wfds, *efds;
        struct timeval _tv;
-#endif /* CONFIG_ELOOP_POLL */
+#endif /* CONFIG_ELOOP_SELECT */
+#ifdef CONFIG_ELOOP_EPOLL
+       int timeout_ms = -1;
+#endif /* CONFIG_ELOOP_EPOLL */
+#ifdef CONFIG_ELOOP_KQUEUE
+       struct timespec ts;
+#endif /* CONFIG_ELOOP_KQUEUE */
        int res;
        struct os_reltime tv, now;
 
-#ifndef CONFIG_ELOOP_POLL
+#ifdef CONFIG_ELOOP_SELECT
        rfds = os_malloc(sizeof(*rfds));
        wfds = os_malloc(sizeof(*wfds));
        efds = os_malloc(sizeof(*efds));
        if (rfds == NULL || wfds == NULL || efds == NULL)
                goto out;
-#endif /* CONFIG_ELOOP_POLL */
+#endif /* CONFIG_ELOOP_SELECT */
 
        while (!eloop.terminate &&
               (!dl_list_empty(&eloop.timeout) || eloop.readers.count > 0 ||
                eloop.writers.count > 0 || eloop.exceptions.count > 0)) {
                struct eloop_timeout *timeout;
+
+               if (eloop.pending_terminate) {
+                       /*
+                        * This may happen in some corner cases where a signal
+                        * is received during a blocking operation. We need to
+                        * process the pending signals and exit if requested to
+                        * avoid hitting the SIGALRM limit if the blocking
+                        * operation took more than two seconds.
+                        */
+                       eloop_process_pending_signals();
+                       if (eloop.terminate)
+                               break;
+               }
+
                timeout = dl_list_first(&eloop.timeout, struct eloop_timeout,
                                        list);
                if (timeout) {
@@ -803,12 +1111,17 @@ void eloop_run(void)
                                os_reltime_sub(&timeout->time, &now, &tv);
                        else
                                tv.sec = tv.usec = 0;
-#ifdef CONFIG_ELOOP_POLL
+#if defined(CONFIG_ELOOP_POLL) || defined(CONFIG_ELOOP_EPOLL)
                        timeout_ms = tv.sec * 1000 + tv.usec / 1000;
-#else /* CONFIG_ELOOP_POLL */
+#endif /* defined(CONFIG_ELOOP_POLL) || defined(CONFIG_ELOOP_EPOLL) */
+#ifdef CONFIG_ELOOP_SELECT
                        _tv.tv_sec = tv.sec;
                        _tv.tv_usec = tv.usec;
-#endif /* CONFIG_ELOOP_POLL */
+#endif /* CONFIG_ELOOP_SELECT */
+#ifdef CONFIG_ELOOP_KQUEUE
+                       ts.tv_sec = tv.sec;
+                       ts.tv_nsec = tv.usec * 1000L;
+#endif /* CONFIG_ELOOP_KQUEUE */
                }
 
 #ifdef CONFIG_ELOOP_POLL
@@ -818,26 +1131,57 @@ void eloop_run(void)
                        eloop.max_pollfd_map);
                res = poll(eloop.pollfds, num_poll_fds,
                           timeout ? timeout_ms : -1);
-
-               if (res < 0 && errno != EINTR && errno != 0) {
-                       wpa_printf(MSG_INFO, "eloop: poll: %s",
-                                  strerror(errno));
-                       goto out;
-               }
-#else /* CONFIG_ELOOP_POLL */
+#endif /* CONFIG_ELOOP_POLL */
+#ifdef CONFIG_ELOOP_SELECT
                eloop_sock_table_set_fds(&eloop.readers, rfds);
                eloop_sock_table_set_fds(&eloop.writers, wfds);
                eloop_sock_table_set_fds(&eloop.exceptions, efds);
                res = select(eloop.max_sock + 1, rfds, wfds, efds,
                             timeout ? &_tv : NULL);
+#endif /* CONFIG_ELOOP_SELECT */
+#ifdef CONFIG_ELOOP_EPOLL
+               if (eloop.count == 0) {
+                       res = 0;
+               } else {
+                       res = epoll_wait(eloop.epollfd, eloop.epoll_events,
+                                        eloop.count, timeout_ms);
+               }
+#endif /* CONFIG_ELOOP_EPOLL */
+#ifdef CONFIG_ELOOP_KQUEUE
+               if (eloop.count == 0) {
+                       res = 0;
+               } else {
+                       res = kevent(eloop.kqueuefd, NULL, 0,
+                                    eloop.kqueue_events, eloop.kqueue_nevents,
+                                    timeout ? &ts : NULL);
+               }
+#endif /* CONFIG_ELOOP_KQUEUE */
                if (res < 0 && errno != EINTR && errno != 0) {
-                       wpa_printf(MSG_INFO, "eloop: select: %s",
-                                  strerror(errno));
+                       wpa_printf(MSG_ERROR, "eloop: %s: %s",
+#ifdef CONFIG_ELOOP_POLL
+                                  "poll"
+#endif /* CONFIG_ELOOP_POLL */
+#ifdef CONFIG_ELOOP_SELECT
+                                  "select"
+#endif /* CONFIG_ELOOP_SELECT */
+#ifdef CONFIG_ELOOP_EPOLL
+                                  "epoll"
+#endif /* CONFIG_ELOOP_EPOLL */
+#ifdef CONFIG_ELOOP_KQUEUE
+                                  "kqueue"
+#endif /* CONFIG_ELOOP_EKQUEUE */
+
+                                  , strerror(errno));
                        goto out;
                }
-#endif /* CONFIG_ELOOP_POLL */
+
+               eloop.readers.changed = 0;
+               eloop.writers.changed = 0;
+               eloop.exceptions.changed = 0;
+
                eloop_process_pending_signals();
 
+
                /* check if some registered timeouts have occurred */
                timeout = dl_list_first(&eloop.timeout, struct eloop_timeout,
                                        list);
@@ -857,24 +1201,44 @@ void eloop_run(void)
                if (res <= 0)
                        continue;
 
+               if (eloop.readers.changed ||
+                   eloop.writers.changed ||
+                   eloop.exceptions.changed) {
+                        /*
+                         * Sockets may have been closed and reopened with the
+                         * same FD in the signal or timeout handlers, so we
+                         * must skip the previous results and check again
+                         * whether any of the currently registered sockets have
+                         * events.
+                         */
+                       continue;
+               }
+
 #ifdef CONFIG_ELOOP_POLL
                eloop_sock_table_dispatch(&eloop.readers, &eloop.writers,
                                          &eloop.exceptions, eloop.pollfds_map,
                                          eloop.max_pollfd_map);
-#else /* CONFIG_ELOOP_POLL */
+#endif /* CONFIG_ELOOP_POLL */
+#ifdef CONFIG_ELOOP_SELECT
                eloop_sock_table_dispatch(&eloop.readers, rfds);
                eloop_sock_table_dispatch(&eloop.writers, wfds);
                eloop_sock_table_dispatch(&eloop.exceptions, efds);
-#endif /* CONFIG_ELOOP_POLL */
+#endif /* CONFIG_ELOOP_SELECT */
+#ifdef CONFIG_ELOOP_EPOLL
+               eloop_sock_table_dispatch(eloop.epoll_events, res);
+#endif /* CONFIG_ELOOP_EPOLL */
+#ifdef CONFIG_ELOOP_KQUEUE
+               eloop_sock_table_dispatch(eloop.kqueue_events, res);
+#endif /* CONFIG_ELOOP_KQUEUE */
        }
 
        eloop.terminate = 0;
 out:
-#ifndef CONFIG_ELOOP_POLL
+#ifdef CONFIG_ELOOP_SELECT
        os_free(rfds);
        os_free(wfds);
        os_free(efds);
-#endif /* CONFIG_ELOOP_POLL */
+#endif /* CONFIG_ELOOP_SELECT */
        return;
 }
 
@@ -918,12 +1282,23 @@ void eloop_destroy(void)
        os_free(eloop.pollfds);
        os_free(eloop.pollfds_map);
 #endif /* CONFIG_ELOOP_POLL */
+#if defined(CONFIG_ELOOP_EPOLL) || defined(CONFIG_ELOOP_KQUEUE)
+       os_free(eloop.fd_table);
+#endif /* CONFIG_ELOOP_EPOLL || CONFIG_ELOOP_KQUEUE */
+#ifdef CONFIG_ELOOP_EPOLL
+       os_free(eloop.epoll_events);
+       close(eloop.epollfd);
+#endif /* CONFIG_ELOOP_EPOLL */
+#ifdef CONFIG_ELOOP_KQUEUE
+       os_free(eloop.kqueue_events);
+       close(eloop.kqueuefd);
+#endif /* CONFIG_ELOOP_KQUEUE */
 }
 
 
 int eloop_terminated(void)
 {
-       return eloop.terminate;
+       return eloop.terminate || eloop.pending_terminate;
 }
 
 
@@ -940,7 +1315,13 @@ void eloop_wait_for_read_sock(int sock)
        pfd.events = POLLIN;
 
        poll(&pfd, 1, -1);
-#else /* CONFIG_ELOOP_POLL */
+#endif /* CONFIG_ELOOP_POLL */
+#if defined(CONFIG_ELOOP_SELECT) || defined(CONFIG_ELOOP_EPOLL)
+       /*
+        * We can use epoll() here. But epoll() requres 4 system calls.
+        * epoll_create1(), epoll_ctl() for ADD, epoll_wait, and close() for
+        * epoll fd. So select() is better for performance here.
+        */
        fd_set rfds;
 
        if (sock < 0)
@@ -949,5 +1330,20 @@ void eloop_wait_for_read_sock(int sock)
        FD_ZERO(&rfds);
        FD_SET(sock, &rfds);
        select(sock + 1, &rfds, NULL, NULL, NULL);
-#endif /* CONFIG_ELOOP_POLL */
+#endif /* defined(CONFIG_ELOOP_SELECT) || defined(CONFIG_ELOOP_EPOLL) */
+#ifdef CONFIG_ELOOP_KQUEUE
+       int kfd;
+       struct kevent ke1, ke2;
+
+       kfd = kqueue();
+       if (kfd == -1)
+               return;
+       EV_SET(&ke1, sock, EVFILT_READ, EV_ADD | EV_ONESHOT, 0, 0, 0);
+       kevent(kfd, &ke1, 1, &ke2, 1, NULL);
+       close(kfd);
+#endif /* CONFIG_ELOOP_KQUEUE */
 }
+
+#ifdef CONFIG_ELOOP_SELECT
+#undef CONFIG_ELOOP_SELECT
+#endif /* CONFIG_ELOOP_SELECT */