forked from mirrors/linux
		
	mptcp: introduce MPTCP retransmission timer
The timer will be used to schedule retransmission. It's frequency is based on the current subflow RTO estimation and is reset on every una_seq update The timer is clearer for good by __mptcp_clear_xmit() Also clean MPTCP rtx queue before each transmission. Signed-off-by: Paolo Abeni <pabeni@redhat.com> Signed-off-by: Mat Martineau <mathew.j.martineau@linux.intel.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
		
							parent
							
								
									18b683bff8
								
							
						
					
					
						commit
						b51f9b80c0
					
				
					 3 changed files with 93 additions and 2 deletions
				
			
		|  | @ -779,8 +779,10 @@ static void update_una(struct mptcp_sock *msk, | ||||||
| 		snd_una = old_snd_una; | 		snd_una = old_snd_una; | ||||||
| 		old_snd_una = atomic64_cmpxchg(&msk->snd_una, snd_una, | 		old_snd_una = atomic64_cmpxchg(&msk->snd_una, snd_una, | ||||||
| 					       new_snd_una); | 					       new_snd_una); | ||||||
| 		if (old_snd_una == snd_una) | 		if (old_snd_una == snd_una) { | ||||||
|  | 			mptcp_data_acked((struct sock *)msk); | ||||||
| 			break; | 			break; | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -251,6 +251,46 @@ static void __mptcp_flush_join_list(struct mptcp_sock *msk) | ||||||
| 	spin_unlock_bh(&msk->join_list_lock); | 	spin_unlock_bh(&msk->join_list_lock); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static void mptcp_set_timeout(const struct sock *sk, const struct sock *ssk) | ||||||
|  | { | ||||||
|  | 	long tout = ssk && inet_csk(ssk)->icsk_pending ? | ||||||
|  | 				      inet_csk(ssk)->icsk_timeout - jiffies : 0; | ||||||
|  | 
 | ||||||
|  | 	if (tout <= 0) | ||||||
|  | 		tout = mptcp_sk(sk)->timer_ival; | ||||||
|  | 	mptcp_sk(sk)->timer_ival = tout > 0 ? tout : TCP_RTO_MIN; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool mptcp_timer_pending(struct sock *sk) | ||||||
|  | { | ||||||
|  | 	return timer_pending(&inet_csk(sk)->icsk_retransmit_timer); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void mptcp_reset_timer(struct sock *sk) | ||||||
|  | { | ||||||
|  | 	struct inet_connection_sock *icsk = inet_csk(sk); | ||||||
|  | 	unsigned long tout; | ||||||
|  | 
 | ||||||
|  | 	/* should never be called with mptcp level timer cleared */ | ||||||
|  | 	tout = READ_ONCE(mptcp_sk(sk)->timer_ival); | ||||||
|  | 	if (WARN_ON_ONCE(!tout)) | ||||||
|  | 		tout = TCP_RTO_MIN; | ||||||
|  | 	sk_reset_timer(sk, &icsk->icsk_retransmit_timer, jiffies + tout); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void mptcp_data_acked(struct sock *sk) | ||||||
|  | { | ||||||
|  | 	mptcp_reset_timer(sk); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void mptcp_stop_timer(struct sock *sk) | ||||||
|  | { | ||||||
|  | 	struct inet_connection_sock *icsk = inet_csk(sk); | ||||||
|  | 
 | ||||||
|  | 	sk_stop_timer(sk, &icsk->icsk_retransmit_timer); | ||||||
|  | 	mptcp_sk(sk)->timer_ival = 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static bool mptcp_ext_cache_refill(struct mptcp_sock *msk) | static bool mptcp_ext_cache_refill(struct mptcp_sock *msk) | ||||||
| { | { | ||||||
| 	if (!msk->cached_ext) | 	if (!msk->cached_ext) | ||||||
|  | @ -596,10 +636,15 @@ static int mptcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) | ||||||
| 		copied += ret; | 		copied += ret; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	mptcp_set_timeout(sk, ssk); | ||||||
| 	if (copied) { | 	if (copied) { | ||||||
| 		ret = copied; | 		ret = copied; | ||||||
| 		tcp_push(ssk, msg->msg_flags, mss_now, tcp_sk(ssk)->nonagle, | 		tcp_push(ssk, msg->msg_flags, mss_now, tcp_sk(ssk)->nonagle, | ||||||
| 			 size_goal); | 			 size_goal); | ||||||
|  | 
 | ||||||
|  | 		/* start the timer, if it's not pending */ | ||||||
|  | 		if (!mptcp_timer_pending(sk)) | ||||||
|  | 			mptcp_reset_timer(sk); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	ssk_check_wmem(msk, ssk); | 	ssk_check_wmem(msk, ssk); | ||||||
|  | @ -787,6 +832,35 @@ static int mptcp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, | ||||||
| 	return copied; | 	return copied; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static void mptcp_retransmit_handler(struct sock *sk) | ||||||
|  | { | ||||||
|  | 	struct mptcp_sock *msk = mptcp_sk(sk); | ||||||
|  | 
 | ||||||
|  | 	if (atomic64_read(&msk->snd_una) == msk->write_seq) | ||||||
|  | 		mptcp_stop_timer(sk); | ||||||
|  | 	else | ||||||
|  | 		mptcp_reset_timer(sk); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void mptcp_retransmit_timer(struct timer_list *t) | ||||||
|  | { | ||||||
|  | 	struct inet_connection_sock *icsk = from_timer(icsk, t, | ||||||
|  | 						       icsk_retransmit_timer); | ||||||
|  | 	struct sock *sk = &icsk->icsk_inet.sk; | ||||||
|  | 
 | ||||||
|  | 	bh_lock_sock(sk); | ||||||
|  | 	if (!sock_owned_by_user(sk)) { | ||||||
|  | 		mptcp_retransmit_handler(sk); | ||||||
|  | 	} else { | ||||||
|  | 		/* delegate our work to tcp_release_cb() */ | ||||||
|  | 		if (!test_and_set_bit(TCP_WRITE_TIMER_DEFERRED, | ||||||
|  | 				      &sk->sk_tsq_flags)) | ||||||
|  | 			sock_hold(sk); | ||||||
|  | 	} | ||||||
|  | 	bh_unlock_sock(sk); | ||||||
|  | 	sock_put(sk); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /* subflow sockets can be either outgoing (connect) or incoming
 | /* subflow sockets can be either outgoing (connect) or incoming
 | ||||||
|  * (accept). |  * (accept). | ||||||
|  * |  * | ||||||
|  | @ -846,6 +920,9 @@ static int __mptcp_init_sock(struct sock *sk) | ||||||
| 
 | 
 | ||||||
| 	mptcp_pm_data_init(msk); | 	mptcp_pm_data_init(msk); | ||||||
| 
 | 
 | ||||||
|  | 	/* re-use the csk retrans timer for MPTCP-level retrans */ | ||||||
|  | 	timer_setup(&msk->sk.icsk_retransmit_timer, mptcp_retransmit_timer, 0); | ||||||
|  | 
 | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -867,6 +944,8 @@ static void __mptcp_clear_xmit(struct sock *sk) | ||||||
| 	struct mptcp_sock *msk = mptcp_sk(sk); | 	struct mptcp_sock *msk = mptcp_sk(sk); | ||||||
| 	struct mptcp_data_frag *dtmp, *dfrag; | 	struct mptcp_data_frag *dtmp, *dfrag; | ||||||
| 
 | 
 | ||||||
|  | 	sk_stop_timer(sk, &msk->sk.icsk_retransmit_timer); | ||||||
|  | 
 | ||||||
| 	list_for_each_entry_safe(dfrag, dtmp, &msk->rtx_queue, list) | 	list_for_each_entry_safe(dfrag, dtmp, &msk->rtx_queue, list) | ||||||
| 		dfrag_clear(dfrag); | 		dfrag_clear(dfrag); | ||||||
| } | } | ||||||
|  | @ -1155,7 +1234,8 @@ static int mptcp_getsockopt(struct sock *sk, int level, int optname, | ||||||
| 	return -EOPNOTSUPP; | 	return -EOPNOTSUPP; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #define MPTCP_DEFERRED_ALL TCPF_DELACK_TIMER_DEFERRED | #define MPTCP_DEFERRED_ALL (TCPF_DELACK_TIMER_DEFERRED | \ | ||||||
|  | 			    TCPF_WRITE_TIMER_DEFERRED) | ||||||
| 
 | 
 | ||||||
| /* this is very alike tcp_release_cb() but we must handle differently a
 | /* this is very alike tcp_release_cb() but we must handle differently a
 | ||||||
|  * different set of events |  * different set of events | ||||||
|  | @ -1171,6 +1251,8 @@ static void mptcp_release_cb(struct sock *sk) | ||||||
| 		nflags = flags & ~MPTCP_DEFERRED_ALL; | 		nflags = flags & ~MPTCP_DEFERRED_ALL; | ||||||
| 	} while (cmpxchg(&sk->sk_tsq_flags, flags, nflags) != flags); | 	} while (cmpxchg(&sk->sk_tsq_flags, flags, nflags) != flags); | ||||||
| 
 | 
 | ||||||
|  | 	sock_release_ownership(sk); | ||||||
|  | 
 | ||||||
| 	if (flags & TCPF_DELACK_TIMER_DEFERRED) { | 	if (flags & TCPF_DELACK_TIMER_DEFERRED) { | ||||||
| 		struct mptcp_sock *msk = mptcp_sk(sk); | 		struct mptcp_sock *msk = mptcp_sk(sk); | ||||||
| 		struct sock *ssk; | 		struct sock *ssk; | ||||||
|  | @ -1179,6 +1261,11 @@ static void mptcp_release_cb(struct sock *sk) | ||||||
| 		if (!ssk || !schedule_work(&msk->work)) | 		if (!ssk || !schedule_work(&msk->work)) | ||||||
| 			__sock_put(sk); | 			__sock_put(sk); | ||||||
| 	} | 	} | ||||||
|  | 
 | ||||||
|  | 	if (flags & TCPF_WRITE_TIMER_DEFERRED) { | ||||||
|  | 		mptcp_retransmit_handler(sk); | ||||||
|  | 		__sock_put(sk); | ||||||
|  | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static int mptcp_get_port(struct sock *sk, unsigned short snum) | static int mptcp_get_port(struct sock *sk, unsigned short snum) | ||||||
|  |  | ||||||
|  | @ -157,6 +157,7 @@ struct mptcp_sock { | ||||||
| 	u64		write_seq; | 	u64		write_seq; | ||||||
| 	u64		ack_seq; | 	u64		ack_seq; | ||||||
| 	atomic64_t	snd_una; | 	atomic64_t	snd_una; | ||||||
|  | 	unsigned long	timer_ival; | ||||||
| 	u32		token; | 	u32		token; | ||||||
| 	unsigned long	flags; | 	unsigned long	flags; | ||||||
| 	bool		can_ack; | 	bool		can_ack; | ||||||
|  | @ -326,6 +327,7 @@ void mptcp_get_options(const struct sk_buff *skb, | ||||||
| void mptcp_finish_connect(struct sock *sk); | void mptcp_finish_connect(struct sock *sk); | ||||||
| void mptcp_data_ready(struct sock *sk, struct sock *ssk); | void mptcp_data_ready(struct sock *sk, struct sock *ssk); | ||||||
| bool mptcp_finish_join(struct sock *sk); | bool mptcp_finish_join(struct sock *sk); | ||||||
|  | void mptcp_data_acked(struct sock *sk); | ||||||
| 
 | 
 | ||||||
| int mptcp_token_new_request(struct request_sock *req); | int mptcp_token_new_request(struct request_sock *req); | ||||||
| void mptcp_token_destroy_request(u32 token); | void mptcp_token_destroy_request(u32 token); | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Paolo Abeni
						Paolo Abeni