Skip to content

genet MTU for upstream - #7617

Draft
nbuchwitz wants to merge 8 commits into
raspberrypi:rpi-6.18.yfrom
nbuchwitz:devel/genet-mtu-rpi
Draft

nbuchwitz wants to merge 8 commits into
raspberrypi:rpi-6.18.yfrom
nbuchwitz:devel/genet-mtu-rpi

Conversation

@nbuchwitz

Copy link
Copy Markdown
Contributor

Based on @6by9's #7614, with the goal of upstreaming the MTU support.

I've tested the original patch on CM4 and discovered some issues. So I've created a slightly different patch (series) which I intend to send to netdev. It also contains some fixes Sashiko would have flagged any way...

  1. TBUF_PKT_RDY_THLD (TBUF + 0x10) is never programmed and it stays at 0x80. At MTU 3824 TX iperf3 is stuck at 0.00 Mbit/s while ping works and the link is up. tx_pkts rises, but tx_good_pkts doesn't. Kudos to @wtschueller who discovered this Jumbo frame support on Pi4 ethernet (Genet) #5561
  2. UMAC_MAX_FRAME_LEN gets the MTU value, but it's a frame length and counts the FCS. Frames from 3824 up result in rx_length_errors (at least in my testing), so the real limit seems to be MTU 3806.
  3. Wire budget is THLD*16-2 = 3838, so 3824 + VLAN = 3842 breaks setups with VLANs configured. Therefore I used 3820.
  4. RX_BUF_LENGTH 10240 costs no throughput (936/941 at MTU 1500, same as unpatched) but is above KMALLOC_MAX_CACHE_SIZE on arm64, thus it cant hurt to derive it from the MTU instead.

0xf0 seems to be the real limit: 0xfb receives fine but resulted in TX hard-hung on my setup.

Happy to add @6by9 as Co-developed-by since it's based on your findings. But this requires a Signed-off, which I wouldn't add without consent.

@nbuchwitz

Copy link
Copy Markdown
Contributor Author

39dfaf2 contains a brutal approach to make MTU 9000 work (without any offloading). Performs quite ok, but needs more testing

@6by9

6by9 commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

I'm still waiting on documentation from Broadcom to read the official word on how jumbo frames with offload was meant to work (if it was).

Seeing as it was the offloading headers that seemed to cause issues, I did wonder if disabling offloading would allow it to work with bigger buffers. I only had a very quick read through the patches, but wonder if we can "dynamically" disable offload when the mtu is increased above the magic threshold. Possibly not based on the comment of losing the queues as well.

I had considered VLAN headers, but didn't know the answer off the top of my head, and wasn't in a position to set up VLANs to test. Thanks for taking care of it.

I'm not fussed over Co-developed-by:. I'm very grateful that someone else is having a look at the patches, particularly when they're looking to upstream it too.

@nbuchwitz

Copy link
Copy Markdown
Contributor Author

@ffainelli and @Ryceancurry if you can spare some time, your thoughts on this would be really appreciated (as always). Thanks!

@starchivore

Copy link
Copy Markdown

https://lore.kernel.org/netdev/20260406-devel-autonomous-eee-v1-1-b335e7143711@tipi-net.de/t/

Other BCM54xx PHYs likely have the same AutogrEEEn register layout, but I only have access to the BCM54210PE/BCM54213PE datasheets.


https://datasheets.raspberrypi.com/cm4/cm4-datasheet.pdf#page=7

The CM4 has an on-board Gigabit Ethernet PHY — the Broadcom BCM54210PE

https://www.broadcom.com/products/ethernet-connectivity/phy-and-poe/copper/gigabit/bcm54210

• Supports jumbo packets up to 18 KB


https://magazine.raspberrypi.com/articles/raspberry-pi-4-in-detail

The BCM54213PE chip connects the Ethernet to a high-speed interface to the CPU.

https://www.broadcom.com/products/ethernet-connectivity/phy-and-poe/copper/gigabit/bcm54213

• Support for jumbo packets up to 10 KB


While we do understand the importance of taking one step at a time, it would be great to test whether 10K (BCM54213PE) and 18K (BCM54210PE) are genuinely supported by the hardware or otherwise. Thanks.

@Ryceancurry

Ryceancurry commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

With the status blocks off jumbo frames seem to come through.

Can you give me more color on the failure? Do we see fragmented packets? Is the packet corrupted? Or do we not receive a RX descriptor at all?

Full disclosure, I threw AI at the RTL(I'm a SW guy), it suggests a RTL bug where the RSB is reserved at every packet ready threshold. So I wonder if we are seeing a 64B hole between each 3820B chunk within the jumbo packet. At least that is the running theory right now. I will continue to dig.

@herisson-88

Copy link
Copy Markdown

Status blocks off, what's the true max frame length the MAC can handle ?

@nbuchwitz

Copy link
Copy Markdown
Contributor Author

Thanks for looking into this too!

With the status blocks off jumbo frames seem to come through.

Can you give me more color on the failure? Do we see fragmented packets? Is the packet corrupted? Or do we not receive a RX descriptor at all?

With status block enabled and threshold at 0xf0 I get a descriptor (one per oversized frame):

desc len=3904 status=0x0f402000 SOP=1 EOP=0

3904 = 64 (RSB) + 2 (align) + 3838. Payload is fine and matches my test pattern. I also don't see any holes, just a hart cut off.

I've also tested with 3840, 5000 and 9014 B frames and all of them produce the same descriptor.

Some things I've noticed and might be worth mentioning:

  • the MAC MIB counts the frame correctly (9014 B increments rx_4096_9216_oct), so the MAC gets the complete frame and I suspect the loss somewhere in RBUF to RDMA handoff
  • no corruption to follow-up traffic, sending 9014 and 1514 B one after the other, every 1514 one is OK

For contrast, with RBUF_64B_EN and TBUF_64B_EN cleared, MTU 9000 works at line speed with byte exact payloads and the threshold still at 0xf0.

@nbuchwitz

Copy link
Copy Markdown
Contributor Author

Seeing as it was the offloading headers that seemed to cause issues, I did wonder if disabling offloading would allow it to work with bigger buffers. I only had a very quick read through the patches, but wonder if we can "dynamically" disable offload when the mtu is increased above the magic threshold. Possibly not based on the comment of losing the queues as well.

I've tested further and came up with a solution which allows to switch to higher MTU on a live interface (tested 1514, 4096, 8192, 9014 B frames with threshold at 0xf0). Anyway, blocks needs to be disabled for anything higher. If I keep the TSB to preserve TX checksum offload, RX is still fine at 986 Mbit/s but TX drops to 0.

A while ago I proposed to get rid of the TX queues in genet [1]. Florian and Justin reviewed and tested it, but the reasoning was not good enough. Even though the queues are not absolutely blocking it,the TSB has no queue selection role anymore and it would simplify the jumbo patch. Might be worth a v2.

[1] https://lore.kernel.org/netdev/20260612205915.3156127-1-nb@tipi-net.de/

@herisson-88

Copy link
Copy Markdown

Patch tested on Audiolinux.
Many hours of Diretta streaming at MTU 9000 without issue.

@Ryceancurry

Ryceancurry commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Status blocks off, what's the true max frame length the MAC can handle ?

As far as I can see the only limitation is the MAC's 14 bit frame len field. so 16383B

I reproduced the 9000B frames with RSB enabled. I printed out the entire 9000B packet and see corruption at each PKT RDY THRESHOLD. 3838B and ~7700B. This confirms my suspicion. The HW puts a 64B header per PKT RDY THRESHOLD. I think the correct way to do this is to set rx_buf_size to PKT RDY THRESHOLD. Then use rx scatter gather with multiple descriptors. We need to strip 64B off of each fragment. Unfortunately this means a big rework on the RX side.

@herisson-88

Copy link
Copy Markdown

@nbuchwitz we can test 16K with the current patch, it is just a question of ENET_MAX_JUMBO_MTU ?

@nbuchwitz
nbuchwitz force-pushed the devel/genet-mtu-rpi branch 2 times, most recently from b060753 to 124162d Compare September 12, 2026 18:57
@nbuchwitz

Copy link
Copy Markdown
Contributor Author

I reproduced the 9000B frames with RSB enabled. I printed out the entire 9000B packet and see corruption at each PKT RDY THRESHOLD. 3838B and ~7700B. This confirms my suspicion. The HW puts a 64B header per PKT RDY THRESHOLD. I think the correct way to do this is to set rx_buf_size to PKT RDY THRESHOLD. Then use rx scatter gather with multiple descriptors. We need to strip 64B off of each fragment. Unfortunately this means a big rework on the RX side.

That helped a lot, thanks. I swapped the MTU 9000 patch for your approach and it works well. The TSB even can stay on with a little quirk. I also bumped max_mtu to what the 14 bit UMAC_MAX_FRAME_LEN allows, 16347.

Do you now if the status block bug is in all GENET (non v1) versions? I only have v5 here to test.

@herisson-88

Copy link
Copy Markdown

@nbuchwitz work great in 1G (I get 9184 the limitation is on other side) but configured in 100M there are packet loss with mtu > 9080

@antonellocaroli

Copy link
Copy Markdown

I reproduced the 9000B frames with RSB enabled. I printed out the entire 9000B packet and see corruption at each PKT RDY THRESHOLD. 3838B and ~7700B. This confirms my suspicion. The HW puts a 64B header per PKT RDY THRESHOLD. I think the correct way to do this is to set rx_buf_size to PKT RDY THRESHOLD. Then use rx scatter gather with multiple descriptors. We need to strip 64B off of each fragment. Unfortunately this means a big rework on the RX side.

That helped a lot, thanks. I swapped the MTU 9000 patch for your approach and it works well. The TSB even can stay on with a little quirk. I also bumped max_mtu to what the 14 bit UMAC_MAX_FRAME_LEN allows, 16347.

Do you now if the status block bug is in all GENET (non v1) versions? I only have v5 here to test.

Could you show me the little quirk you used to keep the TSB enabled? On my Raspberry Pi 4 / GENET v5, MTU 13500 works, but around 13505 it becomes unstable and MTU 14000 fails with RX CRC errors. I noticed that a 14000 MTU results in a 14014-byte skb becoming a 14078-byte DMA buffer after the 64-byte TSB is added.

@nbuchwitz

nbuchwitz commented Sep 13, 2026

Copy link
Copy Markdown
Contributor Author

Could you show me the little quirk you used to keep the TSB enabled? On my Raspberry Pi 4 / GENET v5, MTU 13500 works, but around 13505 it becomes unstable and MTU 14000 fails with RX CRC errors. I noticed that a 14000 MTU results in a 14014-byte skb becoming a 14078-byte DMA buffer after the 64-byte TSB is added.

The "quirk" is to dynamically switch of TX checksum based on the mtu (threshold is the previous 3820). See the last patch for details.

Pi4 is afaik limited by the phy around 10k (see comment above). Cm4 should (theoretically) something around 18k

@nbuchwitz

Copy link
Copy Markdown
Contributor Author

@nbuchwitz work great in 1G (I get 9184 the limitation is on other side) but configured in 100M there are packet loss with mtu > 9080

Haven't tested it yet with fast ethernet. If the time permits I will do some measurements with different mtu and speed. I want to measure the cpu impact of sw checksum. For jumbo frames I assume not much of a penalty

@antonellocaroli

Copy link
Copy Markdown

@nbuchwitz work great in 1G (I get 9184 the limitation is on other side) but configured in 100M there are packet loss with mtu > 9080

Haven't tested it yet with fast ethernet. If the time permits I will do some measurements with different mtu and speed. I want to measure the cpu impact of sw checksum. For jumbo frames I assume not much of a penalty

Thanks, that clarifies the TSB quirk. Interestingly, with two Pi4 Model B (Rev 1.1 and Rev 1.5) directly connected, I can get MTU 13500 working reliably in one direction (10/10 pings), while the opposite direction fails. Around 13503–13507 it becomes unstable/fails. So the Pi4 PHY seems capable of going significantly beyond 10k in at least some cases. Do you know what exactly imposes the ~10k PHY limit you mentioned (PHY register/buffer/specification), and whether it differs between Pi4 board revisions?

@nbuchwitz

nbuchwitz commented Sep 13, 2026

Copy link
Copy Markdown
Contributor Author

Do you really see such big payload or is this already capped by phy and it just works "magically" with the 10k limit?

Limit is stated in the datasheet. So I'd assume it's related to the buffer / state machine

@antonellocaroli

Copy link
Copy Markdown

Do you really see such big payload or is this already capped by phy and it just works "magically" with the 10k limit?

Limit is stated in the datasheet. So I'd assume it's related to the buffer / state machine

Yes, at least at the GENET MAC/driver level I really see the full size. For 10 successful MTU 13500 pings, txq3_packets increases by 10 and txq3_bytes by 135140, i.e. exactly 13514 bytes per packet. tx_oversize also increases by 10 on TX and rx_oversize by 10 on RX, with no additional CRC errors. I'm using ping -M do, so there is no IP fragmentation.

However, I haven't verified on the wire between MAC and PHY, so you're right that this doesn't prove the PHY actually handles the full ~13.5K frame as such. Interestingly, Rev 1.1 -> Rev 1.5 works at MTU 13500, while Rev 1.5 -> Rev 1.1 fails, even though the receiving side counts the request and generates a 13514-byte reply.

Which PHY datasheet/section states the ~10K limit? I'd like to check exactly what that limit refers to.

@nbuchwitz

Copy link
Copy Markdown
Contributor Author

@antonellocaroli

antonellocaroli commented Sep 13, 2026

Copy link
Copy Markdown

https://www.broadcom.com/products/ethernet-connectivity/phy-and-poe/copper/gigabit/bcm54213pe

Support for jumbo packets up to 10 KB

Thanks!

yes, I confirmed that both of my Pi4s (Rev 1.1 and Rev 1.5) are using the BCM54213PE PHY (phy_id 0x600d84a2).

So the 10 KB limit you mentioned is indeed the one stated in the BCM54213PE datasheet.

However, MTU 13500 is really passing end-to-end in my tests: with ping -M do -s 13472, I can get 10/10 replies with no fragmentation. So it looks like the 10 KB figure is a guaranteed/specification limit rather than a strict hardware cutoff.

Above that it becomes unreliable very quickly (around 13503–13507 in my tests), and at MTU 14000 it fails. I also see RX CRC errors when operating around this boundary, so this is clearly outside the PHY's guaranteed operating range.

Interestingly, both Pi4 revisions use exactly the same BCM54213PE, so the different behaviour I saw between the two boards isn't explained by a different PHY model.

I agree that this could be related to an internal PHY buffer/state-machine limit rather than a simple hard packet-size check.

@herisson-88

Copy link
Copy Markdown

I will be able to test 16k CM4 tomorrow

bcmgenet_hfb_init() runs INIT_LIST_HEAD() on priv->rxnfc_list, which drops
every rule off the list, and bcmgenet_open() calls it on each ifup. Every
rule the user configured is silently lost:

  # ethtool -N eth0 flow-type ether dst $MAC action 0
  Added rule with ID 0
  # ethtool -n eth0 | grep -c Filter:
  1
  # ip link set eth0 down && ip link set eth0 up
  # ethtool -n eth0 | grep -c Filter:
  0

Initialise the lists once at probe and restore the rules on open, as
bcmgenet_resume() already does.

Fixes: 3e37095 ("net: bcmgenet: add support for ethtool rxnfc flows")
Signed-off-by: Nicolai Buchwitz <nb@tipi-net.de>
@nbuchwitz

nbuchwitz commented Sep 14, 2026

Copy link
Copy Markdown
Contributor Author

79b69ad should fix this hopefully. Please note if you're testing against another RPi you might want to patch both systems, as this affects the transceiver side.

@nbuchwitz

Copy link
Copy Markdown
Contributor Author

@herisson-88 It would be also great if you can test #7623 as this will be the version for upstream (with backports). It also contains the phy fix

@herisson-88

Copy link
Copy Markdown

@nbuchwitz currently compile kernel with #7623, RTL8156BG arrived (hope there is not issue on this side to not pollute with false positive... It is supposed go to 16K - friends are using it for that.) I have also another old RPI4 that I could use with the CM4 in the other side to use your kernel patchs on both side.

@Ryceancurry

Copy link
Copy Markdown
Contributor

Do you now if the status block bug is in all GENET (non v1) versions? I only have v5 here to test.

Maybe the right way to think about it is that the original one descriptor per jumbo frame wasn't meant to work. It just so happened to work with the RSB disabled. So it is safe to assume this is the correct way to do things in all revisions of genet. The RTL also corroborates.

Thanks for taking this on. Good work so far!

@herisson-88

herisson-88 commented Sep 14, 2026

Copy link
Copy Markdown

bcmgenet jumbo patch on CM4 — native DSD over jumbo frames, test report (2026-09-14)

Three tests, all conclusive: a native DSD512 Diretta stream (2 × 22.58 MHz, ~46 Mb/s) carried in
~16 KB Ethernet frames into the CM4 at 1 Gb/s, sustained, zero errors; a ping test at
10 / 100 / 1000 Mb/s with 9000 to 16000-byte frames, zero loss; and native DSD256 at 100 Mb/s
with 10000-byte frames — the exact case that failed on 6.18.50-1 — streaming clean.

Setup

  • Target: Holo Audio Red = Raspberry Pi Compute Module 4 Rev 1.1, AudioLinux (Arch Linux ARM),
    linux-rpi 6.18.50-2 PREEMPT_RT (Piero's package of 2026-09-14, build
    Mon Sep 14 13:35:35 CEST 2026) with your bcmgenet jumbo patch, GENET 5.0, external RGMII,
    interface end0. Diretta Target library 148 (diretta_alsa_target), ExtEtherMTU set to
    the link MTU of each test.

  • Host: x86 NUC, Fedora 44 (kernel 7.2.4), USB 3 2.5 GbE dongle Realtek RTL8156B (in-tree
    r8152, maxmtu 16362), hardware offloads off. Diretta host: DirettaRendererUPnP, transfer
    mode VarMax with a 20 ms cycle, DSD sent natively (no DoP).

  • Direct cable, no switch. The CM4 is 1 GbE, so 1000 Mb/s is the maximum link speed.

    CM4

    ip -d link show end0 → mtu 16000 minmtu 68 maxmtu 16347

    host

    ip -d link show eth-diretta2 → mtu 16000 minmtu 68 maxmtu 16362

1. Native DSD512 at 1000 Mb/s, MTU 16000 on both ends — 30 s of playback

Host side, interface counters (/sys/class/net/eth-diretta2/statistics):

Frames sent 10 948 in 30 s (364 per second)
Average frame size on the wire 15 979 bytes
Throughput 46.65 Mb/s
Host tx_errors / rx_errors 0 / 0

CM4 side, ethtool -S end0 before and after the same 30 s:

Counter Before After Delta
rx_pkts 550 751 561 709 +10 958 (matches the host)
rx_crc_errors 0 0 0
rx_frame_errors 0 0 0
rx_jabber 0 0 0
rx_length_errors 0 0 0
rx_oversize 93 921 104 858 +10 937 (see the note at the end)
rx_good_pkts 456 830 456 851 +21 (the small control frames only)

Load average on the CM4 during playback: 0.32. Audio continuous, no dropout during the session;
the listener's verdict was "perfect".

For scale: with the Diretta host SDK's default Auto transfer mode, a 48 kHz PCM stream on the
same link was measured at ~784-byte frames, 500 per second. Large frames only appear once the
transfer mode asks for them (VarMax); with it, DSD512 travels in 364 frames per second.

2. Ping test: 12 combinations, zero loss

Method: 100 ICMPv6 echo requests per combination, DF bit set (no fragmentation allowed),
20 ms apart, run from the host to the CM4 and then from the CM4 to the host. Link speed forced
on the host side, the CM4 following by auto-negotiation. Sizes are IP packet sizes.

Link speed 9000 bytes 10000 bytes 14000 bytes 16000 bytes
1000 Mb/s 0 lost / 100 0 lost / 100 0 lost / 100 0 lost / 100
100 Mb/s 0 lost / 100 0 lost / 100 0 lost / 100 0 lost / 100
10 Mb/s 0 lost / 100 0 lost / 100 0 lost / 100 0 lost / 100

Identical result in the other direction (CM4 → host): 0 lost out of 100 in every combination.
Total 2400 packets sent, 2400 received. CM4 counters rx_crc_errors, rx_frame_errors and
rx_jabber stayed at 0 throughout.

This closes the 100BASE-TX issue of the 2026-09-13 report (frames above ~9080 bytes were
dropped with jabber/CRC errors on 6.18.50-1): with the PHY fix in 6.18.50-2, 16000-byte
frames pass at 100 Mb/s and even at 10 Mb/s.

3. Native DSD256 at 100 Mb/s, MTU 10000 on both ends — the case that failed on 6.18.50-1

Link forced to 100 Mb/s full duplex, ExtEtherMTU=10000, native DSD256 (2 × 11.29 MHz,
~22.6 Mb/s), 30 s of playback:

Frames host → CM4 8 481 in 30 s (282 per second), average 10 004 bytes
Throughput / link load 22.6 Mb/s, 22 % of the 100 Mb/s link
CM4 rx_pkts +8 763 (audio frames + control)
CM4 rx_crc_errors / rx_frame_errors / rx_jabber 0 / 0 / 0
Renderer warnings none; audio plays perfectly

Note on the MIB statistics (cosmetic, no effect on traffic)

Measured on both the 16000-byte and the 10000-byte streams: every jumbo frame increments
rx_pkts and rx_oversize by one, rx_good_pkts does not move, and no size bucket moves either
(the last one is rx_4096_9216_oct). Example over 10 s of the 10000-byte stream: rx_pkts
+2 826, rx_oversize +2 826, rx_good_pkts +0, host sent 2 827 frames. The frames are delivered
intact (CRC 0, the DAC plays them); only ethtool -S reads as if every jumbo frame were bad.
Whether that is a threshold register worth updating along with the MTU, or simply how the GENET
MIB block is defined, is your call.

Reproduction

# both ends
ip link set <if> mtu 16000
# CM4 counters, before and after a stream
ethtool -S end0 | grep -E 'rx_pkts|rx_good_pkts|rx_oversize|rx_crc_errors|rx_frame_errors|rx_jabber'
# host frame rate / size
cat /sys/class/net/<if>/statistics/tx_packets /sys/class/net/<if>/statistics/tx_bytes
# ping test, DF bit, IP size S
ping -6 -M do -s $((S-48)) -c 100 -i 0.02 <link-local>%<if>

Thanks for the patch — it does exactly what was hoped for.

Will test with Rpi 4 tomorow, but CM4 is 16k compliant now.

@herisson-88

Copy link
Copy Markdown

bcmgenet jumbo patch on Raspberry Pi 4 Model B (BCM54213PE) — test report (2026-09-15)

Follow-up to the CM4 report above, same kernel build, this time with the Pi 4 Model B as the
Diretta host. Two results: a ping sweep at 1 Gb/s from 9020 to 16000-byte frames, 10 000 pings,
zero loss — the ~13 478-byte wall seen on Pi 4 before the bcm_phy_enable_jumbo() fix is gone;
and a native DSD512 album playing right now through ~16 KB frames from the Pi 4 to the CM4,
16 minutes and 8 tracks so far, frames counted on both ends, zero errors.

Setup

  • Host: Raspberry Pi 4 Model B Rev 1.4 (revision code b03114, 2 GB, Sony UK), BCM2711
    (4× Cortex-A72 r0p3, 1.8 GHz, arm_boost=1), GENET v5, external RGMII, PHY BCM54213PE
    (phy_id 0x600d84a2, the one whose datasheet says "jumbo packets up to 10 KB"), interface
    end0. AudioLinux (Arch Linux ARM), linux-rpi 6.18.50-2 PREEMPT_RT (Piero's package of
    2026-09-14, build Mon Sep 14 13:35:35 CEST 2026, i.e. the reassembly version with the
    PHY jumbo fix). Bootloader EEPROM 2020-09-03, start4.elf 2026-09-14.

  • Target: Holo Audio Red = Raspberry Pi Compute Module 4 Rev 1.1, same kernel package
    (see previous report), ExtEtherMTU=16000.

  • Direct cable between the two GENETs, no switch, autoneg on, link at 1000 Mb/s full duplex.
    Both sides patched, as advised for the PHY-side fix.

  • Diretta host software: DirettaRendererUPnP 2.5.19, --mtu 16000, transfer mode VarMax,
    DSD sent natively (no DoP). The Pi 4's LAN side is a separate USB 3 GbE dongle (ASIX AX88179A,
    ax88179_178a, MTU 1500) so end0 carries Diretta traffic only.

    Pi 4

    ip -d link show end0 → mtu 16000 minmtu 68 maxmtu 16347

    CM4

    ip -d link show end0 → mtu 16000 minmtu 68 maxmtu 16347

