mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Port filesystem metrics to VFS2.
PiperOrigin-RevId: 343196927
This commit is contained in:
+1
-1
@@ -49,13 +49,13 @@ go_library(
|
||||
"//pkg/amutex",
|
||||
"//pkg/context",
|
||||
"//pkg/log",
|
||||
"//pkg/metric",
|
||||
"//pkg/p9",
|
||||
"//pkg/refs",
|
||||
"//pkg/secio",
|
||||
"//pkg/sentry/arch",
|
||||
"//pkg/sentry/device",
|
||||
"//pkg/sentry/fs/lock",
|
||||
"//pkg/sentry/fsmetric",
|
||||
"//pkg/sentry/kernel/auth",
|
||||
"//pkg/sentry/kernel/time",
|
||||
"//pkg/sentry/limits",
|
||||
|
||||
+9
-38
@@ -17,13 +17,12 @@ package fs
|
||||
import (
|
||||
"math"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/amutex"
|
||||
"gvisor.dev/gvisor/pkg/context"
|
||||
"gvisor.dev/gvisor/pkg/metric"
|
||||
"gvisor.dev/gvisor/pkg/refs"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fs/lock"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fsmetric"
|
||||
"gvisor.dev/gvisor/pkg/sentry/limits"
|
||||
"gvisor.dev/gvisor/pkg/sentry/memmap"
|
||||
"gvisor.dev/gvisor/pkg/sentry/uniqueid"
|
||||
@@ -33,28 +32,6 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/waiter"
|
||||
)
|
||||
|
||||
var (
|
||||
// RecordWaitTime controls writing metrics for filesystem reads.
|
||||
// Enabling this comes at a small CPU cost due to performing two
|
||||
// monotonic clock reads per read call.
|
||||
//
|
||||
// Note that this is only performed in the direct read path, and may
|
||||
// not be consistently applied for other forms of reads, such as
|
||||
// splice.
|
||||
RecordWaitTime = false
|
||||
|
||||
reads = metric.MustCreateNewUint64Metric("/fs/reads", false /* sync */, "Number of file reads.")
|
||||
readWait = metric.MustCreateNewUint64NanosecondsMetric("/fs/read_wait", false /* sync */, "Time waiting on file reads, in nanoseconds.")
|
||||
)
|
||||
|
||||
// IncrementWait increments the given wait time metric, if enabled.
|
||||
func IncrementWait(m *metric.Uint64Metric, start time.Time) {
|
||||
if !RecordWaitTime {
|
||||
return
|
||||
}
|
||||
m.IncrementBy(uint64(time.Since(start)))
|
||||
}
|
||||
|
||||
// FileMaxOffset is the maximum possible file offset.
|
||||
const FileMaxOffset = math.MaxInt64
|
||||
|
||||
@@ -257,22 +234,19 @@ func (f *File) Readdir(ctx context.Context, serializer DentrySerializer) error {
|
||||
//
|
||||
// Returns syserror.ErrInterrupted if reading was interrupted.
|
||||
func (f *File) Readv(ctx context.Context, dst usermem.IOSequence) (int64, error) {
|
||||
var start time.Time
|
||||
if RecordWaitTime {
|
||||
start = time.Now()
|
||||
}
|
||||
start := fsmetric.StartReadWait()
|
||||
defer fsmetric.FinishReadWait(fsmetric.ReadWait, start)
|
||||
|
||||
if !f.mu.Lock(ctx) {
|
||||
IncrementWait(readWait, start)
|
||||
return 0, syserror.ErrInterrupted
|
||||
}
|
||||
|
||||
reads.Increment()
|
||||
fsmetric.Reads.Increment()
|
||||
n, err := f.FileOperations.Read(ctx, f, dst, f.offset)
|
||||
if n > 0 && !f.flags.NonSeekable {
|
||||
atomic.AddInt64(&f.offset, n)
|
||||
}
|
||||
f.mu.Unlock()
|
||||
IncrementWait(readWait, start)
|
||||
return n, err
|
||||
}
|
||||
|
||||
@@ -282,19 +256,16 @@ func (f *File) Readv(ctx context.Context, dst usermem.IOSequence) (int64, error)
|
||||
//
|
||||
// Otherwise same as Readv.
|
||||
func (f *File) Preadv(ctx context.Context, dst usermem.IOSequence, offset int64) (int64, error) {
|
||||
var start time.Time
|
||||
if RecordWaitTime {
|
||||
start = time.Now()
|
||||
}
|
||||
start := fsmetric.StartReadWait()
|
||||
defer fsmetric.FinishReadWait(fsmetric.ReadWait, start)
|
||||
|
||||
if !f.mu.Lock(ctx) {
|
||||
IncrementWait(readWait, start)
|
||||
return 0, syserror.ErrInterrupted
|
||||
}
|
||||
|
||||
reads.Increment()
|
||||
fsmetric.Reads.Increment()
|
||||
n, err := f.FileOperations.Read(ctx, f, dst, offset)
|
||||
f.mu.Unlock()
|
||||
IncrementWait(readWait, start)
|
||||
return n, err
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,6 @@ go_library(
|
||||
"//pkg/context",
|
||||
"//pkg/fd",
|
||||
"//pkg/log",
|
||||
"//pkg/metric",
|
||||
"//pkg/p9",
|
||||
"//pkg/refs",
|
||||
"//pkg/safemem",
|
||||
@@ -38,6 +37,7 @@ go_library(
|
||||
"//pkg/sentry/fs/fdpipe",
|
||||
"//pkg/sentry/fs/fsutil",
|
||||
"//pkg/sentry/fs/host",
|
||||
"//pkg/sentry/fsmetric",
|
||||
"//pkg/sentry/kernel/auth",
|
||||
"//pkg/sentry/kernel/pipe",
|
||||
"//pkg/sentry/kernel/time",
|
||||
|
||||
@@ -21,27 +21,17 @@ import (
|
||||
|
||||
"gvisor.dev/gvisor/pkg/context"
|
||||
"gvisor.dev/gvisor/pkg/log"
|
||||
"gvisor.dev/gvisor/pkg/metric"
|
||||
"gvisor.dev/gvisor/pkg/p9"
|
||||
"gvisor.dev/gvisor/pkg/sentry/device"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fs"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fs/fsutil"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fsmetric"
|
||||
"gvisor.dev/gvisor/pkg/sentry/memmap"
|
||||
"gvisor.dev/gvisor/pkg/syserror"
|
||||
"gvisor.dev/gvisor/pkg/usermem"
|
||||
"gvisor.dev/gvisor/pkg/waiter"
|
||||
)
|
||||
|
||||
var (
|
||||
opensWX = metric.MustCreateNewUint64Metric("/gofer/opened_write_execute_file", true /* sync */, "Number of times a writable+executable file was opened from a gofer.")
|
||||
opens9P = metric.MustCreateNewUint64Metric("/gofer/opens_9p", false /* sync */, "Number of times a 9P file was opened from a gofer.")
|
||||
opensHost = metric.MustCreateNewUint64Metric("/gofer/opens_host", false /* sync */, "Number of times a host file was opened from a gofer.")
|
||||
reads9P = metric.MustCreateNewUint64Metric("/gofer/reads_9p", false /* sync */, "Number of 9P file reads from a gofer.")
|
||||
readWait9P = metric.MustCreateNewUint64NanosecondsMetric("/gofer/read_wait_9p", false /* sync */, "Time waiting on 9P file reads from a gofer, in nanoseconds.")
|
||||
readsHost = metric.MustCreateNewUint64Metric("/gofer/reads_host", false /* sync */, "Number of host file reads from a gofer.")
|
||||
readWaitHost = metric.MustCreateNewUint64NanosecondsMetric("/gofer/read_wait_host", false /* sync */, "Time waiting on host file reads from a gofer, in nanoseconds.")
|
||||
)
|
||||
|
||||
// fileOperations implements fs.FileOperations for a remote file system.
|
||||
//
|
||||
// +stateify savable
|
||||
@@ -101,14 +91,14 @@ func NewFile(ctx context.Context, dirent *fs.Dirent, name string, flags fs.FileF
|
||||
}
|
||||
if flags.Write {
|
||||
if err := dirent.Inode.CheckPermission(ctx, fs.PermMask{Execute: true}); err == nil {
|
||||
opensWX.Increment()
|
||||
fsmetric.GoferOpensWX.Increment()
|
||||
log.Warningf("Opened a writable executable: %q", name)
|
||||
}
|
||||
}
|
||||
if handles.Host != nil {
|
||||
opensHost.Increment()
|
||||
fsmetric.GoferOpensHost.Increment()
|
||||
} else {
|
||||
opens9P.Increment()
|
||||
fsmetric.GoferOpens9P.Increment()
|
||||
}
|
||||
return fs.NewFile(ctx, dirent, flags, f)
|
||||
}
|
||||
@@ -278,20 +268,17 @@ func (f *fileOperations) Write(ctx context.Context, file *fs.File, src usermem.I
|
||||
// use this function rather than using a defer in Read() to avoid the performance hit of defer.
|
||||
func (f *fileOperations) incrementReadCounters(start time.Time) {
|
||||
if f.handles.Host != nil {
|
||||
readsHost.Increment()
|
||||
fs.IncrementWait(readWaitHost, start)
|
||||
fsmetric.GoferReadsHost.Increment()
|
||||
fsmetric.FinishReadWait(fsmetric.GoferReadWaitHost, start)
|
||||
} else {
|
||||
reads9P.Increment()
|
||||
fs.IncrementWait(readWait9P, start)
|
||||
fsmetric.GoferReads9P.Increment()
|
||||
fsmetric.FinishReadWait(fsmetric.GoferReadWait9P, start)
|
||||
}
|
||||
}
|
||||
|
||||
// Read implements fs.FileOperations.Read.
|
||||
func (f *fileOperations) Read(ctx context.Context, file *fs.File, dst usermem.IOSequence, offset int64) (int64, error) {
|
||||
var start time.Time
|
||||
if fs.RecordWaitTime {
|
||||
start = time.Now()
|
||||
}
|
||||
start := fsmetric.StartReadWait()
|
||||
if fs.IsDir(file.Dirent.Inode.StableAttr) {
|
||||
// Not all remote file systems enforce this so this client does.
|
||||
f.incrementReadCounters(start)
|
||||
|
||||
@@ -18,9 +18,9 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
"gvisor.dev/gvisor/pkg/context"
|
||||
"gvisor.dev/gvisor/pkg/log"
|
||||
"gvisor.dev/gvisor/pkg/metric"
|
||||
"gvisor.dev/gvisor/pkg/refs"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fs/lock"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fsmetric"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
|
||||
"gvisor.dev/gvisor/pkg/sentry/memmap"
|
||||
"gvisor.dev/gvisor/pkg/sentry/socket/unix/transport"
|
||||
@@ -28,8 +28,6 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/syserror"
|
||||
)
|
||||
|
||||
var opens = metric.MustCreateNewUint64Metric("/fs/opens", false /* sync */, "Number of file opens.")
|
||||
|
||||
// Inode is a file system object that can be simultaneously referenced by different
|
||||
// components of the VFS (Dirent, fs.File, etc).
|
||||
//
|
||||
@@ -247,7 +245,7 @@ func (i *Inode) GetFile(ctx context.Context, d *Dirent, flags FileFlags) (*File,
|
||||
if i.overlay != nil {
|
||||
return overlayGetFile(ctx, i.overlay, d, flags)
|
||||
}
|
||||
opens.Increment()
|
||||
fsmetric.Opens.Increment()
|
||||
return i.InodeOperations.GetFile(ctx, d, flags)
|
||||
}
|
||||
|
||||
|
||||
@@ -15,12 +15,12 @@ go_library(
|
||||
deps = [
|
||||
"//pkg/abi/linux",
|
||||
"//pkg/context",
|
||||
"//pkg/metric",
|
||||
"//pkg/safemem",
|
||||
"//pkg/sentry/device",
|
||||
"//pkg/sentry/fs",
|
||||
"//pkg/sentry/fs/fsutil",
|
||||
"//pkg/sentry/fs/ramfs",
|
||||
"//pkg/sentry/fsmetric",
|
||||
"//pkg/sentry/kernel",
|
||||
"//pkg/sentry/kernel/auth",
|
||||
"//pkg/sentry/kernel/pipe",
|
||||
|
||||
@@ -18,14 +18,13 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"time"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
"gvisor.dev/gvisor/pkg/context"
|
||||
"gvisor.dev/gvisor/pkg/metric"
|
||||
"gvisor.dev/gvisor/pkg/safemem"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fs"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fs/fsutil"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fsmetric"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel"
|
||||
ktime "gvisor.dev/gvisor/pkg/sentry/kernel/time"
|
||||
"gvisor.dev/gvisor/pkg/sentry/memmap"
|
||||
@@ -35,13 +34,6 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/usermem"
|
||||
)
|
||||
|
||||
var (
|
||||
opensRO = metric.MustCreateNewUint64Metric("/in_memory_file/opens_ro", false /* sync */, "Number of times an in-memory file was opened in read-only mode.")
|
||||
opensW = metric.MustCreateNewUint64Metric("/in_memory_file/opens_w", false /* sync */, "Number of times an in-memory file was opened in write mode.")
|
||||
reads = metric.MustCreateNewUint64Metric("/in_memory_file/reads", false /* sync */, "Number of in-memory file reads.")
|
||||
readWait = metric.MustCreateNewUint64NanosecondsMetric("/in_memory_file/read_wait", false /* sync */, "Time waiting on in-memory file reads, in nanoseconds.")
|
||||
)
|
||||
|
||||
// fileInodeOperations implements fs.InodeOperations for a regular tmpfs file.
|
||||
// These files are backed by pages allocated from a platform.Memory, and may be
|
||||
// directly mapped.
|
||||
@@ -157,9 +149,9 @@ func (*fileInodeOperations) Rename(ctx context.Context, inode *fs.Inode, oldPare
|
||||
// GetFile implements fs.InodeOperations.GetFile.
|
||||
func (f *fileInodeOperations) GetFile(ctx context.Context, d *fs.Dirent, flags fs.FileFlags) (*fs.File, error) {
|
||||
if flags.Write {
|
||||
opensW.Increment()
|
||||
fsmetric.TmpfsOpensW.Increment()
|
||||
} else if flags.Read {
|
||||
opensRO.Increment()
|
||||
fsmetric.TmpfsOpensRO.Increment()
|
||||
}
|
||||
flags.Pread = true
|
||||
flags.Pwrite = true
|
||||
@@ -319,14 +311,12 @@ func (*fileInodeOperations) StatFS(context.Context) (fs.Info, error) {
|
||||
}
|
||||
|
||||
func (f *fileInodeOperations) read(ctx context.Context, file *fs.File, dst usermem.IOSequence, offset int64) (int64, error) {
|
||||
var start time.Time
|
||||
if fs.RecordWaitTime {
|
||||
start = time.Now()
|
||||
}
|
||||
reads.Increment()
|
||||
start := fsmetric.StartReadWait()
|
||||
defer fsmetric.FinishReadWait(fsmetric.TmpfsReadWait, start)
|
||||
fsmetric.TmpfsReads.Increment()
|
||||
|
||||
// Zero length reads for tmpfs are no-ops.
|
||||
if dst.NumBytes() == 0 {
|
||||
fs.IncrementWait(readWait, start)
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
@@ -343,7 +333,6 @@ func (f *fileInodeOperations) read(ctx context.Context, file *fs.File, dst userm
|
||||
size := f.attr.Size
|
||||
f.dataMu.RUnlock()
|
||||
if offset >= size {
|
||||
fs.IncrementWait(readWait, start)
|
||||
return 0, io.EOF
|
||||
}
|
||||
|
||||
@@ -354,7 +343,6 @@ func (f *fileInodeOperations) read(ctx context.Context, file *fs.File, dst userm
|
||||
f.attr.AccessTime = ktime.NowFromContext(ctx)
|
||||
f.attrMu.Unlock()
|
||||
}
|
||||
fs.IncrementWait(readWait, start)
|
||||
return n, err
|
||||
}
|
||||
|
||||
|
||||
@@ -59,6 +59,7 @@ go_library(
|
||||
"//pkg/sentry/fs/fsutil",
|
||||
"//pkg/sentry/fs/lock",
|
||||
"//pkg/sentry/fsimpl/host",
|
||||
"//pkg/sentry/fsmetric",
|
||||
"//pkg/sentry/hostfd",
|
||||
"//pkg/sentry/kernel",
|
||||
"//pkg/sentry/kernel/auth",
|
||||
|
||||
@@ -24,6 +24,7 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/fspath"
|
||||
"gvisor.dev/gvisor/pkg/p9"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fsimpl/host"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fsmetric"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel/pipe"
|
||||
@@ -985,14 +986,11 @@ func (d *dentry) open(ctx context.Context, rp *vfs.ResolvingPath, opts *vfs.Open
|
||||
switch d.fileType() {
|
||||
case linux.S_IFREG:
|
||||
if !d.fs.opts.regularFilesUseSpecialFileFD {
|
||||
if err := d.ensureSharedHandle(ctx, ats&vfs.MayRead != 0, ats&vfs.MayWrite != 0, trunc); err != nil {
|
||||
if err := d.ensureSharedHandle(ctx, ats.MayRead(), ats.MayWrite(), trunc); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fd := ®ularFileFD{}
|
||||
fd.LockFD.Init(&d.locks)
|
||||
if err := fd.vfsfd.Init(fd, opts.Flags, mnt, &d.vfsd, &vfs.FileDescriptionOptions{
|
||||
AllowDirectIO: true,
|
||||
}); err != nil {
|
||||
fd, err := newRegularFileFD(mnt, d, opts.Flags)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vfd = &fd.vfsfd
|
||||
@@ -1019,6 +1017,11 @@ func (d *dentry) open(ctx context.Context, rp *vfs.ResolvingPath, opts *vfs.Open
|
||||
if err := fd.vfsfd.Init(fd, opts.Flags, mnt, &d.vfsd, &vfs.FileDescriptionOptions{}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if atomic.LoadInt32(&d.readFD) >= 0 {
|
||||
fsmetric.GoferOpensHost.Increment()
|
||||
} else {
|
||||
fsmetric.GoferOpens9P.Increment()
|
||||
}
|
||||
return &fd.vfsfd, nil
|
||||
case linux.S_IFLNK:
|
||||
// Can't open symlinks without O_PATH (which is unimplemented).
|
||||
@@ -1110,7 +1113,7 @@ retry:
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
fd, err := newSpecialFileFD(h, mnt, d, &d.locks, opts.Flags)
|
||||
fd, err := newSpecialFileFD(h, mnt, d, opts.Flags)
|
||||
if err != nil {
|
||||
h.close(ctx)
|
||||
return nil, err
|
||||
@@ -1205,11 +1208,8 @@ func (d *dentry) createAndOpenChildLocked(ctx context.Context, rp *vfs.Resolving
|
||||
// Finally, construct a file description representing the created file.
|
||||
var childVFSFD *vfs.FileDescription
|
||||
if useRegularFileFD {
|
||||
fd := ®ularFileFD{}
|
||||
fd.LockFD.Init(&child.locks)
|
||||
if err := fd.vfsfd.Init(fd, opts.Flags, mnt, &child.vfsd, &vfs.FileDescriptionOptions{
|
||||
AllowDirectIO: true,
|
||||
}); err != nil {
|
||||
fd, err := newRegularFileFD(mnt, child, opts.Flags)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
childVFSFD = &fd.vfsfd
|
||||
@@ -1221,7 +1221,7 @@ func (d *dentry) createAndOpenChildLocked(ctx context.Context, rp *vfs.Resolving
|
||||
if fdobj != nil {
|
||||
h.fd = int32(fdobj.Release())
|
||||
}
|
||||
fd, err := newSpecialFileFD(h, mnt, child, &d.locks, opts.Flags)
|
||||
fd, err := newSpecialFileFD(h, mnt, child, opts.Flags)
|
||||
if err != nil {
|
||||
h.close(ctx)
|
||||
return nil, err
|
||||
|
||||
@@ -743,7 +743,9 @@ type dentry struct {
|
||||
// for memory mappings. If mmapFD is -1, no such FD is available, and the
|
||||
// internal page cache implementation is used for memory mappings instead.
|
||||
//
|
||||
// These fields are protected by handleMu.
|
||||
// These fields are protected by handleMu. readFD, writeFD, and mmapFD are
|
||||
// additionally written using atomic memory operations, allowing them to be
|
||||
// read (albeit racily) with atomic.LoadInt32() without locking handleMu.
|
||||
//
|
||||
// readFile and writeFile may or may not represent the same p9.File. Once
|
||||
// either p9.File transitions from closed (isNil() == true) to open
|
||||
@@ -1668,7 +1670,7 @@ func (d *dentry) ensureSharedHandle(ctx context.Context, read, write, trunc bool
|
||||
}
|
||||
fdsToClose = append(fdsToClose, d.readFD)
|
||||
invalidateTranslations = true
|
||||
d.readFD = h.fd
|
||||
atomic.StoreInt32(&d.readFD, h.fd)
|
||||
} else {
|
||||
// Otherwise, we want to avoid invalidating existing
|
||||
// memmap.Translations (which is expensive); instead, use
|
||||
@@ -1689,15 +1691,15 @@ func (d *dentry) ensureSharedHandle(ctx context.Context, read, write, trunc bool
|
||||
h.fd = d.readFD
|
||||
}
|
||||
} else {
|
||||
d.readFD = h.fd
|
||||
atomic.StoreInt32(&d.readFD, h.fd)
|
||||
}
|
||||
if d.writeFD != h.fd && d.writeFD >= 0 {
|
||||
fdsToClose = append(fdsToClose, d.writeFD)
|
||||
}
|
||||
d.writeFD = h.fd
|
||||
d.mmapFD = h.fd
|
||||
atomic.StoreInt32(&d.writeFD, h.fd)
|
||||
atomic.StoreInt32(&d.mmapFD, h.fd)
|
||||
} else if openReadable && d.readFD < 0 {
|
||||
d.readFD = h.fd
|
||||
atomic.StoreInt32(&d.readFD, h.fd)
|
||||
// If the file has not been opened for writing, the new FD may
|
||||
// be used for read-only memory mappings. If the file was
|
||||
// previously opened for reading (without an FD), then existing
|
||||
@@ -1705,10 +1707,10 @@ func (d *dentry) ensureSharedHandle(ctx context.Context, read, write, trunc bool
|
||||
// invalidate those mappings.
|
||||
if d.writeFile.isNil() {
|
||||
invalidateTranslations = !d.readFile.isNil()
|
||||
d.mmapFD = h.fd
|
||||
atomic.StoreInt32(&d.mmapFD, h.fd)
|
||||
}
|
||||
} else if openWritable && d.writeFD < 0 {
|
||||
d.writeFD = h.fd
|
||||
atomic.StoreInt32(&d.writeFD, h.fd)
|
||||
if d.readFD >= 0 {
|
||||
// We have an existing read-only FD, but the file has just
|
||||
// been opened for writing, so we need to start supporting
|
||||
@@ -1717,7 +1719,7 @@ func (d *dentry) ensureSharedHandle(ctx context.Context, read, write, trunc bool
|
||||
// writable memory mappings. Switch to using the internal
|
||||
// page cache.
|
||||
invalidateTranslations = true
|
||||
d.mmapFD = -1
|
||||
atomic.StoreInt32(&d.mmapFD, -1)
|
||||
}
|
||||
} else {
|
||||
// The new FD is not useful.
|
||||
@@ -1729,7 +1731,7 @@ func (d *dentry) ensureSharedHandle(ctx context.Context, read, write, trunc bool
|
||||
// memory mappings. However, we have no writable host FD. Switch to
|
||||
// using the internal page cache.
|
||||
invalidateTranslations = true
|
||||
d.mmapFD = -1
|
||||
atomic.StoreInt32(&d.mmapFD, -1)
|
||||
}
|
||||
|
||||
// Switch to new fids.
|
||||
|
||||
@@ -26,6 +26,7 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/p9"
|
||||
"gvisor.dev/gvisor/pkg/safemem"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fs/fsutil"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fsmetric"
|
||||
"gvisor.dev/gvisor/pkg/sentry/memmap"
|
||||
"gvisor.dev/gvisor/pkg/sentry/pgalloc"
|
||||
"gvisor.dev/gvisor/pkg/sentry/usage"
|
||||
@@ -48,6 +49,25 @@ type regularFileFD struct {
|
||||
off int64
|
||||
}
|
||||
|
||||
func newRegularFileFD(mnt *vfs.Mount, d *dentry, flags uint32) (*regularFileFD, error) {
|
||||
fd := ®ularFileFD{}
|
||||
fd.LockFD.Init(&d.locks)
|
||||
if err := fd.vfsfd.Init(fd, flags, mnt, &d.vfsd, &vfs.FileDescriptionOptions{
|
||||
AllowDirectIO: true,
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if fd.vfsfd.IsWritable() && (atomic.LoadUint32(&d.mode)&0111 != 0) {
|
||||
fsmetric.GoferOpensWX.Increment()
|
||||
}
|
||||
if atomic.LoadInt32(&d.mmapFD) >= 0 {
|
||||
fsmetric.GoferOpensHost.Increment()
|
||||
} else {
|
||||
fsmetric.GoferOpens9P.Increment()
|
||||
}
|
||||
return fd, nil
|
||||
}
|
||||
|
||||
// Release implements vfs.FileDescriptionImpl.Release.
|
||||
func (fd *regularFileFD) Release(context.Context) {
|
||||
}
|
||||
@@ -89,6 +109,18 @@ func (fd *regularFileFD) Allocate(ctx context.Context, mode, offset, length uint
|
||||
|
||||
// PRead implements vfs.FileDescriptionImpl.PRead.
|
||||
func (fd *regularFileFD) PRead(ctx context.Context, dst usermem.IOSequence, offset int64, opts vfs.ReadOptions) (int64, error) {
|
||||
start := fsmetric.StartReadWait()
|
||||
d := fd.dentry()
|
||||
defer func() {
|
||||
if atomic.LoadInt32(&d.readFD) >= 0 {
|
||||
fsmetric.GoferReadsHost.Increment()
|
||||
fsmetric.FinishReadWait(fsmetric.GoferReadWaitHost, start)
|
||||
} else {
|
||||
fsmetric.GoferReads9P.Increment()
|
||||
fsmetric.FinishReadWait(fsmetric.GoferReadWait9P, start)
|
||||
}
|
||||
}()
|
||||
|
||||
if offset < 0 {
|
||||
return 0, syserror.EINVAL
|
||||
}
|
||||
@@ -102,7 +134,6 @@ func (fd *regularFileFD) PRead(ctx context.Context, dst usermem.IOSequence, offs
|
||||
|
||||
// Check for reading at EOF before calling into MM (but not under
|
||||
// InteropModeShared, which makes d.size unreliable).
|
||||
d := fd.dentry()
|
||||
if d.cachedMetadataAuthoritative() && uint64(offset) >= atomic.LoadUint64(&d.size) {
|
||||
return 0, io.EOF
|
||||
}
|
||||
@@ -647,10 +678,7 @@ func (fd *regularFileFD) ConfigureMMap(ctx context.Context, opts *memmap.MMapOpt
|
||||
// Whether or not we have a host FD, we're not allowed to use it.
|
||||
return syserror.ENODEV
|
||||
}
|
||||
d.handleMu.RLock()
|
||||
haveFD := d.mmapFD >= 0
|
||||
d.handleMu.RUnlock()
|
||||
if !haveFD {
|
||||
if atomic.LoadInt32(&d.mmapFD) < 0 {
|
||||
return syserror.ENODEV
|
||||
}
|
||||
default:
|
||||
@@ -668,10 +696,7 @@ func (d *dentry) mayCachePages() bool {
|
||||
if d.fs.opts.forcePageCache {
|
||||
return true
|
||||
}
|
||||
d.handleMu.RLock()
|
||||
haveFD := d.mmapFD >= 0
|
||||
d.handleMu.RUnlock()
|
||||
return haveFD
|
||||
return atomic.LoadInt32(&d.mmapFD) >= 0
|
||||
}
|
||||
|
||||
// AddMapping implements memmap.Mappable.AddMapping.
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/fdnotifier"
|
||||
"gvisor.dev/gvisor/pkg/p9"
|
||||
"gvisor.dev/gvisor/pkg/safemem"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fsmetric"
|
||||
"gvisor.dev/gvisor/pkg/sentry/vfs"
|
||||
"gvisor.dev/gvisor/pkg/sync"
|
||||
"gvisor.dev/gvisor/pkg/syserror"
|
||||
@@ -70,7 +71,7 @@ type specialFileFD struct {
|
||||
buf []byte
|
||||
}
|
||||
|
||||
func newSpecialFileFD(h handle, mnt *vfs.Mount, d *dentry, locks *vfs.FileLocks, flags uint32) (*specialFileFD, error) {
|
||||
func newSpecialFileFD(h handle, mnt *vfs.Mount, d *dentry, flags uint32) (*specialFileFD, error) {
|
||||
ftype := d.fileType()
|
||||
seekable := ftype == linux.S_IFREG || ftype == linux.S_IFCHR || ftype == linux.S_IFBLK
|
||||
haveQueue := (ftype == linux.S_IFIFO || ftype == linux.S_IFSOCK) && h.fd >= 0
|
||||
@@ -80,7 +81,7 @@ func newSpecialFileFD(h handle, mnt *vfs.Mount, d *dentry, locks *vfs.FileLocks,
|
||||
seekable: seekable,
|
||||
haveQueue: haveQueue,
|
||||
}
|
||||
fd.LockFD.Init(locks)
|
||||
fd.LockFD.Init(&d.locks)
|
||||
if haveQueue {
|
||||
if err := fdnotifier.AddFD(h.fd, &fd.queue); err != nil {
|
||||
return nil, err
|
||||
@@ -98,6 +99,14 @@ func newSpecialFileFD(h handle, mnt *vfs.Mount, d *dentry, locks *vfs.FileLocks,
|
||||
d.fs.syncMu.Lock()
|
||||
d.fs.specialFileFDs[fd] = struct{}{}
|
||||
d.fs.syncMu.Unlock()
|
||||
if fd.vfsfd.IsWritable() && (atomic.LoadUint32(&d.mode)&0111 != 0) {
|
||||
fsmetric.GoferOpensWX.Increment()
|
||||
}
|
||||
if h.fd >= 0 {
|
||||
fsmetric.GoferOpensHost.Increment()
|
||||
} else {
|
||||
fsmetric.GoferOpens9P.Increment()
|
||||
}
|
||||
return fd, nil
|
||||
}
|
||||
|
||||
@@ -161,6 +170,17 @@ func (fd *specialFileFD) Allocate(ctx context.Context, mode, offset, length uint
|
||||
|
||||
// PRead implements vfs.FileDescriptionImpl.PRead.
|
||||
func (fd *specialFileFD) PRead(ctx context.Context, dst usermem.IOSequence, offset int64, opts vfs.ReadOptions) (int64, error) {
|
||||
start := fsmetric.StartReadWait()
|
||||
defer func() {
|
||||
if fd.handle.fd >= 0 {
|
||||
fsmetric.GoferReadsHost.Increment()
|
||||
fsmetric.FinishReadWait(fsmetric.GoferReadWaitHost, start)
|
||||
} else {
|
||||
fsmetric.GoferReads9P.Increment()
|
||||
fsmetric.FinishReadWait(fsmetric.GoferReadWait9P, start)
|
||||
}
|
||||
}()
|
||||
|
||||
if fd.seekable && offset < 0 {
|
||||
return 0, syserror.EINVAL
|
||||
}
|
||||
|
||||
@@ -67,6 +67,7 @@ go_library(
|
||||
"//pkg/sentry/fs",
|
||||
"//pkg/sentry/fs/fsutil",
|
||||
"//pkg/sentry/fs/lock",
|
||||
"//pkg/sentry/fsmetric",
|
||||
"//pkg/sentry/kernel/auth",
|
||||
"//pkg/sentry/kernel/pipe",
|
||||
"//pkg/sentry/kernel/time",
|
||||
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
"gvisor.dev/gvisor/pkg/context"
|
||||
"gvisor.dev/gvisor/pkg/fspath"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fsmetric"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
|
||||
"gvisor.dev/gvisor/pkg/sentry/socket/unix/transport"
|
||||
"gvisor.dev/gvisor/pkg/sentry/vfs"
|
||||
@@ -439,6 +440,11 @@ func (d *dentry) open(ctx context.Context, rp *vfs.ResolvingPath, opts *vfs.Open
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if fd.vfsfd.IsWritable() {
|
||||
fsmetric.TmpfsOpensW.Increment()
|
||||
} else if fd.vfsfd.IsReadable() {
|
||||
fsmetric.TmpfsOpensRO.Increment()
|
||||
}
|
||||
return &fd.vfsfd, nil
|
||||
case *directory:
|
||||
// Can't open directories writably.
|
||||
|
||||
@@ -25,6 +25,7 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/safemem"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fs"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fs/fsutil"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fsmetric"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
|
||||
"gvisor.dev/gvisor/pkg/sentry/memmap"
|
||||
"gvisor.dev/gvisor/pkg/sentry/pgalloc"
|
||||
@@ -359,6 +360,10 @@ func (fd *regularFileFD) Allocate(ctx context.Context, mode, offset, length uint
|
||||
|
||||
// PRead implements vfs.FileDescriptionImpl.PRead.
|
||||
func (fd *regularFileFD) PRead(ctx context.Context, dst usermem.IOSequence, offset int64, opts vfs.ReadOptions) (int64, error) {
|
||||
start := fsmetric.StartReadWait()
|
||||
defer fsmetric.FinishReadWait(fsmetric.TmpfsReadWait, start)
|
||||
fsmetric.TmpfsReads.Increment()
|
||||
|
||||
if offset < 0 {
|
||||
return 0, syserror.EINVAL
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
load("//tools:defs.bzl", "go_library")
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
go_library(
|
||||
name = "fsmetric",
|
||||
srcs = ["fsmetric.go"],
|
||||
visibility = ["//pkg/sentry:internal"],
|
||||
deps = ["//pkg/metric"],
|
||||
)
|
||||
@@ -0,0 +1,83 @@
|
||||
// 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.
|
||||
|
||||
// Package fsmetric defines filesystem metrics that are used by both VFS1 and
|
||||
// VFS2.
|
||||
//
|
||||
// TODO(gvisor.dev/issue/1624): Once VFS1 is deleted, inline these metrics into
|
||||
// VFS2.
|
||||
package fsmetric
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/metric"
|
||||
)
|
||||
|
||||
// RecordWaitTime enables the ReadWait, GoferReadWait9P, GoferReadWaitHost, and
|
||||
// TmpfsReadWait metrics. Enabling this comes at a CPU cost due to performing
|
||||
// three clock reads per read call.
|
||||
//
|
||||
// Note that this is only performed in the direct read path, and may not be
|
||||
// consistently applied for other forms of reads, such as splice.
|
||||
var RecordWaitTime = false
|
||||
|
||||
// Metrics that apply to all filesystems.
|
||||
var (
|
||||
Opens = metric.MustCreateNewUint64Metric("/fs/opens", false /* sync */, "Number of file opens.")
|
||||
Reads = metric.MustCreateNewUint64Metric("/fs/reads", false /* sync */, "Number of file reads.")
|
||||
ReadWait = metric.MustCreateNewUint64NanosecondsMetric("/fs/read_wait", false /* sync */, "Time waiting on file reads, in nanoseconds.")
|
||||
)
|
||||
|
||||
// Metrics that only apply to fs/gofer and fsimpl/gofer.
|
||||
var (
|
||||
GoferOpensWX = metric.MustCreateNewUint64Metric("/gofer/opened_write_execute_file", true /* sync */, "Number of times a executable file was opened writably from a gofer.")
|
||||
GoferOpens9P = metric.MustCreateNewUint64Metric("/gofer/opens_9p", false /* sync */, "Number of times a file was opened from a gofer and did not have a host file descriptor.")
|
||||
GoferOpensHost = metric.MustCreateNewUint64Metric("/gofer/opens_host", false /* sync */, "Number of times a file was opened from a gofer and did have a host file descriptor.")
|
||||
GoferReads9P = metric.MustCreateNewUint64Metric("/gofer/reads_9p", false /* sync */, "Number of 9P file reads from a gofer.")
|
||||
GoferReadWait9P = metric.MustCreateNewUint64NanosecondsMetric("/gofer/read_wait_9p", false /* sync */, "Time waiting on 9P file reads from a gofer, in nanoseconds.")
|
||||
GoferReadsHost = metric.MustCreateNewUint64Metric("/gofer/reads_host", false /* sync */, "Number of host file reads from a gofer.")
|
||||
GoferReadWaitHost = metric.MustCreateNewUint64NanosecondsMetric("/gofer/read_wait_host", false /* sync */, "Time waiting on host file reads from a gofer, in nanoseconds.")
|
||||
)
|
||||
|
||||
// Metrics that only apply to fs/tmpfs and fsimpl/tmpfs.
|
||||
var (
|
||||
TmpfsOpensRO = metric.MustCreateNewUint64Metric("/in_memory_file/opens_ro", false /* sync */, "Number of times an in-memory file was opened in read-only mode.")
|
||||
TmpfsOpensW = metric.MustCreateNewUint64Metric("/in_memory_file/opens_w", false /* sync */, "Number of times an in-memory file was opened in write mode.")
|
||||
TmpfsReads = metric.MustCreateNewUint64Metric("/in_memory_file/reads", false /* sync */, "Number of in-memory file reads.")
|
||||
TmpfsReadWait = metric.MustCreateNewUint64NanosecondsMetric("/in_memory_file/read_wait", false /* sync */, "Time waiting on in-memory file reads, in nanoseconds.")
|
||||
)
|
||||
|
||||
// StartReadWait indicates the beginning of a file read.
|
||||
func StartReadWait() time.Time {
|
||||
if !RecordWaitTime {
|
||||
return time.Time{}
|
||||
}
|
||||
return time.Now()
|
||||
}
|
||||
|
||||
// FinishReadWait indicates the end of a file read whose time is accounted by
|
||||
// m. start must be the value returned by the corresponding call to
|
||||
// StartReadWait.
|
||||
//
|
||||
// FinishReadWait is marked nosplit for performance since it's often called
|
||||
// from defer statements, which prevents it from being inlined
|
||||
// (https://github.com/golang/go/issues/38471).
|
||||
//go:nosplit
|
||||
func FinishReadWait(m *metric.Uint64Metric, start time.Time) {
|
||||
if !RecordWaitTime {
|
||||
return
|
||||
}
|
||||
m.IncrementBy(uint64(time.Since(start).Nanoseconds()))
|
||||
}
|
||||
@@ -105,6 +105,7 @@ go_library(
|
||||
"//pkg/sentry/arch",
|
||||
"//pkg/sentry/fs",
|
||||
"//pkg/sentry/fs/lock",
|
||||
"//pkg/sentry/fsmetric",
|
||||
"//pkg/sentry/kernel/auth",
|
||||
"//pkg/sentry/kernel/time",
|
||||
"//pkg/sentry/limits",
|
||||
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/context"
|
||||
"gvisor.dev/gvisor/pkg/sentry/arch"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fs/lock"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fsmetric"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
|
||||
"gvisor.dev/gvisor/pkg/sentry/memmap"
|
||||
"gvisor.dev/gvisor/pkg/sync"
|
||||
@@ -583,7 +584,11 @@ func (fd *FileDescription) PRead(ctx context.Context, dst usermem.IOSequence, of
|
||||
if !fd.readable {
|
||||
return 0, syserror.EBADF
|
||||
}
|
||||
return fd.impl.PRead(ctx, dst, offset, opts)
|
||||
start := fsmetric.StartReadWait()
|
||||
n, err := fd.impl.PRead(ctx, dst, offset, opts)
|
||||
fsmetric.Reads.Increment()
|
||||
fsmetric.FinishReadWait(fsmetric.ReadWait, start)
|
||||
return n, err
|
||||
}
|
||||
|
||||
// Read is similar to PRead, but does not specify an offset.
|
||||
@@ -591,7 +596,11 @@ func (fd *FileDescription) Read(ctx context.Context, dst usermem.IOSequence, opt
|
||||
if !fd.readable {
|
||||
return 0, syserror.EBADF
|
||||
}
|
||||
return fd.impl.Read(ctx, dst, opts)
|
||||
start := fsmetric.StartReadWait()
|
||||
n, err := fd.impl.Read(ctx, dst, opts)
|
||||
fsmetric.Reads.Increment()
|
||||
fsmetric.FinishReadWait(fsmetric.ReadWait, start)
|
||||
return n, err
|
||||
}
|
||||
|
||||
// PWrite writes src to the file represented by fd, starting at the given
|
||||
|
||||
@@ -41,6 +41,7 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
"gvisor.dev/gvisor/pkg/context"
|
||||
"gvisor.dev/gvisor/pkg/fspath"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fsmetric"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
|
||||
"gvisor.dev/gvisor/pkg/sentry/socket/unix/transport"
|
||||
"gvisor.dev/gvisor/pkg/sync"
|
||||
@@ -381,6 +382,8 @@ func (vfs *VirtualFilesystem) MknodAt(ctx context.Context, creds *auth.Credentia
|
||||
// OpenAt returns a FileDescription providing access to the file at the given
|
||||
// path. A reference is taken on the returned FileDescription.
|
||||
func (vfs *VirtualFilesystem) OpenAt(ctx context.Context, creds *auth.Credentials, pop *PathOperation, opts *OpenOptions) (*FileDescription, error) {
|
||||
fsmetric.Opens.Increment()
|
||||
|
||||
// Remove:
|
||||
//
|
||||
// - O_CLOEXEC, which affects file descriptors and therefore must be
|
||||
|
||||
Reference in New Issue
Block a user