From ab4ca17ba3dfc92932834b09afc83cf7fe002a14 Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Sun, 20 Apr 2014 02:48:25 +0200 Subject: Linux: use epoll to handle great numbers of peers with less overhead --- cmake/config.cmake | 1 + src/fastd.h | 8 +- src/fastd_config.h.in | 1 + src/poll.c | 219 +++++++++++++++++++++++++++++++++++++++----------- 4 files changed, 179 insertions(+), 50 deletions(-) diff --git a/cmake/config.cmake b/cmake/config.cmake index cbdc775..dbd5668 100644 --- a/cmake/config.cmake +++ b/cmake/config.cmake @@ -6,6 +6,7 @@ endif() set(USE_BINDTODEVICE ${LINUX}) +set(USE_EPOLL ${LINUX}) set(USE_FREEBIND ${LINUX}) set(USE_PMTU ${LINUX}) set(USE_PKTINFO ${LINUX}) diff --git a/src/fastd.h b/src/fastd.h index 8fcd247..f1ae2d3 100644 --- a/src/fastd.h +++ b/src/fastd.h @@ -241,10 +241,14 @@ struct fastd_context { fastd_peer_group_t *peer_group; VECTOR(fastd_peer_t*) peers; - VECTOR(struct pollfd) pollfds; - VECTOR(fastd_peer_t*) peers_temp; +#ifdef USE_EPOLL + int epoll_fd; +#else + VECTOR(struct pollfd) pollfds; +#endif + uint32_t peer_addr_ht_seed; VECTOR(fastd_peer_t*) *peer_addr_ht; diff --git a/src/fastd_config.h.in b/src/fastd_config.h.in index 8394853..0f9522b 100644 --- a/src/fastd_config.h.in +++ b/src/fastd_config.h.in @@ -32,6 +32,7 @@ #cmakedefine USE_BINDTODEVICE +#cmakedefine USE_EPOLL #cmakedefine USE_FREEBIND #cmakedefine USE_PMTU #cmakedefine USE_PKTINFO diff --git a/src/poll.c b/src/poll.c index 31e69bf..150f2ca 100644 --- a/src/poll.c +++ b/src/poll.c @@ -28,6 +28,174 @@ #include "async.h" #include "peer.h" +#ifdef USE_EPOLL + +#include + +#endif + + +static inline bool handle_tun_tap(fastd_context_t *ctx, fastd_buffer_t buffer) { + if (ctx->conf->mode != MODE_TAP) + return false; + + if (buffer.len < ETH_HLEN) { + pr_debug(ctx, "truncated packet on tap interface"); + fastd_buffer_free(buffer); + return true; + } + + fastd_eth_addr_t dest_addr = fastd_get_dest_address(ctx, buffer); + if (!fastd_eth_addr_is_unicast(dest_addr)) + return false; + + fastd_peer_t *peer = fastd_peer_find_by_eth_addr(ctx, dest_addr); + + if (!peer) + return false; + + ctx->conf->protocol->send(ctx, peer, buffer); + return true; +} + +static void handle_tun(fastd_context_t *ctx) { + fastd_buffer_t buffer = fastd_tuntap_read(ctx); + if (!buffer.len) + return; + + if (handle_tun_tap(ctx, buffer)) + return; + + /* TUN mode or multicast packet */ + fastd_send_all(ctx, NULL, buffer); +} + +static inline int handshake_timeout(fastd_context_t *ctx) { + if (!ctx->handshake_queue.next) + return -1; + + fastd_peer_t *peer = container_of(ctx->handshake_queue.next, fastd_peer_t, handshake_entry); + + int diff_msec = timespec_diff(&peer->next_handshake, &ctx->now); + if (diff_msec < 0) + return 0; + else + return diff_msec; +} + + +#ifdef USE_EPOLL + +#include +#include + + +void fastd_poll_init(fastd_context_t *ctx) { + ctx->epoll_fd = epoll_create1(EPOLL_CLOEXEC); + if (ctx->epoll_fd < 0) + exit_errno(ctx, "epoll_create1"); + + struct epoll_event event = { + .events = EPOLLIN, + .data.ptr = &ctx->async_rfd, + }; + if (epoll_ctl(ctx->epoll_fd, EPOLL_CTL_ADD, ctx->async_rfd, &event) < 0) + exit_errno(ctx, "epoll_ctl"); +} + +void fastd_poll_free(fastd_context_t *ctx) { + if (close(ctx->epoll_fd)) + pr_warn_errno(ctx, "closing EPOLL: close"); +} + + +void fastd_poll_set_fd_tuntap(fastd_context_t *ctx) { + struct epoll_event event = { + .events = EPOLLIN, + .data.ptr = &ctx->tunfd, + }; + if (epoll_ctl(ctx->epoll_fd, EPOLL_CTL_ADD, ctx->tunfd, &event) < 0) + exit_errno(ctx, "epoll_ctl"); +} + +void fastd_poll_set_fd_sock(fastd_context_t *ctx, size_t i) { + struct epoll_event event = { + .events = EPOLLIN, + .data.ptr = &ctx->socks[i], + }; + if (epoll_ctl(ctx->epoll_fd, EPOLL_CTL_ADD, ctx->socks[i].fd, &event) < 0) + exit_errno(ctx, "epoll_ctl"); +} + +void fastd_poll_set_fd_peer(fastd_context_t *ctx, size_t i) { + fastd_peer_t *peer = VECTOR_INDEX(ctx->peers, i); + + if (!peer->sock || !fastd_peer_is_socket_dynamic(peer)) + return; + + struct epoll_event event = { + .events = EPOLLIN, + .data.ptr = peer->sock, + }; + if (epoll_ctl(ctx->epoll_fd, EPOLL_CTL_ADD, peer->sock->fd, &event) < 0) + exit_errno(ctx, "epoll_ctl"); +} + +void fastd_poll_add_peer(fastd_context_t *ctx UNUSED) { +} + +void fastd_poll_delete_peer(fastd_context_t *ctx UNUSED, size_t i UNUSED) { +} + + +void fastd_poll_handle(fastd_context_t *ctx) { + int maintenance_timeout = timespec_diff(&ctx->next_maintenance, &ctx->now); + + if (maintenance_timeout < 0) + maintenance_timeout = 0; + + int timeout = handshake_timeout(ctx); + if (timeout < 0 || timeout > maintenance_timeout) + timeout = maintenance_timeout; + + fastd_update_time(ctx); + + struct epoll_event events[16]; + int ret = epoll_wait(ctx->epoll_fd, events, 16, timeout); + if (ret < 0) { + if (errno == EINTR) + return; + + exit_errno(ctx, "epoll_wait"); + } + + size_t i; + for (i = 0; i < (size_t)ret; i++) { + if (events[i].data.ptr == &ctx->tunfd) { + if (events[i].events & EPOLLIN) + handle_tun(ctx); + } + else if (events[i].data.ptr == &ctx->async_rfd) { + if (events[i].events & EPOLLIN) + fastd_async_handle(ctx); + } + else { + fastd_socket_t *sock = events[i].data.ptr; + + if (events[i].events & (EPOLLERR|EPOLLHUP)) { + if (sock->peer) + fastd_peer_reset_socket(ctx, sock->peer); + else + fastd_socket_error(ctx, sock); + } + else if (events[i].events & EPOLLIN) { + fastd_receive(ctx, sock); + } + } + } +} + +#else void fastd_poll_init(fastd_context_t *ctx) { VECTOR_ALLOC(ctx->pollfds, 2 + ctx->n_socks); @@ -91,54 +259,6 @@ void fastd_poll_delete_peer(fastd_context_t *ctx, size_t i) { } -static inline bool handle_tun_tap(fastd_context_t *ctx, fastd_buffer_t buffer) { - if (ctx->conf->mode != MODE_TAP) - return false; - - if (buffer.len < ETH_HLEN) { - pr_debug(ctx, "truncated packet on tap interface"); - fastd_buffer_free(buffer); - return true; - } - - fastd_eth_addr_t dest_addr = fastd_get_dest_address(ctx, buffer); - if (!fastd_eth_addr_is_unicast(dest_addr)) - return false; - - fastd_peer_t *peer = fastd_peer_find_by_eth_addr(ctx, dest_addr); - - if (!peer) - return false; - - ctx->conf->protocol->send(ctx, peer, buffer); - return true; -} - -static void handle_tun(fastd_context_t *ctx) { - fastd_buffer_t buffer = fastd_tuntap_read(ctx); - if (!buffer.len) - return; - - if (handle_tun_tap(ctx, buffer)) - return; - - /* TUN mode or multicast packet */ - fastd_send_all(ctx, NULL, buffer); -} - -static inline int handshake_timeout(fastd_context_t *ctx) { - if (!ctx->handshake_queue.next) - return -1; - - fastd_peer_t *peer = container_of(ctx->handshake_queue.next, fastd_peer_t, handshake_entry); - - int diff_msec = timespec_diff(&peer->next_handshake, &ctx->now); - if (diff_msec < 0) - return 0; - else - return diff_msec; -} - void fastd_poll_handle(fastd_context_t *ctx) { int maintenance_timeout = timespec_diff(&ctx->next_maintenance, &ctx->now); @@ -186,3 +306,6 @@ void fastd_poll_handle(fastd_context_t *ctx) { } } } + +#endif + -- cgit v1.2.3