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.buffer.ByteBuf;
19 import io.netty.util.internal.MathUtil;
20 import io.netty.util.internal.ObjectUtil;
21
22 import java.util.Objects;
23
24 /**
25 * Configuration class for an {@link IoUringBufferRing}.
26 * It will configure the buffer ring size, buffer group id and the chunk size.
27 */
28 public final class IoUringBufferRingConfig {
29 private final short bgId;
30 private final short bufferRingSize;
31 private final int batchSize;
32 private final int maxUnreleasedBuffers;
33 private final boolean incremental;
34 private final IoUringBufferRingAllocator allocator;
35 private final boolean batchAllocation;
36
37 /**
38 * Create a new configuration.
39 *
40 * @param bgId the buffer group id to use (must be non-negative).
41 * @param bufferRingSize the size of the ring
42 * @param maxUnreleasedBuffers this parameter is ignored by the buffer ring.
43 * @param allocator the {@link IoUringBufferRingAllocator} to use to allocate
44 * {@link io.netty.buffer.ByteBuf}s.
45 * @deprecated use {@link Builder}.
46 */
47 @Deprecated
48 public IoUringBufferRingConfig(short bgId, short bufferRingSize, int maxUnreleasedBuffers,
49 IoUringBufferRingAllocator allocator) {
50 this(bgId, bufferRingSize, bufferRingSize / 2, maxUnreleasedBuffers,
51 IoUring.isRegisterBufferRingIncSupported(), allocator);
52 }
53
54 /**
55 * Create a new configuration.
56 *
57 * @param bgId the buffer group id to use (must be non-negative).
58 * @param bufferRingSize the size of the ring
59 * @param batchSize the size of the batch on how many buffers are added everytime we need to expand the
60 * buffer ring.
61 * @param maxUnreleasedBuffers this parameter is ignored by the buffer ring.
62 * @param incremental {@code true} if the buffer ring is using incremental buffer consumption.
63 * @param allocator the {@link IoUringBufferRingAllocator} to use to allocate
64 * {@link io.netty.buffer.ByteBuf}s.
65 * @deprecated use {@link Builder}.
66 */
67 @Deprecated
68 public IoUringBufferRingConfig(short bgId, short bufferRingSize, int batchSize, int maxUnreleasedBuffers,
69 boolean incremental, IoUringBufferRingAllocator allocator) {
70 this(bgId, bufferRingSize, batchSize, maxUnreleasedBuffers, incremental, allocator, false);
71 }
72
73 private IoUringBufferRingConfig(short bgId, short bufferRingSize, int batchSize, int maxUnreleasedBuffers,
74 boolean incremental, IoUringBufferRingAllocator allocator, boolean batchAllocation) {
75 this.bgId = (short) ObjectUtil.checkPositiveOrZero(bgId, "bgId");
76 this.bufferRingSize = checkBufferRingSize(bufferRingSize);
77 this.batchSize = MathUtil.findNextPositivePowerOfTwo(
78 ObjectUtil.checkInRange(batchSize, 1, bufferRingSize, "batchSize"));
79 this.maxUnreleasedBuffers = ObjectUtil.checkInRange(
80 maxUnreleasedBuffers, bufferRingSize, Integer.MAX_VALUE, "maxUnreleasedBuffers");
81 if (incremental && !IoUring.isRegisterBufferRingIncSupported()) {
82 throw new IllegalArgumentException("Incremental buffer ring is not supported");
83 }
84 this.incremental = incremental;
85 this.allocator = ObjectUtil.checkNotNull(allocator, "allocator");
86 this.batchAllocation = batchAllocation;
87 }
88
89 /**
90 * Returns the buffer group id to use.
91 *
92 * @return the buffer group id to use.
93 */
94 public short bufferGroupId() {
95 return bgId;
96 }
97
98 /**
99 * Returns the size of the ring.
100 *
101 * @return the size of the ring.
102 */
103 public short bufferRingSize() {
104 return bufferRingSize;
105 }
106
107 /**
108 * Returns the size of the batch on how many buffers are added everytime we need to expand the buffer ring.
109 *
110 * @return batch size.
111 */
112 public int batchSize() {
113 return batchSize;
114 }
115
116 /**
117 * Returns the maximum buffers that can be allocated out of this buffer ring and are
118 * unreleased yet
119 *
120 * @return the max unreleased buffers.
121 * @deprecated will be removed as it as no effect.
122 */
123 @Deprecated
124 public int maxUnreleasedBuffers() {
125 return maxUnreleasedBuffers;
126 }
127
128 /**
129 * Returns the {@link IoUringBufferRingAllocator} to use to allocate {@link io.netty.buffer.ByteBuf}s.
130 *
131 * @return the allocator.
132 */
133 public IoUringBufferRingAllocator allocator() {
134 return allocator;
135 }
136
137 /**
138 * Returns {@code true} if the ring should always be filled via a batch allocation or
139 * {@code false} if we will try to allocate a new {@link ByteBuf} as we used a buffer from the ring.
140 * @return {@code true} if the ring should always be filled via a batch allocation.
141 */
142 public boolean isBatchAllocation() {
143 return batchAllocation;
144 }
145
146 /**
147 * Returns true if <a href="https://github.com/axboe/liburing/wiki/
148 * What's-new-with-io_uring-in-6.11-and-6.12#incremental-provided-buffer-consumption">incremental mode</a>
149 * should be used for the buffer ring.
150 *
151 * @return {@code true} if incremental mode is used, {@code false} otherwise.
152 */
153 public boolean isIncremental() {
154 return incremental;
155 }
156
157 private static short checkBufferRingSize(short bufferRingSize) {
158 if (bufferRingSize < 1) {
159 throw new IllegalArgumentException("bufferRingSize: " + bufferRingSize + " (expected: > 0)");
160 }
161
162 boolean isPowerOfTwo = (bufferRingSize & (bufferRingSize - 1)) == 0;
163 if (!isPowerOfTwo) {
164 throw new IllegalArgumentException("bufferRingSize: " + bufferRingSize + " (expected: power of 2)");
165 }
166 return bufferRingSize;
167 }
168
169 @Override
170 public boolean equals(Object o) {
171 if (o == null || getClass() != o.getClass()) {
172 return false;
173 }
174 IoUringBufferRingConfig that = (IoUringBufferRingConfig) o;
175 return bgId == that.bgId;
176 }
177
178 @Override
179 public int hashCode() {
180 return Objects.hashCode(bgId);
181 }
182
183 public static Builder builder() {
184 return new Builder();
185 }
186
187 public static final class Builder {
188 private short bgId = -1;
189 private short bufferRingSize = -1;
190 private int batchSize = -1;
191 private boolean incremental = IoUring.isRegisterBufferRingIncSupported();
192 private IoUringBufferRingAllocator allocator;
193 private boolean batchAllocation;
194
195 /**
196 * Set the buffer group id to use.
197 *
198 * @param bgId The buffer group id to use.
199 * @return This builder.
200 */
201 public Builder bufferGroupId(short bgId) {
202 this.bgId = bgId;
203 return this;
204 }
205
206 /**
207 * Set the size of the ring.
208 *
209 * @param bufferRingSize The size of the ring.
210 * @return This builder.
211 */
212 public Builder bufferRingSize(short bufferRingSize) {
213 this.bufferRingSize = bufferRingSize;
214 return this;
215 }
216
217 /**
218 * Set the size of the batch on how many buffers are added everytime we need to expand the buffer ring.
219 *
220 * @param batchSize The batch size.
221 * @return This builder.
222 */
223 public Builder batchSize(int batchSize) {
224 this.batchSize = batchSize;
225 return this;
226 }
227
228 /**
229 * Set the {@link IoUringBufferRingAllocator} to use to allocate {@link ByteBuf}s.
230 *
231 * @param allocator The allocator.
232 */
233 public Builder allocator(IoUringBufferRingAllocator allocator) {
234 this.allocator = allocator;
235 return this;
236 }
237
238 /**
239 * Set allocation strategy that is used to allocate {@link ByteBuf}s.
240 *
241 * @param batchAllocation {@code true} if the ring should always be filled via a batch allocation or
242 * {@code false} if we will try to allocate a new {@link ByteBuf} as soon
243 * as we used a buffer from the ring.
244 * @return This builder.
245 */
246 public Builder batchAllocation(boolean batchAllocation) {
247 this.batchAllocation = batchAllocation;
248 return this;
249 }
250
251 /**
252 * Set if <a href="https://github.com/axboe/liburing/wiki/
253 * What's-new-with-io_uring-in-6.11-and-6.12#incremental-provided-buffer-consumption">incremental mode</a>
254 * should be used for the buffer ring.
255 *
256 * @param incremental {@code true} if incremental mode is used, {@code false} otherwise.
257 * @return This builder.
258 */
259 public Builder incremental(boolean incremental) {
260 this.incremental = incremental;
261 return this;
262 }
263
264 /**
265 * Create a new {@link IoUringBufferRingConfig}.
266 *
267 * @return a new config.
268 */
269 public IoUringBufferRingConfig build() {
270 return new IoUringBufferRingConfig(
271 bgId, bufferRingSize, batchSize, Integer.MAX_VALUE, incremental, allocator, batchAllocation);
272 }
273 }
274 }