When a new rx packet arrives, the rx path will decide whether to reuse
the same page or not according to the current rx frag page offset and
frag size, i.e:
release = frags->page_offset + frag_info->frag_size > PAGE_SIZE;

Martin debugged this and he showed that this can cause mlx4 XDP to reuse
buffers when it shouldn't.

Using frag_info->frag_size is wrong since frag size is always the port
mtu and the frag stride might be larger, especially in XDP case where
frag_stride == PAGE_SIZE.

In XDP there is an assumption to have a page per packet and reuse can
break such assumption and might cause packet data corruptions, since in 
XDP frag_offset will always reset to the beginning of the page when
refilling the rx buffer.

Fix this by using the stride size rather than frag size in "release"
condition evaluation.

For non XDP setup this will yield the same behavior since frag_stride
already equals to ALIGN(frag_size, SMP_CACHE_BYTES) and on XDP setup the
"release" condition will always be true as it is supposed to be since
frag_stride == PAGE_SIZE.

Fixes: 34db548bfb95 ("mlx4: add page recycling in receive path")
Signed-off-by: Saeed Mahameed <sae...@mellanox.com>
Reported-by: Martin KaFai Lau <ka...@fb.com>
CC: Eric Dumazet <eduma...@google.com>

---
 drivers/net/ethernet/mellanox/mlx4/en_rx.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/net/ethernet/mellanox/mlx4/en_rx.c 
b/drivers/net/ethernet/mellanox/mlx4/en_rx.c
index 5c613c6663da..f63dde0288b7 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_rx.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_rx.c
@@ -504,7 +504,7 @@ static int mlx4_en_complete_rx_desc(struct mlx4_en_priv 
*priv,
                        u32 sz_align = ALIGN(frag_size, SMP_CACHE_BYTES);
 
                        frags->page_offset += sz_align;
-                       release = frags->page_offset + frag_info->frag_size > 
PAGE_SIZE;
+                       release = frags->page_offset + frag_info->frag_stride > 
PAGE_SIZE;
                }
                if (release) {
                        dma_unmap_page(priv->ddev, dma, PAGE_SIZE, 
priv->dma_dir);
-- 
2.17.0

Reply via email to