Commit Graph

853 Commits

Author SHA1 Message Date
JackDoan 3803146bc9 improve WriteGSO again 2026-07-27 16:23:34 -05:00
JackDoan f9eb86df9d udp: smoke-test that GSO actually engages, on real sockets in CI
Nothing previously asserted the offload path works end to end -- a
silent fallback to per-packet sends would pass every test and only show
up as a throughput regression. Send a batch through a real StdConn over
loopback and assert (a) the run left as a single sendmmsg entry (GSO
engaged, via a spy around the real syscall) and (b) the kernel carved
the superpacket back into the exact original datagrams at the receiver.
Fails rather than skips when the probe reports no GSO on a UDP_SEGMENT-
capable kernel, so CI (make test, ubuntu-latest) guards engagement.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 16:23:34 -05:00
JackDoan 66cb98e13a udp: test the partial-sendmmsg rewind
The rewind (resume after the kernel accepts fewer entries than
submitted) was the hairiest untested logic in the write path; a bug
there silently duplicates or loses packets under backpressure. Give
batchWriter an injectable sendFn and drive WriteBatch through scripted
partial-acceptance sequences over a mixed GSO-run/plain batch, decoding
what "reached the wire" straight from the prepared iovecs rather than
the entryEnd bookkeeping under test. Also pins the zero-progress abort
and the EIO runtime GSO-disable replay.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 16:23:34 -05:00
JackDoan 3fe2cb970e udp: extract and test the GRO RX splitting 2026-07-27 16:23:34 -05:00
JackDoan 865dc9725c overlay/checksum: test each arch implementation directly
The correctness sweeps only exercised the public Checksum dispatcher, so
wherever it resolved to the gvisor fallback (non-AVX2 amd64, fallback
architectures) the suite compared gvisor against itself and the AVX2
assembly went untested -- silently green. Per-arch export_test.go files
now enumerate the hand-written implementations and every sweep runs
against the dispatcher plus each of them, skipping with an explicit
message when the running CPU can't execute one.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 16:21:10 -05:00
JackDoan 8cebecc087 overlay: test the checksum-seeding math against an RFC 1071 reference
pseudoSumIPv4/IPv6, foldOnceNoInvert, ipv4HdrChecksum (batch) and
foldComplement (tio/virtio) feed the virtio NEEDS_CSUM contract; a wrong
seed means every coalesced packet is silently dropped by the receiver
with nothing failing on our side. Check them against an independent
reference built from explicit RFC pseudo-header bytes -- deliberately
not the production checksum code -- including the carry/fold edge cases
and an end-to-end seed -> kernel-completion -> receiver-accepts
property.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 16:21:10 -05:00
JackDoan fb0d20f123 docs: note the GRO receive-scratch memory cost of listen.batch
The TX arena (128 x 9033B per routine) and GRO receive scratch
(listen.batch x 64KiB per socket) stay at their worst-case bounds by
design: the arena never grows past real demand and the GRO slots cannot
be smaller without truncating coalesced superpackets. Document the
listen.batch knob's memory implication so constrained hosts know what
to tune.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 16:21:10 -05:00
JackDoan 921ed4360a overlay/tio: validate WriteGSO geometry instead of silently dropping
The length checks were fishy on four counts: an empty hdr/transportHdr
with real payload returned nil (silent drop with a success signal); the
HdrLen/GSOSize/CsumStart uint16 conversions could wrap unchecked;
nothing verified transportHdr covers csum_start+csum_offset, so the
kernel's NEEDS_CSUM write could land in payload bytes; and there was no
total-size bound even though every length field involved is 16-bit.

