Index: sys/netinet/tcp_congctl.c
===================================================================
RCS file: /cvsroot/src/sys/netinet/tcp_congctl.c,v
retrieving revision 1.29
diff -p -u -r1.29 tcp_congctl.c
--- sys/netinet/tcp_congctl.c	14 May 2024 19:00:44 -0000	1.29
+++ sys/netinet/tcp_congctl.c	3 Apr 2026 07:38:28 -0000
@@ -477,17 +477,17 @@ tcp_reno_do_fast_retransmit(struct tcpcb
 	 * bytes past the last ACKed segment,
 	 * irrespective of the number of DupAcks.
 	 */
-	
+
 	tcp_seq onxt = tp->snd_nxt;
 
 	tp->t_partialacks = 0;
 	TCP_TIMER_DISARM(tp, TCPT_REXMT);
 	tp->t_rtttime = 0;
 	if (TCP_SACK_ENABLED(tp)) {
-		tp->t_dupacks = tcprexmtthresh;
 		tp->sack_newdata = tp->snd_nxt;
 		tp->snd_cwnd = tp->t_segsz;
 		(void) tcp_output(tp);
+		tp->snd_cwnd = tp->snd_ssthresh + tp->t_segsz * tp->t_dupacks;
 		return 0;
 	}
 	tp->snd_nxt = th->th_ack;
@@ -603,7 +603,7 @@ tcp_reno_newack(struct tcpcb *tp, const 
 
 		int acked = th->th_ack - tp->snd_una;
 
-		if (cw >= tp->snd_ssthresh) {
+		if (cw > tp->snd_ssthresh) {
 			tp->t_bytes_acked += acked;
 			if (tp->t_bytes_acked >= cw) {
 				/* Time to increase the window. */
@@ -624,6 +624,7 @@ tcp_reno_newack(struct tcpcb *tp, const 
 			abc_lim = (tcp_abc_aggressive == 0 ||
 			    tp->snd_nxt != tp->snd_max) ? incr : incr * 2;
 			incr = uimin(acked, abc_lim);
+			tp->t_bytes_acked = 0;
 		}
 	} else {
 
@@ -632,14 +633,17 @@ tcp_reno_newack(struct tcpcb *tp, const 
 		 * in flight, open exponentially (segsz per packet).
 		 * Otherwise open linearly: segsz per window
 		 * (segsz^2 / cwnd per packet).
-		 */
-
-		if (cw >= tp->snd_ssthresh) {
-			incr = incr * incr / cw;
+		 * If cwnd > maxseg^2, fix the cwnd increment at 1 byte
+		 * to avoid capping cwnd.
+                 */
+ 
+		if (cw > tp->snd_ssthresh) {
+			incr = uimax(incr * incr / cw, 1);
 		}
 	}
 
-	tp->snd_cwnd = uimin(cw + incr, TCP_MAXWIN << tp->snd_scale);
+	if (incr > 0)
+		tp->snd_cwnd = uimin(cw + incr, TCP_MAXWIN << tp->snd_scale);
 }
 
 const struct tcp_congctl tcp_reno_ctl = {
@@ -726,6 +730,8 @@ tcp_newreno_fast_retransmit_newack(struc
 			tp->snd_cwnd = sack_bytes_rxmt +
 			    (tp->snd_nxt - tp->sack_newdata) +
 			    sack_num_segs * tp->t_segsz;
+			if (tp->snd_cwnd > tp->snd_ssthresh)
+				tp->snd_cwnd = tp->snd_ssthresh;
 			tp->t_flags |= TF_ACKNOW;
 			(void) tcp_output(tp);
 		} else {
@@ -735,6 +741,7 @@ tcp_newreno_fast_retransmit_newack(struc
 			 * snd_una is not yet updated when we're called
 			 */
 			tp->snd_cwnd = tp->t_segsz + (th->th_ack - tp->snd_una);
+			tp->t_flags |= TF_ACKNOW;
 			(void) tcp_output(tp);
 			tp->snd_cwnd = ocwnd;
 			if (SEQ_GT(onxt, tp->snd_nxt))
@@ -755,17 +762,23 @@ tcp_newreno_fast_retransmit_newack(struc
 		 * snd_ssthresh outstanding data.  But in case we
 		 * would be inclined to send a burst, better to do
 		 * it via the slow start mechanism.
+		 *
+		 * RFC 6582 3.2
+		 * cwnd = min(sshtresh, max(flightsize, smss) + smss)
 		 */
-		if (SEQ_SUB(tp->snd_max, th->th_ack) < tp->snd_ssthresh)
-			tp->snd_cwnd = SEQ_SUB(tp->snd_max, th->th_ack)
-			    + tp->t_segsz;
-		else
-			tp->snd_cwnd = tp->snd_ssthresh;
+
+		long flight;
+		flight = uimax(SEQ_SUB(tp->snd_max, th->th_ack), tp->t_segsz);
+		tp->snd_cwnd = uimin(flight + tp->t_segsz, tp->snd_ssthresh);
+
 		tp->t_partialacks = -1;
 		tp->t_dupacks = 0;
 		tp->t_bytes_acked = 0;
 		if (TCP_SACK_ENABLED(tp) && SEQ_GT(th->th_ack, tp->snd_fack))
 			tp->snd_fack = th->th_ack;
+
+		if (SEQ_LEQ(th->th_ack, tp->snd_max))
+			tp->t_flags |= TF_ACKNOW;
 	}
 }
 
@@ -868,12 +881,17 @@ tcp_cubic_getW(struct tcpcb *tp, uint32_
 	long tK3;
 
 	/* Section 3.1 Eq. 2 */
-	K = tcp_cubic_cbrt(tp->snd_cubic_wmax / CUBIC_BETAB *
-	    CUBIC_CB / CUBIC_CA);
+	K = tcp_cubic_cbrt(
+	    tp->snd_cubic_wmax
+	    * (CUBIC_BETAB - CUBIC_BETAA) / CUBIC_BETAB
+	    * CUBIC_CB / CUBIC_CA);
 	/*  (t-K)^3 - not clear why is the measure unit mattering */
 	tK3 = (long)(ms_elapsed + rtt) - (long)K;
-	tK3 = tK3 * tK3 * tK3;
 
+	if (tK3 <= 0)
+		return tp->snd_cubic_wmax;
+
+	tK3 = tK3 * tK3 * tK3;
 	return CUBIC_CA * tK3 / CUBIC_CB + tp->snd_cubic_wmax;
 }
 
@@ -896,7 +914,7 @@ tcp_cubic_congestion_exp(struct tcpcb *t
 		tp->snd_cubic_wmax = tp->snd_cwnd;
 	}
 
-	tp->snd_cubic_wmax = uimax(tp->t_segsz, tp->snd_cubic_wmax);
+	tp->snd_cubic_wmax = ulmax(tp->t_segsz, tp->snd_cubic_wmax);
 
 	/* Shrink CWND */
 	tcp_common_congestion_exp(tp, CUBIC_BETAA, CUBIC_BETAB);
@@ -939,20 +957,26 @@ tcp_cubic_newack(struct tcpcb *tp, const
 
 		/* Compute W_tcp(t) */
 		w_tcp = tp->snd_cubic_wmax * CUBIC_BETAA / CUBIC_BETAB +
-		    ms_elapsed / rtt / 3;
+		    ms_elapsed / rtt / ((CUBIC_BETAB + CUBIC_BETAA)/(3*(CUBIC_BETAB - CUBIC_BETAA)));
 
-		if (tp->snd_cwnd > w_tcp) {
+		if (tp->snd_cwnd >= w_tcp) {
 			/* Not in TCP friendly mode */
-			tp->snd_cwnd += (tcp_cubic_getW(tp, ms_elapsed, rtt) -
-			    tp->snd_cwnd) / tp->snd_cwnd;
+			u_long w = tcp_cubic_getW(tp, ms_elapsed, rtt);
+
+			if (w > tp->snd_cwnd) {
+				w = ulmin(w, tp->snd_cwnd * 3 / 2);
+
+				tp->snd_cwnd +=
+				    (w - tp->snd_cwnd) / tp->snd_cwnd;
+			}
 		} else {
 			/* friendly TCP mode */
 			tp->snd_cwnd = w_tcp;
 		}
 
 		/* Make sure we are within limits */
-		tp->snd_cwnd = uimax(tp->snd_cwnd, tp->t_segsz);
-		tp->snd_cwnd = uimin(tp->snd_cwnd, TCP_MAXWIN << tp->snd_scale);
+		tp->snd_cwnd = ulmax(tp->snd_cwnd, tp->t_segsz);
+		tp->snd_cwnd = ulmin(tp->snd_cwnd, TCP_MAXWIN << tp->snd_scale);
 	} else {
 		/* Use New Reno */
 		tcp_newreno_newack(tp, th);
Index: sys/netinet/tcp_input.c
===================================================================
RCS file: /cvsroot/src/sys/netinet/tcp_input.c,v
retrieving revision 1.441
diff -p -u -r1.441 tcp_input.c
--- sys/netinet/tcp_input.c	8 Oct 2024 06:17:14 -0000	1.441
+++ sys/netinet/tcp_input.c	3 Apr 2026 07:38:28 -0000
@@ -231,7 +231,7 @@ int	tcp_autorcvbuf_inc = 16 * 1024;
 int	tcp_autorcvbuf_max = 256 * 1024;
 int	tcp_msl = (TCPTV_MSL / PR_SLOWHZ);
 
-int tcp_reass_maxqueuelen = 100;
+int tcp_reass_maxqueuelen = 1024;
 
 static int tcp_rst_ppslim_count = 0;
 static struct timeval tcp_rst_ppslim_last;
@@ -713,6 +713,7 @@ insert_it:
 	if (tp->t_segqlen > tcp_reass_maxqueuelen) {
 		TCP_STATINC(TCP_STAT_RCVMEMDROP);
 		m_freem(m);
+printf("%s.%u: reassembly queue limit reached\n",__func__,__LINE__);
 		goto out;
 	}
 
@@ -728,6 +729,7 @@ insert_it:
 		if (tiqe == NULL) {
 			TCP_STATINC(TCP_STAT_RCVMEMDROP);
 			m_freem(m);
+printf("%s.%u: reassembly queue allocation failed\n",__func__,__LINE__);
 			goto out;
 		}
 	}
@@ -1486,7 +1488,7 @@ findpcb:
 
 	/* Unscale the window into a 32-bit value. */
 	if ((tiflags & TH_SYN) == 0)
-		tiwin = th->th_win << tp->snd_scale;
+		tiwin = (u_long)th->th_win << tp->snd_scale;
 	else
 		tiwin = th->th_win;
 
@@ -1911,7 +1913,7 @@ after_listen:
 		} else if (th->th_ack == tp->snd_una &&
 		    TAILQ_FIRST(&tp->segq) == NULL &&
 		    tlen <= sbspace(&so->so_rcv)) {
-			int newsize = 0;
+			u_long newsize = 0;
 
 			/*
 			 * this is a pure, in-sequence data packet
@@ -1978,8 +1980,8 @@ after_listen:
 					    (so->so_rcv.sb_hiwat / 8 * 7) &&
 					    so->so_rcv.sb_hiwat <
 					    tcp_autorcvbuf_max) {
-						newsize =
-						    uimin(so->so_rcv.sb_hiwat +
+						newsize = tcp_growbuf(
+						    so->so_rcv.sb_hiwat,
 						    tcp_autorcvbuf_inc,
 						    tcp_autorcvbuf_max);
 					}
@@ -2492,11 +2494,13 @@ after_listen:
 					 */
 					if (tp->t_congctl->fast_retransmit(tp, th)) {
 						/* False fast retransmit */
+						tp->t_dupacks = 0;
 						break;
 					}
 					goto drop;
 				} else if (tp->t_dupacks > tcprexmtthresh) {
-					tp->snd_cwnd += tp->t_segsz;
+					if (!TCP_SACK_ENABLED(tp))
+						tp->snd_cwnd += tp->t_segsz;
 					KERNEL_LOCK(1, NULL);
 					(void)tcp_output(tp);
 					KERNEL_UNLOCK_ONE(NULL);
@@ -2518,6 +2522,7 @@ after_listen:
 			}
 			break;
 		}
+
 		/*
 		 * If the congestion window was inflated to account
 		 * for the other side's cached packets, retract it.
Index: sys/netinet/tcp_output.c
===================================================================
RCS file: /cvsroot/src/sys/netinet/tcp_output.c,v
retrieving revision 1.222
diff -p -u -r1.222 tcp_output.c
--- sys/netinet/tcp_output.c	8 Sep 2024 09:36:52 -0000	1.222
+++ sys/netinet/tcp_output.c	3 Apr 2026 07:38:28 -0000
@@ -399,7 +399,7 @@ tcp_segsize(struct tcpcb *tp, int *txseg
 
 static int
 tcp_build_datapkt(struct tcpcb *tp, struct socket *so, int off,
-    long len, int hdrlen, struct mbuf **mp)
+    long len, int hdrlen, struct mbuf **mp, int sack_rxmit)
 {
 	struct mbuf *m, *m0;
 	net_stat_ref_t tcps;
@@ -407,7 +407,7 @@ tcp_build_datapkt(struct tcpcb *tp, stru
 	tcps = TCP_STAT_GETREF();
 	if (tp->t_force && len == 1)
 		_NET_STATINC_REF(tcps, TCP_STAT_SNDPROBE);
-	else if (SEQ_LT(tp->snd_nxt, tp->snd_max)) {
+	else if (SEQ_LT(tp->snd_nxt, tp->snd_max) || sack_rxmit) {
 		tp->t_sndrexmitpack++;
 		_NET_STATINC_REF(tcps, TCP_STAT_SNDREXMITPACK);
 		_NET_STATADD_REF(tcps, TCP_STAT_SNDREXMITBYTE, len);
@@ -521,7 +521,9 @@ tcp_output(struct tcpcb *tp)
 	unsigned int sack_numblks;
 	int idle, sendalot, txsegsize, rxsegsize;
 	int txsegsize_nosack;
+#if 0
 	int maxburst = TCP_MAXBURST;
+#endif
 	int af;		/* address family on the wire */
 	int iphdrlen;
 	int has_tso4, has_tso6;
@@ -596,22 +598,28 @@ tcp_output(struct tcpcb *tp)
 	 *	larger value for the restart window should never increase
 	 *	the size of cwnd).
 	 */
-	if (tcp_cwm) {
+	if (idle && (tcp_now - tp->t_rcvtime) >= tp->t_rxtcur) {
 		/*
-		 * Hughes/Touch/Heidemann Congestion Window Monitoring.
-		 * Count the number of packets currently pending
-		 * acknowledgement, and limit our congestion window
-		 * to a pre-determined allowed burst size plus that count.
-		 * This prevents bursting once all pending packets have
-		 * been acknowledged (i.e. transmission is idle).
-		 *
-		 * XXX Link this to Initial Window?
+		 * RFC2861 Section 2, limit ssthresh to 3/4 of
+		 * the old cwnd to not exit slow-start prematurely.
 		 */
-		tp->snd_cwnd = uimin(tp->snd_cwnd,
-		    (tcp_cwm_burstsize * txsegsize) +
-		    (tp->snd_nxt - tp->snd_una));
-	} else {
-		if (idle && (tcp_now - tp->t_rcvtime) >= tp->t_rxtcur) {
+		tp->snd_ssthresh = ulmax(tp->snd_ssthresh,
+		    tp->snd_cwnd * 3 / 4);
+
+		if (tcp_cwm) {
+			/*
+			 * Hughes/Touch/Heidemann Congestion Window Monitoring.
+			 * Count the number of packets currently pending
+			 * acknowledgement, and limit our congestion window
+			 * to a pre-determined allowed burst size plus that
+			 * count.
+			 * This prevents bursting once all pending packets have
+			 * been acknowledged (i.e. transmission is idle).
+			 */
+				tp->snd_cwnd = uimin(tp->snd_cwnd,
+				    (tcp_cwm_burstsize * txsegsize) +
+				    (tp->snd_nxt - tp->snd_una));
+		} else {
 			/*
 			 * We have been idle for "a while" and no acks are
 			 * expected to clock out any data we send --
@@ -770,7 +778,11 @@ again:
 	}
 
 	if (sack_rxmit == 0) {
+#if 0
 		if (TCP_SACK_ENABLED(tp) && tp->t_partialacks >= 0) {
+#else
+		if (sack_bytes_rxmt > 0) {
+#endif
 			long cwin;
 
 			/*
@@ -796,7 +808,7 @@ again:
 			 */
 			if (len > 0) {
 				cwin = tp->snd_cwnd -
-				    (tp->snd_nxt - tp->sack_newdata) -
+				    imax(0, (tp->snd_nxt - tp->sack_newdata)) -
 				    sack_bytes_rxmt;
 				if (cwin < 0)
 					cwin = 0;
@@ -879,9 +891,11 @@ again:
 		    so->so_snd.sb_cc >= (so->so_snd.sb_hiwat / 8 * 7) &&
 		    so->so_snd.sb_cc < tcp_autosndbuf_max &&
 		    win >= (so->so_snd.sb_cc - (tp->snd_nxt - tp->snd_una))) {
-			if (!sbreserve(&so->so_snd,
-			    uimin(so->so_snd.sb_hiwat + tcp_autosndbuf_inc,
-			     tcp_autosndbuf_max), so))
+			u_long newsize = tcp_growbuf(
+			    so->so_snd.sb_hiwat,
+			    tcp_autosndbuf_inc,
+			    tcp_autosndbuf_max);
+			if (!sbreserve(&so->so_snd, newsize, so))
 				so->so_snd.sb_flags &= ~SB_AUTOSIZE;
 		}
 	}
@@ -1231,7 +1245,8 @@ reset:			TCP_REASS_UNLOCK(tp);
 	 * the template for sends on this connection.
 	 */
 	if (len) {
-		error = tcp_build_datapkt(tp, so, off, len, hdrlen, &m);
+		error = tcp_build_datapkt(tp, so, off, len, hdrlen, &m,
+		    sack_rxmit);
 		if (error)
 			goto out;
 		/*
@@ -1240,7 +1255,8 @@ reset:			TCP_REASS_UNLOCK(tp);
 		 * give data to the user when a buffer fills or
 		 * a PUSH comes in.)
 		 */
-		if (off + len == so->so_snd.sb_cc)
+		if ((so->so_state & SS_MORETOCOME) == 0 &&
+		    off + len == so->so_snd.sb_cc)
 			flags |= TH_PUSH;
 	} else {
 		tcps = TCP_STAT_GETREF();
@@ -1648,12 +1664,17 @@ out:
 	tp->last_ack_sent = tp->rcv_nxt;
 	tp->t_flags &= ~TF_ACKNOW;
 	TCP_CLEAR_DELACK(tp);
+#if 0
 #ifdef DIAGNOSTIC
 	if (maxburst < 0)
 		printf("tcp_output: maxburst exceeded by %d\n", -maxburst);
 #endif
 	if (sendalot && (tp->t_congctl == &tcp_reno_ctl || --maxburst))
 		goto again;
+#else
+	if (sendalot)
+		goto again;
+#endif
 	return 0;
 }
 
Index: sys/netinet/tcp_var.h
===================================================================
RCS file: /cvsroot/src/sys/netinet/tcp_var.h,v
retrieving revision 1.199
diff -p -u -r1.199 tcp_var.h
--- sys/netinet/tcp_var.h	3 Dec 2024 20:02:30 -0000	1.199
+++ sys/netinet/tcp_var.h	3 Apr 2026 07:38:28 -0000
@@ -785,6 +785,21 @@ extern int tcp_do_autosndbuf;
 extern int tcp_autosndbuf_inc;
 extern int tcp_autosndbuf_max;
 
+static __inline u_long
+tcp_growbuf(u_long current, int increment, int limit)
+{
+	if (increment < 0) {
+		uint64_t adj;
+
+		adj = (uint64_t)current * -increment / 65536;
+		current += adj;
+	} else {
+		current += increment;
+	}
+
+	return uimin(current, limit); 
+}
+
 struct secasvar;
 
 void	 tcp_canceltimers(struct tcpcb *);
