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.ByteBufAllocator;
19  import io.netty.channel.ChannelException;
20  import io.netty.channel.ChannelOption;
21  import io.netty.channel.MessageSizeEstimator;
22  import io.netty.channel.RecvByteBufAllocator;
23  import io.netty.channel.WriteBufferWaterMark;
24  import io.netty.channel.socket.SocketChannelConfig;
25  import io.netty.util.internal.ObjectUtil;
26  import io.netty.util.internal.PlatformDependent;
27  
28  import java.io.IOException;
29  import java.util.Map;
30  
31  import static io.netty.channel.ChannelOption.*;
32  
33  
34  final class IoUringSocketChannelConfig extends IoUringStreamChannelConfig implements SocketChannelConfig {
35      private volatile boolean allowHalfClosure;
36      private volatile boolean tcpFastopen;
37  
38      static final int DISABLE_WRITE_ZERO_COPY = -1;
39      private volatile int writeZeroCopyThreshold = DISABLE_WRITE_ZERO_COPY;
40  
41      IoUringSocketChannelConfig(AbstractIoUringChannel channel) {
42          super(channel);
43          if (PlatformDependent.canEnableTcpNoDelayByDefault()) {
44              setTcpNoDelay(true);
45          }
46      }
47  
48      @Override
49      public Map<ChannelOption<?>, Object> getOptions() {
50          return getOptions(
51                  super.getOptions(),
52                  SO_RCVBUF, SO_SNDBUF, TCP_NODELAY, SO_KEEPALIVE, SO_REUSEADDR, SO_LINGER, IP_TOS,
53                  ALLOW_HALF_CLOSURE, IoUringChannelOption.TCP_CORK, IoUringChannelOption.TCP_NOTSENT_LOWAT,
54                  IoUringChannelOption.TCP_KEEPCNT, IoUringChannelOption.TCP_KEEPIDLE, IoUringChannelOption.TCP_KEEPINTVL,
55                  IoUringChannelOption.TCP_QUICKACK, IoUringChannelOption.IP_TRANSPARENT,
56                  ChannelOption.TCP_FASTOPEN_CONNECT, IoUringChannelOption.IO_URING_WRITE_ZERO_COPY_THRESHOLD);
57      }
58  
59      @SuppressWarnings("unchecked")
60      @Override
61      public <T> T getOption(ChannelOption<T> option) {
62          if (option == SO_RCVBUF) {
63              return (T) Integer.valueOf(getReceiveBufferSize());
64          }
65          if (option == SO_SNDBUF) {
66              return (T) Integer.valueOf(getSendBufferSize());
67          }
68          if (option == TCP_NODELAY) {
69              return (T) Boolean.valueOf(isTcpNoDelay());
70          }
71          if (option == SO_KEEPALIVE) {
72              return (T) Boolean.valueOf(isKeepAlive());
73          }
74          if (option == SO_REUSEADDR) {
75              return (T) Boolean.valueOf(isReuseAddress());
76          }
77          if (option == SO_LINGER) {
78              return (T) Integer.valueOf(getSoLinger());
79          }
80          if (option == IP_TOS) {
81              return (T) Integer.valueOf(getTrafficClass());
82          }
83          if (option == ALLOW_HALF_CLOSURE) {
84              return (T) Boolean.valueOf(isAllowHalfClosure());
85          }
86          if (option == IoUringChannelOption.TCP_CORK) {
87              return (T) Boolean.valueOf(isTcpCork());
88          }
89          if (option == IoUringChannelOption.TCP_NOTSENT_LOWAT) {
90              return (T) Long.valueOf(getTcpNotSentLowAt());
91          }
92          if (option == IoUringChannelOption.TCP_KEEPIDLE) {
93              return (T) Integer.valueOf(getTcpKeepIdle());
94          }
95          if (option == IoUringChannelOption.TCP_KEEPINTVL) {
96              return (T) Integer.valueOf(getTcpKeepIntvl());
97          }
98          if (option == IoUringChannelOption.TCP_KEEPCNT) {
99              return (T) Integer.valueOf(getTcpKeepCnt());
100         }
101         if (option == IoUringChannelOption.TCP_USER_TIMEOUT) {
102             return (T) Integer.valueOf(getTcpUserTimeout());
103         }
104         if (option == IoUringChannelOption.TCP_QUICKACK) {
105             return (T) Boolean.valueOf(isTcpQuickAck());
106         }
107         if (option == IoUringChannelOption.IP_TRANSPARENT) {
108             return (T) Boolean.valueOf(isIpTransparent());
109         }
110         if (option == ChannelOption.TCP_FASTOPEN_CONNECT) {
111             return (T) Boolean.valueOf(isTcpFastOpenConnect());
112         }
113         if (option == IoUringChannelOption.IO_URING_WRITE_ZERO_COPY_THRESHOLD) {
114             return (T) Integer.valueOf(getWriteZeroCopyThreshold());
115         }
116         return super.getOption(option);
117     }
118 
119     @Override
120     public <T> boolean setOption(ChannelOption<T> option, T value) {
121         validate(option, value);
122 
123         if (option == SO_RCVBUF) {
124             setReceiveBufferSize((Integer) value);
125         } else if (option == SO_SNDBUF) {
126             setSendBufferSize((Integer) value);
127         } else if (option == TCP_NODELAY) {
128             setTcpNoDelay((Boolean) value);
129         } else if (option == SO_KEEPALIVE) {
130             setKeepAlive((Boolean) value);
131         } else if (option == SO_REUSEADDR) {
132             setReuseAddress((Boolean) value);
133         } else if (option == SO_LINGER) {
134             setSoLinger((Integer) value);
135         } else if (option == IP_TOS) {
136             setTrafficClass((Integer) value);
137         } else if (option == ALLOW_HALF_CLOSURE) {
138             setAllowHalfClosure((Boolean) value);
139         } else if (option == IoUringChannelOption.TCP_CORK) {
140             setTcpCork((Boolean) value);
141         } else if (option == IoUringChannelOption.TCP_NOTSENT_LOWAT) {
142             setTcpNotSentLowAt((Long) value);
143         } else if (option == IoUringChannelOption.TCP_KEEPIDLE) {
144             setTcpKeepIdle((Integer) value);
145         } else if (option == IoUringChannelOption.TCP_KEEPCNT) {
146             setTcpKeepCnt((Integer) value);
147         } else if (option == IoUringChannelOption.TCP_KEEPINTVL) {
148             setTcpKeepIntvl((Integer) value);
149         } else if (option == IoUringChannelOption.TCP_USER_TIMEOUT) {
150             setTcpUserTimeout((Integer) value);
151         } else if (option == IoUringChannelOption.IP_TRANSPARENT) {
152             setIpTransparent((Boolean) value);
153         } else if (option == IoUringChannelOption.TCP_QUICKACK) {
154             setTcpQuickAck((Boolean) value);
155         } else if (option == ChannelOption.TCP_FASTOPEN_CONNECT) {
156             setTcpFastOpenConnect((Boolean) value);
157         } else if (option == IoUringChannelOption.IO_URING_WRITE_ZERO_COPY_THRESHOLD) {
158             setWriteZeroCopyThreshold((Integer) value);
159         } else {
160             return super.setOption(option, value);
161         }
162 
163         return true;
164     }
165 
166     @Override
167     public int getSendBufferSize() {
168         try {
169             return ((IoUringSocketChannel) channel).socket.getSendBufferSize();
170         } catch (IOException e) {
171             throw new ChannelException(e);
172         }
173     }
174 
175     @Override
176     public int getSoLinger() {
177         try {
178             return ((IoUringSocketChannel) channel).socket.getSoLinger();
179         } catch (IOException e) {
180             throw new ChannelException(e);
181         }
182     }
183 
184     @Override
185     public int getTrafficClass() {
186         try {
187             return ((IoUringSocketChannel) channel).socket.getTrafficClass();
188         } catch (IOException e) {
189             throw new ChannelException(e);
190         }
191     }
192 
193     @Override
194     public boolean isKeepAlive() {
195         try {
196             return ((IoUringSocketChannel) channel).socket.isKeepAlive();
197         } catch (IOException e) {
198             throw new ChannelException(e);
199         }
200     }
201 
202     @Override
203     public boolean isReuseAddress() {
204         try {
205             return ((IoUringSocketChannel) channel).socket.isReuseAddress();
206         } catch (IOException e) {
207             throw new ChannelException(e);
208         }
209     }
210 
211     @Override
212     public boolean isTcpNoDelay() {
213         try {
214             return ((IoUringSocketChannel) channel).socket.isTcpNoDelay();
215         } catch (IOException e) {
216             throw new ChannelException(e);
217         }
218     }
219 
220     /**
221      * Get the {@code TCP_CORK} option on the socket. See {@code man 7 tcp} for more details.
222      */
223     public boolean isTcpCork() {
224         try {
225             return ((IoUringSocketChannel) channel).socket.isTcpCork();
226         } catch (IOException e) {
227             throw new ChannelException(e);
228         }
229     }
230 
231     /**
232      * Get the {@code SO_BUSY_POLL} option on the socket. See {@code man 7 tcp} for more details.
233      */
234     public int getSoBusyPoll() {
235         try {
236             return ((IoUringSocketChannel) channel).socket.getSoBusyPoll();
237         } catch (IOException e) {
238             throw new ChannelException(e);
239         }
240     }
241 
242     /**
243      * Get the {@code TCP_NOTSENT_LOWAT} option on the socket. See {@code man 7 tcp} for more details.
244      *
245      * @return value is a uint32_t
246      */
247     public long getTcpNotSentLowAt() {
248         try {
249             return ((IoUringSocketChannel) channel).socket.getTcpNotSentLowAt();
250         } catch (IOException e) {
251             throw new ChannelException(e);
252         }
253     }
254 
255     /**
256      * Get the {@code TCP_KEEPIDLE} option on the socket. See {@code man 7 tcp} for more details.
257      */
258     public int getTcpKeepIdle() {
259         try {
260             return ((IoUringSocketChannel) channel).socket.getTcpKeepIdle();
261         } catch (IOException e) {
262             throw new ChannelException(e);
263         }
264     }
265 
266     /**
267      * Get the {@code TCP_KEEPINTVL} option on the socket. See {@code man 7 tcp} for more details.
268      */
269     public int getTcpKeepIntvl() {
270         try {
271             return ((IoUringSocketChannel) channel).socket.getTcpKeepIntvl();
272         } catch (IOException e) {
273             throw new ChannelException(e);
274         }
275     }
276 
277     /**
278      * Get the {@code TCP_KEEPCNT} option on the socket. See {@code man 7 tcp} for more details.
279      */
280     public int getTcpKeepCnt() {
281         try {
282             return ((IoUringSocketChannel) channel).socket.getTcpKeepCnt();
283         } catch (IOException e) {
284             throw new ChannelException(e);
285         }
286     }
287 
288     /**
289      * Get the {@code TCP_USER_TIMEOUT} option on the socket. See {@code man 7 tcp} for more details.
290      */
291     public int getTcpUserTimeout() {
292         try {
293             return ((IoUringSocketChannel) channel).socket.getTcpUserTimeout();
294         } catch (IOException e) {
295             throw new ChannelException(e);
296         }
297     }
298 
299     @Override
300     public IoUringSocketChannelConfig setKeepAlive(boolean keepAlive) {
301         try {
302             ((IoUringSocketChannel) channel).socket.setKeepAlive(keepAlive);
303             return this;
304         } catch (IOException e) {
305             throw new ChannelException(e);
306         }
307     }
308 
309     @Override
310     public IoUringSocketChannelConfig setPerformancePreferences(
311             int connectionTime, int latency, int bandwidth) {
312         return this;
313     }
314 
315     @Override
316     public IoUringSocketChannelConfig setReceiveBufferSize(int receiveBufferSize) {
317         try {
318             ((IoUringSocketChannel) channel).socket.setReceiveBufferSize(receiveBufferSize);
319             return this;
320         } catch (IOException e) {
321             throw new ChannelException(e);
322         }
323     }
324 
325     @Override
326     public IoUringSocketChannelConfig setReuseAddress(boolean reuseAddress) {
327         try {
328             ((IoUringSocketChannel) channel).socket.setReuseAddress(reuseAddress);
329             return this;
330         } catch (IOException e) {
331             throw new ChannelException(e);
332         }
333     }
334 
335     @Override
336     public IoUringSocketChannelConfig setSendBufferSize(int sendBufferSize) {
337         try {
338             ((IoUringSocketChannel) channel).socket.setSendBufferSize(sendBufferSize);
339             return this;
340         } catch (IOException e) {
341             throw new ChannelException(e);
342         }
343     }
344 
345     @Override
346     public int getReceiveBufferSize() {
347         try {
348             return ((IoUringSocketChannel) channel).socket.getReceiveBufferSize();
349         } catch (IOException e) {
350             throw new ChannelException(e);
351         }
352     }
353 
354     @Override
355     public IoUringSocketChannelConfig setSoLinger(int soLinger) {
356         try {
357             ((IoUringSocketChannel) channel).socket.setSoLinger(soLinger);
358             return this;
359         } catch (IOException e) {
360             throw new ChannelException(e);
361         }
362     }
363 
364     @Override
365     public IoUringSocketChannelConfig setTcpNoDelay(boolean tcpNoDelay) {
366         try {
367             ((IoUringSocketChannel) channel).socket.setTcpNoDelay(tcpNoDelay);
368             return this;
369         } catch (IOException e) {
370             throw new ChannelException(e);
371         }
372     }
373 
374     /**
375      * Set the {@code TCP_CORK} option on the socket. See {@code man 7 tcp} for more details.
376      */
377     public IoUringSocketChannelConfig setTcpCork(boolean tcpCork) {
378         try {
379             ((IoUringSocketChannel) channel).socket.setTcpCork(tcpCork);
380             return this;
381         } catch (IOException e) {
382             throw new ChannelException(e);
383         }
384     }
385 
386     /**
387      * Set the {@code SO_BUSY_POLL} option on the socket. See {@code man 7 tcp} for more details.
388      */
389     public IoUringSocketChannelConfig setSoBusyPoll(int loopMicros) {
390         try {
391             ((IoUringSocketChannel) channel).socket.setSoBusyPoll(loopMicros);
392             return this;
393         } catch (IOException e) {
394             throw new ChannelException(e);
395         }
396     }
397 
398     /**
399      * Set the {@code TCP_NOTSENT_LOWAT} option on the socket. See {@code man 7 tcp} for more details.
400      *
401      * @param tcpNotSentLowAt is a uint32_t
402      */
403     public IoUringSocketChannelConfig setTcpNotSentLowAt(long tcpNotSentLowAt) {
404         try {
405             ((IoUringSocketChannel) channel).socket.setTcpNotSentLowAt(tcpNotSentLowAt);
406             return this;
407         } catch (IOException e) {
408             throw new ChannelException(e);
409         }
410     }
411 
412     @Override
413     public IoUringSocketChannelConfig setTrafficClass(int trafficClass) {
414         try {
415             ((IoUringSocketChannel) channel).socket.setTrafficClass(trafficClass);
416             return this;
417         } catch (IOException e) {
418             throw new ChannelException(e);
419         }
420     }
421 
422     /**
423      * Set the {@code TCP_KEEPIDLE} option on the socket. See {@code man 7 tcp} for more details.
424      */
425     public IoUringSocketChannelConfig setTcpKeepIdle(int seconds) {
426         try {
427             ((IoUringSocketChannel) channel).socket.setTcpKeepIdle(seconds);
428             return this;
429         } catch (IOException e) {
430             throw new ChannelException(e);
431         }
432     }
433 
434     /**
435      * Set the {@code TCP_KEEPINTVL} option on the socket. See {@code man 7 tcp} for more details.
436      */
437     public IoUringSocketChannelConfig setTcpKeepIntvl(int seconds) {
438         try {
439             ((IoUringSocketChannel) channel).socket.setTcpKeepIntvl(seconds);
440             return this;
441         } catch (IOException e) {
442             throw new ChannelException(e);
443         }
444     }
445 
446     /**
447      * @deprecated use {@link #setTcpKeepCnt(int)}
448      */
449     @Deprecated
450     public IoUringSocketChannelConfig setTcpKeepCntl(int probes) {
451         return setTcpKeepCnt(probes);
452     }
453 
454     /**
455      * Set the {@code TCP_KEEPCNT} option on the socket. See {@code man 7 tcp} for more details.
456      */
457     public IoUringSocketChannelConfig setTcpKeepCnt(int probes) {
458         try {
459             ((IoUringSocketChannel) channel).socket.setTcpKeepCnt(probes);
460             return this;
461         } catch (IOException e) {
462             throw new ChannelException(e);
463         }
464     }
465 
466     /**
467      * Set the {@code TCP_USER_TIMEOUT} option on the socket. See {@code man 7 tcp} for more details.
468      */
469     public IoUringSocketChannelConfig setTcpUserTimeout(int milliseconds) {
470         try {
471             ((IoUringSocketChannel) channel).socket.setTcpUserTimeout(milliseconds);
472             return this;
473         } catch (IOException e) {
474             throw new ChannelException(e);
475         }
476     }
477 
478     /**
479      * Returns {@code true} if <a href="https://man7.org/linux/man-pages/man7/ip.7.html">IP_TRANSPARENT</a> is enabled,
480      * {@code false} otherwise.
481      */
482     public boolean isIpTransparent() {
483         try {
484             return ((IoUringSocketChannel) channel).socket.isIpTransparent();
485         } catch (IOException e) {
486             throw new ChannelException(e);
487         }
488     }
489 
490     /**
491      * If {@code true} is used <a href="https://man7.org/linux/man-pages/man7/ip.7.html">IP_TRANSPARENT</a> is enabled,
492      * {@code false} for disable it. Default is disabled.
493      */
494     public IoUringSocketChannelConfig setIpTransparent(boolean transparent) {
495         try {
496             ((IoUringSocketChannel) channel).socket.setIpTransparent(transparent);
497             return this;
498         } catch (IOException e) {
499             throw new ChannelException(e);
500         }
501     }
502 
503 //    /**
504 //     * Set the {@code TCP_MD5SIG} option on the socket. See {@code linux/tcp.h} for more details. Keys can only be set
505 //     * on, not read to prevent a potential leak, as they are confidential. Allowing them being read would mean anyone
506 //     * with access to the channel could get them.
507 //     */
508 //    public IOUringSocketChannelConfig setTcpMd5Sig(Map<InetAddress, byte[]> keys) {
509 //        try {
510 //            ((IOUringSocketChannel) channel).setTcpMd5Sig(keys);
511 //            return this;
512 //        } catch (IOException e) {
513 //            throw new ChannelException(e);
514 //        }
515 //    }
516 
517     /**
518      * Set the {@code TCP_QUICKACK} option on the socket. See <a href="https://linux.die.net/man/7/tcp">TCP_QUICKACK</a>
519      * for more details.
520      */
521     public IoUringSocketChannelConfig setTcpQuickAck(boolean quickAck) {
522         try {
523             ((IoUringSocketChannel) channel).socket.setTcpQuickAck(quickAck);
524             return this;
525         } catch (IOException e) {
526             throw new ChannelException(e);
527         }
528     }
529 
530     /**
531      * Returns {@code true} if <a href="https://linux.die.net/man/7/tcp">TCP_QUICKACK</a> is enabled, {@code false}
532      * otherwise.
533      */
534     public boolean isTcpQuickAck() {
535         try {
536             return ((IoUringSocketChannel) channel).socket.isTcpQuickAck();
537         } catch (IOException e) {
538             throw new ChannelException(e);
539         }
540     }
541 
542     /**
543      * Enables client TCP fast open. See this <a href="https://lwn.net/Articles/508865/">LWN article</a> for more info.
544      */
545     public IoUringSocketChannelConfig setTcpFastOpenConnect(boolean fastOpenConnect) {
546         this.tcpFastopen = fastOpenConnect;
547         return this;
548     }
549 
550     /**
551      * Returns {@code true} if {@code TCP_FASTOPEN_CONNECT} is enabled, {@code false} otherwise.
552      */
553     public boolean isTcpFastOpenConnect() {
554         return tcpFastopen;
555     }
556 
557     @Override
558     public boolean isAllowHalfClosure() {
559         return allowHalfClosure;
560     }
561 
562     @Override
563     public IoUringSocketChannelConfig setAllowHalfClosure(boolean allowHalfClosure) {
564         this.allowHalfClosure = allowHalfClosure;
565         return this;
566     }
567 
568     @Override
569     public IoUringSocketChannelConfig setConnectTimeoutMillis(int connectTimeoutMillis) {
570         super.setConnectTimeoutMillis(connectTimeoutMillis);
571         return this;
572     }
573 
574     @Override
575     @Deprecated
576     public IoUringSocketChannelConfig setMaxMessagesPerRead(int maxMessagesPerRead) {
577         super.setMaxMessagesPerRead(maxMessagesPerRead);
578         return this;
579     }
580 
581     @Override
582     public IoUringSocketChannelConfig setWriteSpinCount(int writeSpinCount) {
583         super.setWriteSpinCount(writeSpinCount);
584         return this;
585     }
586 
587     @Override
588     public IoUringSocketChannelConfig setAllocator(ByteBufAllocator allocator) {
589         super.setAllocator(allocator);
590         return this;
591     }
592 
593     @Override
594     public IoUringSocketChannelConfig setRecvByteBufAllocator(RecvByteBufAllocator allocator) {
595         super.setRecvByteBufAllocator(allocator);
596         return this;
597     }
598 
599     @Override
600     public IoUringSocketChannelConfig setAutoRead(boolean autoRead) {
601         super.setAutoRead(autoRead);
602         return this;
603     }
604 
605     @Override
606     public IoUringSocketChannelConfig setAutoClose(boolean autoClose) {
607         super.setAutoClose(autoClose);
608         return this;
609     }
610 
611     @Override
612     @Deprecated
613     public IoUringSocketChannelConfig setWriteBufferHighWaterMark(int writeBufferHighWaterMark) {
614         super.setWriteBufferHighWaterMark(writeBufferHighWaterMark);
615         return this;
616     }
617 
618     @Override
619     @Deprecated
620     public IoUringSocketChannelConfig setWriteBufferLowWaterMark(int writeBufferLowWaterMark) {
621         super.setWriteBufferLowWaterMark(writeBufferLowWaterMark);
622         return this;
623     }
624 
625     @Override
626     public IoUringSocketChannelConfig setWriteBufferWaterMark(WriteBufferWaterMark writeBufferWaterMark) {
627         super.setWriteBufferWaterMark(writeBufferWaterMark);
628         return this;
629     }
630 
631     @Override
632     public IoUringSocketChannelConfig setMessageSizeEstimator(MessageSizeEstimator estimator) {
633         super.setMessageSizeEstimator(estimator);
634         return this;
635     }
636 
637     private int getWriteZeroCopyThreshold() {
638         return writeZeroCopyThreshold;
639     }
640 
641     IoUringSocketChannelConfig setWriteZeroCopyThreshold(int setWriteZeroCopyThreshold) {
642         if (setWriteZeroCopyThreshold == DISABLE_WRITE_ZERO_COPY) {
643             this.writeZeroCopyThreshold = DISABLE_WRITE_ZERO_COPY;
644         } else {
645             this.writeZeroCopyThreshold =
646                     ObjectUtil.checkPositiveOrZero(setWriteZeroCopyThreshold, "setWriteZeroCopyThreshold");
647         }
648         return this;
649     }
650 
651     boolean shouldWriteZeroCopy(int amount) {
652         // This can reduce one read operation on a volatile field.
653         int threshold = this.getWriteZeroCopyThreshold();
654         return threshold != DISABLE_WRITE_ZERO_COPY && amount >= threshold;
655     }
656 }