]> nv-tegra.nvidia Code Review - linux-2.6.git/blobdiff - net/ipv4/tcp_output.c
tcp: fix tcp_trim_head() to adjust segment count with skb MSS
[linux-2.6.git] / net / ipv4 / tcp_output.c
index c08e06d44428e776edec32cb2fa669b37fd87069..faf257b94154af56272650ff269de53c3eed2239 100644 (file)
@@ -37,6 +37,7 @@
 #include <net/tcp.h>
 
 #include <linux/compiler.h>
+#include <linux/gfp.h>
 #include <linux/module.h>
 
 /* People can turn this off for buggy TCP's found in printers etc. */
@@ -54,12 +55,13 @@ int sysctl_tcp_workaround_signed_windows __read_mostly = 0;
 int sysctl_tcp_tso_win_divisor __read_mostly = 3;
 
 int sysctl_tcp_mtu_probing __read_mostly = 0;
-int sysctl_tcp_base_mss __read_mostly = 512;
+int sysctl_tcp_base_mss __read_mostly = TCP_BASE_MSS;
 
 /* By default, RFC2861 behavior.  */
 int sysctl_tcp_slow_start_after_idle __read_mostly = 1;
 
 int sysctl_tcp_cookie_size __read_mostly = 0; /* TCP_COOKIE_MAX */
+EXPORT_SYMBOL_GPL(sysctl_tcp_cookie_size);
 
 
 /* Account for new data that has been sent to the network. */
@@ -71,7 +73,7 @@ static void tcp_event_new_data_sent(struct sock *sk, struct sk_buff *skb)
        tcp_advance_send_head(sk, skb);
        tp->snd_nxt = TCP_SKB_CB(skb)->end_seq;
 
-       /* Don't override Nagle indefinately with F-RTO */
+       /* Don't override Nagle indefinitely with F-RTO */
        if (tp->frto_counter == 2)
                tp->frto_counter = 3;
 
@@ -117,9 +119,13 @@ static __u16 tcp_advertise_mss(struct sock *sk)
        struct dst_entry *dst = __sk_dst_get(sk);
        int mss = tp->advmss;
 
-       if (dst && dst_metric(dst, RTAX_ADVMSS) < mss) {
-               mss = dst_metric(dst, RTAX_ADVMSS);
-               tp->advmss = mss;
+       if (dst) {
+               unsigned int metric = dst_metric_advmss(dst);
+
+               if (metric < mss) {
+                       mss = metric;
+                       tp->advmss = mss;
+               }
        }
 
        return (__u16)mss;
@@ -182,7 +188,8 @@ static inline void tcp_event_ack_sent(struct sock *sk, unsigned int pkts)
  */
 void tcp_select_initial_window(int __space, __u32 mss,
                               __u32 *rcv_wnd, __u32 *window_clamp,
-                              int wscale_ok, __u8 *rcv_wscale)
+                              int wscale_ok, __u8 *rcv_wscale,
+                              __u32 init_rcv_wnd)
 {
        unsigned int space = (__space < 0 ? 0 : __space);
 
@@ -221,23 +228,28 @@ void tcp_select_initial_window(int __space, __u32 mss,
                }
        }
 
-       /* Set initial window to value enough for senders,
-        * following RFC2414. Senders, not following this RFC,
-        * will be satisfied with 2.
+       /* Set initial window to a value enough for senders starting with
+        * initial congestion window of TCP_DEFAULT_INIT_RCVWND. Place
+        * a limit on the initial window when mss is larger than 1460.
         */
        if (mss > (1 << *rcv_wscale)) {
-               int init_cwnd = 4;
-               if (mss > 1460 * 3)
-                       init_cwnd = 2;
-               else if (mss > 1460)
-                       init_cwnd = 3;
-               if (*rcv_wnd > init_cwnd * mss)
-                       *rcv_wnd = init_cwnd * mss;
+               int init_cwnd = TCP_DEFAULT_INIT_RCVWND;
+               if (mss > 1460)
+                       init_cwnd =
+                       max_t(u32, (1460 * TCP_DEFAULT_INIT_RCVWND) / mss, 2);
+               /* when initializing use the value from init_rcv_wnd
+                * rather than the default from above
+                */
+               if (init_rcv_wnd)
+                       *rcv_wnd = min(*rcv_wnd, init_rcv_wnd * mss);
+               else
+                       *rcv_wnd = min(*rcv_wnd, init_cwnd * mss);
        }
 
        /* Set the clamp no higher than max representable value */
        (*window_clamp) = min(65535U << (*rcv_wscale), *window_clamp);
 }
+EXPORT_SYMBOL(tcp_select_initial_window);
 
 /* Chose a new window to advertise, update state in tcp_sock for the
  * socket, and return result with RFC1323 scaling applied.  The return
@@ -285,9 +297,9 @@ static u16 tcp_select_window(struct sock *sk)
 /* Packet ECN state for a SYN-ACK */
 static inline void TCP_ECN_send_synack(struct tcp_sock *tp, struct sk_buff *skb)
 {
-       TCP_SKB_CB(skb)->flags &= ~TCPCB_FLAG_CWR;
+       TCP_SKB_CB(skb)->flags &= ~TCPHDR_CWR;
        if (!(tp->ecn_flags & TCP_ECN_OK))
-               TCP_SKB_CB(skb)->flags &= ~TCPCB_FLAG_ECE;
+               TCP_SKB_CB(skb)->flags &= ~TCPHDR_ECE;
 }
 
 /* Packet ECN state for a SYN.  */
