quic_cc_newreno (quic v1.3.1)

View Source

QUIC NewReno congestion control implementation.

This module implements the NewReno congestion control algorithm: - Slow Start: Exponential growth until threshold or loss - HyStart++ (RFC 9406): Safer slow start exit via RTT monitoring - Congestion Avoidance: Linear growth after threshold - Recovery: Multiplicative decrease on loss - Persistent Congestion: Reset on prolonged loss

Phases

1. Slow Start with HyStart++: cwnd += bytes_acked with RTT-based exit 2. Conservative Slow Start (CSS): cwnd += cwnd/4 per RTT for 5 rounds 3. Congestion Avoidance: cwnd += max_datagram_size * bytes_acked / cwnd 4. Recovery: ssthresh = cwnd * 0.5, cwnd = max(ssthresh, min_window)

Summary

Functions

Get the available congestion window (cwnd - bytes_in_flight).

Get bytes currently in flight.

Check if we can send more bytes.

Check if a control message can be sent. Control messages (ticks, ACKs) can exceed cwnd by control_allowance to prevent net_tick_timeout in distribution. RFC 9002 recommends allowing at least one packet for progress.

Get the current congestion window.

Detect persistent congestion from lost packets. Returns true if lost packets span more than PTO * kPersistentCongestionThreshold. LostPackets is a list of {PacketNumber, TimeSent} tuples.

Get the current ECN-CE counter.

Get tokens for sending, consuming them and returning updated state. Returns {AllowedBytes, UpdatedState} where AllowedBytes is at most Size.

Check if in recovery phase.

Check if in slow start phase.

Get the current max datagram size.

Get minimum recovery duration setting. External CC implementations can use this to tune recovery behavior.

Create a new congestion control state with options. Options: - max_datagram_size: Maximum datagram size (default: 1200) - initial_window: Override initial congestion window (default: RFC 9002 formula) Higher values can improve throughput for bulk transfers. Recommended: 32768 (32KB) or 65536 (64KB) for LAN/distribution. - minimum_window: Lower bound for cwnd after congestion events (default: 2 * max_datagram_size per RFC 9002). - min_recovery_duration: Minimum time in recovery before exit (ms, default: 100) Prevents rapid cwnd oscillations on low-latency networks.

Handle a congestion event (packet loss detected). SentTime is the time when the lost packet was sent.

Handle ECN-CE (Congestion Experienced) signal from ACK. RFC 9002: An increase in ECN-CE count is treated as a congestion signal. NewCECount is the ECN-CE count from the received ACK frame. SentTime is the time when the largest acknowledged packet was sent.

Record that a packet was sent.

Process acknowledged packets. AckedBytes is the total size of acknowledged packets. LargestAckedSentTime is the time when the largest acknowledged packet was sent. RFC 9002: Exit recovery when the largest acked packet was sent after recovery started.

Process acknowledged packets. RFC 9002: Exit recovery when the largest acked packet was sent after recovery started. Extended: Also requires minimum recovery duration to pass before exiting.

Process lost packets. LostBytes is the total size of lost packets.

Reset to minimum window on persistent congestion (RFC 9002 SS7.6.2). This is a severe response to prolonged packet loss.

Check if pacing allows sending Size bytes. Returns true if enough tokens are available (including burst allowance).

Calculate delay (in ms) until Size bytes can be sent. Returns 0 if sending is allowed immediately.

Fused cwnd + pacing check for the hot send path. One monotonic_time call, one record match. Replaces can_send/can_send_control + pacing_allows + pacing_delay + get_pacing_tokens on every packet.

Get the slow start threshold.

Update congestion control state when MTU changes.

Update pacing rate based on smoothed RTT and cwnd. RFC 9002: pacing_rate = cwnd / smoothed_rtt Called when RTT estimate is updated.

Types

cc_state/0

-opaque cc_state()

Functions

available_cwnd(Cc_state)

-spec available_cwnd(cc_state()) -> non_neg_integer().

Get the available congestion window (cwnd - bytes_in_flight).

bytes_in_flight(Cc_state)

-spec bytes_in_flight(cc_state()) -> non_neg_integer().

Get bytes currently in flight.

can_send(Cc_state, Size)

-spec can_send(cc_state(), non_neg_integer()) -> boolean().

Check if we can send more bytes.

can_send_control(Cc_state, Size)

-spec can_send_control(cc_state(), non_neg_integer()) -> boolean().

Check if a control message can be sent. Control messages (ticks, ACKs) can exceed cwnd by control_allowance to prevent net_tick_timeout in distribution. RFC 9002 recommends allowing at least one packet for progress.

cwnd(Cc_state)

-spec cwnd(cc_state()) -> non_neg_integer().

Get the current congestion window.

detect_persistent_congestion(LostPackets, PTO, State)

-spec detect_persistent_congestion([{non_neg_integer(), non_neg_integer()}],
                                   non_neg_integer(),
                                   cc_state()) ->
                                      boolean().

Detect persistent congestion from lost packets. Returns true if lost packets span more than PTO * kPersistentCongestionThreshold. LostPackets is a list of {PacketNumber, TimeSent} tuples.

ecn_ce_counter(Cc_state)

