mirror of
https://github.com/tailscale/tailscale.git
synced 2026-09-13 22:41:38 -04:00
This commit bumps the wireguard-go dependency to incorporate changes to the packet memory model and the tun.Device.Read and conn.ReceiveFunc I/O interfaces. It updates their implementations accordingly. These changes improve throughput in all measured benchmarks and reduce peak RSS in six of eight cases. The two regressions will be addressed in a follow-up commit that reduces peak RSS below the baseline measured at1e69418. That work is kept separate to simplify review. The following throughput and peak RSS benchmarks were performed with iperf3 between two Intel i5-12400 nodes running Ubuntu 24.04 (Linux 6.8). The UDP benchmarks did not use UDP GSO on the sender, so they were roughly equivalent to single packet I/O through wireguard-go. TCP/1 signifies one TCP stream; TCP/128 signifies 128 parallel TCP streams. Throughput (Mb/s) Test1e69418After Change TCP/1 10,371 11,354 +9.5% TCP/128 7,886 8,404 +6.6% UDP/1 2,111 2,853 +35.1% UDP/128 1,747 2,235 +28.0% Peak memory (VmHWM, kB) Test Side1e69418After Change TCP/1 TX 98,240 52,596 -46.5% RX 287,748 73,384 -74.5% TCP/128 TX 101,196 52,812 -47.8% RX 290,420 63,620 -78.1% UDP/1 TX 58,864 160,840 +173.2% RX 137,516 49,900 -63.7% UDP/128 TX 66,148 116,096 +75.5% RX 154,384 56,556 -63.4% Updates tailscale/corp#46716 Updates tailscale/corp#22467 Updates tailscale/corp#36989 Updates tailscale/corp#37878 Signed-off-by: Jordan Whited <jordan@tailscale.com>
85 lines
3.3 KiB
Go
85 lines
3.3 KiB
Go
// Copyright (c) Tailscale Inc & contributors
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
// Package batching implements a socket optimized for increased throughput.
|
|
package batching
|
|
|
|
import (
|
|
"net/netip"
|
|
|
|
"golang.org/x/net/ipv4"
|
|
"golang.org/x/net/ipv6"
|
|
"tailscale.com/net/packet"
|
|
"tailscale.com/types/nettype"
|
|
)
|
|
|
|
var (
|
|
// This acts as a compile-time check for our usage of ipv6.Message in
|
|
// [Conn] for both IPv6 and IPv4 operations.
|
|
_ ipv6.Message = ipv4.Message{}
|
|
)
|
|
|
|
// ReceivedPacket describes a packet read by [Conn.ReadBatch].
|
|
type ReceivedPacket struct {
|
|
// Offset is the starting byte offset into the slab supplied to [Conn.ReadBatch].
|
|
Offset int
|
|
// Size is the size of the packet.
|
|
Size int
|
|
// Source is the source address that sent the packet.
|
|
Source netip.AddrPort
|
|
}
|
|
|
|
const (
|
|
// ReadSlabMultiple is the minimum slab length accepted by [Conn.ReadBatch],
|
|
// and is the suggested multiple when passing a larger value.
|
|
ReadSlabMultiple = 1<<16 - 1
|
|
// MinimumReadBatchSize is the minimum number of packets descriptors accepted
|
|
// by [Conn.ReadBatch].
|
|
MinimumReadBatchSize = udpGROCountMax
|
|
// MaximumWriteBatchSize is the maximum number of buffs accepted by
|
|
// [Conn.WriteBatchTo].
|
|
MaximumWriteBatchSize = 128
|
|
// udpGROCountMax is the maximum number of datagrams the kernel will coalesce
|
|
// together to present in a single recvmmsg() slot.
|
|
udpGROCountMax = 64
|
|
)
|
|
|
|
// Conn is a [nettype.PacketConn] that provides batched i/o using
|
|
// platform-specific optimizations, e.g. {recv,send}mmsg & UDP GSO/GRO.
|
|
//
|
|
// Conn does not support single packet reads (see ReadFromUDPAddrPort docs). It
|
|
// is the caller's responsibility to use the appropriate read API where a
|
|
// [nettype.PacketConn] has been upgraded to support batched i/o.
|
|
//
|
|
// Conn originated from (and is still used by) magicsock where its API was
|
|
// strongly influenced by [wireguard-go/conn.Bind] constraints, namely
|
|
// wireguard-go's ownership of packet memory.
|
|
type Conn interface {
|
|
nettype.PacketConn
|
|
// ReadFromUDPAddrPort always returns an error, as UDP GRO is incompatible
|
|
// with single packet reads. A single datagram may be multiple, coalesced
|
|
// datagrams, and this API lacks the ability to pass that context.
|
|
//
|
|
// TODO: consider detaching Conn from [nettype.PacketConn]
|
|
ReadFromUDPAddrPort([]byte) (int, netip.AddrPort, error)
|
|
// ReadBatch reads datagrams from [Conn] into slab, and describes them in
|
|
// packets. It returns the number of populated packet descriptors. A single
|
|
// GRO-coalesced datagram may produce multiple descriptors.
|
|
//
|
|
// packets must have a length >= [MinimumReadBatchSize]. slab must have a
|
|
// length >= [ReadSlabMultiple]. ReadBatch reads only as many datagrams as
|
|
// both arguments can accommodate.
|
|
ReadBatch(slab []byte, packets []ReceivedPacket) (n int, err error)
|
|
// WriteBatchTo writes buffs to addr.
|
|
//
|
|
// If geneve.VNI.IsSet(), then geneve is encoded into the space preceding
|
|
// offset, and offset must equal [packet.GeneveFixedHeaderLength]. If
|
|
// !geneve.VNI.IsSet() then the space preceding offset is ignored.
|
|
//
|
|
// len(buffs) must be <= [MaximumWriteBatchSize].
|
|
//
|
|
// WriteBatchTo may return a [neterror.ErrUDPGSODisabled] error if UDP GSO
|
|
// was disabled as a result of a send error.
|
|
WriteBatchTo(buffs [][]byte, addr netip.AddrPort, geneve packet.GeneveHeader, offset int) error
|
|
}
|