[PATCH mptcp-next 1/2] mptcp: sched: add subflow avoid flag and kfunc

Kalpan Jani <[email protected]> Wed, 15 Jul 2026 15:41:47 +0530
Newsgroups dev.linux.lists.mptcp
Message-ID <[email protected]>
The core benches a subflow via the "stale" bit after
net.mptcp.stale_loss_cnt retransmission intervals without progress. That
bit is owned by the core and tightly coupled to the RTO: it is cleared
automatically as soon as a packet is acked on the subflow
(mptcp_subflow_active() calls mptcp_subflow_set_active() when rcv_tstamp
advances).

A packet scheduler may want to bench a subflow for unrelated reasons
(too high latency, too unstable) and keep it benched until it decides
otherwise. Reusing the stale bit does not work: the core would clear it
on the next incoming ack.

Add a separate, scheduler-owned "avoid" flag to mptcp_subflow_context:

 - it is never set or cleared by the core, only through
   mptcp_subflow_set_avoid(), exposed to BPF struct_ops schedulers as a
   kfunc, with the same registration and filter as
   mptcp_subflow_set_scheduled() so only schedulers can call it;

 - it lives in the struct_group(reset) block and is therefore cleared
   when the subflow is reset, so a re-established path is re-evaluated.

The flag is scheduler-local state: a scheduler sets it to remember that a
subflow should be skipped, and consults it when picking a subflow to send
on. The core send and retransmit paths are left unchanged.

Link: https://github.com/multipath-tcp/mptcp_net-next/issues/349
Signed-off-by: Kalpan Jani <[email protected]>
---
 net/mptcp/bpf.c      | 1 +
 net/mptcp/protocol.h | 3 +++
 net/mptcp/sched.c    | 6 ++++++
 3 files changed, 10 insertions(+)

diff --git a/net/mptcp/bpf.c b/net/mptcp/bpf.c
index 0845061ddc65..ca6e3e2c3c60 100644
--- a/net/mptcp/bpf.c
+++ b/net/mptcp/bpf.c
@@ -318,6 +318,7 @@ BTF_KFUNCS_START(bpf_mptcp_common_kfunc_ids)
 BTF_ID_FLAGS(func, bpf_mptcp_subflow_ctx, KF_RET_NULL)
 BTF_ID_FLAGS(func, bpf_mptcp_subflow_tcp_sock, KF_RET_NULL)
 BTF_ID_FLAGS(func, mptcp_subflow_set_scheduled)
+BTF_ID_FLAGS(func, mptcp_subflow_set_avoid)
 BTF_ID_FLAGS(func, mptcp_subflow_active)
 BTF_ID_FLAGS(func, mptcp_set_timeout)
 BTF_ID_FLAGS(func, mptcp_wnd_end)
diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h
index da40c6f3705f..6485629acd63 100644
--- a/net/mptcp/protocol.h
+++ b/net/mptcp/protocol.h
@@ -587,6 +587,7 @@ struct mptcp_subflow_context {
 		__unused : 8;
 	bool	data_avail;
 	bool	scheduled;
+	bool	avoid;		    /* pkt scheduler: skip subflow if possible */
 	bool	pm_listener;	    /* a listener managed by the kernel PM? */
 	bool	fully_established;  /* path validated */
 	u32	lent_mem_frag;
@@ -897,6 +898,8 @@ static inline bool __mptcp_subflow_active(struct mptcp_subflow_context *subflow)
 
 void mptcp_subflow_set_active(struct mptcp_subflow_context *subflow);
 
+void mptcp_subflow_set_avoid(struct mptcp_subflow_context *subflow, bool avoid);
+
 bool mptcp_subflow_active(struct mptcp_subflow_context *subflow);
 
 void mptcp_subflow_drop_ctx(struct sock *ssk);
diff --git a/net/mptcp/sched.c b/net/mptcp/sched.c
index 1e59072d478c..09163f9742b9 100644
--- a/net/mptcp/sched.c
+++ b/net/mptcp/sched.c
@@ -165,6 +165,12 @@ void mptcp_subflow_set_scheduled(struct mptcp_subflow_context *subflow,
 	WRITE_ONCE(subflow->scheduled, scheduled);
 }
 
+void mptcp_subflow_set_avoid(struct mptcp_subflow_context *subflow,
+			     bool avoid)
+{
+	WRITE_ONCE(subflow->avoid, avoid);
+}
+
 int mptcp_sched_get_send(struct mptcp_sock *msk)
 {
 	struct mptcp_subflow_context *subflow;
-- 
2.43.0