Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 18 Jul 2026 09:24:23 +0000
From:      Richard Scheffenegger <rscheff@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org
Subject:   git: 506e3e30a43c - main - tcp: Align PRR implementation with RFC 9937
Message-ID:  <6a5b4647.1e260.62c07abb@gitrepo.freebsd.org>

index | next in thread | raw e-mail

The branch main has been updated by rscheff:

URL: https://cgit.FreeBSD.org/src/commit/?id=506e3e30a43cc04a21aa65a423bbd1cc4e0543f8

commit 506e3e30a43cc04a21aa65a423bbd1cc4e0543f8
Author:     Richard Scheffenegger <rscheff@FreeBSD.org>
AuthorDate: 2026-07-18 08:51:51 +0000
Commit:     Richard Scheffenegger <rscheff@FreeBSD.org>
CommitDate: 2026-07-18 09:24:05 +0000

    tcp: Align PRR implementation with RFC 9937
    
    - Early return when no new data is delivered
    - Switching from PRR-CRB to PRR-SSRB only when both SND.UNA advances and no further loss is indicated.
    - Accounting for sequence ranges SACKed before entering recovery in RecoverFS calculation.
    - Force a fast retransmit upon entering recovery when prr_out is 0 AND SndCnt is 0.
    - Set cwnd to ssthresh post recovery.
    
    Obtained from: mohnishhemanthkumar_gmail.com
    Reviewed by: rscheff, tuexen
    Differential Revision: https://reviews.freebsd.org/D56535
    MFC after: 3 months
---
 sys/netinet/tcp_input.c | 44 +++++++++++++++++++++++---------------------
 1 file changed, 23 insertions(+), 21 deletions(-)

diff --git a/sys/netinet/tcp_input.c b/sys/netinet/tcp_input.c
index 35a9d7633fa7..2a778687faf2 100644
--- a/sys/netinet/tcp_input.c
+++ b/sys/netinet/tcp_input.c
@@ -496,6 +496,7 @@ cc_post_recovery(struct tcpcb *tp, struct tcphdr *th)
 	tp->sackhint.delivered_data = 0;
 	tp->sackhint.prr_delivered = 0;
 	tp->sackhint.prr_out = 0;
+	tp->snd_cwnd = tp->snd_ssthresh;
 }
 
 /*
@@ -1496,6 +1497,7 @@ tcp_do_segment(struct tcpcb *tp, struct mbuf *m, struct tcphdr *th,
 	struct tcpopt to;
 	int tfo_syn;
 	u_int maxseg = 0;
+	uint32_t prev_sacked_bytes = 0;
 	bool no_data;
 
 	no_data = (tlen == 0);
@@ -2515,6 +2517,7 @@ tcp_do_segment(struct tcpcb *tp, struct mbuf *m, struct tcphdr *th,
 			goto dropafterack;
 		}
 		if (tcp_is_sack_recovery(tp, &to)) {
+			prev_sacked_bytes = tp->sackhint.sacked_bytes;
 			sack_changed = tcp_sack_doack(tp, &to, th->th_ack);
 			if ((sack_changed != SACK_NOCHANGE) &&
 			    (tp->t_flags & TF_LRD)) {
@@ -2666,12 +2669,15 @@ enter_recovery:
 						tp->sackhint.prr_delivered =
 						    imin(tp->snd_max - th->th_ack,
 						    (tp->snd_limited + 1) * maxseg);
+						tp->sackhint.recover_fs = imax(1,
+							(tp->snd_nxt - tp->snd_una) - prev_sacked_bytes
+							+ tp->sackhint.delivered_data);
 					} else {
 						tp->sackhint.prr_delivered =
 						    maxseg;
+						tp->sackhint.recover_fs = max(1,
+						    tp->snd_nxt - tp->snd_una);
 					}
-					tp->sackhint.recover_fs = max(1,
-					    tp->snd_nxt - tp->snd_una);
 				}
 				tp->snd_limited = 0;
 				if (tcp_is_sack_recovery(tp, &to)) {
@@ -3994,8 +4000,9 @@ void
 tcp_do_prr_ack(struct tcpcb *tp, struct tcphdr *th, struct tcpopt *to,
     sackstatus_t sack_changed, u_int *maxsegp)
 {
-	int snd_cnt = 0, limit = 0, del_data = 0, pipe = 0;
+	int snd_cnt = 0, del_data = 0, pipe = 0;
 	u_int maxseg;
+	bool safe_ack;
 
 	INP_WLOCK_ASSERT(tptoinpcb(tp));
 
@@ -4021,6 +4028,10 @@ tcp_do_prr_ack(struct tcpcb *tp, struct tcphdr *th, struct tcpopt *to,
 		pipe = imax(0, tp->snd_max - tp->snd_una -
 			    imin(INT_MAX / 65536, tp->t_dupacks) * maxseg);
 	}
+
+	if (del_data == 0)
+		return;
+
 	tp->sackhint.prr_delivered += del_data;
 	/*
 	 * Proportional Rate Reduction
@@ -4033,25 +4044,16 @@ tcp_do_prr_ack(struct tcpcb *tp, struct tcphdr *th, struct tcpopt *to,
 			    tp->snd_ssthresh, tp->sackhint.recover_fs) -
 			    tp->sackhint.prr_out + maxseg - 1;
 	} else {
-		/*
-		 * PRR 6937bis heuristic:
-		 * - A partial ack without SACK block beneath snd_recover
-		 * indicates further loss.
-		 * - An SACK scoreboard update adding a new hole indicates
-		 * further loss, so be conservative and send at most one
-		 * segment.
-		 * - Prevent ACK splitting attacks, by being conservative
-		 * when no new data is acked.
-		 */
-		if ((sack_changed == SACK_NEWLOSS) || (del_data == 0)) {
-			limit = tp->sackhint.prr_delivered -
-				tp->sackhint.prr_out;
-		} else {
-			limit = imax(tp->sackhint.prr_delivered -
-				    tp->sackhint.prr_out, del_data) +
-				    maxseg;
+		safe_ack = SEQ_GT(th->th_ack, tp->snd_una) && (sack_changed != SACK_NEWLOSS);
+		snd_cnt = imax(tp->sackhint.prr_delivered - tp->sackhint.prr_out, del_data);
+		if (safe_ack) {
+			snd_cnt += maxseg;
 		}
-		snd_cnt = imin((tp->snd_ssthresh - pipe), limit);
+		snd_cnt = imin(tp->snd_ssthresh - pipe, snd_cnt);
+	}
+
+	if (tp->sackhint.prr_out == 0 && snd_cnt == 0) {
+		snd_cnt = maxseg;
 	}
 	snd_cnt = imax(snd_cnt, 0) / maxseg;
 	/*


home | help

Want to link to this message? Use this
URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?6a5b4647.1e260.62c07abb>