* [PATCH RFC mptcp-next v2 0/4] mptcp: sched: penalise a slow subflow
@ 2026-08-15 14:40 Shardul Bankar
2026-08-15 14:40 ` [PATCH RFC mptcp-next v2 1/4] mptcp: sched: avoid truncating the pacing rate in the scheduler Shardul Bankar
` (5 more replies)
0 siblings, 6 replies; 7+ messages in thread
From: Shardul Bankar @ 2026-08-15 14:40 UTC (permalink / raw)
To: mptcp
Cc: Matthieu Baerts (NGI0), Janak Patel, Kalpan Jani, Shardul Bankar,
Shardul Bankar
A subflow that is slow relative to the others can soak up connection
resources and head-of-line-block the aggregate stream. This series lets the
packet scheduler send less than such a subflow's congestion window by halving
it, and leaves the congestion control to grow it back. It implements
issue #345 [1]. This is a deliberately simple first form; please treat it
as an RFC.
- Patch 1: a prerequisite fix. The scheduler truncates the pacing rate to a
u32, which skews subflow selection on fast paths (Fixes).
- Patch 2: penalise a slow subflow by halving its cwnd, rate-triggered and
gated on the fast path being cwnd-limited.
- Patch 3: skip the penalty when the transfer is receive-window-limited,
where it would otherwise slow the transfer.
- Patch 4: MPTcpExt counters and a dedicated mptcp_subflow_penalise
tracepoint for observability.
Validation, baseline versus this series (test helper, its run_matrix.sh
base-vs-patch driver, and run steps are in [2]):
- No regression in the mptcp selftests.
- No regression versus baseline across the buffer-limited transfers
(send-buffer, receive-window, both, sndbuf>rcvbuf): the penalised two-path
completion time matches the unpatched kernel. Where the slow subflow is
beneficial (send-buffer-limited), the two-path transfer stays faster than the
fast path alone.
- Patch 3's gate keeps the penalty from firing when receive-window-limited,
where halving cwnd would slow the transfer.
- With a bufferbloated slow path, the penalty roughly halves the slow path's
queueing latency (subflow srtt), with the transfer time unchanged. It does
not reduce the aggregate out-of-order volume or move data off the slow path;
that is the role of #332, and the two are complementary.
- One regime, unbounded buffers with a purely delay-slow path, shows a
throughput cost that grows with the delay; the current gates do not catch it.
On patch 4: observability is a dedicated mptcp_subflow_penalise tracepoint
rather than a field on the existing mptcp_subflow_get_send. The penalise verdict
is only known after the selection loop, so a field there would need a second,
post-decision emit plus a marker to tell it apart from the per-candidate emits,
and would make existing get_send consumers count an extra event per call. A
dedicated tracepoint avoids the double emit and leaves mptcp_subflow_get_send
untouched. Happy to use a field instead if you prefer.
[1] https://github.com/multipath-tcp/mptcp_net-next/issues/345
[2] https://github.com/shardulsdk-mpiric/linux/blob/161a4bd556eb/tools/testing/selftests/net/mptcp/mptcp_sched_penalise.sh
(run_matrix.sh + runs.conf are alongside it in the same directory)
Changes in v2
- Patch 1 (new, not in v1): fix the scheduler's pre-existing u32 pacing-rate
truncation (Fixes: 3ce0852c86b9).
- Patch 2 (v1's 1/3): also skip a subflow already at the cwnd floor
(MPTCP_PENALISE_MIN_CWND); clear the flag when returning without send-window
room; re-check TCP_CA_Open in the apply path; express the rate trigger as a
division (avg_pacing_rate < max_pace / ratio, using the widened pace from
patch 1) instead of a u64-cast multiply.
- Patch 3 (v1's 2/3): make the receive-window test wrap-safe
(after64, like tcp_snd_wnd_test()); same condition otherwise.
- Patch 4 (v1's 3/3): real MPTcpExt counters (dropped DO-NOT-MERGE) plus
a dedicated mptcp_subflow_penalise tracepoint (not a field on
mptcp_subflow_get_send).
- Rebased on current export.
- Link to v1:
https://lore.kernel.org/mptcp/20260726-mptcp_penalise_send-v1-0-84485e0e995b@mpiricsoftware.com
Signed-off-by: Shardul Bankar <shardul.b@mpiricsoftware.com>
---
Shardul Bankar (4):
mptcp: sched: avoid truncating the pacing rate in the scheduler
mptcp: sched: penalise a slow subflow by halving its cwnd
mptcp: sched: do not penalise when receive-window-limited
mptcp: sched: add penalise counters and tracepoint
include/trace/events/mptcp.h | 30 ++++++++++++++++
net/mptcp/mib.c | 2 ++
net/mptcp/mib.h | 2 ++
net/mptcp/protocol.c | 85 ++++++++++++++++++++++++++++++++++++++++----
net/mptcp/protocol.h | 2 ++
5 files changed, 115 insertions(+), 6 deletions(-)
---
base-commit: be46c85306e63c58c223ce02074ed4ba7f21971b
change-id: 20260815-mptcp_penalise_send_v2-ed6206f70a84
Best regards,
--
Shardul Bankar <shardul.b@mpiricsoftware.com>
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH RFC mptcp-next v2 1/4] mptcp: sched: avoid truncating the pacing rate in the scheduler
2026-08-15 14:40 [PATCH RFC mptcp-next v2 0/4] mptcp: sched: penalise a slow subflow Shardul Bankar
@ 2026-08-15 14:40 ` Shardul Bankar
2026-08-15 14:40 ` [PATCH RFC mptcp-next v2 2/4] mptcp: sched: penalise a slow subflow by halving its cwnd Shardul Bankar
` (4 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Shardul Bankar @ 2026-08-15 14:40 UTC (permalink / raw)
To: mptcp
Cc: Matthieu Baerts (NGI0), Janak Patel, Kalpan Jani, Shardul Bankar,
Shardul Bankar
mptcp_subflow_get_send() derives each subflow's linger time and a running
pacing-rate average through a u32 "pace", while sk_pacing_rate and
avg_pacing_rate are unsigned long.
On 64-bit systems the u32 truncates pacing rates above ~4.29 GB/s, skewing
the linger-time based subflow selection on very fast paths. On 32-bit
systems, where unsigned long is also 32-bit, the sk_pacing_rate * burst
product overflows: with burst up to ~65428 it wraps once the pacing rate
exceeds ~526 kbit/s, misestimating the running average at ordinary rates.
Widen "pace" to unsigned long, divide the linger time with a 64-bit
divisor, and cast the pacing-rate average product to u64.
Fixes: 3ce0852c86b9 ("mptcp: enforce HoL-blocking estimation")
Suggested-by: Matthieu Baerts (NGI0) <matttbe@kernel.org>
Signed-off-by: Shardul Bankar <shardul.b@mpiricsoftware.com>
---
net/mptcp/protocol.c | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c
index f879b1061f2d..c04b73123d27 100644
--- a/net/mptcp/protocol.c
+++ b/net/mptcp/protocol.c
@@ -1626,7 +1626,8 @@ struct sock *mptcp_subflow_get_send(struct mptcp_sock *msk)
struct subflow_send_info send_info[SSK_MODE_MAX];
struct mptcp_subflow_context *subflow;
struct sock *sk = (struct sock *)msk;
- u32 pace, burst, wmem;
+ unsigned long pace;
+ u32 burst, wmem;
int i, nr_active = 0;
struct sock *ssk;
u64 linger_time;
@@ -1657,7 +1658,7 @@ struct sock *mptcp_subflow_get_send(struct mptcp_sock *msk)
continue;
}
- linger_time = div_u64((u64)READ_ONCE(ssk->sk_wmem_queued) << 32, pace);
+ linger_time = div64_u64((u64)READ_ONCE(ssk->sk_wmem_queued) << 32, pace);
if (linger_time < send_info[backup].linger_time) {
send_info[backup].ssk = ssk;
send_info[backup].linger_time = linger_time;
@@ -1691,7 +1692,7 @@ struct sock *mptcp_subflow_get_send(struct mptcp_sock *msk)
subflow = mptcp_subflow_ctx(ssk);
subflow->avg_pacing_rate = div_u64((u64)subflow->avg_pacing_rate * wmem +
- READ_ONCE(ssk->sk_pacing_rate) * burst,
+ (u64)READ_ONCE(ssk->sk_pacing_rate) * burst,
burst + wmem);
msk->snd_burst = burst;
return ssk;
--
2.34.1
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH RFC mptcp-next v2 2/4] mptcp: sched: penalise a slow subflow by halving its cwnd
2026-08-15 14:40 [PATCH RFC mptcp-next v2 0/4] mptcp: sched: penalise a slow subflow Shardul Bankar
2026-08-15 14:40 ` [PATCH RFC mptcp-next v2 1/4] mptcp: sched: avoid truncating the pacing rate in the scheduler Shardul Bankar
@ 2026-08-15 14:40 ` Shardul Bankar
2026-08-15 14:40 ` [PATCH RFC mptcp-next v2 3/4] mptcp: sched: do not penalise when receive-window-limited Shardul Bankar
` (3 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Shardul Bankar @ 2026-08-15 14:40 UTC (permalink / raw)
To: mptcp
Cc: Matthieu Baerts (NGI0), Janak Patel, Kalpan Jani, Shardul Bankar,
Shardul Bankar
A poorly-performing but usable subflow (high latency, loss, bufferbloat)
can soak up connection resources and cause head-of-line blocking of the
aggregate stream. Give the default packet scheduler a way to send less
than such a subflow's full congestion window.
Once a subflow has been picked for transmission, flag it for penalisation
when:
- its smoothed delivery rate (avg_pacing_rate) is below half that of the
fastest path, keying on rate, not RTT, so a slow-but-high-throughput
path is left alone;
- the fastest path is cwnd-limited (saturated), so shifting load off the
slow path is worthwhile;
- the subflow is in TCP_CA_Open, so its cwnd is not already being reduced
by loss recovery;
- its cwnd is still above MPTCP_PENALISE_MIN_CWND, so a subflow already at
the floor is left to recover instead of being churned by a halving that
can no longer reduce it;
- it has not been penalised in the last RTT.
The reduction halves tcp_snd_cwnd, floored at MPTCP_PENALISE_MIN_CWND, and
ssthresh if cwnd is past it. It is applied in the push path under the
subflow socket lock, which protects snd_cwnd (the scheduler runs under the
msk lock). The congestion control grows the window back, ACK-clocked; that
regrowth is the built-in probe, so no explicit MPTCP-side probing is
needed.
Closes: https://github.com/multipath-tcp/mptcp_net-next/issues/345
Co-developed-by: Matthieu Baerts (NGI0) <matttbe@kernel.org>
Signed-off-by: Shardul Bankar <shardul.b@mpiricsoftware.com>
---
net/mptcp/protocol.c | 65 ++++++++++++++++++++++++++++++++++++++++++++++++----
net/mptcp/protocol.h | 2 ++
2 files changed, 63 insertions(+), 4 deletions(-)
diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c
index c04b73123d27..075d468d0e14 100644
--- a/net/mptcp/protocol.c
+++ b/net/mptcp/protocol.c
@@ -1617,6 +1617,41 @@ bool mptcp_subflow_active(struct mptcp_subflow_context *subflow)
#define SSK_MODE_BACKUP 1
#define SSK_MODE_MAX 2
+/* Penalise a subflow pacing below the fastest path's rate / this ratio */
+#define MPTCP_PENALISE_RATE_RATIO 2
+/* cwnd floor for the penalty: skip a subflow already there, and never reduce
+ * below it (halving at the floor only churns congestion control).
+ */
+#define MPTCP_PENALISE_MIN_CWND 2
+
+/* Rate-limit the penalty to at most once per subflow RTT, so the congestion
+ * control can grow the window back between reductions.
+ */
+static bool mptcp_penalise_throttle_ok(struct mptcp_subflow_context *subflow)
+{
+ struct sock *ssk = mptcp_subflow_tcp_sock(subflow);
+ u32 rtt = usecs_to_jiffies(READ_ONCE(tcp_sk(ssk)->srtt_us) >> 3);
+
+ return tcp_jiffies32 - subflow->last_penalise >= max_t(u32, rtt, 1);
+}
+
+/* Halve cwnd (and ssthresh if past it) under the subflow socket lock. */
+static void mptcp_penalise_cwnd(struct sock *ssk)
+{
+ struct mptcp_subflow_context *subflow = mptcp_subflow_ctx(ssk);
+ struct tcp_sock *tp = tcp_sk(ssk);
+ u32 cwnd = tcp_snd_cwnd(tp);
+
+ subflow->penalise = false;
+ /* CA state may have left TCP_CA_Open since get_send flagged this */
+ if (inet_csk(ssk)->icsk_ca_state != TCP_CA_Open)
+ return;
+ subflow->last_penalise = tcp_jiffies32;
+ tcp_snd_cwnd_set(tp, max_t(u32, cwnd >> 1, MPTCP_PENALISE_MIN_CWND));
+ if (cwnd >= tp->snd_ssthresh)
+ tp->snd_ssthresh = max_t(u32, tp->snd_ssthresh >> 1, 2);
+}
+
/* implement the mptcp packet scheduler;
* returns the subflow that will transmit the next DSS
* additionally updates the rtx timeout
@@ -1626,10 +1661,11 @@ struct sock *mptcp_subflow_get_send(struct mptcp_sock *msk)
struct subflow_send_info send_info[SSK_MODE_MAX];
struct mptcp_subflow_context *subflow;
struct sock *sk = (struct sock *)msk;
- unsigned long pace;
+ unsigned long pace, max_pace = 0;
u32 burst, wmem;
+ bool penal_cand;
int i, nr_active = 0;
- struct sock *ssk;
+ struct sock *ssk, *fastest = NULL;
u64 linger_time;
long tout = 0;
@@ -1658,6 +1694,12 @@ struct sock *mptcp_subflow_get_send(struct mptcp_sock *msk)
continue;
}
+ /* track the fastest path; slower ones get throttled below */
+ if (pace > max_pace) {
+ max_pace = pace;
+ fastest = ssk;
+ }
+
linger_time = div64_u64((u64)READ_ONCE(ssk->sk_wmem_queued) << 32, pace);
if (linger_time < send_info[backup].linger_time) {
send_info[backup].ssk = ssk;
@@ -1685,12 +1727,24 @@ struct sock *mptcp_subflow_get_send(struct mptcp_sock *msk)
if (!ssk || !sk_stream_memory_free(ssk))
return NULL;
+ /* Flag a slow subflow for cwnd halving, applied in the push path */
+ subflow = mptcp_subflow_ctx(ssk);
+ penal_cand = fastest && ssk != fastest &&
+ subflow->avg_pacing_rate < max_pace / MPTCP_PENALISE_RATE_RATIO;
+ subflow->penalise = penal_cand &&
+ tcp_snd_cwnd(tcp_sk(ssk)) > MPTCP_PENALISE_MIN_CWND &&
+ inet_csk(ssk)->icsk_ca_state == TCP_CA_Open &&
+ tcp_is_cwnd_limited(fastest) &&
+ mptcp_penalise_throttle_ok(subflow);
+
burst = min(MPTCP_SEND_BURST_SIZE, mptcp_wnd_end(msk) - msk->snd_nxt);
wmem = READ_ONCE(ssk->sk_wmem_queued);
- if (!burst)
+ if (!burst) {
+ /* no window room to send; skip the penalty */
+ subflow->penalise = false;
return ssk;
+ }
- subflow = mptcp_subflow_ctx(ssk);
subflow->avg_pacing_rate = div_u64((u64)subflow->avg_pacing_rate * wmem +
(u64)READ_ONCE(ssk->sk_pacing_rate) * burst,
burst + wmem);
@@ -1747,6 +1801,9 @@ static int __subflow_push_pending(struct sock *sk, struct sock *ssk,
struct mptcp_data_frag *dfrag;
int len, copied = 0, err = 0;
+ if (mptcp_subflow_ctx(ssk)->penalise)
+ mptcp_penalise_cwnd(ssk);
+
while ((dfrag = mptcp_send_head(sk))) {
info->sent = dfrag->already_sent;
info->limit = dfrag->data_len;
diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h
index 7e168e450fb0..ab9ba6699ccd 100644
--- a/net/mptcp/protocol.h
+++ b/net/mptcp/protocol.h
@@ -588,6 +588,7 @@ struct mptcp_subflow_context {
__unused : 9;
bool data_avail;
bool scheduled;
+ bool penalise; /* scheduler flagged this subflow for cwnd halving */
bool pm_listener; /* a listener managed by the kernel PM? */
bool fully_established; /* path validated */
u32 lent_mem_frag;
@@ -611,6 +612,7 @@ struct mptcp_subflow_context {
*/
u32 subflow_id;
+ u32 last_penalise; /* tcp_jiffies32 of the last cwnd penalty */
long delegated_status;
unsigned long fail_tout;
--
2.34.1
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH RFC mptcp-next v2 3/4] mptcp: sched: do not penalise when receive-window-limited
2026-08-15 14:40 [PATCH RFC mptcp-next v2 0/4] mptcp: sched: penalise a slow subflow Shardul Bankar
2026-08-15 14:40 ` [PATCH RFC mptcp-next v2 1/4] mptcp: sched: avoid truncating the pacing rate in the scheduler Shardul Bankar
2026-08-15 14:40 ` [PATCH RFC mptcp-next v2 2/4] mptcp: sched: penalise a slow subflow by halving its cwnd Shardul Bankar
@ 2026-08-15 14:40 ` Shardul Bankar
2026-08-15 14:40 ` [PATCH RFC mptcp-next v2 4/4] mptcp: sched: add penalise counters and tracepoint Shardul Bankar
` (2 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Shardul Bankar @ 2026-08-15 14:40 UTC (permalink / raw)
To: mptcp
Cc: Matthieu Baerts (NGI0), Janak Patel, Kalpan Jani, Shardul Bankar,
Shardul Bankar
The penalty in the previous patch shifts load off a slow subflow onto the
fastest one, which only helps if the fastest path can absorb it. When the
connection is receive-window-limited (the receiver's advertised window,
not our congestion window, is the bottleneck), the fastest path is capped
by that shared window too and cannot send more, so halving the slow path's
cwnd just sheds its throughput. In a receive-window-limited transfer this
was measured roughly 2x slower than baseline.
Gate on the application's queued data fitting within the send window:
penalise only while write_seq <= wnd_end. If the application has queued
past the window edge the receive window is the binding constraint, so skip
the penalty. Neither write_seq (application demand) nor wnd_end (peer
window) is derived from cwnd, so the test is not made circular by the
window itself suppressing cwnd.
Co-developed-by: Matthieu Baerts (NGI0) <matttbe@kernel.org>
Signed-off-by: Shardul Bankar <shardul.b@mpiricsoftware.com>
---
net/mptcp/protocol.c | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c
index 075d468d0e14..e6c10816df8d 100644
--- a/net/mptcp/protocol.c
+++ b/net/mptcp/protocol.c
@@ -1635,6 +1635,14 @@ static bool mptcp_penalise_throttle_ok(struct mptcp_subflow_context *subflow)
return tcp_jiffies32 - subflow->last_penalise >= max_t(u32, rtt, 1);
}
+/* Like tcp_snd_wnd_test() but without an skb: true while queued data still fits
+ * the send window, i.e. not receive-window-limited.
+ */
+static bool mptcp_snd_wnd_test(const struct mptcp_sock *msk)
+{
+ return !after64(msk->write_seq, mptcp_wnd_end(msk));
+}
+
/* Halve cwnd (and ssthresh if past it) under the subflow socket lock. */
static void mptcp_penalise_cwnd(struct sock *ssk)
{
@@ -1735,6 +1743,7 @@ struct sock *mptcp_subflow_get_send(struct mptcp_sock *msk)
tcp_snd_cwnd(tcp_sk(ssk)) > MPTCP_PENALISE_MIN_CWND &&
inet_csk(ssk)->icsk_ca_state == TCP_CA_Open &&
tcp_is_cwnd_limited(fastest) &&
+ mptcp_snd_wnd_test(msk) &&
mptcp_penalise_throttle_ok(subflow);
burst = min(MPTCP_SEND_BURST_SIZE, mptcp_wnd_end(msk) - msk->snd_nxt);
--
2.34.1
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH RFC mptcp-next v2 4/4] mptcp: sched: add penalise counters and tracepoint
2026-08-15 14:40 [PATCH RFC mptcp-next v2 0/4] mptcp: sched: penalise a slow subflow Shardul Bankar
` (2 preceding siblings ...)
2026-08-15 14:40 ` [PATCH RFC mptcp-next v2 3/4] mptcp: sched: do not penalise when receive-window-limited Shardul Bankar
@ 2026-08-15 14:40 ` Shardul Bankar
2026-08-15 15:51 ` [PATCH RFC mptcp-next v2 0/4] mptcp: sched: penalise a slow subflow MPTCP CI
2026-08-16 14:15 ` Matthieu Baerts
5 siblings, 0 replies; 7+ messages in thread
From: Shardul Bankar @ 2026-08-15 14:40 UTC (permalink / raw)
To: mptcp
Cc: Matthieu Baerts (NGI0), Janak Patel, Kalpan Jani, Shardul Bankar,
Shardul Bankar
Add observability for the cwnd-halving penalty introduced in the previous
patches.
Two MPTcpExt SNMP counters:
- CwndPenalised: times a subflow cwnd was actually halved;
- PenalCandidate: times the rate trigger picked a slow subflow.
The two show whether the rate trigger fired at all and how many of those
candidates were actually halved; the gap reflects the gates and the
once-per-RTT limiter collectively, not any single condition.
Add a mptcp_subflow_penalise tracepoint. The penalise verdict is decided
after the subflow-selection loop, once the fastest path is known, so it is
exposed through a dedicated tracepoint rather than the per-candidate
mptcp_subflow_get_send emit. It reports the selected subflow's pace, the
fastest path's pace and its cwnd alongside the verdict, so a slow-but-gated
subflow and its window can be watched from a single line.
Co-developed-by: Matthieu Baerts (NGI0) <matttbe@kernel.org>
Signed-off-by: Shardul Bankar <shardul.b@mpiricsoftware.com>
---
include/trace/events/mptcp.h | 30 ++++++++++++++++++++++++++++++
net/mptcp/mib.c | 2 ++
net/mptcp/mib.h | 2 ++
net/mptcp/protocol.c | 6 ++++++
4 files changed, 40 insertions(+)
diff --git a/include/trace/events/mptcp.h b/include/trace/events/mptcp.h
index 22882bd03459..fe13f0c8a34d 100644
--- a/include/trace/events/mptcp.h
+++ b/include/trace/events/mptcp.h
@@ -68,6 +68,36 @@ TRACE_EVENT(mptcp_subflow_get_send,
__entry->backup, __entry->ratio)
);
+TRACE_EVENT(mptcp_subflow_penalise,
+
+ TP_PROTO(struct mptcp_subflow_context *subflow, unsigned long max_pace),
+
+ TP_ARGS(subflow, max_pace),
+
+ TP_STRUCT__entry(
+ __field(u64, pace)
+ __field(u64, max_pace)
+ __field(u32, cwnd)
+ __field(bool, penalise)
+ ),
+
+ TP_fast_assign(
+ struct sock *ssk = mptcp_subflow_tcp_sock(subflow);
+
+ __entry->pace = subflow->avg_pacing_rate;
+ __entry->max_pace = max_pace;
+ if (ssk && sk_fullsock(ssk))
+ __entry->cwnd = tcp_snd_cwnd(tcp_sk(ssk));
+ else
+ __entry->cwnd = 0;
+ __entry->penalise = subflow->penalise;
+ ),
+
+ TP_printk("pace=%llu max_pace=%llu cwnd=%u penalise=%d",
+ __entry->pace, __entry->max_pace,
+ __entry->cwnd, __entry->penalise)
+);
+
DECLARE_EVENT_CLASS(mptcp_dump_mpext,
TP_PROTO(struct mptcp_ext *mpext),
diff --git a/net/mptcp/mib.c b/net/mptcp/mib.c
index 608cb568897c..b39b5511345d 100644
--- a/net/mptcp/mib.c
+++ b/net/mptcp/mib.c
@@ -95,6 +95,8 @@ static const struct snmp_mib mptcp_snmp_list[] = {
SNMP_MIB_ITEM("BacklogDrop", MPTCP_MIB_BACKLOGDROP),
SNMP_MIB_ITEM("RcvPruned", MPTCP_MIB_RCVPRUNED),
SNMP_MIB_ITEM("OFOPruned", MPTCP_MIB_OFOPRUNED),
+ SNMP_MIB_ITEM("CwndPenalised", MPTCP_MIB_CWNDPENALISED),
+ SNMP_MIB_ITEM("PenalCandidate", MPTCP_MIB_PENALCAND),
};
/* mptcp_mib_alloc - allocate percpu mib counters
diff --git a/net/mptcp/mib.h b/net/mptcp/mib.h
index 1ebdb55e9534..a7b5bf684af7 100644
--- a/net/mptcp/mib.h
+++ b/net/mptcp/mib.h
@@ -98,6 +98,8 @@ enum linux_mptcp_mib_field {
MPTCP_MIB_BACKLOGDROP, /* Backlog over memory limit */
MPTCP_MIB_RCVPRUNED, /* Dropped due to memory constraints */
MPTCP_MIB_OFOPRUNED, /* MPTCP-level OoO queue pruned */
+ MPTCP_MIB_CWNDPENALISED, /* subflow cwnd halved by the scheduler */
+ MPTCP_MIB_PENALCAND, /* scheduler picked a slow (low-rate) subflow */
__MPTCP_MIB_MAX
};
diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c
index e6c10816df8d..ee7a3baa45e4 100644
--- a/net/mptcp/protocol.c
+++ b/net/mptcp/protocol.c
@@ -1656,6 +1656,7 @@ static void mptcp_penalise_cwnd(struct sock *ssk)
return;
subflow->last_penalise = tcp_jiffies32;
tcp_snd_cwnd_set(tp, max_t(u32, cwnd >> 1, MPTCP_PENALISE_MIN_CWND));
+ MPTCP_INC_STATS(sock_net(ssk), MPTCP_MIB_CWNDPENALISED);
if (cwnd >= tp->snd_ssthresh)
tp->snd_ssthresh = max_t(u32, tp->snd_ssthresh >> 1, 2);
}
@@ -1739,6 +1740,8 @@ struct sock *mptcp_subflow_get_send(struct mptcp_sock *msk)
subflow = mptcp_subflow_ctx(ssk);
penal_cand = fastest && ssk != fastest &&
subflow->avg_pacing_rate < max_pace / MPTCP_PENALISE_RATE_RATIO;
+ if (penal_cand)
+ MPTCP_INC_STATS(sock_net(ssk), MPTCP_MIB_PENALCAND);
subflow->penalise = penal_cand &&
tcp_snd_cwnd(tcp_sk(ssk)) > MPTCP_PENALISE_MIN_CWND &&
inet_csk(ssk)->icsk_ca_state == TCP_CA_Open &&
@@ -1746,6 +1749,9 @@ struct sock *mptcp_subflow_get_send(struct mptcp_sock *msk)
mptcp_snd_wnd_test(msk) &&
mptcp_penalise_throttle_ok(subflow);
+ /* trace the penalise verdict, decided here after the selection loop */
+ trace_mptcp_subflow_penalise(subflow, max_pace);
+
burst = min(MPTCP_SEND_BURST_SIZE, mptcp_wnd_end(msk) - msk->snd_nxt);
wmem = READ_ONCE(ssk->sk_wmem_queued);
if (!burst) {
--
2.34.1
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH RFC mptcp-next v2 0/4] mptcp: sched: penalise a slow subflow
2026-08-15 14:40 [PATCH RFC mptcp-next v2 0/4] mptcp: sched: penalise a slow subflow Shardul Bankar
` (3 preceding siblings ...)
2026-08-15 14:40 ` [PATCH RFC mptcp-next v2 4/4] mptcp: sched: add penalise counters and tracepoint Shardul Bankar
@ 2026-08-15 15:51 ` MPTCP CI
2026-08-16 14:15 ` Matthieu Baerts
5 siblings, 0 replies; 7+ messages in thread
From: MPTCP CI @ 2026-08-15 15:51 UTC (permalink / raw)
To: Shardul Bankar; +Cc: mptcp
Hi Shardul,
Thank you for your modifications, that's great!
Our CI did some validations and here is its report:
- KVM Validation: normal (except selftest_mptcp_join): Success! ✅
- KVM Validation: normal (only selftest_mptcp_join): Success! ✅
- KVM Validation: debug (except selftest_mptcp_join): Success! ✅
- KVM Validation: debug (only selftest_mptcp_join): Success! ✅
- KVM Validation: btf-normal (only bpftest_all): Success! ✅
- KVM Validation: btf-debug (only bpftest_all): Success! ✅
- Task: https://github.com/multipath-tcp/mptcp_net-next/actions/runs/31891662725
Initiator: Patchew Applier
Commits: https://github.com/multipath-tcp/mptcp_net-next/commits/537913f1e89f
Patchwork: https://patchwork.kernel.org/project/mptcp/list/?series=1146531
If there are some issues, you can reproduce them using the same environment as
the one used by the CI thanks to a docker image, e.g.:
$ cd [kernel source code]
$ docker run -v "${PWD}:${PWD}:rw" -w "${PWD}" --privileged --rm -it \
--pull always mptcp/mptcp-upstream-virtme-docker:latest \
auto-normal
For more details:
https://github.com/multipath-tcp/mptcp-upstream-virtme-docker
Please note that despite all the efforts that have been already done to have a
stable tests suite when executed on a public CI like here, it is possible some
reported issues are not due to your modifications. Still, do not hesitate to
help us improve that ;-)
Cheers,
MPTCP GH Action bot
Bot operated by Matthieu Baerts (NGI0 Core)
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH RFC mptcp-next v2 0/4] mptcp: sched: penalise a slow subflow
2026-08-15 14:40 [PATCH RFC mptcp-next v2 0/4] mptcp: sched: penalise a slow subflow Shardul Bankar
` (4 preceding siblings ...)
2026-08-15 15:51 ` [PATCH RFC mptcp-next v2 0/4] mptcp: sched: penalise a slow subflow MPTCP CI
@ 2026-08-16 14:15 ` Matthieu Baerts
5 siblings, 0 replies; 7+ messages in thread
From: Matthieu Baerts @ 2026-08-16 14:15 UTC (permalink / raw)
To: Shardul Bankar, mptcp; +Cc: Janak Patel, Kalpan Jani, Shardul Bankar
Hi Shardul,
On 15/08/2026 16:40, Shardul Bankar wrote:
> A subflow that is slow relative to the others can soak up connection
> resources and head-of-line-block the aggregate stream. This series lets the
> packet scheduler send less than such a subflow's congestion window by halving
> it, and leaves the congestion control to grow it back. It implements
> issue #345 [1]. This is a deliberately simple first form; please treat it
> as an RFC.
Thank you for this v2. I didn't check in details, but I *think* this
simple first form is acceptable, and you can remove the RFC.
It looks like Sashiko didn't manage to validate it for some unclear
reasons [1]. Do you mind resending it (without the RFC), please?
[1]
https://sashiko.dev/#/patchset/20260815-mptcp_penalise_send_v2-v2-0-3e5049a73681%40mpiricsoftware.com
Cheers,
Matt
--
Sponsored by the NGI0 Core fund.
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-08-16 14:15 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-15 14:40 [PATCH RFC mptcp-next v2 0/4] mptcp: sched: penalise a slow subflow Shardul Bankar
2026-08-15 14:40 ` [PATCH RFC mptcp-next v2 1/4] mptcp: sched: avoid truncating the pacing rate in the scheduler Shardul Bankar
2026-08-15 14:40 ` [PATCH RFC mptcp-next v2 2/4] mptcp: sched: penalise a slow subflow by halving its cwnd Shardul Bankar
2026-08-15 14:40 ` [PATCH RFC mptcp-next v2 3/4] mptcp: sched: do not penalise when receive-window-limited Shardul Bankar
2026-08-15 14:40 ` [PATCH RFC mptcp-next v2 4/4] mptcp: sched: add penalise counters and tracepoint Shardul Bankar
2026-08-15 15:51 ` [PATCH RFC mptcp-next v2 0/4] mptcp: sched: penalise a slow subflow MPTCP CI
2026-08-16 14:15 ` Matthieu Baerts
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.