xref: /freebsd/crypto/openssl/ssl/quic/cc_newreno.c (revision e7be843b4a162e68651d3911f0357ed464915629)
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