mirror of
https://github.com/refraction-networking/uquic.git
synced 2025-04-03 04:07:35 +03:00
add a function to set the UDP send buffer size
This function is the equivalent to the function used to set the UDP receive buffer size. It's so similar that code generation is used to make a copy of the setReceiveBuffer function.
This commit is contained in:
parent
600502ab06
commit
74be4d2755
10 changed files with 150 additions and 8 deletions
4
.github/workflows/go-generate.sh
vendored
4
.github/workflows/go-generate.sh
vendored
|
@ -11,6 +11,10 @@ cp -r "$DIR" generated
|
|||
cd generated
|
||||
# delete all go-generated files generated (that adhere to the comment convention)
|
||||
grep --include \*.go -lrIZ "^// Code generated .* DO NOT EDIT\.$" . | xargs --null rm
|
||||
|
||||
# First regenerate sys_conn_buffers_write.go.
|
||||
# If it doesn't exist, the following mockgen calls will fail.
|
||||
go generate -run "sys_conn_buffers_write.go"
|
||||
# now generate everything
|
||||
go generate ./...
|
||||
cd ..
|
||||
|
|
|
@ -5,6 +5,9 @@ import "time"
|
|||
// DesiredReceiveBufferSize is the kernel UDP receive buffer size that we'd like to use.
|
||||
const DesiredReceiveBufferSize = (1 << 20) * 2 // 2 MB
|
||||
|
||||
// DesiredSendBufferSize is the kernel UDP send buffer size that we'd like to use.
|
||||
const DesiredSendBufferSize = (1 << 20) * 2 // 2 MB
|
||||
|
||||
// InitialPacketSizeIPv4 is the maximum packet size that we use for sending IPv4 packets.
|
||||
const InitialPacketSizeIPv4 = 1252
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
"github.com/quic-go/quic-go/internal/utils"
|
||||
)
|
||||
|
||||
//go:generate sh -c "echo '// Code generated by go generate. DO NOT EDIT.\n// Source: sys_conn_buffers.go\n' > sys_conn_buffers_write.go && sed -e 's/SetReadBuffer/SetWriteBuffer/g' -e 's/setReceiveBuffer/setSendBuffer/g' -e 's/inspectReadBuffer/inspectWriteBuffer/g' -e 's/protocol\\.DesiredReceiveBufferSize/protocol\\.DesiredSendBufferSize/g' -e 's/forceSetReceiveBuffer/forceSetSendBuffer/g' -e 's/receive buffer/send buffer/g' sys_conn_buffers.go | sed '/^\\/\\/go:generate/d' >> sys_conn_buffers_write.go"
|
||||
func setReceiveBuffer(c net.PacketConn, logger utils.Logger) error {
|
||||
conn, ok := c.(interface{ SetReadBuffer(int) error })
|
||||
if !ok {
|
||||
|
|
70
sys_conn_buffers_write.go
Normal file
70
sys_conn_buffers_write.go
Normal file
|
@ -0,0 +1,70 @@
|
|||
// Code generated by go generate. DO NOT EDIT.
|
||||
// Source: sys_conn_buffers.go
|
||||
|
||||
package quic
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"syscall"
|
||||
|
||||
"github.com/quic-go/quic-go/internal/protocol"
|
||||
"github.com/quic-go/quic-go/internal/utils"
|
||||
)
|
||||
|
||||
func setSendBuffer(c net.PacketConn, logger utils.Logger) error {
|
||||
conn, ok := c.(interface{ SetWriteBuffer(int) error })
|
||||
if !ok {
|
||||
return errors.New("connection doesn't allow setting of send buffer size. Not a *net.UDPConn?")
|
||||
}
|
||||
|
||||
var syscallConn syscall.RawConn
|
||||
if sc, ok := c.(interface {
|
||||
SyscallConn() (syscall.RawConn, error)
|
||||
}); ok {
|
||||
var err error
|
||||
syscallConn, err = sc.SyscallConn()
|
||||
if err != nil {
|
||||
syscallConn = nil
|
||||
}
|
||||
}
|
||||
// The connection has a SetWriteBuffer method, but we couldn't obtain a syscall.RawConn.
|
||||
// This shouldn't happen for a net.UDPConn, but is possible if the connection just implements the
|
||||
// net.PacketConn interface and the SetWriteBuffer method.
|
||||
// We have no way of checking if increasing the buffer size actually worked.
|
||||
if syscallConn == nil {
|
||||
return conn.SetWriteBuffer(protocol.DesiredSendBufferSize)
|
||||
}
|
||||
|
||||
size, err := inspectWriteBuffer(syscallConn)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to determine send buffer size: %w", err)
|
||||
}
|
||||
if size >= protocol.DesiredSendBufferSize {
|
||||
logger.Debugf("Conn has send buffer of %d kiB (wanted: at least %d kiB)", size/1024, protocol.DesiredSendBufferSize/1024)
|
||||
return nil
|
||||
}
|
||||
// Ignore the error. We check if we succeeded by querying the buffer size afterward.
|
||||
_ = conn.SetWriteBuffer(protocol.DesiredSendBufferSize)
|
||||
newSize, err := inspectWriteBuffer(syscallConn)
|
||||
if newSize < protocol.DesiredSendBufferSize {
|
||||
// Try again with RCVBUFFORCE on Linux
|
||||
_ = forceSetSendBuffer(syscallConn, protocol.DesiredSendBufferSize)
|
||||
newSize, err = inspectWriteBuffer(syscallConn)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to determine send buffer size: %w", err)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to determine send buffer size: %w", err)
|
||||
}
|
||||
if newSize == size {
|
||||
return fmt.Errorf("failed to increase send buffer size (wanted: %d kiB, got %d kiB)", protocol.DesiredSendBufferSize/1024, newSize/1024)
|
||||
}
|
||||
if newSize < protocol.DesiredSendBufferSize {
|
||||
return fmt.Errorf("failed to sufficiently increase send buffer size (was: %d kiB, wanted: %d kiB, got: %d kiB)", size/1024, protocol.DesiredSendBufferSize/1024, newSize/1024)
|
||||
}
|
||||
logger.Debugf("Increased send buffer size to %d kiB", newSize/1024)
|
||||
return nil
|
||||
}
|
|
@ -31,3 +31,13 @@ func forceSetReceiveBuffer(c syscall.RawConn, bytes int) error {
|
|||
}
|
||||
return serr
|
||||
}
|
||||
|
||||
func forceSetSendBuffer(c syscall.RawConn, bytes int) error {
|
||||
var serr error
|
||||
if err := c.Control(func(fd uintptr) {
|
||||
serr = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_SNDBUFFORCE, bytes)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
return serr
|
||||
}
|
||||
|
|
|
@ -13,8 +13,8 @@ import (
|
|||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var _ = Describe("Can change the receive buffer size", func() {
|
||||
It("Force a change (if we have CAP_NET_ADMIN)", func() {
|
||||
var _ = Describe("forcing a change of send and receive buffer sizes", func() {
|
||||
It("forces a change of the receive buffer size", func() {
|
||||
if os.Getuid() != 0 {
|
||||
Fail("Must be root to force change the receive buffer size")
|
||||
}
|
||||
|
@ -24,17 +24,47 @@ var _ = Describe("Can change the receive buffer size", func() {
|
|||
defer c.Close()
|
||||
syscallConn, err := c.(*net.UDPConn).SyscallConn()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
forceSetReceiveBuffer(syscallConn, 256<<10)
|
||||
|
||||
const small = 256 << 10 // 256 KB
|
||||
Expect(forceSetReceiveBuffer(syscallConn, small)).To(Succeed())
|
||||
|
||||
size, err := inspectReadBuffer(syscallConn)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
// The kernel doubles this value (to allow space for bookkeeping overhead)
|
||||
Expect(size).To(Equal(512 << 10))
|
||||
Expect(size).To(Equal(2 * small))
|
||||
|
||||
forceSetReceiveBuffer(syscallConn, 512<<10)
|
||||
const large = 32 << 20 // 32 MB
|
||||
Expect(forceSetReceiveBuffer(syscallConn, large)).To(Succeed())
|
||||
size, err = inspectReadBuffer(syscallConn)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
// The kernel doubles this value (to allow space for bookkeeping overhead)
|
||||
Expect(size).To(Equal(1024 << 10))
|
||||
Expect(size).To(Equal(2 * large))
|
||||
})
|
||||
|
||||
It("forces a change of the send buffer size", func() {
|
||||
if os.Getuid() != 0 {
|
||||
Fail("Must be root to force change the send buffer size")
|
||||
}
|
||||
|
||||
c, err := net.ListenPacket("udp", "127.0.0.1:0")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
defer c.Close()
|
||||
syscallConn, err := c.(*net.UDPConn).SyscallConn()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
const small = 256 << 10 // 256 KB
|
||||
Expect(forceSetSendBuffer(syscallConn, small)).To(Succeed())
|
||||
|
||||
size, err := inspectWriteBuffer(syscallConn)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
// The kernel doubles this value (to allow space for bookkeeping overhead)
|
||||
Expect(size).To(Equal(2 * small))
|
||||
|
||||
const large = 32 << 20 // 32 MB
|
||||
Expect(forceSetSendBuffer(syscallConn, large)).To(Succeed())
|
||||
size, err = inspectWriteBuffer(syscallConn)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
// The kernel doubles this value (to allow space for bookkeeping overhead)
|
||||
Expect(size).To(Equal(2 * large))
|
||||
})
|
||||
})
|
||||
|
|
|
@ -2,4 +2,5 @@
|
|||
|
||||
package quic
|
||||
|
||||
func forceSetReceiveBuffer(c interface{}, bytes int) error { return nil }
|
||||
func forceSetReceiveBuffer(c any, bytes int) error { return nil }
|
||||
func forceSetSendBuffer(c any, bytes int) error { return nil }
|
||||
|
|
|
@ -8,6 +8,7 @@ func newConn(c net.PacketConn) (rawConn, error) {
|
|||
return &basicConn{PacketConn: c}, nil
|
||||
}
|
||||
|
||||
func inspectReadBuffer(any) (int, error) { return 0, nil }
|
||||
func inspectReadBuffer(any) (int, error) { return 0, nil }
|
||||
func inspectWriteBuffer(any) (int, error) { return 0, nil }
|
||||
|
||||
func (i *packetInfo) OOB() []byte { return nil }
|
||||
|
|
|
@ -42,6 +42,17 @@ func inspectReadBuffer(c syscall.RawConn) (int, error) {
|
|||
return size, serr
|
||||
}
|
||||
|
||||
func inspectWriteBuffer(c syscall.RawConn) (int, error) {
|
||||
var size int
|
||||
var serr error
|
||||
if err := c.Control(func(fd uintptr) {
|
||||
size, serr = unix.GetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_SNDBUF)
|
||||
}); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return size, serr
|
||||
}
|
||||
|
||||
type oobConn struct {
|
||||
OOBCapablePacketConn
|
||||
batchConn batchConn
|
||||
|
|
|
@ -23,4 +23,15 @@ func inspectReadBuffer(c syscall.RawConn) (int, error) {
|
|||
return size, serr
|
||||
}
|
||||
|
||||
func inspectWriteBuffer(c syscall.RawConn) (int, error) {
|
||||
var size int
|
||||
var serr error
|
||||
if err := c.Control(func(fd uintptr) {
|
||||
size, serr = windows.GetsockoptInt(windows.Handle(fd), windows.SOL_SOCKET, windows.SO_SNDBUF)
|
||||
}); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return size, serr
|
||||
}
|
||||
|
||||
func (i *packetInfo) OOB() []byte { return nil }
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue