From 8086ad53294fa424a7d2bc8c84bf6ee800342ede Mon Sep 17 00:00:00 2001 From: Alex Valiushko Date: Tue, 24 Mar 2026 12:14:48 -0700 Subject: [PATCH] address feedback --- net/batching/conn_linux.go | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/net/batching/conn_linux.go b/net/batching/conn_linux.go index dc091d2f8..c990b1d5f 100644 --- a/net/batching/conn_linux.go +++ b/net/batching/conn_linux.go @@ -88,6 +88,9 @@ const ( // GOOS-specific values later. It originates as UDP_MAX_SEGMENTS in the // kernel's TX path, and UDP_GRO_CNT_MAX for RX. udpSegmentMaxDatagrams = 64 + // Max amount of iovec structures supported by the system. + // Linked to Linux IOV_MAX, and hardcoded in most places to 1024. + maxIOVFragments = 1024 ) const ( @@ -101,8 +104,9 @@ const ( // // It aggregates message components as a list of buffers without copying, // and expects to be used only on Linux with scatter-gather writes via sendmmsg(2). -// Single message buffers still represent one datagram, and must present -// correct headers at the top. +// +// All msgs[i].Buffers len must be one. Will panic if there is not enough msgs +// to coalesce all buffs. // // All msgs have their Addr field set to addr. // @@ -133,7 +137,13 @@ func (c *linuxBatchingConn) coalesceMessages(addr *net.UDPAddr, geneve packet.Ge if msgLen+coalescedLen <= maxPayloadLen && msgLen <= gsoSize && dgramCnt < udpSegmentMaxDatagrams && + // (2026-04) udpSegmentMaxDatagrams will always trigger first. + // When we start shipping datagrams in more than one fragment, + // replace dgramCnt below with an independent fragmentCnt. + dgramCnt < maxIOVFragments && !endBatch { + // msgs[base].Buffers[0] is set to buff[i] when a new base is set. + // This append attaches subequent buffs to the iovec. msgs[base].Buffers = append(msgs[base].Buffers, buff) if i == len(buffs)-1 { setGSOSizeInControl(&msgs[base].OOB, uint16(gsoSize)) @@ -177,6 +187,9 @@ func (c *linuxBatchingConn) getSendBatch() *sendBatch { func (c *linuxBatchingConn) putSendBatch(batch *sendBatch) { for i := range batch.msgs { + // Non coalesced write paths access only batch.msgs[i].Buffers[0], + // but we append more during [linuxBatchingConn.coalesceMessages]. + // Leave index zero accessible: batch.msgs[i] = ipv6.Message{Buffers: batch.msgs[i].Buffers[:1], OOB: batch.msgs[i].OOB} } c.sendBatchPool.Put(batch)