@@ -161,6 +161,11 @@ const RTTE_MIN_RTO: u32 = 1000;
161161// seconds
162162const RTTE_MAX_RTO : u32 = 60_000 ;
163163
164+ // BBR: Window length for min_rtt filter (in seconds)
165+ // This matches the Linux kernel BBR implementation (tcp_bbr.c:135)
166+ #[ cfg( feature = "socket-tcp-bbr" ) ]
167+ const BBR_MIN_RTT_WIN_SEC : u64 = 10 ;
168+
164169#[ derive( Debug , Clone , Copy ) ]
165170#[ cfg_attr( feature = "defmt" , derive( defmt:: Format ) ) ]
166171struct RttEstimator {
@@ -176,6 +181,12 @@ struct RttEstimator {
176181 timestamp : Option < ( Instant , TcpSeqNumber ) > ,
177182 max_seq_sent : Option < TcpSeqNumber > ,
178183 rto_count : u8 ,
184+ /// BBR: Minimum RTT observed in the last 10 seconds
185+ #[ cfg( feature = "socket-tcp-bbr" ) ]
186+ min_rtt_value : u32 ,
187+ /// BBR: Timestamp when min_rtt was last updated
188+ #[ cfg( feature = "socket-tcp-bbr" ) ]
189+ min_rtt_stamp : Option < Instant > ,
179190}
180191
181192impl Default for RttEstimator {
@@ -188,6 +199,10 @@ impl Default for RttEstimator {
188199 timestamp : None ,
189200 max_seq_sent : None ,
190201 rto_count : 0 ,
202+ #[ cfg( feature = "socket-tcp-bbr" ) ]
203+ min_rtt_value : u32:: MAX ,
204+ #[ cfg( feature = "socket-tcp-bbr" ) ]
205+ min_rtt_stamp : None ,
191206 }
192207 }
193208}
@@ -197,7 +212,33 @@ impl RttEstimator {
197212 Duration :: from_millis ( self . rto as _ )
198213 }
199214
200- fn sample ( & mut self , new_rtt : u32 ) {
215+ #[ cfg( feature = "socket-tcp-bbr" ) ]
216+ pub ( super ) fn min_rtt ( & self ) -> Duration {
217+ // Return the actual minimum RTT observed in the window, not SRTT
218+ // If no measurement yet, fall back to SRTT as a reasonable estimate
219+ if self . min_rtt_value == u32:: MAX {
220+ Duration :: from_millis ( self . srtt as _ )
221+ } else {
222+ Duration :: from_millis ( self . min_rtt_value as _ )
223+ }
224+ }
225+
226+ #[ cfg( feature = "socket-tcp-bbr" ) ]
227+ pub ( super ) fn is_min_rtt_expired ( & self , now : Instant ) -> bool {
228+ // Check if the min_rtt window has expired (10 seconds)
229+ // This matches Linux kernel BBR behavior (tcp_bbr.c:948-949)
230+ if let Some ( stamp) = self . min_rtt_stamp {
231+ if now >= stamp {
232+ ( now - stamp) > Duration :: from_secs ( BBR_MIN_RTT_WIN_SEC )
233+ } else {
234+ false // Time went backwards, don't consider expired
235+ }
236+ } else {
237+ true // No measurement yet, consider expired
238+ }
239+ }
240+
241+ fn sample ( & mut self , new_rtt : u32 , #[ allow( unused_variables) ] now : Instant ) {
201242 if self . have_measurement {
202243 // RFC 6298 (2.3) When a subsequent RTT measurement R' is made, a host MUST set (...)
203244 let diff = ( self . srtt as i32 - new_rtt as i32 ) . unsigned_abs ( ) ;
@@ -216,6 +257,27 @@ impl RttEstimator {
216257
217258 self . rto_count = 0 ;
218259
260+ // BBR: Track minimum RTT in a sliding 10-second window
261+ // This matches Linux kernel BBR behavior (tcp_bbr.c:947-955)
262+ #[ cfg( feature = "socket-tcp-bbr" ) ]
263+ {
264+ let expired = self . is_min_rtt_expired ( now) ;
265+
266+ // Update min_rtt if:
267+ // 1. New sample is lower than current minimum, OR
268+ // 2. The window has expired (need fresh measurement)
269+ if new_rtt < self . min_rtt_value || expired {
270+ self . min_rtt_value = new_rtt;
271+ self . min_rtt_stamp = Some ( now) ;
272+
273+ tcp_trace ! (
274+ "rtte: min_rtt updated to {:?}ms (expired={})" ,
275+ new_rtt,
276+ expired
277+ ) ;
278+ }
279+ }
280+
219281 tcp_trace ! (
220282 "rtte: sample={:?} srtt={:?} rttvar={:?} rto={:?}" ,
221283 new_rtt,
@@ -242,7 +304,7 @@ impl RttEstimator {
242304 fn on_ack ( & mut self , timestamp : Instant , seq : TcpSeqNumber ) {
243305 if let Some ( ( sent_timestamp, sent_seq) ) = self . timestamp {
244306 if seq >= sent_seq {
245- self . sample ( ( timestamp - sent_timestamp) . total_millis ( ) as u32 ) ;
307+ self . sample ( ( timestamp - sent_timestamp) . total_millis ( ) as u32 , timestamp ) ;
246308 self . timestamp = None ;
247309 }
248310 }
@@ -454,6 +516,9 @@ pub enum CongestionControl {
454516
455517 #[ cfg( feature = "socket-tcp-cubic" ) ]
456518 Cubic ,
519+
520+ #[ cfg( feature = "socket-tcp-bbr" ) ]
521+ Bbr ,
457522}
458523
459524/// A Transmission Control Protocol socket.
@@ -657,6 +722,9 @@ impl<'a> Socket<'a> {
657722
658723 #[ cfg( feature = "socket-tcp-cubic" ) ]
659724 CongestionControl :: Cubic => AnyController :: Cubic ( cubic:: Cubic :: new ( ) ) ,
725+
726+ #[ cfg( feature = "socket-tcp-bbr" ) ]
727+ CongestionControl :: Bbr => AnyController :: Bbr ( bbr:: Bbr :: new ( ) ) ,
660728 }
661729 }
662730
@@ -672,6 +740,9 @@ impl<'a> Socket<'a> {
672740
673741 #[ cfg( feature = "socket-tcp-cubic" ) ]
674742 AnyController :: Cubic ( _) => CongestionControl :: Cubic ,
743+
744+ #[ cfg( feature = "socket-tcp-bbr" ) ]
745+ AnyController :: Bbr ( _) => CongestionControl :: Bbr ,
675746 }
676747 }
677748
@@ -2368,6 +2439,12 @@ impl<'a> Socket<'a> {
23682439 . inner_mut ( )
23692440 . pre_transmit ( cx. now ( ) ) ;
23702441
2442+ // Notify congestion controller about available data for app-limited tracking
2443+ let bytes_available = self . tx_buffer . len ( ) ;
2444+ self . congestion_controller
2445+ . inner_mut ( )
2446+ . on_send_ready ( cx. now ( ) , bytes_available) ;
2447+
23712448 // Check if any state needs to be changed because of a timer.
23722449 if self . timed_out ( cx. now ( ) ) {
23732450 // If a timeout expires, we should abort the connection.
@@ -8570,9 +8647,11 @@ mod test {
85708647 2076 , 2060 , 2048 , 2036 , 2028 , 2024 , 2020 , 2016 , 2012 , 2012 ,
85718648 ] ;
85728649
8650+ let mut now = Instant :: from_millis ( 0 ) ;
85738651 for & rto in rtos {
8574- r. sample ( 2000 ) ;
8652+ r. sample ( 2000 , now ) ;
85758653 assert_eq ! ( r. retransmission_timeout( ) , Duration :: from_millis( rto) ) ;
8654+ now += Duration :: from_millis ( 100 ) ; // Advance time
85768655 }
85778656 }
85788657
0 commit comments