1 /* 2 * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package jdk.net; 27 28 import java.io.FileDescriptor; 29 import java.net.SocketException; 30 import java.net.SocketOption; 31 import java.security.AccessController; 32 import java.security.PrivilegedAction; 33 import java.util.Collections; 34 import java.util.Set; 35 import jdk.internal.access.JavaIOFileDescriptorAccess; 36 import jdk.internal.access.SharedSecrets; 37 import java.lang.annotation.Native; 38 39 /** 40 * Defines socket options specific to RDMA-based TCP sockets and channels. 41 * 42 * @since 12 43 */ 44 public final class RdmaSocketOptions { 45 46 private static class RdmaSocketOption<T> implements SocketOption<T> { 47 private final String name; 48 private final Class<T> type; 49 RdmaSocketOption(String name, Class<T> type) { 50 this.name = name; 51 this.type = type; 52 } 53 @Override public String name() { return name; } 54 @Override public Class<T> type() { return type; } 55 @Override public String toString() { return name; } 56 } 57 58 private RdmaSocketOptions() { } 59 60 /** 61 * The integer size of the underlying RDMA send queue used by the 62 * platform for network I/O. 63 * 64 * The initial/default size of the RDMA send queue and the range of 65 * allowable values is system and device dependent although a negative 66 * size is not allowed. 67 * 68 * <p> An implementation allows this socket option to be set before the 69 * socket is bound or connected. Changing the value of this socket option 70 * after the socket is bound or connected has no effect. 71 * 72 * Valid for RDMA-based TCP sockets. 73 */ 74 public static final SocketOption<Integer> RDMA_SQSIZE = new 75 RdmaSocketOption<Integer>("RDMA_SQSIZE", Integer.class); 76 77 /** 78 * The integer size of the underlying RDMA receive queue used by the 79 * platform for network I/O. 80 * 81 * The initial/default size of the RDMA receive queue and the range of 82 * allowable values is system and device dependent although a negative 83 * size is not allowed. 84 * 85 * <p> An implementation allows this socket option to be set before the 86 * socket is bound or connected. Changing the value of this socket option 87 * after the socket is bound or connected has no effect. 88 * 89 * Valid for RDMA-based TCP sockets. 90 */ 91 public static final SocketOption<Integer> RDMA_RQSIZE = new 92 RdmaSocketOption<Integer>("RDMA_RQSIZE", Integer.class); 93 94 /** 95 * The integer size of the underlying RDMA inline data used by the 96 * platform for network I/O. 97 * 98 * The initial/default size of the RDMA inline data and the range of 99 * allowable values is system and device dependent although a negative 100 * size is not allowed. 101 * 102 * <p> An implementation allows this socket option to be set before the 103 * socket is bound or connected. Changing the value of this socket option 104 * after the socket is bound or connected has no effect. 105 * 106 * Valid for RDMA-based TCP sockets. 107 */ 108 public static final SocketOption<Integer> RDMA_INLINE = new 109 RdmaSocketOption<Integer>("RDMA_INLINE", Integer.class); 110 111 @Native private static final int SQSIZE = 0x3001; 112 113 @Native private static final int RQSIZE = 0x3002; 114 115 @Native private static final int INLINE = 0x3003; 116 117 private static final PlatformRdmaSocketOptions platformRdmaSocketOptions = 118 PlatformRdmaSocketOptions.get(); 119 120 private static final boolean rdmaSocketSupported = 121 platformRdmaSocketOptions.rdmaSocketSupported(); 122 123 private static final Set<SocketOption<?>> rdmaOptions = options(); 124 125 static Set<SocketOption<?>> options() { 126 if (rdmaSocketSupported) 127 return Set.of(RDMA_SQSIZE, RDMA_RQSIZE, RDMA_INLINE); 128 else 129 return Collections.<SocketOption<?>>emptySet(); 130 } 131 132 static { 133 sun.net.ext.RdmaSocketOptions.register( 134 new sun.net.ext.RdmaSocketOptions(rdmaOptions) { 135 136 @Override 137 public void setOption(FileDescriptor fd, 138 SocketOption<?> option, Object value) 139 throws SocketException { 140 SecurityManager sm = System.getSecurityManager(); 141 if (sm != null) 142 sm.checkPermission(new NetworkPermission("setOption." 143 + option.name())); 144 145 if (fd == null || !fd.valid()) 146 throw new SocketException("socket closed"); 147 148 if ((option == RDMA_SQSIZE) || (option == RDMA_RQSIZE) 149 || (option == RDMA_INLINE)) { 150 assert rdmaSocketSupported; 151 152 int val; 153 int opt; 154 if (option == RDMA_SQSIZE) { 155 if (value == null || (!(value instanceof Integer))) 156 throw new SocketException( 157 "Bad parameter for RDMA_SQSIZE"); 158 val = ((Integer) value).intValue(); 159 opt = SQSIZE; 160 if (val < 0) 161 throw new IllegalArgumentException( 162 "send queue size < 0"); 163 } else if (option == RDMA_RQSIZE) { 164 if (value == null || (!(value instanceof Integer))) 165 throw new SocketException( 166 "Bad parameter for RDMA_RQSIZE"); 167 val = ((Integer) value).intValue(); 168 opt = RQSIZE; 169 if (val < 0) 170 throw new IllegalArgumentException( 171 "receive queue size < 0"); 172 } else if (option == RDMA_INLINE) { 173 if (value == null || (!(value instanceof Integer))) 174 throw new SocketException( 175 "Bad parameter for RDMA_INLINE"); 176 val = ((Integer) value).intValue(); 177 opt = INLINE; 178 if (val < 0) 179 throw new IllegalArgumentException( 180 "inline size < 0"); 181 } else { 182 throw new SocketException( 183 "unrecognized RDMA socket option: " + option); 184 } 185 186 setSockOpt(fd, opt, val); 187 188 } else { 189 throw new InternalError("Unexpected option " + option); 190 } 191 } 192 193 @Override 194 public Object getOption(FileDescriptor fd, 195 SocketOption<?> option) throws SocketException { 196 SecurityManager sm = System.getSecurityManager(); 197 if (sm != null) 198 sm.checkPermission(new NetworkPermission("getOption." 199 + option.name())); 200 201 if (fd == null || !fd.valid()) 202 throw new SocketException("socket closed"); 203 204 if ((option == RDMA_SQSIZE) || (option == RDMA_RQSIZE) 205 || (option == RDMA_INLINE)) { 206 assert rdmaSocketSupported; 207 int opt; 208 if (option == RDMA_SQSIZE) { 209 opt = SQSIZE; 210 } else if (option == RDMA_RQSIZE) { 211 opt = RQSIZE; 212 } else { 213 opt = INLINE; 214 } 215 return getSockOpt(fd, opt); 216 } else { 217 throw new InternalError("Unexpected option " + option); 218 } 219 } 220 }); 221 } 222 223 private static final JavaIOFileDescriptorAccess fdAccess = 224 SharedSecrets.getJavaIOFileDescriptorAccess(); 225 226 private static void setSockOpt(FileDescriptor fd, int opt, int val) 227 throws SocketException { 228 platformRdmaSocketOptions.setSockOpt(fdAccess.get(fd), opt, val); 229 } 230 231 private static int getSockOpt(FileDescriptor fd, int opt) 232 throws SocketException { 233 return platformRdmaSocketOptions.getSockOpt(fdAccess.get(fd), opt); 234 } 235 236 static class PlatformRdmaSocketOptions { 237 238 protected PlatformRdmaSocketOptions() {} 239 240 @SuppressWarnings("unchecked") 241 private static PlatformRdmaSocketOptions newInstance(String cn) { 242 Class<PlatformRdmaSocketOptions> c; 243 try { 244 c = (Class<PlatformRdmaSocketOptions>)Class.forName(cn); 245 return c.getConstructor(new Class<?>[] { }).newInstance(); 246 } catch (ReflectiveOperationException x) { 247 if (x.getCause() instanceof UnsupportedOperationException) 248 throw (UnsupportedOperationException)x.getCause(); 249 throw new AssertionError(x); 250 } 251 } 252 253 private static PlatformRdmaSocketOptions create() { 254 String osname = AccessController.doPrivileged( 255 new PrivilegedAction<String>() { 256 public String run() { 257 return System.getProperty("os.name"); 258 } 259 }); 260 if ("Linux".equals(osname)) { 261 try { 262 return newInstance("jdk.net.LinuxRdmaSocketOptions"); 263 } catch (UnsupportedOperationException uoe) { 264 return new PlatformRdmaSocketOptions(); 265 } 266 } 267 return new PlatformRdmaSocketOptions(); 268 } 269 270 private static final PlatformRdmaSocketOptions instance = create(); 271 272 static PlatformRdmaSocketOptions get() { 273 return instance; 274 } 275 276 void setSockOpt(int fd, int opt, int value) throws SocketException { 277 throw new UnsupportedOperationException( 278 "unsupported socket option"); 279 } 280 281 int getSockOpt(int fd, int opt) throws SocketException { 282 throw new UnsupportedOperationException( 283 "unsupported socket option"); 284 } 285 286 boolean rdmaSocketSupported() { 287 return false; 288 } 289 } 290 }