/* Copyright (c) 2017 - 2022 LiteSpeed Technologies Inc.  See LICENSE. */
#ifndef LSQUIC_BBR_H
#define LSQUIC_BBR_H

/* Our BBR implementation is copied from Chromium with some modifications.
 * Besides the obvious translation from C++ to C, differences are:
 *
 *  1. Instead of OnCongestionEvent(), the ACK information is processed at the
 *     same time as the ACK itself using cci_begin_ack(), cci_ack(), and
 *     cci_end_ack() methods.  This is done to fit with the flow in
 *     lsquic_send_ctl_got_ack().
 *
 *  2. The bandwidth sampler does not use a hash.  Instead, the sample
 *     information is attached directly to the packet via po_bwp_state.
 *
 * In this file and in lsquic_bbr.c, C++-style comments are those copied
 * verbatim from Chromium.  C-style comments are ours.
 *
 * Code is based on bbr_sender.cc 1a578a76c16abc942205a1a80584a288c262d03a in
 * the "quiche" repository.  (Not to be confused with Cloudflare's "quiche".)
 *
 * The BBR I-D is here:
 *  https://tools.ietf.org/html/draft-cardwell-iccrg-bbr-congestion-control-00
 *
 * As for quiche, see
 *  http://www.bernstein-plus-sons.com/RPDEQ.html
 *
 * Chromium copyright notice follows.
 */
// 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.chrome file.

struct lsquic_bbr
{
    const struct lsquic_conn_public  *bbr_conn_pub;

    enum bbr_mode
    {
        BBR_MODE_STARTUP,
        BBR_MODE_DRAIN,
        BBR_MODE_PROBE_BW,
        BBR_MODE_PROBE_RTT,
    }                           bbr_mode;

    enum
    {
        BBR_RS_NOT_IN_RECOVERY,
        BBR_RS_CONSERVATION,
        BBR_RS_GROWTH,
    }                           bbr_recovery_state;

    enum
    {
        BBR_FLAG_IN_ACK                  = 1 << 0,   /* cci_begin_ack() has been called */
        BBR_FLAG_LAST_SAMPLE_APP_LIMITED = 1 << 1,
        BBR_FLAG_HAS_NON_APP_LIMITED     = 1 << 2,
        BBR_FLAG_APP_LIMITED_SINCE_LAST_PROBE_RTT
                                         = 1 << 3,
        BBR_FLAG_PROBE_RTT_DISABLED_IF_APP_LIMITED
                                         = 1 << 4,
        BBR_FLAG_PROBE_RTT_SKIPPED_IF_SIMILAR_RTT
                                         = 1 << 5,
        BBR_FLAG_EXIT_STARTUP_ON_LOSS    = 1 << 6,
        BBR_FLAG_IS_AT_FULL_BANDWIDTH    = 1 << 7,
        BBR_FLAG_EXITING_QUIESCENCE      = 1 << 8,
        BBR_FLAG_PROBE_RTT_ROUND_PASSED  = 1 << 9,
        BBR_FLAG_FLEXIBLE_APP_LIMITED    = 1 << 10,
        // If true, will not exit low gain mode until bytes_in_flight drops
        // below BDP or it's time for high gain mode.
        BBR_FLAG_DRAIN_TO_TARGET         = 1 << 11,
        // When true, expire the windowed ack aggregation values in STARTUP
        // when bandwidth increases more than 25%.
        BBR_FLAG_EXPIRE_ACK_AGG_IN_STARTUP
                                         = 1 << 12,
        // If true, use a CWND of 0.75*BDP during probe_rtt instead of 4
        // packets.
        BBR_FLAG_PROBE_RTT_BASED_ON_BDP  = 1 << 13,
        // When true, pace at 1.5x and disable packet conservation in STARTUP.
        BBR_FLAG_SLOWER_STARTUP          = 1 << 14,
        // When true, add the most recent ack aggregation measurement during STARTUP.
        BBR_FLAG_ENABLE_ACK_AGG_IN_STARTUP
                                         = 1 << 15,
        // When true, disables packet conservation in STARTUP.
        BBR_FLAG_RATE_BASED_STARTUP      = 1 << 16,
    }                           bbr_flags;

