summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--pkg/sentry/fs/ext/benchmark/BUILD16
-rw-r--r--pkg/sentry/fs/ext/benchmark/benchmark_test.go193
-rwxr-xr-xpkg/sentry/fs/ext/benchmark/make_deep_ext4.sh72
-rw-r--r--pkg/sentry/fs/ext/ext.go10
-rw-r--r--pkg/sentry/fs/ext/ext_test.go2
5 files changed, 287 insertions, 6 deletions
diff --git a/pkg/sentry/fs/ext/benchmark/BUILD b/pkg/sentry/fs/ext/benchmark/BUILD
new file mode 100644
index 000000000..73999770a
--- /dev/null
+++ b/pkg/sentry/fs/ext/benchmark/BUILD
@@ -0,0 +1,16 @@
+load("//tools/go_stateify:defs.bzl", "go_test")
+
+package(licenses = ["notice"])
+
+go_test(
+ name = "benchmark_test",
+ size = "small",
+ srcs = ["benchmark_test.go"],
+ deps = [
+ "//pkg/sentry/context",
+ "//pkg/sentry/context/contexttest",
+ "//pkg/sentry/fs/ext",
+ "//pkg/sentry/kernel/auth",
+ "//pkg/sentry/vfs",
+ ],
+)
diff --git a/pkg/sentry/fs/ext/benchmark/benchmark_test.go b/pkg/sentry/fs/ext/benchmark/benchmark_test.go
new file mode 100644
index 000000000..08f4dd251
--- /dev/null
+++ b/pkg/sentry/fs/ext/benchmark/benchmark_test.go
@@ -0,0 +1,193 @@
+// 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.
+
+// These benchmarks emulate memfs benchmarks. Ext4 images must be created
+// before this benchmark is run using the `make_deep_ext4.sh` script at
+// /tmp/image-{depth}.ext4 for all the depths tested below.
+package benchmark_test
+
+import (
+ "fmt"
+ "os"
+ "runtime"
+ "strings"
+ "testing"
+
+ "gvisor.dev/gvisor/pkg/sentry/context"
+ "gvisor.dev/gvisor/pkg/sentry/context/contexttest"
+ "gvisor.dev/gvisor/pkg/sentry/fs/ext"
+ "gvisor.dev/gvisor/pkg/sentry/kernel/auth"
+ "gvisor.dev/gvisor/pkg/sentry/vfs"
+)
+
+var depths = []int{1, 2, 3, 8, 64, 100}
+
+const filename = "file.txt"
+
+// setUp opens imagePath as an ext Filesystem and returns all necessary
+// elements required to run tests. If error is nil, it also returns a tear
+// down function which must be called after the test is run for clean up.
+func setUp(b *testing.B, imagePath string) (context.Context, *vfs.VirtualFilesystem, *vfs.VirtualDentry, func(), error) {
+ f, err := os.Open(imagePath)
+ if err != nil {
+ return nil, nil, nil, nil, err
+ }
+
+ ctx := contexttest.Context(b)
+ creds := auth.CredentialsFromContext(ctx)
+
+ // Create VFS.
+ vfsObj := vfs.New()
+ vfsObj.MustRegisterFilesystemType("extfs", ext.FilesystemType{})
+ mntns, err := vfsObj.NewMountNamespace(ctx, creds, imagePath, "extfs", &vfs.NewFilesystemOptions{InternalData: int(f.Fd())})
+ if err != nil {
+ f.Close()
+ return nil, nil, nil, nil, err
+ }
+
+ root := mntns.Root()
+
+ tearDown := func() {
+ root.DecRef()
+
+ if err := f.Close(); err != nil {
+ b.Fatalf("tearDown failed: %v", err)
+ }
+ }
+ return ctx, vfsObj, &root, tearDown, nil
+}
+
+// mount mounts extfs at the path operation passed. Returns a tear down
+// function which must be called after the test is run for clean up.
+func mount(b *testing.B, imagePath string, vfsfs *vfs.VirtualFilesystem, pop *vfs.PathOperation) func() {
+ b.Helper()
+
+ f, err := os.Open(imagePath)
+ if err != nil {
+ b.Fatalf("could not open image at %s: %v", imagePath, err)
+ }
+
+ ctx := contexttest.Context(b)
+ creds := auth.CredentialsFromContext(ctx)
+
+ if err := vfsfs.NewMount(ctx, creds, imagePath, pop, "extfs", &vfs.NewFilesystemOptions{InternalData: int(f.Fd())}); err != nil {
+ b.Fatalf("failed to mount tmpfs submount: %v", err)
+ }
+ return func() {
+ if err := f.Close(); err != nil {
+ b.Fatalf("tearDown failed: %v", err)
+ }
+ }
+}
+
+// BenchmarkVFS2Ext4fsStat emulates BenchmarkVFS2MemfsStat.
+func BenchmarkVFS2Ext4fsStat(b *testing.B) {
+ for _, depth := range depths {
+ b.Run(fmt.Sprintf("%d", depth), func(b *testing.B) {
+ ctx, vfsfs, root, tearDown, err := setUp(b, fmt.Sprintf("/tmp/image-%d.ext4", depth))
+ if err != nil {
+ b.Fatalf("setUp failed: %v", err)
+ }
+ defer tearDown()
+
+ creds := auth.CredentialsFromContext(ctx)
+ var filePathBuilder strings.Builder
+ filePathBuilder.WriteByte('/')
+ for i := 1; i <= depth; i++ {
+ filePathBuilder.WriteString(fmt.Sprintf("%d", i))
+ filePathBuilder.WriteByte('/')
+ }
+ filePathBuilder.WriteString(filename)
+ filePath := filePathBuilder.String()
+
+ runtime.GC()
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ stat, err := vfsfs.StatAt(ctx, creds, &vfs.PathOperation{
+ Root: *root,
+ Start: *root,
+ Pathname: filePath,
+ FollowFinalSymlink: true,
+ }, &vfs.StatOptions{})
+ if err != nil {
+ b.Fatalf("stat(%q) failed: %v", filePath, err)
+ }
+ // Sanity check.
+ if stat.Size > 0 {
+ b.Fatalf("got wrong file size (%d)", stat.Size)
+ }
+ }
+ })
+ }
+}
+
+// BenchmarkVFS2ExtfsMountStat emulates BenchmarkVFS2MemfsMountStat.
+func BenchmarkVFS2ExtfsMountStat(b *testing.B) {
+ for _, depth := range depths {
+ b.Run(fmt.Sprintf("%d", depth), func(b *testing.B) {
+ // Create root extfs with depth 1 so we can mount extfs again at /1/.
+ ctx, vfsfs, root, tearDown, err := setUp(b, fmt.Sprintf("/tmp/image-%d.ext4", 1))
+ if err != nil {
+ b.Fatalf("setUp failed: %v", err)
+ }
+ defer tearDown()
+
+ creds := auth.CredentialsFromContext(ctx)
+ mountPointName := "/1/"
+ pop := vfs.PathOperation{
+ Root: *root,
+ Start: *root,
+ Pathname: mountPointName,
+ }
+
+ // Save the mount point for later use.
+ mountPoint, err := vfsfs.GetDentryAt(ctx, creds, &pop, &vfs.GetDentryOptions{})
+ if err != nil {
+ b.Fatalf("failed to walk to mount point: %v", err)
+ }
+ defer mountPoint.DecRef()
+
+ // Create extfs submount.
+ mountTearDown := mount(b, fmt.Sprintf("/tmp/image-%d.ext4", depth), vfsfs, &pop)
+ defer mountTearDown()
+
+ var filePathBuilder strings.Builder
+ filePathBuilder.WriteString(mountPointName)
+ for i := 1; i <= depth; i++ {
+ filePathBuilder.WriteString(fmt.Sprintf("%d", i))
+ filePathBuilder.WriteByte('/')
+ }
+ filePathBuilder.WriteString(filename)
+ filePath := filePathBuilder.String()
+
+ runtime.GC()
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ stat, err := vfsfs.StatAt(ctx, creds, &vfs.PathOperation{
+ Root: *root,
+ Start: *root,
+ Pathname: filePath,
+ FollowFinalSymlink: true,
+ }, &vfs.StatOptions{})
+ if err != nil {
+ b.Fatalf("stat(%q) failed: %v", filePath, err)
+ }
+ // Sanity check. touch(1) always creates files of size 0 (empty).
+ if stat.Size > 0 {
+ b.Fatalf("got wrong file size (%d)", stat.Size)
+ }
+ }
+ })
+ }
+}
diff --git a/pkg/sentry/fs/ext/benchmark/make_deep_ext4.sh b/pkg/sentry/fs/ext/benchmark/make_deep_ext4.sh
new file mode 100755
index 000000000..d0910da1f
--- /dev/null
+++ b/pkg/sentry/fs/ext/benchmark/make_deep_ext4.sh
@@ -0,0 +1,72 @@
+#!/bin/bash
+
+# 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.
+
+# This script creates an ext4 image with $1 depth of directories and a file in
+# the inner most directory. The created file is at path /1/2/.../depth/file.txt.
+# The ext4 image is written to $2. The image is temporarily mounted at
+# /tmp/mountpoint. This script must be run with sudo privileges.
+
+# Usage:
+# sudo bash make_deep_ext4.sh {depth} {output path}
+
+# Check positional arguments.
+if [ "$#" -ne 2 ]; then
+ echo "Usage: sudo bash make_deep_ext4.sh {depth} {output path}"
+ exit 1
+fi
+
+# Make sure depth is a non-negative number.
+if ! [[ "$1" =~ ^[0-9]+$ ]]; then
+ echo "Depth must be a non-negative number."
+ exit 1
+fi
+
+# Create a 1 MB filesystem image at the requested output path.
+rm -f $2
+fallocate -l 1M $2
+if [ $? -ne 0 ]; then
+ echo "fallocate failed"
+ exit $?
+fi
+
+# Convert that blank into an ext4 image.
+mkfs.ext4 -j $2
+if [ $? -ne 0 ]; then
+ echo "mkfs.ext4 failed"
+ exit $?
+fi
+
+# Mount the image.
+MOUNTPOINT=/tmp/mountpoint
+mkdir -p $MOUNTPOINT
+mount -o loop $2 $MOUNTPOINT
+if [ $? -ne 0 ]; then
+ echo "mount failed"
+ exit $?
+fi
+
+# Create nested directories and the file.
+if [ "$1" -eq 0 ]; then
+ FILEPATH=$MOUNTPOINT/file.txt
+else
+ FILEPATH=$MOUNTPOINT/$(seq -s '/' 1 $1)/file.txt
+fi
+mkdir -p $(dirname $FILEPATH) || exit
+touch $FILEPATH
+
+# Clean up.
+umount $MOUNTPOINT
+rm -rf $MOUNTPOINT
diff --git a/pkg/sentry/fs/ext/ext.go b/pkg/sentry/fs/ext/ext.go
index c3e2c9efb..632ba97f8 100644
--- a/pkg/sentry/fs/ext/ext.go
+++ b/pkg/sentry/fs/ext/ext.go
@@ -30,11 +30,11 @@ import (
"gvisor.dev/gvisor/pkg/syserror"
)
-// filesystemType implements vfs.FilesystemType.
-type filesystemType struct{}
+// FilesystemType implements vfs.FilesystemType.
+type FilesystemType struct{}
-// Compiles only if filesystemType implements vfs.FilesystemType.
-var _ vfs.FilesystemType = (*filesystemType)(nil)
+// Compiles only if FilesystemType implements vfs.FilesystemType.
+var _ vfs.FilesystemType = (*FilesystemType)(nil)
// getDeviceFd returns an io.ReaderAt to the underlying device.
// Currently there are two ways of mounting an ext(2/3/4) fs:
@@ -92,7 +92,7 @@ func isCompatible(sb disklayout.SuperBlock) bool {
}
// NewFilesystem implements vfs.FilesystemType.NewFilesystem.
-func (fstype filesystemType) NewFilesystem(ctx context.Context, creds *auth.Credentials, source string, opts vfs.NewFilesystemOptions) (*vfs.Filesystem, *vfs.Dentry, error) {
+func (FilesystemType) NewFilesystem(ctx context.Context, creds *auth.Credentials, source string, opts vfs.NewFilesystemOptions) (*vfs.Filesystem, *vfs.Dentry, error) {
// TODO(b/134676337): Ensure that the user is mounting readonly. If not,
// EACCESS should be returned according to mount(2). Filesystem independent
// flags (like readonly) are currently not available in pkg/sentry/vfs.
diff --git a/pkg/sentry/fs/ext/ext_test.go b/pkg/sentry/fs/ext/ext_test.go
index 270f38074..31da80ed9 100644
--- a/pkg/sentry/fs/ext/ext_test.go
+++ b/pkg/sentry/fs/ext/ext_test.go
@@ -65,7 +65,7 @@ func setUp(t *testing.T, imagePath string) (context.Context, *vfs.VirtualFilesys
// Create VFS.
vfsObj := vfs.New()
- vfsObj.MustRegisterFilesystemType("extfs", filesystemType{})
+ vfsObj.MustRegisterFilesystemType("extfs", FilesystemType{})
mntns, err := vfsObj.NewMountNamespace(ctx, creds, localImagePath, "extfs", &vfs.NewFilesystemOptions{InternalData: int(f.Fd())})
if err != nil {
f.Close()