Build multiport lanes lazily, on data-plane demand

Lanes were established for every slot as soon as the base tunnel came up,
so a host with `routines: N` paid N-1 extra Noise handshakes, sessions and
keepalives for every peer it talked to, including peers it exchanges a
trickle with. Only routines that actually carry traffic to a peer need a
lane.

Add a per-slot demand flag to laneState. sendInsideMessage already loads
txLanes[laneSlot] on every direct-path packet; that load missing is now
the signal, and EnsureLanes only starts slots the data plane asked for.
The miss path is unchanged otherwise: the packet rides the base tunnel,
the same fallback used while a lane is down.

noteLaneDemand load-guards its store so repeated misses on a lane-less
slot are plain reads rather than a cache line ping-ponging between the
routines sharing it. The demand check is EnsureLanes' last condition, so
a slot that is pending or inside its backoff keeps the flag for the tick
that can act on it. Consuming the flag also stops a lane whose routine
went quiet from being rebuilt forever after it dies.

The wire format, lane negotiation and port-offset scheme are untouched,
so a lazy node interoperates with an eager one in both directions, and
inbound lane handshakes from a busy peer are still accepted regardless of
our own demand.
This commit is contained in:
Wade Simmons
2026-09-02 14:48:07 -04:00
parent 192d994273
commit 11528c6e02
6 changed files with 154 additions and 11 deletions
+6 -3
View File
@@ -196,9 +196,12 @@ listen:
#
# Peers negotiate lanes in the handshake; vanilla peers get a single normal
# tunnel. All control traffic (handshakes, lighthouse, punching, relays) and
# the data fallback stay on the base tunnel/port. Lanes are established after
# the base tunnel comes up, are kept alive with their own keepalives, and
# traffic falls back to the base tunnel while a lane is down.
# the data fallback stay on the base tunnel/port. Lanes are built lazily: a
# lane is only established once a routine actually has traffic for that peer
# and no lane to carry it, so a peer you exchange a trickle with costs one
# tunnel regardless of how many routines are configured. Established lanes
# are kept alive with their own keepalives, and traffic falls back to the base
# tunnel while a lane is down or not yet up.
#
# Requirements: routines > 1, Linux, and the port range
# [listen.port, listen.port+routines-1] reachable through firewalls on both