Re: [PATCH net] net: mana: fix XDP_REDIRECT of pre-allocated RX buffers
From: Maciej Fijalkowski
Date: Wed Nov 04 2026 - 11:30:44 EST
On Mon, Nov 02, 2026 at 06:43:24AM +0000, Orgad Shaneh wrote:
> mana_pre_alloc_rxbufs() fills the RX rings with plain pages from
> dev_alloc_pages() (from_pool == false), but the rxq is registered with
> MEM_TYPE_PAGE_POOL and mana_run_xdp() builds every xdp_buff with that
> rxq info. mana_rx_skb() frees a !from_pool buffer correctly on
> XDP_PASS, XDP_TX and XDP_DROP, but a redirected buffer is freed through
> its memory model: __xdp_return() hands the plain page to
> page_pool_put_full_netmem(). This triggers the WARN in
> include/net/page_pool/helpers.h, breaks the page refcount, RX refills
> fail and the queue stops receiving.
>
> The pre-allocated buffers fill the whole ring of every rxq created by
> an MTU change, by ethtool -L / -G and, since
> commit 730ff06d3f5c ("net: mana: Use page pool fragments for RX buffers
> instead of full pages to improve memory efficiency."), by every XDP
> attach and detach. So on current kernels the first ring of redirected
> packets after attaching an XDP program always hits this.
>
> Reproduced on 7.2.11, and on 6.12.113 after ethtool -L, on an Azure
> MANA VF: an XDP program that redirects UDP to an XSKMAP with an AF_XDP
> socket in copy mode gives WARNINGs at __xdp_return+0x249 (stack
> __xsk_map_redirect, xdp_do_redirect, mana_run_xdp, mana_poll), then
> the queue stops.
>
> Give each rxq a second xdp_rxq_info registered with
> MEM_TYPE_PAGE_SHARED, the model these buffers had before the page pool
> was added, and use it for buffers that are not from the pool. Those
> buffers are already DMA-unmapped by mana_refill_rx_oob() before XDP
> runs, and page_frag_free() frees them whatever their order. Allocating
> the pre-allocated buffers from the page pool instead would need the
> pools to exist before the rxqs and to outlive them, a much larger
> change.
>
> Found, fixed and tested with an AI coding assistant (Claude Code).
>
> Fixes: b1d13f7a3b53 ("net: mana: Add page pool for RX buffers")
> Signed-off-by: Orgad Shaneh <orgads@xxxxxxxxx>
> ---
> Tested on an Azure D2s_v6 MANA VF, net at cb284914e749 with and
> without this patch (same config), XDP program redirecting UDP to an
> AF_XDP socket in copy mode on queue 0, 25k pps offered right after
> attach:
> - without: 41 WARNINGs at __xdp_return+0x252, 0 packets relayed,
> hc_rx_discards_no_wqe 301470.
> - with: no WARNING, 25k of 25k pps relayed, 0 loss; also over three
> more attach/detach cycles, two ethtool -L combined 1/2 cycles and an
> MTU 4000/1500 change before attach. MemAvailable unchanged.
> W=1 build of the mana directories: no new warnings.
>
> drivers/net/ethernet/microsoft/mana/mana_bpf.c | 9 +++++++--
> drivers/net/ethernet/microsoft/mana/mana_en.c | 11 ++++++++++-
> include/net/mana/mana.h | 5 ++++-
> 3 files changed, 21 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/net/ethernet/microsoft/mana/mana_bpf.c b/drivers/net/ethernet/microsoft/mana/mana_bpf.c
> index 7c55f8724441..494267b3069a 100644
> --- a/drivers/net/ethernet/microsoft/mana/mana_bpf.c
> +++ b/drivers/net/ethernet/microsoft/mana/mana_bpf.c
> @@ -78,7 +78,8 @@ int mana_xdp_xmit(struct net_device *ndev, int n, struct xdp_frame **frames,
> }
>
> u32 mana_run_xdp(struct net_device *ndev, struct mana_rxq *rxq,
> - struct xdp_buff *xdp, void *buf_va, uint pkt_len)
> + struct xdp_buff *xdp, void *buf_va, bool from_pool,
> + uint pkt_len)
> {
> struct mana_stats_rx *rx_stats;
> struct bpf_prog *prog;
> @@ -90,7 +91,11 @@ u32 mana_run_xdp(struct net_device *ndev, struct mana_rxq *rxq,
> if (!prog)
> goto out;
>
> - xdp_init_buff(xdp, PAGE_SIZE, &rxq->xdp_rxq);
> + /* A redirected buffer is freed by its memory model, so a buffer not
> + * from the page pool must not claim MEM_TYPE_PAGE_POOL.
> + */
> + xdp_init_buff(xdp, PAGE_SIZE,
> + from_pool ? &rxq->xdp_rxq : &rxq->xdp_rxq_shared);
> xdp_prepare_buff(xdp, buf_va, rxq->headroom, pkt_len, true);
>
> act = bpf_prog_run_xdp(prog, xdp);
> diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c
> index 6b7c027e497e..7547aeb3235d 100644
> --- a/drivers/net/ethernet/microsoft/mana/mana_en.c
> +++ b/drivers/net/ethernet/microsoft/mana/mana_en.c
> @@ -2157,7 +2157,7 @@ static void mana_rx_skb(void *buf_va, bool from_pool,
> return;
> }
>
> - act = mana_run_xdp(ndev, rxq, &xdp, buf_va, pkt_len);
> + act = mana_run_xdp(ndev, rxq, &xdp, buf_va, from_pool, pkt_len);
I wonder if it wouldn't be better to work with rxoob objects which seem to
carry the from_pool setting?
>
> if (act == XDP_REDIRECT && !rxq->xdp_rc)
> return;
> @@ -2947,6 +2947,9 @@ static void mana_destroy_rxq(struct mana_port_context *apc,
> if (xdp_rxq_info_is_reg(&rxq->xdp_rxq))
> xdp_rxq_info_unreg(&rxq->xdp_rxq);
>
> + if (xdp_rxq_info_is_reg(&rxq->xdp_rxq_shared))
> + xdp_rxq_info_unreg(&rxq->xdp_rxq_shared);
> +
> if (rxq->rxobj != INVALID_MANA_HANDLE)
> mana_destroy_wq_obj(apc, GDMA_RQ, rxq->rxobj);
>
> @@ -3225,6 +3228,12 @@ static struct mana_rxq *mana_create_rxq(struct mana_port_context *apc,
> WARN_ON(xdp_rxq_info_reg_mem_model(&rxq->xdp_rxq, MEM_TYPE_PAGE_POOL,
> rxq->page_pool));
>
> + /* The pre-allocated buffers (mana_pre_alloc_rxbufs) are plain pages */
> + WARN_ON(xdp_rxq_info_reg(&rxq->xdp_rxq_shared, ndev, rxq_idx,
> + cq->napi.napi_id));
> + WARN_ON(xdp_rxq_info_reg_mem_model(&rxq->xdp_rxq_shared,
> + MEM_TYPE_PAGE_SHARED, NULL));
> +
> /* Initialize the DIM work before enabling NAPI, so that a poll
> * cannot reach net_dim() with an uninitialized cq->dim.work.
> */
> diff --git a/include/net/mana/mana.h b/include/net/mana/mana.h
> index 71d726b1f784..11ceb5946ee0 100644
> --- a/include/net/mana/mana.h
> +++ b/include/net/mana/mana.h
> @@ -420,6 +420,8 @@ struct mana_rxq {
>
> struct bpf_prog __rcu *bpf_prog;
> struct xdp_rxq_info xdp_rxq;
> + /* For buffers not from the page pool (from_pool == false) */
> + struct xdp_rxq_info xdp_rxq_shared;
> void *xdp_save_va; /* for reusing */
> bool xdp_flush;
> int xdp_rc; /* XDP redirect return code */
> @@ -695,7 +697,8 @@ void mana_xdp_tx(struct sk_buff *skb, struct net_device *ndev);
> int mana_xdp_xmit(struct net_device *ndev, int n, struct xdp_frame **frames,
> u32 flags);
> u32 mana_run_xdp(struct net_device *ndev, struct mana_rxq *rxq,
> - struct xdp_buff *xdp, void *buf_va, uint pkt_len);
> + struct xdp_buff *xdp, void *buf_va, bool from_pool,
> + uint pkt_len);
> struct bpf_prog *mana_xdp_get(struct mana_port_context *apc);
> void mana_chn_setxdp(struct mana_port_context *apc, struct bpf_prog *prog);
> int mana_bpf(struct net_device *ndev, struct netdev_bpf *bpf);
>
> base-commit: cb284914e749388eef402938bdb3115fe1f6d337
> --
> 2.52.0
>
>