Skip to content

Commit

Permalink
Remove dependency on GLIBC 2.12 by using syscalls directly (netty#9797)
Browse files Browse the repository at this point in the history
Motivation:

394a1b3 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 netty#9758.
  • Loading branch information
normanmaurer authored Nov 23, 2019
1 parent b413464 commit 38109b2
Show file tree
Hide file tree
Showing 6 changed files with 49 additions and 109 deletions.
97 changes: 1 addition & 96 deletions transport-native-epoll/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
<unix.common.lib.dir>${project.build.directory}/unix-common-lib</unix.common.lib.dir>
<unix.common.lib.unpacked.dir>${unix.common.lib.dir}/META-INF/native/lib</unix.common.lib.unpacked.dir>
<unix.common.include.unpacked.dir>${unix.common.lib.dir}/META-INF/native/include</unix.common.include.unpacked.dir>
<jni.compiler.args.cflags>CFLAGS=-O3 -Werror -fno-omit-frame-pointer -Wunused-variable -fvisibility=hidden -I${unix.common.include.unpacked.dir}</jni.compiler.args.cflags>
<jni.compiler.args.ldflags>LDFLAGS=-L${unix.common.lib.unpacked.dir} -Wl,--no-as-needed -lrt -Wl,--whole-archive -l${unix.common.lib.name} -Wl,--no-whole-archive</jni.compiler.args.ldflags>
<nativeSourceDirectory>${project.basedir}/src/main/c</nativeSourceDirectory>
<skipTests>true</skipTests>
Expand Down Expand Up @@ -192,102 +193,6 @@
</execution>
</executions>
</plugin>

<plugin>
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<execution>
<!-- Phase must be before regex-glibc-sendmmsg and regex-linux-sendmmsg -->
<phase>validate</phase>
<goals>
<goal>run</goal>
</goals>
<id>ant-get-systeminfo</id>
<configuration>
<exportAntProperties>true</exportAntProperties>
<tasks>
<exec executable="sh" outputproperty="ldd_version">
<arg value="-c" />
<arg value="ldd --version | tail | head -1" />
</exec>
<exec executable="uname" outputproperty="uname_os_version">
<arg value="-r" />
</exec>
</tasks>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<executions>
<execution>
<!-- Phase must be before regex-combined-sendmmsg -->
<phase>initialize</phase>
<id>regex-glibc-sendmmsg</id>
<goals>
<goal>regex-property</goal>
</goals>
<configuration>
<name>glibc.sendmmsg.support</name>
<value>${ldd_version}</value>
<!-- Version must be >= 2.14 - set to IO_NETTY_SENDMSSG_NOT_FOUND if this version is not satisfied -->
<regex>^((?!^[^)]+\)\s+(0*2\.1[4-9]|0*2\.[2-9][0-9]+|0*[3-9][0-9]*|0*[1-9]+[0-9]+).*).)*$</regex>
<replacement>IO_NETTY_SENDMSSG_NOT_FOUND</replacement>
<failIfNoMatch>false</failIfNoMatch>
</configuration>
</execution>
<execution>
<!-- Phase must be before regex-combined-sendmmsg -->
<phase>initialize</phase>
<id>regex-linux-sendmmsg</id>
<goals>
<goal>regex-property</goal>
</goals>
<configuration>
<name>linux.sendmmsg.support</name>
<value>${uname_os_version}</value>
<!-- Version must be >= 3 - set to IO_NETTY_SENDMSSG_NOT_FOUND if this version is not satisfied -->
<regex>^((?!^[0-9]*[3-9]\.?.*).)*$</regex>
<replacement>IO_NETTY_SENDMSSG_NOT_FOUND</replacement>
<failIfNoMatch>false</failIfNoMatch>
</configuration>
</execution>
<execution>
<!-- Phase must be before regex-unset-if-needed-sendmmsg -->
<phase>generate-sources</phase>
<id>regex-combined-sendmmsg</id>
<goals>
<goal>regex-property</goal>
</goals>
<configuration>
<name>jni.compiler.args.cflags</name>
<value>${linux.sendmmsg.support}${glibc.sendmmsg.support}</value>
<!-- If glibc and linux kernel are both not sufficient...then define the CFLAGS -->
<regex>.*IO_NETTY_SENDMSSG_NOT_FOUND.*</regex>
<replacement>CFLAGS=-O3 -DIO_NETTY_SENDMMSG_NOT_FOUND -Werror -fno-omit-frame-pointer -Wunused-variable -fvisibility=hidden -I${unix.common.include.unpacked.dir}</replacement>
<failIfNoMatch>false</failIfNoMatch>
</configuration>
</execution>
<execution>
<!-- Phase must be before build-native-lib -->
<phase>generate-sources</phase>
<id>regex-unset-if-needed-sendmmsg</id>
<goals>
<goal>regex-property</goal>
</goals>
<configuration>
<name>jni.compiler.args.cflags</name>
<value>${jni.compiler.args.cflags}</value>
<!-- If glibc and linux kernel are both not sufficient...then define the CFLAGS -->
<regex>^((?!CFLAGS=).)*$</regex>
<replacement>CFLAGS=-O3 -Werror -fno-omit-frame-pointer -Wunused-variable -fvisibility=hidden -I${unix.common.include.unpacked.dir}</replacement>
<failIfNoMatch>false</failIfNoMatch>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>

Expand Down
44 changes: 32 additions & 12 deletions transport-native-epoll/src/main/c/netty_epoll_native.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@
#include <inttypes.h>
#include <link.h>
#include <time.h>
// Needed to be able to use syscalls directly and so not depend on newer GLIBC versions
#include <linux/net.h>
#include <sys/syscall.h>

#include "netty_epoll_linuxsocket.h"
#include "netty_unix_buffer.h"
Expand All @@ -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
Expand Down Expand Up @@ -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));

Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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 }
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -497,7 +497,11 @@ void epollInReady() {
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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
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;
Expand Down Expand Up @@ -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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ private 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();
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
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;
Expand All @@ -43,6 +45,11 @@

public class EpollDatagramScatteringReadTest extends AbstractDatagramTest {

@BeforeClass
public static void assumeRecvmmsgSupported() {
Assume.assumeTrue(Native.IS_SUPPORTING_RECVMMSG);
}

@Override
protected List<TestsuitePermutation.BootstrapComboFactory<Bootstrap, Bootstrap>> newFactories() {
return EpollSocketTestPermutation.INSTANCE.epollOnlyDatagram(internetProtocolFamily());
Expand Down

0 comments on commit 38109b2

Please sign in to comment.