View Javadoc
1   /*
2    * Copyright 2024 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   https://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  package io.netty.channel.uring;
17  
18  import io.netty.channel.DefaultFileRegion;
19  import io.netty.channel.unix.Buffer;
20  import io.netty.util.internal.ObjectUtil;
21  import io.netty.util.internal.logging.InternalLogger;
22  import io.netty.util.internal.logging.InternalLoggerFactory;
23  import io.netty.channel.unix.FileDescriptor;
24  import io.netty.channel.unix.PeerCredentials;
25  import io.netty.channel.unix.Unix;
26  import io.netty.util.internal.ClassInitializerUtil;
27  import io.netty.util.internal.NativeLibraryLoader;
28  import io.netty.util.internal.PlatformDependent;
29  import io.netty.util.internal.SystemPropertyUtil;
30  import io.netty.util.internal.ThrowableUtil;
31  
32  import java.io.File;
33  import java.io.IOException;
34  import java.nio.channels.Selector;
35  import java.nio.file.Path;
36  import java.util.Arrays;
37  import java.util.Locale;
38  
39  final class Native {
40      private static final InternalLogger logger = InternalLoggerFactory.getInstance(Native.class);
41      static final int DEFAULT_RING_SIZE = Math.max(64, SystemPropertyUtil.getInt("io.netty.iouring.ringSize", 4096));
42  
43      static {
44          Selector selector = null;
45          try {
46              // We call Selector.open() as this will under the hood cause IOUtil to be loaded.
47              // This is a workaround for a possible classloader deadlock that could happen otherwise:
48              //
49              // See https://github.com/netty/netty/issues/10187
50              selector = Selector.open();
51          } catch (IOException ignore) {
52              // Just ignore
53          }
54  
55          // Preload all classes that will be used in the OnLoad(...) function of JNI to eliminate the possiblity of a
56          // class-loader deadlock. This is a workaround for https://github.com/netty/netty/issues/11209.
57  
58          // This needs to match all the classes that are loaded via NETTY_JNI_UTIL_LOAD_CLASS or looked up via
59          // NETTY_JNI_UTIL_FIND_CLASS.
60          ClassInitializerUtil.tryLoadClasses(
61                  Native.class,
62                  // netty_io_uring_linuxsocket
63                  PeerCredentials.class, java.io.FileDescriptor.class
64          );
65  
66          File tmpDir = PlatformDependent.tmpdir();
67          Path tmpFile = tmpDir.toPath().resolve("netty_io_uring.tmp");
68          try {
69              // First, try calling a side-effect free JNI method to see if the library was already
70              // loaded by the application.
71              Native.createFile(tmpFile.toString());
72          } catch (UnsatisfiedLinkError ignore) {
73              // The library was not previously loaded, load it now.
74              loadNativeLibrary();
75          } finally {
76              tmpFile.toFile().delete();
77              try {
78                  if (selector != null) {
79                      selector.close();
80                  }
81              } catch (IOException ignore) {
82                  // Just ignore
83              }
84          }
85          Unix.registerInternal(Native::registerUnix);
86      }
87  
88      static final int SOCK_NONBLOCK = NativeStaticallyReferencedJniMethods.sockNonblock();
89      static final int SOCK_CLOEXEC = NativeStaticallyReferencedJniMethods.sockCloexec();
90      static final short AF_INET = (short) NativeStaticallyReferencedJniMethods.afInet();
91      static final short AF_INET6 = (short) NativeStaticallyReferencedJniMethods.afInet6();
92      static final int SIZEOF_SOCKADDR_STORAGE = NativeStaticallyReferencedJniMethods.sizeofSockaddrStorage();
93      static final int SIZEOF_SOCKADDR_IN = NativeStaticallyReferencedJniMethods.sizeofSockaddrIn();
94      static final int SIZEOF_SOCKADDR_IN6 = NativeStaticallyReferencedJniMethods.sizeofSockaddrIn6();
95      static final int SOCKADDR_IN_OFFSETOF_SIN_FAMILY =
96              NativeStaticallyReferencedJniMethods.sockaddrInOffsetofSinFamily();
97      static final int SOCKADDR_IN_OFFSETOF_SIN_PORT = NativeStaticallyReferencedJniMethods.sockaddrInOffsetofSinPort();
98      static final int SOCKADDR_IN_OFFSETOF_SIN_ADDR = NativeStaticallyReferencedJniMethods.sockaddrInOffsetofSinAddr();
99      static final int IN_ADDRESS_OFFSETOF_S_ADDR = NativeStaticallyReferencedJniMethods.inAddressOffsetofSAddr();
100     static final int SOCKADDR_IN6_OFFSETOF_SIN6_FAMILY =
101             NativeStaticallyReferencedJniMethods.sockaddrIn6OffsetofSin6Family();
102     static final int SOCKADDR_IN6_OFFSETOF_SIN6_PORT =
103             NativeStaticallyReferencedJniMethods.sockaddrIn6OffsetofSin6Port();
104     static final int SOCKADDR_IN6_OFFSETOF_SIN6_FLOWINFO =
105             NativeStaticallyReferencedJniMethods.sockaddrIn6OffsetofSin6Flowinfo();
106     static final int SOCKADDR_IN6_OFFSETOF_SIN6_ADDR =
107             NativeStaticallyReferencedJniMethods.sockaddrIn6OffsetofSin6Addr();
108     static final int SOCKADDR_IN6_OFFSETOF_SIN6_SCOPE_ID =
109             NativeStaticallyReferencedJniMethods.sockaddrIn6OffsetofSin6ScopeId();
110     static final int IN6_ADDRESS_OFFSETOF_S6_ADDR = NativeStaticallyReferencedJniMethods.in6AddressOffsetofS6Addr();
111     static final int SIZEOF_SIZE_T = NativeStaticallyReferencedJniMethods.sizeofSizeT();
112     static final int SIZEOF_IOVEC = NativeStaticallyReferencedJniMethods.sizeofIovec();
113     static final int CMSG_SPACE = NativeStaticallyReferencedJniMethods.cmsgSpace();
114     static final int CMSG_LEN = NativeStaticallyReferencedJniMethods.cmsgLen();
115     static final int CMSG_OFFSETOF_CMSG_LEN = NativeStaticallyReferencedJniMethods.cmsghdrOffsetofCmsgLen();
116     static final int CMSG_OFFSETOF_CMSG_LEVEL = NativeStaticallyReferencedJniMethods.cmsghdrOffsetofCmsgLevel();
117     static final int CMSG_OFFSETOF_CMSG_TYPE = NativeStaticallyReferencedJniMethods.cmsghdrOffsetofCmsgType();
118 
119     static final int IO_URING_BUFFER_RING_TAIL = NativeStaticallyReferencedJniMethods.ioUringBufferRingOffsetTail();
120 
121     static final int IOVEC_OFFSETOF_IOV_BASE = NativeStaticallyReferencedJniMethods.iovecOffsetofIovBase();
122     static final int IOVEC_OFFSETOF_IOV_LEN = NativeStaticallyReferencedJniMethods.iovecOffsetofIovLen();
123     static final int SIZEOF_MSGHDR = NativeStaticallyReferencedJniMethods.sizeofMsghdr();
124     static final int MSGHDR_OFFSETOF_MSG_NAME = NativeStaticallyReferencedJniMethods.msghdrOffsetofMsgName();
125     static final int MSGHDR_OFFSETOF_MSG_NAMELEN = NativeStaticallyReferencedJniMethods.msghdrOffsetofMsgNamelen();
126     static final int MSGHDR_OFFSETOF_MSG_IOV = NativeStaticallyReferencedJniMethods.msghdrOffsetofMsgIov();
127     static final int MSGHDR_OFFSETOF_MSG_IOVLEN = NativeStaticallyReferencedJniMethods.msghdrOffsetofMsgIovlen();
128     static final int MSGHDR_OFFSETOF_MSG_CONTROL = NativeStaticallyReferencedJniMethods.msghdrOffsetofMsgControl();
129     static final int MSGHDR_OFFSETOF_MSG_CONTROLLEN =
130             NativeStaticallyReferencedJniMethods.msghdrOffsetofMsgControllen();
131     static final int MSGHDR_OFFSETOF_MSG_FLAGS = NativeStaticallyReferencedJniMethods.msghdrOffsetofMsgFlags();
132     static final int POLLIN = NativeStaticallyReferencedJniMethods.pollin();
133     static final int POLLOUT = NativeStaticallyReferencedJniMethods.pollout();
134     static final int POLLRDHUP = NativeStaticallyReferencedJniMethods.pollrdhup();
135     static final int ERRNO_ECANCELED_NEGATIVE = -NativeStaticallyReferencedJniMethods.ecanceled();
136     static final int ERRNO_ETIME_NEGATIVE = -NativeStaticallyReferencedJniMethods.etime();
137     static final int ERRNO_NOBUFS_NEGATIVE = -NativeStaticallyReferencedJniMethods.enobufs();
138 
139     static final int PAGE_SIZE = NativeStaticallyReferencedJniMethods.pageSize();
140 
141     static final int SIZEOF_IOURING_BUF = NativeStaticallyReferencedJniMethods.sizeofIoUringBuf();
142     static final int IOURING_BUFFER_OFFSETOF_ADDR = NativeStaticallyReferencedJniMethods.ioUringBufferOffsetAddr();
143     static final int IOURING_BUFFER_OFFSETOF_LEN = NativeStaticallyReferencedJniMethods.ioUringBufferOffsetLen();
144     static final int IOURING_BUFFER_OFFSETOF_BID = NativeStaticallyReferencedJniMethods.ioUringBufferOffsetBid();
145 
146     // These constants must be defined to have the same numeric value as their corresponding
147     // ordinal in the enum defined in the io_uring.h header file.
148     // DO NOT CHANGE THESE VALUES!
149     static final byte IORING_OP_NOP = 0; // Specified by IORING_OP_NOP in io_uring.h
150     static final byte IORING_OP_READV = 1; // Specified by IORING_OP_READV in io_uring.h
151     static final byte IORING_OP_WRITEV = 2; // Specified by IORING_OP_WRITEV in io_uring.h
152     static final byte IORING_OP_FSYNC = 3; // Specified by IORING_OP_FSYNC in io_uring.h
153     static final byte IORING_OP_READ_FIXED = 4; // Specified by IORING_OP_READ_FIXED in io_uring.h
154     static final byte IORING_OP_WRITE_FIXED = 5; // Specified by IORING_OP_WRITE_FIXED in io_uring.h
155     static final byte IORING_OP_POLL_ADD = 6; // Specified by IORING_OP_POLL_ADD in io_uring.h
156     static final byte IORING_OP_POLL_REMOVE = 7; // Specified by IORING_OP_POLL_REMOVE in io_uring.h
157     static final byte IORING_OP_SYNC_FILE_RANGE = 8; // Specified by IORING_OP_SYNC_FILE_RANGE in io_uring.h
158     static final byte IORING_OP_SENDMSG = 9; // Specified by IORING_OP_SENDMSG in io_uring.h
159     static final byte IORING_OP_RECVMSG = 10; // Specified by IORING_OP_RECVMSG in io_uring.h
160     static final byte IORING_OP_TIMEOUT = 11; // Specified by IORING_OP_TIMEOUT in io_uring.h
161     static final byte IORING_OP_TIMEOUT_REMOVE = 12; // Specified by IORING_OP_TIMEOUT_REMOVE in io_uring.h
162     static final byte IORING_OP_ACCEPT = 13; // Specified by IORING_OP_ACCEPT in io_uring.h
163     static final byte IORING_OP_ASYNC_CANCEL = 14; // Specified by IORING_OP_ASYNC_CANCEL in io_uring.h
164     static final byte IORING_OP_LINK_TIMEOUT = 15; // Specified by IORING_OP_LINK_TIMEOUT in io_uring.h
165     static final byte IORING_OP_CONNECT = 16; // Specified by IORING_OP_CONNECT in io_uring.h
166     static final byte IORING_OP_FALLOCATE = 17; // Specified by IORING_OP_FALLOCATE in io_uring.h
167     static final byte IORING_OP_OPENAT = 18; // Specified by IORING_OP_OPENAT in io_uring.h
168     static final byte IORING_OP_CLOSE = 19; // Specified by IORING_OP_CLOSE in io_uring.h
169     static final byte IORING_OP_FILES_UPDATE = 20; // Specified by IORING_OP_FILES_UPDATE in io_uring.h
170     static final byte IORING_OP_STATX = 21; // Specified by IORING_OP_STATX in io_uring.h
171     static final byte IORING_OP_READ = 22; // Specified by IORING_OP_READ in io_uring.h
172     static final byte IORING_OP_WRITE = 23; // Specified by IORING_OP_WRITE in io_uring.h
173     static final byte IORING_OP_FADVISE = 24; // Specified by IORING_OP_FADVISE in io_uring.h
174     static final byte IORING_OP_MADVISE = 25; // Specified by IORING_OP_MADVISE in io_uring.h
175     static final byte IORING_OP_SEND = 26; // Specified by IORING_OP_SEND in io_uring.h
176     static final byte IORING_OP_RECV = 27; // Specified by IORING_OP_RECV in io_uring.h
177     static final byte IORING_OP_OPENAT2 = 28; // Specified by IORING_OP_OPENAT2 in io_uring.h
178     static final byte IORING_OP_EPOLL_CTL = 29; // Specified by IORING_OP_EPOLL_CTL in io_uring.h
179     static final byte IORING_OP_SPLICE = 30; // Specified by IORING_OP_SPLICE in io_uring.h
180     static final byte IORING_OP_PROVIDE_BUFFERS = 31; // Specified by IORING_OP_PROVIDE_BUFFERS in io_uring.h
181     static final byte IORING_OP_REMOVE_BUFFERS = 32; // Specified by IORING_OP_REMOVE_BUFFERS in io_uring.h
182     static final byte IORING_OP_TEE = 33; // Specified by IORING_OP_TEE in io_uring.h
183     static final byte IORING_OP_SHUTDOWN = 34; // Specified by IORING_OP_SHUTDOWN in io_uring.h
184     static final byte IORING_OP_RENAMEAT = 35; // Specified by IORING_OP_RENAMEAT in io_uring.h
185     static final byte IORING_OP_UNLINKAT = 36; // Specified by IORING_OP_UNLINKAT in io_uring.h
186     static final byte IORING_OP_MKDIRAT = 37; // Specified by IORING_OP_MKDIRAT in io_uring.h
187     static final byte IORING_OP_SYMLINKAT = 38; // Specified by IORING_OP_SYMLINKAT in io_uring.h
188     static final byte IORING_OP_LINKAT = 39; // Specified by IORING_OP_LINKAT in io_uring.h
189     static final byte IORING_OP_MSG_RING = 40;
190     static final byte IORING_OP_FSETXATTR = 41;
191     static final byte IORING_OP_SETXATTR = 42;
192     static final byte IORING_OP_FGETXATTR = 43;
193     static final byte IORING_OP_GETXATTR = 44;
194     static final byte IORING_OP_SOCKET = 45;
195     static final byte IORING_OP_URING_CMD = 46;
196     static final byte IORING_OP_SEND_ZC = 47;
197     static final byte IORING_OP_SENDMSG_ZC = 48;
198     static final byte IORING_OP_READ_MULTISHOT = 49;
199     static final byte IORING_OP_WAITID = 50;
200     static final byte IORING_OP_FUTEX_WAIT = 51;
201     static final byte IORING_OP_FUTEX_WAKE = 52;
202     static final byte IORING_OP_FUTEX_WAITV = 53;
203     static final byte IORING_OP_FIXED_FD_INSTALL = 54;
204     static final byte IORING_OP_FTRUNCATE = 55;
205     static final byte IORING_OP_BIND = 56;
206     static final byte IORING_CQE_F_BUFFER = 1 << 0;
207     static final byte IORING_CQE_F_MORE = 1 << 1;
208     static final byte IORING_CQE_F_SOCK_NONEMPTY = 1 << 2;
209     static final byte IORING_CQE_F_BUF_MORE = 1 << 4;
210 
211     static final int IORING_SETUP_CQSIZE = 1 << 3;
212     static final int IORING_SETUP_CLAMP = 1 << 4;
213 
214     static final int IORING_SETUP_R_DISABLED = 1 << 6;
215     static final int IORING_SETUP_SUBMIT_ALL = 1 << 7;
216     static final int IORING_SETUP_SINGLE_ISSUER = 1 << 12;
217     static final int IORING_SETUP_DEFER_TASKRUN = 1 << 13;
218     static final int IORING_CQE_BUFFER_SHIFT = 16;
219 
220     static final short IORING_POLL_ADD_MULTI = 1 << 0;
221 
222     static final short IORING_RECVSEND_POLL_FIRST = 1 << 0;
223     static final short IORING_RECVSEND_BUNDLE = 1 << 4;
224     static final short IORING_RECV_MULTISHOT = 1 << 1;
225 
226     static final short IORING_ACCEPT_MULTISHOT = 1 << 0;
227     static final short IORING_ACCEPT_DONTWAIT = 1 << 1;
228     static final short IORING_ACCEPT_POLL_FIRST = 1 << 2;
229 
230     static final int IORING_FEAT_SUBMIT_STABLE = 1 << 2;
231     static final int IORING_FEAT_RECVSEND_BUNDLE = 1 << 14;
232 
233     static final int SPLICE_F_MOVE = 1;
234 
235     static final int IOU_PBUF_RING_INC = 2;
236     static String opToStr(byte op) {
237         switch (op) {
238             case IORING_OP_NOP: return "NOP";
239             case IORING_OP_READV: return "READV";
240             case IORING_OP_WRITEV: return "WRITEV";
241             case IORING_OP_FSYNC: return "FSYNC";
242             case IORING_OP_READ_FIXED: return "READ_FIXED";
243             case IORING_OP_WRITE_FIXED: return "WRITE_FIXED";
244             case IORING_OP_POLL_ADD: return "POLL_ADD";
245             case IORING_OP_POLL_REMOVE: return "POLL_REMOVE";
246             case IORING_OP_SYNC_FILE_RANGE: return "SYNC_FILE_RANGE";
247             case IORING_OP_SENDMSG: return "SENDMSG";
248             case IORING_OP_RECVMSG: return "RECVMSG";
249             case IORING_OP_TIMEOUT: return "TIMEOUT";
250             case IORING_OP_TIMEOUT_REMOVE: return "TIMEOUT_REMOVE";
251             case IORING_OP_ACCEPT: return "ACCEPT";
252             case IORING_OP_ASYNC_CANCEL: return "ASYNC_CANCEL";
253             case IORING_OP_LINK_TIMEOUT: return "LINK_TIMEOUT";
254             case IORING_OP_CONNECT: return "CONNECT";
255             case IORING_OP_FALLOCATE: return "FALLOCATE";
256             case IORING_OP_OPENAT: return "OPENAT";
257             case IORING_OP_CLOSE: return "CLOSE";
258             case IORING_OP_FILES_UPDATE: return "FILES_UPDATE";
259             case IORING_OP_STATX: return "STATX";
260             case IORING_OP_READ: return "READ";
261             case IORING_OP_WRITE: return "WRITE";
262             case IORING_OP_FADVISE: return "FADVISE";
263             case IORING_OP_MADVISE: return "MADVISE";
264             case IORING_OP_SEND: return "SEND";
265             case IORING_OP_RECV: return "RECV";
266             case IORING_OP_OPENAT2: return "OPENAT2";
267             case IORING_OP_EPOLL_CTL: return "EPOLL_CTL";
268             case IORING_OP_SPLICE: return "SPLICE";
269             case IORING_OP_PROVIDE_BUFFERS: return "PROVIDE_BUFFERS";
270             case IORING_OP_REMOVE_BUFFERS: return "REMOVE_BUFFERS";
271             case IORING_OP_TEE: return "TEE";
272             case IORING_OP_SHUTDOWN: return "SHUTDOWN";
273             case IORING_OP_RENAMEAT: return "RENAMEAT";
274             case IORING_OP_UNLINKAT: return "UNLINKAT";
275             case IORING_OP_MKDIRAT: return "MKDIRAT";
276             case IORING_OP_SYMLINKAT: return "SYMLINKAT";
277             case IORING_OP_LINKAT: return "LINKAT";
278             default: return "[OP CODE " + op + ']';
279         }
280     }
281 
282     static final int IORING_ENTER_GETEVENTS = NativeStaticallyReferencedJniMethods.ioringEnterGetevents();
283     static final int IORING_ENTER_REGISTERED_RING = 1 << 4;
284     static final int IOSQE_ASYNC = NativeStaticallyReferencedJniMethods.iosqeAsync();
285     static final int IOSQE_LINK = NativeStaticallyReferencedJniMethods.iosqeLink();
286     static final int IOSQE_IO_DRAIN = NativeStaticallyReferencedJniMethods.iosqeDrain();
287     static final int IOSQE_BUFFER_SELECT = NativeStaticallyReferencedJniMethods.iosqeBufferSelect();
288     static final int IOSQE_CQE_SKIP_SUCCESS = 1 << 6;
289     static final int MSG_DONTWAIT = NativeStaticallyReferencedJniMethods.msgDontwait();
290     static final int MSG_FASTOPEN = NativeStaticallyReferencedJniMethods.msgFastopen();
291     static final int SOL_UDP = NativeStaticallyReferencedJniMethods.solUdp();
292     static final int UDP_SEGMENT = NativeStaticallyReferencedJniMethods.udpSegment();
293     private static final int TFO_ENABLED_CLIENT_MASK = 0x1;
294     private static final int TFO_ENABLED_SERVER_MASK = 0x2;
295     private static final int TCP_FASTOPEN_MODE = NativeStaticallyReferencedJniMethods.tcpFastopenMode();
296     /**
297      * <a href ="https://www.kernel.org/doc/Documentation/networking/ip-sysctl.txt">tcp_fastopen</a> client mode enabled
298      * state.
299      */
300     static final boolean IS_SUPPORTING_TCP_FASTOPEN_CLIENT =
301             (TCP_FASTOPEN_MODE & TFO_ENABLED_CLIENT_MASK) == TFO_ENABLED_CLIENT_MASK;
302     /**
303      * <a href ="https://www.kernel.org/doc/Documentation/networking/ip-sysctl.txt">tcp_fastopen</a> server mode enabled
304      * state.
305      */
306     static final boolean IS_SUPPORTING_TCP_FASTOPEN_SERVER =
307             (TCP_FASTOPEN_MODE & TFO_ENABLED_SERVER_MASK) == TFO_ENABLED_SERVER_MASK;
308 
309     private static final int[] REQUIRED_IORING_OPS = {
310             IORING_OP_POLL_ADD,
311             IORING_OP_TIMEOUT,
312             IORING_OP_ACCEPT,
313             IORING_OP_READ,
314             IORING_OP_WRITE,
315             IORING_OP_POLL_REMOVE,
316             IORING_OP_CONNECT,
317             IORING_OP_CLOSE,
318             IORING_OP_WRITEV,
319             IORING_OP_SENDMSG,
320             IORING_OP_RECVMSG,
321             IORING_OP_ASYNC_CANCEL,
322             IORING_OP_RECV,
323             IORING_OP_NOP,
324             IORING_OP_SHUTDOWN,
325             IORING_OP_SEND
326     };
327 
328     static int setupFlags() {
329         int flags = Native.IORING_SETUP_R_DISABLED | Native.IORING_SETUP_CLAMP;
330         if (IoUring.isSetupSubmitAllSupported()) {
331             flags |= Native.IORING_SETUP_SUBMIT_ALL;
332         }
333 
334         // See https://github.com/axboe/liburing/wiki/io_uring-and-networking-in-2023#task-work
335         if (IoUring.isSetupSingleIssuerSupported()) {
336             flags |= Native.IORING_SETUP_SINGLE_ISSUER;
337         }
338         if (IoUring.isSetupDeferTaskrunSupported()) {
339             flags |= Native.IORING_SETUP_DEFER_TASKRUN;
340         }
341         return flags;
342     }
343 
344     static RingBuffer createRingBuffer(int ringSize, int setupFlags) {
345         return createRingBuffer(ringSize, ringSize * 2, setupFlags);
346     }
347 
348     static RingBuffer createRingBuffer(int ringSize, int cqeSize, int setupFlags) {
349         ObjectUtil.checkPositive(ringSize, "ringSize");
350         ObjectUtil.checkPositive(cqeSize, "cqeSize");
351         long[] values = ioUringSetup(ringSize, cqeSize, setupFlags);
352         assert values.length == 18;
353         long cqkhead = values[0];
354         long cqktail = values[1];
355         int cqringMask = (int) values[2];
356         int cqringEntries = (int) values[3];
357         long cqArrayAddress = values[4];
358         int cqringSize = (int) values[5];
359         long cqringAddress = values[6];
360         int cqringFd = (int) values[7];
361         int cqringCapacity = (int) values[8];
362         CompletionQueue completionQueue = new CompletionQueue(
363                 Buffer.wrapMemoryAddressWithNativeOrder(cqkhead, Integer.BYTES),
364                 Buffer.wrapMemoryAddressWithNativeOrder(cqktail, Integer.BYTES),
365                 cqringMask,
366                 cqringEntries,
367                 Buffer.wrapMemoryAddressWithNativeOrder(cqArrayAddress, cqringEntries * CompletionQueue.CQE_SIZE),
368                 cqringSize,
369                 cqringAddress,
370                 cqringFd,
371                 cqringCapacity);
372 
373         long sqkhead = values[9];
374         long sqktail = values[10];
375         int sqringMask = (int) values[11];
376         int sqringEntries = (int) values[12];
377         long sqArrayAddress = values[13];
378         int sqringSize = (int) values[14];
379         long sqringAddress = values[15];
380         int sqringFd = (int) values[16];
381         SubmissionQueue submissionQueue = new SubmissionQueue(
382                 Buffer.wrapMemoryAddressWithNativeOrder(sqkhead, Integer.BYTES),
383                 Buffer.wrapMemoryAddressWithNativeOrder(sqktail, Integer.BYTES),
384                 sqringMask,
385                 sqringEntries,
386                 Buffer.wrapMemoryAddressWithNativeOrder(sqArrayAddress, sqringEntries * SubmissionQueue.SQE_SIZE),
387                 sqringSize,
388                 sqringAddress,
389                 sqringFd);
390         return new RingBuffer(submissionQueue, completionQueue, (int) values[17]);
391     }
392 
393     static void checkAllIOSupported(int ringFd) {
394         if (!ioUringProbe(ringFd, REQUIRED_IORING_OPS)) {
395             throw new UnsupportedOperationException("Not all operations are supported: "
396                     + Arrays.toString(REQUIRED_IORING_OPS));
397         }
398     }
399 
400     static boolean isRecvMultishotSupported() {
401         // Added in the same release as IORING_SETUP_SINGLE_ISSUER.
402         return Native.ioUringSetupSupportsFlags(Native.IORING_SETUP_SINGLE_ISSUER);
403     }
404 
405     static boolean isAcceptMultishotSupported(int ringFd) {
406         // IORING_OP_SOCKET was added in the same release (5.19);
407         return ioUringProbe(ringFd, new int[] { Native.IORING_OP_SOCKET });
408     }
409 
410     static boolean isCqeFSockNonEmptySupported(int ringFd) {
411         // IORING_OP_SOCKET was added in the same release (5.19);
412         return ioUringProbe(ringFd, new int[] { Native.IORING_OP_SOCKET });
413     }
414 
415     static boolean isSpliceSupported(int ringFd) {
416         // IORING_OP_SPLICE Available since 5.7
417         return ioUringProbe(ringFd, new int[] { Native.IORING_OP_SPLICE });
418     }
419 
420     static boolean isPollAddMultiShotSupported(int ringfd) {
421         // Was added in the same release and we also need this feature to correctly handle edge-triggered mode.
422         return isCqeFSockNonEmptySupported(ringfd);
423     }
424 
425     /**
426      * check current kernel version whether support io_uring_register_io_wq_worker
427      * Available since 5.15.
428      * @return true if support io_uring_register_io_wq_worker
429      */
430     static boolean isRegisterIoWqWorkerSupported(int ringFd) {
431         // See https://github.com/torvalds/linux/blob/v5.5/fs/io_uring.c#L5488C10-L5488C16
432         int result = ioUringRegisterIoWqMaxWorkers(ringFd, 0, 0);
433         if (result >= 0) {
434             return true;
435         }
436         // This is not supported and so will return -EINVAL
437         return false;
438     }
439 
440     static boolean isRegisterBufferRingSupported(int ringFd, int flags) {
441         int entries = 2;
442         short bgid = 1;
443         long result = ioUringRegisterBufRing(ringFd, entries, bgid, flags);
444         if (result >= 0) {
445             ioUringUnRegisterBufRing(ringFd, result, entries, bgid);
446             return true;
447         }
448         // This is not supported and so will return -EINVAL
449         return false;
450     }
451 
452     static void checkKernelVersion(String kernelVersion) {
453         boolean enforceKernelVersion = SystemPropertyUtil.getBoolean(
454                 "io.netty.transport.iouring.enforceKernelVersion", true);
455         boolean kernelSupported = checkKernelVersion(kernelVersion, 5, 9);
456         if (!kernelSupported) {
457             if (enforceKernelVersion) {
458                 throw new UnsupportedOperationException(
459                         "you need at least kernel version 5.9, current kernel version: " + kernelVersion);
460             } else {
461                 logger.debug("Detected kernel " + kernelVersion + " does not match minimum version of 5.9, " +
462                         "trying to use io_uring anyway");
463             }
464         }
465     }
466 
467     private static boolean checkKernelVersion(String kernelVersion, int major, int minor) {
468         String[] versionComponents = kernelVersion.split("\\.");
469         if (versionComponents.length < 3) {
470             return false;
471         }
472         int nativeMajor;
473         try {
474             nativeMajor = Integer.parseInt(versionComponents[0]);
475         } catch (NumberFormatException e) {
476             return false;
477         }
478 
479         if (nativeMajor < major) {
480             return false;
481         }
482 
483         if (nativeMajor > major) {
484             return true;
485         }
486 
487         int nativeMinor;
488         try {
489             nativeMinor = Integer.parseInt(versionComponents[1]);
490         } catch (NumberFormatException e) {
491             return false;
492         }
493 
494         return nativeMinor >= minor;
495     }
496 
497     static native boolean ioUringSetupSupportsFlags(int setupFlags);
498     private static native boolean ioUringProbe(int ringFd, int[] ios);
499     private static native long[] ioUringSetup(int entries, int cqeSize, int setupFlags);
500 
501     static native int ioUringRegisterIoWqMaxWorkers(int ringFd, int maxBoundedValue, int maxUnboundedValue);
502     static native int ioUringRegisterEnableRings(int ringFd);
503     static native int ioUringRegisterRingFds(int ringFds);
504 
505     static native long ioUringRegisterBufRing(int ringFd, int entries, short bufferGroup, int flags);
506     static native int ioUringUnRegisterBufRing(int ringFd, long ioUringBufRingAddr, int entries, short bufferGroupId);
507     static native int ioUringBufRingSize(int entries);
508     static native int ioUringEnter(int ringFd, int toSubmit, int minComplete, int flags);
509 
510     static native void eventFdWrite(int fd, long value);
511 
512     static int getFd(DefaultFileRegion fileChannel) {
513         return getFd0(fileChannel);
514     }
515 
516     private static native int getFd0(Object fileChannel);
517 
518     static FileDescriptor newBlockingEventFd() {
519         return new FileDescriptor(blockingEventFd());
520     }
521 
522     static native void ioUringExit(long submissionQueueArrayAddress, int submissionQueueRingEntries,
523                                           long submissionQueueRingAddress, int submissionQueueRingSize,
524                                           long completionQueueRingAddress, int completionQueueRingSize,
525                                           int ringFd, int enterRingFd);
526 
527     private static native int blockingEventFd();
528 
529     // for testing only!
530     static native int createFile(String name);
531 
532     private static native int registerUnix();
533 
534     static native long cmsghdrData(long hdrAddr);
535 
536     static native String kernelVersion();
537 
538     private Native() {
539         // utility
540     }
541 
542     // From io_uring native library
543     private static void loadNativeLibrary() {
544         String name = PlatformDependent.normalizedOs().toLowerCase(Locale.ROOT).trim();
545         if (!name.startsWith("linux")) {
546             throw new IllegalStateException("Only supported on Linux");
547         }
548         String staticLibName = "netty_transport_native_io_uring42";
549         String sharedLibName = staticLibName + '_' + PlatformDependent.normalizedArch();
550         ClassLoader cl = PlatformDependent.getClassLoader(Native.class);
551         try {
552             NativeLibraryLoader.load(sharedLibName, cl);
553         } catch (UnsatisfiedLinkError e1) {
554             try {
555                 NativeLibraryLoader.load(staticLibName, cl);
556                 logger.info("Failed to load io_uring");
557             } catch (UnsatisfiedLinkError e2) {
558                 ThrowableUtil.addSuppressed(e1, e2);
559                 throw e1;
560             }
561         }
562     }
563 }