summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--pkg/sentry/fs/gofer/inode.go31
-rw-r--r--runsc/boot/fs.go6
2 files changed, 31 insertions, 6 deletions
diff --git a/pkg/sentry/fs/gofer/inode.go b/pkg/sentry/fs/gofer/inode.go
index 454242923..c00da5fec 100644
--- a/pkg/sentry/fs/gofer/inode.go
+++ b/pkg/sentry/fs/gofer/inode.go
@@ -206,7 +206,7 @@ func (i *inodeFileState) WriteFromBlocksAt(ctx context.Context, srcs safemem.Blo
// SetMaskedAttributes implements fsutil.CachedFileObject.SetMaskedAttributes.
func (i *inodeFileState) SetMaskedAttributes(ctx context.Context, mask fs.AttrMask, attr fs.UnstableAttr) error {
- if mask.Empty() {
+ if i.skipSetAttr(mask) {
return nil
}
as, ans := attr.AccessTime.Unix()
@@ -237,6 +237,35 @@ func (i *inodeFileState) SetMaskedAttributes(ctx context.Context, mask fs.AttrMa
})
}
+// skipSetAttr checks if attribute change can be skipped. It can be skipped
+// when:
+// - Mask is empty
+// - Mask contains only atime and/or mtime, and host FD exists
+//
+// Updates to atime and mtime can be skipped because cached value will be
+// "close enough" to host value, given that operation went directly to host FD.
+// Skipping atime updates is particularly important to reduce the number of
+// operations sent to the Gofer for readonly files.
+func (i *inodeFileState) skipSetAttr(mask fs.AttrMask) bool {
+ if mask.Empty() {
+ return true
+ }
+
+ cpy := mask
+ cpy.AccessTime = false
+ cpy.ModificationTime = false
+ if !cpy.Empty() {
+ // More than just atime and mtime is being set.
+ return false
+ }
+
+ i.handlesMu.RLock()
+ defer i.handlesMu.RUnlock()
+ return (i.readonly != nil && i.readonly.Host != nil) ||
+ (i.readthrough != nil && i.readthrough.Host != nil) ||
+ (i.writeback != nil && i.writeback.Host != nil)
+}
+
// Sync implements fsutil.CachedFileObject.Sync.
func (i *inodeFileState) Sync(ctx context.Context) error {
i.handlesMu.RLock()
diff --git a/runsc/boot/fs.go b/runsc/boot/fs.go
index 2073bd0b1..86cbe1169 100644
--- a/runsc/boot/fs.go
+++ b/runsc/boot/fs.go
@@ -141,10 +141,7 @@ func createMountNamespace(ctx context.Context, spec *specs.Spec, conf *Config, i
// createRootMount creates the root filesystem.
func createRootMount(ctx context.Context, spec *specs.Spec, conf *Config, fds *fdDispenser) (*fs.Inode, error) {
// First construct the filesystem from the spec.Root.
- mf := fs.MountSourceFlags{
- ReadOnly: spec.Root.Readonly,
- NoAtime: true,
- }
+ mf := fs.MountSourceFlags{ReadOnly: spec.Root.Readonly}
var (
rootInode *fs.Inode
@@ -261,7 +258,6 @@ func mountSubmount(ctx context.Context, spec *specs.Spec, conf *Config, mns *fs.
// All writes go to upper, be paranoid and make lower readonly.
mf.ReadOnly = true
}
- mf.NoAtime = true
inode, err := filesystem.Mount(ctx, m.Type, mf, strings.Join(data, ","))
if err != nil {