git: 7146ad04f18e - main - tcp: refactor tcp_autosndbuf() and associated sysctls
Date: Thu, 22 Oct 2026 03:31:25 UTC
The branch main has been updated by glebius:
URL: https://cgit.FreeBSD.org/src/commit/?id=7146ad04f18e7f0767a4d44cff43c1c67ea98cec
commit 7146ad04f18e7f0767a4d44cff43c1c67ea98cec
Author: Gleb Smirnoff <glebius@FreeBSD.org>
AuthorDate: 2026-10-22 03:27:47 +0000
Commit: Gleb Smirnoff <glebius@FreeBSD.org>
CommitDate: 2026-10-22 03:27:47 +0000
tcp: refactor tcp_autosndbuf() and associated sysctls
Rename tcp_sndbuf_autoscale() to tcp_autosndbuf(). This matches
complementary tcp_autorcvbuf() that was factored out as a separate
function a few months before tcp_sndbuf_autoscale() was. Move the large
comment to the header of the function. Use unsigned types for all sizing
sysctls and variables and use bool for the switching sysctl. Add a
comment above net.inet.tcp.sendbuf_auto_lowat (default to off) providing a
reference to its origins. Add DTrace probe tcp::send-autoresize,
complementary to the existing tcp::receive-autoresize.
Rack has its own function, just enable the DTrace probe there.
Reviewed by: tuexen
Differential Revision: https://reviews.freebsd.org/D60732
---
sys/netinet/in_kdtrace.c | 4 ++
sys/netinet/in_kdtrace.h | 1 +
sys/netinet/tcp_output.c | 142 +++++++++++++++++++++---------------------
sys/netinet/tcp_stacks/bbr.c | 2 +-
sys/netinet/tcp_stacks/rack.c | 5 +-
sys/netinet/tcp_var.h | 10 +--
6 files changed, 85 insertions(+), 79 deletions(-)
diff --git a/sys/netinet/in_kdtrace.c b/sys/netinet/in_kdtrace.c
index a44b4c8ed476..063ca3205aee 100644
--- a/sys/netinet/in_kdtrace.c
+++ b/sys/netinet/in_kdtrace.c
@@ -543,6 +543,10 @@ SDT_PROBE_DEFINE2_XLATE(tcp, , , receive__autoresize,
"struct tcpcb *", "tcpsinfo_t *" ,
"u_int", "u_int");
+SDT_PROBE_DEFINE2_XLATE(tcp, , , send__autoresize,
+ "struct tcpcb *", "tcpsinfo_t *" ,
+ "u_int", "u_int");
+
SDT_PROBE_DEFINE5_XLATE(udp, , , receive,
"void *", "pktinfo_t *",
"struct inpcb *", "csinfo_t *",
diff --git a/sys/netinet/in_kdtrace.h b/sys/netinet/in_kdtrace.h
index 08fab3949b5c..01e6ed731982 100644
--- a/sys/netinet/in_kdtrace.h
+++ b/sys/netinet/in_kdtrace.h
@@ -442,6 +442,7 @@ SDT_PROBE_DECLARE(tcp, , , debug__output);
SDT_PROBE_DECLARE(tcp, , , debug__user);
SDT_PROBE_DECLARE(tcp, , , debug__drop);
SDT_PROBE_DECLARE(tcp, , , receive__autoresize);
+SDT_PROBE_DECLARE(tcp, , , send__autoresize);
SDT_PROBE_DECLARE(udp, , , receive);
SDT_PROBE_DECLARE(udp, , , send);
diff --git a/sys/netinet/tcp_output.c b/sys/netinet/tcp_output.c
index 09295544855f..d11a2b745382 100644
--- a/sys/netinet/tcp_output.c
+++ b/sys/netinet/tcp_output.c
@@ -107,31 +107,31 @@ SYSCTL_INT(_net_inet_tcp, OID_AUTO, tso, CTLFLAG_VNET | CTLFLAG_RW,
&VNET_NAME(tcp_do_tso), 0,
"Enable TCP Segmentation Offload");
-VNET_DEFINE(int, tcp_sendspace) = 1024*32;
+VNET_DEFINE(u_int, tcp_sendspace) = 1024*32;
#define V_tcp_sendspace VNET(tcp_sendspace)
-SYSCTL_INT(_net_inet_tcp, TCPCTL_SENDSPACE, sendspace, CTLFLAG_VNET | CTLFLAG_RW,
- &VNET_NAME(tcp_sendspace), 0, "Initial send socket buffer size");
+SYSCTL_UINT(_net_inet_tcp, TCPCTL_SENDSPACE, sendspace,
+ CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(tcp_sendspace), 0,
+ "Initial send socket buffer size");
-VNET_DEFINE(int, tcp_do_autosndbuf) = 1;
-SYSCTL_INT(_net_inet_tcp, OID_AUTO, sendbuf_auto, CTLFLAG_VNET | CTLFLAG_RW,
- &VNET_NAME(tcp_do_autosndbuf), 0,
- "Enable automatic send buffer sizing");
+VNET_DEFINE(bool, tcp_do_autosndbuf) = true;
+SYSCTL_BOOL(_net_inet_tcp, OID_AUTO, sendbuf_auto, CTLFLAG_VNET | CTLFLAG_RW,
+ &VNET_NAME(tcp_do_autosndbuf), 0, "Enable automatic send buffer sizing");
-VNET_DEFINE(int, tcp_autosndbuf_inc) = 8*1024;
-SYSCTL_INT(_net_inet_tcp, OID_AUTO, sendbuf_inc, CTLFLAG_VNET | CTLFLAG_RW,
- &VNET_NAME(tcp_autosndbuf_inc), 0,
- "Incrementor step size of automatic send buffer");
+VNET_DEFINE(u_int, tcp_autosndbuf_inc) = 8*1024;
+SYSCTL_UINT(_net_inet_tcp, OID_AUTO, sendbuf_inc, CTLFLAG_VNET | CTLFLAG_RW,
+ &VNET_NAME(tcp_autosndbuf_inc), 0,
+ "Incrementor step size of automatic send buffer");
-VNET_DEFINE(int, tcp_autosndbuf_max) = 8*1024*1024;
-SYSCTL_INT(_net_inet_tcp, OID_AUTO, sendbuf_max, CTLFLAG_VNET | CTLFLAG_RW,
- &VNET_NAME(tcp_autosndbuf_max), 0,
- "Max size of automatic send buffer");
+VNET_DEFINE(u_int, tcp_autosndbuf_max) = 8*1024*1024;
+SYSCTL_UINT(_net_inet_tcp, OID_AUTO, sendbuf_max, CTLFLAG_VNET | CTLFLAG_RW,
+ &VNET_NAME(tcp_autosndbuf_max), 0, "Max size of automatic send buffer");
-VNET_DEFINE(int, tcp_sendbuf_auto_lowat) = 0;
+/* See D11016, used by LimeLight. */
+VNET_DEFINE_STATIC(bool, tcp_sendbuf_auto_lowat) = false;
#define V_tcp_sendbuf_auto_lowat VNET(tcp_sendbuf_auto_lowat)
-SYSCTL_INT(_net_inet_tcp, OID_AUTO, sendbuf_auto_lowat, CTLFLAG_VNET | CTLFLAG_RW,
- &VNET_NAME(tcp_sendbuf_auto_lowat), 0,
- "Modify threshold for auto send buffer growth to account for SO_SNDLOWAT");
+SYSCTL_BOOL(_net_inet_tcp, OID_AUTO, sendbuf_auto_lowat,
+ CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(tcp_sendbuf_auto_lowat), 0,
+ "Modify threshold for auto send buffer growth to account for SO_SNDLOWAT");
/*
* Make sure that either retransmit or persist timer is set for SYN, FIN and
@@ -508,7 +508,7 @@ after_sack_rexmit:
/* len will be >= 0 after this point. */
KASSERT(len >= 0, ("[%s:%d]: len < 0", __func__, __LINE__));
- tcp_sndbuf_autoscale(tp, so, sendwin);
+ tcp_autosndbuf(tp, sendwin);
/*
* Decide if we can use TCP Segmentation Offloading (if supported by
@@ -2119,61 +2119,59 @@ nospace:
return (NULL);
}
+/*
+ * Automatic sizing of send socket buffer. Often the send buffer size is not
+ * optimally adjusted to the actual network conditions at hand (delay bandwidth
+ * product). Setting the buffer size too small limits throughput on links with
+ * high bandwidth and high delay (eg. trans-continental/oceanic links).
+ * Setting the buffer size too big consumes too much real kernel memory,
+ * especially with many connections on busy servers.
+ *
+ * The criteria to step up the send buffer one notch are:
+ * 1. receive window of remote host is larger than send buffer
+ * (with a fudge factor of 5/4th);
+ * 2. send buffer is filled to 7/8th with data (so we actually
+ * have data to make use of it);
+ * 3. send buffer fill has not hit maximal automatic size;
+ * 4. our send window (slow start and cogestion controlled) is
+ * larger than sent but unacknowledged data in send buffer.
+ *
+ * The remote host receive window scaling factor may limit the growing of the
+ * send buffer before it reaches its allowed maximum.
+ *
+ * It scales directly with slow start or congestion window and does at most one
+ * step per received ACK. This fast scaling has the drawback of growing the
+ * send buffer beyond what is strictly necessary to make full use of a given
+ * delay*bandwidth product. However testing has shown this not to be much of
+ * an problem. At worst we are trading wasting of available bandwidth (the
+ * non-use of it) for wasting some socket buffer memory.
+ *
+ * TODO: Shrink send buffer during idle periods together with congestion window.
+ */
void
-tcp_sndbuf_autoscale(struct tcpcb *tp, struct socket *so, uint32_t sendwin)
+tcp_autosndbuf(struct tcpcb *tp, u_int sendwin)
{
+ struct socket *so = tptosocket(tp);
+ u_int lowat, newsize;
- /*
- * Automatic sizing of send socket buffer. Often the send buffer
- * size is not optimally adjusted to the actual network conditions
- * at hand (delay bandwidth product). Setting the buffer size too
- * small limits throughput on links with high bandwidth and high
- * delay (eg. trans-continental/oceanic links). Setting the
- * buffer size too big consumes too much real kernel memory,
- * especially with many connections on busy servers.
- *
- * The criteria to step up the send buffer one notch are:
- * 1. receive window of remote host is larger than send buffer
- * (with a fudge factor of 5/4th);
- * 2. send buffer is filled to 7/8th with data (so we actually
- * have data to make use of it);
- * 3. send buffer fill has not hit maximal automatic size;
- * 4. our send window (slow start and cogestion controlled) is
- * larger than sent but unacknowledged data in send buffer.
- *
- * The remote host receive window scaling factor may limit the
- * growing of the send buffer before it reaches its allowed
- * maximum.
- *
- * It scales directly with slow start or congestion window
- * and does at most one step per received ACK. This fast
- * scaling has the drawback of growing the send buffer beyond
- * what is strictly necessary to make full use of a given
- * delay*bandwidth product. However testing has shown this not
- * to be much of an problem. At worst we are trading wasting
- * of available bandwidth (the non-use of it) for wasting some
- * socket buffer memory.
- *
- * TODO: Shrink send buffer during idle periods together
- * with congestion window. Requires another timer. Has to
- * wait for upcoming tcp timer rewrite.
- *
- * XXXGL: should there be used sbused() or sbavail()?
- */
- if (V_tcp_do_autosndbuf && so->so_snd.sb_flags & SB_AUTOSIZE) {
- int lowat;
-
- lowat = V_tcp_sendbuf_auto_lowat ? so->so_snd.sb_lowat : 0;
- if ((tp->snd_wnd / 4 * 5) >= so->so_snd.sb_hiwat - lowat &&
- sbused(&so->so_snd) >=
- (so->so_snd.sb_hiwat / 8 * 7) - lowat &&
- sbused(&so->so_snd) < V_tcp_autosndbuf_max &&
- sendwin >= (sbused(&so->so_snd) -
- (tp->snd_nxt - tp->snd_una))) {
- if (!sbreserve_locked(so, SO_SND,
- min(so->so_snd.sb_hiwat + V_tcp_autosndbuf_inc,
- V_tcp_autosndbuf_max), curthread))
- so->so_snd.sb_flags &= ~SB_AUTOSIZE;
+ SOCK_RECVBUF_LOCK_ASSERT(so);
+
+ if (!V_tcp_do_autosndbuf || (so->so_snd.sb_flags & SB_AUTOSIZE) == 0)
+ return;
+
+
+ lowat = V_tcp_sendbuf_auto_lowat ? so->so_snd.sb_lowat : 0;
+ if ((tp->snd_wnd / 4 * 5) >= so->so_snd.sb_hiwat - lowat &&
+ sbused(&so->so_snd) >=
+ (so->so_snd.sb_hiwat / 8 * 7) - lowat &&
+ sbused(&so->so_snd) < V_tcp_autosndbuf_max &&
+ sendwin >= (sbused(&so->so_snd) - (tp->snd_nxt - tp->snd_una))) {
+ newsize = min(so->so_snd.sb_hiwat + V_tcp_autosndbuf_inc,
+ V_tcp_autosndbuf_max);
+ if (sbreserve_locked(so, SO_SND, newsize, curthread)) {
+ TCP_PROBE2(send__autoresize, tp, newsize);
+ } else {
+ so->so_snd.sb_flags &= ~SB_AUTOSIZE;
}
}
}
diff --git a/sys/netinet/tcp_stacks/bbr.c b/sys/netinet/tcp_stacks/bbr.c
index a055f6d756ac..5169b6104393 100644
--- a/sys/netinet/tcp_stacks/bbr.c
+++ b/sys/netinet/tcp_stacks/bbr.c
@@ -12510,7 +12510,7 @@ recheck_resend:
}
/* len will be >= 0 after this point. */
KASSERT(len >= 0, ("[%s:%d]: len < 0", __func__, __LINE__));
- tcp_sndbuf_autoscale(tp, so, sendwin);
+ tcp_autosndbuf(tp, sendwin);
/*
*
*/
diff --git a/sys/netinet/tcp_stacks/rack.c b/sys/netinet/tcp_stacks/rack.c
index 227c765b1cee..d817e97e4261 100644
--- a/sys/netinet/tcp_stacks/rack.c
+++ b/sys/netinet/tcp_stacks/rack.c
@@ -18915,8 +18915,11 @@ rack_sndbuf_autoscale(struct tcp_rack *rack)
scaleup += so->so_snd.sb_hiwat;
if (scaleup > V_tcp_autosndbuf_max)
scaleup = V_tcp_autosndbuf_max;
- if (!sbreserve_locked(so, SO_SND, scaleup, curthread))
+ if (sbreserve_locked(so, SO_SND, scaleup, curthread)) {
+ TCP_PROBE2(send__autoresize, tp, scaleup);
+ } else {
so->so_snd.sb_flags &= ~SB_AUTOSIZE;
+ }
}
}
}
diff --git a/sys/netinet/tcp_var.h b/sys/netinet/tcp_var.h
index 75efd8eb92e0..daca017905ed 100644
--- a/sys/netinet/tcp_var.h
+++ b/sys/netinet/tcp_var.h
@@ -1304,12 +1304,12 @@ VNET_DECLARE(int, tcp_abc_l_var);
VNET_DECLARE(uint32_t, tcp_ack_war_cnt);
VNET_DECLARE(uint32_t, tcp_ack_war_time_window);
VNET_DECLARE(u_int, tcp_autorcvbuf_max);
-VNET_DECLARE(int, tcp_autosndbuf_inc);
-VNET_DECLARE(int, tcp_autosndbuf_max);
+VNET_DECLARE(u_int, tcp_autosndbuf_inc);
+VNET_DECLARE(u_int, tcp_autosndbuf_max);
VNET_DECLARE(int, tcp_bind_all_fibs);
VNET_DECLARE(int, tcp_delack_enabled);
-VNET_DECLARE(int, tcp_do_autosndbuf);
VNET_DECLARE(bool, tcp_do_autorcvbuf);
+VNET_DECLARE(bool, tcp_do_autosndbuf);
VNET_DECLARE(int, tcp_do_ecn);
VNET_DECLARE(int, tcp_do_lrd);
VNET_DECLARE(int, tcp_do_prr);
@@ -1343,7 +1343,7 @@ VNET_DECLARE(int, tcp_sack_globalmaxholes);
VNET_DECLARE(int, tcp_sack_maxholes);
VNET_DECLARE(int, tcp_sack_tso);
VNET_DECLARE(int, tcp_sc_rst_sock_fail);
-VNET_DECLARE(int, tcp_sendspace);
+VNET_DECLARE(u_int, tcp_sendspace);
VNET_DECLARE(int, tcp_udp_tunneling_overhead);
VNET_DECLARE(int, tcp_udp_tunneling_port);
VNET_DECLARE(struct inpcbinfo, tcbinfo);
@@ -1404,6 +1404,7 @@ VNET_DECLARE(struct hhook_head *, tcp_hhh[HHOOK_TCP_LAST + 1]);
#endif
void tcp_autorcvbuf(struct tcpcb *, u_int);
+void tcp_autosndbuf(struct tcpcb *, u_int);
void tcp_account_for_send(struct tcpcb *, uint32_t, uint8_t, uint8_t, bool);
int tcp_addoptions(struct tcpopt *, u_char *);
struct tcpcb *
@@ -1553,7 +1554,6 @@ void tcp_sack_lost_retransmission(struct tcpcb *, struct tcphdr *);
int tcp_newreno(struct tcpcb *, struct tcphdr *);
int tcp_compute_pipe(struct tcpcb *);
uint32_t tcp_compute_initwnd(uint32_t);
-void tcp_sndbuf_autoscale(struct tcpcb *, struct socket *, uint32_t);
int tcp_stats_sample_rollthedice(struct tcpcb *tp, void *seed_bytes,
size_t seed_len);
int tcp_can_enable_pacing(void);