pp_params.pool_size = DESC_NUM;
     pp_params.nid = NUMA_NO_NODE;
     pp_params.dev = priv->dev;
+    pp_params.napi = napi; /* only if locking is tied to NAPI */
     pp_params.dma_dir = xdp_prog ? DMA_BIDIRECTIONAL : DMA_FROM_DEVICE;
     page_pool = page_pool_create(&pp_params);
 
 
        unsigned long           gro_bitmask;
        int                     (*poll)(struct napi_struct *, int);
 #ifdef CONFIG_NETPOLL
+       /* CPU actively polling if netpoll is configured */
        int                     poll_owner;
 #endif
+       /* CPU on which NAPI has been scheduled for processing */
+       int                     list_owner;
        struct net_device       *dev;
        struct gro_list         gro_hash[GRO_HASH_BUCKETS];
        struct sk_buff          *skb;
 
        __skb_frag_ref(&skb_shinfo(skb)->frags[f]);
 }
 
+static inline void
+napi_frag_unref(skb_frag_t *frag, bool recycle, bool napi_safe)
+{
+       struct page *page = skb_frag_page(frag);
+
+#ifdef CONFIG_PAGE_POOL
+       if (recycle && page_pool_return_skb_page(page, napi_safe))
+               return;
+#endif
+       put_page(page);
+}
+
 /**
  * __skb_frag_unref - release a reference on a paged fragment.
  * @frag: the paged fragment
  */
 static inline void __skb_frag_unref(skb_frag_t *frag, bool recycle)
 {
-       struct page *page = skb_frag_page(frag);
-
-#ifdef CONFIG_PAGE_POOL
-       if (recycle && page_pool_return_skb_page(page))
-               return;
-#endif
-       put_page(page);
+       napi_frag_unref(frag, recycle, false);
 }
 
 /**
 
        unsigned int    pool_size;
        int             nid;  /* Numa node id to allocate from pages from */
        struct device   *dev; /* device, for DMA pre-mapping purposes */
+       struct napi_struct *napi; /* Sole consumer of pages, otherwise NULL */
        enum dma_data_direction dma_dir; /* DMA mapping direction */
        unsigned int    max_len; /* max DMA sync memory size */
        unsigned int    offset;  /* DMA addr offset */
        return pool->p.dma_dir;
 }
 
-bool page_pool_return_skb_page(struct page *page);
+bool page_pool_return_skb_page(struct page *page, bool napi_safe);
 
 struct page_pool *page_pool_create(const struct page_pool_params *params);
 
 
        }
 
        list_add_tail(&napi->poll_list, &sd->poll_list);
+       WRITE_ONCE(napi->list_owner, smp_processor_id());
        /* If not called from net_rx_action()
         * we have to raise NET_RX_SOFTIRQ.
         */
                list_del_init(&n->poll_list);
                local_irq_restore(flags);
        }
+       WRITE_ONCE(n->list_owner, -1);
 
        val = READ_ONCE(n->state);
        do {
 #ifdef CONFIG_NETPOLL
        napi->poll_owner = -1;
 #endif
+       napi->list_owner = -1;
        set_bit(NAPI_STATE_SCHED, &napi->state);
        set_bit(NAPI_STATE_NPSVC, &napi->state);
        list_add_rcu(&napi->dev_list, &dev->napi_list);
 
 #include <linux/mm.h> /* for put_page() */
 #include <linux/poison.h>
 #include <linux/ethtool.h>
+#include <linux/netdevice.h>
 
 #include <trace/events/page_pool.h>
 
 }
 EXPORT_SYMBOL(page_pool_update_nid);
 
-bool page_pool_return_skb_page(struct page *page)
+bool page_pool_return_skb_page(struct page *page, bool napi_safe)
 {
+       struct napi_struct *napi;
        struct page_pool *pp;
+       bool allow_direct;
 
        page = compound_head(page);
 
 
        pp = page->pp;
 
+       /* Allow direct recycle if we have reasons to believe that we are
+        * in the same context as the consumer would run, so there's
+        * no possible race.
+        */
+       napi = pp->p.napi;
+       allow_direct = napi_safe && napi &&
+               READ_ONCE(napi->list_owner) == smp_processor_id();
+
        /* Driver set this to memory recycling info. Reset it on recycle.
         * This will *not* work for NIC using a split-page memory model.
         * The page will be returned to the pool here regardless of the
         * 'flipped' fragment being in use or not.
         */
-       page_pool_put_full_page(pp, page, false);
+       page_pool_put_full_page(pp, page, allow_direct);
 
        return true;
 }
 
 {
        if (!IS_ENABLED(CONFIG_PAGE_POOL) || !skb->pp_recycle)
                return false;
-       return page_pool_return_skb_page(virt_to_page(data));
+       return page_pool_return_skb_page(virt_to_page(data), napi_safe);
 }
 
 static void skb_kfree_head(void *head, unsigned int end_offset)
        }
 
        for (i = 0; i < shinfo->nr_frags; i++)
-               __skb_frag_unref(&shinfo->frags[i], skb->pp_recycle);
+               napi_frag_unref(&shinfo->frags[i], skb->pp_recycle, napi_safe);
 
 free_head:
        if (shinfo->frag_list)