net: mptcp: improve fallback to TCP
[linux-2.6-microblaze.git] / net / mptcp / subflow.c
index 102db8c..cb8a42f 100644 (file)
@@ -216,7 +216,6 @@ static void subflow_finish_connect(struct sock *sk, const struct sk_buff *skb)
        struct mptcp_subflow_context *subflow = mptcp_subflow_ctx(sk);
        struct mptcp_options_received mp_opt;
        struct sock *parent = subflow->conn;
-       struct tcp_sock *tp = tcp_sk(sk);
 
        subflow->icsk_af_ops->sk_rx_dst_set(sk, skb);
 
@@ -230,6 +229,8 @@ static void subflow_finish_connect(struct sock *sk, const struct sk_buff *skb)
                return;
 
        subflow->conn_finished = 1;
+       subflow->ssn_offset = TCP_SKB_CB(skb)->seq;
+       pr_debug("subflow=%p synack seq=%x", subflow, subflow->ssn_offset);
 
        mptcp_get_options(skb, &mp_opt);
        if (subflow->request_mptcp && mp_opt.mp_capable) {
@@ -245,21 +246,20 @@ static void subflow_finish_connect(struct sock *sk, const struct sk_buff *skb)
                pr_debug("subflow=%p, thmac=%llu, remote_nonce=%u", subflow,
                         subflow->thmac, subflow->remote_nonce);
        } else {
-               tp->is_mptcp = 0;
+               if (subflow->request_mptcp)
+                       MPTCP_INC_STATS(sock_net(sk),
+                                       MPTCP_MIB_MPCAPABLEACTIVEFALLBACK);
+               mptcp_do_fallback(sk);
+               pr_fallback(mptcp_sk(subflow->conn));
        }
 
-       if (!tp->is_mptcp)
+       if (mptcp_check_fallback(sk))
                return;
 
        if (subflow->mp_capable) {
                pr_debug("subflow=%p, remote_key=%llu", mptcp_subflow_ctx(sk),
                         subflow->remote_key);
                mptcp_finish_connect(sk);
-
-               if (skb) {
-                       pr_debug("synack seq=%u", TCP_SKB_CB(skb)->seq);
-                       subflow->ssn_offset = TCP_SKB_CB(skb)->seq;
-               }
        } else if (subflow->mp_join) {
                u8 hmac[SHA256_DIGEST_SIZE];
 
@@ -279,9 +279,6 @@ static void subflow_finish_connect(struct sock *sk, const struct sk_buff *skb)
 
                memcpy(subflow->hmac, hmac, MPTCPOPT_HMAC_LEN);
 
-               if (skb)
-                       subflow->ssn_offset = TCP_SKB_CB(skb)->seq;
-
                if (!mptcp_finish_join(sk))
                        goto do_reset;
 
@@ -557,7 +554,8 @@ enum mapping_status {
        MAPPING_OK,
        MAPPING_INVALID,
        MAPPING_EMPTY,
-       MAPPING_DATA_FIN
+       MAPPING_DATA_FIN,
+       MAPPING_DUMMY
 };
 
 static u64 expand_seq(u64 old_seq, u16 old_data_len, u64 seq)
@@ -621,6 +619,9 @@ static enum mapping_status get_mapping_status(struct sock *ssk)
        if (!skb)
                return MAPPING_EMPTY;
 
+       if (mptcp_check_fallback(ssk))
+               return MAPPING_DUMMY;
+
        mpext = mptcp_get_ext(skb);
        if (!mpext || !mpext->use_map) {
                if (!subflow->map_valid && !skb->len) {
@@ -762,6 +763,16 @@ static bool subflow_check_data_avail(struct sock *ssk)
                        ssk->sk_err = EBADMSG;
                        goto fatal;
                }
+               if (status == MAPPING_DUMMY) {
+                       __mptcp_do_fallback(msk);
+                       skb = skb_peek(&ssk->sk_receive_queue);
+                       subflow->map_valid = 1;
+                       subflow->map_seq = READ_ONCE(msk->ack_seq);
+                       subflow->map_data_len = skb->len;
+                       subflow->map_subflow_seq = tcp_sk(ssk)->copied_seq -
+                                                  subflow->ssn_offset;
+                       return true;
+               }
 
                if (status != MAPPING_OK)
                        return false;
@@ -885,14 +896,18 @@ static void subflow_data_ready(struct sock *sk)
 {
        struct mptcp_subflow_context *subflow = mptcp_subflow_ctx(sk);
        struct sock *parent = subflow->conn;
+       struct mptcp_sock *msk;
 
-       if (!subflow->mp_capable && !subflow->mp_join) {
-               subflow->tcp_data_ready(sk);
-
+       msk = mptcp_sk(parent);
+       if (inet_sk_state_load(sk) == TCP_LISTEN) {
+               set_bit(MPTCP_DATA_READY, &msk->flags);
                parent->sk_data_ready(parent);
                return;
        }
 
+       WARN_ON_ONCE(!__mptcp_check_fallback(msk) && !subflow->mp_capable &&
+                    !subflow->mp_join);
+
        if (mptcp_subflow_data_available(sk))
                mptcp_data_ready(parent, sk);
 }
@@ -1117,7 +1132,7 @@ static void subflow_state_change(struct sock *sk)
         * a fin packet carrying a DSS can be unnoticed if we don't trigger
         * the data available machinery here.
         */
-       if (subflow->mp_capable && mptcp_subflow_data_available(sk))
+       if (mptcp_subflow_data_available(sk))
                mptcp_data_ready(parent, sk);
 
        if (!(parent->sk_shutdown & RCV_SHUTDOWN) &&