Line data Source code
1 : /*
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 :
19 : #include <vnet/tcp/tcp_bt.h>
20 : #include <vnet/tcp/tcp.h>
21 : #include <vnet/tcp/tcp_inlines.h>
22 :
23 : static tcp_bt_sample_t *
24 171 : bt_get_sample (tcp_byte_tracker_t * bt, u32 bts_index)
25 : {
26 171 : if (pool_is_free_index (bt->samples, bts_index))
27 21 : return 0;
28 150 : return pool_elt_at_index (bt->samples, bts_index);
29 : }
30 :
31 : static tcp_bt_sample_t *
32 64 : bt_next_sample (tcp_byte_tracker_t * bt, tcp_bt_sample_t * bts)
33 : {
34 64 : return bt_get_sample (bt, bts->next);
35 : }
36 :
37 : static tcp_bt_sample_t *
38 16 : bt_prev_sample (tcp_byte_tracker_t * bt, tcp_bt_sample_t * bts)
39 : {
40 16 : return bt_get_sample (bt, bts->prev);
41 : }
42 :
43 : static u32
44 71 : bt_sample_index (tcp_byte_tracker_t * bt, tcp_bt_sample_t * bts)
45 : {
46 71 : if (!bts)
47 1 : return TCP_BTS_INVALID_INDEX;
48 70 : return bts - bt->samples;
49 : }
50 :
51 : static inline int
52 60 : bt_seq_lt (u32 a, u32 b)
53 : {
54 60 : return seq_lt (a, b);
55 : }
56 :
57 : static tcp_bt_sample_t *
58 17 : bt_alloc_sample (tcp_byte_tracker_t * bt, u32 min_seq, u32 max_seq)
59 : {
60 : tcp_bt_sample_t *bts;
61 :
62 17 : pool_get_zero (bt->samples, bts);
63 17 : bts->next = bts->prev = TCP_BTS_INVALID_INDEX;
64 17 : bts->min_seq = min_seq;
65 17 : bts->max_seq = max_seq;
66 17 : rb_tree_add_custom (&bt->sample_lookup, bts->min_seq, bts - bt->samples,
67 : bt_seq_lt);
68 17 : return bts;
69 : }
70 :
71 : static void
72 17 : bt_free_sample (tcp_byte_tracker_t * bt, tcp_bt_sample_t * bts)
73 : {
74 17 : if (bts->prev != TCP_BTS_INVALID_INDEX)
75 : {
76 5 : tcp_bt_sample_t *prev = bt_prev_sample (bt, bts);
77 5 : prev->next = bts->next;
78 : }
79 : else
80 12 : bt->head = bts->next;
81 :
82 17 : if (bts->next != TCP_BTS_INVALID_INDEX)
83 : {
84 11 : tcp_bt_sample_t *next = bt_next_sample (bt, bts);
85 11 : next->prev = bts->prev;
86 : }
87 : else
88 6 : bt->tail = bts->prev;
89 :
90 17 : rb_tree_del_custom (&bt->sample_lookup, bts->min_seq, bt_seq_lt);
91 : if (CLIB_DEBUG)
92 17 : memset (bts, 0xfc, sizeof (*bts));
93 17 : pool_put (bt->samples, bts);
94 17 : }
95 :
96 : static tcp_bt_sample_t *
97 5 : bt_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 5 : bts_index = bt_sample_index (bt, bts);
103 :
104 5 : ASSERT (seq_leq (bts->min_seq, seq) && seq_lt (seq, bts->max_seq));
105 :
106 5 : ns = bt_alloc_sample (bt, seq, bts->max_seq);
107 5 : bts = bt_get_sample (bt, bts_index);
108 :
109 5 : *ns = *bts;
110 5 : ns->min_seq = seq;
111 5 : bts->max_seq = seq;
112 :
113 5 : next = bt_next_sample (bt, bts);
114 5 : if (next)
115 3 : next->prev = bt_sample_index (bt, ns);
116 : else
117 2 : bt->tail = bt_sample_index (bt, ns);
118 :
119 5 : bts->next = bt_sample_index (bt, ns);
120 5 : ns->prev = bt_sample_index (bt, bts);
121 :
122 5 : return ns;
123 : }
124 :
125 : static tcp_bt_sample_t *
126 0 : bt_merge_sample (tcp_byte_tracker_t * bt, tcp_bt_sample_t * prev,
127 : tcp_bt_sample_t * cur)
128 : {
129 0 : ASSERT (prev->max_seq == cur->min_seq);
130 0 : prev->max_seq = cur->max_seq;
131 0 : if (bt_sample_index (bt, cur) == bt->tail)
132 0 : bt->tail = bt_sample_index (bt, prev);
133 0 : bt_free_sample (bt, cur);
134 0 : return prev;
135 : }
136 :
137 : static tcp_bt_sample_t *
138 40 : bt_lookup_seq (tcp_byte_tracker_t * bt, u32 seq)
139 : {
140 40 : rb_tree_t *rt = &bt->sample_lookup;
141 : rb_node_t *cur, *prev;
142 : tcp_bt_sample_t *bts;
143 :
144 40 : cur = rb_node (rt, rt->root);
145 40 : if (rb_node_is_tnil (rt, cur))
146 0 : return 0;
147 :
148 90 : while (seq != cur->key)
149 : {
150 52 : prev = cur;
151 52 : if (seq_lt (seq, cur->key))
152 23 : cur = rb_node_left (rt, cur);
153 : else
154 29 : cur = rb_node_right (rt, cur);
155 :
156 52 : if (rb_node_is_tnil (rt, cur))
157 : {
158 : /* Hit tnil as a left child. Find predecessor */
159 2 : if (seq_lt (seq, prev->key))
160 : {
161 1 : cur = rb_tree_predecessor (rt, prev);
162 1 : if (rb_node_is_tnil (rt, cur))
163 0 : return 0;
164 1 : bts = bt_get_sample (bt, cur->opaque);
165 : }
166 : /* Hit tnil as a right child */
167 : else
168 : {
169 1 : bts = bt_get_sample (bt, prev->opaque);
170 : }
171 :
172 2 : if (seq_geq (seq, bts->min_seq))
173 2 : return bts;
174 :
175 0 : return 0;
176 : }
177 : }
178 :
179 38 : if (!rb_node_is_tnil (rt, cur))
180 38 : return bt_get_sample (bt, cur->opaque);
181 :
182 0 : return 0;
183 : }
184 :
185 : static void
186 3 : bt_update_sample (tcp_byte_tracker_t * bt, tcp_bt_sample_t * bts, u32 seq)
187 : {
188 3 : rb_tree_del_custom (&bt->sample_lookup, bts->min_seq, bt_seq_lt);
189 3 : bts->min_seq = seq;
190 3 : rb_tree_add_custom (&bt->sample_lookup, bts->min_seq,
191 3 : bt_sample_index (bt, bts), bt_seq_lt);
192 3 : }
193 :
194 : static tcp_bt_sample_t *
195 3 : bt_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 3 : cur = start;
201 8 : while (cur && seq_leq (cur->max_seq, seq))
202 : {
203 5 : next = bt_next_sample (bt, cur);
204 5 : bt_free_sample (bt, cur);
205 5 : cur = next;
206 : }
207 :
208 3 : if (cur && seq_lt (cur->min_seq, seq))
209 0 : bt_update_sample (bt, cur, seq);
210 :
211 3 : return cur;
212 : }
213 :
214 : int
215 10 : tcp_bt_is_sane (tcp_byte_tracker_t * bt)
216 : {
217 : tcp_bt_sample_t *bts, *tmp;
218 :
219 10 : if (pool_elts (bt->samples) != pool_elts (bt->sample_lookup.nodes) - 1)
220 0 : return 0;
221 :
222 10 : if (bt->head == TCP_BTS_INVALID_INDEX)
223 : {
224 3 : if (bt->tail != TCP_BTS_INVALID_INDEX)
225 0 : return 0;
226 3 : if (pool_elts (bt->samples) != 0)
227 0 : return 0;
228 3 : return 1;
229 : }
230 :
231 7 : bts = bt_get_sample (bt, bt->tail);
232 7 : if (!bts)
233 0 : return 0;
234 :
235 7 : bts = bt_get_sample (bt, bt->head);
236 7 : if (!bts || bts->prev != TCP_BTS_INVALID_INDEX)
237 0 : return 0;
238 :
239 40 : while (bts)
240 : {
241 33 : tmp = bt_lookup_seq (bt, bts->min_seq);
242 33 : if (!tmp)
243 0 : return 0;
244 33 : if (tmp != bts)
245 0 : return 0;
246 33 : tmp = bt_next_sample (bt, bts);
247 33 : if (tmp)
248 : {
249 26 : if (tmp->prev != bt_sample_index (bt, bts))
250 : {
251 0 : clib_warning ("next %u thinks prev is %u should be %u",
252 : bts->next, tmp->prev, bt_sample_index (bt, bts));
253 0 : return 0;
254 : }
255 26 : if (!seq_lt (bts->min_seq, tmp->min_seq))
256 0 : return 0;
257 : }
258 : else
259 : {
260 7 : if (bt->tail != bt_sample_index (bt, bts))
261 0 : return 0;
262 7 : if (bts->next != TCP_BTS_INVALID_INDEX)
263 0 : return 0;
264 : }
265 33 : bts = tmp;
266 : }
267 7 : return 1;
268 : }
269 :
270 : static tcp_bt_sample_t *
271 12 : tcp_bt_alloc_tx_sample (tcp_connection_t * tc, u32 min_seq, u32 max_seq)
272 : {
273 : tcp_bt_sample_t *bts;
274 12 : bts = bt_alloc_sample (tc->bt, min_seq, max_seq);
275 12 : bts->delivered = tc->delivered;
276 12 : bts->delivered_time = tc->delivered_time;
277 12 : bts->tx_time = tcp_time_now_us (tc->c_thread_index);
278 12 : bts->first_tx_time = tc->first_tx_time;
279 12 : bts->flags |= tc->app_limited ? TCP_BTS_IS_APP_LIMITED : 0;
280 12 : bts->tx_in_flight = tcp_flight_size (tc);
281 12 : bts->tx_lost = tc->lost;
282 12 : return bts;
283 : }
284 :
285 : void
286 0 : tcp_bt_check_app_limited (tcp_connection_t * tc)
287 : {
288 : u32 available_bytes, flight_size;
289 :
290 0 : available_bytes = transport_max_tx_dequeue (&tc->connection);
291 0 : flight_size = tcp_flight_size (tc);
292 :
293 : /* Not enough bytes to fill the cwnd */
294 0 : if (available_bytes + flight_size + tc->snd_mss < tc->cwnd
295 : /* Bytes considered lost have been retransmitted */
296 0 : && tc->sack_sb.lost_bytes <= tc->snd_rxt_bytes)
297 0 : tc->app_limited = tc->delivered + flight_size ? : 1;
298 0 : }
299 :
300 : void
301 9 : tcp_bt_track_tx (tcp_connection_t * tc, u32 len)
302 : {
303 9 : tcp_byte_tracker_t *bt = tc->bt;
304 : tcp_bt_sample_t *bts, *tail;
305 : u32 bts_index;
306 :
307 9 : tail = bt_get_sample (bt, bt->tail);
308 9 : if (tail && tail->max_seq == tc->snd_nxt
309 5 : && !(tail->flags & TCP_BTS_IS_SACKED)
310 5 : && tail->tx_time == tcp_time_now_us (tc->c_thread_index))
311 : {
312 0 : tail->max_seq += len;
313 0 : return;
314 : }
315 :
316 9 : if (tc->snd_una == tc->snd_nxt)
317 : {
318 4 : tc->delivered_time = tcp_time_now_us (tc->c_thread_index);
319 4 : tc->first_tx_time = tc->delivered_time;
320 : }
321 :
322 9 : bts = tcp_bt_alloc_tx_sample (tc, tc->snd_nxt, tc->snd_nxt + len);
323 9 : bts_index = bt_sample_index (bt, bts);
324 9 : tail = bt_get_sample (bt, bt->tail);
325 9 : if (tail)
326 : {
327 5 : tail->next = bts_index;
328 5 : bts->prev = bt->tail;
329 5 : bt->tail = bts_index;
330 : }
331 : else
332 : {
333 4 : bt->tail = bt->head = bts_index;
334 : }
335 : }
336 :
337 : void
338 3 : tcp_bt_track_rxt (tcp_connection_t * tc, u32 start, u32 end)
339 : {
340 3 : tcp_byte_tracker_t *bt = tc->bt;
341 : tcp_bt_sample_t *bts, *next, *cur, *prev, *nbts;
342 : u32 bts_index, cur_index, next_index, prev_index, max_seq;
343 3 : u8 is_end = end == tc->snd_nxt;
344 : tcp_bts_flags_t bts_flags;
345 :
346 : /* Contiguous blocks retransmitted at the same time */
347 3 : bts = bt_get_sample (bt, bt->last_ooo);
348 3 : if (bts && bts->max_seq == start
349 0 : && bts->tx_time == tcp_time_now_us (tc->c_thread_index))
350 : {
351 0 : bts->max_seq = end;
352 0 : next = bt_next_sample (bt, bts);
353 0 : if (next)
354 0 : bt_fix_overlapped (bt, next, end, is_end);
355 :
356 0 : return;
357 : }
358 :
359 : /* Find original tx sample and cache flags in case the sample
360 : * is freed or the pool moves */
361 3 : bts = bt_lookup_seq (bt, start);
362 3 : bts_flags = bts->flags;
363 :
364 3 : ASSERT (bts != 0 && seq_geq (start, bts->min_seq));
365 :
366 : /* Head in the past */
367 3 : if (seq_lt (bts->min_seq, tc->snd_una))
368 0 : bt_update_sample (bt, bts, tc->snd_una);
369 :
370 : /* Head overlap */
371 3 : if (bts->min_seq == start)
372 : {
373 3 : prev_index = bts->prev;
374 3 : next = bt_fix_overlapped (bt, bts, end, is_end);
375 : /* bts might no longer be valid from here */
376 3 : next_index = bt_sample_index (bt, next);
377 :
378 3 : cur = tcp_bt_alloc_tx_sample (tc, start, end);
379 3 : cur->flags |= TCP_BTS_IS_RXT;
380 3 : if (bts_flags & TCP_BTS_IS_RXT)
381 0 : cur->flags |= TCP_BTS_IS_RXT_LOST;
382 3 : cur->next = next_index;
383 3 : cur->prev = prev_index;
384 :
385 3 : cur_index = bt_sample_index (bt, cur);
386 :
387 3 : if (next_index != TCP_BTS_INVALID_INDEX)
388 : {
389 2 : next = bt_get_sample (bt, next_index);
390 2 : next->prev = cur_index;
391 : }
392 : else
393 : {
394 1 : bt->tail = cur_index;
395 : }
396 :
397 3 : if (prev_index != TCP_BTS_INVALID_INDEX)
398 : {
399 2 : prev = bt_get_sample (bt, prev_index);
400 2 : prev->next = cur_index;
401 : }
402 : else
403 : {
404 1 : bt->head = cur_index;
405 : }
406 :
407 3 : bt->last_ooo = cur_index;
408 3 : return;
409 : }
410 :
411 0 : bts_index = bt_sample_index (bt, bts);
412 0 : next = bt_next_sample (bt, bts);
413 0 : if (next)
414 0 : bt_fix_overlapped (bt, next, end, is_end);
415 :
416 0 : max_seq = bts->max_seq;
417 0 : ASSERT (seq_lt (start, max_seq));
418 :
419 : /* Have to split or tail overlap */
420 0 : cur = tcp_bt_alloc_tx_sample (tc, start, end);
421 0 : cur->flags |= TCP_BTS_IS_RXT;
422 0 : if (bts_flags & TCP_BTS_IS_RXT)
423 0 : cur->flags |= TCP_BTS_IS_RXT_LOST;
424 0 : cur->prev = bts_index;
425 0 : cur_index = bt_sample_index (bt, cur);
426 :
427 : /* Split. Allocate another sample */
428 0 : if (seq_lt (end, max_seq))
429 : {
430 0 : nbts = tcp_bt_alloc_tx_sample (tc, end, bts->max_seq);
431 0 : cur = bt_get_sample (bt, cur_index);
432 0 : bts = bt_get_sample (bt, bts_index);
433 :
434 0 : *nbts = *bts;
435 0 : nbts->min_seq = end;
436 :
437 0 : if (nbts->next != TCP_BTS_INVALID_INDEX)
438 : {
439 0 : next = bt_get_sample (bt, nbts->next);
440 0 : next->prev = bt_sample_index (bt, nbts);
441 : }
442 : else
443 0 : bt->tail = bt_sample_index (bt, nbts);
444 :
445 0 : bts->next = nbts->prev = cur_index;
446 0 : cur->next = bt_sample_index (bt, nbts);
447 :
448 0 : bts->max_seq = start;
449 0 : bt->last_ooo = cur_index;
450 : }
451 : /* Tail completely overlapped */
452 : else
453 : {
454 0 : bts = bt_get_sample (bt, bts_index);
455 0 : bts->max_seq = start;
456 :
457 0 : if (bts->next != TCP_BTS_INVALID_INDEX)
458 : {
459 0 : next = bt_get_sample (bt, bts->next);
460 0 : next->prev = cur_index;
461 : }
462 : else
463 0 : bt->tail = cur_index;
464 :
465 0 : cur->next = bts->next;
466 0 : bts->next = cur_index;
467 :
468 0 : bt->last_ooo = cur_index;
469 : }
470 : }
471 :
472 : static void
473 16 : tcp_bt_sample_to_rate_sample (tcp_connection_t * tc, tcp_bt_sample_t * bts,
474 : tcp_rate_sample_t * rs)
475 : {
476 16 : if (bts->flags & TCP_BTS_IS_SACKED)
477 3 : return;
478 :
479 13 : if (rs->prior_delivered && rs->prior_delivered >= bts->delivered)
480 8 : return;
481 :
482 5 : rs->prior_delivered = bts->delivered;
483 5 : rs->prior_time = bts->delivered_time;
484 5 : rs->interval_time = bts->tx_time - bts->first_tx_time;
485 5 : rs->rtt_time = tc->delivered_time - bts->tx_time;
486 5 : rs->flags = bts->flags;
487 5 : rs->tx_in_flight = bts->tx_in_flight;
488 5 : rs->tx_lost = bts->tx_lost;
489 5 : tc->first_tx_time = bts->tx_time;
490 : }
491 :
492 : static void
493 5 : tcp_bt_walk_samples (tcp_connection_t * tc, tcp_rate_sample_t * rs)
494 : {
495 5 : tcp_byte_tracker_t *bt = tc->bt;
496 : tcp_bt_sample_t *next, *cur;
497 :
498 5 : cur = bt_get_sample (bt, bt->head);
499 15 : while (cur && seq_leq (cur->max_seq, tc->snd_una))
500 : {
501 10 : next = bt_next_sample (bt, cur);
502 10 : tcp_bt_sample_to_rate_sample (tc, cur, rs);
503 10 : bt_free_sample (bt, cur);
504 10 : cur = next;
505 : }
506 :
507 5 : if (cur && seq_lt (cur->min_seq, tc->snd_una))
508 : {
509 2 : bt_update_sample (bt, cur, tc->snd_una);
510 2 : tcp_bt_sample_to_rate_sample (tc, cur, rs);
511 : }
512 5 : }
513 :
514 : static void
515 2 : tcp_bt_walk_samples_ooo (tcp_connection_t * tc, tcp_rate_sample_t * rs)
516 : {
517 2 : sack_block_t *blks = tc->rcv_opts.sacks, *blk;
518 2 : tcp_byte_tracker_t *bt = tc->bt;
519 : tcp_bt_sample_t *cur, *prev, *next;
520 : int i;
521 :
522 6 : for (i = 0; i < vec_len (blks); i++)
523 : {
524 4 : blk = &blks[i];
525 :
526 : /* Ignore blocks that are already covered by snd_una */
527 4 : if (seq_lt (blk->end, tc->snd_una))
528 0 : continue;
529 :
530 4 : cur = bt_lookup_seq (bt, blk->start);
531 4 : if (!cur)
532 0 : continue;
533 :
534 4 : ASSERT (seq_geq (blk->start, cur->min_seq)
535 : && seq_lt (blk->start, cur->max_seq));
536 :
537 : /* Current should be split. Second part will be consumed */
538 4 : if (PREDICT_FALSE (cur->min_seq != blk->start))
539 : {
540 2 : cur = bt_split_sample (bt, cur, blk->start);
541 2 : prev = bt_prev_sample (bt, cur);
542 : }
543 : else
544 2 : prev = bt_prev_sample (bt, cur);
545 :
546 4 : while (cur && seq_leq (cur->max_seq, blk->end))
547 : {
548 0 : if (!(cur->flags & TCP_BTS_IS_SACKED))
549 : {
550 0 : tcp_bt_sample_to_rate_sample (tc, cur, rs);
551 0 : cur->flags |= TCP_BTS_IS_SACKED;
552 0 : if (prev && (prev->flags & TCP_BTS_IS_SACKED))
553 : {
554 0 : cur = bt_merge_sample (bt, prev, cur);
555 0 : next = bt_next_sample (bt, cur);
556 : }
557 : else
558 : {
559 0 : next = bt_next_sample (bt, cur);
560 0 : if (next && (next->flags & TCP_BTS_IS_SACKED))
561 : {
562 0 : cur = bt_merge_sample (bt, cur, next);
563 0 : next = bt_next_sample (bt, cur);
564 : }
565 : }
566 : }
567 : else
568 0 : next = bt_next_sample (bt, cur);
569 :
570 0 : prev = cur;
571 0 : cur = next;
572 : }
573 :
574 4 : if (cur && seq_lt (cur->min_seq, blk->end))
575 : {
576 4 : tcp_bt_sample_to_rate_sample (tc, cur, rs);
577 4 : prev = bt_prev_sample (bt, cur);
578 : /* Extend previous to include the newly sacked bytes */
579 4 : if (prev && (prev->flags & TCP_BTS_IS_SACKED))
580 : {
581 1 : prev->max_seq = blk->end;
582 1 : bt_update_sample (bt, cur, blk->end);
583 : }
584 : /* Split sample into two. First part is consumed */
585 : else
586 : {
587 3 : next = bt_split_sample (bt, cur, blk->end);
588 3 : cur = bt_prev_sample (bt, next);
589 3 : cur->flags |= TCP_BTS_IS_SACKED;
590 : }
591 : }
592 : }
593 2 : }
594 :
595 : void
596 5 : tcp_bt_sample_delivery_rate (tcp_connection_t * tc, tcp_rate_sample_t * rs)
597 : {
598 : u32 delivered;
599 :
600 5 : if (PREDICT_FALSE (tc->flags & TCP_CONN_FINSNT))
601 0 : return;
602 :
603 5 : tc->lost += tc->sack_sb.last_lost_bytes;
604 :
605 5 : delivered = tc->bytes_acked + tc->sack_sb.last_sacked_bytes;
606 : /* Do not count bytes that were previously sacked again */
607 5 : delivered -= tc->sack_sb.last_bytes_delivered;
608 5 : if (!delivered || tc->bt->head == TCP_BTS_INVALID_INDEX)
609 0 : return;
610 :
611 5 : tc->delivered += delivered;
612 5 : tc->delivered_time = tcp_time_now_us (tc->c_thread_index);
613 :
614 5 : if (tc->app_limited && tc->delivered > tc->app_limited)
615 1 : tc->app_limited = 0;
616 :
617 5 : if (tc->bytes_acked)
618 5 : tcp_bt_walk_samples (tc, rs);
619 :
620 5 : if (tc->sack_sb.last_sacked_bytes)
621 2 : tcp_bt_walk_samples_ooo (tc, rs);
622 :
623 5 : rs->interval_time = clib_max ((tc->delivered_time - rs->prior_time),
624 : rs->interval_time);
625 5 : rs->delivered = tc->delivered - rs->prior_delivered;
626 5 : rs->acked_and_sacked = delivered;
627 5 : rs->last_lost = tc->sack_sb.last_lost_bytes;
628 5 : rs->lost = tc->lost - rs->tx_lost;
629 : }
630 :
631 : void
632 1 : tcp_bt_flush_samples (tcp_connection_t * tc)
633 : {
634 1 : tcp_byte_tracker_t *bt = tc->bt;
635 : tcp_bt_sample_t *bts;
636 1 : u32 *samples = 0, *si;
637 :
638 1 : vec_validate (samples, pool_elts (bt->samples) - 1);
639 1 : vec_reset_length (samples);
640 :
641 : /* *INDENT-OFF* */
642 3 : pool_foreach (bts, bt->samples) {
643 2 : vec_add1 (samples, bts - bt->samples);
644 : }
645 : /* *INDENT-ON* */
646 :
647 3 : vec_foreach (si, samples)
648 : {
649 2 : bts = bt_get_sample (bt, *si);
650 2 : bt_free_sample (bt, bts);
651 : }
652 :
653 1 : vec_free (samples);
654 1 : }
655 :
656 : void
657 1 : tcp_bt_cleanup (tcp_connection_t * tc)
658 : {
659 1 : tcp_byte_tracker_t *bt = tc->bt;
660 :
661 1 : rb_tree_free_nodes (&bt->sample_lookup);
662 1 : pool_free (bt->samples);
663 1 : clib_mem_free (bt);
664 1 : tc->bt = 0;
665 1 : }
666 :
667 : void
668 1 : tcp_bt_init (tcp_connection_t * tc)
669 : {
670 : tcp_byte_tracker_t *bt;
671 :
672 1 : bt = clib_mem_alloc (sizeof (tcp_byte_tracker_t));
673 1 : clib_memset (bt, 0, sizeof (tcp_byte_tracker_t));
674 :
675 1 : rb_tree_init (&bt->sample_lookup);
676 1 : bt->head = bt->tail = TCP_BTS_INVALID_INDEX;
677 1 : tc->bt = bt;
678 1 : }
679 :
680 : u8 *
681 0 : format_tcp_bt_sample (u8 * s, va_list * args)
682 : {
683 0 : tcp_connection_t *tc = va_arg (*args, tcp_connection_t *);
684 0 : tcp_bt_sample_t *bts = va_arg (*args, tcp_bt_sample_t *);
685 0 : f64 now = tcp_time_now_us (tc->c_thread_index);
686 0 : s = format (s, "[%u, %u] d %u dt %.3f txt %.3f ftxt %.3f flags 0x%x",
687 0 : bts->min_seq - tc->iss, bts->max_seq - tc->iss, bts->delivered,
688 0 : now - bts->delivered_time, now - bts->tx_time,
689 0 : now - bts->first_tx_time, bts->flags);
690 0 : return s;
691 : }
692 :
693 : u8 *
694 0 : format_tcp_bt (u8 * s, va_list * args)
695 : {
696 0 : tcp_connection_t *tc = va_arg (*args, tcp_connection_t *);
697 0 : tcp_byte_tracker_t *bt = tc->bt;
698 : tcp_bt_sample_t *bts;
699 :
700 0 : bts = bt_get_sample (bt, bt->head);
701 0 : while (bts)
702 : {
703 0 : s = format (s, "%U\n", format_tcp_bt_sample, tc, bts);
704 0 : bts = bt_next_sample (bt, bts);
705 : }
706 :
707 0 : return s;
708 : }
709 :
710 : /*
711 : * fd.io coding-style-patch-verification: ON
712 : *
713 : * Local Variables:
714 : * eval: (c-set-style "gnu")
715 : * End:
716 : */
|