* [PATCH export v3 0/4] mptcp: fix TCP fallback on single-subflow MP_FAIL
@ 2026-08-12 5:46 Chenguang Zhao
2026-08-12 5:46 ` [PATCH export v3 1/4] mptcp: add MPFailFallback MIB Chenguang Zhao
` (4 more replies)
0 siblings, 5 replies; 19+ messages in thread
From: Chenguang Zhao @ 2026-08-12 5:46 UTC (permalink / raw)
To: mptcp; +Cc: chenguang.zhao, Chenguang Zhao
From: Chenguang Zhao <zhaochenguang@kylinos.cn>
When a valid MP_FAIL is received and infinite fallback is still allowed
(single contiguous subflow), RFC8684 section 3.7 requires leaving MPTCP
mode. Today the stack only clears allow_subflows and defers the real
fallback to the later infinite-map transmit path. If no data is sent in
between, the peer can still complete the 4th ACK as MPTCP and keep using
MPTCP options.
This series adds a dedicated MPFailFallback MIB, rejects joins after
fallback via mptcp_is_fully_established(), resets when the OoO queue
blocks infinite mapping, and falls back immediately after the MP_FAIL
response through mptcp_try_fallback().
Patch 1 adds the MPFailFallback MIB.
Patch 2 rejects joins once fallback has completed.
Patch 3 resets on MP_FAIL when the OoO queue is non-empty.
Patch 4 falls back to TCP after sending the MP_FAIL response.
v3:
- Address Matthieu Baerts' review
- Introduce the MIB first (Fixes: c65c2e3bae69) and reuse
mptcp_try_fallback() in the functional fix
- Split mptcp_is_fully_established() and OoO handling into dedicated
commits for easier backport
- Check FALLBACK_DONE via test_bit()
- Drop allow_subflows from mptcp_is_fully_established() (and the
READ/WRITE_ONCE follow-up)
- On OoO failure, reset without sending MP_FAIL
- This patch series is based on the export branch, The git repository
is: git://git.kernel.org/pub/scm/linux/kernel/git/mptcp/linux.git
Chenguang Zhao (4):
mptcp: add MPFailFallback MIB
mptcp: reject joins after fallback in mptcp_is_fully_established
mptcp: reset subflow on MP_FAIL when OoO queue is non-empty
mptcp: fallback to TCP on MP_FAIL with a single subflow
net/mptcp/mib.c | 1 +
net/mptcp/mib.h | 1 +
net/mptcp/pm.c | 17 +++++++++++++++--
net/mptcp/protocol.c | 4 +++-
net/mptcp/protocol.h | 5 ++++-
5 files changed, 24 insertions(+), 4 deletions(-)
---
v2:
https://lore.kernel.org/all/20260715061830.1057851-1-chenguang.zhao@linux.dev/
v1:
https://lore.kernel.org/all/20260713064134.914507-1-chenguang.zhao@linux.dev/
--
2.25.1
^ permalink raw reply [flat|nested] 19+ messages in thread
* [PATCH export v3 1/4] mptcp: add MPFailFallback MIB
2026-08-12 5:46 [PATCH export v3 0/4] mptcp: fix TCP fallback on single-subflow MP_FAIL Chenguang Zhao
@ 2026-08-12 5:46 ` Chenguang Zhao
2026-08-12 5:46 ` [PATCH export v3 2/4] mptcp: reject joins after fallback in mptcp_is_fully_established Chenguang Zhao
` (3 subsequent siblings)
4 siblings, 0 replies; 19+ messages in thread
From: Chenguang Zhao @ 2026-08-12 5:46 UTC (permalink / raw)
To: mptcp; +Cc: chenguang.zhao, Chenguang Zhao
From: Chenguang Zhao <zhaochenguang@kylinos.cn>
Add a dedicated MIB to track TCP fallbacks triggered by receiving
MP_FAIL. A follow-up change will use it from
mptcp_pm_mp_fail_received() via mptcp_try_fallback().
Fixes: c65c2e3bae69 ("mptcp: track fallbacks accurately via mibs")
Signed-off-by: Chenguang Zhao <zhaochenguang@kylinos.cn>
---
net/mptcp/mib.c | 1 +
net/mptcp/mib.h | 1 +
2 files changed, 2 insertions(+)
diff --git a/net/mptcp/mib.c b/net/mptcp/mib.c
index 608cb568897c..3e1bb4ff07b7 100644
--- a/net/mptcp/mib.c
+++ b/net/mptcp/mib.c
@@ -90,6 +90,7 @@ static const struct snmp_mib mptcp_snmp_list[] = {
SNMP_MIB_ITEM("DssFallback", MPTCP_MIB_DSSFALLBACK),
SNMP_MIB_ITEM("DssReset", MPTCP_MIB_DSSRESET),
SNMP_MIB_ITEM("SimultConnectFallback", MPTCP_MIB_SIMULTCONNFALLBACK),
+ SNMP_MIB_ITEM("MPFailFallback", MPTCP_MIB_MPFAILFALLBACK),
SNMP_MIB_ITEM("FallbackFailed", MPTCP_MIB_FALLBACKFAILED),
SNMP_MIB_ITEM("WinProbe", MPTCP_MIB_WINPROBE),
SNMP_MIB_ITEM("BacklogDrop", MPTCP_MIB_BACKLOGDROP),
diff --git a/net/mptcp/mib.h b/net/mptcp/mib.h
index 1ebdb55e9534..89ebf7086cc6 100644
--- a/net/mptcp/mib.h
+++ b/net/mptcp/mib.h
@@ -93,6 +93,7 @@ enum linux_mptcp_mib_field {
MPTCP_MIB_DSSFALLBACK, /* Bad or missing DSS */
MPTCP_MIB_DSSRESET, /* MP_RST: bad data mapping */
MPTCP_MIB_SIMULTCONNFALLBACK, /* Simultaneous connect */
+ MPTCP_MIB_MPFAILFALLBACK, /* Received MP_FAIL, fallback to TCP */
MPTCP_MIB_FALLBACKFAILED, /* Can't fallback due to msk status */
MPTCP_MIB_WINPROBE, /* MPTCP-level zero window probe */
MPTCP_MIB_BACKLOGDROP, /* Backlog over memory limit */
--
2.25.1
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH export v3 2/4] mptcp: reject joins after fallback in mptcp_is_fully_established
2026-08-12 5:46 [PATCH export v3 0/4] mptcp: fix TCP fallback on single-subflow MP_FAIL Chenguang Zhao
2026-08-12 5:46 ` [PATCH export v3 1/4] mptcp: add MPFailFallback MIB Chenguang Zhao
@ 2026-08-12 5:46 ` Chenguang Zhao
2026-08-12 9:45 ` Paolo Abeni
2026-08-12 5:46 ` [PATCH export v3 3/4] mptcp: reset subflow on MP_FAIL when OoO queue is non-empty Chenguang Zhao
` (2 subsequent siblings)
4 siblings, 1 reply; 19+ messages in thread
From: Chenguang Zhao @ 2026-08-12 5:46 UTC (permalink / raw)
To: mptcp; +Cc: chenguang.zhao, Chenguang Zhao
From: Chenguang Zhao <zhaochenguang@kylinos.cn>
After fallback, treat the connection as not fully established so later
MP_JOIN attempts are rejected.
Signed-off-by: Chenguang Zhao <zhaochenguang@kylinos.cn>
---
net/mptcp/protocol.h | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h
index 7e168e450fb0..bf2483a7ed92 100644
--- a/net/mptcp/protocol.h
+++ b/net/mptcp/protocol.h
@@ -957,8 +957,11 @@ static inline void mptcp_start_tout_timer(struct sock *sk)
static inline bool mptcp_is_fully_established(struct sock *sk)
{
+ struct mptcp_sock *msk = mptcp_sk(sk);
+
return inet_sk_state_load(sk) == TCP_ESTABLISHED &&
- READ_ONCE(mptcp_sk(sk)->fully_established);
+ READ_ONCE(msk->fully_established) &&
+ !test_bit(MPTCP_FALLBACK_DONE, &msk->flags);
}
static inline u64 mptcp_stamp(void)
--
2.25.1
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH export v3 3/4] mptcp: reset subflow on MP_FAIL when OoO queue is non-empty
2026-08-12 5:46 [PATCH export v3 0/4] mptcp: fix TCP fallback on single-subflow MP_FAIL Chenguang Zhao
2026-08-12 5:46 ` [PATCH export v3 1/4] mptcp: add MPFailFallback MIB Chenguang Zhao
2026-08-12 5:46 ` [PATCH export v3 2/4] mptcp: reject joins after fallback in mptcp_is_fully_established Chenguang Zhao
@ 2026-08-12 5:46 ` Chenguang Zhao
2026-08-12 6:07 ` sashiko-bot
2026-08-12 10:02 ` Paolo Abeni
2026-08-12 5:46 ` [PATCH export v3 4/4] mptcp: fallback to TCP on MP_FAIL with a single subflow Chenguang Zhao
2026-08-12 6:57 ` [PATCH export v3 0/4] mptcp: fix TCP fallback on single-subflow MP_FAIL MPTCP CI
4 siblings, 2 replies; 19+ messages in thread
From: Chenguang Zhao @ 2026-08-12 5:46 UTC (permalink / raw)
To: mptcp; +Cc: chenguang.zhao, Chenguang Zhao
From: Chenguang Zhao <zhaochenguang@kylinos.cn>
Infinite mapping needs contiguous data. If the MPTCP OoO queue is not
empty, reset the subflow instead of falling back.
Signed-off-by: Chenguang Zhao <zhaochenguang@kylinos.cn>
---
net/mptcp/pm.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/net/mptcp/pm.c b/net/mptcp/pm.c
index ba7c6f80a183..8c263084db7b 100644
--- a/net/mptcp/pm.c
+++ b/net/mptcp/pm.c
@@ -882,6 +882,16 @@ void mptcp_pm_mp_fail_received(struct sock *sk, u64 fail_seq)
spin_unlock_bh(&msk->fallback_lock);
return;
}
+
+ /* RFC8684 §3.7: Infinite mapping requires contiguous data */
+ if (!subflow->fail_tout &&
+ !RB_EMPTY_ROOT(&msk->out_of_order_queue)) {
+ spin_unlock_bh(&msk->fallback_lock);
+ MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_FALLBACKFAILED);
+ mptcp_subflow_reset(sk);
+ return;
+ }
+
msk->allow_subflows = false;
spin_unlock_bh(&msk->fallback_lock);
--
2.25.1
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH export v3 4/4] mptcp: fallback to TCP on MP_FAIL with a single subflow
2026-08-12 5:46 [PATCH export v3 0/4] mptcp: fix TCP fallback on single-subflow MP_FAIL Chenguang Zhao
` (2 preceding siblings ...)
2026-08-12 5:46 ` [PATCH export v3 3/4] mptcp: reset subflow on MP_FAIL when OoO queue is non-empty Chenguang Zhao
@ 2026-08-12 5:46 ` Chenguang Zhao
2026-08-12 10:43 ` Paolo Abeni
2026-08-12 10:47 ` Paolo Abeni
2026-08-12 6:57 ` [PATCH export v3 0/4] mptcp: fix TCP fallback on single-subflow MP_FAIL MPTCP CI
4 siblings, 2 replies; 19+ messages in thread
From: Chenguang Zhao @ 2026-08-12 5:46 UTC (permalink / raw)
To: mptcp; +Cc: chenguang.zhao, Chenguang Zhao
From: Chenguang Zhao <zhaochenguang@kylinos.cn>
Fall back immediately via mptcp_try_fallback() after accepting MP_FAIL
on a single contiguous subflow, as required by RFC8684 §3.7.
Fixes: 1e39e5a32ad7 ("mptcp: infinite mapping sending")
Signed-off-by: Chenguang Zhao <zhaochenguang@kylinos.cn>
---
net/mptcp/pm.c | 9 ++++++---
net/mptcp/protocol.c | 4 +++-
2 files changed, 9 insertions(+), 4 deletions(-)
diff --git a/net/mptcp/pm.c b/net/mptcp/pm.c
index 8c263084db7b..cb85caf1df43 100644
--- a/net/mptcp/pm.c
+++ b/net/mptcp/pm.c
@@ -876,7 +876,6 @@ void mptcp_pm_mp_fail_received(struct sock *sk, u64 fail_seq)
pr_debug("fail_seq=%llu\n", fail_seq);
- /* After accepting the fail, we can't create any other subflows */
spin_lock_bh(&msk->fallback_lock);
if (!msk->allow_infinite_fallback) {
spin_unlock_bh(&msk->fallback_lock);
@@ -891,8 +890,6 @@ void mptcp_pm_mp_fail_received(struct sock *sk, u64 fail_seq)
mptcp_subflow_reset(sk);
return;
}
-
- msk->allow_subflows = false;
spin_unlock_bh(&msk->fallback_lock);
if (!subflow->fail_tout) {
@@ -901,6 +898,12 @@ void mptcp_pm_mp_fail_received(struct sock *sk, u64 fail_seq)
subflow->send_mp_fail = 1;
subflow->send_infinite_map = 1;
tcp_send_ack(sk);
+
+ /* RFC8684 §3.7: fallback with a single subflow */
+ if (!mptcp_try_fallback(sk, MPTCP_MIB_MPFAILFALLBACK)) {
+ MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_FALLBACKFAILED);
+ mptcp_subflow_reset(sk);
+ }
} else {
pr_debug("MP_FAIL response received\n");
WRITE_ONCE(subflow->fail_tout, 0);
diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c
index f879b1061f2d..519e8d9c3164 100644
--- a/net/mptcp/protocol.c
+++ b/net/mptcp/protocol.c
@@ -1418,7 +1418,9 @@ static void mptcp_update_infinite_map(struct mptcp_sock *msk,
mpext->infinite_map = 1;
mpext->data_len = 0;
- if (!mptcp_try_fallback(ssk, MPTCP_MIB_INFINITEMAPTX)) {
+ if (__mptcp_check_fallback(msk)) {
+ MPTCP_INC_STATS(sock_net(ssk), MPTCP_MIB_INFINITEMAPTX);
+ } else if (!mptcp_try_fallback(ssk, MPTCP_MIB_INFINITEMAPTX)) {
MPTCP_INC_STATS(sock_net(ssk), MPTCP_MIB_FALLBACKFAILED);
mptcp_subflow_reset(ssk);
return;
--
2.25.1
^ permalink raw reply related [flat|nested] 19+ messages in thread
* Re: [PATCH export v3 3/4] mptcp: reset subflow on MP_FAIL when OoO queue is non-empty
2026-08-12 5:46 ` [PATCH export v3 3/4] mptcp: reset subflow on MP_FAIL when OoO queue is non-empty Chenguang Zhao
@ 2026-08-12 6:07 ` sashiko-bot
2026-08-12 10:02 ` Paolo Abeni
1 sibling, 0 replies; 19+ messages in thread
From: sashiko-bot @ 2026-08-12 6:07 UTC (permalink / raw)
To: Chenguang Zhao; +Cc: mptcp
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] TOCTOU race due to lockless access of `msk->out_of_order_queue`.
--
--- Patch [3]: [PATCH export v3 3/4] mptcp: reset subflow on MP_FAIL when OoO queue is non-empty ---
commit fe6cef2f543d25c806aed2ee11c8a4d7a936c259
Author: Chenguang Zhao <zhaochenguang@kylinos.cn>
mptcp: reset subflow on MP_FAIL when OoO queue is non-empty
This commit updates the handling of an MP_FAIL response to reset the subflow
instead of falling back when the out-of-order queue is not empty. This is done
to ensure the contiguous data requirements for infinite mapping are met.
> diff --git a/net/mptcp/pm.c b/net/mptcp/pm.c
> index ba7c6f80a183c..8c263084db7be 100644
> --- a/net/mptcp/pm.c
> +++ b/net/mptcp/pm.c
> @@ -882,6 +882,16 @@ void mptcp_pm_mp_fail_received(struct sock *sk, u64 fail_seq)
> spin_unlock_bh(&msk->fallback_lock);
> return;
> }
> +
> + /* RFC8684 §3.7: Infinite mapping requires contiguous data */
> + if (!subflow->fail_tout &&
> + !RB_EMPTY_ROOT(&msk->out_of_order_queue)) {
[Severity: High]
Can this lockless check of the out-of-order queue lead to a race condition?
Looking at mptcp_pm_mp_fail_received(), the out_of_order_queue is checked
for emptiness while only holding msk->fallback_lock, without the required
mptcp_data_lock.
If another active subflow concurrently receives out-of-order data, acquires
the msk data lock, and inserts data into the queue, this check might
incorrectly conclude the queue is empty.
If the queue is populated right after this lockless check but before
mptcp_try_fallback() is called, could this cause an infinite map TCP ACK to
be incorrectly transmitted before the fallback is ultimately aborted?
Furthermore, if the queue is populated immediately after fallback occurs,
could this stranded MPTCP out-of-order data be improperly appended to the
plain TCP receive queue as the stream advances, resulting in data duplication
and stream corruption?
> + spin_unlock_bh(&msk->fallback_lock);
> + MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_FALLBACKFAILED);
> + mptcp_subflow_reset(sk);
> + return;
> + }
> +
> msk->allow_subflows = false;
> spin_unlock_bh(&msk->fallback_lock);
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260812054653.43799-1-chenguang.zhao@linux.dev?part=3
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH export v3 0/4] mptcp: fix TCP fallback on single-subflow MP_FAIL
2026-08-12 5:46 [PATCH export v3 0/4] mptcp: fix TCP fallback on single-subflow MP_FAIL Chenguang Zhao
` (3 preceding siblings ...)
2026-08-12 5:46 ` [PATCH export v3 4/4] mptcp: fallback to TCP on MP_FAIL with a single subflow Chenguang Zhao
@ 2026-08-12 6:57 ` MPTCP CI
4 siblings, 0 replies; 19+ messages in thread
From: MPTCP CI @ 2026-08-12 6:57 UTC (permalink / raw)
To: Chenguang Zhao; +Cc: mptcp
Hi Chenguang,
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/31568808826
Initiator: Patchew Applier
Commits: https://github.com/multipath-tcp/mptcp_net-next/commits/185641831778
Patchwork: https://patchwork.kernel.org/project/mptcp/list/?series=1144463
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] 19+ messages in thread
* Re: [PATCH export v3 2/4] mptcp: reject joins after fallback in mptcp_is_fully_established
2026-08-12 5:46 ` [PATCH export v3 2/4] mptcp: reject joins after fallback in mptcp_is_fully_established Chenguang Zhao
@ 2026-08-12 9:45 ` Paolo Abeni
2026-08-12 15:32 ` Matthieu Baerts
2026-08-13 8:45 ` Chenguang Zhao
0 siblings, 2 replies; 19+ messages in thread
From: Paolo Abeni @ 2026-08-12 9:45 UTC (permalink / raw)
To: Chenguang Zhao, mptcp; +Cc: Chenguang Zhao
On 8/12/26 7:46 AM, Chenguang Zhao wrote:
> From: Chenguang Zhao <zhaochenguang@kylinos.cn>
>
> After fallback, treat the connection as not fully established so later
> MP_JOIN attempts are rejected.
>
> Signed-off-by: Chenguang Zhao <zhaochenguang@kylinos.cn>
> ---
> net/mptcp/protocol.h | 5 ++++-
> 1 file changed, 4 insertions(+), 1 deletion(-)
>
> diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h
> index 7e168e450fb0..bf2483a7ed92 100644
> --- a/net/mptcp/protocol.h
> +++ b/net/mptcp/protocol.h
> @@ -957,8 +957,11 @@ static inline void mptcp_start_tout_timer(struct sock *sk)
>
> static inline bool mptcp_is_fully_established(struct sock *sk)
> {
> + struct mptcp_sock *msk = mptcp_sk(sk);
> +
> return inet_sk_state_load(sk) == TCP_ESTABLISHED &&
> - READ_ONCE(mptcp_sk(sk)->fully_established);
> + READ_ONCE(msk->fully_established) &&
> + !test_bit(MPTCP_FALLBACK_DONE, &msk->flags);
Does the above improve actually anything? The test is inherently racy,
as lack the fallback_lock, and AFAICS all critical paths have already
explicit checks under such lock, see i.e. mptcp_finish_join().
I would prefer avoiding additional conditionals, if not well reasoned.
/P
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH export v3 3/4] mptcp: reset subflow on MP_FAIL when OoO queue is non-empty
2026-08-12 5:46 ` [PATCH export v3 3/4] mptcp: reset subflow on MP_FAIL when OoO queue is non-empty Chenguang Zhao
2026-08-12 6:07 ` sashiko-bot
@ 2026-08-12 10:02 ` Paolo Abeni
2026-08-13 8:45 ` Chenguang Zhao
1 sibling, 1 reply; 19+ messages in thread
From: Paolo Abeni @ 2026-08-12 10:02 UTC (permalink / raw)
To: Chenguang Zhao, mptcp; +Cc: Chenguang Zhao
On 8/12/26 7:46 AM, Chenguang Zhao wrote:
> From: Chenguang Zhao <zhaochenguang@kylinos.cn>
>
> Infinite mapping needs contiguous data. If the MPTCP OoO queue is not
> empty, reset the subflow instead of falling back.
Why? the RFC says:
"""if all unacknowledged data in flight is contiguous"""
That statement can be true even when the RTX queue is not empty.
It will become false if/when some mptcp-level retransmission will happen.
__mptcp_push_retrans() atomically sets `allow_infinite_fallback` to
false when such retransmission happen. The code just before the newly
added one checks such condition under the relevant lock.
I don't think this change is needed.
/P
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH export v3 4/4] mptcp: fallback to TCP on MP_FAIL with a single subflow
2026-08-12 5:46 ` [PATCH export v3 4/4] mptcp: fallback to TCP on MP_FAIL with a single subflow Chenguang Zhao
@ 2026-08-12 10:43 ` Paolo Abeni
2026-08-13 8:48 ` Chenguang Zhao
2026-08-12 10:47 ` Paolo Abeni
1 sibling, 1 reply; 19+ messages in thread
From: Paolo Abeni @ 2026-08-12 10:43 UTC (permalink / raw)
To: Chenguang Zhao, mptcp; +Cc: Chenguang Zhao
On 8/12/26 7:46 AM, Chenguang Zhao wrote:
> From: Chenguang Zhao <zhaochenguang@kylinos.cn>
>
> Fall back immediately via mptcp_try_fallback() after accepting MP_FAIL
> on a single contiguous subflow, as required by RFC8684 §3.7.
>
> Fixes: 1e39e5a32ad7 ("mptcp: infinite mapping sending")
> Signed-off-by: Chenguang Zhao <zhaochenguang@kylinos.cn>
> ---
> net/mptcp/pm.c | 9 ++++++---
> net/mptcp/protocol.c | 4 +++-
> 2 files changed, 9 insertions(+), 4 deletions(-)
>
> diff --git a/net/mptcp/pm.c b/net/mptcp/pm.c
> index 8c263084db7b..cb85caf1df43 100644
> --- a/net/mptcp/pm.c
> +++ b/net/mptcp/pm.c
> @@ -876,7 +876,6 @@ void mptcp_pm_mp_fail_received(struct sock *sk, u64 fail_seq)
>
> pr_debug("fail_seq=%llu\n", fail_seq);
>
> - /* After accepting the fail, we can't create any other subflows */
> spin_lock_bh(&msk->fallback_lock);
> if (!msk->allow_infinite_fallback) {
> spin_unlock_bh(&msk->fallback_lock);
> @@ -891,8 +890,6 @@ void mptcp_pm_mp_fail_received(struct sock *sk, u64 fail_seq)
> mptcp_subflow_reset(sk);
> return;
> }
> -
> - msk->allow_subflows = false;
> spin_unlock_bh(&msk->fallback_lock);
At this point another subflow can complete the join, and set
allow_infinite_fallback = false ...
>
> if (!subflow->fail_tout) {
> @@ -901,6 +898,12 @@ void mptcp_pm_mp_fail_received(struct sock *sk, u64 fail_seq)
> subflow->send_mp_fail = 1;
> subflow->send_infinite_map = 1;
> tcp_send_ack(sk);
... so the this mp_fail processing will be bogus [1].
> +
> + /* RFC8684 §3.7: fallback with a single subflow */
> + if (!mptcp_try_fallback(sk, MPTCP_MIB_MPFAILFALLBACK)) {
> + MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_FALLBACKFAILED);
> + mptcp_subflow_reset(sk);
> + }
> } else {
> pr_debug("MP_FAIL response received\n");
> WRITE_ONCE(subflow->fail_tout, 0);
> diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c
> index f879b1061f2d..519e8d9c3164 100644
> --- a/net/mptcp/protocol.c
> +++ b/net/mptcp/protocol.c
> @@ -1418,7 +1418,9 @@ static void mptcp_update_infinite_map(struct mptcp_sock *msk,
> mpext->infinite_map = 1;
> mpext->data_len = 0;
>
> - if (!mptcp_try_fallback(ssk, MPTCP_MIB_INFINITEMAPTX)) {
> + if (__mptcp_check_fallback(msk)) {
> + MPTCP_INC_STATS(sock_net(ssk), MPTCP_MIB_INFINITEMAPTX);
> + } else if (!mptcp_try_fallback(ssk, MPTCP_MIB_INFINITEMAPTX)) {
> MPTCP_INC_STATS(sock_net(ssk), MPTCP_MIB_FALLBACKFAILED);
> mptcp_subflow_reset(ssk);
> return;
I don't understand this change. Can we ever enter the
`if (!mptcp_try_fallback(ssk, MPTCP_MIB_INFINITEMAPTX)) {`
branch? The msk already tried to fallback in
mptcp_pm_mp_fail_received(). If the fallback was successful, the code
will enter the `if (__mptcp_check_fallback(msk)) {` branch and not this one.
Otherwise the fallback will fail again (AFAICS nothing resets
`allow_infinite_fallback` once in become false).
/P
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH export v3 4/4] mptcp: fallback to TCP on MP_FAIL with a single subflow
2026-08-12 5:46 ` [PATCH export v3 4/4] mptcp: fallback to TCP on MP_FAIL with a single subflow Chenguang Zhao
2026-08-12 10:43 ` Paolo Abeni
@ 2026-08-12 10:47 ` Paolo Abeni
2026-08-13 8:50 ` Chenguang Zhao
1 sibling, 1 reply; 19+ messages in thread
From: Paolo Abeni @ 2026-08-12 10:47 UTC (permalink / raw)
To: Chenguang Zhao, mptcp; +Cc: Chenguang Zhao
On 8/12/26 7:46 AM, Chenguang Zhao wrote:
> @@ -901,6 +898,12 @@ void mptcp_pm_mp_fail_received(struct sock *sk, u64 fail_seq)
> subflow->send_mp_fail = 1;
> subflow->send_infinite_map = 1;
> tcp_send_ack(sk);
Slightly unrelated... AFAICS the current code sends the mp_fail reply
immediatally, and will send the infinite mapping with the later data
send, if any.
I *think* it will be better to send MP_FAIL and infinite mapping
together. It should remove a little bit of complexity on the xmit path
and will be IMHO closer to the RFC specs.
/P
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH export v3 2/4] mptcp: reject joins after fallback in mptcp_is_fully_established
2026-08-12 9:45 ` Paolo Abeni
@ 2026-08-12 15:32 ` Matthieu Baerts
2026-08-13 9:32 ` gang.yan
2026-08-13 8:45 ` Chenguang Zhao
1 sibling, 1 reply; 19+ messages in thread
From: Matthieu Baerts @ 2026-08-12 15:32 UTC (permalink / raw)
To: Paolo Abeni, Chenguang Zhao, mptcp; +Cc: Chenguang Zhao
Hi Paolo, Chenguang,
On 12/08/2026 11:45, Paolo Abeni wrote:
> On 8/12/26 7:46 AM, Chenguang Zhao wrote:
>> From: Chenguang Zhao <zhaochenguang@kylinos.cn>
>>
>> After fallback, treat the connection as not fully established so later
>> MP_JOIN attempts are rejected.
@Chenguang: please first reply to the questions and comments from the
previous versions, then only send a new version when the discussions are
over.
>> Signed-off-by: Chenguang Zhao <zhaochenguang@kylinos.cn>
>> ---
>> net/mptcp/protocol.h | 5 ++++-
>> 1 file changed, 4 insertions(+), 1 deletion(-)
>>
>> diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h
>> index 7e168e450fb0..bf2483a7ed92 100644
>> --- a/net/mptcp/protocol.h
>> +++ b/net/mptcp/protocol.h
>> @@ -957,8 +957,11 @@ static inline void mptcp_start_tout_timer(struct sock *sk)
>>
>> static inline bool mptcp_is_fully_established(struct sock *sk)
>> {
>> + struct mptcp_sock *msk = mptcp_sk(sk);
>> +
>> return inet_sk_state_load(sk) == TCP_ESTABLISHED &&
>> - READ_ONCE(mptcp_sk(sk)->fully_established);
>> + READ_ONCE(msk->fully_established) &&
>> + !test_bit(MPTCP_FALLBACK_DONE, &msk->flags);
>
> Does the above improve actually anything? The test is inherently racy,
> as lack the fallback_lock, and AFAICS all critical paths have already
> explicit checks under such lock, see i.e. mptcp_finish_join().
Indeed, the fallback lock is missing.
Note: Chenguang is looking at a bug where, when an MP_FAIL is received
before being in fully_established, the kernel accept going to fully
established, and creating new subflows, then reject them once done.
Same when receiving a 4th packet without MPTCP options.
I think the MP_FAIL should do a fallback if possible, but it is probably
also required to check the fallback status or something similar when
receiving an MPJ.
> I would prefer avoiding additional conditionals, if not well reasoned.
Agreed, the commit messages in this series are not explaining (enough)
the reason and the context.
BTW, Chenguang, please use 'mptcp-net' for fixes (and mptcp-next for
features). Also fixes should have a Fixes tag.
Cheers,
Matt
--
Sponsored by the NGI0 Core fund.
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH export v3 2/4] mptcp: reject joins after fallback in mptcp_is_fully_established
2026-08-12 9:45 ` Paolo Abeni
2026-08-12 15:32 ` Matthieu Baerts
@ 2026-08-13 8:45 ` Chenguang Zhao
2026-08-13 18:06 ` Matthieu Baerts
1 sibling, 1 reply; 19+ messages in thread
From: Chenguang Zhao @ 2026-08-13 8:45 UTC (permalink / raw)
To: Paolo Abeni, mptcp; +Cc: Chenguang Zhao
在 2026/8/12 17:45, Paolo Abeni 写道:
> On 8/12/26 7:46 AM, Chenguang Zhao wrote:
>> From: Chenguang Zhao <zhaochenguang@kylinos.cn>
>>
>> After fallback, treat the connection as not fully established so later
>> MP_JOIN attempts are rejected.
>>
>> Signed-off-by: Chenguang Zhao <zhaochenguang@kylinos.cn>
>> ---
>> net/mptcp/protocol.h | 5 ++++-
>> 1 file changed, 4 insertions(+), 1 deletion(-)
>>
>> diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h
>> index 7e168e450fb0..bf2483a7ed92 100644
>> --- a/net/mptcp/protocol.h
>> +++ b/net/mptcp/protocol.h
>> @@ -957,8 +957,11 @@ static inline void mptcp_start_tout_timer(struct sock *sk)
>>
>> static inline bool mptcp_is_fully_established(struct sock *sk)
>> {
>> + struct mptcp_sock *msk = mptcp_sk(sk);
>> +
>> return inet_sk_state_load(sk) == TCP_ESTABLISHED &&
>> - READ_ONCE(mptcp_sk(sk)->fully_established);
>> + READ_ONCE(msk->fully_established) &&
>> + !test_bit(MPTCP_FALLBACK_DONE, &msk->flags);
> Does the above improve actually anything? The test is inherently racy,
> as lack the fallback_lock, and AFAICS all critical paths have already
> explicit checks under such lock, see i.e. mptcp_finish_join().
>
> I would prefer avoiding additional conditionals, if not well reasoned.
>
> /P
>
Hi Paolo
You are right: the extra FALLBACK_DONE check in
mptcp_is_fully_established() does not actually improve anything.
It is read without fallback_lock, so it is racy. The paths that must
not complete a join after MP_FAIL already reject it under that lock
via allow_subflows, e.g. mptcp_finish_join() / __mptcp_finish_join().
I will drop this patch in the next revision and leave
mptcp_is_fully_established() unchanged.
Thanks,
Chenguang
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH export v3 3/4] mptcp: reset subflow on MP_FAIL when OoO queue is non-empty
2026-08-12 10:02 ` Paolo Abeni
@ 2026-08-13 8:45 ` Chenguang Zhao
0 siblings, 0 replies; 19+ messages in thread
From: Chenguang Zhao @ 2026-08-13 8:45 UTC (permalink / raw)
To: Paolo Abeni, mptcp; +Cc: Chenguang Zhao
在 2026/8/12 18:02, Paolo Abeni 写道:
> On 8/12/26 7:46 AM, Chenguang Zhao wrote:
>> From: Chenguang Zhao <zhaochenguang@kylinos.cn>
>>
>> Infinite mapping needs contiguous data. If the MPTCP OoO queue is not
>> empty, reset the subflow instead of falling back.
> Why? the RFC says:
>
> """if all unacknowledged data in flight is contiguous"""
>
> That statement can be true even when the RTX queue is not empty.
>
> It will become false if/when some mptcp-level retransmission will happen.
>
> __mptcp_push_retrans() atomically sets `allow_infinite_fallback` to
> false when such retransmission happen. The code just before the newly
> added one checks such condition under the relevant lock.
>
> I don't think this change is needed.
>
> /P
>
Hi Paolo
I misread the RFC here. "If all unacknowledged data in flight is
contiguous" is about send-side in-flight data, not about the MPTCP
receive OoO queue being empty. Contiguous in-flight data can still
hold when the RTX queue is not empty; it only becomes false after an
MPTCP-level retransmission.
That is already tracked by allow_infinite_fallback, which
__mptcp_push_retrans() clears under fallback_lock. The existing
check of that flag at the start of mptcp_pm_mp_fail_received()
covers the RFC condition.
I will drop this patch in the next revision.
Thanks,
Chenguang
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH export v3 4/4] mptcp: fallback to TCP on MP_FAIL with a single subflow
2026-08-12 10:43 ` Paolo Abeni
@ 2026-08-13 8:48 ` Chenguang Zhao
0 siblings, 0 replies; 19+ messages in thread
From: Chenguang Zhao @ 2026-08-13 8:48 UTC (permalink / raw)
To: Paolo Abeni, mptcp; +Cc: Chenguang Zhao
在 2026/8/12 18:43, Paolo Abeni 写道:
>
> On 8/12/26 7:46 AM, Chenguang Zhao wrote:
>> From: Chenguang Zhao <zhaochenguang@kylinos.cn>
>>
>> Fall back immediately via mptcp_try_fallback() after accepting MP_FAIL
>> on a single contiguous subflow, as required by RFC8684 §3.7.
>>
>> Fixes: 1e39e5a32ad7 ("mptcp: infinite mapping sending")
>> Signed-off-by: Chenguang Zhao <zhaochenguang@kylinos.cn>
>> ---
>> net/mptcp/pm.c | 9 ++++++---
>> net/mptcp/protocol.c | 4 +++-
>> 2 files changed, 9 insertions(+), 4 deletions(-)
>>
>> diff --git a/net/mptcp/pm.c b/net/mptcp/pm.c
>> index 8c263084db7b..cb85caf1df43 100644
>> --- a/net/mptcp/pm.c
>> +++ b/net/mptcp/pm.c
>> @@ -876,7 +876,6 @@ void mptcp_pm_mp_fail_received(struct sock *sk, u64 fail_seq)
>>
>> pr_debug("fail_seq=%llu\n", fail_seq);
>>
>> - /* After accepting the fail, we can't create any other subflows */
>> spin_lock_bh(&msk->fallback_lock);
>> if (!msk->allow_infinite_fallback) {
>> spin_unlock_bh(&msk->fallback_lock);
>> @@ -891,8 +890,6 @@ void mptcp_pm_mp_fail_received(struct sock *sk, u64 fail_seq)
>> mptcp_subflow_reset(sk);
>> return;
>> }
>> -
>> - msk->allow_subflows = false;
>> spin_unlock_bh(&msk->fallback_lock);
> At this point another subflow can complete the join, and set
> allow_infinite_fallback = false ...
Agreed. I should not have dropped the allow_subflows = false update
under fallback_lock. Join completion already checks that flag under
the same lock, so clearing it before unlocking is what prevents a
concurrent join from sneaking in.
I plan to keep that assignment, then send the MP_FAIL ACK and call
mptcp_try_fallback(). FALLBACK_DONE still has to come after
tcp_send_ack(), otherwise mptcp_established_options() would drop the
MPTCP options on that ACK.
>>
>> if (!subflow->fail_tout) {
>> @@ -901,6 +898,12 @@ void mptcp_pm_mp_fail_received(struct sock *sk, u64 fail_seq)
>> subflow->send_mp_fail = 1;
>> subflow->send_infinite_map = 1;
>> tcp_send_ack(sk);
> ... so the this mp_fail processing will be bogus [1].
>
>> +
>> + /* RFC8684 §3.7: fallback with a single subflow */
>> + if (!mptcp_try_fallback(sk, MPTCP_MIB_MPFAILFALLBACK)) {
>> + MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_FALLBACKFAILED);
>> + mptcp_subflow_reset(sk);
>> + }
>> } else {
>> pr_debug("MP_FAIL response received\n");
>> WRITE_ONCE(subflow->fail_tout, 0);
>> diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c
>> index f879b1061f2d..519e8d9c3164 100644
>> --- a/net/mptcp/protocol.c
>> +++ b/net/mptcp/protocol.c
>> @@ -1418,7 +1418,9 @@ static void mptcp_update_infinite_map(struct mptcp_sock *msk,
>> mpext->infinite_map = 1;
>> mpext->data_len = 0;
>>
>> - if (!mptcp_try_fallback(ssk, MPTCP_MIB_INFINITEMAPTX)) {
>> + if (__mptcp_check_fallback(msk)) {
>> + MPTCP_INC_STATS(sock_net(ssk), MPTCP_MIB_INFINITEMAPTX);
>> + } else if (!mptcp_try_fallback(ssk, MPTCP_MIB_INFINITEMAPTX)) {
>> MPTCP_INC_STATS(sock_net(ssk), MPTCP_MIB_FALLBACKFAILED);
>> mptcp_subflow_reset(ssk);
>> return;
> I don't understand this change. Can we ever enter the
>
> `if (!mptcp_try_fallback(ssk, MPTCP_MIB_INFINITEMAPTX)) {`
>
> branch? The msk already tried to fallback in
> mptcp_pm_mp_fail_received(). If the fallback was successful, the code
> will enter the `if (__mptcp_check_fallback(msk)) {` branch and not this one.
>
> Otherwise the fallback will fail again (AFAICS nothing resets
> `allow_infinite_fallback` once in become false).
>
> /P
No. After moving the fallback to mptcp_pm_mp_fail_received(), that
failure path in mptcp_update_infinite_map() is not reachable in the
normal case: either FALLBACK_DONE is already set, or the earlier
try_fallback() already failed and reset the subflow.
allow_infinite_fallback is not set back to true on that connection.
try_fallback(INFINITEMAPTX) also cannot be used just to account for
the map: __mptcp_try_fallback() returns true immediately when
fallback is already done and does not increment the MIB.
I would drop that second try_fallback(). Fallback would be counted as
MPFailFallback when MP_FAIL is accepted; InfiniteMapTx would be
incremented when the infinite mapping is actually transmitted later.
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH export v3 4/4] mptcp: fallback to TCP on MP_FAIL with a single subflow
2026-08-12 10:47 ` Paolo Abeni
@ 2026-08-13 8:50 ` Chenguang Zhao
0 siblings, 0 replies; 19+ messages in thread
From: Chenguang Zhao @ 2026-08-13 8:50 UTC (permalink / raw)
To: Paolo Abeni, mptcp; +Cc: Chenguang Zhao
在 2026/8/12 18:47, Paolo Abeni 写道:
> On 8/12/26 7:46 AM, Chenguang Zhao wrote:
>> @@ -901,6 +898,12 @@ void mptcp_pm_mp_fail_received(struct sock *sk, u64 fail_seq)
>> subflow->send_mp_fail = 1;
>> subflow->send_infinite_map = 1;
>> tcp_send_ack(sk);
> Slightly unrelated... AFAICS the current code sends the mp_fail reply
> immediatally, and will send the infinite mapping with the later data
> send, if any.
>
> I *think* it will be better to send MP_FAIL and infinite mapping
> together. It should remove a little bit of complexity on the xmit path
> and will be IMHO closer to the RFC specs.
>
> /P
>
To keep both MPTCP options on the same packet, that packet would have
to be sent before FALLBACK_DONE: once fallback is done,
mptcp_established_options() drops MPTCP options unless the skb
already carries an infinite mapping.
tcp_send_ack() builds a plain ACK with no mpext->infinite_map, so it
can include MP_FAIL but not the infinite mapping. The infinite
mapping is filled in later on a data skb in mptcp_sendmsg_frag().
With the current xmit path those two options therefore cannot go out
on the same packet. Sending them together would need a dedicated
transmit helper that builds a skb carrying both, rather than reusing
tcp_send_ack().
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH export v3 2/4] mptcp: reject joins after fallback in mptcp_is_fully_established
2026-08-12 15:32 ` Matthieu Baerts
@ 2026-08-13 9:32 ` gang.yan
2026-08-13 17:42 ` Matthieu Baerts
0 siblings, 1 reply; 19+ messages in thread
From: gang.yan @ 2026-08-13 9:32 UTC (permalink / raw)
To: Matthieu Baerts, Paolo Abeni, Chenguang Zhao, mptcp; +Cc: Chenguang Zhao
August 12, 2026 at 11:32 PM, "Matthieu Baerts" <matttbe@kernel.org mailto:matttbe@kernel.org?to=%22Matthieu%20Baerts%22%20%3Cmatttbe%40kernel.org%3E > wrote:
>
> Note: Chenguang is looking at a bug where, when an MP_FAIL is received
> before being in fully_established, the kernel accept going to fully
> established, and creating new subflows, then reject them once done.
>
> Same when receiving a 4th packet without MPTCP options.
>
> I think the MP_FAIL should do a fallback if possible, but it is probably
> also required to check the fallback status or something similar when
> receiving an MPJ.
Hi, Matt
Sorry, I'm not very familiar with the details of this patch.
No offense intended, I just have a question I'd like to ask based on my
understanding and the RFC documentation: Should MP_FAIL trigger a fallback?
For the multiple‑subflow case, if one subflow encounters an MP_FAIL, it should
be closed with a RST, and the data will be retransmitted over the other healthy
subflows. This should not cause the entire MPTCP connection to fallback to regular
TCP, right?
RFC8684's contents:
'''
The receiver of this option MUST discard all data following the data sequence number
specified. Failed data MUST NOT be DATA_ACKed and so will be retransmitted on other subflows.
'''
For the single‑subflow case, according to the RFC, MP_FAIL should subsequently
lead to a fallback under the infinite mapping (INFINITEMAP) situation, shouldn't it?
RFC 8684's contents:
'''
A special case is when there is a single subflow and it fails with a checksum error.
If it is known that all unacknowledged data in flight is contiguous (which will usually
be the case with a single subflow), an infinite mapping can be applied to the subflow
without the need to close it first, essentially turning off all further MPTCP signaling.
'''
Thanks
Gang
>
> >
> > I would prefer avoiding additional conditionals, if not well reasoned.
> >
> Agreed, the commit messages in this series are not explaining (enough)
> the reason and the context.
>
> BTW, Chenguang, please use 'mptcp-net' for fixes (and mptcp-next for
> features). Also fixes should have a Fixes tag.
>
> Cheers,
> Matt
> --
> Sponsored by the NGI0 Core fund.
>
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH export v3 2/4] mptcp: reject joins after fallback in mptcp_is_fully_established
2026-08-13 9:32 ` gang.yan
@ 2026-08-13 17:42 ` Matthieu Baerts
0 siblings, 0 replies; 19+ messages in thread
From: Matthieu Baerts @ 2026-08-13 17:42 UTC (permalink / raw)
To: gang.yan, Paolo Abeni, Chenguang Zhao, mptcp; +Cc: Chenguang Zhao
Hi Gang,
On 13/08/2026 11:32, gang.yan@linux.dev wrote:
> August 12, 2026 at 11:32 PM, "Matthieu Baerts" <matttbe@kernel.org mailto:matttbe@kernel.org?to=%22Matthieu%20Baerts%22%20%3Cmatttbe%40kernel.org%3E > wrote:
>
>
>>
>> Note: Chenguang is looking at a bug where, when an MP_FAIL is received
>> before being in fully_established, the kernel accept going to fully
>> established, and creating new subflows, then reject them once done.
>>
>> Same when receiving a 4th packet without MPTCP options.
>>
>> I think the MP_FAIL should do a fallback if possible, but it is probably
>> also required to check the fallback status or something similar when
>> receiving an MPJ.
>
> Hi, Matt
>
> Sorry, I'm not very familiar with the details of this patch.
>
> No offense intended, I just have a question I'd like to ask based on my
> understanding and the RFC documentation: Should MP_FAIL trigger a fallback?
>
> For the multiple‑subflow case, if one subflow encounters an MP_FAIL, it should
> be closed with a RST, and the data will be retransmitted over the other healthy
> subflows. This should not cause the entire MPTCP connection to fallback to regular
> TCP, right?
> RFC8684's contents:
> '''
> The receiver of this option MUST discard all data following the data sequence number
> specified. Failed data MUST NOT be DATA_ACKed and so will be retransmitted on other subflows.
> '''
Correct, in this case it shouldn't fallback.
> For the single‑subflow case, according to the RFC, MP_FAIL should subsequently
> lead to a fallback under the infinite mapping (INFINITEMAP) situation, shouldn't it?
> RFC 8684's contents:
> '''
> A special case is when there is a single subflow and it fails with a checksum error.
> If it is known that all unacknowledged data in flight is contiguous (which will usually
> be the case with a single subflow), an infinite mapping can be applied to the subflow
> without the need to close it first, essentially turning off all further MPTCP signaling.
> '''
Correct.
Please note that when I said "the MP_FAIL should [cause] a fallback if
possible", I'm referring to what is done in mptcp_try_fallback() → a
fallback is only possible in the single subflow situation, without
reordering at the MPTCP level.
In addition, in the case we are trying to fix here, the MP_FAIL is
received before switching to fully established: the RFC doesn't explain
this particular case, but it sounds like there is no need to send the
infinite mapping, and a fallback can be done directly *I think*. The
thing is this situation shouldn't happen: we are not supposed to receive
an MP_FAIL at that stage, no data has been exchanged at the MPTCP level,
so let's do the minimal -- i.e. just a fallback -- to avoid issues and
reduce the complexity, no?
Cheers,
Matt
--
Sponsored by the NGI0 Core fund.
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH export v3 2/4] mptcp: reject joins after fallback in mptcp_is_fully_established
2026-08-13 8:45 ` Chenguang Zhao
@ 2026-08-13 18:06 ` Matthieu Baerts
0 siblings, 0 replies; 19+ messages in thread
From: Matthieu Baerts @ 2026-08-13 18:06 UTC (permalink / raw)
To: Chenguang Zhao, Paolo Abeni, mptcp; +Cc: Chenguang Zhao
Hi Chenguang,
On 13/08/2026 10:45, Chenguang Zhao wrote:
>
> 在 2026/8/12 17:45, Paolo Abeni 写道:
>> On 8/12/26 7:46 AM, Chenguang Zhao wrote:
>>> From: Chenguang Zhao <zhaochenguang@kylinos.cn>
>>>
>>> After fallback, treat the connection as not fully established so later
>>> MP_JOIN attempts are rejected.
>>>
>>> Signed-off-by: Chenguang Zhao <zhaochenguang@kylinos.cn>
>>> ---
>>> net/mptcp/protocol.h | 5 ++++-
>>> 1 file changed, 4 insertions(+), 1 deletion(-)
>>>
>>> diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h
>>> index 7e168e450fb0..bf2483a7ed92 100644
>>> --- a/net/mptcp/protocol.h
>>> +++ b/net/mptcp/protocol.h
>>> @@ -957,8 +957,11 @@ static inline void mptcp_start_tout_timer(struct sock *sk)
>>>
>>> static inline bool mptcp_is_fully_established(struct sock *sk)
>>> {
>>> + struct mptcp_sock *msk = mptcp_sk(sk);
>>> +
>>> return inet_sk_state_load(sk) == TCP_ESTABLISHED &&
>>> - READ_ONCE(mptcp_sk(sk)->fully_established);
>>> + READ_ONCE(msk->fully_established) &&
>>> + !test_bit(MPTCP_FALLBACK_DONE, &msk->flags);
>> Does the above improve actually anything? The test is inherently racy,
>> as lack the fallback_lock, and AFAICS all critical paths have already
>> explicit checks under such lock, see i.e. mptcp_finish_join().
>>
>> I would prefer avoiding additional conditionals, if not well reasoned.
>>
>> /P
>>
> Hi Paolo
>
> You are right: the extra FALLBACK_DONE check in
> mptcp_is_fully_established() does not actually improve anything.
> It is read without fallback_lock, so it is racy. The paths that must
> not complete a join after MP_FAIL already reject it under that lock
> via allow_subflows, e.g. mptcp_finish_join() / __mptcp_finish_join().
> I will drop this patch in the next revision and leave
> mptcp_is_fully_established() unchanged.
How do you plan to deal with the case where a fallback has been done
while being in fully established mode?
Could we add WRITE_ONCE(msk->fully_established, false) in
__mptcp_try_fallback()? I guess it is acceptable because
__mptcp_finish_join will check allow_subflows under lock, no?
Or maybe we accept this behaviour?
Cheers,
Matt
--
Sponsored by the NGI0 Core fund.
^ permalink raw reply [flat|nested] 19+ messages in thread
end of thread, other threads:[~2026-08-13 18:06 UTC | newest]
Thread overview: 19+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-12 5:46 [PATCH export v3 0/4] mptcp: fix TCP fallback on single-subflow MP_FAIL Chenguang Zhao
2026-08-12 5:46 ` [PATCH export v3 1/4] mptcp: add MPFailFallback MIB Chenguang Zhao
2026-08-12 5:46 ` [PATCH export v3 2/4] mptcp: reject joins after fallback in mptcp_is_fully_established Chenguang Zhao
2026-08-12 9:45 ` Paolo Abeni
2026-08-12 15:32 ` Matthieu Baerts
2026-08-13 9:32 ` gang.yan
2026-08-13 17:42 ` Matthieu Baerts
2026-08-13 8:45 ` Chenguang Zhao
2026-08-13 18:06 ` Matthieu Baerts
2026-08-12 5:46 ` [PATCH export v3 3/4] mptcp: reset subflow on MP_FAIL when OoO queue is non-empty Chenguang Zhao
2026-08-12 6:07 ` sashiko-bot
2026-08-12 10:02 ` Paolo Abeni
2026-08-13 8:45 ` Chenguang Zhao
2026-08-12 5:46 ` [PATCH export v3 4/4] mptcp: fallback to TCP on MP_FAIL with a single subflow Chenguang Zhao
2026-08-12 10:43 ` Paolo Abeni
2026-08-13 8:48 ` Chenguang Zhao
2026-08-12 10:47 ` Paolo Abeni
2026-08-13 8:50 ` Chenguang Zhao
2026-08-12 6:57 ` [PATCH export v3 0/4] mptcp: fix TCP fallback on single-subflow MP_FAIL MPTCP CI
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.