| From 934c8b7de7c0632cf0dbaa5d9c9dcdcdc23cb33c Mon Sep 17 00:00:00 2001 |
| From: Eric Dumazet <edumazet@google.com> |
| Date: Fri, 25 Oct 2013 17:26:17 -0700 |
| Subject: tcp: gso: fix truesize tracking |
| |
| From: Eric Dumazet <edumazet@google.com> |
| |
| [ Upstream commit 0d08c42cf9a71530fef5ebcfe368f38f2dd0476f ] |
| |
| 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> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| net/ipv4/tcp.c | 13 +++++-------- |
| 1 file changed, 5 insertions(+), 8 deletions(-) |
| |
| --- a/net/ipv4/tcp.c |
| +++ b/net/ipv4/tcp.c |
| @@ -2899,6 +2899,7 @@ struct sk_buff *tcp_tso_segment(struct s |
| 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; |
| @@ -2982,13 +2983,7 @@ struct sk_buff *tcp_tso_segment(struct s |
| 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); |
| @@ -3005,7 +3000,9 @@ struct sk_buff *tcp_tso_segment(struct s |
| 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 - skb->transport_header) + |