--- /dev/null 2018-11-27 03:18:47.532777276 -0500 +++ new/src/jdk.net/linux/classes/jdk/internal/net/rdma/RdmaSocketImpl.java 2018-11-30 08:33:03.309539749 -0500 @@ -0,0 +1,753 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.internal.net.rdma; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.FileDescriptor; +import java.lang.reflect.Field; +import java.net.InetAddress; +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.ProtocolFamily; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.SocketImpl; +import java.net.SocketOption; +import java.net.SocketException; +import java.net.StandardProtocolFamily; +import java.net.UnknownHostException; +import java.net.InetAddress; +import java.net.SocketAddress; +import java.net.InetSocketAddress; +import java.net.StandardSocketOptions; +import java.net.SocketOptions; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.Objects; +import java.util.Set; +import sun.nio.ch.Net; +import sun.net.ConnectionResetException; +import sun.net.ext.RdmaSocketOptions; + +public abstract class RdmaSocketImpl extends SocketImpl +{ + private ProtocolFamily family; + + Socket socket = null; + ServerSocket serverSocket = null; + + int timeout; // timeout in millisec + + int trafficClass; + + InputStream socketInputStream; + OutputStream socketOutputStream; + + private boolean shut_rd = false; + private boolean shut_wr = false; + + /* number of threads using the FileDescriptor */ + protected int fdUseCount = 0; + + /* lock when increment/decrementing fdUseCount */ + protected final Object fdLock = new Object(); + + /* indicates a close is pending on the file descriptor */ + protected boolean closePending = false; + + /* indicates connection reset state */ + private int CONNECTION_NOT_RESET = 0; + private int CONNECTION_RESET_PENDING = 1; + private int CONNECTION_RESET = 2; + private int resetState; + private final Object resetLock = new Object(); + + protected boolean stream; + + static final sun.net.ext.RdmaSocketOptions rdmaOptions = + sun.net.ext.RdmaSocketOptions.getInstance(); + + private static PlatformRdmaSocketImpl platformRdmaSocketImpl = + PlatformRdmaSocketImpl.get(); + + boolean isRdmaAvailable() { + return platformRdmaSocketImpl.isRdmaAvailable(); + } + + private static final Void checkSupported() { + if (PlatformRdmaSocketImpl.unsupported != null) { + Exception e = PlatformRdmaSocketImpl.unsupported; + throw new UnsupportedOperationException(e.getMessage(), e); + } else { + return null; + } + } + + public RdmaSocketImpl(ProtocolFamily family) { + this(checkSupported()); + Objects.requireNonNull(family, "'family' is null"); + if ((family != StandardProtocolFamily.INET) && + (family != StandardProtocolFamily.INET6)) { + throw new UnsupportedOperationException( + "Protocol family not supported"); + } + if (family == StandardProtocolFamily.INET6) { + if (!Net.isIPv6Available()) { + throw new UnsupportedOperationException( + "IPv6 not available"); + } + } + this.family = family; + } + + private RdmaSocketImpl(Void unused) { } + + void setSocket(Socket soc) { + this.socket = soc; + } + + Socket getSocket() { + return socket; + } + + void setServerSocket(ServerSocket soc) { + this.serverSocket = soc; + } + + ServerSocket getServerSocket() { + return serverSocket; + } + + @Override + protected abstract Set> supportedOptions(); + + protected synchronized void create(boolean stream) throws IOException { + this.stream = stream; + if (stream) { + fd = new FileDescriptor(); + + boolean preferIPv6 = Net.isIPv6Available() && + (family != StandardProtocolFamily.INET); + platformRdmaSocketImpl.rdmaSocketCreate(this, preferIPv6, true); + } + } + + 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 { + if (family == StandardProtocolFamily.INET + && !(address instanceof Inet4Address)) + throw new IllegalArgumentException( + "address type not match"); + if (family == StandardProtocolFamily.INET6 + && !(address instanceof Inet6Address)) + throw new IllegalArgumentException( + "address type not match"); + + 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; + InetAddress ia = addr.getAddress(); + if (family == StandardProtocolFamily.INET + && !(ia instanceof Inet4Address)) + throw new IllegalArgumentException( + "address type not match"); + if (family == StandardProtocolFamily.INET6 + && !(ia instanceof Inet6Address)) + throw new IllegalArgumentException( + "address type not match"); + 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); + } + } + + @Override + protected abstract void setOption(SocketOption name, T value) + throws IOException; + + @SuppressWarnings("unchecked") + @Override + protected abstract T getOption(SocketOption name) + throws IOException; + + public void setOption(int opt, Object val) throws SocketException { + if (isClosedOrPending()) { + throw new SocketException("Socket Closed"); + } + boolean on = true; + switch (opt) { + case SO_TIMEOUT: + if (val == null || (!(val instanceof Integer))) + throw new SocketException("Bad parameter for SO_TIMEOUT"); + int tmp = ((Integer) val).intValue(); + if (tmp < 0) + throw new IllegalArgumentException("timeout < 0"); + timeout = tmp; + break; + case SO_BINDADDR: + throw new SocketException("Cannot re-bind socket"); + case TCP_NODELAY: + if (val == null || !(val instanceof Boolean)) + throw new SocketException("bad parameter for TCP_NODELAY"); + on = ((Boolean)val).booleanValue(); + break; + case SO_SNDBUF: + case SO_RCVBUF: + int value = ((Integer)val).intValue(); + int maxValue = 1024 * 1024 * 1024 - 1; //maximum value for the buffer + if (val == null || !(val instanceof Integer) || + !(value > 0)) { + throw new SocketException("bad parameter for SO_SNDBUF " + + "or SO_RCVBUF"); + } + if (value >= maxValue) + value = maxValue; + break; + case SO_REUSEADDR: + if (val == null || !(val instanceof Boolean)) + throw new SocketException("bad parameter for SO_REUSEADDR"); + on = ((Boolean)val).booleanValue(); + if (serverSocket != null && serverSocket.isBound()) + throw new UnsupportedOperationException( + "RDMA server socket cannot set " + + "SO_REUSEADDR after bind."); + if (socket != null && socket.isConnected()) + throw new UnsupportedOperationException( + "RDMA socket cannot set " + + "SO_REUSEADDR after connect."); + break; + default: + throw new SocketException("unrecognized TCP option: " + opt); + } + socketSetOption(opt, on, val); + } + + public Object getOption(int opt) throws SocketException { + if (isClosedOrPending()) { + throw new SocketException("Socket Closed"); + } + if (opt == SO_TIMEOUT) { + return timeout; + } + int ret = 0; + + switch (opt) { + case TCP_NODELAY: + ret = platformRdmaSocketImpl.rdmaSocketGetOption(this, opt, null); + return Boolean.valueOf(ret != -1); + case SO_REUSEADDR: + ret = platformRdmaSocketImpl.rdmaSocketGetOption(this, opt, null); + return Boolean.valueOf(ret != -1); + case SO_BINDADDR: + RdmaInetAddressContainer in = new RdmaInetAddressContainer(); + ret = platformRdmaSocketImpl.rdmaSocketGetOption(this, opt, in); + return in.addr; + case SO_SNDBUF: + case SO_RCVBUF: + ret = platformRdmaSocketImpl.rdmaSocketGetOption(this, opt, null); + return ret; + default: + return null; + } + } + + protected void socketSetOption(int opt, boolean b, Object val) + throws SocketException { + if (opt == SocketOptions.SO_REUSEPORT && + !supportedOptions().contains(StandardSocketOptions.SO_REUSEPORT)) { + throw new UnsupportedOperationException("unsupported option"); + } + try { + platformRdmaSocketImpl.rdmaSocketSetOption(this, opt, b, val); + } catch (SocketException se) { + if (socket == null || !socket.isConnected()) + throw se; + } + } + + synchronized void doConnect(InetAddress address, int port, int timeout) + throws IOException { + try { + acquireFD(); + boolean preferIPv6 = Net.isIPv6Available() && + (family != StandardProtocolFamily.INET); + try { + platformRdmaSocketImpl.rdmaSocketConnect(this, preferIPv6, + address, port, timeout); + synchronized (fdLock) { + if (closePending) { + throw new SocketException ("Socket closed"); + } + } + } finally { + releaseFD(); + } + } catch (IOException e) { + close(); + throw e; + } + } + + protected synchronized void bind(InetAddress address, int lport) + throws IOException { + if (address == null) + throw new IllegalArgumentException("address is null"); + if (family == StandardProtocolFamily.INET + && !(address instanceof Inet4Address)) + throw new IllegalArgumentException( + "address type not match"); + if (family == StandardProtocolFamily.INET6 + && !(address instanceof Inet6Address)) + throw new IllegalArgumentException( + "address type not match"); + boolean preferIPv6 = Net.isIPv6Available() && + (family != StandardProtocolFamily.INET); + platformRdmaSocketImpl.rdmaSocketBind(this, preferIPv6, + address, lport); + } + + protected synchronized void listen(int count) throws IOException { + platformRdmaSocketImpl.rdmaSocketListen(this, count); + } + + protected void accept(SocketImpl s) throws IOException { + acquireFD(); + try { + platformRdmaSocketImpl.rdmaSocketAccept(this, s); + } finally { + releaseFD(); + } + } + + protected synchronized InputStream getInputStream() throws IOException { + synchronized (fdLock) { + if (isClosedOrPending()) + throw new IOException("Socket Closed"); + if (shut_rd) + throw new IOException("Socket input is shutdown"); + if (socketInputStream == null) + socketInputStream = platformRdmaSocketImpl + .getRdmaInputStream(this); + } + return socketInputStream; + } + + protected synchronized OutputStream getOutputStream() throws IOException { + synchronized (fdLock) { + if (isClosedOrPending()) + throw new IOException("Socket Closed"); + if (shut_wr) + throw new IOException("Socket output is shutdown"); + if (socketOutputStream == null) + socketOutputStream = platformRdmaSocketImpl + .getRdmaOutputStream(this); + } + return socketOutputStream; + } + + protected FileDescriptor getFileDescriptor() { + return fd; + } + + protected void setFileDescriptor(FileDescriptor fd) { + this.fd = fd; + } + + protected void setAddress(InetAddress address) { + this.address = address; + } + + void setPort(int port) { + this.port = port; + } + + void setLocalPort(int localport) { + this.localport = localport; + } + + protected synchronized int available() throws IOException { + if (isClosedOrPending()) { + throw new IOException("Stream closed."); + } + + if (isConnectionReset() || shut_rd) { + return 0; + } + + int n = 0; + try { + n = platformRdmaSocketImpl.rdmaSocketAvailable(this); + if (n == 0 && isConnectionResetPending()) { + setConnectionReset(); + } + } catch (ConnectionResetException exc1) { + setConnectionResetPending(); + try { + n = platformRdmaSocketImpl.rdmaSocketAvailable(this); + if (n == 0) { + setConnectionReset(); + } + } catch (ConnectionResetException exc2) { + } + } + return n; + } + + protected void close() throws IOException { + synchronized(fdLock) { + if (fd != null) { + if (fdUseCount == 0) { + if (closePending) { + return; + } + closePending = true; + try { + platformRdmaSocketImpl.rdmaSocketClose(this, true); + } finally { + platformRdmaSocketImpl.rdmaSocketClose(this, false); + } + fd = null; + return; + } else { + if (!closePending) { + closePending = true; + fdUseCount--; + platformRdmaSocketImpl.rdmaSocketClose(this, true); + } + } + } + } + } + + void reset() throws IOException { + if (fd != null) { + platformRdmaSocketImpl.rdmaSocketClose(this, false); + } + fd = null; + postReset(); + } + + void postReset() throws IOException { + address = null; + port = 0; + localport = 0; + } + + protected void shutdownInput() throws IOException { + if (fd != null) { + platformRdmaSocketImpl.rdmaSocketShutdownInput(this, + SHUT_RD, socketInputStream); + shut_rd = true; + } + } + + protected void shutdownOutput() throws IOException { + if (fd != null) { + platformRdmaSocketImpl.rdmaSocketShutdown(this, SHUT_WR); + 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(this, false); + } 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 { + private static UnsupportedOperationException unsupported; + + @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) { + if (x.getCause() instanceof UnsupportedOperationException) + throw (UnsupportedOperationException)x.getCause(); + throw new AssertionError(x); + } + } + + private static PlatformRdmaSocketImpl create() { + String osname = AccessController.doPrivileged( + new PrivilegedAction() { + public String run() { + return System.getProperty("os.name"); + } + }); + PlatformRdmaSocketImpl impl; + UnsupportedOperationException uoe = null; + if ("Linux".equals(osname)) { + try { + impl = newInstance("jdk.internal.net.rdma.LinuxRdmaSocketImpl"); + } catch (UnsupportedOperationException e) { + uoe = e; + impl = new PlatformRdmaSocketImpl(); + } + } else { + impl = new PlatformRdmaSocketImpl(); + } + unsupported = uoe; + return impl; + } + + private static final PlatformRdmaSocketImpl instance = create(); + + static PlatformRdmaSocketImpl get() { + return instance; + } + + boolean isRdmaAvailable() { + return false; + } + + InputStream getRdmaInputStream(RdmaSocketImpl impl) + throws IOException { + throw new UnsupportedOperationException( + "unsupported socket operation"); + } + + OutputStream getRdmaOutputStream(RdmaSocketImpl impl) + throws IOException { + throw new UnsupportedOperationException( + "unsupported socket operation"); + } + + void rdmaSocketClose(RdmaSocketImpl impl, boolean useDeferredClose) + throws IOException { + throw new UnsupportedOperationException( + "unsupported socket operation"); + } + + void rdmaSocketCreate(RdmaSocketImpl impl, boolean preferIPv6, + boolean isServer) throws IOException { + throw new UnsupportedOperationException( + "unsupported socket operation"); + } + + void rdmaSocketConnect(RdmaSocketImpl impl, boolean preferIPv6, + InetAddress address, int port, int timeout) + throws IOException { + throw new UnsupportedOperationException( + "unsupported socket operation"); + } + + void rdmaSocketBind(RdmaSocketImpl impl, boolean preferIPv6, + InetAddress address, int port) throws IOException { + throw new UnsupportedOperationException( + "unsupported socket operation"); + } + + void rdmaSocketListen(RdmaSocketImpl impl, int count) + throws IOException { + throw new UnsupportedOperationException( + "unsupported socket operation"); + } + + void rdmaSocketAccept(RdmaSocketImpl impl, SocketImpl s) + throws IOException { + throw new UnsupportedOperationException( + "unsupported socket operation"); + } + + int rdmaSocketAvailable(RdmaSocketImpl impl) throws IOException { + throw new UnsupportedOperationException( + "unsupported socket operation"); + } + + void rdmaSocketShutdown(RdmaSocketImpl impl, int howto) + throws IOException { + throw new UnsupportedOperationException( + "unsupported socket operation"); + } + + void rdmaSocketShutdownInput(RdmaSocketImpl impl, int howto, + InputStream socketInputStream) throws IOException { + throw new UnsupportedOperationException( + "unsupported socket operation"); + } + + void rdmaSocketSetOption(RdmaSocketImpl impl, int cmd, boolean on, Object value) + throws SocketException { + throw new UnsupportedOperationException( + "unsupported socket operation"); + } + + int rdmaSocketGetOption(RdmaSocketImpl impl, int opt, Object iaContainerObj) + throws SocketException { + throw new UnsupportedOperationException( + "unsupported socket operation"); + } + + void rdmaSocketSendUrgentData(RdmaSocketImpl impl, int data) + throws IOException { + throw new UnsupportedOperationException( + "unsupported socket operation"); + } + } +}