diff options
author | Paolo Abeni <pabeni@redhat.com> | 2020-03-28 00:48:42 +0300 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2020-03-30 08:14:48 +0300 |
commit | cc9d2566986692e5b6df1aac88fbf65fe340d432 (patch) | |
tree | 3aecfec84f8fa69a2314fe3bcc370274e8b4fab1 /net | |
parent | 926bdeab5535856e3a164545630149f212d3482d (diff) | |
download | linux-cc9d2566986692e5b6df1aac88fbf65fe340d432.tar.xz |
mptcp: update per unacked sequence on pkt reception
So that we keep per unacked sequence number consistent; since
we update per msk data, use an atomic64 cmpxchg() to protect
against concurrent updates from multiple subflows.
Initialize the snd_una at connect()/accept() time.
Co-developed-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Florian Westphal <fw@strlen.de>
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>
Diffstat (limited to 'net')
-rw-r--r-- | net/mptcp/options.c | 52 | ||||
-rw-r--r-- | net/mptcp/protocol.c | 2 | ||||
-rw-r--r-- | net/mptcp/protocol.h | 1 |
3 files changed, 49 insertions, 6 deletions
diff --git a/net/mptcp/options.c b/net/mptcp/options.c index 20ba00865c55..b0ff8ad702a3 100644 --- a/net/mptcp/options.c +++ b/net/mptcp/options.c @@ -744,6 +744,46 @@ fully_established: return true; } +static u64 expand_ack(u64 old_ack, u64 cur_ack, bool use_64bit) +{ + u32 old_ack32, cur_ack32; + + if (use_64bit) + return cur_ack; + + old_ack32 = (u32)old_ack; + cur_ack32 = (u32)cur_ack; + cur_ack = (old_ack & GENMASK_ULL(63, 32)) + cur_ack32; + if (unlikely(before(cur_ack32, old_ack32))) + return cur_ack + (1LL << 32); + return cur_ack; +} + +static void update_una(struct mptcp_sock *msk, + struct mptcp_options_received *mp_opt) +{ + u64 new_snd_una, snd_una, old_snd_una = atomic64_read(&msk->snd_una); + u64 write_seq = READ_ONCE(msk->write_seq); + + /* avoid ack expansion on update conflict, to reduce the risk of + * wrongly expanding to a future ack sequence number, which is way + * more dangerous than missing an ack + */ + new_snd_una = expand_ack(old_snd_una, mp_opt->data_ack, mp_opt->ack64); + + /* ACK for data not even sent yet? Ignore. */ + if (after64(new_snd_una, write_seq)) + new_snd_una = old_snd_una; + + while (after64(new_snd_una, old_snd_una)) { + snd_una = old_snd_una; + old_snd_una = atomic64_cmpxchg(&msk->snd_una, snd_una, + new_snd_una); + if (old_snd_una == snd_una) + break; + } +} + static bool add_addr_hmac_valid(struct mptcp_sock *msk, struct mptcp_options_received *mp_opt) { @@ -805,6 +845,12 @@ void mptcp_incoming_options(struct sock *sk, struct sk_buff *skb, if (!mp_opt->dss) return; + /* we can't wait for recvmsg() to update the ack_seq, otherwise + * monodirectional flows will stuck + */ + if (mp_opt->use_ack) + update_una(msk, mp_opt); + mpext = skb_ext_add(skb, SKB_EXT_MPTCP); if (!mpext) return; @@ -831,12 +877,6 @@ void mptcp_incoming_options(struct sock *sk, struct sk_buff *skb, mpext->use_map = 1; } - if (mp_opt->use_ack) { - mpext->data_ack = mp_opt->data_ack; - mpext->use_ack = 1; - mpext->ack64 = mp_opt->ack64; - } - mpext->data_fin = mp_opt->data_fin; } diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c index 5c4560287bd2..d3197ace2a89 100644 --- a/net/mptcp/protocol.c +++ b/net/mptcp/protocol.c @@ -906,6 +906,7 @@ struct sock *mptcp_sk_clone(const struct sock *sk, struct request_sock *req) } msk->write_seq = subflow_req->idsn + 1; + atomic64_set(&msk->snd_una, msk->write_seq); if (subflow_req->remote_key_valid) { msk->can_ack = true; msk->remote_key = subflow_req->remote_key; @@ -1107,6 +1108,7 @@ void mptcp_finish_connect(struct sock *ssk) WRITE_ONCE(msk->write_seq, subflow->idsn + 1); WRITE_ONCE(msk->ack_seq, ack_seq); WRITE_ONCE(msk->can_ack, 1); + atomic64_set(&msk->snd_una, msk->write_seq); mptcp_pm_new_connection(msk, 0); } diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h index 209bdaa43dda..29db05467cc3 100644 --- a/net/mptcp/protocol.h +++ b/net/mptcp/protocol.h @@ -147,6 +147,7 @@ struct mptcp_sock { u64 remote_key; u64 write_seq; u64 ack_seq; + atomic64_t snd_una; u32 token; unsigned long flags; bool can_ack; |