8250521: Configure initial RTO to use minimal retry for loopback connections on Windows

Reviewed-by: alanb
This commit is contained in:
Nikola Grcevski 2020-08-10 12:57:38 +01:00 committed by Alan Bateman
parent 7332181372
commit c2fa441d8d
3 changed files with 87 additions and 4 deletions

View file

@ -770,6 +770,57 @@ NET_EnableFastTcpLoopback(int fd) {
return result == SOCKET_ERROR ? WSAGetLastError() : 0; return result == SOCKET_ERROR ? WSAGetLastError() : 0;
} }
int
IsWindows10RS3OrGreater() {
OSVERSIONINFOEXW osvi = { sizeof(osvi), 0, 0, 0, 0, {0}, 0, 0 };
DWORDLONG const cond_mask = VerSetConditionMask(
VerSetConditionMask(
VerSetConditionMask(
0, VER_MAJORVERSION, VER_GREATER_EQUAL),
VER_MINORVERSION, VER_GREATER_EQUAL),
VER_BUILDNUMBER, VER_GREATER_EQUAL);
osvi.dwMajorVersion = HIBYTE(_WIN32_WINNT_WIN10);
osvi.dwMinorVersion = LOBYTE(_WIN32_WINNT_WIN10);
osvi.dwBuildNumber = 16299; // RS3 (Redstone 3)
return VerifyVersionInfoW(&osvi, VER_MAJORVERSION | VER_MINORVERSION | VER_BUILDNUMBER, cond_mask) != 0;
}
/**
* Shortens the default Windows socket
* connect timeout. Recommended for usage
* on the loopback adapter only.
*/
JNIEXPORT jint JNICALL
NET_EnableFastTcpLoopbackConnect(int fd) {
TCP_INITIAL_RTO_PARAMETERS rto = {
TCP_INITIAL_RTO_UNSPECIFIED_RTT, // Use the default or overriden by the Administrator
1 // Minimum possible value before Windows 10 RS3
};
/**
* In Windows 10 RS3+ we can use the no retransmissions flag to
* completely remove the timeout delay, which is fixed to 500ms
* if Windows receives RST when the destination port is not open.
*/
if (IsWindows10RS3OrGreater()) {
rto.MaxSynRetransmissions = TCP_INITIAL_RTO_NO_SYN_RETRANSMISSIONS;
}
DWORD result_byte_count = -1;
int result = WSAIoctl(fd, // descriptor identifying a socket
SIO_TCP_INITIAL_RTO, // dwIoControlCode
&rto, // pointer to TCP_INITIAL_RTO_PARAMETERS structure
sizeof(rto), // size, in bytes, of the input buffer
NULL, // pointer to output buffer
0, // size of output buffer
&result_byte_count, // number of bytes returned
NULL, // OVERLAPPED structure
NULL); // completion routine
return (result == SOCKET_ERROR) ? WSAGetLastError() : 0;
}
/** /**
* See net_util.h for documentation * See net_util.h for documentation
*/ */

View file

