c88997
From a58f31659a924c59f6342d79d2c19ee956453d82 Mon Sep 17 00:00:00 2001
c88997
From: Mark Andrews <marka@isc.org>
c88997
Date: Sat, 18 Oct 2014 12:40:13 +1100
c88997
Subject: [PATCH 2/2] 3980.   [bug]           Improve --with-tuning=large by
c88997
 self tuning of SO_RCVBUF                         size. [RT #37187]
c88997
c88997
(cherry picked from commit 871f3c8beeb2134b17414ec167b90a57adb8e122)
c88997
---
c88997
 lib/isc/unix/socket.c | 66 +++++++++++++++++++++++++++++++++++++++++++++++----
c88997
 1 file changed, 61 insertions(+), 5 deletions(-)
c88997
c88997
diff --git a/lib/isc/unix/socket.c b/lib/isc/unix/socket.c
c88997
index af0c3bc..90953ff 100644
c88997
--- a/lib/isc/unix/socket.c
c88997
+++ b/lib/isc/unix/socket.c
c88997
@@ -2245,6 +2245,62 @@ free_socket(isc__socket_t **socketp) {
c88997
 	*socketp = NULL;
c88997
 }
c88997
 
c88997
+#ifdef SO_RCVBUF
c88997
+static isc_once_t	rcvbuf_once = ISC_ONCE_INIT;
c88997
+static int		rcvbuf = RCVBUFSIZE;
c88997
+
c88997
+static void
c88997
+set_rcvbuf(void) {
c88997
+	int fd;
c88997
+	int max = rcvbuf, min;
c88997
+	ISC_SOCKADDR_LEN_T len;
c88997
+
c88997
+	fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
c88997
+#if defined(ISC_PLATFORM_HAVEIPV6)
c88997
+	if (fd == -1) {
c88997
+		switch (errno) {
c88997
+		case EPROTONOSUPPORT:
c88997
+		case EPFNOSUPPORT:
c88997
+		case EAFNOSUPPORT:
c88997
+		/*
c88997
+		 * Linux 2.2 (and maybe others) return EINVAL instead of
c88997
+		 * EAFNOSUPPORT.
c88997
+		 */
c88997
+		case EINVAL:
c88997
+			fd = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
c88997
+			break;
c88997
+		}
c88997
+	}
c88997
+#endif
c88997
+	if (fd == -1)
c88997
+		return;
c88997
+
c88997
+	len = sizeof(min);
c88997
+	if (getsockopt(fd, SOL_SOCKET, SO_RCVBUF, (void *)&min, &len) >= 0 &&
c88997
+	    min < rcvbuf) {
c88997
+ again:
c88997
+		if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (void *)&rcvbuf,
c88997
+			       sizeof(rcvbuf)) == -1) {
c88997
+			if (errno == ENOBUFS && rcvbuf > min) {
c88997
+				max = rcvbuf - 1;
c88997
+				rcvbuf = (rcvbuf + min) / 2;
c88997
+				goto again;
c88997
+			} else {
c88997
+				rcvbuf = min;
c88997
+				goto cleanup;
c88997
+			}
c88997
+		} else
c88997
+			min = rcvbuf;
c88997
+		if (min != max) {
c88997
+			rcvbuf = max;
c88997
+			goto again;
c88997
+		}
c88997
+	}
c88997
+ cleanup:
c88997
+	close (fd);
c88997
+}
c88997
+#endif
c88997
+
c88997
 #ifdef SO_BSDCOMPAT
c88997
 /*
c88997
  * This really should not be necessary to do.  Having to workout
c88997
@@ -2609,15 +2665,15 @@ opensocket(isc__socketmgr_t *manager, isc__socket_t *sock,
c88997
 #if defined(SO_RCVBUF)
c88997
 		optlen = sizeof(size);
c88997
 		if (getsockopt(sock->fd, SOL_SOCKET, SO_RCVBUF,
c88997
-			       (void *)&size, &optlen) >= 0 &&
c88997
-		     size < RCVBUFSIZE) {
c88997
-			size = RCVBUFSIZE;
c88997
+			       (void *)&size, &optlen) >= 0 && size < rcvbuf) {
c88997
+			RUNTIME_CHECK(isc_once_do(&rcvbuf_once,
c88997
+						  set_rcvbuf) == ISC_R_SUCCESS);
c88997
 			if (setsockopt(sock->fd, SOL_SOCKET, SO_RCVBUF,
c88997
-				       (void *)&size, sizeof(size)) == -1) {
c88997
+			       (void *)&rcvbuf, sizeof(rcvbuf)) == -1) {
c88997
 				isc__strerror(errno, strbuf, sizeof(strbuf));
c88997
 				UNEXPECTED_ERROR(__FILE__, __LINE__,
c88997
 					"setsockopt(%d, SO_RCVBUF, %d) %s: %s",
c88997
-					sock->fd, size,
c88997
+					sock->fd, rcvbuf,
c88997
 					isc_msgcat_get(isc_msgcat,
c88997
 						       ISC_MSGSET_GENERAL,
c88997
 						       ISC_MSG_FAILED,
c88997
-- 
c88997
2.9.5
c88997