FD.io VPP  v21.01.1
Vector Packet Processing
tcp_sack.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2020 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 
16 #include <vnet/tcp/tcp_sack.h>
17 
18 static void
20 {
21  sack_scoreboard_hole_t *next, *prev;
22 
23  if (hole->next != TCP_INVALID_SACK_HOLE_INDEX)
24  {
25  next = pool_elt_at_index (sb->holes, hole->next);
26  next->prev = hole->prev;
27  }
28  else
29  {
30  sb->tail = hole->prev;
31  }
32 
33  if (hole->prev != TCP_INVALID_SACK_HOLE_INDEX)
34  {
35  prev = pool_elt_at_index (sb->holes, hole->prev);
36  prev->next = hole->next;
37  }
38  else
39  {
40  sb->head = hole->next;
41  }
42 
43  if (scoreboard_hole_index (sb, hole) == sb->cur_rxt_hole)
44  sb->cur_rxt_hole = TCP_INVALID_SACK_HOLE_INDEX;
45 
46  /* Poison the entry */
47  if (CLIB_DEBUG > 0)
48  clib_memset (hole, 0xfe, sizeof (*hole));
49 
50  pool_put (sb->holes, hole);
51 }
52 
55  u32 start, u32 end)
56 {
57  sack_scoreboard_hole_t *hole, *next, *prev;
58  u32 hole_index;
59 
60  pool_get (sb->holes, hole);
61  clib_memset (hole, 0, sizeof (*hole));
62 
63  hole->start = start;
64  hole->end = end;
65  hole_index = scoreboard_hole_index (sb, hole);
66 
67  prev = scoreboard_get_hole (sb, prev_index);
68  if (prev)
69  {
70  hole->prev = prev_index;
71  hole->next = prev->next;
72 
73  if ((next = scoreboard_next_hole (sb, hole)))
74  next->prev = hole_index;
75  else
76  sb->tail = hole_index;
77 
78  prev->next = hole_index;
79  }
80  else
81  {
82  sb->head = hole_index;
83  hole->prev = TCP_INVALID_SACK_HOLE_INDEX;
84  hole->next = TCP_INVALID_SACK_HOLE_INDEX;
85  }
86 
87  return hole;
88 }
89 
90 always_inline void
92  u8 has_rxt, u16 snd_mss)
93 {
94  if (!has_rxt)
95  {
96  /* Sequence was not retransmitted but it was sacked. Estimate reorder
97  * only if not in congestion recovery */
98  if (seq_lt (start, sb->high_sacked))
99  {
100  u32 reord = (sb->high_sacked - start + snd_mss - 1) / snd_mss;
101  reord = clib_min (reord, TCP_MAX_SACK_REORDER);
102  sb->reorder = clib_max (sb->reorder, reord);
103  }
104  return;
105  }
106 
107  if (seq_geq (start, sb->high_rxt))
108  return;
109 
110  sb->rxt_sacked +=
111  seq_lt (end, sb->high_rxt) ? (end - start) : (sb->high_rxt - start);
112 }
113 
114 always_inline void
116 {
118  u32 sacked = 0, blks = 0, old_sacked;
119 
120  old_sacked = sb->sacked_bytes;
121 
122  sb->last_lost_bytes = 0;
123  sb->lost_bytes = 0;
124  sb->sacked_bytes = 0;
125 
126  right = scoreboard_last_hole (sb);
127  if (!right)
128  {
129  sb->sacked_bytes = sb->high_sacked - ack;
130  sb->last_sacked_bytes = sb->sacked_bytes
131  - (old_sacked - sb->last_bytes_delivered);
132  return;
133  }
134 
135  if (seq_gt (sb->high_sacked, right->end))
136  {
137  sacked = sb->high_sacked - right->end;
138  blks = 1;
139  }
140 
141  /* As per RFC 6675 a sequence number is lost if:
142  * DupThresh discontiguous SACKed sequences have arrived above
143  * 'SeqNum' or more than (DupThresh - 1) * SMSS bytes with sequence
144  * numbers greater than 'SeqNum' have been SACKed.
145  * To avoid spurious retransmits, use reordering estimate instead of
146  * DupThresh to detect loss.
147  */
148  while (sacked <= (sb->reorder - 1) * snd_mss && blks < sb->reorder)
149  {
150  if (right->is_lost)
151  sb->lost_bytes += scoreboard_hole_bytes (right);
152 
153  left = scoreboard_prev_hole (sb, right);
154  if (!left)
155  {
156  ASSERT (right->start == ack || sb->is_reneging);
157  sacked += right->start - ack;
158  right = 0;
159  break;
160  }
161 
162  sacked += right->start - left->end;
163  blks++;
164  right = left;
165  }
166 
167  /* right is first lost */
168  while (right)
169  {
170  sb->lost_bytes += scoreboard_hole_bytes (right);
171  sb->last_lost_bytes += right->is_lost ? 0 : (right->end - right->start);
172  right->is_lost = 1;
173  left = scoreboard_prev_hole (sb, right);
174  if (!left)
175  {
176  ASSERT (right->start == ack || sb->is_reneging);
177  sacked += right->start - ack;
178  break;
179  }
180  sacked += right->start - left->end;
181  right = left;
182  }
183 
184  sb->sacked_bytes = sacked;
185  sb->last_sacked_bytes = sacked - (old_sacked - sb->last_bytes_delivered);
186 }
187 
188 /**
189  * Figure out the next hole to retransmit
190  *
191  * Follows logic proposed in RFC6675 Sec. 4, NextSeg()
192  */
195  sack_scoreboard_hole_t * start,
196  u8 have_unsent, u8 * can_rescue, u8 * snd_limited)
197 {
198  sack_scoreboard_hole_t *hole = 0;
199 
200  hole = start ? start : scoreboard_first_hole (sb);
201  while (hole && seq_leq (hole->end, sb->high_rxt) && hole->is_lost)
202  hole = scoreboard_next_hole (sb, hole);
203 
204  /* Nothing, return */
205  if (!hole)
206  {
207  sb->cur_rxt_hole = TCP_INVALID_SACK_HOLE_INDEX;
208  return 0;
209  }
210 
211  /* Rule (1): if higher than rxt, less than high_sacked and lost */
212  if (hole->is_lost && seq_lt (hole->start, sb->high_sacked))
213  {
214  sb->cur_rxt_hole = scoreboard_hole_index (sb, hole);
215  }
216  else
217  {
218  /* Rule (2): available unsent data */
219  if (have_unsent)
220  {
221  sb->cur_rxt_hole = TCP_INVALID_SACK_HOLE_INDEX;
222  return 0;
223  }
224  /* Rule (3): if hole not lost */
225  else if (seq_lt (hole->start, sb->high_sacked))
226  {
227  /* And we didn't already retransmit it */
228  if (seq_leq (hole->end, sb->high_rxt))
229  {
230  sb->cur_rxt_hole = TCP_INVALID_SACK_HOLE_INDEX;
231  return 0;
232  }
233  *snd_limited = 0;
234  sb->cur_rxt_hole = scoreboard_hole_index (sb, hole);
235  }
236  /* Rule (4): if hole beyond high_sacked */
237  else
238  {
239  ASSERT (seq_geq (hole->start, sb->high_sacked));
240  *snd_limited = 1;
241  *can_rescue = 1;
242  /* HighRxt MUST NOT be updated */
243  return 0;
244  }
245  }
246 
247  if (hole && seq_lt (sb->high_rxt, hole->start))
248  sb->high_rxt = hole->start;
249 
250  return hole;
251 }
252 
253 void
255 {
257  hole = scoreboard_first_hole (sb);
258  if (hole)
259  {
260  snd_una = seq_gt (snd_una, hole->start) ? snd_una : hole->start;
261  sb->cur_rxt_hole = sb->head;
262  }
263  sb->high_rxt = snd_una;
264  sb->rescue_rxt = snd_una - 1;
265 }
266 
267 void
269 {
270  sb->head = TCP_INVALID_SACK_HOLE_INDEX;
271  sb->tail = TCP_INVALID_SACK_HOLE_INDEX;
272  sb->cur_rxt_hole = TCP_INVALID_SACK_HOLE_INDEX;
273  sb->reorder = TCP_DUPACK_THRESHOLD;
274 }
275 
276 void
278 {
280  while ((hole = scoreboard_first_hole (sb)))
281  {
282  scoreboard_remove_hole (sb, hole);
283  }
284  ASSERT (sb->head == sb->tail && sb->head == TCP_INVALID_SACK_HOLE_INDEX);
285  ASSERT (pool_elts (sb->holes) == 0);
286  sb->sacked_bytes = 0;
287  sb->last_sacked_bytes = 0;
288  sb->last_bytes_delivered = 0;
289  sb->lost_bytes = 0;
290  sb->last_lost_bytes = 0;
291  sb->cur_rxt_hole = TCP_INVALID_SACK_HOLE_INDEX;
292  sb->is_reneging = 0;
293  sb->reorder = TCP_DUPACK_THRESHOLD;
294 }
295 
296 void
298 {
299  sack_scoreboard_hole_t *last_hole;
300 
301  scoreboard_clear (sb);
303  start, end);
304  last_hole->is_lost = 1;
305  sb->tail = scoreboard_hole_index (sb, last_hole);
306  sb->high_sacked = start;
307  scoreboard_init_rxt (sb, start);
308 }
309 
310 /**
311  * Test that scoreboard is sane after recovery
312  *
313  * Returns 1 if scoreboard is empty or if first hole beyond
314  * snd_una.
315  */
316 u8
318 {
320  hole = scoreboard_first_hole (&tc->sack_sb);
321  return (!hole || (seq_geq (hole->start, tc->snd_una)
322  && seq_lt (hole->end, tc->snd_nxt)));
323 }
324 
325 void
327 {
328  sack_scoreboard_hole_t *hole, *next_hole;
329  sack_scoreboard_t *sb = &tc->sack_sb;
330  sack_block_t *blk, *rcv_sacks;
331  u32 blk_index = 0, i, j, high_sacked;
332  u8 has_rxt;
333 
334  sb->last_sacked_bytes = 0;
335  sb->last_bytes_delivered = 0;
336  sb->rxt_sacked = 0;
337 
338  if (!tcp_opts_sack (&tc->rcv_opts) && !sb->sacked_bytes
339  && sb->head == TCP_INVALID_SACK_HOLE_INDEX)
340  return;
341 
342  has_rxt = tcp_in_cong_recovery (tc);
343 
344  /* Remove invalid blocks */
345  blk = tc->rcv_opts.sacks;
346  while (blk < vec_end (tc->rcv_opts.sacks))
347  {
348  if (seq_lt (blk->start, blk->end)
349  && seq_gt (blk->start, tc->snd_una)
350  && seq_gt (blk->start, ack)
351  && seq_lt (blk->start, tc->snd_nxt)
352  && seq_leq (blk->end, tc->snd_nxt))
353  {
354  blk++;
355  continue;
356  }
357  vec_del1 (tc->rcv_opts.sacks, blk - tc->rcv_opts.sacks);
358  }
359 
360  /* Add block for cumulative ack */
361  if (seq_gt (ack, tc->snd_una))
362  {
363  vec_add2 (tc->rcv_opts.sacks, blk, 1);
364  blk->start = tc->snd_una;
365  blk->end = ack;
366  }
367 
368  if (vec_len (tc->rcv_opts.sacks) == 0)
369  return;
370 
371  tcp_scoreboard_trace_add (tc, ack);
372 
373  /* Make sure blocks are ordered */
374  rcv_sacks = tc->rcv_opts.sacks;
375  for (i = 0; i < vec_len (rcv_sacks); i++)
376  for (j = i + 1; j < vec_len (rcv_sacks); j++)
377  if (seq_lt (rcv_sacks[j].start, rcv_sacks[i].start))
378  {
379  sack_block_t tmp = rcv_sacks[i];
380  rcv_sacks[i] = rcv_sacks[j];
381  rcv_sacks[j] = tmp;
382  }
383 
384  if (sb->head == TCP_INVALID_SACK_HOLE_INDEX)
385  {
386  /* Handle reneging as a special case */
387  if (PREDICT_FALSE (sb->is_reneging))
388  {
389  /* No holes, only sacked bytes */
390  if (seq_leq (tc->snd_nxt, sb->high_sacked))
391  {
392  /* No progress made so return */
393  if (seq_leq (ack, tc->snd_una))
394  return;
395 
396  /* Update sacked bytes delivered and return */
397  sb->last_bytes_delivered = ack - tc->snd_una;
398  sb->sacked_bytes -= sb->last_bytes_delivered;
399  sb->is_reneging = seq_lt (ack, sb->high_sacked);
400  return;
401  }
402 
403  /* New hole above high sacked. Add it and process normally */
405  sb->high_sacked, tc->snd_nxt);
406  sb->tail = scoreboard_hole_index (sb, hole);
407  }
408  /* Not reneging and no holes. Insert the first that covers all
409  * outstanding bytes */
410  else
411  {
413  tc->snd_una, tc->snd_nxt);
414  sb->tail = scoreboard_hole_index (sb, hole);
415  sb->high_sacked = tc->snd_una;
416  }
417  high_sacked = rcv_sacks[vec_len (rcv_sacks) - 1].end;
418  }
419  else
420  {
421  /* If we have holes but snd_nxt is beyond the last hole, update
422  * last hole end or add new hole after high sacked */
423  hole = scoreboard_last_hole (sb);
424  if (seq_gt (tc->snd_nxt, hole->end))
425  {
426  if (seq_geq (hole->start, sb->high_sacked))
427  {
428  hole->end = tc->snd_nxt;
429  }
430  /* New hole after high sacked block */
431  else if (seq_lt (sb->high_sacked, tc->snd_nxt))
432  {
433  scoreboard_insert_hole (sb, sb->tail, sb->high_sacked,
434  tc->snd_nxt);
435  }
436  }
437  /* Keep track of max byte sacked for when the last hole
438  * is acked */
439  high_sacked = seq_max (rcv_sacks[vec_len (rcv_sacks) - 1].end,
440  sb->high_sacked);
441  }
442 
443  /* Walk the holes with the SACK blocks */
444  hole = pool_elt_at_index (sb->holes, sb->head);
445 
446  if (PREDICT_FALSE (sb->is_reneging))
447  {
448  sb->last_bytes_delivered += clib_min (hole->start - tc->snd_una,
449  ack - tc->snd_una);
450  sb->is_reneging = seq_lt (ack, hole->start);
451  }
452 
453  while (hole && blk_index < vec_len (rcv_sacks))
454  {
455  blk = &rcv_sacks[blk_index];
456  if (seq_leq (blk->start, hole->start))
457  {
458  /* Block covers hole. Remove hole */
459  if (seq_geq (blk->end, hole->end))
460  {
461  next_hole = scoreboard_next_hole (sb, hole);
462 
463  /* If covered by ack, compute delivered bytes */
464  if (blk->end == ack)
465  {
466  u32 sacked = next_hole ? next_hole->start :
467  seq_max (sb->high_sacked, hole->end);
468  if (PREDICT_FALSE (seq_lt (ack, sacked)))
469  {
470  sb->last_bytes_delivered += ack - hole->end;
471  sb->is_reneging = 1;
472  }
473  else
474  {
475  sb->last_bytes_delivered += sacked - hole->end;
476  sb->is_reneging = 0;
477  }
478  }
479  scoreboard_update_sacked (sb, hole->start, hole->end,
480  has_rxt, tc->snd_mss);
481  scoreboard_remove_hole (sb, hole);
482  hole = next_hole;
483  }
484  /* Partial 'head' overlap */
485  else
486  {
487  if (seq_gt (blk->end, hole->start))
488  {
489  scoreboard_update_sacked (sb, hole->start, blk->end,
490  has_rxt, tc->snd_mss);
491  hole->start = blk->end;
492  }
493  blk_index++;
494  }
495  }
496  else
497  {
498  /* Hole must be split */
499  if (seq_lt (blk->end, hole->end))
500  {
501  u32 hole_index = scoreboard_hole_index (sb, hole);
502  next_hole = scoreboard_insert_hole (sb, hole_index, blk->end,
503  hole->end);
504  /* Pool might've moved */
505  hole = scoreboard_get_hole (sb, hole_index);
506  hole->end = blk->start;
507  next_hole->is_lost = hole->is_lost;
508 
509  scoreboard_update_sacked (sb, blk->start, blk->end,
510  has_rxt, tc->snd_mss);
511 
512  blk_index++;
513  ASSERT (hole->next == scoreboard_hole_index (sb, next_hole));
514  }
515  else if (seq_lt (blk->start, hole->end))
516  {
517  scoreboard_update_sacked (sb, blk->start, hole->end,
518  has_rxt, tc->snd_mss);
519  hole->end = blk->start;
520  }
521  hole = scoreboard_next_hole (sb, hole);
522  }
523  }
524 
525  sb->high_sacked = high_sacked;
526  scoreboard_update_bytes (sb, ack, tc->snd_mss);
527 
528  ASSERT (sb->last_sacked_bytes <= sb->sacked_bytes || tcp_in_recovery (tc));
529  ASSERT (sb->sacked_bytes == 0 || tcp_in_recovery (tc)
530  || sb->sacked_bytes <= tc->snd_nxt - seq_max (tc->snd_una, ack));
531  ASSERT (sb->last_sacked_bytes + sb->lost_bytes <= tc->snd_nxt
532  - seq_max (tc->snd_una, ack) || tcp_in_recovery (tc));
534  || sb->is_reneging || sb->holes[sb->head].start == ack);
535  ASSERT (sb->last_lost_bytes <= sb->lost_bytes);
536  ASSERT ((ack - tc->snd_una) + sb->last_sacked_bytes
537  - sb->last_bytes_delivered >= sb->rxt_sacked);
538  ASSERT ((ack - tc->snd_una) >= tc->sack_sb.last_bytes_delivered
539  || (tc->flags & TCP_CONN_FINSNT));
540 
541  TCP_EVT (TCP_EVT_CC_SCOREBOARD, tc);
542 }
543 
544 static u8
546 {
547  int i;
548  for (i = 1; i < vec_len (sacks); i++)
549  {
550  if (sacks[i - 1].end == sacks[i].start)
551  return 0;
552  }
553  return 1;
554 }
555 
556 /**
557  * Build SACK list as per RFC2018.
558  *
559  * Makes sure the first block contains the segment that generated the current
560  * ACK and the following ones are the ones most recently reported in SACK
561  * blocks.
562  *
563  * @param tc TCP connection for which the SACK list is updated
564  * @param start Start sequence number of the newest SACK block
565  * @param end End sequence of the newest SACK block
566  */
567 void
569 {
570  sack_block_t *new_list = tc->snd_sacks_fl, *block = 0;
571  int i;
572 
573  /* If the first segment is ooo add it to the list. Last write might've moved
574  * rcv_nxt over the first segment. */
575  if (seq_lt (tc->rcv_nxt, start))
576  {
577  vec_add2 (new_list, block, 1);
578  block->start = start;
579  block->end = end;
580  }
581 
582  /* Find the blocks still worth keeping. */
583  for (i = 0; i < vec_len (tc->snd_sacks); i++)
584  {
585  /* Discard if rcv_nxt advanced beyond current block */
586  if (seq_leq (tc->snd_sacks[i].start, tc->rcv_nxt))
587  continue;
588 
589  /* Merge or drop if segment overlapped by the new segment */
590  if (block && (seq_geq (tc->snd_sacks[i].end, new_list[0].start)
591  && seq_leq (tc->snd_sacks[i].start, new_list[0].end)))
592  {
593  if (seq_lt (tc->snd_sacks[i].start, new_list[0].start))
594  new_list[0].start = tc->snd_sacks[i].start;
595  if (seq_lt (new_list[0].end, tc->snd_sacks[i].end))
596  new_list[0].end = tc->snd_sacks[i].end;
597  continue;
598  }
599 
600  /* Save to new SACK list if we have space. */
601  if (vec_len (new_list) < TCP_MAX_SACK_BLOCKS)
602  vec_add1 (new_list, tc->snd_sacks[i]);
603  }
604 
605  ASSERT (vec_len (new_list) <= TCP_MAX_SACK_BLOCKS);
606 
607  /* Replace old vector with new one */
608  vec_reset_length (tc->snd_sacks);
609  tc->snd_sacks_fl = tc->snd_sacks;
610  tc->snd_sacks = new_list;
611 
612  /* Segments should not 'touch' */
613  ASSERT (tcp_sack_vector_is_sane (tc->snd_sacks));
614 }
615 
616 u32
618 {
619  u32 bytes = 0, i;
620  for (i = 0; i < vec_len (tc->snd_sacks); i++)
621  bytes += tc->snd_sacks[i].end - tc->snd_sacks[i].start;
622  return bytes;
623 }
624 
625 /*
626  * fd.io coding-style-patch-verification: ON
627  *
628  * Local Variables:
629  * eval: (c-set-style "gnu")
630  * End:
631  */
#define TCP_MAX_SACK_REORDER
Definition: tcp_types.h:153
#define clib_min(x, y)
Definition: clib.h:328
struct _sack_block sack_block_t
static u32 scoreboard_hole_bytes(sack_scoreboard_hole_t *hole)
Definition: tcp_sack.h:29
#define TCP_INVALID_SACK_HOLE_INDEX
Definition: tcp_types.h:152
clib_memset(h->entries, 0, sizeof(h->entries[0]) *entries)
static sack_scoreboard_hole_t * scoreboard_get_hole(sack_scoreboard_t *sb, u32 index)
Definition: tcp_sack.h:35
struct _tcp_connection tcp_connection_t
#define vec_add1(V, E)
Add 1 element to end of vector (unspecified alignment).
Definition: vec.h:592
#define vec_add2(V, P, N)
Add N elements to end of vector V, return pointer to new elements in P.
Definition: vec.h:630
#define tcp_opts_sack(_to)
Definition: tcp_packet.h:158
static void scoreboard_update_sacked(sack_scoreboard_t *sb, u32 start, u32 end, u8 has_rxt, u16 snd_mss)
Definition: tcp_sack.c:91
#define pool_get(P, E)
Allocate an object E from a pool P (unspecified alignment).
Definition: pool.h:251
struct _sack_scoreboard sack_scoreboard_t
#define tcp_in_cong_recovery(tc)
Definition: tcp_types.h:426
unsigned char u8
Definition: types.h:56
#define vec_reset_length(v)
Reset vector length to zero NULL-pointer tolerant.
u32 tcp_sack_list_bytes(tcp_connection_t *tc)
Definition: tcp_sack.c:617
#define seq_leq(_s1, _s2)
Definition: tcp_packet.h:179
unsigned int u32
Definition: types.h:88
#define vec_end(v)
End (last data address) of vector.
static sack_scoreboard_hole_t * scoreboard_first_hole(sack_scoreboard_t *sb)
Definition: tcp_sack.h:59
static sack_scoreboard_hole_t * scoreboard_prev_hole(sack_scoreboard_t *sb, sack_scoreboard_hole_t *hole)
Definition: tcp_sack.h:51
static void scoreboard_update_bytes(sack_scoreboard_t *sb, u32 ack, u32 snd_mss)
Definition: tcp_sack.c:115
void scoreboard_clear_reneging(sack_scoreboard_t *sb, u32 start, u32 end)
Definition: tcp_sack.c:297
static sack_scoreboard_hole_t * scoreboard_next_hole(sack_scoreboard_t *sb, sack_scoreboard_hole_t *hole)
Definition: tcp_sack.h:43
void tcp_update_sack_list(tcp_connection_t *tc, u32 start, u32 end)
Build SACK list as per RFC2018.
Definition: tcp_sack.c:568
#define pool_elt_at_index(p, i)
Returns pointer to element at given index.
Definition: pool.h:546
static u8 tcp_sack_vector_is_sane(sack_block_t *sacks)
Definition: tcp_sack.c:545
unsigned short u16
Definition: types.h:57
#define TCP_DUPACK_THRESHOLD
Definition: tcp_types.h:37
static void scoreboard_remove_hole(sack_scoreboard_t *sb, sack_scoreboard_hole_t *hole)
Definition: tcp_sack.c:19
#define pool_put(P, E)
Free an object E in pool P.
Definition: pool.h:301
#define PREDICT_FALSE(x)
Definition: clib.h:121
#define always_inline
Definition: ipsec.h:28
#define vec_del1(v, i)
Delete the element at index I.
Definition: vec.h:875
static u32 scoreboard_hole_index(sack_scoreboard_t *sb, sack_scoreboard_hole_t *hole)
Definition: tcp_sack.h:22
static sack_scoreboard_hole_t * scoreboard_last_hole(sack_scoreboard_t *sb)
Definition: tcp_sack.h:67
#define seq_max(_s1, _s2)
Definition: tcp_packet.h:182
void scoreboard_init(sack_scoreboard_t *sb)
Definition: tcp_sack.c:268
sll srl srl sll sra u16x4 i
Definition: vector_sse42.h:317
#define tcp_scoreboard_trace_add(_tc, _ack)
Definition: tcp_sack.h:96
#define TCP_MAX_SACK_BLOCKS
Max number of SACK blocks stored.
Definition: tcp_types.h:151
#define tcp_in_recovery(tc)
Definition: tcp_types.h:417
#define seq_geq(_s1, _s2)
Definition: tcp_packet.h:181
vslo right
#define ASSERT(truth)
#define seq_gt(_s1, _s2)
Definition: tcp_packet.h:180
struct _sack_scoreboard_hole sack_scoreboard_hole_t
#define clib_max(x, y)
Definition: clib.h:321
u8 tcp_scoreboard_is_sane_post_recovery(tcp_connection_t *tc)
Test that scoreboard is sane after recovery.
Definition: tcp_sack.c:317
void scoreboard_clear(sack_scoreboard_t *sb)
Definition: tcp_sack.c:277
#define vec_len(v)
Number of elements in vector (rvalue-only, NULL tolerant)
static sack_scoreboard_hole_t * scoreboard_insert_hole(sack_scoreboard_t *sb, u32 prev_index, u32 start, u32 end)
Definition: tcp_sack.c:54
sack_scoreboard_hole_t * scoreboard_next_rxt_hole(sack_scoreboard_t *sb, sack_scoreboard_hole_t *start, u8 have_unsent, u8 *can_rescue, u8 *snd_limited)
Figure out the next hole to retransmit.
Definition: tcp_sack.c:194
left
void scoreboard_init_rxt(sack_scoreboard_t *sb, u32 snd_una)
Definition: tcp_sack.c:254
f64 end
end of the time range
Definition: mactime.api:44
#define seq_lt(_s1, _s2)
Definition: tcp_packet.h:178
void tcp_rcv_sacks(tcp_connection_t *tc, u32 ack)
Definition: tcp_sack.c:326
#define TCP_EVT(_evt, _args...)
Definition: tcp_debug.h:145
static uword pool_elts(void *v)
Number of active elements in a pool.
Definition: pool.h:127