tcp: fix tcp_grow_window() for large incoming frames
[pandora-kernel.git] / net / ipv4 / tcp_input.c
index b5e315f..3ff3640 100644 (file)
@@ -61,6 +61,8 @@
  *             Pasi Sarolahti:         F-RTO for dealing with spurious RTOs
  */
 
+#define pr_fmt(fmt) "TCP: " fmt
+
 #include <linux/mm.h>
 #include <linux/slab.h>
 #include <linux/module.h>
@@ -333,6 +335,7 @@ static void tcp_grow_window(struct sock *sk, const struct sk_buff *skb)
                        incr = __tcp_grow_window(sk, skb);
 
                if (incr) {
+                       incr = max_t(int, incr, 2 * skb->len);
                        tp->rcv_ssthresh = min(tp->rcv_ssthresh + incr,
                                               tp->window_clamp);
                        inet_csk(sk)->icsk_ack.quick |= 1;
@@ -472,8 +475,11 @@ static void tcp_rcv_rtt_update(struct tcp_sock *tp, u32 sample, int win_dep)
                if (!win_dep) {
                        m -= (new_sample >> 3);
                        new_sample += m;
-               } else if (m < new_sample)
-                       new_sample = m << 3;
+               } else {
+                       m <<= 3;
+                       if (m < new_sample)
+                               new_sample = m;
+               }
        } else {
                /* No previous measure. */
                new_sample = m << 3;
@@ -3867,9 +3873,9 @@ void tcp_parse_options(const struct sk_buff *skb, struct tcp_options_received *o
                                        opt_rx->wscale_ok = 1;
                                        if (snd_wscale > 14) {
                                                if (net_ratelimit())
-                                                       printk(KERN_INFO "tcp_parse_options: Illegal window "
-                                                              "scaling value %d >14 received.\n",
-                                                              snd_wscale);
+                                                       pr_info("%s: Illegal window scaling value %d >14 received\n",
+                                                               __func__,
+                                                               snd_wscale);
                                                snd_wscale = 14;
                                        }
                                        opt_rx->snd_wscale = snd_wscale;
@@ -4191,7 +4197,7 @@ static void tcp_fin(struct sock *sk)
                /* Only TCP_LISTEN and TCP_CLOSE are left, in these
                 * cases we should never reach this piece of code.
                 */
-               printk(KERN_ERR "%s: Impossible, sk->sk_state=%d\n",
+               pr_err("%s: Impossible, sk->sk_state=%d\n",
                       __func__, sk->sk_state);
                break;
        }
@@ -4444,6 +4450,137 @@ static inline int tcp_try_rmem_schedule(struct sock *sk, unsigned int size)
        return 0;
 }
 
+static void tcp_data_queue_ofo(struct sock *sk, struct sk_buff *skb)
+{
+       struct tcp_sock *tp = tcp_sk(sk);
+       struct sk_buff *skb1;
+       u32 seq, end_seq;
+
+       TCP_ECN_check_ce(tp, skb);
+
+       if (tcp_try_rmem_schedule(sk, skb->truesize)) {
+               /* TODO: should increment a counter */
+               __kfree_skb(skb);
+               return;
+       }
+
+       /* Disable header prediction. */
+       tp->pred_flags = 0;
+       inet_csk_schedule_ack(sk);
+
+       SOCK_DEBUG(sk, "out of order segment: rcv_next %X seq %X - %X\n",
+                  tp->rcv_nxt, TCP_SKB_CB(skb)->seq, TCP_SKB_CB(skb)->end_seq);
+
+       skb1 = skb_peek_tail(&tp->out_of_order_queue);
+       if (!skb1) {
+               /* Initial out of order segment, build 1 SACK. */
+               if (tcp_is_sack(tp)) {
+                       tp->rx_opt.num_sacks = 1;
+                       tp->selective_acks[0].start_seq = TCP_SKB_CB(skb)->seq;
+                       tp->selective_acks[0].end_seq =
+                                               TCP_SKB_CB(skb)->end_seq;
+               }
+               __skb_queue_head(&tp->out_of_order_queue, skb);
+               goto end;
+       }
+
+       seq = TCP_SKB_CB(skb)->seq;
+       end_seq = TCP_SKB_CB(skb)->end_seq;
+
+       if (seq == TCP_SKB_CB(skb1)->end_seq) {
+               /* Packets in ofo can stay in queue a long time.
+                * Better try to coalesce them right now
+                * to avoid future tcp_collapse_ofo_queue(),
+                * probably the most expensive function in tcp stack.
+                */
+               if (skb->len <= skb_tailroom(skb1) && !tcp_hdr(skb)->fin) {
+                       NET_INC_STATS_BH(sock_net(sk),
+                                        LINUX_MIB_TCPRCVCOALESCE);
+                       BUG_ON(skb_copy_bits(skb, 0,
+                                            skb_put(skb1, skb->len),
+                                            skb->len));
+                       TCP_SKB_CB(skb1)->end_seq = end_seq;
+                       TCP_SKB_CB(skb1)->ack_seq = TCP_SKB_CB(skb)->ack_seq;
+                       __kfree_skb(skb);
+                       skb = NULL;
+               } else {
+                       __skb_queue_after(&tp->out_of_order_queue, skb1, skb);
+               }
+
+               if (!tp->rx_opt.num_sacks ||
+                   tp->selective_acks[0].end_seq != seq)
+                       goto add_sack;
+
+               /* Common case: data arrive in order after hole. */
+               tp->selective_acks[0].end_seq = end_seq;
+               goto end;
+       }
+
+       /* Find place to insert this segment. */
+       while (1) {
+               if (!after(TCP_SKB_CB(skb1)->seq, seq))
+                       break;
+               if (skb_queue_is_first(&tp->out_of_order_queue, skb1)) {
+                       skb1 = NULL;
+                       break;
+               }
+               skb1 = skb_queue_prev(&tp->out_of_order_queue, skb1);
+       }
+
+       /* Do skb overlap to previous one? */
+       if (skb1 && before(seq, TCP_SKB_CB(skb1)->end_seq)) {
+               if (!after(end_seq, TCP_SKB_CB(skb1)->end_seq)) {
+                       /* All the bits are present. Drop. */
+                       __kfree_skb(skb);
+                       skb = NULL;
+                       tcp_dsack_set(sk, seq, end_seq);
+                       goto add_sack;
+               }
+               if (after(seq, TCP_SKB_CB(skb1)->seq)) {
+                       /* Partial overlap. */
+                       tcp_dsack_set(sk, seq,
+                                     TCP_SKB_CB(skb1)->end_seq);
+               } else {
+                       if (skb_queue_is_first(&tp->out_of_order_queue,
+                                              skb1))
+                               skb1 = NULL;
+                       else
+                               skb1 = skb_queue_prev(
+                                       &tp->out_of_order_queue,
+                                       skb1);
+               }
+       }
+       if (!skb1)
+               __skb_queue_head(&tp->out_of_order_queue, skb);
+       else
+               __skb_queue_after(&tp->out_of_order_queue, skb1, skb);
+
+       /* And clean segments covered by new one as whole. */
+       while (!skb_queue_is_last(&tp->out_of_order_queue, skb)) {
+               skb1 = skb_queue_next(&tp->out_of_order_queue, skb);
+
+               if (!after(end_seq, TCP_SKB_CB(skb1)->seq))
+                       break;
+               if (before(end_seq, TCP_SKB_CB(skb1)->end_seq)) {
+                       tcp_dsack_extend(sk, TCP_SKB_CB(skb1)->seq,
+                                        end_seq);
+                       break;
+               }
+               __skb_unlink(skb1, &tp->out_of_order_queue);
+               tcp_dsack_extend(sk, TCP_SKB_CB(skb1)->seq,
+                                TCP_SKB_CB(skb1)->end_seq);
+               __kfree_skb(skb1);
+       }
+
+add_sack:
+       if (tcp_is_sack(tp))
+               tcp_sack_new_ofo_skb(sk, seq, end_seq);
+end:
+       if (skb)
+               skb_set_owner_r(skb, sk);
+}
+
+
 static void tcp_data_queue(struct sock *sk, struct sk_buff *skb)
 {
        const struct tcphdr *th = tcp_hdr(skb);
@@ -4559,105 +4696,7 @@ drop:
                goto queue_and_out;
        }
 
-       TCP_ECN_check_ce(tp, skb);
-
-       if (tcp_try_rmem_schedule(sk, skb->truesize))
-               goto drop;
-
-       /* Disable header prediction. */
-       tp->pred_flags = 0;
-       inet_csk_schedule_ack(sk);
-
-       SOCK_DEBUG(sk, "out of order segment: rcv_next %X seq %X - %X\n",
-                  tp->rcv_nxt, TCP_SKB_CB(skb)->seq, TCP_SKB_CB(skb)->end_seq);
-
-       skb_set_owner_r(skb, sk);
-
-       if (!skb_peek(&tp->out_of_order_queue)) {
-               /* Initial out of order segment, build 1 SACK. */
-               if (tcp_is_sack(tp)) {
-                       tp->rx_opt.num_sacks = 1;
-                       tp->selective_acks[0].start_seq = TCP_SKB_CB(skb)->seq;
-                       tp->selective_acks[0].end_seq =
-                                               TCP_SKB_CB(skb)->end_seq;
-               }
-               __skb_queue_head(&tp->out_of_order_queue, skb);
-       } else {
-               struct sk_buff *skb1 = skb_peek_tail(&tp->out_of_order_queue);
-               u32 seq = TCP_SKB_CB(skb)->seq;
-               u32 end_seq = TCP_SKB_CB(skb)->end_seq;
-
-               if (seq == TCP_SKB_CB(skb1)->end_seq) {
-                       __skb_queue_after(&tp->out_of_order_queue, skb1, skb);
-
-                       if (!tp->rx_opt.num_sacks ||
-                           tp->selective_acks[0].end_seq != seq)
-                               goto add_sack;
-
-                       /* Common case: data arrive in order after hole. */
-                       tp->selective_acks[0].end_seq = end_seq;
-                       return;
-               }
-
-               /* Find place to insert this segment. */
-               while (1) {
-                       if (!after(TCP_SKB_CB(skb1)->seq, seq))
-                               break;
-                       if (skb_queue_is_first(&tp->out_of_order_queue, skb1)) {
-                               skb1 = NULL;
-                               break;
-                       }
-                       skb1 = skb_queue_prev(&tp->out_of_order_queue, skb1);
-               }
-
-               /* Do skb overlap to previous one? */
-               if (skb1 && before(seq, TCP_SKB_CB(skb1)->end_seq)) {
-                       if (!after(end_seq, TCP_SKB_CB(skb1)->end_seq)) {
-                               /* All the bits are present. Drop. */
-                               __kfree_skb(skb);
-                               tcp_dsack_set(sk, seq, end_seq);
-                               goto add_sack;
-                       }
-                       if (after(seq, TCP_SKB_CB(skb1)->seq)) {
-                               /* Partial overlap. */
-                               tcp_dsack_set(sk, seq,
-                                             TCP_SKB_CB(skb1)->end_seq);
-                       } else {
-                               if (skb_queue_is_first(&tp->out_of_order_queue,
-                                                      skb1))
-                                       skb1 = NULL;
-                               else
-                                       skb1 = skb_queue_prev(
-                                               &tp->out_of_order_queue,
-                                               skb1);
-                       }
-               }
-               if (!skb1)
-                       __skb_queue_head(&tp->out_of_order_queue, skb);
-               else
-                       __skb_queue_after(&tp->out_of_order_queue, skb1, skb);
-
-               /* And clean segments covered by new one as whole. */
-               while (!skb_queue_is_last(&tp->out_of_order_queue, skb)) {
-                       skb1 = skb_queue_next(&tp->out_of_order_queue, skb);
-
-                       if (!after(end_seq, TCP_SKB_CB(skb1)->seq))
-                               break;
-                       if (before(end_seq, TCP_SKB_CB(skb1)->end_seq)) {
-                               tcp_dsack_extend(sk, TCP_SKB_CB(skb1)->seq,
-                                                end_seq);
-                               break;
-                       }
-                       __skb_unlink(skb1, &tp->out_of_order_queue);
-                       tcp_dsack_extend(sk, TCP_SKB_CB(skb1)->seq,
-                                        TCP_SKB_CB(skb1)->end_seq);
-                       __kfree_skb(skb1);
-               }
-
-add_sack:
-               if (tcp_is_sack(tp))
-                       tcp_sack_new_ofo_skb(sk, seq, end_seq);
-       }
+       tcp_data_queue_ofo(sk, skb);
 }
 
 static struct sk_buff *tcp_collapse_one(struct sock *sk, struct sk_buff *skb,
@@ -5190,7 +5229,7 @@ static int tcp_dma_try_early_copy(struct sock *sk, struct sk_buff *skb,
                return 0;
 
        if (!tp->ucopy.dma_chan && tp->ucopy.pinned_list)
-               tp->ucopy.dma_chan = dma_find_channel(DMA_MEMCPY);
+               tp->ucopy.dma_chan = net_dma_find_channel();
 
        if (tp->ucopy.dma_chan && skb_csum_unnecessary(skb)) {