From 77b7c1a56dcd4fc964137f21caf321a1ca19c0ed Mon Sep 17 00:00:00 2001 From: Chris Vest Date: Fri, 15 Oct 2021 15:18:48 +0200 Subject: [PATCH] Make it possible to query KQueue if TCP FastOpen is available (#11766) Motivation: While TCP FastOpen is mostly a pure optimisation (albeit one that demands idempotency of TFO messages), it can still sometimes be useful to query whether it can be expected to be available. This information can for instance be useful for telemetry, where you want to include if TFO is available to the system, to see if it helps overall. For kqueue, it is possible to query `sysctl` to determine if client-side or server-side TFO is available. Modification: Add static methods to KQueue that delegates to IS_SUPPORTING_TCP_FASTOPEN_{CLIENT/SERVER} fields. These fields are populated when Native is initialized, by querying `sysctl` in a platform-specific way. Whether TFO is available is also predicated upon kqueue itself being available. Result: People, who rely on kqueue, can now query their system in pure Java about whether TFO is available or not. Co-authored-by: Norman Maurer --- .../src/main/c/netty_kqueue_native.c | 65 ++++++++++++++++++- .../java/io/netty/channel/kqueue/KQueue.java | 21 ++++++ .../KQueueStaticallyReferencedJniMethods.java | 4 ++ .../java/io/netty/channel/kqueue/Native.java | 22 +++++++ 4 files changed, 109 insertions(+), 3 deletions(-) diff --git a/transport-native-kqueue/src/main/c/netty_kqueue_native.c b/transport-native-kqueue/src/main/c/netty_kqueue_native.c index ceda160a2eb..98f3414cd09 100644 --- a/transport-native-kqueue/src/main/c/netty_kqueue_native.c +++ b/transport-native-kqueue/src/main/c/netty_kqueue_native.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include "netty_kqueue_bsdsocket.h" @@ -259,14 +260,70 @@ static jshort netty_kqueue_native_noteDisconnected(JNIEnv* env, jclass clazz) { return NOTE_DISCONNECTED; } -static jint netty_kqueue_bsdsocket_connectResumeOnReadWrite(JNIEnv *env) { +static jint netty_kqueue_bsdsocket_connectResumeOnReadWrite(JNIEnv *env, jclass clazz) { return CONNECT_RESUME_ON_READ_WRITE; } -static jint netty_kqueue_bsdsocket_connectDataIdempotent(JNIEnv *env) { +static jint netty_kqueue_bsdsocket_connectDataIdempotent(JNIEnv *env, jclass clazz) { return CONNECT_DATA_IDEMPOTENT; } +static jint netty_kqueue_bsdsocket_fastOpenClient(JNIEnv *env, jclass clazz) { +#ifdef __APPLE__ + unsigned int value = 0; + size_t len = sizeof(value); + if (sysctlbyname("net.inet.tcp.fastopen", &value, &len, NULL, 0) != 0) { + int err = errno; + netty_unix_errors_throwRuntimeExceptionErrorNo(env, "sysctlbyname failed", err); + return -err; + } + if ((value & 2) == 2) { + return 1; + } + return 0; +#else + unsigned int value = 0; + size_t len = sizeof(value); + if (sysctlbyname("net.inet.tcp.fastopen.client_enable", &value, &len, NULL, 0) != 0) { + int err = errno; + netty_unix_errors_throwRuntimeExceptionErrorNo(env, "sysctlbyname failed", err); + return -err; + } + if (value) { + return 1; + } + return 0; +#endif // __APPLE__ +} + +static jint netty_kqueue_bsdsocket_fastOpenServer(JNIEnv *env, jclass clazz) { +#ifdef __APPLE__ + unsigned int value = 0; + size_t len = sizeof(value); + if (sysctlbyname("net.inet.tcp.fastopen", &value, &len, NULL, 0) != 0) { + int err = errno; + netty_unix_errors_throwRuntimeExceptionErrorNo(env, "sysctlbyname failed", err); + return -err; + } + if ((value & 1) == 1) { + return 1; + } + return 0; +#else + unsigned int value = 0; + size_t len = sizeof(value); + if (sysctlbyname("net.inet.tcp.fastopen.server_enable", &value, &len, NULL, 0) != 0) { + int err = errno; + netty_unix_errors_throwRuntimeExceptionErrorNo(env, "sysctlbyname failed", err); + return -err; + } + if (value) { + return 1; + } + return 0; +#endif // __APPLE__ +} + // JNI Method Registration Table Begin static const JNINativeMethod statically_referenced_fixed_method_table[] = { { "evfiltRead", "()S", (void *) netty_kqueue_native_evfiltRead }, @@ -284,7 +341,9 @@ static const JNINativeMethod statically_referenced_fixed_method_table[] = { { "noteConnReset", "()S", (void *) netty_kqueue_native_noteConnReset }, { "noteDisconnected", "()S", (void *) netty_kqueue_native_noteDisconnected }, { "connectResumeOnReadWrite", "()I", (void *) netty_kqueue_bsdsocket_connectResumeOnReadWrite }, - { "connectDataIdempotent", "()I", (void *) netty_kqueue_bsdsocket_connectDataIdempotent } + { "connectDataIdempotent", "()I", (void *) netty_kqueue_bsdsocket_connectDataIdempotent }, + { "fastOpenClient", "()I", (void *) netty_kqueue_bsdsocket_fastOpenClient }, + { "fastOpenServer", "()I", (void *) netty_kqueue_bsdsocket_fastOpenServer } }; static const jint statically_referenced_fixed_method_table_size = sizeof(statically_referenced_fixed_method_table) / sizeof(statically_referenced_fixed_method_table[0]); static const JNINativeMethod fixed_method_table[] = { diff --git a/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueue.java b/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueue.java index 83cb496251c..c5ba0bb05a3 100644 --- a/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueue.java +++ b/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueue.java @@ -15,6 +15,7 @@ */ package io.netty.channel.kqueue; +import io.netty.channel.ChannelOption; import io.netty.channel.unix.FileDescriptor; import io.netty.util.internal.SystemPropertyUtil; import io.netty.util.internal.UnstableApi; @@ -82,6 +83,26 @@ public static Throwable unavailabilityCause() { return UNAVAILABILITY_CAUSE; } + /** + * Returns {@code true} if the kqueue native transport is both {@linkplain #isAvailable() available} and supports + * {@linkplain ChannelOption#TCP_FASTOPEN_CONNECT client-side TCP FastOpen}. + * + * @return {@code true} if it's possible to use client-side TCP FastOpen via kqueue, otherwise {@code false}. + */ + public static boolean isTcpFastOpenClientSideAvailable() { + return isAvailable() && Native.IS_SUPPORTING_TCP_FASTOPEN_CLIENT; + } + + /** + * Returns {@code true} if the kqueue native transport is both {@linkplain #isAvailable() available} and supports + * {@linkplain ChannelOption#TCP_FASTOPEN server-side TCP FastOpen}. + * + * @return {@code true} if it's possible to use server-side TCP FastOpen via kqueue, otherwise {@code false}. + */ + public static boolean isTcpFastOpenServerSideAvailable() { + return isAvailable() && Native.IS_SUPPORTING_TCP_FASTOPEN_SERVER; + } + private KQueue() { } } diff --git a/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueueStaticallyReferencedJniMethods.java b/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueueStaticallyReferencedJniMethods.java index e6926d65faf..e4a4a41ce8e 100644 --- a/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueueStaticallyReferencedJniMethods.java +++ b/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueueStaticallyReferencedJniMethods.java @@ -50,4 +50,8 @@ private KQueueStaticallyReferencedJniMethods() { } // Flags for connectx(2) static native int connectResumeOnReadWrite(); static native int connectDataIdempotent(); + + // Sysctl values. + static native int fastOpenClient(); + static native int fastOpenServer(); } diff --git a/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/Native.java b/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/Native.java index 5f4c77fbc2b..20a09f49b14 100644 --- a/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/Native.java +++ b/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/Native.java @@ -42,6 +42,8 @@ import static io.netty.channel.kqueue.KQueueStaticallyReferencedJniMethods.evfiltSock; import static io.netty.channel.kqueue.KQueueStaticallyReferencedJniMethods.evfiltUser; import static io.netty.channel.kqueue.KQueueStaticallyReferencedJniMethods.evfiltWrite; +import static io.netty.channel.kqueue.KQueueStaticallyReferencedJniMethods.fastOpenClient; +import static io.netty.channel.kqueue.KQueueStaticallyReferencedJniMethods.fastOpenServer; import static io.netty.channel.kqueue.KQueueStaticallyReferencedJniMethods.noteConnReset; import static io.netty.channel.kqueue.KQueueStaticallyReferencedJniMethods.noteDisconnected; import static io.netty.channel.kqueue.KQueueStaticallyReferencedJniMethods.noteReadClosed; @@ -110,6 +112,8 @@ public void run() { private static final int CONNECT_RESUME_ON_READ_WRITE = connectResumeOnReadWrite(); private static final int CONNECT_DATA_IDEMPOTENT = connectDataIdempotent(); static final int CONNECT_TCP_FASTOPEN = CONNECT_RESUME_ON_READ_WRITE | CONNECT_DATA_IDEMPOTENT; + static final boolean IS_SUPPORTING_TCP_FASTOPEN_CLIENT = isSupportingFastOpenClient(); + static final boolean IS_SUPPORTING_TCP_FASTOPEN_SERVER = isSupportingFastOpenServer(); static FileDescriptor newKQueue() { return new FileDescriptor(kqueueCreate()); @@ -160,6 +164,24 @@ private static void loadNativeLibrary() { } } + private static boolean isSupportingFastOpenClient() { + try { + return fastOpenClient() == 1; + } catch (Exception e) { + logger.debug("Failed to probe fastOpenClient sysctl, assuming client-side TCP FastOpen cannot be used.", e); + } + return false; + } + + private static boolean isSupportingFastOpenServer() { + try { + return fastOpenServer() == 1; + } catch (Exception e) { + logger.debug("Failed to probe fastOpenServer sysctl, assuming server-side TCP FastOpen cannot be used.", e); + } + return false; + } + private Native() { // utility }