Changeset View
Changeset View
Standalone View
Standalone View
sys/netinet/tcp_sack.c
Show First 20 Lines • Show All 544 Lines • ▼ Show 20 Lines | tcp_sackhole_remove(struct tcpcb *tp, struct sackhole *hole) | ||||
/* Free this SACK hole. */ | /* Free this SACK hole. */ | ||||
tcp_sackhole_free(tp, hole); | tcp_sackhole_free(tp, hole); | ||||
} | } | ||||
/* | /* | ||||
* Process cumulative ACK and the TCP SACK option to update the scoreboard. | * Process cumulative ACK and the TCP SACK option to update the scoreboard. | ||||
* tp->snd_holes is an ordered list of holes (oldest to newest, in terms of | * tp->snd_holes is an ordered list of holes (oldest to newest, in terms of | ||||
* the sequence space). | * the sequence space). | ||||
* Returns 1 if incoming ACK has previously unknown SACK information, | * Returns SACK_NEWLOSS if incoming ACK indicates ongoing loss (hole split, new hole), | ||||
* 0 otherwise. | * SACK_CHANGE if incoming ACK has previously unknown SACK information, | ||||
* SACK_NOCHANGE otherwise. | |||||
*/ | */ | ||||
int | int | ||||
tcp_sack_doack(struct tcpcb *tp, struct tcpopt *to, tcp_seq th_ack) | tcp_sack_doack(struct tcpcb *tp, struct tcpopt *to, tcp_seq th_ack) | ||||
{ | { | ||||
struct sackhole *cur, *temp; | struct sackhole *cur, *temp; | ||||
struct sackblk sack, sack_blocks[TCP_MAX_SACK + 1], *sblkp; | struct sackblk sack, sack_blocks[TCP_MAX_SACK + 1], *sblkp; | ||||
int i, j, num_sack_blks, sack_changed; | int i, j, num_sack_blks, sack_changed; | ||||
int delivered_data, left_edge_delta; | int delivered_data, left_edge_delta; | ||||
INP_WLOCK_ASSERT(tp->t_inpcb); | INP_WLOCK_ASSERT(tp->t_inpcb); | ||||
num_sack_blks = 0; | num_sack_blks = 0; | ||||
sack_changed = 0; | sack_changed = SACK_NOCHANGE; | ||||
delivered_data = 0; | delivered_data = 0; | ||||
left_edge_delta = 0; | left_edge_delta = 0; | ||||
/* | /* | ||||
* If SND.UNA will be advanced by SEG.ACK, and if SACK holes exist, | * If SND.UNA will be advanced by SEG.ACK, and if SACK holes exist, | ||||
* treat [SND.UNA, SEG.ACK) as if it is a SACK block. | * treat [SND.UNA, SEG.ACK) as if it is a SACK block. | ||||
* Account changes to SND.UNA always in delivered data. | * Account changes to SND.UNA always in delivered data. | ||||
*/ | */ | ||||
if (SEQ_LT(tp->snd_una, th_ack) && !TAILQ_EMPTY(&tp->snd_holes)) { | if (SEQ_LT(tp->snd_una, th_ack) && !TAILQ_EMPTY(&tp->snd_holes)) { | ||||
left_edge_delta = th_ack - tp->snd_una; | left_edge_delta = th_ack - tp->snd_una; | ||||
sack_blocks[num_sack_blks].start = tp->snd_una; | sack_blocks[num_sack_blks].start = tp->snd_una; | ||||
sack_blocks[num_sack_blks++].end = th_ack; | sack_blocks[num_sack_blks++].end = th_ack; | ||||
/* | /* | ||||
* Pulling snd_fack forward if we got here | * Pulling snd_fack forward if we got here | ||||
* due to DSACK blocks | * due to DSACK blocks | ||||
*/ | */ | ||||
if (SEQ_LT(tp->snd_fack, th_ack)) { | if (SEQ_LT(tp->snd_fack, th_ack)) { | ||||
delivered_data += th_ack - tp->snd_una; | delivered_data += th_ack - tp->snd_una; | ||||
tp->snd_fack = th_ack; | tp->snd_fack = th_ack; | ||||
sack_changed = 1; | sack_changed = SACK_CHANGE; | ||||
} | } | ||||
} | } | ||||
/* | /* | ||||
* Append received valid SACK blocks to sack_blocks[], but only if we | * Append received valid SACK blocks to sack_blocks[], but only if we | ||||
* received new blocks from the other side. | * received new blocks from the other side. | ||||
*/ | */ | ||||
if (to->to_flags & TOF_SACK) { | if (to->to_flags & TOF_SACK) { | ||||
for (i = 0; i < to->to_nsacks; i++) { | for (i = 0; i < to->to_nsacks; i++) { | ||||
▲ Show 20 Lines • Show All 67 Lines • ▼ Show 20 Lines | if (SEQ_LT(tp->snd_fack, sblkp->start)) { | ||||
if (((temp = TAILQ_LAST(&tp->snd_holes, sackhole_head)) != NULL) && | if (((temp = TAILQ_LAST(&tp->snd_holes, sackhole_head)) != NULL) && | ||||
SEQ_LEQ(tp->snd_fack,temp->end)) { | SEQ_LEQ(tp->snd_fack,temp->end)) { | ||||
temp->start = SEQ_MAX(tp->snd_fack, SEQ_MAX(tp->snd_una, th_ack)); | temp->start = SEQ_MAX(tp->snd_fack, SEQ_MAX(tp->snd_una, th_ack)); | ||||
temp->end = sblkp->start; | temp->end = sblkp->start; | ||||
temp->rxmit = temp->start; | temp->rxmit = temp->start; | ||||
delivered_data += sblkp->end - sblkp->start; | delivered_data += sblkp->end - sblkp->start; | ||||
tp->snd_fack = sblkp->end; | tp->snd_fack = sblkp->end; | ||||
sblkp--; | sblkp--; | ||||
sack_changed = 1; | sack_changed = SACK_NEWLOSS; | ||||
} else { | } else { | ||||
/* | /* | ||||
* Append a new SACK hole at the tail. If the | * Append a new SACK hole at the tail. If the | ||||
* second or later highest SACK blocks are also | * second or later highest SACK blocks are also | ||||
* beyond the current fack, they will be inserted | * beyond the current fack, they will be inserted | ||||
* by way of hole splitting in the while-loop below. | * by way of hole splitting in the while-loop below. | ||||
*/ | */ | ||||
temp = tcp_sackhole_insert(tp, tp->snd_fack,sblkp->start,NULL); | temp = tcp_sackhole_insert(tp, tp->snd_fack,sblkp->start,NULL); | ||||
if (temp != NULL) { | if (temp != NULL) { | ||||
delivered_data += sblkp->end - sblkp->start; | delivered_data += sblkp->end - sblkp->start; | ||||
tp->snd_fack = sblkp->end; | tp->snd_fack = sblkp->end; | ||||
/* Go to the previous sack block. */ | /* Go to the previous sack block. */ | ||||
sblkp--; | sblkp--; | ||||
sack_changed = 1; | sack_changed = SACK_CHANGE; | ||||
} else { | } else { | ||||
/* | /* | ||||
* We failed to add a new hole based on the current | * We failed to add a new hole based on the current | ||||
* sack block. Skip over all the sack blocks that | * sack block. Skip over all the sack blocks that | ||||
* fall completely to the right of snd_fack and | * fall completely to the right of snd_fack and | ||||
* proceed to trim the scoreboard based on the | * proceed to trim the scoreboard based on the | ||||
* remaining sack blocks. This also trims the | * remaining sack blocks. This also trims the | ||||
* scoreboard for th_ack (which is sack_blocks[0]). | * scoreboard for th_ack (which is sack_blocks[0]). | ||||
*/ | */ | ||||
while (sblkp >= sack_blocks && | while (sblkp >= sack_blocks && | ||||
SEQ_LT(tp->snd_fack, sblkp->start)) | SEQ_LT(tp->snd_fack, sblkp->start)) | ||||
sblkp--; | sblkp--; | ||||
if (sblkp >= sack_blocks && | if (sblkp >= sack_blocks && | ||||
SEQ_LT(tp->snd_fack, sblkp->end)) { | SEQ_LT(tp->snd_fack, sblkp->end)) { | ||||
delivered_data += sblkp->end - tp->snd_fack; | delivered_data += sblkp->end - tp->snd_fack; | ||||
tp->snd_fack = sblkp->end; | tp->snd_fack = sblkp->end; | ||||
sack_changed = 1; | /* | ||||
* While the Scoreboard didn't change in | |||||
* size, we only ended up here because | |||||
* some SACK data had to be dismissed. | |||||
*/ | |||||
sack_changed = SACK_NEWLOSS; | |||||
} | } | ||||
} | } | ||||
} | } | ||||
} else if (SEQ_LT(tp->snd_fack, sblkp->end)) { | } else if (SEQ_LT(tp->snd_fack, sblkp->end)) { | ||||
/* fack is advanced. */ | /* fack is advanced. */ | ||||
delivered_data += sblkp->end - tp->snd_fack; | delivered_data += sblkp->end - tp->snd_fack; | ||||
tp->snd_fack = sblkp->end; | tp->snd_fack = sblkp->end; | ||||
sack_changed = 1; | sack_changed = SACK_CHANGE; | ||||
} | } | ||||
cur = TAILQ_LAST(&tp->snd_holes, sackhole_head); /* Last SACK hole. */ | cur = TAILQ_LAST(&tp->snd_holes, sackhole_head); /* Last SACK hole. */ | ||||
/* | /* | ||||
* Since the incoming sack blocks are sorted, we can process them | * Since the incoming sack blocks are sorted, we can process them | ||||
* making one sweep of the scoreboard. | * making one sweep of the scoreboard. | ||||
*/ | */ | ||||
while (sblkp >= sack_blocks && cur != NULL) { | while (sblkp >= sack_blocks && cur != NULL) { | ||||
if (SEQ_GEQ(sblkp->start, cur->end)) { | if (SEQ_GEQ(sblkp->start, cur->end)) { | ||||
Show All 10 Lines | if (SEQ_LEQ(sblkp->end, cur->start)) { | ||||
* previous hole. | * previous hole. | ||||
*/ | */ | ||||
cur = TAILQ_PREV(cur, sackhole_head, scblink); | cur = TAILQ_PREV(cur, sackhole_head, scblink); | ||||
continue; | continue; | ||||
} | } | ||||
tp->sackhint.sack_bytes_rexmit -= (cur->rxmit - cur->start); | tp->sackhint.sack_bytes_rexmit -= (cur->rxmit - cur->start); | ||||
KASSERT(tp->sackhint.sack_bytes_rexmit >= 0, | KASSERT(tp->sackhint.sack_bytes_rexmit >= 0, | ||||
("sackhint bytes rtx >= 0")); | ("sackhint bytes rtx >= 0")); | ||||
sack_changed = 1; | sack_changed = SACK_CHANGE; | ||||
if (SEQ_LEQ(sblkp->start, cur->start)) { | if (SEQ_LEQ(sblkp->start, cur->start)) { | ||||
/* Data acks at least the beginning of hole. */ | /* Data acks at least the beginning of hole. */ | ||||
if (SEQ_GEQ(sblkp->end, cur->end)) { | if (SEQ_GEQ(sblkp->end, cur->end)) { | ||||
/* Acks entire hole, so delete hole. */ | /* Acks entire hole, so delete hole. */ | ||||
delivered_data += (cur->end - cur->start); | delivered_data += (cur->end - cur->start); | ||||
temp = cur; | temp = cur; | ||||
cur = TAILQ_PREV(cur, sackhole_head, scblink); | cur = TAILQ_PREV(cur, sackhole_head, scblink); | ||||
tcp_sackhole_remove(tp, temp); | tcp_sackhole_remove(tp, temp); | ||||
Show All 18 Lines | if (SEQ_LEQ(sblkp->start, cur->start)) { | ||||
cur->rxmit = SEQ_MIN(cur->rxmit, cur->end); | cur->rxmit = SEQ_MIN(cur->rxmit, cur->end); | ||||
} else { | } else { | ||||
/* | /* | ||||
* ACKs some data in middle of a hole; need | * ACKs some data in middle of a hole; need | ||||
* to split current hole | * to split current hole | ||||
*/ | */ | ||||
temp = tcp_sackhole_insert(tp, sblkp->end, | temp = tcp_sackhole_insert(tp, sblkp->end, | ||||
cur->end, cur); | cur->end, cur); | ||||
sack_changed = SACK_NEWLOSS; | |||||
if (temp != NULL) { | if (temp != NULL) { | ||||
if (SEQ_GT(cur->rxmit, temp->rxmit)) { | if (SEQ_GT(cur->rxmit, temp->rxmit)) { | ||||
temp->rxmit = cur->rxmit; | temp->rxmit = cur->rxmit; | ||||
tp->sackhint.sack_bytes_rexmit | tp->sackhint.sack_bytes_rexmit | ||||
+= (temp->rxmit | += (temp->rxmit | ||||
- temp->start); | - temp->start); | ||||
} | } | ||||
cur->end = sblkp->start; | cur->end = sblkp->start; | ||||
Show All 18 Lines | if (!(to->to_flags & TOF_SACK)) | ||||
/* | /* | ||||
* If this ACK did not contain any | * If this ACK did not contain any | ||||
* SACK blocks, any only moved the | * SACK blocks, any only moved the | ||||
* left edge right, it is a pure | * left edge right, it is a pure | ||||
* cumulative ACK. Do not count | * cumulative ACK. Do not count | ||||
* DupAck for this. Also required | * DupAck for this. Also required | ||||
* for RFC6675 rescue retransmission. | * for RFC6675 rescue retransmission. | ||||
*/ | */ | ||||
sack_changed = 0; | sack_changed = SACK_NOCHANGE; | ||||
tp->sackhint.delivered_data = delivered_data; | tp->sackhint.delivered_data = delivered_data; | ||||
tp->sackhint.sacked_bytes += delivered_data - left_edge_delta; | tp->sackhint.sacked_bytes += delivered_data - left_edge_delta; | ||||
KASSERT((delivered_data >= 0), ("delivered_data < 0")); | KASSERT((delivered_data >= 0), ("delivered_data < 0")); | ||||
KASSERT((tp->sackhint.sacked_bytes >= 0), ("sacked_bytes < 0")); | KASSERT((tp->sackhint.sacked_bytes >= 0), ("sacked_bytes < 0")); | ||||
return (sack_changed); | return (sack_changed); | ||||
} | } | ||||
/* | /* | ||||
▲ Show 20 Lines • Show All 178 Lines • Show Last 20 Lines |