]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - net/ipv4/tcp_output.c
Merge tag 'juno-fix-5.6' of git://git.kernel.org/pub/scm/linux/kernel/git/sudeep...
[linux.git] / net / ipv4 / tcp_output.c
index 58c92a7d671c54564479db061dcec186a8a7bb34..306e25d743e8de1bfe23d6e3b3a9fb0f23664912 100644 (file)
@@ -38,6 +38,7 @@
 #define pr_fmt(fmt) "TCP: " fmt
 
 #include <net/tcp.h>
+#include <net/mptcp.h>
 
 #include <linux/compiler.h>
 #include <linux/gfp.h>
@@ -414,6 +415,7 @@ static inline bool tcp_urg_mode(const struct tcp_sock *tp)
 #define OPTION_WSCALE          (1 << 3)
 #define OPTION_FAST_OPEN_COOKIE        (1 << 8)
 #define OPTION_SMC             (1 << 9)
+#define OPTION_MPTCP           (1 << 10)
 
 static void smc_options_write(__be32 *ptr, u16 *options)
 {
@@ -439,8 +441,17 @@ struct tcp_out_options {
        __u8 *hash_location;    /* temporary pointer, overloaded */
        __u32 tsval, tsecr;     /* need to include OPTION_TS */
        struct tcp_fastopen_cookie *fastopen_cookie;    /* Fast open cookie */
+       struct mptcp_out_options mptcp;
 };
 
+static void mptcp_options_write(__be32 *ptr, struct tcp_out_options *opts)
+{
+#if IS_ENABLED(CONFIG_MPTCP)
+       if (unlikely(OPTION_MPTCP & opts->options))
+               mptcp_write_options(ptr, &opts->mptcp);
+#endif
+}
+
 /* Write previously computed TCP options to the packet.
  *
  * Beware: Something in the Internet is very sensitive to the ordering of
@@ -549,6 +560,8 @@ static void tcp_options_write(__be32 *ptr, struct tcp_sock *tp,
        }
 
        smc_options_write(ptr, &options);
+
+       mptcp_options_write(ptr, opts);
 }
 
 static void smc_set_option(const struct tcp_sock *tp,
@@ -584,6 +597,22 @@ static void smc_set_option_cond(const struct tcp_sock *tp,
 #endif
 }
 
+static void mptcp_set_option_cond(const struct request_sock *req,
+                                 struct tcp_out_options *opts,
+                                 unsigned int *remaining)
+{
+       if (rsk_is_mptcp(req)) {
+               unsigned int size;
+
+               if (mptcp_synack_options(req, &size, &opts->mptcp)) {
+                       if (*remaining >= size) {
+                               opts->options |= OPTION_MPTCP;
+                               *remaining -= size;
+                       }
+               }
+       }
+}
+
 /* Compute TCP options for SYN packets. This is not the final
  * network wire format yet.
  */
@@ -653,6 +682,15 @@ static unsigned int tcp_syn_options(struct sock *sk, struct sk_buff *skb,
 
        smc_set_option(tp, opts, &remaining);
 
+       if (sk_is_mptcp(sk)) {
+               unsigned int size;
+
+               if (mptcp_syn_options(sk, skb, &size, &opts->mptcp)) {
+                       opts->options |= OPTION_MPTCP;
+                       remaining -= size;
+               }
+       }
+
        return MAX_TCP_OPTION_SPACE - remaining;
 }
 
@@ -714,6 +752,8 @@ static unsigned int tcp_synack_options(const struct sock *sk,
                }
        }
 
+       mptcp_set_option_cond(req, opts, &remaining);
+
        smc_set_option_cond(tcp_sk(sk), ireq, opts, &remaining);
 
        return MAX_TCP_OPTION_SPACE - remaining;
@@ -751,16 +791,37 @@ static unsigned int tcp_established_options(struct sock *sk, struct sk_buff *skb
                size += TCPOLEN_TSTAMP_ALIGNED;
        }
 
+       /* MPTCP options have precedence over SACK for the limited TCP
+        * option space because a MPTCP connection would be forced to
+        * fall back to regular TCP if a required multipath option is
+        * missing. SACK still gets a chance to use whatever space is
+        * left.
+        */
+       if (sk_is_mptcp(sk)) {
+               unsigned int remaining = MAX_TCP_OPTION_SPACE - size;
+               unsigned int opt_size = 0;
+
+               if (mptcp_established_options(sk, skb, &opt_size, remaining,
+                                             &opts->mptcp)) {
+                       opts->options |= OPTION_MPTCP;
+                       size += opt_size;
+               }
+       }
+
        eff_sacks = tp->rx_opt.num_sacks + tp->rx_opt.dsack;
        if (unlikely(eff_sacks)) {
                const unsigned int remaining = MAX_TCP_OPTION_SPACE - size;
+               if (unlikely(remaining < TCPOLEN_SACK_BASE_ALIGNED +
+                                        TCPOLEN_SACK_PERBLOCK))
+                       return size;
+
                opts->num_sack_blocks =
                        min_t(unsigned int, eff_sacks,
                              (remaining - TCPOLEN_SACK_BASE_ALIGNED) /
                              TCPOLEN_SACK_PERBLOCK);
-               if (likely(opts->num_sack_blocks))
-                       size += TCPOLEN_SACK_BASE_ALIGNED +
-                               opts->num_sack_blocks * TCPOLEN_SACK_PERBLOCK;
+
+               size += TCPOLEN_SACK_BASE_ALIGNED +
+                       opts->num_sack_blocks * TCPOLEN_SACK_PERBLOCK;
        }
 
        return size;
@@ -2865,7 +2926,7 @@ static void tcp_retrans_try_collapse(struct sock *sk, struct sk_buff *to,
                if (!tcp_can_collapse(sk, skb))
                        break;
 
-               if (!tcp_skb_can_collapse_to(to))
+               if (!tcp_skb_can_collapse(to, skb))
                        break;
 
                space -= skb->len;
@@ -3232,6 +3293,7 @@ int tcp_send_synack(struct sock *sk)
                        if (!nskb)
                                return -ENOMEM;
                        INIT_LIST_HEAD(&nskb->tcp_tsorted_anchor);
+                       tcp_highest_sack_replace(sk, skb, nskb);
                        tcp_rtx_queue_unlink_and_free(skb, sk);
                        __skb_header_release(nskb);
                        tcp_rbtree_insert(&sk->tcp_rtx_queue, nskb);
@@ -3368,8 +3430,8 @@ static void tcp_ca_dst_init(struct sock *sk, const struct dst_entry *dst)
 
        rcu_read_lock();
        ca = tcp_ca_find_key(ca_key);
-       if (likely(ca && try_module_get(ca->owner))) {
-               module_put(icsk->icsk_ca_ops->owner);
+       if (likely(ca && bpf_try_module_get(ca, ca->owner))) {
+               bpf_module_put(icsk->icsk_ca_ops, icsk->icsk_ca_ops->owner);
                icsk->icsk_ca_dst_locked = tcp_ca_dst_locked(dst);
                icsk->icsk_ca_ops = ca;
        }