tcp: use tcp_flags in tcp_data_queue()
[firefly-linux-kernel-4.4.55.git] / net / ipv4 / tcp_input.c
index f97003ad0af55998df0be629f3c99cd093fd4e43..5073eefa6fae0caf4fd2bcd89c873279f77a2f36 100644 (file)
@@ -1295,9 +1295,9 @@ static bool tcp_shifted_skb(struct sock *sk, struct sk_buff *skb,
        TCP_SKB_CB(prev)->end_seq += shifted;
        TCP_SKB_CB(skb)->seq += shifted;
 
-       skb_shinfo(prev)->gso_segs += pcount;
-       BUG_ON(skb_shinfo(skb)->gso_segs < pcount);
-       skb_shinfo(skb)->gso_segs -= pcount;
+       tcp_skb_pcount_add(prev, pcount);
+       BUG_ON(tcp_skb_pcount(skb) < pcount);
+       tcp_skb_pcount_add(skb, -pcount);
 
        /* When we're adding to gso_segs == 1, gso_size will be zero,
         * in theory this shouldn't be necessary but as long as DSACK
@@ -1310,7 +1310,7 @@ static bool tcp_shifted_skb(struct sock *sk, struct sk_buff *skb,
        }
 
        /* CHECKME: To clear or not to clear? Mimics normal skb currently */
-       if (skb_shinfo(skb)->gso_segs <= 1) {
+       if (tcp_skb_pcount(skb) <= 1) {
                skb_shinfo(skb)->gso_size = 0;
                skb_shinfo(skb)->gso_type = 0;
        }
@@ -3208,9 +3208,10 @@ static void tcp_ack_probe(struct sock *sk)
                 * This function is not for random using!
                 */
        } else {
+               unsigned long when = inet_csk_rto_backoff(icsk, TCP_RTO_MAX);
+
                inet_csk_reset_xmit_timer(sk, ICSK_TIME_PROBE0,
-                                         min(icsk->icsk_rto << icsk->icsk_backoff, TCP_RTO_MAX),
-                                         TCP_RTO_MAX);
+                                         when, TCP_RTO_MAX);
        }
 }
 
@@ -4060,6 +4061,44 @@ static void tcp_sack_remove(struct tcp_sock *tp)
        tp->rx_opt.num_sacks = num_sacks;
 }
 
+/**
+ * tcp_try_coalesce - try to merge skb to prior one
+ * @sk: socket
+ * @to: prior buffer
+ * @from: buffer to add in queue
+ * @fragstolen: pointer to boolean
+ *
+ * Before queueing skb @from after @to, try to merge them
+ * to reduce overall memory use and queue lengths, if cost is small.
+ * Packets in ofo or receive queues can stay a long time.
+ * Better try to coalesce them right now to avoid future collapses.
+ * Returns true if caller should free @from instead of queueing it
+ */
+static bool tcp_try_coalesce(struct sock *sk,
+                            struct sk_buff *to,
+                            struct sk_buff *from,
+                            bool *fragstolen)
+{
+       int delta;
+
+       *fragstolen = false;
+
+       /* Its possible this segment overlaps with prior segment in queue */
+       if (TCP_SKB_CB(from)->seq != TCP_SKB_CB(to)->end_seq)
+               return false;
+
+       if (!skb_try_coalesce(to, from, fragstolen, &delta))
+               return false;
+
+       atomic_add(delta, &sk->sk_rmem_alloc);
+       sk_mem_charge(sk, delta);
+       NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPRCVCOALESCE);
+       TCP_SKB_CB(to)->end_seq = TCP_SKB_CB(from)->end_seq;
+       TCP_SKB_CB(to)->ack_seq = TCP_SKB_CB(from)->ack_seq;
+       TCP_SKB_CB(to)->tcp_flags |= TCP_SKB_CB(from)->tcp_flags;
+       return true;
+}
+
 /* This one checks to see if we can put data from the
  * out_of_order queue into the receive_queue.
  */
@@ -4067,7 +4106,8 @@ static void tcp_ofo_queue(struct sock *sk)
 {
        struct tcp_sock *tp = tcp_sk(sk);
        __u32 dsack_high = tp->rcv_nxt;
-       struct sk_buff *skb;
+       struct sk_buff *skb, *tail;
+       bool fragstolen, eaten;
 
        while ((skb = skb_peek(&tp->out_of_order_queue)) != NULL) {
                if (after(TCP_SKB_CB(skb)->seq, tp->rcv_nxt))
@@ -4080,9 +4120,9 @@ static void tcp_ofo_queue(struct sock *sk)
                        tcp_dsack_extend(sk, TCP_SKB_CB(skb)->seq, dsack);
                }
 
+               __skb_unlink(skb, &tp->out_of_order_queue);
                if (!after(TCP_SKB_CB(skb)->end_seq, tp->rcv_nxt)) {
                        SOCK_DEBUG(sk, "ofo packet was already received\n");
-                       __skb_unlink(skb, &tp->out_of_order_queue);
                        __kfree_skb(skb);
                        continue;
                }
@@ -4090,11 +4130,15 @@ static void tcp_ofo_queue(struct sock *sk)
                           tp->rcv_nxt, TCP_SKB_CB(skb)->seq,
                           TCP_SKB_CB(skb)->end_seq);
 
-               __skb_unlink(skb, &tp->out_of_order_queue);
-               __skb_queue_tail(&sk->sk_receive_queue, skb);
+               tail = skb_peek_tail(&sk->sk_receive_queue);
+               eaten = tail && tcp_try_coalesce(sk, tail, skb, &fragstolen);
                tp->rcv_nxt = TCP_SKB_CB(skb)->end_seq;
-               if (tcp_hdr(skb)->fin)
+               if (!eaten)
+                       __skb_queue_tail(&sk->sk_receive_queue, skb);
+               if (TCP_SKB_CB(skb)->tcp_flags & TCPHDR_FIN)
                        tcp_fin(sk);
+               if (eaten)
+                       kfree_skb_partial(skb, fragstolen);
        }
 }
 
@@ -4121,46 +4165,6 @@ static int tcp_try_rmem_schedule(struct sock *sk, struct sk_buff *skb,
        return 0;
 }
 
-/**
- * tcp_try_coalesce - try to merge skb to prior one
- * @sk: socket
- * @to: prior buffer
- * @from: buffer to add in queue
- * @fragstolen: pointer to boolean
- *
- * Before queueing skb @from after @to, try to merge them
- * to reduce overall memory use and queue lengths, if cost is small.
- * Packets in ofo or receive queues can stay a long time.
- * Better try to coalesce them right now to avoid future collapses.
- * Returns true if caller should free @from instead of queueing it
- */
-static bool tcp_try_coalesce(struct sock *sk,
-                            struct sk_buff *to,
-                            struct sk_buff *from,
-                            bool *fragstolen)
-{
-       int delta;
-
-       *fragstolen = false;
-
-       if (tcp_hdr(from)->fin)
-               return false;
-
-       /* Its possible this segment overlaps with prior segment in queue */
-       if (TCP_SKB_CB(from)->seq != TCP_SKB_CB(to)->end_seq)
-               return false;
-
-       if (!skb_try_coalesce(to, from, fragstolen, &delta))
-               return false;
-
-       atomic_add(delta, &sk->sk_rmem_alloc);
-       sk_mem_charge(sk, delta);
-       NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPRCVCOALESCE);
-       TCP_SKB_CB(to)->end_seq = TCP_SKB_CB(from)->end_seq;
-       TCP_SKB_CB(to)->ack_seq = TCP_SKB_CB(from)->ack_seq;
-       return true;
-}
-
 static void tcp_data_queue_ofo(struct sock *sk, struct sk_buff *skb)
 {
        struct tcp_sock *tp = tcp_sk(sk);
@@ -4306,24 +4310,19 @@ static int __must_check tcp_queue_rcv(struct sock *sk, struct sk_buff *skb, int
 
 int tcp_send_rcvq(struct sock *sk, struct msghdr *msg, size_t size)
 {
-       struct sk_buff *skb = NULL;
-       struct tcphdr *th;
+       struct sk_buff *skb;
        bool fragstolen;
 
        if (size == 0)
                return 0;
 
-       skb = alloc_skb(size + sizeof(*th), sk->sk_allocation);
+       skb = alloc_skb(size, sk->sk_allocation);
        if (!skb)
                goto err;
 
-       if (tcp_try_rmem_schedule(sk, skb, size + sizeof(*th)))
+       if (tcp_try_rmem_schedule(sk, skb, skb->truesize))
                goto err_free;
 
-       th = (struct tcphdr *)skb_put(skb, sizeof(*th));
-       skb_reset_transport_header(skb);
-       memset(th, 0, sizeof(*th));
-
        if (memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size))
                goto err_free;
 
@@ -4331,7 +4330,7 @@ int tcp_send_rcvq(struct sock *sk, struct msghdr *msg, size_t size)
        TCP_SKB_CB(skb)->end_seq = TCP_SKB_CB(skb)->seq + size;
        TCP_SKB_CB(skb)->ack_seq = tcp_sk(sk)->snd_una - 1;
 
-       if (tcp_queue_rcv(sk, skb, sizeof(*th), &fragstolen)) {
+       if (tcp_queue_rcv(sk, skb, 0, &fragstolen)) {
                WARN_ON_ONCE(fragstolen); /* should not happen */
                __kfree_skb(skb);
        }
@@ -4345,7 +4344,6 @@ err:
 
 static void tcp_data_queue(struct sock *sk, struct sk_buff *skb)
 {
-       const struct tcphdr *th = tcp_hdr(skb);
        struct tcp_sock *tp = tcp_sk(sk);
        int eaten = -1;
        bool fragstolen = false;
@@ -4354,7 +4352,7 @@ static void tcp_data_queue(struct sock *sk, struct sk_buff *skb)
                goto drop;
 
        skb_dst_drop(skb);
-       __skb_pull(skb, th->doff * 4);
+       __skb_pull(skb, tcp_hdr(skb)->doff * 4);
 
        TCP_ECN_accept_cwr(tp, skb);
 
@@ -4398,7 +4396,7 @@ queue_and_out:
                tp->rcv_nxt = TCP_SKB_CB(skb)->end_seq;
                if (skb->len)
                        tcp_event_data_recv(sk, skb);
-               if (th->fin)
+               if (TCP_SKB_CB(skb)->tcp_flags & TCPHDR_FIN)
                        tcp_fin(sk);
 
                if (!skb_queue_empty(&tp->out_of_order_queue)) {
@@ -4513,7 +4511,7 @@ restart:
                 * - bloated or contains data before "start" or
                 *   overlaps to the next one.
                 */
-               if (!tcp_hdr(skb)->syn && !tcp_hdr(skb)->fin &&
+               if (!(TCP_SKB_CB(skb)->tcp_flags & (TCPHDR_SYN | TCPHDR_FIN)) &&
                    (tcp_win_from_space(skb->truesize) > skb->len ||
                     before(TCP_SKB_CB(skb)->seq, start))) {
                        end_of_skbs = false;
@@ -4532,30 +4530,18 @@ restart:
                /* Decided to skip this, advance start seq. */
                start = TCP_SKB_CB(skb)->end_seq;
        }
-       if (end_of_skbs || tcp_hdr(skb)->syn || tcp_hdr(skb)->fin)
+       if (end_of_skbs ||
+           (TCP_SKB_CB(skb)->tcp_flags & (TCPHDR_SYN | TCPHDR_FIN)))
                return;
 
        while (before(start, end)) {
+               int copy = min_t(int, SKB_MAX_ORDER(0, 0), end - start);
                struct sk_buff *nskb;
-               unsigned int header = skb_headroom(skb);
-               int copy = SKB_MAX_ORDER(header, 0);
 
-               /* Too big header? This can happen with IPv6. */
-               if (copy < 0)
-                       return;
-               if (end - start < copy)
-                       copy = end - start;
-               nskb = alloc_skb(copy + header, GFP_ATOMIC);
+               nskb = alloc_skb(copy, GFP_ATOMIC);
                if (!nskb)
                        return;
 
-               skb_set_mac_header(nskb, skb_mac_header(skb) - skb->head);
-               skb_set_network_header(nskb, (skb_network_header(skb) -
-                                             skb->head));
-               skb_set_transport_header(nskb, (skb_transport_header(skb) -
-                                               skb->head));
-               skb_reserve(nskb, header);
-               memcpy(nskb->head, skb->head, header);
                memcpy(nskb->cb, skb->cb, sizeof(skb->cb));
                TCP_SKB_CB(nskb)->seq = TCP_SKB_CB(nskb)->end_seq = start;
                __skb_queue_before(list, skb, nskb);
@@ -4579,8 +4565,7 @@ restart:
                                skb = tcp_collapse_one(sk, skb, list);
                                if (!skb ||
                                    skb == tail ||
-                                   tcp_hdr(skb)->syn ||
-                                   tcp_hdr(skb)->fin)
+                                   (TCP_SKB_CB(skb)->tcp_flags & (TCPHDR_SYN | TCPHDR_FIN)))
                                        return;
                        }
                }