Commit 0f86a5be authored by David S. Miller's avatar David S. Miller

Merge branch 'mptcp-fixes'

Paolo Abeni says:

====================
mptcp: a bunch of fixes

This series includes a few fixes following-up the
recent code refactor for the MPTCP RX and TX paths.

Boundling them together, since the fixes are somewhat
related.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 7bdddc68 d7b1bfd0
...@@ -802,7 +802,12 @@ static bool check_fully_established(struct mptcp_sock *msk, struct sock *ssk, ...@@ -802,7 +802,12 @@ static bool check_fully_established(struct mptcp_sock *msk, struct sock *ssk,
mptcp_subflow_fully_established(subflow, mp_opt); mptcp_subflow_fully_established(subflow, mp_opt);
fully_established: fully_established:
if (likely(subflow->pm_notified)) /* if the subflow is not already linked into the conn_list, we can't
* notify the PM: this subflow is still on the listener queue
* and the PM possibly acquiring the subflow lock could race with
* the listener close
*/
if (likely(subflow->pm_notified) || list_empty(&subflow->node))
return true; return true;
subflow->pm_notified = 1; subflow->pm_notified = 1;
......
...@@ -126,8 +126,14 @@ void mptcp_pm_fully_established(struct mptcp_sock *msk) ...@@ -126,8 +126,14 @@ void mptcp_pm_fully_established(struct mptcp_sock *msk)
spin_lock_bh(&pm->lock); spin_lock_bh(&pm->lock);
if (READ_ONCE(pm->work_pending)) /* mptcp_pm_fully_established() can be invoked by multiple
* racing paths - accept() and check_fully_established()
* be sure to serve this event only once.
*/
if (READ_ONCE(pm->work_pending) &&
!(msk->pm.status & BIT(MPTCP_PM_ALREADY_ESTABLISHED)))
mptcp_pm_schedule_work(msk, MPTCP_PM_ESTABLISHED); mptcp_pm_schedule_work(msk, MPTCP_PM_ESTABLISHED);
msk->pm.status |= BIT(MPTCP_PM_ALREADY_ESTABLISHED);
spin_unlock_bh(&pm->lock); spin_unlock_bh(&pm->lock);
} }
......
...@@ -701,6 +701,13 @@ void mptcp_data_ready(struct sock *sk, struct sock *ssk) ...@@ -701,6 +701,13 @@ void mptcp_data_ready(struct sock *sk, struct sock *ssk)
int sk_rbuf, ssk_rbuf; int sk_rbuf, ssk_rbuf;
bool wake; bool wake;
/* The peer can send data while we are shutting down this
* subflow at msk destruction time, but we must avoid enqueuing
* more data to the msk receive queue
*/
if (unlikely(subflow->disposable))
return;
/* move_skbs_to_msk below can legitly clear the data_avail flag, /* move_skbs_to_msk below can legitly clear the data_avail flag,
* but we will need later to properly woke the reader, cache its * but we will need later to properly woke the reader, cache its
* value * value
...@@ -2119,6 +2126,8 @@ void __mptcp_close_ssk(struct sock *sk, struct sock *ssk, ...@@ -2119,6 +2126,8 @@ void __mptcp_close_ssk(struct sock *sk, struct sock *ssk,
sock_orphan(ssk); sock_orphan(ssk);
} }
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
* the ssk has been already destroyed, we just need to release the * the ssk has been already destroyed, we just need to release the
* reference owned by msk; * reference owned by msk;
...@@ -2126,8 +2135,7 @@ void __mptcp_close_ssk(struct sock *sk, struct sock *ssk, ...@@ -2126,8 +2135,7 @@ void __mptcp_close_ssk(struct sock *sk, struct sock *ssk,
if (!inet_csk(ssk)->icsk_ulp_ops) { if (!inet_csk(ssk)->icsk_ulp_ops) {
kfree_rcu(subflow, rcu); kfree_rcu(subflow, rcu);
} else { } else {
/* otherwise ask tcp do dispose of ssk and subflow ctx */ /* otherwise tcp will dispose of the ssk and subflow ctx */
subflow->disposable = 1;
__tcp_close(ssk, 0); __tcp_close(ssk, 0);
/* close acquired an extra ref */ /* close acquired an extra ref */
...@@ -3208,6 +3216,17 @@ static int mptcp_stream_accept(struct socket *sock, struct socket *newsock, ...@@ -3208,6 +3216,17 @@ static int mptcp_stream_accept(struct socket *sock, struct socket *newsock,
bool slowpath; bool slowpath;
slowpath = lock_sock_fast(newsk); slowpath = lock_sock_fast(newsk);
/* PM/worker can now acquire the first subflow socket
* lock without racing with listener queue cleanup,
* we can notify it, if needed.
*/
subflow = mptcp_subflow_ctx(msk->first);
list_add(&subflow->node, &msk->conn_list);
sock_hold(msk->first);
if (mptcp_is_fully_established(newsk))
mptcp_pm_fully_established(msk);
mptcp_copy_inaddrs(newsk, msk->first); mptcp_copy_inaddrs(newsk, msk->first);
mptcp_rcv_space_init(msk, msk->first); mptcp_rcv_space_init(msk, msk->first);
......
...@@ -165,6 +165,7 @@ enum mptcp_pm_status { ...@@ -165,6 +165,7 @@ enum mptcp_pm_status {
MPTCP_PM_ADD_ADDR_SEND_ACK, MPTCP_PM_ADD_ADDR_SEND_ACK,
MPTCP_PM_RM_ADDR_RECEIVED, MPTCP_PM_RM_ADDR_RECEIVED,
MPTCP_PM_ESTABLISHED, MPTCP_PM_ESTABLISHED,
MPTCP_PM_ALREADY_ESTABLISHED, /* persistent status, set after ESTABLISHED event */
MPTCP_PM_SUBFLOW_ESTABLISHED, MPTCP_PM_SUBFLOW_ESTABLISHED,
}; };
......
...@@ -614,8 +614,9 @@ static struct sock *subflow_syn_recv_sock(const struct sock *sk, ...@@ -614,8 +614,9 @@ static struct sock *subflow_syn_recv_sock(const struct sock *sk,
*/ */
inet_sk_state_store((void *)new_msk, TCP_ESTABLISHED); inet_sk_state_store((void *)new_msk, TCP_ESTABLISHED);
/* link the newly created socket to the msk */ /* record the newly created socket as the first msk
mptcp_add_pending_subflow(mptcp_sk(new_msk), ctx); * subflow, but don't link it yet into conn_list
*/
WRITE_ONCE(mptcp_sk(new_msk)->first, child); WRITE_ONCE(mptcp_sk(new_msk)->first, child);
/* new mpc subflow takes ownership of the newly /* new mpc subflow takes ownership of the newly
...@@ -1148,13 +1149,18 @@ int __mptcp_subflow_connect(struct sock *sk, const struct mptcp_addr_info *loc, ...@@ -1148,13 +1149,18 @@ int __mptcp_subflow_connect(struct sock *sk, const struct mptcp_addr_info *loc,
subflow->request_bkup = !!(loc->flags & MPTCP_PM_ADDR_FLAG_BACKUP); subflow->request_bkup = !!(loc->flags & MPTCP_PM_ADDR_FLAG_BACKUP);
mptcp_info2sockaddr(remote, &addr); mptcp_info2sockaddr(remote, &addr);
mptcp_add_pending_subflow(msk, subflow);
err = kernel_connect(sf, (struct sockaddr *)&addr, addrlen, O_NONBLOCK); err = kernel_connect(sf, (struct sockaddr *)&addr, addrlen, O_NONBLOCK);
if (err && err != -EINPROGRESS) if (err && err != -EINPROGRESS)
goto failed; goto failed_unlink;
mptcp_add_pending_subflow(msk, subflow);
return err; return err;
failed_unlink:
spin_lock_bh(&msk->join_list_lock);
list_del(&subflow->node);
spin_unlock_bh(&msk->join_list_lock);
failed: failed:
subflow->disposable = 1; subflow->disposable = 1;
sock_release(sf); sock_release(sf);
...@@ -1333,9 +1339,10 @@ static void subflow_ulp_release(struct sock *ssk) ...@@ -1333,9 +1339,10 @@ static void subflow_ulp_release(struct sock *ssk)
sk = ctx->conn; sk = ctx->conn;
if (sk) { if (sk) {
/* if the msk has been orphaned, keep the ctx /* if the msk has been orphaned, keep the ctx
* alive, will be freed by mptcp_done() * alive, will be freed by __mptcp_close_ssk(),
* when the subflow is still unaccepted
*/ */
release = ctx->disposable; release = ctx->disposable || list_empty(&ctx->node);
sock_put(sk); sock_put(sk);
} }
......
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