Patchwork Add epoll option for better performance

login
register
mail settings
Submitter Masashi Honma
Date March 22, 2014, 6:17 a.m.
Message ID <1395469046-5933-1-git-send-email-masashi.honma@gmail.com>
Download mbox | patch
Permalink /patch/332796/
State Superseded
Headers show

Comments

Masashi Honma - March 22, 2014, 6:17 a.m.
This patch adds epoll option.

[merit]
See Table1.

Table1. comparison table
+--------+--------+-----------+------------+-------------+
|        | add fd | remove fd | prepare fd | dispatch fd |
+--------+--------+-----------+------------+-------------+
| select | O(1)   | O(1)      | O(N)       | O(N)        |
+--------+--------+-----------+------------+-------------+
| poll   | O(1)   | O(1)      | O(N)       | O(N)        |
+--------+--------+-----------+------------+-------------+
| epoll  | O(1)   | O(1)      | 0          | O(M)        |
+--------+--------+-----------+------------+-------------+
"add fd" is addition of fd by eloop_sock_table_add_sock().
"remove fd" is removal of fd by eloop_sock_table_remove_sock().
"prepare fd" is preparation of fds before wait in eloop_run().
"dispatch fd" is dispatchment of fds by eloop_sock_table_dispatch().
"N" is all watching fds.
"M" is fds which could be dispatched after waiting.

As shown in Table1, epoll option has better performance on "prepare fd" column.
Because select/poll option requires setting fds before every select()/poll().
But epoll_wait() doesn't need it.

And epoll option has also better performance on "dispatch fd" column.
Because select/poll option needs to check all registered fds to find out
dispatchable fds. But epoll option doesn't require checking all registered fds.
Because epoll_wait() returns dispatchable fd set.

So epoll option is effective for GO/AP functionality.

[demerit]
The epoll option requires additional heap memory. In case of P2P GO, it is
about 8K bytes.

Signed-off-by: Masashi Honma <masashi.honma@gmail.com>
---
 hostapd/Makefile              |   3 +
 src/utils/eloop.c             | 167 ++++++++++++++++++++++++++++++++++++++----
 wpa_supplicant/Android.mk     |   3 +
 wpa_supplicant/Makefile       |   3 +
 wpa_supplicant/android.config |   1 +
 wpa_supplicant/defconfig      |   1 +
 6 files changed, 164 insertions(+), 14 deletions(-)
Masashi Honma - March 31, 2014, 9:19 a.m.
One week has passed. Is there any comment for this patch ?

Regards,
Masashi Honma.


