fix(listener): keep *net.UDPConn visible to quic-go for GSO/GRO
The quic and wt (WebTransport) listeners wrap the raw UDP socket with net.PacketConn metrics/stats/admission/limiter wrappers before handing it to quic-go. WrapPacketConn returns a plain net.PacketConn, which hides the underlying *net.UDPConn. quic-go probes the conn for OOBCapablePacketConn (SyscallConn, SetReadBuffer, ReadMsgUDP, WriteMsgUDP); behind the wrapper that probe fails, so quic-go disables its kernel offloads and cannot size its receive buffer. Switch the non-cipher path of both listeners to WrapUDPConn variants, which forward SyscallConn/SetReadBuffer/ReadMsgUDP/WriteMsgUDP down to the underlying *net.UDPConn, so quic-go sees an OOBCapablePacketConn and keeps GSO/GRO + buffer sizing. The same metrics/stats/admission/ limiting still apply. The quic listener's cipher path keeps the net.PacketConn wrappers, since CipherPacketConn is not OOB-capable. +48% throughput (1.80 → 2.69 Gbit/s single-stream, loopback iperf3). The quic-go receive-buffer warning is gone.
This commit is contained in:
@@ -74,10 +74,12 @@ func (l *wtListener) Init(md md.Metadata) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
pc = metrics.WrapPacketConn(l.options.Service, pc)
|
||||
pc = stats.WrapPacketConn(pc, l.options.Stats)
|
||||
pc = admission.WrapPacketConn(l.options.Admission, pc)
|
||||
pc = limiter_wrapper.WrapPacketConn(
|
||||
pc = metrics.WrapUDPConn(l.options.Service, pc)
|
||||
if l.options.Stats != nil {
|
||||
pc = stats.WrapUDPConn(pc, l.options.Stats)
|
||||
}
|
||||
pc = admission.WrapUDPConn(l.options.Admission, pc)
|
||||
pc = limiter_wrapper.WrapUDPConn(
|
||||
pc,
|
||||
l.options.TrafficLimiter,
|
||||
traffic_limiter.ServiceLimitKey,
|
||||
|
||||
+25
-12
@@ -70,20 +70,33 @@ func (l *quicListener) Init(md md.Metadata) (err error) {
|
||||
}
|
||||
if l.md.cipherKey != nil {
|
||||
conn = quic_util.CipherPacketConn(conn, l.md.cipherKey)
|
||||
conn = metrics.WrapPacketConn(l.options.Service, conn)
|
||||
conn = stats.WrapPacketConn(conn, l.options.Stats)
|
||||
conn = admission.WrapPacketConn(l.options.Admission, conn)
|
||||
conn = limiter_wrapper.WrapPacketConn(
|
||||
conn,
|
||||
l.options.TrafficLimiter,
|
||||
traffic_limiter.ServiceLimitKey,
|
||||
limiter.ScopeOption(limiter.ScopeService),
|
||||
limiter.ServiceOption(l.options.Service),
|
||||
limiter.NetworkOption(conn.LocalAddr().Network()),
|
||||
)
|
||||
} else {
|
||||
conn = metrics.WrapUDPConn(l.options.Service, conn)
|
||||
if l.options.Stats != nil {
|
||||
conn = stats.WrapUDPConn(conn, l.options.Stats)
|
||||
}
|
||||
conn = admission.WrapUDPConn(l.options.Admission, conn)
|
||||
conn = limiter_wrapper.WrapUDPConn(
|
||||
conn,
|
||||
l.options.TrafficLimiter,
|
||||
traffic_limiter.ServiceLimitKey,
|
||||
limiter.ScopeOption(limiter.ScopeService),
|
||||
limiter.ServiceOption(l.options.Service),
|
||||
limiter.NetworkOption(conn.LocalAddr().Network()),
|
||||
)
|
||||
}
|
||||
|
||||
conn = metrics.WrapPacketConn(l.options.Service, conn)
|
||||
conn = stats.WrapPacketConn(conn, l.options.Stats)
|
||||
conn = admission.WrapPacketConn(l.options.Admission, conn)
|
||||
conn = limiter_wrapper.WrapPacketConn(
|
||||
conn,
|
||||
l.options.TrafficLimiter,
|
||||
traffic_limiter.ServiceLimitKey,
|
||||
limiter.ScopeOption(limiter.ScopeService),
|
||||
limiter.ServiceOption(l.options.Service),
|
||||
limiter.NetworkOption(conn.LocalAddr().Network()),
|
||||
)
|
||||
|
||||
config := &quic.Config{
|
||||
KeepAlivePeriod: l.md.keepAlivePeriod,
|
||||
HandshakeIdleTimeout: l.md.handshakeTimeout,
|
||||
|
||||
Reference in New Issue
Block a user