From: Tjerk Kusters <[email protected]>

igb does not support XDP multi-buffer: igb_xdp_setup() only accepts an
MTU whose frame fits in one Rx buffer, and the XDP program only sees
the first buffer of a frame. The maximum receive length is not limited
accordingly, though. RLPML is only lowered when the frame fits a build_skb
buffer on systems with PAGE_SIZE < 8192 and no VFs, and the PF pool limit
in VMOLR is set the same way. Otherwise the MAC still accepts frames of
up to MAX_JUMBO_FRAME_SIZE while XDP is attached, for example at MTU
3000, at any MTU when PAGE_SIZE >= 8192, or when VFs are allocated.

Such frames span several descriptors. When the program does not return
XDP_PASS for the first buffer, no skb is built, and igb_clean_rx_irq()
handles the next buffer of the same frame as the start of a new frame:
it runs the program on it and may pass it to the stack as a bogus frame
made from the middle of the original payload.

Limit the receive length to the configured maximum frame size while XDP
is attached: in RLPML when no VFs are allocated, and in the PF pool's
VMOLR otherwise, as RLPML then has to stay large for the VFs. Larger
frames are dropped by the MAC. Also leave room for the inline Rx
timestamp header when checking that the frame fits in one Rx buffer, on
XDP setup and on MTU change; this lowers the largest MTU accepted with
XDP on 3 KiB buffers by 16 bytes.

Both registers are programmed from igb_set_rx_mode(), which
igb_configure() calls, and attaching or removing an XDP program on a
running interface goes through igb_close() and igb_open(), as does an
MTU change.

This follows the same approach as the igc fix that limits RLPML while
XDP is attached.

Tested on an I350 at MTU 3000 with Rx timestamping on and xdp-filter
dropping all IPv4 from the peer: 8242-byte frames are now dropped by the
MAC (rx_long_length_errors), instead of their last two buffers being
delivered as a separate frame. XDP attach at MTU 3030 still works, at
MTU 3046 it is refused, as is an MTU change from 3030 to 3046 with XDP
attached.

With two VFs allocated, the same frames are counted by the MAC but
dropped by the PF pool limit in VMOLR before they reach the Rx queue;
without this patch their last two buffers are again delivered as a
separate frame.

Link: https://lore.kernel.org/all/[email protected]/
Fixes: 9cbc948b5a20 ("igb: add XDP support")
Cc: [email protected]
Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Tjerk Kusters <[email protected]>
---
 drivers/net/ethernet/intel/igb/igb_main.c | 30 +++++++++++++++++++++---------
 1 file changed, 21 insertions(+), 9 deletions(-)

diff --git a/drivers/net/ethernet/intel/igb/igb_main.c 
b/drivers/net/ethernet/intel/igb/igb_main.c
index ce91dda00ec0..5bf489cd7b06 100644
--- a/drivers/net/ethernet/intel/igb/igb_main.c
+++ b/drivers/net/ethernet/intel/igb/igb_main.c
@@ -2894,11 +2894,13 @@ static int igb_xdp_setup(struct net_device *dev, struct 
netdev_bpf *bpf)
        bool running = netif_running(dev);
        bool need_reset;
 
-       /* verify igb ring attributes are sufficient for XDP */
+       /* verify igb ring attributes are sufficient for XDP, leaving room
+        * for the inline Rx timestamp header
+        */
        for (i = 0; i < adapter->num_rx_queues; i++) {
                struct igb_ring *ring = adapter->rx_ring[i];
 
-               if (frame_size > igb_rx_bufsz(ring)) {
+               if (frame_size + IGB_TS_HDR_LEN > igb_rx_bufsz(ring)) {
                        NL_SET_ERR_MSG_MOD(bpf->extack,
                                           "The RX buffer size is too small for 
the frame size");
                        netdev_warn(dev, "XDP RX buffer size %d is too small 
for the frame size %d\n",
@@ -5324,6 +5326,7 @@ static void igb_set_rx_mode(struct net_device *netdev)
        struct e1000_hw *hw = &adapter->hw;
        unsigned int vfn = adapter->vfs_allocated_count;
        u32 rctl = 0, vmolr = 0, rlpml = MAX_JUMBO_FRAME_SIZE;
+       u32 pool_rlpml;
        int count;
 
        /* Check for Promiscuous and All Multicast modes */
@@ -5386,6 +5389,12 @@ static void igb_set_rx_mode(struct net_device *netdev)
                        rlpml = IGB_MAX_FRAME_BUILD_SKB;
        }
 #endif
+       /* XDP does not support frames that span more than one Rx buffer.
+        * With VFs the global limit stays large and the PF pool is limited
+        * through VMOLR below.
+        */
+       if (!adapter->vfs_allocated_count && igb_xdp_is_enabled(adapter))
+               rlpml = min_t(u32, rlpml, adapter->max_frame_size);
        wr32(E1000_RLPML, rlpml);
 
        /* In order to support SR-IOV and eventually VMDq it is necessary to set
@@ -5402,15 +5411,18 @@ static void igb_set_rx_mode(struct net_device *netdev)
        vmolr |= rd32(E1000_VMOLR(vfn)) &
                 ~(E1000_VMOLR_ROPE | E1000_VMOLR_MPME | E1000_VMOLR_ROMPE);
 
-       /* enable Rx jumbo frames, restrict as needed to support build_skb */
-       vmolr &= ~E1000_VMOLR_RLPML_MASK;
+       /* enable Rx jumbo frames, restrict as needed to support build_skb
+        * and XDP
+        */
+       pool_rlpml = MAX_JUMBO_FRAME_SIZE;
 #if (PAGE_SIZE < 8192)
        if (adapter->max_frame_size <= IGB_MAX_FRAME_BUILD_SKB)
-               vmolr |= IGB_MAX_FRAME_BUILD_SKB;
-       else
+               pool_rlpml = IGB_MAX_FRAME_BUILD_SKB;
 #endif
-               vmolr |= MAX_JUMBO_FRAME_SIZE;
-       vmolr |= E1000_VMOLR_LPE;
+       if (igb_xdp_is_enabled(adapter))
+               pool_rlpml = min_t(u32, pool_rlpml, adapter->max_frame_size);
+       vmolr &= ~E1000_VMOLR_RLPML_MASK;
+       vmolr |= pool_rlpml | E1000_VMOLR_LPE;
 
        wr32(E1000_VMOLR(vfn), vmolr);
 
@@ -6713,7 +6725,7 @@ static int igb_change_mtu(struct net_device *netdev, int 
new_mtu)
                for (i = 0; i < adapter->num_rx_queues; i++) {
                        struct igb_ring *ring = adapter->rx_ring[i];
 
-                       if (max_frame > igb_rx_bufsz(ring)) {
+                       if (max_frame + IGB_TS_HDR_LEN > igb_rx_bufsz(ring)) {
                                netdev_warn(adapter->netdev,
                                            "Requested MTU size is not 
supported with XDP. Max frame size is %d\n",
                                            max_frame);

-- 
2.27.0


Reply via email to