Index: sys/netinet/tcp_input.c =================================================================== --- sys/netinet/tcp_input.c +++ sys/netinet/tcp_input.c @@ -2436,7 +2436,9 @@ hhook_run_tcp_est_in(tp, th, &to); if (SEQ_LEQ(th->th_ack, tp->snd_una)) { - if (tlen == 0 && tiwin == tp->snd_wnd) { + if ((tlen == 0 && tiwin == tp->snd_wnd) || + ((tp->t_flags & TF_SACK_PERMIT) && + tp->sackhint.change_in_sacked_bytes)) { /* * If this is the first time we've seen a * FIN from the remote, this is not a @@ -2478,8 +2480,13 @@ * When using TCP ECN, notify the peer that * we reduced the cwnd. */ - if (!tcp_timer_active(tp, TT_REXMT) || - th->th_ack != tp->snd_una) + /* + * Old acks should not be affecting t_dupacks + * counting so ignore them. + */ + if (th->th_ack != tp->snd_una) + break; + else if (!tcp_timer_active(tp, TT_REXMT)) tp->t_dupacks = 0; else if (++tp->t_dupacks > tcprexmtthresh || IN_FASTRECOVERY(tp->t_flags)) { @@ -2608,8 +2615,7 @@ tp->snd_cwnd = oldcwnd; goto drop; } - } else - tp->t_dupacks = 0; + } break; } Index: sys/netinet/tcp_sack.c =================================================================== --- sys/netinet/tcp_sack.c +++ sys/netinet/tcp_sack.c @@ -351,11 +351,12 @@ { struct sackhole *cur, *temp; struct sackblk sack, sack_blocks[TCP_MAX_SACK + 1], *sblkp; - int i, j, num_sack_blks; + int i, j, num_sack_blks, old_sacked_bytes; INP_WLOCK_ASSERT(tp->t_inpcb); num_sack_blks = 0; + old_sacked_bytes = 0; /* * 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. @@ -369,6 +370,7 @@ * received new blocks from the other side. */ if (to->to_flags & TOF_SACK) { + old_sacked_bytes = tp->sackhint.sacked_bytes; tp->sackhint.sacked_bytes = 0; /* reset */ for (i = 0; i < to->to_nsacks; i++) { bcopy((to->to_sacks + i * TCPOLEN_SACK), @@ -387,12 +389,19 @@ } } } + + /* Find the change in sacked bytes */ + tp->sackhint.change_in_sacked_bytes = tp->sackhint.sacked_bytes - + old_sacked_bytes; /* * Return if SND.UNA is not advanced and no valid SACK block is * received. */ - if (num_sack_blks == 0) + if (num_sack_blks == 0) { + /* XXXHP: Unsure if this is really needed */ + tp->sackhint.change_in_sacked_bytes = 0; return; + } /* * Sort the SACK blocks so we can update the scoreboard with just one Index: sys/netinet/tcp_var.h =================================================================== --- sys/netinet/tcp_var.h +++ sys/netinet/tcp_var.h @@ -78,6 +78,11 @@ * Total sacked bytes reported by the * receiver via sack option */ + int change_in_sacked_bytes; + /* + * (Signed) Difference of data in + * scoreboard due to the current ACK + */ uint64_t _pad[1]; /* TBD */ };