* [PATCH mptcp-next] mptcp: fallback to TCP on MP_FAIL with a single subflow
@ 2026-07-13 6:41 Chenguang Zhao
2026-07-13 7:32 ` gang.yan
0 siblings, 1 reply; 2+ messages in thread
From: Chenguang Zhao @ 2026-07-13 6:41 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 §3.7 requires leaving MPTCP mode.
The stack only cleared allow_subflows and deferred the real fallback to
the later infinite-map transmit path. Before any data is sent, a peer
could still complete the 4th ACK as MPTCP and keep using MPTCP options.
Fall back immediately after sending the MP_FAIL response, teach
mptcp_is_fully_established() to reject joins after fallback or when
subflows are disallowed, and annotate allow_subflows with READ/WRITE_ONCE.
Fixes: 1e39e5a32ad7 ("mptcp: infinite mapping sending")
Signed-off-by: Chenguang Zhao <zhaochenguang@kylinos.cn>
---
- On a valid MP_FAIL while infinite fallback is still allowed (single
subflow, contiguous data), comply with RFC 8684 §3.7 by calling
mptcp_try_fallback() immediately after the MP_FAIL response, instead
of deferring fallback until the infinite-map transmit path runs.
- Send the MP_FAIL ACK before setting FALLBACK_DONE, so MPTCP options
are not dropped from that ACK by mptcp_established_options().
- If the out-of-order queue is non-empty, fallback cannot proceed safely:
increment the FallbackFailed MIB and reset the subflow, rather than leaving
allow_subflows=false without completing fallback.
- Add the MPTcpExtMPFailFallback MIB to count fallbacks triggered by MP_FAIL.
- In mptcp_update_infinite_map(), if fallback was already completed on MP_FAIL
reception, only account for InfiniteMapTx and do not attempt fallback again.
- Extend mptcp_is_fully_established() to require that the socket is not in
fallback and that allow_subflows is true, so new subflow joins are rejected
after fallback.
- Use READ_ONCE/WRITE_ONCE for all allow_subflows accesses to avoid concurrent read/write issues.
- Fix the fallback_lock comment: allow_join → allow_subflows.
net/mptcp/mib.c | 1 +
net/mptcp/mib.h | 1 +
net/mptcp/pm.c | 33 +++++++++++++++++++++++++++++++--
net/mptcp/protocol.c | 19 ++++++++++++-------
net/mptcp/protocol.h | 18 +++++++++++-------
net/mptcp/subflow.c | 2 +-
6 files changed, 57 insertions(+), 17 deletions(-)
diff --git a/net/mptcp/mib.c b/net/mptcp/mib.c
index f23fda0c55a7..4dc107ab1aec 100644
--- a/net/mptcp/mib.c
+++ b/net/mptcp/mib.c
@@ -83,6 +83,7 @@ static const struct snmp_mib mptcp_snmp_list[] = {
SNMP_MIB_ITEM("MD5SigFallback", MPTCP_MIB_MD5SIGFALLBACK),
SNMP_MIB_ITEM("DssFallback", MPTCP_MIB_DSSFALLBACK),
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),
};
diff --git a/net/mptcp/mib.h b/net/mptcp/mib.h
index 812218b5ed2b..9db8812d8e04 100644
--- a/net/mptcp/mib.h
+++ b/net/mptcp/mib.h
@@ -86,6 +86,7 @@ enum linux_mptcp_mib_field {
MPTCP_MIB_MD5SIGFALLBACK, /* Conflicting TCP option enabled */
MPTCP_MIB_DSSFALLBACK, /* Bad or missing DSS */
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_MAX
diff --git a/net/mptcp/pm.c b/net/mptcp/pm.c
index 6afd39aea110..82d3dbedd603 100644
--- a/net/mptcp/pm.c
+++ b/net/mptcp/pm.c
@@ -870,21 +870,50 @@ 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 */
+ /* MP_FAIL on a single contiguous subflow: fall back to TCP.
+ * allow_infinite_fallback is cleared once other subflows join or
+ * non-contiguous data is retransmitted; in that case ignore MP_FAIL
+ * here (the peer should reset the failing subflow instead).
+ *
+ * Send the MP_FAIL (+ DSS) response before setting FALLBACK_DONE,
+ * otherwise mptcp_established_options() would drop all MPTCP options
+ * on this ACK. InfiniteMapTx is accounted later when the map is sent.
+ */
spin_lock_bh(&msk->fallback_lock);
if (!msk->allow_infinite_fallback) {
spin_unlock_bh(&msk->fallback_lock);
return;
}
- msk->allow_subflows = false;
+ WRITE_ONCE(msk->allow_subflows, false);
spin_unlock_bh(&msk->fallback_lock);
if (!subflow->fail_tout) {
pr_debug("send MP_FAIL response and infinite map\n");
+ /* Infinite mapping requires contiguous data. With OoO still
+ * queued, do not leave allow_subflows=false without
+ * FALLBACK_DONE; tear the subflow down instead (RFC8684 §3.7).
+ */
+ if (!RB_EMPTY_ROOT(&msk->out_of_order_queue)) {
+ MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_FALLBACKFAILED);
+ subflow->send_mp_fail = 1;
+ mptcp_subflow_reset(sk);
+ return;
+ }
+
subflow->send_mp_fail = 1;
subflow->send_infinite_map = 1;
tcp_send_ack(sk);
+
+ /* RFC8684 §3.7: after accepting MP_FAIL with a single
+ * subflow, leave MPTCP mode and never revert. Use
+ * mptcp_try_fallback() so pending DATA_FIN is handled.
+ * InfiniteMapTx is counted when the map is transmitted.
+ */
+ 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 b32f0cd262a7..181403f43e54 100644
--- a/net/mptcp/protocol.c
+++ b/net/mptcp/protocol.c
@@ -93,7 +93,7 @@ bool __mptcp_try_fallback(struct mptcp_sock *msk, int fb_mib)
return false;
}
- msk->allow_subflows = false;
+ WRITE_ONCE(msk->allow_subflows, false);
set_bit(MPTCP_FALLBACK_DONE, &msk->flags);
__MPTCP_INC_STATS(net, fb_mib);
spin_unlock_bh(&msk->fallback_lock);
@@ -958,7 +958,7 @@ static bool __mptcp_finish_join(struct mptcp_sock *msk, struct sock *ssk)
return false;
spin_lock_bh(&msk->fallback_lock);
- if (!msk->allow_subflows) {
+ if (!READ_ONCE(msk->allow_subflows)) {
spin_unlock_bh(&msk->fallback_lock);
return false;
}
@@ -1299,7 +1299,12 @@ 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)) {
+ /* Fallback may already have been completed on MP_FAIL reception;
+ * still account for the infinite mapping being transmitted.
+ */
+ 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;
@@ -2839,7 +2844,7 @@ static void __mptcp_retrans(struct sock *sk)
*/
spin_lock_bh(&msk->fallback_lock);
if (__mptcp_check_fallback(msk) ||
- !msk->allow_subflows) {
+ !READ_ONCE(msk->allow_subflows)) {
spin_unlock_bh(&msk->fallback_lock);
release_sock(ssk);
goto clear_scheduled;
@@ -3050,7 +3055,7 @@ static void __mptcp_init_sock(struct sock *sk)
inet_csk(sk)->icsk_sync_mss = mptcp_sync_mss;
WRITE_ONCE(msk->csum_enabled, mptcp_is_checksum_enabled(sock_net(sk)));
msk->allow_infinite_fallback = true;
- msk->allow_subflows = true;
+ WRITE_ONCE(msk->allow_subflows, true);
msk->recovery = false;
msk->subflow_id = 1;
msk->last_data_sent = tcp_jiffies32;
@@ -3468,7 +3473,7 @@ static int mptcp_disconnect(struct sock *sk, int flags)
* can't overlap with a fallback anymore
*/
spin_lock_bh(&msk->fallback_lock);
- msk->allow_subflows = true;
+ WRITE_ONCE(msk->allow_subflows, true);
msk->allow_infinite_fallback = true;
WRITE_ONCE(msk->flags, 0);
spin_unlock_bh(&msk->fallback_lock);
@@ -3868,7 +3873,7 @@ bool mptcp_finish_join(struct sock *ssk)
*/
if (!list_empty(&subflow->node)) {
spin_lock_bh(&msk->fallback_lock);
- if (!msk->allow_subflows) {
+ if (!READ_ONCE(msk->allow_subflows)) {
spin_unlock_bh(&msk->fallback_lock);
return false;
}
diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h
index 4a2d40cd7b13..833c76c41768 100644
--- a/net/mptcp/protocol.h
+++ b/net/mptcp/protocol.h
@@ -369,7 +369,7 @@ struct mptcp_sock {
spinlock_t fallback_lock; /* protects fallback,
* allow_infinite_fallback and
- * allow_join
+ * allow_subflows
*/
struct list_head backlog_list; /* protected by the data lock */
@@ -947,12 +947,6 @@ static inline void mptcp_start_tout_timer(struct sock *sk)
mptcp_reset_tout_timer(mptcp_sk(sk), 0);
}
-static inline bool mptcp_is_fully_established(struct sock *sk)
-{
- return inet_sk_state_load(sk) == TCP_ESTABLISHED &&
- READ_ONCE(mptcp_sk(sk)->fully_established);
-}
-
static inline u64 mptcp_stamp(void)
{
return div_u64(tcp_clock_ns(), NSEC_PER_USEC);
@@ -1290,6 +1284,16 @@ static inline bool mptcp_check_fallback(const struct sock *sk)
return __mptcp_check_fallback(msk);
}
+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(msk->fully_established) &&
+ !__mptcp_check_fallback(msk) &&
+ READ_ONCE(msk->allow_subflows);
+}
+
static inline bool __mptcp_has_initial_subflow(const struct mptcp_sock *msk)
{
struct sock *ssk = READ_ONCE(msk->first);
diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c
index 8e386899ceb9..b04d6edb9be3 100644
--- a/net/mptcp/subflow.c
+++ b/net/mptcp/subflow.c
@@ -1323,7 +1323,7 @@ static bool mptcp_subflow_fail(struct mptcp_sock *msk, struct sock *ssk)
spin_unlock_bh(&msk->fallback_lock);
return false;
}
- msk->allow_subflows = false;
+ WRITE_ONCE(msk->allow_subflows, false);
spin_unlock_bh(&msk->fallback_lock);
/* graceful failure can happen only on the MPC subflow */
--
2.25.1
^ permalink raw reply related [flat|nested] 2+ messages in thread* Re: [PATCH mptcp-next] mptcp: fallback to TCP on MP_FAIL with a single subflow
2026-07-13 6:41 [PATCH mptcp-next] mptcp: fallback to TCP on MP_FAIL with a single subflow Chenguang Zhao
@ 2026-07-13 7:32 ` gang.yan
0 siblings, 0 replies; 2+ messages in thread
From: gang.yan @ 2026-07-13 7:32 UTC (permalink / raw)
To: Chenguang Zhao, mptcp; +Cc: chenguang.zhao, Chenguang Zhao
July 13, 2026 at 2:41 PM, "Chenguang Zhao" <chenguang.zhao@linux.dev mailto:chenguang.zhao@linux.dev?to=%22Chenguang%20Zhao%22%20%3Cchenguang.zhao%40linux.dev%3E > wrote:
Hi chenguang,
This patch is rather large and would benefit from being split into a few smaller
patches for easier review. For instance:
Patch 1: fix the kernel issues you mentioned.
Patch 2: use WRITE_ONCE/READ_ONCE in allow_subflows.
Patch 3: add a new MIB counter.
@Matt, WDYT?
But I'm not sure we need the new MIB counter just yet; it may be too early.
I would suggest adding the MIB counters only after the fix commit has been
merged. However, the final decision rests with you.
Thanks
Gang
>
> From: Chenguang Zhao <zhaochenguang@kylinos.cn>
>
> When a valid MP_FAIL is received and infinite fallback is still allowed
> (single contiguous subflow), RFC8684 §3.7 requires leaving MPTCP mode.
> The stack only cleared allow_subflows and deferred the real fallback to
> the later infinite-map transmit path. Before any data is sent, a peer
> could still complete the 4th ACK as MPTCP and keep using MPTCP options.
>
> Fall back immediately after sending the MP_FAIL response, teach
> mptcp_is_fully_established() to reject joins after fallback or when
> subflows are disallowed, and annotate allow_subflows with READ/WRITE_ONCE.
>
> Fixes: 1e39e5a32ad7 ("mptcp: infinite mapping sending")
> Signed-off-by: Chenguang Zhao <zhaochenguang@kylinos.cn>
> ---
> - On a valid MP_FAIL while infinite fallback is still allowed (single
> subflow, contiguous data), comply with RFC 8684 §3.7 by calling
> mptcp_try_fallback() immediately after the MP_FAIL response, instead
> of deferring fallback until the infinite-map transmit path runs.
>
> - Send the MP_FAIL ACK before setting FALLBACK_DONE, so MPTCP options
> are not dropped from that ACK by mptcp_established_options().
>
> - If the out-of-order queue is non-empty, fallback cannot proceed safely:
> increment the FallbackFailed MIB and reset the subflow, rather than leaving
> allow_subflows=false without completing fallback.
>
> - Add the MPTcpExtMPFailFallback MIB to count fallbacks triggered by MP_FAIL.
>
> - In mptcp_update_infinite_map(), if fallback was already completed on MP_FAIL
> reception, only account for InfiniteMapTx and do not attempt fallback again.
>
> - Extend mptcp_is_fully_established() to require that the socket is not in
> fallback and that allow_subflows is true, so new subflow joins are rejected
> after fallback.
>
> - Use READ_ONCE/WRITE_ONCE for all allow_subflows accesses to avoid concurrent read/write issues.
>
> - Fix the fallback_lock comment: allow_join → allow_subflows.
>
> net/mptcp/mib.c | 1 +
> net/mptcp/mib.h | 1 +
> net/mptcp/pm.c | 33 +++++++++++++++++++++++++++++++--
> net/mptcp/protocol.c | 19 ++++++++++++-------
> net/mptcp/protocol.h | 18 +++++++++++-------
> net/mptcp/subflow.c | 2 +-
> 6 files changed, 57 insertions(+), 17 deletions(-)
>
> diff --git a/net/mptcp/mib.c b/net/mptcp/mib.c
> index f23fda0c55a7..4dc107ab1aec 100644
> --- a/net/mptcp/mib.c
> +++ b/net/mptcp/mib.c
> @@ -83,6 +83,7 @@ static const struct snmp_mib mptcp_snmp_list[] = {
> SNMP_MIB_ITEM("MD5SigFallback", MPTCP_MIB_MD5SIGFALLBACK),
> SNMP_MIB_ITEM("DssFallback", MPTCP_MIB_DSSFALLBACK),
> 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),
> };
> diff --git a/net/mptcp/mib.h b/net/mptcp/mib.h
> index 812218b5ed2b..9db8812d8e04 100644
> --- a/net/mptcp/mib.h
> +++ b/net/mptcp/mib.h
> @@ -86,6 +86,7 @@ enum linux_mptcp_mib_field {
> MPTCP_MIB_MD5SIGFALLBACK, /* Conflicting TCP option enabled */
> MPTCP_MIB_DSSFALLBACK, /* Bad or missing DSS */
> 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_MAX
> diff --git a/net/mptcp/pm.c b/net/mptcp/pm.c
> index 6afd39aea110..82d3dbedd603 100644
> --- a/net/mptcp/pm.c
> +++ b/net/mptcp/pm.c
> @@ -870,21 +870,50 @@ 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 */
> + /* MP_FAIL on a single contiguous subflow: fall back to TCP.
> + * allow_infinite_fallback is cleared once other subflows join or
> + * non-contiguous data is retransmitted; in that case ignore MP_FAIL
> + * here (the peer should reset the failing subflow instead).
> + *
> + * Send the MP_FAIL (+ DSS) response before setting FALLBACK_DONE,
> + * otherwise mptcp_established_options() would drop all MPTCP options
> + * on this ACK. InfiniteMapTx is accounted later when the map is sent.
> + */
> spin_lock_bh(&msk->fallback_lock);
> if (!msk->allow_infinite_fallback) {
> spin_unlock_bh(&msk->fallback_lock);
> return;
> }
> - msk->allow_subflows = false;
> + WRITE_ONCE(msk->allow_subflows, false);
> spin_unlock_bh(&msk->fallback_lock);
>
> if (!subflow->fail_tout) {
> pr_debug("send MP_FAIL response and infinite map\n");
>
> + /* Infinite mapping requires contiguous data. With OoO still
> + * queued, do not leave allow_subflows=false without
> + * FALLBACK_DONE; tear the subflow down instead (RFC8684 §3.7).
> + */
> + if (!RB_EMPTY_ROOT(&msk->out_of_order_queue)) {
> + MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_FALLBACKFAILED);
> + subflow->send_mp_fail = 1;
> + mptcp_subflow_reset(sk);
> + return;
> + }
> +
> subflow->send_mp_fail = 1;
> subflow->send_infinite_map = 1;
> tcp_send_ack(sk);
> +
> + /* RFC8684 §3.7: after accepting MP_FAIL with a single
> + * subflow, leave MPTCP mode and never revert. Use
> + * mptcp_try_fallback() so pending DATA_FIN is handled.
> + * InfiniteMapTx is counted when the map is transmitted.
> + */
> + 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 b32f0cd262a7..181403f43e54 100644
> --- a/net/mptcp/protocol.c
> +++ b/net/mptcp/protocol.c
> @@ -93,7 +93,7 @@ bool __mptcp_try_fallback(struct mptcp_sock *msk, int fb_mib)
> return false;
> }
>
> - msk->allow_subflows = false;
> + WRITE_ONCE(msk->allow_subflows, false);
> set_bit(MPTCP_FALLBACK_DONE, &msk->flags);
> __MPTCP_INC_STATS(net, fb_mib);
> spin_unlock_bh(&msk->fallback_lock);
> @@ -958,7 +958,7 @@ static bool __mptcp_finish_join(struct mptcp_sock *msk, struct sock *ssk)
> return false;
>
> spin_lock_bh(&msk->fallback_lock);
> - if (!msk->allow_subflows) {
> + if (!READ_ONCE(msk->allow_subflows)) {
> spin_unlock_bh(&msk->fallback_lock);
> return false;
> }
> @@ -1299,7 +1299,12 @@ 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)) {
> + /* Fallback may already have been completed on MP_FAIL reception;
> + * still account for the infinite mapping being transmitted.
> + */
> + 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;
> @@ -2839,7 +2844,7 @@ static void __mptcp_retrans(struct sock *sk)
> */
> spin_lock_bh(&msk->fallback_lock);
> if (__mptcp_check_fallback(msk) ||
> - !msk->allow_subflows) {
> + !READ_ONCE(msk->allow_subflows)) {
> spin_unlock_bh(&msk->fallback_lock);
> release_sock(ssk);
> goto clear_scheduled;
> @@ -3050,7 +3055,7 @@ static void __mptcp_init_sock(struct sock *sk)
> inet_csk(sk)->icsk_sync_mss = mptcp_sync_mss;
> WRITE_ONCE(msk->csum_enabled, mptcp_is_checksum_enabled(sock_net(sk)));
> msk->allow_infinite_fallback = true;
> - msk->allow_subflows = true;
> + WRITE_ONCE(msk->allow_subflows, true);
> msk->recovery = false;
> msk->subflow_id = 1;
> msk->last_data_sent = tcp_jiffies32;
> @@ -3468,7 +3473,7 @@ static int mptcp_disconnect(struct sock *sk, int flags)
> * can't overlap with a fallback anymore
> */
> spin_lock_bh(&msk->fallback_lock);
> - msk->allow_subflows = true;
> + WRITE_ONCE(msk->allow_subflows, true);
> msk->allow_infinite_fallback = true;
> WRITE_ONCE(msk->flags, 0);
> spin_unlock_bh(&msk->fallback_lock);
> @@ -3868,7 +3873,7 @@ bool mptcp_finish_join(struct sock *ssk)
> */
> if (!list_empty(&subflow->node)) {
> spin_lock_bh(&msk->fallback_lock);
> - if (!msk->allow_subflows) {
> + if (!READ_ONCE(msk->allow_subflows)) {
> spin_unlock_bh(&msk->fallback_lock);
> return false;
> }
> diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h
> index 4a2d40cd7b13..833c76c41768 100644
> --- a/net/mptcp/protocol.h
> +++ b/net/mptcp/protocol.h
> @@ -369,7 +369,7 @@ struct mptcp_sock {
>
> spinlock_t fallback_lock; /* protects fallback,
> * allow_infinite_fallback and
> - * allow_join
> + * allow_subflows
> */
>
> struct list_head backlog_list; /* protected by the data lock */
> @@ -947,12 +947,6 @@ static inline void mptcp_start_tout_timer(struct sock *sk)
> mptcp_reset_tout_timer(mptcp_sk(sk), 0);
> }
>
> -static inline bool mptcp_is_fully_established(struct sock *sk)
> -{
> - return inet_sk_state_load(sk) == TCP_ESTABLISHED &&
> - READ_ONCE(mptcp_sk(sk)->fully_established);
> -}
> -
> static inline u64 mptcp_stamp(void)
> {
> return div_u64(tcp_clock_ns(), NSEC_PER_USEC);
> @@ -1290,6 +1284,16 @@ static inline bool mptcp_check_fallback(const struct sock *sk)
> return __mptcp_check_fallback(msk);
> }
>
> +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(msk->fully_established) &&
> + !__mptcp_check_fallback(msk) &&
> + READ_ONCE(msk->allow_subflows);
> +}
> +
> static inline bool __mptcp_has_initial_subflow(const struct mptcp_sock *msk)
> {
> struct sock *ssk = READ_ONCE(msk->first);
> diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c
> index 8e386899ceb9..b04d6edb9be3 100644
> --- a/net/mptcp/subflow.c
> +++ b/net/mptcp/subflow.c
> @@ -1323,7 +1323,7 @@ static bool mptcp_subflow_fail(struct mptcp_sock *msk, struct sock *ssk)
> spin_unlock_bh(&msk->fallback_lock);
> return false;
> }
> - msk->allow_subflows = false;
> + WRITE_ONCE(msk->allow_subflows, false);
> spin_unlock_bh(&msk->fallback_lock);
>
> /* graceful failure can happen only on the MPC subflow */
> --
> 2.25.1
>
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-07-13 7:32 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-13 6:41 [PATCH mptcp-next] mptcp: fallback to TCP on MP_FAIL with a single subflow Chenguang Zhao
2026-07-13 7:32 ` gang.yan
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.