1 /*
   2  * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 #include <errno.h>
  26 #include <stdlib.h>
  27 #include <string.h>
  28 
  29 #include "jvm.h"
  30 #include "net_util.h"
  31 #include "rdma_util_md.h"
  32 
  33 #include "rdma_ch_RdmaSocketInputStream.h"
  34 
  35 static jfieldID IO_fd_fdID;
  36 
  37 /*
  38  * Class:     jdk_net_RdmaSocketInputStream
  39  * Method:    init
  40  * Signature: ()V
  41  */
  42 JNIEXPORT void JNICALL
  43 Java_rdma_ch_RdmaSocketInputStream_init(JNIEnv *env, jclass cls) {
  44     IO_fd_fdID = NET_GetFileDescriptorID(env);
  45 }
  46 
  47 static int RDMA_ReadWithTimeout(JNIEnv *env, int fd, char *bufP, int len, long timeout) {
  48     int result = 0;
  49     jlong prevNanoTime = JVM_NanoTime(env, 0);
  50     jlong nanoTimeout = (jlong) timeout * NET_NSEC_PER_MSEC;
  51     while (nanoTimeout >= NET_NSEC_PER_MSEC) {
  52         result = RDMA_Timeout(env, fd, nanoTimeout / NET_NSEC_PER_MSEC, prevNanoTime);
  53         if (result <= 0) {
  54             if (result == 0) {
  55                 JNU_ThrowByName(env, "java/net/SocketTimeoutException", "Read timed out");
  56             } else if (result == -1) {
  57                 if (errno == EBADF) {
  58                     JNU_ThrowByName(env, "java/net/SocketException", "Socket closed");
  59                 } else if (errno == ENOMEM) {
  60                     JNU_ThrowOutOfMemoryError(env, "NET_Timeout native heap allocation failed");
  61                 } else {
  62                     JNU_ThrowByNameWithMessageAndLastError
  63                             (env, "java/net/SocketException", "select/poll failed");
  64                 }
  65             }
  66             return -1;
  67         }
  68         result = RDMA_NonBlockingRead(fd, bufP, len);
  69         if (result == -1 && ((errno == EAGAIN) || (errno == EWOULDBLOCK))) {
  70             jlong newtNanoTime = JVM_NanoTime(env, 0);
  71             nanoTimeout -= newtNanoTime - prevNanoTime;
  72             if (nanoTimeout >= NET_NSEC_PER_MSEC) {
  73                 prevNanoTime = newtNanoTime;
  74             }
  75         } else {
  76             break;
  77         }
  78     }
  79     return result;
  80 }
  81 
  82 /*
  83  * Class:     jdk_net_RdmaSocketInputStream
  84  * Method:    rdmaSocketRead0
  85  * Signature: (Ljava/io/FileDescriptor;[BIII)I
  86  */
  87 JNIEXPORT jint JNICALL
  88 Java_rdma_ch_RdmaSocketInputStream_rdmaSocketRead0(JNIEnv *env, jobject this,
  89                                             jobject fdObj, jbyteArray data,
  90                                             jint off, jint len, jint timeout)
  91 {
  92     char BUF[MAX_BUFFER_LEN];
  93     char *bufP;
  94     jint fd, nread;
  95 
  96     if (IS_NULL(fdObj)) {
  97         JNU_ThrowByName(env, "java/net/SocketException",
  98                         "Socket closed");
  99         return -1;
 100     }
 101     fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
 102     if (fd == -1) {
 103         JNU_ThrowByName(env, "java/net/SocketException", "Socket closed");
 104         return -1;
 105     }
 106 
 107     if (len > MAX_BUFFER_LEN) {
 108         if (len > MAX_HEAP_BUFFER_LEN) {
 109             len = MAX_HEAP_BUFFER_LEN;
 110         }
 111         bufP = (char *)malloc((size_t)len);
 112         if (bufP == NULL) {
 113             bufP = BUF;
 114             len = MAX_BUFFER_LEN;
 115         }
 116     } else {
 117         bufP = BUF;
 118     }
 119     if (timeout) {
 120         nread = RDMA_ReadWithTimeout(env, fd, bufP, len, timeout);
 121         if ((*env)->ExceptionCheck(env)) {
 122             if (bufP != BUF) {
 123                 free(bufP);
 124             }
 125             return nread;
 126         }
 127     } else {
 128         nread = RDMA_Read(fd, bufP, len);
 129     }
 130 
 131     if (nread <= 0) {
 132         if (nread < 0) {
 133 
 134             switch (errno) {
 135                 case ECONNRESET:
 136                 case EPIPE:
 137                     JNU_ThrowByName(env, "sun/net/ConnectionResetException",
 138                         "Connection reset");
 139                     break;
 140 
 141                 case EBADF:
 142                     JNU_ThrowByName(env, "java/net/SocketException",
 143                         "Socket closed");
 144                     break;
 145 
 146                 case EINTR:
 147                      JNU_ThrowByName(env, "java/io/InterruptedIOException",
 148                            "Operation interrupted");
 149                      break;
 150                 default:
 151                     JNU_ThrowByNameWithMessageAndLastError
 152                         (env, "java/net/SocketException", "Read failed");
 153             }
 154         }
 155     } else {
 156         (*env)->SetByteArrayRegion(env, data, off, nread, (jbyte *)bufP);
 157     }
 158 
 159     if (bufP != BUF) {
 160         free(bufP);
 161     }
 162     return nread;
 163 }