* [PATCH] mptcp: push queued data on passive TFO subflows becoming established
@ 2026-10-08 9:52 T S Rameshkumar
2026-10-08 9:59 ` Petar Sakic
` (3 more replies)
0 siblings, 4 replies; 6+ messages in thread
From: T S Rameshkumar @ 2026-10-08 9:52 UTC (permalink / raw)
To: Matthieu Baerts, Mat Martineau, Geliang Tang
Cc: netdev, mptcp, linux-kernel, Petar Sakic, T S Rameshkumar
With TCP Fast Open on an MPTCP listener, if the server application
writes data while the passive subflow is still in SYN_RECV (after
consuming the client's SYN data but before the MP_CAPABLE third ACK
arrives), __mptcp_subflow_active() refuses transmission and the data
is queued into the msk write queue.
When the MPC third ACK arrives, check_fully_established() marks the
subflow established, but because the third ACK carries no DSS data,
the queued bytes remain stranded until the peer sends more data.
Fix this by:
1. Invoking __mptcp_check_push() in check_fully_established() when
subflow->is_mptfo is set.
2. Setting MPTCP_PUSH_PENDING and scheduling the MPTCP worker in
subflow_state_change() so that once the underlying subflow transitions
to TCP_ESTABLISHED, pending queued bytes are immediately flushed.
Reported-by: Petar Sakic <petar.sakic@ink.fish>
Closes: https://lore.kernel.org/netdev/CAFPPu1gU2Y-D+d4i3F0MoNkYK+e1U+=X3qf6QycjfKBw+8snPg@mail.gmail.com/
Fixes: e00b63056fb4 ("fastopen: only mark MPTFO subflows with SYN data")
Signed-off-by: T S Rameshkumar <rameshkumar.t@phytecembedded.in>
---
net/mptcp/options.c | 7 +++++++
net/mptcp/subflow.c | 5 +++++
2 files changed, 12 insertions(+)
diff --git a/net/mptcp/options.c b/net/mptcp/options.c
index ce0de02f5..d5238fa11 100644
--- a/net/mptcp/options.c
+++ b/net/mptcp/options.c
@@ -1042,6 +1042,13 @@ static bool check_fully_established(struct mptcp_sock *msk, struct sock *ssk,
mptcp_data_lock((struct sock *)msk);
__mptcp_subflow_fully_established(msk, subflow, mp_opt);
+ /* Passive TFO: the application may have written data while the
+ * subflow was still in SYN_RECV; __mptcp_subflow_active() refused
+ * it then and nothing else spools the msk write queue when the
+ * MPC third ack (no DSS) arrives. Push it now.
+ */
+ if (subflow->is_mptfo)
+ __mptcp_check_push((struct sock *)msk, ssk);
mptcp_data_unlock((struct sock *)msk);
check_notify:
diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c
index f0a6725d2..f499073a6 100644
--- a/net/mptcp/subflow.c
+++ b/net/mptcp/subflow.c
@@ -1894,6 +1894,11 @@ static void subflow_state_change(struct sock *sk)
if (subflow->resetting)
return;
+ if (subflow->is_mptfo) {
+ set_bit(MPTCP_PUSH_PENDING, &mptcp_sk(parent)->cb_flags);
+ mptcp_schedule_work(parent);
+ }
+
/* as recvmsg() does not acquire the subflow socket for ssk selection
* a fin packet carrying a DSS can be unnoticed if we don't trigger
* the data available machinery here.
--
2.34.1
^ permalink raw reply related [flat|nested] 6+ messages in thread* Re: [PATCH] mptcp: push queued data on passive TFO subflows becoming established
2026-10-08 9:52 [PATCH] mptcp: push queued data on passive TFO subflows becoming established T S Rameshkumar
@ 2026-10-08 9:59 ` Petar Sakic
2026-10-08 10:03 ` sashiko-bot
` (2 subsequent siblings)
3 siblings, 0 replies; 6+ messages in thread
From: Petar Sakic @ 2026-10-08 9:59 UTC (permalink / raw)
To: T S Rameshkumar
Cc: Matthieu Baerts, Mat Martineau, Geliang Tang, netdev, mptcp,
linux-kernel, T S Rameshkumar
Hi T S,
Thanks for picking this up. One note on the Fixes tag: we reproduced
the hang on 6.8 as well as 6.12.111, 6.18.15 and 7.2.6, so it predates
e00b63056fb4 (Aug 2026). That commit is related but not the origin.
Cheers
- Petar
On Thu, Oct 8, 2026 at 11:53 AM T S Rameshkumar <rameshsv06@gmail.com> wrote:
>
> With TCP Fast Open on an MPTCP listener, if the server application
> writes data while the passive subflow is still in SYN_RECV (after
> consuming the client's SYN data but before the MP_CAPABLE third ACK
> arrives), __mptcp_subflow_active() refuses transmission and the data
> is queued into the msk write queue.
>
> When the MPC third ACK arrives, check_fully_established() marks the
> subflow established, but because the third ACK carries no DSS data,
> the queued bytes remain stranded until the peer sends more data.
>
> Fix this by:
> 1. Invoking __mptcp_check_push() in check_fully_established() when
> subflow->is_mptfo is set.
> 2. Setting MPTCP_PUSH_PENDING and scheduling the MPTCP worker in
> subflow_state_change() so that once the underlying subflow transitions
> to TCP_ESTABLISHED, pending queued bytes are immediately flushed.
>
> Reported-by: Petar Sakic <petar.sakic@ink.fish>
> Closes: https://lore.kernel.org/netdev/CAFPPu1gU2Y-D+d4i3F0MoNkYK+e1U+=X3qf6QycjfKBw+8snPg@mail.gmail.com/
> Fixes: e00b63056fb4 ("fastopen: only mark MPTFO subflows with SYN data")
> Signed-off-by: T S Rameshkumar <rameshkumar.t@phytecembedded.in>
> ---
> net/mptcp/options.c | 7 +++++++
> net/mptcp/subflow.c | 5 +++++
> 2 files changed, 12 insertions(+)
>
> diff --git a/net/mptcp/options.c b/net/mptcp/options.c
> index ce0de02f5..d5238fa11 100644
> --- a/net/mptcp/options.c
> +++ b/net/mptcp/options.c
> @@ -1042,6 +1042,13 @@ static bool check_fully_established(struct mptcp_sock *msk, struct sock *ssk,
>
> mptcp_data_lock((struct sock *)msk);
> __mptcp_subflow_fully_established(msk, subflow, mp_opt);
> + /* Passive TFO: the application may have written data while the
> + * subflow was still in SYN_RECV; __mptcp_subflow_active() refused
> + * it then and nothing else spools the msk write queue when the
> + * MPC third ack (no DSS) arrives. Push it now.
> + */
> + if (subflow->is_mptfo)
> + __mptcp_check_push((struct sock *)msk, ssk);
> mptcp_data_unlock((struct sock *)msk);
>
> check_notify:
> diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c
> index f0a6725d2..f499073a6 100644
> --- a/net/mptcp/subflow.c
> +++ b/net/mptcp/subflow.c
> @@ -1894,6 +1894,11 @@ static void subflow_state_change(struct sock *sk)
> if (subflow->resetting)
> return;
>
> + if (subflow->is_mptfo) {
> + set_bit(MPTCP_PUSH_PENDING, &mptcp_sk(parent)->cb_flags);
> + mptcp_schedule_work(parent);
> + }
> +
> /* as recvmsg() does not acquire the subflow socket for ssk selection
> * a fin packet carrying a DSS can be unnoticed if we don't trigger
> * the data available machinery here.
> --
> 2.34.1
>
^ permalink raw reply [flat|nested] 6+ messages in thread* Re: [PATCH] mptcp: push queued data on passive TFO subflows becoming established
2026-10-08 9:52 [PATCH] mptcp: push queued data on passive TFO subflows becoming established T S Rameshkumar
2026-10-08 9:59 ` Petar Sakic
@ 2026-10-08 10:03 ` sashiko-bot
2026-10-08 10:56 ` MPTCP CI
2026-10-12 10:21 ` netdev-bot+sashiko
3 siblings, 0 replies; 6+ messages in thread
From: sashiko-bot @ 2026-10-08 10:03 UTC (permalink / raw)
To: T S Rameshkumar; +Cc: mptcp
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] mptcp: data race on cb_flags due to mixed atomic and non-atomic bitops
- [Medium] mptcp: unintended worker scheduling on all TCP state changes for TFO subflows
--
commit ae556f46010534274aa7940f77f235a6807ae115
Author: T S Rameshkumar <rameshsv06@gmail.com>
mptcp: push queued data on passive TFO subflows becoming established
With TCP Fast Open on an MPTCP listener, if the server application writes
data while the passive subflow is still in SYN_RECV, the data is queued.
This patch attempts to push stranded data when the underlying subflow
transitions to established by modifying cb_flags and scheduling the MPTCP
worker.
Closes: https://lore.kernel.org/netdev/CAFPPu1gU2Y-D+d4i3F0MoNkYK+e1U+=X3qf6QycjfKBw+8snPg@mail.gmail.com/
> diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c
> index f0a6725d2c376..f499073a62d7c 100644
> --- a/net/mptcp/subflow.c
> +++ b/net/mptcp/subflow.c
[ ... ]
> @@ -1894,6 +1894,11 @@ static void subflow_state_change(struct sock *sk)
> if (subflow->resetting)
> return;
>
> + if (subflow->is_mptfo) {
[Severity: Medium]
Since subflow->is_mptfo is never cleared and there is no check for
sk->sk_state == TCP_ESTABLISHED here, won't this trigger on every subsequent
TCP state change?
The commit message states this logic is intended specifically for when "the
underlying subflow transitions to TCP_ESTABLISHED", but without checking the
state, this could fire erroneously and schedule the worker when transitioning
into shutdown states like TCP_FIN_WAIT1, TCP_CLOSE_WAIT, or TCP_CLOSE.
> + set_bit(MPTCP_PUSH_PENDING, &mptcp_sk(parent)->cb_flags);
[Severity: High]
Does mixing atomic and non-atomic bit operations on cb_flags cause data races
and lost updates?
subflow_state_change() uses the atomic set_bit() operation to modify
cb_flags without holding the parent MPTCP socket's mptcp_data_lock.
However, concurrently, other paths modify cb_flags using non-atomic operations
while holding mptcp_data_lock. For example:
net/mptcp/protocol.c:__mptcp_check_push() {
...
} else {
__set_bit(MPTCP_PUSH_PENDING, &mptcp_sk(sk)->cb_flags);
}
}
If the atomic set_bit() occurs between the load and store of the non-atomic
operation, the newly set bit will be clobbered and permanently lost, which
could lead to stranded data or missing wakeups.
> + mptcp_schedule_work(parent);
> + }
> +
> /* as recvmsg() does not acquire the subflow socket for ssk selection
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261008095231.103186-1-rameshkumar.t@phytecembedded.in?part=1
^ permalink raw reply [flat|nested] 6+ messages in thread* Re: [PATCH] mptcp: push queued data on passive TFO subflows becoming established
2026-10-08 9:52 [PATCH] mptcp: push queued data on passive TFO subflows becoming established T S Rameshkumar
2026-10-08 9:59 ` Petar Sakic
2026-10-08 10:03 ` sashiko-bot
@ 2026-10-08 10:56 ` MPTCP CI
2026-10-12 10:21 ` netdev-bot+sashiko
3 siblings, 0 replies; 6+ messages in thread
From: MPTCP CI @ 2026-10-08 10:56 UTC (permalink / raw)
To: T S Rameshkumar; +Cc: mptcp
Hi T,
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): Unstable: 1 failed test(s): packetdrill_syscalls ⚠️
- 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! ✅
- Perf: Success! ✅
- Task: https://github.com/multipath-tcp/mptcp_net-next/actions/runs/37761463175
Initiator: Patchew Applier
Commits: https://github.com/multipath-tcp/mptcp_net-next/commits/4cb33181b687
Patchwork: https://patchwork.kernel.org/project/mptcp/list/?series=1181518
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] 6+ messages in thread* Re: [PATCH] mptcp: push queued data on passive TFO subflows becoming established
2026-10-08 9:52 [PATCH] mptcp: push queued data on passive TFO subflows becoming established T S Rameshkumar
` (2 preceding siblings ...)
2026-10-08 10:56 ` MPTCP CI
@ 2026-10-12 10:21 ` netdev-bot+sashiko
3 siblings, 0 replies; 6+ messages in thread
From: netdev-bot+sashiko @ 2026-10-12 10:21 UTC (permalink / raw)
To: rameshsv06
Cc: matttbe, martineau, geliang, netdev, mptcp, linux-kernel,
petar.sakic, rameshkumar.t, kuba
Thank you for your contribution! Sashiko AI review found 6 potential
issue(s) to consider.
Critical: 0 · High: 1 · Medium: 2 · Low: 3
If you believe none of the issues are legitimate, please reply to each
issue of Medium severity or higher and explain why the AI is wrong.
If any of them are legitimate and you plan to respin, please let patchwork
know by including "pw-bot: cr" as a separate line at the end of your reply
(one such reply per series is enough).
- [High] The fix is incomplete because both new push triggers depend on
subflow->is_mptfo.
- [Medium] The new block in subflow_state_change()
(net/mptcp/subflow.c:1897-1900) checks only subflow->is_mptfo.
- [Medium] subflow_state_change() now does set_bit(MPTCP_PUSH_PENDING,
&mptcp_sk(parent)->cb_flags) without holding the msk data lock…
- [Low] The Fixes: tag names e00b63056fb4 ('mptcp: fastopen: only mark
MPTFO subflows with SYN data'), but that commit did not introduce the…
- [Low] The commit author is 'T S Rameshkumar <rameshsv06@gmail.com>',
but the only Signed-off-by is 'T S Rameshkumar…
- [Low] The new comment in check_fully_established()
(net/mptcp/options.c:1045-1049) says '... nothing else spools the msk…
--
mptcp: push queued data on passive TFO subflows becoming established
This patch targets data that gets queued in the msk write queue while a
passive TFO subflow is still in SYN_RECV. That data stays stranded after
the MPC third ACK arrives. The patch adds a __mptcp_check_push() call in
check_fully_established(). It also sets MPTCP_PUSH_PENDING and schedules
the msk worker in subflow_state_change(). Both changes are gated on
subflow->is_mptfo.
Closes: https://lore.kernel.org/netdev/CAFPPu1gU2Y-D+d4i3F0MoNkYK+e1U+=X3qf6QycjfKBw+8snPg@mail.gmail.com/
> Fixes: e00b63056fb4 ("fastopen: only mark MPTFO subflows with SYN data")
[Severity: Low]
Is this the right commit for the Fixes: tag?
The diff of e00b63056fb4 ("mptcp: fastopen: only mark MPTFO subflows with
SYN data") does two things in mptcp_fastopen_subflow_synack_set_params():
- it removes WARN_ON_ONCE(!skb)
- it moves subflow->is_mptfo = 1 after the skb_peek() check
It does not add or remove any push of the msk write queue.
This patch targets TFO SYNs that carry data, and for those the behaviour
is the same before and after that commit. The missing push after the MPC
third ACK seems to date back to the original passive MPTFO support.
e00b63056fb4 points there with its own Fixes: tag on 36b122baf6a8
("mptcp: add subflow_v(4,6)_send_synack()").
Would 36b122baf6a8 be a more accurate Fixes: target?
> Signed-off-by: T S Rameshkumar <rameshkumar.t@phytecembedded.in>
[Severity: Low]
The author is T S Rameshkumar <rameshsv06@gmail.com>, but the only
Signed-off-by uses rameshkumar.t@phytecembedded.in. checkpatch.pl reports
"From:/Signed-off-by: email address mismatch" for this.
Could the From: line or the sign-off be updated so they match?
> diff --git a/net/mptcp/options.c b/net/mptcp/options.c
> index ce0de02f5a3a1..d5238fa111dee 100644
> --- a/net/mptcp/options.c
> +++ b/net/mptcp/options.c
> @@ -1042,6 +1042,13 @@ static bool check_fully_established(struct mptcp_sock *msk, struct sock *ssk,
>
> mptcp_data_lock((struct sock *)msk);
> __mptcp_subflow_fully_established(msk, subflow, mp_opt);
> + /* Passive TFO: the application may have written data while the
> + * subflow was still in SYN_RECV; __mptcp_subflow_active() refused
> + * it then and nothing else spools the msk write queue when the
> + * MPC third ack (no DSS) arrives. Push it now.
> + */
[Severity: Low]
This isn't a bug, but is the "nothing else spools the msk write queue"
part of this comment still accurate?
The same patch adds a second push in subflow_state_change(). It fires on
the SYN_RECV->ESTABLISHED transition caused by this same third ACK, before
check_fully_established() runs.
The subflow.c hunk also has no comment explaining how the two mechanisms
relate.
> + if (subflow->is_mptfo)
> + __mptcp_check_push((struct sock *)msk, ssk);
[Severity: High]
Does this cover TFO connections where the SYN has a valid cookie but no
data? Since e00b63056fb4, is_mptfo is only set when the SYN had a
payload:
net/mptcp/fastopen.c:mptcp_fastopen_subflow_synack_set_params() {
...
/* A valid TFO cookie does not guarantee SYN data. */
skb = skb_peek(&ssk->sk_receive_queue);
if (!skb)
return;
subflow->is_mptfo = 1;
...
}
tcp_try_fastopen() still creates an early-accepted Fast Open child in
SYN_RECV whenever a cookie is present, even without data. A cookie
request without data also qualifies when TFO_SERVER_COOKIE_NOT_REQD is
set:
net/ipv4/tcp_fastopen.c:tcp_try_fastopen() {
...
if (!((tcp_fastopen & TFO_SERVER_ENABLE) &&
(syn_data || foc->len >= 0) &&
tcp_fastopen_queue_check(sk))) {
...
}
In that case mptcp_sk_clone_init() puts the msk in TCP_ESTABLISHED, so
the application can accept() and write() right away.
__mptcp_subflow_active()->__tcp_can_send() refuses the SYN_RECV subflow,
and the data stays in the msk write queue.
When the MPC third ACK arrives:
- check_fully_established() reaches set_fully_established but skips
__mptcp_check_push(), because is_mptfo is 0
- the new block in subflow_state_change() is skipped for the same reason
- the ACK has no DSS, so ack_update_msk() is not reached
- nothing was copied, so no rtx timer is armed
Wouldn't the data then stay stuck until the peer sends something? That is
the same stall this patch is meant to fix.
Should the condition be whether the msk was exposed or written to before
the subflow was fully established, rather than whether the SYN carried
data?
> mptcp_data_unlock((struct sock *)msk);
>
> check_notify:
> diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c
> index f0a6725d2c376..f499073a62d7c 100644
> --- a/net/mptcp/subflow.c
> +++ b/net/mptcp/subflow.c
> @@ -1894,6 +1894,11 @@ static void subflow_state_change(struct sock *sk)
> if (subflow->resetting)
> return;
>
> + if (subflow->is_mptfo) {
[Severity: Medium]
Should this be limited to the transition to TCP_ESTABLISHED?
is_mptfo is set once in mptcp_fastopen_subflow_synack_set_params() and is
never cleared. So this block runs on every sk_state_change of a passive
TFO subflow for the life of the connection, including:
- FIN receipt via tcp_fin() (ESTABLISHED->CLOSE_WAIT)
- FIN_WAIT1->FIN_WAIT2
- tcp_done()
Each of these schedules the msk worker, which takes the msk lock and runs
a __mptcp_push_pending() pass.
The commit message says this flushes "once the underlying subflow
transitions to TCP_ESTABLISHED", but there is no state check here.
Even for the SYN_RECV->ESTABLISHED transition, the callback runs before
the MPC third ACK is parsed:
tcp_rcv_state_process()
tcp_set_state(sk, TCP_ESTABLISHED);
sk->sk_state_change(sk); <- subflow_state_change()
...
tcp_data_queue()
mptcp_incoming_options()
check_fully_established()
So the worker is scheduled before subflow->fully_established is set.
Change 1 in check_fully_established() already pushes on the third ACK,
and the commit message doesn't explain why both mechanisms are needed.
Could that be clarified?
> + set_bit(MPTCP_PUSH_PENDING, &mptcp_sk(parent)->cb_flags);
[Severity: Medium]
Can this set_bit() race with the non-atomic updates to msk->cb_flags?
subflow_state_change() runs from the subflow's softirq path with only the
subflow lock held, not the msk data lock (parent->sk_lock.slock). Every
other cb_flags writer holds slock, and most of them use plain
read-modify-write operations. For example, in mptcp_release_cb():
msk->cb_flags &= ~flags;
spin_unlock_bh(&sk->sk_lock.slock);
...
if (__test_and_clear_bit(MPTCP_CLEAN_UNA, &msk->cb_flags))
and in mptcp_check_and_set_pending():
mptcp_data_lock(sk);
mptcp_sk(sk)->cb_flags |= BIT(MPTCP_PUSH_PENDING);
mptcp_data_unlock(sk);
An atomic set_bit() on one CPU doesn't protect against a plain RMW on
another:
CPU0 CPU1 (softirq)
release_sock(msk)
mptcp_release_cb()
load cb_flags (X)
subflow_state_change()
set_bit(MPTCP_PUSH_PENDING)
store X & ~flags
mptcp_schedule_work()
PUSH_PENDING is lost, and the worker's release_sock()->mptcp_release_cb()
then finds nothing to push.
Suppose the third ACK has no MPC option. check_fully_established() then
falls back via mptcp_try_fallback() and returns without pushing, so this
hook is the only trigger and the queued data would stay stranded.
KCSAN would likely also report the unlocked writer racing with the plain
reads in mptcp_release_cb().
Would it be better to set the bit under mptcp_data_lock(parent), as
mptcp_check_and_set_pending() does?
> + mptcp_schedule_work(parent);
> + }
[ ... ]
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20261008095231.103186-1-rameshkumar.t%40phytecembedded.in
^ permalink raw reply [flat|nested] 6+ messages in thread
* Bug report mptcp: passive TFO: data written by the server before the MPC third ACK is never sent
@ 2026-10-08 8:59 Petar Sakic
2026-10-08 10:05 ` [PATCH] mptcp: push queued data on passive TFO subflows becoming established T S Rameshkumar
0 siblings, 1 reply; 6+ messages in thread
From: Petar Sakic @ 2026-10-08 8:59 UTC (permalink / raw)
To: mptcp; +Cc: netdev, matttbe, martineau, geliang
Hi linux dev team,
There is bug I'd like to report:
Summary:
With TCP Fast Open on an MPTCP listener, if the server application
writes data while the passive subflow is still in SYN_RECV (i.e. after
it has consumed the client's SYN data but before the client's
MP_CAPABLE third ACK arrives), that data stays in the msk write queue
and is never transmitted. The connection hangs until the client gives
up and closes. Plain TCP with TFO is not affected.
Reproduced on:
Server kernels 6.8, 6.12.111, 6.18.15 and 7.2.6 (Debian builds),
client 6.12. Source read at 7.3-rc6 (602042bf, 7 Oct 2026). Lab:
client, NAT hop and server in separate network namespaces, ~120 ms
RTT.
Steps:
1. Server: `net.ipv4.tcp_fastopen=3`, an MPTCP listener with
`TCP_FASTOPEN`, an application that reads the request and replies
immediately (we used a proxy whose upstream answers in < 1 ms).
2. Client: `net.ipv4.tcp_fastopen=3`, MPTCP socket with
`TCP_FASTOPEN_CONNECT`; prime the cookie with one connection.
3. Second connection: the request fits in the SYN. Server replies
before the client's third ACK arrives.
Expected: the reply is sent once the subflow is fully established.
Actual: server `ss -M` shows FIN-WAIT-1 with the reply bytes queued
and `bytes_sent` unchanged; the wire shows the reply leave only after
the client closes. If the server's reply comes later than one RTT
(slow upstream), everything works.
Analysis:
- `__mptcp_subflow_active()` / `__tcp_can_send()` skip a subflow that
is not yet established, so the server's early write is queued on the
msk.
- When the MPC third ACK arrives, `check_fully_established()`
(options.c) marks the subflow fully established but nothing pushes the
msk write queue. The third ACK carries no DSS, so no later event
triggers a push until the peer sends data, and the peer is waiting for
the reply.
Candidate fix: (tested only in the lab: 30/30 requests succeed with
TFO in use on a patched 6.12.111)
```diff
--- a/net/mptcp/options.c
+++ b/net/mptcp/options.c
@@ -1058,6 +1058,13 @@ set_fully_established:
mptcp_data_lock((struct sock *)msk);
__mptcp_subflow_fully_established(msk, subflow, mp_opt);
+ /* Passive TFO: the application may have written data while the
+ * subflow was still in SYN_RECV; __mptcp_subflow_active() refused
+ * it then and nothing else spools the msk write queue when the
+ * MPC third ack (no DSS) arrives. Push it now.
+ */
+ if (subflow->is_mptfo)
+ __mptcp_check_push((struct sock *)msk, ssk);
mptcp_data_unlock((struct sock *)msk);
check_notify:
```
We are not kernel developers; the maintainers will know whether this
belongs here or in the fully-established path. Related but different:
e00b63056fb4 ("fastopen: only mark MPTFO subflows with SYN data", Aug
2026).
Impact we saw in production-like use
Shadowsocks-2022 proxies over MPTCP (xray) put the whole request in
the first write, so small requests ride the SYN; requests to targets
near the server hang. We have turned TFO off.
Thank you
Kind regards
--
Petar Sakic
ETO/AV-IT Engineer
Inkfish | www.ink.fish
Deeply curious
This e-mail is for the sole use of the intended recipient(s). It
contains information that may be confidential. If you believe that it
has been sent to you in error, please notify the sender immediately by
reply e-mail and destroy all copies of this message. Any disclosure,
copying, distribution, or use of this information by anyone other than
the intended recipient is prohibited.
^ permalink raw reply [flat|nested] 6+ messages in thread* Re: [PATCH] mptcp: push queued data on passive TFO subflows becoming established
2026-10-08 8:59 Bug report mptcp: passive TFO: data written by the server before the MPC third ACK is never sent Petar Sakic
@ 2026-10-08 10:05 ` T S Rameshkumar
0 siblings, 0 replies; 6+ messages in thread
From: T S Rameshkumar @ 2026-10-08 10:05 UTC (permalink / raw)
To: Petar Sakic
Cc: Matthieu Baerts, Mat Martineau, Geliang Tang, netdev, mptcp,
linux-kernel
Hi Petar, Matthieu, Mat,
Thanks for the feedback and confirmation!
Indeed, since the bug predates e00b63056fb4 and exists back to when server-side
Fast Open was originally introduced, the appropriate Fixes tag would be:
Fixes: fb7084501a61 ("mptcp: add support for TCP_FASTOPEN sockopt")
During our testing with simulated network latency in namespaces, we also found
that when the 3rd ACK arrives:
1. check_fully_established() marks subflow->fully_established = true.
2. But within tcp_ack(), the underlying TCP state is still TCP_SYN_RECV until
tcp_rcv_state_process() sets it to TCP_ESTABLISHED.
3. Therefore, invoking the push in subflow_state_change() ensures the subflow
is recognized as active by __tcp_can_send() and immediately flushes the
queued write buffer.
I can send a [PATCH net v2] with the updated Fixes tag once maintainers review.
Thanks,
T S Rameshkumar
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-10-12 10:21 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-08 9:52 [PATCH] mptcp: push queued data on passive TFO subflows becoming established T S Rameshkumar
2026-10-08 9:59 ` Petar Sakic
2026-10-08 10:03 ` sashiko-bot
2026-10-08 10:56 ` MPTCP CI
2026-10-12 10:21 ` netdev-bot+sashiko
-- strict thread matches above, loose matches on Subject: below --
2026-10-08 8:59 Bug report mptcp: passive TFO: data written by the server before the MPC third ACK is never sent Petar Sakic
2026-10-08 10:05 ` [PATCH] mptcp: push queued data on passive TFO subflows becoming established T S Rameshkumar
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.