Skip to content

Commit ec465e7

Browse files
IoUring: Cache the result of ioUringProbe to speed up the initialization of IoUring (#15482)
Motivation: We can cache the probe result to speed up initialization speed. Modification: - Cache the result of ioUringProbe to speed up the initialization of the io.netty.channel.uring.Native class. Result: Speed up the initialization of IoUring class initialization --------- Co-authored-by: Norman Maurer <norman_maurer@apple.com>
1 parent a9234c3 commit ec465e7

4 files changed

Lines changed: 106 additions & 36 deletions

File tree

transport-classes-io_uring/src/main/java/io/netty/channel/uring/IoUring.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -95,16 +95,17 @@ public final class IoUring {
9595
// 160kb.
9696
numElementsIoVec = SystemPropertyUtil.getInt(
9797
"io.netty.iouring.numElementsIoVec", 10 * Limits.IOV_MAX);
98-
Native.checkAllIOSupported(ringBuffer.fd());
99-
socketNonEmptySupported = Native.isCqeFSockNonEmptySupported(ringBuffer.fd());
100-
spliceSupported = Native.isSpliceSupported(ringBuffer.fd());
98+
Native.IoUringProbe ioUringProbe = Native.ioUringProbe(ringBuffer.fd());
99+
Native.checkAllIOSupported(ioUringProbe);
100+
socketNonEmptySupported = Native.isCqeFSockNonEmptySupported(ioUringProbe);
101+
spliceSupported = Native.isSpliceSupported(ioUringProbe);
101102
recvsendBundleSupported = (ringBuffer.features() & Native.IORING_FEAT_RECVSEND_BUNDLE) != 0;
102103
// IORING_FEAT_RECVSEND_BUNDLE was added in the same release.
103104
acceptSupportNoWait = recvsendBundleSupported;
104105

105-
acceptMultishotSupported = Native.isAcceptMultishotSupported(ringBuffer.fd());
106+
acceptMultishotSupported = Native.isAcceptMultishotSupported(ioUringProbe);
106107
recvMultishotSupported = Native.isRecvMultishotSupported();
107-
pollAddMultishotSupported = Native.isPollAddMultiShotSupported(ringBuffer.fd());
108+
pollAddMultishotSupported = Native.isPollAddMultiShotSupported(ioUringProbe);
108109
registerIowqWorkersSupported = Native.isRegisterIoWqWorkerSupported(ringBuffer.fd());
109110
submitAllSupported = Native.ioUringSetupSupportsFlags(Native.IORING_SETUP_SUBMIT_ALL);
110111
setUpCqSizeSupported = Native.ioUringSetupSupportsFlags(Native.IORING_SETUP_CQSIZE);

transport-classes-io_uring/src/main/java/io/netty/channel/uring/Native.java

Lines changed: 61 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,8 @@ final class Native {
244244
static final int SPLICE_F_MOVE = 1;
245245

246246
static final int IOU_PBUF_RING_INC = 2;
247+
248+
static final int IO_URING_OP_SUPPORTED = 1;
247249
static String opToStr(byte op) {
248250
switch (op) {
249251
case IORING_OP_NOP: return "NOP";
@@ -408,8 +410,8 @@ static RingBuffer createRingBuffer(int ringSize, int cqeSize, int setupFlags) {
408410
return new RingBuffer(submissionQueue, completionQueue, (int) values[17]);
409411
}
410412

411-
static void checkAllIOSupported(int ringFd) {
412-
if (!ioUringProbe(ringFd, REQUIRED_IORING_OPS)) {
413+
static void checkAllIOSupported(IoUringProbe probe) {
414+
if (!ioUringProbe(probe, REQUIRED_IORING_OPS)) {
413415
throw new UnsupportedOperationException("Not all operations are supported: "
414416
+ Arrays.toString(REQUIRED_IORING_OPS));
415417
}
@@ -420,24 +422,24 @@ static boolean isRecvMultishotSupported() {
420422
return Native.ioUringSetupSupportsFlags(Native.IORING_SETUP_SINGLE_ISSUER);
421423
}
422424

423-
static boolean isAcceptMultishotSupported(int ringFd) {
425+
static boolean isAcceptMultishotSupported(IoUringProbe probe) {
424426
// IORING_OP_SOCKET was added in the same release (5.19);
425-
return ioUringProbe(ringFd, new int[] { Native.IORING_OP_SOCKET });
427+
return ioUringProbe(probe, new int[] { Native.IORING_OP_SOCKET });
426428
}
427429

428-
static boolean isCqeFSockNonEmptySupported(int ringFd) {
430+
static boolean isCqeFSockNonEmptySupported(IoUringProbe probe) {
429431
// IORING_OP_SOCKET was added in the same release (5.19);
430-
return ioUringProbe(ringFd, new int[] { Native.IORING_OP_SOCKET });
432+
return ioUringProbe(probe, new int[] { Native.IORING_OP_SOCKET });
431433
}
432434

433-
static boolean isSpliceSupported(int ringFd) {
435+
static boolean isSpliceSupported(IoUringProbe probe) {
434436
// IORING_OP_SPLICE Available since 5.7
435-
return ioUringProbe(ringFd, new int[] { Native.IORING_OP_SPLICE });
437+
return ioUringProbe(probe, new int[] { Native.IORING_OP_SPLICE });
436438
}
437439

438-
static boolean isPollAddMultiShotSupported(int ringfd) {
440+
static boolean isPollAddMultiShotSupported(IoUringProbe probe) {
439441
// Was added in the same release and we also need this feature to correctly handle edge-triggered mode.
440-
return isCqeFSockNonEmptySupported(ringfd);
442+
return isCqeFSockNonEmptySupported(probe);
441443
}
442444

443445
/**
@@ -512,10 +514,57 @@ private static boolean checkKernelVersion(String kernelVersion, int major, int m
512514
return nativeMinor >= minor;
513515
}
514516

515-
static native boolean ioUringSetupSupportsFlags(int setupFlags);
516-
private static native boolean ioUringProbe(int ringFd, int[] ios);
517+
static final class IoUringProbe {
518+
final byte lastOp;
519+
final byte opsLen;
520+
final IoUringProbeOp[] ops;
521+
522+
IoUringProbe(int[] values) {
523+
int idx = 0;
524+
lastOp = (byte) values[idx++];
525+
opsLen = (byte) values[idx++];
526+
ops = new IoUringProbeOp[opsLen];
527+
for (int i = 0; i < opsLen; i++) {
528+
ops[i] = new IoUringProbeOp((byte) values[idx++], values[idx++]);
529+
}
530+
}
531+
}
532+
533+
static class IoUringProbeOp {
534+
final byte op;
535+
final int flags;
536+
537+
IoUringProbeOp(byte op, int flags) {
538+
this.op = op;
539+
this.flags = flags;
540+
}
541+
}
542+
543+
static boolean ioUringProbe(IoUringProbe probe, int[] ops) {
544+
IoUringProbeOp[] ioUringProbeOps = probe.ops;
545+
if (ioUringProbeOps == null) {
546+
return false;
547+
}
548+
for (int op : ops) {
549+
if (op > probe.lastOp || (ioUringProbeOps[op].flags & IO_URING_OP_SUPPORTED) == 0) {
550+
return false;
551+
}
552+
}
553+
return true;
554+
}
555+
556+
static native boolean ioUringSetupSupportsFlags(int setupFlags);;
517557
private static native long[] ioUringSetup(int entries, int cqeSize, int setupFlags);
518558

559+
static IoUringProbe ioUringProbe(int ringfd) {
560+
int[] values = ioUringProbe0(ringfd);
561+
if (values == null) {
562+
return null;
563+
}
564+
return new IoUringProbe(values);
565+
}
566+
private static native int[] ioUringProbe0(int ringFd);
567+
519568
static native int ioUringRegisterIoWqMaxWorkers(int ringFd, int maxBoundedValue, int maxUnboundedValue);
520569
static native int ioUringRegisterEnableRings(int ringFd);
521570
static native int ioUringRegisterRingFds(int ringFds);

transport-native-io_uring/src/main/c/netty_io_uring_native.c

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -273,38 +273,41 @@ static jboolean netty_io_uring_setup_supports_flags(JNIEnv *env, jclass clazz, j
273273
return JNI_TRUE;
274274
}
275275

276-
static jboolean netty_io_uring_probe(JNIEnv *env, jclass clazz, jint ring_fd, jintArray ops) {
277-
jboolean supported = JNI_FALSE;
276+
static jintArray netty_io_uring_probe0(JNIEnv *env, jclass clazz, jint ring_fd) {
277+
jintArray array = NULL;
278278
struct io_uring_probe *probe;
279279
size_t mallocLen = sizeof(*probe) + 256 * sizeof(struct io_uring_probe_op);
280280
probe = malloc(mallocLen);
281281
memset(probe, 0, mallocLen);
282282

283283
if (sys_io_uring_register(ring_fd, IORING_REGISTER_PROBE, probe, 256) < 0) {
284284
netty_unix_errors_throwRuntimeExceptionErrorNo(env, "failed to probe via sys_io_uring_register(....) ", errno);
285-
goto done;
285+
goto cleanup;
286286
}
287287

288-
jsize opsLen = (*env)->GetArrayLength(env, ops);
289-
jint *opsElements = (*env)->GetIntArrayElements(env, ops, 0);
290-
if (opsElements == NULL) {
291-
goto done;
288+
array = (*env)->NewIntArray(env, 2 + probe->ops_len * 2);
289+
if (array == NULL) {
290+
goto cleanup;
292291
}
293-
int i;
294-
for (i = 0; i < opsLen; i++) {
295-
int op = opsElements[i];
296-
if (op > probe->last_op || (probe->ops[op].flags & IO_URING_OP_SUPPORTED) == 0) {
297-
goto done;
298-
}
292+
int idx = 0;
293+
jint lastOp = (jint) probe->last_op;
294+
(*env)->SetIntArrayRegion(env, array, idx++, 1, &lastOp);
295+
296+
jint opsLen = (jint) probe->ops_len;
297+
(*env)->SetIntArrayRegion(env, array, idx++, 1, &opsLen);
298+
299+
for (int i = 0; i < probe->ops_len; ++i) {
300+
jint op = (jint) probe->ops[i].op;
301+
(*env)->SetIntArrayRegion(env, array, idx++, 1, &op);
302+
303+
jint flags = (jint) probe->ops[i].flags;
304+
(*env)->SetIntArrayRegion(env, array, idx++, 1, &flags);
299305
}
300-
// all supported
301-
supported = JNI_TRUE;
302-
done:
306+
cleanup:
303307
free(probe);
304-
return supported;
308+
return array;
305309
}
306310

307-
308311
static jlongArray netty_io_uring_setup(JNIEnv *env, jclass clazz, jint entries, jint cqSize, jint setupFlags) {
309312
struct io_uring_params p;
310313
memset(&p, 0, sizeof(p));
@@ -832,7 +835,7 @@ static const JNINativeMethod method_table[] = {
832835
{"ioUringRegisterIoWqMaxWorkers","(III)I", (void*) netty_io_uring_register_iowq_max_workers },
833836
{"ioUringRegisterEnableRings","(I)I", (void*) netty_io_uring_register_enable_rings },
834837
{"ioUringRegisterRingFds","(I)I", (void*) netty_io_uring_register_ring_fds },
835-
{"ioUringProbe", "(I[I)Z", (void *) netty_io_uring_probe},
838+
{"ioUringProbe0", "(I)[I", (void *) netty_io_uring_probe0},
836839
{"ioUringExit", "(JIJIJIII)V", (void *) netty_io_uring_ring_buffer_exit},
837840
{"createFile", "(Ljava/lang/String;)I", (void *) netty_create_file},
838841
{"ioUringEnter", "(IIII)I", (void *) netty_io_uring_enter},

transport-native-io_uring/src/test/java/io/netty/channel/uring/SubmissionQueueTest.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,4 +118,21 @@ public void testSetUpCqSize() {
118118
private static boolean setUpCQSizeUnavailable() {
119119
return !IoUring.isSetupCqeSizeSupported();
120120
}
121+
122+
@Test
123+
public void testIoUringProbeSupported() {
124+
RingBuffer ringBuffer = Native.createRingBuffer(8, 0);
125+
Native.IoUringProbe ioUringProbe = Native.ioUringProbe(ringBuffer.fd());
126+
assertNotNull(ioUringProbe);
127+
assertNotEquals(0, ioUringProbe.lastOp);
128+
assertNotEquals(0, ioUringProbe.opsLen);
129+
assertNotNull(ioUringProbe.ops);
130+
assertFalse(Native.ioUringProbe(ioUringProbe, new int[] {Integer.MAX_VALUE}));
131+
assertDoesNotThrow(() -> Native.checkAllIOSupported(ioUringProbe));
132+
133+
// Let's mark it as not supported.
134+
ioUringProbe.ops[Native.IORING_OP_READ] = new Native.IoUringProbeOp(Native.IORING_OP_READ, 0);
135+
assertFalse(Native.ioUringProbe(ioUringProbe, new int[] {Native.IORING_OP_READ}));
136+
assertThrows(UnsupportedOperationException.class, () -> Native.checkAllIOSupported(ioUringProbe));
137+
}
121138
}

0 commit comments

Comments
 (0)