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, 139 Object value) 140 throws SocketException 141 { 142 SecurityManager sm = System.getSecurityManager(); 143 if (sm != null) 144 sm.checkPermission(new NetworkPermission("setOption." + option.name())); 145 146 if (fd == null || !fd.valid()) 147 throw new SocketException("socket closed"); 148 149 if ((option == RDMA_SQSIZE) || (option == RDMA_RQSIZE) 150 || (option == RDMA_INLINE)) { 151 assert rdmaSocketSupported; 152 153 int val; 154 int opt; 155 if (option == RDMA_SQSIZE) { 156 if (value == null || (!(value instanceof Integer))) 157 throw new SocketException("Bad parameter for RDMA_SQSIZE"); 158 val = ((Integer) value).intValue(); 159 opt = SQSIZE; 160 if (val < 0) 161 throw new IllegalArgumentException("send queue size < 0"); 162 } else if (option == RDMA_RQSIZE) { 163 if (value == null || (!(value instanceof Integer))) 164 throw new SocketException("Bad parameter for RDMA_RQSIZE"); 165 val = ((Integer) value).intValue(); 166 opt = RQSIZE; 167 if (val < 0) 168 throw new IllegalArgumentException("receive queue size < 0"); 169 } else if (option == RDMA_INLINE) { 170 if (value == null || (!(value instanceof Integer))) 171 throw new SocketException("Bad parameter for RDMA_INLINE"); 172 val = ((Integer) value).intValue(); 173 opt = INLINE; 174 if (val < 0) 175 throw new IllegalArgumentException("inline size < 0"); 176 } else { 177 throw new SocketException("unrecognized RDMA socket option: " + option); 178 } 179 180 setSockOpt(fd, opt, val); 181 182 } else { 183 throw new InternalError("Unexpected option " + option); 184 } 185 } 186 187 @Override 188 public Object getOption(FileDescriptor fd, 189 SocketOption<?> option) 190 throws SocketException 191 { 192 SecurityManager sm = System.getSecurityManager(); 193 if (sm != null) 194 sm.checkPermission(new NetworkPermission("getOption." + option.name())); 195 196 if (fd == null || !fd.valid()) 197 throw new SocketException("socket closed"); 198 199 if ((option == RDMA_SQSIZE) || (option == RDMA_RQSIZE) 200 || (option == RDMA_INLINE)) { 201 assert rdmaSocketSupported; 202 int opt; 203 if (option == RDMA_SQSIZE) { 204 opt = SQSIZE; 205 } else if (option == RDMA_RQSIZE) { 206 opt = RQSIZE; 207 } else { 208 opt = INLINE; 209 } 210 return getSockOpt(fd, opt); 211 } else { 212 throw new InternalError("Unexpected option " + option); 213 } 214 } 215 }); 216 } 217 218 private static final JavaIOFileDescriptorAccess fdAccess = 219 SharedSecrets.getJavaIOFileDescriptorAccess(); 220 221 private static void setSockOpt(FileDescriptor fd, int opt, int val) 222 throws SocketException 223 { 224 platformRdmaSocketOptions.setSockOpt(fdAccess.get(fd), opt, val); 225 } 226 227 private static int getSockOpt(FileDescriptor fd, int opt) 228 throws SocketException 229 { 230 return platformRdmaSocketOptions.getSockOpt(fdAccess.get(fd), opt); 231 } 232 233 static class PlatformRdmaSocketOptions { 234 235 protected PlatformRdmaSocketOptions() {} 236 237 @SuppressWarnings("unchecked") 238 private static PlatformRdmaSocketOptions newInstance(String cn) { 239 Class<PlatformRdmaSocketOptions> c; 240 try { 241 c = (Class<PlatformRdmaSocketOptions>)Class.forName(cn); 242 return c.getConstructor(new Class<?>[] { }).newInstance(); 243 } catch (ReflectiveOperationException x) { 244 throw new AssertionError(x); 245 } 246 } 247 248 private static PlatformRdmaSocketOptions create() { 249 String osname = AccessController.doPrivileged( 250 new PrivilegedAction<String>() { 251 public String run() { 252 return System.getProperty("os.name"); 253 } 254 }); 255 if ("Linux".equals(osname)) 256 return newInstance("jdk.net.LinuxRdmaSocketOptions"); 257 return new PlatformRdmaSocketOptions(); 258 } 259 260 private static final PlatformRdmaSocketOptions instance = create(); 261 262 static PlatformRdmaSocketOptions get() { 263 return instance; 264 } 265 266 void setSockOpt(int fd, int opt, int value) 267 throws SocketException 268 { 269 throw new UnsupportedOperationException("unsupported socket option"); 270 } 271 272 int getSockOpt(int fd, int opt) throws SocketException { 273 throw new UnsupportedOperationException("unsupported socket option"); 274 } 275 276 boolean rdmaSocketSupported() { 277 return false; 278 } 279 } 280 }