sb->last_sacked_bytes);
TCP_TEST ((sb->last_bytes_delivered == 0), "last bytes delivered %d",
sb->last_bytes_delivered);
+ /* Hole should be split in 2 lost holes that add up to 300 */
TCP_TEST ((sb->lost_bytes == 300), "lost bytes %u", sb->lost_bytes);
+ TCP_TEST ((sb->reorder == 7), "reorder %u", sb->reorder);
/*
* Ack [100 300] in two steps
sb->last_sacked_bytes);
TCP_TEST ((sb->last_bytes_delivered == 0), "last bytes delivered %d",
sb->last_bytes_delivered);
- TCP_TEST ((sb->lost_bytes == 200), "lost bytes %u", sb->lost_bytes);
+ /* No bytes lost because of reorder */
+ TCP_TEST ((sb->lost_bytes == 0), "lost bytes %u", sb->lost_bytes);
+ TCP_TEST ((sb->reorder == 7), "reorder %u", sb->reorder);
TCP_TEST ((!sb->is_reneging), "is not reneging");
/*
* to force the peer to send enough dupacks to start retransmitting as
* per Limited Transmit (RFC3042)
*/
- if (PREDICT_FALSE (tc->rcv_dupacks != 0 || tc->sack_sb.sacked_bytes))
+ if (PREDICT_FALSE (tc->rcv_dupacks || tc->sack_sb.sacked_bytes))
{
- if (tc->limited_transmit != tc->snd_nxt
- && (seq_lt (tc->limited_transmit, tc->snd_nxt - 2 * tc->snd_mss)
- || seq_gt (tc->limited_transmit, tc->snd_nxt)))
+ int snt_limited, n_pkts;
+
+ n_pkts = tcp_opts_sack_permitted (&tc->rcv_opts)
+ ? tc->sack_sb.reorder - 1 : 2;
+
+ if ((seq_lt (tc->limited_transmit, tc->snd_nxt - n_pkts * tc->snd_mss)
+ || seq_gt (tc->limited_transmit, tc->snd_nxt)))
tc->limited_transmit = tc->snd_nxt;
ASSERT (seq_leq (tc->limited_transmit, tc->snd_nxt));
- int snt_limited = tc->snd_nxt - tc->limited_transmit;
- snd_space = clib_max ((int) 2 * tc->snd_mss - snt_limited, 0);
+ snt_limited = tc->snd_nxt - tc->limited_transmit;
+ snd_space = clib_max (n_pkts * tc->snd_mss - snt_limited, 0);
}
return tcp_round_snd_space (tc, snd_space);
}
" rxt_sacked %u\n",
sb->sacked_bytes, sb->last_sacked_bytes, sb->lost_bytes,
sb->last_lost_bytes, sb->rxt_sacked);
- s = format (s, "%Ulast_delivered %u high_sacked %u is_reneging %u\n",
+ s = format (s, "%Ulast_delivered %u high_sacked %u is_reneging %u",
format_white_space, indent, sb->last_bytes_delivered,
sb->high_sacked - tc->iss, sb->is_reneging);
+ s = format (s, " reorder %u\n", sb->reorder);
s = format (s, "%Ucur_rxt_hole %u high_rxt %u rescue_rxt %u",
format_white_space, indent, sb->cur_rxt_hole,
sb->high_rxt - tc->iss, sb->rescue_rxt - tc->iss);
}
always_inline void
-scoreboard_update_sacked_rxt (sack_scoreboard_t * sb, u32 start, u32 end,
- u8 has_rxt)
+scoreboard_update_sacked (sack_scoreboard_t * sb, u32 start, u32 end,
+ u8 has_rxt, u16 snd_mss)
{
- if (!has_rxt || seq_geq (start, sb->high_rxt))
+ if (!has_rxt)
+ {
+ /* Sequence was not retransmitted but it was sacked. Estimate reorder
+ * only if not in congestion recovery */
+ if (seq_lt (start, sb->high_sacked))
+ {
+ u32 reord = (sb->high_sacked - start + snd_mss - 1) / snd_mss;
+ reord = clib_min (reord, TCP_MAX_SACK_REORDER);
+ sb->reorder = clib_max (sb->reorder, reord);
+ }
+ return;
+ }
+
+ if (seq_geq (start, sb->high_rxt))
return;
sb->rxt_sacked +=
blks = 1;
}
- while (sacked < (TCP_DUPACK_THRESHOLD - 1) * snd_mss
- && blks < TCP_DUPACK_THRESHOLD)
+ /* As per RFC 6675 a sequence number is lost if:
+ * DupThresh discontiguous SACKed sequences have arrived above
+ * 'SeqNum' or more than (DupThresh - 1) * SMSS bytes with sequence
+ * numbers greater than 'SeqNum' have been SACKed.
+ * To avoid spurious retransmits, use reordering estimate instead of
+ * DupThresh to detect loss.
+ */
+ while (sacked <= (sb->reorder - 1) * snd_mss && blks < sb->reorder)
{
if (right->is_lost)
sb->lost_bytes += scoreboard_hole_bytes (right);
sb->head = TCP_INVALID_SACK_HOLE_INDEX;
sb->tail = TCP_INVALID_SACK_HOLE_INDEX;
sb->cur_rxt_hole = TCP_INVALID_SACK_HOLE_INDEX;
+ sb->reorder = TCP_DUPACK_THRESHOLD;
}
void
sb->last_lost_bytes = 0;
sb->cur_rxt_hole = TCP_INVALID_SACK_HOLE_INDEX;
sb->is_reneging = 0;
+ sb->reorder = TCP_DUPACK_THRESHOLD;
}
void
sack_scoreboard_hole_t *hole, *next_hole;
sack_scoreboard_t *sb = &tc->sack_sb;
sack_block_t *blk, *rcv_sacks;
- u32 blk_index = 0, i, j;
+ u32 blk_index = 0, i, j, high_sacked;
u8 has_rxt;
sb->last_sacked_bytes = 0;
hole = scoreboard_insert_hole (sb, TCP_INVALID_SACK_HOLE_INDEX,
tc->snd_una, tc->snd_nxt);
sb->tail = scoreboard_hole_index (sb, hole);
+ sb->high_sacked = tc->snd_una;
}
- sb->high_sacked = rcv_sacks[vec_len (rcv_sacks) - 1].end;
+ high_sacked = rcv_sacks[vec_len (rcv_sacks) - 1].end;
}
else
{
tc->snd_nxt);
}
}
-
/* Keep track of max byte sacked for when the last hole
* is acked */
- sb->high_sacked = seq_max (rcv_sacks[vec_len (rcv_sacks) - 1].end,
- sb->high_sacked);
+ high_sacked = seq_max (rcv_sacks[vec_len (rcv_sacks) - 1].end,
+ sb->high_sacked);
}
/* Walk the holes with the SACK blocks */
/* If covered by ack, compute delivered bytes */
if (blk->end == ack)
{
- u32 sacked = next_hole ? next_hole->start : sb->high_sacked;
+ u32 sacked = next_hole ? next_hole->start :
+ seq_max (sb->high_sacked, hole->end);
if (PREDICT_FALSE (seq_lt (ack, sacked)))
{
sb->last_bytes_delivered += ack - hole->end;
sb->is_reneging = 0;
}
}
- scoreboard_update_sacked_rxt (sb, hole->start, hole->end,
- has_rxt);
+ scoreboard_update_sacked (sb, hole->start, hole->end,
+ has_rxt, tc->snd_mss);
scoreboard_remove_hole (sb, hole);
hole = next_hole;
}
{
if (seq_gt (blk->end, hole->start))
{
- scoreboard_update_sacked_rxt (sb, hole->start, blk->end,
- has_rxt);
+ scoreboard_update_sacked (sb, hole->start, blk->end,
+ has_rxt, tc->snd_mss);
hole->start = blk->end;
}
blk_index++;
/* Pool might've moved */
hole = scoreboard_get_hole (sb, hole_index);
hole->end = blk->start;
+ next_hole->is_lost = hole->is_lost;
- scoreboard_update_sacked_rxt (sb, blk->start, blk->end,
- has_rxt);
+ scoreboard_update_sacked (sb, blk->start, blk->end,
+ has_rxt, tc->snd_mss);
blk_index++;
ASSERT (hole->next == scoreboard_hole_index (sb, next_hole));
}
else if (seq_lt (blk->start, hole->end))
{
- scoreboard_update_sacked_rxt (sb, blk->start, hole->end,
- has_rxt);
+ scoreboard_update_sacked (sb, blk->start, hole->end,
+ has_rxt, tc->snd_mss);
hole->end = blk->start;
}
hole = scoreboard_next_hole (sb, hole);
}
}
+ sb->high_sacked = high_sacked;
scoreboard_update_bytes (sb, ack, tc->snd_mss);
ASSERT (sb->last_sacked_bytes <= sb->sacked_bytes || tcp_in_recovery (tc));