1. Ping sweep at 1000 Mb/s, MTU 16000 on both ends — 1000 pings per size

ping -6 -M do -c 1000 -i 0.005 -W 1 -s <payload> <link-local>%end0, Pi 4 → CM4:

Frame (IP size) Payload Sent / received Loss RTT min / avg / max (ms)
9 020 8 972 1000 / 1000 0 % 0.224 / 0.241 / 0.544
10 020 9 972 1000 / 1000 0 % 0.244 / 0.263 / 0.696
11 020 10 972 1000 / 1000 0 % 0.259 / 0.283 / 4.012
12 020 11 972 1000 / 1000 0 % 0.278 / 0.301 / 0.559
13 020 12 972 1000 / 1000 0 % 0.298 / 0.319 / 0.634
13 478 13 430 1000 / 1000 0 % 0.307 / 0.325 / 0.654
13 520 13 472 1000 / 1000 0 % 0.306 / 0.326 / 0.541
14 020 13 972 1000 / 1000 0 % 0.317 / 0.336 / 0.579
15 020 14 972 1000 / 1000 0 % 0.324 / 0.348 / 1.079
16 000 15 952 1000 / 1000 0 % 0.345 / 0.366 / 0.671

Pi 4 end0 before / after the sweep: rx_errors 0 / 0, rx_crc_errors 0 / 0,
rx_length_errors 0 / 0, rx_align 0 / 0, tx_errors 0 / 0. RTT grows linearly with frame
size (about 12 µs per extra KB, i.e. wire time at 1 Gb/s) with no outliers at the old threshold.