2014-03-22 15:17 GMT+09:00 Masashi Honma <masashi.honma@gmail.com>:
> This patch adds epoll option.
>
> [merit]
> See Table1.
>
> Table1. comparison table
> +--------+--------+-----------+------------+-------------+
> |        | add fd | remove fd | prepare fd | dispatch fd |
> +--------+--------+-----------+------------+-------------+
> | select | O(1)   | O(1)      | O(N)       | O(N)        |
> +--------+--------+-----------+------------+-------------+
> | poll   | O(1)   | O(1)      | O(N)       | O(N)        |
> +--------+--------+-----------+------------+-------------+
> | epoll  | O(1)   | O(1)      | 0          | O(M)        |
> +--------+--------+-----------+------------+-------------+
> "add fd" is addition of fd by eloop_sock_table_add_sock().
> "remove fd" is removal of fd by eloop_sock_table_remove_sock().
> "prepare fd" is preparation of fds before wait in eloop_run().
> "dispatch fd" is dispatchment of fds by eloop_sock_table_dispatch().
> "N" is all watching fds.
> "M" is fds which could be dispatched after waiting.
>
> As shown in Table1, epoll option has better performance on "prepare fd" column.
> Because select/poll option requires setting fds before every select()/poll().
> But epoll_wait() doesn't need it.
>
> And epoll option has also better performance on "dispatch fd" column.
> Because select/poll option needs to check all registered fds to find out
> dispatchable fds. But epoll option doesn't require checking all registered fds.
> Because epoll_wait() returns dispatchable fd set.
>
> So epoll option is effective for GO/AP functionality.
>
> [demerit]
> The epoll option requires additional heap memory. In case of P2P GO, it is
> about 8K bytes.
>
> Signed-off-by: Masashi Honma <masashi.honma@gmail.com>
> ---
>  hostapd/Makefile              |   3 +
>  src/utils/eloop.c             | 167 ++++++++++++++++++++++++++++++++++++++----
>  wpa_supplicant/Android.mk     |   3 +
>  wpa_supplicant/Makefile       |   3 +
>  wpa_supplicant/android.config |   1 +
>  wpa_supplicant/defconfig      |   1 +
>  6 files changed, 164 insertions(+), 14 deletions(-)
>
> diff --git a/hostapd/Makefile b/hostapd/Makefile
> index c681b5f..78badb6 100644
> --- a/hostapd/Makefile
> +++ b/hostapd/Makefile
> @@ -102,6 +102,9 @@ endif
>  ifeq ($(CONFIG_ELOOP), poll)
>  CFLAGS += -DCONFIG_ELOOP_POLL
>  endif
> +ifeq ($(CONFIG_ELOOP), epoll)
> +CFLAGS += -DCONFIG_ELOOP_EPOLL
> +endif
>
>  # Using glibc < 2.17 requires -lrt for clock_gettime()
>  LIBS += -lrt
> diff --git a/src/utils/eloop.c b/src/utils/eloop.c
> index c4012b3..7298106 100644
> --- a/src/utils/eloop.c
> +++ b/src/utils/eloop.c
> @@ -18,6 +18,9 @@
>  #include <poll.h>
>  #endif /* CONFIG_ELOOP_POLL */
>
> +#ifdef CONFIG_ELOOP_EPOLL
> +#include <sys/epoll.h>
> +#endif /* CONFIG_ELOOP_EPOLL */
>
>  struct eloop_sock {
>         int sock;
> @@ -50,7 +53,11 @@ struct eloop_signal {
>  struct eloop_sock_table {
>         int count;
>         struct eloop_sock *table;
> +#ifdef CONFIG_ELOOP_EPOLL
> +       eloop_event_type type;
> +#else /* CONFIG_ELOOP_EPOLL */
>         int changed;
> +#endif /* CONFIG_ELOOP_EPOLL */
>  };
>
>  struct eloop_data {
> @@ -63,6 +70,13 @@ struct eloop_data {
>         struct pollfd *pollfds;
>         struct pollfd **pollfds_map;
>  #endif /* CONFIG_ELOOP_POLL */
> +#ifdef CONFIG_ELOOP_EPOLL
> +       int epollfd;
> +       int epoll_max_event_num;
> +       int epoll_max_fd;
> +       struct eloop_sock *epoll_table;
> +       struct epoll_event *epoll_events;
> +#endif /* CONFIG_ELOOP_EPOLL */
>         struct eloop_sock_table readers;
>         struct eloop_sock_table writers;
>         struct eloop_sock_table exceptions;
> @@ -75,7 +89,6 @@ struct eloop_data {
>         int pending_terminate;
>
>         int terminate;
> -       int reader_table_changed;
>  };
>
>  static struct eloop_data eloop;
> @@ -128,6 +141,17 @@ 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\n",
> +                          __func__, strerror(errno));
> +               return -1;
> +       }
> +       eloop.readers.type = EVENT_TYPE_READ;
> +       eloop.writers.type = EVENT_TYPE_WRITE;
> +       eloop.exceptions.type = EVENT_TYPE_EXCEPTION;
> +#endif /* CONFIG_ELOOP_EPOLL */
>  #ifdef WPA_TRACE
>         signal(SIGSEGV, eloop_sigsegv_handler);
>  #endif /* WPA_TRACE */
> @@ -139,6 +163,11 @@ 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 eloop_sock *temp_table;
> +       struct epoll_event ev, *temp_events;
> +       int next;
> +#endif /* CONFIG_ELOOP_EPOLL */
>         struct eloop_sock *tmp;
>         int new_max_sock;
>
> @@ -174,6 +203,33 @@ static int eloop_sock_table_add_sock(struct eloop_sock_table *table,
>                 eloop.pollfds = n;
>         }
>  #endif /* CONFIG_ELOOP_POLL */
> +#ifdef CONFIG_ELOOP_EPOLL
> +       if (new_max_sock >= eloop.epoll_max_fd) {
> +               next = eloop.epoll_max_fd == 0 ? 16 : eloop.epoll_max_fd * 2;
> +               temp_table = os_realloc_array(eloop.epoll_table, next,
> +                                             sizeof(struct eloop_sock));
> +               if (temp_table == NULL)
> +                       return -1;
> +
> +               eloop.epoll_max_fd = next;
> +               eloop.epoll_table = temp_table;
> +       }
> +
> +       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\n", __func__, strerror(errno));
> +                       return -1;
> +               }
> +
> +               eloop.epoll_max_event_num = next;
> +               eloop.epoll_events = temp_events;
> +       }
> +#endif /* CONFIG_ELOOP_EPOLL */
>
>         eloop_trace_sock_remove_ref(table);
>         tmp = os_realloc_array(table->table, table->count + 1,
> @@ -190,9 +246,38 @@ static int eloop_sock_table_add_sock(struct eloop_sock_table *table,
>         table->table = tmp;
>         eloop.max_sock = new_max_sock;
>         eloop.count++;
> +#ifndef CONFIG_ELOOP_EPOLL
>         table->changed = 1;
> +#endif /* CONFIG_ELOOP_EPOLL */
>         eloop_trace_sock_add_ref(table);
>
> +#ifdef CONFIG_ELOOP_EPOLL
> +       os_memset(&ev, 0, sizeof(ev));
> +       switch (table->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\n", __func__, sock, strerror(errno));
> +               return -1;
> +       }
> +       os_memcpy(&eloop.epoll_table[sock], &table->table[table->count - 1],
> +                 sizeof(struct eloop_sock));
> +#endif /* CONFIG_ELOOP_EPOLL */
>         return 0;
>  }
>
> @@ -219,8 +304,18 @@ static void eloop_sock_table_remove_sock(struct eloop_sock_table *table,
>         }
>         table->count--;
>         eloop.count--;
> +#ifndef CONFIG_ELOOP_EPOLL
>         table->changed = 1;
> +#endif /* CONFIG_ELOOP_EPOLL */
>         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\n", __func__, sock, strerror(errno));
> +               return;
> +       }
> +       os_memset(&eloop.epoll_table[sock], 0, sizeof(struct eloop_sock));
> +#endif /* CONFIG_ELOOP_EPOLL */
>  }
>
>
> @@ -404,6 +499,23 @@ static void eloop_sock_table_dispatch(struct eloop_sock_table *table,
>  #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.epoll_table[events[i].data.fd];
> +               if (table->handler == NULL)
> +                       continue;
> +               table->handler(table->sock, table->eloop_data,
> +                              table->user_data);
> +       }
> +}
> +#endif /* CONFIG_ELOOP_EPOLL */
> +
> +
>  static void eloop_sock_table_destroy(struct eloop_sock_table *table)
>  {
>         if (table) {
> @@ -780,6 +892,9 @@ void eloop_run(void)
>         fd_set *rfds, *wfds, *efds;
>         struct timeval _tv;
>  #endif /* CONFIG_ELOOP_SELECT */
> +#ifdef CONFIG_ELOOP_EPOLL
> +       int timeout_ms = -1;
> +#endif /* CONFIG_ELOOP_EPOLL */
>         int res;
>         struct os_reltime tv, now;
>
> @@ -803,9 +918,9 @@ 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;
> -#endif /* 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;
> @@ -819,12 +934,6 @@ 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;
> -               }
>  #endif /* CONFIG_ELOOP_POLL */
>  #ifdef CONFIG_ELOOP_SELECT
>                 eloop_sock_table_set_fds(&eloop.readers, rfds);
> @@ -832,12 +941,29 @@ void eloop_run(void)
>                 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 */
>                 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 */
> +                                  , strerror(errno));
>                         goto out;
>                 }
> -#endif /* CONFIG_ELOOP_SELECT */
>                 eloop_process_pending_signals();
>
>                 /* check if some registered timeouts have occurred */
> @@ -869,6 +995,9 @@ void eloop_run(void)
>                 eloop_sock_table_dispatch(&eloop.writers, wfds);
>                 eloop_sock_table_dispatch(&eloop.exceptions, efds);
>  #endif /* CONFIG_ELOOP_SELECT */
> +#ifdef CONFIG_ELOOP_EPOLL
> +               eloop_sock_table_dispatch(eloop.epoll_events, res);
> +#endif /* CONFIG_ELOOP_EPOLL */
>         }
>
>         eloop.terminate = 0;
> @@ -921,6 +1050,11 @@ void eloop_destroy(void)
>         os_free(eloop.pollfds);
>         os_free(eloop.pollfds_map);
>  #endif /* CONFIG_ELOOP_POLL */
> +#ifdef CONFIG_ELOOP_EPOLL
> +       os_free(eloop.epoll_table);
> +       os_free(eloop.epoll_events);
> +       close(eloop.epollfd);
> +#endif /* CONFIG_ELOOP_EPOLL */
>  }
>
>
> @@ -944,7 +1078,12 @@ void eloop_wait_for_read_sock(int sock)
>
>         poll(&pfd, 1, -1);
>  #endif /* CONFIG_ELOOP_POLL */
> -#ifdef CONFIG_ELOOP_SELECT
> +#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)
> @@ -953,5 +1092,5 @@ 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_SELECT */
> +#endif /* defined(CONFIG_ELOOP_SELECT) || defined(CONFIG_ELOOP_EPOLL) */
>  }
> diff --git a/wpa_supplicant/Android.mk b/wpa_supplicant/Android.mk
> index 4d6a7d2..d7c30b8 100644
> --- a/wpa_supplicant/Android.mk
> +++ b/wpa_supplicant/Android.mk
> @@ -142,6 +142,9 @@ endif
>  ifeq ($(CONFIG_ELOOP), poll)
>  L_CFLAGS += -DCONFIG_ELOOP_POLL
>  endif
> +ifeq ($(CONFIG_ELOOP), epoll)
> +CFLAGS += -DCONFIG_ELOOP_EPOLL
> +endif
>
>  ifdef CONFIG_EAPOL_TEST
>  L_CFLAGS += -Werror -DEAPOL_TEST
> diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile
> index 8dc3850..84e8124 100644
> --- a/wpa_supplicant/Makefile
> +++ b/wpa_supplicant/Makefile
> @@ -142,6 +142,9 @@ endif
>  ifeq ($(CONFIG_ELOOP), poll)
>  CFLAGS += -DCONFIG_ELOOP_POLL
>  endif
> +ifeq ($(CONFIG_ELOOP), epoll)
> +CFLAGS += -DCONFIG_ELOOP_EPOLL
> +endif
>  endif
>
>  ifdef CONFIG_EAPOL_TEST
> diff --git a/wpa_supplicant/android.config b/wpa_supplicant/android.config
> index 8fd7624..c26c14e 100644
> --- a/wpa_supplicant/android.config
> +++ b/wpa_supplicant/android.config
> @@ -248,6 +248,7 @@ CONFIG_OS=unix
>  # Select event loop implementation
>  # select = select() loop (default)
>  # poll = poll() loop
> +# epoll = epoll() loop
>  # windows = Windows events and WaitForMultipleObject() loop
>  CONFIG_ELOOP=select
>
> diff --git a/wpa_supplicant/defconfig b/wpa_supplicant/defconfig
> index 2ba4a98..1b68381 100644
> --- a/wpa_supplicant/defconfig
> +++ b/wpa_supplicant/defconfig
> @@ -264,6 +264,7 @@ CONFIG_BACKEND=file
>  # Select event loop implementation
>  # select = select() loop (default)
>  # poll = poll() loop
> +# epoll = epoll() loop
>  # windows = Windows events and WaitForMultipleObject() loop
>  #CONFIG_ELOOP=select
>
> --
> 1.8.1.2
>

