tcp: gso: fix truesize tracking
authorEric Dumazet <edumazet@google.com>
Sat, 26 Oct 2013 00:26:17 +0000 (17:26 -0700)
committerDavid S. Miller <davem@davemloft.net>
Tue, 29 Oct 2013 04:04:47 +0000 (00:04 -0400)
commit 6ff50cd55545 ("tcp: gso: do not generate out of order packets")
had an heuristic that can trigger a warning in skb_try_coalesce(),
because skb->truesize of the gso segments were exactly set to mss.

This breaks the requirement that

skb->truesize >= skb->len + truesizeof(struct sk_buff);

It can trivially be reproduced by :

ifconfig lo mtu 1500
ethtool -K lo tso off
netperf

As the skbs are looped into the TCP networking stack, skb_try_coalesce()
warns us of these skb under-estimating their truesize.

Signed-off-by: Eric Dumazet <edumazet@google.com>
Reported-by: Alexei Starovoitov <ast@plumgrid.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/ipv4/tcp_offload.c

index 3a7525e6c08633dad9424bc1a9cb13bf26dfec1e..533c58a5cfb7a9cf9f4f2b1842533916b20b39da 100644 (file)
@@ -18,6 +18,7 @@ struct sk_buff *tcp_tso_segment(struct sk_buff *skb,
                                netdev_features_t features)
 {
        struct sk_buff *segs = ERR_PTR(-EINVAL);
+       unsigned int sum_truesize = 0;
        struct tcphdr *th;
        unsigned int thlen;
        unsigned int seq;
@@ -102,13 +103,7 @@ struct sk_buff *tcp_tso_segment(struct sk_buff *skb,
                if (copy_destructor) {
                        skb->destructor = gso_skb->destructor;
                        skb->sk = gso_skb->sk;
-                       /* {tcp|sock}_wfree() use exact truesize accounting :
-                        * sum(skb->truesize) MUST be exactly be gso_skb->truesize
-                        * So we account mss bytes of 'true size' for each segment.
-                        * The last segment will contain the remaining.
-                        */
-                       skb->truesize = mss;
-                       gso_skb->truesize -= mss;
+                       sum_truesize += skb->truesize;
                }
                skb = skb->next;
                th = tcp_hdr(skb);
@@ -125,7 +120,9 @@ struct sk_buff *tcp_tso_segment(struct sk_buff *skb,
        if (copy_destructor) {
                swap(gso_skb->sk, skb->sk);
                swap(gso_skb->destructor, skb->destructor);
-               swap(gso_skb->truesize, skb->truesize);
+               sum_truesize += skb->truesize;
+               atomic_add(sum_truesize - gso_skb->truesize,
+                          &skb->sk->sk_wmem_alloc);
        }
 
        delta = htonl(oldlen + (skb_tail_pointer(skb) -