From e884168e1ea5cd8be4d50c85a4ad4fbcdaca1e5c Mon Sep 17 00:00:00 2001 From: Nicolas Lacasse Date: Fri, 8 Feb 2019 15:47:25 -0800 Subject: [PATCH] Encode stat to bytes manually, instead of calling CopyObjectOut. CopyObjectOut grows its destination byte slice incrementally, causing many small slice allocations on the heap. This leads to increased GC and noticeably slower stat calls. PiperOrigin-RevId: 233140904 Change-Id: Ieb90295dd8dd45b3e56506fef9d7f86c92e97d97 --- pkg/abi/linux/file.go | 4 ++ pkg/sentry/syscalls/linux/sys_stat.go | 61 ++++++++++++++++++++------- 2 files changed, 50 insertions(+), 15 deletions(-) diff --git a/pkg/abi/linux/file.go b/pkg/abi/linux/file.go index ae33f4a4d..e5a51a9fd 100644 --- a/pkg/abi/linux/file.go +++ b/pkg/abi/linux/file.go @@ -19,6 +19,7 @@ import ( "strings" "gvisor.googlesource.com/gvisor/pkg/abi" + "gvisor.googlesource.com/gvisor/pkg/binary" ) // Constants for open(2). @@ -177,6 +178,9 @@ type Stat struct { X_unused [3]int64 } +// SizeOfStat is the size of a Stat struct. +var SizeOfStat = binary.Size(Stat{}) + // FileMode represents a mode_t. type FileMode uint diff --git a/pkg/sentry/syscalls/linux/sys_stat.go b/pkg/sentry/syscalls/linux/sys_stat.go index 9c433c45d..95f161aac 100644 --- a/pkg/sentry/syscalls/linux/sys_stat.go +++ b/pkg/sentry/syscalls/linux/sys_stat.go @@ -16,6 +16,7 @@ package linux import ( "gvisor.googlesource.com/gvisor/pkg/abi/linux" + "gvisor.googlesource.com/gvisor/pkg/binary" "gvisor.googlesource.com/gvisor/pkg/sentry/arch" "gvisor.googlesource.com/gvisor/pkg/sentry/fs" "gvisor.googlesource.com/gvisor/pkg/sentry/kernel" @@ -124,21 +125,51 @@ func stat(t *kernel.Task, d *fs.Dirent, dirPath bool, statAddr usermem.Addr) err mode |= linux.ModeSocket } - _, err = t.CopyOut(statAddr, linux.Stat{ - Dev: uint64(d.Inode.StableAttr.DeviceID), - Rdev: uint64(linux.MakeDeviceID(d.Inode.StableAttr.DeviceFileMajor, d.Inode.StableAttr.DeviceFileMinor)), - Ino: uint64(d.Inode.StableAttr.InodeID), - Nlink: uattr.Links, - Mode: mode | uint32(uattr.Perms.LinuxMode()), - UID: uint32(uattr.Owner.UID.In(t.UserNamespace()).OrOverflow()), - GID: uint32(uattr.Owner.GID.In(t.UserNamespace()).OrOverflow()), - Size: uattr.Size, - Blksize: d.Inode.StableAttr.BlockSize, - Blocks: uattr.Usage / 512, - ATime: uattr.AccessTime.Timespec(), - MTime: uattr.ModificationTime.Timespec(), - CTime: uattr.StatusChangeTime.Timespec(), - }) + // We encode the stat struct to bytes manually, as stat() is a very + // common syscall for many applications, and t.CopyObjectOut has + // noticeable performance impact due to its many slice allocations and + // use of reflection. + b := make([]byte, 0, linux.SizeOfStat) + + // Dev (uint64) + b = binary.AppendUint64(b, usermem.ByteOrder, uint64(d.Inode.StableAttr.DeviceID)) + // Ino (uint64) + b = binary.AppendUint64(b, usermem.ByteOrder, uint64(d.Inode.StableAttr.InodeID)) + // Nlink (uint64) + b = binary.AppendUint64(b, usermem.ByteOrder, uattr.Links) + // Mode (uint32) + b = binary.AppendUint32(b, usermem.ByteOrder, mode|uint32(uattr.Perms.LinuxMode())) + // UID (uint32) + b = binary.AppendUint32(b, usermem.ByteOrder, uint32(uattr.Owner.UID.In(t.UserNamespace()).OrOverflow())) + // GID (uint32) + b = binary.AppendUint32(b, usermem.ByteOrder, uint32(uattr.Owner.GID.In(t.UserNamespace()).OrOverflow())) + // Padding (uint32) + b = binary.AppendUint32(b, usermem.ByteOrder, 0) + // Rdev (uint64) + b = binary.AppendUint64(b, usermem.ByteOrder, uint64(linux.MakeDeviceID(d.Inode.StableAttr.DeviceFileMajor, d.Inode.StableAttr.DeviceFileMinor))) + // Size (uint64) + b = binary.AppendUint64(b, usermem.ByteOrder, uint64(uattr.Size)) + // Blksize (uint64) + b = binary.AppendUint64(b, usermem.ByteOrder, uint64(d.Inode.StableAttr.BlockSize)) + // Blocks (uint64) + b = binary.AppendUint64(b, usermem.ByteOrder, uint64(uattr.Usage/512)) + + // ATime + atime := uattr.AccessTime.Timespec() + b = binary.AppendUint64(b, usermem.ByteOrder, uint64(atime.Sec)) + b = binary.AppendUint64(b, usermem.ByteOrder, uint64(atime.Nsec)) + + // MTime + mtime := uattr.ModificationTime.Timespec() + b = binary.AppendUint64(b, usermem.ByteOrder, uint64(mtime.Sec)) + b = binary.AppendUint64(b, usermem.ByteOrder, uint64(mtime.Nsec)) + + // CTime + ctime := uattr.StatusChangeTime.Timespec() + b = binary.AppendUint64(b, usermem.ByteOrder, uint64(ctime.Sec)) + b = binary.AppendUint64(b, usermem.ByteOrder, uint64(ctime.Nsec)) + + _, err = t.CopyOutBytes(statAddr, b) return err }