Patch

diff --git a/hostapd/Makefile b/hostapd/Makefile
index c681b5f..78badb6 100644
--- a/hostapd/Makefile
+++ b/hostapd/Makefile
@@ -102,6 +102,9 @@  endif
 ifeq ($(CONFIG_ELOOP), poll)
 CFLAGS += -DCONFIG_ELOOP_POLL
 endif
+ifeq ($(CONFIG_ELOOP), epoll)
+CFLAGS += -DCONFIG_ELOOP_EPOLL
+endif
 
 # Using glibc < 2.17 requires -lrt for clock_gettime()
 LIBS += -lrt
diff --git a/src/utils/eloop.c b/src/utils/eloop.c
index c4012b3..7298106 100644
--- a/src/utils/eloop.c
+++ b/src/utils/eloop.c
@@ -18,6 +18,9 @@ 
 #include <poll.h>
 #endif /* CONFIG_ELOOP_POLL */
 
+#ifdef CONFIG_ELOOP_EPOLL
+#include <sys/epoll.h>
+#endif /* CONFIG_ELOOP_EPOLL */
 
 struct eloop_sock {
 	int sock;
@@ -50,7 +53,11 @@  struct eloop_signal {
 struct eloop_sock_table {
 	int count;
 	struct eloop_sock *table;
+#ifdef CONFIG_ELOOP_EPOLL
+	eloop_event_type type;
+#else /* CONFIG_ELOOP_EPOLL */
 	int changed;
+#endif /* CONFIG_ELOOP_EPOLL */
 };
 
 struct eloop_data {
@@ -63,6 +70,13 @@  struct eloop_data {
 	struct pollfd *pollfds;
 	struct pollfd **pollfds_map;
 #endif /* CONFIG_ELOOP_POLL */
+#ifdef CONFIG_ELOOP_EPOLL
+	int epollfd;
+	int epoll_max_event_num;
+	int epoll_max_fd;
+	struct eloop_sock *epoll_table;
+	struct epoll_event *epoll_events;
+#endif /* CONFIG_ELOOP_EPOLL */
 	struct eloop_sock_table readers;
 	struct eloop_sock_table writers;
 	struct eloop_sock_table exceptions;
@@ -75,7 +89,6 @@  struct eloop_data {
 	int pending_terminate;
 
 	int terminate;
-	int reader_table_changed;
 };
 
 static struct eloop_data eloop;
@@ -128,6 +141,17 @@  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\n",
+			   __func__, strerror(errno));
+		return -1;
+	}
+	eloop.readers.type = EVENT_TYPE_READ;
+	eloop.writers.type = EVENT_TYPE_WRITE;
+	eloop.exceptions.type = EVENT_TYPE_EXCEPTION;
+#endif /* CONFIG_ELOOP_EPOLL */
 #ifdef WPA_TRACE
 	signal(SIGSEGV, eloop_sigsegv_handler);
 #endif /* WPA_TRACE */
@@ -139,6 +163,11 @@  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 eloop_sock *temp_table;
+	struct epoll_event ev, *temp_events;
+	int next;
+#endif /* CONFIG_ELOOP_EPOLL */
 	struct eloop_sock *tmp;
 	int new_max_sock;
 
@@ -174,6 +203,33 @@  static int eloop_sock_table_add_sock(struct eloop_sock_table *table,
 		eloop.pollfds = n;
 	}
 #endif /* CONFIG_ELOOP_POLL */
+#ifdef CONFIG_ELOOP_EPOLL
+	if (new_max_sock >= eloop.epoll_max_fd) {
+		next = eloop.epoll_max_fd == 0 ? 16 : eloop.epoll_max_fd * 2;
+		temp_table = os_realloc_array(eloop.epoll_table, next,
+					      sizeof(struct eloop_sock));
+		if (temp_table == NULL)
+			return -1;
+
+		eloop.epoll_max_fd = next;
+		eloop.epoll_table = temp_table;
+	}
+
+	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\n", __func__, strerror(errno));
+			return -1;
+		}
+
+		eloop.epoll_max_event_num = next;
+		eloop.epoll_events = temp_events;
+	}
+#endif /* CONFIG_ELOOP_EPOLL */
 
 	eloop_trace_sock_remove_ref(table);
 	tmp = os_realloc_array(table->table, table->count + 1,
@@ -190,9 +246,38 @@  static int eloop_sock_table_add_sock(struct eloop_sock_table *table,
 	table->table = tmp;
 	eloop.max_sock = new_max_sock;
 	eloop.count++;
+#ifndef CONFIG_ELOOP_EPOLL
 	table->changed = 1;
+#endif /* CONFIG_ELOOP_EPOLL */
 	eloop_trace_sock_add_ref(table);
 
+#ifdef CONFIG_ELOOP_EPOLL
+	os_memset(&ev, 0, sizeof(ev));
+	switch (table->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\n", __func__, sock, strerror(errno));
+		return -1;
+	}
+	os_memcpy(&eloop.epoll_table[sock], &table->table[table->count - 1],
+		  sizeof(struct eloop_sock));
+#endif /* CONFIG_ELOOP_EPOLL */
 	return 0;
 }
 
@@ -219,8 +304,18 @@  static void eloop_sock_table_remove_sock(struct eloop_sock_table *table,
 	}
 	table->count--;
 	eloop.count--;
+#ifndef CONFIG_ELOOP_EPOLL
 	table->changed = 1;
+#endif /* CONFIG_ELOOP_EPOLL */
 	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\n", __func__, sock, strerror(errno));
+		return;
+	}
+	os_memset(&eloop.epoll_table[sock], 0, sizeof(struct eloop_sock));
+#endif /* CONFIG_ELOOP_EPOLL */
 }
 
 