-spec ecn_ce_counter(cc_state()) -> non_neg_integer().

Get the current ECN-CE counter.

get_pacing_tokens(Cc_state, Size)

-spec get_pacing_tokens(cc_state(), non_neg_integer()) -> {non_neg_integer(), cc_state()}.

Get tokens for sending, consuming them and returning updated state. Returns {AllowedBytes, UpdatedState} where AllowedBytes is at most Size.

in_recovery(Cc_state)

-spec in_recovery(cc_state()) -> boolean().

Check if in recovery phase.

in_slow_start(Cc_state)

-spec in_slow_start(cc_state()) -> boolean().

Check if in slow start phase.

max_datagram_size(Cc_state)

-spec max_datagram_size(cc_state()) -> pos_integer().

Get the current max datagram size.

min_recovery_duration(Cc_state)

-spec min_recovery_duration(cc_state()) -> non_neg_integer().

Get minimum recovery duration setting. External CC implementations can use this to tune recovery behavior.

new(Opts)

-spec new(quic_cc:cc_opts()) -> cc_state().

Create a new congestion control state with options. Options: - max_datagram_size: Maximum datagram size (default: 1200) - initial_window: Override initial congestion window (default: RFC 9002 formula) Higher values can improve throughput for bulk transfers. Recommended: 32768 (32KB) or 65536 (64KB) for LAN/distribution. - minimum_window: Lower bound for cwnd after congestion events (default: 2 * max_datagram_size per RFC 9002). - min_recovery_duration: Minimum time in recovery before exit (ms, default: 100) Prevents rapid cwnd oscillations on low-latency networks.

on_congestion_event(Cc_state, SentTime)

-spec on_congestion_event(cc_state(), non_neg_integer()) -> cc_state().

Handle a congestion event (packet loss detected). SentTime is the time when the lost packet was sent.

on_ecn_ce(Cc_state, NewCECount)

-spec on_ecn_ce(cc_state(), non_neg_integer()) -> cc_state().

Handle ECN-CE (Congestion Experienced) signal from ACK. RFC 9002: An increase in ECN-CE count is treated as a congestion signal. NewCECount is the ECN-CE count from the received ACK frame. SentTime is the time when the largest acknowledged packet was sent.

on_packet_sent(Cc_state, Size)

-spec on_packet_sent(cc_state(), non_neg_integer()) -> cc_state().

Record that a packet was sent.

on_packets_acked(State, AckedBytes)

-spec on_packets_acked(cc_state(), non_neg_integer()) -> cc_state().

Process acknowledged packets. AckedBytes is the total size of acknowledged packets. LargestAckedSentTime is the time when the largest acknowledged packet was sent. RFC 9002: Exit recovery when the largest acked packet was sent after recovery started.

on_packets_acked(Cc_state, AckedBytes, LargestAckedSentTime)

-spec on_packets_acked(cc_state(), non_neg_integer(), non_neg_integer()) -> cc_state().

Process acknowledged packets. RFC 9002: Exit recovery when the largest acked packet was sent after recovery started. Extended: Also requires minimum recovery duration to pass before exiting.

on_packets_lost(Cc_state, LostBytes)

-spec on_packets_lost(cc_state(), non_neg_integer()) -> cc_state().

Process lost packets. LostBytes is the total size of lost packets.

on_persistent_congestion(Cc_state)

-spec on_persistent_congestion(cc_state()) -> cc_state().

Reset to minimum window on persistent congestion (RFC 9002 SS7.6.2). This is a severe response to prolonged packet loss.

pacing_allows(Cc_state, Size)

-spec pacing_allows(cc_state(), non_neg_integer()) -> boolean().

Check if pacing allows sending Size bytes. Returns true if enough tokens are available (including burst allowance).

pacing_delay(Cc_state, Size)

-spec pacing_delay(cc_state(), non_neg_integer()) -> non_neg_integer().

Calculate delay (in ms) until Size bytes can be sent. Returns 0 if sending is allowed immediately.

send_check(Cc_state, Size, Urgency)

-spec send_check(cc_state(), non_neg_integer(), non_neg_integer()) ->
                    {ok, cc_state()} |
                    {blocked_cwnd, non_neg_integer()} |
                    {blocked_pacing, non_neg_integer()}.

Fused cwnd + pacing check for the hot send path. One monotonic_time call, one record match. Replaces can_send/can_send_control + pacing_allows + pacing_delay + get_pacing_tokens on every packet.

ssthresh(Cc_state)

-spec ssthresh(cc_state()) -> non_neg_integer() | infinity.

Get the slow start threshold.

update_mtu(Cc_state, NewMTU)

-spec update_mtu(cc_state(), pos_integer()) -> cc_state().

Update congestion control state when MTU changes.

Called by PMTU discovery when a new MTU is discovered. Updates max_datagram_size, minimum_window, and pacing_max_burst.

RFC 9002: When max_datagram_size changes, dependent parameters should be updated proportionally.

update_pacing_rate(Cc_state, SmoothedRTT)

-spec update_pacing_rate(cc_state(), non_neg_integer()) -> cc_state().

Update pacing rate based on smoothed RTT and cwnd. RFC 9002: pacing_rate = cwnd / smoothed_rtt Called when RTT estimate is updated.