Sizes 13 478 and 13 520 were chosen deliberately: 13 478 is where Snyder's Pi 4 started losing
packets at 1 Gb/s on the pre-fix build (report of 2026-09-14), and ~13 500 is where
@antonellocaroli saw his two Pi 4s become unstable. With bcm_phy_enable_jumbo() on both ends
neither threshold is visible any more; the BCM54213PE passes 16 000-byte frames at 1 Gb/s
despite the 10 KB figure on its product page.

2. Native DSD512 at 1000 Mb/s, MTU 16000 on both ends — real payload, both ends counted

Pings prove the PHY passes the frames; this proves the frames carry audio that plays. The Pi 4
has been streaming a native DSD512 album (DFF, 2 × 22.5792 MHz, ~46 Mb/s) to the CM4 since
08:27:34 — 8 tracks opened back to back in the renderer journal by 08:43, no gap, no dropout,
the DAC playing throughout. Diretta SDK profile negotiated with the CM4: cycle=2834us,
cycleSize=15992B, packets/cycle=1, reqMTU=16000 maxMTU=16000 — one Ethernet frame per cycle.

Counters read on both ends around the same 31 s window, mid-album (08:42:42 → 08:43:13):

Pi 4 (host, TX) CM4 (target, RX)
Frames 10 725 sent (346 per second) rx_pkts +10 955 (audio frames + Diretta control)
Bytes / average frame size 171 354 928 B / 15 977 B per frame rx_oversize +10 935 (one per jumbo frame, see MIB note)
Throughput 44.3 Mb/s (track change inside the window)
Errors tx_errors 0, tx_dropped unchanged rx_crc_errors 0, rx_frame_errors 0, rx_jabber 0, rx_length_errors 0
Software renderer: no underrun / xrun, SoC 61.8 °C, get_throttled=0x0 diretta_alsa_target active, end0 mtu 16000 / maxmtu 16347, 1000 Mb/s full

