[vfs2] Implement /proc/sys/net/ipv4/tcp_rmem and /proc/sys/net/ipv4/tcp_wmem.

Updates #1035

PiperOrigin-RevId: 327253907
This commit is contained in:
Ayush Ranjan 2020-08-18 10:20:17 -07:00 committed by gVisor bot
parent 673b6cc7fc
commit b9d87ae7af
2 changed files with 106 additions and 5 deletions

View File

@ -36,6 +36,7 @@ go_library(
"//pkg/sentry/socket/unix/transport",
"//pkg/sentry/usage",
"//pkg/sentry/vfs",
"//pkg/sync",
"//pkg/syserror",
"//pkg/tcpip/header",
"//pkg/usermem",

View File

@ -25,10 +25,18 @@ import (
"gvisor.dev/gvisor/pkg/sentry/kernel"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
"gvisor.dev/gvisor/pkg/sentry/vfs"
"gvisor.dev/gvisor/pkg/sync"
"gvisor.dev/gvisor/pkg/syserror"
"gvisor.dev/gvisor/pkg/usermem"
)
type tcpMemDir int
const (
tcpRMem tcpMemDir = iota
tcpWMem
)
// newSysDir returns the dentry corresponding to /proc/sys directory.
func (fs *filesystem) newSysDir(root *auth.Credentials, k *kernel.Kernel) *kernfs.Dentry {
return kernfs.NewStaticDir(root, linux.UNNAMED_MAJOR, fs.devMinor, fs.NextIno(), 0555, map[string]*kernfs.Dentry{
@ -56,7 +64,9 @@ func (fs *filesystem) newSysNetDir(root *auth.Credentials, k *kernel.Kernel) *ke
contents = map[string]*kernfs.Dentry{
"ipv4": kernfs.NewStaticDir(root, linux.UNNAMED_MAJOR, fs.devMinor, fs.NextIno(), 0555, map[string]*kernfs.Dentry{
"tcp_recovery": fs.newDentry(root, fs.NextIno(), 0644, &tcpRecoveryData{stack: stack}),
"tcp_rmem": fs.newDentry(root, fs.NextIno(), 0644, &tcpMemData{stack: stack, dir: tcpRMem}),
"tcp_sack": fs.newDentry(root, fs.NextIno(), 0644, &tcpSackData{stack: stack}),
"tcp_wmem": fs.newDentry(root, fs.NextIno(), 0644, &tcpMemData{stack: stack, dir: tcpWMem}),
// The following files are simple stubs until they are implemented in
// netstack, most of these files are configuration related. We use the
@ -181,10 +191,11 @@ func (d *tcpSackData) Generate(ctx context.Context, buf *bytes.Buffer) error {
// Tough luck.
val = "1\n"
}
buf.WriteString(val)
return nil
_, err := buf.WriteString(val)
return err
}
// Write implements vfs.WritableDynamicBytesSource.Write.
func (d *tcpSackData) Write(ctx context.Context, src usermem.IOSequence, offset int64) (int64, error) {
if offset != 0 {
// No need to handle partial writes thus far.
@ -200,7 +211,7 @@ func (d *tcpSackData) Write(ctx context.Context, src usermem.IOSequence, offset
var v int32
n, err := usermem.CopyInt32StringInVec(ctx, src.IO, src.Addrs, &v, src.Opts)
if err != nil {
return n, err
return 0, err
}
if d.enabled == nil {
d.enabled = new(bool)
@ -228,10 +239,11 @@ func (d *tcpRecoveryData) Generate(ctx context.Context, buf *bytes.Buffer) error
return err
}
buf.WriteString(fmt.Sprintf("%d\n", recovery))
return nil
_, err = buf.WriteString(fmt.Sprintf("%d\n", recovery))
return err
}
// Write implements vfs.WritableDynamicBytesSource.Write.
func (d *tcpRecoveryData) Write(ctx context.Context, src usermem.IOSequence, offset int64) (int64, error) {
if offset != 0 {
// No need to handle partial writes thus far.
@ -254,3 +266,91 @@ func (d *tcpRecoveryData) Write(ctx context.Context, src usermem.IOSequence, off
}
return n, nil
}
// tcpMemData implements vfs.WritableDynamicBytesSource for
// /proc/sys/net/ipv4/tcp_rmem and /proc/sys/net/ipv4/tcp_wmem.
//
// +stateify savable
type tcpMemData struct {
kernfs.DynamicBytesFile
dir tcpMemDir
stack inet.Stack `state:"wait"`
// mu protects against concurrent reads/writes to FDs based on the dentry
// backing this byte source.
mu sync.Mutex `state:"nosave"`
}
var _ vfs.WritableDynamicBytesSource = (*tcpMemData)(nil)
// Generate implements vfs.DynamicBytesSource.
func (d *tcpMemData) Generate(ctx context.Context, buf *bytes.Buffer) error {
d.mu.Lock()
defer d.mu.Unlock()
size, err := d.readSizeLocked()
if err != nil {
return err
}
_, err = buf.WriteString(fmt.Sprintf("%d\t%d\t%d\n", size.Min, size.Default, size.Max))
return err
}
// Write implements vfs.WritableDynamicBytesSource.Write.
func (d *tcpMemData) Write(ctx context.Context, src usermem.IOSequence, offset int64) (int64, error) {
if offset != 0 {
// No need to handle partial writes thus far.
return 0, syserror.EINVAL
}
if src.NumBytes() == 0 {
return 0, nil
}
d.mu.Lock()
defer d.mu.Unlock()
// Limit the amount of memory allocated.
src = src.TakeFirst(usermem.PageSize - 1)
size, err := d.readSizeLocked()
if err != nil {
return 0, err
}
buf := []int32{int32(size.Min), int32(size.Default), int32(size.Max)}
n, err := usermem.CopyInt32StringsInVec(ctx, src.IO, src.Addrs, buf, src.Opts)
if err != nil {
return 0, err
}
newSize := inet.TCPBufferSize{
Min: int(buf[0]),
Default: int(buf[1]),
Max: int(buf[2]),
}
if err := d.writeSizeLocked(newSize); err != nil {
return 0, err
}
return n, nil
}
// Precondition: d.mu must be locked.
func (d *tcpMemData) readSizeLocked() (inet.TCPBufferSize, error) {
switch d.dir {
case tcpRMem:
return d.stack.TCPReceiveBufferSize()
case tcpWMem:
return d.stack.TCPSendBufferSize()
default:
panic(fmt.Sprintf("unknown tcpMemFile type: %v", d.dir))
}
}
// Precondition: d.mu must be locked.
func (d *tcpMemData) writeSizeLocked(size inet.TCPBufferSize) error {
switch d.dir {
case tcpRMem:
return d.stack.SetTCPReceiveBufferSize(size)
case tcpWMem:
return d.stack.SetTCPSendBufferSize(size)
default:
panic(fmt.Sprintf("unknown tcpMemFile type: %v", d.dir))
}
}