Skip to content

Commit

Permalink
Make it possible to query KQueue if TCP FastOpen is available (netty#…
Browse files Browse the repository at this point in the history
…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 <norman_maurer@apple.com>
  • Loading branch information
2 people authored and laosijikaichele committed Dec 16, 2021
1 parent 35a88aa commit 29dce22
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 3 deletions.
65 changes: 62 additions & 3 deletions transport-native-kqueue/src/main/c/netty_kqueue_native.c
Expand Up @@ -24,6 +24,7 @@
#include <time.h>
#include <sys/event.h>
#include <sys/socket.h>
#include <sys/sysctl.h>
#include <sys/time.h>

#include "netty_kqueue_bsdsocket.h"
Expand Down Expand Up @@ -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 },
Expand All @@ -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[] = {
Expand Down
Expand Up @@ -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;
Expand Down Expand Up @@ -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() {
}
}
Expand Up @@ -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();
}
Expand Up @@ -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;
Expand Down Expand Up @@ -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());
Expand Down Expand Up @@ -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
}
Expand Down

0 comments on commit 29dce22

Please sign in to comment.