2019-07-18 22:09:14 +00:00
|
|
|
// Copyright 2019 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 benchmark_test
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"runtime"
|
|
|
|
"strings"
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
"gvisor.dev/gvisor/pkg/abi/linux"
|
2019-12-23 21:17:29 +00:00
|
|
|
"gvisor.dev/gvisor/pkg/fspath"
|
2019-11-25 23:20:25 +00:00
|
|
|
"gvisor.dev/gvisor/pkg/refs"
|
2019-07-18 22:09:14 +00:00
|
|
|
"gvisor.dev/gvisor/pkg/sentry/context"
|
|
|
|
"gvisor.dev/gvisor/pkg/sentry/context/contexttest"
|
|
|
|
"gvisor.dev/gvisor/pkg/sentry/fs"
|
|
|
|
_ "gvisor.dev/gvisor/pkg/sentry/fs/tmpfs"
|
|
|
|
"gvisor.dev/gvisor/pkg/sentry/fsimpl/memfs"
|
|
|
|
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
|
|
|
|
"gvisor.dev/gvisor/pkg/sentry/vfs"
|
|
|
|
"gvisor.dev/gvisor/pkg/syserror"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Differences from stat_benchmark:
|
|
|
|
//
|
|
|
|
// - Syscall interception, CopyInPath, copyOutStat, and overlayfs overheads are
|
|
|
|
// not included.
|
|
|
|
//
|
|
|
|
// - *MountStat benchmarks use a tmpfs root mount and a tmpfs submount at /tmp.
|
|
|
|
// Non-MountStat benchmarks use a tmpfs root mount and no submounts.
|
|
|
|
// stat_benchmark uses a varying root mount, a tmpfs submount at /tmp, and a
|
|
|
|
// subdirectory /tmp/<top_dir> (assuming TEST_TMPDIR == "/tmp"). Thus
|
|
|
|
// stat_benchmark at depth 1 does a comparable amount of work to *MountStat
|
|
|
|
// benchmarks at depth 2, and non-MountStat benchmarks at depth 3.
|
|
|
|
var depths = []int{1, 2, 3, 8, 64, 100}
|
|
|
|
|
|
|
|
const (
|
|
|
|
mountPointName = "tmp"
|
|
|
|
filename = "gvisor_test_temp_0_1557494568"
|
|
|
|
)
|
|
|
|
|
|
|
|
// This is copied from syscalls/linux/sys_file.go, with the dependency on
|
|
|
|
// kernel.Task stripped out.
|
|
|
|
func fileOpOn(ctx context.Context, mntns *fs.MountNamespace, root, wd *fs.Dirent, dirFD int32, path string, resolve bool, fn func(root *fs.Dirent, d *fs.Dirent) error) error {
|
|
|
|
var (
|
|
|
|
d *fs.Dirent // The file.
|
|
|
|
rel *fs.Dirent // The relative directory for search (if required.)
|
|
|
|
err error
|
|
|
|
)
|
|
|
|
|
|
|
|
// Extract the working directory (maybe).
|
|
|
|
if len(path) > 0 && path[0] == '/' {
|
|
|
|
// Absolute path; rel can be nil.
|
|
|
|
} else if dirFD == linux.AT_FDCWD {
|
|
|
|
// Need to reference the working directory.
|
|
|
|
rel = wd
|
|
|
|
} else {
|
|
|
|
// Need to extract the given FD.
|
|
|
|
return syserror.EBADF
|
|
|
|
}
|
|
|
|
|
|
|
|
// Lookup the node.
|
|
|
|
remainingTraversals := uint(linux.MaxSymlinkTraversals)
|
|
|
|
if resolve {
|
|
|
|
d, err = mntns.FindInode(ctx, root, rel, path, &remainingTraversals)
|
|
|
|
} else {
|
|
|
|
d, err = mntns.FindLink(ctx, root, rel, path, &remainingTraversals)
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
err = fn(root, d)
|
|
|
|
d.DecRef()
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
func BenchmarkVFS1TmpfsStat(b *testing.B) {
|
|
|
|
for _, depth := range depths {
|
|
|
|
b.Run(fmt.Sprintf("%d", depth), func(b *testing.B) {
|
|
|
|
ctx := contexttest.Context(b)
|
|
|
|
|
|
|
|
// Create VFS.
|
|
|
|
tmpfsFS, ok := fs.FindFilesystem("tmpfs")
|
|
|
|
if !ok {
|
|
|
|
b.Fatalf("failed to find tmpfs filesystem type")
|
|
|
|
}
|
|
|
|
rootInode, err := tmpfsFS.Mount(ctx, "tmpfs", fs.MountSourceFlags{}, "", nil)
|
|
|
|
if err != nil {
|
|
|
|
b.Fatalf("failed to create tmpfs root mount: %v", err)
|
|
|
|
}
|
|
|
|
mntns, err := fs.NewMountNamespace(ctx, rootInode)
|
|
|
|
if err != nil {
|
|
|
|
b.Fatalf("failed to create mount namespace: %v", err)
|
|
|
|
}
|
|
|
|
defer mntns.DecRef()
|
|
|
|
|
|
|
|
var filePathBuilder strings.Builder
|
|
|
|
filePathBuilder.WriteByte('/')
|
|
|
|
|
|
|
|
// Create nested directories with given depth.
|
|
|
|
root := mntns.Root()
|
|
|
|
defer root.DecRef()
|
|
|
|
d := root
|
|
|
|
d.IncRef()
|
|
|
|
defer d.DecRef()
|
|
|
|
for i := depth; i > 0; i-- {
|
|
|
|
name := fmt.Sprintf("%d", i)
|
|
|
|
if err := d.Inode.CreateDirectory(ctx, d, name, fs.FilePermsFromMode(0755)); err != nil {
|
|
|
|
b.Fatalf("failed to create directory %q: %v", name, err)
|
|
|
|
}
|
|
|
|
next, err := d.Walk(ctx, root, name)
|
|
|
|
if err != nil {
|
|
|
|
b.Fatalf("failed to walk to directory %q: %v", name, err)
|
|
|
|
}
|
|
|
|
d.DecRef()
|
|
|
|
d = next
|
|
|
|
filePathBuilder.WriteString(name)
|
|
|
|
filePathBuilder.WriteByte('/')
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create the file that will be stat'd.
|
|
|
|
file, err := d.Inode.Create(ctx, d, filename, fs.FileFlags{Read: true, Write: true}, fs.FilePermsFromMode(0644))
|
|
|
|
if err != nil {
|
|
|
|
b.Fatalf("failed to create file %q: %v", filename, err)
|
|
|
|
}
|
|
|
|
file.DecRef()
|
|
|
|
filePathBuilder.WriteString(filename)
|
|
|
|
filePath := filePathBuilder.String()
|
|
|
|
|
|
|
|
dirPath := false
|
|
|
|
runtime.GC()
|
|
|
|
b.ResetTimer()
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
|
|
err := fileOpOn(ctx, mntns, root, root, linux.AT_FDCWD, filePath, true /* resolve */, func(root *fs.Dirent, d *fs.Dirent) error {
|
|
|
|
if dirPath && !fs.IsDir(d.Inode.StableAttr) {
|
|
|
|
return syserror.ENOTDIR
|
|
|
|
}
|
|
|
|
uattr, err := d.Inode.UnstableAttr(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
// Sanity check.
|
|
|
|
if uattr.Perms.User.Execute {
|
|
|
|
b.Fatalf("got wrong permissions (%0o)", uattr.Perms.LinuxMode())
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
b.Fatalf("stat(%q) failed: %v", filePath, err)
|
|
|
|
}
|
|
|
|
}
|
2019-11-25 23:20:25 +00:00
|
|
|
// Don't include deferred cleanup in benchmark time.
|
|
|
|
b.StopTimer()
|
2019-07-18 22:09:14 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func BenchmarkVFS2MemfsStat(b *testing.B) {
|
|
|
|
for _, depth := range depths {
|
|
|
|
b.Run(fmt.Sprintf("%d", depth), func(b *testing.B) {
|
|
|
|
ctx := contexttest.Context(b)
|
|
|
|
creds := auth.CredentialsFromContext(ctx)
|
|
|
|
|
|
|
|
// Create VFS.
|
|
|
|
vfsObj := vfs.New()
|
2019-12-27 08:12:14 +00:00
|
|
|
vfsObj.MustRegisterFilesystemType("memfs", memfs.FilesystemType{}, &vfs.RegisterFilesystemTypeOptions{
|
|
|
|
AllowUserMount: true,
|
|
|
|
})
|
Minor VFS2 interface changes.
- Remove the Filesystem argument from DentryImpl.*Ref(); in general DentryImpls
that need the Filesystem for reference counting will probably also need it
for other interface methods that don't plumb Filesystem, so it's easier to
just store a pointer to the filesystem in the DentryImpl.
- Add a pointer to the VirtualFilesystem to Filesystem, which is needed by the
gofer client to disown dentries for cache eviction triggered by dentry
reference count changes.
- Rename FilesystemType.NewFilesystem to GetFilesystem; in some cases (e.g.
sysfs, cgroupfs) it's much cleaner for there to be only one Filesystem that
is used by all mounts, and in at least one case (devtmpfs) it's visibly
incorrect not to do so, so NewFilesystem doesn't always actually create and
return a *new* Filesystem.
- Require callers of FileDescription.Init() to increment Mount/Dentry
references. This is because the gofer client may, in the OpenAt() path, take
a reference on a dentry with 0 references, which is safe due to
synchronization that is outside the scope of this CL, and it would be safer
to still have its implementation of DentryImpl.IncRef() check for an
increment for 0 references in other cases.
- Add FileDescription.TryIncRef. This is used by the gofer client to take
references on "special file descriptions" (FDs for files such as pipes,
sockets, and devices), which use per-FD handles (fids) instead of
dentry-shared handles, for sync() and syncfs().
PiperOrigin-RevId: 282473364
2019-11-26 02:09:15 +00:00
|
|
|
mntns, err := vfsObj.NewMountNamespace(ctx, creds, "", "memfs", &vfs.GetFilesystemOptions{})
|
2019-07-18 22:09:14 +00:00
|
|
|
if err != nil {
|
|
|
|
b.Fatalf("failed to create tmpfs root mount: %v", err)
|
|
|
|
}
|
2019-11-25 23:20:25 +00:00
|
|
|
defer mntns.DecRef(vfsObj)
|
2019-07-18 22:09:14 +00:00
|
|
|
|
|
|
|
var filePathBuilder strings.Builder
|
|
|
|
filePathBuilder.WriteByte('/')
|
|
|
|
|
|
|
|
// Create nested directories with given depth.
|
|
|
|
root := mntns.Root()
|
|
|
|
defer root.DecRef()
|
|
|
|
vd := root
|
|
|
|
vd.IncRef()
|
|
|
|
for i := depth; i > 0; i-- {
|
|
|
|
name := fmt.Sprintf("%d", i)
|
|
|
|
pop := vfs.PathOperation{
|
2019-12-23 21:17:29 +00:00
|
|
|
Root: root,
|
|
|
|
Start: vd,
|
|
|
|
Path: fspath.Parse(name),
|
2019-07-18 22:09:14 +00:00
|
|
|
}
|
|
|
|
if err := vfsObj.MkdirAt(ctx, creds, &pop, &vfs.MkdirOptions{
|
|
|
|
Mode: 0755,
|
|
|
|
}); err != nil {
|
|
|
|
b.Fatalf("failed to create directory %q: %v", name, err)
|
|
|
|
}
|
|
|
|
nextVD, err := vfsObj.GetDentryAt(ctx, creds, &pop, &vfs.GetDentryOptions{})
|
|
|
|
if err != nil {
|
|
|
|
b.Fatalf("failed to walk to directory %q: %v", name, err)
|
|
|
|
}
|
|
|
|
vd.DecRef()
|
|
|
|
vd = nextVD
|
|
|
|
filePathBuilder.WriteString(name)
|
|
|
|
filePathBuilder.WriteByte('/')
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create the file that will be stat'd.
|
|
|
|
fd, err := vfsObj.OpenAt(ctx, creds, &vfs.PathOperation{
|
|
|
|
Root: root,
|
|
|
|
Start: vd,
|
2019-12-23 21:17:29 +00:00
|
|
|
Path: fspath.Parse(filename),
|
2019-07-18 22:09:14 +00:00
|
|
|
FollowFinalSymlink: true,
|
|
|
|
}, &vfs.OpenOptions{
|
|
|
|
Flags: linux.O_RDWR | linux.O_CREAT | linux.O_EXCL,
|
|
|
|
Mode: 0644,
|
|
|
|
})
|
2019-11-25 23:20:25 +00:00
|
|
|
vd.DecRef()
|
|
|
|
vd = vfs.VirtualDentry{}
|
2019-07-18 22:09:14 +00:00
|
|
|
if err != nil {
|
|
|
|
b.Fatalf("failed to create file %q: %v", filename, err)
|
|
|
|
}
|
|
|
|
defer fd.DecRef()
|
|
|
|
filePathBuilder.WriteString(filename)
|
|
|
|
filePath := filePathBuilder.String()
|
|
|
|
|
|
|
|
runtime.GC()
|
|
|
|
b.ResetTimer()
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
|
|
stat, err := vfsObj.StatAt(ctx, creds, &vfs.PathOperation{
|
|
|
|
Root: root,
|
|
|
|
Start: root,
|
2019-12-23 21:17:29 +00:00
|
|
|
Path: fspath.Parse(filePath),
|
2019-07-18 22:09:14 +00:00
|
|
|
FollowFinalSymlink: true,
|
|
|
|
}, &vfs.StatOptions{})
|
|
|
|
if err != nil {
|
|
|
|
b.Fatalf("stat(%q) failed: %v", filePath, err)
|
|
|
|
}
|
|
|
|
// Sanity check.
|
|
|
|
if stat.Mode&^linux.S_IFMT != 0644 {
|
|
|
|
b.Fatalf("got wrong permissions (%0o)", stat.Mode)
|
|
|
|
}
|
|
|
|
}
|
2019-11-25 23:20:25 +00:00
|
|
|
// Don't include deferred cleanup in benchmark time.
|
|
|
|
b.StopTimer()
|
2019-07-18 22:09:14 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func BenchmarkVFS1TmpfsMountStat(b *testing.B) {
|
|
|
|
for _, depth := range depths {
|
|
|
|
b.Run(fmt.Sprintf("%d", depth), func(b *testing.B) {
|
|
|
|
ctx := contexttest.Context(b)
|
|
|
|
|
|
|
|
// Create VFS.
|
|
|
|
tmpfsFS, ok := fs.FindFilesystem("tmpfs")
|
|
|
|
if !ok {
|
|
|
|
b.Fatalf("failed to find tmpfs filesystem type")
|
|
|
|
}
|
|
|
|
rootInode, err := tmpfsFS.Mount(ctx, "tmpfs", fs.MountSourceFlags{}, "", nil)
|
|
|
|
if err != nil {
|
|
|
|
b.Fatalf("failed to create tmpfs root mount: %v", err)
|
|
|
|
}
|
|
|
|
mntns, err := fs.NewMountNamespace(ctx, rootInode)
|
|
|
|
if err != nil {
|
|
|
|
b.Fatalf("failed to create mount namespace: %v", err)
|
|
|
|
}
|
|
|
|
defer mntns.DecRef()
|
|
|
|
|
|
|
|
var filePathBuilder strings.Builder
|
|
|
|
filePathBuilder.WriteByte('/')
|
|
|
|
|
|
|
|
// Create and mount the submount.
|
|
|
|
root := mntns.Root()
|
|
|
|
defer root.DecRef()
|
|
|
|
if err := root.Inode.CreateDirectory(ctx, root, mountPointName, fs.FilePermsFromMode(0755)); err != nil {
|
|
|
|
b.Fatalf("failed to create mount point: %v", err)
|
|
|
|
}
|
|
|
|
mountPoint, err := root.Walk(ctx, root, mountPointName)
|
|
|
|
if err != nil {
|
|
|
|
b.Fatalf("failed to walk to mount point: %v", err)
|
|
|
|
}
|
|
|
|
defer mountPoint.DecRef()
|
|
|
|
submountInode, err := tmpfsFS.Mount(ctx, "tmpfs", fs.MountSourceFlags{}, "", nil)
|
|
|
|
if err != nil {
|
|
|
|
b.Fatalf("failed to create tmpfs submount: %v", err)
|
|
|
|
}
|
|
|
|
if err := mntns.Mount(ctx, mountPoint, submountInode); err != nil {
|
|
|
|
b.Fatalf("failed to mount tmpfs submount: %v", err)
|
|
|
|
}
|
|
|
|
filePathBuilder.WriteString(mountPointName)
|
|
|
|
filePathBuilder.WriteByte('/')
|
|
|
|
|
|
|
|
// Create nested directories with given depth.
|
|
|
|
d, err := root.Walk(ctx, root, mountPointName)
|
|
|
|
if err != nil {
|
|
|
|
b.Fatalf("failed to walk to mount root: %v", err)
|
|
|
|
}
|
|
|
|
defer d.DecRef()
|
|
|
|
for i := depth; i > 0; i-- {
|
|
|
|
name := fmt.Sprintf("%d", i)
|
|
|
|
if err := d.Inode.CreateDirectory(ctx, d, name, fs.FilePermsFromMode(0755)); err != nil {
|
|
|
|
b.Fatalf("failed to create directory %q: %v", name, err)
|
|
|
|
}
|
|
|
|
next, err := d.Walk(ctx, root, name)
|
|
|
|
if err != nil {
|
|
|
|
b.Fatalf("failed to walk to directory %q: %v", name, err)
|
|
|
|
}
|
|
|
|
d.DecRef()
|
|
|
|
d = next
|
|
|
|
filePathBuilder.WriteString(name)
|
|
|
|
filePathBuilder.WriteByte('/')
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create the file that will be stat'd.
|
|
|
|
file, err := d.Inode.Create(ctx, d, filename, fs.FileFlags{Read: true, Write: true}, fs.FilePermsFromMode(0644))
|
|
|
|
if err != nil {
|
|
|
|
b.Fatalf("failed to create file %q: %v", filename, err)
|
|
|
|
}
|
|
|
|
file.DecRef()
|
|
|
|
filePathBuilder.WriteString(filename)
|
|
|
|
filePath := filePathBuilder.String()
|
|
|
|
|
|
|
|
dirPath := false
|
|
|
|
runtime.GC()
|
|
|
|
b.ResetTimer()
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
|
|
err := fileOpOn(ctx, mntns, root, root, linux.AT_FDCWD, filePath, true /* resolve */, func(root *fs.Dirent, d *fs.Dirent) error {
|
|
|
|
if dirPath && !fs.IsDir(d.Inode.StableAttr) {
|
|
|
|
return syserror.ENOTDIR
|
|
|
|
}
|
|
|
|
uattr, err := d.Inode.UnstableAttr(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
// Sanity check.
|
|
|
|
if uattr.Perms.User.Execute {
|
|
|
|
b.Fatalf("got wrong permissions (%0o)", uattr.Perms.LinuxMode())
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
b.Fatalf("stat(%q) failed: %v", filePath, err)
|
|
|
|
}
|
|
|
|
}
|
2019-11-25 23:20:25 +00:00
|
|
|
// Don't include deferred cleanup in benchmark time.
|
|
|
|
b.StopTimer()
|
2019-07-18 22:09:14 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func BenchmarkVFS2MemfsMountStat(b *testing.B) {
|
|
|
|
for _, depth := range depths {
|
|
|
|
b.Run(fmt.Sprintf("%d", depth), func(b *testing.B) {
|
|
|
|
ctx := contexttest.Context(b)
|
|
|
|
creds := auth.CredentialsFromContext(ctx)
|
|
|
|
|
|
|
|
// Create VFS.
|
|
|
|
vfsObj := vfs.New()
|
2019-12-27 08:12:14 +00:00
|
|
|
vfsObj.MustRegisterFilesystemType("memfs", memfs.FilesystemType{}, &vfs.RegisterFilesystemTypeOptions{
|
|
|
|
AllowUserMount: true,
|
|
|
|
})
|
Minor VFS2 interface changes.
- Remove the Filesystem argument from DentryImpl.*Ref(); in general DentryImpls
that need the Filesystem for reference counting will probably also need it
for other interface methods that don't plumb Filesystem, so it's easier to
just store a pointer to the filesystem in the DentryImpl.
- Add a pointer to the VirtualFilesystem to Filesystem, which is needed by the
gofer client to disown dentries for cache eviction triggered by dentry
reference count changes.
- Rename FilesystemType.NewFilesystem to GetFilesystem; in some cases (e.g.
sysfs, cgroupfs) it's much cleaner for there to be only one Filesystem that
is used by all mounts, and in at least one case (devtmpfs) it's visibly
incorrect not to do so, so NewFilesystem doesn't always actually create and
return a *new* Filesystem.
- Require callers of FileDescription.Init() to increment Mount/Dentry
references. This is because the gofer client may, in the OpenAt() path, take
a reference on a dentry with 0 references, which is safe due to
synchronization that is outside the scope of this CL, and it would be safer
to still have its implementation of DentryImpl.IncRef() check for an
increment for 0 references in other cases.
- Add FileDescription.TryIncRef. This is used by the gofer client to take
references on "special file descriptions" (FDs for files such as pipes,
sockets, and devices), which use per-FD handles (fids) instead of
dentry-shared handles, for sync() and syncfs().
PiperOrigin-RevId: 282473364
2019-11-26 02:09:15 +00:00
|
|
|
mntns, err := vfsObj.NewMountNamespace(ctx, creds, "", "memfs", &vfs.GetFilesystemOptions{})
|
2019-07-18 22:09:14 +00:00
|
|
|
if err != nil {
|
|
|
|
b.Fatalf("failed to create tmpfs root mount: %v", err)
|
|
|
|
}
|
2019-11-25 23:20:25 +00:00
|
|
|
defer mntns.DecRef(vfsObj)
|
2019-07-18 22:09:14 +00:00
|
|
|
|
|
|
|
var filePathBuilder strings.Builder
|
|
|
|
filePathBuilder.WriteByte('/')
|
|
|
|
|
|
|
|
// Create the mount point.
|
|
|
|
root := mntns.Root()
|
|
|
|
defer root.DecRef()
|
|
|
|
pop := vfs.PathOperation{
|
2019-12-23 21:17:29 +00:00
|
|
|
Root: root,
|
|
|
|
Start: root,
|
|
|
|
Path: fspath.Parse(mountPointName),
|
2019-07-18 22:09:14 +00:00
|
|
|
}
|
|
|
|
if err := vfsObj.MkdirAt(ctx, creds, &pop, &vfs.MkdirOptions{
|
|
|
|
Mode: 0755,
|
|
|
|
}); err != nil {
|
|
|
|
b.Fatalf("failed to create mount point: %v", err)
|
|
|
|
}
|
|
|
|
// Save the mount point for later use.
|
|
|
|
mountPoint, err := vfsObj.GetDentryAt(ctx, creds, &pop, &vfs.GetDentryOptions{})
|
|
|
|
if err != nil {
|
|
|
|
b.Fatalf("failed to walk to mount point: %v", err)
|
|
|
|
}
|
|
|
|
defer mountPoint.DecRef()
|
|
|
|
// Create and mount the submount.
|
2019-12-11 02:16:47 +00:00
|
|
|
if err := vfsObj.MountAt(ctx, creds, "", &pop, "memfs", &vfs.MountOptions{}); err != nil {
|
2019-07-18 22:09:14 +00:00
|
|
|
b.Fatalf("failed to mount tmpfs submount: %v", err)
|
|
|
|
}
|
|
|
|
filePathBuilder.WriteString(mountPointName)
|
|
|
|
filePathBuilder.WriteByte('/')
|
|
|
|
|
|
|
|
// Create nested directories with given depth.
|
|
|
|
vd, err := vfsObj.GetDentryAt(ctx, creds, &pop, &vfs.GetDentryOptions{})
|
|
|
|
if err != nil {
|
|
|
|
b.Fatalf("failed to walk to mount root: %v", err)
|
|
|
|
}
|
|
|
|
for i := depth; i > 0; i-- {
|
|
|
|
name := fmt.Sprintf("%d", i)
|
|
|
|
pop := vfs.PathOperation{
|
2019-12-23 21:17:29 +00:00
|
|
|
Root: root,
|
|
|
|
Start: vd,
|
|
|
|
Path: fspath.Parse(name),
|
2019-07-18 22:09:14 +00:00
|
|
|
}
|
|
|
|
if err := vfsObj.MkdirAt(ctx, creds, &pop, &vfs.MkdirOptions{
|
|
|
|
Mode: 0755,
|
|
|
|
}); err != nil {
|
|
|
|
b.Fatalf("failed to create directory %q: %v", name, err)
|
|
|
|
}
|
|
|
|
nextVD, err := vfsObj.GetDentryAt(ctx, creds, &pop, &vfs.GetDentryOptions{})
|
|
|
|
if err != nil {
|
|
|
|
b.Fatalf("failed to walk to directory %q: %v", name, err)
|
|
|
|
}
|
|
|
|
vd.DecRef()
|
|
|
|
vd = nextVD
|
|
|
|
filePathBuilder.WriteString(name)
|
|
|
|
filePathBuilder.WriteByte('/')
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify that we didn't create any directories under the mount
|
|
|
|
// point (i.e. they were all created on the submount).
|
|
|
|
firstDirName := fmt.Sprintf("%d", depth)
|
|
|
|
if child := mountPoint.Dentry().Child(firstDirName); child != nil {
|
|
|
|
b.Fatalf("created directory %q under root mount, not submount", firstDirName)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create the file that will be stat'd.
|
|
|
|
fd, err := vfsObj.OpenAt(ctx, creds, &vfs.PathOperation{
|
|
|
|
Root: root,
|
|
|
|
Start: vd,
|
2019-12-23 21:17:29 +00:00
|
|
|
Path: fspath.Parse(filename),
|
2019-07-18 22:09:14 +00:00
|
|
|
FollowFinalSymlink: true,
|
|
|
|
}, &vfs.OpenOptions{
|
|
|
|
Flags: linux.O_RDWR | linux.O_CREAT | linux.O_EXCL,
|
|
|
|
Mode: 0644,
|
|
|
|
})
|
2019-11-25 23:20:25 +00:00
|
|
|
vd.DecRef()
|
2019-07-18 22:09:14 +00:00
|
|
|
if err != nil {
|
|
|
|
b.Fatalf("failed to create file %q: %v", filename, err)
|
|
|
|
}
|
|
|
|
fd.DecRef()
|
|
|
|
filePathBuilder.WriteString(filename)
|
|
|
|
filePath := filePathBuilder.String()
|
|
|
|
|
|
|
|
runtime.GC()
|
|
|
|
b.ResetTimer()
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
|
|
stat, err := vfsObj.StatAt(ctx, creds, &vfs.PathOperation{
|
|
|
|
Root: root,
|
|
|
|
Start: root,
|
2019-12-23 21:17:29 +00:00
|
|
|
Path: fspath.Parse(filePath),
|
2019-07-18 22:09:14 +00:00
|
|
|
FollowFinalSymlink: true,
|
|
|
|
}, &vfs.StatOptions{})
|
|
|
|
if err != nil {
|
|
|
|
b.Fatalf("stat(%q) failed: %v", filePath, err)
|
|
|
|
}
|
|
|
|
// Sanity check.
|
|
|
|
if stat.Mode&^linux.S_IFMT != 0644 {
|
|
|
|
b.Fatalf("got wrong permissions (%0o)", stat.Mode)
|
|
|
|
}
|
|
|
|
}
|
2019-11-25 23:20:25 +00:00
|
|
|
// Don't include deferred cleanup in benchmark time.
|
|
|
|
b.StopTimer()
|
2019-07-18 22:09:14 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
2019-11-25 23:20:25 +00:00
|
|
|
|
|
|
|
func init() {
|
|
|
|
// Turn off reference leak checking for a fair comparison between vfs1 and
|
|
|
|
// vfs2.
|
|
|
|
refs.SetLeakMode(refs.NoLeakChecking)
|
|
|
|
}
|