Skip to content

fix(iroh): stop caching the IP sockets' GSO limit in TransportsSender - #4645

Open
ThierryCantin-Demers wants to merge 3 commits into
n0-computer:mainfrom
ThierryCantin-Demers:fix/gso-live-limit
Open

ThierryCantin-Demers wants to merge 3 commits into
n0-computer:mainfrom
ThierryCantin-Demers:fix/gso-live-limit

Conversation

@ThierryCantin-Demers

@ThierryCantin-Demers ThierryCantin-Demers commented Oct 27, 2026 •

Copy link
Copy Markdown

Description

Fixes #4555. After the kernel refuses a GSO send, noq-udp lowers the socket's max_gso_segments to 1, but TransportsSender kept the limit it read when it was created, so open connections kept sending batches that failed until they timed out.

TransportsSender now reads the IP sockets' limit on every max_transmit_segments() call. Only the custom transports' limit stays stored: CustomSender cannot report one, and changing that trait is outside this fix.

The code was written by Claude and reviewed by me.

API Changes

None.

Notes & open questions

  • Cost: noq calls max_transmit_segments() once per drive_transmit, and each call adds one read of each IP socket's RwLock, which poll_send already takes once per transmit. iroh-bench shows no measurable difference.
  • Affected machine (MT7922 wifi, kernel 6.8): a Burn training run over one iroh connection, which did not finish within 480 s on main with GSO on, now runs as fast as with GSO off.
  • Test: more segments than UDP_MAX_SEGMENTS fail with EINVAL, which noq-udp handles like the EIO from the issue, so the test needs only loopback. Against main's stored limit, its assertion sees 64 instead of 1.
Benchmarks

iroh-bench bulk over loopback, 2 GiB per run, main and this PR alternating, 9 runs each, MiB/s as median (slowest to fastest run). The ranges overlap in every row:

Machine Clients main This PR
Ryzen 7 5700X 1 826 (814 to 841) 820 (804 to 830)
Ryzen 7 5700X 4 764 (749 to 789) 752 (744 to 796)
Xeon E5-2697A v4 1 518 (499 to 543) 518 (481 to 536)
Xeon E5-2697A v4 4 486 (466 to 506) 484 (476 to 494)

Burn's remote backend: a client on another machine trains MNIST for one epoch on the affected machine's GPU, over one iroh connection with relays disabled, so every tensor goes over that direct UDP path:

iroh GSO Run 1 Run 2 Run 3
main off 161.0 s 157.5 s 166.9 s
This PR on 157.6 s 157.3 s 160.1 s
main on not finished after 480 s

Change checklist

  • Self-review.
  • Documentation updates following the style guide, if relevant.
  • Tests if relevant.
  • All API changes documented.

noq-udp lowers a socket's max_gso_segments to 1 after the kernel refuses
a segmented send, but TransportsSender kept the limit it read when the
connection was created, so open connections kept sending batches that
failed and timed out.

Refs n0-computer#4555
On Linux a loopback send with more segments than UDP_MAX_SEGMENTS fails
with EINVAL, which noq-udp handles like the EIO of a NIC without checksum
offload, so the stale-limit path from n0-computer#4555 runs without special hardware.
Fails on main with 64 against 1.
@n0bot n0bot Bot added this to iroh Oct 27, 2026
@github-project-automation github-project-automation Bot moved this to 🚑 Needs Triage in iroh Oct 27, 2026
@ThierryCantin-Demers ThierryCantin-Demers changed the title fix(iroh): read the IP sockets' GSO limit on every transmit fix(iroh): stop caching the IP sockets' GSO limit in TransportsSender Oct 27, 2026
@ThierryCantin-Demers
ThierryCantin-Demers marked this pull request as ready for review October 27, 2026 19:26

This branch has not been deployed

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

Labels

None yet

Projects

Status: 🚑 Needs Triage

Development

Successfully merging this pull request may close these issues.

Existing connections keep sending GSO batches after noq-udp disables GSO

1 participant