diff options
Diffstat (limited to 'net/mptcp/protocol.c')
| -rw-r--r-- | net/mptcp/protocol.c | 56 | 
1 files changed, 48 insertions, 8 deletions
diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c index edf14c2c2062..6a817a13b154 100644 --- a/net/mptcp/protocol.c +++ b/net/mptcp/protocol.c @@ -560,10 +560,9 @@ static bool mptcp_check_data_fin(struct sock *sk)  static void mptcp_dss_corruption(struct mptcp_sock *msk, struct sock *ssk)  { -	if (READ_ONCE(msk->allow_infinite_fallback)) { +	if (mptcp_try_fallback(ssk)) {  		MPTCP_INC_STATS(sock_net(ssk),  				MPTCP_MIB_DSSCORRUPTIONFALLBACK); -		mptcp_do_fallback(ssk);  	} else {  		MPTCP_INC_STATS(sock_net(ssk), MPTCP_MIB_DSSCORRUPTIONRESET);  		mptcp_subflow_reset(ssk); @@ -792,7 +791,7 @@ void mptcp_data_ready(struct sock *sk, struct sock *ssk)  static void mptcp_subflow_joined(struct mptcp_sock *msk, struct sock *ssk)  {  	mptcp_subflow_ctx(ssk)->map_seq = READ_ONCE(msk->ack_seq); -	WRITE_ONCE(msk->allow_infinite_fallback, false); +	msk->allow_infinite_fallback = false;  	mptcp_event(MPTCP_EVENT_SUB_ESTABLISHED, msk, ssk, GFP_ATOMIC);  } @@ -803,6 +802,14 @@ static bool __mptcp_finish_join(struct mptcp_sock *msk, struct sock *ssk)  	if (sk->sk_state != TCP_ESTABLISHED)  		return false; +	spin_lock_bh(&msk->fallback_lock); +	if (!msk->allow_subflows) { +		spin_unlock_bh(&msk->fallback_lock); +		return false; +	} +	mptcp_subflow_joined(msk, ssk); +	spin_unlock_bh(&msk->fallback_lock); +  	/* attach to msk socket only after we are sure we will deal with it  	 * at close time  	 */ @@ -811,7 +818,6 @@ static bool __mptcp_finish_join(struct mptcp_sock *msk, struct sock *ssk)  	mptcp_subflow_ctx(ssk)->subflow_id = msk->subflow_id++;  	mptcp_sockopt_sync_locked(msk, ssk); -	mptcp_subflow_joined(msk, ssk);  	mptcp_stop_tout_timer(sk);  	__mptcp_propagate_sndbuf(sk, ssk);  	return true; @@ -1136,10 +1142,14 @@ static void mptcp_update_infinite_map(struct mptcp_sock *msk,  	mpext->infinite_map = 1;  	mpext->data_len = 0; +	if (!mptcp_try_fallback(ssk)) { +		mptcp_subflow_reset(ssk); +		return; +	} +  	MPTCP_INC_STATS(sock_net(ssk), MPTCP_MIB_INFINITEMAPTX);  	mptcp_subflow_ctx(ssk)->send_infinite_map = 0;  	pr_fallback(msk); -	mptcp_do_fallback(ssk);  }  #define MPTCP_MAX_GSO_SIZE (GSO_LEGACY_MAX_SIZE - (MAX_TCP_HEADER + 1)) @@ -2543,9 +2553,9 @@ static void mptcp_check_fastclose(struct mptcp_sock *msk)  static void __mptcp_retrans(struct sock *sk)  { +	struct mptcp_sendmsg_info info = { .data_lock_held = true, };  	struct mptcp_sock *msk = mptcp_sk(sk);  	struct mptcp_subflow_context *subflow; -	struct mptcp_sendmsg_info info = {};  	struct mptcp_data_frag *dfrag;  	struct sock *ssk;  	int ret, err; @@ -2590,6 +2600,18 @@ static void __mptcp_retrans(struct sock *sk)  			info.sent = 0;  			info.limit = READ_ONCE(msk->csum_enabled) ? dfrag->data_len :  								    dfrag->already_sent; + +			/* +			 * make the whole retrans decision, xmit, disallow +			 * fallback atomic +			 */ +			spin_lock_bh(&msk->fallback_lock); +			if (__mptcp_check_fallback(msk)) { +				spin_unlock_bh(&msk->fallback_lock); +				release_sock(ssk); +				return; +			} +  			while (info.sent < info.limit) {  				ret = mptcp_sendmsg_frag(sk, ssk, dfrag, &info);  				if (ret <= 0) @@ -2603,8 +2625,9 @@ static void __mptcp_retrans(struct sock *sk)  				len = max(copied, len);  				tcp_push(ssk, 0, info.mss_now, tcp_sk(ssk)->nonagle,  					 info.size_goal); -				WRITE_ONCE(msk->allow_infinite_fallback, false); +				msk->allow_infinite_fallback = false;  			} +			spin_unlock_bh(&msk->fallback_lock);  			release_sock(ssk);  		} @@ -2730,7 +2753,8 @@ static void __mptcp_init_sock(struct sock *sk)  	WRITE_ONCE(msk->first, NULL);  	inet_csk(sk)->icsk_sync_mss = mptcp_sync_mss;  	WRITE_ONCE(msk->csum_enabled, mptcp_is_checksum_enabled(sock_net(sk))); -	WRITE_ONCE(msk->allow_infinite_fallback, true); +	msk->allow_infinite_fallback = true; +	msk->allow_subflows = true;  	msk->recovery = false;  	msk->subflow_id = 1;  	msk->last_data_sent = tcp_jiffies32; @@ -2738,6 +2762,7 @@ static void __mptcp_init_sock(struct sock *sk)  	msk->last_ack_recv = tcp_jiffies32;  	mptcp_pm_data_init(msk); +	spin_lock_init(&msk->fallback_lock);  	/* re-use the csk retrans timer for MPTCP-level retrans */  	timer_setup(&msk->sk.icsk_retransmit_timer, mptcp_retransmit_timer, 0); @@ -3117,7 +3142,16 @@ static int mptcp_disconnect(struct sock *sk, int flags)  	 * subflow  	 */  	mptcp_destroy_common(msk, MPTCP_CF_FASTCLOSE); + +	/* The first subflow is already in TCP_CLOSE status, the following +	 * can't overlap with a fallback anymore +	 */ +	spin_lock_bh(&msk->fallback_lock); +	msk->allow_subflows = true; +	msk->allow_infinite_fallback = true;  	WRITE_ONCE(msk->flags, 0); +	spin_unlock_bh(&msk->fallback_lock); +  	msk->cb_flags = 0;  	msk->recovery = false;  	WRITE_ONCE(msk->can_ack, false); @@ -3524,7 +3558,13 @@ bool mptcp_finish_join(struct sock *ssk)  	/* active subflow, already present inside the conn_list */  	if (!list_empty(&subflow->node)) { +		spin_lock_bh(&msk->fallback_lock); +		if (!msk->allow_subflows) { +			spin_unlock_bh(&msk->fallback_lock); +			return false; +		}  		mptcp_subflow_joined(msk, ssk); +		spin_unlock_bh(&msk->fallback_lock);  		mptcp_propagate_sndbuf(parent, ssk);  		return true;  	}  | 
