* [PATCH bpf v4 1/9] xdp: use modulo operation to calculate XDP frag tailroom
2026-03-02 16:17 [PATCH bpf v4 0/9] Address XDP frags having negative tailroom Larysa Zaremba
@ 2026-03-02 16:17 ` Larysa Zaremba
2026-03-02 16:17 ` [PATCH bpf v4 2/9] xsk: introduce helper to determine rxq->frag_size Larysa Zaremba
` (8 subsequent siblings)
9 siblings, 0 replies; 13+ messages in thread
From: Larysa Zaremba @ 2026-03-02 16:17 UTC (permalink / raw)
To: bpf
Cc: Larysa Zaremba, Claudiu Manoil, Vladimir Oltean, Wei Fang,
Clark Wang, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Tony Nguyen, Przemek Kitszel,
Alexei Starovoitov, Daniel Borkmann, Jesper Dangaard Brouer,
John Fastabend, Stanislav Fomichev, Andrii Nakryiko,
Martin KaFai Lau, Eduard Zingerman, Song Liu, Yonghong Song,
KP Singh, Hao Luo, Jiri Olsa, Simon Horman, Shuah Khan,
Alexander Lobakin, Maciej Fijalkowski,
Bastien Curutchet (eBPF Foundation), Tushar Vyavahare, Jason Xing,
Ricardo B. Marlière, Eelco Chaudron, Lorenzo Bianconi,
Toke Hoiland-Jorgensen, imx, netdev, linux-kernel,
intel-wired-lan, linux-kselftest, Aleksandr Loktionov,
Dragos Tatulea
The current formula for calculating XDP tailroom in mbuf packets works only
if each frag has its own page (if rxq->frag_size is PAGE_SIZE), this
defeats the purpose of the parameter overall and without any indication
leads to negative calculated tailroom on at least half of frags, if shared
pages are used.
There are not many drivers that set rxq->frag_size. Among them:
* i40e and enetc always split page uniformly between frags, use shared
pages
* ice uses page_pool frags via libeth, those are power-of-2 and uniformly
distributed across page
* idpf has variable frag_size with XDP on, so current API is not applicable
* mlx5, mtk and mvneta use PAGE_SIZE or 0 as frag_size for page_pool
As for AF_XDP ZC, only ice, i40e and idpf declare frag_size for it. Modulo
operation yields good results for aligned chunks, they are all power-of-2,
between 2K and PAGE_SIZE. Formula without modulo fails when chunk_size is
2K. Buffers in unaligned mode are not distributed uniformly, so modulo
operation would not work.
To accommodate unaligned buffers, we could define frag_size as
data + tailroom, and hence do not subtract offset when calculating
tailroom, but this would necessitate more changes in the drivers.
Define rxq->frag_size as an even portion of a page that fully belongs to a
single frag. When calculating tailroom, locate the data start within such
portion by performing a modulo operation on page offset.
Fixes: bf25146a5595 ("bpf: add frags support to the bpf_xdp_adjust_tail() API")
Acked-by: Jakub Kicinski <kuba@kernel.org>
Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
Signed-off-by: Larysa Zaremba <larysa.zaremba@intel.com>
---
net/core/filter.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/net/core/filter.c b/net/core/filter.c
index 0d5d5a17acb2..d6fafb3633b0 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -4155,7 +4155,8 @@ static int bpf_xdp_frags_increase_tail(struct xdp_buff *xdp, int offset)
if (!rxq->frag_size || rxq->frag_size > xdp->frame_sz)
return -EOPNOTSUPP;
- tailroom = rxq->frag_size - skb_frag_size(frag) - skb_frag_off(frag);
+ tailroom = rxq->frag_size - skb_frag_size(frag) -
+ skb_frag_off(frag) % rxq->frag_size;
if (unlikely(offset > tailroom))
return -EINVAL;
--
2.52.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH bpf v4 2/9] xsk: introduce helper to determine rxq->frag_size
2026-03-02 16:17 [PATCH bpf v4 0/9] Address XDP frags having negative tailroom Larysa Zaremba
2026-03-02 16:17 ` [PATCH bpf v4 1/9] xdp: use modulo operation to calculate XDP frag tailroom Larysa Zaremba
@ 2026-03-02 16:17 ` Larysa Zaremba
2026-03-02 16:17 ` [PATCH bpf v4 3/9] ice: fix rxq info registering in mbuf packets Larysa Zaremba
` (7 subsequent siblings)
9 siblings, 0 replies; 13+ messages in thread
From: Larysa Zaremba @ 2026-03-02 16:17 UTC (permalink / raw)
To: bpf
Cc: Larysa Zaremba, Claudiu Manoil, Vladimir Oltean, Wei Fang,
Clark Wang, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Tony Nguyen, Przemek Kitszel,
Alexei Starovoitov, Daniel Borkmann, Jesper Dangaard Brouer,
John Fastabend, Stanislav Fomichev, Andrii Nakryiko,
Martin KaFai Lau, Eduard Zingerman, Song Liu, Yonghong Song,
KP Singh, Hao Luo, Jiri Olsa, Simon Horman, Shuah Khan,
Alexander Lobakin, Maciej Fijalkowski,
Bastien Curutchet (eBPF Foundation), Tushar Vyavahare, Jason Xing,
Ricardo B. Marlière, Eelco Chaudron, Lorenzo Bianconi,
Toke Hoiland-Jorgensen, imx, netdev, linux-kernel,
intel-wired-lan, linux-kselftest, Aleksandr Loktionov,
Dragos Tatulea
rxq->frag_size is basically a step between consecutive strictly aligned
frames. In ZC mode, chunk size fits exactly, but if chunks are unaligned,
there is no safe way to determine accessible space to grow tailroom.
Report frag_size to be zero, if chunks are unaligned, chunk_size otherwise.
Fixes: 24ea50127ecf ("xsk: support mbuf on ZC RX")
Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
Signed-off-by: Larysa Zaremba <larysa.zaremba@intel.com>
---
include/net/xdp_sock_drv.h | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/include/net/xdp_sock_drv.h b/include/net/xdp_sock_drv.h
index 242e34f771cc..09d972f4bd60 100644
--- a/include/net/xdp_sock_drv.h
+++ b/include/net/xdp_sock_drv.h
@@ -51,6 +51,11 @@ static inline u32 xsk_pool_get_rx_frame_size(struct xsk_buff_pool *pool)
return xsk_pool_get_chunk_size(pool) - xsk_pool_get_headroom(pool);
}
+static inline u32 xsk_pool_get_rx_frag_step(struct xsk_buff_pool *pool)
+{
+ return pool->unaligned ? 0 : xsk_pool_get_chunk_size(pool);
+}
+
static inline void xsk_pool_set_rxq_info(struct xsk_buff_pool *pool,
struct xdp_rxq_info *rxq)
{
@@ -337,6 +342,11 @@ static inline u32 xsk_pool_get_rx_frame_size(struct xsk_buff_pool *pool)
return 0;
}
+static inline u32 xsk_pool_get_rx_frag_step(struct xsk_buff_pool *pool)
+{
+ return 0;
+}
+
static inline void xsk_pool_set_rxq_info(struct xsk_buff_pool *pool,
struct xdp_rxq_info *rxq)
{
--
2.52.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH bpf v4 3/9] ice: fix rxq info registering in mbuf packets
2026-03-02 16:17 [PATCH bpf v4 0/9] Address XDP frags having negative tailroom Larysa Zaremba
2026-03-02 16:17 ` [PATCH bpf v4 1/9] xdp: use modulo operation to calculate XDP frag tailroom Larysa Zaremba
2026-03-02 16:17 ` [PATCH bpf v4 2/9] xsk: introduce helper to determine rxq->frag_size Larysa Zaremba
@ 2026-03-02 16:17 ` Larysa Zaremba
2026-03-02 16:17 ` [PATCH bpf v4 4/9] ice: change XDP RxQ frag_size from DMA write length to xdp.frame_sz Larysa Zaremba
` (6 subsequent siblings)
9 siblings, 0 replies; 13+ messages in thread
From: Larysa Zaremba @ 2026-03-02 16:17 UTC (permalink / raw)
To: bpf
Cc: Larysa Zaremba, Claudiu Manoil, Vladimir Oltean, Wei Fang,
Clark Wang, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Tony Nguyen, Przemek Kitszel,
Alexei Starovoitov, Daniel Borkmann, Jesper Dangaard Brouer,
John Fastabend, Stanislav Fomichev, Andrii Nakryiko,
Martin KaFai Lau, Eduard Zingerman, Song Liu, Yonghong Song,
KP Singh, Hao Luo, Jiri Olsa, Simon Horman, Shuah Khan,
Alexander Lobakin, Maciej Fijalkowski,
Bastien Curutchet (eBPF Foundation), Tushar Vyavahare, Jason Xing,
Ricardo B. Marlière, Eelco Chaudron, Lorenzo Bianconi,
Toke Hoiland-Jorgensen, imx, netdev, linux-kernel,
intel-wired-lan, linux-kselftest, Aleksandr Loktionov,
Dragos Tatulea
XDP RxQ info contains frag_size, which depends on the MTU. This makes the
old way of registering RxQ info before calculating new buffer sizes
invalid. Currently, it leads to frag_size being outdated, making it
sometimes impossible to grow tailroom in a mbuf packet. E.g. fragments are
actually 3K+, but frag size is still as if MTU was 1500.
Always register new XDP RxQ info after reconfiguring memory pools.
Fixes: 2fba7dc5157b ("ice: Add support for XDP multi-buffer on Rx side")
Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
Signed-off-by: Larysa Zaremba <larysa.zaremba@intel.com>
---
drivers/net/ethernet/intel/ice/ice_base.c | 26 ++++++--------------
drivers/net/ethernet/intel/ice/ice_ethtool.c | 1 +
drivers/net/ethernet/intel/ice/ice_txrx.c | 3 ++-
drivers/net/ethernet/intel/ice/ice_xsk.c | 3 +++
4 files changed, 13 insertions(+), 20 deletions(-)
diff --git a/drivers/net/ethernet/intel/ice/ice_base.c b/drivers/net/ethernet/intel/ice/ice_base.c
index eb77dfa934aa..7a5468db6a67 100644
--- a/drivers/net/ethernet/intel/ice/ice_base.c
+++ b/drivers/net/ethernet/intel/ice/ice_base.c
@@ -663,23 +663,12 @@ static int ice_vsi_cfg_rxq(struct ice_rx_ring *ring)
int err;
if (ring->vsi->type == ICE_VSI_PF || ring->vsi->type == ICE_VSI_SF) {
- if (!xdp_rxq_info_is_reg(&ring->xdp_rxq)) {
- err = __xdp_rxq_info_reg(&ring->xdp_rxq, ring->netdev,
- ring->q_index,
- ring->q_vector->napi.napi_id,
- ring->rx_buf_len);
- if (err)
- return err;
- }
-
ice_rx_xsk_pool(ring);
err = ice_realloc_rx_xdp_bufs(ring, ring->xsk_pool);
if (err)
return err;
if (ring->xsk_pool) {
- xdp_rxq_info_unreg(&ring->xdp_rxq);
-
rx_buf_len =
xsk_pool_get_rx_frame_size(ring->xsk_pool);
err = __xdp_rxq_info_reg(&ring->xdp_rxq, ring->netdev,
@@ -702,14 +691,13 @@ static int ice_vsi_cfg_rxq(struct ice_rx_ring *ring)
if (err)
return err;
- if (!xdp_rxq_info_is_reg(&ring->xdp_rxq)) {
- err = __xdp_rxq_info_reg(&ring->xdp_rxq, ring->netdev,
- ring->q_index,
- ring->q_vector->napi.napi_id,
- ring->rx_buf_len);
- if (err)
- goto err_destroy_fq;
- }
+ err = __xdp_rxq_info_reg(&ring->xdp_rxq, ring->netdev,
+ ring->q_index,
+ ring->q_vector->napi.napi_id,
+ ring->rx_buf_len);
+ if (err)
+ goto err_destroy_fq;
+
xdp_rxq_info_attach_page_pool(&ring->xdp_rxq,
ring->pp);
}
diff --git a/drivers/net/ethernet/intel/ice/ice_ethtool.c b/drivers/net/ethernet/intel/ice/ice_ethtool.c
index 0ea64b330614..ca4fcd6b5117 100644
--- a/drivers/net/ethernet/intel/ice/ice_ethtool.c
+++ b/drivers/net/ethernet/intel/ice/ice_ethtool.c
@@ -3338,6 +3338,7 @@ ice_set_ringparam(struct net_device *netdev, struct ethtool_ringparam *ring,
rx_rings[i].cached_phctime = pf->ptp.cached_phc_time;
rx_rings[i].desc = NULL;
rx_rings[i].xdp_buf = NULL;
+ rx_rings[i].xdp_rxq = (struct xdp_rxq_info){ };
/* this is to allow wr32 to have something to write to
* during early allocation of Rx buffers
diff --git a/drivers/net/ethernet/intel/ice/ice_txrx.c b/drivers/net/ethernet/intel/ice/ice_txrx.c
index a5bbce68f76c..5fbff85aaa6e 100644
--- a/drivers/net/ethernet/intel/ice/ice_txrx.c
+++ b/drivers/net/ethernet/intel/ice/ice_txrx.c
@@ -560,7 +560,8 @@ void ice_clean_rx_ring(struct ice_rx_ring *rx_ring)
i = 0;
}
- if (rx_ring->vsi->type == ICE_VSI_PF &&
+ if ((rx_ring->vsi->type == ICE_VSI_PF ||
+ rx_ring->vsi->type == ICE_VSI_SF) &&
xdp_rxq_info_is_reg(&rx_ring->xdp_rxq)) {
xdp_rxq_info_detach_mem_model(&rx_ring->xdp_rxq);
xdp_rxq_info_unreg(&rx_ring->xdp_rxq);
diff --git a/drivers/net/ethernet/intel/ice/ice_xsk.c b/drivers/net/ethernet/intel/ice/ice_xsk.c
index c673094663a3..0643017541c3 100644
--- a/drivers/net/ethernet/intel/ice/ice_xsk.c
+++ b/drivers/net/ethernet/intel/ice/ice_xsk.c
@@ -899,6 +899,9 @@ void ice_xsk_clean_rx_ring(struct ice_rx_ring *rx_ring)
u16 ntc = rx_ring->next_to_clean;
u16 ntu = rx_ring->next_to_use;
+ if (xdp_rxq_info_is_reg(&rx_ring->xdp_rxq))
+ xdp_rxq_info_unreg(&rx_ring->xdp_rxq);
+
while (ntc != ntu) {
struct xdp_buff *xdp = *ice_xdp_buf(rx_ring, ntc);
--
2.52.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH bpf v4 4/9] ice: change XDP RxQ frag_size from DMA write length to xdp.frame_sz
2026-03-02 16:17 [PATCH bpf v4 0/9] Address XDP frags having negative tailroom Larysa Zaremba
` (2 preceding siblings ...)
2026-03-02 16:17 ` [PATCH bpf v4 3/9] ice: fix rxq info registering in mbuf packets Larysa Zaremba
@ 2026-03-02 16:17 ` Larysa Zaremba
2026-03-02 16:17 ` [PATCH bpf v4 5/9] i40e: fix registering XDP RxQ info Larysa Zaremba
` (5 subsequent siblings)
9 siblings, 0 replies; 13+ messages in thread
From: Larysa Zaremba @ 2026-03-02 16:17 UTC (permalink / raw)
To: bpf
Cc: Larysa Zaremba, Claudiu Manoil, Vladimir Oltean, Wei Fang,
Clark Wang, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Tony Nguyen, Przemek Kitszel,
Alexei Starovoitov, Daniel Borkmann, Jesper Dangaard Brouer,
John Fastabend, Stanislav Fomichev, Andrii Nakryiko,
Martin KaFai Lau, Eduard Zingerman, Song Liu, Yonghong Song,
KP Singh, Hao Luo, Jiri Olsa, Simon Horman, Shuah Khan,
Alexander Lobakin, Maciej Fijalkowski,
Bastien Curutchet (eBPF Foundation), Tushar Vyavahare, Jason Xing,
Ricardo B. Marlière, Eelco Chaudron, Lorenzo Bianconi,
Toke Hoiland-Jorgensen, imx, netdev, linux-kernel,
intel-wired-lan, linux-kselftest, Aleksandr Loktionov,
Dragos Tatulea
The only user of frag_size field in XDP RxQ info is
bpf_xdp_frags_increase_tail(). It clearly expects whole buff size instead
of DMA write size. Different assumptions in ice driver configuration lead
to negative tailroom.
This allows to trigger kernel panic, when using
XDP_ADJUST_TAIL_GROW_MULTI_BUFF xskxceiver test and changing packet size to
6912 and the requested offset to a huge value, e.g.
XSK_UMEM__MAX_FRAME_SIZE * 100.
Due to other quirks of the ZC configuration in ice, panic is not observed
in ZC mode, but tailroom growing still fails when it should not.
Use fill queue buffer truesize instead of DMA write size in XDP RxQ info.
Fix ZC mode too by using the new helper.
Fixes: 2fba7dc5157b ("ice: Add support for XDP multi-buffer on Rx side")
Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
Signed-off-by: Larysa Zaremba <larysa.zaremba@intel.com>
---
drivers/net/ethernet/intel/ice/ice_base.c | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/drivers/net/ethernet/intel/ice/ice_base.c b/drivers/net/ethernet/intel/ice/ice_base.c
index 7a5468db6a67..9f9189a65721 100644
--- a/drivers/net/ethernet/intel/ice/ice_base.c
+++ b/drivers/net/ethernet/intel/ice/ice_base.c
@@ -659,7 +659,6 @@ static int ice_vsi_cfg_rxq(struct ice_rx_ring *ring)
{
struct device *dev = ice_pf_to_dev(ring->vsi->back);
u32 num_bufs = ICE_DESC_UNUSED(ring);
- u32 rx_buf_len;
int err;
if (ring->vsi->type == ICE_VSI_PF || ring->vsi->type == ICE_VSI_SF) {
@@ -669,12 +668,12 @@ static int ice_vsi_cfg_rxq(struct ice_rx_ring *ring)
return err;
if (ring->xsk_pool) {
- rx_buf_len =
- xsk_pool_get_rx_frame_size(ring->xsk_pool);
+ u32 frag_size =
+ xsk_pool_get_rx_frag_step(ring->xsk_pool);
err = __xdp_rxq_info_reg(&ring->xdp_rxq, ring->netdev,
ring->q_index,
ring->q_vector->napi.napi_id,
- rx_buf_len);
+ frag_size);
if (err)
return err;
err = xdp_rxq_info_reg_mem_model(&ring->xdp_rxq,
@@ -694,7 +693,7 @@ static int ice_vsi_cfg_rxq(struct ice_rx_ring *ring)
err = __xdp_rxq_info_reg(&ring->xdp_rxq, ring->netdev,
ring->q_index,
ring->q_vector->napi.napi_id,
- ring->rx_buf_len);
+ ring->truesize);
if (err)
goto err_destroy_fq;
--
2.52.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH bpf v4 5/9] i40e: fix registering XDP RxQ info
2026-03-02 16:17 [PATCH bpf v4 0/9] Address XDP frags having negative tailroom Larysa Zaremba
` (3 preceding siblings ...)
2026-03-02 16:17 ` [PATCH bpf v4 4/9] ice: change XDP RxQ frag_size from DMA write length to xdp.frame_sz Larysa Zaremba
@ 2026-03-02 16:17 ` Larysa Zaremba
2026-03-02 16:17 ` [PATCH bpf v4 6/9] i40e: use xdp.frame_sz as XDP RxQ info frag_size Larysa Zaremba
` (4 subsequent siblings)
9 siblings, 0 replies; 13+ messages in thread
From: Larysa Zaremba @ 2026-03-02 16:17 UTC (permalink / raw)
To: bpf
Cc: Larysa Zaremba, Claudiu Manoil, Vladimir Oltean, Wei Fang,
Clark Wang, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Tony Nguyen, Przemek Kitszel,
Alexei Starovoitov, Daniel Borkmann, Jesper Dangaard Brouer,
John Fastabend, Stanislav Fomichev, Andrii Nakryiko,
Martin KaFai Lau, Eduard Zingerman, Song Liu, Yonghong Song,
KP Singh, Hao Luo, Jiri Olsa, Simon Horman, Shuah Khan,
Alexander Lobakin, Maciej Fijalkowski,
Bastien Curutchet (eBPF Foundation), Tushar Vyavahare, Jason Xing,
Ricardo B. Marlière, Eelco Chaudron, Lorenzo Bianconi,
Toke Hoiland-Jorgensen, imx, netdev, linux-kernel,
intel-wired-lan, linux-kselftest, Aleksandr Loktionov,
Dragos Tatulea
Current way of handling XDP RxQ info in i40e has a problem, where frag_size
is not updated when xsk_buff_pool is detached or when MTU is changed, this
leads to growing tail always failing for multi-buffer packets.
Couple XDP RxQ info registering with buffer allocations and unregistering
with cleaning the ring.
Fixes: a045d2f2d03d ("i40e: set xdp_rxq_info::frag_size")
Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
Signed-off-by: Larysa Zaremba <larysa.zaremba@intel.com>
---
drivers/net/ethernet/intel/i40e/i40e_main.c | 34 ++++++++++++---------
drivers/net/ethernet/intel/i40e/i40e_txrx.c | 5 +--
2 files changed, 22 insertions(+), 17 deletions(-)
diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c
index 7b9e147d7365..781ec5aa814b 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_main.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_main.c
@@ -3583,18 +3583,8 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring)
if (ring->vsi->type != I40E_VSI_MAIN)
goto skip;
- if (!xdp_rxq_info_is_reg(&ring->xdp_rxq)) {
- err = __xdp_rxq_info_reg(&ring->xdp_rxq, ring->netdev,
- ring->queue_index,
- ring->q_vector->napi.napi_id,
- ring->rx_buf_len);
- if (err)
- return err;
- }
-
ring->xsk_pool = i40e_xsk_pool(ring);
if (ring->xsk_pool) {
- xdp_rxq_info_unreg(&ring->xdp_rxq);
ring->rx_buf_len = xsk_pool_get_rx_frame_size(ring->xsk_pool);
err = __xdp_rxq_info_reg(&ring->xdp_rxq, ring->netdev,
ring->queue_index,
@@ -3606,17 +3596,23 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring)
MEM_TYPE_XSK_BUFF_POOL,
NULL);
if (err)
- return err;
+ goto unreg_xdp;
dev_info(&vsi->back->pdev->dev,
"Registered XDP mem model MEM_TYPE_XSK_BUFF_POOL on Rx ring %d\n",
ring->queue_index);
} else {
+ err = __xdp_rxq_info_reg(&ring->xdp_rxq, ring->netdev,
+ ring->queue_index,
+ ring->q_vector->napi.napi_id,
+ ring->rx_buf_len);
+ if (err)
+ return err;
err = xdp_rxq_info_reg_mem_model(&ring->xdp_rxq,
MEM_TYPE_PAGE_SHARED,
NULL);
if (err)
- return err;
+ goto unreg_xdp;
}
skip:
@@ -3654,7 +3650,8 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring)
dev_info(&vsi->back->pdev->dev,
"Failed to clear LAN Rx queue context on Rx ring %d (pf_q %d), error: %d\n",
ring->queue_index, pf_q, err);
- return -ENOMEM;
+ err = -ENOMEM;
+ goto unreg_xdp;
}
/* set the context in the HMC */
@@ -3663,7 +3660,8 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring)
dev_info(&vsi->back->pdev->dev,
"Failed to set LAN Rx queue context on Rx ring %d (pf_q %d), error: %d\n",
ring->queue_index, pf_q, err);
- return -ENOMEM;
+ err = -ENOMEM;
+ goto unreg_xdp;
}
/* configure Rx buffer alignment */
@@ -3671,7 +3669,8 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring)
if (I40E_2K_TOO_SMALL_WITH_PADDING) {
dev_info(&vsi->back->pdev->dev,
"2k Rx buffer is too small to fit standard MTU and skb_shared_info\n");
- return -EOPNOTSUPP;
+ err = -EOPNOTSUPP;
+ goto unreg_xdp;
}
clear_ring_build_skb_enabled(ring);
} else {
@@ -3701,6 +3700,11 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring)
}
return 0;
+unreg_xdp:
+ if (ring->vsi->type == I40E_VSI_MAIN)
+ xdp_rxq_info_unreg(&ring->xdp_rxq);
+
+ return err;
}
/**
diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c
index 34db7d8866b0..894f2d06d39d 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c
@@ -1470,6 +1470,9 @@ void i40e_clean_rx_ring(struct i40e_ring *rx_ring)
if (!rx_ring->rx_bi)
return;
+ if (xdp_rxq_info_is_reg(&rx_ring->xdp_rxq))
+ xdp_rxq_info_unreg(&rx_ring->xdp_rxq);
+
if (rx_ring->xsk_pool) {
i40e_xsk_clean_rx_ring(rx_ring);
goto skip_free;
@@ -1527,8 +1530,6 @@ void i40e_clean_rx_ring(struct i40e_ring *rx_ring)
void i40e_free_rx_resources(struct i40e_ring *rx_ring)
{
i40e_clean_rx_ring(rx_ring);
- if (rx_ring->vsi->type == I40E_VSI_MAIN)
- xdp_rxq_info_unreg(&rx_ring->xdp_rxq);
rx_ring->xdp_prog = NULL;
kfree(rx_ring->rx_bi);
rx_ring->rx_bi = NULL;
--
2.52.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH bpf v4 6/9] i40e: use xdp.frame_sz as XDP RxQ info frag_size
2026-03-02 16:17 [PATCH bpf v4 0/9] Address XDP frags having negative tailroom Larysa Zaremba
` (4 preceding siblings ...)
2026-03-02 16:17 ` [PATCH bpf v4 5/9] i40e: fix registering XDP RxQ info Larysa Zaremba
@ 2026-03-02 16:17 ` Larysa Zaremba
2026-03-02 17:32 ` bot+bpf-ci
2026-03-02 16:17 ` [PATCH bpf v4 7/9] libeth, idpf: use truesize " Larysa Zaremba
` (3 subsequent siblings)
9 siblings, 1 reply; 13+ messages in thread
From: Larysa Zaremba @ 2026-03-02 16:17 UTC (permalink / raw)
To: bpf
Cc: Larysa Zaremba, Claudiu Manoil, Vladimir Oltean, Wei Fang,
Clark Wang, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Tony Nguyen, Przemek Kitszel,
Alexei Starovoitov, Daniel Borkmann, Jesper Dangaard Brouer,
John Fastabend, Stanislav Fomichev, Andrii Nakryiko,
Martin KaFai Lau, Eduard Zingerman, Song Liu, Yonghong Song,
KP Singh, Hao Luo, Jiri Olsa, Simon Horman, Shuah Khan,
Alexander Lobakin, Maciej Fijalkowski,
Bastien Curutchet (eBPF Foundation), Tushar Vyavahare, Jason Xing,
Ricardo B. Marlière, Eelco Chaudron, Lorenzo Bianconi,
Toke Hoiland-Jorgensen, imx, netdev, linux-kernel,
intel-wired-lan, linux-kselftest, Aleksandr Loktionov,
Dragos Tatulea
The only user of frag_size field in XDP RxQ info is
bpf_xdp_frags_increase_tail(). It clearly expects whole buffer size instead
of DMA write size. Different assumptions in i40e driver configuration lead
to negative tailroom.
Set frag_size to the same value as frame_sz in shared pages mode, use new
helper to set frag_size when AF_XDP ZC is active.
Fixes: a045d2f2d03d ("i40e: set xdp_rxq_info::frag_size")
Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
Signed-off-by: Larysa Zaremba <larysa.zaremba@intel.com>
---
drivers/net/ethernet/intel/i40e/i40e_main.c | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c
index 781ec5aa814b..1ef99ae819df 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_main.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_main.c
@@ -3567,6 +3567,7 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring)
struct i40e_vsi *vsi = ring->vsi;
u32 chain_len = vsi->back->hw.func_caps.rx_buf_chain_len;
u16 pf_q = vsi->base_queue + ring->queue_index;
+ u32 xdp_frame_sz = i40e_rx_pg_size(ring) / 2;
struct i40e_hw *hw = &vsi->back->hw;
struct i40e_hmc_obj_rxq rx_ctx;
int err = 0;
@@ -3585,11 +3586,12 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring)
ring->xsk_pool = i40e_xsk_pool(ring);
if (ring->xsk_pool) {
+ xdp_frame_sz = xsk_pool_get_rx_frag_step(ring->xsk_pool);
ring->rx_buf_len = xsk_pool_get_rx_frame_size(ring->xsk_pool);
err = __xdp_rxq_info_reg(&ring->xdp_rxq, ring->netdev,
ring->queue_index,
ring->q_vector->napi.napi_id,
- ring->rx_buf_len);
+ xdp_frame_sz);
if (err)
return err;
err = xdp_rxq_info_reg_mem_model(&ring->xdp_rxq,
@@ -3605,7 +3607,7 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring)
err = __xdp_rxq_info_reg(&ring->xdp_rxq, ring->netdev,
ring->queue_index,
ring->q_vector->napi.napi_id,
- ring->rx_buf_len);
+ xdp_frame_sz);
if (err)
return err;
err = xdp_rxq_info_reg_mem_model(&ring->xdp_rxq,
@@ -3616,7 +3618,7 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring)
}
skip:
- xdp_init_buff(&ring->xdp, i40e_rx_pg_size(ring) / 2, &ring->xdp_rxq);
+ xdp_init_buff(&ring->xdp, xdp_frame_sz, &ring->xdp_rxq);
rx_ctx.dbuff = DIV_ROUND_UP(ring->rx_buf_len,
BIT_ULL(I40E_RXQ_CTX_DBUFF_SHIFT));
--
2.52.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* Re: [PATCH bpf v4 6/9] i40e: use xdp.frame_sz as XDP RxQ info frag_size
2026-03-02 16:17 ` [PATCH bpf v4 6/9] i40e: use xdp.frame_sz as XDP RxQ info frag_size Larysa Zaremba
@ 2026-03-02 17:32 ` bot+bpf-ci
0 siblings, 0 replies; 13+ messages in thread
From: bot+bpf-ci @ 2026-03-02 17:32 UTC (permalink / raw)
To: larysa.zaremba, bpf
Cc: larysa.zaremba, claudiu.manoil, vladimir.oltean, wei.fang,
xiaoning.wang, andrew+netdev, davem, edumazet, kuba, pabeni,
anthony.l.nguyen, przemyslaw.kitszel, ast, daniel, hawk,
john.fastabend, sdf, andrii, martin.lau, eddyz87, song,
yonghong.song, kpsingh, haoluo, jolsa, horms, shuah,
aleksander.lobakin, maciej.fijalkowski, bastien.curutchet,
tushar.vyavahare, kernelxing, rbm, echaudro, lorenzo, toke, imx,
netdev, linux-kernel, intel-wired-lan, linux-kselftest,
aleksandr.loktionov, dtatulea, ast, andrii, daniel, martin.lau,
eddyz87, yonghong.song, clm, ihor.solodrai
[-- Attachment #1: Type: text/plain, Size: 3134 bytes --]
> diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c
> --- a/drivers/net/ethernet/intel/i40e/i40e_main.c
> +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c
> @@ -3567,6 +3567,7 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring)
> struct i40e_vsi *vsi = ring->vsi;
> u32 chain_len = vsi->back->hw.func_caps.rx_buf_chain_len;
> u16 pf_q = vsi->base_queue + ring->queue_index;
> + u32 xdp_frame_sz = i40e_rx_pg_size(ring) / 2;
> struct i40e_hw *hw = &vsi->back->hw;
On PAGE_SIZE < 8192 systems, i40e_rx_pg_size() calls
i40e_rx_pg_order() which checks ring->rx_buf_len:
i40e_txrx.h:i40e_rx_pg_order() {
#if (PAGE_SIZE < 8192)
if (ring->rx_buf_len > (PAGE_SIZE / 2))
return 1;
#endif
return 0;
}
At this point in the function, ring->rx_buf_len still holds the
value from the previous configuration. It is not updated until
later in the function body:
i40e_main.c:i40e_configure_rx_ring() {
...
u32 xdp_frame_sz = i40e_rx_pg_size(ring) / 2; // stale rx_buf_len
...
ring->rx_buf_len = vsi->rx_buf_len; // updated here, after init
...
}
Could this lead to a wrong xdp_frame_sz on 4K page systems when
ring->rx_buf_len changes across the PAGE_SIZE/2 (2048) boundary
between configurations?
For example, after AF_XDP ZC teardown on a standard MTU interface,
the previous rx_buf_len from XSK (~3840 with default 4K chunks) is
above 2048, giving pg_order=1 and xdp_frame_sz=4096. But the new
vsi->rx_buf_len for standard MTU is 1534, which would give
pg_order=0 and the correct xdp_frame_sz=2048.
With frag_size set to 4096 but actual buffers only 2048 bytes,
bpf_xdp_frags_increase_tail() would see frag_size > frame_sz and
return -EOPNOTSUPP.
The previous code computed i40e_rx_pg_size(ring) / 2 at the skip
label, after ring->rx_buf_len was already updated, so this ordering
issue did not exist before.
Would it work to move the computation after the rx_buf_len
assignment, for example:
ring->rx_buf_len = vsi->rx_buf_len;
xdp_frame_sz = i40e_rx_pg_size(ring) / 2;
if (ring->vsi->type != I40E_VSI_MAIN)
goto skip;
> [ ... ]
> @@ -3616,7 +3618,7 @@
> }
>
> skip:
> - xdp_init_buff(&ring->xdp, i40e_rx_pg_size(ring) / 2, &ring->xdp_rxq);
> + xdp_init_buff(&ring->xdp, xdp_frame_sz, &ring->xdp_rxq);
> [ ... ]
---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
CI run summary: https://github.com/kernel-patches/bpf/actions/runs/22586694870
AI-authorship-score: low
AI-authorship-explanation: The commit shows domain-specific driver knowledge, targeted fix with minimal changes, and a natural commit message style consistent with human kernel developers.
issues-found: 1
issue-severity-score: low
issue-severity-explanation: Variable initialization order causes wrong xdp_frame_sz on 4K page systems when rx_buf_len crosses the PAGE_SIZE/2 boundary between reconfigurations, leading to bpf_xdp_adjust_tail returning -EOPNOTSUPP incorrectly but no memory corruption.
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH bpf v4 7/9] libeth, idpf: use truesize as XDP RxQ info frag_size
2026-03-02 16:17 [PATCH bpf v4 0/9] Address XDP frags having negative tailroom Larysa Zaremba
` (5 preceding siblings ...)
2026-03-02 16:17 ` [PATCH bpf v4 6/9] i40e: use xdp.frame_sz as XDP RxQ info frag_size Larysa Zaremba
@ 2026-03-02 16:17 ` Larysa Zaremba
2026-03-02 16:17 ` [PATCH bpf v4 8/9] net: enetc: " Larysa Zaremba
` (2 subsequent siblings)
9 siblings, 0 replies; 13+ messages in thread
From: Larysa Zaremba @ 2026-03-02 16:17 UTC (permalink / raw)
To: bpf
Cc: Larysa Zaremba, Claudiu Manoil, Vladimir Oltean, Wei Fang,
Clark Wang, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Tony Nguyen, Przemek Kitszel,
Alexei Starovoitov, Daniel Borkmann, Jesper Dangaard Brouer,
John Fastabend, Stanislav Fomichev, Andrii Nakryiko,
Martin KaFai Lau, Eduard Zingerman, Song Liu, Yonghong Song,
KP Singh, Hao Luo, Jiri Olsa, Simon Horman, Shuah Khan,
Alexander Lobakin, Maciej Fijalkowski,
Bastien Curutchet (eBPF Foundation), Tushar Vyavahare, Jason Xing,
Ricardo B. Marlière, Eelco Chaudron, Lorenzo Bianconi,
Toke Hoiland-Jorgensen, imx, netdev, linux-kernel,
intel-wired-lan, linux-kselftest, Aleksandr Loktionov,
Dragos Tatulea
The only user of frag_size field in XDP RxQ info is
bpf_xdp_frags_increase_tail(). It clearly expects whole buffer size instead
of DMA write size. Different assumptions in idpf driver configuration lead
to negative tailroom.
To make it worse, buffer sizes are not actually uniform in idpf when
splitq is enabled, as there are several buffer queues, so rxq->rx_buf_size
is meaningless in this case.
Use truesize of the first bufq in AF_XDP ZC, as there is only one. Disable
growing tail for regular splitq.
Fixes: ac8a861f632e ("idpf: prepare structures to support XDP")
Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
Signed-off-by: Larysa Zaremba <larysa.zaremba@intel.com>
---
drivers/net/ethernet/intel/idpf/xdp.c | 6 +++++-
drivers/net/ethernet/intel/idpf/xsk.c | 1 +
drivers/net/ethernet/intel/libeth/xsk.c | 1 +
include/net/libeth/xsk.h | 3 +++
4 files changed, 10 insertions(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/intel/idpf/xdp.c b/drivers/net/ethernet/intel/idpf/xdp.c
index 6ac9c6624c2a..cbccd4546768 100644
--- a/drivers/net/ethernet/intel/idpf/xdp.c
+++ b/drivers/net/ethernet/intel/idpf/xdp.c
@@ -47,12 +47,16 @@ static int __idpf_xdp_rxq_info_init(struct idpf_rx_queue *rxq, void *arg)
{
const struct idpf_vport *vport = rxq->q_vector->vport;
const struct idpf_q_vec_rsrc *rsrc;
+ u32 frag_size = 0;
bool split;
int err;
+ if (idpf_queue_has(XSK, rxq))
+ frag_size = rxq->bufq_sets[0].bufq.truesize;
+
err = __xdp_rxq_info_reg(&rxq->xdp_rxq, vport->netdev, rxq->idx,
rxq->q_vector->napi.napi_id,
- rxq->rx_buf_size);
+ frag_size);
if (err)
return err;
diff --git a/drivers/net/ethernet/intel/idpf/xsk.c b/drivers/net/ethernet/intel/idpf/xsk.c
index 676cbd80774d..d95d3efdfd36 100644
--- a/drivers/net/ethernet/intel/idpf/xsk.c
+++ b/drivers/net/ethernet/intel/idpf/xsk.c
@@ -403,6 +403,7 @@ int idpf_xskfq_init(struct idpf_buf_queue *bufq)
bufq->pending = fq.pending;
bufq->thresh = fq.thresh;
bufq->rx_buf_size = fq.buf_len;
+ bufq->truesize = fq.truesize;
if (!idpf_xskfq_refill(bufq))
netdev_err(bufq->pool->netdev,
diff --git a/drivers/net/ethernet/intel/libeth/xsk.c b/drivers/net/ethernet/intel/libeth/xsk.c
index 846e902e31b6..4882951d5c9c 100644
--- a/drivers/net/ethernet/intel/libeth/xsk.c
+++ b/drivers/net/ethernet/intel/libeth/xsk.c
@@ -167,6 +167,7 @@ int libeth_xskfq_create(struct libeth_xskfq *fq)
fq->pending = fq->count;
fq->thresh = libeth_xdp_queue_threshold(fq->count);
fq->buf_len = xsk_pool_get_rx_frame_size(fq->pool);
+ fq->truesize = xsk_pool_get_rx_frag_step(fq->pool);
return 0;
}
diff --git a/include/net/libeth/xsk.h b/include/net/libeth/xsk.h
index 481a7b28e6f2..82b5d21aae87 100644
--- a/include/net/libeth/xsk.h
+++ b/include/net/libeth/xsk.h
@@ -597,6 +597,7 @@ __libeth_xsk_run_pass(struct libeth_xdp_buff *xdp,
* @pending: current number of XSkFQEs to refill
* @thresh: threshold below which the queue is refilled
* @buf_len: HW-writeable length per each buffer
+ * @truesize: step between consecutive buffers, 0 if none exists
* @nid: ID of the closest NUMA node with memory
*/
struct libeth_xskfq {
@@ -614,6 +615,8 @@ struct libeth_xskfq {
u32 thresh;
u32 buf_len;
+ u32 truesize;
+
int nid;
};
--
2.52.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH bpf v4 8/9] net: enetc: use truesize as XDP RxQ info frag_size
2026-03-02 16:17 [PATCH bpf v4 0/9] Address XDP frags having negative tailroom Larysa Zaremba
` (6 preceding siblings ...)
2026-03-02 16:17 ` [PATCH bpf v4 7/9] libeth, idpf: use truesize " Larysa Zaremba
@ 2026-03-02 16:17 ` Larysa Zaremba
2026-03-02 16:17 ` [PATCH bpf v4 9/9] xdp: produce a warning when calculated tailroom is negative Larysa Zaremba
2026-03-05 2:55 ` [PATCH bpf v4 0/9] Address XDP frags having negative tailroom Jakub Kicinski
9 siblings, 0 replies; 13+ messages in thread
From: Larysa Zaremba @ 2026-03-02 16:17 UTC (permalink / raw)
To: bpf
Cc: Larysa Zaremba, Claudiu Manoil, Vladimir Oltean, Wei Fang,
Clark Wang, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Tony Nguyen, Przemek Kitszel,
Alexei Starovoitov, Daniel Borkmann, Jesper Dangaard Brouer,
John Fastabend, Stanislav Fomichev, Andrii Nakryiko,
Martin KaFai Lau, Eduard Zingerman, Song Liu, Yonghong Song,
KP Singh, Hao Luo, Jiri Olsa, Simon Horman, Shuah Khan,
Alexander Lobakin, Maciej Fijalkowski,
Bastien Curutchet (eBPF Foundation), Tushar Vyavahare, Jason Xing,
Ricardo B. Marlière, Eelco Chaudron, Lorenzo Bianconi,
Toke Hoiland-Jorgensen, imx, netdev, linux-kernel,
intel-wired-lan, linux-kselftest, Aleksandr Loktionov,
Dragos Tatulea
The only user of frag_size field in XDP RxQ info is
bpf_xdp_frags_increase_tail(). It clearly expects truesize instead of DMA
write size. Different assumptions in enetc driver configuration lead to
negative tailroom.
Set frag_size to the same value as frame_sz.
Fixes: 2768b2e2f7d2 ("net: enetc: register XDP RX queues with frag_size")
Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
Reviewed-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: Larysa Zaremba <larysa.zaremba@intel.com>
---
drivers/net/ethernet/freescale/enetc/enetc.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/freescale/enetc/enetc.c b/drivers/net/ethernet/freescale/enetc/enetc.c
index 70768392912c..a146ceaf2ed6 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc.c
@@ -3467,7 +3467,7 @@ static int enetc_int_vector_init(struct enetc_ndev_priv *priv, int i,
priv->rx_ring[i] = bdr;
err = __xdp_rxq_info_reg(&bdr->xdp.rxq, priv->ndev, i, 0,
- ENETC_RXB_DMA_SIZE_XDP);
+ ENETC_RXB_TRUESIZE);
if (err)
goto free_vector;
--
2.52.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH bpf v4 9/9] xdp: produce a warning when calculated tailroom is negative
2026-03-02 16:17 [PATCH bpf v4 0/9] Address XDP frags having negative tailroom Larysa Zaremba
` (7 preceding siblings ...)
2026-03-02 16:17 ` [PATCH bpf v4 8/9] net: enetc: " Larysa Zaremba
@ 2026-03-02 16:17 ` Larysa Zaremba
2026-03-05 2:55 ` [PATCH bpf v4 0/9] Address XDP frags having negative tailroom Jakub Kicinski
9 siblings, 0 replies; 13+ messages in thread
From: Larysa Zaremba @ 2026-03-02 16:17 UTC (permalink / raw)
To: bpf
Cc: Larysa Zaremba, Claudiu Manoil, Vladimir Oltean, Wei Fang,
Clark Wang, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Tony Nguyen, Przemek Kitszel,
Alexei Starovoitov, Daniel Borkmann, Jesper Dangaard Brouer,
John Fastabend, Stanislav Fomichev, Andrii Nakryiko,
Martin KaFai Lau, Eduard Zingerman, Song Liu, Yonghong Song,
KP Singh, Hao Luo, Jiri Olsa, Simon Horman, Shuah Khan,
Alexander Lobakin, Maciej Fijalkowski,
Bastien Curutchet (eBPF Foundation), Tushar Vyavahare, Jason Xing,
Ricardo B. Marlière, Eelco Chaudron, Lorenzo Bianconi,
Toke Hoiland-Jorgensen, imx, netdev, linux-kernel,
intel-wired-lan, linux-kselftest, Aleksandr Loktionov,
Dragos Tatulea, Martin KaFai Lau
Many ethernet drivers report xdp Rx queue frag size as being the same as
DMA write size. However, the only user of this field, namely
bpf_xdp_frags_increase_tail(), clearly expects a truesize.
Such difference leads to unspecific memory corruption issues under certain
circumstances, e.g. in ixgbevf maximum DMA write size is 3 KB, so when
running xskxceiver's XDP_ADJUST_TAIL_GROW_MULTI_BUFF, 6K packet fully uses
all DMA-writable space in 2 buffers. This would be fine, if only
rxq->frag_size was properly set to 4K, but value of 3K results in a
negative tailroom, because there is a non-zero page offset.
We are supposed to return -EINVAL and be done with it in such case, but due
to tailroom being stored as an unsigned int, it is reported to be somewhere
near UINT_MAX, resulting in a tail being grown, even if the requested
offset is too much (it is around 2K in the abovementioned test). This later
leads to all kinds of unspecific calltraces.
[ 7340.337579] xskxceiver[1440]: segfault at 1da718 ip 00007f4161aeac9d sp 00007f41615a6a00 error 6
[ 7340.338040] xskxceiver[1441]: segfault at 7f410000000b ip 00000000004042b5 sp 00007f415bffecf0 error 4
[ 7340.338179] in libc.so.6[61c9d,7f4161aaf000+160000]
[ 7340.339230] in xskxceiver[42b5,400000+69000]
[ 7340.340300] likely on CPU 6 (core 0, socket 6)
[ 7340.340302] Code: ff ff 01 e9 f4 fe ff ff 0f 1f 44 00 00 4c 39 f0 74 73 31 c0 ba 01 00 00 00 f0 0f b1 17 0f 85 ba 00 00 00 49 8b 87 88 00 00 00 <4c> 89 70 08 eb cc 0f 1f 44 00 00 48 8d bd f0 fe ff ff 89 85 ec fe
[ 7340.340888] likely on CPU 3 (core 0, socket 3)
[ 7340.345088] Code: 00 00 00 ba 00 00 00 00 be 00 00 00 00 89 c7 e8 31 ca ff ff 89 45 ec 8b 45 ec 85 c0 78 07 b8 00 00 00 00 eb 46 e8 0b c8 ff ff <8b> 00 83 f8 69 74 24 e8 ff c7 ff ff 8b 00 83 f8 0b 74 18 e8 f3 c7
[ 7340.404334] Oops: general protection fault, probably for non-canonical address 0x6d255010bdffc: 0000 [#1] SMP NOPTI
[ 7340.405972] CPU: 7 UID: 0 PID: 1439 Comm: xskxceiver Not tainted 6.19.0-rc1+ #21 PREEMPT(lazy)
[ 7340.408006] Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.17.0-5.fc42 04/01/2014
[ 7340.409716] RIP: 0010:lookup_swap_cgroup_id+0x44/0x80
[ 7340.410455] Code: 83 f8 1c 73 39 48 ba ff ff ff ff ff ff ff 03 48 8b 04 c5 20 55 fa bd 48 21 d1 48 89 ca 83 e1 01 48 d1 ea c1 e1 04 48 8d 04 90 <8b> 00 48 83 c4 10 d3 e8 c3 cc cc cc cc 31 c0 e9 98 b7 dd 00 48 89
[ 7340.412787] RSP: 0018:ffffcc5c04f7f6d0 EFLAGS: 00010202
[ 7340.413494] RAX: 0006d255010bdffc RBX: ffff891f477895a8 RCX: 0000000000000010
[ 7340.414431] RDX: 0001c17e3fffffff RSI: 00fa070000000000 RDI: 000382fc7fffffff
[ 7340.415354] RBP: 00fa070000000000 R08: ffffcc5c04f7f8f8 R09: ffffcc5c04f7f7d0
[ 7340.416283] R10: ffff891f4c1a7000 R11: ffffcc5c04f7f9c8 R12: ffffcc5c04f7f7d0
[ 7340.417218] R13: 03ffffffffffffff R14: 00fa06fffffffe00 R15: ffff891f47789500
[ 7340.418229] FS: 0000000000000000(0000) GS:ffff891ffdfaa000(0000) knlGS:0000000000000000
[ 7340.419489] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 7340.420286] CR2: 00007f415bfffd58 CR3: 0000000103f03002 CR4: 0000000000772ef0
[ 7340.421237] PKRU: 55555554
[ 7340.421623] Call Trace:
[ 7340.421987] <TASK>
[ 7340.422309] ? softleaf_from_pte+0x77/0xa0
[ 7340.422855] swap_pte_batch+0xa7/0x290
[ 7340.423363] zap_nonpresent_ptes.constprop.0.isra.0+0xd1/0x270
[ 7340.424102] zap_pte_range+0x281/0x580
[ 7340.424607] zap_pmd_range.isra.0+0xc9/0x240
[ 7340.425177] unmap_page_range+0x24d/0x420
[ 7340.425714] unmap_vmas+0xa1/0x180
[ 7340.426185] exit_mmap+0xe1/0x3b0
[ 7340.426644] __mmput+0x41/0x150
[ 7340.427098] exit_mm+0xb1/0x110
[ 7340.427539] do_exit+0x1b2/0x460
[ 7340.427992] do_group_exit+0x2d/0xc0
[ 7340.428477] get_signal+0x79d/0x7e0
[ 7340.428957] arch_do_signal_or_restart+0x34/0x100
[ 7340.429571] exit_to_user_mode_loop+0x8e/0x4c0
[ 7340.430159] do_syscall_64+0x188/0x6b0
[ 7340.430672] ? __do_sys_clone3+0xd9/0x120
[ 7340.431212] ? switch_fpu_return+0x4e/0xd0
[ 7340.431761] ? arch_exit_to_user_mode_prepare.isra.0+0xa1/0xc0
[ 7340.432498] ? do_syscall_64+0xbb/0x6b0
[ 7340.433015] ? __handle_mm_fault+0x445/0x690
[ 7340.433582] ? count_memcg_events+0xd6/0x210
[ 7340.434151] ? handle_mm_fault+0x212/0x340
[ 7340.434697] ? do_user_addr_fault+0x2b4/0x7b0
[ 7340.435271] ? clear_bhb_loop+0x30/0x80
[ 7340.435788] ? clear_bhb_loop+0x30/0x80
[ 7340.436299] ? clear_bhb_loop+0x30/0x80
[ 7340.436812] ? clear_bhb_loop+0x30/0x80
[ 7340.437323] entry_SYSCALL_64_after_hwframe+0x76/0x7e
[ 7340.437973] RIP: 0033:0x7f4161b14169
[ 7340.438468] Code: Unable to access opcode bytes at 0x7f4161b1413f.
[ 7340.439242] RSP: 002b:00007ffc6ebfa770 EFLAGS: 00000246 ORIG_RAX: 00000000000000ca
[ 7340.440173] RAX: fffffffffffffe00 RBX: 00000000000005a1 RCX: 00007f4161b14169
[ 7340.441061] RDX: 00000000000005a1 RSI: 0000000000000109 RDI: 00007f415bfff990
[ 7340.441943] RBP: 00007ffc6ebfa7a0 R08: 0000000000000000 R09: 00000000ffffffff
[ 7340.442824] R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
[ 7340.443707] R13: 0000000000000000 R14: 00007f415bfff990 R15: 00007f415bfff6c0
[ 7340.444586] </TASK>
[ 7340.444922] Modules linked in: rfkill intel_rapl_msr intel_rapl_common intel_uncore_frequency_common skx_edac_common nfit libnvdimm kvm_intel vfat fat kvm snd_pcm irqbypass rapl iTCO_wdt snd_timer intel_pmc_bxt iTCO_vendor_support snd ixgbevf virtio_net soundcore i2c_i801 pcspkr libeth_xdp net_failover i2c_smbus lpc_ich failover libeth virtio_balloon joydev 9p fuse loop zram lz4hc_compress lz4_compress 9pnet_virtio 9pnet netfs ghash_clmulni_intel serio_raw qemu_fw_cfg
[ 7340.449650] ---[ end trace 0000000000000000 ]---
The issue can be fixed in all in-tree drivers, but we cannot just trust OOT
drivers to not do this. Therefore, make tailroom a signed int and produce a
warning when it is negative to prevent such mistakes in the future.
Fixes: bf25146a5595 ("bpf: add frags support to the bpf_xdp_adjust_tail() API")
Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
Reviewed-by: Toke Høiland-Jørgensen <toke@redhat.com>
Acked-by: Martin KaFai Lau <martin.lau@kernel.org>
Signed-off-by: Larysa Zaremba <larysa.zaremba@intel.com>
---
net/core/filter.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/net/core/filter.c b/net/core/filter.c
index d6fafb3633b0..a77d23fe2359 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -4150,13 +4150,14 @@ static int bpf_xdp_frags_increase_tail(struct xdp_buff *xdp, int offset)
struct skb_shared_info *sinfo = xdp_get_shared_info_from_buff(xdp);
skb_frag_t *frag = &sinfo->frags[sinfo->nr_frags - 1];
struct xdp_rxq_info *rxq = xdp->rxq;
- unsigned int tailroom;
+ int tailroom;
if (!rxq->frag_size || rxq->frag_size > xdp->frame_sz)
return -EOPNOTSUPP;
tailroom = rxq->frag_size - skb_frag_size(frag) -
skb_frag_off(frag) % rxq->frag_size;
+ WARN_ON_ONCE(tailroom < 0);
if (unlikely(offset > tailroom))
return -EINVAL;
--
2.52.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* Re: [PATCH bpf v4 0/9] Address XDP frags having negative tailroom
2026-03-02 16:17 [PATCH bpf v4 0/9] Address XDP frags having negative tailroom Larysa Zaremba
` (8 preceding siblings ...)
2026-03-02 16:17 ` [PATCH bpf v4 9/9] xdp: produce a warning when calculated tailroom is negative Larysa Zaremba
@ 2026-03-05 2:55 ` Jakub Kicinski
2026-03-05 11:48 ` Larysa Zaremba
9 siblings, 1 reply; 13+ messages in thread
From: Jakub Kicinski @ 2026-03-05 2:55 UTC (permalink / raw)
To: Larysa Zaremba
Cc: bpf, Claudiu Manoil, Vladimir Oltean, Wei Fang, Clark Wang,
Andrew Lunn, David S. Miller, Eric Dumazet, Paolo Abeni,
Tony Nguyen, Przemek Kitszel, Alexei Starovoitov, Daniel Borkmann,
Jesper Dangaard Brouer, John Fastabend, Stanislav Fomichev,
Andrii Nakryiko, Martin KaFai Lau, Eduard Zingerman, Song Liu,
Yonghong Song, KP Singh, Hao Luo, Jiri Olsa, Simon Horman,
Shuah Khan, Alexander Lobakin, Maciej Fijalkowski,
Bastien Curutchet (eBPF Foundation), Tushar Vyavahare, Jason Xing,
Ricardo B. Marlière, Eelco Chaudron, Lorenzo Bianconi,
Toke Hoiland-Jorgensen, imx, netdev, linux-kernel,
intel-wired-lan, linux-kselftest, Aleksandr Loktionov,
Dragos Tatulea
On Mon, 2 Mar 2026 17:17:13 +0100 Larysa Zaremba wrote:
> Aside from the issue described below, tailroom calculation does not account
> for pages being split between frags, e.g. in i40e, enetc and
> AF_XDP ZC with smaller chunks. These series address the problem by
> calculating modulo (skb_frag_off() % rxq->frag_size) in order to get
> data offset within a smaller block of memory. Please note, xskxceiver
> tail grow test passes without modulo e.g. in xdpdrv mode on i40e,
> because there is not enough descriptors to get to flipped buffers.
This was re-assigned to netdev in pw, I presume by BPF maintainers.
But it doesn't apply to net. There's a conflict in ice.
Could you rebase on net and repost?
^ permalink raw reply [flat|nested] 13+ messages in thread* Re: [PATCH bpf v4 0/9] Address XDP frags having negative tailroom
2026-03-05 2:55 ` [PATCH bpf v4 0/9] Address XDP frags having negative tailroom Jakub Kicinski
@ 2026-03-05 11:48 ` Larysa Zaremba
0 siblings, 0 replies; 13+ messages in thread
From: Larysa Zaremba @ 2026-03-05 11:48 UTC (permalink / raw)
To: Jakub Kicinski
Cc: bpf, Claudiu Manoil, Vladimir Oltean, Wei Fang, Clark Wang,
Andrew Lunn, David S. Miller, Eric Dumazet, Paolo Abeni,
Tony Nguyen, Przemek Kitszel, Alexei Starovoitov, Daniel Borkmann,
Jesper Dangaard Brouer, John Fastabend, Stanislav Fomichev,
Andrii Nakryiko, Martin KaFai Lau, Eduard Zingerman, Song Liu,
Yonghong Song, KP Singh, Hao Luo, Jiri Olsa, Simon Horman,
Shuah Khan, Alexander Lobakin, Maciej Fijalkowski,
Bastien Curutchet (eBPF Foundation), Tushar Vyavahare, Jason Xing,
Ricardo B. Marlière, Eelco Chaudron, Lorenzo Bianconi,
Toke Hoiland-Jorgensen, imx, netdev, linux-kernel,
intel-wired-lan, linux-kselftest, Aleksandr Loktionov,
Dragos Tatulea
On Wed, Mar 04, 2026 at 06:55:31PM -0800, Jakub Kicinski wrote:
> On Mon, 2 Mar 2026 17:17:13 +0100 Larysa Zaremba wrote:
> > Aside from the issue described below, tailroom calculation does not account
> > for pages being split between frags, e.g. in i40e, enetc and
> > AF_XDP ZC with smaller chunks. These series address the problem by
> > calculating modulo (skb_frag_off() % rxq->frag_size) in order to get
> > data offset within a smaller block of memory. Please note, xskxceiver
> > tail grow test passes without modulo e.g. in xdpdrv mode on i40e,
> > because there is not enough descriptors to get to flipped buffers.
>
> This was re-assigned to netdev in pw, I presume by BPF maintainers.
> But it doesn't apply to net. There's a conflict in ice.
> Could you rebase on net and repost?
>
I have just sent v5 with net as a base tree, had to fix one other thing anyway.
^ permalink raw reply [flat|nested] 13+ messages in thread