Every frame the Pi 4 put on the wire was received intact by the CM4: the 230-frame difference
is the target's own control traffic (rx_good_pkts +20 for the ≤1518-byte ones plus the
Diretta return channel). A shorter 10 s sample taken earlier at track steady state gave
362 frames per second, 16 003 bytes per frame, 46.4 Mb/s, end0 interrupts 374 per second
(all on CPU0), CPU 1.4–3.4 % per core, audio thread SCHED_FIFO 50 at 1.8 %.

The ping sweep of section 1 ran while this stream was playing; neither disturbed the other.

Note on the MIB statistics (same as on the CM4)

Every jumbo frame increments tx_oversize (host side) and rx_oversize (target side) by one
and no size bucket above 4096_9216 moves. On the Pi 4 after the sweep: rx_oversize +10 014
for the 10 000 echo replies, rx_4096_9216_oct +1 002 (only the 9 020-byte replies land in a
bucket), tx_oversize at 258 125 after a few minutes of DSD512. Cosmetic: CRC counters stay at
zero and the payload is intact.

Not covered here

100 Mb/s and 10 Mb/s on the Pi 4 side (the CM4 report already covers those speeds with the PHY
fix); can be run on request with the same script.

Thanks again — with the PHY fix, Pi 4 Model B is 16 K-clean at 1 Gb/s too.

@nbuchwitz

Copy link
Copy Markdown
Contributor Author

Thanks for testing. I will have a look regarding the MIB counters, but am not really confident that this can be fixed as rx_oversize and rx_good_pkts come straight from the UMAC. The hardware counts everything above 1518 bytes as oversize and leaves it out of good_pkts, which is older than any jumbo support.

Functionality is not affected and the interface statistics are correct (rx_packets matches and no error counter), so I'd assume this is ok.

@nbuchwitz

Copy link
Copy Markdown
Contributor Author

Do you now if the status block bug is in all GENET (non v1) versions? I only have v5 here to test.

Maybe the right way to think about it is that the original one descriptor per jumbo frame wasn't meant to work. It just so happened to work with the RSB disabled. So it is safe to assume this is the correct way to do things in all revisions of genet. The RTL also corroborates.

Thanks for taking this on. Good work so far!

Thanks for the confirmation. I will mention this in my cover letter, so clashiko does not have to ask 😄

@nbuchwitz

Copy link
Copy Markdown
Contributor Author

@herisson-88 may I add a Tested-by: Name <email> to the upstream series for your testing?

@herisson-88

Copy link
Copy Markdown

If you want 🤗 but it is very small things...

@nbuchwitz

Copy link
Copy Markdown
Contributor Author

It is always good to see if someone else than the author has actually tested the patches. I would need the name and mail i should use for the tag

@herisson-88

Copy link
Copy Markdown

Pierre-Marin Leclercq pierremarinleclercq88@gmail.com

@Oleks1y

Oleks1y commented Sep 16, 2026

Copy link
Copy Markdown

Wow, 16000MTU is fantastic achivement!

@wtschueller

Copy link
Copy Markdown

First of all, thank you all so much for your continued efforts.

