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