batch more writes

This commit is contained in:
Jay Wren
2025-11-19 16:58:59 -05:00
parent 4e333c76ba
commit bc9711df68
15 changed files with 389 additions and 68 deletions

View File

@@ -4,7 +4,6 @@ import (
"context"
"errors"
"fmt"
"io"
"net/netip"
"os"
"runtime"
@@ -52,9 +51,10 @@ type InterfaceConfig struct {
}
type batchMetrics struct {
udpReadSize metrics.Histogram
tunReadSize metrics.Histogram
udpWriteSize metrics.Histogram
udpReadSize metrics.Histogram
tunReadSize metrics.Histogram
udpWriteSize metrics.Histogram
tunWriteSize metrics.Histogram
}
type Interface struct {
@@ -93,7 +93,7 @@ type Interface struct {
conntrackCacheTimeout time.Duration
writers []udp.Conn
readers []io.ReadWriteCloser
readers []overlay.BatchReadWriter
metricHandshakes metrics.Histogram
messageMetrics *MessageMetrics
@@ -185,7 +185,7 @@ func NewInterface(ctx context.Context, c *InterfaceConfig) (*Interface, error) {
routines: c.routines,
version: c.version,
writers: make([]udp.Conn, c.routines),
readers: make([]io.ReadWriteCloser, c.routines),
readers: make([]overlay.BatchReadWriter, c.routines),
myVpnNetworks: cs.myVpnNetworks,
myVpnNetworksTable: cs.myVpnNetworksTable,
myVpnAddrs: cs.myVpnAddrs,
@@ -205,6 +205,7 @@ func NewInterface(ctx context.Context, c *InterfaceConfig) (*Interface, error) {
udpReadSize: metrics.GetOrRegisterHistogram("batch.udp_read_size", nil, metrics.NewUniformSample(1024)),
tunReadSize: metrics.GetOrRegisterHistogram("batch.tun_read_size", nil, metrics.NewUniformSample(1024)),
udpWriteSize: metrics.GetOrRegisterHistogram("batch.udp_write_size", nil, metrics.NewUniformSample(1024)),
tunWriteSize: metrics.GetOrRegisterHistogram("batch.tun_write_size", nil, metrics.NewUniformSample(1024)),
},
l: c.l,
@@ -238,7 +239,7 @@ func (f *Interface) activate() {
metrics.GetOrRegisterGauge("routines", nil).Update(int64(f.routines))
// Prepare n tun queues
var reader io.ReadWriteCloser = f.inside
var reader overlay.BatchReadWriter = f.inside
for i := 0; i < f.routines; i++ {
if i > 0 {
reader, err = f.inside.NewMultiQueueReader()
@@ -297,53 +298,10 @@ func (f *Interface) listenOut(i int) {
})
}
// BatchReader is an interface for devices that support reading multiple packets at once
type BatchReader interface {
BatchRead(bufs [][]byte, sizes []int) (int, error)
BatchSize() int
}
func (f *Interface) listenIn(reader io.ReadWriteCloser, i int) {
func (f *Interface) listenIn(reader overlay.BatchReadWriter, i int) {
runtime.LockOSThread()
// Check if reader supports batching
batchReader, supportsBatching := reader.(BatchReader)
if supportsBatching {
f.listenInBatch(reader, batchReader, i)
} else {
f.listenInSingle(reader, i)
}
}
func (f *Interface) listenInSingle(reader io.ReadWriteCloser, i int) {
packet := make([]byte, mtu)
// Allocate out buffer with virtio header headroom (10 bytes) to avoid copies on write
outBuf := make([]byte, virtioNetHdrLen+mtu)
out := outBuf[virtioNetHdrLen:] // Use slice starting after headroom
fwPacket := &firewall.Packet{}
nb := make([]byte, 12)
conntrackCache := firewall.NewConntrackCacheTicker(f.conntrackCacheTimeout)
for {
n, err := reader.Read(packet)
if err != nil {
if errors.Is(err, os.ErrClosed) && f.closed.Load() {
return
}
f.l.WithError(err).Error("Error while reading outbound packet")
// This only seems to happen when something fatal happens to the fd, so exit.
os.Exit(2)
}
f.consumeInsidePacket(packet[:n], fwPacket, nb, out, i, conntrackCache.Get(f.l))
}
}
func (f *Interface) listenInBatch(reader io.ReadWriteCloser, batchReader BatchReader, i int) {
batchSize := batchReader.BatchSize()
batchSize := reader.BatchSize()
// Allocate buffers for batch reading
bufs := make([][]byte, batchSize)
@@ -370,7 +328,7 @@ func (f *Interface) listenInBatch(reader io.ReadWriteCloser, batchReader BatchRe
conntrackCache := firewall.NewConntrackCacheTicker(f.conntrackCacheTimeout)
for {
n, err := batchReader.BatchRead(bufs, sizes)
n, err := reader.BatchRead(bufs, sizes)
if err != nil {
if errors.Is(err, os.ErrClosed) && f.closed.Load() {
return