    // Number of round-trips in PROBE_BW mode, used for determining the current
    // pacing gain cycle.
    unsigned                    bbr_cycle_current_offset;

    const struct lsquic_rtt_stats
                               *bbr_rtt_stats;

    struct bw_sampler           bbr_bw_sampler;

    /*
     " BBR.BtlBwFilter: The max filter used to estimate BBR.BtlBw.
     */
    struct minmax               bbr_max_bandwidth;

    // Tracks the maximum number of bytes acked faster than the sending rate.
    struct minmax               bbr_max_ack_height;

    // The initial value of the bbr_cwnd.
    uint64_t                    bbr_init_cwnd;
    // The smallest value the bbr_cwnd can achieve.
    uint64_t                    bbr_min_cwnd;
    // The largest value the bbr_cwnd can achieve.
    uint64_t                    bbr_max_cwnd;
    // The maximum allowed number of bytes in flight.
    uint64_t                    bbr_cwnd;

    // The time this aggregation started and the number of bytes acked during it.
    lsquic_time_t               bbr_aggregation_epoch_start_time;
    uint64_t                    bbr_aggregation_epoch_bytes;

    lsquic_packno_t             bbr_last_sent_packno;
    lsquic_packno_t             bbr_current_round_trip_end;

    // Receiving acknowledgement of a packet after |bbr_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.
    lsquic_packno_t             bbr_end_recovery_at;

    /*
     " BBR.round_count: Count of packet-timed round trips.
     */
    uint64_t                    bbr_round_count;

    /* Not documented in the draft: */
    uint64_t                    bbr_full_bw;

    /* Not documented in the draft: */
    uint64_t                    bbr_full_bw_count;

    /*
     " BBR.pacing_rate: The current pacing rate for a BBR flow, which
     " controls inter-packet spacing.
     */
    struct bandwidth            bbr_pacing_rate;

    // Sum of bytes lost in STARTUP.
    uint64_t                    bbr_startup_bytes_lost;

    /*
     " BBR.pacing_gain: The dynamic gain factor used to scale BBR.BtlBw to
     " produce BBR.pacing_rate.
     */
    float                       bbr_pacing_gain;

    // The pacing gain applied during the STARTUP phase.
    float                       bbr_high_gain;

    // The CWND gain applied during the STARTUP phase.
    float                       bbr_high_cwnd_gain;

    // The pacing gain applied during the DRAIN phase.
    float                       bbr_drain_gain;

    // The number of RTTs to stay in STARTUP mode.  Defaults to 3.
    unsigned                    bbr_num_startup_rtts;

    // Number of rounds during which there was no significant bandwidth
    // increase.
    unsigned                    bbr_round_wo_bw_gain;

    /*
     " BBR.cwnd_gain: The dynamic gain factor used to scale the estimated
     " BDP to produce a congestion window (cwnd).
     */
    float                       bbr_cwnd_gain;

    // The bandwidth compared to which the increase is measured.
    struct bandwidth            bbr_bw_at_last_round;

    // The time at which the last pacing gain cycle was started.
    lsquic_time_t               bbr_last_cycle_start;

    // 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.
    lsquic_time_t               bbr_exit_probe_rtt_at;

    lsquic_time_t               bbr_min_rtt_since_last_probe;
    lsquic_time_t               bbr_min_rtt;
    lsquic_time_t               bbr_min_rtt_timestamp;

    // A window used to limit the number of bytes in flight during loss recovery
    uint64_t                    bbr_recovery_window;

    /* Accumulate information from a single ACK.  Gets processed when
     * cci_end_ack() is called.
     */
    struct
    {
        TAILQ_HEAD(, bw_sample) samples;
        lsquic_time_t       ack_time;
        lsquic_packno_t     max_packno;
        uint64_t            acked_bytes;
        uint64_t            lost_bytes;
        uint64_t            total_bytes_acked_before;
        uint64_t            in_flight;
        int                 has_losses;
    }                           bbr_ack_state;
};

extern const struct cong_ctl_if lsquic_cong_bbr_if;

#endif