Commit b29fcfb5 authored by Paolo Abeni's avatar Paolo Abeni Committed by David S. Miller

mptcp: full disconnect implementation

The current mptcp_disconnect() implementation lacks several
steps, we additionally need to reset the msk socket state
and flush the subflow list.

Factor out the needed helper to avoid code duplication.

Additionally ensure that the initial subflow is disposed
only after mptcp_close(), just reset it at disconnect time.
Signed-off-by: default avatarPaolo Abeni <pabeni@redhat.com>
Signed-off-by: default avatarMat Martineau <mathew.j.martineau@linux.intel.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent f284c0c7
...@@ -356,7 +356,7 @@ void mptcp_pm_subflow_chk_stale(const struct mptcp_sock *msk, struct sock *ssk) ...@@ -356,7 +356,7 @@ void mptcp_pm_subflow_chk_stale(const struct mptcp_sock *msk, struct sock *ssk)
} }
} }
void mptcp_pm_data_init(struct mptcp_sock *msk) void mptcp_pm_data_reset(struct mptcp_sock *msk)
{ {
msk->pm.add_addr_signaled = 0; msk->pm.add_addr_signaled = 0;
msk->pm.add_addr_accepted = 0; msk->pm.add_addr_accepted = 0;
...@@ -371,10 +371,14 @@ void mptcp_pm_data_init(struct mptcp_sock *msk) ...@@ -371,10 +371,14 @@ void mptcp_pm_data_init(struct mptcp_sock *msk)
WRITE_ONCE(msk->pm.remote_deny_join_id0, false); WRITE_ONCE(msk->pm.remote_deny_join_id0, false);
msk->pm.status = 0; msk->pm.status = 0;
mptcp_pm_nl_data_init(msk);
}
void mptcp_pm_data_init(struct mptcp_sock *msk)
{
spin_lock_init(&msk->pm.lock); spin_lock_init(&msk->pm.lock);
INIT_LIST_HEAD(&msk->pm.anno_list); INIT_LIST_HEAD(&msk->pm.anno_list);
mptcp_pm_data_reset(msk);
mptcp_pm_nl_data_init(msk);
} }
void __init mptcp_pm_init(void) void __init mptcp_pm_init(void)
......
...@@ -2253,6 +2253,10 @@ bool __mptcp_retransmit_pending_data(struct sock *sk) ...@@ -2253,6 +2253,10 @@ bool __mptcp_retransmit_pending_data(struct sock *sk)
return true; return true;
} }
/* flags for __mptcp_close_ssk() */
#define MPTCP_CF_PUSH BIT(1)
#define MPTCP_CF_FASTCLOSE BIT(2)
/* subflow sockets can be either outgoing (connect) or incoming /* subflow sockets can be either outgoing (connect) or incoming
* (accept). * (accept).
* *
...@@ -2262,22 +2266,37 @@ bool __mptcp_retransmit_pending_data(struct sock *sk) ...@@ -2262,22 +2266,37 @@ bool __mptcp_retransmit_pending_data(struct sock *sk)
* parent socket. * parent socket.
*/ */
static void __mptcp_close_ssk(struct sock *sk, struct sock *ssk, static void __mptcp_close_ssk(struct sock *sk, struct sock *ssk,
struct mptcp_subflow_context *subflow) struct mptcp_subflow_context *subflow,
unsigned int flags)
{ {
struct mptcp_sock *msk = mptcp_sk(sk); struct mptcp_sock *msk = mptcp_sk(sk);
bool need_push; bool need_push, dispose_it;
list_del(&subflow->node); dispose_it = !msk->subflow || ssk != msk->subflow->sk;
if (dispose_it)
list_del(&subflow->node);
lock_sock_nested(ssk, SINGLE_DEPTH_NESTING); lock_sock_nested(ssk, SINGLE_DEPTH_NESTING);
if (flags & MPTCP_CF_FASTCLOSE)
subflow->send_fastclose = 1;
need_push = (flags & MPTCP_CF_PUSH) && __mptcp_retransmit_pending_data(sk);
if (!dispose_it) {
tcp_disconnect(ssk, 0);
msk->subflow->state = SS_UNCONNECTED;
mptcp_subflow_ctx_reset(subflow);
release_sock(ssk);
goto out;
}
/* if we are invoked by the msk cleanup code, the subflow is /* if we are invoked by the msk cleanup code, the subflow is
* already orphaned * already orphaned
*/ */
if (ssk->sk_socket) if (ssk->sk_socket)
sock_orphan(ssk); sock_orphan(ssk);
need_push = __mptcp_retransmit_pending_data(sk);
subflow->disposable = 1; subflow->disposable = 1;
/* if ssk hit tcp_done(), tcp_cleanup_ulp() cleared the related ops /* if ssk hit tcp_done(), tcp_cleanup_ulp() cleared the related ops
...@@ -2297,14 +2316,12 @@ static void __mptcp_close_ssk(struct sock *sk, struct sock *ssk, ...@@ -2297,14 +2316,12 @@ static void __mptcp_close_ssk(struct sock *sk, struct sock *ssk,
sock_put(ssk); sock_put(ssk);
if (ssk == msk->last_snd)
msk->last_snd = NULL;
if (ssk == msk->first) if (ssk == msk->first)
msk->first = NULL; msk->first = NULL;
if (msk->subflow && ssk == msk->subflow->sk) out:
mptcp_dispose_initial_subflow(msk); if (ssk == msk->last_snd)
msk->last_snd = NULL;
if (need_push) if (need_push)
__mptcp_push_pending(sk, 0); __mptcp_push_pending(sk, 0);
...@@ -2315,7 +2332,7 @@ void mptcp_close_ssk(struct sock *sk, struct sock *ssk, ...@@ -2315,7 +2332,7 @@ void mptcp_close_ssk(struct sock *sk, struct sock *ssk,
{ {
if (sk->sk_state == TCP_ESTABLISHED) if (sk->sk_state == TCP_ESTABLISHED)
mptcp_event(MPTCP_EVENT_SUB_CLOSED, mptcp_sk(sk), ssk, GFP_KERNEL); mptcp_event(MPTCP_EVENT_SUB_CLOSED, mptcp_sk(sk), ssk, GFP_KERNEL);
__mptcp_close_ssk(sk, ssk, subflow); __mptcp_close_ssk(sk, ssk, subflow, MPTCP_CF_PUSH);
} }
static unsigned int mptcp_sync_mss(struct sock *sk, u32 pmtu) static unsigned int mptcp_sync_mss(struct sock *sk, u32 pmtu)
...@@ -2533,9 +2550,20 @@ static int __mptcp_init_sock(struct sock *sk) ...@@ -2533,9 +2550,20 @@ static int __mptcp_init_sock(struct sock *sk)
return 0; return 0;
} }
static int mptcp_init_sock(struct sock *sk) static void mptcp_ca_reset(struct sock *sk)
{ {
struct inet_connection_sock *icsk = inet_csk(sk); struct inet_connection_sock *icsk = inet_csk(sk);
tcp_assign_congestion_control(sk);
strcpy(mptcp_sk(sk)->ca_name, icsk->icsk_ca_ops->name);
/* no need to keep a reference to the ops, the name will suffice */
tcp_cleanup_congestion_control(sk);
icsk->icsk_ca_ops = NULL;
}
static int mptcp_init_sock(struct sock *sk)
{
struct net *net = sock_net(sk); struct net *net = sock_net(sk);
int ret; int ret;
...@@ -2556,12 +2584,7 @@ static int mptcp_init_sock(struct sock *sk) ...@@ -2556,12 +2584,7 @@ static int mptcp_init_sock(struct sock *sk)
/* fetch the ca name; do it outside __mptcp_init_sock(), so that clone will /* fetch the ca name; do it outside __mptcp_init_sock(), so that clone will
* propagate the correct value * propagate the correct value
*/ */
tcp_assign_congestion_control(sk); mptcp_ca_reset(sk);
strcpy(mptcp_sk(sk)->ca_name, icsk->icsk_ca_ops->name);
/* no need to keep a reference to the ops, the name will suffice */
tcp_cleanup_congestion_control(sk);
icsk->icsk_ca_ops = NULL;
sk_sockets_allocated_inc(sk); sk_sockets_allocated_inc(sk);
sk->sk_rcvbuf = sock_net(sk)->ipv4.sysctl_tcp_rmem[1]; sk->sk_rcvbuf = sock_net(sk)->ipv4.sysctl_tcp_rmem[1];
...@@ -2720,9 +2743,13 @@ static void __mptcp_destroy_sock(struct sock *sk) ...@@ -2720,9 +2743,13 @@ static void __mptcp_destroy_sock(struct sock *sk)
sk_stop_timer(sk, &sk->sk_timer); sk_stop_timer(sk, &sk->sk_timer);
msk->pm.status = 0; msk->pm.status = 0;
/* clears msk->subflow, allowing the following loop to close
* even the initial subflow
*/
mptcp_dispose_initial_subflow(msk);
list_for_each_entry_safe(subflow, tmp, &conn_list, node) { list_for_each_entry_safe(subflow, tmp, &conn_list, node) {
struct sock *ssk = mptcp_subflow_tcp_sock(subflow); struct sock *ssk = mptcp_subflow_tcp_sock(subflow);
__mptcp_close_ssk(sk, ssk, subflow); __mptcp_close_ssk(sk, ssk, subflow, 0);
} }
sk->sk_prot->destroy(sk); sk->sk_prot->destroy(sk);
...@@ -2733,7 +2760,6 @@ static void __mptcp_destroy_sock(struct sock *sk) ...@@ -2733,7 +2760,6 @@ static void __mptcp_destroy_sock(struct sock *sk)
xfrm_sk_free_policy(sk); xfrm_sk_free_policy(sk);
sk_refcnt_debug_release(sk); sk_refcnt_debug_release(sk);
mptcp_dispose_initial_subflow(msk);
sock_put(sk); sock_put(sk);
} }
...@@ -2769,6 +2795,9 @@ static void mptcp_close(struct sock *sk, long timeout) ...@@ -2769,6 +2795,9 @@ static void mptcp_close(struct sock *sk, long timeout)
sock_hold(sk); sock_hold(sk);
pr_debug("msk=%p state=%d", sk, sk->sk_state); pr_debug("msk=%p state=%d", sk, sk->sk_state);
if (mptcp_sk(sk)->token)
mptcp_event(MPTCP_EVENT_CLOSED, mptcp_sk(sk), NULL, GFP_KERNEL);
if (sk->sk_state == TCP_CLOSE) { if (sk->sk_state == TCP_CLOSE) {
__mptcp_destroy_sock(sk); __mptcp_destroy_sock(sk);
do_cancel_work = true; do_cancel_work = true;
...@@ -2779,9 +2808,6 @@ static void mptcp_close(struct sock *sk, long timeout) ...@@ -2779,9 +2808,6 @@ static void mptcp_close(struct sock *sk, long timeout)
if (do_cancel_work) if (do_cancel_work)
mptcp_cancel_work(sk); mptcp_cancel_work(sk);
if (mptcp_sk(sk)->token)
mptcp_event(MPTCP_EVENT_CLOSED, mptcp_sk(sk), NULL, GFP_KERNEL);
sock_put(sk); sock_put(sk);
} }
...@@ -2815,13 +2841,36 @@ static int mptcp_disconnect(struct sock *sk, int flags) ...@@ -2815,13 +2841,36 @@ static int mptcp_disconnect(struct sock *sk, int flags)
mptcp_do_flush_join_list(msk); mptcp_do_flush_join_list(msk);
inet_sk_state_store(sk, TCP_CLOSE);
mptcp_for_each_subflow(msk, subflow) { mptcp_for_each_subflow(msk, subflow) {
struct sock *ssk = mptcp_subflow_tcp_sock(subflow); struct sock *ssk = mptcp_subflow_tcp_sock(subflow);
lock_sock(ssk); __mptcp_close_ssk(sk, ssk, subflow, MPTCP_CF_FASTCLOSE);
tcp_disconnect(ssk, flags);
release_sock(ssk);
} }
sk_stop_timer(sk, &msk->sk.icsk_retransmit_timer);
sk_stop_timer(sk, &sk->sk_timer);
if (mptcp_sk(sk)->token)
mptcp_event(MPTCP_EVENT_CLOSED, mptcp_sk(sk), NULL, GFP_KERNEL);
mptcp_destroy_common(msk);
msk->last_snd = NULL;
msk->flags = 0;
msk->recovery = false;
msk->can_ack = false;
msk->fully_established = false;
msk->rcv_data_fin = false;
msk->snd_data_fin_enable = false;
msk->rcv_fastclose = false;
msk->use_64bit_ack = false;
WRITE_ONCE(msk->csum_enabled, mptcp_is_checksum_enabled(sock_net(sk)));
mptcp_pm_data_reset(msk);
mptcp_ca_reset(sk);
sk->sk_shutdown = 0;
sk_error_report(sk);
return 0; return 0;
} }
...@@ -2961,9 +3010,11 @@ void mptcp_destroy_common(struct mptcp_sock *msk) ...@@ -2961,9 +3010,11 @@ void mptcp_destroy_common(struct mptcp_sock *msk)
__mptcp_clear_xmit(sk); __mptcp_clear_xmit(sk);
/* move to sk_receive_queue, sk_stream_kill_queues will purge it */ /* move to sk_receive_queue, sk_stream_kill_queues will purge it */
mptcp_data_lock(sk);
skb_queue_splice_tail_init(&msk->receive_queue, &sk->sk_receive_queue); skb_queue_splice_tail_init(&msk->receive_queue, &sk->sk_receive_queue);
__skb_queue_purge(&sk->sk_receive_queue); __skb_queue_purge(&sk->sk_receive_queue);
skb_rbtree_purge(&msk->out_of_order_queue); skb_rbtree_purge(&msk->out_of_order_queue);
mptcp_data_unlock(sk);
/* move all the rx fwd alloc into the sk_mem_reclaim_final in /* move all the rx fwd alloc into the sk_mem_reclaim_final in
* inet_sock_destruct() will dispose it * inet_sock_destruct() will dispose it
......
...@@ -395,6 +395,9 @@ DECLARE_PER_CPU(struct mptcp_delegated_action, mptcp_delegated_actions); ...@@ -395,6 +395,9 @@ DECLARE_PER_CPU(struct mptcp_delegated_action, mptcp_delegated_actions);
/* MPTCP subflow context */ /* MPTCP subflow context */
struct mptcp_subflow_context { struct mptcp_subflow_context {
struct list_head node;/* conn_list of subflows */ struct list_head node;/* conn_list of subflows */
char reset_start[0];
unsigned long avg_pacing_rate; /* protected by msk socket lock */ unsigned long avg_pacing_rate; /* protected by msk socket lock */
u64 local_key; u64 local_key;
u64 remote_key; u64 remote_key;
...@@ -442,6 +445,9 @@ struct mptcp_subflow_context { ...@@ -442,6 +445,9 @@ struct mptcp_subflow_context {
u8 stale_count; u8 stale_count;
long delegated_status; long delegated_status;
char reset_end[0];
struct list_head delegated_node; /* link into delegated_action, protected by local BH */ struct list_head delegated_node; /* link into delegated_action, protected by local BH */
u32 setsockopt_seq; u32 setsockopt_seq;
...@@ -473,6 +479,13 @@ mptcp_subflow_tcp_sock(const struct mptcp_subflow_context *subflow) ...@@ -473,6 +479,13 @@ mptcp_subflow_tcp_sock(const struct mptcp_subflow_context *subflow)
return subflow->tcp_sock; return subflow->tcp_sock;
} }
static inline void
mptcp_subflow_ctx_reset(struct mptcp_subflow_context *subflow)
{
memset(subflow->reset_start, 0, subflow->reset_end - subflow->reset_start);
subflow->request_mptcp = 1;
}
static inline u64 static inline u64
mptcp_subflow_get_map_offset(const struct mptcp_subflow_context *subflow) mptcp_subflow_get_map_offset(const struct mptcp_subflow_context *subflow)
{ {
...@@ -713,6 +726,7 @@ void mptcp_crypto_hmac_sha(u64 key1, u64 key2, u8 *msg, int len, void *hmac); ...@@ -713,6 +726,7 @@ void mptcp_crypto_hmac_sha(u64 key1, u64 key2, u8 *msg, int len, void *hmac);
void __init mptcp_pm_init(void); void __init mptcp_pm_init(void);
void mptcp_pm_data_init(struct mptcp_sock *msk); void mptcp_pm_data_init(struct mptcp_sock *msk);
void mptcp_pm_data_reset(struct mptcp_sock *msk);
void mptcp_pm_subflow_chk_stale(const struct mptcp_sock *msk, struct sock *ssk); void mptcp_pm_subflow_chk_stale(const struct mptcp_sock *msk, struct sock *ssk);
void mptcp_pm_nl_subflow_chk_stale(const struct mptcp_sock *msk, struct sock *ssk); void mptcp_pm_nl_subflow_chk_stale(const struct mptcp_sock *msk, struct sock *ssk);
void mptcp_pm_new_connection(struct mptcp_sock *msk, const struct sock *ssk, int server_side); void mptcp_pm_new_connection(struct mptcp_sock *msk, const struct sock *ssk, int server_side);
......
...@@ -384,6 +384,7 @@ void mptcp_token_destroy(struct mptcp_sock *msk) ...@@ -384,6 +384,7 @@ void mptcp_token_destroy(struct mptcp_sock *msk)
bucket->chain_len--; bucket->chain_len--;
} }
spin_unlock_bh(&bucket->lock); spin_unlock_bh(&bucket->lock);
WRITE_ONCE(msk->token, 0);
} }
void __init mptcp_token_init(void) void __init mptcp_token_init(void)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment