Changeset View
Changeset View
Standalone View
Standalone View
head/sys/netinet/tcp_sack.c
Show First 20 Lines • Show All 143 Lines • ▼ Show 20 Lines | SYSCTL_INT(_net_inet_tcp_sack, OID_AUTO, globalmaxholes, CTLFLAG_VNET | CTLFLAG_RW, | ||||
&VNET_NAME(tcp_sack_globalmaxholes), 0, | &VNET_NAME(tcp_sack_globalmaxholes), 0, | ||||
"Global maximum number of TCP SACK holes"); | "Global maximum number of TCP SACK holes"); | ||||
VNET_DEFINE(int, tcp_sack_globalholes) = 0; | VNET_DEFINE(int, tcp_sack_globalholes) = 0; | ||||
SYSCTL_INT(_net_inet_tcp_sack, OID_AUTO, globalholes, CTLFLAG_VNET | CTLFLAG_RD, | SYSCTL_INT(_net_inet_tcp_sack, OID_AUTO, globalholes, CTLFLAG_VNET | CTLFLAG_RD, | ||||
&VNET_NAME(tcp_sack_globalholes), 0, | &VNET_NAME(tcp_sack_globalholes), 0, | ||||
"Global number of TCP SACK holes currently allocated"); | "Global number of TCP SACK holes currently allocated"); | ||||
/* | /* | ||||
* This function will find overlaps with the currently stored sackblocks | |||||
* and add any overlap as a dsack block upfront | |||||
*/ | |||||
void | |||||
tcp_update_dsack_list(struct tcpcb *tp, tcp_seq rcv_start, tcp_seq rcv_end) | |||||
{ | |||||
struct sackblk head_blk,mid_blk,saved_blks[MAX_SACK_BLKS]; | |||||
int i, j, n, identical; | |||||
tcp_seq start, end; | |||||
INP_WLOCK_ASSERT(tp->t_inpcb); | |||||
KASSERT(SEQ_LT(rcv_start, rcv_end), ("rcv_start < rcv_end")); | |||||
if (tp->t_inpcb->inp_socket->so_options & SO_DEBUG) { | |||||
log(LOG_DEBUG, "\nDSACK update: %d..%d, rcv_nxt: %u\n", | |||||
rcv_start, rcv_end, tp->rcv_nxt); | |||||
} | |||||
if (SEQ_LT(rcv_end, tp->rcv_nxt) || | |||||
((rcv_end == tp->rcv_nxt) && | |||||
(tp->rcv_numsacks > 0 ) && | |||||
(tp->sackblks[0].end == tp->rcv_nxt))) { | |||||
saved_blks[0].start = rcv_start; | |||||
saved_blks[0].end = rcv_end; | |||||
} else { | |||||
saved_blks[0].start = saved_blks[0].end = 0; | |||||
} | |||||
head_blk.start = head_blk.end = 0; | |||||
mid_blk.start = rcv_start; | |||||
mid_blk.end = rcv_end; | |||||
identical = 0; | |||||
for (i = 0; i < tp->rcv_numsacks; i++) { | |||||
start = tp->sackblks[i].start; | |||||
end = tp->sackblks[i].end; | |||||
if (SEQ_LT(rcv_end, start)) { | |||||
/* pkt left to sack blk */ | |||||
continue; | |||||
} | |||||
if (SEQ_GT(rcv_start, end)) { | |||||
/* pkt right to sack blk */ | |||||
continue; | |||||
} | |||||
if (SEQ_GT(tp->rcv_nxt, end)) { | |||||
if ((SEQ_MAX(rcv_start, start) != SEQ_MIN(rcv_end, end)) && | |||||
(SEQ_GT(head_blk.start, SEQ_MAX(rcv_start, start)) || | |||||
(head_blk.start == head_blk.end))) { | |||||
head_blk.start = SEQ_MAX(rcv_start, start); | |||||
head_blk.end = SEQ_MIN(rcv_end, end); | |||||
} | |||||
continue; | |||||
} | |||||
if (((head_blk.start == head_blk.end) || | |||||
SEQ_LT(start, head_blk.start)) && | |||||
(SEQ_GT(end, rcv_start) && | |||||
SEQ_LEQ(start, rcv_end))) { | |||||
head_blk.start = start; | |||||
head_blk.end = end; | |||||
} | |||||
mid_blk.start = SEQ_MIN(mid_blk.start, start); | |||||
mid_blk.end = SEQ_MAX(mid_blk.end, end); | |||||
if ((mid_blk.start == start) && | |||||
(mid_blk.end == end)) | |||||
identical = 1; | |||||
} | |||||
if (SEQ_LT(head_blk.start, head_blk.end)) { | |||||
/* store overlapping range */ | |||||
saved_blks[0].start = SEQ_MAX(rcv_start, head_blk.start); | |||||
saved_blks[0].end = SEQ_MIN(rcv_end, head_blk.end); | |||||
} | |||||
n = 1; | |||||
/* | |||||
* Second, if not ACKed, store the SACK block that | |||||
* overlaps with the DSACK block unless it is identical | |||||
*/ | |||||
if ((SEQ_LT(tp->rcv_nxt, mid_blk.end) && | |||||
!((mid_blk.start == saved_blks[0].start) && | |||||
(mid_blk.end == saved_blks[0].end))) || | |||||
identical == 1) { | |||||
saved_blks[n].start = mid_blk.start; | |||||
saved_blks[n++].end = mid_blk.end; | |||||
} | |||||
for (j = 0; (j < tp->rcv_numsacks) && (j < MAX_SACK_BLKS-1); j++) { | |||||
if (((SEQ_LT(tp->sackblks[j].end, mid_blk.start) || | |||||
SEQ_GT(tp->sackblks[j].start, mid_blk.end)) && | |||||
(SEQ_GT(tp->sackblks[j].start, tp->rcv_nxt)))) | |||||
saved_blks[n++] = tp->sackblks[j]; | |||||
} | |||||
j = 0; | |||||
for (i = 0; i < n; i++) { | |||||
/* we can end up with a stale inital entry */ | |||||
if (SEQ_LT(saved_blks[i].start, saved_blks[i].end)) { | |||||
tp->sackblks[j++] = saved_blks[i]; | |||||
} | |||||
} | |||||
tp->rcv_numsacks = j; | |||||
} | |||||
/* | |||||
* This function is called upon receipt of new valid data (while not in | * This function is called upon receipt of new valid data (while not in | ||||
* header prediction mode), and it updates the ordered list of sacks. | * header prediction mode), and it updates the ordered list of sacks. | ||||
*/ | */ | ||||
void | void | ||||
tcp_update_sack_list(struct tcpcb *tp, tcp_seq rcv_start, tcp_seq rcv_end) | tcp_update_sack_list(struct tcpcb *tp, tcp_seq rcv_start, tcp_seq rcv_end) | ||||
{ | { | ||||
/* | /* | ||||
* First reported block MUST be the most recent one. Subsequent | * First reported block MUST be the most recent one. Subsequent | ||||
* blocks SHOULD be in the order in which they arrived at the | * blocks SHOULD be in the order in which they arrived at the | ||||
* receiver. These two conditions make the implementation fully | * receiver. These two conditions make the implementation fully | ||||
* compliant with RFC 2018. | * compliant with RFC 2018. | ||||
*/ | */ | ||||
struct sackblk head_blk, saved_blks[MAX_SACK_BLKS]; | struct sackblk head_blk, saved_blks[MAX_SACK_BLKS]; | ||||
int num_head, num_saved, i; | int num_head, num_saved, i; | ||||
INP_WLOCK_ASSERT(tp->t_inpcb); | INP_WLOCK_ASSERT(tp->t_inpcb); | ||||
/* Check arguments. */ | /* Check arguments. */ | ||||
KASSERT(SEQ_LEQ(rcv_start, rcv_end), ("rcv_start <= rcv_end")); | KASSERT(SEQ_LEQ(rcv_start, rcv_end), ("rcv_start <= rcv_end")); | ||||
if ((rcv_start == rcv_end) && | |||||
(tp->rcv_numsacks >= 1) && | |||||
(rcv_end == tp->sackblks[0].end)) { | |||||
/* retaining DSACK block below rcv_nxt (todrop) */ | |||||
head_blk = tp->sackblks[0]; | |||||
} else { | |||||
/* SACK block for the received segment. */ | /* SACK block for the received segment. */ | ||||
head_blk.start = rcv_start; | head_blk.start = rcv_start; | ||||
head_blk.end = rcv_end; | head_blk.end = rcv_end; | ||||
} | |||||
/* | /* | ||||
* Merge updated SACK blocks into head_blk, and save unchanged SACK | * Merge updated SACK blocks into head_blk, and save unchanged SACK | ||||
* blocks into saved_blks[]. num_saved will have the number of the | * blocks into saved_blks[]. num_saved will have the number of the | ||||
* saved SACK blocks. | * saved SACK blocks. | ||||
*/ | */ | ||||
num_saved = 0; | num_saved = 0; | ||||
for (i = 0; i < tp->rcv_numsacks; i++) { | for (i = 0; i < tp->rcv_numsacks; i++) { | ||||
▲ Show 20 Lines • Show All 77 Lines • ▼ Show 20 Lines | if (SEQ_LT(rcv_start, rcv_end)) { | ||||
tp->sackblks[0] = head_blk; | tp->sackblks[0] = head_blk; | ||||
num_head = 1; | num_head = 1; | ||||
/* | /* | ||||
* If the number of saved SACK blocks exceeds its limit, | * If the number of saved SACK blocks exceeds its limit, | ||||
* discard the last SACK block. | * discard the last SACK block. | ||||
*/ | */ | ||||
if (num_saved >= MAX_SACK_BLKS) | if (num_saved >= MAX_SACK_BLKS) | ||||
num_saved--; | num_saved--; | ||||
} | |||||
if ((rcv_start == rcv_end) && | |||||
(rcv_start == tp->sackblks[0].end)) { | |||||
num_head = 1; | |||||
} | } | ||||
if (num_saved > 0) { | if (num_saved > 0) { | ||||
/* | /* | ||||
* Copy the saved SACK blocks back. | * Copy the saved SACK blocks back. | ||||
*/ | */ | ||||
bcopy(saved_blks, &tp->sackblks[num_head], | bcopy(saved_blks, &tp->sackblks[num_head], | ||||
sizeof(struct sackblk) * num_saved); | sizeof(struct sackblk) * num_saved); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 502 Lines • Show Last 20 Lines |