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.ChannelOption;
19  import io.netty.util.internal.PlatformDependent;
20  import io.netty.util.internal.SystemPropertyUtil;
21  import io.netty.util.internal.logging.InternalLogger;
22  import io.netty.util.internal.logging.InternalLoggerFactory;
23  
24  public final class IoUring {
25  
26      private static final Throwable UNAVAILABILITY_CAUSE;
27      private static final boolean IORING_CQE_F_SOCK_NONEMPTY_SUPPORTED;
28      private static final boolean IORING_SPLICE_SUPPORTED;
29      private static final boolean IORING_ACCEPT_NO_WAIT_SUPPORTED;
30      private static final boolean IORING_ACCEPT_MULTISHOT_SUPPORTED;
31      private static final boolean IORING_RECV_MULTISHOT_SUPPORTED;
32      private static final boolean IORING_RECVSEND_BUNDLE_SUPPORTED;
33      private static final boolean IORING_POLL_ADD_MULTISHOT_SUPPORTED;
34      private static final boolean IORING_REGISTER_IOWQ_MAX_WORKERS_SUPPORTED;
35      private static final boolean IORING_SETUP_SUBMIT_ALL_SUPPORTED;
36      private static final boolean IORING_SETUP_CQ_SIZE_SUPPORTED;
37      private static final boolean IORING_SETUP_SINGLE_ISSUER_SUPPORTED;
38      private static final boolean IORING_SETUP_DEFER_TASKRUN_SUPPORTED;
39      private static final boolean IORING_REGISTER_BUFFER_RING_SUPPORTED;
40      private static final boolean IORING_REGISTER_BUFFER_RING_INC_SUPPORTED;
41  
42      private static final InternalLogger logger;
43  
44      static {
45          logger = InternalLoggerFactory.getInstance(IoUring.class);
46          Throwable cause = null;
47          boolean socketNonEmptySupported = false;
48          boolean spliceSupported = false;
49          boolean acceptSupportNoWait = false;
50          boolean acceptMultishotSupported = false;
51          boolean recvsendBundleSupported = false;
52          boolean recvMultishotSupported = false;
53          boolean pollAddMultishotSupported = false;
54          boolean registerIowqWorkersSupported = false;
55          boolean submitAllSupported = false;
56          boolean setUpCqSizeSupported = false;
57          boolean singleIssuerSupported = false;
58          boolean deferTaskrunSupported = false;
59          boolean registerBufferRingSupported = false;
60          boolean registerBufferRingIncSupported = false;
61  
62          try {
63              if (SystemPropertyUtil.getBoolean("io.netty.transport.noNative", false)) {
64                  cause = new UnsupportedOperationException(
65                          "Native transport was explicit disabled with -Dio.netty.transport.noNative=true");
66              } else {
67                  String kernelVersion = Native.kernelVersion();
68                  Native.checkKernelVersion(kernelVersion);
69                  Throwable unsafeCause = PlatformDependent.getUnsafeUnavailabilityCause();
70                  if (unsafeCause == null) {
71                      RingBuffer ringBuffer = null;
72                      try {
73                          ringBuffer = Native.createRingBuffer(1, 0);
74                          Native.checkAllIOSupported(ringBuffer.fd());
75                          socketNonEmptySupported = Native.isCqeFSockNonEmptySupported(ringBuffer.fd());
76                          spliceSupported = Native.isSpliceSupported(ringBuffer.fd());
77                          recvsendBundleSupported = (ringBuffer.features() & Native.IORING_FEAT_RECVSEND_BUNDLE) != 0;
78                          // IORING_FEAT_RECVSEND_BUNDLE was added in the same release.
79                          acceptSupportNoWait = recvsendBundleSupported;
80                          acceptMultishotSupported = Native.isAcceptMultishotSupported(ringBuffer.fd());
81                          recvMultishotSupported = Native.isRecvMultishotSupported();
82                          pollAddMultishotSupported = Native.isPollAddMultiShotSupported(ringBuffer.fd());
83                          registerIowqWorkersSupported = Native.isRegisterIoWqWorkerSupported(ringBuffer.fd());
84                          submitAllSupported = Native.ioUringSetupSupportsFlags(Native.IORING_SETUP_SUBMIT_ALL);
85                          setUpCqSizeSupported = Native.ioUringSetupSupportsFlags(Native.IORING_SETUP_CQSIZE);
86                          singleIssuerSupported = Native.ioUringSetupSupportsFlags(Native.IORING_SETUP_SINGLE_ISSUER);
87                          // IORING_SETUP_DEFER_TASKRUN requires to also set IORING_SETUP_SINGLE_ISSUER.
88                          // See https://manpages.debian.org/unstable/liburing-dev/io_uring_setup.2.en.html
89                          deferTaskrunSupported = Native.ioUringSetupSupportsFlags(
90                                  Native.IORING_SETUP_SINGLE_ISSUER | Native.IORING_SETUP_DEFER_TASKRUN);
91                          registerBufferRingSupported = Native.isRegisterBufferRingSupported(ringBuffer.fd(), 0);
92                          registerBufferRingIncSupported = Native.isRegisterBufferRingSupported(ringBuffer.fd(),
93                                  Native.IOU_PBUF_RING_INC);
94                      } finally {
95                          if (ringBuffer != null) {
96                              try {
97                                  ringBuffer.close();
98                              } catch (Exception ignore) {
99                                  // ignore
100                             }
101                         }
102                     }
103                 } else {
104                     cause = new UnsupportedOperationException("Unsafe is not supported", unsafeCause);
105                 }
106             }
107         } catch (Throwable t) {
108             cause = t;
109         }
110         if (cause != null) {
111             if (logger.isTraceEnabled()) {
112                 logger.debug("IoUring support is not available", cause);
113             } else if (logger.isDebugEnabled()) {
114                 logger.debug("IoUring support is not available: {}", cause.getMessage());
115             }
116         } else {
117             if (logger.isDebugEnabled()) {
118                 logger.debug("IoUring support is available (" +
119                         "CQE_F_SOCK_NONEMPTY_SUPPORTED={}, " +
120                         "SPLICE_SUPPORTED={}, " +
121                         "ACCEPT_NO_WAIT_SUPPORTED={}, " +
122                         "ACCEPT_MULTISHOT_SUPPORTED={}, " +
123                         "POLL_ADD_MULTISHOT_SUPPORTED={} " +
124                         "RECV_MULTISHOT_SUPPORTED={}, " +
125                         "IORING_RECVSEND_BUNDLE_SUPPORTED={}, " +
126                         "REGISTER_IOWQ_MAX_WORKERS_SUPPORTED={}, " +
127                         "SETUP_SUBMIT_ALL_SUPPORTED={}, " +
128                         "SETUP_SINGLE_ISSUER_SUPPORTED={}, " +
129                         "SETUP_DEFER_TASKRUN_SUPPORTED={}, " +
130                         "REGISTER_BUFFER_RING_SUPPORTED={}, " +
131                         "REGISTER_BUFFER_RING_INC_SUPPORTED={}" +
132                         ")", socketNonEmptySupported, spliceSupported, acceptSupportNoWait, acceptMultishotSupported,
133                         pollAddMultishotSupported, recvMultishotSupported, recvsendBundleSupported,
134                         registerIowqWorkersSupported, submitAllSupported, singleIssuerSupported, deferTaskrunSupported,
135                         registerBufferRingSupported, registerBufferRingIncSupported);
136             }
137         }
138         UNAVAILABILITY_CAUSE = cause;
139         IORING_CQE_F_SOCK_NONEMPTY_SUPPORTED = socketNonEmptySupported;
140         IORING_SPLICE_SUPPORTED = spliceSupported;
141         IORING_ACCEPT_NO_WAIT_SUPPORTED = acceptSupportNoWait;
142         IORING_ACCEPT_MULTISHOT_SUPPORTED = acceptMultishotSupported;
143         IORING_RECV_MULTISHOT_SUPPORTED = recvMultishotSupported;
144         IORING_RECVSEND_BUNDLE_SUPPORTED = recvsendBundleSupported;
145         IORING_POLL_ADD_MULTISHOT_SUPPORTED = pollAddMultishotSupported;
146         IORING_REGISTER_IOWQ_MAX_WORKERS_SUPPORTED = registerIowqWorkersSupported;
147         IORING_SETUP_SUBMIT_ALL_SUPPORTED = submitAllSupported;
148         IORING_SETUP_CQ_SIZE_SUPPORTED = setUpCqSizeSupported;
149         IORING_SETUP_SINGLE_ISSUER_SUPPORTED = singleIssuerSupported;
150         IORING_SETUP_DEFER_TASKRUN_SUPPORTED = deferTaskrunSupported;
151         IORING_REGISTER_BUFFER_RING_SUPPORTED = registerBufferRingSupported;
152         IORING_REGISTER_BUFFER_RING_INC_SUPPORTED = registerBufferRingIncSupported;
153     }
154 
155     public static boolean isAvailable() {
156         return UNAVAILABILITY_CAUSE == null;
157     }
158 
159     /**
160      * Returns {@code true} if the io_uring native transport is both {@linkplain #isAvailable() available} and supports
161      * {@linkplain ChannelOption#TCP_FASTOPEN_CONNECT client-side TCP FastOpen}.
162      *
163      * @return {@code true} if it's possible to use client-side TCP FastOpen via io_uring, otherwise {@code false}.
164      */
165     public static boolean isTcpFastOpenClientSideAvailable() {
166         return isAvailable() && Native.IS_SUPPORTING_TCP_FASTOPEN_CLIENT;
167     }
168 
169     /**
170      * Returns {@code true} if the io_uring native transport is both {@linkplain #isAvailable() available} and supports
171      * {@linkplain ChannelOption#TCP_FASTOPEN server-side TCP FastOpen}.
172      *
173      * @return {@code true} if it's possible to use server-side TCP FastOpen via io_uring, otherwise {@code false}.
174      */
175     public static boolean isTcpFastOpenServerSideAvailable() {
176         return isAvailable() && Native.IS_SUPPORTING_TCP_FASTOPEN_SERVER;
177     }
178 
179     static boolean isCqeFSockNonEmptySupported() {
180         return IORING_CQE_F_SOCK_NONEMPTY_SUPPORTED;
181     }
182 
183     static boolean isSpliceSupported() {
184         return IORING_SPLICE_SUPPORTED;
185     }
186 
187     static boolean isAcceptNoWaitSupported() {
188         return IORING_ACCEPT_NO_WAIT_SUPPORTED;
189     }
190 
191     static boolean isAcceptMultishotSupported() {
192         return IORING_ACCEPT_MULTISHOT_SUPPORTED;
193     }
194 
195     static boolean isRecvMultishotSupported() {
196         return IORING_RECV_MULTISHOT_SUPPORTED;
197     }
198 
199     static boolean isRecvsendBundleSupported() {
200         return IORING_RECVSEND_BUNDLE_SUPPORTED;
201     }
202 
203     static boolean isPollAddMultishotSupported() {
204         return IORING_POLL_ADD_MULTISHOT_SUPPORTED;
205     }
206 
207     static boolean isRegisterIowqMaxWorkersSupported() {
208         return IORING_REGISTER_IOWQ_MAX_WORKERS_SUPPORTED;
209     }
210 
211     static boolean isSetupCqeSizeSupported() {
212         return IORING_SETUP_CQ_SIZE_SUPPORTED;
213     }
214 
215     static boolean isSetupSubmitAllSupported() {
216         return IORING_SETUP_SUBMIT_ALL_SUPPORTED;
217     }
218 
219     static boolean isSetupSingleIssuerSupported() {
220         return IORING_SETUP_SINGLE_ISSUER_SUPPORTED;
221     }
222 
223     static boolean isSetupDeferTaskrunSupported() {
224         return IORING_SETUP_DEFER_TASKRUN_SUPPORTED;
225     }
226 
227     public static boolean isRegisterBufferRingSupported() {
228         return IORING_REGISTER_BUFFER_RING_SUPPORTED;
229     }
230 
231     public static boolean isRegisterBufferRingIncSupported() {
232         return IORING_REGISTER_BUFFER_RING_INC_SUPPORTED;
233     }
234 
235     public static void ensureAvailability() {
236         if (UNAVAILABILITY_CAUSE != null) {
237             throw (Error) new UnsatisfiedLinkError(
238                     "failed to load the required native library").initCause(UNAVAILABILITY_CAUSE);
239         }
240     }
241 
242     public static Throwable unavailabilityCause() {
243         return UNAVAILABILITY_CAUSE;
244     }
245 
246     private IoUring() {
247     }
248 }