From: Jakub Kicinski <kuba@kernel.org>
To: anthony.l.nguyen@intel.com
Cc: Jakub Kicinski <kuba@kernel.org>,
davem@davemloft.net, pabeni@redhat.com, edumazet@google.com,
andrew+netdev@lunn.ch, netdev@vger.kernel.org, tactii@gmail.com,
dima.ruinskiy@intel.com, aleksandr.loktionov@intel.com,
pmenzel@molgen.mpg.de, michalx.cohen@intel.com
Subject: Re: [PATCH net-next 10/15] i40e: prepare for XDP metadata ops support
Date: Mon, 10 Aug 2026 17:35:19 -0700 [thread overview]
Message-ID: <20260811003519.1056926-1-kuba@kernel.org> (raw)
In-Reply-To: <20260805213541.2281287-11-anthony.l.nguyen@intel.com>
This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
---
i40e: prepare for XDP metadata ops support
This adds struct i40e_xdp_buff, which wraps the xdp_buff stored on the Rx
ring together with a pointer to the Rx descriptor, and sets that pointer
before running the XDP program in both the normal and the AF_XDP zero-copy
receive paths. The ZC path also gains XSK_CHECK_PRIV_TYPE() to verify the
new struct fits in the xdp_buff_xsk cb area.
> diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c
> index 0cd0e5597c909..4b11ae9ed8d53 100644
> --- a/drivers/net/ethernet/intel/i40e/i40e_main.c
> +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c
> @@ -3622,7 +3622,7 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring)
> }
>
> skip:
> - xdp_init_buff(&ring->xdp, xdp_frame_sz, &ring->xdp_rxq);
> + xdp_init_buff(&ring->xdp_ctx.xdp, xdp_frame_sz, &ring->xdp_rxq);
[Severity: High]
This isn't a bug introduced by this patch, but can the state persisted in
rx_ring->xdp_ctx outlive the pages it points at?
i40e_clean_rx_ring() unmaps and drains every Rx page and resets the
next_to_* indices, but leaves the saved buffer alone:
drivers/net/ethernet/intel/i40e/i40e_txrx.c:i40e_clean_rx_ring() {
...
__page_frag_cache_drain(rx_bi->page, rx_bi->pagecnt_bias);
rx_bi->page = NULL;
rx_bi->page_offset = 0;
}
skip_free:
...
rx_ring->next_to_alloc = 0;
rx_ring->next_to_clean = 0;
rx_ring->next_to_process = 0;
rx_ring->next_to_use = 0;
}
On bring-up the only re-initialization is the xdp_init_buff() call above,
and that touches rxq, frame_sz and flags only:
include/net/xdp.h:xdp_init_buff() {
xdp->rxq = rxq;
...
xdp->frame_sz_flags_init = frame_sz;
...
}
So data, data_hard_start and the frag state survive an ifdown/ifup, MTU
change, ring resize or PF reset. If NAPI returned in the middle of a
multi-buffer packet (the state the comment above the member says is kept on
purpose), does the first iteration after the ring comes back up then take
the fragment-append branch on stale data?
drivers/net/ethernet/intel/i40e/i40e_txrx.c:i40e_clean_rx_irq() {
...
if (!xdp->data) {
...
xdp_prepare_buff(xdp, hard_start, offset, size, true);
} else if (i40e_add_xdp_frag(xdp, &nfrags, rx_buffer, size) &&
!neop) {
...
}
i40e_add_xdp_frag() derives sinfo from data_hard_start and writes nr_frags,
xdp_frags_size and a frag entry, which would land in a page already handed
back by __page_frag_cache_drain(), and the following i40e_build_skb() /
i40e_process_rx_buffs() would then operate on and drop a reference to that
page.
ice does clear the stash in this situation: ice_clean_rx_ring() calls
libeth_xdp_return_stash(&rx_ring->xdp), described as freeing an incomplete
buffer stash when the queue is about to be destroyed. Would clearing
xdp_ctx (data = NULL and the new desc = NULL) in i40e_clean_rx_ring() be
appropriate here?
The new desc pointer inherits the same lifetime problem: it is a raw
pointer into the descriptor ring that i40e_free_rx_resources() releases via
dma_free_coherent(), and it is only assigned on the EOP path, never
cleared.
>
> rx_ctx.dbuff = DIV_ROUND_UP(ring->rx_buf_len,
> BIT_ULL(I40E_RXQ_CTX_DBUFF_SHIFT));
> diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c
> index ef5e657816f00..7f68adb5109b0 100644
> --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c
> +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c
[ ... ]
> @@ -2530,6 +2531,8 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget,
> if (neop)
> continue;
>
> + xdp_ctx->desc = rx_desc;
> +
> xdp_res = i40e_run_xdp(rx_ring, xdp, xdp_prog);
>
> if (xdp_res) {
> diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.h b/drivers/net/ethernet/intel/i40e/i40e_txrx.h
> index 1e5fd63d47f47..bb741ff3e5f2c 100644
> --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.h
> +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.h
> @@ -283,6 +283,11 @@ struct i40e_rx_buffer {
> __u32 page_count;
> };
>
> +struct i40e_xdp_buff {
> + struct xdp_buff xdp;
> + const union i40e_rx_desc *desc;
> +};
> +
> struct i40e_queue_stats {
> u64 packets;
> u64 bytes;
> @@ -345,7 +350,7 @@ struct i40e_ring {
> * and to resume packet building for this ring in the next call to
> * i40e_clean_rx_ring_irq().
> */
> - struct xdp_buff xdp;
> + struct i40e_xdp_buff xdp_ctx;
[Severity: Low]
This is a pre-existing issue, not something this patch introduced, but the
comment just above this member names i40e_clean_rx_ring_irq() twice and
that function does not exist; git grep for it matches only these two
comment lines. The function that saves and resumes this state is
i40e_clean_rx_irq(). Since the hunk already touches the member, would it
make sense to fix the name, and also the "Storing xdp_buff on ring" wording
now that the member is a struct i40e_xdp_buff?
While there, could the lifetime of the new desc field be described? It is
set only on the EOP path in i40e_clean_rx_irq() and is not initialized by
the xdp_init_buff() call in i40e_configure_rx_ring().
>
> /* Next descriptor to be processed; next_to_clean is updated only on
> * processing EOP descriptor
> diff --git a/drivers/net/ethernet/intel/i40e/i40e_xsk.c b/drivers/net/ethernet/intel/i40e/i40e_xsk.c
> index 9f47388eaba53..51a05ce4c7ce8 100644
> --- a/drivers/net/ethernet/intel/i40e/i40e_xsk.c
> +++ b/drivers/net/ethernet/intel/i40e/i40e_xsk.c
[ ... ]
> @@ -472,6 +482,8 @@ int i40e_clean_rx_irq_zc(struct i40e_ring *rx_ring, int budget)
> if (i40e_is_non_eop(rx_ring, rx_desc))
> continue;
>
> + xsk_buff_to_i40e_ctx(first)->desc = rx_desc;
> +
> xdp_res = i40e_run_xdp_zc(rx_ring, first, xdp_prog);
> i40e_handle_xdp_result_zc(rx_ring, first, rx_desc, &rx_packets,
> &rx_bytes, xdp_res, &failure);
[Severity: High]
This isn't a bug introduced by this patch either, but can "first" here
already be a freed xsk buffer, so that this new store writes into the cb of
a buffer that is back on the pool free list?
Two places in i40e_clean_rx_irq_zc() free a buffer without advancing
next_to_clean and without clearing the rx_bi_zc slot. The programming
status branch:
if (i40e_rx_is_programming_status(qword)) {
...
bi = *i40e_rx_bi(rx_ring, next_to_process);
xsk_buff_free(bi);
if (++next_to_process == count)
next_to_process = 0;
continue;
}
and the multi-buffer error path:
if (!first)
first = bi;
else if (!xsk_buff_add_frag(first, bi)) {
xsk_buff_free(first);
break;
}
If the loop then exits at:
size = FIELD_GET(I40E_RXD_QW1_LENGTH_PBUF_MASK, qword);
if (!size)
break;
the function writes back rx_ring->next_to_clean unchanged while
next_to_process has moved on, and i40e_alloc_rx_buffers_zc() cannot refill
that slot because I40E_DESC_UNUSED stops at next_to_clean - 1. So does
rx_bi_zc[next_to_clean] keep pointing at a freed buffer that the next poll
picks up?
if (next_to_process != next_to_clean)
first = *i40e_rx_bi(rx_ring, next_to_clean);
That buffer is then passed to xsk_buff_set_size(), xsk_buff_add_frag() and
i40e_run_xdp_zc(), and with this patch also written through by
xsk_buff_to_i40e_ctx(first)->desc = rx_desc.
On teardown, does the same stale pointer get freed a second time?
i40e_xsk_clean_rx_ring() walks [next_to_clean, next_to_use):
while (ntc != ntu) {
struct xdp_buff *rx_bi = *i40e_rx_bi(rx_ring, ntc);
xsk_buff_free(rx_bi);
and it runs before i40e_clear_rx_bi_zc() zeroes the array.
ice_clean_rx_irq_zc() frees, clears first, and still advances ntc:
} else if (likely(size) && !xsk_buff_add_frag(first, xdp)) {
xsk_buff_free(first);
first = NULL;
}
if (++ntc == cnt)
ntc = 0;
Would the same handling be right for i40e?
The multi-buffer error leg looks hard to reach on this hardware, since
xsk_buff_add_frag() only fails at MAX_SKB_FRAGS while rx_ctx.rxmax is
programmed from I40E_MAX_CHAINED_RX_BUFFERS, but the programming status leg
needs no oversized frame.
next prev parent reply other threads:[~2026-08-11 0:35 UTC|newest]
Thread overview: 27+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-05 21:35 [PATCH net-next 00/15][pull request] Intel Wired LAN Driver Updates 2026-08-05 (ice, i40e, igc, e1000e) Tony Nguyen
2026-08-05 21:35 ` [PATCH net-next 01/15] ice: add support for unmanaged DPLL on E830 NIC Tony Nguyen
2026-08-11 0:33 ` Jakub Kicinski
2026-08-05 21:35 ` [PATCH net-next 02/15] ice: always do GCS if hardware supports it Tony Nguyen
2026-08-11 0:35 ` Jakub Kicinski
2026-08-05 21:35 ` [PATCH net-next 03/15] ice: use NETIF_F_HW_CSUM instead of IP/IPV6 Tony Nguyen
2026-08-11 0:35 ` Jakub Kicinski
2026-08-05 21:35 ` [PATCH net-next 04/15] virtchnl: add VIRTCHNL_VLAN_ETHERTYPE_88E7 support Tony Nguyen
2026-08-11 0:35 ` Jakub Kicinski
2026-08-05 21:35 ` [PATCH net-next 05/15] ice: add 0x88E7 handling to SW validation paths Tony Nguyen
2026-08-11 0:35 ` Jakub Kicinski
2026-08-05 21:35 ` [PATCH net-next 06/15] ice: reduce loglevel to debug for 'Can't delete DSCP' message Tony Nguyen
2026-08-05 21:35 ` [PATCH net-next 07/15] ice: use ice_fill_eth_hdr() in ice_fill_sw_rule() Tony Nguyen
2026-08-05 21:35 ` [PATCH net-next 08/15] ice: increase OICR interrupt moderation rate to 20K interrupts/sec Tony Nguyen
2026-08-05 21:35 ` [PATCH net-next 09/15] ice: add rx timestamp tracepoint for debugging Tony Nguyen
2026-08-05 21:35 ` [PATCH net-next 10/15] i40e: prepare for XDP metadata ops support Tony Nguyen
2026-08-06 21:36 ` sashiko-bot
2026-08-07 19:00 ` Kohei Enju
2026-08-11 0:35 ` Jakub Kicinski [this message]
2026-08-05 21:35 ` [PATCH net-next 11/15] i40e: add support for bpf_xdp_metadata_rx_hash() Tony Nguyen
2026-08-05 21:35 ` [PATCH net-next 12/15] i40e: add support for bpf_xdp_metadata_rx_vlan_tag() Tony Nguyen
2026-08-05 21:35 ` [PATCH net-next 13/15] i40e: Avoid repeating RX filter warning Tony Nguyen
2026-08-05 21:35 ` [PATCH net-next 14/15] igc: Support ACPI-based MAC pass-through Tony Nguyen
2026-08-11 0:35 ` Jakub Kicinski
2026-08-05 21:35 ` [PATCH net-next 15/15] e1000e: Avoid DMA re-mapping on RX copybreak Tony Nguyen
2026-08-11 0:35 ` Jakub Kicinski
2026-08-11 0:35 ` [PATCH net-next 00/15][pull request] Intel Wired LAN Driver Updates 2026-08-05 (ice, i40e, igc, e1000e) Jakub Kicinski
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=20260811003519.1056926-1-kuba@kernel.org \
--to=kuba@kernel.org \
--cc=aleksandr.loktionov@intel.com \
--cc=andrew+netdev@lunn.ch \
--cc=anthony.l.nguyen@intel.com \
--cc=davem@davemloft.net \
--cc=dima.ruinskiy@intel.com \
--cc=edumazet@google.com \
--cc=michalx.cohen@intel.com \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=pmenzel@molgen.mpg.de \
--cc=tactii@gmail.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 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.