write in batches

This commit is contained in:
Jay Wren
2025-11-11 15:06:45 -05:00
parent 0f9b33aa36
commit b2bc6a09ca
6 changed files with 286 additions and 10 deletions

View File

@@ -18,6 +18,7 @@ type Conn interface {
LocalAddr() (netip.AddrPort, error)
ListenOut(r EncReader)
WriteTo(b []byte, addr netip.AddrPort) error
WriteMulti(packets [][]byte, addrs []netip.AddrPort) (int, error)
ReloadConfig(c *config.C)
Close() error
}
@@ -36,6 +37,9 @@ func (NoopConn) ListenOut(_ EncReader) {
func (NoopConn) WriteTo(_ []byte, _ netip.AddrPort) error {
return nil
}
func (NoopConn) WriteMulti(_ [][]byte, _ []netip.AddrPort) (int, error) {
return 0, nil
}
func (NoopConn) ReloadConfig(_ *config.C) {
return
}

View File

@@ -140,6 +140,17 @@ func (u *StdConn) WriteTo(b []byte, ap netip.AddrPort) error {
}
}
// WriteMulti sends multiple packets - fallback implementation without sendmmsg
func (u *StdConn) WriteMulti(packets [][]byte, addrs []netip.AddrPort) (int, error) {
for i := range packets {
err := u.WriteTo(packets[i], addrs[i])
if err != nil {
return i, err
}
}
return len(packets), nil
}
func (u *StdConn) LocalAddr() (netip.AddrPort, error) {
a := u.UDPConn.LocalAddr()

View File

@@ -194,6 +194,19 @@ func (u *StdConn) WriteTo(b []byte, ip netip.AddrPort) error {
return u.writeTo6(b, ip)
}
func (u *StdConn) WriteMulti(packets [][]byte, addrs []netip.AddrPort) (int, error) {
if len(packets) != len(addrs) {
return 0, fmt.Errorf("packets and addrs length mismatch")
}
if len(packets) == 0 {
return 0, nil
}
if u.isV4 {
return u.writeMulti4(packets, addrs)
}
return u.writeMulti6(packets, addrs)
}
func (u *StdConn) writeTo6(b []byte, ip netip.AddrPort) error {
var rsa unix.RawSockaddrInet6
rsa.Family = unix.AF_INET6
@@ -248,6 +261,78 @@ func (u *StdConn) writeTo4(b []byte, ip netip.AddrPort) error {
}
}
func (u *StdConn) writeMulti4(packets [][]byte, addrs []netip.AddrPort) (int, error) {
msgs, iovecs, names := u.PrepareWriteMessages4(len(packets))
for i := range packets {
if !addrs[i].Addr().Is4() {
return i, ErrInvalidIPv6RemoteForSocket
}
// Setup the packet buffer
iovecs[i].Base = &packets[i][0]
iovecs[i].Len = uint64(len(packets[i]))
// Setup the destination address
rsa := (*unix.RawSockaddrInet4)(unsafe.Pointer(&names[i][0]))
rsa.Family = unix.AF_INET
rsa.Addr = addrs[i].Addr().As4()
binary.BigEndian.PutUint16((*[2]byte)(unsafe.Pointer(&rsa.Port))[:], addrs[i].Port())
}
for {
n, _, err := unix.Syscall6(
unix.SYS_SENDMMSG,
uintptr(u.sysFd),
uintptr(unsafe.Pointer(&msgs[0])),
uintptr(len(msgs)),
0,
0,
0,
)
if err != 0 {
return int(n), &net.OpError{Op: "sendmmsg", Err: err}
}
return int(n), nil
}
}
func (u *StdConn) writeMulti6(packets [][]byte, addrs []netip.AddrPort) (int, error) {
msgs, iovecs, names := u.PrepareWriteMessages6(len(packets))
for i := range packets {
// Setup the packet buffer
iovecs[i].Base = &packets[i][0]
iovecs[i].Len = uint64(len(packets[i]))
// Setup the destination address
rsa := (*unix.RawSockaddrInet6)(unsafe.Pointer(&names[i][0]))
rsa.Family = unix.AF_INET6
rsa.Addr = addrs[i].Addr().As16()
binary.BigEndian.PutUint16((*[2]byte)(unsafe.Pointer(&rsa.Port))[:], addrs[i].Port())
}
for {
n, _, err := unix.Syscall6(
unix.SYS_SENDMMSG,
uintptr(u.sysFd),
uintptr(unsafe.Pointer(&msgs[0])),
uintptr(len(msgs)),
0,
0,
0,
)
if err != 0 {
return int(n), &net.OpError{Op: "sendmmsg", Err: err}
}
return int(n), nil
}
}
func (u *StdConn) ReloadConfig(c *config.C) {
b := c.GetInt("listen.read_buffer", 0)
if b > 0 {

View File

@@ -55,3 +55,41 @@ func (u *StdConn) PrepareRawMessages(n int) ([]rawMessage, [][]byte, [][]byte) {
return msgs, buffers, names
}
func (u *StdConn) PrepareWriteMessages4(n int) ([]rawMessage, []iovec, [][]byte) {
msgs := make([]rawMessage, n)
iovecs := make([]iovec, n)
names := make([][]byte, n)
for i := range msgs {
names[i] = make([]byte, unix.SizeofSockaddrInet4)
// Point to the iovec in the slice
msgs[i].Hdr.Iov = &iovecs[i]
msgs[i].Hdr.Iovlen = 1
msgs[i].Hdr.Name = &names[i][0]
msgs[i].Hdr.Namelen = unix.SizeofSockaddrInet4
}
return msgs, iovecs, names
}
func (u *StdConn) PrepareWriteMessages6(n int) ([]rawMessage, []iovec, [][]byte) {
msgs := make([]rawMessage, n)
iovecs := make([]iovec, n)
names := make([][]byte, n)
for i := range msgs {
names[i] = make([]byte, unix.SizeofSockaddrInet6)
// Point to the iovec in the slice
msgs[i].Hdr.Iov = &iovecs[i]
msgs[i].Hdr.Iovlen = 1
msgs[i].Hdr.Name = &names[i][0]
msgs[i].Hdr.Namelen = unix.SizeofSockaddrInet6
}
return msgs, iovecs, names
}