From: sashiko-bot@kernel.org
To: "Björn Töpel" <bjorn@kernel.org>, bpf@vger.kernel.org
Subject: Re: [PATCH net-next v1 06/11] page_pool: Coordinate refill with memory providers
Date: Wed, 11 Nov 2026 10:22:45 +0000 [thread overview]
Message-ID: <sashiko-outbox-208789@kernel.org> (raw)
In-Reply-To: <20261110102039.1002605-7-bjorn@kernel.org>
Thank you for your contribution! Sashiko AI review found 3 potential
issues to consider:
- [High] xsk: use-after-free of pp->pool in xsk_pp_refill_done() after
detach
- [Medium] net: wrong mp_napi state after netdev_rx_queue_reconfig()
error recovery
- [Medium] net: dangling rxq->napi left by netif_mp_napi_del() and
republished
--
commit 3fe1ff48cc345ebc097d0481e784047c7126ab42
Author: Björn Töpel <bjorn@kernel.org>
page_pool: Coordinate refill with memory providers
This commit adds page_pool_refill_done() and NAPI wakeup tracking on RX
queues so that memory providers such as AF_XDP can re-poll or wake queues
when buffers return.
> diff --git a/net/core/netdev_rx_queue.c b/net/core/netdev_rx_queue.c
> index 4e2f906fbc735..3fe904b56791b 100644
> --- a/net/core/netdev_rx_queue.c
> +++ b/net/core/netdev_rx_queue.c
> @@ -116,6 +116,94 @@ bool netif_all_mps_have_cap(struct net_device *dev, u32 cap)
> return true;
> }
>
> +/**
> + * netif_mp_wake - schedule the NAPI that refills a memory provider queue
> + * @rxq: physical RX queue of the provider
> + *
> + * Context: IRQs enabled, not in hardirq. The caller keeps the device of
> + * @rxq alive.
> + *
> + * Return: false if no NAPI refills the queue.
> + */
> +bool netif_mp_wake(struct netdev_rx_queue *rxq)
> +{
> + struct napi_struct *napi;
> + bool running = false;
> +
> + rcu_read_lock();
> + napi = rcu_dereference(rxq->mp_napi);
> + if (napi) {
> + local_bh_disable();
> + napi_schedule(napi);
> + local_bh_enable();
> + running = true;
> + }
> + rcu_read_unlock();
> +
> + return running;
> +}
> +
> +/* Point wakeups at the NAPI of the running queue, and schedule it once: a
> + * refill during the start may have found no buffers.
> + */
> +void netif_mp_update_napi(struct netdev_rx_queue *rxq)
> +{
> + const struct memory_provider_ops *ops = rxq->mp_params.mp_ops;
> + struct napi_struct *napi = NULL;
> +
> + if (rxq->mp_armed && ops && ops->refill_done)
> + napi = rxq->napi;
> + if (rcu_access_pointer(rxq->mp_napi) == napi)
> + return;
> +
> + rcu_assign_pointer(rxq->mp_napi, napi);
> + if (napi)
> + netif_mp_wake(rxq);
> +}
> +
> +static void netif_mp_arm_napi(struct netdev_rx_queue *rxq)
> +{
> + rxq->mp_armed = true;
> + netif_mp_update_napi(rxq);
> +}
> +
> +static void netif_mp_disarm_napi(struct netdev_rx_queue *rxq)
> +{
> + rxq->mp_armed = false;
> + RCU_INIT_POINTER(rxq->mp_napi, NULL);
> +}
> +
> +void netif_mp_arm_dev(struct net_device *dev)
> +{
> + unsigned int i;
> +
> + for (i = 0; i < dev->real_num_rx_queues; i++)
> + netif_mp_arm_napi(__netif_get_rx_queue(dev, i));
> +}
> +
> +void netif_mp_disarm_dev(struct net_device *dev)
> +{
> + unsigned int i;
> +
> + for (i = 0; i < dev->num_rx_queues; i++)
> + netif_mp_disarm_napi(&dev->_rx[i]);
> +}
> +
> +/* The grace period of netif_napi_del() covers wakeups that found @napi. */
> +void netif_mp_napi_del(struct napi_struct *napi)
> +{
> + struct net_device *dev = napi->dev;
> + unsigned int i;
> +
> + /* free_netdev() frees the queues before it deletes the last NAPIs */
> + if (dev->reg_state != NETREG_REGISTERED)
> + return;
> +
> + for (i = 0; i < dev->num_rx_queues; i++)
> + if (rcu_access_pointer(dev->_rx[i].mp_napi) == napi)
> + RCU_INIT_POINTER(dev->_rx[i].mp_napi, NULL);
> +}
[Severity: Medium]
Does netif_mp_napi_del() leave a dangling pointer in dev->_rx[i].napi?
__netif_napi_del_locked() calls netif_mp_napi_del(), which clears
dev->_rx[i].mp_napi, including when mp_napi is already NULL because
__dev_close_many() or netdev_rx_queue_reconfig() disarmed the queue first.
Because dev->_rx[i].napi is left pointing to @napi, if a driver deletes a
NAPI without calling netif_queue_set_napi(..., NULL), the next
netif_mp_update_napi() copies the freed dev->_rx[i].napi back into
rxq->mp_napi and calls napi_schedule() on it, causing a use-after-free.
> @@ -152,13 +240,15 @@ static int netdev_rx_queue_reconfig(struct net_device *dev,
> goto err_free_new_queue_mem;
>
> if (netif_running(dev)) {
> + netif_mp_disarm_napi(rxq);
> err = qops->ndo_queue_stop(dev, old_mem, rxq_idx);
> if (err)
> - goto err_free_new_queue_mem;
> + goto err_arm_old_queue;
>
> err = qops->ndo_queue_start(dev, qcfg_new, new_mem, rxq_idx);
> if (err)
> goto err_start_queue;
> + netif_mp_arm_napi(rxq);
> } else {
> swap(new_mem, old_mem);
> }
> @@ -183,8 +273,13 @@ static int netdev_rx_queue_reconfig(struct net_device *dev,
> "Failed to restart old queue in error path. RX queue %d may be unhealthy.",
> rxq_idx);
> qops->ndo_queue_mem_free(dev, old_mem);
> + goto err_free_new_queue_mem;
> }
>
> +err_arm_old_queue:
> + /* The old queue may run */
> + netif_mp_arm_napi(rxq);
> +
> err_free_new_queue_mem:
> qops->ndo_queue_mem_free(dev, new_mem);
[Severity: Medium]
Does err_arm_old_queue arm rxq->mp_napi using the target rxq->mp_params
instead of the running old queue's provider state?
Both __netif_mp_open_rxq() and __netif_mp_close_rxq() mutate rxq->mp_params
before calling netdev_rx_queue_reconfig():
net/core/netdev_rx_queue.c:__netif_mp_open_rxq() {
...
rxq->mp_params = *p;
...
ret = netdev_rx_queue_reconfig(dev, rxq_idx, &qcfg[0], &qcfg[1]);
if (ret)
goto err_clear_mp;
return 0;
err_clear_mp:
memset(&rxq->mp_params, 0, sizeof(rxq->mp_params));
return ret;
}
When ndo_queue_stop() or ndo_queue_start() fails during __netif_mp_open_rxq(),
err_arm_old_queue sees the new provider in rxq->mp_params, sets rxq->mp_napi
to rxq->napi, and wakes NAPI before err_clear_mp zeroes rxq->mp_params,
leaving rxq->mp_napi set with no provider installed. Conversely,
__netif_mp_close_rxq() zeroes rxq->mp_params before calling
netdev_rx_queue_reconfig(), so on failure err_arm_old_queue sees NULL mp_ops
and leaves rxq->mp_napi NULL even though the old provider-backed queue is
still running.
> diff --git a/net/xdp/xsk_pp.c b/net/xdp/xsk_pp.c
> index c2214e718cdb3..a77213f7fef6b 100644
> --- a/net/xdp/xsk_pp.c
> +++ b/net/xdp/xsk_pp.c
[ ... ]
> @@ -232,11 +253,47 @@ static bool xsk_pp_release_netmem(struct page_pool *pool, netmem_ref netmem)
> pp->state[idx] = XSK_PP_REUSE;
> pp->reuse[pp->reuse_cnt++] = idx;
> }
> + starved = pp->starved;
> + WRITE_ONCE(pp->starved, false);
> spin_unlock_bh(&pp->lock);
>
> + /* A starved queue may wait for this release */
> + if (starved)
> + xsk_pp_wake(pp);
> +
> return false;
> }
>
> +static bool xsk_pp_refill_done(struct page_pool *pool, enum pp_refill state)
> +{
> + struct xsk_pp *pp = pool->mp_priv;
> + struct xsk_buff_pool *xp = pp->pool;
> + bool wait = xp->uses_need_wakeup;
> + bool more;
> +
> + if (state == PP_REFILL_FULL && !READ_ONCE(pp->starved))
> + return true;
> +
> + spin_lock_bh(&pp->lock);
> + if (!pp->fq) {
> + spin_unlock_bh(&pp->lock);
> + return true;
> + }
[Severity: High]
Can xsk_pp_refill_done() dereference pp->pool after xp_destroy() has freed
it?
A page_pool holds its own reference on struct xsk_pp via pp->refs, while
pp->pool is an uncounted backpointer to struct xsk_buff_pool. When
xp_release_deferred() tears down the pool, xsk_pp_unbind() calls
netif_mp_close_rxq() and unconditionally calls xsk_pp_detach(pp) (which sets
pp->fq = NULL under pp->lock and unmaps DMA), and xp_release_deferred() then
frees pp->pool in xp_destroy(). If netif_mp_close_rxq() fails in
netdev_rx_queue_reconfig(), the old queue and its page_pool keep running and
calling xsk_pp_refill_done(), which reads xp->uses_need_wakeup before taking
pp->lock and checking pp->fq, causing a use-after-free.
In addition, if netif_mp_open_rxq() fails in xsk_pp_bind() while a page_pool
still holds a reference on pp (so xsk_pp_abandon(pp) returns false),
xsk_pp_bind() leaves pp->fq non-NULL when xsk_bind() calls xp_destroy().
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261110102039.1002605-1-bjorn@kernel.org?part=6
next prev parent reply other threads:[~2026-11-11 10:22 UTC|newest]
Thread overview: 62+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-10 10:20 [PATCH net-next v1 00/11] xsk: Zero copy through page-pool memory providers Björn Töpel
2026-11-10 10:20 ` [PATCH net-next v1 01/11] net: Get skb fragment addresses through netmem Björn Töpel
2026-11-12 8:32 ` saeed bishara
2026-11-12 13:57 ` Björn Töpel
2026-11-10 10:20 ` [PATCH net-next v1 02/11] net: Add memory provider capabilities Björn Töpel
2026-11-10 22:19 ` Stanislav Fomichev
2026-11-11 12:41 ` Björn Töpel
2026-11-11 23:01 ` Stanislav Fomichev
2026-11-12 14:01 ` Björn Töpel
2026-11-13 0:21 ` Stanislav Fomichev
2026-11-11 10:22 ` sashiko-bot
2026-11-11 22:41 ` netdev-bot+sashiko
2026-11-10 10:20 ` [PATCH net-next v1 03/11] xdp: Copy buffers with restricted lifetime Björn Töpel
2026-11-10 22:32 ` Stanislav Fomichev
2026-11-11 13:27 ` Björn Töpel
2026-11-11 23:05 ` Stanislav Fomichev
2026-11-11 10:22 ` sashiko-bot
2026-11-11 22:41 ` netdev-bot+sashiko
2026-11-10 10:20 ` [PATCH net-next v1 04/11] xsk: Allocate UMEM chunks through page_pool Björn Töpel
2026-11-10 15:08 ` Magnus Karlsson
2026-11-11 13:51 ` Björn Töpel
2026-11-10 23:02 ` Stanislav Fomichev
2026-11-11 13:59 ` Björn Töpel
2026-11-11 23:09 ` Stanislav Fomichev
2026-11-11 22:41 ` netdev-bot+sashiko
2026-11-10 10:20 ` [PATCH net-next v1 05/11] xsk: Install UMEM page pool providers in the core Björn Töpel
2026-11-10 23:50 ` Stanislav Fomichev
2026-11-11 14:01 ` Björn Töpel
2026-11-11 8:59 ` Magnus Karlsson
2026-11-11 19:26 ` Björn Töpel
2026-11-12 8:08 ` Magnus Karlsson
2026-11-11 10:22 ` sashiko-bot
2026-11-11 22:41 ` netdev-bot+sashiko
2026-11-12 11:53 ` Maciej Fijalkowski
2026-11-12 14:48 ` Björn Töpel
2026-11-12 15:43 ` Maciej Fijalkowski
2026-11-12 19:51 ` Björn Töpel
2026-11-10 10:20 ` [PATCH net-next v1 06/11] page_pool: Coordinate refill with memory providers Björn Töpel
2026-11-10 23:51 ` Stanislav Fomichev
2026-11-11 19:37 ` Björn Töpel
2026-11-11 23:13 ` Stanislav Fomichev
2026-11-12 14:53 ` Björn Töpel
2026-11-11 10:22 ` sashiko-bot [this message]
2026-11-11 22:41 ` netdev-bot+sashiko
2026-11-10 10:20 ` [PATCH net-next v1 07/11] xsk: Deliver provider chunks without copying Björn Töpel
2026-11-11 22:41 ` netdev-bot+sashiko
2026-11-11 23:20 ` Stanislav Fomichev
2026-11-12 15:10 ` Björn Töpel
2026-11-12 12:17 ` Maciej Fijalkowski
2026-11-12 19:57 ` Björn Töpel
2026-11-10 10:20 ` [PATCH net-next v1 08/11] eth: fbnic: Support XDP redirect Björn Töpel
2026-11-11 10:22 ` sashiko-bot
2026-11-10 10:20 ` [PATCH net-next v1 09/11] eth: fbnic: Use netmem for RX headers and XDP_TX Björn Töpel
2026-11-10 10:20 ` [PATCH net-next v1 10/11] eth: fbnic: Support RX buffers it cannot split Björn Töpel
2026-11-11 10:22 ` sashiko-bot
2026-11-11 21:45 ` Dimitri Daskalakis
2026-11-11 22:41 ` netdev-bot+sashiko
2026-11-10 10:20 ` [PATCH net-next v1 11/11] eth: fbnic: Support AF_XDP page pool zero copy Björn Töpel
2026-11-11 10:22 ` sashiko-bot
2026-11-11 22:41 ` netdev-bot+sashiko
2026-11-12 11:37 ` Maciej Fijalkowski
2026-11-12 20:03 ` Björn Töpel
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=sashiko-outbox-208789@kernel.org \
--to=sashiko-bot@kernel.org \
--cc=bjorn@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=sashiko-reviews@lists.linux.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.