changeset 3271:4bb2a0229796

6984182: Setting SO_RCVBUF/SO_SNDBUF to larger than tcp_max_buf fails on Solaris 11 if kernel params changed Reviewed-by: alanb, chegar
author michaelm
date Mon, 22 Nov 2010 16:09:31 +0000
parents 892c54251ac8
children 4b93d39eb352
files src/solaris/native/java/net/net_util_md.c
diffstat 1 files changed, 83 insertions(+), 13 deletions(-) [+]
line wrap: on
line diff
--- a/src/solaris/native/java/net/net_util_md.c	Fri Nov 19 13:35:07 2010 +0000
+++ b/src/solaris/native/java/net/net_util_md.c	Mon Nov 22 16:09:31 2010 +0000
@@ -33,6 +33,7 @@
 #include <netdb.h>
 #include <stdlib.h>
 #include <dlfcn.h>
+#include <values.h>
 
 #ifdef __solaris__
 #include <sys/sockio.h>
@@ -75,17 +76,17 @@
 #endif
 
 #ifdef __solaris__
-static int init_max_buf;
+static int init_tcp_max_buf, init_udp_max_buf;
 static int tcp_max_buf;
 static int udp_max_buf;
 
 /*
  * Get the specified parameter from the specified driver. The value
  * 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
-getParam(char *driver, char *param, int dflt)
+getParam(char *driver, char *param)
 {
     struct strioctl stri;
     char buf [64];
@@ -94,7 +95,7 @@
 
     s = open (driver, O_RDWR);
     if (s < 0) {
-        return dflt;
+        return -1;
     }
     strncpy (buf, param, sizeof(buf));
     stri.ic_cmd = ND_GET;
@@ -102,13 +103,64 @@
     stri.ic_dp = buf;
     stri.ic_len = sizeof(buf);
     if (ioctl (s, I_STR, &stri) < 0) {
-        value = dflt;
+        value = -1;
     } else {
         value = atoi(buf);
     }
     close (s);
     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
 
 #ifdef __linux__
@@ -1148,7 +1200,6 @@
     return rv;
 }
 
-
 /*
  * Wrapper for setsockopt system routine - performs any
  * necessary pre/post processing to deal with OS specific
@@ -1212,7 +1263,7 @@
 #ifdef __solaris__
     if (level == SOL_SOCKET) {
         if (opt == SO_SNDBUF || opt == SO_RCVBUF) {
-            int sotype, arglen;
+            int sotype=0, arglen;
             int *bufsize, maxbuf;
             int ret;
 
@@ -1223,18 +1274,37 @@
 
             /* 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);
             if (getsockopt(fd, SOL_SOCKET, SO_TYPE, (void *)&sotype,
                            &arglen) < 0) {
                 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;
             bufsize = (int *)arg;
             if (*bufsize > maxbuf) {