diff options
| author | Jakub Kicinski <kuba@kernel.org> | 2026-10-27 16:49:38 -0700 |
|---|---|---|
| committer | Jakub Kicinski <kuba@kernel.org> | 2026-10-29 08:49:32 -0700 |
| commit | 1b2f02fb4ba92d01603c8261c94be8d348de65b0 (patch) | |
| tree | 7f00974e4597cce7964beaa2791abbca5549f121 | |
| parent | 09f10efee6f93863ea71489ce84563f4d51787f6 (diff) | |
| download | linux-1b2f02fb4ba92d01603c8261c94be8d348de65b0.tar.gz linux-1b2f02fb4ba92d01603c8261c94be8d348de65b0.zip | |
eth: bnxt: don't leak buffers when XDP agg buffer allocation fails
If a replacement agg buffer can't be allocated for a packet received
with XDP attached, bnxt_rx_pkt() drops the packet without returning
its head buffer to the ring. The head buffer leaks, and since rx_prod
still advances, the device gets a BD with a stale DMA address which
points at a buffer the driver no longer owns. When that BD completes
the driver finds no buffer in the slot.
The agg buffers consumed before the failing one have already been
replaced on the agg ring, they are referenced only by the frags of
the xdp_buff, which nobody frees. bnxt_xdp_buff_frags_free() would
skip them since the frags flag is set only on success, so set it
upfront.
Pointed out in LLM review of the first patch of the series.
Tested on a BCM57504 at MTU 9000 with a frags-capable XDP_PASS
program, receiving 8000B ICMPv6 echo requests (a head and two agg
buffers each) while page_pool_alloc_netmems() called from
bnxt_rx_agg_netmems_xdp() failed half of the time (bpftrace
override()). 44.7k packets were dropped, the page pools' inflight
count stayed flat and there were no KASAN or DEBUG_PAGEALLOC splats.
Fixes: 4c6c123c9af9 ("bnxt: adding bnxt_rx_agg_pages_xdp for aggregated xdp")
Reviewed-by: Michael Chan <michael.chan@broadcom.com>
Link: https://patch.msgid.link/20261027234940.1267225-4-kuba@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
| -rw-r--r-- | drivers/net/ethernet/broadcom/bnxt/bnxt.c | 17 |
1 files changed, 11 insertions, 6 deletions
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 2e7276801fdd..4607c31eedf8 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -1427,16 +1427,19 @@ static u32 bnxt_rx_agg_netmems_xdp(struct bnxt *bp, struct skb_shared_info *shinfo = xdp_get_shared_info_from_buff(xdp); u32 total_frag_len = 0; - if (!xdp_buff_has_frags(xdp)) + if (!xdp_buff_has_frags(xdp)) { + xdp_buff_set_frags_flag(xdp); shinfo->nr_frags = 0; + } total_frag_len = __bnxt_rx_agg_netmems(bp, cpr, idx, agg_bufs, tpa, NULL, xdp); - if (total_frag_len) { - xdp_buff_set_frags_flag(xdp); - shinfo->nr_frags = agg_bufs; - shinfo->xdp_frags_size = total_frag_len; + if (!total_frag_len) { + bnxt_xdp_buff_frags_free(cpr->bnapi->rx_ring, xdp); + return 0; } + shinfo->nr_frags = agg_bufs; + shinfo->xdp_frags_size = total_frag_len; return total_frag_len; } @@ -2293,8 +2296,10 @@ static int bnxt_rx_pkt(struct bnxt *bp, struct bnxt_cp_ring_info *cpr, cp_cons, agg_bufs, false); - if (!frag_len) + if (!frag_len) { + bnxt_reuse_rx_data(rxr, cons, data); goto oom_next_rx; + } } xdp_active = true; |
