net: add and use skb_unclone_keeptruesize() helper
authorEric Dumazet <edumazet@google.com>
Tue, 2 Nov 2021 00:45:55 +0000 (17:45 -0700)
committerJakub Kicinski <kuba@kernel.org>
Wed, 3 Nov 2021 01:10:34 +0000 (18:10 -0700)
While commit 097b9146c0e2 ("net: fix up truesize of cloned
skb in skb_prepare_for_shift()") fixed immediate issues found
when KFENCE was enabled/tested, there are still similar issues,
when tcp_trim_head() hits KFENCE while the master skb
is cloned.

This happens under heavy networking TX workloads,
when the TX completion might be delayed after incoming ACK.

This patch fixes the WARNING in sk_stream_kill_queues
when sk->sk_mem_queued/sk->sk_forward_alloc are not zero.

Fixes: d3fb45f370d9 ("mm, kfence: insert KFENCE hooks for SLAB")
Signed-off-by: Eric Dumazet <edumazet@google.com>
Acked-by: Marco Elver <elver@google.com>
Link: https://lore.kernel.org/r/20211102004555.1359210-1-eric.dumazet@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
include/linux/skbuff.h
net/core/skbuff.c
net/ipv4/tcp_output.c

index 0bd6520329f6fd04420c9e1772277b948931ac99..a63e130823979ea6ba73936bb0acebcd6c14c238 100644 (file)
@@ -1675,6 +1675,22 @@ static inline int skb_unclone(struct sk_buff *skb, gfp_t pri)
        return 0;
 }
 
+/* This variant of skb_unclone() makes sure skb->truesize is not changed */
+static inline int skb_unclone_keeptruesize(struct sk_buff *skb, gfp_t pri)
+{
+       might_sleep_if(gfpflags_allow_blocking(pri));
+
+       if (skb_cloned(skb)) {
+               unsigned int save = skb->truesize;
+               int res;
+
+               res = pskb_expand_head(skb, 0, 0, pri);
+               skb->truesize = save;
+               return res;
+       }
+       return 0;
+}
+
 /**
  *     skb_header_cloned - is the header a clone
  *     @skb: buffer to check
index 67a9188d8a49c83ce722f68d3064e9ba0545ea37..3ec42cdee16afe904258225a986c88d4b3379432 100644 (file)
@@ -3449,19 +3449,7 @@ EXPORT_SYMBOL(skb_split);
  */
 static int skb_prepare_for_shift(struct sk_buff *skb)
 {
-       int ret = 0;
-
-       if (skb_cloned(skb)) {
-               /* Save and restore truesize: pskb_expand_head() may reallocate
-                * memory where ksize(kmalloc(S)) != ksize(kmalloc(S)), but we
-                * cannot change truesize at this point.
-                */
-               unsigned int save_truesize = skb->truesize;
-
-               ret = pskb_expand_head(skb, 0, 0, GFP_ATOMIC);
-               skb->truesize = save_truesize;
-       }
-       return ret;
+       return skb_unclone_keeptruesize(skb, GFP_ATOMIC);
 }
 
 /**
index 6fbbf155803372efed056a6f4df8809f7734be68..76cc1641beb4bdf5bd774a734db9ce814b88241e 100644 (file)
@@ -1559,7 +1559,7 @@ int tcp_fragment(struct sock *sk, enum tcp_queue tcp_queue,
                return -ENOMEM;
        }
 
-       if (skb_unclone(skb, gfp))
+       if (skb_unclone_keeptruesize(skb, gfp))
                return -ENOMEM;
 
        /* Get a new skb... force flag on. */
@@ -1667,7 +1667,7 @@ int tcp_trim_head(struct sock *sk, struct sk_buff *skb, u32 len)
 {
        u32 delta_truesize;
 
-       if (skb_unclone(skb, GFP_ATOMIC))
+       if (skb_unclone_keeptruesize(skb, GFP_ATOMIC))
                return -ENOMEM;
 
        delta_truesize = __pskb_trim_head(skb, len);
@@ -3166,7 +3166,7 @@ int __tcp_retransmit_skb(struct sock *sk, struct sk_buff *skb, int segs)
                                 cur_mss, GFP_ATOMIC))
                        return -ENOMEM; /* We'll try again later. */
        } else {
-               if (skb_unclone(skb, GFP_ATOMIC))
+               if (skb_unclone_keeptruesize(skb, GFP_ATOMIC))
                        return -ENOMEM;
 
                diff = tcp_skb_pcount(skb);