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 }