Malformed geometry is now a real error, and a single 65535 total-length
guard makes all the u16 conversions exact.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 16:21:10 -05:00
JackDoan 6fb10c1c1a udp: make parseRecvCmsg's length check overflow-safe 2026-07-27 16:21:10 -05:00
JackDoan 8006b58758 util: unlock the OS thread when CPU pinning fails
PinThreadToCPU left the goroutine locked to its OS thread even when
sched_setaffinity failed. The lock only exists to make the affinity
stick; without it the kernel migrates the thread anyway, so a failed pin
kept a dedicated thread for zero benefit. Unwind on the error path.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 16:20:10 -05:00
JackDoan 56d2a3841d tun: default pin_threads off
Thread pinning trades scheduler freedom for TX-ring ordering; that's the
right trade on dedicated forwarders but not as a surprise default on
hosts sharing cores with other workloads. Make it opt-in and document
the default in the example config.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 16:20:10 -05:00
JackDoan 6c0305c7ae overlay/tio: derive WriteGSO geometry from non-empty fragments
GSOSize came from len(pays[0]) while the iovec build skips empty
fragments, so a leading empty fragment emitted a TSO/USO header with
gso_size == 0 -- virtio_net_hdr_to_skb rejects that with EINVAL and the
whole superpacket is lost. Compute gso_size from the first non-empty
fragment and use the non-empty count to decide superpacket vs plain.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 16:20:10 -05:00
JackDoan 5c0a5ee4be overlay/tio: guard Offload.Write against zero-length buffers
Write took &buf[0] before calling writeWithScratch, so the len==0 guard
in the helper could never run -- a zero-length buffer panicked on the
index instead of returning. Hoist the guard above the indexing and fold
writeWithScratch into Write since it was the only caller and duplicated
the iovec setup.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 16:20:10 -05:00
JackDoan 35596c7708 another VIRTIO_NET_HDR_GSO_ECN mistake 2026-07-27 16:20:10 -05:00
JackDoan d74c5ac5c5 overlay/batch: don't false-set PSH when merging a short-tail-sealed slot, clarify PSH vs sealing 2026-07-27 16:20:10 -05:00
JackDoan c06bfb46be overlay/batch: remove end-of-batch debug scaffolding
The Warn("==== end of batch ====") delimiter (and the `logged` flag that
fed it) was left over from debugging the cross-slot gap logging.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 16:14:33 -05:00
JackDoan be35d4f059 udp: downgrade the per-batch sendmmsg failure log to debug 2026-07-27 16:14:33 -05:00
JackDoan eea3c81626 udp: disable GSO at runtime when the kernel rejects a GSO send 2026-07-27 16:12:21 -05:00
Nate Brown 88872a8433 Don't fail on the batch at the first error (#1826) 2026-07-27 14:43:30 -05:00
Nate Brown 6bf424f749 Transmit a computed-zero UDP checksum as all ones (#1823)
yum yum
2026-07-24 19:25:49 -05:00
JackDoan 9688d32f5b make it nicer 2026-07-24 16:44:51 -05:00
JackDoan 8c91fa2699 fix it! 2026-07-24 16:44:51 -05:00
JackDoan 92d51c042e fix it! 2026-07-24 16:44:51 -05:00
JackDoan 0a0b2404a2 put locks around the replay window 2026-07-24 16:44:51 -05:00
JackDoan ef0e3015f9 decrypt in place 2026-07-24 16:44:05 -05:00
JackDoan c6ebe71c08 silly optimization 2026-07-24 16:39:03 -05:00
JackDoan 1d768ac4e4 tio: accept VIRTIO_NET_HDR_GSO_ECN-qualified superpackets
TUN_F_TSO_ECN is negotiated, so once ECN feedback flows the kernel hands
us TSO superpackets typed TCPV4|GSO_ECN (CWR set). protoFromGSOType
treated the qualifier bit as an unknown type and the read path dropped
every such superpacket - a latent bug that only fires when a congested
hop CE-marks the flow, exactly when drops hurt most. The segmenter
already handles CWR (first segment only); just mask the bit.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 16:39:03 -05:00
JackDoan 69fa9e4a2e unslop some comments 2026-07-24 16:39:03 -05:00
JackDoan ed55cf40d5 batch: back SendBatch with Arena instead of a hand-rolled slab
SendBatch.Reserve duplicated Arena's grow-on-demand logic byte for byte.
Use an Arena for the slot backing so the borrow/grow/recycle semantics
live in one place.
2026-07-24 16:39:03 -05:00
JackDoan a6ae44ddb1 batch: move shared-arena Reset ownership from lanes to their owner 2026-07-24 16:39:03 -05:00
JackDoan 7cc37323a8 re-align to master 2026-07-24 16:39:03 -05:00
JackDoan 68e3fae870 some tests 2026-07-24 16:39:03 -05:00
JackDoan 720990ddcd simplify making new Queues 2026-07-24 16:39:03 -05:00
JackDoan 05443523bd make service test less annoying 2026-07-24 16:39:03 -05:00
JackDoan 99bf613a2c checkpt 2026-07-24 16:39:03 -05:00
JackDoan 9e7c783eb3 checkpt 2026-07-24 16:39:03 -05:00
JackDoan 8b14a6ee56 more ram -> more speed 2026-07-24 16:39:03 -05:00
JackDoan ae17513bbf more fixes! 2026-07-24 16:39:03 -05:00
JackDoan 1aca2f75ae more fixes! 2026-07-24 16:39:03 -05:00
JackDoan c7918d1096 lint 2026-07-24 16:39:03 -05:00
JackDoan 44dd2e9ca4 datapath: fix 12 correctness findings from tun/UDP offload review
Multi-disciplinary correctness review of the batched tun / GSO-GRO / sendmmsg
rework. Each fix has a regression test; the merged tree builds on
linux/darwin/openbsd/windows/freebsd/netbsd, vets clean, passes the unit and
e2e suites, and is -race clean.

Critical:
- C1 zero-length inner UDP datagram no longer panics the process (remote DoS):
  the UDP coalescer routes payLen==0 to passthrough instead of seeding a GSO
  slot, and WriteGSO skips empty payload iovecs as defense in depth.
- C2 segmenter no longer corrupts inner headers when gsoSize < headerLen: the
  L3+L4 header is snapshotted once and each segment stamped from the copy,
  replacing the destructive overlapping in-place slide (SegmentTCP + SegmentUDP).

High:
- H1 applyOuterECN updates the IPv4 header checksum (RFC 1624 incremental) when
  folding outer CE into the inner ToS, so passthrough packets are no longer
  dropped by the peer stack.
- H2 the GRO reject path caps the borrowed RX segment ([:n:n]) so a reject can
  no longer overrun into the next coalesced segment's Nebula header. Note:
  oversized ICMPv6 rejects that need >16B beyond the segment are now refused
  rather than sent under GRO (safe; see TOFIX.md for the scratch-buffer follow-up).
- H3 WriteBatch falls back to per-packet WriteTo for a chunk when writeSockaddr
  fails, so one bad-family destination costs only its own packet, not the batch.
- H4 UserDevice.Readers returns N distinct queue wrappers with private buffers
  (sharing the pipes) so concurrent readers no longer race/overwrite borrowed
  packet bytes.
- H5 Poll.Close / Offload.Close no longer null t.fd (matching master's
  tunFile.Close), removing the data race with a concurrent readOne load.

Medium/Low:
- M1 the UDP GSO 127-segment gate moved from kernel >=5.5 to >=6.9 (the real
  UDP_MAX_SEGMENTS 64->128 threshold), avoiding EINVAL + per-packet fallback on
  5.5-6.8 kernels.
- M2 NewMultiQueueReader replays the offload mask newTun actually negotiated
  instead of the TSO-only mask, so adding a queue no longer disables USO
  device-wide; the advertised USO capability derives from the same mask.
- M3 the shutdown eventfd is closed in pollQueueSet.Close / offloadQueueSet.Close
  (double-close guarded), fixing the per-lifecycle fd leak.
- M4 dual-stack ECN selects the cmsg by address family, not socket family: RX
  parseRecvCmsg reads both IP_TOS and IPV6_TCLASS; TX writeEntryCmsg stamps
  IP_TOS for v4/v4-mapped dests and IPV6_TCLASS for v6 (on-host verified).
- L1 newPoll no longer closes the fd on failure (matching newOffload), removing
  the double-close on QueueSet.Add error.
2026-07-24 16:39:03 -05:00
JackDoan 733dc06192 make mobile happy 2026-07-24 16:39:03 -05:00
JackDoan 7fee3a97b2 correctly shutdown the pprofserver 2026-07-24 16:39:03 -05:00
JackDoan 1e218737dc SendVia: don't emit a zero-length packet when prepareSendVia fails 2026-07-24 16:39:03 -05:00
JackDoan 243c920f88 adapt Control lifecycle tests to the batched tio.Queue Device interface 2026-07-24 16:39:03 -05:00
JackDoan 9bdab873f2 udp setsockopt correctness fixes 2026-07-24 16:39:03 -05:00
JackDoan a081fba023 use less ram pls 2026-07-24 16:39:03 -05:00
JackDoan b50d6276e3 clean up a comment a bit 2026-07-24 16:39:03 -05:00
JackDoan 3b16f1adb6 drop in a logger 2026-07-24 16:39:03 -05:00
JackDoan 410bac9688 go mod tidy 2026-07-24 16:39:03 -05:00