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 }