[PATCH] Add epoll option for better performance
Masashi Honma
masashi.honma
Mon Mar 31 02:19:40 PDT 2014
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 at 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 at 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
>
More information about the Hostap
mailing list