mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
net: stmmac: drop multi-buffer frames in XDP mode
stmmac_rx() runs the XDP program once per descriptor buffer and uses skb == NULL as the only marker for the start of a frame. A frame larger than one buffer is split by the hardware across several descriptors; GMAC4 enables jumbo frames unconditionally (GMAC_CONFIG_JE in GMAC_CORE_INIT), so an on-link peer can deliver such a frame even when the interface MTU is left at 1500. When an XDP verdict ends a non-last descriptor, skb is cleared and the next descriptor of the same hardware frame is parsed as a new frame. For XDP_CONSUMED this happens via read_again with len preserved, so stmmac_rx_buf1_len() then returns min(dma_buf_sz, plen - len) for the last descriptor, which can be as small as 0-3 bytes. buf1_len -= ETH_FCS_LEN underflows and xdp_prepare_buff() puts data_end far past data; on XDP_PASS this produces an skb whose length is close to 4G that reaches the network stack (skb->tail and skb->len are 32-bit and wrap below skb->end, so skb_over_panic() does not fire). A 4+ byte tail is instead offered to the XDP program and delivered as a standalone mid-payload Ethernet frame on XDP_PASS. XDP_TX/XDP_REDIRECT instead falls through to the next iteration, resetting the running length, so the tail is re-parsed as a full-size new frame. The same fault exists when a single descriptor uses its second buffer: buffer2 is now handed to the hardware for non-SPH frames on GMAC4/XGMAC, so a frame up to 2 * dma_buf_sz can land on a single descriptor while the XDP program still only sees the first buffer. XDP_TX then transmits a truncated frame, and XDP_PASS appends the second buffer only after the program has already run over the first one. XDP does not support scatter-gather frames until the multi-buffer rework, so drop the whole frame instead. When a non-last descriptor is seen while an XDP program is attached, mark the frame as errored and drain the remaining descriptors, including the last. A descriptor that uses its second buffer is dropped directly before the program runs as well; in XDP mode it is necessarily the last descriptor of the frame, so it does not need the error marker. Removing the re-parse lets both XDP verdict branches simply complete the packet via next. The buffers consumed by the discard path are no longer returned to the page pool, and the new SG and second-buffer drops do not return them either. They stay in rx_q->buf_pool[] and stmmac_rx_refill() reuses them in place, allocating only when a slot is empty. This is safe because the drop checks run before the buffer is synced for CPU access or prefetched. The discard-path change and the in-place reuse apply with or without an XDP program attached. The !error guard on the discard check also changes the accounting for errored multi-descriptor frames on dwmac100/dwmac1000: rx_errors is incremented once per frame instead of once per descriptor. This too is independent of XDP mode. Note: the subsequent "net: stmmac: rework stmmac_rx to support XDP rx multi-buff" accumulates the whole frame into a single xdp_buff and supersedes this patch; the fix is kept so the net portion of the series stands on its own. Fixes:5fabb01207("net: stmmac: Add initial XDP support") Fixes:8809572851("net: stmmac: fix rx Scatter-Gather support") Reviewed-by: Nicolai Buchwitz <nb@tipi-net.de> Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@oss.qualcomm.com> Link: https://patch.msgid.link/20261027-stmmac-xdp-fixes-v2-3-10eae8a093a8@oss.qualcomm.com Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
parent
834ec95308
commit
52ffd771e0
|
|
@ -5912,18 +5912,23 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue)
|
|||
status);
|
||||
}
|
||||
|
||||
if (unlikely(status & discard_frame)) {
|
||||
page_pool_put_page(rx_q->page_pool, buf->page, 0, true);
|
||||
buf->page = NULL;
|
||||
if (unlikely(!error && (status & discard_frame))) {
|
||||
error = 1;
|
||||
if (!priv->hwts_rx_en)
|
||||
rx_errors++;
|
||||
}
|
||||
|
||||
if (unlikely(error && (status & rx_not_ls)))
|
||||
goto read_again;
|
||||
/* We do not support SG in XDP mode */
|
||||
if (unlikely(!error && (status & rx_not_ls) &&
|
||||
stmmac_xdp_is_enabled(priv))) {
|
||||
rx_dropped++;
|
||||
error = 1;
|
||||
}
|
||||
|
||||
if (unlikely(error)) {
|
||||
if (status & rx_not_ls)
|
||||
goto read_again;
|
||||
|
||||
dev_kfree_skb(skb);
|
||||
goto next;
|
||||
}
|
||||
|
|
@ -5935,6 +5940,14 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue)
|
|||
buf2_len = stmmac_rx_buf2_len(priv, p, status, len);
|
||||
len += buf2_len;
|
||||
|
||||
/* Drop the packet if it is split over buf1 and buf2 on a
|
||||
* single descriptor if the interface is running in XDP mode.
|
||||
*/
|
||||
if (unlikely(stmmac_xdp_is_enabled(priv) && buf2_len)) {
|
||||
rx_dropped++;
|
||||
goto next;
|
||||
}
|
||||
|
||||
/* ACS is disabled; strip manually. */
|
||||
if (likely(!(status & rx_not_ls)))
|
||||
len -= ETH_FCS_LEN;
|
||||
|
|
@ -5984,17 +5997,7 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue)
|
|||
sync_len, true);
|
||||
buf->page = NULL;
|
||||
rx_dropped++;
|
||||
|
||||
/* Clear skb as it was set as
|
||||
* status by XDP program.
|
||||
*/
|
||||
skb = NULL;
|
||||
|
||||
if (unlikely((status & rx_not_ls)))
|
||||
goto read_again;
|
||||
|
||||
count++;
|
||||
continue;
|
||||
goto next;
|
||||
} else if (xdp_res & (STMMAC_XDP_TX |
|
||||
STMMAC_XDP_REDIRECT)) {
|
||||
xdp_status |= xdp_res;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user