diff --git a/sys/netinet/tcp_input.c b/sys/netinet/tcp_input.c --- a/sys/netinet/tcp_input.c +++ b/sys/netinet/tcp_input.c @@ -4048,7 +4048,8 @@ if (tp->t_fb->tfb_compute_pipe == NULL) { return (tp->snd_max - tp->snd_una + tp->sackhint.sack_bytes_rexmit - - tp->sackhint.sacked_bytes); + tp->sackhint.sacked_bytes - + tp->sackhint.lost_bytes); } else { return((*tp->t_fb->tfb_compute_pipe)(tp)); } diff --git a/sys/netinet/tcp_sack.c b/sys/netinet/tcp_sack.c --- a/sys/netinet/tcp_sack.c +++ b/sys/netinet/tcp_sack.c @@ -555,6 +555,11 @@ int i, j, num_sack_blks, sack_changed; int delivered_data, left_edge_delta; + tcp_seq loss_hiack = 0; + int loss_thresh = 0; + int loss_sblks = 0; + int notlost_bytes = 0; + INP_WLOCK_ASSERT(tptoinpcb(tp)); num_sack_blks = 0; @@ -637,6 +642,7 @@ */ tp->snd_fack = SEQ_MAX(tp->snd_una, th_ack); tp->sackhint.sacked_bytes = 0; /* reset */ + tp->sackhint.hole_bytes = 0; } /* * In the while-loop below, incoming SACK blocks (sack_blocks[]) and @@ -661,13 +667,17 @@ */ if (((temp = TAILQ_LAST(&tp->snd_holes, sackhole_head)) != NULL) && SEQ_LEQ(tp->snd_fack,temp->end)) { + tp->sackhint.hole_bytes -= temp->end - temp->start; 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->sackhint.hole_bytes += temp->end - temp->start; + KASSERT(tp->sackhint.hole_bytes >= 0, + ("sackhint hole bytes >= 0")); tp->snd_fack = sblkp->end; - sblkp--; sack_changed = 1; + sblkp--; } else { /* * Append a new SACK hole at the tail. If the @@ -678,10 +688,11 @@ temp = tcp_sackhole_insert(tp, tp->snd_fack,sblkp->start,NULL); if (temp != NULL) { delivered_data += sblkp->end - sblkp->start; + tp->sackhint.hole_bytes += temp->end - temp->start; tp->snd_fack = sblkp->end; + sack_changed = 1; /* Go to the previous sack block. */ sblkp--; - sack_changed = 1; } else { /* * We failed to add a new hole based on the current @@ -709,11 +720,29 @@ sack_changed = 1; } cur = TAILQ_LAST(&tp->snd_holes, sackhole_head); /* Last SACK hole. */ + loss_hiack = tp->snd_fack; + /* * Since the incoming sack blocks are sorted, we can process them * making one sweep of the scoreboard. */ - while (sblkp >= sack_blocks && cur != NULL) { + while (cur != NULL) { + if (!(sblkp >= sack_blocks)) { + if (((loss_sblks >= tcprexmtthresh) || + (loss_thresh > (tcprexmtthresh-1)*tp->t_maxseg))) + break; + loss_thresh += loss_hiack - cur->end; + loss_hiack = cur->start; + loss_sblks++; + if (!((loss_sblks >= tcprexmtthresh) || + (loss_thresh > (tcprexmtthresh-1)*tp->t_maxseg))) { + notlost_bytes += cur->end - cur->start; + } else { + break; + } + cur = TAILQ_PREV(cur, sackhole_head, scblink); + continue; + } if (SEQ_GEQ(sblkp->start, cur->end)) { /* * SACKs data beyond the current hole. Go to the @@ -727,6 +756,12 @@ * SACKs data before the current hole. Go to the * previous hole. */ + loss_thresh += loss_hiack - cur->end; + loss_hiack = cur->start; + loss_sblks++; + if (!((loss_sblks >= tcprexmtthresh) || + (loss_thresh > (tcprexmtthresh-1)*tp->t_maxseg))) + notlost_bytes += cur->end - cur->start; cur = TAILQ_PREV(cur, sackhole_head, scblink); continue; } @@ -742,6 +777,7 @@ delivered_data += (cur->end - cur->start); temp = cur; cur = TAILQ_PREV(cur, sackhole_head, scblink); + tp->sackhint.hole_bytes -= temp->end - temp->start; tcp_sackhole_remove(tp, temp); /* * The sack block may ack all or part of the @@ -752,6 +788,7 @@ } else { /* Move start of hole forward. */ delivered_data += (sblkp->end - cur->start); + tp->sackhint.hole_bytes -= sblkp->end - cur->start; cur->start = sblkp->end; cur->rxmit = SEQ_MAX(cur->rxmit, cur->start); } @@ -760,6 +797,7 @@ if (SEQ_GEQ(sblkp->end, cur->end)) { /* Move end of hole backward. */ delivered_data += (cur->end - sblkp->start); + tp->sackhint.hole_bytes -= cur->end - sblkp->start; cur->end = sblkp->start; cur->rxmit = SEQ_MIN(cur->rxmit, cur->end); if ((tp->t_flags & TF_LRD) && SEQ_GEQ(cur->rxmit, cur->end)) @@ -778,6 +816,13 @@ (SEQ_MIN(temp->rxmit, temp->end) - temp->start); } + tp->sackhint.hole_bytes -= sblkp->end - sblkp->start; + loss_thresh += loss_hiack - temp->end; + loss_hiack = temp->start; + loss_sblks++; + if (!((loss_sblks >= tcprexmtthresh) || + (loss_thresh > (tcprexmtthresh-1)*tp->t_maxseg))) + notlost_bytes += temp->end - temp->start; cur->end = sblkp->start; cur->rxmit = SEQ_MIN(cur->rxmit, cur->end); @@ -794,11 +839,25 @@ * we're done with the sack block or the sack hole. * Accordingly, we advance one or the other. */ - if (SEQ_LEQ(sblkp->start, cur->start)) + if (SEQ_LEQ(sblkp->start, cur->start)) { + loss_thresh += loss_hiack - cur->end; + loss_hiack = cur->start; + loss_sblks++; + if (!((loss_sblks >= tcprexmtthresh) || + (loss_thresh > (tcprexmtthresh-1)*tp->t_maxseg))) + notlost_bytes += cur->end - cur->start; cur = TAILQ_PREV(cur, sackhole_head, scblink); - else + } else { sblkp--; + } } + + KASSERT(!(TAILQ_EMPTY(&tp->snd_holes) && (tp->sackhint.hole_bytes != 0)), + ("SACK scoreboard empty, but accounting non-zero\n")); + + KASSERT(notlost_bytes <= tp->sackhint.hole_bytes, + ("SACK: more bytes marked notlost than in scoreboard holes")); + if (!(to->to_flags & TOF_SACK)) /* * If this ACK did not contain any @@ -811,6 +870,7 @@ sack_changed = 0; tp->sackhint.delivered_data = delivered_data; tp->sackhint.sacked_bytes += delivered_data - left_edge_delta; + tp->sackhint.lost_bytes = tp->sackhint.hole_bytes - notlost_bytes; KASSERT((delivered_data >= 0), ("delivered_data < 0")); KASSERT((tp->sackhint.sacked_bytes >= 0), ("sacked_bytes < 0")); return (sack_changed); @@ -845,6 +905,7 @@ void tcp_sack_partialack(struct tcpcb *tp, struct tcphdr *th) { + struct sackhole *temp; int num_segs = 1; u_int maxseg = tcp_maxseg(tp); @@ -891,8 +952,10 @@ highdata = SEQ_MIN(highdata, tp->snd_recover); if (th->th_ack != highdata) { tp->snd_fack = th->th_ack; - (void)tcp_sackhole_insert(tp, SEQ_MAX(th->th_ack, - highdata - maxseg), highdata, NULL); + if ((temp = tcp_sackhole_insert(tp, SEQ_MAX(th->th_ack, + highdata - maxseg), highdata, NULL)) != NULL) + tp->sackhint.hole_bytes += temp->end - + temp->start; } } (void) tcp_output(tp); diff --git a/sys/netinet/tcp_var.h b/sys/netinet/tcp_var.h --- a/sys/netinet/tcp_var.h +++ b/sys/netinet/tcp_var.h @@ -128,6 +128,8 @@ uint32_t recover_fs; /* Flight Size at the start of Loss recovery */ uint32_t prr_delivered; /* Total bytes delivered using PRR */ uint32_t prr_out; /* Bytes sent during IN_RECOVERY */ + uint32_t hole_bytes; /* current number of bytes in scoreboard holes */ + uint32_t lost_bytes; /* number of rfc6675 IsLost() bytes */ }; #define SEGQ_EMPTY(tp) TAILQ_EMPTY(&(tp)->t_segq)