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