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