@@ -404,6 +499,23 @@  static void eloop_sock_table_dispatch(struct eloop_sock_table *table,
 #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.epoll_table[events[i].data.fd];
+		if (table->handler == NULL)
+			continue;
+		table->handler(table->sock, table->eloop_data,
+			       table->user_data);
+	}
+}
+#endif /* CONFIG_ELOOP_EPOLL */
+
+
 static void eloop_sock_table_destroy(struct eloop_sock_table *table)
 {
 	if (table) {
@@ -780,6 +892,9 @@  void eloop_run(void)
 	fd_set *rfds, *wfds, *efds;
 	struct timeval _tv;
 #endif /* CONFIG_ELOOP_SELECT */
+#ifdef CONFIG_ELOOP_EPOLL
+	int timeout_ms = -1;
+#endif /* CONFIG_ELOOP_EPOLL */
 	int res;
 	struct os_reltime tv, now;
 
@@ -803,9 +918,9 @@  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;
-#endif /* 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;
@@ -819,12 +934,6 @@  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;
-		}
 #endif /* CONFIG_ELOOP_POLL */
 #ifdef CONFIG_ELOOP_SELECT
 		eloop_sock_table_set_fds(&eloop.readers, rfds);
@@ -832,12 +941,29 @@  void eloop_run(void)
 		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 */
 		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 */
+				   , strerror(errno));
 			goto out;
 		}
