From 2a1596a4e9f6f9e5dad94ee2e83c4eb4f9dce0c6 Mon Sep 17 00:00:00 2001 From: Matteo Merli Date: Fri, 7 Sep 2018 11:50:51 -0700 Subject: [PATCH] Allow to configure socket option SO_BUSY_POLL (#8268) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Motivation: When using Epoll based transport, allow applications to configure SO_BUSY_POLL socket option: SO_BUSY_POLL (since Linux 3.11) Sets the approximate time in microseconds to busy poll on a blocking receive when there is no data. Increasing this value requires CAP_NET_ADMIN. The default for this option is con‐ trolled by the /proc/sys/net/core/busy_read file. The value in the /proc/sys/net/core/busy_poll file determines how long select(2) and poll(2) will busy poll when they oper‐ ate on sockets with SO_BUSY_POLL set and no events to report are found. In both cases, busy polling will only be done when the socket last received data from a network device that supports this option. While busy polling may improve latency of some applications, care must be taken when using it since this will increase both CPU utilization and power usage. Modification: Added SO_BUSY_POLL socket option Result: Able to configure SO_BUSY_POLL from Netty --- .../src/main/c/netty_epoll_linuxsocket.c | 19 ++++++++++++ .../channel/epoll/EpollChannelOption.java | 1 + .../epoll/EpollSocketChannelConfig.java | 30 ++++++++++++++++++- .../io/netty/channel/epoll/LinuxSocket.java | 10 +++++++ 4 files changed, 59 insertions(+), 1 deletion(-) diff --git a/transport-native-epoll/src/main/c/netty_epoll_linuxsocket.c b/transport-native-epoll/src/main/c/netty_epoll_linuxsocket.c index d6e3ead94f..05d889f175 100644 --- a/transport-native-epoll/src/main/c/netty_epoll_linuxsocket.c +++ b/transport-native-epoll/src/main/c/netty_epoll_linuxsocket.c @@ -50,6 +50,11 @@ #define TCP_NOTSENT_LOWAT 25 #endif +// SO_BUSY_POLL is defined in linux 3.11. We define this here so older kernels can compile. +#ifndef SO_BUSY_POLL +#define SO_BUSY_POLL 46 +#endif + static jclass peerCredentialsClass = NULL; static jmethodID peerCredentialsMethodId = NULL; @@ -111,6 +116,10 @@ static void netty_epoll_linuxsocket_setIpRecvOrigDestAddr(JNIEnv* env, jclass cl netty_unix_socket_setOption(env, fd, IPPROTO_IP, IP_RECVORIGDSTADDR, &optval, sizeof(optval)); } +static void netty_epoll_linuxsocket_setSoBusyPoll(JNIEnv* env, jclass clazz, jint fd, jint optval) { + netty_unix_socket_setOption(env, fd, SOL_SOCKET, SO_BUSY_POLL, &optval, sizeof(optval)); +} + static void netty_epoll_linuxsocket_setTcpMd5Sig(JNIEnv* env, jclass clazz, jint fd, jbyteArray address, jint scopeId, jbyteArray key) { struct sockaddr_storage addr; socklen_t addrSize; @@ -256,6 +265,14 @@ static jint netty_epoll_linuxsocket_isTcpCork(JNIEnv* env, jclass clazz, jint fd return optval; } +static jint netty_epoll_linuxsocket_getSoBusyPoll(JNIEnv* env, jclass clazz, jint fd) { + int optval; + if (netty_unix_socket_getOption(env, fd, SOL_SOCKET, SO_BUSY_POLL, &optval, sizeof(optval)) == -1) { + return -1; + } + return optval; +} + static jint netty_epoll_linuxsocket_getTcpDeferAccept(JNIEnv* env, jclass clazz, jint fd) { int optval; if (netty_unix_socket_getOption(env, fd, IPPROTO_TCP, TCP_DEFER_ACCEPT, &optval, sizeof(optval)) == -1) { @@ -341,10 +358,12 @@ static jlong netty_epoll_linuxsocket_sendFile(JNIEnv* env, jclass clazz, jint fd // JNI Method Registration Table Begin static const JNINativeMethod fixed_method_table[] = { { "setTcpCork", "(II)V", (void *) netty_epoll_linuxsocket_setTcpCork }, + { "setSoBusyPoll", "(II)V", (void *) netty_epoll_linuxsocket_setSoBusyPoll }, { "setTcpQuickAck", "(II)V", (void *) netty_epoll_linuxsocket_setTcpQuickAck }, { "setTcpDeferAccept", "(II)V", (void *) netty_epoll_linuxsocket_setTcpDeferAccept }, { "setTcpNotSentLowAt", "(II)V", (void *) netty_epoll_linuxsocket_setTcpNotSentLowAt }, { "isTcpCork", "(I)I", (void *) netty_epoll_linuxsocket_isTcpCork }, + { "getSoBusyPoll", "(I)I", (void *) netty_epoll_linuxsocket_getSoBusyPoll }, { "getTcpDeferAccept", "(I)I", (void *) netty_epoll_linuxsocket_getTcpDeferAccept }, { "getTcpNotSentLowAt", "(I)I", (void *) netty_epoll_linuxsocket_getTcpNotSentLowAt }, { "isTcpQuickAck", "(I)I", (void *) netty_epoll_linuxsocket_isTcpQuickAck }, diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollChannelOption.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollChannelOption.java index d03e3e7236..1f5127c5dc 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollChannelOption.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollChannelOption.java @@ -38,6 +38,7 @@ public final class EpollChannelOption extends UnixChannelOption { public static final ChannelOption TCP_DEFER_ACCEPT = ChannelOption.valueOf(EpollChannelOption.class, "TCP_DEFER_ACCEPT"); public static final ChannelOption TCP_QUICKACK = valueOf(EpollChannelOption.class, "TCP_QUICKACK"); + public static final ChannelOption SO_BUSY_POLL = valueOf(EpollChannelOption.class, "SO_BUSY_POLL"); public static final ChannelOption EPOLL_MODE = ChannelOption.valueOf(EpollChannelOption.class, "EPOLL_MODE"); diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollSocketChannelConfig.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollSocketChannelConfig.java index 67468910b2..d61bf19e27 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollSocketChannelConfig.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollSocketChannelConfig.java @@ -62,7 +62,7 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement ALLOW_HALF_CLOSURE, EpollChannelOption.TCP_CORK, EpollChannelOption.TCP_NOTSENT_LOWAT, EpollChannelOption.TCP_KEEPCNT, EpollChannelOption.TCP_KEEPIDLE, EpollChannelOption.TCP_KEEPINTVL, EpollChannelOption.TCP_MD5SIG, EpollChannelOption.TCP_QUICKACK, EpollChannelOption.IP_TRANSPARENT, - EpollChannelOption.TCP_FASTOPEN_CONNECT); + EpollChannelOption.TCP_FASTOPEN_CONNECT, EpollChannelOption.SO_BUSY_POLL); } @SuppressWarnings("unchecked") @@ -119,6 +119,9 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement if (option == EpollChannelOption.TCP_FASTOPEN_CONNECT) { return (T) Boolean.valueOf(isTcpFastOpenConnect()); } + if (option == EpollChannelOption.SO_BUSY_POLL) { + return (T) Integer.valueOf(getSoBusyPoll()); + } return super.getOption(option); } @@ -164,6 +167,8 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement setTcpQuickAck((Boolean) value); } else if (option == EpollChannelOption.TCP_FASTOPEN_CONNECT) { setTcpFastOpenConnect((Boolean) value); + } else if (option == EpollChannelOption.SO_BUSY_POLL) { + setSoBusyPoll((Integer) value); } else { return super.setOption(option, value); } @@ -245,6 +250,17 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement } } + /** + * Get the {@code SO_BUSY_POLL} option on the socket. See {@code man 7 tcp} for more details. + */ + public int getSoBusyPoll() { + try { + return channel.socket.getSoBusyPoll(); + } catch (IOException e) { + throw new ChannelException(e); + } + } + /** * Get the {@code TCP_NOTSENT_LOWAT} option on the socket. See {@code man 7 tcp} for more details. * @return value is a uint32_t @@ -380,6 +396,18 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement } } + /** + * Set the {@code SO_BUSY_POLL} option on the socket. See {@code man 7 tcp} for more details. + */ + public EpollSocketChannelConfig setSoBusyPoll(int loopMicros) { + try { + channel.socket.setSoBusyPoll(loopMicros); + return this; + } catch (IOException e) { + throw new ChannelException(e); + } + } + /** * Set the {@code TCP_NOTSENT_LOWAT} option on the socket. See {@code man 7 tcp} for more details. * @param tcpNotSentLowAt is a uint32_t diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/LinuxSocket.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/LinuxSocket.java index f0a5304a74..d3578b4dae 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/LinuxSocket.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/LinuxSocket.java @@ -56,6 +56,10 @@ final class LinuxSocket extends Socket { setTcpCork(intValue(), tcpCork ? 1 : 0); } + void setSoBusyPoll(int loopMicros) throws IOException { + setSoBusyPoll(intValue(), loopMicros); + } + void setTcpNotSentLowAt(long tcpNotSentLowAt) throws IOException { if (tcpNotSentLowAt < 0 || tcpNotSentLowAt > MAX_UINT32_T) { throw new IllegalArgumentException("tcpNotSentLowAt must be a uint32_t"); @@ -116,6 +120,10 @@ final class LinuxSocket extends Socket { return isTcpCork(intValue()) != 0; } + int getSoBusyPoll() throws IOException { + return getSoBusyPoll(intValue()); + } + int getTcpDeferAccept() throws IOException { return getTcpDeferAccept(intValue()); } @@ -190,6 +198,7 @@ final class LinuxSocket extends Socket { private static native int getTcpDeferAccept(int fd) throws IOException; private static native int isTcpQuickAck(int fd) throws IOException; private static native int isTcpCork(int fd) throws IOException; + private static native int getSoBusyPoll(int fd) throws IOException; private static native int getTcpNotSentLowAt(int fd) throws IOException; private static native int getTcpKeepIdle(int fd) throws IOException; private static native int getTcpKeepIntvl(int fd) throws IOException; @@ -205,6 +214,7 @@ final class LinuxSocket extends Socket { private static native void setTcpDeferAccept(int fd, int deferAccept) throws IOException; private static native void setTcpQuickAck(int fd, int quickAck) throws IOException; private static native void setTcpCork(int fd, int tcpCork) throws IOException; + private static native void setSoBusyPoll(int fd, int loopMicros) throws IOException; private static native void setTcpNotSentLowAt(int fd, int tcpNotSentLowAt) throws IOException; private static native void setTcpFastOpen(int fd, int tcpFastopenBacklog) throws IOException; private static native void setTcpFastOpenConnect(int fd, int tcpFastOpenConnect) throws IOException;