--- old/make/gensrc/GensrcMisc.gmk 2018-10-05 14:14:30.667756881 -0700 +++ new/make/gensrc/GensrcMisc.gmk 2018-10-05 14:14:30.397756878 -0700 @@ -81,6 +81,17 @@ GENSRC_JAVA_BASE += $(GENSRC_SOR_FILE) +ifeq ($(OPENJDK_TARGET_OS), linux) + GENSRC_RSOR_FILE += $(SUPPORT_OUTPUTDIR)/gensrc/jdk.net/jdk/internal/net/rdma/RdmaSocketOptionRegistry.java + + $(GENSRC_RSOR_FILE): \ + $(TOPDIR)/src/jdk.net/linux/classes/jdk/internal/net/rdma/RdmaSocketOptionRegistry.java.template + $(generate-preproc-src) + + GENSRC_JAVA_BASE += $(GENSRC_RSOR_FILE) + +endif + ################################################################################ ifneq ($(OPENJDK_TARGET_OS), windows) --- old/make/lib/Lib-jdk.net.gmk 2018-10-05 14:14:31.230756888 -0700 +++ new/make/lib/Lib-jdk.net.gmk 2018-10-05 14:14:30.957756885 -0700 @@ -32,12 +32,17 @@ $(eval $(call SetupJdkLibrary, BUILD_LIBEXTNET, \ NAME := extnet, \ OPTIMIZATION := LOW, \ - CFLAGS := $(CFLAGS_JDKLIB), \ + CFLAGS := $(CFLAGS_JDKLIB) \ + -I$(SUPPORT_OUTPUTDIR)/headers/java.base \ + -I$(TOPDIR)/src/java.base/share/native/libnet \ + -I$(TOPDIR)/src/java.base/unix/native/libnet \ + -I$(TOPDIR)/src/java.base/share/native/libnio/ch \ + -I$(TOPDIR)/src/java.base/unix/native/libnio/ch, \ LDFLAGS := $(LDFLAGS_JDKLIB) \ $(call SET_SHARED_LIBRARY_ORIGIN), \ LIBS := -ljava, \ LIBS_solaris := -lsocket, \ - LIBS_linux := -ljvm, \ + LIBS_linux := -ljvm -lnet -lnio -lpthread -ldl, \ )) $(BUILD_LIBEXTNET): $(call FindLib, java.base, java) --- old/make/test/JtregNativeJdk.gmk 2018-10-05 14:14:31.880756895 -0700 +++ new/make/test/JtregNativeJdk.gmk 2018-10-05 14:14:31.554756892 -0700 @@ -59,6 +59,7 @@ BUILD_JDK_JTREG_LIBRARIES_LIBS_libDirectIO := -ljava ifeq ($(OPENJDK_TARGET_OS), linux) BUILD_JDK_JTREG_LIBRARIES_LIBS_libInheritedChannel := -ljava + BUILD_JDK_JTREG_LIBRARIES_LIBS_libRsocketTest := -ljava -ldl else ifeq ($(OPENJDK_TARGET_OS), solaris) BUILD_JDK_JTREG_LIBRARIES_LIBS_libInheritedChannel := -ljava endif --- old/src/java.base/share/classes/java/net/ServerSocket.java 2018-10-05 14:14:32.451756902 -0700 +++ new/src/java.base/share/classes/java/net/ServerSocket.java 2018-10-05 14:14:32.179756899 -0700 @@ -76,10 +76,12 @@ private boolean oldImpl = false; /** - * Package-private constructor to create a ServerSocket associated with - * the given SocketImpl. + * Creates a server socket with a user-specified SocketImpl. + * + * @param impl the user-specified SocketImpl + * @since 12 */ - ServerSocket(SocketImpl impl) { + protected ServerSocket(SocketImpl impl) { this.impl = impl; impl.setServerSocket(this); } @@ -990,6 +992,8 @@ private static Set> options; private static boolean optionsSet = false; + private static Set> rdmaOptions; + private static boolean rdmaOptionsSet = false; /** * Returns a set of the socket options supported by this server socket. @@ -1004,16 +1008,29 @@ */ public Set> supportedOptions() { synchronized (ServerSocket.class) { - if (optionsSet) { - return options; - } try { SocketImpl impl = getImpl(); - options = Collections.unmodifiableSet(impl.supportedOptions()); + String implName = impl.getClass().getName(); + if (implName.equals("jdk.internal.net.rdma.RdmaSocketImpl")) { + if (rdmaOptionsSet) + return rdmaOptions; + rdmaOptions = Collections.unmodifiableSet( + impl.supportedOptions()); + rdmaOptionsSet = true; + return rdmaOptions; + } else { + if (optionsSet) + return options; + options = Collections.unmodifiableSet( + impl.supportedOptions()); + optionsSet = true; + return options; + } } catch (IOException e) { options = Collections.emptySet(); + rdmaOptions = Collections.emptySet(); } - optionsSet = true; + //should not reach here return options; } } --- old/src/java.base/share/classes/java/net/Socket.java 2018-10-05 14:14:33.037756909 -0700 +++ new/src/java.base/share/classes/java/net/Socket.java 2018-10-05 14:14:32.763756906 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1995, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1791,6 +1791,8 @@ private static Set> options; private static boolean optionsSet = false; + private static Set> rdmaOptions; + private static boolean rdmaOptionsSet = false; /** * Returns a set of the socket options supported by this socket. @@ -1805,16 +1807,29 @@ */ public Set> supportedOptions() { synchronized (Socket.class) { - if (optionsSet) { - return options; - } try { SocketImpl impl = getImpl(); - options = Collections.unmodifiableSet(impl.supportedOptions()); + String implName = impl.getClass().getName(); + if (implName.equals("jdk.internal.net.rdma.RdmaSocketImpl")) { + if (rdmaOptionsSet) + return rdmaOptions; + rdmaOptions = Collections.unmodifiableSet( + impl.supportedOptions()); + rdmaOptionsSet = true; + return rdmaOptions; + } else { + if (optionsSet) + return options; + options = Collections.unmodifiableSet( + impl.supportedOptions()); + optionsSet = true; + return options; + } } catch (IOException e) { options = Collections.emptySet(); + rdmaOptions = Collections.emptySet(); } - optionsSet = true; + //should not reach here return options; } } --- old/src/java.base/share/classes/java/net/SocketImpl.java 2018-10-05 14:14:33.673756917 -0700 +++ new/src/java.base/share/classes/java/net/SocketImpl.java 2018-10-05 14:14:33.397756913 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1995, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,6 +30,8 @@ import java.io.OutputStream; import java.io.FileDescriptor; import java.util.Set; +import jdk.internal.misc.JdkRdmaSocketImplAccess; +import jdk.internal.misc.SharedSecrets; /** * The abstract class {@code SocketImpl} is a common superclass @@ -69,6 +71,9 @@ */ protected int localport; + private static final JdkRdmaSocketImplAccess rsiAccess = + SharedSecrets.getJdkRdmaSocketImplAccess(); + /** * Creates either a stream or a datagram socket. * @@ -281,7 +286,11 @@ } void setSocket(Socket soc) { - this.socket = soc; + String implName = this.getClass().getName(); + if (!implName.equals("jdk.internal.net.rdma.RdmaSocketImpl")) + this.socket = soc; + else + rsiAccess.setSocket(this, soc); } Socket getSocket() { @@ -289,7 +298,11 @@ } void setServerSocket(ServerSocket soc) { - this.serverSocket = soc; + String implName = this.getClass().getName(); + if (!implName.equals("jdk.internal.net.rdma.RdmaSocketImpl")) + this.serverSocket = soc; + else + rsiAccess.setServerSocket(this, soc); } ServerSocket getServerSocket() { --- old/src/java.base/share/classes/jdk/internal/misc/SharedSecrets.java 2018-10-05 14:14:34.314756924 -0700 +++ new/src/java.base/share/classes/jdk/internal/misc/SharedSecrets.java 2018-10-05 14:14:33.973756920 -0700 @@ -73,6 +73,7 @@ private static JavaObjectInputFilterAccess javaObjectInputFilterAccess; private static JavaIORandomAccessFileAccess javaIORandomAccessFileAccess; private static JavaxCryptoSealedObjectAccess javaxCryptoSealedObjectAccess; + private static JdkRdmaSocketImplAccess jdkRdmaSocketImplAccess; public static JavaUtilJarAccess javaUtilJarAccess() { if (javaUtilJarAccess == null) { @@ -336,4 +337,18 @@ } return javaxCryptoSealedObjectAccess; } + + public static void setJdkRdmaSocketImplAccess(JdkRdmaSocketImplAccess jrsia) { + jdkRdmaSocketImplAccess = jrsia; + } + + public static JdkRdmaSocketImplAccess getJdkRdmaSocketImplAccess() { + if (jdkRdmaSocketImplAccess == null) { + try { + Class c = Class.forName("jdk.internal.net.rdma.RdmaSocketImpl"); + unsafe.ensureClassInitialized(c); + } catch (ClassNotFoundException e) {}; + } + return jdkRdmaSocketImplAccess; + } } --- old/src/java.base/share/classes/module-info.java 2018-10-05 14:14:34.916756931 -0700 +++ new/src/java.base/share/classes/module-info.java 2018-10-05 14:14:34.644756928 -0700 @@ -223,7 +223,8 @@ jdk.jfr; exports sun.net to java.net.http, - jdk.naming.dns; + jdk.naming.dns, + jdk.net; exports sun.net.ext to jdk.net; exports sun.net.dns to @@ -243,7 +244,8 @@ jdk.crypto.cryptoki, jdk.net, jdk.sctp, - jdk.unsupported; + jdk.unsupported, + jdk.net; exports sun.nio.cs to jdk.charsets; exports sun.nio.fs to @@ -261,7 +263,8 @@ java.sql.rowset; exports sun.security.action to java.desktop, - java.security.jgss; + java.security.jgss, + jdk.net; exports sun.security.internal.interfaces to jdk.crypto.cryptoki; exports sun.security.internal.spec to @@ -367,4 +370,7 @@ provides java.nio.file.spi.FileSystemProvider with jdk.internal.jrtfs.JrtFileSystemProvider; + + opens java.net to + jdk.net; } --- old/src/java.base/share/classes/sun/nio/ch/IOUtil.java 2018-10-05 14:14:35.488756938 -0700 +++ new/src/java.base/share/classes/sun/nio/ch/IOUtil.java 2018-10-05 14:14:35.218756935 -0700 @@ -43,7 +43,7 @@ private IOUtil() { } // No instantiation - static int write(FileDescriptor fd, ByteBuffer src, long position, + public static int write(FileDescriptor fd, ByteBuffer src, long position, NativeDispatcher nd) throws IOException { @@ -123,7 +123,7 @@ return write(fd, bufs, 0, bufs.length, false, -1, nd); } - static long write(FileDescriptor fd, ByteBuffer[] bufs, int offset, int length, + public static long write(FileDescriptor fd, ByteBuffer[] bufs, int offset, int length, NativeDispatcher nd) throws IOException { @@ -216,7 +216,7 @@ } } - static int read(FileDescriptor fd, ByteBuffer dst, long position, + public static int read(FileDescriptor fd, ByteBuffer dst, long position, NativeDispatcher nd) throws IOException { @@ -286,7 +286,7 @@ return read(fd, bufs, 0, bufs.length, false, -1, nd); } - static long read(FileDescriptor fd, ByteBuffer[] bufs, int offset, int length, + public static long read(FileDescriptor fd, ByteBuffer[] bufs, int offset, int length, NativeDispatcher nd) throws IOException { --- old/src/java.base/share/classes/sun/nio/ch/Net.java 2018-10-05 14:14:36.115756946 -0700 +++ new/src/java.base/share/classes/sun/nio/ch/Net.java 2018-10-05 14:14:35.781756942 -0700 @@ -54,7 +54,7 @@ public class Net { - private Net() { } + protected Net() { } // unspecified protocol family static final ProtocolFamily UNSPEC = new ProtocolFamily() { @@ -79,7 +79,7 @@ /** * Tells whether dual-IPv4/IPv6 sockets should be used. */ - static boolean isIPv6Available() { + protected static boolean isIPv6Available() { if (!checkedIPv6) { isIPv6Available = isIPv6Available0(); checkedIPv6 = true; @@ -150,7 +150,7 @@ return (InetSocketAddress)sa; } - static void translateToSocketException(Exception x) + public static void translateToSocketException(Exception x) throws SocketException { if (x instanceof SocketException) @@ -180,7 +180,7 @@ throw new Error("Untranslated exception", nx); } - static void translateException(Exception x, + public static void translateException(Exception x, boolean unknownHostForUnresolved) throws IOException { @@ -196,7 +196,7 @@ translateToSocketException(x); } - static void translateException(Exception x) + public static void translateException(Exception x) throws IOException { translateException(x, false); @@ -205,7 +205,7 @@ /** * Returns the local address after performing a SecurityManager#checkConnect. */ - static InetSocketAddress getRevealedLocalAddress(InetSocketAddress addr) { + public static InetSocketAddress getRevealedLocalAddress(InetSocketAddress addr) { SecurityManager sm = System.getSecurityManager(); if (addr == null || sm == null) return addr; @@ -220,7 +220,7 @@ return addr; } - static String getRevealedLocalAddressAsString(InetSocketAddress addr) { + public static String getRevealedLocalAddressAsString(InetSocketAddress addr) { return System.getSecurityManager() == null ? addr.toString() : getLoopbackAddress(addr.getPort()).toString(); } --- old/src/java.base/unix/classes/sun/nio/ch/PollSelectorImpl.java 2018-10-05 14:14:36.783756953 -0700 +++ new/src/java.base/unix/classes/sun/nio/ch/PollSelectorImpl.java 2018-10-05 14:14:36.448756949 -0700 @@ -42,7 +42,7 @@ * Selector implementation based on poll */ -class PollSelectorImpl extends SelectorImpl { +public class PollSelectorImpl extends SelectorImpl { // initial capacity of poll array private static final int INITIAL_CAPACITY = 16; @@ -67,7 +67,7 @@ private final Object interruptLock = new Object(); private boolean interruptTriggered; - PollSelectorImpl(SelectorProvider sp) throws IOException { + protected PollSelectorImpl(SelectorProvider sp) throws IOException { super(sp); int size = pollArrayCapacity * SIZE_POLLFD; @@ -133,6 +133,14 @@ } /** + * Protected poll method allows different platform-specific + * native implementations + */ + protected int poll(long pollAddress, int numfds, int timeout) { + return poll0(pollAddress, numfds, timeout); + } + + /** * Process changes to the interest ops. */ private void processUpdateQueue() { @@ -377,7 +385,7 @@ return pollArray.getShort(offset); } - private static native int poll(long pollAddress, int numfds, int timeout); + private static native int poll0(long pollAddress, int numfds, int timeout); static { IOUtil.load(); --- old/src/java.base/unix/classes/sun/nio/ch/SocketDispatcher.java 2018-10-05 14:14:37.444756961 -0700 +++ new/src/java.base/unix/classes/sun/nio/ch/SocketDispatcher.java 2018-10-05 14:14:37.112756957 -0700 @@ -33,29 +33,29 @@ * for read and write operations. */ -class SocketDispatcher extends NativeDispatcher { +public class SocketDispatcher extends NativeDispatcher { - int read(FileDescriptor fd, long address, int len) throws IOException { + protected int read(FileDescriptor fd, long address, int len) throws IOException { return FileDispatcherImpl.read0(fd, address, len); } - long readv(FileDescriptor fd, long address, int len) throws IOException { + protected long readv(FileDescriptor fd, long address, int len) throws IOException { return FileDispatcherImpl.readv0(fd, address, len); } - int write(FileDescriptor fd, long address, int len) throws IOException { + protected int write(FileDescriptor fd, long address, int len) throws IOException { return FileDispatcherImpl.write0(fd, address, len); } - long writev(FileDescriptor fd, long address, int len) throws IOException { + protected long writev(FileDescriptor fd, long address, int len) throws IOException { return FileDispatcherImpl.writev0(fd, address, len); } - void close(FileDescriptor fd) throws IOException { + protected void close(FileDescriptor fd) throws IOException { FileDispatcherImpl.close0(fd); } - void preClose(FileDescriptor fd) throws IOException { + public void preClose(FileDescriptor fd) throws IOException { FileDispatcherImpl.preClose0(fd); } } --- old/src/java.base/unix/native/libnio/ch/PollSelectorImpl.c 2018-10-05 14:14:38.061756969 -0700 +++ new/src/java.base/unix/native/libnio/ch/PollSelectorImpl.c 2018-10-05 14:14:37.733756965 -0700 @@ -33,7 +33,7 @@ #include "sun_nio_ch_PollSelectorImpl.h" JNIEXPORT jint JNICALL -Java_sun_nio_ch_PollSelectorImpl_poll(JNIEnv *env, jclass clazz, +Java_sun_nio_ch_PollSelectorImpl_poll0(JNIEnv *env, jclass clazz, jlong address, jint numfds, jint timeout) { --- old/src/jdk.net/share/classes/jdk/net/Sockets.java 2018-10-05 14:14:38.843756978 -0700 +++ new/src/jdk.net/share/classes/jdk/net/Sockets.java 2018-10-05 14:14:38.510756974 -0700 @@ -25,7 +25,18 @@ package jdk.net; -import java.net.*; +import java.net.DatagramSocket; +import java.net.MulticastSocket; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.SocketException; +import java.net.SocketImpl; +import java.net.SocketOption; +import java.net.StandardSocketOptions; +import java.nio.channels.Selector; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; +import java.nio.channels.spi.SelectorProvider; import java.io.IOException; import java.util.Collections; import java.util.HashMap; @@ -33,6 +44,8 @@ import java.util.Map; import java.util.Set; import jdk.net.ExtendedSocketOptions.PlatformSocketOptions; +import jdk.internal.net.rdma.RdmaPollSelectorProvider; +import jdk.internal.net.rdma.RdmaSocketProvider; /** * Defines static methods to set and get socket options defined by the @@ -45,10 +58,21 @@ * the complete set of options available (per socket type) on the * current system. *

