netstack: add gro_flush_timeout

Makes a per-interface file available to configure the GRO timeout, e.g.
/sys/class/net/eth0/gro_flush_timeout

PiperOrigin-RevId: 487082821
This commit is contained in:
Kevin Krakauer
2022-11-08 16:34:55 -08:00
committed by gVisor bot
parent 28bbe00821
commit 901d9a75d3
8 changed files with 169 additions and 0 deletions
+2
View File
@@ -19,6 +19,7 @@ go_library(
srcs = [
"dir_refs.go",
"kcov.go",
"net.go",
"sys.go",
],
visibility = ["//pkg/sentry:internal"],
@@ -33,6 +34,7 @@ go_library(
"//pkg/refsvfs2",
"//pkg/sentry/arch",
"//pkg/sentry/fsimpl/kernfs",
"//pkg/sentry/inet",
"//pkg/sentry/kernel",
"//pkg/sentry/kernel/auth",
"//pkg/sentry/memmap",
+93
View File
@@ -0,0 +1,93 @@
// Copyright 2022 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.
package sys
import (
"bytes"
"fmt"
"time"
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
"gvisor.dev/gvisor/pkg/sentry/fsimpl/kernfs"
"gvisor.dev/gvisor/pkg/sentry/inet"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
"gvisor.dev/gvisor/pkg/sentry/vfs"
"gvisor.dev/gvisor/pkg/usermem"
)
// newNetDir returns a directory containing a subdirectory for each network
// interface.
func (fs *filesystem) newNetDir(ctx context.Context, creds *auth.Credentials, mode linux.FileMode) map[string]kernfs.Inode {
// Get list of interfaces.
stk := inet.StackFromContext(ctx)
if stk == nil {
return map[string]kernfs.Inode{}
}
subDirs := make(map[string]kernfs.Inode)
for idx, iface := range stk.Interfaces() {
subDirs[iface.Name] = fs.newIfaceDir(ctx, creds, mode, idx, stk)
}
return subDirs
}
// newIfaceDir returns a directory containing per-interface files.
func (fs *filesystem) newIfaceDir(ctx context.Context, creds *auth.Credentials, mode linux.FileMode, idx int32, stk inet.Stack) kernfs.Inode {
files := map[string]kernfs.Inode{
"gro_flush_timeout": fs.newGROTimeoutFile(ctx, creds, mode, idx, stk),
}
return fs.newDir(ctx, creds, mode, files)
}
// groTimeoutFile enables the reading and writing of the GRO timeout.
//
// +stateify savable
type groTimeoutFile struct {
implStatFS
kernfs.DynamicBytesFile
idx int32
stk inet.Stack
}
// newGROTimeoutFile returns a file that can be used to read and set the GRO
// timeout.
func (fs *filesystem) newGROTimeoutFile(ctx context.Context, creds *auth.Credentials, mode linux.FileMode, idx int32, stk inet.Stack) kernfs.Inode {
file := groTimeoutFile{idx: idx, stk: stk}
file.DynamicBytesFile.Init(ctx, creds, linux.UNNAMED_MAJOR, fs.devMinor, fs.NextIno(), &file, mode)
return &file
}
// Generate implements vfs.DynamicBytesSource.Generate.
func (gf *groTimeoutFile) Generate(ctx context.Context, buf *bytes.Buffer) error {
timeout, err := gf.stk.GROTimeout(gf.idx)
if err != nil {
return err
}
fmt.Fprintf(buf, "%d\n", timeout.Nanoseconds())
return nil
}
// Write implements vfs.WritableDynamicBytesSource.Write.
func (gf *groTimeoutFile) Write(ctx context.Context, _ *vfs.FileDescription, src usermem.IOSequence, offset int64) (int64, error) {
val := []int32{0}
nRead, err := usermem.CopyInt32StringsInVec(ctx, src.IO, src.Addrs, val, src.Opts)
if err != nil {
return 0, err
}
gf.stk.SetGROTimeout(gf.idx, time.Duration(val[0])*time.Nanosecond)
return nRead, nil
}
+2
View File
@@ -108,12 +108,14 @@ func (fsType FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.Virt
classSub := map[string]kernfs.Inode{
"power_supply": fs.newDir(ctx, creds, defaultSysDirMode, nil),
"net": fs.newDir(ctx, creds, defaultSysDirMode, fs.newNetDir(ctx, creds, defaultSysDirMode)),
}
devicesSub := map[string]kernfs.Inode{
"system": fs.newDir(ctx, creds, defaultSysDirMode, map[string]kernfs.Inode{
"cpu": cpuDir(ctx, fs, creds),
}),
}
productName := ""
if opts.InternalData != nil {
data := opts.InternalData.(*InternalData)
+8
View File
@@ -16,6 +16,8 @@
package inet
import (
"time"
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/stack"
@@ -108,6 +110,12 @@ type Stack interface {
// SetPortRange sets the UDP and TCP IPv4 and IPv6 ephemeral port range
// (inclusive).
SetPortRange(start uint16, end uint16) error
// GROTimeout returns the GRO timeout.
GROTimeout(NICID int32) (time.Duration, error)
// GROTimeout sets the GRO timeout.
SetGROTimeout(NICID int32, timeout time.Duration) error
}
// Interface contains information about a network interface.
+13
View File
@@ -17,6 +17,7 @@ package inet
import (
"bytes"
"fmt"
"time"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/stack"
@@ -184,3 +185,15 @@ func (*TestStack) SetPortRange(start uint16, end uint16) error {
// No-op.
return nil
}
// GROTimeout implements Stack.
func (*TestStack) GROTimeout(NICID int32) (time.Duration, error) {
// No-op.
return 0, nil
}
// SetGROTimeout implements Stack.
func (*TestStack) SetGROTimeout(NICID int32, timeout time.Duration) error {
// No-op.
return nil
}
+12
View File
@@ -24,6 +24,7 @@ import (
"strconv"
"strings"
"syscall"
"time"
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/abi/linux"
@@ -515,3 +516,14 @@ func (*Stack) PortRange() (uint16, uint16) {
func (*Stack) SetPortRange(uint16, uint16) error {
return linuxerr.EACCES
}
// GROTimeout implements inet.Stack.GROTimeout.
func (s *Stack) GROTimeout(NICID int32) (time.Duration, error) {
return 0, nil
}
// SetGROTimeout implements inet.Stack.SetGROTimeout.
func (s *Stack) SetGROTimeout(NICID int32, timeout time.Duration) error {
// We don't support setting the hostinet GRO timeout.
return linuxerr.EINVAL
}
+12
View File
@@ -16,6 +16,7 @@ package netstack
import (
"fmt"
"time"
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/errors/linuxerr"
@@ -491,3 +492,14 @@ func (s *Stack) PortRange() (uint16, uint16) {
func (s *Stack) SetPortRange(start uint16, end uint16) error {
return syserr.TranslateNetstackError(s.Stack.SetPortRange(start, end)).ToError()
}
// GROTimeout implements inet.Stack.GROTimeout.
func (s *Stack) GROTimeout(NICID int32) (time.Duration, error) {
timeout, err := s.Stack.GROTimeout(NICID)
return timeout, syserr.TranslateNetstackError(err).ToError()
}
// SetGROTimeout implements inet.Stack.SetGROTimeout.
func (s *Stack) SetGROTimeout(NICID int32, timeout time.Duration) error {
return syserr.TranslateNetstackError(s.Stack.SetGROTimeout(NICID, timeout)).ToError()
}
+27
View File
@@ -710,6 +710,33 @@ func (s *Stack) SetPortRange(start uint16, end uint16) tcpip.Error {
return s.PortManager.SetPortRange(start, end)
}
// GROTimeout returns the GRO timeout.
func (s *Stack) GROTimeout(NICID int32) (time.Duration, tcpip.Error) {
s.mu.RLock()
defer s.mu.RUnlock()
nic, ok := s.nics[tcpip.NICID(NICID)]
if !ok {
return 0, &tcpip.ErrUnknownNICID{}
}
return nic.gro.getInterval(), nil
}
// SetGROTimeout sets the GRO timeout.
func (s *Stack) SetGROTimeout(NICID int32, timeout time.Duration) tcpip.Error {
s.mu.RLock()
defer s.mu.RUnlock()
nic, ok := s.nics[tcpip.NICID(NICID)]
if !ok {
return &tcpip.ErrUnknownNICID{}
}
nic.gro.setInterval(timeout)
return nil
}
// SetRouteTable assigns the route table to be used by this stack. It
// specifies which NIC to use for given destination address ranges.
//