Internal change.

PiperOrigin-RevId: 381375705
This commit is contained in:
Jamie Liu
2021-06-24 18:03:15 -07:00
committed by gVisor bot
parent 4470caec4e
commit ccd2d607e5
13 changed files with 223 additions and 297 deletions
-18
View File
@@ -1,18 +0,0 @@
load("//tools:defs.bzl", "go_library", "go_test")
package(licenses = ["notice"])
go_library(
name = "iovec",
srcs = ["iovec.go"],
visibility = ["//:sandbox"],
deps = ["@org_golang_x_sys//unix:go_default_library"],
)
go_test(
name = "iovec_test",
size = "small",
srcs = ["iovec_test.go"],
library = ":iovec",
deps = ["@org_golang_x_sys//unix:go_default_library"],
)
-71
View File
@@ -1,71 +0,0 @@
// Copyright 2020 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// +build linux
// Package iovec provides helpers to interact with vectorized I/O on host
// system.
package iovec
import (
"golang.org/x/sys/unix"
)
// MaxIovs is the maximum number of iovecs host platform can accept.
var MaxIovs = 1024
// Builder is a builder for slice of unix.Iovec.
type Builder struct {
iovec []unix.Iovec
storage [8]unix.Iovec
// overflow tracks the last buffer when iovec length is at MaxIovs.
overflow []byte
}
// Add adds buf to b preparing to be written. Zero-length buf won't be added.
func (b *Builder) Add(buf []byte) {
if len(buf) == 0 {
return
}
if b.iovec == nil {
b.iovec = b.storage[:0]
}
if len(b.iovec) >= MaxIovs {
b.addByAppend(buf)
return
}
b.iovec = append(b.iovec, unix.Iovec{Base: &buf[0]})
b.iovec[len(b.iovec)-1].SetLen(len(buf))
// Keep the last buf if iovec is at max capacity. We will need to append to it
// for later bufs.
if len(b.iovec) == MaxIovs {
n := len(buf)
b.overflow = buf[:n:n]
}
}
func (b *Builder) addByAppend(buf []byte) {
b.overflow = append(b.overflow, buf...)
b.iovec[len(b.iovec)-1] = unix.Iovec{Base: &b.overflow[0]}
b.iovec[len(b.iovec)-1].SetLen(len(b.overflow))
}
// Build returns the final Iovec slice. The length of returned iovec will not
// excceed MaxIovs.
func (b *Builder) Build() []unix.Iovec {
return b.iovec
}
-120
View File
@@ -1,120 +0,0 @@
// Copyright 2020 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// +build linux
package iovec
import (
"bytes"
"fmt"
"testing"
"unsafe"
"golang.org/x/sys/unix"
)
func TestBuilderEmpty(t *testing.T) {
var builder Builder
iovecs := builder.Build()
if got, want := len(iovecs), 0; got != want {
t.Errorf("len(iovecs) = %d, want %d", got, want)
}
}
func TestBuilderBuild(t *testing.T) {
a := []byte{1, 2}
b := []byte{3, 4, 5}
var builder Builder
builder.Add(a)
builder.Add(b)
builder.Add(nil) // Nil slice won't be added.
builder.Add([]byte{}) // Empty slice won't be added.
iovecs := builder.Build()
if got, want := len(iovecs), 2; got != want {
t.Fatalf("len(iovecs) = %d, want %d", got, want)
}
for i, data := range [][]byte{a, b} {
if got, want := *iovecs[i].Base, data[0]; got != want {
t.Fatalf("*iovecs[%d].Base = %d, want %d", i, got, want)
}
if got, want := iovecs[i].Len, uint64(len(data)); got != want {
t.Fatalf("iovecs[%d].Len = %d, want %d", i, got, want)
}
}
}
func TestBuilderBuildMaxIov(t *testing.T) {
for _, test := range []struct {
numIov int
}{
{
numIov: MaxIovs - 1,
},
{
numIov: MaxIovs,
},
{
numIov: MaxIovs + 1,
},
{
numIov: MaxIovs + 10,
},
} {
name := fmt.Sprintf("numIov=%v", test.numIov)
t.Run(name, func(t *testing.T) {
var data []byte
var builder Builder
for i := 0; i < test.numIov; i++ {
buf := []byte{byte(i)}
builder.Add(buf)
data = append(data, buf...)
}
iovec := builder.Build()
// Check the expected length of iovec.
wantNum := test.numIov
if wantNum > MaxIovs {
wantNum = MaxIovs
}
if got, want := len(iovec), wantNum; got != want {
t.Errorf("len(iovec) = %d, want %d", got, want)
}
// Test a real read-write.
var fds [2]int
if err := unix.Pipe(fds[:]); err != nil {
t.Fatalf("Pipe: %v", err)
}
defer unix.Close(fds[0])
defer unix.Close(fds[1])
wrote, _, e := unix.RawSyscall(unix.SYS_WRITEV, uintptr(fds[1]), uintptr(unsafe.Pointer(&iovec[0])), uintptr(len(iovec)))
if int(wrote) != len(data) || e != 0 {
t.Fatalf("writev: %v, %v; want %v, 0", wrote, e, len(data))
}
got := make([]byte, len(data))
if n, err := unix.Read(fds[0], got); n != len(got) || err != nil {
t.Fatalf("read: %v, %v; want %v, nil", n, err, len(got))
}
if !bytes.Equal(got, data) {
t.Errorf("read: got data %v, want %v", got, data)
}
})
}
}
+1 -1
View File
@@ -31,7 +31,6 @@ go_library(
"//pkg/errors/linuxerr",
"//pkg/fd",
"//pkg/fdnotifier",
"//pkg/iovec",
"//pkg/log",
"//pkg/marshal/primitive",
"//pkg/refs",
@@ -41,6 +40,7 @@ go_library(
"//pkg/sentry/device",
"//pkg/sentry/fs",
"//pkg/sentry/fs/fsutil",
"//pkg/sentry/hostfd",
"//pkg/sentry/kernel",
"//pkg/sentry/kernel/auth",
"//pkg/sentry/kernel/time",
+2 -2
View File
@@ -16,7 +16,7 @@ package host
import (
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/iovec"
"gvisor.dev/gvisor/pkg/sentry/hostfd"
"gvisor.dev/gvisor/pkg/syserror"
)
@@ -72,7 +72,7 @@ func buildIovec(bufs [][]byte, maxlen int64, truncate bool) (length int64, iovec
}
}
if iovsRequired > iovec.MaxIovs {
if iovsRequired > hostfd.MaxSendRecvMsgIov {
// The kernel will reject our call if we pass this many iovs.
// Use a single intermediate buffer instead.
b := make([]byte, stopLen)
-1
View File
@@ -49,7 +49,6 @@ go_library(
"//pkg/fdnotifier",
"//pkg/fspath",
"//pkg/hostarch",
"//pkg/iovec",
"//pkg/log",
"//pkg/marshal/primitive",
"//pkg/refs",
+2 -2
View File
@@ -16,7 +16,7 @@ package host
import (
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/iovec"
"gvisor.dev/gvisor/pkg/sentry/hostfd"
"gvisor.dev/gvisor/pkg/syserror"
)
@@ -70,7 +70,7 @@ func buildIovec(bufs [][]byte, maxlen int64, truncate bool) (length int64, iovec
}
}
if iovsRequired > iovec.MaxIovs {
if iovsRequired > hostfd.MaxSendRecvMsgIov {
// The kernel will reject our call if we pass this many iovs.
// Use a single intermediate buffer instead.
b := make([]byte, stopLen)
+7 -2
View File
@@ -14,5 +14,10 @@
package hostfd
// maxIov is the maximum permitted size of a struct iovec array.
const maxIov = 1024 // UIO_MAXIOV
// MaxReadWriteIov is the maximum permitted size of a struct iovec array in a
// readv, writev, preadv, or pwritev host syscall.
const MaxReadWriteIov = 1024 // UIO_MAXIOV
// MaxSendRecvMsgIov is the maximum permitted size of a struct iovec array in a
// sendmsg or recvmsg host syscall.
const MaxSendRecvMsgIov = 1024 // UIO_MAXIOV
+11 -6
View File
@@ -23,6 +23,11 @@ import (
"gvisor.dev/gvisor/pkg/safemem"
)
const (
sizeofIovec = unsafe.Sizeof(unix.Iovec{})
sizeofMsghdr = unsafe.Sizeof(unix.Msghdr{})
)
// Preadv2 reads up to dsts.NumBytes() bytes from host file descriptor fd into
// dsts. offset and flags are interpreted as for preadv2(2).
//
@@ -44,9 +49,9 @@ func Preadv2(fd int32, dsts safemem.BlockSeq, offset int64, flags uint32) (uint6
}
} else {
iovs := safemem.IovecsFromBlockSeq(dsts)
if len(iovs) > maxIov {
log.Debugf("hostfd.Preadv2: truncating from %d iovecs to %d", len(iovs), maxIov)
iovs = iovs[:maxIov]
if len(iovs) > MaxReadWriteIov {
log.Debugf("hostfd.Preadv2: truncating from %d iovecs to %d", len(iovs), MaxReadWriteIov)
iovs = iovs[:MaxReadWriteIov]
}
n, _, e = unix.Syscall6(unix.SYS_PREADV2, uintptr(fd), uintptr((unsafe.Pointer)(&iovs[0])), uintptr(len(iovs)), uintptr(offset), 0 /* pos_h */, uintptr(flags))
}
@@ -80,9 +85,9 @@ func Pwritev2(fd int32, srcs safemem.BlockSeq, offset int64, flags uint32) (uint
}
} else {
iovs := safemem.IovecsFromBlockSeq(srcs)
if len(iovs) > maxIov {
log.Debugf("hostfd.Preadv2: truncating from %d iovecs to %d", len(iovs), maxIov)
iovs = iovs[:maxIov]
if len(iovs) > MaxReadWriteIov {
log.Debugf("hostfd.Preadv2: truncating from %d iovecs to %d", len(iovs), MaxReadWriteIov)
iovs = iovs[:MaxReadWriteIov]
}
n, _, e = unix.Syscall6(unix.SYS_PWRITEV2, uintptr(fd), uintptr((unsafe.Pointer)(&iovs[0])), uintptr(len(iovs)), uintptr(offset), 0 /* pos_h */, uintptr(flags))
}
-1
View File
@@ -46,7 +46,6 @@ deps_test(
"//pkg/gohacks",
"//pkg/goid",
"//pkg/ilist",
"//pkg/iovec",
"//pkg/linewriter",
"//pkg/log",
"//pkg/rand",
-1
View File
@@ -14,7 +14,6 @@ go_library(
],
visibility = ["//visibility:public"],
deps = [
"//pkg/iovec",
"//pkg/sync",
"//pkg/tcpip",
"//pkg/tcpip/buffer",
+146 -65
View File
@@ -44,7 +44,6 @@ import (
"sync/atomic"
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/iovec"
"gvisor.dev/gvisor/pkg/sync"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/buffer"
@@ -138,6 +137,20 @@ type endpoint struct {
// gsoKind is the supported kind of GSO.
gsoKind stack.SupportedGSO
// maxSyscallHeaderBytes has the same meaning as
// Options.MaxSyscallHeaderBytes.
maxSyscallHeaderBytes uintptr
// writevMaxIovs is the maximum number of iovecs that may be passed to
// rawfile.NonBlockingWriteIovec, as possibly limited by
// maxSyscallHeaderBytes. (No analogous limit is defined for
// rawfile.NonBlockingSendMMsg, since in that case the maximum number of
// iovecs also depends on the number of mmsghdrs. Instead, if sendBatch
// encounters a packet whose iovec count is limited by
// maxSyscallHeaderBytes, it falls back to writing the packet using writev
// via WritePacket.)
writevMaxIovs int
}
// Options specify the details about the fd-based endpoint to be created.
@@ -186,6 +199,11 @@ type Options struct {
// RXChecksumOffload if true, indicates that this endpoints capability
// set should include CapabilityRXChecksumOffload.
RXChecksumOffload bool
// If MaxSyscallHeaderBytes is non-zero, it is the maximum number of bytes
// of struct iovec, msghdr, and mmsghdr that may be passed by each host
// system call.
MaxSyscallHeaderBytes int
}
// fanoutID is used for AF_PACKET based endpoints to enable PACKET_FANOUT
@@ -235,14 +253,25 @@ func New(opts *Options) (stack.LinkEndpoint, error) {
return nil, fmt.Errorf("opts.FD is empty, at least one FD must be specified")
}
if opts.MaxSyscallHeaderBytes < 0 {
return nil, fmt.Errorf("opts.MaxSyscallHeaderBytes is negative")
}
e := &endpoint{
fds: opts.FDs,
mtu: opts.MTU,
caps: caps,
closed: opts.ClosedFunc,
addr: opts.Address,
hdrSize: hdrSize,
packetDispatchMode: opts.PacketDispatchMode,
fds: opts.FDs,
mtu: opts.MTU,
caps: caps,
closed: opts.ClosedFunc,
addr: opts.Address,
hdrSize: hdrSize,
packetDispatchMode: opts.PacketDispatchMode,
maxSyscallHeaderBytes: uintptr(opts.MaxSyscallHeaderBytes),
writevMaxIovs: rawfile.MaxIovs,
}
if e.maxSyscallHeaderBytes != 0 {
if max := int(e.maxSyscallHeaderBytes / rawfile.SizeofIovec); max < e.writevMaxIovs {
e.writevMaxIovs = max
}
}
// Increment fanoutID to ensure that we don't re-use the same fanoutID for
@@ -470,9 +499,8 @@ func (e *endpoint) WritePacket(r stack.RouteInfo, protocol tcpip.NetworkProtocol
e.AddHeader(r.LocalLinkAddress, r.RemoteLinkAddress, protocol, pkt)
}
var builder iovec.Builder
fd := e.fds[pkt.Hash%uint32(len(e.fds))]
var vnetHdrBuf []byte
if e.gsoKind == stack.HWGSOSupported {
vnetHdr := virtioNetHdr{}
if pkt.GSOOptions.Type != stack.GSONone {
@@ -494,71 +522,123 @@ func (e *endpoint) WritePacket(r stack.RouteInfo, protocol tcpip.NetworkProtocol
vnetHdr.gsoSize = pkt.GSOOptions.MSS
}
}
vnetHdrBuf := vnetHdr.marshal()
builder.Add(vnetHdrBuf)
vnetHdrBuf = vnetHdr.marshal()
}
for _, v := range pkt.Views() {
builder.Add(v)
views := pkt.Views()
numIovecs := len(views)
if len(vnetHdrBuf) != 0 {
numIovecs++
}
return rawfile.NonBlockingWriteIovec(fd, builder.Build())
if numIovecs > e.writevMaxIovs {
numIovecs = e.writevMaxIovs
}
// Allocate small iovec arrays on the stack.
var iovecsArr [8]unix.Iovec
iovecs := iovecsArr[:0]
if numIovecs > len(iovecsArr) {
iovecs = make([]unix.Iovec, 0, numIovecs)
}
iovecs = rawfile.AppendIovecFromBytes(iovecs, vnetHdrBuf, numIovecs)
for _, v := range views {
iovecs = rawfile.AppendIovecFromBytes(iovecs, v, numIovecs)
}
return rawfile.NonBlockingWriteIovec(fd, iovecs)
}
func (e *endpoint) sendBatch(batchFD int, batch []*stack.PacketBuffer) (int, tcpip.Error) {
func (e *endpoint) sendBatch(batchFD int, pkts []*stack.PacketBuffer) (int, tcpip.Error) {
// Send a batch of packets through batchFD.
mmsgHdrs := make([]rawfile.MMsgHdr, 0, len(batch))
for _, pkt := range batch {
if e.hdrSize > 0 {
e.AddHeader(pkt.EgressRoute.LocalLinkAddress, pkt.EgressRoute.RemoteLinkAddress, pkt.NetworkProtocolNumber, pkt)
}
mmsgHdrsStorage := make([]rawfile.MMsgHdr, 0, len(pkts))
packets := 0
for packets < len(pkts) {
mmsgHdrs := mmsgHdrsStorage
batch := pkts[packets:]
syscallHeaderBytes := uintptr(0)
for _, pkt := range batch {
if e.hdrSize > 0 {
e.AddHeader(pkt.EgressRoute.LocalLinkAddress, pkt.EgressRoute.RemoteLinkAddress, pkt.NetworkProtocolNumber, pkt)
}
var vnetHdrBuf []byte
if e.gsoKind == stack.HWGSOSupported {
vnetHdr := virtioNetHdr{}
if pkt.GSOOptions.Type != stack.GSONone {
vnetHdr.hdrLen = uint16(pkt.HeaderSize())
if pkt.GSOOptions.NeedsCsum {
vnetHdr.flags = _VIRTIO_NET_HDR_F_NEEDS_CSUM
vnetHdr.csumStart = header.EthernetMinimumSize + pkt.GSOOptions.L3HdrLen
vnetHdr.csumOffset = pkt.GSOOptions.CsumOffset
}
if pkt.GSOOptions.Type != stack.GSONone && uint16(pkt.Data().Size()) > pkt.GSOOptions.MSS {
switch pkt.GSOOptions.Type {
case stack.GSOTCPv4:
vnetHdr.gsoType = _VIRTIO_NET_HDR_GSO_TCPV4
case stack.GSOTCPv6:
vnetHdr.gsoType = _VIRTIO_NET_HDR_GSO_TCPV6
default:
panic(fmt.Sprintf("Unknown gso type: %v", pkt.GSOOptions.Type))
var vnetHdrBuf []byte
if e.gsoKind == stack.HWGSOSupported {
vnetHdr := virtioNetHdr{}
if pkt.GSOOptions.Type != stack.GSONone {
vnetHdr.hdrLen = uint16(pkt.HeaderSize())
if pkt.GSOOptions.NeedsCsum {
vnetHdr.flags = _VIRTIO_NET_HDR_F_NEEDS_CSUM
vnetHdr.csumStart = header.EthernetMinimumSize + pkt.GSOOptions.L3HdrLen
vnetHdr.csumOffset = pkt.GSOOptions.CsumOffset
}
vnetHdr.gsoSize = pkt.GSOOptions.MSS
if pkt.GSOOptions.Type != stack.GSONone && uint16(pkt.Data().Size()) > pkt.GSOOptions.MSS {
switch pkt.GSOOptions.Type {
case stack.GSOTCPv4:
vnetHdr.gsoType = _VIRTIO_NET_HDR_GSO_TCPV4
case stack.GSOTCPv6:
vnetHdr.gsoType = _VIRTIO_NET_HDR_GSO_TCPV6
default:
panic(fmt.Sprintf("Unknown gso type: %v", pkt.GSOOptions.Type))
}
vnetHdr.gsoSize = pkt.GSOOptions.MSS
}
}
vnetHdrBuf = vnetHdr.marshal()
}
views := pkt.Views()
numIovecs := len(views)
if len(vnetHdrBuf) != 0 {
numIovecs++
}
if numIovecs > rawfile.MaxIovs {
numIovecs = rawfile.MaxIovs
}
if e.maxSyscallHeaderBytes != 0 {
syscallHeaderBytes += rawfile.SizeofMMsgHdr + uintptr(numIovecs)*rawfile.SizeofIovec
if syscallHeaderBytes > e.maxSyscallHeaderBytes {
// We can't fit this packet into this call to sendmmsg().
// We could potentially do so if we reduced numIovecs
// further, but this might incur considerable extra
// copying. Leave it to the next batch instead.
break
}
}
vnetHdrBuf = vnetHdr.marshal()
// We can't easily allocate iovec arrays on the stack here since
// they will escape this loop iteration via mmsgHdrs.
iovecs := make([]unix.Iovec, 0, numIovecs)
iovecs = rawfile.AppendIovecFromBytes(iovecs, vnetHdrBuf, numIovecs)
for _, v := range views {
iovecs = rawfile.AppendIovecFromBytes(iovecs, v, numIovecs)
}
var mmsgHdr rawfile.MMsgHdr
mmsgHdr.Msg.Iov = &iovecs[0]
mmsgHdr.Msg.SetIovlen(len(iovecs))
mmsgHdrs = append(mmsgHdrs, mmsgHdr)
}
var builder iovec.Builder
builder.Add(vnetHdrBuf)
for _, v := range pkt.Views() {
builder.Add(v)
if len(mmsgHdrs) == 0 {
// We can't fit batch[0] into a mmsghdr while staying under
// e.maxSyscallHeaderBytes. Use WritePacket, which will avoid the
// mmsghdr (by using writev) and re-buffer iovecs more aggressively
// if necessary (by using e.writevMaxIovs instead of
// rawfile.MaxIovs).
pkt := batch[0]
if err := e.WritePacket(pkt.EgressRoute, pkt.NetworkProtocolNumber, pkt); err != nil {
return packets, err
}
packets++
} else {
for len(mmsgHdrs) > 0 {
sent, err := rawfile.NonBlockingSendMMsg(batchFD, mmsgHdrs)
if err != nil {
return packets, err
}
packets += sent
mmsgHdrs = mmsgHdrs[sent:]
}
}
iovecs := builder.Build()
var mmsgHdr rawfile.MMsgHdr
mmsgHdr.Msg.Iov = &iovecs[0]
mmsgHdr.Msg.SetIovlen((len(iovecs)))
mmsgHdrs = append(mmsgHdrs, mmsgHdr)
}
packets := 0
for len(mmsgHdrs) > 0 {
sent, err := rawfile.NonBlockingSendMMsg(batchFD, mmsgHdrs)
if err != nil {
return packets, err
}
packets += sent
mmsgHdrs = mmsgHdrs[sent:]
}
return packets, nil
@@ -676,8 +756,9 @@ func NewInjectable(fd int, mtu uint32, capabilities stack.LinkEndpointCapabiliti
unix.SetNonblock(fd, true)
return &InjectableEndpoint{endpoint: endpoint{
fds: []int{fd},
mtu: mtu,
caps: capabilities,
fds: []int{fd},
mtu: mtu,
caps: capabilities,
writevMaxIovs: rawfile.MaxIovs,
}}
}
+54 -7
View File
@@ -19,12 +19,66 @@
package rawfile
import (
"reflect"
"unsafe"
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/tcpip"
)
// SizeofIovec is the size of a unix.Iovec in bytes.
const SizeofIovec = unsafe.Sizeof(unix.Iovec{})
// MaxIovs is UIO_MAXIOV, the maximum number of iovecs that may be passed to a
// host system call in a single array.
const MaxIovs = 1024
// IovecFromBytes returns a unix.Iovec representing bs.
//
// Preconditions: len(bs) > 0.
func IovecFromBytes(bs []byte) unix.Iovec {
iov := unix.Iovec{
Base: &bs[0],
}
iov.SetLen(len(bs))
return iov
}
func bytesFromIovec(iov unix.Iovec) (bs []byte) {
sh := (*reflect.SliceHeader)(unsafe.Pointer(&bs))
sh.Data = uintptr(unsafe.Pointer(iov.Base))
sh.Len = int(iov.Len)
sh.Cap = int(iov.Len)
return
}
// AppendIovecFromBytes returns append(iovs, IovecFromBytes(bs)). If len(bs) ==
// 0, AppendIovecFromBytes returns iovs without modification. If len(iovs) >=
// max, AppendIovecFromBytes replaces the final iovec in iovs with one that
// also includes the contents of bs. Note that this implies that
// AppendIovecFromBytes is only usable when the returned iovec slice is used as
// the source of a write.
func AppendIovecFromBytes(iovs []unix.Iovec, bs []byte, max int) []unix.Iovec {
if len(bs) == 0 {
return iovs
}
if len(iovs) < max {
return append(iovs, IovecFromBytes(bs))
}
iovs[len(iovs)-1] = IovecFromBytes(append(bytesFromIovec(iovs[len(iovs)-1]), bs...))
return iovs
}
// MMsgHdr represents the mmsg_hdr structure required by recvmmsg() on linux.
type MMsgHdr struct {
Msg unix.Msghdr
Len uint32
_ [4]byte
}
// SizeofMMsgHdr is the size of a MMsgHdr in bytes.
const SizeofMMsgHdr = unsafe.Sizeof(MMsgHdr{})
// GetMTU determines the MTU of a network interface device.
func GetMTU(name string) (uint32, error) {
fd, err := unix.Socket(unix.AF_UNIX, unix.SOCK_DGRAM, 0)
@@ -137,13 +191,6 @@ func BlockingReadv(fd int, iovecs []unix.Iovec) (int, tcpip.Error) {
}
}
// MMsgHdr represents the mmsg_hdr structure required by recvmmsg() on linux.
type MMsgHdr struct {
Msg unix.Msghdr
Len uint32
_ [4]byte
}
// BlockingRecvMMsg reads from a file descriptor that is set up as non-blocking
// and stores the received messages in a slice of MMsgHdr structures. If no data
// is available, it will block in a poll() syscall until the file descriptor