Unfortunately, a quick smoke test (based #5561 (comment)):

for ((MTU=1400; MTU<4000; ((MTU=MTU+4)) )) do sudo ifconfig eth0 mtu ${MTU} ; ifconfig eth0 ; dd if=/dev/zero bs=65536 count=10 |netcat -u 169.254.1.1 65000 -q 1; done

still causes eth0 on my RPi4 to freeze:

[  665.887455] bcmgenet fd580000.ethernet eth0: NETDEV WATCHDOG: CPU: 2: transmit queue 4 timed out 2004 ms
[  667.891440] bcmgenet fd580000.ethernet eth0: NETDEV WATCHDOG: CPU: 2: transmit queue 4 timed out 2004 ms
[  669.895440] bcmgenet fd580000.ethernet eth0: NETDEV WATCHDOG: CPU: 3: transmit queue 4 timed out 2004 ms
[  671.899457] bcmgenet fd580000.ethernet eth0: NETDEV WATCHDOG: CPU: 2: transmit queue 4 timed out 2004 ms
[  682.783457] bcmgenet fd580000.ethernet eth0: NETDEV WATCHDOG: CPU: 3: transmit queue 3 timed out 2656 ms
[  692.415409] bcmgenet fd580000.ethernet eth0: Timed out while disabling TX DMA
[  692.444144] bcmgenet fd580000.ethernet eth0: failed to halt Tx DMA
[  692.450590] bcmgenet fd580000.ethernet eth0: failed to halt Tx DMA
[  692.450608] bcmgenet fd580000.ethernet eth0: failed to restore MTU 3572, closing
[  692.450750] bcmgenet fd580000.ethernet eth0: Link is Down

Hopefully I'm wrong and I is just some mistake.
Test against https://github.com/nbuchwitz/linux/tree/devel/genet-mtu-upstream.

@herisson-88

herisson-88 commented Sep 17, 2026

Copy link
Copy Markdown

@wtschueller I can reproduce your MTU sweep freeze here too, on the CM4 (Holo Red) from my reports above.

Kernel: the same linux-rpi 6.18.50-2 PREEMPT_RT package (Piero's AudioLinux build of 2026-09-14) that passed the fixed-MTU tests (9000 to 16000, pings and DSD streaming) in my two reports. So the jumbo path is fine at a fixed MTU; what fails is the transition.

Test: your loop, MTU 1400 to 4000 in steps of 4, 10 × 65000-byte UDP datagrams per step. Two small differences: ip link set end0 mtu instead of ifconfig, and the datagrams sent with a Python script over IPv6 link-local (the only address on the direct link to the peer, an x86 host with an r8169 at MTU 9184). Sender = the CM4, GENET v5, link at 1 Gb/s full duplex, Diretta target running on the side.

Result: 543 steps from 1400 to 3568 go through, ~0.16 s each, 10 datagrams sent every time, no error. The log stops after the step at 3568; the CM4 stopped answering (ping, ssh) during the step at 3572 and did not come back. The CM4 has a single interface, so no kernel log survived (volatile journal, log on tmpfs); I had to power-cycle it. A scripted ip link set end0 down / up from the box itself after the hang did not bring the link back either.

The last successful MTU (3568) and the failing one (3572) match your failed to restore MTU 3572 exactly. Both are the first steps above 3564, the single-descriptor limit of the page-pool series ("On 4K pages the page is the tighter limit and leaves 3564 bytes"), so the freeze seems to sit right at the switch to the multi-descriptor / software-checksum path under TX load, not in the jumbo path itself. Piero's package is built from #7623.

Happy to run it again with a serial console attached to capture the watchdog messages, or any variant you want (fixed 3572 under load, sweep 3500 to 3600 only, TX checksum forced off).

@herisson-88

Copy link
Copy Markdown

bcmgenet jumbo patch on CM4 — TX freeze at MTU 3572, and where it starts and stops (2026-09-17/18)

Follow-up to @wtschueller's MTU sweep report. Same hardware and kernel as my two reports above.
Summary: the sweep freezes here too, at the same MTU. With fixed MTUs, frames of 3586 to 3618 bytes
(MTU 3572 to 3604) freeze TX, frames of 3582 and below and of 3622 and above (MTU 3568, 3608 and up
to 16000) work. Frames just above the next multiples of the threshold (7168, 10752, 14336) do not freeze.

Setup

  • Target under test: Holo Audio Red = Raspberry Pi Compute Module 4 Rev 1.1, GENET v5, external
    RGMII, interface end0, AudioLinux (Arch Linux ARM), linux-rpi 6.18.50-2 PREEMPT_RT (Piero's
    package of 2026-09-14, the build with the reassembly patch and the bcm_phy_enable_jumbo() fix),
    ip -d link show end0maxmtu 16347. diretta_alsa_target running on the side.
  • Peer: x86 NUC, Fedora 44 (kernel 7.2.4), on-board Realtek RTL8168 (r8169), MTU 9184, no IPv4
    on the link, IPv6 link-local only. Direct cable, no switch, 1000 Mb/s full duplex, autoneg.
  • The CM4 has a single interface, so a TX freeze cuts ssh too. The log is written on the CM4
    and mirrored line by line to the NUC over the link; each step is logged before its traffic starts,
    so the mirrored log always shows which step froze. Every freeze needed a power cycle of the CM4
    (root filesystem read-only, journal volatile: no kernel log survived from after the freeze).

1. The MTU sweep (wtschueller's loop)

Loop: MTU 1400 to 4000 in steps of 4; at each step ip link set end0 mtu <MTU>, then 10 UDP
datagrams of 65000 bytes (Python, sendto() on an AF_INET6 socket) to the NUC's link-local address,
port 65000. Differences from the original: ip link instead of ifconfig, IPv6 instead of IPv4
169.254.x, Python instead of netcat -u.

Step Result
1400 → 3564 542 steps, ~0.17 s each, 10 datagrams sent every time, no error
3568 sent, last step logged
3572 freeze: no further log line, no answer to ping or ssh, power cycle needed

wtschueller's log ends with failed to restore MTU 3572 (interface was at 3572 when the next
change failed).

2. Fixed-MTU probes

For each MTU: ip link set end0 mtu <MTU>; 30 datagrams of 65000 bytes, 1 s pause, 30 more;
ethtool -S tx_pkts before/after; 3 pings to the NUC; back to MTU 16000; 3 pings. Runs stop at
the first freeze (power cycle), then continue with the remaining values.

"OK" = 60 datagrams sent, tx_pkts up by 1019 to 1144 (fragments), all pings answered, no
NETDEV WATCHDOG in dmesg. "Freeze" = no further output from the CM4, no answer to ping.

Frame = MTU + 14 (Ethernet header, no VLAN, FCS not counted).

MTU Frame Result
3568 3582 OK (sweep)
3572 3586 freeze (sweep)
3600 3614 freeze
3604 3618 freeze
3608 3622 OK
3612 3626 OK
3616 3630 OK
3620 3634 OK
3624 3638 OK
3628 3642 OK
3632 3646 OK
3636 3650 OK
3648 3662 OK
3700 3714 OK
3840 3854 OK
4096 4110 OK

Not tested: MTU 3576 to 3596 (inside the freezing range), and MTU 4100 to 7144.

3. The next multiples of the threshold

Reading the driver, the packet ready threshold on this build is bounded by the 4K page at
0xE0 = 3584 bytes (ENET_THLD_PAGE_LEN), so 3584, 7168, 10752 and 14336 were probed the same way:

MTU Frame Relative to the multiple Result
7148 7162 6 below 7168 OK
7160 7174 6 above 7168 OK
7196 7210 42 above 7168 OK
10744 10758 6 above 10752 OK
10780 10794 42 above 10752 OK
14328 14342 6 above 14336 OK
14364 14378 42 above 14336 OK

Same criteria as section 2: 60 datagrams each, tx_pkts up by 305 to 602, pings answered, no
watchdog. (Frames above 9184 are dropped by the NUC's r8169 on receive; the CM4 still transmits them.)

Counts

  • 3 freezes in total (MTU 3572, 3600, 3604), each needing a power cycle.
  • 27 fixed MTUs probed, 24 OK.
  • All freezes happened during the UDP traffic at a fixed MTU, after the MTU change had completed
    (ip link set returned 0 and /sys/class/net/end0/mtu showed the new value).
  • No rx_crc_errors, rx_length_errors or tx_dma_failed at any of the OK steps.

Reproduction

# on the CM4, PEER = link-local IPv6 of the peer on end0
for MTU in 3608 3604; do          # 3608 works, 3604 freezes
  ip link set end0 mtu $MTU
  python3 - "$PEER" <<'PY'
import socket, sys
s = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
for i in range(30):
    s.sendto(b'\0' * 65000, (sys.argv[1], 65000, 0, socket.if_nametoindex('end0')))
PY
  ping -6 -c 3 -W 1 "$PEER%end0"
  ip link set end0 mtu 16000
done

@herisson-88

Copy link
Copy Markdown

4. Sweep above the window

Same loop as section 1, MTU 3608 to 16344 in steps of 4: 3185 steps, 10 datagrams each, no freeze, no watchdog, tx_pkts +269152, rx_crc_errors and tx_dma_failed at 0.

MTU 1400 to 16344 is now covered in 4-byte steps. The only freezing range found is MTU 3572 to 3604 (frames 3586 to 3618 bytes).

@nbuchwitz

Copy link
Copy Markdown
Contributor Author

Thanks for reporting and already pinning it down. I will grab some coffee and have a look...

@nbuchwitz

Copy link
Copy Markdown
Contributor Author

Found it. The transmit packet ready threshold was derived from the MTU, and a frame ending just past it stops the transmitter for good.

What actually matters is the frame length, not the MTU. A 3586 byte frame hangs at MTU 9000 too, while 3578 and 3622 are fine. MTU 3572 to 3604 is just where IP fragmentation produces those sizes.

My fix pins the threshold at its maximum. Pushed to #7623 (which matches the upstream series). Looking forward to your test results.

Thanks again for the reports, the fixed MTU table made this quick to find!

@herisson-88

Copy link
Copy Markdown

Same sweep on a Pi 4 Model B Rev 1.4 (Pi OS Trixie, rpi-update pulls/7623 at da22975, GENET v5, peer = RTL8156B at 1 Gb/s, MTU 16000, pause off), MTU 1400 to 16344 in steps of 4 with reboots after each freeze:

MTU Frame (no FCS) Result
1400 to 3828 up to 3842 OK (3572 to 3604 included)
3832 to 3868 3846 to 3882 freeze (3832 going up, 3868 going down)
3872 to 15348 3886 to 15362 OK
15352, 15356 15366, 15370 freeze (15352 going up, 15356 going down)
15360 to 16344 15374 and up OK

MTU 7660 to 7700 and 11500 to 11540 (around 2 × 3840 and 3 × 3840) pass. One step at MTU 4500 took 14.6 s instead of 0.11 s, with no kernel message, then the sweep went on normally. After each freeze ip link set up fails with failed to initialize DMA; a reboot was needed every time.

@nbuchwitz

Copy link
Copy Markdown
Contributor Author

Not what I have hoped for and at the moment I'm not sure how to proceed:

The transmitter stops for good when a frame slightly longer than TBUF_PKT_RDY_THLD is followed by a shorter one. Watchdog fires, TX DMA won't halt and every bcmgenet_init_dma() after that fails, so the interface can't be reopened.

Raw frames at MTU 9000:

  • threshold 0xe0 (3584 B): 3586 to 3618 stall
  • threshold 0xf0 (3840 B): 3842 to 3886 stall, 3890 is fine

So roughly threshold + 2 to + 46, and it moves with the register. 300 identical 3842 B frames are fine, but 17 of them followed by one shorter frame kill it on the first burst. Unfortunately that is what IP fragmentation produces, N full frames and a short tail.

Since the register tops out at 0xf0, every jumbo frame is past the threshold, so a few questions I hope @Ryceancurry can make sense of:

  • Is there a minimum number of bytes a frame has to extend past the threshold, or is a frame ending just after it simply not supported?
  • Why does the next frame matter? Does TBUF carry state between frames when the previous one was handed over early?
  • Is there a way to force store and forward on TX, so the threshold stops mattering?
  • Should TBUF_PKT_RDY_THLD be programmed at all for jumbo, or is the 0x80 reset default the only value you expect?

@wtschueller

Copy link
Copy Markdown

@herisson-88 I overlooked that part of your earlier report, sorry.
However, your latest findings nearly match, I think. I never did tests above 9k.

First, I noticed the freezes when I played with the very old patch (global mtu change) and kernel 5.10.
They were strictly limited to a narrow range of packet sizes (2048 ... 2096 bytes).
I could avoid the freezes by simply padding those packets by 64 bytes in the genet driver.
Later, I learned that this range was tightly coupled to TBUF_PKT_RDY_THLD.

As far as I remember, triggering the freeze with packets of only same size took hours.
It went much faster with alternating packet sizes.
The mtu sweep is a oneliner relying on fragmention. A far more realistic stress test goes like this:

import socket
import os
import subprocess

HOST = "169.254.1.1"
PORT = 5000
INTERFACE = "eth0"

mtu = 9000
subprocess.check_call([
    "sudo", "ifconfig", INTERFACE,
    "mtu", str(mtu)
])
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as sock:
    sock.connect((HOST, PORT))
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 2**20)

    blob = os.urandom(10*1024)
    for payload_size in range(1, 8192+36):
        print("payload size %d" % payload_size, flush=True)
        payload_1 = blob[:8192]
        payload_2 = blob[:payload_size]
        for _ in range(10000):
            sock.send(payload_1)
            sock.send(payload_2

My guess was that there might be some extra settings in the MAC allowing a safe write-through of packets larger than TBUF_PKT_RDY_THLD. Something similar is already done in the PHY.

@nbuchwitz

Copy link
Copy Markdown
Contributor Author

Padding seems to mitigate the issue at least in my testing. I've also ran the stress test from @wtschueller and it seems to work: c0beb41

@herisson-88

Copy link
Copy Markdown

@nbuchwitz trigger the ci on the PR would facilitate testing on my side 🤗

@nbuchwitz

Copy link
Copy Markdown
Contributor Author

The builds in 7623 are up to date (Phil already triggered the builds last night)

@herisson-88

Copy link
Copy Markdown

c0beb41 on Pi 4: sweep and TX stress clean, one RX length-error pattern

Pi 4 Model B Rev 1.4, rpi-update pulls/7623 at c0beb41, GENET v5, 1 Gb/s full duplex, direct cable to an x86 host (Fedora 44). Peer NIC: Realtek RTL8156B USB (r8152, MTU 16000), later the on-board RTL8168 (r8169, MTU 9184) for the cross-check.

1. MTU sweep (10 × 65000-byte UDP datagrams per step, steps of 4)

Direction Range Result
Pi → x86 1400 to 16344 3737 steps, no freeze, no bcmgenet message
x86 → Pi (Pi at MTU 16000) 1400 to 16344 3737 steps, no freeze; only rx_length_errors on the steps where the x86 MTU exceeds 16024, i.e. frames longer than the Pi's MTU

Yesterday's freezing ranges (MTU 3832 to 3868 and 15352 to 15356) are gone.

2. Stress test with payload validation

wtschueller's pattern at fixed MTU: one full-size datagram then one short datagram of variable size, N pairs per size, sender paced at 850 Mb/s. Each datagram carries a sequence number and a CRC32; the receiver checks the CRC and counts sequence gaps, reordering and duplicates. IPv6 link-local: frame = UDP payload + 62 bytes.

Pi → x86 (GENET transmits)

MTU Short payload Pairs/size Sent Received, CRC OK Lost
9000 8 to 8228 1000 16 442 001 16 442 001 0
9000 3770 to 3840 (frames 3832 to 3902) 10 000 1 420 001 1 420 001 0
16000 15288 to 15328 (frames 15350 to 15390) 10 000 820 001 820 001 0

No watchdog, no bcmgenet message, tx_dropped/tx_errors 0 on the Pi, rx_errors 0 on the x86 side.

x86 → Pi (GENET receives)

MTU Short payload Pairs/size Sent Received, CRC OK Lost
9000 8 to 8228 1000 16 442 001 16 440 001 2000
9000 3770 to 3840 10 000 1 420 001 1 420 001 0
16000 15288 to 15328 10 000 820 001 820 001 0

The 2000 lost datagrams match rx_length_errors +2000 on the Pi exactly; rx_crc_errors, rx_frame_errors, rx_over_errors, rx_missed_errors, rx_dropped stay at 0, no kernel message.

3. The lost sizes

Sizes around k × 3584 replayed with 1000 pairs each, then the suspects with their ±1 byte neighbours, at MTU 16000 and 9000. Every time exactly one size is lost, 1000 of 1000, neighbours arrive:

Payload Frame (no FCS) Result
3520 / 3522 3582 / 3584 OK
3521 3583 = 1 × 3584 − 1 all rejected
7104 / 7106 7166 / 7168 OK
7105 7167 = 2 × 3584 − 1 all rejected
10688 / 10690 10750 / 10752 OK
10689 10751 = 3 × 3584 − 1 all rejected
14272 / 14274 14334 / 14336 OK
14273 14335 = 4 × 3584 − 1 all rejected
  • Each rejected batch adds exactly 1000 to rx_length_errors; the sender's tx_packets grows by the number sent, tx_dropped 0.
  • Same result from a second sender NIC, the on-board RTL8168 (r8169, flow control off), for 3583 and 7167.
  • The GENET MIB rx_mtu_err does not move for these frames (it does for frames longer than the MTU); only the driver's rx_length_errors does.
  • Independent of the MTU (9000 and 16000). The same frame lengths pass in the other direction (Pi → x86, first pass above).

Summary: on c0beb41 a received frame of exactly k × 3584 − 1 bytes (FCS excluded, k = 1..4) is dropped with a length error; k × 3584 − 2 and k × 3584 are received. Logs available on request.

@nbuchwitz

Copy link
Copy Markdown
Contributor Author

Thanks for testing. The remaining RX issue is related to the already merged page pool migration (which upstream doesn't hit since max MTU is 1500). I have added another commit which hopefully should fix it.

@herisson-88

Copy link
Copy Markdown

f90d595 on Pi 4: k × 3584 − 1 RX drop fixed, stress clean in both directions

Same setup as my previous report: Pi 4 Model B Rev 1.4, rpi-update pulls/7623 at f90d595 (kernel 6.18.52-v8+ #1 SMP PREEMPT Sat Sep 19 16:36:41 UTC 2026), GENET v5, 1 Gb/s full duplex with flow control rx/tx, direct cable to an x86 host (Fedora 44, Realtek RTL8156B USB, r8152). Same scripts: each datagram carries a sequence number and a CRC32, the receiver checks the CRC and counts gaps, reordering and duplicates. IPv6 link-local: frame = UDP payload + 62 bytes.

1. The previously rejected sizes (x86 → Pi, MTU 16000, 1000 pairs per size)

Payload Frame (no FCS) c0beb41 f90d595
3520 / 3521 / 3522 3582 / 3583 / 3584 OK / all rejected / OK all received
7104 / 7105 / 7106 7166 / 7167 / 7168 OK / all rejected / OK all received
10688 / 10689 / 10690 10750 / 10751 / 10752 OK / all rejected / OK all received
14272 / 14273 / 14274 14334 / 14335 / 14336 OK / all rejected / OK all received

24 004 datagrams sent, 24 004 received with a valid CRC, rx_length_errors 0.

2. Full stress test (wtschueller's pattern, MTU 9000, short payload 8 to 8228, 1000 pairs per size, sender paced at 850 Mb/s)

Direction Sent Received, CRC OK Lost Reordered / duplicated
x86 → Pi (GENET receives) 16 442 001 16 442 001 0 0 / 0
Pi → x86 (GENET transmits) 16 442 001 16 442 001 0 0 / 0

On c0beb41 the x86 → Pi pass lost exactly 2000 datagrams (the two k × 3584 − 1 sizes in range); it now loses none. After both passes on the Pi: rx_length_errors, rx_crc_errors, rx_mtu_err, rx_dropped, tx_errors, tx_dropped, tx_dma_failed, alloc_rx_buff_failed all 0; rx_errors 0 on the x86 side. No bcmgenet message, no watchdog.

3. MTU sweep (10 × 65000-byte UDP datagrams per step, steps of 4)

Direction Range Result
Pi → x86 1400 to 16344 3737 steps, no freeze, no bcmgenet message, error counters unchanged
x86 → Pi (Pi at MTU 16000) 1400 to 16344 3737 steps, no freeze, no bcmgenet message; rx_length_errors +2680, the same count as on c0beb41, where sampling tied them to the steps with the x86 MTU above 16024, i.e. frames longer than the Pi's MTU (rx_mtu_err moves with them)

Summary: f90d595 fixes the k × 3584 − 1 receive drop and I see no regression against c0beb41. Logs available on request.

@herisson-88

Copy link
Copy Markdown

@nbuchwitz thanks for your hard work to fix all !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants