Linux Kernel Selftest development
 help / color / mirror / Atom feed
* [PATCH net-next v9 0/2] Explicit TSO segment count
@ 2026-08-31 16:52 chia-yu.chang
  2026-08-31 16:52 ` [PATCH net-next v9 1/2] tcp: Replace min_tso_segs() with tso_segs() CC callback chia-yu.chang
  2026-08-31 16:52 ` [PATCH net-next v9 2/2] bpf: make tcp_tso_autosize() available to BPF congestion controls chia-yu.chang
  0 siblings, 2 replies; 5+ messages in thread
From: chia-yu.chang @ 2026-08-31 16:52 UTC (permalink / raw)
  To: ihor.solodrai, john.fastabend, jakub, jiayuan.chen, netdev, bpf,
	ast, daniel, andrii, eddyz87, martin.lau, song, yonghong.song,
	jolsa, emil, linux-kselftest, shuah, horms, dsahern, pabeni, jhs,
	stephen, davem, edumazet, andrew+netdev, donald.hunter, kuniyu,
	ij, ncardwell, koen.de_schepper, g.white, ingemar.s.johansson,
	mirja.kuehlewind, cheshire, rs.ietf, Jason_Livingood, vidhi_goel,
	Mike_Rudolph, Jeff_Howe, srichard
  Cc: Chia-Yu Chang

From: Chia-Yu Chang <chia-yu.chang@nokia-bell-labs.com>

Hello,

This series replaces the existing min_tso_segs() congestion control
callback with a new tso_segs() callback that allows congestion control
algorithms to provide an explicit TSO segment count for each data burst.

To support BPF congestion controls, the series also exposes
tcp_tso_autosize() as a BPF kfunc, allowing BPF implementations to
reuse the kernel TSO autosizing logic while implementing custom
tso_segs() callbacks.

Changes since v8:
- Remove deprecated flag on bbr_min_tso_segs()

Thanks,
Chia-Yu

---
Chia-Yu Chang (2):
  tcp: Replace min_tso_segs() with tso_segs() CC callback
  bpf: make tcp_tso_autosize() available to BPF congestion controls

 include/net/tcp.h                             | 15 ++++++++--
 net/ipv4/bpf_tcp_ca.c                         |  5 ++--
 net/ipv4/tcp_bbr.c                            | 13 +++++++--
 net/ipv4/tcp_output.c                         | 28 ++++++++++++-------
 .../selftests/bpf/progs/tcp_ca_kfunc.c        |  8 +++---
 5 files changed, 48 insertions(+), 21 deletions(-)

-- 
2.34.1


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH net-next v9 1/2] tcp: Replace min_tso_segs() with tso_segs() CC callback
  2026-08-31 16:52 [PATCH net-next v9 0/2] Explicit TSO segment count chia-yu.chang
@ 2026-08-31 16:52 ` chia-yu.chang
  2026-09-03 19:57   ` [net-next,v9,1/2] " netdev-bot+sashiko
  2026-08-31 16:52 ` [PATCH net-next v9 2/2] bpf: make tcp_tso_autosize() available to BPF congestion controls chia-yu.chang
  1 sibling, 1 reply; 5+ messages in thread
From: chia-yu.chang @ 2026-08-31 16:52 UTC (permalink / raw)
  To: ihor.solodrai, john.fastabend, jakub, jiayuan.chen, netdev, bpf,
	ast, daniel, andrii, eddyz87, martin.lau, song, yonghong.song,
	jolsa, emil, linux-kselftest, shuah, horms, dsahern, pabeni, jhs,
	stephen, davem, edumazet, andrew+netdev, donald.hunter, kuniyu,
	ij, ncardwell, koen.de_schepper, g.white, ingemar.s.johansson,
	mirja.kuehlewind, cheshire, rs.ietf, Jason_Livingood, vidhi_goel,
	Mike_Rudolph, Jeff_Howe, srichard
  Cc: Chia-Yu Chang

From: Chia-Yu Chang <chia-yu.chang@nokia-bell-labs.com>

This patch replaces the existing min_tso_segs() callback with a new
tso_segs() callback, allowing congestion control algorithms to provide
an explicit TSO segment count for each data burst and bypass
tcp_tso_autosize(). The resulting tso_segs value is clamped to
[1, sk->sk_gso_max_segs], preventing congestion-control implementations
from returning an invalid zero-segment value.

This change has the following impacts on BPF struct_ops users:
- The callback is renamed from min_tso_segs() to tso_segs()
- The signature gains an extra u32 mss_now argument
- The return value semantics is changed from "floor value passed into
  tcp_tso_autosize()" to "final tso_segs value", bypassing autosizing

As a result, existing BPF programs must be updated, because returning a
small constant will now directly limit the final tso_segs value instead
of specifying the minimum value passed to tcp_tso_autosize().

Signed-off-by: Chia-Yu Chang <chia-yu.chang@nokia-bell-labs.com>
Signed-off-by: Ilpo Järvinen <ij@kernel.org>
Reviewed-by: Emil Tsalapatis <emil@etsalapatis.com>

---
v9:
- Remove deprecated flag on bbr_min_tso_segs()

v8:
- Deprecate bbr_min_tso_segs() kfunc instead of removing it
- Add bbr_tso_segs() kfunc
- Update tso_segs() callback documentation
- Document tso_segs value clamping
- Update commit messages

v7:
- Update the comments for tso_segs()
- Restore bpf_tcp_ca_tso_segs() to return 0
- Move READ_ONCE() to the else branch if ca_ops->tso_segs() is undefined
- Update tcp_tso_autosize() to use EXPORT_SYMBOL_GPL

v6:
- Add clamp_t to avoid returning 0 by ca_ops->tso_segs()
- Update commit message

v5:
- Revert back to v3 and add Reviewed-by tag

v4:
- Use union for both min_tso_segs() and tso_segs() and a

v3:
- Update bpf_tcp_ca_tso_segs() to use tcp_tso_autosize()
- Add divide by 0 protection in case of mss_now=0

v2:
- Export tcp_tso_autosize()
---
 include/net/tcp.h                              | 15 +++++++++++++--
 net/ipv4/bpf_tcp_ca.c                          |  4 ++--
 net/ipv4/tcp_bbr.c                             | 13 ++++++++++---
 net/ipv4/tcp_output.c                          | 18 +++++++++---------
 .../testing/selftests/bpf/progs/tcp_ca_kfunc.c |  8 ++++----
 5 files changed, 38 insertions(+), 20 deletions(-)

diff --git a/include/net/tcp.h b/include/net/tcp.h
index 670c20876f26..79f27ac9e963 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -824,6 +824,9 @@ unsigned int tcp_sync_mss(struct sock *sk, u32 pmtu);
 unsigned int tcp_current_mss(struct sock *sk);
 u32 tcp_clamp_probe0_to_user_timeout(const struct sock *sk, u32 when);
 
+u32 tcp_tso_autosize(const struct sock *sk, unsigned int mss_now,
+		     int min_tso_segs);
+
 /* Bound MSS / TSO packet size with the half of the window */
 static inline int tcp_bound_to_half_wnd(struct tcp_sock *tp, int pktsize)
 {
@@ -1361,8 +1364,16 @@ struct tcp_congestion_ops {
 	/* hook for packet ack accounting (optional) */
 	void (*pkts_acked)(struct sock *sk, const struct ack_sample *sample);
 
-	/* override sysctl_tcp_min_tso_segs (optional) */
-	u32 (*min_tso_segs)(struct sock *sk);
+	/* Override tcp_tso_autosize() (optional)
+	 *
+	 * If provided, this callback supplies the TSO segment target count
+	 * instead of using tcp_tso_autosize(). The returned value is
+	 * subsequently clamped to [1, sk->sk_gso_max_segs] by the caller.
+	 *
+	 * For the kernel callback path, mss_now originates from
+	 * tcp_current_mss() and should never be zero.
+	 */
+	u32 (*tso_segs)(struct sock *sk, u32 mss_now);
 
 	/* new value of cwnd after loss (required) */
 	u32  (*undo_cwnd)(struct sock *sk);
diff --git a/net/ipv4/bpf_tcp_ca.c b/net/ipv4/bpf_tcp_ca.c
index 791e15063237..ed4fea98dfde 100644
--- a/net/ipv4/bpf_tcp_ca.c
+++ b/net/ipv4/bpf_tcp_ca.c
@@ -284,7 +284,7 @@ static void bpf_tcp_ca_pkts_acked(struct sock *sk, const struct ack_sample *samp
 {
 }
 
-static u32 bpf_tcp_ca_min_tso_segs(struct sock *sk)
+static u32 bpf_tcp_ca_tso_segs(struct sock *sk, u32 mss_now)
 {
 	return 0;
 }
@@ -320,7 +320,7 @@ static struct tcp_congestion_ops __bpf_ops_tcp_congestion_ops = {
 	.cwnd_event_tx_start = bpf_tcp_ca_cwnd_event_tx_start,
 	.in_ack_event = bpf_tcp_ca_in_ack_event,
 	.pkts_acked = bpf_tcp_ca_pkts_acked,
-	.min_tso_segs = bpf_tcp_ca_min_tso_segs,
+	.tso_segs = bpf_tcp_ca_tso_segs,
 	.cong_control = bpf_tcp_ca_cong_control,
 	.undo_cwnd = bpf_tcp_ca_undo_cwnd,
 	.sndbuf_expand = bpf_tcp_ca_sndbuf_expand,
diff --git a/net/ipv4/tcp_bbr.c b/net/ipv4/tcp_bbr.c
index 82378a2bfd1e..ecf11be46f38 100644
--- a/net/ipv4/tcp_bbr.c
+++ b/net/ipv4/tcp_bbr.c
@@ -297,11 +297,18 @@ static void bbr_set_pacing_rate(struct sock *sk, u32 bw, int gain)
 }
 
 /* override sysctl_tcp_min_tso_segs */
-__bpf_kfunc static u32 bbr_min_tso_segs(struct sock *sk)
+static u32 bbr_min_tso_segs(struct sock *sk)
 {
 	return READ_ONCE(sk->sk_pacing_rate) < (bbr_min_tso_rate >> 3) ? 1 : 2;
 }
 
+__bpf_kfunc static u32 bbr_tso_segs(struct sock *sk, u32 mss_now)
+{
+	if (unlikely(!mss_now))
+		return bbr_min_tso_segs(sk);
+	return tcp_tso_autosize(sk, mss_now, bbr_min_tso_segs(sk));
+}
+
 static u32 bbr_tso_segs_goal(struct sock *sk)
 {
 	struct tcp_sock *tp = tcp_sk(sk);
@@ -1151,7 +1158,7 @@ static struct tcp_congestion_ops tcp_bbr_cong_ops __read_mostly = {
 	.undo_cwnd	= bbr_undo_cwnd,
 	.cwnd_event_tx_start	= bbr_cwnd_event_tx_start,
 	.ssthresh	= bbr_ssthresh,
-	.min_tso_segs	= bbr_min_tso_segs,
+	.tso_segs	= bbr_tso_segs,
 	.get_info	= bbr_get_info,
 	.set_state	= bbr_set_state,
 };
@@ -1163,7 +1170,7 @@ BTF_ID_FLAGS(func, bbr_sndbuf_expand)
 BTF_ID_FLAGS(func, bbr_undo_cwnd)
 BTF_ID_FLAGS(func, bbr_cwnd_event_tx_start)
 BTF_ID_FLAGS(func, bbr_ssthresh)
-BTF_ID_FLAGS(func, bbr_min_tso_segs)
+BTF_ID_FLAGS(func, bbr_tso_segs)
 BTF_ID_FLAGS(func, bbr_set_state)
 BTF_KFUNCS_END(tcp_bbr_check_kfunc_ids)
 
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index 6f4dca4a4de9..b1b493d46b9c 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -2253,8 +2253,8 @@ static bool tcp_nagle_check(bool partial, const struct tcp_sock *tp,
  * for every 2^9 usec (aka 512 us) of RTT, so that the RTT-based allowance
  * is below 1500 bytes after 6 * ~500 usec = 3ms.
  */
-static u32 tcp_tso_autosize(const struct sock *sk, unsigned int mss_now,
-			    int min_tso_segs)
+u32 tcp_tso_autosize(const struct sock *sk, unsigned int mss_now,
+		     int min_tso_segs)
 {
 	unsigned long bytes;
 	u32 r;
@@ -2269,6 +2269,7 @@ static u32 tcp_tso_autosize(const struct sock *sk, unsigned int mss_now,
 
 	return max_t(u32, bytes / mss_now, min_tso_segs);
 }
+EXPORT_SYMBOL_GPL(tcp_tso_autosize);
 
 /* Return the number of segments we want in the skb we are transmitting.
  * See if congestion control module wants to decide; otherwise, autosize.
@@ -2276,14 +2277,13 @@ static u32 tcp_tso_autosize(const struct sock *sk, unsigned int mss_now,
 static u32 tcp_tso_segs(struct sock *sk, unsigned int mss_now)
 {
 	const struct tcp_congestion_ops *ca_ops = inet_csk(sk)->icsk_ca_ops;
-	u32 min_tso, tso_segs;
+	u32 tso_segs;
 
-	min_tso = ca_ops->min_tso_segs ?
-			ca_ops->min_tso_segs(sk) :
-			READ_ONCE(sock_net(sk)->ipv4.sysctl_tcp_min_tso_segs);
-
-	tso_segs = tcp_tso_autosize(sk, mss_now, min_tso);
-	return min_t(u32, tso_segs, sk->sk_gso_max_segs);
+	tso_segs = ca_ops->tso_segs ?
+			ca_ops->tso_segs(sk, mss_now) :
+			tcp_tso_autosize(sk, mss_now,
+					 READ_ONCE(sock_net(sk)->ipv4.sysctl_tcp_min_tso_segs));
+	return clamp_t(u32, tso_segs, 1, sk->sk_gso_max_segs);
 }
 
 /* Returns the portion of skb which can be sent right away */
diff --git a/tools/testing/selftests/bpf/progs/tcp_ca_kfunc.c b/tools/testing/selftests/bpf/progs/tcp_ca_kfunc.c
index 0a3e9d35bf6f..58262e490336 100644
--- a/tools/testing/selftests/bpf/progs/tcp_ca_kfunc.c
+++ b/tools/testing/selftests/bpf/progs/tcp_ca_kfunc.c
@@ -10,7 +10,7 @@ extern u32 bbr_sndbuf_expand(struct sock *sk) __ksym;
 extern u32 bbr_undo_cwnd(struct sock *sk) __ksym;
 extern void bbr_cwnd_event_tx_start(struct sock *sk) __ksym;
 extern u32 bbr_ssthresh(struct sock *sk) __ksym;
-extern u32 bbr_min_tso_segs(struct sock *sk) __ksym;
+extern u32 bbr_tso_segs(struct sock *sk, u32 mss_now) __ksym;
 extern void bbr_set_state(struct sock *sk, u8 new_state) __ksym;
 
 extern void dctcp_init(struct sock *sk) __ksym;
@@ -90,9 +90,9 @@ u32 BPF_PROG(ssthresh, struct sock *sk)
 }
 
 SEC("struct_ops")
-u32 BPF_PROG(min_tso_segs, struct sock *sk)
+u32 BPF_PROG(tso_segs, struct sock *sk, u32 mss_now)
 {
-	return bbr_min_tso_segs(sk);
+	return bbr_tso_segs(sk, mss_now);
 }
 
 SEC("struct_ops")
@@ -120,7 +120,7 @@ struct tcp_congestion_ops tcp_ca_kfunc = {
 	.cwnd_event	= (void *)cwnd_event,
 	.cwnd_event_tx_start = (void *)cwnd_event_tx_start,
 	.ssthresh	= (void *)ssthresh,
-	.min_tso_segs	= (void *)min_tso_segs,
+	.tso_segs	= (void *)tso_segs,
 	.set_state	= (void *)set_state,
 	.pkts_acked     = (void *)pkts_acked,
 	.name		= "tcp_ca_kfunc",
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH net-next v9 2/2] bpf: make tcp_tso_autosize() available to BPF congestion controls
  2026-08-31 16:52 [PATCH net-next v9 0/2] Explicit TSO segment count chia-yu.chang
  2026-08-31 16:52 ` [PATCH net-next v9 1/2] tcp: Replace min_tso_segs() with tso_segs() CC callback chia-yu.chang
