LCOV - code coverage report
Current view: top level - vnet/tcp - tcp_bt.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 254 364 69.8 %
Date: 2023-10-26 01:39:38 Functions: 22 26 84.6 %

          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             :  */

Generated by: LCOV version 1.14