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.misc.JavaIOFileDescriptorAccess;
  36 import jdk.internal.misc.SharedSecrets;
  37 import java.lang.annotation.Native;
  38 
  39 /**
  40  * Defines socket options specific to rsocket.
  41  * The rsocket implementation is on Linux only.
  42  *
  43  * @since 12
  44  */
  45 public final class RdmaSocketOptions {
  46 
  47     private static class RdmaSocketOption<T> implements SocketOption<T> {
  48         private final String name;
  49         private final Class<T> type;
  50         RdmaSocketOption(String name, Class<T> type) {
  51             this.name = name;
  52             this.type = type;
  53         }
  54         @Override public String name() { return name; }
  55         @Override public Class<T> type() { return type; }
  56         @Override public String toString() { return name; }
  57     }
  58 
  59     private RdmaSocketOptions() { }
  60 
  61     /**
  62      * Set the integer size of the underlying RDMA send queue used by the
  63      * platform for network I/O.
  64      *
  65      * Valid for RDMA based TCP sockets: RdmaSocketImpl
  66      */
  67     public static final SocketOption<Integer> RDMA_SQSIZE = new
  68         RdmaSocketOption<Integer>("RDMA_SQSIZE", Integer.class);
  69 
  70     /**
  71      * Set the integer size of the underlying RDMA receive queue used by the
  72      * platform for network I/O.
  73      *
  74      * Valid for RMDA based TCP sockets: RdmaSocketImpl
  75      */
  76     public static final SocketOption<Integer> RDMA_RQSIZE = new
  77         RdmaSocketOption<Integer>("RDMA_RQSIZE", Integer.class);
  78 
  79     /**
  80      * Set the integer size of the underlying RDMA inline data used by the
  81      * platform for network I/O.
  82      *
  83      * Valid for RMDA based TCP sockets: RdmaSocketImpl
  84      */
  85     public static final SocketOption<Integer> RDMA_INLINE = new
  86         RdmaSocketOption<Integer>("RDMA_INLINE", Integer.class);
  87 
  88     @Native private static final int SQSIZE = 0x3001;
  89 
  90     @Native private static final int RQSIZE = 0x3002;
  91 
  92     @Native private static final int INLINE = 0x3003;
  93 
  94     private static final PlatformRdmaSocketOptions platformRdmaSocketOptions =
  95             PlatformRdmaSocketOptions.get();
  96 
  97     private static final boolean rdmaSocketSupported =
  98             platformRdmaSocketOptions.rdmaSocketSupported();
  99 
 100     private static final Set<SocketOption<?>> rdmaOptions = options();
 101 
 102     static Set<SocketOption<?>> options() {
 103         if (rdmaSocketSupported)
 104             return Set.of(RDMA_SQSIZE, RDMA_RQSIZE, RDMA_INLINE);
 105         else
 106             return Collections.<SocketOption<?>>emptySet();
 107     }
 108 
 109     static {
 110         sun.net.ext.RdmaSocketOptions.register(
 111                 new sun.net.ext.RdmaSocketOptions(rdmaOptions) {
 112 
 113             @Override
 114             public void setOption(FileDescriptor fd,
 115                                   SocketOption<?> option,
 116                                   Object value)
 117                 throws SocketException
 118             {
 119                 SecurityManager sm = System.getSecurityManager();
 120                 if (sm != null)
 121                     sm.checkPermission(new NetworkPermission("setOption." + option.name()));
 122 
 123                 if (fd == null || !fd.valid())
 124                     throw new SocketException("socket closed");
 125 
 126                 if ((option == RDMA_SQSIZE) || (option == RDMA_RQSIZE)
 127                     || (option == RDMA_INLINE)) {
 128                     assert rdmaSocketSupported;
 129 
 130                     int val;
 131                     int opt;
 132                     if (option == RDMA_SQSIZE) {
 133                         if (value == null || (!(value instanceof Integer)))
 134                             throw new SocketException("Bad parameter for RDMA_SQSIZE");
 135                         val = ((Integer) value).intValue();
 136                         opt = SQSIZE;
 137                         if (val < 0)
 138                             throw new IllegalArgumentException("send queue size < 0");
 139                     } else if (option == RDMA_RQSIZE) {
 140                         if (value == null || (!(value instanceof Integer)))
 141                             throw new SocketException("Bad parameter for RDMA_RQSIZE");
 142                         val = ((Integer) value).intValue();
 143                         opt = RQSIZE;
 144                         if (val < 0)
 145                             throw new IllegalArgumentException("receive queue size < 0");
 146                     } else if (option == RDMA_INLINE) {
 147                         if (value == null || (!(value instanceof Integer)))
 148                             throw new SocketException("Bad parameter for RDMA_INLINE");
 149                         val = ((Integer) value).intValue();
 150                         opt = INLINE;
 151                         if (val < 0)
 152                             throw new IllegalArgumentException("inline size < 0");
 153                     } else {
 154                         throw new SocketException("unrecognized RDMA socket option: " + option);
 155                     }
 156 
 157                     setSockOpt(fd, opt, val);
 158 
 159                 } else {
 160                     throw new InternalError("Unexpected option " + option);
 161                 }
 162             }
 163 
 164             @Override
 165             public Object getOption(FileDescriptor fd,
 166                                     SocketOption<?> option)
 167                 throws SocketException
 168             {
 169                 SecurityManager sm = System.getSecurityManager();
 170                 if (sm != null)
 171                     sm.checkPermission(new NetworkPermission("getOption." + option.name()));
 172 
 173                 if (fd == null || !fd.valid())
 174                     throw new SocketException("socket closed");
 175 
 176                 if ((option == RDMA_SQSIZE) || (option == RDMA_RQSIZE)
 177                     || (option == RDMA_INLINE)) {
 178                     assert rdmaSocketSupported;
 179                     int opt;
 180                     if (option == RDMA_SQSIZE) {
 181                         opt = SQSIZE;
 182                     } else if (option == RDMA_RQSIZE) {
 183                         opt = RQSIZE;
 184                     } else {
 185                         opt = INLINE;
 186                     }
 187                     return getSockOpt(fd, opt);
 188                 } else {
 189                     throw new InternalError("Unexpected option " + option);
 190                 }
 191             }
 192         });
 193     }
 194 
 195     private static final JavaIOFileDescriptorAccess fdAccess =
 196             SharedSecrets.getJavaIOFileDescriptorAccess();
 197 
 198     private static void setSockOpt(FileDescriptor fd, int opt, int val)
 199         throws SocketException
 200     {
 201         platformRdmaSocketOptions.setSockOpt(fdAccess.get(fd), opt, val);
 202     }
 203 
 204     private static int getSockOpt(FileDescriptor fd, int opt)
 205         throws SocketException
 206     {
 207         return platformRdmaSocketOptions.getSockOpt(fdAccess.get(fd), opt);
 208     }
 209 
 210     static class PlatformRdmaSocketOptions {
 211 
 212         protected PlatformRdmaSocketOptions() {}
 213 
 214         @SuppressWarnings("unchecked")
 215         private static PlatformRdmaSocketOptions newInstance(String cn) {
 216             Class<PlatformRdmaSocketOptions> c;
 217             try {
 218                 c = (Class<PlatformRdmaSocketOptions>)Class.forName(cn);
 219                 return c.getConstructor(new Class<?>[] { }).newInstance();
 220             } catch (ReflectiveOperationException x) {
 221                 throw new AssertionError(x);
 222             }
 223         }
 224 
 225         private static PlatformRdmaSocketOptions create() {
 226             String osname = AccessController.doPrivileged(
 227                     new PrivilegedAction<String>() {
 228                         public String run() {
 229                             return System.getProperty("os.name");
 230                         }
 231                     });
 232             if ("Linux".equals(osname))
 233                 return newInstance("jdk.net.LinuxRdmaSocketOptions");
 234             return new PlatformRdmaSocketOptions();
 235         }
 236 
 237         private static final PlatformRdmaSocketOptions instance = create();
 238 
 239         static PlatformRdmaSocketOptions get() {
 240             return instance;
 241         }
 242 
 243         void setSockOpt(int fd, int opt, int value)
 244             throws SocketException
 245         {
 246             throw new UnsupportedOperationException("unsupported socket option");
 247         }
 248 
 249         int getSockOpt(int fd, int opt) throws SocketException {
 250             throw new UnsupportedOperationException("unsupported socket option");
 251         }
 252 
 253         boolean rdmaSocketSupported() {
 254             return false;
 255         }
 256     }
 257 }