[PATCH] Receive multiple packets at a time
Samuel Thibault
samuel.thibault at ens-lyon.org
Wed Dec 2 12:58:26 CET 2015
Hello,
Linux has a recvmmsg() system call which allows to achieve several
recvfrom() at a time. The patch below makes tinc use it (patch against
1.1-pre11). Basically the patch turns the handle_incoming_vpn_data
variables into arrays (of size 1 when recvmmsg is not available, and
thus compiled the same as before), and makes the code index into the
arrays. You may want to use interdiff -w /dev/null patch to better see
what changes the patch makes.
With this patch, I saw the non-ciphered bandwidth achieved over direct
ethernet improve from 680Mbps to 800Mbps (or conversely, reduce the CPU
usage for the same bandwidth).
More is yet to come: I'll have a look at extending the tun/tap interface
to send/receive several packets at a time, and then also using sendmmsg
will again improve performance.
Samuel
--- configure.ac.original 2015-10-02 17:06:31.250034677 +0200
+++ configure.ac 2015-10-02 17:06:54.147546590 +0200
@@ -187,7 +187,7 @@
dnl Checks for library functions.
AC_TYPE_SIGNAL
-AC_CHECK_FUNCS([asprintf daemon fchmod flock ftime fork get_current_dir_name gettimeofday mlockall putenv random select strdup strerror strsignal strtol system time usleep unsetenv vsyslog writev],
+AC_CHECK_FUNCS([asprintf daemon fchmod flock ftime fork get_current_dir_name gettimeofday mlockall putenv random recvmmsg select strdup strerror strsignal strtol system time usleep unsetenv vsyslog writev],
[], [], [#include "src/have.h"]
)
--- src/net_packet.c.original 2015-10-02 16:26:36.828841493 +0200
+++ src/net_packet.c 2015-10-02 17:15:05.892051503 +0200
@@ -1057,92 +1057,137 @@
return n;
}
+#ifdef HAVE_RECVMMSG
+#define MAX_MSG 256
+#else
+#define MAX_MSG 1
+#endif
+
void handle_incoming_vpn_data(void *data, int flags) {
listen_socket_t *ls = data;
- vpn_packet_t pkt;
+ vpn_packet_t pkt[MAX_MSG];
char *hostname;
node_id_t nullid = {};
- sockaddr_t addr = {};
- socklen_t addrlen = sizeof addr;
+ sockaddr_t addr[MAX_MSG] = {};
+#ifdef HAVE_RECVMMSG
+ struct mmsghdr msg[MAX_MSG];
+ struct iovec iov[MAX_MSG];
+#else
+ socklen_t addrlen = sizeof addr[0];
+#endif
node_t *from, *to;
bool direct = false;
+ int num = 1, i;
- pkt.offset = 0;
- int len = recvfrom(ls->udp.fd, DATA(&pkt), MAXSIZE, 0, &addr.sa, &addrlen);
+#ifdef HAVE_RECVMMSG
+ for (i = 0; i < MAX_MSG; i++)
+ {
+ pkt[i].offset = 0;
+ msg[i].msg_hdr.msg_name = &addr[i].sa;
+ msg[i].msg_hdr.msg_namelen = sizeof(addr[i]);
+ iov[i].iov_base = DATA(&pkt[i]);
+ iov[i].iov_len = MAXSIZE;
+ msg[i].msg_hdr.msg_iov = &iov[i];
+ msg[i].msg_hdr.msg_iovlen = 1;
+ msg[i].msg_hdr.msg_control = NULL;
+ msg[i].msg_hdr.msg_controllen = 0;
+ }
- if(len <= 0 || len > MAXSIZE) {
+ num = recvmmsg(ls->udp.fd, msg, MAX_MSG, MSG_DONTWAIT, NULL);
+#else
+ pkt[0].offset = 0;
+ int len = recvfrom(ls->udp.fd, DATA(&pkt[0]), MAXSIZE, 0, &addr[0].sa, &addrlen);
+#endif
+
+#ifdef HAVE_RECVMMSG
+ if(num < 0)
+#else
+ if(len <= 0 || len > MAXSIZE)
+#endif
+ {
if(!sockwouldblock(sockerrno))
logger(DEBUG_ALWAYS, LOG_ERR, "Receiving packet failed: %s", sockstrerror(sockerrno));
return;
}
- pkt.len = len;
-
- sockaddrunmap(&addr); /* Some braindead IPv6 implementations do stupid things. */
-
- // Try to figure out who sent this packet.
-
- node_t *n = lookup_node_udp(&addr);
-
- if(!n) {
- // It might be from a 1.1 node, which might have a source ID in the packet.
- pkt.offset = 2 * sizeof(node_id_t);
- from = lookup_node_id(SRCID(&pkt));
- if(from && !memcmp(DSTID(&pkt), &nullid, sizeof nullid) && from->status.sptps) {
- if(sptps_verify_datagram(&from->sptps, DATA(&pkt), pkt.len - 2 * sizeof(node_id_t)))
- n = from;
- else
- goto skip_harder;
+#ifndef HAVE_RECVMMSG
+ pkt[0].len = len;
+#endif
+
+ for (i = 0; i < num; i++)
+ {
+#ifdef HAVE_RECVMMSG
+ pkt[i].len = msg[i].msg_len;
+ if(pkt[i].len <= 0 || pkt[i].len > MAXSIZE)
+ continue;
+#endif
+
+ sockaddrunmap(&addr[i]); /* Some braindead IPv6 implementations do stupid things. */
+
+ // Try to figure out who sent this packet.
+
+ node_t *n = lookup_node_udp(&addr[i]);
+
+ if(!n) {
+ // It might be from a 1.1 node, which might have a source ID in the packet.
+ pkt[i].offset = 2 * sizeof(node_id_t);
+ from = lookup_node_id(SRCID(&pkt[i]));
+ if(from && !memcmp(DSTID(&pkt[i]), &nullid, sizeof nullid) && from->status.sptps) {
+ if(sptps_verify_datagram(&from->sptps, DATA(&pkt[i]), pkt[i].len - 2 * sizeof(node_id_t)))
+ n = from;
+ else
+ goto skip_harder;
+ }
}
- }
- if(!n) {
- pkt.offset = 0;
- n = try_harder(&addr, &pkt);
- }
+ if(!n) {
+ pkt[i].offset = 0;
+ n = try_harder(&addr[i], &pkt[i]);
+ }
-skip_harder:
- if(!n) {
- if(debug_level >= DEBUG_PROTOCOL) {
- hostname = sockaddr2hostname(&addr);
- logger(DEBUG_PROTOCOL, LOG_WARNING, "Received UDP packet from unknown source %s", hostname);
- free(hostname);
+ skip_harder:
+ if(!n) {
+ if(debug_level >= DEBUG_PROTOCOL) {
+ hostname = sockaddr2hostname(&addr[i]);
+ logger(DEBUG_PROTOCOL, LOG_WARNING, "Received UDP packet from unknown source %s", hostname);
+ free(hostname);
+ }
+ continue;
}
- return;
- }
- if(n->status.sptps) {
- pkt.offset = 2 * sizeof(node_id_t);
+ if(n->status.sptps) {
+ pkt[i].offset = 2 * sizeof(node_id_t);
- if(!memcmp(DSTID(&pkt), &nullid, sizeof nullid)) {
+ if(!memcmp(DSTID(&pkt[i]), &nullid, sizeof nullid)) {
+ direct = true;
+ from = n;
+ to = myself;
+ } else {
+ from = lookup_node_id(SRCID(&pkt[i]));
+ to = lookup_node_id(DSTID(&pkt[i]));
+ }
+ if(!from || !to) {
+ logger(DEBUG_PROTOCOL, LOG_WARNING, "Received UDP packet from %s (%s) with unknown source and/or destination ID", n->name, n->hostname);
+ continue;
+ }
+
+ if(to != myself) {
+ send_sptps_data_priv(to, n, 0, DATA(&pkt[i]), pkt[i].len - 2 * sizeof(node_id_t));
+ continue;
+ }
+ } else {
direct = true;
from = n;
- to = myself;
- } else {
- from = lookup_node_id(SRCID(&pkt));
- to = lookup_node_id(DSTID(&pkt));
- }
- if(!from || !to) {
- logger(DEBUG_PROTOCOL, LOG_WARNING, "Received UDP packet from %s (%s) with unknown source and/or destination ID", n->name, n->hostname);
- return;
}
- if(to != myself) {
- send_sptps_data_priv(to, n, 0, DATA(&pkt), pkt.len - 2 * sizeof(node_id_t));
- return;
- }
- } else {
- direct = true;
- from = n;
+ pkt[i].offset = 0;
+ if(!receive_udppacket(from, &pkt[i]))
+ continue;
+
+ n->sock = ls - listen_socket;
+ if(direct && sockaddrcmp(&addr[i], &n->address))
+ update_node_udp(n, &addr[i]);
}
-
- pkt.offset = 0;
- if(!receive_udppacket(from, &pkt))
- return;
-
- n->sock = ls - listen_socket;
- if(direct && sockaddrcmp(&addr, &n->address))
- update_node_udp(n, &addr);
}
void handle_device_data(void *data, int flags) {
More information about the tinc-devel
mailing list