Re: [PATCH export v3 4/4] mptcp: fallback to TCP on MP_FAIL with a single subflow

Chenguang Zhao <[email protected]>
Newsgroups dev.linux.lists.mptcp
Message-ID <[email protected]>
在 2026/8/12 18:43, Paolo Abeni 写道:
>
> On 8/12/26 7:46 AM, Chenguang Zhao wrote:
>> From: Chenguang Zhao <[email protected]>
>>
>> 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 <[email protected]>
>> ---
>>  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.
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.