@ 2026-08-31 16:52 ` chia-yu.chang
  2026-09-03 19:57   ` [net-next,v9,2/2] " netdev-bot+sashiko
  1 sibling, 1 reply; 5+ messages in thread
From: chia-yu.chang @ 2026-08-31 16:52 UTC (permalink / raw)
  To: ihor.solodrai, john.fastabend, jakub, jiayuan.chen, netdev, bpf,
	ast, daniel, andrii, eddyz87, martin.lau, song, yonghong.song,
	jolsa, emil, linux-kselftest, shuah, horms, dsahern, pabeni, jhs,
	stephen, davem, edumazet, andrew+netdev, donald.hunter, kuniyu,
	ij, ncardwell, koen.de_schepper, g.white, ingemar.s.johansson,
	mirja.kuehlewind, cheshire, rs.ietf, Jason_Livingood, vidhi_goel,
	Mike_Rudolph, Jeff_Howe, srichard
  Cc: Chia-Yu Chang

From: Chia-Yu Chang <chia-yu.chang@nokia-bell-labs.com>

Expose tcp_tso_autosize() as a BPF kfunc and register it in the TCP
congestion-control kfunc set. This allows BPF congestion controls to
reuse the kernel TSO autosizing logic while applying their own
minimum TSO segment policy.

