View Javadoc
1   /*
2    * Copyright 2025 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.util.internal.ObjectUtil;
19  
20  import java.util.ArrayList;
21  import java.util.HashSet;
22  import java.util.List;
23  import java.util.Objects;
24  import java.util.Set;
25  
26  /**
27   * Configuration class for an {@link IoUringIoHandler},
28   * managing the settings for a {@link RingBuffer} and its io_uring file descriptor.
29   *
30   * <h3>Option Map</h3>
31   * These options are used exclusively during the initialization of the {@link IoUringIoHandler}
32   * to configure the associated io_uring instance.
33   *
34   * <p>
35   * The {@link IoUringIoHandlerConfig} class provides the following configurable options:
36   * </p>
37   *
38   * <table border="1" cellspacing="0" cellpadding="6">
39   *   <caption>Available Configuration Options</caption>
40   *   <thead>
41   *     <tr>
42   *       <th>Setter Method</th>
43   *       <th>Description</th>
44   *     </tr>
45   *   </thead>
46   *   <tbody>
47   *     <tr>
48   *       <td>{@link IoUringIoHandlerConfig#setRingSize}</td>
49   *       <td>Sets the size of the submission queue for the io_uring instance.
50   *        <br>
51   *        If you want to submit a large number of io_uring requests at once,
52   *        it is recommended to properly configure this option.
53   *        The default value is 4096, which is sufficient for most scenarios.
54   *       </td>
55   *     </tr>
56   *     <tr>
57   *       <td>{@link IoUringIoHandlerConfig#setMaxBoundedWorker}</td>
58   *       <td>Defines the maximum number of bounded io_uring worker threads.
59   *        <br>
60   *        If you extend io_uring-related file operations based on Netty,
61   *        it is recommended to properly configure this option.
62   *        For more details, refer to the
63   *        <a href="https://man7.org/linux/man-pages/man3/io_uring_register_iowq_max_workers.3.html>
64   *            manual.
65   *        </a>
66   *       </td>
67   *     </tr>
68   *     <tr>
69   *       <td>{@link IoUringIoHandlerConfig#setMaxUnboundedWorker}</td>
70   *       <td>Defines the maximum number of unbounded io_uring worker threads.
71   *        <br>
72   *        If you use FileRegion to perform `sendfile` operations in io_uring,
73   *        it is recommended to properly configure this option as otherwise you might
74   *        end up with an <a href="https://github.com/netty/netty/issues/15125>unexpected number of kernel threads</a>.
75   *       </td>
76   *     </tr>
77   *     <tr>
78   *       <td>{@link IoUringIoHandlerConfig#setCqSize}</td>
79   *       <td>Sets the size of the completionQueue queue for the io_uring instance.
80   *        <br>
81   *        If your current kernel supports some multishot variants
82   *        (such as IORING_RECV_MULTISHOT, IORING_ACCEPT_MULTISHOT) or IORING_RECVSEND_BUNDLE,
83   *        and you want to handle more CQEs in a single syscall
84   *        it is recommended to properly configure this option.
85   *        The default value is twice the ring size, which is sufficient for most scenarios.
86   *       </td>
87   *     </tr>
88   *     <tr>
89   *       <td>{@link IoUringIoHandlerConfig#setBufferRingConfig}</td>
90   *       <td>
91   *         Adds a buffer ring configuration to the list of buffer ring configurations.
92   *         It will be used to register the buffer ring for the io_uring instance.
93   *       </td>
94   *     </tr>
95   *   </tbody>
96   * </table>
97   */
98  
99  public final class IoUringIoHandlerConfig {
100     private static final int DISABLE_SETUP_CQ_SIZE = -1;
101 
102     private int ringSize = Native.DEFAULT_RING_SIZE;
103     private int cqSize = DISABLE_SETUP_CQ_SIZE;
104 
105     private int maxBoundedWorker;
106 
107     private int maxUnboundedWorker;
108 
109     private Set<IoUringBufferRingConfig> bufferRingConfigs;
110 
111     /**
112      * Return the ring size of the io_uring instance.
113      * @return the ring size of the io_uring instance.
114      */
115     public int getRingSize() {
116         return ringSize;
117     }
118 
119     /**
120      * Return the size of the io_uring cqe.
121      * @return the cq size of the io_uring.
122      */
123     public int getCqSize() {
124         return cqSize;
125     }
126 
127     /**
128      * Return the maximum number of bounded iowq worker threads.
129      * @return the maximum number of bounded iowq worker threads.
130      */
131     public int getMaxBoundedWorker() {
132         return maxBoundedWorker;
133     }
134 
135     /**
136      * Return the maximum number of unbounded iowq worker threads.
137      * @return the maximum number of unbounded iowq worker threads.
138      */
139     public int getMaxUnboundedWorker() {
140         return maxUnboundedWorker;
141     }
142 
143     /**
144      * Set the ring size of the io_uring instance.
145      * @param ringSize the ring size of the io_uring instance.
146      * @return reference to this, so the API can be used fluently
147      */
148     public IoUringIoHandlerConfig setRingSize(int ringSize) {
149         this.ringSize = ObjectUtil.checkPositive(ringSize, "ringSize");
150         return this;
151     }
152 
153     /**
154      * Set the size of the io_uring cqe.
155      * @param cqSize the size of the io_uring cqe.
156      * @throws IllegalArgumentException if cqSize is less than ringSize, or not a power of 2
157      * @return reference to this, so the API can be used fluently
158      */
159     public IoUringIoHandlerConfig setCqSize(int cqSize) {
160         ObjectUtil.checkPositive(cqSize, "cqSize");
161         this.cqSize = checkCqSize(cqSize);
162         return this;
163     }
164 
165     int checkCqSize(int cqSize) {
166         if (cqSize < ringSize) {
167             throw new IllegalArgumentException("cqSize must be greater than or equal to ringSize");
168         }
169 
170         boolean isPowerOfTwo = Integer.bitCount(cqSize) == 1;
171         if (!isPowerOfTwo) {
172             throw new IllegalArgumentException("cqSize: " + cqSize + " (expected: power of 2)");
173         }
174         return cqSize;
175     }
176 
177     /**
178      * Set the maximum number of bounded iowq worker threads.
179      * @param maxBoundedWorker the maximum number of bounded iowq worker threads,
180      *                         or 0 for the Linux kernel default
181      * @return reference to this, so the API can be used fluently
182      */
183     public IoUringIoHandlerConfig setMaxBoundedWorker(int maxBoundedWorker) {
184         this.maxBoundedWorker = ObjectUtil.checkPositiveOrZero(maxBoundedWorker, "maxBoundedWorker");
185         return this;
186     }
187 
188     /**
189      * Set the maximum number of unbounded iowq worker threads.
190      * @param maxUnboundedWorker the maximum number of unbounded iowq worker threads,
191      *                           of 0 for the Linux kernel default
192      * @return reference to this, so the API can be used fluently
193      */
194     public IoUringIoHandlerConfig setMaxUnboundedWorker(int maxUnboundedWorker) {
195         this.maxUnboundedWorker = ObjectUtil.checkPositiveOrZero(maxUnboundedWorker, "maxUnboundedWorker");
196         return this;
197     }
198 
199     /**
200      * Add a buffer ring configuration to the list of buffer ring configurations.
201      * Each {@link IoUringBufferRingConfig} must have a different {@link IoUringBufferRingConfig#bufferGroupId()}.
202      *
203      * @param ringConfig        the buffer ring configuration to append.
204      * @return reference to this, so the API can be used fluently
205      */
206     public IoUringIoHandlerConfig setBufferRingConfig(IoUringBufferRingConfig... ringConfig) {
207         Set<IoUringBufferRingConfig> configSet = new HashSet<>(ringConfig.length);
208         for (IoUringBufferRingConfig bufferRingConfig : ringConfig) {
209             if (!configSet.add(bufferRingConfig)) {
210                 throw new IllegalArgumentException("Duplicated buffer group id: " + bufferRingConfig.bufferGroupId());
211             }
212         }
213         bufferRingConfigs = configSet;
214         return this;
215     }
216 
217     /**
218      * Get the list of buffer ring configurations.
219      * @return the copy of buffer ring configurations.
220      */
221     public List<IoUringBufferRingConfig> getBufferRingConfigs() {
222         return new ArrayList<>(bufferRingConfigs);
223     }
224 
225     boolean needRegisterIowqMaxWorker() {
226         return maxBoundedWorker > 0 || maxUnboundedWorker > 0;
227     }
228 
229     boolean needSetupCqeSize() {
230         return cqSize != DISABLE_SETUP_CQ_SIZE;
231     }
232 
233     Set<IoUringBufferRingConfig> getInternBufferRingConfigs() {
234         return bufferRingConfigs;
235     }
236 }