Commit c37fa339 authored by David S. Miller's avatar David S. Miller

[TCP]: Fix sk_forward_alloc assertion failures with TSO.

do_tcp_sendpages() needs to do skb->truesize et al.
accounting just like tcp_sendmsg() does.

tcp_sendmsg() works by gradually adjusting these
accounting knobs as user data is copied into the
packet.

do_tcp_sendpages() works differently, when it allocates
a new SKB it optimistically adds in tp->mss_cache to
these values and then makes no adjustments at all as
pages are tacked onto the packet.

This does not work at all if tcp_sendmsg() queues a
packet onto the send queue, and then do_tcp_sendpages()
attaches pages onto the end of that SKB.  We are left
with a very inaccurate skb->truesize in that case.

Consequently, if we were building a TSO frame and it
gets partially ACK'd, then since skb->truesize is too
small tcp_trim_skb() will potentially underflow it's
value and all the accounting becomes corrupted.

This is usually seen as sk->sk_forward_alloc being
negative at socket destroy time, which triggers an
assertion check.
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 0f76379c
...@@ -664,7 +664,7 @@ static ssize_t do_tcp_sendpages(struct sock *sk, struct page **pages, int poffse ...@@ -664,7 +664,7 @@ static ssize_t do_tcp_sendpages(struct sock *sk, struct page **pages, int poffse
if (!sk_stream_memory_free(sk)) if (!sk_stream_memory_free(sk))
goto wait_for_sndbuf; goto wait_for_sndbuf;
skb = sk_stream_alloc_pskb(sk, 0, tp->mss_cache, skb = sk_stream_alloc_pskb(sk, 0, 0,
sk->sk_allocation); sk->sk_allocation);
if (!skb) if (!skb)
goto wait_for_memory; goto wait_for_memory;
...@@ -689,6 +689,9 @@ static ssize_t do_tcp_sendpages(struct sock *sk, struct page **pages, int poffse ...@@ -689,6 +689,9 @@ static ssize_t do_tcp_sendpages(struct sock *sk, struct page **pages, int poffse
skb->len += copy; skb->len += copy;
skb->data_len += copy; skb->data_len += copy;
skb->truesize += copy;
sk->sk_wmem_queued += copy;
sk->sk_forward_alloc -= copy;
skb->ip_summed = CHECKSUM_HW; skb->ip_summed = CHECKSUM_HW;
tp->write_seq += copy; tp->write_seq += copy;
TCP_SKB_CB(skb)->end_seq += copy; TCP_SKB_CB(skb)->end_seq += copy;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment