From: sashiko-bot@kernel.org
To: "Haishuang Yan" <yanhaishuang@cmss.chinamobile.com>
Cc: virtualization@lists.linux.dev, bpf@vger.kernel.org,
"Michael S. Tsirkin" <mst@redhat.com>,
"Eugenio Perez" <eperezma@redhat.com>
Subject: Re: [PATCH net 2/2] virtio_net: Keep AF_XDP zero-copy pools bound across suspend
Date: Sun, 08 Nov 2026 04:58:28 +0000 [thread overview]
Message-ID: <sashiko-outbox-203566@kernel.org> (raw)
In-Reply-To: <20261107125723.33547-3-yanhaishuang@cmss.chinamobile.com>
Thank you for your contribution! Sashiko AI review found 4 potential
issues to consider:
- [High] virtio_net: stale DMA addresses in free_list after unaligned
XSK pool rebind
- [High] virtio_net: uninitialized xsk_hdr_dma_addr used in
virtnet_xsk_pool_enable()
- [High] virtio_net: race between RX NAPI and xsk_pool_dma_unmap() in
virtnet_freeze()
- [Medium] virtio_net: virtnet_xsk_pool_disable() fails for qid >=
curr_queue_pairs
--
commit 0f52eff49a0a7d3c2cb4265b671ce30bc13fd5e3
Author: Haishuang Yan <yanhaishuang@cmss.chinamobile.com>
virtio_net: Keep AF_XDP zero-copy pools bound across suspend
This commit unbinds the driver side of active AF_XDP zero-copy buffer pools
before freezing virtio_net queues and rebinds them after restore.
> diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
> index 4f5d8812f75b0..5995ad730a91a 100644
> --- a/drivers/net/virtio_net.c
> +++ b/drivers/net/virtio_net.c
> @@ -6052,6 +6055,42 @@ static int virtnet_xsk_pool_disable(struct net_device *dev, u16 qid)
> return err;
> }
>
> +/*
> + * Freeze frees the receive and send queues while the AF_XDP sockets stay
> + * bound to the device, so release the driver side of the zero-copy pools
> + * before and bind them again after restore.
> + */
> +static void __maybe_unused virtnet_xsk_pools_unbind(struct virtnet_info *vi)
> +{
> + int i;
> +
> + ASSERT_RTNL();
> +
> + for (i = 0; i < vi->max_queue_pairs; i++)
> + if (vi->rq[i].xsk_pool)
> + virtnet_xsk_pool_disable(vi->dev, i);
> +}
[Severity: Medium]
Does virtnet_xsk_pool_disable() fail without unbinding the pool when
i >= vi->curr_queue_pairs?
Attaching an XDP program in virtnet_xdp_set() expands vi->curr_queue_pairs
and dev->real_num_rx_queues by xdp_qp, allowing an AF_XDP zero-copy pool to
be bound to a queue in that upper range, and detaching the XDP program
shrinks vi->curr_queue_pairs back down without unbinding the pool. When
virtnet_xsk_pools_unbind() iterates up to vi->max_queue_pairs,
virtnet_xsk_pool_disable() rejects any qid >= vi->curr_queue_pairs:
drivers/net/virtio_net.c:virtnet_xsk_pool_disable() {
if (qid >= vi->curr_queue_pairs)
return -EINVAL;
}
As a result, remove_vq_common() frees vi->rq and vi->sq while leaving
rq->xsk_rxq_info registered, the pool DMA mapping active, and rq->xsk_buffs
and sq->xsk_hdr leaked.
> +
> +static void __maybe_unused virtnet_xsk_pools_rebind(struct virtnet_info *vi)
> +{
> + struct xsk_buff_pool *pool;
> + int i, err;
> +
> + ASSERT_RTNL();
> +
> + for (i = 0; i < vi->curr_queue_pairs; i++) {
> + pool = xsk_get_pool_from_qid(vi->dev, i);
> + if (!pool || !pool->umem->zc)
> + continue;
> +
> + err = virtnet_xsk_pool_enable(vi->dev, pool, i);
[Severity: High]
When pool->unaligned is true, does rebinding the pool after freeze leave
stale pre-suspend DMA addresses in pool->free_list?
During virtnet_freeze(), virtnet_xsk_pool_disable() resets rq->vq and passes
each posted RX buffer to xsk_buff_free(), which places the xdp_buff_xsk
entries onto pool->free_list before xsk_pool_dma_unmap() unmaps and frees
the old DMA mapping. On restore, virtnet_xsk_pool_enable() calls
xsk_pool_dma_map() -> xp_dma_map() -> xp_init_dma_info(), which skips
reinitializing xskb->dma when pool->unaligned is set:
net/xdp/xsk_buff_pool.c:xp_init_dma_info() {
if (!pool->unaligned) {
u32 i;
for (i = 0; i < pool->heads_cnt; i++) {
struct xdp_buff_xsk *xskb = &pool->heads[i];
u64 orig_addr;
orig_addr = xskb->xdp.data_hard_start - pool->addrs - pool->headroom;
xp_init_xskb_dma(xskb, pool, dma_map->dma_pages, orig_addr);
}
}
}
When virtnet_rq_bind_xsk_pool() refills rq->vq via virtnet_rx_resume() ->
try_fill_recv() -> virtnet_add_recvbuf_xsk(), xsk_buff_alloc_batch() pops
those recycled entries from pool->free_list via xp_alloc_reused() without
updating xskb->dma, programming rq->vq with the unmapped pre-suspend DMA
addresses and causing IOMMU faults or DMA memory corruption.
> + if (err)
> + netdev_warn(vi->dev,
> + "failed to rebind AF_XDP pool to queue %d: %d\n",
> + i, err);
> + }
> +}
[Severity: High]
Can virtnet_xsk_pool_enable() transmit from sq->xsk_pool during restore
before sq->xsk_hdr_dma_addr is initialized?
On restore, init_vqs() allocates fresh zeroed send queues (so
sq->xsk_hdr_dma_addr is 0), while bound AF_XDP sockets are already on
pool->xsk_tx_list and may have pending descriptors in their TX rings.
virtnet_xsk_pool_enable() calls virtnet_sq_bind_xsk_pool() before setting
sq->xsk_hdr_dma_addr:
drivers/net/virtio_net.c:virtnet_xsk_pool_enable() {
...
err = virtnet_sq_bind_xsk_pool(vi, sq, pool);
if (err)
goto err_sq;
/* Now, we do not support tx offload(such as tx csum), so all the tx
* virtnet hdr is zero. So all the tx packets can share a single hdr.
*/
sq->xsk_hdr_dma_addr = hdr_dma;
...
}
When the device is running, virtnet_sq_bind_xsk_pool() sets sq->xsk_pool =
pool and calls virtnet_tx_resume() -> virtnet_napi_tx_enable() ->
virtnet_napi_do_enable(), which schedules sq->napi and runs virtnet_poll_tx()
synchronously on local_bh_enable(). virtnet_poll_tx() -> virtnet_xsk_xmit()
-> virtnet_xsk_xmit_one() then submits sq->xsk_hdr_dma_addr (0) as the
virtio header DMA address to the virtqueue.
[ ... ]
> @@ -7246,6 +7285,10 @@ static __maybe_unused int virtnet_freeze(struct virtio_device *vdev)
> {
> struct virtnet_info *vi = vdev->priv;
>
> + rtnl_lock();
> + virtnet_xsk_pools_unbind(vi);
> + rtnl_unlock();
> +
> virtnet_cpu_notif_remove(vi);
> virtnet_freeze_down(vdev);
[Severity: High]
Can unbinding the pools before virtnet_freeze_down() race with RX NAPI
redirecting to the still-bound AF_XDP socket while xsk_pool_dma_unmap()
frees pool->dma_pages?
Because virtnet_xsk_pools_unbind() runs while netif_running(vi->dev) is
true and before virtnet_freeze_down() closes the device,
virtnet_rq_bind_xsk_pool(vi, rq, NULL) clears rq->xsk_pool and calls
virtnet_rx_resume(vi, rq, true), which refills rq->vq with page_pool
buffers and re-enables and schedules rq->napi before xsk_pool_dma_unmap()
runs. Unlike normal socket teardown, the AF_XDP socket is still bound in
xsks_map, sorq->xdp_prog can redirect received page_pool packets into
xsk_rcv(), which falls back to copy mode (__xsk_rcv() -> xsk_buff_alloc())
while xsk_pool_dma_unmap() concurrently tears down the pool's DMA state:
CPU0 CPU1 (<softirq>)
virtnet_freeze()
virtnet_xsk_pools_unbind()
virtnet_xsk_pool_disable()
virtnet_rq_bind_xsk_pool(..., NULL)
rq->xsk_pool = NULL
virtnet_rx_resume()
virtnet_napi_enable(rq)
virtnet_poll()
...
xsk_rcv()
__xsk_rcv()
xsk_buff_alloc(xs->pool)
xp_alloc()
xsk_pool_dma_unmap(pool, 0)
xp_dma_unmap()
kvfree(pool->dma_pages)
xp_get_xskb()
xp_init_xskb_dma(...,
pool->dma_pages, ...)
<-- use-after-free
pool->dma_pages = NULL
pool->dev = NULL
In addition to reading freed pool->dma_pages in xp_check_unaligned() and
xp_init_xskb_dma(), xp_alloc() can call xp_dma_sync_for_cpu() as pool->dev
and the DMA mappings are being cleared, or leave uninitialized xskb->dma
entries on pool->free_list when the socket consumes the copied frame.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261107125723.33547-1-yanhaishuang@cmss.chinamobile.com?part=2
next prev parent reply other threads:[~2026-11-08 4:58 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-07 12:57 [PATCH net 0/2] virtio_net: AF_XDP zero-copy fixes for arm64 and suspend Haishuang Yan
2026-11-07 5:00 ` netdev-bot+sinfo
2026-11-07 12:57 ` [PATCH net 1/2] virtio_net: Don't DMA map the kernel image for the AF_XDP TX header Haishuang Yan
2026-11-08 5:41 ` netdev-bot+sashiko
2026-11-08 17:23 ` Haishuang Yan
2026-11-07 12:57 ` [PATCH net 2/2] virtio_net: Keep AF_XDP zero-copy pools bound across suspend Haishuang Yan
2026-11-08 4:58 ` sashiko-bot [this message]
2026-11-08 5:41 ` netdev-bot+sashiko
2026-11-08 17:23 ` Haishuang Yan
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-203566@kernel.org \
--to=sashiko-bot@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=eperezma@redhat.com \
--cc=mst@redhat.com \
--cc=sashiko-reviews@lists.linux.dev \
--cc=virtualization@lists.linux.dev \
--cc=yanhaishuang@cmss.chinamobile.com \
/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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox