Path: blob/main/crypto/openssl/ssl/quic/cc_newreno.c
48261 views
#include "internal/quic_cc.h"1#include "internal/quic_types.h"2#include "internal/safe_math.h"34OSSL_SAFE_MATH_UNSIGNED(u64, uint64_t)56typedef struct ossl_cc_newreno_st {7/* Dependencies. */8OSSL_TIME (*now_cb)(void *arg);9void *now_cb_arg;1011/* 'Constants' (which we allow to be configurable). */12uint64_t k_init_wnd, k_min_wnd;13uint32_t k_loss_reduction_factor_num, k_loss_reduction_factor_den;14uint32_t persistent_cong_thresh;1516/* State. */17size_t max_dgram_size;18uint64_t bytes_in_flight, cong_wnd, slow_start_thresh, bytes_acked;19OSSL_TIME cong_recovery_start_time;2021/* Unflushed state during multiple on-loss calls. */22int processing_loss; /* 1 if not flushed */23OSSL_TIME tx_time_of_last_loss;2425/* Diagnostic state. */26int in_congestion_recovery;2728/* Diagnostic output locations. */29size_t *p_diag_max_dgram_payload_len;30uint64_t *p_diag_cur_cwnd_size;31uint64_t *p_diag_min_cwnd_size;32uint64_t *p_diag_cur_bytes_in_flight;33uint32_t *p_diag_cur_state;34} OSSL_CC_NEWRENO;3536#define MIN_MAX_INIT_WND_SIZE 14720 /* RFC 9002 s. 7.2 */3738/* TODO(QUIC FUTURE): Pacing support. */3940static void newreno_set_max_dgram_size(OSSL_CC_NEWRENO *nr,41size_t max_dgram_size);42static void newreno_update_diag(OSSL_CC_NEWRENO *nr);4344static void newreno_reset(OSSL_CC_DATA *cc);4546static OSSL_CC_DATA *newreno_new(OSSL_TIME (*now_cb)(void *arg),47void *now_cb_arg)48{49OSSL_CC_NEWRENO *nr;5051if ((nr = OPENSSL_zalloc(sizeof(*nr))) == NULL)52return NULL;5354nr->now_cb = now_cb;55nr->now_cb_arg = now_cb_arg;5657newreno_set_max_dgram_size(nr, QUIC_MIN_INITIAL_DGRAM_LEN);58newreno_reset((OSSL_CC_DATA *)nr);5960return (OSSL_CC_DATA *)nr;61}6263static void newreno_free(OSSL_CC_DATA *cc)64{65OPENSSL_free(cc);66}6768static void newreno_set_max_dgram_size(OSSL_CC_NEWRENO *nr,69size_t max_dgram_size)70{71size_t max_init_wnd;72int is_reduced = (max_dgram_size < nr->max_dgram_size);7374nr->max_dgram_size = max_dgram_size;7576max_init_wnd = 2 * max_dgram_size;77if (max_init_wnd < MIN_MAX_INIT_WND_SIZE)78max_init_wnd = MIN_MAX_INIT_WND_SIZE;7980nr->k_init_wnd = 10 * max_dgram_size;81if (nr->k_init_wnd > max_init_wnd)82nr->k_init_wnd = max_init_wnd;8384nr->k_min_wnd = 2 * max_dgram_size;8586if (is_reduced)87nr->cong_wnd = nr->k_init_wnd;8889newreno_update_diag(nr);90}9192static void newreno_reset(OSSL_CC_DATA *cc)93{94OSSL_CC_NEWRENO *nr = (OSSL_CC_NEWRENO *)cc;9596nr->k_loss_reduction_factor_num = 1;97nr->k_loss_reduction_factor_den = 2;98nr->persistent_cong_thresh = 3;99100nr->cong_wnd = nr->k_init_wnd;101nr->bytes_in_flight = 0;102nr->bytes_acked = 0;103nr->slow_start_thresh = UINT64_MAX;104nr->cong_recovery_start_time = ossl_time_zero();105106nr->processing_loss = 0;107nr->tx_time_of_last_loss = ossl_time_zero();108nr->in_congestion_recovery = 0;109}110111static int newreno_set_input_params(OSSL_CC_DATA *cc, const OSSL_PARAM *params)112{113OSSL_CC_NEWRENO *nr = (OSSL_CC_NEWRENO *)cc;114const OSSL_PARAM *p;115size_t value;116117p = OSSL_PARAM_locate_const(params, OSSL_CC_OPTION_MAX_DGRAM_PAYLOAD_LEN);118if (p != NULL) {119if (!OSSL_PARAM_get_size_t(p, &value))120return 0;121if (value < QUIC_MIN_INITIAL_DGRAM_LEN)122return 0;123124newreno_set_max_dgram_size(nr, value);125}126127return 1;128}129130static int bind_diag(OSSL_PARAM *params, const char *param_name, size_t len,131void **pp)132{133const OSSL_PARAM *p = OSSL_PARAM_locate_const(params, param_name);134135*pp = NULL;136137if (p == NULL)138return 1;139140if (p->data_type != OSSL_PARAM_UNSIGNED_INTEGER141|| p->data_size != len)142return 0;143144*pp = p->data;145return 1;146}147148static int newreno_bind_diagnostic(OSSL_CC_DATA *cc, OSSL_PARAM *params)149{150OSSL_CC_NEWRENO *nr = (OSSL_CC_NEWRENO *)cc;151size_t *new_p_max_dgram_payload_len;152uint64_t *new_p_cur_cwnd_size;153uint64_t *new_p_min_cwnd_size;154uint64_t *new_p_cur_bytes_in_flight;155uint32_t *new_p_cur_state;156157if (!bind_diag(params, OSSL_CC_OPTION_MAX_DGRAM_PAYLOAD_LEN,158sizeof(size_t), (void **)&new_p_max_dgram_payload_len)159|| !bind_diag(params, OSSL_CC_OPTION_CUR_CWND_SIZE,160sizeof(uint64_t), (void **)&new_p_cur_cwnd_size)161|| !bind_diag(params, OSSL_CC_OPTION_MIN_CWND_SIZE,162sizeof(uint64_t), (void **)&new_p_min_cwnd_size)163|| !bind_diag(params, OSSL_CC_OPTION_CUR_BYTES_IN_FLIGHT,164sizeof(uint64_t), (void **)&new_p_cur_bytes_in_flight)165|| !bind_diag(params, OSSL_CC_OPTION_CUR_STATE,166sizeof(uint32_t), (void **)&new_p_cur_state))167return 0;168169if (new_p_max_dgram_payload_len != NULL)170nr->p_diag_max_dgram_payload_len = new_p_max_dgram_payload_len;171172if (new_p_cur_cwnd_size != NULL)173nr->p_diag_cur_cwnd_size = new_p_cur_cwnd_size;174175if (new_p_min_cwnd_size != NULL)176nr->p_diag_min_cwnd_size = new_p_min_cwnd_size;177178if (new_p_cur_bytes_in_flight != NULL)179nr->p_diag_cur_bytes_in_flight = new_p_cur_bytes_in_flight;180181if (new_p_cur_state != NULL)182nr->p_diag_cur_state = new_p_cur_state;183184newreno_update_diag(nr);185return 1;186}187188static void unbind_diag(OSSL_PARAM *params, const char *param_name,189void **pp)190{191const OSSL_PARAM *p = OSSL_PARAM_locate_const(params, param_name);192193if (p != NULL)194*pp = NULL;195}196197static int newreno_unbind_diagnostic(OSSL_CC_DATA *cc, OSSL_PARAM *params)198{199OSSL_CC_NEWRENO *nr = (OSSL_CC_NEWRENO *)cc;200201unbind_diag(params, OSSL_CC_OPTION_MAX_DGRAM_PAYLOAD_LEN,202(void **)&nr->p_diag_max_dgram_payload_len);203unbind_diag(params, OSSL_CC_OPTION_CUR_CWND_SIZE,204(void **)&nr->p_diag_cur_cwnd_size);205unbind_diag(params, OSSL_CC_OPTION_MIN_CWND_SIZE,206(void **)&nr->p_diag_min_cwnd_size);207unbind_diag(params, OSSL_CC_OPTION_CUR_BYTES_IN_FLIGHT,208(void **)&nr->p_diag_cur_bytes_in_flight);209unbind_diag(params, OSSL_CC_OPTION_CUR_STATE,210(void **)&nr->p_diag_cur_state);211return 1;212}213214static void newreno_update_diag(OSSL_CC_NEWRENO *nr)215{216if (nr->p_diag_max_dgram_payload_len != NULL)217*nr->p_diag_max_dgram_payload_len = nr->max_dgram_size;218219if (nr->p_diag_cur_cwnd_size != NULL)220*nr->p_diag_cur_cwnd_size = nr->cong_wnd;221222if (nr->p_diag_min_cwnd_size != NULL)223*nr->p_diag_min_cwnd_size = nr->k_min_wnd;224225if (nr->p_diag_cur_bytes_in_flight != NULL)226*nr->p_diag_cur_bytes_in_flight = nr->bytes_in_flight;227228if (nr->p_diag_cur_state != NULL) {229if (nr->in_congestion_recovery)230*nr->p_diag_cur_state = 'R';231else if (nr->cong_wnd < nr->slow_start_thresh)232*nr->p_diag_cur_state = 'S';233else234*nr->p_diag_cur_state = 'A';235}236}237238static int newreno_in_cong_recovery(OSSL_CC_NEWRENO *nr, OSSL_TIME tx_time)239{240return ossl_time_compare(tx_time, nr->cong_recovery_start_time) <= 0;241}242243static void newreno_cong(OSSL_CC_NEWRENO *nr, OSSL_TIME tx_time)244{245int err = 0;246247/* No reaction if already in a recovery period. */248if (newreno_in_cong_recovery(nr, tx_time))249return;250251/* Start a new recovery period. */252nr->in_congestion_recovery = 1;253nr->cong_recovery_start_time = nr->now_cb(nr->now_cb_arg);254255/* slow_start_thresh = cong_wnd * loss_reduction_factor */256nr->slow_start_thresh257= safe_muldiv_u64(nr->cong_wnd,258nr->k_loss_reduction_factor_num,259nr->k_loss_reduction_factor_den,260&err);261262if (err)263nr->slow_start_thresh = UINT64_MAX;264265nr->cong_wnd = nr->slow_start_thresh;266if (nr->cong_wnd < nr->k_min_wnd)267nr->cong_wnd = nr->k_min_wnd;268}269270static void newreno_flush(OSSL_CC_NEWRENO *nr, uint32_t flags)271{272if (!nr->processing_loss)273return;274275newreno_cong(nr, nr->tx_time_of_last_loss);276277if ((flags & OSSL_CC_LOST_FLAG_PERSISTENT_CONGESTION) != 0) {278nr->cong_wnd = nr->k_min_wnd;279nr->cong_recovery_start_time = ossl_time_zero();280}281282nr->processing_loss = 0;283newreno_update_diag(nr);284}285286static uint64_t newreno_get_tx_allowance(OSSL_CC_DATA *cc)287{288OSSL_CC_NEWRENO *nr = (OSSL_CC_NEWRENO *)cc;289290if (nr->bytes_in_flight >= nr->cong_wnd)291return 0;292293return nr->cong_wnd - nr->bytes_in_flight;294}295296static OSSL_TIME newreno_get_wakeup_deadline(OSSL_CC_DATA *cc)297{298if (newreno_get_tx_allowance(cc) > 0) {299/* We have TX allowance now so wakeup immediately */300return ossl_time_zero();301} else {302/*303* The NewReno congestion controller does not vary its state in time,304* only in response to stimulus.305*/306return ossl_time_infinite();307}308}309310static int newreno_on_data_sent(OSSL_CC_DATA *cc, uint64_t num_bytes)311{312OSSL_CC_NEWRENO *nr = (OSSL_CC_NEWRENO *)cc;313314nr->bytes_in_flight += num_bytes;315newreno_update_diag(nr);316return 1;317}318319static int newreno_is_cong_limited(OSSL_CC_NEWRENO *nr)320{321uint64_t wnd_rem;322323/* We are congestion-limited if we are already at the congestion window. */324if (nr->bytes_in_flight >= nr->cong_wnd)325return 1;326327wnd_rem = nr->cong_wnd - nr->bytes_in_flight;328329/*330* Consider ourselves congestion-limited if less than three datagrams' worth331* of congestion window remains to be spent, or if we are in slow start and332* have consumed half of our window.333*/334return (nr->cong_wnd < nr->slow_start_thresh && wnd_rem <= nr->cong_wnd / 2)335|| wnd_rem <= 3 * nr->max_dgram_size;336}337338static int newreno_on_data_acked(OSSL_CC_DATA *cc,339const OSSL_CC_ACK_INFO *info)340{341OSSL_CC_NEWRENO *nr = (OSSL_CC_NEWRENO *)cc;342343/*344* Packet has been acked. Firstly, remove it from the aggregate count of345* bytes in flight.346*/347nr->bytes_in_flight -= info->tx_size;348349/*350* We use acknowledgement of data as a signal that we are not at channel351* capacity and that it may be reasonable to increase the congestion window.352* However, acknowledgement is not a useful signal that there is further353* capacity if we are not actually saturating the congestion window that we354* already have (for example, if the application is not generating much data355* or we are limited by flow control). Therefore, we only expand the356* congestion window if we are consuming a significant fraction of the357* congestion window.358*/359if (!newreno_is_cong_limited(nr))360goto out;361362/*363* We can handle acknowledgement of a packet in one of three ways364* depending on our current state:365*366* - Congestion Recovery: Do nothing. We don't start increasing367* the congestion window in response to acknowledgements until368* we are no longer in the Congestion Recovery state.369*370* - Slow Start: Increase the congestion window using the slow371* start scale.372*373* - Congestion Avoidance: Increase the congestion window using374* the congestion avoidance scale.375*/376if (newreno_in_cong_recovery(nr, info->tx_time)) {377/* Congestion recovery, do nothing. */378} else if (nr->cong_wnd < nr->slow_start_thresh) {379/* When this condition is true we are in the Slow Start state. */380nr->cong_wnd += info->tx_size;381nr->in_congestion_recovery = 0;382} else {383/* Otherwise, we are in the Congestion Avoidance state. */384nr->bytes_acked += info->tx_size;385386/*387* Avoid integer division as per RFC 9002 s. B.5. / RFC3465 s. 2.1.388*/389if (nr->bytes_acked >= nr->cong_wnd) {390nr->bytes_acked -= nr->cong_wnd;391nr->cong_wnd += nr->max_dgram_size;392}393394nr->in_congestion_recovery = 0;395}396397out:398newreno_update_diag(nr);399return 1;400}401402static int newreno_on_data_lost(OSSL_CC_DATA *cc,403const OSSL_CC_LOSS_INFO *info)404{405OSSL_CC_NEWRENO *nr = (OSSL_CC_NEWRENO *)cc;406407if (info->tx_size > nr->bytes_in_flight)408return 0;409410nr->bytes_in_flight -= info->tx_size;411412if (!nr->processing_loss) {413414if (ossl_time_compare(info->tx_time, nr->tx_time_of_last_loss) <= 0)415/*416* After triggering congestion due to a lost packet at time t, don't417* trigger congestion again due to any subsequently detected lost418* packet at a time s < t, as we've effectively already signalled419* congestion on loss of that and subsequent packets.420*/421goto out;422423nr->processing_loss = 1;424425/*426* Cancel any pending window increase in the Congestion Avoidance state.427*/428nr->bytes_acked = 0;429}430431nr->tx_time_of_last_loss432= ossl_time_max(nr->tx_time_of_last_loss, info->tx_time);433434out:435newreno_update_diag(nr);436return 1;437}438439static int newreno_on_data_lost_finished(OSSL_CC_DATA *cc, uint32_t flags)440{441OSSL_CC_NEWRENO *nr = (OSSL_CC_NEWRENO *)cc;442443newreno_flush(nr, flags);444return 1;445}446447static int newreno_on_data_invalidated(OSSL_CC_DATA *cc,448uint64_t num_bytes)449{450OSSL_CC_NEWRENO *nr = (OSSL_CC_NEWRENO *)cc;451452nr->bytes_in_flight -= num_bytes;453newreno_update_diag(nr);454return 1;455}456457static int newreno_on_ecn(OSSL_CC_DATA *cc,458const OSSL_CC_ECN_INFO *info)459{460OSSL_CC_NEWRENO *nr = (OSSL_CC_NEWRENO *)cc;461462nr->processing_loss = 1;463nr->bytes_acked = 0;464nr->tx_time_of_last_loss = info->largest_acked_time;465newreno_flush(nr, 0);466return 1;467}468469const OSSL_CC_METHOD ossl_cc_newreno_method = {470newreno_new,471newreno_free,472newreno_reset,473newreno_set_input_params,474newreno_bind_diagnostic,475newreno_unbind_diagnostic,476newreno_get_tx_allowance,477newreno_get_wakeup_deadline,478newreno_on_data_sent,479newreno_on_data_acked,480newreno_on_data_lost,481newreno_on_data_lost_finished,482newreno_on_data_invalidated,483newreno_on_ecn,484};485486487