To make the kfunc robust against BPF-provided inputs, min_tso_segs is
sanitized to at least 1 and mss_now == 0 returns the sanitized minimum
value instead of performing autosizing.

Signed-off-by: Chia-Yu Chang <chia-yu.chang@nokia-bell-labs.com>

--
v8:
- Sanitize min_tso_segs in tcp_tso_autosize()
- Return sanitized min_tso_segs when mss_now == 0
- Update commit messages
---
 net/ipv4/bpf_tcp_ca.c |  1 +
 net/ipv4/tcp_output.c | 14 +++++++++++---
 2 files changed, 12 insertions(+), 3 deletions(-)

diff --git a/net/ipv4/bpf_tcp_ca.c b/net/ipv4/bpf_tcp_ca.c
index ed4fea98dfde..9deed2244c2d 100644
--- a/net/ipv4/bpf_tcp_ca.c
+++ b/net/ipv4/bpf_tcp_ca.c
@@ -194,6 +194,7 @@ BTF_ID_FLAGS(func, tcp_reno_cong_avoid)
 BTF_ID_FLAGS(func, tcp_reno_undo_cwnd)
 BTF_ID_FLAGS(func, tcp_slow_start)
 BTF_ID_FLAGS(func, tcp_cong_avoid_ai)
+BTF_ID_FLAGS(func, tcp_tso_autosize)
 BTF_KFUNCS_END(bpf_tcp_ca_check_kfunc_ids)
 
 static const struct btf_kfunc_id_set bpf_tcp_ca_kfunc_set = {
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index b1b493d46b9c..b7f2ee5e7a6b 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -2252,13 +2252,21 @@ static bool tcp_nagle_check(bool partial, const struct tcp_sock *tp,
  * in bigger TSO bursts. We we cut the RTT-based allowance in half
  * for every 2^9 usec (aka 512 us) of RTT, so that the RTT-based allowance
  * is below 1500 bytes after 6 * ~500 usec = 3ms.
+ *
+ * The min_tso_segs is floored to 1 to avoid surprising conversion. Also,
+ * BPF callers may pass mss_now == 0. In that case the function returns the
+ * sanitized min_tso_segs value and skips autosizing.
  */
-u32 tcp_tso_autosize(const struct sock *sk, unsigned int mss_now,
-		     int min_tso_segs)
+__bpf_kfunc u32 tcp_tso_autosize(const struct sock *sk, unsigned int mss_now,
+				 int min_tso_segs)
 {
+	u32 min_tso = max(min_tso_segs, 1);
 	unsigned long bytes;
 	u32 r;
 
+	if (unlikely(!mss_now))
+		return min_tso;
+
 	bytes = READ_ONCE(sk->sk_pacing_rate) >> READ_ONCE(sk->sk_pacing_shift);
 
 	r = tcp_min_rtt(tcp_sk(sk)) >> READ_ONCE(sock_net(sk)->ipv4.sysctl_tcp_tso_rtt_log);
@@ -2267,7 +2275,7 @@ u32 tcp_tso_autosize(const struct sock *sk, unsigned int mss_now,
 
 	bytes = min_t(unsigned long, bytes, sk->sk_gso_max_size);
 
-	return max_t(u32, bytes / mss_now, min_tso_segs);
+	return max_t(u32, bytes / mss_now, min_tso);
 }
 EXPORT_SYMBOL_GPL(tcp_tso_autosize);
 
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [net-next,v9,1/2] tcp: Replace min_tso_segs() with tso_segs() CC callback
  2026-08-31 16:52 ` [PATCH net-next v9 1/2] tcp: Replace min_tso_segs() with tso_segs() CC callback chia-yu.chang
@ 2026-09-03 19:57   ` netdev-bot+sashiko
  0 siblings, 0 replies; 5+ messages in thread
From: netdev-bot+sashiko @ 2026-09-03 19:57 UTC (permalink / raw)
  To: chia-yu.chang
  Cc: ihor.solodrai, john.fastabend, jakub, jiayuan.chen, netdev, bpf,
	ast, daniel, andrii, eddyz87, martin.lau, song, yonghong.song,
	jolsa, emil, linux-kselftest, shuah, horms, dsahern, pabeni, jhs,
	stephen, davem, edumazet, andrew+netdev, donald.hunter, kuniyu,
	ij, ncardwell, koen.de_schepper, g.white, ingemar.s.johansson,
	mirja.kuehlewind, cheshire, rs.ietf, Jason_Livingood, vidhi_goel,
	Mike_Rudolph, Jeff_Howe, srichard, kuba

Thank you for your contribution! Sashiko AI review found 1 potential
issue(s) to consider:

Critical: 0 · High: 0 · Medium: 1 · Low: 0

- [Medium] The patch silently removes the `bbr_min_tso_segs` BPF kfunc
  without a deprecation window and without mentioning it in the commit…

--

tcp: Replace min_tso_segs() with tso_segs() CC callback

The min_tso_segs() congestion control callback is replaced by a new
tso_segs() callback that takes mss_now and returns the final TSO segment
count, bypassing tcp_tso_autosize().  tcp_tso_autosize() is exported so
modules can still do the autosizing themselves, and BBR is converted to
the new callback.

> diff --git a/include/net/tcp.h b/include/net/tcp.h

[ ... ]

> diff --git a/net/ipv4/tcp_bbr.c b/net/ipv4/tcp_bbr.c
> index 82378a2bfd1e1..ecf11be46f382 100644
> --- a/net/ipv4/tcp_bbr.c
> +++ b/net/ipv4/tcp_bbr.c
> @@ -297,11 +297,18 @@ static void bbr_set_pacing_rate(struct sock *sk, u32 bw, int gain)
>  }
>  
>  /* override sysctl_tcp_min_tso_segs */
> -__bpf_kfunc static u32 bbr_min_tso_segs(struct sock *sk)
> +static u32 bbr_min_tso_segs(struct sock *sk)
>  {
>  	return READ_ONCE(sk->sk_pacing_rate) < (bbr_min_tso_rate >> 3) ? 1 : 2;
>  }
>  
> +__bpf_kfunc static u32 bbr_tso_segs(struct sock *sk, u32 mss_now)
> +{
> +	if (unlikely(!mss_now))
> +		return bbr_min_tso_segs(sk);
> +	return tcp_tso_autosize(sk, mss_now, bbr_min_tso_segs(sk));
> +}
> +

[ ... ]

> @@ -1163,7 +1170,7 @@ BTF_ID_FLAGS(func, bbr_sndbuf_expand)
>  BTF_ID_FLAGS(func, bbr_undo_cwnd)
>  BTF_ID_FLAGS(func, bbr_cwnd_event_tx_start)
>  BTF_ID_FLAGS(func, bbr_ssthresh)
> -BTF_ID_FLAGS(func, bbr_min_tso_segs)
> +BTF_ID_FLAGS(func, bbr_tso_segs)
>  BTF_ID_FLAGS(func, bbr_set_state)
>  BTF_KFUNCS_END(tcp_bbr_check_kfunc_ids)

[Severity: Medium]
Dropping the __bpf_kfunc annotation from bbr_min_tso_segs() and replacing its
BTF_ID_FLAGS() entry with bbr_tso_segs() takes the bbr_min_tso_segs kfunc out
of the kernel BTF kfunc set in one step.  Should this go through a
deprecation release instead?

Any out-of-tree BPF congestion control containing:

    extern u32 bbr_min_tso_segs(struct sock *sk) __ksym;

now fails to load with "kernel function bbr_min_tso_segs not found in kernel
BTF", and that applies to programs calling it from any callback, not only
from the renamed one.

The helper is still present and still called internally, both from
bbr_tso_segs() and from bbr_tso_segs_goal():

net/ipv4/tcp_bbr.c:bbr_tso_segs_goal() {
    ...
	segs = max_t(u32, bytes / tp->mss_cache, bbr_min_tso_segs(sk));
    ...
}

so would keeping the BTF_ID_FLAGS(func, bbr_min_tso_segs, KF_DEPRECATED)
entry for a release work here?  Documentation/bpf/kfuncs.rst says "It's
expected that the common case will be that kfuncs will go through a
deprecation period rather than being changed or removed without warning".

The v8 changelog of this submission said "Deprecate bbr_min_tso_segs() kfunc
instead of removing it", and v9 said "Remove deprecated flag on
bbr_min_tso_segs()", so the deprecation flag appears to have been dropped
again between revisions.  Was that intentional?

The commit message lists the BPF struct_ops impacts as:

    - The callback is renamed from min_tso_segs() to tso_segs()
    - The signature gains an extra u32 mss_now argument
    - The return value semantics is changed from "floor value passed into
      tcp_tso_autosize()" to "final tso_segs value", bypassing autosizing

Could the disappearance of the bbr_min_tso_segs kfunc be added to that list?

On the replacement side, the follow-up commit in this series, "bpf: make
tcp_tso_autosize() available to BPF congestion controls", marks
tcp_tso_autosize() as __bpf_kfunc and adds it to bpf_tcp_ca_check_kfunc_ids,
so a BPF congestion control can reproduce the old "raise the floor, keep
autosizing" behaviour by calling tcp_tso_autosize(sk, mss_now, my_floor)
from there on.  At this commit alone that kfunc is not yet registered, so
bbr_tso_segs() with BBR's hardcoded 1-or-2 floor is the only substitute.

In-tree users were updated in
tools/testing/selftests/bpf/progs/tcp_ca_kfunc.c and no other in-tree
reference to the old kfunc remains.

-- 
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260831165230.2696893-1-chia-yu.chang%40nokia-bell-labs.com

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [net-next,v9,2/2] bpf: make tcp_tso_autosize() available to BPF congestion controls
  2026-08-31 16:52 ` [PATCH net-next v9 2/2] bpf: make tcp_tso_autosize() available to BPF congestion controls chia-yu.chang
@ 2026-09-03 19:57   ` netdev-bot+sashiko
  0 siblings, 0 replies; 5+ messages in thread
From: netdev-bot+sashiko @ 2026-09-03 19:57 UTC (permalink / raw)
  To: chia-yu.chang
  Cc: ihor.solodrai, john.fastabend, jakub, jiayuan.chen, netdev, bpf,
	ast, daniel, andrii, eddyz87, martin.lau, song, yonghong.song,
	jolsa, emil, linux-kselftest, shuah, horms, dsahern, pabeni, jhs,
	stephen, davem, edumazet, andrew+netdev, donald.hunter, kuniyu,
	ij, ncardwell, koen.de_schepper, g.white, ingemar.s.johansson,
	mirja.kuehlewind, cheshire, rs.ietf, Jason_Livingood, vidhi_goel,
	Mike_Rudolph, Jeff_Howe, srichard, kuba

Thank you for your contribution! Sashiko AI review found 1 potential
issue(s) to consider:

Critical: 0 · High: 0 · Medium: 0 · Low: 1

- [Low] New kfunc `tcp_tso_autosize()` is registered in the TCP
  congestion-control kfunc set (net/ipv4/bpf_tcp_ca.c:197) without any…

--

bpf: make tcp_tso_autosize() available to BPF congestion controls

This exposes tcp_tso_autosize() as a BPF kfunc and registers it in the TCP
congestion-control kfunc set.  It also sanitizes min_tso_segs to at least 1
and returns that sanitized minimum when mss_now is 0.

> diff --git a/net/ipv4/bpf_tcp_ca.c b/net/ipv4/bpf_tcp_ca.c
> index ed4fea98dfde1..9deed2244c2de 100644
> --- a/net/ipv4/bpf_tcp_ca.c
> +++ b/net/ipv4/bpf_tcp_ca.c
> @@ -194,6 +194,7 @@ BTF_ID_FLAGS(func, tcp_reno_cong_avoid)
>  BTF_ID_FLAGS(func, tcp_reno_undo_cwnd)
>  BTF_ID_FLAGS(func, tcp_slow_start)
>  BTF_ID_FLAGS(func, tcp_cong_avoid_ai)
> +BTF_ID_FLAGS(func, tcp_tso_autosize)
>  BTF_KFUNCS_END(bpf_tcp_ca_check_kfunc_ids)

[Severity: Low]
Is there a selftest planned for this new kfunc?  A grep for
tcp_tso_autosize at this commit matches only:

    Documentation/networking/net_cachelines/netns_ipv4_sysctl.rst
    include/net/tcp.h
    net/ipv4/bpf_tcp_ca.c
    net/ipv4/tcp.c
    net/ipv4/tcp_bbr.c
    net/ipv4/tcp_output.c

so nothing under tools/testing/selftests/bpf references it.

The other members of bpf_tcp_ca_check_kfunc_ids do have a consumer, for
example in tools/testing/selftests/bpf/progs/bpf_cubic.c:

    extern __u32 tcp_slow_start(struct tcp_sock *tp, __u32 acked) __ksym;
    extern void tcp_cong_avoid_ai(struct tcp_sock *tp, __u32 w, __u32 acked) __ksym;

and the tso_segs struct_ops program in
tools/testing/selftests/bpf/progs/tcp_ca_kfunc.c still calls
bbr_tso_segs(sk, mss_now) rather than the newly exposed kfunc.

Would adding a struct_ops program that declares

    extern __u32 tcp_tso_autosize(struct sock *sk, __u32 mss_now, int min_tso_segs) __ksym;

and calls it be worthwhile, so that the load-time BTF/argument match for
the promoted kfunc is checked?

> diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
> index b1b493d46b9cf..b7f2ee5e7a6b1 100644
> --- a/net/ipv4/tcp_output.c
> +++ b/net/ipv4/tcp_output.c
> @@ -2252,13 +2252,21 @@ static bool tcp_nagle_check(bool partial, const struct tcp_sock *tp,
>   * in bigger TSO bursts. We we cut the RTT-based allowance in half
>   * for every 2^9 usec (aka 512 us) of RTT, so that the RTT-based allowance
>   * is below 1500 bytes after 6 * ~500 usec = 3ms.
> + *
> + * The min_tso_segs is floored to 1 to avoid surprising conversion. Also,
> + * BPF callers may pass mss_now == 0. In that case the function returns the
> + * sanitized min_tso_segs value and skips autosizing.
>   */
> -u32 tcp_tso_autosize(const struct sock *sk, unsigned int mss_now,
> -		     int min_tso_segs)
> +__bpf_kfunc u32 tcp_tso_autosize(const struct sock *sk, unsigned int mss_now,
> +				 int min_tso_segs)
>  {
> +	u32 min_tso = max(min_tso_segs, 1);
>  	unsigned long bytes;
>  	u32 r;
>  
> +	if (unlikely(!mss_now))
> +		return min_tso;
> +

[Severity: Low]
Can either of these two new branches be reached by anything in the tree
today?

tcp_tso_segs() passes sysctl_tcp_min_tso_segs, whose minimum is 1, together
with a non-zero tcp_current_mss() value, and bbr_tso_segs() already filters
out !mss_now before passing 1 or 2:

    net/ipv4/tcp_bbr.c:bbr_tso_segs() {
        ...
        if (mss_now)
            segs = tcp_tso_autosize(sk, mss_now, min_tso_segs);
        ...
    }

That leaves a BPF congestion control as the only caller that can pass
mss_now == 0 or min_tso_segs < 1, and no such program exists in
tools/testing/selftests/bpf.  Would a test that exercises these two paths
be worth adding alongside the kfunc registration?

>  	bytes = READ_ONCE(sk->sk_pacing_rate) >> READ_ONCE(sk->sk_pacing_shift);
>  
>  	r = tcp_min_rtt(tcp_sk(sk)) >> READ_ONCE(sock_net(sk)->ipv4.sysctl_tcp_tso_rtt_log);

[ ... ]

-- 
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260831165230.2696893-1-chia-yu.chang%40nokia-bell-labs.com

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2026-09-03 19:57 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-31 16:52 [PATCH net-next v9 0/2] Explicit TSO segment count chia-yu.chang
2026-08-31 16:52 ` [PATCH net-next v9 1/2] tcp: Replace min_tso_segs() with tso_segs() CC callback chia-yu.chang
2026-09-03 19:57   ` [net-next,v9,1/2] " netdev-bot+sashiko
2026-08-31 16:52 ` [PATCH net-next v9 2/2] bpf: make tcp_tso_autosize() available to BPF congestion controls chia-yu.chang
2026-09-03 19:57   ` [net-next,v9,2/2] " netdev-bot+sashiko

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox