summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJakub Kicinski <kuba@kernel.org>2026-10-27 16:49:38 -0700
committerJakub Kicinski <kuba@kernel.org>2026-10-29 08:49:32 -0700
commit1b2f02fb4ba92d01603c8261c94be8d348de65b0 (patch)
tree7f00974e4597cce7964beaa2791abbca5549f121
parent09f10efee6f93863ea71489ce84563f4d51787f6 (diff)
downloadlinux-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.c17
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;