+ * The {@link #openRdmaSelector() openRdmaSelector}, {@link + * #openRdmaSocketChannel() openRdmaSocketChannel}, and {@link + * #openRdmaServerSocketChannel() openRdmaServerSocketChannel} methods + * create selectors and selectable channels for use with RDMA sockets. + * These objects are created by a {@link java.nio.channels.spi.SelectorProvider + * SelectorProvider} that is not the default {@code SelectorProvider}. + * Consequently, selectable channels to RDMA sockets may not be multiplexed + * with selectable channels created by the default selector provider. The + * selector provider does not support datagram channels, its {@code + * openDatagramChannel} methods throw {@link UnsupportedOperationException}. * When a security manager is installed, some non-standard socket options * may require a security permission before being set or get. * The details are specified in {@link ExtendedSocketOptions}. No permission * is required for {@link java.net.StandardSocketOptions}. + *

* * @see java.nio.channels.NetworkChannel */ @@ -375,4 +399,111 @@ ExtendedSocketOptions.TCP_KEEPINTERVAL)); } } + + + /** + * Creates an unconnected RDMA socket. + * + *

A RDMA socket supports the same socket options that {@code + * java.net.Socket} defines. In addition, it supports the socket options + * specified by {@link RdmaSocketOptions}. + * + * @apiNote The rsocket implementation on Linux only supports IPv4 addresses. + * Consequently, attempting to bind or connect to an IPv6 address will fail + * with {@code IllegalArgumentException}. + * + * @throws IOException + * If an I/O error occurs + * @throws UnsupportedOperationException + * If RDMA sockets are not supported on this platform + * + * @since 12 + */ + public static Socket openRdmaSocket() throws IOException { + return RdmaSocketProvider.openSocket(); + } + + /** + * Creates an unbound RDMA server socket. + * + *

A RDMA socket supports the same socket options that {@code + * java.net.ServerSocket} defines. + * + * @apiNote The rsocket implementation on Linux only supports IPv4 addresses. + * Consequently, attempting to bind to an IPv6 address will fail with + * {@code IllegalArgumentException}. + * + * @throws IOException + * If an I/O error occurs + * @throws UnsupportedOperationException + * If RDMA sockets are not supported on this platform + * + * @since 12 + */ + public static ServerSocket openRdmaServerSocket() throws IOException { + return RdmaSocketProvider.openServerSocket(); + } + + /** + * Opens a socket channel to a RDMA socket. A newly created socket channel + * is {@link SocketChannel#isOpen() open}, not yet bound to a {@link + * SocketChannel#getLocalAddress() local address}, and not yet + * {@link SocketChannel#isConnected() connected}. + * + *

A socket channel to a RDMA socket supports all of the socket options + * specified by {@code SocketChannel}. In addition, it supports the + * socket options specified by {@link RdmaSocketOptions}. + * + * @apiNote The rsocket implementation on Linux only supports IPv4 addresses. + * Consequently, attempting to bind or connect to an IPv6 address will fail + * with {@code UnsupportedAddressTypeException}. + * + * @throws IOException + * If an I/O error occurs + * @throws UnsupportedOperationException + * If RDMA sockets are not supported on this platform + * + * @since 12 + */ + public static SocketChannel openRdmaSocketChannel() throws IOException { + SelectorProvider provider = RdmaPollSelectorProvider.provider(); + return provider.openSocketChannel(); + } + + /** + * Opens a server-socket channel to a RDMA socket. A newly created socket + * channel is {@link SocketChannel#isOpen() open} but not yet bound to a + * {@link SocketChannel#getLocalAddress() local address}. + * + * @apiNote The rsocket implementation on Linux only supports IPv4 addresses. + * Consequently, attempting to bind to an IPv6 address will fail with + * {@code UnsupportedAddressTypeException}. + * + * @throws IOException + * If an I/O error occurs + * @throws UnsupportedOperationException + * If RDMA sockets are not supported on this platform + * + * @since 12 + */ + public static ServerSocketChannel openRdmaServerSocketChannel() + throws IOException { + SelectorProvider provider = RdmaPollSelectorProvider.provider(); + return provider.openServerSocketChannel(); + } + + /** + * Opens a selector to multiplex selectable channels to RDMA sockets. + * + * @throws IOException + * If an I/O error occurs + * @throws UnsupportedOperationException + * If RDMA sockets are not supported on this platform + * + * @since 12 + */ + public static Selector openRdmaSelector() throws IOException { + SelectorProvider provider = RdmaPollSelectorProvider.provider(); + return provider.openSelector(); + } } --- /dev/null 2018-09-16 08:14:05.604314476 -0700 +++ new/src/java.base/share/classes/jdk/internal/misc/JdkRdmaSocketImplAccess.java 2018-10-05 14:14:39.325756983 -0700 @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.internal.misc; + +import java.net.ServerSocket; +import java.net.Socket; +import java.net.SocketImpl; + +public interface JdkRdmaSocketImplAccess { + public void setSocket(SocketImpl si, Socket s); + public void setServerSocket(SocketImpl si, ServerSocket ss); +} --- /dev/null 2018-09-16 08:14:05.604314476 -0700 +++ new/src/java.base/share/classes/sun/net/ext/RdmaSocketOptions.java 2018-10-05 14:14:39.861756990 -0700 @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net.ext; + +import java.io.FileDescriptor; +import java.net.SocketException; +import java.net.SocketOption; +import java.util.Collections; +import java.util.Set; + +/** + * Defines the infrastructure to support RDMA socket options. + * + * RDMA socket options are accessed through the jdk.net API, which is in + * the jdk.net module. + */ +public abstract class RdmaSocketOptions { + + private final Set> options; + + /** Tells whether or not the option is supported. */ + public final boolean isOptionSupported(SocketOption option) { + return options().contains(option); + } + + /** Return the, possibly empty, set of RDMA socket options available. */ + public final Set> options() { return options; } + + /** Sets the value of a socket option, for the given socket. */ + public abstract void setOption(FileDescriptor fd, SocketOption option, Object value) + throws SocketException; + + /** Returns the value of a socket option, for the given socket. */ + public abstract Object getOption(FileDescriptor fd, SocketOption option) + throws SocketException; + + protected RdmaSocketOptions(Set> options) { + this.options = options; + } + + private static volatile RdmaSocketOptions instance; + + public static final RdmaSocketOptions getInstance() { return instance; } + + /** Registers support for RDMA socket options. Invoked by the jdk.net module. */ + public static final void register(RdmaSocketOptions rdmaOptions) { + if (instance != null) + throw new InternalError("Attempting to reregister RDMA options"); + + instance = rdmaOptions; + } + + static { + try { + // If the class is present, it will be initialized which + // triggers registration of the RDMA socket options. + Class c = Class.forName("jdk.net.RdmaSocketOptions"); + } catch (ClassNotFoundException e) { + // the jdk.net module is not present => no RDMA socket options + instance = new NoRdmaSocketOptions(); + } + } + + static final class NoRdmaSocketOptions extends RdmaSocketOptions { + + NoRdmaSocketOptions() { + super(Collections.>emptySet()); + } + + @Override + public void setOption(FileDescriptor fd, SocketOption option, Object value) + throws SocketException + { + throw new UnsupportedOperationException( + "no RDMA options: " + option.name()); + } + + @Override + public Object getOption(FileDescriptor fd, SocketOption option) + throws SocketException + { + throw new UnsupportedOperationException( + "no RDMA options: " + option.name()); + } + } +} --- /dev/null 2018-09-16 08:14:05.604314476 -0700 +++ new/src/jdk.net/linux/classes/jdk/internal/net/rdma/LinuxRdmaPollSelectorProvider.java 2018-10-05 14:14:40.457756997 -0700 @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.internal.net.rdma; + +import java.io.IOException; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; +import java.nio.channels.spi.AbstractSelector; +import jdk.internal.net.rdma.RdmaPollSelectorProvider.PlatformRdmaPollSelectorProvider; + +public class LinuxRdmaPollSelectorProvider + extends RdmaPollSelectorProvider.PlatformRdmaPollSelectorProvider +{ + public AbstractSelector openSelector(RdmaPollSelectorProvider sp) + throws IOException { + return new RdmaPollSelectorImpl(sp); + } + + public SocketChannel openSocketChannel(RdmaPollSelectorProvider sp) + throws IOException { + return new RdmaSocketChannelImpl(sp); + } + + public ServerSocketChannel openServerSocketChannel(RdmaPollSelectorProvider sp) + throws IOException { + return new RdmaServerSocketChannelImpl(sp); + } +} --- /dev/null 2018-09-16 08:14:05.604314476 -0700 +++ new/src/jdk.net/linux/classes/jdk/internal/net/rdma/LinuxRdmaSocketDispatcher.java 2018-10-05 14:14:41.029757004 -0700 @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.internal.net.rdma; +import java.io.FileDescriptor; +import java.io.IOException; + +public class LinuxRdmaSocketDispatcher + extends RdmaSocketDispatcher.PlatformRdmaSocketDispatcher +{ + protected int read(FileDescriptor fd, long address, int len) throws IOException { + return LinuxRdmaSocketDispatcherImpl.read0(fd, address, len); + } + + protected long readv(FileDescriptor fd, long address, int len) throws IOException { + return LinuxRdmaSocketDispatcherImpl.readv0(fd, address, len); + } + + protected int write(FileDescriptor fd, long address, int len) throws IOException { + return LinuxRdmaSocketDispatcherImpl.write0(fd, address, len); + } + + protected long writev(FileDescriptor fd, long address, int len) throws IOException { + return LinuxRdmaSocketDispatcherImpl.writev0(fd, address, len); + } + + protected void close(FileDescriptor fd) throws IOException { + LinuxRdmaSocketDispatcherImpl.close0(fd); + } + + public void preClose(FileDescriptor fd) throws IOException { + } +} --- /dev/null 2018-09-16 08:14:05.604314476 -0700 +++ new/src/jdk.net/linux/classes/jdk/internal/net/rdma/LinuxRdmaSocketDispatcherImpl.java 2018-10-05 14:14:41.556757010 -0700 @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.internal.net.rdma; + +import java.io.FileDescriptor; +import java.io.IOException; +import sun.nio.ch.IOUtil; + +import jdk.internal.misc.JavaIOFileDescriptorAccess; +import jdk.internal.misc.SharedSecrets; + +public class LinuxRdmaSocketDispatcherImpl { + + static { + java.security.AccessController.doPrivileged( + new java.security.PrivilegedAction() { + public Void run() { + System.loadLibrary("extnet"); + return null; + } + }); + IOUtil.load(); + init(); + } + + private static final JavaIOFileDescriptorAccess fdAccess = + SharedSecrets.getJavaIOFileDescriptorAccess(); + + LinuxRdmaSocketDispatcherImpl() { + } + + protected void close(FileDescriptor fd) throws IOException { + fdAccess.close(fd); + } + + // -- Native methods -- + + static native int read0(FileDescriptor fd, long address, int len) + throws IOException; + + static native long readv0(FileDescriptor fd, long address, int len) + throws IOException; + + static native int write0(FileDescriptor fd, long address, int len) + throws IOException; + + static native long writev0(FileDescriptor fd, long address, int len) + throws IOException; + + static native void close0(FileDescriptor fd) throws IOException; + + static native void init(); +} --- /dev/null 2018-09-16 08:14:05.604314476 -0700 +++ new/src/jdk.net/linux/classes/jdk/internal/net/rdma/LinuxRdmaSocketImpl.java 2018-10-05 14:14:42.117757016 -0700 @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.internal.net.rdma; + +import java.io.IOException; +import java.io.InputStream; +import java.net.SocketException; +import java.net.InetAddress; +import java.net.SocketImpl; +import sun.net.ext.RdmaSocketOptions; +import static jdk.internal.net.rdma.RdmaSocketImpl.PlatformRdmaSocketImpl; + +class LinuxRdmaSocketImpl extends PlatformRdmaSocketImpl +{ + static { + java.security.AccessController.doPrivileged( + new java.security.PrivilegedAction<>() { + public Void run() { + System.loadLibrary("net"); + System.loadLibrary("extnet"); + return null; + } + }); + initProto(); + } + + public LinuxRdmaSocketImpl() { } + + static final RdmaSocketOptions rdmaOptions = + RdmaSocketOptions.getInstance(); + + private static volatile boolean checkedRdma; + private static volatile boolean isRdmaAvailable; + + boolean isRdmaAvailable() { + if (!checkedRdma) { + isRdmaAvailable = isRdmaAvailable0(); + checkedRdma = true; + } + return isRdmaAvailable; + } + + protected synchronized RdmaSocketInputStream getRdmaInputStream( + RdmaSocketImpl impl) throws IOException { + return new RdmaSocketInputStream(impl); + } + + protected synchronized RdmaSocketOutputStream getRdmaOutputStream( + RdmaSocketImpl impl) throws IOException { + return new RdmaSocketOutputStream(impl); + } + + protected void rdmaSocketShutdownInput(int howto, RdmaSocketImpl impl, + InputStream socketInputStream) throws IOException { + rdmaSocketShutdown(howto, impl); + if (socketInputStream != null) { + ((RdmaSocketInputStream)socketInputStream).setEOF(true); + } + } + + static native void initProto(); + + private static native boolean isRdmaAvailable0(); + + native void rdmaSocketCreate(boolean isServer, RdmaSocketImpl impl) throws IOException; + + native void rdmaSocketConnect(RdmaSocketImpl impl, InetAddress address, int port, int timeout) + throws IOException; + + native void rdmaSocketBind(RdmaSocketImpl impl, InetAddress address, int port) + throws IOException; + + native void rdmaSocketListen(RdmaSocketImpl impl, int count) throws IOException; + + native void rdmaSocketAccept(SocketImpl impl, RdmaSocketImpl s) throws IOException; + + native int rdmaSocketAvailable(RdmaSocketImpl impl) throws IOException; + + native void rdmaSocketClose(boolean useDeferredClose, RdmaSocketImpl impl) throws IOException; + + native void rdmaSocketShutdown(int howto, RdmaSocketImpl impl) throws IOException; + + native void rdmaSocketSetOption(RdmaSocketImpl impl, int cmd, boolean on, Object value) + throws SocketException; + + native int rdmaSocketGetOption(RdmaSocketImpl impl, int opt, Object iaContainerObj) throws SocketException; + + native void rdmaSocketSendUrgentData(RdmaSocketImpl impl, int data) throws IOException; +} --- /dev/null 2018-09-16 08:14:05.604314476 -0700 +++ new/src/jdk.net/linux/classes/jdk/internal/net/rdma/RdmaInetAddressContainer.java 2018-10-05 14:14:42.653757023 -0700 @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.internal.net.rdma; + +import java.net.InetAddress; +class RdmaInetAddressContainer { + InetAddress addr; +} --- /dev/null 2018-09-16 08:14:05.604314476 -0700 +++ new/src/jdk.net/linux/classes/jdk/internal/net/rdma/RdmaNet.java 2018-10-05 14:14:43.209757029 -0700 @@ -0,0 +1,253 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.internal.net.rdma; + +import java.io.FileDescriptor; +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.ProtocolFamily; +import java.net.SocketOption; +import java.net.StandardProtocolFamily; +import java.net.StandardSocketOptions; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.Enumeration; +import sun.net.ext.RdmaSocketOptions; +import sun.nio.ch.IOUtil; +import sun.nio.ch.Net; + +public class RdmaNet extends Net { + protected RdmaNet() { } + + static final ProtocolFamily UNSPEC = new ProtocolFamily() { + public String name() { + return "UNSPEC"; + } + }; + + static boolean isReusePortAvailable() { + return false; + } + + private static volatile boolean checkedRdma; + private static volatile boolean isRdmaAvailable; + + static boolean isRdmaAvailable() { + if (!checkedRdma) { + isRdmaAvailable = isRdmaAvailable0(); + checkedRdma = true; + } + return isRdmaAvailable; + } + + private static native boolean isRdmaAvailable0(); + + /** + * Returns true if exclusive binding is on + */ + static boolean useExclusiveBind() { + return false; + } + + // -- Socket options + + static final RdmaSocketOptions rdmaOptions = + RdmaSocketOptions.getInstance(); + + static void setSocketOption(FileDescriptor fd, ProtocolFamily family, + SocketOption name, Object value) + throws IOException + { + if (value == null) + throw new IllegalArgumentException("Invalid option value"); + + Class type = name.type(); + + if (rdmaOptions.isOptionSupported(name)) { + rdmaOptions.setOption(fd, name, value); + return; + } + + if (type != Integer.class && type != Boolean.class) + throw new AssertionError("Should not reach here"); + + if (name == StandardSocketOptions.SO_RCVBUF || + name == StandardSocketOptions.SO_SNDBUF) + { + int i = ((Integer)value).intValue(); + if (i < 0) + throw new IllegalArgumentException("Invalid send/receive buffer size"); + } + RdmaOptionKey key = RdmaSocketOptionRegistry.findOption(name, family); + if (key == null) + throw new AssertionError("Option not found"); + + int arg; + int maxValue = 1024 * 1024 * 1024 - 1; + if (type == Integer.class) { + arg = ((Integer)value).intValue(); + if (arg > maxValue) + arg = maxValue; + } else { + boolean b = ((Boolean)value).booleanValue(); + arg = (b) ? 1 : 0; + } + + boolean mayNeedConversion = (family == UNSPEC); + boolean isIPv6 = (family == StandardProtocolFamily.INET6); + setIntOption0(fd, mayNeedConversion, key.level(), key.name(), arg, isIPv6); + } + + static Object getSocketOption(FileDescriptor fd, ProtocolFamily family, + SocketOption name) + throws IOException + { + Class type = name.type(); + + if (rdmaOptions.isOptionSupported(name)) { + return rdmaOptions.getOption(fd, name); + } + + if (type != Integer.class && type != Boolean.class) + throw new AssertionError("Should not reach here"); + + RdmaOptionKey key = RdmaSocketOptionRegistry.findOption(name, family); + if (key == null) + throw new AssertionError("Option not found"); + + boolean mayNeedConversion = (family == UNSPEC); + int value = getIntOption0(fd, mayNeedConversion, key.level(), key.name()); + + if (type == Integer.class) { + return Integer.valueOf(value); + } else { + return (value == 0) ? Boolean.FALSE : Boolean.TRUE; + } + } + + // -- Socket operations -- + + static FileDescriptor socket(boolean stream) throws IOException { + return socket(UNSPEC, stream); + } + + static FileDescriptor socket(ProtocolFamily family, boolean stream) + throws IOException { + boolean preferIPv6 = isIPv6Available() && + (family != StandardProtocolFamily.INET); + return IOUtil.newFD(socket0(preferIPv6, stream, false, false)); + } + + static FileDescriptor serverSocket(boolean stream) { + return IOUtil.newFD(socket0(isIPv6Available(), stream, true, false)); + } + + private static native int socket0(boolean preferIPv6, boolean stream, boolean reuse, + boolean fastLoopback); + + public static void bind(FileDescriptor fd, InetAddress addr, int port) + throws IOException + { + bind(UNSPEC, fd, addr, port); + } + + static void bind(ProtocolFamily family, FileDescriptor fd, + InetAddress addr, int port) throws IOException + { + boolean preferIPv6 = isIPv6Available() && + (family != StandardProtocolFamily.INET); + bind0(fd, preferIPv6, false, addr, port); + } + + private static native void bind0(FileDescriptor fd, boolean preferIPv6, + boolean useExclBind, InetAddress addr, + int port) + throws IOException; + + static native void listen(FileDescriptor fd, int backlog) throws IOException; + + static int connect(FileDescriptor fd, InetAddress remote, int remotePort) + throws IOException + { + return connect(UNSPEC, fd, remote, remotePort); + } + + static int connect(ProtocolFamily family, FileDescriptor fd, InetAddress remote, int remotePort) + throws IOException + { + boolean preferIPv6 = isIPv6Available() && + (family != StandardProtocolFamily.INET); + return connect0(preferIPv6, fd, remote, remotePort); + } + + public static InetSocketAddress localAddress(FileDescriptor fd) + throws IOException + { + return new InetSocketAddress(localInetAddress(fd), localPort(fd)); + } + + private static native int connect0(boolean preferIPv6, + FileDescriptor fd, + InetAddress remote, + int remotePort) + throws IOException; + + static native void shutdown(FileDescriptor fd, int how) throws IOException; + + private static native int localPort(FileDescriptor fd) + throws IOException; + + private static native InetAddress localInetAddress(FileDescriptor fd) + throws IOException; + + private static native int getIntOption0(FileDescriptor fd, boolean mayNeedConversion, + int level, int opt) + throws IOException; + + private static native void setIntOption0(FileDescriptor fd, boolean mayNeedConversion, + int level, int opt, int arg, boolean isIPv6) + throws IOException; + + static native int poll(FileDescriptor fd, int events, long timeout) + throws IOException; + + public static native void configureBlocking(FileDescriptor fd, boolean blocking); + + private static native void initIDs(); + + static { + java.security.AccessController.doPrivileged( + new java.security.PrivilegedAction<>() { + public Void run() { + System.loadLibrary("extnet"); + return null; + } + }); + IOUtil.load(); + initIDs(); + } +} --- /dev/null 2018-09-16 08:14:05.604314476 -0700 +++ new/src/jdk.net/linux/classes/jdk/internal/net/rdma/RdmaOptionKey.java 2018-10-05 14:14:43.740757036 -0700 @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.internal.net.rdma; + +/** + * Represents the level/name of a RDMA socket option + */ + +class RdmaOptionKey { + private int level; + private int name; + + RdmaOptionKey(int level, int name) { + this.level = level; + this.name = name; + } + + int level() { + return level; + } + + int name() { + return name; + } +} --- /dev/null 2018-09-16 08:14:05.604314476 -0700 +++ new/src/jdk.net/linux/classes/jdk/internal/net/rdma/RdmaPollSelectorImpl.java 2018-10-05 14:14:44.320757043 -0700 @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.internal.net.rdma; + +import java.io.IOException; +import java.nio.channels.spi.SelectorProvider; +import sun.nio.ch.PollSelectorImpl; +import sun.nio.ch.IOUtil; + +/** + * Selector implementation based on poll + */ + +public class RdmaPollSelectorImpl extends PollSelectorImpl { + + protected RdmaPollSelectorImpl(SelectorProvider sp) throws IOException { + super(sp); + } + + protected int poll(long pollAddress, int numfds, int timeout) { + return poll0(pollAddress, numfds, timeout); + } + + private static native int poll0(long pollAddress, int numfds, int timeout); + + static { + java.security.AccessController.doPrivileged( + new java.security.PrivilegedAction<>() { + public Void run() { + System.loadLibrary("extnet"); + return null; + } + }); + IOUtil.load(); + init(); + } + + private static native void init(); +} --- /dev/null 2018-09-16 08:14:05.604314476 -0700 +++ new/src/jdk.net/linux/classes/jdk/internal/net/rdma/RdmaPollSelectorProvider.java 2018-10-05 14:14:44.853757049 -0700 @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.internal.net.rdma; + +import java.io.IOException; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; +import java.nio.channels.spi.AbstractSelector; +import java.nio.channels.spi.SelectorProvider; +import sun.nio.ch.SelectorProviderImpl; +import java.security.AccessController; +import java.security.PrivilegedAction; + +public class RdmaPollSelectorProvider + extends SelectorProviderImpl +{ + private static final Object lock = new Object(); + private static SelectorProvider provider = null; + + private static PlatformRdmaPollSelectorProvider pPollSelectorProvider = + PlatformRdmaPollSelectorProvider.get(); + + public static SelectorProvider provider() { + synchronized (lock) { + if (provider != null) + return provider; + return AccessController.doPrivileged( + new PrivilegedAction<>() { + public SelectorProvider run() { + provider = new RdmaPollSelectorProvider(); + return provider; + } + }); + } + } + + public AbstractSelector openSelector() throws IOException { + return pPollSelectorProvider.openSelector(this); + } + + public SocketChannel openSocketChannel() throws IOException { + return pPollSelectorProvider.openSocketChannel(this); + } + + public ServerSocketChannel openServerSocketChannel() throws IOException { + return pPollSelectorProvider.openServerSocketChannel(this); + } + + static class PlatformRdmaPollSelectorProvider { + + @SuppressWarnings("unchecked") + private static PlatformRdmaPollSelectorProvider newInstance(String cn) { + Class c; + try { + c = (Class)Class.forName(cn); + return c.getConstructor(new Class[] {}).newInstance(); + } catch (ReflectiveOperationException x) { + throw new AssertionError(x); + } + } + + private static PlatformRdmaPollSelectorProvider create() { + String osname = AccessController.doPrivileged( + new PrivilegedAction() { + public String run() { + return System.getProperty("os.name"); + } + }); + if ("Linux".equals(osname)) + return newInstance("jdk.internal.net.rdma.LinuxRdmaPollSelectorProvider"); + return new PlatformRdmaPollSelectorProvider(); + } + + private static final PlatformRdmaPollSelectorProvider instance = create(); + + static PlatformRdmaPollSelectorProvider get() { + return instance; + } + + public AbstractSelector openSelector(RdmaPollSelectorProvider sp) + throws IOException { + throw new UnsupportedOperationException("unsupported socket operation"); + } + + public SocketChannel openSocketChannel(RdmaPollSelectorProvider sp) + throws IOException { + throw new UnsupportedOperationException("unsupported socket operation"); + } + + public ServerSocketChannel openServerSocketChannel(RdmaPollSelectorProvider sp) + throws IOException { + throw new UnsupportedOperationException("unsupported socket operation"); + } + } +} --- /dev/null 2018-09-16 08:14:05.604314476 -0700 +++ new/src/jdk.net/linux/classes/jdk/internal/net/rdma/RdmaServerSocketAdaptor.java 2018-10-05 14:14:45.431757056 -0700 @@ -0,0 +1,195 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.internal.net.rdma; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.SocketAddress; +import java.net.SocketException; +import java.net.SocketTimeoutException; +import java.net.StandardSocketOptions; +import java.nio.channels.IllegalBlockingModeException; +import java.nio.channels.NotYetBoundException; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; + +class RdmaServerSocketAdaptor + extends ServerSocket +{ + private final RdmaServerSocketChannelImpl ssc; + + private volatile int timeout; + + public static ServerSocket create(RdmaServerSocketChannelImpl ssc) { + try { + return new RdmaServerSocketAdaptor(ssc); + } catch (IOException x) { + throw new Error(x); + } + } + + private RdmaServerSocketAdaptor(RdmaServerSocketChannelImpl ssc) throws IOException { + this.ssc = ssc; + } + + public void bind(SocketAddress local) throws IOException { + bind(local, 50); + } + + public void bind(SocketAddress local, int backlog) throws IOException { + if (local == null) + local = new InetSocketAddress(0); + try { + ssc.bind(local, backlog); + } catch (Exception x) { + RdmaNet.translateException(x); + } + } + + public InetAddress getInetAddress() { + InetSocketAddress local = ssc.localAddress(); + if (local == null) { + return null; + } else { + return RdmaNet.getRevealedLocalAddress(local).getAddress(); + } + } + + public int getLocalPort() { + InetSocketAddress local = ssc.localAddress(); + if (local == null) { + return -1; + } else { + return local.getPort(); + } + } + + public Socket accept() throws IOException { + synchronized (ssc.blockingLock()) { + try { + if (!ssc.isBound()) + throw new NotYetBoundException(); + + long to = this.timeout; + if (to == 0) { + // for compatibility reasons: accept connection if available + // when configured non-blocking + SocketChannel sc = ssc.accept(); + if (sc == null && !ssc.isBlocking()) + throw new IllegalBlockingModeException(); + return sc.socket(); + } + + if (!ssc.isBlocking()) + throw new IllegalBlockingModeException(); + for (;;) { + long st = System.currentTimeMillis(); + if (ssc.pollAccept(to)) + return ssc.accept().socket(); + to -= System.currentTimeMillis() - st; + if (to <= 0) + throw new SocketTimeoutException(); + } + + } catch (Exception x) { + RdmaNet.translateException(x); + assert false; + return null; // Never happens + } + } + } + + public void close() throws IOException { + ssc.close(); + } + + public ServerSocketChannel getChannel() { + return ssc; + } + + public boolean isBound() { + return ssc.isBound(); + } + + public boolean isClosed() { + return !ssc.isOpen(); + } + + public void setSoTimeout(int timeout) throws SocketException { + this.timeout = timeout; + } + + public int getSoTimeout() throws SocketException { + return timeout; + } + + public void setReuseAddress(boolean on) throws SocketException { + try { + ssc.setOption(StandardSocketOptions.SO_REUSEADDR, on); + } catch (IOException x) { + RdmaNet.translateToSocketException(x); + } + } + + public boolean getReuseAddress() throws SocketException { + try { + return ssc.getOption(StandardSocketOptions.SO_REUSEADDR).booleanValue(); + } catch (IOException x) { + RdmaNet.translateToSocketException(x); + return false; // Never happens + } + } + + public String toString() { + if (!isBound()) + return "RdmaServerSocket[unbound]"; + return "RdmaServerSocket[addr=" + getInetAddress() + + ",localport=" + getLocalPort() + "]"; + } + + public void setReceiveBufferSize(int size) throws SocketException { + // size 0 valid for ServerSocketChannel, invalid for ServerSocket + if (size <= 0) + throw new IllegalArgumentException("size cannot be 0 or negative"); + try { + ssc.setOption(StandardSocketOptions.SO_RCVBUF, size); + } catch (IOException x) { + RdmaNet.translateToSocketException(x); + } + } + + public int getReceiveBufferSize() throws SocketException { + try { + return ssc.getOption(StandardSocketOptions.SO_RCVBUF).intValue(); + } catch (IOException x) { + RdmaNet.translateToSocketException(x); + return -1; // Never happens + } + } +} --- /dev/null 2018-09-16 08:14:05.604314476 -0700 +++ new/src/jdk.net/linux/classes/jdk/internal/net/rdma/RdmaServerSocketChannelImpl.java 2018-10-05 14:14:46.035757063 -0700 @@ -0,0 +1,478 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.internal.net.rdma; + +import java.io.FileDescriptor; +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.ServerSocket; +import java.net.SocketAddress; +import java.net.SocketOption; +import java.net.StandardSocketOptions; +import java.nio.channels.AlreadyBoundException; +import java.nio.channels.AsynchronousCloseException; +import java.nio.channels.ClosedChannelException; +import java.nio.channels.NotYetBoundException; +import java.nio.channels.SelectionKey; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; +import java.nio.channels.spi.SelectorProvider; +import java.util.Collections; +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.locks.ReentrantLock; +import sun.nio.ch.IOStatus; +import sun.nio.ch.IOUtil; +import sun.nio.ch.NativeThread; +import sun.nio.ch.SelChImpl; +import sun.nio.ch.SelectionKeyImpl; +import sun.net.ext.RdmaSocketOptions; + +public class RdmaServerSocketChannelImpl + extends ServerSocketChannel + implements SelChImpl +{ + private static RdmaSocketDispatcher nd; + + private final FileDescriptor fd; + private final int fdVal; + + private final ReentrantLock acceptLock = new ReentrantLock(); + + private final Object stateLock = new Object(); + + private static final int ST_INUSE = 0; + private static final int ST_CLOSING = 1; + private static final int ST_KILLPENDING = 2; + private static final int ST_KILLED = 3; + private int state; + + private long thread; + + private InetSocketAddress localAddress; + + private boolean isReuseAddress; + + private ServerSocket socket; + + RdmaServerSocketChannelImpl(SelectorProvider sp) throws IOException { + super(sp); + this.fd = RdmaNet.serverSocket(true); + this.fdVal = IOUtil.fdVal(fd); + } + + RdmaServerSocketChannelImpl(SelectorProvider sp, FileDescriptor fd, boolean bound) + throws IOException + { + super(sp); + this.fd = fd; + this.fdVal = IOUtil.fdVal(fd); + if (bound) { + synchronized (stateLock) { + localAddress = RdmaNet.localAddress(fd); + } + } + } + + private void ensureOpen() throws ClosedChannelException { + if (!isOpen()) + throw new ClosedChannelException(); + } + + @Override + public ServerSocket socket() { + synchronized (stateLock) { + if (socket == null) + socket = RdmaServerSocketAdaptor.create(this); + return socket; + } + } + + @Override + public SocketAddress getLocalAddress() throws IOException { + synchronized (stateLock) { + ensureOpen(); + return (localAddress == null) + ? null + : RdmaNet.getRevealedLocalAddress(localAddress); + } + } + + @Override + public ServerSocketChannel setOption(SocketOption name, T value) + throws IOException + { + Objects.requireNonNull(name); + if (!supportedOptions().contains(name)) + throw new UnsupportedOperationException("'" + name + "' not supported"); + synchronized (stateLock) { + ensureOpen(); + + if (name == StandardSocketOptions.SO_REUSEADDR && + RdmaNet.useExclusiveBind()) { + isReuseAddress = (Boolean)value; + return this; + } + + if (isBound() && (name == StandardSocketOptions.SO_REUSEADDR)) + throw new UnsupportedOperationException( + "RDMA server socket channel cannot set the socket option " + + name.toString() + " after bind."); + + RdmaNet.setSocketOption(fd, RdmaNet.UNSPEC, name, value); + return this; + } + } + + @Override + @SuppressWarnings("unchecked") + public T getOption(SocketOption name) + throws IOException + { + Objects.requireNonNull(name); + if (!supportedOptions().contains(name)) + throw new UnsupportedOperationException("'" + name + "' not supported"); + + synchronized (stateLock) { + ensureOpen(); + if (name == StandardSocketOptions.SO_REUSEADDR && RdmaNet.useExclusiveBind()) { + return (T)Boolean.valueOf(isReuseAddress); + } + return (T) RdmaNet.getSocketOption(fd, RdmaNet.UNSPEC, name); + } + } + + private static class DefaultOptionsHolder { + static final Set> defaultOptions = defaultOptions(); + + private static Set> defaultOptions() { + HashSet> set = new HashSet<>(2); + set.add(StandardSocketOptions.SO_RCVBUF); + set.add(StandardSocketOptions.SO_REUSEADDR); + if (RdmaNet.isRdmaAvailable()) { + RdmaSocketOptions rdmaOptions = + RdmaSocketOptions.getInstance(); + set.addAll(rdmaOptions.options()); + } + return Collections.unmodifiableSet(set); + } + } + + public final Set> supportedOptions() { + return DefaultOptionsHolder.defaultOptions; + } + + @Override + public ServerSocketChannel bind(SocketAddress local, int backlog) throws IOException { + synchronized (stateLock) { + ensureOpen(); + if (localAddress != null) + throw new AlreadyBoundException(); + InetSocketAddress isa = (local == null) + ? new InetSocketAddress(0) + : RdmaNet.checkAddress(local); + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkListen(isa.getPort()); + RdmaNet.bind(fd, isa.getAddress(), isa.getPort()); + RdmaNet.listen(fd, backlog < 1 ? 50 : backlog); + localAddress = RdmaNet.localAddress(fd); + } + return this; + } + + private void begin(boolean blocking) throws ClosedChannelException { + if (blocking) + begin(); + synchronized (stateLock) { + ensureOpen(); + if (localAddress == null) + throw new NotYetBoundException(); + if (blocking) + thread = NativeThread.current(); + } + } + + private void end(boolean blocking, boolean completed) + throws AsynchronousCloseException + { + if (blocking) { + synchronized (stateLock) { + thread = 0; + if (state == ST_CLOSING) { + stateLock.notifyAll(); + } + } + end(completed); + } + } + + @Override + public SocketChannel accept() throws IOException { + acceptLock.lock(); + try { + int n = 0; + FileDescriptor newfd = new FileDescriptor(); + InetSocketAddress[] isaa = new InetSocketAddress[1]; + + boolean blocking = isBlocking(); + try { + begin(blocking); + do { + n = accept(this.fd, newfd, isaa); + } while (n == IOStatus.INTERRUPTED && isOpen()); + } finally { + end(blocking, n > 0); + assert IOStatus.check(n); + } + + if (n < 1) + return null; + + // newly accepted socket is initially in blocking mode + RdmaNet.configureBlocking(newfd, true); + + InetSocketAddress isa = isaa[0]; + SocketChannel sc = new RdmaSocketChannelImpl(provider(), newfd, isa); + + // check permitted to accept connections from the remote address + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + try { + sm.checkAccept(isa.getAddress().getHostAddress(), isa.getPort()); + } catch (SecurityException x) { + sc.close(); + throw x; + } + } + return sc; + + } finally { + acceptLock.unlock(); + } + } + + @Override + protected void implConfigureBlocking(boolean block) throws IOException { + acceptLock.lock(); + try { + synchronized (stateLock) { + ensureOpen(); + RdmaNet.configureBlocking(fd, block); + } + } finally { + acceptLock.unlock(); + } + } + + @Override + protected void implCloseSelectableChannel() throws IOException { + assert !isOpen(); + + boolean interrupted = false; + boolean blocking; + + // set state to ST_CLOSING + synchronized (stateLock) { + assert state < ST_CLOSING; + state = ST_CLOSING; + blocking = isBlocking(); + } + + // wait for any outstanding accept to complete + if (blocking) { + synchronized (stateLock) { + assert state == ST_CLOSING; + long th = thread; + if (th != 0) { + nd.preClose(fd); + NativeThread.signal(th); + + // wait for accept operation to end + while (thread != 0) { + try { + stateLock.wait(); + } catch (InterruptedException e) { + interrupted = true; + } + } + } + } + } else { + // non-blocking mode: wait for accept to complete + acceptLock.lock(); + acceptLock.unlock(); + } + + // set state to ST_KILLPENDING + synchronized (stateLock) { + assert state == ST_CLOSING; + state = ST_KILLPENDING; + } + + // close socket if not registered with Selector + if (!isRegistered()) + kill(); + + // restore interrupt status + if (interrupted) + Thread.currentThread().interrupt(); + } + + @Override + public void kill() throws IOException { + synchronized (stateLock) { + if (state == ST_KILLPENDING) { + state = ST_KILLED; + nd.close(fd); + } + } + } + + boolean isBound() { + synchronized (stateLock) { + return localAddress != null; + } + } + + InetSocketAddress localAddress() { + synchronized (stateLock) { + return localAddress; + } + } + + /** + * Poll this channel's socket for a new connection up to the given timeout. + * @return {@code true} if there is a connection to accept + */ + boolean pollAccept(long timeout) throws IOException { + assert Thread.holdsLock(blockingLock()) && isBlocking(); + acceptLock.lock(); + try { + boolean polled = false; + try { + begin(true); + int events = RdmaNet.poll(fd, RdmaNet.POLLIN, timeout); + polled = (events != 0); + } finally { + end(true, polled); + } + return polled; + } finally { + acceptLock.unlock(); + } + } + + public boolean translateReadyOps(int ops, int initialOps, SelectionKeyImpl ski) { + int intOps = ski.nioInterestOps(); + int oldOps = ski.nioReadyOps(); + int newOps = initialOps; + + if ((ops & RdmaNet.POLLNVAL) != 0) { + return false; + } + + if ((ops & (RdmaNet.POLLERR | RdmaNet.POLLHUP)) != 0) { + newOps = intOps; + ski.nioReadyOps(newOps); + return (newOps & ~oldOps) != 0; + } + + if (((ops & RdmaNet.POLLIN) != 0) && + ((intOps & SelectionKey.OP_ACCEPT) != 0)) + newOps |= SelectionKey.OP_ACCEPT; + + ski.nioReadyOps(newOps); + return (newOps & ~oldOps) != 0; + } + + public boolean translateAndUpdateReadyOps(int ops, SelectionKeyImpl ski) { + return translateReadyOps(ops, ski.nioReadyOps(), ski); + } + + public boolean translateAndSetReadyOps(int ops, SelectionKeyImpl ski) { + return translateReadyOps(ops, 0, ski); + } + + public int translateInterestOps(int ops) { + int newOps = 0; + if ((ops & SelectionKey.OP_ACCEPT) != 0) + newOps |= RdmaNet.POLLIN; + return newOps; + } + + public FileDescriptor getFD() { + return fd; + } + + public int getFDVal() { + return fdVal; + } + + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(this.getClass().getName()); + sb.append('['); + if (!isOpen()) { + sb.append("closed"); + } else { + synchronized (stateLock) { + InetSocketAddress addr = localAddress; + if (addr == null) { + sb.append("unbound"); + } else { + sb.append(RdmaNet.getRevealedLocalAddressAsString(addr)); + } + } + } + sb.append(']'); + return sb.toString(); + } + + private int accept(FileDescriptor ssfd, + FileDescriptor newfd, + InetSocketAddress[] isaa) + throws IOException + { + return accept0(ssfd, newfd, isaa); + } + + // -- Native methods -- + + private native int accept0(FileDescriptor ssfd, + FileDescriptor newfd, + InetSocketAddress[] isaa) + throws IOException; + + private static native void initIDs(); + + static { + IOUtil.load(); + initIDs(); + nd = new RdmaSocketDispatcher(); + } +} --- /dev/null 2018-09-16 08:14:05.604314476 -0700 +++ new/src/jdk.net/linux/classes/jdk/internal/net/rdma/RdmaSocketAdaptor.java 2018-10-05 14:14:46.637757070 -0700 @@ -0,0 +1,396 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.internal.net.rdma; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.SocketAddress; +import java.net.SocketException; +import java.net.SocketOption; +import java.net.SocketTimeoutException; +import java.net.StandardSocketOptions; +import java.nio.ByteBuffer; +import java.nio.channels.Channels; +import java.nio.channels.ClosedChannelException; +import java.nio.channels.IllegalBlockingModeException; +import java.nio.channels.SocketChannel; +import java.security.AccessController; +import java.security.PrivilegedExceptionAction; +import sun.nio.ch.ChannelInputStream; + +import static java.util.concurrent.TimeUnit.*; + +class RdmaSocketAdaptor + extends Socket +{ + // The channel being adapted + private final RdmaSocketChannelImpl sc; + + // Timeout "option" value for reads + private volatile int timeout; + + private RdmaSocketAdaptor(RdmaSocketChannelImpl sc) throws SocketException { + super((RdmaSocketImpl) null); + this.sc = sc; + } + + public static Socket create(RdmaSocketChannelImpl sc) { + try { + return new RdmaSocketAdaptor(sc); + } catch (SocketException e) { + throw new InternalError("Should not reach here"); + } + } + + public SocketChannel getChannel() { + return sc; + } + + // Override this method just to protect against changes in the superclass + // + public void connect(SocketAddress remote) throws IOException { + connect(remote, 0); + } + + public void connect(SocketAddress remote, int timeout) throws IOException { + if (remote == null) + throw new IllegalArgumentException("connect: The address can't be null"); + if (timeout < 0) + throw new IllegalArgumentException("connect: timeout can't be negative"); + + synchronized (sc.blockingLock()) { + if (!sc.isBlocking()) + throw new IllegalBlockingModeException(); + + try { + if (timeout == 0) { + sc.connect(remote); + return; + } + + sc.configureBlocking(false); + try { + if (sc.connect(remote)) + return; + } finally { + try { + sc.configureBlocking(true); + } catch (ClosedChannelException e) { } + } + + long timeoutNanos = NANOSECONDS.convert(timeout, MILLISECONDS); + long to = timeout; + for (;;) { + long startTime = System.nanoTime(); + if (sc.pollConnected(to)) { + boolean connected = sc.finishConnect(); + assert connected; + break; + } + timeoutNanos -= System.nanoTime() - startTime; + if (timeoutNanos <= 0) { + try { + sc.close(); + } catch (IOException x) { } + throw new SocketTimeoutException(); + } + to = MILLISECONDS.convert(timeoutNanos, NANOSECONDS); + } + + } catch (Exception x) { + RdmaNet.translateException(x, true); + } + } + + } + + public void bind(SocketAddress local) throws IOException { + try { + sc.bind(local); + } catch (Exception x) { + RdmaNet.translateException(x); + } + } + + public InetAddress getInetAddress() { + InetSocketAddress remote = sc.remoteAddress(); + if (remote == null) { + return null; + } else { + return remote.getAddress(); + } + } + + public InetAddress getLocalAddress() { + if (sc.isOpen()) { + InetSocketAddress local = sc.localAddress(); + if (local != null) { + return RdmaNet.getRevealedLocalAddress(local).getAddress(); + } + } + return new InetSocketAddress(0).getAddress(); + } + + public int getPort() { + InetSocketAddress remote = sc.remoteAddress(); + if (remote == null) { + return 0; + } else { + return remote.getPort(); + } + } + + public int getLocalPort() { + InetSocketAddress local = sc.localAddress(); + if (local == null) { + return -1; + } else { + return local.getPort(); + } + } + + private class SocketInputStream + extends ChannelInputStream + { + private SocketInputStream() { + super(sc); + } + + protected int read(ByteBuffer bb) + throws IOException + { + synchronized (sc.blockingLock()) { + if (!sc.isBlocking()) + throw new IllegalBlockingModeException(); + + // no timeout + long to = RdmaSocketAdaptor.this.timeout; + if (to == 0) + return sc.read(bb); + + // timed read + long timeoutNanos = NANOSECONDS.convert(to, MILLISECONDS); + for (;;) { + long startTime = System.nanoTime(); + if (sc.pollRead(to)) { + return sc.read(bb); + } + timeoutNanos -= System.nanoTime() - startTime; + if (timeoutNanos <= 0) + throw new SocketTimeoutException(); + to = MILLISECONDS.convert(timeoutNanos, NANOSECONDS); + } + } + } + } + + private InputStream socketInputStream = null; + + public InputStream getInputStream() throws IOException { + if (!sc.isOpen()) + throw new SocketException("Socket is closed"); + if (!sc.isConnected()) + throw new SocketException("Socket is not connected"); + if (!sc.isInputOpen()) + throw new SocketException("Socket input is shutdown"); + if (socketInputStream == null) { + try { + socketInputStream = AccessController.doPrivileged( + new PrivilegedExceptionAction() { + public InputStream run() throws IOException { + return new SocketInputStream(); + } + }); + } catch (java.security.PrivilegedActionException e) { + throw (IOException)e.getException(); + } + } + return socketInputStream; + } + + public OutputStream getOutputStream() throws IOException { + if (!sc.isOpen()) + throw new SocketException("Socket is closed"); + if (!sc.isConnected()) + throw new SocketException("Socket is not connected"); + if (!sc.isOutputOpen()) + throw new SocketException("Socket output is shutdown"); + OutputStream os = null; + try { + os = AccessController.doPrivileged( + new PrivilegedExceptionAction() { + public OutputStream run() throws IOException { + return Channels.newOutputStream(sc); + } + }); + } catch (java.security.PrivilegedActionException e) { + throw (IOException)e.getException(); + } + return os; + } + + private void setBooleanOption(SocketOption name, boolean value) + throws SocketException + { + try { + sc.setOption(name, value); + } catch (IOException x) { + RdmaNet.translateToSocketException(x); + } + } + + private void setIntOption(SocketOption name, int value) + throws SocketException + { + try { + sc.setOption(name, value); + } catch (IOException x) { + RdmaNet.translateToSocketException(x); + } + } + + private boolean getBooleanOption(SocketOption name) throws SocketException { + try { + return sc.getOption(name).booleanValue(); + } catch (IOException x) { + RdmaNet.translateToSocketException(x); + return false; // keep compiler happy + } + } + + private int getIntOption(SocketOption name) throws SocketException { + try { + return sc.getOption(name).intValue(); + } catch (IOException x) { + RdmaNet.translateToSocketException(x); + return -1; // keep compiler happy + } + } + + public void setTcpNoDelay(boolean on) throws SocketException { + setBooleanOption(StandardSocketOptions.TCP_NODELAY, on); + } + + public boolean getTcpNoDelay() throws SocketException { + return getBooleanOption(StandardSocketOptions.TCP_NODELAY); + } + + public void sendUrgentData(int data) throws IOException { + int n = sc.sendOutOfBandData((byte) data); + if (n == 0) + throw new IOException("Socket buffer full"); + } + + public void setSoTimeout(int timeout) throws SocketException { + if (timeout < 0) + throw new IllegalArgumentException("timeout can't be negative"); + this.timeout = timeout; + } + + public int getSoTimeout() throws SocketException { + return timeout; + } + + public void setSendBufferSize(int size) throws SocketException { + if (size <= 0) + throw new IllegalArgumentException("Invalid send size"); + setIntOption(StandardSocketOptions.SO_SNDBUF, size); + } + + public int getSendBufferSize() throws SocketException { + return getIntOption(StandardSocketOptions.SO_SNDBUF); + } + + public void setReceiveBufferSize(int size) throws SocketException { + if (size <= 0) + throw new IllegalArgumentException("Invalid receive size"); + setIntOption(StandardSocketOptions.SO_RCVBUF, size); + } + + public int getReceiveBufferSize() throws SocketException { + return getIntOption(StandardSocketOptions.SO_RCVBUF); + } + + public void setReuseAddress(boolean on) throws SocketException { + setBooleanOption(StandardSocketOptions.SO_REUSEADDR, on); + } + + public boolean getReuseAddress() throws SocketException { + return getBooleanOption(StandardSocketOptions.SO_REUSEADDR); + } + + public void close() throws IOException { + sc.close(); + } + + public void shutdownInput() throws IOException { + try { + sc.shutdownInput(); + } catch (Exception x) { + RdmaNet.translateException(x); + } + } + + public void shutdownOutput() throws IOException { + try { + sc.shutdownOutput(); + } catch (Exception x) { + RdmaNet.translateException(x); + } + } + + public String toString() { + if (sc.isConnected()) + return "RdmaSocket[addr=" + getInetAddress() + + ",port=" + getPort() + + ",localport=" + getLocalPort() + "]"; + return "RdmaSocket[unconnected]"; + } + + public boolean isConnected() { + return sc.isConnected(); + } + + public boolean isBound() { + return sc.localAddress() != null; + } + + public boolean isClosed() { + return !sc.isOpen(); + } + + public boolean isInputShutdown() { + return !sc.isInputOpen(); + } + + public boolean isOutputShutdown() { + return !sc.isOutputOpen(); + } +} --- /dev/null 2018-09-16 08:14:05.604314476 -0700 +++ new/src/jdk.net/linux/classes/jdk/internal/net/rdma/RdmaSocketChannelImpl.java 2018-10-05 14:14:47.209757077 -0700 @@ -0,0 +1,963 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.internal.net.rdma; + +import java.io.FileDescriptor; +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.SocketAddress; +import java.net.SocketOption; +import java.net.StandardSocketOptions; +import java.nio.ByteBuffer; +import java.nio.channels.AlreadyBoundException; +import java.nio.channels.AlreadyConnectedException; +import java.nio.channels.AsynchronousCloseException; +import java.nio.channels.ClosedChannelException; +import java.nio.channels.ConnectionPendingException; +import java.nio.channels.NoConnectionPendingException; +import java.nio.channels.NotYetConnectedException; +import java.nio.channels.SelectionKey; +import java.nio.channels.SocketChannel; +import java.nio.channels.spi.SelectorProvider; +import java.util.Collections; +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.locks.ReentrantLock; +import sun.net.ext.RdmaSocketOptions; +import sun.nio.ch.IOStatus; +import sun.nio.ch.IOUtil; +import sun.nio.ch.Net; +import sun.nio.ch.NativeThread; +import sun.nio.ch.SelChImpl; +import sun.nio.ch.SelectionKeyImpl; + +public class RdmaSocketChannelImpl + extends SocketChannel + implements SelChImpl +{ + private static RdmaSocketDispatcher nd; + private final FileDescriptor fd; + private final int fdVal; + + private final ReentrantLock readLock = new ReentrantLock(); + private final ReentrantLock writeLock = new ReentrantLock(); + + private final Object stateLock = new Object(); + + private volatile boolean isInputClosed; + private volatile boolean isOutputClosed; + + private boolean isReuseAddress; + + private static final int ST_UNCONNECTED = 0; + private static final int ST_CONNECTIONPENDING = 1; + private static final int ST_CONNECTED = 2; + private static final int ST_CLOSING = 3; + private static final int ST_KILLPENDING = 4; + private static final int ST_KILLED = 5; + private volatile int state; // need stateLock to change + + private long readerThread; + private long writerThread; + + private InetSocketAddress localAddress; + private InetSocketAddress remoteAddress; + + private Socket socket; + + protected RdmaSocketChannelImpl(SelectorProvider sp) throws IOException { + super(sp); + this.fd = RdmaNet.socket(true); + this.fdVal = IOUtil.fdVal(fd); + } + + protected RdmaSocketChannelImpl(SelectorProvider sp, FileDescriptor fd, boolean bound) + throws IOException + { + super(sp); + this.fd = fd; + this.fdVal = IOUtil.fdVal(fd); + if (bound) { + synchronized (stateLock) { + this.localAddress = RdmaNet.localAddress(fd); + } + } + } + + RdmaSocketChannelImpl(SelectorProvider sp, FileDescriptor fd, InetSocketAddress isa) + throws IOException + { + super(sp); + this.fd = fd; + this.fdVal = IOUtil.fdVal(fd); + synchronized (stateLock) { + this.localAddress = RdmaNet.localAddress(fd); + this.remoteAddress = isa; + this.state = ST_CONNECTED; + } + } + + private void ensureOpen() throws ClosedChannelException { + if (!isOpen()) + throw new ClosedChannelException(); + } + + private void ensureOpenAndConnected() throws ClosedChannelException { + int state = this.state; + if (state < ST_CONNECTED) { + throw new NotYetConnectedException(); + } else if (state > ST_CONNECTED) { + throw new ClosedChannelException(); + } + } + + @Override + public Socket socket() { + synchronized (stateLock) { + if (socket == null) + socket = RdmaSocketAdaptor.create(this); + return socket; + } + } + + @Override + public SocketAddress getLocalAddress() throws IOException { + synchronized (stateLock) { + ensureOpen(); + return RdmaNet.getRevealedLocalAddress(localAddress); + } + } + + @Override + public SocketAddress getRemoteAddress() throws IOException { + synchronized (stateLock) { + ensureOpen(); + return remoteAddress; + } + } + + @Override + public SocketChannel setOption(SocketOption name, T value) + throws IOException + { + Objects.requireNonNull(name); + if (!supportedOptions().contains(name)) + throw new UnsupportedOperationException("'" + name + "' not supported"); + + synchronized (stateLock) { + ensureOpen(); + + if (name == StandardSocketOptions.SO_REUSEADDR && + RdmaNet.useExclusiveBind()) { + isReuseAddress = (Boolean)value; + return this; + } + + RdmaNet.setSocketOption(fd, RdmaNet.UNSPEC, name, value); + return this; + } + } + + @Override + @SuppressWarnings("unchecked") + public T getOption(SocketOption name) + throws IOException + { + Objects.requireNonNull(name); + if (!supportedOptions().contains(name)) + throw new UnsupportedOperationException("'" + name + "' not supported"); + + synchronized (stateLock) { + ensureOpen(); + + if (name == StandardSocketOptions.SO_REUSEADDR + && RdmaNet.useExclusiveBind()) { + return (T)Boolean.valueOf(isReuseAddress); + } + + return (T) RdmaNet.getSocketOption(fd, RdmaNet.UNSPEC, name); + } + } + + private static class DefaultOptionsHolder { + static final Set> defaultOptions = defaultOptions(); + + private static Set> defaultOptions() { + HashSet> set = new HashSet<>(); + set.add(StandardSocketOptions.SO_SNDBUF); + set.add(StandardSocketOptions.SO_RCVBUF); + set.add(StandardSocketOptions.SO_REUSEADDR); + set.add(StandardSocketOptions.TCP_NODELAY); + RdmaSocketOptions rdmaOptions = + RdmaSocketOptions.getInstance(); + set.addAll(rdmaOptions.options()); + return Collections.unmodifiableSet(set); + } + } + + public Set> supportedOptions() { + return DefaultOptionsHolder.defaultOptions; + } + + private void beginRead(boolean blocking) throws ClosedChannelException { + if (blocking) { + // set hook for Thread.interrupt + begin(); + + synchronized (stateLock) { + ensureOpenAndConnected(); + // record thread so it can be signalled if needed + readerThread = NativeThread.current(); + } + } else { + ensureOpenAndConnected(); + } + } + private void endRead(boolean blocking, boolean completed) + throws AsynchronousCloseException + { + if (blocking) { + synchronized (stateLock) { + readerThread = 0; + // notify any thread waiting in implCloseSelectableChannel + if (state == ST_CLOSING) { + stateLock.notifyAll(); + } + } + // remove hook for Thread.interrupt + end(completed); + } + } + + @Override + public int read(ByteBuffer buf) throws IOException { + Objects.requireNonNull(buf); + + readLock.lock(); + try { + boolean blocking = isBlocking(); + int n = 0; + try { + beginRead(blocking); + + // check if input is shutdown + if (isInputClosed) + return IOStatus.EOF; + + if (blocking) { + do { + n = IOUtil.read(fd, buf, -1, nd); + } while (n == IOStatus.INTERRUPTED && isOpen()); + } else { + n = IOUtil.read(fd, buf, -1, nd); + } + } finally { + endRead(blocking, n > 0); + if (n <= 0 && isInputClosed) + return IOStatus.EOF; + } + return IOStatus.normalize(n); + } finally { + readLock.unlock(); + } + } + + @Override + public long read(ByteBuffer[] dsts, int offset, int length) + throws IOException + { + Objects.checkFromIndexSize(offset, length, dsts.length); + + readLock.lock(); + try { + boolean blocking = isBlocking(); + long n = 0; + try { + beginRead(blocking); + + // check if input is shutdown + if (isInputClosed) + return IOStatus.EOF; + + if (blocking) { + do { + n = IOUtil.read(fd, dsts, offset, length, nd); + } while (n == IOStatus.INTERRUPTED && isOpen()); + } else { + n = IOUtil.read(fd, dsts, offset, length, nd); + } + } finally { + endRead(blocking, n > 0); + if (n <= 0 && isInputClosed) + return IOStatus.EOF; + } + return IOStatus.normalize(n); + } finally { + readLock.unlock(); + } + } + + private void beginWrite(boolean blocking) throws ClosedChannelException { + if (blocking) { + // set hook for Thread.interrupt + begin(); + + synchronized (stateLock) { + ensureOpenAndConnected(); + if (isOutputClosed) + throw new ClosedChannelException(); + // record thread so it can be signalled if needed + writerThread = NativeThread.current(); + } + } else { + ensureOpenAndConnected(); + } + } + + private void endWrite(boolean blocking, boolean completed) + throws AsynchronousCloseException + { + if (blocking) { + synchronized (stateLock) { + writerThread = 0; + // notify any thread waiting in implCloseSelectableChannel + if (state == ST_CLOSING) { + stateLock.notifyAll(); + } + } + // remove hook for Thread.interrupt + end(completed); + } + } + + @Override + public int write(ByteBuffer buf) throws IOException { + Objects.requireNonNull(buf); + + writeLock.lock(); + try { + boolean blocking = isBlocking(); + int n = 0; + try { + beginWrite(blocking); + if (blocking) { + do { + n = IOUtil.write(fd, buf, -1, nd); + } while (n == IOStatus.INTERRUPTED && isOpen()); + } else { + n = IOUtil.write(fd, buf, -1, nd); + } + } finally { + endWrite(blocking, n > 0); + if (n <= 0 && isOutputClosed) + throw new AsynchronousCloseException(); + } + return IOStatus.normalize(n); + } finally { + writeLock.unlock(); + } + } + + @Override + public long write(ByteBuffer[] srcs, int offset, int length) + throws IOException + { + Objects.checkFromIndexSize(offset, length, srcs.length); + + writeLock.lock(); + try { + boolean blocking = isBlocking(); + long n = 0; + try { + beginWrite(blocking); + if (blocking) { + do { + n = IOUtil.write(fd, srcs, offset, length, nd); + } while (n == IOStatus.INTERRUPTED && isOpen()); + } else { + n = IOUtil.write(fd, srcs, offset, length, nd); + } + } finally { + endWrite(blocking, n > 0); + if (n <= 0 && isOutputClosed) + throw new AsynchronousCloseException(); + } + return IOStatus.normalize(n); + } finally { + writeLock.unlock(); + } + } + + int sendOutOfBandData(byte b) throws IOException { + writeLock.lock(); + try { + boolean blocking = isBlocking(); + int n = 0; + try { + beginWrite(blocking); + if (blocking) { + do { + n = sendOutOfBandData(fd, b); + } while (n == IOStatus.INTERRUPTED && isOpen()); + } else { + n = sendOutOfBandData(fd, b); + } + } finally { + endWrite(blocking, n > 0); + if (n <= 0 && isOutputClosed) + throw new AsynchronousCloseException(); + } + return IOStatus.normalize(n); + } finally { + writeLock.unlock(); + } + } + + @Override + protected void implConfigureBlocking(boolean block) throws IOException { + readLock.lock(); + try { + writeLock.lock(); + try { + synchronized (stateLock) { + ensureOpen(); + RdmaNet.configureBlocking(fd, block); + } + } finally { + writeLock.unlock(); + } + } finally { + readLock.unlock(); + } + } + + InetSocketAddress localAddress() { + synchronized (stateLock) { + return localAddress; + } + } + + InetSocketAddress remoteAddress() { + synchronized (stateLock) { + return remoteAddress; + } + } + + @Override + public SocketChannel bind(SocketAddress local) throws IOException { + readLock.lock(); + try { + writeLock.lock(); + try { + synchronized (stateLock) { + ensureOpen(); + if (state == ST_CONNECTIONPENDING) + throw new ConnectionPendingException(); + if (localAddress != null) + throw new AlreadyBoundException(); + InetSocketAddress isa = (local == null) ? + new InetSocketAddress(0) : RdmaNet.checkAddress(local); + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkListen(isa.getPort()); + } + RdmaNet.bind(fd, isa.getAddress(), isa.getPort()); + localAddress = RdmaNet.localAddress(fd); + } + } finally { + writeLock.unlock(); + } + } finally { + readLock.unlock(); + } + return this; + } + + @Override + public boolean isConnected() { + return (state == ST_CONNECTED); + } + + @Override + public boolean isConnectionPending() { + return (state == ST_CONNECTIONPENDING); + } + + private void beginConnect(boolean blocking, InetSocketAddress isa) + throws IOException + { + if (blocking) { + // set hook for Thread.interrupt + begin(); + } + synchronized (stateLock) { + ensureOpen(); + int state = this.state; + if (state == ST_CONNECTED) + throw new AlreadyConnectedException(); + if (state == ST_CONNECTIONPENDING) + throw new ConnectionPendingException(); + assert state == ST_UNCONNECTED; + this.state = ST_CONNECTIONPENDING; + + remoteAddress = isa; + + if (blocking) { + // record thread so it can be signalled if needed + readerThread = NativeThread.current(); + } + } + } + + private void endConnect(boolean blocking, boolean completed) + throws IOException + { + endRead(blocking, completed); + + if (completed) { + synchronized (stateLock) { + if (state == ST_CONNECTIONPENDING) { + localAddress = RdmaNet.localAddress(fd); + state = ST_CONNECTED; + } + } + } + } + + @Override + public boolean connect(SocketAddress sa) throws IOException { + InetSocketAddress isa = RdmaNet.checkAddress(sa); + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkConnect(isa.getAddress().getHostAddress(), isa.getPort()); + + InetAddress ia = isa.getAddress(); + if (ia.isAnyLocalAddress()) + ia = InetAddress.getLocalHost(); + + try { + readLock.lock(); + try { + writeLock.lock(); + try { + int n = 0; + boolean blocking = isBlocking(); + try { + beginConnect(blocking, isa); + do { + n = RdmaNet.connect(fd, ia, isa.getPort()); + } while (n == IOStatus.INTERRUPTED && isOpen()); + } finally { + endConnect(blocking, (n > 0)); + } + assert IOStatus.check(n); + return n > 0; + } finally { + writeLock.unlock(); + } + } finally { + readLock.unlock(); + } + } catch (IOException ioe) { + // connect failed, close the channel + close(); + throw ioe; + } + } + + private void beginFinishConnect(boolean blocking) throws ClosedChannelException { + if (blocking) { + // set hook for Thread.interrupt + begin(); + } + synchronized (stateLock) { + ensureOpen(); + if (state != ST_CONNECTIONPENDING) + throw new NoConnectionPendingException(); + if (blocking) { + // record thread so it can be signalled if needed + readerThread = NativeThread.current(); + } + } + } + + private void endFinishConnect(boolean blocking, boolean completed) + throws IOException + { + endRead(blocking, completed); + + if (completed) { + synchronized (stateLock) { + if (state == ST_CONNECTIONPENDING) { + localAddress = RdmaNet.localAddress(fd); + state = ST_CONNECTED; + } + } + } + } + + @Override + public boolean finishConnect() throws IOException { + try { + readLock.lock(); + try { + writeLock.lock(); + try { + // no-op if already connected + if (isConnected()) + return true; + + boolean blocking = isBlocking(); + boolean connected = false; + try { + beginFinishConnect(blocking); + int n = 0; + if (blocking) { + do { + n = checkConnect(fd, true); + } while ((n == 0 || n == IOStatus.INTERRUPTED) && isOpen()); + } else { + n = checkConnect(fd, false); + } + connected = (n > 0); + } finally { + endFinishConnect(blocking, connected); + } + assert (blocking && connected) ^ !blocking; + return connected; + } finally { + writeLock.unlock(); + } + } finally { + readLock.unlock(); + } + } catch (IOException ioe) { + // connect failed, close the channel + close(); + throw ioe; + } + } + + @Override + protected void implCloseSelectableChannel() throws IOException { + assert !isOpen(); + + boolean blocking; + boolean connected; + boolean interrupted = false; + + // set state to ST_CLOSING + synchronized (stateLock) { + assert state < ST_CLOSING; + blocking = isBlocking(); + connected = (state == ST_CONNECTED); + state = ST_CLOSING; + } + + // wait for any outstanding I/O operations to complete + if (blocking) { + synchronized (stateLock) { + assert state == ST_CLOSING; + long reader = readerThread; + long writer = writerThread; + if (reader != 0 || writer != 0) { + nd.preClose(fd); + connected = false; // fd is no longer connected socket + + if (reader != 0) + NativeThread.signal(reader); + if (writer != 0) + NativeThread.signal(writer); + + // wait for blocking I/O operations to end + while (readerThread != 0 || writerThread != 0) { + try { + stateLock.wait(); + } catch (InterruptedException e) { + interrupted = true; + } + } + } + } + } else { + // non-blocking mode: wait for read/write to complete + readLock.lock(); + try { + writeLock.lock(); + writeLock.unlock(); + } finally { + readLock.unlock(); + } + } + + // set state to ST_KILLPENDING + synchronized (stateLock) { + assert state == ST_CLOSING; + // if connected and the channel is registered with a Selector then + // shutdown the output if possible so that the peer reads EOF. + if (connected && isRegistered()) { + try { + RdmaNet.shutdown(fd, RdmaNet.SHUT_WR); + } catch (IOException ignore) { } + } + state = ST_KILLPENDING; + } + + // close socket if not registered with Selector + if (!isRegistered()) + kill(); + + // restore interrupt status + if (interrupted) + Thread.currentThread().interrupt(); + } + + @Override + public void kill() throws IOException { + synchronized (stateLock) { + if (state == ST_KILLPENDING) { + state = ST_KILLED; + nd.close(fd); + } + } + } + + @Override + public SocketChannel shutdownInput() throws IOException { + synchronized (stateLock) { + ensureOpen(); + if (!isConnected()) + throw new NotYetConnectedException(); + if (!isInputClosed) { + RdmaNet.shutdown(fd, RdmaNet.SHUT_RD); + long thread = readerThread; + if (thread != 0) + NativeThread.signal(thread); + isInputClosed = true; + } + return this; + } + } + + @Override + public SocketChannel shutdownOutput() throws IOException { + synchronized (stateLock) { + ensureOpen(); + if (!isConnected()) + throw new NotYetConnectedException(); + if (!isOutputClosed) { + RdmaNet.shutdown(fd, RdmaNet.SHUT_WR); + long thread = writerThread; + if (thread != 0) + NativeThread.signal(thread); + isOutputClosed = true; + } + return this; + } + } + + boolean isInputOpen() { + return !isInputClosed; + } + + boolean isOutputOpen() { + return !isOutputClosed; + } + + /** + * Poll this channel's socket for reading up to the given timeout. + * @return {@code true} if the socket is polled + */ + boolean pollRead(long timeout) throws IOException { + boolean blocking = isBlocking(); + assert Thread.holdsLock(blockingLock()) && blocking; + + readLock.lock(); + try { + boolean polled = false; + try { + beginRead(blocking); + int events = RdmaNet.poll(fd, RdmaNet.POLLIN, timeout); + polled = (events != 0); + } finally { + endRead(blocking, polled); + } + return polled; + } finally { + readLock.unlock(); + } + } + + /** + * Poll this channel's socket for a connection, up to the given timeout. + * @return {@code true} if the socket is polled + */ + boolean pollConnected(long timeout) throws IOException { + boolean blocking = isBlocking(); + assert Thread.holdsLock(blockingLock()) && blocking; + + readLock.lock(); + try { + writeLock.lock(); + try { + boolean polled = false; + try { + beginFinishConnect(blocking); + int events = RdmaNet.poll(fd, RdmaNet.POLLCONN, timeout); + polled = (events != 0); + } finally { + // invoke endFinishConnect with completed = false so that + // the state is not changed to ST_CONNECTED. The socket + // adaptor will use finishConnect to finish. + endFinishConnect(blocking, /*completed*/false); + } + return polled; + } finally { + writeLock.unlock(); + } + } finally { + readLock.unlock(); + } + } + + public boolean translateReadyOps(int ops, int initialOps, SelectionKeyImpl ski) { + int intOps = ski.nioInterestOps(); + int oldOps = ski.nioReadyOps(); + int newOps = initialOps; + + if ((ops & Net.POLLNVAL) != 0) { + return false; + } + + if ((ops & (Net.POLLERR | Net.POLLHUP)) != 0) { + newOps = intOps; + ski.nioReadyOps(newOps); + return (newOps & ~oldOps) != 0; + } + + boolean connected = isConnected(); + if (((ops & Net.POLLIN) != 0) && + ((intOps & SelectionKey.OP_READ) != 0) && connected) + newOps |= SelectionKey.OP_READ; + + if (((ops & Net.POLLCONN) != 0) && + ((intOps & SelectionKey.OP_CONNECT) != 0) && isConnectionPending()) + newOps |= SelectionKey.OP_CONNECT; + + if (((ops & Net.POLLOUT) != 0) && + ((intOps & SelectionKey.OP_WRITE) != 0) && connected) + newOps |= SelectionKey.OP_WRITE; + + ski.nioReadyOps(newOps); + return (newOps & ~oldOps) != 0; + } + + public boolean translateAndUpdateReadyOps(int ops, SelectionKeyImpl ski) { + return translateReadyOps(ops, ski.nioReadyOps(), ski); + } + + public boolean translateAndSetReadyOps(int ops, SelectionKeyImpl ski) { + return translateReadyOps(ops, 0, ski); + } + + public int translateInterestOps(int ops) { + int newOps = 0; + if ((ops & SelectionKey.OP_READ) != 0) + newOps |= Net.POLLIN; + if ((ops & SelectionKey.OP_WRITE) != 0) + newOps |= Net.POLLOUT; + if ((ops & SelectionKey.OP_CONNECT) != 0) + newOps |= Net.POLLCONN; + return newOps; + } + + public FileDescriptor getFD() { + return fd; + } + + public int getFDVal() { + return fdVal; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(this.getClass().getSuperclass().getName()); + sb.append('['); + if (!isOpen()) + sb.append("closed"); + else { + synchronized (stateLock) { + switch (state) { + case ST_UNCONNECTED: + sb.append("unconnected"); + break; + case ST_CONNECTIONPENDING: + sb.append("connection-pending"); + break; + case ST_CONNECTED: + sb.append("connected"); + if (isInputClosed) + sb.append(" ishut"); + if (isOutputClosed) + sb.append(" oshut"); + break; + } + InetSocketAddress addr = localAddress(); + if (addr != null) { + sb.append(" local="); + sb.append(RdmaNet.getRevealedLocalAddressAsString(addr)); + } + if (remoteAddress() != null) { + sb.append(" remote="); + sb.append(remoteAddress().toString()); + } + } + } + sb.append(']'); + return sb.toString(); + } + + // -- Native methods -- + + private static native void initIDs(); + + private static native int checkConnect(FileDescriptor fd, boolean block) + throws IOException; + + private static native int sendOutOfBandData(FileDescriptor fd, byte data) + throws IOException; + + static { + IOUtil.load(); + initIDs(); + nd = new RdmaSocketDispatcher(); + } + +} --- /dev/null 2018-09-16 08:14:05.604314476 -0700 +++ new/src/jdk.net/linux/classes/jdk/internal/net/rdma/RdmaSocketDispatcher.java 2018-10-05 14:14:47.827757084 -0700 @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.internal.net.rdma; + +import java.io.FileDescriptor; +import java.io.IOException; +import java.security.AccessController; +import java.security.PrivilegedAction; +import sun.nio.ch.SocketDispatcher; + +/** + * Allows different platforms to call different native methods + * for read and write operations. + */ + +public class RdmaSocketDispatcher extends SocketDispatcher +{ + private static PlatformRdmaSocketDispatcher platformRdmaSocketDispatcher = + PlatformRdmaSocketDispatcher.get(); + + protected int read(FileDescriptor fd, long address, int len) throws IOException { + return platformRdmaSocketDispatcher.read(fd, address, len); + } + + protected long readv(FileDescriptor fd, long address, int len) throws IOException { + return platformRdmaSocketDispatcher.readv(fd, address, len); + } + + protected int write(FileDescriptor fd, long address, int len) throws IOException { + return platformRdmaSocketDispatcher.write(fd, address, len); + } + + protected long writev(FileDescriptor fd, long address, int len) throws IOException { + return platformRdmaSocketDispatcher.writev(fd, address, len); + } + + protected void close(FileDescriptor fd) throws IOException { + platformRdmaSocketDispatcher.close(fd); + } + + public void preClose(FileDescriptor fd) throws IOException { + platformRdmaSocketDispatcher.preClose(fd); + } + + static class PlatformRdmaSocketDispatcher { + @SuppressWarnings("unchecked") + private static PlatformRdmaSocketDispatcher newInstance(String cn) { + Class c; + try { + c = (Class)Class.forName(cn); + return c.getConstructor(new Class[] {}).newInstance(); + } catch (ReflectiveOperationException x) { + throw new AssertionError(x); + } + } + + private static PlatformRdmaSocketDispatcher create() { + String osname = AccessController.doPrivileged( + new PrivilegedAction() { + public String run() { + return System.getProperty("os.name"); + } + }); + if ("Linux".equals(osname)) + return newInstance("jdk.internal.net.rdma.LinuxRdmaSocketDispatcher"); + return new PlatformRdmaSocketDispatcher(); + } + + private static final PlatformRdmaSocketDispatcher instance = create(); + + static PlatformRdmaSocketDispatcher get() { + return instance; + } + + protected int read(FileDescriptor fd, long address, int len) throws IOException { + throw new UnsupportedOperationException("unsupported socket operation"); + } + + protected long readv(FileDescriptor fd, long address, int len) throws IOException { + throw new UnsupportedOperationException("unsupported socket operation"); + } + + protected int write(FileDescriptor fd, long address, int len) throws IOException { + throw new UnsupportedOperationException("unsupported socket operation"); + } + + protected long writev(FileDescriptor fd, long address, int len) throws IOException { + throw new UnsupportedOperationException("unsupported socket operation"); + } + + protected void close(FileDescriptor fd) throws IOException { + throw new UnsupportedOperationException("unsupported socket operation"); + } + + public void preClose(FileDescriptor fd) throws IOException { + throw new UnsupportedOperationException("unsupported socket operation"); + } + } +} --- /dev/null 2018-09-16 08:14:05.604314476 -0700 +++ new/src/jdk.net/linux/classes/jdk/internal/net/rdma/RdmaSocketImpl.java 2018-10-05 14:14:48.490757092 -0700 @@ -0,0 +1,790 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.internal.net.rdma; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.FileDescriptor; +import java.lang.reflect.Field; +import java.net.Socket; +import java.net.ServerSocket; +import java.net.SocketImpl; +import java.net.SocketOption; +import java.net.SocketException; +import java.net.UnknownHostException; +import java.net.InetAddress; +import java.net.SocketAddress; +import java.net.InetSocketAddress; +import java.net.StandardSocketOptions; +import java.net.SocketOptions; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.Set; +import java.util.HashSet; +import java.util.Collections; +import jdk.internal.misc.JdkRdmaSocketImplAccess; +import jdk.internal.misc.SharedSecrets; +import sun.net.ConnectionResetException; +import sun.net.ext.RdmaSocketOptions; + +public class RdmaSocketImpl extends SocketImpl +{ + Socket socket = null; + ServerSocket serverSocket = null; + + int timeout; // timeout in millisec + + int trafficClass; + + InputStream socketInputStream; + OutputStream socketOutputStream; + + private boolean shut_rd = false; + private boolean shut_wr = false; + + /* number of threads using the FileDescriptor */ + protected int fdUseCount = 0; + + /* lock when increment/decrementing fdUseCount */ + protected final Object fdLock = new Object(); + + /* indicates a close is pending on the file descriptor */ + protected boolean closePending = false; + + /* indicates connection reset state */ + private int CONNECTION_NOT_RESET = 0; + private int CONNECTION_RESET_PENDING = 1; + private int CONNECTION_RESET = 2; + private int resetState; + private final Object resetLock = new Object(); + + /* whether this Socket is a stream (TCP) socket or not (UDP) + */ + protected boolean stream; + + static final sun.net.ext.RdmaSocketOptions rdmaOptions = + sun.net.ext.RdmaSocketOptions.getInstance(); + + private static PlatformRdmaSocketImpl platformRdmaSocketImpl = + PlatformRdmaSocketImpl.get(); + + private static Field sCreateState; + private static Field sBoundState; + private static Field sConnectState; + private static Field ssCreateState; + private static Field ssBoundState; + private static boolean socketStateSet; + private static boolean serverSocketStateSet; + + static { + SharedSecrets.setJdkRdmaSocketImplAccess( + new JdkRdmaSocketImplAccess() { + public void setSocket(SocketImpl si, Socket s) { + ((RdmaSocketImpl)si).setSocket(s); + } + public void setServerSocket(SocketImpl si, + ServerSocket ss) { + ((RdmaSocketImpl)si).setServerSocket(ss); + } + } + ); + } + + boolean isRdmaAvailable() { + return platformRdmaSocketImpl.isRdmaAvailable(); + } + + void setSocket(Socket soc) { + this.socket = soc; + try { + if (!socketStateSet) { + sCreateState = Socket.class.getDeclaredField("created"); + sCreateState.setAccessible(true); + sBoundState = Socket.class.getDeclaredField("bound"); + sBoundState.setAccessible(true); + sConnectState= Socket.class.getDeclaredField("connected"); + sConnectState.setAccessible(true); + sCreateState.setBoolean(socket, false); + sBoundState.setBoolean(socket, false); + sConnectState.setBoolean(socket, false); + } + } catch (NoSuchFieldException | IllegalAccessException e) { + throw new Error(e); + } + socketStateSet = true; + } + + Socket getSocket() { + return socket; + } + + void setServerSocket(ServerSocket soc) { + this.serverSocket = soc; + try { + if (!serverSocketStateSet) { + ssCreateState = ServerSocket.class.getDeclaredField("created"); + ssCreateState.setAccessible(true); + ssBoundState = ServerSocket.class.getDeclaredField("bound"); + ssBoundState.setAccessible(true); + ssCreateState.setBoolean(serverSocket, false); + ssBoundState.setBoolean(serverSocket, false); + } + } catch (NoSuchFieldException | IllegalAccessException e) { + throw new Error(e); + } + serverSocketStateSet = true; + } + + ServerSocket getServerSocket() { + return serverSocket; + } + + private static final Set> socketOptions; + + private static final Set> serverSocketOptions; + + static { + socketOptions = Set.of(StandardSocketOptions.SO_SNDBUF, + StandardSocketOptions.SO_RCVBUF, + StandardSocketOptions.SO_REUSEADDR, + StandardSocketOptions.TCP_NODELAY); + + serverSocketOptions = Set.of(StandardSocketOptions.SO_RCVBUF, + StandardSocketOptions.SO_REUSEADDR); + } + + @Override + protected Set> supportedOptions() { + Set> options = new HashSet<>(); + if (socket != null) + options.addAll(socketOptions); + else + options.addAll(serverSocketOptions); + + if (isRdmaAvailable()) { + RdmaSocketOptions rdmaOptions = + RdmaSocketOptions.getInstance(); + options.addAll(rdmaOptions.options()); + } + options = Collections.unmodifiableSet(options); + return options; + } + + protected synchronized void create(boolean stream) throws IOException { + this.stream = stream; + if (stream) { + fd = new FileDescriptor(); + platformRdmaSocketImpl.rdmaSocketCreate(true, this); + } + try { + if (socket != null) { + sCreateState.setBoolean(socket, true); + } + if (serverSocket != null) + ssCreateState.setBoolean(serverSocket, true); + } catch (IllegalAccessException e) { + throw new AssertionError(e); + } + } + + protected void connect(String host, int port) + throws UnknownHostException, IOException + { + boolean connected = false; + try { + InetAddress address = InetAddress.getByName(host); + this.port = port; + this.address = address; + + connectToAddress(address, port, timeout); + connected = true; + } finally { + if (!connected) { + try { + close(); + } catch (IOException ioe) { + } + } + } + } + + protected void connect(InetAddress address, int port) throws IOException { + this.port = port; + this.address = address; + + try { + connectToAddress(address, port, timeout); + return; + } catch (IOException e) { + close(); + throw e; + } + } + + protected void connect(SocketAddress address, int timeout) + throws IOException { + boolean connected = false; + try { + if (address == null || !(address instanceof InetSocketAddress)) + throw new IllegalArgumentException("unsupported address type"); + InetSocketAddress addr = (InetSocketAddress) address; + if (addr.isUnresolved()) + throw new UnknownHostException(addr.getHostName()); + this.port = addr.getPort(); + this.address = addr.getAddress(); + + connectToAddress(this.address, port, timeout); + connected = true; + } finally { + if (!connected) { + try { + close(); + } catch (IOException ioe) { + } + } + } + } + + private void connectToAddress(InetAddress address, int port, int timeout) throws IOException { + if (address.isAnyLocalAddress()) { + doConnect(InetAddress.getLocalHost(), port, timeout); + } else { + doConnect(address, port, timeout); + } + } + + protected void setOption(SocketOption name, T value) throws IOException { + if (!rdmaOptions.isOptionSupported(name)) { + int opt; + if (name == StandardSocketOptions.SO_SNDBUF && + socket != null) { + opt = SocketOptions.SO_SNDBUF; + } else if (name == StandardSocketOptions.SO_RCVBUF) { + opt = SocketOptions.SO_RCVBUF; + } else if (name == StandardSocketOptions.SO_REUSEADDR) { + opt = SocketOptions.SO_REUSEADDR; + } else if (name == StandardSocketOptions.TCP_NODELAY && + (socket != null)) { + opt = SocketOptions.TCP_NODELAY; + } else { + throw new UnsupportedOperationException("unsupported option"); + } + setOption(opt, value); + } else { + rdmaOptions.setOption(fd, name, value); + } + } + + @SuppressWarnings("unchecked") + protected T getOption(SocketOption name) throws IOException { + if (!rdmaOptions.isOptionSupported(name)) { + int opt; + if (name == StandardSocketOptions.SO_SNDBUF && + (socket != null)) { + opt = SocketOptions.SO_SNDBUF; + } else if (name == StandardSocketOptions.SO_RCVBUF) { + opt = SocketOptions.SO_RCVBUF; + } else if (name == StandardSocketOptions.SO_REUSEADDR) { + opt = SocketOptions.SO_REUSEADDR; + } else if (name == StandardSocketOptions.TCP_NODELAY && + (socket != null)) { + opt = SocketOptions.TCP_NODELAY; + } else { + throw new UnsupportedOperationException("unsupported option"); + } + return (T) getOption(opt); + } else { + return (T) rdmaOptions.getOption(fd, name); + } + } + + public void setOption(int opt, Object val) throws SocketException { + if (isClosedOrPending()) { + throw new SocketException("Socket Closed"); + } + boolean on = true; + switch (opt) { + case SO_TIMEOUT: + if (val == null || (!(val instanceof Integer))) + throw new SocketException("Bad parameter for SO_TIMEOUT"); + int tmp = ((Integer) val).intValue(); + if (tmp < 0) + throw new IllegalArgumentException("timeout < 0"); + timeout = tmp; + break; + case SO_BINDADDR: + throw new SocketException("Cannot re-bind socket"); + case TCP_NODELAY: + if (val == null || !(val instanceof Boolean)) + throw new SocketException("bad parameter for TCP_NODELAY"); + on = ((Boolean)val).booleanValue(); + break; + case SO_SNDBUF: + case SO_RCVBUF: + int value = ((Integer)val).intValue(); + int maxValue = 1024 * 1024 * 1024 - 1; //maximum value for the buffer + if (val == null || !(val instanceof Integer) || + !(value > 0)) { + throw new SocketException("bad parameter for SO_SNDBUF " + + "or SO_RCVBUF"); + } + if (value >= maxValue) + value = maxValue; + break; + case SO_REUSEADDR: + if (val == null || !(val instanceof Boolean)) + throw new SocketException("bad parameter for SO_REUSEADDR"); + on = ((Boolean)val).booleanValue(); + if (serverSocket != null && serverSocket.isBound()) + throw new UnsupportedOperationException( + "RDMA server socket cannot set " + + "SO_REUSEADDR after bind."); + if (socket != null && socket.isConnected()) + throw new UnsupportedOperationException( + "RDMA socket cannot set " + + "SO_REUSEADDR after connect."); + break; + default: + throw new SocketException("unrecognized TCP option: " + opt); + } + socketSetOption(opt, on, val); + } + + public Object getOption(int opt) throws SocketException { + if (isClosedOrPending()) { + throw new SocketException("Socket Closed"); + } + if (opt == SO_TIMEOUT) { + return timeout; + } + int ret = 0; + + switch (opt) { + case TCP_NODELAY: + ret = platformRdmaSocketImpl.rdmaSocketGetOption(this, opt, null); + return Boolean.valueOf(ret != -1); + case SO_REUSEADDR: + ret = platformRdmaSocketImpl.rdmaSocketGetOption(this, opt, null); + return Boolean.valueOf(ret != -1); + case SO_BINDADDR: + RdmaInetAddressContainer in = new RdmaInetAddressContainer(); + ret = platformRdmaSocketImpl.rdmaSocketGetOption(this, opt, in); + return in.addr; + case SO_SNDBUF: + case SO_RCVBUF: + ret = platformRdmaSocketImpl.rdmaSocketGetOption(this, opt, null); + return ret; + default: + return null; + } + } + + protected void socketSetOption(int opt, boolean b, Object val) throws SocketException { + if (opt == SocketOptions.SO_REUSEPORT && + !supportedOptions().contains(StandardSocketOptions.SO_REUSEPORT)) { + throw new UnsupportedOperationException("unsupported option"); + } + try { + platformRdmaSocketImpl.rdmaSocketSetOption(this, opt, b, val); + } catch (SocketException se) { + if (socket == null || !socket.isConnected()) + throw se; + } + } + + synchronized void doConnect(InetAddress address, int port, int timeout) throws IOException { + try { + acquireFD(); + try { + platformRdmaSocketImpl.rdmaSocketConnect(this, address, port, timeout); + synchronized (fdLock) { + if (closePending) { + throw new SocketException ("Socket closed"); + } + } + try { + if (socket != null) { + sBoundState.setBoolean(socket, true); + sConnectState.setBoolean(socket, true); + } + } catch (IllegalAccessException e) { + throw new AssertionError(e); + } + } finally { + releaseFD(); + } + } catch (IOException e) { + close(); + throw e; + } + } + + protected synchronized void bind(InetAddress address, int lport) + throws IOException + { + platformRdmaSocketImpl.rdmaSocketBind(this, address, lport); + try { + if (socket != null) + sBoundState.setBoolean(socket, true); + if (serverSocket != null) + ssBoundState.setBoolean(serverSocket, true); + } catch (IllegalAccessException e) { + throw new AssertionError(e); + } + } + + protected synchronized void listen(int count) throws IOException { + platformRdmaSocketImpl.rdmaSocketListen(this, count); + } + + protected void accept(SocketImpl s) throws IOException { + acquireFD(); + try { + platformRdmaSocketImpl.rdmaSocketAccept(s, this); + } finally { + releaseFD(); + } + } + + protected synchronized InputStream getInputStream() throws IOException { + synchronized (fdLock) { + if (isClosedOrPending()) + throw new IOException("Socket Closed"); + if (shut_rd) + throw new IOException("Socket input is shutdown"); + if (socketInputStream == null) + socketInputStream = platformRdmaSocketImpl.getRdmaInputStream(this); + } + return socketInputStream; + } + + protected synchronized OutputStream getOutputStream() throws IOException { + synchronized (fdLock) { + if (isClosedOrPending()) + throw new IOException("Socket Closed"); + if (shut_wr) + throw new IOException("Socket output is shutdown"); + if (socketOutputStream == null) + socketOutputStream = platformRdmaSocketImpl.getRdmaOutputStream(this); + } + return socketOutputStream; + } + + protected FileDescriptor getFileDescriptor() { + return fd; + } + + protected void setFileDescriptor(FileDescriptor fd) { + this.fd = fd; + } + + protected void setAddress(InetAddress address) { + this.address = address; + } + + void setPort(int port) { + this.port = port; + } + + void setLocalPort(int localport) { + this.localport = localport; + } + + protected synchronized int available() throws IOException { + if (isClosedOrPending()) { + throw new IOException("Stream closed."); + } + + if (isConnectionReset() || shut_rd) { + return 0; + } + + int n = 0; + try { + n = platformRdmaSocketImpl.rdmaSocketAvailable(this); + if (n == 0 && isConnectionResetPending()) { + setConnectionReset(); + } + } catch (ConnectionResetException exc1) { + setConnectionResetPending(); + try { + n = platformRdmaSocketImpl.rdmaSocketAvailable(this); + if (n == 0) { + setConnectionReset(); + } + } catch (ConnectionResetException exc2) { + } + } + return n; + } + + protected void close() throws IOException { + synchronized(fdLock) { + if (fd != null) { + if (fdUseCount == 0) { + if (closePending) { + return; + } + closePending = true; + try { + platformRdmaSocketImpl.rdmaSocketClose(true, this); + } finally { + platformRdmaSocketImpl.rdmaSocketClose(false, this); + } + fd = null; + return; + } else { + if (!closePending) { + closePending = true; + fdUseCount--; + platformRdmaSocketImpl.rdmaSocketClose(true, this); + } + } + } + } + } + + void reset() throws IOException { + if (fd != null) { + platformRdmaSocketImpl.rdmaSocketClose(false, this); + } + fd = null; + postReset(); + } + + void postReset() throws IOException { + address = null; + port = 0; + localport = 0; + } + + protected void shutdownInput() throws IOException { + if (fd != null) { + platformRdmaSocketImpl.rdmaSocketShutdownInput(SHUT_RD, this, socketInputStream); + shut_rd = true; + } + } + + protected void shutdownOutput() throws IOException { + if (fd != null) { + platformRdmaSocketImpl.rdmaSocketShutdown(SHUT_WR, this); + shut_wr = true; + } + } + + protected boolean supportsUrgentData () { + return true; + } + + protected void sendUrgentData (int data) throws IOException { + if (fd == null) { + throw new IOException("Socket Closed"); + } + platformRdmaSocketImpl.rdmaSocketSendUrgentData(this, data); + } + + FileDescriptor acquireFD() { + synchronized (fdLock) { + fdUseCount++; + return fd; + } + } + + void releaseFD() { + synchronized (fdLock) { + fdUseCount--; + if (fdUseCount == -1) { + if (fd != null) { + try { + platformRdmaSocketImpl.rdmaSocketClose(false, this); + } catch (IOException e) { + } finally { + fd = null; + } + } + } + } + } + + public boolean isConnectionReset() { + synchronized (resetLock) { + return (resetState == CONNECTION_RESET); + } + } + + public boolean isConnectionResetPending() { + synchronized (resetLock) { + return (resetState == CONNECTION_RESET_PENDING); + } + } + + public void setConnectionReset() { + synchronized (resetLock) { + resetState = CONNECTION_RESET; + } + } + + public void setConnectionResetPending() { + synchronized (resetLock) { + if (resetState == CONNECTION_NOT_RESET) { + resetState = CONNECTION_RESET_PENDING; + } + } + + } + + public boolean isClosedOrPending() { + synchronized (fdLock) { + if (closePending || (fd == null)) { + return true; + } else { + return false; + } + } + } + + public int getTimeout() { + return timeout; + } + + protected InetAddress getInetAddress() { + return address; + } + + protected int getPort() { + return port; + } + + protected int getLocalPort() { + return localport; + } + + public static final int SHUT_RD = 0; + public static final int SHUT_WR = 1; + + static class PlatformRdmaSocketImpl { + + @SuppressWarnings("unchecked") + private static PlatformRdmaSocketImpl newInstance(String cn) { + Class c; + try { + c = (Class)Class.forName(cn); + return c.getConstructor(new Class[] {}).newInstance(); + } catch (ReflectiveOperationException x) { + throw new AssertionError(x); + } + } + + private static PlatformRdmaSocketImpl create() { + String osname = AccessController.doPrivileged( + new PrivilegedAction() { + public String run() { + return System.getProperty("os.name"); + } + }); + if ("Linux".equals(osname)) + return newInstance("jdk.internal.net.rdma.LinuxRdmaSocketImpl"); + return new PlatformRdmaSocketImpl(); + } + + private static final PlatformRdmaSocketImpl instance = create(); + + static PlatformRdmaSocketImpl get() { + return instance; + } + + boolean isRdmaAvailable() { + return false; + } + + InputStream getRdmaInputStream(RdmaSocketImpl impl) throws IOException { + throw new UnsupportedOperationException("unsupported socket operation"); + } + + OutputStream getRdmaOutputStream(RdmaSocketImpl impl) throws IOException { + throw new UnsupportedOperationException("unsupported socket operation"); + } + + void rdmaSocketClose(boolean useDeferredClose, RdmaSocketImpl impl) throws IOException { + throw new UnsupportedOperationException("unsupported socket operation"); + } + + void rdmaSocketCreate(boolean isServer, RdmaSocketImpl impl) throws IOException { + throw new UnsupportedOperationException("unsupported socket operation"); + } + + void rdmaSocketConnect(RdmaSocketImpl impl, InetAddress address, int port, int timeout) + throws IOException { + throw new UnsupportedOperationException("unsupported socket operation"); + } + + void rdmaSocketBind(RdmaSocketImpl impl, InetAddress address, int port) + throws IOException { + throw new UnsupportedOperationException("unsupported socket operation"); + } + + void rdmaSocketListen(RdmaSocketImpl impl, int count) + throws IOException { + throw new UnsupportedOperationException("unsupported socket operation"); + } + + void rdmaSocketAccept(SocketImpl s, RdmaSocketImpl impl) + throws IOException { + throw new UnsupportedOperationException("unsupported socket operation"); + } + + int rdmaSocketAvailable(RdmaSocketImpl impl) + throws IOException { + throw new UnsupportedOperationException("unsupported socket operation"); + } + + void rdmaSocketShutdown(int howto, RdmaSocketImpl impl) + throws IOException { + throw new UnsupportedOperationException("unsupported socket operation"); + } + + void rdmaSocketShutdownInput(int howto, RdmaSocketImpl impl, + InputStream socketInputStream) throws IOException { + throw new UnsupportedOperationException("unsupported socket operation"); + } + + void rdmaSocketSetOption(RdmaSocketImpl impl, int cmd, boolean on, Object value) + throws SocketException { + throw new UnsupportedOperationException("unsupported socket operation"); + } + + int rdmaSocketGetOption(RdmaSocketImpl impl, int opt, Object iaContainerObj) + throws SocketException { + throw new UnsupportedOperationException("unsupported socket operation"); + } + + void rdmaSocketSendUrgentData(RdmaSocketImpl impl, int data) + throws IOException { + throw new UnsupportedOperationException("unsupported socket operation"); + } + } +} --- /dev/null 2018-09-16 08:14:05.604314476 -0700 +++ new/src/jdk.net/linux/classes/jdk/internal/net/rdma/RdmaSocketInputStream.java 2018-10-05 14:14:49.073757099 -0700 @@ -0,0 +1,210 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.internal.net.rdma; + +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.IOException; +import java.nio.channels.FileChannel; +import java.net.Socket; +import java.net.SocketException; + +import sun.net.ConnectionResetException; + +class RdmaSocketInputStream extends FileInputStream +{ + static { + init(); + } + + private boolean eof; + private RdmaSocketImpl impl = null; + private byte temp[]; + private Socket socket = null; + + RdmaSocketInputStream(RdmaSocketImpl impl) throws IOException { + super(impl.getFileDescriptor()); + this.impl = impl; + socket = impl.getSocket(); + } + + public final FileChannel getChannel() { + return null; + } + + private native int rdmaSocketRead0(FileDescriptor fd, + byte b[], int off, int len, + int timeout) + throws IOException; + + + private int rdmaSocketRead(FileDescriptor fd, + byte b[], int off, int len, + int timeout) + throws IOException { + return rdmaSocketRead0(fd, b, off, len, timeout); + } + + public int read(byte b[]) throws IOException { + return read(b, 0, b.length); + } + + public int read(byte b[], int off, int length) throws IOException { + return read(b, off, length, impl.getTimeout()); + } + + int read(byte b[], int off, int length, int timeout) throws IOException { + int n; + + if (eof) { + return -1; + } + + if (impl.isConnectionReset()) { + throw new SocketException("Connection reset"); + } + + if (length <= 0 || off < 0 || length > b.length - off) { + if (length == 0) { + return 0; + } + throw new ArrayIndexOutOfBoundsException("length == " + length + + " off == " + off + " buffer length == " + b.length); + } + + boolean gotReset = false; + + FileDescriptor fd = impl.acquireFD(); + try { + n = rdmaSocketRead(fd, b, off, length, timeout); + if (n > 0) { + return n; + } + } catch (ConnectionResetException rstExc) { + gotReset = true; + } finally { + impl.releaseFD(); + } + + if (gotReset) { + impl.setConnectionResetPending(); + impl.acquireFD(); + try { + n = rdmaSocketRead(fd, b, off, length, timeout); + if (n > 0) { + return n; + } + } catch (ConnectionResetException rstExc) { + } finally { + impl.releaseFD(); + } + } + + if (impl.isClosedOrPending()) { + throw new SocketException("Socket closed"); + } + if (impl.isConnectionResetPending()) { + impl.setConnectionReset(); + } + if (impl.isConnectionReset()) { + throw new SocketException("Connection reset"); + } + eof = true; + return -1; + } + + /** + * Reads a single byte from the socket. + */ + public int read() throws IOException { + if (eof) { + return -1; + } + temp = new byte[1]; + int n = read(temp, 0, 1); + if (n <= 0) { + return -1; + } + return temp[0] & 0xff; + } + + /** + * Skips n bytes of input. + * @param numbytes the number of bytes to skip + * @return the actual number of bytes skipped. + * @exception IOException If an I/O error has occurred. + */ + public long skip(long numbytes) throws IOException { + if (numbytes <= 0) { + return 0; + } + long n = numbytes; + int buflen = (int) Math.min(1024, n); + byte data[] = new byte[buflen]; + while (n > 0) { + int r = read(data, 0, (int) Math.min((long) buflen, n)); + if (r < 0) { + break; + } + n -= r; + } + return numbytes - n; + } + + /** + * Returns the number of bytes that can be read without blocking. + * @return the number of immediately available bytes + */ + public int available() throws IOException { + return impl.available(); + } + + /** + * Closes the stream. + */ + private boolean closing = false; + public void close() throws IOException { + // Prevent recursion. See BugId 4484411 + if (closing) + return; + closing = true; + if (socket != null) { + if (!socket.isClosed()) + socket.close(); + } else + impl.close(); + closing = false; + } + + void setEOF(boolean eof) { + this.eof = eof; + } + + /** + * Perform class load-time initializations. + */ + private static native void init(); +} --- /dev/null 2018-09-16 08:14:05.604314476 -0700 +++ new/src/jdk.net/linux/classes/jdk/internal/net/rdma/RdmaSocketOptionRegistry.java.template 2018-10-05 14:14:49.658757106 -0700 @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ +@@END_COPYRIGHT@@ + +#include +#include +#include +#include +#include + +/* To be able to name the Java constants the same as the C constants without + having the preprocessor rewrite those identifiers, add PREFIX_ to all + identifiers matching a C constant. The PREFIX_ is filtered out in the + makefile. */ + +@@START_HERE@@ + +package jdk.internal.net.rdma; +import java.net.SocketOption; +import java.net.StandardSocketOptions; +import java.net.ProtocolFamily; +import java.util.Map; +import java.util.HashMap; + +class RdmaSocketOptionRegistry { + + private RdmaSocketOptionRegistry() { } + + private static class RegistryKey { + private final SocketOption name; + private final ProtocolFamily family; + RegistryKey(SocketOption name, ProtocolFamily family) { + this.name = name; + this.family = family; + } + public int hashCode() { + return name.hashCode() + family.hashCode(); + } + public boolean equals(Object ob) { + if (ob == null) return false; + if (!(ob instanceof RegistryKey)) return false; + RegistryKey other = (RegistryKey)ob; + if (this.name != other.name) return false; + if (this.family != other.family) return false; + return true; + } + } + + private static class LazyInitialization { + + static final Map options = options(); + + private static Map options() { + Map map = + new HashMap(); + map.put(new RegistryKey(StandardSocketOptions.PREFIX_SO_SNDBUF, + RdmaNet.UNSPEC), new RdmaOptionKey(SOL_SOCKET, SO_SNDBUF)); + map.put(new RegistryKey(StandardSocketOptions.PREFIX_SO_RCVBUF, + RdmaNet.UNSPEC), new RdmaOptionKey(SOL_SOCKET, SO_RCVBUF)); + map.put(new RegistryKey(StandardSocketOptions.PREFIX_SO_REUSEADDR, + RdmaNet.UNSPEC), new RdmaOptionKey(SOL_SOCKET, SO_REUSEADDR)); + // IPPROTO_TCP is 6 + map.put(new RegistryKey(StandardSocketOptions.PREFIX_TCP_NODELAY, + RdmaNet.UNSPEC), new RdmaOptionKey(6, TCP_NODELAY)); + return map; + } + } + + public static RdmaOptionKey findOption(SocketOption name, ProtocolFamily family) { + RegistryKey key = new RegistryKey(name, family); + return LazyInitialization.options.get(key); + } +} --- /dev/null 2018-09-16 08:14:05.604314476 -0700 +++ new/src/jdk.net/linux/classes/jdk/internal/net/rdma/RdmaSocketOutputStream.java 2018-10-05 14:14:50.199757112 -0700 @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.internal.net.rdma; + +import java.io.FileDescriptor; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.channels.FileChannel; +import java.net.Socket; +import java.net.SocketException; + +class RdmaSocketOutputStream extends FileOutputStream +{ + static { + init(); + } + + private RdmaSocketImpl impl = null; + private byte temp[] = new byte[1]; + private Socket socket = null; + + RdmaSocketOutputStream(RdmaSocketImpl impl) throws IOException { + super(impl.getFileDescriptor()); + this.impl = impl; + socket = impl.getSocket(); + } + + public final FileChannel getChannel() { + return null; + } + + /** + * Writes to the socket. + * @param fd the FileDescriptor + * @param b the data to be written + * @param off the start offset in the data + * @param len the number of bytes that are written + * @exception IOException If an I/O error has occurred. + */ + private native void rdmaSocketWrite0(FileDescriptor fd, byte[] b, int off, + int len) throws IOException; + + /** + * Writes to the socket with appropriate locking of the + * FileDescriptor. + * @param b the data to be written + * @param off the start offset in the data + * @param len the number of bytes that are written + * @exception IOException If an I/O error has occurred. + */ + private void rdmaSocketWrite(byte b[], int off, int len) throws IOException { + + + if (len <= 0 || off < 0 || len > b.length - off) { + if (len == 0) { + return; + } + throw new ArrayIndexOutOfBoundsException("len == " + len + + " off == " + off + " buffer length == " + b.length); + } + + FileDescriptor fd = impl.acquireFD(); + try { + rdmaSocketWrite0(fd, b, off, len); + } catch (SocketException se) { + if (se instanceof sun.net.ConnectionResetException) { + impl.setConnectionResetPending(); + se = new SocketException("Connection reset"); + } + if (impl.isClosedOrPending()) { + throw new SocketException("Socket closed"); + } else { + throw se; + } + } finally { + impl.releaseFD(); + } + } + + /** + * Writes a byte to the socket. + * @param b the data to be written + * @exception IOException If an I/O error has occurred. + */ + public void write(int b) throws IOException { + temp[0] = (byte)b; + rdmaSocketWrite(temp, 0, 1); + } + + /** + * Writes the contents of the buffer b to the socket. + * @param b the data to be written + * @exception SocketException If an I/O error has occurred. + */ + public void write(byte b[]) throws IOException { + rdmaSocketWrite(b, 0, b.length); + } + + /** + * Writes length bytes from buffer b starting at + * offset len. + * @param b the data to be written + * @param off the start offset in the data + * @param len the number of bytes that are written + * @exception SocketException If an I/O error has occurred. + */ + public void write(byte b[], int off, int len) throws IOException { + rdmaSocketWrite(b, off, len); + } + + /** + * Closes the stream. + */ + private boolean closing = false; + public void close() throws IOException { + if (closing) + return; + closing = true; + if (socket != null) { + if (!socket.isClosed()) + socket.close(); + } else + impl.close(); + closing = false; + } + + /** + * Perform class load-time initializations. + */ + private static native void init(); + +} --- /dev/null 2018-09-16 08:14:05.604314476 -0700 +++ new/src/jdk.net/linux/classes/jdk/internal/net/rdma/RdmaSocketProvider.java 2018-10-05 14:14:50.836757120 -0700 @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.internal.net.rdma; + +import java.io.IOException; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.SocketException; + +public class RdmaSocketProvider { + public static Socket openSocket() throws IOException { + RdmaSocketImpl impl = new RdmaSocketImpl(); + Socket s = new Socket(impl) {}; + return s; + } + + public static ServerSocket openServerSocket() throws IOException { + RdmaSocketImpl impl = new RdmaSocketImpl(); + ServerSocket ss = new ServerSocket(impl) { + public Socket accept() throws IOException { + if (isClosed()) + throw new SocketException("Socket is closed"); + if (!isBound()) + throw new SocketException("Socket is not bound yet"); + + Socket s = openSocket(); + implAccept(s); + return s; + } + }; + return ss; + } + + private RdmaSocketProvider() {} +} --- /dev/null 2018-09-16 08:14:05.604314476 -0700 +++ new/src/jdk.net/linux/classes/jdk/net/LinuxRdmaSocketOptions.java 2018-10-05 14:14:51.404757126 -0700 @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.net; + +import java.net.SocketException; +import jdk.net.RdmaSocketOptions.PlatformRdmaSocketOptions; + +class LinuxRdmaSocketOptions extends PlatformRdmaSocketOptions { + + public LinuxRdmaSocketOptions() { } + + @Override native void setSockOpt(int fd, int opt, int value) + throws SocketException; + + @Override native int getSockOpt(int fd, int opt) + throws SocketException; + + @Override native boolean rdmaSocketSupported(); + + static { + java.security.AccessController.doPrivileged( + new java.security.PrivilegedAction<>() { + public Void run() { + System.loadLibrary("extnet"); + return null; + } + }); + init(); + } + private static native void init(); +} --- /dev/null 2018-09-16 08:14:05.604314476 -0700 +++ new/src/jdk.net/linux/native/libextnet/LinuxRdmaSocketDispatcherImpl.c 2018-10-05 14:14:52.008757133 -0700 @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include "jdk_internal_net_rdma_LinuxRdmaSocketDispatcherImpl.h" +#include "nio.h" +#include "nio_util.h" +#include + +static int preCloseFD = -1; /* File descriptor to which we dup other fd's + before closing them for real */ +static jfieldID fd_fdID; + +jint +convertReturnVal(JNIEnv *env, jint n, jboolean reading) +{ + if (n > 0) + return n; + else if (n == 0) { + if (reading) { + return IOS_EOF; + } else { + return 0; + } + } + else if (errno == EAGAIN) + return IOS_UNAVAILABLE; + else if (errno == EINTR) + return IOS_INTERRUPTED; + else { + const char *msg = reading ? "Read failed" : "Write failed"; + JNU_ThrowIOExceptionWithLastError(env, msg); + return IOS_THROWN; + } +} + +jlong +convertLongReturnVal(JNIEnv *env, jlong n, jboolean reading) +{ + if (n > 0) + return n; + else if (n == 0) { + if (reading) { + return IOS_EOF; + } else { + return 0; + } + } + else if (errno == EAGAIN) + return IOS_UNAVAILABLE; + else if (errno == EINTR) + return IOS_INTERRUPTED; + else { + const char *msg = reading ? "Read failed" : "Write failed"; + JNU_ThrowIOExceptionWithLastError(env, msg); + return IOS_THROWN; + } +} + +JNIEXPORT void JNICALL +Java_jdk_internal_net_rdma_LinuxRdmaSocketDispatcherImpl_init(JNIEnv *env, jclass cl) +{ + loadRdmaFuncs(env); + CHECK_NULL(cl = (*env)->FindClass(env, "java/io/FileDescriptor")); + CHECK_NULL(fd_fdID = (*env)->GetFieldID(env, cl, "fd", "I")); + + int sp[2]; + if (socketpair(PF_UNIX, SOCK_STREAM, 0, sp) < 0) { + JNU_ThrowIOExceptionWithLastError(env, "socketpair failed"); + return; + } + preCloseFD = sp[0]; + rs_close(sp[1]); +} + +JNIEXPORT jint JNICALL +Java_jdk_internal_net_rdma_LinuxRdmaSocketDispatcherImpl_read0(JNIEnv *env, jclass clazz, + jobject fdo, jlong address, jint len) +{ + jint fd = (*env)->GetIntField(env, fdo, fd_fdID); + void *buf = (void *)jlong_to_ptr(address); + return convertReturnVal(env, rs_read(fd, buf, len), JNI_TRUE); +} + +JNIEXPORT jlong JNICALL +Java_jdk_internal_net_rdma_LinuxRdmaSocketDispatcherImpl_readv0(JNIEnv *env, jclass clazz, + jobject fdo, jlong address, jint len) +{ + jint fd = (*env)->GetIntField(env, fdo, fd_fdID); + struct iovec *iov = (struct iovec *)jlong_to_ptr(address); + return convertLongReturnVal(env, rs_readv(fd, iov, len), JNI_TRUE); +} + +JNIEXPORT jint JNICALL +Java_jdk_internal_net_rdma_LinuxRdmaSocketDispatcherImpl_write0(JNIEnv *env, jclass clazz, + jobject fdo, jlong address, jint len) +{ + jint fd = (*env)->GetIntField(env, fdo, fd_fdID); + void *buf = (void *)jlong_to_ptr(address); + return convertReturnVal(env, rs_write(fd, buf, len), JNI_FALSE); +} + +JNIEXPORT jlong JNICALL +Java_jdk_internal_net_rdma_LinuxRdmaSocketDispatcherImpl_writev0(JNIEnv *env, jclass clazz, + jobject fdo, jlong address, jint len) +{ + jint fd = (*env)->GetIntField(env, fdo, fd_fdID); + struct iovec *iov = (struct iovec *)jlong_to_ptr(address); + return convertLongReturnVal(env, rs_writev(fd, iov, len), JNI_FALSE); +} + +static void closeFileDescriptor(JNIEnv *env, int fd) { + if (fd != -1) { + int result = rs_close(fd); + if (result < 0) + JNU_ThrowIOExceptionWithLastError(env, "Close failed"); + } +} + +JNIEXPORT void JNICALL +Java_jdk_internal_net_rdma_LinuxRdmaSocketDispatcherImpl_close0(JNIEnv *env, jclass clazz, jobject fdo) +{ + jint fd = (*env)->GetIntField(env, fdo, fd_fdID); + closeFileDescriptor(env, fd); +} --- /dev/null 2018-09-16 08:14:05.604314476 -0700 +++ new/src/jdk.net/linux/native/libextnet/LinuxRdmaSocketImpl.c 2018-10-05 14:14:52.542757140 -0700 @@ -0,0 +1,746 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include "java_net_SocketOptions.h" +#include "jdk_internal_net_rdma_LinuxRdmaSocketImpl.h" +#include "jvm.h" +#include "net_util.h" +#include "rdma_util_md.h" +#include "Rsocket.h" + +/************************************************************************ + * RdmaSocketImpl + */ + +static jfieldID IO_fd_fdID; + +jfieldID psi_fdID; +jfieldID psi_addressID; +jfieldID psi_ipaddressID; +jfieldID psi_portID; +jfieldID psi_localportID; +jfieldID psi_timeoutID; +jfieldID psi_trafficClassID; +jfieldID psi_serverSocketID; +jfieldID psi_fdLockID; +jfieldID psi_closePendingID; + + +/* + * file descriptor used for dup2 + */ +static int marker_fd = -1; + +#define SET_NONBLOCKING(fd) { \ + int flags = rs_fcntl(fd, F_GETFL); \ + flags |= O_NONBLOCK; \ + rs_fcntl(fd, F_SETFL, flags); \ +} + +#define SET_BLOCKING(fd) { \ + int flags = rs_fcntl(fd, F_GETFL); \ + flags &= ~O_NONBLOCK; \ + rs_fcntl(fd, F_SETFL, flags); \ +} + +static jclass socketExceptionCls; + +static int getMarkerFD() +{ + int sv[2]; + +#ifdef AF_UNIX + if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv) == -1) { + return -1; + } +#else + return -1; +#endif + + rs_shutdown(sv[0], 2); + rs_close(sv[1]); + + return sv[0]; +} + +static int getFD(JNIEnv *env, jobject this) { + jobject fdObj = (*env)->GetObjectField(env, this, psi_fdID); + CHECK_NULL_RETURN(fdObj, -1); + return (*env)->GetIntField(env, fdObj, IO_fd_fdID); +} + + +jfieldID +NET_GetFileDescriptorID(JNIEnv *env) +{ + jclass cls = (*env)->FindClass(env, "java/io/FileDescriptor"); + CHECK_NULL_RETURN(cls, NULL); + return (*env)->GetFieldID(env, cls, "fd", "I"); +} + +JNIEXPORT void JNICALL +Java_jdk_internal_net_rdma_LinuxRdmaSocketImpl_initProto(JNIEnv *env, jclass cls) { + loadRdmaFuncs(env); + + jclass clazz = (*env)->FindClass(env, "jdk/internal/net/rdma/RdmaSocketImpl"); + psi_fdID = (*env)->GetFieldID(env, clazz , "fd", + "Ljava/io/FileDescriptor;"); + CHECK_NULL(psi_fdID); + psi_addressID = (*env)->GetFieldID(env, clazz, "address", + "Ljava/net/InetAddress;"); + CHECK_NULL(psi_addressID); + psi_timeoutID = (*env)->GetFieldID(env, clazz, "timeout", "I"); + CHECK_NULL(psi_timeoutID); + psi_trafficClassID = (*env)->GetFieldID(env, clazz, "trafficClass", "I"); + CHECK_NULL(psi_trafficClassID); + psi_portID = (*env)->GetFieldID(env, clazz, "port", "I"); + CHECK_NULL(psi_portID); + psi_localportID = (*env)->GetFieldID(env, clazz, "localport", "I"); + CHECK_NULL(psi_localportID); + psi_serverSocketID = (*env)->GetFieldID(env, clazz, "serverSocket", + "Ljava/net/ServerSocket;"); + CHECK_NULL(psi_serverSocketID); + + IO_fd_fdID = NET_GetFileDescriptorID(env); + CHECK_NULL(IO_fd_fdID); + + initInetAddressIDs(env); + JNU_CHECK_EXCEPTION(env); + marker_fd = getMarkerFD(); +} + +JNIEXPORT jboolean JNICALL +Java_jdk_internal_net_rdma_LinuxRdmaSocketImpl_isRdmaAvailable0(JNIEnv *env, jclass cls) { + return rdma_supported(); +} + +void +NET_SetTrafficClass(SOCKETADDRESS *sa, int trafficClass) { + if (sa->sa.sa_family == AF_INET6) { + sa->sa6.sin6_flowinfo = htonl((trafficClass & 0xff) << 20); + } +} + +/* + * Class: jdk_net_LinuxRdmaSocketImpl + * Method: rdmaSocketCreate + * Signature: (Z)V */ +JNIEXPORT void JNICALL +Java_jdk_internal_net_rdma_LinuxRdmaSocketImpl_rdmaSocketCreate(JNIEnv *env, jobject this, + jboolean stream, jobject impl) { + jobject fdObj, ssObj; + int fd; + int type = (stream ? SOCK_STREAM : SOCK_DGRAM); + int domain = ipv6_available() ? AF_INET6 : AF_INET; + + if (socketExceptionCls == NULL) { + jclass c = (*env)->FindClass(env, "java/net/SocketException"); + CHECK_NULL(c); + socketExceptionCls = (jclass)(*env)->NewGlobalRef(env, c); + CHECK_NULL(socketExceptionCls); + } + + fdObj = (*env)->GetObjectField(env, impl, psi_fdID); + + if (fdObj == NULL) { + (*env)->ThrowNew(env, socketExceptionCls, "null fd object"); + return; + } + + if ((fd = rs_socket(domain, type, 0)) == -1) { + NET_ThrowNew(env, errno, "can't create socket"); + return; + } + if (domain == AF_INET6) { + int arg = 0; + if (rs_setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&arg, + sizeof(int)) < 0) { + NET_ThrowNew(env, errno, "cannot set IPPROTO_IPV6"); + rs_close(fd); + return; + } + } + + ssObj = (*env)->GetObjectField(env, impl, psi_serverSocketID); + if (ssObj != NULL) { + int arg = 1; + SET_NONBLOCKING(fd); + if (RDMA_SetSockOpt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&arg, + sizeof(arg)) < 0) { + NET_ThrowNew(env, errno, "cannot set SO_REUSEADDR"); + rs_close(fd); + return; + } + } + (*env)->SetIntField(env, fdObj, IO_fd_fdID, fd); +} + +JNIEXPORT void JNICALL +Java_jdk_internal_net_rdma_LinuxRdmaSocketImpl_rdmaSocketConnect(JNIEnv *env, jobject this, jobject impl, + jobject iaObj, jint port, + jint timeout) +{ + jint localport = (*env)->GetIntField(env, impl, psi_localportID); + int len = 0; + jobject fdObj = (*env)->GetObjectField(env, impl, psi_fdID); + + jobject fdLock; + + jint trafficClass = (*env)->GetIntField(env, impl, psi_trafficClassID); + + jint fd; + + SOCKETADDRESS sa; + int connect_rv = -1; + + if (IS_NULL(fdObj)) { + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Socket closed"); + return; + } else { + fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID); + } + if (IS_NULL(iaObj)) { + JNU_ThrowNullPointerException(env, "inet address argument null."); + return; + } + + if (NET_InetAddressToSockaddr(env, iaObj, port, &sa, &len, + JNI_TRUE) != 0) { + return; + } + + if (trafficClass != 0 && ipv6_available()) { + NET_SetTrafficClass(&sa, trafficClass); + } + + if (timeout <= 0) { + connect_rv = RDMA_Connect(fd, &sa.sa, len); + } else { + SET_NONBLOCKING(fd); + + connect_rv = rs_connect(fd, &sa.sa, len); + + if (connect_rv != 0) { + socklen_t optlen; + jlong nanoTimeout = (jlong) timeout * NET_NSEC_PER_MSEC; + jlong prevNanoTime = JVM_NanoTime(env, 0); + + if (errno != EINPROGRESS) { + JNU_ThrowByNameWithMessageAndLastError(env, JNU_JAVANETPKG "ConnectException", + "connect failed"); + SET_BLOCKING(fd); + return; + } + + while (1) { + jlong newNanoTime; + struct pollfd pfd; + pfd.fd = fd; + pfd.events = POLLOUT; + + errno = 0; + + connect_rv = RDMA_Poll(&pfd, 1, nanoTimeout / NET_NSEC_PER_MSEC); + + if (connect_rv >= 0) { + break; + } + if (errno != EINTR) { + break; + } + + newNanoTime = JVM_NanoTime(env, 0); + nanoTimeout -= (newNanoTime - prevNanoTime); + if (nanoTimeout < NET_NSEC_PER_MSEC) { + connect_rv = 0; + break; + } + prevNanoTime = newNanoTime; + + } + + if (connect_rv == 0) { + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketTimeoutException", + "connect timed out"); + + SET_BLOCKING(fd); + rs_shutdown(fd, 2); + return; + } + + optlen = sizeof(connect_rv); + if (rs_getsockopt(fd, SOL_SOCKET, SO_ERROR, (void*)&connect_rv, + &optlen) <0) { + connect_rv = errno; + } + } + + SET_BLOCKING(fd); + + if (connect_rv != 0) { + errno = connect_rv; + connect_rv = -1; + } + } + + if (connect_rv < 0) { + if (connect_rv == -1 && errno == EINVAL) { + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", + "Invalid argument or cannot assign requested address"); + return; + } +#if defined(EPROTO) + if (errno == EPROTO) { + JNU_ThrowByNameWithMessageAndLastError(env, JNU_JAVANETPKG "ProtocolException", + "Protocol error"); + return; + } +#endif + if (errno == ECONNREFUSED) { + JNU_ThrowByNameWithMessageAndLastError(env, JNU_JAVANETPKG "ConnectException", + "Connection refused"); + } else if (errno == ETIMEDOUT) { + JNU_ThrowByNameWithMessageAndLastError(env, JNU_JAVANETPKG "ConnectException", + "Connection timed out"); + } else if (errno == EHOSTUNREACH) { + JNU_ThrowByNameWithMessageAndLastError(env, JNU_JAVANETPKG "NoRouteToHostException", + "Host unreachable"); + } else if (errno == EADDRNOTAVAIL) { + JNU_ThrowByNameWithMessageAndLastError(env, JNU_JAVANETPKG "NoRouteToHostException", + "Address not available"); + } else if ((errno == EISCONN) || (errno == EBADF)) { + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", + "Socket closed"); + } else { + JNU_ThrowByNameWithMessageAndLastError + (env, JNU_JAVANETPKG "SocketException", "connect failed"); + } + return; + } + + (*env)->SetIntField(env, fdObj, IO_fd_fdID, fd); + + (*env)->SetObjectField(env, impl, psi_addressID, iaObj); + (*env)->SetIntField(env, impl, psi_portID, port); + + if (localport == 0) { + socklen_t slen = sizeof(SOCKETADDRESS); + if (rs_getsockname(fd, &sa.sa, &slen) == -1) { + JNU_ThrowByNameWithMessageAndLastError + (env, JNU_JAVANETPKG "SocketException", "Error getting socket name"); + } else { + localport = NET_GetPortFromSockaddr(&sa); + (*env)->SetIntField(env, impl, psi_localportID, localport); + } + } +} + +JNIEXPORT void JNICALL +Java_jdk_internal_net_rdma_LinuxRdmaSocketImpl_rdmaSocketBind(JNIEnv *env, jobject this, jobject impl, + jobject iaObj, jint localport) { + jobject fdObj = (*env)->GetObjectField(env, impl, psi_fdID); + int fd; + int len = 0; + SOCKETADDRESS sa; + + if (IS_NULL(fdObj)) { + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", + "Socket closed"); + return; + } else { + fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID); + } + if (IS_NULL(iaObj)) { + JNU_ThrowNullPointerException(env, "iaObj is null."); + return; + } + + if (NET_InetAddressToSockaddr(env, iaObj, localport, &sa, + &len, JNI_TRUE) != 0) { + return; + } + + if (RDMA_Bind(fd, &sa, len) < 0) { + if (errno == EADDRINUSE || errno == EADDRNOTAVAIL || + errno == EPERM || errno == EACCES) { + JNU_ThrowByNameWithMessageAndLastError(env, JNU_JAVANETPKG "BindException", + "Bind failed"); + } else { + JNU_ThrowByNameWithMessageAndLastError + (env, JNU_JAVANETPKG "SocketException", "Bind failed"); + } + return; + } + (*env)->SetObjectField(env, impl, psi_addressID, iaObj); + + if (localport == 0) { + socklen_t slen = sizeof(SOCKETADDRESS); + if (rs_getsockname(fd, &sa.sa, &slen) == -1) { + JNU_ThrowByNameWithMessageAndLastError + (env, JNU_JAVANETPKG "SocketException", "Error getting socket name"); + return; + } + localport = NET_GetPortFromSockaddr(&sa); + (*env)->SetIntField(env, impl, psi_localportID, localport); + } else { + (*env)->SetIntField(env, impl, psi_localportID, localport); + } +} + +JNIEXPORT void JNICALL +Java_jdk_internal_net_rdma_LinuxRdmaSocketImpl_rdmaSocketListen(JNIEnv *env, jobject this, jobject impl, + jint count) +{ + jobject fdObj = (*env)->GetObjectField(env, impl, psi_fdID); + int fd; + + if (IS_NULL(fdObj)) { + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", + "Socket closed"); + return; + } else { + fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID); + } + + if (count == 0x7fffffff) + count -= 1; + + int rv = rs_listen(fd, count); + if (rv == -1) { + JNU_ThrowByNameWithMessageAndLastError + (env, JNU_JAVANETPKG "SocketException", "Listen failed"); + } +} + +JNIEXPORT void JNICALL +Java_jdk_internal_net_rdma_LinuxRdmaSocketImpl_rdmaSocketAccept(JNIEnv *env, jobject this, jobject socket, jobject impl) +{ + int port; + jint timeout = (*env)->GetIntField(env, impl, psi_timeoutID); + jlong prevNanoTime = 0; + jlong nanoTimeout = (jlong) timeout * NET_NSEC_PER_MSEC; + jobject fdObj = (*env)->GetObjectField(env, impl, psi_fdID); + + jobject socketFdObj; + jobject socketAddressObj; + + jint fd; + + jint newfd; + + SOCKETADDRESS sa; + socklen_t slen = sizeof(SOCKETADDRESS); + + if (IS_NULL(fdObj)) { + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", + "Socket closed"); + return; + } else { + fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID); + } + if (IS_NULL(socket)) { + JNU_ThrowNullPointerException(env, "socket is null"); + return; + } + + for (;;) { + int ret; + jlong currNanoTime; + + if (prevNanoTime == 0 && nanoTimeout > 0) { + prevNanoTime = JVM_NanoTime(env, 0); + } + + if (timeout <= 0) { + ret = RDMA_Timeout(env, fd, -1, 0); + } else { + ret = RDMA_Timeout(env, fd, nanoTimeout / NET_NSEC_PER_MSEC, prevNanoTime); + } + + if (ret == 0) { + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketTimeoutException", + "Accept timed out"); + return; + } else if (ret == -1) { + if (errno == EBADF) { + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Socket closed"); + } else if (errno == ENOMEM) { + JNU_ThrowOutOfMemoryError(env, "NET_Timeout native heap allocation failed"); + } else { + JNU_ThrowByNameWithMessageAndLastError + (env, JNU_JAVANETPKG "SocketException", "Accept failed"); + } + return; + } + + newfd = RDMA_Accept(fd, &sa.sa, &slen); + + if (newfd >= 0) { + SET_BLOCKING(newfd); + break; + } + + if (!(errno == ECONNABORTED || errno == EWOULDBLOCK)) { + break; + } + + if (nanoTimeout >= NET_NSEC_PER_MSEC) { + currNanoTime = JVM_NanoTime(env, 0); + nanoTimeout -= (currNanoTime - prevNanoTime); + if (nanoTimeout < NET_NSEC_PER_MSEC) { + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketTimeoutException", + "Accept timed out"); + return; + } + prevNanoTime = currNanoTime; + } + } + + if (newfd < 0) { + if (newfd == -2) { + JNU_ThrowByName(env, JNU_JAVAIOPKG "InterruptedIOException", + "operation interrupted"); + } else { + if (errno == EINVAL) { + errno = EBADF; + } + if (errno == EBADF) { + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Socket closed"); + } else { + JNU_ThrowByNameWithMessageAndLastError + (env, JNU_JAVANETPKG "SocketException", "Accept failed"); + } + } + return; + } + + socketAddressObj = NET_SockaddrToInetAddress(env, &sa, &port); + if (socketAddressObj == NULL) { + rs_close(newfd); + return; + } + + socketFdObj = (*env)->GetObjectField(env, socket, psi_fdID); + (*env)->SetIntField(env, socketFdObj, IO_fd_fdID, newfd); + (*env)->SetObjectField(env, socket, psi_addressID, socketAddressObj); + (*env)->SetIntField(env, socket, psi_portID, port); + port = (*env)->GetIntField(env, impl, psi_localportID); + (*env)->SetIntField(env, socket, psi_localportID, port); +} + + +JNIEXPORT jint JNICALL +Java_jdk_internal_net_rdma_LinuxRdmaSocketImpl_rdmaSocketAvailable(JNIEnv *env, jobject this, jobject impl) { + jint ret = -1; + jobject fdObj = (*env)->GetObjectField(env, impl, psi_fdID); + jint fd; + + if (IS_NULL(fdObj)) { + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", + "Socket closed"); + return -1; + } else { + fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID); + } + if (RDMA_SocketAvailable(fd, &ret) == 0){ + if (errno == ECONNRESET) { + JNU_ThrowByName(env, "sun/net/ConnectionResetException", ""); + } else { + JNU_ThrowByNameWithMessageAndLastError + (env, JNU_JAVANETPKG "SocketException", "ioctl FIONREAD failed"); + } + } + return ret; +} + +JNIEXPORT void JNICALL +Java_jdk_internal_net_rdma_LinuxRdmaSocketImpl_rdmaSocketClose(JNIEnv *env, jobject this, + jboolean useDeferredClose, jobject impl) { + jobject fdObj = (*env)->GetObjectField(env, impl, psi_fdID); + jint fd; + + if (IS_NULL(fdObj)) { + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", + "socket already closed"); + return; + } else { + fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID); + } + if (fd != -1) { + if (useDeferredClose && marker_fd >= 0) { + RDMA_Dup2(marker_fd, fd); + } else { + (*env)->SetIntField(env, fdObj, IO_fd_fdID, -1); + RDMA_SocketClose(fd); + } + } +} + +JNIEXPORT void JNICALL +Java_jdk_internal_net_rdma_LinuxRdmaSocketImpl_rdmaSocketShutdown(JNIEnv *env, jobject this, + jint howto, jobject impl) +{ + jobject fdObj = (*env)->GetObjectField(env, impl, psi_fdID); + jint fd; + + if (IS_NULL(fdObj)) { + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", + "socket already closed"); + return; + } else { + fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID); + } + rs_shutdown(fd, howto); +} + + +JNIEXPORT void JNICALL +Java_jdk_internal_net_rdma_LinuxRdmaSocketImpl_rdmaSocketSetOption + (JNIEnv *env, jobject this, jobject impl, jint cmd, jboolean on, jobject value) +{ + int fd; + int level, optname, optlen; + union { + int i; + struct linger ling; + } optval; + + fd = getFD(env, impl); + if (fd < 0) { + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", + "Socket closed"); + return; + } + + if (cmd == java_net_SocketOptions_SO_TIMEOUT) { + return; + } + + if (RDMA_MapSocketOption(cmd, &level, &optname)) { + JNU_ThrowByName(env, "java/net/SocketException", "Invalid option"); + return; + } + + switch (cmd) { + case java_net_SocketOptions_SO_SNDBUF : + case java_net_SocketOptions_SO_RCVBUF : + default : + optval.i = (on ? 1 : 0); + optlen = sizeof(optval.i); + + } + if (RDMA_SetSockOpt(fd, level, optname, (const void *)&optval, optlen) < 0) { + JNU_ThrowByNameWithMessageAndLastError + (env, JNU_JAVANETPKG "SocketException", "Error setting socket option"); + } +} + +JNIEXPORT jint JNICALL +Java_jdk_internal_net_rdma_LinuxRdmaSocketImpl_rdmaSocketGetOption + (JNIEnv *env, jobject this, jobject impl, jint cmd, jobject iaContainerObj) +{ + int fd; + int level, optname, optlen; + union { + int i; + struct linger ling; + } optval; + + fd = getFD(env, impl); + if (fd < 0) { + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", + "Socket closed"); + return -1; + } + + if (cmd == java_net_SocketOptions_SO_BINDADDR) { + SOCKETADDRESS sa; + socklen_t len = sizeof(SOCKETADDRESS); + int port; + jobject iaObj; + jclass iaCntrClass; + jfieldID iaFieldID; + + if (rs_getsockname(fd, &sa.sa, &len) < 0) { + JNU_ThrowByNameWithMessageAndLastError + (env, JNU_JAVANETPKG "SocketException", "Error getting socket name"); + return -1; + } + iaObj = NET_SockaddrToInetAddress(env, &sa, &port); + CHECK_NULL_RETURN(iaObj, -1); + + iaCntrClass = (*env)->GetObjectClass(env, iaContainerObj); + iaFieldID = (*env)->GetFieldID(env, iaCntrClass, "addr", "Ljava/net/InetAddress;"); + CHECK_NULL_RETURN(iaFieldID, -1); + (*env)->SetObjectField(env, iaContainerObj, iaFieldID, iaObj); + return 0; + } + + if (RDMA_MapSocketOption(cmd, &level, &optname)) { + JNU_ThrowByName(env, "java/net/SocketException", "Invalid option"); + return -1; + } + + optlen = sizeof(optval.i); + + if (RDMA_GetSockOpt(fd, level, optname, (void *)&optval, &optlen) < 0) { + JNU_ThrowByNameWithMessageAndLastError + (env, JNU_JAVANETPKG "SocketException", "Error getting socket option"); + return -1; + } + + switch (cmd) { + case java_net_SocketOptions_SO_SNDBUF: + case java_net_SocketOptions_SO_RCVBUF: + return optval.i; + + default : + return (optval.i == 0) ? -1 : 1; + } +} + +JNIEXPORT void JNICALL +Java_jdk_internal_net_rdma_LinuxRdmaSocketImpl_rdmaSocketSendUrgentData(JNIEnv *env, jobject this, jobject impl, + jint data) { + jobject fdObj = (*env)->GetObjectField(env, impl, psi_fdID); + int n, fd; + unsigned char d = data & 0xFF; + + if (IS_NULL(fdObj)) { + JNU_ThrowByName(env, "java/net/SocketException", "Socket closed"); + return; + } else { + fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID); + if (fd == -1) { + JNU_ThrowByName(env, "java/net/SocketException", "Socket closed"); + return; + } + + } + n = RDMA_Send(fd, (char *)&d, 1, MSG_OOB); + if (n == -1) { + JNU_ThrowIOExceptionWithLastError(env, "Write failed"); + } +} --- /dev/null 2018-09-16 08:14:05.604314476 -0700 +++ new/src/jdk.net/linux/native/libextnet/LinuxRdmaSocketOptions.c 2018-10-05 14:14:53.118757147 -0700 @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +#include "jdk_net_LinuxRdmaSocketOptions.h" +#include "rdma_util_md.h" +#include "Rsocket.h" +#include "jni_util.h" + +void throwByNameWithLastError + (JNIEnv *env, const char *name, const char *defaultDetail) +{ + char defaultMsg[255]; + sprintf(defaultMsg, "errno: %d, %s", errno, defaultDetail); + JNU_ThrowByNameWithLastError(env, name, defaultMsg); +} + +JNIEXPORT void JNICALL Java_jdk_net_LinuxRdmaSocketOptions_init + (JNIEnv *env, jclass c) +{ + loadRdmaFuncs(env); +} + +JNIEXPORT void JNICALL Java_jdk_net_LinuxRdmaSocketOptions_setSockOpt + (JNIEnv *env, jobject unused, jint fd, jint opt, jint value) +{ + int optname; + int level = SOL_RDMA; + RDMA_MapSocketOption(opt, &level, &optname); + int len = sizeof(value); + int rv = RDMA_SetSockOpt(fd, level, optname, (char*)&value, len); + if (rv < 0) { + if (errno == ENOPROTOOPT) { + JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", + "unsupported RDMA socket option"); + } else if (errno == EACCES || errno == EPERM) { + JNU_ThrowByName(env, "java/net/SocketException", "Permission denied"); + } else { + throwByNameWithLastError(env, "java/net/SocketException", + "set RDMA option failed"); + } + } +} + +JNIEXPORT jint JNICALL Java_jdk_net_LinuxRdmaSocketOptions_getSockOpt + (JNIEnv *env, jobject unused, jint fd, jint opt) +{ + int optname; + int level = SOL_RDMA; + RDMA_MapSocketOption(opt, &level, &optname); + int value; + int len = sizeof(value); + int rv = RDMA_GetSockOpt(fd, level, optname, (char*)&value, &len); + + if (rv < 0) { + if (errno == ENOPROTOOPT) { + JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", + "unsupported RDMA socket option"); + } else if (errno == EACCES || errno == EPERM) { + JNU_ThrowByName(env, "java/net/SocketException", "Permission denied"); + } else { + throwByNameWithLastError(env, "java/net/SocketException", + "get RDMA option failed"); + } + return -1; + } + return value; +} + +JNIEXPORT jboolean JNICALL Java_jdk_net_LinuxRdmaSocketOptions_rdmaSocketSupported + (JNIEnv *env, jobject unused) +{ + int rv, s; + + s = rs_socket(PF_INET, SOCK_STREAM, 0); + if (s < 0) { + return JNI_FALSE; + } + rs_close(s); + return JNI_TRUE; +} --- /dev/null 2018-09-16 08:14:05.604314476 -0700 +++ new/src/jdk.net/linux/native/libextnet/RdmaNet.c 2018-10-05 14:14:53.741757154 -0700 @@ -0,0 +1,286 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include "net_util.h" +#include "nio.h" +#include "nio_util.h" +#include "rdma_util_md.h" +#include "Rsocket.h" +#include "sun_nio_ch_Net.h" + +static jfieldID fd_fdID; + +JNIEXPORT jboolean JNICALL +Java_jdk_internal_net_rdma_RdmaNet_isRdmaAvailable0(JNIEnv *env, jclass cls) { + return rdma_supported(); +} + +static int +configureBlocking(int fd, jboolean blocking) +{ + int flags = rs_fcntl(fd, F_GETFL); + int newflags = blocking ? (flags & ~O_NONBLOCK) : (flags | O_NONBLOCK); + + return (flags == newflags) ? 0 : rs_fcntl(fd, F_SETFL, newflags); +} + +JNIEXPORT void JNICALL +Java_jdk_internal_net_rdma_RdmaNet_configureBlocking(JNIEnv *env, jclass clazz, + jobject fdo, jboolean blocking) +{ + int fd = (*env)->GetIntField(env, fdo, fd_fdID); + if (configureBlocking(fd, blocking) < 0) + JNU_ThrowIOExceptionWithLastError(env, "Configure blocking failed"); +} + +JNIEXPORT void JNICALL +Java_jdk_internal_net_rdma_RdmaNet_initIDs(JNIEnv *env, jclass clazz) +{ + loadRdmaFuncs(env); + CHECK_NULL(clazz = (*env)->FindClass(env, "java/io/FileDescriptor")); + CHECK_NULL(fd_fdID = (*env)->GetFieldID(env, clazz, "fd", "I")); + initInetAddressIDs(env); +} + +JNIEXPORT jboolean JNICALL +Java_jdk_internal_net_rdma_RdmaNet_isIPv6Available0(JNIEnv* env, jclass cl) +{ + return (ipv6_available()) ? JNI_TRUE : JNI_FALSE; +} + +JNIEXPORT jboolean JNICALL +Java_jdk_internal_net_rdma_RdmaNet_canIPv6SocketJoinIPv4Group0(JNIEnv* env, jclass cl) +{ + return JNI_TRUE; +} + +JNIEXPORT jboolean JNICALL +Java_jdk_internal_net_rdma_RdmaNet_canJoin6WithIPv4Group0(JNIEnv* env, jclass cl) +{ + return JNI_FALSE; +} + +JNIEXPORT jint JNICALL +Java_jdk_internal_net_rdma_RdmaNet_socket0(JNIEnv *env, jclass cl, jboolean preferIPv6, + jboolean stream, jboolean reuse, jboolean ignored) +{ + int fd; + int type = (stream ? SOCK_STREAM : SOCK_DGRAM); + int domain = (ipv6_available() && preferIPv6) ? AF_INET6 : AF_INET; + + fd = rs_socket(domain, type, 0); + if (fd < 0) { + return handleSocketError(env, errno); + } + + if (reuse) { + int arg = 1; + if (rs_setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&arg, + sizeof(arg)) < 0) { + JNU_ThrowByNameWithLastError(env, + JNU_JAVANETPKG "SocketException", + "Unable to set SO_REUSEADDR"); + rs_close(fd); + return -1; + } + } + + return fd; +} + +JNIEXPORT void JNICALL +Java_jdk_internal_net_rdma_RdmaNet_bind0(JNIEnv *env, jclass clazz, jobject fdo, jboolean preferIPv6, + jboolean useExclBind, jobject iao, int port) +{ + SOCKETADDRESS sa; + int sa_len = 0; + int rv = 0; + + if (NET_InetAddressToSockaddr(env, iao, port, &sa, &sa_len, + preferIPv6) != 0) { + return; + } + + int fd = (*env)->GetIntField(env, fdo, fd_fdID); + rv = RDMA_Bind(fd, &sa, sa_len); + if (rv != 0) { + handleSocketError(env, errno); + } +} + +JNIEXPORT void JNICALL +Java_jdk_internal_net_rdma_RdmaNet_listen(JNIEnv *env, jclass cl, jobject fdo, jint backlog) +{ + int fd = (*env)->GetIntField(env, fdo, fd_fdID); + if (rs_listen(fd, backlog) < 0) + handleSocketError(env, errno); +} + +JNIEXPORT jint JNICALL +Java_jdk_internal_net_rdma_RdmaNet_connect0(JNIEnv *env, jclass clazz, jboolean preferIPv6, + jobject fdo, jobject iao, jint port) +{ + SOCKETADDRESS sa; + int sa_len = 0; + int rv; + + if (NET_InetAddressToSockaddr(env, iao, port, &sa, &sa_len, + preferIPv6) != 0) { + return IOS_THROWN; + } + + int fd = (*env)->GetIntField(env, fdo, fd_fdID); + + rv = rs_connect(fd, &sa.sa, sa_len); + if (rv != 0) { + if (errno == EINPROGRESS) { + return IOS_UNAVAILABLE; + } else if (errno == EINTR) { + return IOS_INTERRUPTED; + } + return handleSocketError(env, errno); + } + return 1; +} + +JNIEXPORT jint JNICALL +Java_jdk_internal_net_rdma_RdmaNet_localPort(JNIEnv *env, jclass clazz, jobject fdo) +{ + SOCKETADDRESS sa; + socklen_t sa_len = sizeof(SOCKETADDRESS); + int fd = (*env)->GetIntField(env, fdo, fd_fdID); + if (rs_getsockname(fd, &sa.sa, &sa_len) < 0) { + handleSocketError(env, errno); + return -1; + } + return NET_GetPortFromSockaddr(&sa); +} + +JNIEXPORT jobject JNICALL +Java_jdk_internal_net_rdma_RdmaNet_localInetAddress(JNIEnv *env, jclass clazz, jobject fdo) +{ + SOCKETADDRESS sa; + socklen_t sa_len = sizeof(SOCKETADDRESS); + int port; + int fd = (*env)->GetIntField(env, fdo, fd_fdID); + if (rs_getsockname(fd, &sa.sa, &sa_len) < 0) { + handleSocketError(env, errno); + return NULL; + } + return NET_SockaddrToInetAddress(env, &sa, &port); +} + +JNIEXPORT jint JNICALL +Java_jdk_internal_net_rdma_RdmaNet_getIntOption0(JNIEnv *env, jclass clazz, jobject fdo, + jboolean mayNeedConversion, jint level, jint opt) +{ + int result; + struct linger linger; + u_char carg; + void *arg; + socklen_t arglen; + int n; + + arg = (void *)&result; + arglen = sizeof(result); + + int fd = (*env)->GetIntField(env, fdo, fd_fdID); + if (mayNeedConversion) { + n = RDMA_GetSockOpt(fd, level, opt, arg, (int*)&arglen); + } else { + n = rs_getsockopt(fd, level, opt, arg, &arglen); + } + if (n < 0) { + JNU_ThrowByNameWithLastError(env, + JNU_JAVANETPKG "SocketException", + "jdk.internal.net.rdma.RdmaNet.getIntOption"); + return -1; + } + + return (jint)result; +} + +JNIEXPORT void JNICALL +Java_jdk_internal_net_rdma_RdmaNet_setIntOption0(JNIEnv *env, jclass clazz, jobject fdo, + jboolean mayNeedConversion, jint level, + jint opt, jint arg, jboolean isIPv6) +{ + int result; + struct linger linger; + u_char carg; + void *parg; + socklen_t arglen; + int n; + + parg = (void*)&arg; + arglen = sizeof(arg); + + int fd = (*env)->GetIntField(env, fdo, fd_fdID); + if (mayNeedConversion) { + n = RDMA_SetSockOpt(fd, level, opt, parg, arglen); + } else { + n = rs_setsockopt(fd, level, opt, parg, arglen); + } + if (n < 0) { + JNU_ThrowByNameWithLastError(env, + JNU_JAVANETPKG "SocketException", + "jdk.internal.net.rdma.RdmaNet.setIntOption"); + } +} + +JNIEXPORT void JNICALL +Java_jdk_internal_net_rdma_RdmaNet_shutdown(JNIEnv *env, jclass cl, jobject fdo, jint jhow) +{ + int how = (jhow == sun_nio_ch_Net_SHUT_RD) ? SHUT_RD : + (jhow == sun_nio_ch_Net_SHUT_WR) ? SHUT_WR : SHUT_RDWR; + int fd = (*env)->GetIntField(env, fdo, fd_fdID); + if ((rs_shutdown(fd, how) < 0) && (errno != ENOTCONN)) + handleSocketError(env, errno); +} + +JNIEXPORT jint JNICALL +Java_jdk_internal_net_rdma_RdmaNet_poll(JNIEnv* env, jclass this, jobject fdo, jint events, jlong timeout) +{ + struct pollfd pfd; + int rv; + pfd.fd = (*env)->GetIntField(env, fdo, fd_fdID); + pfd.events = events; + if (timeout < -1) { + timeout = -1; + } else if (timeout > INT_MAX) { + timeout = INT_MAX; + } + rv = rs_poll(&pfd, 1, (int)timeout); + + if (rv >= 0) { + return pfd.revents; + } else if (errno == EINTR) { + // interrupted, no events to return + return 0; + } else { + handleSocketError(env, errno); + return IOS_THROWN; + } +} --- /dev/null 2018-09-16 08:14:05.604314476 -0700 +++ new/src/jdk.net/linux/native/libextnet/RdmaPollSelectorImpl.c 2018-10-05 14:14:54.309757161 -0700 @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include +#include "jni_util.h" +#include "jvm.h" +#include "nio.h" +#include "jdk_internal_net_rdma_RdmaPollSelectorImpl.h" + +JNIEXPORT void JNICALL +Java_jdk_internal_net_rdma_RdmaPollSelectorImpl_init(JNIEnv *env, jclass clazz) +{ + loadRdmaFuncs(env); +} + +JNIEXPORT jint JNICALL +Java_jdk_internal_net_rdma_RdmaPollSelectorImpl_poll0(JNIEnv *env, jclass clazz, + jlong address, jint numfds, + jint timeout) +{ + struct pollfd *a; + int res; + + a = (struct pollfd *) jlong_to_ptr(address); + res = rs_poll(a, numfds, timeout); + if (res < 0) { + if (errno == EINTR) { + return IOS_INTERRUPTED; + } else { + JNU_ThrowIOExceptionWithLastError(env, "poll failed"); + return IOS_THROWN; + } + } + return (jint) res; +} --- /dev/null 2018-09-16 08:14:05.604314476 -0700 +++ new/src/jdk.net/linux/native/libextnet/RdmaServerSocketChannelImpl.c 2018-10-05 14:14:54.901757168 -0700 @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include "jdk_internal_net_rdma_RdmaServerSocketChannelImpl.h" +#include "net_util.h" +#include "nio.h" +#include + +static jfieldID fd_fdID; /* java.io.FileDescriptor.fd */ +static jclass isa_class; /* java.net.InetSocketAddress */ +static jmethodID isa_ctorID; /* .InetSocketAddress(InetAddress, int) */ + + +JNIEXPORT void JNICALL +Java_jdk_internal_net_rdma_RdmaServerSocketChannelImpl_initIDs(JNIEnv *env, jclass c) +{ + loadRdmaFuncs(env); + + jclass cls; + + cls = (*env)->FindClass(env, "java/io/FileDescriptor"); + CHECK_NULL(cls); + fd_fdID = (*env)->GetFieldID(env, cls, "fd", "I"); + CHECK_NULL(fd_fdID); + + cls = (*env)->FindClass(env, "java/net/InetSocketAddress"); + CHECK_NULL(cls); + isa_class = (*env)->NewGlobalRef(env, cls); + if (isa_class == NULL) { + JNU_ThrowOutOfMemoryError(env, NULL); + return; + } + isa_ctorID = (*env)->GetMethodID(env, cls, "", + "(Ljava/net/InetAddress;I)V"); + CHECK_NULL(isa_ctorID); +} + +JNIEXPORT jint JNICALL +Java_jdk_internal_net_rdma_RdmaServerSocketChannelImpl_accept0(JNIEnv *env, jobject this, + jobject ssfdo, jobject newfdo, + jobjectArray isaa) +{ + jint ssfd = (*env)->GetIntField(env, ssfdo, fd_fdID); + jint newfd; + SOCKETADDRESS sa; + socklen_t sa_len = sizeof(SOCKETADDRESS); + jobject remote_ia = 0; + jobject isa; + jint remote_port = 0; + + /* + * accept connection but ignore ECONNABORTED indicating that + * a connection was eagerly accepted but was reset before + * accept() was called. + */ + for (;;) { + newfd = rs_accept(ssfd, &sa.sa, &sa_len); + if (newfd >= 0) { + break; + } + if (errno != ECONNABORTED) { + break; + } + /* ECONNABORTED => restart accept */ + } + + if (newfd < 0) { + if (errno == EAGAIN) + return IOS_UNAVAILABLE; + if (errno == EINTR) + return IOS_INTERRUPTED; + JNU_ThrowIOExceptionWithLastError(env, "Accept failed"); + return IOS_THROWN; + } + + (*env)->SetIntField(env, newfdo, fd_fdID, newfd); + remote_ia = NET_SockaddrToInetAddress(env, &sa, (int *)&remote_port); + CHECK_NULL_RETURN(remote_ia, IOS_THROWN); + isa = (*env)->NewObject(env, isa_class, isa_ctorID, remote_ia, remote_port); + CHECK_NULL_RETURN(isa, IOS_THROWN); + (*env)->SetObjectArrayElement(env, isaa, 0, isa); + return 1; +} --- /dev/null 2018-09-16 08:14:05.604314476 -0700 +++ new/src/jdk.net/linux/native/libextnet/RdmaSocketChannelImpl.c 2018-10-05 14:14:55.488757175 -0700 @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include "jdk_internal_net_rdma_RdmaSocketChannelImpl.h" +#include "nio_util.h" +#include "nio.h" +#include + +static jfieldID fd_fdID; + +JNIEXPORT void JNICALL +Java_jdk_internal_net_rdma_RdmaSocketChannelImpl_initIDs(JNIEnv *env, jclass clazz) +{ + loadRdmaFuncs(env); + CHECK_NULL(clazz = (*env)->FindClass(env, "java/io/FileDescriptor")); + CHECK_NULL(fd_fdID = (*env)->GetFieldID(env, clazz, "fd", "I")); +} + +jint fdVal(JNIEnv *env, jobject fdo) +{ + return (*env)->GetIntField(env, fdo, fd_fdID); +} + +JNIEXPORT jint JNICALL +Java_jdk_internal_net_rdma_RdmaSocketChannelImpl_checkConnect(JNIEnv *env, jobject this, + jobject fdo, jboolean block) +{ + int error = 0; + socklen_t n = sizeof(int); + jint fd = fdVal(env, fdo); + int result = 0; + struct pollfd poller; + + poller.fd = fd; + poller.events = POLLOUT; + poller.revents = 0; + result = rs_poll(&poller, 1, block ? -1 : 0); + + if (result < 0) { + if (errno == EINTR) { + return IOS_INTERRUPTED; + } else { + JNU_ThrowIOExceptionWithLastError(env, "poll failed"); + return IOS_THROWN; + } + } + if (!block && (result == 0)) + return IOS_UNAVAILABLE; + + if (result > 0) { + errno = 0; + result = rs_getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &n); + if (result < 0) { + return handleSocketError(env, errno); + } else if (error) { + return handleSocketError(env, error); + } else if ((poller.revents & POLLHUP) != 0) { + return handleSocketError(env, ENOTCONN); + } + // connected + return 1; + } + return 0; +} + +JNIEXPORT jint JNICALL +Java_jdk_internal_net_rdma_RdmaSocketChannelImpl_sendOutOfBandData(JNIEnv* env, jclass this, + jobject fdo, jbyte b) +{ + int n = rs_send(fdVal(env, fdo), (const void*)&b, 1, MSG_OOB); + return convertReturnVal(env, n, JNI_FALSE); +} --- /dev/null 2018-09-16 08:14:05.604314476 -0700 +++ new/src/jdk.net/linux/native/libextnet/RdmaSocketInputStream.c 2018-10-05 14:14:56.096757182 -0700 @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +#include +#include "jvm.h" +#include "net_util.h" +#include "rdma_util_md.h" +#include "jdk_internal_net_rdma_RdmaSocketInputStream.h" + +static jfieldID IO_fd_fdID; + +/* + * Class: jdk_net_RdmaSocketInputStream + * Method: init + * Signature: ()V + */ +JNIEXPORT void JNICALL +Java_jdk_internal_net_rdma_RdmaSocketInputStream_init(JNIEnv *env, jclass cls) { + IO_fd_fdID = NET_GetFileDescriptorID(env); +} + +static int RDMA_ReadWithTimeout(JNIEnv *env, int fd, char *bufP, int len, long timeout) { + int result = 0; + jlong prevNanoTime = JVM_NanoTime(env, 0); + jlong nanoTimeout = (jlong) timeout * NET_NSEC_PER_MSEC; + while (nanoTimeout >= NET_NSEC_PER_MSEC) { + result = RDMA_Timeout(env, fd, nanoTimeout / NET_NSEC_PER_MSEC, prevNanoTime); + if (result <= 0) { + if (result == 0) { + JNU_ThrowByName(env, "java/net/SocketTimeoutException", "Read timed out"); + } else if (result == -1) { + if (errno == EBADF) { + JNU_ThrowByName(env, "java/net/SocketException", "Socket closed"); + } else if (errno == ENOMEM) { + JNU_ThrowOutOfMemoryError(env, "NET_Timeout native heap allocation failed"); + } else { + JNU_ThrowByNameWithMessageAndLastError + (env, "java/net/SocketException", "select/poll failed"); + } + } + return -1; + } + result = RDMA_NonBlockingRead(fd, bufP, len); + if (result == -1 && ((errno == EAGAIN) || (errno == EWOULDBLOCK))) { + jlong newtNanoTime = JVM_NanoTime(env, 0); + nanoTimeout -= newtNanoTime - prevNanoTime; + if (nanoTimeout >= NET_NSEC_PER_MSEC) { + prevNanoTime = newtNanoTime; + } + } else { + break; + } + } + return result; +} + +/* + * Class: jdk_net_RdmaSocketInputStream + * Method: rdmaSocketRead0 + * Signature: (Ljava/io/FileDescriptor;[BIII)I + */ +JNIEXPORT jint JNICALL +Java_jdk_internal_net_rdma_RdmaSocketInputStream_rdmaSocketRead0(JNIEnv *env, jobject this, + jobject fdObj, jbyteArray data, + jint off, jint len, jint timeout) +{ + char BUF[MAX_BUFFER_LEN]; + char *bufP; + jint fd, nread; + + if (IS_NULL(fdObj)) { + JNU_ThrowByName(env, "java/net/SocketException", + "Socket closed"); + return -1; + } + fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID); + if (fd == -1) { + JNU_ThrowByName(env, "java/net/SocketException", "Socket closed"); + return -1; + } + + if (len > MAX_BUFFER_LEN) { + if (len > MAX_HEAP_BUFFER_LEN) { + len = MAX_HEAP_BUFFER_LEN; + } + bufP = (char *)malloc((size_t)len); + if (bufP == NULL) { + bufP = BUF; + len = MAX_BUFFER_LEN; + } + } else { + bufP = BUF; + } + if (timeout) { + nread = RDMA_ReadWithTimeout(env, fd, bufP, len, timeout); + if ((*env)->ExceptionCheck(env)) { + if (bufP != BUF) { + free(bufP); + } + return nread; + } + } else { + nread = RDMA_Read(fd, bufP, len); + } + + if (nread <= 0) { + if (nread < 0) { + + switch (errno) { + case ECONNRESET: + case EPIPE: + JNU_ThrowByName(env, "sun/net/ConnectionResetException", + "Connection reset"); + break; + + case EBADF: + JNU_ThrowByName(env, "java/net/SocketException", + "Socket closed"); + break; + + case EINTR: + JNU_ThrowByName(env, "java/io/InterruptedIOException", + "Operation interrupted"); + break; + default: + JNU_ThrowByNameWithMessageAndLastError + (env, "java/net/SocketException", "Read failed"); + } + } + } else { + (*env)->SetByteArrayRegion(env, data, off, nread, (jbyte *)bufP); + } + + if (bufP != BUF) { + free(bufP); + } + return nread; +} --- /dev/null 2018-09-16 08:14:05.604314476 -0700 +++ new/src/jdk.net/linux/native/libextnet/RdmaSocketOutputStream.c 2018-10-05 14:14:56.731757189 -0700 @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +#include +#include "net_util.h" +#include "rdma_util_md.h" +#include "jdk_internal_net_rdma_RdmaSocketOutputStream.h" + +#define min(a, b) ((a) < (b) ? (a) : (b)) + +static jfieldID IO_fd_fdID; + +/* + * Class: jdk_net_SocketOutputStream + * Method: init + * Signature: ()V + */ +JNIEXPORT void JNICALL +Java_jdk_internal_net_rdma_RdmaSocketOutputStream_init(JNIEnv *env, jclass cls) { + IO_fd_fdID = NET_GetFileDescriptorID(env); +} + +/* + * Class: jdk_net_RdmaSocketOutputStream + * Method: rdmaSocketWrite0 + * Signature: (Ljava/io/FileDescriptor;[BII)V + */ +JNIEXPORT void JNICALL +Java_jdk_internal_net_rdma_RdmaSocketOutputStream_rdmaSocketWrite0(JNIEnv *env, jobject this, + jobject fdObj, + jbyteArray data, + jint off, jint len) { + char *bufP; + char BUF[MAX_BUFFER_LEN]; + int buflen; + int fd; + + if (IS_NULL(fdObj)) { + JNU_ThrowByName(env, "java/net/SocketException", "Socket closed"); + return; + } else { + fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID); + if (fd == -1) { + JNU_ThrowByName(env, "java/net/SocketException", "Socket closed"); + return; + } + + } + + if (len <= MAX_BUFFER_LEN) { + bufP = BUF; + buflen = MAX_BUFFER_LEN; + } else { + buflen = min(MAX_HEAP_BUFFER_LEN, len); + bufP = (char *)malloc((size_t)buflen); + + if (bufP == NULL) { + bufP = BUF; + buflen = MAX_BUFFER_LEN; + } + } + + while(len > 0) { + int loff = 0; + int chunkLen = min(buflen, len); + int llen = chunkLen; + (*env)->GetByteArrayRegion(env, data, off, chunkLen, (jbyte *)bufP); + + if ((*env)->ExceptionCheck(env)) { + break; + } else { + while(llen > 0) { + int n = RDMA_Send(fd, bufP + loff, llen, 0); + if (n > 0) { + llen -= n; + loff += n; + continue; + } + if (errno == ECONNRESET) { + JNU_ThrowByName(env, "sun/net/ConnectionResetException", + "Connection reset"); + } else { + JNU_ThrowByNameWithMessageAndLastError + (env, "java/net/SocketException", "Write failed"); + } + if (bufP != BUF) { + free(bufP); + } + return; + } + len -= chunkLen; + off += chunkLen; + } + } + + if (bufP != BUF) { + free(bufP); + } +} --- /dev/null 2018-09-16 08:14:05.604314476 -0700 +++ new/src/jdk.net/linux/native/libextnet/Rsocket.c 2018-10-05 14:14:57.298757196 -0700 @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include +#include +#include "jni_util.h" + +static const char* nativeRdmaLib = "librdmacm.so"; + +jboolean loadRdmaFuncs(JNIEnv* env) { + if (dlopen(nativeRdmaLib, RTLD_GLOBAL | RTLD_LAZY) == NULL) { + JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", + dlerror()); + return JNI_FALSE; + } + + if ((rs_socket = (rs_rsocket_func*) + dlsym(RTLD_DEFAULT, "rsocket")) == NULL) { + JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", + dlerror()); + return JNI_FALSE; + } + + if ((rs_fcntl = (rs_rfcntl_func*) + dlsym(RTLD_DEFAULT, "rfcntl")) == NULL) { + JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", + dlerror()); + return JNI_FALSE; + } + + if ((rs_listen = (rs_rlisten_func*) + dlsym(RTLD_DEFAULT, "rlisten")) == NULL) { + JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", + dlerror()); + return JNI_FALSE; + } + + if ((rs_bind = (rs_rbind_func*) + dlsym(RTLD_DEFAULT, "rbind")) == NULL) { + JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", + dlerror()); + return JNI_FALSE; + } + + if ((rs_connect = (rs_rconnect_func*) + dlsym(RTLD_DEFAULT, "rconnect")) == NULL) { + JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", + dlerror()); + return JNI_FALSE; + } + + if ((rs_getsockname = (rs_rgetsockname_func*) + dlsym(RTLD_DEFAULT, "rgetsockname")) == NULL) { + JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", + dlerror()); + return JNI_FALSE; + } + + if ((rs_getsockopt = (rs_rgetsockopt_func*) + dlsym(RTLD_DEFAULT, "rgetsockopt")) == NULL) { + JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", + dlerror()); + return JNI_FALSE; + } + + if ((rs_setsockopt = (rs_rsetsockopt_func*) + dlsym(RTLD_DEFAULT, "rsetsockopt")) == NULL) { + JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", + dlerror()); + return JNI_FALSE; + } + + if ((rs_shutdown = (rs_rshutdown_func*) + dlsym(RTLD_DEFAULT, "rshutdown")) == NULL) { + JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", + dlerror()); + return JNI_FALSE; + } + + if ((rs_poll = (rs_rpoll_func*) + dlsym(RTLD_DEFAULT, "rpoll")) == NULL) { + JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", + dlerror()); + return JNI_FALSE; + } + + if ((rs_send = (rs_rsend_func*) + dlsym(RTLD_DEFAULT, "rsend")) == NULL) { + JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", + dlerror()); + return JNI_FALSE; + } + + if ((rs_accept = (rs_raccept_func*) + dlsym(RTLD_DEFAULT, "raccept")) == NULL) { + JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", + dlerror()); + return JNI_FALSE; + } + + if ((rs_close = (rs_rclose_func*) + dlsym(RTLD_DEFAULT, "rclose")) == NULL) { + JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", + dlerror()); + return JNI_FALSE; + } + + if ((rs_read = (rs_rread_func*) + dlsym(RTLD_DEFAULT, "rread")) == NULL) { + JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", + dlerror()); + return JNI_FALSE; + } + + if ((rs_readv = (rs_rreadv_func*) + dlsym(RTLD_DEFAULT, "rreadv")) == NULL) { + JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", + dlerror()); + return JNI_FALSE; + } + + if ((rs_write = (rs_rwrite_func*) + dlsym(RTLD_DEFAULT, "rwrite")) == NULL) { + JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", + dlerror()); + return JNI_FALSE; + } + + if ((rs_writev = (rs_rwritev_func*) + dlsym(RTLD_DEFAULT, "rwritev")) == NULL) { + JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", + dlerror()); + return JNI_FALSE; + } + + if ((rs_recv = (rs_rrecv_func*) + dlsym(RTLD_DEFAULT, "rrecv")) == NULL) { + JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", + dlerror()); + return JNI_FALSE; + } + + if ((rs_recvfrom = (rs_rrecvfrom_func*) + dlsym(RTLD_DEFAULT, "rrecvfrom")) == NULL) { + JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", + dlerror()); + return JNI_FALSE; + } + + if ((rs_sendto = (rs_rsendto_func*) + dlsym(RTLD_DEFAULT, "rsendto")) == NULL) { + JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", + dlerror()); + return JNI_FALSE; + } + + return JNI_TRUE; +} --- /dev/null 2018-09-16 08:14:05.604314476 -0700 +++ new/src/jdk.net/linux/native/libextnet/Rsocket.h 2018-10-05 14:14:57.890757203 -0700 @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include +#include +#include "jni.h" + +/* Function types to support dynamic linking of socket API extension functions + * for RDMA. This is so that there is no linkage depandancy during build or + * runtime for librdmacm.*/ +typedef int rs_rsocket_func(int domain, int type, int protocol); +typedef int rs_rfcntl_func(int fd, int cmd, ... /* arg */ ); +typedef int rs_rlisten_func(int sockfd, int backlog); +typedef int rs_rbind_func(int sockfd, const struct sockaddr *addr, + socklen_t addrlen); +typedef int rs_rconnect_func(int sockfd, const struct sockaddr *addr, + socklen_t addlen); +typedef int rs_rgetsockname_func(int sockfd, struct sockaddr *addr, + socklen_t *addrlen); +typedef int rs_rgetsockopt_func(int sockfd, int level, int optname, + void *optval, socklen_t *optlen); +typedef int rs_rsetsockopt_func(int sockfd, int level, int optname, + const void *optval, socklen_t optlen); +typedef int rs_rshutdown_func(int sockfd, int how); +typedef int rs_rpoll_func(struct pollfd *fds, nfds_t nfds, int timeout); +typedef size_t rs_rsend_func(int sockfd, const void *buf, size_t len, + int flags); +typedef int rs_raccept_func(int sockfd, struct sockaddr *addr, + socklen_t *addrlen); +typedef int rs_rclose_func(int sockfd); +typedef ssize_t rs_rread_func(int sockfd, void *buf, size_t count); +typedef ssize_t rs_rreadv_func(int sockfd, const struct iovec *iov, int iovcnt); +typedef ssize_t rs_rwrite_func(int sockfd, const void *buf, size_t count); +typedef ssize_t rs_rwritev_func(int sockfd, const struct iovec *iov, int iovcnt); +typedef ssize_t rs_rrecv_func(int sockfd, void *buf, size_t len, int flags); +typedef ssize_t rs_rrecvfrom_func(int sockfd, void *buf, size_t len, int flags, + struct sockaddr *src_addr, socklen_t *addrlen); +typedef ssize_t rs_rsendto_func(int sockfd, const void *buf, size_t len, int flags, + const struct sockaddr *dest_addr, socklen_t addrlen); + +rs_rsocket_func* rs_socket; +rs_rfcntl_func* rs_fcntl; +rs_rlisten_func* rs_listen; +rs_rbind_func* rs_bind; +rs_rconnect_func* rs_connect; +rs_rgetsockname_func* rs_getsockname; +rs_rgetsockopt_func* rs_getsockopt; +rs_rsetsockopt_func* rs_setsockopt; +rs_rshutdown_func* rs_shutdown; +rs_rpoll_func* rs_poll; +rs_rsend_func* rs_send; +rs_raccept_func* rs_accept; +rs_rclose_func* rs_close; +rs_rread_func* rs_read; +rs_rreadv_func* rs_readv; +rs_rwrite_func* rs_write; +rs_rwritev_func* rs_writev; +rs_rrecv_func* rs_recv; +rs_rrecvfrom_func* rs_recvfrom; +rs_rsendto_func* rs_sendto; + +jboolean loadRdmaFuncs(JNIEnv* env); + +/* Definitions taken from librdmacm/include/rdma/rsocket.h */ +#ifndef SOL_RDMA +#define SOL_RDMA 0x10000 +#endif +enum { + RDMA_SQSIZE, + RDMA_RQSIZE, + RDMA_INLINE, +}; --- /dev/null 2018-09-16 08:14:05.604314476 -0700 +++ new/src/jdk.net/linux/native/libextnet/rdma_util_md.c 2018-10-05 14:14:58.469757210 -0700 @@ -0,0 +1,484 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include +#include "java_net_SocketOptions.h" +#include "jdk_net_RdmaSocketOptions.h" +#include "jvm.h" +#include // defines TCP_NODELAY +#include "net_util.h" +#include "rdma_util_md.h" +#include "Rsocket.h" +#include +#include +#include +#include + +#define BLOCKING_IO_RETURN_INT(FD, FUNC) { \ + int ret; \ + threadEntry_t self; \ + fdEntry_t *fdEntry = getFdEntry(FD); \ + if (fdEntry == NULL) { \ + errno = EBADF; \ + return -1; \ + } \ + do { \ + startOp(fdEntry, &self); \ + ret = FUNC; \ + endOp(fdEntry, &self); \ + } while (ret == -1 && errno == EINTR); \ + return ret; \ +} + +typedef struct threadEntry { + pthread_t thr; /* this thread */ + struct threadEntry *next; /* next thread */ + int intr; /* interrupted */ +} threadEntry_t; + +typedef struct { + pthread_mutex_t lock; /* fd lock */ + threadEntry_t *threads; /* threads blocked on fd */ +} fdEntry_t; + +static int sigWakeup = (__SIGRTMAX - 2); + +static fdEntry_t* fdTable = NULL; + +static const int fdTableMaxSize = 0x1000; /* 4K */ + +static int fdTableLen = 0; + +static int fdLimit = 0; + +static fdEntry_t** fdOverflowTable = NULL; + +static int fdOverflowTableLen = 0; + +static const int fdOverflowTableSlabSize = 0x10000; /* 64k */ + +pthread_mutex_t fdOverflowTableLock = PTHREAD_MUTEX_INITIALIZER; + +static void sig_wakeup(int sig) { +} + + +static void __attribute((constructor)) init() { + struct rlimit nbr_files; + sigset_t sigset; + struct sigaction sa; + int i = 0; + + if (-1 == getrlimit(RLIMIT_NOFILE, &nbr_files)) { + fprintf(stderr, "library initialization failed - " + "unable to get max # of allocated fds\n"); + abort(); + } + if (nbr_files.rlim_max != RLIM_INFINITY) { + fdLimit = nbr_files.rlim_max; + } else { + fdLimit = INT_MAX; + } + + fdTableLen = fdLimit < fdTableMaxSize ? fdLimit : fdTableMaxSize; + fdTable = (fdEntry_t*) calloc(fdTableLen, sizeof(fdEntry_t)); + if (fdTable == NULL) { + fprintf(stderr, "library initialization failed - " + "unable to allocate file descriptor table - out of memory"); + abort(); + } else { + for (i = 0; i < fdTableLen; i ++) { + pthread_mutex_init(&fdTable[i].lock, NULL); + } + } + + if (fdLimit > fdTableMaxSize) { + fdOverflowTableLen = ((fdLimit - fdTableMaxSize) / fdOverflowTableSlabSize) + 1; + fdOverflowTable = (fdEntry_t**) calloc(fdOverflowTableLen, sizeof(fdEntry_t*)); + if (fdOverflowTable == NULL) { + fprintf(stderr, "library initialization failed - " + "unable to allocate file descriptor overflow table - out of memory"); + abort(); + } + } + + sa.sa_handler = sig_wakeup; + sa.sa_flags = 0; + sigemptyset(&sa.sa_mask); + sigaction(sigWakeup, &sa, NULL); + + sigemptyset(&sigset); + sigaddset(&sigset, sigWakeup); + sigprocmask(SIG_UNBLOCK, &sigset, NULL); +} + +static inline fdEntry_t *getFdEntry(int fd) +{ + fdEntry_t* result = NULL; + + if (fd < 0) { + return NULL; + } + + assert(fd < fdLimit); + + if (fd < fdTableMaxSize) { + assert(fd < fdTableLen); + result = &fdTable[fd]; + } else { + const int indexInOverflowTable = fd - fdTableMaxSize; + const int rootindex = indexInOverflowTable / fdOverflowTableSlabSize; + const int slabindex = indexInOverflowTable % fdOverflowTableSlabSize; + fdEntry_t* slab = NULL; + assert(rootindex < fdOverflowTableLen); + assert(slabindex < fdOverflowTableSlabSize); + pthread_mutex_lock(&fdOverflowTableLock); + if (fdOverflowTable[rootindex] == NULL) { + fdEntry_t* const newSlab = + (fdEntry_t*)calloc(fdOverflowTableSlabSize, sizeof(fdEntry_t)); + if (newSlab == NULL) { + fprintf(stderr, "Unable to allocate file descriptor overflow" + " table slab - out of memory"); + pthread_mutex_unlock(&fdOverflowTableLock); + abort(); + } else { + int i; + for (i = 0; i < fdOverflowTableSlabSize; i ++) { + pthread_mutex_init(&newSlab[i].lock, NULL); + } + fdOverflowTable[rootindex] = newSlab; + } + } + pthread_mutex_unlock(&fdOverflowTableLock); + slab = fdOverflowTable[rootindex]; + result = &slab[slabindex]; + } + + return result; + +} + +static inline void startOp(fdEntry_t *fdEntry, threadEntry_t *self) +{ + self->thr = pthread_self(); + self->intr = 0; + + pthread_mutex_lock(&(fdEntry->lock)); + { + self->next = fdEntry->threads; + fdEntry->threads = self; + } + pthread_mutex_unlock(&(fdEntry->lock)); +} + +static inline void endOp + (fdEntry_t *fdEntry, threadEntry_t *self) +{ + int orig_errno = errno; + pthread_mutex_lock(&(fdEntry->lock)); + { + threadEntry_t *curr, *prev=NULL; + curr = fdEntry->threads; + while (curr != NULL) { + if (curr == self) { + if (curr->intr) { + orig_errno = EBADF; + } + if (prev == NULL) { + fdEntry->threads = curr->next; + } else { + prev->next = curr->next; + } + break; + } + prev = curr; + curr = curr->next; + } + } + pthread_mutex_unlock(&(fdEntry->lock)); + errno = orig_errno; +} + +#define RESTARTABLE(_cmd, _result) do { \ + do { \ + _result = _cmd; \ + } while((_result == -1) && (errno == EINTR)); \ +} while(0) + +int rdma_supported() +{ + int one = 1; + int rv, s; + s = rs_socket(PF_INET, SOCK_STREAM, 0); + if (s < 0) { + return JNI_FALSE; + } + return JNI_TRUE; +} + +int RDMA_SocketAvailable(int s, jint *pbytes) { + int result; + RESTARTABLE(ioctl(s, FIONREAD, pbytes), result); + return (result == -1) ? 0 : 1; +} + +int +RDMA_MapSocketOption(jint cmd, int *level, int *optname) { + static struct { + jint cmd; + int level; + int optname; + } const opts[] = { + { java_net_SocketOptions_TCP_NODELAY, IPPROTO_TCP, TCP_NODELAY }, + { java_net_SocketOptions_SO_SNDBUF, SOL_SOCKET, SO_SNDBUF }, + { java_net_SocketOptions_SO_RCVBUF, SOL_SOCKET, SO_RCVBUF }, + { java_net_SocketOptions_SO_REUSEADDR, SOL_SOCKET, SO_REUSEADDR }, + { jdk_net_RdmaSocketOptions_SQSIZE, SOL_RDMA, RDMA_SQSIZE }, + { jdk_net_RdmaSocketOptions_RQSIZE, SOL_RDMA, RDMA_RQSIZE }, + { jdk_net_RdmaSocketOptions_INLINE, SOL_RDMA, RDMA_INLINE }, + }; + int i; + for (i=0; i<(int)(sizeof(opts) / sizeof(opts[0])); i++) { + if (cmd == opts[i].cmd) { + *level = opts[i].level; + *optname = opts[i].optname; + return 0; + } + } + + return -1; +} + +int +RDMA_GetSockOpt(int fd, int level, int opt, void *result, + int *len) +{ + int rv; + socklen_t socklen = *len; + + rv = rs_getsockopt(fd, level, opt, result, &socklen); + *len = socklen; + + if (rv < 0) { + return rv; + } + + if ((level == SOL_SOCKET) && ((opt == SO_SNDBUF) + || (opt == SO_RCVBUF))) { + int n = *((int *)result); + n /= 2; + *((int *)result) = n; + } + return rv; +} + +int +RDMA_SetSockOpt(int fd, int level, int opt, const void *arg, + int len) +{ + int *bufsize; + if (level == SOL_SOCKET && opt == SO_RCVBUF) { + int *bufsize = (int *)arg; + if (*bufsize < 1024) { + *bufsize = 1024; + } + } + + return rs_setsockopt(fd, level, opt, arg, len); +} + +int +RDMA_Bind(int fd, SOCKETADDRESS *sa, int len) +{ + int rv; + int arg, alen; + + if (sa->sa.sa_family == AF_INET) { + if ((ntohl(sa->sa4.sin_addr.s_addr) & 0x7f0000ff) == 0x7f0000ff) { + errno = EADDRNOTAVAIL; + return -1; + } + } + rv = rs_bind(fd, &sa->sa, len); + return rv; +} + +jint +RDMA_Wait(JNIEnv *env, jint fd, jint flags, jint timeout) +{ + jlong prevNanoTime = JVM_NanoTime(env, 0); + jlong nanoTimeout = (jlong) timeout * NET_NSEC_PER_MSEC; + jint read_rv; + + while (1) { + jlong newNanoTime; + struct pollfd pfd; + pfd.fd = fd; + pfd.events = 0; + if (flags & NET_WAIT_READ) + pfd.events |= POLLIN; + if (flags & NET_WAIT_WRITE) + pfd.events |= POLLOUT; + if (flags & NET_WAIT_CONNECT) + pfd.events |= POLLOUT; + + errno = 0; + read_rv = RDMA_Poll(&pfd, 1, nanoTimeout / NET_NSEC_PER_MSEC); + + newNanoTime = JVM_NanoTime(env, 0); + nanoTimeout -= (newNanoTime - prevNanoTime); + if (nanoTimeout < NET_NSEC_PER_MSEC) { + return read_rv > 0 ? 0 : -1; + } + prevNanoTime = newNanoTime; + + if (read_rv > 0) { + break; + } + } + return (nanoTimeout / NET_NSEC_PER_MSEC); +} + +static int rdma_closefd(int fd1, int fd2) { + int rv, orig_errno; + fdEntry_t *fdEntry = getFdEntry(fd2); + if (fdEntry == NULL) { + errno = EBADF; + return -1; + } + + pthread_mutex_lock(&(fdEntry->lock)); + + { + do { + if (fd1 < 0) { + rv = rs_close(fd2); + } else { +// rv = dup2(fd1, fd2); + } + } while (rv == -1 && errno == EINTR); + + threadEntry_t *curr = fdEntry->threads; + while (curr != NULL) { + curr->intr = 1; + pthread_kill( curr->thr, sigWakeup ); + curr = curr->next; + } + } + + orig_errno = errno; + pthread_mutex_unlock(&(fdEntry->lock)); + errno = orig_errno; + + return rv; +} + +int RDMA_Dup2(int fd, int fd2) { + if (fd < 0) { + errno = EBADF; + return -1; + } + return rdma_closefd(fd, fd2); +} + +int RDMA_SocketClose(int fd) { + return rdma_closefd(-1, fd); +} + +int RDMA_Read(int s, void* buf, size_t len) { + BLOCKING_IO_RETURN_INT( s, rs_recv(s, buf, len, 0) ); +} + +int RDMA_NonBlockingRead(int s, void* buf, size_t len) { + BLOCKING_IO_RETURN_INT( s, rs_recv(s, buf, len, MSG_DONTWAIT) ); +} + +int RDMA_ReadV(int s, const struct iovec * vector, int count) { + BLOCKING_IO_RETURN_INT( s, rs_readv(s, vector, count) ); +} + +int RDMA_RecvFrom(int s, void *buf, int len, unsigned int flags, + struct sockaddr *from, socklen_t *fromlen) { + BLOCKING_IO_RETURN_INT( s, rs_recvfrom(s, buf, len, flags, from, fromlen) ); +} + +int RDMA_Send(int s, void *msg, int len, unsigned int flags) { + BLOCKING_IO_RETURN_INT( s, rs_send(s, msg, len, flags) ); +} + +int RDMA_WriteV(int s, const struct iovec * vector, int count) { + BLOCKING_IO_RETURN_INT( s, rs_writev(s, vector, count) ); +} + +int NET_RSendTo(int s, const void *msg, int len, unsigned int + flags, const struct sockaddr *to, int tolen) { + BLOCKING_IO_RETURN_INT( s, rs_sendto(s, msg, len, flags, to, tolen) ); +} + +int RDMA_Accept(int s, struct sockaddr *addr, socklen_t *addrlen) { + BLOCKING_IO_RETURN_INT( s, rs_accept(s, addr, addrlen) ); +} + +int RDMA_Connect(int s, struct sockaddr *addr, int addrlen) { + BLOCKING_IO_RETURN_INT( s, rs_connect(s, addr, addrlen) ); +} + +int RDMA_Poll(struct pollfd *ufds, unsigned int nfds, int timeout) { + BLOCKING_IO_RETURN_INT( ufds[0].fd, rs_poll(ufds, nfds, timeout) ); +} + +int RDMA_Timeout(JNIEnv *env, int s, long timeout, jlong nanoTimeStamp) { + jlong prevNanoTime = nanoTimeStamp; + jlong nanoTimeout = (jlong)timeout * NET_NSEC_PER_MSEC; + fdEntry_t *fdEntry = getFdEntry(s); + + if (fdEntry == NULL) { + errno = EBADF; + return -1; + } + + for(;;) { + struct pollfd pfd; + int rv; + threadEntry_t self; + + pfd.fd = s; + pfd.events = POLLIN | POLLERR; + + startOp(fdEntry, &self); + rv = rs_poll(&pfd, 1, nanoTimeout / NET_NSEC_PER_MSEC); + endOp(fdEntry, &self); + if (rv < 0 && errno == EINTR) { + jlong newNanoTime = JVM_NanoTime(env, 0); + nanoTimeout -= newNanoTime - prevNanoTime; + if (nanoTimeout < NET_NSEC_PER_MSEC) { + return 0; + } + prevNanoTime = newNanoTime; + } else { + return rv; + } + } +} --- /dev/null 2018-09-16 08:14:05.604314476 -0700 +++ new/src/jdk.net/linux/native/libextnet/rdma_util_md.h 2018-10-05 14:14:59.035757217 -0700 @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include +#include +#include +#include + +/************************************************************************ + * Functions + */ +int rdma_supported(); +int RDMA_MapSocketOption(jint cmd, int *level, int *optname); +int RDMA_Bind(int fd, SOCKETADDRESS *sa, int len); +int RDMA_Timeout(JNIEnv *env, int s, long timeout, jlong nanoTimeStamp); +int RDMA_Read(int s, void* buf, size_t len); +int RDMA_NonBlockingRead(int s, void* buf, size_t len); +int RDMA_RecvFrom(int s, void *buf, int len, unsigned int flags, + struct sockaddr *from, socklen_t *fromlen); +int RDMA_ReadV(int s, const struct iovec * vector, int count); +int RDMA_Send(int s, void *msg, int len, unsigned int flags); +int RDMA_SendTo(int s, const void *msg, int len, unsigned int + flags, const struct sockaddr *to, int tolen); +int RDMA_Writev(int s, const struct iovec * vector, int count); +int RDMA_Connect(int s, struct sockaddr *addr, int addrlen); +int RDMA_Accept(int s, struct sockaddr *addr, socklen_t *addrlen); +int RDMA_SocketClose(int s); +int RDMA_Dup2(int oldfd, int newfd); +int RDMA_Poll(struct pollfd *ufds, unsigned int nfds, int timeout); +int RDMA_SocketAvailable(int s, jint *pbytes); +int RDMA_SetSockOpt(int fd, int level, int opt, const void *arg, int len); +int RDMA_GetSockOpt(int fd, int level, int opt, void *result, int *len); --- /dev/null 2018-09-16 08:14:05.604314476 -0700 +++ new/src/jdk.net/share/classes/jdk/internal/net/rdma/RdmaPollSelectorProvider.java 2018-10-05 14:14:59.659757224 -0700 @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.internal.net.rdma; + +import java.io.IOException; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; +import java.nio.channels.spi.AbstractSelector; +import java.nio.channels.spi.SelectorProvider; +import sun.nio.ch.SelectorProviderImpl; + +public class RdmaPollSelectorProvider + extends SelectorProviderImpl +{ + public static SelectorProvider provider() { + throw new UnsupportedOperationException(); + } + + public AbstractSelector openSelector() throws IOException { + throw new UnsupportedOperationException(); + } + + public SocketChannel openSocketChannel() throws IOException { + throw new UnsupportedOperationException(); + } + + public ServerSocketChannel openServerSocketChannel() throws IOException { + throw new UnsupportedOperationException(); + } +} --- /dev/null 2018-09-16 08:14:05.604314476 -0700 +++ new/src/jdk.net/share/classes/jdk/internal/net/rdma/RdmaSocketProvider.java 2018-10-05 14:15:00.253757231 -0700 @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.internal.net.rdma; + +import java.io.IOException; +import java.net.ServerSocket; +import java.net.Socket; + +public class RdmaSocketProvider { + public static Socket openSocket() throws IOException { + throw new UnsupportedOperationException(); + } + + public static ServerSocket openServerSocket() throws IOException { + throw new UnsupportedOperationException(); + } + + private RdmaSocketProvider() {} +} --- /dev/null 2018-09-16 08:14:05.604314476 -0700 +++ new/src/jdk.net/share/classes/jdk/net/RdmaSocketOptions.java 2018-10-05 14:15:00.875757238 -0700 @@ -0,0 +1,269 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.net; + +import java.io.FileDescriptor; +import java.net.SocketException; +import java.net.SocketOption; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.Collections; +import java.util.Set; +import jdk.internal.misc.JavaIOFileDescriptorAccess; +import jdk.internal.misc.SharedSecrets; +import java.lang.annotation.Native; + +/** + * Defines socket options specific to rsocket. + * The rsocket implementation is on Linux only. + * + * @since 12 + */ +public final class RdmaSocketOptions { + + private static class RdmaSocketOption implements SocketOption { + private final String name; + private final Class type; + RdmaSocketOption(String name, Class type) { + this.name = name; + this.type = type; + } + @Override public String name() { return name; } + @Override public Class type() { return type; } + @Override public String toString() { return name; } + } + + private RdmaSocketOptions() { } + + /** + * Set the integer size of the underlying RDMA send queue used by the + * platform for network I/O. + * + *

An implementation allows this socket option to be set before the + * socket is bound or connected. Changing the value of this socket option + * after the socket is bound has no effect. + * + * Valid for RDMA based TCP sockets: RdmaSocketImpl + */ + public static final SocketOption RDMA_SQSIZE = new + RdmaSocketOption("RDMA_SQSIZE", Integer.class); + + /** + * Set the integer size of the underlying RDMA receive queue used by the + * platform for network I/O. + * + *

An implementation allows this socket option to be set before the + * socket is bound or connected. Changing the value of this socket option + * after the socket is bound has no effect. + * + * Valid for RDMA based TCP sockets: RdmaSocketImpl + */ + public static final SocketOption RDMA_RQSIZE = new + RdmaSocketOption("RDMA_RQSIZE", Integer.class); + + /** + * Set the integer size of the underlying RDMA inline data used by the + * platform for network I/O. + * + *

An implementation allows this socket option to be set before the + * socket is bound or connected. Changing the value of this socket option + * after the socket is bound has no effect. + * + * Valid for RDMA based TCP sockets: RdmaSocketImpl + */ + public static final SocketOption RDMA_INLINE = new + RdmaSocketOption("RDMA_INLINE", Integer.class); + + @Native private static final int SQSIZE = 0x3001; + + @Native private static final int RQSIZE = 0x3002; + + @Native private static final int INLINE = 0x3003; + + private static final PlatformRdmaSocketOptions platformRdmaSocketOptions = + PlatformRdmaSocketOptions.get(); + + private static final boolean rdmaSocketSupported = + platformRdmaSocketOptions.rdmaSocketSupported(); + + private static final Set> rdmaOptions = options(); + + static Set> options() { + if (rdmaSocketSupported) + return Set.of(RDMA_SQSIZE, RDMA_RQSIZE, RDMA_INLINE); + else + return Collections.>emptySet(); + } + + static { + sun.net.ext.RdmaSocketOptions.register( + new sun.net.ext.RdmaSocketOptions(rdmaOptions) { + + @Override + public void setOption(FileDescriptor fd, + SocketOption option, + Object value) + throws SocketException + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkPermission(new NetworkPermission("setOption." + option.name())); + + if (fd == null || !fd.valid()) + throw new SocketException("socket closed"); + + if ((option == RDMA_SQSIZE) || (option == RDMA_RQSIZE) + || (option == RDMA_INLINE)) { + assert rdmaSocketSupported; + + int val; + int opt; + if (option == RDMA_SQSIZE) { + if (value == null || (!(value instanceof Integer))) + throw new SocketException("Bad parameter for RDMA_SQSIZE"); + val = ((Integer) value).intValue(); + opt = SQSIZE; + if (val < 0) + throw new IllegalArgumentException("send queue size < 0"); + } else if (option == RDMA_RQSIZE) { + if (value == null || (!(value instanceof Integer))) + throw new SocketException("Bad parameter for RDMA_RQSIZE"); + val = ((Integer) value).intValue(); + opt = RQSIZE; + if (val < 0) + throw new IllegalArgumentException("receive queue size < 0"); + } else if (option == RDMA_INLINE) { + if (value == null || (!(value instanceof Integer))) + throw new SocketException("Bad parameter for RDMA_INLINE"); + val = ((Integer) value).intValue(); + opt = INLINE; + if (val < 0) + throw new IllegalArgumentException("inline size < 0"); + } else { + throw new SocketException("unrecognized RDMA socket option: " + option); + } + + setSockOpt(fd, opt, val); + + } else { + throw new InternalError("Unexpected option " + option); + } + } + + @Override + public Object getOption(FileDescriptor fd, + SocketOption option) + throws SocketException + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkPermission(new NetworkPermission("getOption." + option.name())); + + if (fd == null || !fd.valid()) + throw new SocketException("socket closed"); + + if ((option == RDMA_SQSIZE) || (option == RDMA_RQSIZE) + || (option == RDMA_INLINE)) { + assert rdmaSocketSupported; + int opt; + if (option == RDMA_SQSIZE) { + opt = SQSIZE; + } else if (option == RDMA_RQSIZE) { + opt = RQSIZE; + } else { + opt = INLINE; + } + return getSockOpt(fd, opt); + } else { + throw new InternalError("Unexpected option " + option); + } + } + }); + } + + private static final JavaIOFileDescriptorAccess fdAccess = + SharedSecrets.getJavaIOFileDescriptorAccess(); + + private static void setSockOpt(FileDescriptor fd, int opt, int val) + throws SocketException + { + platformRdmaSocketOptions.setSockOpt(fdAccess.get(fd), opt, val); + } + + private static int getSockOpt(FileDescriptor fd, int opt) + throws SocketException + { + return platformRdmaSocketOptions.getSockOpt(fdAccess.get(fd), opt); + } + + static class PlatformRdmaSocketOptions { + + protected PlatformRdmaSocketOptions() {} + + @SuppressWarnings("unchecked") + private static PlatformRdmaSocketOptions newInstance(String cn) { + Class c; + try { + c = (Class)Class.forName(cn); + return c.getConstructor(new Class[] { }).newInstance(); + } catch (ReflectiveOperationException x) { + throw new AssertionError(x); + } + } + + private static PlatformRdmaSocketOptions create() { + String osname = AccessController.doPrivileged( + new PrivilegedAction() { + public String run() { + return System.getProperty("os.name"); + } + }); + if ("Linux".equals(osname)) + return newInstance("jdk.net.LinuxRdmaSocketOptions"); + return new PlatformRdmaSocketOptions(); + } + + private static final PlatformRdmaSocketOptions instance = create(); + + static PlatformRdmaSocketOptions get() { + return instance; + } + + void setSockOpt(int fd, int opt, int value) + throws SocketException + { + throw new UnsupportedOperationException("unsupported socket option"); + } + + int getSockOpt(int fd, int opt) throws SocketException { + throw new UnsupportedOperationException("unsupported socket option"); + } + + boolean rdmaSocketSupported() { + return false; + } + } +} --- /dev/null 2018-09-16 08:14:05.604314476 -0700 +++ new/test/jdk/jdk/net/Sockets/rsocket/RsocketTest.java 2018-10-05 14:15:01.501757246 -0700 @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8195160 + * @summary Test to check if rsocket is available + * @requires (os.family == "linux") + * @library /test/lib + * @build jdk.test.lib.Platform + * @run main/native RsocketTest + */ + +import jdk.net.Sockets; + +public class RsocketTest { + private static boolean supported; + private static boolean checked; + + static { + System.loadLibrary("RsocketTest"); + } + + public static boolean isRsocketAvailable() { + if (!checked) { + checked = true; + supported = isRsocketAvailable0(); + } + return supported; + } + + private static native boolean isRsocketAvailable0(); + + public static void main(String[] args) throws Exception { + System.out.println("testing rsocket!"); + } +} --- /dev/null 2018-09-16 08:14:05.604314476 -0700 +++ new/test/jdk/jdk/net/Sockets/rsocket/Selector/BasicAccept.java 2018-10-05 14:15:02.126757253 -0700 @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8195160 + * @summary Test RdmaSelector with RdmaServerSocketChannels + * @requires (os.family == "linux") + * @library .. /test/lib /test/jdk/java/nio/channels + * @build RsocketTest + * @run main/othervm -Djava.net.preferIPv4Stack=true BasicAccept + */ + +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.nio.ByteBuffer; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; +import java.nio.channels.spi.SelectorProvider; +import java.util.Iterator; +import java.util.Set; +import jdk.net.Sockets; + +public class BasicAccept { + + static void server(ServerSocketChannel ssc) throws Exception { + Selector acceptSelector = Sockets.openRdmaSelector(); + try { + ssc.configureBlocking(false); + SelectionKey acceptKey + = ssc.register(acceptSelector, SelectionKey.OP_ACCEPT); + for (;;) { + int n = acceptSelector.select(); + if (Thread.interrupted()) + break; + if (n == 0) + continue; + Set readyKeys = acceptSelector.selectedKeys(); + Iterator i = readyKeys.iterator(); + while (i.hasNext()) { + SelectionKey sk = i.next(); + i.remove(); + ServerSocketChannel nextReady + = (ServerSocketChannel)sk.channel(); + SocketChannel sc = nextReady.accept(); + ByteBuffer bb = ByteBuffer.wrap(new byte[] { 42 }); + sc.write(bb); + sc.close(); + } + } + } finally { + acceptSelector.close(); + } + } + + private static class Server extends TestThread { + final ServerSocketChannel ssc; + Server() throws IOException { + super("Server", System.err); + this.ssc = Sockets.openRdmaServerSocketChannel() + .bind(new InetSocketAddress(InetAddress.getLocalHost(), 0)); + } + int port() { + return ssc.socket().getLocalPort(); + } + void go() throws Exception { + try { + server(ssc); + } finally { + ssc.close(); + } + } + } + + static void client(int port) throws Exception { + // Get a connection from the server + InetAddress lh = InetAddress.getLocalHost(); + InetSocketAddress isa + = new InetSocketAddress(lh, port); + int connectFailures = 0; + boolean result = false; + SocketChannel sc = Sockets.openRdmaSocketChannel(); + for (;;) { + try { + result = sc.connect(isa); + break; + } catch (java.net.ConnectException e) { + connectFailures++; + if (connectFailures > 30) + throw new RuntimeException("Cannot connect"); + Thread.currentThread().sleep(100); + sc = Sockets.openRdmaSocketChannel(); + } + } + if (result) { + System.err.println("Connected"); + } else { + // Only happens when server and client are on separate machines + System.err.println("Connection pending..."); + connectFailures = 0; + while (!result) { + try { + result = sc.finishConnect(); + if (!result) + System.err.println("Not finished"); + Thread.sleep(50); + } catch (java.net.ConnectException e) { + Thread.sleep(100); + connectFailures++; + if (connectFailures > 30) + throw new RuntimeException("Cannot finish connecting"); + } + } + System.err.println("Finished connecting"); + } + + ByteBuffer bb = ByteBuffer.allocateDirect(1024); + if (sc.read(bb) < 0) + throw new RuntimeException("Failed to read from server"); + if (bb.get(0) != 42) + throw new RuntimeException("Read wrong byte from server"); + System.err.println("Read from server"); + sc.close(); + } + + public static void main(String[] args) throws Exception { + if (!RsocketTest.isRsocketAvailable()) + return; + + Server server = new Server(); + server.start(); + try { + client(server.port()); + } finally { + server.interrupt(); + server.finish(2000); + } + } + +} --- /dev/null 2018-09-16 08:14:05.604314476 -0700 +++ new/test/jdk/jdk/net/Sockets/rsocket/Selector/BasicConnect.java 2018-10-05 14:15:02.734757260 -0700 @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 8195160 + * @summary Test for nonblocking connect and finishConnect + * @requires (os.family == "linux") + * @library .. /test/lib + * @build jdk.test.lib.Utils TestServers + * @build RsocketTest + * @run main/othervm -Djava.net.preferIPv4Stack=true BasicConnect + */ + +import java.net.InetSocketAddress; +import java.nio.ByteBuffer; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.nio.channels.SocketChannel; +import java.nio.channels.spi.SelectorProvider; +import java.util.Iterator; +import java.util.Set; +import jdk.net.Sockets; + +/** + * Typically there would be more than one channel registered to select + * on, this test is just a very simple version with only one channel + * registered for the connectSelector. + */ + +public class BasicConnect { + + public static void main(String[] args) throws Exception { + if (!RsocketTest.isRsocketAvailable()) + return; + + Selector connectSelector = + Sockets.openRdmaSelector(); + try { + TestServers.EchoServer echoServer + = TestServers.EchoServer.startNewServer(100); + InetSocketAddress isa + = new InetSocketAddress(echoServer.getAddress(), + echoServer.getPort()); + SocketChannel sc = Sockets.openRdmaSocketChannel(); + sc.configureBlocking(false); + boolean result = sc.connect(isa); + if (result) { + System.out.println("Socket immediately connected on " + + System.getProperty("os.name") + + ": " + sc); + } + while (!result) { + SelectionKey connectKey = sc.register(connectSelector, + SelectionKey.OP_CONNECT); + int keysAdded = connectSelector.select(); + if (keysAdded > 0) { + Set readyKeys = connectSelector.selectedKeys(); + Iterator i = readyKeys.iterator(); + while (i.hasNext()) { + SelectionKey sk = (SelectionKey)i.next(); + i.remove(); + SocketChannel nextReady = (SocketChannel)sk.channel(); + result = nextReady.finishConnect(); + if (result) + sk.cancel(); + } + } + } + + byte[] bs = new byte[] { (byte)0xca, (byte)0xfe, + (byte)0xba, (byte)0xbe }; + ByteBuffer bb = ByteBuffer.wrap(bs); + sc.configureBlocking(true); + sc.write(bb); + bb.rewind(); + + ByteBuffer bb2 = ByteBuffer.allocateDirect(100); + int n = sc.read(bb2); + bb2.flip(); + + sc.close(); + connectSelector.close(); + + if (!bb.equals(bb2)) + throw new Exception("Echoed bytes incorrect: Sent " + + bb + ", got " + bb2); + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException("Test Failed!"); + } + } +} --- /dev/null 2018-09-16 08:14:05.604314476 -0700 +++ new/test/jdk/jdk/net/Sockets/rsocket/Selector/Connect.java 2018-10-05 14:15:03.322757267 -0700 @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 8195160 + * @summary Test Test Making lots of Selectors + * @requires (os.family == "linux") + * @library .. /test/lib + * @build jdk.test.lib.Utils TestServers + * @build RsocketTest + * @run main/othervm -Djava.net.preferIPv4Stack=true Connect + */ + +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.nio.ByteBuffer; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.nio.channels.SocketChannel; +import java.nio.channels.spi.SelectorProvider; +import java.util.Iterator; +import java.util.Set; +import jdk.net.Sockets; + +public class Connect implements Runnable { + + static int success = 0; + static int LIMIT = 30; + static SocketChannel sc; + static InetSocketAddress isa; + static boolean connected; + + public static void main(String[] args) throws Exception { + if (!RsocketTest.isRsocketAvailable()) + return; + + try (TestServers.DayTimeServer daytimeServer + = TestServers.DayTimeServer.startNewServer(50)) { + scaleTest(daytimeServer); + } + } + + static void scaleTest(TestServers.DayTimeServer daytimeServer) + throws Exception + { + InetAddress myAddress = daytimeServer.getAddress(); + isa = new InetSocketAddress(myAddress, daytimeServer.getPort()); + + for (int j=0; j 0) { + Set readyKeys = RSelector.selectedKeys(); + Iterator i = readyKeys.iterator(); + while (i.hasNext()) { + SelectionKey sk = i.next(); + SocketChannel nextReady = (SocketChannel)sk.channel(); + connected = nextReady.finishConnect(); + } + readyKeys.clear(); + } + } + RSelector.close(); + } + readAndClose(sc); + } + } + + static void readAndClose(SocketChannel sc) throws Exception { + ByteBuffer bb = ByteBuffer.allocateDirect(100); + int n = 0; + while (n == 0) // Note this is not a rigorous check for done reading + n = sc.read(bb); + sc.close(); + success++; + System.out.println("success count = " + success); + } + + public void run() { + try { + connected = sc.connect(isa); + System.out.println("connected = " + connected); + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException("Test Failed!"); + } + } +} --- /dev/null 2018-09-16 08:14:05.604314476 -0700 +++ new/test/jdk/jdk/net/Sockets/rsocket/Selector/SelectorTest.java 2018-10-05 14:15:03.902757274 -0700 @@ -0,0 +1,425 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 8195160 + * @summary Test RdmaSelector with RdmaServerSocketChannels + * @requires (os.family == "linux") + * @library .. /test/lib /test/jdk/java/nio/channels + * @build RsocketTest + * @run main/othervm -Djava.net.preferIPv4Stack=true BasicAccept + */ + +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.nio.ByteBuffer; +import java.nio.channels.Selector; +import java.nio.channels.SelectionKey; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; +import java.nio.channels.spi.SelectorProvider; +import java.util.Iterator; +import java.util.Set; +import jdk.net.Sockets; + +public class SelectorTest { + private static List clientList = new LinkedList(); + private static Random rnd = new Random(); + public static int NUM_CLIENTS = 5; + public static int TEST_PORT = 31452; + static PrintStream log = System.err; + private static int FINISH_TIME = 30000; + + /* + * Usage note + * + * java SelectorTest [server] [client ] [] + * + * No arguments runs both client and server in separate threads + * using the default port of 31452. + * + * client runs the client on this machine and connects to server + * at the given IP address. + * + * server runs the server on localhost. + */ + public static void main(String[] args) throws Exception { + if (!RsocketTest.isRsocketAvailable()) + return; + + if (args.length == 0) { + Server server = new Server(0); + server.start(); + try { + Thread.sleep(1000); + } catch (InterruptedException e) { } + InetSocketAddress isa + = new InetSocketAddress(InetAddress.getLocalHost(), server.port()); + Client client = new Client(isa); + client.start(); + if ((server.finish(FINISH_TIME) & client.finish(FINISH_TIME)) == 0) + throw new Exception("Failure"); + log.println(); + + } else if (args[0].equals("server")) { + + if (args.length > 1) + TEST_PORT = Integer.parseInt(args[1]); + Server server = new Server(TEST_PORT); + server.start(); + if (server.finish(FINISH_TIME) == 0) + throw new Exception("Failure"); + log.println(); + + } else if (args[0].equals("client")) { + + if (args.length < 2) { + log.println("No host specified: terminating."); + return; + } + String ip = args[1]; + if (args.length > 2) + TEST_PORT = Integer.parseInt(args[2]); + InetAddress ia = InetAddress.getByName(ip); + InetSocketAddress isa = new InetSocketAddress(ia, TEST_PORT); + Client client = new Client(isa); + client.start(); + if (client.finish(FINISH_TIME) == 0) + throw new Exception("Failure"); + log.println(); + + } else { + System.out.println("Usage note:"); + System.out.println("java SelectorTest [server] [client ] []"); + System.out.println("No arguments runs both client and server in separate threads using the default port of 31452."); + System.out.println("client runs the client on this machine and connects to the server specified."); + System.out.println("server runs the server on localhost."); + } + } + + static class Client extends TestThread { + InetSocketAddress isa; + Client(InetSocketAddress isa) { + super("Client", SelectorTest.log); + this.isa = isa; + } + + public void go() throws Exception { + log.println("starting client..."); + for (int i=0; i 0); + } + } + + static class Server extends TestThread { + private final ServerSocketChannel ssc; + private List socketList = new ArrayList(); + private ServerSocket ss; + private int connectionsAccepted = 0; + private Selector pollSelector; + private Selector acceptSelector; + private Set pkeys; + private Set pskeys; + + Server(int port) throws IOException { + super("Server", SelectorTest.log); + this.ssc = Sockets.openRdmaServerSocketChannel(); + ssc.bind(new InetSocketAddress(InetAddress.getLocalHost(), port)); + } + + int port() { + return ssc.socket().getLocalPort(); + } + + public void go() throws Exception { + log.println("starting server..."); + acceptSelector = Sockets.openRdmaSelector(); + pollSelector = Sockets.openRdmaSelector(); + pkeys = pollSelector.keys(); + pskeys = pollSelector.selectedKeys(); + Set readyKeys = acceptSelector.selectedKeys(); + RequestHandler rh = new RequestHandler(pollSelector, log); + Thread requestThread = new Thread(rh); + + requestThread.start(); + + ssc.configureBlocking(false); + SelectionKey acceptKey = ssc.register(acceptSelector, + SelectionKey.OP_ACCEPT); + while(connectionsAccepted < SelectorTest.NUM_CLIENTS) { + int keysAdded = acceptSelector.select(100); + if (keysAdded > 0) { + Iterator i = readyKeys.iterator(); + while(i.hasNext()) { + SelectionKey sk = (SelectionKey)i.next(); + i.remove(); + ServerSocketChannel nextReady = + (ServerSocketChannel)sk.channel(); + SocketChannel sc = nextReady.accept(); + connectionsAccepted++; + if (sc != null) { + sc.configureBlocking(false); + synchronized (pkeys) { + sc.register(pollSelector, SelectionKey.OP_READ); + } + } else { + throw new RuntimeException( + "Socket does not support Channels"); + } + } + } + } + acceptKey.cancel(); + requestThread.join(); + acceptSelector.close(); + pollSelector.close(); + } + } +} + +class RemoteEntity { + private static Random rnd = new Random(); + int id; + ByteBuffer data; + int dataWrittenIndex; + int totalDataLength; + boolean initiated = false; + boolean connected = false; + boolean written = false; + boolean acked = false; + boolean closed = false; + private SocketChannel sc; + ByteBuffer ackBuffer; + PrintStream log; + InetSocketAddress server; + + RemoteEntity(int id, InetSocketAddress server, PrintStream log) + throws Exception + { + int connectFailures = 0; + this.id = id; + this.log = log; + this.server = server; + + sc = Sockets.openRdmaSocketChannel(); + sc.configureBlocking(false); + + // Prepare the data buffer to write out from this entity + // Let's use both slow and fast buffers + if (rnd.nextBoolean()) + data = ByteBuffer.allocateDirect(100); + else + data = ByteBuffer.allocate(100); + String number = Integer.toString(id); + if (number.length() == 1) + number = "0"+number; + String source = "Testing from " + number; + data.put(source.getBytes("8859_1")); + data.flip(); + totalDataLength = source.length(); + + // Allocate an ack buffer + ackBuffer = ByteBuffer.allocateDirect(10); + } + + private void reset() throws Exception { + sc.close(); + sc = Sockets.openRdmaSocketChannel(); + sc.configureBlocking(false); + } + + private void connect() throws Exception { + try { + connected = sc.connect(server); + initiated = true; + } catch (ConnectException e) { + initiated = false; + reset(); + } + } + + private void finishConnect() throws Exception { + try { + connected = sc.finishConnect(); + } catch (IOException e) { + initiated = false; + reset(); + } + } + + int id() { + return id; + } + + boolean cycle() throws Exception { + if (!initiated) + connect(); + else if (!connected) + finishConnect(); + else if (!written) + writeCycle(); + else if (!acked) + ackCycle(); + else if (!closed) + close(); + return closed; + } + + private void ackCycle() throws Exception { + //log.println("acking from "+id); + int bytesRead = sc.read(ackBuffer); + if (bytesRead > 0) { + acked = true; + } + } + + private void close() throws Exception { + sc.close(); + closed = true; + } + + private void writeCycle() throws Exception { + log.println("writing from "+id); + int numBytesToWrite = rnd.nextInt(10)+1; + int newWriteTarget = dataWrittenIndex + numBytesToWrite; + if (newWriteTarget > totalDataLength) + newWriteTarget = totalDataLength; + data.limit(newWriteTarget); + int bytesWritten = sc.write(data); + if (bytesWritten > 0) + dataWrittenIndex += bytesWritten; + if (dataWrittenIndex == totalDataLength) { + written = true; + sc.socket().shutdownOutput(); + } + } + +} + + +class RequestHandler implements Runnable { + private static Random rnd = new Random(); + private Selector selector; + private int connectionsHandled = 0; + private HashMap dataBin = new HashMap(); + PrintStream log; + + public RequestHandler(Selector selector, PrintStream log) { + this.selector = selector; + this.log = log; + } + + public void run() { + log.println("starting request handler..."); + int connectionsAccepted = 0; + + Set nKeys = selector.keys(); + Set readyKeys = selector.selectedKeys(); + + try { + while(connectionsHandled < SelectorTest.NUM_CLIENTS) { + int numKeys = selector.select(100); + + // Process channels with data + synchronized (nKeys) { + if (readyKeys.size() > 0) { + Iterator i = readyKeys.iterator(); + while(i.hasNext()) { + SelectionKey sk = (SelectionKey)i.next(); + i.remove(); + SocketChannel sc = (SocketChannel)sk.channel(); + if (sc.isOpen()) + read(sk, sc); + } + } + } + + // Give other threads a chance to run + if (numKeys == 0) { + try { + Thread.sleep(1); + } catch (Exception x) {} + } + } + } catch (Exception e) { + log.println("Unexpected error 1: "+e); + e.printStackTrace(); + } + } + + private void read(SelectionKey sk, SocketChannel sc) throws Exception { + ByteBuffer bin = (ByteBuffer)dataBin.get(sc); + if (bin == null) { + if (rnd.nextBoolean()) + bin = ByteBuffer.allocateDirect(100); + else + bin = ByteBuffer.allocate(100); + dataBin.put(sc, bin); + } + + int bytesRead = 0; + do { + bytesRead = sc.read(bin); + } while(bytesRead > 0); + + if (bytesRead == -1) { + sk.interestOps(0); + bin.flip(); + int size = bin.limit(); + byte[] data = new byte[size]; + for(int j=0; j>>"+message + "<<<"); + log.println("Handled: "+connectionsHandled); + } + } + + private void acknowledge(SocketChannel sc) throws Exception { + ByteBuffer ackBuffer = ByteBuffer.allocateDirect(10); + String s = "ack"; + ackBuffer.put(s.getBytes("8859_1")); + ackBuffer.flip(); + int bytesWritten = 0; + while(bytesWritten == 0) { + bytesWritten += sc.write(ackBuffer); + } + sc.close(); + } +} --- /dev/null 2018-09-16 08:14:05.604314476 -0700 +++ new/test/jdk/jdk/net/Sockets/rsocket/ServerSocketChannel/Basic.java 2018-10-05 14:15:04.496757281 -0700 @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 8195160 + * @summary Unit test for server-socket channels + * @requires (os.family == "linux") + * @library .. /test/lib /test/jdk/java/nio/channels + * @build RsocketTest + * @run main/othervm -Djava.net.preferIPv4Stack=true Basic + */ + +import java.io.IOException; +import java.io.PrintStream; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.nio.ByteBuffer; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; +import java.util.Random; +import jdk.net.Sockets; + +public class Basic { + + static PrintStream log = System.err; + + static class Server + extends TestThread + { + ServerSocketChannel ssc; + boolean block; + + Server(ServerSocketChannel ssc, boolean block) { + super("Server", Basic.log); + this.ssc = ssc; + this.block = block; + } + + void go() throws Exception { + log.println("Server: Listening " + + (block ? "(blocking)" : "(non-blocking)")); + if (!block) + ssc.configureBlocking(false); + log.println(" " + ssc); + SocketChannel sc = null; + for (;;) { + sc = ssc.accept(); + if (sc != null) { + break; + } + log.println("Server: Sleeping..."); + Thread.sleep(50); + } + log.println("Server: Accepted " + sc); + ByteBuffer bb = ByteBuffer.allocateDirect(100); + if (sc.read(bb) != 1) + throw new Exception("Read failed"); + bb.flip(); + byte b = bb.get(); + log.println("Server: Read " + b + ", writing " + (b + 1)); + bb.clear(); + bb.put((byte)43); + bb.flip(); + if (sc.write(bb) != 1) + throw new Exception("Write failed"); + sc.close(); + ssc.close(); + log.println("Server: Finished"); + } + + } + + static class Client + extends TestThread + { + int port; + boolean dally; + + Client(int port, boolean block) { + super("Client", Basic.log); + this.port = port; + this.dally = !block; + } + + public void go() throws Exception { + if (dally) + Thread.sleep(200); + InetSocketAddress isa + = new InetSocketAddress(InetAddress.getLocalHost(), port); + log.println("Client: Connecting to " + isa); + SocketChannel sc = Sockets.openRdmaSocketChannel(); + sc.connect(isa); + log.println("Client: Connected"); + ByteBuffer bb = ByteBuffer.allocateDirect(512); + bb.put((byte)42).flip(); + log.println("Client: Writing " + bb.get(0)); + if (sc.write(bb) != 1) + throw new Exception("Write failed"); + bb.clear(); + if (sc.read(bb) != 1) + throw new Exception("Read failed"); + bb.flip(); + if (bb.get() != 43) + throw new Exception("Read " + bb.get(bb.position() - 1)); + log.println("Client: Read " + bb.get(0)); + sc.close(); + log.println("Client: Finished"); + } + + } + + static void test(boolean block) throws Exception { + ServerSocketChannel ssc = Sockets.openRdmaServerSocketChannel(); + ssc.socket().setReuseAddress(true); + InetAddress lh = InetAddress.getLocalHost(); + int port; + Random r = new Random(); + for (;;) { + port = r.nextInt((1 << 16) - 1024) + 1024; + InetSocketAddress isa = new InetSocketAddress(lh, port); + try { + ssc.socket().bind(isa); + } catch (IOException x) { + continue; + } + break; + } + + Server server = new Server(ssc, block); + Client client = new Client(port, block); + server.start(); + client.start(); + if ((server.finish(0) & client.finish(0)) == 0) + throw new Exception("Failure"); + log.println(); + } + + public static void main(String[] args) throws Exception { + if (!RsocketTest.isRsocketAvailable()) + return; + + log.println(); + test(true); + test(false); + } + +} --- /dev/null 2018-09-16 08:14:05.604314476 -0700 +++ new/test/jdk/jdk/net/Sockets/rsocket/ServerSocketChannel/SocketOptionTests.java 2018-10-05 14:15:05.130757289 -0700 @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 8195160 + * @summary Unit test for ServerSocketChannel setOption/getOption/options + * methods. + * @modules jdk.net + * @requires !vm.graal.enabled + * @requires (os.family == "linux") + * @library .. + * @build RsocketTest + * @run main/othervm -Djava.net.preferIPv4Stack=true SocketOptionTests + */ + +import java.io.IOException; +import java.net.SocketOption; +import java.nio.channels.ClosedChannelException; +import java.nio.channels.ServerSocketChannel; +import java.util.Set; +import jdk.net.Sockets; +import static java.net.StandardSocketOptions.*; +import static jdk.net.RdmaSocketOptions.*; + +public class SocketOptionTests { + + static void checkOption(ServerSocketChannel ssc, SocketOption name, Object expectedValue) + throws IOException + { + Object value = ssc.getOption(name); + if (!value.equals(expectedValue)) + throw new RuntimeException("value not as expected"); + } + + public static void main(String[] args) throws IOException { + if (!RsocketTest.isRsocketAvailable()) + return; + + ServerSocketChannel ssc = Sockets.openRdmaServerSocketChannel(); + + // check supported options + Set> options = ssc.supportedOptions(); + if (!options.contains(SO_REUSEADDR)) + throw new RuntimeException("SO_REUSEADDR should be supported"); + if (!options.contains(SO_RCVBUF)) + throw new RuntimeException("SO_RCVBUF should be supported"); + if (!options.contains(RDMA_SQSIZE)) + throw new RuntimeException("RDMA_SQSIZE should be supported"); + if (!options.contains(RDMA_RQSIZE)) + throw new RuntimeException("RDMA_RQSIZE should be supported"); + if (!options.contains(RDMA_INLINE)) + throw new RuntimeException("RDMA_INLINE should be supported"); + + // allowed to change when not bound + ssc.setOption(SO_RCVBUF, 256*1024); // can't check + int before = ssc.getOption(SO_RCVBUF); + int after = ssc.setOption(SO_RCVBUF, Integer.MAX_VALUE).getOption(SO_RCVBUF); + if (after < before) + throw new RuntimeException("setOption caused SO_RCVBUF to decrease"); + ssc.setOption(SO_REUSEADDR, true); + checkOption(ssc, SO_REUSEADDR, true); + ssc.setOption(SO_REUSEADDR, false); + checkOption(ssc, SO_REUSEADDR, false); + + // NullPointerException + try { + ssc.setOption(null, "value"); + throw new RuntimeException("NullPointerException not thrown"); + } catch (NullPointerException x) { + } + try { + ssc.getOption(null); + throw new RuntimeException("NullPointerException not thrown"); + } catch (NullPointerException x) { + } + + // ClosedChannelException + ssc.close(); + try { + ssc.setOption(SO_REUSEADDR, true); + throw new RuntimeException("ClosedChannelException not thrown"); + } catch (ClosedChannelException x) { + } + } +} --- /dev/null 2018-09-16 08:14:05.604314476 -0700 +++ new/test/jdk/jdk/net/Sockets/rsocket/Socket/BasicSocketTest.java 2018-10-05 14:15:05.721757296 -0700 @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8195160 + * @summary Test for basic functionality for RdmaSocket and RdmaServerSocket + * @requires (os.family == "linux") + * @library .. /test/lib + * @build RsocketTest + * @run main/othervm -Djava.net.preferIPv4Stack=true BasicSocketTest + */ + +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; +import jdk.net.Sockets; + +public class BasicSocketTest implements Runnable { + static ServerSocket ss; + static Socket s1, s2; + static InetAddress iaddr; + static int port = 0; + static ServerSocketChannel ssc; + static SocketChannel sc1, sc2; + static String message = "This is a message!"; + static int length = -1; + + public static void main(String args[]) throws Exception { + if (!RsocketTest.isRsocketAvailable()) + return; + + iaddr = InetAddress.getLocalHost(); + + try { + ss = Sockets.openRdmaServerSocket(); + s1 = Sockets.openRdmaSocket(); + ss.bind(new InetSocketAddress(iaddr, port)); + + Thread t = new Thread(new BasicSocketTest()); + t.start(); + s2 = ss.accept(); + + InputStream is = s2.getInputStream(); + length = message.length(); + + int num = 0; + byte[] buf = new byte[length]; + while (num < length) { + int l = is.read(buf); + num += l; + } + + String result = new String(buf); + if(!result.equals(message)) + throw new RuntimeException("Test Failed!"); + + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException("Test Failed!"); + } finally { + ss.close(); + s1.close(); + s2.close(); + } + } + + public void run() { + try { + s1.connect(new InetSocketAddress(iaddr, ss.getLocalPort())); + + OutputStream os = s1.getOutputStream(); + os.write(message.getBytes()); + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException("Test Failed!"); + } + } +} --- /dev/null 2018-09-16 08:14:05.604314476 -0700 +++ new/test/jdk/jdk/net/Sockets/rsocket/Socket/GetLocalAddress.java 2018-10-05 14:15:06.333757303 -0700 @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 8195160 + * @summary Test the java.net.socket.GetLocalAddress method + * @requires (os.family == "linux") + * @library .. /test/lib + * @build RsocketTest + * @run main/othervm -Djava.net.preferIPv4Stack=true GetLocalAddress + */ + +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.ServerSocket; +import java.net.Socket; +import jdk.net.Sockets; + +public class GetLocalAddress implements Runnable { + static Socket s; + static ServerSocket ss; + static InetAddress addr; + static int port; + + public static void main(String args[]) throws Exception { + if (!RsocketTest.isRsocketAvailable()) + return; + + testBindNull(); + + boolean error = true; + int value = 0; + addr = InetAddress.getLocalHost(); + + ss = Sockets.openRdmaServerSocket(); + s = Sockets.openRdmaSocket(); + addr = InetAddress.getLocalHost(); + ss.bind(new InetSocketAddress(addr, 0)); + + port = ss.getLocalPort(); + + Thread t = new Thread(new GetLocalAddress()); + t.start(); + + Socket soc = ss.accept(); + + if(addr.equals(soc.getLocalAddress())) { + error = false; + } + if (error) + throw new RuntimeException("Socket.GetLocalAddress failed."); + soc.close(); + } + + public void run() { + try { + s.connect(new InetSocketAddress(addr, port)); + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException("Socket.GetLocalAddress failed."); + } + } + + static void testBindNull() throws Exception { + try { + Socket soc = Sockets.openRdmaSocket(); + soc.bind(null); + if (!soc.isBound()) + throw new RuntimeException( + "should be bound after bind(null)"); + if (soc.getLocalPort() <= 0) + throw new RuntimeException( + "bind(null) failed, local port: " + soc.getLocalPort()); + if (soc.getLocalAddress() == null) + throw new RuntimeException( + "bind(null) failed, local address is null"); + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException("Socket.GetLocalAddress failed."); + } + } +} --- /dev/null 2018-09-16 08:14:05.604314476 -0700 +++ new/test/jdk/jdk/net/Sockets/rsocket/SocketChannel/AdaptSocket.java 2018-10-05 14:15:06.929757310 -0700 @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 8195160 + * @summary Unit test for socket-channel adaptors + * @requires (os.family == "linux") + * @library .. /test/lib + * @build jdk.test.lib.Utils TestServers + * @build RsocketTest + * @run main/othervm -Djava.net.preferIPv4Stack=true AdaptSocket + */ + +import java.io.InputStream; +import java.io.PrintStream; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.SocketTimeoutException; +import java.nio.channels.SocketChannel; +import java.util.Arrays; +import jdk.net.Sockets; + +public class AdaptSocket { + + static final PrintStream out = System.out; + + static void test(TestServers.AbstractServer server, + int timeout, + boolean shouldTimeout) + throws Exception + { + out.println(); + + InetSocketAddress isa = new InetSocketAddress(server.getAddress(), server.getPort()); + SocketChannel sc = Sockets.openRdmaSocketChannel(); + Socket so = sc.socket(); + out.println("opened: " + so); + out.println(" " + sc); + + so.setTcpNoDelay(true); + so.setKeepAlive(true); + so.setReceiveBufferSize(512); + so.setSendBufferSize(512); + + if (timeout == 0) + so.connect(isa); + else { + try { + so.connect(isa, timeout); + } catch (SocketTimeoutException x) { + if (shouldTimeout) { + out.println("Connection timed out, as expected"); + return; + } else { + throw x; + } + } + } + out.println("connected: " + so); + out.println(" " + sc); + byte[] bb = new byte[100]; + int n = so.getInputStream().read(bb); + String s = new String(bb, 0, n - 2, "US-ASCII"); + out.println(isa + " says: \"" + s + "\""); + so.shutdownInput(); + out.println("ishut: " + sc); + so.shutdownOutput(); + out.println("oshut: " + sc); + so.close(); + out.println("closed: " + so); + out.println(" " + sc); + } + + static String dataString = "foo\r\n"; + + static void testRead(Socket so, boolean shouldTimeout) + throws Exception + { + String data = "foo\r\n"; + so.getOutputStream().write(dataString.getBytes("US-ASCII")); + InputStream is = so.getInputStream(); + try { + byte[] b = new byte[100]; + int n = is.read(b); + if (shouldTimeout) { + throw new Exception("Should time out, but not, data: " + Arrays.toString(b)); + } + if (n != 5) { + throw new Exception("Incorrect number of bytes read: " + n); + } + if (!dataString.equals(new String(b, 0, n, "US-ASCII"))) { + throw new Exception("Incorrect data read: " + n); + } + } catch (SocketTimeoutException x) { + if (shouldTimeout) { + out.println("Read timed out, as expected"); + return; + } + throw x; + } + } + + static void testRead(TestServers.EchoServer echoServer, + int timeout, + boolean shouldTimeout) + throws Exception + { + out.println(); + + InetSocketAddress isa + = new InetSocketAddress(echoServer.getAddress(), + echoServer.getPort()); + SocketChannel sc = Sockets.openRdmaSocketChannel(); + sc.connect(isa); + Socket so = sc.socket(); + out.println("connected: " + so); + out.println(" " + sc); + + if (timeout > 0) + so.setSoTimeout(timeout); + out.println("timeout: " + so.getSoTimeout()); + + testRead(so, shouldTimeout); + for (int i = 0; i < 4; i++) { + out.println("loop: " + i); + testRead(so, shouldTimeout); + } + + sc.close(); + } + + public static void main(String[] args) throws Exception { + if (!RsocketTest.isRsocketAvailable()) + return; + + try (TestServers.DayTimeServer dayTimeServer + = TestServers.DayTimeServer.startNewServer()) { + test(dayTimeServer, 0, false); + test(dayTimeServer, 1000, false); + } + + try (TestServers.DayTimeServer lingerDayTimeServer + = TestServers.DayTimeServer.startNewServer(100)) { + // this test no longer really test the connection timeout + // since there is no way to prevent the server from eagerly + // accepting connection... + test(lingerDayTimeServer, 10, true); + } + + try (TestServers.EchoServer echoServer + = TestServers.EchoServer.startNewServer()) { + testRead(echoServer, 0, false); + testRead(echoServer, 8000, false); + } + + try (TestServers.NoResponseServer noResponseServer + = TestServers.NoResponseServer.startNewServer()) { + testRead(noResponseServer, 10, true); + } + } +} --- /dev/null 2018-09-16 08:14:05.604314476 -0700 +++ new/test/jdk/jdk/net/Sockets/rsocket/SocketChannel/Basic.java 2018-10-05 14:15:07.522757317 -0700 @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 8195160 + * @summary Unit test for socket channels + * @requires (os.family == "linux") + * @library .. /test/lib + * @build jdk.test.lib.Utils TestServers + * @build RsocketTest + * @run main/othervm -Djava.net.preferIPv4Stack=true Basic + */ + +import java.net.InetSocketAddress; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.channels.SocketChannel; +import java.nio.charset.Charset; +import jdk.net.Sockets; + +public class Basic { + + static java.io.PrintStream out = System.out; + + static void test(TestServers.DayTimeServer daytimeServer) throws Exception { + InetSocketAddress isa + = new InetSocketAddress(daytimeServer.getAddress(), + daytimeServer.getPort()); + SocketChannel sc = Sockets.openRdmaSocketChannel(); + out.println("opened: " + sc); + sc.connect(isa); + out.println("connected: " + sc); + ByteBuffer bb = ByteBuffer.allocateDirect(100); + int n = sc.read(bb); + bb.position(bb.position() - 2); // Drop CRLF + bb.flip(); + CharBuffer cb = Charset.forName("US-ASCII").newDecoder().decode(bb); + out.println(isa + " says: \"" + cb + "\""); + sc.socket().shutdownInput(); + out.println("ishut: " + sc); + sc.socket().shutdownOutput(); + out.println("oshut: " + sc); + sc.close(); + out.println("closed: " + sc); + } + + public static void main(String[] args) throws Exception { + if (!RsocketTest.isRsocketAvailable()) + return; + + try (TestServers.DayTimeServer dayTimeServer + = TestServers.DayTimeServer.startNewServer(100)) { + test(dayTimeServer); + } + } +} --- /dev/null 2018-09-16 08:14:05.604314476 -0700 +++ new/test/jdk/jdk/net/Sockets/rsocket/SocketChannel/BasicSocketChannelTest.java 2018-10-05 14:15:08.149757324 -0700 @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8195160 + * @summary Test for basic functionality for RdmaSocketChannel + * and RdmaServerSocketChannel + * @requires (os.family == "linux") + * @library .. /test/lib + * @build RsocketTest + * @run main/othervm -Djava.net.preferIPv4Stack=true BasicSocketChannelTest + */ + +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; +import java.nio.ByteBuffer; +import jdk.net.Sockets; + +public class BasicSocketChannelTest implements Runnable { + static ServerSocketChannel ssc; + static SocketChannel sc1, sc2; + static InetAddress iaddr; + static int port = 0; + static String message = "This is a message!"; + static int length = -1; + + public static void main(String args[]) throws Exception { + if (!RsocketTest.isRsocketAvailable()) + return; + + iaddr = InetAddress.getLocalHost(); + length = message.length(); + String result; + + //test SocketChannel and ServerSocketChannel + try { + ssc = Sockets.openRdmaServerSocketChannel(); + sc1 = Sockets.openRdmaSocketChannel(); + + ssc.bind(new InetSocketAddress(iaddr, port)); + + Thread t = new Thread(new BasicSocketChannelTest(), "Channel"); + t.start(); + + sc2 = ssc.accept(); + ByteBuffer output = ByteBuffer.allocate(length); + int outputNum = 0; + while (outputNum < length) { + outputNum += sc2.read(output); + } + + result = new String(output.array()); + if(!result.equals(message)) + throw new RuntimeException("Test Failed!"); + sc2.shutdownInput(); + sc2.shutdownOutput(); + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException("Test Failed!"); + } finally { + ssc.close(); + sc1.close(); + sc2.close(); + } + + //test SocketChannel.socket() and ServerSocketChannel.socket() + try { + ssc = Sockets.openRdmaServerSocketChannel(); + sc1 = Sockets.openRdmaSocketChannel(); + + ssc.socket().bind(new InetSocketAddress(iaddr, port)); + + Thread t = new Thread(new BasicSocketChannelTest(), "Socket"); + t.start(); + + sc2 = ssc.accept(); + Socket conn = sc2.socket(); + InputStream is = conn.getInputStream(); + + int num = 0; + byte[] buf = new byte[length]; + while (num < length) { + int l = is.read(buf); + num += l; + } + + result = new String(buf); + if(!result.equals(message)) + throw new RuntimeException("Test Failed!"); + conn.shutdownInput(); + conn.shutdownOutput(); + if (!conn.isInputShutdown() || !conn.isOutputShutdown()) + throw new RuntimeException("Test Failed!"); + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException("Test Failed!"); + } finally { + ssc.close(); + sc1.close(); + sc2.close(); + } + } + + public void run() { + int port = ssc.socket().getLocalPort(); + byte[] arr = new byte[length]; + arr = message.getBytes(); + + try { + if (Thread.currentThread().getName().startsWith("Channel")) { + sc1.connect(new InetSocketAddress(iaddr, port)); + if (!sc1.isConnected()) + throw new RuntimeException("Test Failed!"); + ByteBuffer input = ByteBuffer.allocate(length); + input.put(arr); + input.flip(); + int inputNum = 0; + while (inputNum < length) { + inputNum += sc1.write(input); + } + sc1.shutdownInput(); + sc1.shutdownOutput(); + } else if (Thread.currentThread().getName().startsWith("Socket")) { + Socket client = sc1.socket(); + client.connect(new InetSocketAddress(iaddr, port)); + if (!client.isConnected()) + throw new RuntimeException("Test Failed!"); + OutputStream os = client.getOutputStream(); + os.write(arr); + client.shutdownInput(); + client.shutdownOutput(); + if (!client.isInputShutdown() || !client.isOutputShutdown()) + throw new RuntimeException("Test Failed!"); + } + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException("Test Failed!"); + } + } +} --- /dev/null 2018-09-16 08:14:05.604314476 -0700 +++ new/test/jdk/jdk/net/Sockets/rsocket/SocketChannel/CloseDuringWrite.java 2018-10-05 14:15:08.754757331 -0700 @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 8195160 + * @summary Test asynchronous close during a blocking write + * @requires (os.family == "linux") + * @library .. /test/lib + * @build RsocketTest + * @run main/othervm -Djava.net.preferIPv4Stack=true CloseDuringWrite + * @key randomness + */ + +import java.io.Closeable; +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.nio.ByteBuffer; +import java.nio.channels.ClosedChannelException; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; +import java.util.concurrent.Callable; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.Random; +import jdk.net.Sockets; + +public class CloseDuringWrite { + + static final Random rand = new Random(); + static ServerSocketChannel ssc; + static SocketChannel source; + static SocketAddress sa; + + /** + * A task that closes a Closeable + */ + static class Closer implements Callable { + final Closeable c; + Closer(Closeable c) { + this.c = c; + } + public Void call() throws IOException { + c.close(); + return null; + } + } + + public static void main(String[] args) throws Exception { + if (!RsocketTest.isRsocketAvailable()) + return; + + ScheduledExecutorService pool = Executors.newSingleThreadScheduledExecutor(); + try { + try { + ssc = Sockets.openRdmaServerSocketChannel(); + InetAddress lh = InetAddress.getLocalHost(); + ssc.bind(new InetSocketAddress(lh, 0)); + int port = ssc.socket().getLocalPort(); + sa = new InetSocketAddress(lh, port); + + ByteBuffer bb = ByteBuffer.allocate(2 * 1024 * 1024); + + for (int i = 0; i < 20; i++) { + try { + source = Sockets.openRdmaSocketChannel(); + Runnable runnable = new Runnable() { + @Override + public void run() { + try { + source.connect(sa); + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException("Test Failed"); + } + } + }; + + Thread t = new Thread(runnable); + t.start(); + SocketChannel sink = ssc.accept(); + // schedule channel to be closed + Closer c = new Closer(source); + int when = 1000 + rand.nextInt(2000); + Future result = pool.schedule(c, when, TimeUnit.MILLISECONDS); + + // the write should either succeed or else throw a + // ClosedChannelException (more likely an + // AsynchronousCloseException) + try { + for (;;) { + int limit = rand.nextInt(bb.capacity()); + bb.position(0); + bb.limit(limit); + int n = source.write(bb); + System.out.format("wrote %d, expected %d%n", n, limit); + } + } catch (ClosedChannelException expected) { + System.out.println(expected + " (expected)"); + } finally { + result.get(); + } + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException("Test Failed"); + } + } + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException("Test Failed"); + } + } finally { + pool.shutdown(); + } + } +} --- /dev/null 2018-09-16 08:14:05.604314476 -0700 +++ new/test/jdk/jdk/net/Sockets/rsocket/SocketChannel/Connect.java 2018-10-05 14:15:09.339757338 -0700 @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 8195160 + * @summary Unit test for socket channels + * @requires (os.family == "linux") + * @library .. /test/lib + * @build jdk.test.lib.Utils TestServers + * @build RsocketTest + * @run main/othervm -Djava.net.preferIPv4Stack=true Connect + */ + +import java.net.ConnectException; +import java.net.InetSocketAddress; +import java.nio.ByteBuffer; +import java.nio.channels.Selector; +import java.nio.channels.SelectionKey; +import java.nio.channels.SocketChannel; +import java.util.Set; +import jdk.net.Sockets; + +public class Connect { + + private static final long INCREMENTAL_DELAY = 30L * 1000L; + + public static void main(String args[]) throws Exception { + if (!RsocketTest.isRsocketAvailable()) + return; + + try (TestServers.EchoServer echoServer + = TestServers.EchoServer.startNewServer(1000)) { + test1(echoServer); + } + try { + test1(TestServers.RefusingServer.newRefusingServer()); + throw new Exception("Refused connection throws no exception"); + } catch (ConnectException ce) { + // Correct result + } + } + + static void test1(TestServers.AbstractServer server) throws Exception { + Selector selector; + SocketChannel sc; + SelectionKey sk; + InetSocketAddress isa = new InetSocketAddress( + server.getAddress(), server.getPort()); + sc = Sockets.openRdmaSocketChannel(); + sc.configureBlocking(false); + + selector = Sockets.openRdmaSelector(); + sk = sc.register(selector, SelectionKey.OP_CONNECT); + if (sc.connect(isa)) { + System.err.println("Connected immediately!"); + sc.close(); + selector.close(); + return; + } else { + ByteBuffer buf = ByteBuffer.allocateDirect(100); + buf.asCharBuffer().put(new String( + "The quick brown fox jumped over the lazy dog." + ).toCharArray()); + buf.flip(); + long startTime = System.currentTimeMillis(); + while(true) { + selector.select(INCREMENTAL_DELAY); + Set selectedKeys = selector.selectedKeys(); + + if(selectedKeys.isEmpty()) { + System.err.println("Elapsed time without response: " + + (System.currentTimeMillis() - + startTime) / 1000L + " seconds."); + } + else if(!selectedKeys.contains(sk)) + { + System.err.println("Got wrong event about selection key."); + } else { + System.err.println("Got event for our selection key."); + if(sk.isConnectable()) { + if(sc.finishConnect()) { + if(sc.isConnected()) { + System.err.println("Successful connect."); + sk.interestOps(SelectionKey.OP_WRITE); + sc.write(buf); + } else { + System.err.println( + "Finish connect completed incorrectly."); + } + } else { + System.err.println( + "key incorrectly indicated socket channel connectable."); + } + } + if(sk.isWritable() && (buf.remaining() > 0)) { + sc.write(buf); + } + if(buf.remaining() == 0) { + System.err.println( + "SUCCESS! buffer contents were sent."); + sc.close(); + selector.close(); + return; + } + } + selectedKeys.clear(); + } + } + } +} --- /dev/null 2018-09-16 08:14:05.604314476 -0700 +++ new/test/jdk/jdk/net/Sockets/rsocket/SocketChannel/FinishConnect.java 2018-10-05 14:15:09.899757345 -0700 @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 8195160 + * @summary Test RDMA SocketChannel.finishConnect + * @requires (os.family == "linux") + * @library .. /test/lib + * @build jdk.test.lib.Utils TestServers + * @build RsocketTest + * @run main/othervm -Djava.net.preferIPv4Stack=true FinishConnect + */ + +import java.net.InetSocketAddress; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.channels.Selector; +import java.nio.channels.SelectionKey; +import java.nio.channels.SocketChannel; +import java.nio.channels.spi.SelectorProvider; +import java.nio.charset.Charset; +import java.util.Iterator; +import java.util.Set; +import jdk.net.Sockets; + +public class FinishConnect { + + public static void main(String[] args) throws Exception { + if (!RsocketTest.isRsocketAvailable()) + return; + + try (TestServers.DayTimeServer dayTimeServer + = TestServers.DayTimeServer.startNewServer(100)) { + test1(dayTimeServer, true, true); + test1(dayTimeServer, true, false); + test1(dayTimeServer, false, true); + test1(dayTimeServer, false, false); + test2(dayTimeServer); + } + } + + static void test1(TestServers.DayTimeServer daytimeServer, + boolean select, + boolean setBlocking) + throws Exception + { + InetSocketAddress isa + = new InetSocketAddress(daytimeServer.getAddress(), + daytimeServer.getPort()); + SocketChannel sc = Sockets.openRdmaSocketChannel(); + sc.configureBlocking(false); + boolean connected = sc.connect(isa); + int attempts = 0; + + try { + sc.connect(isa); + throw new RuntimeException("Allowed another connect call"); + } catch (IllegalStateException ise) { + // Correct behavior + } + + if (setBlocking) + sc.configureBlocking(true); + + if (!connected && select && !setBlocking) { + Selector selector = Sockets.openRdmaSelector(); + sc.register(selector, SelectionKey.OP_CONNECT); + while (!connected) { + int keysAdded = selector.select(100); + if (keysAdded > 0) { + Set readyKeys = selector.selectedKeys(); + Iterator i = readyKeys.iterator(); + while (i.hasNext()) { + SelectionKey sk = (SelectionKey)i.next(); + SocketChannel nextReady = + (SocketChannel)sk.channel(); + connected = sc.finishConnect(); + } + } + } + selector.close(); + } + + while (!connected) { + if (attempts++ > 30) + throw new RuntimeException("Failed to connect"); + Thread.sleep(100); + connected = sc.finishConnect(); + } + + ByteBuffer bb = ByteBuffer.allocateDirect(100); + int bytesRead = 0; + int totalRead = 0; + while (totalRead < 20) { + bytesRead = sc.read(bb); + if (bytesRead > 0) + totalRead += bytesRead; + if (bytesRead < 0) + throw new RuntimeException("Message shorter than expected"); + } + bb.position(bb.position() - 2); // Drop CRLF + bb.flip(); + CharBuffer cb = Charset.forName("US-ASCII").newDecoder().decode(bb); + System.err.println(isa + " says: \"" + cb + "\""); + sc.close(); + } + + static void test2(TestServers.DayTimeServer daytimeServer) throws Exception { + InetSocketAddress isa + = new InetSocketAddress(daytimeServer.getAddress(), + daytimeServer.getPort()); + boolean done = false; + int globalAttempts = 0; + int connectSuccess = 0; + while (!done) { + // When using a local daytime server it is not always possible + // to get a pending connection, as sc.connect(isa) may always + // return true. + // So we're going to throw the exception only if there was + // at least 1 case where we did not manage to connect. + if (globalAttempts++ > 50) { + if (globalAttempts == connectSuccess + 1) { + System.out.println("Can't fully test on " + + System.getProperty("os.name")); + break; + } + throw new RuntimeException("Failed to connect"); + } + SocketChannel sc = Sockets.openRdmaSocketChannel(); + sc.configureBlocking(false); + boolean connected = sc.connect(isa); + int localAttempts = 0; + while (!connected) { + if (localAttempts++ > 500) + throw new RuntimeException("Failed to connect"); + connected = sc.finishConnect(); + if (connected) { + done = true; + break; + } + Thread.sleep(10); + } + if (connected) { + connectSuccess++; + } + sc.close(); + } + } + +} --- /dev/null 2018-09-16 08:14:05.604314476 -0700 +++ new/test/jdk/jdk/net/Sockets/rsocket/SocketChannel/SocketOptionTests.java 2018-10-05 14:15:10.495757352 -0700 @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 8195160 + * @summary Unit test to check SocketChannel setOption/getOption/options + * methods. + * @requires !vm.graal.enabled + * @requires (os.family == "linux") + * @library .. + * @build RsocketTest + * @run main/othervm -Djava.net.preferIPv4Stack=true SocketOptionTests + */ + +import java.nio.channels.*; +import java.net.*; +import java.io.IOException; +import java.util.*; +import jdk.net.Sockets; +import static java.net.StandardSocketOptions.*; +import static jdk.net.RdmaSocketOptions.*; + +public class SocketOptionTests { + + static void checkOption(SocketChannel sc, SocketOption name, Object expectedValue) + throws IOException + { + Object value = sc.getOption(name); + if (!value.equals(expectedValue)) + throw new RuntimeException("value not as expected"); + } + + public static void main(String[] args) throws IOException { + if (!RsocketTest.isRsocketAvailable()) + return; + + SocketChannel sc = Sockets.openRdmaSocketChannel(); + + // check supported options + Set> options = sc.supportedOptions(); + + List rdmaOptions = List.of(RDMA_SQSIZE, + RDMA_RQSIZE, RDMA_INLINE); + List expected; + expected = Arrays.asList(SO_SNDBUF, SO_RCVBUF, SO_REUSEADDR, + TCP_NODELAY, RDMA_SQSIZE, RDMA_RQSIZE, RDMA_INLINE); + for (SocketOption opt: expected) { + if (!options.contains(opt)) + throw new RuntimeException(opt.name() + " should be supported"); + } + + // check specified defaults + checkOption(sc, TCP_NODELAY, false); + + // allowed to change when not bound + sc.setOption(SO_SNDBUF, 128*1024); // can't check + sc.setOption(SO_RCVBUF, 256*1024); // can't check + int before, after; + before = sc.getOption(SO_SNDBUF); + after = sc.setOption(SO_SNDBUF, Integer.MAX_VALUE).getOption(SO_SNDBUF); + if (after < before) + throw new RuntimeException("setOption caused SO_SNDBUF to decrease"); + before = sc.getOption(SO_RCVBUF); + after = sc.setOption(SO_RCVBUF, Integer.MAX_VALUE).getOption(SO_RCVBUF); + if (after < before) + throw new RuntimeException("setOption caused SO_RCVBUF to decrease"); + sc.setOption(SO_REUSEADDR, true); + checkOption(sc, SO_REUSEADDR, true); + sc.setOption(SO_REUSEADDR, false); + checkOption(sc, SO_REUSEADDR, false); + sc.setOption(TCP_NODELAY, true); + checkOption(sc, TCP_NODELAY, true); + sc.setOption(TCP_NODELAY, false); // can't check + + sc.setOption(RDMA_SQSIZE, 1024); + checkOption(sc, RDMA_SQSIZE, 1024); + + sc.setOption(RDMA_RQSIZE, 1024); + checkOption(sc, RDMA_RQSIZE, 1024); + + sc.setOption(RDMA_INLINE, 512); + checkOption(sc, RDMA_INLINE, 512); + + // bind socket + sc.bind(new InetSocketAddress(0)); + + sc.setOption(TCP_NODELAY, true); // can't check + sc.setOption(TCP_NODELAY, false); // can't check + // NullPointerException + try { + sc.setOption(null, "value"); + throw new RuntimeException("NullPointerException not thrown"); + } catch (NullPointerException x) { + } + try { + sc.getOption(null); + throw new RuntimeException("NullPointerException not thrown"); + } catch (NullPointerException x) { + } + + // ClosedChannelException + sc.close(); + try { + sc.setOption(TCP_NODELAY, true); + throw new RuntimeException("ClosedChannelException not thrown"); + } catch (ClosedChannelException x) { + } + } +} --- /dev/null 2018-09-16 08:14:05.604314476 -0700 +++ new/test/jdk/jdk/net/Sockets/rsocket/SocketChannel/Stream.java 2018-10-05 14:15:11.173757360 -0700 @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 8195160 + * @summary Test result of read on stream from nonblocking channel + * @requires (os.family == "linux") + * @library .. /test/lib + * @build jdk.test.lib.Utils TestServers + * @build RsocketTest + * @run main/othervm -Djava.net.preferIPv4Stack=true Stream + */ + +import java.io.InputStream; +import java.net.InetSocketAddress; +import java.nio.channels.IllegalBlockingModeException; +import java.nio.channels.SocketChannel; +import jdk.net.Sockets; + +public class Stream { + + static void test(TestServers.DayTimeServer daytimeServer) throws Exception { + InetSocketAddress isa + = new InetSocketAddress(daytimeServer.getAddress(), + daytimeServer.getPort()); + SocketChannel sc = Sockets.openRdmaSocketChannel(); + sc.connect(isa); + sc.configureBlocking(false); + InputStream is = sc.socket().getInputStream(); + byte b[] = new byte[10]; + try { + int n = is.read(b); + throw new RuntimeException("Exception expected; none thrown"); + } catch (IllegalBlockingModeException e) { + // expected result + } + sc.close(); + } + + public static void main(String[] args) throws Exception { + if (!RsocketTest.isRsocketAvailable()) + return; + + try (TestServers.DayTimeServer dayTimeServer + = TestServers.DayTimeServer.startNewServer(100)) { + test(dayTimeServer); + } + } +} --- /dev/null 2018-09-16 08:14:05.604314476 -0700 +++ new/test/jdk/jdk/net/Sockets/rsocket/SocketChannel/VectorIO.java 2018-10-05 14:15:11.802757368 -0700 @@ -0,0 +1,228 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 8195160 + * @summary Test socketchannel vector IO (use -Dseed=X to set PRNG seed) + * @requires (os.family == "linux") + * @library .. /test/lib /test/jdk/java/nio/channels + * @build jdk.test.lib.RandomFactory + * @build RsocketTest + * @run main/othervm -Djava.net.preferIPv4Stack=true VectorIO + * @key randomness + */ + +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.nio.ByteBuffer; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; +import java.util.Random; +import jdk.test.lib.RandomFactory; +import jdk.net.Sockets; + +public class VectorIO { + + private static Random generator = RandomFactory.getRandom(); + + static int testSize; + + // whether to use the write/read variant with a length parameter + static boolean setLength; + + public static void main(String[] args) throws Exception { + if (!RsocketTest.isRsocketAvailable()) + return; + + testSize = 1; + setLength = false; + runTest(); + for(int i = 15; i < 18; i++) { + testSize = i; + setLength = !setLength; + runTest(); + } + } + + static void runTest() throws Exception { + System.err.println("Length " + testSize); + Server sv = new Server(testSize); + sv.start(); + bufferTest(sv.port()); + if (sv.finish(8000) == 0) + throw new Exception("Failed: Length = " + testSize); + } + + static void bufferTest(int port) throws Exception { + ByteBuffer[] bufs = new ByteBuffer[testSize]; + long total = 0L; + for(int i = 0; i < testSize; i++) { + String source = "buffer" + i; + if (generator.nextBoolean()) + bufs[i] = ByteBuffer.allocateDirect(source.length()); + else + bufs[i] = ByteBuffer.allocate(source.length()); + + bufs[i].put(source.getBytes("8859_1")); + bufs[i].flip(); + total += bufs[i].remaining(); + } + + ByteBuffer[] bufsPlus1 = new ByteBuffer[bufs.length + 1]; + System.arraycopy(bufs, 0, bufsPlus1, 0, bufs.length); + + // Get a connection to the server + InetAddress lh = InetAddress.getLocalHost(); + InetSocketAddress isa = new InetSocketAddress(lh, port); + SocketChannel sc = Sockets.openRdmaSocketChannel(); + sc.connect(isa); + sc.configureBlocking(generator.nextBoolean()); + + // Write the data out + long rem = total; + while (rem > 0L) { + long bytesWritten; + if (setLength) { + bytesWritten = sc.write(bufsPlus1, 0, bufs.length); + } else { + bytesWritten = sc.write(bufs); + } + if (bytesWritten == 0) { + if (sc.isBlocking()) { + throw new RuntimeException("write did not block"); + } else { + System.err.println("Non-blocking write() wrote zero bytes"); + } + Thread.sleep(50); + } else { + rem -= bytesWritten; + } + } + + // Clean up + sc.close(); + } + + static class Server + extends TestThread + { + final int testSize; + final ServerSocketChannel ssc; + + Server(int testSize) throws IOException { + super("Server " + testSize); + this.testSize = testSize; + this.ssc = Sockets.openRdmaServerSocketChannel().bind(new InetSocketAddress(0)); + } + + int port() { + return ssc.socket().getLocalPort(); + } + + void go() throws Exception { + bufferTest(); + } + + void bufferTest() throws Exception { + long total = 0L; + ByteBuffer[] bufs = new ByteBuffer[testSize]; + for(int i=0; i 0) { + long bytesRead; + if (setLength) { + bytesRead = sc.read(bufsPlus1, 0, bufs.length); + } else { + bytesRead = sc.read(bufs); + } + if (bytesRead < 0) + break; + if (bytesRead == 0) { + if (sc.isBlocking()) { + throw new RuntimeException("read did not block"); + } else { + System.err.println + ("Non-blocking read() read zero bytes"); + } + Thread.sleep(50); + } + avail -= bytesRead; + } + + // Check results + for(int i=0; i option, Object testValue) { + this.option = option; + this.testValue = testValue; + } + static Test create (SocketOption option, Object testValue) { + return new Test(option, testValue); + } + Object option; + Object testValue; + } + + // The tests set the option using the new API, read back the set value + // which could be diferent, and then use the legacy get API to check + // these values are the same + + static Test[] socketTests = new Test[] { + Test.create(StandardSocketOptions.SO_SNDBUF, Integer.valueOf(10 * 100)), + Test.create(StandardSocketOptions.SO_RCVBUF, Integer.valueOf(8 * 100)), + Test.create(StandardSocketOptions.SO_REUSEADDR, Boolean.FALSE), + }; + + static Test[] serverSocketTests = new Test[] { + Test.create(StandardSocketOptions.SO_RCVBUF, Integer.valueOf(8 * 100)), + Test.create(StandardSocketOptions.SO_REUSEADDR, Boolean.FALSE), + }; + + static Test[] rdmaSocketOptionTests = new Test[] { + Test.create(RdmaSocketOptions.RDMA_SQSIZE, Integer.valueOf(8 * 100)), + Test.create(RdmaSocketOptions.RDMA_RQSIZE, Integer.valueOf(10 * 100)), + Test.create(RdmaSocketOptions.RDMA_INLINE, Integer.valueOf(20 * 100)), + }; + + static void doSocketTests() throws Exception { + try { + Socket c = Sockets.openRdmaSocket(); + + for (int i = 0; i < socketTests.length; i++) { + Test test = socketTests[i]; + c.setOption((SocketOption)test.option, test.testValue); + Object getval = c.getOption((SocketOption)test.option); + Object legacyget = legacyGetOption(Socket.class, c, test.option); + if (!getval.equals(legacyget)) { + Formatter f = new Formatter(); + f.format("S Err %d: %s/%s", i, getval, legacyget); + throw new RuntimeException(f.toString()); + } + } + + for (int i = 0; i < rdmaSocketOptionTests.length; i++) { + Test test = rdmaSocketOptionTests[i]; + c.setOption((SocketOption)test.option, test.testValue); + Object getval = c.getOption((SocketOption)test.option); + if (((Integer)getval).intValue() != + ((Integer)test.testValue).intValue()) + throw new RuntimeException("Test Failed"); + } + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException("Test Failed"); + } + } + + static void doServerSocketTests() throws Exception { + try { + ServerSocket srv = Sockets.openRdmaServerSocket(); + for (int i = 0; i < serverSocketTests.length; i++) { + Test test = serverSocketTests[i]; + srv.setOption((SocketOption)test.option, test.testValue); + Object getval = srv.getOption((SocketOption)test.option); + Object legacyget = legacyGetOption( + ServerSocket.class, srv, test.option + ); + if (!getval.equals(legacyget)) { + Formatter f = new Formatter(); + f.format("SS Err %d: %s/%s", i, getval, legacyget); + throw new RuntimeException(f.toString()); + } + } + + for (int i = 0; i < rdmaSocketOptionTests.length; i++) { + Test test = rdmaSocketOptionTests[i]; + srv.setOption((SocketOption)test.option, test.testValue); + Object getval = srv.getOption((SocketOption)test.option); + if (((Integer)getval).intValue() != + ((Integer)test.testValue).intValue()) + throw new RuntimeException("Test Failed"); + } + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException("Test Failed"); + } + } + + static Object legacyGetOption( + Class type, Object s, Object option) + + throws Exception + { + if (type.equals(Socket.class)) { + Socket socket = (Socket)s; + Set> options = socket.supportedOptions(); + + if (option.equals(StandardSocketOptions.SO_SNDBUF)) { + return Integer.valueOf(socket.getSendBufferSize()); + } else if (option.equals(StandardSocketOptions.SO_RCVBUF)) { + return Integer.valueOf(socket.getReceiveBufferSize()); + } else if (option.equals(StandardSocketOptions.SO_REUSEADDR)) { + return Boolean.valueOf(socket.getReuseAddress()); + } else if (option.equals(StandardSocketOptions.TCP_NODELAY)) { + return Boolean.valueOf(socket.getTcpNoDelay()); + } else { + throw new RuntimeException("unexecpted socket option"); + } + } else if (type.equals(ServerSocket.class)) { + ServerSocket socket = (ServerSocket)s; + Set> options = socket.supportedOptions(); + + if (option.equals(StandardSocketOptions.SO_RCVBUF)) { + return Integer.valueOf(socket.getReceiveBufferSize()); + } else if (option.equals(StandardSocketOptions.SO_REUSEADDR)) { + return Boolean.valueOf(socket.getReuseAddress()); + } else { + throw new RuntimeException("unexecpted socket option"); + } + } + throw new RuntimeException("unexecpted socket type"); + } + + public static void main(String args[]) throws Exception { + if (!RsocketTest.isRsocketAvailable()) + return; + + doSocketTests(); + doServerSocketTests(); + } +} --- /dev/null 2018-09-16 08:14:05.604314476 -0700 +++ new/test/jdk/jdk/net/Sockets/rsocket/SocketOption/UnsupportedOptionsTest.java 2018-10-05 14:15:13.016757382 -0700 @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.io.IOException; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.SocketOption; +import java.net.StandardSocketOptions; +import java.util.ArrayList; +import java.util.List; +import jdk.net.Sockets; +import jdk.net.RdmaSocketOptions; + +/* @test + * @bug 8195160 + * @summary Test checks that UnsupportedOperationException for unsupported + * SOCKET_OPTIONS is thrown by both getOption() and setOption() methods. + * @requires (os.family == "linux") + * @requires !vm.graal.enabled + * @library .. /test/lib + * @build RsocketTest + * @run main/othervm -Djava.net.preferIPv4Stack=true UnsupportedOptionsTest + */ +public class UnsupportedOptionsTest { + + private static final List> socketOptions = new ArrayList<>(); + + static { + socketOptions.add(StandardSocketOptions.SO_RCVBUF); + socketOptions.add(StandardSocketOptions.SO_REUSEADDR); + socketOptions.add(StandardSocketOptions.SO_SNDBUF); + socketOptions.add(StandardSocketOptions.TCP_NODELAY); + socketOptions.add(RdmaSocketOptions.RDMA_SQSIZE); + socketOptions.add(RdmaSocketOptions.RDMA_RQSIZE); + socketOptions.add(RdmaSocketOptions.RDMA_INLINE); + } + + public static void main(String[] args) throws IOException { + if (!RsocketTest.isRsocketAvailable()) + return; + + Socket s = Sockets.openRdmaSocket(); + ServerSocket ss = Sockets.openRdmaServerSocket(); + + for (SocketOption option : socketOptions) { + if (!s.supportedOptions().contains(option)) { + testUnsupportedSocketOption(s, option); + } + + if (!ss.supportedOptions().contains(option)) { + testUnsupportedSocketOption(ss, option); + } + } + } + + /* + * Check that UnsupportedOperationException for unsupported option is + * thrown from both getOption() and setOption() methods. + */ + private static void testUnsupportedSocketOption(Object socket, + SocketOption option) { + testSet(socket, option); + testGet(socket, option); + } + + private static void testSet(Object socket, SocketOption option) { + try { + setOption(socket, option); + } catch (UnsupportedOperationException e) { + System.out.println("UnsupportedOperationException was throw " + + "as expected. Socket: " + socket + " Option: " + option); + return; + } catch (Exception e) { + throw new RuntimeException("FAIL. Unexpected exception.", e); + } + throw new RuntimeException("FAIL. UnsupportedOperationException " + + "hasn't been thrown. Socket: " + socket + " Option: " + option); + } + + private static void testGet(Object socket, SocketOption option) { + try { + getOption(socket, option); + } catch (UnsupportedOperationException e) { + System.out.println("UnsupportedOperationException was throw " + + "as expected. Socket: " + socket + " Option: " + option); + return; + } catch (Exception e) { + throw new RuntimeException("FAIL. Unexpected exception.", e); + } + throw new RuntimeException("FAIL. UnsupportedOperationException " + + "hasn't been thrown. Socket: " + socket + " Option: " + option); + } + + private static void getOption(Object socket, + SocketOption option) throws IOException { + if (socket instanceof Socket) { + ((Socket) socket).getOption(option); + } else if (socket instanceof ServerSocket) { + ((ServerSocket) socket).getOption(option); + } else { + throw new RuntimeException("Unsupported socket type"); + } + } + + private static void setOption(Object socket, + SocketOption option) throws IOException { + if (socket instanceof Socket) { + ((Socket) socket).setOption(option, null); + } else if (socket instanceof ServerSocket) { + ((ServerSocket) socket).setOption(option, null); + } else { + throw new RuntimeException("Unsupported socket type"); + } + } +} --- /dev/null 2018-09-16 08:14:05.604314476 -0700 +++ new/test/jdk/jdk/net/Sockets/rsocket/SocketsTest.java 2018-10-05 14:15:13.663757390 -0700 @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8195160 + * @summary Test to check if UOE is thrown on non Linux platform with RDMA + * methods + * @requires (os.family == "windows" | os.family == "solaris" + * | os.family == "aix" | os.family == "macos") + * @library /test/lib + * @build jdk.test.lib.Platform + * @run main/native SocketsTest + */ + +import java.net.Socket; +import java.net.ServerSocket; +import java.nio.channels.Selector; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; +import jdk.net.Sockets; + +public class SocketsTest { + + public static void main(String[] args) throws Exception { + try { + Socket s = Sockets.openRdmaSocket(); + throw new RuntimeException("Expected exception not thrown"); + } catch (UnsupportedOperationException e) { + //correct result + } + + try { + ServerSocket s = Sockets.openRdmaServerSocket(); + throw new RuntimeException("Expected exception not thrown"); + } catch (UnsupportedOperationException e) { + //correct result + } + + try { + SocketChannel sc = Sockets.openRdmaSocketChannel(); + throw new RuntimeException("Expected exception not thrown"); + } catch (UnsupportedOperationException e) { + //correct result + } + + try { + ServerSocketChannel ssc = Sockets.openRdmaServerSocketChannel(); + throw new RuntimeException("Expected exception not thrown"); + } catch (UnsupportedOperationException e) { + //correct result + } + + try { + Selector sel = Sockets.openRdmaSelector(); + throw new RuntimeException("Expected exception not thrown"); + } catch (UnsupportedOperationException e) { + //correct result + } + } +} --- /dev/null 2018-09-16 08:14:05.604314476 -0700 +++ new/test/jdk/jdk/net/Sockets/rsocket/TestServers.java 2018-10-05 14:15:14.252757396 -0700 @@ -0,0 +1,496 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.io.Closeable; +import java.io.InputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import jdk.net.Sockets; + +import jdk.test.lib.Utils; + + +public class TestServers { + + private TestServers() { } + + /** + * An abstract server identifies a server which listens on a port on on a + * given machine. + */ + static abstract class AbstractServer { + + private AbstractServer() { + } + + public abstract int getPort(); + + public abstract InetAddress getAddress(); + } + + /** + * A downgraded type of AbstractServer which will refuse connections. Note: + * use it once and throw it away - this implementation opens an anonymous + * socket and closes it, returning the address of the closed socket. If + * other servers are started afterwards, the address/port might get reused + * and become connectable again - so it's not a good idea to assume that + * connections using this address/port will always be refused. Connections + * will be refused as long as the address/port of the refusing server has + * not been reused. + */ + static class RefusingServer extends AbstractServer { + + final InetAddress address; + final int port; + + private RefusingServer(InetAddress address, int port) { + this.address = address; + this.port = port; + } + + @Override + public int getPort() { + return port; + } + + @Override + public InetAddress getAddress() { + return address; + } + + public static RefusingServer newRefusingServer() throws IOException { + return new RefusingServer(InetAddress.getLocalHost(), + Utils.refusingEndpoint().getPort()); + } + } + + /** + * An abstract class for implementing small TCP servers for the nio tests + * purposes. Disclaimer: This is a naive implementation that uses the old + * networking APIs (not those from {@code java.nio.*}) and shamelessly + * extends/creates Threads instead of using an executor service. + */ + static abstract class AbstractTcpServer extends AbstractServer + implements Runnable, Closeable { + + protected final long linger; // #of ms to wait before responding + private Thread acceptThread; // thread waiting for accept + // list of opened connections that should be closed on close. + private List connections = new ArrayList<>(); + private ServerSocket serverSocket; // the server socket + private boolean started = false; // whether the server is started + Throwable error = null; + + /** + * Creates a new abstract TCP server. + * + * @param linger the amount of time the server should wait before + * responding to requests. + */ + protected AbstractTcpServer(long linger) { + this.linger = linger; + } + + /** + * The local port to which the server is bound. + * + * @return The local port to which the server is bound. + * @exception IllegalStateException is thrown if the server is not + * started. + */ + @Override + public final synchronized int getPort() { + if (!started) { + throw new IllegalStateException("Not started"); + } + return serverSocket.getLocalPort(); + } + + /** + * The local address to which the server is bound. + * + * @return The local address to which the server is bound. + * @exception IllegalStateException is thrown if the server is not + * started. + */ + @Override + public final synchronized InetAddress getAddress() { + if (!started) { + throw new IllegalStateException("Not started"); + } + return serverSocket.getInetAddress(); + } + + /** + * Tells whether the server is started. + * + * @return true if the server is started. + */ + public final synchronized boolean isStarted() { + return started; + } + + /** + * Creates a new server socket. + * + * @param port local port to bind to. + * @param backlog requested maximum length of the queue of incoming + * connections. + * @param address local address to bind to. + * @return a new bound server socket ready to accept connections. + * @throws IOException if the socket cannot be created or bound. + */ + protected ServerSocket newServerSocket(int port, int backlog, + InetAddress address) + throws IOException { + ServerSocket ss = Sockets.openRdmaServerSocket(); + ss.bind(new InetSocketAddress(address, port), backlog); + return ss; + } + + /** + * Starts listening for connections. + * + * @throws IOException if the server socket cannot be created or bound. + */ + public final synchronized void start() throws IOException { + if (started) { + return; + } + final ServerSocket socket = + newServerSocket(0, 100, InetAddress.getLocalHost()); + serverSocket = socket; + acceptThread = new Thread(this); + acceptThread.setDaemon(true); + acceptThread.start(); + started = true; + } + + /** + * Calls {@code Thread.sleep(linger);} + */ + protected final void lingerIfRequired() { + if (linger > 0) { + try { + Thread.sleep(linger); + } catch (InterruptedException x) { + Thread.interrupted(); + final ServerSocket socket = serverSocket(); + if (socket != null && !socket.isClosed()) { + System.err.println("Thread interrupted..."); + } + } + } + } + + final synchronized ServerSocket serverSocket() { + return this.serverSocket; + } + + /** + * The main accept loop. + */ + @Override + public final void run() { + final ServerSocket sSocket = serverSocket(); + try { + Socket s; + while (isStarted() && !Thread.interrupted() + && (s = sSocket.accept()) != null) { + lingerIfRequired(); + listen(s); + } + } catch (Exception x) { + error = x; + } finally { + synchronized (this) { + if (!sSocket.isClosed()) { + try { + sSocket.close(); + } catch (IOException x) { + System.err.println("Failed to close server socket"); + } + } + if (started && this.serverSocket == sSocket) { + started = false; + this.serverSocket = null; + this.acceptThread = null; + } + } + } + } + + /** + * Represents a connection accepted by the server. + */ + protected abstract class TcpConnectionThread extends Thread { + + protected final Socket socket; + + protected TcpConnectionThread(Socket socket) { + this.socket = socket; + this.setDaemon(true); + } + + public void close() throws IOException { + socket.close(); + interrupt(); + } + } + + /** + * Creates a new TcpConnnectionThread to handle the connection through + * an accepted socket. + * + * @param s the socket returned by {@code serverSocket.accept()}. + * @return a new TcpConnnectionThread to handle the connection through + * an accepted socket. + */ + protected abstract TcpConnectionThread createConnection(Socket s); + + /** + * Creates and starts a new TcpConnectionThread to handle the accepted + * socket. + * + * @param s the socket returned by {@code serverSocket.accept()}. + */ + private synchronized void listen(Socket s) { + TcpConnectionThread c = createConnection(s); + c.start(); + addConnection(c); + } + + /** + * Add the connection to the list of accepted connections. + * + * @param connection an accepted connection. + */ + protected synchronized void addConnection( + TcpConnectionThread connection) { + connections.add(connection); + } + + /** + * Remove the connection from the list of accepted connections. + * + * @param connection an accepted connection. + */ + protected synchronized void removeConnection( + TcpConnectionThread connection) { + connections.remove(connection); + } + + /** + * Close the server socket and all the connections present in the list + * of accepted connections. + * + * @throws IOException + */ + @Override + public synchronized void close() throws IOException { + if (serverSocket != null && !serverSocket.isClosed()) { + serverSocket.close(); + } + if (acceptThread != null) { + acceptThread.interrupt(); + } + int failed = 0; + for (TcpConnectionThread c : connections) { + try { + c.close(); + } catch (IOException x) { + // no matter - we're closing. + failed++; + } + } + connections.clear(); + if (failed > 0) { + throw new IOException("Failed to close some connections"); + } + } + } + + /** + * A small TCP Server that emulates the echo service for tests purposes. See + * http://en.wikipedia.org/wiki/Echo_Protocol This server uses an anonymous + * port - NOT the standard port 7. We don't guarantee that its behavior + * exactly matches the RFC - the only purpose of this server is to have + * something that responds to nio tests... + */ + static class EchoServer extends AbstractTcpServer { + + public EchoServer() { + this(0L); + } + + public EchoServer(long linger) { + super(linger); + } + + @Override + protected TcpConnectionThread createConnection(Socket s) { + return new EchoConnection(s); + } + + private final class EchoConnection extends TcpConnectionThread { + + public EchoConnection(Socket socket) { + super(socket); + } + + @Override + public void run() { + try { + final InputStream is = socket.getInputStream(); + final OutputStream out = socket.getOutputStream(); + byte[] b = new byte[255]; + int n; + while ((n = is.read(b)) > 0) { + lingerIfRequired(); + out.write(b, 0, n); + } + } catch (IOException io) { + // fall through to finally + } finally { + if (!socket.isClosed()) { + try { + socket.close(); + } catch (IOException x) { + System.err.println( + "Failed to close echo connection socket"); + } + } + removeConnection(this); + } + } + } + + public static EchoServer startNewServer() throws IOException { + return startNewServer(0); + } + + public static EchoServer startNewServer(long linger) throws IOException { + final EchoServer echoServer = new EchoServer(linger); + echoServer.start(); + return echoServer; + } + } + + + /** + * A small TCP Server that accept connections but does not response to any input. + */ + static final class NoResponseServer extends EchoServer { + public NoResponseServer() { + super(Long.MAX_VALUE); + } + + public static NoResponseServer startNewServer() throws IOException { + final NoResponseServer noResponseServer = new NoResponseServer(); + noResponseServer.start(); + return noResponseServer; + } + } + + + /** + * A small TCP server that emulates the Day & Time service for tests + * purposes. See http://en.wikipedia.org/wiki/Daytime_Protocol This server + * uses an anonymous port - NOT the standard port 13. We don't guarantee + * that its behavior exactly matches the RFC - the only purpose of this + * server is to have something that responds to nio tests... + */ + static final class DayTimeServer extends AbstractTcpServer { + + public DayTimeServer() { + this(0L); + } + + public DayTimeServer(long linger) { + super(linger); + } + + @Override + protected TcpConnectionThread createConnection(Socket s) { + return new DayTimeServerConnection(s); + } + + @Override + protected void addConnection(TcpConnectionThread connection) { + // do nothing - the connection just write the date and terminates. + } + + @Override + protected void removeConnection(TcpConnectionThread connection) { + // do nothing - we're not adding connections to the list... + } + + private final class DayTimeServerConnection extends TcpConnectionThread { + + public DayTimeServerConnection(Socket socket) { + super(socket); + } + + @Override + public void run() { + try { + final OutputStream out = socket.getOutputStream(); + lingerIfRequired(); + out.write(new Date(System.currentTimeMillis()) + .toString().getBytes("US-ASCII")); + out.flush(); + } catch (IOException io) { + // fall through to finally + } finally { + if (!socket.isClosed()) { + try { + socket.close(); + } catch (IOException x) { + System.err.println( + "Failed to close echo connection socket"); + } + } + } + } + } + + public static DayTimeServer startNewServer() + throws IOException { + return startNewServer(0); + } + + public static DayTimeServer startNewServer(long linger) + throws IOException { + final DayTimeServer daytimeServer = new DayTimeServer(linger); + daytimeServer.start(); + return daytimeServer; + } + } +} --- /dev/null 2018-09-16 08:14:05.604314476 -0700 +++ new/test/jdk/jdk/net/Sockets/rsocket/libRsocketTest.c 2018-10-05 14:15:14.890757404 -0700 @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * Test if rsocket is available + */ +#include +#ifdef __linux__ +#include +#include +#endif +#include "jni.h" + +/* + * Class: RsocketTest + * Method: isRsocketAvailable0 + * Signature: ()Z + */ +JNIEXPORT jboolean Java_RsocketTest_isRsocketAvailable0(JNIEnv *env, jclass cls) { + jboolean result = JNI_FALSE; +#ifdef __linux__ + void *handle; + int (*rs)(int, int, int); + + handle = dlopen("librdmacm.so", RTLD_LAZY); + if (!handle) + return JNI_FALSE; + + rs = dlsym(handle, "rsocket"); + if (!rs) + return JNI_FALSE; + + if ((*rs)(AF_INET, SOCK_STREAM, 0) > 0) + result = JNI_TRUE; + + dlclose(handle); +#endif + return result; +}