@ -26,6 +26,7 @@
#include <WS2tcpip.h> #include <WS2tcpip.h>
#include <iphlpapi.h> #include <iphlpapi.h>
#include <icmpapi.h> #include <icmpapi.h>
#include <mstcpip.h>
/* used to disable connection reset messages on Windows XP */ /* used to disable connection reset messages on Windows XP */
#ifndef SIO_UDP_CONNRESET #ifndef SIO_UDP_CONNRESET
@ -86,10 +87,29 @@ struct ipv6bind {
#define GET_PORT(X) ((X)->sa.sa_family == AF_INET ? (X)->sa4.sin_port : (X)->sa6.sin6_port) #define GET_PORT(X) ((X)->sa.sa_family == AF_INET ? (X)->sa4.sin_port : (X)->sa6.sin6_port)
/**
* With dual socket implementation the
* IPv4 addresseses might be mapped as IPv6.
* The IPv4 loopback adapter address will
* be mapped as the following IPv6 ::ffff:127.0.0.1.
* For example, this is done by NET_InetAddressToSockaddr.
*/
#define IN6_IS_ADDR_V4MAPPED_LOOPBACK(x) ( \
(((x)->s6_words[0] == 0) && \
((x)->s6_words[1] == 0) && \
((x)->s6_words[2] == 0) && \
((x)->s6_words[3] == 0) && \
((x)->s6_words[4] == 0) && \
((x)->s6_words[5] == 0xFFFF) && \
((x)->s6_words[6] == 0x007F) && \
((x)->s6_words[7] == 0x0100)) \
)
#define IS_LOOPBACK_ADDRESS(x) ( \ #define IS_LOOPBACK_ADDRESS(x) ( \
((x)->sa.sa_family == AF_INET) ? \ ((x)->sa.sa_family == AF_INET) ? \
(ntohl((x)->sa4.sin_addr.s_addr) == INADDR_LOOPBACK) : \ (ntohl((x)->sa4.sin_addr.s_addr) == INADDR_LOOPBACK) : \
(IN6ADDR_ISLOOPBACK(x)) \ ((IN6_IS_ADDR_LOOPBACK(&(x)->sa6.sin6_addr)) || \
(IN6_IS_ADDR_V4MAPPED_LOOPBACK(&(x)->sa6.sin6_addr))) \
) )
JNIEXPORT int JNICALL NET_SocketClose(int fd); JNIEXPORT int JNICALL NET_SocketClose(int fd);
@ -119,6 +139,8 @@ JNIEXPORT int JNICALL NET_BindV6(struct ipv6bind *b, jboolean exclBind);
JNIEXPORT int JNICALL NET_WinBind(int s, SOCKETADDRESS *sa, int len, JNIEXPORT int JNICALL NET_WinBind(int s, SOCKETADDRESS *sa, int len,
jboolean exclBind); jboolean exclBind);
JNIEXPORT jint JNICALL NET_EnableFastTcpLoopbackConnect(int fd);
/* XP versions of the native routines */ /* XP versions of the native routines */
JNIEXPORT jobject JNICALL Java_java_net_NetworkInterface_getByName0_XP JNIEXPORT jobject JNICALL Java_java_net_NetworkInterface_getByName0_XP

View file

@ -226,13 +226,25 @@ Java_sun_nio_ch_Net_connect0(JNIEnv *env, jclass clazz, jboolean preferIPv6, job
{ {
SOCKETADDRESS sa; SOCKETADDRESS sa;
int rv; int rv;
int so_rv;
int sa_len = 0; int sa_len = 0;
SOCKET s = (SOCKET)fdval(env, fdo); SOCKET s = (SOCKET)fdval(env, fdo);
int type = 0, optlen = sizeof(type);
if (NET_InetAddressToSockaddr(env, iao, port, &sa, &sa_len, preferIPv6) != 0) { if (NET_InetAddressToSockaddr(env, iao, port, &sa, &sa_len, preferIPv6) != 0) {
return IOS_THROWN; return IOS_THROWN;
} }
so_rv = getsockopt(s, SOL_SOCKET, SO_TYPE, (char*)&type, &optlen);
/**
* Windows has a very long socket connect timeout of 2 seconds.
* If it's the loopback adapter we can shorten the wait interval.
*/
if (so_rv == 0 && type == SOCK_STREAM && IS_LOOPBACK_ADDRESS(&sa)) {
NET_EnableFastTcpLoopbackConnect((jint)s);
}
rv = connect(s, &sa.sa, sa_len); rv = connect(s, &sa.sa, sa_len);
if (rv != 0) { if (rv != 0) {
int err = WSAGetLastError(); int err = WSAGetLastError();
@ -243,9 +255,7 @@ Java_sun_nio_ch_Net_connect0(JNIEnv *env, jclass clazz, jboolean preferIPv6, job
return IOS_THROWN; return IOS_THROWN;
} else { } else {
/* Enable WSAECONNRESET errors when a UDP socket is connected */ /* Enable WSAECONNRESET errors when a UDP socket is connected */
int type = 0, optlen = sizeof(type); if (so_rv == 0 && type == SOCK_DGRAM) {
rv = getsockopt(s, SOL_SOCKET, SO_TYPE, (char*)&type, &optlen);
if (rv == 0 && type == SOCK_DGRAM) {
setConnectionReset(s, TRUE); setConnectionReset(s, TRUE);
} }
} }