8243099: SO_INCOMING_NAPI_ID support
Add support for the SO_INCOMING_NAPI_ID socket option to jdk.net.ExtendedSocketOptions Co-authored-by: Dinesh Kumar <dinesh.kumar@intel.com> Reviewed-by: alanb, chegar, dfuchs, vtewari, pconcannon
This commit is contained in:
parent
822ec45b02
commit
93fcbec20a
@ -84,6 +84,16 @@ class LinuxSocketOptions extends PlatformSocketOptions {
|
||||
return getTcpKeepAliveIntvl0(fd);
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean incomingNapiIdSupported() {
|
||||
return incomingNapiIdSupported0();
|
||||
}
|
||||
|
||||
@Override
|
||||
int getIncomingNapiId(int fd) throws SocketException {
|
||||
return getIncomingNapiId0(fd);
|
||||
}
|
||||
|
||||
private static native void setTcpkeepAliveProbes0(int fd, int value) throws SocketException;
|
||||
private static native void setTcpKeepAliveTime0(int fd, int value) throws SocketException;
|
||||
private static native void setTcpKeepAliveIntvl0(int fd, int value) throws SocketException;
|
||||
@ -94,6 +104,8 @@ class LinuxSocketOptions extends PlatformSocketOptions {
|
||||
private static native boolean getQuickAck0(int fd) throws SocketException;
|
||||
private static native boolean keepAliveOptionsSupported0();
|
||||
private static native boolean quickAckSupported0();
|
||||
private static native boolean incomingNapiIdSupported0();
|
||||
private static native int getIncomingNapiId0(int fd) throws SocketException;
|
||||
static {
|
||||
if (System.getSecurityManager() == null) {
|
||||
System.loadLibrary("extnet");
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2017, 2020, 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
|
||||
@ -33,6 +33,39 @@
|
||||
#include "jni_util.h"
|
||||
#include "jdk_net_LinuxSocketOptions.h"
|
||||
|
||||
#ifndef SO_INCOMING_NAPI_ID
|
||||
#define SO_INCOMING_NAPI_ID 56
|
||||
#endif
|
||||
|
||||
static void handleError(JNIEnv *env, jint rv, const char *errmsg) {
|
||||
if (rv < 0) {
|
||||
if (errno == ENOPROTOOPT) {
|
||||
JNU_ThrowByName(env, "java/lang/UnsupportedOperationException",
|
||||
"unsupported socket option");
|
||||
} else {
|
||||
JNU_ThrowByNameWithLastError(env, "java/net/SocketException", errmsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static jint socketOptionSupported(jint level, jint optname) {
|
||||
jint one = 1;
|
||||
jint rv, s;
|
||||
socklen_t sz = sizeof (one);
|
||||
s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
if (s < 0) {
|
||||
return 0;
|
||||
}
|
||||
rv = getsockopt(s, level, optname, (void *) &one, &sz);
|
||||
if (rv != 0 && errno == ENOPROTOOPT) {
|
||||
rv = 0;
|
||||
} else {
|
||||
rv = 1;
|
||||
}
|
||||
close(s);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/*
|
||||
* Declare library specific JNI_Onload entry if static build
|
||||
*/
|
||||
@ -49,15 +82,7 @@ JNIEXPORT void JNICALL Java_jdk_net_LinuxSocketOptions_setQuickAck0
|
||||
int rv;
|
||||
optval = (on ? 1 : 0);
|
||||
rv = setsockopt(fd, SOL_SOCKET, TCP_QUICKACK, &optval, sizeof (optval));
|
||||
if (rv < 0) {
|
||||
if (errno == ENOPROTOOPT) {
|
||||
JNU_ThrowByName(env, "java/lang/UnsupportedOperationException",
|
||||
"unsupported socket option");
|
||||
} else {
|
||||
JNU_ThrowByNameWithLastError(env, "java/net/SocketException",
|
||||
"set option TCP_QUICKACK failed");
|
||||
}
|
||||
}
|
||||
handleError(env, rv, "set option TCP_QUICKACK failed");
|
||||
}
|
||||
|
||||
/*
|
||||
@ -70,15 +95,7 @@ JNIEXPORT jboolean JNICALL Java_jdk_net_LinuxSocketOptions_getQuickAck0
|
||||
int on;
|
||||
socklen_t sz = sizeof (on);
|
||||
int rv = getsockopt(fd, SOL_SOCKET, TCP_QUICKACK, &on, &sz);
|
||||
if (rv < 0) {
|
||||
if (errno == ENOPROTOOPT) {
|
||||
JNU_ThrowByName(env, "java/lang/UnsupportedOperationException",
|
||||
"unsupported socket option");
|
||||
} else {
|
||||
JNU_ThrowByNameWithLastError(env, "java/net/SocketException",
|
||||
"get option TCP_QUICKACK failed");
|
||||
}
|
||||
}
|
||||
handleError(env, rv, "get option TCP_QUICKACK failed");
|
||||
return on != 0;
|
||||
}
|
||||
|
||||
@ -89,48 +106,7 @@ JNIEXPORT jboolean JNICALL Java_jdk_net_LinuxSocketOptions_getQuickAck0
|
||||
*/
|
||||
JNIEXPORT jboolean JNICALL Java_jdk_net_LinuxSocketOptions_quickAckSupported0
|
||||
(JNIEnv *env, jobject unused) {
|
||||
int one = 1;
|
||||
int rv, s;
|
||||
s = socket(PF_INET, SOCK_STREAM, 0);
|
||||
if (s < 0) {
|
||||
return JNI_FALSE;
|
||||
}
|
||||
rv = setsockopt(s, SOL_SOCKET, TCP_QUICKACK, (void *) &one, sizeof (one));
|
||||
if (rv != 0 && errno == ENOPROTOOPT) {
|
||||
rv = JNI_FALSE;
|
||||
} else {
|
||||
rv = JNI_TRUE;
|
||||
}
|
||||
close(s);
|
||||
return rv;
|
||||
}
|
||||
|
||||
static jint socketOptionSupported(jint sockopt) {
|
||||
jint one = 1;
|
||||
jint rv, s;
|
||||
s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
if (s < 0) {
|
||||
return 0;
|
||||
}
|
||||
rv = setsockopt(s, SOL_TCP, sockopt, (void *) &one, sizeof (one));
|
||||
if (rv != 0 && errno == ENOPROTOOPT) {
|
||||
rv = 0;
|
||||
} else {
|
||||
rv = 1;
|
||||
}
|
||||
close(s);
|
||||
return rv;
|
||||
}
|
||||
|
||||
static void handleError(JNIEnv *env, jint rv, const char *errmsg) {
|
||||
if (rv < 0) {
|
||||
if (errno == ENOPROTOOPT) {
|
||||
JNU_ThrowByName(env, "java/lang/UnsupportedOperationException",
|
||||
"unsupported socket option");
|
||||
} else {
|
||||
JNU_ThrowByNameWithLastError(env, "java/net/SocketException", errmsg);
|
||||
}
|
||||
}
|
||||
return socketOptionSupported(SOL_SOCKET, TCP_QUICKACK);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -140,8 +116,8 @@ static void handleError(JNIEnv *env, jint rv, const char *errmsg) {
|
||||
*/
|
||||
JNIEXPORT jboolean JNICALL Java_jdk_net_LinuxSocketOptions_keepAliveOptionsSupported0
|
||||
(JNIEnv *env, jobject unused) {
|
||||
return socketOptionSupported(TCP_KEEPIDLE) && socketOptionSupported(TCP_KEEPCNT)
|
||||
&& socketOptionSupported(TCP_KEEPINTVL);
|
||||
return socketOptionSupported(SOL_TCP, TCP_KEEPIDLE) && socketOptionSupported(SOL_TCP, TCP_KEEPCNT)
|
||||
&& socketOptionSupported(SOL_TCP, TCP_KEEPINTVL);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -218,3 +194,27 @@ JNIEXPORT jint JNICALL Java_jdk_net_LinuxSocketOptions_getTcpKeepAliveIntvl0
|
||||
handleError(env, rv, "get option TCP_KEEPINTVL failed");
|
||||
return optval;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: jdk_net_LinuxSocketOptions
|
||||
* Method: incomingNapiIdSupported0
|
||||
* Signature: ()Z;
|
||||
*/
|
||||
JNIEXPORT jboolean JNICALL Java_jdk_net_LinuxSocketOptions_incomingNapiIdSupported0
|
||||
(JNIEnv *env, jobject unused) {
|
||||
return socketOptionSupported(SOL_SOCKET, SO_INCOMING_NAPI_ID);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: jdk_net_LinuxSocketOptions
|
||||
* Method: getIncomingNapiId0
|
||||
* Signature: (I)I;
|
||||
*/
|
||||
JNIEXPORT jint JNICALL Java_jdk_net_LinuxSocketOptions_getIncomingNapiId0
|
||||
(JNIEnv *env, jobject unused, jint fd) {
|
||||
jint optval, rv;
|
||||
socklen_t sz = sizeof (optval);
|
||||
rv = getsockopt(fd, SOL_SOCKET, SO_INCOMING_NAPI_ID, &optval, &sz);
|
||||
handleError(env, rv, "get option SO_INCOMING_NAPI_ID failed");
|
||||
return optval;
|
||||
}
|
||||
|
@ -165,6 +165,34 @@ public final class ExtendedSocketOptions {
|
||||
public static final SocketOption<Integer> TCP_KEEPCOUNT
|
||||
= new ExtSocketOption<Integer>("TCP_KEEPCOUNT", Integer.class);
|
||||
|
||||
/**
|
||||
* Identifies the receive queue that the last incoming packet for the socket
|
||||
* was received on.
|
||||
*
|
||||
* <p> The value of this socket option is a positive {@code Integer} that
|
||||
* identifies a receive queue that the application can use to split the
|
||||
* incoming flows among threads based on the queue identifier. The value is
|
||||
* {@code 0} when the socket is not bound, a packet has not been received,
|
||||
* or more generally, when there is no receive queue to identify.
|
||||
* The socket option is supported by both stream-oriented and datagram-oriented
|
||||
* sockets.
|
||||
*
|
||||
* <p> The socket option is read-only and an attempt to set the socket option
|
||||
* will throw {@code SocketException}.
|
||||
*
|
||||
* @apiNote
|
||||
* Network devices may have multiple queues or channels to transmit and receive
|
||||
* network packets. The {@code SO_INCOMING_NAPI_ID} socket option provides a hint
|
||||
* to the application to indicate the receive queue on which an incoming socket
|
||||
* connection or packets for that connection are directed to. An application may
|
||||
* take advantage of this by handling all socket connections assigned to a
|
||||
* specific queue on one thread.
|
||||
*
|
||||
* @since 15
|
||||
*/
|
||||
public static final SocketOption<Integer> SO_INCOMING_NAPI_ID
|
||||
= new ExtSocketOption<Integer>("SO_INCOMING_NAPI_ID", Integer.class);
|
||||
|
||||
private static final PlatformSocketOptions platformSocketOptions =
|
||||
PlatformSocketOptions.get();
|
||||
|
||||
@ -174,6 +202,8 @@ public final class ExtendedSocketOptions {
|
||||
platformSocketOptions.quickAckSupported();
|
||||
private static final boolean keepAliveOptSupported =
|
||||
platformSocketOptions.keepAliveOptionsSupported();
|
||||
private static final boolean incomingNapiIdOptSupported =
|
||||
platformSocketOptions.incomingNapiIdSupported();
|
||||
private static final Set<SocketOption<?>> extendedOptions = options();
|
||||
|
||||
static Set<SocketOption<?>> options() {
|
||||
@ -184,6 +214,9 @@ public final class ExtendedSocketOptions {
|
||||
if (quickAckSupported) {
|
||||
options.add(TCP_QUICKACK);
|
||||
}
|
||||
if (incomingNapiIdOptSupported) {
|
||||
options.add(SO_INCOMING_NAPI_ID);
|
||||
}
|
||||
if (keepAliveOptSupported) {
|
||||
options.addAll(Set.of(TCP_KEEPCOUNT, TCP_KEEPIDLE, TCP_KEEPINTERVAL));
|
||||
}
|
||||
@ -221,6 +254,11 @@ public final class ExtendedSocketOptions {
|
||||
setTcpKeepAliveTime(fd, (Integer) value);
|
||||
} else if (option == TCP_KEEPINTERVAL) {
|
||||
setTcpKeepAliveIntvl(fd, (Integer) value);
|
||||
} else if (option == SO_INCOMING_NAPI_ID) {
|
||||
if (!incomingNapiIdOptSupported)
|
||||
throw new UnsupportedOperationException("Attempt to set unsupported option " + option);
|
||||
else
|
||||
throw new SocketException("Attempt to set read only option " + option);
|
||||
} else {
|
||||
throw new InternalError("Unexpected option " + option);
|
||||
}
|
||||
@ -252,6 +290,8 @@ public final class ExtendedSocketOptions {
|
||||
return getTcpKeepAliveTime(fd);
|
||||
} else if (option == TCP_KEEPINTERVAL) {
|
||||
return getTcpKeepAliveIntvl(fd);
|
||||
} else if (option == SO_INCOMING_NAPI_ID) {
|
||||
return getIncomingNapiId(fd);
|
||||
} else {
|
||||
throw new InternalError("Unexpected option " + option);
|
||||
}
|
||||
@ -325,6 +365,10 @@ public final class ExtendedSocketOptions {
|
||||
return platformSocketOptions.getTcpKeepAliveIntvl(fdAccess.get(fd));
|
||||
}
|
||||
|
||||
private static int getIncomingNapiId(FileDescriptor fd) throws SocketException {
|
||||
return platformSocketOptions.getIncomingNapiId(fdAccess.get(fd));
|
||||
}
|
||||
|
||||
static class PlatformSocketOptions {
|
||||
|
||||
protected PlatformSocketOptions() {}
|
||||
@ -418,5 +462,13 @@ public final class ExtendedSocketOptions {
|
||||
int getTcpKeepAliveIntvl(int fd) throws SocketException {
|
||||
throw new UnsupportedOperationException("unsupported TCP_KEEPINTVL option");
|
||||
}
|
||||
|
||||
boolean incomingNapiIdSupported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
int getIncomingNapiId(int fd) throws SocketException {
|
||||
throw new UnsupportedOperationException("unsupported SO_INCOMING_NAPI_ID socket option");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -263,6 +263,8 @@ public class Sockets {
|
||||
private static Map<Class<?>,Set<SocketOption<?>>> optionSets() {
|
||||
Map<Class<?>,Set<SocketOption<?>>> options = new HashMap<>();
|
||||
boolean flowsupported = PlatformSocketOptions.get().flowSupported();
|
||||
boolean incomingNapiIdsupported = PlatformSocketOptions.get().incomingNapiIdSupported();
|
||||
|
||||
boolean reuseportsupported = isReusePortAvailable();
|
||||
// Socket
|
||||
|
||||
@ -288,6 +290,9 @@ public class Sockets {
|
||||
ExtendedSocketOptions.TCP_KEEPIDLE,
|
||||
ExtendedSocketOptions.TCP_KEEPINTERVAL));
|
||||
}
|
||||
if (incomingNapiIdsupported) {
|
||||
set.add(ExtendedSocketOptions.SO_INCOMING_NAPI_ID);
|
||||
}
|
||||
set = Collections.unmodifiableSet(set);
|
||||
options.put(Socket.class, set);
|
||||
|
||||
@ -308,6 +313,9 @@ public class Sockets {
|
||||
ExtendedSocketOptions.TCP_KEEPINTERVAL));
|
||||
}
|
||||
set.add(StandardSocketOptions.IP_TOS);
|
||||
if (incomingNapiIdsupported) {
|
||||
set.add(ExtendedSocketOptions.SO_INCOMING_NAPI_ID);
|
||||
}
|
||||
set = Collections.unmodifiableSet(set);
|
||||
options.put(ServerSocket.class, set);
|
||||
|
||||
@ -324,6 +332,9 @@ public class Sockets {
|
||||
if (flowsupported) {
|
||||
set.add(ExtendedSocketOptions.SO_FLOW_SLA);
|
||||
}
|
||||
if (incomingNapiIdsupported) {
|
||||
set.add(ExtendedSocketOptions.SO_INCOMING_NAPI_ID);
|
||||
}
|
||||
set = Collections.unmodifiableSet(set);
|
||||
options.put(DatagramSocket.class, set);
|
||||
|
||||
|
@ -59,6 +59,7 @@ import static org.testng.Assert.expectThrows;
|
||||
public class AfterClose {
|
||||
|
||||
static final Class<IOException> IOE = IOException.class;
|
||||
static final String RO = "READ_ONLY";
|
||||
|
||||
static Map<SocketOption<?>,List<Object>> OPTION_VALUES_MAP = optionValueMap();
|
||||
|
||||
@ -106,6 +107,8 @@ public class AfterClose {
|
||||
map.put((SocketOption<?>)field.get(null), listOf(10, 100));
|
||||
field = c.getField("TCP_KEEPCOUNT");
|
||||
map.put((SocketOption<?>)field.get(null), listOf(10, 100));
|
||||
field = c.getField("SO_INCOMING_NAPI_ID");
|
||||
map.put((SocketOption<?>)field.get(null), listOf(RO));
|
||||
} catch (ClassNotFoundException e) {
|
||||
// ignore, jdk.net module not present
|
||||
} catch (ReflectiveOperationException e) {
|
||||
@ -158,7 +161,7 @@ public class AfterClose {
|
||||
Socket socket = createClosedSocketFromAdapter();
|
||||
for (int i=0; i<3; i++); {
|
||||
for (T value : values) {
|
||||
expectThrows(IOE, () -> socket.setOption(option, value));
|
||||
if (!RO.equals(value)) expectThrows(IOE, () -> socket.setOption(option, value));
|
||||
expectThrows(IOE, () -> socket.getOption(option));
|
||||
}
|
||||
}
|
||||
@ -211,7 +214,7 @@ public class AfterClose {
|
||||
ServerSocket serverSocket = createClosedServerSocketFromAdapter();
|
||||
for (int i=0; i<3; i++); {
|
||||
for (T value : values) {
|
||||
expectThrows(IOE, () -> serverSocket.setOption(option, value));
|
||||
if (!RO.equals(value)) expectThrows(IOE, () -> serverSocket.setOption(option, value));
|
||||
expectThrows(IOE, () -> serverSocket.getOption(option));
|
||||
}
|
||||
}
|
||||
|
@ -33,6 +33,7 @@
|
||||
import java.io.IOException;
|
||||
import java.net.SocketOption;
|
||||
import java.nio.channels.*;
|
||||
import java.util.*;
|
||||
|
||||
import jdk.test.lib.net.IPSupport;
|
||||
|
||||
@ -54,6 +55,8 @@ public class PrintSupportedOptions {
|
||||
test(() -> AsynchronousServerSocketChannel.open());
|
||||
}
|
||||
|
||||
static final Set<String> READ_ONLY_OPTS = Set.of("SO_INCOMING_NAPI_ID");
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static <T extends NetworkChannel>
|
||||
void test(NetworkChannelSupplier<T> supplier) throws IOException {
|
||||
@ -62,8 +65,9 @@ public class PrintSupportedOptions {
|
||||
for (SocketOption<?> opt : ch.supportedOptions()) {
|
||||
Object value = ch.getOption(opt);
|
||||
System.out.format(" %s -> %s%n", opt.name(), value);
|
||||
if (value != null) {
|
||||
ch.setOption((SocketOption<Object>) opt, value);
|
||||
if (!READ_ONLY_OPTS.contains(opt.name())) {
|
||||
if (value != null)
|
||||
ch.setOption((SocketOption<Object>) opt, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user