return napi_pp_put_page(virt_to_page(data), napi_safe);
 }
 
+/**
+ * skb_pp_frag_ref() - Increase fragment references of a page pool aware skb
+ * @skb:       page pool aware skb
+ *
+ * Increase the fragment reference count (pp_ref_count) of a skb. This is
+ * intended to gain fragment references only for page pool aware skbs,
+ * i.e. when skb->pp_recycle is true, and not for fragments in a
+ * non-pp-recycling skb. It has a fallback to increase references on normal
+ * pages, as page pool aware skbs may also have normal page fragments.
+ */
+static int skb_pp_frag_ref(struct sk_buff *skb)
+{
+       struct skb_shared_info *shinfo;
+       struct page *head_page;
+       int i;
+
+       if (!skb->pp_recycle)
+               return -EINVAL;
+
+       shinfo = skb_shinfo(skb);
+
+       for (i = 0; i < shinfo->nr_frags; i++) {
+               head_page = compound_head(skb_frag_page(&shinfo->frags[i]));
+               if (likely(is_pp_page(head_page)))
+                       page_pool_ref_page(head_page);
+               else
+                       page_ref_inc(head_page);
+       }
+       return 0;
+}
+
 static void skb_kfree_head(void *head, unsigned int end_offset)
 {
        if (end_offset == SKB_SMALL_HEAD_HEADROOM)
                return false;
 
        /* In general, avoid mixing page_pool and non-page_pool allocated
-        * pages within the same SKB. Additionally avoid dealing with clones
-        * with page_pool pages, in case the SKB is using page_pool fragment
-        * references (page_pool_alloc_frag()). Since we only take full page
-        * references for cloned SKBs at the moment that would result in
-        * inconsistent reference counts.
-        * In theory we could take full references if @from is cloned and
-        * !@to->pp_recycle but its tricky (due to potential race with
-        * the clone disappearing) and rare, so not worth dealing with.
+        * pages within the same SKB. In theory we could take full
+        * references if @from is cloned and !@to->pp_recycle but its
+        * tricky (due to potential race with the clone disappearing) and
+        * rare, so not worth dealing with.
         */
-       if (to->pp_recycle != from->pp_recycle ||
-           (from->pp_recycle && skb_cloned(from)))
+       if (to->pp_recycle != from->pp_recycle)
                return false;
 
        if (len <= skb_tailroom(to)) {
        /* if the skb is not cloned this does nothing
         * since we set nr_frags to 0.
         */
-       for (i = 0; i < from_shinfo->nr_frags; i++)
-               __skb_frag_ref(&from_shinfo->frags[i]);
+       if (skb_pp_frag_ref(from)) {
+               for (i = 0; i < from_shinfo->nr_frags; i++)
+                       __skb_frag_ref(&from_shinfo->frags[i]);
+       }
 
        to->truesize += delta;
        to->len += len;