From 1c9d8bcceb23c56dd045ef006c74031212e5fea2 Mon Sep 17 00:00:00 2001 From: JackDoan Date: Mon, 13 Jul 2026 18:56:07 -0500 Subject: [PATCH] more ram -> more speed --- cmd/nebula-service/main.go | 5 +++-- interface.go | 8 ++++++++ overlay/batch/tx_batch.go | 5 +++++ overlay/tio/tio_gso_linux.go | 9 ++++++--- 4 files changed, 22 insertions(+), 5 deletions(-) diff --git a/cmd/nebula-service/main.go b/cmd/nebula-service/main.go index e0b335f5..a1cec03a 100644 --- a/cmd/nebula-service/main.go +++ b/cmd/nebula-service/main.go @@ -98,14 +98,15 @@ func main() { } if !*configTest { - if err := ctrl.Start(); err != nil { + wait, err := ctrl.Start() + if err != nil { util.LogWithContextIfNeeded("Error while running", err, l) os.Exit(1) } go ctrl.ShutdownBlock() - if err := ctrl.Wait(); err != nil { + if err := wait(); err != nil { l.Error("Nebula stopped due to fatal error", "error", err) os.Exit(2) } diff --git a/interface.go b/interface.go index 6625ea3f..256849b3 100644 --- a/interface.go +++ b/interface.go @@ -435,6 +435,14 @@ func (f *Interface) listenIn(reader tio.Queue, i int) { for _, pkt := range pkts { f.consumeInsidePacket(pkt, fwPacket, nb, sb, rejectBuf, i, conntrackCache.Get()) + // Flush incrementally once a full sendmmsg batch has + // accumulated so the first packets of a deep read drain + // hit the wire while the rest are still being encrypted. + if sb.Len() >= batch.SendBatchCap { + if err := sb.Flush(); err != nil { + f.l.Error("Failed to write outgoing batch", "error", err, "writer", i) + } + } } if err := sb.Flush(); err != nil { f.l.Error("Failed to write outgoing batch", "error", err, "writer", i) diff --git a/overlay/batch/tx_batch.go b/overlay/batch/tx_batch.go index 38f86b25..64fd5b60 100644 --- a/overlay/batch/tx_batch.go +++ b/overlay/batch/tx_batch.go @@ -45,6 +45,11 @@ func (b *SendBatch) Reserve(sz int) []byte { return b.backing[start : start+sz : start+sz] } +// Len reports how many packets are queued for the next Flush. Callers use +// it to flush incrementally once a full sendmmsg batch has accumulated, +// bounding how long the first packet of a large read batch waits. +func (b *SendBatch) Len() int { return len(b.bufs) } + func (b *SendBatch) Commit(pkt []byte, dst netip.AddrPort, outerECN byte) { b.bufs = append(b.bufs, pkt) b.dsts = append(b.dsts, dst) diff --git a/overlay/tio/tio_gso_linux.go b/overlay/tio/tio_gso_linux.go index 460db0ad..37a2f9f2 100644 --- a/overlay/tio/tio_gso_linux.go +++ b/overlay/tio/tio_gso_linux.go @@ -28,9 +28,12 @@ const tunRxBufSize = 64 * 1024 // tunRxBufCap is the total size we allocate for the per-reader rx // buffer. With reads landing directly in rxBuf, each drain iteration // consumes up to tunRxBufSize of headroom for the kernel-supplied bytes. -// Sized to two such iterations so the initial blocking read plus one -// drain read both fit without partial-drop. -const tunRxBufCap = tunRxBufSize * 2 +// Sized to eight such iterations so a single poll wake can drain several +// TSO/USO superpackets under bulk load, amortizing the wake and giving +// the sendmmsg planner longer same-destination runs. Hold latency stays +// bounded because listenIn flushes its send batch incrementally rather +// than only at end-of-drain. +const tunRxBufCap = tunRxBufSize * 8 // tunDrainCap caps how many packets a single Read will accumulate via // the post-wake drain loop. Sized to soak up a burst of small ACKs while