@@ -297,7 +309,7 @@ static inline void TCP_ECN_send_syn(struct sock *sk, struct sk_buff *skb)
 
        tp->ecn_flags = 0;
        if (sysctl_tcp_ecn == 1) {
-               TCP_SKB_CB(skb)->flags |= TCPCB_FLAG_ECE | TCPCB_FLAG_CWR;
+               TCP_SKB_CB(skb)->flags |= TCPHDR_ECE | TCPHDR_CWR;
                tp->ecn_flags = TCP_ECN_OK;
        }
 }
@@ -341,6 +353,7 @@ static inline void TCP_ECN_send(struct sock *sk, struct sk_buff *skb,
  */
 static void tcp_init_nondata_skb(struct sk_buff *skb, u32 seq, u8 flags)
 {
+       skb->ip_summed = CHECKSUM_PARTIAL;
        skb->csum = 0;
 
        TCP_SKB_CB(skb)->flags = flags;
@@ -351,7 +364,7 @@ static void tcp_init_nondata_skb(struct sk_buff *skb, u32 seq, u8 flags)
        skb_shinfo(skb)->gso_type = 0;
 
        TCP_SKB_CB(skb)->seq = seq;
-       if (flags & (TCPCB_FLAG_SYN | TCPCB_FLAG_FIN))
+       if (flags & (TCPHDR_SYN | TCPHDR_FIN))
                seq++;
        TCP_SKB_CB(skb)->end_seq = seq;
 }
@@ -381,27 +394,30 @@ struct tcp_out_options {
  */
 static u8 tcp_cookie_size_check(u8 desired)
 {
-       if (desired > 0) {
+       int cookie_size;
+
+       if (desired > 0)
                /* previously specified */
                return desired;
-       }
-       if (sysctl_tcp_cookie_size <= 0) {
+
+       cookie_size = ACCESS_ONCE(sysctl_tcp_cookie_size);
+       if (cookie_size <= 0)
                /* no default specified */
                return 0;
-       }
-       if (sysctl_tcp_cookie_size <= TCP_COOKIE_MIN) {
+
+       if (cookie_size <= TCP_COOKIE_MIN)
                /* value too small, specify minimum */
                return TCP_COOKIE_MIN;
-       }
-       if (sysctl_tcp_cookie_size >= TCP_COOKIE_MAX) {
+
+       if (cookie_size >= TCP_COOKIE_MAX)
                /* value too large, specify maximum */
                return TCP_COOKIE_MAX;
-       }
-       if (0x1 & sysctl_tcp_cookie_size) {
+
+       if (cookie_size & 1)
                /* 8-bit multiple, illegal, fix it */
-               return (u8)(sysctl_tcp_cookie_size + 0x1);
-       }
-       return (u8)sysctl_tcp_cookie_size;
+               cookie_size++;
+
+       return (u8)cookie_size;
 }
 
 /* Write previously computed TCP options to the packet.
@@ -552,7 +568,6 @@ static unsigned tcp_syn_options(struct sock *sk, struct sk_buff *skb,
                                struct tcp_md5sig_key **md5) {
        struct tcp_sock *tp = tcp_sk(sk);
        struct tcp_cookie_values *cvp = tp->cookie_values;
-       struct dst_entry *dst = __sk_dst_get(sk);
        unsigned remaining = MAX_TCP_OPTION_SPACE;
        u8 cookie_size = (!tp->rx_opt.cookie_out_never && cvp != NULL) ?
                         tcp_cookie_size_check(cvp->cookie_desired) :
@@ -580,22 +595,18 @@ static unsigned tcp_syn_options(struct sock *sk, struct sk_buff *skb,
        opts->mss = tcp_advertise_mss(sk);
        remaining -= TCPOLEN_MSS_ALIGNED;
 
-       if (likely(sysctl_tcp_timestamps &&
-                  !dst_feature(dst, RTAX_FEATURE_NO_TSTAMP) &&
-                  *md5 == NULL)) {
+       if (likely(sysctl_tcp_timestamps && *md5 == NULL)) {
                opts->options |= OPTION_TS;
                opts->tsval = TCP_SKB_CB(skb)->when;
                opts->tsecr = tp->rx_opt.ts_recent;
                remaining -= TCPOLEN_TSTAMP_ALIGNED;
        }
-       if (likely(sysctl_tcp_window_scaling &&
-                  !dst_feature(dst, RTAX_FEATURE_NO_WSCALE))) {
+       if (likely(sysctl_tcp_window_scaling)) {
                opts->ws = tp->rx_opt.rcv_wscale;
                opts->options |= OPTION_WSCALE;
                remaining -= TCPOLEN_WSCALE_ALIGNED;
        }
-       if (likely(sysctl_tcp_sack &&
-                  !dst_feature(dst, RTAX_FEATURE_NO_SACK))) {
+       if (likely(sysctl_tcp_sack)) {
                opts->options |= OPTION_SACK_ADVERTISE;
                if (unlikely(!(OPTION_TS & opts->options)))
                        remaining -= TCPOLEN_SACKPERM_ALIGNED;
@@ -663,7 +674,6 @@ static unsigned tcp_synack_options(struct sock *sk,
        u8 cookie_plus = (xvp != NULL && !xvp->cookie_out_never) ?
                         xvp->cookie_plus :
                         0;
-       bool doing_ts = ireq->tstamp_ok;
 
 #ifdef CONFIG_TCP_MD5SIG
        *md5 = tcp_rsk(req)->af_specific->md5_lookup(sk, req);
@@ -676,7 +686,7 @@ static unsigned tcp_synack_options(struct sock *sk,
                 * rather than TS in order to fit in better with old,
                 * buggy kernels, but that was deemed to be unnecessary.
                 */
-               doing_ts &= !ireq->sack_ok;
+               ireq->tstamp_ok &= !ireq->sack_ok;
        }
 #else
        *md5 = NULL;
@@ -691,7 +701,7 @@ static unsigned tcp_synack_options(struct sock *sk,
                opts->options |= OPTION_WSCALE;
                remaining -= TCPOLEN_WSCALE_ALIGNED;
        }
-       if (likely(doing_ts)) {
+       if (likely(ireq->tstamp_ok)) {
                opts->options |= OPTION_TS;
                opts->tsval = TCP_SKB_CB(skb)->when;
                opts->tsecr = req->ts_recent;
@@ -699,7 +709,7 @@ static unsigned tcp_synack_options(struct sock *sk,
        }
        if (likely(ireq->sack_ok)) {
                opts->options |= OPTION_SACK_ADVERTISE;
-               if (unlikely(!doing_ts))
+               if (unlikely(!ireq->tstamp_ok))
                        remaining -= TCPOLEN_SACKPERM_ALIGNED;
        }
 
@@ -707,7 +717,7 @@ static unsigned tcp_synack_options(struct sock *sk,
         * If the <SYN> options fit, the same options should fit now!
         */
        if (*md5 == NULL &&
-           doing_ts &&
+           ireq->tstamp_ok &&
            cookie_plus > TCPOLEN_COOKIE_BASE) {
                int need = cookie_plus; /* has TCPOLEN_COOKIE_BASE */
 
@@ -816,15 +826,18 @@ static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it,
        tcb = TCP_SKB_CB(skb);
        memset(&opts, 0, sizeof(opts));
 
-       if (unlikely(tcb->flags & TCPCB_FLAG_SYN))
+       if (unlikely(tcb->flags & TCPHDR_SYN))
                tcp_options_size = tcp_syn_options(sk, skb, &opts, &md5);
        else
                tcp_options_size = tcp_established_options(sk, skb, &opts,
                                                           &md5);
        tcp_header_size = tcp_options_size + sizeof(struct tcphdr);
 
-       if (tcp_packets_in_flight(tp) == 0)
+       if (tcp_packets_in_flight(tp) == 0) {
                tcp_ca_event(sk, CA_EVENT_TX_START);
+               skb->ooo_okay = 1;
+       } else
+               skb->ooo_okay = 0;
 
        skb_push(skb, tcp_header_size);
        skb_reset_transport_header(skb);
@@ -839,7 +852,7 @@ static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it,
        *(((__be16 *)th) + 6)   = htons(((tcp_header_size >> 2) << 12) |
                                        tcb->flags);
 
-       if (unlikely(tcb->flags & TCPCB_FLAG_SYN)) {
+       if (unlikely(tcb->flags & TCPHDR_SYN)) {
                /* RFC1323: The window in SYN & SYN/ACK segments
                 * is never scaled.
                 */
@@ -856,36 +869,37 @@ static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it,
                        th->urg_ptr = htons(tp->snd_up - tcb->seq);
                        th->urg = 1;
                } else if (after(tcb->seq + 0xFFFF, tp->snd_nxt)) {
-                       th->urg_ptr = 0xFFFF;
+                       th->urg_ptr = htons(0xFFFF);
                        th->urg = 1;
                }
        }
 
        tcp_options_write((__be32 *)(th + 1), tp, &opts);
-       if (likely((tcb->flags & TCPCB_FLAG_SYN) == 0))
+       if (likely((tcb->flags & TCPHDR_SYN) == 0))
                TCP_ECN_send(sk, skb, tcp_header_size);
 
 #ifdef CONFIG_TCP_MD5SIG
        /* Calculate the MD5 hash, as we have all we need now */
        if (md5) {
-               sk->sk_route_caps &= ~NETIF_F_GSO_MASK;
+               sk_nocaps_add(sk, NETIF_F_GSO_MASK);
                tp->af_specific->calc_md5_hash(opts.hash_location,
                                               md5, sk, NULL, skb);
        }
 #endif
 
-       icsk->icsk_af_ops->send_check(sk, skb->len, skb);
+       icsk->icsk_af_ops->send_check(sk, skb);
 
-       if (likely(tcb->flags & TCPCB_FLAG_ACK))
+       if (likely(tcb->flags & TCPHDR_ACK))
                tcp_event_ack_sent(sk, tcp_skb_pcount(skb));
 
        if (skb->len != tcp_header_size)
                tcp_event_data_sent(tp, skb, sk);
 
        if (after(tcb->end_seq, tp->snd_nxt) || tcb->seq == tcb->end_seq)
-               TCP_INC_STATS(sock_net(sk), TCP_MIB_OUTSEGS);
+               TCP_ADD_STATS(sock_net(sk), TCP_MIB_OUTSEGS,
+                             tcp_skb_pcount(skb));
 
-       err = icsk->icsk_af_ops->queue_xmit(skb, 0);
+       err = icsk->icsk_af_ops->queue_xmit(skb, &inet->cork.fl);
        if (likely(err <= 0))
                return err;
 
@@ -989,7 +1003,8 @@ int tcp_fragment(struct sock *sk, struct sk_buff *skb, u32 len,
        int nlen;
        u8 flags;
 
-       BUG_ON(len > skb->len);
+       if (WARN_ON(len > skb->len))
+               return -EINVAL;
 
        nsize = skb_headlen(skb) - len;
        if (nsize < 0)
@@ -1018,7 +1033,7 @@ int tcp_fragment(struct sock *sk, struct sk_buff *skb, u32 len,
 
        /* PSH and FIN should only be set in the second packet. */
        flags = TCP_SKB_CB(skb)->flags;
-       TCP_SKB_CB(skb)->flags = flags & ~(TCPCB_FLAG_FIN | TCPCB_FLAG_PSH);
+       TCP_SKB_CB(skb)->flags = flags & ~(TCPHDR_FIN | TCPHDR_PSH);
        TCP_SKB_CB(buff)->flags = flags;
        TCP_SKB_CB(buff)->sacked = TCP_SKB_CB(skb)->sacked;
 
@@ -1119,11 +1134,9 @@ int tcp_trim_head(struct sock *sk, struct sk_buff *skb, u32 len)
        sk_mem_uncharge(sk, len);
        sock_set_flag(sk, SOCK_QUEUE_SHRUNK);
 
-       /* Any change of skb->len requires recalculation of tso
-        * factor and mss.
-        */
+       /* Any change of skb->len requires recalculation of tso factor. */
        if (tcp_skb_pcount(skb) > 1)
-               tcp_set_skb_tso_segs(sk, skb, tcp_current_mss(sk));
+               tcp_set_skb_tso_segs(sk, skb, tcp_skb_mss(skb));
 
        return 0;
 }
@@ -1184,6 +1197,7 @@ void tcp_mtup_init(struct sock *sk)
        icsk->icsk_mtup.search_low = tcp_mss_to_mtu(sk, sysctl_tcp_base_mss);
        icsk->icsk_mtup.probe_size = 0;
 }
+EXPORT_SYMBOL(tcp_mtup_init);
 
 /* This function synchronize snd mss to current pmtu/exthdr set.
 
@@ -1227,6 +1241,7 @@ unsigned int tcp_sync_mss(struct sock *sk, u32 pmtu)
 
        return mss_now;
 }
+EXPORT_SYMBOL(tcp_sync_mss);
 
 /* Compute the current effective MSS, taking SACKs and IP options,
  * and even PMTU discovery events into account.
@@ -1323,8 +1338,7 @@ static inline unsigned int tcp_cwnd_test(struct tcp_sock *tp,
        u32 in_flight, cwnd;
 
        /* Don't be strict about the congestion window for the final FIN.  */
-       if ((TCP_SKB_CB(skb)->flags & TCPCB_FLAG_FIN) &&
-           tcp_skb_pcount(skb) == 1)
+       if ((TCP_SKB_CB(skb)->flags & TCPHDR_FIN) && tcp_skb_pcount(skb) == 1)
                return 1;
 
        in_flight = tcp_packets_in_flight(tp);
@@ -1335,7 +1349,7 @@ static inline unsigned int tcp_cwnd_test(struct tcp_sock *tp,
        return 0;
 }
 
-/* Intialize TSO state of a skb.
+/* Initialize TSO state of a skb.
  * This must be invoked the first time we consider transmitting
  * SKB onto the wire.
  */
@@ -1369,9 +1383,9 @@ static inline int tcp_nagle_check(const struct tcp_sock *tp,
                                  const struct sk_buff *skb,
                                  unsigned mss_now, int nonagle)
 {
-       return (skb->len < mss_now &&
+       return skb->len < mss_now &&
                ((nonagle & TCP_NAGLE_CORK) ||
-                (!nonagle && tp->packets_out && tcp_minshall_check(tp))));
+                (!nonagle && tp->packets_out && tcp_minshall_check(tp)));
 }
 
 /* Return non-zero if the Nagle test allows this packet to be
@@ -1393,7 +1407,7 @@ static inline int tcp_nagle_test(struct tcp_sock *tp, struct sk_buff *skb,
         * Nagle can be ignored during F-RTO too (see RFC4138).
         */
        if (tcp_urg_mode(tp) || (tp->frto_counter == 2) ||
-           (TCP_SKB_CB(skb)->flags & TCPCB_FLAG_FIN))
+           (TCP_SKB_CB(skb)->flags & TCPHDR_FIN))
                return 1;
 
        if (!tcp_nagle_check(tp, skb, cur_mss, nonagle))
@@ -1442,10 +1456,10 @@ int tcp_may_send_now(struct sock *sk)
        struct tcp_sock *tp = tcp_sk(sk);
        struct sk_buff *skb = tcp_send_head(sk);
 
-       return (skb &&
+       return skb &&
                tcp_snd_test(sk, skb, tcp_current_mss(sk),
                             (tcp_skb_is_last(sk, skb) ?
-                             tp->nonagle : TCP_NAGLE_PUSH)));
+                             tp->nonagle : TCP_NAGLE_PUSH));
 }
 
 /* Trim TSO SKB to LEN bytes, put the remaining data into a new packet
@@ -1456,7 +1470,7 @@ int tcp_may_send_now(struct sock *sk)
  * packet has never been sent out before (and thus is not cloned).
  */
 static int tso_fragment(struct sock *sk, struct sk_buff *skb, unsigned int len,
-                       unsigned int mss_now)
+                       unsigned int mss_now, gfp_t gfp)
 {
        struct sk_buff *buff;
        int nlen = skb->len - len;
@@ -1466,7 +1480,7 @@ static int tso_fragment(struct sock *sk, struct sk_buff *skb, unsigned int len,
        if (skb->len != skb->data_len)
                return tcp_fragment(sk, skb, len, mss_now);
 
-       buff = sk_stream_alloc_skb(sk, 0, GFP_ATOMIC);
+       buff = sk_stream_alloc_skb(sk, 0, gfp);
        if (unlikely(buff == NULL))
                return -ENOMEM;
 
@@ -1482,7 +1496,7 @@ static int tso_fragment(struct sock *sk, struct sk_buff *skb, unsigned int len,
 
        /* PSH and FIN should only be set in the second packet. */
        flags = TCP_SKB_CB(skb)->flags;
-       TCP_SKB_CB(skb)->flags = flags & ~(TCPCB_FLAG_FIN | TCPCB_FLAG_PSH);
+       TCP_SKB_CB(skb)->flags = flags & ~(TCPHDR_FIN | TCPHDR_PSH);
        TCP_SKB_CB(buff)->flags = flags;
 
        /* This packet was never sent out yet, so no SACK bits. */
@@ -1512,8 +1526,9 @@ static int tcp_tso_should_defer(struct sock *sk, struct sk_buff *skb)
        struct tcp_sock *tp = tcp_sk(sk);
        const struct inet_connection_sock *icsk = inet_csk(sk);
        u32 send_win, cong_win, limit, in_flight;
+       int win_divisor;
 
-       if (TCP_SKB_CB(skb)->flags & TCPCB_FLAG_FIN)
+       if (TCP_SKB_CB(skb)->flags & TCPHDR_FIN)
                goto send_now;
 
        if (icsk->icsk_ca_state != TCP_CA_Open)
@@ -1543,13 +1558,14 @@ static int tcp_tso_should_defer(struct sock *sk, struct sk_buff *skb)
        if ((skb != tcp_write_queue_tail(sk)) && (limit >= skb->len))
                goto send_now;
 
-       if (sysctl_tcp_tso_win_divisor) {
+       win_divisor = ACCESS_ONCE(sysctl_tcp_tso_win_divisor);
+       if (win_divisor) {
                u32 chunk = min(tp->snd_wnd, tp->snd_cwnd * tp->mss_cache);
 
                /* If at least some fraction of a window is available,
                 * just use it.
                 */
-               chunk /= sysctl_tcp_tso_win_divisor;
+               chunk /= win_divisor;
                if (limit >= chunk)
                        goto send_now;
        } else {
@@ -1639,7 +1655,7 @@ static int tcp_mtu_probe(struct sock *sk)
 
        TCP_SKB_CB(nskb)->seq = TCP_SKB_CB(skb)->seq;
        TCP_SKB_CB(nskb)->end_seq = TCP_SKB_CB(skb)->seq + probe_size;
-       TCP_SKB_CB(nskb)->flags = TCPCB_FLAG_ACK;
+       TCP_SKB_CB(nskb)->flags = TCPHDR_ACK;
        TCP_SKB_CB(nskb)->sacked = 0;
        nskb->csum = 0;
        nskb->ip_summed = skb->ip_summed;
@@ -1664,7 +1680,7 @@ static int tcp_mtu_probe(struct sock *sk)
                        sk_wmem_free_skb(sk, skb);
                } else {
                        TCP_SKB_CB(nskb)->flags |= TCP_SKB_CB(skb)->flags &
-                                                  ~(TCPCB_FLAG_FIN|TCPCB_FLAG_PSH);
+                                                  ~(TCPHDR_FIN|TCPHDR_PSH);
                        if (!skb_shinfo(skb)->nr_frags) {
                                skb_pull(skb, copy);
                                if (skb->ip_summed != CHECKSUM_PARTIAL)
@@ -1764,7 +1780,7 @@ static int tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle,
                                                    cwnd_quota);
 
                if (skb->len > limit &&
-                   unlikely(tso_fragment(sk, skb, limit, mss_now)))
+                   unlikely(tso_fragment(sk, skb, limit, mss_now, gfp)))
                        break;
 
                TCP_SKB_CB(skb)->when = tcp_time_stamp;
@@ -1798,11 +1814,6 @@ static int tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle,
 void __tcp_push_pending_frames(struct sock *sk, unsigned int cur_mss,
                               int nonagle)
 {
-       struct sk_buff *skb = tcp_send_head(sk);
-
-       if (!skb)
-               return;
-
        /* If we are closed, the bytes will have to remain here.
         * In time closedown will finish, we empty the write queue and
         * all will be happy.
@@ -2020,7 +2031,7 @@ static void tcp_retrans_try_collapse(struct sock *sk, struct sk_buff *to,
 
        if (!sysctl_tcp_retrans_collapse)
                return;
-       if (TCP_SKB_CB(skb)->flags & TCPCB_FLAG_SYN)
+       if (TCP_SKB_CB(skb)->flags & TCPHDR_SYN)
                return;
 
        tcp_for_write_queue_from_safe(skb, tmp, sk) {
@@ -2112,7 +2123,7 @@ int tcp_retransmit_skb(struct sock *sk, struct sk_buff *skb)
         * since it is cheap to do so and saves bytes on the network.
         */
        if (skb->len > 0 &&
-           (TCP_SKB_CB(skb)->flags & TCPCB_FLAG_FIN) &&
+           (TCP_SKB_CB(skb)->flags & TCPHDR_FIN) &&
            tp->snd_una == (TCP_SKB_CB(skb)->end_seq - 1)) {
                if (!pskb_trim(skb, 0)) {
                        /* Reuse, even though it does some unnecessary work */
@@ -2150,7 +2161,7 @@ int tcp_retransmit_skb(struct sock *sk, struct sk_buff *skb)
                if (!tp->retrans_stamp)
                        tp->retrans_stamp = TCP_SKB_CB(skb)->when;
 
-               tp->undo_retrans++;
+               tp->undo_retrans += tcp_skb_pcount(skb);
 
                /* snd_nxt is stored to detect loss of retransmitted segment,
                 * see tcp_input.c tcp_sacktag_write_queue().
@@ -2208,6 +2219,9 @@ void tcp_xmit_retransmit_queue(struct sock *sk)
        int mib_idx;
        int fwd_rexmitting = 0;
 
+       if (!tp->packets_out)
+               return;
+
        if (!tp->lost_out)
                tp->retransmit_high = tp->snd_una;
 
@@ -2301,7 +2315,7 @@ void tcp_send_fin(struct sock *sk)
        mss_now = tcp_current_mss(sk);
 
        if (tcp_send_head(sk) != NULL) {
-               TCP_SKB_CB(skb)->flags |= TCPCB_FLAG_FIN;
+               TCP_SKB_CB(skb)->flags |= TCPHDR_FIN;
                TCP_SKB_CB(skb)->end_seq++;
                tp->write_seq++;
        } else {
@@ -2318,7 +2332,7 @@ void tcp_send_fin(struct sock *sk)
                skb_reserve(skb, MAX_TCP_HEADER);
                /* FIN eats a sequence byte, write_seq advanced by tcp_queue_skb(). */
                tcp_init_nondata_skb(skb, tp->write_seq,
-                                    TCPCB_FLAG_ACK | TCPCB_FLAG_FIN);
+                                    TCPHDR_ACK | TCPHDR_FIN);
                tcp_queue_skb(sk, skb);
        }
        __tcp_push_pending_frames(sk, mss_now, TCP_NAGLE_OFF);
@@ -2343,7 +2357,7 @@ void tcp_send_active_reset(struct sock *sk, gfp_t priority)
        /* Reserve space for headers and prepare control bits. */
        skb_reserve(skb, MAX_TCP_HEADER);
        tcp_init_nondata_skb(skb, tcp_acceptable_seq(sk),
-                            TCPCB_FLAG_ACK | TCPCB_FLAG_RST);
+                            TCPHDR_ACK | TCPHDR_RST);
        /* Send it off. */
        TCP_SKB_CB(skb)->when = tcp_time_stamp;
        if (tcp_transmit_skb(sk, skb, 0, priority))
@@ -2363,11 +2377,11 @@ int tcp_send_synack(struct sock *sk)
        struct sk_buff *skb;
 
        skb = tcp_write_queue_head(sk);
-       if (skb == NULL || !(TCP_SKB_CB(skb)->flags & TCPCB_FLAG_SYN)) {
+       if (skb == NULL || !(TCP_SKB_CB(skb)->flags & TCPHDR_SYN)) {
                printk(KERN_DEBUG "tcp_send_synack: wrong queue state\n");
                return -EFAULT;
        }
-       if (!(TCP_SKB_CB(skb)->flags & TCPCB_FLAG_ACK)) {
+       if (!(TCP_SKB_CB(skb)->flags & TCPHDR_ACK)) {
                if (skb_cloned(skb)) {
                        struct sk_buff *nskb = skb_copy(skb, GFP_ATOMIC);
                        if (nskb == NULL)
@@ -2381,7 +2395,7 @@ int tcp_send_synack(struct sock *sk)
                        skb = nskb;
                }
 
-               TCP_SKB_CB(skb)->flags |= TCPCB_FLAG_ACK;
+               TCP_SKB_CB(skb)->flags |= TCPHDR_ACK;
                TCP_ECN_send_synack(tcp_sk(sk), skb);
        }
        TCP_SKB_CB(skb)->when = tcp_time_stamp;
@@ -2397,13 +2411,17 @@ struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst,
        struct tcp_extend_values *xvp = tcp_xv(rvp);
        struct inet_request_sock *ireq = inet_rsk(req);
        struct tcp_sock *tp = tcp_sk(sk);
+       const struct tcp_cookie_values *cvp = tp->cookie_values;
        struct tcphdr *th;
        struct sk_buff *skb;
        struct tcp_md5sig_key *md5;
        int tcp_header_size;
        int mss;
+       int s_data_desired = 0;
 
-       skb = sock_wmalloc(sk, MAX_TCP_HEADER + 15, 1, GFP_ATOMIC);
+       if (cvp != NULL && cvp->s_data_constant && cvp->s_data_desired)
+               s_data_desired = cvp->s_data_desired;
+       skb = sock_wmalloc(sk, MAX_TCP_HEADER + 15 + s_data_desired, 1, GFP_ATOMIC);
        if (skb == NULL)
                return NULL;
 
@@ -2412,7 +2430,7 @@ struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst,
 
        skb_dst_set(skb, dst_clone(dst));
 
-       mss = dst_metric(dst, RTAX_ADVMSS);
+       mss = dst_metric_advmss(dst);
        if (tp->rx_opt.user_mss && tp->rx_opt.user_mss < mss)
                mss = tp->rx_opt.user_mss;
 
@@ -2420,13 +2438,20 @@ struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst,
                __u8 rcv_wscale;
                /* Set this up on the first call only */
                req->window_clamp = tp->window_clamp ? : dst_metric(dst, RTAX_WINDOW);
+
+               /* limit the window selection if the user enforce a smaller rx buffer */
+               if (sk->sk_userlocks & SOCK_RCVBUF_LOCK &&
+                   (req->window_clamp > tcp_full_space(sk) || req->window_clamp == 0))
+                       req->window_clamp = tcp_full_space(sk);
+
                /* tcp_full_space because it is guaranteed to be the first packet */
                tcp_select_initial_window(tcp_full_space(sk),
                        mss - (ireq->tstamp_ok ? TCPOLEN_TSTAMP_ALIGNED : 0),
                        &req->rcv_wnd,
                        &req->window_clamp,
                        ireq->wscale_ok,
-                       &rcv_wscale);
+                       &rcv_wscale,
+                       dst_metric(dst, RTAX_INITRWND));
                ireq->rcv_wscale = rcv_wscale;
        }
 
@@ -2455,19 +2480,15 @@ struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst,
         * not even correctly set)
         */
        tcp_init_nondata_skb(skb, tcp_rsk(req)->snt_isn,
-                            TCPCB_FLAG_SYN | TCPCB_FLAG_ACK);
+                            TCPHDR_SYN | TCPHDR_ACK);
 
        if (OPTION_COOKIE_EXTENSION & opts.options) {
-               const struct tcp_cookie_values *cvp = tp->cookie_values;
-
-               if (cvp != NULL &&
-                   cvp->s_data_constant &&
-                   cvp->s_data_desired > 0) {
-                       u8 *buf = skb_put(skb, cvp->s_data_desired);
+               if (s_data_desired) {
+                       u8 *buf = skb_put(skb, s_data_desired);
 
                        /* copy data directly from the listening socket. */
-                       memcpy(buf, cvp->s_data_payload, cvp->s_data_desired);
-                       TCP_SKB_CB(skb)->end_seq += cvp->s_data_desired;
+                       memcpy(buf, cvp->s_data_payload, s_data_desired);
+                       TCP_SKB_CB(skb)->end_seq += s_data_desired;
                }
 
                if (opts.hash_size > 0) {
@@ -2484,7 +2505,7 @@ struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst,
                        *tail-- ^= TCP_SKB_CB(skb)->seq + 1;
 
                        /* recommended */
-                       *tail-- ^= ((th->dest << 16) | th->source);
+                       *tail-- ^= (((__force u32)th->dest << 16) | (__force u32)th->source);
                        *tail-- ^= (u32)(unsigned long)cvp; /* per sockopt */
 
                        sha_transform((__u32 *)&xvp->cookie_bakery[0],
@@ -2502,7 +2523,7 @@ struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst,
        th->window = htons(min(req->rcv_wnd, 65535U));
        tcp_options_write((__be32 *)(th + 1), tp, &opts);
        th->doff = (tcp_header_size >> 2);
-       TCP_INC_STATS(sock_net(sk), TCP_MIB_OUTSEGS);
+       TCP_ADD_STATS(sock_net(sk), TCP_MIB_OUTSEGS, tcp_skb_pcount(skb));
 
 #ifdef CONFIG_TCP_MD5SIG
        /* Okay, we have all we need - do the md5 hash if needed */
@@ -2514,6 +2535,7 @@ struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst,
 
        return skb;
 }
+EXPORT_SYMBOL(tcp_make_synack);
 
 /* Do all connect socket setups that can be done AF independent. */
 static void tcp_connect_init(struct sock *sk)
@@ -2526,9 +2548,7 @@ static void tcp_connect_init(struct sock *sk)
         * See tcp_input.c:tcp_rcv_state_process case TCP_SYN_SENT.
         */
        tp->tcp_header_len = sizeof(struct tcphdr) +
-               (sysctl_tcp_timestamps &&
-               (!dst_feature(dst, RTAX_FEATURE_NO_TSTAMP) ?
-                 TCPOLEN_TSTAMP_ALIGNED : 0));
+               (sysctl_tcp_timestamps ? TCPOLEN_TSTAMP_ALIGNED : 0);
 
 #ifdef CONFIG_TCP_MD5SIG
        if (tp->af_specific->md5_lookup(sk, sk) != NULL)
@@ -2544,19 +2564,24 @@ static void tcp_connect_init(struct sock *sk)
 
        if (!tp->window_clamp)
                tp->window_clamp = dst_metric(dst, RTAX_WINDOW);
-       tp->advmss = dst_metric(dst, RTAX_ADVMSS);
+       tp->advmss = dst_metric_advmss(dst);
        if (tp->rx_opt.user_mss && tp->rx_opt.user_mss < tp->advmss)
                tp->advmss = tp->rx_opt.user_mss;
 
        tcp_initialize_rcv_mss(sk);
 
+       /* limit the window selection if the user enforce a smaller rx buffer */
+       if (sk->sk_userlocks & SOCK_RCVBUF_LOCK &&
+           (tp->window_clamp > tcp_full_space(sk) || tp->window_clamp == 0))
+               tp->window_clamp = tcp_full_space(sk);
+
        tcp_select_initial_window(tcp_full_space(sk),
                                  tp->advmss - (tp->rx_opt.ts_recent_stamp ? tp->tcp_header_len - sizeof(struct tcphdr) : 0),
                                  &tp->rcv_wnd,
                                  &tp->window_clamp,
-                                 (sysctl_tcp_window_scaling &&
-                                  !dst_feature(dst, RTAX_FEATURE_NO_WSCALE)),
-                                 &rcv_wscale);
+                                 sysctl_tcp_window_scaling,
+                                 &rcv_wscale,
+                                 dst_metric(dst, RTAX_INITRWND));
 
        tp->rx_opt.rcv_wscale = rcv_wscale;
        tp->rcv_ssthresh = tp->rcv_wnd;
@@ -2582,6 +2607,7 @@ int tcp_connect(struct sock *sk)
 {
        struct tcp_sock *tp = tcp_sk(sk);
        struct sk_buff *buff;
+       int err;
 
        tcp_connect_init(sk);
 
@@ -2593,7 +2619,7 @@ int tcp_connect(struct sock *sk)
        skb_reserve(buff, MAX_TCP_HEADER);
 
        tp->snd_nxt = tp->write_seq;
-       tcp_init_nondata_skb(buff, tp->write_seq++, TCPCB_FLAG_SYN);
+       tcp_init_nondata_skb(buff, tp->write_seq++, TCPHDR_SYN);
        TCP_ECN_send_syn(sk, buff);
 
        /* Send it off. */
@@ -2604,7 +2630,9 @@ int tcp_connect(struct sock *sk)
        sk->sk_wmem_queued += buff->truesize;
        sk_mem_charge(sk, buff->truesize);
        tp->packets_out += tcp_skb_pcount(buff);
-       tcp_transmit_skb(sk, buff, 1, sk->sk_allocation);
+       err = tcp_transmit_skb(sk, buff, 1, sk->sk_allocation);
+       if (err == -ECONNREFUSED)
+               return err;
 
        /* We change tp->snd_nxt after the tcp_transmit_skb() call
         * in order to make this packet get counted in tcpOutSegs.
@@ -2618,6 +2646,7 @@ int tcp_connect(struct sock *sk)
                                  inet_csk(sk)->icsk_rto, TCP_RTO_MAX);
        return 0;
 }
+EXPORT_SYMBOL(tcp_connect);
 
 /* Send out a delayed ack, the caller does the policy checking
  * to see if we should even be here.  See tcp_input.c:tcp_ack_snd_check()
@@ -2699,7 +2728,7 @@ void tcp_send_ack(struct sock *sk)
 
        /* Reserve space for headers and prepare control bits. */
        skb_reserve(buff, MAX_TCP_HEADER);
-       tcp_init_nondata_skb(buff, tcp_acceptable_seq(sk), TCPCB_FLAG_ACK);
+       tcp_init_nondata_skb(buff, tcp_acceptable_seq(sk), TCPHDR_ACK);
 
        /* Send it off, this clears delayed acks for us. */
        TCP_SKB_CB(buff)->when = tcp_time_stamp;
@@ -2733,7 +2762,7 @@ static int tcp_xmit_probe_skb(struct sock *sk, int urgent)
         * end to send an ack.  Don't queue or clone SKB, just
         * send it.
         */
-       tcp_init_nondata_skb(skb, tp->snd_una - !urgent, TCPCB_FLAG_ACK);
+       tcp_init_nondata_skb(skb, tp->snd_una - !urgent, TCPHDR_ACK);
        TCP_SKB_CB(skb)->when = tcp_time_stamp;
        return tcp_transmit_skb(sk, skb, 0, GFP_ATOMIC);
 }
@@ -2763,13 +2792,13 @@ int tcp_write_wakeup(struct sock *sk)
                if (seg_size < TCP_SKB_CB(skb)->end_seq - TCP_SKB_CB(skb)->seq ||
                    skb->len > mss) {
                        seg_size = min(seg_size, mss);
-                       TCP_SKB_CB(skb)->flags |= TCPCB_FLAG_PSH;
+                       TCP_SKB_CB(skb)->flags |= TCPHDR_PSH;
                        if (tcp_fragment(sk, skb, seg_size, mss))
                                return -1;
                } else if (!tcp_skb_pcount(skb))
                        tcp_set_skb_tso_segs(sk, skb, mss);
 
-               TCP_SKB_CB(skb)->flags |= TCPCB_FLAG_PSH;
+               TCP_SKB_CB(skb)->flags |= TCPHDR_PSH;
                TCP_SKB_CB(skb)->when = tcp_time_stamp;
                err = tcp_transmit_skb(sk, skb, 1, GFP_ATOMIC);
                if (!err)
@@ -2822,10 +2851,3 @@ void tcp_send_probe0(struct sock *sk)
                                          TCP_RTO_MAX);
        }
 }
-
-EXPORT_SYMBOL(tcp_select_initial_window);
-EXPORT_SYMBOL(tcp_connect);
-EXPORT_SYMBOL(tcp_make_synack);
-EXPORT_SYMBOL(tcp_simple_retransmit);
-EXPORT_SYMBOL(tcp_sync_mss);
-EXPORT_SYMBOL(tcp_mtup_init);