tcp: Deal with DSACKs, and adjust rescue hole on success.
When a rescue retransmission is successful, rather than inserting new holes to the left of it, adjust the old rescue entry to cover the missed sequence space. Also, as snd_fack may be stale by that point, pull it forward in order to never create a hole left of snd_una/th_ack. Finally, with DSACKs, tcp_sack_doack() may be called with new full ACKs but a DSACK block. Account for this eventuality properly to keep sacked_bytes >= 0. MFC after: 3 days Reviewed By: kbowling, tuexen, #transport Sponsored by: NetApp, Inc. Differential Revision: https://reviews.freebsd.org/D29835
This commit is contained in:
parent
9ca874cf74
commit
a649f1f6fd
@ -497,7 +497,7 @@ static struct sackhole *
|
||||
tcp_sackhole_insert(struct tcpcb *tp, tcp_seq start, tcp_seq end,
|
||||
struct sackhole *after)
|
||||
{
|
||||
struct sackhole *hole, *tail;
|
||||
struct sackhole *hole;
|
||||
|
||||
/* Allocate a new SACK hole. */
|
||||
hole = tcp_sackhole_alloc(tp, start, end);
|
||||
@ -508,15 +508,7 @@ tcp_sackhole_insert(struct tcpcb *tp, tcp_seq start, tcp_seq end,
|
||||
if (after != NULL)
|
||||
TAILQ_INSERT_AFTER(&tp->snd_holes, after, hole, scblink);
|
||||
else
|
||||
/*
|
||||
* With Rescue Retransmission, new holes may need to
|
||||
* be inserted just before the tail.
|
||||
*/
|
||||
if (((tail = TAILQ_LAST_FAST(&tp->snd_holes, sackhole,
|
||||
scblink)) != NULL) && SEQ_LEQ(end, tail->start))
|
||||
TAILQ_INSERT_BEFORE(tail, hole, scblink);
|
||||
else
|
||||
TAILQ_INSERT_TAIL(&tp->snd_holes, hole, scblink);
|
||||
TAILQ_INSERT_TAIL(&tp->snd_holes, hole, scblink);
|
||||
|
||||
/* Update SACK hint. */
|
||||
if (tp->sackhint.nexthole == NULL)
|
||||
@ -573,6 +565,15 @@ tcp_sack_doack(struct tcpcb *tp, struct tcpopt *to, tcp_seq th_ack)
|
||||
left_edge_delta = th_ack - tp->snd_una;
|
||||
sack_blocks[num_sack_blks].start = tp->snd_una;
|
||||
sack_blocks[num_sack_blks++].end = th_ack;
|
||||
/*
|
||||
* Pulling snd_fack forward if we got here
|
||||
* due to DSACK blocks
|
||||
*/
|
||||
if (SEQ_LT(tp->snd_fack, th_ack)) {
|
||||
delivered_data += th_ack - tp->snd_una;
|
||||
tp->snd_fack = th_ack;
|
||||
sack_changed = 1;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Append received valid SACK blocks to sack_blocks[], but only if we
|
||||
@ -642,35 +643,52 @@ tcp_sack_doack(struct tcpcb *tp, struct tcpopt *to, tcp_seq th_ack)
|
||||
tp->sackhint.last_sack_ack = sblkp->end;
|
||||
if (SEQ_LT(tp->snd_fack, sblkp->start)) {
|
||||
/*
|
||||
* The highest SACK block is beyond fack. Append new SACK
|
||||
* hole at the tail. If the second or later highest SACK
|
||||
* blocks are also beyond the current fack, they will be
|
||||
* inserted by way of hole splitting in the while-loop below.
|
||||
* The highest SACK block is beyond fack. First,
|
||||
* check if there was a successful Rescue Retransmission,
|
||||
* and move this hole left. With normal holes, snd_fack
|
||||
* is always to the right of the end.
|
||||
*/
|
||||
temp = tcp_sackhole_insert(tp, tp->snd_fack,sblkp->start,NULL);
|
||||
if (temp != NULL) {
|
||||
if (((temp = TAILQ_LAST(&tp->snd_holes, sackhole_head)) != NULL) &&
|
||||
SEQ_LEQ(tp->snd_fack,temp->end)) {
|
||||
temp->start = SEQ_MAX(tp->snd_fack, SEQ_MAX(tp->snd_una, th_ack));
|
||||
temp->end = sblkp->start;
|
||||
temp->rxmit = temp->start;
|
||||
delivered_data += sblkp->end - sblkp->start;
|
||||
tp->snd_fack = sblkp->end;
|
||||
/* Go to the previous sack block. */
|
||||
sblkp--;
|
||||
sack_changed = 1;
|
||||
} else {
|
||||
/*
|
||||
* We failed to add a new hole based on the current
|
||||
* sack block. Skip over all the sack blocks that
|
||||
* fall completely to the right of snd_fack and
|
||||
* proceed to trim the scoreboard based on the
|
||||
* remaining sack blocks. This also trims the
|
||||
* scoreboard for th_ack (which is sack_blocks[0]).
|
||||
* Append a new SACK hole at the tail. If the
|
||||
* second or later highest SACK blocks are also
|
||||
* beyond the current fack, they will be inserted
|
||||
* by way of hole splitting in the while-loop below.
|
||||
*/
|
||||
while (sblkp >= sack_blocks &&
|
||||
SEQ_LT(tp->snd_fack, sblkp->start))
|
||||
sblkp--;
|
||||
if (sblkp >= sack_blocks &&
|
||||
SEQ_LT(tp->snd_fack, sblkp->end)) {
|
||||
delivered_data += sblkp->end - tp->snd_fack;
|
||||
temp = tcp_sackhole_insert(tp, tp->snd_fack,sblkp->start,NULL);
|
||||
if (temp != NULL) {
|
||||
delivered_data += sblkp->end - sblkp->start;
|
||||
tp->snd_fack = sblkp->end;
|
||||
/* Go to the previous sack block. */
|
||||
sblkp--;
|
||||
sack_changed = 1;
|
||||
} else {
|
||||
/*
|
||||
* We failed to add a new hole based on the current
|
||||
* sack block. Skip over all the sack blocks that
|
||||
* fall completely to the right of snd_fack and
|
||||
* proceed to trim the scoreboard based on the
|
||||
* remaining sack blocks. This also trims the
|
||||
* scoreboard for th_ack (which is sack_blocks[0]).
|
||||
*/
|
||||
while (sblkp >= sack_blocks &&
|
||||
SEQ_LT(tp->snd_fack, sblkp->start))
|
||||
sblkp--;
|
||||
if (sblkp >= sack_blocks &&
|
||||
SEQ_LT(tp->snd_fack, sblkp->end)) {
|
||||
delivered_data += sblkp->end - tp->snd_fack;
|
||||
tp->snd_fack = sblkp->end;
|
||||
sack_changed = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (SEQ_LT(tp->snd_fack, sblkp->end)) {
|
||||
|
Loading…
Reference in New Issue
Block a user