blob: 67e9a14ceda7788a1a731510a23fee5f1a00d3eb [file] [log] [blame]
Florin Coras52814732019-06-12 15:38:19 -07001/*
2 * Copyright (c) 2019 Cisco and/or its affiliates.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at:
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 *
15 * TCP byte tracker that can generate delivery rate estimates. Based on
16 * draft-cheng-iccrg-delivery-rate-estimation-00
17 */
18
Florin Coras999840c2020-03-18 20:31:34 +000019#include <vnet/tcp/tcp_bt.h>
Florin Coras52814732019-06-12 15:38:19 -070020#include <vnet/tcp/tcp.h>
Florin Coras999840c2020-03-18 20:31:34 +000021#include <vnet/tcp/tcp_inlines.h>
Florin Coras52814732019-06-12 15:38:19 -070022
23static tcp_bt_sample_t *
24bt_get_sample (tcp_byte_tracker_t * bt, u32 bts_index)
25{
26 if (pool_is_free_index (bt->samples, bts_index))
27 return 0;
28 return pool_elt_at_index (bt->samples, bts_index);
29}
30
31static tcp_bt_sample_t *
32bt_next_sample (tcp_byte_tracker_t * bt, tcp_bt_sample_t * bts)
33{
34 return bt_get_sample (bt, bts->next);
35}
36
37static tcp_bt_sample_t *
38bt_prev_sample (tcp_byte_tracker_t * bt, tcp_bt_sample_t * bts)
39{
40 return bt_get_sample (bt, bts->prev);
41}
42
43static u32
44bt_sample_index (tcp_byte_tracker_t * bt, tcp_bt_sample_t * bts)
45{
46 if (!bts)
47 return TCP_BTS_INVALID_INDEX;
48 return bts - bt->samples;
49}
50
51static inline int
52bt_seq_lt (u32 a, u32 b)
53{
54 return seq_lt (a, b);
55}
56
57static tcp_bt_sample_t *
Florin Corasd6ae4bf2019-10-12 18:10:20 -070058bt_alloc_sample (tcp_byte_tracker_t * bt, u32 min_seq, u32 max_seq)
Florin Coras52814732019-06-12 15:38:19 -070059{
60 tcp_bt_sample_t *bts;
61
62 pool_get_zero (bt->samples, bts);
63 bts->next = bts->prev = TCP_BTS_INVALID_INDEX;
64 bts->min_seq = min_seq;
Florin Corasd6ae4bf2019-10-12 18:10:20 -070065 bts->max_seq = max_seq;
Florin Coras52814732019-06-12 15:38:19 -070066 rb_tree_add_custom (&bt->sample_lookup, bts->min_seq, bts - bt->samples,
67 bt_seq_lt);
68 return bts;
69}
70
71static void
72bt_free_sample (tcp_byte_tracker_t * bt, tcp_bt_sample_t * bts)
73{
74 if (bts->prev != TCP_BTS_INVALID_INDEX)
75 {
76 tcp_bt_sample_t *prev = bt_prev_sample (bt, bts);
77 prev->next = bts->next;
78 }
79 else
80 bt->head = bts->next;
81
82 if (bts->next != TCP_BTS_INVALID_INDEX)
83 {
84 tcp_bt_sample_t *next = bt_next_sample (bt, bts);
85 next->prev = bts->prev;
86 }
87 else
88 bt->tail = bts->prev;
89
90 rb_tree_del_custom (&bt->sample_lookup, bts->min_seq, bt_seq_lt);
91 if (CLIB_DEBUG)
92 memset (bts, 0xfc, sizeof (*bts));
93 pool_put (bt->samples, bts);
94}
95
96static tcp_bt_sample_t *
Florin Corasd6ae4bf2019-10-12 18:10:20 -070097bt_split_sample (tcp_byte_tracker_t * bt, tcp_bt_sample_t * bts, u32 seq)
98{
99 tcp_bt_sample_t *ns, *next;
100 u32 bts_index;
101
102 bts_index = bt_sample_index (bt, bts);
103
104 ASSERT (seq_leq (bts->min_seq, seq) && seq_lt (seq, bts->max_seq));
105
106 ns = bt_alloc_sample (bt, seq, bts->max_seq);
107 bts = bt_get_sample (bt, bts_index);
108
109 *ns = *bts;
110 ns->min_seq = seq;
111 bts->max_seq = seq;
112
113 next = bt_next_sample (bt, bts);
114 if (next)
115 next->prev = bt_sample_index (bt, ns);
116 else
117 bt->tail = bt_sample_index (bt, ns);
118
119 bts->next = bt_sample_index (bt, ns);
120 ns->prev = bt_sample_index (bt, bts);
121
122 return ns;
123}
124
125static tcp_bt_sample_t *
126bt_merge_sample (tcp_byte_tracker_t * bt, tcp_bt_sample_t * prev,
127 tcp_bt_sample_t * cur)
128{
129 ASSERT (prev->max_seq == cur->min_seq);
130 prev->max_seq = cur->max_seq;
131 if (bt_sample_index (bt, cur) == bt->tail)
132 bt->tail = bt_sample_index (bt, prev);
133 bt_free_sample (bt, cur);
134 return prev;
135}
136
137static tcp_bt_sample_t *
Florin Coras52814732019-06-12 15:38:19 -0700138bt_lookup_seq (tcp_byte_tracker_t * bt, u32 seq)
139{
140 rb_tree_t *rt = &bt->sample_lookup;
141 rb_node_t *cur, *prev;
142 tcp_bt_sample_t *bts;
143
144 cur = rb_node (rt, rt->root);
145 if (rb_node_is_tnil (rt, cur))
146 return 0;
147
148 while (seq != cur->key)
149 {
150 prev = cur;
151 if (seq_lt (seq, cur->key))
152 cur = rb_node_left (rt, cur);
153 else
154 cur = rb_node_right (rt, cur);
155
156 if (rb_node_is_tnil (rt, cur))
157 {
158 /* Hit tnil as a left child. Find predecessor */
159 if (seq_lt (seq, prev->key))
160 {
161 cur = rb_tree_predecessor (rt, prev);
162 if (rb_node_is_tnil (rt, cur))
163 return 0;
164 bts = bt_get_sample (bt, cur->opaque);
165 }
166 /* Hit tnil as a right child */
167 else
168 {
169 bts = bt_get_sample (bt, prev->opaque);
170 }
171
172 if (seq_geq (seq, bts->min_seq))
173 return bts;
174
175 return 0;
176 }
177 }
178
179 if (!rb_node_is_tnil (rt, cur))
180 return bt_get_sample (bt, cur->opaque);
181
182 return 0;
183}
184
185static void
186bt_update_sample (tcp_byte_tracker_t * bt, tcp_bt_sample_t * bts, u32 seq)
187{
188 rb_tree_del_custom (&bt->sample_lookup, bts->min_seq, bt_seq_lt);
189 bts->min_seq = seq;
190 rb_tree_add_custom (&bt->sample_lookup, bts->min_seq,
191 bt_sample_index (bt, bts), bt_seq_lt);
192}
193
194static tcp_bt_sample_t *
195bt_fix_overlapped (tcp_byte_tracker_t * bt, tcp_bt_sample_t * start,
196 u32 seq, u8 is_end)
197{
198 tcp_bt_sample_t *cur, *next;
199
200 cur = start;
Florin Corasd6ae4bf2019-10-12 18:10:20 -0700201 while (cur && seq_leq (cur->max_seq, seq))
Florin Coras52814732019-06-12 15:38:19 -0700202 {
Florin Corasd6ae4bf2019-10-12 18:10:20 -0700203 next = bt_next_sample (bt, cur);
Florin Coras52814732019-06-12 15:38:19 -0700204 bt_free_sample (bt, cur);
205 cur = next;
206 }
207
Florin Corasd6ae4bf2019-10-12 18:10:20 -0700208 if (cur && seq_lt (cur->min_seq, seq))
209 bt_update_sample (bt, cur, seq);
Florin Coras52814732019-06-12 15:38:19 -0700210
Florin Coras52814732019-06-12 15:38:19 -0700211 return cur;
212}
213
214int
215tcp_bt_is_sane (tcp_byte_tracker_t * bt)
216{
217 tcp_bt_sample_t *bts, *tmp;
218
219 if (pool_elts (bt->samples) != pool_elts (bt->sample_lookup.nodes) - 1)
220 return 0;
221
222 if (bt->head == TCP_BTS_INVALID_INDEX)
223 {
224 if (bt->tail != TCP_BTS_INVALID_INDEX)
225 return 0;
226 if (pool_elts (bt->samples) != 0)
227 return 0;
228 return 1;
229 }
230
231 bts = bt_get_sample (bt, bt->tail);
232 if (!bts)
233 return 0;
234
235 bts = bt_get_sample (bt, bt->head);
236 if (!bts || bts->prev != TCP_BTS_INVALID_INDEX)
237 return 0;
238
239 while (bts)
240 {
241 tmp = bt_lookup_seq (bt, bts->min_seq);
242 if (!tmp)
243 return 0;
244 if (tmp != bts)
245 return 0;
246 tmp = bt_next_sample (bt, bts);
247 if (tmp)
248 {
249 if (tmp->prev != bt_sample_index (bt, bts))
250 {
251 clib_warning ("next %u thinks prev is %u should be %u",
252 bts->next, tmp->prev, bt_sample_index (bt, bts));
253 return 0;
254 }
255 if (!seq_lt (bts->min_seq, tmp->min_seq))
256 return 0;
257 }
258 else
259 {
260 if (bt->tail != bt_sample_index (bt, bts))
261 return 0;
262 if (bts->next != TCP_BTS_INVALID_INDEX)
263 return 0;
264 }
265 bts = tmp;
266 }
267 return 1;
268}
269
270static tcp_bt_sample_t *
Florin Corasd6ae4bf2019-10-12 18:10:20 -0700271tcp_bt_alloc_tx_sample (tcp_connection_t * tc, u32 min_seq, u32 max_seq)
Florin Coras52814732019-06-12 15:38:19 -0700272{
273 tcp_bt_sample_t *bts;
Florin Corasd6ae4bf2019-10-12 18:10:20 -0700274 bts = bt_alloc_sample (tc->bt, min_seq, max_seq);
Florin Coras52814732019-06-12 15:38:19 -0700275 bts->delivered = tc->delivered;
276 bts->delivered_time = tc->delivered_time;
Florin Coras85fc1302019-06-26 09:12:34 -0700277 bts->tx_time = tcp_time_now_us (tc->c_thread_index);
Florin Coras7436b432019-09-10 23:26:27 -0700278 bts->first_tx_time = tc->first_tx_time;
Florin Coras52814732019-06-12 15:38:19 -0700279 bts->flags |= tc->app_limited ? TCP_BTS_IS_APP_LIMITED : 0;
Florin Corasdb88ffb2020-03-11 18:30:48 +0000280 bts->tx_in_flight = tcp_flight_size (tc);
281 bts->tx_lost = tc->lost;
Florin Coras52814732019-06-12 15:38:19 -0700282 return bts;
283}
284
285void
286tcp_bt_check_app_limited (tcp_connection_t * tc)
287{
288 u32 available_bytes, flight_size;
289
290 available_bytes = transport_max_tx_dequeue (&tc->connection);
291 flight_size = tcp_flight_size (tc);
292
293 /* Not enough bytes to fill the cwnd */
294 if (available_bytes + flight_size + tc->snd_mss < tc->cwnd
295 /* Bytes considered lost have been retransmitted */
296 && tc->sack_sb.lost_bytes <= tc->snd_rxt_bytes)
297 tc->app_limited = tc->delivered + flight_size ? : 1;
298}
299
300void
Florin Corasd6ae4bf2019-10-12 18:10:20 -0700301tcp_bt_track_tx (tcp_connection_t * tc, u32 len)
Florin Coras52814732019-06-12 15:38:19 -0700302{
303 tcp_byte_tracker_t *bt = tc->bt;
304 tcp_bt_sample_t *bts, *tail;
305 u32 bts_index;
306
Florin Corasd6ae4bf2019-10-12 18:10:20 -0700307 tail = bt_get_sample (bt, bt->tail);
308 if (tail && tail->max_seq == tc->snd_nxt
Ryujiro Shibuyad899d542020-10-23 07:35:53 +0000309 && !(tail->flags & TCP_BTS_IS_SACKED)
Florin Corasd6ae4bf2019-10-12 18:10:20 -0700310 && tail->tx_time == tcp_time_now_us (tc->c_thread_index))
311 {
312 tail->max_seq += len;
313 return;
314 }
315
Florin Coras85fc1302019-06-26 09:12:34 -0700316 if (tc->snd_una == tc->snd_nxt)
Florin Coras7436b432019-09-10 23:26:27 -0700317 {
318 tc->delivered_time = tcp_time_now_us (tc->c_thread_index);
319 tc->first_tx_time = tc->delivered_time;
320 }
Florin Coras52814732019-06-12 15:38:19 -0700321
Florin Corasd6ae4bf2019-10-12 18:10:20 -0700322 bts = tcp_bt_alloc_tx_sample (tc, tc->snd_nxt, tc->snd_nxt + len);
Florin Coras52814732019-06-12 15:38:19 -0700323 bts_index = bt_sample_index (bt, bts);
324 tail = bt_get_sample (bt, bt->tail);
325 if (tail)
326 {
327 tail->next = bts_index;
328 bts->prev = bt->tail;
329 bt->tail = bts_index;
330 }
331 else
332 {
333 bt->tail = bt->head = bts_index;
334 }
335}
336
337void
338tcp_bt_track_rxt (tcp_connection_t * tc, u32 start, u32 end)
339{
340 tcp_byte_tracker_t *bt = tc->bt;
341 tcp_bt_sample_t *bts, *next, *cur, *prev, *nbts;
Florin Corasd6ae4bf2019-10-12 18:10:20 -0700342 u32 bts_index, cur_index, next_index, prev_index, max_seq;
Florin Coras52814732019-06-12 15:38:19 -0700343 u8 is_end = end == tc->snd_nxt;
Florin Corasc17ff6e2020-04-22 22:07:43 +0000344 tcp_bts_flags_t bts_flags;
Florin Coras52814732019-06-12 15:38:19 -0700345
Florin Corasd6ae4bf2019-10-12 18:10:20 -0700346 /* Contiguous blocks retransmitted at the same time */
Florin Coras52814732019-06-12 15:38:19 -0700347 bts = bt_get_sample (bt, bt->last_ooo);
Florin Corasd6ae4bf2019-10-12 18:10:20 -0700348 if (bts && bts->max_seq == start
349 && bts->tx_time == tcp_time_now_us (tc->c_thread_index))
Florin Coras52814732019-06-12 15:38:19 -0700350 {
351 bts->max_seq = end;
352 next = bt_next_sample (bt, bts);
353 if (next)
354 bt_fix_overlapped (bt, next, end, is_end);
355
356 return;
357 }
358
Florin Corasc17ff6e2020-04-22 22:07:43 +0000359 /* Find original tx sample and cache flags in case the sample
360 * is freed or the pool moves */
Florin Coras52814732019-06-12 15:38:19 -0700361 bts = bt_lookup_seq (bt, start);
Florin Corasc17ff6e2020-04-22 22:07:43 +0000362 bts_flags = bts->flags;
Florin Coras52814732019-06-12 15:38:19 -0700363
364 ASSERT (bts != 0 && seq_geq (start, bts->min_seq));
365
366 /* Head in the past */
367 if (seq_lt (bts->min_seq, tc->snd_una))
368 bt_update_sample (bt, bts, tc->snd_una);
369
370 /* Head overlap */
371 if (bts->min_seq == start)
372 {
373 prev_index = bts->prev;
374 next = bt_fix_overlapped (bt, bts, end, is_end);
Florin Corasc17ff6e2020-04-22 22:07:43 +0000375 /* bts might no longer be valid from here */
Florin Coras52814732019-06-12 15:38:19 -0700376 next_index = bt_sample_index (bt, next);
377
Florin Corasd6ae4bf2019-10-12 18:10:20 -0700378 cur = tcp_bt_alloc_tx_sample (tc, start, end);
Florin Coras52814732019-06-12 15:38:19 -0700379 cur->flags |= TCP_BTS_IS_RXT;
Benoît Gannea04adbf2020-04-22 19:15:49 +0200380 if (bts_flags & TCP_BTS_IS_RXT)
Florin Coras46ec6e02019-10-22 13:34:30 -0700381 cur->flags |= TCP_BTS_IS_RXT_LOST;
Florin Coras52814732019-06-12 15:38:19 -0700382 cur->next = next_index;
383 cur->prev = prev_index;
384
385 cur_index = bt_sample_index (bt, cur);
386
387 if (next_index != TCP_BTS_INVALID_INDEX)
388 {
389 next = bt_get_sample (bt, next_index);
390 next->prev = cur_index;
391 }
392 else
393 {
394 bt->tail = cur_index;
395 }
396
397 if (prev_index != TCP_BTS_INVALID_INDEX)
398 {
399 prev = bt_get_sample (bt, prev_index);
400 prev->next = cur_index;
401 }
402 else
403 {
404 bt->head = cur_index;
405 }
406
407 bt->last_ooo = cur_index;
408 return;
409 }
410
411 bts_index = bt_sample_index (bt, bts);
412 next = bt_next_sample (bt, bts);
413 if (next)
Florin Coras62a7fe22020-02-20 16:04:03 +0000414 bt_fix_overlapped (bt, next, end, is_end);
Florin Coras52814732019-06-12 15:38:19 -0700415
Florin Corasd6ae4bf2019-10-12 18:10:20 -0700416 max_seq = bts->max_seq;
417 ASSERT (seq_lt (start, max_seq));
Florin Coras52814732019-06-12 15:38:19 -0700418
419 /* Have to split or tail overlap */
Florin Corasd6ae4bf2019-10-12 18:10:20 -0700420 cur = tcp_bt_alloc_tx_sample (tc, start, end);
Florin Coras52814732019-06-12 15:38:19 -0700421 cur->flags |= TCP_BTS_IS_RXT;
Florin Corasc17ff6e2020-04-22 22:07:43 +0000422 if (bts_flags & TCP_BTS_IS_RXT)
Florin Coras46ec6e02019-10-22 13:34:30 -0700423 cur->flags |= TCP_BTS_IS_RXT_LOST;
Florin Coras52814732019-06-12 15:38:19 -0700424 cur->prev = bts_index;
425 cur_index = bt_sample_index (bt, cur);
426
427 /* Split. Allocate another sample */
Florin Corasd6ae4bf2019-10-12 18:10:20 -0700428 if (seq_lt (end, max_seq))
Florin Coras52814732019-06-12 15:38:19 -0700429 {
Florin Corasd6ae4bf2019-10-12 18:10:20 -0700430 nbts = tcp_bt_alloc_tx_sample (tc, end, bts->max_seq);
Florin Coras52814732019-06-12 15:38:19 -0700431 cur = bt_get_sample (bt, cur_index);
432 bts = bt_get_sample (bt, bts_index);
433
434 *nbts = *bts;
435 nbts->min_seq = end;
436
437 if (nbts->next != TCP_BTS_INVALID_INDEX)
438 {
439 next = bt_get_sample (bt, nbts->next);
440 next->prev = bt_sample_index (bt, nbts);
441 }
442 else
443 bt->tail = bt_sample_index (bt, nbts);
444
445 bts->next = nbts->prev = cur_index;
446 cur->next = bt_sample_index (bt, nbts);
447
Florin Corasd6ae4bf2019-10-12 18:10:20 -0700448 bts->max_seq = start;
Florin Coras52814732019-06-12 15:38:19 -0700449 bt->last_ooo = cur_index;
450 }
451 /* Tail completely overlapped */
452 else
453 {
454 bts = bt_get_sample (bt, bts_index);
Florin Corasd6ae4bf2019-10-12 18:10:20 -0700455 bts->max_seq = start;
Florin Coras52814732019-06-12 15:38:19 -0700456
457 if (bts->next != TCP_BTS_INVALID_INDEX)
458 {
459 next = bt_get_sample (bt, bts->next);
460 next->prev = cur_index;
461 }
462 else
463 bt->tail = cur_index;
464
465 cur->next = bts->next;
466 bts->next = cur_index;
467
468 bt->last_ooo = cur_index;
469 }
470}
471
472static void
473tcp_bt_sample_to_rate_sample (tcp_connection_t * tc, tcp_bt_sample_t * bts,
474 tcp_rate_sample_t * rs)
475{
Florin Corasd6ae4bf2019-10-12 18:10:20 -0700476 if (bts->flags & TCP_BTS_IS_SACKED)
477 return;
478
Florin Coras85fc1302019-06-26 09:12:34 -0700479 if (rs->prior_delivered && rs->prior_delivered >= bts->delivered)
Florin Coras52814732019-06-12 15:38:19 -0700480 return;
481
Florin Coras85fc1302019-06-26 09:12:34 -0700482 rs->prior_delivered = bts->delivered;
483 rs->prior_time = bts->delivered_time;
Florin Coras7436b432019-09-10 23:26:27 -0700484 rs->interval_time = bts->tx_time - bts->first_tx_time;
Florin Corasd6ae4bf2019-10-12 18:10:20 -0700485 rs->rtt_time = tc->delivered_time - bts->tx_time;
Florin Coras52814732019-06-12 15:38:19 -0700486 rs->flags = bts->flags;
Florin Corasdb88ffb2020-03-11 18:30:48 +0000487 rs->tx_in_flight = bts->tx_in_flight;
488 rs->tx_lost = bts->tx_lost;
Florin Coras7436b432019-09-10 23:26:27 -0700489 tc->first_tx_time = bts->tx_time;
Florin Coras52814732019-06-12 15:38:19 -0700490}
491
492static void
493tcp_bt_walk_samples (tcp_connection_t * tc, tcp_rate_sample_t * rs)
494{
495 tcp_byte_tracker_t *bt = tc->bt;
496 tcp_bt_sample_t *next, *cur;
497
498 cur = bt_get_sample (bt, bt->head);
Florin Corasd6ae4bf2019-10-12 18:10:20 -0700499 while (cur && seq_leq (cur->max_seq, tc->snd_una))
Florin Coras52814732019-06-12 15:38:19 -0700500 {
Florin Corasd6ae4bf2019-10-12 18:10:20 -0700501 next = bt_next_sample (bt, cur);
502 tcp_bt_sample_to_rate_sample (tc, cur, rs);
Florin Coras52814732019-06-12 15:38:19 -0700503 bt_free_sample (bt, cur);
504 cur = next;
505 }
Florin Corasd6ae4bf2019-10-12 18:10:20 -0700506
507 if (cur && seq_lt (cur->min_seq, tc->snd_una))
Ryujiro Shibuyad899d542020-10-23 07:35:53 +0000508 {
509 bt_update_sample (bt, cur, tc->snd_una);
510 tcp_bt_sample_to_rate_sample (tc, cur, rs);
511 }
Florin Coras52814732019-06-12 15:38:19 -0700512}
513
514static void
515tcp_bt_walk_samples_ooo (tcp_connection_t * tc, tcp_rate_sample_t * rs)
516{
517 sack_block_t *blks = tc->rcv_opts.sacks, *blk;
518 tcp_byte_tracker_t *bt = tc->bt;
Florin Corasd6ae4bf2019-10-12 18:10:20 -0700519 tcp_bt_sample_t *cur, *prev, *next;
Florin Coras52814732019-06-12 15:38:19 -0700520 int i;
521
522 for (i = 0; i < vec_len (blks); i++)
523 {
524 blk = &blks[i];
525
526 /* Ignore blocks that are already covered by snd_una */
527 if (seq_lt (blk->end, tc->snd_una))
528 continue;
529
530 cur = bt_lookup_seq (bt, blk->start);
531 if (!cur)
532 continue;
533
Florin Corasd6ae4bf2019-10-12 18:10:20 -0700534 ASSERT (seq_geq (blk->start, cur->min_seq)
535 && seq_lt (blk->start, cur->max_seq));
Florin Coras52814732019-06-12 15:38:19 -0700536
Florin Corasd6ae4bf2019-10-12 18:10:20 -0700537 /* Current should be split. Second part will be consumed */
538 if (PREDICT_FALSE (cur->min_seq != blk->start))
Florin Coras52814732019-06-12 15:38:19 -0700539 {
Florin Corasd6ae4bf2019-10-12 18:10:20 -0700540 cur = bt_split_sample (bt, cur, blk->start);
541 prev = bt_prev_sample (bt, cur);
Florin Coras52814732019-06-12 15:38:19 -0700542 }
Florin Corasd6ae4bf2019-10-12 18:10:20 -0700543 else
544 prev = bt_prev_sample (bt, cur);
Florin Coras52814732019-06-12 15:38:19 -0700545
Florin Corasd6ae4bf2019-10-12 18:10:20 -0700546 while (cur && seq_leq (cur->max_seq, blk->end))
Florin Coras52814732019-06-12 15:38:19 -0700547 {
Florin Corasd6ae4bf2019-10-12 18:10:20 -0700548 if (!(cur->flags & TCP_BTS_IS_SACKED))
549 {
550 tcp_bt_sample_to_rate_sample (tc, cur, rs);
551 cur->flags |= TCP_BTS_IS_SACKED;
552 if (prev && (prev->flags & TCP_BTS_IS_SACKED))
553 {
554 cur = bt_merge_sample (bt, prev, cur);
555 next = bt_next_sample (bt, cur);
556 }
557 else
558 {
559 next = bt_next_sample (bt, cur);
560 if (next && (next->flags & TCP_BTS_IS_SACKED))
561 {
562 cur = bt_merge_sample (bt, cur, next);
563 next = bt_next_sample (bt, cur);
564 }
565 }
566 }
567 else
568 next = bt_next_sample (bt, cur);
569
570 prev = cur;
Florin Coras52814732019-06-12 15:38:19 -0700571 cur = next;
572 }
573
Florin Corasd6ae4bf2019-10-12 18:10:20 -0700574 if (cur && seq_lt (cur->min_seq, blk->end))
575 {
576 tcp_bt_sample_to_rate_sample (tc, cur, rs);
577 prev = bt_prev_sample (bt, cur);
578 /* Extend previous to include the newly sacked bytes */
579 if (prev && (prev->flags & TCP_BTS_IS_SACKED))
580 {
581 prev->max_seq = blk->end;
582 bt_update_sample (bt, cur, blk->end);
583 }
584 /* Split sample into two. First part is consumed */
585 else
586 {
587 next = bt_split_sample (bt, cur, blk->end);
588 cur = bt_prev_sample (bt, next);
589 cur->flags |= TCP_BTS_IS_SACKED;
590 }
591 }
Florin Coras52814732019-06-12 15:38:19 -0700592 }
593}
594
595void
596tcp_bt_sample_delivery_rate (tcp_connection_t * tc, tcp_rate_sample_t * rs)
597{
598 u32 delivered;
599
600 if (PREDICT_FALSE (tc->flags & TCP_CONN_FINSNT))
601 return;
602
Florin Corasdb88ffb2020-03-11 18:30:48 +0000603 tc->lost += tc->sack_sb.last_lost_bytes;
604
Florin Coras52814732019-06-12 15:38:19 -0700605 delivered = tc->bytes_acked + tc->sack_sb.last_sacked_bytes;
Florin Coras3e2ec422020-10-09 12:04:50 -0700606 /* Do not count bytes that were previously sacked again */
607 delivered -= tc->sack_sb.last_bytes_delivered;
Florin Coras52814732019-06-12 15:38:19 -0700608 if (!delivered || tc->bt->head == TCP_BTS_INVALID_INDEX)
609 return;
610
Florin Coras3e2ec422020-10-09 12:04:50 -0700611 tc->delivered += delivered;
Florin Coras52814732019-06-12 15:38:19 -0700612 tc->delivered_time = tcp_time_now_us (tc->c_thread_index);
613
614 if (tc->app_limited && tc->delivered > tc->app_limited)
615 tc->app_limited = 0;
616
617 if (tc->bytes_acked)
618 tcp_bt_walk_samples (tc, rs);
619
620 if (tc->sack_sb.last_sacked_bytes)
621 tcp_bt_walk_samples_ooo (tc, rs);
Florin Coras85fc1302019-06-26 09:12:34 -0700622
Florin Corasd6ae4bf2019-10-12 18:10:20 -0700623 rs->interval_time = clib_max ((tc->delivered_time - rs->prior_time),
Florin Coras7436b432019-09-10 23:26:27 -0700624 rs->interval_time);
Florin Coras85fc1302019-06-26 09:12:34 -0700625 rs->delivered = tc->delivered - rs->prior_delivered;
Florin Coras85fc1302019-06-26 09:12:34 -0700626 rs->acked_and_sacked = delivered;
Florin Corasdb88ffb2020-03-11 18:30:48 +0000627 rs->last_lost = tc->sack_sb.last_lost_bytes;
628 rs->lost = tc->lost - rs->tx_lost;
Florin Coras52814732019-06-12 15:38:19 -0700629}
630
631void
632tcp_bt_flush_samples (tcp_connection_t * tc)
633{
634 tcp_byte_tracker_t *bt = tc->bt;
635 tcp_bt_sample_t *bts;
636 u32 *samples = 0, *si;
637
638 vec_validate (samples, pool_elts (bt->samples) - 1);
Florin Coras92f190a2019-08-23 10:28:01 -0700639 vec_reset_length (samples);
Florin Coras52814732019-06-12 15:38:19 -0700640
641 /* *INDENT-OFF* */
Damjan Marionb2c31b62020-12-13 21:47:40 +0100642 pool_foreach (bts, bt->samples) {
Florin Coras52814732019-06-12 15:38:19 -0700643 vec_add1 (samples, bts - bt->samples);
Damjan Marionb2c31b62020-12-13 21:47:40 +0100644 }
Florin Coras52814732019-06-12 15:38:19 -0700645 /* *INDENT-ON* */
646
647 vec_foreach (si, samples)
648 {
649 bts = bt_get_sample (bt, *si);
650 bt_free_sample (bt, bts);
651 }
652
653 vec_free (samples);
654}
655
656void
657tcp_bt_cleanup (tcp_connection_t * tc)
658{
659 tcp_byte_tracker_t *bt = tc->bt;
660
661 rb_tree_free_nodes (&bt->sample_lookup);
662 pool_free (bt->samples);
663 clib_mem_free (bt);
664 tc->bt = 0;
665}
666
667void
668tcp_bt_init (tcp_connection_t * tc)
669{
670 tcp_byte_tracker_t *bt;
671
672 bt = clib_mem_alloc (sizeof (tcp_byte_tracker_t));
673 clib_memset (bt, 0, sizeof (tcp_byte_tracker_t));
674
675 rb_tree_init (&bt->sample_lookup);
676 bt->head = bt->tail = TCP_BTS_INVALID_INDEX;
677 tc->bt = bt;
678}
679
Florin Corasd6ae4bf2019-10-12 18:10:20 -0700680u8 *
681format_tcp_bt_sample (u8 * s, va_list * args)
682{
683 tcp_connection_t *tc = va_arg (*args, tcp_connection_t *);
684 tcp_bt_sample_t *bts = va_arg (*args, tcp_bt_sample_t *);
685 f64 now = tcp_time_now_us (tc->c_thread_index);
686 s = format (s, "[%u, %u] d %u dt %.3f txt %.3f ftxt %.3f flags 0x%x",
687 bts->min_seq - tc->iss, bts->max_seq - tc->iss, bts->delivered,
688 now - bts->delivered_time, now - bts->tx_time,
689 now - bts->first_tx_time, bts->flags);
690 return s;
691}
692
693u8 *
694format_tcp_bt (u8 * s, va_list * args)
695{
696 tcp_connection_t *tc = va_arg (*args, tcp_connection_t *);
697 tcp_byte_tracker_t *bt = tc->bt;
698 tcp_bt_sample_t *bts;
699
700 bts = bt_get_sample (bt, bt->head);
701 while (bts)
702 {
703 s = format (s, "%U\n", format_tcp_bt_sample, tc, bts);
704 bts = bt_next_sample (bt, bts);
705 }
706
707 return s;
708}
709
Florin Coras52814732019-06-12 15:38:19 -0700710/*
711 * fd.io coding-style-patch-verification: ON
712 *
713 * Local Variables:
714 * eval: (c-set-style "gnu")
715 * End:
716 */