From 38109b288e6cb2137c9d89bd0e498cfb3aef3048 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Sat, 23 Nov 2019 21:12:24 +0100 Subject: [PATCH] Remove dependency on GLIBC 2.12 by using syscalls directly (#9797) Motivation: 394a1b3485000c211595aff7495c4f863972af29 introduced a hard dependency on GLIBC 2.12 which was not the case before. This had the effect of not be able to use the native epoll transports on platforms which ship with earlier versions of GLIBC. To make things a backward compatible as possible we should not introduce such changes in a bugfix release. Special thanks to @weissi with all the help to fix this. Modifications: - Use syscalls directly to remove dependency on GLIBC 2.12 - Make code consistent that needs newer GLIBC versions - Adjust scattering read test to only run if recvmmsg syscall is supported - Cleanup pom.xml as some stuff is not needed anymore after using syscalls. Result: Fixes https://github.com/netty/netty/issues/9758. --- transport-native-epoll/pom.xml | 97 +------------------ .../src/main/c/netty_epoll_native.c | 44 ++++++--- .../channel/epoll/EpollDatagramChannel.java | 6 +- .../java/io/netty/channel/epoll/Native.java | 3 + .../NativeStaticallyReferencedJniMethods.java | 1 + .../EpollDatagramScatteringReadTest.java | 7 ++ 6 files changed, 49 insertions(+), 109 deletions(-) diff --git a/transport-native-epoll/pom.xml b/transport-native-epoll/pom.xml index 3f5098d3f3..6c71c86611 100644 --- a/transport-native-epoll/pom.xml +++ b/transport-native-epoll/pom.xml @@ -34,6 +34,7 @@ ${project.build.directory}/unix-common-lib ${unix.common.lib.dir}/META-INF/native/lib ${unix.common.lib.dir}/META-INF/native/include + CFLAGS=-O3 -Werror -fno-omit-frame-pointer -Wunused-variable -fvisibility=hidden -I${unix.common.include.unpacked.dir} LDFLAGS=-L${unix.common.lib.unpacked.dir} -Wl,--no-as-needed -lrt -Wl,--whole-archive -l${unix.common.lib.name} -Wl,--no-whole-archive ${project.basedir}/src/main/c true @@ -192,102 +193,6 @@ - - - maven-antrun-plugin - - - - validate - - run - - ant-get-systeminfo - - true - - - - - - - - - - - - - - - org.codehaus.mojo - build-helper-maven-plugin - - - - initialize - regex-glibc-sendmmsg - - regex-property - - - glibc.sendmmsg.support - ${ldd_version} - - ^((?!^[^)]+\)\s+(0*2\.1[4-9]|0*2\.[2-9][0-9]+|0*[3-9][0-9]*|0*[1-9]+[0-9]+).*).)*$ - IO_NETTY_SENDMSSG_NOT_FOUND - false - - - - - initialize - regex-linux-sendmmsg - - regex-property - - - linux.sendmmsg.support - ${uname_os_version} - - ^((?!^[0-9]*[3-9]\.?.*).)*$ - IO_NETTY_SENDMSSG_NOT_FOUND - false - - - - - generate-sources - regex-combined-sendmmsg - - regex-property - - - jni.compiler.args.cflags - ${linux.sendmmsg.support}${glibc.sendmmsg.support} - - .*IO_NETTY_SENDMSSG_NOT_FOUND.* - CFLAGS=-O3 -DIO_NETTY_SENDMMSG_NOT_FOUND -Werror -fno-omit-frame-pointer -Wunused-variable -fvisibility=hidden -I${unix.common.include.unpacked.dir} - false - - - - - generate-sources - regex-unset-if-needed-sendmmsg - - regex-property - - - jni.compiler.args.cflags - ${jni.compiler.args.cflags} - - ^((?!CFLAGS=).)*$ - CFLAGS=-O3 -Werror -fno-omit-frame-pointer -Wunused-variable -fvisibility=hidden -I${unix.common.include.unpacked.dir} - false - - - - diff --git a/transport-native-epoll/src/main/c/netty_epoll_native.c b/transport-native-epoll/src/main/c/netty_epoll_native.c index a7f6f18857..6f9af3dec8 100644 --- a/transport-native-epoll/src/main/c/netty_epoll_native.c +++ b/transport-native-epoll/src/main/c/netty_epoll_native.c @@ -36,6 +36,9 @@ #include #include #include +// Needed to be able to use syscalls directly and so not depend on newer GLIBC versions +#include +#include #include "netty_epoll_linuxsocket.h" #include "netty_unix_buffer.h" @@ -54,15 +57,20 @@ // optional extern int epoll_create1(int flags) __attribute__((weak)); -#ifdef IO_NETTY_SENDMMSG_NOT_FOUND -extern int sendmmsg(int sockfd, struct mmsghdr* msgvec, unsigned int vlen, unsigned int flags) __attribute__((weak)); - #ifndef __USE_GNU struct mmsghdr { struct msghdr msg_hdr; /* Message header */ unsigned int msg_len; /* Number of bytes transmitted */ }; #endif + +// All linux syscall numbers are stable so this is safe. +#ifndef SYS_recvmmsg +#define SYS_recvmmsg 299 +#endif + +#ifndef SYS_sendmmsg +#define SYS_sendmmsg 307 #endif // Those are initialized in the init(...) method and cached for performance reasons @@ -311,7 +319,8 @@ static jint netty_epoll_native_sendmmsg0(JNIEnv* env, jclass clazz, jint fd, jbo ssize_t res; int err; do { - res = sendmmsg(fd, msg, len, 0); + // We directly use the syscall to prevent depending on GLIBC 2.14. + res = syscall(SYS_sendmmsg, fd, msg, len, 0); // keep on writing if it was interrupted } while (res == -1 && ((err = errno) == EINTR)); @@ -342,8 +351,10 @@ static jint netty_epoll_native_recvmmsg0(JNIEnv* env, jclass clazz, jint fd, jbo ssize_t res; int err; do { - res = recvmmsg(fd, msg, len, 0, NULL); - // keep on reading if it was interrupted + // We directly use the syscall to prevent depending on GLIBC 2.12. + res = syscall(SYS_recvmmsg, fd, &msg, len, 0, NULL); + + // keep on reading if it was interrupted } while (res == -1 && ((err = errno) == EINTR)); if (res < 0) { @@ -394,14 +405,22 @@ static jstring netty_epoll_native_kernelVersion(JNIEnv* env, jclass clazz) { netty_unix_errors_throwRuntimeExceptionErrorNo(env, "uname() failed: ", errno); return NULL; } - static jboolean netty_epoll_native_isSupportingSendmmsg(JNIEnv* env, jclass clazz) { - // Use & to avoid warnings with -Wtautological-pointer-compare when sendmmsg is - // not weakly defined. - if (&sendmmsg != NULL) { - return JNI_TRUE; + if (syscall(SYS_sendmmsg, -1, NULL, 0, 0) == -1) { + if (errno == ENOSYS) { + return JNI_FALSE; + } } - return JNI_FALSE; + return JNI_TRUE; +} + +static jboolean netty_epoll_native_isSupportingRecvmmsg(JNIEnv* env, jclass clazz) { + if (syscall(SYS_recvmmsg, -1, NULL, 0, 0, NULL) == -1) { + if (errno == ENOSYS) { + return JNI_FALSE; + } + } + return JNI_TRUE; } static jboolean netty_epoll_native_isSupportingTcpFastopen(JNIEnv* env, jclass clazz) { @@ -482,6 +501,7 @@ static const JNINativeMethod statically_referenced_fixed_method_table[] = { { "epollerr", "()I", (void *) netty_epoll_native_epollerr }, { "tcpMd5SigMaxKeyLen", "()I", (void *) netty_epoll_native_tcpMd5SigMaxKeyLen }, { "isSupportingSendmmsg", "()Z", (void *) netty_epoll_native_isSupportingSendmmsg }, + { "isSupportingRecvmmsg", "()Z", (void *) netty_epoll_native_isSupportingRecvmmsg }, { "isSupportingTcpFastopen", "()Z", (void *) netty_epoll_native_isSupportingTcpFastopen }, { "kernelVersion", "()Ljava/lang/String;", (void *) netty_epoll_native_kernelVersion } }; diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollDatagramChannel.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollDatagramChannel.java index cb611ab41d..06c9f4b39e 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollDatagramChannel.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollDatagramChannel.java @@ -497,7 +497,11 @@ public final class EpollDatagramChannel extends AbstractEpollChannel implements ByteBuf byteBuf = allocHandle.allocate(allocator); final boolean read; int datagramSize = config().getMaxDatagramPayloadSize(); - int numDatagram = datagramSize == 0 ? 1 : byteBuf.writableBytes() / datagramSize; + + // Only try to use recvmmsg if its really supported by the running system. + int numDatagram = Native.IS_SUPPORTING_RECVMMSG ? + datagramSize == 0 ? 1 : byteBuf.writableBytes() / datagramSize : + 0; try { if (numDatagram <= 1) { diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/Native.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/Native.java index d0d1051edc..437e0efcda 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/Native.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/Native.java @@ -32,6 +32,7 @@ import static io.netty.channel.epoll.NativeStaticallyReferencedJniMethods.epolle import static io.netty.channel.epoll.NativeStaticallyReferencedJniMethods.epollin; import static io.netty.channel.epoll.NativeStaticallyReferencedJniMethods.epollout; import static io.netty.channel.epoll.NativeStaticallyReferencedJniMethods.epollrdhup; +import static io.netty.channel.epoll.NativeStaticallyReferencedJniMethods.isSupportingRecvmmsg; import static io.netty.channel.epoll.NativeStaticallyReferencedJniMethods.isSupportingSendmmsg; import static io.netty.channel.epoll.NativeStaticallyReferencedJniMethods.isSupportingTcpFastopen; import static io.netty.channel.epoll.NativeStaticallyReferencedJniMethods.kernelVersion; @@ -67,6 +68,8 @@ public final class Native { public static final int EPOLLERR = epollerr(); public static final boolean IS_SUPPORTING_SENDMMSG = isSupportingSendmmsg(); + static final boolean IS_SUPPORTING_RECVMMSG = isSupportingRecvmmsg(); + public static final boolean IS_SUPPORTING_TCP_FASTOPEN = isSupportingTcpFastopen(); public static final int TCP_MD5SIG_MAXKEYLEN = tcpMd5SigMaxKeyLen(); public static final String KERNEL_VERSION = kernelVersion(); diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/NativeStaticallyReferencedJniMethods.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/NativeStaticallyReferencedJniMethods.java index 4f6f99c528..9b20d686f0 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/NativeStaticallyReferencedJniMethods.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/NativeStaticallyReferencedJniMethods.java @@ -40,6 +40,7 @@ final class NativeStaticallyReferencedJniMethods { static native int iovMax(); static native int uioMaxIov(); static native boolean isSupportingSendmmsg(); + static native boolean isSupportingRecvmmsg(); static native boolean isSupportingTcpFastopen(); static native String kernelVersion(); } diff --git a/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollDatagramScatteringReadTest.java b/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollDatagramScatteringReadTest.java index 0142a7ebc6..6cb62808ed 100644 --- a/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollDatagramScatteringReadTest.java +++ b/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollDatagramScatteringReadTest.java @@ -26,6 +26,8 @@ import io.netty.channel.socket.DatagramPacket; import io.netty.testsuite.transport.TestsuitePermutation; import io.netty.testsuite.transport.socket.AbstractDatagramTest; import io.netty.util.internal.PlatformDependent; +import org.junit.Assume; +import org.junit.BeforeClass; import org.junit.Test; import java.net.InetSocketAddress; @@ -43,6 +45,11 @@ import static org.junit.Assert.fail; public class EpollDatagramScatteringReadTest extends AbstractDatagramTest { + @BeforeClass + public static void assumeRecvmmsgSupported() { + Assume.assumeTrue(Native.IS_SUPPORTING_RECVMMSG); + } + @Override protected List> newFactories() { return EpollSocketTestPermutation.INSTANCE.epollOnlyDatagram(internetProtocolFamily());