Message ID | 1395469046-5933-1-git-send-email-masashi.honma@gmail.com |
---|---|
State | Superseded |
Headers | show |
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 >
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
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(-)