Index: sys/netinet/tcp_input.c =================================================================== --- sys/netinet/tcp_input.c +++ sys/netinet/tcp_input.c @@ -153,11 +153,6 @@ &VNET_NAME(drop_synfin), 0, "Drop TCP packets with SYN+FIN set"); -VNET_DEFINE(int, tcp_do_prr_conservative) = 0; -SYSCTL_INT(_net_inet_tcp, OID_AUTO, do_prr_conservative, CTLFLAG_VNET | CTLFLAG_RW, - &VNET_NAME(tcp_do_prr_conservative), 0, - "Do conservative Proportional Rate Reduction"); - VNET_DEFINE(int, tcp_do_prr) = 1; SYSCTL_INT(_net_inet_tcp, OID_AUTO, do_prr, CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(tcp_do_prr), 1, @@ -2597,13 +2592,14 @@ tp->snd_ssthresh, tp->sackhint.recover_fs) - tp->sackhint.sack_bytes_rexmit; } else { - if (V_tcp_do_prr_conservative) - limit = tp->sackhint.prr_delivered - - tp->sackhint.sack_bytes_rexmit; - else - limit = imax(tp->sackhint.prr_delivered - - tp->sackhint.sack_bytes_rexmit, - del_data) + maxseg; + /* + * PRR 6937bis heuristic: + * Up here we deal with duplicate ACKs, + * which by definition do not advance snd_una. + * Therefore be always conservative. + */ + limit = tp->sackhint.prr_delivered - + tp->sackhint.sack_bytes_rexmit; snd_cnt = imin(tp->snd_ssthresh - pipe, limit); } snd_cnt = imax(snd_cnt, 0) / maxseg; @@ -2811,7 +2807,7 @@ if (SEQ_LT(th->th_ack, tp->snd_recover)) { if (tp->t_flags & TF_SACK_PERMIT) if (V_tcp_do_prr && to.to_flags & TOF_SACK) - tcp_prr_partialack(tp, th); + tcp_prr_partialack(tp, th, sack_changed); else tcp_sack_partialack(tp, th); else @@ -3940,7 +3936,7 @@ } void -tcp_prr_partialack(struct tcpcb *tp, struct tcphdr *th) +tcp_prr_partialack(struct tcpcb *tp, struct tcphdr *th, int sack_changed) { int snd_cnt = 0, limit = 0, del_data = 0, pipe = 0; int maxseg = tcp_maxseg(tp); @@ -3971,7 +3967,17 @@ tp->snd_ssthresh, tp->sackhint.recover_fs) - tp->sackhint.sack_bytes_rexmit; } else { - if (V_tcp_do_prr_conservative) + /* + * PRR 6937bis heuristic: + * - A partial ack without SACK block beneath snd_recover + * indicates further loss. This case is not handled here, + * but in tcp_sack_partialack, to send a rescue + * retransmissions. + * - An SACK scoreboard update adding a new hole indicates + * further loss, so be conservative and send at most one + * segment. + */ + if (sack_changed == 2) limit = tp->sackhint.prr_delivered - tp->sackhint.sack_bytes_rexmit; else Index: sys/netinet/tcp_sack.c =================================================================== --- sys/netinet/tcp_sack.c +++ sys/netinet/tcp_sack.c @@ -533,7 +533,8 @@ * 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 * the sequence space). - * Returns 1 if incoming ACK has previously unknown SACK information, + * Returns 2 if incoming ACK indicates ongoing loss (hole split, new hole), + * 1 if incoming ACK has previously unknown SACK information, * 0 otherwise. */ int @@ -639,7 +640,7 @@ tp->snd_fack = sblkp->end; /* Go to the previous sack block. */ sblkp--; - sack_changed = 1; + sack_changed = 2; } else { /* * We failed to add a new hole based on the current @@ -656,7 +657,12 @@ SEQ_LT(tp->snd_fack, sblkp->end)) { delivered_data += sblkp->end - tp->snd_fack; 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 = 2; } } } else if (SEQ_LT(tp->snd_fack, sblkp->end)) { @@ -725,6 +731,7 @@ */ temp = tcp_sackhole_insert(tp, sblkp->end, cur->end, cur); + sack_changed = 2; if (temp != NULL) { if (SEQ_GT(cur->rxmit, temp->rxmit)) { temp->rxmit = cur->rxmit; Index: sys/netinet/tcp_var.h =================================================================== --- sys/netinet/tcp_var.h +++ sys/netinet/tcp_var.h @@ -869,7 +869,6 @@ VNET_DECLARE(struct inpcbinfo, tcbinfo); #define V_tcp_do_prr VNET(tcp_do_prr) -#define V_tcp_do_prr_conservative VNET(tcp_do_prr_conservative) #define V_tcp_do_newcwv VNET(tcp_do_newcwv) #define V_drop_synfin VNET(drop_synfin) #define V_path_mtu_discovery VNET(path_mtu_discovery) @@ -1063,7 +1062,7 @@ void tcp_clean_sackreport(struct tcpcb *tp); void tcp_sack_adjust(struct tcpcb *tp); struct sackhole *tcp_sack_output(struct tcpcb *tp, int *sack_bytes_rexmt); -void tcp_prr_partialack(struct tcpcb *, struct tcphdr *); +void tcp_prr_partialack(struct tcpcb *, struct tcphdr *, int); void tcp_sack_partialack(struct tcpcb *, struct tcphdr *); void tcp_free_sackholes(struct tcpcb *tp); int tcp_newreno(struct tcpcb *, struct tcphdr *);