[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