--- old/make/gensrc/GensrcMisc.gmk 2018-05-03 16:52:19.461493092 -0700 +++ new/make/gensrc/GensrcMisc.gmk 2018-05-03 16:52:19.232493107 -0700 @@ -81,6 +81,17 @@ GENSRC_JAVA_BASE += $(GENSRC_SOR_FILE) +ifeq ($(OPENJDK_TARGET_OS), linux) + GENSRC_RSOR_FILE += $(SUPPORT_OUTPUTDIR)/gensrc/jdk.net/rdma/ch/RdmaSocketOptionRegistry.java + + $(GENSRC_RSOR_FILE): \ + $(TOPDIR)/src/jdk.net/share/classes/rdma/ch/RdmaSocketOptionRegistry.java.template + $(generate-preproc-src) + + GENSRC_JAVA_BASE += $(GENSRC_RSOR_FILE) + +endif + ################################################################################ ifneq ($(OPENJDK_TARGET_OS), windows) --- old/make/lib/Lib-java.base.gmk 2018-05-03 16:52:20.126493049 -0700 +++ new/make/lib/Lib-java.base.gmk 2018-05-03 16:52:19.907493064 -0700 @@ -57,7 +57,7 @@ $(call SET_SHARED_LIBRARY_ORIGIN), \ LDFLAGS_windows := -delayload:secur32.dll -delayload:iphlpapi.dll, \ LIBS_unix := -ljvm -ljava, \ - LIBS_linux := $(LIBDL) -lpthread, \ + LIBS_linux := $(LIBDL) -lpthread -lrdmacm, \ LIBS_solaris := -lnsl -lsocket $(LIBDL), \ LIBS_aix := $(LIBDL),\ LIBS_windows := ws2_32.lib jvm.lib secur32.lib iphlpapi.lib winhttp.lib \ @@ -100,7 +100,7 @@ LDFLAGS := $(LDFLAGS_JDKLIB) \ $(call SET_SHARED_LIBRARY_ORIGIN), \ LIBS_unix := -ljava -lnet, \ - LIBS_linux := -lpthread $(LIBDL), \ + LIBS_linux := -lpthread $(LIBDL) -lrdmacm, \ LIBS_solaris := -ljvm -lsocket -lposix4 $(LIBDL) \ -lsendfile, \ LIBS_aix := $(LIBDL), \ --- old/make/lib/Lib-jdk.net.gmk 2018-05-03 16:52:20.962492995 -0700 +++ new/make/lib/Lib-jdk.net.gmk 2018-05-03 16:52:20.735493010 -0700 @@ -26,24 +26,23 @@ include LibCommon.gmk ################################################################################ - ifneq ($(filter $(OPENJDK_TARGET_OS), solaris linux), ) $(eval $(call SetupJdkLibrary, BUILD_LIBEXTNET, \ NAME := extnet, \ - SRC := $(TOPDIR)/src/jdk.net/$(OPENJDK_TARGET_OS)/native/libextnet, \ + SRC := $(TOPDIR)/src/jdk.net/linux/native/libextnet, \ OPTIMIZATION := LOW, \ - CFLAGS := $(CFLAGS_JDKLIB) -I$(SUPPORT_OUTPUTDIR)/headers/jdk.net, \ + CFLAGS := $(CFLAGS_JDKLIB) -I$(SUPPORT_OUTPUTDIR)/headers/java.base -I$(SUPPORT_OUTPUTDIR)/headers/jdk.net \ + -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 \ + $(LIBJAVA_HEADER_FLAGS) $(addprefix -I, $(call FindSrcDirsForLib, java.base, net, nio)), \ LDFLAGS := $(LDFLAGS_JDKLIB) \ $(call SET_SHARED_LIBRARY_ORIGIN), \ - LIBS := -ljava, \ - LIBS_solaris := -lsocket, \ - LIBS_linux := -ljvm, \ + LIBS := -lc -ljava -lnet -lnio -lpthread -ljvm -lrdmacm, \ + OBJECT_DIR := $(SUPPORT_OUTPUTDIR)/native/$(MODULE)/libextnet, \ )) $(BUILD_LIBEXTNET): $(call FindLib, java.base, java) TARGETS += $(BUILD_LIBEXTNET) endif - ################################################################################ --- old/src/java.base/share/classes/java/net/AbstractPlainSocketImpl.java 2018-05-03 16:52:21.653492951 -0700 +++ new/src/java.base/share/classes/java/net/AbstractPlainSocketImpl.java 2018-05-03 16:52:21.403492967 -0700 @@ -494,14 +494,6 @@ return socketOutputStream; } - void setFileDescriptor(FileDescriptor fd) { - this.fd = fd; - } - - void setAddress(InetAddress address) { - this.address = address; - } - void setPort(int port) { this.port = port; } --- old/src/java.base/share/classes/java/net/ServerSocket.java 2018-05-03 16:52:22.375492904 -0700 +++ new/src/java.base/share/classes/java/net/ServerSocket.java 2018-05-03 16:52:22.165492918 -0700 @@ -76,10 +76,11 @@ 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 */ - ServerSocket(SocketImpl impl) { + protected ServerSocket(SocketImpl impl) { this.impl = impl; impl.setServerSocket(this); } @@ -515,7 +516,20 @@ throw new SocketException("Socket is closed"); if (!isBound()) throw new SocketException("Socket is not bound yet"); - Socket s = new Socket((SocketImpl) null); + + Socket s = null; + Class cls = impl.getClass(); + if (cls.getName().contains("RdmaSocketImpl")) { + try { + Constructor c = cls.getConstructor(); + s = new Socket((SocketImpl)c.newInstance()); + } catch (NoSuchMethodException | InstantiationException | + IllegalAccessException | InvocationTargetException e) { + throw new AssertionError(e); + } + } else { + s = new Socket((SocketImpl) null); + } implAccept(s); return s; } @@ -546,8 +560,8 @@ } si = s.impl; s.impl = null; - si.address = new InetAddress(); - si.fd = new FileDescriptor(); + si.setAddress(new InetAddress()); + si.setFileDescriptor(new FileDescriptor()); getImpl().accept(si); SocketCleanable.register(si.fd); // raw fd has been set --- old/src/java.base/share/classes/java/net/SocketImpl.java 2018-05-03 16:52:23.149492854 -0700 +++ new/src/java.base/share/classes/java/net/SocketImpl.java 2018-05-03 16:52:22.962492866 -0700 @@ -228,6 +228,15 @@ } /** + * Sets the value of this socket's {@code fd} field. + * + * @param fd the value of the fd + */ + protected void setFileDescriptor(FileDescriptor fd) { + this.fd = fd; + } + + /** * Returns the value of this socket's {@code address} field. * * @return the value of this socket's {@code address} field. @@ -238,6 +247,15 @@ } /** + * Sets the value of this socket's {@code address} field. + * + * @param address the value of the address + */ + protected void setAddress(InetAddress address) { + this.address = address; + } + + /** * Returns the value of this socket's {@code port} field. * * @return the value of this socket's {@code port} field. --- old/src/java.base/share/classes/module-info.java 2018-05-03 16:52:23.911492805 -0700 +++ new/src/java.base/share/classes/module-info.java 2018-05-03 16:52:23.723492817 -0700 @@ -209,7 +209,8 @@ jdk.jartool; 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 @@ -229,7 +230,8 @@ java.management, jdk.crypto.cryptoki, jdk.sctp, - jdk.unsupported; + jdk.unsupported, + jdk.net; exports sun.nio.cs to jdk.charsets; exports sun.nio.fs to @@ -248,7 +250,8 @@ java.xml; 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 --- old/src/java.base/share/classes/sun/nio/ch/Net.java 2018-05-03 16:52:24.718492753 -0700 +++ new/src/java.base/share/classes/sun/nio/ch/Net.java 2018-05-03 16:52:24.488492768 -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; @@ -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/share/classes/sun/nio/ch/ServerSocketChannelImpl.java 2018-05-03 16:52:25.517492701 -0700 +++ new/src/java.base/share/classes/sun/nio/ch/ServerSocketChannelImpl.java 2018-05-03 16:52:25.291492716 -0700 @@ -54,55 +54,55 @@ * An implementation of ServerSocketChannels */ -class ServerSocketChannelImpl +public class ServerSocketChannelImpl extends ServerSocketChannel implements SelChImpl { // Used to make native close and configure calls - private static NativeDispatcher nd; + protected static NativeDispatcher nd; // Our file descriptor - private final FileDescriptor fd; - private final int fdVal; + protected FileDescriptor fd; + protected final int fdVal; // Lock held by thread currently blocked on this channel - private final ReentrantLock acceptLock = new ReentrantLock(); + protected final ReentrantLock acceptLock = new ReentrantLock(); // Lock held by any thread that modifies the state fields declared below // DO NOT invoke a blocking I/O operation while holding this lock! - private final Object stateLock = new Object(); + protected final Object stateLock = new Object(); // -- The following fields are protected by stateLock // Channel state, increases monotonically - 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; + protected static final int ST_INUSE = 0; + protected static final int ST_CLOSING = 1; + protected static final int ST_KILLPENDING = 2; + protected static final int ST_KILLED = 3; + protected int state; // ID of native thread currently blocked in this channel, for signalling - private long thread; + protected long thread; // Binding - private InetSocketAddress localAddress; // null => unbound + protected InetSocketAddress localAddress; // null => unbound // set true when exclusive binding is on and SO_REUSEADDR is emulated - private boolean isReuseAddress; + protected boolean isReuseAddress; // Our socket adaptor, if any - private ServerSocket socket; + protected ServerSocket socket; // -- End of fields protected by stateLock - ServerSocketChannelImpl(SelectorProvider sp) throws IOException { + protected ServerSocketChannelImpl(SelectorProvider sp) throws IOException { super(sp); - this.fd = Net.serverSocket(true); + this.fd = createFD(); this.fdVal = IOUtil.fdVal(fd); } - ServerSocketChannelImpl(SelectorProvider sp, FileDescriptor fd, boolean bound) + protected ServerSocketChannelImpl(SelectorProvider sp, FileDescriptor fd, boolean bound) throws IOException { super(sp); @@ -110,13 +110,22 @@ this.fdVal = IOUtil.fdVal(fd); if (bound) { synchronized (stateLock) { - localAddress = Net.localAddress(fd); + localAddress = createLocalAddress(fd); } } } + protected FileDescriptor createFD() throws IOException { + return Net.serverSocket(true); + } + + protected InetSocketAddress createLocalAddress(FileDescriptor fd) + throws IOException { + return Net.localAddress(fd); + } + // @throws ClosedChannelException if channel is closed - private void ensureOpen() throws ClosedChannelException { + protected void ensureOpen() throws ClosedChannelException { if (!isOpen()) throw new ClosedChannelException(); } @@ -204,7 +213,7 @@ } @Override - public final Set> supportedOptions() { + public Set> supportedOptions() { return DefaultOptionsHolder.defaultOptions; } @@ -234,7 +243,7 @@ * @throws ClosedChannelException if the channel is closed * @throws NotYetBoundException if the channel's socket has not been bound yet */ - private void begin(boolean blocking) throws ClosedChannelException { + protected void begin(boolean blocking) throws ClosedChannelException { if (blocking) begin(); // set blocker to close channel if interrupted synchronized (stateLock) { @@ -252,7 +261,7 @@ * @throws AsynchronousCloseException if the channel was closed due to this * thread being interrupted on a blocking I/O operation. */ - private void end(boolean blocking, boolean completed) + protected void end(boolean blocking, boolean completed) throws AsynchronousCloseException { if (blocking) { --- old/src/java.base/share/classes/sun/nio/ch/SocketChannelImpl.java 2018-05-03 16:52:26.325492649 -0700 +++ new/src/java.base/share/classes/sun/nio/ch/SocketChannelImpl.java 2018-05-03 16:52:26.101492664 -0700 @@ -59,68 +59,68 @@ * An implementation of SocketChannels */ -class SocketChannelImpl +public class SocketChannelImpl extends SocketChannel implements SelChImpl { // Used to make native read and write calls - private static NativeDispatcher nd; + protected static NativeDispatcher nd; // Our file descriptor object - private final FileDescriptor fd; - private final int fdVal; + protected FileDescriptor fd; + protected final int fdVal; // Lock held by current reading or connecting thread - private final ReentrantLock readLock = new ReentrantLock(); + protected final ReentrantLock readLock = new ReentrantLock(); // Lock held by current writing or connecting thread - private final ReentrantLock writeLock = new ReentrantLock(); + protected final ReentrantLock writeLock = new ReentrantLock(); // Lock held by any thread that modifies the state fields declared below // DO NOT invoke a blocking I/O operation while holding this lock! - private final Object stateLock = new Object(); + protected final Object stateLock = new Object(); // Input/Output closed - private volatile boolean isInputClosed; - private volatile boolean isOutputClosed; + protected volatile boolean isInputClosed; + protected volatile boolean isOutputClosed; // -- The following fields are protected by stateLock // set true when exclusive binding is on and SO_REUSEADDR is emulated - private boolean isReuseAddress; + protected boolean isReuseAddress; // State, increases monotonically - 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 + protected static final int ST_UNCONNECTED = 0; + protected static final int ST_CONNECTIONPENDING = 1; + protected static final int ST_CONNECTED = 2; + protected static final int ST_CLOSING = 3; + protected static final int ST_KILLPENDING = 4; + protected static final int ST_KILLED = 5; + protected volatile int state; // need stateLock to change // IDs of native threads doing reads and writes, for signalling - private long readerThread; - private long writerThread; + protected long readerThread; + protected long writerThread; // Binding - private InetSocketAddress localAddress; - private InetSocketAddress remoteAddress; + protected InetSocketAddress localAddress; + protected InetSocketAddress remoteAddress; // Socket adaptor, created on demand - private Socket socket; + protected Socket socket; // -- End of fields protected by stateLock // Constructor for normal connecting sockets // - SocketChannelImpl(SelectorProvider sp) throws IOException { + protected SocketChannelImpl(SelectorProvider sp) throws IOException { super(sp); - this.fd = Net.socket(true); + this.fd = createFD(); this.fdVal = IOUtil.fdVal(fd); } - SocketChannelImpl(SelectorProvider sp, FileDescriptor fd, boolean bound) + protected SocketChannelImpl(SelectorProvider sp, FileDescriptor fd, boolean bound) throws IOException { super(sp); @@ -128,32 +128,41 @@ this.fdVal = IOUtil.fdVal(fd); if (bound) { synchronized (stateLock) { - this.localAddress = Net.localAddress(fd); + this.localAddress = createLocalAddress(fd); } } } // Constructor for sockets obtained from server sockets // - SocketChannelImpl(SelectorProvider sp, FileDescriptor fd, InetSocketAddress isa) + protected SocketChannelImpl(SelectorProvider sp, FileDescriptor fd, InetSocketAddress isa) throws IOException { super(sp); this.fd = fd; this.fdVal = IOUtil.fdVal(fd); synchronized (stateLock) { - this.localAddress = Net.localAddress(fd); + this.localAddress = createLocalAddress(fd); this.remoteAddress = isa; this.state = ST_CONNECTED; } } + protected FileDescriptor createFD() throws IOException { + return Net.socket(true); + } + + protected InetSocketAddress createLocalAddress(FileDescriptor fd) + throws IOException { + return Net.localAddress(fd); + } + /** * Checks that the channel is open. * * @throws ClosedChannelException if channel is closed (or closing) */ - private void ensureOpen() throws ClosedChannelException { + protected void ensureOpen() throws ClosedChannelException { if (!isOpen()) throw new ClosedChannelException(); } @@ -286,7 +295,7 @@ } @Override - public final Set> supportedOptions() { + public Set> supportedOptions() { return DefaultOptionsHolder.defaultOptions; } @@ -296,7 +305,7 @@ * @throws ClosedChannelException if the channel is closed * @throws NotYetConnectedException if the channel is not yet connected */ - private void beginRead(boolean blocking) throws ClosedChannelException { + protected void beginRead(boolean blocking) throws ClosedChannelException { if (blocking) { // set hook for Thread.interrupt begin(); @@ -317,7 +326,7 @@ * @throws AsynchronousCloseException if the channel was closed due to this * thread being interrupted on a blocking read operation. */ - private void endRead(boolean blocking, boolean completed) + protected void endRead(boolean blocking, boolean completed) throws AsynchronousCloseException { if (blocking) { @@ -407,7 +416,7 @@ * @throws ClosedChannelException if the channel is closed or output shutdown * @throws NotYetConnectedException if the channel is not yet connected */ - private void beginWrite(boolean blocking) throws ClosedChannelException { + protected void beginWrite(boolean blocking) throws ClosedChannelException { if (blocking) { // set hook for Thread.interrupt begin(); @@ -430,7 +439,7 @@ * @throws AsynchronousCloseException if the channel was closed due to this * thread being interrupted on a blocking write operation. */ - private void endWrite(boolean blocking, boolean completed) + protected void endWrite(boolean blocking, boolean completed) throws AsynchronousCloseException { if (blocking) { @@ -507,7 +516,7 @@ /** * Writes a byte of out of band data. */ - int sendOutOfBandData(byte b) throws IOException { + protected int sendOutOfBandData(byte b) throws IOException { writeLock.lock(); try { boolean blocking = isBlocking(); @@ -553,7 +562,7 @@ /** * Returns the local address, or null if not bound */ - InetSocketAddress localAddress() { + protected InetSocketAddress localAddress() { synchronized (stateLock) { return localAddress; } @@ -562,7 +571,7 @@ /** * Returns the remote address, or null if not connected */ - InetSocketAddress remoteAddress() { + protected InetSocketAddress remoteAddress() { synchronized (stateLock) { return remoteAddress; } @@ -618,7 +627,7 @@ * @throws ConnectionPendingException is a connection is pending * @throws IOException if the pre-connect hook fails */ - private void beginConnect(boolean blocking, InetSocketAddress isa) + protected void beginConnect(boolean blocking, InetSocketAddress isa) throws IOException { if (blocking) { @@ -653,7 +662,7 @@ * thread being interrupted on a blocking connect operation. * @throws IOException if completed and unable to obtain the local address */ - private void endConnect(boolean blocking, boolean completed) + protected void endConnect(boolean blocking, boolean completed) throws IOException { endRead(blocking, completed); @@ -715,7 +724,7 @@ * @throws ClosedChannelException if the channel is closed * @throws NoConnectionPendingException if no connection is pending */ - private void beginFinishConnect(boolean blocking) throws ClosedChannelException { + protected void beginFinishConnect(boolean blocking) throws ClosedChannelException { if (blocking) { // set hook for Thread.interrupt begin(); @@ -738,7 +747,7 @@ * thread being interrupted on a blocking connect operation. * @throws IOException if completed and unable to obtain the local address */ - private void endFinishConnect(boolean blocking, boolean completed) + protected void endFinishConnect(boolean blocking, boolean completed) throws IOException { endRead(blocking, completed); @@ -930,11 +939,11 @@ } } - boolean isInputOpen() { + protected boolean isInputOpen() { return !isInputClosed; } - boolean isOutputOpen() { + protected boolean isOutputOpen() { return !isOutputClosed; } --- old/src/java.base/unix/classes/sun/nio/ch/PollSelectorImpl.java 2018-05-03 16:52:27.050492602 -0700 +++ new/src/java.base/unix/classes/sun/nio/ch/PollSelectorImpl.java 2018-05-03 16:52:26.814492617 -0700 @@ -40,7 +40,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; @@ -65,7 +65,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; @@ -128,6 +128,10 @@ return updateSelectedKeys(); } + protected int poll(long pollAddress, int numfds, int timeout) { + return poll0(pollAddress, numfds, timeout); + } + /** * Process changes to the interest ops. */ @@ -383,7 +387,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-05-03 16:52:27.617492566 -0700 +++ new/src/java.base/unix/classes/sun/nio/ch/SocketDispatcher.java 2018-05-03 16:52:27.378492581 -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-05-03 16:52:28.171492530 -0700 +++ new/src/java.base/unix/native/libnio/ch/PollSelectorImpl.c 2018-05-03 16:52:27.954492544 -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-05-03 16:52:28.744492493 -0700 +++ new/src/jdk.net/share/classes/jdk/net/Sockets.java 2018-05-03 16:52:28.518492507 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 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 @@ -26,12 +26,15 @@ package jdk.net; import java.net.*; +import java.nio.channels.*; import java.io.IOException; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; +import rdma.ch.RdmaPollSelectorProvider; +import rdma.ch.RdmaSocketImpl; import jdk.net.ExtendedSocketOptions.PlatformSocketOptions; /** @@ -350,4 +353,69 @@ available = s.contains(ExtendedSocketOptions.TCP_QUICKACK); } } + + /** + * Creates an unconnected RDMA socket + */ + public static Socket openRdmaSocket() throws IOException { + RdmaSocketImplFactory factory = new RdmaSocketImplFactory(); + SocketImpl impl = factory.createSocketImpl(); + return new Socket(impl) { }; + } + + /** + * Creates an unbound RDMA server socket + */ + public static ServerSocket openRdmaServerSocket() throws IOException { + RdmaSocketImplFactory factory = new RdmaSocketImplFactory(); + SocketImpl impl = factory.createSocketImpl(); + return new ServerSocket(impl) { }; + } + + /** + * Opens a RDMA socket channel. + * + * @return A new RDMA socket channel + * + * @throws IOException + * If an I/O error occurs + */ + public static SocketChannel openRdmaSocketChannel() throws IOException { + return RdmaPollSelectorProvider.provider().openSocketChannel(); + } + + /** + * Opens a RDMA server-socket channel. + * + *

