eloop: Support poll() in eloop

When using more than around 200 virtual stations, we start hitting the
max number of file descriptors supported by select(). This patch adds
support for poll(), which has no hard upper limit.

Signed-hostap: Ben Greear <greearb@candelatech.com>
This commit is contained in:
Ben Greear 2012-02-12 17:47:23 +02:00 committed by Jouni Malinen
parent a565c23bf3
commit 2df4c4ef2f
3 changed files with 234 additions and 1 deletions

View file

@ -13,6 +13,11 @@
#include "list.h" #include "list.h"
#include "eloop.h" #include "eloop.h"
#ifdef CONFIG_ELOOP_POLL
#include <assert.h>
#include <poll.h>
#endif /* CONFIG_ELOOP_POLL */
struct eloop_sock { struct eloop_sock {
int sock; int sock;
@ -51,6 +56,13 @@ struct eloop_sock_table {
struct eloop_data { struct eloop_data {
int max_sock; int max_sock;
int count; /* sum of all table counts */
#ifdef CONFIG_ELOOP_POLL
int max_pollfd_map; /* number of pollfds_map currently allocated */
int max_poll_fds; /* number of pollfds currently allocated */
struct pollfd *pollfds;
struct pollfd **pollfds_map;
#endif /* CONFIG_ELOOP_POLL */
struct eloop_sock_table readers; struct eloop_sock_table readers;
struct eloop_sock_table writers; struct eloop_sock_table writers;
struct eloop_sock_table exceptions; struct eloop_sock_table exceptions;
@ -148,6 +160,26 @@ static int eloop_sock_table_add_sock(struct eloop_sock_table *table,
table->table = tmp; table->table = tmp;
if (sock > eloop.max_sock) if (sock > eloop.max_sock)
eloop.max_sock = sock; eloop.max_sock = sock;
eloop.count++;
#ifdef CONFIG_ELOOP_POLL
if (eloop.max_sock >= eloop.max_pollfd_map) {
os_free(eloop.pollfds_map);
eloop.max_pollfd_map = eloop.max_sock + 50;
eloop.pollfds_map = os_zalloc(sizeof(struct pollfd *) *
eloop.max_pollfd_map);
if (!eloop.pollfds_map)
eloop.max_pollfd_map = 0;
}
if (eloop.count > eloop.max_poll_fds) {
os_free(eloop.pollfds);
eloop.max_poll_fds = eloop.count + 50;
eloop.pollfds = os_zalloc(sizeof(struct pollfd) *
eloop.max_poll_fds);
if (!eloop.pollfds)
eloop.max_poll_fds = 0;
}
#endif /* CONFIG_ELOOP_POLL */
table->changed = 1; table->changed = 1;
eloop_trace_sock_add_ref(table); eloop_trace_sock_add_ref(table);
@ -176,11 +208,151 @@ static void eloop_sock_table_remove_sock(struct eloop_sock_table *table,
sizeof(struct eloop_sock)); sizeof(struct eloop_sock));
} }
table->count--; table->count--;
eloop.count--;
table->changed = 1; table->changed = 1;
eloop_trace_sock_add_ref(table); eloop_trace_sock_add_ref(table);
} }
#ifdef CONFIG_ELOOP_POLL
static struct pollfd * find_pollfd(struct pollfd **pollfds_map, int fd, int mx)
{
if (fd < mx && fd >= 0)
return pollfds_map[fd];
return NULL;
}
static int eloop_sock_table_set_fds(struct eloop_sock_table *readers,
struct eloop_sock_table *writers,
struct eloop_sock_table *exceptions,
struct pollfd *pollfds,
struct pollfd **pollfds_map,
int max_pollfd_map)
{
int i;
int nxt = 0;
int fd;
struct pollfd *pfd;
/* Clear pollfd lookup map. It will be re-populated below. */
os_memset(pollfds_map, 0, sizeof(struct pollfd *) * max_pollfd_map);
if (readers && readers->table) {
for (i = 0; i < readers->count; i++) {
fd = readers->table[i].sock;
assert(fd >= 0 && fd < max_pollfd_map);
pollfds[nxt].fd = fd;
pollfds[nxt].events = POLLIN;
pollfds[nxt].revents = 0;
pollfds_map[fd] = &(pollfds[nxt]);
nxt++;
}
}
if (writers && writers->table) {
for (i = 0; i < writers->count; i++) {
/*
* See if we already added this descriptor, update it
* if so.
*/
fd = writers->table[i].sock;
assert(fd >= 0 && fd < max_pollfd_map);
pfd = pollfds_map[fd];
if (!pfd) {
pfd = &(pollfds[nxt]);
pfd->events = 0;
pfd->fd = fd;
pollfds[i].revents = 0;
pollfds_map[fd] = pfd;
nxt++;
}
pfd->events |= POLLIN;
}
}
/*
* Exceptions are always checked when using poll, but I suppose it's
* possible that someone registered a socket *only* for exception
* handling. Set the POLLIN bit in this case.
*/
if (exceptions && exceptions->table) {
for (i = 0; i < exceptions->count; i++) {
/*
* See if we already added this descriptor, just use it
* if so.
*/
fd = exceptions->table[i].sock;
assert(fd >= 0 && fd < max_pollfd_map);
pfd = pollfds_map[fd];
if (!pfd) {
pfd = &(pollfds[nxt]);
pfd->events = POLLIN;
pfd->fd = fd;
pollfds[i].revents = 0;
pollfds_map[fd] = pfd;
nxt++;
}
}
}
return nxt;
}
static int eloop_sock_table_dispatch_table(struct eloop_sock_table *table,
struct pollfd **pollfds_map,
int max_pollfd_map,
short int revents)
{
int i;
struct pollfd *pfd;
if (!table || !table->table)
return 0;
table->changed = 0;
for (i = 0; i < table->count; i++) {
pfd = find_pollfd(pollfds_map, table->table[i].sock,
max_pollfd_map);
if (!pfd)
continue;
if (!(pfd->revents & revents))
continue;
table->table[i].handler(table->table[i].sock,
table->table[i].eloop_data,
table->table[i].user_data);
if (table->changed)
return 1;
}
return 0;
}
static void eloop_sock_table_dispatch(struct eloop_sock_table *readers,
struct eloop_sock_table *writers,
struct eloop_sock_table *exceptions,
struct pollfd **pollfds_map,
int max_pollfd_map)
{
if (eloop_sock_table_dispatch_table(readers, pollfds_map,
max_pollfd_map, POLLIN))
return; /* pollfds may be invalid at this point */
if (eloop_sock_table_dispatch_table(writers, pollfds_map,
max_pollfd_map, POLLOUT))
return; /* pollfds may be invalid at this point */
eloop_sock_table_dispatch_table(exceptions, pollfds_map,
max_pollfd_map, POLLERR | POLLHUP);
}
#else /* CONFIG_ELOOP_POLL */
static void eloop_sock_table_set_fds(struct eloop_sock_table *table, static void eloop_sock_table_set_fds(struct eloop_sock_table *table,
fd_set *fds) fd_set *fds)
{ {
@ -216,6 +388,8 @@ static void eloop_sock_table_dispatch(struct eloop_sock_table *table,
} }
} }
#endif /* CONFIG_ELOOP_POLL */
static void eloop_sock_table_destroy(struct eloop_sock_table *table) static void eloop_sock_table_destroy(struct eloop_sock_table *table)
{ {
@ -496,16 +670,23 @@ int eloop_register_signal_reconfig(eloop_signal_handler handler,
void eloop_run(void) void eloop_run(void)
{ {
#ifdef CONFIG_ELOOP_POLL
int num_poll_fds;
int timeout_ms = 0;
#else /* CONFIG_ELOOP_POLL */
fd_set *rfds, *wfds, *efds; fd_set *rfds, *wfds, *efds;
int res;
struct timeval _tv; struct timeval _tv;
#endif /* CONFIG_ELOOP_POLL */
int res;
struct os_time tv, now; struct os_time tv, now;
#ifndef CONFIG_ELOOP_POLL
rfds = os_malloc(sizeof(*rfds)); rfds = os_malloc(sizeof(*rfds));
wfds = os_malloc(sizeof(*wfds)); wfds = os_malloc(sizeof(*wfds));
efds = os_malloc(sizeof(*efds)); efds = os_malloc(sizeof(*efds));
if (rfds == NULL || wfds == NULL || efds == NULL) if (rfds == NULL || wfds == NULL || efds == NULL)
goto out; goto out;
#endif /* CONFIG_ELOOP_POLL */
while (!eloop.terminate && while (!eloop.terminate &&
(!dl_list_empty(&eloop.timeout) || eloop.readers.count > 0 || (!dl_list_empty(&eloop.timeout) || eloop.readers.count > 0 ||
@ -519,10 +700,27 @@ void eloop_run(void)
os_time_sub(&timeout->time, &now, &tv); os_time_sub(&timeout->time, &now, &tv);
else else
tv.sec = tv.usec = 0; tv.sec = tv.usec = 0;
#ifdef CONFIG_ELOOP_POLL
timeout_ms = tv.sec * 1000 + tv.usec / 1000;
#else /* CONFIG_ELOOP_POLL */
_tv.tv_sec = tv.sec; _tv.tv_sec = tv.sec;
_tv.tv_usec = tv.usec; _tv.tv_usec = tv.usec;
#endif /* CONFIG_ELOOP_POLL */
} }
#ifdef CONFIG_ELOOP_POLL
num_poll_fds = eloop_sock_table_set_fds(
&eloop.readers, &eloop.writers, &eloop.exceptions,
eloop.pollfds, eloop.pollfds_map,
eloop.max_pollfd_map);
res = poll(eloop.pollfds, num_poll_fds,
timeout ? timeout_ms : -1);
if (res < 0 && errno != EINTR && errno != 0) {
perror("poll");
goto out;
}
#else /* CONFIG_ELOOP_POLL */
eloop_sock_table_set_fds(&eloop.readers, rfds); eloop_sock_table_set_fds(&eloop.readers, rfds);
eloop_sock_table_set_fds(&eloop.writers, wfds); eloop_sock_table_set_fds(&eloop.writers, wfds);
eloop_sock_table_set_fds(&eloop.exceptions, efds); eloop_sock_table_set_fds(&eloop.exceptions, efds);
@ -532,6 +730,7 @@ void eloop_run(void)
perror("select"); perror("select");
goto out; goto out;
} }
#endif /* CONFIG_ELOOP_POLL */
eloop_process_pending_signals(); eloop_process_pending_signals();
/* check if some registered timeouts have occurred */ /* check if some registered timeouts have occurred */
@ -553,15 +752,24 @@ void eloop_run(void)
if (res <= 0) if (res <= 0)
continue; 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 */
eloop_sock_table_dispatch(&eloop.readers, rfds); eloop_sock_table_dispatch(&eloop.readers, rfds);
eloop_sock_table_dispatch(&eloop.writers, wfds); eloop_sock_table_dispatch(&eloop.writers, wfds);
eloop_sock_table_dispatch(&eloop.exceptions, efds); eloop_sock_table_dispatch(&eloop.exceptions, efds);
#endif /* CONFIG_ELOOP_POLL */
} }
out: out:
#ifndef CONFIG_ELOOP_POLL
os_free(rfds); os_free(rfds);
os_free(wfds); os_free(wfds);
os_free(efds); os_free(efds);
#endif /* CONFIG_ELOOP_POLL */
return;
} }
@ -599,6 +807,11 @@ void eloop_destroy(void)
eloop_sock_table_destroy(&eloop.writers); eloop_sock_table_destroy(&eloop.writers);
eloop_sock_table_destroy(&eloop.exceptions); eloop_sock_table_destroy(&eloop.exceptions);
os_free(eloop.signals); os_free(eloop.signals);
#ifdef CONFIG_ELOOP_POLL
os_free(eloop.pollfds);
os_free(eloop.pollfds_map);
#endif /* CONFIG_ELOOP_POLL */
} }
@ -610,6 +823,18 @@ int eloop_terminated(void)
void eloop_wait_for_read_sock(int sock) void eloop_wait_for_read_sock(int sock)
{ {
#ifdef CONFIG_ELOOP_POLL
struct pollfd pfd;
if (sock < 0)
return;
os_memset(&pfd, 0, sizeof(pfd));
pfd.fd = sock;
pfd.events = POLLIN;
poll(&pfd, 1, -1);
#else /* CONFIG_ELOOP_POLL */
fd_set rfds; fd_set rfds;
if (sock < 0) if (sock < 0)
@ -618,4 +843,5 @@ void eloop_wait_for_read_sock(int sock)
FD_ZERO(&rfds); FD_ZERO(&rfds);
FD_SET(sock, &rfds); FD_SET(sock, &rfds);
select(sock + 1, &rfds, NULL, NULL, NULL); select(sock + 1, &rfds, NULL, NULL, NULL);
#endif /* CONFIG_ELOOP_POLL */
} }

View file

@ -108,6 +108,10 @@ endif
OBJS += ../src/utils/$(CONFIG_ELOOP).o OBJS += ../src/utils/$(CONFIG_ELOOP).o
OBJS_c += ../src/utils/$(CONFIG_ELOOP).o OBJS_c += ../src/utils/$(CONFIG_ELOOP).o
ifdef CONFIG_ELOOP_POLL
CFLAGS += -DCONFIG_ELOOP_POLL
endif
ifdef CONFIG_EAPOL_TEST ifdef CONFIG_EAPOL_TEST
CFLAGS += -Werror -DEAPOL_TEST CFLAGS += -Werror -DEAPOL_TEST

View file

@ -306,6 +306,9 @@ CONFIG_BACKEND=file
# eloop_none = Empty template # eloop_none = Empty template
#CONFIG_ELOOP=eloop #CONFIG_ELOOP=eloop
# Should we use poll instead of select? Select is used by default.
#CONFIG_ELOOP_POLL=y
# Select layer 2 packet implementation # Select layer 2 packet implementation
# linux = Linux packet socket (default) # linux = Linux packet socket (default)
# pcap = libpcap/libdnet/WinPcap # pcap = libpcap/libdnet/WinPcap