All of lore.kernel.org
 help / color / mirror / Atom feed
* [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; 12+ 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] 12+ 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; 12+ 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] 12+ 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; 12+ 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] 12+ 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; 12+ 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] 12+ 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; 12+ 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] 12+ 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; 12+ 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] 12+ 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; 12+ 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] 12+ 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
  0 siblings, 1 reply; 12+ 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] 12+ 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; 12+ 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] 12+ 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
  1 sibling, 0 replies; 12+ 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] 12+ 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
  1 sibling, 0 replies; 12+ 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] 12+ 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
  0 siblings, 0 replies; 12+ 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] 12+ messages in thread

end of thread, other threads:[~2026-08-12 15:32 UTC | newest]

Thread overview: 12+ 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-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-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-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.