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 }