mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Internal change.
PiperOrigin-RevId: 381375705
This commit is contained in:
@@ -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"],
|
||||
)
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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",
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -49,7 +49,6 @@ go_library(
|
||||
"//pkg/fdnotifier",
|
||||
"//pkg/fspath",
|
||||
"//pkg/hostarch",
|
||||
"//pkg/iovec",
|
||||
"//pkg/log",
|
||||
"//pkg/marshal/primitive",
|
||||
"//pkg/refs",
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
@@ -46,7 +46,6 @@ deps_test(
|
||||
"//pkg/gohacks",
|
||||
"//pkg/goid",
|
||||
"//pkg/ilist",
|
||||
"//pkg/iovec",
|
||||
"//pkg/linewriter",
|
||||
"//pkg/log",
|
||||
"//pkg/rand",
|
||||
|
||||
@@ -14,7 +14,6 @@ go_library(
|
||||
],
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//pkg/iovec",
|
||||
"//pkg/sync",
|
||||
"//pkg/tcpip",
|
||||
"//pkg/tcpip/buffer",
|
||||
|
||||
@@ -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,
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user