View Javadoc
1   /*
2    * Copyright 2024 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.buffer.ByteBufAllocator;
20  import io.netty.channel.ChannelException;
21  import io.netty.channel.ChannelOption;
22  import io.netty.channel.DefaultChannelConfig;
23  import io.netty.channel.FixedRecvByteBufAllocator;
24  import io.netty.channel.MessageSizeEstimator;
25  import io.netty.channel.RecvByteBufAllocator;
26  import io.netty.channel.WriteBufferWaterMark;
27  import io.netty.channel.socket.DatagramChannelConfig;
28  import io.netty.util.internal.ObjectUtil;
29  
30  import java.io.IOException;
31  import java.net.InetAddress;
32  import java.net.NetworkInterface;
33  import java.util.Map;
34  
35  final class IoUringDatagramChannelConfig extends DefaultChannelConfig implements DatagramChannelConfig {
36      private static final RecvByteBufAllocator DEFAULT_RCVBUF_ALLOCATOR = new FixedRecvByteBufAllocator(2048);
37      private boolean activeOnOpen;
38      private volatile int maxDatagramSize;
39  
40      IoUringDatagramChannelConfig(AbstractIoUringChannel channel) {
41          super(channel);
42          setRecvByteBufAllocator(DEFAULT_RCVBUF_ALLOCATOR);
43      }
44  
45      @Override
46      @SuppressWarnings("deprecation")
47      public Map<ChannelOption<?>, Object> getOptions() {
48          return getOptions(
49                  super.getOptions(),
50                  ChannelOption.SO_BROADCAST, ChannelOption.SO_RCVBUF, ChannelOption.SO_SNDBUF,
51                  ChannelOption.SO_REUSEADDR, ChannelOption.IP_MULTICAST_LOOP_DISABLED,
52                  ChannelOption.IP_MULTICAST_ADDR, ChannelOption.IP_MULTICAST_IF, ChannelOption.IP_MULTICAST_TTL,
53                  ChannelOption.IP_TOS, ChannelOption.DATAGRAM_CHANNEL_ACTIVE_ON_REGISTRATION,
54                  IoUringChannelOption.SO_REUSEPORT, IoUringChannelOption.IP_FREEBIND,
55                  IoUringChannelOption.IP_TRANSPARENT, IoUringChannelOption.MAX_DATAGRAM_PAYLOAD_SIZE);
56      }
57  
58      @SuppressWarnings({ "unchecked", "deprecation" })
59      @Override
60      public <T> T getOption(ChannelOption<T> option) {
61          if (option == ChannelOption.SO_BROADCAST) {
62              return (T) Boolean.valueOf(isBroadcast());
63          }
64          if (option == ChannelOption.SO_RCVBUF) {
65              return (T) Integer.valueOf(getReceiveBufferSize());
66          }
67          if (option == ChannelOption.SO_SNDBUF) {
68              return (T) Integer.valueOf(getSendBufferSize());
69          }
70          if (option == ChannelOption.SO_REUSEADDR) {
71              return (T) Boolean.valueOf(isReuseAddress());
72          }
73          if (option == ChannelOption.IP_MULTICAST_LOOP_DISABLED) {
74              return (T) Boolean.valueOf(isLoopbackModeDisabled());
75          }
76          if (option == ChannelOption.IP_MULTICAST_ADDR) {
77              return (T) getInterface();
78          }
79          if (option == ChannelOption.IP_MULTICAST_IF) {
80              return (T) getNetworkInterface();
81          }
82          if (option == ChannelOption.IP_MULTICAST_TTL) {
83              return (T) Integer.valueOf(getTimeToLive());
84          }
85          if (option == ChannelOption.IP_TOS) {
86              return (T) Integer.valueOf(getTrafficClass());
87          }
88          if (option == ChannelOption.DATAGRAM_CHANNEL_ACTIVE_ON_REGISTRATION) {
89              return (T) Boolean.valueOf(activeOnOpen);
90          }
91          if (option == IoUringChannelOption.SO_REUSEPORT) {
92              return (T) Boolean.valueOf(isReusePort());
93          }
94          if (option == IoUringChannelOption.IP_TRANSPARENT) {
95              return (T) Boolean.valueOf(isIpTransparent());
96          }
97          if (option == IoUringChannelOption.IP_FREEBIND) {
98              return (T) Boolean.valueOf(isFreeBind());
99          }
100         if (option == IoUringChannelOption.MAX_DATAGRAM_PAYLOAD_SIZE) {
101             return (T) Integer.valueOf(getMaxDatagramPayloadSize());
102         }
103         return super.getOption(option);
104     }
105 
106     @Override
107     @SuppressWarnings("deprecation")
108     public <T> boolean setOption(ChannelOption<T> option, T value) {
109         validate(option, value);
110 
111         if (option == ChannelOption.SO_BROADCAST) {
112             setBroadcast((Boolean) value);
113         } else if (option == ChannelOption.SO_RCVBUF) {
114             setReceiveBufferSize((Integer) value);
115         } else if (option == ChannelOption.SO_SNDBUF) {
116             setSendBufferSize((Integer) value);
117         } else if (option == ChannelOption.SO_REUSEADDR) {
118             setReuseAddress((Boolean) value);
119         } else if (option == ChannelOption.IP_MULTICAST_LOOP_DISABLED) {
120             setLoopbackModeDisabled((Boolean) value);
121         } else if (option == ChannelOption.IP_MULTICAST_ADDR) {
122             setInterface((InetAddress) value);
123         } else if (option == ChannelOption.IP_MULTICAST_IF) {
124             setNetworkInterface((NetworkInterface) value);
125         } else if (option == ChannelOption.IP_MULTICAST_TTL) {
126             setTimeToLive((Integer) value);
127         } else if (option == ChannelOption.IP_TOS) {
128             setTrafficClass((Integer) value);
129         } else if (option == ChannelOption.DATAGRAM_CHANNEL_ACTIVE_ON_REGISTRATION) {
130             setActiveOnOpen((Boolean) value);
131         } else if (option == IoUringChannelOption.SO_REUSEPORT) {
132             setReusePort((Boolean) value);
133         } else if (option == IoUringChannelOption.IP_FREEBIND) {
134             setFreeBind((Boolean) value);
135         } else if (option == IoUringChannelOption.IP_TRANSPARENT) {
136             setIpTransparent((Boolean) value);
137         } else if (option == IoUringChannelOption.MAX_DATAGRAM_PAYLOAD_SIZE) {
138             setMaxDatagramPayloadSize((Integer) value);
139         } else {
140             return super.setOption(option, value);
141         }
142 
143         return true;
144     }
145 
146     private void setActiveOnOpen(boolean activeOnOpen) {
147         if (channel.isRegistered()) {
148             throw new IllegalStateException("Can only changed before channel was registered");
149         }
150         this.activeOnOpen = activeOnOpen;
151     }
152 
153     boolean getActiveOnOpen() {
154         return activeOnOpen;
155     }
156 
157     @Override
158     public IoUringDatagramChannelConfig setMessageSizeEstimator(MessageSizeEstimator estimator) {
159         super.setMessageSizeEstimator(estimator);
160         return this;
161     }
162 
163     @Override
164     @Deprecated
165     public IoUringDatagramChannelConfig setWriteBufferLowWaterMark(int writeBufferLowWaterMark) {
166         super.setWriteBufferLowWaterMark(writeBufferLowWaterMark);
167         return this;
168     }
169 
170     @Override
171     @Deprecated
172     public IoUringDatagramChannelConfig setWriteBufferHighWaterMark(int writeBufferHighWaterMark) {
173         super.setWriteBufferHighWaterMark(writeBufferHighWaterMark);
174         return this;
175     }
176 
177     @Override
178     public IoUringDatagramChannelConfig setWriteBufferWaterMark(WriteBufferWaterMark writeBufferWaterMark) {
179         super.setWriteBufferWaterMark(writeBufferWaterMark);
180         return this;
181     }
182 
183     @Override
184     public IoUringDatagramChannelConfig setAutoClose(boolean autoClose) {
185         super.setAutoClose(autoClose);
186         return this;
187     }
188 
189     @Override
190     public IoUringDatagramChannelConfig setAutoRead(boolean autoRead) {
191         super.setAutoRead(autoRead);
192         return this;
193     }
194 
195     @Override
196     public IoUringDatagramChannelConfig setRecvByteBufAllocator(RecvByteBufAllocator allocator) {
197         super.setRecvByteBufAllocator(allocator);
198         return this;
199     }
200 
201     @Override
202     public IoUringDatagramChannelConfig setWriteSpinCount(int writeSpinCount) {
203         super.setWriteSpinCount(writeSpinCount);
204         return this;
205     }
206 
207     @Override
208     public IoUringDatagramChannelConfig setAllocator(ByteBufAllocator allocator) {
209         super.setAllocator(allocator);
210         return this;
211     }
212 
213     @Override
214     public IoUringDatagramChannelConfig setConnectTimeoutMillis(int connectTimeoutMillis) {
215         super.setConnectTimeoutMillis(connectTimeoutMillis);
216         return this;
217     }
218 
219     @Override
220     @Deprecated
221     public IoUringDatagramChannelConfig setMaxMessagesPerRead(int maxMessagesPerRead) {
222         super.setMaxMessagesPerRead(maxMessagesPerRead);
223         return this;
224     }
225 
226     @Override
227     public int getSendBufferSize() {
228         try {
229             return ((AbstractIoUringChannel) channel).socket.getSendBufferSize();
230         } catch (IOException e) {
231             throw new ChannelException(e);
232         }
233     }
234 
235     @Override
236     public IoUringDatagramChannelConfig setSendBufferSize(int sendBufferSize) {
237         try {
238             ((AbstractIoUringChannel) channel).socket.setSendBufferSize(sendBufferSize);
239             return this;
240         } catch (IOException e) {
241             throw new ChannelException(e);
242         }
243     }
244 
245     @Override
246     public int getReceiveBufferSize() {
247         try {
248             return ((AbstractIoUringChannel) channel).socket.getReceiveBufferSize();
249         } catch (IOException e) {
250             throw new ChannelException(e);
251         }
252     }
253 
254     @Override
255     public IoUringDatagramChannelConfig setReceiveBufferSize(int receiveBufferSize) {
256         try {
257             ((AbstractIoUringChannel) channel).socket.setReceiveBufferSize(receiveBufferSize);
258             return this;
259         } catch (IOException e) {
260             throw new ChannelException(e);
261         }
262     }
263 
264     @Override
265     public int getTrafficClass() {
266         try {
267             return ((AbstractIoUringChannel) channel).socket.getTrafficClass();
268         } catch (IOException e) {
269             throw new ChannelException(e);
270         }
271     }
272 
273     @Override
274     public IoUringDatagramChannelConfig setTrafficClass(int trafficClass) {
275         try {
276             ((AbstractIoUringChannel) channel).socket.setTrafficClass(trafficClass);
277             return this;
278         } catch (IOException e) {
279             throw new ChannelException(e);
280         }
281     }
282 
283     @Override
284     public boolean isReuseAddress() {
285         try {
286             return ((AbstractIoUringChannel) channel).socket.isReuseAddress();
287         } catch (IOException e) {
288             throw new ChannelException(e);
289         }
290     }
291 
292     @Override
293     public IoUringDatagramChannelConfig setReuseAddress(boolean reuseAddress) {
294         try {
295             ((AbstractIoUringChannel) channel).socket.setReuseAddress(reuseAddress);
296             return this;
297         } catch (IOException e) {
298             throw new ChannelException(e);
299         }
300     }
301 
302     @Override
303     public boolean isBroadcast() {
304         try {
305             return ((AbstractIoUringChannel) channel).socket.isBroadcast();
306         } catch (IOException e) {
307             throw new ChannelException(e);
308         }
309     }
310 
311     @Override
312     public IoUringDatagramChannelConfig setBroadcast(boolean broadcast) {
313         try {
314             ((AbstractIoUringChannel) channel).socket.setBroadcast(broadcast);
315             return this;
316         } catch (IOException e) {
317             throw new ChannelException(e);
318         }
319     }
320 
321     @Override
322     public boolean isLoopbackModeDisabled() {
323         try {
324             return ((AbstractIoUringChannel) channel).socket.isLoopbackModeDisabled();
325         } catch (IOException e) {
326             throw new ChannelException(e);
327         }
328     }
329 
330     @Override
331     public IoUringDatagramChannelConfig setLoopbackModeDisabled(boolean loopbackModeDisabled) {
332         try {
333             ((AbstractIoUringChannel) channel).socket.setLoopbackModeDisabled(loopbackModeDisabled);
334             return this;
335         } catch (IOException e) {
336             throw new ChannelException(e);
337         }
338     }
339 
340     @Override
341     public int getTimeToLive() {
342         try {
343             return ((AbstractIoUringChannel) channel).socket.getTimeToLive();
344         } catch (IOException e) {
345             throw new ChannelException(e);
346         }
347     }
348 
349     @Override
350     public IoUringDatagramChannelConfig setTimeToLive(int ttl) {
351         try {
352             ((AbstractIoUringChannel) channel).socket.setTimeToLive(ttl);
353             return this;
354         } catch (IOException e) {
355             throw new ChannelException(e);
356         }
357     }
358 
359     @Override
360     public InetAddress getInterface() {
361         try {
362             return ((AbstractIoUringChannel) channel).socket.getInterface();
363         } catch (IOException e) {
364             throw new ChannelException(e);
365         }
366     }
367 
368     @Override
369     public IoUringDatagramChannelConfig setInterface(InetAddress interfaceAddress) {
370         try {
371             ((AbstractIoUringChannel) channel).socket.setInterface(interfaceAddress);
372             return this;
373         } catch (IOException e) {
374             throw new ChannelException(e);
375         }
376     }
377 
378     @Override
379     public NetworkInterface getNetworkInterface() {
380         try {
381             return ((AbstractIoUringChannel) channel).socket.getNetworkInterface();
382         } catch (IOException e) {
383             throw new ChannelException(e);
384         }
385     }
386 
387     @Override
388     public IoUringDatagramChannelConfig setNetworkInterface(NetworkInterface networkInterface) {
389         try {
390             ((AbstractIoUringChannel) channel).socket.setNetworkInterface(networkInterface);
391             return this;
392         } catch (IOException e) {
393             throw new ChannelException(e);
394         }
395     }
396 
397     /**
398      * Returns {@code true} if the SO_REUSEPORT option is set.
399      */
400     public boolean isReusePort() {
401         try {
402             return ((AbstractIoUringChannel) channel).socket.isReusePort();
403         } catch (IOException e) {
404             throw new ChannelException(e);
405         }
406     }
407 
408     /**
409      * Set the SO_REUSEPORT option on the underlying Channel. This will allow to bind multiple
410      * {@link io.netty.channel.socket.DatagramChannel}s to the same port and so receive datagrams with multiple threads.
411      *
412      * Be aware this method needs be called before
413      * {@link io.netty.channel.socket.DatagramChannel#bind(java.net.SocketAddress)} to have
414      * any affect.
415      */
416     public IoUringDatagramChannelConfig setReusePort(boolean reusePort) {
417         try {
418             ((AbstractIoUringChannel) channel).socket.setReusePort(reusePort);
419             return this;
420         } catch (IOException e) {
421             throw new ChannelException(e);
422         }
423     }
424 
425     /**
426      * Returns {@code true} if <a href="https://man7.org/linux/man-pages/man7/ip.7.html">IP_TRANSPARENT</a> is enabled,
427      * {@code false} otherwise.
428      */
429     public boolean isIpTransparent() {
430         try {
431             return ((AbstractIoUringChannel) channel).socket.isIpTransparent();
432         } catch (IOException e) {
433             throw new ChannelException(e);
434         }
435     }
436 
437     /**
438      * If {@code true} is used <a href="https://man7.org/linux/man-pages/man7/ip.7.html">IP_TRANSPARENT</a> is enabled,
439      * {@code false} for disable it. Default is disabled.
440      */
441     public IoUringDatagramChannelConfig setIpTransparent(boolean ipTransparent) {
442         try {
443             ((AbstractIoUringChannel) channel).socket.setIpTransparent(ipTransparent);
444             return this;
445         } catch (IOException e) {
446             throw new ChannelException(e);
447         }
448     }
449 
450     /**
451      * Returns {@code true} if <a href="https://man7.org/linux/man-pages/man7/ip.7.html">IP_FREEBIND</a> is enabled,
452      * {@code false} otherwise.
453      */
454     public boolean isFreeBind() {
455         try {
456             return ((AbstractIoUringChannel) channel).socket.isIpFreeBind();
457         } catch (IOException e) {
458             throw new ChannelException(e);
459         }
460     }
461 
462     /**
463      * If {@code true} is used <a href="https://man7.org/linux/man-pages/man7/ip.7.html">IP_FREEBIND</a> is enabled,
464      * {@code false} for disable it. Default is disabled.
465      */
466     public IoUringDatagramChannelConfig setFreeBind(boolean freeBind) {
467         try {
468             ((AbstractIoUringChannel) channel).socket.setIpFreeBind(freeBind);
469             return this;
470         } catch (IOException e) {
471             throw new ChannelException(e);
472         }
473     }
474 
475     /**
476      * Set the maximum {@link io.netty.channel.socket.DatagramPacket} size. This will be used to determine if
477      * a batch of {@code IORING_IO_RECVMSG} should be used when reading from the underlying socket.
478      * When batched {@code recvmmsg} is used
479      * we may be able to read multiple {@link io.netty.channel.socket.DatagramPacket}s with one syscall and so
480      * greatly improve the performance. This number will be used to slice {@link ByteBuf}s returned by the used
481      * {@link RecvByteBufAllocator}. You can use {@code 0} to disable the usage of batching, any other bigger value
482      * will enable it.
483      */
484     public IoUringDatagramChannelConfig setMaxDatagramPayloadSize(int maxDatagramSize) {
485         this.maxDatagramSize = ObjectUtil.checkPositiveOrZero(maxDatagramSize, "maxDatagramSize");
486         return this;
487     }
488 
489     /**
490      * Get the maximum {@link io.netty.channel.socket.DatagramPacket} size.
491      */
492     public int getMaxDatagramPayloadSize() {
493         return maxDatagramSize;
494     }
495 }