skbuff: in skb_segment, call zerocopy functions once per nskb
authorWillem de Bruijn <willemb@google.com>
Sat, 23 Dec 2017 00:00:17 +0000 (19:00 -0500)
committerDavid S. Miller <davem@davemloft.net>
Wed, 27 Dec 2017 21:44:13 +0000 (16:44 -0500)
This is a net-next follow-up to commit 268b79067942 ("skbuff: orphan
frags before zerocopy clone"), which fixed a bug in net, but added a
call to skb_zerocopy_clone at each frag to do so.

When segmenting skbs with user frags, either the user frags must be
replaced with private copies and uarg released, or the uarg must have
its refcount increased for each new skb.

skb_orphan_frags does the first, except for cases that can handle
reference counting. skb_zerocopy_clone then does the second.

Call these once per nskb, instead of once per frag.

That is, in the common case. With a frag list, also refresh when the
origin skb (frag_skb) changes.

Signed-off-by: Willem de Bruijn <willemb@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/core/skbuff.c

index a3cb0be4c6f3b5b519b60ac8dde73c33e902763a..00b0757830e2c3224ebdb696e4eb8cfc36d87602 100644 (file)
@@ -3656,6 +3656,10 @@ normal:
                skb_shinfo(nskb)->tx_flags |= skb_shinfo(head_skb)->tx_flags &
                                              SKBTX_SHARED_FRAG;
 
+               if (skb_orphan_frags(frag_skb, GFP_ATOMIC) ||
+                   skb_zerocopy_clone(nskb, frag_skb, GFP_ATOMIC))
+                       goto err;
+
                while (pos < offset + len) {
                        if (i >= nfrags) {
                                BUG_ON(skb_headlen(list_skb));
@@ -3667,6 +3671,11 @@ normal:
 
                                BUG_ON(!nfrags);
 
+                               if (skb_orphan_frags(frag_skb, GFP_ATOMIC) ||
+                                   skb_zerocopy_clone(nskb, frag_skb,
+                                                      GFP_ATOMIC))
+                                       goto err;
+
                                list_skb = list_skb->next;
                        }
 
@@ -3678,11 +3687,6 @@ normal:
                                goto err;
                        }
 
-                       if (unlikely(skb_orphan_frags(frag_skb, GFP_ATOMIC)))
-                               goto err;
-                       if (skb_zerocopy_clone(nskb, frag_skb, GFP_ATOMIC))
-                               goto err;
-
                        *nskb_frag = *frag;
                        __skb_frag_ref(nskb_frag);
                        size = skb_frag_size(nskb_frag);