-#endif /* CONFIG_ELOOP_SELECT */
 		eloop_process_pending_signals();
 
 		/* check if some registered timeouts have occurred */
@@ -869,6 +995,9 @@  void eloop_run(void)
 		eloop_sock_table_dispatch(&eloop.writers, wfds);
 		eloop_sock_table_dispatch(&eloop.exceptions, efds);
 #endif /* CONFIG_ELOOP_SELECT */
+#ifdef CONFIG_ELOOP_EPOLL
+		eloop_sock_table_dispatch(eloop.epoll_events, res);
+#endif /* CONFIG_ELOOP_EPOLL */
 	}
 
 	eloop.terminate = 0;
@@ -921,6 +1050,11 @@  void eloop_destroy(void)
 	os_free(eloop.pollfds);
 	os_free(eloop.pollfds_map);
 #endif /* CONFIG_ELOOP_POLL */
+#ifdef CONFIG_ELOOP_EPOLL
+	os_free(eloop.epoll_table);
+	os_free(eloop.epoll_events);
+	close(eloop.epollfd);
+#endif /* CONFIG_ELOOP_EPOLL */
 }
 
 
@@ -944,7 +1078,12 @@  void eloop_wait_for_read_sock(int sock)
 
 	poll(&pfd, 1, -1);
 #endif /* CONFIG_ELOOP_POLL */
