Home | History | Annotate | Line # | Download | only in quic
      1      1.1  christos #include "internal/quic_cc.h"
      2      1.1  christos #include "internal/quic_types.h"
      3      1.1  christos #include "internal/safe_math.h"
      4      1.1  christos 
      5      1.1  christos OSSL_SAFE_MATH_UNSIGNED(u64, uint64_t)
      6      1.1  christos 
      7      1.1  christos typedef struct ossl_cc_newreno_st {
      8      1.1  christos     /* Dependencies. */
      9  1.1.1.2  christos     OSSL_TIME (*now_cb)(void *arg);
     10  1.1.1.2  christos     void *now_cb_arg;
     11      1.1  christos 
     12      1.1  christos     /* 'Constants' (which we allow to be configurable). */
     13  1.1.1.2  christos     uint64_t k_init_wnd, k_min_wnd;
     14  1.1.1.2  christos     uint32_t k_loss_reduction_factor_num, k_loss_reduction_factor_den;
     15  1.1.1.2  christos     uint32_t persistent_cong_thresh;
     16      1.1  christos 
     17      1.1  christos     /* State. */
     18  1.1.1.2  christos     size_t max_dgram_size;
     19  1.1.1.2  christos     uint64_t bytes_in_flight, cong_wnd, slow_start_thresh, bytes_acked;
     20  1.1.1.2  christos     OSSL_TIME cong_recovery_start_time;
     21      1.1  christos 
     22      1.1  christos     /* Unflushed state during multiple on-loss calls. */
     23  1.1.1.2  christos     int processing_loss; /* 1 if not flushed */
     24  1.1.1.2  christos     OSSL_TIME tx_time_of_last_loss;
     25      1.1  christos 
     26      1.1  christos     /* Diagnostic state. */
     27  1.1.1.2  christos     int in_congestion_recovery;
     28      1.1  christos 
     29      1.1  christos     /* Diagnostic output locations. */
     30  1.1.1.2  christos     size_t *p_diag_max_dgram_payload_len;
     31  1.1.1.2  christos     uint64_t *p_diag_cur_cwnd_size;
     32  1.1.1.2  christos     uint64_t *p_diag_min_cwnd_size;
     33  1.1.1.2  christos     uint64_t *p_diag_cur_bytes_in_flight;
     34  1.1.1.2  christos     uint32_t *p_diag_cur_state;
     35      1.1  christos } OSSL_CC_NEWRENO;
     36      1.1  christos 
     37  1.1.1.2  christos #define MIN_MAX_INIT_WND_SIZE 14720 /* RFC 9002 s. 7.2 */
     38      1.1  christos 
     39      1.1  christos /* TODO(QUIC FUTURE): Pacing support. */
     40      1.1  christos 
     41      1.1  christos static void newreno_set_max_dgram_size(OSSL_CC_NEWRENO *nr,
     42  1.1.1.2  christos     size_t max_dgram_size);
     43      1.1  christos static void newreno_update_diag(OSSL_CC_NEWRENO *nr);
     44      1.1  christos 
     45      1.1  christos static void newreno_reset(OSSL_CC_DATA *cc);
     46      1.1  christos 
     47      1.1  christos static OSSL_CC_DATA *newreno_new(OSSL_TIME (*now_cb)(void *arg),
     48  1.1.1.2  christos     void *now_cb_arg)
     49      1.1  christos {
     50      1.1  christos     OSSL_CC_NEWRENO *nr;
     51      1.1  christos 
     52      1.1  christos     if ((nr = OPENSSL_zalloc(sizeof(*nr))) == NULL)
     53      1.1  christos         return NULL;
     54      1.1  christos 
     55  1.1.1.2  christos     nr->now_cb = now_cb;
     56  1.1.1.2  christos     nr->now_cb_arg = now_cb_arg;
     57      1.1  christos 
     58      1.1  christos     newreno_set_max_dgram_size(nr, QUIC_MIN_INITIAL_DGRAM_LEN);
     59      1.1  christos     newreno_reset((OSSL_CC_DATA *)nr);
     60      1.1  christos 
     61      1.1  christos     return (OSSL_CC_DATA *)nr;
     62      1.1  christos }
     63      1.1  christos 
     64      1.1  christos static void newreno_free(OSSL_CC_DATA *cc)
     65      1.1  christos {
     66      1.1  christos     OPENSSL_free(cc);
     67      1.1  christos }
     68      1.1  christos 
     69      1.1  christos static void newreno_set_max_dgram_size(OSSL_CC_NEWRENO *nr,
     70  1.1.1.2  christos     size_t max_dgram_size)
     71      1.1  christos {
     72      1.1  christos     size_t max_init_wnd;
     73      1.1  christos     int is_reduced = (max_dgram_size < nr->max_dgram_size);
     74      1.1  christos 
     75      1.1  christos     nr->max_dgram_size = max_dgram_size;
     76      1.1  christos 
     77      1.1  christos     max_init_wnd = 2 * max_dgram_size;
     78      1.1  christos     if (max_init_wnd < MIN_MAX_INIT_WND_SIZE)
     79      1.1  christos         max_init_wnd = MIN_MAX_INIT_WND_SIZE;
     80      1.1  christos 
     81      1.1  christos     nr->k_init_wnd = 10 * max_dgram_size;
     82      1.1  christos     if (nr->k_init_wnd > max_init_wnd)
     83      1.1  christos         nr->k_init_wnd = max_init_wnd;
     84      1.1  christos 
     85      1.1  christos     nr->k_min_wnd = 2 * max_dgram_size;
     86      1.1  christos 
     87      1.1  christos     if (is_reduced)
     88      1.1  christos         nr->cong_wnd = nr->k_init_wnd;
     89      1.1  christos 
     90      1.1  christos     newreno_update_diag(nr);
     91      1.1  christos }
     92      1.1  christos 
     93      1.1  christos static void newreno_reset(OSSL_CC_DATA *cc)
     94      1.1  christos {
     95      1.1  christos     OSSL_CC_NEWRENO *nr = (OSSL_CC_NEWRENO *)cc;
     96      1.1  christos 
     97  1.1.1.2  christos     nr->k_loss_reduction_factor_num = 1;
     98  1.1.1.2  christos     nr->k_loss_reduction_factor_den = 2;
     99  1.1.1.2  christos     nr->persistent_cong_thresh = 3;
    100  1.1.1.2  christos 
    101  1.1.1.2  christos     nr->cong_wnd = nr->k_init_wnd;
    102  1.1.1.2  christos     nr->bytes_in_flight = 0;
    103  1.1.1.2  christos     nr->bytes_acked = 0;
    104  1.1.1.2  christos     nr->slow_start_thresh = UINT64_MAX;
    105  1.1.1.2  christos     nr->cong_recovery_start_time = ossl_time_zero();
    106  1.1.1.2  christos 
    107  1.1.1.2  christos     nr->processing_loss = 0;
    108  1.1.1.2  christos     nr->tx_time_of_last_loss = ossl_time_zero();
    109  1.1.1.2  christos     nr->in_congestion_recovery = 0;
    110      1.1  christos }
    111      1.1  christos 
    112      1.1  christos static int newreno_set_input_params(OSSL_CC_DATA *cc, const OSSL_PARAM *params)
    113      1.1  christos {
    114      1.1  christos     OSSL_CC_NEWRENO *nr = (OSSL_CC_NEWRENO *)cc;
    115      1.1  christos     const OSSL_PARAM *p;
    116      1.1  christos     size_t value;
    117      1.1  christos 
    118      1.1  christos     p = OSSL_PARAM_locate_const(params, OSSL_CC_OPTION_MAX_DGRAM_PAYLOAD_LEN);
    119      1.1  christos     if (p != NULL) {
    120      1.1  christos         if (!OSSL_PARAM_get_size_t(p, &value))
    121      1.1  christos             return 0;
    122      1.1  christos         if (value < QUIC_MIN_INITIAL_DGRAM_LEN)
    123      1.1  christos             return 0;
    124      1.1  christos 
    125      1.1  christos         newreno_set_max_dgram_size(nr, value);
    126      1.1  christos     }
    127      1.1  christos 
    128      1.1  christos     return 1;
    129      1.1  christos }
    130      1.1  christos 
    131      1.1  christos static int bind_diag(OSSL_PARAM *params, const char *param_name, size_t len,
    132  1.1.1.2  christos     void **pp)
    133      1.1  christos {
    134      1.1  christos     const OSSL_PARAM *p = OSSL_PARAM_locate_const(params, param_name);
    135      1.1  christos 
    136      1.1  christos     *pp = NULL;
    137      1.1  christos 
    138      1.1  christos     if (p == NULL)
    139      1.1  christos         return 1;
    140      1.1  christos 
    141      1.1  christos     if (p->data_type != OSSL_PARAM_UNSIGNED_INTEGER
    142      1.1  christos         || p->data_size != len)
    143      1.1  christos         return 0;
    144      1.1  christos 
    145      1.1  christos     *pp = p->data;
    146      1.1  christos     return 1;
    147      1.1  christos }
    148      1.1  christos 
    149      1.1  christos static int newreno_bind_diagnostic(OSSL_CC_DATA *cc, OSSL_PARAM *params)
    150      1.1  christos {
    151      1.1  christos     OSSL_CC_NEWRENO *nr = (OSSL_CC_NEWRENO *)cc;
    152      1.1  christos     size_t *new_p_max_dgram_payload_len;
    153      1.1  christos     uint64_t *new_p_cur_cwnd_size;
    154      1.1  christos     uint64_t *new_p_min_cwnd_size;
    155      1.1  christos     uint64_t *new_p_cur_bytes_in_flight;
    156      1.1  christos     uint32_t *new_p_cur_state;
    157      1.1  christos 
    158      1.1  christos     if (!bind_diag(params, OSSL_CC_OPTION_MAX_DGRAM_PAYLOAD_LEN,
    159  1.1.1.2  christos             sizeof(size_t), (void **)&new_p_max_dgram_payload_len)
    160      1.1  christos         || !bind_diag(params, OSSL_CC_OPTION_CUR_CWND_SIZE,
    161  1.1.1.2  christos             sizeof(uint64_t), (void **)&new_p_cur_cwnd_size)
    162      1.1  christos         || !bind_diag(params, OSSL_CC_OPTION_MIN_CWND_SIZE,
    163  1.1.1.2  christos             sizeof(uint64_t), (void **)&new_p_min_cwnd_size)
    164      1.1  christos         || !bind_diag(params, OSSL_CC_OPTION_CUR_BYTES_IN_FLIGHT,
    165  1.1.1.2  christos             sizeof(uint64_t), (void **)&new_p_cur_bytes_in_flight)
    166      1.1  christos         || !bind_diag(params, OSSL_CC_OPTION_CUR_STATE,
    167  1.1.1.2  christos             sizeof(uint32_t), (void **)&new_p_cur_state))
    168      1.1  christos         return 0;
    169      1.1  christos 
    170      1.1  christos     if (new_p_max_dgram_payload_len != NULL)
    171      1.1  christos         nr->p_diag_max_dgram_payload_len = new_p_max_dgram_payload_len;
    172      1.1  christos 
    173      1.1  christos     if (new_p_cur_cwnd_size != NULL)
    174      1.1  christos         nr->p_diag_cur_cwnd_size = new_p_cur_cwnd_size;
    175      1.1  christos 
    176      1.1  christos     if (new_p_min_cwnd_size != NULL)
    177      1.1  christos         nr->p_diag_min_cwnd_size = new_p_min_cwnd_size;
    178      1.1  christos 
    179      1.1  christos     if (new_p_cur_bytes_in_flight != NULL)
    180      1.1  christos         nr->p_diag_cur_bytes_in_flight = new_p_cur_bytes_in_flight;
    181      1.1  christos 
    182      1.1  christos     if (new_p_cur_state != NULL)
    183      1.1  christos         nr->p_diag_cur_state = new_p_cur_state;
    184      1.1  christos 
    185      1.1  christos     newreno_update_diag(nr);
    186      1.1  christos     return 1;
    187      1.1  christos }
    188      1.1  christos 
    189      1.1  christos static void unbind_diag(OSSL_PARAM *params, const char *param_name,
    190  1.1.1.2  christos     void **pp)
    191      1.1  christos {
    192      1.1  christos     const OSSL_PARAM *p = OSSL_PARAM_locate_const(params, param_name);
    193      1.1  christos 
    194      1.1  christos     if (p != NULL)
    195      1.1  christos         *pp = NULL;
    196      1.1  christos }
    197      1.1  christos 
    198      1.1  christos static int newreno_unbind_diagnostic(OSSL_CC_DATA *cc, OSSL_PARAM *params)
    199      1.1  christos {
    200      1.1  christos     OSSL_CC_NEWRENO *nr = (OSSL_CC_NEWRENO *)cc;
    201      1.1  christos 
    202      1.1  christos     unbind_diag(params, OSSL_CC_OPTION_MAX_DGRAM_PAYLOAD_LEN,
    203  1.1.1.2  christos         (void **)&nr->p_diag_max_dgram_payload_len);
    204      1.1  christos     unbind_diag(params, OSSL_CC_OPTION_CUR_CWND_SIZE,
    205  1.1.1.2  christos         (void **)&nr->p_diag_cur_cwnd_size);
    206      1.1  christos     unbind_diag(params, OSSL_CC_OPTION_MIN_CWND_SIZE,
    207  1.1.1.2  christos         (void **)&nr->p_diag_min_cwnd_size);
    208      1.1  christos     unbind_diag(params, OSSL_CC_OPTION_CUR_BYTES_IN_FLIGHT,
    209  1.1.1.2  christos         (void **)&nr->p_diag_cur_bytes_in_flight);
    210      1.1  christos     unbind_diag(params, OSSL_CC_OPTION_CUR_STATE,
    211  1.1.1.2  christos         (void **)&nr->p_diag_cur_state);
    212      1.1  christos     return 1;
    213      1.1  christos }
    214      1.1  christos 
    215      1.1  christos static void newreno_update_diag(OSSL_CC_NEWRENO *nr)
    216      1.1  christos {
    217      1.1  christos     if (nr->p_diag_max_dgram_payload_len != NULL)
    218      1.1  christos         *nr->p_diag_max_dgram_payload_len = nr->max_dgram_size;
    219      1.1  christos 
    220      1.1  christos     if (nr->p_diag_cur_cwnd_size != NULL)
    221      1.1  christos         *nr->p_diag_cur_cwnd_size = nr->cong_wnd;
    222      1.1  christos 
    223      1.1  christos     if (nr->p_diag_min_cwnd_size != NULL)
    224      1.1  christos         *nr->p_diag_min_cwnd_size = nr->k_min_wnd;
    225      1.1  christos 
    226      1.1  christos     if (nr->p_diag_cur_bytes_in_flight != NULL)
    227      1.1  christos         *nr->p_diag_cur_bytes_in_flight = nr->bytes_in_flight;
    228      1.1  christos 
    229      1.1  christos     if (nr->p_diag_cur_state != NULL) {
    230      1.1  christos         if (nr->in_congestion_recovery)
    231      1.1  christos             *nr->p_diag_cur_state = 'R';
    232      1.1  christos         else if (nr->cong_wnd < nr->slow_start_thresh)
    233      1.1  christos             *nr->p_diag_cur_state = 'S';
    234      1.1  christos         else
    235      1.1  christos             *nr->p_diag_cur_state = 'A';
    236      1.1  christos     }
    237      1.1  christos }
    238      1.1  christos 
    239      1.1  christos static int newreno_in_cong_recovery(OSSL_CC_NEWRENO *nr, OSSL_TIME tx_time)
    240      1.1  christos {
    241      1.1  christos     return ossl_time_compare(tx_time, nr->cong_recovery_start_time) <= 0;
    242      1.1  christos }
    243      1.1  christos 
    244      1.1  christos static void newreno_cong(OSSL_CC_NEWRENO *nr, OSSL_TIME tx_time)
    245      1.1  christos {
    246      1.1  christos     int err = 0;
    247      1.1  christos 
    248      1.1  christos     /* No reaction if already in a recovery period. */
    249      1.1  christos     if (newreno_in_cong_recovery(nr, tx_time))
    250      1.1  christos         return;
    251      1.1  christos 
    252      1.1  christos     /* Start a new recovery period. */
    253      1.1  christos     nr->in_congestion_recovery = 1;
    254      1.1  christos     nr->cong_recovery_start_time = nr->now_cb(nr->now_cb_arg);
    255      1.1  christos 
    256      1.1  christos     /* slow_start_thresh = cong_wnd * loss_reduction_factor */
    257      1.1  christos     nr->slow_start_thresh
    258      1.1  christos         = safe_muldiv_u64(nr->cong_wnd,
    259  1.1.1.2  christos             nr->k_loss_reduction_factor_num,
    260  1.1.1.2  christos             nr->k_loss_reduction_factor_den,
    261  1.1.1.2  christos             &err);
    262      1.1  christos 
    263      1.1  christos     if (err)
    264      1.1  christos         nr->slow_start_thresh = UINT64_MAX;
    265      1.1  christos 
    266      1.1  christos     nr->cong_wnd = nr->slow_start_thresh;
    267      1.1  christos     if (nr->cong_wnd < nr->k_min_wnd)
    268      1.1  christos         nr->cong_wnd = nr->k_min_wnd;
    269      1.1  christos }
    270      1.1  christos 
    271      1.1  christos static void newreno_flush(OSSL_CC_NEWRENO *nr, uint32_t flags)
    272      1.1  christos {
    273      1.1  christos     if (!nr->processing_loss)
    274      1.1  christos         return;
    275      1.1  christos 
    276      1.1  christos     newreno_cong(nr, nr->tx_time_of_last_loss);
    277      1.1  christos 
    278      1.1  christos     if ((flags & OSSL_CC_LOST_FLAG_PERSISTENT_CONGESTION) != 0) {
    279  1.1.1.2  christos         nr->cong_wnd = nr->k_min_wnd;
    280  1.1.1.2  christos         nr->cong_recovery_start_time = ossl_time_zero();
    281      1.1  christos     }
    282      1.1  christos 
    283      1.1  christos     nr->processing_loss = 0;
    284      1.1  christos     newreno_update_diag(nr);
    285      1.1  christos }
    286      1.1  christos 
    287      1.1  christos static uint64_t newreno_get_tx_allowance(OSSL_CC_DATA *cc)
    288      1.1  christos {
    289      1.1  christos     OSSL_CC_NEWRENO *nr = (OSSL_CC_NEWRENO *)cc;
    290      1.1  christos 
    291      1.1  christos     if (nr->bytes_in_flight >= nr->cong_wnd)
    292      1.1  christos         return 0;
    293      1.1  christos 
    294      1.1  christos     return nr->cong_wnd - nr->bytes_in_flight;
    295      1.1  christos }
    296      1.1  christos 
    297      1.1  christos static OSSL_TIME newreno_get_wakeup_deadline(OSSL_CC_DATA *cc)
    298      1.1  christos {
    299      1.1  christos     if (newreno_get_tx_allowance(cc) > 0) {
    300      1.1  christos         /* We have TX allowance now so wakeup immediately */
    301      1.1  christos         return ossl_time_zero();
    302      1.1  christos     } else {
    303      1.1  christos         /*
    304      1.1  christos          * The NewReno congestion controller does not vary its state in time,
    305      1.1  christos          * only in response to stimulus.
    306      1.1  christos          */
    307      1.1  christos         return ossl_time_infinite();
    308      1.1  christos     }
    309      1.1  christos }
    310      1.1  christos 
    311      1.1  christos static int newreno_on_data_sent(OSSL_CC_DATA *cc, uint64_t num_bytes)
    312      1.1  christos {
    313      1.1  christos     OSSL_CC_NEWRENO *nr = (OSSL_CC_NEWRENO *)cc;
    314      1.1  christos 
    315      1.1  christos     nr->bytes_in_flight += num_bytes;
    316      1.1  christos     newreno_update_diag(nr);
    317      1.1  christos     return 1;
    318      1.1  christos }
    319      1.1  christos 
    320      1.1  christos static int newreno_is_cong_limited(OSSL_CC_NEWRENO *nr)
    321      1.1  christos {
    322      1.1  christos     uint64_t wnd_rem;
    323      1.1  christos 
    324      1.1  christos     /* We are congestion-limited if we are already at the congestion window. */
    325      1.1  christos     if (nr->bytes_in_flight >= nr->cong_wnd)
    326      1.1  christos         return 1;
    327      1.1  christos 
    328      1.1  christos     wnd_rem = nr->cong_wnd - nr->bytes_in_flight;
    329      1.1  christos 
    330      1.1  christos     /*
    331      1.1  christos      * Consider ourselves congestion-limited if less than three datagrams' worth
    332      1.1  christos      * of congestion window remains to be spent, or if we are in slow start and
    333      1.1  christos      * have consumed half of our window.
    334      1.1  christos      */
    335      1.1  christos     return (nr->cong_wnd < nr->slow_start_thresh && wnd_rem <= nr->cong_wnd / 2)
    336  1.1.1.2  christos         || wnd_rem <= 3 * nr->max_dgram_size;
    337      1.1  christos }
    338      1.1  christos 
    339      1.1  christos static int newreno_on_data_acked(OSSL_CC_DATA *cc,
    340  1.1.1.2  christos     const OSSL_CC_ACK_INFO *info)
    341      1.1  christos {
    342      1.1  christos     OSSL_CC_NEWRENO *nr = (OSSL_CC_NEWRENO *)cc;
    343      1.1  christos 
    344      1.1  christos     /*
    345      1.1  christos      * Packet has been acked. Firstly, remove it from the aggregate count of
    346      1.1  christos      * bytes in flight.
    347      1.1  christos      */
    348      1.1  christos     nr->bytes_in_flight -= info->tx_size;
    349      1.1  christos 
    350      1.1  christos     /*
    351      1.1  christos      * We use acknowledgement of data as a signal that we are not at channel
    352      1.1  christos      * capacity and that it may be reasonable to increase the congestion window.
    353      1.1  christos      * However, acknowledgement is not a useful signal that there is further
    354      1.1  christos      * capacity if we are not actually saturating the congestion window that we
    355      1.1  christos      * already have (for example, if the application is not generating much data
    356      1.1  christos      * or we are limited by flow control). Therefore, we only expand the
    357      1.1  christos      * congestion window if we are consuming a significant fraction of the
    358      1.1  christos      * congestion window.
    359      1.1  christos      */
    360      1.1  christos     if (!newreno_is_cong_limited(nr))
    361      1.1  christos         goto out;
    362      1.1  christos 
    363      1.1  christos     /*
    364      1.1  christos      * We can handle acknowledgement of a packet in one of three ways
    365      1.1  christos      * depending on our current state:
    366      1.1  christos      *
    367      1.1  christos      *   - Congestion Recovery: Do nothing. We don't start increasing
    368      1.1  christos      *     the congestion window in response to acknowledgements until
    369      1.1  christos      *     we are no longer in the Congestion Recovery state.
    370      1.1  christos      *
    371      1.1  christos      *   - Slow Start: Increase the congestion window using the slow
    372      1.1  christos      *     start scale.
    373      1.1  christos      *
    374      1.1  christos      *   - Congestion Avoidance: Increase the congestion window using
    375      1.1  christos      *     the congestion avoidance scale.
    376      1.1  christos      */
    377      1.1  christos     if (newreno_in_cong_recovery(nr, info->tx_time)) {
    378      1.1  christos         /* Congestion recovery, do nothing. */
    379      1.1  christos     } else if (nr->cong_wnd < nr->slow_start_thresh) {
    380      1.1  christos         /* When this condition is true we are in the Slow Start state. */
    381      1.1  christos         nr->cong_wnd += info->tx_size;
    382      1.1  christos         nr->in_congestion_recovery = 0;
    383      1.1  christos     } else {
    384      1.1  christos         /* Otherwise, we are in the Congestion Avoidance state. */
    385      1.1  christos         nr->bytes_acked += info->tx_size;
    386      1.1  christos 
    387      1.1  christos         /*
    388      1.1  christos          * Avoid integer division as per RFC 9002 s. B.5. / RFC3465 s. 2.1.
    389      1.1  christos          */
    390      1.1  christos         if (nr->bytes_acked >= nr->cong_wnd) {
    391      1.1  christos             nr->bytes_acked -= nr->cong_wnd;
    392  1.1.1.2  christos             nr->cong_wnd += nr->max_dgram_size;
    393      1.1  christos         }
    394      1.1  christos 
    395      1.1  christos         nr->in_congestion_recovery = 0;
    396      1.1  christos     }
    397      1.1  christos 
    398      1.1  christos out:
    399      1.1  christos     newreno_update_diag(nr);
    400      1.1  christos     return 1;
    401      1.1  christos }
    402      1.1  christos 
    403      1.1  christos static int newreno_on_data_lost(OSSL_CC_DATA *cc,
    404  1.1.1.2  christos     const OSSL_CC_LOSS_INFO *info)
    405      1.1  christos {
    406      1.1  christos     OSSL_CC_NEWRENO *nr = (OSSL_CC_NEWRENO *)cc;
    407      1.1  christos 
    408      1.1  christos     if (info->tx_size > nr->bytes_in_flight)
    409      1.1  christos         return 0;
    410      1.1  christos 
    411      1.1  christos     nr->bytes_in_flight -= info->tx_size;
    412      1.1  christos 
    413      1.1  christos     if (!nr->processing_loss) {
    414      1.1  christos 
    415      1.1  christos         if (ossl_time_compare(info->tx_time, nr->tx_time_of_last_loss) <= 0)
    416      1.1  christos             /*
    417      1.1  christos              * After triggering congestion due to a lost packet at time t, don't
    418      1.1  christos              * trigger congestion again due to any subsequently detected lost
    419      1.1  christos              * packet at a time s < t, as we've effectively already signalled
    420      1.1  christos              * congestion on loss of that and subsequent packets.
    421      1.1  christos              */
    422      1.1  christos             goto out;
    423      1.1  christos 
    424      1.1  christos         nr->processing_loss = 1;
    425      1.1  christos 
    426      1.1  christos         /*
    427      1.1  christos          * Cancel any pending window increase in the Congestion Avoidance state.
    428      1.1  christos          */
    429      1.1  christos         nr->bytes_acked = 0;
    430      1.1  christos     }
    431      1.1  christos 
    432      1.1  christos     nr->tx_time_of_last_loss
    433      1.1  christos         = ossl_time_max(nr->tx_time_of_last_loss, info->tx_time);
    434      1.1  christos 
    435      1.1  christos out:
    436      1.1  christos     newreno_update_diag(nr);
    437      1.1  christos     return 1;
    438      1.1  christos }
    439      1.1  christos 
    440      1.1  christos static int newreno_on_data_lost_finished(OSSL_CC_DATA *cc, uint32_t flags)
    441      1.1  christos {
    442      1.1  christos     OSSL_CC_NEWRENO *nr = (OSSL_CC_NEWRENO *)cc;
    443      1.1  christos 
    444      1.1  christos     newreno_flush(nr, flags);
    445      1.1  christos     return 1;
    446      1.1  christos }
    447      1.1  christos 
    448      1.1  christos static int newreno_on_data_invalidated(OSSL_CC_DATA *cc,
    449  1.1.1.2  christos     uint64_t num_bytes)
    450      1.1  christos {
    451      1.1  christos     OSSL_CC_NEWRENO *nr = (OSSL_CC_NEWRENO *)cc;
    452      1.1  christos 
    453      1.1  christos     nr->bytes_in_flight -= num_bytes;
    454      1.1  christos     newreno_update_diag(nr);
    455      1.1  christos     return 1;
    456      1.1  christos }
    457      1.1  christos 
    458      1.1  christos static int newreno_on_ecn(OSSL_CC_DATA *cc,
    459  1.1.1.2  christos     const OSSL_CC_ECN_INFO *info)
    460      1.1  christos {
    461      1.1  christos     OSSL_CC_NEWRENO *nr = (OSSL_CC_NEWRENO *)cc;
    462      1.1  christos 
    463  1.1.1.2  christos     nr->processing_loss = 1;
    464  1.1.1.2  christos     nr->bytes_acked = 0;
    465  1.1.1.2  christos     nr->tx_time_of_last_loss = info->largest_acked_time;
    466      1.1  christos     newreno_flush(nr, 0);
    467      1.1  christos     return 1;
    468      1.1  christos }
    469      1.1  christos 
    470      1.1  christos const OSSL_CC_METHOD ossl_cc_newreno_method = {
    471      1.1  christos     newreno_new,
    472      1.1  christos     newreno_free,
    473      1.1  christos     newreno_reset,
    474      1.1  christos     newreno_set_input_params,
    475      1.1  christos     newreno_bind_diagnostic,
    476      1.1  christos     newreno_unbind_diagnostic,
    477      1.1  christos     newreno_get_tx_allowance,
    478      1.1  christos     newreno_get_wakeup_deadline,
    479      1.1  christos     newreno_on_data_sent,
    480      1.1  christos     newreno_on_data_acked,
    481      1.1  christos     newreno_on_data_lost,
    482      1.1  christos     newreno_on_data_lost_finished,
    483      1.1  christos     newreno_on_data_invalidated,
    484      1.1  christos     newreno_on_ecn,
    485      1.1  christos };
    486