diff options
author | Eric Dumazet <edumazet@google.com> | 2019-03-26 11:34:55 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2019-03-27 16:59:02 -0400 |
commit | 4f661542a40217713f2cee0bb6678fbb30d9d367 (patch) | |
tree | 8c49f8cb2bb8e14e76b8f912ec39659dc06cb4e5 /net/ipv4/tcp.c | |
parent | 4d5ec89fc8d14dcdab7214a0c13a1c7321dc6ea9 (diff) |
tcp: fix zerocopy and notsent_lowat issues
My recent patch had at least three problems :
1) TX zerocopy wants notification when skb is acknowledged,
thus we need to call skb_zcopy_clear() if the skb is
cached into sk->sk_tx_skb_cache
2) Some applications might expect precise EPOLLOUT
notifications, so we need to update sk->sk_wmem_queued
and call sk_mem_uncharge() from sk_wmem_free_skb()
in all cases. The SOCK_QUEUE_SHRUNK flag must also be set.
3) Reuse of saved skb should have used skb_cloned() instead
of simply checking if the fast clone has been freed.
Fixes: 472c2e07eef0 ("tcp: add one skb cache for tx")
Signed-off-by: Eric Dumazet <edumazet@google.com>
Cc: Willem de Bruijn <willemb@google.com>
Cc: Soheil Hassas Yeganeh <soheil@google.com>
Acked-by: Soheil Hassas Yeganeh <soheil@google.com>
Tested-by: Holger Hoffstätte <holger@applied-asynchrony.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv4/tcp.c')
-rw-r--r-- | net/ipv4/tcp.c | 13 |
1 files changed, 3 insertions, 10 deletions
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 29b94edf05f9..82bd707c0347 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c | |||
@@ -865,14 +865,9 @@ struct sk_buff *sk_stream_alloc_skb(struct sock *sk, int size, gfp_t gfp, | |||
865 | { | 865 | { |
866 | struct sk_buff *skb; | 866 | struct sk_buff *skb; |
867 | 867 | ||
868 | skb = sk->sk_tx_skb_cache; | 868 | if (likely(!size)) { |
869 | if (skb && !size) { | 869 | skb = sk->sk_tx_skb_cache; |
870 | const struct sk_buff_fclones *fclones; | 870 | if (skb && !skb_cloned(skb)) { |
871 | |||
872 | fclones = container_of(skb, struct sk_buff_fclones, skb1); | ||
873 | if (refcount_read(&fclones->fclone_ref) == 1) { | ||
874 | sk->sk_wmem_queued -= skb->truesize; | ||
875 | sk_mem_uncharge(sk, skb->truesize); | ||
876 | skb->truesize -= skb->data_len; | 871 | skb->truesize -= skb->data_len; |
877 | sk->sk_tx_skb_cache = NULL; | 872 | sk->sk_tx_skb_cache = NULL; |
878 | pskb_trim(skb, 0); | 873 | pskb_trim(skb, 0); |
@@ -2543,8 +2538,6 @@ void tcp_write_queue_purge(struct sock *sk) | |||
2543 | tcp_rtx_queue_purge(sk); | 2538 | tcp_rtx_queue_purge(sk); |
2544 | skb = sk->sk_tx_skb_cache; | 2539 | skb = sk->sk_tx_skb_cache; |
2545 | if (skb) { | 2540 | if (skb) { |
2546 | sk->sk_wmem_queued -= skb->truesize; | ||
2547 | sk_mem_uncharge(sk, skb->truesize); | ||
2548 | __kfree_skb(skb); | 2541 | __kfree_skb(skb); |
2549 | sk->sk_tx_skb_cache = NULL; | 2542 | sk->sk_tx_skb_cache = NULL; |
2550 | } | 2543 | } |