| // Copyright 2016 The Chromium Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | // BBR (Bottleneck Bandwidth and RTT) congestion control algorithm. | 
 |  | 
 | #ifndef QUICHE_QUIC_CORE_CONGESTION_CONTROL_BBR_SENDER_H_ | 
 | #define QUICHE_QUIC_CORE_CONGESTION_CONTROL_BBR_SENDER_H_ | 
 |  | 
 | #include <cstdint> | 
 | #include <ostream> | 
 | #include <string> | 
 |  | 
 | #include "quiche/quic/core/congestion_control/bandwidth_sampler.h" | 
 | #include "quiche/quic/core/congestion_control/send_algorithm_interface.h" | 
 | #include "quiche/quic/core/congestion_control/windowed_filter.h" | 
 | #include "quiche/quic/core/crypto/quic_random.h" | 
 | #include "quiche/quic/core/quic_bandwidth.h" | 
 | #include "quiche/quic/core/quic_packet_number.h" | 
 | #include "quiche/quic/core/quic_packets.h" | 
 | #include "quiche/quic/core/quic_time.h" | 
 | #include "quiche/quic/core/quic_unacked_packet_map.h" | 
 | #include "quiche/quic/platform/api/quic_export.h" | 
 | #include "quiche/quic/platform/api/quic_flags.h" | 
 |  | 
 | namespace quic { | 
 |  | 
 | class RttStats; | 
 |  | 
 | // BbrSender implements BBR congestion control algorithm.  BBR aims to estimate | 
 | // the current available Bottleneck Bandwidth and RTT (hence the name), and | 
 | // regulates the pacing rate and the size of the congestion window based on | 
 | // those signals. | 
 | // | 
 | // BBR relies on pacing in order to function properly.  Do not use BBR when | 
 | // pacing is disabled. | 
 | // | 
 | // TODO(vasilvv): implement traffic policer (long-term sampling) mode. | 
 | class QUICHE_EXPORT BbrSender : public SendAlgorithmInterface { | 
 |  public: | 
 |   enum Mode { | 
 |     // Startup phase of the connection. | 
 |     STARTUP, | 
 |     // After achieving the highest possible bandwidth during the startup, lower | 
 |     // the pacing rate in order to drain the queue. | 
 |     DRAIN, | 
 |     // Cruising mode. | 
 |     PROBE_BW, | 
 |     // Temporarily slow down sending in order to empty the buffer and measure | 
 |     // the real minimum RTT. | 
 |     PROBE_RTT, | 
 |   }; | 
 |  | 
 |   // Indicates how the congestion control limits the amount of bytes in flight. | 
 |   enum RecoveryState { | 
 |     // Do not limit. | 
 |     NOT_IN_RECOVERY, | 
 |     // Allow an extra outstanding byte for each byte acknowledged. | 
 |     CONSERVATION, | 
 |     // Allow two extra outstanding bytes for each byte acknowledged (slow | 
 |     // start). | 
 |     GROWTH | 
 |   }; | 
 |  | 
 |   // Debug state can be exported in order to troubleshoot potential congestion | 
 |   // control issues. | 
 |   struct QUICHE_EXPORT DebugState { | 
 |     explicit DebugState(const BbrSender& sender); | 
 |     DebugState(const DebugState& state); | 
 |  | 
 |     Mode mode; | 
 |     QuicBandwidth max_bandwidth; | 
 |     QuicRoundTripCount round_trip_count; | 
 |     int gain_cycle_index; | 
 |     QuicByteCount congestion_window; | 
 |  | 
 |     bool is_at_full_bandwidth; | 
 |     QuicBandwidth bandwidth_at_last_round; | 
 |     QuicRoundTripCount rounds_without_bandwidth_gain; | 
 |  | 
 |     QuicTime::Delta min_rtt; | 
 |     QuicTime min_rtt_timestamp; | 
 |  | 
 |     RecoveryState recovery_state; | 
 |     QuicByteCount recovery_window; | 
 |  | 
 |     bool last_sample_is_app_limited; | 
 |     QuicPacketNumber end_of_app_limited_phase; | 
 |   }; | 
 |  | 
 |   BbrSender(QuicTime now, const RttStats* rtt_stats, | 
 |             const QuicUnackedPacketMap* unacked_packets, | 
 |             QuicPacketCount initial_tcp_congestion_window, | 
 |             QuicPacketCount max_tcp_congestion_window, QuicRandom* random, | 
 |             QuicConnectionStats* stats); | 
 |   BbrSender(const BbrSender&) = delete; | 
 |   BbrSender& operator=(const BbrSender&) = delete; | 
 |   ~BbrSender() override; | 
 |  | 
 |   // Start implementation of SendAlgorithmInterface. | 
 |   bool InSlowStart() const override; | 
 |   bool InRecovery() const override; | 
 |  | 
 |   void SetFromConfig(const QuicConfig& config, | 
 |                      Perspective perspective) override; | 
 |   void ApplyConnectionOptions(const QuicTagVector& connection_options) override; | 
 |  | 
 |   void AdjustNetworkParameters(const NetworkParams& params) override; | 
 |   void SetInitialCongestionWindowInPackets( | 
 |       QuicPacketCount congestion_window) override; | 
 |   void SetApplicationDrivenPacingRate( | 
 |       QuicBandwidth /*application_bandwidth_target*/) override {} | 
 |   void OnCongestionEvent(bool rtt_updated, QuicByteCount prior_in_flight, | 
 |                          QuicTime event_time, | 
 |                          const AckedPacketVector& acked_packets, | 
 |                          const LostPacketVector& lost_packets, | 
 |                          QuicPacketCount num_ect, | 
 |                          QuicPacketCount num_ce) override; | 
 |   void OnPacketSent(QuicTime sent_time, QuicByteCount bytes_in_flight, | 
 |                     QuicPacketNumber packet_number, QuicByteCount bytes, | 
 |                     HasRetransmittableData is_retransmittable) override; | 
 |   void OnPacketNeutered(QuicPacketNumber packet_number) override; | 
 |   void OnRetransmissionTimeout(bool /*packets_retransmitted*/) override {} | 
 |   void OnConnectionMigration() override {} | 
 |   bool CanSend(QuicByteCount bytes_in_flight) override; | 
 |   QuicBandwidth PacingRate(QuicByteCount bytes_in_flight) const override; | 
 |   QuicBandwidth BandwidthEstimate() const override; | 
 |   bool HasGoodBandwidthEstimateForResumption() const override { | 
 |     return has_non_app_limited_sample(); | 
 |   } | 
 |   QuicByteCount GetCongestionWindow() const override; | 
 |   QuicByteCount GetSlowStartThreshold() const override; | 
 |   CongestionControlType GetCongestionControlType() const override; | 
 |   std::string GetDebugState() const override; | 
 |   void OnApplicationLimited(QuicByteCount bytes_in_flight) override; | 
 |   void PopulateConnectionStats(QuicConnectionStats* stats) const override; | 
 |   bool EnableECT0() override { return false; } | 
 |   bool EnableECT1() override { return false; } | 
 |   // End implementation of SendAlgorithmInterface. | 
 |  | 
 |   // Gets the number of RTTs BBR remains in STARTUP phase. | 
 |   QuicRoundTripCount num_startup_rtts() const { return num_startup_rtts_; } | 
 |   bool has_non_app_limited_sample() const { | 
 |     return has_non_app_limited_sample_; | 
 |   } | 
 |  | 
 |   // Sets the pacing gain used in STARTUP.  Must be greater than 1. | 
 |   void set_high_gain(float high_gain) { | 
 |     QUICHE_DCHECK_LT(1.0f, high_gain); | 
 |     high_gain_ = high_gain; | 
 |     if (mode_ == STARTUP) { | 
 |       pacing_gain_ = high_gain; | 
 |     } | 
 |   } | 
 |  | 
 |   // Sets the CWND gain used in STARTUP.  Must be greater than 1. | 
 |   void set_high_cwnd_gain(float high_cwnd_gain) { | 
 |     QUICHE_DCHECK_LT(1.0f, high_cwnd_gain); | 
 |     high_cwnd_gain_ = high_cwnd_gain; | 
 |     if (mode_ == STARTUP) { | 
 |       congestion_window_gain_ = high_cwnd_gain; | 
 |     } | 
 |   } | 
 |  | 
 |   // Sets the gain used in DRAIN.  Must be less than 1. | 
 |   void set_drain_gain(float drain_gain) { | 
 |     QUICHE_DCHECK_GT(1.0f, drain_gain); | 
 |     drain_gain_ = drain_gain; | 
 |   } | 
 |  | 
 |   // Returns the current estimate of the RTT of the connection.  Outside of the | 
 |   // edge cases, this is minimum RTT. | 
 |   QuicTime::Delta GetMinRtt() const; | 
 |  | 
 |   DebugState ExportDebugState() const; | 
 |  | 
 |  private: | 
 |   // For switching send algorithm mid connection. | 
 |   friend class Bbr2Sender; | 
 |  | 
 |   using MaxBandwidthFilter = | 
 |       WindowedFilter<QuicBandwidth, MaxFilter<QuicBandwidth>, | 
 |                      QuicRoundTripCount, QuicRoundTripCount>; | 
 |  | 
 |   using MaxAckHeightFilter = | 
 |       WindowedFilter<QuicByteCount, MaxFilter<QuicByteCount>, | 
 |                      QuicRoundTripCount, QuicRoundTripCount>; | 
 |  | 
 |   // Computes the target congestion window using the specified gain. | 
 |   QuicByteCount GetTargetCongestionWindow(float gain) const; | 
 |   // The target congestion window during PROBE_RTT. | 
 |   QuicByteCount ProbeRttCongestionWindow() const; | 
 |   bool MaybeUpdateMinRtt(QuicTime now, QuicTime::Delta sample_min_rtt); | 
 |  | 
 |   // Enters the STARTUP mode. | 
 |   void EnterStartupMode(QuicTime now); | 
 |   // Enters the PROBE_BW mode. | 
 |   void EnterProbeBandwidthMode(QuicTime now); | 
 |  | 
 |   // Updates the round-trip counter if a round-trip has passed.  Returns true if | 
 |   // the counter has been advanced. | 
 |   bool UpdateRoundTripCounter(QuicPacketNumber last_acked_packet); | 
 |  | 
 |   // Updates the current gain used in PROBE_BW mode. | 
 |   void UpdateGainCyclePhase(QuicTime now, QuicByteCount prior_in_flight, | 
 |                             bool has_losses); | 
 |   // Tracks for how many round-trips the bandwidth has not increased | 
 |   // significantly. | 
 |   void CheckIfFullBandwidthReached(const SendTimeState& last_packet_send_state); | 
 |   // Transitions from STARTUP to DRAIN and from DRAIN to PROBE_BW if | 
 |   // appropriate. | 
 |   void MaybeExitStartupOrDrain(QuicTime now); | 
 |   // Decides whether to enter or exit PROBE_RTT. | 
 |   void MaybeEnterOrExitProbeRtt(QuicTime now, bool is_round_start, | 
 |                                 bool min_rtt_expired); | 
 |   // Determines whether BBR needs to enter, exit or advance state of the | 
 |   // recovery. | 
 |   void UpdateRecoveryState(QuicPacketNumber last_acked_packet, bool has_losses, | 
 |                            bool is_round_start); | 
 |  | 
 |   // Updates the ack aggregation max filter in bytes. | 
 |   // Returns the most recent addition to the filter, or |newly_acked_bytes| if | 
 |   // nothing was fed in to the filter. | 
 |   QuicByteCount UpdateAckAggregationBytes(QuicTime ack_time, | 
 |                                           QuicByteCount newly_acked_bytes); | 
 |  | 
 |   // Determines the appropriate pacing rate for the connection. | 
 |   void CalculatePacingRate(QuicByteCount bytes_lost); | 
 |   // Determines the appropriate congestion window for the connection. | 
 |   void CalculateCongestionWindow(QuicByteCount bytes_acked, | 
 |                                  QuicByteCount excess_acked); | 
 |   // Determines the appropriate window that constrains the in-flight during | 
 |   // recovery. | 
 |   void CalculateRecoveryWindow(QuicByteCount bytes_acked, | 
 |                                QuicByteCount bytes_lost); | 
 |  | 
 |   // Called right before exiting STARTUP. | 
 |   void OnExitStartup(QuicTime now); | 
 |  | 
 |   // Return whether we should exit STARTUP due to excessive loss. | 
 |   bool ShouldExitStartupDueToLoss( | 
 |       const SendTimeState& last_packet_send_state) const; | 
 |  | 
 |   const RttStats* rtt_stats_; | 
 |   const QuicUnackedPacketMap* unacked_packets_; | 
 |   QuicRandom* random_; | 
 |   QuicConnectionStats* stats_; | 
 |  | 
 |   Mode mode_; | 
 |  | 
 |   // Bandwidth sampler provides BBR with the bandwidth measurements at | 
 |   // individual points. | 
 |   BandwidthSampler sampler_; | 
 |  | 
 |   // The number of the round trips that have occurred during the connection. | 
 |   QuicRoundTripCount round_trip_count_; | 
 |  | 
 |   // The packet number of the most recently sent packet. | 
 |   QuicPacketNumber last_sent_packet_; | 
 |   // Acknowledgement of any packet after |current_round_trip_end_| will cause | 
 |   // the round trip counter to advance. | 
 |   QuicPacketNumber current_round_trip_end_; | 
 |  | 
 |   // Number of congestion events with some losses, in the current round. | 
 |   int64_t num_loss_events_in_round_; | 
 |  | 
 |   // Number of total bytes lost in the current round. | 
 |   QuicByteCount bytes_lost_in_round_; | 
 |  | 
 |   // The filter that tracks the maximum bandwidth over the multiple recent | 
 |   // round-trips. | 
 |   MaxBandwidthFilter max_bandwidth_; | 
 |  | 
 |   // Minimum RTT estimate.  Automatically expires within 10 seconds (and | 
 |   // triggers PROBE_RTT mode) if no new value is sampled during that period. | 
 |   QuicTime::Delta min_rtt_; | 
 |   // The time at which the current value of |min_rtt_| was assigned. | 
 |   QuicTime min_rtt_timestamp_; | 
 |  | 
 |   // The maximum allowed number of bytes in flight. | 
 |   QuicByteCount congestion_window_; | 
 |  | 
 |   // The initial value of the |congestion_window_|. | 
 |   QuicByteCount initial_congestion_window_; | 
 |  | 
 |   // The largest value the |congestion_window_| can achieve. | 
 |   QuicByteCount max_congestion_window_; | 
 |  | 
 |   // The smallest value the |congestion_window_| can achieve. | 
 |   QuicByteCount min_congestion_window_; | 
 |  | 
 |   // The pacing gain applied during the STARTUP phase. | 
 |   float high_gain_; | 
 |  | 
 |   // The CWND gain applied during the STARTUP phase. | 
 |   float high_cwnd_gain_; | 
 |  | 
 |   // The pacing gain applied during the DRAIN phase. | 
 |   float drain_gain_; | 
 |  | 
 |   // The current pacing rate of the connection. | 
 |   QuicBandwidth pacing_rate_; | 
 |  | 
 |   // The gain currently applied to the pacing rate. | 
 |   float pacing_gain_; | 
 |   // The gain currently applied to the congestion window. | 
 |   float congestion_window_gain_; | 
 |  | 
 |   // The gain used for the congestion window during PROBE_BW.  Latched from | 
 |   // quic_bbr_cwnd_gain flag. | 
 |   const float congestion_window_gain_constant_; | 
 |   // The number of RTTs to stay in STARTUP mode.  Defaults to 3. | 
 |   QuicRoundTripCount num_startup_rtts_; | 
 |  | 
 |   // Number of round-trips in PROBE_BW mode, used for determining the current | 
 |   // pacing gain cycle. | 
 |   int cycle_current_offset_; | 
 |   // The time at which the last pacing gain cycle was started. | 
 |   QuicTime last_cycle_start_; | 
 |  | 
 |   // Indicates whether the connection has reached the full bandwidth mode. | 
 |   bool is_at_full_bandwidth_; | 
 |   // Number of rounds during which there was no significant bandwidth increase. | 
 |   QuicRoundTripCount rounds_without_bandwidth_gain_; | 
 |   // The bandwidth compared to which the increase is measured. | 
 |   QuicBandwidth bandwidth_at_last_round_; | 
 |  | 
 |   // Set to true upon exiting quiescence. | 
 |   bool exiting_quiescence_; | 
 |  | 
 |   // Time at which PROBE_RTT has to be exited.  Setting it to zero indicates | 
 |   // that the time is yet unknown as the number of packets in flight has not | 
 |   // reached the required value. | 
 |   QuicTime exit_probe_rtt_at_; | 
 |   // Indicates whether a round-trip has passed since PROBE_RTT became active. | 
 |   bool probe_rtt_round_passed_; | 
 |  | 
 |   // Indicates whether the most recent bandwidth sample was marked as | 
 |   // app-limited. | 
 |   bool last_sample_is_app_limited_; | 
 |   // Indicates whether any non app-limited samples have been recorded. | 
 |   bool has_non_app_limited_sample_; | 
 |  | 
 |   // Current state of recovery. | 
 |   RecoveryState recovery_state_; | 
 |   // Receiving acknowledgement of a packet after |end_recovery_at_| will cause | 
 |   // BBR to exit the recovery mode.  A value above zero indicates at least one | 
 |   // loss has been detected, so it must not be set back to zero. | 
 |   QuicPacketNumber end_recovery_at_; | 
 |   // A window used to limit the number of bytes in flight during loss recovery. | 
 |   QuicByteCount recovery_window_; | 
 |   // If true, consider all samples in recovery app-limited. | 
 |   bool is_app_limited_recovery_; | 
 |  | 
 |   // When true, pace at 1.5x and disable packet conservation in STARTUP. | 
 |   bool slower_startup_; | 
 |   // When true, disables packet conservation in STARTUP. | 
 |   bool rate_based_startup_; | 
 |  | 
 |   // When true, add the most recent ack aggregation measurement during STARTUP. | 
 |   bool enable_ack_aggregation_during_startup_; | 
 |   // When true, expire the windowed ack aggregation values in STARTUP when | 
 |   // bandwidth increases more than 25%. | 
 |   bool expire_ack_aggregation_in_startup_; | 
 |  | 
 |   // If true, will not exit low gain mode until bytes_in_flight drops below BDP | 
 |   // or it's time for high gain mode. | 
 |   bool drain_to_target_; | 
 |  | 
 |   // If true, slow down pacing rate in STARTUP when overshooting is detected. | 
 |   bool detect_overshooting_; | 
 |   // Bytes lost while detect_overshooting_ is true. | 
 |   QuicByteCount bytes_lost_while_detecting_overshooting_; | 
 |   // Slow down pacing rate if | 
 |   // bytes_lost_while_detecting_overshooting_ * | 
 |   // bytes_lost_multiplier_while_detecting_overshooting_ > IW. | 
 |   uint8_t bytes_lost_multiplier_while_detecting_overshooting_; | 
 |   // When overshooting is detected, do not drop pacing_rate_ below this value / | 
 |   // min_rtt. | 
 |   QuicByteCount cwnd_to_calculate_min_pacing_rate_; | 
 |  | 
 |   // Max congestion window when adjusting network parameters. | 
 |   QuicByteCount max_congestion_window_with_network_parameters_adjusted_; | 
 | }; | 
 |  | 
 | QUICHE_EXPORT std::ostream& operator<<(std::ostream& os, | 
 |                                        const BbrSender::Mode& mode); | 
 | QUICHE_EXPORT std::ostream& operator<<(std::ostream& os, | 
 |                                        const BbrSender::DebugState& state); | 
 |  | 
 | }  // namespace quic | 
 |  | 
 | #endif  // QUICHE_QUIC_CORE_CONGESTION_CONTROL_BBR_SENDER_H_ |