-#ifdef CONFIG_ELOOP_SELECT
+#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)
@@ -953,5 +1092,5 @@  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_SELECT */
+#endif /* defined(CONFIG_ELOOP_SELECT) || defined(CONFIG_ELOOP_EPOLL) */
 }
diff --git a/wpa_supplicant/Android.mk b/wpa_supplicant/Android.mk
index 4d6a7d2..d7c30b8 100644
--- a/wpa_supplicant/Android.mk
+++ b/wpa_supplicant/Android.mk
@@ -142,6 +142,9 @@  endif
 ifeq ($(CONFIG_ELOOP), poll)
 L_CFLAGS += -DCONFIG_ELOOP_POLL
 endif
+ifeq ($(CONFIG_ELOOP), epoll)
+CFLAGS += -DCONFIG_ELOOP_EPOLL
+endif
 
 ifdef CONFIG_EAPOL_TEST
 L_CFLAGS += -Werror -DEAPOL_TEST
diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile
index 8dc3850..84e8124 100644
--- a/wpa_supplicant/Makefile
+++ b/wpa_supplicant/Makefile
@@ -142,6 +142,9 @@  endif
 ifeq ($(CONFIG_ELOOP), poll)
 CFLAGS += -DCONFIG_ELOOP_POLL
 endif
+ifeq ($(CONFIG_ELOOP), epoll)
+CFLAGS += -DCONFIG_ELOOP_EPOLL
+endif
 endif
 
 ifdef CONFIG_EAPOL_TEST
diff --git a/wpa_supplicant/android.config b/wpa_supplicant/android.config
index 8fd7624..c26c14e 100644
--- a/wpa_supplicant/android.config
+++ b/wpa_supplicant/android.config
@@ -248,6 +248,7 @@  CONFIG_OS=unix
 # Select event loop implementation
 # select = select() loop (default)
 # poll = poll() loop
+# epoll = epoll() loop
 # windows = Windows events and WaitForMultipleObject() loop
 CONFIG_ELOOP=select
 
diff --git a/wpa_supplicant/defconfig b/wpa_supplicant/defconfig
index 2ba4a98..1b68381 100644
--- a/wpa_supplicant/defconfig
+++ b/wpa_supplicant/defconfig
@@ -264,6 +264,7 @@  CONFIG_BACKEND=file
 # Select event loop implementation
 # select = select() loop (default)
 # poll = poll() loop
+# epoll = epoll() loop
 # windows = Windows events and WaitForMultipleObject() loop
 #CONFIG_ELOOP=select