The new channel is created by invoking the {@link + * java.nio.channels.spi.SelectorProvider#openServerSocketChannel + * openServerSocketChannel} method of the system-wide default {@link + * java.nio.channels.spi.SelectorProvider} object. + * + *

The new channel's socket is initially unbound; it must be bound to a + * specific address via one of its socket's {@link + * java.net.ServerSocket#bind(SocketAddress) bind} methods before + * connections can be accepted.

+ * + * @return A new RDMA server socket channel + * + * @throws IOException + * If an I/O error occurs + */ + public static ServerSocketChannel openRdmaServerSocketChannel() + throws IOException { + return RdmaPollSelectorProvider.provider().openServerSocketChannel(); + } + + /** + * Opens a RDMA selector. + * + * @return A new RDMA selector + * + * @throws IOException + * If an I/O error occurs + */ + public static Selector openRdmaSelector() throws IOException { + return RdmaPollSelectorProvider.provider().openSelector(); + } } --- old/src/jdk.net/share/classes/module-info.java 2018-05-03 16:52:29.244492461 -0700 +++ new/src/jdk.net/share/classes/module-info.java 2018-05-03 16:52:29.013492475 -0700 @@ -31,5 +31,7 @@ */ module jdk.net { exports jdk.net; + exports rdma.ch to + java.base; } --- /dev/null 2018-04-24 15:28:37.238183177 -0700 +++ new/src/java.base/share/classes/sun/net/ext/RdmaSocketOptions.java 2018-05-03 16:52:29.549492441 -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. + * + * Extended 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 extended 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 extended 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 extended socket options. + Class c = Class.forName("jdk.net.RdmaSocketOptions"); + } catch (ClassNotFoundException e) { + // the jdk.net module is not present => no extended 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 extended options: " + option.name()); + } + + @Override + public Object getOption(FileDescriptor fd, SocketOption option) + throws SocketException + { + throw new UnsupportedOperationException( + "no extended options: " + option.name()); + } + } +} --- /dev/null 2018-04-24 15:28:37.238183177 -0700 +++ new/src/jdk.net/linux/classes/jdk/net/LinuxRdmaSocketOptions.java 2018-05-03 16:52:30.082492406 -0700 @@ -0,0 +1,52 @@ +/* + * 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; + } + }); + } +} --- /dev/null 2018-04-24 15:28:37.238183177 -0700 +++ new/src/jdk.net/linux/classes/rdma/ch/LinuxRdmaSocketDispatcher.java 2018-05-03 16:52:30.615492372 -0700 @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2000, 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 rdma.ch; +import java.io.*; + +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 { + LinuxRdmaSocketDispatcherImpl.preClose0(fd); + } +} --- /dev/null 2018-04-24 15:28:37.238183177 -0700 +++ new/src/jdk.net/linux/classes/rdma/ch/LinuxRdmaSocketDispatcherImpl.java 2018-05-03 16:52:31.157492337 -0700 @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2000, 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 rdma.ch; + +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); + } + + protected void preClose(FileDescriptor fd) throws IOException { + preClose0(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 preClose0(FileDescriptor fd) throws IOException; + + static native void init(); +} --- /dev/null 2018-04-24 15:28:37.238183177 -0700 +++ new/src/jdk.net/linux/classes/rdma/ch/LinuxRdmaSocketImpl.java 2018-05-03 16:52:31.648492305 -0700 @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2007, 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 rdma.ch; + +import java.io.IOException; +import java.net.SocketException; +import java.net.InetAddress; +import java.net.SocketImpl; +import sun.net.ext.RdmaSocketOptions; +import rdma.ch.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; + } + + 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-04-24 15:28:37.238183177 -0700 +++ new/src/jdk.net/linux/native/libextnet/LinuxRdmaSocketDispatcherImpl.c 2018-05-03 16:52:32.198492270 -0700 @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2000, 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 "jni.h" +#include "jni_util.h" +#include "jvm.h" +#include "jlong.h" +#include "nio_util.h" +#include "rdma_ch_LinuxRdmaSocketDispatcherImpl.h" +#include "java_lang_Long.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "nio.h" + +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_rdma_ch_LinuxRdmaSocketDispatcherImpl_init(JNIEnv *env, jclass cl) +{ + 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]; + rclose(sp[1]); +} + +JNIEXPORT jint JNICALL +Java_rdma_ch_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, rread(fd, buf, len), JNI_TRUE); +} + +JNIEXPORT jlong JNICALL +Java_rdma_ch_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, rreadv(fd, iov, len), JNI_TRUE); +} + +JNIEXPORT jint JNICALL +Java_rdma_ch_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, rwrite(fd, buf, len), JNI_FALSE); +} + +JNIEXPORT jlong JNICALL +Java_rdma_ch_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, rwritev(fd, iov, len), JNI_FALSE); +} + +static jlong +handle(JNIEnv *env, jlong rv, char *msg) +{ + if (rv >= 0) + return rv; + if (errno == EINTR) + return IOS_INTERRUPTED; + JNU_ThrowIOExceptionWithLastError(env, msg); + return IOS_THROWN; +} + +static void closeFileDescriptor(JNIEnv *env, int fd) { + if (fd != -1) { + int result = rclose(fd); + if (result < 0) + JNU_ThrowIOExceptionWithLastError(env, "Close failed"); + } +} + +JNIEXPORT void JNICALL +Java_rdma_ch_LinuxRdmaSocketDispatcherImpl_close0(JNIEnv *env, jclass clazz, jobject fdo) +{ + jint fd = (*env)->GetIntField(env, fdo, fd_fdID); + closeFileDescriptor(env, fd); +} + +JNIEXPORT void JNICALL +Java_rdma_ch_LinuxRdmaSocketDispatcherImpl_preClose0(JNIEnv *env, jclass clazz, jobject fdo) +{ +/* + jint fd = (*env)->GetIntField(env, fdo, fd_fdID); + if (preCloseFD >= 0) { + if (dup2(preCloseFD, fd) < 0) + JNU_ThrowIOExceptionWithLastError(env, "dup2 failed"); + } +*/ +} --- /dev/null 2018-04-24 15:28:37.238183177 -0700 +++ new/src/jdk.net/linux/native/libextnet/LinuxRdmaSocketImpl.c 2018-05-03 16:52:32.735492235 -0700 @@ -0,0 +1,762 @@ +/* + * Copyright (c) 1997, 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 "rdma_util_md.h" + +#include "java_net_SocketOptions.h" + +#include "net_util.h" +#include "net_util_md.h" +#include "rdma_ch_LinuxRdmaSocketImpl.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 = rfcntl(fd, F_GETFL); \ + flags |= O_NONBLOCK; \ + rfcntl(fd, F_SETFL, flags); \ +} + +#define SET_BLOCKING(fd) { \ + int flags = rfcntl(fd, F_GETFL); \ + flags &= ~O_NONBLOCK; \ + rfcntl(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 + + rshutdown(sv[0], 2); + rclose(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_rdma_ch_LinuxRdmaSocketImpl_initProto(JNIEnv *env, jclass cls) { + jclass clazz = (*env)->FindClass(env, "rdma/ch/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_rdma_ch_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_rdma_ch_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 = rsocket(domain, type, 0)) == -1) { + NET_ThrowNew(env, errno, "can't create socket"); + return; + } + if (domain == AF_INET6) { + int arg = 0; + if (rsetsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&arg, + sizeof(int)) < 0) { + NET_ThrowNew(env, errno, "cannot set IPPROTO_IPV6"); + rclose(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"); + rclose(fd); + return; + } + } + (*env)->SetIntField(env, fdObj, IO_fd_fdID, fd); +} + +JNIEXPORT void JNICALL +Java_rdma_ch_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 = rconnect(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); + rshutdown(fd, 2); + return; + } + + optlen = sizeof(connect_rv); + if (rgetsockopt(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 (rgetsockname(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_rdma_ch_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 (rgetsockname(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_rdma_ch_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 = rlisten(fd, count); + if (rv == -1) { + JNU_ThrowByNameWithMessageAndLastError + (env, JNU_JAVANETPKG "SocketException", "Listen failed"); + } +} + +JNIEXPORT void JNICALL +Java_rdma_ch_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) { + rclose(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_rdma_ch_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_rdma_ch_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_rdma_ch_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); + } + rshutdown(fd, howto); +} + + +JNIEXPORT void JNICALL +Java_rdma_ch_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 : + case java_net_SocketOptions_SO_LINGER : + { + jclass cls; + jfieldID fid; + + cls = (*env)->FindClass(env, "java/lang/Integer"); + CHECK_NULL(cls); + fid = (*env)->GetFieldID(env, cls, "value", "I"); + CHECK_NULL(fid); + + optval.i = (*env)->GetIntField(env, value, fid); + optlen = sizeof(optval.i); + + break; + } + + 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_rdma_ch_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 (rgetsockname(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_rdma_ch_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-04-24 15:28:37.238183177 -0700 +++ new/src/jdk.net/linux/native/libextnet/LinuxRdmaSocketOptions.c 2018-05-03 16:52:33.252492202 -0700 @@ -0,0 +1,96 @@ +/* + * 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 "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_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 = rsocket(PF_INET, SOCK_STREAM, 0); + if (s < 0) { + return JNI_FALSE; + } + rclose(s); + return JNI_TRUE; +} --- /dev/null 2018-04-24 15:28:37.238183177 -0700 +++ new/src/jdk.net/linux/native/libextnet/RdmaNet.c 2018-05-03 16:52:33.804492166 -0700 @@ -0,0 +1,378 @@ +/* + * Copyright (c) 2001, 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 +#include +#include +#include +#include + +#include "jni.h" +#include "jni_util.h" +#include "jvm.h" +#include "jlong.h" +#include "sun_nio_ch_Net.h" +#include "net_util.h" +#include "net_util_md.h" +#include "nio_util.h" +#include "nio.h" +#include "rdma_util_md.h" +//#include "rdma_ch_RdmaPollArrayWrapper.h" + +#ifndef IP_MULTICAST_ALL + #define IP_MULTICAST_ALL 49 +#endif + +#define COPY_INET6_ADDRESS(env, source, target) \ + (*env)->GetByteArrayRegion(env, source, 0, 16, target) + +static jfieldID fd_fdID; + +JNIEXPORT jboolean JNICALL +Java_rdma_ch_RdmaNet_isRdmaAvailable0(JNIEnv *env, jclass cls) { + return rdma_supported(); +} + +static int +configureBlocking(int fd, jboolean blocking) +{ + int flags = rfcntl(fd, F_GETFL); + int newflags = blocking ? (flags & ~O_NONBLOCK) : (flags | O_NONBLOCK); + + return (flags == newflags) ? 0 : rfcntl(fd, F_SETFL, newflags); +} + +JNIEXPORT void JNICALL +Java_rdma_ch_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_rdma_ch_RdmaNet_initIDs(JNIEnv *env, jclass clazz) +{ + 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_rdma_ch_RdmaNet_isIPv6Available0(JNIEnv* env, jclass cl) +{ + return (ipv6_available()) ? JNI_TRUE : JNI_FALSE; +} + +JNIEXPORT jboolean JNICALL +Java_rdma_ch_RdmaNet_canIPv6SocketJoinIPv4Group0(JNIEnv* env, jclass cl) +{ + return JNI_TRUE; +} + +JNIEXPORT jboolean JNICALL +Java_rdma_ch_RdmaNet_canJoin6WithIPv4Group0(JNIEnv* env, jclass cl) +{ + return JNI_FALSE; +} + +JNIEXPORT jint JNICALL +Java_rdma_ch_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 = rsocket(domain, type, 0); + if (fd < 0) { + return handleSocketError(env, errno); + } + + /* Disable IPV6_V6ONLY to ensure dual-socket support */ + if (domain == AF_INET6) { + int arg = 0; + if (rsetsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&arg, + sizeof(int)) < 0) { + JNU_ThrowByNameWithLastError(env, + JNU_JAVANETPKG "SocketException", + "Unable to set IPV6_V6ONLY"); + rclose(fd); + return -1; + } + } + + if (reuse) { + int arg = 1; + if (rsetsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&arg, + sizeof(arg)) < 0) { + JNU_ThrowByNameWithLastError(env, + JNU_JAVANETPKG "SocketException", + "Unable to set SO_REUSEADDR"); + rclose(fd); + return -1; + } + } + + if (type == SOCK_DGRAM) { + int arg = 0; + int level = (domain == AF_INET6) ? IPPROTO_IPV6 : IPPROTO_IP; + if ((rsetsockopt(fd, level, IP_MULTICAST_ALL, (char*)&arg, sizeof(arg)) < 0) && + (errno != ENOPROTOOPT)) { + JNU_ThrowByNameWithLastError(env, + JNU_JAVANETPKG "SocketException", + "Unable to set IP_MULTICAST_ALL"); + rclose(fd); + return -1; + } + } + + /* By default, Linux uses the route default */ + if (domain == AF_INET6 && type == SOCK_DGRAM) { + int arg = 1; + if (rsetsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &arg, + sizeof(arg)) < 0) { + JNU_ThrowByNameWithLastError(env, + JNU_JAVANETPKG "SocketException", + "Unable to set IPV6_MULTICAST_HOPS"); + rclose(fd); + return -1; + } + } + return fd; +} + +JNIEXPORT void JNICALL +Java_rdma_ch_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_rdma_ch_RdmaNet_listen(JNIEnv *env, jclass cl, jobject fdo, jint backlog) +{ + int fd = (*env)->GetIntField(env, fdo, fd_fdID); + if (rlisten(fd, backlog) < 0) + handleSocketError(env, errno); +} + +JNIEXPORT jint JNICALL +Java_rdma_ch_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 = rconnect(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_rdma_ch_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 (rgetsockname(fd, &sa.sa, &sa_len) < 0) { + handleSocketError(env, errno); + return -1; + } + return NET_GetPortFromSockaddr(&sa); +} + +JNIEXPORT jobject JNICALL +Java_rdma_ch_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 (rgetsockname(fd, &sa.sa, &sa_len) < 0) { + handleSocketError(env, errno); + return NULL; + } + return NET_SockaddrToInetAddress(env, &sa, &port); +} + +JNIEXPORT jint JNICALL +Java_rdma_ch_RdmaNet_getIntOption0(JNIEnv *env, jclass clazz, jobject fdo, + jboolean mayNeedConversion, jint level, jint opt) +{ + int result; + 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 = rgetsockopt(fd, level, opt, arg, &arglen); + } + if (n < 0) { + JNU_ThrowByNameWithLastError(env, + JNU_JAVANETPKG "SocketException", + "rdma.ch.RdmaNet.getIntOption"); + return -1; + } + + return (jint)result; +} + +JNIEXPORT void JNICALL +Java_rdma_ch_RdmaNet_setIntOption0(JNIEnv *env, jclass clazz, jobject fdo, + jboolean mayNeedConversion, jint level, + jint opt, jint arg, jboolean isIPv6) +{ + int result; + 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 = rsetsockopt(fd, level, opt, parg, arglen); + } + if (n < 0) { + JNU_ThrowByNameWithLastError(env, + JNU_JAVANETPKG "SocketException", + "rdma.ch.RdmaNet.setIntOption"); + } +} + +JNIEXPORT void JNICALL +Java_rdma_ch_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 ((rshutdown(fd, how) < 0) && (errno != ENOTCONN)) + handleSocketError(env, errno); +} + +JNIEXPORT jint JNICALL +Java_rdma_ch_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 = rpoll(&pfd, 1, (int)timeout); + + if (rv >= 0) { + return pfd.revents; + } else if (errno == EINTR) { + return IOS_INTERRUPTED; + } else { + handleSocketError(env, errno); + return IOS_THROWN; + } +} + +/* +JNIEXPORT jshort JNICALL +Java_rdma_ch_RdmaNet_pollinValue(JNIEnv *env, jclass this) +{ + return (jshort)POLLIN; +} + +JNIEXPORT jshort JNICALL +Java_rdma_ch_RdmaNet_polloutValue(JNIEnv *env, jclass this) +{ + return (jshort)POLLOUT; +} + +JNIEXPORT jshort JNICALL +Java_rdma_ch_RdmaNet_pollerrValue(JNIEnv *env, jclass this) +{ + return (jshort)POLLERR; +} + +JNIEXPORT jshort JNICALL +Java_rdma_ch_RdmaNet_pollhupValue(JNIEnv *env, jclass this) +{ + return (jshort)POLLHUP; +} + +JNIEXPORT jshort JNICALL +Java_rdma_ch_RdmaNet_pollnvalValue(JNIEnv *env, jclass this) +{ + return (jshort)POLLNVAL; +} + +JNIEXPORT jshort JNICALL +Java_rdma_ch_RdmaNet_pollconnValue(JNIEnv *env, jclass this) +{ + return (jshort)POLLOUT; +} +*/ --- /dev/null 2018-04-24 15:28:37.238183177 -0700 +++ new/src/jdk.net/linux/native/libextnet/RdmaPollSelectorImpl.c 2018-05-03 16:52:34.320492133 -0700 @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2001, 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.h" +#include "jni_util.h" +#include "jvm.h" +#include "jlong.h" +#include "nio.h" +#include "rdma_ch_RdmaPollSelectorImpl.h" + +JNIEXPORT jint JNICALL +Java_rdma_ch_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 = rpoll(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-04-24 15:28:37.238183177 -0700 +++ new/src/jdk.net/linux/native/libextnet/RdmaServerSocketChannelImpl.c 2018-05-03 16:52:34.822492100 -0700 @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2000, 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 +#include +#include + +#include "jni.h" +#include "jni_util.h" +#include "net_util.h" +#include "jvm.h" +#include "jlong.h" +#include "rdma_ch_RdmaServerSocketChannelImpl.h" +#include "nio.h" +#include "nio_util.h" + + +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_rdma_ch_RdmaServerSocketChannelImpl_initIDs(JNIEnv *env, jclass c) +{ + 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_rdma_ch_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 = raccept(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-04-24 15:28:37.238183177 -0700 +++ new/src/jdk.net/linux/native/libextnet/RdmaSocketInputStream.c 2018-05-03 16:52:35.319492068 -0700 @@ -0,0 +1,163 @@ +/* + * Copyright (c) 1997, 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 "jvm.h" +#include "net_util.h" +#include "rdma_util_md.h" + +#include "rdma_ch_RdmaSocketInputStream.h" + +static jfieldID IO_fd_fdID; + +/* + * Class: jdk_net_RdmaSocketInputStream + * Method: init + * Signature: ()V + */ +JNIEXPORT void JNICALL +Java_rdma_ch_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_rdma_ch_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-04-24 15:28:37.238183177 -0700 +++ new/src/jdk.net/linux/native/libextnet/RdmaSocketOutputStream.c 2018-05-03 16:52:35.832492035 -0700 @@ -0,0 +1,124 @@ +/* + * Copyright (c) 1997, 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 "net_util.h" +#include "rdma_util_md.h" + +#include "rdma_ch_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_rdma_ch_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_rdma_ch_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-04-24 15:28:37.238183177 -0700 +++ new/src/jdk.net/linux/native/libextnet/rdma_util_md.c 2018-05-03 16:52:36.295492005 -0700 @@ -0,0 +1,507 @@ +/* + * Copyright (c) 1997, 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 // defines TCP_NODELAY +#include +#include +#include +#include + +#include +#include +#include + +#include "jvm.h" +#include "rdma_util_md.h" + +#include "java_net_SocketOptions.h" +#include "jdk_net_RdmaSocketOptions.h" +#include "java_net_InetAddress.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "net_util.h" + +#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 = 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_KEEPALIVE, SOL_SOCKET, SO_KEEPALIVE }, + { 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 = rgetsockopt(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 rsetsockopt(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 = rbind(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 = rclose(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, rrecv(s, buf, len, 0) ); +} + +int RDMA_NonBlockingRead(int s, void* buf, size_t len) { + BLOCKING_IO_RETURN_INT( s, rrecv(s, buf, len, MSG_DONTWAIT) ); +} + +int RDMA_ReadV(int s, const struct iovec * vector, int count) { + BLOCKING_IO_RETURN_INT( s, rreadv(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, rrecvfrom(s, buf, len, flags, from, fromlen) ); +} + +int RDMA_Send(int s, void *msg, int len, unsigned int flags) { + BLOCKING_IO_RETURN_INT( s, rsend(s, msg, len, flags) ); +} + +int RDMA_WriteV(int s, const struct iovec * vector, int count) { + BLOCKING_IO_RETURN_INT( s, rwritev(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, rsendto(s, msg, len, flags, to, tolen) ); +} + +int RDMA_Accept(int s, struct sockaddr *addr, socklen_t *addrlen) { + BLOCKING_IO_RETURN_INT( s, raccept(s, addr, addrlen) ); +} + +int RDMA_Connect(int s, struct sockaddr *addr, int addrlen) { + BLOCKING_IO_RETURN_INT( s, rconnect(s, addr, addrlen) ); +} + +int RDMA_Poll(struct pollfd *ufds, unsigned int nfds, int timeout) { + BLOCKING_IO_RETURN_INT( ufds[0].fd, rpoll(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 = rpoll(&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-04-24 15:28:37.238183177 -0700 +++ new/src/jdk.net/linux/native/libextnet/rdma_util_md.h 2018-05-03 16:52:36.835491970 -0700 @@ -0,0 +1,59 @@ +/* + * Copyright (c) 1997, 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. + */ + +#ifndef RDMA_UTILS_MD_H +#define RDMA_UTILS_MD_H + +#include +#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); +#endif /* RDMA_UTILS_MD_H */ --- /dev/null 2018-04-24 15:28:37.238183177 -0700 +++ new/src/jdk.net/share/classes/jdk/net/RdmaSocketImplFactory.java 2018-05-03 16:52:37.372491936 -0700 @@ -0,0 +1,38 @@ +/* + * 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 + * 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.SocketImplFactory; +import java.net.SocketImpl; +import rdma.ch.RdmaSocketImpl; + +public +class RdmaSocketImplFactory implements SocketImplFactory { + public SocketImpl createSocketImpl() { + RdmaSocketImpl impl = new RdmaSocketImpl(); + return impl; + } +} --- /dev/null 2018-04-24 15:28:37.238183177 -0700 +++ new/src/jdk.net/share/classes/jdk/net/RdmaSocketOptions.java 2018-05-03 16:52:37.919491900 -0700 @@ -0,0 +1,239 @@ +/* + * 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 rsockets. + * These options may be platform specific. + * + * @since 11 + */ +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() { } + + public static final SocketOption RDMA_SQSIZE = new + RdmaSocketOption("RDMA_SQSIZE", Integer.class); + + public static final SocketOption RDMA_RQSIZE = new + RdmaSocketOption("RDMA_RQSIZE", Integer.class); + + public static final SocketOption RDMA_INLINE = new + RdmaSocketOption("RDMA_INLINE", Integer.class); + + @Native public static final int SQSIZE = 0x3001; + + @Native public static final int RQSIZE = 0x3002; + + @Native public 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 = SQSIZE; + } 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-04-24 15:28:37.238183177 -0700 +++ new/src/jdk.net/share/classes/rdma/ch/RdmaNet.java 2018-05-03 16:52:38.458491866 -0700 @@ -0,0 +1,245 @@ +/* + * Copyright (c) 2000, 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 rdma.ch; + +import java.io.*; +import java.net.*; +import java.nio.channels.*; +import java.util.*; +import java.security.AccessController; +import java.security.PrivilegedAction; +import sun.net.ext.RdmaSocketOptions; +import sun.nio.ch.Net; +import sun.nio.ch.IOUtil; + +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; + if (type == Integer.class) { + arg = ((Integer)value).intValue(); + } 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-04-24 15:28:37.238183177 -0700 +++ new/src/jdk.net/share/classes/rdma/ch/RdmaOptionKey.java 2018-05-03 16:52:39.002491830 -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 rdma.ch; + +/** + * 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-04-24 15:28:37.238183177 -0700 +++ new/src/jdk.net/share/classes/rdma/ch/RdmaPollSelectorImpl.java 2018-05-03 16:52:39.532491796 -0700 @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2001, 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 rdma.ch; + +import java.io.IOException; +import java.nio.channels.spi.SelectorProvider; +import sun.nio.ch.PollSelectorImpl; +import sun.nio.ch.IOUtil; + +import jdk.internal.misc.Unsafe; + +/** + * 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(); + } +} --- /dev/null 2018-04-24 15:28:37.238183177 -0700 +++ new/src/jdk.net/share/classes/rdma/ch/RdmaPollSelectorProvider.java 2018-05-03 16:52:40.058491762 -0700 @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2001, 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 rdma.ch; + +import java.io.IOException; +import java.nio.channels.*; +import java.nio.channels.spi.*; +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; + + 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 new RdmaPollSelectorImpl(this); + } + + public SocketChannel openSocketChannel() throws IOException { + return new RdmaSocketChannelImpl(this); + } + + public ServerSocketChannel openServerSocketChannel() throws IOException { + return new RdmaServerSocketChannelImpl(this); + } +} --- /dev/null 2018-04-24 15:28:37.238183177 -0700 +++ new/src/jdk.net/share/classes/rdma/ch/RdmaServerSocketChannelImpl.java 2018-05-03 16:52:40.577491729 -0700 @@ -0,0 +1,278 @@ +/* + * Copyright (c) 2000, 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 rdma.ch; + +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.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 sun.nio.ch.ServerSocketChannelImpl; +import sun.nio.ch.IOStatus; +import sun.nio.ch.IOUtil; +import sun.net.ext.RdmaSocketOptions; + +public class RdmaServerSocketChannelImpl + extends ServerSocketChannelImpl +{ + protected RdmaServerSocketChannelImpl(SelectorProvider sp) throws IOException { + super(sp); + } + + protected RdmaServerSocketChannelImpl(SelectorProvider sp, FileDescriptor fd, boolean bound) + throws IOException + { + super(sp, fd, bound); + } + + protected FileDescriptor createFD() throws IOException { + return RdmaNet.serverSocket(true); + } + + protected InetSocketAddress createLocalAddress(FileDescriptor fd) + throws IOException { + return RdmaNet.localAddress(fd); + } + + @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; + } else { + 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; + } + + @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(); + } + } + + /** + * 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 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-04-24 15:28:37.238183177 -0700 +++ new/src/jdk.net/share/classes/rdma/ch/RdmaSocketChannelImpl.java 2018-05-03 16:52:41.073491697 -0700 @@ -0,0 +1,487 @@ +/* + * Copyright (c) 2000, 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 rdma.ch; + +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.channels.AlreadyBoundException; +import java.nio.channels.ConnectionPendingException; +import java.nio.channels.NotYetConnectedException; +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 sun.nio.ch.NativeThread; +import sun.nio.ch.IOStatus; +import sun.nio.ch.IOUtil; +import sun.nio.ch.SocketChannelImpl; +import sun.net.ext.RdmaSocketOptions; + +public class RdmaSocketChannelImpl + extends SocketChannelImpl +{ + // Constructor for normal connecting sockets + + protected RdmaSocketChannelImpl(SelectorProvider sp) throws IOException { + super(sp); + } + + protected RdmaSocketChannelImpl(SelectorProvider sp, FileDescriptor fd, boolean bound) + throws IOException + { + super(sp, fd, bound); + } + + RdmaSocketChannelImpl(SelectorProvider sp, FileDescriptor fd, InetSocketAddress isa) + throws IOException + { + super(sp, fd, isa); + } + + protected FileDescriptor createFD() throws IOException { + return RdmaNet.socket(true); + } + + protected InetSocketAddress createLocalAddress(FileDescriptor fd) + throws IOException { + return RdmaNet.localAddress(fd); + } + + @Override + public SocketAddress getLocalAddress() throws IOException { + synchronized (stateLock) { + ensureOpen(); + return RdmaNet.getRevealedLocalAddress(localAddress); + } + } + + @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_KEEPALIVE); + 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; + } + + @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(); + } + } + + @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; + } + + protected 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; + } + } + + protected 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 + 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) { + ((RdmaSocketDispatcher)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, we + // shutdown the output 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 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; + } + } + + /** + * 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(); + } + } + + @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 int checkConnect(FileDescriptor fd, boolean block) + throws IOException; + + private static native int sendOutOfBandData(FileDescriptor fd, byte data) + throws IOException; + + static { + IOUtil.load(); + nd = new RdmaSocketDispatcher(); + } + +} --- /dev/null 2018-04-24 15:28:37.238183177 -0700 +++ new/src/jdk.net/share/classes/rdma/ch/RdmaSocketDispatcher.java 2018-05-03 16:52:41.600491663 -0700 @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2000, 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 rdma.ch; + +import sun.nio.ch.SocketDispatcher; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.io.*; + +/** + * 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("rdma.ch.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-04-24 15:28:37.238183177 -0700 +++ new/src/jdk.net/share/classes/rdma/ch/RdmaSocketImpl.java 2018-05-03 16:52:42.073491632 -0700 @@ -0,0 +1,744 @@ +/* + * 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 + * 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 rdma.ch; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.FileDescriptor; + +import java.util.Set; +import java.util.HashSet; +import java.util.Collections; +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.lang.reflect.Field; +import sun.net.ConnectionResetException; +import java.security.AccessController; +import java.security.PrivilegedAction; +import sun.net.ext.RdmaSocketOptions; + +public class RdmaSocketImpl extends SocketImpl +{ + Socket socket = null; + ServerSocket serverSocket = null; + + protected FileDescriptor fd; + + int timeout; // timeout in millisec + + int trafficClass; + + protected InetAddress address; + + protected int port; + + protected int localport; + + private boolean shut_rd = false; + private boolean shut_wr = false; + + private RdmaSocketInputStream socketInputStream = null; + private RdmaSocketOutputStream socketOutputStream = null; + + /* 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 Field sCreateState; + private Field sBoundState; + private Field sConnectState; + private Field ssCreateState; + private Field ssBoundState; + private Class sClass; + private Class ssClass; + + boolean isRdmaAvailable() { + return platformRdmaSocketImpl.isRdmaAvailable(); + } + + void setSocket(Socket soc) throws NoSuchFieldException { + this.socket = soc; + try { + this.sClass = socket.getClass(); + this.sCreateState = sClass.getDeclaredField("created"); + this.sBoundState = sClass.getDeclaredField("bound"); + this.sConnectState = sClass.getDeclaredField("connect"); + } catch (NoSuchFieldException e) { + throw e; + } + } + + Socket getSocket() { + return socket; + } + + void setServerSocket(ServerSocket soc) throws NoSuchFieldException { + this.serverSocket = soc; + try { + this.ssClass = serverSocket.getClass(); + this.ssCreateState = ssClass.getDeclaredField("created"); + this.ssBoundState = ssClass.getDeclaredField("bound"); + } catch (NoSuchFieldException e) { + throw e; + } + } + + ServerSocket getServerSocket() { + return serverSocket; + } + + @Override + protected Set> supportedOptions() { + Set> options; + if (isRdmaAvailable()) { + options = new HashSet<>(); + options.addAll(super.supportedOptions()); + options.add(jdk.net.RdmaSocketOptions.RDMA_SQSIZE); + options.add(jdk.net.RdmaSocketOptions.RDMA_RQSIZE); + options.add(jdk.net.RdmaSocketOptions.RDMA_INLINE); + options = Collections.unmodifiableSet(options); + } else { + options = super.supportedOptions(); + } + 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_KEEPALIVE) { + opt = SocketOptions.SO_KEEPALIVE; + } else if (name == StandardSocketOptions.SO_SNDBUF) { + 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) { + opt = SocketOptions.TCP_NODELAY; + } else { + throw new UnsupportedOperationException("unsupported option"); + } + setOption(opt, value); + } else { + if (((socket != null) && !socket.isConnected()) + || ((serverSocket != null) && !serverSocket.isBound())) { + rdmaOptions.setOption(fd, name, value); + } else { + throw new UnsupportedOperationException("RDMA socket option must be set before connect!"); + } + } + } + + @SuppressWarnings("unchecked") + protected T getOption(SocketOption name) throws IOException { + if (!rdmaOptions.isOptionSupported(name)) { + int opt; + if (name == StandardSocketOptions.SO_KEEPALIVE) { + opt = SocketOptions.SO_KEEPALIVE; + } else if (name == StandardSocketOptions.SO_SNDBUF) { + 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) { + 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: + if (val == null || !(val instanceof Integer) || + !(((Integer)val).intValue() > 0)) { + throw new SocketException("bad parameter for SO_SNDBUF " + + "or SO_RCVBUF"); + } + break; + case SO_KEEPALIVE: + if (val == null || !(val instanceof Boolean)) + throw new SocketException("bad parameter for SO_KEEPALIVE"); + on = ((Boolean)val).booleanValue(); + break; + case SO_REUSEADDR: + if (val == null || !(val instanceof Boolean)) + throw new SocketException("bad parameter for SO_REUSEADDR"); + on = ((Boolean)val).booleanValue(); + 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_SNDBUF: + case SO_RCVBUF: + ret = platformRdmaSocketImpl.rdmaSocketGetOption(this, opt, null); + return ret; + case SO_KEEPALIVE: + ret = platformRdmaSocketImpl.rdmaSocketGetOption(this, opt, null); + return Boolean.valueOf(ret != -1); + 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) + sBoundState.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 = new RdmaSocketInputStream(this); + } + return socketInputStream; + } + + void setInputStream(RdmaSocketInputStream in) { + socketInputStream = in; + } + + 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 = new RdmaSocketOutputStream(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.rdmaSocketShutdown(SHUT_RD, this); + if (socketInputStream != null) { + socketInputStream.setEOF(true); + } + 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("rdma.ch.LinuxRdmaSocketImpl"); + return new PlatformRdmaSocketImpl(); + } + + private static final PlatformRdmaSocketImpl instance = create(); + + static PlatformRdmaSocketImpl get() { + return instance; + } + + boolean isRdmaAvailable() { + return false; + } + + void rdmaSocketClose(boolean useDeferredClose, RdmaSocketImpl impl) throws IOException { + throw new UnsupportedOperationException("unsupported socket option"); + } + + void rdmaSocketCreate(boolean isServer, RdmaSocketImpl impl) throws IOException { + throw new UnsupportedOperationException("unsupported socket option"); + } + + void rdmaSocketConnect(RdmaSocketImpl impl, InetAddress address, int port, int timeout) + throws IOException { + throw new UnsupportedOperationException("unsupported socket option"); + } + + void rdmaSocketBind(RdmaSocketImpl impl, InetAddress address, int port) + throws IOException { + throw new UnsupportedOperationException("unsupported socket option"); + } + + void rdmaSocketListen(RdmaSocketImpl impl, int count) + throws IOException { + throw new UnsupportedOperationException("unsupported socket option"); + } + + void rdmaSocketAccept(SocketImpl s, RdmaSocketImpl impl) + throws IOException { + throw new UnsupportedOperationException("unsupported socket option"); + } + + int rdmaSocketAvailable(RdmaSocketImpl impl) + throws IOException { + throw new UnsupportedOperationException("unsupported socket option"); + } + + void rdmaSocketShutdown(int howto, RdmaSocketImpl impl) + throws IOException { + throw new UnsupportedOperationException("unsupported socket option"); + } + + void rdmaSocketSetOption(RdmaSocketImpl impl, int cmd, boolean on, Object value) + throws SocketException { + throw new UnsupportedOperationException("unsupported socket option"); + } + + int rdmaSocketGetOption(RdmaSocketImpl impl, int opt, Object iaContainerObj) + throws SocketException { + throw new UnsupportedOperationException("unsupported socket option"); + } + + void rdmaSocketSendUrgentData(RdmaSocketImpl impl, int data) + throws IOException { + throw new UnsupportedOperationException("unsupported socket option"); + } + } +} --- /dev/null 2018-04-24 15:28:37.238183177 -0700 +++ new/src/jdk.net/share/classes/rdma/ch/RdmaSocketInputStream.java 2018-05-03 16:52:42.587491599 -0700 @@ -0,0 +1,210 @@ +/* + * 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 + * 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 rdma.ch; + +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-04-24 15:28:37.238183177 -0700 +++ new/src/jdk.net/share/classes/rdma/ch/RdmaSocketOptionRegistry.java.template 2018-05-03 16:52:43.090491566 -0700 @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2008, 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 rdma.ch; +import java.net.SocketOption; +import java.net.StandardSocketOptions; +import java.net.ProtocolFamily; +import java.net.StandardProtocolFamily; +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_KEEPALIVE, + RdmaNet.UNSPEC), new RdmaOptionKey(SOL_SOCKET, SO_KEEPALIVE)); + 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)); + +#ifdef AF_INET6 + // IPPROTO_IPV6 is 41 + map.put(new RegistryKey(StandardSocketOptions.PREFIX_IP_TOS, + StandardProtocolFamily.INET6), new RdmaOptionKey(41, IPV6_TCLASS)); + map.put(new RegistryKey(StandardSocketOptions.PREFIX_IP_MULTICAST_IF, + StandardProtocolFamily.INET6), new RdmaOptionKey(41, IPV6_MULTICAST_IF)); + map.put(new RegistryKey(StandardSocketOptions.PREFIX_IP_MULTICAST_TTL, + StandardProtocolFamily.INET6), new RdmaOptionKey(41, IPV6_MULTICAST_HOPS)); + map.put(new RegistryKey(StandardSocketOptions.PREFIX_IP_MULTICAST_LOOP, + StandardProtocolFamily.INET6), new RdmaOptionKey(41, IPV6_MULTICAST_LOOP)); +#endif + return map; + } + } + + public static RdmaOptionKey findOption(SocketOption name, ProtocolFamily family) { + RegistryKey key = new RegistryKey(name, family); + return LazyInitialization.options.get(key); + } +} --- /dev/null 2018-04-24 15:28:37.238183177 -0700 +++ new/src/jdk.net/share/classes/rdma/ch/RdmaSocketOutputStream.java 2018-05-03 16:52:43.607491533 -0700 @@ -0,0 +1,155 @@ +/* + * 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 + * 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 rdma.ch; + +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(); + +}