提交 eedb3a79 编写于 作者: M michaelm

6984182: Setting SO_RCVBUF/SO_SNDBUF to larger than tcp_max_buf fails on...

6984182: Setting SO_RCVBUF/SO_SNDBUF to larger than tcp_max_buf fails on Solaris 11 if kernel params changed
Reviewed-by: alanb, chegar
上级 f541c96a
...@@ -33,6 +33,7 @@ ...@@ -33,6 +33,7 @@
#include <netdb.h> #include <netdb.h>
#include <stdlib.h> #include <stdlib.h>
#include <dlfcn.h> #include <dlfcn.h>
#include <values.h>
#ifdef __solaris__ #ifdef __solaris__
#include <sys/sockio.h> #include <sys/sockio.h>
...@@ -75,17 +76,17 @@ getnameinfo_f getnameinfo_ptr = NULL; ...@@ -75,17 +76,17 @@ getnameinfo_f getnameinfo_ptr = NULL;
#endif #endif
#ifdef __solaris__ #ifdef __solaris__
static int init_max_buf; static int init_tcp_max_buf, init_udp_max_buf;
static int tcp_max_buf; static int tcp_max_buf;
static int udp_max_buf; static int udp_max_buf;
/* /*
* Get the specified parameter from the specified driver. The value * Get the specified parameter from the specified driver. The value
* of the parameter is assumed to be an 'int'. If the parameter * of the parameter is assumed to be an 'int'. If the parameter
* cannot be obtained return the specified default value. * cannot be obtained return -1
*/ */
static int static int
getParam(char *driver, char *param, int dflt) getParam(char *driver, char *param)
{ {
struct strioctl stri; struct strioctl stri;
char buf [64]; char buf [64];
...@@ -94,7 +95,7 @@ getParam(char *driver, char *param, int dflt) ...@@ -94,7 +95,7 @@ getParam(char *driver, char *param, int dflt)
s = open (driver, O_RDWR); s = open (driver, O_RDWR);
if (s < 0) { if (s < 0) {
return dflt; return -1;
} }
strncpy (buf, param, sizeof(buf)); strncpy (buf, param, sizeof(buf));
stri.ic_cmd = ND_GET; stri.ic_cmd = ND_GET;
...@@ -102,13 +103,64 @@ getParam(char *driver, char *param, int dflt) ...@@ -102,13 +103,64 @@ getParam(char *driver, char *param, int dflt)
stri.ic_dp = buf; stri.ic_dp = buf;
stri.ic_len = sizeof(buf); stri.ic_len = sizeof(buf);
if (ioctl (s, I_STR, &stri) < 0) { if (ioctl (s, I_STR, &stri) < 0) {
value = dflt; value = -1;
} else { } else {
value = atoi(buf); value = atoi(buf);
} }
close (s); close (s);
return value; return value;
} }
/*
* Iterative way to find the max value that SO_SNDBUF or SO_RCVBUF
* for Solaris versions that do not support the ioctl() in getParam().
* Ugly, but only called once (for each sotype).
*
* As an optimisation, we make a guess using the default values for Solaris
* assuming they haven't been modified with ndd.
*/
#define MAX_TCP_GUESS 1024 * 1024
#define MAX_UDP_GUESS 2 * 1024 * 1024
#define FAIL_IF_NOT_ENOBUFS if (errno != ENOBUFS) return -1
static int findMaxBuf(int fd, int opt, int sotype) {
int a = 0;
int b = MAXINT;
int initial_guess;
int limit = -1;
if (sotype == SOCK_DGRAM) {
initial_guess = MAX_UDP_GUESS;
} else {
initial_guess = MAX_TCP_GUESS;
}
if (setsockopt(fd, SOL_SOCKET, opt, &initial_guess, sizeof(int)) == 0) {
initial_guess++;
if (setsockopt(fd, SOL_SOCKET, opt, &initial_guess,sizeof(int)) < 0) {
FAIL_IF_NOT_ENOBUFS;
return initial_guess - 1;
}
a = initial_guess;
} else {
FAIL_IF_NOT_ENOBUFS;
b = initial_guess - 1;
}
do {
int mid = a + (b-a)/2;
if (setsockopt(fd, SOL_SOCKET, opt, &mid, sizeof(int)) == 0) {
limit = mid;
a = mid + 1;
} else {
FAIL_IF_NOT_ENOBUFS;
b = mid - 1;
}
} while (b >= a);
return limit;
}
#endif #endif
#ifdef __linux__ #ifdef __linux__
...@@ -1148,7 +1200,6 @@ NET_GetSockOpt(int fd, int level, int opt, void *result, ...@@ -1148,7 +1200,6 @@ NET_GetSockOpt(int fd, int level, int opt, void *result,
return rv; return rv;
} }
/* /*
* Wrapper for setsockopt system routine - performs any * Wrapper for setsockopt system routine - performs any
* necessary pre/post processing to deal with OS specific * necessary pre/post processing to deal with OS specific
...@@ -1212,7 +1263,7 @@ NET_SetSockOpt(int fd, int level, int opt, const void *arg, ...@@ -1212,7 +1263,7 @@ NET_SetSockOpt(int fd, int level, int opt, const void *arg,
#ifdef __solaris__ #ifdef __solaris__
if (level == SOL_SOCKET) { if (level == SOL_SOCKET) {
if (opt == SO_SNDBUF || opt == SO_RCVBUF) { if (opt == SO_SNDBUF || opt == SO_RCVBUF) {
int sotype, arglen; int sotype=0, arglen;
int *bufsize, maxbuf; int *bufsize, maxbuf;
int ret; int ret;
...@@ -1223,18 +1274,37 @@ NET_SetSockOpt(int fd, int level, int opt, const void *arg, ...@@ -1223,18 +1274,37 @@ NET_SetSockOpt(int fd, int level, int opt, const void *arg,
/* Exceeded system limit so clamp and retry */ /* Exceeded system limit so clamp and retry */
if (!init_max_buf) {
tcp_max_buf = getParam("/dev/tcp", "tcp_max_buf", 1024*1024);
udp_max_buf = getParam("/dev/udp", "udp_max_buf", 2048*1024);
init_max_buf = 1;
}
arglen = sizeof(sotype); arglen = sizeof(sotype);
if (getsockopt(fd, SOL_SOCKET, SO_TYPE, (void *)&sotype, if (getsockopt(fd, SOL_SOCKET, SO_TYPE, (void *)&sotype,
&arglen) < 0) { &arglen) < 0) {
return -1; return -1;
} }
/*
* We try to get tcp_maxbuf (and udp_max_buf) using
* an ioctl() that isn't available on all versions of Solaris.
* If that fails, we use the search algorithm in findMaxBuf()
*/
if (!init_tcp_max_buf && sotype == SOCK_STREAM) {
tcp_max_buf = getParam("/dev/tcp", "tcp_max_buf");
if (tcp_max_buf == -1) {
tcp_max_buf = findMaxBuf(fd, opt, SOCK_STREAM);
if (tcp_max_buf == -1) {
return -1;
}
}
init_tcp_max_buf = 1;
} else if (!init_udp_max_buf && sotype == SOCK_DGRAM) {
udp_max_buf = getParam("/dev/udp", "udp_max_buf");
if (udp_max_buf == -1) {
udp_max_buf = findMaxBuf(fd, opt, SOCK_DGRAM);
if (udp_max_buf == -1) {
return -1;
}
}
init_udp_max_buf = 1;
}
maxbuf = (sotype == SOCK_STREAM) ? tcp_max_buf : udp_max_buf; maxbuf = (sotype == SOCK_STREAM) ? tcp_max_buf : udp_max_buf;
bufsize = (int *)arg; bufsize = (int *)arg;
if (*bufsize > maxbuf) { if (*bufsize > maxbuf) {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册