From 2afd1babd9ce15224792ee254407d79df5cd2241 Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Fri, 13 Dec 2019 22:48:31 +0100 Subject: [PATCH] subflow: place further subflows on new 'join_list' When the pm workqueue establishes a new subflow, we never place this socket on any list, ie. if such a request would time out/never complete we can't clean it up later on -- it never made it to the conn_list of established subflows. Place them on a new join_list. When the connection completes, the subflow is moved to the conn_list. If it never completes, the socket gets closed at mptcp_shutdown time. Not done yet: 1. extend mptcp worker to collect timed-out entries 2. prevent arbitrary growth of pending ssks. Signed-off-by: Florian Westphal --- net/mptcp/protocol.c | 25 +++++++++++++++++++++++++ net/mptcp/protocol.h | 2 ++ 2 files changed, 27 insertions(+) diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c index df27f9da7d767..d7585fc9a4ffc 100644 --- a/net/mptcp/protocol.c +++ b/net/mptcp/protocol.c @@ -137,6 +137,16 @@ static struct socket *__mptcp_socket_create(struct mptcp_sock *msk, int state) return ssock; } +static void __mptcp_flush_join_list(struct mptcp_sock *msk) +{ + if (likely(list_empty(&msk->join_list))) + return; + + spin_lock_bh(&msk->join_list_lock); + list_splice_tail(&msk->join_list, &msk->conn_list); + 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 ? @@ -556,6 +566,7 @@ static int mptcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) mptcp_clean_una(sk); + __mptcp_flush_join_list(msk); ssk = mptcp_subflow_get_send(msk); while (!sk_stream_memory_free(sk) || !ssk) { ret = sk_stream_wait_memory(sk, &timeo); @@ -694,6 +705,7 @@ static int mptcp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, len = min_t(size_t, len, INT_MAX); target = sock_rcvlowat(sk, flags & MSG_WAITALL, len); + __mptcp_flush_join_list(msk); while (!done) { u32 map_remaining; @@ -945,6 +957,7 @@ static void mptcp_worker(struct work_struct *work) if (!dfrag) goto unlock; + __mptcp_flush_join_list(msk); ssk = mptcp_subflow_get_retrans(msk); if (!ssk) goto reset_unlock; @@ -990,7 +1003,10 @@ static int __mptcp_init_sock(struct sock *sk) { struct mptcp_sock *msk = mptcp_sk(sk); + spin_lock_init(&msk->join_list_lock); + INIT_LIST_HEAD(&msk->conn_list); + INIT_LIST_HEAD(&msk->join_list); INIT_LIST_HEAD(&msk->rtx_queue); __set_bit(MPTCP_SEND_SPACE, &msk->flags); @@ -1076,6 +1092,8 @@ static void __mptcp_close(struct sock *sk, long timeout) mptcp_token_destroy(msk->token); inet_sk_state_store(sk, TCP_CLOSE); + __mptcp_flush_join_list(msk); + list_for_each_entry_safe(subflow, tmp, &msk->conn_list, node) { struct sock *ssk = mptcp_subflow_tcp_sock(subflow); @@ -1388,6 +1406,13 @@ bool mptcp_finish_join(struct sock *sk) if (parent_sock) { if (!sk->sk_socket) mptcp_sock_graft(sk, parent_sock); + + /* active connections are already on conn_list */ + if (list_empty(&subflow->node)) { + spin_lock(&msk->join_list_lock); + list_add_tail(&subflow->node, &msk->join_list); + spin_unlock(&msk->join_list_lock); + } } return true; } diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h index 836eadeafec5b..ead95592d9ebc 100644 --- a/net/mptcp/protocol.h +++ b/net/mptcp/protocol.h @@ -139,9 +139,11 @@ struct mptcp_sock { u32 token; unsigned long flags; bool can_ack; + spinlock_t join_list_lock; struct work_struct rtx_work; struct list_head conn_list; struct list_head rtx_queue; + struct list_head join_list; struct skb_ext *cached_ext; /* for the next sendmsg */ struct socket *subflow; /* outgoing connect/listener/!mp_capable */ struct sock *first;