diff options
| author | Eric Dumazet <edumazet@google.com> | 2013-10-25 20:26:17 -0400 |
|---|---|---|
| committer | David S. Miller <davem@davemloft.net> | 2013-10-29 00:04:47 -0400 |
| commit | 0d08c42cf9a71530fef5ebcfe368f38f2dd0476f (patch) | |
| tree | d806d557fb1411add3dddbe6e59ff580afb7e05b | |
| parent | fc59d5bdf1e3dca0336d155e55d812286db075ad (diff) | |
tcp: gso: fix truesize tracking
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>
| -rw-r--r-- | net/ipv4/tcp_offload.c | 13 |
1 files changed, 5 insertions, 8 deletions
diff --git a/net/ipv4/tcp_offload.c b/net/ipv4/tcp_offload.c index 3a7525e6c086..533c58a5cfb7 100644 --- a/net/ipv4/tcp_offload.c +++ b/net/ipv4/tcp_offload.c | |||
| @@ -18,6 +18,7 @@ struct sk_buff *tcp_tso_segment(struct sk_buff *skb, | |||
| 18 | netdev_features_t features) | 18 | netdev_features_t features) |
| 19 | { | 19 | { |
| 20 | struct sk_buff *segs = ERR_PTR(-EINVAL); | 20 | struct sk_buff *segs = ERR_PTR(-EINVAL); |
| 21 | unsigned int sum_truesize = 0; | ||
| 21 | struct tcphdr *th; | 22 | struct tcphdr *th; |
| 22 | unsigned int thlen; | 23 | unsigned int thlen; |
| 23 | unsigned int seq; | 24 | unsigned int seq; |
| @@ -102,13 +103,7 @@ struct sk_buff *tcp_tso_segment(struct sk_buff *skb, | |||
| 102 | if (copy_destructor) { | 103 | if (copy_destructor) { |
| 103 | skb->destructor = gso_skb->destructor; | 104 | skb->destructor = gso_skb->destructor; |
| 104 | skb->sk = gso_skb->sk; | 105 | skb->sk = gso_skb->sk; |
| 105 | /* {tcp|sock}_wfree() use exact truesize accounting : | 106 | sum_truesize += skb->truesize; |
| 106 | * sum(skb->truesize) MUST be exactly be gso_skb->truesize | ||
| 107 | * So we account mss bytes of 'true size' for each segment. | ||
| 108 | * The last segment will contain the remaining. | ||
| 109 | */ | ||
| 110 | skb->truesize = mss; | ||
| 111 | gso_skb->truesize -= mss; | ||
| 112 | } | 107 | } |
| 113 | skb = skb->next; | 108 | skb = skb->next; |
| 114 | th = tcp_hdr(skb); | 109 | th = tcp_hdr(skb); |
| @@ -125,7 +120,9 @@ struct sk_buff *tcp_tso_segment(struct sk_buff *skb, | |||
| 125 | if (copy_destructor) { | 120 | if (copy_destructor) { |
| 126 | swap(gso_skb->sk, skb->sk); | 121 | swap(gso_skb->sk, skb->sk); |
| 127 | swap(gso_skb->destructor, skb->destructor); | 122 | swap(gso_skb->destructor, skb->destructor); |
| 128 | swap(gso_skb->truesize, skb->truesize); | 123 | sum_truesize += skb->truesize; |
| 124 | atomic_add(sum_truesize - gso_skb->truesize, | ||
| 125 | &skb->sk->sk_wmem_alloc); | ||
| 129 | } | 126 | } |
| 130 | 127 | ||
| 131 | delta = htonl(oldlen + (skb_tail_pointer(skb) - | 128 | delta = htonl(oldlen + (skb_tail_pointer(skb) - |
