diff options
author | Matthias Schiffer <mschiffer@universe-factory.net> | 2014-04-20 02:48:25 +0200 |
---|---|---|
committer | Matthias Schiffer <mschiffer@universe-factory.net> | 2014-04-20 02:48:25 +0200 |
commit | ab4ca17ba3dfc92932834b09afc83cf7fe002a14 (patch) | |
tree | ddaad23777006ce39a3fda055fad7efe5cd09289 /src/poll.c | |
parent | 8c705b9cd4c7866227f09af2a859744e47602ba4 (diff) | |
download | fastd-ab4ca17ba3dfc92932834b09afc83cf7fe002a14.tar fastd-ab4ca17ba3dfc92932834b09afc83cf7fe002a14.zip |
Linux: use epoll to handle great numbers of peers with less overhead
Diffstat (limited to 'src/poll.c')
-rw-r--r-- | src/poll.c | 219 |
1 files changed, 171 insertions, 48 deletions
@@ -28,6 +28,174 @@ #include "async.h" #include "peer.h" +#ifdef USE_EPOLL + +#include <sys/epoll.h> + +#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 <fcntl.h> +#include <sys/epoll.h> + + +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 + |