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 
101     private int ringSize = IoUring.DEFAULT_RING_SIZE;
102     private int cqSize = IoUring.DEFAULT_CQ_SIZE;
103 
104     private int maxBoundedWorker;
105 
106     private int maxUnboundedWorker;
107 
108     private Set<IoUringBufferRingConfig> bufferRingConfigs;
109 
110     /**
111      * Return the ring size of the io_uring instance.
112      * @return the ring size of the io_uring instance.
113      */
114     public int getRingSize() {
115         return ringSize;
116     }
117 
118     /**
119      * Return the size of the io_uring cqe.
120      * @return the cq size of the io_uring.
121      */
122     public int getCqSize() {
123         return cqSize;
124     }
125 
126     /**
127      * Return the maximum number of bounded iowq worker threads.
128      * @return the maximum number of bounded iowq worker threads.
129      */
130     public int getMaxBoundedWorker() {
131         return maxBoundedWorker;
132     }
133 
134     /**
135      * Return the maximum number of unbounded iowq worker threads.
136      * @return the maximum number of unbounded iowq worker threads.
137      */
138     public int getMaxUnboundedWorker() {
139         return maxUnboundedWorker;
140     }
141 
142     /**
143      * Set the ring size of the io_uring instance.
144      * @param ringSize the ring size of the io_uring instance.
145      * @return reference to this, so the API can be used fluently
146      */
147     public IoUringIoHandlerConfig setRingSize(int ringSize) {
148         this.ringSize = ObjectUtil.checkPositive(ringSize, "ringSize");
149         return this;
150     }
151 
152     /**
153      * Set the size of the io_uring cqe.
154      * @param cqSize the size of the io_uring cqe.
155      * @throws IllegalArgumentException if cqSize is less than ringSize, or not a power of 2
156      * @return reference to this, so the API can be used fluently
157      */
158     public IoUringIoHandlerConfig setCqSize(int cqSize) {
159         ObjectUtil.checkPositive(cqSize, "cqSize");
160         this.cqSize = checkCqSize(cqSize);
161         return this;
162     }
163 
164     int checkCqSize(int cqSize) {
165         if (cqSize < ringSize) {
166             throw new IllegalArgumentException("cqSize must be greater than or equal to ringSize");
167         }
168 
169         boolean isPowerOfTwo = Integer.bitCount(cqSize) == 1;
170         if (!isPowerOfTwo) {
171             throw new IllegalArgumentException("cqSize: " + cqSize + " (expected: power of 2)");
172         }
173         return cqSize;
174     }
175 
176     /**
177      * Set the maximum number of bounded iowq worker threads.
178      * @param maxBoundedWorker the maximum number of bounded iowq worker threads,
179      *                         or 0 for the Linux kernel default
180      * @return reference to this, so the API can be used fluently
181      */
182     public IoUringIoHandlerConfig setMaxBoundedWorker(int maxBoundedWorker) {
183         this.maxBoundedWorker = ObjectUtil.checkPositiveOrZero(maxBoundedWorker, "maxBoundedWorker");
184         return this;
185     }
186 
187     /**
188      * Set the maximum number of unbounded iowq worker threads.
189      * @param maxUnboundedWorker the maximum number of unbounded iowq worker threads,
190      *                           of 0 for the Linux kernel default
191      * @return reference to this, so the API can be used fluently
192      */
193     public IoUringIoHandlerConfig setMaxUnboundedWorker(int maxUnboundedWorker) {
194         this.maxUnboundedWorker = ObjectUtil.checkPositiveOrZero(maxUnboundedWorker, "maxUnboundedWorker");
195         return this;
196     }
197 
198     /**
199      * Add a buffer ring configuration to the list of buffer ring configurations.
200      * Each {@link IoUringBufferRingConfig} must have a different {@link IoUringBufferRingConfig#bufferGroupId()}.
201      *
202      * @param ringConfig        the buffer ring configuration to append.
203      * @return reference to this, so the API can be used fluently
204      */
205     public IoUringIoHandlerConfig setBufferRingConfig(IoUringBufferRingConfig... ringConfig) {
206         Set<IoUringBufferRingConfig> configSet = new HashSet<>(ringConfig.length);
207         for (IoUringBufferRingConfig bufferRingConfig : ringConfig) {
208             if (!configSet.add(bufferRingConfig)) {
209                 throw new IllegalArgumentException("Duplicated buffer group id: " + bufferRingConfig.bufferGroupId());
210             }
211         }
212         bufferRingConfigs = configSet;
213         return this;
214     }
215 
216     /**
217      * Get the list of buffer ring configurations.
218      * @return the copy of buffer ring configurations.
219      */
220     public List<IoUringBufferRingConfig> getBufferRingConfigs() {
221         return new ArrayList<>(bufferRingConfigs);
222     }
223 
224     boolean needRegisterIowqMaxWorker() {
225         return maxBoundedWorker > 0 || maxUnboundedWorker > 0;
226     }
227 
228     boolean needSetupCqeSize() {
229         return cqSize != IoUring.DISABLE_SETUP_CQ_SIZE;
230     }
231 
232     Set<IoUringBufferRingConfig> getInternBufferRingConfigs() {
233         return bufferRingConfigs;
234     }
235 }