@@ -613,10 +613,13 @@ impl ProbeBW {
613613 self . cycle . probe_up_acked += congestion_event. bytes_acked ;
614614 }
615615
616- if let Some ( probe_up_bytes) = self . cycle . probe_up_bytes . as_mut ( ) {
617- if self . cycle . probe_up_acked >= * probe_up_bytes {
618- let delta = self . cycle . probe_up_acked / * probe_up_bytes;
619- self . cycle . probe_up_acked -= * probe_up_bytes;
616+ if let Some ( probe_up_bytes) = self . cycle . probe_up_bytes {
617+ if self . cycle . probe_up_acked >= probe_up_bytes {
618+ let delta = self . cycle . probe_up_acked / probe_up_bytes;
619+ // probe_up_acked is set to the remainder mod probe_up_bytes;
620+ // probe_up_acked >= delta * probe_up_bytes so this
621+ // can't underflow.
622+ self . cycle . probe_up_acked -= delta * probe_up_bytes;
620623 let new_inflight_hi =
621624 self . model . inflight_hi ( ) + delta * DEFAULT_MSS ;
622625 if new_inflight_hi > self . model . inflight_hi ( ) {
@@ -640,3 +643,87 @@ impl ProbeBW {
640643 next_mode
641644 }
642645}
646+
647+ #[ cfg( test) ]
648+ mod tests {
649+ use rstest:: rstest;
650+
651+ use super :: * ;
652+ use crate :: recovery:: gcongestion:: bbr2:: SendTimeState ;
653+ use crate :: recovery:: gcongestion:: bbr2:: DEFAULT_PARAMS ;
654+
655+ #[ rstest]
656+ fn probe_upward ( #[ values( 100 , 10_000 , 65_536 , 300_000 ) ] step : usize ) {
657+ let test_event =
658+ |probe_bw : & ProbeBW , bytes_acked : usize , end_of_round_trip : bool | {
659+ BBRv2CongestionEvent {
660+ event_time : probe_bw. cycle . start_time ,
661+ prior_cwnd : probe_bw. model . inflight_hi ( ) ,
662+ prior_bytes_in_flight : probe_bw. model . inflight_hi ( ) ,
663+ bytes_in_flight : 0 ,
664+ bytes_acked,
665+ bytes_lost : 0 ,
666+ end_of_round_trip,
667+ is_probing_for_bandwidth : true ,
668+ sample_max_bandwidth : None ,
669+ sample_min_rtt : None ,
670+ last_packet_send_state : SendTimeState :: default ( ) ,
671+ }
672+ } ;
673+
674+ let do_probe_up = |probe_bw : & mut ProbeBW ,
675+ params : & Params ,
676+ total_bytes : usize | {
677+ let mut remaining = total_bytes;
678+ loop {
679+ let congestion_event =
680+ test_event ( probe_bw, step. min ( remaining) , false ) ;
681+
682+ probe_bw. probe_inflight_high_upward ( & congestion_event, params) ;
683+
684+ if remaining > step {
685+ remaining -= step;
686+ } else {
687+ break ;
688+ }
689+ }
690+ } ;
691+
692+ let params = & DEFAULT_PARAMS ;
693+ let model = BBRv2NetworkModel :: new ( & params, Duration :: from_millis ( 333 ) ) ;
694+ let cycle = Cycle :: default ( ) ;
695+ let mut probe_bw = ProbeBW { model, cycle } ;
696+ probe_bw. model . set_inflight_hi ( 100_000 ) ;
697+ probe_bw. raise_inflight_high_slope ( 100_000 ) ;
698+ assert_eq ! ( probe_bw. cycle. probe_up_rounds, 1 ) ;
699+ assert_eq ! ( probe_bw. cycle. probe_up_bytes, Some ( 100_000 ) ) ;
700+
701+ assert_eq ! ( probe_bw. model. inflight_hi( ) , 100_000 ) ;
702+ do_probe_up ( & mut probe_bw, & params, 1_000_000 ) ;
703+ // End inflight_hi should be independent of step size.
704+ assert_eq ! ( probe_bw. model. inflight_hi( ) , 113_000 ) ;
705+
706+ // Slope is increased at the end of round by decreasing probe_up_bytes.
707+ probe_bw. probe_inflight_high_upward (
708+ & test_event ( & probe_bw, 10_000 , true ) ,
709+ params,
710+ ) ;
711+ assert_eq ! ( probe_bw. cycle. probe_up_rounds, 2 ) ;
712+ assert_eq ! ( probe_bw. cycle. probe_up_bytes, Some ( 56500 ) ) ;
713+
714+ do_probe_up ( & mut probe_bw, & params, 1_000_000 ) ;
715+ // End inflight_hi should be independent of step size.
716+ assert_eq ! ( probe_bw. model. inflight_hi( ) , 135_100 ) ;
717+
718+ probe_bw. probe_inflight_high_upward (
719+ & test_event ( & probe_bw, 10_000 , true ) ,
720+ params,
721+ ) ;
722+ assert_eq ! ( probe_bw. cycle. probe_up_rounds, 3 ) ;
723+ assert_eq ! ( probe_bw. cycle. probe_up_bytes, Some ( 33775 ) ) ;
724+
725+ do_probe_up ( & mut probe_bw, & params, 1_000_000 ) ;
726+ // End inflight_hi should be independent of step size.
727+ assert_eq ! ( probe_bw. model. inflight_hi( ) , 174_100 ) ;
728+ }
729+ }
0 commit comments