skbuff: Fix a potential race while recycling page_pool packets
[linux-2.6-microblaze.git] / net / core / skbuff.c
index f63de96..0fe97d6 100644 (file)
@@ -663,7 +663,7 @@ static void skb_release_data(struct sk_buff *skb)
        if (skb->cloned &&
            atomic_sub_return(skb->nohdr ? (1 << SKB_DATAREF_SHIFT) + 1 : 1,
                              &shinfo->dataref))
-               return;
+               goto exit;
 
        skb_zcopy_clear(skb, true);
 
@@ -674,6 +674,17 @@ static void skb_release_data(struct sk_buff *skb)
                kfree_skb_list(shinfo->frag_list);
 
        skb_free_head(skb);
+exit:
+       /* When we clone an SKB we copy the reycling bit. The pp_recycle
+        * bit is only set on the head though, so in order to avoid races
+        * while trying to recycle fragments on __skb_frag_unref() we need
+        * to make one SKB responsible for triggering the recycle path.
+        * So disable the recycling bit if an SKB is cloned and we have
+        * additional references to to the fragmented part of the SKB.
+        * Eventually the last SKB will have the recycling bit set and it's
+        * dataref set to 0, which will trigger the recycling
+        */
+       skb->pp_recycle = 0;
 }
 
 /*