mptcp: update per unacked sequence on pkt reception
authorPaolo Abeni <pabeni@redhat.com>
Fri, 27 Mar 2020 21:48:42 +0000 (14:48 -0700)
committerDavid S. Miller <davem@davemloft.net>
Mon, 30 Mar 2020 05:14:48 +0000 (22:14 -0700)
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>
net/mptcp/options.c
net/mptcp/protocol.c
net/mptcp/protocol.h

index 20ba008..b0ff8ad 100644 (file)
@@ -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;
 }
 
index 5c45602..d3197ac 100644 (file)
@@ -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);
 }
index 209bdaa..29db054 100644 (file)
@@ -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;